info: trival debug output fix
[olsrd.git] / lib / info / olsrd_info.c
1 /*
2  * The olsr.org Optimized Link-State Routing daemon (olsrd)
3  *
4  * (c) by the OLSR project
5  *
6  * See our Git repository to find out who worked on this file
7  * and thus is a copyright holder on it.
8  *
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  *
15  * * Redistributions of source code must retain the above copyright
16  *   notice, this list of conditions and the following disclaimer.
17  * * Redistributions in binary form must reproduce the above copyright
18  *   notice, this list of conditions and the following disclaimer in
19  *   the documentation and/or other materials provided with the
20  *   distribution.
21  * * Neither the name of olsr.org, olsrd nor the names of its
22  *   contributors may be used to endorse or promote products derived
23  *   from this software without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
28  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
29  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
30  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
31  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
32  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
33  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
35  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  *
38  * Visit http://www.olsr.org for more information.
39  *
40  * If you find this software useful feel free to make a donation
41  * to the project. For more information see the website or contact
42  * the copyright holders.
43  *
44  */
45
46 #include <arpa/inet.h>
47 #include <unistd.h>
48 #include <assert.h>
49 #include <ctype.h>
50 #include <stdbool.h>
51
52 #include "olsrd_info.h"
53 #include "olsr.h"
54 #include "scheduler.h"
55 #include "ipcalc.h"
56 #include "http_headers.h"
57
58 #ifdef _WIN32
59 #define close(x) closesocket(x)
60 #endif /* _WIN32 */
61
62 #define MAX_CLIENTS 8
63
64 /*
65  * There is the problem that writing to a network socket can block,
66  * and the olsrd scheduler does not care about write events.
67  *
68  * There was a case that olsrd just froze for minutes when people used
69  * jsoninfo/txtinfo with large topologies over bad WiFi.
70  *
71  * This is the solution that was chosen at that time, unwilling to
72  * rewrite the whole scheduler:
73  * A timer was added and each time it expires each non-empty buffer
74  * in this structure will try to write data into a "non-blocking"
75  * socket until all data is sent, so that no blocking occurs.
76  */
77 typedef struct {
78   int socket[MAX_CLIENTS];
79   char *buffer[MAX_CLIENTS];
80   size_t size[MAX_CLIENTS];
81   size_t written[MAX_CLIENTS];
82   int count;
83 } info_plugin_outbuffer_t;
84
85 static const char * name;
86
87 static info_plugin_functions_t *functions = NULL;
88
89 static info_plugin_config_t *config = NULL;
90
91 static int ipc_socket = -1;
92
93 static info_plugin_outbuffer_t outbuffer;
94
95 static struct timer_entry *writetimer_entry = NULL;
96
97 static struct info_cache_t info_cache;
98
99 #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
100
101 static char * skipMultipleSlashes(char * requ, size_t* len) {
102   char * r = requ;
103
104   if ((r[0] == '\0') // zero length
105       || (r[0] != '/') // does not start with a slash
106       || (r[1] != '/')) // does not have another slash
107   {
108     return r;
109   }
110
111   while (r[1] == '/') {
112     r++;
113     if (len) {
114       *len = *len - 1;
115     }
116   }
117   return r;
118 }
119
120 static unsigned long long SIW_ENTRIES_ALL[] = {
121 //
122     SIW_NEIGHBORS,//
123     SIW_LINKS, //
124     SIW_ROUTES, //
125     SIW_HNA, //
126     SIW_MID, //
127     SIW_TOPOLOGY, //
128     SIW_GATEWAYS, //
129     SIW_INTERFACES, //
130     SIW_2HOP, //
131     SIW_SGW, //
132     SIW_PUD_POSITION, //
133     SIW_RUNTIME_ALL,//
134     SIW_NEIGHBORS_FREIFUNK, //
135     //
136     SIW_VERSION,//
137     SIW_CONFIG, //
138     SIW_PLUGINS, //
139     SIW_STARTUP_ALL, //
140     //
141     SIW_ALL, //
142     //
143     SIW_OLSRD_CONF, //
144     //
145     SIW_NETJSON_NETWORK_ROUTES, //
146     SIW_NETJSON_NETWORK_GRAPH, //
147     SIW_NETJSON_DEVICE_CONFIGURATION, //
148     SIW_NETJSON_DEVICE_MONITORING, //
149     SIW_NETJSON_NETWORK_COLLECTION //
150     };
151
152 long cache_timeout_generic(info_plugin_config_t *plugin_config, unsigned long long siw) {
153   long timeout = !plugin_config ? 0 : plugin_config->cache_timeout;
154   if (timeout <= 0) {
155     return timeout;
156   }
157
158   switch (siw) {
159     case SIW_NEIGHBORS:
160     case SIW_LINKS:
161     case SIW_ROUTES:
162     case SIW_HNA:
163     case SIW_MID:
164     case SIW_TOPOLOGY:
165     case SIW_GATEWAYS:
166     case SIW_INTERFACES:
167     case SIW_2HOP:
168     case SIW_SGW:
169     case SIW_PUD_POSITION:
170
171     case SIW_NETJSON_NETWORK_ROUTES:
172     case SIW_NETJSON_NETWORK_GRAPH:
173     case SIW_NETJSON_DEVICE_CONFIGURATION:
174     case SIW_NETJSON_DEVICE_MONITORING:
175     case SIW_NETJSON_NETWORK_COLLECTION:
176       return timeout;
177
178     case SIW_VERSION:
179     case SIW_CONFIG:
180     case SIW_PLUGINS:
181       return LONG_MAX;
182
183     default:
184       /* not cached */
185       return false;
186   }
187 }
188
189 static void info_plugin_cache_init(bool init) {
190   unsigned int i;
191
192   if (!functions->cache_timeout) {
193     return;
194   }
195
196   for (i = 0; i < ARRAY_SIZE(SIW_ENTRIES_ALL); ++i) {
197     unsigned long long siw = SIW_ENTRIES_ALL[i];
198     struct info_cache_entry_t * entry = info_cache_get_entry(&info_cache, siw);
199     if (!entry) {
200       continue;
201     }
202
203     if (init) {
204       entry->timestamp = 0;
205       abuf_init(&entry->buf, 0);
206     } else {
207       abuf_free(&entry->buf);
208       entry->timestamp = 0;
209     }
210   }
211 }
212
213 static INLINE void info_plugin_cache_init_entry(struct info_cache_entry_t * entry) {
214   if (!entry->buf.buf) {
215     entry->timestamp = 0;
216     abuf_init(&entry->buf, AUTOBUFCHUNK);
217     assert(!entry->timestamp);
218     assert(!entry->buf.len);
219     assert(entry->buf.size == AUTOBUFCHUNK);
220     assert(entry->buf.buf);
221   } else {
222     assert(entry->timestamp >= 0);
223     assert(entry->buf.len >= 0);
224     assert(entry->buf.size >= AUTOBUFCHUNK);
225     assert(entry->buf.buf);
226   }
227 }
228
229 static unsigned int determine_single_action(char *requ) {
230   unsigned int i;
231   unsigned long long siw_mask = !functions->supported_commands_mask ? SIW_EVERYTHING : functions->supported_commands_mask();
232
233   if (!functions->is_command || !siw_mask)
234     return 0;
235
236   for (i = 0; i < ARRAY_SIZE(SIW_ENTRIES_ALL); ++i) {
237     unsigned long long siw = SIW_ENTRIES_ALL[i];
238     if ((siw & siw_mask) && functions->is_command(requ, siw))
239       return siw;
240   }
241
242   return 0;
243 }
244
245 static unsigned int determine_action(char *requ) {
246   if (!functions->is_command) {
247     return 0;
248   }
249
250   if (!requ || (requ[0] == '\0')) {
251     /* no more text */
252     return 0;
253   }
254
255   /* requ is guaranteed to be at least 1 character long */
256
257   if (!functions->supportsCompositeCommands) {
258     /* no support for composite commands */
259     return determine_single_action(requ);
260   }
261
262   /* composite commands */
263
264   {
265     unsigned int action = 0;
266
267     char * requestSegment = requ;
268     while (requestSegment) {
269       requestSegment = skipMultipleSlashes(requestSegment, NULL);
270       if (requestSegment[0] == '\0') {
271         /* there is no more text */
272         requestSegment = NULL;
273         continue;
274       }
275
276       /* there is more text */
277
278       {
279         unsigned int r = 0;
280         char * requestSegmentTail = strchr(&requestSegment[1], '/');
281
282         if (!requestSegmentTail) {
283           /* there isn't another slash, process everything that is left */
284           r = determine_single_action(requestSegment);
285         } else {
286           /* there is another slash, process everything before the slash */
287           char savedCharacter = *requestSegmentTail;
288           *requestSegmentTail = '\0';
289           r = determine_single_action(requestSegment);
290           *requestSegmentTail = savedCharacter;
291         }
292
293         if (!r) {
294           /* not found */
295           return 0;
296         }
297
298         action |= r;
299
300         /* process everything that is left in the next iteration */
301         requestSegment = requestSegmentTail;
302       }
303     }
304
305     return action;
306   }
307 }
308
309 static void send_status_no_retries(const char * req, int the_socket, unsigned int status) {
310   struct autobuf abuf;
311
312   abuf_init(&abuf, AUTOBUFCHUNK);
313
314   if (config->http_headers) {
315     http_header_build_result(status, &abuf);
316   } else if (status != INFO_HTTP_OK) {
317     if (functions->output_error) {
318       functions->output_error(&abuf, status, req, config->http_headers);
319     } else if (status == INFO_HTTP_NOCONTENT) {
320       /* wget can't handle output of zero length */
321       abuf_puts(&abuf, "\n");
322     }
323   }
324
325   (void) send(the_socket, abuf.buf, abuf.len, 0);
326   close(the_socket);
327   abuf_free(&abuf);
328 }
329
330 static void write_data(void *unused __attribute__((unused))) {
331   fd_set set;
332   int result, i, max;
333   struct timeval tv;
334
335   if (outbuffer.count <= 0) {
336     /* exit early if there is nothing to send */
337     return;
338   }
339
340   FD_ZERO(&set);
341   max = 0;
342   for (i = 0; i < MAX_CLIENTS; i++) {
343     if (outbuffer.socket[i] < 0) {
344       continue;
345     }
346
347     /* And we cast here since we get a warning on Win32 */
348     FD_SET((unsigned int ) (outbuffer.socket[i]), &set);
349
350     if (outbuffer.socket[i] > max) {
351       max = outbuffer.socket[i];
352     }
353   }
354
355   tv.tv_sec = 0;
356   tv.tv_usec = 0;
357
358   result = select(max + 1, NULL, &set, NULL, &tv);
359   if (result <= 0) {
360     /* exit early if any of the sockets is not ready for writing */
361     return;
362   }
363
364   for (i = 0; i < MAX_CLIENTS; i++) {
365     if (outbuffer.socket[i] < 0) {
366       continue;
367     }
368
369     result = send(outbuffer.socket[i], outbuffer.buffer[i] + outbuffer.written[i], outbuffer.size[i] - outbuffer.written[i], 0);
370     if (result > 0) {
371       outbuffer.written[i] += result;
372     }
373
374 #if EWOULDBLOCK == EAGAIN
375     if ((result < 0) && (errno == EAGAIN)) {
376 #else
377     if ((result < 0) && ((errno == EWOULDBLOCK) || (errno == EAGAIN))) {
378 #endif
379       continue;
380     }
381
382     if ((result < 0) || (outbuffer.written[i] >= outbuffer.size[i])) {
383       /* close this socket and cleanup*/
384       close(outbuffer.socket[i]);
385       outbuffer.socket[i] = -1;
386       free(outbuffer.buffer[i]);
387       outbuffer.buffer[i] = NULL;
388       outbuffer.size[i] = 0;
389       outbuffer.written[i] = 0;
390
391       outbuffer.count--;
392     }
393   }
394
395   if (!outbuffer.count) {
396     olsr_stop_timer(writetimer_entry);
397   }
398 }
399
400 typedef struct {
401   unsigned long long siw;
402   printer_generic func;
403 } SiwLookupTableEntry;
404
405 static void send_info_from_table(struct autobuf *abuf, unsigned int send_what, SiwLookupTableEntry *funcs, unsigned int funcsSize, unsigned int *outputLength) {
406   unsigned int i;
407   unsigned int preLength;
408   unsigned int what = send_what;
409   cache_timeout_func cache_timeout_f = functions->cache_timeout;
410
411   if (functions->output_start) {
412     functions->output_start(abuf);
413   }
414
415   preLength = abuf->len;
416
417   for (i = 0; (i < funcsSize) && what; i++) {
418     unsigned long long siw = funcs[i].siw;
419     if (what & siw) {
420       printer_generic func = funcs[i].func;
421       if (func) {
422         long cache_timeout = 0;
423         struct info_cache_entry_t *cache_entry = NULL;
424
425         if (cache_timeout_f) {
426           cache_timeout = cache_timeout_f(config, siw);
427           cache_entry = (cache_timeout <= 0) ? NULL : info_cache_get_entry(&info_cache, siw);
428         }
429
430         if (!cache_entry) {
431             func(abuf);
432         } else {
433           long long now;
434           long long age;
435
436           info_plugin_cache_init_entry(cache_entry);
437
438           now = olsr_times();
439           age = llabs(now - cache_entry->timestamp);
440           if (!cache_entry->timestamp || (age >= cache_timeout)) {
441             /* cache is never used before or cache is too old */
442             cache_entry->buf.buf[0] = '\0';
443             cache_entry->buf.len = 0;
444             cache_entry->timestamp = now;
445             func(&cache_entry->buf);
446           }
447
448           abuf_concat(abuf, &cache_entry->buf);
449         }
450       }
451     }
452     what &= ~siw;
453   }
454
455   *outputLength = abuf->len - preLength;
456
457   if (functions->output_end) {
458     functions->output_end(abuf);
459   }
460 }
461
462 static void send_info(const char * req, unsigned int send_what, int the_socket, unsigned int status) {
463   struct autobuf abuf;
464   unsigned int outputLength = 0;
465   unsigned int send_index = 0;
466   bool first_reply = false;
467
468   const char *content_type = functions->determine_mime_type ? functions->determine_mime_type(send_what) : "text/plain; charset=utf-8";
469   int contentLengthIndex = 0;
470   int headerLength = 0;
471
472   assert(outbuffer.count <= MAX_CLIENTS);
473
474   abuf_init(&abuf, AUTOBUFCHUNK);
475
476   if (config->http_headers) {
477     http_header_build(name, status, content_type, &abuf, &contentLengthIndex);
478     headerLength = abuf.len;
479   }
480
481   if (status == INFO_HTTP_OK) {
482     /* OK */
483
484     // only add if normal format
485     if (send_what & SIW_ALL) {
486       SiwLookupTableEntry funcs[] = {
487         { SIW_NEIGHBORS   , functions->neighbors   }, //
488         { SIW_LINKS       , functions->links       }, //
489         { SIW_ROUTES      , functions->routes      }, //
490         { SIW_HNA         , functions->hna         }, //
491         { SIW_MID         , functions->mid         }, //
492         { SIW_TOPOLOGY    , functions->topology    }, //
493         { SIW_GATEWAYS    , functions->gateways    }, //
494         { SIW_INTERFACES  , functions->interfaces  }, //
495         { SIW_2HOP        , functions->twohop      }, //
496         { SIW_SGW         , functions->sgw         }, //
497         { SIW_PUD_POSITION, functions->pudPosition }, //
498         //
499         { SIW_VERSION     , functions->version     }, //
500         { SIW_CONFIG      , functions->config      }, //
501         { SIW_PLUGINS     , functions->plugins     } //
502       };
503
504       send_info_from_table(&abuf, send_what, funcs, ARRAY_SIZE(funcs), &outputLength);
505     } else if (send_what & SIW_NETJSON) {
506       SiwLookupTableEntry funcs[] = {
507         { SIW_NETJSON_NETWORK_ROUTES      , functions->networkRoutes      }, //
508         { SIW_NETJSON_NETWORK_GRAPH       , functions->networkGraph       }, //
509         { SIW_NETJSON_DEVICE_CONFIGURATION, functions->deviceConfiguration}, //
510         { SIW_NETJSON_DEVICE_MONITORING   , functions->deviceMonitoring   }, //
511         { SIW_NETJSON_NETWORK_COLLECTION  , functions->networkCollection  } //
512       };
513
514       send_info_from_table(&abuf, send_what, funcs, ARRAY_SIZE(funcs), &outputLength);
515     } else if ((send_what & SIW_OLSRD_CONF) && functions->olsrd_conf) {
516       /* this outputs the olsrd.conf text directly, not normal format */
517       unsigned int preLength = abuf.len;
518       functions->olsrd_conf(&abuf);
519       outputLength = abuf.len - preLength;
520     }
521
522     if (!abuf.len || !outputLength) {
523       status = INFO_HTTP_NOCONTENT;
524       abuf.buf[0] = '\0';
525       abuf.len = 0;
526       if (config->http_headers) {
527         http_header_build(name, status, content_type, &abuf, &contentLengthIndex);
528         headerLength = abuf.len;
529       }
530     }
531   }
532
533   if (status != INFO_HTTP_OK) {
534     if (functions->output_error) {
535       functions->output_error(&abuf, status, req, config->http_headers);
536     } else if (status == INFO_HTTP_NOCONTENT) {
537       /* wget can't handle output of zero length */
538       abuf_puts(&abuf, "\n");
539     }
540   }
541
542   if (config->http_headers) {
543     http_header_adjust_content_length(&abuf, contentLengthIndex, abuf.len - headerLength);
544   }
545
546   /*
547    * Determine the last available outbuffer slot.
548    * Search from the end towards the start to avoid starvation of
549    * older replies that are still in-flight (since the send function
550    * iterates from the start towards the end).
551    */
552   send_index = MAX_CLIENTS;
553   while (send_index) {
554     send_index--;
555     if (!outbuffer.buffer[send_index]) {
556       break;
557     }
558   }
559   assert(send_index < MAX_CLIENTS);
560   assert(!outbuffer.buffer[send_index]);
561
562   /* avoid a memcpy: just move the abuf.buf pointer and clear abuf */
563   outbuffer.buffer[send_index] = abuf.buf;
564   outbuffer.size[send_index] = abuf.len;
565   outbuffer.written[send_index] = 0;
566   outbuffer.socket[send_index] = the_socket;
567   abuf.buf = NULL;
568   abuf.len = 0;
569   abuf.size = 0;
570
571   first_reply = !outbuffer.count;
572
573   outbuffer.count++;
574
575   write_data(NULL);
576
577   if (first_reply && outbuffer.buffer[send_index]) {
578     writetimer_entry = olsr_start_timer(10, 0, OLSR_TIMER_PERIODIC, &write_data, NULL, 0);
579   }
580 }
581
582 static char * skipLeadingWhitespace(char * requ, size_t *len) {
583   while (isspace(*requ) && (*requ != '\0')) {
584     *len = *len - 1;
585     requ++;
586   }
587   return requ;
588 }
589
590 static char * stripEOLs(char * requ, size_t *len) {
591   while (isspace(requ[*len - 1]) && (requ[*len - 1] != '\0')) {
592     *len = *len - 1;
593     requ[*len] = '\0';
594   }
595   return requ;
596 }
597
598 static char * stripTrailingSlashes(char * requ, size_t *len) {
599   while ((requ[*len - 1] == '/') && (requ[*len - 1] != '\0')) {
600     *len = *len - 1;
601     requ[*len] = '\0';
602   }
603   return requ;
604 }
605
606 static char * cutAtFirstEOL(char * requ, size_t *len) {
607   char * s = requ;
608   size_t l = 0;
609   while (!((*s == '\n') || (*s == '\r')) && (*s != '\0')) {
610     s++;
611     l++;
612   }
613   if ((*s == '\n') || (*s == '\r')) {
614     *s = '\0';
615   }
616   *len = l;
617   return requ;
618 }
619
620 static char * parseRequest(char * req, size_t *len) {
621   if (!req || !len || !*len) {
622     return req;
623   }
624
625   /* HTTP request: GET whitespace URI whitespace HTTP/1.[01] */
626   if (*len < (3 + 1 + 1 + 1 + 8) //
627       || strncasecmp(req, "GET", 3) //
628       || !isspace(req[3]) //
629       || !isspace(req[*len - 9]) //
630       || strncasecmp(&req[*len - 8], "HTTP/1.", 7) //
631       || ((req[*len - 1] != '1') && (req[*len - 1] != '0'))) {
632     /* too short or does not start with 'GET ' nor ends with ' HTTP/1.[01]'*/
633     return req;
634   }
635
636   /* skip 'GET ' */
637   *len = *len - 4;
638   req = &req[4];
639
640   /* strip ' HTTP/1.[01]' */
641   *len = *len - 9;
642   req[*len] = '\0';
643
644   return req;
645 }
646
647 static void drain_request(int ipc_connection) {
648   static char drain_buffer[AUTOBUFCHUNK];
649
650   /* input was much too long: read until the end for graceful connection termination
651    * because wget can't handle the premature connection termination that is allowed
652    * by the INFO_HTTP_REQUEST_ENTITY_TOO_LARGE HTTP status code
653    */
654   while (recv(ipc_connection, (void *) &drain_buffer, sizeof(drain_buffer), 0) == sizeof(drain_buffer)) {}
655 }
656
657 static void ipc_action(int fd, void *data __attribute__ ((unused)), unsigned int flags __attribute__ ((unused))) {
658 #ifndef NODEBUG
659   char addr[INET6_ADDRSTRLEN];
660 #endif /* NODEBUG */
661
662   int ipc_connection = -1;
663   union olsr_sockaddr sock_addr;
664   socklen_t sock_addr_len = sizeof(sock_addr);
665   bool hostDenied = false;
666   struct timeval timeout;
667   fd_set read_fds;
668   char req_buffer[1024];
669   char * req = req_buffer;
670   ssize_t rx_count = 0;
671   unsigned int send_what = 0;
672   unsigned int http_status = INFO_HTTP_OK;
673
674   *req = '\0';
675
676   if ((ipc_connection = accept(fd, &sock_addr.in, &sock_addr_len)) < 0) {
677 #ifndef NODEBUG
678     olsr_printf(1, "(%s) accept()=%s\n", name, strerror(errno));
679 #endif /* NODEBUG */
680     /* the caller will retry later */
681     return;
682   }
683
684   if (outbuffer.count >= MAX_CLIENTS) {
685     /* limit the number of replies that are in-flight */
686     drain_request(ipc_connection);
687     send_status_no_retries(req, ipc_connection, INFO_HTTP_SERVICE_UNAVAILABLE);
688     return;
689   }
690
691   if (olsr_cnf->ip_version == AF_INET) {
692     hostDenied = //
693         (ntohl(config->accept_ip.v4.s_addr) != INADDR_ANY) //
694         && !ip4equal(&sock_addr.in4.sin_addr, &config->accept_ip.v4) //
695         && (!config->allow_localhost //
696             || (ntohl(sock_addr.in4.sin_addr.s_addr) != INADDR_LOOPBACK));
697   } else {
698     hostDenied = //
699         !ip6equal(&config->accept_ip.v6, &in6addr_any) //
700         && !ip6equal(&sock_addr.in6.sin6_addr, &config->accept_ip.v6) //
701         && (!config->allow_localhost //
702             || !ip6equal(&config->accept_ip.v6, &in6addr_loopback));
703   }
704
705 #ifndef NODEBUG
706   if (!inet_ntop( //
707       olsr_cnf->ip_version, //
708       (olsr_cnf->ip_version == AF_INET) ? (void *) &sock_addr.in4.sin_addr : (void *) &sock_addr.in6.sin6_addr, //
709       addr, //
710       sizeof(addr))) {
711     addr[0] = '\0';
712   }
713 #endif /* NODEBUG */
714
715   if (hostDenied) {
716 #ifndef NODEBUG
717     olsr_printf(1, "(%s) Connect from host %s is not allowed!\n", name, addr);
718 #endif /* NODEBUG */
719     drain_request(ipc_connection);
720     send_info(req, send_what, ipc_connection, INFO_HTTP_FORBIDDEN);
721     return;
722   }
723
724 #ifndef NODEBUG
725   olsr_printf(2, "(%s) Connect from host %s is allowed\n", name, addr);
726 #endif /* NODEBUG */
727
728   timeout.tv_sec = timeout.tv_usec = 0;
729
730   FD_ZERO(&read_fds);
731 #ifndef _WIN32
732   FD_SET(ipc_connection, &read_fds);
733 #else
734   FD_SET((unsigned int ) ipc_connection, &read_fds);
735 #endif
736
737   /* On success, select() and pselect() return the number of file descriptors
738    * contained in the three returned descriptor sets (that is, the total number
739    * of bits that are set in readfds, writefds, exceptfds) which may be zero if
740    * the timeout expires before anything interesting happens. On error, -1 is
741    * returned, and errno is set to indicate the error; the file descriptor sets
742    * are unmodified, and timeout becomes undefined.
743    */
744
745   if (select(ipc_connection + 1, &read_fds, NULL, NULL, &timeout) < 0) {
746 #ifndef NODEBUG
747     olsr_printf(1, "(%s) select()=%s\n", name, strerror(errno));
748 #endif /* NODEBUG */
749     drain_request(ipc_connection);
750     send_info(req, send_what, ipc_connection, INFO_HTTP_INTERNAL_SERVER_ERROR);
751     return;
752   }
753
754   rx_count = recv(ipc_connection, req, sizeof(req_buffer), 0); /* Win32 needs the cast here */
755
756   /* Upon successful completion, recv() shall return the length of the message
757    * in bytes. If no messages are available to be received and the peer has
758    * performed an orderly shutdown, recv() shall return 0. Otherwise, −1 shall
759    * be returned and errno set to indicate the error.
760    */
761
762   if (rx_count < 0) {
763 #ifndef NODEBUG
764     olsr_printf(1, "(%s) rx_count < 0\n", name);
765 #endif /* NODEBUG */
766     *req = '\0';
767     drain_request(ipc_connection);
768     send_info(req, send_what, ipc_connection, INFO_HTTP_INTERNAL_SERVER_ERROR);
769     return;
770   }
771
772   /* rx_count >= 0 */
773
774   if (!rx_count) {
775 #ifndef NODEBUG
776     olsr_printf(1, "(%s) rx_count == 0\n", name);
777 #endif /* NODEBUG */
778     *req = '\0';
779     drain_request(ipc_connection);
780     send_info(req, SIW_EVERYTHING, ipc_connection, INFO_HTTP_OK);
781     return;
782   }
783
784   /* rx_count > 0 */
785
786   if (rx_count >= (ssize_t) sizeof(req_buffer)) {
787 #ifndef NODEBUG
788     olsr_printf(1, "(%s) rx_count > %ld\n", name, (long int) sizeof(req_buffer));
789 #endif /* NODEBUG */
790     req[sizeof(req_buffer) - 1] = '\0';
791     drain_request(ipc_connection);
792     send_info(req, send_what, ipc_connection, INFO_HTTP_REQUEST_ENTITY_TOO_LARGE);
793     return;
794   }
795
796   /* 0 < rx_count < sizeof(requ) */
797
798   req[rx_count] = '\0';
799
800   req = cutAtFirstEOL(req, (size_t*) &rx_count);
801
802   req = stripEOLs(req, (size_t*) &rx_count);
803   req = skipLeadingWhitespace(req, (size_t*) &rx_count);
804
805   req = parseRequest(req, (size_t*) &rx_count);
806
807   req = stripEOLs(req, (size_t*) &rx_count);
808   req = stripTrailingSlashes(req, (size_t*) &rx_count);
809   req = skipLeadingWhitespace(req, (size_t*) &rx_count);
810   req = skipMultipleSlashes(req, (size_t*) &rx_count);
811
812   if ((req[0] == '\0') //
813       || ((req[0] == '/') && (req[1] == '\0'))) {
814     /* empty or '/' */
815     send_what = SIW_EVERYTHING;
816   } else {
817     send_what = determine_action(req);
818   }
819
820   if (!send_what) {
821     http_status = INFO_HTTP_NOTFOUND;
822   }
823
824   send_info(req, send_what, ipc_connection, http_status);
825 }
826
827 static int plugin_ipc_init(void) {
828   union olsr_sockaddr sock_addr;
829   uint32_t yes = 1;
830   socklen_t sock_addr_len;
831
832   /* Init ipc socket */
833   if ((ipc_socket = socket(olsr_cnf->ip_version, SOCK_STREAM, 0)) == -1) {
834 #ifndef NODEBUG
835     olsr_printf(1, "(%s) socket()=%s\n", name, strerror(errno));
836 #endif /* NODEBUG */
837     goto error_out;
838   }
839
840   if (setsockopt(ipc_socket, SOL_SOCKET, SO_REUSEADDR, (char *) &yes, sizeof(yes)) < 0) {
841 #ifndef NODEBUG
842     olsr_printf(1, "(%s) setsockopt()=%s\n", name, strerror(errno));
843 #endif /* NODEBUG */
844     goto error_out;
845   }
846
847 #if (defined __FreeBSD__ || defined __FreeBSD_kernel__) && defined SO_NOSIGPIPE
848   if (setsockopt(ipc_socket, SOL_SOCKET, SO_NOSIGPIPE, (char *) &yes, sizeof(yes)) < 0) {
849     perror("SO_NOSIGPIPE failed");
850     goto error_out;
851   }
852 #endif /* (defined __FreeBSD__ || defined __FreeBSD_kernel__) && defined SO_NOSIGPIPE */
853
854 #if defined __linux__ && defined IPV6_V6ONLY
855   if (config->ipv6_only && (olsr_cnf->ip_version == AF_INET6) //
856       && (setsockopt(ipc_socket, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &yes, sizeof(yes)) < 0)) {
857     perror("IPV6_V6ONLY failed");
858     goto error_out;
859   }
860 #endif /* defined __linux__ && defined IPV6_V6ONLY */
861
862   /* complete the socket structure */
863   memset(&sock_addr, 0, sizeof(sock_addr));
864   if (olsr_cnf->ip_version == AF_INET) {
865     sock_addr.in4.sin_family = AF_INET;
866     sock_addr_len = sizeof(struct sockaddr_in);
867 #ifdef SIN6_LEN
868     sock_addr.in4.sin_len = sock_addr_len;
869 #endif /* SIN6_LEN */
870     sock_addr.in4.sin_addr.s_addr = config->listen_ip.v4.s_addr;
871     sock_addr.in4.sin_port = htons(config->ipc_port);
872   } else {
873     sock_addr.in6.sin6_family = AF_INET6;
874     sock_addr_len = sizeof(struct sockaddr_in6);
875 #ifdef SIN6_LEN
876     sock_addr.in6.sin6_len = sock_addr_len;
877 #endif /* SIN6_LEN */
878     sock_addr.in6.sin6_addr = config->listen_ip.v6;
879     sock_addr.in6.sin6_port = htons(config->ipc_port);
880   }
881
882   /* bind the socket to the port number */
883   if (bind(ipc_socket, &sock_addr.in, sock_addr_len) == -1) {
884 #ifndef NODEBUG
885     olsr_printf(1, "(%s) bind()=%s\n", name, strerror(errno));
886 #endif /* NODEBUG */
887     goto error_out;
888   }
889
890   /* show that we are willing to listen */
891   if (listen(ipc_socket, 1) == -1) {
892 #ifndef NODEBUG
893     olsr_printf(1, "(%s) listen()=%s\n", name, strerror(errno));
894 #endif /* NODEBUG */
895     goto error_out;
896   }
897
898   /* Register with olsrd */
899   add_olsr_socket(ipc_socket, &ipc_action, NULL, NULL, SP_PR_READ);
900
901 #ifndef NODEBUG
902   olsr_printf(2, "(%s) listening on port %d\n", name, config->ipc_port);
903 #endif /* NODEBUG */
904
905   return 1;
906
907   error_out: //
908   if (ipc_socket >= 0) {
909     close(ipc_socket);
910     ipc_socket = -1;
911   }
912   return 0;
913 }
914
915 int info_plugin_init(const char * plugin_name, info_plugin_functions_t *plugin_functions, info_plugin_config_t *plugin_config) {
916   int i;
917
918   assert(plugin_name);
919   assert(plugin_functions);
920   assert(plugin_config);
921
922   name = plugin_name;
923   functions = plugin_functions;
924   config = plugin_config;
925
926   memset(&outbuffer, 0, sizeof(outbuffer));
927   for (i = 0; i < MAX_CLIENTS; ++i) {
928     outbuffer.socket[i] = -1;
929   }
930
931   ipc_socket = -1;
932
933   if (functions->init) {
934     functions->init(name);
935   }
936
937   info_plugin_cache_init(true);
938
939   return plugin_ipc_init();
940 }
941
942 void info_plugin_exit(void) {
943   int i;
944
945   if (ipc_socket != -1) {
946     close(ipc_socket);
947     ipc_socket = -1;
948   }
949   for (i = 0; i < MAX_CLIENTS; ++i) {
950     if (outbuffer.buffer[i]) {
951       free(outbuffer.buffer[i]);
952       outbuffer.buffer[i] = NULL;
953     }
954     outbuffer.size[i] = 0;
955     outbuffer.written[i] = 0;
956     if (outbuffer.socket[i]) {
957       close(outbuffer.socket[i]);
958       outbuffer.socket[i] = -1;
959     }
960   }
961   outbuffer.count = 0;
962
963   info_plugin_cache_init(false);
964 }