6733cda4de758929d842fef81d0f2df2b85d7861
[collectd.git] / src / libcollectdclient / network.c
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  **/
26
27 #include "collectd.h"
28
29 #include <stdlib.h>
30 #include <stdio.h>
31 #include <unistd.h>
32 #include <string.h>
33 #include <errno.h>
34 #include <assert.h>
35
36 #include <sys/types.h>
37 #include <sys/socket.h>
38 #include <netdb.h>
39
40 #if HAVE_NETINET_IN_H
41 # include <netinet/in.h>
42 #endif
43
44 #include "collectd/network.h"
45 #include "collectd/network_buffer.h"
46
47 /*
48  * Private data types
49  */
50 struct lcc_network_s
51 {
52   lcc_server_t *servers;
53 };
54
55 struct lcc_server_s
56 {
57   char *node;
58   char *service;
59
60   int ttl;
61   lcc_security_level_t security_level;
62   char *username;
63   char *password;
64
65   int fd;
66   struct sockaddr *sa;
67   socklen_t sa_len;
68
69   lcc_network_buffer_t *buffer;
70
71   lcc_server_t *next;
72 };
73
74 /*
75  * Private functions
76  */
77 static int server_close_socket (lcc_server_t *srv) /* {{{ */
78 {
79   if (srv == NULL)
80     return (EINVAL);
81
82   if (srv->fd < 0)
83     return (0);
84
85   close (srv->fd);
86   free (srv->sa);
87   srv->sa = NULL;
88   srv->sa_len = 0;
89
90   return (0);
91 } /* }}} int server_close_socket */
92
93 static void int_server_destroy (lcc_server_t *srv) /* {{{ */
94 {
95   lcc_server_t *next;
96
97   if (srv == NULL)
98     return;
99
100   server_close_socket (srv);
101
102   next = srv->next;
103
104   if (srv->fd >= 0)
105   {
106     close (srv->fd);
107     srv->fd = -1;
108   }
109
110   free (srv->node);
111   free (srv->service);
112   free (srv->username);
113   free (srv->password);
114   free (srv);
115
116   int_server_destroy (next);
117 } /* }}} void int_server_destroy */
118
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;
125
126   if (srv == NULL)
127     return (EINVAL);
128
129   if (srv->fd >= 0)
130     server_close_socket (srv);
131
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;
137
138   status = getaddrinfo (srv->node, srv->service, &ai_hints, &ai_list);
139   if (status != 0)
140     return (status);
141   assert (ai_list != NULL);
142
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;
148
149     if (ai_ptr->ai_family == AF_INET)
150     {
151       struct sockaddr_in *addr = (struct sockaddr_in *) ai_ptr->ai_addr;
152       int optname;
153
154       if (IN_MULTICAST (ntohl (addr->sin_addr.s_addr)))
155         optname = IP_MULTICAST_TTL;
156       else
157         optname = IP_TTL;
158
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;
167
168       if (IN6_IS_ADDR_MULTICAST (&addr->sin6_addr))
169         optname = IPV6_MULTICAST_HOPS;
170       else
171         optname = IPV6_UNICAST_HOPS;
172
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     }
183
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     }
191
192     memcpy (srv->sa, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
193     srv->sa_len = ai_ptr->ai_addrlen;
194     break;
195   }
196
197   freeaddrinfo (ai_list);
198
199   if (srv->fd < 0)
200     return (-1);
201   return (0);
202 } /* }}} int server_open_socket */
203
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;
209
210   if (srv->fd < 0)
211   {
212     status = server_open_socket (srv);
213     if (status != 0)
214       return (status);
215   }
216
217   memset (buffer, 0, sizeof (buffer));
218   buffer_size = sizeof (buffer);
219
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);
223
224   if (status != 0)
225     return (status);
226
227   if (buffer_size > sizeof (buffer))
228     buffer_size = sizeof (buffer);
229
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;
238
239     break;
240   }
241
242   if (status < 0)
243     return (status);
244   return (0);
245 } /* }}} int server_send_buffer */
246
247 static int server_value_add (lcc_server_t *srv, /* {{{ */
248     const lcc_value_list_t *vl)
249 {
250   int status;
251
252   status = lcc_network_buffer_add_value (srv->buffer, vl);
253   if (status == 0)
254     return (0);
255
256   server_send_buffer (srv);
257   return (lcc_network_buffer_add_value (srv->buffer, vl));
258 } /* }}} int server_value_add */
259
260 /*
261  * Public functions
262  */
263 lcc_network_t *lcc_network_create (void) /* {{{ */
264 {
265   lcc_network_t *net;
266
267   net = malloc (sizeof (*net));
268   if (net == NULL)
269     return (NULL);
270   memset (net, 0, sizeof (*net));
271
272   net->servers = NULL;
273
274   return (net);
275 } /* }}} lcc_network_t *lcc_network_create */
276
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 */
284
285 lcc_server_t *lcc_server_create (lcc_network_t *net, /* {{{ */
286     const char *node, const char *service)
287 {
288   lcc_server_t *srv;
289
290   if ((net == NULL) || (node == NULL))
291     return (NULL);
292   if (service == NULL)
293     service = NET_DEFAULT_PORT;
294
295   srv = malloc (sizeof (*srv));
296   if (srv == NULL)
297     return (NULL);
298   memset (srv, 0, sizeof (*srv));
299
300   srv->fd = -1;
301   srv->security_level = NONE;
302   srv->username = NULL;
303   srv->password = NULL;
304   srv->next = NULL;
305
306   srv->node = strdup (node);
307   if (srv->node == NULL)
308   {
309     free (srv);
310     return (NULL);
311   }
312
313   srv->service = strdup (service);
314   if (srv->service == NULL)
315   {
316     free (srv->node);
317     free (srv);
318     return (NULL);
319   }
320
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   }
329
330   if (net->servers == NULL)
331   {
332     net->servers = srv;
333   }
334   else
335   {
336     lcc_server_t *last = net->servers;
337
338     while (last->next != NULL)
339       last = last->next;
340
341     last->next = srv;
342   }
343
344   return (srv);
345 } /* }}} lcc_server_t *lcc_server_create */
346
347 int lcc_server_destroy (lcc_network_t *net, lcc_server_t *srv) /* {{{ */
348 {
349   if ((net == NULL) || (srv == NULL))
350     return (EINVAL);
351
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;
360
361     while ((prev != NULL) && (prev->next != srv))
362       prev = prev->next;
363
364     if (prev == NULL)
365       return (ENOENT);
366
367     prev->next = srv->next;
368     srv->next = NULL;
369   }
370
371   int_server_destroy (srv);
372
373   return (0);
374 } /* }}} int lcc_server_destroy */
375
376 int lcc_server_set_ttl (lcc_server_t *srv, uint8_t ttl) /* {{{ */
377 {
378   if (srv == NULL)
379     return (EINVAL);
380
381   srv->ttl = (int) ttl;
382
383   return (0);
384 } /* }}} int lcc_server_set_ttl */
385
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 */
393
394 int lcc_network_values_send (lcc_network_t *net, /* {{{ */
395     const lcc_value_list_t *vl)
396 {
397   lcc_server_t *srv;
398
399   if ((net == NULL) || (vl == NULL))
400     return (EINVAL);
401
402   for (srv = net->servers; srv != NULL; srv = srv->next)
403     server_value_add (srv, vl);
404
405   return (0);
406 } /* }}} int lcc_network_values_send */
407
408 /* vim: set sw=2 sts=2 et fdm=marker : */