vx32

Local 9vx git repository for patches.
git clone git://r-36.net/vx32
Log | Files | Refs

devip.c (16129B)


      1 /*
      2  * /net interface to host IPv4 stack.
      3  */
      4 
      5 #include "u.h"
      6 #include "lib.h"
      7 #include "mem.h"
      8 #include "dat.h"
      9 #include "fns.h"
     10 #include "error.h"
     11 #include "ip.h"
     12 #include "devip.h"
     13 
     14 void	csclose(Chan*);
     15 long	csread(Chan*, void*, long, vlong);
     16 long	cswrite(Chan*, void*, long, vlong);
     17 
     18 static int csremoved = 1;	/* was nice while it lasted... */
     19 
     20 void osipinit(void);
     21 
     22 enum
     23 {
     24 	Qtopdir		= 1,	/* top level directory */
     25 	Qcs,
     26 	Qdns,
     27 	Qprotodir,		/* directory for a protocol */
     28 	Qclonus,
     29 	Qconvdir,		/* directory for a conversation */
     30 	Qdata,
     31 	Qctl,
     32 	Qstatus,
     33 	Qremote,
     34 	Qlocal,
     35 	Qlisten,
     36 
     37 	MAXPROTO	= 4
     38 };
     39 #define TYPE(x) 	((int)((x).path & 0xf))
     40 #define CONV(x) 	((int)(((x).path >> 4)&0xfff))
     41 #define PROTO(x) 	((int)(((x).path >> 16)&0xff))
     42 #define QID(p, c, y) 	(((p)<<16) | ((c)<<4) | (y))
     43 
     44 typedef struct Proto	Proto;
     45 typedef struct Conv	Conv;
     46 struct Conv
     47 {
     48 	int	x;
     49 	Ref	r;
     50 	int	sfd;
     51 	int	eof;
     52 	int	perm;
     53 	char	owner[KNAMELEN];
     54 	char*	state;
     55 	ulong	laddr;
     56 	ushort	lport;
     57 	ulong	raddr;
     58 	ushort	rport;
     59 	int	restricted;
     60 	char	cerr[KNAMELEN];
     61 	Proto*	p;
     62 };
     63 
     64 struct Proto
     65 {
     66 	Lock	l;
     67 	int	x;
     68 	int	stype;
     69 	char	name[KNAMELEN];
     70 	int	nc;
     71 	int	maxconv;
     72 	Conv**	conv;
     73 	Qid	qid;
     74 };
     75 
     76 static	int	np;
     77 static	Proto	proto[MAXPROTO];
     78 
     79 static	Conv*	protoclone(Proto*, char*, int);
     80 static	void	setladdr(Conv*);
     81 
     82 int
     83 ipgen(Chan *c, char *nname, Dirtab *d, int nd, int s, Dir *dp)
     84 {
     85 	Qid q;
     86 	Conv *cv;
     87 	char *p;
     88 
     89 	USED(nname);
     90 	q.vers = 0;
     91 	q.type = 0;
     92 	switch(TYPE(c->qid)) {
     93 	case Qtopdir:
     94 	case Qcs:
     95 	case Qdns:
     96 		if(s >= 2+np)
     97 			return -1;
     98 		if(s == 0){
     99 			if(csremoved)
    100 				return 0;
    101 			q.path = QID(s, 0, Qcs);
    102 			devdir(c, q, "cs", 0, "network", 0666, dp);
    103 		}else if(s == 1){
    104 			q.path = QID(s, 0, Qdns);
    105 			devdir(c, q, "dns", 0, "network", 0666, dp);
    106 		}else{
    107 			s-=2;
    108 			q.path = QID(s, 0, Qprotodir);
    109 			q.type = QTDIR;
    110 			devdir(c, q, proto[s].name, 0, "network", DMDIR|0555, dp);
    111 		}
    112 		return 1;
    113 	case Qprotodir:
    114 	case Qclonus:
    115 		if(s < proto[PROTO(c->qid)].nc) {
    116 			cv = proto[PROTO(c->qid)].conv[s];
    117 			sprint(up->genbuf, "%d", s);
    118 			q.path = QID(PROTO(c->qid), s, Qconvdir);
    119 			q.type = QTDIR;
    120 			devdir(c, q, up->genbuf, 0, cv->owner, DMDIR|0555, dp);
    121 			return 1;
    122 		}
    123 		s -= proto[PROTO(c->qid)].nc;
    124 		switch(s) {
    125 		default:
    126 			return -1;
    127 		case 0:
    128 			p = "clone";
    129 			q.path = QID(PROTO(c->qid), 0, Qclonus);
    130 			break;
    131 		}
    132 		devdir(c, q, p, 0, "network", 0555, dp);
    133 		return 1;
    134 	case Qconvdir:
    135 	case Qdata:
    136 	case Qctl:
    137 	case Qstatus:
    138 	case Qremote:
    139 	case Qlocal:
    140 	case Qlisten:
    141 		cv = proto[PROTO(c->qid)].conv[CONV(c->qid)];
    142 		switch(s) {
    143 		default:
    144 			return -1;
    145 		case 0:
    146 			q.path = QID(PROTO(c->qid), CONV(c->qid), Qdata);
    147 			devdir(c, q, "data", 0, cv->owner, cv->perm, dp);
    148 			return 1;
    149 		case 1:
    150 			q.path = QID(PROTO(c->qid), CONV(c->qid), Qctl);
    151 			devdir(c, q, "ctl", 0, cv->owner, cv->perm, dp);
    152 			return 1;
    153 		case 2:
    154 			p = "status";
    155 			q.path = QID(PROTO(c->qid), CONV(c->qid), Qstatus);
    156 			break;
    157 		case 3:
    158 			p = "remote";
    159 			q.path = QID(PROTO(c->qid), CONV(c->qid), Qremote);
    160 			break;
    161 		case 4:
    162 			p = "local";
    163 			q.path = QID(PROTO(c->qid), CONV(c->qid), Qlocal);
    164 			break;
    165 		case 5:
    166 			p = "listen";
    167 			q.path = QID(PROTO(c->qid), CONV(c->qid), Qlisten);
    168 			break;
    169 		}
    170 		devdir(c, q, p, 0, cv->owner, 0444, dp);
    171 		return 1;
    172 	}
    173 	return -1;
    174 }
    175 
    176 static void
    177 newproto(char *name, int type, int maxconv)
    178 {
    179 	int l;
    180 	Proto *p;
    181 
    182 	if(np >= MAXPROTO) {
    183 		print("no %s: increase MAXPROTO", name);
    184 		return;
    185 	}
    186 
    187 	p = &proto[np];
    188 	strcpy(p->name, name);
    189 	p->stype = type;
    190 	p->qid.path = QID(np, 0, Qprotodir);
    191 	p->qid.type = QTDIR;
    192 	p->x = np++;
    193 	p->maxconv = maxconv;
    194 	l = sizeof(Conv*)*(p->maxconv+1);
    195 	p->conv = mallocz(l, 1);
    196 	if(p->conv == 0)
    197 		panic("no memory");
    198 }
    199 
    200 void
    201 ipinit(void)
    202 {
    203 	osipinit();
    204 
    205 	newproto("udp", S_UDP, 10);
    206 	newproto("tcp", S_TCP, 30);
    207 
    208 	fmtinstall('E', eipfmt);
    209 	fmtinstall('V', eipfmt);
    210 }
    211 
    212 Chan *
    213 ipattach(char *spec)
    214 {
    215 	Chan *c;
    216 
    217 	c = devattach('I', spec);
    218 	c->qid.path = QID(0, 0, Qtopdir);
    219 	c->qid.type = QTDIR;
    220 	c->qid.vers = 0;
    221 	return c;
    222 }
    223 
    224 static Walkqid*
    225 ipwalk(Chan *c, Chan *nc, char **name, int nname)
    226 {
    227 	return devwalk(c, nc, name, nname, 0, 0, ipgen);
    228 }
    229 
    230 int
    231 ipstat(Chan *c, uchar *dp, int n)
    232 {
    233 	return devstat(c, dp, n, 0, 0, ipgen);
    234 }
    235 
    236 Chan *
    237 ipopen(Chan *c, int omode)
    238 {
    239 	Proto *p;
    240 	ulong raddr;
    241 	ushort rport;
    242 	int perm, sfd;
    243 	Conv *cv, *lcv;
    244 
    245 	omode &= 3;
    246 	perm = 0;
    247 	switch(omode) {
    248 	case OREAD:
    249 		perm = 4;
    250 		break;
    251 	case OWRITE:
    252 		perm = 2;
    253 		break;
    254 	case ORDWR:
    255 		perm = 6;
    256 		break;
    257 	}
    258 
    259 	switch(TYPE(c->qid)) {
    260 	default:
    261 		break;
    262 	case Qtopdir:
    263 	case Qprotodir:
    264 	case Qconvdir:
    265 	case Qstatus:
    266 	case Qremote:
    267 	case Qlocal:
    268 		if(omode != OREAD)
    269 			error(Eperm);
    270 		break;
    271 	case Qclonus:
    272 		p = &proto[PROTO(c->qid)];
    273 		cv = protoclone(p, up->user, -1);
    274 		if(cv == 0)
    275 			error(Enodev);
    276 		c->qid.path = QID(p->x, cv->x, Qctl);
    277 		c->qid.vers = 0;
    278 		break;
    279 	case Qdata:
    280 	case Qctl:
    281 		p = &proto[PROTO(c->qid)];
    282 		lock(&p->l);
    283 		cv = p->conv[CONV(c->qid)];
    284 		lock(&cv->r.lk);
    285 		if((perm & (cv->perm>>6)) != perm) {
    286 			if(strcmp(up->user, cv->owner) != 0 ||
    287 		 	  (perm & cv->perm) != perm) {
    288 				unlock(&cv->r.lk);
    289 				unlock(&p->l);
    290 				error(Eperm);
    291 			}
    292 		}
    293 		cv->r.ref++;
    294 		if(cv->r.ref == 1) {
    295 			memmove(cv->owner, up->user, KNAMELEN);
    296 			cv->perm = 0660;
    297 		}
    298 		unlock(&cv->r.lk);
    299 		unlock(&p->l);
    300 		break;
    301 	case Qlisten:
    302 		p = &proto[PROTO(c->qid)];
    303 		lcv = p->conv[CONV(c->qid)];
    304 		sfd = so_accept(lcv->sfd, &raddr, &rport);
    305 		cv = protoclone(p, up->user, sfd);
    306 		if(cv == 0) {
    307 			close(sfd);
    308 			error(Enodev);
    309 		}
    310 		cv->raddr = raddr;
    311 		cv->rport = rport;
    312 		setladdr(cv);
    313 		cv->state = "Established";
    314 		c->qid.path = QID(p->x, cv->x, Qctl);
    315 		break;
    316 	}
    317 	c->mode = openmode(omode);
    318 	c->flag |= COPEN;
    319 	c->offset = 0;
    320 	return c;
    321 }
    322 
    323 void
    324 ipclose(Chan *c)
    325 {
    326 	Conv *cc;
    327 
    328 	switch(TYPE(c->qid)) {
    329 	case Qcs:
    330 	case Qdns:
    331 		csclose(c);
    332 		break;
    333 	case Qdata:
    334 	case Qctl:
    335 		if((c->flag & COPEN) == 0)
    336 			break;
    337 		cc = proto[PROTO(c->qid)].conv[CONV(c->qid)];
    338 		if(decref(&cc->r) != 0)
    339 			break;
    340 		strcpy(cc->owner, "network");
    341 		cc->perm = 0666;
    342 		cc->state = "Closed";
    343 		cc->laddr = 0;
    344 		cc->raddr = 0;
    345 		cc->lport = 0;
    346 		cc->rport = 0;
    347 		close(cc->sfd);
    348 		break;
    349 	}
    350 }
    351 
    352 void
    353 ipremove(Chan *c)
    354 {
    355 	if(TYPE(c->qid) == Qcs){
    356 		csremoved = 1;
    357 		csclose(c);
    358 		return;
    359 	}
    360 	devremove(c);
    361 }
    362 
    363 long
    364 ipread(Chan *ch, void *a, long n, vlong offset)
    365 {
    366 	int r;
    367 	Conv *c;
    368 	Proto *x;
    369 	uchar ip[4];
    370 	char buf[128], *p;
    371 
    372 /*print("ipread %s %lux\n", c2name(ch), (long)ch->qid.path);*/
    373 	p = a;
    374 	switch(TYPE(ch->qid)) {
    375 	default:
    376 		error(Eperm);
    377 	case Qcs:
    378 	case Qdns:
    379 		return csread(ch, a, n, offset);
    380 	case Qprotodir:
    381 	case Qtopdir:
    382 	case Qconvdir:
    383 		return devdirread(ch, a, n, 0, 0, ipgen);
    384 	case Qctl:
    385 		sprint(buf, "%d", CONV(ch->qid));
    386 		return readstr(offset, p, n, buf);
    387 	case Qremote:
    388 		c = proto[PROTO(ch->qid)].conv[CONV(ch->qid)];
    389 		hnputl(ip, c->raddr);
    390 		sprint(buf, "%V!%d\n", ip, c->rport);
    391 		return readstr(offset, p, n, buf);
    392 	case Qlocal:
    393 		c = proto[PROTO(ch->qid)].conv[CONV(ch->qid)];
    394 		hnputl(ip, c->laddr);
    395 		sprint(buf, "%V!%d\n", ip, c->lport);
    396 		return readstr(offset, p, n, buf);
    397 	case Qstatus:
    398 		x = &proto[PROTO(ch->qid)];
    399 		c = x->conv[CONV(ch->qid)];
    400 		sprint(buf, "%s/%d %d %s \n",
    401 			c->p->name, c->x, c->r.ref, c->state);
    402 		return readstr(offset, p, n, buf);
    403 	case Qdata:
    404 		c = proto[PROTO(ch->qid)].conv[CONV(ch->qid)];
    405 		r = so_recv(c->sfd, a, n, 0);
    406 		if(r < 0){
    407 			oserrstr();
    408 			nexterror();
    409 		}
    410 		if(r == 0 && ++c->eof > 3)
    411 			error(Ehungup);
    412 		return r;
    413 	}
    414 }
    415 
    416 static void
    417 setladdr(Conv *c)
    418 {
    419 	so_getsockname(c->sfd, &c->laddr, &c->lport);
    420 }
    421 
    422 static void
    423 setlport(Conv *c)
    424 {
    425 	if(c->restricted == 0 && c->lport == 0)
    426 		return;
    427 
    428 	so_bind(c->sfd, c->restricted, c->lport);
    429 }
    430 
    431 static void
    432 setladdrport(Conv *c, char *str)
    433 {
    434 	char *p;
    435 	uchar addr[4];
    436 
    437 	p = strchr(str, '!');
    438 	if(p == 0) {
    439 		p = str;
    440 		c->laddr = 0;
    441 	}
    442 	else {
    443 		*p++ = 0;
    444 		v4parseip(addr, str);
    445 		c->laddr = nhgetl(addr);
    446 	}
    447 	if(*p == '*')
    448 		c->lport = 0;
    449 	else
    450 		c->lport = atoi(p);
    451 
    452 	setlport(c);
    453 }
    454 
    455 static char*
    456 setraddrport(Conv *c, char *str)
    457 {
    458 	char *p;
    459 	uchar addr[4];
    460 
    461 	p = strchr(str, '!');
    462 	if(p == 0)
    463 		return "malformed address";
    464 	*p++ = 0;
    465 	v4parseip(addr, str);
    466 	c->raddr = nhgetl(addr);
    467 	c->rport = atoi(p);
    468 	p = strchr(p, '!');
    469 	if(p) {
    470 		if(strcmp(p, "!r") == 0)
    471 			c->restricted = 1;
    472 	}
    473 	return 0;
    474 }
    475 
    476 long
    477 ipwrite(Chan *ch, void *a, long n, vlong offset)
    478 {
    479 	Conv *c;
    480 	Proto *x;
    481 	int r, nf;
    482 	char *p, *fields[3], buf[128];
    483 
    484 	switch(TYPE(ch->qid)) {
    485 	default:
    486 		error(Eperm);
    487 	case Qcs:
    488 	case Qdns:
    489 		return cswrite(ch, a, n, offset);
    490 	case Qctl:
    491 		x = &proto[PROTO(ch->qid)];
    492 		c = x->conv[CONV(ch->qid)];
    493 		if(n > sizeof(buf)-1)
    494 			n = sizeof(buf)-1;
    495 		memmove(buf, a, n);
    496 		buf[n] = '\0';
    497 
    498 		nf = tokenize(buf, fields, 3);
    499 		if(strcmp(fields[0], "connect") == 0){
    500 			switch(nf) {
    501 			default:
    502 				error("bad args to connect");
    503 			case 2:
    504 				p = setraddrport(c, fields[1]);
    505 				if(p != 0)
    506 					error(p);
    507 				break;
    508 			case 3:
    509 				p = setraddrport(c, fields[1]);
    510 				if(p != 0)
    511 					error(p);
    512 				c->lport = atoi(fields[2]);
    513 				setlport(c);
    514 				break;
    515 			}
    516 			so_connect(c->sfd, c->raddr, c->rport);
    517 			setladdr(c);
    518 			c->state = "Established";
    519 			return n;
    520 		}
    521 		if(strcmp(fields[0], "announce") == 0) {
    522 			switch(nf){
    523 			default:
    524 				error("bad args to announce");
    525 			case 2:
    526 				setladdrport(c, fields[1]);
    527 				break;
    528 			}
    529 			so_listen(c->sfd);
    530 			c->state = "Announced";
    531 			return n;
    532 		}
    533 		if(strcmp(fields[0], "bind") == 0){
    534 			switch(nf){
    535 			default:
    536 				error("bad args to bind");
    537 			case 2:
    538 				c->lport = atoi(fields[1]);
    539 				break;
    540 			}
    541 			setlport(c);
    542 			return n;
    543 		}
    544 		error("bad control message");
    545 	case Qdata:
    546 		x = &proto[PROTO(ch->qid)];
    547 		c = x->conv[CONV(ch->qid)];
    548 		r = so_send(c->sfd, a, n, 0);
    549 		if(r < 0){
    550 			oserrstr();
    551 			nexterror();
    552 		}
    553 		return r;
    554 	}
    555 	return n;
    556 }
    557 
    558 static Conv*
    559 protoclone(Proto *p, char *user, int nfd)
    560 {
    561 	Conv *c, **pp, **ep;
    562 
    563 	c = 0;
    564 	lock(&p->l);
    565 	if(waserror()) {
    566 		unlock(&p->l);
    567 		nexterror();
    568 	}
    569 	ep = &p->conv[p->maxconv];
    570 	for(pp = p->conv; pp < ep; pp++) {
    571 		c = *pp;
    572 		if(c == 0) {
    573 			c = mallocz(sizeof(Conv), 1);
    574 			if(c == 0)
    575 				error(Enomem);
    576 			lock(&c->r.lk);
    577 			c->r.ref = 1;
    578 			c->p = p;
    579 			c->x = pp - p->conv;
    580 			p->nc++;
    581 			*pp = c;
    582 			break;
    583 		}
    584 		lock(&c->r.lk);
    585 		if(c->r.ref == 0) {
    586 			c->r.ref++;
    587 			break;
    588 		}
    589 		unlock(&c->r.lk);
    590 	}
    591 	if(pp >= ep) {
    592 		unlock(&p->l);
    593 		poperror();
    594 		return 0;
    595 	}
    596 
    597 	strcpy(c->owner, user);
    598 	c->perm = 0660;
    599 	c->state = "Closed";
    600 	c->restricted = 0;
    601 	c->laddr = 0;
    602 	c->raddr = 0;
    603 	c->lport = 0;
    604 	c->rport = 0;
    605 	c->sfd = nfd;
    606 	if(nfd == -1)
    607 		c->sfd = so_socket(p->stype);
    608 	c->eof = 0;
    609 
    610 	unlock(&c->r.lk);
    611 	unlock(&p->l);
    612 	poperror();
    613 	return c;
    614 }
    615 
    616 /*
    617  * In-kernel /net/cs and /net/dns
    618  */
    619 void
    620 csclose(Chan *c)
    621 {
    622 	free(c->aux);
    623 }
    624 
    625 long
    626 csread(Chan *c, void *a, long n, vlong offset)
    627 {
    628 	if(c->aux == nil)
    629 		return 0;
    630 	return readstr(offset, a, n, c->aux);
    631 }
    632 
    633 static struct
    634 {
    635 	char *proto;
    636 	char *name;
    637 	uint num;
    638 } tab[] = {
    639 	"tcp", "cs", 1,
    640 	"tcp", "echo", 7,
    641 	"tcp", "discard", 9,
    642 	"tcp", "systat", 11,
    643 	"tcp", "daytime", 13,
    644 	"tcp", "netstat", 15,
    645 	"tcp", "chargen", 19,
    646 	"tcp", "ftp-data", 20,
    647 	"tcp", "ftp", 21,
    648 	"tcp", "ssh", 22,
    649 	"tcp", "telnet", 23,
    650 	"tcp", "smtp", 25,
    651 	"tcp", "time", 37,
    652 	"tcp", "whois", 43,
    653 	"tcp", "dns", 53,
    654 	"tcp", "domain", 53,
    655 	"tcp", "uucp", 64,
    656 	"tcp", "gopher", 70,
    657 	"tcp", "rje", 77,
    658 	"tcp", "finger", 79,
    659 	"tcp", "http", 80,
    660 	"tcp", "link", 87,
    661 	"tcp", "supdup", 95,
    662 	"tcp", "hostnames", 101,
    663 	"tcp", "iso-tsap", 102,
    664 	"tcp", "x400", 103,
    665 	"tcp", "x400-snd", 104,
    666 	"tcp", "csnet-ns", 105,
    667 	"tcp", "pop-2", 109,
    668 	"tcp", "pop3", 110,
    669 	"tcp", "portmap", 111,
    670 	"tcp", "uucp-path", 117,
    671 	"tcp", "nntp", 119,
    672 	"tcp", "netbios", 139,
    673 	"tcp", "imap4", 143,
    674 	"tcp", "imap", 143,
    675 	"tcp", "NeWS", 144,
    676 	"tcp", "print-srv", 170,
    677 	"tcp", "z39.50", 210,
    678 	"tcp", "fsb", 400,
    679 	"tcp", "sysmon", 401,
    680 	"tcp", "proxy", 402,
    681 	"tcp", "proxyd", 404,
    682 	"tcp", "https", 443,
    683 	"tcp", "cifs", 445,
    684 	"tcp", "ssmtp", 465,
    685 	"tcp", "rexec", 512,
    686 	"tcp", "login", 513,
    687 	"tcp", "shell", 514,
    688 	"tcp", "printer", 515,
    689 	"tcp", "ncp", 524,
    690 	"tcp", "courier", 530,
    691 	"tcp", "cscan", 531,
    692 	"tcp", "uucp", 540,
    693 	"tcp", "snntp", 563,
    694 	"tcp", "9fs", 564,
    695 	"tcp", "whoami", 565,
    696 	"tcp", "guard", 566,
    697 	"tcp", "ticket", 567,
    698 	"tcp", "fmclient", 729,
    699 	"tcp", "imaps", 993,
    700 	"tcp", "pop3s", 995,
    701 	"tcp", "ingreslock", 1524,
    702 	"tcp", "pptp", 1723,
    703 	"tcp", "nfs", 2049,
    704 	"tcp", "webster", 2627,
    705 	"tcp", "weather", 3000,
    706 	"tcp", "sip", 5060,
    707 	"tcp", "sips", 5061,
    708 	"tcp", "secstore", 5356,
    709 	"tcp", "vnc-http", 5800,
    710 	"tcp", "vnc", 5900,
    711 	"tcp", "Xdisplay", 6000,
    712 	"tcp", "styx", 6666,
    713 	"tcp", "mpeg", 6667,
    714 	"tcp", "rstyx", 6668,
    715 	"tcp", "infdb", 6669,
    716 	"tcp", "infsigner", 6671,
    717 	"tcp", "infcsigner", 6672,
    718 	"tcp", "inflogin", 6673,
    719 	"tcp", "bandt", 7330,
    720 	"tcp", "face", 32000,
    721 	"tcp", "dhashgate", 11978,
    722 	"tcp", "exportfs", 17007,
    723 	"tcp", "rexexec", 17009,
    724 	"tcp", "ncpu", 17010,
    725 	"tcp", "cpu", 17013,
    726 	"tcp", "glenglenda1", 17020,
    727 	"tcp", "glenglenda2", 17021,
    728 	"tcp", "glenglenda3", 17022,
    729 	"tcp", "glenglenda4", 17023,
    730 	"tcp", "glenglenda5", 17024,
    731 	"tcp", "glenglenda6", 17025,
    732 	"tcp", "glenglenda7", 17026,
    733 	"tcp", "glenglenda8", 17027,
    734 	"tcp", "glenglenda9", 17028,
    735 	"tcp", "glenglenda10", 17029,
    736 	"tcp", "flyboy", 17032,
    737 	"tcp", "venti", 17034,
    738 	"tcp", "wiki", 17035,
    739 	"tcp", "vica", 17036,
    740 
    741 //	"il", "9fs", 17008,
    742 
    743 	"udp", "echo", 7,
    744 	"udp", "tacacs", 49,
    745 	"udp", "tftp", 69,
    746 	"udp", "bootpc", 68,
    747 	"udp", "bootp", 67,
    748 	"udp", "domain", 53,
    749 	"udp", "dns", 53,
    750 	"udp", "portmap", 111,
    751 	"udp", "ntp", 123,
    752 	"udp", "netbios-ns", 137,
    753 	"udp", "snmp", 161,
    754 	"udp", "syslog", 514,
    755 	"udp", "rip", 520,
    756 	"udp", "dhcp6c", 546,
    757 	"udp", "dhcp6s", 547,
    758 	"udp", "nfs", 2049,
    759 	"udp", "bfs", 2201,
    760 	"udp", "virgil", 2202,
    761 	"udp", "sip", 5060,
    762 	"udp", "bandt2", 7331,
    763 	"udp", "oradius", 1645,
    764 	"udp", "dhash", 11977,
    765 	0
    766 };
    767 
    768 static int
    769 lookupport(char *s, char **pproto)
    770 {
    771 	int i;
    772 	char buf[10], *p, *proto;
    773 
    774 	i = strtol(s, &p, 0);
    775 	if(*s && *p == 0)
    776 		return i;
    777 
    778 	proto = *pproto;
    779 	if(strcmp(proto, "net") == 0)
    780 		proto = nil;
    781 	if(proto == nil){
    782 		if(so_getservbyname(s, "tcp", buf) >= 0){
    783 			*pproto = "tcp";
    784 			return atoi(buf);
    785 		}
    786 		if(so_getservbyname(s, "udp", buf) >= 0){
    787 			*pproto = "udp";
    788 			return atoi(buf);
    789 		}
    790 	}else{
    791 		if(strcmp(proto, "tcp") != 0 && strcmp(proto, "udp") != 0)
    792 			return 0;
    793 		if(so_getservbyname(s, proto, buf) >= 0){
    794 			*pproto = "tcp";
    795 			return atoi(buf);
    796 		}
    797 	}
    798 	for(i=0; tab[i].proto; i++){
    799 		if(proto == nil || strcmp(proto, tab[i].proto) == 0)
    800 		if(strcmp(s, tab[i].name) == 0){
    801 			if(proto == nil)
    802 				*pproto = tab[i].proto;
    803 			return tab[i].num;
    804 		}
    805 	}
    806 	return 0;
    807 }
    808 
    809 static int
    810 lookuphost(char *s, uchar *to)
    811 {
    812 	ulong ip;
    813 	char *p;
    814 
    815 	memset(to, 0, 4);
    816 	p = v4parseip(to, s);
    817 	if(p && *p == 0 && (ip = nhgetl(to)) != 0)
    818 		return 0;
    819 	if((s = hostlookup(s)) == nil)
    820 		return -1;
    821 	v4parseip(to, s);
    822 	free(s);
    823 	return 0;
    824 }
    825 
    826 long
    827 cswrite(Chan *c, void *a, long n, vlong offset)
    828 {
    829 	char *f[4];
    830 	char *s, *ns;
    831 	int nf, port, bang;
    832 	uchar ip[4];
    833 
    834 	s = malloc(n+1);
    835 	if(s == nil)
    836 		error(Enomem);
    837 	ns = malloc(128);
    838 	if(ns == nil){
    839 		free(s);
    840 		error(Enomem);
    841 	}
    842 	if(waserror()){
    843 		free(s);
    844 		free(ns);
    845 		nexterror();
    846 	}
    847 	memmove(s, a, n);
    848 	s[n] = 0;
    849 	
    850 	if(TYPE(c->qid) == Qcs){
    851 		nf = getfields(s, f, nelem(f), 0, "!");
    852 		if(nf != 3)
    853 			error("bad syntax");
    854 
    855 		port = lookupport(f[2], &f[0]);
    856 		if(port <= 0)
    857 			error("no translation for port found");
    858 
    859 		if(lookuphost(f[1], ip) < 0)
    860 			error("no translation for host found");
    861 		snprint(ns, 128, "/net/%s/clone %V!%d", f[0], ip, port);
    862 	}else{
    863 		/* dns */
    864 		bang = 0;
    865 		if(s[0] == '!')
    866 			bang = 1;
    867 		nf = tokenize(s+bang, f, nelem(f));
    868 		if(nf > 2)
    869 			error("bad syntax");
    870 		if(nf > 1 && strcmp(f[1], "ip") != 0)
    871 			error("can only lookup ip addresses");
    872 		if(lookuphost(f[0], ip) < 0)
    873 			error("no translation for host found");
    874 		if(bang)
    875 			snprint(ns, 128, "dom=%s ip=%V", f[0], ip);
    876 		else
    877 			snprint(ns, 128, "%s ip\t%V", f[0], ip);
    878 	}
    879 	free(c->aux);
    880 	c->aux = ns;
    881 	poperror();
    882 	free(s);
    883 	return n;
    884 }
    885 
    886 Dev pipdevtab = 
    887 {
    888 	'I',
    889 	"ip",
    890 
    891 	devreset,
    892 	ipinit,
    893 	devshutdown,
    894 	ipattach,
    895 	ipwalk,
    896 	ipstat,
    897 	ipopen,
    898 	devcreate,
    899 	ipclose,
    900 	ipread,
    901 	devbread,
    902 	ipwrite,
    903 	devbwrite,
    904 	ipremove,
    905 	devwstat,
    906 };
    907