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