* New upstream release.
[debian/iodine.git] / src / iodined.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 <sys/types.h>
24 #include <sys/socket.h>
25 #include <fcntl.h>
26 #include <err.h>
27 #include <time.h>
28 #include <pwd.h>
29 #include <arpa/inet.h>
30 #include <netinet/in.h>
31 #include <netinet/in_systm.h>
32 #include <netinet/ip.h>
33 #include <zlib.h>
34
35 #include "common.h"
36 #include "dns.h"
37 #include "user.h"
38 #include "login.h"
39 #include "tun.h"
40 #include "encoding.h"
41 #include "version.h"
42
43 int running = 1;
44
45 char *topdomain;
46
47 char password[33];
48
49 int my_mtu;
50 in_addr_t my_ip;
51
52 static int read_dns(int, struct query *, char *, int);
53 static void write_dns(int, struct query *, char *, int);
54
55 static void
56 sigint(int sig) 
57 {
58         running = 0;
59 }
60
61 static int
62 tunnel_tun(int tun_fd, int dns_fd)
63 {
64         unsigned long outlen;
65         struct ip *header;
66         char out[64*1024];
67         char in[64*1024];
68         int userid;
69         int read;
70
71         if ((read = read_tun(tun_fd, in, sizeof(in))) <= 0)
72                 return 0;
73         
74         /* find target ip in packet, in is padded with 4 bytes TUN header */
75         header = (struct ip*) (in + 4);
76         userid = find_user_by_ip(header->ip_dst.s_addr);
77         if (userid < 0)
78                 return 0;
79
80         outlen = sizeof(out);
81         compress2((uint8_t*)out, &outlen, (uint8_t*)in, read, 9);
82
83         /* if another packet is queued, throw away this one. TODO build queue */
84         if (users[userid].outpacket.len == 0) {
85                 memcpy(users[userid].outpacket.data, out, outlen);
86                 users[userid].outpacket.len = outlen;
87                 return outlen;
88         } else {
89                 return 0;
90         }
91 }
92
93 typedef enum {
94         VERSION_ACK,
95         VERSION_NACK,
96         VERSION_FULL
97 } version_ack_t;
98
99 static void
100 send_version_response(int fd, version_ack_t ack, uint32_t payload, struct user *u)
101 {
102         char out[9];
103         
104         switch (ack) {
105         case VERSION_ACK:
106                 strncpy(out, "VACK", sizeof(out));
107                 break;
108         case VERSION_NACK:
109                 strncpy(out, "VNAK", sizeof(out));
110                 break;
111         case VERSION_FULL:
112                 strncpy(out, "VFUL", sizeof(out));
113                 break;
114         }
115         
116         out[4] = ((payload >> 24) & 0xff);
117         out[5] = ((payload >> 16) & 0xff);
118         out[6] = ((payload >> 8) & 0xff);
119         out[7] = ((payload) & 0xff);
120         out[8] = u->id;
121
122
123         write_dns(fd, &u->q, out, sizeof(out));
124 }
125
126 static int
127 tunnel_dns(int tun_fd, int dns_fd)
128 {
129         struct in_addr tempip;
130         struct user dummy;
131         struct ip *hdr;
132         unsigned long outlen;
133         char logindata[16];
134         char out[64*1024];
135         char in[64*1024];
136         char *tmp[2];
137         int userid;
138         int touser;
139         int version;
140         int read;
141         int code;
142
143         userid = -1;
144         if ((read = read_dns(dns_fd, &(dummy.q), in, sizeof(in))) <= 0)
145                 return 0;
146                                 
147         if(in[0] == 'V' || in[0] == 'v') {
148                 /* Version greeting, compare and send ack/nak */
149                 if (read > 4) { 
150                         /* Received V + 32bits version */
151
152                         version = (((in[1] & 0xff) << 24) |
153                                            ((in[2] & 0xff) << 16) |
154                                            ((in[3] & 0xff) << 8) |
155                                            ((in[4] & 0xff)));
156
157                         if (version == VERSION) {
158                                 userid = find_available_user();
159                                 if (userid >= 0) {
160                                         users[userid].seed = rand();
161                                         memcpy(&(users[userid].host), &(dummy.q.from), dummy.q.fromlen);
162                                         memcpy(&(users[userid].q), &(dummy.q), sizeof(struct query));
163                                         users[userid].addrlen = dummy.q.fromlen;
164                                         send_version_response(dns_fd, VERSION_ACK, users[userid].seed, &users[userid]);
165                                         users[userid].q.id = 0;
166                                 } else {
167                                         /* No space for another user */
168                                         send_version_response(dns_fd, VERSION_FULL, USERS, &dummy);
169                                 }
170                         } else {
171                                 send_version_response(dns_fd, VERSION_NACK, VERSION, &dummy);
172                         }
173                 } else {
174                         send_version_response(dns_fd, VERSION_NACK, VERSION, &dummy);
175                 }
176         } else if(in[0] == 'L' || in[0] == 'l') {
177                 /* Login phase, handle auth */
178                 userid = in[1];
179                 if (userid < 0 || userid >= USERS) {
180                         write_dns(dns_fd, &(dummy.q), "BADIP", 5);
181                         return 0; /* illegal id */
182                 }
183                 users[userid].last_pkt = time(NULL);
184                 login_calculate(logindata, 16, password, users[userid].seed);
185
186                 if (dummy.q.fromlen != users[userid].addrlen ||
187                                 memcmp(&(users[userid].host), &(dummy.q.from), dummy.q.fromlen) != 0) {
188                         write_dns(dns_fd, &(dummy.q), "BADIP", 5);
189                 } else {
190                         if (read >= 18 && (memcmp(logindata, in+2, 16) == 0)) {
191                                 /* Login ok, send ip/mtu info */
192
193                                 tempip.s_addr = my_ip;
194                                 tmp[0] = strdup(inet_ntoa(tempip));
195                                 tempip.s_addr = users[userid].tun_ip;
196                                 tmp[1] = strdup(inet_ntoa(tempip));
197
198                                 read = snprintf(out, sizeof(out), "%s-%s-%d", 
199                                                 tmp[0], tmp[1], my_mtu);
200
201                                 write_dns(dns_fd, &(dummy.q), out, read);
202                                 dummy.q.id = 0;
203
204                                 free(tmp[1]);
205                                 free(tmp[0]);
206                         } else {
207                                 write_dns(dns_fd, &(dummy.q), "LNAK", 4);
208                         }
209                 }
210         } else if(in[0] == 'P' || in[0] == 'p') {
211                 /* Ping packet, store userid */
212                 userid = in[1];
213                 if (userid < 0 || userid >= USERS) {
214                         write_dns(dns_fd, &(dummy.q), "BADIP", 5);
215                         return 0; /* illegal id */
216                 }
217                 memcpy(&(users[userid].q), &(dummy.q), sizeof(struct query));
218                 users[userid].last_pkt = time(NULL);
219         } else if((in[0] >= '0' && in[0] <= '9')
220                         || (in[0] >= 'a' && in[0] <= 'f')
221                         || (in[0] >= 'A' && in[0] <= 'F')) {
222                 if ((in[0] >= '0' && in[0] <= '9'))
223                         code = in[0] - '0';
224                 if ((in[0] >= 'a' && in[0] <= 'f'))
225                         code = in[0] - 'a' + 10;
226                 if ((in[0] >= 'A' && in[0] <= 'F'))
227                         code = in[0] - 'A' + 10;
228
229                 userid = code >> 1;
230                 if (userid < 0 || userid >= USERS) {
231                         write_dns(dns_fd, &(dummy.q), "BADIP", 5);
232                         return 0; /* illegal id */
233                 }
234
235                 /* Check sending ip number */
236                 if (dummy.q.fromlen != users[userid].addrlen ||
237                                 memcmp(&(users[userid].host), &(dummy.q.from), dummy.q.fromlen) != 0) {
238                         write_dns(dns_fd, &(dummy.q), "BADIP", 5);
239                 } else {
240                         users[userid].last_pkt = time(NULL);
241                         memcpy(&(users[userid].q), &(dummy.q), sizeof(struct query));
242                         users[userid].addrlen = dummy.q.fromlen;
243                         memcpy(users[userid].inpacket.data + users[userid].inpacket.offset, in + 1, read - 1);
244                         users[userid].inpacket.len += read - 1;
245                         users[userid].inpacket.offset += read - 1;
246
247                         if (code & 1) {
248                                 outlen = sizeof(out);
249                                 uncompress((uint8_t*)out, &outlen, 
250                                                    (uint8_t*)users[userid].inpacket.data, users[userid].inpacket.len);
251
252                                 hdr = (struct ip*) (out + 4);
253                                 touser = find_user_by_ip(hdr->ip_dst.s_addr);
254
255                                 if (touser == -1) {
256                                         /* send the uncompressed packet to tun device */
257                                         write_tun(tun_fd, out, outlen);
258                                 } else {
259                                         /* send the compressed packet to other client
260                                          * if another packet is queued, throw away this one. TODO build queue */
261                                         if (users[touser].outpacket.len == 0) {
262                                                 memcpy(users[touser].outpacket.data, users[userid].inpacket.data, users[userid].inpacket.len);
263                                                 users[touser].outpacket.len = users[userid].inpacket.len;
264                                         }
265                                 }
266                                 users[userid].inpacket.len = users[userid].inpacket.offset = 0;
267                         }
268                 }
269         }
270         /* userid must be set for a reply to be sent */
271         if (userid >= 0 && userid < USERS && dummy.q.fromlen == users[userid].addrlen &&
272                         memcmp(&(users[userid].host), &(dummy.q.from), dummy.q.fromlen) == 0 &&
273                         users[userid].outpacket.len > 0) {
274
275                 write_dns(dns_fd, &(dummy.q), users[userid].outpacket.data, users[userid].outpacket.len);
276                 users[userid].outpacket.len = 0;
277                 users[userid].q.id = 0;
278         }
279
280         return 0;
281 }
282
283 static int
284 tunnel(int tun_fd, int dns_fd)
285 {
286         struct timeval tv;
287         fd_set fds;
288         int i;
289         int j;
290
291         while (running) {
292                 if (users_waiting_on_reply()) {
293                         tv.tv_sec = 0;
294                         tv.tv_usec = 5000;
295                 } else {
296                         tv.tv_sec = 1;
297                         tv.tv_usec = 0;
298                 }
299
300                 FD_ZERO(&fds);
301                 /* TODO : use some kind of packet queue */
302                 if(!all_users_waiting_to_send()) {
303                         FD_SET(tun_fd, &fds);
304                 }
305                 FD_SET(dns_fd, &fds);
306
307                 i = select(MAX(tun_fd, dns_fd) + 1, &fds, NULL, NULL, &tv);
308                 
309                 if(i < 0) {
310                         if (running) 
311                                 warn("select");
312                         return 1;
313                 }
314         
315                 if (i==0) {     
316                         for (j = 0; j < USERS; j++) {
317                                 if (users[j].q.id != 0) {
318                                         write_dns(dns_fd, &(users[j].q), users[j].outpacket.data, users[j].outpacket.len);
319                                         users[j].outpacket.len = 0;
320                                         users[j].q.id = 0;
321                                 }
322                         }
323                 } else {
324                         if(FD_ISSET(tun_fd, &fds)) {
325                                 tunnel_tun(tun_fd, dns_fd);
326                                 continue;
327                         }
328                         if(FD_ISSET(dns_fd, &fds)) {
329                                 tunnel_dns(tun_fd, dns_fd);
330                                 continue;
331                         } 
332                 }
333         }
334
335         return 0;
336 }
337
338 static int
339 read_dns(int fd, struct query *q, char *buf, int buflen)
340 {
341         struct sockaddr_in from;
342         char packet[64*1024];
343         char *domain;
344         socklen_t addrlen;
345         int rv;
346         int r;
347
348         addrlen = sizeof(struct sockaddr);
349         r = recvfrom(fd, packet, sizeof(packet), 0, (struct sockaddr*)&from, &addrlen);
350
351         if (r > 0) {
352                 dns_decode(buf, buflen, q, QR_QUERY, packet, r);
353                 domain = strstr(q->name, topdomain);
354                 rv = decode_data(buf, buflen, q->name, domain);
355                 q->fromlen = addrlen;
356                 memcpy((struct sockaddr*)&q->from, (struct sockaddr*)&from, addrlen);
357         } else if (r < 0) {     
358                 /* Error */
359                 perror("recvfrom");
360                 rv = 0;
361         }
362
363         return rv;
364 }
365
366 static void
367 write_dns(int fd, struct query *q, char *data, int datalen)
368 {
369         char buf[64*1024];
370         int len;
371
372         len = dns_encode(buf, sizeof(buf), q, QR_ANSWER, data, datalen);
373         
374         sendto(fd, buf, len, 0, (struct sockaddr*)&q->from, q->fromlen);
375 }
376
377 static void
378 usage() {
379         extern char *__progname;
380
381         printf("Usage: %s [-v] [-h] [-f] [-u user] [-t chrootdir] [-d device] [-m mtu] "
382                 "[-l ip address to listen on] [-p port] [-P password]"
383                 " tunnel_ip topdomain\n", __progname);
384         exit(2);
385 }
386
387 static void
388 help() {
389         extern char *__progname;
390
391         printf("iodine IP over DNS tunneling server\n");
392         printf("Usage: %s [-v] [-h] [-f] [-u user] [-t chrootdir] [-d device] [-m mtu] "
393                 "[-l ip address to listen on] [-p port] [-P password]"
394                 " tunnel_ip topdomain\n", __progname);
395         printf("  -v to print version info and exit\n");
396         printf("  -h to print this help and exit\n");
397         printf("  -f to keep running in foreground\n");
398         printf("  -u name to drop privileges and run as user 'name'\n");
399         printf("  -t dir to chroot to directory dir\n");
400         printf("  -d device to set tunnel device name\n");
401         printf("  -m mtu to set tunnel device mtu\n");
402         printf("  -l ip address to listen on for incoming dns traffic (default 0.0.0.0)\n");
403         printf("  -p port to listen on for incoming dns traffic (default 53)\n");
404         printf("  -P password used for authentication (max 32 chars will be used)\n");
405         printf("tunnel_ip is the IP number of the local tunnel interface.\n");
406         printf("topdomain is the FQDN that is delegated to this server.\n");
407         exit(0);
408 }
409
410 static void
411 version() {
412         printf("iodine IP over DNS tunneling server\n");
413         printf("version: 0.4.0 from 2007-03-25\n");
414         exit(0);
415 }
416
417 int
418 main(int argc, char **argv)
419 {
420         int choice;
421         int tun_fd;
422         int dnsd_fd;
423         char *newroot;
424         char *username;
425         char *device;
426         int foreground;
427         int mtu;
428         struct passwd *pw;
429         in_addr_t listen_ip;
430         int port;
431
432         username = NULL;
433         newroot = NULL;
434         device = NULL;
435         foreground = 0;
436         mtu = 1024;
437         listen_ip = INADDR_ANY;
438         port = 53;
439
440         memset(password, 0, 33);
441         srand(time(NULL));
442         
443         while ((choice = getopt(argc, argv, "vfhu:t:d:m:l:p:P:")) != -1) {
444                 switch(choice) {
445                 case 'v':
446                         version();
447                         break;
448                 case 'f':
449                         foreground = 1;
450                         break;
451                 case 'h':
452                         help();
453                         break;
454                 case 'u':
455                         username = optarg;
456                         break;
457                 case 't':
458                         newroot = optarg;
459                         break;
460                 case 'd':
461                         device = optarg;
462                         break;
463                 case 'm':
464                         mtu = atoi(optarg);
465                         break;
466                 case 'l':
467                         listen_ip = inet_addr(optarg);
468                         break;
469                 case 'p':
470                         port = atoi(optarg);
471                         if (port) {
472                                 printf("ALERT! Other dns servers expect you to run on port 53.\n");
473                                 printf("You must manually forward port 53 to port %d for things to work.\n", port);
474                         }
475                         break;
476                 case 'P':
477                         strncpy(password, optarg, 32);
478                         password[32] = 0;
479                         break;
480                 default:
481                         usage();
482                         break;
483                 }
484         }
485
486         argc -= optind;
487         argv += optind;
488
489         if (geteuid() != 0) {
490                 printf("Run as root and you'll be happy.\n");
491                 usage();
492         }
493
494         if (argc != 2) 
495                 usage();
496
497         topdomain = strdup(argv[1]);
498
499         if (username) {
500                 pw = getpwnam(username);
501                 if (!pw) {
502                         printf("User %s does not exist!\n", username);
503                         usage();
504                 }
505         }
506
507         if (mtu == 0) {
508                 printf("Bad MTU given.\n");
509                 usage();
510         }
511
512         if (listen_ip == INADDR_NONE) {
513                 printf("Bad IP address to listen on.\n");
514                 usage();
515         }
516
517         if (strlen(password) == 0) {
518                 printf("Enter password on stdin:\n");
519                 scanf("%32s", password);
520                 password[32] = 0;
521         }
522
523         if ((tun_fd = open_tun(device)) == -1)
524                 goto cleanup0;
525         if (tun_setip(argv[0]) != 0 || tun_setmtu(mtu) != 0)
526                 goto cleanup1;
527         if ((dnsd_fd = open_dns(port, listen_ip)) == -1) 
528                 goto cleanup2;
529
530         my_ip = inet_addr(argv[0]);
531         my_mtu = mtu;
532         init_users(my_ip);
533
534         printf("Listening to dns for domain %s\n", argv[1]);
535
536         do_chroot(newroot);
537
538         signal(SIGINT, sigint);
539         if (username) {
540                 if (setgid(pw->pw_gid) < 0 || setuid(pw->pw_uid) < 0) {
541                         printf("Could not switch to user %s!\n", username);
542                         usage();
543                 }
544         }
545         
546         if (!foreground) {
547                 do_detach();
548         }
549         
550         tunnel(tun_fd, dnsd_fd);
551
552 cleanup2:
553         close_dns(dnsd_fd);
554 cleanup1:
555         close_tun(tun_fd);      
556 cleanup0:
557
558         return 0;
559 }