* New upstream release.
[debian/iodine.git] / src / iodine.c
1 /*
2  * Copyright (c) 2006-2007 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
3  *
4  * Permission to use, copy, modify, and distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16
17 #include <stdio.h>
18 #include <stdint.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <signal.h>
22 #include <unistd.h>
23 #include <netdb.h>
24 #include <netinet/in.h>
25 #include <sys/types.h>
26 #include <sys/socket.h>
27 #include <fcntl.h>
28 #include <err.h>
29 #include <pwd.h>
30 #include <arpa/inet.h>
31 #include <zlib.h>
32 #include <arpa/nameser.h>
33 #ifdef DARWIN
34 #include <arpa/nameser8_compat.h>
35 #endif
36
37 #include "common.h"
38 #include "dns.h"
39 #include "login.h"
40 #include "tun.h"
41 #include "version.h"
42
43 static void send_ping(int fd);
44 static void send_chunk(int fd);
45
46 int running = 1;
47 char password[33];
48
49 struct sockaddr_in peer;
50 static char *topdomain;
51
52 uint16_t rand_seed;
53
54 /* Current IP packet */
55 static char activepacket[4096];
56 static char userid;
57 static int lastlen;
58 static int packetpos;
59 static int packetlen;
60 static uint16_t chunkid;
61
62 static void
63 sighandler(int sig) 
64 {
65         running = 0;
66 }
67
68 static void
69 send_packet(int fd, char cmd, const char *data, const size_t datalen)
70 {
71         char packet[4096];
72         struct query q;
73         char buf[4096];
74         size_t len;
75
76         q.id = ++chunkid;
77         q.type = T_NULL;
78
79         buf[0] = cmd;
80         
81         len = dns_build_hostname(buf + 1, sizeof(buf) - 1, data, datalen, topdomain);
82         len = dns_encode(packet, sizeof(packet), &q, QR_QUERY, buf, strlen(buf));
83
84         sendto(fd, packet, len, 0, (struct sockaddr*)&peer, sizeof(peer));
85 }
86
87 int
88 is_sending()
89 {
90         return (packetlen != 0);
91 }
92
93 int
94 read_dns(int fd, char *buf, int buflen)
95 {
96         struct sockaddr_in from;
97         char packet[64*1024];
98         socklen_t addrlen;
99         struct query q;
100         int rv;
101         int r;
102
103         addrlen = sizeof(struct sockaddr);
104         if ((r = recvfrom(fd, packet, sizeof(packet), 0, 
105                                           (struct sockaddr*)&from, &addrlen)) == -1) {
106                 warn("recvfrom");
107                 return 0;
108         }
109
110         rv = dns_decode(buf, buflen, &q, QR_ANSWER, packet, r);
111
112         if (is_sending() && chunkid == q.id) {
113                 /* Got ACK on sent packet */
114                 packetpos += lastlen;
115                 if (packetpos == packetlen) {
116                         /* Packet completed */
117                         packetpos = 0;
118                         packetlen = 0;
119                         lastlen = 0;
120                 } else {
121                         /* More to send */
122                         send_chunk(fd);
123                 }
124         }
125         return rv;
126 }
127
128
129 static int
130 tunnel_tun(int tun_fd, int dns_fd)
131 {
132         unsigned long outlen;
133         unsigned long inlen;
134         char out[64*1024];
135         char in[64*1024];
136         size_t read;
137
138         if ((read = read_tun(tun_fd, in, sizeof(in))) <= 0)
139                 return -1;
140
141         outlen = sizeof(out);
142         inlen = read;
143         compress2((uint8_t*)out, &outlen, (uint8_t*)in, inlen, 9);
144
145         memcpy(activepacket, out, MIN(outlen, sizeof(activepacket)));
146         lastlen = 0;
147         packetpos = 0;
148         packetlen = outlen;
149
150         send_chunk(dns_fd);
151
152         return read;
153 }
154
155 static int
156 tunnel_dns(int tun_fd, int dns_fd)
157 {
158         unsigned long outlen;
159         unsigned long inlen;
160         char out[64*1024];
161         char in[64*1024];
162         size_t read;
163
164         if ((read = read_dns(dns_fd, in, sizeof(in))) <= 0) 
165                 return -1;
166                 
167         outlen = sizeof(out);
168         inlen = read;
169         if (uncompress((uint8_t*)out, &outlen, (uint8_t*)in, inlen) != Z_OK)
170                 return -1;
171
172         write_tun(tun_fd, out, outlen);
173         if (!is_sending()) 
174                 send_ping(dns_fd);
175         
176         return read;
177 }
178
179 static int
180 tunnel(int tun_fd, int dns_fd)
181 {
182         struct timeval tv;
183         fd_set fds;
184         int rv;
185         int i;
186
187         rv = 0;
188
189         while (running) {
190                 tv.tv_sec = 1;
191                 tv.tv_usec = 0;
192
193                 FD_ZERO(&fds);
194                 if (!is_sending()) 
195                         FD_SET(tun_fd, &fds);
196                 FD_SET(dns_fd, &fds);
197
198                 i = select(MAX(tun_fd, dns_fd) + 1, &fds, NULL, NULL, &tv);
199                 
200                 if (running == 0)
201                         break;
202
203                 if (i < 0) 
204                         err(1, "select");
205
206                 if (i == 0) /* timeout */
207                         send_ping(dns_fd);
208                 else {
209                         if (FD_ISSET(tun_fd, &fds)) {
210                                 if (tunnel_tun(tun_fd, dns_fd) <= 0)
211                                         continue;
212                         }
213                         if (FD_ISSET(dns_fd, &fds)) {
214                                 if (tunnel_dns(tun_fd, dns_fd) <= 0)
215                                         continue;
216                         } 
217                 }
218         }
219
220         return rv;
221 }
222
223 static void
224 send_chunk(int fd)
225 {
226         char hex[] = "0123456789ABCDEF";
227         char packet[4096];
228         struct query q;
229         char buf[4096];
230         int avail;
231         int code;
232         char *p;
233         int len;
234
235         q.id = ++chunkid;
236         q.type = T_NULL;
237
238         p = activepacket;
239         p += packetpos;
240         avail = packetlen - packetpos;
241
242         lastlen = dns_build_hostname(buf + 1, sizeof(buf) - 1, p, avail, topdomain);
243
244         if (lastlen == avail)
245                 code = 1;
246         else
247                 code = 0;
248                 
249         code |= (userid << 1);
250         buf[0] = hex[code];
251         len = dns_encode(packet, sizeof(packet), &q, QR_QUERY, buf, strlen(buf));
252
253         sendto(fd, packet, len, 0, (struct sockaddr*)&peer, sizeof(peer));
254 }
255
256 void
257 send_login(int fd, char *login, int len)
258 {
259         char data[19];
260
261         memset(data, 0, sizeof(data));
262         data[0] = userid;
263         memcpy(&data[1], login, MIN(len, 16));
264
265         data[17] = (rand_seed >> 8) & 0xff;
266         data[18] = (rand_seed >> 0) & 0xff;
267         
268         rand_seed++;
269
270         send_packet(fd, 'L', data, sizeof(data));
271 }
272
273 static void
274 send_ping(int fd)
275 {
276         char data[3];
277         
278         if (is_sending()) {
279                 lastlen = 0;
280                 packetpos = 0;
281                 packetlen = 0;
282         }
283
284         data[0] = userid;
285         data[1] = (rand_seed >> 8) & 0xff;
286         data[2] = (rand_seed >> 0) & 0xff;
287         
288         rand_seed++;
289
290         send_packet(fd, 'P', data, sizeof(data));
291 }
292
293 void 
294 send_version(int fd, uint32_t version)
295 {
296         char data[6];
297
298         data[0] = (version >> 24) & 0xff;
299         data[1] = (version >> 16) & 0xff;
300         data[2] = (version >> 8) & 0xff;
301         data[3] = (version >> 0) & 0xff;
302
303         data[4] = (rand_seed >> 8) & 0xff;
304         data[5] = (rand_seed >> 0) & 0xff;
305         
306         rand_seed++;
307
308         send_packet(fd, 'V', data, sizeof(data));
309 }
310
311 static int
312 handshake(int dns_fd)
313 {
314         struct timeval tv;
315         uint32_t payload;
316         char server[65];
317         char client[65];
318         char login[16];
319         char in[4096];
320         fd_set fds;
321         int read;
322         int mtu;
323         int seed;
324         int i;
325         int r;
326
327         for (i = 0; running && i < 5; i++) {
328                 tv.tv_sec = i + 1;
329                 tv.tv_usec = 0;
330
331                 send_version(dns_fd, VERSION);
332                 
333                 FD_ZERO(&fds);
334                 FD_SET(dns_fd, &fds);
335
336                 r = select(dns_fd + 1, &fds, NULL, NULL, &tv);
337
338                 if(r > 0) {
339                         read = read_dns(dns_fd, in, sizeof(in));
340                         
341                         if(read < 0) {
342                                 perror("read");
343                                 continue;
344                         }
345
346                         if (read >= 9) {
347                                 payload =  (((in[4] & 0xff) << 24) |
348                                                         ((in[5] & 0xff) << 16) |
349                                                         ((in[6] & 0xff) << 8) |
350                                                         ((in[7] & 0xff)));
351
352                                 if (strncmp("VACK", in, 4) == 0) {
353                                         seed = payload;
354                                         userid = in[8];
355
356                                         printf("Version ok, both running 0x%08x. You are user #%d\n", VERSION, userid);
357                                         goto perform_login;
358                                 } else if (strncmp("VNAK", in, 4) == 0) {
359                                         errx(1, "you run 0x%08x, server runs 0x%08x. giving up\n", 
360                                                         VERSION, payload);
361                                         /* NOTREACHED */
362                                 } else if (strncmp("VFUL", in, 4) == 0) {
363                                         errx(1, "server full, all %d slots are taken. try again later\n", payload);
364                                         /* NOTREACHED */
365                                 }
366                         } else 
367                                 warnx("did not receive proper login challenge\n");
368                 }
369                 
370                 printf("Retrying version check...\n");
371         }
372         errx(1, "couldn't connect to server");
373         /* NOTREACHED */
374         
375 perform_login:
376         login_calculate(login, 16, password, seed);
377         
378         for (i=0; running && i<5 ;i++) {
379                 tv.tv_sec = i + 1;
380                 tv.tv_usec = 0;
381
382                 send_login(dns_fd, login, 16);
383                 
384                 FD_ZERO(&fds);
385                 FD_SET(dns_fd, &fds);
386
387                 r = select(dns_fd + 1, &fds, NULL, NULL, &tv);
388
389                 if(r > 0) {
390                         read = read_dns(dns_fd, in, sizeof(in));
391                         
392                         if(read <= 0) {
393                                 warn("read");
394                                 continue;
395                         }
396
397                         if (read > 0) {
398                                 if (strncmp("LNAK", in, 4) == 0) {
399                                         printf("Bad password\n");
400                                         return 1;
401                                 } else if (sscanf(in, "%64[^-]-%64[^-]-%d", 
402                                         server, client, &mtu) == 3) {
403                                         
404                                         server[64] = 0;
405                                         client[64] = 0;
406                                         if (tun_setip(client) == 0 && 
407                                                 tun_setmtu(mtu) == 0) {
408                                                 return 0;
409                                         } else {
410                                                 warnx("Received handshake with bad data");
411                                         }
412                                 } else {
413                                         printf("Received bad handshake\n");
414                                 }
415                         }
416                 }
417
418                 printf("Retrying login...\n");
419         }
420
421         return 1;
422 }
423
424 static void 
425 set_target(const char *host) 
426 {
427         struct hostent *h;
428
429         h = gethostbyname(host);
430         if (!h)
431                 err(1, "couldn't resolve name %s", host);
432
433         memset(&peer, 0, sizeof(peer));
434         peer.sin_family = AF_INET;
435         peer.sin_port = htons(53);
436         peer.sin_addr = *((struct in_addr *) h->h_addr);
437 }
438
439 static void
440 usage() {
441         extern char *__progname;
442
443         printf("Usage: %s [-v] [-h] [-f] [-u user] [-t chrootdir] [-d device] "
444                         "nameserver topdomain\n", __progname);
445         exit(2);
446 }
447
448 static void
449 help() {
450         extern char *__progname;
451
452         printf("iodine IP over DNS tunneling client\n");
453         printf("Usage: %s [-v] [-h] [-f] [-u user] [-t chrootdir] [-d device] "
454                         "[-P password] nameserver topdomain\n", __progname);
455         printf("  -v to print version info and exit\n");
456         printf("  -h to print this help and exit\n");
457         printf("  -f to keep running in foreground\n");
458         printf("  -u name to drop privileges and run as user 'name'\n");
459         printf("  -t dir to chroot to directory dir\n");
460         printf("  -d device to set tunnel device name\n");
461         printf("  -P password used for authentication (max 32 chars will be used)\n");
462         printf("nameserver is the IP number of the relaying nameserver\n");
463         printf("topdomain is the FQDN that is delegated to the tunnel endpoint.\n");
464
465         exit(0);
466 }
467
468 static void
469 version() {
470
471         printf("iodine IP over DNS tunneling client\n");
472         printf("version: 0.4.0 from 2007-03-25\n");
473
474         exit(0);
475 }
476
477 int
478 main(int argc, char **argv)
479 {
480         struct passwd *pw;
481         char *username;
482         int foreground;
483         char *newroot;
484         char *device;
485         int choice;
486         int tun_fd;
487         int dns_fd;
488
489         memset(password, 0, 33);
490         username = NULL;
491         foreground = 0;
492         newroot = NULL;
493         device = NULL;
494         chunkid = 0;
495         
496         while ((choice = getopt(argc, argv, "vfhu:t:d:P:")) != -1) {
497                 switch(choice) {
498                 case 'v':
499                         version();
500                         break;
501                 case 'f':
502                         foreground = 1;
503                         break;
504                 case 'h':
505                         help();
506                         break;
507                 case 'u':
508                         username = optarg;
509                         break;
510                 case 't':
511                         newroot = optarg;
512                         break;
513                 case 'd':
514                         device = optarg;
515                         break;
516                 case 'P':
517                         strncpy(password, optarg, 32);
518                         password[32] = 0;
519                         break;
520                 default:
521                         usage();
522                         /* NOTREACHED */
523                 }
524         }
525         
526         if (geteuid() != 0) {
527                 printf("Run as root and you'll be happy.\n");
528                 usage();
529         }
530
531         argc -= optind;
532         argv += optind;
533         
534         if (argc != 2) 
535                 usage();
536
537         topdomain = strdup(argv[1]);
538
539         if(username) {
540                 pw = getpwnam(username);
541                 if (!pw) {
542                         printf("User %s does not exist!\n", username);
543                         usage();
544                 }
545         }
546         
547         if (strlen(password) == 0) {
548                 printf("Enter password on stdin:\n");
549                 scanf("%32s", password);
550                 password[32] = 0;
551         }
552
553         if ((tun_fd = open_tun(device)) == -1)
554                 goto cleanup1;
555         if ((dns_fd = open_dns(0, INADDR_ANY)) == -1)
556                 goto cleanup2;
557         set_target(argv[0]);
558
559         signal(SIGINT, sighandler);
560         signal(SIGTERM, sighandler);
561
562         if(handshake(dns_fd))
563                 goto cleanup2;
564         
565         printf("Sending queries for %s to %s\n", argv[1], argv[0]);
566
567         do_chroot(newroot);
568         
569         if (username) {
570                 if (setgid(pw->pw_gid) < 0 || setuid(pw->pw_uid) < 0) {
571                         printf("Could not switch to user %s!\n", username);
572                         usage();
573                 }
574         }
575         
576         if (!foreground) {
577                 do_detach();
578         }
579
580         tunnel(tun_fd, dns_fd);
581
582 cleanup2:
583         close_dns(dns_fd);
584         close_tun(tun_fd);
585 cleanup1:
586
587         return 0;
588 }