Merge branch 'collectd-5.7' into collectd-5.8
[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 <assert.h>
32 #include <errno.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <unistd.h>
37
38 #include <netdb.h>
39 #include <sys/socket.h>
40 #include <sys/types.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   lcc_server_t *servers;
58 };
59
60 struct lcc_server_s {
61   char *node;
62   char *service;
63
64   int ttl;
65   lcc_security_level_t security_level;
66   char *username;
67   char *password;
68
69   int fd;
70   struct sockaddr *sa;
71   socklen_t sa_len;
72
73   lcc_network_buffer_t *buffer;
74
75   lcc_server_t *next;
76 };
77
78 /*
79  * Private functions
80  */
81 static int server_close_socket(lcc_server_t *srv) /* {{{ */
82 {
83   if (srv == NULL)
84     return EINVAL;
85
86   if (srv->fd < 0)
87     return 0;
88
89   close(srv->fd);
90   srv->fd = -1;
91   free(srv->sa);
92   srv->sa = NULL;
93   srv->sa_len = 0;
94
95   return 0;
96 } /* }}} int server_close_socket */
97
98 static void int_server_destroy(lcc_server_t *srv) /* {{{ */
99 {
100   lcc_server_t *next;
101
102   if (srv == NULL)
103     return;
104
105   server_close_socket(srv);
106
107   next = srv->next;
108
109   free(srv->node);
110   free(srv->service);
111   free(srv->username);
112   free(srv->password);
113   free(srv);
114
115   int_server_destroy(next);
116 } /* }}} void int_server_destroy */
117
118 static int server_open_socket(lcc_server_t *srv) /* {{{ */
119 {
120   struct addrinfo *ai_list;
121   int status;
122
123   if (srv == NULL)
124     return EINVAL;
125
126   if (srv->fd >= 0)
127     server_close_socket(srv);
128
129   struct addrinfo ai_hints = {.ai_family = AF_UNSPEC,
130                               .ai_flags = AI_ADDRCONFIG,
131                               .ai_socktype = SOCK_DGRAM};
132
133   status = getaddrinfo(srv->node, srv->service, &ai_hints, &ai_list);
134   if (status != 0)
135     return status;
136   assert(ai_list != NULL);
137
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;
144
145     if (ai_ptr->ai_family == AF_INET) {
146       struct sockaddr_in *addr = (struct sockaddr_in *)ai_ptr->ai_addr;
147       int optname;
148
149       if (IN_MULTICAST(ntohl(addr->sin_addr.s_addr)))
150         optname = IP_MULTICAST_TTL;
151       else
152         optname = IP_TTL;
153
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;
161
162       if (IN6_IS_ADDR_MULTICAST(&addr->sin6_addr))
163         optname = IPV6_MULTICAST_HOPS;
164       else
165         optname = IPV6_UNICAST_HOPS;
166
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     }
176
177     srv->sa = malloc(ai_ptr->ai_addrlen);
178     if (srv->sa == NULL) {
179       close(srv->fd);
180       srv->fd = -1;
181       continue;
182     }
183
184     memcpy(srv->sa, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
185     srv->sa_len = ai_ptr->ai_addrlen;
186     break;
187   }
188
189   freeaddrinfo(ai_list);
190
191   if (srv->fd < 0)
192     return -1;
193   return 0;
194 } /* }}} int server_open_socket */
195
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;
201
202   if (srv->fd < 0) {
203     status = server_open_socket(srv);
204     if (status != 0)
205       return status;
206   }
207
208   buffer_size = sizeof(buffer);
209
210   status = lcc_network_buffer_finalize(srv->buffer);
211   if (status != 0) {
212     lcc_network_buffer_initialize(srv->buffer);
213     return status;
214   }
215
216   status = lcc_network_buffer_get(srv->buffer, buffer, &buffer_size);
217   lcc_network_buffer_initialize(srv->buffer);
218
219   if (status != 0)
220     return status;
221
222   if (buffer_size > sizeof(buffer))
223     buffer_size = sizeof(buffer);
224
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;
232
233     break;
234   }
235
236   if (status < 0)
237     return status;
238   return 0;
239 } /* }}} int server_send_buffer */
240
241 static int server_value_add(lcc_server_t *srv, /* {{{ */
242                             const lcc_value_list_t *vl) {
243   int status;
244
245   status = lcc_network_buffer_add_value(srv->buffer, vl);
246   if (status == 0)
247     return 0;
248
249   server_send_buffer(srv);
250   return lcc_network_buffer_add_value(srv->buffer, vl);
251 } /* }}} int server_value_add */
252
253 /*
254  * Public functions
255  */
256 lcc_network_t *lcc_network_create(void) /* {{{ */
257 {
258   lcc_network_t *net;
259
260   net = calloc(1, sizeof(*net));
261   if (net == NULL)
262     return NULL;
263
264   net->servers = NULL;
265
266   return net;
267 } /* }}} lcc_network_t *lcc_network_create */
268
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 */
276
277 lcc_server_t *lcc_server_create(lcc_network_t *net, /* {{{ */
278                                 const char *node, const char *service) {
279   lcc_server_t *srv;
280
281   if ((net == NULL) || (node == NULL))
282     return NULL;
283   if (service == NULL)
284     service = NET_DEFAULT_PORT;
285
286   srv = calloc(1, sizeof(*srv));
287   if (srv == NULL)
288     return NULL;
289
290   srv->fd = -1;
291   srv->security_level = NONE;
292   srv->username = NULL;
293   srv->password = NULL;
294   srv->next = NULL;
295
296   srv->node = strdup(node);
297   if (srv->node == NULL) {
298     free(srv);
299     return NULL;
300   }
301
302   srv->service = strdup(service);
303   if (srv->service == NULL) {
304     free(srv->node);
305     free(srv);
306     return NULL;
307   }
308
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   }
316
317   if (net->servers == NULL) {
318     net->servers = srv;
319   } else {
320     lcc_server_t *last = net->servers;
321
322     while (last->next != NULL)
323       last = last->next;
324
325     last->next = srv;
326   }
327
328   return srv;
329 } /* }}} lcc_server_t *lcc_server_create */
330
331 int lcc_server_destroy(lcc_network_t *net, lcc_server_t *srv) /* {{{ */
332 {
333   if ((net == NULL) || (srv == NULL))
334     return EINVAL;
335
336   if (net->servers == srv) {
337     net->servers = srv->next;
338     srv->next = NULL;
339   } else {
340     lcc_server_t *prev = net->servers;
341
342     while ((prev != NULL) && (prev->next != srv))
343       prev = prev->next;
344
345     if (prev == NULL)
346       return ENOENT;
347
348     prev->next = srv->next;
349     srv->next = NULL;
350   }
351
352   int_server_destroy(srv);
353
354   return 0;
355 } /* }}} int lcc_server_destroy */
356
357 int lcc_server_set_ttl(lcc_server_t *srv, uint8_t ttl) /* {{{ */
358 {
359   if (srv == NULL)
360     return EINVAL;
361
362   srv->ttl = (int)ttl;
363
364   return 0;
365 } /* }}} int lcc_server_set_ttl */
366
367 int lcc_server_set_interface(lcc_server_t *srv, char const *interface) /* {{{ */
368 {
369   unsigned int if_index;
370   int status;
371
372   if ((srv == NULL) || (interface == NULL))
373     return EINVAL;
374
375   if_index = if_nametoindex(interface);
376   if (if_index == 0)
377     return ENOENT;
378
379   /* IPv4 multicast */
380   if (srv->sa->sa_family == AF_INET) {
381     struct sockaddr_in *addr = (struct sockaddr_in *)srv->sa;
382
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
397
398       status =
399           setsockopt(srv->fd, IPPROTO_IP, IP_MULTICAST_IF, &mreq, sizeof(mreq));
400       if (status != 0)
401         return status;
402
403       return 0;
404     }
405   }
406
407   /* IPv6 multicast */
408   if (srv->sa->sa_family == AF_INET6) {
409     struct sockaddr_in6 *addr = (struct sockaddr_in6 *)srv->sa;
410
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;
416
417       return 0;
418     }
419   }
420
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
428
429   return 0;
430 } /* }}} int lcc_server_set_interface */
431
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 */
438
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;
443
444   for (lcc_server_t *srv = net->servers; srv != NULL; srv = srv->next)
445     server_value_add(srv, vl);
446
447   return 0;
448 } /* }}} int lcc_network_values_send */