00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include <config.h>
00021
00022 #include <stdio.h>
00023 #include <stdlib.h>
00024 #include <stdarg.h>
00025 #include <errno.h>
00026
00027 #ifdef HAVE_SYS_TYPES_H
00028 #include <sys/types.h>
00029 #endif
00030
00031 #include <sys/socket.h>
00032 #include <time.h>
00033 #include <setjmp.h>
00034
00035 #ifdef HAVE_STRING_H
00036 #include <string.h>
00037 #endif
00038
00039 #ifdef HAVE_UNISTD_H
00040 #include <unistd.h>
00041 #endif
00042
00043 #ifdef HAVE_LIMITS_H
00044 #include <limits.h>
00045 #endif
00046
00047 #include "processor.h"
00048 #include "base64.h"
00049
00050 #define REQUEST 1
00051 #define RESPONSE 2
00052 #define HEADER 3
00053 #define PARAM 4
00054
00055
00056 static sigjmp_buf timeout;
00057
00058
00059 static void do_service(RequestWrapper);
00060 static void send_response(HttpResponse);
00061 static char *get_date();
00062 static char *get_server();
00063 static HttpRequest create_HttpRequest(RequestWrapper);
00064 static HttpResponse create_HttpResponse(RequestWrapper);
00065 static void create_headers(HttpRequest);
00066 static int create_parameters(HttpRequest req);
00067 static void reset_response(HttpResponse res);
00068 static int is_authenticated(HttpRequest, HttpResponse);
00069 static int basic_authenticate(HttpRequest);
00070 static void done(HttpRequest, HttpResponse);
00071 static void destroy_HttpRequest(HttpRequest);
00072 static void destroy_HttpResponse(HttpResponse);
00073 static void destroy_entry(void *);
00074 static void internal_error(int, int, char *);
00075 static HttpParameter parse_parameters(char *);
00076 static int do_timeout(int);
00077 static void request_timeout(int);
00078
00079
00112
00113
00114
00121 void *http_processor(void *wrapper) {
00122
00123 RequestWrapper W= wrapper;
00124
00125 if (do_timeout(W->socket))
00126 goto shutdown;
00127
00128 do_service(W);
00129
00130 shutdown:
00131 alarm(0);
00132 destroy_wrapper(W);
00133
00134 return NULL;
00135
00136 }
00137
00138
00144 void add_Impl(void *doGetFunc, void *doPostFunc) {
00145
00146 Impl.doGet= doGetFunc;
00147 Impl.doPost= doPostFunc;
00148
00149 }
00150
00151
00152
00153
00154
00161 void send_error(HttpResponse res, int code, char *msg) {
00162
00163 char *err= get_status_string(code);
00164 char *server= get_server();
00165
00166 reset_response(res);
00167 set_content_type(res, "text/html");
00168 set_status(res, code, err);
00169 out_print(res,
00170 "<html><head><title>%d %s</title></head>"\
00171 "<body bgcolor=#FFFFFF><h2>%s</h2>%s<p>"\
00172 "<hr><a href='%s'><font size=-1>%s</font></a>"\
00173 "</body></html>\r\n",
00174 code, err, err, msg?msg:"", SERVER_URL, server);
00175 free(err);
00176 free(server);
00177
00178 }
00179
00180
00187 void send_redirect(HttpResponse res, char *location) {
00188
00189 char *string= get_status_string(SC_MOVED_TEMPORARILY);
00190 reset_response(res);
00191 set_status(res, SC_MOVED_TEMPORARILY, string);
00192 set_header(res, "Location", location);
00193 free(string);
00194
00195 }
00196
00197
00212 void out_print(HttpResponse res, const char *format, ...) {
00213
00214 if ( format ) {
00215
00216 int n= 0;
00217 ssize_t need= 0;
00218 ssize_t have= 0;
00219 int size= RES_STRLEN;
00220 char *string= xmalloc(size);
00221 va_list ap;
00222
00223
00224 while (1) {
00225
00226 va_start(ap, format);
00227 n= vsnprintf(string, size, format, ap);
00228 va_end(ap);
00229
00230 if (n > -1 && n < size) {
00231
00232 break;
00233
00234 }
00235
00236 if (n > -1) {
00237
00238 size= n+1;
00239
00240 } else {
00241
00242 size*= 2;
00243
00244 }
00245
00246 string= xresize(string, size);
00247
00248 }
00249
00250
00251 if ( (float)((float)(sizeof(char)*(res->bufsize + RES_STRLEN + n + 1))
00252 > SSIZE_MAX) ) {
00253
00254 log("out_print: Possible http response buffer overflow detected. "
00255 "aborting \n");
00256 exit(1);
00257
00258 }
00259
00260 have= res->bufsize - res->bufused;
00261 need= sizeof(char)*( n + 1);
00262
00263 if ( have <= need ) {
00264
00265
00266 res->outputbuffer=
00267 xresize(res->outputbuffer,(res->bufsize + need + RES_STRLEN));
00268
00269 res->bufsize+= (need + RES_STRLEN);
00270
00271 if ( !res->bufused ) {
00272
00273 memset(res->outputbuffer, 0, res->bufsize);
00274
00275 }
00276
00277 res->bufused+= need;
00278
00279 } else {
00280
00281 res->bufused+= need;
00282
00283 }
00284
00285 strcat(res->outputbuffer, string);
00286
00287 free(string);
00288
00289 }
00290
00291 }
00292
00293
00298 void destroy_wrapper(RequestWrapper w) {
00299
00300 if ( w && w->inetaddr ) {
00301
00302 free(w->inetaddr->remote_host);
00303 free(w->inetaddr->local_host);
00304 free(w->inetaddr);
00305
00306 }
00307
00308 close_socket(w->socket);
00309
00310 free(w);
00311
00312 }
00313
00314
00315
00316
00317
00325 void set_header(HttpResponse res, char *name, char *value) {
00326
00327 HttpHeader h= NEW(h);
00328
00329 h->name= xstrdup(name);
00330 h->value= xstrdup(value);
00331
00332 if ( res->headers ) {
00333
00334 HttpHeader n, p;
00335
00336 for ( n= p= res->headers; p; n= p, p= p->next) {
00337
00338 if ( !strcasecmp(p->name, name) ) {
00339
00340 free(p->value);
00341 p->value= xstrdup(value);
00342 destroy_entry(h);
00343 return;
00344
00345 }
00346
00347 }
00348
00349 n->next= h;
00350
00351 } else {
00352
00353 res->headers= h;
00354
00355 }
00356
00357 }
00358
00359
00366 void set_status(HttpResponse res, int code, char *msg) {
00367
00368 res->status= code;
00369 free(res->status_msg);
00370 res->status_msg= xstrdup(msg);
00371
00372 }
00373
00374
00380 void set_content_type(HttpResponse res, char *mime) {
00381
00382 set_header(res, "Content-Type", mime);
00383
00384 }
00385
00386
00393 char *get_header(HttpRequest req, const char *name) {
00394
00395 HttpHeader p;
00396
00397 for ( p= req->headers; p; p= p->next) {
00398
00399 if ( !strcasecmp(p->name, name) ) {
00400
00401 return xstrdup(p->value);
00402
00403 }
00404
00405 }
00406
00407 return NULL;
00408
00409 }
00410
00411
00418 char *get_parameter(HttpRequest req, const char *name) {
00419
00420 HttpParameter p;
00421
00422 for ( p= req->params; p; p= p->next) {
00423
00424 if ( !strcasecmp(p->name, name) ) {
00425
00426 return xstrdup(p->value);
00427
00428 }
00429
00430 }
00431
00432 return NULL;
00433
00434 }
00435
00436
00444 char *get_headers(HttpResponse res) {
00445
00446 HttpHeader p;
00447 char buf[RES_STRLEN];
00448 char *b= buf;
00449
00450 for ( p= res->headers; p; p= p->next) {
00451
00452 b+= sprintf(b, "%s: %s\r\n", p->name, p->value);
00453
00454 }
00455
00456 return buf[0]?xstrdup(buf):NULL;
00457
00458 }
00459
00460
00468 char *get_status_string(int status) {
00469
00470 switch (status) {
00471 case SC_OK:
00472 return xstrdup("OK");
00473 case SC_ACCEPTED:
00474 return xstrdup("Accepted");
00475 case SC_BAD_GATEWAY:
00476 return xstrdup("Bad Gateway");
00477 case SC_BAD_REQUEST:
00478 return xstrdup("Bad Request");
00479 case SC_CONFLICT:
00480 return xstrdup("Conflict");
00481 case SC_CONTINUE:
00482 return xstrdup("Continue");
00483 case SC_CREATED:
00484 return xstrdup("Created");
00485 case SC_EXPECTATION_FAILED:
00486 return xstrdup("Expectation Failed");
00487 case SC_FORBIDDEN:
00488 return xstrdup("Forbidden");
00489 case SC_GATEWAY_TIMEOUT:
00490 return xstrdup("Gateway Timeout");
00491 case SC_GONE:
00492 return xstrdup("Gone");
00493 case SC_VERSION_NOT_SUPPORTED:
00494 return xstrdup("HTTP Version Not Supported");
00495 case SC_INTERNAL_SERVER_ERROR:
00496 return xstrdup("Internal Server Error");
00497 case SC_LENGTH_REQUIRED:
00498 return xstrdup("Length Required");
00499 case SC_METHOD_NOT_ALLOWED:
00500 return xstrdup("Method Not Allowed");
00501 case SC_MOVED_PERMANENTLY:
00502 return xstrdup("Moved Permanently");
00503 case SC_MOVED_TEMPORARILY:
00504 return xstrdup("Moved Temporarily");
00505 case SC_MULTIPLE_CHOICES:
00506 return xstrdup("Multiple Choices");
00507 case SC_NO_CONTENT:
00508 return xstrdup("No Content");
00509 case SC_NON_AUTHORITATIVE:
00510 return xstrdup("Non-Authoritative Information");
00511 case SC_NOT_ACCEPTABLE:
00512 return xstrdup("Not Acceptable");
00513 case SC_NOT_FOUND:
00514 return xstrdup("Not Found");
00515 case SC_NOT_IMPLEMENTED:
00516 return xstrdup("Not Implemented");
00517 case SC_NOT_MODIFIED:
00518 return xstrdup("Not Modified");
00519 case SC_PARTIAL_CONTENT:
00520 return xstrdup("Partial Content");
00521 case SC_PAYMENT_REQUIRED:
00522 return xstrdup("Payment Required");
00523 case SC_PRECONDITION_FAILED:
00524 return xstrdup("Precondition Failed");
00525 case SC_PROXY_AUTHENTICATION_REQUIRED:
00526 return xstrdup("Proxy Authentication Required");
00527 case SC_REQUEST_ENTITY_TOO_LARGE:
00528 return xstrdup("Request Entity Too Large");
00529 case SC_REQUEST_TIMEOUT:
00530 return xstrdup("Request Timeout");
00531 case SC_REQUEST_URI_TOO_LARGE:
00532 return xstrdup("Request URI Too Large");
00533 case SC_RANGE_NOT_SATISFIABLE:
00534 return xstrdup("Requested Range Not Satisfiable");
00535 case SC_RESET_CONTENT:
00536 return xstrdup("Reset Content");
00537 case SC_SEE_OTHER:
00538 return xstrdup("See Other");
00539 case SC_SERVICE_UNAVAILABLE:
00540 return xstrdup("Service Unavailable");
00541 case SC_SWITCHING_PROTOCOLS:
00542 return xstrdup("Switching Protocols");
00543 case SC_UNAUTHORIZED:
00544 return xstrdup("Unauthorized");
00545 case SC_UNSUPPORTED_MEDIA_TYPE:
00546 return xstrdup("Unsupported Media Type");
00547 case SC_USE_PROXY:
00548 return xstrdup("Use Proxy");
00549 default: {
00550 char buf[STRLEN];
00551 snprintf(buf, STRLEN, "HTTP Response Status %d", status);
00552 return xstrdup(buf);
00553 }
00554 }
00555
00556 }
00557
00558
00559
00560
00561
00566 static void do_service(RequestWrapper w) {
00567
00568 volatile HttpResponse res= create_HttpResponse(w);
00569 volatile HttpRequest req= create_HttpRequest(w);
00570
00571 if ( res && req ) {
00572
00573 if ( is_authenticated(req, res) ) {
00574
00575 if ( !strcmp(req->method, METHOD_GET) ) {
00576
00577 Impl.doGet(req, res);
00578
00579 }
00580 else if ( !strcmp(req->method, METHOD_POST) ) {
00581
00582 Impl.doPost(req, res);
00583
00584 }
00585 else {
00586
00587 send_error(res, SC_NOT_IMPLEMENTED, "Method not implemented");
00588
00589 }
00590
00591 }
00592
00593 if ( check_socket(w->socket) ) {
00594
00595 send_response(res);
00596
00597 }
00598
00599 }
00600
00601 done(req, res);
00602
00603 }
00604
00605
00609 static char *get_date() {
00610
00611 char date[STRLEN];
00612 time_t now;
00613
00614 time(&now);
00615
00616 if ( !strftime(date, STRLEN, DATEFMT, gmtime(&now)) ) {
00617
00618 memset(date, 0, STRLEN);
00619
00620 }
00621
00622 return xstrdup(date);
00623
00624 }
00625
00626
00630 static char *get_server() {
00631
00632 char server[STRLEN];
00633 snprintf(server, STRLEN, "%s %s", SERVER_NAME, SERVER_VERSION);
00634
00635 return xstrdup(server);
00636
00637 }
00638
00639
00644 static void send_response(HttpResponse res) {
00645
00646 if ( !res->is_committed ) {
00647
00648 FILE *out= res->outputstream;
00649 char *date= get_date();
00650 char *server= get_server();
00651 char *headers= get_headers(res);
00652 int len= res->bufused?strlen(res->outputbuffer):0;
00653
00654 fprintf(out, "%s %d %s\r\n", res->protocol, res->status, res->status_msg);
00655 fprintf(out, "Date: %s\r\n", date);
00656 fprintf(out, "Server: %s\r\n", server);
00657 fprintf(out, "Content-length: %d\r\n", len);
00658 fprintf(out, "Connection: close\r\n");
00659 if ( headers ) fprintf(out, "%s", headers);
00660 fprintf(out, "\r\n\r\n");
00661
00662 if ( res->bufused ) fprintf(out, "%s", res->outputbuffer);
00663
00664 free(headers);
00665 free(date);
00666 free(server);
00667
00668 }
00669
00670 }
00671
00672
00673
00674
00675
00679 static HttpRequest create_HttpRequest(RequestWrapper W) {
00680
00681 char line[REQ_STRLEN];
00682 char method[STRLEN];
00683 char url[REQ_STRLEN];
00684 char protocol[STRLEN];
00685 HttpRequest req;
00686 FILE *in= fdopen(dup(W->socket), "r");
00687
00688 if ( in == NULL ) {
00689
00690 internal_error(W->socket, SC_INTERNAL_SERVER_ERROR,
00691 "Cannot create inputstream");
00692 return NULL;
00693
00694 }
00695
00696 if ( fgets(line, sizeof(line), in) == NULL ) {
00697
00698 internal_error(W->socket, SC_BAD_REQUEST, "No request found");
00699 fclose(in);
00700 return NULL;
00701
00702 }
00703
00704 if ( sscanf(line, "%s %s HTTP/%3[1.0]", method, url, protocol) != 3 ) {
00705
00706 internal_error(W->socket, SC_BAD_REQUEST, "Cannot parse request");
00707 fclose(in);
00708 return NULL;
00709
00710 }
00711
00712 req= NEW(req);
00713 unescape_url(url);
00714 chomp(protocol);
00715 req->method= xstrdup(method);
00716 req->url= xstrdup(url);
00717 req->protocol= xstrdup(protocol);
00718 req->inputstream= in;
00719 req->inetaddr= W->inetaddr;
00720
00721 create_headers(req);
00722
00723 if ( !create_parameters(req) ) {
00724
00725 internal_error(W->socket, SC_BAD_REQUEST,
00726 "Cannot parse Request parameters");
00727 fclose(in);
00728 return NULL;
00729
00730 }
00731
00732 return req;
00733
00734 }
00735
00736
00741 static HttpResponse create_HttpResponse(RequestWrapper W) {
00742
00743 HttpResponse res;
00744 FILE *out= fdopen(dup(W->socket), "w");
00745
00746 if ( out == NULL ) {
00747
00748 internal_error(W->socket, SC_INTERNAL_SERVER_ERROR,
00749 "Cannot create outputstream");
00750 return NULL;
00751
00752 }
00753
00754 res= NEW(res);
00755 res->protocol= xstrdup(SERVER_PROTOCOL);
00756 res->status= 200;
00757 res->status_msg= get_status_string(SC_OK);
00758 res->outputstream= out;
00759 res->outputbuffer= NULL;
00760 res->bufsize= 0;
00761 res->bufused= 0;
00762 res->is_committed= FALSE;
00763
00764 return res;
00765
00766 }
00767
00768
00772 static void create_headers(HttpRequest req) {
00773
00774 char line[REQ_STRLEN];
00775 char *value;
00776 HttpHeader p;
00777 HttpHeader header;
00778
00779 while ( NULL != fgets(line, sizeof(line), req->inputstream) ) {
00780
00781 if ( !strcmp(line, "\r\n") || !strcmp(line, "\n") )
00782 break;
00783
00784 if( NULL != (value= strchr(line, ':')) ) {
00785
00786 header= NEW(header);
00787 *value++= '\0';
00788 trim(line);
00789 trim(value);
00790 chomp(value);
00791 header->name= xstrdup(line);
00792 header->value= xstrdup(value);
00793
00794 if ( req->headers ) {
00795
00796 for ( p= req->headers; p->next; p= p->next)
00797 ;
00798 p->next= header;
00799
00800 } else {
00801
00802 req->headers= header;
00803
00804 }
00805
00806 }
00807
00808 }
00809
00810 }
00811
00812
00817 static int create_parameters(HttpRequest req) {
00818
00819 char *query_string= NULL;
00820 int alloc= FALSE;
00821
00822 if ( !strcmp(req->method, METHOD_POST) ) {
00823
00824 int len; char *l= get_header(req, "Content-Length");
00825
00826 if ( l && (len= atoi(l)) ) {
00827
00828 query_string= xmalloc(sizeof(char) * (len + 1));
00829
00830 if ( fgets(query_string, (sizeof(char)*(len+1)),
00831 req->inputstream) == NULL ) {
00832
00833 free(query_string);
00834 return FALSE;
00835
00836 }
00837
00838 alloc= TRUE;
00839
00840 }
00841
00842 } else if ( !strcmp(req->method, METHOD_GET) ) {
00843
00844 char *p;
00845
00846 if( NULL != (p= strchr(req->url, '?')) ) {
00847
00848 *p++= '\0';
00849 query_string= p;
00850
00851 }
00852
00853 }
00854
00855 if ( query_string ) {
00856
00857 char *p;
00858
00859 if( NULL != (p= strchr(query_string, '/')) ) {
00860
00861 *p++= '\0';
00862 req->pathinfo= p;
00863
00864 }
00865
00866 req->params= parse_parameters(query_string);
00867
00868 }
00869
00870 if ( alloc ) free(query_string);
00871
00872 return TRUE;
00873
00874 }
00875
00876
00877
00878
00879
00880
00884 static void reset_response(HttpResponse res) {
00885
00886 if ( res->headers ) destroy_entry(res->headers);
00887 memset(res->outputbuffer, 0, res->bufsize);
00888 res->headers= NULL;
00889
00890 }
00891
00892
00896 static void done(HttpRequest req, HttpResponse res) {
00897
00898 destroy_HttpRequest(req);
00899 destroy_HttpResponse(res);
00900
00901 }
00902
00903
00907 static void destroy_HttpRequest(HttpRequest req) {
00908
00909 if ( req ) {
00910
00911 if ( req->inputstream ) fclose(req->inputstream);
00912 free(req->method);
00913 free(req->url);
00914 free(req->protocol);
00915 if ( req->headers ) destroy_entry(req->headers);
00916 if ( req->params ) destroy_entry(req->params);
00917 free(req);
00918
00919 }
00920
00921 }
00922
00923
00927 static void destroy_HttpResponse(HttpResponse res) {
00928
00929 if ( res ) {
00930
00931 if ( res->outputstream ) fclose(res->outputstream);
00932 free(res->protocol);
00933 free(res->status_msg);
00934 free(res->outputbuffer);
00935 if ( res->headers) destroy_entry(res->headers);
00936 free(res);
00937
00938 }
00939
00940 }
00941
00942
00947 static void destroy_entry(void *p) {
00948
00949 struct entry *h= p;
00950
00951 if ( h->next ) {
00952
00953 destroy_entry(h->next);
00954
00955 }
00956
00957 free(h->name);
00958 free(h->value);
00959 free(h);
00960
00961 }
00962
00963
00964
00965
00966
00971 static int do_timeout(int client) {
00972
00973 set_alarm_handler(request_timeout);
00974
00975 alarm(REQUEST_TIMEOUT);
00976
00977 if ( sigsetjmp(timeout, TRUE) ) {
00978
00979 internal_error(client, SC_REQUEST_TIMEOUT,
00980 "Time out when handling the Request");
00981
00982 return TRUE;
00983
00984 }
00985
00986 return FALSE;
00987
00988 }
00989
00990
00994 static int is_authenticated(HttpRequest req, HttpResponse res) {
00995
00996 if ( Run.Auth.defined ) {
00997
00998 if ( ! basic_authenticate(req) ) {
00999
01000 send_error(res, SC_UNAUTHORIZED,
01001 "You are <b>not</b> authorized to access <i>monit</i>. "
01002 "Either you supplied the wrong credentials (e.g. bad "
01003 "password), or your browser doesn't understand how to supply "
01004 "the credentials required");
01005 set_header(res, "WWW-Authenticate", "Basic realm=\"monit\"");
01006
01007 return FALSE;
01008
01009 }
01010
01011 }
01012
01013 return TRUE;
01014
01015 }
01016
01017
01022 static int basic_authenticate(HttpRequest req) {
01023
01024 int rv= FALSE;
01025 char *credentials= get_header(req, "Authorization");
01026
01027 if ( credentials ) {
01028
01029 unsigned char *cr= xcalloc(sizeof(unsigned char), strlen(credentials)+1);
01030
01031 if ( decode_base64(cr, strchr(credentials, ' ')) ) {
01032
01033 char buf[STRLEN];
01034
01035 snprintf(buf, STRLEN, "%s:%s", Run.Auth.uname, Run.Auth.passwd);
01036
01037 if ( !strncmp(cr, buf, strlen(buf)) ) {
01038
01039 rv= TRUE;
01040
01041 }
01042
01043 }
01044
01045 free(cr);
01046 free(credentials);
01047
01048 }
01049
01050 return rv;
01051
01052 }
01053
01054
01055
01056
01057
01063 static void internal_error(int client, int status, char *msg) {
01064
01065 char response[RES_STRLEN];
01066 char *server= get_server();
01067 char *date= get_date();
01068 char *status_msg= get_status_string(status);
01069
01070 snprintf(response, RES_STRLEN,
01071 "%s %d %s\r\n"
01072 "Date: %s\r\n"
01073 "Server: %s\r\n"
01074 "Content-Type: text/html\r\n"
01075 "Connection: close\r\n"
01076 "\r\n"
01077 "<html><head><title>%s</title></head>"
01078 "<body bgcolor=#FFFFFF><h2>%s</h2>%s<p>"
01079 "<hr><a href='%s'><font size=-1>%s</font></a>"
01080 "</body></html>\r\n",
01081 SERVER_PROTOCOL, status, status_msg, date, server,
01082 status_msg, status_msg, msg, SERVER_URL, server);
01083 sock_send(client, response, strlen(response), 0);
01084 free(server);
01085 free(date);
01086 free(status_msg);
01087
01088 }
01089
01090
01095 static HttpParameter parse_parameters(char *query_string) {
01096
01097 HttpParameter head= NULL;
01098 int controller= 1000;
01099
01100 while ( query_string[0] && controller-- ) {
01101
01102 HttpParameter p= NEW(p);
01103 p->value= makeword(query_string,'&');
01104 p->name= makeword(p->value,'=');
01105 plustospace(p->value);
01106 unescape_url(p->value);
01107 plustospace(p->name);
01108 unescape_url(p->name);
01109 p->next= head;
01110 head= p;
01111
01112 }
01113
01114 return head;
01115
01116 }
01117
01118
01122 static void request_timeout(int sig) {
01123
01124 siglongjmp(timeout, TRUE);
01125
01126 }
01127