Make `AI_ADDRCONFIG' and `AI_PASSIVE' conditional (use them only if they exist..)
[collectd.git] / src / network.c
1 /**
2  * collectd - src/network.c
3  * Copyright (C) 2006  Florian octo Forster
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the
7  * Free Software Foundation; either version 2 of the License, or (at your
8  * option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
18  *
19  * Authors:
20  *   Florian octo Forster <octo at verplant.org>
21  **/
22
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <string.h>
26 #include <sys/types.h>
27 #include <sys/socket.h>
28 #include <netdb.h>
29 #include <netinet/in.h>
30 #include <arpa/inet.h>
31 #include <syslog.h>
32 #include <errno.h>
33
34 #include "network.h"
35 #include "common.h"
36 #include "configfile.h"
37 #include "utils_debug.h"
38
39 /* 1500 - 40 - 8  =  Ethernet packet - IPv6 header - UDP header */
40 /* #define BUFF_SIZE 1452 */
41
42 #define BUFF_SIZE 4096
43
44 #ifdef HAVE_LIBRRD
45 extern int operating_mode;
46 #else
47 static int operating_mode = MODE_CLIENT;
48 #endif
49
50 typedef struct sockent
51 {
52         int                      fd;
53         int                      mode;
54         struct sockaddr_storage *addr;
55         socklen_t                addrlen;
56         struct sockent          *next;
57 } sockent_t;
58
59 static sockent_t *socklist_head = NULL;
60
61 static int network_set_ttl (const sockent_t *se, const struct addrinfo *ai)
62 {
63         char *ttl_str;
64         int   ttl_int;
65
66         ttl_str = cf_get_option ("TimeToLive", NULL);
67         if (ttl_str == NULL)
68                 return (-1);
69
70         ttl_int = atoi (ttl_str);
71         if ((ttl_int < 1) || (ttl_int > 255))
72         {
73                 syslog (LOG_WARNING, "A TTL value of %i is invalid.", ttl_int);
74                 return (-1);
75         }
76
77         DBG ("ttl = %i", ttl_int);
78
79         if (ai->ai_family == AF_INET)
80         {
81                 struct sockaddr_in *addr = (struct sockaddr_in *) ai->ai_addr;
82                 int optname;
83
84                 if (IN_MULTICAST (ntohl (addr->sin_addr.s_addr)))
85                         optname = IP_MULTICAST_TTL;
86                 else
87                         optname = IP_TTL;
88
89                 if (setsockopt (se->fd, IPPROTO_IP, optname,
90                                         &ttl_int, sizeof (ttl_int)) == -1)
91                 {
92                         syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
93                         return (-1);
94                 }
95         }
96         else if (ai->ai_family == AF_INET6)
97         {
98                 /* Useful example: http://gsyc.escet.urjc.es/~eva/IPv6-web/examples/mcast.html */
99                 struct sockaddr_in6 *addr = (struct sockaddr_in6 *) ai->ai_addr;
100                 int optname;
101
102                 if (IN6_IS_ADDR_MULTICAST (&addr->sin6_addr))
103                         optname = IPV6_MULTICAST_HOPS;
104                 else
105                         optname = IPV6_UNICAST_HOPS;
106
107                 if (setsockopt (se->fd, IPPROTO_IPV6, optname,
108                                         &ttl_int, sizeof (ttl_int)) == -1)
109                 {
110                         syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
111                         return (-1);
112                 }
113         }
114
115         return (0);
116 }
117
118 static int network_bind_socket (const sockent_t *se, const struct addrinfo *ai)
119 {
120         int loop = 1;
121
122         DBG ("fd = %i; calling `bind'", se->fd);
123
124         if (bind (se->fd, ai->ai_addr, ai->ai_addrlen) == -1)
125         {
126                 syslog (LOG_ERR, "bind: %s", strerror (errno));
127                 return (-1);
128         }
129
130         if (ai->ai_family == AF_INET)
131         {
132                 struct sockaddr_in *addr = (struct sockaddr_in *) ai->ai_addr;
133                 if (IN_MULTICAST (ntohl (addr->sin_addr.s_addr)))
134                 {
135                         struct ip_mreq mreq;
136
137                         DBG ("fd = %i; IPv4 multicast address found", se->fd);
138
139                         mreq.imr_multiaddr.s_addr = addr->sin_addr.s_addr;
140                         mreq.imr_interface.s_addr = htonl (INADDR_ANY);
141
142                         if (setsockopt (se->fd, IPPROTO_IP, IP_MULTICAST_LOOP,
143                                                 &loop, sizeof (loop)) == -1)
144                         {
145                                 syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
146                                 return (-1);
147                         }
148
149                         if (setsockopt (se->fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
150                                                 &mreq, sizeof (mreq)) == -1)
151                         {
152                                 syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
153                                 return (-1);
154                         }
155                 }
156         }
157         else if (ai->ai_family == AF_INET6)
158         {
159                 /* Useful example: http://gsyc.escet.urjc.es/~eva/IPv6-web/examples/mcast.html */
160                 struct sockaddr_in6 *addr = (struct sockaddr_in6 *) ai->ai_addr;
161                 if (IN6_IS_ADDR_MULTICAST (&addr->sin6_addr))
162                 {
163                         struct ipv6_mreq mreq;
164
165                         DBG ("fd = %i; IPv6 multicast address found", se->fd);
166
167                         memcpy (&mreq.ipv6mr_multiaddr,
168                                         &addr->sin6_addr,
169                                         sizeof (addr->sin6_addr));
170
171                         /* http://developer.apple.com/documentation/Darwin/Reference/ManPages/man4/ip6.4.html
172                          * ipv6mr_interface may be set to zeroes to
173                          * choose the default multicast interface or to
174                          * the index of a particular multicast-capable
175                          * interface if the host is multihomed.
176                          * Membership is associ-associated with a
177                          * single interface; programs running on
178                          * multihomed hosts may need to join the same
179                          * group on more than one interface.*/
180                         mreq.ipv6mr_interface = 0;
181
182                         if (setsockopt (se->fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
183                                                 &loop, sizeof (loop)) == -1)
184                         {
185                                 syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
186                                 return (-1);
187                         }
188
189                         if (setsockopt (se->fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP,
190                                                 &mreq, sizeof (mreq)) == -1)
191                         {
192                                 syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
193                                 return (-1);
194                         }
195                 }
196         }
197
198         return (0);
199 }
200
201 int network_create_socket (const char *node, const char *service)
202 {
203         sockent_t *socklist_tail;
204
205         struct addrinfo  ai_hints;
206         struct addrinfo *ai_list, *ai_ptr;
207         int              ai_return;
208
209         int num_added = 0;
210
211         DBG ("node = %s, service = %s", node, service);
212
213         if (operating_mode == MODE_LOCAL)
214                 return (-1);
215
216         socklist_tail = socklist_head;
217         while ((socklist_tail != NULL) && (socklist_tail->next != NULL))
218                 socklist_tail = socklist_tail->next;
219
220         memset (&ai_hints, '\0', sizeof (ai_hints));
221         ai_hints.ai_flags    = 0;
222 #ifdef AI_PASSIVE
223         ai_hints.ai_flags |= AI_PASSIVE;
224 #endif
225 #ifdef AI_ADDRCONFIG
226         ai_hints.ai_flags |= AI_ADDRCONFIG;
227 #endif
228         ai_hints.ai_family   = PF_UNSPEC;
229         ai_hints.ai_socktype = SOCK_DGRAM;
230         ai_hints.ai_protocol = IPPROTO_UDP;
231
232         if ((ai_return = getaddrinfo (node, service, &ai_hints, &ai_list)) != 0)
233         {
234                 syslog (LOG_ERR, "getaddrinfo (%s, %s): %s",
235                                 node == NULL ? "(null)" : node,
236                                 service == NULL ? "(null)" : service,
237                                 ai_return == EAI_SYSTEM ? strerror (errno) : gai_strerror (ai_return));
238                 return (-1);
239         }
240
241         for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
242         {
243                 sockent_t *se;
244
245                 if ((se = (sockent_t *) malloc (sizeof (sockent_t))) == NULL)
246                 {
247                         syslog (LOG_EMERG, "malloc: %s", strerror (errno));
248                         continue;
249                 }
250
251                 if ((se->addr = (struct sockaddr_storage *) malloc (sizeof (struct sockaddr_storage))) == NULL)
252                 {
253                         syslog (LOG_EMERG, "malloc: %s", strerror (errno));
254                         free (se);
255                         continue;
256                 }
257
258                 assert (sizeof (struct sockaddr_storage) >= ai_ptr->ai_addrlen);
259                 memset (se->addr, '\0', sizeof (struct sockaddr_storage));
260                 memcpy (se->addr, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
261                 se->addrlen = ai_ptr->ai_addrlen;
262
263                 se->mode = operating_mode;
264                 se->fd   = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
265                 se->next = NULL;
266
267                 if (se->fd == -1)
268                 {
269                         syslog (LOG_ERR, "socket: %s", strerror (errno));
270                         free (se->addr);
271                         free (se);
272                         continue;
273                 }
274
275                 if (operating_mode == MODE_SERVER)
276                 {
277                         if (network_bind_socket (se, ai_ptr) != 0)
278                         {
279                                 free (se->addr);
280                                 free (se);
281                                 continue;
282                         }
283                 }
284                 else if (operating_mode == MODE_CLIENT)
285                 {
286                         network_set_ttl (se, ai_ptr);
287                 }
288
289                 if (socklist_tail == NULL)
290                 {
291                         socklist_head = se;
292                         socklist_tail = se;
293                 }
294                 else
295                 {
296                         socklist_tail->next = se;
297                         socklist_tail = se;
298                 }
299
300                 num_added++;
301
302                 /* We don't open more than one write-socket per node/service pair.. */
303                 if (operating_mode == MODE_CLIENT)
304                         break;
305         }
306
307         freeaddrinfo (ai_list);
308
309         return (num_added);
310 }
311
312 static int network_connect_default (void)
313 {
314         int ret;
315
316         if (socklist_head != NULL)
317                 return (0);
318
319         DBG ("socklist_head is NULL");
320
321         ret = 0;
322
323         if (network_create_socket (NET_DEFAULT_V6_ADDR, NET_DEFAULT_PORT) > 0)
324                 ret++;
325
326         /* Don't use IPv4 and IPv6 in parallel by default.. */
327         if ((operating_mode == MODE_CLIENT) && (ret != 0))
328                 return (ret);
329
330         if (network_create_socket (NET_DEFAULT_V4_ADDR, NET_DEFAULT_PORT) > 0)
331                 ret++;
332
333         if (ret == 0)
334                 ret = -1;
335
336         return (ret);
337 }
338
339 static int network_get_listen_socket (void)
340 {
341         int fd;
342         int max_fd;
343         int status;
344
345         fd_set readfds;
346         sockent_t *se;
347
348         if (socklist_head == NULL)
349                 network_connect_default ();
350
351         FD_ZERO (&readfds);
352         max_fd = -1;
353         for (se = socklist_head; se != NULL; se = se->next)
354         {
355                 if (se->mode != operating_mode)
356                         continue;
357
358                 FD_SET (se->fd, &readfds);
359                 if (se->fd >= max_fd)
360                         max_fd = se->fd + 1;
361         }
362
363         if (max_fd == -1)
364         {
365                 syslog (LOG_WARNING, "No listen sockets found!");
366                 return (-1);
367         }
368
369         status = select (max_fd, &readfds, NULL, NULL, NULL);
370
371         if (status == -1)
372         {
373                 if (errno != EINTR)
374                         syslog (LOG_ERR, "select: %s", strerror (errno));
375                 return (-1);
376         }
377
378         fd = -1;
379         for (se = socklist_head; se != NULL; se = se->next)
380         {
381                 if (se->mode != operating_mode)
382                         continue;
383
384                 if (FD_ISSET (se->fd, &readfds))
385                 {
386                         fd = se->fd;
387                         break;
388                 }
389         }
390
391         if (fd == -1)
392                 syslog (LOG_WARNING, "No socket ready..?");
393
394         DBG ("fd = %i", fd);
395         return (fd);
396 }
397
398 int network_receive (char **host, char **type, char **inst, char **value)
399 {
400         int fd;
401         char buffer[BUFF_SIZE];
402
403         struct sockaddr_storage addr;
404         socklen_t               addrlen;
405         int status;
406
407         char *fields[4];
408
409         assert (operating_mode == MODE_SERVER);
410
411         *host  = NULL;
412         *type  = NULL;
413         *inst  = NULL;
414         *value = NULL;
415
416         if ((fd = network_get_listen_socket ()) < 0)
417                 return (-1);
418
419         addrlen = sizeof (addr);
420         if (recvfrom (fd, buffer, BUFF_SIZE, 0, (struct sockaddr *) &addr, &addrlen) == -1)
421         {
422                 syslog (LOG_ERR, "recvfrom: %s", strerror (errno));
423                 return (-1);
424         }
425
426         if ((*host = (char *) malloc (BUFF_SIZE)) == NULL)
427         {
428                 syslog (LOG_EMERG, "malloc: %s", strerror (errno));
429                 return (-1);
430         }
431
432         status = getnameinfo ((struct sockaddr *) &addr, addrlen,
433                         *host, BUFF_SIZE, NULL, 0, 0);
434         if (status != 0)
435         {
436                 free (*host); *host = NULL;
437                 syslog (LOG_ERR, "getnameinfo: %s",
438                                 status == EAI_SYSTEM ? strerror (errno) : gai_strerror (status));
439                 return (-1);
440         }
441
442         if (strsplit (buffer, fields, 4) != 3)
443         {
444                 syslog (LOG_WARNING, "Invalid message from `%s'", *host);
445                 free (*host); *host = NULL;
446                 return (-1);
447         }
448
449         if ((*type = strdup (fields[0])) == NULL)
450         {
451                 syslog (LOG_EMERG, "strdup: %s", strerror (errno));
452                 free (*host); *host = NULL;
453                 return (-1);
454         }
455
456         if ((*inst = strdup (fields[1])) == NULL)
457         {
458                 syslog (LOG_EMERG, "strdup: %s", strerror (errno));
459                 free (*host); *host = NULL;
460                 free (*type); *type = NULL;
461                 return (-1);
462         }
463
464         if ((*value = strdup (fields[2])) == NULL)
465         {
466                 syslog (LOG_EMERG, "strdup: %s", strerror (errno));
467                 free (*host); *host = NULL;
468                 free (*type); *type = NULL;
469                 free (*inst); *inst = NULL;
470                 return (-1);
471         }
472
473         DBG ("host = %s, type = %s, inst = %s, value = %s",
474                         *host, *type, *inst, *value);
475
476         return (0);
477 }
478
479 int network_send (char *type, char *inst, char *value)
480 {
481         char buf[BUFF_SIZE];
482         int buflen;
483
484         sockent_t *se;
485
486         int ret;
487         int status;
488
489         DBG ("type = %s, inst = %s, value = %s", type, inst, value);
490
491         assert (operating_mode == MODE_CLIENT);
492
493         buflen = snprintf (buf, BUFF_SIZE, "%s %s %s", type, inst, value);
494         if ((buflen >= BUFF_SIZE) || (buflen < 1))
495         {
496                 syslog (LOG_WARNING, "network_send: snprintf failed..");
497                 return (-1);
498         }
499         buf[buflen] = '\0';
500         buflen++;
501
502         if (socklist_head == NULL)
503                 network_connect_default ();
504
505         ret = 0;
506         for (se = socklist_head; se != NULL; se = se->next)
507         {
508                 if (se->mode != operating_mode)
509                         continue;
510
511                 DBG ("fd = %i", se->fd);
512
513                 while (1)
514                 {
515                         status = sendto (se->fd, buf, buflen, 0,
516                                         (struct sockaddr *) se->addr, se->addrlen);
517
518                         if (status == -1)
519                         {
520                                 if (errno == EINTR)
521                                 {
522                                         DBG ("sendto was interrupted");
523                                         continue;
524                                 }
525                                 else
526                                 {
527                                         syslog (LOG_ERR, "sendto: %s", strerror (errno));
528                                         ret = -1;
529                                         break;
530                                 }
531                         }
532                         else if (ret >= 0)
533                                 ret++;
534                         break;
535                 }
536         }
537
538         if (ret == 0)
539                 syslog (LOG_WARNING, "Message wasn't sent to anybody..");
540
541         return (ret);
542 }