int chitcpd_server_stop(serverinfo_t *si) { int rc; chilog(DEBUG, "Stopping the chiTCP daemon."); /* To stop the server, all we need to do is shutdown the server's * TCP and UNIX sockets. Once the server is running, its two * threads (server and network) are blocking on accept() on * each of these two sockets. Shutting down the sockets forces * a return from those calls which, combined with the change * in the state of the server, will result in an orderly shutdown. */ pthread_mutex_lock(&si->lock_state); si->state = CHITCPD_STATE_STOPPING; pthread_cond_broadcast(&si->cv_state); pthread_mutex_unlock(&si->lock_state); rc = shutdown(si->network_socket, SHUT_RDWR); if(rc != 0) return CHITCP_ESOCKET; rc = shutdown(si->server_socket, SHUT_RDWR); if(rc != 0) return CHITCP_ESOCKET; chilog(DEBUG, "chiTCP daemon is now in STOPPING state."); return CHITCP_OK; }
/* * chitcpd_server_wait - Wait for chiTCP daemon to exit * * si: Server info * * Returns: * - CHITCP_OK: chiTCP daemon has exited correctly * */ int chitcpd_server_wait(serverinfo_t *si) { chilog(DEBUG, "Waiting for chiTCP daemon to stop."); while(si->state != CHITCPD_STATE_STOPPED) pthread_cond_wait(&si->cv_state, &si->lock_state); pthread_mutex_unlock(&si->lock_state); chilog(DEBUG, "chiTCP daemon has fully stopped."); return CHITCP_OK; }
void chilog_chitcp(loglevel_t level, uint8_t *packet, char prefix) { if(level > loglevel) return; chitcphdr_t *header = (chitcphdr_t*) packet; flockfile(stdout); chilog(level, " ======================================================================"); chilog(level, "%c Payload length: %i", prefix, chitcp_ntohs(header->payload_len)); chilog(level, "%c Protocol: %i", prefix, header->proto); chilog(level, " ======================================================================"); funlockfile(stdout); }
// Based on http://stackoverflow.com/questions/7775991/how-to-get-hexdump-of-a-structure-data void chilog_hex (loglevel_t level, void *data, int len) { int i; char buf[8]; char ascii[17]; char line[74]; uint8_t *pc = data; line[0] = '\0'; // Process every byte in the data. for (i = 0; i < len; i++) { // Multiple of 16 means new line (with line offset). if ((i % 16) == 0) { // Just don't print ASCII for the zeroth line. if (i != 0) { chilog(level, "%s %s", line, ascii); line[0] = '\0'; } // Output the offset. sprintf(buf, " %04x ", i); strcat(line, buf); } // Now the hex code for the specific character. sprintf(buf, " %02x", pc[i]); strcat(line, buf); // And store a printable ASCII character for later. if ((pc[i] < 0x20) || (pc[i] > 0x7e)) ascii[i % 16] = '.'; else ascii[i % 16] = pc[i]; ascii[(i % 16) + 1] = '\0'; } // Pad out last line if not exactly 16 characters. while ((i % 16) != 0) { strcat(line, " "); i++; } // And print the final ASCII bit. chilog(level, "%s %s", line, ascii); }
int chitcpd_tcp_state_handle_SYN_RCVD(serverinfo_t *si, chisocketentry_t *entry, tcp_event_type_t event){ tcp_data_t *data = &entry->socket_state.active.tcp_data; if (event == PACKET_ARRIVAL){ pthread_mutex_lock(&data->lock_pending_packets); tcp_packet_t *pack = list_fetch(&(data->pending_packets)); pthread_mutex_unlock(&data->lock_pending_packets); tcphdr_t *head = TCP_PACKET_HEADER(pack); if (acceptability_test(data,pack)){ //Its acceptable, but is this true in all cases or just receiving information if (head->ack && ((data->SND_UNA <= SEG_ACK(pack)) && (SEG_ACK(pack) <= data->SND_NXT))){ data->SND_UNA = data->SND_NXT; data->SND_WND = SEG_WND(pack); chitcpd_update_tcp_state(si,entry,ESTABLISHED); } else { send_ACK(si, entry, data); data->SND_WND = SEG_WND(pack); } } chitcp_tcp_packet_free(pack); } else chilog(WARNING, "In SYN_RCVD state, received unexpected event."); return CHITCP_OK; }
int chitcpd_tcp_state_handle_CLOSING(serverinfo_t *si, chisocketentry_t *entry, tcp_event_type_t event) { tcp_data_t *data = &entry->socket_state.active.tcp_data; if (event == PACKET_ARRIVAL) { while (!list_empty(&data->pending_packets)) { pthread_mutex_lock(&data->lock_pending_packets); tcp_packet_t *pack = list_fetch(&(data->pending_packets)); pthread_mutex_unlock(&data->lock_pending_packets); tcphdr_t *head = TCP_PACKET_HEADER(pack); if (!acceptability_test(data, pack)) { send_ACK(si, entry, data); chitcp_tcp_packet_free(pack); return CHITCP_OK; } if (head->ack) { if (inside_window(data, pack)) { update_WND_and_UNA(data, pack); chitcpd_update_tcp_state(si, entry, TIME_WAIT); } if (head->fin) always_on_fb(si, entry, data, pack); } chitcp_tcp_packet_free(pack); } } else chilog(WARNING, "In CLOSING state, received unexpected event (%i).", event); return CHITCP_OK; }
/* * chitcpd_server_wait - Wait for chiTCP daemon to exit * * si: Server info * * Returns: * - CHITCP_OK: chiTCP daemon has exited correctly * */ int chitcpd_server_wait(serverinfo_t *si) { chilog(DEBUG, "Waiting for chiTCP daemon to stop."); /* TODO: Retrieve return values */ pthread_join(si->server_thread, NULL); pthread_join(si->network_thread, NULL); pthread_mutex_lock(&si->lock_state); si->state = CHITCPD_STATE_STOPPED; pthread_cond_broadcast(&si->cv_state); pthread_mutex_unlock(&si->lock_state); chilog(DEBUG, "chiTCP daemon has fully stopped."); return CHITCP_OK; }
int chitcpd_tcp_state_handle_SYN_SENT(serverinfo_t *si, chisocketentry_t *entry, tcp_event_type_t event){ tcp_data_t *data = &entry->socket_state.active.tcp_data; if (event == PACKET_ARRIVAL){ pthread_mutex_lock(&data->lock_pending_packets); tcp_packet_t *pack = list_fetch(&(data->pending_packets)); pthread_mutex_unlock(&data->lock_pending_packets); tcphdr_t *head = TCP_PACKET_HEADER(pack); /* This condition checks if there is an ack that it is acceptable and there is a syn*/ if (((head->ack && ((data->SND_UNA <= SEG_ACK(pack)) && (SEG_ACK(pack) <= data->SND_NXT))) || !head->ack) && head->syn){ data->RCV_NXT = SEG_SEQ(pack)+1; data->IRS = SEG_SEQ(pack); if (head->ack){ //only if its an ack data->SND_WND = SEG_WND(pack); data->SND_UNA = SEG_ACK(pack); } if (data->SND_UNA > data->ISS){ send_ACK(si, entry, data); data->SND_UNA = data->SND_NXT; chitcpd_update_tcp_state(si,entry,ESTABLISHED); chitcp_tcp_packet_free(pack); return CHITCP_OK; } else { tcp_packet_t *new_pack = (tcp_packet_t*)malloc(sizeof(tcp_packet_t)); chitcpd_tcp_packet_create(entry,new_pack,NULL,0); tcphdr_t *SYN_ACK = TCP_PACKET_HEADER(new_pack); SYN_ACK->seq = chitcp_htonl(data->ISS); SYN_ACK->ack_seq = chitcp_htonl(data->RCV_NXT); SYN_ACK->syn = 1; SYN_ACK->ack = 1; SYN_ACK->win = chitcp_htons(data->RCV_WND); chilog_tcp(CRITICAL,new_pack,LOG_OUTBOUND); chitcpd_send_tcp_packet(si,entry,new_pack); chitcpd_update_tcp_state(si,entry,SYN_RCVD); chitcp_tcp_packet_free(pack); chitcp_tcp_packet_free(new_pack); return CHITCP_OK; } } } else chilog(WARNING, "In SYN_SENT state, received unexpected event."); return CHITCP_OK; }
int chitcpd_tcp_state_handle_CLOSE_WAIT(serverinfo_t *si, chisocketentry_t *entry, tcp_event_type_t event) { tcp_data_t *data = &entry->socket_state.active.tcp_data; if (event == APPLICATION_CLOSE) { tcp_packet_t *new_pack = (tcp_packet_t*)malloc(sizeof(tcp_packet_t)); chitcpd_tcp_packet_create(entry,new_pack,NULL,0); tcphdr_t *FIN = TCP_PACKET_HEADER(new_pack); send_all(si, entry, data); FIN->seq = chitcp_htonl(data->SND_NXT); FIN->ack_seq = chitcp_htonl(data->RCV_NXT); FIN->fin = 1; FIN->win = chitcp_htons(data->RCV_WND); chilog_tcp(CRITICAL,new_pack,LOG_OUTBOUND); chitcpd_send_tcp_packet(si,entry,new_pack); circular_buffer_close(&data->recv); circular_buffer_close(&data->send); chitcpd_update_tcp_state(si,entry,LAST_ACK); chitcp_tcp_packet_free(new_pack); } else if (event == PACKET_ARRIVAL) { while (!list_empty(&data->pending_packets)) { pthread_mutex_lock(&data->lock_pending_packets); tcp_packet_t *pack = list_fetch(&(data->pending_packets)); pthread_mutex_unlock(&data->lock_pending_packets); tcphdr_t *head = TCP_PACKET_HEADER(pack); if (!acceptability_test(data, pack)) { send_ACK(si, entry, data); chitcp_tcp_packet_free(pack); return CHITCP_OK; } if (head->ack) { if (inside_window(data, pack)) update_WND_and_UNA(data, pack); if (head->fin){ always_on_fb(si, entry, data, pack); } } chitcp_tcp_packet_free(pack); } } else chilog(WARNING, "In CLOSE_WAIT state, received unexpected event (%i).", event); return CHITCP_OK; }
int chitcpd_tcp_state_handle_LISTEN(serverinfo_t *si, chisocketentry_t *entry, tcp_event_type_t event){ tcp_data_t *data = &entry->socket_state.active.tcp_data; if (event == PACKET_ARRIVAL) { pthread_mutex_lock(&data->lock_pending_packets); tcp_packet_t *pack = list_fetch(&(data->pending_packets)); pthread_mutex_unlock(&data->lock_pending_packets); tcphdr_t *head = TCP_PACKET_HEADER(pack); if (head->syn) { data->RCV_NXT = SEG_SEQ(pack) + 1; data->IRS = SEG_SEQ(pack); data->ISS = rand() % 1000 + 1; data->RCV_WND = circular_buffer_available(&data->recv); tcp_packet_t *new_pack = (tcp_packet_t*)malloc(sizeof(tcp_packet_t)); chitcpd_tcp_packet_create(entry,new_pack,NULL,0); tcphdr_t *SYN_ACK = TCP_PACKET_HEADER(new_pack); SYN_ACK->seq = chitcp_htonl(data->ISS); SYN_ACK->ack_seq = chitcp_htonl(data->RCV_NXT); SYN_ACK->syn = 1; SYN_ACK->ack = 1; SYN_ACK->win = chitcp_htons(data->RCV_WND); chilog_tcp(CRITICAL,new_pack,LOG_OUTBOUND); chitcpd_send_tcp_packet(si,entry,new_pack); data->SND_WND = SEG_WND(pack); data->SND_NXT = data->ISS + 1; data->SND_UNA = data->ISS; chitcpd_update_tcp_state(si,entry,SYN_RCVD); chitcp_tcp_packet_free(pack); chitcp_tcp_packet_free(new_pack); return CHITCP_OK; } } else chilog(WARNING, "In LISTEN state, received unexpected event."); return CHITCP_OK; }
int chitcpd_tcp_state_handle_CLOSED(serverinfo_t *si, chisocketentry_t *entry, tcp_event_type_t event){ tcp_data_t *data = &entry->socket_state.active.tcp_data; if (event == APPLICATION_CONNECT) { /* Initializes the data */ data->ISS = rand() % 1000 + 1; data->SND_UNA = data->ISS; data->SND_NXT = data->ISS + 1; data->RCV_WND = circular_buffer_available(&data->recv); tcp_packet_t *pack = (tcp_packet_t*)malloc(sizeof(tcp_packet_t)); chitcpd_tcp_packet_create(entry,pack,NULL,0); tcphdr_t *SYN = TCP_PACKET_HEADER(pack); SYN->seq = chitcp_htonl(data->ISS); SYN->ack_seq = chitcp_htonl(data->ISS + 1); SYN->syn = 1; SYN->win = chitcp_htons(data->RCV_WND); chilog_tcp(CRITICAL,pack,LOG_OUTBOUND); chitcpd_send_tcp_packet(si,entry,pack); chitcpd_update_tcp_state(si,entry,SYN_SENT); chitcp_tcp_packet_free(pack); } else if (event == CLEANUP) { chitcpd_free_socket_entry(si, entry); pthread_exit(NULL); /* Any additional cleanup goes here */ } else chilog(WARNING, "In CLOSED state, received unexpected event."); return CHITCP_OK; }
int chitcpd_tcp_state_handle_LAST_ACK(serverinfo_t *si, chisocketentry_t *entry, tcp_event_type_t event) { tcp_data_t *data = &entry->socket_state.active.tcp_data; if (event == PACKET_ARRIVAL) { while (!list_empty(&data->pending_packets)){ pthread_mutex_lock(&data->lock_pending_packets); tcp_packet_t *pack = list_fetch(&(data->pending_packets)); pthread_mutex_unlock(&data->lock_pending_packets); tcphdr_t *head = TCP_PACKET_HEADER(pack); if (!acceptability_test(data, pack)) { send_ACK(si, entry, data); chitcp_tcp_packet_free(pack); return CHITCP_OK; } if (head->ack) { if (inside_window(data, pack)) { circular_buffer_free(&data->recv); circular_buffer_free(&data->send); list_destroy(&data->pending_packets); pthread_mutex_destroy(&data->lock_pending_packets); pthread_mutex_destroy(&data->lock_withheld_packets); list_destroy(&data->withheld_packets); free(data); chitcpd_update_tcp_state(si, entry, CLOSED); } } chitcp_tcp_packet_free(pack); } } else chilog(WARNING, "In LAST_ACK state, received unexpected event (%i).", event); return CHITCP_OK; }
void chilog_tcp(loglevel_t level, tcp_packet_t *packet, char prefix) { if(level > loglevel) return; tcphdr_t *header = (tcphdr_t*) packet->raw; uint8_t *payload = TCP_PAYLOAD_START(packet); uint16_t payload_len = TCP_PAYLOAD_LEN(packet); flockfile(stdout); chilog(level, " ######################################################################"); chilog(level, "%c Src: %i Dest: %i Seq: %i Ack: %i Doff: %i Win: %i", prefix, chitcp_ntohs(header->source), chitcp_ntohs(header->dest), chitcp_ntohl(header->seq), chitcp_ntohl(header->ack_seq), header->doff, chitcp_ntohs(header->win)); chilog(level, "%c CWR: %i ECE: %i URG: %i ACK: %i PSH: %i RST: %i SYN: %i FIN: %i", prefix, header->cwr, header->ece, header->urg, header->ack, header->psh, header->rst, header->syn, header->fin); if(payload_len > 0) { chilog(level, "%c Payload (%i bytes):", prefix, payload_len); chilog_hex(level, payload, payload_len); } else { chilog(level, "%c No Payload", prefix); } chilog(level, " ######################################################################"); funlockfile(stdout); }
/* * chitcpd_server_network_thread_func - Server thread function * * This function will spawn a connection thread (see connection.c) * for each new connection on the UNIX socket. * * args: arguments (a serverinfo_t variable in network_thread_args_t) * * Returns: Nothing. * */ void* chitcpd_server_network_thread_func(void *args) { socklen_t sunSize; network_thread_args_t *nta; socket_t realsocket; serverinfo_t *si; tcpconnentry_t* connection; char addr_str[100]; pthread_setname_np(pthread_self(), "network_server"); /* Unpack arguments */ nta = (network_thread_args_t *) args; si = nta->si; struct sockaddr_storage client_addr; /* Accept connections on the TCP socket */ for(;;) { /* Accept a connection */ sunSize = sizeof(client_addr); if ((realsocket = accept(si->network_socket, (struct sockaddr *)&client_addr, &sunSize)) == -1) { /* If accept() returns in the CHITCPD_STATE_STOPPING, we don't * care what the error is. We just break out of the loop and * initiate an orderly shutdown. */ if(si->state == CHITCPD_STATE_STOPPING) break; /* If this particular connection fails, no need to kill the entire thread. */ perror("Could not accept() connection on network socket"); continue; } chitcp_addr_str((struct sockaddr *) &client_addr, addr_str, sizeof(addr_str)); chilog(INFO, "TCP connection received from %s", addr_str); /* Check whether the connection already exists. */ connection = chitcpd_get_connection(si, (struct sockaddr *) &client_addr); if (connection != NULL) { /* If this is a loopback connection, there is already an entry in * the connection table, but we need to update its receive socket * and create its connection thread. */ if(chitcp_addr_is_loopback((struct sockaddr *) &client_addr)) { connection->realsocket_recv = realsocket; if(chitcpd_create_connection_thread(si, connection) != CHITCP_OK) { perror("Could not create connection thread."); // TODO: Perform orderly shutdown pthread_exit(NULL); } continue; } else /* Otherwise, this is an error. The peer chiTCP daemon tried to create * a second connection, which shouldn't happen. */ { perror("Peer chiTCP daemon tried to establish more than one connection."); close(realsocket); close(si->server_socket); // TODO: Perform orderly shutdown instead of just exiting pthread_exit(NULL); } } /* If this is not a loopback connection, we need to add an entry * for this connection */ connection = chitcpd_add_connection(si, realsocket, realsocket, (struct sockaddr*) &client_addr); if (!connection) { perror("Could not create a connection to a peer chiTCP daemon"); // TODO: Perform orderly shutdown pthread_exit(NULL); } if(chitcpd_create_connection_thread(si, connection) != CHITCP_OK) { perror("Could not create connection thread."); // TODO: Perform orderly shutdown pthread_exit(NULL); } } /* Close all TCP connections. This will force an exit of the * corresponding connection threads. */ for(int i=0; i < si->connection_table_size; i++) { connection = &si->connection_table[i]; if(!connection->available) { shutdown(connection->realsocket_recv, SHUT_RDWR); if (connection->realsocket_recv != connection->realsocket_send) shutdown(connection->realsocket_send, SHUT_RDWR); pthread_join(connection->thread, NULL); } } chilog(DEBUG, "Network thread is exiting."); pthread_exit(NULL); }
/* * chitcpd_server_thread_func - Server thread function * * This function will spawn a handler thread (see handler.c) for each * new connection on the UNIX socket. * * args: arguments (a serverinfo_t variable in server_threads_args_t) * * Returns: Nothing. * */ void* chitcpd_server_thread_func(void *args) { socklen_t sunSize; handler_thread_t *handler_thread; server_thread_args_t *sta; serverinfo_t *si; handler_thread_args_t *ha; list_t handler_thread_list; int rc; ChitcpdMsg *req; ChitcpdInitArgs *init_args; ChitcpdConnectionType conntype; ChitcpdMsg resp_outer = CHITCPD_MSG__INIT; ChitcpdResp resp_inner = CHITCPD_RESP__INIT; resp_outer.code = CHITCPD_MSG_CODE__RESP; resp_outer.resp = &resp_inner; /* For naming the handler threads we create (for debugging/logging) */ int next_thread_id = 0; pthread_setname_np(pthread_self(), "unix_server"); /* Unpack arguments */ sta = (server_thread_args_t *) args; si = sta->si; list_init(&handler_thread_list); struct sockaddr_un client_addr; /* Accept connections on the UNIX socket */ for(;;) { socket_t client_socket; /* Accept a connection */ sunSize = sizeof(client_addr); if ((client_socket = accept(si->server_socket, (struct sockaddr *)&client_addr, &sunSize)) == -1) { /* If accept() returns in the CHITCPD_STATE_STOPPING, we don't * care what the error is. We just break out of the loop and * initiate an orderly shutdown. */ if(si->state == CHITCPD_STATE_STOPPING) break; /* If this particular connection fails, no need to kill the entire thread. */ perror("Could not accept() connection on UNIX socket"); continue; } /* We receive a single message, which has to be an INIT message */ rc = chitcpd_recv_msg(client_socket, &req); if (rc < 0) { if(si->state == CHITCPD_STATE_STOPPING) break; else { chilog(ERROR, "Error when receiving lead message through UNIX socket"); shutdown(client_socket, SHUT_RDWR); continue; } } if(req->code != CHITCPD_MSG_CODE__INIT) { chilog(ERROR, "Expected INIT message, instead got message code %i", req->code); chitcpd_msg__free_unpacked(req, NULL); shutdown(client_socket, SHUT_RDWR); continue; } /* Unpack INIT request */ assert(req->init_args != NULL); init_args = req->init_args; conntype = init_args->connection_type; /* There are two types of connections: command connections and debug * connections. * * When a command connection is created, a new thread is created to * handle the incoming chisocket commands on that connection (socket, * send, recv, etc.) * * When a debug connection is created, no additional thread is necessary. * The connection on the UNIX socket is simply "handed off" to a * debug monitor that will be associated with a chiTCP socket. * That UNIX socket is then used to send back debug messages. */ if (conntype == CHITCPD_CONNECTION_TYPE__COMMAND_CONNECTION) { /* Create arguments for handler thread */ ha = malloc(sizeof(handler_thread_args_t)); ha->si = si; handler_thread = malloc(sizeof(handler_thread_t)); handler_thread->handler_socket = client_socket; pthread_mutex_init(&handler_thread->handler_lock, NULL); /* Create handler thread to handle this connection */ ha->client_socket = handler_thread->handler_socket; ha->handler_lock = &handler_thread->handler_lock; snprintf(ha->thread_name, 16, "socket-layer-%d", next_thread_id++); if (pthread_create(&handler_thread->thread, NULL, chitcpd_handler_dispatch, ha) != 0) { perror("Could not create a worker thread"); resp_outer.resp->ret = CHITCP_ETHREAD; resp_outer.resp->error_code = 0; rc = chitcpd_send_msg(client_socket, &resp_outer); free(ha); close(ha->client_socket); close(si->server_socket); // TODO: Perform an orderly shutdown instead of exiting pthread_exit(NULL); } resp_outer.resp->ret = CHITCP_OK; resp_outer.resp->error_code = 0; rc = chitcpd_send_msg(client_socket, &resp_outer); list_append(&handler_thread_list, handler_thread); } else if(conntype == CHITCPD_CONNECTION_TYPE__DEBUG_CONNECTION) { int debug_sockfd, debug_event_flags; ChitcpdDebugArgs *debug_args; /* Unpack debug parameter */ assert(init_args->debug != NULL); debug_args = init_args->debug; debug_sockfd = debug_args->sockfd; debug_event_flags = debug_args->event_flags; rc = chitcpd_init_debug_connection(si, debug_sockfd, debug_event_flags, client_socket); if(rc == CHITCP_OK) { resp_outer.resp->ret = CHITCP_OK; resp_outer.resp->error_code = 0; rc = chitcpd_send_msg(client_socket, &resp_outer); } else { chilog(ERROR, "Error when creating debug connection for socket %i", debug_sockfd); resp_outer.resp->ret = CHITCP_EINIT; resp_outer.resp->error_code = rc; rc = chitcpd_send_msg(client_socket, &resp_outer); shutdown(client_socket, SHUT_RDWR); } } else { chilog(ERROR, "Received INIT message with unknown connection type %i", conntype); resp_outer.resp->ret = CHITCP_EINVAL; resp_outer.resp->error_code = 0; rc = chitcpd_send_msg(client_socket, &resp_outer); shutdown(client_socket, SHUT_RDWR); } chitcpd_msg__free_unpacked(req, NULL); } while(!list_empty(&handler_thread_list)) { /* For each handler thread we spawned, we close its socket, which * will force the thread to exit (and we then join it). * * Note that closing a handler thread will also free up all chiTCP * sockets created through that thread, and will also terminate * all associated TCP threads. * * TODO: We should simply detach those threads, since they can exit * before an orderly shutdown and would be left lingering until * we call join here. */ handler_thread_t *ht = list_fetch(&handler_thread_list); /* We don't want to shutdown the handler's socket if an operation is * in progress. The handler thread may have read a command, but * not sent a response back yet */ pthread_mutex_lock(&ht->handler_lock); shutdown(ht->handler_socket, SHUT_RDWR); pthread_mutex_unlock(&ht->handler_lock); pthread_join(ht->thread, NULL); pthread_mutex_destroy(&ht->handler_lock); free(ht); } list_destroy(&handler_thread_list); chilog(DEBUG, "Server thread is exiting."); pthread_exit(NULL); }
/* * chitcpd_server_init - Starts the chiTCP daemon * * si: Server info * * Returns: * - CHITCP_OK: Daemon (including server thread and network thread) * has started correctly. * - CHITCP_ENOMEM: Could not allocate memory for daemon * - CHITCP_ESOCKET: Could not create a socket needed by daemon * - CHITCP_ETHREAD: Could not create a thread needed by daemon * */ int chitcpd_server_init(serverinfo_t *si) { /* TODO: Make this configurable */ si->chisocket_table_size = DEFAULT_MAX_SOCKETS; si->port_table_size = DEFAULT_MAX_PORTS; si->connection_table_size = DEFAULT_MAX_CONNECTIONS; si->ephemeral_port_start = DEFAULT_EPHEMERAL_PORT_START; si->latency = 0.0; /* Initialize chisocket table */ pthread_mutex_init(&si->lock_chisocket_table, NULL); si->chisocket_table = calloc(si->chisocket_table_size, sizeof(chisocketentry_t)); if(si->chisocket_table == NULL) { perror("Could not initialize chisocket table"); return CHITCP_ENOMEM; } for(int i=0; i< si->chisocket_table_size; i++) { pthread_mutex_init(&si->chisocket_table[i].lock_debug_monitor, NULL); si->chisocket_table[i].available = TRUE; } /* Initialize connection table */ pthread_mutex_init(&si->lock_connection_table, NULL); si->connection_table = calloc(si->connection_table_size, sizeof(tcpconnentry_t)); if(si->connection_table == NULL) { perror("Could not initialize connection table"); return CHITCP_ENOMEM; } for(int i=0; i< si->connection_table_size; i++) si->connection_table[i].available = TRUE; /* Initialize port table */ /* This is an array of pointers, and they are all set to NULL */ si->port_table = calloc(si->port_table_size, sizeof(chisocketentry_t*)); if(si->port_table == NULL) { perror("Could not initialize port table"); return CHITCP_ENOMEM; } /* Daemon state lock and condvar */ pthread_mutex_init(&si->lock_state, NULL); pthread_cond_init(&si->cv_state, NULL); /* Delivery list (+ lock and condvar) */ list_init(&si->delivery_queue); pthread_mutex_init(&si->lock_delivery, NULL); pthread_cond_init(&si->cv_delivery, NULL); /* Open libpcap file if a name is provided. Will overwrite any data currentlly in said file. */ if (si->libpcap_file_name != NULL) { si->libpcap_file = fopen(si->libpcap_file_name, "wb"); if (si->libpcap_file == NULL) { perror("Could not open libpcap file for writing"); return CHITCP_ENOMEM; } chilog(INFO, "Logging to libpcap file %s", si->libpcap_file_name); /* Write the libpcap header */ pcap_hdr_t pcap_header; pcap_header.magic_number = 0xa1b23c4d; pcap_header.version_major = 2; pcap_header.version_minor = 4; pcap_header.thiszone = 0; pcap_header.sigfigs = 0; pcap_header.snaplen = 65535; pcap_header.network = 101; fwrite(&pcap_header, sizeof(pcap_hdr_t), 1, si->libpcap_file); } else { si->libpcap_file = NULL; } si->state = CHITCPD_STATE_READY; return CHITCP_OK; }
/* * chitcpd_handler_dispatch - Handler thread function * * Handles a connection on chitcpd's UNIX socket, and dispatches * incoming requests to the appropriate function, using the * dispatch table defined above. * * args: arguments (in handler_thread_args_t) * * Returns: Nothing. * */ void* chitcpd_handler_dispatch(void *args) { handler_thread_args_t *ha = (handler_thread_args_t *) args; serverinfo_t *si = ha->si; socket_t client_socket = ha->client_socket; pthread_mutex_t *handler_lock = ha->handler_lock; pthread_setname_np(pthread_self(), ha->thread_name); ChitcpdMsg *req; ChitcpdMsg resp_outer = CHITCPD_MSG__INIT; ChitcpdResp resp_inner = CHITCPD_RESP__INIT; bool_t done = FALSE; /* Should we keep looping? */ int rc; resp_outer.code = CHITCPD_MSG_CODE__RESP; resp_outer.resp = &resp_inner; do { rc = chitcpd_recv_msg(client_socket, &req); if (rc < 0) break; chilog(TRACE, "Received request (code=%s)", handler_code_string(req->code)); /* We have received a request, so we grab the handler lock to * prevent a race condition when the server is shutting down */ pthread_mutex_lock(handler_lock); /* Call handler function using dispatch table */ rc = handlers[req->code](si, req, &resp_inner); chitcpd_msg__free_unpacked(req, NULL); if(rc != CHITCP_OK) { chilog(ERROR, "Error when handling request."); /* We don't need to bail out just because one request failed */ } /* Send response */ rc = chitcpd_send_msg(client_socket, &resp_outer); if (resp_inner.has_buf) { /* This buffer was allocated in RECV. */ free(resp_inner.buf.data); resp_inner.has_buf = FALSE; } if (resp_inner.socket_state != NULL) { /* This submessage was allocated in GET_SOCKET_STATE. */ free(resp_inner.socket_state); resp_inner.socket_state = NULL; } if (resp_inner.socket_buffer_contents != NULL) { /* This submessage was allocated in GET_SOCKET_BUFFER_CONTENTS. */ if (resp_inner.socket_buffer_contents->snd.data != NULL) free(resp_inner.socket_buffer_contents->snd.data); if (resp_inner.socket_buffer_contents->rcv.data != NULL) free(resp_inner.socket_buffer_contents->rcv.data); free(resp_inner.socket_buffer_contents); resp_inner.socket_buffer_contents = NULL; } /* We're done processing the request (we've run the handler and * we've returned a response). We can release the handler lock and, * if a shutdown is in progress, it will make sure it can proceed * safely */ pthread_mutex_unlock(handler_lock); if (rc < 0) break; } while (!done); /* TODO: Be more discerning about what kind of shutdown this is */ if(si->state == CHITCPD_STATE_STOPPING) chilog(DEBUG, "chiTCP daemon is stopping. Freeing open sockets for this handler..."); else chilog(DEBUG, "Daemon client has disconnected. Freeing open sockets for this handler..."); int freed_sockets = 0; for(int i=0; i < si->chisocket_table_size; i++) { chisocketentry_t *entry = &si->chisocket_table[i]; if(!entry->available && entry->creator_thread == pthread_self()) { chilog(DEBUG, "Freeing socket %i", i); /* TODO: The connection should be aborted (not closed) here. * However, we do not currently support the ABORT call or * RST's so we simply "force close" each socket. */ if(entry->actpas_type == SOCKET_ACTIVE) { /* Any transition to CLOSED will force a termination of the TCP thread */ chitcpd_update_tcp_state(si, entry, CLOSED); pthread_join(entry->socket_state.active.tcp_thread, NULL); } else if(entry->actpas_type == SOCKET_PASSIVE) chitcpd_free_socket_entry(si, entry); freed_sockets++; /* TODO: Close the connection */ } } if (freed_sockets) chilog(DEBUG, "Done freeing open sockets."); else chilog(DEBUG, "This handler had no sockets to free."); chilog(DEBUG, "Handler is exiting."); free(args); return NULL; }
int chitcpd_tcp_state_handle_TIME_WAIT(serverinfo_t *si, chisocketentry_t *entry, tcp_event_type_t event) { chilog(WARNING, "Running handler for TIME_WAIT. This should not happen."); return CHITCP_OK; }