P2P-File-Sharing / Peer / peer.c
peer.c
Raw
/* 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;
}