📄 mod_dontdothat.c
字号:
/* * mod_dontdothat.c: an Apache filter that allows you to return arbitrary * errors for various types of Subversion requests. * * ==================================================================== * Copyright (c) 2006 CollabNet. All rights reserved. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://subversion.tigris.org/license-1.html. * If newer versions of this license are posted there, you may use a * newer version instead, at your option. * * This software consists of voluntary contributions made by many * individuals. For exact contribution history, see the revision * history and logs, available at http://subversion.tigris.org/. * ==================================================================== */#include <httpd.h>#include <http_config.h>#include <http_request.h>#include <http_log.h>#include <util_filter.h>#include <ap_config.h>#include <apr_strings.h>#include <expat.h>#include "mod_dav_svn.h"#include "svn_string.h"#include "svn_config.h"module AP_MODULE_DECLARE_DATA dontdothat_module;typedef struct { const char *config_file; const char *base_path;} dontdothat_config_rec;static void *create_dontdothat_dir_config(apr_pool_t *pool, char *dir){ dontdothat_config_rec *cfg = apr_pcalloc(pool, sizeof(*cfg)); cfg->base_path = dir; return cfg;}static const command_rec dontdothat_cmds[] ={ AP_INIT_TAKE1("DontDoThatConfigFile", ap_set_file_slot, (void *) APR_OFFSETOF(dontdothat_config_rec, config_file), OR_ALL, "Text file containing actions to take for specific requests"), { NULL }};typedef enum { STATE_BEGINNING, STATE_IN_UPDATE, STATE_IN_SRC_PATH, STATE_IN_DST_PATH, STATE_IN_RECURSIVE} parse_state_t;typedef struct { /* Set to TRUE when we determine that the request is safe and should be * allowed to continue. */ svn_boolean_t let_it_go; /* Set to TRUE when we determine that the request is unsafe and should be * stopped in its tracks. */ svn_boolean_t no_soup_for_you; XML_Parser xmlp; /* The current location in the REPORT body. */ parse_state_t state; /* A buffer to hold CDATA we encounter. */ svn_stringbuf_t *buffer; dontdothat_config_rec *cfg; /* An array of wildcards that are special cased to be allowed. */ apr_array_header_t *allow_recursive_ops; /* An array of wildcards where recursive operations are not allowed. */ apr_array_header_t *no_recursive_ops; /* TRUE if a path has failed a test already. */ svn_boolean_t path_failed; /* An error for when we're using this as a baton while parsing config * files. */ svn_error_t *err; /* The current request. */ request_rec *r;} dontdothat_filter_ctx;/* Return TRUE if wildcard WC matches path P, FALSE otherwise. */static svn_boolean_tmatches(const char *wc, const char *p){ for (;;) { switch (*wc) { case '*': if (wc[1] != '/' && wc[1] != '\0') abort(); /* This was checked for during parsing of the config. */ /* It's a wild card, so eat up until the next / in p. */ while (*p && p[1] != '/') ++p; /* If we ran out of p and we're out of wc then it matched. */ if (! *p) { if (wc[1] == '\0') return TRUE; else return FALSE; } break; case '\0': if (*p != '\0') /* This means we hit the end of wc without running out of p. */ return FALSE; else /* Or they were exactly the same length, so it's not lower. */ return TRUE; default: if (*wc != *p) return FALSE; /* If we don't match, then move on to the next * case. */ else break; } ++wc; ++p; if (! *p && *wc) return FALSE; }}static svn_boolean_tis_this_legal(dontdothat_filter_ctx *ctx, const char *uri){ const char *relative_path; const char *cleaned_uri; const char *repos_name; int trailing_slash; dav_error *derr; /* Ok, so we need to skip past the scheme, host, etc. */ uri = ap_strstr_c(uri, "://"); if (uri) uri = ap_strchr_c(uri + 3, '/'); if (uri) { const char *repos_path; derr = dav_svn_split_uri(ctx->r, uri, ctx->cfg->base_path, &cleaned_uri, &trailing_slash, &repos_name, &relative_path, &repos_path); if (! derr) { int idx; if (! repos_path) repos_path = ""; repos_path = apr_psprintf(ctx->r->pool, "/%s", repos_path); /* First check the special cases that are always legal... */ for (idx = 0; idx < ctx->allow_recursive_ops->nelts; ++idx) { const char *wc = APR_ARRAY_IDX(ctx->allow_recursive_ops, idx, const char *); if (matches(wc, repos_path)) { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, ctx->r, "mod_dontdothat: rule %s allows %s", wc, repos_path); return TRUE; } } /* Then look for stuff we explicitly don't allow. */ for (idx = 0; idx < ctx->no_recursive_ops->nelts; ++idx) { const char *wc = APR_ARRAY_IDX(ctx->no_recursive_ops, idx, const char *); if (matches(wc, repos_path)) { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, ctx->r, "mod_dontdothat: rule %s forbids %s", wc, repos_path); return FALSE; } } } } return TRUE;}static apr_status_tdontdothat_filter(ap_filter_t *f, apr_bucket_brigade *bb, ap_input_mode_t mode, apr_read_type_e block, apr_off_t readbytes){ dontdothat_filter_ctx *ctx = f->ctx; apr_status_t rv; apr_bucket *e; if (mode != AP_MODE_READBYTES) return ap_get_brigade(f->next, bb, mode, block, readbytes); rv = ap_get_brigade(f->next, bb, mode, block, readbytes); if (rv) return rv; for (e = APR_BRIGADE_FIRST(bb); e != APR_BRIGADE_SENTINEL(bb); e = APR_BUCKET_NEXT(e)) { svn_boolean_t last = APR_BUCKET_IS_EOS(e); const char *str; apr_size_t len; if (last) { str = ""; len = 0; } else { rv = apr_bucket_read(e, &str, &len, APR_BLOCK_READ); if (rv) return rv; } if (! XML_Parse(ctx->xmlp, str, len, last)) { /* let_it_go so we clean up our parser, no_soup_for_you so that we * bail out before bothering to parse this stuff a second time. */ ctx->let_it_go = TRUE; ctx->no_soup_for_you = TRUE; } /* If we found something that isn't allowed, set the correct status * and return an error so it'll bail out before it gets anywhere it * can do real damage. */ if (ctx->no_soup_for_you) { /* XXX maybe set up the SVN-ACTION env var so that it'll show up * in the Subversion operational logs? */ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, f->r, "mod_dontdothat: client broke the rules, " "returning error"); f->r->status = 403; f->r->status_line = "403 Forbidden, No Soup For You!"; return APR_EGENERAL; } else if (ctx->let_it_go || last) { ap_remove_input_filter(f); ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, f->r, "mod_dontdothat: letting request go through"); return rv; } } return rv;}static voidcdata(void *baton, const char *data, int len){ dontdothat_filter_ctx *ctx = baton; if (ctx->no_soup_for_you || ctx->let_it_go) return; switch (ctx->state) { case STATE_IN_SRC_PATH: /* FALLTHROUGH */ case STATE_IN_DST_PATH: /* FALLTHROUGH */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -