2 * The olsr.org Optimized Link-State Routing daemon(olsrd)
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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
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.
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.
34 * Visit http://www.olsr.org for more information.
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.
42 #include <arpa/inet.h>
48 #include "olsrd_info.h"
50 #include "scheduler.h"
52 #include "http_headers.h"
55 #define close(x) closesocket(x)
61 * There is the problem that writing to a network socket can block,
62 * and the olsrd scheduler does not care about write events.
64 * There was a case that olsrd just froze for minutes when people used
65 * jsoninfo/txtinfo with large topologies over bad WiFi.
67 * This is the solution that was chosen at that time, unwilling to
68 * rewrite the whole scheduler:
69 * A timer was added and each time it expires each non-empty buffer
70 * in this structure will try to write data into a "non-blocking"
71 * socket until all data is sent, so that no blocking occurs.
74 int socket[MAX_CLIENTS];
75 char *buffer[MAX_CLIENTS];
76 size_t size[MAX_CLIENTS];
77 size_t written[MAX_CLIENTS];
79 } info_plugin_outbuffer_t;
81 static char sink_buffer[AUTOBUFCHUNK];
83 static const char * name = NULL;
85 static info_plugin_functions_t *functions = NULL;
87 static info_plugin_config_t *config = NULL;
89 static int ipc_socket = -1;
91 static info_plugin_outbuffer_t outbuffer;
93 static struct timer_entry *writetimer_entry = NULL;
95 #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
97 static char * skipMultipleSlashes(char * requ) {
100 if ((r[0] == '\0') // zero length
101 || (r[0] != '/') // does not start with a slash
102 || (r[1] != '/')) // does not have another slash
107 while (r[1] == '/') {
113 static unsigned long long SIW_ENTRIES_ALL[] = {
126 SIW_NEIGHBORS_FREIFUNK, //
138 static unsigned int determine_single_action(char *requ) {
141 if (!functions->is_command)
144 for (i = 0; i < ARRAY_SIZE(SIW_ENTRIES_ALL); ++i) {
145 unsigned long long siw = SIW_ENTRIES_ALL[i];
146 if (functions->is_command(requ, siw))
153 static unsigned int determine_action(char *requ) {
154 if (!functions->is_command) {
158 if (!requ || (requ[0] == '\0')) {
163 /* requ is guaranteed to be at least 1 character long */
165 if (!functions->supportsCompositeCommands) {
166 /* no support for composite commands */
167 return determine_single_action(requ);
170 /* composite commands */
173 unsigned int action = 0;
175 char * requestSegment = requ;
176 while (requestSegment) {
177 requestSegment = skipMultipleSlashes(requestSegment);
178 if (requestSegment[0] == '\0') {
179 /* there is no more text */
180 requestSegment = NULL;
184 /* there is more text */
188 char * requestSegmentTail = strchr(&requestSegment[1], '/');
190 if (!requestSegmentTail) {
191 /* there isn't another slash, process everything that is left */
192 r = determine_single_action(requestSegment);
194 /* there is another slash, process everything before the slash */
195 char savedCharacter = *requestSegmentTail;
196 *requestSegmentTail = '\0';
197 r = determine_single_action(requestSegment);
198 *requestSegmentTail = savedCharacter;
208 /* process everything that is left in the next iteration */
209 requestSegment = requestSegmentTail;
217 static void write_data(void *fullyWritten) {
222 if (outbuffer.count <= 0) {
223 /* exit early if there is nothing to send */
229 for (i = 0; i < outbuffer.count; i++) {
230 if (outbuffer.socket[i] < 0) {
234 /* And we cast here since we get a warning on Win32 */
235 FD_SET((unsigned int ) (outbuffer.socket[i]), &set);
237 if (outbuffer.socket[i] > max) {
238 max = outbuffer.socket[i];
245 result = select(max + 1, NULL, &set, NULL, &tv);
247 /* exit early if any of the sockets is not ready for writing */
251 for (i = 0; i < MAX_CLIENTS; i++) {
252 if (outbuffer.socket[i] < 0) {
256 result = send(outbuffer.socket[i], outbuffer.buffer[i] + outbuffer.written[i], outbuffer.size[i] - outbuffer.written[i], 0);
258 outbuffer.written[i] += result;
261 if ((result < 0) && ((errno == EWOULDBLOCK) || (errno == EAGAIN))) {
265 if ((result < 0) || (outbuffer.written[i] >= outbuffer.size[i])) {
266 /* close this socket and cleanup*/
267 close(outbuffer.socket[i]);
268 outbuffer.socket[i] = -1;
269 free(outbuffer.buffer[i]);
270 outbuffer.buffer[i] = NULL;
271 outbuffer.size[i] = 0;
272 outbuffer.written[i] = 0;
276 bool * p = fullyWritten;
282 if (!outbuffer.count) {
283 olsr_stop_timer(writetimer_entry);
288 unsigned long long siw;
289 printer_generic func;
290 } SiwLookupTableEntry;
292 static void send_info_from_table(struct autobuf *abuf, unsigned int send_what, SiwLookupTableEntry *funcs, unsigned int funcsSize, unsigned int *outputLength) {
294 unsigned int preLength;
296 if (functions->output_start) {
297 functions->output_start(abuf);
300 preLength = abuf->len;
302 for (i = 0; i < funcsSize; i++) {
303 if (send_what & funcs[i].siw) {
304 printer_generic func = funcs[i].func;
311 *outputLength = abuf->len - preLength;
313 if (functions->output_end) {
314 functions->output_end(abuf);
318 static void send_info(const char * req, unsigned int send_what, int the_socket, unsigned int status) {
320 unsigned int outputLength = 0;
322 const char *content_type = functions->determine_mime_type ? functions->determine_mime_type(send_what) : "text/plain; charset=utf-8";
323 int contentLengthIndex = 0;
324 int headerLength = 0;
326 abuf_init(&abuf, 2 * AUTOBUFCHUNK);
328 if (config->http_headers) {
329 http_header_build(name, status, content_type, &abuf, &contentLengthIndex);
330 headerLength = abuf.len;
333 if (status == INFO_HTTP_OK) {
336 // only add if normal format
337 if (send_what & SIW_ALL) {
338 SiwLookupTableEntry funcs[] = {
339 { SIW_NEIGHBORS , functions->neighbors }, //
340 { SIW_LINKS , functions->links }, //
341 { SIW_ROUTES , functions->routes }, //
342 { SIW_HNA , functions->hna }, //
343 { SIW_MID , functions->mid }, //
344 { SIW_TOPOLOGY , functions->topology }, //
345 { SIW_GATEWAYS , functions->gateways }, //
346 { SIW_INTERFACES, functions->interfaces }, //
347 { SIW_2HOP , functions->twohop }, //
348 { SIW_SGW , functions->sgw }, //
350 { SIW_VERSION , functions->version }, //
351 { SIW_CONFIG , functions->config }, //
352 { SIW_PLUGINS , functions->plugins } //
355 send_info_from_table(&abuf, send_what, funcs, ARRAY_SIZE(funcs), &outputLength);
356 } else if ((send_what & SIW_OLSRD_CONF) && functions->olsrd_conf) {
357 /* this outputs the olsrd.conf text directly, not normal format */
358 unsigned int preLength = abuf.len;
359 functions->olsrd_conf(&abuf);
360 outputLength = abuf.len - preLength;
363 if (!abuf.len || !outputLength) {
364 status = INFO_HTTP_NOCONTENT;
367 if (config->http_headers) {
368 http_header_build(name, status, content_type, &abuf, &contentLengthIndex);
369 headerLength = abuf.len;
374 if (status != INFO_HTTP_OK) {
375 if (functions->output_error) {
376 functions->output_error(&abuf, status, req, config->http_headers);
377 } else if (status == INFO_HTTP_NOCONTENT) {
378 /* wget can't handle output of zero length */
379 abuf_puts(&abuf, "\n");
383 if (config->http_headers) {
384 http_header_adjust_content_length(&abuf, contentLengthIndex, abuf.len - headerLength);
387 /* avoid a memcpy: just move the abuf.buf pointer and clear abuf */
388 outbuffer.buffer[outbuffer.count] = abuf.buf;
389 outbuffer.size[outbuffer.count] = abuf.len;
390 outbuffer.written[outbuffer.count] = 0;
391 outbuffer.socket[outbuffer.count] = the_socket;
399 bool fullyWritten = false;
400 write_data(&fullyWritten);
402 if (!fullyWritten && (outbuffer.count == 1)) {
403 writetimer_entry = olsr_start_timer(10, 0, OLSR_TIMER_PERIODIC, &write_data, NULL, 0);
410 static char * skipLeadingWhitespace(char * requ, size_t *len) {
411 while (isspace(*requ) && (*requ != '\0')) {
418 static char * stripEOLs(char * requ, size_t *len) {
419 while (isspace(requ[*len - 1]) && (requ[*len - 1] != '\0')) {
426 static char * stripTrailingSlashes(char * requ, size_t *len) {
427 while ((requ[*len - 1] == '/') && (requ[*len - 1] != '\0')) {
434 static char * cutAtFirstEOL(char * requ, size_t *len) {
437 while (!((*s == '\n') || (*s == '\r')) && (*s != '\0')) {
441 if ((*s == '\n') || (*s == '\r')) {
448 static char * parseRequest(char * requ, size_t *len) {
455 req = skipLeadingWhitespace(req, len);
456 req = stripEOLs(req, len);
458 /* HTTP request: GET whitespace URI whitespace HTTP/1.1 */
459 if (*len < (3 + 1 + 1 + 1 + 8)) {
463 if (strncasecmp(req, "GET", 3) || !isspace(req[3])) {
464 /* does not start with 'GET ' */
468 /* skip 'GET ' and further leading whitespace */
469 req = skipLeadingWhitespace(&req[4], len);
470 if (!*len) return req;
472 /* cut req at the first '\n' */
473 req = cutAtFirstEOL(req, len);
474 if (!*len) return req;
476 /* strip req of trailing EOL and whitespace */
477 req = stripEOLs(req, len);
478 if (*len < 9) return req;
480 if (!isspace(req[*len - 9]) //
481 || strncasecmp(&req[*len - 8], "HTTP/1.", 7) //
482 || ((req[*len - 1] != '1') && (req[*len - 1] != '0'))) {
487 if (!*len) return req;
489 /* strip req of trailing EOL and whitespace */
490 req = stripEOLs(req, len);
491 req = stripTrailingSlashes(req, len);
496 static void ipc_action(int fd, void *data __attribute__ ((unused)), unsigned int flags __attribute__ ((unused))) {
498 char addr[INET6_ADDRSTRLEN];
502 unsigned int http_status = INFO_HTTP_OK;
503 union olsr_sockaddr sock_addr;
504 socklen_t sock_addr_len = sizeof(sock_addr);
507 unsigned int send_what = 0;
508 int ipc_connection = -1;
510 if (outbuffer.count >= MAX_CLIENTS) {
514 if ((ipc_connection = accept(fd, &sock_addr.in, &sock_addr_len)) == -1) {
516 olsr_printf(1, "(%s) accept()=%s\n", name, strerror(errno));
523 olsr_cnf->ip_version, //
524 (olsr_cnf->ip_version == AF_INET) ? (void *) &sock_addr.in4.sin_addr : (void *) &sock_addr.in6.sin6_addr, //
531 tv.tv_sec = tv.tv_usec = 0;
532 if (olsr_cnf->ip_version == AF_INET) {
533 if (!ip4equal(&sock_addr.in4.sin_addr, &config->accept_ip.v4) && (config->accept_ip.v4.s_addr != INADDR_ANY) //
534 && (!config->allow_localhost || (ntohl(sock_addr.in4.sin_addr.s_addr) != INADDR_LOOPBACK))) {
536 olsr_printf(1, "(%s) From host(%s) not allowed!\n", name, addr);
538 close(ipc_connection);
542 /* Use in6addr_any (::) in olsr.conf to allow anybody. */
543 if (!ip6equal(&sock_addr.in6.sin6_addr, &config->accept_ip.v6) && !ip6equal(&config->accept_ip.v6, &in6addr_any)) {
545 olsr_printf(1, "(%s) From host(%s) not allowed!\n", name, addr);
547 close(ipc_connection);
553 olsr_printf(2, "(%s) Connect from %s\n", name, addr);
556 /* purge read buffer to prevent blocking on linux */
558 FD_SET((unsigned int ) ipc_connection, &rfds); /* Win32 needs the cast here */
559 if (0 <= select(ipc_connection + 1, &rfds, NULL, NULL, &tv)) {
561 ssize_t s = recv(ipc_connection, (void *) &requ, sizeof(requ) - 1, 0); /* Win32 needs the cast here */
563 if (s >= (ssize_t) (sizeof(requ) - 1)) {
564 /* input was much too long, just skip the rest */
565 while (recv(ipc_connection, (void *) &sink_buffer, sizeof(sink_buffer), 0) == sizeof(sink_buffer))
573 req = parseRequest(req, (size_t*)&s);
574 req = skipMultipleSlashes(req);
575 if ((req[0] == '\0') || ((req[0] == '/') && (req[1] == '\0'))) {
579 send_what = determine_action(req);
584 http_status = INFO_HTTP_NOTFOUND;
588 send_info(req ? req : "", send_what, ipc_connection, http_status);
591 static int plugin_ipc_init(void) {
592 union olsr_sockaddr sock_addr;
594 socklen_t sock_addr_len;
596 /* Init ipc socket */
597 if ((ipc_socket = socket(olsr_cnf->ip_version, SOCK_STREAM, 0)) == -1) {
599 olsr_printf(1, "(%s) socket()=%s\n", name, strerror(errno));
604 if (setsockopt(ipc_socket, SOL_SOCKET, SO_REUSEADDR, (char *) &yes, sizeof(yes)) < 0) {
606 olsr_printf(1, "(%s) setsockopt()=%s\n", name, strerror(errno));
611 #if (defined __FreeBSD__ || defined __FreeBSD_kernel__) && defined SO_NOSIGPIPE
612 if (setsockopt(ipc_socket, SOL_SOCKET, SO_NOSIGPIPE, (char *) &yes, sizeof(yes)) < 0) {
613 perror("SO_NOSIGPIPE failed");
616 #endif /* (defined __FreeBSD__ || defined __FreeBSD_kernel__) && defined SO_NOSIGPIPE */
618 #if defined __linux__ && defined IPV6_V6ONLY
619 if (config->ipv6_only && (olsr_cnf->ip_version == AF_INET6) //
620 && (setsockopt(ipc_socket, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &yes, sizeof(yes)) < 0)) {
621 perror("IPV6_V6ONLY failed");
624 #endif /* defined __linux__ && defined IPV6_V6ONLY */
626 /* complete the socket structure */
627 memset(&sock_addr, 0, sizeof(sock_addr));
628 if (olsr_cnf->ip_version == AF_INET) {
629 sock_addr.in4.sin_family = AF_INET;
630 sock_addr_len = sizeof(struct sockaddr_in);
632 sock_addr.in4.sin_len = sock_addr_len;
633 #endif /* SIN6_LEN */
634 sock_addr.in4.sin_addr.s_addr = config->listen_ip.v4.s_addr;
635 sock_addr.in4.sin_port = htons(config->ipc_port);
637 sock_addr.in6.sin6_family = AF_INET6;
638 sock_addr_len = sizeof(struct sockaddr_in6);
640 sock_addr.in6.sin6_len = sock_addr_len;
641 #endif /* SIN6_LEN */
642 sock_addr.in6.sin6_addr = config->listen_ip.v6;
643 sock_addr.in6.sin6_port = htons(config->ipc_port);
646 /* bind the socket to the port number */
647 if (bind(ipc_socket, &sock_addr.in, sock_addr_len) == -1) {
649 olsr_printf(1, "(%s) bind()=%s\n", name, strerror(errno));
654 /* show that we are willing to listen */
655 if (listen(ipc_socket, 1) == -1) {
657 olsr_printf(1, "(%s) listen()=%s\n", name, strerror(errno));
662 /* Register with olsrd */
663 add_olsr_socket(ipc_socket, &ipc_action, NULL, NULL, SP_PR_READ);
666 olsr_printf(2, "(%s) listening on port %d\n", name, config->ipc_port);
672 if (ipc_socket >= 0) {
679 int info_plugin_init(const char * plugin_name, info_plugin_functions_t *plugin_functions, info_plugin_config_t *plugin_config) {
683 assert(plugin_functions);
684 assert(plugin_config);
687 functions = plugin_functions;
688 config = plugin_config;
690 memset(&outbuffer, 0, sizeof(outbuffer));
691 for (i = 0; i < MAX_CLIENTS; ++i) {
692 outbuffer.socket[i] = -1;
697 if (functions->init) {
698 functions->init(name);
705 void info_plugin_exit(void) {
708 if (ipc_socket != -1) {
712 for (i = 0; i < MAX_CLIENTS; ++i) {
713 if (outbuffer.buffer[i]) {
714 free(outbuffer.buffer[i]);
715 outbuffer.buffer[i] = NULL;
717 outbuffer.size[i] = 0;
718 outbuffer.written[i] = 0;
719 if (outbuffer.socket[i]) {
720 close(outbuffer.socket[i]);
721 outbuffer.socket[i] = -1;