/* * steve diverdi * cs125 project 4 * * tftp client * see RFC 1350 */ // INCLUDES------------------------------------------------------------- #include "common.hh" // --------------------------------------------------------------------- int main(int, char**); void usageError(); int readFile(char*, int, struct sockaddr*); int writeFile(char*, int, struct sockaddr*); int sendRRQ(int, char*, struct sockaddr*, int); int sendWRQ(int, char*, struct sockaddr*, int); // --------------------------------------------------------------------- /* * Top level call. gets the command line arguments, creates the socket, * and determines if this is a read or write, calling the appropriate * function. Returns 0 if the transfer succeeded, -1 if not. */ int main ( int argc, char* argv[]) { char* server; int port; int op; char* filename; if (argc == 4) { server = argv[1]; port = SERVER_PORT; if (strncmp("-r", argv[2], 2) == 0) op = RRQ; else if (strncmp("-w", argv[2], 2) == 0) op = WRQ; else usageError(); filename = argv[3]; } else if (argc == 5) { server = argv[1]; port = atoi(argv[2]); if (strncmp("-r", argv[2], 2) == 0) op = RRQ; else if (strncmp("-w", argv[2], 2) == 0) op = WRQ; else usageError(); filename = argv[3]; } else usageError(); int s = createSocket(0); struct sockaddr server_addr; createAddr((struct sockaddr_in*) &server_addr, server, port); int result; if (op == RRQ) result = readFile(filename, s, &server_addr); else result = writeFile(filename, s, &server_addr); return result; } /* * Prints the usage message and exits. */ void usageError () { cerr << "usage: tftp client [ port ] [ -r | -w ] filename" << endl; exit(-1); } // --------------------------------------------------------------------- /* * call to read a file from the server and put it in a local file of the * same name. returns 0 on success, -1 on failure. Arguments are the * name of the file to retrieve, a socket id, and the address of the * server */ int readFile ( char* filename, int s, struct sockaddr* server_addr) { // open the local file for writing int file; mode_t mode = S_IRUSR|S_IWUSR; if ((file = creat(filename, mode)) == -1) { cerr << "Couldn't open file for writing. Error " << errno << endl; return -1; } // send the initial read request to the server int server_len = sizeof(struct sockaddr); if (sendRRQ(s, filename, server_addr, server_len) == -1) return -1; int server_tid = -1; bool firstLoop; int blockNum = 1; int blen; struct data_packet incoming; int ilen = MAX_PACKET; // loop as long as we're receiving data packets 512 bytes long. we // know we're done when we get the last packet, which is less than // 512 bytes. while (ilen == MAX_PACKET) { // receive the data packet if (recvDATA(s, &incoming, &ilen, blockNum, server_tid, server_addr, &server_len) == -1) return -1; // print a . for each 512 byte packet received cout << "." << flush; // first time we receive a packet from the server, store its port // number for later use if (firstLoop) server_tid = ((struct sockaddr_in*) server_addr)->sin_port; firstLoop = false; // write the data to the local file if ((blen = write(file, incoming.data, ilen - 4)) == -1) { if (errno == EDQUOT) error(TDSKFUL, "Disk full or allocation exceeded", s, server_addr, server_len); else error(TNOTDEF, "Couldn't write file", s, server_addr, server_len); return -1; } // get the block number off of the received data packet blockNum = getBlock((struct packet*) &incoming); // send an acknowledgement to the server if (sendACK(s, blockNum, server_addr, server_len, (ilen == MAX_PACKET)) == -1) return -1; blockNum++; } // we're done, so close the new file and exit cout << endl; close(file); return 0; } /* * call to write a local file to the server. returns 0 on success, -1 on * failure. Arguments are the name of the file to write, a socket id, * and the address of the server */ int writeFile ( char* filename, int s, struct sockaddr* server_addr) { // open the local file for reading int file; if ((file = open(filename, O_RDONLY)) == -1) { cerr << "Couldn't open file for reading. Error " << errno << endl; return -1; } // send the initial write request to the server int server_len = sizeof(struct sockaddr); sendWRQ(s, filename, server_addr, server_len); int server_tid = -1; bool firstLoop = true; int blockNum = 0; char blockData[MAX_DATA]; int blen = MAX_DATA; // keep looping as long as we're still reading in the max amount of // data possible. when we read less than a full packet of data, // we know we've reached the last packet. while (blen == MAX_DATA) { // receive the server's acknowledgement if (recvACK(s, blockNum, server_tid, server_addr, &server_len) == -1) return -1; // store the server's port number after we receive our first // packet, for later verification if (firstLoop) server_tid = ((struct sockaddr_in*) server_addr)->sin_port; firstLoop = false; // read the next block of data from the file blockNum++; blen = read(file, blockData, MAX_DATA); // send the data to the server if (sendDATA(s, blockNum, blockData, blen, server_addr, server_len) == -1) return -1; // print one . for each 512 bytes of transfer cout << "." << flush; } // receive the final acknowledgement if (recvACK(s, blockNum, server_tid, server_addr, &server_len) == -1) return -1; // all done, so close the file and return 0 cout << endl; close(file); return 0; } // --------------------------------------------------------------------- /* * Form and send a read request to a server. Takes as arguments, a * socket id, the file name to read, the server's address and the length * of the server's address. returns 0 on success, -1 on failure */ int sendRRQ ( int s, char* filename, struct sockaddr* server_addr, int server_len) { // make the request packet struct packet request; int rlen; makeRRQ(&request, &rlen, filename, "octet"); // send the request packet to the server if (sendto(s, &request, rlen, 0, server_addr, server_len) < 0) { cerr << "Couldn't send request. Error " << errno << endl; return -1; } bool response = false; for (int i = 0; (i < RETRIES) && (! response); i++) { // check if a response is ready within the time out response = ! timeout(s); if (! response) { // since nothing was returned, resend our request packet if (sendto(s, &request, rlen, 0, server_addr, server_len) < 0) { cerr << "Couldn't send request. Error " << errno << endl; return -1; } } } // check if a response timed out the last time. if it does, fail if (!response) { error(TNOTDEF, "Connection timed out", s, server_addr, server_len); return -1; } return 0; } /* * form and send a write request. Takes as arguments a socket id, the * file to write, the server's address, and the length of the server's * address. */ int sendWRQ ( int s, char* filename, struct sockaddr* server_addr, int server_len) { // make the request packet struct packet request; int rlen; makeWRQ(&request, &rlen, filename, "octet"); // send the request packet to the server if (sendto(s, &request, rlen, 0, server_addr, server_len) < 0) { cerr << "Couldn't send request. Error " << errno << endl; return -1; } bool response = false; for (int i = 0; (i < RETRIES) && (! response); i++) { // check if a response is ready within the time out response = ! timeout(s); if (! response) { // no response was waiting, so resend our request packet if (sendto(s, &request, rlen, 0, server_addr, server_len) < 0) { cerr << "Couldn't send request. Error " << errno << endl; return -1; } } } // check if a response comes within the time out. if one doesn't // this time, fail. if (! response) { error(TNOTDEF, "Connection timed out", s, server_addr, server_len); return -1; } return 0; }