blob: 46a7675084f906d5b101238fc310531e6b0d78df [file]
/*
* 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);
}
}
}