1 /**
2 * Copyright 2017 Florian Forster
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 * SOFTWARE.
21 *
22 * Authors:
23 * Florian octo Forster <octo at collectd.org>
24 **/
25
26 #ifdef WIN32
27 #include "gnulib_config.h"
28 #endif
29
30 #include "config.h"
31
32 #if !defined(__GNUC__) || !__GNUC__
33 #define __attribute__(x) /**/
34 #endif
35
36 #include "collectd/lcc_features.h"
37 #include "collectd/network_parse.h" /* for lcc_network_parse_options_t */
38 #include "collectd/server.h"
39
40 // clang-format off
41 #include <errno.h>
42 #include <stdbool.h>
43 #include <string.h>
44 #include <unistd.h>
45 #include <sys/socket.h>
46 #include <sys/types.h>
47 #include <net/if.h>
48 #include <netdb.h>
49 #include <netinet/in.h>
50 // clang-format on
51
52 #include <stdio.h>
53 #define DEBUG(...) printf(__VA_ARGS__)
54
55 #ifdef WIN32
56 #include <ws2tcpip.h>
57 #define AI_ADDRCONFIG 0
58 #endif
59
is_multicast(struct addrinfo const * ai)60 static bool is_multicast(struct addrinfo const *ai) {
61 if (ai->ai_family == AF_INET) {
62 struct sockaddr_in *addr = (struct sockaddr_in *)ai->ai_addr;
63 return IN_MULTICAST(ntohl(addr->sin_addr.s_addr));
64 } else if (ai->ai_family == AF_INET6) {
65 struct sockaddr_in6 *addr = (struct sockaddr_in6 *)ai->ai_addr;
66 return IN6_IS_ADDR_MULTICAST(&addr->sin6_addr);
67 }
68 return 0;
69 }
70
server_multicast_join(lcc_listener_t * srv,struct sockaddr_storage * group,int loop_back,int ttl)71 static int server_multicast_join(lcc_listener_t *srv,
72 struct sockaddr_storage *group, int loop_back,
73 int ttl) {
74 if (group->ss_family == AF_INET) {
75 struct sockaddr_in *sa = (struct sockaddr_in *)group;
76
77 int status = setsockopt(srv->conn, IPPROTO_IP, IP_MULTICAST_LOOP,
78 &loop_back, sizeof(loop_back));
79 if (status == -1) {
80 DEBUG("setsockopt(IP_MULTICAST_LOOP, %d) = %d\n", loop_back, errno);
81 return errno;
82 }
83
84 status =
85 setsockopt(srv->conn, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl));
86 if (status == -1)
87 return errno;
88
89 #if HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
90 struct ip_mreqn mreq = {
91 .imr_address.s_addr = INADDR_ANY,
92 .imr_multiaddr.s_addr = sa->sin_addr.s_addr,
93 .imr_ifindex = if_nametoindex(srv->iface),
94 };
95 #else
96 #ifdef WIN32
97 struct ip_mreq mreq = {
98 .imr_interface.s_addr = INADDR_ANY,
99 .imr_multiaddr.s_addr = sa->sin_addr.s_addr,
100 };
101 #else
102 struct ip_mreq mreq = {
103 .imr_multiaddr.s_addr = sa->sin_addr.s_addr,
104 };
105 #endif /* WIN32 */
106 #endif /* HAVE_STRUCT_IP_MREQN_IMR_IFINDEX */
107 status = setsockopt(srv->conn, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq,
108 sizeof(mreq));
109 if (status == -1)
110 return errno;
111 } else if (group->ss_family == AF_INET6) {
112 struct sockaddr_in6 *sa = (struct sockaddr_in6 *)group;
113
114 int status = setsockopt(srv->conn, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
115 &loop_back, sizeof(loop_back));
116 if (status == -1)
117 return errno;
118
119 status = setsockopt(srv->conn, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl,
120 sizeof(ttl));
121 if (status == -1)
122 return errno;
123
124 struct ipv6_mreq mreq6 = {
125 .ipv6mr_interface = if_nametoindex(srv->iface),
126 };
127 memmove(&mreq6.ipv6mr_multiaddr, &sa->sin6_addr, sizeof(struct in6_addr));
128
129 status = setsockopt(srv->conn, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq6,
130 sizeof(mreq6));
131 if (status == -1)
132 return errno;
133 } else {
134 return EINVAL;
135 }
136
137 return 0;
138 }
139
server_bind_socket(lcc_listener_t * srv,struct addrinfo const * ai)140 static int server_bind_socket(lcc_listener_t *srv, struct addrinfo const *ai) {
141 /* allow multiple sockets to use the same PORT number */
142 if (setsockopt(srv->conn, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int)) ==
143 -1) {
144 return errno;
145 }
146
147 if (bind(srv->conn, ai->ai_addr, ai->ai_addrlen) == -1) {
148 return -1;
149 }
150
151 if (is_multicast(ai)) {
152 int status = server_multicast_join(srv, (void *)ai->ai_addr, /* loop = */ 1,
153 /* ttl = */ 16);
154 if (status != 0)
155 return status;
156 }
157
158 return 0;
159 }
160
server_open(lcc_listener_t * srv)161 static int server_open(lcc_listener_t *srv) {
162 struct addrinfo *res = NULL;
163 int status = getaddrinfo(srv->node ? srv->node : "::",
164 srv->service ? srv->service : LCC_DEFAULT_PORT,
165 &(struct addrinfo){
166 .ai_flags = AI_ADDRCONFIG,
167 .ai_family = AF_UNSPEC,
168 .ai_socktype = SOCK_DGRAM,
169 },
170 &res);
171 if (status != 0)
172 return status;
173
174 for (struct addrinfo *ai = res; ai != NULL; ai = ai->ai_next) {
175 srv->conn = socket(ai->ai_family, ai->ai_socktype, 0);
176 if (srv->conn == -1)
177 continue;
178
179 status = server_bind_socket(srv, ai);
180 if (status != 0) {
181 close(srv->conn);
182 srv->conn = -1;
183 continue;
184 }
185
186 break;
187 }
188
189 freeaddrinfo(res);
190
191 if (srv->conn >= 0)
192 return 0;
193 return status != 0 ? status : -1;
194 }
195
lcc_listen_and_write(lcc_listener_t srv)196 int lcc_listen_and_write(lcc_listener_t srv) {
197 bool close_socket = 0;
198
199 if (srv.conn < 0) {
200 int status = server_open(&srv);
201 if (status != 0)
202 return status;
203 close_socket = 1;
204 }
205
206 if (srv.buffer_size == 0)
207 srv.buffer_size = LCC_NETWORK_BUFFER_SIZE;
208
209 if (srv.parser == NULL)
210 srv.parser = lcc_network_parse;
211
212 int ret = 0;
213 while (42) {
214 char buffer[srv.buffer_size];
215 ssize_t len = recv(srv.conn, buffer, sizeof(buffer), /* flags = */ 0);
216 if (len == -1) {
217 ret = errno;
218 break;
219 } else if (len == 0) {
220 break;
221 }
222
223 (void)srv.parser(buffer, (size_t)len, srv.parse_options);
224 }
225
226 if (close_socket) {
227 close(srv.conn);
228 srv.conn = -1;
229 }
230
231 return ret;
232 }
233