root/src/noit_rest.c

Revision 4778e9c3ac3f3b976f6d6d47720c5968e552d316, 19.0 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 4 years ago)

add MERGE support into the rest driver, fixes #271

  • 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   char *eoq, *eob;
99   eoq = strchr(restc->http_ctx->req.uri_str, '?');
100   if(!eoq)
101     eoq = restc->http_ctx->req.uri_str + strlen(restc->http_ctx->req.uri_str);
102   eob = eoq - 1;
103
104   /* find the right base */
105   while(1) {
106     void *vcont;
107     while(eob >= restc->http_ctx->req.uri_str && *eob != '/') eob--;
108     if(eob < restc->http_ctx->req.uri_str) break; /* off the front */
109     if(noit_hash_retrieve(&dispatch_points, restc->http_ctx->req.uri_str,
110                           eob - restc->http_ctx->req.uri_str + 1, &vcont)) {
111       cont = vcont;
112       eob++; /* move past the determined base */
113       break;
114     }
115     eob--;
116   }
117   if(!cont) return NULL;
118   for(rule = cont->rules; rule; rule = rule->next) {
119     int ovector[30];
120     int cnt;
121     if(strcmp(rule->method, restc->http_ctx->req.method_str)) continue;
122     if((cnt = pcre_exec(rule->expression, rule->extra, eob, eoq - eob, 0, 0,
123                         ovector, sizeof(ovector)/sizeof(*ovector))) > 0) {
124       /* We match, set 'er up */
125       restc->fastpath = rule->handler;
126       restc->nparams = cnt - 1;
127       if(restc->nparams) {
128         restc->params = calloc(restc->nparams, sizeof(*restc->params));
129         for(cnt = 0; cnt < restc->nparams; cnt++) {
130           int start = ovector[(cnt+1)*2];
131           int end = ovector[(cnt+1)*2+1];
132           restc->params[cnt] = malloc(end - start + 1);
133           memcpy(restc->params[cnt], eob + start, end - start);
134           restc->params[cnt][end - start] = '\0';
135         }
136       }
137       if(rule->auth && !rule->auth(restc, restc->nparams, restc->params))
138         return noit_http_rest_permission_denied;
139       return restc->fastpath;
140     }
141   }
142   return NULL;
143 }
144 noit_boolean
145 noit_http_rest_access(noit_http_rest_closure_t *restc,
146                       int npats, char **pats) {
147   struct noit_rest_acl *acl;
148   struct noit_rest_acl_rule *rule;
149   int ovector[30];
150
151   for(acl = global_rest_acls; acl; acl = acl->next) {
152     if(acl->cn && pcre_exec(acl->cn, NULL, "", 0, 0, 0,
153                             ovector, sizeof(ovector)/sizeof(*ovector)) <= 0)
154       continue;
155     if(acl->url && pcre_exec(acl->url, NULL, restc->http_ctx->req.uri_str,
156                              strlen(restc->http_ctx->req.uri_str), 0, 0,
157                              ovector, sizeof(ovector)/sizeof(*ovector)) <= 0)
158       continue;
159     for(rule = acl->rules; rule; rule = rule->next) {
160       if(rule->cn && pcre_exec(rule->cn, NULL, "", 0, 0, 0,
161                                ovector, sizeof(ovector)/sizeof(*ovector)) <= 0)
162         continue;
163       if(rule->url && pcre_exec(rule->url, NULL, restc->http_ctx->req.uri_str,
164                                 strlen(restc->http_ctx->req.uri_str), 0, 0,
165                                 ovector, sizeof(ovector)/sizeof(*ovector)) <= 0)
166         continue;
167       return rule->allow;
168     }
169     return acl->allow;
170   }
171   return noit_false;
172 }
173 noit_boolean
174 noit_http_rest_client_cert_auth(noit_http_rest_closure_t *restc,
175                                 int npats, char **pats) {
176   struct noit_rest_acl *acl;
177   struct noit_rest_acl_rule *rule;
178   int ovector[30];
179
180   if(!restc->remote_cn || !strlen(restc->remote_cn)) return noit_false;
181   for(acl = global_rest_acls; acl; acl = acl->next) {
182     if(acl->cn && pcre_exec(acl->cn, NULL, restc->remote_cn,
183                             strlen(restc->remote_cn), 0, 0,
184                             ovector, sizeof(ovector)/sizeof(*ovector)) <= 0)
185       continue;
186     if(acl->url && pcre_exec(acl->url, NULL, restc->http_ctx->req.uri_str,
187                              strlen(restc->http_ctx->req.uri_str), 0, 0,
188                              ovector, sizeof(ovector)/sizeof(*ovector)) <= 0)
189       continue;
190     for(rule = acl->rules; rule; rule = rule->next) {
191       if(rule->cn && pcre_exec(rule->cn, NULL, restc->remote_cn,
192                                strlen(restc->remote_cn), 0, 0,
193                                ovector, sizeof(ovector)/sizeof(*ovector)) <= 0)
194         continue;
195       if(rule->url && pcre_exec(rule->url, NULL, restc->http_ctx->req.uri_str,
196                                 strlen(restc->http_ctx->req.uri_str), 0, 0,
197                                 ovector, sizeof(ovector)/sizeof(*ovector)) <= 0)
198         continue;
199       return rule->allow;
200     }
201     return acl->allow;
202   }
203   return noit_false;
204 }
205 int
206 noit_http_rest_register(const char *method, const char *base,
207                         const char *expr, rest_request_handler f) {
208   return noit_http_rest_register_auth(method, base, expr, f, NULL);
209 }
210 int
211 noit_http_rest_register_auth(const char *method, const char *base,
212                              const char *expr, rest_request_handler f,
213                              rest_authorize_func_t auth) {
214   void *vcont;
215   struct rule_container *cont;
216   struct rest_url_dispatcher *rule;
217   const char *error;
218   int erroffset;
219   pcre *pcre_expr;
220   int blen = strlen(base);
221   /* base must end in a /, 'cause I said so */
222   if(blen == 0 || base[blen-1] != '/') return -1;
223   pcre_expr = pcre_compile(expr, 0, &error, &erroffset, NULL);
224   if(!pcre_expr) {
225     noitL(noit_error, "Error in rest expr(%s) '%s'@%d: %s\n",
226           base, expr, erroffset, error);
227     return -1;
228   }
229   rule = calloc(1, sizeof(*rule));
230   rule->method = strdup(method);
231   rule->expression = pcre_expr;
232   rule->extra = pcre_study(rule->expression, 0, &error);
233   rule->handler = f;
234   rule->auth = auth;
235
236   /* Make sure we have a container */
237   if(!noit_hash_retrieve(&dispatch_points, base, strlen(base), &vcont)) {
238     cont = calloc(1, sizeof(*cont));
239     cont->base = strdup(base);
240     noit_hash_store(&dispatch_points, cont->base, strlen(cont->base), cont);
241   }
242   else cont = vcont;
243
244   /* Append the rule */
245   if(cont->rules_endptr) {
246     cont->rules_endptr->next = rule;
247     cont->rules_endptr = cont->rules_endptr->next;
248   }
249   else
250     cont->rules = cont->rules_endptr = rule;
251   return 0;
252 }
253
254 static noit_http_rest_closure_t *
255 noit_http_rest_closure_alloc() {
256   noit_http_rest_closure_t *restc;
257   restc = calloc(1, sizeof(*restc));
258   return restc;
259 }
260 static void
261 noit_http_rest_clean_request(noit_http_rest_closure_t *restc) {
262   int i;
263   if(restc->nparams) {
264     for(i=0;i<restc->nparams;i++) free(restc->params[i]);
265     free(restc->params);
266   }
267   if(restc->call_closure_free) restc->call_closure_free(restc->call_closure);
268   restc->call_closure_free = NULL;
269   restc->call_closure = NULL;
270   restc->nparams = 0;
271   restc->params = NULL;
272   restc->fastpath = NULL;
273 }
274 void
275 noit_http_rest_closure_free(noit_http_rest_closure_t *restc) {
276   free(restc->remote_cn);
277   noit_http_rest_clean_request(restc);
278   free(restc);
279 }
280
281 int
282 noit_rest_request_dispatcher(noit_http_session_ctx *ctx) {
283   noit_http_rest_closure_t *restc = ctx->dispatcher_closure;
284   rest_request_handler handler = restc->fastpath;
285   if(!handler) handler = noit_http_get_handler(restc);
286   if(handler) {
287     int rv;
288     rv = handler(restc, restc->nparams, restc->params);
289     if(ctx->res.closed) noit_http_rest_clean_request(restc);
290     return rv;
291   }
292   noit_http_response_status_set(ctx, 404, "NOT FOUND");
293   noit_http_response_option_set(ctx, NOIT_HTTP_CHUNKED);
294   noit_http_rest_clean_request(restc);
295   noit_http_response_end(ctx);
296   return 0;
297 }
298
299 int
300 noit_http_rest_handler(eventer_t e, int mask, void *closure,
301                        struct timeval *now) {
302   int newmask = EVENTER_READ | EVENTER_EXCEPTION;
303   acceptor_closure_t *ac = closure;
304   noit_http_rest_closure_t *restc = ac->service_ctx;
305
306   if(mask & EVENTER_EXCEPTION || (restc && restc->wants_shutdown)) {
307 socket_error:
308     /* Exceptions cause us to simply snip the connection */
309     eventer_remove_fd(e->fd);
310     e->opset->close(e->fd, &newmask, e);
311     if(restc) noit_http_rest_closure_free(restc);
312     if(ac) acceptor_closure_free(ac);
313     return 0;
314   }
315
316   if(!ac->service_ctx) {
317     const char *primer = "";
318     ac->service_ctx = restc = noit_http_rest_closure_alloc();
319     restc->ac = ac;
320     restc->remote_cn = strdup(ac->remote_cn ? ac->remote_cn : "");
321     restc->http_ctx =
322         noit_http_session_ctx_new(noit_rest_request_dispatcher,
323                                   restc, e, ac);
324    
325     switch(ac->cmd) {
326       case NOIT_CONTROL_DELETE:
327         primer = "DELE";
328         break;
329       case NOIT_CONTROL_GET:
330         primer = "GET ";
331         break;
332       case NOIT_CONTROL_HEAD:
333         primer = "HEAD";
334         break;
335       case NOIT_CONTROL_POST:
336         primer = "POST";
337         break;
338       case NOIT_CONTROL_PUT:
339         primer = "PUT ";
340         break;
341       case NOIT_CONTROL_MERGE:
342         primer = "MERG";
343         break;
344       default:
345         goto socket_error;
346     }
347     noit_http_session_prime_input(restc->http_ctx, primer, 4);
348   }
349   return restc->http_ctx->drive(e, mask, restc->http_ctx, now);
350 }
351
352 int
353 noit_http_rest_raw_handler(eventer_t e, int mask, void *closure,
354                            struct timeval *now) {
355   int newmask = EVENTER_READ | EVENTER_EXCEPTION;
356   acceptor_closure_t *ac = closure;
357   noit_http_rest_closure_t *restc = ac->service_ctx;
358
359   if(mask & EVENTER_EXCEPTION || (restc && restc->wants_shutdown)) {
360     /* Exceptions cause us to simply snip the connection */
361     eventer_remove_fd(e->fd);
362     e->opset->close(e->fd, &newmask, e);
363     if(restc) noit_http_rest_closure_free(restc);
364     if(ac) acceptor_closure_free(ac);
365     return 0;
366   }
367   if(!ac->service_ctx) {
368     ac->service_ctx = restc = noit_http_rest_closure_alloc();
369     restc->ac = ac;
370     restc->http_ctx =
371         noit_http_session_ctx_new(noit_rest_request_dispatcher,
372                                   restc, e, ac);
373   }
374   return restc->http_ctx->drive(e, mask, restc->http_ctx, now);
375 }
376
377 static void
378 rest_xml_payload_free(void *f) {
379   struct rest_xml_payload *xmlin = f;
380   if(xmlin->buffer) free(xmlin->buffer);
381   if(xmlin->indoc) xmlFreeDoc(xmlin->indoc);
382 }
383
384 xmlDocPtr
385 rest_get_xml_upload(noit_http_rest_closure_t *restc,
386                     int *mask, int *complete) {
387   struct rest_xml_payload *rxc;
388   if(restc->call_closure == NULL) {
389     rxc = restc->call_closure = calloc(1, sizeof(*rxc));
390     restc->call_closure_free = rest_xml_payload_free;
391   }
392   rxc = restc->call_closure;
393   while(!rxc->complete) {
394     int len;
395     if(rxc->len == rxc->allocd) {
396       char *b;
397       rxc->allocd += 32768;
398       b = rxc->buffer ? realloc(rxc->buffer, rxc->allocd) :
399                         malloc(rxc->allocd);
400       if(!b) {
401         *complete = 1;
402         return NULL;
403       }
404       rxc->buffer = b;
405     }
406     len = noit_http_session_req_consume(restc->http_ctx,
407                                         rxc->buffer + rxc->len,
408                                         rxc->allocd - rxc->len,
409                                         mask);
410     if(len > 0) rxc->len += len;
411     if(len < 0 && errno == EAGAIN) return NULL;
412     else if(len < 0) {
413       *complete = 1;
414       return NULL;
415     }
416     if(rxc->len == restc->http_ctx->req.content_length) {
417       rxc->indoc = xmlParseMemory(rxc->buffer, rxc->len);
418       rxc->complete = 1;
419     }
420   }
421
422   *complete = 1;
423   return rxc->indoc;
424 }
425 int
426 noit_rest_simple_file_handler(noit_http_rest_closure_t *restc,
427                               int npats, char **pats) {
428   int drlen = 0;
429   const char *document_root = NULL;
430   const char *index_file = NULL;
431   noit_http_session_ctx *ctx = restc->http_ctx;
432   char file[PATH_MAX], rfile[PATH_MAX];
433   struct stat st;
434   int fd;
435   void *contents;
436
437   if(npats != 1 ||
438      !noit_hash_retr_str(restc->ac->config,
439                          "document_root", strlen("document_root"),
440                          &document_root)) {
441     goto not_found;
442   }
443   if(!noit_hash_retr_str(restc->ac->config,
444                          "index_file", strlen("index_file"),
445                          &index_file)) {
446     index_file = "index.html";
447   }
448   drlen = strlen(document_root);
449   snprintf(file, sizeof(file), "%s/%s", document_root, pats[0]);
450   if(file[strlen(file) - 1] == '/') {
451     snprintf(file + strlen(file), sizeof(file) - strlen(file),
452              "%s", index_file);
453   }
454   /* resolve */
455   if(realpath(file, rfile) == NULL) goto not_found;
456   /* restrict */
457   if(strncmp(rfile, document_root, drlen)) goto denied;
458   if(rfile[drlen] != '/' && rfile[drlen + 1] != '/') goto denied;
459   /* stat */
460   if(stat(rfile, &st) != 0) {
461     switch (errno) {
462       case EACCES: goto denied;
463       default: goto not_found;
464     }
465   }
466   /* open */
467   if(st.st_size > 0) {
468     fd = open(rfile, O_RDONLY);
469     if(fd < 0) goto not_found;
470     contents = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
471     close(fd);
472     if(contents == (void *)-1) goto not_found;
473   }
474   noit_http_response_ok(ctx, "text/html");
475   if(st.st_size > 0) {
476     noit_http_response_append(ctx, contents, st.st_size);
477     munmap(contents, st.st_size);
478   }
479   noit_http_response_end(ctx);
480   return 0;
481
482  denied:
483   noit_http_response_denied(ctx, "text/html");
484   noit_http_response_end(ctx);
485   return 0;
486  not_found:
487   noit_http_response_not_found(ctx, "text/html");
488   noit_http_response_end(ctx);
489   return 0;
490 }
491
492 void noit_http_rest_load_rules() {
493   int ai, cnt = 0;
494   noit_conf_section_t *acls;
495   char path[256];
496   struct noit_rest_acl *newhead = NULL, *oldacls, *remove_acl;
497   struct noit_rest_acl_rule *remove_rule;
498
499   snprintf(path, sizeof(path), "//rest/acl");
500   acls = noit_conf_get_sections(NULL, path, &cnt);
501   noitL(noit_stderr, "Found %d acl stanzas\n", cnt);
502   for(ai = cnt-1; ai>=0; ai--) {
503     char tbuff[32];
504     struct noit_rest_acl *newacl;
505     int ri, rcnt = 0;
506     noit_boolean default_allow = noit_false;
507     noit_conf_section_t *rules;
508
509     newacl = calloc(1, sizeof(*newacl));
510     newacl->next = newhead;
511     newhead = newacl;
512     if(noit_conf_get_stringbuf(acls[ai], "@type", tbuff, sizeof(tbuff)) &&
513        !strcmp(tbuff, "allow"))
514       newacl->allow = noit_true;
515
516 #define compile_re(node, cont, name) do { \
517   char buff[256]; \
518   if(noit_conf_get_stringbuf(node, "@" #name, buff, sizeof(buff))) { \
519     const char *error; \
520     int erroffset; \
521     cont->name = pcre_compile(buff, 0, &error, &erroffset, NULL); \
522   } \
523 } while(0)
524
525     newacl->allow = default_allow;
526     compile_re(acls[ai], newacl, cn);
527     compile_re(acls[ai], newacl, url);
528     rules = noit_conf_get_sections(acls[ai], "rule", &rcnt);
529     for(ri = rcnt - 1; ri >= 0; ri--) {
530       struct noit_rest_acl_rule *newacl_rule;
531       newacl_rule = calloc(1, sizeof(*newacl_rule));
532       newacl_rule->next = newacl->rules;
533       newacl->rules = newacl_rule;
534       if(noit_conf_get_stringbuf(rules[ri], "@type", tbuff, sizeof(tbuff)) &&
535          !strcmp(tbuff, "allow"))
536         newacl_rule->allow = noit_true;
537       compile_re(rules[ri], newacl_rule, cn);
538       compile_re(rules[ri], newacl_rule, url);
539     }
540     free(rules);
541   }
542   free(acls);
543
544   oldacls = global_rest_acls;
545   global_rest_acls = newhead;
546
547   while(oldacls) {
548     remove_acl = oldacls->next;
549     while(oldacls->rules) {
550       remove_rule = oldacls->rules->next;
551       if(oldacls->rules->cn) pcre_free(oldacls->rules->cn);
552       if(oldacls->rules->url) pcre_free(oldacls->rules->url);
553       free(oldacls->rules);
554       oldacls->rules = remove_rule;
555     }
556     if(oldacls->cn) pcre_free(oldacls->cn);
557     if(oldacls->url) pcre_free(oldacls->url);
558     free(oldacls);
559     oldacls = remove_acl;
560   }
561 }
562 void noit_http_rest_init() {
563   noit_http_init();
564   eventer_name_callback("noit_wire_rest_api/1.0", noit_http_rest_handler);
565   eventer_name_callback("http_rest_api", noit_http_rest_raw_handler);
566
567   noit_http_rest_load_rules();
568
569   noit_control_dispatch_delegate(noit_control_dispatch,
570                                  NOIT_CONTROL_DELETE,
571                                  noit_http_rest_handler);
572   noit_control_dispatch_delegate(noit_control_dispatch,
573                                  NOIT_CONTROL_MERGE,
574                                  noit_http_rest_handler);
575   noit_control_dispatch_delegate(noit_control_dispatch,
576                                  NOIT_CONTROL_GET,
577                                  noit_http_rest_handler);
578   noit_control_dispatch_delegate(noit_control_dispatch,
579                                  NOIT_CONTROL_HEAD,
580                                  noit_http_rest_handler);
581   noit_control_dispatch_delegate(noit_control_dispatch,
582                                  NOIT_CONTROL_POST,
583                                  noit_http_rest_handler);
584   noit_control_dispatch_delegate(noit_control_dispatch,
585                                  NOIT_CONTROL_PUT,
586                                  noit_http_rest_handler);
587 }
588
Note: See TracBrowser for help on using the browser.