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