net: rock: Use a helper for conversation filenames (XCC)
[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 #include <ctype.h>
19
20 /* bsd extensions */
21 #include <sys/uio.h>
22 #include <sys/socket.h>
23 #include <netinet/in.h>
24 #include <sys/un.h>
25 #include <arpa/inet.h>
26
27 #include <sys/plan9_helpers.h>
28
29 /* Puts the path of the conversation file for 'name' for the Rock in retval,
30  * which must be a char [Ctlsize]. */
31 void _sock_get_conv_filename(Rock *r, const char *name, char *retloc)
32 {
33         char *p;
34
35         strlcpy(retloc, r->ctl, Ctlsize);
36         p = strrchr(retloc, '/');
37         assert(p);
38         p++;
39         *p = 0;
40         strlcat(retloc, name, Ctlsize);
41 }
42
43 void
44 _sock_ingetaddr(Rock *r, struct sockaddr_in *ip, socklen_t *alen,
45                 const char *a)
46 {
47         int n, fd;
48         char *p;
49         char name[Ctlsize];
50         int open_flags;
51
52         /* get remote address */
53         _sock_get_conv_filename(r, a, name);
54         open_flags = O_RDONLY;
55         open_flags |= (r->sopts & SOCK_CLOEXEC ? O_CLOEXEC : 0);
56         fd = open(name, open_flags);
57         if (fd >= 0) {
58                 n = read(fd, name, sizeof(name) - 1);
59                 if (n > 0) {
60                         name[n] = 0;
61                         p = strchr(name, '!');
62                         if (p) {
63                                 *p++ = 0;
64                                 ip->sin_family = AF_INET;
65                                 ip->sin_port = htons(atoi(p));
66                                 ip->sin_addr.s_addr = inet_addr(name);
67                                 if (alen)
68                                         *alen = sizeof(struct sockaddr_in);
69                         }
70                 }
71                 close(fd);
72         }
73
74 }
75
76 /*
77  * return ndb attribute type of an ip name
78  */
79 int _sock_ipattr(const char *name)
80 {
81         const char *p;
82         int dot = 0;
83         int alpha = 0;
84
85         for (p = name; *p; p++) {
86                 if (isdigit(*p))
87                         continue;
88                 else if (isalpha(*p) || *p == '-')
89                         alpha = 1;
90                 else if (*p == '.')
91                         dot = 1;
92                 else
93                         return Tsys;
94         }
95
96         if (alpha) {
97                 if (dot)
98                         return Tdom;
99                 else
100                         return Tsys;
101         }
102
103         if (dot)
104                 return Tip;
105         else
106                 return Tsys;
107 }
108
109 /* we can't avoid overrunning npath because we don't know how big it is. */
110 void _sock_srvname(char *npath, char *path)
111 {
112         char *p;
113
114         strcpy(npath, "/srv/UD.");
115         p = strrchr(path, '/');
116         if (p == 0)
117                 p = path;
118         else
119                 p++;
120         strcat(npath, p);
121 }
122
123 int _sock_srv(char *path, int fd)
124 {
125         int sfd;
126         char msg[8 + 256 + 1];
127
128         /* change the path to something in srv */
129         _sock_srvname(msg, path);
130
131         /* remove any previous instance */
132         unlink(msg);
133
134         /* put the fd in /srv and then close it */
135         sfd = creat(msg, 0666);
136         if (sfd < 0) {
137                 close(fd);
138                 return -1;
139         }
140         snprintf(msg, sizeof(msg), "%d", fd);
141         if (write(sfd, msg, strlen(msg)) < 0) {
142                 close(sfd);
143                 close(fd);
144                 return -1;
145         }
146         close(sfd);
147         close(fd);
148         return 0;
149 }
150
151 #warning "Not threadsafe!"
152 Rock *_sock_rock;
153
154 Rock *_sock_findrock(int fd, struct stat *dp)
155 {
156         Rock *r;
157         struct stat d;
158
159         /* Skip the fstat if there are no socket rocks */
160         if (!_sock_rock)
161                 return 0;
162         /* If they pass us a struct stat, then they already did an fstat */
163         if (dp == 0) {
164                 dp = &d;
165                 fstat(fd, dp);
166         }
167         for (r = _sock_rock; r; r = r->next) {
168                 if (r->inode == dp->st_ino && r->dev == dp->st_dev)
169                         break;
170         }
171         return r;
172 }
173
174 Rock *_sock_newrock(int fd)
175 {
176         Rock *r;
177         struct stat d;
178
179         fstat(fd, &d);
180         r = _sock_findrock(fd, &d);
181         if (r == 0) {
182                 r = malloc(sizeof(Rock));
183                 if (r == 0)
184                         return 0;
185                 r->dev = d.st_dev;
186                 r->inode = d.st_ino;
187                 /* TODO: this is not thread-safe! */
188                 r->next = _sock_rock;
189                 _sock_rock = r;
190         }
191         assert(r->dev == d.st_dev);
192         assert(r->inode == d.st_ino);
193         r->domain = 0;
194         r->stype = 0;
195         r->sopts = 0;
196         r->protocol = 0;
197         memset(&r->addr, 0, sizeof(r->addr_stor));
198         r->reserved = 0;
199         memset(&r->raddr, 0, sizeof(r->raddr_stor));
200         r->ctl[0] = '\0';
201         r->ctl_fd = -1;
202         r->other = -1;
203         r->has_listen_fd = FALSE;
204         r->listen_fd = -1;
205         return r;
206 }
207
208 void _sock_fd_closed(int fd)
209 {
210         Rock *r = _sock_findrock(fd, 0);
211
212         if (!r)
213                 return;
214         if (r->ctl_fd >= 0)
215                 close(r->ctl_fd);
216         if (r->has_listen_fd) {
217                 close(r->listen_fd);
218                 /* This shouldn't matter - the rock is being closed anyways. */
219                 r->has_listen_fd = FALSE;
220         }
221 }
222
223 /* For a ctlfd and a few other settings, it opens and returns the corresponding
224  * datafd.  This will close cfd on error, or store it in the rock o/w. */
225 int _sock_data(int cfd, const char *net, int domain, int type, int protocol,
226                Rock **rp)
227 {
228         int n, fd;
229         Rock *r;
230         char name[Ctlsize];
231         int open_flags;
232
233         /* get the data file name */
234         n = read(cfd, name, sizeof(name) - 1);
235         if (n < 0) {
236                 close(cfd);
237                 errno = ENOBUFS;
238                 return -1;
239         }
240         name[n] = 0;
241         n = strtoul(name, 0, 0);
242         snprintf(name, sizeof(name), "/net/%s/%d/data", net, n);
243
244         /* open data file */
245         open_flags = O_RDWR;
246         open_flags |= (type & SOCK_NONBLOCK ? O_NONBLOCK : 0);
247         open_flags |= (type & SOCK_CLOEXEC ? O_CLOEXEC : 0);
248         fd = open(name, open_flags);
249         if (fd < 0) {
250                 close(cfd);
251                 errno = ENOBUFS;
252                 return -1;
253         }
254
255         /* hide stuff under the rock */
256         snprintf(name, sizeof(name), "/net/%s/%d/ctl", net, n);
257         r = _sock_newrock(fd);
258         if (r == 0) {
259                 close(cfd);
260                 close(fd);
261                 errno = ENOBUFS;
262                 return -1;
263         }
264         if (rp)
265                 *rp = r;
266         memset(&r->raddr, 0, sizeof(r->raddr_stor));
267         memset(&r->addr, 0, sizeof(r->addr_stor));
268         r->domain = domain;
269         r->stype = _sock_strip_opts(type);
270         r->sopts = _sock_get_opts(type);
271         r->protocol = protocol;
272         strcpy(r->ctl, name);
273         r->ctl_fd = cfd;
274         return fd;
275 }
276
277 /* Takes network-byte ordered IPv4 addr and writes it into buf, in the plan 9 IP
278  * addr format */
279 void naddr_to_plan9addr(uint32_t sin_addr, uint8_t *buf)
280 {
281         uint8_t *sin_bytes = (uint8_t *)&sin_addr;
282
283         memset(buf, 0, 10);
284         buf += 10;
285         buf[0] = 0xff;
286         buf[1] = 0xff;
287         buf += 2;
288         buf[0] = sin_bytes[0];  /* e.g. 192 */
289         buf[1] = sin_bytes[1];  /* e.g. 168 */
290         buf[2] = sin_bytes[2];  /* e.g.   0 */
291         buf[3] = sin_bytes[3];  /* e.g.   1 */
292 }
293
294 /* does v4 only */
295 uint32_t plan9addr_to_naddr(uint8_t *buf)
296 {
297         buf += 12;
298         return (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | (buf[0] << 0);
299 }
300
301 /* Returns a rock* if the socket exists and is UDP */
302 Rock *udp_sock_get_rock(int fd)
303 {
304         Rock *r = _sock_findrock(fd, 0);
305
306         if (!r) {
307                 errno = ENOTSOCK;
308                 return 0;
309         }
310         if ((r->domain == PF_INET) && (r->stype == SOCK_DGRAM))
311                 return r;
312
313         return 0;
314 }
315
316 /* In Linux, socket options are multiplexed in the socket type. */
317 int _sock_strip_opts(int type)
318 {
319         return type & ~(SOCK_NONBLOCK | SOCK_CLOEXEC);
320 }
321
322 int _sock_get_opts(int type)
323 {
324         return type & (SOCK_NONBLOCK | SOCK_CLOEXEC);
325 }
326
327 /* Opens the FD for "listen", and attaches it to the Rock.  When the dfd (and
328  * thus the Rock) closes, we'll close the listen file too.  Returns the FD on
329  * success, -1 on error.  This is racy, like a lot of other Rock stuff. */
330 static int _rock_open_listen_fd(Rock *r)
331 {
332         char listen_file[Ctlsize];
333         int ret;
334         int open_flags;
335
336         _sock_get_conv_filename(r, "listen", listen_file);
337         open_flags = O_PATH;
338         open_flags |= (r->sopts & SOCK_CLOEXEC ? O_CLOEXEC : 0);
339         ret = open(listen_file, open_flags);
340         /* Probably a bug in the rock code (or the kernel!) if we couldn't walk to
341          * our listen. */
342         assert(ret >= 0);
343         r->listen_fd = ret;
344         r->has_listen_fd = TRUE;
345
346         return ret;
347 }
348
349 /* Used by user/iplib (e.g. epoll).  Returns the FDs for the ctl_fd and for the
350  * listen file, opened O_PATH, for this conversation.  Returns -1 on no FDs. */
351 void _sock_lookup_rock_fds(int sock_fd, bool can_open_listen_fd,
352                            int *listen_fd_r, int *ctl_fd_r)
353 {
354         Rock *r = _sock_findrock(sock_fd, 0);
355
356         *listen_fd_r = -1;
357         *ctl_fd_r = -1;
358         if (!r || r->domain == PF_UNIX)
359                 return;
360         if (!r->has_listen_fd && can_open_listen_fd)
361                 _rock_open_listen_fd(r);
362         *listen_fd_r = r->listen_fd;    /* might still be -1.  that's OK. */
363         *ctl_fd_r = r->ctl_fd;
364 }
365
366 /* Used by fcntl for F_SETFL. */
367 void _sock_mirror_fcntl(int sock_fd, int cmd, long arg)
368 {
369         Rock *r = _sock_findrock(sock_fd, 0);
370
371         if (!r || r->domain == PF_UNIX)
372                 return;
373         if (r->ctl_fd >= 0)
374                 syscall(SYS_fcntl, r->ctl_fd, cmd, arg);
375         if (r->has_listen_fd)
376                 syscall(SYS_fcntl, r->listen_fd, cmd, arg);
377 }
378
379 /* Given an FD, opens the FD with the name 'sibling' in the same directory.
380  * e.g., you have a data, you open a ctl.  Don't use this with cloned FDs (i.e.
381  * open clone, get a ctl back) until we fix 9p and fd2path.
382  *
383  * Careful, this will always open O_CLOEXEC.  The rationale is that the callers
384  * of this are low-level libraries that quickly close the FD, before any
385  * non-malicious exec. */
386 int get_sibling_fd(int fd, const char *sibling)
387 {
388         char path[MAX_PATH_LEN];
389         char *graft;
390
391         if (syscall(SYS_fd2path, fd, path, sizeof(path)) < 0)
392                 return -1;
393         graft = strrchr(path, '/');
394         if (!graft)
395                 return -1;
396         graft++;
397         *graft = 0;
398         snprintf(graft, sizeof(path) - strlen(path), sibling);
399         return open(path, O_RDWR | O_CLOEXEC);
400 }
401
402 /* Writes num to FD in ASCII in hex format. */
403 int write_hex_to_fd(int fd, uint64_t num)
404 {
405         int ret;
406         char cmd[50];
407         char *ptr;
408
409         ptr = u64_to_str(num, cmd, sizeof(cmd));
410         if (!ptr)
411                 return -1;
412         ret = write(fd, ptr, sizeof(cmd) - (ptr - cmd));
413         if (ret <= 0)
414                 return -1;
415         return 0;
416 }
417
418 /* Returns a char representing the lowest 4 bits of x */
419 static char num_to_nibble(unsigned int x)
420 {
421         return "0123456789abcdef"[x & 0xf];
422 }
423
424 /* Converts num to a string, in hex, using buf as storage.  Returns a pointer to
425  * the string from within your buf, or 0 on failure. */
426 char *u64_to_str(uint64_t num, char *buf, size_t len)
427 {
428         char *ptr;
429         size_t nr_nibbles = sizeof(num) * 8 / 4;
430
431         /* 3: 0, x, and \0 */
432         if (len < nr_nibbles + 3)
433                 return 0;
434         ptr = &buf[len - 1];
435         /* Build the string backwards */
436         *ptr = '\0';
437         for (int i = 0; i < nr_nibbles; i++) {
438                 ptr--;
439                 *ptr = num_to_nibble(num);
440                 num >>= 4;
441         }
442         ptr--;
443         *ptr = 'x';
444         ptr--;
445         *ptr = '0';
446         return ptr;
447 }