diff --git a/examples/wificfg/Makefile b/examples/wificfg/Makefile
index 2cbb996..586b087 100644
--- a/examples/wificfg/Makefile
+++ b/examples/wificfg/Makefile
@@ -2,4 +2,11 @@
PROGRAM=wificfg
EXTRA_COMPONENTS=extras/dhcpserver extras/wificfg
+# For the mDNS responder included under extras:
+# EXTRA_COMPONENTS+=extras/mdnsresponder
+# EXTRA_CFLAGS=-DEXTRAS_MDNS_RESPONDER
+
+# For the mDNS responder included with lwip:
+EXTRA_CFLAGS=-DLWIP_MDNS_RESPONDER=1 -DLWIP_NUM_NETIF_CLIENT_DATA=1 -DLWIP_NETIF_EXT_STATUS_CALLBACK=1
+
include ../../common.mk
diff --git a/examples/wificfg/wificfg.c b/examples/wificfg/wificfg.c
index a90a9da..33e70c0 100644
--- a/examples/wificfg/wificfg.c
+++ b/examples/wificfg/wificfg.c
@@ -56,20 +56,32 @@ static int handle_index(int s, wificfg_method method,
if (wificfg_write_html_title(s, buf, len, "Home") < 0) return -1;
if (wificfg_write_string_chunk(s, http_index_content[1], buf, len) < 0) return -1;
- socklen_t addr_len;
- struct sockaddr addr;
- addr_len = sizeof(addr);
- getpeername(s, (struct sockaddr*)&addr, &addr_len);
-
if (wificfg_write_string_chunk(s, "
", buf, len) < 0) return -1;
- if (addr.sa_family == AF_INET) {
- struct sockaddr_in *sa = (struct sockaddr_in *)&addr;
- if (wificfg_write_string_chunk(s, "- Peer address
", buf, len) < 0) return -1;
- snprintf(buf, len, "- " IPSTR " : %d
",
- IP2STR((ip4_addr_t *)&sa->sin_addr.s_addr), ntohs(sa->sin_port));
- if (wificfg_write_string_chunk(s, buf, buf, len) < 0) return -1;
+
+ struct sockaddr_storage addr;
+ socklen_t addr_len = sizeof(addr);
+ if (getpeername(s, (struct sockaddr *)&addr, &addr_len) == 0) {
+ if (((struct sockaddr *)&addr)->sa_family == AF_INET) {
+ struct sockaddr_in *sa = (struct sockaddr_in *)&addr;
+ if (wificfg_write_string_chunk(s, "- Peer address
", buf, len) < 0) return -1;
+ snprintf(buf, len, "- " IPSTR ", port %u
",
+ IP2STR((ip4_addr_t *)&sa->sin_addr.s_addr), ntohs(sa->sin_port));
+ if (wificfg_write_string_chunk(s, buf, buf, len) < 0) return -1;
+ }
+
+#if LWIP_IPV6
+ if (((struct sockaddr *)&addr)->sa_family == AF_INET6) {
+ struct sockaddr_in6 *sa = (struct sockaddr_in6 *)&addr;
+ if (wificfg_write_string_chunk(s, "- Peer address
- ", buf, len) < 0) return -1;
+ if (inet6_ntoa_r(sa->sin6_addr, buf, len)) {
+ if (wificfg_write_string_chunk(s, buf, buf, len) < 0) return -1;
+ }
+ snprintf(buf, len, ", port %u
", ntohs(sa->sin6_port));
+ if (wificfg_write_string_chunk(s, buf, buf, len) < 0) return -1;
+ }
+#endif
}
-
+
if (wificfg_write_string_chunk(s, "
", buf, len) < 0) return -1;
if (wificfg_write_string_chunk(s, http_index_content[2], buf, len) < 0) return -1;
if (wificfg_write_chunk_end(s) < 0) return -1;
diff --git a/extras/wificfg/wificfg.c b/extras/wificfg/wificfg.c
index 5c7a089..22f796e 100644
--- a/extras/wificfg/wificfg.c
+++ b/extras/wificfg/wificfg.c
@@ -45,6 +45,15 @@
#include "wificfg.h"
#include "sysparam.h"
+#if EXTRAS_MDNS_RESPONDER
+#include
+#endif
+
+#if LWIP_MDNS_RESPONDER
+#include
+#endif
+
+
char *wificfg_default_ssid = "EOR_%02X%02X%02X";
char *wificfg_default_password = "esp-open-rtos";
char *wificfg_default_hostname = "eor-%02x%02x%02x";
@@ -529,14 +538,34 @@ static int handle_ipaddr_redirect(int s, char *buf, size_t len)
{
if (wificfg_write_string(s, "HTTP/1.1 302 \r\nLocation: http://") < 0) return -1;
- struct sockaddr addr;
+ struct sockaddr_storage addr;
socklen_t addr_len = sizeof(addr);
- getsockname(s, &addr, &addr_len);
- struct sockaddr_in *sa = (struct sockaddr_in *)&addr;
- snprintf(buf, len, "" IPSTR "/\r\n", IP2STR((ip4_addr_t *)&sa->sin_addr.s_addr));
- if (wificfg_write_string(s, buf) < 0) return -1;;
+ if (getsockname(s, (struct sockaddr *)&addr, &addr_len) == 0) {
+ if (((struct sockaddr *)&addr)->sa_family == AF_INET) {
+ struct sockaddr_in *sa = (struct sockaddr_in *)&addr;
+ snprintf(buf, len, IPSTR, IP2STR((ip4_addr_t *)&sa->sin_addr.s_addr));
+ if (wificfg_write_string(s, buf) < 0) return -1;
+ }
+#if LWIP_IPV6
+ if (((struct sockaddr *)&addr)->sa_family == AF_INET6) {
+ struct sockaddr_in6 *sa = (struct sockaddr_in6 *)&addr;
+ const ip6_addr_t *addr6 = (const ip6_addr_t*)&(sa->sin6_addr);
+ if (ip6_addr_isipv4mappedipv6(addr6)) {
+ snprintf(buf, len, IPSTR, IP2STR((ip4_addr_t *)&addr6->addr[3]));
+ if (wificfg_write_string(s, buf) < 0) return -1;
+ } else {
+ if (wificfg_write_string(s, "[") < 0) return -1;
+ if (ip6addr_ntoa_r(addr6, buf, len)) {
+ if (wificfg_write_string(s, buf) < 0) return -1;
+ }
+ if (wificfg_write_string(s, "]") < 0) return -1;
+ }
+ }
+#endif
+ }
+
/* Always close here - expect a new connection. */
- return wificfg_write_string(s, "Content-Length: 0\r\n"
+ return wificfg_write_string(s, "\r\nContent-Length: 0\r\n"
"Connection: close\r\n"
"\r\n");
}
@@ -692,6 +721,20 @@ static int handle_wificfg_index(int s, wificfg_method method,
snprintf(buf, len, "" IPSTR "", IP2STR(&info.gw));
if (wificfg_write_string_chunk(s, buf, buf, len) < 0) return -1;
}
+#if LWIP_IPV6
+ struct netif *netif = sdk_system_get_netif(STATION_IF);
+ if (netif) {
+ for (int i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+ if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i))) {
+ if (wificfg_write_string_chunk(s, "Station IPv6", buf, len) < 0) return -1;
+ const ip6_addr_t *addr6 = netif_ip6_addr(netif, i);
+ ip6addr_ntoa_r(addr6, buf, len);
+ if (wificfg_write_string_chunk(s, buf, buf, len) < 0) return -1;
+ if (wificfg_write_string_chunk(s, "", buf, len) < 0) return -1;
+ }
+ }
+ }
+#endif
}
if (opmode == SOFTAP_MODE || opmode == STATIONAP_MODE) {
@@ -713,18 +756,51 @@ static int handle_wificfg_index(int s, wificfg_method method,
snprintf(buf, len, "" IPSTR "", IP2STR(&info.gw));
if (wificfg_write_string_chunk(s, buf, buf, len) < 0) return -1;
}
+
+#if LWIP_IPV6
+ struct netif *netif = sdk_system_get_netif(SOFTAP_IF);
+ if (netif) {
+ for (int i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+ if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i))) {
+ if (wificfg_write_string_chunk(s, "AP IPv6", buf, len) < 0) return -1;
+ const ip6_addr_t *addr6 = netif_ip6_addr(netif, i);
+ ip6addr_ntoa_r(addr6, buf, len);
+ if (wificfg_write_string_chunk(s, buf, buf, len) < 0) return -1;
+ if (wificfg_write_string_chunk(s, "", buf, len) < 0) return -1;
+ }
+ }
+ }
+#endif
}
- struct sockaddr addr;
+ struct sockaddr_storage addr;
socklen_t addr_len = sizeof(addr);
- getpeername(s, (struct sockaddr*)&addr, &addr_len);
+ if (getpeername(s, (struct sockaddr *)&addr, &addr_len) == 0) {
+ if (((struct sockaddr *)&addr)->sa_family == AF_INET) {
+ struct sockaddr_in *sa = (struct sockaddr_in *)&addr;
+ if (wificfg_write_string_chunk(s, "Peer address", buf, len) < 0) return -1;
+ snprintf(buf, len, "" IPSTR ", port %u",
+ IP2STR((ip4_addr_t *)&sa->sin_addr.s_addr), ntohs(sa->sin_port));
+ if (wificfg_write_string_chunk(s, buf, buf, len) < 0) return -1;
+ }
- if (addr.sa_family == AF_INET) {
- struct sockaddr_in *sa = (struct sockaddr_in *)&addr;
- if (wificfg_write_string_chunk(s, "Peer address", buf, len) < 0) return -1;
- snprintf(buf, len, "" IPSTR " : %u",
- IP2STR((ip4_addr_t *)&sa->sin_addr.s_addr), ntohs(sa->sin_port));
- if (wificfg_write_string_chunk(s, buf, buf, len) < 0) return -1;
+#if LWIP_IPV6
+ if (((struct sockaddr *)&addr)->sa_family == AF_INET6) {
+ struct sockaddr_in6 *sa = (struct sockaddr_in6 *)&addr;
+ if (wificfg_write_string_chunk(s, "Peer address", buf, len) < 0) return -1;
+ const ip6_addr_t *addr6 = (const ip6_addr_t*)&(sa->sin6_addr);
+ if (ip6_addr_isipv4mappedipv6(addr6)) {
+ snprintf(buf, len, IPSTR, IP2STR((ip4_addr_t *)&addr6->addr[3]));
+ if (wificfg_write_string_chunk(s, buf, buf, len) < 0) return -1;
+ } else {
+ if (ip6addr_ntoa_r(addr6, buf, len)) {
+ if (wificfg_write_string_chunk(s, buf, buf, len) < 0) return -1;
+ }
+ }
+ snprintf(buf, len, ", port %u", ntohs(sa->sin6_port));
+ if (wificfg_write_string_chunk(s, buf, buf, len) < 0) return -1;
+ }
+#endif
}
if (wificfg_write_string_chunk(s, http_wificfg_content[2], buf, len) < 0) return -1;
@@ -1509,6 +1585,41 @@ typedef struct {
const wificfg_dispatch *dispatch;
} server_params;
+/*
+ * Test if the http host string is a literal IP address. This is needed for the
+ * softap mode captive portal and must return false for typical server strings
+ * which will be redirected, and true for literal IP address to avoid a redirect
+ * for them.
+ */
+static bool host_is_name(const char *host)
+{
+ if (host == NULL) {
+ return true;
+ }
+
+ size_t len = strlen(host);
+
+ if (len < 4) {
+ return true;
+ }
+
+ char first = host[0];
+ char last = host[len - 1];
+
+ if (first == '[' && last == ']') {
+ /* Likely an IPv6 address */
+ return false;
+ }
+
+ if (first >= '0' && first <= '9' && last >= '0' && last <= '9') {
+ /* Likely IPv4 address */
+ return false;
+ }
+
+ return true;
+}
+
+
/*
* The http server uses a single thread to service all requests, one request at
* a time, to keep peak resource usage to a minimum. Keeping connections open
@@ -1529,14 +1640,52 @@ typedef struct {
*/
static void server_task(void *pvParameters)
{
+ char *hostname_local = NULL;
+ char *hostname = NULL;
+ sysparam_get_string("hostname", &hostname);
+ if (hostname) {
+ size_t len = strlen(hostname) + 6 + 1;
+ hostname_local = (char *)malloc(len);
+ if (hostname_local) {
+ snprintf(hostname_local, len, "%s.local", hostname);
+ }
+
+#if EXTRAS_MDNS_RESPONDER
+ mdns_init();
+ mdns_add_facility(hostname, "_http", NULL, mdns_TCP + mdns_Browsable, 80, 600);
+#endif
+#if LWIP_MDNS_RESPONDER
+ mdns_resp_init();
+ struct netif *netif = sdk_system_get_netif(STATION_IF);
+ if (netif) {
+ mdns_resp_add_netif(netif, hostname, 120);
+ mdns_resp_add_service(netif, hostname, "_http",
+ DNSSD_PROTO_TCP, 80, 3600, NULL, NULL);
+ }
+#endif
+
+ free(hostname);
+ }
+
server_params *params = pvParameters;
- struct sockaddr_in serv_addr;
+#if LWIP_IPV6
+ int listenfd = socket(AF_INET6, SOCK_STREAM, 0);
+ struct sockaddr_in6 serv_addr;
+ memset(&serv_addr, '0', sizeof(serv_addr));
+ serv_addr.sin6_family = AF_INET6;
+ serv_addr.sin6_port = htons(params->port);
+ serv_addr.sin6_flowinfo = 0;
+ serv_addr.sin6_addr = in6addr_any;
+ serv_addr.sin6_scope_id = IP6_NO_ZONE;
+#else
int listenfd = socket(AF_INET, SOCK_STREAM, 0);
+ struct sockaddr_in serv_addr;
memset(&serv_addr, '0', sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
- serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(params->port);
+ serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
+#endif
bind(listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
listen(listenfd, 2);
@@ -1618,8 +1767,7 @@ static void server_task(void *pvParameters)
/* Read the headers, noting some of interest. */
wificfg_content_type content_type = HTTP_CONTENT_TYPE_OTHER;
bool connection_close = false;
- bool hostp = false;
- uint32_t host = IPADDR_NONE;
+ bool host_redirect = false;
long content_length = 0;
for (;;) {
@@ -1640,8 +1788,10 @@ static void server_task(void *pvParameters)
value = skip_whitespace(value);
switch (header) {
case HTTP_HEADER_HOST:
- hostp = true;
- host = ipaddr_addr(value);
+ if (hostname_local && host_is_name(value) &&
+ strcmp(value, hostname_local)) {
+ host_redirect = true;
+ }
break;
case HTTP_HEADER_CONTENT_LENGTH:
content_length = strtoul(value, NULL, 10);
@@ -1658,12 +1808,14 @@ static void server_task(void *pvParameters)
}
}
- if (hostp && host == IPADDR_NONE) {
+ if (host_redirect) {
/* Redirect to an IP address. */
handle_ipaddr_redirect(s, buf, sizeof(buf));
/* Close the connection. */
break;
- } else if (match) {
+ }
+
+ if (match) {
if ((*match->handler)(s, method, content_length, content_type, buf, sizeof(buf)) < 0) break;
} else {
if (wificfg_write_string(s, not_found_header) < 0) break;
@@ -1712,13 +1864,23 @@ static void dns_task(void *pvParameters)
ip4_addr_t server_addr;
server_addr.addr = ipaddr_addr(wifi_ap_ip_addr);
- struct sockaddr_in serv_addr;
+#if LWIP_IPV6
+ int fd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
+ struct sockaddr_in6 serv_addr;
+ memset(&serv_addr, '0', sizeof(serv_addr));
+ serv_addr.sin6_family = AF_INET6;
+ serv_addr.sin6_port = htons(53);
+ serv_addr.sin6_flowinfo = 0;
+ serv_addr.sin6_addr = in6addr_any;
+ serv_addr.sin6_scope_id = IP6_NO_ZONE;
+#else
int fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
-
+ struct sockaddr_in serv_addr;
memset(&serv_addr, '0', sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(53);
+#endif
bind(fd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
const struct ifreq ifreq0 = { "en0" };
@@ -1729,12 +1891,12 @@ static void dns_task(void *pvParameters)
for (;;) {
char buffer[96];
- struct sockaddr src_addr;
+ struct sockaddr_storage src_addr;
socklen_t src_addr_len = sizeof(src_addr);
size_t count = recvfrom(fd, buffer, sizeof(buffer), 0, (struct sockaddr*)&src_addr, &src_addr_len);
/* Drop messages that are too large to send a response in the buffer */
- if (count > 0 && count <= sizeof(buffer) - 16 && src_addr.sa_family == AF_INET) {
+ if (count > 0 && count <= sizeof(buffer) - 16) {
size_t qname_len = strlen(buffer + 12) + 1;
uint32_t reply_len = 2 + 10 + qname_len + 16 + 4;
@@ -1771,7 +1933,7 @@ static void dns_task(void *pvParameters)
*head++ = ip4_addr3(&server_addr);
*head++ = ip4_addr4(&server_addr);
- sendto(fd, buffer, reply_len, 0, &src_addr, src_addr_len);
+ sendto(fd, buffer, reply_len, 0, (struct sockaddr*)&src_addr, src_addr_len);
}
}
}
@@ -2020,6 +2182,11 @@ void wificfg_init(uint32_t port, const wificfg_dispatch *dispatch)
params->wificfg_dispatch = wificfg_dispatch_list;
params->dispatch = dispatch;
- xTaskCreate(server_task, "WiFi Cfg HTTP", 464, params, 2, NULL);
+ size_t stack_size = 464;
+#if LWIP_MDNS_RESPONDER
+ /* Uses a lot of stack space, so allocate extra. */
+ stack_size += 128;
+#endif
+ xTaskCreate(server_task, "WiFi Cfg HTTP", stack_size, params, 2, NULL);
}
}