forked from mirrors/tftp-hpa-google
remap: fix timeouts for "g", add a "gg" flag to match sed s///g
Make sure that when using the global option, we still bump the deadman timer. The "g" option really should only have applied to the right-hand unmatched part of the string, like in sed. Add a "gg" option which does that. Signed-off-by: H. Peter Anvin <hpa@zytor.com>
This commit is contained in:
parent
74c5d8a020
commit
ac7f98e4d8
2 changed files with 189 additions and 119 deletions
149
tftpd/remap.c
149
tftpd/remap.c
|
@ -22,7 +22,7 @@
|
||||||
#include "tftpd.h"
|
#include "tftpd.h"
|
||||||
#include "remap.h"
|
#include "remap.h"
|
||||||
|
|
||||||
#define DEADMAN_MAX_STEPS 1024 /* Timeout after this many steps */
|
#define DEADMAN_MAX_STEPS 4096 /* Timeout after this many steps */
|
||||||
#define MAXLINE 16384 /* Truncate a line at this many bytes */
|
#define MAXLINE 16384 /* Truncate a line at this many bytes */
|
||||||
|
|
||||||
#define RULE_REWRITE 0x01 /* This is a rewrite rule */
|
#define RULE_REWRITE 0x01 /* This is a rewrite rule */
|
||||||
|
@ -37,6 +37,7 @@
|
||||||
#define RULE_HASFILE 0x100 /* Valid if rule results in a valid filename */
|
#define RULE_HASFILE 0x100 /* Valid if rule results in a valid filename */
|
||||||
#define RULE_RRQ 0x200 /* Get (read) only */
|
#define RULE_RRQ 0x200 /* Get (read) only */
|
||||||
#define RULE_WRQ 0x400 /* Put (write) only */
|
#define RULE_WRQ 0x400 /* Put (write) only */
|
||||||
|
#define RULE_SEDG 0x800 /* sed-style global */
|
||||||
|
|
||||||
struct rule {
|
struct rule {
|
||||||
struct rule *next;
|
struct rule *next;
|
||||||
|
@ -61,22 +62,30 @@ static int xform_tolower(int c)
|
||||||
return tolower(c);
|
return tolower(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Do \-substitution. Call with string == NULL to get length only. */
|
/*
|
||||||
static int genmatchstring(char *string, const char *pattern,
|
* Do \-substitution. Call with string == NULL to get length only.
|
||||||
const char *input, const regmatch_t * pmatch,
|
* "start" indicates an offset into the input buffer where the pattern
|
||||||
match_pattern_callback macrosub)
|
* match was started.
|
||||||
|
*/
|
||||||
|
static int do_genmatchstring(char *string, const char *pattern,
|
||||||
|
const char *ibuf,
|
||||||
|
const regmatch_t * pmatch,
|
||||||
|
match_pattern_callback macrosub,
|
||||||
|
int start, int *nextp)
|
||||||
{
|
{
|
||||||
int (*xform) (int) = xform_null;
|
int (*xform) (int) = xform_null;
|
||||||
int len = 0;
|
int len = 0;
|
||||||
int n, mlen, sublen;
|
int n, mlen, sublen;
|
||||||
int endbytes;
|
int endbytes;
|
||||||
|
const char *input = ibuf + start;
|
||||||
|
|
||||||
/* Get section before match; note pmatch[0] is the whole match */
|
/* Get section before match; note pmatch[0] is the whole match */
|
||||||
endbytes = strlen(input) - pmatch[0].rm_eo;
|
endbytes = strlen(input) - pmatch[0].rm_eo;
|
||||||
len = pmatch[0].rm_so + endbytes;
|
len = start + pmatch[0].rm_so;
|
||||||
if (string) {
|
if (string) {
|
||||||
memcpy(string, input, pmatch[0].rm_so);
|
/* Copy the prefix before "start" as well! */
|
||||||
string += pmatch[0].rm_so;
|
memcpy(string, ibuf, start + pmatch[0].rm_so);
|
||||||
|
string += start + pmatch[0].rm_so;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Transform matched section */
|
/* Transform matched section */
|
||||||
|
@ -102,7 +111,7 @@ static int genmatchstring(char *string, const char *pattern,
|
||||||
mlen = pmatch[n].rm_eo - pmatch[n].rm_so;
|
mlen = pmatch[n].rm_eo - pmatch[n].rm_so;
|
||||||
len += mlen;
|
len += mlen;
|
||||||
if (string) {
|
if (string) {
|
||||||
const char *p = input + pmatch[n].rm_so;
|
const char *p = input + start + pmatch[n].rm_so;
|
||||||
while (mlen--)
|
while (mlen--)
|
||||||
*string++ = xform(*p++);
|
*string++ = xform(*p++);
|
||||||
}
|
}
|
||||||
|
@ -145,7 +154,12 @@ static int genmatchstring(char *string, const char *pattern,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Pointer to post-substitution tail */
|
||||||
|
if (nextp)
|
||||||
|
*nextp = len;
|
||||||
|
|
||||||
/* Copy section after match */
|
/* Copy section after match */
|
||||||
|
len += endbytes;
|
||||||
if (string) {
|
if (string) {
|
||||||
memcpy(string, input + pmatch[0].rm_eo, endbytes);
|
memcpy(string, input + pmatch[0].rm_eo, endbytes);
|
||||||
string[endbytes] = '\0';
|
string[endbytes] = '\0';
|
||||||
|
@ -154,6 +168,26 @@ static int genmatchstring(char *string, const char *pattern,
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ditto, but allocate the string in a new buffer
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int genmatchstring(char **string, const char *pattern,
|
||||||
|
const char *ibuf,
|
||||||
|
const regmatch_t * pmatch,
|
||||||
|
match_pattern_callback macrosub,
|
||||||
|
int start, int *nextp)
|
||||||
|
{
|
||||||
|
int len;
|
||||||
|
char *buf;
|
||||||
|
|
||||||
|
len = do_genmatchstring(NULL, pattern, ibuf, pmatch,
|
||||||
|
macrosub, start, NULL);
|
||||||
|
buf = tf_malloc(len + 1);
|
||||||
|
return do_genmatchstring(buf, pattern, ibuf, pmatch,
|
||||||
|
macrosub, start, nextp);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Extract a string terminated by non-escaped whitespace; ignoring
|
* Extract a string terminated by non-escaped whitespace; ignoring
|
||||||
* leading whitespace. Consider an unescaped # to be a comment marker,
|
* leading whitespace. Consider an unescaped # to be a comment marker,
|
||||||
|
@ -211,6 +245,9 @@ static int parseline(char *line, struct rule *r, int lineno)
|
||||||
r->rule_flags |= RULE_REWRITE;
|
r->rule_flags |= RULE_REWRITE;
|
||||||
break;
|
break;
|
||||||
case 'g':
|
case 'g':
|
||||||
|
if (r->rule_flags & RULE_GLOBAL)
|
||||||
|
r->rule_flags |= RULE_SEDG;
|
||||||
|
else
|
||||||
r->rule_flags |= RULE_GLOBAL;
|
r->rule_flags |= RULE_GLOBAL;
|
||||||
break;
|
break;
|
||||||
case 'e':
|
case 'e':
|
||||||
|
@ -258,15 +295,15 @@ static int parseline(char *line, struct rule *r, int lineno)
|
||||||
lineno, line);
|
lineno, line);
|
||||||
return -1; /* Error */
|
return -1; /* Error */
|
||||||
}
|
}
|
||||||
if ((r->rule_flags & (RULE_GLOBAL|RULE_HASFILE))
|
if ((r->rule_flags & (RULE_GLOBAL|RULE_SEDG|RULE_HASFILE))
|
||||||
== (RULE_GLOBAL|RULE_HASFILE)) {
|
== (RULE_GLOBAL|RULE_HASFILE)) {
|
||||||
syslog(LOG_ERR, "E rules cannot be combined with g, line %d: %s\n",
|
syslog(LOG_ERR, "E rules cannot be combined with g (but gg is OK), line %d: %s\n",
|
||||||
lineno, line);
|
lineno, line);
|
||||||
return -1; /* Error */
|
return -1; /* Error */
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* RULE_GLOBAL is meaningless without RULE_REWRITE */
|
/* RULE_GLOBAL and RULE_SEDG are meaningless without RULE_REWRITE */
|
||||||
r->rule_flags &= ~RULE_GLOBAL;
|
r->rule_flags &= ~(RULE_GLOBAL|RULE_SEDG);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Read and compile the regex */
|
/* Read and compile the regex */
|
||||||
|
@ -353,14 +390,18 @@ char *rewrite_string(const struct formats *pf,
|
||||||
const char **errmsg)
|
const char **errmsg)
|
||||||
{
|
{
|
||||||
char *current = tfstrdup(input);
|
char *current = tfstrdup(input);
|
||||||
char *newstr;
|
char *newstr, *newerstr;
|
||||||
const char *accerr;
|
const char *accerr;
|
||||||
const struct rule *ruleptr = rules;
|
const struct rule *ruleptr = rules;
|
||||||
regmatch_t pmatch[10];
|
regmatch_t pmatch[10];
|
||||||
int len;
|
int i;
|
||||||
|
int len, newlen;
|
||||||
int was_match = 0;
|
int was_match = 0;
|
||||||
int deadman = DEADMAN_MAX_STEPS;
|
int deadman = DEADMAN_MAX_STEPS;
|
||||||
|
int matchsense;
|
||||||
|
int pmatches;
|
||||||
unsigned int bad_flags;
|
unsigned int bad_flags;
|
||||||
|
int ggoffset;
|
||||||
|
|
||||||
/* Default error */
|
/* Default error */
|
||||||
*errmsg = "Remap table failure";
|
*errmsg = "Remap table failure";
|
||||||
|
@ -379,26 +420,21 @@ char *rewrite_string(const struct formats *pf,
|
||||||
if (ruleptr->rule_flags & bad_flags)
|
if (ruleptr->rule_flags & bad_flags)
|
||||||
continue; /* This rule is excluded by flags */
|
continue; /* This rule is excluded by flags */
|
||||||
|
|
||||||
if (!deadman--) {
|
matchsense = ruleptr->rule_flags & RULE_INVERSE ? REG_NOMATCH : 0;
|
||||||
syslog(LOG_WARNING,
|
pmatches = ruleptr->rule_flags & RULE_INVERSE ? 0 : 10;
|
||||||
"remap: Breaking loop, input = %s, last = %s", input,
|
|
||||||
current);
|
|
||||||
free(current);
|
|
||||||
return NULL; /* Did not terminate! */
|
|
||||||
}
|
|
||||||
|
|
||||||
do {
|
/* Clear the pmatch[] array */
|
||||||
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++)
|
for (i = 0; i < 10; i++)
|
||||||
pmatch[i].rm_so = pmatch[i].rm_eo = -1;
|
pmatch[i].rm_so = pmatch[i].rm_eo = -1;
|
||||||
}
|
|
||||||
|
do {
|
||||||
|
if (!deadman--)
|
||||||
|
goto dead;
|
||||||
|
|
||||||
|
if (regexec(&ruleptr->rx, current, pmatches, pmatch, 0)
|
||||||
|
== matchsense) {
|
||||||
|
/* Match on this rule */
|
||||||
|
was_match = 1;
|
||||||
|
|
||||||
if (ruleptr->rule_flags & RULE_ABORT) {
|
if (ruleptr->rule_flags & RULE_ABORT) {
|
||||||
if (verbosity >= 3) {
|
if (verbosity >= 3) {
|
||||||
|
@ -407,12 +443,8 @@ char *rewrite_string(const struct formats *pf,
|
||||||
}
|
}
|
||||||
if (ruleptr->pattern[0]) {
|
if (ruleptr->pattern[0]) {
|
||||||
/* Custom error message */
|
/* Custom error message */
|
||||||
len =
|
genmatchstring(&newstr, ruleptr->pattern, current,
|
||||||
genmatchstring(NULL, ruleptr->pattern, current,
|
pmatch, macrosub, 0, NULL);
|
||||||
pmatch, macrosub);
|
|
||||||
newstr = tfmalloc(len + 1);
|
|
||||||
genmatchstring(newstr, ruleptr->pattern, current,
|
|
||||||
pmatch, macrosub);
|
|
||||||
*errmsg = newstr;
|
*errmsg = newstr;
|
||||||
} else {
|
} else {
|
||||||
*errmsg = NULL;
|
*errmsg = NULL;
|
||||||
|
@ -422,11 +454,29 @@ char *rewrite_string(const struct formats *pf,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ruleptr->rule_flags & RULE_REWRITE) {
|
if (ruleptr->rule_flags & RULE_REWRITE) {
|
||||||
len = genmatchstring(NULL, ruleptr->pattern, current,
|
len = genmatchstring(&newstr, ruleptr->pattern, current,
|
||||||
pmatch, macrosub);
|
pmatch, macrosub, 0, &ggoffset);
|
||||||
newstr = tfmalloc(len + 1);
|
|
||||||
genmatchstring(newstr, ruleptr->pattern, current,
|
if (ruleptr->rule_flags & RULE_SEDG) {
|
||||||
pmatch, macrosub);
|
/* sed-style partial-matching global */
|
||||||
|
while (ggoffset < len &&
|
||||||
|
regexec(ruleptr->rx, newstr + ggoffset,
|
||||||
|
pmatches, pmatch,
|
||||||
|
ggoffset ? REG_NOTBOL : 0)
|
||||||
|
== matchsense) {
|
||||||
|
if (!deadman--) {
|
||||||
|
free(current);
|
||||||
|
current = newstr;
|
||||||
|
goto dead;
|
||||||
|
}
|
||||||
|
len = genmatchstring(&newerstr, ruleptr->pattern,
|
||||||
|
newstr, pmatch, macrosub,
|
||||||
|
ggoffset, &ggoffset);
|
||||||
|
free(newstr);
|
||||||
|
newstr = newerstr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ((ruleptr->rule_flags & RULE_HASFILE) &&
|
if ((ruleptr->rule_flags & RULE_HASFILE) &&
|
||||||
pf->f_validate(newstr, mode, pf, &accerr)) {
|
pf->f_validate(newstr, mode, pf, &accerr)) {
|
||||||
if (verbosity >= 3) {
|
if (verbosity >= 3) {
|
||||||
|
@ -437,6 +487,7 @@ char *rewrite_string(const struct formats *pf,
|
||||||
was_match = 0;
|
was_match = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
free(current);
|
free(current);
|
||||||
current = newstr;
|
current = newstr;
|
||||||
if (verbosity >= 3) {
|
if (verbosity >= 3) {
|
||||||
|
@ -456,8 +507,8 @@ char *rewrite_string(const struct formats *pf,
|
||||||
} else {
|
} else {
|
||||||
break; /* No match, terminate unconditionally */
|
break; /* No match, terminate unconditionally */
|
||||||
}
|
}
|
||||||
/* If the rule is global, keep going until no match */
|
/* If the rule is (old-style) global, keep going until no match */
|
||||||
} while (ruleptr->rule_flags & RULE_GLOBAL);
|
} while ((ruleptr->rule_flags & (RULE_GLOBAL|RULE_SEDG)) == RULE_GLOBAL);
|
||||||
|
|
||||||
if (was_match) {
|
if (was_match) {
|
||||||
was_match = 0;
|
was_match = 0;
|
||||||
|
@ -476,10 +527,16 @@ char *rewrite_string(const struct formats *pf,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (verbosity >= 3) {
|
if (verbosity >= 3) {
|
||||||
syslog(LOG_INFO, "remap: done");
|
syslog(LOG_INFO, "remap: done");
|
||||||
}
|
}
|
||||||
return current;
|
return current;
|
||||||
|
|
||||||
|
dead: /* Deadman expired */
|
||||||
|
syslog(LOG_WARNING,
|
||||||
|
"remap: Breaking loop, input = %s, last = %s", input,
|
||||||
|
current);
|
||||||
|
free(current);
|
||||||
|
return NULL; /* Did not terminate! */
|
||||||
}
|
}
|
||||||
|
|
|
@ -273,6 +273,17 @@ The replacement pattern may contain escape sequences; see below.
|
||||||
Repeat this rule until it no longer matches. This is always used with
|
Repeat this rule until it no longer matches. This is always used with
|
||||||
.BR r .
|
.BR r .
|
||||||
.TP
|
.TP
|
||||||
|
.B gg
|
||||||
|
Repeat this rule until it no longer matches, but only on the portion
|
||||||
|
of the string that has not yet been matched, similar to how the
|
||||||
|
.B s
|
||||||
|
command with the
|
||||||
|
.B g
|
||||||
|
option works in
|
||||||
|
.BR sed (1).
|
||||||
|
This is always used with
|
||||||
|
.BR r .
|
||||||
|
.TP
|
||||||
.B i
|
.B i
|
||||||
Match the
|
Match the
|
||||||
.I regex
|
.I regex
|
||||||
|
@ -288,7 +299,9 @@ end rule processing after executing the rule. If this is combined with
|
||||||
.BR r ,
|
.BR r ,
|
||||||
then if the substitution does \fInot\fP result in a valid filename,
|
then if the substitution does \fInot\fP result in a valid filename,
|
||||||
the substitution is undone. This cannot be combined with
|
the substitution is undone. This cannot be combined with
|
||||||
.BR g .
|
.BR g ,
|
||||||
|
but \fIcan\fP be combined with
|
||||||
|
.BR gg .
|
||||||
.TP
|
.TP
|
||||||
.B s
|
.B s
|
||||||
If this rule matches, start rule processing over from the very first
|
If this rule matches, start rule processing over from the very first
|
||||||
|
@ -400,7 +413,7 @@ Access to files can, and should, be restricted by invoking
|
||||||
.B tftpd
|
.B tftpd
|
||||||
with a list of directories by including pathnames as server program
|
with a list of directories by including pathnames as server program
|
||||||
arguments on the command line. In this case access is restricted to
|
arguments on the command line. In this case access is restricted to
|
||||||
files whole names are prefixed by one of the given directories. If
|
files whose names are prefixed by one of the given directories. If
|
||||||
possible, it is recommended that the
|
possible, it is recommended that the
|
||||||
.B \-\-secure
|
.B \-\-secure
|
||||||
flag is used to set up a chroot() environment for the server to run in
|
flag is used to set up a chroot() environment for the server to run in
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue