cae36a52 |
1 | #include <u.h> |
2 | #include <libc.h> |
3 | #include <ureg.h> |
4 | #include "dat.h" |
5 | #include "fns.h" |
6 | #include "linux.h" |
7 | |
8 | typedef struct Socket Socket; |
9 | typedef struct Connectproc Connectproc; |
10 | typedef struct Listenproc Listenproc; |
11 | |
12 | enum { |
13 | Ctlsize = 128, |
14 | }; |
15 | |
16 | struct Socket |
17 | { |
18 | Ufile; |
19 | |
20 | int family; |
21 | int stype; |
22 | int protocol; |
23 | |
24 | int other; |
25 | char net[40]; |
26 | char name[Ctlsize]; |
27 | |
28 | int naddr; |
29 | uchar addr[40]; |
30 | |
31 | void *bufproc; |
32 | Connectproc *connectproc; |
33 | Listenproc *listenproc; |
34 | |
35 | int connected; |
36 | int error; |
37 | |
38 | Socket *next; |
39 | }; |
40 | |
41 | struct Connectproc |
42 | { |
43 | Ref; |
44 | QLock; |
45 | Socket *sock; |
46 | int notefd; |
47 | Uwaitq wq; |
48 | char str[Ctlsize]; |
49 | }; |
50 | |
51 | struct Listenproc |
52 | { |
53 | Ref; |
54 | QLock; |
55 | Socket *sock; |
56 | int notefd; |
57 | Uwaitq wq; |
58 | Socket *q; |
59 | char str[Ctlsize]; |
60 | }; |
61 | |
62 | enum |
63 | { |
64 | AF_UNIX =1, |
65 | AF_INET =2, |
66 | AF_INET6 =10, |
67 | }; |
68 | |
69 | enum |
70 | { |
71 | SOCK_STREAM =1, |
72 | SOCK_DGRAM =2, |
73 | SOCK_RAW =3, |
74 | }; |
75 | |
76 | static char* |
77 | srvname(char *npath, char *path, int len) |
78 | { |
79 | char *p; |
80 | |
81 | p = strrchr(path, '/'); |
82 | if(p == 0) |
83 | p = path; |
84 | else |
85 | p++; |
86 | snprint(npath, len, "/srv/UD.%s", p); |
87 | return npath; |
88 | } |
89 | |
90 | static int |
91 | srvunixsock(int fd, char *path) |
92 | { |
93 | int ret; |
94 | int sfd; |
95 | char buf[8+Ctlsize+1]; |
96 | |
97 | sfd = -1; |
98 | ret = -1; |
99 | if(fd < 0) |
100 | goto out; |
101 | srvname(buf, path, sizeof(buf)); |
102 | remove(buf); |
103 | if((sfd = create(buf, OWRITE, 0666)) < 0) |
104 | goto out; |
105 | sprint(buf, "%d", fd); |
106 | if(write(sfd, buf, strlen(buf)) < 0) |
107 | goto out; |
108 | ret = 0; |
109 | out: |
110 | if(sfd >= 0) |
111 | close(sfd); |
112 | return ret; |
113 | } |
114 | |
115 | static void |
116 | unsrvunixsock(char *path) |
117 | { |
118 | char buf[8+Ctlsize+1]; |
119 | |
120 | srvname(buf, path, sizeof(buf)); |
121 | remove(buf); |
122 | } |
123 | |
124 | static Socket* |
125 | allocsock(int family, int stype, int protocol) |
126 | { |
127 | Socket *sock; |
128 | |
129 | sock = kmallocz(sizeof(*sock), 1); |
130 | sock->family = family; |
131 | sock->stype = stype; |
132 | sock->protocol = protocol; |
133 | sock->fd = -1; |
134 | sock->other = -1; |
135 | sock->ref = 1; |
136 | sock->dev = SOCKDEV; |
137 | sock->mode = O_RDWR; |
138 | |
139 | return sock; |
140 | } |
141 | |
142 | static int |
143 | newsock(int family, int stype, int protocol) |
144 | { |
145 | Socket *sock; |
146 | char *net; |
147 | char buf[Ctlsize]; |
148 | int pfd[2]; |
149 | int cfd, dfd; |
150 | int n; |
151 | int err; |
152 | |
153 | trace("newsock(%d, %d, %d)", family, stype, protocol); |
154 | |
155 | err = -EINVAL; |
156 | switch(family){ |
157 | case AF_INET: |
158 | case AF_INET6: |
159 | switch(stype){ |
160 | case SOCK_DGRAM: |
161 | net = "udp"; |
162 | break; |
163 | case SOCK_STREAM: |
164 | net = "tcp"; |
165 | break; |
166 | default: |
167 | trace("newsock() unknown socket type %d/%d", family, stype); |
168 | return err; |
169 | } |
170 | break; |
171 | case AF_UNIX: |
172 | net = nil; |
173 | break; |
174 | |
175 | default: |
176 | trace("newsock() unknown network family %d", family); |
177 | return err; |
178 | } |
179 | |
180 | sock = allocsock(family, stype, protocol); |
181 | cfd = -1; |
182 | if(net == nil){ |
183 | if(pipe(pfd) < 0){ |
184 | err = mkerror(); |
185 | goto errout; |
186 | } |
187 | sock->other = pfd[1]; |
188 | sock->fd = pfd[0]; |
189 | } else { |
190 | snprint(buf, sizeof(buf), "/net/%s/clone", net); |
191 | if((cfd = open(buf, ORDWR)) < 0){ |
192 | err = mkerror(); |
193 | goto errout; |
194 | } |
195 | n = read(cfd, buf, sizeof(buf)-1); |
196 | if(n < 0) |
197 | err = mkerror(); |
198 | if(n <= 0) |
199 | goto errout; |
200 | buf[n] = 0; |
201 | n = atoi(buf); |
202 | snprint(buf, sizeof(buf), "/net/%s/%d/data", net, n); |
203 | if((dfd = open(buf, ORDWR)) < 0){ |
204 | err = mkerror(); |
205 | goto errout; |
206 | } |
207 | close(cfd); |
208 | sock->fd = dfd; |
209 | snprint(sock->net, sizeof(sock->net), "/net/%s", net); |
210 | snprint(sock->name, sizeof(sock->name), "%s/%d", sock->net, n); |
211 | } |
212 | return newfd(sock, FD_CLOEXEC); |
213 | |
214 | errout: |
215 | close(cfd); |
216 | free(sock); |
217 | return err; |
218 | } |
219 | |
220 | static void |
221 | freeconnectproc(Connectproc *cp) |
222 | { |
223 | if(cp == nil) |
224 | return; |
225 | qlock(cp); |
226 | cp->sock = nil; |
227 | if(decref(cp)){ |
228 | write(cp->notefd, "interrupt", 9); |
229 | qunlock(cp); |
230 | return; |
231 | } |
232 | qunlock(cp); |
233 | close(cp->notefd); |
234 | free(cp); |
235 | } |
236 | |
237 | static void |
238 | freelistenproc(Listenproc *lp) |
239 | { |
240 | Socket *q; |
241 | |
242 | if(lp == nil) |
243 | return; |
244 | qlock(lp); |
245 | lp->sock = nil; |
246 | if(decref(lp)){ |
247 | write(lp->notefd, "interrupt", 9); |
248 | qunlock(lp); |
249 | return; |
250 | } |
251 | while(q = lp->q){ |
252 | lp->q = q->next; |
253 | putfile(q); |
254 | } |
255 | qunlock(lp); |
256 | close(lp->notefd); |
257 | free(lp); |
258 | } |
259 | |
260 | static int |
261 | closesock(Ufile *file) |
262 | { |
263 | Socket *sock = (Socket*)file; |
264 | |
265 | close(sock->fd); |
266 | close(sock->other); |
267 | freebufproc(sock->bufproc); |
268 | freeconnectproc(sock->connectproc); |
269 | freelistenproc(sock->listenproc); |
270 | return 0; |
271 | } |
272 | |
273 | |
274 | static void |
275 | connectproc(void *aux) |
276 | { |
277 | int fd, cfd, other; |
278 | char buf[Ctlsize], tmp[8+Ctlsize+1]; |
279 | Connectproc *cp; |
280 | Socket *sock; |
281 | int err; |
282 | |
283 | cp = (Connectproc*)aux; |
284 | qlock(cp); |
285 | if((sock = cp->sock) == nil) |
286 | goto out; |
287 | |
288 | snprint(buf, sizeof(buf), "connectproc() %s", cp->str); |
289 | setprocname(buf); |
290 | |
291 | err = 0; |
292 | switch(sock->family){ |
293 | case AF_UNIX: |
294 | fd = sock->fd; |
295 | other = sock->other; |
296 | qunlock(cp); |
297 | |
298 | err = -ECONNREFUSED; |
299 | srvname(tmp, cp->str, sizeof(buf)); |
300 | if((cfd = open(tmp, ORDWR)) < 0) |
301 | break; |
302 | |
303 | memset(buf, 0, sizeof(buf)); |
304 | snprint(buf, sizeof(buf), "linuxemu.%d.%lux", getpid(), (ulong)sock); |
305 | if(srvunixsock(other, buf) < 0){ |
306 | close(cfd); |
307 | break; |
308 | } |
309 | |
310 | /* |
311 | * write Ctrlsize-1 bytes so concurrent writes will not be merged together as |
312 | * Ctrlsize-1 is the size used in read(). see /sys/src/ape/lib/bsd/accept.c:87 |
313 | * this should be fixed in ape's connect() as well. |
314 | */ |
315 | if(write(cfd, buf, sizeof(buf)-1) != sizeof(buf)-1){ |
316 | close(cfd); |
317 | unsrvunixsock(buf); |
318 | break; |
319 | } |
320 | close(cfd); |
321 | if((read(fd, tmp, strlen(buf)) != strlen(buf)) || memcmp(buf, tmp, strlen(buf))){ |
322 | unsrvunixsock(buf); |
323 | break; |
324 | } |
325 | unsrvunixsock(buf); |
326 | err = 0; |
327 | break; |
328 | |
329 | default: |
330 | snprint(buf, sizeof(buf), "%s/ctl", sock->name); |
331 | qunlock(cp); |
332 | if((cfd = open(buf, ORDWR)) < 0){ |
333 | err = mkerror(); |
334 | break; |
335 | } |
336 | if(fprint(cfd, "connect %s", cp->str) < 0) |
337 | err = mkerror(); |
338 | close(cfd); |
339 | } |
340 | |
341 | qlock(cp); |
342 | if((sock = cp->sock) == nil) |
343 | goto out; |
344 | if(err == 0){ |
345 | close(sock->other); |
346 | sock->other = -1; |
347 | sock->connected = 1; |
348 | } |
349 | sock->error = err; |
350 | out: |
351 | wakeq(&cp->wq, MAXPROC); |
352 | qunlock(cp); |
353 | freeconnectproc(cp); |
354 | } |
355 | |
356 | static int |
357 | sockaddr2str(Socket *sock, uchar *addr, int addrlen, char *buf, int nbuf) |
358 | { |
359 | int err; |
360 | |
361 | err = -EINVAL; |
362 | switch(sock->family){ |
363 | case AF_INET: |
364 | if(addrlen < 8) |
365 | break; |
366 | err = snprint(buf, nbuf, "%d.%d.%d.%d!%d", |
367 | (int)(addr[4]), |
368 | (int)(addr[5]), |
369 | (int)(addr[6]), |
370 | (int)(addr[7]), |
371 | (int)(((ulong)addr[2]<<8)|(ulong)addr[3])); |
372 | break; |
373 | |
374 | case AF_INET6: |
375 | /* TODO */ |
376 | break; |
377 | |
378 | case AF_UNIX: |
379 | if(addrlen <= 2) |
380 | break; |
381 | addrlen -= 2; |
382 | if(addrlen >= nbuf) |
383 | addrlen = nbuf-1; |
384 | memmove(buf, addr+2, addrlen); |
385 | buf[addrlen] = 0; |
386 | err = addrlen; |
387 | break; |
388 | } |
389 | |
390 | return err; |
391 | } |
392 | |
393 | static int |
394 | connectsock(Socket *sock, uchar *addr, int addrlen) |
395 | { |
396 | Connectproc *cp; |
397 | int err; |
398 | char buf[Ctlsize]; |
399 | int pid; |
400 | |
401 | if(sock->connected) |
402 | return -EISCONN; |
403 | if(sock->connectproc) |
404 | return -EALREADY; |
405 | |
406 | if((err = sockaddr2str(sock, addr, addrlen, buf, sizeof(buf))) < 0) |
407 | return err; |
408 | |
409 | cp = kmallocz(sizeof(*cp), 1); |
410 | cp->ref = 2; |
411 | cp->sock = sock; |
412 | strncpy(cp->str, buf, sizeof(cp->str)); |
413 | |
414 | qlock(cp); |
415 | sock->error = 0; |
416 | if((pid = procfork(connectproc, cp, 0)) < 0){ |
417 | qunlock(cp); |
418 | free(cp); |
419 | return mkerror(); |
420 | } |
421 | snprint(buf, sizeof(buf), "/proc/%d/note", pid); |
422 | cp->notefd = open(buf, OWRITE); |
423 | |
424 | if(addrlen > sizeof(sock->addr)) |
425 | addrlen = sizeof(sock->addr); |
426 | sock->naddr = addrlen; |
427 | memmove(sock->addr, addr, addrlen); |
428 | |
429 | sock->connectproc = cp; |
430 | if(sock->mode & O_NONBLOCK){ |
431 | qunlock(cp); |
432 | return -EINPROGRESS; |
433 | } |
434 | if((err = sleepq(&cp->wq, cp, 1)) == 0) |
435 | err = sock->error; |
436 | qunlock(cp); |
437 | |
438 | /* |
439 | * crazy shit is going on! |
440 | * see: http://www.madore.org/~david/computers/connect-intr.html |
441 | */ |
442 | if(err != -EINTR && err != -ERESTART){ |
443 | sock->connectproc = nil; |
444 | freeconnectproc(cp); |
445 | } |
446 | return err; |
447 | } |
448 | |
449 | static int |
450 | shutdownsock(Socket *sock, int how) |
451 | { |
452 | USED(how); |
453 | |
454 | freebufproc(sock->bufproc); |
455 | sock->bufproc = nil; |
456 | freeconnectproc(sock->connectproc); |
457 | sock->connectproc = nil; |
458 | freelistenproc(sock->listenproc); |
459 | sock->listenproc = nil; |
460 | close(sock->fd); |
461 | sock->fd = -1; |
462 | sock->connected = 0; |
463 | |
464 | return 0; |
465 | } |
466 | |
467 | static int |
468 | bindsock(Socket *sock, uchar *addr, int addrlen) |
469 | { |
470 | int port; |
471 | int cfd; |
472 | char buf[Ctlsize]; |
473 | |
474 | port = -1; |
475 | switch(sock->family){ |
476 | default: |
477 | return -EINVAL; |
478 | |
479 | case AF_UNIX: |
480 | break; |
481 | case AF_INET: |
482 | if(addrlen < 4) |
483 | return -EINVAL; |
484 | port = (int)(((ulong)addr[2]<<8)|(ulong)addr[3]); |
485 | break; |
486 | case AF_INET6: |
487 | /* TODO */ |
488 | return -EINVAL; |
489 | } |
490 | |
491 | if(port >= 0){ |
492 | snprint(buf, sizeof(buf), "%s/ctl", sock->name); |
493 | if((cfd = open(buf, ORDWR)) < 0) |
494 | return mkerror(); |
495 | if((fprint(cfd, "announce %d", port) < 0) || (fprint(cfd, "bind %d", port) < 0)){ |
496 | close(cfd); |
497 | return mkerror(); |
498 | } |
499 | close(cfd); |
500 | } |
501 | |
502 | if(addrlen > sizeof(sock->addr)) |
503 | addrlen = sizeof(sock->addr); |
504 | sock->naddr = addrlen; |
505 | memmove(sock->addr, addr, addrlen); |
506 | |
507 | return 0; |
508 | } |
509 | |
510 | static int |
511 | strtoip(char *str, uchar *ip, int iplen) |
512 | { |
513 | int i, d, v6; |
514 | char *p, *k; |
515 | |
516 | i = 0; |
517 | v6 = 1; |
518 | memset(ip, 0, iplen); |
519 | for(p = str; *p; p++){ |
520 | if(*p == ':'){ |
521 | if(p[1] == ':'){ |
522 | p++; |
523 | i = iplen; |
524 | for(k = p+1; *k; k++){ |
525 | if(*k == ':'){ |
526 | v6 = 1; |
527 | i -= 2; |
528 | } |
529 | if(*k == '.'){ |
530 | v6 = 0; |
531 | i -= 1; |
532 | } |
533 | } |
534 | i -= v6+1; |
535 | } else { |
536 | i += 2; |
537 | } |
538 | continue; |
539 | } else if(*p == '.'){ |
540 | i++; |
541 | continue; |
542 | } |
543 | |
544 | for(k = p; *k && *k != '.' && *k != ':'; k++) |
545 | ; |
546 | if(*k == '.'){ |
547 | v6 = 0; |
548 | } else if(*k == ':'){ |
549 | v6 = 1; |
550 | } |
551 | |
552 | if(i < 0 || i + v6+1 > iplen) |
553 | return -1; |
554 | |
555 | if(*p >= '0' && *p <= '9'){ |
556 | d = *p - '0'; |
557 | } else if(v6 && (*p >= 'a' && *p <= 'f')){ |
558 | d = 0x0A + *p - 'a'; |
559 | } else if(v6 && (*p >= 'A' && *p <= 'F')){ |
560 | d = 0x0A + *p - 'A'; |
561 | } else { |
562 | return -1; |
563 | } |
564 | |
565 | if(v6){ |
566 | d |= ((int)ip[i]<<12 | (int)ip[i+1]<<4); |
567 | ip[i] = (d>>8) & 0xFF; |
568 | ip[i+1] = d & 0xFF; |
569 | } else { |
570 | ip[i] = ip[i]*10 + d; |
571 | } |
572 | } |
573 | |
574 | return i + v6+1; |
575 | } |
576 | |
577 | static int |
578 | getsockaddr(Socket *sock, int remote, uchar *addr, int len) |
579 | { |
580 | char buf[Ctlsize]; |
581 | char *p; |
582 | uchar *a; |
583 | int fd; |
584 | int n, port; |
585 | |
586 | a = addr; |
587 | switch(sock->family){ |
588 | case AF_UNIX: |
589 | if(len < sock->naddr) |
590 | break; |
591 | memmove(a, sock->addr, sock->naddr); |
592 | return sock->naddr; |
593 | case AF_INET: |
594 | case AF_INET6: |
595 | snprint(buf, sizeof(buf), "%s/%s", sock->name, remote?"remote":"local"); |
596 | if((fd = open(buf, OREAD)) < 0) |
597 | return mkerror(); |
598 | if((n = read(fd, buf, sizeof(buf)-1)) < 0){ |
599 | close(fd); |
600 | return mkerror(); |
601 | } |
602 | close(fd); |
603 | if(n > 0 && buf[n-1] == '\n') |
604 | n--; |
605 | buf[n] = 0; |
606 | break; |
607 | default: |
608 | return -EINVAL; |
609 | } |
610 | |
611 | if((p = strrchr(buf, '!')) == nil) |
612 | return -EINVAL; |
613 | *p++ = 0; |
614 | port = atoi(p); |
615 | |
616 | trace("getsockaddr(): ip=%s port=%d", buf, port); |
617 | |
618 | switch(sock->family){ |
619 | case AF_INET: |
620 | if(len < 8) |
621 | break; |
622 | if(len > 16) |
623 | len = 16; |
624 | memset(a, 0, len); |
625 | a[0] = sock->family & 0xFF; |
626 | a[1] = (sock->family>>8) & 0xFF; |
627 | a[2] = (port >> 8) & 0xFF; |
628 | a[3] = port & 0xFF; |
629 | if(strtoip(buf, &a[4], 4) < 0) |
630 | break; |
631 | return len; |
632 | |
633 | case AF_INET6: |
634 | /* TODO */ |
635 | break; |
636 | } |
637 | |
638 | return -EINVAL; |
639 | } |
640 | |
641 | static void |
642 | listenproc(void *aux) |
643 | { |
644 | Listenproc *lp; |
645 | Socket *sock, *q; |
646 | char buf[Ctlsize], tmp[8+Ctlsize+1]; |
647 | int cfd, fd, n; |
648 | |
649 | lp = (Listenproc*)aux; |
650 | qlock(lp); |
651 | if((sock = lp->sock) == nil) |
652 | goto out; |
653 | |
654 | snprint(buf, sizeof(buf), "listenproc() %s", lp->str); |
655 | setprocname(buf); |
656 | |
657 | for(;;){ |
658 | n = 0; |
659 | cfd = -1; |
660 | switch(sock->family){ |
661 | case AF_UNIX: |
662 | srvunixsock(sock->other, lp->str); |
663 | close(sock->other); |
664 | sock->other = -1; |
665 | fd = sock->fd; |
666 | qunlock(lp); |
667 | n = read(fd, buf, sizeof(buf)-1); |
668 | qlock(lp); |
669 | break; |
670 | |
671 | default: |
672 | snprint(buf, sizeof(buf), "%s/listen", sock->name); |
673 | qunlock(lp); |
674 | if((cfd = open(buf, ORDWR)) >= 0) |
675 | n = read(cfd, buf, sizeof(buf)-1); |
676 | qlock(lp); |
677 | if(n <= 0) |
678 | close(cfd); |
679 | } |
680 | if(n <= 0) |
681 | break; |
682 | buf[n] = 0; |
683 | |
684 | if((sock = lp->sock) == nil){ |
685 | close(cfd); |
686 | break; |
687 | } |
688 | |
689 | switch(sock->family){ |
690 | case AF_UNIX: |
691 | srvname(tmp, buf, sizeof(tmp)); |
692 | if((fd = open(tmp, ORDWR)) < 0) |
693 | break; |
694 | unsrvunixsock(buf); |
695 | if(write(fd, buf, strlen(buf)) != strlen(buf)){ |
696 | close(fd); |
697 | fd = -1; |
698 | } |
699 | buf[0] = 0; |
700 | break; |
701 | |
702 | default: |
703 | n = atoi(buf); |
704 | snprint(buf, sizeof(buf), "%s/%d", sock->net, n); |
705 | snprint(tmp, sizeof(tmp), "%s/data", buf); |
706 | fd = open(tmp, ORDWR); |
707 | close(cfd); |
708 | break; |
709 | } |
710 | |
711 | if(fd < 0) |
712 | continue; |
713 | |
714 | q = allocsock(sock->family, sock->stype, sock->protocol); |
715 | strncpy(q->net, sock->net, sizeof(q->net)); |
716 | strncpy(q->name, buf, sizeof(q->name)); |
717 | |
718 | if(sock->family == AF_UNIX){ |
719 | memmove(q->addr, sock->addr, q->naddr = sock->naddr); |
720 | } else { |
721 | q->naddr = getsockaddr(q, 0, q->addr, sizeof(q->addr)); |
722 | } |
723 | |
724 | q->fd = fd; |
725 | q->connected = 1; |
726 | q->next = lp->q; |
727 | lp->q = q; |
728 | wakeq(&lp->wq, MAXPROC); |
729 | } |
730 | |
731 | if(sock->family == AF_UNIX) |
732 | unsrvunixsock(lp->str); |
733 | out: |
734 | wakeq(&lp->wq, MAXPROC); |
735 | qunlock(lp); |
736 | freelistenproc(lp); |
737 | } |
738 | |
739 | |
740 | static int |
741 | listensock(Socket *sock) |
742 | { |
743 | Listenproc *lp; |
744 | int pid, err; |
745 | char buf[Ctlsize]; |
746 | |
747 | trace("listensock()"); |
748 | |
749 | if(sock->listenproc) |
750 | return 0; |
751 | if((err = sockaddr2str(sock, sock->addr, sock->naddr, buf, sizeof(buf))) < 0) |
752 | return err; |
753 | |
754 | lp = kmallocz(sizeof(*lp), 1); |
755 | lp->ref = 2; |
756 | lp->sock = sock; |
757 | strncpy(lp->str, buf, sizeof(lp->str)); |
758 | |
759 | qlock(lp); |
760 | if((pid = procfork(listenproc, lp, 0)) < 0){ |
761 | qunlock(lp); |
762 | free(lp); |
763 | return mkerror(); |
764 | } |
765 | snprint(buf, sizeof(buf), "/proc/%d/note", pid); |
766 | lp->notefd = open(buf, OWRITE); |
767 | sock->listenproc = lp; |
768 | qunlock(lp); |
769 | |
770 | return 0; |
771 | } |
772 | |
773 | static int |
774 | getsockname(Socket *sock, uchar *addr, int *paddrlen) |
775 | { |
776 | int ret; |
777 | |
778 | trace("getsockname(%p, %p, %p (%x))", sock, addr, paddrlen, paddrlen ? *paddrlen : 0); |
779 | |
780 | if(addr == nil || paddrlen == nil) |
781 | return -EINVAL; |
782 | |
783 | ret = sock->naddr; |
784 | memmove(addr, sock->addr, ret); |
785 | *paddrlen = ret; |
786 | |
787 | return ret; |
788 | } |
789 | |
790 | static int |
791 | getpeername(Socket *sock, uchar *addr, int *paddrlen) |
792 | { |
793 | int ret; |
794 | |
795 | trace("getpeername(%p, %p, %p (%x))", sock, addr, paddrlen, paddrlen ? *paddrlen : 0); |
796 | |
797 | if(addr == nil || paddrlen == nil) |
798 | return -EINVAL; |
799 | |
800 | if((ret = getsockaddr(sock, 1, addr, *paddrlen)) > 0) |
801 | *paddrlen = ret; |
802 | return ret; |
803 | } |
804 | |
805 | static int |
806 | acceptsock(Socket *sock, uchar *addr, int *paddrlen) |
807 | { |
808 | Listenproc *lp; |
809 | Socket *nsock; |
810 | int err; |
811 | |
812 | trace("acceptsock(%p, %p, %p (%x))", sock, addr, paddrlen, paddrlen ? *paddrlen : 0); |
813 | |
814 | if((lp = sock->listenproc) == nil) |
815 | return -EINVAL; |
816 | |
817 | qlock(lp); |
818 | for(;;){ |
819 | if(nsock = lp->q){ |
820 | lp->q = nsock->next; |
821 | nsock->next = nil; |
822 | qunlock(lp); |
823 | |
824 | if(addr != nil && paddrlen != nil){ |
825 | err = getsockaddr(nsock, 1, addr, *paddrlen); |
826 | *paddrlen = err < 0 ? 0 : err; |
827 | } |
828 | return newfd(nsock, FD_CLOEXEC); |
829 | } |
830 | |
831 | if(sock->mode & O_NONBLOCK){ |
832 | err = -EAGAIN; |
833 | break; |
834 | } |
835 | |
836 | if((err = sleepq(&lp->wq, lp, 1)) < 0) |
837 | break; |
838 | } |
839 | qunlock(lp); |
840 | |
841 | return err; |
842 | } |
843 | |
844 | static int |
845 | socketpair(int family, int stype, int protocol, int sv[2]) |
846 | { |
847 | Socket *sock; |
848 | int p[2]; |
849 | int i, fd; |
850 | |
851 | trace("socketpair(%d, %d, %d, %p)", family, stype, protocol, sv); |
852 | |
853 | if(family != AF_UNIX) |
854 | return -EAFNOSUPPORT; |
855 | if(pipe(p) < 0) |
856 | return mkerror(); |
857 | for(i=0; i<2; i++){ |
858 | sock = allocsock(family, stype, protocol); |
859 | sock->fd = p[i]; |
860 | sock->connected = 1; |
861 | if((fd = newfd(sock, FD_CLOEXEC)) < 0){ |
862 | if(i > 0) |
863 | sys_close(sv[0]); |
864 | close(p[0]); |
865 | close(p[1]); |
866 | return fd; |
867 | } |
868 | sv[i] = fd; |
869 | } |
870 | return 0; |
871 | } |
872 | |
873 | static void* |
874 | bufprocsock(Socket *sock) |
875 | { |
876 | if(sock->bufproc == nil) |
877 | sock->bufproc = newbufproc(sock->fd); |
878 | return sock->bufproc; |
879 | } |
880 | |
881 | static int |
882 | pollsock(Ufile *file, void *tab) |
883 | { |
884 | Socket *sock = (Socket*)file; |
885 | Listenproc *lp; |
886 | Connectproc *cp; |
887 | |
888 | if(!sock->connected){ |
889 | if(lp = sock->listenproc){ |
890 | qlock(lp); |
891 | pollwait(file, &lp->wq, tab); |
892 | if(lp->q){ |
893 | qunlock(lp); |
894 | return POLLIN; |
895 | } |
896 | qunlock(lp); |
897 | } |
898 | if(cp = sock->connectproc){ |
899 | qlock(cp); |
900 | pollwait(file, &cp->wq, tab); |
901 | if(sock->error < 0){ |
902 | qunlock(cp); |
903 | return POLLOUT; |
904 | } |
905 | qunlock(cp); |
906 | } |
907 | return 0; |
908 | } |
909 | |
910 | return pollbufproc(bufprocsock(sock), sock, tab); |
911 | } |
912 | |
913 | static int |
914 | readsock(Ufile *file, void *buf, int len, vlong) |
915 | { |
916 | Socket *sock = (Socket*)file; |
917 | int ret; |
918 | |
919 | if(!sock->connected) |
920 | return -ENOTCONN; |
921 | if((sock->mode & O_NONBLOCK) || (sock->bufproc != nil)){ |
922 | ret = readbufproc(bufprocsock(sock), buf, len, 0, (sock->mode & O_NONBLOCK)); |
923 | } else { |
924 | if(notifyme(1)) |
925 | return -ERESTART; |
926 | ret = read(sock->fd, buf, len); |
927 | notifyme(0); |
928 | if(ret < 0) |
929 | ret = mkerror(); |
930 | } |
931 | return ret; |
932 | } |
933 | |
934 | extern int pipewrite(int fd, void *buf, int len); |
935 | |
936 | static int |
937 | writesock(Ufile *file, void *buf, int len, vlong) |
938 | { |
939 | Socket *sock = (Socket*)file; |
940 | int ret; |
941 | |
942 | if(!sock->connected) |
943 | return -ENOTCONN; |
944 | if(sock->family == AF_UNIX) |
945 | return pipewrite(sock->fd, buf, len); |
946 | if(notifyme(1)) |
947 | return -ERESTART; |
948 | ret = write(sock->fd, buf, len); |
949 | notifyme(0); |
950 | if(ret < 0) |
951 | ret = mkerror(); |
952 | return ret; |
953 | } |
954 | |
955 | static int |
956 | ioctlsock(Ufile *file, int cmd, void *arg) |
957 | { |
958 | Socket *sock = (Socket*)file; |
959 | |
960 | switch(cmd){ |
961 | default: |
962 | return -ENOTTY; |
963 | case 0x541B: |
964 | { |
965 | int r; |
966 | |
967 | if(arg == nil) |
968 | return -EINVAL; |
969 | if((r = nreadablebufproc(bufprocsock(sock))) < 0){ |
970 | *((int*)arg) = 0; |
971 | return r; |
972 | } |
973 | *((int*)arg) = r; |
974 | } |
975 | return 0; |
976 | } |
977 | } |
978 | |
979 | static int |
980 | sendto(Socket *sock, void *data, int len, int, uchar *, int) |
981 | { |
982 | trace("sendto(%p, %p, %d, ...)", sock, data, len); |
983 | |
984 | return writesock(sock, data, len, sock->off); |
985 | } |
986 | |
987 | static int |
988 | recvfrom(Socket *sock, void *data, int len, int flags, uchar *addr, int addrlen) |
989 | { |
990 | int ret; |
991 | |
992 | trace("recvfrom(%p, %p, %d, %x, %p, %d)", sock, data, len, flags, addr, addrlen); |
993 | |
994 | if(flags & 2){ |
995 | if(!sock->connected) |
996 | return -ENOTCONN; |
997 | ret = readbufproc(bufprocsock(sock), data, len, 1, 1); |
998 | } else { |
999 | ret = readsock(sock, data, len, sock->off); |
1000 | } |
1001 | if(addr){ |
1002 | memmove(addr, sock->addr, sock->naddr); |
1003 | } |
1004 | return ret; |
1005 | } |
1006 | |
1007 | enum { |
1008 | SOL_SOCKET = 1, |
1009 | |
1010 | SO_DEBUG = 1, |
1011 | SO_REUSEADDR, |
1012 | SO_TYPE, |
1013 | SO_ERROR, |
1014 | }; |
1015 | |
1016 | static int |
1017 | getoptsock(Socket *sock, int lvl, int opt, char *ov, int *ol) |
1018 | { |
1019 | trace("getoptsock(%p, %d, %d, %p, %p)", sock, lvl, opt, ov, ol); |
1020 | |
1021 | switch(lvl){ |
1022 | default: |
1023 | Default: |
1024 | return -EINVAL; |
1025 | |
1026 | case SOL_SOCKET: |
1027 | switch(opt){ |
1028 | default: |
1029 | goto Default; |
1030 | case SO_ERROR: |
1031 | *ol = sizeof(int); |
1032 | *((int*)ov) = sock->error; |
1033 | break; |
1034 | } |
1035 | break; |
1036 | } |
1037 | |
1038 | return 0; |
1039 | } |
1040 | |
1041 | enum { |
1042 | SYS_SOCKET=1, |
1043 | SYS_BIND, |
1044 | SYS_CONNECT, |
1045 | SYS_LISTEN, |
1046 | SYS_ACCEPT, |
1047 | SYS_GETSOCKNAME, |
1048 | SYS_GETPEERNAME, |
1049 | SYS_SOCKETPAIR, |
1050 | SYS_SEND, |
1051 | SYS_RECV, |
1052 | SYS_SENDTO, |
1053 | SYS_RECVFROM, |
1054 | SYS_SHUTDOWN, |
1055 | SYS_SETSOCKOPT, |
1056 | SYS_GETSOCKOPT, |
1057 | SYS_SENDMSG, |
1058 | SYS_RECVMSG, |
1059 | }; |
1060 | |
1061 | int sys_linux_socketcall(int call, int *arg) |
1062 | { |
1063 | Socket *sock; |
1064 | int ret; |
1065 | |
1066 | trace("sys_linux_socketcall(%d, %p)", call, arg); |
1067 | |
1068 | if(call == SYS_SOCKET) |
1069 | return newsock(arg[0], arg[1], arg[2]); |
1070 | |
1071 | if(call == SYS_SOCKETPAIR) |
1072 | return socketpair(arg[0], arg[1], arg[2], (int*)arg[3]); |
1073 | |
1074 | if((sock = (Socket*)fdgetfile(arg[0])) == nil) |
1075 | return -EBADF; |
1076 | |
1077 | if(sock->dev != SOCKDEV){ |
1078 | putfile(sock); |
1079 | return -ENOTSOCK; |
1080 | } |
1081 | |
1082 | ret = -1; |
1083 | switch(call){ |
1084 | case SYS_CONNECT: |
1085 | ret = connectsock(sock, (void*)arg[1], arg[2]); |
1086 | break; |
1087 | case SYS_SENDTO: |
1088 | ret = sendto(sock, (void*)arg[1], arg[2], arg[3], (void*)arg[4], arg[5]); |
1089 | break; |
1090 | case SYS_RECVFROM: |
1091 | ret = recvfrom(sock, (void*)arg[1], arg[2], arg[3], (void*)arg[4], arg[5]); |
1092 | break; |
1093 | case SYS_SEND: |
1094 | ret = sendto(sock, (void*)arg[1], arg[2], arg[3], nil, 0); |
1095 | break; |
1096 | case SYS_RECV: |
1097 | ret = recvfrom(sock, (void*)arg[1], arg[2], arg[3], nil, 0); |
1098 | break; |
1099 | case SYS_GETSOCKNAME: |
1100 | ret = getsockname(sock, (void*)arg[1], (void*)arg[2]); |
1101 | break; |
1102 | case SYS_GETPEERNAME: |
1103 | ret = getpeername(sock, (void*)arg[1], (void*)arg[2]); |
1104 | break; |
1105 | case SYS_SHUTDOWN: |
1106 | ret = shutdownsock(sock, arg[1]); |
1107 | break; |
1108 | case SYS_BIND: |
1109 | ret = bindsock(sock, (void*)arg[1], arg[2]); |
1110 | break; |
1111 | case SYS_LISTEN: |
1112 | ret = listensock(sock); |
1113 | break; |
1114 | case SYS_ACCEPT: |
1115 | ret = acceptsock(sock, (void*)arg[1], (void*)arg[2]); |
1116 | break; |
1117 | case SYS_SETSOCKOPT: |
1118 | ret = 0; |
1119 | break; |
1120 | case SYS_GETSOCKOPT: |
1121 | ret = getoptsock(sock, (int)arg[1], (int)arg[2], (char*)arg[3], (int*)arg[4]); |
1122 | break; |
1123 | case SYS_SENDMSG: |
1124 | case SYS_RECVMSG: |
1125 | default: |
1126 | trace("socketcall(): call %d not implemented", call); |
1127 | } |
1128 | |
1129 | putfile(sock); |
1130 | |
1131 | return ret; |
1132 | } |
1133 | |
1134 | static void |
1135 | fillstat(Ustat *s) |
1136 | { |
1137 | s->mode = 0666 | S_IFSOCK; |
1138 | s->uid = current->uid; |
1139 | s->gid = current->gid; |
1140 | s->size = 0; |
1141 | } |
1142 | |
1143 | static int |
1144 | fstatsock(Ufile *, Ustat *s) |
1145 | { |
1146 | fillstat(s); |
1147 | return 0; |
1148 | }; |
1149 | |
1150 | static Udev sockdev = |
1151 | { |
1152 | .read = readsock, |
1153 | .write = writesock, |
1154 | .poll = pollsock, |
1155 | .close = closesock, |
1156 | .ioctl = ioctlsock, |
1157 | .fstat = fstatsock, |
1158 | }; |
1159 | |
1160 | void sockdevinit(void) |
1161 | { |
1162 | devtab[SOCKDEV] = &sockdev; |
1163 | } |