#include <u.h>#include <libc.h>#include <ip.h> int socksver;char inside[128];char outside[128]; intstr2addr(char *s, uchar *a){ uchar *a0, ip[16]; char *p; if((s = strchr(s, '!')) == nil) return 0; if((p = strchr(++s, '!')) == nil) return 0; if(strchr(++p, '!') != nil) return 0; if(parseip(ip, s) == -1) return 0; a0 = a; if(socksver == 4){ a += 2; hnputs(a, atoi(p)); a += 2; v6tov4(a, ip); a += 4; } else { a += 3; if(isv4(ip)){ *a++ = 0x01; v6tov4(a, ip); a += 4; } else { *a++ = 0x04; memmove(a, ip, 16); a += 16; } hnputs(a, atoi(p)); a += 2; } return a - a0;} char*addr2str(char *proto, uchar *a){ static char s[128]; uchar ip[16]; int n, port; if(socksver == 4){ a += 2; port = nhgets(a); a += 2; if((a[0] | a[1] | a[2]) == 0 && a[3]){ a += 4; a += strlen((char*)a)+1; snprint(s, sizeof(s), "%s!%s!%d", proto, (char*)a, port); return s; } v4tov6(ip, a); } else { a += 3; switch(*a++){ default: return nil; case 0x01: v4tov6(ip, a); port = nhgets(a+4); break; case 0x04: memmove(ip, a, 16); port = nhgets(a+16); break; case 0x03: n = *a++; port = nhgets(a+n); n = utfnlen((char*)a, n); snprint(s, sizeof(s), "%s!%.*s!%d", proto, n, (char*)a, port); return s; } } snprint(s, sizeof(s), "%s!%I!%d", proto, ip, port); return s;} intudprelay(int fd, char *dir){ struct { Udphdr; uchar data[8*1024]; } msg; char addr[128], ldir[40]; int r, n, rfd, cfd; uchar *p; snprint(addr, sizeof(addr), "%s/udp!*!0", outside); if((cfd = announce(addr, ldir)) < 0) return -1; if(write(cfd, "headers", 7) != 7) return -1; strcat(ldir, "/data"); if((rfd = open(ldir, ORDWR)) < 0) return -1; close(cfd); if((r = rfork(RFMEM|RFPROC|RFNOWAIT)) <= 0) return r; if((cfd = listen(dir, ldir)) < 0) return -1; close(fd); /* close inside udp server */ if((fd = accept(cfd, ldir)) < 0) return -1; switch(rfork(RFMEM|RFPROC|RFNOWAIT)){ case -1: return -1; case 0: while((r = read(fd, msg.data, sizeof(msg.data))) > 0){ if(r < 4) continue; p = msg.data; if(p[0] | p[1] | p[2]) continue; p += 3; switch(*p++){ default: continue; case 0x01: r -= 2+1+1+4+2; if(r < 0) continue; v4tov6(msg.raddr, p); p += 4; break; case 0x04: r -= 2+1+1+16+2; if(r < 0) continue; memmove(msg.raddr, p, 16); p += 16; break; } memmove(msg.rport, p, 2); p += 2; memmove(msg.data, p, r); write(rfd, &msg, sizeof(Udphdr)+r); } break; default: while((r = read(rfd, &msg, sizeof(msg))) > 0){ r -= sizeof(Udphdr); if(r < 0) continue; p = msg.data; if(isv4(msg.raddr)) n = 2+1+1+4+2; else n = 2+1+1+16+2; if(r+n > sizeof(msg.data)) r = sizeof(msg.data)-n; memmove(p+n, p, r); *p++ = 0; *p++ = 0; *p++ = 0; if(isv4(msg.raddr)){ *p++ = 0x01; v6tov4(p, msg.raddr); p += 4; } else { *p++ = 0x04; memmove(p, msg.raddr, 16); p += 16; } memmove(p, msg.rport, 2); r += n; write(fd, msg.data, r); } } return -1;} intsockerr(int err){ /* general error */ if(socksver == 4) return err ? 0x5b : 0x5a; else return err != 0;} voidmain(int argc, char *argv[]){ uchar buf[8*1024], *p; char addr[128], dir[40], ldir[40], *s; int cmd, fd, cfd, n; NetConnInfo *nc; fmtinstall('I', eipfmt); setnetmtpt(inside, sizeof(inside), 0); setnetmtpt(outside, sizeof(outside), 0); ARGBEGIN { case 'x': setnetmtpt(inside, sizeof(inside), ARGF()); break; case 'o': setnetmtpt(outside, sizeof(outside), ARGF()); break; } ARGEND; /* ver+cmd or ver+nmethod */ if(readn(0, buf, 2) != 2) return; socksver = buf[0]; if(socksver < 4) return; if(socksver > 5) socksver = 5; if(socksver == 4){ /* port+ip4 */ if(readn(0, buf+2, 2+4) != 2+4) return; /* +user\0 */ for(p = buf+2+2+4;; p++){ if(p >= buf+sizeof(buf)) return; if(read(0, p, 1) != 1) return; if(*p == 0) break; } /* socks 4a dom hack */ if((buf[4] | buf[5] | buf[6]) == 0 && buf[7]){ /* +dom\0 */ for(++p;; p++){ if(p >= buf+sizeof(buf)) return; if(read(0, p, 1) != 1) return; if(*p == 0) break; } } } else { /* nmethod */ if((n = buf[1]) > 0) if(readn(0, buf+2, n) != n) return; /* ver+method */ buf[0] = socksver; buf[1] = 0x00; /* no authentication required */ if(write(1, buf, 2) != 2) return; /* ver+cmd+res+atyp */ if(readn(0, buf, 4) != 4) return; switch(buf[3]){ default: return; case 0x01: /* +ipv4 */ if(readn(0, buf+4, 4+2) != 4+2) return; break; case 0x03: /* +len+dom[len] */ if(readn(0, buf+4, 1) != 1) return; if((n = buf[4]) == 0) return; if(readn(0, buf+5, n+2) != n+2) return; break; case 0x04: /* +ipv6 */ if(readn(0, buf+4, 16+2) != 16+2) return; break; } } dir[0] = 0; fd = cfd = -1; cmd = buf[1]; switch(cmd){ case 0x01: /* CONNECT */ snprint(addr, sizeof(addr), "%s/tcp", outside); if((s = addr2str(addr, buf)) == nil) break; alarm(30000); fd = dial(s, 0, dir, &cfd); alarm(0); break; case 0x02: /* BIND */ if(myipaddr(buf, outside) < 0) break; snprint(addr, sizeof(addr), "%s/tcp!%I!0", outside, buf); fd = announce(addr, dir); break; case 0x03: /* UDP */ if(myipaddr(buf, inside) < 0) break; snprint(addr, sizeof(addr), "%s/udp!%I!0", inside, buf); fd = announce(addr, dir); break; } Reply: /* reply */ buf[1] = sockerr(fd < 0); /* status */ if(socksver == 4){ buf[0] = 0x00; /* vc */ if(fd < 0){ memset(buf+2, 0, 2+4); write(1, buf, 2+2+4); return; } } else { buf[0] = socksver; /* ver */ buf[2] = 0x00; /* res */ if(fd < 0){ buf[3] = 0x01; /* atyp */ memset(buf+4, 0, 4+2); write(1, buf, 4+4+2); return; } } if((nc = getnetconninfo(dir, cfd)) == nil) return; if((n = str2addr((cmd & 0x100) ? nc->raddr : nc->laddr, buf)) <= 0) return; if(write(1, buf, n) != n) return; switch(cmd){ default: return; case 0x01: /* CONNECT */ break; case 0x02: /* BIND */ cfd = listen(dir, ldir); close(fd); fd = -1; if(cfd >= 0){ strcpy(dir, ldir); fd = accept(cfd, dir); } cmd |= 0x100; goto Reply; case 0x102: break; case 0x03: /* UDP */ if(udprelay(fd, dir) == 0) while(read(0, buf, sizeof(buf)) > 0) ; goto Hangup; } /* relay data */ switch(rfork(RFMEM|RFPROC|RFFDG|RFNOWAIT)){ case -1: return; case 0: dup(fd, 0); break; default: dup(fd, 1); } while((n = read(0, buf, sizeof(buf))) > 0) if(write(1, buf, n) != n) break;Hangup: if(cfd >= 0) hangup(cfd); postnote(PNGROUP, getpid(), "kill");}
#include <u.h>
#include <libc.h>
#include <ip.h>
int socksver;
char inside[128];
char outside[128];
int
str2addr(char *s, uchar *a)
{
uchar *a0, ip[16];
char *p;
if((s = strchr(s, '!')) == nil)
return 0;
if((p = strchr(++s, '!')) == nil)
return 0;
if(strchr(++p, '!') != nil)
return 0;
if(parseip(ip, s) == -1)
return 0;
a0 = a;
if(socksver == 4){
a += 2;
hnputs(a, atoi(p));
a += 2;
v6tov4(a, ip);
a += 4;
} else {
a += 3;
if(isv4(ip)){
*a++ = 0x01;
v6tov4(a, ip);
a += 4;
} else {
*a++ = 0x04;
memmove(a, ip, 16);
a += 16;
}
hnputs(a, atoi(p));
a += 2;
}
return a - a0;
}
char*
addr2str(char *proto, uchar *a){
static char s[128];
uchar ip[16];
int n, port;
if(socksver == 4){
a += 2;
port = nhgets(a);
a += 2;
if((a[0] | a[1] | a[2]) == 0 && a[3]){
a += 4;
a += strlen((char*)a)+1;
snprint(s, sizeof(s), "%s!%s!%d", proto, (char*)a, port);
return s;
}
v4tov6(ip, a);
} else {
a += 3;
switch(*a++){
default:
return nil;
case 0x01:
v4tov6(ip, a);
port = nhgets(a+4);
break;
case 0x04:
memmove(ip, a, 16);
port = nhgets(a+16);
break;
case 0x03:
n = *a++;
port = nhgets(a+n);
n = utfnlen((char*)a, n);
snprint(s, sizeof(s), "%s!%.*s!%d", proto, n, (char*)a, port);
return s;
}
}
snprint(s, sizeof(s), "%s!%I!%d", proto, ip, port);
return s;
}
int
udprelay(int fd, char *dir)
{
struct {
Udphdr;
uchar data[8*1024];
} msg;
char addr[128], ldir[40];
int r, n, rfd, cfd;
uchar *p;
snprint(addr, sizeof(addr), "%s/udp!*!0", outside);
if((cfd = announce(addr, ldir)) < 0)
return -1;
if(write(cfd, "headers", 7) != 7)
return -1;
strcat(ldir, "/data");
if((rfd = open(ldir, ORDWR)) < 0)
return -1;
close(cfd);
if((r = rfork(RFMEM|RFPROC|RFNOWAIT)) <= 0)
return r;
if((cfd = listen(dir, ldir)) < 0)
return -1;
close(fd); /* close inside udp server */
if((fd = accept(cfd, ldir)) < 0)
return -1;
switch(rfork(RFMEM|RFPROC|RFNOWAIT)){
case -1:
return -1;
case 0:
while((r = read(fd, msg.data, sizeof(msg.data))) > 0){
if(r < 4)
continue;
p = msg.data;
if(p[0] | p[1] | p[2])
continue;
p += 3;
switch(*p++){
default:
continue;
case 0x01:
r -= 2+1+1+4+2;
if(r < 0)
continue;
v4tov6(msg.raddr, p);
p += 4;
break;
case 0x04:
r -= 2+1+1+16+2;
if(r < 0)
continue;
memmove(msg.raddr, p, 16);
p += 16;
break;
}
memmove(msg.rport, p, 2);
p += 2;
memmove(msg.data, p, r);
write(rfd, &msg, sizeof(Udphdr)+r);
}
break;
default:
while((r = read(rfd, &msg, sizeof(msg))) > 0){
r -= sizeof(Udphdr);
if(r < 0)
continue;
p = msg.data;
if(isv4(msg.raddr))
n = 2+1+1+4+2;
else
n = 2+1+1+16+2;
if(r+n > sizeof(msg.data))
r = sizeof(msg.data)-n;
memmove(p+n, p, r);
*p++ = 0;
*p++ = 0;
*p++ = 0;
if(isv4(msg.raddr)){
*p++ = 0x01;
v6tov4(p, msg.raddr);
p += 4;
} else {
*p++ = 0x04;
memmove(p, msg.raddr, 16);
p += 16;
}
memmove(p, msg.rport, 2);
r += n;
write(fd, msg.data, r);
}
}
return -1;
}
int
sockerr(int err)
{
/* general error */
if(socksver == 4)
return err ? 0x5b : 0x5a;
else
return err != 0;
}
void
main(int argc, char *argv[])
{
uchar buf[8*1024], *p;
char addr[128], dir[40], ldir[40], *s;
int cmd, fd, cfd, n;
NetConnInfo *nc;
fmtinstall('I', eipfmt);
setnetmtpt(inside, sizeof(inside), 0);
setnetmtpt(outside, sizeof(outside), 0);
ARGBEGIN {
case 'x':
setnetmtpt(inside, sizeof(inside), ARGF());
break;
case 'o':
setnetmtpt(outside, sizeof(outside), ARGF());
break;
} ARGEND;
/* ver+cmd or ver+nmethod */
if(readn(0, buf, 2) != 2)
return;
socksver = buf[0];
if(socksver < 4)
return;
if(socksver > 5)
socksver = 5;
if(socksver == 4){
/* port+ip4 */
if(readn(0, buf+2, 2+4) != 2+4)
return;
/* +user\0 */
for(p = buf+2+2+4;; p++){
if(p >= buf+sizeof(buf))
return;
if(read(0, p, 1) != 1)
return;
if(*p == 0)
break;
}
/* socks 4a dom hack */
if((buf[4] | buf[5] | buf[6]) == 0 && buf[7]){
/* +dom\0 */
for(++p;; p++){
if(p >= buf+sizeof(buf))
return;
if(read(0, p, 1) != 1)
return;
if(*p == 0)
break;
}
}
} else {
/* nmethod */
if((n = buf[1]) > 0)
if(readn(0, buf+2, n) != n)
return;
/* ver+method */
buf[0] = socksver;
buf[1] = 0x00; /* no authentication required */
if(write(1, buf, 2) != 2)
return;
/* ver+cmd+res+atyp */
if(readn(0, buf, 4) != 4)
return;
switch(buf[3]){
default:
return;
case 0x01: /* +ipv4 */
if(readn(0, buf+4, 4+2) != 4+2)
return;
break;
case 0x03: /* +len+dom[len] */
if(readn(0, buf+4, 1) != 1)
return;
if((n = buf[4]) == 0)
return;
if(readn(0, buf+5, n+2) != n+2)
return;
break;
case 0x04: /* +ipv6 */
if(readn(0, buf+4, 16+2) != 16+2)
return;
break;
}
}
dir[0] = 0;
fd = cfd = -1;
cmd = buf[1];
switch(cmd){
case 0x01: /* CONNECT */
snprint(addr, sizeof(addr), "%s/tcp", outside);
if((s = addr2str(addr, buf)) == nil)
break;
alarm(30000);
fd = dial(s, 0, dir, &cfd);
alarm(0);
break;
case 0x02: /* BIND */
if(myipaddr(buf, outside) < 0)
break;
snprint(addr, sizeof(addr), "%s/tcp!%I!0", outside, buf);
fd = announce(addr, dir);
break;
case 0x03: /* UDP */
if(myipaddr(buf, inside) < 0)
break;
snprint(addr, sizeof(addr), "%s/udp!%I!0", inside, buf);
fd = announce(addr, dir);
break;
}
Reply:
/* reply */
buf[1] = sockerr(fd < 0); /* status */
if(socksver == 4){
buf[0] = 0x00; /* vc */
if(fd < 0){
memset(buf+2, 0, 2+4);
write(1, buf, 2+2+4);
return;
}
} else {
buf[0] = socksver; /* ver */
buf[2] = 0x00; /* res */
if(fd < 0){
buf[3] = 0x01; /* atyp */
memset(buf+4, 0, 4+2);
write(1, buf, 4+4+2);
return;
}
}
if((nc = getnetconninfo(dir, cfd)) == nil)
return;
if((n = str2addr((cmd & 0x100) ? nc->raddr : nc->laddr, buf)) <= 0)
return;
if(write(1, buf, n) != n)
return;
switch(cmd){
default:
return;
case 0x01: /* CONNECT */
break;
case 0x02: /* BIND */
cfd = listen(dir, ldir);
close(fd);
fd = -1;
if(cfd >= 0){
strcpy(dir, ldir);
fd = accept(cfd, dir);
}
cmd |= 0x100;
goto Reply;
case 0x102:
break;
case 0x03: /* UDP */
if(udprelay(fd, dir) == 0)
while(read(0, buf, sizeof(buf)) > 0)
;
goto Hangup;
}
/* relay data */
switch(rfork(RFMEM|RFPROC|RFFDG|RFNOWAIT)){
case -1:
return;
case 0:
dup(fd, 0);
break;
default:
dup(fd, 1);
}
while((n = read(0, buf, sizeof(buf))) > 0)
if(write(1, buf, n) != n)
break;
Hangup:
if(cfd >= 0)
hangup(cfd);
postnote(PNGROUP, getpid(), "kill");
}