source: src/iodine.c @ b38293

Revision b38293, 15.1 KB checked in by Erik Ekman <yarrick@…>, 5 years ago (diff)

Stop client if server is restarted

  • Property mode set to 100644
Line 
1/*
2 * Copyright (c) 2006-2008 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/param.h>
27#include <sys/time.h>
28#include <sys/socket.h>
29#include <fcntl.h>
30#include <err.h>
31#include <grp.h>
32#include <pwd.h>
33#include <arpa/inet.h>
34#include <zlib.h>
35#include <arpa/nameser.h>
36#ifdef DARWIN
37#include <arpa/nameser8_compat.h>
38#endif
39
40#include "common.h"
41#include "encoding.h"
42#include "base32.h"
43#include "dns.h"
44#include "login.h"
45#include "tun.h"
46#include "version.h"
47
48static void send_ping(int fd);
49static void send_chunk(int fd);
50static int build_hostname(char *buf, size_t buflen,
51        const char *data, const size_t datalen,
52        const char *topdomain, struct encoder *encoder);
53
54static int running = 1;
55static char password[33];
56
57static struct sockaddr_in nameserv;
58static char *topdomain;
59
60static uint16_t rand_seed;
61
62/* Current IP packet */
63static struct packet packet;
64
65/* My userid at the server */
66static char userid;
67
68/* DNS id for next packet */
69static uint16_t chunkid;
70
71/* Base32 encoder used for non-data packets */
72static struct encoder *b32;
73
74/* The encoder used for data packets
75 * Defaults to Base32, can be changed after handshake */
76static struct encoder *dataenc;
77
78/* result of case preservation check done after login */
79static int case_preserved;
80
81#if !defined(BSD) && !defined(__GLIBC__)
82static char *__progname;
83#endif
84
85static void
86sighandler(int sig)
87{
88        running = 0;
89}
90
91static void
92send_query(int fd, char *hostname)
93{
94        char packet[4096];
95        struct query q;
96        size_t len;
97
98        q.id = ++chunkid;
99        q.type = T_NULL;
100
101        len = dns_encode(packet, sizeof(packet), &q, QR_QUERY, hostname, strlen(hostname));
102
103        sendto(fd, packet, len, 0, (struct sockaddr*)&nameserv, sizeof(nameserv));
104}
105
106static void
107send_packet(int fd, char cmd, const char *data, const size_t datalen)
108{
109        char buf[4096];
110
111        buf[0] = cmd;
112       
113        build_hostname(buf + 1, sizeof(buf) - 1, data, datalen, topdomain, b32);
114        send_query(fd, buf);
115}
116
117static int
118build_hostname(char *buf, size_t buflen,
119                const char *data, const size_t datalen,
120                const char *topdomain, struct encoder *encoder)
121{
122        int encsize;
123        size_t space;
124        char *b;
125
126
127        space = MIN(0xFF, buflen) - strlen(topdomain) - 2;
128        if (!encoder->places_dots())
129                space -= (space / 62); /* space for dots */
130
131        memset(buf, 0, buflen);
132       
133        encsize = encoder->encode(buf, &space, data, datalen);
134
135        if (!encoder->places_dots())
136                inline_dotify(buf, buflen);
137
138        b = buf;
139        b += strlen(buf);
140
141        if (*b != '.')
142                *b++ = '.';
143
144        strncpy(b, topdomain, strlen(topdomain)+1);
145
146        return space;
147}
148
149int
150is_sending()
151{
152        return (packet.len != 0);
153}
154
155int
156read_dns(int fd, char *buf, int buflen)
157{
158        struct sockaddr_in from;
159        char data[64*1024];
160        socklen_t addrlen;
161        struct query q;
162        int rv;
163        int r;
164
165        addrlen = sizeof(struct sockaddr);
166        if ((r = recvfrom(fd, data, sizeof(data), 0,
167                          (struct sockaddr*)&from, &addrlen)) == -1) {
168                warn("recvfrom");
169                return 0;
170        }
171
172        rv = dns_decode(buf, buflen, &q, QR_ANSWER, data, r);
173
174        if (is_sending() && chunkid == q.id) {
175                /* Got ACK on sent packet */
176                packet.offset += packet.sentlen;
177                if (packet.offset == packet.len) {
178                        /* Packet completed */
179                        packet.offset = 0;
180                        packet.len = 0;
181                        packet.sentlen = 0;
182                } else {
183                        /* More to send */
184                        send_chunk(fd);
185                }
186        }
187        return rv;
188}
189
190
191static int
192tunnel_tun(int tun_fd, int dns_fd)
193{
194        unsigned long outlen;
195        unsigned long inlen;
196        char out[64*1024];
197        char in[64*1024];
198        size_t read;
199
200        if ((read = read_tun(tun_fd, in, sizeof(in))) <= 0)
201                return -1;
202
203        outlen = sizeof(out);
204        inlen = read;
205        compress2((uint8_t*)out, &outlen, (uint8_t*)in, inlen, 9);
206
207        memcpy(packet.data, out, MIN(outlen, sizeof(packet.data)));
208        packet.sentlen = 0;
209        packet.offset = 0;
210        packet.len = outlen;
211
212        send_chunk(dns_fd);
213
214        return read;
215}
216
217static int
218tunnel_dns(int tun_fd, int dns_fd)
219{
220        unsigned long outlen;
221        unsigned long inlen;
222        char out[64*1024];
223        char in[64*1024];
224        size_t read;
225
226        if ((read = read_dns(dns_fd, in, sizeof(in))) <= 0)
227                return -1;
228               
229        if (read == 5 && strncmp("BADIP", in, 5) == 0) {
230                errx(3, "Not recognized by server. "
231                        "Has it been restarted? Aborting");
232                /* NOTREACHED */
233        }
234
235        outlen = sizeof(out);
236        inlen = read;
237        if (uncompress((uint8_t*)out, &outlen, (uint8_t*)in, inlen) != Z_OK)
238                return -1;
239
240        write_tun(tun_fd, out, outlen);
241
242        return read;
243}
244
245static int
246tunnel(int tun_fd, int dns_fd)
247{
248        struct timeval tv;
249        fd_set fds;
250        int rv;
251        int i;
252        int short_ping;
253
254        rv = 0;
255        short_ping = 0;
256
257        while (running) {
258                if (short_ping) {
259                        tv.tv_sec = 0;
260                        tv.tv_usec = 5000;
261                        short_ping = 0;
262                } else {
263                        tv.tv_sec = 5;
264                        tv.tv_usec = 0;
265                }
266
267                FD_ZERO(&fds);
268                if (!is_sending())
269                        FD_SET(tun_fd, &fds);
270                FD_SET(dns_fd, &fds);
271
272                i = select(MAX(tun_fd, dns_fd) + 1, &fds, NULL, NULL, &tv);
273               
274                if (running == 0)
275                        break;
276
277                if (i < 0)
278                        err(1, "select");
279
280                if (i == 0) { /* timeout */
281                        send_ping(dns_fd);
282                } else {
283                        if (FD_ISSET(tun_fd, &fds)) {
284                                if (tunnel_tun(tun_fd, dns_fd) <= 0)
285                                        continue;
286                        }
287                        if (FD_ISSET(dns_fd, &fds)) {
288                                if (tunnel_dns(tun_fd, dns_fd) <= 0)
289                                        continue;
290                                /* If we have nothing to send within x ms, send a ping
291                                 * to get more data from server */
292                                if (!is_sending())
293                                        short_ping = 1;
294                        }
295                }
296        }
297
298        return rv;
299}
300
301static void
302send_chunk(int fd)
303{
304        char hex[] = "0123456789ABCDEF";
305        char buf[4096];
306        int avail;
307        int code;
308        char *p;
309
310        p = packet.data;
311        p += packet.offset;
312        avail = packet.len - packet.offset;
313
314        packet.sentlen = build_hostname(buf + 1, sizeof(buf) - 1, p, avail, topdomain, dataenc);
315
316        if (packet.sentlen == avail)
317                code = 1;
318        else
319                code = 0;
320               
321        code |= (userid << 1);
322        buf[0] = hex[code];
323
324        send_query(fd, buf);
325}
326
327void
328send_login(int fd, char *login, int len)
329{
330        char data[19];
331
332        memset(data, 0, sizeof(data));
333        data[0] = userid;
334        memcpy(&data[1], login, MIN(len, 16));
335
336        data[17] = (rand_seed >> 8) & 0xff;
337        data[18] = (rand_seed >> 0) & 0xff;
338       
339        rand_seed++;
340
341        send_packet(fd, 'L', data, sizeof(data));
342}
343
344static void
345send_ping(int fd)
346{
347        char data[3];
348       
349        if (is_sending()) {
350                packet.sentlen = 0;
351                packet.offset = 0;
352                packet.len = 0;
353        }
354
355        data[0] = userid;
356        data[1] = (rand_seed >> 8) & 0xff;
357        data[2] = (rand_seed >> 0) & 0xff;
358       
359        rand_seed++;
360
361        send_packet(fd, 'P', data, sizeof(data));
362}
363
364void
365send_version(int fd, uint32_t version)
366{
367        char data[6];
368
369        data[0] = (version >> 24) & 0xff;
370        data[1] = (version >> 16) & 0xff;
371        data[2] = (version >> 8) & 0xff;
372        data[3] = (version >> 0) & 0xff;
373
374        data[4] = (rand_seed >> 8) & 0xff;
375        data[5] = (rand_seed >> 0) & 0xff;
376       
377        rand_seed++;
378
379        send_packet(fd, 'V', data, sizeof(data));
380}
381
382void
383send_case_check(int fd)
384{
385        char buf[512] = "zZaAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyY123-4560789.";
386
387        strncat(buf, topdomain, 512 - strlen(buf));
388        send_query(fd, buf);
389}
390
391static int
392handshake(int dns_fd)
393{
394        struct timeval tv;
395        uint32_t payload;
396        char server[65];
397        char client[65];
398        char login[16];
399        char in[4096];
400        fd_set fds;
401        int read;
402        int mtu;
403        int seed;
404        int i;
405        int r;
406
407        for (i = 0; running && i < 5; i++) {
408                tv.tv_sec = i + 1;
409                tv.tv_usec = 0;
410
411                send_version(dns_fd, VERSION);
412               
413                FD_ZERO(&fds);
414                FD_SET(dns_fd, &fds);
415
416                r = select(dns_fd + 1, &fds, NULL, NULL, &tv);
417
418                if(r > 0) {
419                        read = read_dns(dns_fd, in, sizeof(in));
420                       
421                        if(read <= 0) {
422                                if (read == 0) {
423                                        warn("handshake read");
424                                }
425                                /* if read < 0 then warning has been printed already */
426                                continue;
427                        }
428
429                        if (read >= 9) {
430                                payload =  (((in[4] & 0xff) << 24) |
431                                                ((in[5] & 0xff) << 16) |
432                                                ((in[6] & 0xff) << 8) |
433                                                ((in[7] & 0xff)));
434
435                                if (strncmp("VACK", in, 4) == 0) {
436                                        seed = payload;
437                                        userid = in[8];
438
439                                        printf("Version ok, both using protocol v 0x%08x. You are user #%d\n", VERSION, userid);
440                                        goto perform_login;
441                                } else if (strncmp("VNAK", in, 4) == 0) {
442                                        errx(1, "You use protocol v 0x%08x, server uses v 0x%08x. Giving up",
443                                                        VERSION, payload);
444                                        /* NOTREACHED */
445                                } else if (strncmp("VFUL", in, 4) == 0) {
446                                        errx(1, "Server full, all %d slots are taken. Try again later", payload);
447                                        /* NOTREACHED */
448                                }
449                        } else
450                                warnx("did not receive proper login challenge");
451                }
452               
453                printf("Retrying version check...\n");
454        }
455        errx(1, "couldn't connect to server");
456        /* NOTREACHED */
457       
458perform_login:
459        login_calculate(login, 16, password, seed);
460       
461        for (i=0; running && i<5 ;i++) {
462                tv.tv_sec = i + 1;
463                tv.tv_usec = 0;
464
465                send_login(dns_fd, login, 16);
466               
467                FD_ZERO(&fds);
468                FD_SET(dns_fd, &fds);
469
470                r = select(dns_fd + 1, &fds, NULL, NULL, &tv);
471
472                if(r > 0) {
473                        read = read_dns(dns_fd, in, sizeof(in));
474                       
475                        if(read <= 0) {
476                                warn("read");
477                                continue;
478                        }
479
480                        if (read > 0) {
481                                if (strncmp("LNAK", in, 4) == 0) {
482                                        printf("Bad password\n");
483                                        return 1;
484                                } else if (sscanf(in, "%64[^-]-%64[^-]-%d",
485                                        server, client, &mtu) == 3) {
486                                       
487                                        server[64] = 0;
488                                        client[64] = 0;
489                                        if (tun_setip(client) == 0 &&
490                                                tun_setmtu(mtu) == 0) {
491                                                goto perform_case_check;
492                                        } else {
493                                                warnx("Received handshake with bad data");
494                                        }
495                                } else {
496                                        printf("Received bad handshake\n");
497                                }
498                        }
499                }
500
501                printf("Retrying login...\n");
502        }
503        errx(1, "couldn't login to server");
504        /* NOTREACHED */
505
506perform_case_check:
507        case_preserved = 0;
508        for (i=0; running && i<5 ;i++) {
509                tv.tv_sec = i + 1;
510                tv.tv_usec = 0;
511
512                send_case_check(dns_fd);
513               
514                FD_ZERO(&fds);
515                FD_SET(dns_fd, &fds);
516
517                r = select(dns_fd + 1, &fds, NULL, NULL, &tv);
518
519                if(r > 0) {
520                        read = read_dns(dns_fd, in, sizeof(in));
521                       
522                        if(read <= 0) {
523                                warn("read");
524                                continue;
525                        }
526
527                        if (read > 0) {
528                                if (in[0] == 'z' || in[0] == 'Z') {
529                                        if (read < (26 * 2)) {
530                                                printf("Received short case reply...\n");
531                                        } else {
532                                                int k;
533
534                                                case_preserved = 1;
535                                                for (k = 0; k < 26 && case_preserved; k += 2) {
536                                                        if (in[k] == in[k+1]) {
537                                                                /* test string: zZaAbBcCdD... */
538                                                                case_preserved = 0;
539                                                        }
540                                                }
541                                                return 0;
542                                        }
543                                } else {
544                                        printf("Received bad case check reply\n");
545                                }
546                        }
547                }
548
549                printf("Retrying case check...\n");
550        }
551
552        printf("No reply on case check, continuing\n");
553        return 0;
554}
555               
556static char *
557get_resolvconf_addr()
558{
559        static char addr[16];
560        char buf[80];
561        char *rv;
562        FILE *fp;
563       
564        rv = NULL;
565
566        if ((fp = fopen("/etc/resolv.conf", "r")) == NULL)
567                err(1, "/etc/resolve.conf");
568       
569        while (feof(fp) == 0) {
570                fgets(buf, sizeof(buf), fp);
571
572                if (sscanf(buf, "nameserver %15s", addr) == 1) {
573                        rv = addr;
574                        break;
575                }
576        }
577       
578        fclose(fp);
579
580        return rv;
581}
582
583static void
584set_nameserver(const char *cp)
585{
586        struct in_addr addr;
587
588        if (inet_aton(cp, &addr) != 1)
589                errx(1, "error parsing nameserver address: '%s'", cp);
590
591        memset(&nameserv, 0, sizeof(nameserv));
592        nameserv.sin_family = AF_INET;
593        nameserv.sin_port = htons(53);
594        nameserv.sin_addr = addr;
595}
596
597static void
598usage() {
599        extern char *__progname;
600
601        printf("Usage: %s [-v] [-h] [-f] [-u user] [-t chrootdir] [-d device] "
602                        "[nameserver] topdomain\n", __progname);
603        exit(2);
604}
605
606static void
607help() {
608        extern char *__progname;
609
610        printf("iodine IP over DNS tunneling client\n");
611        printf("Usage: %s [-v] [-h] [-f] [-u user] [-t chrootdir] [-d device] "
612                        "[-P password] [nameserver] topdomain\n", __progname);
613        printf("  -v to print version info and exit\n");
614        printf("  -h to print this help and exit\n");
615        printf("  -f to keep running in foreground\n");
616        printf("  -u name to drop privileges and run as user 'name'\n");
617        printf("  -t dir to chroot to directory dir\n");
618        printf("  -d device to set tunnel device name\n");
619        printf("  -P password used for authentication (max 32 chars will be used)\n");
620        printf("nameserver is the IP number of the relaying nameserver, if absent /etc/resolv.conf is used\n");
621        printf("topdomain is the FQDN that is delegated to the tunnel endpoint.\n");
622
623        exit(0);
624}
625
626static void
627version() {
628        char *svnver;
629
630        svnver = "$Rev$ from $Date$";
631
632        printf("iodine IP over DNS tunneling client\n");
633        printf("SVN version: %s\n", svnver);
634
635        exit(0);
636}
637
638int
639main(int argc, char **argv)
640{
641        char *nameserv_addr;
642        struct passwd *pw;
643        char *username;
644        int foreground;
645        char *newroot;
646        char *device;
647        int choice;
648        int tun_fd;
649        int dns_fd;
650
651        memset(password, 0, 33);
652        username = NULL;
653        foreground = 0;
654        newroot = NULL;
655        device = NULL;
656        chunkid = 0;
657
658        b32 = get_base32_encoder();
659        dataenc = get_base32_encoder();
660       
661#if !defined(BSD) && !defined(__GLIBC__)
662        __progname = strrchr(argv[0], '/');
663        if (__progname == NULL)
664                __progname = argv[0];
665        else
666                __progname++;
667#endif
668
669        while ((choice = getopt(argc, argv, "vfhu:t:d:P:")) != -1) {
670                switch(choice) {
671                case 'v':
672                        version();
673                        break;
674                case 'f':
675                        foreground = 1;
676                        break;
677                case 'h':
678                        help();
679                        break;
680                case 'u':
681                        username = optarg;
682                        break;
683                case 't':
684                        newroot = optarg;
685                        break;
686                case 'd':
687                        device = optarg;
688                        break;
689                case 'P':
690                        strncpy(password, optarg, sizeof(password));
691                        password[sizeof(password)-1] = 0;
692                       
693                        /* XXX: find better way of cleaning up ps(1) */
694                        memset(optarg, 0, strlen(optarg));
695                        break;
696                default:
697                        usage();
698                        /* NOTREACHED */
699                }
700        }
701       
702        if (geteuid() != 0) {
703                warnx("Run as root and you'll be happy.\n");
704                usage();
705        }
706
707        argc -= optind;
708        argv += optind;
709
710        switch (argc) {
711        case 1:
712                nameserv_addr = get_resolvconf_addr();
713                topdomain = strdup(argv[0]);
714                break;
715        case 2:
716                nameserv_addr = argv[0];
717                topdomain = strdup(argv[1]);
718                break;
719        default:
720                usage();
721                /* NOTREACHED */
722        }
723
724        set_nameserver(nameserv_addr);
725
726        if(strlen(topdomain) <= 128) {
727                if(check_topdomain(topdomain)) {
728                        warnx("Topdomain contains invalid characters.\n");
729                        usage();
730                }
731        } else {
732                warnx("Use a topdomain max 128 chars long.\n");
733                usage();
734        }
735
736        if (username != NULL) {
737                if ((pw = getpwnam(username)) == NULL) {
738                        warnx("User %s does not exist!\n", username);
739                        usage();
740                }
741        }
742       
743        if (strlen(password) == 0)
744                read_password(password, sizeof(password));
745
746        if ((tun_fd = open_tun(device)) == -1)
747                goto cleanup1;
748        if ((dns_fd = open_dns(0, INADDR_ANY)) == -1)
749                goto cleanup2;
750
751        signal(SIGINT, sighandler);
752        signal(SIGTERM, sighandler);
753
754        if(handshake(dns_fd))
755                goto cleanup2;
756       
757        printf("Sending queries for %s to %s\n", topdomain, nameserv_addr);
758
759        if (foreground == 0)
760                do_detach();
761
762        if (newroot != NULL)
763                do_chroot(newroot);
764       
765        if (username != NULL) {
766                gid_t gids[1];
767                gids[0] = pw->pw_gid;
768                if (setgroups(1, gids) < 0 || setgid(pw->pw_gid) < 0 || setuid(pw->pw_uid) < 0) {
769                        warnx("Could not switch to user %s!\n", username);
770                        usage();
771                }
772        }
773       
774        tunnel(tun_fd, dns_fd);
775
776cleanup2:
777        close_dns(dns_fd);
778        close_tun(tun_fd);
779cleanup1:
780
781        return 0;
782}
Note: See TracBrowser for help on using the repository browser.