/* Tiny I/O callback called on recv/send I/O events on idle connections. * It simply sets the CO_FL_SOCK_RD_SH flag so that si_idle_conn_wake_cb() * is notified and can kill the connection. */ static void si_idle_conn_null_cb(struct connection *conn) { if (conn->flags & (CO_FL_ERROR | CO_FL_SOCK_RD_SH)) return; if (fdtab[conn->t.sock.fd].ev & (FD_POLL_ERR|FD_POLL_HUP)) { fdtab[conn->t.sock.fd].linger_risk = 0; conn->flags |= CO_FL_SOCK_RD_SH; } else { conn_drain(conn); } /* disable draining if we were called and have no drain function */ if (!conn->ctrl->drain) __conn_data_stop_recv(conn); }
/** * Main loop. Handles the following: * - Input from STDIN. * - Messages from programs. * - Packets from the socket. * - Timeouts. */ void do_loop() { char buf[MAX_PACKET_SIZE]; conn_t *conn = NULL; while (true) { memset(buf, 0, MAX_PACKET_SIZE); poll(events, NUM_POLL + num_connected, need_timer_in(&last_timeout, ctcp_cfg->timer)); /* Input from stdin. Server will only send to most-recently connected client. */ if (!run_program && events[STDIN_FILENO].revents & POLLIN) { conn = get_connections(); if (conn != NULL) ctcp_read(conn->state); } /* See if we can output more. */ if (events[STDOUT_FILENO].revents & (POLLOUT | POLLHUP | POLLERR)) { for (conn = get_connections(); conn; conn = conn->next) { conn_drain(conn); } } /* Poll for output received from running programs. Send to client client associated with this program instance. */ if (run_program) { conn = get_connections(); while (conn != NULL) { if (conn->poll_fd->revents & POLLIN) { ctcp_read(conn->state); } conn = conn->next; } } /* Receive packet on socket from other hosts. Ignore packets if they are not large enough or not for us. */ if (events[2].revents & POLLIN) { conn = NULL; int len = recv_filter(config->socket, buf, MAX_PACKET_SIZE, 0, &conn); if (len >= FULL_HDR_SIZE) { tcphdr_t *tcp_hdr = (tcphdr_t *) (buf + IP_HDR_SIZE); /* Packet from an established connection. Pass to student code. */ if (conn != NULL) { ctcp_segment_t *segment = convert_to_ctcp(conn, buf, len); len = len - FULL_HDR_SIZE + sizeof(ctcp_segment_t); /* Don't log or forward to student code if it's an ACK from a new connection. */ if (tcp_hdr->th_sport == new_connection && (segment->flags & TH_ACK) && ntohl(segment->seqno) == 1 && ntohl(segment->ackno) == 1) { new_connection = 0; free(segment); } else { if (log_file != -1 || test_debug_on) { log_segment(log_file, config->ip_addr, config->port, conn, segment, len, false, unix_socket); } ctcp_receive(conn->state, segment, len); } } /* New connection. */ else if (tcp_hdr->th_flags & TH_SYN) { conn_t *conn = tcp_new_connection(buf); /* Start a new program associated with this client. */ if (run_program && conn) execute_program(conn); new_connection = tcp_hdr->th_sport; } } } /* Check if timer is up. */ if (need_timer_in(&last_timeout, ctcp_cfg->timer) == 0) { ctcp_timer(); get_time(&last_timeout); } /* Delete connections if needed. */ delete_all_connections(); } }