]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/ip/socksd.c
ip/torrent: remove unneeded assignment
[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         if((fd = accept(cfd, ldir)) < 0)
116                 return -1;
117
118         switch(rfork(RFMEM|RFPROC|RFNOWAIT)){
119         case -1:
120                 return -1;
121         case 0:
122                 while((r = read(fd, msg.data, sizeof(msg.data))) > 0){
123                         if(r < 4)
124                                 continue;
125                         p = msg.data;
126                         if(p[0] | p[1] | p[2])
127                                 continue;
128                         p += 3;
129                         switch(*p++){
130                         default:
131                                 continue;
132                         case 0x01:
133                                 r -= 2+1+1+4+2;
134                                 if(r < 0)
135                                         continue;
136                                 v4tov6(msg.raddr, p);
137                                 p += 4;
138                                 break;
139                         case 0x04:
140                                 r -= 2+1+1+16+2;
141                                 if(r < 0)
142                                         continue;
143                                 memmove(msg.raddr, p, 16);
144                                 p += 16;
145                                 break;
146                         }
147                         memmove(msg.rport, p, 2);
148                         p += 2;
149                         memmove(msg.data, p, r);
150                         write(rfd, &msg, sizeof(Udphdr)+r);
151                 }
152                 break;
153         default:
154                 while((r = read(rfd, &msg, sizeof(msg))) > 0){
155                         r -= sizeof(Udphdr);
156                         if(r < 0)
157                                 continue;
158                         p = msg.data;
159                         if(isv4(msg.raddr))
160                                 n = 2+1+1+4+2;
161                         else
162                                 n = 2+1+1+16+2;
163                         if(r+n > sizeof(msg.data))
164                                 r = sizeof(msg.data)-n;
165                         memmove(p+n, p, r);
166                         *p++ = 0;
167                         *p++ = 0;
168                         *p++ = 0;
169                         if(isv4(msg.raddr)){
170                                 *p++ = 0x01;
171                                 v6tov4(p, msg.raddr);
172                                 p += 4;
173                         } else {
174                                 *p++ = 0x04;
175                                 memmove(p, msg.raddr, 16);
176                                 p += 16;
177                         }
178                         memmove(p, msg.rport, 2);
179                         r += n;
180                         write(fd, msg.data, r);
181                 }
182         }
183         return -1;
184 }
185
186 int
187 sockerr(int err)
188 {
189         /* general error */
190         if(socksver == 4)
191                 return err ? 0x5b : 0x5a;
192         else
193                 return err != 0;
194 }
195
196 void
197 main(int argc, char *argv[])
198 {
199         uchar buf[8*1024], *p;
200         char addr[128], dir[40], ldir[40], *s;
201         int cmd, fd, cfd, n;
202         NetConnInfo *nc;
203
204         fmtinstall('I', eipfmt);
205
206         setnetmtpt(inside, sizeof(inside), 0);
207         setnetmtpt(outside, sizeof(outside), 0);
208         ARGBEGIN {
209         case 'x':
210                 setnetmtpt(inside, sizeof(inside), ARGF());
211                 break;
212         case 'o':
213                 setnetmtpt(outside, sizeof(outside), ARGF());
214                 break;
215         } ARGEND;
216
217         /* ver+cmd or ver+nmethod */
218         if(readn(0, buf, 2) != 2)
219                 return;
220         socksver = buf[0];
221         if(socksver < 4)
222                 return;
223         if(socksver > 5)
224                 socksver = 5;
225
226         if(socksver == 4){
227                 /* port+ip4 */
228                 if(readn(0, buf+2, 2+4) != 2+4)
229                         return;
230                 /* +user\0 */
231                 for(p = buf+2+2+4;; p++){
232                         if(p >= buf+sizeof(buf))
233                                 return;
234                         if(read(0, p, 1) != 1)
235                                 return;
236                         if(*p == 0)
237                                 break;
238                 }
239                 /* socks 4a dom hack */
240                 if((buf[4] | buf[5] | buf[6]) == 0 && buf[7]){
241                         /* +dom\0 */
242                         for(++p;; p++){
243                                 if(p >= buf+sizeof(buf))
244                                         return;
245                                 if(read(0, p, 1) != 1)
246                                         return;
247                                 if(*p == 0)
248                                         break;
249                         }
250                 }
251         } else {
252                 /* nmethod */
253                 if((n = buf[1]) > 0)
254                         if(readn(0, buf+2, n) != n)
255                                 return;
256
257                 /* ver+method */
258                 buf[0] = socksver;
259                 buf[1] = 0x00;  /* no authentication required */
260                 if(write(1, buf, 2) != 2)
261                         return;
262
263                 /* ver+cmd+res+atyp */
264                 if(readn(0, buf, 4) != 4)
265                         return;
266                 switch(buf[3]){
267                 default:
268                         return;
269                 case 0x01:      /* +ipv4 */
270                         if(readn(0, buf+4, 4+2) != 4+2)
271                                 return;
272                         break;
273                 case 0x03:      /* +len+dom[len] */
274                         if(readn(0, buf+4, 1) != 1)
275                                 return;
276                         if((n = buf[4]) == 0)
277                                 return;
278                         if(readn(0, buf+5, n+2) != n+2)
279                                 return;
280                         break;
281                 case 0x04:      /* +ipv6 */
282                         if(readn(0, buf+4, 16+2) != 16+2)
283                                 return;
284                         break;
285                 }
286         }
287
288         dir[0] = 0;
289         fd = cfd = -1;
290         cmd = buf[1];
291         switch(cmd){
292         case 0x01:      /* CONNECT */
293                 snprint(addr, sizeof(addr), "%s/tcp", outside);
294                 if((s = addr2str(addr, buf)) == nil)
295                         break;
296                 alarm(30000);
297                 fd = dial(s, 0, dir, &cfd);
298                 alarm(0);
299                 break;
300         case 0x02:      /* BIND */
301                 if(myipaddr(buf, outside) < 0)
302                         break;
303                 snprint(addr, sizeof(addr), "%s/tcp!%I!0", outside, buf);
304                 fd = announce(addr, dir);
305                 break;
306         case 0x03:      /* UDP */
307                 if(myipaddr(buf, inside) < 0)
308                         break;
309                 snprint(addr, sizeof(addr), "%s/udp!%I!0", inside, buf);
310                 fd = announce(addr, dir);
311                 break;
312         }
313
314 Reply:
315         /* reply */
316         buf[1] = sockerr(fd < 0);                       /* status */
317         if(socksver == 4){
318                 buf[0] = 0x00;                          /* vc */
319                 if(fd < 0){
320                         memset(buf+2, 0, 2+4);
321                         write(1, buf, 2+2+4);
322                         return;
323                 }
324         } else {
325                 buf[0] = socksver;                      /* ver */
326                 buf[2] = 0x00;                          /* res */
327                 if(fd < 0){
328                         buf[3] = 0x01;                  /* atyp */
329                         memset(buf+4, 0, 4+2);
330                         write(1, buf, 4+4+2);
331                         return;
332                 }
333         }
334         if((nc = getnetconninfo(dir, cfd)) == nil)
335                 return;
336         if((n = str2addr((cmd & 0x100) ? nc->raddr : nc->laddr, buf)) <= 0)
337                 return;
338         if(write(1, buf, n) != n)
339                 return;
340
341         switch(cmd){
342         default:
343                 return;
344         case 0x01:      /* CONNECT */
345                 break;
346         case 0x02:      /* BIND */
347                 cfd = listen(dir, ldir);
348                 close(fd);
349                 fd = -1;
350                 if(cfd >= 0){
351                         strcpy(dir, ldir);
352                         fd = accept(cfd, dir);
353                 }
354                 cmd |= 0x100;
355                 goto Reply;
356         case 0x102:
357                 break;          
358         case 0x03:      /* UDP */
359                 if(udprelay(fd, dir) == 0)
360                         while(read(0, buf, sizeof(buf)) > 0)
361                                 ;
362                 goto Hangup;
363         }
364         
365         /* relay data */
366         switch(rfork(RFMEM|RFPROC|RFFDG|RFNOWAIT)){
367         case -1:
368                 return;
369         case 0:
370                 dup(fd, 0);
371                 break;
372         default:
373                 dup(fd, 1);
374         }
375         while((n = read(0, buf, sizeof(buf))) > 0)
376                 if(write(1, buf, n) != n)
377                         break;
378 Hangup:
379         if(cfd >= 0)
380                 hangup(cfd);
381         postnote(PNGROUP, getpid(), "kill");
382 }
383