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