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); } }
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; }
/* transport layer thread; transport_init() should not return until the * transport layer finishes (i.e. the connection is over). */ static void *transport_thread_func(void *arg_ptr) { mysock_context_t *ctx = (mysock_context_t *) arg_ptr; char eof_packet; assert(ctx); ASSERT_VALID_MYSOCKET_DESCRIPTOR(ctx, ctx->my_sd); /* enter the STCP control loop. transport_init() doesn't return until the * connection's finished. that function should first signal establishment * of the connection after SYN/SYN-ACK (or an error condition if the * connection couldn't be established) to the application by using * stcp_unblock_application(); as the name suggests, this unblocks the * calling code. transport_init() then handles the connection, * returning only after the connection is closed. */ transport_init(ctx->my_sd, ctx->is_active); /* transport_init() has returned; both sides have closed the connection, * do some final cleanup here... */ PTHREAD_CALL(pthread_mutex_lock(&ctx->blocking_lock)); if (ctx->blocking) { /* if we're still blocked, STCP must not have indicated the * connection completed. pass the error up to the application. */ if (errno == 0 || errno == EINTR) { /* this is a bit of a kludge--this should really be set by STCP * itself, but it's a reasonable guess if for some reason (e.g. * oversight) the transport layer hasn't announced why it * bailed out... */ errno = (ctx->is_active) ? ECONNREFUSED : ECONNABORTED; } PTHREAD_CALL(pthread_mutex_unlock(&ctx->blocking_lock)); stcp_unblock_application(ctx->my_sd); } else { PTHREAD_CALL(pthread_mutex_unlock(&ctx->blocking_lock)); } /* force final myread() to return 0 bytes (this should have been done * by the transport layer already in response to the peer's FIN). */ _mysock_enqueue_buffer(ctx, &ctx->app_send_queue, &eof_packet, 0); return NULL; }
/* 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 }