7d70ceeec10780b709eb7c61c27722679993e026
[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 print_proto(const int proto)
37 {
38     switch (proto) {
39         case FKO_PROTO_UDP:
40             printf("udp");
41             break;
42         case FKO_PROTO_TCP_RAW:
43             printf("tcpraw");
44             break;
45         case FKO_PROTO_TCP:
46             printf("tcp");
47             break;
48         case FKO_PROTO_ICMP:
49             printf("icmp");
50             break;
51         case FKO_PROTO_HTTP:
52             printf("http");
53             break;
54     }
55     return;
56 }
57
58 static void
59 dump_transmit_options(const fko_cli_options_t *options)
60 {
61     printf("Generating SPA packet:\n    protocol: ");
62     print_proto(options->spa_proto),
63     printf("\n        port: %d\n", options->spa_dst_port);
64     printf("     IP/host: %s\n", options->spa_server_str);
65     return;
66 }
67
68 /* Function to generate a header checksum.
69 */
70 unsigned short
71 chksum(unsigned short *buf, int nbytes)
72 {
73     unsigned int   sum;
74     unsigned short oddbyte;
75
76     sum = 0;
77     while (nbytes > 1)
78     {
79         sum += *buf++;
80         nbytes -= 2;
81     }
82
83     if (nbytes == 1)
84     {
85         oddbyte = 0;
86         *((unsigned short *) &oddbyte) = *(unsigned short *) buf;
87         sum += oddbyte;
88     }
89
90     sum = (sum >> 16) + (sum & 0xffff);
91     sum += (sum >> 16);
92
93     return (unsigned short) ~sum;
94 }
95
96 /*
97 static int is_ip(char *str)
98 {
99     int rv = 1;
100     unsigned int i;
101
102     for (i=0; i < strlen(str); i++) {
103         if (str[i] != '.' && ! isdigit(str[i])) {
104             rv = 0;
105             break;
106         }
107     }
108
109     return rv;
110 }
111 */
112
113 /* Send the SPA data via UDP packet.
114 */
115 static int
116 send_spa_packet_tcp_or_udp(const char *spa_data, const int sd_len,
117     const fko_cli_options_t *options)
118 {
119     int     sock, res=0, error;
120     struct  addrinfo *result, *rp, hints;
121     char    port_str[MAX_PORT_STR_LEN];
122
123     if (options->test)
124     {
125         fprintf(stderr,
126             "test mode enabled, SPA packet not actually sent.\n");
127         return res;
128     }
129
130     memset(&hints, 0, sizeof(struct addrinfo));
131
132     hints.ai_family   = AF_UNSPEC; /* Allow IPv4 or IPv6 */
133
134     if (options->spa_proto == FKO_PROTO_UDP)
135     {
136         /* Send the SPA data packet via an single UDP packet - this is the
137          * most common usage.
138         */
139         hints.ai_socktype = SOCK_DGRAM;
140         hints.ai_protocol = IPPROTO_UDP;
141     }
142     else
143     {
144         /* Send the SPA data packet via an established TCP connection.
145         */
146         hints.ai_socktype = SOCK_STREAM;
147         hints.ai_protocol = IPPROTO_TCP;
148     }
149
150     sprintf(port_str, "%d", options->spa_dst_port);
151
152     error = getaddrinfo(options->spa_server_str, port_str, &hints, &result);
153
154     if (error != 0)
155     {
156         fprintf(stderr, "error in getaddrinfo: %s\n", gai_strerror(error));
157         exit(EXIT_FAILURE);
158     }
159
160     for (rp = result; rp != NULL; rp = rp->ai_next) {
161         sock = socket(rp->ai_family, rp->ai_socktype,
162                 rp->ai_protocol);
163         if (sock < 0)
164             continue;
165
166         if ((error = (connect(sock, rp->ai_addr, rp->ai_addrlen) != -1)))
167             break;  /* made it */
168
169 #ifdef WIN32
170         closesocket(sock);
171 #else
172         close(sock);
173 #endif
174     }
175
176     if (rp == NULL) {
177         perror("send_spa_packet_tcp_or_udp: Could not create socket: ");
178         exit(EXIT_FAILURE);
179     }
180
181     freeaddrinfo(result);
182
183     res = send(sock, spa_data, sd_len, 0);
184
185     if(res < 0)
186     {
187         perror("send_spa_packet_tcp_or_udp: write error: ");
188     }
189     else if(res != sd_len)
190     {
191         fprintf(stderr,
192             "[#] Warning: bytes sent (%i) not spa data length (%i).\n",
193             res, sd_len
194         );
195     }
196
197 #ifdef WIN32
198     closesocket(sock);
199 #else
200     close(sock);
201 #endif
202
203     return(res);
204 }
205
206 /* Send the SPA data via raw TCP packet.
207 */
208 static int
209 send_spa_packet_tcp_raw(const char *spa_data, const int sd_len,
210     const struct sockaddr_in *saddr, const struct sockaddr_in *daddr,
211     const fko_cli_options_t *options)
212 {
213 #ifdef WIN32
214     fprintf(stderr,
215         "send_spa_packet_tcp_raw: raw packets are not yet supported.\n");
216     return(-1);
217 #else
218     int  sock, res = 0;
219     char pkt_data[2048] = {0}; /* Should be enough for our purposes */
220
221     struct iphdr  *iph  = (struct iphdr *) pkt_data;
222     struct tcphdr *tcph = (struct tcphdr *) (pkt_data + sizeof (struct iphdr));
223
224     int hdrlen = sizeof(struct iphdr) + sizeof(struct tcphdr);
225
226     /* Values for setsockopt.
227     */
228     int         one     = 1;
229     const int  *so_val  = &one;
230
231     if (options->test)
232     {
233         fprintf(stderr,
234             "test mode enabled, SPA packet not actually sent.\n");
235         return res;
236     }
237
238     sock = socket (PF_INET, SOCK_RAW, IPPROTO_RAW);
239     if (sock < 0)
240     {
241         perror("send_spa_packet_tcp_raw: create socket: ");
242         return(sock);
243     }
244
245     /* Put the spa data in place.
246     */
247     memcpy((pkt_data + hdrlen), spa_data, sd_len);
248
249     /* Construct our own header by filling in the ip/tcp header values,
250      * starting with the IP header values.
251     */
252     iph->ihl        = 5;
253     iph->version    = 4;
254     iph->tos        = 0;
255     /* Total size is header plus payload */
256     iph->tot_len    = hdrlen + sd_len;
257     /* The value here does not matter */
258     iph->id         = random() & 0xffff;
259     iph->frag_off   = 0;
260     iph->ttl        = 255;
261     iph->protocol   = IPPROTO_TCP;
262     iph->check      = 0;
263     iph->saddr      = saddr->sin_addr.s_addr;
264     iph->daddr      = daddr->sin_addr.s_addr;
265
266     /* Now the TCP header values.
267     */
268     tcph->source    = saddr->sin_port;
269     tcph->dest      = daddr->sin_port;
270     tcph->seq       = htonl(1);
271     tcph->ack_seq   = 0;
272     tcph->doff      = 5;
273     tcph->res1      = 0;
274     /* TCP flags */
275     tcph->fin       = 0;
276     tcph->syn       = 1;
277     tcph->rst       = 0;
278     tcph->psh       = 0;
279     tcph->ack       = 0;
280     tcph->urg       = 0;
281
282     tcph->res2      = 0;
283     tcph->window    = htons(32767);
284     tcph->check     = 0;
285     tcph->urg_ptr   = 0;
286
287     /* No we can compute our checksum.
288     */
289     iph->check = chksum((unsigned short *)pkt_data, iph->tot_len);
290
291     /* Make sure the kernel knows the header is included in the data so it
292      * doesn't try to insert its own header into the packet.
293     */
294     if (setsockopt (sock, IPPROTO_IP, IP_HDRINCL, so_val, sizeof(one)) < 0)
295         perror("send_spa_packet_tcp_raw: setsockopt HDRINCL: ");
296
297     res = sendto (sock, pkt_data, iph->tot_len, 0,
298         (struct sockaddr *)daddr, sizeof(*daddr));
299
300     if(res < 0)
301     {
302         perror("send_spa_packet_tcp_raw: sendto error: ");
303     }
304     else if(res != sd_len + hdrlen) /* account for the header ?*/
305     {
306         fprintf(stderr,
307             "[#] Warning: bytes sent (%i) not spa data length (%i).\n",
308             res, sd_len
309         );
310     }
311
312     close(sock);
313
314     return(res);
315
316 #endif /* !WIN32 */
317 }
318
319 /* Send the SPA data via ICMP packet.
320 */
321 static int
322 send_spa_packet_icmp(const char *spa_data, const int sd_len,
323     const struct sockaddr_in *saddr, const struct sockaddr_in *daddr,
324     const fko_cli_options_t *options)
325 {
326 #ifdef WIN32
327     fprintf(stderr, "send_spa_packet_icmp: raw packets are not yet supported.\n");
328     return(-1);
329 #else
330     int res = 0, sock;
331     char pkt_data[2048] = {0};
332
333     struct iphdr  *iph    = (struct iphdr *) pkt_data;
334     struct icmphdr *icmph = (struct icmphdr *) (pkt_data + sizeof (struct iphdr));
335
336     int hdrlen = sizeof(struct iphdr) + sizeof(struct icmphdr);
337
338     /* Values for setsockopt.
339     */
340     int         one     = 1;
341     const int  *so_val  = &one;
342
343     if (options->test)
344     {
345         fprintf(stderr,
346             "test mode enabled, SPA packet not actually sent.\n");
347         return res;
348     }
349
350     sock = socket (PF_INET, SOCK_RAW, IPPROTO_RAW);
351
352     if (sock < 0)
353     {
354         perror("send_spa_packet_icmp: create socket: ");
355         return(sock);
356     }
357
358     /* Put the spa data in place.
359     */
360     memcpy((pkt_data + hdrlen), spa_data, sd_len);
361
362     /* Construct our own header by filling in the ip/icmp header values,
363      * starting with the IP header values.
364     */
365     iph->ihl        = 5;
366     iph->version    = 4;
367     iph->tos        = 0;
368     /* Total size is header plus payload */
369     iph->tot_len    = hdrlen + sd_len;
370     /* The value here does not matter */
371     iph->id         = random() & 0xffff;
372     iph->frag_off   = 0;
373     iph->ttl        = 255;
374     iph->protocol   = IPPROTO_ICMP;
375     iph->check      = 0;
376     iph->saddr      = saddr->sin_addr.s_addr;
377     iph->daddr      = daddr->sin_addr.s_addr;
378
379     /* Now the ICMP header values.
380     */
381     icmph->type     = ICMP_ECHOREPLY; /* Make it an echo reply */
382     icmph->code     = 0;
383     icmph->checksum = 0;
384
385     /* No we can compute our checksum.
386     */
387     iph->check = chksum((unsigned short *)pkt_data, iph->tot_len);
388     icmph->checksum = chksum((unsigned short *)icmph, sizeof(struct icmphdr) + sd_len);
389
390     /* Make sure the kernel knows the header is included in the data so it
391      * doesn't try to insert its own header into the packet.
392     */
393     if (setsockopt (sock, IPPROTO_IP, IP_HDRINCL, so_val, sizeof(one)) < 0)
394         perror("send_spa_packet_icmp: setsockopt HDRINCL: ");
395
396     res = sendto (sock, pkt_data, iph->tot_len, 0,
397         (struct sockaddr *)daddr, sizeof(*daddr));
398
399     if(res < 0)
400     {
401         perror("send_spa_packet_icmp: sendto error: ");
402     }
403     else if(res != sd_len + hdrlen) /* account for icmp header */
404     {
405         fprintf(stderr, "[#] Warning: bytes sent (%i) not spa data length (%i).\n",
406             res, sd_len);
407     }
408
409     close(sock);
410
411     return(res);
412
413 #endif /* !WIN32 */
414 }
415
416 /* Send the SPA data packet via an HTTP request
417 */
418 static int
419 send_spa_packet_http(const char *spa_data, const int sd_len,
420     fko_cli_options_t *options)
421 {
422     char http_buf[HTTP_MAX_REQUEST_LEN], *spa_data_copy = NULL;
423     char *ndx = options->http_proxy;
424     int  i, proxy_port = 0;
425
426     spa_data_copy = malloc(sd_len+1);
427     if (spa_data_copy == NULL)
428     {
429         exit(EXIT_FAILURE);
430     }
431     memcpy(spa_data_copy, spa_data, sd_len+1);
432
433     /* Change "+" to "-", and "/" to "_" for HTTP requests (the server
434      * side will translate these back before decrypting)
435     */
436     for (i=0; i < sd_len; i++) {
437         if (spa_data_copy[i] == '+') {
438             spa_data_copy[i] = '-';
439         }
440         else if (spa_data_copy[i] == '/') {
441             spa_data_copy[i] = '_';
442         }
443     }
444
445     if(options->http_proxy[0] == 0x0)
446     {
447         snprintf(http_buf, HTTP_MAX_REQUEST_LEN,
448             "GET /%s HTTP/1.0\r\nUser-Agent: %s\r\nAccept: */*\r\n"
449             "Host: %s\r\nConnection: close\r\n\r\n",
450             spa_data_copy,
451             options->http_user_agent,
452             options->spa_server_str  /* hostname or IP */
453         );
454     }
455     else /* we are sending the SPA packet through an HTTP proxy */
456     {
457         /* Extract the hostname if it was specified as a URL. Actually,
458          * we just move the start of the hostname to the begining of the
459          * original string.
460         */
461         if(strncasecmp(ndx, "http://", 7) == 0)
462             memmove(ndx, ndx+7, strlen(ndx)+1);
463
464         /* If there is a colon assume the proxy hostame or IP is on the left
465          * and the proxy port is on the right. So we make the : a \0 and 
466          * extract the port value.
467         */
468         ndx = strchr(options->http_proxy, ':');
469         if(ndx)
470         {
471             *ndx = '\0';
472             proxy_port = atoi(ndx+1);
473         }
474
475         /* If we have a valid port value, use it.
476         */
477         if(proxy_port)
478             options->spa_dst_port = proxy_port;
479
480         snprintf(http_buf, HTTP_MAX_REQUEST_LEN,
481             "GET http://%s/%s HTTP/1.0\r\nUser-Agent: %s\r\nAccept: */*\r\n"
482             "Host: %s\r\nConnection: close\r\n\r\n",
483             options->spa_server_str,
484             spa_data_copy,
485             options->http_user_agent,
486             options->http_proxy  /* hostname or IP */
487         );
488         strlcpy(options->spa_server_str, options->http_proxy, MAX_SERVER_STR_LEN);
489     }
490     free(spa_data_copy);
491
492     if (options->test)
493     {
494         if (options->verbose)
495            fprintf(stderr, "%s\n", http_buf);
496
497         fprintf(stderr,
498             "Test mode enabled, SPA packet not actually sent.\n");
499         return 0;
500     }
501
502     return send_spa_packet_tcp_or_udp(http_buf, strlen(http_buf), options);
503 }
504
505 /* Function used to send the SPA data.
506 */
507 int
508 send_spa_packet(fko_ctx_t ctx, fko_cli_options_t *options)
509 {
510     int                 res, sd_len;
511     char               *spa_data;
512
513     struct sockaddr_in  saddr, daddr;
514
515 #ifdef WIN32
516     WSADATA wsa_data;
517 #endif
518
519     /* Get our spa data here.
520     */
521     res = fko_get_spa_data(ctx, &spa_data);
522
523     if(res != FKO_SUCCESS)
524     {
525         fprintf(stderr,
526             "send_spa_packet: Error #%i from fko_get_spa_data: %s\n",
527             res, fko_errstr(res)
528         );
529         return(-1);
530     }
531
532     sd_len = strlen(spa_data);
533
534 #ifdef WIN32
535     /* Winsock needs to be initialized...
536     */
537     res = WSAStartup( MAKEWORD(1,1), &wsa_data );
538     if( res != 0 )
539     {
540         fprintf(stderr, "Winsock initialization error %d\n", res );
541         return(-1);
542     }
543 #endif
544
545     errno = 0;
546
547     if (options->verbose)
548         dump_transmit_options(options);
549
550     if (options->spa_proto == FKO_PROTO_TCP || options->spa_proto == FKO_PROTO_UDP)
551     {
552         res = send_spa_packet_tcp_or_udp(spa_data, sd_len, options);
553     }
554     else if (options->spa_proto == FKO_PROTO_HTTP)
555     {
556         res = send_spa_packet_http(spa_data, sd_len, options);
557     }
558     else if (options->spa_proto == FKO_PROTO_TCP_RAW
559             || options->spa_proto == FKO_PROTO_ICMP)
560     {
561         memset(&saddr, 0, sizeof(saddr));
562         memset(&daddr, 0, sizeof(daddr));
563
564         saddr.sin_family = AF_INET;
565         daddr.sin_family = AF_INET;
566
567         /* Set source address and port
568         */
569         if (options->spa_src_port)
570             saddr.sin_port = htons(options->spa_src_port);
571         else
572             saddr.sin_port = INADDR_ANY;  /* default */
573
574         if (options->spoof_ip_src_str[0] != 0x00) {
575             saddr.sin_addr.s_addr = inet_addr(options->spoof_ip_src_str);
576         } else
577             saddr.sin_addr.s_addr = INADDR_ANY;  /* default */
578
579         if (saddr.sin_addr.s_addr == -1)
580         {
581             fprintf(stderr, "Could not set source IP.\n");
582             exit(EXIT_FAILURE);
583         }
584
585         /* Set destination address and port
586         */
587         daddr.sin_port = htons(options->spa_dst_port);
588         daddr.sin_addr.s_addr = inet_addr(options->spa_server_str);
589
590         if (daddr.sin_addr.s_addr == -1)
591         {
592             fprintf(stderr, "Could not set destination IP.\n");
593             exit(EXIT_FAILURE);
594         }
595
596         if (options->spa_proto == FKO_PROTO_TCP_RAW)
597         {
598             res = send_spa_packet_tcp_raw(spa_data, sd_len, &saddr, &daddr, options);
599         }
600         else
601         {
602             res = send_spa_packet_icmp(spa_data, sd_len, &saddr, &daddr, options);
603         }
604     }
605     else
606     {
607         /* --DSS XXX: What to we really want to do here? */
608         fprintf(stderr, "%i is not a valid or supported protocol.\n",
609             options->spa_proto);
610         res = -1;
611     }
612
613     return res;
614 }
615
616 /* Function to write SPA packet data to the filesystem
617 */
618 int write_spa_packet_data(fko_ctx_t ctx, const fko_cli_options_t *options)
619 {
620     FILE   *fp;
621     char   *spa_data;
622     int     res;
623
624     res = fko_get_spa_data(ctx, &spa_data);
625
626     if(res != FKO_SUCCESS)
627     {
628         fprintf(stderr,
629             "write_spa_packet_data: Error #%i from fko_get_spa_data: %s\n",
630             res, fko_errstr(res)
631         );
632
633         return(-1);
634     }
635
636     if (options->save_packet_file_append)
637     {
638         fp = fopen(options->save_packet_file, "a");
639     }
640     else
641     {
642         unlink(options->save_packet_file);
643         fp = fopen(options->save_packet_file, "w");
644     }
645
646     if(fp == NULL)
647     {
648         perror("write_spa_packet_data: ");
649         return(-1);
650     }
651
652     fprintf(fp, "%s\n",
653         (spa_data == NULL) ? "<NULL>" : spa_data);
654
655     fclose(fp);
656
657     return(0);
658 }
659
660 /***EOF***/