| /* | 
 |  * 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; | 
 | } |