// Unix domain sockets. // // A connected pair is two buffers, not one. Writing to a socket appends to // its *peer's* buffer and reading drains its own, which is what makes a // socket bidirectional where a pipe is not, without needing two of anything. // // **One lock covers the whole table**, rather than a lock per socket. A pipe // gets away with a lock per pipe because every operation touches exactly one // object; a socket operation touches two, and two-object locking needs an // ordering rule that every path obeys or it deadlocks. Worse, the wakeup // races are subtle: a reader that tests `peer_closed` under its own lock while // the closer sets it under the peer's has no interlock at all, and sleeps // through the wakeup that was meant for it. With one lock the condition and // the sleep are atomic with respect to every writer of that condition, which // is the property sleep/wakeup actually requires. Thirty-two sockets carrying // GUI messages will not notice the contention. // // **Names are held in a table here, not in the filesystem.** A real Unix // socket appears in a directory listing as a special file, and binding one // creates that node. Doing that properly means a new inode type threaded // through both filesystems, iget, dirlookup and stat -- a VFS change, not a // socket change. The table gives every behaviour that matters: a client // finds a server by a name it agreed in advance, which is the whole job. // What is lost is `ls -l /tmp/schcom` showing an `s`, and `unlink()` of a // socket path failing with ENOENT. Both are worth fixing when the VFS next // gets opened up; neither blocks anything. #include "core/types.h" #include "core/defs.h" #include "core/param.h" #include "arch/mmu.h" #include "core/proc.h" #include "fs/fs.h" #include "core/spinlock.h" #include "core/sleeplock.h" #include "fs/file.h" #include "uapi/posix.h" #include "uapi/errno.h" #define _KERNEL_SOCKET_INTERNALS #include "net/socket.h" #include "core/sbuf.h" #include "core/dbg.h" static struct { struct spinlock lock; struct socket sock[NSOCK]; } stab; // Defined with the rest of the descriptor-passing code below; declared here // because sockclose is the one caller that cannot be moved after it. static void scm_drain(struct socket *s); // The socket table. // // Two columns here are the ones worth having: `buf`, because a peer that has // stopped reading is invisible until its buffer is full and the writer blocks; // and `scm`, because a descriptor sent over a socket and never received holds // a kernel reference open, and there is no garbage collector for it -- see // "What it does not do" in docs/architecture/networking.md. static void sock_dump(struct sbuf *out, const char *arg) { (void)arg; struct socket *s; int used = 0; sb_puts(out, " slot dom state ref buf peer scm name\n"); acquire(&stab.lock); for (s = stab.sock; s < stab.sock + NSOCK; s++) { if (s->state == SS_FREE && s->ref == 0) continue; used++; sb_putu_pad(out, (int)(s - stab.sock), 6); sb_puts(out, " "); sb_pad(out, s->domain == AF_UNIX ? "unix" : s->domain == AF_INET ? "inet" : s->domain == AF_NETLINK ? "nl" : "?", 7); sb_pad(out, s->state == SS_FREE ? "free" : s->state == SS_UNBOUND ? "unbound" : s->state == SS_BOUND ? "bound" : s->state == SS_LISTENING ? "listening" : s->state == SS_CONNECTED ? "connected" : "?", 11); sb_putu_pad(out, s->ref, 3); sb_putu_pad(out, s->nwrite - s->nread, 7); sb_puts(out, " "); if (s->peer) sb_putu_pad(out, (int)(s->peer - stab.sock), 4); else sb_puts(out, s->peer_closed ? "gone" : " -"); sb_putu_pad(out, s->nscm, 5); sb_puts(out, " "); if (s->name[0]) sb_puts(out, s->name); else if (s->nqueue) sb_putu(out, s->nqueue); sb_putc(out, '\n'); } release(&stab.lock); if (!used) sb_puts(out, " (no sockets)\n"); sb_puts(out, "\n buf is bytes written and not yet read; scm is descriptors\n" " sent and not yet received, which nothing reclaims.\n"); } void sockinit(void) { initlock(&stab.lock, "socktab"); dbg_register("sock", sock_dump, "the socket table, with buffered bytes and descriptors in flight"); } // Caller holds stab.lock. static struct socket * alloc_locked(int type) { struct socket *s; for(s = stab.sock; s < stab.sock + NSOCK; s++){ if(s->state == SS_FREE && s->ref == 0){ memset(s, 0, sizeof(*s)); s->ref = 1; s->type = type; s->state = SS_UNBOUND; if(myproc()){ s->cred_pid = myproc()->pid; s->cred_uid = myproc()->euid; s->cred_gid = myproc()->egid; } return s; } } return 0; } // Find a socket listening on this name. Caller holds stab.lock. static struct socket * find_listener_ino(uint dev, uint64 ino) { struct socket *s; for(s = stab.sock; s < stab.sock + NSOCK; s++) if(s->state == SS_LISTENING && s->bind_dev == dev && s->bind_ino == ino) return s; return 0; } // Detach s from its peer and tell the peer about it. Caller holds stab.lock. static void orphan_peer(struct socket *s) { struct socket *peer = s->peer; if(peer == 0) return; peer->peer_closed = 1; peer->peer = 0; s->peer = 0; // Both channels, because the peer may be blocked reading (waiting for data // that will now never come) or writing (waiting for room in a buffer nobody // will ever drain). wakeup(&peer->nread); wakeup(&peer->nwrite); } void sockclose(struct socket *s) { int i; if(s == 0) return; // An AF_INET socket's state lives in inet.c behind the lwIP lock, so its // teardown happens there. The table entry below is freed either way. if(s->domain == AF_INET){ acquire(&stab.lock); int last = (--s->ref <= 0); if(last) s->state = SS_FREE; release(&stab.lock); if(last && s->inet){ inet_close(s->inet); s->inet = 0; } return; } struct socket *q[NBACKLOG]; int nq; acquire(&stab.lock); if(--s->ref > 0){ release(&stab.lock); return; } // A listener that goes away takes its unaccepted connections with it. Each // queued entry is the *server* side of a connection whose client is already // live and may be writing into it; that client has to be told, or it hangs // on a socket that will never be read. // // Taking the backlog out here rather than walking it below is what lets the // drain happen with the lock dropped. At this point the reference count is // zero and the state is not yet SS_FREE, so nothing can find these sockets // and alloc_locked will not reuse them either. nq = s->nqueue; for(i = 0; i < nq; i++) q[i] = s->queue[i]; s->nqueue = 0; release(&stab.lock); // Descriptors that were sent to these sockets and never received have to be // closed rather than leaked -- the kernel is holding the only reference. scm_drain(s); for(i = 0; i < nq; i++) if(q[i]) scm_drain(q[i]); acquire(&stab.lock); orphan_peer(s); for(i = 0; i < nq; i++){ if(q[i] == 0) continue; orphan_peer(q[i]); q[i]->state = SS_FREE; q[i]->ref = 0; } s->state = SS_FREE; s->ref = 0; wakeup(&s->nqueue); // an accept() blocked on this listener release(&stab.lock); poll_kick(); // the peer's poll() sees the hangup now } void sockdup(struct socket *s) { acquire(&stab.lock); s->ref++; release(&stab.lock); } // --------------------------------------------------------------------------- // Socket options // // Only the options that are genuinely honoured appear here; everything else is // answered ENOPROTOOPT by the switch's default. That is deliberate and it is a // change of policy: both calls used to succeed unconditionally, which is the // worst of the three possible answers. A caller told "no" adapts; a caller told // "yes" falsely proceeds on the strength of it -- setting SO_RCVTIMEO and then // blocking forever in a read it believes will time out. // // The flags are recorded here as well as pushed into lwIP so that getsockopt // has one answer rather than two, and so that an AF_UNIX socket -- which has no // pcb and for which none of these mean anything -- can still give back what was // set, as POSIX requires. int sockopt_set(struct socket *s, int level, int name, int val) { int r = 0; acquire(&stab.lock); if (level == SOL_SOCKET) { switch (name) { case SO_REUSEADDR: s->o_reuseaddr = !!val; break; case SO_KEEPALIVE: s->o_keepalive = !!val; break; case SO_BROADCAST: s->o_broadcast = !!val; break; case 16: s->o_passcred = !!val; break; // SO_PASSCRED // Accepted and left at the size we have. Not the same lie as before: // Linux is explicitly free to clamp these, and sockopt_get below reports // the real figure rather than the requested one. case SO_SNDBUF: case SO_RCVBUF: release(&stab.lock); return 0; default: release(&stab.lock); return -ENOPROTOOPT; } } else if (level == IPPROTO_TCP && name == TCP_NODELAY) { if (s->type != SOCK_STREAM) { release(&stab.lock); return -EINVAL; } s->o_nodelay = !!val; } else { release(&stab.lock); return -ENOPROTOOPT; } int inet = (s->domain == AF_INET); void *h = s->inet; release(&stab.lock); // Outside the table lock: inet_setopt takes the lwIP lock, which is a // sleeplock, and the same ordering rule the rest of this file follows. if (inet && h) r = inet_setopt(h, level, name, val); return r; } int sockopt_get(struct socket *s, int level, int name, int *out) { int r = 0; acquire(&stab.lock); if (level == IPPROTO_TCP && name == TCP_NODELAY) { *out = s->o_nodelay; } else if (level != SOL_SOCKET) { r = -ENOPROTOOPT; } else { switch (name) { case SO_TYPE: *out = s->type; break; case SO_ACCEPTCONN: *out = (s->state == SS_LISTENING); break; case SO_REUSEADDR: *out = s->o_reuseaddr; break; case SO_KEEPALIVE: *out = s->o_keepalive; break; case SO_BROADCAST: *out = s->o_broadcast; break; case 16: *out = s->o_passcred; break; // SO_PASSCRED // The real buffer, not a number somebody asked for. case SO_SNDBUF: case SO_RCVBUF: *out = SOCKBUF; break; // An honest zero rather than the blanket one it replaces: connect() here // is blocking and reports failure as its own return value, so there is no // deferred error for SO_ERROR to carry. It becomes a real field the day a // non-blocking connect exists. case SO_ERROR: *out = 0; break; default: r = -ENOPROTOOPT; break; } } release(&stab.lock); return r; } const char * sockname(struct socket *s) { return s->name; } // --------------------------------------------------------------------------- // Descriptor passing // // docs/plan/hybrid.md mechanism 1, and item 1 of foundations.md. The kernel // holds a reference on every descriptor in flight, which is what makes the // sender free to close its own copy the instant sendmsg returns -- and what // makes dropping a socket with descriptors still queued a thing that has to be // handled rather than a thing that happens to work. // // **What this does not have is a garbage collector.** A descriptor for an // AF_UNIX socket, sent over that same socket and never received, holds a // reference to the socket that holds the reference to it; neither side's close // can reach zero and the pair leaks until reboot. Linux carries several hundred // lines to break exactly these cycles. Nothing on either plan passes a socket // over a socket -- Wayland passes memory and the driver work passes devices -- // so this is a known, bounded leak (thirty-two sockets) and not a silent one. // --------------------------------------------------------------------------- // Close every descriptor still queued on s. // // Called with the socket already unreachable but *not yet marked free*, and // without stab.lock: fileclose can sleep. Popping one at a time keeps the // kernel stack out of it -- a snapshot of every batch would be half a page. static void scm_drain(struct socket *s) { struct file *f; int i, j; for(;;){ f = 0; acquire(&stab.lock); for(i = 0; i < s->nscm && f == 0; i++) for(j = 0; j < s->scm[i].n; j++) if(s->scm[i].f[j]){ f = s->scm[i].f[j]; s->scm[i].f[j] = 0; break; } if(f == 0){ s->nscm = 0; release(&stab.lock); return; } release(&stab.lock); fileclose(f); } } // --------------------------------------------------------------------------- // Data // --------------------------------------------------------------------------- // Read bytes, and the descriptors that came with them. // // `fout` is an array of SCM_MAXFD slots, or 0. **Zero is not "ignore them"**: // a plain read(2) of a message carrying descriptors *closes* them, because the // alternative is holding kernel references for a receiver that has already // proved it is not looking. That is what Linux does and it is the only choice // that cannot leak. int sockread_fds(struct socket *s, char *addr, int n, struct file **fout, int *nfout) { struct file *taken[SCM_MAXFD]; int got = 0; int i; if(nfout) *nfout = 0; if(n < 0) return -EINVAL; if(s->domain == AF_INET) return inet_read(s->inet, addr, n); // Netlink never blocks: the answer was generated when the request was sent, // so an empty buffer means the caller has already drained this dump rather // than that more is coming. Blocking here would hang a getifaddrs() that // asked one question too many. if(s->domain == AF_NETLINK){ acquire(&stab.lock); uint have = s->nwrite - s->nread; if(have == 0){ release(&stab.lock); return -EAGAIN; } if((uint)n > have) n = (int)have; memmove(addr, s->data + s->nread, (uint)n); s->nread += (uint)n; release(&stab.lock); return n; } acquire(&stab.lock); while(s->nread == s->nwrite && !s->peer_closed){ if(myproc()->killed || s->state == SS_FREE){ release(&stab.lock); return -EINTR; } sleep(&s->nread, &stab.lock); } // A message carrying descriptors is a boundary. This read either starts // exactly on one -- and takes them -- or is cut short so that it stops // before it, leaving the next read to start there. if(s->nscm > 0){ if(s->scm[0].pos == s->nread){ got = s->scm[0].n; for(i = 0; i < got; i++) taken[i] = s->scm[0].f[i]; for(i = 1; i < s->nscm; i++) s->scm[i - 1] = s->scm[i]; s->nscm--; } // Positions strictly increase -- a message with descriptors carries at // least one byte -- so after taking one the next is genuinely ahead. if(s->nscm > 0 && (uint)n > s->scm[0].pos - s->nread) n = (int)(s->scm[0].pos - s->nread); } for(i = 0; i < n; i++){ if(s->nread == s->nwrite) break; addr[i] = s->data[s->nread++ % SOCKBUF]; } // Waking the writer matters even when nothing was read: it may be blocked // on a full buffer that this call has just made room in. wakeup(&s->nwrite); release(&stab.lock); poll_kick(); // a writer polling for room // Outside the lock, because fileclose can sleep. if(fout){ for(int j = 0; j < got; j++) fout[j] = taken[j]; if(nfout) *nfout = got; } else { for(int j = 0; j < got; j++) fileclose(taken[j]); } return i; } int sockread(struct socket *s, char *addr, int n) { return sockread_fds(s, addr, n, 0, 0); } // Write bytes, optionally handing `nf` descriptors over with them. // // The kernel takes its own reference on each before it gets here, so the // sender may close its copies as soon as this returns. A return of *any* // positive count means the descriptors were queued; a negative return means // they were not, and are still the caller's to close. There is no third case, // which is deliberate -- the attach happens under the same lock hold as the // first byte, so no reader can slip between them. int sockwrite_fds(struct socket *s, char *addr, int n, struct file **f, int nf) { struct socket *dst; int i; if(n < 0) return -EINVAL; if(nf > 0 && (s->domain != AF_UNIX || n == 0)) return -EINVAL; if(s->domain == AF_INET) return inet_write(s->inet, addr, n); // A netlink request is answered immediately, into this socket's own buffer // rather than a peer's -- there is no peer. Doing the work here is what lets // the read that follows never block, which matters because musl reads with // MSG_DONTWAIT and treats a short read as the end of the dump. if(s->domain == AF_NETLINK){ acquire(&stab.lock); s->nread = s->nwrite = 0; int got = netlink_request(addr, (uint)n, s->data, SOCKBUF); if(got > 0) s->nwrite = (uint)got; release(&stab.lock); return n; /* the request was consumed whole */ } acquire(&stab.lock); // Data goes into the peer's buffer, because that is where the peer will // look for it. Same contract as a pipe with no reader: a signal, then an // error. A process that writes to a dead connection and is not told about // it spins forever believing it is making progress. if(s->peer == 0 || s->wr_shut){ release(&stab.lock); sigsend(myproc()->pid, SIGPIPE); return -EPIPE; } // Refuse early and cleanly rather than part-way through the bytes. A sender // that is this far ahead of its receiver is not helped by a partial message. if(nf > 0 && s->peer->nscm >= NSCM){ release(&stab.lock); return -ENOBUFS; } for(i = 0; i < n; i++){ // Re-read every iteration: the peer can go away while we sleep below. dst = s->peer; while(dst && dst->nwrite == dst->nread + SOCKBUF){ if(myproc()->killed){ release(&stab.lock); return i > 0 ? i : -EINTR; } wakeup(&dst->nread); sleep(&dst->nwrite, &stab.lock); dst = s->peer; } if(dst == 0){ release(&stab.lock); sigsend(myproc()->pid, SIGPIPE); return i > 0 ? i : -EPIPE; // a short write is still a write } // The descriptors ride at the position of the first byte, pinned here -- // between the room check above and the store below, with the lock held // throughout, so the position cannot be read past before it exists. if(i == 0 && nf > 0){ if(dst->nscm >= NSCM){ release(&stab.lock); return -ENOBUFS; } struct scm_fds *b = &dst->scm[dst->nscm++]; b->pos = dst->nwrite; b->n = nf; for(int j = 0; j < nf; j++) b->f[j] = f[j]; } dst->data[dst->nwrite++ % SOCKBUF] = addr[i]; } wakeup(&s->peer->nread); release(&stab.lock); // poll() and epoll waiters, now rather than at the next tick: an X client // and its server talk over these, a request and a reply at a time. poll_kick(); return i; } int sockwrite(struct socket *s, char *addr, int n) { return sockwrite_fds(s, addr, n, 0, 0); } // --------------------------------------------------------------------------- // Connection setup // --------------------------------------------------------------------------- struct socket * socknew(int domain, int type, int proto) { struct socket *s; if(domain != AF_UNIX && domain != AF_INET && domain != AF_NETLINK) return 0; // AF_UNIX is stream-only here; AF_INET has datagrams too, and inet_new is // what decides whether it can serve the type asked for. if(domain == AF_UNIX && type != SOCK_STREAM) return 0; // AF_NETLINK carries whole messages and musl asks for SOCK_RAW; SOCK_DGRAM is // accepted too because Linux does. NETLINK_ROUTE is the only protocol served // -- see kernel/net/netlink.c, which answers RTM_GETLINK and RTM_GETADDR and // nothing else. if(domain == AF_NETLINK){ if(type != SOCK_RAW && type != SOCK_DGRAM) return 0; if(proto != NETLINK_ROUTE) return 0; } acquire(&stab.lock); s = alloc_locked(type); if(s) s->domain = domain; release(&stab.lock); if(s == 0) return 0; if(domain == AF_INET){ // Outside the table lock: inet_new takes the lwIP lock, which is a // sleeplock, and sleeping while holding a spinlock is a panic. s->inet = inet_new(type, proto); if(s->inet == 0){ sockclose(s); return 0; } } return s; } // A connected pair with no name and no listener, for socketpair(2). int sockpair(struct socket **pa, struct socket **pb) { struct socket *a, *b; acquire(&stab.lock); if((a = alloc_locked(SOCK_STREAM)) == 0){ release(&stab.lock); return -ENFILE; } if((b = alloc_locked(SOCK_STREAM)) == 0){ a->state = SS_FREE; a->ref = 0; release(&stab.lock); return -ENFILE; } // The domain was never set here, and nothing noticed for as long as every // data path asked only whether it was AF_INET or AF_NETLINK -- a socketpair // socket answered "neither" by being zero. SCM_RIGHTS asks the other // question, "is this AF_UNIX", and zero is the wrong answer to that one. a->domain = AF_UNIX; b->domain = AF_UNIX; a->peer = b; a->state = SS_CONNECTED; b->peer = a; b->state = SS_CONNECTED; release(&stab.lock); *pa = a; *pb = b; return 0; } // bind(2) for AF_UNIX. The caller has already created the filesystem node and // hands over its identity; see sys_bind() in kernel/sys/sys_net.c for why the // creation lives there and not here. // // The name used to live only in this table, so `bind()` created nothing -- // there was no file to stat(), no `s` in `ls -l`, and nothing to unlink. A // Wayland client finds its compositor by bind()ing a real path, and // libwayland locks the file beside it; a name table can express neither. // // **The node is a marker, not the binding.** It holds no data. What makes a // socket findable is that connect() resolves the path to an inode and looks // for the socket bound to *that inode*, so any spelling of the path arrives // at the same place -- which a string comparison could never manage. int sockbind(struct socket *s, const char *path, uint dev, uint64 ino) { if(path[0] == '\0') return -EINVAL; acquire(&stab.lock); if(s->state != SS_UNBOUND){ release(&stab.lock); return -EINVAL; } safestrcpy(s->name, path, UNIX_PATH_MAX); s->bind_dev = dev; s->bind_ino = ino; s->state = SS_BOUND; release(&stab.lock); return 0; } // The abstract namespace: a sun_path that starts with a NUL names no file. // Linux keeps these names in the socket layer, and Xorg binds one before its // /tmp/.X11-unix path -- which this used to read as the empty path, resolve // to the directory it was called from and refuse with EADDRINUSE, so Xorg // reported "server already running" and exited. // // The name becomes a key on a device number no filesystem has, and the rest // is the inode machinery unchanged: connect() finds the listener bound to // (SOCK_ABSTRACT_DEV, key). The name goes when the socket does, because // nothing else holds it. int sockbind_abstract(struct socket *s, uint64 key) { acquire(&stab.lock); if(s->state != SS_UNBOUND){ release(&stab.lock); return -EINVAL; } for(struct socket *o = stab.sock; o < stab.sock + NSOCK; o++) if(o != s && o->state != SS_FREE && o->state != SS_UNBOUND && o->bind_dev == SOCK_ABSTRACT_DEV && o->bind_ino == key){ release(&stab.lock); return -EADDRINUSE; } s->name[0] = '\0'; // getsockname reports it unnamed, not as a path s->bind_dev = SOCK_ABSTRACT_DEV; s->bind_ino = key; s->state = SS_BOUND; release(&stab.lock); return 0; } // The path for getsockname (peer 0) or getpeername (peer 1) of an AF_UNIX // socket. Empty for an unnamed one -- a client that never bound, or an // abstract name, which the caller reports as the family alone. int sockname_inet(struct socket *s, int peer, uint *addr, ushort *port) { return inet_name(s->inet, peer, addr, port); } int sockname_unix(struct socket *s, int peer, char *path) { acquire(&stab.lock); struct socket *o = peer ? s->peer : s; if(o == 0){ release(&stab.lock); return -ENOTCONN; } safestrcpy(path, o->name, UNIX_PATH_MAX); release(&stab.lock); return 0; } int socklisten(struct socket *s, int backlog) { if(s->domain == AF_INET) return inet_listen(s->inet, backlog); (void)backlog; // the queue is a fixed NBACKLOG either way acquire(&stab.lock); if(s->state != SS_BOUND && s->state != SS_LISTENING){ release(&stab.lock); return -EINVAL; } s->state = SS_LISTENING; release(&stab.lock); return 0; } // Returns the server-side socket of a new connection with a reference already // held, or 0 on error. struct socket * sockaccept(struct socket *s) { struct socket *c; int i; if(s->domain == AF_INET){ // The connection lwIP hands back needs a table entry of its own, because // everything above this layer works in `struct socket` and knows nothing // about lwIP. void *h = inet_accept(s->inet); if(h == 0) return 0; acquire(&stab.lock); c = alloc_locked(s->type); if(c){ c->domain = AF_INET; c->state = SS_CONNECTED; } release(&stab.lock); if(c == 0){ inet_close(h); return 0; } c->inet = h; return c; } acquire(&stab.lock); if(s->state != SS_LISTENING){ release(&stab.lock); return 0; } while(s->nqueue == 0){ if(myproc()->killed || s->state != SS_LISTENING){ release(&stab.lock); return 0; } sleep(&s->nqueue, &stab.lock); } c = s->queue[0]; for(i = 0; i + 1 < s->nqueue; i++) s->queue[i] = s->queue[i+1]; s->nqueue--; release(&stab.lock); return c; } // connect(2) for AF_UNIX. The listener is the socket bound to the inode the // path resolved to -- the caller did the resolving, for the same reason // sockbind() does not create the node. int sockconnect(struct socket *s, uint dev, uint64 ino) { struct socket *srv, *peer; acquire(&stab.lock); if(s->state != SS_UNBOUND && s->state != SS_BOUND){ release(&stab.lock); return -EINVAL; } srv = find_listener_ino(dev, ino); if(srv == 0){ release(&stab.lock); return -ECONNREFUSED; } if(srv->nqueue >= NBACKLOG){ release(&stab.lock); return -EAGAIN; } // Every connection needs a socket at each end. The listener is not one of // them -- it stays listening, and accept() hands back this new one. peer = alloc_locked(s->type); if(peer == 0){ release(&stab.lock); return -ENFILE; } peer->peer = s; peer->state = SS_CONNECTED; peer->cred_pid = srv->cred_pid; peer->cred_uid = srv->cred_uid; peer->cred_gid = srv->cred_gid; s->peer = peer; s->state = SS_CONNECTED; srv->queue[srv->nqueue++] = peer; wakeup(&srv->nqueue); // an accept() may be blocked on this release(&stab.lock); poll_kick(); // or a listener in poll(), as Xorg's is // Deliberately does *not* wait for accept(). On Unix a queued connection is // already established: the client may write, and the bytes sit in the // server-side buffer until somebody accepts and reads them. Blocking here // instead would deadlock any server that connects to itself, and would make // a busy server's backlog useless. return 0; } // shutdown(2). There is one buffer per direction and no per-direction flag, // so a half-close is approximated: SHUT_RD stops this end reading anything // further, and SHUT_WR/SHUT_RDWR give the peer end-of-file. What programs // actually use shutdown for -- "I have finished sending, read until EOF and // then reply" -- works; what is missing is the ability to keep reading after // SHUT_WR, since the connection is torn down rather than half-closed. int sock_shutdown(struct socket *s, int how) { if(s->domain == AF_INET) return inet_shutdown(s->inet, how); acquire(&stab.lock); if(s->state != SS_CONNECTED){ release(&stab.lock); return -ENOTCONN; } if(how == SHUT_RD || how == SHUT_RDWR){ s->peer_closed = 1; wakeup(&s->nread); } if(how == SHUT_RDWR) orphan_peer(s); else if(how == SHUT_WR){ // Half-close: the peer sees EOF once it has read what is there, and the // link stays, so its reply still reaches us. Cutting the link here made // the reply a SIGPIPE -- busybox wget does exactly this to ssl_client. s->wr_shut = 1; if(s->peer){ s->peer->peer_closed = 1; wakeup(&s->peer->nread); } } release(&stab.lock); poll_kick(); return 0; } // --------------------------------------------------------------------------- // Readiness, for poll/select // --------------------------------------------------------------------------- int sockreadable(struct socket *s) { if(s->domain == AF_INET) return inet_readable(s->inet); if(s->state == SS_LISTENING) return s->nqueue > 0; return s->nread != s->nwrite || s->peer_closed; } // The peer is gone and everything it sent has been read. int sock_hup(struct socket *s) { if(s->domain == AF_INET) return inet_hup(s->inet); if(s->state == SS_LISTENING) return 0; return s->peer == 0 && s->nread == s->nwrite; } /* ------------------------------------------------------------------ */ /* AF_INET addressing */ /* */ /* Separate entry points rather than an overloaded sockbind, because */ /* the two families do not name the same kind of thing: one takes a */ /* path, the other an address and a port. The syscall layer picks by */ /* the family in the sockaddr the caller passed. */ /* ------------------------------------------------------------------ */ int sockbind_in(struct socket *s, uint addr, ushort port) { if(s->domain != AF_INET) return -EAFNOSUPPORT; int r = inet_bind(s->inet, addr, port); if(r == 0){ acquire(&stab.lock); s->state = SS_BOUND; release(&stab.lock); } return r; } int sockconnect_in(struct socket *s, uint addr, ushort port) { if(s->domain != AF_INET) return -EAFNOSUPPORT; int r = inet_connect(s->inet, addr, port); if(r == 0){ acquire(&stab.lock); s->state = SS_CONNECTED; release(&stab.lock); } return r; } // Which family a socket belongs to. An accessor because kernel/sys/sys_net.c is // the translation layer and deliberately does not see inside struct socket -- // it works in descriptors and user addresses, not in the socket's guts. int sock_domain(struct socket *s) { return s ? s->domain : 0; } // recvfrom and sendto, which differ from read and write only in carrying an // address. Both live here rather than in sys_net.c for the same reason as // sock_domain: the syscall layer must not know which side of the socket union // is live. // // `port` comes back in host byte order, matching what sockbind_in and // sockconnect_in take. The syscall layer converts on the way out, in the one // place that also builds the sockaddr. int sockrecvfrom(struct socket *s, char *buf, int n, uint *addr, ushort *port) { if(n < 0) return -EINVAL; if(s->domain == AF_INET) return inet_recvfrom(s->inet, buf, n, addr, port); // A Unix socket has no address of this shape. Saying so by leaving the // caller's address untouched, rather than inventing one, is what lets // recvfrom report an address length of zero -- which is exactly what a // program reading from an unnamed peer expects. return sockread(s, buf, n); } int socksendto(struct socket *s, char *buf, int n, uint addr, ushort port) { if(n < 0) return -EINVAL; if(s->domain == AF_INET) return inet_sendto(s->inet, buf, n, addr, port); if(addr) return -EAFNOSUPPORT; // an AF_INET destination on an AF_UNIX socket return sockwrite(s, buf, n); } int sock_passcred(struct socket *s) { return s->domain != AF_INET && s->o_passcred; } // SO_PEERCRED: the credentials recorded for the far end of a connected Unix // socket. -1 if there is no far end. int sock_peercred(struct socket *s, int *pid, uint *uid, uint *gid) { int r = -1; acquire(&stab.lock); if(s->domain != AF_INET && s->peer){ *pid = s->peer->cred_pid; *uid = s->peer->cred_uid; *gid = s->peer->cred_gid; r = 0; } release(&stab.lock); return r; }