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 <stdlib.h>
28 #include <stdio.h>
29 #include <unistd.h>
30 #include <string.h>
31 #include <errno.h>
32 #include <assert.h>
34 #include <sys/types.h>
35 #include <sys/socket.h>
36 #include <netdb.h>
38 #include "collectd/network.h"
39 #include "collectd/network_buffer.h"
41 /*
42 * Private data types
43 */
44 struct lcc_network_s
45 {
46 lcc_server_t *servers;
47 };
49 struct lcc_server_s
50 {
51 char *node;
52 char *service;
54 int ttl;
55 lcc_security_level_t security_level;
56 char *username;
57 char *password;
59 int fd;
60 struct sockaddr *sa;
61 socklen_t sa_len;
63 lcc_network_buffer_t *buffer;
65 lcc_server_t *next;
66 };
68 /*
69 * Private functions
70 */
71 static int server_close_socket (lcc_server_t *srv) /* {{{ */
72 {
73 if (srv == NULL)
74 return (EINVAL);
76 if (srv->fd < 0)
77 return (0);
79 close (srv->fd);
80 free (srv->sa);
81 srv->sa = NULL;
82 srv->sa_len = 0;
84 return (0);
85 } /* }}} int server_close_socket */
87 static void int_server_destroy (lcc_server_t *srv) /* {{{ */
88 {
89 lcc_server_t *next;
91 if (srv == NULL)
92 return;
94 server_close_socket (srv);
96 next = srv->next;
98 if (srv->fd >= 0)
99 {
100 close (srv->fd);
101 srv->fd = -1;
102 }
104 free (srv->node);
105 free (srv->service);
106 free (srv->username);
107 free (srv->password);
108 free (srv);
110 int_server_destroy (next);
111 } /* }}} void int_server_destroy */
113 static int server_open_socket (lcc_server_t *srv) /* {{{ */
114 {
115 struct addrinfo ai_hints = { 0 };
116 struct addrinfo *ai_list = NULL;
117 struct addrinfo *ai_ptr;
118 int status;
120 if (srv == NULL)
121 return (EINVAL);
123 if (srv->fd >= 0)
124 server_close_socket (srv);
126 #ifdef AI_ADDRCONFIG
127 ai_hints.ai_flags |= AI_ADDRCONFIG;
128 #endif
129 ai_hints.ai_family = AF_UNSPEC;
130 ai_hints.ai_socktype = SOCK_DGRAM;
132 status = getaddrinfo (srv->node, srv->service, &ai_hints, &ai_list);
133 if (status != 0)
134 return (status);
135 assert (ai_list != NULL);
137 for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
138 {
139 srv->fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
140 if (srv->fd < 0)
141 continue;
143 if (ai_ptr->ai_family == AF_INET)
144 {
146 struct sockaddr_in *addr = (struct sockaddr_in *) ai_ptr->ai_addr;
147 int optname;
149 if (IN_MULTICAST (ntohl (addr->sin_addr.s_addr)))
150 optname = IP_MULTICAST_TTL;
151 else
152 optname = IP_TTL;
154 setsockopt (srv->fd, IPPROTO_IP, optname,
155 &srv->ttl,
156 sizeof (srv->ttl));
157 }
158 else if (ai_ptr->ai_family == AF_INET6)
159 {
160 /* Useful example: http://gsyc.escet.urjc.es/~eva/IPv6-web/examples/mcast.html */
161 struct sockaddr_in6 *addr = (struct sockaddr_in6 *) ai_ptr->ai_addr;
162 int optname;
164 if (IN6_IS_ADDR_MULTICAST (&addr->sin6_addr))
165 optname = IPV6_MULTICAST_HOPS;
166 else
167 optname = IPV6_UNICAST_HOPS;
169 setsockopt (srv->fd, IPPROTO_IPV6, optname,
170 &srv->ttl,
171 sizeof (srv->ttl));
172 }
174 srv->sa = malloc (ai_ptr->ai_addrlen);
175 if (srv->sa == NULL)
176 {
177 close (srv->fd);
178 srv->fd = -1;
179 continue;
180 }
182 memcpy (srv->sa, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
183 srv->sa_len = ai_ptr->ai_addrlen;
184 break;
185 }
187 freeaddrinfo (ai_list);
189 if (srv->fd < 0)
190 return (-1);
191 return (0);
192 } /* }}} int server_open_socket */
194 static int server_send_buffer (lcc_server_t *srv) /* {{{ */
195 {
196 char buffer[LCC_NETWORK_BUFFER_SIZE_DEFAULT];
197 size_t buffer_size;
198 int status;
200 if (srv->fd < 0)
201 {
202 status = server_open_socket (srv);
203 if (status != 0)
204 return (status);
205 }
207 memset (buffer, 0, sizeof (buffer));
208 buffer_size = sizeof (buffer);
210 lcc_network_buffer_finalize (srv->buffer);
211 status = lcc_network_buffer_get (srv->buffer, buffer, &buffer_size);
212 lcc_network_buffer_initialize (srv->buffer);
214 if (status != 0)
215 return (status);
217 if (buffer_size > sizeof (buffer))
218 buffer_size = sizeof (buffer);
220 while (42)
221 {
222 assert (srv->fd >= 0);
223 assert (srv->sa != NULL);
224 status = (int) sendto (srv->fd, buffer, buffer_size, /* flags = */ 0,
225 srv->sa, srv->sa_len);
226 if ((status < 0) && ((errno == EINTR) || (errno == EAGAIN)))
227 continue;
229 break;
230 }
232 if (status < 0)
233 return (status);
234 return (0);
235 } /* }}} int server_send_buffer */
237 static int server_value_add (lcc_server_t *srv, /* {{{ */
238 const lcc_value_list_t *vl)
239 {
240 int status;
242 status = lcc_network_buffer_add_value (srv->buffer, vl);
243 if (status == 0)
244 return (0);
246 server_send_buffer (srv);
247 return (lcc_network_buffer_add_value (srv->buffer, vl));
248 } /* }}} int server_value_add */
250 /*
251 * Public functions
252 */
253 lcc_network_t *lcc_network_create (void) /* {{{ */
254 {
255 lcc_network_t *net;
257 net = malloc (sizeof (*net));
258 if (net == NULL)
259 return (NULL);
260 memset (net, 0, sizeof (*net));
262 net->servers = NULL;
264 return (net);
265 } /* }}} lcc_network_t *lcc_network_create */
267 void lcc_network_destroy (lcc_network_t *net) /* {{{ */
268 {
269 if (net == NULL)
270 return;
271 int_server_destroy (net->servers);
272 free (net);
273 } /* }}} void lcc_network_destroy */
275 lcc_server_t *lcc_server_create (lcc_network_t *net, /* {{{ */
276 const char *node, const char *service)
277 {
278 lcc_server_t *srv;
280 if ((net == NULL) || (node == NULL))
281 return (NULL);
282 if (service == NULL)
283 service = NET_DEFAULT_PORT;
285 srv = malloc (sizeof (*srv));
286 if (srv == NULL)
287 return (NULL);
288 memset (srv, 0, sizeof (*srv));
290 srv->fd = -1;
291 srv->security_level = NONE;
292 srv->username = NULL;
293 srv->password = NULL;
294 srv->next = NULL;
296 srv->node = strdup (node);
297 if (srv->node == NULL)
298 {
299 free (srv);
300 return (NULL);
301 }
303 srv->service = strdup (service);
304 if (srv->service == NULL)
305 {
306 free (srv->node);
307 free (srv);
308 return (NULL);
309 }
311 srv->buffer = lcc_network_buffer_create (/* size = */ 0);
312 if (srv->buffer == NULL)
313 {
314 free (srv->service);
315 free (srv->node);
316 free (srv);
317 return (NULL);
318 }
320 if (net->servers == NULL)
321 {
322 net->servers = srv;
323 }
324 else
325 {
326 lcc_server_t *last = net->servers;
328 while (last->next != NULL)
329 last = last->next;
331 last->next = srv;
332 }
334 return (srv);
335 } /* }}} lcc_server_t *lcc_server_create */
337 int lcc_server_destroy (lcc_network_t *net, lcc_server_t *srv) /* {{{ */
338 {
339 if ((net == NULL) || (srv == NULL))
340 return (EINVAL);
342 if (net->servers == srv)
343 {
344 net->servers = srv->next;
345 srv->next = NULL;
346 }
347 else
348 {
349 lcc_server_t *prev = net->servers;
351 while ((prev != NULL) && (prev->next != srv))
352 prev = prev->next;
354 if (prev == NULL)
355 return (ENOENT);
357 prev->next = srv->next;
358 srv->next = NULL;
359 }
361 int_server_destroy (srv);
363 return (0);
364 } /* }}} int lcc_server_destroy */
366 int lcc_server_set_ttl (lcc_server_t *srv, uint8_t ttl) /* {{{ */
367 {
368 if (srv == NULL)
369 return (EINVAL);
371 srv->ttl = (int) ttl;
373 return (0);
374 } /* }}} int lcc_server_set_ttl */
376 int lcc_server_set_security_level (lcc_server_t *srv, /* {{{ */
377 lcc_security_level_t level,
378 const char *username, const char *password)
379 {
380 return (lcc_network_buffer_set_security_level (srv->buffer,
381 level, username, password));
382 } /* }}} int lcc_server_set_security_level */
384 int lcc_network_values_send (lcc_network_t *net, /* {{{ */
385 const lcc_value_list_t *vl)
386 {
387 lcc_server_t *srv;
389 if ((net == NULL) || (vl == NULL))
390 return (EINVAL);
392 for (srv = net->servers; srv != NULL; srv = srv->next)
393 server_value_add (srv, vl);
395 return (0);
396 } /* }}} int lcc_network_values_send */
398 /* vim: set sw=2 sts=2 et fdm=marker : */