3adf4a54c2f3bd54d18a4bf38cff930acb36d9c9
[olsrd.git] / lib / info / olsrd_info.c
1 /*
2  * The olsr.org Optimized Link-State Routing daemon(olsrd)
3  * Copyright (c) 2015
4  *
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  *   notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above copyright
14  *   notice, this list of conditions and the following disclaimer in
15  *   the documentation and/or other materials provided with the
16  *   distribution.
17  * * Neither the name of olsr.org, olsrd nor the names of its
18  *   contributors may be used to endorse or promote products derived
19  *   from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  *
34  * Visit http://www.olsr.org for more information.
35  *
36  * If you find this software useful feel free to make a donation
37  * to the project. For more information see the website or contact
38  * the copyright holders.
39  *
40  */
41
42 #include <arpa/inet.h>
43 #include <unistd.h>
44 #include <assert.h>
45
46 #include "olsrd_info.h"
47 #include "olsr.h"
48 #include "scheduler.h"
49 #include "ipcalc.h"
50 #include "http_headers.h"
51
52 #ifdef _WIN32
53 #define close(x) closesocket(x)
54 #endif /* _WIN32 */
55
56 #define MAX_CLIENTS 3
57
58 typedef struct {
59   int socket[MAX_CLIENTS];
60   char *buffer[MAX_CLIENTS];
61   size_t size[MAX_CLIENTS];
62   size_t written[MAX_CLIENTS];
63   int count;
64 } info_plugin_outbuffer_t;
65
66 static char sink_buffer[4096];
67
68 static const char * name = NULL;
69
70 static info_plugin_functions_t *functions = NULL;
71
72 static info_plugin_config_t *config = NULL;
73
74 static int ipc_socket = -1;
75
76 static info_plugin_outbuffer_t outbuffer;
77
78 static struct timer_entry *writetimer_entry = NULL;
79
80 static unsigned int determine_action(char *requ) {
81   static unsigned int SIW_ENTRIES[] = {
82   //
83       SIW_OLSRD_CONF,//
84       SIW_ALL, //
85       //
86       // these are the two overarching categories
87       SIW_RUNTIME_ALL,//
88       SIW_STARTUP_ALL, //
89       //
90       // these are the individual sections
91       SIW_NEIGHBORS,//
92       SIW_LINKS, //
93       SIW_ROUTES, //
94       SIW_HNA, //
95       SIW_MID, //
96       SIW_TOPOLOGY, //
97       SIW_GATEWAYS, //
98       SIW_INTERFACES, //
99       SIW_2HOP, //
100       SIW_SGW, //
101       //
102       // specials
103       SIW_VERSION,//
104       SIW_CONFIG, //
105       SIW_PLUGINS, //
106       //
107       // Freifunk special
108       SIW_NEIGHBORS_FREIFUNK //
109       };
110
111   unsigned int i;
112
113   if (!functions->is_command)
114     return 0;
115
116   for (i = 0; i < (sizeof(SIW_ENTRIES) / sizeof(SIW_ENTRIES[0])); ++i) {
117     unsigned int siw = SIW_ENTRIES[i];
118     if (functions->is_command(requ, siw))
119       return siw;
120   }
121
122   return 0;
123 }
124
125 static void write_data(void *foo __attribute__ ((unused))) {
126   fd_set set;
127   int result, i, max;
128   struct timeval tv;
129
130   if (outbuffer.count <= 0) {
131     /* exit early if there is nothing to send */
132     return;
133   }
134
135   FD_ZERO(&set);
136   max = 0;
137   for (i = 0; i < outbuffer.count; i++) {
138     if (outbuffer.socket[i] < 0) {
139       continue;
140     }
141
142     /* And we cast here since we get a warning on Win32 */
143     FD_SET((unsigned int ) (outbuffer.socket[i]), &set);
144
145     if (outbuffer.socket[i] > max) {
146       max = outbuffer.socket[i];
147     }
148   }
149
150   tv.tv_sec = 0;
151   tv.tv_usec = 0;
152
153   result = select(max + 1, NULL, &set, NULL, &tv);
154   if (result <= 0) {
155     /* exit early if any of the sockets is not ready for writing */
156     return;
157   }
158
159   for (i = 0; i < MAX_CLIENTS; i++) {
160     if (outbuffer.socket[i] < 0) {
161       continue;
162     }
163
164     result = send(outbuffer.socket[i], outbuffer.buffer[i] + outbuffer.written[i], outbuffer.size[i] - outbuffer.written[i], 0);
165     if (result > 0) {
166       outbuffer.written[i] += result;
167     }
168
169     if ((result <= 0) || (outbuffer.written[i] >= outbuffer.size[i])) {
170       /* close this socket and cleanup*/
171       close(outbuffer.socket[i]);
172       outbuffer.socket[i] = -1;
173       free(outbuffer.buffer[i]);
174       outbuffer.buffer[i] = NULL;
175       outbuffer.size[i] = 0;
176       outbuffer.written[i] = 0;
177
178       outbuffer.count--;
179     }
180   }
181
182   if (!outbuffer.count) {
183     olsr_stop_timer(writetimer_entry);
184   }
185 }
186
187 static void send_info(unsigned int send_what, int the_socket) {
188   struct autobuf abuf;
189
190   const char *content_type = functions->determine_mime_type ? functions->determine_mime_type(send_what) : "text/plain; charset=utf-8";
191   int contentLengthIndex = 0;
192   int headerLength = 0;
193
194   abuf_init(&abuf, 2 * 4096);
195
196   if (config->http_headers) {
197     http_header_build(name, HTTP_200, content_type, &abuf, &contentLengthIndex);
198     headerLength = abuf.len;
199   }
200
201   // only add if normal format
202   if (send_what & SIW_ALL) {
203     if (functions->output_start)
204       functions->output_start(&abuf);
205
206     if ((send_what & SIW_NEIGHBORS) && functions->neighbors)
207       functions->neighbors(&abuf);
208     if ((send_what & SIW_LINKS) && functions->links)
209       functions->links(&abuf);
210     if ((send_what & SIW_ROUTES) && functions->routes)
211       functions->routes(&abuf);
212     if ((send_what & SIW_HNA) && functions->hna)
213       functions->hna(&abuf);
214     if ((send_what & SIW_MID) && functions->mid)
215       functions->mid(&abuf);
216     if ((send_what & SIW_TOPOLOGY) && functions->topology)
217       functions->topology(&abuf);
218     if ((send_what & SIW_GATEWAYS) && functions->gateways)
219       functions->gateways(&abuf);
220     if ((send_what & SIW_INTERFACES) && functions->interfaces)
221       functions->interfaces(&abuf);
222     if ((send_what & SIW_2HOP) && functions->twohop)
223       functions->twohop(&abuf);
224     if ((send_what & SIW_SGW) && functions->sgw)
225       functions->sgw(&abuf);
226
227     if ((send_what & SIW_VERSION) && functions->version)
228       functions->version(&abuf);
229     if ((send_what & SIW_CONFIG) && functions->config)
230       functions->config(&abuf);
231     if ((send_what & SIW_PLUGINS) && functions->plugins)
232       functions->plugins(&abuf);
233
234     if (functions->output_end)
235       functions->output_end(&abuf);
236   } else if ((send_what & SIW_OLSRD_CONF) && functions->olsrd_conf) {
237     /* this outputs the olsrd.conf text directly, not normal format */
238     functions->olsrd_conf(&abuf);
239   }
240
241   if (config->http_headers) {
242     http_header_adjust_content_length(&abuf, contentLengthIndex, abuf.len - headerLength);
243   }
244
245   /* avoid a memcpy: just move the abuf.buf pointer and clear abuf */
246   outbuffer.buffer[outbuffer.count] = abuf.buf;
247   outbuffer.size[outbuffer.count] = abuf.len;
248   outbuffer.written[outbuffer.count] = 0;
249   outbuffer.socket[outbuffer.count] = the_socket;
250   abuf.buf = NULL;
251   abuf.len = 0;
252   abuf.size = 0;
253
254   outbuffer.count++;
255
256   if (outbuffer.count == 1) {
257     writetimer_entry = olsr_start_timer(100, 0, OLSR_TIMER_PERIODIC, &write_data, NULL, 0);
258   }
259
260   abuf_free(&abuf);
261 }
262
263 static void stripEOLs(char * requ) {
264   char c;
265   size_t index = strlen(requ);
266
267   if (index <= 0) {
268     return;
269   }
270
271   c = requ[--index];
272   while (c == '\n' || c == '\r') {
273     index--;
274     c = requ[index];
275   }
276
277   requ[index + 1] = '\0';
278 }
279
280 static void ipc_action(int fd, void *data __attribute__ ((unused)), unsigned int flags __attribute__ ((unused))) {
281 #ifndef NODEBUG
282   char addr[INET6_ADDRSTRLEN];
283 #endif /* NODEBUG */
284
285   union olsr_sockaddr sock_addr;
286   socklen_t sock_addr_len = sizeof(sock_addr);
287   fd_set rfds;
288   struct timeval tv;
289   unsigned int send_what = 0;
290   int ipc_connection = -1;
291
292   if (outbuffer.count >= MAX_CLIENTS) {
293     return;
294   }
295
296   if ((ipc_connection = accept(fd, &sock_addr.in, &sock_addr_len)) == -1) {
297 #ifndef NODEBUG
298     olsr_printf(1, "(%s) accept()=%s\n", name, strerror(errno));
299 #endif /* NODEBUG */
300     return;
301   }
302
303 #ifndef NODEBUG
304   if (!inet_ntop( //
305       olsr_cnf->ip_version, //
306       (olsr_cnf->ip_version == AF_INET) ? (void *) &sock_addr.in4.sin_addr : (void *) &sock_addr.in6.sin6_addr, //
307       addr, //
308       sizeof(addr))) {
309     addr[0] = '\0';
310   }
311 #endif /* NODEBUG */
312
313   tv.tv_sec = tv.tv_usec = 0;
314   if (olsr_cnf->ip_version == AF_INET) {
315     if (!ip4equal(&sock_addr.in4.sin_addr, &config->accept_ip.v4) && (config->accept_ip.v4.s_addr != INADDR_ANY) //
316         && (!config->allow_localhost || (ntohl(sock_addr.in4.sin_addr.s_addr) != INADDR_LOOPBACK))) {
317 #ifndef NODEBUG
318       olsr_printf(1, "(%s) From host(%s) not allowed!\n", name, addr);
319 #endif /* NODEBUG */
320       close(ipc_connection);
321       return;
322     }
323   } else {
324     /* Use in6addr_any (::) in olsr.conf to allow anybody. */
325     if (!ip6equal(&sock_addr.in6.sin6_addr, &config->accept_ip.v6) && !ip6equal(&config->accept_ip.v6, &in6addr_any)) {
326 #ifndef NODEBUG
327       olsr_printf(1, "(%s) From host(%s) not allowed!\n", name, addr);
328 #endif /* NODEBUG */
329       close(ipc_connection);
330       return;
331     }
332   }
333
334 #ifndef NODEBUG
335   olsr_printf(2, "(%s) Connect from %s\n", name, addr);
336 #endif /* NODEBUG */
337
338   /* purge read buffer to prevent blocking on linux */
339   FD_ZERO(&rfds);
340   FD_SET((unsigned int ) ipc_connection, &rfds); /* Win32 needs the cast here */
341   if (0 <= select(ipc_connection + 1, &rfds, NULL, NULL, &tv)) {
342     char requ[1024];
343     ssize_t s = recv(ipc_connection, (void *) &requ, sizeof(requ) - 1, 0); /* Win32 needs the cast here */
344
345     if (s >= (ssize_t) (sizeof(requ) - 1)) {
346       /* input was much too long, just skip the rest */
347       while (recv(ipc_connection, (void *) &sink_buffer, sizeof(sink_buffer), 0) == sizeof(sink_buffer))
348         ;
349       s = 0;
350     }
351
352     if (0 < s) {
353       requ[s] = '\0';
354       stripEOLs(requ);
355       send_what = determine_action(requ);
356     }
357
358     if (!send_what)
359       send_what = SIW_ALL;
360   }
361
362   send_info(send_what, ipc_connection);
363 }
364
365 static int plugin_ipc_init(void) {
366   union olsr_sockaddr sock_addr;
367   uint32_t yes = 1;
368   socklen_t sock_addr_len;
369
370   /* Init ipc socket */
371   if ((ipc_socket = socket(olsr_cnf->ip_version, SOCK_STREAM, 0)) == -1) {
372 #ifndef NODEBUG
373     olsr_printf(1, "(%s) socket()=%s\n", name, strerror(errno));
374 #endif /* NODEBUG */
375     goto error_out;
376   }
377
378   if (setsockopt(ipc_socket, SOL_SOCKET, SO_REUSEADDR, (char *) &yes, sizeof(yes)) < 0) {
379 #ifndef NODEBUG
380     olsr_printf(1, "(%s) setsockopt()=%s\n", name, strerror(errno));
381 #endif /* NODEBUG */
382     goto error_out;
383   }
384
385 #if (defined __FreeBSD__ || defined __FreeBSD_kernel__) && defined SO_NOSIGPIPE
386   if (setsockopt(ipc_socket, SOL_SOCKET, SO_NOSIGPIPE, (char *) &yes, sizeof(yes)) < 0) {
387     perror("SO_NOSIGPIPE failed");
388     goto error_out;
389   }
390 #endif /* (defined __FreeBSD__ || defined __FreeBSD_kernel__) && defined SO_NOSIGPIPE */
391
392 #if defined __linux__ && defined IPV6_V6ONLY
393   if (config->ipv6_only && (olsr_cnf->ip_version == AF_INET6) //
394       && (setsockopt(ipc_socket, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &yes, sizeof(yes)) < 0)) {
395     perror("IPV6_V6ONLY failed");
396     goto error_out;
397   }
398 #endif /* defined __linux__ && defined IPV6_V6ONLY */
399
400   /* complete the socket structure */
401   memset(&sock_addr, 0, sizeof(sock_addr));
402   if (olsr_cnf->ip_version == AF_INET) {
403     sock_addr.in4.sin_family = AF_INET;
404     sock_addr_len = sizeof(struct sockaddr_in);
405 #ifdef SIN6_LEN
406     sock_addr.in4.sin_len = sock_addr_len;
407 #endif /* SIN6_LEN */
408     sock_addr.in4.sin_addr.s_addr = config->listen_ip.v4.s_addr;
409     sock_addr.in4.sin_port = htons(config->ipc_port);
410   } else {
411     sock_addr.in6.sin6_family = AF_INET6;
412     sock_addr_len = sizeof(struct sockaddr_in6);
413 #ifdef SIN6_LEN
414     sock_addr.in6.sin6_len = sock_addr_len;
415 #endif /* SIN6_LEN */
416     sock_addr.in6.sin6_addr = config->listen_ip.v6;
417     sock_addr.in6.sin6_port = htons(config->ipc_port);
418   }
419
420   /* bind the socket to the port number */
421   if (bind(ipc_socket, &sock_addr.in, sock_addr_len) == -1) {
422 #ifndef NODEBUG
423     olsr_printf(1, "(%s) bind()=%s\n", name, strerror(errno));
424 #endif /* NODEBUG */
425     goto error_out;
426   }
427
428   /* show that we are willing to listen */
429   if (listen(ipc_socket, 1) == -1) {
430 #ifndef NODEBUG
431     olsr_printf(1, "(%s) listen()=%s\n", name, strerror(errno));
432 #endif /* NODEBUG */
433     goto error_out;
434   }
435
436   /* Register with olsrd */
437   add_olsr_socket(ipc_socket, &ipc_action, NULL, NULL, SP_PR_READ);
438
439 #ifndef NODEBUG
440   olsr_printf(2, "(%s) listening on port %d\n", name, config->ipc_port);
441 #endif /* NODEBUG */
442
443   return 1;
444
445   error_out: //
446   if (ipc_socket >= 0) {
447     close(ipc_socket);
448     ipc_socket = -1;
449   }
450   return 0;
451 }
452
453 int info_plugin_init(const char * plugin_name, info_plugin_functions_t *plugin_functions, info_plugin_config_t *plugin_config) {
454   int i;
455
456   assert(plugin_name);
457   assert(plugin_functions);
458   assert(plugin_config);
459
460   name = plugin_name;
461   functions = plugin_functions;
462   config = plugin_config;
463
464   memset(&outbuffer, 0, sizeof(outbuffer));
465   for (i = 0; i < MAX_CLIENTS; ++i) {
466     outbuffer.socket[i] = -1;
467   }
468
469   ipc_socket = -1;
470
471   if (functions->init) {
472     functions->init(name);
473   }
474
475   plugin_ipc_init();
476   return 1;
477 }
478
479 void info_plugin_exit(void) {
480   int i;
481
482   if (ipc_socket != -1) {
483     close(ipc_socket);
484     ipc_socket = -1;
485   }
486   for (i = 0; i < MAX_CLIENTS; ++i) {
487     if (outbuffer.buffer[i]) {
488       free(outbuffer.buffer[i]);
489       outbuffer.buffer[i] = NULL;
490     }
491     outbuffer.size[i] = 0;
492     outbuffer.written[i] = 0;
493     if (outbuffer.socket[i]) {
494       close(outbuffer.socket[i]);
495       outbuffer.socket[i] = -1;
496     }
497   }
498   outbuffer.count = 0;
499 }