| /*  | 
 |  * 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 <fcntl.h> | 
 | #include <errno.h> | 
 | #include <stdlib.h> | 
 |  | 
 | /* bsd extensions */ | 
 | #include <sys/uio.h> | 
 | #include <sys/socket.h> | 
 | #include <netinet/in.h> | 
 |  | 
 | #include "priv.h" | 
 |  | 
 | /* The plan9 UDP header looks like: | 
 |  * | 
 |  * 52 bytes | 
 |  *		raddr (16 b) | 
 |  *		laddr (16 b) | 
 |  *		IFC addr (ignored if user says it)  (16 b) | 
 |  *		rport (2 b) (network ordering) | 
 |  *		lport (ignored if user says it) (2b) | 
 |  * | 
 |  * The v4 addr format is 10 bytes of 0s, then two 0xff, then 4 bytes of addr. */ | 
 |  | 
 | #define P9_UDP_HDR_SZ 52 | 
 | /* Takes network-byte ordered IPv4 addr and writes it into buf, in the plan 9 IP | 
 |  * addr format */ | 
 | static 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 */ | 
 | static 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 */ | 
 | static 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; | 
 | } | 
 |  | 
 | ssize_t sendto (int fd, __const void *a, size_t n, | 
 | 			int flags, __CONST_SOCKADDR_ARG to, | 
 | 			socklen_t tolen) | 
 | { | 
 | 	Rock *r; | 
 | 	if (flags & MSG_OOB) { | 
 | 		errno = EOPNOTSUPP; | 
 | 		return -1; | 
 | 	} | 
 | 	/* UDP sockets need to have headers added to the payload for all packets, | 
 | 	 * since we're supporting blind sendto/recvfrom. */ | 
 | 	if ((r = udp_sock_get_rock(fd))) { | 
 | 		int ret; | 
 | 		uint32_t remote_addr; | 
 | 		uint16_t remote_port; | 
 | 		char *p, *newbuf; | 
 | 		/* Might not have a to if we were called from send() */ | 
 | 		if (!to) { | 
 | 			/* if they didn't connect yet, then there's no telling what raddr | 
 | 			 * will be.  TODO: check a state flag or something? */ | 
 | 			to = (struct sockaddr*)(&r->raddr); | 
 | 		} | 
 | 		remote_addr = ((struct sockaddr_in*)to)->sin_addr.s_addr; | 
 | 		remote_port = ((struct sockaddr_in*)to)->sin_port; | 
 | 		newbuf = malloc(n + P9_UDP_HDR_SZ); | 
 | 		if (!newbuf) { | 
 | 			errno = ENOMEM; | 
 | 			return -1; | 
 | 		} | 
 | 		p = newbuf; | 
 | 		naddr_to_plan9addr(remote_addr, p); | 
 | 		p += 16; | 
 | 		/* we don't need to specify an address.  if we don't specify a valid | 
 | 		 * local IP addr, the kernel will pick the one closest to dest */ | 
 | 		p += 16; | 
 | 		p += 16; /* skip ipifc */ | 
 | 		*(uint16_t*)p = remote_port; | 
 | 		p += 2; | 
 | 		p += 2; /* skip local port */ | 
 | 		memcpy(p, a, n); | 
 | 		ret = write(fd, newbuf, n + P9_UDP_HDR_SZ); | 
 | 		free(newbuf); | 
 | 		if (ret < 0) | 
 | 			return -1; | 
 | 		return ret - P9_UDP_HDR_SZ; | 
 | 	} | 
 | 	return write(fd, a, n); | 
 | } | 
 |  | 
 | ssize_t recvfrom (int fd, void *__restrict a, size_t n, | 
 | 			  int flags, __SOCKADDR_ARG from, | 
 | 			  socklen_t *__restrict fromlen) | 
 | { | 
 | 	Rock *r; | 
 | 	if (flags & MSG_OOB) { | 
 | 		errno = EOPNOTSUPP; | 
 | 		return -1; | 
 | 	} | 
 | 	if (from && getsockname(fd, from, fromlen) < 0) | 
 | 		return -1; | 
 | 	/* UDP sockets need to have headers added to the payload for all packets, | 
 | 	 * since we're supporting blind sendto/recvfrom. */ | 
 | 	if ((r = udp_sock_get_rock(fd))) { | 
 | 		int ret; | 
 | 		struct sockaddr_in *remote = (struct sockaddr_in*)from; | 
 | 		char *p, *newbuf = malloc(n + P9_UDP_HDR_SZ); | 
 | 		if (!newbuf) { | 
 | 			errno = ENOMEM; | 
 | 			return -1; | 
 | 		} | 
 | 		ret = read(fd, newbuf, n + P9_UDP_HDR_SZ); | 
 | 		/* subtracting before, so that we error out if we got less than the | 
 | 		 * headers needed */ | 
 | 		ret -= P9_UDP_HDR_SZ; | 
 | 		if (ret < 0) { | 
 | 			free(newbuf); | 
 | 			return -1; | 
 | 		} | 
 | 		memcpy(a, newbuf + P9_UDP_HDR_SZ, n); | 
 | 		/* Might not have a remote, if we were called via recv().  Could assert | 
 | 		 * that it's the same remote that we think we connected to, and that we | 
 | 		 * were already connected. (TODO) */ | 
 | 		if (remote) { | 
 | 			p = newbuf; | 
 | 			remote->sin_addr.s_addr = plan9addr_to_naddr(p); | 
 | 			p += 16; | 
 | 			p += 16;	/* skip local addr */ | 
 | 			p += 16;	/* skip ipifc */ | 
 | 			remote->sin_port = (p[0] << 0) | (p[1] << 8); | 
 | 			remote->sin_port = *(uint16_t*)p; | 
 | 			*fromlen = sizeof(struct sockaddr_in); | 
 | 		} | 
 | 		free(newbuf); | 
 | 		return ret; | 
 | 	} | 
 | 	return read(fd, a, n); | 
 | } |