credit Blair and Tim with MacPorts and Homebrew maintainer status
[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, res=0, error;
90     struct  addrinfo *result, *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             break;  /* made it */
138
139 #ifdef WIN32
140         closesocket(sock);
141 #else
142         close(sock);
143 #endif
144     }
145
146     if (rp == NULL) {
147         log_msg(LOG_VERBOSITY_ERROR,
148                 "send_spa_packet_tcp_or_udp: Could not create socket: ",
149                 strerror(errno));
150         return -1;
151     }
152
153     freeaddrinfo(result);
154
155     res = send(sock, spa_data, sd_len, 0);
156
157     if(res < 0)
158     {
159         log_msg(LOG_VERBOSITY_ERROR, "send_spa_packet_tcp_or_udp: write error: ", strerror(errno));
160     }
161     else if(res != sd_len)
162     {
163         log_msg(LOG_VERBOSITY_WARNING,
164             "[#] Warning: bytes sent (%i) not spa data length (%i).",
165             res, sd_len
166         );
167     }
168
169 #ifdef WIN32
170     closesocket(sock);
171 #else
172     close(sock);
173 #endif
174
175     return(res);
176 }
177
178 /* Send the SPA data via raw TCP packet.
179 */
180 static int
181 send_spa_packet_tcp_raw(const char *spa_data, const int sd_len,
182     const struct sockaddr_in *saddr, const struct sockaddr_in *daddr,
183     const fko_cli_options_t *options)
184 {
185 #ifdef WIN32
186     log_msg(LOG_VERBOSITY_ERROR,
187         "send_spa_packet_tcp_raw: raw packets are not yet supported.");
188     return(-1);
189 #else
190     int  sock, res = 0;
191     char pkt_data[2048] = {0}; /* Should be enough for our purposes */
192
193     struct iphdr  *iph  = (struct iphdr *) pkt_data;
194     struct tcphdr *tcph = (struct tcphdr *) (pkt_data + sizeof (struct iphdr));
195
196     int hdrlen = sizeof(struct iphdr) + sizeof(struct tcphdr);
197
198     /* Values for setsockopt.
199     */
200     int         one     = 1;
201     const int  *so_val  = &one;
202
203     if (options->test)
204     {
205         log_msg(LOG_VERBOSITY_NORMAL,
206             "test mode enabled, SPA packet not actually sent.");
207         return res;
208     }
209
210     sock = socket (PF_INET, SOCK_RAW, IPPROTO_RAW);
211     if (sock < 0)
212     {
213         log_msg(LOG_VERBOSITY_ERROR, "send_spa_packet_tcp_raw: create socket: ", strerror(errno));
214         return(sock);
215     }
216
217     /* Put the spa data in place.
218     */
219     memcpy((pkt_data + hdrlen), spa_data, sd_len);
220
221     /* Construct our own header by filling in the ip/tcp header values,
222      * starting with the IP header values.
223     */
224     iph->ihl        = 5;
225     iph->version    = 4;
226     iph->tos        = 0;
227     /* Total size is header plus payload */
228     iph->tot_len    = hdrlen + sd_len;
229     /* The value here does not matter */
230     iph->id         = random() & 0xffff;
231     iph->frag_off   = 0;
232     iph->ttl        = RAW_SPA_TTL;
233     iph->protocol   = IPPROTO_TCP;
234     iph->check      = 0;
235     iph->saddr      = saddr->sin_addr.s_addr;
236     iph->daddr      = daddr->sin_addr.s_addr;
237
238     /* Now the TCP header values.
239     */
240     tcph->source    = saddr->sin_port;
241     tcph->dest      = daddr->sin_port;
242     tcph->seq       = htonl(1);
243     tcph->ack_seq   = 0;
244     tcph->doff      = 5;
245     tcph->res1      = 0;
246     /* TCP flags */
247     tcph->fin       = 0;
248     tcph->syn       = 1;
249     tcph->rst       = 0;
250     tcph->psh       = 0;
251     tcph->ack       = 0;
252     tcph->urg       = 0;
253
254     tcph->res2      = 0;
255     tcph->window    = htons(32767);
256     tcph->check     = 0;
257     tcph->urg_ptr   = 0;
258
259     /* No we can compute our checksum.
260     */
261     iph->check = chksum((unsigned short *)pkt_data, iph->tot_len);
262
263     /* Make sure the kernel knows the header is included in the data so it
264      * doesn't try to insert its own header into the packet.
265     */
266     if (setsockopt (sock, IPPROTO_IP, IP_HDRINCL, so_val, sizeof(one)) < 0)
267         log_msg(LOG_VERBOSITY_ERROR, "send_spa_packet_tcp_raw: setsockopt HDRINCL: ", strerror(errno));
268
269     res = sendto (sock, pkt_data, iph->tot_len, 0,
270         (struct sockaddr *)daddr, sizeof(*daddr));
271
272     if(res < 0)
273     {
274         log_msg(LOG_VERBOSITY_ERROR, "send_spa_packet_tcp_raw: sendto error: ", strerror(errno));
275     }
276     else if(res != sd_len + hdrlen) /* account for the header ?*/
277     {
278         log_msg(LOG_VERBOSITY_WARNING,
279             "[#] Warning: bytes sent (%i) not spa data length (%i).",
280             res, sd_len
281         );
282     }
283
284     close(sock);
285
286     return(res);
287
288 #endif /* !WIN32 */
289 }
290
291 /* Send the SPA data via raw UDP packet.
292 */
293 static int
294 send_spa_packet_udp_raw(const char *spa_data, const int sd_len,
295     const struct sockaddr_in *saddr, const struct sockaddr_in *daddr,
296     const fko_cli_options_t *options)
297 {
298 #ifdef WIN32
299     log_msg(LOG_VERBOSITY_ERROR,
300         "send_spa_packet_udp_raw: raw packets are not yet supported.");
301     return(-1);
302 #else
303     int  sock, res = 0;
304     char pkt_data[2048] = {0}; /* Should be enough for our purposes */
305
306     struct iphdr  *iph  = (struct iphdr *) pkt_data;
307     struct udphdr *udph = (struct udphdr *) (pkt_data + sizeof (struct iphdr));
308
309     int hdrlen = sizeof(struct iphdr) + sizeof(struct udphdr);
310
311     /* Values for setsockopt.
312     */
313     int         one     = 1;
314     const int  *so_val  = &one;
315
316     if (options->test)
317     {
318         log_msg(LOG_VERBOSITY_NORMAL,
319             "test mode enabled, SPA packet not actually sent.");
320         return res;
321     }
322
323     sock = socket (PF_INET, SOCK_RAW, IPPROTO_RAW);
324     if (sock < 0)
325     {
326         log_msg(LOG_VERBOSITY_ERROR, "send_spa_packet_udp_raw: create socket: ", strerror(errno));
327         return(sock);
328     }
329
330     /* Put the spa data in place.
331     */
332     memcpy((pkt_data + hdrlen), spa_data, sd_len);
333
334     /* Construct our own header by filling in the ip/udp header values,
335      * starting with the IP header values.
336     */
337     iph->ihl        = 5;
338     iph->version    = 4;
339     iph->tos        = 0;
340     /* Total size is header plus payload */
341     iph->tot_len    = hdrlen + sd_len;
342     /* The value here does not matter */
343     iph->id         = random() & 0xffff;
344     iph->frag_off   = 0;
345     iph->ttl        = RAW_SPA_TTL;
346     iph->protocol   = IPPROTO_UDP;
347     iph->check      = 0;
348     iph->saddr      = saddr->sin_addr.s_addr;
349     iph->daddr      = daddr->sin_addr.s_addr;
350
351     /* Now the UDP header values.
352     */
353     udph->source    = saddr->sin_port;
354     udph->dest      = daddr->sin_port;
355     udph->check     = 0;
356     udph->len       = htons(sd_len + sizeof(struct udphdr));
357
358     /* No we can compute our checksum.
359     */
360     iph->check = chksum((unsigned short *)pkt_data, iph->tot_len);
361
362     /* Make sure the kernel knows the header is included in the data so it
363      * doesn't try to insert its own header into the packet.
364     */
365     if (setsockopt (sock, IPPROTO_IP, IP_HDRINCL, so_val, sizeof(one)) < 0)
366         log_msg(LOG_VERBOSITY_ERROR, "send_spa_packet_udp_raw: setsockopt HDRINCL: ", strerror(errno));
367
368     res = sendto (sock, pkt_data, iph->tot_len, 0,
369         (struct sockaddr *)daddr, sizeof(*daddr));
370
371     if(res < 0)
372     {
373         log_msg(LOG_VERBOSITY_ERROR, "send_spa_packet_udp_raw: sendto error: ", strerror(errno));
374     }
375     else if(res != sd_len + hdrlen) /* account for the header ?*/
376     {
377         log_msg(LOG_VERBOSITY_WARNING,
378             "[#] Warning: bytes sent (%i) not spa data length (%i).",
379             res, sd_len
380         );
381     }
382
383     close(sock);
384
385     return(res);
386
387 #endif /* !WIN32 */
388 }
389
390 /* Send the SPA data via ICMP packet.
391 */
392 static int
393 send_spa_packet_icmp(const char *spa_data, const int sd_len,
394     const struct sockaddr_in *saddr, const struct sockaddr_in *daddr,
395     const fko_cli_options_t *options)
396 {
397 #ifdef WIN32
398     log_msg(LOG_VERBOSITY_ERROR, "send_spa_packet_icmp: raw packets are not yet supported.");
399     return(-1);
400 #else
401     int res = 0, sock;
402     char pkt_data[2048] = {0};
403
404     struct iphdr  *iph    = (struct iphdr *) pkt_data;
405     struct icmphdr *icmph = (struct icmphdr *) (pkt_data + sizeof (struct iphdr));
406
407     int hdrlen = sizeof(struct iphdr) + sizeof(struct icmphdr);
408
409     /* Values for setsockopt.
410     */
411     int         one     = 1;
412     const int  *so_val  = &one;
413
414     if (options->test)
415     {
416         log_msg(LOG_VERBOSITY_NORMAL,
417             "test mode enabled, SPA packet not actually sent.");
418         return res;
419     }
420
421     sock = socket (PF_INET, SOCK_RAW, IPPROTO_RAW);
422
423     if (sock < 0)
424     {
425         log_msg(LOG_VERBOSITY_ERROR, "send_spa_packet_icmp: create socket: ", strerror(errno));
426         return(sock);
427     }
428
429     /* Put the spa data in place.
430     */
431     memcpy((pkt_data + hdrlen), spa_data, sd_len);
432
433     /* Construct our own header by filling in the ip/icmp header values,
434      * starting with the IP header values.
435     */
436     iph->ihl        = 5;
437     iph->version    = 4;
438     iph->tos        = 0;
439     /* Total size is header plus payload */
440     iph->tot_len    = hdrlen + sd_len;
441     /* The value here does not matter */
442     iph->id         = random() & 0xffff;
443     iph->frag_off   = 0;
444     iph->ttl        = RAW_SPA_TTL;
445     iph->protocol   = IPPROTO_ICMP;
446     iph->check      = 0;
447     iph->saddr      = saddr->sin_addr.s_addr;
448     iph->daddr      = daddr->sin_addr.s_addr;
449
450     /* Now the ICMP header values.
451     */
452     icmph->type     = options->spa_icmp_type;
453     icmph->code     = options->spa_icmp_code;
454     icmph->checksum = 0;
455
456     if(icmph->type == ICMP_ECHO && icmph->code == 0)
457     {
458         icmph->un.echo.id       = htons(random() & 0xffff);
459         icmph->un.echo.sequence = htons(1);
460     }
461
462     /* No we can compute our checksum.
463     */
464     iph->check = chksum((unsigned short *)pkt_data, iph->tot_len);
465     icmph->checksum = chksum((unsigned short *)icmph, sizeof(struct icmphdr) + sd_len);
466
467     /* Make sure the kernel knows the header is included in the data so it
468      * doesn't try to insert its own header into the packet.
469     */
470     if (setsockopt (sock, IPPROTO_IP, IP_HDRINCL, so_val, sizeof(one)) < 0)
471         log_msg(LOG_VERBOSITY_ERROR, "send_spa_packet_icmp: setsockopt HDRINCL: ", strerror(errno));
472
473     res = sendto (sock, pkt_data, iph->tot_len, 0,
474         (struct sockaddr *)daddr, sizeof(*daddr));
475
476     if(res < 0)
477     {
478         log_msg(LOG_VERBOSITY_ERROR, "send_spa_packet_icmp: sendto error: ", strerror(errno));
479     }
480     else if(res != sd_len + hdrlen) /* account for icmp header */
481     {
482         log_msg(LOG_VERBOSITY_WARNING, "[#] Warning: bytes sent (%i) not spa data length (%i).",
483             res, sd_len);
484     }
485
486     close(sock);
487
488     return(res);
489
490 #endif /* !WIN32 */
491 }
492
493 /* Send the SPA data packet via an HTTP request
494 */
495 static int
496 send_spa_packet_http(const char *spa_data, const int sd_len,
497     fko_cli_options_t *options)
498 {
499     char http_buf[HTTP_MAX_REQUEST_LEN] = {0}, *spa_data_copy = NULL;
500     char *ndx = options->http_proxy;
501     int  i, proxy_port = 0, is_err;
502
503     spa_data_copy = malloc(sd_len+1);
504     if (spa_data_copy == NULL)
505     {
506         log_msg(LOG_VERBOSITY_ERROR, "[*] Fatal, could not allocate memory.");
507         return -1;
508     }
509     memcpy(spa_data_copy, spa_data, sd_len+1);
510
511     /* Change "+" to "-", and "/" to "_" for HTTP requests (the server
512      * side will translate these back before decrypting)
513     */
514     for (i=0; i < sd_len; i++) {
515         if (spa_data_copy[i] == '+') {
516             spa_data_copy[i] = '-';
517         }
518         else if (spa_data_copy[i] == '/') {
519             spa_data_copy[i] = '_';
520         }
521     }
522
523     if(options->http_proxy[0] == 0x0)
524     {
525         snprintf(http_buf, HTTP_MAX_REQUEST_LEN,
526             "GET /%s HTTP/1.0\r\nUser-Agent: %s\r\nAccept: */*\r\n"
527             "Host: %s\r\nConnection: close\r\n\r\n",
528             spa_data_copy,
529             options->http_user_agent,
530             options->spa_server_str  /* hostname or IP */
531         );
532     }
533     else /* we are sending the SPA packet through an HTTP proxy */
534     {
535         /* Extract the hostname if it was specified as a URL. Actually,
536          * we just move the start of the hostname to the begining of the
537          * original string.
538         */
539         if(strncasecmp(ndx, "http://", 7) == 0)
540             memmove(ndx, ndx+7, strlen(ndx)+1);
541
542         /* If there is a colon assume the proxy hostame or IP is on the left
543          * and the proxy port is on the right. So we make the : a \0 and
544          * extract the port value.
545         */
546         ndx = strchr(options->http_proxy, ':');
547         if(ndx)
548         {
549             *ndx = '\0';
550             proxy_port = strtol_wrapper(ndx+1, 1, MAX_PORT, NO_EXIT_UPON_ERR, &is_err);
551             if(is_err != FKO_SUCCESS)
552             {
553                 log_msg(LOG_VERBOSITY_ERROR,
554                     "[-] proxy port value is invalid, must be in [%d-%d]",
555                     1, MAX_PORT);
556                 free(spa_data_copy);
557                 return -1;
558             }
559         }
560
561         /* If we have a valid port value, use it.
562         */
563         if(proxy_port)
564             options->spa_dst_port = proxy_port;
565
566         snprintf(http_buf, HTTP_MAX_REQUEST_LEN,
567             "GET http://%s/%s HTTP/1.0\r\nUser-Agent: %s\r\nAccept: */*\r\n"
568             "Host: %s\r\nConnection: close\r\n\r\n",
569             options->spa_server_str,
570             spa_data_copy,
571             options->http_user_agent,
572             options->http_proxy  /* hostname or IP */
573         );
574         strlcpy(options->spa_server_str, options->http_proxy,
575                 sizeof(options->spa_server_str));
576     }
577     free(spa_data_copy);
578
579     if (options->test)
580     {
581         log_msg(LOG_VERBOSITY_INFO, "%s", http_buf);
582
583         log_msg(LOG_VERBOSITY_NORMAL,
584             "Test mode enabled, SPA packet not actually sent.");
585         return 0;
586     }
587
588     return send_spa_packet_tcp_or_udp(http_buf, strlen(http_buf), options);
589 }
590
591 /* Function used to send the SPA data.
592 */
593 int
594 send_spa_packet(fko_ctx_t ctx, fko_cli_options_t *options)
595 {
596     int                 res, sd_len;
597     char               *spa_data;
598     struct sockaddr_in  saddr, daddr;
599     char                ip_str[INET_ADDRSTRLEN] = {0};  /* String used to contain the ip addres of an hostname */
600     struct addrinfo     hints;                          /* Structure used to set hints to resolve hostname */
601 #ifdef WIN32
602     WSADATA wsa_data;
603 #endif
604
605     /* Initialize the hint buffer */
606     memset(&hints, 0 , sizeof(hints));
607
608     /* Get our spa data here.
609     */
610     res = fko_get_spa_data(ctx, &spa_data);
611
612     if(res != FKO_SUCCESS)
613     {
614         log_msg(LOG_VERBOSITY_ERROR,
615             "send_spa_packet: Error #%i from fko_get_spa_data: %s",
616             res, fko_errstr(res)
617         );
618         return(-1);
619     }
620
621     sd_len = strlen(spa_data);
622
623 #ifdef WIN32
624     /* Winsock needs to be initialized...
625     */
626     res = WSAStartup( MAKEWORD(1,1), &wsa_data );
627     if( res != 0 )
628     {
629         log_msg(LOG_VERBOSITY_ERROR, "Winsock initialization error %d", res );
630         return(-1);
631     }
632 #endif
633
634     errno = 0;
635
636     dump_transmit_options(options);
637
638     if (options->spa_proto == FKO_PROTO_TCP || options->spa_proto == FKO_PROTO_UDP)
639     {
640         res = send_spa_packet_tcp_or_udp(spa_data, sd_len, options);
641     }
642     else if (options->spa_proto == FKO_PROTO_HTTP)
643     {
644         res = send_spa_packet_http(spa_data, sd_len, options);
645     }
646     else if (options->spa_proto == FKO_PROTO_TCP_RAW
647             || options->spa_proto == FKO_PROTO_UDP_RAW
648             || options->spa_proto == FKO_PROTO_ICMP)
649     {
650         memset(&saddr, 0, sizeof(saddr));
651         memset(&daddr, 0, sizeof(daddr));
652
653         saddr.sin_family = AF_INET;
654         daddr.sin_family = AF_INET;
655
656         /* Set source address and port
657         */
658         if (options->spa_src_port)
659             saddr.sin_port = htons(options->spa_src_port);
660         else
661             saddr.sin_port = INADDR_ANY;
662
663         if (options->spoof_ip_src_str[0] != 0x00) {
664             saddr.sin_addr.s_addr = inet_addr(options->spoof_ip_src_str);
665         } else
666             saddr.sin_addr.s_addr = INADDR_ANY;  /* default */
667
668         if (saddr.sin_addr.s_addr == -1)
669         {
670             log_msg(LOG_VERBOSITY_ERROR, "Could not set source IP.");
671             return -1;
672         }
673
674         /* Set destination port
675         */
676         daddr.sin_port = htons(options->spa_dst_port);
677
678         /* Set destination address. We use the default protocol to resolve
679          * the ip address */
680         hints.ai_family = AF_INET;
681
682         if (resolve_dest_adr(options->spa_server_str, &hints, ip_str, sizeof(ip_str)) != 0)
683         {
684             log_msg(LOG_VERBOSITY_ERROR, "[*] Unable to resolve %s as an ip address",
685                     options->spa_server_str);
686             return -1;
687         }
688         else;
689
690         daddr.sin_addr.s_addr = inet_addr(ip_str);
691
692         if (options->spa_proto == FKO_PROTO_TCP_RAW)
693         {
694             res = send_spa_packet_tcp_raw(spa_data, sd_len, &saddr, &daddr, options);
695         }
696         else if (options->spa_proto == FKO_PROTO_UDP_RAW)
697         {
698             res = send_spa_packet_udp_raw(spa_data, sd_len, &saddr, &daddr, options);
699         }
700         else
701         {
702             res = send_spa_packet_icmp(spa_data, sd_len, &saddr, &daddr, options);
703         }
704     }
705     else
706     {
707         /* --DSS XXX: What to we really want to do here? */
708         log_msg(LOG_VERBOSITY_ERROR, "%i is not a valid or supported protocol.",
709             options->spa_proto);
710         res = -1;
711     }
712
713     return res;
714 }
715
716 /* Function to write SPA packet data to the filesystem
717 */
718 int write_spa_packet_data(fko_ctx_t ctx, const fko_cli_options_t *options)
719 {
720     FILE   *fp;
721     char   *spa_data;
722     int     res;
723
724     res = fko_get_spa_data(ctx, &spa_data);
725
726     if(res != FKO_SUCCESS)
727     {
728         log_msg(LOG_VERBOSITY_ERROR,
729             "write_spa_packet_data: Error #%i from fko_get_spa_data: %s",
730             res, fko_errstr(res)
731         );
732
733         return(-1);
734     }
735
736     if (options->save_packet_file_append)
737     {
738         fp = fopen(options->save_packet_file, "a");
739     }
740     else
741     {
742         unlink(options->save_packet_file);
743         fp = fopen(options->save_packet_file, "w");
744     }
745
746     if(fp == NULL)
747     {
748         log_msg(LOG_VERBOSITY_ERROR, "write_spa_packet_data: ", strerror(errno));
749         return(-1);
750     }
751
752     fprintf(fp, "%s\n",
753         (spa_data == NULL) ? "<NULL>" : spa_data);
754
755     fclose(fp);
756
757     return(0);
758 }
759
760 /***EOF***/