#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "server.h" extern char *program_name; extern volatile sig_atomic_t quit; /* * Prints a usage message and exits. * @param msg Additional output message. */ static void usage(const char *msg) { fprintf(stderr, "Usage: %s [-p PORT] COMMAND\n%s\n", program_name, msg); exit(EXIT_FAILURE); } /* * Print an error message and exit with EXIT_FAILURE. * @param msg Additional output message. */ static void error_exit(const char *msg) { if (errno == 0) fprintf(stderr, "[%s] [ERROR] %s\n", program_name, msg); else fprintf(stderr, "[%s] [ERROR] %s - %s\n", program_name, msg, strerror(errno)); exit(EXIT_FAILURE); } static void print_error(const char *msg) { fprintf(stderr, "[%s] [ERROR] %s - %s\n", program_name, msg, strerror(errno)); } static void print_message(const char *msg) { fprintf(stderr, "[%s] %s\n", program_name, msg); } /** * @brief Function freeing the resources. */ void free_resources(void) { } /* * Parse Arguments */ void parse_arguments(int argc, char *argv[], struct arguments *args) { memset(args, 0, sizeof(*args)); int opt; while ((opt = getopt(argc, argv, "p:")) != -1) { switch (opt) { case 'p': if (args->port_str) usage("-p option allowed only once"); args->port_str = optarg; break; default: usage("invalid option"); } } if (args->port_str == NULL) args->port_str = "2020"; if (argc - optind != 1) usage("invalid number of arguments"); args->command = argv[optind]; } /* * Custom getaddrinfo implementation for localhost only. */ int getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res) { struct in_addr localhost; localhost.s_addr = htonl(0x7f000001); // 127.0.0.1 // check hostname (allow only `localhost' or any string which `inet_aton()' // converts to 127.0.0.1) struct in_addr node_addr; if (node != NULL && (inet_aton(node, &node_addr) == 0 || node_addr.s_addr != localhost.s_addr) && strcmp(node, "localhost") != 0) return EAI_NONAME; // check port (allow only numerical ports) char *endptr; int port = strtol(service, &endptr, 10); if (*endptr != '\0') return EAI_NONAME; // check hints (NULL is acceptable) if (hints != NULL) { // check family if (hints->ai_family != AF_UNSPEC && hints->ai_family != AF_INET) return EAI_FAMILY; // check socket type if (hints->ai_socktype != 0 && hints->ai_socktype != SOCK_STREAM) return EAI_SOCKTYPE; // check protocol if (hints->ai_protocol != 0 && hints->ai_protocol != IPPROTO_TCP) return EAI_SOCKTYPE; } // allocate memory for result *res = malloc(sizeof(struct addrinfo)); struct sockaddr_in *sin = malloc(sizeof(struct sockaddr_in)); if (*res == NULL || sin == NULL) return EAI_MEMORY; memset(sin, 0, sizeof(struct sockaddr_in)); sin->sin_family = AF_INET; sin->sin_addr = localhost; sin->sin_port = htons(port); (*res)->ai_flags = (AI_V4MAPPED | AI_ADDRCONFIG); (*res)->ai_family = AF_INET; (*res)->ai_socktype = SOCK_STREAM; (*res)->ai_protocol = IPPROTO_TCP; (*res)->ai_addrlen = sizeof(struct sockaddr_in); (*res)->ai_addr = (struct sockaddr *)sin; (*res)->ai_canonname = NULL; (*res)->ai_next = NULL; return 0; } /****************************************************************************** * DEMO FUNCTIONS *****************************************************************************/ int DEMO_setup_connection(const char *port_str) { struct addrinfo hints, *ai; memset(&hints, 0, sizeof hints); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; int res = getaddrinfo(NULL, port_str, &hints, &ai); if (res != 0) error_exit("getaddrinfo() failed"); int sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (sockfd < 0) error_exit("socket() failed"); int reuse = 1; if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) error_exit("setsockopt() failed"); if (bind(sockfd, ai->ai_addr, ai->ai_addrlen) < 0) error_exit("bind() failed"); freeaddrinfo(ai); if (listen(sockfd, 1) < 0) error_exit("listen() failed"); return sockfd; } void DEMO_server_request_handler(int sockfd, const char *command, int flags) { int connfd = accept(sockfd, NULL, NULL); if (connfd < 0) error_exit("accept() failed"); print_message("Accepted connection"); FILE *fps = fdopen(connfd, "w+"); char argument[MAX_ARGUMENT_LEN + 1] = {0}; fgets(argument, sizeof(argument), fps); FILE *fpc = NULL; if (flags & USE_DEMO) { print_message("Calling DEMO_execute_command()"); fpc = DEMO_execute_command(command, argument); } else { print_message("Calling execute_command()"); fpc = execute_command(command, argument); } if (fpc) { int c; while ((c = fgetc(fpc)) != EOF) fputc(c, fps); fclose(fpc); } else { fputs("COMMAND_FAILED", fps); } fflush(fps); fclose(fps); } FILE *DEMO_execute_command(const char *command, const char *argument) { // file stream to read the output of the process FILE *proc_output = NULL; // file descriptors for the pipe for parent-child process communication int c2p[2]; // create the pipes if (pipe(c2p) == -1) error_exit("cannot create pipe"); pid_t pid = fork(); switch (pid) { case 0: // duplicate write end to stdout if (dup2(c2p[1], STDOUT_FILENO) == -1) { error_exit("dup2() failed"); } close(c2p[0]); close(STDIN_FILENO); // exec the command if (execlp(command, command, argument, NULL) == -1) { error_exit("exec() failed"); } case -1: error_exit("fork() failed"); default: // parent process proc_output = fdopen(c2p[0], "r"); if (proc_output == NULL) { error_exit("fdopen() failed"); } close(c2p[1]); int stat = 0; wait(&stat); if (WIFEXITED(stat) && WEXITSTATUS(stat) == 0) { print_message("child process exited normally"); } else { print_error("child process exited abnormally"); proc_output = NULL; } } return proc_output; }