int socket(int domain, int stype, int protocol) { Rock *r; int cfd, fd, n; int pfd[2]; char *net; switch(domain){ case PF_INET: case PF_INET6: /* get a free network directory */ switch(stype){ case SOCK_DGRAM: net = "udp"; cfd = open("/net/udp/clone", O_RDWR); break; case SOCK_STREAM: net = "tcp"; cfd = open("/net/tcp/clone", O_RDWR); break; case SOCK_RDM: net = "il"; cfd = open("/net/il/clone", O_RDWR); break; default: errno = EPROTONOSUPPORT; return -1; } if(cfd < 0) return -1; return _sock_data(cfd, net, domain, stype, protocol, 0); case PF_UNIX: if(pipe(pfd) < 0) return -1; r = _sock_newrock(pfd[0]); if(r == 0){ close(pfd[0]); close(pfd[1]); errno = ENOBUFS; return -1; } r->domain = domain; r->stype = stype; r->protocol = protocol; r->other = pfd[1]; return pfd[0]; default: errno = EPROTONOSUPPORT; return -1; } }
int _sock_data(int cfd, char *net, int domain, int stype, int protocol, Rock **rp) { int n, fd; Rock *r; char name[Ctlsize]; /* get the data file name */ n = read(cfd, name, sizeof(name)-1); if(n < 0){ close(cfd); errno = ENOBUFS; return -1; } name[n] = 0; n = strtoul(name, 0, 0); snprintf(name, sizeof name, "/net/%s/%d/data", net, n); /* open data file */ fd = open(name, O_RDWR); close(cfd); if(fd < 0){ close(cfd); errno = ENOBUFS; return -1; } /* hide stuff under the rock */ snprintf(name, sizeof name, "/net/%s/%d/ctl", net, n); r = _sock_newrock(fd); if(r == 0){ errno = ENOBUFS; close(fd); return -1; } if(rp) *rp = r; memset(&r->raddr, 0, sizeof(r->raddr)); memset(&r->addr, 0, sizeof(r->addr)); r->domain = domain; r->stype = stype; r->protocol = protocol; strcpy(r->ctl, name); return fd; }
int __libc_accept4(int fd, __SOCKADDR_ARG addr, socklen_t *alen, int a4_flags) { int nfd, lcfd; socklen_t n; Rock *r, *nr; struct sockaddr_in *ip; char name[Ctlsize]; char file[8 + Ctlsize + 1]; const char *net = 0; char listen[Ctlsize]; int open_flags; r = _sock_findrock(fd, 0); if (r == 0) { errno = ENOTSOCK; return -1; } switch (r->domain) { case PF_INET: switch (r->stype) { case SOCK_DGRAM: net = "udp"; break; case SOCK_STREAM: net = "tcp"; break; } /* at this point, our FD is for the data file. we need to open * the listen file. */ _sock_get_conv_filename(r, "listen", listen); open_flags = O_RDWR; /* This is for the listen - maybe don't block on open */ open_flags |= (r->sopts & SOCK_NONBLOCK ? O_NONBLOCK : 0); /* This is for the ctl we get back - maybe CLOEXEC, based on * what accept4 wants for the child */ open_flags |= (a4_flags & SOCK_CLOEXEC ? O_CLOEXEC : 0); lcfd = open(listen, open_flags); if (lcfd < 0) return -1; /* at this point, we have a new conversation, and lcfd is its * ctl fd. nfd will be the FD for that conv's data file. * sock_data will store our lcfd in the rock and return the data * file fd. * * Note, we pass the listen socket's stype, but not it's sopts. * The sopts (e.g. SOCK_NONBLOCK) apply to the original socket, * not to the new one. Instead, we pass the accept4 flags, * which are the sopts for the new socket. Note that this is * just the sopts. Both the listen socket and the new socket * have the same stype. */ nfd = _sock_data(lcfd, net, r->domain, a4_flags | r->stype, r->protocol, &nr); if (nfd < 0) return -1; /* get remote address */ ip = (struct sockaddr_in *)&nr->raddr; _sock_ingetaddr(nr, ip, &n, "remote"); if (addr.__sockaddr__) { memmove(addr.__sockaddr_in__, ip, sizeof(struct sockaddr_in)); *alen = sizeof(struct sockaddr_in); } return nfd; case PF_UNIX: if (r->other >= 0) { errno = EINVAL; // was EGREG return -1; } for (;;) { /* read path to new connection */ n = read(fd, name, sizeof(name) - 1); if (n < 0) return -1; if (n == 0) continue; name[n] = 0; /* open new connection */ _sock_srvname(file, name); open_flags = O_RDWR; /* This is for the listen - maybe don't block on open */ open_flags |= (r->sopts & SOCK_NONBLOCK ? O_NONBLOCK : 0); /* This is for the ctl we get back - maybe CLOEXEC, * based on what accept4 wants for the child */ open_flags |= (a4_flags & SOCK_CLOEXEC ? O_CLOEXEC : 0); nfd = open(file, open_flags); if (nfd < 0) continue; /* confirm opening on new connection */ if (write(nfd, name, strlen(name)) > 0) break; close(nfd); } nr = _sock_newrock(nfd); if (nr == 0) { close(nfd); return -1; } nr->domain = r->domain; nr->stype = r->stype; nr->sopts = a4_flags; nr->protocol = r->protocol; return nfd; default: errno = EOPNOTSUPP; return -1; } }
/* Create a new socket of type TYPE in domain DOMAIN, using protocol PROTOCOL. If PROTOCOL is zero, one is chosen automatically. Returns a file descriptor for the new socket, or -1 for errors. */ int __socket(int domain, int type, int protocol) { Rock *r; int cfd, n; int open_flags = O_RDWR; int pfd[2]; char *net; char msg[128]; static struct close_cb _sock_close_cb = {.func = _sock_fd_closed}; run_once(register_close_cb(&_sock_close_cb)); switch (domain) { case PF_INET: open_flags |= (type & SOCK_NONBLOCK ? O_NONBLOCK : 0); /* get a free network directory */ switch (_sock_strip_opts(type)) { case SOCK_DGRAM: net = "udp"; cfd = open("/net/udp/clone", open_flags); /* All BSD UDP sockets are in 'headers' mode, where each * packet has the remote addr:port, local addr:port and * other info. */ if (!(cfd < 0)) { n = snprintf(msg, sizeof(msg), "headers"); n = write(cfd, msg, n); if (n < 0) { perror("UDP socket headers failed"); return -1; } if (lseek(cfd, 0, SEEK_SET) != 0) { perror("UDP socket seek failed"); return -1; } } break; case SOCK_STREAM: net = "tcp"; cfd = open("/net/tcp/clone", open_flags); break; default: errno = EPROTONOSUPPORT; return -1; } if (cfd < 0) { return -1; } return _sock_data(cfd, net, domain, type, protocol, 0); case PF_UNIX: if (pipe(pfd) < 0) { return -1; } r = _sock_newrock(pfd[0]); r->domain = domain; r->stype = _sock_strip_opts(type); r->sopts = _sock_get_opts(type); r->protocol = protocol; r->other = pfd[1]; return pfd[0]; default: errno = EPROTONOSUPPORT; return -1; } }
int accept(int fd, void *a, int *alen) { int n, nfd, cfd; Rock *r, *nr; struct sockaddr_in *ip; char name[Ctlsize]; char file[8+Ctlsize+1]; char *net; r = _sock_findrock(fd, 0); if(r == 0){ errno = ENOTSOCK; return -1; } switch(r->domain){ case PF_INET: switch(r->stype){ case SOCK_DGRAM: net = "udp"; break; case SOCK_STREAM: net = "tcp"; break; default: net = "gok"; break; } /* get control file name from listener process */ n = read(fd, name, sizeof(name)-1); if(n <= 0){ _syserrno(); return -1; } name[n] = 0; cfd = open(name, O_RDWR); if(cfd < 0){ _syserrno(); return -1; } nfd = _sock_data(cfd, net, r->domain, r->stype, r->protocol, &nr); if(nfd < 0){ _syserrno(); return -1; } if(write(fd, "OK", 2) < 0){ close(nfd); _syserrno(); return -1; } /* get remote address */ ip = (struct sockaddr_in*)&nr->raddr; _sock_ingetaddr(nr, ip, &n, "remote"); if(a){ memmove(a, ip, sizeof(struct sockaddr_in)); *alen = sizeof(struct sockaddr_in); } return nfd; case PF_UNIX: if(r->other >= 0){ errno = EGREG; return -1; } for(;;){ /* read path to new connection */ n = read(fd, name, sizeof(name) - 1); if(n < 0) return -1; if(n == 0) continue; name[n] = 0; /* open new connection */ _sock_srvname(file, name); nfd = open(file, O_RDWR); if(nfd < 0) continue; /* confirm opening on new connection */ if(write(nfd, name, strlen(name)) > 0) break; close(nfd); } nr = _sock_newrock(nfd); if(nr == 0){ close(nfd); return -1; } nr->domain = r->domain; nr->stype = r->stype; nr->protocol = r->protocol; return nfd; default: errno = EOPNOTSUPP; return -1; } }