GitHub CI: update list of container images
[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         fprintf(stderr,
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                 program_name);
177 }
178
179 #ifdef HAVE_WINDOWS
180
181 int stdin_sock_fd = -1;
182
183 // Windows does not allow calling select() on anything but sockets. Therefore,
184 // to keep the same code as on other operating systems, we have to put a
185 // separate thread between the stdin and the sptps loop way below. This thread
186 // reads stdin and sends its content to the main thread through a TCP socket,
187 // which can be properly select()'ed.
188 static DWORD WINAPI stdin_reader_thread(LPVOID arg) {
189         struct sockaddr_in sa;
190         socklen_t sa_size = sizeof(sa);
191
192         while(true) {
193                 int peer_fd = accept(stdin_sock_fd, (struct sockaddr *) &sa, &sa_size);
194
195                 if(peer_fd < 0) {
196                         fprintf(stderr, "accept() failed: %s\n", strerror(errno));
197                         continue;
198                 }
199
200                 if(verbose) {
201                         fprintf(stderr, "New connection received from :%d\n", ntohs(sa.sin_port));
202                 }
203
204                 char buf[1024];
205                 ssize_t nread;
206
207                 while((nread = read(STDIN_FILENO, buf, sizeof(buf))) > 0) {
208                         if(verbose) {
209                                 fprintf(stderr, "Read %lld bytes from input\n", nread);
210                         }
211
212                         char *start = buf;
213                         ssize_t nleft = nread;
214
215                         while(nleft) {
216                                 ssize_t nsend = send(peer_fd, start, nleft, 0);
217
218                                 if(nsend < 0) {
219                                         if(sockwouldblock(sockerrno)) {
220                                                 continue;
221                                         }
222
223                                         break;
224                                 }
225
226                                 start += nsend;
227                                 nleft -= nsend;
228                         }
229
230                         if(nleft) {
231                                 fprintf(stderr, "Could not send data: %s\n", strerror(errno));
232                                 break;
233                         }
234
235                         if(verbose) {
236                                 fprintf(stderr, "Sent %lld bytes to peer\n", nread);
237                         }
238                 }
239
240                 closesocket(peer_fd);
241         }
242
243         closesocket(stdin_sock_fd);
244         stdin_sock_fd = -1;
245         return 0;
246 }
247
248 static int start_input_reader(void) {
249         if(stdin_sock_fd != -1) {
250                 fprintf(stderr, "stdin thread can only be started once.\n");
251                 return -1;
252         }
253
254         stdin_sock_fd = socket(AF_INET, SOCK_STREAM, 0);
255
256         if(stdin_sock_fd < 0) {
257                 fprintf(stderr, "Could not create server socket: %s\n", strerror(errno));
258                 return -1;
259         }
260
261         struct sockaddr_in serv_sa;
262
263         memset(&serv_sa, 0, sizeof(serv_sa));
264
265         serv_sa.sin_family = AF_INET;
266
267         serv_sa.sin_addr.s_addr = htonl(0x7f000001); // 127.0.0.1
268
269         int res = bind(stdin_sock_fd, (struct sockaddr *)&serv_sa, sizeof(serv_sa));
270
271         if(res < 0) {
272                 fprintf(stderr, "Could not bind socket: %s\n", strerror(errno));
273                 goto server_err;
274         }
275
276         if(listen(stdin_sock_fd, 1) < 0) {
277                 fprintf(stderr, "Could not listen: %s\n", strerror(errno));
278                 goto server_err;
279         }
280
281         struct sockaddr_in connect_sa;
282
283         socklen_t addr_len = sizeof(connect_sa);
284
285         if(getsockname(stdin_sock_fd, (struct sockaddr *)&connect_sa, &addr_len) < 0) {
286                 fprintf(stderr, "Could not determine the address of the stdin thread socket\n");
287                 goto server_err;
288         }
289
290         if(verbose) {
291                 fprintf(stderr, "stdin thread is listening on :%d\n", ntohs(connect_sa.sin_port));
292         }
293
294         if(!CreateThread(NULL, 0, stdin_reader_thread, NULL, 0, NULL)) {
295                 fprintf(stderr, "Could not start reader thread: %d\n", GetLastError());
296                 goto server_err;
297         }
298
299         int client_fd = socket(AF_INET, SOCK_STREAM, 0);
300
301         if(client_fd < 0) {
302                 fprintf(stderr, "Could not create client socket: %s\n", strerror(errno));
303                 return -1;
304         }
305
306         if(connect(client_fd, (struct sockaddr *)&connect_sa, sizeof(connect_sa)) < 0) {
307                 fprintf(stderr, "Could not connect: %s\n", strerror(errno));
308                 closesocket(client_fd);
309                 return -1;
310         }
311
312         return client_fd;
313
314 server_err:
315
316         if(stdin_sock_fd != -1) {
317                 closesocket(stdin_sock_fd);
318                 stdin_sock_fd = -1;
319         }
320
321         return -1;
322 }
323
324 #endif // HAVE_WINDOWS
325
326 static void print_listening_msg(int sock) {
327         sockaddr_t sa = {0};
328         socklen_t salen = sizeof(sa);
329         int port = 0;
330
331         if(!getsockname(sock, &sa.sa, &salen)) {
332                 port = ntohs(sa.in.sin_port);
333         }
334
335         fprintf(stderr, "Listening on %d...\n", port);
336         fflush(stderr);
337 }
338
339 static int run_test(int argc, char *argv[]) {
340         program_name = argv[0];
341         bool initiator = false;
342         bool datagram = false;
343 #ifdef HAVE_LINUX
344         bool tun = false;
345 #endif
346         int packetloss = 0;
347         int r;
348         int option_index = 0;
349         bool quit = false;
350
351         while((r = getopt_long(argc, argv, "dqrstwL:W:v46", long_options, &option_index)) != EOF) {
352                 switch((option_t) r) {
353                 case OPT_LONG_OPTION:
354                         break;
355
356                 case OPT_BAD_OPTION:
357                         usage();
358                         return 1;
359
360                 case OPT_DATAGRAM:
361                         datagram = true;
362                         break;
363
364                 case OPT_QUIT_ON_EOF:
365                         quit = true;
366                         break;
367
368                 case OPT_READONLY:
369                         readonly = true;
370                         break;
371
372                 case OPT_TUN:
373 #ifdef HAVE_LINUX
374                         tun = true;
375 #else
376                         fprintf(stderr, "--tun is only supported on Linux.\n");
377                         usage();
378                         return 1;
379 #endif
380                         break;
381
382                 case OPT_WRITEONLY:
383                         writeonly = true;
384                         break;
385
386                 case OPT_PACKET_LOSS:
387                         packetloss = atoi(optarg);
388                         break;
389
390                 case OPT_REPLAY_WINDOW:
391                         sptps_replaywin = atoi(optarg);
392                         break;
393
394                 case OPT_VERBOSE:
395                         verbose = true;
396                         break;
397
398                 case OPT_SPECIAL_CHAR:
399                         special = true;
400                         break;
401
402                 case OPT_IPV4:
403                         addressfamily = AF_INET;
404                         break;
405
406                 case OPT_IPV6:
407                         addressfamily = AF_INET6;
408                         break;
409
410                 case OPT_HELP:
411                         usage();
412                         return 0;
413
414                 default:
415                         break;
416                 }
417         }
418
419         argc -= optind - 1;
420         argv += optind - 1;
421
422         if(argc < 4 || argc > 5) {
423                 fprintf(stderr, "Wrong number of arguments.\n");
424                 usage();
425                 return 1;
426         }
427
428         if(argc > 4) {
429                 initiator = true;
430         }
431
432 #ifdef HAVE_LINUX
433
434         if(tun) {
435                 in = out = open("/dev/net/tun", O_RDWR | O_NONBLOCK);
436
437                 if(in < 0) {
438                         fprintf(stderr, "Could not open tun device: %s\n", strerror(errno));
439                         return 1;
440                 }
441
442                 struct ifreq ifr = {
443                         .ifr_flags = IFF_TUN
444                 };
445
446                 if(ioctl(in, TUNSETIFF, &ifr)) {
447                         fprintf(stderr, "Could not configure tun interface: %s\n", strerror(errno));
448                         return 1;
449                 }
450
451                 ifr.ifr_name[IFNAMSIZ - 1] = 0;
452                 fprintf(stderr, "Using tun interface %s\n", ifr.ifr_name);
453         }
454
455 #endif
456
457 #ifdef HAVE_WINDOWS
458         static struct WSAData wsa_state;
459
460         if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
461                 return 1;
462         }
463
464 #endif
465
466         struct addrinfo *ai, hint;
467         memset(&hint, 0, sizeof(hint));
468
469         hint.ai_family = addressfamily;
470         hint.ai_socktype = datagram ? SOCK_DGRAM : SOCK_STREAM;
471         hint.ai_protocol = datagram ? IPPROTO_UDP : IPPROTO_TCP;
472         hint.ai_flags = initiator ? 0 : AI_PASSIVE;
473
474         if(getaddrinfo(initiator ? argv[3] : NULL, initiator ? argv[4] : argv[3], &hint, &ai) || !ai) {
475                 fprintf(stderr, "getaddrinfo() failed: %s\n", sockstrerror(sockerrno));
476                 return 1;
477         }
478
479         int sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
480
481         if(sock < 0) {
482                 fprintf(stderr, "Could not create socket: %s\n", sockstrerror(sockerrno));
483                 freeaddrinfo(ai);
484                 return 1;
485         }
486
487         int one = 1;
488         setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&one, sizeof(one));
489
490         if(initiator) {
491                 int res = connect(sock, ai->ai_addr, ai->ai_addrlen);
492
493                 freeaddrinfo(ai);
494                 ai = NULL;
495
496                 if(res) {
497                         fprintf(stderr, "Could not connect to peer: %s\n", sockstrerror(sockerrno));
498                         return 1;
499                 }
500
501                 fprintf(stderr, "Connected\n");
502         } else {
503                 int res = bind(sock, ai->ai_addr, ai->ai_addrlen);
504
505                 freeaddrinfo(ai);
506                 ai = NULL;
507
508                 if(res) {
509                         fprintf(stderr, "Could not bind socket: %s\n", sockstrerror(sockerrno));
510                         return 1;
511                 }
512
513                 if(!datagram) {
514                         if(listen(sock, 1)) {
515                                 fprintf(stderr, "Could not listen on socket: %s\n", sockstrerror(sockerrno));
516                                 return 1;
517                         }
518
519                         print_listening_msg(sock);
520
521                         sock = accept(sock, NULL, NULL);
522
523                         if(sock < 0) {
524                                 fprintf(stderr, "Could not accept connection: %s\n", sockstrerror(sockerrno));
525                                 return 1;
526                         }
527                 } else {
528                         print_listening_msg(sock);
529
530                         char buf[65536];
531                         struct sockaddr addr;
532                         socklen_t addrlen = sizeof(addr);
533
534                         if(recvfrom(sock, buf, sizeof(buf), MSG_PEEK, &addr, &addrlen) <= 0) {
535                                 fprintf(stderr, "Could not read from socket: %s\n", sockstrerror(sockerrno));
536                                 return 1;
537                         }
538
539                         if(connect(sock, &addr, addrlen)) {
540                                 fprintf(stderr, "Could not accept connection: %s\n", sockstrerror(sockerrno));
541                                 return 1;
542                         }
543                 }
544
545                 fprintf(stderr, "Connected\n");
546         }
547
548         FILE *fp = fopen(argv[1], "r");
549
550         if(!fp) {
551                 fprintf(stderr, "Could not open %s: %s\n", argv[1], strerror(errno));
552                 return 1;
553         }
554
555         ecdsa_t *mykey = NULL;
556
557         if(!(mykey = ecdsa_read_pem_private_key(fp))) {
558                 return 1;
559         }
560
561         fclose(fp);
562
563         fp = fopen(argv[2], "r");
564
565         if(!fp) {
566                 fprintf(stderr, "Could not open %s: %s\n", argv[2], strerror(errno));
567                 ecdsa_free(mykey);
568                 return 1;
569         }
570
571         ecdsa_t *hiskey = NULL;
572
573         if(!(hiskey = ecdsa_read_pem_public_key(fp))) {
574                 ecdsa_free(mykey);
575                 return 1;
576         }
577
578         fclose(fp);
579
580         if(verbose) {
581                 fprintf(stderr, "Keys loaded\n");
582         }
583
584         sptps_t s;
585
586         if(!sptps_start(&s, &sock, initiator, datagram, mykey, hiskey, "sptps_test", 10, send_data, receive_record)) {
587                 ecdsa_free(mykey);
588                 ecdsa_free(hiskey);
589                 return 1;
590         }
591
592 #ifdef HAVE_WINDOWS
593
594         if(!readonly) {
595                 in = start_input_reader();
596
597                 if(in < 0) {
598                         fprintf(stderr, "Could not init stdin reader thread\n");
599                         ecdsa_free(mykey);
600                         ecdsa_free(hiskey);
601                         return 1;
602                 }
603         }
604
605 #endif
606
607         int max_fd = MAX(sock, in);
608
609         while(true) {
610                 if(writeonly && readonly) {
611                         break;
612                 }
613
614                 char buf[65535] = "";
615                 size_t readsize = datagram ? 1460u : sizeof(buf);
616
617                 fd_set fds;
618                 FD_ZERO(&fds);
619
620                 if(!readonly && s.instate) {
621                         FD_SET(in, &fds);
622                 }
623
624                 FD_SET(sock, &fds);
625
626                 if(select(max_fd + 1, &fds, NULL, NULL, NULL) <= 0) {
627                         ecdsa_free(mykey);
628                         ecdsa_free(hiskey);
629                         return 1;
630                 }
631
632                 if(FD_ISSET(in, &fds)) {
633 #ifdef HAVE_WINDOWS
634                         ssize_t len = recv(in, buf, readsize, 0);
635 #else
636                         ssize_t len = read(in, buf, readsize);
637 #endif
638
639                         if(len < 0) {
640                                 fprintf(stderr, "Could not read from stdin: %s\n", strerror(errno));
641                                 ecdsa_free(mykey);
642                                 ecdsa_free(hiskey);
643                                 return 1;
644                         }
645
646                         if(len == 0) {
647 #ifdef HAVE_WINDOWS
648                                 shutdown(in, SD_SEND);
649                                 closesocket(in);
650 #endif
651
652                                 if(quit) {
653                                         break;
654                                 }
655
656                                 readonly = true;
657                                 continue;
658                         }
659
660                         if(special && buf[0] == '#') {
661                                 s.outseqno = atoi(buf + 1);
662                         }
663
664                         if(special && buf[0] == '^') {
665                                 sptps_send_record(&s, SPTPS_HANDSHAKE, NULL, 0);
666                         } else if(special && buf[0] == '$') {
667                                 sptps_force_kex(&s);
668
669                                 if(len > 1) {
670                                         sptps_send_record(&s, 0, buf, len);
671                                 }
672                         } else if(!sptps_send_record(&s, buf[0] == '!' ? 1 : 0, buf, (len == 1 && buf[0] == '\n') ? 0 : buf[0] == '*' ? sizeof(buf) : (size_t)len)) {
673                                 ecdsa_free(mykey);
674                                 ecdsa_free(hiskey);
675                                 return 1;
676                         }
677                 }
678
679                 if(FD_ISSET(sock, &fds)) {
680                         ssize_t len = recv(sock, buf, sizeof(buf), 0);
681
682                         if(len < 0) {
683                                 fprintf(stderr, "Could not read from socket: %s\n", sockstrerror(sockerrno));
684                                 ecdsa_free(mykey);
685                                 ecdsa_free(hiskey);
686                                 return 1;
687                         }
688
689                         if(len == 0) {
690                                 fprintf(stderr, "Connection terminated by peer.\n");
691                                 break;
692                         }
693
694                         if(verbose) {
695                                 char *hex = alloca(len * 2 + 1);
696                                 bin2hex(buf, hex, len);
697                                 fprintf(stderr, "Received %ld bytes of data:\n%s\n", (long)len, hex);
698                         }
699
700                         if(packetloss && (int)prng(100) < packetloss) {
701                                 if(verbose) {
702                                         fprintf(stderr, "Dropped.\n");
703                                 }
704
705                                 continue;
706                         }
707
708                         char *bufp = buf;
709
710                         while(len) {
711                                 size_t done = sptps_receive_data(&s, bufp, len);
712
713                                 if(!done) {
714                                         if(!datagram) {
715                                                 ecdsa_free(mykey);
716                                                 ecdsa_free(hiskey);
717                                                 return 1;
718                                         }
719                                 }
720
721                                 bufp += done;
722                                 len -= (ssize_t) done;
723                         }
724                 }
725         }
726
727         bool stopped = sptps_stop(&s);
728
729         ecdsa_free(mykey);
730         ecdsa_free(hiskey);
731         closesocket(sock);
732
733         return !stopped;
734 }
735
736 int main(int argc, char *argv[]) {
737         random_init();
738         crypto_init();
739         prng_init();
740
741         int result = run_test(argc, argv);
742
743         random_exit();
744
745         return result;
746 }