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