[svn-inject] Applying Debian modifications to trunk
[debian/iodine.git] / iodine.c
1 /*
2  * Copyright (c) 2006 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 <netinet/in.h>
24 #include <sys/types.h>
25 #include <sys/socket.h>
26 #include <sys/stat.h>
27 #include <fcntl.h>
28 #include <err.h>
29 #include <pwd.h>
30 #include <arpa/inet.h>
31 #include <zlib.h>
32
33 #include "tun.h"
34 #include "structs.h"
35 #include "dns.h"
36
37 #ifndef MAX
38 #define MAX(a,b) ((a)>(b)?(a):(b))
39 #endif
40         
41 int running = 1;
42
43 static void
44 sighandler(int sig) {
45         running = 0;
46 }
47
48 static int
49 tunnel(int tun_fd, int dns_fd)
50 {
51         char out[64*1024];
52         char in[64*1024];
53         struct timeval tv;
54         long outlen;
55         fd_set fds;
56         int read;
57         int i;
58         int rv;
59
60         rv = 0;
61
62         while (running) {
63                 tv.tv_sec = 1;
64                 tv.tv_usec = 0;
65
66                 FD_ZERO(&fds);
67                 if (!dns_sending()) 
68                         FD_SET(tun_fd, &fds);
69                 FD_SET(dns_fd, &fds);
70
71                 i = select(MAX(tun_fd, dns_fd) + 1, &fds, NULL, NULL, &tv);
72
73                 if (!running) {
74                         rv = 1;
75                         break;
76                 }
77                 
78                 if(i < 0) {
79                         warn("select");
80                         rv = 1;
81                         break;
82                 } else if (i > 0) {
83                         if(FD_ISSET(tun_fd, &fds)) {
84                                 read = read_tun(tun_fd, in, sizeof(in));
85                                 if(read <= 0)
86                                         continue;
87
88                                 outlen = sizeof(out);
89                                 compress2(out, &outlen, in, read, 9);
90                                 dns_handle_tun(dns_fd, out, outlen);
91                         }
92                         if(FD_ISSET(dns_fd, &fds)) {
93                                 read = dns_read(dns_fd, in, sizeof(in));
94                                 if (read <= 0) 
95                                         continue;
96
97                                 outlen = sizeof(out);
98                                 uncompress(out, &outlen, in, read);
99
100                                 write_tun(tun_fd, out, outlen);
101                                 if (!dns_sending()) 
102                                         dns_ping(dns_fd);
103                         } 
104                 } else
105                         dns_ping(dns_fd);
106         }
107
108         return rv;
109 }
110
111 static int
112 handshake(int dns_fd)
113 {
114         struct timeval tv;
115         char server[65];
116         char client[65];
117         char in[4096];
118         int timeout;
119         fd_set fds;
120         int read;
121         int mtu;
122         int i;
123         int r;
124
125         timeout = 1;
126         
127         for (i=0; running && i<5 ;i++) {
128                 tv.tv_sec = timeout++;
129                 tv.tv_usec = 0;
130
131                 dns_handshake(dns_fd);
132                 
133                 FD_ZERO(&fds);
134                 FD_SET(dns_fd, &fds);
135
136                 r = select(dns_fd + 1, &fds, NULL, NULL, &tv);
137
138                 if(r > 0) {
139                         read = dns_read(dns_fd, in, sizeof(in));
140                         
141                         if(read < 0) {
142                                 perror("read");
143                                 continue;
144                         }
145
146                         if (read > 0) {
147                                 if (sscanf(in, "%64[^-]-%64[^-]-%d", 
148                                         server, client, &mtu) == 3) {
149                                         
150                                         server[64] = 0;
151                                         client[64] = 0;
152                                         if (tun_setip(client) == 0 && 
153                                                 tun_setmtu(mtu) == 0) {
154
155                                                 return 0;
156                                         } else {
157                                                 warn("Received handshake with bad data");
158                                         }
159                                 } else {
160                                         printf("Received bad handshake\n");
161                                 }
162                         }
163                 }
164
165                 printf("Retrying...\n");
166         }
167
168         return 1;
169 }
170
171 static void
172 usage() {
173         extern char *__progname;
174
175         printf("Usage: %s [-v] [-h] [-f] [-u user] [-t chrootdir] [-d device] "
176                         "nameserver topdomain\n", __progname);
177         exit(2);
178 }
179
180 static void
181 help() {
182         extern char *__progname;
183
184         printf("iodine IP over DNS tunneling client\n");
185         printf("Usage: %s [-v] [-h] [-f] [-u user] [-t chrootdir] [-d device] "
186                         "nameserver topdomain\n", __progname);
187         printf("  -v to print version info and exit\n");
188         printf("  -h to print this help and exit\n");
189         printf("  -f to keep running in foreground\n");
190         printf("  -u name to drop privileges and run as user 'name'\n");
191         printf("  -t dir to chroot to directory dir\n");
192         printf("  -d device to set tunnel device name\n");
193         printf("nameserver is the IP number of the relaying nameserver\n");
194         printf("topdomain is the FQDN that is delegated to the tunnel endpoint.\n");
195         exit(0);
196 }
197
198 static void
199 version() {
200         printf("iodine IP over DNS tunneling client\n");
201         printf("version: 0.3.4 from 2006-11-08\n");
202         exit(0);
203 }
204
205 int
206 main(int argc, char **argv)
207 {
208         struct passwd *pw;
209         char *username;
210         int foreground;
211         char *newroot;
212         char *device;
213         int choice;
214         int tun_fd;
215         int dns_fd;
216
217         username = NULL;
218         foreground = 0;
219         newroot = NULL;
220         device = NULL;
221         
222         while ((choice = getopt(argc, argv, "vfhu:t:d:")) != -1) {
223                 switch(choice) {
224                 case 'v':
225                         version();
226                         break;
227                 case 'f':
228                         foreground = 1;
229                         break;
230                 case 'h':
231                         help();
232                         break;
233                 case 'u':
234                         username = optarg;
235                         break;
236                 case 't':
237                         newroot = optarg;
238                         break;
239                 case 'd':
240                         device = optarg;
241                         break;
242                 default:
243                         usage();
244                         break;
245                 }
246         }
247         
248         if (geteuid() != 0) {
249                 printf("Run as root and you'll be happy.\n");
250                 usage();
251         }
252
253         argc -= optind;
254         argv += optind;
255         
256         if (argc != 2) 
257                 usage();
258         
259         if(username) {
260                 pw = getpwnam(username);
261                 if (!pw) {
262                         printf("User %s does not exist!\n", username);
263                         usage();
264                 }
265         }
266
267         if ((tun_fd = open_tun(device)) == -1)
268                 goto cleanup1;
269         if ((dns_fd = open_dns(argv[1], 0, INADDR_ANY)) == -1)
270                 goto cleanup2;
271         if (dns_settarget(argv[0]) == -1)
272                 goto cleanup2;
273
274         printf("Sending queries for %s to %s\n", argv[1], argv[0]);
275
276         signal(SIGINT, sighandler);
277         signal(SIGTERM, sighandler);
278
279         if(handshake(dns_fd))
280                 goto cleanup2;
281
282         if (newroot) {
283                 if (chroot(newroot) != 0 || chdir("/") != 0)
284                         err(1, "%s", newroot);
285                 seteuid(geteuid());
286                 setuid(getuid());
287         }
288         
289         if (!foreground) {
290                 printf("Detaching from terminal...\n");
291                 daemon(0, 0);
292                 umask(0);
293                 alarm(0);
294         }
295         
296         if (username) {
297                 if (setgid(pw->pw_gid) < 0 || setuid(pw->pw_uid) < 0) {
298                         printf("Could not switch to user %s!\n", username);
299                         usage();
300                 }
301         }
302
303         tunnel(tun_fd, dns_fd);
304
305 cleanup2:
306         close_dns(dns_fd);
307         close_tun(tun_fd);
308 cleanup1:
309
310         return 0;
311 }