Updated `configure.in'
[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 "utils_debug.h"
37
38 /* 1500 - 40 - 8  =  Ethernet packet - IPv6 header - UDP header */
39 /* #define BUFF_SIZE 1452 */
40
41 #define BUFF_SIZE 4096
42
43 #ifdef HAVE_LIBRRD
44 extern int operating_mode;
45 #else
46 static int operating_mode = MODE_CLIENT;
47 #endif
48
49 typedef struct sockent
50 {
51         int                      fd;
52         int                      mode;
53         struct sockaddr_storage *addr;
54         socklen_t                addrlen;
55         struct sockent          *next;
56 } sockent_t;
57
58 static sockent_t *socklist_head = NULL;
59
60 static int network_bind_socket (const sockent_t *se, const struct addrinfo *ai)
61 {
62         int loop = 1;
63
64         DBG ("fd = %i; calling `bind'", se->fd);
65
66         if (bind (se->fd, ai->ai_addr, ai->ai_addrlen) == -1)
67         {
68                 syslog (LOG_ERR, "bind: %s", strerror (errno));
69                 return (-1);
70         }
71
72         if (ai->ai_family == AF_INET)
73         {
74                 struct sockaddr_in *addr = (struct sockaddr_in *) ai->ai_addr;
75                 if (IN_MULTICAST (ntohl (addr->sin_addr.s_addr)))
76                 {
77                         struct ip_mreq mreq;
78
79                         DBG ("fd = %i; IPv4 multicast address found", se->fd);
80
81                         mreq.imr_multiaddr.s_addr = addr->sin_addr.s_addr;
82                         mreq.imr_interface.s_addr = htonl (INADDR_ANY);
83
84                         if (setsockopt (se->fd, IPPROTO_IP, IP_MULTICAST_LOOP,
85                                                 &loop, sizeof (loop)) == -1)
86                         {
87                                 syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
88                                 return (-1);
89                         }
90
91                         if (setsockopt (se->fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
92                                                 &mreq, sizeof (mreq)) == -1)
93                         {
94                                 syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
95                                 return (-1);
96                         }
97                 }
98         }
99         else if (ai->ai_family == AF_INET6)
100         {
101                 /* Useful example: http://gsyc.escet.urjc.es/~eva/IPv6-web/examples/mcast.html */
102                 struct sockaddr_in6 *addr = (struct sockaddr_in6 *) ai->ai_addr;
103                 if (IN6_IS_ADDR_MULTICAST (&addr->sin6_addr))
104                 {
105                         struct ipv6_mreq mreq;
106
107                         DBG ("fd = %i; IPv6 multicast address found", se->fd);
108
109                         memcpy (&mreq.ipv6mr_multiaddr,
110                                         &addr->sin6_addr,
111                                         sizeof (addr->sin6_addr));
112
113                         /* http://developer.apple.com/documentation/Darwin/Reference/ManPages/man4/ip6.4.html
114                          * ipv6mr_interface may be set to zeroes to
115                          * choose the default multicast interface or to
116                          * the index of a particular multicast-capable
117                          * interface if the host is multihomed.
118                          * Membership is associ-associated with a
119                          * single interface; programs running on
120                          * multihomed hosts may need to join the same
121                          * group on more than one interface.*/
122                         mreq.ipv6mr_interface = 0;
123
124                         if (setsockopt (se->fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
125                                                 &loop, sizeof (loop)) == -1)
126                         {
127                                 syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
128                                 return (-1);
129                         }
130
131                         if (setsockopt (se->fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP,
132                                                 &mreq, sizeof (mreq)) == -1)
133                         {
134                                 syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
135                                 return (-1);
136                         }
137                 }
138         }
139
140         return (0);
141 }
142
143 int network_create_socket (const char *node, const char *service)
144 {
145         sockent_t *socklist_tail;
146
147         struct addrinfo  ai_hints;
148         struct addrinfo *ai_list, *ai_ptr;
149         int              ai_return;
150
151         int num_added = 0;
152
153         DBG ("node = %s, service = %s", node, service);
154
155         if (operating_mode == MODE_LOCAL)
156                 return (-1);
157
158         socklist_tail = socklist_head;
159         while ((socklist_tail != NULL) && (socklist_tail->next != NULL))
160                 socklist_tail = socklist_tail->next;
161
162         memset (&ai_hints, '\0', sizeof (ai_hints));
163         ai_hints.ai_flags    = AI_PASSIVE | AI_ADDRCONFIG;
164         ai_hints.ai_family   = PF_UNSPEC;
165         ai_hints.ai_socktype = SOCK_DGRAM;
166         ai_hints.ai_protocol = IPPROTO_UDP; /* XXX is this right here?!? */
167
168         if ((ai_return = getaddrinfo (node, service, &ai_hints, &ai_list)) != 0)
169         {
170                 syslog (LOG_ERR, "getaddrinfo (%s, %s): %s",
171                                 node == NULL ? "(null)" : node,
172                                 service == NULL ? "(null)" : service,
173                                 ai_return == EAI_SYSTEM ? strerror (errno) : gai_strerror (ai_return));
174                 return (-1);
175         }
176
177         for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
178         {
179                 sockent_t *se;
180
181                 if ((se = (sockent_t *) malloc (sizeof (sockent_t))) == NULL)
182                 {
183                         syslog (LOG_EMERG, "malloc: %s", strerror (errno));
184                         continue;
185                 }
186
187                 if ((se->addr = (struct sockaddr_storage *) malloc (sizeof (struct sockaddr_storage))) == NULL)
188                 {
189                         syslog (LOG_EMERG, "malloc: %s", strerror (errno));
190                         free (se);
191                         continue;
192                 }
193
194                 assert (sizeof (struct sockaddr_storage) >= ai_ptr->ai_addrlen);
195                 memset (se->addr, '\0', sizeof (struct sockaddr_storage));
196                 memcpy (se->addr, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
197                 se->addrlen = ai_ptr->ai_addrlen;
198
199                 se->mode = operating_mode;
200                 se->fd   = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
201                 se->next = NULL;
202
203                 if (se->fd == -1)
204                 {
205                         syslog (LOG_ERR, "socket: %s", strerror (errno));
206                         free (se->addr);
207                         free (se);
208                         continue;
209                 }
210
211                 if (operating_mode == MODE_SERVER)
212                         if (network_bind_socket (se, ai_ptr) != 0)
213                         {
214                                 free (se->addr);
215                                 free (se);
216                                 continue;
217                         }
218
219                 if (socklist_tail == NULL)
220                 {
221                         socklist_head = se;
222                         socklist_tail = se;
223                 }
224                 else
225                 {
226                         socklist_tail->next = se;
227                         socklist_tail = se;
228                 }
229
230                 num_added++;
231
232                 /* We don't open more than one write-socket per node/service pair.. */
233                 if (operating_mode == MODE_CLIENT)
234                         break;
235         }
236
237         freeaddrinfo (ai_list);
238
239         return (num_added);
240 }
241
242 static int network_connect_default (void)
243 {
244         int ret;
245
246         if (socklist_head != NULL)
247                 return (0);
248
249         DBG ("socklist_head is NULL");
250
251         ret = 0;
252
253         if (network_create_socket (NET_DEFAULT_V6_ADDR, NET_DEFAULT_PORT) > 0)
254                 ret++;
255
256         /* Don't use IPv4 and IPv6 in parallel by default.. */
257         if ((operating_mode == MODE_CLIENT) && (ret != 0))
258                 return (ret);
259
260         if (network_create_socket (NET_DEFAULT_V4_ADDR, NET_DEFAULT_PORT) > 0)
261                 ret++;
262
263         if (ret == 0)
264                 ret = -1;
265
266         return (ret);
267 }
268
269 static int network_get_listen_socket (void)
270 {
271         int fd;
272         int max_fd;
273         int status;
274
275         fd_set readfds;
276         sockent_t *se;
277
278         if (socklist_head == NULL)
279                 network_connect_default ();
280
281         FD_ZERO (&readfds);
282         max_fd = -1;
283         for (se = socklist_head; se != NULL; se = se->next)
284         {
285                 if (se->mode != operating_mode)
286                         continue;
287
288                 FD_SET (se->fd, &readfds);
289                 if (se->fd >= max_fd)
290                         max_fd = se->fd + 1;
291         }
292
293         if (max_fd == -1)
294         {
295                 syslog (LOG_WARNING, "No listen sockets found!");
296                 return (-1);
297         }
298
299         status = select (max_fd, &readfds, NULL, NULL, NULL);
300
301         if (status == -1)
302         {
303                 if (errno != EINTR)
304                         syslog (LOG_ERR, "select: %s", strerror (errno));
305                 return (-1);
306         }
307
308         fd = -1;
309         for (se = socklist_head; se != NULL; se = se->next)
310         {
311                 if (se->mode != operating_mode)
312                         continue;
313
314                 if (FD_ISSET (se->fd, &readfds))
315                 {
316                         fd = se->fd;
317                         break;
318                 }
319         }
320
321         if (fd == -1)
322                 syslog (LOG_WARNING, "No socket ready..?");
323
324         DBG ("fd = %i", fd);
325         return (fd);
326 }
327
328 int network_receive (char **host, char **type, char **inst, char **value)
329 {
330         int fd;
331         char buffer[BUFF_SIZE];
332
333         struct sockaddr_storage addr;
334         socklen_t               addrlen;
335         int status;
336
337         char *fields[4];
338
339         assert (operating_mode == MODE_SERVER);
340
341         *host  = NULL;
342         *type  = NULL;
343         *inst  = NULL;
344         *value = NULL;
345
346         if ((fd = network_get_listen_socket ()) < 0)
347                 return (-1);
348
349         if (recvfrom (fd, buffer, BUFF_SIZE, 0, (struct sockaddr *) &addr, &addrlen) == -1)
350         {
351                 syslog (LOG_ERR, "recvfrom: %s", strerror (errno));
352                 return (-1);
353         }
354
355         if ((*host = (char *) malloc (BUFF_SIZE)) == NULL)
356         {
357                 syslog (LOG_EMERG, "malloc: %s", strerror (errno));
358                 return (-1);
359         }
360
361         status = getnameinfo ((struct sockaddr *) &addr, addrlen,
362                         *host, BUFF_SIZE, NULL, 0, 0);
363         if (status != 0)
364         {
365                 free (*host); *host = NULL;
366                 syslog (LOG_ERR, "getnameinfo: %s",
367                                 status == EAI_SYSTEM ? strerror (errno) : gai_strerror (status));
368                 return (-1);
369         }
370
371         if (strsplit (buffer, fields, 4) != 3)
372         {
373                 syslog (LOG_WARNING, "Invalid message from `%s'", *host);
374                 free (*host); *host = NULL;
375                 return (-1);
376         }
377
378         if ((*type = strdup (fields[0])) == NULL)
379         {
380                 syslog (LOG_EMERG, "strdup: %s", strerror (errno));
381                 free (*host); *host = NULL;
382                 return (-1);
383         }
384
385         if ((*inst = strdup (fields[1])) == NULL)
386         {
387                 syslog (LOG_EMERG, "strdup: %s", strerror (errno));
388                 free (*host); *host = NULL;
389                 free (*type); *type = NULL;
390                 return (-1);
391         }
392
393         if ((*value = strdup (fields[2])) == NULL)
394         {
395                 syslog (LOG_EMERG, "strdup: %s", strerror (errno));
396                 free (*host); *host = NULL;
397                 free (*type); *type = NULL;
398                 free (*inst); *inst = NULL;
399                 return (-1);
400         }
401
402         DBG ("host = %s, type = %s, inst = %s, value = %s",
403                         *host, *type, *inst, *value);
404
405         return (0);
406 }
407
408 int network_send (char *type, char *inst, char *value)
409 {
410         char buf[BUFF_SIZE];
411         int buflen;
412
413         sockent_t *se;
414
415         int ret;
416         int status;
417
418         DBG ("type = %s, inst = %s, value = %s", type, inst, value);
419
420         assert (operating_mode == MODE_CLIENT);
421
422         buflen = snprintf (buf, BUFF_SIZE, "%s %s %s", type, inst, value);
423         if ((buflen >= BUFF_SIZE) || (buflen < 1))
424         {
425                 syslog (LOG_WARNING, "network_send: snprintf failed..");
426                 return (-1);
427         }
428         buf[buflen] = '\0';
429         buflen++;
430
431         if (socklist_head == NULL)
432                 network_connect_default ();
433
434         ret = 0;
435         for (se = socklist_head; se != NULL; se = se->next)
436         {
437                 if (se->mode != operating_mode)
438                         continue;
439
440                 DBG ("fd = %i", se->fd);
441
442                 while (1)
443                 {
444                         status = sendto (se->fd, buf, buflen, 0,
445                                         (struct sockaddr *) se->addr, se->addrlen);
446
447                         if (status == -1)
448                         {
449                                 if (errno == EINTR)
450                                 {
451                                         DBG ("sendto was interrupted");
452                                         continue;
453                                 }
454                                 else
455                                 {
456                                         syslog (LOG_ERR, "sendto: %s", strerror (errno));
457                                         ret = -1;
458                                         break;
459                                 }
460                         }
461                         else if (ret >= 0)
462                                 ret++;
463                         break;
464                 }
465         }
466
467         if (ret == 0)
468                 syslog (LOG_WARNING, "Message wasn't sent to anybody..");
469
470         return (ret);
471 }