|
@@ -0,0 +1,211 @@
|
|
|
+#include "civetweb.h"
|
|
|
+#include <stdint.h>
|
|
|
+#include <string.h>
|
|
|
+#include <stdlib.h>
|
|
|
+
|
|
|
+#if defined(_WIN32)
|
|
|
+#error "Currently not supported"
|
|
|
+#else
|
|
|
+
|
|
|
+#include <unistd.h>
|
|
|
+#define test_sleep(x) (sleep(x))
|
|
|
+#include <sys/types.h>
|
|
|
+#include <sys/socket.h>
|
|
|
+#include <arpa/inet.h>
|
|
|
+#include <netinet/in.h>
|
|
|
+#include <netinet/ip.h>
|
|
|
+typedef int SOCKET;
|
|
|
+#define closesocket(a) (close(a))
|
|
|
+
|
|
|
+#endif
|
|
|
+
|
|
|
+
|
|
|
+static uint64_t call_count = 0;
|
|
|
+
|
|
|
+static struct mg_context *ctx;
|
|
|
+static const char *OPTIONS[] = {
|
|
|
+ "listening_ports", "8080,8443s",
|
|
|
+#ifdef _WIN32
|
|
|
+ "document_root", "fuzz\\docroot",
|
|
|
+ "ssl_certificate", "resources\\cert\\server.pem",
|
|
|
+#else
|
|
|
+ "document_root", "fuzz/docroot",
|
|
|
+ "ssl_certificate", "resources/cert/server.pem",
|
|
|
+#endif
|
|
|
+ NULL, NULL
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
+static void
|
|
|
+init_civetweb(void)
|
|
|
+{
|
|
|
+ struct mg_callbacks callbacks;
|
|
|
+ memset(&callbacks, 0, sizeof(callbacks));
|
|
|
+
|
|
|
+ ctx = mg_start(&callbacks, 0, OPTIONS);
|
|
|
+
|
|
|
+ if (!ctx) {
|
|
|
+ fprintf(stderr, "\nCivetWeb test server failed to start\n");
|
|
|
+ abort();
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Give server 5 seconds to start, before flooding with requests.
|
|
|
+ * Don't know if this is required for fuzz-tests, but it was helpful
|
|
|
+ * when testing starting/stopping the server multiple times in test
|
|
|
+ * container environments. */
|
|
|
+ test_sleep(5);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+static int
|
|
|
+test_http_request(const char *server,
|
|
|
+ uint16_t port,
|
|
|
+ int use_ssl,
|
|
|
+ const char *uri)
|
|
|
+{
|
|
|
+ /* Client var */
|
|
|
+ struct mg_connection *client;
|
|
|
+ char client_err_buf[256];
|
|
|
+ char client_data_buf[4096];
|
|
|
+ const struct mg_response_info *client_ri;
|
|
|
+ int64_t data_read;
|
|
|
+ int r;
|
|
|
+
|
|
|
+ client = mg_connect_client(
|
|
|
+ server, port, use_ssl, client_err_buf, sizeof(client_err_buf));
|
|
|
+
|
|
|
+ if ((client == NULL) || (0 != strcmp(client_err_buf, ""))) {
|
|
|
+ fprintf(stderr, "%s connection to server [%s] port [%u] failed: [%s]\n",
|
|
|
+ use_ssl ? "HTTPS" : "HTTP",
|
|
|
+ server,
|
|
|
+ port,
|
|
|
+ client_err_buf);
|
|
|
+ if (client) {
|
|
|
+ mg_close_connection(client);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* In heavy fuzz testing, sometimes we run out of available sockets.
|
|
|
+ * Wait for some seconds, and retry. */
|
|
|
+ test_sleep(5);
|
|
|
+
|
|
|
+ /* retry once */
|
|
|
+ client = mg_connect_client(
|
|
|
+ server, port, use_ssl, client_err_buf, sizeof(client_err_buf));
|
|
|
+ if (!client) {
|
|
|
+ fprintf(stderr, "Retry: error\n");
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+ fprintf(stderr, "Retry: success\n");
|
|
|
+ }
|
|
|
+
|
|
|
+ mg_printf(client, "GET %s HTTP/1.0\r\n\r\n", uri);
|
|
|
+
|
|
|
+ r = mg_get_response(client, client_err_buf, sizeof(client_err_buf), 10000);
|
|
|
+
|
|
|
+ if ((r < 0) || (0 != strcmp(client_err_buf, ""))) {
|
|
|
+ mg_close_connection(client);
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ client_ri = mg_get_response_info(client);
|
|
|
+ if (client_ri == NULL) {
|
|
|
+ mg_close_connection(client);
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ data_read = 0;
|
|
|
+ while (data_read < client_ri->content_length) {
|
|
|
+ /* store the first sizeof(client_data_buf) bytes
|
|
|
+ * of the HTTP response. */
|
|
|
+ r = mg_read(client,
|
|
|
+ client_data_buf + data_read,
|
|
|
+ sizeof(client_data_buf) - (size_t)data_read);
|
|
|
+ if (r > 0) {
|
|
|
+ data_read += r;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* buffer filled? */
|
|
|
+ if (sizeof(client_data_buf) == (size_t)data_read) {
|
|
|
+ /* ignore the rest */
|
|
|
+ while (r > 0) {
|
|
|
+ char trash[1024];
|
|
|
+ r = mg_read(client, trash, sizeof(trash));
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Nothing left to read */
|
|
|
+ r = mg_read(client, client_data_buf, sizeof(client_data_buf));
|
|
|
+ if (r != 0) {
|
|
|
+ mg_close_connection(client);
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ mg_close_connection(client);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+static int
|
|
|
+LLVMFuzzerTestOneInput_URI(const uint8_t *data, size_t size)
|
|
|
+{
|
|
|
+ static char URI[1024 * 64]; /* static, to avoid stack overflow */
|
|
|
+
|
|
|
+ if (call_count == 0) {
|
|
|
+ memset(URI, 0, sizeof(URI));
|
|
|
+ init_civetweb();
|
|
|
+ }
|
|
|
+ call_count++;
|
|
|
+
|
|
|
+ if (size < sizeof(URI)) {
|
|
|
+ memcpy(URI, data, size);
|
|
|
+ URI[size] = 0;
|
|
|
+ } else {
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ printf("URI: %s\n", URI);
|
|
|
+
|
|
|
+ return test_http_request("127.0.0.1", 8080, 0, URI);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+static int
|
|
|
+LLVMFuzzerTestOneInput_REQUEST(const uint8_t *data, size_t size)
|
|
|
+{
|
|
|
+ if (call_count == 0) {
|
|
|
+ init_civetweb();
|
|
|
+ }
|
|
|
+ call_count++;
|
|
|
+
|
|
|
+ SOCKET sock = socket(AF_INET, SOCK_STREAM, 6);
|
|
|
+ struct sockaddr_in sin;
|
|
|
+ memset(&sin, 0, sizeof(sin));
|
|
|
+ sin.sin_family = AF_INET;
|
|
|
+ sin.sin_port = htons(8080);
|
|
|
+ sin.sin_addr.s_addr = htonl(0x7F000001);
|
|
|
+ connect(sock, (struct sockaddr *)&sin, sizeof(sin));
|
|
|
+
|
|
|
+ char trash[1024];
|
|
|
+ send(sock, data, size, 0);
|
|
|
+ int r, data_read = 0;
|
|
|
+ while ((r = recv(sock, trash, sizeof(trash), 0)) > 0) {
|
|
|
+ data_read += r;
|
|
|
+ };
|
|
|
+
|
|
|
+ shutdown(sock, SHUT_RDWR);
|
|
|
+ closesocket(sock);
|
|
|
+
|
|
|
+ static int max_data_read = 0;
|
|
|
+ if (data_read>max_data_read) {
|
|
|
+ max_data_read = data_read;
|
|
|
+ printf("GOT data: %i\n", data_read);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
|
|
|
+{
|
|
|
+ return LLVMFuzzerTestOneInput_REQUEST(data, size);
|
|
|
+}
|