Merge branch 'master' into ff/netlib
[collectd.git] / src / libcollectdclient / network.c
1 /**
2  * collectd - src/libcollectdclient/network.h
3  * Copyright (C) 2005-2010  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 Lesser General Public License as published by
7  * the Free Software Foundation; only version 2.1 of the License is
8  * applicable.
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  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public License
16  * along with this program; if not, write to the Free Software Foundation,
17  * Inc., 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 <unistd.h>
26 #include <string.h>
27 #include <errno.h>
28 #include <assert.h>
29
30 #include <sys/types.h>
31 #include <sys/socket.h>
32 #include <netdb.h>
33
34 #include "collectd/network.h"
35 #include "collectd/network_buffer.h"
36
37 /*
38  * Private data types
39  */
40 struct lcc_network_s
41 {
42   lcc_server_t *servers;
43 };
44
45 struct lcc_server_s
46 {
47   char *node;
48   char *service;
49
50   int ttl;
51   lcc_security_level_t security_level;
52   char *username;
53   char *password;
54
55   int fd;
56   struct sockaddr *sa;
57   socklen_t sa_len;
58
59   lcc_network_buffer_t *buffer;
60
61   lcc_server_t *next;
62 };
63
64 /*
65  * Private functions
66  */
67 static int server_close_socket (lcc_server_t *srv) /* {{{ */
68 {
69   if (srv == NULL)
70     return (EINVAL);
71
72   if (srv->fd < 0)
73     return (0);
74
75   close (srv->fd);
76   free (srv->sa);
77   srv->sa = NULL;
78   srv->sa_len = 0;
79
80   return (0);
81 } /* }}} int server_close_socket */
82
83 static void int_server_destroy (lcc_server_t *srv) /* {{{ */
84 {
85   lcc_server_t *next;
86
87   if (srv == NULL)
88     return;
89
90   server_close_socket (srv);
91
92   next = srv->next;
93
94   if (srv->fd >= 0)
95   {
96     close (srv->fd);
97     srv->fd = -1;
98   }
99
100   free (srv->node);
101   free (srv->service);
102   free (srv->username);
103   free (srv->password);
104   free (srv);
105
106   int_server_destroy (next);
107 } /* }}} void int_server_destroy */
108
109 static int server_open_socket (lcc_server_t *srv) /* {{{ */
110 {
111   struct addrinfo ai_hints = { 0 };
112   struct addrinfo *ai_list = NULL;
113   struct addrinfo *ai_ptr;
114   int status;
115
116   if (srv == NULL)
117     return (EINVAL);
118
119   if (srv->fd >= 0)
120     server_close_socket (srv);
121
122 #ifdef AI_ADDRCONFIG
123   ai_hints.ai_flags |= AI_ADDRCONFIG;
124 #endif
125   ai_hints.ai_family   = AF_UNSPEC;
126   ai_hints.ai_socktype = SOCK_DGRAM;
127
128   status = getaddrinfo (srv->node, srv->service, &ai_hints, &ai_list);
129   if (status != 0)
130     return (status);
131   assert (ai_list != NULL);
132
133   for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
134   {
135     srv->fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
136     if (srv->fd < 0)
137       continue;
138
139     if (ai_ptr->ai_family == AF_INET)
140     {
141
142       struct sockaddr_in *addr = (struct sockaddr_in *) ai_ptr->ai_addr;
143       int optname;
144
145       if (IN_MULTICAST (ntohl (addr->sin_addr.s_addr)))
146         optname = IP_MULTICAST_TTL;
147       else
148         optname = IP_TTL;
149
150       setsockopt (srv->fd, IPPROTO_IP, optname,
151           &srv->ttl,
152           sizeof (srv->ttl));
153     }
154     else if (ai_ptr->ai_family == AF_INET6)
155     {
156       /* Useful example: http://gsyc.escet.urjc.es/~eva/IPv6-web/examples/mcast.html */
157       struct sockaddr_in6 *addr = (struct sockaddr_in6 *) ai_ptr->ai_addr;
158       int optname;
159
160       if (IN6_IS_ADDR_MULTICAST (&addr->sin6_addr))
161         optname = IPV6_MULTICAST_HOPS;
162       else
163         optname = IPV6_UNICAST_HOPS;
164
165       setsockopt (srv->fd, IPPROTO_IPV6, optname,
166           &srv->ttl,
167           sizeof (srv->ttl));
168     }
169
170     srv->sa = malloc (ai_ptr->ai_addrlen);
171     if (srv->sa == NULL)
172     {
173       close (srv->fd);
174       srv->fd = -1;
175       continue;
176     }
177
178     memcpy (srv->sa, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
179     srv->sa_len = ai_ptr->ai_addrlen;
180     break;
181   }
182
183   freeaddrinfo (ai_list);
184
185   if (srv->fd < 0)
186     return (-1);
187   return (0);
188 } /* }}} int server_open_socket */
189
190 static int server_send_buffer (lcc_server_t *srv) /* {{{ */
191 {
192   char buffer[LCC_NETWORK_BUFFER_SIZE_DEFAULT];
193   size_t buffer_size;
194   int status;
195
196   if (srv->fd < 0)
197   {
198     status = server_open_socket (srv);
199     if (status != 0)
200       return (status);
201   }
202
203   memset (buffer, 0, sizeof (buffer));
204   buffer_size = sizeof (buffer);
205
206   lcc_network_buffer_finalize (srv->buffer);
207   status = lcc_network_buffer_get (srv->buffer, buffer, &buffer_size);
208   lcc_network_buffer_initialize (srv->buffer);
209
210   if (status != 0)
211     return (status);
212
213   if (buffer_size > sizeof (buffer))
214     buffer_size = sizeof (buffer);
215
216   while (42)
217   {
218     assert (srv->fd >= 0);
219     assert (srv->sa != NULL);
220     status = (int) sendto (srv->fd, buffer, buffer_size, /* flags = */ 0,
221         srv->sa, srv->sa_len);
222     if ((status < 0) && ((errno == EINTR) || (errno == EAGAIN)))
223       continue;
224
225     break;
226   }
227
228   if (status < 0)
229     return (status);
230   return (0);
231 } /* }}} int server_send_buffer */
232
233 static int server_value_add (lcc_server_t *srv, /* {{{ */
234     const lcc_value_list_t *vl)
235 {
236   int status;
237
238   status = lcc_network_buffer_add_value (srv->buffer, vl);
239   if (status == 0)
240     return (0);
241
242   server_send_buffer (srv);
243   return (lcc_network_buffer_add_value (srv->buffer, vl));
244 } /* }}} int server_value_add */
245
246 /*
247  * Public functions
248  */
249 lcc_network_t *lcc_network_create (void) /* {{{ */
250 {
251   lcc_network_t *net;
252
253   net = malloc (sizeof (*net));
254   if (net == NULL)
255     return (NULL);
256   memset (net, 0, sizeof (*net));
257
258   net->servers = NULL;
259
260   return (net);
261 } /* }}} lcc_network_t *lcc_network_create */
262
263 void lcc_network_destroy (lcc_network_t *net) /* {{{ */
264 {
265   if (net == NULL)
266     return;
267   int_server_destroy (net->servers);
268   free (net);
269 } /* }}} void lcc_network_destroy */
270
271 lcc_server_t *lcc_server_create (lcc_network_t *net, /* {{{ */
272     const char *node, const char *service)
273 {
274   lcc_server_t *srv;
275
276   if ((net == NULL) || (node == NULL))
277     return (NULL);
278   if (service == NULL)
279     service = NET_DEFAULT_PORT;
280
281   srv = malloc (sizeof (*srv));
282   if (srv == NULL)
283     return (NULL);
284   memset (srv, 0, sizeof (*srv));
285
286   srv->fd = -1;
287   srv->security_level = NONE;
288   srv->username = NULL;
289   srv->password = NULL;
290   srv->next = NULL;
291
292   srv->node = strdup (node);
293   if (srv->node == NULL)
294   {
295     free (srv);
296     return (NULL);
297   }
298
299   srv->service = strdup (service);
300   if (srv->service == NULL)
301   {
302     free (srv->node);
303     free (srv);
304     return (NULL);
305   }
306
307   srv->buffer = lcc_network_buffer_create (/* size = */ 0);
308   if (srv->buffer == NULL)
309   {
310     free (srv->service);
311     free (srv->node);
312     free (srv);
313     return (NULL);
314   }
315
316   if (net->servers == NULL)
317   {
318     net->servers = srv;
319   }
320   else
321   {
322     lcc_server_t *last = net->servers;
323
324     while (last->next != NULL)
325       last = last->next;
326
327     last->next = srv;
328   }
329
330   return (srv);
331 } /* }}} lcc_server_t *lcc_server_create */
332
333 int lcc_server_destroy (lcc_network_t *net, lcc_server_t *srv) /* {{{ */
334 {
335   if ((net == NULL) || (srv == NULL))
336     return (EINVAL);
337
338   if (net->servers == srv)
339   {
340     net->servers = srv->next;
341     srv->next = NULL;
342   }
343   else
344   {
345     lcc_server_t *prev = net->servers;
346
347     while ((prev != NULL) && (prev->next != srv))
348       prev = prev->next;
349
350     if (prev == NULL)
351       return (ENOENT);
352
353     prev->next = srv->next;
354     srv->next = NULL;
355   }
356
357   int_server_destroy (srv);
358
359   return (0);
360 } /* }}} int lcc_server_destroy */
361
362 int lcc_server_set_ttl (lcc_server_t *srv, uint8_t ttl) /* {{{ */
363 {
364   if (srv == NULL)
365     return (EINVAL);
366
367   srv->ttl = (int) ttl;
368
369   return (0);
370 } /* }}} int lcc_server_set_ttl */
371
372 int lcc_server_set_security_level (lcc_server_t *srv, /* {{{ */
373     lcc_security_level_t level,
374     const char *username, const char *password)
375 {
376   return (lcc_network_buffer_set_security_level (srv->buffer,
377         level, username, password));
378 } /* }}} int lcc_server_set_security_level */
379
380 int lcc_network_values_send (lcc_network_t *net, /* {{{ */
381     const lcc_value_list_t *vl)
382 {
383   lcc_server_t *srv;
384
385   if ((net == NULL) || (vl == NULL))
386     return (EINVAL);
387
388   for (srv = net->servers; srv != NULL; srv = srv->next)
389     server_value_add (srv, vl);
390
391   return (0);
392 } /* }}} int lcc_network_values_send */
393
394 /* vim: set sw=2 sts=2 et fdm=marker : */