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