socksd: socks4 and socks4a support

This commit is contained in:
cinap_lenrek 2012-03-02 04:32:46 +01:00
parent a57252cadb
commit 2fec3c0bc8

View file

@ -2,6 +2,8 @@
#include <libc.h> #include <libc.h>
#include <ip.h> #include <ip.h>
int socksver;
int int
str2addr(char *s, uchar *a) str2addr(char *s, uchar *a)
{ {
@ -16,18 +18,28 @@ str2addr(char *s, uchar *a)
return 0; return 0;
if(parseip(ip, s) == -1) if(parseip(ip, s) == -1)
return 0; return 0;
a0 = a; a0 = a;
if(isv4(ip)){ if(socksver == 4){
*a++ = 0x01; a += 2;
hnputs(a, atoi(p));
a += 2;
v6tov4(a, ip); v6tov4(a, ip);
a += 4; a += 4;
} else { } else {
*a++ = 0x04; a += 3;
memmove(a, ip, 16); if(isv4(ip)){
a += 16; *a++ = 0x01;
v6tov4(a, ip);
a += 4;
} else {
*a++ = 0x04;
memmove(a, ip, 16);
a += 16;
}
hnputs(a, atoi(p));
a += 2;
} }
hnputs(a, atoi(p));
a += 2;
return a - a0; return a - a0;
} }
@ -37,96 +49,148 @@ addr2str(char *proto, uchar *a){
uchar ip[16]; uchar ip[16];
int n, port; int n, port;
switch(*a++){ if(socksver == 4){
default: a += 2;
abort(); port = nhgets(a);
return 0; a += 2;
case 0x01: 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); v4tov6(ip, a);
port = nhgets(a+4); } else {
break; a += 3;
case 0x04: switch(*a++){
memmove(ip, a, 16); default:
port = nhgets(a+16); return nil;
break; case 0x01:
case 0x03: v4tov6(ip, a);
n = *a++; port = nhgets(a+4);
port = nhgets(a+n); break;
snprint(s, sizeof(s), "%s!%.*s!%d", proto, n, (char*)a, port); case 0x04:
return s; memmove(ip, a, 16);
port = nhgets(a+16);
break;
case 0x03:
n = *a++;
port = nhgets(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); snprint(s, sizeof(s), "%s!%I!%d", proto, ip, port);
return s; return s;
} }
int int
sockerr(void) sockerr(int err)
{ {
return 1; /* general error */ /* general error */
if(socksver == 4)
return err ? 0x5b : 0x5a;
else
return err != 0;
} }
void void
main(int argc, char *argv[]) main(int argc, char *argv[])
{ {
uchar buf[8*1024]; uchar buf[8*1024], *p;
char dir[40], *s;
NetConnInfo *nc; NetConnInfo *nc;
int fd, cfd, v, n; int fd, cfd, n;
fmtinstall('I', eipfmt); fmtinstall('I', eipfmt);
ARGBEGIN { ARGBEGIN {
} ARGEND; } ARGEND;
nc = nil; /* ver+cmd or ver+nmethod */
fd = cfd = -1;
/* negotiation */
if(readn(0, buf, 2) != 2) if(readn(0, buf, 2) != 2)
return; return;
v = buf[0]; socksver = buf[0];
n = buf[1]; if(socksver < 4)
if(n > 0)
if(readn(0, buf, n) != n)
return;
if(v > 5)
v = 5;
buf[0] = v;
buf[1] = 0x00; /* no authentication required */
if(write(1, buf, 2) != 2)
return; return;
Loop: if(socksver > 5)
/* request */ socksver = 5;
if(readn(0, buf, 4) != 4)
return; if(socksver == 4){
switch(buf[3]){ /* port+ip4 */
default: if(readn(0, buf+2, 2+4) != 2+4)
return;
case 0x01: /* ipv4 */
if(readn(0, buf+4, 4+2) != 4+2)
return; return;
break; /* +user\0 */
case 0x03: /* domain name */ for(p = buf+2+2+4;; p++){
if(readn(0, buf+4, 1) != 1) 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; return;
if((n = buf[4]) == 0)
/* ver+cmd+res+atyp */
if(readn(0, buf, 4) != 4)
return; return;
if(readn(0, buf+5, n+2) != n+2) switch(buf[3]){
default:
return; return;
break; case 0x01: /* +ipv4 */
case 0x04: /* ipv6 */ if(readn(0, buf+4, 4+2) != 4+2)
if(readn(0, buf+4, 16+2) != 16+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;
}
}
nc = nil;
dir[0] = 0;
fd = cfd = -1;
switch(buf[1]){
case 0x01: /* CONNECT */
if((s = addr2str("tcp", buf)) == nil)
return; return;
fd = dial(s, 0, dir, &cfd);
break; break;
} }
/* cmd */
switch(buf[1]){
case 0x01: /* CONNECT */
fd = dial(addr2str("tcp", buf+3), 0, 0, &cfd);
break;
}
if(fd >= 0){ if(fd >= 0){
if((nc = getnetconninfo(nil, fd)) == nil){ if((nc = getnetconninfo(dir, -1)) == nil){
if(cfd >= 0) if(cfd >= 0)
close(cfd); close(cfd);
close(fd); close(fd);
@ -134,23 +198,31 @@ Loop:
} }
} }
/* response */ /* reply */
buf[0] = v; buf[1] = sockerr(fd < 0); /* status */
buf[1] = (fd < 0) ? sockerr() : 0; if(socksver == 4){
buf[2] = 0x00; /* res */ buf[0] = 0x00; /* vc */
if(fd < 0){ if(fd < 0){
buf[3] = 0x01; /* atype */ memset(buf+2, 0, 2+4);
memset(buf+4, 0, 4+2); write(1, buf, 2+2+4);
if(write(1, buf, 4+4+2) != 4+4+2)
return; return;
goto Loop; }
} 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((n = str2addr(nc->laddr, buf+3)) <= 2) if((n = str2addr(nc->laddr, buf)) <= 0)
return; return;
if(write(1, buf, 3+n) != 3+n) if(write(1, buf, n) != n)
return; return;
/* rely data */ /* reley data */
switch(rfork(RFMEM|RFPROC|RFFDG|RFNOWAIT)){ switch(rfork(RFMEM|RFPROC|RFFDG|RFNOWAIT)){
case -1: case -1:
return; return;