static void log_group( const struct asym *grp, const struct dsym *segs ) /**********************************************************************/ { unsigned i; const char *pdots; struct seg_item *curr; i = grp->name_size; pdots = (( i >= DOTSMAX ) ? "" : dots + i + 1); LstPrintf( "%s %s %s", grp->name, pdots, strings[LS_GROUP] ); LstNL(); /* the FLAT groups is always empty */ if ( grp == (struct asym *)ModuleInfo.flat_grp ) { for( ; segs; segs = segs->next ) { log_segment( (struct asym *)segs, grp ); } } else for( curr = ((struct dsym *)grp)->e.grpinfo->seglist; curr; curr = curr->next ) { log_segment( (struct asym *)curr->seg, grp ); } }
/** * Sends a cTCP segment to a destination associated with the provided * connection object. * * conn: Connection object. * segment: Pointer to cTCP segment to send. * len: Length of the segment (including the cTCP header and data). * * returns: The number of bytes actually sent, 0 if nothing was sent, -1 if * there in an error. */ int conn_send(conn_t *conn, ctcp_segment_t *segment, size_t len) { ASSERT_CONN; /* Check parameters. */ if (conn == NULL || segment == NULL) { fprintf(stderr, "[ERROR] NULL parameters in conn_send\n"); return -1; } /* Make a copy of the segment first. */ ctcp_segment_t *segment_copy = calloc(len, 1); memcpy(segment_copy, segment, len); /* Fork process off in order to do unreliability. Keep track of whether we are forked or not. */ int fork_level = 0; bool am_i_forked = 0; /* Segment drop. Don't send the segment. */ if ((test_debug_on && !tester_did_unreliable && opt_drop) || (!test_debug_on && rand_percent(fork_level) < opt_drop)) { tester_did_unreliable = true; if (DEBUG) { fprintf(stderr, "[DEBUG] Dropping segment\n"); print_hdr_ctcp(segment_copy); } free(segment_copy); return len; } /* Segment duplication. Fork another process to send the other segment. */ if ((test_debug_on && !tester_did_unreliable && opt_duplicate) || (!test_debug_on && rand_percent(fork_level) < opt_duplicate)) { tester_did_unreliable = true; if (DEBUG) { fprintf(stderr, "[DEBUG] Duplicating segment\n"); print_hdr_ctcp(segment_copy); } if (fork() == 0) { am_i_forked = 1; fork_level++; } } /* Segment delay. Fork another process to delay the segment, and kill the current one. */ if ((test_debug_on && !tester_did_unreliable && opt_delay) || (!test_debug_on && rand_percent(fork_level) < opt_delay)) { tester_did_unreliable = true; if (DEBUG) { fprintf(stderr, "[DEBUG] Delaying segment\n"); print_hdr_ctcp(segment_copy); } /* Forked process. Sleep for a bit. */ if (fork() == 0) { am_i_forked = 1; fork_level++; sleep(rand() % 5); } /* Original process. */ else { free(segment_copy); return len; } } /* Segment corruption. Flip bits in the segment after the TCP flags (to avoid corrupting the flags, which may cause problems). */ bool do_corrupt = rand_percent(fork_level) < opt_corrupt; uint16_t data_length = len - sizeof(ctcp_segment_t) + sizeof(uint32_t); uint16_t rand_bit = rand() % (data_length * 8 - 1) + (sizeof(ctcp_segment_t) - sizeof(uint32_t)) * 8; if ((test_debug_on && !tester_did_unreliable && opt_corrupt) || (!test_debug_on && do_corrupt)) { tester_did_unreliable = true; if (DEBUG) { fprintf(stderr, "[DEBUG] Corrupting segment\n"); print_hdr_ctcp(segment_copy); } flipbit(segment_copy, rand_bit); } uint16_t data_len = len - sizeof(ctcp_segment_t); uint16_t total_len = FULL_HDR_SIZE + data_len; if (log_file != -1 || test_debug_on) { log_segment(log_file, config->ip_addr, config->port, conn, segment_copy, len, true, unix_socket); } /* Convert from a cTCP segment to a real one and finally send the segment. */ char *pkt = convert_to_datagram(conn, segment_copy, len); int n = send_pkt(conn, config->socket, pkt, total_len, 0); if (DEBUG) { fprintf(stderr, "[DEBUG] Sent segment\n"); print_hdr_ctcp(segment_copy); } free(pkt); free(segment_copy); /* Kill forked process. */ if (am_i_forked) exit(0); /* Return number of bytes sent. Need to subtract some because the return value is actually the size of the TCP segment instead of the cTCP segment. */ if (n >= (long int)TCP_HDR_SIZE) return n - (TCP_HDR_SIZE + IP_HDR_SIZE - sizeof(ctcp_segment_t)); return n; }
/** * 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(); } }