📄 remap.c
字号:
/* $Id$ *//* ----------------------------------------------------------------------- * * * Copyright 2001-2004 H. Peter Anvin - All Rights Reserved * * This program is free software available under the same license * as the "OpenBSD" operating system, distributed at * http://www.openbsd.org/. * * ----------------------------------------------------------------------- *//* * remap.c * * Perform regular-expression based filename remapping. */#include "config.h" /* Must be included first! */#include <ctype.h>#include <syslog.h>#include <regex.h>#include "tftpd.h"#include "remap.h"#define DEADMAN_MAX_STEPS 1024 /* Timeout after this many steps */#define MAXLINE 16384 /* Truncate a line at this many bytes */#define RULE_REWRITE 0x01 /* This is a rewrite rule */#define RULE_GLOBAL 0x02 /* Global rule (repeat until no match) */#define RULE_EXIT 0x04 /* Exit after matching this rule */#define RULE_RESTART 0x08 /* Restart at the top after matching this rule */#define RULE_ABORT 0x10 /* Terminate processing with an error */#define RULE_GETONLY 0x20 /* Applicable to GET only */#define RULE_PUTONLY 0x40 /* Applicable to PUT only */#define RULE_INVERSE 0x80 /* Execute if regex *doesn't* match */struct rule { struct rule *next; int nrule; int rule_flags; regex_t rx; const char *pattern;};static int xform_null(int c){ return c;}static int xform_toupper(int c){ return toupper(c);}static int xform_tolower(int c){ return tolower(c);}/* Do \-substitution. Call with string == NULL to get length only. */static int genmatchstring(char *string, const char *pattern, const char *input, const regmatch_t *pmatch, match_pattern_callback macrosub){ int (*xform)(int) = xform_null; int len = 0; int n, mlen, sublen; int endbytes; /* Get section before match; note pmatch[0] is the whole match */ endbytes = strlen(input) - pmatch[0].rm_eo; len = pmatch[0].rm_so + endbytes; if ( string ) { memcpy(string, input, pmatch[0].rm_so); string += pmatch[0].rm_so; } /* Transform matched section */ while ( *pattern ) { mlen = 0; if ( *pattern == '\\' && pattern[1] != '\0' ) { char macro = pattern[1]; switch ( macro ) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': n = pattern[1] - '0'; if ( pmatch[n].rm_so != -1 ) { mlen = pmatch[n].rm_eo - pmatch[n].rm_so; len += mlen; if ( string ) { const char *p = input+pmatch[n].rm_so; while ( mlen-- ) *string++ = xform(*p++); } } break; case 'L': xform = xform_tolower; break; case 'U': xform = xform_toupper; break; case 'E': xform = xform_null; break; default: if ( macrosub && (sublen = macrosub(macro, string)) >= 0 ) { while ( sublen-- ) { len++; if ( string ) { *string = xform(*string); string++; } } } else { len++; if ( string ) *string++ = xform(pattern[1]); } } pattern += 2; } else { len++; if ( string ) *string++ = xform(*pattern); pattern++; } } /* Copy section after match */ if ( string ) { memcpy(string, input+pmatch[0].rm_eo, endbytes); string[endbytes] = '\0'; } return len;}/* * Extract a string terminated by non-escaped whitespace; ignoring * leading whitespace. Consider an unescaped # to be a comment marker, * functionally \n. */static int readescstring(char *buf, char **str){ char *p = *str; int wasbs = 0, len = 0; while ( *p && isspace(*p) ) p++; if ( ! *p ) { *buf = '\0'; *str = p; return 0; } while ( *p ) { if ( !wasbs && (isspace(*p) || *p == '#') ) { *buf = '\0'; *str = p; return len; } /* Important: two backslashes leave us in the !wasbs state! */ wasbs = !wasbs && ( *p == '\\' ); *buf++ = *p++; len++; } *buf = '\0'; *str = p; return len;}/* Parse a line into a set of instructions */static int parseline(char *line, struct rule *r, int lineno){ char buffer[MAXLINE]; char *p; int rv; int rxflags = REG_EXTENDED; static int nrule; memset(r, 0, sizeof *r); r->nrule = nrule; if ( !readescstring(buffer, &line) ) return 0; /* No rule found */ for ( p = buffer ; *p ; p++ ) { switch(*p) { case 'r': r->rule_flags |= RULE_REWRITE; break; case 'g': r->rule_flags |= RULE_GLOBAL; break; case 'e': r->rule_flags |= RULE_EXIT; break; case 's': r->rule_flags |= RULE_RESTART; break; case 'a': r->rule_flags |= RULE_ABORT; break; case 'i': rxflags |= REG_ICASE; break; case 'G': r->rule_flags |= RULE_GETONLY; break; case 'P': r->rule_flags |= RULE_PUTONLY; break; case '~': r->rule_flags |= RULE_INVERSE; break; default: syslog(LOG_ERR, "Remap command \"%s\" on line %d contains invalid char \"%c\"", buffer, lineno, *p); return -1; /* Error */ break; } } /* RULE_GLOBAL only applies when RULE_REWRITE specified */ if ( !(r->rule_flags & RULE_REWRITE) ) r->rule_flags &= ~RULE_GLOBAL; if ( (r->rule_flags & (RULE_INVERSE|RULE_REWRITE)) == (RULE_INVERSE|RULE_REWRITE) ) { syslog(LOG_ERR, "r rules cannot be inverted, line %d: %s\n", lineno, line); return -1; /* Error */ } /* Read and compile the regex */ if ( !readescstring(buffer, &line) ) { syslog(LOG_ERR, "No regex on remap line %d: %s\n", lineno, line); return -1; /* Error */ } if ( (rv = regcomp(&r->rx, buffer, rxflags)) != 0 ) { char errbuf[BUFSIZ]; regerror(rv, &r->rx, errbuf, BUFSIZ); syslog(LOG_ERR, "Bad regex in remap line %d: %s\n", lineno, errbuf); return -1; /* Error */ } /* Read the rewrite pattern, if any */ if ( readescstring(buffer, &line) ) { r->pattern = tfstrdup(buffer); } else { r->pattern = ""; } nrule++; return 1; /* Rule found */}/* Read a rule file */struct rule *parserulefile(FILE *f){ char line[MAXLINE]; struct rule *first_rule = NULL; struct rule **last_rule = &first_rule; struct rule *this_rule = tfmalloc(sizeof(struct rule)); int rv; int lineno = 0; int err = 0; while ( lineno++, fgets(line, MAXLINE, f) ) { rv = parseline(line, this_rule, lineno); if ( rv < 0 ) err = 1; if ( rv > 0 ) { *last_rule = this_rule; last_rule = &this_rule->next; this_rule = tfmalloc(sizeof(struct rule)); } } free(this_rule); /* Last one is always unused */ if ( err ) { /* Bail on error, we have already logged an error message */ exit(EX_CONFIG); } return first_rule;}/* Destroy a rule file data structure */void freerules(struct rule *r){ struct rule *next; while ( r ) { next = r->next; regfree(&r->rx); /* "" patterns aren't allocated by malloc() */ if ( r->pattern && *r->pattern ) free((void *)r->pattern); free(r); r = next; }}/* Execute a rule set on a string; returns a malloc'd new string. */char *rewrite_string(const char *input, const struct rule *rules, int is_put, match_pattern_callback macrosub, const char **errmsg){ char *current = tfstrdup(input); char *newstr; const struct rule *ruleptr = rules; regmatch_t pmatch[10]; int len; int was_match = 0; int deadman = DEADMAN_MAX_STEPS; /* Default error */ *errmsg = "Remap table failure"; if ( verbosity >= 3 ) { syslog(LOG_INFO, "remap: input: %s", current); } for ( ruleptr = rules ; ruleptr ; ruleptr = ruleptr->next ) { if ( ((ruleptr->rule_flags & RULE_GETONLY) && is_put) || ((ruleptr->rule_flags & RULE_PUTONLY) && !is_put) ) { continue; /* Rule not applicable, try next */ } if ( ! deadman-- ) { syslog(LOG_WARNING, "remap: Breaking loop, input = %s, last = %s", input, current); free(current); return NULL; /* Did not terminate! */ } do { if ( regexec(&ruleptr->rx, current, 10, pmatch, 0) == (ruleptr->rule_flags & RULE_INVERSE ? REG_NOMATCH : 0) ) { /* Match on this rule */ was_match = 1; if ( ruleptr->rule_flags & RULE_INVERSE ) { /* No actual match, so clear out the pmatch array */ int i; for ( i = 0 ; i < 10 ; i++ ) pmatch[i].rm_so = pmatch[i].rm_eo = -1; } if ( ruleptr->rule_flags & RULE_ABORT ) { if ( verbosity >= 3 ) { syslog(LOG_INFO, "remap: rule %d: abort: %s", ruleptr->nrule, current); } if ( ruleptr->pattern[0] ) { /* Custom error message */ len = genmatchstring(NULL, ruleptr->pattern, current, pmatch, macrosub); newstr = tfmalloc(len+1); genmatchstring(newstr, ruleptr->pattern, current, pmatch, macrosub); *errmsg = newstr; } else { *errmsg = NULL; } free(current); return(NULL); } if ( ruleptr->rule_flags & RULE_REWRITE ) { len = genmatchstring(NULL, ruleptr->pattern, current, pmatch, macrosub); newstr = tfmalloc(len+1); genmatchstring(newstr, ruleptr->pattern, current, pmatch, macrosub); free(current); current = newstr; if ( verbosity >= 3 ) { syslog(LOG_INFO, "remap: rule %d: rewrite: %s", ruleptr->nrule, current); } } } else { break; /* No match, terminate unconditionally */ } /* If the rule is global, keep going until no match */ } while ( ruleptr->rule_flags & RULE_GLOBAL ); if ( was_match ) { was_match = 0; if ( ruleptr->rule_flags & RULE_EXIT ) { if ( verbosity >= 3 ) { syslog(LOG_INFO, "remap: rule %d: exit", ruleptr->nrule); } return current; /* Exit here, we're done */ } else if ( ruleptr->rule_flags & RULE_RESTART ) { ruleptr = rules; /* Start from the top */ if ( verbosity >= 3 ) { syslog(LOG_INFO, "remap: rule %d: restart", ruleptr->nrule); } } } } if ( verbosity >= 3 ) { syslog(LOG_INFO, "remap: done"); } return current;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -