changes since 2.6.4
[fwknop.git] / client / spa_comm.c
1 /*
2  *****************************************************************************
3  *
4  * File:    spa_comm.c
5  *
6  * Purpose: Network-related functions for the fwknop client
7  *
8  *  Fwknop is developed primarily by the people listed in the file 'AUTHORS'.
9  *  Copyright (C) 2009-2014 fwknop developers and contributors. For a full
10  *  list of contributors, see the file 'CREDITS'.
11  *
12  *  License (GNU General Public License):
13  *
14  *  This program is free software; you can redistribute it and/or
15  *  modify it under the terms of the GNU General Public License
16  *  as published by the Free Software Foundation; either version 2
17  *  of the License, or (at your option) any later version.
18  *
19  *  This program is distributed in the hope that it will be useful,
20  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
21  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  *  GNU General Public License for more details.
23  *
24  *  You should have received a copy of the GNU General Public License
25  *  along with this program; if not, write to the Free Software
26  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
27  *  USA
28  *
29  *****************************************************************************
30 */
31 #include "spa_comm.h"
32 #include "utils.h"
33
34 static void
35 dump_transmit_options(const fko_cli_options_t *options)
36 {
37     char proto_str[PROTOCOL_BUFSIZE] = {0};   /* Protocol string */
38
39     proto_inttostr(options->spa_proto, proto_str, sizeof(proto_str));
40
41     log_msg(LOG_VERBOSITY_INFO, "Generating SPA packet:");
42     log_msg(LOG_VERBOSITY_INFO, "            protocol: %s", proto_str);
43
44     if (options->spa_src_port)
45         log_msg(LOG_VERBOSITY_INFO, "         source port: %d", options->spa_src_port);
46     else
47         log_msg(LOG_VERBOSITY_INFO, "         source port: <OS assigned>");
48
49     log_msg(LOG_VERBOSITY_INFO, "    destination port: %d", options->spa_dst_port);
50     log_msg(LOG_VERBOSITY_INFO, "             IP/host: %s", options->spa_server_str);
51
52     return;
53 }
54
55 /* Function to generate a header checksum.
56 */
57 unsigned short
58 chksum(unsigned short *buf, int nbytes)
59 {
60     unsigned int   sum;
61     unsigned short oddbyte;
62
63     sum = 0;
64     while (nbytes > 1)
65     {
66         sum += *buf++;
67         nbytes -= 2;
68     }
69
70     if (nbytes == 1)
71     {
72         oddbyte = 0;
73         *((unsigned short *) &oddbyte) = *(unsigned short *) buf;
74         sum += oddbyte;
75     }
76
77     sum = (sum >> 16) + (sum & 0xffff);
78     sum += (sum >> 16);
79
80     return (unsigned short) ~sum;
81 }
82
83 /* Send the SPA data via UDP packet.
84 */
85 static int
86 send_spa_packet_tcp_or_udp(const char *spa_data, const int sd_len,
87     const fko_cli_options_t *options)
88 {
89     int     sock=-1, sock_success=0, res=0, error;
90     struct  addrinfo *result=NULL, *rp, hints;
91     char    port_str[MAX_PORT_STR_LEN+1] = {0};
92
93     if (options->test)
94     {
95         log_msg(LOG_VERBOSITY_NORMAL,
96             "test mode enabled, SPA packet not actually sent.");
97         return res;
98     }
99
100     memset(&hints, 0, sizeof(struct addrinfo));
101
102     hints.ai_family   = AF_UNSPEC; /* Allow IPv4 or IPv6 */
103
104     if (options->spa_proto == FKO_PROTO_UDP)
105     {
106         /* Send the SPA data packet via an single UDP packet - this is the
107          * most common usage.
108         */
109         hints.ai_socktype = SOCK_DGRAM;
110         hints.ai_protocol = IPPROTO_UDP;
111     }
112     else
113     {
114         /* Send the SPA data packet via an established TCP connection.
115         */
116         hints.ai_socktype = SOCK_STREAM;
117         hints.ai_protocol = IPPROTO_TCP;
118     }
119
120     snprintf(port_str, MAX_PORT_STR_LEN+1, "%d", options->spa_dst_port);
121
122     error = getaddrinfo(options->spa_server_str, port_str, &hints, &result);
123
124     if (error != 0)
125     {
126         log_msg(LOG_VERBOSITY_ERROR, "error in getaddrinfo: %s", gai_strerror(error));
127         return -1;
128     }
129
130     for (rp = result; rp != NULL; rp = rp->ai_next) {
131         sock = socket(rp->ai_family, rp->ai_socktype,
132                 rp->ai_protocol);
133         if (sock < 0)
134             continue;
135
136         if ((error = (connect(sock, rp->ai_addr, rp->ai_addrlen) != -1)))
137         {
138             sock_success = 1;
139             break;  /* made it */
140         }
141         else /* close the open socket if there was a connect error */
142         {
143 #ifdef WIN32
144             closesocket(sock);
145 #else
146             close(sock);
147 #endif
148         }
149     }
150     if(result != NULL)
151         freeaddrinfo(result);
152
153     if (! sock_success) {
154         log_msg(LOG_VERBOSITY_ERROR,
155                 "send_spa_packet_tcp_or_udp: Could not create socket: ",
156                 strerror(errno));
157         return -1;
158     }
159
160     res = send(sock, spa_data, sd_len, 0);
161
162     if(res < 0)
163     {
164         log_msg(LOG_VERBOSITY_ERROR, "send_spa_packet_tcp_or_udp: write error: ", strerror(errno));
165     }
166     else if(res != sd_len)
167     {
168         log_msg(LOG_VERBOSITY_WARNING,
169             "[#] Warning: bytes sent (%i) not spa data length (%i).",
170             res, sd_len
171         );
172     }
173
174 #ifdef WIN32
175     closesocket(sock);
176 #else
177     close(sock);
178 #endif
179
180     return(res);
181 }
182
183 /* Send the SPA data via raw TCP packet.
184 */
185 static int
186 send_spa_packet_tcp_raw(const char *spa_data, const int sd_len,
187     const struct sockaddr_in *saddr, const struct sockaddr_in *daddr,
188     const fko_cli_options_t *options)
189 {
190 #ifdef WIN32
191     log_msg(LOG_VERBOSITY_ERROR,
192         "send_spa_packet_tcp_raw: raw packets are not yet supported.");
193     return(-1);
194 #else
195     int  sock, res = 0;
196     char pkt_data[2048] = {0}; /* Should be enough for our purposes */
197
198     struct iphdr  *iph  = (struct iphdr *) pkt_data;
199     struct tcphdr *tcph = (struct tcphdr *) (pkt_data + sizeof (struct iphdr));
200
201     int hdrlen = sizeof(struct iphdr) + sizeof(struct tcphdr);
202
203     /* Values for setsockopt.
204     */
205     int         one     = 1;
206     const int  *so_val  = &one;
207
208     if (options->test)
209     {
210         log_msg(LOG_VERBOSITY_NORMAL,
211             "test mode enabled, SPA packet not actually sent.");
212         return res;
213     }
214
215     sock = socket (PF_INET, SOCK_RAW, IPPROTO_RAW);
216     if (sock < 0)
217     {
218         log_msg(LOG_VERBOSITY_ERROR, "send_spa_packet_tcp_raw: create socket: ", strerror(errno));
219         return(sock);
220     }
221
222     /* Put the spa data in place.
223     */
224     memcpy((pkt_data + hdrlen), spa_data, sd_len);
225
226     /* Construct our own header by filling in the ip/tcp header values,
227      * starting with the IP header values.
228     */
229     iph->ihl        = 5;
230     iph->version    = 4;
231     iph->tos        = 0;
232     /* Total size is header plus payload */
233     iph->tot_len    = hdrlen + sd_len;
234     /* The value here does not matter */
235     iph->id         = random() & 0xffff;
236     iph->frag_off   = 0;
237     iph->ttl        = RAW_SPA_TTL;
238     iph->protocol   = IPPROTO_TCP;
239     iph->check      = 0;
240     iph->saddr      = saddr->sin_addr.s_addr;
241     iph->daddr      = daddr->sin_addr.s_addr;
242
243     /* Now the TCP header values.
244     */
245     tcph->source    = saddr->sin_port;
246     tcph->dest      = daddr->sin_port;
247     tcph->seq       = htonl(1);
248     tcph->ack_seq   = 0;
249     tcph->doff      = 5;
250     tcph->res1      = 0;
251     /* TCP flags */
252     tcph->fin       = 0;
253     tcph->syn       = 1;
254     tcph->rst       = 0;
255     tcph->psh       = 0;
256     tcph->ack       = 0;
257     tcph->urg       = 0;
258
259     tcph->res2      = 0;
260     tcph->window    = htons(32767);
261     tcph->check     = 0;
262     tcph->urg_ptr   = 0;
263
264     /* No we can compute our checksum.
265     */
266     iph->check = chksum((unsigned short *)pkt_data, iph->tot_len);
267
268     /* Make sure the kernel knows the header is included in the data so it
269      * doesn't try to insert its own header into the packet.
270     */
271     if (setsockopt (sock, IPPROTO_IP, IP_HDRINCL, so_val, sizeof(one)) < 0)
272         log_msg(LOG_VERBOSITY_ERROR, "send_spa_packet_tcp_raw: setsockopt HDRINCL: ", strerror(errno));
273
274     res = sendto (sock, pkt_data, iph->tot_len, 0,
275         (struct sockaddr *)daddr, sizeof(*daddr));
276
277     if(res < 0)
278     {
279         log_msg(LOG_VERBOSITY_ERROR, "send_spa_packet_tcp_raw: sendto error: ", strerror(errno));
280     }
281     else if(res != sd_len + hdrlen) /* account for the header ?*/
282     {
283         log_msg(LOG_VERBOSITY_WARNING,
284             "[#] Warning: bytes sent (%i) not spa data length (%i).",
285             res, sd_len
286         );
287     }
288
289     close(sock);
290
291     return(res);
292
293 #endif /* !WIN32 */
294 }
295
296 /* Send the SPA data via raw UDP packet.
297 */
298 static int
299 send_spa_packet_udp_raw(const char *spa_data, const int sd_len,
300     const struct sockaddr_in *saddr, const struct sockaddr_in *daddr,
301     const fko_cli_options_t *options)
302 {
303 #ifdef WIN32
304     log_msg(LOG_VERBOSITY_ERROR,
305         "send_spa_packet_udp_raw: raw packets are not yet supported.");
306     return(-1);
307 #else
308     int  sock, res = 0;
309     char pkt_data[2048] = {0}; /* Should be enough for our purposes */
310
311     struct iphdr  *iph  = (struct iphdr *) pkt_data;
312     struct udphdr *udph = (struct udphdr *) (pkt_data + sizeof (struct iphdr));
313
314     int hdrlen = sizeof(struct iphdr) + sizeof(struct udphdr);
315
316     /* Values for setsockopt.
317     */
318     int         one     = 1;
319     const int  *so_val  = &one;
320
321     if (options->test)
322     {
323         log_msg(LOG_VERBOSITY_NORMAL,
324             "test mode enabled, SPA packet not actually sent.");
325         return res;
326     }
327
328     sock = socket (PF_INET, SOCK_RAW, IPPROTO_RAW);
329     if (sock < 0)
330     {
331         log_msg(LOG_VERBOSITY_ERROR, "send_spa_packet_udp_raw: create socket: ", strerror(errno));
332         return(sock);
333     }
334
335     /* Put the spa data in place.
336     */
337     memcpy((pkt_data + hdrlen), spa_data, sd_len);
338
339     /* Construct our own header by filling in the ip/udp header values,
340      * starting with the IP header values.
341     */
342     iph->ihl        = 5;
343     iph->version    = 4;
344     iph->tos        = 0;
345     /* Total size is header plus payload */
346     iph->tot_len    = hdrlen + sd_len;
347     /* The value here does not matter */
348     iph->id         = random() & 0xffff;
349     iph->frag_off   = 0;
350     iph->ttl        = RAW_SPA_TTL;
351     iph->protocol   = IPPROTO_UDP;
352     iph->check      = 0;
353     iph->saddr      = saddr->sin_addr.s_addr;
354     iph->daddr      = daddr->sin_addr.s_addr;
355
356     /* Now the UDP header values.
357     */
358     udph->source    = saddr->sin_port;
359     udph->dest      = daddr->sin_port;
360     udph->check     = 0;
361     udph->len       = htons(sd_len + sizeof(struct udphdr));
362
363     /* No we can compute our checksum.
364     */
365     iph->check = chksum((unsigned short *)pkt_data, iph->tot_len);
366
367     /* Make sure the kernel knows the header is included in the data so it
368      * doesn't try to insert its own header into the packet.
369     */
370     if (setsockopt (sock, IPPROTO_IP, IP_HDRINCL, so_val, sizeof(one)) < 0)
371         log_msg(LOG_VERBOSITY_ERROR, "send_spa_packet_udp_raw: setsockopt HDRINCL: ", strerror(errno));
372
373     res = sendto (sock, pkt_data, iph->tot_len, 0,
374         (struct sockaddr *)daddr, sizeof(*daddr));
375
376     if(res < 0)
377     {
378         log_msg(LOG_VERBOSITY_ERROR, "send_spa_packet_udp_raw: sendto error: ", strerror(errno));
379     }
380     else if(res != sd_len + hdrlen) /* account for the header ?*/
381     {
382         log_msg(LOG_VERBOSITY_WARNING,
383             "[#] Warning: bytes sent (%i) not spa data length (%i).",
384             res, sd_len
385         );
386     }
387
388     close(sock);
389
390     return(res);
391
392 #endif /* !WIN32 */
393 }
394
395 /* Send the SPA data via ICMP packet.
396 */
397 static int
398 send_spa_packet_icmp(const char *spa_data, const int sd_len,
399     const struct sockaddr_in *saddr, const struct sockaddr_in *daddr,
400     const fko_cli_options_t *options)
401 {
402 #ifdef WIN32
403     log_msg(LOG_VERBOSITY_ERROR, "send_spa_packet_icmp: raw packets are not yet supported.");
404     return(-1);
405 #else
406     int res = 0, sock;
407     char pkt_data[2048] = {0};
408
409     struct iphdr  *iph    = (struct iphdr *) pkt_data;
410     struct icmphdr *icmph = (struct icmphdr *) (pkt_data + sizeof (struct iphdr));
411
412     int hdrlen = sizeof(struct iphdr) + sizeof(struct icmphdr);
413
414     /* Values for setsockopt.
415     */
416     int         one     = 1;
417     const int  *so_val  = &one;
418
419     if (options->test)
420     {
421         log_msg(LOG_VERBOSITY_NORMAL,
422             "test mode enabled, SPA packet not actually sent.");
423         return res;
424     }
425
426     sock = socket (PF_INET, SOCK_RAW, IPPROTO_RAW);
427
428     if (sock < 0)
429     {
430         log_msg(LOG_VERBOSITY_ERROR, "send_spa_packet_icmp: create socket: ", strerror(errno));
431         return(sock);
432     }
433
434     /* Put the spa data in place.
435     */
436     memcpy((pkt_data + hdrlen), spa_data, sd_len);
437
438     /* Construct our own header by filling in the ip/icmp header values,
439      * starting with the IP header values.
440     */
441     iph->ihl        = 5;
442     iph->version    = 4;
443     iph->tos        = 0;
444     /* Total size is header plus payload */
445     iph->tot_len    = hdrlen + sd_len;
446     /* The value here does not matter */
447     iph->id         = random() & 0xffff;
448     iph->frag_off   = 0;
449     iph->ttl        = RAW_SPA_TTL;
450     iph->protocol   = IPPROTO_ICMP;
451     iph->check      = 0;
452     iph->saddr      = saddr->sin_addr.s_addr;
453     iph->daddr      = daddr->sin_addr.s_addr;
454
455     /* Now the ICMP header values.
456     */
457     icmph->type     = options->spa_icmp_type;
458     icmph->code     = options->spa_icmp_code;
459     icmph->checksum = 0;
460
461     if(icmph->type == ICMP_ECHO && icmph->code == 0)
462     {
463         icmph->un.echo.id       = htons(random() & 0xffff);
464         icmph->un.echo.sequence = htons(1);
465     }
466
467     /* No we can compute our checksum.
468     */
469     iph->check = chksum((unsigned short *)pkt_data, iph->tot_len);
470     icmph->checksum = chksum((unsigned short *)icmph, sizeof(struct icmphdr) + sd_len);
471
472     /* Make sure the kernel knows the header is included in the data so it
473      * doesn't try to insert its own header into the packet.
474     */
475     if (setsockopt (sock, IPPROTO_IP, IP_HDRINCL, so_val, sizeof(one)) < 0)
476         log_msg(LOG_VERBOSITY_ERROR, "send_spa_packet_icmp: setsockopt HDRINCL: ", strerror(errno));
477
478     res = sendto (sock, pkt_data, iph->tot_len, 0,
479         (struct sockaddr *)daddr, sizeof(*daddr));
480
481     if(res < 0)
482     {
483         log_msg(LOG_VERBOSITY_ERROR, "send_spa_packet_icmp: sendto error: ", strerror(errno));
484     }
485     else if(res != sd_len + hdrlen) /* account for icmp header */
486     {
487         log_msg(LOG_VERBOSITY_WARNING, "[#] Warning: bytes sent (%i) not spa data length (%i).",
488             res, sd_len);
489     }
490
491     close(sock);
492
493     return(res);
494
495 #endif /* !WIN32 */
496 }
497
498 /* Send the SPA data packet via an HTTP request
499 */
500 static int
501 send_spa_packet_http(const char *spa_data, const int sd_len,
502     fko_cli_options_t *options)
503 {
504     char http_buf[HTTP_MAX_REQUEST_LEN] = {0}, *spa_data_copy = NULL;
505     char *ndx = options->http_proxy;
506     int  i, proxy_port = 0, is_err;
507
508     spa_data_copy = malloc(sd_len+1);
509     if (spa_data_copy == NULL)
510     {
511         log_msg(LOG_VERBOSITY_ERROR, "[*] Fatal, could not allocate memory.");
512         return -1;
513     }
514     memcpy(spa_data_copy, spa_data, sd_len+1);
515
516     /* Change "+" to "-", and "/" to "_" for HTTP requests (the server
517      * side will translate these back before decrypting)
518     */
519     for (i=0; i < sd_len; i++) {
520         if (spa_data_copy[i] == '+') {
521             spa_data_copy[i] = '-';
522         }
523         else if (spa_data_copy[i] == '/') {
524             spa_data_copy[i] = '_';
525         }
526     }
527
528     if(options->http_proxy[0] == 0x0)
529     {
530         snprintf(http_buf, HTTP_MAX_REQUEST_LEN,
531             "GET /%s HTTP/1.0\r\nUser-Agent: %s\r\nAccept: */*\r\n"
532             "Host: %s\r\nConnection: close\r\n\r\n",
533             spa_data_copy,
534             options->http_user_agent,
535             options->spa_server_str  /* hostname or IP */
536         );
537     }
538     else /* we are sending the SPA packet through an HTTP proxy */
539     {
540         /* Extract the hostname if it was specified as a URL. Actually,
541          * we just move the start of the hostname to the begining of the
542          * original string.
543         */
544         if(strncasecmp(ndx, "http://", 7) == 0)
545             memmove(ndx, ndx+7, strlen(ndx)+1);
546
547         /* If there is a colon assume the proxy hostame or IP is on the left
548          * and the proxy port is on the right. So we make the : a \0 and
549          * extract the port value.
550         */
551         ndx = strchr(options->http_proxy, ':');
552         if(ndx)
553         {
554             *ndx = '\0';
555             proxy_port = strtol_wrapper(ndx+1, 1, MAX_PORT, NO_EXIT_UPON_ERR, &is_err);
556             if(is_err != FKO_SUCCESS)
557             {
558                 log_msg(LOG_VERBOSITY_ERROR,
559                     "[-] proxy port value is invalid, must be in [%d-%d]",
560                     1, MAX_PORT);
561                 free(spa_data_copy);
562                 return -1;
563             }
564         }
565
566         /* If we have a valid port value, use it.
567         */
568         if(proxy_port)
569             options->spa_dst_port = proxy_port;
570
571         snprintf(http_buf, HTTP_MAX_REQUEST_LEN,
572             "GET http://%s/%s HTTP/1.0\r\nUser-Agent: %s\r\nAccept: */*\r\n"
573             "Host: %s\r\nConnection: close\r\n\r\n",
574             options->spa_server_str,
575             spa_data_copy,
576             options->http_user_agent,
577             options->http_proxy  /* hostname or IP */
578         );
579         strlcpy(options->spa_server_str, options->http_proxy,
580                 sizeof(options->spa_server_str));
581     }
582     free(spa_data_copy);
583
584     if (options->test)
585     {
586         log_msg(LOG_VERBOSITY_INFO, "%s", http_buf);
587
588         log_msg(LOG_VERBOSITY_NORMAL,
589             "Test mode enabled, SPA packet not actually sent.");
590         return 0;
591     }
592
593     return send_spa_packet_tcp_or_udp(http_buf, strlen(http_buf), options);
594 }
595
596 /* Function used to send the SPA data.
597 */
598 int
599 send_spa_packet(fko_ctx_t ctx, fko_cli_options_t *options)
600 {
601     int                 res, sd_len;
602     char               *spa_data;
603     struct sockaddr_in  saddr, daddr;
604     char                ip_str[INET_ADDRSTRLEN] = {0};  /* String used to contain the ip addres of an hostname */
605     struct addrinfo     hints;                          /* Structure used to set hints to resolve hostname */
606 #ifdef WIN32
607     WSADATA wsa_data;
608 #endif
609
610     /* Initialize the hint buffer */
611     memset(&hints, 0 , sizeof(hints));
612
613     /* Get our spa data here.
614     */
615     res = fko_get_spa_data(ctx, &spa_data);
616
617     if(res != FKO_SUCCESS)
618     {
619         log_msg(LOG_VERBOSITY_ERROR,
620             "send_spa_packet: Error #%i from fko_get_spa_data: %s",
621             res, fko_errstr(res)
622         );
623         return(-1);
624     }
625
626     sd_len = strlen(spa_data);
627
628 #ifdef WIN32
629     /* Winsock needs to be initialized...
630     */
631     res = WSAStartup( MAKEWORD(1,1), &wsa_data );
632     if( res != 0 )
633     {
634         log_msg(LOG_VERBOSITY_ERROR, "Winsock initialization error %d", res );
635         return(-1);
636     }
637 #endif
638
639     errno = 0;
640
641     dump_transmit_options(options);
642
643     if (options->spa_proto == FKO_PROTO_TCP || options->spa_proto == FKO_PROTO_UDP)
644     {
645         res = send_spa_packet_tcp_or_udp(spa_data, sd_len, options);
646     }
647     else if (options->spa_proto == FKO_PROTO_HTTP)
648     {
649         res = send_spa_packet_http(spa_data, sd_len, options);
650     }
651     else if (options->spa_proto == FKO_PROTO_TCP_RAW
652             || options->spa_proto == FKO_PROTO_UDP_RAW
653             || options->spa_proto == FKO_PROTO_ICMP)
654     {
655         memset(&saddr, 0, sizeof(saddr));
656         memset(&daddr, 0, sizeof(daddr));
657
658         saddr.sin_family = AF_INET;
659         daddr.sin_family = AF_INET;
660
661         /* Set source address and port
662         */
663         if (options->spa_src_port)
664             saddr.sin_port = htons(options->spa_src_port);
665         else
666             saddr.sin_port = INADDR_ANY;
667
668         if (options->spoof_ip_src_str[0] != 0x00) {
669             saddr.sin_addr.s_addr = inet_addr(options->spoof_ip_src_str);
670         } else
671             saddr.sin_addr.s_addr = INADDR_ANY;  /* default */
672
673         if (saddr.sin_addr.s_addr == -1)
674         {
675             log_msg(LOG_VERBOSITY_ERROR, "Could not set source IP.");
676             return -1;
677         }
678
679         /* Set destination port
680         */
681         daddr.sin_port = htons(options->spa_dst_port);
682
683         /* Set destination address. We use the default protocol to resolve
684          * the ip address */
685         hints.ai_family = AF_INET;
686
687         if (resolve_dest_adr(options->spa_server_str, &hints, ip_str, sizeof(ip_str)) != 0)
688         {
689             log_msg(LOG_VERBOSITY_ERROR, "[*] Unable to resolve %s as an ip address",
690                     options->spa_server_str);
691             return -1;
692         }
693         else;
694
695         daddr.sin_addr.s_addr = inet_addr(ip_str);
696
697         if (options->spa_proto == FKO_PROTO_TCP_RAW)
698         {
699             res = send_spa_packet_tcp_raw(spa_data, sd_len, &saddr, &daddr, options);
700         }
701         else if (options->spa_proto == FKO_PROTO_UDP_RAW)
702         {
703             res = send_spa_packet_udp_raw(spa_data, sd_len, &saddr, &daddr, options);
704         }
705         else
706         {
707             res = send_spa_packet_icmp(spa_data, sd_len, &saddr, &daddr, options);
708         }
709     }
710     else
711     {
712         /* --DSS XXX: What to we really want to do here? */
713         log_msg(LOG_VERBOSITY_ERROR, "%i is not a valid or supported protocol.",
714             options->spa_proto);
715         res = -1;
716     }
717
718     return res;
719 }
720
721 /* Function to write SPA packet data to the filesystem
722 */
723 int write_spa_packet_data(fko_ctx_t ctx, const fko_cli_options_t *options)
724 {
725     FILE   *fp;
726     char   *spa_data;
727     int     res;
728
729     res = fko_get_spa_data(ctx, &spa_data);
730
731     if(res != FKO_SUCCESS)
732     {
733         log_msg(LOG_VERBOSITY_ERROR,
734             "write_spa_packet_data: Error #%i from fko_get_spa_data: %s",
735             res, fko_errstr(res)
736         );
737
738         return(-1);
739     }
740
741     if (options->save_packet_file_append)
742     {
743         fp = fopen(options->save_packet_file, "a");
744     }
745     else
746     {
747         unlink(options->save_packet_file);
748         fp = fopen(options->save_packet_file, "w");
749     }
750
751     if(fp == NULL)
752     {
753         log_msg(LOG_VERBOSITY_ERROR, "write_spa_packet_data: ", strerror(errno));
754         return(-1);
755     }
756
757     fprintf(fp, "%s\n",
758         (spa_data == NULL) ? "<NULL>" : spa_data);
759
760     fclose(fp);
761
762     return(0);
763 }
764
765 /***EOF***/