From c3aae185dfbcce4ac37e3f5f415601aecdc1bdb3 Mon Sep 17 00:00:00 2001 From: cpq Date: Tue, 22 Dec 2020 10:16:31 +0000 Subject: [PATCH] Add ipv6 server test --- .github/workflows/test.yml | 8 ++--- Makefile | 6 ++-- mongoose.c | 73 ++++++++++++++++++++------------------ src/http.c | 2 +- src/sock.c | 71 ++++++++++++++++++------------------ test/unit_test.c | 9 +++++ 6 files changed, 93 insertions(+), 76 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7df14a62..3489968a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,9 +8,9 @@ jobs: - name: Install packages run: sudo apt-get install libmbedtls-dev - name: make - run: make test test++ + run: make test test++ IPV6=0 - name: openssl - run: make test SSL=OPENSSL + run: make test SSL=OPENSSL IPV6=0 - name: examples run: make ex macos: @@ -20,9 +20,9 @@ jobs: - name: Install packages run: HOMEBREW_NO_AUTO_UPDATE=1 brew install jq mbedtls openssl - name: make - run: make test++ upload-coverage + run: make test++ upload-coverage IPV6=0 - name: openssl - run: make test SSL=OPENSSL + run: make test SSL=OPENSSL IPV6=0 - name: exports run: make mg_prefix windows: diff --git a/Makefile b/Makefile index ecd8e7c1..b851affd 100644 --- a/Makefile +++ b/Makefile @@ -9,6 +9,7 @@ VC2017 = docker run --rm -e WINEDEBUG=-all -v $(CDIR):$(CDIR) -w $(CDIR) docker. GCC = docker run --rm -v $(CDIR):$(CDIR) -w $(CDIR) mdashnet/cc2 VCFLAGS = /nologo /W3 /O2 /I. $(DEFS) $(TFLAGS) CLANG ?= clang # /usr/local/opt/llvm\@9/bin/clang +IPV6 ?= 1 ASAN_OPTIONS ?= EXAMPLES := $(wildcard examples/*) EXAMPLE_TARGET ?= example @@ -51,7 +52,7 @@ fuzz: mongoose.c mongoose.h Makefile test/fuzz.c $(DEBUGGER) ./fuzzer # make CLANG=/usr/local/opt/llvm\@8/bin/clang ASAN_OPTIONS=detect_leaks=1 -test: CFLAGS += -DMG_ENABLE_IPV6=1 -fsanitize=address#,undefined +test: CFLAGS += -DMG_ENABLE_IPV6=$(IPV6) -fsanitize=address#,undefined test: mongoose.c mongoose.h Makefile test/unit_test.c $(CLANG) mongoose.c test/unit_test.c $(CFLAGS) -coverage $(LDFLAGS) -g -o unit_test ASAN_OPTIONS=$(ASAN_OPTIONS) $(DEBUGGER) ./unit_test @@ -76,7 +77,8 @@ vc2017: Makefile mongoose.c mongoose.h test/unit_test.c $(VC2017) wine64 cl mongoose.c test/unit_test.c $(VCFLAGS) ws2_32.lib /Fe$@.exe $(VC2017) wine64 $@.exe -linux: CFLAGS += -DMG_ENABLE_IPV6=1 -fsanitize=address,undefined +#linux: CFLAGS += -DMG_ENABLE_IPV6=$(IPV6) +linux: CFLAGS += -fsanitize=address,undefined linux: Makefile mongoose.c mongoose.h test/unit_test.c $(GCC) $(CC) mongoose.c test/unit_test.c $(CFLAGS) $(LDFLAGS) -o unit_test_gcc $(GCC) ./unit_test_gcc diff --git a/mongoose.c b/mongoose.c index 4b8fa857..8d80c2c5 100644 --- a/mongoose.c +++ b/mongoose.c @@ -1129,7 +1129,7 @@ struct mg_connection *mg_http_listen(struct mg_mgr *mgr, const char *url, struct mg_connection *c = mg_listen(mgr, url, fn, fn_data); if (c != NULL) c->pfn = http_cb, c->pfn_data = mgr; #if MG_ENABLE_HTTP_DEBUG_ENDPOINT - snprintf(c->label, sizeof(c->label) - 1, "<-LSN"); + if (c != NULL) snprintf(c->label, sizeof(c->label) - 1, "<-LSN"); #endif return c; } @@ -2581,45 +2581,49 @@ static void mg_set_non_blocking_mode(SOCKET fd) { #endif } -SOCKET mg_open_listener(const char *ip, uint16_t port, int is_udp) { - union usa usa; - int on = 1; - int proto = is_udp ? IPPROTO_UDP : IPPROTO_TCP; - int type = is_udp ? SOCK_DGRAM : SOCK_STREAM; +SOCKET mg_open_listener(const char *url) { struct mg_addr addr; - SOCKET fd; + SOCKET fd = INVALID_SOCKET; - memset(&usa, 0, sizeof(usa)); - usa.sin.sin_family = AF_INET; - usa.sin.sin_port = mg_htons(port); - mg_aton(mg_url_host(ip), &addr); - *(uint32_t *) &usa.sin.sin_addr = addr.ip; + memset(&addr, 0, sizeof(addr)); + addr.port = mg_htons(mg_url_port(url)); + if (!mg_aton(mg_url_host(url), &addr)) { + LOG(LL_ERROR, ("invalid listening URL: %s", url)); + } else { + union usa usa = tousa(&addr); + int on = 1, af = AF_INET; + int type = strncmp(url, "udp:", 4) == 0 ? SOCK_DGRAM : SOCK_STREAM; + int proto = type == SOCK_DGRAM ? IPPROTO_UDP : IPPROTO_TCP; + socklen_t slen = sizeof(usa.sin); +#if MG_ENABLE_IPV6 + if (addr.is_ip6) af = AF_INET6, slen = sizeof(usa.sin6); +#endif - if ((fd = socket(AF_INET, type, proto)) != INVALID_SOCKET && + if ((fd = socket(af, type, proto)) != INVALID_SOCKET && #if !defined(_WIN32) || !defined(SO_EXCLUSIVEADDRUSE) - // SO_RESUSEADDR is not enabled on Windows because the semantics of - // SO_REUSEADDR on UNIX and Windows is different. On Windows, - // SO_REUSEADDR allows to bind a socket to a port without error even if - // the port is already open by another program. This is not the behavior - // SO_REUSEADDR was designed for, and leads to hard-to-track failure - // scenarios. Therefore, SO_REUSEADDR was disabled on Windows unless - // SO_EXCLUSIVEADDRUSE is supported and set on a socket. - !setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *) &on, sizeof(on)) && + // SO_RESUSEADDR is not enabled on Windows because the semantics of + // SO_REUSEADDR on UNIX and Windows is different. On Windows, + // SO_REUSEADDR allows to bind a socket to a port without error even if + // the port is already open by another program. This is not the behavior + // SO_REUSEADDR was designed for, and leads to hard-to-track failure + // scenarios. Therefore, SO_REUSEADDR was disabled on Windows unless + // SO_EXCLUSIVEADDRUSE is supported and set on a socket. + !setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *) &on, sizeof(on)) && #endif #if defined(_WIN32) && defined(SO_EXCLUSIVEADDRUSE) && !defined(WINCE) - // "Using SO_REUSEADDR and SO_EXCLUSIVEADDRUSE" - !setsockopt(fd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (void *) &on, - sizeof(on)) && + // "Using SO_REUSEADDR and SO_EXCLUSIVEADDRUSE" + !setsockopt(fd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (void *) &on, + sizeof(on)) && #endif - bind(fd, &usa.sa, sizeof(usa.sin)) == 0 && - // NOTE(lsm): FreeRTOS uses backlog value as a connection limit - (type == SOCK_DGRAM || listen(fd, 128) == 0)) { - mg_set_non_blocking_mode(fd); - } else if (fd >= 0) { - LOG(LL_ERROR, - ("Failed to listen on %s:%hu, errno %d", ip, port, MG_SOCK_ERRNO)); - closesocket(fd); - fd = INVALID_SOCKET; + bind(fd, &usa.sa, slen) == 0 && + // NOTE(lsm): FreeRTOS uses backlog value as a connection limit + (type == SOCK_DGRAM || listen(fd, 128) == 0)) { + mg_set_non_blocking_mode(fd); + } else if (fd >= 0) { + LOG(LL_ERROR, ("Failed to listen on %s, errno %d", url, MG_SOCK_ERRNO)); + closesocket(fd); + fd = INVALID_SOCKET; + } } return fd; @@ -2836,8 +2840,7 @@ struct mg_connection *mg_listen(struct mg_mgr *mgr, const char *url, mg_event_handler_t fn, void *fn_data) { struct mg_connection *c = NULL; int is_udp = strncmp(url, "udp:", 4) == 0; - struct mg_str host = mg_url_host(url); - SOCKET fd = mg_open_listener(host.ptr, mg_url_port(url), is_udp); + SOCKET fd = mg_open_listener(url); if (fd == INVALID_SOCKET) { } else if ((c = alloc_conn(mgr, 0, fd)) == NULL) { LOG(LL_ERROR, ("OOM %s", url)); diff --git a/src/http.c b/src/http.c index 2d7a2087..89621879 100644 --- a/src/http.c +++ b/src/http.c @@ -730,7 +730,7 @@ struct mg_connection *mg_http_listen(struct mg_mgr *mgr, const char *url, struct mg_connection *c = mg_listen(mgr, url, fn, fn_data); if (c != NULL) c->pfn = http_cb, c->pfn_data = mgr; #if MG_ENABLE_HTTP_DEBUG_ENDPOINT - snprintf(c->label, sizeof(c->label) - 1, "<-LSN"); + if (c != NULL) snprintf(c->label, sizeof(c->label) - 1, "<-LSN"); #endif return c; } diff --git a/src/sock.c b/src/sock.c index ed00875a..4265f0ad 100644 --- a/src/sock.c +++ b/src/sock.c @@ -174,45 +174,49 @@ static void mg_set_non_blocking_mode(SOCKET fd) { #endif } -SOCKET mg_open_listener(const char *ip, uint16_t port, int is_udp) { - union usa usa; - int on = 1; - int proto = is_udp ? IPPROTO_UDP : IPPROTO_TCP; - int type = is_udp ? SOCK_DGRAM : SOCK_STREAM; +SOCKET mg_open_listener(const char *url) { struct mg_addr addr; - SOCKET fd; + SOCKET fd = INVALID_SOCKET; - memset(&usa, 0, sizeof(usa)); - usa.sin.sin_family = AF_INET; - usa.sin.sin_port = mg_htons(port); - mg_aton(mg_url_host(ip), &addr); - *(uint32_t *) &usa.sin.sin_addr = addr.ip; + memset(&addr, 0, sizeof(addr)); + addr.port = mg_htons(mg_url_port(url)); + if (!mg_aton(mg_url_host(url), &addr)) { + LOG(LL_ERROR, ("invalid listening URL: %s", url)); + } else { + union usa usa = tousa(&addr); + int on = 1, af = AF_INET; + int type = strncmp(url, "udp:", 4) == 0 ? SOCK_DGRAM : SOCK_STREAM; + int proto = type == SOCK_DGRAM ? IPPROTO_UDP : IPPROTO_TCP; + socklen_t slen = sizeof(usa.sin); +#if MG_ENABLE_IPV6 + if (addr.is_ip6) af = AF_INET6, slen = sizeof(usa.sin6); +#endif - if ((fd = socket(AF_INET, type, proto)) != INVALID_SOCKET && + if ((fd = socket(af, type, proto)) != INVALID_SOCKET && #if !defined(_WIN32) || !defined(SO_EXCLUSIVEADDRUSE) - // SO_RESUSEADDR is not enabled on Windows because the semantics of - // SO_REUSEADDR on UNIX and Windows is different. On Windows, - // SO_REUSEADDR allows to bind a socket to a port without error even if - // the port is already open by another program. This is not the behavior - // SO_REUSEADDR was designed for, and leads to hard-to-track failure - // scenarios. Therefore, SO_REUSEADDR was disabled on Windows unless - // SO_EXCLUSIVEADDRUSE is supported and set on a socket. - !setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *) &on, sizeof(on)) && + // SO_RESUSEADDR is not enabled on Windows because the semantics of + // SO_REUSEADDR on UNIX and Windows is different. On Windows, + // SO_REUSEADDR allows to bind a socket to a port without error even if + // the port is already open by another program. This is not the behavior + // SO_REUSEADDR was designed for, and leads to hard-to-track failure + // scenarios. Therefore, SO_REUSEADDR was disabled on Windows unless + // SO_EXCLUSIVEADDRUSE is supported and set on a socket. + !setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *) &on, sizeof(on)) && #endif #if defined(_WIN32) && defined(SO_EXCLUSIVEADDRUSE) && !defined(WINCE) - // "Using SO_REUSEADDR and SO_EXCLUSIVEADDRUSE" - !setsockopt(fd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (void *) &on, - sizeof(on)) && + // "Using SO_REUSEADDR and SO_EXCLUSIVEADDRUSE" + !setsockopt(fd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (void *) &on, + sizeof(on)) && #endif - bind(fd, &usa.sa, sizeof(usa.sin)) == 0 && - // NOTE(lsm): FreeRTOS uses backlog value as a connection limit - (type == SOCK_DGRAM || listen(fd, 128) == 0)) { - mg_set_non_blocking_mode(fd); - } else if (fd >= 0) { - LOG(LL_ERROR, - ("Failed to listen on %s:%hu, errno %d", ip, port, MG_SOCK_ERRNO)); - closesocket(fd); - fd = INVALID_SOCKET; + bind(fd, &usa.sa, slen) == 0 && + // NOTE(lsm): FreeRTOS uses backlog value as a connection limit + (type == SOCK_DGRAM || listen(fd, 128) == 0)) { + mg_set_non_blocking_mode(fd); + } else if (fd >= 0) { + LOG(LL_ERROR, ("Failed to listen on %s, errno %d", url, MG_SOCK_ERRNO)); + closesocket(fd); + fd = INVALID_SOCKET; + } } return fd; @@ -429,8 +433,7 @@ struct mg_connection *mg_listen(struct mg_mgr *mgr, const char *url, mg_event_handler_t fn, void *fn_data) { struct mg_connection *c = NULL; int is_udp = strncmp(url, "udp:", 4) == 0; - struct mg_str host = mg_url_host(url); - SOCKET fd = mg_open_listener(host.ptr, mg_url_port(url), is_udp); + SOCKET fd = mg_open_listener(url); if (fd == INVALID_SOCKET) { } else if ((c = alloc_conn(mgr, 0, fd)) == NULL) { LOG(LL_ERROR, ("OOM %s", url)); diff --git a/test/unit_test.c b/test/unit_test.c index 0fde86a3..d3c2ecd8 100644 --- a/test/unit_test.c +++ b/test/unit_test.c @@ -564,6 +564,15 @@ static void test_http_server(void) { ASSERT(fetch(&mgr, buf, url, "GET /a.txt HTTP/1.0\n\n") == 200); ASSERT(fetch(&mgr, buf, url, "HEAD /a.txt HTTP/1.0\n\n") == 200); +#if MG_ENABLE_IPV6 + { + const char *url6 = "http://[::1]:12346"; + ASSERT(mg_http_listen(&mgr, url6, eh1, NULL) != NULL); + ASSERT(fetch(&mgr, buf, url6, "GET /a.txt HTTP/1.0\n\n") == 200); + ASSERT(cmpbody(buf, "hello\n") == 0); + } +#endif + mg_mgr_free(&mgr); ASSERT(mgr.conns == NULL); }