2 /* ----------------------------------------------------------------------- *
4 * Copyright 2001 H. Peter Anvin - All Rights Reserved
6 * This program is free software available under the same license
7 * as the "OpenBSD" operating system, distributed at
8 * http://www.openbsd.org/.
10 * ----------------------------------------------------------------------- */
15 * Perform regular-expression based filename remapping.
18 #include "config.h" /* Must be included first! */
26 #define DEADMAN_MAX_STEPS 1024 /* Timeout after this many steps */
27 #define MAXLINE 16384 /* Truncate a line at this many bytes */
29 #define RULE_REWRITE 0x01 /* This is a rewrite rule */
30 #define RULE_GLOBAL 0x02 /* Global rule (repeat until no match) */
31 #define RULE_EXIT 0x04 /* Exit after matching this rule */
32 #define RULE_RESTART 0x08 /* Restart at the top after matching this rule */
33 #define RULE_ABORT 0x10 /* Terminate processing with an error */
34 #define RULE_GETONLY 0x20 /* Applicable to GET only */
35 #define RULE_PUTONLY 0x40 /* Applicable to PUT only */
45 /* Do \-substitution. Call with string == NULL to get length only. */
46 static int genmatchstring(char *string
, const char *pattern
, const char *input
, const regmatch_t
*pmatch
)
52 /* Get section before match; note pmatch[0] is the whole match */
53 endbytes
= strlen(input
) - pmatch
[0].rm_eo
;
54 len
= pmatch
[0].rm_so
+ endbytes
;
56 memcpy(string
, input
, pmatch
[0].rm_so
);
57 string
+= pmatch
[0].rm_so
;
60 /* Transform matched section */
62 if ( *pattern
== '\\' && pattern
[1] != '\0' ) {
63 if ( pattern
[1] < '0' || pattern
[1] > '9' ) {
66 *string
++ = pattern
[1];
70 if ( pmatch
[n
].rm_so
!= -1 ) {
71 mlen
= pmatch
[n
].rm_eo
- pmatch
[n
].rm_so
;
74 memcpy(string
, input
+pmatch
[n
].rm_so
, mlen
);
88 /* Copy section after match */
90 memcpy(string
, input
+pmatch
[0].rm_eo
, endbytes
);
91 string
[endbytes
] = '\0';
97 /* Extract a string terminated by non-escaped whitespace; ignore leading whitespace */
98 /* Consider an unescaped # to be a comment marker, functionally \n */
99 static int readescstring(char *buf
, char **str
)
102 int wasbs
= 0, len
= 0;
104 while ( *p
&& isspace(*p
) )
114 if ( !wasbs
&& (isspace(*p
) || *p
== '#') ) {
119 /* Important: two backslashes leave us in the !wasbs state! */
120 wasbs
= !wasbs
&& ( *p
== '\\' );
130 /* Parse a line into a set of instructions */
131 static int parseline(char *line
, struct rule
*r
, int lineno
)
133 char buffer
[MAXLINE
];
136 int rxflags
= REG_EXTENDED
;
139 memset(r
, 0, sizeof r
);
142 if ( !readescstring(buffer
, &line
) )
143 return 0; /* No rule found */
145 for ( p
= buffer
; *p
; p
++ ) {
148 r
->rule_flags
|= RULE_REWRITE
;
151 r
->rule_flags
|= RULE_GLOBAL
;
154 r
->rule_flags
|= RULE_EXIT
;
157 r
->rule_flags
|= RULE_RESTART
;
160 r
->rule_flags
|= RULE_ABORT
;
163 rxflags
|= REG_ICASE
;
166 r
->rule_flags
|= RULE_GETONLY
;
169 r
->rule_flags
|= RULE_PUTONLY
;
172 syslog(LOG_ERR
, "Remap command \"%s\" on line %d contains invalid char \"%c\"",
174 return -1; /* Error */
179 /* RULE_GLOBAL only applies when RULE_REWRITE specified */
180 if ( !(r
->rule_flags
& RULE_REWRITE
) )
181 r
->rule_flags
&= ~RULE_GLOBAL
;
183 /* Read and compile the regex */
184 if ( !readescstring(buffer
, &line
) ) {
185 syslog(LOG_ERR
, "No regex on remap line %d: %s\n", lineno
, line
);
186 return -1; /* Error */
189 if ( (rv
= regcomp(&r
->rx
, buffer
, rxflags
)) != 0 ) {
191 regerror(rv
, &r
->rx
, errbuf
, BUFSIZ
);
192 syslog(LOG_ERR
, "Bad regex in remap line %d: %s\n", lineno
, errbuf
);
193 return -1; /* Error */
196 /* Read the rewrite pattern, if any */
197 if ( readescstring(buffer
, &line
) ) {
198 r
->pattern
= tfstrdup(buffer
);
204 return 1; /* Rule found */
207 /* Read a rule file */
208 struct rule
*parserulefile(FILE *f
)
211 struct rule
*first_rule
= NULL
;
212 struct rule
**last_rule
= &first_rule
;
213 struct rule
*this_rule
= tfmalloc(sizeof(struct rule
));
218 while ( lineno
++, fgets(line
, MAXLINE
, f
) ) {
219 rv
= parseline(line
, this_rule
, lineno
);
223 *last_rule
= this_rule
;
224 last_rule
= &this_rule
->next
;
225 this_rule
= tfmalloc(sizeof(struct rule
));
229 free(this_rule
); /* Last one is always unused */
232 /* Bail on error, we have already logged an error message */
239 /* Destroy a rule file data structure */
240 void freerules(struct rule
*r
)
249 /* "" patterns aren't allocated by malloc() */
250 if ( r
->pattern
&& *r
->pattern
)
251 free((void *)r
->pattern
);
259 /* Execute a rule set on a string; returns a malloc'd new string. */
260 char *rewrite_string(const char *input
, const struct rule
*rules
, int is_put
)
262 char *current
= tfstrdup(input
);
264 const struct rule
*ruleptr
= rules
;
265 regmatch_t pmatch
[10];
268 int deadman
= DEADMAN_MAX_STEPS
;
270 if ( verbosity
>= 3 ) {
271 syslog(LOG_INFO
, "remap: input: %s", current
);
274 for ( ruleptr
= rules
; ruleptr
; ruleptr
= ruleptr
->next
) {
275 if ( ((ruleptr
->rule_flags
& RULE_GETONLY
) && is_put
) ||
276 ((ruleptr
->rule_flags
& RULE_PUTONLY
) && !is_put
) ) {
277 continue; /* Rule not applicable, try next */
281 syslog(LOG_WARNING
, "remap: Breaking loop, input = %s, last = %s",
284 return NULL
; /* Did not terminate! */
288 if ( regexec(&ruleptr
->rx
, current
, 10, pmatch
, 0) == 0 ) {
289 /* Match on this rule */
292 if ( ruleptr
->rule_flags
& RULE_ABORT
) {
293 if ( verbosity
>= 3 ) {
294 syslog(LOG_INFO
, "remap: rule %d: abort: %s",
295 ruleptr
->nrule
, current
);
301 if ( ruleptr
->rule_flags
& RULE_REWRITE
) {
302 len
= genmatchstring(NULL
, ruleptr
->pattern
, current
, pmatch
);
303 newstr
= tfmalloc(len
+1);
304 genmatchstring(newstr
, ruleptr
->pattern
, current
, pmatch
);
307 if ( verbosity
>= 3 ) {
308 syslog(LOG_INFO
, "remap: rule %d: rewrite: %s",
309 ruleptr
->nrule
, current
);
313 break; /* No match, terminate unconditionally */
315 /* If the rule is global, keep going until no match */
316 } while ( ruleptr
->rule_flags
& RULE_GLOBAL
);
321 if ( ruleptr
->rule_flags
& RULE_EXIT
) {
322 if ( verbosity
>= 3 ) {
323 syslog(LOG_INFO
, "remap: rule %d: exit", ruleptr
->nrule
);
325 return current
; /* Exit here, we're done */
326 } else if ( ruleptr
->rule_flags
& RULE_RESTART
) {
327 ruleptr
= rules
; /* Start from the top */
328 if ( verbosity
>= 3 ) {
329 syslog(LOG_INFO
, "remap: rule %d: restart", ruleptr
->nrule
);
335 if ( verbosity
>= 3 ) {
336 syslog(LOG_INFO
, "remap: done");