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