Select Git revision
machine-vuln4.c
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;
}