]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/ip/socksd.c
6in4: add -m mtu option to specify outer MTU
[plan9front.git] / sys / src / cmd / ip / socksd.c
1 #include <u.h>
2 #include <libc.h>
3 #include <ip.h>
4
5 int socksver;
6 char inside[128];
7 char outside[128];
8
9 int
10 str2addr(char *s, uchar *a)
11 {
12         uchar *a0, ip[16];
13         char *p;
14
15         if((s = strchr(s, '!')) == nil)
16                 return 0;
17         if((p = strchr(++s, '!')) == nil)
18                 return 0;
19         if(strchr(++p, '!') != nil)
20                 return 0;
21         if(parseip(ip, s) == -1)
22                 return 0;
23
24         a0 = a;
25         if(socksver == 4){
26                 a += 2;
27                 hnputs(a, atoi(p));
28                 a += 2;
29                 v6tov4(a, ip);
30                 a += 4;
31         } else {
32                 a += 3;
33                 if(isv4(ip)){
34                         *a++ = 0x01;
35                         v6tov4(a, ip);
36                         a += 4;
37                 } else {
38                         *a++ = 0x04;
39                         memmove(a, ip, 16);
40                         a += 16;
41                 }
42                 hnputs(a, atoi(p));
43                 a += 2;
44         }
45         return a - a0;
46 }
47
48 char*
49 addr2str(char *proto, uchar *a){
50         static char s[128];
51         uchar ip[16];
52         int n, port;
53
54         if(socksver == 4){
55                 a += 2;
56                 port = nhgets(a);
57                 a += 2;
58                 if((a[0] | a[1] | a[2]) == 0 && a[3]){
59                         a += 4;
60                         a += strlen((char*)a)+1;
61                         snprint(s, sizeof(s), "%s!%s!%d", proto, (char*)a, port);
62                         return s;
63                 }
64                 v4tov6(ip, a);
65         } else {
66                 a += 3;
67                 switch(*a++){
68                 default:
69                         return nil;
70                 case 0x01:
71                         v4tov6(ip, a);
72                         port = nhgets(a+4);
73                         break;
74                 case 0x04:
75                         memmove(ip, a, 16);
76                         port = nhgets(a+16);
77                         break;
78                 case 0x03:
79                         n = *a++;
80                         port = nhgets(a+n);
81                         snprint(s, sizeof(s), "%s!%.*s!%d", proto, n, (char*)a, port);
82                         return s;
83                 }
84         }
85         snprint(s, sizeof(s), "%s!%I!%d", proto, ip, port);
86         return s;
87 }
88
89 int
90 udprelay(int fd, char *dir)
91 {
92         struct {
93                 Udphdr;
94                 uchar data[8*1024];
95         } msg;
96         char addr[128], ldir[40];
97         int r, n, rfd, cfd;
98         uchar *p;
99
100         snprint(addr, sizeof(addr), "%s/udp!*!0", outside);
101         if((cfd = announce(addr, ldir)) < 0)
102                 return -1;
103         if(write(cfd, "headers", 7) != 7)
104                 return -1;
105         strcat(ldir, "/data");
106         if((rfd = open(ldir, ORDWR)) < 0)
107                 return -1;
108         close(cfd);
109         
110         if((r = rfork(RFMEM|RFPROC|RFNOWAIT)) <= 0)
111                 return r;
112
113         if((cfd = listen(dir, ldir)) < 0)
114                 return -1;
115         close(fd);      /* close inside udp server */
116         if((fd = accept(cfd, ldir)) < 0)
117                 return -1;
118
119         switch(rfork(RFMEM|RFPROC|RFNOWAIT)){
120         case -1:
121                 return -1;
122         case 0:
123                 while((r = read(fd, msg.data, sizeof(msg.data))) > 0){
124                         if(r < 4)
125                                 continue;
126                         p = msg.data;
127                         if(p[0] | p[1] | p[2])
128                                 continue;
129                         p += 3;
130                         switch(*p++){
131                         default:
132                                 continue;
133                         case 0x01:
134                                 r -= 2+1+1+4+2;
135                                 if(r < 0)
136                                         continue;
137                                 v4tov6(msg.raddr, p);
138                                 p += 4;
139                                 break;
140                         case 0x04:
141                                 r -= 2+1+1+16+2;
142                                 if(r < 0)
143                                         continue;
144                                 memmove(msg.raddr, p, 16);
145                                 p += 16;
146                                 break;
147                         }
148                         memmove(msg.rport, p, 2);
149                         p += 2;
150                         memmove(msg.data, p, r);
151                         write(rfd, &msg, sizeof(Udphdr)+r);
152                 }
153                 break;
154         default:
155                 while((r = read(rfd, &msg, sizeof(msg))) > 0){
156                         r -= sizeof(Udphdr);
157                         if(r < 0)
158                                 continue;
159                         p = msg.data;
160                         if(isv4(msg.raddr))
161                                 n = 2+1+1+4+2;
162                         else
163                                 n = 2+1+1+16+2;
164                         if(r+n > sizeof(msg.data))
165                                 r = sizeof(msg.data)-n;
166                         memmove(p+n, p, r);
167                         *p++ = 0;
168                         *p++ = 0;
169                         *p++ = 0;
170                         if(isv4(msg.raddr)){
171                                 *p++ = 0x01;
172                                 v6tov4(p, msg.raddr);
173                                 p += 4;
174                         } else {
175                                 *p++ = 0x04;
176                                 memmove(p, msg.raddr, 16);
177                                 p += 16;
178                         }
179                         memmove(p, msg.rport, 2);
180                         r += n;
181                         write(fd, msg.data, r);
182                 }
183         }
184         return -1;
185 }
186
187 int
188 sockerr(int err)
189 {
190         /* general error */
191         if(socksver == 4)
192                 return err ? 0x5b : 0x5a;
193         else
194                 return err != 0;
195 }
196
197 void
198 main(int argc, char *argv[])
199 {
200         uchar buf[8*1024], *p;
201         char addr[128], dir[40], ldir[40], *s;
202         int cmd, fd, cfd, n;
203         NetConnInfo *nc;
204
205         fmtinstall('I', eipfmt);
206
207         setnetmtpt(inside, sizeof(inside), 0);
208         setnetmtpt(outside, sizeof(outside), 0);
209         ARGBEGIN {
210         case 'x':
211                 setnetmtpt(inside, sizeof(inside), ARGF());
212                 break;
213         case 'o':
214                 setnetmtpt(outside, sizeof(outside), ARGF());
215                 break;
216         } ARGEND;
217
218         /* ver+cmd or ver+nmethod */
219         if(readn(0, buf, 2) != 2)
220                 return;
221         socksver = buf[0];
222         if(socksver < 4)
223                 return;
224         if(socksver > 5)
225                 socksver = 5;
226
227         if(socksver == 4){
228                 /* port+ip4 */
229                 if(readn(0, buf+2, 2+4) != 2+4)
230                         return;
231                 /* +user\0 */
232                 for(p = buf+2+2+4;; p++){
233                         if(p >= buf+sizeof(buf))
234                                 return;
235                         if(read(0, p, 1) != 1)
236                                 return;
237                         if(*p == 0)
238                                 break;
239                 }
240                 /* socks 4a dom hack */
241                 if((buf[4] | buf[5] | buf[6]) == 0 && buf[7]){
242                         /* +dom\0 */
243                         for(++p;; p++){
244                                 if(p >= buf+sizeof(buf))
245                                         return;
246                                 if(read(0, p, 1) != 1)
247                                         return;
248                                 if(*p == 0)
249                                         break;
250                         }
251                 }
252         } else {
253                 /* nmethod */
254                 if((n = buf[1]) > 0)
255                         if(readn(0, buf+2, n) != n)
256                                 return;
257
258                 /* ver+method */
259                 buf[0] = socksver;
260                 buf[1] = 0x00;  /* no authentication required */
261                 if(write(1, buf, 2) != 2)
262                         return;
263
264                 /* ver+cmd+res+atyp */
265                 if(readn(0, buf, 4) != 4)
266                         return;
267                 switch(buf[3]){
268                 default:
269                         return;
270                 case 0x01:      /* +ipv4 */
271                         if(readn(0, buf+4, 4+2) != 4+2)
272                                 return;
273                         break;
274                 case 0x03:      /* +len+dom[len] */
275                         if(readn(0, buf+4, 1) != 1)
276                                 return;
277                         if((n = buf[4]) == 0)
278                                 return;
279                         if(readn(0, buf+5, n+2) != n+2)
280                                 return;
281                         break;
282                 case 0x04:      /* +ipv6 */
283                         if(readn(0, buf+4, 16+2) != 16+2)
284                                 return;
285                         break;
286                 }
287         }
288
289         dir[0] = 0;
290         fd = cfd = -1;
291         cmd = buf[1];
292         switch(cmd){
293         case 0x01:      /* CONNECT */
294                 snprint(addr, sizeof(addr), "%s/tcp", outside);
295                 if((s = addr2str(addr, buf)) == nil)
296                         break;
297                 alarm(30000);
298                 fd = dial(s, 0, dir, &cfd);
299                 alarm(0);
300                 break;
301         case 0x02:      /* BIND */
302                 if(myipaddr(buf, outside) < 0)
303                         break;
304                 snprint(addr, sizeof(addr), "%s/tcp!%I!0", outside, buf);
305                 fd = announce(addr, dir);
306                 break;
307         case 0x03:      /* UDP */
308                 if(myipaddr(buf, inside) < 0)
309                         break;
310                 snprint(addr, sizeof(addr), "%s/udp!%I!0", inside, buf);
311                 fd = announce(addr, dir);
312                 break;
313         }
314
315 Reply:
316         /* reply */
317         buf[1] = sockerr(fd < 0);                       /* status */
318         if(socksver == 4){
319                 buf[0] = 0x00;                          /* vc */
320                 if(fd < 0){
321                         memset(buf+2, 0, 2+4);
322                         write(1, buf, 2+2+4);
323                         return;
324                 }
325         } else {
326                 buf[0] = socksver;                      /* ver */
327                 buf[2] = 0x00;                          /* res */
328                 if(fd < 0){
329                         buf[3] = 0x01;                  /* atyp */
330                         memset(buf+4, 0, 4+2);
331                         write(1, buf, 4+4+2);
332                         return;
333                 }
334         }
335         if((nc = getnetconninfo(dir, cfd)) == nil)
336                 return;
337         if((n = str2addr((cmd & 0x100) ? nc->raddr : nc->laddr, buf)) <= 0)
338                 return;
339         if(write(1, buf, n) != n)
340                 return;
341
342         switch(cmd){
343         default:
344                 return;
345         case 0x01:      /* CONNECT */
346                 break;
347         case 0x02:      /* BIND */
348                 cfd = listen(dir, ldir);
349                 close(fd);
350                 fd = -1;
351                 if(cfd >= 0){
352                         strcpy(dir, ldir);
353                         fd = accept(cfd, dir);
354                 }
355                 cmd |= 0x100;
356                 goto Reply;
357         case 0x102:
358                 break;          
359         case 0x03:      /* UDP */
360                 if(udprelay(fd, dir) == 0)
361                         while(read(0, buf, sizeof(buf)) > 0)
362                                 ;
363                 goto Hangup;
364         }
365         
366         /* relay data */
367         switch(rfork(RFMEM|RFPROC|RFFDG|RFNOWAIT)){
368         case -1:
369                 return;
370         case 0:
371                 dup(fd, 0);
372                 break;
373         default:
374                 dup(fd, 1);
375         }
376         while((n = read(0, buf, sizeof(buf))) > 0)
377                 if(write(1, buf, n) != n)
378                         break;
379 Hangup:
380         if(cfd >= 0)
381                 hangup(cfd);
382         postnote(PNGROUP, getpid(), "kill");
383 }
384