void minimsg_network_handler(network_interrupt_arg_t* arg) { interrupt_level_t old_level = set_interrupt_level(DISABLED); //disable interrupt //Get header and destination port mini_header_t receivedHeader; memcpy(&receivedHeader, arg->buffer, sizeof(mini_header_t)); int destPort = (int)unpack_unsigned_short(receivedHeader.destination_port); assert(destPort >= UNBOUNDED_PORT_START && destPort <= UNBOUNDED_PORT_END); // sanity checking //if the unbounded port has not been initialized, throw away the packet if (g_unboundedPortPtrs[destPort] == NULL) { set_interrupt_level(old_level); //restore interrupt level return; } //queue the packet and V the semaphore assert(g_unboundedPortPtrs[destPort]->port_type == 'u' && g_unboundedPortPtrs[destPort]->unbound_port.datagrams_ready != NULL && g_unboundedPortPtrs[destPort]->unbound_port.incoming_data != NULL); int appendSuccess = queue_append(g_unboundedPortPtrs[destPort]->unbound_port.incoming_data, (void*)arg); AbortOnCondition(appendSuccess == -1, "Queue_append failed in minimsg_network_handler()"); semaphore_V(g_unboundedPortPtrs[destPort]->unbound_port.datagrams_ready); set_interrupt_level(old_level); //restore interrupt level }
int minisocket_receive(minisocket_t *socket, char *msg, int max_len, minisocket_error *error) { if (socket == NULL || msg == NULL || max_len < 0) { *error = SOCKET_INVALIDPARAMS; return -1; } if (socket->state == CLOSED) { *error = SOCKET_RECEIVEERROR; return -1; } if (max_len == 0) { return 0; } int received = 0; while (!received) { semaphore_P(socket->datagrams_ready); network_interrupt_arg_t *interrupt_message = NULL; interrupt_level_t old_level = set_interrupt_level(DISABLED); queue_dequeue(socket->incoming_data, (void **) &interrupt_message); set_interrupt_level(old_level); if (interrupt_message != NULL) { mini_header_reliable_t* received_header = (mini_header_reliable_t *) interrupt_message->buffer; network_address_t temp_address; unpack_address(received_header->source_address, temp_address); if (socket->remote_port_number == unpack_unsigned_short(received_header->source_port) && network_compare_network_addresses(socket->remote_address, temp_address) != 0 && received_header->message_type == MSG_ACK && interrupt_message->size > sizeof(mini_header_reliable_t)) { //same address, same ports, right message received = 1; int data_left = interrupt_message->size - sizeof(mini_header_reliable_t) - socket->next_read; interrupt_level_t old_level = set_interrupt_level(DISABLED); if (data_left <= max_len) { memcpy(msg, interrupt_message->buffer + sizeof(mini_header_reliable_t) + socket->next_read, data_left); socket->next_read = 0; free(interrupt_message); set_interrupt_level(old_level); // must protect global data field next_read return data_left; } else { memcpy(msg, interrupt_message->buffer + sizeof(mini_header_reliable_t) + socket->next_read, max_len); socket->next_read += max_len; queue_prepend(socket->incoming_data, interrupt_message); //ack works as well when there is data semaphore_V(socket->datagrams_ready); //another message in queue, V semaphore set_interrupt_level(old_level); return max_len; } } else { free(interrupt_message); } } } return -1; }
/* Receives a message through a locally unbound port. Threads that call this function are * blocked until a message arrives. Upon arrival of each message, the function must create * a new bound port that targets the sender's address and listening port, so that use of * this created bound port results in replying directly back to the sender. It is the * responsibility of this function to strip off and parse the header before returning the * data payload and data length via the respective msg and len parameter. The return value * of this function is the number of data payload bytes received not inclusive of the header. */ int minimsg_receive(miniport_t local_unbound_port, miniport_t* new_local_bound_port, minimsg_t msg, int *len) { network_interrupt_arg_t* pkt; mini_header_t pkt_header; char protocol; network_address_t src_addr; unsigned short src_port; network_address_t dst_addr; int i; char* buff; if (local_unbound_port == NULL || local_unbound_port->p_type != UNBOUND_PORT || local_unbound_port->p_num >= BOUND_PORT_START || miniport_array[local_unbound_port->p_num] != local_unbound_port){ return -1; } //block until packet arrives semaphore_P(local_unbound_port->u.unbound.port_pkt_available_sem); semaphore_P(local_unbound_port->u.unbound.q_lock); if( queue_dequeue(local_unbound_port->u.unbound.port_pkt_q, (void**)&pkt)){ return -1; } semaphore_V(local_unbound_port->u.unbound.q_lock); pkt_header = (mini_header_t)(&pkt->buffer); protocol = pkt_header->protocol; unpack_address(pkt_header->source_address, src_addr); src_port = unpack_unsigned_short(pkt_header->source_port); unpack_address(pkt_header->destination_address, dst_addr); if (protocol != PROTOCOL_MINIDATAGRAM && protocol != PROTOCOL_MINISTREAM){ return -1; } if (protocol == PROTOCOL_MINISTREAM){ return 0; //currently unsupported } else { //UDP if ( pkt->size < sizeof(struct mini_header)){ return -1; } *len = pkt->size-sizeof(struct mini_header) > *len? *len : pkt->size-sizeof(struct mini_header); *new_local_bound_port = miniport_create_bound(pkt->sender, src_port); //copy payload buff = (char*)&(pkt->buffer); buff += sizeof(struct mini_header); for (i = 0; i < *len; i++){ msg[i] = *buff; buff++; } free(pkt); return *len; } }
/* Receives a message through a locally unbound port. Threads that call this function are * blocked until a message arrives. Upon arrival of each message, the function must create * a new bound port that targets the sender's address and listening port, so that use of * this created bound port results in replying directly back to the sender. It is the * responsibility of this function to strip off and parse the header before returning the * data payload and data length via the respective msg and len parameter. The return value * of this function is the number of data payload bytes received not inclusive of the header. */ int minimsg_receive(miniport_t local_unbound_port, miniport_t* new_local_bound_port, minimsg_t msg, int *len) { int received; miniport_t port; network_interrupt_arg_t *intrpt; network_address_t dest_addr; mini_header_t header; unsigned short dest_port; interrupt_level_t oldlevel; if (NULL == local_unbound_port || NULL == new_local_bound_port || UNBOUNDED != local_unbound_port->type || NULL == len || BOUNDED == local_unbound_port->type) return -1; /* * These shared data structures can be changed in the newtwork interrupt * handler, so interrupts should be disabled here as well. */ oldlevel = set_interrupt_level(DISABLED); semaphore_P(local_unbound_port->unbound.ready); queue_wrap_dequeue(local_unbound_port->unbound.data, (void**) &intrpt); set_interrupt_level(oldlevel); /* * The copied size should be the minimum among the user provided buffer * size (original *len), the received data size (received), and * MINIMSG_MAX_MSG_SIZE. */ received = intrpt->size - MINIMSG_HDRSIZE; if (*len >= received) *len = received; if (*len >= MINIMSG_MAX_MSG_SIZE) *len = MINIMSG_MAX_MSG_SIZE; header = (mini_header_t) intrpt->buffer; unpack_address(header->source_address, dest_addr); dest_port = unpack_unsigned_short(header->source_port); port = miniport_create_bound(dest_addr, dest_port); if (NULL == port) return -1; *new_local_bound_port = port; memcpy(msg, header + 1, *len); free(intrpt); return received; }
int minimsg_receive(miniport_t* local_unbound_port, miniport_t** new_local_bound_port, minimsg_t* msg, int *len) { assert(g_boundPortCounter >= 0); //sanity check to ensure minimsg_initialize() has been called first //validate input if (new_local_bound_port == NULL || local_unbound_port == NULL|| msg == NULL || len == NULL || *len < 0) return -1; assert(local_unbound_port->port_type == 'u' && local_unbound_port->unbound_port.datagrams_ready != NULL && local_unbound_port->unbound_port.datagrams_ready != NULL); semaphore_P(local_unbound_port->unbound_port.datagrams_ready); //P the semaphore, if the count is 0 we're blocked until packet arrives //once a packet arrives and we've woken network_interrupt_arg_t* dequeuedPacket = NULL; interrupt_level_t old_level = set_interrupt_level(DISABLED); // critical session (to dequeue the packet queue) assert(queue_length(local_unbound_port->unbound_port.incoming_data) > 0); //sanity check - our queue should have a packet waiting int dequeueSuccess = queue_dequeue(local_unbound_port->unbound_port.incoming_data, (void**)&dequeuedPacket); AbortOnCondition(dequeueSuccess != 0, "Queue_dequeue failed in minimsg_receive()"); set_interrupt_level(old_level); //end of critical session to restore interrupt level //Our packet size should be valid assert(dequeuedPacket->size >= 0); //get our header and message from the dequeued packet assert(dequeuedPacket->size >= sizeof(mini_header_t)); mini_header_t receivedHeader; memcpy(&receivedHeader, dequeuedPacket->buffer, sizeof(mini_header_t)); //set *len to the msg length to be copied: if the length of the message received is >= *len, no change to *len. Otherwise, set *len to the length of our received message if (dequeuedPacket->size - sizeof(mini_header_t) < *len) *len = dequeuedPacket->size - sizeof(mini_header_t); memcpy(msg, dequeuedPacket->buffer + sizeof(mini_header_t), *len); // msg is after header //create our new local bound port pointed back to the sender int sourcePort = (int)unpack_unsigned_short(receivedHeader.source_port); // get source's listening port assert(sourcePort >= UNBOUNDED_PORT_START && sourcePort <= UNBOUNDED_PORT_END); //make sure source port num is valid network_address_t remoteAddr; unpack_address(receivedHeader.source_address, remoteAddr); // get source's network address free(dequeuedPacket); // release the memory allocated to the packet *new_local_bound_port = miniport_create_bound(remoteAddr, sourcePort); // create a bound port if (*new_local_bound_port == NULL) return -1; return *len; //return data payload bytes received not inclusive of header }
/* Place the interrupt structure to the correct unbounded port, or discard it */ int minimsg_process(network_interrupt_arg_t *intrpt) { interrupt_level_t oldlevel; mini_header_t header = (mini_header_t) intrpt->buffer; int port_num = unpack_unsigned_short(header->destination_port); if (port_num < MIN_UNBOUNDED || port_num > MAX_UNBOUNDED || NULL == port[port_num]) { free(intrpt); return -1; } oldlevel = set_interrupt_level(DISABLED); if (queue_wrap_enqueue(port[port_num]->unbound.data, intrpt) != 0) { free(intrpt); set_interrupt_level(oldlevel); return -1; } semaphore_V(port[port_num]->unbound.ready); set_interrupt_level(oldlevel); return 0; }
/* * Listen for a connection from somebody else. When communication link is * created return a minisocket_t through which the communication can be made * from now on. * * The argument "port" is the port number on the local machine to which the * client will connect. * * Return value: the minisocket_t created, otherwise NULL with the errorcode * stored in the "error" variable. */ minisocket_t minisocket_server_create(int port, minisocket_error *error) { minisocket_t newMinisocket; int ack_check; int connected = 1; network_interrupt_arg_t *arg; mini_header_reliable_t header; if (error == NULL) return NULL; if (port < TCP_MINIMUM_SERVER || port > TCP_MAXIMUM_SERVER) { *error = SOCKET_INVALIDPARAMS; return NULL; } semaphore_P(server_mutex); //Checks if port already exists if (minisockets[port] != NULL) { *error = SOCKET_PORTINUSE; semaphore_V(server_mutex); return NULL; } newMinisocket = minisocket_create_socket(port); if (newMinisocket == NULL) { *error = SOCKET_OUTOFMEMORY; semaphore_V(server_mutex); return NULL; } newMinisocket->port_type = TCP_PORT_TYPE_SERVER; minisockets[port] = newMinisocket; semaphore_V(server_mutex); while (connected == 1) { semaphore_P(newMinisocket->packet_ready); queue_dequeue(newMinisocket->waiting_packets, (void **) &arg); header = (mini_header_reliable_t) arg->buffer; if (header->message_type != MSG_SYN) continue; newMinisocket->status = TCP_PORT_CONNECTING; unpack_address(header->source_address, newMinisocket->destination_addr); newMinisocket->destination_port = unpack_unsigned_short(header->source_port); ack_check = transmit_packet(newMinisocket, newMinisocket->destination_addr, newMinisocket->destination_port, 1, MSG_SYNACK, 0, NULL, error); if (ack_check == -1) { newMinisocket->status = TCP_PORT_LISTENING; network_address_blankify(newMinisocket->destination_addr); newMinisocket->destination_port = 0; } else { newMinisocket->status = TCP_PORT_CONNECTED; connected = 0; } } *error = SOCKET_NOERROR; return newMinisocket; }
void network_handler(network_interrupt_arg_t* arg) { // Used to extract the network address out of the interrupt argument //network_address_t addr; // The source port number - this is really the remote unbound port number int src_port_number; // The port number the packet was sent to int dst_port_number; // Used to store the header data for UDP/minimsgs mini_header_t header; // This is used to extract the sender's address from the header network_address_t src_addr; // This is used to extract the destination (our) address from the header network_address_t dst_addr; // Disable interrupts... interrupt_level_t prev_level = set_interrupt_level(DISABLED); // This is used to check our own address for sanity checks network_address_t my_addr; // This will store the total packet size int packet_size; // This will store the size of the data in the packet int data_len; // This will store the header of a TCP packet mini_header_reliable_t header_reliable; // This will store the ACK number of a TCP packet int ack_num; // This will store the Sequence number of a TCP packet int seq_num; // This will store the socket minisocket_t socket; // This will store the TCP error minisocket_error error; // This will be used to indicate whether the TCP packet is a duplicate or not int duplicate = 0; // This will tell us if we need to free the arg or not int enqueued; // Used for general for loops int i; // Used to check if we've already broadcasted a route discovery req int* last_seen_req_id; // Used for various tasks network_address_t tmp_addr; network_address_t tmp_addr2; // Used to handle routing and discovery requests route_request_t route_request; int current_req_id; int path_len; int ttl; // Get the buffer without the routing header char* buffer_without_routing = (char*) (arg->buffer + sizeof(struct routing_header)); // Used to get the data buffer char* data_buffer; // Handle the mini route stuff // Extract the information from the routing header routing_header_t routing_header = (routing_header_t) arg->buffer; char routing_packet_type = routing_header->routing_packet_type; unpack_address(routing_header->destination, dst_addr); current_req_id = unpack_unsigned_int(routing_header->id); ttl = unpack_unsigned_int(routing_header->ttl); path_len = unpack_unsigned_int(routing_header->path_len); // Get the data buffer & data length switch(buffer_without_routing[0]) { case (char) PROTOCOL_MINISTREAM: data_buffer = (char*) (buffer_without_routing + sizeof(struct mini_header_reliable)); data_len = arg->size - sizeof(struct routing_header) - sizeof(struct mini_header_reliable); break; //default: todo: put this back in, but leave w/o it for testing case (char) PROTOCOL_MINIDATAGRAM: data_buffer = (char*) (buffer_without_routing + sizeof(struct mini_header)); data_len = arg->size - sizeof(struct routing_header) - sizeof(struct mini_header); break; } network_get_my_address(my_addr); //if (network_compare_network_addresses(my_addr, dst_addr) == 0 && ttl == 0) if (my_addr[0] != dst_addr[0] && ttl == 0) { free(arg); set_interrupt_level(prev_level); return; } switch (routing_packet_type) { // If this is a data packet case ROUTING_DATA: // If the data packet is meant for us, then break and let the higher // protocols get the data unpack_address(routing_header->path[path_len-1], tmp_addr); if (network_compare_network_addresses(my_addr, tmp_addr) != 0) { break; } else { // If it's not meant for us, we must pass it along // Go through the path and find the next node for (i = 0; i < path_len; i++) { unpack_address(routing_header->path[i], tmp_addr); // If this node is us, break - the node we need to send to is next if (network_compare_network_addresses(tmp_addr, my_addr) != 0) { break; } } // If we're the last node (i == path_len-1) or we weren't found in it, quit if (i >= path_len - 1) { free(arg); set_interrupt_level(prev_level); return; } // Now we'll forward the packet by reusing the headers we have // Get the next host in the path and set it as the packet's dst unpack_address(routing_header->path[i+1], tmp_addr); pack_unsigned_int(routing_header->ttl, ttl - 1); // Send the packet onward in the route network_send_pkt(tmp_addr, arg->size - data_len, (char*) arg->buffer, data_len, data_buffer); // Revert the header back (not entirely necessary) //pack_unsigned_int(routing_header->ttl, ttl); } free(arg); set_interrupt_level(prev_level); return; break; case ROUTING_ROUTE_DISCOVERY: // We're not the dst, so just forward this packet along //if (network_compare_network_addresses(my_addr, dst_addr) == 0) if (my_addr[0] != dst_addr[0]) { // Check if we're in the path, if so, no need to send again (no loops) for (i = 0; i < path_len; i++) { unpack_address(routing_header->path[i], tmp_addr); if (network_compare_network_addresses(tmp_addr, my_addr) != 0) { break; } } // If we were in the path, return - no need to do anything else if (i < path_len) { free(arg); set_interrupt_level(prev_level); return; } // todo: this next part w/ the discovery packets seen is probably wrong... // also, lookup the "explain" part in my txt // todo: need something to clean up old discovery packets // Check if we've already seen this discovery request - if so, don't resend last_seen_req_id = (int*) hashmap_get(discovery_packets_seen, current_req_id); if (last_seen_req_id != NULL) { // If this exists, then we've already seen this packet - no need to resend free(arg); set_interrupt_level(prev_level); return; } // Now we'll rebroadcast the discovery packet by reusing the header we have // Modify the header as needed pack_unsigned_int(routing_header->path_len, path_len+1); pack_address(routing_header->path[path_len], my_addr); pack_unsigned_int(routing_header->ttl, ttl - 1); // Broadcast this packet network_bcast_pkt(arg->size - data_len, (char*) arg->buffer, data_len, data_buffer); // Revert the header - dont need to actually remove from route /*pack_unsigned_int(routing_header->path_len, path_len); pack_unsigned_int(routing_header->ttl, ttl); // update already broadcasted hashmap - just put a garbage ptr in hashmap_insert(discovery_packets_seen, current_req_id, (void*) 0x555555); */ free(arg); set_interrupt_level(prev_level); return; } else { // If we were the host being sought, send a reply packet back // and ensure the higher protocols get the data sent // reverse the path so we can send a packet back to the host // Don't forget to add ourselves to the route path_len++; pack_unsigned_int(routing_header->path_len, path_len); pack_address(routing_header->path[path_len-1], my_addr); for (i = 0; i < path_len/2; i++) { unpack_address(routing_header->path[path_len-1-i], tmp_addr); unpack_address(routing_header->path[i], tmp_addr2); pack_address(routing_header->path[i], tmp_addr); pack_address(routing_header->path[path_len-1-i], tmp_addr2); } // We'll start sending the packet back now by reusing the header we have // Prepare the headers pack_unsigned_int(routing_header->ttl, MAX_ROUTE_LENGTH); routing_header->routing_packet_type = ROUTING_ROUTE_REPLY; // send a route reply packet, starting from the next host in the reversed route unpack_address(routing_header->path[1], tmp_addr); pack_address(routing_header->destination, tmp_addr); network_send_pkt(tmp_addr, arg->size - data_len, (char*) arg->buffer, 0, NULL); // Revert the header /*pack_unsigned_int(routing_header->ttl, ttl); routing_header->routing_packet_type = ROUTING_ROUTE_DISCOVERY; // Reverse the path back for (i = 0; i < path_len/2; i++) { unpack_address(routing_header->path[path_len-1-i], tmp_addr); unpack_address(routing_header->path[i], tmp_addr2); pack_address(routing_header->path[i], tmp_addr); pack_address(routing_header->path[path_len-1-i], tmp_addr); } */ // DONT return, ensure that the higher protocols will get the data // ^ never mind. return, it will send a data packet later. free(arg); set_interrupt_level(prev_level); return; } break; case ROUTING_ROUTE_REPLY: unpack_address(routing_header->path[path_len-1], tmp_addr); // If we were the initiator of the request and just got our response //if (network_compare_network_addresses(my_addr, tmp_addr) != 0) if (my_addr[0] == tmp_addr[0]) { // Get the addr of the host we were trying to discover unpack_address(routing_header->path[0], tmp_addr); // find the discovery request struct for this dst addr //route_request = (route_request_t) hashmap_get(current_discovery_requests, hash_address(tmp_addr)); route_request = (route_request_t) hashmap_get(current_discovery_requests, tmp_addr[0]); if (route_request == NULL) { free(arg); set_interrupt_level(prev_level); return; // it could be we already got this path break; } // Check if we already got this path, but miniroute_send_pkt() hasn't deleted the req struct yet if (route_request->interrupt_arg != NULL) { free(arg); set_interrupt_level(prev_level); return; break; } route_request->interrupt_arg = arg; semaphore_V(route_request->initiator_sem); set_interrupt_level(prev_level); return; } else { // Find the next node in the route for (i = 0; i < path_len; i++) { unpack_address(routing_header->path[i], tmp_addr); // Stop if we found ourselves - we need to send to the next node if (network_compare_network_addresses(tmp_addr, my_addr) != 0) { break; } } // If we were the last node OR not in the route at all if (i >= path_len - 1) { free(arg); set_interrupt_level(prev_level); return; } // We'll forward the reply along by reusing the header // Get the next host in the path and set it as the packet's dst unpack_address(routing_header->path[i+1], tmp_addr); pack_unsigned_int(routing_header->ttl, ttl - 1); pack_address(routing_header->destination, tmp_addr); // Send the packet onward in the route network_send_pkt(tmp_addr, arg->size - data_len, (char*) arg->buffer, 0, NULL); // Make sure we set the header back //pack_unsigned_int(routing_header->ttl, ttl); } free(arg); set_interrupt_level(prev_level); return; break; } // If we're here, the packet was meant for us // The "normal" packet without the routing header is in buffer_without_routing // Adjust arg->size in case something uses it arg->size -= sizeof(struct routing_header); // Check the protocol switch (buffer_without_routing[0]) { case (char) PROTOCOL_MINIDATAGRAM: // Extract data from the network interrupt arg //network_address_copy(arg->addr, addr); // Get the header struct, unpack the parameters header = (mini_header_t) buffer_without_routing; dst_port_number = (int) unpack_unsigned_short(header->destination_port); // Ensure the port number is valid if (dst_port_number < MIN_UNBOUND || dst_port_number > MAX_UNBOUND) { free(arg); set_interrupt_level(prev_level); return; } // Then ensure the miniport exists if (miniports[dst_port_number] == NULL) { free(arg); set_interrupt_level(prev_level); return; } // Add the arg to the queue queue_append(miniports[dst_port_number]->port_data.unbound.data_queue, arg); // Wake up thread that's waiting to receive - sem_V() semaphore_V(miniports[dst_port_number]->port_data.unbound.data_ready); // Ensure the argument isn't free'd enqueued = 1; set_interrupt_level(prev_level); return; break; case PROTOCOL_MINISTREAM: // Get the total size of the packet and data length packet_size = arg->size; data_len = packet_size - sizeof(struct mini_header_reliable); // Get the header and extract information header_reliable = (mini_header_reliable_t) buffer_without_routing; src_port_number = (int) unpack_unsigned_short(header_reliable->source_port); dst_port_number = (int) unpack_unsigned_short(header_reliable->destination_port); unpack_address(header_reliable->source_address, src_addr); unpack_address(header_reliable->destination_address, dst_addr); seq_num = (int) unpack_unsigned_int(header_reliable->seq_number); ack_num = (int) unpack_unsigned_int(header_reliable->ack_number); // Don't respond if socket doesn't exist - will trigger SOCKET_NOSERVER for client if (minisockets[dst_port_number] == NULL) { free(arg); set_interrupt_level(prev_level); return; } // Get the socket in question socket = minisockets[dst_port_number]; // Check if packet was meant for us - ignore if not network_get_my_address(my_addr); if (network_compare_network_addresses(my_addr, dst_addr) == 0) { free(arg); set_interrupt_level(prev_level); return; } if (socket->status == TCP_PORT_CLOSING || socket->waiting == TCP_PORT_WAITING_CLOSE) { free(arg); set_interrupt_level(prev_level); return; } // Packet handling for established connections if (socket->status != TCP_PORT_LISTENING) { // Ensure source address is correct - ignore if not if (network_compare_network_addresses(src_addr, socket->dst_addr) == 0 || src_port_number != socket->dst_port) { if (header_reliable->message_type == MSG_SYN) { // A client is trying to connect to an already-connected port // Send them a FIN, which will result in their client // returning a SOCKET_BUSY error transmit_packet(socket, src_addr, src_port_number, 0, MSG_FIN, 0, NULL, &error); } free(arg); set_interrupt_level(prev_level); return; } /* We got a duplicate SYN and need to ensure that the host gets * another SYNACK. The thread that sent the SYNACK will currently * be retransmitting the SYNACKs, as it didn't get an ACK. If it * was done with the retransmission sequence, the socket would be * closed, and this would therefore not have gotten this far. * HOWEVER: * If the retransmission sequence already sent its LAST * retransmission and was waiting on it, then this duplicate SYN * will NOT get a SYNACK, as the retransmission is just waiting * out the last alarm before it ends. Therefore, if this is the case, * reset the timeout to the last timeout value so it sends just * one more SYNACK. * * Professor Sirer mentioned that this isn't even necessary, * but this does seem to make the code follow the specs more. */ if (header_reliable->message_type == MSG_SYN) { // If the timeout is at 6400, then it's the last transmission // but it hasn't been doubled yet. If it's 12800, it's just // been doubled. In each case we want to keep it at 6400 // at the end of the while loop, so we set the timeout to half // it's current value. if (socket->timeout >= 6400) { socket->timeout /= 2; } free(arg); set_interrupt_level(prev_level); return; } // If we were trying to connect and got a FIN, that means the socket's busy if (socket->status == TCP_PORT_CONNECTING && header_reliable->message_type == MSG_FIN) { // Not connected, got a FIN - that means we couldnt start connection b/c it was busy // This will let the client function infer that socket->status = TCP_PORT_COULDNT_CONNECT; // Wake it up so it can end the retransmission sequence semaphore_V(socket->wait_for_ack_sem); free(arg); set_interrupt_level(prev_level); return; } /* Note: the code below handles ACKs. It also inherently deals with * duplicate ACKs. We have a status code that indicates whether the socket * is waiting for an ACK or not. If it's set and we get an ACK (or any * packet) with the right ACK number, we can process it. However, if our * status indicates we're NOT waiting for an ACK, we can infer from the * fact that window_size = 1 that we already got the only ACK we could've * been expecting, and this new one is therefore a duplicate. */ // If we're waiting on an ACK if (socket->waiting == TCP_PORT_WAITING_ACK /*|| socket->waiting == TCP_PORT_WAITING_ACK_WAKING*/) { // This can be an ACK or really any other data packet, we just // need the ACK number if (ack_num == socket->seq_num) { // Update our status to show we're no longer waiting for an ACK socket->waiting = TCP_PORT_WAITING_NONE; // Wake up the thread waiting for the ACK semaphore_V(socket->wait_for_ack_sem); } else if (ack_num == socket->seq_num - 1) { // This follows the same logic from the comment block around // line 170. if (socket->timeout >= 6400) { socket->timeout /= 2; } } } // If it's an ACK, it requires no further processing if (header_reliable->message_type == MSG_ACK && data_len == 0) { free(arg); set_interrupt_level(prev_level); return; } // Check if it's a SYNACK we're waiting for if (socket->waiting == TCP_PORT_WAITING_SYNACK && header_reliable->message_type == MSG_SYNACK) { // We're now fully connected in our eyes, handshake complete socket->waiting = TCP_PORT_WAITING_NONE; semaphore_V(socket->wait_for_ack_sem); } // If we're here, the packet isnt an ACK or SYN, so we should ACK it // First check if we should increment our ack number if (seq_num == socket->ack_num + 1) { socket->ack_num++; } else { // It's a duplicate, don't add to queue duplicate = 1; } // Next, perform the ACK, don't incr the seq# transmit_packet(socket, socket->dst_addr, socket->dst_port, 0, MSG_ACK, 0, NULL, &error); /* Note: the code below handles FINs, and is also protected against * duplicate FINs inherently. The seq_num == ack_num check doesn't * guarantee it's not a duplicate; however, if we process one FIN, * then the socket's status is set to TCP_PORT_CLOSING. Therefore, * if we get another FIN, we can tell that the port is already closing * and we don't need to process it, which also ensures we don't * process duplicate FINs multiple times. We'll include the * duplicate == 0 check just for good measure, however. */ // We're required to close the conn 15s after we ACK a FIN, so do that here if (seq_num == socket->ack_num && header_reliable->message_type == MSG_FIN && socket->status != TCP_PORT_CLOSING && duplicate == 0) { socket->status = TCP_PORT_CLOSING; queue_append(sockets_to_delete, socket); semaphore_V(socket_needs_delete); } } else if (socket->status == TCP_PORT_LISTENING) { // Start a connection with the client if (header_reliable->message_type == MSG_SYN) { // Update socket's dst addr & dst port to the client's network_address_copy(src_addr, socket->dst_addr); socket->dst_port = src_port_number; // Set the status to connecting socket->status = TCP_PORT_CONNECTING; // Awake the create_server thread, it'll handle the rest semaphore_V(socket->wait_for_ack_sem); } } // Add the packet to the socket's packet queue if not duplicate & it's a data pkt if (duplicate == 0 && header_reliable->message_type == MSG_ACK && data_len != 0) { enqueued = 1; queue_append(socket->waiting_packets, arg); if (socket->data_len == 0) semaphore_V(socket->packet_ready); } // remember to free arg if we dont push this to the tcp recv queue break; } if (enqueued == 0) { free(arg); } // Restore the interrupt level set_interrupt_level(prev_level); return; }
/* minisocket_process_packet is called within the network interrupt handler. * Therefore we can safely assume that interrupts are disabled. * */ void minisocket_process_packet(void* packet) { network_interrupt_arg_t* pkt; mini_header_reliable_t pkt_hdr; unsigned int seq_num; unsigned int ack_num; minisocket_error error; network_address_t src_addr; network_address_t dst_addr; unsigned int src_port; unsigned int dst_port; minisocket_t sock; int type; int data_len; pkt = (network_interrupt_arg_t*)packet; pkt_hdr = (mini_header_reliable_t)(&pkt->buffer); //printf("in minisocket_process_packet\n"); // error checking if (pkt->size < sizeof(struct mini_header_reliable)) { free(pkt); return; } unpack_address(pkt_hdr->source_address, src_addr); src_port = unpack_unsigned_short(pkt_hdr->source_port); dst_port = unpack_unsigned_short(pkt_hdr->destination_port); unpack_address(pkt_hdr->destination_address, dst_addr); seq_num = unpack_unsigned_int(pkt_hdr->seq_number); ack_num = unpack_unsigned_int(pkt_hdr->ack_number); data_len = pkt->size - sizeof(struct mini_header_reliable); if (src_port < 0 || dst_port < 0 || src_port >= NUM_SOCKETS || dst_port >= NUM_SOCKETS || !network_compare_network_addresses(dst_addr, my_addr)){ free(pkt); return; } error = SOCKET_NOERROR; sock = sock_array[dst_port]; if (sock == NULL) { free(pkt); return; } type = pkt_hdr->message_type; //printf("socket %d with state %d\n", sock->src_port, sock->curr_state); //printf("pkt ack number is %d\n", ack_num); //printf("pkt seq number is %d\n", seq_num); //printf("my ack number is %d\n", sock->curr_ack); //printf("my seq number is %d\n", sock->curr_seq); //printf("type of msg received is %d\n", type); if (sock->curr_state != LISTEN && ( !network_compare_network_addresses(src_addr, sock->dst_addr) || src_port != sock->dst_port ) ){ if (type == MSG_SYN){ minisocket_send_ctrl_to(MSG_FIN, sock, &error, src_addr, src_port); } free(pkt); return; } switch (sock->curr_state) { case LISTEN: if (type == MSG_SYN) { sock->curr_ack = 1; semaphore_V(sock->ack_ready_sem); sock->curr_state = CONNECTING; sock->dst_port = src_port; network_address_copy(src_addr, sock->dst_addr); } free(pkt); break; case CONNECTING: if (type == MSG_ACK) { if (ack_num == sock->curr_seq) { semaphore_V(sock->ack_ready_sem); sock->curr_state = CONNECTED; } if (seq_num == sock->curr_ack+1 && data_len > 0) { queue_append(sock->pkt_q, pkt); semaphore_V(sock->pkt_ready_sem); minisocket_send_ctrl(MSG_ACK, sock, &error); sock->curr_ack++; } else { free(pkt); } } else { free(pkt); } break; case CONNECT_WAIT://TODO if (type == MSG_FIN) { sock->curr_state = CLOSE_RCV; semaphore_V(sock->ack_ready_sem);//notify blocked guy } else if (type == MSG_SYNACK) { if (ack_num == sock->curr_seq) { sock->curr_state = CONNECTED; sock->curr_ack++; minisocket_send_ctrl(MSG_ACK, sock, &error); semaphore_V(sock->ack_ready_sem); } } free(pkt); break; case MSG_WAIT: if (type == MSG_FIN){ sock->curr_ack++; minisocket_send_ctrl(MSG_ACK, sock, &error);//notify to closer sock->curr_state = CLOSE_RCV; if (sock->resend_alarm){ deregister_alarm(sock->resend_alarm); sock->resend_alarm = NULL; } sock->resend_alarm = set_alarm(RESEND_TIME_UNIT * 150, self_destruct, (void*)sock, minithread_time()); semaphore_V(sock->ack_ready_sem);//notify blocked guy } else if (type == MSG_ACK) { if (ack_num == sock->curr_seq) { //printf("got an ACK!\n"); semaphore_V(sock->ack_ready_sem); sock->curr_state = CONNECTED; } if (seq_num == sock->curr_ack+1 && data_len > 0) { //printf("got a MESSAGE!\n"); queue_append(sock->pkt_q, pkt); sock->curr_ack++; minisocket_send_ctrl(MSG_ACK, sock, &error); semaphore_V(sock->pkt_ready_sem); } else { free(pkt); } } else { free(pkt); } break; case CLOSE_SEND: if (type == MSG_ACK && ack_num == sock->curr_seq) { semaphore_V(sock->ack_ready_sem); } if (type == MSG_FIN){ sock->curr_ack++; minisocket_send_ctrl(MSG_ACK, sock, &error);//notify to closer sock->curr_state = CLOSE_RCV; if (sock->resend_alarm){ deregister_alarm(sock->resend_alarm); sock->resend_alarm = NULL; } sock->resend_alarm = set_alarm(RESEND_TIME_UNIT * 150, self_destruct, (void*)sock, minithread_time()); //minisocket_send_ctrl(MSG_ACK, sock, &error); } //free(pkt); break; case CLOSE_RCV: if (type == MSG_FIN && ack_num == sock->curr_seq) { minisocket_send_ctrl(MSG_ACK, sock, &error); //semaphore_V(sock->ack_ready_sem); } free(pkt); break; case CONNECTED: if (type == MSG_FIN){ sock->curr_ack++; minisocket_send_ctrl(MSG_ACK, sock, &error);//notify to closer sock->curr_state = CLOSE_RCV; if (sock->resend_alarm){ deregister_alarm(sock->resend_alarm); sock->resend_alarm = NULL; } sock->resend_alarm = set_alarm(RESEND_TIME_UNIT * 150, self_destruct, (void*)sock, minithread_time()); } if (type == MSG_ACK) { if (ack_num == sock->curr_seq) { //semaphore_V(sock->ack_ready_sem); sock->curr_state = CONNECTED; } if (seq_num == sock->curr_ack+1 && data_len > 0) { //printf("got some DATA\n"); queue_append(sock->pkt_q, pkt); sock->curr_ack++; minisocket_send_ctrl(MSG_ACK, sock, &error); semaphore_V(sock->pkt_ready_sem); //printf("got data, no seg fault\n"); } else if (seq_num == sock->curr_ack && data_len > 0){ minisocket_send_ctrl(MSG_ACK, sock, &error); } else { free(pkt); } } else { free(pkt); } break; case EXIT: default: free(pkt); break; } }
minisocket_t* minisocket_server_create(int port, minisocket_error *error) { if (port < MIN_SERVER_PORT || port > MAX_SERVER_PORT) { *error = SOCKET_INVALIDPARAMS; return NULL; } if (ports[port]) { *error = SOCKET_PORTINUSE; return NULL; } minisocket_t *new_socket = (minisocket_t *) malloc(sizeof(minisocket_t)); if (!new_socket) { *error = SOCKET_OUTOFMEMORY; return NULL; } // Initialize new socket new_socket->socket_state = INITIAL; semaphore_P(ports_mutex); ports[port] = new_socket; semaphore_V(ports_mutex); new_socket->socket_type = 's'; new_socket->local_port = port; network_address_copy(local_host, new_socket->local_addr); new_socket->data = queue_new(); new_socket->data_ready = semaphore_create(); new_socket->ack_flag = 0; new_socket->wait_for_ack = semaphore_create(); new_socket->send_receive_mutex = semaphore_create(); semaphore_initialize(new_socket->data_ready, 0); semaphore_initialize(new_socket->wait_for_ack, 0); semaphore_initialize(new_socket->send_receive_mutex, 1); interrupt_level_t old_level; while (1) { new_socket->seq_number = 0; new_socket->ack_number = 0; new_socket->remote_port = -1; new_socket->remote_addr[0] = 0; new_socket->remote_addr[1] = 0; new_socket->socket_state = WAITING_SYN; // receiving SYN while (1) { semaphore_P(new_socket->data_ready); network_interrupt_arg_t *arg = NULL; queue_dequeue(new_socket->data, (void **) &arg); mini_header_reliable_t *header = (mini_header_reliable_t *) arg->buffer; if (header->message_type -'0'== MSG_SYN) { unpack_address(header->source_address, new_socket->remote_addr); new_socket->remote_port = unpack_unsigned_short(header->source_port); new_socket->socket_state = WAITING_ACK; new_socket->seq_number = 0; new_socket->ack_number = 1; break; } else { free(arg); } } minisocket_error s_error; int wait_val = 100; while (wait_val <= 12800) { send_control_message(MSG_SYNACK, new_socket->remote_port, new_socket->remote_addr, new_socket->local_port, 0 , 1, &s_error); new_socket->seq_number = 1; new_socket->ack_number = 1; if (s_error == SOCKET_OUTOFMEMORY) { minisocket_free(new_socket); semaphore_P(ports_mutex); ports[new_socket->local_port] = NULL; semaphore_V(ports_mutex); *error = s_error; return NULL; } alarm_id a = register_alarm(wait_val, (alarm_handler_t) semaphore_V, new_socket->data_ready); semaphore_P(new_socket->data_ready); old_level = set_interrupt_level(DISABLED); if (queue_length(new_socket->data)) { deregister_alarm(a); } set_interrupt_level(old_level); if (!queue_length(new_socket->data)) { wait_val *= 2; continue; } network_interrupt_arg_t *arg = NULL; queue_dequeue(new_socket->data, (void **) &arg); mini_header_reliable_t *header = (mini_header_reliable_t *) arg->buffer; network_address_t saddr; unpack_address(header->source_address, saddr); int sport = unpack_unsigned_short(header->source_port); if (header->message_type - '0' == MSG_SYN) { if (new_socket->remote_port == sport && network_compare_network_addresses(new_socket->remote_addr, saddr)) { continue; } send_control_message(MSG_FIN, sport, saddr, new_socket->local_port, 0, 0, &s_error); } if (header->message_type - '0' == MSG_ACK) { if (new_socket->remote_port == sport && network_compare_network_addresses(new_socket->remote_addr, saddr)) { network_interrupt_arg_t *packet = NULL; while (queue_dequeue(new_socket->data, (void **)&packet) != -1) { free(packet); } semaphore_initialize(new_socket->data_ready, 0); new_socket->socket_state = OPEN; new_socket->seq_number = 1; new_socket->ack_number = 2; return new_socket; } } free (arg); } } return NULL; }
void network_handler(network_interrupt_arg_t* packet) { interrupt_level_t old_level = set_interrupt_level(DISABLED); //strip relevant packet header information mini_header_t* header = (mini_header_t *) packet->buffer; unsigned short dest_port = unpack_unsigned_short(header->destination_port); char packet_protocol = header->protocol; if (packet_protocol == PROTOCOL_MINIDATAGRAM) { //appends entire packet (including header) to the relevant miniport queue miniport_t* mini = minimsg_get_port(dest_port); if (mini == NULL) { set_interrupt_level(old_level); free(packet); return; //unallocated mini_port within minimsg, so drop packet! } //wake up possible waiting threads (or keep track of enqueued packets) queue_append(mini->port_data.incoming_data, packet); semaphore_V(mini->port_data.datagrams_ready); set_interrupt_level(old_level); } else { //printf("handling TCP packet\n\n"); mini_header_reliable_t* received_header = (mini_header_reliable_t *) packet->buffer; minisocket_t* socket = minisocket_get_socket(dest_port); if (socket == NULL) { printf("no socket found type: %u\n", received_header->message_type); free(packet); set_interrupt_level(old_level); return; } //check packet size if (packet->size < sizeof(mini_header_reliable_t)) { free(packet); return; } //sending header data network_address_t send_address; unpack_address(received_header->source_address, send_address); mini_header_reliable_t send_header; unsigned short source_port = unpack_unsigned_short(received_header->source_port); minisocket_create_reliable_header((mini_header_reliable_t *) &send_header, socket, source_port, send_address, MSG_ACK); if (socket->state == CLOSED && received_header->message_type == MSG_FIN) { printf("ACKing FIN packet\n"); //closed socket that hasn't been re-allocated, send back MSG_ACK send_header.message_type = MSG_ACK; network_send_pkt(send_address, sizeof(mini_header_reliable_t), (char *) &send_header, 0, NULL); free(packet); set_interrupt_level(old_level); return; } if (socket->state == CONNECTING || socket->state == LISTENING) { //currently trying to establish a connection to socket queue_append(socket->acknowledgements, packet); semaphore_V(socket->ack_ready); set_interrupt_level(old_level); return; } //check that this packet source information matches expected socket connection if (socket->state == CONNECTED && socket->remote_port_number == source_port && network_compare_network_addresses(socket->remote_address, send_address) != 0) { if (packet->size == sizeof(mini_header_reliable_t) && received_header->message_type == MSG_ACK) { //printf("got empty ACK packet with ack: %u\n", unpack_unsigned_int(received_header->ack_number)); // received ack return message for pending sent message queue_append(socket->acknowledgements, packet); semaphore_V(socket->ack_ready); set_interrupt_level(old_level); return; } if (packet->size == sizeof(mini_header_reliable_t) && received_header->message_type == MSG_SYNACK) { send_header.message_type = MSG_ACK; network_send_pkt(send_address, sizeof(mini_header_reliable_t), (char *) &send_header, 0, NULL); free(packet); return; } if (received_header->message_type == MSG_FIN) { printf("received messsage FIN\n"); // received termination message for currently connected socket // send back MSG_ACK to closer send_header.message_type = MSG_ACK; network_send_pkt(send_address, sizeof(mini_header_reliable_t), (char *) &send_header, 0, NULL); //mark socket as closed, so no further calls can be made to send() or receive() socket->state = CLOSED; printf("REGISTERING ALARM\n"); //sleep for 15s before destroying the socket register_alarm(15000, minisocket_destroy, (void *) socket); set_interrupt_level(old_level); return; } //check sequence number unsigned int seq_number = unpack_unsigned_int(received_header->seq_number); if (seq_number < socket->ack) { //printf("already seen this!\n"); //have seen this packet before, send an ACK to client send_header.message_type = MSG_ACK; network_send_pkt(send_address, sizeof(mini_header_reliable_t), (char *) &send_header, 0, NULL); //free(packet); set_interrupt_level(old_level); return; } else if (seq_number > socket->ack) { //printf("don't want packet yet!\n"); //want to wait for earlier sequential packet free(packet); set_interrupt_level(old_level); return; } else if (seq_number == socket->ack && received_header->message_type == MSG_ACK && packet->size > sizeof(mini_header_reliable_t)) { //else seq_number == socket->ack // append data packet and increment ack number queue_append(socket->incoming_data, packet); semaphore_V(socket->datagrams_ready); send_header.message_type = MSG_ACK; unsigned int new_ack = seq_number + packet->size - sizeof(mini_header_reliable_t); pack_unsigned_int(send_header.ack_number, new_ack); socket->ack = new_ack; //printf("sending ack %u\n", socket->ack); network_send_pkt(send_address, sizeof(mini_header_reliable_t), (char *) &send_header, 0, NULL); set_interrupt_level(old_level); } } else { //send back a MSG_FIN to request that this client stop sending messages network_send_pkt(send_address, sizeof(mini_header_reliable_t), (char *) &send_header, 0, NULL); set_interrupt_level(old_level); } } }
void network_handler(network_interrupt_arg_t* arg) { // Used to extract the network address out of the interrupt argument //network_address_t addr; // The source port number - this is really the remote unbound port number int src_port_number; // The port number the packet was sent to int dst_port_number; // Used to store the header data for UDP/minimsgs mini_header_t header; // This is used to extract the sender's address from the header network_address_t src_addr; // This is used to extract the destination (our) address from the header network_address_t dst_addr; // Disable interrupts... interrupt_level_t prev_level = set_interrupt_level(DISABLED); // This is used to check our own address for sanity checks network_address_t my_addr; // This will store the total packet size int packet_size; // This will store the size of the data in the packet int data_len; // This will store the header of a TCP packet mini_header_reliable_t header_reliable; // This will store the ACK number of a TCP packet int ack_num; // This will store the Sequence number of a TCP packet int seq_num; // This will store the socket minisocket_t socket; // This will store the TCP error minisocket_error error; // This will be used to indicate whether the TCP packet is a duplicate or not int duplicate = 0; // Check the protocol switch (arg->buffer[0]) { case (char) PROTOCOL_MINIDATAGRAM: // Extract data from the network interrupt arg //network_address_copy(arg->addr, addr); // Get the header struct, unpack the parameters header = (mini_header_t) &arg->buffer; dst_port_number = (int) unpack_unsigned_short(header->destination_port); // Ensure the port number is valid if (dst_port_number < MIN_UNBOUND || dst_port_number > MAX_UNBOUND) { free(arg); set_interrupt_level(prev_level); return; } // Then ensure the miniport exists if (miniports[dst_port_number] == NULL) { free(arg); set_interrupt_level(prev_level); return; } // Add the arg to the queue queue_append(miniports[dst_port_number]->port_data.unbound.data_queue, arg); // Wake up thread that's waiting to receive - sem_V() semaphore_V(miniports[dst_port_number]->port_data.unbound.data_ready); break; case PROTOCOL_MINISTREAM: // Get the total size of the packet and data length packet_size = arg->size; data_len = packet_size - sizeof(struct mini_header_reliable); // Get the header and extract information header_reliable = (mini_header_reliable_t) arg->buffer; src_port_number = (int) unpack_unsigned_short(header_reliable->source_port); dst_port_number = (int) unpack_unsigned_short(header_reliable->destination_port); unpack_address(header_reliable->source_address, src_addr); unpack_address(header_reliable->destination_address, dst_addr); seq_num = (int) unpack_unsigned_int(header_reliable->seq_number); ack_num = (int) unpack_unsigned_int(header_reliable->ack_number); // Don't respond if socket doesn't exist - will trigger SOCKET_NOSERVER for client if (minisockets[dst_port_number] == NULL) { set_interrupt_level(prev_level); return; } // Get the socket in question socket = minisockets[dst_port_number]; // Check if packet was meant for us - ignore if not network_get_my_address(my_addr); if (network_compare_network_addresses(my_addr, dst_addr) == 0) { set_interrupt_level(prev_level); return; } if (socket->status == TCP_PORT_CLOSING || socket->waiting == TCP_PORT_WAITING_CLOSE) { set_interrupt_level(prev_level); return; } // Packet handling for established connections if (socket->status != TCP_PORT_LISTENING) { // Ensure source address is correct - ignore if not if (network_compare_network_addresses(src_addr, socket->dst_addr) == 0 || src_port_number != socket->dst_port) { if (header_reliable->message_type == MSG_SYN) { // A client is trying to connect to an already-connected port // Send them a FIN, which will result in their client // returning a SOCKET_BUSY error transmit_packet(socket, src_addr, src_port_number, 0, MSG_FIN, 0, NULL, &error); } set_interrupt_level(prev_level); return; } /* We got a duplicate SYN and need to ensure that the host gets * another SYNACK. The thread that sent the SYNACK will currently * be retransmitting the SYNACKs, as it didn't get an ACK. If it * was done with the retransmission sequence, the socket would be * closed, and this would therefore not have gotten this far. * HOWEVER: * If the retransmission sequence already sent its LAST * retransmission and was waiting on it, then this duplicate SYN * will NOT get a SYNACK, as the retransmission is just waiting * out the last alarm before it ends. Therefore, if this is the case, * reset the timeout to the last timeout value so it sends just * one more SYNACK. * * Professor Sirer mentioned that this isn't even necessary, * but this does seem to make the code follow the specs more. */ if (header_reliable->message_type == MSG_SYN) { // If the timeout is at 6400, then it's the last transmission // but it hasn't been doubled yet. If it's 12800, it's just // been doubled. In each case we want to keep it at 6400 // at the end of the while loop, so we set the timeout to half // it's current value. if (socket->timeout >= 6400) { socket->timeout /= 2; } set_interrupt_level(prev_level); return; } // If we were trying to connect and got a FIN, that means the socket's busy if (socket->status == TCP_PORT_CONNECTING && header_reliable->message_type == MSG_FIN) { // Not connected, got a FIN - that means we couldnt start connection b/c it was busy // This will let the client function infer that socket->status = TCP_PORT_COULDNT_CONNECT; // Wake it up so it can end the retransmission sequence semaphore_V(socket->wait_for_ack_sem); set_interrupt_level(prev_level); return; } /* Note: the code below handles ACKs. It also inherently deals with * duplicate ACKs. We have a status code that indicates whether the socket * is waiting for an ACK or not. If it's set and we get an ACK (or any * packet) with the right ACK number, we can process it. However, if our * status indicates we're NOT waiting for an ACK, we can infer from the * fact that window_size = 1 that we already got the only ACK we could've * been expecting, and this new one is therefore a duplicate. */ // If we're waiting on an ACK if (socket->waiting == TCP_PORT_WAITING_ACK /*|| socket->waiting == TCP_PORT_WAITING_ACK_WAKING*/) { // This can be an ACK or really any other data packet, we just // need the ACK number if (ack_num == socket->seq_num) { // Update our status to show we're no longer waiting for an ACK socket->waiting = TCP_PORT_WAITING_NONE; // Wake up the thread waiting for the ACK semaphore_V(socket->wait_for_ack_sem); } else if (ack_num == socket->seq_num - 1) { // This follows the same logic from the comment block around // line 170. if (socket->timeout >= 6400) { socket->timeout /= 2; } } } // If it's an ACK, it requires no further processing if (header_reliable->message_type == MSG_ACK && data_len == 0) { set_interrupt_level(prev_level); return; } // Check if it's a SYNACK we're waiting for if (socket->waiting == TCP_PORT_WAITING_SYNACK && header_reliable->message_type == MSG_SYNACK) { // We're now fully connected in our eyes, handshake complete socket->waiting = TCP_PORT_WAITING_NONE; semaphore_V(socket->wait_for_ack_sem); } // If we're here, the packet isnt an ACK or SYN, so we should ACK it // First check if we should increment our ack number if (seq_num == socket->ack_num + 1) { socket->ack_num++; } else { // It's a duplicate, don't add to queue duplicate = 1; } // Next, perform the ACK, don't incr the seq# transmit_packet(socket, socket->dst_addr, socket->dst_port, 0, MSG_ACK, 0, NULL, &error); /* Note: the code below handles FINs, and is also protected against * duplicate FINs inherently. The seq_num == ack_num check doesn't * guarantee it's not a duplicate; however, if we process one FIN, * then the socket's status is set to TCP_PORT_CLOSING. Therefore, * if we get another FIN, we can tell that the port is already closing * and we don't need to process it, which also ensures we don't * process duplicate FINs multiple times. We'll include the * duplicate == 0 check just for good measure, however. */ // We're required to close the conn 15s after we ACK a FIN, so do that here if (seq_num == socket->ack_num && header_reliable->message_type == MSG_FIN && socket->status != TCP_PORT_CLOSING && duplicate == 0) { socket->status = TCP_PORT_CLOSING; queue_append(sockets_to_delete, socket); semaphore_V(socket_needs_delete); } } else if (socket->status == TCP_PORT_LISTENING) { // Start a connection with the client if (header_reliable->message_type == MSG_SYN) { // Update socket's dst addr & dst port to the client's network_address_copy(src_addr, socket->dst_addr); socket->dst_port = src_port_number; // Set the status to connecting socket->status = TCP_PORT_CONNECTING; // Awake the create_server thread, it'll handle the rest semaphore_V(socket->wait_for_ack_sem); } } // Add the packet to the socket's packet queue if not duplicate & it's a data pkt if (duplicate == 0 && header_reliable->message_type == MSG_ACK && data_len != 0) { queue_append(socket->waiting_packets, arg); if (socket->data_len == 0) semaphore_V(socket->packet_ready); } // remember to free arg if we dont push this to the tcp recv queue break; } // Restore the interrupt level set_interrupt_level(prev_level); return; }
void minisocket_handle_tcp_packet(network_interrupt_arg_t *arg) { mini_header_reliable_t *header = (mini_header_reliable_t *) (arg->buffer); int port = unpack_unsigned_short(header->destination_port); if (port < MIN_SERVER_PORT || port > MAX_CLIENT_PORT || !ports[port]) { free(arg); return; } if (ports[port]->socket_state == INITIAL || ports[port]->socket_state == CLOSED) { free(arg); return; } if (ports[port]->socket_state != OPEN) { // handled in handshake queue_append(ports[port]->data, arg); semaphore_V(ports[port]->data_ready); return; } minisocket_error s_error; network_address_t saddr; unpack_address(header->source_address, saddr); int sport = unpack_unsigned_short(header->source_port); if (!network_compare_network_addresses(ports[port]->remote_addr, saddr) || ports[port]->remote_port != sport) { if(header->message_type -'0' == MSG_SYN) { send_control_message(MSG_FIN, sport, saddr, port, 0, 0, &s_error); } free(arg); return; } //If the message is of type SYNACK and from the same client if (header->message_type - '0' == MSG_SYNACK) { send_control_message(MSG_ACK, sport, saddr, port, 0, 0, &s_error); free(arg); return; } //If the message is of type FIN if (header->message_type - '0' == MSG_FIN) { ports[port]->ack_number += 1; send_control_message(MSG_ACK, sport, saddr, port, ports[port]->seq_number, ports[port]->ack_number, &s_error); ports[port]->socket_state = CLOSING; int count = semaphore_get_count(ports[port]->data_ready); while (count < 0) { semaphore_V(ports[port]->data_ready); count++; } register_alarm(15000, (alarm_handler_t) minisocket_close, ports[port]); //minisocket_free(ports[port]); free(arg); return; } //If the message is of type ACK from the same client if (header->message_type - '0' == MSG_ACK) { unsigned int ack_no = unpack_unsigned_int(header->ack_number); int packet_size = arg->size - sizeof(mini_header_reliable_t); minisocket_error s_error; //If it's a correct acknowledgement of the sent data, enqueue it and V the wait for ack semaphore if (ack_no == ports[port]->seq_number/* + MAX_NETWORK_PKT_SIZE - sizeof(mini_header_reliable_t) + 1*/) { if (packet_size != 0) { queue_append(ports[port]->data, arg); semaphore_V(ports[port]->data_ready); ports[port]->ack_number += packet_size; send_control_message(MSG_ACK, sport, saddr, port, ports[port]->seq_number, ports[port]->ack_number, &s_error); } if (ports[port]->ack_flag == 0) { ports[port]->ack_flag = 1; semaphore_V(ports[port]->wait_for_ack); } return; } else { free(arg); return; } } }
/* Receives a message through a locally unbound port. Threads that call this function are * blocked until a message arrives. Upon arrival of each message, the function must create * a new bound port that targets the sender's address and listening port, so that use of * this created bound port results in replying directly back to the sender. It is the * responsibility of this function to strip off and parse the header before returning the * data payload and data length via the respective msg and len parameter. The return value * of this function is the number of data payload bytes received not inclusive of the header. */ int minimsg_receive(miniport_t local_unbound_port, miniport_t* new_local_bound_port, minimsg_t msg, int *len) { unsigned short remote_port; char* buffer; // int size = 0; int i = 0; network_address_t remote_receive_addr; network_interrupt_arg_t* packet = NULL; // mini_header_t header; // semaphore_P(msgmutex); // Check for valid arguments if (local_unbound_port == NULL) { fprintf(stderr, "ERROR: minimsg_receive() passed a NULL local_unbound_port miniport argument\n"); semaphore_V(msgmutex); return -1; } if (new_local_bound_port == NULL) { fprintf(stderr, "ERROR: minimsg_receive() passed a NULL new_local_bound_port miniport* argument\n"); semaphore_V(msgmutex); return -1; } // semaphore_V(msgmutex); // fprintf(stderr, "minimsg_receive() paused\n"); semaphore_P(local_unbound_port->u.unbound.datagrams_ready); // Block until message arrives // fprintf(stderr, "minimsg_receive() resuming\n"); // semaphore_P(msgmutex); // Obtain received message from miniport queue and extract header data if (queue_dequeue(local_unbound_port->u.unbound.incoming_data, (void**) &packet) < 0) { fprintf(stderr, "ERROR: minimsg_receive() failed to dequeue message from miniport queue\n"); semaphore_V(msgmutex); return -1; } // Extract header stuff buffer = packet->buffer; // size = packet->size; // *len = ((packet->size) - 20) / sizeof(char); *len = sizeof(buffer[21]) / sizeof(char); unpack_address(&buffer[1], remote_receive_addr); remote_port = unpack_unsigned_short(&buffer[9]); // msg = (minimsg_t) &buffer[21]; // *msg = *((minimsg_t) &buffer[21]); while (buffer[21 + i]) { msg[i] = buffer[21 + i]; i++; } // fprintf(stderr, "minimsg_receive() past extracting header stuff\n"); /*fprintf(stderr, "minimsg_receive() msg: %s\n", msg); fprintf(stderr, "minimsg_receive() msg size: %d\n", (int) sizeof(msg)); fprintf(stderr, "minimsg_receive() len: %d\n", *len); fprintf(stderr, "minimsg_receive() force msg len: %d\n", (int) (sizeof((minimsg_t) &buffer[21]) / sizeof(char))); fprintf(stderr, "minimsg_receive() get body length: %lu\n", sizeof(buffer) / sizeof(buffer[21]) - 20);*/ // Create new bound port *new_local_bound_port = miniport_create_bound(remote_receive_addr, remote_port); //return number of bytes of payload actually received (drop stuff beyond max) free(packet); // semaphore_V(msgmutex); return sizeof(msg); }
int minisocket_send(minisocket_t *socket, const char *msg, int len, minisocket_error *error) { //Check params if (socket == NULL || mini_socket_data[socket->local_port_number] != socket || len < 0 || msg == NULL) { *error = SOCKET_INVALIDPARAMS; return -1; } if (socket->state == CLOSED) { *error = SOCKET_SENDERROR; return -1; } if (len == 0) { return 0; } //can't send more bytes than we have if (len > strlen(msg)) { len = strlen(msg); } int bytes_sent = 0; int max_data_size = MAX_NETWORK_PKT_SIZE - sizeof(mini_header_reliable_t); int data_left = len; int packet_size; mini_header_reliable_t header; minisocket_create_reliable_header((mini_header_reliable_t *) &header, socket, socket->remote_port_number, socket->remote_address, MSG_ACK); //Keep sending until all is sent while (data_left != 0) { if (data_left >= max_data_size) { packet_size = max_data_size; } else { packet_size = data_left; } data_left -= packet_size; pack_unsigned_int(header.seq_number, socket->seq); int send_success = 0; //Attempt to send message int timeout = START_TIMEOUT/2; for (int i = 0; i < MAX_FAILURES; i++) { //printf("i: %d\n", i); timeout = timeout * 2; //update the seq and ack numbers for the header from the socket //printf("seq: %u, ack: %u\n", socket->seq, socket->ack); pack_unsigned_int(header.ack_number, socket->ack); pack_unsigned_int(header.seq_number, socket->seq); network_send_pkt(socket->remote_address, sizeof(mini_header_reliable_t), (char *) &header, packet_size, (char *) msg); alarm_id timeout_alarm_id = register_alarm(timeout, handshake_timeout_handler, (void *) socket->ack_ready); int alarm_fired = 0; //keep looking through received packets until either the alarm fires or it finds the correct packet while (!send_success && !alarm_fired) { semaphore_P(socket->ack_ready); if (queue_length(socket->acknowledgements) == 0) { // printf("no message in queue\n"); alarm_fired = 1; //goes to next iteration of for loop } else { // printf("length of queue currently %d\n", queue_length(socket->acknowledgements)); network_interrupt_arg_t* interrupt_message = NULL; interrupt_level_t old_level = set_interrupt_level(DISABLED); queue_dequeue(socket->acknowledgements, (void **) &interrupt_message); set_interrupt_level(old_level); if (interrupt_message != NULL) { mini_header_reliable_t* received_header = (mini_header_reliable_t *) interrupt_message->buffer; network_address_t temp_address; unpack_address(received_header->source_address, temp_address); unsigned int new_ack = unpack_unsigned_int(received_header->ack_number); //printf("Received an acknowledgment with ack number %u \n", new_ack); if (socket->remote_port_number == unpack_unsigned_short(received_header->source_port) && network_compare_network_addresses(socket->remote_address, temp_address) != 0 && received_header->message_type == MSG_ACK && new_ack == (socket->seq + packet_size)) { //same address, same ports, right message, right ack deregister_alarm(timeout_alarm_id); //only deregister alarm when the right packet is found send_success = 1; bytes_sent += packet_size; msg += packet_size; socket->seq += packet_size; free(interrupt_message); break; } else { free(interrupt_message); } } } } if (send_success) { break; } } if (!send_success) { //Got a timeout, should close down socket *error = SOCKET_SENDERROR; minisocket_destroy(socket); return bytes_sent; } } //printf("\n\n"); return bytes_sent; }
minisocket_t* minisocket_client_create(const network_address_t addr, int port, minisocket_error *error) { //Check socket number first if (valid_server_port(port) == 0) { *error = SOCKET_INVALIDPARAMS; return NULL; } //Then check if we can make another client if (client_count >= MAX_CLIENTS) { *error = SOCKET_NOMOREPORTS; return NULL; } //create new minisocket minisocket_t* socket = (minisocket_t *) malloc(sizeof(minisocket_t)); if (socket == NULL) { *error = SOCKET_OUTOFMEMORY; return NULL; //OOM } while (mini_socket_data[next_port]) { next_port = ((next_port + 1) % PORT_RANGE); } socket->socket_type = CLIENT_TYPE; socket->local_port_number = next_port; next_port = ((next_port + 1) % PORT_RANGE); socket->state = CONNECTING; socket->seq = 0; socket->ack = 0; network_address_copy(addr, socket->remote_address); socket->remote_port_number = (unsigned short) port; network_address_copy(addr, socket->remote_address); socket->next_read = 0; network_address_t my_address; network_get_my_address(my_address); network_address_copy(my_address, socket->local_address); socket->datagrams_ready = semaphore_create(); semaphore_initialize(socket->datagrams_ready, 0); socket->ack_ready = semaphore_create(); semaphore_initialize(socket->ack_ready, 0); socket->incoming_data = queue_new(); socket->acknowledgements = queue_new(); //add socket to global array mini_socket_data[socket->local_port_number] = socket; mini_header_reliable_t header; minisocket_create_reliable_header((mini_header_reliable_t *) &header, socket, socket->remote_port_number, addr, MSG_SYN); //minisocket and header now created, try to establish connection pack_unsigned_int(header.seq_number, socket->seq); pack_unsigned_int(header.ack_number, socket->ack); //send first message and wait for response int response = 0; //no response yet int timeout = START_TIMEOUT / 2; //this way first timeout will be at START_TIMEOUT for (int i = 0; i < MAX_FAILURES; i++) { printf("sending MSG_SYN in client, i: %d\n", i); timeout = timeout * 2; network_send_pkt(addr, sizeof(mini_header_reliable_t), (char *) &header, 0, empty_message); alarm_id timeout_alarm_id = register_alarm(timeout, handshake_timeout_handler, (void *) socket->ack_ready); int alarm_fired = 0; while (!response && !alarm_fired) { semaphore_P(socket->ack_ready); //If queue length == 0, then the alarm must have fired if (queue_length(socket->acknowledgements) == 0) { alarm_fired = 1; //goes to next iteration of for loop } //otherwise, we received a packet else { network_interrupt_arg_t *interrupt_message; interrupt_level_t old_level = set_interrupt_level(DISABLED); queue_dequeue(socket->acknowledgements, (void **) &interrupt_message); set_interrupt_level(old_level); // if message not null if (interrupt_message != NULL) { mini_header_reliable_t* received_header = (mini_header_reliable_t *) interrupt_message->buffer; network_address_t temp_address; unpack_address(received_header->source_address, temp_address); if (socket->remote_port_number == unpack_unsigned_short(received_header->source_port) && network_compare_network_addresses(socket->remote_address, temp_address) != 0) { //if SYNACK printf("CLIENT\n"); if (received_header->message_type == MSG_SYNACK) { printf("GOT message SYN_ACK\n"); deregister_alarm(timeout_alarm_id); response = 1; break; } //if MSG_FIN else if (received_header->message_type == MSG_FIN) { printf("got message MSG_FIN in client\n"); //server already in use deregister_alarm(timeout_alarm_id); *error = SOCKET_BUSY; response = 1; minisocket_destroy(socket); return NULL; } //WRONG MESSAGE TYPE else { printf("wrong message type received in client\n"); //TODO : try another message type, maybe do nothing? } } } } } if (response) { break; } } // timeout after 12.8s occured, close down socket if (response != 1) { printf("full timeout in client sending SYN\n"); *error = SOCKET_NOSERVER; minisocket_destroy(socket); return NULL; } // send final MSG_ACK once to server (future data packets will also have MSG_ACK as header type) header.message_type = MSG_ACK; printf("sending final MSG_ACK and leaving client create\n"); network_send_pkt(addr, sizeof(mini_header_reliable_t), (char *) &header, 0, empty_message); socket->state = CONNECTED; client_count++; return socket; }
minisocket_t* minisocket_server_create(int port, minisocket_error *error) { //Check socket number first if (valid_server_port(port) == 0) { *error = SOCKET_INVALIDPARAMS; return NULL; } //check if socket is already in use if (mini_socket_data[port] != NULL) { *error = SOCKET_PORTINUSE; return NULL; } minisocket_t* socket = (minisocket_t *) malloc(sizeof(minisocket_t)); if (socket == NULL) { *error = SOCKET_OUTOFMEMORY; return NULL; } socket->state = LISTENING; socket->socket_type = SERVER_TYPE; socket->local_port_number = port; network_address_t my_address; network_get_my_address(my_address); network_address_copy(my_address, socket->local_address); socket->datagrams_ready = semaphore_create(); semaphore_initialize(socket->datagrams_ready, 0); socket->ack_ready = semaphore_create(); semaphore_initialize(socket->ack_ready, 0); socket->incoming_data = queue_new(); socket->acknowledgements = queue_new(); socket->seq = 0; socket->ack = 0; socket->next_read = 0; //add socket to global array mini_socket_data[socket->local_port_number] = socket; char connection_established = 0; mini_header_reliable_t header; minisocket_create_reliable_header((mini_header_reliable_t *) &header, socket, socket->remote_port_number, socket->remote_address, MSG_SYNACK); network_address_t dest; pack_unsigned_int(header.seq_number, socket->seq); pack_unsigned_int(header.ack_number, socket->ack); //loop until a full connection is established while (connection_established == 0) { //sleep until initial MSG_SYN arrives from client char message_received = 0; while (message_received == 0) { //Waits until it receives a packet semaphore_P(socket->ack_ready); interrupt_level_t old_level = set_interrupt_level(DISABLED); network_interrupt_arg_t* interrupt_message = NULL; queue_dequeue(socket->acknowledgements, (void **) &interrupt_message); set_interrupt_level(old_level); //TODO: check more contents of message? change seq and ack values in response??} if (interrupt_message != NULL ) { mini_header_reliable_t* received_header = (mini_header_reliable_t *) interrupt_message->buffer; unpack_address(received_header->source_address, dest); pack_address(header.destination_address, dest); pack_unsigned_short(header.destination_port, unpack_unsigned_short(received_header->source_port)); if (valid_client_port(unpack_unsigned_short(received_header->source_port)) == 1) { //check valid port number if (received_header->message_type != MSG_SYN) { //check valid message type header.message_type = MSG_FIN; network_send_pkt(dest, sizeof(mini_header_reliable_t), (char *) &header, 0, empty_message); free(interrupt_message); } else { //set remote port values printf("server got the SYN message\n"); socket->remote_port_number = unpack_unsigned_short(received_header->source_port); network_address_copy(dest, socket->remote_address); socket->seq = unpack_unsigned_int(received_header->seq_number); message_received = 1; //will break loop free(interrupt_message); socket->state = CONNECTING; break; } } } } //reset loop value for return message_received = 0; //otherwise the message was the correct type and format int timeout = START_TIMEOUT / 2; //this way first timeout will be at START_TIMEOUT for (int i = 0; i < MAX_FAILURES; i++) { printf("sending MSG_SYNACK in server, i: %d\n", i); timeout = timeout * 2; header.message_type = MSG_SYNACK; network_send_pkt(dest, sizeof(mini_header_reliable_t), (char *) &header, 0, empty_message); alarm_id timeout_alarm_id = register_alarm(timeout, handshake_timeout_handler, (void *) socket->ack_ready); int alarm_fired = 0; //keep looking through received packets until either the alarm fires or it finds the correct packet while (!connection_established && !alarm_fired) { semaphore_P(socket->ack_ready); //alarm has fired, since there are no packets to be received if (queue_length(socket->acknowledgements) == 0) { alarm_fired = 1; //goes to next iteration of for loop } //There is a packet (alarm hasnt fired yet) else { network_interrupt_arg_t* interrupt_message = NULL; interrupt_level_t old_level = set_interrupt_level(DISABLED); queue_dequeue(socket->acknowledgements, (void **) &interrupt_message); set_interrupt_level(old_level); // verify non null message if (interrupt_message != NULL) { mini_header_reliable_t* received_header = (mini_header_reliable_t *) interrupt_message->buffer; network_address_t temp_address; unpack_address(received_header->source_address, temp_address); if (socket->remote_port_number == unpack_unsigned_short(received_header->source_port) && network_compare_network_addresses(socket->remote_address, temp_address) != 0 && received_header->message_type == MSG_ACK) { //same address, same ports, right message deregister_alarm(timeout_alarm_id); //only deregister alarm when the right packet is found connection_established = 1; //queue_prepend(socket->acknowledgements, interrupt_message); //ack works as well when there is data break; } else { free(interrupt_message); } } } } if (connection_established) { //correct response has been found, get out of this timeout loop! break; } } //if timeout occurs, will loop back to initial waiting phase for MSG_SYN from client } printf("leaving server create\n"); //update server socket values with client connection socket->state = CONNECTED; assert(socket != NULL); return socket; }
/** * method for packet processor to repeatedly * check for new packets that arrived and upon * sanity checks forward them to the appropriate * port to be queued up. If the destination port * is not initialized, the packet is thrown away. * * We protect important global data structures (ie. * our array of miniports) with semaphores. Each * miniport has an associated semaphore as well. */ int process_packets() { interrupt_level_t l; network_interrupt_arg_t* pkt; char protocol; network_address_t src_addr; mini_header_t header; unsigned short src_port_num; network_address_t dst_addr; unsigned short dst_port_num; //char message_type; //unsigned int seq_number; //unsigned int ack_number; // TCP miniport_t dst_port; while (1) { semaphore_P(pkt_available_sem); l = set_interrupt_level(DISABLED); if (queue_dequeue(pkt_q, (void**)&pkt)){ //dequeue fails set_interrupt_level(l); continue; //move on with life } set_interrupt_level(l); //perform checks on packet, free & return if invalid protocol = pkt->buffer[0]; if (protocol != PROTOCOL_MINIDATAGRAM && protocol != PROTOCOL_MINISTREAM){ free(pkt); continue; } else { // JUMP ON IT header = (mini_header_t)(&pkt->buffer); unpack_address(header->source_address, src_addr); src_port_num = unpack_unsigned_short(header->source_port); unpack_address(header->destination_address, dst_addr); dst_port_num = unpack_unsigned_short(header->destination_port); if (protocol == PROTOCOL_MINIDATAGRAM){ //check address if (!network_compare_network_addresses(my_addr, dst_addr) || src_port_num >= BOUND_PORT_START || dst_port_num >= BOUND_PORT_START ) { free(pkt); continue; } //if port DNE or not an unbound port, fail if (miniport_array[dst_port_num] == NULL || miniport_array[dst_port_num]->p_type != UNBOUND_PORT) { free(pkt); continue; } dst_port = miniport_array[dst_port_num]; semaphore_P(dst_port->u.unbound.q_lock); queue_append(dst_port->u.unbound.port_pkt_q,pkt); semaphore_V(dst_port->u.unbound.q_lock); semaphore_V(dst_port->u.unbound.port_pkt_available_sem); continue; } else if (protocol == PROTOCOL_MINISTREAM) { free(pkt); continue; //for now, ignore tcp packets } } //DROP DA BASE //JUUUUUMPP ONNNN ITTT } return -1; }
minisocket_t* minisocket_client_create(const network_address_t addr, int port, minisocket_error *error) { if (!addr || port < MIN_SERVER_PORT || port > MAX_SERVER_PORT) { *error = SOCKET_INVALIDPARAMS; return NULL; } int port_val = -1; // CHECK what n_client_ports does if (!ports[n_client_ports]) { port_val = n_client_ports; } else { for (int i = MIN_CLIENT_PORT; i <= MAX_CLIENT_PORT; i++) { if (!ports[i]) { port_val = i; break; } } } n_client_ports = port_val + 1; if (n_client_ports > MAX_CLIENT_PORT) { n_client_ports = 0; } if (port_val == -1) { *error = SOCKET_NOMOREPORTS; return NULL; } minisocket_t *new_socket = (minisocket_t *) malloc(sizeof(minisocket_t)); if (!new_socket) { *error = SOCKET_OUTOFMEMORY; return NULL; } new_socket->socket_state = INITIAL; semaphore_P(ports_mutex); ports[port_val] = new_socket; semaphore_V(ports_mutex); new_socket->socket_type = 'c'; network_address_copy(local_host, new_socket->local_addr); //network_get_my_address(new_socket->local_addr); new_socket->local_port = port_val; network_address_copy(addr, new_socket->remote_addr); new_socket->remote_port = port; new_socket->data = queue_new(); new_socket->data_ready = semaphore_create(); new_socket->wait_for_ack = semaphore_create(); new_socket->send_receive_mutex = semaphore_create(); semaphore_initialize(new_socket->data_ready, 0); semaphore_initialize(new_socket->wait_for_ack, 0); semaphore_initialize(new_socket->send_receive_mutex, 1); new_socket->socket_state = WAITING_SYNACK; minisocket_error s_error; int wait_val = 100; interrupt_level_t old_level; while (wait_val <= 12800) { send_control_message(MSG_SYN, new_socket->remote_port, new_socket->remote_addr, new_socket->local_port, 0, 0, &s_error); new_socket->seq_number = 1; new_socket->ack_number = 0; if (s_error == SOCKET_OUTOFMEMORY) { minisocket_free(new_socket); semaphore_P(ports_mutex); ports[new_socket->local_port] = NULL; semaphore_V(ports_mutex); *error = s_error; return NULL; } alarm_id a = register_alarm(wait_val, (alarm_handler_t) semaphore_V, new_socket->data_ready); semaphore_P(new_socket->data_ready); old_level = set_interrupt_level(DISABLED); if (queue_length(new_socket->data)) { deregister_alarm(a); } set_interrupt_level(old_level); if (!queue_length(new_socket->data)) { wait_val *= 2; continue; } network_interrupt_arg_t *arg = NULL; queue_dequeue(new_socket->data, (void **) &arg); mini_header_reliable_t *header = (mini_header_reliable_t *) arg->buffer; network_address_t saddr; unpack_address(header->source_address, saddr); int sport = unpack_unsigned_short(header->source_port); if (sport == new_socket->remote_port && network_compare_network_addresses(saddr, new_socket->remote_addr)) { if (header->message_type - '0' == MSG_SYNACK) { new_socket->seq_number = 1; new_socket->ack_number = 1; send_control_message(MSG_ACK, new_socket->remote_port, new_socket->remote_addr, new_socket->local_port, 1, 1, &s_error); if (s_error == SOCKET_OUTOFMEMORY) { minisocket_free(new_socket); semaphore_P(ports_mutex); ports[new_socket->local_port] = NULL; semaphore_V(ports_mutex); *error = s_error; return NULL; } network_interrupt_arg_t *packet = NULL; while (queue_dequeue(new_socket->data, (void **)&packet) != -1) { free(packet); } semaphore_initialize(new_socket->data_ready, 0); new_socket->socket_state = OPEN; new_socket->seq_number = 2; new_socket->ack_number = 1; return new_socket; } if (header->message_type -'0' == MSG_FIN) { minisocket_free(new_socket); return NULL; } } free(arg); } minisocket_free(new_socket); return NULL; }