c40149a42f9ede768159cf25f39d5a352e24dc0f
[akaros.git] / tools / compilers / gcc-glibc / glibc-2.19-akaros / sysdeps / akaros / plan9_sockets.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 <stdlib.h>
13 #include <stdio.h>
14 #include <fcntl.h>
15 #include <errno.h>
16 #include <string.h>
17 #include <assert.h>
18
19 /* bsd extensions */
20 #include <sys/uio.h>
21 #include <sys/socket.h>
22 #include <netinet/in.h>
23 #include <sys/un.h>
24 #include <arpa/inet.h>
25
26 #include "plan9_sockets.h"
27
28 void
29 _sock_ingetaddr(Rock * r, struct sockaddr_in *ip, socklen_t * alen,
30                                 const char *a)
31 {
32         int n, fd;
33         char *p;
34         char name[Ctlsize];
35
36         /* get remote address */
37         strcpy(name, r->ctl);
38         p = strrchr(name, '/');
39         strcpy(p + 1, a);
40         fd = open(name, O_RDONLY);
41         if (fd >= 0) {
42                 n = read(fd, name, sizeof(name) - 1);
43                 if (n > 0) {
44                         name[n] = 0;
45                         p = strchr(name, '!');
46                         if (p) {
47                                 *p++ = 0;
48                                 ip->sin_family = AF_INET;
49                                 ip->sin_port = htons(atoi(p));
50                                 ip->sin_addr.s_addr = inet_addr(name);
51                                 if (alen)
52                                         *alen = sizeof(struct sockaddr_in);
53                         }
54                 }
55                 close(fd);
56         }
57
58 }
59
60 /*
61  *  return ndb attribute type of an ip name
62  */
63 int _sock_ipattr(const char *name)
64 {
65         const char *p;
66         int dot = 0;
67         int alpha = 0;
68
69         for (p = name; *p; p++) {
70                 if (isdigit(*p)) ;
71                 else if (isalpha(*p) || *p == '-')
72                         alpha = 1;
73                 else if (*p == '.')
74                         dot = 1;
75                 else
76                         return Tsys;
77         }
78
79         if (alpha) {
80                 if (dot)
81                         return Tdom;
82                 else
83                         return Tsys;
84         }
85
86         if (dot)
87                 return Tip;
88         else
89                 return Tsys;
90 }
91
92 /* we can't avoid overrunning npath because we don't know how big it is. */
93 void _sock_srvname(char *npath, char *path)
94 {
95         char *p;
96
97         strcpy(npath, "/srv/UD.");
98         p = strrchr(path, '/');
99         if (p == 0)
100                 p = path;
101         else
102                 p++;
103         strcat(npath, p);
104 }
105
106 int _sock_srv(char *path, int fd)
107 {
108         int sfd;
109         char msg[8 + 256 + 1];
110
111         /* change the path to something in srv */
112         _sock_srvname(msg, path);
113
114         /* remove any previous instance */
115         unlink(msg);
116
117         /* put the fd in /srv and then close it */
118         sfd = creat(msg, 0666);
119         if (sfd < 0) {
120                 close(fd);
121                 return -1;
122         }
123         snprintf(msg, sizeof msg, "%d", fd);
124         if (write(sfd, msg, strlen(msg)) < 0) {
125                 close(sfd);
126                 close(fd);
127                 return -1;
128         }
129         close(sfd);
130         close(fd);
131         return 0;
132 }
133
134 #warning "Not threadsafe!"
135 Rock *_sock_rock;
136
137 Rock *_sock_findrock(int fd, struct stat * dp)
138 {
139         Rock *r;
140         struct stat d;
141
142         /* Skip the fstat if there are no socket rocks */
143         if (!_sock_rock)
144                 return 0;
145         /* If they pass us a struct stat, then they already did an fstat */
146         if (dp == 0) {
147                 dp = &d;
148                 fstat(fd, dp);
149         }
150         for (r = _sock_rock; r; r = r->next) {
151                 if (r->inode == dp->st_ino && r->dev == dp->st_dev)
152                         break;
153         }
154         return r;
155 }
156
157 Rock *_sock_newrock(int fd)
158 {
159         Rock *r;
160         struct stat d;
161
162         fstat(fd, &d);
163         r = _sock_findrock(fd, &d);
164         if (r == 0) {
165                 r = malloc(sizeof(Rock));
166                 if (r == 0)
167                         return 0;
168                 r->dev = d.st_dev;
169                 r->inode = d.st_ino;
170                 /* TODO: this is not thread-safe! */
171                 r->next = _sock_rock;
172                 _sock_rock = r;
173         }
174         assert(r->dev == d.st_dev);
175         assert(r->inode == d.st_ino);
176         r->domain = 0;
177         r->stype = 0;
178         r->sopts = 0;
179         r->protocol = 0;
180         memset(&r->addr, 0, sizeof(r->addr_stor));
181         r->reserved = 0;
182         memset(&r->raddr, 0, sizeof(r->raddr_stor));
183         r->ctl[0] = '\0';
184         r->other = -1;
185         r->is_listener = FALSE;
186         r->listen_fd = -1;
187         return r;
188 }
189
190 /* For a ctlfd and a few other settings, it opens and returns the corresponding
191  * datafd.  This will close cfd for you. */
192 int
193 _sock_data(int cfd, char *net, int domain, int type, int protocol, Rock ** rp)
194 {
195         int n, fd;
196         Rock *r;
197         char name[Ctlsize];
198
199         /* get the data file name */
200         n = read(cfd, name, sizeof(name) - 1);
201         if (n < 0) {
202                 close(cfd);
203                 errno = ENOBUFS;
204                 return -1;
205         }
206         name[n] = 0;
207         n = strtoul(name, 0, 0);
208         snprintf(name, sizeof name, "/net/%s/%d/data", net, n);
209
210         /* open data file */
211         fd = open(name, O_RDWR);
212         close(cfd);     /* close this no matter what */
213         if (fd < 0) {
214                 errno = ENOBUFS;
215                 return -1;
216         }
217
218         /* hide stuff under the rock */
219         snprintf(name, sizeof name, "/net/%s/%d/ctl", net, n);
220         r = _sock_newrock(fd);
221         if (r == 0) {
222                 errno = ENOBUFS;
223                 close(fd);
224                 return -1;
225         }
226         if (rp)
227                 *rp = r;
228         memset(&r->raddr, 0, sizeof(r->raddr_stor));
229         memset(&r->addr, 0, sizeof(r->addr_stor));
230         r->domain = domain;
231         r->stype = _sock_strip_opts(type);
232         r->sopts = _sock_get_opts(type);
233         r->protocol = protocol;
234         strcpy(r->ctl, name);
235         return fd;
236 }
237
238 /* Takes network-byte ordered IPv4 addr and writes it into buf, in the plan 9 IP
239  * addr format */
240 void naddr_to_plan9addr(uint32_t sin_addr, uint8_t * buf)
241 {
242         uint8_t *sin_bytes = (uint8_t *) & sin_addr;
243         memset(buf, 0, 10);
244         buf += 10;
245         buf[0] = 0xff;
246         buf[1] = 0xff;
247         buf += 2;
248         buf[0] = sin_bytes[0];  /* e.g. 192 */
249         buf[1] = sin_bytes[1];  /* e.g. 168 */
250         buf[2] = sin_bytes[2];  /* e.g.   0 */
251         buf[3] = sin_bytes[3];  /* e.g.   1 */
252 }
253
254 /* does v4 only */
255 uint32_t plan9addr_to_naddr(uint8_t * buf)
256 {
257         buf += 12;
258         return (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | (buf[0] << 0);
259 }
260
261 /* Returns a rock* if the socket exists and is UDP */
262 Rock *udp_sock_get_rock(int fd)
263 {
264         Rock *r = _sock_findrock(fd, 0);
265         if (!r) {
266                 errno = ENOTSOCK;
267                 return 0;
268         }
269         if ((r->domain == PF_INET) && (r->stype == SOCK_DGRAM))
270                 return r;
271         else
272                 return 0;
273 }
274
275 /* In Linux, socket options are multiplexed in the socket type. */
276 int _sock_strip_opts(int type)
277 {
278         return type & ~(SOCK_NONBLOCK | SOCK_CLOEXEC);
279 }
280
281 int _sock_get_opts(int type)
282 {
283         return type & (SOCK_NONBLOCK | SOCK_CLOEXEC);
284 }
285
286 /* Used by user/iplib (e.g. epoll).  Opens and returns the FD for the
287  * conversation's listen fd, which the caller needs to close.  Returns -1 if the
288  * FD is not a listener. */
289 int _sock_get_listen_fd(int sock_fd)
290 {
291         char listen_file[Ctlsize + 3];
292         char *x, *last_ctl;
293         Rock *r = _sock_findrock(sock_fd, 0);
294         int ret;
295
296         if (!r)
297                 return -1;
298         if (!r->is_listener)
299                 return -1;
300         /* We want an FD for the "listen" file.  This is for epoll.  We
301          * could optimize and only do this on demand, but whatever. */
302         strncpy(listen_file, r->ctl, sizeof(listen_file));
303         /* We want the conversation directory.  We can find the last "ctl"
304          * in the CTL name (they could have mounted at /ctlfoo/net/) */
305         x = listen_file;
306         do {
307                 last_ctl = x;
308                 x++;    /* move forward enough to not find the same "ctl" */
309                 x = strstr(x, "ctl");
310         } while (x);
311         /* last_ctl is either listen_file (if we never found ctl, which should never
312          * happen) or it points at the 'c' in the last "ctl". */
313         assert(last_ctl != listen_file);
314         strcpy(last_ctl, "listen");
315         ret = open(listen_file, O_PATH);
316         /* Probably a bug in the rock code (or the kernel!) if we couldn't walk to
317          * our listen. */
318         assert(ret >= 0);
319         r->listen_fd = ret;
320         return ret;
321 }
322
323 /* Used by user/iplib (e.g. epoll).  Looks up a previously opened FD for the
324  * listen file for this conversation.  Returns -1 if the FD is not a listener
325  * with an already-opened listen FD. */
326 int _sock_lookup_listen_fd(int sock_fd)
327 {
328         Rock *r = _sock_findrock(sock_fd, 0);
329
330         if (!r)
331                 return -1;
332         if (!r->is_listener)
333                 return -1;
334         return r->listen_fd;
335 }