| /* |
| * Copyright (c) 2020 Friedt Professional Engineering Services, Inc |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <errno.h> |
| #include <net/dns_sd.h> |
| #include <net/socket.h> |
| #include <posix/netinet/in.h> |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <zephyr.h> |
| |
| #include <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); |
| } |
| |
| /* 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; |
| int server_fd; |
| int client_fd; |
| socklen_t len; |
| void *addrp; |
| uint16_t *portp; |
| struct sockaddr client_addr; |
| char addrstr[INET6_ADDRSTRLEN]; |
| uint8_t line[64]; |
| |
| static struct sockaddr 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(&server_addr)->sin6_family = AF_INET6; |
| net_sin6(&server_addr)->sin6_addr = in6addr_any; |
| net_sin6(&server_addr)->sin6_port = sys_cpu_to_be16(DEFAULT_PORT); |
| } else if (IS_ENABLED(CONFIG_NET_IPV4)) { |
| net_sin(&server_addr)->sin_family = AF_INET; |
| net_sin(&server_addr)->sin_addr.s_addr = htonl(INADDR_ANY); |
| net_sin(&server_addr)->sin_port = sys_cpu_to_be16(DEFAULT_PORT); |
| } else { |
| __ASSERT(false, "Neither IPv6 nor IPv4 are enabled"); |
| } |
| |
| r = socket(server_addr.sa_family, SOCK_STREAM, 0); |
| if (r == -1) { |
| NET_DBG("socket() failed (%d)", errno); |
| return; |
| } |
| |
| server_fd = r; |
| NET_DBG("server_fd is %d", server_fd); |
| |
| r = bind(server_fd, &server_addr, sizeof(server_addr)); |
| if (r == -1) { |
| NET_DBG("bind() failed (%d)", errno); |
| close(server_fd); |
| return; |
| } |
| |
| 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)); |
| NET_DBG("bound to [%s]:%u", |
| log_strdup(addrstr), ntohs(*portp)); |
| |
| r = listen(server_fd, 1); |
| if (r == -1) { |
| NET_DBG("listen() failed (%d)", errno); |
| close(server_fd); |
| return; |
| } |
| |
| for (;;) { |
| len = sizeof(client_addr); |
| r = accept(server_fd, (struct sockaddr *)&client_addr, &len); |
| if (r == -1) { |
| NET_DBG("accept() failed (%d)", errno); |
| continue; |
| } |
| |
| client_fd = r; |
| |
| inet_ntop(server_addr.sa_family, addrp, addrstr, sizeof(addrstr)); |
| NET_DBG("accepted connection from [%s]:%u", |
| log_strdup(addrstr), ntohs(*portp)); |
| |
| /* send a banner */ |
| r = welcome(client_fd); |
| if (r == -1) { |
| NET_DBG("send() failed (%d)", errno); |
| close(client_fd); |
| return; |
| } |
| |
| for (;;) { |
| /* echo 1 line at a time */ |
| r = recv(client_fd, line, sizeof(line), 0); |
| if (r == -1) { |
| NET_DBG("recv() failed (%d)", errno); |
| close(client_fd); |
| break; |
| } |
| len = r; |
| |
| r = send(client_fd, line, len, 0); |
| if (r == -1) { |
| NET_DBG("send() failed (%d)", errno); |
| close(client_fd); |
| break; |
| } |
| } |
| } |
| } |