1 // Copyright 2021 Espressif Systems (Shanghai) PTE LTD
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 /**
16 * @brief MDNS Server Networking module implemented using BSD sockets
17 */
18
19 #include <string.h>
20 #include "esp_event.h"
21 #include "mdns_networking.h"
22 #include <sys/types.h>
23 #include <sys/socket.h>
24 #include <arpa/inet.h>
25 #include <netdb.h>
26 #include <errno.h>
27 #include <stdbool.h>
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <sys/param.h>
31 #include "esp_log.h"
32
33 #if defined(CONFIG_IDF_TARGET_LINUX)
34 #include <sys/ioctl.h>
35 #include <net/if.h>
36 #endif
37
38 extern mdns_server_t * _mdns_server;
39
40 static const char *TAG = "MDNS_Networking";
41 static bool s_run_sock_recv_task = false;
42 static int create_socket(esp_netif_t *netif);
43 static int join_mdns_multicast_group(int sock, esp_netif_t *netif, mdns_ip_protocol_t ip_protocol);
44
45 #if defined(CONFIG_IDF_TARGET_LINUX)
46 // Need to define packet buffer struct on linux
47 struct pbuf {
48 struct pbuf * next;
49 void * payload;
50 size_t tot_len;
51 size_t len;
52 };
53 #else
54 // Compatibility define to access sock-addr struct the same way for lwip and linux
55 #define s6_addr32 un.u32_addr
56 #endif // CONFIG_IDF_TARGET_LINUX
57
delete_socket(int sock)58 static void delete_socket(int sock)
59 {
60 close(sock);
61 }
62
sock_to_pcb(int sock)63 static struct udp_pcb* sock_to_pcb(int sock)
64 {
65 if (sock < 0) {
66 return NULL;
67 }
68 // Note: sock=0 is a valid descriptor, so save it as +1 ("1" is a valid pointer)
69 intptr_t sock_plus_one = sock + 1;
70 return (struct udp_pcb*)sock_plus_one;
71 }
72
pcb_to_sock(struct udp_pcb * pcb)73 static int pcb_to_sock(struct udp_pcb* pcb)
74 {
75 if (pcb == NULL) {
76 return -1;
77 }
78 intptr_t sock_plus_one = (intptr_t)pcb;
79 return sock_plus_one - 1;
80 }
81
_mdns_get_packet_data(mdns_rx_packet_t * packet)82 void* _mdns_get_packet_data(mdns_rx_packet_t *packet)
83 {
84 return packet->pb->payload;
85 }
86
_mdns_get_packet_len(mdns_rx_packet_t * packet)87 size_t _mdns_get_packet_len(mdns_rx_packet_t *packet)
88 {
89 return packet->pb->len;
90 }
91
_mdns_packet_free(mdns_rx_packet_t * packet)92 void _mdns_packet_free(mdns_rx_packet_t *packet)
93 {
94 free(packet->pb->payload);
95 free(packet->pb);
96 free(packet);
97 }
98
_mdns_pcb_deinit(mdns_if_t tcpip_if,mdns_ip_protocol_t ip_protocol)99 esp_err_t _mdns_pcb_deinit(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
100 {
101 struct udp_pcb * pcb = _mdns_server->interfaces[tcpip_if].pcbs[ip_protocol].pcb;
102 _mdns_server->interfaces[tcpip_if].pcbs[ip_protocol].pcb = NULL;
103 if (_mdns_server->interfaces[tcpip_if].pcbs[MDNS_IP_PROTOCOL_V4].pcb == NULL &&
104 _mdns_server->interfaces[tcpip_if].pcbs[MDNS_IP_PROTOCOL_V6].pcb == NULL) {
105 // if the interface for both protocol uninitialized, close the interface socket
106 int sock = pcb_to_sock(pcb);
107 if (sock >= 0) {
108 delete_socket(sock);
109 }
110 }
111
112 for (int i=0; i<MDNS_IF_MAX; i++) {
113 for (int j=0; j<MDNS_IP_PROTOCOL_MAX; j++) {
114 if (_mdns_server->interfaces[i].pcbs[j].pcb)
115 // If any of the interfaces/protocol initialized
116 return ESP_OK;
117 }
118 }
119
120 // no interface alive, stop the rx task
121 s_run_sock_recv_task = false;
122 vTaskDelay(pdMS_TO_TICKS(500));
123 return ESP_OK;
124 }
125
126 #if defined(CONFIG_IDF_TARGET_LINUX)
127 #ifdef CONFIG_LWIP_IPV6
inet6_ntoa_r(struct in6_addr addr,char * ptr,size_t size)128 static char* inet6_ntoa_r(struct in6_addr addr, char* ptr, size_t size)
129 {
130 inet_ntop(AF_INET6, &(addr.s6_addr32[0]), ptr, size);
131 return ptr;
132 }
133 #endif // CONFIG_LWIP_IPV6
inet_ntoa_r(struct in_addr addr,char * ptr,size_t size)134 static char* inet_ntoa_r(struct in_addr addr, char* ptr, size_t size)
135 {
136 char * res = inet_ntoa(addr);
137 if (res && strlen(res) < size) {
138 strcpy(ptr, res);
139 }
140 return res;
141 }
142 #endif // CONFIG_IDF_TARGET_LINUX
143
get_string_address(struct sockaddr_storage * source_addr)144 static inline char* get_string_address(struct sockaddr_storage *source_addr)
145 {
146 static char address_str[40]; // 40=(8*4+7+term) is the max size of ascii IPv6 addr "XXXX:XX...XX:XXXX"
147 char *res = NULL;
148 // Convert ip address to string
149 if (source_addr->ss_family == PF_INET) {
150 res = inet_ntoa_r(((struct sockaddr_in *)source_addr)->sin_addr, address_str, sizeof(address_str));
151 }
152 #ifdef CONFIG_LWIP_IPV6
153 else if (source_addr->ss_family == PF_INET6) {
154 res = inet6_ntoa_r(((struct sockaddr_in6 *)source_addr)->sin6_addr, address_str, sizeof(address_str));
155 }
156 #endif
157 if (!res) {
158 address_str[0] = '\0'; // Returns empty string if conversion didn't succeed
159 }
160 return address_str;
161 }
162
163
espaddr_to_inet(const esp_ip_addr_t * addr,const uint16_t port,const mdns_ip_protocol_t ip_protocol,struct sockaddr_storage * in_addr)164 static inline size_t espaddr_to_inet(const esp_ip_addr_t *addr, const uint16_t port, const mdns_ip_protocol_t ip_protocol, struct sockaddr_storage *in_addr)
165 {
166 size_t ss_addr_len = 0;
167 memset(in_addr, 0, sizeof(struct sockaddr_storage));
168 if (ip_protocol == MDNS_IP_PROTOCOL_V4 && addr->type == ESP_IPADDR_TYPE_V4) {
169 in_addr->ss_family = PF_INET;
170 #if !defined(CONFIG_IDF_TARGET_LINUX)
171 in_addr->s2_len = sizeof(struct sockaddr_in);
172 #endif
173 ss_addr_len = sizeof(struct sockaddr_in);
174 struct sockaddr_in *in_addr_ip4 = (struct sockaddr_in *) in_addr;
175 in_addr_ip4->sin_port = port;
176 in_addr_ip4->sin_addr.s_addr = addr->u_addr.ip4.addr;
177 }
178 #if CONFIG_LWIP_IPV6
179 else if (ip_protocol == MDNS_IP_PROTOCOL_V6 && addr->type == ESP_IPADDR_TYPE_V6) {
180 memset(in_addr, 0, sizeof(struct sockaddr_storage));
181 in_addr->ss_family = PF_INET6;
182 #if !defined(CONFIG_IDF_TARGET_LINUX)
183 in_addr->s2_len = sizeof(struct sockaddr_in6);
184 #endif
185 ss_addr_len = sizeof(struct sockaddr_in6);
186 struct sockaddr_in6 * in_addr_ip6 = (struct sockaddr_in6 *)in_addr;
187 uint32_t *u32_addr = in_addr_ip6->sin6_addr.s6_addr32;
188 in_addr_ip6->sin6_port = port;
189 u32_addr[0] = addr->u_addr.ip6.addr[0];
190 u32_addr[1] = addr->u_addr.ip6.addr[1];
191 u32_addr[2] = addr->u_addr.ip6.addr[2];
192 u32_addr[3] = addr->u_addr.ip6.addr[3];
193 }
194 #endif // CONFIG_LWIP_IPV6
195 return ss_addr_len;
196 }
197
_mdns_udp_pcb_write(mdns_if_t tcpip_if,mdns_ip_protocol_t ip_protocol,const esp_ip_addr_t * ip,uint16_t port,uint8_t * data,size_t len)198 size_t _mdns_udp_pcb_write(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, const esp_ip_addr_t *ip, uint16_t port, uint8_t * data, size_t len)
199 {
200 int sock = pcb_to_sock(_mdns_server->interfaces[tcpip_if].pcbs[ip_protocol].pcb);
201 if (sock < 0) {
202 return 0;
203 }
204 struct sockaddr_storage in_addr;
205 size_t ss_size = espaddr_to_inet(ip, htons(port), ip_protocol, &in_addr);
206 if (!ss_size) {
207 ESP_LOGE(TAG, "espaddr_to_inet() failed: Mismatch of IP protocols");
208 return 0;
209 }
210 ESP_LOGD(TAG, "[sock=%d]: Sending to IP %s port %d", sock, get_string_address(&in_addr), port);
211 ssize_t actual_len = sendto(sock, data, len, 0, (struct sockaddr *)&in_addr, ss_size);
212 if (actual_len < 0) {
213 ESP_LOGE(TAG, "[sock=%d]: _mdns_udp_pcb_write sendto() has failed\n error=%d: %s", sock, errno, strerror(errno));
214 }
215 return actual_len;
216 }
217
inet_to_espaddr(const struct sockaddr_storage * in_addr,esp_ip_addr_t * addr,uint16_t * port)218 static inline void inet_to_espaddr(const struct sockaddr_storage *in_addr, esp_ip_addr_t *addr, uint16_t *port)
219 {
220 if (in_addr->ss_family == PF_INET) {
221 struct sockaddr_in * in_addr_ip4 = (struct sockaddr_in *)in_addr;
222 memset(addr, 0, sizeof(esp_ip_addr_t));
223 *port = in_addr_ip4->sin_port;
224 addr->u_addr.ip4.addr = in_addr_ip4->sin_addr.s_addr;
225 addr->type = ESP_IPADDR_TYPE_V4;
226 }
227 #if CONFIG_LWIP_IPV6
228 else if (in_addr->ss_family == PF_INET6) {
229 struct sockaddr_in6 * in_addr_ip6 = (struct sockaddr_in6 *)in_addr;
230 memset(addr, 0, sizeof(esp_ip_addr_t));
231 *port = in_addr_ip6->sin6_port;
232 uint32_t *u32_addr = in_addr_ip6->sin6_addr.s6_addr32;
233 if (u32_addr[0] == 0 && u32_addr[1] == 0 && u32_addr[2] == esp_netif_htonl(0x0000FFFFUL)) {
234 // Mapped IPv4 address, convert directly to IPv4
235 addr->type = ESP_IPADDR_TYPE_V4;
236 addr->u_addr.ip4.addr = u32_addr[3];
237 } else {
238 addr->type = ESP_IPADDR_TYPE_V6;
239 addr->u_addr.ip6.addr[0] = u32_addr[0];
240 addr->u_addr.ip6.addr[1] = u32_addr[1];
241 addr->u_addr.ip6.addr[2] = u32_addr[2];
242 addr->u_addr.ip6.addr[3] = u32_addr[3];
243 }
244 }
245 #endif // CONFIG_LWIP_IPV6
246 }
247
sock_recv_task(void * arg)248 void sock_recv_task(void* arg)
249 {
250 while (s_run_sock_recv_task) {
251 struct timeval tv = {
252 .tv_sec = 1,
253 .tv_usec = 0,
254 };
255 fd_set rfds;
256 FD_ZERO(&rfds);
257 int max_sock = -1;
258 for (int i=0; i<MDNS_IF_MAX; i++) {
259 for (int j=0; j<MDNS_IP_PROTOCOL_MAX; j++) {
260 int sock = pcb_to_sock(_mdns_server->interfaces[i].pcbs[j].pcb);
261 if (sock >= 0) {
262 FD_SET(sock, &rfds);
263 max_sock = MAX(max_sock, sock);
264 }
265 }
266 }
267 if (max_sock < 0) {
268 vTaskDelay(pdMS_TO_TICKS(1000));
269 ESP_LOGI(TAG, "No sock!");
270 continue;
271 }
272
273 int s = select(max_sock + 1, &rfds, NULL, NULL, &tv);
274 if (s < 0) {
275 ESP_LOGE(TAG, "Select failed: errno %d", errno);
276 break;
277 } else if (s > 0) {
278 for (int tcpip_if=0; tcpip_if<MDNS_IF_MAX; tcpip_if++) {
279 // Both protocols share once socket
280 int sock = pcb_to_sock(_mdns_server->interfaces[tcpip_if].pcbs[MDNS_IP_PROTOCOL_V4].pcb);
281 if (sock < 0) {
282 sock = pcb_to_sock(_mdns_server->interfaces[tcpip_if].pcbs[MDNS_IP_PROTOCOL_V6].pcb);
283 }
284 if (sock < 0) {
285 continue;
286 }
287 if (FD_ISSET(sock, &rfds)) {
288 static char recvbuf[MDNS_MAX_PACKET_SIZE];
289 uint16_t port = 0;
290
291 struct sockaddr_storage raddr; // Large enough for both IPv4 or IPv6
292 socklen_t socklen = sizeof(struct sockaddr_storage);
293 esp_ip_addr_t addr = {0};
294 int len = recvfrom(sock, recvbuf, sizeof(recvbuf), 0,
295 (struct sockaddr *) &raddr, &socklen);
296 if (len < 0) {
297 ESP_LOGE(TAG, "multicast recvfrom failed: errno %d", errno);
298 break;
299 }
300 ESP_LOGD(TAG, "[sock=%d]: Received from IP:%s", sock, get_string_address(&raddr));
301 ESP_LOG_BUFFER_HEXDUMP(TAG, recvbuf, len, ESP_LOG_VERBOSE);
302 inet_to_espaddr(&raddr, &addr, &port);
303
304 // Allocate the packet structure and pass it to the mdns main engine
305 mdns_rx_packet_t *packet = (mdns_rx_packet_t *) calloc(1, sizeof(mdns_rx_packet_t));
306 struct pbuf *packet_pbuf = calloc(1, sizeof(struct pbuf));
307 uint8_t *buf = malloc(len);
308 if (packet == NULL || packet_pbuf == NULL || buf == NULL ) {
309 free(buf);
310 free(packet_pbuf);
311 free(packet);
312 HOOK_MALLOC_FAILED;
313 ESP_LOGE(TAG, "Failed to allocate the mdns packet");
314 continue;
315 }
316 memcpy(buf, recvbuf, len);
317 packet_pbuf->next = NULL;
318 packet_pbuf->payload = buf;
319 packet_pbuf->tot_len = len;
320 packet_pbuf->len = len;
321 packet->tcpip_if = tcpip_if;
322 packet->pb = packet_pbuf;
323 packet->src_port = ntohs(port);
324 memcpy(&packet->src, &addr, sizeof(esp_ip_addr_t));
325 // TODO(IDF-3651): Add the correct dest addr -- for mdns to decide multicast/unicast
326 // Currently it's enough to assume the packet is multicast and mdns to check the source port of the packet
327 memset(&packet->dest, 0, sizeof(esp_ip_addr_t));
328 packet->multicast = 1;
329 packet->dest.type = packet->src.type;
330 packet->ip_protocol =
331 packet->src.type == ESP_IPADDR_TYPE_V4 ? MDNS_IP_PROTOCOL_V4 : MDNS_IP_PROTOCOL_V6;
332 if (!_mdns_server || !_mdns_server->action_queue || _mdns_send_rx_action(packet) != ESP_OK) {
333 ESP_LOGE(TAG, "_mdns_send_rx_action failed!");
334 free(packet->pb->payload);
335 free(packet->pb);
336 free(packet);
337 }
338 }
339 }
340 }
341 }
342 vTaskDelete(NULL);
343 }
344
mdns_networking_init(void)345 static void mdns_networking_init(void)
346 {
347 if (s_run_sock_recv_task == false) {
348 s_run_sock_recv_task = true;
349 xTaskCreate( sock_recv_task, "mdns recv task", 3*1024, NULL, 5, NULL );
350 }
351 }
352
create_pcb(mdns_if_t tcpip_if,mdns_ip_protocol_t ip_protocol)353 static struct udp_pcb* create_pcb(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
354 {
355 if (_mdns_server->interfaces[tcpip_if].pcbs[ip_protocol].pcb) {
356 return _mdns_server->interfaces[tcpip_if].pcbs[ip_protocol].pcb;
357 }
358 mdns_ip_protocol_t other_ip_proto = ip_protocol==MDNS_IP_PROTOCOL_V4?MDNS_IP_PROTOCOL_V6:MDNS_IP_PROTOCOL_V4;
359 esp_netif_t *netif = _mdns_get_esp_netif(tcpip_if);
360 if (_mdns_server->interfaces[tcpip_if].pcbs[other_ip_proto].pcb) {
361 struct udp_pcb* other_pcb = _mdns_server->interfaces[tcpip_if].pcbs[other_ip_proto].pcb;
362 int err = join_mdns_multicast_group(pcb_to_sock(other_pcb), netif, ip_protocol);
363 if (err < 0) {
364 ESP_LOGE(TAG, "Failed to add ipv6 multicast group for protocol %d", ip_protocol);
365 return NULL;
366 }
367 return other_pcb;
368 }
369 int sock = create_socket(netif);
370 if (sock < 0) {
371 ESP_LOGE(TAG, "Failed to create the socket!");
372 return NULL;
373 }
374 int err = join_mdns_multicast_group(sock, netif, ip_protocol);
375 if (err < 0) {
376 ESP_LOGE(TAG, "Failed to add ipv6 multicast group for protocol %d", ip_protocol);
377 }
378 return sock_to_pcb(sock);
379 }
380
_mdns_pcb_init(mdns_if_t tcpip_if,mdns_ip_protocol_t ip_protocol)381 esp_err_t _mdns_pcb_init(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
382 {
383 ESP_LOGI(TAG, "_mdns_pcb_init(tcpip_if=%d, ip_protocol=%d)", tcpip_if, ip_protocol);
384 _mdns_server->interfaces[tcpip_if].pcbs[ip_protocol].pcb = create_pcb(tcpip_if, ip_protocol);
385 _mdns_server->interfaces[tcpip_if].pcbs[ip_protocol].failed_probes = 0;
386
387 mdns_networking_init();
388 return ESP_OK;
389 }
390
create_socket(esp_netif_t * netif)391 static int create_socket(esp_netif_t *netif)
392 {
393 #if CONFIG_LWIP_IPV6
394 int sock = socket(PF_INET6, SOCK_DGRAM, 0);
395 #else
396 int sock = socket(PF_INET, SOCK_DGRAM, 0);
397 #endif
398 if (sock < 0) {
399 ESP_LOGE(TAG, "Failed to create socket. Error %d", errno);
400 return -1;
401 }
402
403 int on = 1;
404 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on) ) < 0) {
405 ESP_LOGE(TAG, "setsockopt SO_REUSEADDR: %s\n", strerror(errno));
406 }
407 // Bind the socket to any address
408 #if CONFIG_LWIP_IPV6
409 struct sockaddr_in6 saddr = { INADDR_ANY };
410 saddr.sin6_family = AF_INET6;
411 saddr.sin6_port = htons(5353);
412 bzero(&saddr.sin6_addr.s6_addr, sizeof(saddr.sin6_addr.s6_addr));
413 int err = bind(sock, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in6));
414 if (err < 0) {
415 ESP_LOGE(TAG, "Failed to bind socket. Error %d", errno);
416 goto err;
417 }
418 #else
419 struct sockaddr_in saddr = { 0 };
420 saddr.sin_family = AF_INET;
421 saddr.sin_port = htons(5353);
422 bzero(&saddr.sin_addr.s_addr, sizeof(saddr.sin_addr.s_addr));
423 int err = bind(sock, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in));
424 if (err < 0) {
425 ESP_LOGE(TAG, "Failed to bind socket. Error %d", errno);
426 goto err;
427 }
428 #endif // CONFIG_LWIP_IPV6
429 struct ifreq ifr;
430 esp_netif_get_netif_impl_name(netif, ifr.ifr_name);
431 int ret = setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, (void*)&ifr, sizeof(struct ifreq));
432 if (ret < 0) {
433 ESP_LOGE(TAG, "\"%s\" Unable to bind socket to specified interface: errno %d", esp_netif_get_desc(netif), errno);
434 goto err;
435 }
436
437 return sock;
438
439 err:
440 close(sock);
441 return -1;
442 }
443
444 #if CONFIG_LWIP_IPV6
socket_add_ipv6_multicast_group(int sock,esp_netif_t * netif)445 static int socket_add_ipv6_multicast_group(int sock, esp_netif_t *netif)
446 {
447 int ifindex = esp_netif_get_netif_impl_index(netif);
448 int err = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifindex, sizeof(ifindex));
449 if (err < 0) {
450 ESP_LOGE(TAG, "Failed to set IPV6_MULTICAST_IF. Error %d", errno);
451 return err;
452 }
453
454 struct ipv6_mreq v6imreq = { 0 };
455 esp_ip_addr_t multi_addr = ESP_IP6ADDR_INIT(0x000002ff, 0, 0, 0xfb000000);
456 memcpy(&v6imreq.ipv6mr_multiaddr, &multi_addr.u_addr.ip6.addr, sizeof(v6imreq.ipv6mr_multiaddr));
457 v6imreq.ipv6mr_interface = ifindex;
458 err = setsockopt(sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &v6imreq, sizeof(struct ipv6_mreq));
459 if (err < 0) {
460 ESP_LOGE(TAG, "Failed to set IPV6_ADD_MEMBERSHIP. Error %d", errno);
461 return err;
462 }
463 return err;
464 }
465 #endif // CONFIG_LWIP_IPV6
466
socket_add_ipv4_multicast_group(int sock,esp_netif_t * netif)467 static int socket_add_ipv4_multicast_group(int sock, esp_netif_t *netif)
468 {
469 struct ip_mreq imreq = { 0 };
470 int err = 0;
471 esp_netif_ip_info_t ip_info = { 0 };
472
473 if (esp_netif_get_ip_info(netif, &ip_info) != ESP_OK) {
474 ESP_LOGE(TAG, "Failed to esp_netif_get_ip_info()");
475 goto err;
476 }
477 imreq.imr_interface.s_addr = ip_info.ip.addr;
478
479 esp_ip_addr_t multicast_addr = ESP_IP4ADDR_INIT(224, 0, 0, 251);
480 imreq.imr_multiaddr.s_addr = multicast_addr.u_addr.ip4.addr;
481
482 err = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imreq, sizeof(struct ip_mreq));
483 if (err < 0) {
484 ESP_LOGE(TAG, "%d %s", sock, strerror(errno));
485 ESP_LOGE(TAG, "Failed to set IP_ADD_MEMBERSHIP. Error %d", errno);
486 goto err;
487 }
488
489 err:
490 return err;
491 }
492
join_mdns_multicast_group(int sock,esp_netif_t * netif,mdns_ip_protocol_t ip_protocol)493 static int join_mdns_multicast_group(int sock, esp_netif_t *netif, mdns_ip_protocol_t ip_protocol)
494 {
495 if (ip_protocol == MDNS_IP_PROTOCOL_V4) {
496 return socket_add_ipv4_multicast_group(sock, netif);
497 }
498 #if CONFIG_LWIP_IPV6
499 if (ip_protocol == MDNS_IP_PROTOCOL_V6) {
500 return socket_add_ipv6_multicast_group(sock, netif);
501 }
502 #endif // CONFIG_LWIP_IPV6
503 return -1;
504 }
505