Better error messages.

This commit is contained in:
hpa 2002-11-08 01:10:08 +00:00
parent 82eae1bcd6
commit b3ec0c58e6
5 changed files with 109 additions and 74 deletions

View file

@ -1,5 +1,11 @@
$Id$
Changes in 0.32:
Better error messages; including the capability to send a
custom error message to the client when hitting an "a" rule in
a remapping table.
Changes in 0.31:
Put in a check to make sure xinetd (in particular) doesn't
pass us an IPv6 socket.

View file

@ -65,7 +65,7 @@ int timeout;
sigjmp_buf toplevel;
sigjmp_buf timeoutbuf;
static void nak(int);
static void nak(int, const char *);
static int makerequest(int, const char *, struct tftphdr *, const char *);
static void printstats(const char *, unsigned long);
static void startclock(void);
@ -107,7 +107,7 @@ tftp_sendfile(int fd, const char *name, const char *mode)
/* size = read(fd, dp->th_data, SEGSIZE); */
size = readit(file, &dp, convert);
if (size < 0) {
nak(errno + 100);
nak(errno + 100, NULL);
break;
}
dp->th_opcode = htons((u_short)DATA);
@ -274,7 +274,7 @@ send_ack:
/* size = write(fd, dp->th_data, n - 4); */
size = writeit(file, &dp, n - 4, convert);
if (size < 0) {
nak(errno + 100);
nak(errno + 100, NULL);
break;
}
amount += size;
@ -331,29 +331,37 @@ struct errmsg {
* offset by 100.
*/
static void
nak(int error)
nak(int error, const char *msg)
{
struct errmsg *pe;
struct tftphdr *tp;
int length;
tp = (struct tftphdr *)ackbuf;
tp->th_opcode = htons((u_short)ERROR);
tp->th_code = htons((u_short)error);
for (pe = errmsgs; pe->e_code >= 0; pe++)
if (pe->e_code == error)
break;
if (pe->e_code < 0) {
pe->e_msg = strerror(error - 100);
tp->th_code = EUNDEF;
struct errmsg *pe;
struct tftphdr *tp;
int length;
tp = (struct tftphdr *)ackbuf;
tp->th_opcode = htons((u_short)ERROR);
tp->th_code = htons((u_short)error);
if ( !msg ) {
if ( error >= 100 ) {
msg = strerror(error - 100);
tp->th_code = EUNDEF;
} else {
for (pe = errmsgs; pe->e_code >= 0; pe++) {
if (pe->e_code == error) {
msg = pe->e_msg;
break;
}
strcpy(tp->th_msg, pe->e_msg);
length = strlen(pe->e_msg) + 4;
if (trace)
tpacket("sent", tp, length);
if (sendto(f, ackbuf, length, 0, (struct sockaddr *)&peeraddr,
sizeof(peeraddr)) != length)
perror("nak");
}
}
}
length = strlen(msg)+1;
memcpy(tp->th_msg, msg, length);
length += 4; /* Add space for header */
if (trace)
tpacket("sent", tp, length);
if (sendto(f, ackbuf, length, 0, (struct sockaddr *)&peeraddr,
sizeof(peeraddr)) != length)
perror("nak");
}
static void

View file

@ -1,7 +1,7 @@
/* $Id$ */
/* ----------------------------------------------------------------------- *
*
* Copyright 2001 H. Peter Anvin - All Rights Reserved
* Copyright 2001-2002 H. Peter Anvin - All Rights Reserved
*
* This program is free software available under the same license
* as the "OpenBSD" operating system, distributed at
@ -104,8 +104,11 @@ static int genmatchstring(char *string, const char *pattern, const char *input,
return len;
}
/* Extract a string terminated by non-escaped whitespace; ignore leading whitespace */
/* Consider an unescaped # to be a comment marker, functionally \n */
/*
* 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;
@ -268,7 +271,8 @@ void freerules(struct rule *r)
/* 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)
int is_put, match_pattern_callback macrosub,
const char **errmsg)
{
char *current = tfstrdup(input);
char *newstr;
@ -278,6 +282,9 @@ char *rewrite_string(const char *input, const struct rule *rules,
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);
}
@ -305,6 +312,17 @@ char *rewrite_string(const char *input, const struct rule *rules,
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);
}

View file

@ -37,7 +37,7 @@ void freerules(struct rule *);
/* Execute a rule set on a string; returns a malloc'd new string. */
char *rewrite_string(const char *, const struct rule *, int,
match_pattern_callback);
match_pattern_callback, const char **);
#endif /* WITH_REGEX */
#endif /* TFTPD_REMAP_H */

View file

@ -114,7 +114,7 @@ static struct rule *rewrite_rules = NULL;
#endif
int tftp(struct tftphdr *, int);
void nak(int);
static void nak(int, const char *);
void timer(int);
void justquit(int);
void do_opt(char *, char *, char **);
@ -683,14 +683,14 @@ main(int argc, char **argv)
exit(0);
}
char *rewrite_access(char *, int);
char *rewrite_access(char *, int, const char **);
int validate_access(char *, int, struct formats *);
void tftp_sendfile(struct formats *, struct tftphdr *, int);
void tftp_recvfile(struct formats *, struct tftphdr *, int);
struct formats {
const char *f_mode;
char *(*f_rewrite)(char *, int);
char *(*f_rewrite)(char *, int, const char **);
int (*f_validate)(char *, int, struct formats *);
void (*f_send)(struct formats *, struct tftphdr *, int);
void (*f_recv)(struct formats *, struct tftphdr *, int);
@ -712,6 +712,7 @@ tftp(struct tftphdr *tp, int size)
struct formats *pf = NULL;
char *origfilename;
char *filename, *mode = NULL;
const char *maperrmsg;
char *val = NULL, *opt = NULL;
char *ap = ackbuf + 2;
@ -727,7 +728,7 @@ tftp(struct tftphdr *tp, int size)
} while (cp < buf + size && *cp);
if ( *cp ) {
nak(EBADOP); /* Corrupt packet - no final NULL */
nak(EBADOP, "Request not null-terminated");
exit(0);
}
@ -742,11 +743,12 @@ tftp(struct tftphdr *tp, int size)
break;
}
if (!pf->f_mode) {
nak(EBADOP);
nak(EBADOP, "Unknown mode");
exit(0);
}
if ( !(filename = (*pf->f_rewrite)(origfilename, tp->th_opcode)) ) {
nak(EACCESS); /* File denied by mapping rule */
if ( !(filename =
(*pf->f_rewrite)(origfilename, tp->th_opcode, &maperrmsg)) ) {
nak(EACCESS, maperrmsg); /* File denied by mapping rule */
exit(0);
}
if ( verbosity >= 1 ) {
@ -761,7 +763,7 @@ tftp(struct tftphdr *tp, int size)
}
ecode = (*pf->f_validate)(filename, tp->th_opcode, pf);
if (ecode) {
nak(ecode);
nak(ecode, NULL);
exit(0);
}
opt = ++cp;
@ -774,7 +776,7 @@ tftp(struct tftphdr *tp, int size)
}
if (!pf) {
nak(EBADOP);
nak(EBADOP, "Missing mode");
exit(0);
}
@ -946,13 +948,13 @@ do_opt(char *opt, char *val, char **ap)
if (po->o_fnc(val, &ret)) {
if (*ap + strlen(opt) + strlen(ret) + 2 >=
ackbuf + sizeof(ackbuf)) {
nak(ENOSPACE); /* EOPTNEG? */
nak(EOPTNEG, "Insufficient space for options");
exit(0);
}
*ap = strrchr(strcpy(strrchr(strcpy(*ap, opt),'\0') + 1,
ret),'\0') + 1;
} else {
nak(EOPTNEG);
nak(EOPTNEG, "Unsupported option(s) requested");
exit(0);
}
break;
@ -999,11 +1001,11 @@ rewrite_macros(char macro, char *output)
* Modify the filename, if applicable. If it returns NULL, deny the access.
*/
char *
rewrite_access(char *filename, int mode)
rewrite_access(char *filename, int mode, const char **msg)
{
if ( rewrite_rules ) {
char *newname = rewrite_string(filename, rewrite_rules,
mode != RRQ, rewrite_macros);
char *newname = rewrite_string(filename, rewrite_rules, mode != RRQ,
rewrite_macros, msg);
filename = newname;
}
return filename;
@ -1011,9 +1013,10 @@ rewrite_access(char *filename, int mode)
#else
char *
rewrite_access(char *filename, int mode)
rewrite_access(char *filename, int mode, const char **msg)
{
(void)mode; /* Avoid warning */
(void)msg;
return filename;
}
#endif
@ -1172,7 +1175,7 @@ tftp_sendfile(struct formats *pf, struct tftphdr *oap, int oacklen)
do {
size = readit(file, &dp, pf->f_convert);
if (size < 0) {
nak(errno + 100);
nak(errno + 100, NULL);
goto abort;
}
dp->th_opcode = htons((u_short)DATA);
@ -1283,8 +1286,8 @@ tftp_recvfile(struct formats *pf, struct tftphdr *oap, int oacklen)
/* size = write(file, dp->th_data, n - 4); */
size = writeit(file, &dp, n - 4, pf->f_convert);
if (size != (n-4)) { /* ahem */
if (size < 0) nak(errno + 100);
else nak(ENOSPACE);
if (size < 0) nak(errno + 100, NULL);
else nak(ENOSPACE, NULL);
goto abort;
}
} while (size == segsize);
@ -1308,21 +1311,19 @@ tftp_recvfile(struct formats *pf, struct tftphdr *oap, int oacklen)
return;
}
struct errmsg {
int e_code;
const char *e_msg;
} errmsgs[] = {
{ EUNDEF, "Undefined error code" },
{ ENOTFOUND, "File not found" },
{ EACCESS, "Access violation" },
{ ENOSPACE, "Disk full or allocation exceeded" },
{ EBADOP, "Illegal TFTP operation" },
{ EBADID, "Unknown transfer ID" },
{ EEXISTS, "File already exists" },
{ ENOUSER, "No such user" },
{ EOPTNEG, "Failure to negotiate RFC2347 options" },
{ -1, 0 }
static const char * const errmsgs[] =
{
"Undefined error code", /* 0 - EUNDEF */
"File not found", /* 1 - ENOTFOUND */
"Access denied", /* 2 - EACCESS */
"Disk full or allocation exceeded", /* 3 - ENOSPACE */
"Illegal TFTP operation", /* 4 - EBADOP */
"Unknown transfer ID", /* 5 - EBADID */
"File already exists", /* 6 - EEXISTS */
"No such user", /* 7 - ENOUSER */
"Failure to negotiate RFC2347 options" /* 8 - EOPTNEG */
};
#define ERR_CNT (sizeof(errmsgs)/sizeof(const char *))
/*
* Send a nak packet (error message).
@ -1330,27 +1331,29 @@ struct errmsg {
* standard TFTP codes, or a UNIX errno
* offset by 100.
*/
void
nak(int error)
static void
nak(int error, const char *msg)
{
struct tftphdr *tp;
int length;
struct errmsg *pe;
tp = (struct tftphdr *)buf;
tp->th_opcode = htons((u_short)ERROR);
tp->th_code = htons((u_short)error);
for (pe = errmsgs; pe->e_code >= 0; pe++)
if (pe->e_code == error)
break;
if (pe->e_code < 0) {
pe->e_msg = strerror(error - 100);
tp->th_code = EUNDEF; /* set 'undef' errorcode */
if ( !msg ) {
if ( error >= 100 ) {
/* This is a Unix errno+100 */
msg = strerror(error - 100);
tp->th_code = EUNDEF;
} else {
if ( (unsigned)error >= ERR_CNT )
tp->th_code = error = EUNDEF;
msg = errmsgs[error];
}
}
strcpy(tp->th_msg, pe->e_msg);
length = strlen(pe->e_msg);
tp->th_msg[length] = '\0';
length += 5;
length = strlen(msg)+1;
memcpy(tp->th_msg, msg, length);
length += 4; /* Add space for header */
if ( verbosity >= 2 ) {
syslog(LOG_INFO, "sending NAK (%d, %s) to %s",