- Fix bug in the handling of timeouts.

- Add support for \U..\E and \L..\E.
- Add support for inverse rules.
This commit is contained in:
hpa 2004-09-14 22:38:46 +00:00
parent bc8e3b32ee
commit 17b5188b65
4 changed files with 101 additions and 17 deletions

View file

@ -1,5 +1,12 @@
$Id$
Changes in 0.39:
Support Perl-style \U...\E and \L...\E, as well as allow
matching rules to be inverted (execute if rule *doesn't*
match.)
Fix a timeout bug.
Changes in 0.38:
Portability fixes.

View file

@ -1,7 +1,7 @@
/* $Id$ */
/* ----------------------------------------------------------------------- *
*
* Copyright 2001-2002 H. Peter Anvin - All Rights Reserved
* 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
@ -33,6 +33,7 @@
#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;
@ -42,12 +43,28 @@ struct rule {
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;
int n, mlen, sublen;
int endbytes;
/* Get section before match; note pmatch[0] is the whole match */
@ -60,9 +77,13 @@ static int genmatchstring(char *string, const char *pattern, const char *input,
/* Transform matched section */
while ( *pattern ) {
mlen = 0;
if ( *pattern == '\\' && pattern[1] != '\0' ) {
char macro = pattern[1];
if ( macro >= '0' && macro <= '9' ) {
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 ) {
@ -73,24 +94,41 @@ static int genmatchstring(char *string, const char *pattern, const char *input,
string += mlen;
}
}
} else {
int sublen;
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 ) {
len += sublen;
if ( string )
string += sublen;
while ( sublen-- ) {
len++;
if ( string ) {
*string = xform(*string);
string++;
}
}
} else {
len++;
if ( string )
*string++ = pattern[1];
*string++ = xform(pattern[1]);
}
}
pattern += 2;
} else {
len++;
if ( string )
*string++ = *pattern;
*string++ = xform(*pattern);
pattern++;
}
}
@ -181,6 +219,9 @@ static int parseline(char *line, struct rule *r, int lineno)
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);
@ -193,6 +234,11 @@ static int parseline(char *line, struct rule *r, int lineno)
if ( !(r->rule_flags & RULE_REWRITE) )
r->rule_flags &= ~RULE_GLOBAL;
if ( r->rule_flags & (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);
@ -303,10 +349,18 @@ char *rewrite_string(const char *input, const struct rule *rules,
}
do {
if ( regexec(&ruleptr->rx, current, 10, pmatch, 0) == 0 ) {
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",

View file

@ -31,7 +31,7 @@
.\" SUCH DAMAGE.
.\"
.\"----------------------------------------------------------------------- */
.TH TFTPD 8 "23 October 2002" "tftp-hpa @@VERSION@@" "System Manager's Manual"
.TH TFTPD 8 "3 September 2004" "tftp-hpa @@VERSION@@" "System Manager's Manual"
.SH NAME
.B tftpd
\- IPv4 Trivial File Transfer Protocol server
@ -261,6 +261,15 @@ This rule applies to GET (RRQ) requests only.
.TP
.B P
This rule applies to PUT (WRQ) requests only.
.TP
.B ~
Inverse the sense of this rule, i.e. execute the
.I operation
only if the
.I regex
.I doesn't
match. Cannot used together with
.BR r .
.PP
The following escape sequences are recognized as part of the
.IR "replacement pattern" :
@ -289,8 +298,17 @@ Literal backslash.
\fB\\\fP\fIwhitespace\fP
Literal whitespace.
.TP
\fB\\#\fI
\fB\\#\fP
Literal hash mark.
.TP
\fB\\U\fP
Turns all subsequent letters to upper case.
.TP
\fB\\L\fP
Turns all subsequent letters to lower case.
.TP
\fB\\E\fP
Cancels the effect of \fB\\U\fP or \fB\\L\fP.
.PP
If the mapping file is changed, you need to send
.B SIGHUP

View file

@ -1180,18 +1180,20 @@ tftp_sendfile(struct formats *pf, struct tftphdr *oap, int oacklen)
struct tftphdr *ap; /* ack packet */
static u_short block = 1; /* Static to avoid longjmp funnies */
u_short ap_opcode, ap_block;
unsigned long r_timeout;
int size, n;
if (oap) {
timeout = rexmtval;
(void)sigsetjmp(timeoutbuf,1);
oack:
r_timeout = timeout;
if (send(peer, oap, oacklen, 0) != oacklen) {
syslog(LOG_WARNING, "tftpd: oack: %m\n");
goto abort;
}
for ( ; ; ) {
n = recv_time(peer, ackbuf, sizeof(ackbuf), 0, &timeout);
n = recv_time(peer, ackbuf, sizeof(ackbuf), 0, &r_timeout);
if (n < 0) {
syslog(LOG_WARNING, "tftpd: read: %m\n");
goto abort;
@ -1226,13 +1228,14 @@ tftp_sendfile(struct formats *pf, struct tftphdr *oap, int oacklen)
timeout = rexmtval;
(void) sigsetjmp(timeoutbuf,1);
r_timeout = timeout;
if (send(peer, dp, size + 4, 0) != size + 4) {
syslog(LOG_WARNING, "tftpd: write: %m");
goto abort;
}
read_ahead(file, pf->f_convert);
for ( ; ; ) {
n = recv_time(peer, ackbuf, sizeof (ackbuf), 0, &timeout);
n = recv_time(peer, ackbuf, sizeof (ackbuf), 0, &r_timeout);
if (n < 0) {
syslog(LOG_WARNING, "tftpd: read(ack): %m");
goto abort;
@ -1286,6 +1289,7 @@ tftp_recvfile(struct formats *pf, struct tftphdr *oap, int oacklen)
static u_short block = 0;
static int acksize;
u_short dp_opcode, dp_block;
unsigned long r_timeout;
dp = w_init();
do {
@ -1303,13 +1307,14 @@ tftp_recvfile(struct formats *pf, struct tftphdr *oap, int oacklen)
block++;
(void) sigsetjmp(timeoutbuf,1);
send_ack:
r_timeout = timeout;
if (send(peer, ackbuf, acksize, 0) != acksize) {
syslog(LOG_WARNING, "tftpd: write(ack): %m");
goto abort;
}
write_behind(file, pf->f_convert);
for ( ; ; ) {
n = recv_time(peer, dp, PKTSIZE, 0, &timeout);
n = recv_time(peer, dp, PKTSIZE, 0, &r_timeout);
if (n < 0) { /* really? */
syslog(LOG_WARNING, "tftpd: read: %m");
goto abort;