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