/** * File : proxy.c * Author : Apostolos Scondrianis * Date : Sunday, January 23rd, 2022 * Version : 1.0 * This simple program takes a GET request from a browser * and forwards it towards a server, then receives data * from the server, edits if needed, and then forwards * it to the browser that made the request. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /** * Part of the host information and socket communication was * learnt from http://beej.us/guide/bgnet guide and then was * altered to fit this application */ #define QUEUE 20 #define TOTALMETHODS 1 //Proxy Address struct information will be stored here struct addrinfo proxy_hints, *proxy_res; //The target of the browser HTTP request will be stored here struct addrinfo hints_server, *res_server, *p; //Socket that proxy listens for connections on int proxy_socket; //client socket that connects to the proxy int client_socket; //socket used for communications with the server the client //wants to connect to int server_socket; //server port char proxy_port[6]; /** The following functions were taken from https://www.geeksforgeeks.org/c-program-display-hostname-ip-address/ and are used to display the server where the proxy software is ran on. */ void checkHostName(int hostname) { if (hostname == -1) { perror("gethostname"); exit(1); } } // Returns host information corresponding to host name void checkHostEntry(struct hostent * hostentry) { if (hostentry == NULL) { perror("gethostbyname"); exit(1); } } // Converts space-delimited IPv4 addresses // to dotted-decimal format void checkIPbuffer(char *IPbuffer) { if (NULL == IPbuffer) { perror("inet_ntoa"); exit(1); } } /** * End of functions taken from : * https://www.geeksforgeeks.org/c-program-display-hostname-ip-address/ * that are used to show the IP Address of the server. */ /** * function used to read a valid port from terminal */ void readPort() { int read_port; int scan_status; int c; while( 1 ) { memset(proxy_port ,0, 6); printf("Enter Port you wish the proxy to run on : "); scan_status = scanf("%d", &read_port); printf("\n"); if( scan_status != 1 || (read_port < 1024 || read_port > 44444)) { // note: infinite loop for invalid input printf("Invalid Proxy PORT given. Must an integer greater than 1024 and less than 44444.\n"); while ((c = getchar()) != '\n' && c != EOF) { } } else { sprintf(proxy_port, "%d", read_port); printf("You chose to run the proxy on PORT : %s\n", proxy_port); while ((c = getchar()) != '\n' && c != EOF) { } break; // OK } } } /* signal handler */ void catcher( int sig ) { freeaddrinfo(proxy_res); freeaddrinfo(res_server); close(proxy_socket); close(client_socket); close(server_socket); printf("\nsignal handling-socket closures %d %d %d.\n", proxy_socket, client_socket, server_socket); exit(1); } /** void createProxySocket() : * gets local address and creates the * proxy socket that listens for clients */ void createProxySocket() { memset(&proxy_hints, 0, sizeof(proxy_hints)); //Doesn't matter which family proxy_hints.ai_family = AF_UNSPEC; //TCP Socket proxy_hints.ai_socktype = SOCK_STREAM; //Local IP proxy_hints.ai_flags = AI_PASSIVE; //local machine address getaddrinfo(NULL, proxy_port, &proxy_hints, &proxy_res); //creation of a socket proxy_socket = socket(proxy_res->ai_family, proxy_res->ai_socktype, proxy_res->ai_protocol); } /** * function used to split the request into tokens */ void requestTokens(char ** token_list, int max_tokens, int *array_cursor, char *request) { token_list[(*array_cursor)] = strtok(request, "\r\n"); (*array_cursor)++; while(1) { if(*array_cursor < max_tokens) { token_list[*array_cursor] = strtok(NULL, "\r\n"); if(token_list[*array_cursor] == NULL) { *array_cursor--; break; } else { //printf("token[%d] : %s\n",*array_cursor, token_list[*array_cursor]); (*array_cursor)++; } } else { (*array_cursor)--; break; } } } /** * function used to show the IP address and hostname of the server * the contents of this function were taken from : * function used to show the IP address and hostname of the server * https://www.geeksforgeeks.org/c-program-display-hostname-ip-address/ */ void showProxyIP() { char hostbuffer[256]; char *IPbuffer; struct hostent *host_entry; int hostname; // To retrieve hostname hostname = gethostname(hostbuffer, sizeof(hostbuffer)); checkHostName(hostname); // To retrieve host information host_entry = gethostbyname(hostbuffer); checkHostEntry(host_entry); // To convert an Internet network // address into ASCII string IPbuffer = inet_ntoa(*((struct in_addr*)host_entry->h_addr_list[0])); printf("Proxy is hosted on server : %s\n", hostbuffer); printf("The IP of the server that the Proxy is hosted on is: %s\n", IPbuffer); } /** function used to initialize the server information in res_server * which will be needed for the creation of the server socket */ void createServerResources(char * host_link) { //We will pick the first valid IP address form to use in the server socket initialization //And we will print the IP addressses of the server that we detected. /* Following Code Snippet taken from beej.us/guide.net ebook*/ int status_server; char ipstr[INET6_ADDRSTRLEN]; memset(&hints_server, 0, sizeof(hints_server)); hints_server.ai_family = AF_UNSPEC; hints_server.ai_socktype = SOCK_STREAM; if((status_server = getaddrinfo(host_link, "http", &hints_server, &res_server)) != 0) { fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status_server)); exit(2); } printf("IP addresses for %s :\n\n", host_link); for(p = res_server; p != NULL; p = p->ai_next) { void * addr; char *ipver; //get the pointer to the address itself, //different fields in IPv4 and IPv6 : if(p->ai_family == AF_INET) { //It's IPv4 struct sockaddr_in *ipv4 = (struct sockaddr_in *) p->ai_addr; addr = &(ipv4->sin_addr); ipver = "IPv4"; } else { //It's IPv6 struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr; addr = &(ipv6->sin6_addr); ipver ="IPv6"; } //convert the IP to a string and print it: inet_ntop(p->ai_family, addr, ipstr, sizeof(ipstr)); printf(" %s: %s\n", ipver, ipstr); } /* code snippet ends */ } int main(int argc, char *argv[]) { //random seed srand(time(0)); //precoded image HTTP requests for the 2 clowns. char * clown_request[2] = { "GET http://pages.cpsc.ucalgary.ca/~carey/CPSC441/ass1/clown1.png HTTP/1.0\r\nHost: pages.cpsc.ucalgary.ca\r\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:96.0) Gecko/20100101 Firefox/96.0\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8\r\nAccept-Language: en-CA,en-US;q=0.7,en;q=0.3\r\nAccept-Encoding: gzip, deflate\r\nConnection: keep-alive\r\nUpgrade-Insecure-Requests: 1\r\n\r\n", "GET http://pages.cpsc.ucalgary.ca/~carey/CPSC441/ass1/clown2.png HTTP/1.0\r\nHost: pages.cpsc.ucalgary.ca\r\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:96.0) Gecko/20100101 Firefox/96.0\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8\r\nAccept-Language: en-CA,en-US;q=0.7,en;q=0.3\r\nAccept-Encoding: gzip, deflate\r\nConnection: keep-alive\r\nUpgrade-Insecure-Requests: 1\r\n\r\n" }; //Text Request Flag int textflag = -1; //Image Request Flag int imageflag = -1; //Set function handlers signal(SIGINT, catcher); signal(SIGTERM, catcher); signal(SIGTSTP, catcher); //zero the buffer char buffer[10000]; bzero(buffer, 10000); //for future implementation to handle more than GET requests char *valid_methods[1] = { "GET" }; //client information will be passed by accept() //we don't use this information again struct sockaddr_storage client_address; socklen_t client_addr_size; printf("Welcome to my proxy (Apostolos Scondrianis).\n"); readPort(); //creation of the socket used to listen to the clients createProxySocket(); showProxyIP(); //Unsuccessful binding if -1 int bind_status = -1; //Unsuccessful listen if -1 int listen_status; while(bind_status != 0) { bind_status = bind(proxy_socket, proxy_res->ai_addr, proxy_res->ai_addrlen); if(bind_status != 0) { printf("Unable to bind on the specified port. Try a different port.\n"); memset(proxy_port ,0, 6); readPort(); } } if(bind_status == -1) { printf("There was an error with binding. Unable to launch proxy\n"); } else { listen_status = listen(proxy_socket, 10); printf("bind status %d, listen status %d, for socket %d\n", bind_status, listen_status, proxy_socket); int received_bytes; while(1) { //wait for a connection from a client client_socket = accept(proxy_socket, (struct sockaddr *)&client_address, &client_addr_size); //receive data from the client received_bytes = recv(client_socket, buffer, 10000, 0); //if we received bytes > 0 printf("----- Request -----\n"); if(received_bytes > 0) { printf("Proxy received a total of %d from the browser.\n", received_bytes); //printf("The proxy received following message from browser with %d bytes.\n", received_bytes); //printf("%s", buffer); //request flag int request_found = -1; /* code used to split the http request into tokes */ int max_length = 50; char *token[max_length]; char *request_type; int current_position = 0; char browser_request[received_bytes]; //browser_request back up before we edit the buffer memcpy(browser_request, buffer, received_bytes); requestTokens(token, max_length, ¤t_position, buffer); /* split tokens code ends */ char * find_host_token; if(token[0] == NULL) { //check if first token is empty. If it is invalid HTTP request for sure. printf("invalid format for HTTP Request.\n"); break; } else { //Check to see if it is an image request with jpg or jpeg extension included. //flag is set to 1 if it is, and -1 i it isn't. if((strstr(token[0], ".jpg") != NULL) || (strstr(token[0], ".jpeg") != NULL)) { imageflag = 1; } else { imageflag = -1; } //check third token of the request, to see if it's a text request. if(strstr(token[3], "text") != NULL) { //text request found, alter the text flag textflag = 1; } else { //text request not found, set textflag = -1 textflag = -1; } //get the type of HTTP request and then check it against //the ones that our proxy accepts. Redundant at the moment, //but keep it for future implementation of future requests request_type = strtok(token[0], " "); for(int i = 0; i < TOTALMETHODS; i++) { //if the request is one we handle, set request_found handle to the // position of the request. if(strcmp(valid_methods[i], request_type) == 0) { //foundd a valid request, break the loop request_found = i; break; } } //if request was not found invalid if(request_found == -1) { printf("Unsupported or invalid HTTP method.\n"); } else { //request acceptable. printf("A %s method is being parsed by the proxy server.\n", valid_methods[request_found]); //attempt to find host in our request tokens. for(int i = 0; i <= current_position; i++) { find_host_token = strstr(token[i], "Host: "); if(find_host_token != NULL) { break; } } //if we find in the header "Host :" let's use that to get the host address. //our proxy will handle only requests that contain the Host tag in the header //of the request. if(find_host_token != NULL) { //Let's extract the host address int hostlength = strlen(find_host_token)-6; char host[hostlength+1]; memcpy(host, &find_host_token[6], hostlength); host[hostlength] = '\0'; printf("We will attempt to request data from %s \n", host); printf("Let's first fetch the IP address of that server.\n"); //let's fetch the IP addresses of the host createServerResources(host); //create a server socket to connect to the server the browser //wants to communicate with server_socket = socket(res_server->ai_family, res_server->ai_socktype, res_server->ai_protocol); if(server_socket > 0) { printf("A socket has been successfully created for communications with the server of request \n"); } else { printf("Failed to create a socket for the browser request.\n"); } int server_connection_status; server_connection_status = connect(server_socket, res_server->ai_addr, res_server->ai_addrlen); if(server_connection_status != 0) { //Connection to the server from the proxy failed. printf("Failed to connect to the server the browser requested.\n"); } else { //Successful connection to the server. printf("Succesffuly connected to the server the browser requested.\n"); } int sent_server_bytes; //Let's alter the request if a jpg/jpeg request was found. if(imageflag == 1) { //random number between 0-39 int rand_req = (rand() % 40); if(rand_req < 20) { //if rand number is < 20, we select the first clown rand_req = 0; } else { //select second clown request rand_req = 1; } printf("jpg/jpeg images request detected.\nProxy selected randomly clown image : %d \n", rand_req+1); sent_server_bytes = send(server_socket, clown_request[rand_req], strlen(clown_request[rand_req]), 0); //let's check and see if we successed on our clown request. if(sent_server_bytes > 0 ) { //clown request was successful printf("Successful forward of clown request to server.\n"); } else { //unsuccessful clown request printf("Unsuccessful forward of clown request to server.\n"); } } else { //No jpg/jpeg image request, forward the browser request sent_server_bytes = send(server_socket, browser_request, received_bytes, 0); if(sent_server_bytes > 0) { //successful request forwarding of the browser request to the server printf("Browser request successfully forwarded.\n"); } else { //Unsuccesful browser request forwarding of the browser request to the server printf("Browser request failed to be forwarded through the proxy.\n"); } } printf("Proxy has sent %d bytes to the server.\n", sent_server_bytes); //Now let's attempt to receive data from the server and then forward it to the browser. int bytes_received_from_server; //empty buffer bzero(buffer, 10000); //as long as there is data being received, do stuff while((bytes_received_from_server = recv(server_socket, buffer, 10000, 0)) > 0 ) { printf("Received data from the server that contains %d bytes.\n ",bytes_received_from_server); if(textflag == 1) { char *happy; while ((happy = strstr(buffer, "Happy"))) { printf("Replaced \"Happy\" with \"Silly\""); memcpy(happy, "Silly", 5); } } int bytes_sent_to_browser; bytes_sent_to_browser = send(client_socket, buffer, bytes_received_from_server, 0); if(bytes_sent_to_browser > 0) { //successful data forwarding to the browser from the server printf("Proxy has sent %d bytes to the browser.\n", bytes_sent_to_browser); } else { //unsuccessful data forwarding to the browser from the server printf("Could not forward data to the browser.\n"); } bzero(buffer, 10000); } printf("- END OF REQUEST --\n"); close(server_socket); close(client_socket); } else { printf("Host not found.\n"); } } } bzero(buffer, 10000); } else { //Did not received a message from the browser or connection closed if(received_bytes < 0) { //Error in connection printf("No request received from the browser.\n"); } else { //Connection closed. printf("Connection closed.\n"); } } } } freeaddrinfo(proxy_res); freeaddrinfo(res_server); close(proxy_socket); close(client_socket); close(server_socket); }