385 lines · 6.6 KB · C
RawDownload
#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");}