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