- 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$ $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: Changes in 0.38:
Portability fixes. Portability fixes.

View file

@ -1,7 +1,7 @@
/* $Id$ */ /* $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 * This program is free software available under the same license
* as the "OpenBSD" operating system, distributed at * as the "OpenBSD" operating system, distributed at
@ -33,6 +33,7 @@
#define RULE_ABORT 0x10 /* Terminate processing with an error */ #define RULE_ABORT 0x10 /* Terminate processing with an error */
#define RULE_GETONLY 0x20 /* Applicable to GET only */ #define RULE_GETONLY 0x20 /* Applicable to GET only */
#define RULE_PUTONLY 0x40 /* Applicable to PUT only */ #define RULE_PUTONLY 0x40 /* Applicable to PUT only */
#define RULE_INVERSE 0x80 /* Execute if regex *doesn't* match */
struct rule { struct rule {
struct rule *next; struct rule *next;
@ -42,12 +43,28 @@ struct rule {
const char *pattern; 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. */ /* Do \-substitution. Call with string == NULL to get length only. */
static int genmatchstring(char *string, const char *pattern, const char *input, static int genmatchstring(char *string, const char *pattern, const char *input,
const regmatch_t *pmatch, match_pattern_callback macrosub) const regmatch_t *pmatch, match_pattern_callback macrosub)
{ {
int (*xform)(int) = xform_null;
int len = 0; int len = 0;
int n, mlen; int n, mlen, sublen;
int endbytes; int endbytes;
/* Get section before match; note pmatch[0] is the whole match */ /* 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 */ /* Transform matched section */
while ( *pattern ) { while ( *pattern ) {
mlen = 0;
if ( *pattern == '\\' && pattern[1] != '\0' ) { if ( *pattern == '\\' && pattern[1] != '\0' ) {
char macro = pattern[1]; 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'; n = pattern[1] - '0';
if ( pmatch[n].rm_so != -1 ) { if ( pmatch[n].rm_so != -1 ) {
@ -73,24 +94,41 @@ static int genmatchstring(char *string, const char *pattern, const char *input,
string += mlen; string += mlen;
} }
} }
} else { break;
int sublen;
case 'L':
xform = xform_tolower;
break;
case 'U':
xform = xform_toupper;
break;
case 'E':
xform = xform_null;
break;
default:
if ( macrosub && if ( macrosub &&
(sublen = macrosub(macro, string)) >= 0 ) { (sublen = macrosub(macro, string)) >= 0 ) {
len += sublen; while ( sublen-- ) {
if ( string ) len++;
string += sublen; if ( string ) {
*string = xform(*string);
string++;
}
}
} else { } else {
len++; len++;
if ( string ) if ( string )
*string++ = pattern[1]; *string++ = xform(pattern[1]);
} }
} }
pattern += 2; pattern += 2;
} else { } else {
len++; len++;
if ( string ) if ( string )
*string++ = *pattern; *string++ = xform(*pattern);
pattern++; pattern++;
} }
} }
@ -181,6 +219,9 @@ static int parseline(char *line, struct rule *r, int lineno)
case 'P': case 'P':
r->rule_flags |= RULE_PUTONLY; r->rule_flags |= RULE_PUTONLY;
break; break;
case '~':
r->rule_flags |= RULE_INVERSE;
break;
default: default:
syslog(LOG_ERR, "Remap command \"%s\" on line %d contains invalid char \"%c\"", syslog(LOG_ERR, "Remap command \"%s\" on line %d contains invalid char \"%c\"",
buffer, lineno, *p); buffer, lineno, *p);
@ -193,6 +234,11 @@ static int parseline(char *line, struct rule *r, int lineno)
if ( !(r->rule_flags & RULE_REWRITE) ) if ( !(r->rule_flags & RULE_REWRITE) )
r->rule_flags &= ~RULE_GLOBAL; 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 */ /* Read and compile the regex */
if ( !readescstring(buffer, &line) ) { if ( !readescstring(buffer, &line) ) {
syslog(LOG_ERR, "No regex on remap line %d: %s\n", lineno, 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 { 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 */ /* Match on this rule */
was_match = 1; 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 ( ruleptr->rule_flags & RULE_ABORT ) {
if ( verbosity >= 3 ) { if ( verbosity >= 3 ) {
syslog(LOG_INFO, "remap: rule %d: abort: %s", syslog(LOG_INFO, "remap: rule %d: abort: %s",

View file

@ -31,7 +31,7 @@
.\" SUCH DAMAGE. .\" 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 .SH NAME
.B tftpd .B tftpd
\- IPv4 Trivial File Transfer Protocol server \- IPv4 Trivial File Transfer Protocol server
@ -261,6 +261,15 @@ This rule applies to GET (RRQ) requests only.
.TP .TP
.B P .B P
This rule applies to PUT (WRQ) requests only. 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 .PP
The following escape sequences are recognized as part of the The following escape sequences are recognized as part of the
.IR "replacement pattern" : .IR "replacement pattern" :
@ -289,8 +298,17 @@ Literal backslash.
\fB\\\fP\fIwhitespace\fP \fB\\\fP\fIwhitespace\fP
Literal whitespace. Literal whitespace.
.TP .TP
\fB\\#\fI \fB\\#\fP
Literal hash mark. 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 .PP
If the mapping file is changed, you need to send If the mapping file is changed, you need to send
.B SIGHUP .B SIGHUP

View file

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