Exemple #1
0
/* fills in addr with current port associated with the mysocket descriptor.
 * like the regular getsockname(), this does not fill in the local IP
 * address unless it's known.
 */
int mygetsockname(mysocket_t sd, struct sockaddr *addr, socklen_t *addrlen)
{
    mysock_context_t *ctx = _mysock_get_context(sd);

    assert(addr && addrlen);

    MYSOCK_CHECK(ctx != NULL, EBADF);
    MYSOCK_CHECK(addr != NULL && addrlen != NULL, EFAULT);

    *addr = ctx->network_state.local_addr;
    assert(!addr->sa_family || addr->sa_family == AF_INET);

    addr->sa_family = AF_INET;
    ((struct sockaddr_in *) addr)->sin_port =
        _network_get_port(&ctx->network_state);

    if (ctx->network_state.peer_addr_valid)
    {
        /* XXX: if local address has been bound, and local_addr is set,
         * we probably shouldn't override it here... although this
         * shouldn't really affect anything...
         */
        ((struct sockaddr_in *) addr)->sin_addr.s_addr =
            _network_get_local_addr(&ctx->network_state);
    }

    return 0;
}
Exemple #2
0
/* initiate a new STCP connection; called by myconnect() and myaccept() */
void _mysock_transport_init(mysocket_t sd, bool_t is_active)
{
    mysock_context_t *connection_context = _mysock_get_context(sd);

    assert(!connection_context->listening);
    connection_context->is_active = is_active;

    /* start a new network thread; this handles incoming data, passing it
     * up to the transport layer.  (the network input is threaded so we can
     * keep track of timeouts/when data arrives, in a portable manner
     * independent of the underlying network I/O functionality).
     */
    if (_network_start_recv_thread(connection_context) < 0)
    {
        assert(0);
        abort();
    }

    /* start a new transport layer thread */
    connection_context->transport_thread = _mysock_create_thread(
        transport_thread_func,
        connection_context,
        FALSE);
    connection_context->transport_thread_started = TRUE;
}
Exemple #3
0
/* in this implementation, mylisten() is assumed to follow mybind() */
int mylisten(mysocket_t sd, int backlog)
{
    mysock_context_t *ctx = _mysock_get_context(sd);

    assert(ctx->bound);

    MYSOCK_CHECK(ctx != NULL, EBADF);
    MYSOCK_CHECK(ctx->bound, EINVAL);

    /* set up the socket for demultiplexing */
    ctx->listening = TRUE;
    _mysock_set_backlog(ctx, backlog);

    if (_network_listen(&ctx->network_state, backlog) < 0)
        return -1;

    /* since we don't spawn an STCP worker thread for passive sockets
     * (there's no transport layer related work to do, so
     * _mysock_transport_init() is never called for such sockets), we
     * begin receiving network packets here...
     */
    if (_network_start_recv_thread(ctx) < 0)
    {
        assert(0);
        return -1;
    }

    return 0;
}
void stcp_fin_received(mysocket_t sd)
{
    mysock_context_t *ctx = _mysock_get_context(sd);
    assert(ctx);
    DEBUG_LOG(("stcp_fin_received(%d):  setting eof flag\n", sd));
    _mysock_enqueue_buffer(ctx, &ctx->app_send_queue, NULL, 0);
}
/* pass data up to the application for consumption by myread() */
void stcp_app_send(mysocket_t sd, const void *src, size_t src_len)
{
    mysock_context_t *ctx = _mysock_get_context(sd);
    assert(ctx && src);
    if (src_len > 0)
    {
        DEBUG_LOG(("stcp_app_send(%d):  sending %u bytes up to app\n",
                   sd, src_len));
        _mysock_enqueue_buffer(ctx, &ctx->app_send_queue, src, src_len);
    }
}
/* receive data from the application (sent to us using mywrite()).
 * the call blocks until data is available.
 */
size_t stcp_app_recv(mysocket_t sd, void *dst, size_t max_len)
{
    mysock_context_t *ctx = _mysock_get_context(sd);
    assert(ctx && dst);

    /* app may have passed in data of arbitrary length; all of it must be
     * passed down to the transport layer.  if it doesn't fit in the specified
     * buffer, any left over is kept for the next call to app_recv().
     */
    return _mysock_dequeue_buffer(ctx, &ctx->app_recv_queue,
                                  dst, max_len, TRUE);
}
Exemple #7
0
int mywrite(mysocket_t sd, const void *buf, size_t buf_len)
{
    mysock_context_t *ctx = _mysock_get_context(sd);

    MYSOCK_CHECK(ctx != NULL, EBADF);
    MYSOCK_CHECK(!ctx->listening, EINVAL);

    assert(!ctx->close_requested);
    _mysock_enqueue_buffer(ctx, &ctx->app_recv_queue, buf, buf_len);

    /* XXX: all bytes are queued, irrespective of current sender window */
    return buf_len;
}
Exemple #8
0
int mygetpeername(mysocket_t sd, struct sockaddr *name, socklen_t *namelen)
{
    mysock_context_t *ctx = _mysock_get_context(sd);

    assert(name && namelen);
    MYSOCK_CHECK(name != NULL && namelen != NULL, EFAULT);

    memcpy(name, &ctx->network_state.peer_addr,
           MIN(*namelen, (socklen_t)ctx->network_state.peer_addr_len));

    MYSOCK_CHECK((*namelen = ctx->network_state.peer_addr_len) > 0, ENOTCONN);
    return 0;
}
Exemple #9
0
/* simply a wrapper around bind() */
int mybind(mysocket_t sd, struct sockaddr *addr, int addrlen)
{
    mysock_context_t *ctx = _mysock_get_context(sd);
    assert(addr);

    MYSOCK_CHECK(ctx != NULL, EBADF);
    MYSOCK_CHECK(addr->sa_family == AF_INET, EADDRNOTAVAIL);

    /* mybind() must precede mylisten() */
    assert(!ctx->listening);
    ctx->bound = TRUE;
    ctx->network_state.local_addr = *addr;
    return _network_bind(&ctx->network_state, addr, addrlen);
}
Exemple #10
0
void _mysock_passive_connection_complete(mysock_context_t *ctx)
{
    listen_queue_t *q;

    assert(ctx);

    PTHREAD_CALL(pthread_rwlock_rdlock(&listen_lock));
    assert(ctx->listen_sd >= 0);
    if ((q = _get_connection_queue(_mysock_get_context(ctx->listen_sd))))
    {
        completed_connect_t *tail, *new_entry;
        connect_request_t *connection_req = NULL;
        unsigned int k;

        /* find this connection in the incomplete connection queue */
        for (k = 0; k < q->max_len && !connection_req; ++k)
        {
            if (q->connection_queue[k].sd == ctx->my_sd)
                connection_req = &q->connection_queue[k];
        }

        assert(connection_req);

        new_entry = (completed_connect_t *)malloc(sizeof(completed_connect_t));
        assert(new_entry);

        new_entry->request = connection_req;
        new_entry->next = NULL;

        PTHREAD_CALL(pthread_mutex_lock(&q->connection_lock));

        /* add established connection to tail of completed connection queue */
        for (tail = q->completed_queue; tail && tail->next; tail = tail->next)
            ;

        if (tail)
            tail->next = new_entry;
        else
            q->completed_queue = new_entry;

        PTHREAD_CALL(pthread_mutex_unlock(&q->connection_lock));
        PTHREAD_CALL(pthread_cond_signal(&q->connection_cond));
    }
    PTHREAD_CALL(pthread_rwlock_unlock(&listen_lock));
}
Exemple #11
0
/* called by myaccept() to grab the first completed connection off the
 * given mysocket's connection queue, or block until one completes.
 */
void _mysock_dequeue_connection(mysock_context_t  *accept_ctx,
                                mysock_context_t **new_ctx)
{
    listen_queue_t *q;
    completed_connect_t *r;

    assert(accept_ctx && new_ctx);
    assert(accept_ctx->listening && accept_ctx->bound);

    DEBUG_LOG(("waiting for new connection...\n"));
    PTHREAD_CALL(pthread_rwlock_rdlock(&listen_lock));
    q = _get_connection_queue(accept_ctx);
    assert(q);

    PTHREAD_CALL(pthread_mutex_lock(&q->connection_lock));
    while (!q->completed_queue)
    {
        PTHREAD_CALL(pthread_cond_wait(&q->connection_cond,
                                       &q->connection_lock));
    }

    r = q->completed_queue;
    q->completed_queue = q->completed_queue->next;

    DEBUG_LOG(("dequeueing established connection from %s:%hu\n",
               inet_ntoa(((struct sockaddr_in *)
                          &r->request->peer_addr)->sin_addr),
               ntohs(((struct sockaddr_in *)
                      &r->request->peer_addr)->sin_port)));

    assert(r->request);
    *new_ctx = _mysock_get_context(r->request->sd);
    assert(*new_ctx);

    /* free up this entry from the listen queue */
    INVALIDATE_CONNECT_REQUEST(r->request);
    memset(r, 0, sizeof(*r));
    free(r);

    assert(q->cur_len > 0);
    --q->cur_len;

    PTHREAD_CALL(pthread_mutex_unlock(&q->connection_lock));
    PTHREAD_CALL(pthread_rwlock_unlock(&listen_lock));
}
/* stcp_network_send()
 *
 * Send data (unreliably) to the peer.
 *
 * sd           Mysocket descriptor
 * src          A pointer to the data to send
 * src_len      The length in bytes of the buffer
 *
 * This function takes data in multiple segments and sends them as a single
 * UDP datagram. The buffer and length parameters may be repeated an arbitrary
 * number of times; a NULL pointer signifies the end of buffer/length pairs.
 * For example:
 *
 * stcp_network_send(mysd, buf1, len1, buf2, len2, NULL);
 *
 * Unreliability is handled by a helper function (_network_send()); if we're
 * operating in unreliable mode, we decide in there whether to drop the
 * datagram or send it later.
 *
 * Returns the number of bytes transferred on success, or -1 on failure.
 *
 */
ssize_t stcp_network_send(mysocket_t sd, const void *src, size_t src_len, ...)
{
    mysock_context_t *ctx = _mysock_get_context(sd);
    char              packet[MAX_IP_PAYLOAD_LEN];
    size_t            packet_len;
    const void       *next_buf;
    va_list           argptr;
    struct tcphdr    *header;

    assert(ctx && src);

    assert(src_len <= sizeof(packet));
    memcpy(packet, src, src_len);
    packet_len = src_len;

    va_start(argptr, src_len);
    while ((next_buf = va_arg(argptr, const void *)))
    {
        size_t next_len = va_arg(argptr, size_t);

        assert(packet_len + next_len <= sizeof(packet));
        memcpy(packet + packet_len, next_buf, next_len);
        packet_len += next_len;
    }
    va_end(argptr);

    /* fill in fields in the TCP header that aren't handled by students */
    assert(packet_len >= sizeof(struct tcphdr));
    header = (struct tcphdr *) packet;

    header->th_sport = _network_get_port(&ctx->network_state);
    /* N.B. assert(header->th_sport > 0) fires in the UDP SYN-ACK case */

    assert(ctx->network_state.peer_addr.sa_family == AF_INET);
    header->th_dport =
        ((struct sockaddr_in *) &ctx->network_state.peer_addr)->sin_port;
    assert(header->th_dport > 0);

    header->th_sum = 0; /* set below */
    header->th_urp = 0; /* ignored */

    _mysock_set_checksum(ctx, packet, packet_len);
    return _network_send(sd, packet, packet_len);
}
/* TODO: pass in errno as argument.  several libc calls on Solaris set this
 * even on successful operation.
 */
void stcp_unblock_application(mysocket_t sd)
{
    mysock_context_t *ctx = _mysock_get_context(sd);

    /* pthread_mutex_lock sometimes sets errno even on successful operation */
    int stcp_errno = errno;

    PTHREAD_CALL(pthread_mutex_lock(&ctx->blocking_lock));
    assert(ctx->blocking);
    ctx->blocking = FALSE;
    if ((ctx->stcp_errno = stcp_errno) == EINTR)
        ctx->stcp_errno = 0;
    PTHREAD_CALL(pthread_mutex_unlock(&ctx->blocking_lock));
    PTHREAD_CALL(pthread_cond_signal(&ctx->blocking_cond));

    if (!ctx->is_active)
    {
        /* move from incomplete to completed connection queue */
        _mysock_passive_connection_complete(ctx);
    }
}
Exemple #14
0
/* connect to the address specified in name on the mysocket sd */
int myconnect(mysocket_t sd, struct sockaddr *name, int namelen)
{
    mysock_context_t *ctx = _mysock_get_context(sd);

    MYSOCK_CHECK(ctx != NULL, EINVAL);
    MYSOCK_CHECK((ctx->network_state.peer_addr_len == 0), EISCONN);

#ifdef DEBUG
    struct sockaddr_in *sin = (struct sockaddr_in *) name;
    fprintf(stderr, "\n####Initiating a new connection to %s:%u#### (sd=%d)\n",
            inet_ntoa(sin->sin_addr), ntohs(sin->sin_port), sd);
    fflush(stderr);
#endif  /*DEBUG*/

    ctx->network_state.peer_addr       = *name;
    ctx->network_state.peer_addr_len   = namelen;
    ctx->network_state.peer_addr_valid = TRUE;

    /* record connection setup for demultiplexing */
    if (!ctx->bound)
    {
        int rc;

        /* we need to find the local port number to set up demultiplexing
         * before we send the SYN.  (this is really only required in the VNS
         * case--we have to demultiplex only on listening sockets for the
         * UDP/TCP network layer--but it doesn't do any harm here in
         * general).
         */
        if ((rc = _mysock_bind_ephemeral(ctx)) < 0)
            return rc;
    }

    /* time for kick off */
    _mysock_transport_init(sd, TRUE);

    /* block until connection is established, or we hit an error */
    return _mysock_wait_for_connection(ctx);
}
Exemple #15
0
int myread(mysocket_t sd, void *buf, size_t buf_len)
{
    mysock_context_t *ctx = _mysock_get_context(sd);
    int len;

    MYSOCK_CHECK(ctx != NULL, EBADF);
    MYSOCK_CHECK(!ctx->listening, EINVAL);

    assert(!ctx->close_requested);

    if (ctx->eof)
        return 0;

    if ((len = _mysock_dequeue_buffer(ctx, &ctx->app_send_queue,
                                      buf, buf_len, TRUE)) == 0)
    {
        /* make sure repeated calls to myread() return 0 on EOF */
        ctx->eof = TRUE;
    }

    return len;
}
Exemple #16
0
mysocket_t myaccept(mysocket_t sd, struct sockaddr *addr, int *addrlen)
{
    mysock_context_t *accept_ctx = _mysock_get_context(sd);
    mysock_context_t *ctx;

    MYSOCK_CHECK(accept_ctx != NULL, EBADF);
    MYSOCK_CHECK(accept_ctx->listening, EINVAL);

#ifdef DEBUG
    fprintf(stderr, "\n####Accepting a new connection at port# %hu#### "
            "(sd=%d)\n",
            ntohs(_network_get_port(&accept_ctx->network_state)), sd);
    fflush(stderr);
#endif  /*DEBUG*/

    /* the new socket is created on an incoming SYN.  block here until we
     * establish a connection, or STCP indicates an error condition.
     */
    _mysock_dequeue_connection(accept_ctx, &ctx);
    assert(ctx);

    if (!ctx->stcp_errno)
    {
        /* fill in addr, addrlen with address of peer */
        assert(ctx->network_state.peer_addr_len > 0);

        if (addr && addrlen)
        {
            *addr    = ctx->network_state.peer_addr;
            *addrlen = ctx->network_state.peer_addr_len;
        }
    }

    assert(ctx->listen_sd == sd);
    DEBUG_LOG(("***myaccept(%d) returning new sd %d***\n", sd, ctx->my_sd));
    return (errno = ctx->stcp_errno) ? -1 : ctx->my_sd;
}
Exemple #17
0
/* close the given mysocket.  note that the semantics of myclose() differ
 * slightly from a regular close(); STCP doesn't implement WAIT_TIME, so
 * myclose() simply discards all knowledge of the connection once the
 * connection is terminated.
 */
int myclose(mysocket_t sd)
{
    mysock_context_t *ctx = _mysock_get_context(sd);

    DEBUG_LOG(("***myclose(%d)***\n", sd));
    MYSOCK_CHECK(ctx != NULL, EBADF);

    /* stcp_wait_for_event() needs to wake up on a socket close request */
    PTHREAD_CALL(pthread_mutex_lock(&ctx->data_ready_lock));
    ctx->close_requested = TRUE;
    PTHREAD_CALL(pthread_mutex_unlock(&ctx->data_ready_lock));
    PTHREAD_CALL(pthread_cond_broadcast(&ctx->data_ready_cond));

    /* block until STCP thread exits */
    if (ctx->transport_thread_started)
    {
        assert(!ctx->listening);
        assert(ctx->is_active || ctx->listen_sd != -1);
        PTHREAD_CALL(pthread_join(ctx->transport_thread, NULL));
        ctx->transport_thread_started = FALSE;
    }

    _network_stop_recv_thread(ctx);

    if (ctx->listening)
    {
        /* remove entry from SYN demultiplexing table */
        _mysock_close_passive_socket(ctx);
    }

    /* free all resources associated with this mysocket */
    _mysock_free_context(ctx);

    DEBUG_LOG(("myclose(%d) returning...\n", sd));
    return 0;
}
/* allow STCP implementation to establish a context for a given mysocket
 * descriptor.  this context should contain any information that needs to be
 * tracked for the given mysocket, e.g. sequence numbers, retransmission
 * timers, etc.  it would typically be malloc()ed in transport_init() (or even
 * just allocated on the stack), and freed on connection close.
 */
void stcp_set_context(mysocket_t sd, const void *stcp_state)
{
    mysock_context_t *ctx = _mysock_get_context(sd);
    ctx->stcp_state = (void *) stcp_state;
}
/* called by the transport layer to wait for new data, either from the network
 * or from the application, or for the application to request that the
 * mysocket be closed, depending on the value of flags.  abstime is the
 * absolute time at which the function should quit waiting; if NULL, it blocks
 * indefinitely until data arrives.
 *
 * sd is the mysocket descriptor for the connection of interest.
 *
 * returns bit vector corresponding to application/network data being ready,
 * of the same format as the flags passed (see the enum in stcp_api.h).
 */
unsigned int stcp_wait_for_event(mysocket_t             sd,
                                 unsigned int           flags,
                                 const struct timespec *abstime)
{
    unsigned int rc = 0;
    mysock_context_t *ctx = _mysock_get_context(sd);

    PTHREAD_CALL(pthread_mutex_lock(&ctx->data_ready_lock));
    for (;;)
    {
        if ((flags & APP_DATA) && (ctx->app_recv_queue.head != NULL))
            rc |= APP_DATA;

        if ((flags & NETWORK_DATA) && (ctx->network_recv_queue.head != NULL))
            rc |= NETWORK_DATA;

        if (/*(flags & APP_CLOSE_REQUESTED) &&*/
            ctx->close_requested && (ctx->app_recv_queue.head == NULL))
        {
            /* we should only wake up on this event once.  also, we don't
             * pass the close event down to STCP until we've already passed
             * it all outstanding data from the app.
             */
            ctx->close_requested = FALSE;
            rc |= APP_CLOSE_REQUESTED;
        }

        if (rc)
            break;

        if (abstime)
        {
            /* wait with timeout */
            switch (pthread_cond_timedwait(&ctx->data_ready_cond,
                                           &ctx->data_ready_lock,
                                           abstime))
            {
            case 0: /* some data might be available */
            case EINTR:
                break;

            case ETIMEDOUT: /* no data arrived in the specified time */
                goto done;

            case EINVAL:
                assert(0);
                break;

            default:
                assert(0);
                break;
            }
        }
        else
        {
            /* block indefinitely */
            PTHREAD_CALL(pthread_cond_wait(&ctx->data_ready_cond,
                                           &ctx->data_ready_lock));
        }
    }

done:
    PTHREAD_CALL(pthread_mutex_unlock(&ctx->data_ready_lock));

    return rc;
}
Exemple #20
0
/* new connection requests for the given mysocket are queued to the
 * corresponding listen queue if one exists and there's sufficient
 * space, or dropped otherwise.  ctx is the context associated with
 * a mysocket for which myaccept() will be called (i.e., a listening
 * socket).
 *
 * returns TRUE if the new connection has been queued, FALSE otherwise.
 */
bool_t _mysock_enqueue_connection(mysock_context_t      *ctx,
                                  const void            *packet,
                                  size_t                 packet_len,
                                  const struct sockaddr *peer_addr,
                                  int                    peer_addr_len,
                                  void                  *user_data)
{
    listen_queue_t *q;
    connect_request_t *queue_entry = NULL;
    unsigned int k;

    assert(ctx && ctx->listening && ctx->bound);
    assert(packet && peer_addr);
    assert(peer_addr_len > 0);

#define DEBUG_CONNECTION_MSG(msg, reason) \
    _debug_print_connection(msg, reason, ctx, peer_addr)

    PTHREAD_CALL(pthread_rwlock_rdlock(&listen_lock));
    if (packet_len < sizeof(struct tcphdr) ||
        !(((struct tcphdr *) packet)->th_flags & TH_SYN))
    {
        DEBUG_CONNECTION_MSG("received non-SYN packet", "(ignoring)");
        goto done;  /* not a connection setup request */
    }

    if (!(q = _get_connection_queue(ctx)))
    {
        DEBUG_CONNECTION_MSG("dropping SYN packet", "(socket not listening)");
        goto done;  /* the socket was closed or not listening */
    }

    /* see if this is a retransmission of an existing request */
    for (k = 0; k < q->max_len; ++k)
    {
        connect_request_t *r = &q->connection_queue[k];

        assert(r->sd == -1 ||
               peer_addr_len == r->peer_addr_len);  /* both are sockaddr_in */
        if (!memcmp(&r->peer_addr, peer_addr, peer_addr_len))
        {
            DEBUG_CONNECTION_MSG("dropping SYN packet",
                                 "(retransmission of queued request)");
            goto done;  /* retransmission */
        }
    }

    /* if it's not a retransmission, find an empty slot in the incomplete
     * connection table
     */
    if (q->cur_len < q->max_len)
    {
        for (k = 0; k < q->max_len && !queue_entry; ++k)
        {
            if (q->connection_queue[k].sd < 0)
                queue_entry = &q->connection_queue[k];
        }

        assert(queue_entry);
        ++q->cur_len;
    }

    if (queue_entry)
    {
        mysock_context_t *new_ctx;

        /* establish the connection */
        assert(queue_entry->sd == -1);
        if ((queue_entry->sd =
             _mysock_new_mysocket(ctx->network_state.is_reliable)) < 0)
        {
            DEBUG_CONNECTION_MSG("dropping SYN packet",
                                 "(couldn't allocate new mysocket)");
            INVALIDATE_CONNECT_REQUEST(queue_entry);
            --q->cur_len;
            queue_entry = NULL;
            goto done;
        }

        new_ctx = _mysock_get_context(queue_entry->sd);
        new_ctx->listen_sd = ctx->my_sd;

        new_ctx->network_state.peer_addr       = *peer_addr;
        new_ctx->network_state.peer_addr_len   = peer_addr_len;
        new_ctx->network_state.peer_addr_valid = TRUE;

        queue_entry->peer_addr     = *peer_addr;
        queue_entry->peer_addr_len = peer_addr_len;
        queue_entry->user_data     = (void *) user_data;

        DEBUG_CONNECTION_MSG("establishing connection", "");

        /* update any additional network layer state based on the initial
         * packet, e.g. remapped sequence numbers, etc.
         */
        _network_update_passive_state(&new_ctx->network_state,
                                      &ctx->network_state,
                                      user_data, packet, packet_len);

        _mysock_transport_init(queue_entry->sd, FALSE);

        /* pass the SYN packet on to the main STCP code */
        _mysock_enqueue_buffer(new_ctx, &new_ctx->network_recv_queue,
                               packet, packet_len);
    }
    else
    {
        /* the packet is dropped (maximum backlog reached) */
        DEBUG_CONNECTION_MSG("dropping SYN packet", "(queue full)");
    }

done:
    PTHREAD_CALL(pthread_rwlock_unlock(&listen_lock));
    return (queue_entry != NULL);

#undef DEBUG_CONNECTION_MSG
}
void *stcp_get_context(mysocket_t sd)
{
    mysock_context_t *ctx = _mysock_get_context(sd);
    return ctx->stcp_state;
}