akaros/tools/compilers/gcc-glibc/glibc-2.19-akaros/sysdeps/akaros/plan9_sockets.c
<<
>>
Prefs
   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]. */
  31void _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
  43void
  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 */
  79int _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. */
 110void _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
 123int _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!"
 152Rock *_sock_rock;
 153
 154Rock *_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
 174Rock *_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
 208void _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. */
 225int _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 */
 279void 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 */
 295uint32_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 */
 302Rock *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. */
 317int _sock_strip_opts(int type)
 318{
 319        return type & ~(SOCK_NONBLOCK | SOCK_CLOEXEC);
 320}
 321
 322int _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. */
 330static 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
 341         * to 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. */
 351void _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. */
 367void _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. */
 386int 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. */
 403int 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 */
 419static 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. */
 426char *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}
 448