Merge branch 'collectd-5.4' into collectd-5.5
[collectd.git] / src / libcollectdclient / network.c
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  **/
28
29 #include "collectd.h"
30
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <unistd.h>
34 #include <string.h>
35 #include <errno.h>
36 #include <assert.h>
37
38 #include <sys/types.h>
39 #include <sys/socket.h>
40 #include <netdb.h>
41
42 #if HAVE_NETINET_IN_H
43 # include <netinet/in.h>
44 #endif
45
46 #if HAVE_NET_IF_H
47 # include <net/if.h>
48 #endif
49
50 #include "collectd/network.h"
51 #include "collectd/network_buffer.h"
52
53 /*
54  * Private data types
55  */
56 struct lcc_network_s
57 {
58   lcc_server_t *servers;
59 };
60
61 struct lcc_server_s
62 {
63   char *node;
64   char *service;
65
66   int ttl;
67   lcc_security_level_t security_level;
68   char *username;
69   char *password;
70
71   int fd;
72   struct sockaddr *sa;
73   socklen_t sa_len;
74
75   lcc_network_buffer_t *buffer;
76
77   lcc_server_t *next;
78 };
79
80 /*
81  * Private functions
82  */
83 static int server_close_socket (lcc_server_t *srv) /* {{{ */
84 {
85   if (srv == NULL)
86     return (EINVAL);
87
88   if (srv->fd < 0)
89     return (0);
90
91   close (srv->fd);
92   free (srv->sa);
93   srv->sa = NULL;
94   srv->sa_len = 0;
95
96   return (0);
97 } /* }}} int server_close_socket */
98
99 static void int_server_destroy (lcc_server_t *srv) /* {{{ */
100 {
101   lcc_server_t *next;
102
103   if (srv == NULL)
104     return;
105
106   server_close_socket (srv);
107
108   next = srv->next;
109
110   if (srv->fd >= 0)
111   {
112     close (srv->fd);
113     srv->fd = -1;
114   }
115
116   free (srv->node);
117   free (srv->service);
118   free (srv->username);
119   free (srv->password);
120   free (srv);
121
122   int_server_destroy (next);
123 } /* }}} void int_server_destroy */
124
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;
131
132   if (srv == NULL)
133     return (EINVAL);
134
135   if (srv->fd >= 0)
136     server_close_socket (srv);
137
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;
143
144   status = getaddrinfo (srv->node, srv->service, &ai_hints, &ai_list);
145   if (status != 0)
146     return (status);
147   assert (ai_list != NULL);
148
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;
154
155     if (ai_ptr->ai_family == AF_INET)
156     {
157
158       struct sockaddr_in *addr = (struct sockaddr_in *) ai_ptr->ai_addr;
159       int optname;
160
161       if (IN_MULTICAST (ntohl (addr->sin_addr.s_addr)))
162         optname = IP_MULTICAST_TTL;
163       else
164         optname = IP_TTL;
165
166       setsockopt (srv->fd, IPPROTO_IP, optname,
167           &srv->ttl,
168           sizeof (srv->ttl));
169     }
170     else if (ai_ptr->ai_family == AF_INET6)
171     {
172       /* Useful example: http://gsyc.escet.urjc.es/~eva/IPv6-web/examples/mcast.html */
173       struct sockaddr_in6 *addr = (struct sockaddr_in6 *) ai_ptr->ai_addr;
174       int optname;
175
176       if (IN6_IS_ADDR_MULTICAST (&addr->sin6_addr))
177         optname = IPV6_MULTICAST_HOPS;
178       else
179         optname = IPV6_UNICAST_HOPS;
180
181       setsockopt (srv->fd, IPPROTO_IPV6, optname,
182           &srv->ttl,
183           sizeof (srv->ttl));
184     }
185
186     srv->sa = malloc (ai_ptr->ai_addrlen);
187     if (srv->sa == NULL)
188     {
189       close (srv->fd);
190       srv->fd = -1;
191       continue;
192     }
193
194     memcpy (srv->sa, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
195     srv->sa_len = ai_ptr->ai_addrlen;
196     break;
197   }
198
199   freeaddrinfo (ai_list);
200
201   if (srv->fd < 0)
202     return (-1);
203   return (0);
204 } /* }}} int server_open_socket */
205
206 static int server_send_buffer (lcc_server_t *srv) /* {{{ */
207 {
208   char buffer[LCC_NETWORK_BUFFER_SIZE_DEFAULT];
209   size_t buffer_size;
210   int status;
211
212   if (srv->fd < 0)
213   {
214     status = server_open_socket (srv);
215     if (status != 0)
216       return (status);
217   }
218
219   memset (buffer, 0, sizeof (buffer));
220   buffer_size = sizeof (buffer);
221
222   status = lcc_network_buffer_finalize (srv->buffer);
223   if (status != 0)
224   {
225     lcc_network_buffer_initialize (srv->buffer);
226     return (status);
227   }
228
229   status = lcc_network_buffer_get (srv->buffer, buffer, &buffer_size);
230   lcc_network_buffer_initialize (srv->buffer);
231
232   if (status != 0)
233     return (status);
234
235   if (buffer_size > sizeof (buffer))
236     buffer_size = sizeof (buffer);
237
238   while (42)
239   {
240     assert (srv->fd >= 0);
241     assert (srv->sa != NULL);
242     status = (int) sendto (srv->fd, buffer, buffer_size, /* flags = */ 0,
243         srv->sa, srv->sa_len);
244     if ((status < 0) && ((errno == EINTR) || (errno == EAGAIN)))
245       continue;
246
247     break;
248   }
249
250   if (status < 0)
251     return (status);
252   return (0);
253 } /* }}} int server_send_buffer */
254
255 static int server_value_add (lcc_server_t *srv, /* {{{ */
256     const lcc_value_list_t *vl)
257 {
258   int status;
259
260   status = lcc_network_buffer_add_value (srv->buffer, vl);
261   if (status == 0)
262     return (0);
263
264   server_send_buffer (srv);
265   return (lcc_network_buffer_add_value (srv->buffer, vl));
266 } /* }}} int server_value_add */
267
268 /*
269  * Public functions
270  */
271 lcc_network_t *lcc_network_create (void) /* {{{ */
272 {
273   lcc_network_t *net;
274
275   net = malloc (sizeof (*net));
276   if (net == NULL)
277     return (NULL);
278   memset (net, 0, sizeof (*net));
279
280   net->servers = NULL;
281
282   return (net);
283 } /* }}} lcc_network_t *lcc_network_create */
284
285 void lcc_network_destroy (lcc_network_t *net) /* {{{ */
286 {
287   if (net == NULL)
288     return;
289   int_server_destroy (net->servers);
290   free (net);
291 } /* }}} void lcc_network_destroy */
292
293 lcc_server_t *lcc_server_create (lcc_network_t *net, /* {{{ */
294     const char *node, const char *service)
295 {
296   lcc_server_t *srv;
297
298   if ((net == NULL) || (node == NULL))
299     return (NULL);
300   if (service == NULL)
301     service = NET_DEFAULT_PORT;
302
303   srv = malloc (sizeof (*srv));
304   if (srv == NULL)
305     return (NULL);
306   memset (srv, 0, sizeof (*srv));
307
308   srv->fd = -1;
309   srv->security_level = NONE;
310   srv->username = NULL;
311   srv->password = NULL;
312   srv->next = NULL;
313
314   srv->node = strdup (node);
315   if (srv->node == NULL)
316   {
317     free (srv);
318     return (NULL);
319   }
320
321   srv->service = strdup (service);
322   if (srv->service == NULL)
323   {
324     free (srv->node);
325     free (srv);
326     return (NULL);
327   }
328
329   srv->buffer = lcc_network_buffer_create (/* size = */ 0);
330   if (srv->buffer == NULL)
331   {
332     free (srv->service);
333     free (srv->node);
334     free (srv);
335     return (NULL);
336   }
337
338   if (net->servers == NULL)
339   {
340     net->servers = srv;
341   }
342   else
343   {
344     lcc_server_t *last = net->servers;
345
346     while (last->next != NULL)
347       last = last->next;
348
349     last->next = srv;
350   }
351
352   return (srv);
353 } /* }}} lcc_server_t *lcc_server_create */
354
355 int lcc_server_destroy (lcc_network_t *net, lcc_server_t *srv) /* {{{ */
356 {
357   if ((net == NULL) || (srv == NULL))
358     return (EINVAL);
359
360   if (net->servers == srv)
361   {
362     net->servers = srv->next;
363     srv->next = NULL;
364   }
365   else
366   {
367     lcc_server_t *prev = net->servers;
368
369     while ((prev != NULL) && (prev->next != srv))
370       prev = prev->next;
371
372     if (prev == NULL)
373       return (ENOENT);
374
375     prev->next = srv->next;
376     srv->next = NULL;
377   }
378
379   int_server_destroy (srv);
380
381   return (0);
382 } /* }}} int lcc_server_destroy */
383
384 int lcc_server_set_ttl (lcc_server_t *srv, uint8_t ttl) /* {{{ */
385 {
386   if (srv == NULL)
387     return (EINVAL);
388
389   srv->ttl = (int) ttl;
390
391   return (0);
392 } /* }}} int lcc_server_set_ttl */
393
394 int lcc_server_set_interface (lcc_server_t *srv, char const *interface) /* {{{ */
395 {
396   int if_index;
397   int status;
398
399   if ((srv == NULL) || (interface == NULL))
400     return (EINVAL);
401
402   if_index = if_nametoindex (interface);
403   if (if_index == 0)
404     return (ENOENT);
405
406   /* IPv4 multicast */
407   if (srv->sa->sa_family == AF_INET)
408   {
409     struct sockaddr_in *addr = (struct sockaddr_in *) srv->sa;
410
411     if (IN_MULTICAST (ntohl (addr->sin_addr.s_addr)))
412     {
413 #if HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
414       /* If possible, use the "ip_mreqn" structure which has
415        * an "interface index" member. Using the interface
416        * index is preferred here, because of its similarity
417        * to the way IPv6 handles this. Unfortunately, it
418        * appears not to be portable. */
419       struct ip_mreqn mreq;
420
421       memset (&mreq, 0, sizeof (mreq));
422       mreq.imr_multiaddr.s_addr = addr->sin_addr.s_addr;
423       mreq.imr_address.s_addr = ntohl (INADDR_ANY);
424       mreq.imr_ifindex = if_index;
425 #else
426       struct ip_mreq mreq;
427
428       memset (&mreq, 0, sizeof (mreq));
429       mreq.imr_multiaddr.s_addr = addr->sin_addr.s_addr;
430       mreq.imr_interface.s_addr = ntohl (INADDR_ANY);
431 #endif
432
433       status = setsockopt (srv->fd, IPPROTO_IP, IP_MULTICAST_IF,
434           &mreq, sizeof (mreq));
435       if (status != 0)
436         return (status);
437
438       return (0);
439     }
440   }
441
442   /* IPv6 multicast */
443   if (srv->sa->sa_family == AF_INET6)
444   {
445     struct sockaddr_in6 *addr = (struct sockaddr_in6 *) srv->sa;
446
447     if (IN6_IS_ADDR_MULTICAST (&addr->sin6_addr))
448     {
449       status = setsockopt (srv->fd, IPPROTO_IPV6, IPV6_MULTICAST_IF,
450           &if_index, sizeof (if_index));
451       if (status != 0)
452         return (status);
453
454       return (0);
455     }
456   }
457
458   /* else: Not a multicast interface. */
459 #if defined(SO_BINDTODEVICE)
460   status = setsockopt (srv->fd, SOL_SOCKET, SO_BINDTODEVICE,
461       interface, strlen (interface) + 1);
462   if (status != 0)
463     return (-1);
464 #endif
465
466   return (0);
467 } /* }}} int lcc_server_set_interface */
468
469 int lcc_server_set_security_level (lcc_server_t *srv, /* {{{ */
470     lcc_security_level_t level,
471     const char *username, const char *password)
472 {
473   return (lcc_network_buffer_set_security_level (srv->buffer,
474         level, username, password));
475 } /* }}} int lcc_server_set_security_level */
476
477 int lcc_network_values_send (lcc_network_t *net, /* {{{ */
478     const lcc_value_list_t *vl)
479 {
480   lcc_server_t *srv;
481
482   if ((net == NULL) || (vl == NULL))
483     return (EINVAL);
484
485   for (srv = net->servers; srv != NULL; srv = srv->next)
486     server_value_add (srv, vl);
487
488   return (0);
489 } /* }}} int lcc_network_values_send */
490
491 /* vim: set sw=2 sts=2 et fdm=marker : */