Update copyright notices.
[tinc] / src / sptps_test.c
1 /*
2     sptps_test.c -- Simple Peer-to-Peer Security test program
3     Copyright (C) 2011-2014 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 <getopt.h>
27
28 #include "crypto.h"
29 #include "ecdsa.h"
30 #include "sptps.h"
31 #include "utils.h"
32
33 // Symbols necessary to link with logger.o
34 bool send_request(void *c, const char *msg, ...) {
35         (void)c;
36         (void)msg;
37         return false;
38 }
39
40 struct list_t *connection_list = NULL;
41
42 bool send_meta(void *c, const char *msg, int len) {
43         (void)c;
44         (void)msg;
45         (void)len;
46         return false;
47 }
48
49 char *logfilename = NULL;
50 bool do_detach = false;
51 struct timeval now;
52
53 static bool special;
54 static bool verbose;
55 static bool readonly;
56 static bool writeonly;
57 static int in = 0;
58 static int out = 1;
59 static int addressfamily = AF_UNSPEC;
60
61 static bool send_data(void *handle, uint8_t type, const void *data, size_t len) {
62         (void)type;
63         char hex[len * 2 + 1];
64         bin2hex(data, hex, len);
65
66         if(verbose) {
67                 fprintf(stderr, "Sending %d bytes of data:\n%s\n", (int)len, hex);
68         }
69
70         const int *sock = handle;
71
72         if((size_t)send(*sock, data, len, 0) != len) {
73                 return false;
74         }
75
76         return true;
77 }
78
79 static bool receive_record(void *handle, uint8_t type, const void *data, uint16_t len) {
80         (void)handle;
81
82         if(verbose) {
83                 fprintf(stderr, "Received type %d record of %u bytes:\n", type, len);
84         }
85
86         if(!writeonly) {
87                 write(out, data, len);
88         }
89
90         return true;
91 }
92
93 static struct option const long_options[] = {
94         {"datagram", no_argument, NULL, 'd'},
95         {"quit", no_argument, NULL, 'q'},
96         {"readonly", no_argument, NULL, 'r'},
97         {"writeonly", no_argument, NULL, 'w'},
98         {"packet-loss", required_argument, NULL, 'L'},
99         {"replay-window", required_argument, NULL, 'W'},
100         {"special", no_argument, NULL, 's'},
101         {"verbose", required_argument, NULL, 'v'},
102         {"help", no_argument, NULL, 1},
103         {NULL, 0, NULL, 0}
104 };
105
106 const char *program_name;
107
108 static void usage() {
109         fprintf(stderr, "Usage: %s [options] my_ed25519_key_file his_ed25519_key_file [host] port\n\n", program_name);
110         fprintf(stderr, "Valid options are:\n"
111                 "  -d, --datagram          Enable datagram mode.\n"
112                 "  -q, --quit              Quit when EOF occurs on stdin.\n"
113                 "  -r, --readonly          Only send data from the socket to stdout.\n"
114 #ifdef HAVE_LINUX
115                 "  -t, --tun               Use a tun device instead of stdio.\n"
116 #endif
117                 "  -w, --writeonly         Only send data from stdin to the socket.\n"
118                 "  -L, --packet-loss RATE  Fake packet loss of RATE percent.\n"
119                 "  -R, --replay-window N   Set replay window to N bytes.\n"
120                 "  -s, --special           Enable special handling of lines starting with #, ^ and $.\n"
121                 "  -v, --verbose           Display debug messages.\n"
122                 "  -4                      Use IPv4.\n"
123                 "  -6                      Use IPv6.\n"
124                 "\n");
125         fprintf(stderr, "Report bugs to tinc@tinc-vpn.org.\n");
126 }
127
128 int main(int argc, char *argv[]) {
129         program_name = argv[0];
130         bool initiator = false;
131         bool datagram = false;
132 #ifdef HAVE_LINUX
133         bool tun = false;
134 #endif
135         int packetloss = 0;
136         int r;
137         int option_index = 0;
138         ecdsa_t *mykey = NULL, *hiskey = NULL;
139         bool quit = false;
140
141         while((r = getopt_long(argc, argv, "dqrstwL:W:v46", long_options, &option_index)) != EOF) {
142                 switch(r) {
143                 case 0:   /* long option */
144                         break;
145
146                 case 'd': /* datagram mode */
147                         datagram = true;
148                         break;
149
150                 case 'q': /* close connection on EOF from stdin */
151                         quit = true;
152                         break;
153
154                 case 'r': /* read only */
155                         readonly = true;
156                         break;
157
158                 case 't': /* read only */
159 #ifdef HAVE_LINUX
160                         tun = true;
161 #else
162                         fprintf(stderr, "--tun is only supported on Linux.\n");
163                         usage();
164                         return 1;
165 #endif
166                         break;
167
168                 case 'w': /* write only */
169                         writeonly = true;
170                         break;
171
172                 case 'L': /* packet loss rate */
173                         packetloss = atoi(optarg);
174                         break;
175
176                 case 'W': /* replay window size */
177                         sptps_replaywin = atoi(optarg);
178                         break;
179
180                 case 'v': /* be verbose */
181                         verbose = true;
182                         break;
183
184                 case 's': /* special character handling */
185                         special = true;
186                         break;
187
188                 case '?': /* wrong options */
189                         usage();
190                         return 1;
191
192                 case '4': /* IPv4 */
193                         addressfamily = AF_INET;
194                         break;
195
196                 case '6': /* IPv6 */
197                         addressfamily = AF_INET6;
198                         break;
199
200                 case 1: /* help */
201                         usage();
202                         return 0;
203
204                 default:
205                         break;
206                 }
207         }
208
209         argc -= optind - 1;
210         argv += optind - 1;
211
212         if(argc < 4 || argc > 5) {
213                 fprintf(stderr, "Wrong number of arguments.\n");
214                 usage();
215                 return 1;
216         }
217
218         if(argc > 4) {
219                 initiator = true;
220         }
221
222         srand(time(NULL));
223
224 #ifdef HAVE_LINUX
225
226         if(tun) {
227                 in = out = open("/dev/net/tun", O_RDWR | O_NONBLOCK);
228
229                 if(in < 0) {
230                         fprintf(stderr, "Could not open tun device: %s\n", strerror(errno));
231                         return 1;
232                 }
233
234                 struct ifreq ifr = {
235                         .ifr_flags = IFF_TUN
236                 };
237
238                 if(ioctl(in, TUNSETIFF, &ifr)) {
239                         fprintf(stderr, "Could not configure tun interface: %s\n", strerror(errno));
240                         return 1;
241                 }
242
243                 ifr.ifr_name[IFNAMSIZ - 1] = 0;
244                 fprintf(stderr, "Using tun interface %s\n", ifr.ifr_name);
245         }
246
247 #endif
248
249 #ifdef HAVE_MINGW
250         static struct WSAData wsa_state;
251
252         if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
253                 return 1;
254         }
255
256 #endif
257
258         struct addrinfo *ai, hint;
259         memset(&hint, 0, sizeof(hint));
260
261         hint.ai_family = addressfamily;
262         hint.ai_socktype = datagram ? SOCK_DGRAM : SOCK_STREAM;
263         hint.ai_protocol = datagram ? IPPROTO_UDP : IPPROTO_TCP;
264         hint.ai_flags = initiator ? 0 : AI_PASSIVE;
265
266         if(getaddrinfo(initiator ? argv[3] : NULL, initiator ? argv[4] : argv[3], &hint, &ai) || !ai) {
267                 fprintf(stderr, "getaddrinfo() failed: %s\n", sockstrerror(sockerrno));
268                 return 1;
269         }
270
271         int sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
272
273         if(sock < 0) {
274                 fprintf(stderr, "Could not create socket: %s\n", sockstrerror(sockerrno));
275                 return 1;
276         }
277
278         int one = 1;
279         setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&one, sizeof(one));
280
281         if(initiator) {
282                 if(connect(sock, ai->ai_addr, ai->ai_addrlen)) {
283                         fprintf(stderr, "Could not connect to peer: %s\n", sockstrerror(sockerrno));
284                         return 1;
285                 }
286
287                 fprintf(stderr, "Connected\n");
288         } else {
289                 if(bind(sock, ai->ai_addr, ai->ai_addrlen)) {
290                         fprintf(stderr, "Could not bind socket: %s\n", sockstrerror(sockerrno));
291                         return 1;
292                 }
293
294                 if(!datagram) {
295                         if(listen(sock, 1)) {
296                                 fprintf(stderr, "Could not listen on socket: %s\n", sockstrerror(sockerrno));
297                                 return 1;
298                         }
299
300                         fprintf(stderr, "Listening...\n");
301
302                         sock = accept(sock, NULL, NULL);
303
304                         if(sock < 0) {
305                                 fprintf(stderr, "Could not accept connection: %s\n", sockstrerror(sockerrno));
306                                 return 1;
307                         }
308                 } else {
309                         fprintf(stderr, "Listening...\n");
310
311                         char buf[65536];
312                         struct sockaddr addr;
313                         socklen_t addrlen = sizeof(addr);
314
315                         if(recvfrom(sock, buf, sizeof(buf), MSG_PEEK, &addr, &addrlen) <= 0) {
316                                 fprintf(stderr, "Could not read from socket: %s\n", sockstrerror(sockerrno));
317                                 return 1;
318                         }
319
320                         if(connect(sock, &addr, addrlen)) {
321                                 fprintf(stderr, "Could not accept connection: %s\n", sockstrerror(sockerrno));
322                                 return 1;
323                         }
324                 }
325
326                 fprintf(stderr, "Connected\n");
327         }
328
329         crypto_init();
330
331         FILE *fp = fopen(argv[1], "r");
332
333         if(!fp) {
334                 fprintf(stderr, "Could not open %s: %s\n", argv[1], strerror(errno));
335                 return 1;
336         }
337
338         if(!(mykey = ecdsa_read_pem_private_key(fp))) {
339                 return 1;
340         }
341
342         fclose(fp);
343
344         fp = fopen(argv[2], "r");
345
346         if(!fp) {
347                 fprintf(stderr, "Could not open %s: %s\n", argv[2], strerror(errno));
348                 return 1;
349         }
350
351         if(!(hiskey = ecdsa_read_pem_public_key(fp))) {
352                 return 1;
353         }
354
355         fclose(fp);
356
357         if(verbose) {
358                 fprintf(stderr, "Keys loaded\n");
359         }
360
361         sptps_t s;
362
363         if(!sptps_start(&s, &sock, initiator, datagram, mykey, hiskey, "sptps_test", 10, send_data, receive_record)) {
364                 return 1;
365         }
366
367         while(true) {
368                 if(writeonly && readonly) {
369                         break;
370                 }
371
372                 char buf[65535] = "";
373                 size_t readsize = datagram ? 1460u : sizeof(buf);
374
375                 fd_set fds;
376                 FD_ZERO(&fds);
377 #ifndef HAVE_MINGW
378
379                 if(!readonly && s.instate) {
380                         FD_SET(in, &fds);
381                 }
382
383 #endif
384                 FD_SET(sock, &fds);
385
386                 if(select(sock + 1, &fds, NULL, NULL, NULL) <= 0) {
387                         return 1;
388                 }
389
390                 if(FD_ISSET(in, &fds)) {
391                         ssize_t len = read(in, buf, readsize);
392
393                         if(len < 0) {
394                                 fprintf(stderr, "Could not read from stdin: %s\n", strerror(errno));
395                                 return 1;
396                         }
397
398                         if(len == 0) {
399                                 if(quit) {
400                                         break;
401                                 }
402
403                                 readonly = true;
404                                 continue;
405                         }
406
407                         if(special && buf[0] == '#') {
408                                 s.outseqno = atoi(buf + 1);
409                         }
410
411                         if(special && buf[0] == '^') {
412                                 sptps_send_record(&s, SPTPS_HANDSHAKE, NULL, 0);
413                         } else if(special && buf[0] == '$') {
414                                 sptps_force_kex(&s);
415
416                                 if(len > 1) {
417                                         sptps_send_record(&s, 0, buf, len);
418                                 }
419                         } else if(!sptps_send_record(&s, buf[0] == '!' ? 1 : 0, buf, (len == 1 && buf[0] == '\n') ? 0 : buf[0] == '*' ? sizeof(buf) : (size_t)len)) {
420                                 return 1;
421                         }
422                 }
423
424                 if(FD_ISSET(sock, &fds)) {
425                         ssize_t len = recv(sock, buf, sizeof(buf), 0);
426
427                         if(len < 0) {
428                                 fprintf(stderr, "Could not read from socket: %s\n", sockstrerror(sockerrno));
429                                 return 1;
430                         }
431
432                         if(len == 0) {
433                                 fprintf(stderr, "Connection terminated by peer.\n");
434                                 break;
435                         }
436
437                         if(verbose) {
438                                 char hex[len * 2 + 1];
439                                 bin2hex(buf, hex, len);
440                                 fprintf(stderr, "Received %d bytes of data:\n%s\n", (int)len, hex);
441                         }
442
443                         if(packetloss && (rand() % 100) < packetloss) {
444                                 if(verbose) {
445                                         fprintf(stderr, "Dropped.\n");
446                                 }
447
448                                 continue;
449                         }
450
451                         char *bufp = buf;
452
453                         while(len) {
454                                 size_t done = sptps_receive_data(&s, bufp, len);
455
456                                 if(!done) {
457                                         if(!datagram) {
458                                                 return 1;
459                                         }
460                                 }
461
462                                 bufp += done;
463                                 len -= done;
464                         }
465                 }
466         }
467
468         if(!sptps_stop(&s)) {
469                 return 1;
470         }
471
472         return 0;
473 }