xref: /plan9-contrib/sys/src/cmd/unix/drawterm/kern/devip.c (revision 8ccd4a6360d974db7bd7bbd4f37e7018419ea908)
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, 0);
692 		if(*p == '.')
693 			p++;
694 	}
695 	switch(CLASS(to)){
696 	case 0:	/* class A - 1 byte net */
697 	case 1:
698 		if(i == 3){
699 			to[3] = to[2];
700 			to[2] = to[1];
701 			to[1] = 0;
702 		} else if (i == 2){
703 			to[3] = to[1];
704 			to[1] = 0;
705 		}
706 		break;
707 	case 2:	/* class B - 2 byte net */
708 		if(i == 3){
709 			to[3] = to[2];
710 			to[2] = 0;
711 		}
712 		break;
713 	}
714 	return nhgetl(to);
715 }
716 
717 void
718 csclose(Chan *c)
719 {
720 	free(c->aux);
721 }
722 
723 long
724 csread(Chan *c, void *a, long n, vlong offset)
725 {
726 	if(c->aux == nil)
727 		return 0;
728 	return readstr(offset, a, n, c->aux);
729 }
730 
731 static struct
732 {
733 	char *name;
734 	uint num;
735 } tab[] = {
736 	"cs", 1,
737 	"echo", 7,
738 	"discard", 9,
739 	"systat", 11,
740 	"daytime", 13,
741 	"netstat", 15,
742 	"chargen", 19,
743 	"ftp-data", 20,
744 	"ftp", 21,
745 	"ssh", 22,
746 	"telnet", 23,
747 	"smtp", 25,
748 	"time", 37,
749 	"whois", 43,
750 	"dns", 53,
751 	"domain", 53,
752 	"uucp", 64,
753 	"gopher", 70,
754 	"rje", 77,
755 	"finger", 79,
756 	"http", 80,
757 	"link", 87,
758 	"supdup", 95,
759 	"hostnames", 101,
760 	"iso-tsap", 102,
761 	"x400", 103,
762 	"x400-snd", 104,
763 	"csnet-ns", 105,
764 	"pop-2", 109,
765 	"pop3", 110,
766 	"portmap", 111,
767 	"uucp-path", 117,
768 	"nntp", 119,
769 	"netbios", 139,
770 	"imap4", 143,
771 	"NeWS", 144,
772 	"print-srv", 170,
773 	"z39.50", 210,
774 	"fsb", 400,
775 	"sysmon", 401,
776 	"proxy", 402,
777 	"proxyd", 404,
778 	"https", 443,
779 	"cifs", 445,
780 	"ssmtp", 465,
781 	"rexec", 512,
782 	"login", 513,
783 	"shell", 514,
784 	"printer", 515,
785 	"courier", 530,
786 	"cscan", 531,
787 	"uucp", 540,
788 	"snntp", 563,
789 	"9fs", 564,
790 	"whoami", 565,
791 	"guard", 566,
792 	"ticket", 567,
793 	"dlsftp", 666,
794 	"fmclient", 729,
795 	"imaps", 993,
796 	"pop3s", 995,
797 	"ingreslock", 1524,
798 	"pptp", 1723,
799 	"nfs", 2049,
800 	"webster", 2627,
801 	"weather", 3000,
802 	"secstore", 5356,
803 	"Xdisplay", 6000,
804 	"styx", 6666,
805 	"mpeg", 6667,
806 	"rstyx", 6668,
807 	"infdb", 6669,
808 	"infsigner", 6671,
809 	"infcsigner", 6672,
810 	"inflogin", 6673,
811 	"bandt", 7330,
812 	"face", 32000,
813 	"dhashgate", 11978,
814 	"exportfs", 17007,
815 	"rexexec", 17009,
816 	"ncpu", 17010,
817 	"cpu", 17013,
818 	"glenglenda1", 17020,
819 	"glenglenda2", 17021,
820 	"glenglenda3", 17022,
821 	"glenglenda4", 17023,
822 	"glenglenda5", 17024,
823 	"glenglenda6", 17025,
824 	"glenglenda7", 17026,
825 	"glenglenda8", 17027,
826 	"glenglenda9", 17028,
827 	"glenglenda10", 17029,
828 	"flyboy", 17032,
829 	"dlsftp", 17033,
830 	"venti", 17034,
831 	"wiki", 17035,
832 	"vica", 17036,
833 	0
834 };
835 
836 static int
837 lookupport(char *s)
838 {
839 	int i;
840 	char buf[10], *p;
841 
842 	i = strtol(s, &p, 0);
843 	if(*s && *p == 0)
844 		return i;
845 
846 	i = so_getservbyname(s, "tcp", buf);
847 	if(i != -1)
848 		return atoi(buf);
849 	for(i=0; tab[i].name; i++)
850 		if(strcmp(s, tab[i].name) == 0)
851 			return tab[i].num;
852 	return 0;
853 }
854 
855 static ulong
856 lookuphost(char *s)
857 {
858 	char to[4];
859 	ulong ip;
860 
861 	memset(to, 0, sizeof to);
862 	parseip(to, s);
863 	ip = nhgetl(to);
864 	if(ip != 0)
865 		return ip;
866 	if((s = hostlookup(s)) == nil)
867 		return 0;
868 	parseip(to, s);
869 	ip = nhgetl(to);
870 	free(s);
871 	return ip;
872 }
873 
874 long
875 cswrite(Chan *c, void *a, long n, vlong offset)
876 {
877 	char *f[4];
878 	char *s, *ns;
879 	ulong ip;
880 	int nf, port;
881 
882 	s = malloc(n+1);
883 	if(s == nil)
884 		error(Enomem);
885 	if(waserror()){
886 		free(s);
887 		nexterror();
888 	}
889 	memmove(s, a, n);
890 	s[n] = 0;
891 	nf = getfields(s, f, nelem(f), 0, "!");
892 	if(nf != 3)
893 		error("can't translate");
894 
895 	port = lookupport(f[2]);
896 	if(port <= 0)
897 		error("no translation for port found");
898 
899 	ip = lookuphost(f[1]);
900 	if(ip == 0)
901 		error("no translation for host found");
902 
903 	ns = smprint("/net/%s/clone %I!%d", f[0], ip, port);
904 	if(ns == nil)
905 		error(Enomem);
906 	free(c->aux);
907 	c->aux = ns;
908 	poperror();
909 	free(s);
910 	return n;
911 }
912 
913 Dev ipdevtab =
914 {
915 	'I',
916 	"ip",
917 
918 	devreset,
919 	ipinit,
920 	devshutdown,
921 	ipattach,
922 	ipwalk,
923 	ipstat,
924 	ipopen,
925 	devcreate,
926 	ipclose,
927 	ipread,
928 	devbread,
929 	ipwrite,
930 	devbwrite,
931 	devremove,
932 	devwstat,
933 };
934 
935