int getpeername(int fd, void *addr, int *alen) { Rock *r; int i; struct sockaddr_in *rip; struct sockaddr_un *runix; r = _sock_findrock(fd, 0); if(r == 0){ errno = ENOTSOCK; return -1; } switch(r->domain){ case PF_INET: rip = (struct sockaddr_in*)&r->raddr; memmove(addr, rip, sizeof(struct sockaddr_in)); *alen = sizeof(struct sockaddr_in); break; case PF_UNIX: runix = (struct sockaddr_un*)&r->raddr; i = &runix->sun_path[strlen(runix->sun_path)] - (char*)runix; memmove(addr, runix, i); *alen = i; break; default: errno = EAFNOSUPPORT; return -1; } return 0; }
Rock *_sock_newrock(int fd) { Rock *r; struct stat d; fstat(fd, &d); r = _sock_findrock(fd, &d); if (r == 0) { r = malloc(sizeof(Rock)); if (r == 0) return 0; r->dev = d.st_dev; r->inode = d.st_ino; /* TODO: this is not thread-safe! */ r->next = _sock_rock; _sock_rock = r; } assert(r->dev == d.st_dev); assert(r->inode == d.st_ino); r->domain = 0; r->stype = 0; r->sopts = 0; r->protocol = 0; memset(&r->addr, 0, sizeof(r->addr_stor)); r->reserved = 0; memset(&r->raddr, 0, sizeof(r->raddr_stor)); r->ctl[0] = '\0'; r->other = -1; r->is_listener = FALSE; r->listen_fd = -1; return r; }
Rock* _sock_newrock(int fd) { Rock *r; struct stat d; r = _sock_findrock(fd, &d); if(r == 0){ r = malloc(sizeof(Rock)); if(r == 0) return 0; r->dev = d.st_dev; r->inode = d.st_ino; r->other = -1; r->next = _sock_rock; _sock_rock = r; } memset(&r->raddr, 0, sizeof(r->raddr)); memset(&r->addr, 0, sizeof(r->addr)); r->reserved = 0; r->dev = d.st_dev; r->inode = d.st_ino; r->other = -1; return r; }
int getsockname(int fd, struct sockaddr *addr, int *alen) { Rock *r; int i; struct sockaddr_in *lip; struct sockaddr_un *lunix; r = _sock_findrock(fd, 0); if(r == 0){ errno = ENOTSOCK; return -1; } switch(r->domain){ case PF_INET: lip = (struct sockaddr_in*)addr; _sock_ingetaddr(r, lip, alen, "local"); break; case PF_UNIX: lunix = (struct sockaddr_un*)&r->addr; i = &lunix->sun_path[strlen(lunix->sun_path)] - (char*)lunix; memmove(addr, lunix, i); *alen = i; break; default: errno = EAFNOSUPPORT; return -1; } return 0; }
/* Used by user/iplib (e.g. epoll). Opens and returns the FD for the * conversation's listen fd, which the caller needs to close. Returns -1 if the * FD is not a listener. */ int _sock_get_listen_fd(int sock_fd) { char listen_file[Ctlsize + 3]; char *x, *last_ctl; Rock *r = _sock_findrock(sock_fd, 0); int ret; if (!r) return -1; if (!r->is_listener) return -1; /* We want an FD for the "listen" file. This is for epoll. We * could optimize and only do this on demand, but whatever. */ strncpy(listen_file, r->ctl, sizeof(listen_file)); /* We want the conversation directory. We can find the last "ctl" * in the CTL name (they could have mounted at /ctlfoo/net/) */ x = listen_file; do { last_ctl = x; x++; /* move forward enough to not find the same "ctl" */ x = strstr(x, "ctl"); } while (x); /* last_ctl is either listen_file (if we never found ctl, which should never * happen) or it points at the 'c' in the last "ctl". */ assert(last_ctl != listen_file); strcpy(last_ctl, "listen"); ret = open(listen_file, O_PATH); /* Probably a bug in the rock code (or the kernel!) if we couldn't walk to * our listen. */ assert(ret >= 0); r->listen_fd = ret; return ret; }
/* If fd is a nonblocking socket, this returns O_NONBLOCK */ static int get_nonblock_status(int fd) { Rock *r = _sock_findrock(fd, 0); if (!r) return 0; return r->sopts & SOCK_NONBLOCK ? O_NONBLOCK : 0; }
int issocket(int fd) { Rock *r; r = _sock_findrock(fd, 0); return (r != 0); }
void _sock_fd_closed(int fd) { Rock *r = _sock_findrock(fd, 0); if (!r) return; if (r->is_listener) close(r->listen_fd); }
/* Give the socket FD the local address ADDR (which is LEN bytes long). */ int __bind(int fd, __CONST_SOCKADDR_ARG addr, socklen_t alen) { int n, cfd; socklen_t len; Rock *r; char msg[128]; struct sockaddr_in *lip; /* assign the address */ r = _sock_findrock(fd, 0); if (r == 0) { errno = ENOTSOCK; return -1; } if (alen > sizeof(r->addr_stor)) { errno = ENAMETOOLONG; return -1; } memmove(&r->addr, addr.__sockaddr__, alen); /* the rest is IP sepecific */ if (r->domain != PF_INET) return 0; cfd = open(r->ctl, O_RDWR); if (cfd < 0) { errno = EBADF; return -1; } lip = (struct sockaddr_in *)&r->addr; if (lip->sin_port > 0) snprintf(msg, sizeof msg, "bind %d", ntohs(lip->sin_port)); else strcpy(msg, "bind *"); n = write(cfd, msg, strlen(msg)); if (n < 0) { errno = EOPNOTSUPP; /* Improve error reporting!!! */ close(cfd); return -1; } if (lip->sin_port <= 0) _sock_ingetaddr(r, lip, &len, "local"); /* UDP sockets are in headers mode, and need to be announced. This isn't a * full announce, in that the kernel UDP stack doesn't expect someone to * open the listen file or anything like that. */ if ((r->domain == PF_INET) && (r->stype == SOCK_DGRAM)) { n = snprintf(msg, sizeof(msg), "announce *!%d", ntohs(lip->sin_port)); n = write(cfd, msg, n); if (n < 0) { perror("bind-announce failed"); return -1; } } close(cfd); return 0; }
/* Used by user/iplib (e.g. epoll). Looks up the FD listen file for this * conversation. Returns -1 if the FD is not a listener. */ int _sock_lookup_listen_fd(int sock_fd) { Rock *r = _sock_findrock(sock_fd, 0); if (!r) return -1; if (!r->is_listener) return -1; return r->listen_fd; }
/* Returns a rock* if the socket exists and is UDP */ Rock *udp_sock_get_rock(int fd) { Rock *r = _sock_findrock(fd, 0); if (!r) { errno = ENOTSOCK; return 0; } if ((r->domain == PF_INET) && (r->stype == SOCK_DGRAM)) return r; else return 0; }
/* Sets the fd's nonblock status IAW arg's settings. Returns 0 on success. * Modifies *arg such that the new version is the one that should be passed to * the kernel for setfl. */ static int set_nonblock_status(int fd, int *arg) { int ret = 0; Rock *r = _sock_findrock(fd, 0); if (!r) { /* We don't clear O_NONBLOCK, so non-sockets will still pass it to the * kernel and probably error out */ return 0; } /* XOR of two flag tests, checking if we need to change anything */ if (!(r->sopts & SOCK_NONBLOCK) != !(*arg & O_NONBLOCK)) ret = toggle_nonblock(r); /* Either way, we clear O_NONBLOCK, so we don't ask the kernel to set it */ *arg &= ~O_NONBLOCK; return ret; }
int __setsockopt(int sockfd, int level, int optname, const __ptr_t __optval, socklen_t optlen) { Rock *r = _sock_findrock(sockfd, 0); void *optval = (void*)__optval; if (!r) { /* could be EBADF too, we can't tell */ __set_errno(ENOTSOCK); return -1; } switch (level) { case (SOL_SOCKET): return sol_socket_sso(r, optname, optval, optlen); default: __set_errno(ENOPROTOOPT); return -1; }; }
int rcmd(char **dst, int port, char *luser, char *ruser, char *cmd, int *fd2p) { char c; int i, fd, lfd, fd2, port2; struct hostent *h; Rock *r; struct sockaddr_in in; char buf[128]; void (*x)(int); h = gethostbyname(*dst); if(h == 0) return -1; *dst = h->h_name; /* connect using a reserved tcp port */ fd = socket(PF_INET, SOCK_STREAM, 0); if(fd < 0) return -1; r = _sock_findrock(fd, 0); if(r == 0){ errno = ENOTSOCK; return -1; } r->reserved = 1; in.sin_family = AF_INET; in.sin_port = htons(port); memmove(&in.sin_addr, h->h_addr_list[0], sizeof(in.sin_addr)); if(connect(fd, &in, sizeof(in)) < 0){ close(fd); return -1; } /* error stream */ if(fd2p){ /* create an error stream and wait for a call in */ for(i = 0; i < 10; i++){ lfd = rresvport(&port2); if(lfd < 0) continue; if(listen(lfd, 1) == 0) break; close(lfd); } if(i >= 10){ fprintf(stderr, pbotch); return -1; } snprintf(buf, sizeof buf, "%d", port2); if(write(fd, buf, strlen(buf)+1) < 0){ close(fd); close(lfd); fprintf(stderr, lbotch); return -1; } } else { if(write(fd, "", 1) < 0){ fprintf(stderr, pbotch); return -1; } } /* pass id's and command */ if(write(fd, luser, strlen(luser)+1) < 0 || write(fd, ruser, strlen(ruser)+1) < 0 || write(fd, cmd, strlen(cmd)+1) < 0){ if(fd2p) close(fd2); fprintf(stderr, pbotch); return -1; } if(fd2p){ x = signal(SIGALRM, ding); alarm(15); fd2 = accept(lfd, &in, &i); alarm(0); close(lfd); signal(SIGALRM, x); if(fd2 < 0){ close(fd); close(lfd); fprintf(stderr, lbotch); return -1; } *fd2p = fd2; } /* get reply */ if(read(fd, &c, 1) != 1){ if(fd2p) close(fd2); fprintf(stderr, pbotch); return -1; } if(c == 0) return fd; i = 0; while(c){ buf[i++] = c; if(read(fd, &c, 1) != 1) break; if(i >= sizeof(buf)-1) break; } buf[i] = 0; fprintf(stderr, "rcmd: %s\n", buf); close(fd); return -1; }
#include <sys/uio.h> #include <sys/socket.h> #include <netinet/in.h> #include <sys/un.h> #include "plan9_sockets.h" /* Put the local address of FD into *ADDR and its length in *LEN. */ int __getsockname(int fd, __SOCKADDR_ARG addr, socklen_t * __restrict alen) { Rock *r; int i; struct sockaddr_in *lip; struct sockaddr_un *lunix; r = _sock_findrock(fd, 0); if (r == 0) { errno = ENOTSOCK; return -1; } switch (r->domain) { case PF_INET: lip = addr.__sockaddr_in__; _sock_ingetaddr(r, lip, alen, "local"); break; case PF_UNIX: lunix = (struct sockaddr_un *)&r->addr; i = &lunix->sun_path[strlen(lunix->sun_path)] - (char *)lunix; memmove(addr.__sockaddr_un__, lunix, i); *alen = i;
int listen(int fd, int i) { Rock *r; int n, cfd; char msg[128]; struct sockaddr_in *lip; struct sockaddr_un *lunix; r = _sock_findrock(fd, 0); if(r == 0){ errno = ENOTSOCK; return -1; } switch(r->domain){ case PF_INET: cfd = open(r->ctl, O_RDWR); if(cfd < 0){ errno = EBADF; return -1; } lip = (struct sockaddr_in*)&r->addr; if(1 || lip->sin_port >= 0) { /* sin_port is unsigned */ if(write(cfd, "bind 0", 6) < 0) { errno = EGREG; close(cfd); return -1; } snprintf(msg, sizeof msg, "announce %d", ntohs(lip->sin_port)); } else strcpy(msg, "announce *"); n = write(cfd, msg, strlen(msg)); if(n < 0){ errno = EOPNOTSUPP; /* Improve error reporting!!! */ close(cfd); return -1; } close(cfd); return listenproc(r, fd); case PF_UNIX: if(r->other < 0){ errno = EGREG; return -1; } lunix = (struct sockaddr_un*)&r->addr; if(_sock_srv(lunix->sun_path, r->other) < 0){ _syserrno(); r->other = -1; return -1; } r->other = -1; return 0; default: errno = EAFNOSUPPORT; return -1; } }
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; } }
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; } }