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