/*! * \brief Send the current program version to the client. * * \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_send_version(simplecmd_t scp, int sock) { // Unused parameters (void) scp; __sock_send(sock, NULL, SP_MAIN_VERSION); return 1; }
/*! * \brief Add a file to the specified server. * * \param[in] server_pid Process identifier of the server to act on * \param[in] file Name and path of the file to serve * \param[in] uri URI of the file to serve * \param[in] count Number of times the file should be served * * \return true if the file was successfully added to the server, false if * something went wrong (and the file was not added to the server) */ bool simplecmd_set_file( pid_t server_pid, const char* file, const char* uri, unsigned int count) { int sock; // Socket descriptor char buffer[512]; // Count as a string sock = __open_sock_by_pid(server_pid); if(sock < 0) return false; __sock_send(sock, __command_handlers[SP_COMMAND_SET_FILE].request, NULL); if(file) { impact(3, "%s: %s: Sending %s %s\n", SP_COMMAND_HEADER_NAMESPACE, __func__, SP_COMMAND_FILE_FILE, file); __sock_send(sock, SP_COMMAND_FILE_FILE, file); } if(count) { sprintf(buffer, "%u", count); impact(3, "%s: %s: Sending %s %s\n", SP_COMMAND_HEADER_NAMESPACE, __func__, SP_COMMAND_FILE_COUNT, buffer); __sock_send(sock, SP_COMMAND_FILE_COUNT, buffer); } if(uri) { impact(3, "%s: %s: Sending %s %s\n", SP_COMMAND_HEADER_NAMESPACE, __func__, SP_COMMAND_FILE_URI, uri); __sock_send(sock, SP_COMMAND_FILE_URI, uri); } close(sock); return true; }
/*! * \brief Send the port our web server is listening on to the client. * * \note If the web server is not running, zero will be sent as the port * number. * * \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_send_port(simplecmd_t scp, int sock) { char buffer[30]; // Port as a string unsigned short port; // Port the web server is listening on port = simplepost_get_port(scp->spp); if(port == 0) return false; if(sprintf(buffer, "%hu", port) <= 0) return false; __sock_send(sock, NULL, buffer); return true; }
/*! * \brief Send the primary address our web server is bound to to the client. * * \note If the web server is not running, a zero-length string will be sent. * See the related comments in __sock_send() and __sock_recv() to get a better * understanding of how this contingency is handled. * * \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_send_address(simplecmd_t scp, int sock) { char* address; // Address of the web server size_t length; // Length of the address string length = simplepost_get_address(scp->spp, &address); if(address == NULL || length == 0) return false; __sock_send(sock, NULL, address ? address : ""); free(address); return true; }
ssize_t __sock_send_r(int fd, const 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_send(fd, buf, len, flags)) == -1) pthread_testcancel(); __sock_putuser(&fd); } else { if ((ret = __libc_send(fd, buf, len, flags)) == -1) pthread_testcancel(); } pthread_cleanup_pop_restore_np(0); return (ret); }
/*! * \brief Send the list of files that we are serving to the client. * * \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_send_files(simplecmd_t scp, int sock) { char buffer[30]; // File count or index as a string simplepost_file_t files; // List of files being served size_t count; // Number of files being served size_t i = 0; // Index of the current file being sent count = simplepost_get_files(scp->spp, &files); impact(3, "%s: %s: Sending list of %zu files\n", SP_COMMAND_HEADER_NAMESPACE, __func__, count); if(sprintf(buffer, "%zu", count) <= 0) { simplepost_file_free(files); return false; } __sock_send(sock, NULL, buffer); for(simplepost_file_t p = files; p; p = p->next) { impact(3, "%s: %s: Sending %s %zu\n", SP_COMMAND_HEADER_NAMESPACE, __func__, SP_COMMAND_FILE_INDEX, i); if(sprintf(buffer, "%zu", i++) <= 0) { simplepost_file_free(files); return false; } __sock_send(sock, SP_COMMAND_FILE_INDEX, buffer); if(p->file) { impact(3, "%s: %s: Sending %s %s\n", SP_COMMAND_HEADER_NAMESPACE, __func__, SP_COMMAND_FILE_FILE, p->file); __sock_send(sock, SP_COMMAND_FILE_FILE, p->file); } if(p->count) { if(sprintf(buffer, "%u", p->count) <= 0) { impact(0, "%s: %s: Failed to buffer %s %u\n", SP_COMMAND_HEADER_NAMESPACE, __func__, SP_COMMAND_FILE_COUNT, p->count); simplepost_file_free(files); return false; } impact(3, "%s: %s: Sending %s %s\n", SP_COMMAND_HEADER_NAMESPACE, __func__, SP_COMMAND_FILE_COUNT, buffer); __sock_send(sock, SP_COMMAND_FILE_COUNT, buffer); } /* Always send the URL last. The reason for this is that only the FILE * and URL fields and required per-file. All others are optional. * Therefore to make sure that the optional fields are not skipped on * the client side, always send a required field last. */ if(p->url) { impact(3, "%s: %s: Sending %s %s\n", SP_COMMAND_HEADER_NAMESPACE, __func__, SP_COMMAND_FILE_URL, p->url); __sock_send(sock, SP_COMMAND_FILE_URL, p->url); } } simplepost_file_free(files); return true; }
/*! * \brief Send a command to the client and read the response. * * \param[in] sock Socket descriptor * \param[in] command * \parblock * Command to send to the client * * If this parameter is NULL, no command will be sent. * \endparblock * \param[out] data * \parblock * NULL-terminated string from the client * * If *data != NULL, you are responsible for freeing it. * \endparblock * * \return the number of bytes written to the read buffer. If an error * occurred (or the operation timed out), zero will be returned instead */ static size_t __sock_recv(int sock, const char* command, char** data) { char buffer[30]; // Number of characters to be read from the buffer size_t length; // Number of characters received char b; // Last byte read from the client *data = NULL; // Failsafe if(command) __sock_send(sock, command, NULL); for(size_t i = 0; read(sock, (void*) &b, 1) == 1; ++i) { if(i == sizeof(buffer)) { impact(0, "%s: %s: String size cannot be longer than %zu bytes\n", SP_COMMAND_HEADER_NAMESPACE, SP_COMMAND_HEADER_PROTOCOL_ERROR, sizeof(buffer)); return 0; } buffer[i] = b; if(b == '\0') break; } if(sscanf(buffer, "%zu", &length) != 1) { impact(0, "%s: %s: %s is not a valid string size\n", SP_COMMAND_HEADER_NAMESPACE, SP_COMMAND_HEADER_PROTOCOL_ERROR, buffer); return 0; } *data = (char*) malloc(sizeof(char) * (length + 1)); if(*data == NULL) { impact(2, "%s: %s: Failed to allocate memory for command data buffer\n", SP_COMMAND_HEADER_NAMESPACE, SP_MAIN_HEADER_MEMORY_ALLOC); if(command) { impact(0, "%s: Data buffer required for command %s\n", SP_COMMAND_HEADER_NAMESPACE, command); } else { impact(0, "%s: Buffer required to receive data\n", SP_COMMAND_HEADER_NAMESPACE); } return 0; } /* The data received may be a zero-length string. The following loop * handles this condition implicitly, but it is not to be overlooked. * Since no terminating character is sent for data (unlike for its length), * a zero-length data string effectively means that we should not attempt * to read anything from the socket. */ for(size_t i = 0; i < length; ++i) { if(read(sock, (void*) &b, 1) == 1) { (*data)[i] = b; } else { impact(0, "%s: Read of %s aborted after receiving only %zu of %zu bytes\n", SP_COMMAND_HEADER_NAMESPACE, command ? command : "data", i, length); free(*data); *data = NULL; return 0; } } (*data)[length] = '\0'; return length; }