/* Server side - UDP Code */ /* By Connor McKee */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/wait.h> #include <fcntl.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include "gethostbyname.h" #include "networks.h" #include "safeUtil.h" #include "cpe464.h" #include "pollLib.h" #include "pdu.h" #include "window.h" #define MAXBUF 80 #define DEBUG_FLAG 1 // server states typedef enum STATE { INIT, DONE, FILENAME, SEND_DATA, WAIT_ACK, TIMEOUT_ACK, WAIT_EOF_ACK, TIMEOUT_EOF_ACK } STATE; // Information passed on from the client in setup/filename packet typedef struct { uint32_t windowSize; uint16_t bufferSize; char fileName[MAX_FNAME_LEN+1]; FILE *dataFile; } ClientInfo; void processServer(int serverSocket, double errRate); void processClient(uint8_t *pkt, int32_t recvLen, ConnectionData *client); int checkArgs(int argc, char *argv[]); double getRate(char * argv[]); int safeRecvBuf(int serverSocket, uint8_t *pkt, int len, ConnectionData *client, uint8_t *flag, uint32_t *seqNum); STATE processFilenameEX(ConnectionData *client, uint8_t *pkt, int32_t recvLen, ClientInfo *meta); void sendFilenameResponse (ConnectionData *client, uint8_t response); STATE sendData(ConnectionData *client, ClientInfo metadata, Window *window, uint32_t *seqNum); void handleDataSending(ConnectionData *client, Window *window, uint32_t *seqNum, uint8_t *dataPayload, uint32_t bytesRead); int processWindowClosed(ConnectionData *client, Window *window, int *retryCount); STATE processWaitEOFACK(ConnectionData *client, ClientInfo metadata, Window *window, uint32_t *seqNum); bool processRRandSREJOpen(ConnectionData *client, Window *window); bool processRRandSREJ(ConnectionData *client, Window *window); void processRR(Window *window, uint8_t *packet); void processSREJ(ConnectionData *client, Window *window, uint8_t *packet); void sendLowestPacket(ConnectionData *client, Window *window); void sendEOF(ConnectionData *client, uint32_t *seqNum); void cleanExit(Window *window, ConnectionData *client); int main ( int argc, char *argv[] ) { int socketNum = 0; int portNumber = 0; setupPollSet(); portNumber = checkArgs(argc, argv); sendErr_init(getRate(argv), DROP_ON, FLIP_ON, DEBUG_ON, RSEED_ON); socketNum = udpServerSetup(portNumber); processServer(socketNum, getRate(argv)); close(socketNum); return 0; } void processServer(int serverSocket, double errRate) { pid_t pid = 0; int currentSocket = 0; int clientSocket = 0; int clientStatus = 0; int recvLen = 0; uint8_t packet[MAX_BUFFER] = {0}; ConnectionData client; uint8_t flag = 0; uint32_t seqNum = 0; addToPollSet(serverSocket); while (1) { // block to wait for a new client to connect if ((currentSocket = pollCall(POLL_WAIT_FOREVER)) != -1) { // Initial receive to gather client info recvLen = safeRecvBuf(serverSocket, packet, MAX_BUFFER, &client, &flag, &seqNum); // check for CRC Error, if error, continue to next response, don't fork if(!validChecksum(packet, recvLen)) { continue; } // Check for fork errors, new process started otherwise if ((pid = fork()) < 0) { perror("Fork failed"); exit(-1); } // Child, client process if (pid == 0) { // Close the server socket and remove from pollSet close(serverSocket); removeFromPollSet(serverSocket); // Set up the socket for the child client process and add it to the pollSet clientSocket = socket(AF_INET6, SOCK_DGRAM, 0); client.socket = clientSocket; addToPollSet(clientSocket); // received data from client, process on the new client socket processClient(packet, recvLen, &client); //done processing exit(0); } // For single client processing // received data from client, process //client.socket = serverSocket; //processClient(serverSocket, packet, recvLen, &client); //done processing //exit(0); } else { printf("Poll timed out waiting for client\n"); // Wait for dead children while (waitpid(-1, &clientStatus, WNOHANG) > 0) { } } } } void processClient(uint8_t *packet, int32_t recvLen, ConnectionData *client) { STATE state = INIT; uint32_t seqNum = 0; ClientInfo metadata; Window window; while (state != DONE) { switch(state) { case INIT: state = FILENAME; break; case FILENAME: state = processFilenameEX(client, packet, recvLen, &metadata); break; case SEND_DATA: // setup the window window = createWindow(metadata.windowSize); state = sendData(client, metadata, &window, &seqNum); break; case WAIT_EOF_ACK: state = processWaitEOFACK(client, metadata, &window, &seqNum); return; break; case DONE: cleanExit(&window, client); break; default: printf("got to default in state machine\n"); state = DONE; break; } } // Clean up the window table, exited the state machine free(window.table); } STATE processWaitEOFACK(ConnectionData *client, ClientInfo metadata, Window *window, uint32_t *seqNum) { uint8_t packet[MAX_BUFFER] = {0}; STATE nextState = DONE; int retryCount = 0; int recvLen = 0; uint32_t eofSeqNum = *seqNum; // send out the EOF initially sendEOF(client, seqNum); // continuously loop to poll and get client packets. No more sending of data now. // could be getting RRs, SREJS, EOF_ACK (Finally done! clean up after the client) // else, poll expires, resend EOF, increment counter, poll again while (1) { if (pollCall(POLL_WAIT_1_SEC) != -1) { // Data to process recvLen = safeRecvfrom(client->socket, packet, MAX_BUFFER, 0, (struct sockaddr *) &((*client).address), &((*client).len)); // Verify CRC if (!validChecksum(packet, recvLen)) { // Bad packet, continue continue; } // Check flags if (packet[FLAG_LOC] == RR_FLAG) { processRR(window, packet); retryCount = 0; } else if (packet[FLAG_LOC] == SREJ_FLAG) { // if the SREJ is for EOF, don't process, send lowest packet until fixed if (getOffsetSeqNumber(packet) == eofSeqNum) { sendLowestPacket(client, window); } else { processSREJ(client, window, packet); retryCount = 0; } } else if (packet[FLAG_LOC] == EOF_ACK_FLAG) { printf("Received Client EOF ACK. Cleaning up and Exiting.\n"); nextState = DONE; break; } else { // Flag was something else, garbage? Send EOF again and increment sendEOF(client, seqNum); retryCount++; } } else { // timeout, no data from client. Send EOF again, increment, continue. sendEOF(client, seqNum); retryCount++; } if (retryCount > 9) { // Poll timed out ten times. Client unresponsive printf("Client EOF ACK lost. Cleaning up.\n"); nextState = DONE; break; } } return nextState; } // reads from the data file, places information into the buffer and sets the next state based on // the sizes set in ClientInfo STATE sendData(ConnectionData *client, ClientInfo metadata, Window *window, uint32_t *seqNum) { uint8_t dataPayload[MAX_BUFFER] = {0}; uint32_t bytesRead = 0; int retryCount = 0; bool eofFound = false; STATE nextState = DONE; // loop until EOF read from file while(!eofFound) { while (isWindowOpen(*window)) { // Read the specified number of bytes (1 byte chunks) from file and fill a payload bytesRead = fread(dataPayload, sizeof(uint8_t), metadata.bufferSize, metadata.dataFile); // check for EOF after reading if (bytesRead == 0) { if (feof(metadata.dataFile)) { // EOF! Need to send EOF packet and move to EOF ack state eofFound = true; } if (ferror(metadata.dataFile)) { printf("ERROR WHEN READING FROM FILE: %s\n", metadata.fileName); nextState = DONE; } // if 0 bytes were read, either EOF was found and set or an error occurred while reading. // Break from loop and double check for EOF break; } // EOF not found, send data handleDataSending(client, window, seqNum, dataPayload, bytesRead); // Handle and process any RR/SREJs if (!processRRandSREJOpen(client, window)) { // discard the packet and continue looking for more if checksum failed continue; } } // window closed while(!isWindowOpen(*window)) { if(processWindowClosed(client, window, &retryCount) < 0) { // Tried to break deadlock 10 times, time to exit return DONE; } } // reset the retry counter to 0 here retryCount = 0; } // if EOF, go to EOF sending state here if (eofFound) { nextState = WAIT_EOF_ACK; } return nextState; } // Main processing when the window is closed. Tries to process SREJs/RRs, if none available, sends back the lowest packet. // Returns -1 on recovery failure int processWindowClosed(ConnectionData *client, Window *window, int *retryCount) { //if count is 10, we're done trying to recover. if ((*retryCount) > 9) { // Error'd out, go to DONE state printf("Deadlock recovery failed, aborting transfer\n"); //cleanExit(window, client); return -1; } // try to poll at one second for a max of ten times to try and recv RR/SREJs, // else enter recovery and send lowest packet if (pollCall(POLL_WAIT_1_SEC) != -1) { // there's a packet waiting to be processed, hopefully this will allow processRRandSREJ(client, window); // Reset count, recv'd some type of data from client whether valid or not (*retryCount) = 0; } else { // entered recovery, send the packet pointed to by lower sendLowestPacket(client, window); (*retryCount)++; } return 1; } // Sends an EOF packet to a client void sendEOF(ConnectionData *client, uint32_t *seqNum) { uint8_t *PDU; uint8_t payload = 0; PDU = createPDU(*seqNum, EOF_FLAG, &payload, 0); safeSendto(client->socket, PDU, HEADER_LEN, 0, (struct sockaddr *) &(client->address), client->len); } // send the lowest (RR) packet to client void sendLowestPacket(ConnectionData *client, Window *window) { windowEntry we = getLowestEntry(*window); // TODO, MAKE A FUNCTION THAT WILL LOOP TO FIND THE LOWEST VALID PACKET if (!we.isValid) { printf("Oh no, the lowest packet was invalid. What to do???\n"); } safeSendto(client->socket, we.PDU, we.packetLength, 0, (struct sockaddr *) &(client->address), client->len); } // Processes a single RR/SREJ bool processRRandSREJ(ConnectionData *client, Window *window) { uint8_t packet[MAX_BUFFER] = {0}; int recvLen = 0; recvLen = safeRecvfrom(client->socket, packet, MAX_BUFFER, 0, (struct sockaddr *) &((*client).address), &((*client).len)); // check for CRC error or flags other than (RR, 5) or (SREJ, 6) if (!validChecksum(packet, recvLen)) { // discard the packet and continue looking for more return false; } else if (packet[FLAG_LOC] == RR_FLAG) { // RR recv'd, get RR seqNum and modify window processRR(window, packet); } else if (packet[FLAG_LOC] == SREJ_FLAG) { // SREJ recv'd, get the entry/pdu and send it back processSREJ(client, window, packet); } return true; } // Main processing function to poll for SREJs and RRs from a client when the window is open bool processRRandSREJOpen(ConnectionData *client, Window *window) { int currentSocket = 0; // while RR/SREJS coming in, process while ((currentSocket = pollCall(0)) != -1) { if (!processRRandSREJ(client, window)) { return false; } } return true; } // Handles the sending of data packets (flag 3) to the connected client void handleDataSending(ConnectionData *client, Window *window, uint32_t *seqNum, uint8_t *dataPayload, uint32_t bytesRead) { uint8_t *PDU; int dataLen = bytesRead + HEADER_LEN; PDU = createPDU(*seqNum, DATA_FLAG, dataPayload, bytesRead); // Now add this PDU to the window addPDUToWindow(window, PDU, dataLen); // Now send this PDU and increment the sequeunce number safeSendto(client->socket, PDU, dataLen, 0, (struct sockaddr *) &(client->address), client->len); (*seqNum)++; } // Used for the initial setup and filename exchange of the client. STATE processFilenameEX(ConnectionData *client, uint8_t *packet, int32_t recvLen, ClientInfo *metadata) { FILE *dataFile = NULL; uint8_t fnameLen = 0; STATE nextState = DONE; // verify valid checksum, if invalid, quit client and wait for a good filename packet request if (in_cksum((unsigned short *) packet, recvLen) != 0) { nextState = DONE; } // get the metadata [header | winsize | buffersize | fnamelen | fname] memcpy(&(metadata->windowSize), &packet[7], sizeof(uint32_t)); metadata->windowSize = ntohl(metadata->windowSize); memcpy(&(metadata->bufferSize), &packet[11], sizeof(uint16_t)); metadata->bufferSize = ntohs(metadata->bufferSize); memcpy(&fnameLen, &packet[13], sizeof(uint8_t)); memcpy(&(metadata->fileName), &packet[14], fnameLen); // Try to open the file specified for reading (determines next packet sending) if ((dataFile = fopen(metadata->fileName, "r")) == NULL) { // couldn't open the file, bad filename, send the response sendFilenameResponse(client, FNAME_BAD); nextState = DONE; } else { // Good file! send the response metadata->dataFile = dataFile; sendFilenameResponse(client, FNAME_GOOD); nextState = SEND_DATA; } return nextState; } // Forms and sends the filename response packet back to the connected client (flag 8) void sendFilenameResponse (ConnectionData *client, uint8_t response) { // form the packet and send (seq num doesn't matter, flag=8, len=header+1) uint8_t *packet = createPDU(0, FNAME_RESPONSE_FLAG, &response, 1); safeSendto(client->socket, packet, HEADER_LEN+1, 0, (struct sockaddr *) &(client->address), client->len); } // Process the SREJ packet by getting the correct packet from the window and sending it to the requesting client void processSREJ(ConnectionData *client, Window *window, uint8_t *packet) { windowEntry we; uint32_t srej = getOffsetSeqNumber(packet); // get the sequence number for SREJ and get that window entry we = handleSREJ(*window, srej); // send off the pdu safeSendto(client->socket, we.PDU, we.packetLength, 0, (struct sockaddr *) &(client->address), client->len); } // Process the RR by getting the RR number and sliding the window void processRR(Window *window, uint8_t *packet) { uint32_t rr = getOffsetSeqNumber(packet); // update window handleRR(window, rr); } // Populates the client info on the first recv int safeRecvBuf(int serverSocket, uint8_t *pkt, int len, ConnectionData *client, uint8_t *flag, uint32_t *seqNum) { (*client).len = sizeof(struct sockaddr_in6); return safeRecvfrom(serverSocket, pkt, len, *flag, (struct sockaddr *) &((*client).address), &((*client).len)); } // Closes the socket created for the client, removes it from the pollset, and frees memory allocated for the window table void cleanExit(Window *window, ConnectionData *client) { close(client->socket); removeFromPollSet(client->socket); // free(window->table); //exit(1); } // CL argument checking int checkArgs(int argc, char *argv[]) { // Checks args and returns port number int portNumber = 0; if (argc > 3 || argc < 2) { fprintf(stderr, "Usage %s error-rate [optional port number]\n", argv[0]); exit(-1); } if (argc == 3) { portNumber = atoi(argv[2]); } return portNumber; } // Gets and converts the errRate string passed on the CL double getRate(char * argv[]) { double rate = 0.0; rate = atof(argv[1]); // rate == 0 || ---- error rate can be zero if (rate > 1.0 || rate < 0) { printf("error-rate must be between 0-1\n"); exit(1); } return rate; }