3fe25fd46274c5d508a4e38b7877dca7640f1a8a
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 **/
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>
34 #include "network.h"
35 #include "common.h"
36 #include "utils_debug.h"
38 /* 1500 - 40 - 8 = Ethernet packet - IPv6 header - UDP header */
39 /* #define BUFF_SIZE 1452 */
41 #define BUFF_SIZE 4096
43 #ifdef HAVE_LIBRRD
44 extern int operating_mode;
45 #else
46 static int operating_mode = MODE_CLIENT;
47 #endif
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;
58 static sockent_t *socklist_head = NULL;
60 static int network_bind_socket (const sockent_t *se, const struct addrinfo *ai)
61 {
62 int loop = 1;
64 DBG ("fd = %i; calling `bind'", se->fd);
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 }
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;
79 DBG ("fd = %i; IPv4 multicast address found", se->fd);
81 mreq.imr_multiaddr.s_addr = addr->sin_addr.s_addr;
82 mreq.imr_interface.s_addr = htonl (INADDR_ANY);
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 }
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;
107 DBG ("fd = %i; IPv6 multicast address found", se->fd);
109 memcpy (&mreq.ipv6mr_multiaddr,
110 &addr->sin6_addr,
111 sizeof (addr->sin6_addr));
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;
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 }
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 }
140 return (0);
141 }
143 int network_create_socket (const char *node, const char *service)
144 {
145 sockent_t *socklist_tail;
147 struct addrinfo ai_hints;
148 struct addrinfo *ai_list, *ai_ptr;
149 int ai_return;
151 int num_added = 0;
153 DBG ("node = %s, service = %s", node, service);
155 if (operating_mode == MODE_LOCAL)
156 return (-1);
158 socklist_tail = socklist_head;
159 while ((socklist_tail != NULL) && (socklist_tail->next != NULL))
160 socklist_tail = socklist_tail->next;
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;
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 }
177 for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
178 {
179 sockent_t *se;
181 if ((se = (sockent_t *) malloc (sizeof (sockent_t))) == NULL)
182 {
183 syslog (LOG_EMERG, "malloc: %s", strerror (errno));
184 continue;
185 }
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 }
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;
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;
203 if (se->fd == -1)
204 {
205 syslog (LOG_ERR, "socket: %s", strerror (errno));
206 free (se->addr);
207 free (se);
208 continue;
209 }
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 }
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 }
230 num_added++;
232 /* We don't open more than one write-socket per node/service pair.. */
233 if (operating_mode == MODE_CLIENT)
234 break;
235 }
237 freeaddrinfo (ai_list);
239 return (num_added);
240 }
242 static int network_connect_default (void)
243 {
244 int ret;
246 if (socklist_head != NULL)
247 return (0);
249 DBG ("socklist_head is NULL");
251 ret = 0;
253 if (network_create_socket (NET_DEFAULT_V6_ADDR, NET_DEFAULT_PORT) > 0)
254 ret++;
256 /* Don't use IPv4 and IPv6 in parallel by default.. */
257 if ((operating_mode == MODE_CLIENT) && (ret != 0))
258 return (ret);
260 if (network_create_socket (NET_DEFAULT_V4_ADDR, NET_DEFAULT_PORT) > 0)
261 ret++;
263 if (ret == 0)
264 ret = -1;
266 return (ret);
267 }
269 static int network_get_listen_socket (void)
270 {
271 int fd;
272 int max_fd;
273 int status;
275 fd_set readfds;
276 sockent_t *se;
278 if (socklist_head == NULL)
279 network_connect_default ();
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;
288 FD_SET (se->fd, &readfds);
289 if (se->fd >= max_fd)
290 max_fd = se->fd + 1;
291 }
293 if (max_fd == -1)
294 {
295 syslog (LOG_WARNING, "No listen sockets found!");
296 return (-1);
297 }
299 status = select (max_fd, &readfds, NULL, NULL, NULL);
301 if (status == -1)
302 {
303 if (errno != EINTR)
304 syslog (LOG_ERR, "select: %s", strerror (errno));
305 return (-1);
306 }
308 fd = -1;
309 for (se = socklist_head; se != NULL; se = se->next)
310 {
311 if (se->mode != operating_mode)
312 continue;
314 if (FD_ISSET (se->fd, &readfds))
315 {
316 fd = se->fd;
317 break;
318 }
319 }
321 if (fd == -1)
322 syslog (LOG_WARNING, "No socket ready..?");
324 DBG ("fd = %i", fd);
325 return (fd);
326 }
328 int network_receive (char **host, char **type, char **inst, char **value)
329 {
330 int fd;
331 char buffer[BUFF_SIZE];
333 struct sockaddr_storage addr;
334 socklen_t addrlen;
335 int status;
337 char *fields[4];
339 assert (operating_mode == MODE_SERVER);
341 *host = NULL;
342 *type = NULL;
343 *inst = NULL;
344 *value = NULL;
346 if ((fd = network_get_listen_socket ()) < 0)
347 return (-1);
349 addrlen = sizeof (addr);
350 if (recvfrom (fd, buffer, BUFF_SIZE, 0, (struct sockaddr *) &addr, &addrlen) == -1)
351 {
352 syslog (LOG_ERR, "recvfrom: %s", strerror (errno));
353 return (-1);
354 }
356 if ((*host = (char *) malloc (BUFF_SIZE)) == NULL)
357 {
358 syslog (LOG_EMERG, "malloc: %s", strerror (errno));
359 return (-1);
360 }
362 status = getnameinfo ((struct sockaddr *) &addr, addrlen,
363 *host, BUFF_SIZE, NULL, 0, 0);
364 if (status != 0)
365 {
366 free (*host); *host = NULL;
367 syslog (LOG_ERR, "getnameinfo: %s",
368 status == EAI_SYSTEM ? strerror (errno) : gai_strerror (status));
369 return (-1);
370 }
372 if (strsplit (buffer, fields, 4) != 3)
373 {
374 syslog (LOG_WARNING, "Invalid message from `%s'", *host);
375 free (*host); *host = NULL;
376 return (-1);
377 }
379 if ((*type = strdup (fields[0])) == NULL)
380 {
381 syslog (LOG_EMERG, "strdup: %s", strerror (errno));
382 free (*host); *host = NULL;
383 return (-1);
384 }
386 if ((*inst = strdup (fields[1])) == NULL)
387 {
388 syslog (LOG_EMERG, "strdup: %s", strerror (errno));
389 free (*host); *host = NULL;
390 free (*type); *type = NULL;
391 return (-1);
392 }
394 if ((*value = strdup (fields[2])) == NULL)
395 {
396 syslog (LOG_EMERG, "strdup: %s", strerror (errno));
397 free (*host); *host = NULL;
398 free (*type); *type = NULL;
399 free (*inst); *inst = NULL;
400 return (-1);
401 }
403 DBG ("host = %s, type = %s, inst = %s, value = %s",
404 *host, *type, *inst, *value);
406 return (0);
407 }
409 int network_send (char *type, char *inst, char *value)
410 {
411 char buf[BUFF_SIZE];
412 int buflen;
414 sockent_t *se;
416 int ret;
417 int status;
419 DBG ("type = %s, inst = %s, value = %s", type, inst, value);
421 assert (operating_mode == MODE_CLIENT);
423 buflen = snprintf (buf, BUFF_SIZE, "%s %s %s", type, inst, value);
424 if ((buflen >= BUFF_SIZE) || (buflen < 1))
425 {
426 syslog (LOG_WARNING, "network_send: snprintf failed..");
427 return (-1);
428 }
429 buf[buflen] = '\0';
430 buflen++;
432 if (socklist_head == NULL)
433 network_connect_default ();
435 ret = 0;
436 for (se = socklist_head; se != NULL; se = se->next)
437 {
438 if (se->mode != operating_mode)
439 continue;
441 DBG ("fd = %i", se->fd);
443 while (1)
444 {
445 status = sendto (se->fd, buf, buflen, 0,
446 (struct sockaddr *) se->addr, se->addrlen);
448 if (status == -1)
449 {
450 if (errno == EINTR)
451 {
452 DBG ("sendto was interrupted");
453 continue;
454 }
455 else
456 {
457 syslog (LOG_ERR, "sendto: %s", strerror (errno));
458 ret = -1;
459 break;
460 }
461 }
462 else if (ret >= 0)
463 ret++;
464 break;
465 }
466 }
468 if (ret == 0)
469 syslog (LOG_WARNING, "Message wasn't sent to anybody..");
471 return (ret);
472 }