| /*  | 
 |  * 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 <errno.h> | 
 | #include <string.h> | 
 | #include <fcntl.h> | 
 | #include <sys/stat.h> | 
 |  | 
 | /* bsd extensions */ | 
 | #include <sys/uio.h> | 
 | #include <sys/socket.h> | 
 | #include <netinet/in.h> | 
 |  | 
 | #include "priv.h" | 
 |  | 
 | Rock *_sock_rock; | 
 |  | 
 | Rock* | 
 | _sock_findrock(int fd, struct stat *dp) | 
 | { | 
 | 	Rock *r; | 
 | 	struct stat d; | 
 |  | 
 | 	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; | 
 |  | 
 | 	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; | 
 | 		/* TODO: this is not thread-safe! */ | 
 | 		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; | 
 | } | 
 |  | 
 | /* For a ctlfd and a few other settings, it opens and returns the corresponding | 
 |  * datafd.  This will close cfd for you. */ | 
 | 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); /* close this no matter what */ | 
 | 	if(fd < 0){ | 
 | 		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 | 
 | socket(int domain, int stype, int protocol) | 
 | { | 
 | 	Rock *r; | 
 | 	int cfd, fd, n; | 
 | 	int pfd[2]; | 
 | 	char *net; | 
 | 	char msg[128]; | 
 |  | 
 | 	switch(domain){ | 
 | 	case PF_INET: | 
 | 		/* get a free network directory */ | 
 | 		switch(stype){ | 
 | 		case SOCK_DGRAM: | 
 | 			net = "udp"; | 
 | 			cfd = open("/net/udp/clone", O_RDWR); | 
 | 			/* 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; | 
 | 				} | 
 | 			} | 
 | 			break; | 
 | 		case SOCK_STREAM: | 
 | 			net = "tcp"; | 
 | 			cfd = open("/net/tcp/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]); | 
 | 		r->domain = domain; | 
 | 		r->stype = stype; | 
 | 		r->protocol = protocol; | 
 | 		r->other = pfd[1]; | 
 | 		return pfd[0]; | 
 | 	default: | 
 | 		errno = EPROTONOSUPPORT; | 
 | 		return -1; | 
 | 	} | 
 | } | 
 |  | 
 | int | 
 | issocket(int fd) | 
 | { | 
 | 	Rock *r; | 
 |  | 
 | 	r = _sock_findrock(fd, 0); | 
 | 	return (r != 0); | 
 | } | 
 |  | 
 | /* | 
 |  * probably should do better than this | 
 |  */ | 
 | int getsockopt (int __fd, int __level, int __optname, | 
 | 		       void *__restrict __optval, | 
 | 		       socklen_t *__restrict __optlen) | 
 | { | 
 | 	return -1; | 
 | } | 
 |  | 
 | int setsockopt (int __fd, int __level, int __optname, | 
 | 		       __const void *__optval, socklen_t __optlen) | 
 | { | 
 | 	return 0; | 
 | } | 
 |  |