/* 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; }
static void _debug_print_connection(const char *msg, const char *reason, const mysock_context_t *ctx, const struct sockaddr *peer_addr) { assert(msg && reason && ctx && peer_addr); assert(peer_addr->sa_family == AF_INET); DEBUG_LOG(("%s from %s:%hu for local port %hu %s\n", msg, inet_ntoa(((struct sockaddr_in *) peer_addr)->sin_addr), ntohs(((struct sockaddr_in *) peer_addr)->sin_port), ntohs(_network_get_port((network_context_t *) &ctx->network_state)), reason)); }
/* called by mylisten() to specify the number of pending connection * requests permitted for a listening socket. a backlog of zero * specifies at most one pending connection is permitted for the socket. */ void _mysock_set_backlog(mysock_context_t *ctx, unsigned int backlog) { unsigned int k, max_len = backlog + 1; uint16_t local_port; listen_queue_t *q; assert(ctx && ctx->listening && ctx->bound); local_port = ntohs(_network_get_port(&ctx->network_state)); assert(local_port > 0); PTHREAD_CALL(pthread_rwlock_wrlock(&listen_lock)); if ((q = _get_connection_queue(ctx)) == NULL) { /* first backlog specified for new listening socket */ DEBUG_LOG(("allocating connection queue for local port %hu\n", local_port)); q = (listen_queue_t *) calloc(1, sizeof(listen_queue_t)); assert(q); q->local_port = local_port; PTHREAD_CALL(pthread_cond_init(&q->connection_cond, NULL)); PTHREAD_CALL(pthread_mutex_init(&q->connection_lock, NULL)); HASH_INSERT(listen_table, ctx->my_sd, q); } assert(q); assert(q->local_port == local_port); if (max_len > q->max_len) { q->connection_queue = (connect_request_t *) realloc(q->connection_queue, max_len * sizeof(connect_request_t)); assert(q->connection_queue); memset(q->connection_queue + q->max_len, 0, (max_len - q->max_len) * sizeof(connect_request_t)); } for (k = q->max_len; k < max_len; ++k) q->connection_queue[k].sd = -1; q->max_len = max_len; 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); }
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; }