#include #include #include #include #include #include #include #include #include #include #include #include #define LISTEN_PORT 1234 // Port we accept connections on /* Name: createTcpSocket * Description: Creates a new, unconnected TCP socket * Parameters: sock - buffer to receive socket * Return code: true if successful * false if an error occurred */ bool createTcpSocket(int *sock) { bool failed = false; *sock = socket(AF_INET, SOCK_STREAM, 0); if (*sock == -1) { fprintf(stderr, "Couldn't create socket: %s\n", strerror(errno)); failed = true; } return !failed; } /* Name: bindTcpSocket * Description: Binds a TCP socket to a specific port on all interfaces * Parameters: sock - socket to bind * port - port to bind socket to * Return code: true if successful * false if an error occurred */ bool bindTcpSocket(int sock, unsigned short port) { bool result = false; struct sockaddr_in saddr; saddr.sin_family = AF_INET; saddr.sin_port = htons(port); saddr.sin_addr.s_addr = INADDR_ANY; printf("Binding to %s:%u...", inet_ntoa(saddr.sin_addr), port); fflush(stdout); if (bind(sock, (struct sockaddr*) &saddr, sizeof(saddr)) == 0) { printf(" OK.\n"); result = true; } else { printf(" FAILED.\n"); fprintf(stderr, "Couldn't bind socket: %s\n", strerror(errno)); } return result; } /* Name: setSocketToListenMode * Description: Sets a bound TCP socket to listen mode * Parameters: sock - socket to set to listen mode * Return code: true if successful * false if an error occurred */ bool setSocketToListenMode(int sock) { bool failed = false; if (listen(sock, 3) == -1) { fprintf(stderr, "Couldn't set socket to listen mode: %s\n", strerror(errno)); failed = true; } return !failed; } /* Name: acceptClient * Description: Accepts a new client on a socket in listen mode * Parameters: sock - socket in listen mode * client - buffer for socket of new client * Return code: true if successful * false if an error occurred */ bool acceptClient(int sock, int *client) { bool result = false; struct sockaddr_in saddr; socklen_t size = sizeof(saddr); *client = accept(sock, (struct sockaddr*) &saddr, &size); if (*client >= 0) { printf("Got a new client from %s:%u.\n", inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port)); result = true; } else { fprintf(stderr, "Accepting client failed: %s\n", strerror(errno)); } return result; } /* Name: safeCloseSocket * Description: Checks if a socket is valid, closes it. The socket is * set to an invalid value to avoid double free errors. * Parameters: sock - socket to communicate with * Return code: none */ void safeCloseSocket(int *sock) { if (*sock != -1) { close(*sock); *sock = -1; } } /* Name: handleChildExit * Description: Handles exit of a child process by obtaining the exit * code and avoiding zombies. * Parameters: signum - number of signal that occurred * Return code: none */ void handleChildExit(int signum) { if (signum == SIGCHLD) { int status; pid_t id = wait(&status); if (id == -1) { fprintf(stderr, "Waiting for child failed: %s\n", strerror(errno)); } else { if (WIFEXITED(status)) { printf("Child %i exited with code %i\n", id, WEXITSTATUS(status)); } else { printf("Something strange happened to child %i\n", id); } } } } /* Name: installSignalHandler * Description: Installs handler for SIGCHLD. * Parameters: none * Return code: true if successful * false if an error occurred */ bool installSignalHandler(void) { bool failed = false; struct sigaction action; action.sa_handler = handleChildExit; action.sa_flags = SA_RESTART; if (sigaction(SIGCHLD, &action, NULL) == -1) { fprintf(stderr, "Installing signal handler failed: %s\n", strerror(errno)); failed = true; } return !failed; } /* Name: handleClient * Description: Handles new client by creating a child process that * then runs an external command. * Parameters: sock - client socket * Return code: true if successful * false if an error occurred */ bool handleClient(int sock) { pid_t id = fork(); if (id == 0) { if (dup2(sock, STDOUT_FILENO) == -1) { fprintf(stderr, "Duplicating file descriptor failed: %s\n", strerror(errno)); exit(EXIT_FAILURE); } safeCloseSocket(&sock); execl("/bin/ls", "ls", "-la", (char*) NULL); // If reached execl() failed! fprintf(stderr, "Executing command failed: %s\n", strerror(errno)); exit(EXIT_FAILURE); } else if (id == -1) { fprintf(stderr, "Creating child process failed: %s\n", strerror(errno)); } else { printf("Started child process with ID %i\n", id); } return id > 0; } int main(void) { int sock = -1; bool good = createTcpSocket(&sock); if (good) good = bindTcpSocket(sock, LISTEN_PORT); if (good) good = setSocketToListenMode(sock); if (good) good = installSignalHandler(); while (good) { int client = -1; good = acceptClient(sock, &client); if (good) good = handleClient(client); safeCloseSocket(&client); } safeCloseSocket(&sock); return good ? EXIT_SUCCESS : EXIT_FAILURE; }