[PATCH 2/2] Adds some resolv functionality (XCC)
[akaros.git] / user / bsd / sendto.c
1 /* 
2  * This file is part of the UCB release of Plan 9. It is subject to the license
3  * terms in the LICENSE file found in the top-level directory of this
4  * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
5  * part of the UCB release of Plan 9, including this file, may be copied,
6  * modified, propagated, or distributed except according to the terms contained
7  * in the LICENSE file.
8  */
9 /* posix */
10 #include <sys/types.h>
11 #include <unistd.h>
12 #include <fcntl.h>
13 #include <errno.h>
14 #include <stdlib.h>
15
16 /* bsd extensions */
17 #include <sys/uio.h>
18 #include <sys/socket.h>
19 #include <netinet/in.h>
20
21 #include "priv.h"
22
23 /* The plan9 UDP header looks like:
24  *
25  * 52 bytes
26  *              raddr (16 b)
27  *              laddr (16 b)
28  *              IFC addr (ignored if user says it)  (16 b)
29  *              rport (2 b) (network ordering)
30  *              lport (ignored if user says it) (2b)
31  *
32  * The v4 addr format is 10 bytes of 0s, then two 0xff, then 4 bytes of addr. */
33
34 #define P9_UDP_HDR_SZ 52
35 /* Takes network-byte ordered IPv4 addr and writes it into buf, in the plan 9 IP
36  * addr format */
37 static void naddr_to_plan9addr(uint32_t sin_addr, uint8_t *buf)
38 {
39         uint8_t *sin_bytes = (uint8_t*)&sin_addr;
40         memset(buf, 0, 10);
41         buf += 10;
42         buf[0] = 0xff;
43         buf[1] = 0xff;
44         buf += 2;
45         buf[0] = sin_bytes[0];  /* e.g. 192 */
46         buf[1] = sin_bytes[1];  /* e.g. 168 */
47         buf[2] = sin_bytes[2];  /* e.g.   0 */
48         buf[3] = sin_bytes[3];  /* e.g.   1 */
49 }
50
51 /* does v4 only */
52 static uint32_t plan9addr_to_naddr(uint8_t *buf)
53 {
54         buf += 12;
55         return (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | (buf[0] << 0);
56 }
57
58 /* Returns a rock* if the socket exists and is UDP */
59 static Rock *udp_sock_get_rock(int fd)
60 {
61         Rock *r = _sock_findrock(fd, 0);
62         if (!r) {
63                 errno = ENOTSOCK;
64                 return 0;
65         }
66         if ((r->domain == PF_INET) && (r->stype == SOCK_DGRAM))
67                 return r;
68         else
69                 return 0;
70 }
71
72 ssize_t sendto (int fd, __const void *a, size_t n,
73                         int flags, __CONST_SOCKADDR_ARG to,
74                         socklen_t tolen)
75 {
76         Rock *r;
77         if (flags & MSG_OOB) {
78                 errno = EOPNOTSUPP;
79                 return -1;
80         }
81         /* UDP sockets need to have headers added to the payload for all packets,
82          * since we're supporting blind sendto/recvfrom. */
83         if ((r = udp_sock_get_rock(fd))) {
84                 int ret;
85                 uint32_t remote_addr;
86                 uint16_t remote_port;
87                 char *p, *newbuf;
88                 /* Might not have a to if we were called from send() */
89                 if (!to) {
90                         /* if they didn't connect yet, then there's no telling what raddr
91                          * will be.  TODO: check a state flag or something? */
92                         to = (struct sockaddr*)(&r->raddr);
93                 }
94                 remote_addr = ((struct sockaddr_in*)to)->sin_addr.s_addr;
95                 remote_port = ((struct sockaddr_in*)to)->sin_port;
96                 newbuf = malloc(n + P9_UDP_HDR_SZ);
97                 if (!newbuf) {
98                         errno = ENOMEM;
99                         return -1;
100                 }
101                 p = newbuf;
102                 naddr_to_plan9addr(remote_addr, p);
103                 p += 16;
104                 /* we don't need to specify an address.  if we don't specify a valid
105                  * local IP addr, the kernel will pick the one closest to dest */
106                 p += 16;
107                 p += 16; /* skip ipifc */
108                 *(uint16_t*)p = remote_port;
109                 p += 2;
110                 p += 2; /* skip local port */
111                 memcpy(p, a, n);
112                 ret = write(fd, newbuf, n + P9_UDP_HDR_SZ);
113                 free(newbuf);
114                 if (ret < 0)
115                         return -1;
116                 return ret - P9_UDP_HDR_SZ;
117         }
118         return write(fd, a, n);
119 }
120
121 ssize_t recvfrom (int fd, void *__restrict a, size_t n,
122                           int flags, __SOCKADDR_ARG from,
123                           socklen_t *__restrict fromlen)
124 {
125         Rock *r;
126         if (flags & MSG_OOB) {
127                 errno = EOPNOTSUPP;
128                 return -1;
129         }
130         if (from && getsockname(fd, from, fromlen) < 0)
131                 return -1;
132         /* UDP sockets need to have headers added to the payload for all packets,
133          * since we're supporting blind sendto/recvfrom. */
134         if ((r = udp_sock_get_rock(fd))) {
135                 int ret;
136                 struct sockaddr_in *remote = (struct sockaddr_in*)from;
137                 char *p, *newbuf = malloc(n + P9_UDP_HDR_SZ);
138                 if (!newbuf) {
139                         errno = ENOMEM;
140                         return -1;
141                 }
142                 ret = read(fd, newbuf, n + P9_UDP_HDR_SZ);
143                 /* subtracting before, so that we error out if we got less than the
144                  * headers needed */
145                 ret -= P9_UDP_HDR_SZ;
146                 if (ret < 0) {
147                         free(newbuf);
148                         return -1;
149                 }
150                 memcpy(a, newbuf + P9_UDP_HDR_SZ, n);
151                 /* Might not have a remote, if we were called via recv().  Could assert
152                  * that it's the same remote that we think we connected to, and that we
153                  * were already connected. (TODO) */
154                 if (remote) {
155                         p = newbuf;
156                         remote->sin_addr.s_addr = plan9addr_to_naddr(p);
157                         p += 16;
158                         p += 16;        /* skip local addr */
159                         p += 16;        /* skip ipifc */
160                         remote->sin_port = (p[0] << 0) | (p[1] << 8);
161                         remote->sin_port = *(uint16_t*)p;
162                         *fromlen = sizeof(struct sockaddr_in);
163                 }
164                 free(newbuf);
165                 return ret;
166         }
167         return read(fd, a, n);
168 }