|  | /* | 
|  | * This file is part of the UCB release of Plan 9. It is subject to the license | 
|  | * terms in the LICENSE file found in the top-level directory of this | 
|  | * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No | 
|  | * part of the UCB release of Plan 9, including this file, may be copied, | 
|  | * modified, propagated, or distributed except according to the terms contained | 
|  | * in the LICENSE file. | 
|  | */ | 
|  | /* posix */ | 
|  | #include <sys/types.h> | 
|  | #include <unistd.h> | 
|  | #include <stdlib.h> | 
|  | #include <stdio.h> | 
|  | #include <fcntl.h> | 
|  | #include <errno.h> | 
|  | #include <string.h> | 
|  | #include <assert.h> | 
|  | #include <ctype.h> | 
|  |  | 
|  | /* bsd extensions */ | 
|  | #include <sys/uio.h> | 
|  | #include <sys/socket.h> | 
|  | #include <netinet/in.h> | 
|  | #include <sys/un.h> | 
|  | #include <arpa/inet.h> | 
|  |  | 
|  | #include <sys/plan9_helpers.h> | 
|  |  | 
|  | /* Puts the path of the conversation file for 'name' for the Rock in retval, | 
|  | * which must be a char [Ctlsize]. */ | 
|  | void _sock_get_conv_filename(Rock *r, const char *name, char *retloc) | 
|  | { | 
|  | char *p; | 
|  |  | 
|  | strlcpy(retloc, r->ctl, Ctlsize); | 
|  | p = strrchr(retloc, '/'); | 
|  | assert(p); | 
|  | p++; | 
|  | *p = 0; | 
|  | strlcat(retloc, name, Ctlsize); | 
|  | } | 
|  |  | 
|  | void | 
|  | _sock_ingetaddr(Rock *r, struct sockaddr_in *ip, socklen_t *alen, | 
|  | const char *a) | 
|  | { | 
|  | int n, fd; | 
|  | char *p; | 
|  | char name[Ctlsize]; | 
|  | int open_flags; | 
|  |  | 
|  | /* get remote address */ | 
|  | _sock_get_conv_filename(r, a, name); | 
|  | open_flags = O_RDONLY; | 
|  | open_flags |= (r->sopts & SOCK_CLOEXEC ? O_CLOEXEC : 0); | 
|  | fd = open(name, open_flags); | 
|  | if (fd >= 0) { | 
|  | n = read(fd, name, sizeof(name) - 1); | 
|  | if (n > 0) { | 
|  | name[n] = 0; | 
|  | p = strchr(name, '!'); | 
|  | if (p) { | 
|  | *p++ = 0; | 
|  | ip->sin_family = AF_INET; | 
|  | ip->sin_port = htons(atoi(p)); | 
|  | ip->sin_addr.s_addr = inet_addr(name); | 
|  | if (alen) | 
|  | *alen = sizeof(struct sockaddr_in); | 
|  | } | 
|  | } | 
|  | close(fd); | 
|  | } | 
|  |  | 
|  | } | 
|  |  | 
|  | /* | 
|  | * return ndb attribute type of an ip name | 
|  | */ | 
|  | int _sock_ipattr(const char *name) | 
|  | { | 
|  | const char *p; | 
|  | int dot = 0; | 
|  | int alpha = 0; | 
|  |  | 
|  | for (p = name; *p; p++) { | 
|  | if (isdigit(*p)) | 
|  | continue; | 
|  | else if (isalpha(*p) || *p == '-') | 
|  | alpha = 1; | 
|  | else if (*p == '.') | 
|  | dot = 1; | 
|  | else | 
|  | return Tsys; | 
|  | } | 
|  |  | 
|  | if (alpha) { | 
|  | if (dot) | 
|  | return Tdom; | 
|  | else | 
|  | return Tsys; | 
|  | } | 
|  |  | 
|  | if (dot) | 
|  | return Tip; | 
|  | else | 
|  | return Tsys; | 
|  | } | 
|  |  | 
|  | /* we can't avoid overrunning npath because we don't know how big it is. */ | 
|  | void _sock_srvname(char *npath, char *path) | 
|  | { | 
|  | char *p; | 
|  |  | 
|  | strcpy(npath, "/srv/UD."); | 
|  | p = strrchr(path, '/'); | 
|  | if (p == 0) | 
|  | p = path; | 
|  | else | 
|  | p++; | 
|  | strcat(npath, p); | 
|  | } | 
|  |  | 
|  | int _sock_srv(char *path, int fd) | 
|  | { | 
|  | int sfd; | 
|  | char msg[8 + 256 + 1]; | 
|  |  | 
|  | /* change the path to something in srv */ | 
|  | _sock_srvname(msg, path); | 
|  |  | 
|  | /* remove any previous instance */ | 
|  | unlink(msg); | 
|  |  | 
|  | /* put the fd in /srv and then close it */ | 
|  | sfd = creat(msg, 0666); | 
|  | if (sfd < 0) { | 
|  | close(fd); | 
|  | return -1; | 
|  | } | 
|  | snprintf(msg, sizeof(msg), "%d", fd); | 
|  | if (write(sfd, msg, strlen(msg)) < 0) { | 
|  | close(sfd); | 
|  | close(fd); | 
|  | return -1; | 
|  | } | 
|  | close(sfd); | 
|  | close(fd); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | #warning "Not threadsafe!" | 
|  | Rock *_sock_rock; | 
|  |  | 
|  | Rock *_sock_findrock(int fd, struct stat *dp) | 
|  | { | 
|  | Rock *r; | 
|  | struct stat d; | 
|  |  | 
|  | /* Skip the fstat if there are no socket rocks */ | 
|  | if (!_sock_rock) | 
|  | return 0; | 
|  | /* If they pass us a struct stat, then they already did an fstat */ | 
|  | if (dp == 0) { | 
|  | dp = &d; | 
|  | fstat(fd, dp); | 
|  | } | 
|  | for (r = _sock_rock; r; r = r->next) { | 
|  | if (r->inode == dp->st_ino && r->dev == dp->st_dev) | 
|  | break; | 
|  | } | 
|  | return r; | 
|  | } | 
|  |  | 
|  | 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->ctl_fd = -1; | 
|  | r->other = -1; | 
|  | r->has_listen_fd = FALSE; | 
|  | r->listen_fd = -1; | 
|  | return r; | 
|  | } | 
|  |  | 
|  | void _sock_fd_closed(int fd) | 
|  | { | 
|  | Rock *r = _sock_findrock(fd, 0); | 
|  |  | 
|  | if (!r) | 
|  | return; | 
|  | if (r->ctl_fd >= 0) | 
|  | close(r->ctl_fd); | 
|  | if (r->has_listen_fd) { | 
|  | close(r->listen_fd); | 
|  | /* This shouldn't matter - the rock is being closed anyways. */ | 
|  | r->has_listen_fd = FALSE; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* For a ctlfd and a few other settings, it opens and returns the corresponding | 
|  | * datafd.  This will close cfd on error, or store it in the rock o/w. */ | 
|  | int _sock_data(int cfd, const char *net, int domain, int type, int protocol, | 
|  | Rock **rp) | 
|  | { | 
|  | int n, fd; | 
|  | Rock *r; | 
|  | char name[Ctlsize]; | 
|  | int open_flags; | 
|  |  | 
|  | /* 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 */ | 
|  | open_flags = O_RDWR; | 
|  | open_flags |= (type & SOCK_NONBLOCK ? O_NONBLOCK : 0); | 
|  | open_flags |= (type & SOCK_CLOEXEC ? O_CLOEXEC : 0); | 
|  | fd = open(name, open_flags); | 
|  | 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) { | 
|  | close(cfd); | 
|  | close(fd); | 
|  | errno = ENOBUFS; | 
|  | return -1; | 
|  | } | 
|  | if (rp) | 
|  | *rp = r; | 
|  | memset(&r->raddr, 0, sizeof(r->raddr_stor)); | 
|  | memset(&r->addr, 0, sizeof(r->addr_stor)); | 
|  | r->domain = domain; | 
|  | r->stype = _sock_strip_opts(type); | 
|  | r->sopts = _sock_get_opts(type); | 
|  | r->protocol = protocol; | 
|  | strcpy(r->ctl, name); | 
|  | r->ctl_fd = cfd; | 
|  | return fd; | 
|  | } | 
|  |  | 
|  | /* Takes network-byte ordered IPv4 addr and writes it into buf, in the plan 9 IP | 
|  | * addr format */ | 
|  | void naddr_to_plan9addr(uint32_t sin_addr, uint8_t *buf) | 
|  | { | 
|  | uint8_t *sin_bytes = (uint8_t *)&sin_addr; | 
|  |  | 
|  | memset(buf, 0, 10); | 
|  | buf += 10; | 
|  | buf[0] = 0xff; | 
|  | buf[1] = 0xff; | 
|  | buf += 2; | 
|  | buf[0] = sin_bytes[0];	/* e.g. 192 */ | 
|  | buf[1] = sin_bytes[1];	/* e.g. 168 */ | 
|  | buf[2] = sin_bytes[2];	/* e.g.   0 */ | 
|  | buf[3] = sin_bytes[3];	/* e.g.   1 */ | 
|  | } | 
|  |  | 
|  | /* does v4 only */ | 
|  | uint32_t plan9addr_to_naddr(uint8_t *buf) | 
|  | { | 
|  | buf += 12; | 
|  | return (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | (buf[0] << 0); | 
|  | } | 
|  |  | 
|  | /* 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; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* In Linux, socket options are multiplexed in the socket type. */ | 
|  | int _sock_strip_opts(int type) | 
|  | { | 
|  | return type & ~(SOCK_NONBLOCK | SOCK_CLOEXEC); | 
|  | } | 
|  |  | 
|  | int _sock_get_opts(int type) | 
|  | { | 
|  | return type & (SOCK_NONBLOCK | SOCK_CLOEXEC); | 
|  | } | 
|  |  | 
|  | /* Opens the FD for "listen", and attaches it to the Rock.  When the dfd (and | 
|  | * thus the Rock) closes, we'll close the listen file too.  Returns the FD on | 
|  | * success, -1 on error.  This is racy, like a lot of other Rock stuff. */ | 
|  | static int _rock_open_listen_fd(Rock *r) | 
|  | { | 
|  | char listen_file[Ctlsize]; | 
|  | int ret; | 
|  | int open_flags; | 
|  |  | 
|  | _sock_get_conv_filename(r, "listen", listen_file); | 
|  | open_flags = O_PATH; | 
|  | open_flags |= (r->sopts & SOCK_CLOEXEC ? O_CLOEXEC : 0); | 
|  | ret = open(listen_file, open_flags); | 
|  | /* 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; | 
|  | r->has_listen_fd = TRUE; | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* Used by user/iplib (e.g. epoll).  Returns the FDs for the ctl_fd and for the | 
|  | * listen file, opened O_PATH, for this conversation.  Returns -1 on no FDs. */ | 
|  | void _sock_lookup_rock_fds(int sock_fd, bool can_open_listen_fd, | 
|  | int *listen_fd_r, int *ctl_fd_r) | 
|  | { | 
|  | Rock *r = _sock_findrock(sock_fd, 0); | 
|  |  | 
|  | *listen_fd_r = -1; | 
|  | *ctl_fd_r = -1; | 
|  | if (!r || r->domain == PF_UNIX) | 
|  | return; | 
|  | if (!r->has_listen_fd && can_open_listen_fd) | 
|  | _rock_open_listen_fd(r); | 
|  | *listen_fd_r = r->listen_fd;	/* might still be -1.  that's OK. */ | 
|  | *ctl_fd_r = r->ctl_fd; | 
|  | } | 
|  |  | 
|  | /* Used by fcntl for F_SETFL. */ | 
|  | void _sock_mirror_fcntl(int sock_fd, int cmd, long arg) | 
|  | { | 
|  | Rock *r = _sock_findrock(sock_fd, 0); | 
|  |  | 
|  | if (!r || r->domain == PF_UNIX) | 
|  | return; | 
|  | if (r->ctl_fd >= 0) | 
|  | syscall(SYS_fcntl, r->ctl_fd, cmd, arg); | 
|  | if (r->has_listen_fd) | 
|  | syscall(SYS_fcntl, r->listen_fd, cmd, arg); | 
|  | } | 
|  |  | 
|  | /* Given an FD, opens the FD with the name 'sibling' in the same directory. | 
|  | * e.g., you have a data, you open a ctl.  Don't use this with cloned FDs (i.e. | 
|  | * open clone, get a ctl back) until we fix 9p and fd2path. | 
|  | * | 
|  | * Careful, this will always open O_CLOEXEC.  The rationale is that the callers | 
|  | * of this are low-level libraries that quickly close the FD, before any | 
|  | * non-malicious exec. */ | 
|  | int get_sibling_fd(int fd, const char *sibling) | 
|  | { | 
|  | char path[MAX_PATH_LEN]; | 
|  | char *graft; | 
|  |  | 
|  | if (syscall(SYS_fd2path, fd, path, sizeof(path)) < 0) | 
|  | return -1; | 
|  | graft = strrchr(path, '/'); | 
|  | if (!graft) | 
|  | return -1; | 
|  | graft++; | 
|  | *graft = 0; | 
|  | snprintf(graft, sizeof(path) - strlen(path), sibling); | 
|  | return open(path, O_RDWR | O_CLOEXEC); | 
|  | } | 
|  |  | 
|  | /* Writes num to FD in ASCII in hex format. */ | 
|  | int write_hex_to_fd(int fd, uint64_t num) | 
|  | { | 
|  | int ret; | 
|  | char cmd[50]; | 
|  | char *ptr; | 
|  |  | 
|  | ptr = u64_to_str(num, cmd, sizeof(cmd)); | 
|  | if (!ptr) | 
|  | return -1; | 
|  | ret = write(fd, ptr, sizeof(cmd) - (ptr - cmd)); | 
|  | if (ret <= 0) | 
|  | return -1; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* Returns a char representing the lowest 4 bits of x */ | 
|  | static char num_to_nibble(unsigned int x) | 
|  | { | 
|  | return "0123456789abcdef"[x & 0xf]; | 
|  | } | 
|  |  | 
|  | /* Converts num to a string, in hex, using buf as storage.  Returns a pointer to | 
|  | * the string from within your buf, or 0 on failure. */ | 
|  | char *u64_to_str(uint64_t num, char *buf, size_t len) | 
|  | { | 
|  | char *ptr; | 
|  | size_t nr_nibbles = sizeof(num) * 8 / 4; | 
|  |  | 
|  | /* 3: 0, x, and \0 */ | 
|  | if (len < nr_nibbles + 3) | 
|  | return 0; | 
|  | ptr = &buf[len - 1]; | 
|  | /* Build the string backwards */ | 
|  | *ptr = '\0'; | 
|  | for (int i = 0; i < nr_nibbles; i++) { | 
|  | ptr--; | 
|  | *ptr = num_to_nibble(num); | 
|  | num >>= 4; | 
|  | } | 
|  | ptr--; | 
|  | *ptr = 'x'; | 
|  | ptr--; | 
|  | *ptr = '0'; | 
|  | return ptr; | 
|  | } |