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