874 lines · 16.2 KB · C
RawDownload
/* * Point-to-point Tunneling Protocol (PPTP) * See RFC 2637, pptpd.c */ #include <u.h>#include <libc.h>#include <bio.h>#include <ip.h>#include <thread.h> int	ack;int	alarmed;int	ctlechotime;int	ctlfd;int	ctlrcvtime;int	debug;int	grefd;uchar localip[IPaddrlen];int	localwin;char	*keyspec;int	now;char	*pppnetmntpt;int	pid;Channel *pidchan;int	pppfd;int	primary;int	rack;Channel	*rdchan;int	rdexpect;int	remid;uchar remoteip[IPaddrlen];int	remwin;int	rseq;int	seq;char	tcpdir[40];Channel *tickchan;int	topppfd; int	aread(int, int, void*, int);int	catchalarm(void*, char*);void	dumpctlpkt(uchar*);void	getaddrs(void);void	*emalloc(long);void	ewrite(int, void*, int);void	myfatal(char*, ...);#pragma varargck argpos myfatal 1int	pptp(char*);void	pushppp(int);void	recordack(int);int	schedack(int, uchar*, int);void	waitacks(void); voidusage(void){	fprint(2, "usage: ip/pptp [-Pd] [-k keyspec] [-x pppnetmntpt] [-w window] server\n");	exits("usage");} voidthreadmain(int argc, char **argv){	int fd; 	ARGBEGIN{	case 'P':		primary = 1;		break;	case 'd':		debug++;		break;	case 'k':		keyspec = EARGF(usage());		break;	case 'w':		localwin = atoi(EARGF(usage()));		break;	case 'x':		pppnetmntpt = EARGF(usage());		break;	default:		usage();	}ARGEND 	if(argc != 1)		usage(); 	fmtinstall('E', eipfmt);	fmtinstall('I', eipfmt); 	rfork(RFNOTEG);	atnotify(catchalarm, 1);	fd = pptp(argv[0]);	pushppp(fd);	exits(nil);} intcatchalarm(void *a, char *msg){	USED(a); 	if(strstr(msg, "alarm")){		alarmed = 1;		return 1;	}	if(debug)		fprint(2, "note rcved: %s\n", msg);	return 0;} enum {	Stack	= 8192, 	PptpProto	= 0x0100, 	Magic	= 0x1a2b3c4d,	Window	= 16,		/* default window size */	Timeout	= 60,		/* timeout in seconds for control channel */	Pktsize = 2000,		/* maximum packet size */	Tick	= 500,		/* tick length in milliseconds */	Sendtimeout = 4,	/* in ticks */ 	Servertimeout = 5*60*1000/Tick,	Echointerval = 60*1000/Tick,}; enum {	Syncframe	= 0x1,	Asyncframe	= 0x2,	Analog		= 0x1,	Digital		= 0x2,	Version		= 0x100,}; enum {	Tstart		= 1,	Rstart		= 2,	Tstop		= 3,	Rstop		= 4,	Techo		= 5,	Recho		= 6,	Tcallout	= 7,	Rcallout	= 8,	Tcallreq	= 9,	Rcallreq	= 10,	Acallcon	= 11,	Tcallclear	= 12,	Acalldis	= 13,	Awaninfo	= 14,	Alinkinfo	= 15,}; voidrecho(uchar *in){	uchar out[20]; 	if(nhgets(in) < 16)		return; 	memset(out, 0, sizeof out);	hnputs(out, sizeof out);	hnputs(out+2, 1);	hnputl(out+4, Magic);	hnputs(out+8, Recho);	memmove(out+12, in+12, 4);	out[16] = 1; 	ewrite(ctlfd, out, sizeof out);} voidsendecho(void){	uchar out[16]; 	ctlechotime = now;		memset(out, 0, sizeof out);	hnputs(out, sizeof out);	hnputs(out+2, 1);	hnputl(out+4, Magic);	hnputs(out+8, Techo); 	ewrite(ctlfd, out, sizeof out);} voidpptpctlproc(void*){	uchar pkt[1600], *p;	int len; 	for(;;){		if(readn(ctlfd, pkt, 2) != 2)			myfatal("pptpread: %r");		len = nhgets(pkt);		if(len < 12 || len+2 >= sizeof pkt)			myfatal("pptpread: bad length %d", len);		if(readn(ctlfd, pkt+2, len-2) != len-2)			myfatal("pptpread: %r");		if(nhgetl(pkt+4) != Magic)			myfatal("pptpread bad magic");		if(nhgets(pkt+2) != 1)			myfatal("pptpread bad message type");		if(debug)			dumpctlpkt(pkt);		ctlrcvtime = now; 		switch(nhgets(pkt+8)){		case Tstart:		case Tstop:		case Tcallout:		case Tcallreq:		case Tcallclear:		case Acallcon:		case Acalldis:		case Awaninfo:			myfatal("unexpected msg type %d", nhgets(pkt+8));		case Techo:			recho(pkt);			break;		case Recho:			break;		case Rstart:		case Rstop:		case Rcallout:		case Rcallreq:			if(rdexpect != nhgets(pkt+8))				continue;			p = emalloc(len);			memmove(p, pkt, len);			sendp(rdchan, p);			break;		case Alinkinfo:			myfatal("cannot change ppp params on the fly");		}	}} enum {	Seqnum = 0x1000,	Acknum = 0x0080, 	GrePPP = 0x880B,}; voidgrereadproc(void*){	int datoff, flags, len, n, pass;	uchar pkt[1600];	uchar src[IPaddrlen], dst[IPaddrlen]; 	rfork(RFFDG);	close(pppfd);	sendul(pidchan, getpid()); 	while((n = read(grefd, pkt, sizeof pkt)) > 0){		if(n == sizeof pkt)			myfatal("gre pkt buffer too small");		if(n < 16){			if(debug)				fprint(2, "small pkt len %d ignored\n", n);			continue;		}		v4tov6(src, pkt);		v4tov6(dst, pkt+4);		if(ipcmp(src, remoteip) != 0 || ipcmp(dst, localip) != 0)			myfatal("%I: gre read bad address src=%I dst=%I",				remoteip, src, dst);		if(nhgets(pkt+10) != GrePPP)			myfatal("%I: gre read bad protocol 0x%x",				remoteip, nhgets(pkt+10)); 		flags = nhgets(pkt+8);		if((flags&0xEF7F) != 0x2001){			if(debug)				fprint(2, "bad flags in gre hdr 0x%x\n", flags);			continue;		}		datoff = 8+8;		pass = 0;		len = nhgets(pkt+8+4);		if(len > n-datoff){			fprint(2, "bad payload length %d > %d\n",				len, n-datoff);			continue;		}		if(flags&Seqnum)			datoff += 4;		if(flags&Acknum){			recordack(nhgetl(pkt+datoff));			datoff += 4;		}		if(flags&Seqnum)			pass = schedack(nhgetl(pkt+8+8), pkt+datoff, len);		if(debug)			fprint(2, "got gre callid %d len %d flag 0x%x pass %d seq %d rseq %d\n", nhgets(pkt+8+6),				len, flags, pass, nhgetl(pkt+8+8), rseq);	}	threadexits(nil);} voidpppreadproc(void*){	int n, myrseq;	uchar pkt[1600];	enum {		Hdr = 8+16,	}; 	rfork(RFFDG);	close(pppfd);	sendul(pidchan, getpid()); 	while((n = read(topppfd, pkt+Hdr, sizeof pkt-Hdr)) > 0){		if(n == sizeof pkt-Hdr)			myfatal("ppp pkt buffer too small");		v6tov4(pkt+0, localip);		v6tov4(pkt+4, remoteip);		hnputs(pkt+8, 0x2001 | Seqnum | Acknum);		hnputs(pkt+10, GrePPP);		hnputs(pkt+12, n);		hnputs(pkt+14, remid);		hnputl(pkt+16, ++seq);		myrseq = rseq;		hnputl(pkt+20, myrseq);		rack = myrseq;		if(debug)			fprint(2, "wrote gre callid %d len %d flag 0x%x seq %d rseq %d\n", nhgets(pkt+8+6),				n, nhgets(pkt+8), nhgetl(pkt+16), nhgetl(pkt+20));		if(write(grefd, pkt, n+Hdr) != n+Hdr)			myfatal("gre write: %r");		waitacks();	}	threadexits(nil);} voidsendack(void){	int myrseq;	uchar pkt[20]; 	v6tov4(pkt+0, localip);	v6tov4(pkt+4, remoteip);	hnputs(pkt+8, 0x2001 | Acknum);	hnputs(pkt+10, GrePPP);	hnputs(pkt+12, 0);	hnputs(pkt+14, remid);	myrseq = rseq;	rack = myrseq;	hnputs(pkt+16, myrseq); 	if(write(grefd, pkt, sizeof pkt) != sizeof pkt)		myfatal("gre write: %r");} intschedack(int n, uchar *dat, int len){	static uchar sdat[1600];	static int srseq, slen; 	if(n-rseq <= 0){		fprint(2, "skipping pkt %d len %d, have %d\n", n, len, rseq);		return 0;	} 	/* missed one pkt, maybe a swap happened, save pkt */	if(n==rseq+2){		memmove(sdat, dat, len);		slen = len;		srseq = n;		return 0;	} 	if(n-rseq > 1){		if(slen && srseq == n-1){				fprint(2, "reswapped pkts %d and %d\n", srseq, n);			write(topppfd, sdat, slen);			slen = 0;		}else			fprint(2, "missed pkts %d-%d, got %d len %d\n", rseq+1, n-1, n, len);	}	write(topppfd, dat, len);	rseq = n; 	/* send ack if we haven't recently */	if((int)(rseq-rack) > (localwin>>1))		sendack(); 	return 1;} voidgretimeoutproc(void*){	for(;;){		sleep(Tick);		now++;		nbsendul(tickchan, now);		if(now - ctlrcvtime > Servertimeout)			myfatal("server timeout");		if(now - ctlechotime > Echointerval)			sendecho();	}} voidrecordack(int n){	ack = n;} voidwaitacks(void){/*	int start; 	start = now;	while(seq-ack > remwin && now-start < Sendtimeout){		print("seq %d ack %d remwin %d now %d start %d\n",			seq, ack, remwin, now, start);		recvul(tickchan);	}*/} voidtstart(void){	char *name;	uchar pkt[200], *rpkt; 	memset(pkt, 0, sizeof pkt); 	hnputs(pkt+0, 156);	hnputs(pkt+2, 1);	hnputl(pkt+4, Magic);	hnputs(pkt+8, Tstart);	hnputs(pkt+12, PptpProto);	hnputl(pkt+16, 1);	hnputl(pkt+20, 1);	hnputs(pkt+24, 1);	name = sysname();	if(name == nil)		name = "gnot";	strcpy((char*)pkt+28, name);	strcpy((char*)pkt+92, "plan 9"); 	if(debug)		dumpctlpkt(pkt); 	rdexpect = Rstart;	ewrite(ctlfd, pkt, 156); 	rpkt = recvp(rdchan);	if(rpkt == nil)		myfatal("recvp: %r");	if(nhgets(rpkt) != 156)		myfatal("Rstart wrong length %d != 156", nhgets(rpkt));	if(rpkt[14] != 1)		myfatal("Rstart error %d", rpkt[15]);	free(rpkt);} voidtcallout(void){	uchar pkt[200], *rpkt; 	pid = getpid(); 	memset(pkt, 0, sizeof pkt);	hnputs(pkt+0, 168);	hnputs(pkt+2, 1);	hnputl(pkt+4, Magic);	hnputs(pkt+8, Tcallout); 	hnputl(pkt+16, 56000);	hnputl(pkt+20, 768000);	hnputl(pkt+24, 3);	hnputl(pkt+28, 3);	if(localwin == 0)		localwin = Window;	hnputs(pkt+32, localwin); 	if(debug)		dumpctlpkt(pkt); 	rdexpect = Rcallout;	ewrite(ctlfd, pkt, 168); 	rpkt = recvp(rdchan);	if(rpkt == nil)

Lines 1–500 of 874.