/** * @file client.c * @author Ivaylo Ivanov 11777707 * @date 03.11.2018 * * @brief Client program module. * * The client module takes a URL as input, connects to the corresponding server on the corresponding * port(`80` by default) and requests the file specified in the URL. * The content of that file is written to `stdout`, to a file or to a directory. * * SYNOPSIS * client [-p PORT] [ -o FILE | -d DIR ] URL * * EXAMPLE * client http://ivayloivanov.eu/en/ * **/ #define _GNU_SOURCE ///< in order for optarg to be defined #include #include #include #include #include #include #include #include "shared/http.h" static struct addrinfo* get_host_info(const char *hostname, char *port); int main(int argc, char *argv[]) { check_opts_number(argc); char *port = "80"; char *ofile = NULL; char *dir = NULL; char *url = NULL; int c; /// Process the options while((c = getopt(argc, argv, "p:o:d:h")) != -1) { switch(c) { case 'p': port = optarg; break; case 'o': ofile = optarg; break; case 'd': dir = optarg; break; case 'h': printf("Command usage:\n"); print_usage(); exit(EXIT_SUCCESS); break; case '?': print_usage(); exit(EXIT_FAILURE); default: abort(); } } /// Check for the case that both output file and directory are set if(ofile != NULL && dir != NULL) { printf("Either output file or directory is specified. You cannot use both.\n"); print_usage(); exit(EXIT_FAILURE); } /// Check if the required argument is there if(argv[optind] == NULL) { fprintf(stderr, "Mandatory argument 'url' missing.\n"); print_usage(); exit(EXIT_FAILURE); } else { url = argv[optind]; } const char *full_url = strstr(url, "http://") + 7; //< the full url starts after http:// const char *path = strstr(full_url, "/"); const char *hostname = "google.com"; //< TODO: Change this later struct addrinfo* res = get_host_info(hostname, port); //< Check if the address is resolvable freeaddrinfo(res); //< Free the addrinfo struct } static void print_usage(void) { printf("client [-p PORT] [ -o FILE | -d DIR ] URL\n"); } /** * @brief Function to check the number of arguments * @details Checks the number of arguments supplied to the command line. * If the arguments are less than the required or more than possible, it exits * @param argc - Number of arguments obtained from the command line * @return none * **/ static void check_opts_number(int argc) { if(argc <= 1) { fprintf(stderr, "At least one argument expected.\n"); print_usage(); exit(EXIT_FAILURE); } else if(argc > 6) { fprintf(stderr, "Too many arguments supplied.\n"); print_usage(); exit(EXIT_FAILURE); } } /** * @brief Function to check if the address is resolvable by the client * @details Checks if the address is resolvable by the client. * If the address is resolvable, it writes the result to res * If the address is not resolvable, it exits with an error * @param * hostname - The address to be resolved * port - The port to try * @return res - The result of getaddrinfo() * **/ static struct addrinfo* get_host_info(const char *hostname, char *port) { struct addrinfo hints; //< Configuration for the client hints.ai_family = AF_INET; //< Use IPv4 hints.ai_socktype = SOCK_STREAM; //< Stream socket hints.ai_flags = 0; //< Do not use any flags hints.ai_protocol = IPPROTO_TCP; //< Use only TCP struct addrinfo* res = NULL; int err = getaddrinfo(hostname, port, &hints, &res); if(err != 0) { (void) fprintf(stderr, "ERROR: %s\n", gai_strerror(err)); exit(EXIT_FAILURE); //< Exit if there is an error } if(res == NULL) { (void) fprintf(stderr, "ERROR: Could not resolve address %s\n", hostname); exit(EXIT_FAILURE); //< Exit if the address is not resolved } return res; }