root/src/noit_rest.c

Revision 6de024fe1a245773c4b8c107f9063f2f40e5c344, 19.4 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 4 years ago)

updates... yay! for test suites... all passes, refs #334

  • Property mode set to 100644
Line 
1 /*
2  * Copyright (c) 2007, OmniTI Computer Consulting, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  *     * Redistributions of source code must retain the above copyright
10  *       notice, this list of conditions and the following disclaimer.
11  *     * Redistributions in binary form must reproduce the above
12  *       copyright notice, this list of conditions and the following
13  *       disclaimer in the documentation and/or other materials provided
14  *       with the distribution.
15  *     * Neither the name OmniTI Computer Consulting, Inc. nor the names
16  *       of its contributors may be used to endorse or promote products
17  *       derived from this software without specific prior written
18  *       permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 #include "noit_defines.h"
34 #include "noit_listener.h"
35 #include "noit_http.h"
36 #include "noit_rest.h"
37 #include "noit_conf.h"
38
39 #include <pcre.h>
40 #include <unistd.h>
41 #include <fcntl.h>
42 #include <sys/mman.h>
43 #include <errno.h>
44
45 struct rest_xml_payload {
46   char *buffer;
47   xmlDocPtr indoc;
48   int len;
49   int allocd;
50   int complete;
51 };
52
53 struct rest_url_dispatcher {
54   char *method;
55   pcre *expression;
56   pcre_extra *extra;
57   rest_request_handler handler;
58   rest_authorize_func_t auth;
59   /* Chain to the next one */
60   struct rest_url_dispatcher *next;
61 };
62
63 struct rule_container {
64   char *base;
65   struct rest_url_dispatcher *rules;
66   struct rest_url_dispatcher *rules_endptr;
67 };
68 noit_hash_table dispatch_points = NOIT_HASH_EMPTY;
69
70 struct noit_rest_acl_rule {
71   noit_boolean allow;
72   pcre *url;
73   pcre *cn;
74   struct noit_rest_acl_rule *next;
75 };
76 struct noit_rest_acl {
77   noit_boolean allow;
78   pcre *url;
79   pcre *cn;
80   struct noit_rest_acl_rule *rules;
81   struct noit_rest_acl *next;
82 };
83
84 static struct noit_rest_acl *global_rest_acls = NULL;
85
86 static int
87 noit_http_rest_permission_denied(noit_http_rest_closure_t *restc,
88                                  int npats, char **pats) {
89   noit_http_session_ctx *ctx = restc->http_ctx;
90   noit_http_response_standard(ctx, 403, "DENIED", "text/xml");
91   noit_http_response_end(ctx);
92   return 0;
93 }
94 static rest_request_handler
95 noit_http_get_handler(noit_http_rest_closure_t *restc) {
96   struct rule_container *cont = NULL;
97   struct rest_url_dispatcher *rule;
98   noit_http_request *req = noit_http_session_request(restc->http_ctx);
99   const char *uri_str;
100   const char *eoq, *eob;
101   uri_str = noit_http_request_uri_str(req);
102   eoq = strchr(uri_str, '?');
103   if(!eoq)
104     eoq = uri_str + strlen(uri_str);
105   eob = eoq - 1;
106
107   /* find the right base */
108   while(1) {
109     void *vcont;
110     while(eob >= uri_str && *eob != '/') eob--;
111     if(eob < uri_str) break; /* off the front */
112     if(noit_hash_retrieve(&dispatch_points, uri_str,
113                           eob - uri_str + 1, &vcont)) {
114       cont = vcont;
115       eob++; /* move past the determined base */
116       break;
117     }
118     eob--;
119   }
120   if(!cont) return NULL;
121   for(rule = cont->rules; rule; rule = rule->next) {
122     int ovector[30];
123     int cnt;
124     if(strcmp(rule->method, noit_http_request_method_str(req))) continue;
125     if((cnt = pcre_exec(rule->expression, rule->extra, eob, eoq - eob, 0, 0,
126                         ovector, sizeof(ovector)/sizeof(*ovector))) > 0) {
127       /* We match, set 'er up */
128       restc->fastpath = rule->handler;
129       restc->nparams = cnt - 1;
130       if(restc->nparams) {
131         restc->params = calloc(restc->nparams, sizeof(*restc->params));
132         for(cnt = 0; cnt < restc->nparams; cnt++) {
133           int start = ovector[(cnt+1)*2];
134           int end = ovector[(cnt+1)*2+1];
135           restc->params[cnt] = malloc(end - start + 1);
136           memcpy(restc->params[cnt], eob + start, end - start);
137           restc->params[cnt][end - start] = '\0';
138         }
139       }
140       if(rule->auth && !rule->auth(restc, restc->nparams, restc->params))
141         return noit_http_rest_permission_denied;
142       return restc->fastpath;
143     }
144   }
145   return NULL;
146 }
147 noit_boolean
148 noit_http_rest_access(noit_http_rest_closure_t *restc,
149                       int npats, char **pats) {
150   struct noit_rest_acl *acl;
151   struct noit_rest_acl_rule *rule;
152   noit_http_request *req = noit_http_session_request(restc->http_ctx);
153   const char *uri_str;
154   int ovector[30];
155
156   uri_str = noit_http_request_uri_str(req);
157   for(acl = global_rest_acls; acl; acl = acl->next) {
158     if(acl->cn && pcre_exec(acl->cn, NULL, "", 0, 0, 0,
159                             ovector, sizeof(ovector)/sizeof(*ovector)) <= 0)
160       continue;
161     if(acl->url && pcre_exec(acl->url, NULL, uri_str, strlen(uri_str), 0, 0,
162                              ovector, sizeof(ovector)/sizeof(*ovector)) <= 0)
163       continue;
164     for(rule = acl->rules; rule; rule = rule->next) {
165       if(rule->cn && pcre_exec(rule->cn, NULL, "", 0, 0, 0,
166                                ovector, sizeof(ovector)/sizeof(*ovector)) <= 0)
167         continue;
168       if(rule->url && pcre_exec(rule->url, NULL, uri_str, strlen(uri_str), 0, 0,
169                                 ovector, sizeof(ovector)/sizeof(*ovector)) <= 0)
170         continue;
171       return rule->allow;
172     }
173     return acl->allow;
174   }
175   return noit_false;
176 }
177 noit_boolean
178 noit_http_rest_client_cert_auth(noit_http_rest_closure_t *restc,
179                                 int npats, char **pats) {
180   struct noit_rest_acl *acl;
181   struct noit_rest_acl_rule *rule;
182   noit_http_request *req = noit_http_session_request(restc->http_ctx);
183   const char *uri_str;
184   int ovector[30];
185
186   uri_str = noit_http_request_uri_str(req);
187   if(!restc->remote_cn || !strlen(restc->remote_cn)) return noit_false;
188   for(acl = global_rest_acls; acl; acl = acl->next) {
189     if(acl->cn && pcre_exec(acl->cn, NULL, restc->remote_cn,
190                             strlen(restc->remote_cn), 0, 0,
191                             ovector, sizeof(ovector)/sizeof(*ovector)) <= 0)
192       continue;
193     if(acl->url && pcre_exec(acl->url, NULL, uri_str, strlen(uri_str), 0, 0,
194                              ovector, sizeof(ovector)/sizeof(*ovector)) <= 0)
195       continue;
196     for(rule = acl->rules; rule; rule = rule->next) {
197       if(rule->cn && pcre_exec(rule->cn, NULL, restc->remote_cn,
198                                strlen(restc->remote_cn), 0, 0,
199                                ovector, sizeof(ovector)/sizeof(*ovector)) <= 0)
200         continue;
201       if(rule->url && pcre_exec(rule->url, NULL, uri_str, strlen(uri_str), 0, 0,
202                                 ovector, sizeof(ovector)/sizeof(*ovector)) <= 0)
203         continue;
204       return rule->allow;
205     }
206     return acl->allow;
207   }
208   return noit_false;
209 }
210 int
211 noit_http_rest_register(const char *method, const char *base,
212                         const char *expr, rest_request_handler f) {
213   return noit_http_rest_register_auth(method, base, expr, f, NULL);
214 }
215 int
216 noit_http_rest_register_auth(const char *method, const char *base,
217                              const char *expr, rest_request_handler f,
218                              rest_authorize_func_t auth) {
219   void *vcont;
220   struct rule_container *cont;
221   struct rest_url_dispatcher *rule;
222   const char *error;
223   int erroffset;
224   pcre *pcre_expr;
225   int blen = strlen(base);
226   /* base must end in a /, 'cause I said so */
227   if(blen == 0 || base[blen-1] != '/') return -1;
228   pcre_expr = pcre_compile(expr, 0, &error, &erroffset, NULL);
229   if(!pcre_expr) {
230     noitL(noit_error, "Error in rest expr(%s) '%s'@%d: %s\n",
231           base, expr, erroffset, error);
232     return -1;
233   }
234   rule = calloc(1, sizeof(*rule));
235   rule->method = strdup(method);
236   rule->expression = pcre_expr;
237   rule->extra = pcre_study(rule->expression, 0, &error);
238   rule->handler = f;
239   rule->auth = auth;
240
241   /* Make sure we have a container */
242   if(!noit_hash_retrieve(&dispatch_points, base, strlen(base), &vcont)) {
243     cont = calloc(1, sizeof(*cont));
244     cont->base = strdup(base);
245     noit_hash_store(&dispatch_points, cont->base, strlen(cont->base), cont);
246   }
247   else cont = vcont;
248
249   /* Append the rule */
250   if(cont->rules_endptr) {
251     cont->rules_endptr->next = rule;
252     cont->rules_endptr = cont->rules_endptr->next;
253   }
254   else
255     cont->rules = cont->rules_endptr = rule;
256   return 0;
257 }
258
259 static noit_http_rest_closure_t *
260 noit_http_rest_closure_alloc() {
261   noit_http_rest_closure_t *restc;
262   restc = calloc(1, sizeof(*restc));
263   return restc;
264 }
265 static void
266 noit_http_rest_clean_request(noit_http_rest_closure_t *restc) {
267   int i;
268   if(restc->nparams) {
269     for(i=0;i<restc->nparams;i++) free(restc->params[i]);
270     free(restc->params);
271   }
272   if(restc->call_closure_free) restc->call_closure_free(restc->call_closure);
273   restc->call_closure_free = NULL;
274   restc->call_closure = NULL;
275   restc->nparams = 0;
276   restc->params = NULL;
277   restc->fastpath = NULL;
278 }
279 void
280 noit_http_rest_closure_free(void *v) {
281   noit_http_rest_closure_t *restc = v;
282   free(restc->remote_cn);
283   noit_http_rest_clean_request(restc);
284   free(restc);
285 }
286
287 int
288 noit_rest_request_dispatcher(noit_http_session_ctx *ctx) {
289   noit_http_rest_closure_t *restc = noit_http_session_dispatcher_closure(ctx);
290   rest_request_handler handler = restc->fastpath;
291   if(!handler) handler = noit_http_get_handler(restc);
292   if(handler) {
293     noit_http_response *res = noit_http_session_response(ctx);
294     int rv;
295     rv = handler(restc, restc->nparams, restc->params);
296     if(noit_http_response_closed(res)) noit_http_rest_clean_request(restc);
297     return rv;
298   }
299   noit_http_response_status_set(ctx, 404, "NOT FOUND");
300   noit_http_response_option_set(ctx, NOIT_HTTP_CHUNKED);
301   noit_http_rest_clean_request(restc);
302   noit_http_response_end(ctx);
303   return 0;
304 }
305
306 int
307 noit_http_rest_handler(eventer_t e, int mask, void *closure,
308                        struct timeval *now) {
309   int newmask = EVENTER_READ | EVENTER_EXCEPTION, rv, done = 0;
310   acceptor_closure_t *ac = closure;
311   noit_http_rest_closure_t *restc = ac->service_ctx;
312
313   if(mask & EVENTER_EXCEPTION || (restc && restc->wants_shutdown)) {
314 socket_error:
315     /* Exceptions cause us to simply snip the connection */
316     eventer_remove_fd(e->fd);
317     e->opset->close(e->fd, &newmask, e);
318     if(ac) acceptor_closure_free(ac);
319     return 0;
320   }
321
322   if(!ac->service_ctx) {
323     const char *primer = "";
324     ac->service_ctx = restc = noit_http_rest_closure_alloc();
325     ac->service_ctx_free = noit_http_rest_closure_free;
326     restc->ac = ac;
327     restc->remote_cn = strdup(ac->remote_cn ? ac->remote_cn : "");
328     restc->http_ctx =
329         noit_http_session_ctx_new(noit_rest_request_dispatcher,
330                                   restc, e, ac);
331    
332     switch(ac->cmd) {
333       case NOIT_CONTROL_DELETE:
334         primer = "DELE";
335         break;
336       case NOIT_CONTROL_GET:
337         primer = "GET ";
338         break;
339       case NOIT_CONTROL_HEAD:
340         primer = "HEAD";
341         break;
342       case NOIT_CONTROL_POST:
343         primer = "POST";
344         break;
345       case NOIT_CONTROL_PUT:
346         primer = "PUT ";
347         break;
348       case NOIT_CONTROL_MERGE:
349         primer = "MERG";
350         break;
351       default:
352         goto socket_error;
353     }
354     noit_http_session_prime_input(restc->http_ctx, primer, 4);
355   }
356   rv = noit_http_session_drive(e, mask, restc->http_ctx, now, &done);
357   if(done) {
358     if(ac) acceptor_closure_free(ac);
359   }
360   return rv;
361 }
362
363 int
364 noit_http_rest_raw_handler(eventer_t e, int mask, void *closure,
365                            struct timeval *now) {
366   int newmask = EVENTER_READ | EVENTER_EXCEPTION, rv, done = 0;
367   acceptor_closure_t *ac = closure;
368   noit_http_rest_closure_t *restc = ac->service_ctx;
369
370   if(mask & EVENTER_EXCEPTION || (restc && restc->wants_shutdown)) {
371     /* Exceptions cause us to simply snip the connection */
372     eventer_remove_fd(e->fd);
373     e->opset->close(e->fd, &newmask, e);
374     if(ac) acceptor_closure_free(ac);
375     return 0;
376   }
377   if(!ac->service_ctx) {
378     ac->service_ctx = restc = noit_http_rest_closure_alloc();
379     ac->service_ctx_free = noit_http_rest_closure_free;
380     restc->ac = ac;
381     restc->http_ctx =
382         noit_http_session_ctx_new(noit_rest_request_dispatcher,
383                                   restc, e, ac);
384   }
385   rv = noit_http_session_drive(e, mask, restc->http_ctx, now, &done);
386   if(done) {
387     if(ac) acceptor_closure_free(ac);
388   }
389   return rv;
390 }
391
392 static void
393 rest_xml_payload_free(void *f) {
394   struct rest_xml_payload *xmlin = f;
395   if(xmlin->buffer) free(xmlin->buffer);
396   if(xmlin->indoc) xmlFreeDoc(xmlin->indoc);
397 }
398
399 xmlDocPtr
400 rest_get_xml_upload(noit_http_rest_closure_t *restc,
401                     int *mask, int *complete) {
402   struct rest_xml_payload *rxc;
403   noit_http_request *req = noit_http_session_request(restc->http_ctx);
404
405   if(restc->call_closure == NULL) {
406     restc->call_closure = calloc(1, sizeof(*rxc));
407     restc->call_closure_free = rest_xml_payload_free;
408   }
409   rxc = restc->call_closure;
410   while(!rxc->complete) {
411     int len;
412     if(rxc->len == rxc->allocd) {
413       char *b;
414       rxc->allocd += 32768;
415       b = rxc->buffer ? realloc(rxc->buffer, rxc->allocd) :
416                         malloc(rxc->allocd);
417       if(!b) {
418         *complete = 1;
419         return NULL;
420       }
421       rxc->buffer = b;
422     }
423     len = noit_http_session_req_consume(restc->http_ctx,
424                                         rxc->buffer + rxc->len,
425                                         rxc->allocd - rxc->len,
426                                         mask);
427     if(len > 0) rxc->len += len;
428     if(len < 0 && errno == EAGAIN) return NULL;
429     else if(len < 0) {
430       *complete = 1;
431       return NULL;
432     }
433     if(rxc->len == noit_http_request_content_length(req)) {
434       rxc->indoc = xmlParseMemory(rxc->buffer, rxc->len);
435       rxc->complete = 1;
436     }
437   }
438
439   *complete = 1;
440   return rxc->indoc;
441 }
442 int
443 noit_rest_simple_file_handler(noit_http_rest_closure_t *restc,
444                               int npats, char **pats) {
445   int drlen = 0;
446   const char *document_root = NULL;
447   const char *index_file = NULL;
448   noit_http_session_ctx *ctx = restc->http_ctx;
449   char file[PATH_MAX], rfile[PATH_MAX];
450   struct stat st;
451   int fd;
452   void *contents = MAP_FAILED;
453
454   if(npats != 1 ||
455      !noit_hash_retr_str(restc->ac->config,
456                          "document_root", strlen("document_root"),
457                          &document_root)) {
458     goto not_found;
459   }
460   if(!noit_hash_retr_str(restc->ac->config,
461                          "index_file", strlen("index_file"),
462                          &index_file)) {
463     index_file = "index.html";
464   }
465   drlen = strlen(document_root);
466   snprintf(file, sizeof(file), "%s/%s", document_root, pats[0]);
467   if(file[strlen(file) - 1] == '/') {
468     snprintf(file + strlen(file), sizeof(file) - strlen(file),
469              "%s", index_file);
470   }
471   /* resolve */
472   if(realpath(file, rfile) == NULL) goto not_found;
473   /* restrict */
474   if(strncmp(rfile, document_root, drlen)) goto denied;
475   if(rfile[drlen] != '/' && rfile[drlen + 1] != '/') goto denied;
476   /* stat */
477   if(stat(rfile, &st) != 0) {
478     switch (errno) {
479       case EACCES: goto denied;
480       default: goto not_found;
481     }
482   }
483   /* open */
484   if(st.st_size > 0) {
485     fd = open(rfile, O_RDONLY);
486     if(fd < 0) goto not_found;
487     contents = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
488     close(fd);
489     if(contents == MAP_FAILED) goto not_found;
490   }
491   noit_http_response_ok(ctx, "text/html");
492   if(st.st_size > 0) {
493     noit_http_response_append(ctx, contents, st.st_size);
494     munmap(contents, st.st_size);
495   }
496   noit_http_response_end(ctx);
497   return 0;
498
499  denied:
500   noit_http_response_denied(ctx, "text/html");
501   noit_http_response_end(ctx);
502   return 0;
503  not_found:
504   noit_http_response_not_found(ctx, "text/html");
505   noit_http_response_end(ctx);
506   return 0;
507 }
508
509 void noit_http_rest_load_rules() {
510   int ai, cnt = 0;
511   noit_conf_section_t *acls;
512   char path[256];
513   struct noit_rest_acl *newhead = NULL, *oldacls, *remove_acl;
514   struct noit_rest_acl_rule *remove_rule;
515
516   snprintf(path, sizeof(path), "//rest//acl");
517   acls = noit_conf_get_sections(NULL, path, &cnt);
518   noitL(noit_stderr, "Found %d acl stanzas\n", cnt);
519   for(ai = cnt-1; ai>=0; ai--) {
520     char tbuff[32];
521     struct noit_rest_acl *newacl;
522     int ri, rcnt = 0;
523     noit_boolean default_allow = noit_false;
524     noit_conf_section_t *rules;
525
526     newacl = calloc(1, sizeof(*newacl));
527     newacl->next = newhead;
528     newhead = newacl;
529     if(noit_conf_get_stringbuf(acls[ai], "@type", tbuff, sizeof(tbuff)) &&
530        !strcmp(tbuff, "allow"))
531       newacl->allow = noit_true;
532
533 #define compile_re(node, cont, name) do { \
534   char buff[256]; \
535   if(noit_conf_get_stringbuf(node, "@" #name, buff, sizeof(buff))) { \
536     const char *error; \
537     int erroffset; \
538     cont->name = pcre_compile(buff, 0, &error, &erroffset, NULL); \
539   } \
540 } while(0)
541
542     newacl->allow = default_allow;
543     compile_re(acls[ai], newacl, cn);
544     compile_re(acls[ai], newacl, url);
545     rules = noit_conf_get_sections(acls[ai], "rule", &rcnt);
546     for(ri = rcnt - 1; ri >= 0; ri--) {
547       struct noit_rest_acl_rule *newacl_rule;
548       newacl_rule = calloc(1, sizeof(*newacl_rule));
549       newacl_rule->next = newacl->rules;
550       newacl->rules = newacl_rule;
551       if(noit_conf_get_stringbuf(rules[ri], "@type", tbuff, sizeof(tbuff)) &&
552          !strcmp(tbuff, "allow"))
553         newacl_rule->allow = noit_true;
554       compile_re(rules[ri], newacl_rule, cn);
555       compile_re(rules[ri], newacl_rule, url);
556     }
557     free(rules);
558   }
559   free(acls);
560
561   oldacls = global_rest_acls;
562   global_rest_acls = newhead;
563
564   while(oldacls) {
565     remove_acl = oldacls->next;
566     while(oldacls->rules) {
567       remove_rule = oldacls->rules->next;
568       if(oldacls->rules->cn) pcre_free(oldacls->rules->cn);
569       if(oldacls->rules->url) pcre_free(oldacls->rules->url);
570       free(oldacls->rules);
571       oldacls->rules = remove_rule;
572     }
573     if(oldacls->cn) pcre_free(oldacls->cn);
574     if(oldacls->url) pcre_free(oldacls->url);
575     free(oldacls);
576     oldacls = remove_acl;
577   }
578 }
579 void noit_http_rest_init() {
580   noit_http_init();
581   eventer_name_callback("noit_wire_rest_api/1.0", noit_http_rest_handler);
582   eventer_name_callback("http_rest_api", noit_http_rest_raw_handler);
583
584   noit_http_rest_load_rules();
585
586   noit_control_dispatch_delegate(noit_control_dispatch,
587                                  NOIT_CONTROL_DELETE,
588                                  noit_http_rest_handler);
589   noit_control_dispatch_delegate(noit_control_dispatch,
590                                  NOIT_CONTROL_MERGE,
591                                  noit_http_rest_handler);
592   noit_control_dispatch_delegate(noit_control_dispatch,
593                                  NOIT_CONTROL_GET,
594                                  noit_http_rest_handler);
595   noit_control_dispatch_delegate(noit_control_dispatch,
596                                  NOIT_CONTROL_HEAD,
597                                  noit_http_rest_handler);
598   noit_control_dispatch_delegate(noit_control_dispatch,
599                                  NOIT_CONTROL_POST,
600                                  noit_http_rest_handler);
601   noit_control_dispatch_delegate(noit_control_dispatch,
602                                  NOIT_CONTROL_PUT,
603                                  noit_http_rest_handler);
604 }
605
Note: See TracBrowser for help on using the browser.