Fix resource leaks found by GCC -fanalyzer
[tinc] / src / sptps_test.c
1 /*
2     sptps_test.c -- Simple Peer-to-Peer Security test program
3     Copyright (C) 2011-2022 Guus Sliepen <guus@tinc-vpn.org>
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License along
16     with this program; if not, write to the Free Software Foundation, Inc.,
17     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "system.h"
21
22 #ifdef HAVE_LINUX
23 #include <linux/if_tun.h>
24 #endif
25
26 #include "crypto.h"
27 #include "ecdsa.h"
28 #include "meta.h"
29 #include "protocol.h"
30 #include "sptps.h"
31 #include "utils.h"
32 #include "names.h"
33 #include "random.h"
34
35 #ifndef HAVE_WINDOWS
36 #define closesocket(s) close(s)
37 #endif
38
39 // Symbols necessary to link with logger.o
40 bool send_request(struct connection_t *c, const char *msg, ...) {
41         (void)c;
42         (void)msg;
43         return false;
44 }
45
46 list_t connection_list;
47
48 bool send_meta(struct connection_t *c, const void *msg, size_t len) {
49         (void)c;
50         (void)msg;
51         (void)len;
52         return false;
53 }
54
55 bool do_detach = false;
56 struct timeval now;
57
58 static bool special;
59 static bool verbose;
60 static bool readonly;
61 static bool writeonly;
62 static int in = 0;
63 static int out = 1;
64 int addressfamily = AF_UNSPEC;
65
66 static bool send_data(void *handle, uint8_t type, const void *data, size_t len) {
67         (void)type;
68         char *hex = alloca(len * 2 + 1);
69         bin2hex(data, hex, len);
70
71         if(verbose) {
72                 fprintf(stderr, "Sending %lu bytes of data:\n%s\n", (unsigned long)len, hex);
73         }
74
75         const int *sock = handle;
76         const char *p = data;
77
78         while(len) {
79                 ssize_t sent = send(*sock, p, len, 0);
80
81                 if(sent <= 0) {
82                         fprintf(stderr, "Error sending data: %s\n", strerror(errno));
83                         return false;
84                 }
85
86                 p += sent;
87                 len -= sent;
88         }
89
90         return true;
91 }
92
93 static bool receive_record(void *handle, uint8_t type, const void *data, uint16_t len) {
94         (void)handle;
95
96         if(verbose) {
97                 fprintf(stderr, "Received type %d record of %u bytes:\n", type, len);
98         }
99
100         if(writeonly) {
101                 return true;
102         }
103
104         const char *p = data;
105
106         while(len) {
107                 ssize_t written = write(out, p, len);
108
109                 if(written <= 0) {
110                         fprintf(stderr, "Error writing received data: %s\n", strerror(errno));
111                         return false;
112                 }
113
114                 p += written;
115                 len -= written;
116         }
117
118         return true;
119 }
120
121 typedef enum option_t {
122         OPT_BAD_OPTION    = '?',
123         OPT_LONG_OPTION   =  0,
124
125         // Short options
126         OPT_DATAGRAM      = 'd',
127         OPT_QUIT_ON_EOF   = 'q',
128         OPT_READONLY      = 'r',
129         OPT_WRITEONLY     = 'w',
130         OPT_PACKET_LOSS   = 'L',
131         OPT_REPLAY_WINDOW = 'W',
132         OPT_SPECIAL_CHAR  = 's',
133         OPT_TUN           = 't',
134         OPT_VERBOSE       = 'v',
135         OPT_IPV4          = '4',
136         OPT_IPV6          = '6',
137
138         // Long options
139         OPT_HELP          = 255,
140 } option_t;
141
142 static struct option const long_options[] = {
143         {"datagram",      no_argument,       NULL, OPT_DATAGRAM},
144         {"quit",          no_argument,       NULL, OPT_QUIT_ON_EOF},
145         {"readonly",      no_argument,       NULL, OPT_READONLY},
146         {"writeonly",     no_argument,       NULL, OPT_WRITEONLY},
147         {"packet-loss",   required_argument, NULL, OPT_PACKET_LOSS},
148         {"replay-window", required_argument, NULL, OPT_REPLAY_WINDOW},
149         {"special",       no_argument,       NULL, OPT_SPECIAL_CHAR},
150         {"tun",           no_argument,       NULL, OPT_TUN},
151         {"verbose",       required_argument, NULL, OPT_VERBOSE},
152         {"help",          no_argument,       NULL, OPT_HELP},
153         {NULL,            0,                 NULL, 0}
154 };
155
156 static void usage(void) {
157         static const char *message =
158                 "Usage: %s [options] my_ed25519_key_file his_ed25519_key_file [host] port\n"
159                 "\n"
160                 "Valid options are:\n"
161                 "  -d, --datagram          Enable datagram mode.\n"
162                 "  -q, --quit              Quit when EOF occurs on stdin.\n"
163                 "  -r, --readonly          Only send data from the socket to stdout.\n"
164 #ifdef HAVE_LINUX
165                 "  -t, --tun               Use a tun device instead of stdio.\n"
166 #endif
167                 "  -w, --writeonly         Only send data from stdin to the socket.\n"
168                 "  -L, --packet-loss RATE  Fake packet loss of RATE percent.\n"
169                 "  -R, --replay-window N   Set replay window to N bytes.\n"
170                 "  -s, --special           Enable special handling of lines starting with #, ^ and $.\n"
171                 "  -v, --verbose           Display debug messages.\n"
172                 "  -4                      Use IPv4.\n"
173                 "  -6                      Use IPv6.\n"
174                 "\n"
175                 "Report bugs to tinc@tinc-vpn.org.\n";
176
177         fprintf(stderr, message, program_name);
178 }
179
180 #ifdef HAVE_WINDOWS
181
182 int stdin_sock_fd = -1;
183
184 // Windows does not allow calling select() on anything but sockets. Therefore,
185 // to keep the same code as on other operating systems, we have to put a
186 // separate thread between the stdin and the sptps loop way below. This thread
187 // reads stdin and sends its content to the main thread through a TCP socket,
188 // which can be properly select()'ed.
189 static DWORD WINAPI stdin_reader_thread(LPVOID arg) {
190         struct sockaddr_in sa;
191         socklen_t sa_size = sizeof(sa);
192
193         while(true) {
194                 int peer_fd = accept(stdin_sock_fd, (struct sockaddr *) &sa, &sa_size);
195
196                 if(peer_fd < 0) {
197                         fprintf(stderr, "accept() failed: %s\n", strerror(errno));
198                         continue;
199                 }
200
201                 if(verbose) {
202                         fprintf(stderr, "New connection received from :%d\n", ntohs(sa.sin_port));
203                 }
204
205                 char buf[1024];
206                 ssize_t nread;
207
208                 while((nread = read(STDIN_FILENO, buf, sizeof(buf))) > 0) {
209                         if(verbose) {
210                                 fprintf(stderr, "Read %lld bytes from input\n", nread);
211                         }
212
213                         char *start = buf;
214                         ssize_t nleft = nread;
215
216                         while(nleft) {
217                                 ssize_t nsend = send(peer_fd, start, nleft, 0);
218
219                                 if(nsend < 0) {
220                                         if(sockwouldblock(sockerrno)) {
221                                                 continue;
222                                         }
223
224                                         break;
225                                 }
226
227                                 start += nsend;
228                                 nleft -= nsend;
229                         }
230
231                         if(nleft) {
232                                 fprintf(stderr, "Could not send data: %s\n", strerror(errno));
233                                 break;
234                         }
235
236                         if(verbose) {
237                                 fprintf(stderr, "Sent %lld bytes to peer\n", nread);
238                         }
239                 }
240
241                 closesocket(peer_fd);
242         }
243
244         closesocket(stdin_sock_fd);
245         stdin_sock_fd = -1;
246         return 0;
247 }
248
249 static int start_input_reader(void) {
250         if(stdin_sock_fd != -1) {
251                 fprintf(stderr, "stdin thread can only be started once.\n");
252                 return -1;
253         }
254
255         stdin_sock_fd = socket(AF_INET, SOCK_STREAM, 0);
256
257         if(stdin_sock_fd < 0) {
258                 fprintf(stderr, "Could not create server socket: %s\n", strerror(errno));
259                 return -1;
260         }
261
262         struct sockaddr_in serv_sa;
263
264         memset(&serv_sa, 0, sizeof(serv_sa));
265
266         serv_sa.sin_family = AF_INET;
267
268         serv_sa.sin_addr.s_addr = htonl(0x7f000001); // 127.0.0.1
269
270         int res = bind(stdin_sock_fd, (struct sockaddr *)&serv_sa, sizeof(serv_sa));
271
272         if(res < 0) {
273                 fprintf(stderr, "Could not bind socket: %s\n", strerror(errno));
274                 goto server_err;
275         }
276
277         if(listen(stdin_sock_fd, 1) < 0) {
278                 fprintf(stderr, "Could not listen: %s\n", strerror(errno));
279                 goto server_err;
280         }
281
282         struct sockaddr_in connect_sa;
283
284         socklen_t addr_len = sizeof(connect_sa);
285
286         if(getsockname(stdin_sock_fd, (struct sockaddr *)&connect_sa, &addr_len) < 0) {
287                 fprintf(stderr, "Could not determine the address of the stdin thread socket\n");
288                 goto server_err;
289         }
290
291         if(verbose) {
292                 fprintf(stderr, "stdin thread is listening on :%d\n", ntohs(connect_sa.sin_port));
293         }
294
295         if(!CreateThread(NULL, 0, stdin_reader_thread, NULL, 0, NULL)) {
296                 fprintf(stderr, "Could not start reader thread: %d\n", GetLastError());
297                 goto server_err;
298         }
299
300         int client_fd = socket(AF_INET, SOCK_STREAM, 0);
301
302         if(client_fd < 0) {
303                 fprintf(stderr, "Could not create client socket: %s\n", strerror(errno));
304                 return -1;
305         }
306
307         if(connect(client_fd, (struct sockaddr *)&connect_sa, sizeof(connect_sa)) < 0) {
308                 fprintf(stderr, "Could not connect: %s\n", strerror(errno));
309                 closesocket(client_fd);
310                 return -1;
311         }
312
313         return client_fd;
314
315 server_err:
316
317         if(stdin_sock_fd != -1) {
318                 closesocket(stdin_sock_fd);
319                 stdin_sock_fd = -1;
320         }
321
322         return -1;
323 }
324
325 #endif // HAVE_WINDOWS
326
327 static void print_listening_msg(int sock) {
328         sockaddr_t sa = {0};
329         socklen_t salen = sizeof(sa);
330         int port = 0;
331
332         if(!getsockname(sock, &sa.sa, &salen)) {
333                 port = ntohs(sa.in.sin_port);
334         }
335
336         fprintf(stderr, "Listening on %d...\n", port);
337         fflush(stderr);
338 }
339
340 static int run_test(int argc, char *argv[]) {
341         program_name = argv[0];
342         bool initiator = false;
343         bool datagram = false;
344 #ifdef HAVE_LINUX
345         bool tun = false;
346 #endif
347         int packetloss = 0;
348         int r;
349         int option_index = 0;
350         bool quit = false;
351
352         while((r = getopt_long(argc, argv, "dqrstwL:W:v46", long_options, &option_index)) != EOF) {
353                 switch((option_t) r) {
354                 case OPT_LONG_OPTION:
355                         break;
356
357                 case OPT_BAD_OPTION:
358                         usage();
359                         return 1;
360
361                 case OPT_DATAGRAM:
362                         datagram = true;
363                         break;
364
365                 case OPT_QUIT_ON_EOF:
366                         quit = true;
367                         break;
368
369                 case OPT_READONLY:
370                         readonly = true;
371                         break;
372
373                 case OPT_TUN:
374 #ifdef HAVE_LINUX
375                         tun = true;
376 #else
377                         fprintf(stderr, "--tun is only supported on Linux.\n");
378                         usage();
379                         return 1;
380 #endif
381                         break;
382
383                 case OPT_WRITEONLY:
384                         writeonly = true;
385                         break;
386
387                 case OPT_PACKET_LOSS:
388                         packetloss = atoi(optarg);
389                         break;
390
391                 case OPT_REPLAY_WINDOW:
392                         sptps_replaywin = atoi(optarg);
393                         break;
394
395                 case OPT_VERBOSE:
396                         verbose = true;
397                         break;
398
399                 case OPT_SPECIAL_CHAR:
400                         special = true;
401                         break;
402
403                 case OPT_IPV4:
404                         addressfamily = AF_INET;
405                         break;
406
407                 case OPT_IPV6:
408                         addressfamily = AF_INET6;
409                         break;
410
411                 case OPT_HELP:
412                         usage();
413                         return 0;
414
415                 default:
416                         break;
417                 }
418         }
419
420         argc -= optind - 1;
421         argv += optind - 1;
422
423         if(argc < 4 || argc > 5) {
424                 fprintf(stderr, "Wrong number of arguments.\n");
425                 usage();
426                 return 1;
427         }
428
429         if(argc > 4) {
430                 initiator = true;
431         }
432
433 #ifdef HAVE_LINUX
434
435         if(tun) {
436                 in = out = open("/dev/net/tun", O_RDWR | O_NONBLOCK);
437
438                 if(in < 0) {
439                         fprintf(stderr, "Could not open tun device: %s\n", strerror(errno));
440                         return 1;
441                 }
442
443                 struct ifreq ifr = {
444                         .ifr_flags = IFF_TUN
445                 };
446
447                 if(ioctl(in, TUNSETIFF, &ifr)) {
448                         fprintf(stderr, "Could not configure tun interface: %s\n", strerror(errno));
449                         return 1;
450                 }
451
452                 ifr.ifr_name[IFNAMSIZ - 1] = 0;
453                 fprintf(stderr, "Using tun interface %s\n", ifr.ifr_name);
454         }
455
456 #endif
457
458 #ifdef HAVE_WINDOWS
459         static struct WSAData wsa_state;
460
461         if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
462                 return 1;
463         }
464
465 #endif
466
467         struct addrinfo *ai, hint;
468         memset(&hint, 0, sizeof(hint));
469
470         hint.ai_family = addressfamily;
471         hint.ai_socktype = datagram ? SOCK_DGRAM : SOCK_STREAM;
472         hint.ai_protocol = datagram ? IPPROTO_UDP : IPPROTO_TCP;
473         hint.ai_flags = initiator ? 0 : AI_PASSIVE;
474
475         if(getaddrinfo(initiator ? argv[3] : NULL, initiator ? argv[4] : argv[3], &hint, &ai) || !ai) {
476                 fprintf(stderr, "getaddrinfo() failed: %s\n", sockstrerror(sockerrno));
477                 return 1;
478         }
479
480         int sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
481
482         if(sock < 0) {
483                 fprintf(stderr, "Could not create socket: %s\n", sockstrerror(sockerrno));
484                 freeaddrinfo(ai);
485                 return 1;
486         }
487
488         int one = 1;
489         setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&one, sizeof(one));
490
491         if(initiator) {
492                 int res = connect(sock, ai->ai_addr, ai->ai_addrlen);
493
494                 freeaddrinfo(ai);
495                 ai = NULL;
496
497                 if(res) {
498                         fprintf(stderr, "Could not connect to peer: %s\n", sockstrerror(sockerrno));
499                         return 1;
500                 }
501
502                 fprintf(stderr, "Connected\n");
503         } else {
504                 int res = bind(sock, ai->ai_addr, ai->ai_addrlen);
505
506                 freeaddrinfo(ai);
507                 ai = NULL;
508
509                 if(res) {
510                         fprintf(stderr, "Could not bind socket: %s\n", sockstrerror(sockerrno));
511                         return 1;
512                 }
513
514                 if(!datagram) {
515                         if(listen(sock, 1)) {
516                                 fprintf(stderr, "Could not listen on socket: %s\n", sockstrerror(sockerrno));
517                                 return 1;
518                         }
519
520                         print_listening_msg(sock);
521
522                         sock = accept(sock, NULL, NULL);
523
524                         if(sock < 0) {
525                                 fprintf(stderr, "Could not accept connection: %s\n", sockstrerror(sockerrno));
526                                 return 1;
527                         }
528                 } else {
529                         print_listening_msg(sock);
530
531                         char buf[65536];
532                         struct sockaddr addr;
533                         socklen_t addrlen = sizeof(addr);
534
535                         if(recvfrom(sock, buf, sizeof(buf), MSG_PEEK, &addr, &addrlen) <= 0) {
536                                 fprintf(stderr, "Could not read from socket: %s\n", sockstrerror(sockerrno));
537                                 return 1;
538                         }
539
540                         if(connect(sock, &addr, addrlen)) {
541                                 fprintf(stderr, "Could not accept connection: %s\n", sockstrerror(sockerrno));
542                                 return 1;
543                         }
544                 }
545
546                 fprintf(stderr, "Connected\n");
547         }
548
549         FILE *fp = fopen(argv[1], "r");
550
551         if(!fp) {
552                 fprintf(stderr, "Could not open %s: %s\n", argv[1], strerror(errno));
553                 return 1;
554         }
555
556         ecdsa_t *mykey = NULL;
557
558         if(!(mykey = ecdsa_read_pem_private_key(fp))) {
559                 return 1;
560         }
561
562         fclose(fp);
563
564         fp = fopen(argv[2], "r");
565
566         if(!fp) {
567                 fprintf(stderr, "Could not open %s: %s\n", argv[2], strerror(errno));
568                 ecdsa_free(mykey);
569                 return 1;
570         }
571
572         ecdsa_t *hiskey = NULL;
573
574         if(!(hiskey = ecdsa_read_pem_public_key(fp))) {
575                 ecdsa_free(mykey);
576                 return 1;
577         }
578
579         fclose(fp);
580
581         if(verbose) {
582                 fprintf(stderr, "Keys loaded\n");
583         }
584
585         sptps_t s;
586
587         if(!sptps_start(&s, &sock, initiator, datagram, mykey, hiskey, "sptps_test", 10, send_data, receive_record)) {
588                 ecdsa_free(mykey);
589                 ecdsa_free(hiskey);
590                 return 1;
591         }
592
593 #ifdef HAVE_WINDOWS
594
595         if(!readonly) {
596                 in = start_input_reader();
597
598                 if(in < 0) {
599                         fprintf(stderr, "Could not init stdin reader thread\n");
600                         ecdsa_free(mykey);
601                         ecdsa_free(hiskey);
602                         return 1;
603                 }
604         }
605
606 #endif
607
608         int max_fd = MAX(sock, in);
609
610         while(true) {
611                 if(writeonly && readonly) {
612                         break;
613                 }
614
615                 char buf[65535] = "";
616                 size_t readsize = datagram ? 1460u : sizeof(buf);
617
618                 fd_set fds;
619                 FD_ZERO(&fds);
620
621                 if(!readonly && s.instate) {
622                         FD_SET(in, &fds);
623                 }
624
625                 FD_SET(sock, &fds);
626
627                 if(select(max_fd + 1, &fds, NULL, NULL, NULL) <= 0) {
628                         ecdsa_free(mykey);
629                         ecdsa_free(hiskey);
630                         return 1;
631                 }
632
633                 if(FD_ISSET(in, &fds)) {
634 #ifdef HAVE_WINDOWS
635                         ssize_t len = recv(in, buf, readsize, 0);
636 #else
637                         ssize_t len = read(in, buf, readsize);
638 #endif
639
640                         if(len < 0) {
641                                 fprintf(stderr, "Could not read from stdin: %s\n", strerror(errno));
642                                 ecdsa_free(mykey);
643                                 ecdsa_free(hiskey);
644                                 return 1;
645                         }
646
647                         if(len == 0) {
648 #ifdef HAVE_WINDOWS
649                                 shutdown(in, SD_SEND);
650                                 closesocket(in);
651 #endif
652
653                                 if(quit) {
654                                         break;
655                                 }
656
657                                 readonly = true;
658                                 continue;
659                         }
660
661                         if(special && buf[0] == '#') {
662                                 s.outseqno = atoi(buf + 1);
663                         }
664
665                         if(special && buf[0] == '^') {
666                                 sptps_send_record(&s, SPTPS_HANDSHAKE, NULL, 0);
667                         } else if(special && buf[0] == '$') {
668                                 sptps_force_kex(&s);
669
670                                 if(len > 1) {
671                                         sptps_send_record(&s, 0, buf, len);
672                                 }
673                         } else if(!sptps_send_record(&s, buf[0] == '!' ? 1 : 0, buf, (len == 1 && buf[0] == '\n') ? 0 : buf[0] == '*' ? sizeof(buf) : (size_t)len)) {
674                                 ecdsa_free(mykey);
675                                 ecdsa_free(hiskey);
676                                 return 1;
677                         }
678                 }
679
680                 if(FD_ISSET(sock, &fds)) {
681                         ssize_t len = recv(sock, buf, sizeof(buf), 0);
682
683                         if(len < 0) {
684                                 fprintf(stderr, "Could not read from socket: %s\n", sockstrerror(sockerrno));
685                                 ecdsa_free(mykey);
686                                 ecdsa_free(hiskey);
687                                 return 1;
688                         }
689
690                         if(len == 0) {
691                                 fprintf(stderr, "Connection terminated by peer.\n");
692                                 break;
693                         }
694
695                         if(verbose) {
696                                 char *hex = alloca(len * 2 + 1);
697                                 bin2hex(buf, hex, len);
698                                 fprintf(stderr, "Received %ld bytes of data:\n%s\n", (long)len, hex);
699                         }
700
701                         if(packetloss && (int)prng(100) < packetloss) {
702                                 if(verbose) {
703                                         fprintf(stderr, "Dropped.\n");
704                                 }
705
706                                 continue;
707                         }
708
709                         char *bufp = buf;
710
711                         while(len) {
712                                 size_t done = sptps_receive_data(&s, bufp, len);
713
714                                 if(!done) {
715                                         if(!datagram) {
716                                                 ecdsa_free(mykey);
717                                                 ecdsa_free(hiskey);
718                                                 return 1;
719                                         }
720                                 }
721
722                                 bufp += done;
723                                 len -= (ssize_t) done;
724                         }
725                 }
726         }
727
728         bool stopped = sptps_stop(&s);
729
730         ecdsa_free(mykey);
731         ecdsa_free(hiskey);
732         closesocket(sock);
733
734         return !stopped;
735 }
736
737 int main(int argc, char *argv[]) {
738         random_init();
739         crypto_init();
740         prng_init();
741
742         int result = run_test(argc, argv);
743
744         random_exit();
745
746         return result;
747 }