root/src/noit_filters_rest.c

Revision 02e2463695b8becfcf8b7c2e3c2af911228c6747, 8.3 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 6 months ago)

Expose POST /filters/cull that will remove unused filtersets.

  • Property mode set to 100644
Line 
1 /*
2  * Copyright (c) 2009, 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 <assert.h>
35 #include <errno.h>
36 #include <libxml/parser.h>
37 #include <libxml/tree.h>
38 #include <libxml/xpath.h>
39 #include "noit_listener.h"
40 #include "noit_http.h"
41 #include "noit_rest.h"
42 #include "noit_filters.h"
43 #include "noit_check.h"
44 #include "noit_check_tools.h"
45 #include "noit_conf.h"
46 #include "noit_conf_private.h"
47
48 #define FAIL(a) do { error = (a); goto error; } while(0)
49
50 static int
51 rest_show_filter(noit_http_rest_closure_t *restc,
52                  int npats, char **pats) {
53   noit_http_session_ctx *ctx = restc->http_ctx;
54   xmlDocPtr doc = NULL;
55   xmlNodePtr node, root;
56   char xpath[1024];
57   int error_code = 500;
58
59   if(npats != 2) goto error;
60
61   snprintf(xpath, sizeof(xpath), "//filtersets%sfilterset[@name=\"%s\"]",
62            pats[0], pats[1]);
63
64   node = noit_conf_get_section(NULL, xpath);
65   if(!node) goto not_found;
66
67   doc = xmlNewDoc((xmlChar *)"1.0");
68   root = xmlCopyNode(node, 1);
69   xmlDocSetRootElement(doc, root);
70   noit_http_response_ok(ctx, "text/xml");
71   noit_http_response_xml(ctx, doc);
72   noit_http_response_end(ctx);
73   goto cleanup;
74
75  not_found:
76   noit_http_response_not_found(ctx, "text/html");
77   noit_http_response_end(ctx);
78   goto cleanup;
79
80  error:
81   noit_http_response_standard(ctx, error_code, "ERROR", "text/html");
82   noit_http_response_end(ctx);
83   goto cleanup;
84
85  cleanup:
86   if(doc) xmlFreeDoc(doc);
87   return 0;
88 }
89
90 static xmlNodePtr
91 make_conf_path(char *path) {
92   xmlNodePtr start, tmp;
93   char fullpath[1024], *tok, *brk;
94   if(!path || strlen(path) < 1) return NULL;
95   snprintf(fullpath, sizeof(fullpath), "%s", path+1);
96   fullpath[strlen(fullpath)-1] = '\0';
97   start = noit_conf_get_section(NULL, "/noit/filtersets");
98   if(!start) return NULL;
99   for (tok = strtok_r(fullpath, "/", &brk);
100        tok;
101        tok = strtok_r(NULL, "/", &brk)) {
102     if(!xmlValidateNameValue((xmlChar *)tok)) return NULL;
103     if(!strcmp(tok, "filterset")) return NULL;
104     for (tmp = start->children; tmp; tmp = tmp->next) {
105       if(!strcmp((char *)tmp->name, tok)) break;
106     }
107     if(!tmp) {
108       tmp = xmlNewNode(NULL, (xmlChar *)tok);
109       xmlAddChild(start, tmp);
110       CONF_DIRTY(tmp);
111     }
112     start = tmp;
113   }
114   return start;
115 }
116 static xmlNodePtr
117 validate_filter_post(xmlDocPtr doc) {
118   xmlNodePtr root, r;
119   root = xmlDocGetRootElement(doc);
120   if(!root) return NULL;
121   if(strcmp((char *)root->name, "filterset")) return NULL;
122   if(xmlHasProp(root, (xmlChar *)"name")) return NULL;
123   if(!root->children) return NULL;
124   for(r = root->children; r; r = r->next) {
125     char *type;
126     if(strcmp((char *)r->name, "rule")) return NULL;
127     type = (char *)xmlGetProp(r, (xmlChar *)"type");
128     if(!type || (strcmp(type, "deny") && strcmp(type, "accept"))) {
129       if(type) xmlFree(type);
130       return NULL;
131     }
132     if(type) xmlFree(type);
133   }
134   return root;
135 }
136 static int
137 rest_delete_filter(noit_http_rest_closure_t *restc,
138                    int npats, char **pats) {
139   noit_http_session_ctx *ctx = restc->http_ctx;
140   xmlNodePtr node;
141   char xpath[1024];
142   int error_code = 500;
143
144   if(npats != 2) goto error;
145
146   snprintf(xpath, sizeof(xpath), "//filtersets%sfilterset[@name=\"%s\"]",
147            pats[0], pats[1]);
148   node = noit_conf_get_section(NULL, xpath);
149   if(!node) goto not_found;
150   if(noit_filter_remove(node) == 0) goto not_found;
151   CONF_REMOVE(node);
152   xmlUnlinkNode(node);
153   xmlFreeNode(node);
154
155   if(noit_conf_write_file(NULL) != 0)
156     noitL(noit_error, "local config write failed\n");
157   noit_conf_mark_changed();
158   noit_http_response_ok(ctx, "text/html");
159   noit_http_response_end(ctx);
160   goto cleanup;
161
162  not_found:
163   noit_http_response_not_found(ctx, "text/html");
164   noit_http_response_end(ctx);
165   goto cleanup;
166
167  error:
168   noit_http_response_standard(ctx, error_code, "ERROR", "text/html");
169   noit_http_response_end(ctx);
170   goto cleanup;
171
172  cleanup:
173   return 0;
174 }
175
176 static int
177 rest_cull_filter(noit_http_rest_closure_t *restc,
178                  int npats, char **pats) {
179   int rv;
180   char cnt_str[32];
181   noit_http_session_ctx *ctx = restc->http_ctx;
182
183   rv = noit_filtersets_cull_unused();
184   if(rv > 0) noit_conf_mark_changed();
185   snprintf(cnt_str, sizeof(cnt_str), "%d", rv);
186   noit_http_response_ok(ctx, "text/html");
187   noit_http_response_header_set(ctx, "X-Filters-Removed", cnt_str);
188   noit_http_response_end(ctx);
189   return 0;
190 }
191
192 static int
193 rest_set_filter(noit_http_rest_closure_t *restc,
194                 int npats, char **pats) {
195   noit_http_session_ctx *ctx = restc->http_ctx;
196   xmlDocPtr doc = NULL, indoc = NULL;
197   xmlNodePtr node, parent, root, newfilter;
198   char xpath[1024];
199   int error_code = 500, complete = 0, mask = 0;
200   const char *error = "internal error";
201
202   if(npats != 2) goto error;
203
204   indoc = rest_get_xml_upload(restc, &mask, &complete);
205   if(!complete) return mask;
206   if(indoc == NULL) FAIL("xml parse error");
207
208   snprintf(xpath, sizeof(xpath), "//filtersets%sfilterset[@name=\"%s\"]",
209            pats[0], pats[1]);
210   node = noit_conf_get_section(NULL, xpath);
211   if(!node && noit_filter_exists(pats[1])) {
212     /* It's someone else's */
213     error_code = 403;
214     goto error;
215   }
216
217   if((newfilter = validate_filter_post(indoc)) == NULL) goto error;
218   xmlSetProp(newfilter, (xmlChar *)"name", (xmlChar *)pats[1]);
219
220   parent = make_conf_path(pats[0]);
221   if(!parent) FAIL("invalid path");
222   if(node) {
223     xmlUnlinkNode(node);
224     xmlFreeNode(node);
225   }
226   xmlUnlinkNode(newfilter);
227   xmlAddChild(parent, newfilter);
228   CONF_DIRTY(newfilter);
229
230   noit_conf_mark_changed();
231   if(noit_conf_write_file(NULL) != 0)
232     noitL(noit_error, "local config write failed\n");
233   noit_filter_compile_add(newfilter);
234   if(restc->call_closure_free) restc->call_closure_free(restc->call_closure);
235   restc->call_closure_free = NULL;
236   restc->call_closure = NULL;
237   restc->fastpath = rest_show_filter;
238   return restc->fastpath(restc, restc->nparams, restc->params);
239
240  error:
241   noit_http_response_standard(ctx, error_code, "ERROR", "text/html");
242   doc = xmlNewDoc((xmlChar *)"1.0");
243   root = xmlNewDocNode(doc, NULL, (xmlChar *)"error", NULL);
244   xmlDocSetRootElement(doc, root);
245   xmlNodeAddContent(root, (xmlChar *)error);
246   noit_http_response_xml(ctx, doc);
247   noit_http_response_end(ctx);
248   goto cleanup;
249
250  cleanup:
251   if(doc) xmlFreeDoc(doc);
252   return 0;
253 }
254
255 void
256 noit_filters_rest_init() {
257   assert(noit_http_rest_register_auth(
258     "GET", "/filters/", "^show(/.*)(?<=/)([^/]+)$",
259     rest_show_filter, noit_http_rest_client_cert_auth
260   ) == 0);
261   assert(noit_http_rest_register_auth(
262     "PUT", "/filters/", "^set(/.*)(?<=/)([^/]+)$",
263     rest_set_filter, noit_http_rest_client_cert_auth
264   ) == 0);
265   assert(noit_http_rest_register_auth(
266     "DELETE", "/filters/", "^delete(/.*)(?<=/)([^/]+)$",
267     rest_delete_filter, noit_http_rest_client_cert_auth
268   ) == 0);
269   assert(noit_http_rest_register_auth(
270     "POST", "/filters/", "^cull$",
271     rest_cull_filter, noit_http_rest_client_cert_auth
272   ) == 0);
273 }
274
Note: See TracBrowser for help on using the browser.