|
version 1.1.1.4, 2021/03/17 00:56:46
|
version 1.1.1.5, 2023/09/27 11:02:07
|
|
Line 1
|
Line 1
|
| /* dnsmasq is Copyright (c) 2000-2021 Simon Kelley | /* dnsmasq is Copyright (c) 2000-2022 Simon Kelley |
| |
|
| This program is free software; you can redistribute it and/or modify |
This program is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
it under the terms of the GNU General Public License as published by |
|
Line 19
|
Line 19
|
| #ifdef HAVE_TFTP |
#ifdef HAVE_TFTP |
| |
|
| static void handle_tftp(time_t now, struct tftp_transfer *transfer, ssize_t len); |
static void handle_tftp(time_t now, struct tftp_transfer *transfer, ssize_t len); |
| static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix); | static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix, char *client); |
| static void free_transfer(struct tftp_transfer *transfer); |
static void free_transfer(struct tftp_transfer *transfer); |
| static ssize_t tftp_err(int err, char *packet, char *message, char *file); | static ssize_t tftp_err(int err, char *packet, char *message, char *file, char *arg2); |
| static ssize_t tftp_err_oops(char *packet, char *file); | static ssize_t tftp_err_oops(char *packet, const char *file); |
| static ssize_t get_block(char *packet, struct tftp_transfer *transfer); |
static ssize_t get_block(char *packet, struct tftp_transfer *transfer); |
| static char *next(char **p, char *end); |
static char *next(char **p, char *end); |
| static void sanitise(char *buf); |
static void sanitise(char *buf); |
|
Line 39 static void sanitise(char *buf);
|
Line 39 static void sanitise(char *buf);
|
| #define ERR_PERM 2 |
#define ERR_PERM 2 |
| #define ERR_FULL 3 |
#define ERR_FULL 3 |
| #define ERR_ILL 4 |
#define ERR_ILL 4 |
| |
#define ERR_TID 5 |
| |
|
| void tftp_request(struct listener *listen, time_t now) |
void tftp_request(struct listener *listen, time_t now) |
| { |
{ |
|
Line 95 void tftp_request(struct listener *listen, time_t now)
|
Line 96 void tftp_request(struct listener *listen, time_t now)
|
| if ((len = recvmsg(listen->tftpfd, &msg, 0)) < 2) |
if ((len = recvmsg(listen->tftpfd, &msg, 0)) < 2) |
| return; |
return; |
| |
|
| |
#ifdef HAVE_DUMPFILE |
| |
dump_packet_udp(DUMP_TFTP, (void *)packet, len, (union mysockaddr *)&peer, NULL, listen->tftpfd); |
| |
#endif |
| |
|
| /* Can always get recvd interface for IPv6 */ |
/* Can always get recvd interface for IPv6 */ |
| if (!check_dest) |
if (!check_dest) |
| { |
{ |
|
Line 361 void tftp_request(struct listener *listen, time_t now)
|
Line 366 void tftp_request(struct listener *listen, time_t now)
|
| !(mode = next(&p, end)) || |
!(mode = next(&p, end)) || |
| (strcasecmp(mode, "octet") != 0 && strcasecmp(mode, "netascii") != 0)) |
(strcasecmp(mode, "octet") != 0 && strcasecmp(mode, "netascii") != 0)) |
| { |
{ |
| len = tftp_err(ERR_ILL, packet, _("unsupported request from %s"), daemon->addrbuff); | len = tftp_err(ERR_ILL, packet, _("unsupported request from %s"), daemon->addrbuff, NULL); |
| is_err = 1; |
is_err = 1; |
| } |
} |
| else |
else |
|
Line 400 void tftp_request(struct listener *listen, time_t now)
|
Line 405 void tftp_request(struct listener *listen, time_t now)
|
| if (*p == '\\') |
if (*p == '\\') |
| *p = '/'; |
*p = '/'; |
| else if (option_bool(OPT_TFTP_LC)) |
else if (option_bool(OPT_TFTP_LC)) |
| *p = tolower(*p); | *p = tolower((unsigned char)*p); |
| |
|
| strcpy(daemon->namebuff, "/"); |
strcpy(daemon->namebuff, "/"); |
| if (prefix) |
if (prefix) |
|
Line 471 void tftp_request(struct listener *listen, time_t now)
|
Line 476 void tftp_request(struct listener *listen, time_t now)
|
| strncat(daemon->namebuff, filename, (MAXDNAME-1) - strlen(daemon->namebuff)); |
strncat(daemon->namebuff, filename, (MAXDNAME-1) - strlen(daemon->namebuff)); |
| |
|
| /* check permissions and open file */ |
/* check permissions and open file */ |
| if ((transfer->file = check_tftp_fileperm(&len, prefix))) | if ((transfer->file = check_tftp_fileperm(&len, prefix, daemon->addrbuff))) |
| { |
{ |
| if ((len = get_block(packet, transfer)) == -1) |
if ((len = get_block(packet, transfer)) == -1) |
| len = tftp_err_oops(packet, daemon->namebuff); |
len = tftp_err_oops(packet, daemon->namebuff); |
|
Line 481 void tftp_request(struct listener *listen, time_t now)
|
Line 486 void tftp_request(struct listener *listen, time_t now)
|
| } |
} |
| |
|
| send_from(transfer->sockfd, !option_bool(OPT_SINGLE_PORT), packet, len, &peer, &addra, if_index); |
send_from(transfer->sockfd, !option_bool(OPT_SINGLE_PORT), packet, len, &peer, &addra, if_index); |
| |
|
| |
#ifdef HAVE_DUMPFILE |
| |
dump_packet_udp(DUMP_TFTP, (void *)packet, len, NULL, (union mysockaddr *)&peer, transfer->sockfd); |
| |
#endif |
| |
|
| if (is_err) |
if (is_err) |
| free_transfer(transfer); |
free_transfer(transfer); |
|
Line 491 void tftp_request(struct listener *listen, time_t now)
|
Line 500 void tftp_request(struct listener *listen, time_t now)
|
| } |
} |
| } |
} |
| |
|
| static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix) | static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix, char *client) |
| { |
{ |
| char *packet = daemon->packet, *namebuff = daemon->namebuff; |
char *packet = daemon->packet, *namebuff = daemon->namebuff; |
| struct tftp_file *file; |
struct tftp_file *file; |
|
Line 508 static struct tftp_file *check_tftp_fileperm(ssize_t *
|
Line 517 static struct tftp_file *check_tftp_fileperm(ssize_t *
|
| { |
{ |
| if (errno == ENOENT) |
if (errno == ENOENT) |
| { |
{ |
| *len = tftp_err(ERR_FNF, packet, _("file %s not found"), namebuff); | *len = tftp_err(ERR_FNF, packet, _("file %s not found for %s"), namebuff, client); |
| return NULL; |
return NULL; |
| } |
} |
| else if (errno == EACCES) |
else if (errno == EACCES) |
|
Line 561 static struct tftp_file *check_tftp_fileperm(ssize_t *
|
Line 570 static struct tftp_file *check_tftp_fileperm(ssize_t *
|
| return file; |
return file; |
| |
|
| perm: |
perm: |
| errno = EACCES; | *len = tftp_err(ERR_PERM, packet, _("cannot access %s: %s"), namebuff, strerror(EACCES)); |
| *len = tftp_err(ERR_PERM, packet, _("cannot access %s: %s"), namebuff); | |
| if (fd != -1) |
if (fd != -1) |
| close(fd); |
close(fd); |
| return NULL; |
return NULL; |
|
Line 583 void check_tftp_listeners(time_t now)
|
Line 591 void check_tftp_listeners(time_t now)
|
| for (transfer = daemon->tftp_trans; transfer; transfer = transfer->next) |
for (transfer = daemon->tftp_trans; transfer; transfer = transfer->next) |
| if (poll_check(transfer->sockfd, POLLIN)) |
if (poll_check(transfer->sockfd, POLLIN)) |
| { |
{ |
| |
union mysockaddr peer; |
| |
socklen_t addr_len = sizeof(union mysockaddr); |
| |
ssize_t len; |
| |
|
| /* we overwrote the buffer... */ |
/* we overwrote the buffer... */ |
| daemon->srv_save = NULL; |
daemon->srv_save = NULL; |
| handle_tftp(now, transfer, recv(transfer->sockfd, daemon->packet, daemon->packet_buff_sz, 0)); |
|
| } |
|
| |
|
| |
if ((len = recvfrom(transfer->sockfd, daemon->packet, daemon->packet_buff_sz, 0, &peer.sa, &addr_len)) > 0) |
| |
{ |
| |
if (sockaddr_isequal(&peer, &transfer->peer)) |
| |
handle_tftp(now, transfer, len); |
| |
else |
| |
{ |
| |
/* Wrong source address. See rfc1350 para 4. */ |
| |
prettyprint_addr(&peer, daemon->addrbuff); |
| |
len = tftp_err(ERR_TID, daemon->packet, _("ignoring packet from %s (TID mismatch)"), daemon->addrbuff, NULL); |
| |
while(retry_send(sendto(transfer->sockfd, daemon->packet, len, 0, &peer.sa, sa_len(&peer)))); |
| |
|
| |
#ifdef HAVE_DUMPFILE |
| |
dump_packet_udp(DUMP_TFTP, (void *)daemon->packet, len, NULL, (union mysockaddr *)&peer, transfer->sockfd); |
| |
#endif |
| |
} |
| |
} |
| |
} |
| |
|
| for (transfer = daemon->tftp_trans, up = &daemon->tftp_trans; transfer; transfer = tmp) |
for (transfer = daemon->tftp_trans, up = &daemon->tftp_trans; transfer; transfer = tmp) |
| { |
{ |
| tmp = transfer->next; |
tmp = transfer->next; |
|
Line 602 void check_tftp_listeners(time_t now)
|
Line 630 void check_tftp_listeners(time_t now)
|
| |
|
| /* we overwrote the buffer... */ |
/* we overwrote the buffer... */ |
| daemon->srv_save = NULL; |
daemon->srv_save = NULL; |
| | |
| if ((len = get_block(daemon->packet, transfer)) == -1) |
if ((len = get_block(daemon->packet, transfer)) == -1) |
| { |
{ |
| len = tftp_err_oops(daemon->packet, transfer->file->filename); |
len = tftp_err_oops(daemon->packet, transfer->file->filename); |
|
Line 618 void check_tftp_listeners(time_t now)
|
Line 646 void check_tftp_listeners(time_t now)
|
| } |
} |
| |
|
| if (len != 0) |
if (len != 0) |
| send_from(transfer->sockfd, !option_bool(OPT_SINGLE_PORT), daemon->packet, len, | { |
| &transfer->peer, &transfer->source, transfer->if_index); | send_from(transfer->sockfd, !option_bool(OPT_SINGLE_PORT), daemon->packet, len, |
| | &transfer->peer, &transfer->source, transfer->if_index); |
| | #ifdef HAVE_DUMPFILE |
| | dump_packet_udp(DUMP_TFTP, (void *)daemon->packet, len, NULL, (union mysockaddr *)&transfer->peer, transfer->sockfd); |
| | #endif |
| | } |
| | |
| if (endcon || len == 0) |
if (endcon || len == 0) |
| { |
{ |
| strcpy(daemon->namebuff, transfer->file->filename); |
strcpy(daemon->namebuff, transfer->file->filename); |
|
Line 726 static void sanitise(char *buf)
|
Line 759 static void sanitise(char *buf)
|
| } |
} |
| |
|
| #define MAXMESSAGE 500 /* limit to make packet < 512 bytes and definitely smaller than buffer */ |
#define MAXMESSAGE 500 /* limit to make packet < 512 bytes and definitely smaller than buffer */ |
| static ssize_t tftp_err(int err, char *packet, char *message, char *file) | static ssize_t tftp_err(int err, char *packet, char *message, char *file, char *arg2) |
| { |
{ |
| struct errmess { |
struct errmess { |
| unsigned short op, err; |
unsigned short op, err; |
| char message[]; |
char message[]; |
| } *mess = (struct errmess *)packet; |
} *mess = (struct errmess *)packet; |
| ssize_t len, ret = 4; |
ssize_t len, ret = 4; |
| char *errstr = strerror(errno); | |
| | |
| memset(packet, 0, daemon->packet_buff_sz); |
memset(packet, 0, daemon->packet_buff_sz); |
| sanitise(file); | if (file) |
| | sanitise(file); |
| |
|
| mess->op = htons(OP_ERR); |
mess->op = htons(OP_ERR); |
| mess->err = htons(err); |
mess->err = htons(err); |
| len = snprintf(mess->message, MAXMESSAGE, message, file, errstr); | len = snprintf(mess->message, MAXMESSAGE, message, file, arg2); |
| ret += (len < MAXMESSAGE) ? len + 1 : MAXMESSAGE; /* include terminating zero */ |
ret += (len < MAXMESSAGE) ? len + 1 : MAXMESSAGE; /* include terminating zero */ |
| |
|
| my_syslog(MS_TFTP | LOG_ERR, "%s", mess->message); | if (err != ERR_FNF || !option_bool(OPT_QUIET_TFTP)) |
| | my_syslog(MS_TFTP | LOG_ERR, "%s", mess->message); |
| |
|
| return ret; |
return ret; |
| } |
} |
| |
|
| static ssize_t tftp_err_oops(char *packet, char *file) | static ssize_t tftp_err_oops(char *packet, const char *file) |
| { |
{ |
| /* May have >1 refs to file, so potentially mangle a copy of the name */ |
/* May have >1 refs to file, so potentially mangle a copy of the name */ |
| strcpy(daemon->namebuff, file); | if (file != daemon->namebuff) |
| return tftp_err(ERR_NOTDEF, packet, _("cannot read %s: %s"), daemon->namebuff); | strcpy(daemon->namebuff, file); |
| | return tftp_err(ERR_NOTDEF, packet, _("cannot read %s: %s"), daemon->namebuff, strerror(errno)); |
| } |
} |
| |
|
| /* return -1 for error, zero for done. */ |
/* return -1 for error, zero for done. */ |