| /* |
| * Copyright (c) 2020 Friedt Professional Engineering Services, Inc |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <errno.h> |
| #include <zephyr/net/dns_sd.h> |
| #include <zephyr/net/socket.h> |
| #include <zephyr/posix/netinet/in.h> |
| #include <zephyr/posix/sys/socket.h> |
| #include <zephyr/posix/unistd.h> |
| #include <zephyr/posix/arpa/inet.h> |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <zephyr/kernel.h> |
| |
| #include <zephyr/logging/log.h> |
| LOG_MODULE_REGISTER(mdns_echo_service, LOG_LEVEL_DBG); |
| |
| /* A default port of 0 causes bind(2) to request an ephemeral port */ |
| #define DEFAULT_PORT 0 |
| |
| static int welcome(int fd) |
| { |
| static const char msg[] = "Bonjour, Zephyr world!\n"; |
| |
| return send(fd, msg, sizeof(msg), 0); |
| } |
| |
| static int echo_service(const struct sockaddr *server_addr) |
| { |
| int r; |
| int server_fd; |
| int client_fd; |
| socklen_t len; |
| void *addrp; |
| uint16_t *portp; |
| struct sockaddr_storage client_addr; |
| char addrstr[INET6_ADDRSTRLEN]; |
| uint8_t line[64]; |
| |
| r = socket(server_addr->sa_family, SOCK_STREAM, 0); |
| if (r == -1) { |
| r = -errno; |
| LOG_DBG("socket() failed (%d)", r); |
| return r; |
| } |
| |
| server_fd = r; |
| LOG_DBG("server_fd is %d", server_fd); |
| |
| r = setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int)); |
| if (r == -1) { |
| r = -errno; |
| LOG_ERR("setsockopt() failed (%d)", r); |
| close(server_fd); |
| return r; |
| } |
| |
| r = bind(server_fd, server_addr, sizeof(*server_addr)); |
| if (r == -1) { |
| r = -errno; |
| LOG_DBG("bind() failed (%d)", r); |
| close(server_fd); |
| return r; |
| } |
| |
| if (server_addr->sa_family == AF_INET6) { |
| addrp = &net_sin6((server_addr))->sin6_addr; |
| portp = &net_sin6(server_addr)->sin6_port; |
| } else { |
| addrp = &net_sin(server_addr)->sin_addr; |
| portp = &net_sin(server_addr)->sin_port; |
| } |
| |
| inet_ntop(server_addr->sa_family, addrp, addrstr, sizeof(addrstr)); |
| LOG_DBG("bound to [%s]:%u", addrstr, ntohs(*portp)); |
| |
| r = listen(server_fd, 1); |
| if (r == -1) { |
| r = -errno; |
| LOG_DBG("listen() failed (%d)", r); |
| close(server_fd); |
| return r; |
| } |
| |
| for (;;) { |
| len = sizeof(client_addr); |
| r = accept(server_fd, (struct sockaddr *)&client_addr, &len); |
| if (r == -1) { |
| LOG_DBG("accept() failed (%d)", errno); |
| break; |
| } |
| |
| client_fd = r; |
| |
| inet_ntop(server_addr->sa_family, addrp, addrstr, sizeof(addrstr)); |
| LOG_DBG("accepted connection from [%s]:%u", addrstr, ntohs(*portp)); |
| |
| /* send a banner */ |
| r = welcome(client_fd); |
| if (r == -1) { |
| LOG_DBG("send() failed (%d)", errno); |
| close(client_fd); |
| continue; |
| } |
| |
| for (;;) { |
| /* echo 1 line at a time */ |
| r = recv(client_fd, line, sizeof(line), 0); |
| if (r == -1) { |
| LOG_DBG("recv() failed (%d)", errno); |
| close(client_fd); |
| break; |
| } |
| |
| if (r == 0) { |
| LOG_DBG("connection closed by peer"); |
| close(client_fd); |
| break; |
| } |
| |
| len = r; |
| |
| r = send(client_fd, line, len, 0); |
| if (r == -1) { |
| LOG_DBG("send() failed (%d)", errno); |
| close(client_fd); |
| break; |
| } |
| } |
| } |
| |
| close(server_fd); |
| |
| return 0; |
| } |
| |
| /* This is mainly here to bind to a port to get service advertisement |
| * to work.. but since we're already here we might as well do something |
| * useful. |
| */ |
| void service(void) |
| { |
| int r = 0; |
| |
| static struct sockaddr_storage server_addr; |
| |
| #if DEFAULT_PORT == 0 |
| /* The advanced use case: ephemeral port */ |
| #if defined(CONFIG_NET_IPV6) |
| DNS_SD_REGISTER_SERVICE(zephyr, CONFIG_NET_HOSTNAME, |
| "_zephyr", "_tcp", "local", DNS_SD_EMPTY_TXT, |
| &((struct sockaddr_in6 *)&server_addr)->sin6_port); |
| #elif defined(CONFIG_NET_IPV4) |
| DNS_SD_REGISTER_SERVICE(zephyr, CONFIG_NET_HOSTNAME, |
| "_zephyr", "_tcp", "local", DNS_SD_EMPTY_TXT, |
| &((struct sockaddr_in *)&server_addr)->sin_port); |
| #endif |
| #else |
| /* The simple use case: fixed port */ |
| DNS_SD_REGISTER_TCP_SERVICE(zephyr, CONFIG_NET_HOSTNAME, |
| "_zephyr", "local", DNS_SD_EMPTY_TXT, DEFAULT_PORT); |
| #endif |
| |
| if (IS_ENABLED(CONFIG_NET_IPV6)) { |
| net_sin6(net_sad(&server_addr))->sin6_family = AF_INET6; |
| net_sin6(net_sad(&server_addr))->sin6_addr = in6addr_any; |
| net_sin6(net_sad(&server_addr))->sin6_port = sys_cpu_to_be16(DEFAULT_PORT); |
| } else if (IS_ENABLED(CONFIG_NET_IPV4)) { |
| net_sin(net_sad(&server_addr))->sin_family = AF_INET; |
| net_sin(net_sad(&server_addr))->sin_addr.s_addr = htonl(INADDR_ANY); |
| net_sin(net_sad(&server_addr))->sin_port = sys_cpu_to_be16(DEFAULT_PORT); |
| } else { |
| __ASSERT(false, "Neither IPv6 nor IPv4 are enabled"); |
| } |
| |
| while (r == 0) { |
| r = echo_service((struct sockaddr *)&server_addr); |
| if (r < 0) { |
| LOG_ERR("Fatal echo server error, %d", r); |
| } |
| } |
| } |