1 /*
2  * Copyright (c) 2020 Friedt Professional Engineering Services, Inc
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <errno.h>
8 #include <net/dns_sd.h>
9 #include <net/socket.h>
10 #include <posix/netinet/in.h>
11 #include <stdint.h>
12 #include <stdio.h>
13 #include <string.h>
14 #include <zephyr.h>
15 
16 #include <logging/log.h>
17 LOG_MODULE_REGISTER(mdns_echo_service, LOG_LEVEL_DBG);
18 
19 /* A default port of 0 causes bind(2) to request an ephemeral port */
20 #define DEFAULT_PORT 0
21 
welcome(int fd)22 static int welcome(int fd)
23 {
24 	static const char msg[] = "Bonjour, Zephyr world!\n";
25 
26 	return send(fd, msg, sizeof(msg), 0);
27 }
28 
29 /* This is mainly here to bind to a port to get service advertisement
30  * to work.. but since we're already here we might as well do something
31  * useful.
32  */
service(void)33 void service(void)
34 {
35 	int r;
36 	int server_fd;
37 	int client_fd;
38 	socklen_t len;
39 	void *addrp;
40 	uint16_t *portp;
41 	struct sockaddr client_addr;
42 	char addrstr[INET6_ADDRSTRLEN];
43 	uint8_t line[64];
44 
45 	static struct sockaddr server_addr;
46 
47 #if DEFAULT_PORT == 0
48 	/* The advanced use case: ephemeral port */
49 #if defined(CONFIG_NET_IPV6)
50 	DNS_SD_REGISTER_SERVICE(zephyr, CONFIG_NET_HOSTNAME,
51 				"_zephyr", "_tcp", "local", DNS_SD_EMPTY_TXT,
52 				&((struct sockaddr_in6 *)&server_addr)->sin6_port);
53 #elif defined(CONFIG_NET_IPV4)
54 	DNS_SD_REGISTER_SERVICE(zephyr, CONFIG_NET_HOSTNAME,
55 				"_zephyr", "_tcp", "local", DNS_SD_EMPTY_TXT,
56 				&((struct sockaddr_in *)&server_addr)->sin_port);
57 #endif
58 #else
59 	/* The simple use case: fixed port */
60 	DNS_SD_REGISTER_TCP_SERVICE(zephyr, CONFIG_NET_HOSTNAME,
61 				    "_zephyr", "local", DNS_SD_EMPTY_TXT, DEFAULT_PORT);
62 #endif
63 
64 	if (IS_ENABLED(CONFIG_NET_IPV6)) {
65 		net_sin6(&server_addr)->sin6_family = AF_INET6;
66 		net_sin6(&server_addr)->sin6_addr = in6addr_any;
67 		net_sin6(&server_addr)->sin6_port = sys_cpu_to_be16(DEFAULT_PORT);
68 	} else if (IS_ENABLED(CONFIG_NET_IPV4)) {
69 		net_sin(&server_addr)->sin_family = AF_INET;
70 		net_sin(&server_addr)->sin_addr.s_addr = htonl(INADDR_ANY);
71 		net_sin(&server_addr)->sin_port = sys_cpu_to_be16(DEFAULT_PORT);
72 	} else {
73 		__ASSERT(false, "Neither IPv6 nor IPv4 are enabled");
74 	}
75 
76 	r = socket(server_addr.sa_family, SOCK_STREAM, 0);
77 	if (r == -1) {
78 		NET_DBG("socket() failed (%d)", errno);
79 		return;
80 	}
81 
82 	server_fd = r;
83 	NET_DBG("server_fd is %d", server_fd);
84 
85 	r = bind(server_fd, &server_addr, sizeof(server_addr));
86 	if (r == -1) {
87 		NET_DBG("bind() failed (%d)", errno);
88 		close(server_fd);
89 		return;
90 	}
91 
92 	if (server_addr.sa_family == AF_INET6) {
93 		addrp = &net_sin6(&server_addr)->sin6_addr;
94 		portp = &net_sin6(&server_addr)->sin6_port;
95 	} else {
96 		addrp = &net_sin(&server_addr)->sin_addr;
97 		portp = &net_sin(&server_addr)->sin_port;
98 	}
99 
100 	inet_ntop(server_addr.sa_family, addrp, addrstr, sizeof(addrstr));
101 	NET_DBG("bound to [%s]:%u",
102 		log_strdup(addrstr), ntohs(*portp));
103 
104 	r = listen(server_fd, 1);
105 	if (r == -1) {
106 		NET_DBG("listen() failed (%d)", errno);
107 		close(server_fd);
108 		return;
109 	}
110 
111 	for (;;) {
112 		len = sizeof(client_addr);
113 		r = accept(server_fd, (struct sockaddr *)&client_addr, &len);
114 		if (r == -1) {
115 			NET_DBG("accept() failed (%d)", errno);
116 			continue;
117 		}
118 
119 		client_fd = r;
120 
121 		inet_ntop(server_addr.sa_family, addrp, addrstr, sizeof(addrstr));
122 		NET_DBG("accepted connection from [%s]:%u",
123 			log_strdup(addrstr), ntohs(*portp));
124 
125 		/* send a banner */
126 		r = welcome(client_fd);
127 		if (r == -1) {
128 			NET_DBG("send() failed (%d)", errno);
129 			close(client_fd);
130 			return;
131 		}
132 
133 		for (;;) {
134 			/* echo 1 line at a time */
135 			r = recv(client_fd, line, sizeof(line), 0);
136 			if (r == -1) {
137 				NET_DBG("recv() failed (%d)", errno);
138 				close(client_fd);
139 				break;
140 			}
141 			len = r;
142 
143 			r = send(client_fd, line, len, 0);
144 			if (r == -1) {
145 				NET_DBG("send() failed (%d)", errno);
146 				close(client_fd);
147 				break;
148 			}
149 		}
150 	}
151 }
152