libcollectdclient: Relicense under the MIT license.
[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 <stdlib.h>
28 #include <stdio.h>
29 #include <unistd.h>
30 #include <string.h>
31 #include <errno.h>
32 #include <assert.h>
33
34 #include <sys/types.h>
35 #include <sys/socket.h>
36 #include <netdb.h>
37
38 #include "collectd/network.h"
39 #include "collectd/network_buffer.h"
40
41 /*
42  * Private data types
43  */
44 struct lcc_network_s
45 {
46   lcc_server_t *servers;
47 };
48
49 struct lcc_server_s
50 {
51   char *node;
52   char *service;
53
54   int ttl;
55   lcc_security_level_t security_level;
56   char *username;
57   char *password;
58
59   int fd;
60   struct sockaddr *sa;
61   socklen_t sa_len;
62
63   lcc_network_buffer_t *buffer;
64
65   lcc_server_t *next;
66 };
67
68 /*
69  * Private functions
70  */
71 static int server_close_socket (lcc_server_t *srv) /* {{{ */
72 {
73   if (srv == NULL)
74     return (EINVAL);
75
76   if (srv->fd < 0)
77     return (0);
78
79   close (srv->fd);
80   free (srv->sa);
81   srv->sa = NULL;
82   srv->sa_len = 0;
83
84   return (0);
85 } /* }}} int server_close_socket */
86
87 static void int_server_destroy (lcc_server_t *srv) /* {{{ */
88 {
89   lcc_server_t *next;
90
91   if (srv == NULL)
92     return;
93
94   server_close_socket (srv);
95
96   next = srv->next;
97
98   if (srv->fd >= 0)
99   {
100     close (srv->fd);
101     srv->fd = -1;
102   }
103
104   free (srv->node);
105   free (srv->service);
106   free (srv->username);
107   free (srv->password);
108   free (srv);
109
110   int_server_destroy (next);
111 } /* }}} void int_server_destroy */
112
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;
119
120   if (srv == NULL)
121     return (EINVAL);
122
123   if (srv->fd >= 0)
124     server_close_socket (srv);
125
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;
131
132   status = getaddrinfo (srv->node, srv->service, &ai_hints, &ai_list);
133   if (status != 0)
134     return (status);
135   assert (ai_list != NULL);
136
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;
142
143     if (ai_ptr->ai_family == AF_INET)
144     {
145
146       struct sockaddr_in *addr = (struct sockaddr_in *) ai_ptr->ai_addr;
147       int optname;
148
149       if (IN_MULTICAST (ntohl (addr->sin_addr.s_addr)))
150         optname = IP_MULTICAST_TTL;
151       else
152         optname = IP_TTL;
153
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;
163
164       if (IN6_IS_ADDR_MULTICAST (&addr->sin6_addr))
165         optname = IPV6_MULTICAST_HOPS;
166       else
167         optname = IPV6_UNICAST_HOPS;
168
169       setsockopt (srv->fd, IPPROTO_IPV6, optname,
170           &srv->ttl,
171           sizeof (srv->ttl));
172     }
173
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     }
181
182     memcpy (srv->sa, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
183     srv->sa_len = ai_ptr->ai_addrlen;
184     break;
185   }
186
187   freeaddrinfo (ai_list);
188
189   if (srv->fd < 0)
190     return (-1);
191   return (0);
192 } /* }}} int server_open_socket */
193
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;
199
200   if (srv->fd < 0)
201   {
202     status = server_open_socket (srv);
203     if (status != 0)
204       return (status);
205   }
206
207   memset (buffer, 0, sizeof (buffer));
208   buffer_size = sizeof (buffer);
209
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);
213
214   if (status != 0)
215     return (status);
216
217   if (buffer_size > sizeof (buffer))
218     buffer_size = sizeof (buffer);
219
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;
228
229     break;
230   }
231
232   if (status < 0)
233     return (status);
234   return (0);
235 } /* }}} int server_send_buffer */
236
237 static int server_value_add (lcc_server_t *srv, /* {{{ */
238     const lcc_value_list_t *vl)
239 {
240   int status;
241
242   status = lcc_network_buffer_add_value (srv->buffer, vl);
243   if (status == 0)
244     return (0);
245
246   server_send_buffer (srv);
247   return (lcc_network_buffer_add_value (srv->buffer, vl));
248 } /* }}} int server_value_add */
249
250 /*
251  * Public functions
252  */
253 lcc_network_t *lcc_network_create (void) /* {{{ */
254 {
255   lcc_network_t *net;
256
257   net = malloc (sizeof (*net));
258   if (net == NULL)
259     return (NULL);
260   memset (net, 0, sizeof (*net));
261
262   net->servers = NULL;
263
264   return (net);
265 } /* }}} lcc_network_t *lcc_network_create */
266
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 */
274
275 lcc_server_t *lcc_server_create (lcc_network_t *net, /* {{{ */
276     const char *node, const char *service)
277 {
278   lcc_server_t *srv;
279
280   if ((net == NULL) || (node == NULL))
281     return (NULL);
282   if (service == NULL)
283     service = NET_DEFAULT_PORT;
284
285   srv = malloc (sizeof (*srv));
286   if (srv == NULL)
287     return (NULL);
288   memset (srv, 0, sizeof (*srv));
289
290   srv->fd = -1;
291   srv->security_level = NONE;
292   srv->username = NULL;
293   srv->password = NULL;
294   srv->next = NULL;
295
296   srv->node = strdup (node);
297   if (srv->node == NULL)
298   {
299     free (srv);
300     return (NULL);
301   }
302
303   srv->service = strdup (service);
304   if (srv->service == NULL)
305   {
306     free (srv->node);
307     free (srv);
308     return (NULL);
309   }
310
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   }
319
320   if (net->servers == NULL)
321   {
322     net->servers = srv;
323   }
324   else
325   {
326     lcc_server_t *last = net->servers;
327
328     while (last->next != NULL)
329       last = last->next;
330
331     last->next = srv;
332   }
333
334   return (srv);
335 } /* }}} lcc_server_t *lcc_server_create */
336
337 int lcc_server_destroy (lcc_network_t *net, lcc_server_t *srv) /* {{{ */
338 {
339   if ((net == NULL) || (srv == NULL))
340     return (EINVAL);
341
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;
350
351     while ((prev != NULL) && (prev->next != srv))
352       prev = prev->next;
353
354     if (prev == NULL)
355       return (ENOENT);
356
357     prev->next = srv->next;
358     srv->next = NULL;
359   }
360
361   int_server_destroy (srv);
362
363   return (0);
364 } /* }}} int lcc_server_destroy */
365
366 int lcc_server_set_ttl (lcc_server_t *srv, uint8_t ttl) /* {{{ */
367 {
368   if (srv == NULL)
369     return (EINVAL);
370
371   srv->ttl = (int) ttl;
372
373   return (0);
374 } /* }}} int lcc_server_set_ttl */
375
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 */
383
384 int lcc_network_values_send (lcc_network_t *net, /* {{{ */
385     const lcc_value_list_t *vl)
386 {
387   lcc_server_t *srv;
388
389   if ((net == NULL) || (vl == NULL))
390     return (EINVAL);
391
392   for (srv = net->servers; srv != NULL; srv = srv->next)
393     server_value_add (srv, vl);
394
395   return (0);
396 } /* }}} int lcc_network_values_send */
397
398 /* vim: set sw=2 sts=2 et fdm=marker : */