/*! * \brief Get the port the specified server is listening on. * * \param[in] server_pid Process identifier of the server to query * * \return the server's port number */ unsigned short simplecmd_get_port(pid_t server_pid) { int sock; // Socket descriptor unsigned short port; // Port the specified server is listening on char* buffer; // Port as a string (directly from the server) sock = __open_sock_by_pid(server_pid); if(sock < 0) return 0; __sock_recv(sock, __command_handlers[SP_COMMAND_GET_PORT].request, &buffer); if(buffer) { if(sscanf(buffer, "%hu", &port) != 1) { impact(0, "%s: %s: %s is not a port number\n", SP_COMMAND_HEADER_NAMESPACE, SP_COMMAND_HEADER_PROTOCOL_ERROR, buffer); port = 0; } free(buffer); } else { port = 0; } close(sock); return port; }
/*! * \brief Process a request accepted by the server. * * \param[in] p SimplePost command + client socket wrapper * * \return NULL */ static void* __process_request(void* p) { struct simplecmd_request* scrp = (struct simplecmd_request*) p; // Properly cast SimplePost command request handle simplecmd_t scp = scrp->scp; // SimplePost command instance to act on int sock = scrp->client_sock; // Socket the client connected on free(scrp); scrp = p = NULL; char* command; // Command to process size_t length; // Length of the command string bool response; // Command handler return value ++(scp->client_count); length = __sock_recv(sock, NULL, &command); if(command == NULL || length == 0) goto error; response = -1; for(unsigned int i = SP_COMMAND_MIN; i <= SP_COMMAND_MAX; ++i) { if(strcmp(command, __command_handlers[i].request) == 0) { impact(2, "%s: Request 0x%lx: Responding to %s command\n", SP_COMMAND_HEADER_NAMESPACE, pthread_self(), __command_handlers[i].request); response = (*__command_handlers[i].handler)(scp, sock); break; } } #ifdef DEBUG if(response) { impact(2, "%s: Request 0x%lx: Successfully processed %s command\n", SP_COMMAND_HEADER_NAMESPACE, pthread_self(), command); } else { impact(2, "%s: Request 0x%lx: Failed to process %s command\n", SP_COMMAND_HEADER_NAMESPACE, pthread_self(), command); } #endif // DEBUG error: impact(4, "%s: Request 0x%lx: Closing client %d\n", SP_COMMAND_HEADER_NAMESPACE, pthread_self(), sock); close(sock); if(command) free(command); --(scp->client_count); return NULL; }
/*! * \brief Get the version of the specified server. * * \param[in] server_pid Process identifier of the server to query * \param[out] version * \parblock * Version of the server * * The storage for this string will be dynamically allocated. You are * responsible for freeing it (unless it is NULL, in which case an error * occurred). * \endparblock * * \return the number of characters written to the version string (excluding * the NULL-terminating character) */ size_t simplecmd_get_version(pid_t server_pid, char** version) { int sock; // Socket descriptor size_t length = 0; // Length of the version string *version = NULL; // Failsafe sock = __open_sock_by_pid(server_pid); if(sock < 0) return 0; length = __sock_recv(sock, __command_handlers[SP_COMMAND_GET_VERSION].request, version); close(sock); return length; }
/*! * \brief Get the address the specified server is bound to. * * \param[in] server_pid Process identifier of the server to query * \param[out] address * \parblock * Address of the server * * The storage for this string will be dynamically allocated. You are * responsible for freeing it (unless it is NULL, in which case an error * occurred). * \endparblock * * \return the number of characters written to the address (excluding the NULL- * terminating character) */ size_t simplecmd_get_address(pid_t server_pid, char** address) { int sock; // Socket descriptor size_t length = 0; // Length of the address *address = NULL; // Failsafe sock = __open_sock_by_pid(server_pid); if(sock < 0) return 0; length = __sock_recv(sock, __command_handlers[SP_COMMAND_GET_ADDRESS].request, address); close(sock); return length; }
/** * @brief Recursive receive data from a socket. * @param fd the socket from which to receive data. * @param buf a pointer to a buffer into which to receive the data. * @param len the length of the buffer. * @param flags receive flags (e.g. MSG_WAITALL). */ ssize_t __sock_recv_r(int fd, void *buf, size_t len, int flags) { ssize_t ret = -1; pthread_cleanup_push_defer_np(__sock_putuser, &fd); if (__sock_getuser(fd)) { if ((ret = __sock_recv(fd, buf, len, flags)) == -1) pthread_testcancel(); __sock_putuser(&fd); } else { if ((ret = __libc_recv(fd, buf, len, flags)) == -1) pthread_testcancel(); } pthread_cleanup_pop_restore_np(0); return (ret); }
/*! * \brief Receive a file and count from the client and add it our web server. * * \param[in] scp Instance to act on * \param[in] sock Client socket * * \retval true the requested information was sent successfully * \retval false failed to respond to the request */ static bool __command_recv_file(simplecmd_t scp, int sock) { char* url = NULL; // URL of the file being served char* file = NULL; // Name and path of the file to serve char* uri = NULL; // URI of the file to serve char* buffer = NULL; // Count or identifier string from the client unsigned int count; // Number of times the file should be served while(__sock_recv(sock, NULL, &buffer)) { if(buffer == NULL) goto error; impact(3, "%s: %s: Receiving %s\n", SP_COMMAND_HEADER_NAMESPACE, __func__, buffer); if(strcmp(buffer, SP_COMMAND_FILE_FILE) == 0) { free(buffer); buffer = NULL; if(file) { impact(0, "%s: %s: Received a second FILE\n", SP_COMMAND_HEADER_NAMESPACE, SP_COMMAND_HEADER_PROTOCOL_ERROR); goto error; } else if(__sock_recv(sock, NULL, &file) == 0) { impact(0, "%s: %s: Did not receive a FILE as expected\n", SP_COMMAND_HEADER_NAMESPACE, SP_COMMAND_HEADER_PROTOCOL_ERROR); goto error; } } else if(strcmp(buffer, SP_COMMAND_FILE_URI) == 0) { free(buffer); buffer = NULL; if(uri) { impact(0, "%s: %s: Received a second URI\n", SP_COMMAND_HEADER_NAMESPACE, SP_COMMAND_HEADER_PROTOCOL_ERROR); goto error; } else if(__sock_recv(sock, NULL, &uri) == 0) { impact(0, "%s: %s: Did not receive a URI as expected\n", SP_COMMAND_HEADER_NAMESPACE, SP_COMMAND_HEADER_PROTOCOL_ERROR); goto error; } } else if(strcmp(buffer, SP_COMMAND_FILE_COUNT) == 0) { free(buffer); buffer = NULL; if(__sock_recv(sock, NULL, &buffer) == 0) { impact(0, "%s: %s: Did not receive the COUNT as expected\n", SP_COMMAND_HEADER_NAMESPACE, SP_COMMAND_HEADER_PROTOCOL_ERROR); goto error; } if(sscanf(buffer, "%u", &count) == EOF) { impact(0, "%s: %s: %s is not a valid COUNT\n", SP_COMMAND_HEADER_NAMESPACE, SP_MAIN_HEADER_MEMORY_ALLOC, buffer); goto error; } free(buffer); buffer = NULL; } else { impact(3, "%s: %s: Invalid file identifier \"%s\"\n", SP_COMMAND_HEADER_NAMESPACE, SP_COMMAND_HEADER_PROTOCOL_ERROR, buffer); goto error; } } if(file == NULL) { impact(0, "%s: %s: Did not receive a FILE to serve\n", SP_COMMAND_HEADER_NAMESPACE, SP_COMMAND_HEADER_PROTOCOL_ERROR); goto error; } if(simplepost_serve_file(scp->spp, &url, file, uri, count) == 0) return false; free(buffer); free(uri); free(file); free(url); return true; error: free(buffer); free(uri); free(file); free(url); return false; }
/*! * \brief Get the list of files being served by the specified server. * * \param[in] server_pid Process identifier of the server to act on * \param[out] files List of files currently being served * * \return the number of files hosted by the server, or -1 if the list could * not be retrieved */ ssize_t simplecmd_get_files(pid_t server_pid, simplepost_file_t* files) { int sock; // Socket descriptor char* buffer = NULL; // Count, index, or identifier string from the server size_t count; // Number of files being served size_t i = 0; // Index of the current file being received size_t t = 0; // Temporary file index converted from the buffer simplepost_file_t tail; // Last file in the *files list tail = *files = NULL; // Failsafe sock = __open_sock_by_pid(server_pid); if(sock < 0) return -1; __sock_recv(sock, __command_handlers[SP_COMMAND_GET_FILES].request, &buffer); if(buffer == NULL) goto error; if(sscanf(buffer, "%zu", &count) != 1) { impact(0, "%s: %s: %s is not a number\n", SP_COMMAND_HEADER_NAMESPACE, SP_COMMAND_HEADER_PROTOCOL_ERROR, buffer); goto error; } free(buffer); buffer = NULL; impact(3, "%s: %s: Receiving list of %zu files\n", SP_COMMAND_HEADER_NAMESPACE, __func__, count); if(count == 0) goto no_error; while((i + 1) < count || tail == NULL || tail->file == NULL || tail->url == NULL) { __sock_recv(sock, NULL, &buffer); if(buffer == NULL) goto error; impact(3, "%s: %s: Receiving %s\n", SP_COMMAND_HEADER_NAMESPACE, __func__, buffer); if(strcmp(buffer, SP_COMMAND_FILE_INDEX) == 0) { free(buffer); buffer = NULL; __sock_recv(sock, NULL, &buffer); if(buffer == NULL) { impact(0, "%s: %s: Did not receive the next file index as expected\n", SP_COMMAND_HEADER_NAMESPACE, SP_COMMAND_HEADER_PROTOCOL_ERROR); goto error; } if(sscanf(buffer, "%zu", &t) != 1) { impact(0, "%s: %s: %s is not a number\n", SP_COMMAND_HEADER_NAMESPACE, SP_COMMAND_HEADER_PROTOCOL_ERROR, buffer); goto error; } free(buffer); buffer = NULL; if(tail == NULL) { if(t != i) { impact(0, "%s: %s: Expected the next file index to be %zu, not %zu\n", SP_COMMAND_HEADER_NAMESPACE, SP_COMMAND_HEADER_PROTOCOL_ERROR, i, t); goto error; } tail = *files = simplepost_file_init(); if(tail == NULL) goto error; } else { if(t != ++i) { impact(0, "%s: %s: Expected the next file index to be %zu, not %zu\n", SP_COMMAND_HEADER_NAMESPACE, SP_COMMAND_HEADER_PROTOCOL_ERROR, i, t); goto error; } simplepost_file_t prev = tail; tail = simplepost_file_init(); if(tail == NULL) goto error; tail->prev = prev; prev->next = tail; } } else if(tail == NULL) { impact(0, "%s: %s: Received \"%s\" before the first file index\n", SP_COMMAND_HEADER_NAMESPACE, SP_COMMAND_HEADER_PROTOCOL_ERROR, buffer); goto error; } else if(strcmp(buffer, SP_COMMAND_FILE_FILE) == 0) { free(buffer); buffer = NULL; __sock_recv(sock, NULL, &buffer); if(buffer == NULL) { impact(0, "%s: %s: Did not receive the file[%zu] location as expected\n", SP_COMMAND_HEADER_NAMESPACE, SP_COMMAND_HEADER_PROTOCOL_ERROR, i); goto error; } if(tail->file) { impact(0, "%s: %s: Received new file[%zu] location \"%s\", but it is already set to \"%s\"\n", SP_COMMAND_HEADER_NAMESPACE, SP_COMMAND_HEADER_PROTOCOL_ERROR, i, buffer, tail->file); goto error; } tail->file = buffer; } else if(strcmp(buffer, SP_COMMAND_FILE_URL) == 0) { free(buffer); buffer = NULL; __sock_recv(sock, NULL, &buffer); if(buffer == NULL) { impact(0, "%s: %s: Did not receive the file[%zu] URL as expected\n", SP_COMMAND_HEADER_NAMESPACE, SP_COMMAND_HEADER_PROTOCOL_ERROR, i); goto error; } if(tail->url) { impact(0, "%s: %s: Received new file[%zu] URL \"%s\", but it is already set to \"%s\"\n", SP_COMMAND_HEADER_NAMESPACE, SP_COMMAND_HEADER_PROTOCOL_ERROR, i, buffer, tail->url); goto error; } tail->url = buffer; } else if(strcmp(buffer, SP_COMMAND_FILE_COUNT) == 0) { free(buffer); buffer = NULL; __sock_recv(sock, NULL, &buffer); if(buffer == NULL) { impact(0, "%s: %s: Did not receive the file[%zu] count as expected\n", SP_COMMAND_HEADER_NAMESPACE, SP_COMMAND_HEADER_PROTOCOL_ERROR, i); goto error; } if(sscanf(buffer, "%u", &tail->count) != 1) { impact(0, "%s: %s: Received new file[%zu] count \"%s\", but it is not a positive integer as expected!\n", SP_COMMAND_HEADER_NAMESPACE, SP_COMMAND_HEADER_PROTOCOL_ERROR, i, buffer); goto error; } free(buffer); buffer = NULL; } else { /* We probably should not have run into this condition in the * first place. It most likely means that we are talking to an * older (or newer) version of this program, and the command * protocol has changed. If not, it is probably a bug. */ impact(3, "%s: %s: Skipping unsupported file identifier \"%s\"\n", SP_COMMAND_HEADER_NAMESPACE, SP_COMMAND_HEADER_PROTOCOL_ERROR, buffer); free(buffer); buffer = NULL; /* Read and discard the argument that presumably comes after the * unsupported file identifier that we encountered. */ __sock_recv(sock, NULL, &buffer); free(buffer); buffer = NULL; } } for(tail = *files, i = 0; tail; tail = tail->next, ++i); if(i != count) { impact(0, "%s: BUG! Only received %zu of %zu files\n", __PRETTY_FUNCTION__, i, count); goto error; } no_error: close(sock); return count; error: simplepost_file_free(*files); *files = NULL; free(buffer); close(sock); return -1; }