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 <stdlib.h>
32 #include <stdio.h>
33 #include <unistd.h>
34 #include <string.h>
35 #include <errno.h>
36 #include <assert.h>
38 #include <sys/types.h>
39 #include <sys/socket.h>
40 #include <netdb.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 {
58 lcc_server_t *servers;
59 };
61 struct lcc_server_s
62 {
63 char *node;
64 char *service;
66 int ttl;
67 lcc_security_level_t security_level;
68 char *username;
69 char *password;
71 int fd;
72 struct sockaddr *sa;
73 socklen_t sa_len;
75 lcc_network_buffer_t *buffer;
77 lcc_server_t *next;
78 };
80 /*
81 * Private functions
82 */
83 static int server_close_socket (lcc_server_t *srv) /* {{{ */
84 {
85 if (srv == NULL)
86 return (EINVAL);
88 if (srv->fd < 0)
89 return (0);
91 close (srv->fd);
92 free (srv->sa);
93 srv->sa = NULL;
94 srv->sa_len = 0;
96 return (0);
97 } /* }}} int server_close_socket */
99 static void int_server_destroy (lcc_server_t *srv) /* {{{ */
100 {
101 lcc_server_t *next;
103 if (srv == NULL)
104 return;
106 server_close_socket (srv);
108 next = srv->next;
110 if (srv->fd >= 0)
111 {
112 close (srv->fd);
113 srv->fd = -1;
114 }
116 free (srv->node);
117 free (srv->service);
118 free (srv->username);
119 free (srv->password);
120 free (srv);
122 int_server_destroy (next);
123 } /* }}} void int_server_destroy */
125 static int server_open_socket (lcc_server_t *srv) /* {{{ */
126 {
127 struct addrinfo ai_hints = { 0 };
128 struct addrinfo *ai_list = NULL;
129 struct addrinfo *ai_ptr;
130 int status;
132 if (srv == NULL)
133 return (EINVAL);
135 if (srv->fd >= 0)
136 server_close_socket (srv);
138 #ifdef AI_ADDRCONFIG
139 ai_hints.ai_flags |= AI_ADDRCONFIG;
140 #endif
141 ai_hints.ai_family = AF_UNSPEC;
142 ai_hints.ai_socktype = SOCK_DGRAM;
144 status = getaddrinfo (srv->node, srv->service, &ai_hints, &ai_list);
145 if (status != 0)
146 return (status);
147 assert (ai_list != NULL);
149 for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
150 {
151 srv->fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
152 if (srv->fd < 0)
153 continue;
155 if (ai_ptr->ai_family == AF_INET)
156 {
157 struct sockaddr_in *addr = (struct sockaddr_in *) ai_ptr->ai_addr;
158 int optname;
160 if (IN_MULTICAST (ntohl (addr->sin_addr.s_addr)))
161 optname = IP_MULTICAST_TTL;
162 else
163 optname = IP_TTL;
165 status = setsockopt (srv->fd, IPPROTO_IP, optname,
166 &srv->ttl, sizeof (srv->ttl));
167 }
168 else if (ai_ptr->ai_family == AF_INET6)
169 {
170 /* Useful example: http://gsyc.escet.urjc.es/~eva/IPv6-web/examples/mcast.html */
171 struct sockaddr_in6 *addr = (struct sockaddr_in6 *) ai_ptr->ai_addr;
172 int optname;
174 if (IN6_IS_ADDR_MULTICAST (&addr->sin6_addr))
175 optname = IPV6_MULTICAST_HOPS;
176 else
177 optname = IPV6_UNICAST_HOPS;
179 status = setsockopt (srv->fd, IPPROTO_IPV6, optname,
180 &srv->ttl, sizeof (srv->ttl));
181 }
182 if (status != 0)
183 {
184 /* setsockopt failed. */
185 close (srv->fd);
186 srv->fd = -1;
187 continue;
188 }
190 srv->sa = malloc (ai_ptr->ai_addrlen);
191 if (srv->sa == NULL)
192 {
193 close (srv->fd);
194 srv->fd = -1;
195 continue;
196 }
198 memcpy (srv->sa, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
199 srv->sa_len = ai_ptr->ai_addrlen;
200 break;
201 }
203 freeaddrinfo (ai_list);
205 if (srv->fd < 0)
206 return (-1);
207 return (0);
208 } /* }}} int server_open_socket */
210 static int server_send_buffer (lcc_server_t *srv) /* {{{ */
211 {
212 char buffer[LCC_NETWORK_BUFFER_SIZE_DEFAULT];
213 size_t buffer_size;
214 int status;
216 if (srv->fd < 0)
217 {
218 status = server_open_socket (srv);
219 if (status != 0)
220 return (status);
221 }
223 memset (buffer, 0, sizeof (buffer));
224 buffer_size = sizeof (buffer);
226 status = lcc_network_buffer_finalize (srv->buffer);
227 if (status != 0)
228 {
229 lcc_network_buffer_initialize (srv->buffer);
230 return (status);
231 }
233 status = lcc_network_buffer_get (srv->buffer, buffer, &buffer_size);
234 lcc_network_buffer_initialize (srv->buffer);
236 if (status != 0)
237 return (status);
239 if (buffer_size > sizeof (buffer))
240 buffer_size = sizeof (buffer);
242 while (42)
243 {
244 assert (srv->fd >= 0);
245 assert (srv->sa != NULL);
246 status = (int) sendto (srv->fd, buffer, buffer_size, /* flags = */ 0,
247 srv->sa, srv->sa_len);
248 if ((status < 0) && ((errno == EINTR) || (errno == EAGAIN)))
249 continue;
251 break;
252 }
254 if (status < 0)
255 return (status);
256 return (0);
257 } /* }}} int server_send_buffer */
259 static int server_value_add (lcc_server_t *srv, /* {{{ */
260 const lcc_value_list_t *vl)
261 {
262 int status;
264 status = lcc_network_buffer_add_value (srv->buffer, vl);
265 if (status == 0)
266 return (0);
268 server_send_buffer (srv);
269 return (lcc_network_buffer_add_value (srv->buffer, vl));
270 } /* }}} int server_value_add */
272 /*
273 * Public functions
274 */
275 lcc_network_t *lcc_network_create (void) /* {{{ */
276 {
277 lcc_network_t *net;
279 net = malloc (sizeof (*net));
280 if (net == NULL)
281 return (NULL);
282 memset (net, 0, sizeof (*net));
284 net->servers = NULL;
286 return (net);
287 } /* }}} lcc_network_t *lcc_network_create */
289 void lcc_network_destroy (lcc_network_t *net) /* {{{ */
290 {
291 if (net == NULL)
292 return;
293 int_server_destroy (net->servers);
294 free (net);
295 } /* }}} void lcc_network_destroy */
297 lcc_server_t *lcc_server_create (lcc_network_t *net, /* {{{ */
298 const char *node, const char *service)
299 {
300 lcc_server_t *srv;
302 if ((net == NULL) || (node == NULL))
303 return (NULL);
304 if (service == NULL)
305 service = NET_DEFAULT_PORT;
307 srv = malloc (sizeof (*srv));
308 if (srv == NULL)
309 return (NULL);
310 memset (srv, 0, sizeof (*srv));
312 srv->fd = -1;
313 srv->security_level = NONE;
314 srv->username = NULL;
315 srv->password = NULL;
316 srv->next = NULL;
318 srv->node = strdup (node);
319 if (srv->node == NULL)
320 {
321 free (srv);
322 return (NULL);
323 }
325 srv->service = strdup (service);
326 if (srv->service == NULL)
327 {
328 free (srv->node);
329 free (srv);
330 return (NULL);
331 }
333 srv->buffer = lcc_network_buffer_create (/* size = */ 0);
334 if (srv->buffer == NULL)
335 {
336 free (srv->service);
337 free (srv->node);
338 free (srv);
339 return (NULL);
340 }
342 if (net->servers == NULL)
343 {
344 net->servers = srv;
345 }
346 else
347 {
348 lcc_server_t *last = net->servers;
350 while (last->next != NULL)
351 last = last->next;
353 last->next = srv;
354 }
356 return (srv);
357 } /* }}} lcc_server_t *lcc_server_create */
359 int lcc_server_destroy (lcc_network_t *net, lcc_server_t *srv) /* {{{ */
360 {
361 if ((net == NULL) || (srv == NULL))
362 return (EINVAL);
364 if (net->servers == srv)
365 {
366 net->servers = srv->next;
367 srv->next = NULL;
368 }
369 else
370 {
371 lcc_server_t *prev = net->servers;
373 while ((prev != NULL) && (prev->next != srv))
374 prev = prev->next;
376 if (prev == NULL)
377 return (ENOENT);
379 prev->next = srv->next;
380 srv->next = NULL;
381 }
383 int_server_destroy (srv);
385 return (0);
386 } /* }}} int lcc_server_destroy */
388 int lcc_server_set_ttl (lcc_server_t *srv, uint8_t ttl) /* {{{ */
389 {
390 if (srv == NULL)
391 return (EINVAL);
393 srv->ttl = (int) ttl;
395 return (0);
396 } /* }}} int lcc_server_set_ttl */
398 int lcc_server_set_interface (lcc_server_t *srv, char const *interface) /* {{{ */
399 {
400 int if_index;
401 int status;
403 if ((srv == NULL) || (interface == NULL))
404 return (EINVAL);
406 if_index = if_nametoindex (interface);
407 if (if_index == 0)
408 return (ENOENT);
410 /* IPv4 multicast */
411 if (srv->sa->sa_family == AF_INET)
412 {
413 struct sockaddr_in *addr = (struct sockaddr_in *) srv->sa;
415 if (IN_MULTICAST (ntohl (addr->sin_addr.s_addr)))
416 {
417 #if HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
418 /* If possible, use the "ip_mreqn" structure which has
419 * an "interface index" member. Using the interface
420 * index is preferred here, because of its similarity
421 * to the way IPv6 handles this. Unfortunately, it
422 * appears not to be portable. */
423 struct ip_mreqn mreq;
425 memset (&mreq, 0, sizeof (mreq));
426 mreq.imr_multiaddr.s_addr = addr->sin_addr.s_addr;
427 mreq.imr_address.s_addr = ntohl (INADDR_ANY);
428 mreq.imr_ifindex = if_index;
429 #else
430 struct ip_mreq mreq;
432 memset (&mreq, 0, sizeof (mreq));
433 mreq.imr_multiaddr.s_addr = addr->sin_addr.s_addr;
434 mreq.imr_interface.s_addr = ntohl (INADDR_ANY);
435 #endif
437 status = setsockopt (srv->fd, IPPROTO_IP, IP_MULTICAST_IF,
438 &mreq, sizeof (mreq));
439 if (status != 0)
440 return (status);
442 return (0);
443 }
444 }
446 /* IPv6 multicast */
447 if (srv->sa->sa_family == AF_INET6)
448 {
449 struct sockaddr_in6 *addr = (struct sockaddr_in6 *) srv->sa;
451 if (IN6_IS_ADDR_MULTICAST (&addr->sin6_addr))
452 {
453 status = setsockopt (srv->fd, IPPROTO_IPV6, IPV6_MULTICAST_IF,
454 &if_index, sizeof (if_index));
455 if (status != 0)
456 return (status);
458 return (0);
459 }
460 }
462 /* else: Not a multicast interface. */
463 #if defined(SO_BINDTODEVICE)
464 status = setsockopt (srv->fd, SOL_SOCKET, SO_BINDTODEVICE,
465 interface, strlen (interface) + 1);
466 if (status != 0)
467 return (-1);
468 #endif
470 return (0);
471 } /* }}} int lcc_server_set_interface */
473 int lcc_server_set_security_level (lcc_server_t *srv, /* {{{ */
474 lcc_security_level_t level,
475 const char *username, const char *password)
476 {
477 return (lcc_network_buffer_set_security_level (srv->buffer,
478 level, username, password));
479 } /* }}} int lcc_server_set_security_level */
481 int lcc_network_values_send (lcc_network_t *net, /* {{{ */
482 const lcc_value_list_t *vl)
483 {
484 lcc_server_t *srv;
486 if ((net == NULL) || (vl == NULL))
487 return (EINVAL);
489 for (srv = net->servers; srv != NULL; srv = srv->next)
490 server_value_add (srv, vl);
492 return (0);
493 } /* }}} int lcc_network_values_send */
495 /* vim: set sw=2 sts=2 et fdm=marker : */