/* This code contains a tiny bit of an updated version of the sample code from "Computer Networks: * A Systems Approach," 5th Edition by Larry L. Peterson and Bruce S. Davis. Some code comes from * man pages, mostly getaddrinfo(3). * * Furthermore, this code has been downloaded and edited for Program 2 by Sloane Tribble * and Chris Gemperle of EECE-446-02-3096, Fall 2022 * * * */ #include <stdio.h> #include <stdlib.h> #include <dirent.h> #include <sys/socket.h> #include <netdb.h> #include <string.h> #include <unistd.h> #include <inttypes.h> #include <arpa/inet.h> #include <netinet/in.h> // hold solution peer's IP and port #, in HOST byte order // for connecting to solution peer struct SearchResponseHolder { uint32_t peer_id; char ip[INET_ADDRSTRLEN]; char port[6]; }; int lookup_and_connect( const char *host, const char *service ); void join_request(const uint32_t peer_id, char *peer_buffer); int publish(char *peer_buffer); int fetch(char* peer_buffer, int reg_sockfd, struct SearchResponseHolder *holder); int search_request(char *peer_buffer); int parse_search_response(char *peer_buffer, struct SearchResponseHolder *holder); int recvall(int sockfd, char *buffer, int *len ); int recv_and_write(int sockfd, char *buffer, char *filename); int sendall(int s, char *buf, int *len); // Take three command line arguments: registry location (IP addr or hostname), registry port number, peer's ID int main(int argc, char* argv[] ) { const char *host = argv[1]; const char *port = argv[2]; int reg_sockfd; // Lookup IP and connect to server if ( ( reg_sockfd = lookup_and_connect( host, port ) ) < 0 ) { exit( 1 ); } struct SearchResponseHolder s_srHolder; char command[10] = {'\0'}; // Holds user commands char peer_buffer[1200]; // used as a buffer for all send/recv data int reply_len = 10; int msg_size; // Used to determine length for sendall do { printf("Enter a command: "); scanf("%s", command); if(strcmp(command,"EXIT") == 0){ printf("You entered EXIT"); } else if(strcmp(command,"JOIN") == 0){ join_request(htonl(atol(argv[3])), peer_buffer); msg_size = 5; peer_buffer[5] = '\0'; sendall(reg_sockfd, peer_buffer, &msg_size); } else if(strcmp(command,"SEARCH") == 0){ // The search "if" is a monster, still trying to clean up my mess msg_size = search_request(peer_buffer) + 2; sendall(reg_sockfd, peer_buffer, &msg_size); recvall(reg_sockfd, peer_buffer, &reply_len); if (parse_search_response(peer_buffer, &s_srHolder) == 0) { printf("File not indexed by registry\n"); } else { printf("File found at\n Peer %u\n %s:%s\n", s_srHolder.peer_id, s_srHolder.ip, s_srHolder.port); } } else if(strcmp(command,"PUBLISH") == 0) { msg_size = publish(peer_buffer); sendall(reg_sockfd, peer_buffer, &msg_size); } else if(strcmp(command,"FETCH") == 0){ fetch(peer_buffer, reg_sockfd, &s_srHolder); } else printf("Not a Recognized Command.\n"); // Should probably not output this and just go straight back to start. } while(!(strcmp(command, "EXIT") == 0)); close( reg_sockfd ); // Close Cleanly return 0; } int fetch(char* peer_buffer, int reg_sockfd, struct SearchResponseHolder *holder) { int peer_sockfd; int reply_len = 10; int msg_size = search_request(peer_buffer) + 2; char file_name[strlen(peer_buffer + 1)]; strcpy(file_name, peer_buffer+1); sendall(reg_sockfd, peer_buffer, &msg_size); recvall(reg_sockfd, peer_buffer, &reply_len); // if file was not found in registry if(parse_search_response(peer_buffer, holder) == 0){ return 0; } // connect to peer containing the desired file if ( ( peer_sockfd = lookup_and_connect( holder->ip, holder->port ) ) < 0 ) { exit( 1 ); } // Change contents of peer_buffer into fetch request peer_buffer[0] = '\3'; memcpy(peer_buffer + 1, file_name, strlen(file_name)); sendall(peer_sockfd, peer_buffer, &msg_size); // Check first byte of received data to ensure response code == 0 recv(peer_sockfd, peer_buffer, 1, 0); if(peer_buffer[0] != 0){ printf("FETCH request produced an error"); return 0; } return recv_and_write(peer_sockfd, peer_buffer, file_name); } // Takes peer id (already in network byte order) and address to a buffer // and returns a full join request for that peer id. void join_request(const uint32_t peer_id, char* peer_buffer) { peer_buffer[0] = '\0'; memcpy(peer_buffer + 1, &peer_id, sizeof(uint32_t)); } // Pushes file names, excluding hidden files and hidden directories // into the peer_buffer as a publish request int publish(char *peer_buffer) { DIR *dir; struct dirent *file; int pos = 5; int count = 0; peer_buffer[0] = '\1'; dir = opendir("./SharedFiles"); while((file = readdir(dir))) { char *filename = file->d_name; if (*filename != '.') { int len = strlen(filename) + 1; filename[len] = '\0'; memcpy(peer_buffer + pos, filename, len); count++; pos += len; } } count = htonl(count); memcpy(peer_buffer + 1, &count, sizeof(uint32_t)); return pos; } // Builds a search request from a scanline in function, // appends that data to a buffer int search_request(char *peer_buffer) { printf("Enter a file name: "); peer_buffer[0] = '\2'; scanf("%s", peer_buffer+1); return strlen((char*)peer_buffer+1); } // Translate data from buffer into id, ip, and port variables // Print variables then copy to a struct int parse_search_response(char *peer_buffer, struct SearchResponseHolder *holder) { memcpy(&holder->peer_id, peer_buffer, sizeof(uint32_t)); holder->peer_id = ntohl(holder->peer_id); inet_ntop(AF_INET, peer_buffer + sizeof(uint32_t), holder->ip, sizeof(holder->ip)); sprintf(holder->port, "%d", ntohs(*((uint16_t*) (peer_buffer + sizeof(uint32_t) + sizeof(uint8_t[4]))))); if (holder->peer_id == 0 && (holder->ip[0] + holder->ip[1] + holder->ip[2] + holder->ip[3]) == 0 && holder->port[0] == '\0') { return 0; } else { return 1; } } int lookup_and_connect( const char *host, const char *service ) { struct addrinfo hints; struct addrinfo *rp, *result; int s; /* Translate host name into peer's IP address */ memset( &hints, 0, sizeof( hints ) ); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = 0; hints.ai_protocol = 0; if ( ( s = getaddrinfo( host, service, &hints, &result ) ) != 0 ) { fprintf( stderr, "stream-talk-client: getaddrinfo: %s\n", gai_strerror( s ) ); return -1; } /* Iterate through the address list and try to connect */ for ( rp = result; rp != NULL; rp = rp->ai_next ) { if ( ( s = socket( rp->ai_family, rp->ai_socktype, rp->ai_protocol ) ) == -1 ) { continue; } if ( connect( s, rp->ai_addr, rp->ai_addrlen ) != -1 ) { break; } close( s ); } if ( rp == NULL ) { perror( "stream-talk-client: connect" ); return -1; } freeaddrinfo( result ); return s; } // From Beej's guide section 7.4 int sendall(int s, char *buf, int *len) { int total = 0; // how many bytes we've sent int bytesleft = *len; // how many we have left to send int n; while(total < *len) { n = send(s, buf+total, bytesleft, 0); if (n == -1) { break; } total += n; bytesleft -= n; } *len = total; // return number actually sent here return n==-1?-1:0; // return -1 on failure, 0 on success } int recvall(int sockfd, char *buffer, int *len ) { int total = 0; int bytesleft = *len; int n = 1; while (total < *len && n != 0) { n = recv(sockfd, buffer+total, bytesleft, 0); if (n == -1) { break; } total += n; bytesleft -= n; } *len = total; return total; } // returns 1 on successful write int recv_and_write(int sockfd, char *buffer, char *file_name) { FILE *fptr; fptr = fopen(file_name, "w"); if(!fptr){ perror("File could not be opened"); return -1; } int bytes_received = 0; do{ bytes_received = recv(sockfd, buffer, sizeof buffer, 0); fwrite(buffer, bytes_received, 1, fptr); } while( bytes_received > 0); fclose(fptr); return 1; }