diff options
Diffstat (limited to 'pflocal')
-rw-r--r-- | pflocal/sock.c | 227 |
1 files changed, 163 insertions, 64 deletions
diff --git a/pflocal/sock.c b/pflocal/sock.c index 492a90da..a95f77fd 100644 --- a/pflocal/sock.c +++ b/pflocal/sock.c @@ -23,21 +23,23 @@ /* ---------------------------------------------------------------- */ /* Returns the pipe that SOCK is reading from in PIPE, locked and with an - additional reference, or an error saying why it's not possible. A null - value may also be returned in PIPE with a 0 error, meaning that EOF should - be returned. SOCK mustn't be locked. */ + additional reference, or an error saying why it's not possible. SOCK + mustn't be locked. */ error_t sock_aquire_read_pipe (struct sock *sock, struct pipe **pipe) { error_t err; mutex_lock (&sock->lock); + *pipe = user->sock->read_pipe; - if (*pipe != NULL) - pipe_aquire (*pipe); /* Do this before unlocking the sock! */ - else if (! (sock->flags & SOCK_SHUTDOWN_READ)) - /* We only return ENOTCONN if a shutdown hasn't been performed. */ + assert (*pipe); /* A socket always has a read pipe. */ + + if (((*pipe)->flags & PIPE_BROKEN) && ! (sock->flags & SOCK_SHUTDOWN_READ)) err = ENOTCONN; + else + pipe_aquire (*pipe); + mutex_unlock (&sock->lock); return err; @@ -56,7 +58,12 @@ sock_aquire_write_pipe (struct sock *sock, struct pipe **pipe) if (*pipe != NULL) pipe_aquire (*pipe); /* Do this before unlocking the sock! */ else if (sock->flags & SOCK_SHUTDOWN_WRITE) + /* Writing on a socket with the write-half shutdown always acts as if the + pipe we're broken, even if the socket isn't connected yet. */ err = EPIPE; + else if (sock->pipe_class->flags & PIPE_CLASS_CONNECTIONLESS) + /* Connectionless protocols give a different error when unconnected. */ + err = EDESTADDRREQ; else err = ENOTCONN; mutex_unlock (&sock->lock); @@ -66,21 +73,32 @@ sock_aquire_write_pipe (struct sock *sock, struct pipe **pipe) /* ---------------------------------------------------------------- */ -/* Return a new socket with the given pipe ops in SOCK. */ +/* Return a new socket with the given pipe class in SOCK. */ error_t -sock_create (struct pipe_ops *pipe_ops, struct sock **sock) +sock_create (struct pipe_class *pipe_class, struct sock **sock) { + error_t err; static unsigned next_sock_id = 0; struct sock *new = malloc (sizeof (struct sock)); if (new == NULL) return ENOMEM; + /* A socket always has a read pipe, so create it here. */ + err = pipe_create (pipe_class, &new->read_pipe); + if (err) + { + free (new); + return err; + } + if (! (pipe_class->flags & PIPE_CLASS_CONNECTIONLESS)) + /* No data source yet. */ + new->read_pipe->flags |= PIPE_BROKEN; + new->refs = 0; - new->read_pipe = new->write_pipe = NULL; new->flags = 0; + new->write_pipe = NULL; new->id = next_sock_id++; - new->pipe_ops = pipe_ops; new->listenq = NULL; new->addr = NULL; bzero (&new->change_time, sizeof (new->change_time)); @@ -90,19 +108,54 @@ sock_create (struct pipe_ops *pipe_ops, struct sock **sock) return 0; } +/* Free SOCK, assuming there are no more handle on it. */ void sock_free (struct sock *sock) { - + /* sock_shutdown will get rid of the write pipe. */ + sock_shutdown (sock, SOCK_SHUTDOWN_READ | SOCK_SHUTDOWN_WRITE); + + /* But we must do the read pipe ourselves. */ + pipe_release (sock->read_pipe); + + free (sock); +} + +/* Remove a reference from SOCK, possibly freeing it. */ +void +sock_unref (struct sock *sock) +{ + mutex_lock (&sock->lock); + if (--sock->refs == 0) + sock_free (sock); + else + mutex_unlock (&sock->lock); } +/* ---------------------------------------------------------------- */ + +static struct port_class *sock_user_port_class = NULL; + +/* Get rid of a user reference to a socket. */ +static void +clean_sock_user (void *vuser) +{ + struct sock_user *user = vuser; + sock_unref (user->sock); +} + /* Return a new user port on SOCK in PORT. */ error_t sock_create_port (struct sock *sock, mach_port_t *port) { - struct sock_user *user = - port_allocate_port (sock_user_bucket, - sizeof (struct sock_user), sock_user_class); + struct sock_user *user; + + if (sock_user_port_class == NULL) + sock_user_port_class = ports_create_class (NULL, clean_sock_user); + + user = + port_allocate_port (pflocal_port_bucket, + sizeof (struct sock_user), sock_user_port_class); if (!user) return ENOMEM; @@ -131,53 +184,112 @@ error_t sock_connect (struct sock *sock1, struct sock *sock2) { error_t err = 0; - struct pipe_class *pipe_class = sock1->pipe_class; + /* In the case of a connectionless protocol, an already-connected socket may + be reconnected elsewhere, so we save the old write pipe for later + disposal. */ + struct pipe *old_sock1_write_pipe = NULL; + struct pipe_class *pipe_class = sock1->read_pipe->pipe_class; + /* True if this protocol is a connectionless one. */ + int connless = (pipe_class->flags & PIPE_CLASS_CONNECTIONLESS); + + int connected (struct sock *s) + { + /* A socket is considered connected if it has a write pipe or a + non-broken read-pipe. */ + return s->write_pipe != NULL || ! (s->read_pipe->flags & PIPE_BROKEN); + } + void connect (struct sock *wr, struct sock *rd) + { + if ((wr->flags & SOCK_SHUTDOWN_WRITE) + || (rd->flags & SOCK_SHUTDOWN_READ)) + { + struct pipe *pipe = rd->read_pipe; + pipe_aquire (pipe); + pipe->flags &= ~PIPE_BROKEN; /* Not yet... */ + wr->write_pipe = pipe; + mutex_unlock (&pipe->lock); + } + } - if (sock2->pipe_class != pipe_class) + if (sock2->read_pipe->pipe_class != pipe_class) + /* Incompatible socket types. */ return EOPNOTSUPP; /* XXX?? */ mutex_lock (&connect_lock); mutex_lock (&sock1->lock); mutex_lock (&sock2->lock); - if ((sock1->flags & SOCK_CONNECTED) || (sock2->flags & SOCK_CONNECTED)) + if (!connless && (connected (sock1) || connected (sock2))) err = EISCONN; else { - struct pipe *pipe1, *pipe2; + old_sock1_write_pipe = sock1->write_pipe; - /* Make one direction.... */ - if ((sock1->flags & SOCK_SHUTDOWN_WRITE) - || (sock2->flags & SOCK_SHUTDOWN_READ)) - pipe1 = NULL; - else - err = pipe_create (pipe_class, &pipe1); + /* We always try and make the forward connection. */ + connect (sock1, sock2); - /* Then the other... */ - if (!err) - { - if ((sock2->flags & SOCK_SHUTDOWN_WRITE) - || (sock1->flags & SOCK_SHUTDOWN_READ)) - pipe2 = NULL; - else - err = pipe_create (pipe_class, &pipe2); - - if (err) - pipe_free (pipe1); - else - { - sock1->write_pipe = sock2->read_pipe = pipe1; - sock2->write_pipe = sock1->read_pipe = pipe2; - } - } + /* We only make the backward connection for connection-oriented + protocols. */ + if (! (pipe_class->flags & PIPE_CLASS_CONNECTIONLESS)) + connect (sock2, sock1); } mutex_unlock (&sock2->lock); mutex_unlock (&sock1->lock); mutex_unlock (&connect_lock); + if (old_sock1_write_pipe) + /* Discard SOCK1's previous write pipe. */ + pipe_discard (old_sock1_write_pipe); + + return err; +} + +/* ---------------------------------------------------------------- */ + +/* Bind SOCK to ADDR. */ +error_t +sock_bind (struct sock *sock, struct addr *addr) +{ + error_t err; + + mutex_lock (&sock->lock); + + if (sock->addr) + if (addr) + err = EINVAL; /* Already bound. */ + else + err = addr_set_sock (sock->addr, NULL); + else + if (addr) + err = addr_set_sock (addr, sock); + + if (!err) + sock->addr = addr; + + mutex_unlock (&sock->lock); + + return err; +} + +/* Returns SOCK's address in ADDR. If SOCK doesn't currently have an + address, one is fabricated first. */ +error_t +sock_get_addr (struct sock *sock, struct addr **addr) +{ + error_t err; + mutex_lock (&sock->lock); + if (sock->addr == NULL) + err = addr_create (&sock->addr); + *addr = sock->addr; + mutex_unlock (&sock->lock); return err; } + +error_t +sock_get_peer_addr (struct sock *sock, struct addr **addr) +{ +} /* ---------------------------------------------------------------- */ @@ -189,16 +301,15 @@ sock_shutdown (struct sock *sock, unsigned flags) sock->flags |= flags; if (which & SOCK_SHUTDOWN_READ) - /* Shutdown the read half. */ + /* Shutdown the read half. We keep the pipe around though. */ { struct pipe *pipe = sock->read_pipe; - if (pipe != NULL) - { - sock->read_pipe = NULL; - mutex_lock (&pipe->lock); - pipe->flags |= PIPE_BROKEN; - pipe_release (pipe); /* Unlock PIPE and get rid of SOCK's ref. */ - } + mutex_lock (&pipe->lock); + /* This will prevent any further writes to PIPE. */ + pipe->flags |= PIPE_BROKEN; + /* Make sure subsequent reads return EOF. */ + pipe_drain (pipe); + mutex_unlock (&pipe->lock); } if (which & SOCK_SHUTDOWN_WRITE) @@ -208,22 +319,9 @@ sock_shutdown (struct sock *sock, unsigned flags) if (pipe != NULL) { sock->write_pipe = NULL; - - mutex_lock (&pipe->lock); - - /* As there may be multiple writers on a connectionless socket, we - never allow EOF to be signaled on the reader. */ - if (! (pipe->pipe_class->flags & PIPE_CLASS_CONNECTIONLESS)) - pipe->flags |= PIPE_DRY; - /* Unlock SOCK here, as we may subsequently wake up other threads. */ mutex_unlock (&sock->lock); - - if (pipe->refs > 1) - /* Other references to PIPE besides ours? Wake 'em up. */ - pipe_kick (pipe); - - pipe_release (pipe); + pipe_discard (pipe); } else mutex_unlock (&sock->lock); @@ -231,3 +329,4 @@ sock_shutdown (struct sock *sock, unsigned flags) else mutex_unlock (&sock->lock); } + |