info: sanitise the request right after it's been received
[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 // null pointer
105       || (len && !*len) // zero length
106       || (r[0] == '\0') // zero length
107       || (r[0] != '/') // does not start with a slash
108       || (r[1] != '/')) // does not have another slash
109   {
110     return r;
111   }
112
113   while (r[1] == '/') {
114     r++;
115     if (len) {
116       *len = *len - 1;
117     }
118   }
119   return r;
120 }
121
122 static unsigned long long SIW_ENTRIES_ALL[] = {
123 //
124     SIW_NEIGHBORS,//
125     SIW_LINKS, //
126     SIW_ROUTES, //
127     SIW_HNA, //
128     SIW_MID, //
129     SIW_TOPOLOGY, //
130     SIW_GATEWAYS, //
131     SIW_INTERFACES, //
132     SIW_2HOP, //
133     SIW_SGW, //
134     SIW_PUD_POSITION, //
135     SIW_RUNTIME_ALL,//
136     SIW_NEIGHBORS_FREIFUNK, //
137     //
138     SIW_VERSION,//
139     SIW_CONFIG, //
140     SIW_PLUGINS, //
141     SIW_STARTUP_ALL, //
142     //
143     SIW_ALL, //
144     //
145     SIW_OLSRD_CONF, //
146     //
147     SIW_NETJSON_NETWORK_ROUTES, //
148     SIW_NETJSON_NETWORK_GRAPH, //
149     SIW_NETJSON_DEVICE_CONFIGURATION, //
150     SIW_NETJSON_DEVICE_MONITORING, //
151     SIW_NETJSON_NETWORK_COLLECTION //
152     };
153
154 long cache_timeout_generic(info_plugin_config_t *plugin_config, unsigned long long siw) {
155   long timeout = !plugin_config ? 0 : plugin_config->cache_timeout;
156   if (timeout <= 0) {
157     return timeout;
158   }
159
160   switch (siw) {
161     case SIW_NEIGHBORS:
162     case SIW_LINKS:
163     case SIW_ROUTES:
164     case SIW_HNA:
165     case SIW_MID:
166     case SIW_TOPOLOGY:
167     case SIW_GATEWAYS:
168     case SIW_INTERFACES:
169     case SIW_2HOP:
170     case SIW_SGW:
171     case SIW_PUD_POSITION:
172
173     case SIW_NETJSON_NETWORK_ROUTES:
174     case SIW_NETJSON_NETWORK_GRAPH:
175     case SIW_NETJSON_DEVICE_CONFIGURATION:
176     case SIW_NETJSON_DEVICE_MONITORING:
177     case SIW_NETJSON_NETWORK_COLLECTION:
178       return timeout;
179
180     case SIW_VERSION:
181     case SIW_CONFIG:
182     case SIW_PLUGINS:
183       return LONG_MAX;
184
185     default:
186       /* not cached */
187       return false;
188   }
189 }
190
191 static void info_plugin_cache_init(bool init) {
192   unsigned int i;
193
194   if (!functions->cache_timeout) {
195     return;
196   }
197
198   for (i = 0; i < ARRAY_SIZE(SIW_ENTRIES_ALL); ++i) {
199     unsigned long long siw = SIW_ENTRIES_ALL[i];
200     struct info_cache_entry_t * entry = info_cache_get_entry(&info_cache, siw);
201     if (!entry) {
202       continue;
203     }
204
205     if (init) {
206       entry->timestamp = 0;
207       abuf_init(&entry->buf, 0);
208     } else {
209       abuf_free(&entry->buf);
210       entry->timestamp = 0;
211     }
212   }
213 }
214
215 static INLINE void info_plugin_cache_init_entry(struct info_cache_entry_t * entry) {
216   if (!entry->buf.buf) {
217     entry->timestamp = 0;
218     abuf_init(&entry->buf, AUTOBUFCHUNK);
219     assert(!entry->timestamp);
220     assert(!entry->buf.len);
221     assert(entry->buf.size == AUTOBUFCHUNK);
222     assert(entry->buf.buf);
223   } else {
224     assert(entry->timestamp >= 0);
225     assert(entry->buf.len >= 0);
226     assert(entry->buf.size >= AUTOBUFCHUNK);
227     assert(entry->buf.buf);
228   }
229 }
230
231 static unsigned int determine_single_action(char *requ) {
232   unsigned int i;
233   unsigned long long siw_mask = !functions->supported_commands_mask ? SIW_EVERYTHING : functions->supported_commands_mask();
234
235   if (!functions->is_command || !siw_mask)
236     return 0;
237
238   for (i = 0; i < ARRAY_SIZE(SIW_ENTRIES_ALL); ++i) {
239     unsigned long long siw = SIW_ENTRIES_ALL[i];
240     if ((siw & siw_mask) && functions->is_command(requ, siw))
241       return siw;
242   }
243
244   return 0;
245 }
246
247 static unsigned int determine_action(char *requ) {
248   if (!functions->is_command) {
249     return 0;
250   }
251
252   if (!requ || (requ[0] == '\0')) {
253     /* no more text */
254     return 0;
255   }
256
257   /* requ is guaranteed to be at least 1 character long */
258
259   if (!functions->supportsCompositeCommands) {
260     /* no support for composite commands */
261     return determine_single_action(requ);
262   }
263
264   /* composite commands */
265
266   {
267     unsigned int action = 0;
268
269     char * requestSegment = requ;
270     while (requestSegment) {
271       requestSegment = skipMultipleSlashes(requestSegment, NULL);
272       if (requestSegment[0] == '\0') {
273         /* there is no more text */
274         requestSegment = NULL;
275         continue;
276       }
277
278       /* there is more text */
279
280       {
281         unsigned int r = 0;
282         char * requestSegmentTail = strchr(&requestSegment[1], '/');
283
284         if (!requestSegmentTail) {
285           /* there isn't another slash, process everything that is left */
286           r = determine_single_action(requestSegment);
287         } else {
288           /* there is another slash, process everything before the slash */
289           char savedCharacter = *requestSegmentTail;
290           *requestSegmentTail = '\0';
291           r = determine_single_action(requestSegment);
292           *requestSegmentTail = savedCharacter;
293         }
294
295         if (!r) {
296           /* not found */
297           return 0;
298         }
299
300         action |= r;
301
302         /* process everything that is left in the next iteration */
303         requestSegment = requestSegmentTail;
304       }
305     }
306
307     return action;
308   }
309 }
310
311 static void send_status_no_retries(const char * req, bool add_headers, int the_socket, unsigned int status) {
312   struct autobuf abuf;
313
314   abuf_init(&abuf, AUTOBUFCHUNK);
315
316   if (add_headers) {
317     http_header_build_result(status, &abuf);
318   } else if (status != INFO_HTTP_OK) {
319     if (functions->output_error) {
320       functions->output_error(&abuf, status, req, add_headers);
321     } else if (status == INFO_HTTP_NOCONTENT) {
322       /* wget can't handle output of zero length */
323       abuf_puts(&abuf, "\n");
324     }
325   }
326
327   (void) send(the_socket, abuf.buf, abuf.len, 0);
328   close(the_socket);
329   abuf_free(&abuf);
330 }
331
332 static void write_data(void *unused __attribute__((unused))) {
333   fd_set set;
334   int result, i, max;
335   struct timeval tv;
336
337   if (outbuffer.count <= 0) {
338     /* exit early if there is nothing to send */
339     return;
340   }
341
342   FD_ZERO(&set);
343   max = 0;
344   for (i = 0; i < MAX_CLIENTS; i++) {
345     if (outbuffer.socket[i] < 0) {
346       continue;
347     }
348
349     /* And we cast here since we get a warning on Win32 */
350     FD_SET((unsigned int ) (outbuffer.socket[i]), &set);
351
352     if (outbuffer.socket[i] > max) {
353       max = outbuffer.socket[i];
354     }
355   }
356
357   tv.tv_sec = 0;
358   tv.tv_usec = 0;
359
360   result = select(max + 1, NULL, &set, NULL, &tv);
361   if (result <= 0) {
362     /* exit early if any of the sockets is not ready for writing */
363     return;
364   }
365
366   for (i = 0; i < MAX_CLIENTS; i++) {
367     if (outbuffer.socket[i] < 0) {
368       continue;
369     }
370
371     result = send(outbuffer.socket[i], outbuffer.buffer[i] + outbuffer.written[i], outbuffer.size[i] - outbuffer.written[i], 0);
372     if (result > 0) {
373       outbuffer.written[i] += result;
374     }
375
376 #if EWOULDBLOCK == EAGAIN
377     if ((result < 0) && (errno == EAGAIN)) {
378 #else
379     if ((result < 0) && ((errno == EWOULDBLOCK) || (errno == EAGAIN))) {
380 #endif
381       continue;
382     }
383
384     if ((result < 0) || (outbuffer.written[i] >= outbuffer.size[i])) {
385       /* close this socket and cleanup*/
386       close(outbuffer.socket[i]);
387       outbuffer.socket[i] = -1;
388       free(outbuffer.buffer[i]);
389       outbuffer.buffer[i] = NULL;
390       outbuffer.size[i] = 0;
391       outbuffer.written[i] = 0;
392
393       outbuffer.count--;
394     }
395   }
396
397   if (!outbuffer.count) {
398     olsr_stop_timer(writetimer_entry);
399   }
400 }
401
402 typedef struct {
403   unsigned long long siw;
404   printer_generic func;
405 } SiwLookupTableEntry;
406
407 static void send_info_from_table(struct autobuf *abuf, unsigned int send_what, SiwLookupTableEntry *funcs, unsigned int funcsSize, unsigned int *outputLength) {
408   unsigned int i;
409   unsigned int preLength;
410   unsigned int what = send_what;
411   cache_timeout_func cache_timeout_f = functions->cache_timeout;
412
413   if (functions->output_start) {
414     functions->output_start(abuf);
415   }
416
417   preLength = abuf->len;
418
419   for (i = 0; (i < funcsSize) && what; i++) {
420     unsigned long long siw = funcs[i].siw;
421     if (what & siw) {
422       printer_generic func = funcs[i].func;
423       if (func) {
424         long cache_timeout = 0;
425         struct info_cache_entry_t *cache_entry = NULL;
426
427         if (cache_timeout_f) {
428           cache_timeout = cache_timeout_f(config, siw);
429           cache_entry = (cache_timeout <= 0) ? NULL : info_cache_get_entry(&info_cache, siw);
430         }
431
432         if (!cache_entry) {
433             func(abuf);
434         } else {
435           long long now;
436           long long age;
437
438           info_plugin_cache_init_entry(cache_entry);
439
440           now = olsr_times();
441           age = llabs(now - cache_entry->timestamp);
442           if (!cache_entry->timestamp || (age >= cache_timeout)) {
443             /* cache is never used before or cache is too old */
444             cache_entry->buf.buf[0] = '\0';
445             cache_entry->buf.len = 0;
446             cache_entry->timestamp = now;
447             func(&cache_entry->buf);
448           }
449
450           abuf_concat(abuf, &cache_entry->buf);
451         }
452       }
453     }
454     what &= ~siw;
455   }
456
457   *outputLength = abuf->len - preLength;
458
459   if (functions->output_end) {
460     functions->output_end(abuf);
461   }
462 }
463
464 static void send_info(const char * req, bool add_headers, unsigned int send_what, int the_socket, unsigned int status) {
465   struct autobuf abuf;
466   unsigned int outputLength = 0;
467   unsigned int send_index = 0;
468   bool first_reply = false;
469
470   const char *content_type = functions->determine_mime_type ? functions->determine_mime_type(send_what) : "text/plain; charset=utf-8";
471   int contentLengthIndex = 0;
472   int headerLength = 0;
473
474   assert(outbuffer.count <= MAX_CLIENTS);
475
476   abuf_init(&abuf, AUTOBUFCHUNK);
477
478   if (add_headers) {
479     http_header_build(name, status, content_type, &abuf, &contentLengthIndex);
480     headerLength = abuf.len;
481   }
482
483   if (status == INFO_HTTP_OK) {
484     /* OK */
485
486     // only add if normal format
487     if (send_what & SIW_ALL) {
488       SiwLookupTableEntry funcs[] = {
489         { SIW_NEIGHBORS   , functions->neighbors   }, //
490         { SIW_LINKS       , functions->links       }, //
491         { SIW_ROUTES      , functions->routes      }, //
492         { SIW_HNA         , functions->hna         }, //
493         { SIW_MID         , functions->mid         }, //
494         { SIW_TOPOLOGY    , functions->topology    }, //
495         { SIW_GATEWAYS    , functions->gateways    }, //
496         { SIW_INTERFACES  , functions->interfaces  }, //
497         { SIW_2HOP        , functions->twohop      }, //
498         { SIW_SGW         , functions->sgw         }, //
499         { SIW_PUD_POSITION, functions->pudPosition }, //
500         //
501         { SIW_VERSION     , functions->version     }, //
502         { SIW_CONFIG      , functions->config      }, //
503         { SIW_PLUGINS     , functions->plugins     } //
504       };
505
506       send_info_from_table(&abuf, send_what, funcs, ARRAY_SIZE(funcs), &outputLength);
507     } else if (send_what & SIW_NETJSON) {
508       SiwLookupTableEntry funcs[] = {
509         { SIW_NETJSON_NETWORK_ROUTES      , functions->networkRoutes      }, //
510         { SIW_NETJSON_NETWORK_GRAPH       , functions->networkGraph       }, //
511         { SIW_NETJSON_DEVICE_CONFIGURATION, functions->deviceConfiguration}, //
512         { SIW_NETJSON_DEVICE_MONITORING   , functions->deviceMonitoring   }, //
513         { SIW_NETJSON_NETWORK_COLLECTION  , functions->networkCollection  } //
514       };
515
516       send_info_from_table(&abuf, send_what, funcs, ARRAY_SIZE(funcs), &outputLength);
517     } else if ((send_what & SIW_OLSRD_CONF) && functions->olsrd_conf) {
518       /* this outputs the olsrd.conf text directly, not normal format */
519       unsigned int preLength = abuf.len;
520       functions->olsrd_conf(&abuf);
521       outputLength = abuf.len - preLength;
522     }
523
524     if (!abuf.len || !outputLength) {
525       status = INFO_HTTP_NOCONTENT;
526       abuf.buf[0] = '\0';
527       abuf.len = 0;
528       if (add_headers) {
529         http_header_build(name, status, content_type, &abuf, &contentLengthIndex);
530         headerLength = abuf.len;
531       }
532     }
533   }
534
535   if (status != INFO_HTTP_OK) {
536     if (functions->output_error) {
537       functions->output_error(&abuf, status, req, add_headers);
538     } else if (status == INFO_HTTP_NOCONTENT) {
539       /* wget can't handle output of zero length */
540       abuf_puts(&abuf, "\n");
541     }
542   }
543
544   if (add_headers) {
545     http_header_adjust_content_length(&abuf, contentLengthIndex, abuf.len - headerLength);
546   }
547
548   /*
549    * Determine the last available outbuffer slot.
550    * Search from the end towards the start to avoid starvation of
551    * older replies that are still in-flight (since the send function
552    * iterates from the start towards the end).
553    */
554   send_index = MAX_CLIENTS;
555   while (send_index) {
556     send_index--;
557     if (!outbuffer.buffer[send_index]) {
558       break;
559     }
560   }
561   assert(send_index < MAX_CLIENTS);
562   assert(!outbuffer.buffer[send_index]);
563
564   /* avoid a memcpy: just move the abuf.buf pointer and clear abuf */
565   outbuffer.buffer[send_index] = abuf.buf;
566   outbuffer.size[send_index] = abuf.len;
567   outbuffer.written[send_index] = 0;
568   outbuffer.socket[send_index] = the_socket;
569   abuf.buf = NULL;
570   abuf.len = 0;
571   abuf.size = 0;
572
573   first_reply = !outbuffer.count;
574
575   outbuffer.count++;
576
577   write_data(NULL);
578
579   if (first_reply && outbuffer.buffer[send_index]) {
580     writetimer_entry = olsr_start_timer(10, 0, OLSR_TIMER_PERIODIC, &write_data, NULL, 0);
581   }
582 }
583
584 static char * skipLeadingWhitespace(char * requ, size_t *len) {
585   if (!requ || !len || !*len) {
586     return requ;
587   }
588
589   while (isspace(*requ) && (*requ != '\0')) {
590     *len = *len - 1;
591     requ++;
592   }
593   return requ;
594 }
595
596 static char * stripTrailingWhitespace(char * requ, size_t *len) {
597   if (!requ || !len || !*len) {
598     return requ;
599   }
600
601   while (isspace(requ[*len - 1]) && (requ[*len - 1] != '\0')) {
602     *len = *len - 1;
603     requ[*len] = '\0';
604   }
605   return requ;
606 }
607
608 static char * stripTrailingSlashes(char * requ, size_t *len) {
609   if (!requ || !len || !*len) {
610     return requ;
611   }
612
613   while ((requ[*len - 1] == '/') && (requ[*len - 1] != '\0')) {
614     *len = *len - 1;
615     requ[*len] = '\0';
616   }
617   return requ;
618 }
619
620 static char * cutAtFirstEOL(char * requ, size_t *len) {
621   char * s = requ;
622   size_t l = 0;
623
624   if (!requ || !len || !*len) {
625     return requ;
626   }
627
628   while (!((*s == '\n') || (*s == '\r')) && (*s != '\0')) {
629     s++;
630     l++;
631   }
632   if ((*s == '\n') || (*s == '\r')) {
633     *s = '\0';
634   }
635   *len = l;
636   return requ;
637 }
638
639 static char * parseRequest(char * req, size_t *len) {
640   if (!req || !len || !*len) {
641     return req;
642   }
643
644   /* HTTP request: GET whitespace URI whitespace HTTP/1.[01] */
645   if (*len < (3 + 1 + 1 + 1 + 8) //
646       || strncasecmp(req, "GET", 3) //
647       || !isspace(req[3]) //
648       || !isspace(req[*len - 9]) //
649       || strncasecmp(&req[*len - 8], "HTTP/1.", 7) //
650       || ((req[*len - 1] != '1') && (req[*len - 1] != '0'))) {
651     /* too short or does not start with 'GET ' nor ends with ' HTTP/1.[01]'*/
652     return req;
653   }
654
655   /* skip 'GET ' */
656   *len = *len - 4;
657   req = &req[4];
658
659   /* strip ' HTTP/1.[01]' */
660   *len = *len - 9;
661   req[*len] = '\0';
662
663   return req;
664 }
665
666 static void drain_request(int ipc_connection) {
667   static char drain_buffer[AUTOBUFCHUNK];
668
669   /* input was much too long: read until the end for graceful connection termination
670    * because wget can't handle the premature connection termination that is allowed
671    * by the INFO_HTTP_REQUEST_ENTITY_TOO_LARGE HTTP status code
672    */
673   while (recv(ipc_connection, (void *) &drain_buffer, sizeof(drain_buffer), 0) == sizeof(drain_buffer)) {}
674 }
675
676 static void ipc_action(int fd, void *data __attribute__ ((unused)), unsigned int flags __attribute__ ((unused))) {
677 #ifndef NODEBUG
678   char addr[INET6_ADDRSTRLEN];
679 #endif /* NODEBUG */
680
681   int ipc_connection = -1;
682   union olsr_sockaddr sock_addr;
683   socklen_t sock_addr_len = sizeof(sock_addr);
684   bool hostDenied = false;
685   struct timeval timeout;
686   fd_set read_fds;
687   char req_buffer[1024];
688   char * req = req_buffer;
689   ssize_t rx_count = 0;
690   unsigned int send_what = 0;
691   unsigned int http_status = INFO_HTTP_OK;
692   bool add_headers = config->http_headers;
693
694   *req = '\0';
695
696   if ((ipc_connection = accept(fd, &sock_addr.in, &sock_addr_len)) < 0) {
697 #ifndef NODEBUG
698     olsr_printf(1, "(%s) accept()=%s\n", name, strerror(errno));
699 #endif /* NODEBUG */
700     /* the caller will retry later */
701     return;
702   }
703
704   timeout.tv_sec = timeout.tv_usec = 0;
705
706   FD_ZERO(&read_fds);
707 #ifndef _WIN32
708   FD_SET(ipc_connection, &read_fds);
709 #else
710   FD_SET((unsigned int ) ipc_connection, &read_fds); /* Win32 needs the cast here */
711 #endif
712
713   /* On success, select() and pselect() return the number of file descriptors
714    * contained in the three returned descriptor sets (that is, the total number
715    * of bits that are set in readfds, writefds, exceptfds) which may be zero if
716    * the timeout expires before anything interesting happens. On error, -1 is
717    * returned, and errno is set to indicate the error; the file descriptor sets
718    * are unmodified, and timeout becomes undefined.
719    */
720
721   if (select(ipc_connection + 1, &read_fds, NULL, NULL, &timeout) < 0) {
722     /* ipc_connection is not ready for reading */
723 #ifndef NODEBUG
724     olsr_printf(1, "(%s) select()=%s\n", name, strerror(errno));
725 #endif /* NODEBUG */
726     drain_request(ipc_connection);
727     if (outbuffer.count >= MAX_CLIENTS) {
728       send_status_no_retries(req, add_headers, ipc_connection, INFO_HTTP_INTERNAL_SERVER_ERROR);
729     } else {
730       send_info(req, add_headers, send_what, ipc_connection, INFO_HTTP_INTERNAL_SERVER_ERROR);
731     }
732     return;
733   }
734
735   rx_count = recv(ipc_connection, req, sizeof(req_buffer), 0);
736
737   /* Upon successful completion, recv() shall return the length of the message
738    * in bytes. If no messages are available to be received and the peer has
739    * performed an orderly shutdown, recv() shall return 0. Otherwise, −1 shall
740    * be returned and errno set to indicate the error.
741    */
742
743   /* ensure proper request termination */
744   if (rx_count <= 0) {
745     *req = '\0';
746   } else if (rx_count < (ssize_t) sizeof(req_buffer)) {
747     req[rx_count] = '\0';
748   } else {
749     req[sizeof(req_buffer) - 1] = '\0';
750   }
751
752   /* sanitise the request */
753   if (rx_count > 0) {
754     req = cutAtFirstEOL(req, (size_t*) &rx_count);
755
756     req = stripTrailingWhitespace(req, (size_t*) &rx_count);
757     req = skipLeadingWhitespace(req, (size_t*) &rx_count);
758
759     /* detect http requests */
760     req = parseRequest(req, (size_t*) &rx_count);
761
762     req = stripTrailingWhitespace(req, (size_t*) &rx_count);
763     req = stripTrailingSlashes(req, (size_t*) &rx_count);
764     req = skipLeadingWhitespace(req, (size_t*) &rx_count);
765     req = skipMultipleSlashes(req, (size_t*) &rx_count);
766   }
767
768   if (outbuffer.count >= MAX_CLIENTS) {
769     /* limit the number of replies that are in-flight */
770     drain_request(ipc_connection);
771     send_status_no_retries(req, add_headers, ipc_connection, INFO_HTTP_SERVICE_UNAVAILABLE);
772     return;
773   }
774
775   if (olsr_cnf->ip_version == AF_INET) {
776     hostDenied = //
777         (ntohl(config->accept_ip.v4.s_addr) != INADDR_ANY) //
778         && !ip4equal(&sock_addr.in4.sin_addr, &config->accept_ip.v4) //
779         && (!config->allow_localhost //
780             || (ntohl(sock_addr.in4.sin_addr.s_addr) != INADDR_LOOPBACK));
781   } else {
782     hostDenied = //
783         !ip6equal(&config->accept_ip.v6, &in6addr_any) //
784         && !ip6equal(&sock_addr.in6.sin6_addr, &config->accept_ip.v6) //
785         && (!config->allow_localhost //
786             || !ip6equal(&config->accept_ip.v6, &in6addr_loopback));
787   }
788
789 #ifndef NODEBUG
790   if (!inet_ntop( //
791       olsr_cnf->ip_version, //
792       (olsr_cnf->ip_version == AF_INET) ? (void *) &sock_addr.in4.sin_addr : (void *) &sock_addr.in6.sin6_addr, //
793       addr, //
794       sizeof(addr))) {
795     addr[0] = '\0';
796   }
797 #endif /* NODEBUG */
798
799   if (hostDenied) {
800 #ifndef NODEBUG
801     olsr_printf(1, "(%s) Connect from host %s is not allowed!\n", name, addr);
802 #endif /* NODEBUG */
803     drain_request(ipc_connection);
804     send_info(req, add_headers, send_what, ipc_connection, INFO_HTTP_FORBIDDEN);
805     return;
806   }
807
808 #ifndef NODEBUG
809   olsr_printf(2, "(%s) Connect from host %s is allowed\n", name, addr);
810 #endif /* NODEBUG */
811
812   if (rx_count < 0) {
813 #ifndef NODEBUG
814     olsr_printf(1, "(%s) rx_count < 0\n", name);
815 #endif /* NODEBUG */
816     drain_request(ipc_connection);
817     send_info(req, add_headers, send_what, ipc_connection, INFO_HTTP_INTERNAL_SERVER_ERROR);
818     return;
819   }
820
821   /* rx_count >= 0 */
822
823   if (!rx_count) {
824 #ifndef NODEBUG
825     olsr_printf(1, "(%s) rx_count == 0\n", name);
826 #endif /* NODEBUG */
827     drain_request(ipc_connection);
828     send_info(req, add_headers, SIW_EVERYTHING, ipc_connection, INFO_HTTP_OK);
829     return;
830   }
831
832   /* rx_count > 0 */
833
834   if (rx_count >= (ssize_t) sizeof(req_buffer)) {
835 #ifndef NODEBUG
836     olsr_printf(1, "(%s) rx_count > %ld\n", name, (long int) sizeof(req_buffer));
837 #endif /* NODEBUG */
838     drain_request(ipc_connection);
839     send_info(req, add_headers, send_what, ipc_connection, INFO_HTTP_REQUEST_ENTITY_TOO_LARGE);
840     return;
841   }
842
843   /* 0 < rx_count < sizeof(requ) */
844
845   if (!rx_count //
846       || ((rx_count == 1) && (*req == '/'))) {
847     /* empty or '/' */
848     send_what = SIW_EVERYTHING;
849   } else {
850     send_what = determine_action(req);
851   }
852
853   if (!send_what) {
854     http_status = INFO_HTTP_NOTFOUND;
855   }
856
857   send_info(req, add_headers, send_what, ipc_connection, http_status);
858 }
859
860 static int plugin_ipc_init(void) {
861   union olsr_sockaddr sock_addr;
862   uint32_t yes = 1;
863   socklen_t sock_addr_len;
864
865   /* Init ipc socket */
866   if ((ipc_socket = socket(olsr_cnf->ip_version, SOCK_STREAM, 0)) == -1) {
867 #ifndef NODEBUG
868     olsr_printf(1, "(%s) socket()=%s\n", name, strerror(errno));
869 #endif /* NODEBUG */
870     goto error_out;
871   }
872
873   if (setsockopt(ipc_socket, SOL_SOCKET, SO_REUSEADDR, (char *) &yes, sizeof(yes)) < 0) {
874 #ifndef NODEBUG
875     olsr_printf(1, "(%s) setsockopt()=%s\n", name, strerror(errno));
876 #endif /* NODEBUG */
877     goto error_out;
878   }
879
880 #if (defined __FreeBSD__ || defined __FreeBSD_kernel__) && defined SO_NOSIGPIPE
881   if (setsockopt(ipc_socket, SOL_SOCKET, SO_NOSIGPIPE, (char *) &yes, sizeof(yes)) < 0) {
882     perror("SO_NOSIGPIPE failed");
883     goto error_out;
884   }
885 #endif /* (defined __FreeBSD__ || defined __FreeBSD_kernel__) && defined SO_NOSIGPIPE */
886
887 #if defined __linux__ && defined IPV6_V6ONLY
888   if (config->ipv6_only && (olsr_cnf->ip_version == AF_INET6) //
889       && (setsockopt(ipc_socket, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &yes, sizeof(yes)) < 0)) {
890     perror("IPV6_V6ONLY failed");
891     goto error_out;
892   }
893 #endif /* defined __linux__ && defined IPV6_V6ONLY */
894
895   /* complete the socket structure */
896   memset(&sock_addr, 0, sizeof(sock_addr));
897   if (olsr_cnf->ip_version == AF_INET) {
898     sock_addr.in4.sin_family = AF_INET;
899     sock_addr_len = sizeof(struct sockaddr_in);
900 #ifdef SIN6_LEN
901     sock_addr.in4.sin_len = sock_addr_len;
902 #endif /* SIN6_LEN */
903     sock_addr.in4.sin_addr.s_addr = config->listen_ip.v4.s_addr;
904     sock_addr.in4.sin_port = htons(config->ipc_port);
905   } else {
906     sock_addr.in6.sin6_family = AF_INET6;
907     sock_addr_len = sizeof(struct sockaddr_in6);
908 #ifdef SIN6_LEN
909     sock_addr.in6.sin6_len = sock_addr_len;
910 #endif /* SIN6_LEN */
911     sock_addr.in6.sin6_addr = config->listen_ip.v6;
912     sock_addr.in6.sin6_port = htons(config->ipc_port);
913   }
914
915   /* bind the socket to the port number */
916   if (bind(ipc_socket, &sock_addr.in, sock_addr_len) == -1) {
917 #ifndef NODEBUG
918     olsr_printf(1, "(%s) bind()=%s\n", name, strerror(errno));
919 #endif /* NODEBUG */
920     goto error_out;
921   }
922
923   /* show that we are willing to listen */
924   if (listen(ipc_socket, 1) == -1) {
925 #ifndef NODEBUG
926     olsr_printf(1, "(%s) listen()=%s\n", name, strerror(errno));
927 #endif /* NODEBUG */
928     goto error_out;
929   }
930
931   /* Register with olsrd */
932   add_olsr_socket(ipc_socket, &ipc_action, NULL, NULL, SP_PR_READ);
933
934 #ifndef NODEBUG
935   olsr_printf(2, "(%s) listening on port %d\n", name, config->ipc_port);
936 #endif /* NODEBUG */
937
938   return 1;
939
940   error_out: //
941   if (ipc_socket >= 0) {
942     close(ipc_socket);
943     ipc_socket = -1;
944   }
945   return 0;
946 }
947
948 int info_plugin_init(const char * plugin_name, info_plugin_functions_t *plugin_functions, info_plugin_config_t *plugin_config) {
949   int i;
950
951   assert(plugin_name);
952   assert(plugin_functions);
953   assert(plugin_config);
954
955   name = plugin_name;
956   functions = plugin_functions;
957   config = plugin_config;
958
959   memset(&outbuffer, 0, sizeof(outbuffer));
960   for (i = 0; i < MAX_CLIENTS; ++i) {
961     outbuffer.socket[i] = -1;
962   }
963
964   ipc_socket = -1;
965
966   if (functions->init) {
967     functions->init(name);
968   }
969
970   info_plugin_cache_init(true);
971
972   return plugin_ipc_init();
973 }
974
975 void info_plugin_exit(void) {
976   int i;
977
978   if (ipc_socket != -1) {
979     close(ipc_socket);
980     ipc_socket = -1;
981   }
982   for (i = 0; i < MAX_CLIENTS; ++i) {
983     if (outbuffer.buffer[i]) {
984       free(outbuffer.buffer[i]);
985       outbuffer.buffer[i] = NULL;
986     }
987     outbuffer.size[i] = 0;
988     outbuffer.written[i] = 0;
989     if (outbuffer.socket[i]) {
990       close(outbuffer.socket[i]);
991       outbuffer.socket[i] = -1;
992     }
993   }
994   outbuffer.count = 0;
995
996   info_plugin_cache_init(false);
997 }