Skip to content
Snippets Groups Projects
Select Git revision
  • aceca5c0ff1f55e98313e253178dcb3144fee204
  • master default protected
  • dzl
  • vuln
  • Callum
5 results

machine-vuln4.c

Blame
  • Forked from Toby Murray / swen90006-a2-2018
    Source project has a limited visibility.
    server.c 13.23 KiB
    /*
    ** COMP30023_2019SM1 Project1
    ** Code completed by Xun Zhang (854776)
    **
    ** Reference
    ** "http-server.c" from Lab 6
    */
    
    #include <errno.h>
    #include <stdbool.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #include <arpa/inet.h>
    #include <fcntl.h>
    #include <netdb.h>
    #include <netinet/in.h>
    #include <strings.h>
    #include <sys/select.h>
    #include <sys/sendfile.h>
    #include <sys/socket.h>
    #include <sys/stat.h>
    #include <sys/types.h>
    #include <unistd.h>
    
    // constants
    static char const* const HTTP_200_FORMAT =
        "HTTP/1.1 200 OK\r\n\
    Content-Type: text/html\r\n\
    Content-Length: %ld\r\n\r\n";
    static char const* const HTTP_400 =
        "HTTP/1.1 400 Bad Request\r\nContent-Length: 0\r\n\r\n";
    static int const HTTP_400_LENGTH = 47;
    static char const* const HTTP_404 =
        "HTTP/1.1 404 Not Found\r\nContent-Length: 0\r\n\r\n";
    static int const HTTP_404_LENGTH = 45;
    
    static char const* const INTRO_PAGE = "./views/1_intro.html";
    static char const* const START_PAGE = "./views/2_start.html";
    static char const* const FIRST_TURN_PAGE = "./views/3_first_turn.html";
    static char const* const ACCEPTED_PAGE = "./views/4_accepted.html";
    static char const* const DISCARDED_PAGE = "./views/5_discarded.html";
    static char const* const ENDGAME_PAGE = "./views/6_endgame.html";
    static char const* const GAMEOVER_PAGE = "./views/7_gameover.html";
    
    bool is_end_game, is_quit_game;
    bool p1_is_ready, p2_is_ready;
    int p1_sockfd, p2_sockfd;
    // assume we can save 2000 keywords from each player,
    // and 200 words for each keyword
    char p1_guess[2000][200], p2_guess[2000][200];
    int p1_guess_len, p2_guess_len;
    
    // represents the types of method
    typedef enum { GET, POST, UNKNOWN } METHOD;
    
    // methods reference
    void run_server(const char* ip, const int port);
    int create_server_socket(const char* ip, const int port);
    static bool handle_http_request(int sockfd);
    bool show_page(int sockfd, const char* htmldir);
    bool show_modified_page(int sockfd, const char* htmldir, char* added,
                            int inset_index);
    bool show_start_page(int sockfd, char* username);
    bool show_accepted_page(int sockfd);
    void print_buff(char* request);
    bool keyword_check(int sockfd, char* keyword);
    void player_init();
    
    int main(int argc, char** argv) {
        int port;
        char* ip;
    
        // check input
        if (argc != 3) {
            fprintf(stderr, "usage: %s ip port\n", argv[0]);
            exit(EXIT_FAILURE);
        }
    
        ip = argv[1];
    
        port = atoi(argv[2]);
    
        run_server(ip, port);
    
        return 0;
    }
    
    void run_server(const char* ip, const int port) {
        int sockfd;
    
        sockfd = create_server_socket(ip, port);
    
        // listen on the socket
        if (listen(sockfd, 5) < 0) {
            perror("listen");
            exit(EXIT_FAILURE);
        }
    
        printf("image_tagger server is now running at IP: %s on port %d\n", ip,
               port);
    
        // Initialise
        player_init();
        is_end_game = is_quit_game = false;
    
        // initialise an active file descriptors set
        fd_set masterfds;
        FD_ZERO(&masterfds);
        FD_SET(sockfd, &masterfds);
        // record the maximum socket number
        int maxfd = sockfd;
    
        while (1) {
            // monitor file descriptors
            fd_set readfds = masterfds;
            if (select(FD_SETSIZE, &readfds, NULL, NULL, NULL) < 0) {
                perror("select");
                exit(EXIT_FAILURE);
            }
    
            // loop all possible descriptor
            for (int i = 0; i <= maxfd; ++i)
                // determine if the current file descriptor is active
                if (FD_ISSET(i, &readfds)) {
                    // create new socket if there is new incoming connection request
                    if (i == sockfd) {
                        struct sockaddr_in cliaddr;
                        socklen_t clilen = sizeof(cliaddr);
                        int newsockfd =
                            accept(sockfd, (struct sockaddr*)&cliaddr, &clilen);
                        if (newsockfd < 0)
                            perror("accept");
                        else {
                            // add the socket to the set
                            FD_SET(newsockfd, &masterfds);
                            // update the maximum tracker
                            if (newsockfd > maxfd) maxfd = newsockfd;
                            // print out the IP and the socket number
                            char ip[INET_ADDRSTRLEN];
                            printf("new connection from %s on socket %d\n",
                                   // convert to human readable string
                                   inet_ntop(cliaddr.sin_family, &cliaddr.sin_addr,
                                             ip, INET_ADDRSTRLEN),
                                   newsockfd);
                        }
                    }
                    // a request is sent from the client
                    else if (!handle_http_request(i)) {
                        close(i);
                        FD_CLR(i, &masterfds);
                    }
                }
        }
    }
    
    int create_server_socket(const char* ip, const int port) {
        int sockfd;
        struct sockaddr_in serv_addr;
    
        // create TCP socket which only accept IPv4
        sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (sockfd < 0) {
            perror("socket");
            exit(EXIT_FAILURE);
        }
    
        // create and initialise address we will listen on
        bzero(&serv_addr, sizeof(serv_addr));
        serv_addr.sin_family = AF_INET;
        serv_addr.sin_addr.s_addr = inet_addr(ip);
        serv_addr.sin_port = htons(port);
    
        // reuse the socket if possible
        int const reuse = 1;
        if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int)) < 0) {
            perror("Could not reopen socket");
            exit(EXIT_FAILURE);
        }
    
        // bind address to socket
        if (bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
            perror("bind");
            exit(EXIT_FAILURE);
        }
    
        return sockfd;
    }
    
    static bool handle_http_request(int sockfd) {
        // try to read the request
        char buff[2049];
        int n = read(sockfd, buff, 2049);
        if (n <= 0) {
            if (n < 0)
                perror("read");
            else
                printf("socket %d close the connection\n", sockfd);
            return false;
        }
    
        // players setting and detecting
        if (p1_sockfd < 0 && sockfd != p2_sockfd)
            p1_sockfd = sockfd;
        else if (p2_sockfd < 0 && sockfd != p1_sockfd)
            p2_sockfd = sockfd;
        else if (sockfd != p1_sockfd && sockfd != p2_sockfd) {
            perror("more than 2 players");
            exit(EXIT_FAILURE);
        }
    
        // terminate the string
        buff[n] = 0;
    
        char* curr = buff;
    
        // print the HTML request for test
        // print_html_request(buff, sockfd);
    
        // parse the method
        METHOD method = UNKNOWN;
        if (strncmp(curr, "GET ", 4) == 0) {
            curr += 4;
            method = GET;
        } else if (strncmp(curr, "POST ", 5) == 0) {
            curr += 5;
            method = POST;
        } else if (write(sockfd, HTTP_400, HTTP_400_LENGTH) < 0) {
            perror("write");
            return false;
        }
    
        // sanitise the URI
        while (*curr == '.' || *curr == '/') ++curr;
    
        // contents of each pages
        if (*curr == ' ') {
            if (method == GET) {
                return show_page(sockfd, INTRO_PAGE);
            } else if (method == POST) {
                if (strstr(buff, "user=") != NULL) {
                    char* username = strstr(buff, "user=") + 5;
                    return show_start_page(sockfd, username);
                } else if (strstr(buff, "quit=") != NULL) {
                    player_init();
                    is_quit_game = true;
                    return show_page(sockfd, GAMEOVER_PAGE);
                }
            } else
                // never used, just for completeness
                fprintf(stderr, "no other methods supported");
        } else if (*curr == '?') {
            if (method == GET) {
                // flag ready for players
                if (sockfd == p1_sockfd)
                    p1_is_ready = true;
                else if (sockfd == p2_sockfd)
                    p2_is_ready = true;
                else {
                    perror("more than 2 players");
                    exit(EXIT_FAILURE);
                }
                return show_page(sockfd, FIRST_TURN_PAGE);
            } else if (method == POST) {
                if (strstr(buff, "quit=") != NULL) {
                    player_init();
                    is_quit_game = true;
                    return show_page(sockfd, GAMEOVER_PAGE);
                } else if (strstr(buff, "keyword=") != NULL) {
                    char* keyword = strstr(buff, "keyword=") + 8;
                    if (p1_is_ready && p2_is_ready) {
                        if (keyword_check(sockfd, keyword)) {
                            // initial players setting
                            player_init();
                            is_end_game = true;
                            return show_page(sockfd, ENDGAME_PAGE);
                        } else
                            return show_page(sockfd, ACCEPTED_PAGE);
                    } else if (is_end_game) {
                        is_end_game = false;
                        return show_page(sockfd, ENDGAME_PAGE);
                    } else if (is_quit_game) {
                        is_quit_game = false;
                        return show_page(sockfd, GAMEOVER_PAGE);
                    } else {
                        return show_page(sockfd, DISCARDED_PAGE);
                    }
                }
            } else
                // never used, just for completeness
                fprintf(stderr, "no other methods supported");
        }
        // send 404
        else if (write(sockfd, HTTP_404, HTTP_404_LENGTH) < 0) {
            perror("write");
            return false;
        }
    
        // never used, just for completeness
        return true;
    }
    
    bool show_page(int sockfd, const char* htmldir) {
        char buff[2049];
        int n;
    
        // get the size of the file
        struct stat st;
        stat(htmldir, &st);
        n = sprintf(buff, HTTP_200_FORMAT, st.st_size);
        // send the header first
        if (write(sockfd, buff, n) < 0) {
            perror("write");
            return false;
        }
        // send the file
        int filefd = open(htmldir, O_RDONLY);
        do {
            n = sendfile(sockfd, filefd, NULL, 2048);
        } while (n > 0);
        if (n < 0) {
            perror("sendfile");
            close(filefd);
            return false;
        }
        close(filefd);
    
        return true;
    }
    
    bool show_modified_page(int sockfd, const char* htmldir, char* added,
                            int inset_index) {
        char buff[2049];
        int n;
    
        // the length needs to include the html tags for the username
        int added_length = strlen(added);
    
        // get the size of the file
        struct stat st;
        stat(START_PAGE, &st);
        // increase file size to accommodate the username
        long size = st.st_size + added_length;
        n = sprintf(buff, HTTP_200_FORMAT, size);
        // send the header first
        if (write(sockfd, buff, n) < 0) {
            perror("write");
            return false;
        }
        // read the content of the HTML file
        int filefd = open(START_PAGE, O_RDONLY);
        n = read(filefd, buff, 2048);
        if (n < 0) {
            perror("read");
            close(filefd);
            return false;
        }
        close(filefd);
        // move the trailing part backward
        int p1, p2;
        for (p1 = size - 1, p2 = p1 - added_length; p1 >= size - inset_index;
             --p1, --p2)
            buff[p1] = buff[p2];
        ++p2;
    
        // copy the username
        strncpy(buff + p2, added, added_length);
    
        print_buff(buff);
    
        if (write(sockfd, buff, size) < 0) {
            perror("write");
            return false;
        }
    
        return true;
    }
    
    bool show_start_page(int sockfd, char* username) {
        printf("user=%s\r\n", username);
        int username_length = strlen(username);
        printf("user_len = %d\r\n", username_length);
        char* added_prefix = "<p>";
        int added_prefix_length = strlen(added_prefix);
        printf("added_prefix_len = %d\r\n", added_prefix_length);
        char* added_suffix = "</p>\n\n";
        int added_suffix_length = sizeof(added_suffix);
        printf("added_suffix_len = %d\r\n", added_prefix_length);
        // the length needs to include the html tags for the username
        int added_length = username_length + 9;
    
        char added[added_length];
    
        // create added string
        strncpy(added, added_prefix, added_prefix_length);
        printf("added is now %s\r\n", added);
        printf("added_len = %ld\r\n", strlen(added));
        strncpy(added + added_prefix_length, username, username_length);
        printf("added is now %s\r\n", added);
        printf("added_len = %ld\r\n", strlen(added));
        strncpy(added + added_prefix_length + username_length, added_suffix,
                added_suffix_length);
        printf("added is now %s\r\n", added);
        printf("added_len = %ld\r\n", strlen(added));
    
        return show_modified_page(sockfd, START_PAGE, added, 212);
    }
    
    bool show_accepted_page(int sockfd) {
        return true;
    }
    
    void print_buff(char* request) {
        for (int i = 0; i < strlen(request); i++) {
            printf("%c", request[i]);
        }
    }
    
    bool keyword_check(int sockfd, char* keyword) {
        if (sockfd == p1_sockfd) {
            // check p2 list
            for (int i = 0; i < p2_guess_len; i++) {
                if (strcmp(keyword, p2_guess[i]) == 0) return true;
            }
            // add new keyword to p1 list
            strcpy(p1_guess[p1_guess_len], keyword);
            p1_guess_len++;
        } else if (sockfd == p2_sockfd) {
            // check p1 list
            for (int i = 0; i < p1_guess_len; i++) {
                if (strcmp(keyword, p1_guess[i]) == 0) return true;
            }
            // add new keyword to p2 list
            strcpy(p2_guess[p2_guess_len], keyword);
            p2_guess_len++;
        }
        return false;
    }
    
    void player_init() {
        p1_is_ready = p2_is_ready = false;
        p1_sockfd = p2_sockfd = -1;
        p1_guess_len = p2_guess_len = 0;
    }