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