// read_tracker_response(t) // Reads an RPC response from the tracker using read_to_taskbuf(). // An example RPC response is the following: // // FILE README \ DATA PORTION // FILE osptracker.cc | Zero or more lines. // ... | // FILE writescan.o / // 200-This is a context line. \ MESSAGE PORTION // 200-This is another context line. | Zero or more CONTEXT lines, // ... | which start with "###-", and // 200 Number of registered files: 12 / then a TERMINATOR line, which // starts with "### ". // The "###" is an error code: // 200-299 indicate success, // other codes indicate error. // // This function empties the task buffer, then reads into it until it // finds a terminator line. It returns the number of characters in the // data portion. It also terminates this client if the tracker's response // is formatted badly. (This code trusts the tracker.) static size_t read_tracker_response(task_t *t) { char *s; size_t split_pos = (size_t) -1, pos = 0; t->head = t->tail = 0; while (1) { // Check for whether buffer is complete. for (; pos+3 < t->tail; pos++) if ((pos == 0 || t->buf[pos-1] == '\n') && isdigit((unsigned char) t->buf[pos]) && isdigit((unsigned char) t->buf[pos+1]) && isdigit((unsigned char) t->buf[pos+2])) { if (split_pos == (size_t) -1) split_pos = pos; if (pos + 4 >= t->tail) break; if (isspace((unsigned char) t->buf[pos + 3]) && t->buf[t->tail - 1] == '\n') { t->buf[t->tail] = '\0'; return split_pos; } } // If not, read more data. Note that the read will not block // unless NO data is available. int ret = read_to_taskbuf(t->peer_fd, t); if (ret == TBUF_ERROR) die("tracker read error"); else if (ret == TBUF_END) die("tracker connection closed prematurely!\n"); } }
// task_upload(t) // Handles an upload request from another peer. // First reads the request into the task buffer, then serves the peer // the requested file. static void task_upload(task_t *t) { int blockno; int namelen; size_t offset; assert(t->type == TASK_UPLOAD); // First, read the request from the peer. while (1) { int ret = read_to_taskbuf(t->peer_fd, t); if (ret == TBUF_ERROR) { error("* Cannot read from connection"); goto exit; } else if (ret == TBUF_END || (t->tail && t->buf[t->tail-1] == '\n')) break; } //************************************* //Exercise 2: prevent a peer from requesting an invalid file name if(strlen(t->buf) > FILENAMESIZ + 10){ error("Error: file name requested by peer is too long\n"); goto exit; } assert(t->head == 0); if (osp2p_snscanf(t->buf, t->tail, "GET %s OSP2P\n", t->filename) < 0) { error("* Odd request %.*s\n", t->tail, t->buf); goto exit; } namelen = strlen(t->filename); blockno = t->filename[namelen-1] - '0'; t->filename[namelen-1] = 0; t->head = t->tail = 0; int i; for(i = 0; i < FILENAMESIZ && t->filename[i] != 0; i++){ if(t->filename[i] == '/'){ error("Error: file requested by peer is not in current directory\n"); goto exit; } } //************************************************* if(evil_mode == 0){ t->disk_fd = open(t->filename, O_RDONLY); if (t->disk_fd == -1) { error("* Cannot open file %s", t->filename); goto exit; } lseek(t->disk_fd, BLKSIZE, SEEK_CUR); message("* Transferring file %s\n", t->filename); // Now, read file from disk and write it to the requesting peer. while (1) { int ret = write_from_taskbuf(t->peer_fd, t); if (ret == TBUF_ERROR) { error("* Peer write error"); goto exit; } ret = read_to_taskbuf(t->disk_fd, t); if (ret == TBUF_ERROR) { error("* Disk read error"); goto exit; } else if (ret == TBUF_END && t->head == t->tail) /* End of file */ break; } } else{ //*************************** //Exercise 3: send null device data to peer t->disk_fd = open("/dev/null", O_RDONLY); if (t->disk_fd == -1) { error("* Cannot open file\n"); goto exit; } message("* Transferring file\n"); while (1) { int ret = write_from_taskbuf(t->peer_fd, t); if (ret == TBUF_ERROR) { error("* Peer write error"); goto exit; } ret = read_to_taskbuf(t->disk_fd, t); if (ret == TBUF_ERROR) { error("* Disk read error"); goto exit; } } //**************************** } message("* Upload of %s complete\n", t->filename); exit: task_free(t); }
// task_download(t, tracker_task) // Downloads the file specified by the input task 't' into the current // directory. 't' was created by start_download(). // Starts with the first peer on 't's peer list, then tries all peers // until a download is successful. static void task_download(task_t *t, task_t *tracker_task) { int i, ret = -1; size_t messagepos; assert((!t || t->type == TASK_DOWNLOAD) && tracker_task->type == TASK_TRACKER); //Read the checksum of the file from tracker: char *file_digest = 0; osp2p_writef(tracker_task->peer_fd, "MD5SUM %s\n", t->filename); messagepos = read_tracker_response(tracker_task); message("Tracker buf: %s\n", tracker_task->buf); if (tracker_task->buf[messagepos] != '2') { error("* Tracker error message while requesting checksum of '%s':\n%s", t->filename, &tracker_task->buf[messagepos]); } else{ file_digest = malloc(MD5_TEXT_DIGEST_SIZE+1); memcpy(file_digest, tracker_task->buf, MD5_TEXT_DIGEST_SIZE); file_digest[MD5_TEXT_DIGEST_SIZE] = 0; } // Quit if no peers, and skip this peer if (!t || !t->peer_list) { error("* No peers are willing to serve '%s'\n", (t ? t->filename : "that file")); task_free(t); return; } else if (t->peer_list->addr.s_addr == listen_addr.s_addr && t->peer_list->port == listen_port) goto try_again; t->disk_fd = open(t->disk_filename, O_WRONLY | O_CREAT | O_EXCL, 0666); if (t->disk_fd == -1 && errno != EEXIST) { error("* Cannot open local file"); goto try_again; } else if (t->disk_fd != -1) message("* Saving result to '%s'\n", t->disk_filename); int blockno = 0; int i_peer = 0; peer_t *apeer; while(1){ apeer = get_peer(t->peer_list, i_peer); // Connect to the peer and write the GET command message("* Connecting to %s:%d to download '%s'\n", inet_ntoa(apeer->addr), apeer->port, t->filename); t->peer_fd = open_socket(apeer->addr, apeer->port); if (t->peer_fd == -1) { error("* Cannot connect to peer: %s\n", strerror(errno)); goto try_again; } if(evil_mode == 0) osp2p_writef(t->peer_fd, "GET %s%d OSP2P\n", t->filename, blockno); else{ //******************************* //Exercise 3: try to overflow a peer's buffer by requesting a file with a long name char evil_name[2*FILENAMESIZ]; int i_name; for(i_name = 0; i_name < 2*FILENAMESIZ - 1; i_name++) evil_name[i_name] = 'a'; evil_name[i_name] = 0; osp2p_writef(t->peer_fd, "GET %s OSP2P\n", evil_name); //********************************** } // Open disk file for the result. // If the filename already exists, save the file in a name like // "foo.txt~1~". However, if there are 50 local files, don't download // at all. /* for (i = 0; i < 50; i++) { if (i == 0) strcpy(t->disk_filename, t->filename); else sprintf(t->disk_filename, "%s~%d~", t->filename, i); t->disk_fd = open(t->disk_filename, O_WRONLY | O_CREAT | O_EXCL, 0666); if (t->disk_fd == -1 && errno != EEXIST) { error("* Cannot open local file"); goto try_again; } else if (t->disk_fd != -1) { message("* Saving result to '%s'\n", t->disk_filename); break; } } if (t->disk_fd == -1) { error("* Too many local files like '%s' exist already.\n\ * Try 'rm %s.~*~' to remove them.\n", t->filename, t->filename); task_free(t); return; }*/ lseek(t->disk_fd, BLKSIZE, SEEK_CUR); // Read the file into the task buffer from the peer, // and write it from the task buffer onto disk. while (1) { int ret = read_to_taskbuf(t->peer_fd, t); if (ret == TBUF_ERROR) { error("* Peer read error"); goto try_again; } else if (ret == TBUF_END && t->head == t->tail) /* End of file */ break; ret = write_from_taskbuf(t->disk_fd, t); if (ret == TBUF_ERROR) { error("* Disk write error"); goto try_again; } //********************************* //Exercise 2: prevent from downloading a file that is too large if(t->total_written > MAX_FILE_SIZE){ error("* Error: file too big for download\n"); goto try_again; } //********************************** } if(ret == TBUF_END && t->head == t->tail) break; //Compare the checksum of file on disk to checksum reported by tracker: if(file_digest){ char *download_digest = create_digest(t->disk_filename); message("* Tracker's checksum for file '%s' is: %s\n", t->filename, file_digest); message("* Checksum of the downloaded file is: %s\n", download_digest); if(strcmp(file_digest, download_digest) != 0){ error("* Downloaded a corrupted file!!! Try download again...\n"); } } blockno++; i_peer++; } // Empty files are usually a symptom of some error. if (t->total_written > 0) { message("* Downloaded '%s' was %lu bytes long\n", t->disk_filename, (unsigned long) t->total_written); // Inform the tracker that we now have the file, // and can serve it to others! (But ignore tracker errors.) if (strcmp(t->filename, t->disk_filename) == 0) { osp2p_writef(tracker_task->peer_fd, "HAVE %s\n", t->filename); (void) read_tracker_response(tracker_task); } task_free(t); return; } error("* Download was empty, trying next peer\n"); try_again: if (t->disk_filename[0]) unlink(t->disk_filename); // recursive call task_pop_peer(t); task_download(t, tracker_task); }