93ad6a8cae68123cbb57a6fbab41b92517e18add
[olsrd.git] / src / olsr_comport_http.c
1
2 /*
3  * The olsr.org Optimized Link-State Routing daemon(olsrd)
4  * Copyright (c) 2004-2009, 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 #include <assert.h>
43 #include <string.h>
44
45 #include "common/autobuf.h"
46 #include "common/avl.h"
47 #include "common/avl_olsr_comp.h"
48 #include "common/string.h"
49 #include "olsr_logging.h"
50 #include "olsr_cookie.h"
51 #include "olsr_comport.h"
52 #include "olsr_comport_http.h"
53 #include "olsr_comport_txt.h"
54 #include "olsr_cfg.h"
55 #include "ipcalc.h"
56
57 #define HTTP_TESTSITE
58
59 static const char HTTP_VERSION[] = "HTTP/1.0";
60 static const char TELNET_PATH[] = "/telnet/";
61
62 static struct olsr_cookie_info *htmlsite_cookie;
63 struct avl_tree http_handler_tree;
64
65 /**Response types */
66 static char http_200_response[] = "OK";
67 static char http_400_response[] = "Bad Request";
68 static char http_401_response[] = "Unauthorized";
69 static char http_403_response[] = "Forbidden";
70 static char http_404_response[] = "Not Found";
71 static char http_413_response[] = "Request Entity Too Large";
72 static char http_501_response[] = "Not Implemented";
73 static char http_503_response[] = "Service Unavailable";
74
75 static bool parse_http_header(char *message, size_t message_len, struct http_request *request);
76
77 /* sample for a static html page */
78 #ifdef HTTP_TESTSITE
79
80 static void
81 test_handler(struct comport_connection *con, struct http_request *request) {
82   size_t i;
83
84   abuf_puts(&con->out, "<html><body>");
85   abuf_appendf(&con->out, "<br>Request: %s</br>\n", request->method);
86   abuf_appendf(&con->out, "<br>Filename: %s</br>\n", request->request_uri);
87   abuf_appendf(&con->out, "<br>Http-Version: %s</br>\n", request->http_version);
88
89   for (i=0; i<request->query_count; i++) {
90     abuf_appendf(&con->out, "<br>URL-Parameter: %s = %s</br>\n", request->query_name[i], request->query_value[i]);
91   }
92   for (i=0; i<request->header_count; i++) {
93     abuf_appendf(&con->out, "<br>Header: %s = %s</br>\n", request->header_name[i], request->header_value[i]);
94   }
95   abuf_puts(&con->out, "</body></html>");
96 }
97
98 static void init_test(void) {
99   static char content[] = "<html><body>Yes, you got it !</body></html>";
100   static char acl[] = "d2lraTpwZWRpYQ=="; /* base 64 .. this is "wiki:pedia" */
101   static char *aclPtr[] = { acl };
102   struct olsr_html_site *site;
103
104   site = olsr_com_add_htmlsite("/", content, strlen(content));
105   olsr_com_set_htmlsite_acl_auth(site, NULL, 1, aclPtr);
106
107   olsr_com_add_htmlhandler(test_handler, "/print/");
108 }
109 #endif
110
111 static void
112 olsr_com_html2telnet_gate(struct comport_connection *con, struct http_request *request) {
113   if (strlen(request->request_uri) > strlen(TELNET_PATH)) {
114     char *cmd = &request->request_uri[strlen(TELNET_PATH)];
115     char *next;
116     size_t count = 1;
117     enum olsr_txtcommand_result result;
118
119     while (cmd) {
120       next = strchr(cmd, '/');
121       if (next) {
122         *next++ = 0;
123       }
124
125       result = olsr_com_handle_txtcommand(con, cmd, request->query_count > count ? request->query_value[count] : NULL);
126
127       /* sorry, no continous output */
128       if (result == CONTINOUS) {
129         con->stop_handler(con);
130       }
131       else if (result == UNKNOWN) {
132         abuf_appendf(&con->out, "Unknown command %s\n", cmd);
133         con->send_as = HTTP_404_NOT_FOUND;
134       }
135       else if (result != CONTINUE) {
136         abuf_appendf(&con->out, "Http-Telnet gate had problems with command %s\n", cmd);
137         con->send_as = HTTP_400_BAD_REQ;
138       }
139       cmd = next;
140       count ++;
141     }
142   }
143 }
144
145 void
146 olsr_com_init_http(void) {
147   avl_init(&http_handler_tree, &avl_comp_strcasecmp, false, NULL);
148
149   htmlsite_cookie = olsr_create_memcookie("comport http sites", sizeof(struct olsr_html_site));
150
151   /* activate telnet gateway */
152   olsr_com_add_htmlhandler(olsr_com_html2telnet_gate, TELNET_PATH);
153 #ifdef HTTP_TESTSITE
154   init_test();
155 #endif
156 }
157
158 void olsr_com_destroy_http(void) {
159   struct olsr_html_site *site;
160   struct list_iterator iterator;
161
162   OLSR_FOR_ALL_HTML_ENTRIES(site, iterator) {
163     olsr_com_remove_htmlsite(site);
164   }
165 }
166
167 struct olsr_html_site *
168 olsr_com_add_htmlsite(const char *path, const char *content, size_t length) {
169   struct olsr_html_site *site;
170
171   site = olsr_cookie_malloc(htmlsite_cookie);
172   site->node.key = strdup(path);
173
174   site->static_site = true;
175   site->site_data = content;
176   site->site_length = length;
177
178   avl_insert(&http_handler_tree, &site->node);
179   return site;
180 }
181
182 struct olsr_html_site *
183 olsr_com_add_htmlhandler(void(*sitehandler)(struct comport_connection *con, struct http_request *request),
184     const char *path) {
185   struct olsr_html_site *site;
186
187   site = olsr_cookie_malloc(htmlsite_cookie);
188   site->node.key = strdup(path);
189
190   site->static_site = false;
191   site->sitehandler = sitehandler;
192
193   avl_insert(&http_handler_tree, &site->node);
194   return site;
195 }
196
197 void
198 olsr_com_remove_htmlsite(struct olsr_html_site *site) {
199   avl_delete(&http_handler_tree, &site->node);
200   free(site->node.key);
201   olsr_cookie_free(htmlsite_cookie, site);
202 }
203
204 void
205 olsr_com_set_htmlsite_acl_auth(struct olsr_html_site *site, struct ip_acl *ipacl, int auth_count, char **auth_entries) {
206   site->acl = ipacl;
207   site->auth_count = auth_count;
208   site->auth = auth_entries;
209 }
210
211 /* handle the html site. returns true on successful handling (even if it was unauthorized)
212  * false if we did not find a site
213 */
214 static bool
215 olsr_com_handle_htmlsite(struct comport_connection *con, char *path,
216     struct http_request *request) {
217   char *str;
218   int i;
219   struct olsr_html_site *site;
220
221   site = (struct olsr_html_site *)avl_find(&http_handler_tree, path);
222   if (site == NULL) {
223     OLSR_DEBUG(LOG_COMPORT, "No httphandler found for path %s\n", path);
224     return false;
225   }
226
227   /* check if username/password is necessary */
228   if (site->auth) {
229     /* test for correct ACL */
230     char key[256] = { 0 };
231
232     con->send_as = HTTP_401_UNAUTHORIZED;
233
234     str = strstr(con->in.buf, "\nAuthorization: Basic ");
235     if (str != NULL && sscanf(str + 1, "%*s %*s %s\n", key) == 1) {
236       OLSR_DEBUG(LOG_COMPORT, "ACL string received: %s\n", key);
237       for (i = 0; i < site->auth_count; i++) {
238         if (strcmp(site->auth[i], key) == 0) {
239           con->send_as = HTTP_200_OK;
240           break;
241         }
242       }
243     }
244     if (con->send_as == HTTP_401_UNAUTHORIZED) {
245       OLSR_DEBUG(LOG_COMPORT, "Error, invalid authorization\n");
246       return true;
247     }
248   }
249
250   /* check if ip is allowed */
251   if (site->acl != NULL && !ip_acl_acceptable(site->acl, &con->addr, olsr_cnf->ip_version)) {
252 #if !defined(REMOVE_LOG_DEBUG)
253     struct ipaddr_str buf;
254 #endif
255     con->send_as = HTTP_403_FORBIDDEN;
256     OLSR_DEBUG(LOG_COMPORT, "Error, access by IP %s is not allowed for path %s\n",
257         path, olsr_ip_to_string(&buf, &con->addr));
258     return true;
259   }
260
261   /* call site handler */
262   if (site->static_site) {
263     abuf_memcpy(&con->out, site->site_data, site->site_length);
264   } else {
265     site->sitehandler(con, request);
266   }
267   con->send_as = HTTP_200_OK;
268   return true;
269 }
270
271 static bool parse_http_header(char *message, size_t message_len, struct http_request *request) {
272   size_t header_index;
273
274   assert(message);
275   assert(request);
276
277   memset(request, 0, sizeof(struct http_request));
278   request->method = message;
279
280   while(true) {
281     if (message_len < 2) {
282       goto unexpected_end;
283     }
284
285     if (*message == ' ' && request->http_version == NULL) {
286       *message = '\0';
287
288       if (request->request_uri == NULL) {
289         request->request_uri = &message[1];
290       }
291       else if (request->http_version == NULL) {
292         request->http_version = &message[1];
293       }
294     }
295     else if (*message == '\r') {
296       *message = '\0';
297     }
298     else if (*message == '\n') {
299       *message = '\0';
300
301       message++; message_len--;
302       break;
303     }
304
305     message++; message_len--;
306   }
307
308   if (request->http_version == NULL) {
309     goto unexpected_end;
310   }
311
312   for(header_index = 0; true; header_index++) {
313     if (message_len < 1) {
314       goto unexpected_end;
315     }
316
317     if (*message == '\n') {
318       break;
319     }
320     else if (*message == '\r') {
321       if (message_len < 2) return true;
322
323       if (message[1] == '\n') {
324         break;
325       }
326     }
327
328     if (header_index >= MAX_HTTP_HEADERS) {
329       goto too_many_fields;
330     }
331
332     request->header_name[header_index] = message;
333
334     while(true) {
335       if (message_len < 1) {
336         goto unexpected_end;
337       }
338
339       if (*message == ':') {
340         *message = '\0';
341
342         message++; message_len--;
343         break;
344       }
345       else if (*message == ' ' || *message == '\t') {
346         *message = '\0';
347       }
348       else if (*message == '\n' || *message == '\r') {
349         goto unexpected_end;
350       }
351
352       message++; message_len--;
353     }
354
355     while(true) {
356       if (message_len < 1) {
357         goto unexpected_end;
358       }
359
360       if (request->header_value[header_index] == NULL) {
361         if (*message != ' ' && *message != '\t') {
362           request->header_value[header_index] = message;
363         }
364       }
365
366       if (*message == '\n') {
367         if (message_len < 2) {
368           goto unexpected_end;
369         }
370
371         if (message[1] == ' ' || message[1] == '\t') {
372           *message = ' ';
373           message[1] = ' ';
374
375           message += 2; message_len -= 2;
376           continue;
377         }
378
379         *message = '\0';
380
381         if (request->header_value[header_index] == NULL) {
382           request->header_value[header_index] = message;
383         }
384
385         message++; message_len--;
386         break;
387       }
388       else if (*message == '\r') {
389         if (message_len < 2) {
390           goto unexpected_end;
391         }
392
393         if (message[1] == '\n') {
394           if (message_len < 3) {
395             goto unexpected_end;
396           }
397
398           if (message[2] == ' ' || message[2] == '\t') {
399             *message = ' ';
400             message[1] = ' ';
401             message[2] = ' ';
402
403             message += 3; message_len -= 3;
404             continue;
405           }
406
407           *message = '\0';
408
409           if (request->header_value[header_index] == NULL) {
410             request->header_value[header_index] = message;
411           }
412
413           message += 2; message_len -= 2;
414           break;
415         }
416       }
417
418       message++; message_len--;
419     }
420   }
421
422   request->header_count = header_index;
423   return false;
424
425 too_many_fields:
426   OLSR_DEBUG(LOG_COMPORT, "Error, too many HTTP header fields\n");
427   return true;
428
429 unexpected_end:
430   OLSR_DEBUG(LOG_COMPORT, "Error, unexpected end of HTTP header\n");
431   return true;
432 }
433
434 static size_t parse_query_string(char *s, char **name, char **value, size_t count) {
435   char *ptr;
436   size_t i = 0;
437
438   assert(s);
439   assert(name);
440   assert(value);
441
442   while (s != NULL && i < count) {
443     name[i] = s;
444
445     s = strchr(s, '&');
446     if (s != NULL) {
447       *s++ = '\0';
448     }
449
450     ptr = strchr(name[i], '=');
451     if (ptr != NULL) {
452       *ptr++ = '\0';
453       value[i] = ptr;
454     } else {
455       value[i] = &name[i][strlen(name[i])];
456     }
457
458     if(name[i][0] != '\0') {
459       i++;
460     }
461   }
462
463   return i;
464 }
465
466 void olsr_com_parse_http(struct comport_connection *con,
467     unsigned int flags  __attribute__ ((unused))) {
468   struct http_request request;
469   char *para = NULL, *str = NULL;
470   char processed_filename[256];
471   int idx = 0;
472   size_t i = 0;
473
474   /*
475    * find end of http header, might be useful for POST to keep the index !
476    * (implemented as a finite element automaton)
477    */
478
479   while (i < 5) {
480     switch (con->in.buf[idx++]) {
481       case '\0':
482         i = 6;
483         break;
484       case '\r':
485         i = (i == 3) ? 4 : 2;
486         break;
487       case '\n':
488         if (i == 1 || i == 4) {
489           i = 5;
490         } else if (i == 2) {
491           i = 3;
492         } else {
493           i = 1;
494         }
495         break;
496       default:
497         i = 0;
498         break;
499     }
500   }
501
502   if (i != 5) {
503     OLSR_DEBUG(LOG_COMPORT, "  need end of http header, still waiting...\n");
504     return;
505   }
506
507   /* got http header */
508
509   if (parse_http_header(con->in.buf, con->in.len, &request)) {
510     OLSR_DEBUG(LOG_COMPORT, "Error, illegal http header.\n");
511     con->send_as = HTTP_400_BAD_REQ;
512     con->state = SEND_AND_QUIT;
513     return;
514   }
515
516   if (strcasecmp(request.http_version, "HTTP/1.1") != 0 && strcasecmp(request.http_version, "HTTP/1.0") != 0) {
517     OLSR_DEBUG(LOG_COMPORT, "Unknown Http-Version: '%s'\n", request.http_version);
518     con->send_as = HTTP_400_BAD_REQ;
519     con->state = SEND_AND_QUIT;
520     return;
521   }
522
523   OLSR_DEBUG(LOG_COMPORT, "HTTP Request: %s %s %s\n", request.method, request.request_uri, request.http_version);
524
525   /* store a copy for the http_handlers */
526   strscpy(processed_filename, request.request_uri, sizeof(processed_filename));
527   if (strcmp(request.method, "POST") == 0) {
528     /* load the rest of the header for POST commands */
529     int clen;
530
531     for (i=0, clen=-1; i<request.header_count; i++) {
532       if (strcasecmp(request.header_name[i], "Content-Length") == 0) {
533         clen = atoi(request.header_value[i]);
534         break;
535       }
536     }
537
538     if (clen == -1) {
539       con->send_as = HTTP_400_BAD_REQ;
540       con->state = SEND_AND_QUIT;
541       return;
542     }
543
544     if (con->in.len < idx + clen) {
545       /* we still need more data */
546       return;
547     }
548
549     request.form_count = parse_query_string(&con->in.buf[idx], request.form_name, request.form_value, MAX_HTTP_FORM);
550   }
551
552   /* strip the URL marker away */
553   str = strchr(processed_filename, '#');
554   if (str) {
555     *str = 0;
556   }
557
558   /* we have everything to process the http request */
559   con->state = SEND_AND_QUIT;
560   olsr_com_decode_url(processed_filename);
561
562   if (strcmp(request.method, "GET") == 0) {
563     /* HTTP-GET request */
564     para = strchr(processed_filename, '?');
565     if (para != NULL) {
566       *para++ = 0;
567       request.query_count = parse_query_string(para, request.query_name, request.query_value, MAX_HTTP_QUERY);
568     }
569   } else if (strcmp(request.method, "POST") != 0) {
570     con->send_as = HTTP_501_NOT_IMPLEMENTED;
571     return;
572   }
573
574   /* create body */
575   i = strlen(processed_filename);
576
577   /*
578    * add a '/' at the end if it's not there to detect
579    *  paths without terminating '/' from the browser
580    */
581   if (processed_filename[i - 1] != '/' && request.query_count == 0) {
582     strcat(processed_filename, "/");
583   }
584
585   while (i > 0) {
586     if (olsr_com_handle_htmlsite(con, processed_filename, &request)) {
587       return;
588     }
589
590     /* try to find a handler for a path prefix */
591     if (i > 0 && processed_filename[i] == '/') {
592       processed_filename[i--] = 0;
593     }
594     else {
595       do {
596         processed_filename[i--] = 0;
597       } while (i > 0 && processed_filename[i] != '/');
598     }
599   }
600   con->send_as = HTTP_404_NOT_FOUND;
601 }
602
603 void
604 olsr_com_build_httpheader(struct comport_connection *con) {
605   struct autobuf buf;
606   time_t currtime;
607
608   abuf_init(&buf, 1024);
609
610   abuf_appendf(&buf, "%s %d %s\r\n", HTTP_VERSION, con->send_as, olsr_com_get_http_message(con->send_as));
611
612   /* Date */
613   time(&currtime);
614   abuf_strftime(&buf, "Date: %a, %d %b %Y %H:%M:%S GMT\r\n", localtime(&currtime));
615
616   /* Server version */
617   abuf_appendf(&buf, "Server: %s %s %s %s\r\n", olsrd_version, build_date, build_host, HTTP_VERSION);
618
619   /* connection-type */
620   abuf_puts(&buf, "Connection: closed\r\n");
621
622   /* MIME type */
623   if (con->http_contenttype == NULL) {
624     con->http_contenttype = con->send_as != HTTP_PLAIN ? "text/html" : "text/plain";
625   }
626   abuf_appendf(&buf, "Content-type: %s\r\n", con->http_contenttype);
627
628   /* Content length */
629   if (con->out.len > 0) {
630     abuf_appendf(&buf, "Content-length: %u\r\n", con->out.len);
631   }
632
633   if (con->send_as == HTTP_401_UNAUTHORIZED) {
634     abuf_appendf(&buf, "WWW-Authenticate: Basic realm=\"%s\"\r\n", "RealmName");
635   }
636   /* Cache-control
637    * No caching dynamic pages
638    */
639   abuf_puts(&buf, "Cache-Control: no-cache\r\n");
640
641   if (con->send_as == HTTP_PLAIN) {
642     abuf_puts(&buf, "Accept-Ranges: bytes\r\n");
643   }
644   /* End header */
645   abuf_puts(&buf, "\r\n");
646
647   abuf_memcpy_prefix(&con->out, buf.buf, buf.len);
648   OLSR_DEBUG(LOG_PLUGINS, "HEADER:\n%s", buf.buf);
649
650   abuf_free(&buf);
651 }
652
653 void
654 olsr_com_create_httperror(struct comport_connection *con) {
655   abuf_appendf(&con->out, "<body><h1>HTTP error %d: %s</h1></body>", con->send_as, olsr_com_get_http_message(con->send_as));
656 }
657
658 char *
659 olsr_com_get_http_message(enum http_header_type type) {
660   static char nothing[] = "";
661
662   switch (type) {
663     case HTTP_PLAIN:
664     case HTTP_200_OK:
665       return http_200_response;
666     case HTTP_400_BAD_REQ:
667       return http_400_response;
668     case HTTP_401_UNAUTHORIZED:
669       return http_401_response;
670     case HTTP_403_FORBIDDEN:
671       return http_403_response;
672     case HTTP_404_NOT_FOUND:
673       return http_404_response;
674     case HTTP_413_REQUEST_TOO_LARGE:
675       return http_413_response;
676     case HTTP_501_NOT_IMPLEMENTED:
677       return http_501_response;
678     case HTTP_503_SERVICE_UNAVAILABLE:
679       return http_503_response;
680     default:
681       return nothing;
682   }
683 }
684
685 void olsr_com_decode_url(char *str) {
686   char *dst = str;
687
688   while (*str) {
689     if (*str == '%' && str[1] && str[2]) {
690       int value = 0;
691
692       str++;
693       sscanf(str, "%02x", &value);
694       *dst++ = (char) value;
695       str += 2;
696     } else {
697       *dst++ = *str++;
698     }
699   }
700   *dst = 0;
701 }