862368c7cafe51e1d9d652d61d89591044e9a944
[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 #include <ctype.h>
46
47 #include "olsrd_info.h"
48 #include "olsr.h"
49 #include "scheduler.h"
50 #include "ipcalc.h"
51 #include "http_headers.h"
52
53 #ifdef _WIN32
54 #define close(x) closesocket(x)
55 #endif /* _WIN32 */
56
57 #define MAX_CLIENTS 3
58
59 typedef struct {
60   int socket[MAX_CLIENTS];
61   char *buffer[MAX_CLIENTS];
62   size_t size[MAX_CLIENTS];
63   size_t written[MAX_CLIENTS];
64   int count;
65 } info_plugin_outbuffer_t;
66
67 static char sink_buffer[4096];
68
69 static const char * name = NULL;
70
71 static info_plugin_functions_t *functions = NULL;
72
73 static info_plugin_config_t *config = NULL;
74
75 static int ipc_socket = -1;
76
77 static info_plugin_outbuffer_t outbuffer;
78
79 static struct timer_entry *writetimer_entry = NULL;
80
81 #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
82
83 static unsigned int determine_action(char *requ) {
84   static unsigned int SIW_ENTRIES[] = {
85   //
86       SIW_OLSRD_CONF,//
87       SIW_ALL, //
88       //
89       // these are the two overarching categories
90       SIW_RUNTIME_ALL,//
91       SIW_STARTUP_ALL, //
92       //
93       // these are the individual sections
94       SIW_NEIGHBORS,//
95       SIW_LINKS, //
96       SIW_ROUTES, //
97       SIW_HNA, //
98       SIW_MID, //
99       SIW_TOPOLOGY, //
100       SIW_GATEWAYS, //
101       SIW_INTERFACES, //
102       SIW_2HOP, //
103       SIW_SGW, //
104       //
105       // specials
106       SIW_VERSION,//
107       SIW_CONFIG, //
108       SIW_PLUGINS, //
109       //
110       // Freifunk special
111       SIW_NEIGHBORS_FREIFUNK //
112       };
113
114   unsigned int i;
115
116   if (!functions->is_command)
117     return 0;
118
119   for (i = 0; i < ARRAY_SIZE(SIW_ENTRIES); ++i) {
120     unsigned int siw = SIW_ENTRIES[i];
121     if (functions->is_command(requ, siw))
122       return siw;
123   }
124
125   return 0;
126 }
127
128 static void write_data(void *foo __attribute__ ((unused))) {
129   fd_set set;
130   int result, i, max;
131   struct timeval tv;
132
133   if (outbuffer.count <= 0) {
134     /* exit early if there is nothing to send */
135     return;
136   }
137
138   FD_ZERO(&set);
139   max = 0;
140   for (i = 0; i < outbuffer.count; i++) {
141     if (outbuffer.socket[i] < 0) {
142       continue;
143     }
144
145     /* And we cast here since we get a warning on Win32 */
146     FD_SET((unsigned int ) (outbuffer.socket[i]), &set);
147
148     if (outbuffer.socket[i] > max) {
149       max = outbuffer.socket[i];
150     }
151   }
152
153   tv.tv_sec = 0;
154   tv.tv_usec = 0;
155
156   result = select(max + 1, NULL, &set, NULL, &tv);
157   if (result <= 0) {
158     /* exit early if any of the sockets is not ready for writing */
159     return;
160   }
161
162   for (i = 0; i < MAX_CLIENTS; i++) {
163     if (outbuffer.socket[i] < 0) {
164       continue;
165     }
166
167     result = send(outbuffer.socket[i], outbuffer.buffer[i] + outbuffer.written[i], outbuffer.size[i] - outbuffer.written[i], 0);
168     if (result > 0) {
169       outbuffer.written[i] += result;
170     }
171
172     if ((result < 0) || (outbuffer.written[i] >= outbuffer.size[i])) {
173       /* close this socket and cleanup*/
174       close(outbuffer.socket[i]);
175       outbuffer.socket[i] = -1;
176       free(outbuffer.buffer[i]);
177       outbuffer.buffer[i] = NULL;
178       outbuffer.size[i] = 0;
179       outbuffer.written[i] = 0;
180
181       outbuffer.count--;
182     }
183   }
184
185   if (!outbuffer.count) {
186     olsr_stop_timer(writetimer_entry);
187   }
188 }
189
190 static void send_info(const char * req, unsigned int send_what, int the_socket, unsigned int status) {
191   struct autobuf abuf;
192   unsigned int outputLength = 0;
193
194   const char *content_type = functions->determine_mime_type ? functions->determine_mime_type(send_what) : "text/plain; charset=utf-8";
195   int contentLengthIndex = 0;
196   int headerLength = 0;
197
198   abuf_init(&abuf, 2 * 4096);
199
200   if (config->http_headers) {
201     http_header_build(name, status, content_type, &abuf, &contentLengthIndex);
202     headerLength = abuf.len;
203   }
204
205   if (status == INFO_HTTP_OK) {
206     /* OK */
207
208     // only add if normal format
209     if (send_what & SIW_ALL) {
210       typedef struct {
211         unsigned int siw;
212         printer_generic func;
213       } SiwLookupTableEntry;
214
215       SiwLookupTableEntry funcs[] = {
216         { SIW_NEIGHBORS , functions->neighbors  }, //
217         { SIW_LINKS     , functions->links      }, //
218         { SIW_ROUTES    , functions->routes     }, //
219         { SIW_HNA       , functions->hna        }, //
220         { SIW_MID       , functions->mid        }, //
221         { SIW_TOPOLOGY  , functions->topology   }, //
222         { SIW_GATEWAYS  , functions->gateways   }, //
223         { SIW_INTERFACES, functions->interfaces }, //
224         { SIW_2HOP      , functions->twohop     }, //
225         { SIW_SGW       , functions->sgw        }, //
226         //
227         { SIW_VERSION, functions->version }, //
228         { SIW_CONFIG, functions->config }, //
229         { SIW_PLUGINS, functions->plugins } //
230         };
231
232       unsigned int i;
233       unsigned int preLength;
234
235       if (functions->output_start) {
236         functions->output_start(&abuf);
237       }
238
239       preLength = abuf.len;
240
241       for (i = 0; i < ARRAY_SIZE(funcs); i++) {
242         if (send_what & funcs[i].siw) {
243           printer_generic func = funcs[i].func;
244           if (func) {
245             func(&abuf);
246           }
247         }
248       }
249
250       outputLength = abuf.len - preLength;
251
252       if (functions->output_end) {
253         functions->output_end(&abuf);
254       }
255     } else if ((send_what & SIW_OLSRD_CONF) && functions->olsrd_conf) {
256       /* this outputs the olsrd.conf text directly, not normal format */
257       unsigned int preLength = abuf.len;
258       functions->olsrd_conf(&abuf);
259       outputLength = abuf.len - preLength;
260     }
261
262     if (!abuf.len || !outputLength) {
263       status = INFO_HTTP_NOCONTENT;
264       abuf.buf[0] = '\0';
265       abuf.len = 0;
266       if (config->http_headers) {
267         http_header_build(name, status, content_type, &abuf, &contentLengthIndex);
268         headerLength = abuf.len;
269       }
270     }
271   }
272
273   if (status != INFO_HTTP_OK) {
274     if (functions->output_error) {
275       functions->output_error(&abuf, status, req, config->http_headers);
276     } else if (status == INFO_HTTP_NOCONTENT) {
277       /* wget can't handle output of zero length */
278       abuf_puts(&abuf, "\n");
279     }
280   }
281
282   if (config->http_headers) {
283     http_header_adjust_content_length(&abuf, contentLengthIndex, abuf.len - headerLength);
284   }
285
286   /* avoid a memcpy: just move the abuf.buf pointer and clear abuf */
287   outbuffer.buffer[outbuffer.count] = abuf.buf;
288   outbuffer.size[outbuffer.count] = abuf.len;
289   outbuffer.written[outbuffer.count] = 0;
290   outbuffer.socket[outbuffer.count] = the_socket;
291   abuf.buf = NULL;
292   abuf.len = 0;
293   abuf.size = 0;
294
295   outbuffer.count++;
296
297   if (outbuffer.count == 1) {
298     writetimer_entry = olsr_start_timer(100, 0, OLSR_TIMER_PERIODIC, &write_data, NULL, 0);
299   }
300
301   abuf_free(&abuf);
302 }
303
304 static char * skipLeadingWhitespace(char * requ, size_t *len) {
305   while (isspace(*requ) && (*requ != '\0')) {
306     *len = *len - 1;
307     requ++;
308   }
309   return requ;
310 }
311
312 static char * stripEOLs(char * requ, size_t *len) {
313   while (isspace(requ[*len - 1]) && (requ[*len - 1] != '\0')) {
314     *len = *len - 1;
315     requ[*len] = '\0';
316   }
317   return requ;
318 }
319
320 static char * cutAtFirstEOL(char * requ, size_t *len) {
321   char * s = requ;
322   size_t l = 0;
323   while (!((*s == '\n') || (*s == '\r')) && (*s != '\0')) {
324     s++;
325     l++;
326   }
327   if ((*s == '\n') || (*s == '\r')) {
328     *s = '\0';
329   }
330   *len = l;
331   return requ;
332 }
333
334 static char * parseRequest(char * requ, size_t *len) {
335   char * req = requ;
336
337   if (!req || !*len) {
338     return requ;
339   }
340
341   req = skipLeadingWhitespace(req, len);
342   req = stripEOLs(req, len);
343
344   /* HTTP request: GET whitespace URI whitespace HTTP/1.1 */
345   if (*len < (3 + 1 + 1 + 1 + 8)) {
346     return req;
347   }
348
349   if (strncasecmp(req, "GET", 3) || !isspace(req[3])) {
350     /* does not start with 'GET ' */
351     return req;
352   }
353
354   /* skip 'GET ' and further leading whitespace */
355   req = skipLeadingWhitespace(&req[4], len);
356   if (!*len) return req;
357
358   /* cut req at the first '\n' */
359   req = cutAtFirstEOL(req, len);
360   if (!*len) return req;
361
362   /* strip req of trailing EOL and whitespace */
363   req = stripEOLs(req, len);
364   if (*len < 9) return req;
365
366   if (!isspace(req[*len - 9]) //
367       || strncasecmp(&req[*len - 8], "HTTP/1.", 7) //
368       || ((req[*len - 1] != '1') && (req[*len - 1] != '0'))) {
369     return req;
370   }
371   *len = *len - 8;
372   req[*len] = '\0';
373   if (!*len) return req;
374
375   /* strip req of trailing EOL and whitespace */
376   req = stripEOLs(req, len);
377
378   return req;
379 }
380
381 static void ipc_action(int fd, void *data __attribute__ ((unused)), unsigned int flags __attribute__ ((unused))) {
382 #ifndef NODEBUG
383   char addr[INET6_ADDRSTRLEN];
384 #endif /* NODEBUG */
385
386   char * req = NULL;
387   unsigned int http_status = INFO_HTTP_OK;
388   union olsr_sockaddr sock_addr;
389   socklen_t sock_addr_len = sizeof(sock_addr);
390   fd_set rfds;
391   struct timeval tv;
392   unsigned int send_what = 0;
393   int ipc_connection = -1;
394
395   if (outbuffer.count >= MAX_CLIENTS) {
396     return;
397   }
398
399   if ((ipc_connection = accept(fd, &sock_addr.in, &sock_addr_len)) == -1) {
400 #ifndef NODEBUG
401     olsr_printf(1, "(%s) accept()=%s\n", name, strerror(errno));
402 #endif /* NODEBUG */
403     return;
404   }
405
406 #ifndef NODEBUG
407   if (!inet_ntop( //
408       olsr_cnf->ip_version, //
409       (olsr_cnf->ip_version == AF_INET) ? (void *) &sock_addr.in4.sin_addr : (void *) &sock_addr.in6.sin6_addr, //
410       addr, //
411       sizeof(addr))) {
412     addr[0] = '\0';
413   }
414 #endif /* NODEBUG */
415
416   tv.tv_sec = tv.tv_usec = 0;
417   if (olsr_cnf->ip_version == AF_INET) {
418     if (!ip4equal(&sock_addr.in4.sin_addr, &config->accept_ip.v4) && (config->accept_ip.v4.s_addr != INADDR_ANY) //
419         && (!config->allow_localhost || (ntohl(sock_addr.in4.sin_addr.s_addr) != INADDR_LOOPBACK))) {
420 #ifndef NODEBUG
421       olsr_printf(1, "(%s) From host(%s) not allowed!\n", name, addr);
422 #endif /* NODEBUG */
423       close(ipc_connection);
424       return;
425     }
426   } else {
427     /* Use in6addr_any (::) in olsr.conf to allow anybody. */
428     if (!ip6equal(&sock_addr.in6.sin6_addr, &config->accept_ip.v6) && !ip6equal(&config->accept_ip.v6, &in6addr_any)) {
429 #ifndef NODEBUG
430       olsr_printf(1, "(%s) From host(%s) not allowed!\n", name, addr);
431 #endif /* NODEBUG */
432       close(ipc_connection);
433       return;
434     }
435   }
436
437 #ifndef NODEBUG
438   olsr_printf(2, "(%s) Connect from %s\n", name, addr);
439 #endif /* NODEBUG */
440
441   /* purge read buffer to prevent blocking on linux */
442   FD_ZERO(&rfds);
443   FD_SET((unsigned int ) ipc_connection, &rfds); /* Win32 needs the cast here */
444   if (0 <= select(ipc_connection + 1, &rfds, NULL, NULL, &tv)) {
445     char requ[1024];
446     ssize_t s = recv(ipc_connection, (void *) &requ, sizeof(requ) - 1, 0); /* Win32 needs the cast here */
447
448     if (s >= (ssize_t) (sizeof(requ) - 1)) {
449       /* input was much too long, just skip the rest */
450       while (recv(ipc_connection, (void *) &sink_buffer, sizeof(sink_buffer), 0) == sizeof(sink_buffer))
451         ;
452       s = -1;
453     }
454
455     if (0 <= s) {
456       req = requ;
457       req[s] = '\0';
458       req = parseRequest(req, (size_t*)&s);
459       if ((req[0] == '\0') || ((req[0] == '/') && (req[1] == '\0'))) {
460         /* empty or '/' */
461         send_what = SIW_ALL;
462       } else {
463         send_what = determine_action(req);
464       }
465     }
466
467     if (!send_what) {
468       http_status = INFO_HTTP_NOTFOUND;
469     }
470   }
471
472   send_info(req ? req : "", send_what, ipc_connection, http_status);
473 }
474
475 static int plugin_ipc_init(void) {
476   union olsr_sockaddr sock_addr;
477   uint32_t yes = 1;
478   socklen_t sock_addr_len;
479
480   /* Init ipc socket */
481   if ((ipc_socket = socket(olsr_cnf->ip_version, SOCK_STREAM, 0)) == -1) {
482 #ifndef NODEBUG
483     olsr_printf(1, "(%s) socket()=%s\n", name, strerror(errno));
484 #endif /* NODEBUG */
485     goto error_out;
486   }
487
488   if (setsockopt(ipc_socket, SOL_SOCKET, SO_REUSEADDR, (char *) &yes, sizeof(yes)) < 0) {
489 #ifndef NODEBUG
490     olsr_printf(1, "(%s) setsockopt()=%s\n", name, strerror(errno));
491 #endif /* NODEBUG */
492     goto error_out;
493   }
494
495 #if (defined __FreeBSD__ || defined __FreeBSD_kernel__) && defined SO_NOSIGPIPE
496   if (setsockopt(ipc_socket, SOL_SOCKET, SO_NOSIGPIPE, (char *) &yes, sizeof(yes)) < 0) {
497     perror("SO_NOSIGPIPE failed");
498     goto error_out;
499   }
500 #endif /* (defined __FreeBSD__ || defined __FreeBSD_kernel__) && defined SO_NOSIGPIPE */
501
502 #if defined __linux__ && defined IPV6_V6ONLY
503   if (config->ipv6_only && (olsr_cnf->ip_version == AF_INET6) //
504       && (setsockopt(ipc_socket, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &yes, sizeof(yes)) < 0)) {
505     perror("IPV6_V6ONLY failed");
506     goto error_out;
507   }
508 #endif /* defined __linux__ && defined IPV6_V6ONLY */
509
510   /* complete the socket structure */
511   memset(&sock_addr, 0, sizeof(sock_addr));
512   if (olsr_cnf->ip_version == AF_INET) {
513     sock_addr.in4.sin_family = AF_INET;
514     sock_addr_len = sizeof(struct sockaddr_in);
515 #ifdef SIN6_LEN
516     sock_addr.in4.sin_len = sock_addr_len;
517 #endif /* SIN6_LEN */
518     sock_addr.in4.sin_addr.s_addr = config->listen_ip.v4.s_addr;
519     sock_addr.in4.sin_port = htons(config->ipc_port);
520   } else {
521     sock_addr.in6.sin6_family = AF_INET6;
522     sock_addr_len = sizeof(struct sockaddr_in6);
523 #ifdef SIN6_LEN
524     sock_addr.in6.sin6_len = sock_addr_len;
525 #endif /* SIN6_LEN */
526     sock_addr.in6.sin6_addr = config->listen_ip.v6;
527     sock_addr.in6.sin6_port = htons(config->ipc_port);
528   }
529
530   /* bind the socket to the port number */
531   if (bind(ipc_socket, &sock_addr.in, sock_addr_len) == -1) {
532 #ifndef NODEBUG
533     olsr_printf(1, "(%s) bind()=%s\n", name, strerror(errno));
534 #endif /* NODEBUG */
535     goto error_out;
536   }
537
538   /* show that we are willing to listen */
539   if (listen(ipc_socket, 1) == -1) {
540 #ifndef NODEBUG
541     olsr_printf(1, "(%s) listen()=%s\n", name, strerror(errno));
542 #endif /* NODEBUG */
543     goto error_out;
544   }
545
546   /* Register with olsrd */
547   add_olsr_socket(ipc_socket, &ipc_action, NULL, NULL, SP_PR_READ);
548
549 #ifndef NODEBUG
550   olsr_printf(2, "(%s) listening on port %d\n", name, config->ipc_port);
551 #endif /* NODEBUG */
552
553   return 1;
554
555   error_out: //
556   if (ipc_socket >= 0) {
557     close(ipc_socket);
558     ipc_socket = -1;
559   }
560   return 0;
561 }
562
563 int info_plugin_init(const char * plugin_name, info_plugin_functions_t *plugin_functions, info_plugin_config_t *plugin_config) {
564   int i;
565
566   assert(plugin_name);
567   assert(plugin_functions);
568   assert(plugin_config);
569
570   name = plugin_name;
571   functions = plugin_functions;
572   config = plugin_config;
573
574   memset(&outbuffer, 0, sizeof(outbuffer));
575   for (i = 0; i < MAX_CLIENTS; ++i) {
576     outbuffer.socket[i] = -1;
577   }
578
579   ipc_socket = -1;
580
581   if (functions->init) {
582     functions->init(name);
583   }
584
585   plugin_ipc_init();
586   return 1;
587 }
588
589 void info_plugin_exit(void) {
590   int i;
591
592   if (ipc_socket != -1) {
593     close(ipc_socket);
594     ipc_socket = -1;
595   }
596   for (i = 0; i < MAX_CLIENTS; ++i) {
597     if (outbuffer.buffer[i]) {
598       free(outbuffer.buffer[i]);
599       outbuffer.buffer[i] = NULL;
600     }
601     outbuffer.size[i] = 0;
602     outbuffer.written[i] = 0;
603     if (outbuffer.socket[i]) {
604       close(outbuffer.socket[i]);
605       outbuffer.socket[i] = -1;
606     }
607   }
608   outbuffer.count = 0;
609 }