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