/* 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);
}
Exemple #2
0
/**
 * 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();
  }
}