1 /**
2 * collectd - src/libcollectdclient/network.c
3 * Copyright (C) 2005-2012 Florian octo Forster
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 *
23 * Authors:
24 * Florian octo Forster <octo at collectd.org>
25 **/
27 #include "collectd.h"
29 #include <stdlib.h>
30 #include <stdio.h>
31 #include <unistd.h>
32 #include <string.h>
33 #include <errno.h>
34 #include <assert.h>
36 #include <sys/types.h>
37 #include <sys/socket.h>
38 #include <netdb.h>
40 #if HAVE_NETINET_IN_H
41 # include <netinet/in.h>
42 #endif
44 #include "collectd/network.h"
45 #include "collectd/network_buffer.h"
47 /*
48 * Private data types
49 */
50 struct lcc_network_s
51 {
52 lcc_server_t *servers;
53 };
55 struct lcc_server_s
56 {
57 char *node;
58 char *service;
60 int ttl;
61 lcc_security_level_t security_level;
62 char *username;
63 char *password;
65 int fd;
66 struct sockaddr *sa;
67 socklen_t sa_len;
69 lcc_network_buffer_t *buffer;
71 lcc_server_t *next;
72 };
74 /*
75 * Private functions
76 */
77 static int server_close_socket (lcc_server_t *srv) /* {{{ */
78 {
79 if (srv == NULL)
80 return (EINVAL);
82 if (srv->fd < 0)
83 return (0);
85 close (srv->fd);
86 free (srv->sa);
87 srv->sa = NULL;
88 srv->sa_len = 0;
90 return (0);
91 } /* }}} int server_close_socket */
93 static void int_server_destroy (lcc_server_t *srv) /* {{{ */
94 {
95 lcc_server_t *next;
97 if (srv == NULL)
98 return;
100 server_close_socket (srv);
102 next = srv->next;
104 if (srv->fd >= 0)
105 {
106 close (srv->fd);
107 srv->fd = -1;
108 }
110 free (srv->node);
111 free (srv->service);
112 free (srv->username);
113 free (srv->password);
114 free (srv);
116 int_server_destroy (next);
117 } /* }}} void int_server_destroy */
119 static int server_open_socket (lcc_server_t *srv) /* {{{ */
120 {
121 struct addrinfo ai_hints = { 0 };
122 struct addrinfo *ai_list = NULL;
123 struct addrinfo *ai_ptr;
124 int status;
126 if (srv == NULL)
127 return (EINVAL);
129 if (srv->fd >= 0)
130 server_close_socket (srv);
132 #ifdef AI_ADDRCONFIG
133 ai_hints.ai_flags |= AI_ADDRCONFIG;
134 #endif
135 ai_hints.ai_family = AF_UNSPEC;
136 ai_hints.ai_socktype = SOCK_DGRAM;
138 status = getaddrinfo (srv->node, srv->service, &ai_hints, &ai_list);
139 if (status != 0)
140 return (status);
141 assert (ai_list != NULL);
143 for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
144 {
145 srv->fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
146 if (srv->fd < 0)
147 continue;
149 if (ai_ptr->ai_family == AF_INET)
150 {
151 struct sockaddr_in *addr = (struct sockaddr_in *) ai_ptr->ai_addr;
152 int optname;
154 if (IN_MULTICAST (ntohl (addr->sin_addr.s_addr)))
155 optname = IP_MULTICAST_TTL;
156 else
157 optname = IP_TTL;
159 status = setsockopt (srv->fd, IPPROTO_IP, optname,
160 &srv->ttl, sizeof (srv->ttl));
161 }
162 else if (ai_ptr->ai_family == AF_INET6)
163 {
164 /* Useful example: http://gsyc.escet.urjc.es/~eva/IPv6-web/examples/mcast.html */
165 struct sockaddr_in6 *addr = (struct sockaddr_in6 *) ai_ptr->ai_addr;
166 int optname;
168 if (IN6_IS_ADDR_MULTICAST (&addr->sin6_addr))
169 optname = IPV6_MULTICAST_HOPS;
170 else
171 optname = IPV6_UNICAST_HOPS;
173 status = setsockopt (srv->fd, IPPROTO_IPV6, optname,
174 &srv->ttl, sizeof (srv->ttl));
175 }
176 if (status != 0)
177 {
178 /* setsockopt failed. */
179 close (srv->fd);
180 srv->fd = -1;
181 continue;
182 }
184 srv->sa = malloc (ai_ptr->ai_addrlen);
185 if (srv->sa == NULL)
186 {
187 close (srv->fd);
188 srv->fd = -1;
189 continue;
190 }
192 memcpy (srv->sa, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
193 srv->sa_len = ai_ptr->ai_addrlen;
194 break;
195 }
197 freeaddrinfo (ai_list);
199 if (srv->fd < 0)
200 return (-1);
201 return (0);
202 } /* }}} int server_open_socket */
204 static int server_send_buffer (lcc_server_t *srv) /* {{{ */
205 {
206 char buffer[LCC_NETWORK_BUFFER_SIZE_DEFAULT];
207 size_t buffer_size;
208 int status;
210 if (srv->fd < 0)
211 {
212 status = server_open_socket (srv);
213 if (status != 0)
214 return (status);
215 }
217 memset (buffer, 0, sizeof (buffer));
218 buffer_size = sizeof (buffer);
220 lcc_network_buffer_finalize (srv->buffer);
221 status = lcc_network_buffer_get (srv->buffer, buffer, &buffer_size);
222 lcc_network_buffer_initialize (srv->buffer);
224 if (status != 0)
225 return (status);
227 if (buffer_size > sizeof (buffer))
228 buffer_size = sizeof (buffer);
230 while (42)
231 {
232 assert (srv->fd >= 0);
233 assert (srv->sa != NULL);
234 status = (int) sendto (srv->fd, buffer, buffer_size, /* flags = */ 0,
235 srv->sa, srv->sa_len);
236 if ((status < 0) && ((errno == EINTR) || (errno == EAGAIN)))
237 continue;
239 break;
240 }
242 if (status < 0)
243 return (status);
244 return (0);
245 } /* }}} int server_send_buffer */
247 static int server_value_add (lcc_server_t *srv, /* {{{ */
248 const lcc_value_list_t *vl)
249 {
250 int status;
252 status = lcc_network_buffer_add_value (srv->buffer, vl);
253 if (status == 0)
254 return (0);
256 server_send_buffer (srv);
257 return (lcc_network_buffer_add_value (srv->buffer, vl));
258 } /* }}} int server_value_add */
260 /*
261 * Public functions
262 */
263 lcc_network_t *lcc_network_create (void) /* {{{ */
264 {
265 lcc_network_t *net;
267 net = malloc (sizeof (*net));
268 if (net == NULL)
269 return (NULL);
270 memset (net, 0, sizeof (*net));
272 net->servers = NULL;
274 return (net);
275 } /* }}} lcc_network_t *lcc_network_create */
277 void lcc_network_destroy (lcc_network_t *net) /* {{{ */
278 {
279 if (net == NULL)
280 return;
281 int_server_destroy (net->servers);
282 free (net);
283 } /* }}} void lcc_network_destroy */
285 lcc_server_t *lcc_server_create (lcc_network_t *net, /* {{{ */
286 const char *node, const char *service)
287 {
288 lcc_server_t *srv;
290 if ((net == NULL) || (node == NULL))
291 return (NULL);
292 if (service == NULL)
293 service = NET_DEFAULT_PORT;
295 srv = malloc (sizeof (*srv));
296 if (srv == NULL)
297 return (NULL);
298 memset (srv, 0, sizeof (*srv));
300 srv->fd = -1;
301 srv->security_level = NONE;
302 srv->username = NULL;
303 srv->password = NULL;
304 srv->next = NULL;
306 srv->node = strdup (node);
307 if (srv->node == NULL)
308 {
309 free (srv);
310 return (NULL);
311 }
313 srv->service = strdup (service);
314 if (srv->service == NULL)
315 {
316 free (srv->node);
317 free (srv);
318 return (NULL);
319 }
321 srv->buffer = lcc_network_buffer_create (/* size = */ 0);
322 if (srv->buffer == NULL)
323 {
324 free (srv->service);
325 free (srv->node);
326 free (srv);
327 return (NULL);
328 }
330 if (net->servers == NULL)
331 {
332 net->servers = srv;
333 }
334 else
335 {
336 lcc_server_t *last = net->servers;
338 while (last->next != NULL)
339 last = last->next;
341 last->next = srv;
342 }
344 return (srv);
345 } /* }}} lcc_server_t *lcc_server_create */
347 int lcc_server_destroy (lcc_network_t *net, lcc_server_t *srv) /* {{{ */
348 {
349 if ((net == NULL) || (srv == NULL))
350 return (EINVAL);
352 if (net->servers == srv)
353 {
354 net->servers = srv->next;
355 srv->next = NULL;
356 }
357 else
358 {
359 lcc_server_t *prev = net->servers;
361 while ((prev != NULL) && (prev->next != srv))
362 prev = prev->next;
364 if (prev == NULL)
365 return (ENOENT);
367 prev->next = srv->next;
368 srv->next = NULL;
369 }
371 int_server_destroy (srv);
373 return (0);
374 } /* }}} int lcc_server_destroy */
376 int lcc_server_set_ttl (lcc_server_t *srv, uint8_t ttl) /* {{{ */
377 {
378 if (srv == NULL)
379 return (EINVAL);
381 srv->ttl = (int) ttl;
383 return (0);
384 } /* }}} int lcc_server_set_ttl */
386 int lcc_server_set_security_level (lcc_server_t *srv, /* {{{ */
387 lcc_security_level_t level,
388 const char *username, const char *password)
389 {
390 return (lcc_network_buffer_set_security_level (srv->buffer,
391 level, username, password));
392 } /* }}} int lcc_server_set_security_level */
394 int lcc_network_values_send (lcc_network_t *net, /* {{{ */
395 const lcc_value_list_t *vl)
396 {
397 lcc_server_t *srv;
399 if ((net == NULL) || (vl == NULL))
400 return (EINVAL);
402 for (srv = net->servers; srv != NULL; srv = srv->next)
403 server_value_add (srv, vl);
405 return (0);
406 } /* }}} int lcc_network_values_send */
408 /* vim: set sw=2 sts=2 et fdm=marker : */