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