1 /**
2 * collectd - src/libcollectdclient/network.c
3 * Copyright (C) 2005-2015 Florian Forster
4 * Copyright (C) 2010 Max Henkel
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *
24 * Authors:
25 * Florian Forster <octo at collectd.org>
26 * Max Henkel <henkel at gmx.at>
27 **/
29 #include "collectd.h"
31 #include <assert.h>
32 #include <errno.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <unistd.h>
38 #include <netdb.h>
39 #include <sys/socket.h>
40 #include <sys/types.h>
42 #if HAVE_NETINET_IN_H
43 #include <netinet/in.h>
44 #endif
46 #if HAVE_NET_IF_H
47 #include <net/if.h>
48 #endif
50 #include "collectd/network.h"
51 #include "collectd/network_buffer.h"
53 /*
54 * Private data types
55 */
56 struct lcc_network_s {
57 lcc_server_t *servers;
58 };
60 struct lcc_server_s {
61 char *node;
62 char *service;
64 int ttl;
65 lcc_security_level_t security_level;
66 char *username;
67 char *password;
69 int fd;
70 struct sockaddr *sa;
71 socklen_t sa_len;
73 lcc_network_buffer_t *buffer;
75 lcc_server_t *next;
76 };
78 /*
79 * Private functions
80 */
81 static int server_close_socket(lcc_server_t *srv) /* {{{ */
82 {
83 if (srv == NULL)
84 return (EINVAL);
86 if (srv->fd < 0)
87 return (0);
89 close(srv->fd);
90 srv->fd = -1;
91 free(srv->sa);
92 srv->sa = NULL;
93 srv->sa_len = 0;
95 return (0);
96 } /* }}} int server_close_socket */
98 static void int_server_destroy(lcc_server_t *srv) /* {{{ */
99 {
100 lcc_server_t *next;
102 if (srv == NULL)
103 return;
105 server_close_socket(srv);
107 next = srv->next;
109 free(srv->node);
110 free(srv->service);
111 free(srv->username);
112 free(srv->password);
113 free(srv);
115 int_server_destroy(next);
116 } /* }}} void int_server_destroy */
118 static int server_open_socket(lcc_server_t *srv) /* {{{ */
119 {
120 struct addrinfo *ai_list;
121 int status;
123 if (srv == NULL)
124 return (EINVAL);
126 if (srv->fd >= 0)
127 server_close_socket(srv);
129 struct addrinfo ai_hints = {.ai_family = AF_UNSPEC,
130 .ai_flags = AI_ADDRCONFIG,
131 .ai_socktype = SOCK_DGRAM};
133 status = getaddrinfo(srv->node, srv->service, &ai_hints, &ai_list);
134 if (status != 0)
135 return (status);
136 assert(ai_list != NULL);
138 for (struct addrinfo *ai_ptr = ai_list; ai_ptr != NULL;
139 ai_ptr = ai_ptr->ai_next) {
140 srv->fd =
141 socket(ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
142 if (srv->fd < 0)
143 continue;
145 if (ai_ptr->ai_family == AF_INET) {
146 struct sockaddr_in *addr = (struct sockaddr_in *)ai_ptr->ai_addr;
147 int optname;
149 if (IN_MULTICAST(ntohl(addr->sin_addr.s_addr)))
150 optname = IP_MULTICAST_TTL;
151 else
152 optname = IP_TTL;
154 status =
155 setsockopt(srv->fd, IPPROTO_IP, optname, &srv->ttl, sizeof(srv->ttl));
156 } else if (ai_ptr->ai_family == AF_INET6) {
157 /* Useful example:
158 * http://gsyc.escet.urjc.es/~eva/IPv6-web/examples/mcast.html */
159 struct sockaddr_in6 *addr = (struct sockaddr_in6 *)ai_ptr->ai_addr;
160 int optname;
162 if (IN6_IS_ADDR_MULTICAST(&addr->sin6_addr))
163 optname = IPV6_MULTICAST_HOPS;
164 else
165 optname = IPV6_UNICAST_HOPS;
167 status = setsockopt(srv->fd, IPPROTO_IPV6, optname, &srv->ttl,
168 sizeof(srv->ttl));
169 }
170 if (status != 0) {
171 /* setsockopt failed. */
172 close(srv->fd);
173 srv->fd = -1;
174 continue;
175 }
177 srv->sa = malloc(ai_ptr->ai_addrlen);
178 if (srv->sa == NULL) {
179 close(srv->fd);
180 srv->fd = -1;
181 continue;
182 }
184 memcpy(srv->sa, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
185 srv->sa_len = ai_ptr->ai_addrlen;
186 break;
187 }
189 freeaddrinfo(ai_list);
191 if (srv->fd < 0)
192 return (-1);
193 return (0);
194 } /* }}} int server_open_socket */
196 static int server_send_buffer(lcc_server_t *srv) /* {{{ */
197 {
198 char buffer[LCC_NETWORK_BUFFER_SIZE_DEFAULT] = {0};
199 size_t buffer_size;
200 int status;
202 if (srv->fd < 0) {
203 status = server_open_socket(srv);
204 if (status != 0)
205 return (status);
206 }
208 buffer_size = sizeof(buffer);
210 status = lcc_network_buffer_finalize(srv->buffer);
211 if (status != 0) {
212 lcc_network_buffer_initialize(srv->buffer);
213 return (status);
214 }
216 status = lcc_network_buffer_get(srv->buffer, buffer, &buffer_size);
217 lcc_network_buffer_initialize(srv->buffer);
219 if (status != 0)
220 return (status);
222 if (buffer_size > sizeof(buffer))
223 buffer_size = sizeof(buffer);
225 while (42) {
226 assert(srv->fd >= 0);
227 assert(srv->sa != NULL);
228 status = (int)sendto(srv->fd, buffer, buffer_size, /* flags = */ 0, srv->sa,
229 srv->sa_len);
230 if ((status < 0) && ((errno == EINTR) || (errno == EAGAIN)))
231 continue;
233 break;
234 }
236 if (status < 0)
237 return (status);
238 return (0);
239 } /* }}} int server_send_buffer */
241 static int server_value_add(lcc_server_t *srv, /* {{{ */
242 const lcc_value_list_t *vl) {
243 int status;
245 status = lcc_network_buffer_add_value(srv->buffer, vl);
246 if (status == 0)
247 return (0);
249 server_send_buffer(srv);
250 return (lcc_network_buffer_add_value(srv->buffer, vl));
251 } /* }}} int server_value_add */
253 /*
254 * Public functions
255 */
256 lcc_network_t *lcc_network_create(void) /* {{{ */
257 {
258 lcc_network_t *net;
260 net = calloc(1, sizeof(*net));
261 if (net == NULL)
262 return (NULL);
264 net->servers = NULL;
266 return (net);
267 } /* }}} lcc_network_t *lcc_network_create */
269 void lcc_network_destroy(lcc_network_t *net) /* {{{ */
270 {
271 if (net == NULL)
272 return;
273 int_server_destroy(net->servers);
274 free(net);
275 } /* }}} void lcc_network_destroy */
277 lcc_server_t *lcc_server_create(lcc_network_t *net, /* {{{ */
278 const char *node, const char *service) {
279 lcc_server_t *srv;
281 if ((net == NULL) || (node == NULL))
282 return (NULL);
283 if (service == NULL)
284 service = NET_DEFAULT_PORT;
286 srv = calloc(1, sizeof(*srv));
287 if (srv == NULL)
288 return (NULL);
290 srv->fd = -1;
291 srv->security_level = NONE;
292 srv->username = NULL;
293 srv->password = NULL;
294 srv->next = NULL;
296 srv->node = strdup(node);
297 if (srv->node == NULL) {
298 free(srv);
299 return (NULL);
300 }
302 srv->service = strdup(service);
303 if (srv->service == NULL) {
304 free(srv->node);
305 free(srv);
306 return (NULL);
307 }
309 srv->buffer = lcc_network_buffer_create(/* size = */ 0);
310 if (srv->buffer == NULL) {
311 free(srv->service);
312 free(srv->node);
313 free(srv);
314 return (NULL);
315 }
317 if (net->servers == NULL) {
318 net->servers = srv;
319 } else {
320 lcc_server_t *last = net->servers;
322 while (last->next != NULL)
323 last = last->next;
325 last->next = srv;
326 }
328 return (srv);
329 } /* }}} lcc_server_t *lcc_server_create */
331 int lcc_server_destroy(lcc_network_t *net, lcc_server_t *srv) /* {{{ */
332 {
333 if ((net == NULL) || (srv == NULL))
334 return (EINVAL);
336 if (net->servers == srv) {
337 net->servers = srv->next;
338 srv->next = NULL;
339 } else {
340 lcc_server_t *prev = net->servers;
342 while ((prev != NULL) && (prev->next != srv))
343 prev = prev->next;
345 if (prev == NULL)
346 return (ENOENT);
348 prev->next = srv->next;
349 srv->next = NULL;
350 }
352 int_server_destroy(srv);
354 return (0);
355 } /* }}} int lcc_server_destroy */
357 int lcc_server_set_ttl(lcc_server_t *srv, uint8_t ttl) /* {{{ */
358 {
359 if (srv == NULL)
360 return (EINVAL);
362 srv->ttl = (int)ttl;
364 return (0);
365 } /* }}} int lcc_server_set_ttl */
367 int lcc_server_set_interface(lcc_server_t *srv, char const *interface) /* {{{ */
368 {
369 unsigned int if_index;
370 int status;
372 if ((srv == NULL) || (interface == NULL))
373 return (EINVAL);
375 if_index = if_nametoindex(interface);
376 if (if_index == 0)
377 return (ENOENT);
379 /* IPv4 multicast */
380 if (srv->sa->sa_family == AF_INET) {
381 struct sockaddr_in *addr = (struct sockaddr_in *)srv->sa;
383 if (IN_MULTICAST(ntohl(addr->sin_addr.s_addr))) {
384 #if HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
385 /* If possible, use the "ip_mreqn" structure which has
386 * an "interface index" member. Using the interface
387 * index is preferred here, because of its similarity
388 * to the way IPv6 handles this. Unfortunately, it
389 * appears not to be portable. */
390 struct ip_mreqn mreq = {.imr_multiaddr.s_addr = addr->sin_addr.s_addr,
391 .imr_address.s_addr = ntohl(INADDR_ANY),
392 .imr_ifindex = (int)if_index};
393 #else
394 struct ip_mreq mreq = {.imr_multiaddr.s_addr = addr->sin_addr.s_addr,
395 .imr_interface.s_addr = ntohl(INADDR_ANY)};
396 #endif
398 status =
399 setsockopt(srv->fd, IPPROTO_IP, IP_MULTICAST_IF, &mreq, sizeof(mreq));
400 if (status != 0)
401 return (status);
403 return (0);
404 }
405 }
407 /* IPv6 multicast */
408 if (srv->sa->sa_family == AF_INET6) {
409 struct sockaddr_in6 *addr = (struct sockaddr_in6 *)srv->sa;
411 if (IN6_IS_ADDR_MULTICAST(&addr->sin6_addr)) {
412 status = setsockopt(srv->fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &if_index,
413 sizeof(if_index));
414 if (status != 0)
415 return (status);
417 return (0);
418 }
419 }
421 /* else: Not a multicast interface. */
422 #if defined(SO_BINDTODEVICE)
423 status = setsockopt(srv->fd, SOL_SOCKET, SO_BINDTODEVICE, interface,
424 (socklen_t)(strlen(interface) + 1));
425 if (status != 0)
426 return (-1);
427 #endif
429 return (0);
430 } /* }}} int lcc_server_set_interface */
432 int lcc_server_set_security_level(lcc_server_t *srv, /* {{{ */
433 lcc_security_level_t level,
434 const char *username, const char *password) {
435 return (lcc_network_buffer_set_security_level(srv->buffer, level, username,
436 password));
437 } /* }}} int lcc_server_set_security_level */
439 int lcc_network_values_send(lcc_network_t *net, /* {{{ */
440 const lcc_value_list_t *vl) {
441 if ((net == NULL) || (vl == NULL))
442 return (EINVAL);
444 for (lcc_server_t *srv = net->servers; srv != NULL; srv = srv->next)
445 server_value_add(srv, vl);
447 return (0);
448 } /* }}} int lcc_network_values_send */