ddcbf58acc13abaf05784fc6cf6d3ee1679eec08
[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   if (!functions->is_command)
82     return 0;
83
84   if (functions->is_command(requ, SIW_OLSRD_CONF))
85     return SIW_OLSRD_CONF;
86
87   if (functions->is_command(requ, SIW_ALL))
88     return SIW_ALL;
89
90   // these are the two overarching categories
91   if (functions->is_command(requ, SIW_RUNTIME_ALL))
92     return SIW_RUNTIME_ALL;
93
94   if (functions->is_command(requ, SIW_STARTUP_ALL))
95     return SIW_STARTUP_ALL;
96
97   // these are the individual sections
98
99   if (functions->is_command(requ, SIW_NEIGHBORS))
100     return SIW_NEIGHBORS;
101
102   if (functions->is_command(requ, SIW_LINKS))
103     return SIW_LINKS;
104
105   if (functions->is_command(requ, SIW_ROUTES))
106     return SIW_ROUTES;
107
108   if (functions->is_command(requ, SIW_HNA))
109     return SIW_HNA;
110
111   if (functions->is_command(requ, SIW_MID))
112     return SIW_MID;
113
114   if (functions->is_command(requ, SIW_TOPOLOGY))
115     return SIW_TOPOLOGY;
116
117   if (functions->is_command(requ, SIW_GATEWAYS))
118     return SIW_GATEWAYS;
119
120   if (functions->is_command(requ, SIW_INTERFACES))
121     return SIW_INTERFACES;
122
123   if (functions->is_command(requ, SIW_2HOP))
124     return SIW_2HOP;
125
126   if (functions->is_command(requ, SIW_SGW))
127     return SIW_SGW;
128
129   // specials
130
131   if (functions->is_command(requ, SIW_VERSION))
132     return SIW_VERSION;
133
134   if (functions->is_command(requ, SIW_CONFIG))
135     return SIW_CONFIG;
136
137   if (functions->is_command(requ, SIW_PLUGINS))
138     return SIW_PLUGINS;
139
140   /* To print out neighbours only on the Freifunk Status
141    * page the normal output is somewhat lengthy. The
142    * header parsing is sufficient for standard wget.
143    */
144   if (functions->is_command(requ, SIW_NEIGHBORS_FREIFUNK))
145     return SIW_NEIGHBORS_FREIFUNK;
146
147   return 0;
148 }
149
150 static void write_data(void *foo __attribute__ ((unused))) {
151   fd_set set;
152   int result, i, max;
153   struct timeval tv;
154
155   if (outbuffer.count <= 0) {
156     /* exit early if there is nothing to send */
157     return;
158   }
159
160   FD_ZERO(&set);
161   max = 0;
162   for (i = 0; i < outbuffer.count; i++) {
163     if (outbuffer.socket[i] < 0) {
164       continue;
165     }
166
167     /* And we cast here since we get a warning on Win32 */
168     FD_SET((unsigned int ) (outbuffer.socket[i]), &set);
169
170     if (outbuffer.socket[i] > max) {
171       max = outbuffer.socket[i];
172     }
173   }
174
175   tv.tv_sec = 0;
176   tv.tv_usec = 0;
177
178   result = select(max + 1, NULL, &set, NULL, &tv);
179   if (result <= 0) {
180     /* exit early if any of the sockets is not ready for writing */
181     return;
182   }
183
184   for (i = 0; i < MAX_CLIENTS; i++) {
185     if (outbuffer.socket[i] < 0) {
186       continue;
187     }
188
189     result = send(outbuffer.socket[i], outbuffer.buffer[i] + outbuffer.written[i], outbuffer.size[i] - outbuffer.written[i], 0);
190     if (result > 0) {
191       outbuffer.written[i] += result;
192     }
193
194     if ((result <= 0) || (outbuffer.written[i] >= outbuffer.size[i])) {
195       /* close this socket and cleanup*/
196       close(outbuffer.socket[i]);
197       outbuffer.socket[i] = -1;
198       free(outbuffer.buffer[i]);
199       outbuffer.buffer[i] = NULL;
200       outbuffer.size[i] = 0;
201       outbuffer.written[i] = 0;
202
203       outbuffer.count--;
204     }
205   }
206
207   if (!outbuffer.count) {
208     olsr_stop_timer(writetimer_entry);
209   }
210 }
211
212 static void send_info(unsigned int send_what, int the_socket) {
213   struct autobuf abuf;
214
215   const char *content_type = functions->determine_mime_type ? functions->determine_mime_type(send_what) : "text/plain; charset=utf-8";
216   int contentLengthIndex = 0;
217   int headerLength = 0;
218
219   abuf_init(&abuf, 2 * 4096);
220
221   if (config->http_headers) {
222     http_header_build(name, HTTP_200, content_type, &abuf, &contentLengthIndex);
223     headerLength = abuf.len;
224   }
225
226   // only add if normal format
227   if (send_what & SIW_ALL) {
228     if (functions->output_start)
229       functions->output_start(&abuf);
230
231     if ((send_what & SIW_NEIGHBORS) && functions->neighbors)
232       functions->neighbors(&abuf);
233     if ((send_what & SIW_LINKS) && functions->links)
234       functions->links(&abuf);
235     if ((send_what & SIW_ROUTES) && functions->routes)
236       functions->routes(&abuf);
237     if ((send_what & SIW_HNA) && functions->hna)
238       functions->hna(&abuf);
239     if ((send_what & SIW_MID) && functions->mid)
240       functions->mid(&abuf);
241     if ((send_what & SIW_TOPOLOGY) && functions->topology)
242       functions->topology(&abuf);
243     if ((send_what & SIW_GATEWAYS) && functions->gateways)
244       functions->gateways(&abuf);
245     if ((send_what & SIW_INTERFACES) && functions->interfaces)
246       functions->interfaces(&abuf);
247     if ((send_what & SIW_2HOP) && functions->twohop)
248       functions->twohop(&abuf);
249     if ((send_what & SIW_SGW) && functions->sgw)
250       functions->sgw(&abuf);
251
252     if ((send_what & SIW_VERSION) && functions->version)
253       functions->version(&abuf);
254     if ((send_what & SIW_CONFIG) && functions->config)
255       functions->config(&abuf);
256     if ((send_what & SIW_PLUGINS) && functions->plugins)
257       functions->plugins(&abuf);
258
259     if (functions->output_end)
260       functions->output_end(&abuf);
261   } else if ((send_what & SIW_OLSRD_CONF) && functions->olsrd_conf) {
262     /* this outputs the olsrd.conf text directly, not normal format */
263     functions->olsrd_conf(&abuf);
264   }
265
266   if (config->http_headers) {
267     http_header_adjust_content_length(&abuf, contentLengthIndex, abuf.len - headerLength);
268   }
269
270   /* avoid a memcpy: just move the abuf.buf pointer and clear abuf */
271   outbuffer.buffer[outbuffer.count] = abuf.buf;
272   outbuffer.size[outbuffer.count] = abuf.len;
273   outbuffer.written[outbuffer.count] = 0;
274   outbuffer.socket[outbuffer.count] = the_socket;
275   abuf.buf = NULL;
276   abuf.len = 0;
277   abuf.size = 0;
278
279   outbuffer.count++;
280
281   if (outbuffer.count == 1) {
282     writetimer_entry = olsr_start_timer(100, 0, OLSR_TIMER_PERIODIC, &write_data, NULL, 0);
283   }
284
285   abuf_free(&abuf);
286 }
287
288 static void ipc_action(int fd, void *data __attribute__ ((unused)), unsigned int flags __attribute__ ((unused))) {
289 #ifndef NODEBUG
290   char addr[INET6_ADDRSTRLEN];
291 #endif /* NODEBUG */
292
293   union olsr_sockaddr sock_addr;
294   socklen_t sock_addr_len = sizeof(sock_addr);
295   fd_set rfds;
296   struct timeval tv;
297   unsigned int send_what = 0;
298   int ipc_connection = -1;
299
300   if (outbuffer.count >= MAX_CLIENTS) {
301     return;
302   }
303
304   if ((ipc_connection = accept(fd, &sock_addr.in, &sock_addr_len)) == -1) {
305 #ifndef NODEBUG
306     olsr_printf(1, "(%s) accept()=%s\n", name, strerror(errno));
307 #endif /* NODEBUG */
308     return;
309   }
310
311 #ifndef NODEBUG
312   if (!inet_ntop( //
313       olsr_cnf->ip_version, //
314       (olsr_cnf->ip_version == AF_INET) ? (void *) &sock_addr.in4.sin_addr : (void *) &sock_addr.in6.sin6_addr, //
315       addr, //
316       sizeof(addr))) {
317     addr[0] = '\0';
318   }
319 #endif /* NODEBUG */
320
321   tv.tv_sec = tv.tv_usec = 0;
322   if (olsr_cnf->ip_version == AF_INET) {
323     if (!ip4equal(&sock_addr.in4.sin_addr, &config->accept_ip.v4) && (config->accept_ip.v4.s_addr != INADDR_ANY) //
324         && (!config->allow_localhost || (ntohl(sock_addr.in4.sin_addr.s_addr) != INADDR_LOOPBACK))) {
325 #ifndef NODEBUG
326       olsr_printf(1, "(%s) From host(%s) not allowed!\n", name, addr);
327 #endif /* NODEBUG */
328       close(ipc_connection);
329       return;
330     }
331   } else {
332     /* Use in6addr_any (::) in olsr.conf to allow anybody. */
333     if (!ip6equal(&sock_addr.in6.sin6_addr, &config->accept_ip.v6) && !ip6equal(&config->accept_ip.v6, &in6addr_any)) {
334 #ifndef NODEBUG
335       olsr_printf(1, "(%s) From host(%s) not allowed!\n", name, addr);
336 #endif /* NODEBUG */
337       close(ipc_connection);
338       return;
339     }
340   }
341
342 #ifndef NODEBUG
343   olsr_printf(2, "(%s) Connect from %s\n", name, addr);
344 #endif /* NODEBUG */
345
346   /* purge read buffer to prevent blocking on linux */
347   FD_ZERO(&rfds);
348   FD_SET((unsigned int ) ipc_connection, &rfds); /* Win32 needs the cast here */
349   if (0 <= select(ipc_connection + 1, &rfds, NULL, NULL, &tv)) {
350     char requ[1024];
351     ssize_t s = recv(ipc_connection, (void *) &requ, sizeof(requ) - 1, 0); /* Win32 needs the cast here */
352
353     if (s >= (ssize_t) (sizeof(requ) - 1)) {
354       /* input was much too long, just skip the rest */
355       while (recv(ipc_connection, (void *) &sink_buffer, sizeof(sink_buffer), 0) == sizeof(sink_buffer))
356         ;
357     }
358
359     if (0 < s) {
360       requ[s] = '\0';
361       send_what = determine_action(requ);
362     }
363
364     if (!send_what)
365       send_what = SIW_ALL;
366   }
367
368   send_info(send_what, ipc_connection);
369 }
370
371 static int plugin_ipc_init(void) {
372   union olsr_sockaddr sock_addr;
373   uint32_t yes = 1;
374   socklen_t sock_addr_len;
375
376   /* Init ipc socket */
377   if ((ipc_socket = socket(olsr_cnf->ip_version, SOCK_STREAM, 0)) == -1) {
378 #ifndef NODEBUG
379     olsr_printf(1, "(%s) socket()=%s\n", name, strerror(errno));
380 #endif /* NODEBUG */
381     goto error_out;
382   }
383
384   if (setsockopt(ipc_socket, SOL_SOCKET, SO_REUSEADDR, (char *) &yes, sizeof(yes)) < 0) {
385 #ifndef NODEBUG
386     olsr_printf(1, "(%s) setsockopt()=%s\n", name, strerror(errno));
387 #endif /* NODEBUG */
388     goto error_out;
389   }
390
391 #if (defined __FreeBSD__ || defined __FreeBSD_kernel__) && defined SO_NOSIGPIPE
392   if (setsockopt(ipc_socket, SOL_SOCKET, SO_NOSIGPIPE, (char *) &yes, sizeof(yes)) < 0) {
393     perror("SO_NOSIGPIPE failed");
394     goto error_out;
395   }
396 #endif /* (defined __FreeBSD__ || defined __FreeBSD_kernel__) && defined SO_NOSIGPIPE */
397
398 #if defined __linux__ && defined IPV6_V6ONLY
399   if (config->ipv6_only && (olsr_cnf->ip_version == AF_INET6) //
400       && (setsockopt(ipc_socket, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &yes, sizeof(yes)) < 0)) {
401     perror("IPV6_V6ONLY failed");
402     goto error_out;
403   }
404 #endif /* defined __linux__ && defined IPV6_V6ONLY */
405
406   /* complete the socket structure */
407   memset(&sock_addr, 0, sizeof(sock_addr));
408   if (olsr_cnf->ip_version == AF_INET) {
409     sock_addr.in4.sin_family = AF_INET;
410     sock_addr_len = sizeof(struct sockaddr_in);
411 #ifdef SIN6_LEN
412     sock_addr.in4.sin_len = sock_addr_len;
413 #endif /* SIN6_LEN */
414     sock_addr.in4.sin_addr.s_addr = config->listen_ip.v4.s_addr;
415     sock_addr.in4.sin_port = htons(config->ipc_port);
416   } else {
417     sock_addr.in6.sin6_family = AF_INET6;
418     sock_addr_len = sizeof(struct sockaddr_in6);
419 #ifdef SIN6_LEN
420     sock_addr.in6.sin6_len = sock_addr_len;
421 #endif /* SIN6_LEN */
422     sock_addr.in6.sin6_addr = config->listen_ip.v6;
423     sock_addr.in6.sin6_port = htons(config->ipc_port);
424   }
425
426   /* bind the socket to the port number */
427   if (bind(ipc_socket, &sock_addr.in, sock_addr_len) == -1) {
428 #ifndef NODEBUG
429     olsr_printf(1, "(%s) bind()=%s\n", name, strerror(errno));
430 #endif /* NODEBUG */
431     goto error_out;
432   }
433
434   /* show that we are willing to listen */
435   if (listen(ipc_socket, 1) == -1) {
436 #ifndef NODEBUG
437     olsr_printf(1, "(%s) listen()=%s\n", name, strerror(errno));
438 #endif /* NODEBUG */
439     goto error_out;
440   }
441
442   /* Register with olsrd */
443   add_olsr_socket(ipc_socket, &ipc_action, NULL, NULL, SP_PR_READ);
444
445 #ifndef NODEBUG
446   olsr_printf(2, "(%s) listening on port %d\n", name, config->ipc_port);
447 #endif /* NODEBUG */
448
449   return 1;
450
451   error_out: //
452   if (ipc_socket >= 0) {
453     close(ipc_socket);
454     ipc_socket = -1;
455   }
456   return 0;
457 }
458
459 int info_plugin_init(const char * plugin_name, info_plugin_functions_t *plugin_functions, info_plugin_config_t *plugin_config) {
460   int i;
461
462   assert(plugin_name);
463   assert(plugin_functions);
464   assert(plugin_config);
465
466   name = plugin_name;
467   functions = plugin_functions;
468   config = plugin_config;
469
470   memset(&outbuffer, 0, sizeof(outbuffer));
471   for (i = 0; i < MAX_CLIENTS; ++i) {
472     outbuffer.socket[i] = -1;
473   }
474
475   ipc_socket = -1;
476
477   if (functions->init) {
478     functions->init(name);
479   }
480
481   plugin_ipc_init();
482   return 1;
483 }
484
485 void info_plugin_exit(void) {
486   int i;
487
488   if (ipc_socket != -1) {
489     close(ipc_socket);
490     ipc_socket = -1;
491   }
492   for (i = 0; i < MAX_CLIENTS; ++i) {
493     if (outbuffer.buffer[i]) {
494       free(outbuffer.buffer[i]);
495       outbuffer.buffer[i] = NULL;
496     }
497     outbuffer.size[i] = 0;
498     outbuffer.written[i] = 0;
499     if (outbuffer.socket[i]) {
500       close(outbuffer.socket[i]);
501       outbuffer.socket[i] = -1;
502     }
503   }
504   outbuffer.count = 0;
505 }