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