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