Rename "subsystems" directory to "base"
[oonf.git] / src / base / oonf_http.c
1
2 /*
3  * The olsr.org Optimized Link-State Routing daemon version 2 (olsrd2)
4  * Copyright (c) 2004-2015, the olsr.org team - see HISTORY file
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 /**
43  * @file
44  */
45
46 #include <errno.h>
47 #include <fcntl.h>
48 #include <sys/stat.h>
49 #include <sys/types.h>
50
51 #include <oonf/libcommon/avl.h>
52 #include <oonf/libcommon/avl_comp.h>
53 #include <oonf/oonf.h>
54 #include <oonf/libcommon/netaddr.h>
55 #include <oonf/libcommon/netaddr_acl.h>
56
57 #include <oonf/libconfig/cfg_schema.h>
58
59 #include <oonf/libcore/oonf_cfg.h>
60 #include <oonf/libcore/oonf_libdata.h>
61 #include <oonf/libcore/oonf_logging.h>
62 #include <oonf/libcore/oonf_subsystem.h>
63 #include <oonf/libcore/os_core.h>
64 #include <oonf/base/oonf_stream_socket.h>
65 #include <oonf/base/oonf_telnet.h>
66
67 #include <oonf/base/oonf_http.h>
68
69 #ifndef O_DIRECTORY
70 /* uClibc does not define it */
71 #define O_DIRECTORY 00200000 /* must be a directory */
72 #endif
73
74 /* Definitions */
75 #define LOG_HTTP _oonf_http_subsystem.logging
76
77 /**
78  * configuration of http plugin
79  */
80 struct _http_config {
81   /*! http tcp configuration */
82   struct oonf_stream_managed_config smc;
83
84   /*! directory for webserver functionality */
85   char *www_dir;
86
87   /*! internal variable with file descriptor to webserver directory */
88   int www_dir_fd;
89 };
90 /* HTTP text constants */
91 static const char HTTP_VERSION_1_0[] = "HTTP/1.0";
92 static const char HTTP_VERSION_1_1[] = "HTTP/1.1";
93
94 static const char HTTP_GET[] = "GET";
95 static const char HTTP_POST[] = "POST";
96
97 static const char HTTP_CONTENT_LENGTH[] = "Content-Length";
98 static const char HTTP_CONTENT_TYPE[] = "Content-Type";
99
100 static const char HTTP_RESPONSE_200[] = "OK";
101 static const char HTTP_RESPONSE_400[] = "Bad Request";
102 static const char HTTP_RESPONSE_401[] = "Unauthorized";
103 static const char HTTP_RESPONSE_403[] = "Forbidden";
104 static const char HTTP_RESPONSE_404[] = "Not Found";
105 static const char HTTP_RESPONSE_413[] = "Request Entity Too Large";
106 static const char HTTP_RESPONSE_500[] = "Internal Server Error";
107 static const char HTTP_RESPONSE_501[] = "Not Implemented";
108 static const char HTTP_RESPONSE_503[] = "Service Unavailable";
109
110 /* http to telnet bridge path */
111 static const char HTTP_TO_TELNET[] = "/telnet/";
112 static const char HTTP_FILES[] = "/www/";
113
114 /* prototypes */
115 static int _init(void);
116 static void _cleanup(void);
117
118 static void _cb_config_changed(void);
119 static enum oonf_stream_session_state _cb_receive_data(struct oonf_stream_session *session);
120 static void _cb_create_error(struct oonf_stream_session *session, enum oonf_stream_errors error);
121 static void _cb_cleanup_session(struct oonf_stream_session *);
122
123 static bool _auth_okay(struct oonf_http_handler *handler, struct oonf_http_session *session);
124 static void _create_http_error(struct oonf_stream_session *session, enum oonf_http_result error);
125 static struct oonf_http_handler *_get_site_handler(const char *uri);
126 static const char *_get_headertype_string(enum oonf_http_result type);
127 static void _create_http_header(
128   struct oonf_stream_session *session, enum oonf_http_result code, const char *content_type, size_t content_length);
129 static int _parse_http_header(char *header_data, size_t header_len, struct oonf_http_session *header);
130 static size_t _parse_query_string(char *s, char **name, char **value, size_t count);
131 static void _decode_uri(char *src);
132 static enum oonf_http_result _cb_telnet_handler(struct autobuf *out, struct oonf_http_session *);
133 static enum oonf_http_result _cb_file_handler(struct autobuf *out, struct oonf_http_session *);
134
135 /* configuration variables */
136 static struct cfg_schema_entry _http_entries[] = {
137   CFG_MAP_ACL_V46(_http_config, smc.acl, "acl", ACL_DEFAULT_ACCEPT, "Access control list for http interface"),
138   CFG_MAP_ACL_V46(_http_config, smc.bindto, "bindto",
139     "127.0.0.1\0"
140     "::1\0" ACL_DEFAULT_REJECT,
141     "Bind http socket to this address"),
142   CFG_MAP_INT32_MINMAX(_http_config, smc.port, "port", "1980", "Network port for http interface", 0, 1, 65535),
143   CFG_MAP_STRING(_http_config, www_dir, "webserver", "",
144     "Path to map into the /www subdirectory of the HTTP server, empty path"
145     " feature will be disabled"),
146 };
147
148 static struct cfg_schema_section _http_section = { .type = OONF_HTTP_SUBSYSTEM,
149   .mode = CFG_SSMODE_UNNAMED,
150   .entries = _http_entries,
151   .entry_count = ARRAYSIZE(_http_entries),
152   .help = "Settings for the http interface",
153   .cb_delta_handler = _cb_config_changed };
154
155 /* tree of http sites */
156 static struct avl_tree _http_site_tree;
157
158 /* http session handling */
159 static struct oonf_stream_managed _http_managed_socket = {
160   .config =
161     {
162       .session_timeout = 120000, /* 120 seconds */
163       .maximum_input_buffer = 65536,
164       .allowed_sessions = 10,
165       .receive_data = _cb_receive_data,
166       .create_error = _cb_create_error,
167       .cleanup_session = _cb_cleanup_session,
168     },
169 };
170
171 static struct _http_config _config;
172
173 /* integrated telnet/file handler */
174 static struct oonf_http_handler _telnet_handler = {
175   .site = HTTP_TO_TELNET,
176   .content_handler = _cb_telnet_handler,
177   .acl =
178     {
179       .accept_default = true,
180     },
181 };
182
183 static struct oonf_http_handler _file_handler = {
184   .site = HTTP_FILES,
185   .content_handler = _cb_file_handler,
186   .acl =
187     {
188       .accept_default = true,
189     },
190 };
191
192 /* subsystem definition */
193 static const char *_dependencies[] = {
194   OONF_STREAM_SUBSYSTEM,
195   OONF_TELNET_SUBSYSTEM,
196 };
197
198 static struct oonf_subsystem _oonf_http_subsystem = {
199   .name = OONF_HTTP_SUBSYSTEM,
200   .dependencies = _dependencies,
201   .dependencies_count = ARRAYSIZE(_dependencies),
202   .init = _init,
203   .cleanup = _cleanup,
204   .cfg_section = &_http_section,
205 };
206 DECLARE_OONF_PLUGIN(_oonf_http_subsystem);
207
208 /**
209  * Initialize http subsystem
210  * @return always returns 0
211  */
212 static int
213 _init(void) {
214   oonf_stream_add_managed(&_http_managed_socket);
215   avl_init(&_http_site_tree, avl_comp_strcasecmp, false);
216
217   oonf_http_add(&_telnet_handler);
218   oonf_http_add(&_file_handler);
219
220   _config.www_dir_fd = -1;
221   return 0;
222 }
223
224 /**
225  * Free all resources allocated by http subsystem
226  */
227 void
228 _cleanup(void) {
229   free(_config.www_dir);
230   if (_config.www_dir_fd != -1) {
231     close(_config.www_dir_fd);
232   }
233
234   oonf_http_remove(&_telnet_handler);
235   oonf_http_remove(&_file_handler);
236   oonf_stream_remove_managed(&_http_managed_socket, true);
237   oonf_stream_free_managed_config(&_config.smc);
238 }
239
240 /**
241  * Add a http handler to the server. The site variable has
242  * to be initialized before this call.
243  * @param handler pointer to http handler
244  */
245 void
246 oonf_http_add(struct oonf_http_handler *handler) {
247   handler->directory = handler->site[strlen(handler->site) - 1] == '/';
248   handler->node.key = handler->site;
249   avl_insert(&_http_site_tree, &handler->node);
250
251   OONF_DEBUG(LOG_HTTP, "Added http handler for uri: %s", handler->site);
252 }
253
254 /**
255  * Removes a http handler from the server
256  * @param handler pointer to http handler
257  */
258 void
259 oonf_http_remove(struct oonf_http_handler *handler) {
260   avl_remove(&_http_site_tree, &handler->node);
261 }
262
263 /**
264  * Helper function to look for a http header, get or post value
265  * corresponding to a certain key.
266  * Use the oonf_http_lookup_(get|post|header)() inline functions.
267  *
268  * @param keys pointer to list of strings (char pointers) with keys
269  * @param values pointer to list of strings (char pointers) with values
270  * @param count number of keys/values
271  * @param key pointer to key string to look for
272  * @return pointer to value or NULL if not found
273  */
274 const char *
275 oonf_http_lookup_value(char **keys, char **values, size_t count, const char *key) {
276   size_t i;
277
278   for (i = 0; i < count; i++) {
279     if (strcmp(keys[i], key) == 0) {
280       return values[i];
281     }
282   }
283   return NULL;
284 }
285
286 /**
287  * Callback for incoming http data
288  * @param session pointer to tcp session
289  * @return state of tcp session
290  */
291 static enum oonf_stream_session_state
292 _cb_receive_data(struct oonf_stream_session *session) {
293   struct oonf_http_session header;
294   struct oonf_http_handler *handler;
295   char uri[OONF_HTTP_MAX_URI_LENGTH + 1];
296   char *first_header;
297   char *ptr;
298   size_t len;
299
300   /* search for end of http header */
301   if ((first_header = strstr(abuf_getptr(&session->in), "\r\n\r\n"))) {
302     first_header += 4;
303   }
304   else if ((first_header = strstr(abuf_getptr(&session->in), "\n\n"))) {
305     first_header += 2;
306   }
307   else {
308     /* still waiting for end of http header */
309     return STREAM_SESSION_ACTIVE;
310   }
311
312   if (_parse_http_header(abuf_getptr(&session->in), abuf_getlen(&session->in), &header)) {
313     OONF_INFO(LOG_HTTP, "Error, malformed HTTP header.\n");
314     _create_http_error(session, HTTP_400_BAD_REQ);
315     return STREAM_SESSION_SEND_AND_QUIT;
316   }
317
318   if (strcmp(header.http_version, HTTP_VERSION_1_0) != 0 && strcmp(header.http_version, HTTP_VERSION_1_1) != 0) {
319     OONF_INFO(LOG_HTTP, "Unknown HTTP version: '%s'\n", header.http_version);
320     _create_http_error(session, HTTP_400_BAD_REQ);
321     return STREAM_SESSION_SEND_AND_QUIT;
322   }
323
324   len = strlen(header.request_uri);
325   if (len >= OONF_HTTP_MAX_URI_LENGTH) {
326     OONF_INFO(LOG_HTTP, "Too long URI in HTTP header: '%s'\n", header.request_uri);
327     _create_http_error(session, HTTP_400_BAD_REQ);
328     return STREAM_SESSION_SEND_AND_QUIT;
329   }
330
331   OONF_DEBUG(LOG_HTTP, "Incoming HTTP request: %s %s %s\n", header.method, header.request_uri, header.http_version);
332
333   /* make working copy of URI string */
334   strscpy(uri, header.request_uri, sizeof(uri));
335
336   if (strcmp(header.method, HTTP_POST) == 0) {
337     const char *content_length;
338
339     content_length =
340       oonf_http_lookup_value(header.header_name, header.header_value, header.header_count, HTTP_CONTENT_LENGTH);
341     if (!content_length) {
342       OONF_INFO(LOG_HTTP, "Need 'content-length' for POST requests");
343       _create_http_error(session, HTTP_400_BAD_REQ);
344       return STREAM_SESSION_SEND_AND_QUIT;
345     }
346
347     if (strtoul(content_length, NULL, 10) > abuf_getlen(&session->in)) {
348       /* header not complete */
349       return STREAM_SESSION_ACTIVE;
350       ;
351     }
352
353     header.param_count = _parse_query_string(first_header, header.param_name, header.param_value, OONF_HTTP_MAX_PARAMS);
354   }
355
356   /* strip the URL fragment away */
357   ptr = strchr(uri, '#');
358   if (ptr) {
359     *ptr = 0;
360   }
361
362   /* decode special characters of URI */
363   _decode_uri(uri);
364
365   if (strcmp(header.method, HTTP_GET) == 0) {
366     /* HTTP-GET request */
367     ptr = strchr(uri, '?');
368     if (ptr != NULL) {
369       *ptr++ = 0;
370       header.param_count = _parse_query_string(ptr, header.param_name, header.param_value, OONF_HTTP_MAX_PARAMS);
371     }
372   }
373   else if (strcmp(header.method, HTTP_POST) != 0) {
374     OONF_INFO(LOG_HTTP, "HTTP method not implemented :'%s'", header.method);
375     _create_http_error(session, HTTP_501_NOT_IMPLEMENTED);
376     return STREAM_SESSION_SEND_AND_QUIT;
377   }
378
379   header.decoded_request_uri = uri;
380   header.remote = &session->remote_address;
381
382   handler = _get_site_handler(uri);
383   if (handler == NULL) {
384     OONF_DEBUG(LOG_HTTP, "No HTTP handler for site: %s", uri);
385     _create_http_error(session, HTTP_404_NOT_FOUND);
386     return STREAM_SESSION_SEND_AND_QUIT;
387   }
388
389   if (handler->content) {
390     /* static content */
391     abuf_memcpy(&session->out, handler->content, handler->content_size);
392     _create_http_header(session, HTTP_200_OK, NULL, abuf_getlen(&session->out));
393   }
394   else {
395     /* custom handler */
396     enum oonf_http_result result;
397     /* check acl */
398     if (!netaddr_acl_check_accept(&handler->acl, &session->remote_address)) {
399       _create_http_error(session, HTTP_403_FORBIDDEN);
400       return STREAM_SESSION_SEND_AND_QUIT;
401     }
402
403     /* check if username/password is necessary */
404     if (!strarray_is_empty(&handler->auth)) {
405       if (!_auth_okay(handler, &header)) {
406         _create_http_error(session, HTTP_401_UNAUTHORIZED);
407         return STREAM_SESSION_SEND_AND_QUIT;
408       }
409     }
410
411     len = abuf_getlen(&session->out);
412     result = handler->content_handler(&session->out, &header);
413     if (abuf_has_failed(&session->out)) {
414       abuf_setlen(&session->out, len);
415       result = HTTP_500_INTERNAL_SERVER_ERROR;
416     }
417
418     if (result == HTTP_START_FILE_TRANSFER) {
419       os_fd_init(&session->copy_fd, header.transfer_fd);
420       session->copy_total_size = header.transfer_length;
421       session->copy_bytes_sent = 0;
422
423       _create_http_header(session, HTTP_200_OK, header.content_type, header.transfer_length);
424     }
425     else if (result != HTTP_200_OK) {
426       /* create error message */
427       _create_http_error(session, result);
428     }
429     else {
430       _create_http_header(session, HTTP_200_OK, header.content_type, abuf_getlen(&session->out));
431     }
432   }
433   return STREAM_SESSION_SEND_AND_QUIT;
434 }
435
436 /**
437  * Close file transfer descriptor during cleanup
438  * @param session stream session to be cleaned up
439  */
440 static void
441 _cb_cleanup_session(struct oonf_stream_session *session) {
442   os_fd_close(&session->copy_fd);
443 }
444
445 /**
446  * Check if an incoming session is authorized to view a http site.
447  * @param handler pointer to site handler
448  * @param session pointer to http session.
449  * @return true if authorized, false if not
450  */
451 static bool
452 _auth_okay(struct oonf_http_handler *handler, struct oonf_http_session *session) {
453   const char *auth, *name_pw_base64;
454   char *ptr;
455
456   auth = oonf_http_lookup_header(session, "Authorization");
457   if (auth == NULL) {
458     return false;
459   }
460
461   name_pw_base64 = str_hasnextword(auth, "Basic");
462   if (name_pw_base64 == NULL) {
463     return false;
464   }
465
466   strarray_for_each_element(&handler->auth, ptr) {
467     if (strcmp(ptr, name_pw_base64) == 0) {
468       return true;
469     }
470   }
471   return false;
472 }
473
474 /**
475  * Callback for generating a TCP error
476  * @param session pointer to tcp session
477  * @param error tcp error code
478  */
479 static void
480 _cb_create_error(struct oonf_stream_session *session, enum oonf_stream_errors error) {
481   _create_http_error(session, (enum oonf_http_result)error);
482 }
483
484 /**
485  * Create body and header for a HTTP error
486  * @param session pointer to tcp session
487  * @param error http error code
488  */
489 static void
490 _create_http_error(struct oonf_stream_session *session, enum oonf_http_result error) {
491   abuf_clear(&session->out);
492   abuf_appendf(&session->out,
493     "<html><head><title>%s %s http server</title></head>"
494     "<body><h1>HTTP error %d: %s</h1></body></html>",
495     oonf_log_get_appdata()->app_name, oonf_log_get_libdata()->version, error, _get_headertype_string(error));
496   _create_http_header(session, error, NULL, abuf_getlen(&session->out));
497 }
498
499 /**
500  * Lookup the http site handler for an URI
501  * @param uri pointer to URI
502  * @return http site handler or NULL if none available
503  */
504 static struct oonf_http_handler *
505 _get_site_handler(const char *uri) {
506   struct oonf_http_handler *handler;
507   size_t len;
508
509   OONF_DEBUG(LOG_HTTP, "Look for handler for uri: %s", uri);
510
511   /* look for exact match */
512   handler = avl_find_element(&_http_site_tree, uri, handler, node);
513   if (handler) {
514     return handler;
515   }
516
517   /* look for directory handler with shorter URL */
518   handler = avl_find_le_element(&_http_site_tree, uri, handler, node);
519   if (handler && handler->directory) {
520     len = strlen(handler->site);
521
522     /* check if complete handler path (ending with /) matchs uri */
523     if (strncasecmp(handler->site, uri, len) == 0) {
524       return handler;
525     }
526   }
527
528   /* user might have skipped trailing / for directory */
529   handler = avl_find_ge_element(&_http_site_tree, uri, handler, node);
530   if (handler) {
531     len = strlen(uri);
532
533     if (strncasecmp(handler->site, uri, len) == 0 && handler->site[len] == '/' && handler->site[len + 1] == 0) {
534       return handler;
535     }
536   }
537   return NULL;
538 }
539
540 /**
541  * @param type http result code
542  * @return string representation of http result code
543  */
544 static const char *
545 _get_headertype_string(enum oonf_http_result type) {
546   switch (type) {
547     case HTTP_200_OK:
548       return HTTP_RESPONSE_200;
549     case HTTP_400_BAD_REQ:
550       return HTTP_RESPONSE_400;
551     case HTTP_401_UNAUTHORIZED:
552       return HTTP_RESPONSE_401;
553     case HTTP_403_FORBIDDEN:
554       return HTTP_RESPONSE_403;
555     case HTTP_404_NOT_FOUND:
556       return HTTP_RESPONSE_404;
557     case HTTP_413_REQUEST_TOO_LARGE:
558       return HTTP_RESPONSE_413;
559     case HTTP_500_INTERNAL_SERVER_ERROR:
560       return HTTP_RESPONSE_500;
561     case HTTP_501_NOT_IMPLEMENTED:
562       return HTTP_RESPONSE_501;
563     case HTTP_503_SERVICE_UNAVAILABLE:
564       return HTTP_RESPONSE_503;
565     default:
566       return HTTP_RESPONSE_500;
567   }
568 }
569
570 /**
571  * Create a http header for an existing content and put
572  * it in front of the content.
573  * @param session pointer to tcp session
574  * @param code http result code
575  * @param content_type explicit content type or NULL for
576  * @param content_length length of content, 0 for no content length
577  *   plain html
578  */
579 static void
580 _create_http_header(
581   struct oonf_stream_session *session, enum oonf_http_result code, const char *content_type, size_t content_length) {
582   struct autobuf buf;
583   struct timeval currtime;
584
585   abuf_init(&buf);
586
587   abuf_appendf(&buf, "%s %d %s\r\n", HTTP_VERSION_1_0, code, _get_headertype_string(code));
588
589   /* Date */
590   os_core_gettimeofday(&currtime);
591   abuf_strftime(&buf, "Date: %a, %d %b %Y %H:%M:%S GMT\r\n", localtime(&currtime.tv_sec));
592
593   /* Server version */
594   abuf_appendf(&buf, "Server: %s\r\n", oonf_log_get_libdata()->version);
595
596   /* connection-type */
597   abuf_puts(&buf, "Connection: closed\r\n");
598
599   /* allow cross domain access */
600   abuf_puts(&buf, "Access-Control-Allow-Origin: *\r\n");
601
602   /* MIME type */
603   if (content_type == NULL) {
604     content_type = HTTP_CONTENTTYPE_HTML;
605   }
606   abuf_appendf(&buf, "%s: %s\r\n", HTTP_CONTENT_TYPE, content_type);
607
608   /* Content length */
609   if (content_length > 0) {
610     abuf_appendf(&buf, "Content-length: %zu\r\n", content_length);
611   }
612
613   if (code == HTTP_401_UNAUTHORIZED) {
614     abuf_appendf(&buf, "WWW-Authenticate: Basic realm=\"%s\"\r\n", "RealmName");
615   }
616
617   /*
618    * Cache-control
619    * No caching dynamic pages
620    */
621   abuf_puts(&buf, "Cache-Control: no-cache\r\n");
622
623   /* End header */
624   abuf_puts(&buf, "\r\n");
625
626   abuf_memcpy_prepend(&session->out, abuf_getptr(&buf), abuf_getlen(&buf));
627   OONF_DEBUG(LOG_HTTP, "Generated Http-Header:\n%s", abuf_getptr(&buf));
628
629   abuf_free(&buf);
630 }
631
632 /**
633  * Parse a HTTP header
634  * @param header_data pointer to header data
635  * @param header_len length of header data
636  * @param header pointer to object to store the results
637  * @return 0 if http header was correct, -1 if an error happened
638  */
639 static int
640 _parse_http_header(char *header_data, size_t header_len, struct oonf_http_session *header) {
641   size_t header_index;
642
643   memset(header, 0, sizeof(struct oonf_http_session));
644   header->method = header_data;
645
646   while (true) {
647     if (header_len < 2) {
648       goto unexpected_end;
649     }
650
651     if (*header_data == ' ' && header->http_version == NULL) {
652       *header_data = '\0';
653
654       if (header->request_uri == NULL) {
655         header->request_uri = &header_data[1];
656       }
657       else if (header->http_version == NULL) {
658         header->http_version = &header_data[1];
659       }
660     }
661     else if (*header_data == '\r') {
662       *header_data = '\0';
663     }
664     else if (*header_data == '\n') {
665       *header_data = '\0';
666
667       header_data++;
668       header_len--;
669       break;
670     }
671
672     header_data++;
673     header_len--;
674   }
675
676   if (header->http_version == NULL) {
677     goto unexpected_end;
678   }
679
680   for (header_index = 0; true; header_index++) {
681     if (*header_data == '\n') {
682       break;
683     }
684     else if (*header_data == '\r') {
685       if (header_len < 2)
686         return true;
687
688       if (header_data[1] == '\n') {
689         break;
690       }
691     }
692
693     if (header_index >= OONF_HTTP_MAX_HEADERS) {
694       goto too_many_fields;
695     }
696
697     header->header_name[header_index] = header_data;
698
699     while (true) {
700       if (header_len < 1) {
701         goto unexpected_end;
702       }
703
704       if (*header_data == ':') {
705         *header_data = '\0';
706
707         header_data++;
708         header_len--;
709         break;
710       }
711       else if (*header_data == ' ' || *header_data == '\t') {
712         *header_data = '\0';
713       }
714       else if (*header_data == '\n' || *header_data == '\r') {
715         goto unexpected_end;
716       }
717
718       header_data++;
719       header_len--;
720     }
721
722     while (true) {
723       if (header_len < 1) {
724         goto unexpected_end;
725       }
726
727       if (header->header_value[header_index] == NULL) {
728         if (*header_data != ' ' && *header_data != '\t') {
729           header->header_value[header_index] = header_data;
730         }
731       }
732
733       if (*header_data == '\n') {
734         if (header_len < 2) {
735           goto unexpected_end;
736         }
737
738         if (header_data[1] == ' ' || header_data[1] == '\t') {
739           *header_data = ' ';
740           header_data[1] = ' ';
741
742           header_data += 2;
743           header_len -= 2;
744           continue;
745         }
746
747         *header_data = '\0';
748
749         if (header->header_value[header_index] == NULL) {
750           header->header_value[header_index] = header_data;
751         }
752
753         header_data++;
754         header_len--;
755         break;
756       }
757       else if (*header_data == '\r') {
758         if (header_len < 2) {
759           goto unexpected_end;
760         }
761
762         if (header_data[1] == '\n') {
763           if (header_len < 3) {
764             goto unexpected_end;
765           }
766
767           if (header_data[2] == ' ' || header_data[2] == '\t') {
768             *header_data = ' ';
769             header_data[1] = ' ';
770             header_data[2] = ' ';
771
772             header_data += 3;
773             header_len -= 3;
774             continue;
775           }
776
777           *header_data = '\0';
778
779           if (header->header_value[header_index] == NULL) {
780             header->header_value[header_index] = header_data;
781           }
782
783           header_data += 2;
784           header_len -= 2;
785           break;
786         }
787       }
788
789       header_data++;
790       header_len--;
791     }
792   }
793
794   header->header_count = header_index;
795   return 0;
796
797 too_many_fields:
798   OONF_DEBUG(LOG_HTTP, "Error, too many HTTP header fields\n");
799   return -1;
800
801 unexpected_end:
802   OONF_DEBUG(LOG_HTTP, "Error, unexpected end of HTTP header\n");
803   return -1;
804 }
805
806 /**
807  * Parse the query string (either get or post) and store it into
808  * a list of key/value pointers. The original string will be
809  * modified for doing this.
810  * @param s pointer to query string
811  * @param name pointer to array of stringpointers for keys
812  * @param value pointer to array of stringpointers for values
813  * @param count maximum allowed number of keys/values
814  * @return number of generated keys/values
815  */
816 static size_t
817 _parse_query_string(char *s, char **name, char **value, size_t count) {
818   char *ptr;
819   size_t i = 0;
820
821   while (s != NULL && i < count) {
822     name[i] = s;
823
824     s = strchr(s, '&');
825     if (s != NULL) {
826       *s++ = '\0';
827     }
828
829     ptr = strchr(name[i], '=');
830     if (ptr != NULL) {
831       *ptr++ = '\0';
832       value[i] = ptr;
833     }
834     else {
835       value[i] = &name[i][strlen(name[i])];
836     }
837
838     if (name[i][0] != '\0') {
839       i++;
840     }
841   }
842
843   return i;
844 }
845
846 /**
847  * Decode encoded characters of an URI. The URL will be modified
848  * inline by this function.
849  * @param src pointer to URI string
850  */
851 static void
852 _decode_uri(char *src) {
853   char *dst = src;
854
855   while (*src) {
856     if (*src == '%' && src[1] && src[2]) {
857       int value = 0;
858
859       src++;
860       sscanf(src, "%02x", &value);
861       *dst++ = (char)value;
862       src += 2;
863     }
864     else {
865       *dst++ = *src++;
866     }
867   }
868   *dst = 0;
869 }
870
871 /**
872  * Http to Telnet bridge
873  * @param out output stream
874  * @param session http session
875  * @return http result calculated from telnet result
876  */
877 static enum oonf_http_result
878 _cb_telnet_handler(struct autobuf *out, struct oonf_http_session *session) {
879   static char EOL = 0;
880   enum oonf_telnet_result result;
881   char buffer[1024];
882   char *ptr1, *ptr2, *ptr3;
883
884   session->content_type = HTTP_CONTENTTYPE_TEXT;
885   strscpy(buffer, &session->decoded_request_uri[sizeof(HTTP_TO_TELNET) - 1], sizeof(buffer));
886
887   ptr1 = buffer;
888   while (true) {
889     ptr2 = strchr(ptr1, '/');
890     if (ptr2) {
891       *ptr2 = 0;
892     }
893
894     OONF_DEBUG(LOG_HTTP, "Process '%s'", ptr1);
895     ptr3 = strchr(ptr1, ' ');
896     if (ptr3) {
897       *ptr3++ = 0;
898     }
899     else {
900       ptr3 = &EOL;
901     }
902
903     result = oonf_telnet_execute(ptr1, ptr3, out, session->remote);
904     switch (result) {
905       case TELNET_RESULT_ACTIVE:
906       case TELNET_RESULT_QUIT:
907         break;
908
909       case _TELNET_RESULT_UNKNOWN_COMMAND:
910         return HTTP_404_NOT_FOUND;
911
912       default:
913         return HTTP_400_BAD_REQ;
914     }
915
916     if (!ptr2) {
917       break;
918     }
919     ptr1 = ptr2 + 1;
920   }
921   return HTTP_200_OK;
922 }
923
924 /**
925  * Http File transfer handler
926  * @param out output stream
927  * @param session http session
928  * @return http result
929  */
930 static enum oonf_http_result
931 _cb_file_handler(struct autobuf *out __attribute__((unused)), struct oonf_http_session *session) {
932   const char *file;
933   struct stat st;
934   int fd;
935
936   if (_config.www_dir_fd == -1) {
937     /* file server not active */
938     return HTTP_404_NOT_FOUND;
939   }
940
941   if (strstr(session->decoded_request_uri, "/../")) {
942     /* directory traversal is not allowed */
943     OONF_INFO(LOG_HTTP, "Blocked directory traversal '%s' uri", session->decoded_request_uri);
944     return HTTP_404_NOT_FOUND;
945   }
946
947   file = &session->decoded_request_uri[sizeof(HTTP_FILES) - 1];
948   if (file[0] == '/') {
949     /* directory traversal is not allowed */
950     OONF_INFO(LOG_HTTP, "Blocked directory traversal '%s' uri", session->decoded_request_uri);
951     return HTTP_404_NOT_FOUND;
952   }
953
954   fd = openat(_config.www_dir_fd, file, O_NONBLOCK, O_RDONLY);
955   if (fd == -1) {
956     OONF_INFO(LOG_HTTP, "Could not open file '%s': %s (%d)", file, strerror(errno), errno);
957     return HTTP_404_NOT_FOUND;
958   }
959
960   if (fstat(fd, &st)) {
961     OONF_WARN(LOG_HTTP, "Could not get file statistics of '%s': %s (%d)", file, strerror(errno), errno);
962     return HTTP_404_NOT_FOUND;
963   }
964
965   /* initialize file transfer */
966   session->content_type = oonf_http_lookup_header(session, HTTP_CONTENT_TYPE);
967   session->transfer_fd = fd;
968   session->transfer_length = st.st_size;
969
970   /* start file transfer */
971   return HTTP_START_FILE_TRANSFER;
972 }
973
974 /**
975  * Callback for configuration changes
976  */
977 static void
978 _cb_config_changed(void) {
979   /* generate binary config */
980   if (cfg_schema_tobin(&_config, _http_section.post, _http_entries, ARRAYSIZE(_http_entries))) {
981     /* error in conversion */
982     OONF_WARN(LOG_HTTP, "Cannot map http config to binary data");
983     return;
984   }
985
986   oonf_stream_apply_managed(&_http_managed_socket, &_config.smc);
987
988   if (_config.www_dir_fd != -1) {
989     close(_config.www_dir_fd);
990     _config.www_dir_fd = -1;
991   }
992   if (_config.www_dir && _config.www_dir[0]) {
993     _config.www_dir_fd = open(_config.www_dir, O_DIRECTORY, O_RDONLY);
994     if (_config.www_dir_fd == -1) {
995       OONF_WARN(LOG_HTTP, "Could not open file directory '%s': %s (%d)", _config.www_dir, strerror(errno), errno);
996     }
997   }
998 }