mirror of
http://thekelleys.org.uk/git/dnsmasq.git
synced 2026-11-06 15:51:41 +00:00
Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eb60168382 | ||
|
|
57a2f5778c | ||
|
|
7d5fbe7da3 | ||
|
|
ded935be37 | ||
|
|
aa9b71c681 | ||
|
|
e497f3f2c8 | ||
|
|
ee09f0655c |
No files matched your search
@@ -70,6 +70,12 @@ version 2.92
|
|||||||
is functional when client and server networks aren't mutually
|
is functional when client and server networks aren't mutually
|
||||||
route-able.
|
route-able.
|
||||||
|
|
||||||
|
Fix failure to add client MAC address to queries in TCP mode.
|
||||||
|
The options which cause dnsmasq to decorate a DNS query with the MAC
|
||||||
|
address on the originating client can fail when the query is sent
|
||||||
|
using TCP. Thanks to Bruno Ravara for spotting and
|
||||||
|
characterising this bug.
|
||||||
|
|
||||||
|
|
||||||
version 2.91
|
version 2.91
|
||||||
Fix spurious "resource limit exceeded messages". Thanks to
|
Fix spurious "resource limit exceeded messages". Thanks to
|
||||||
|
|||||||
+2870
File diff suppressed because it is too large.
Load diff
+885
-704
File diff suppressed because it is too large.
Load diff
@@ -111,22 +111,26 @@ int find_mac(union mysockaddr *addr, unsigned char *mac, int lazy, time_t now)
|
|||||||
|
|
||||||
again:
|
again:
|
||||||
|
|
||||||
/* If the database is less then INTERVAL old, look in there */
|
/* If the database is less then INTERVAL old, look in there.
|
||||||
if (difftime(now, last) < INTERVAL)
|
|
||||||
|
If we're a child process, we always rely on the existing cache we
|
||||||
|
inherited from the parent, since we don't have a netlink socket.
|
||||||
|
*/
|
||||||
|
if (difftime(now, last) < INTERVAL || daemon->pipe_to_parent != -1)
|
||||||
{
|
{
|
||||||
/* addr == NULL -> just make cache up-to-date */
|
/* addr == NULL -> just make cache up-to-date */
|
||||||
if (!addr)
|
if (!addr)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
for (arp = arps; arp; arp = arp->next)
|
for (arp = arps; arp; arp = arp->next)
|
||||||
{
|
{
|
||||||
if (addr->sa.sa_family != arp->family)
|
if (addr->sa.sa_family != arp->family)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (arp->family == AF_INET &&
|
if (arp->family == AF_INET &&
|
||||||
arp->addr.addr4.s_addr != addr->in.sin_addr.s_addr)
|
arp->addr.addr4.s_addr != addr->in.sin_addr.s_addr)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (arp->family == AF_INET6 &&
|
if (arp->family == AF_INET6 &&
|
||||||
!IN6_ARE_ADDR_EQUAL(&arp->addr.addr6, &addr->in6.sin6_addr))
|
!IN6_ARE_ADDR_EQUAL(&arp->addr.addr6, &addr->in6.sin6_addr))
|
||||||
continue;
|
continue;
|
||||||
@@ -141,6 +145,10 @@ int find_mac(union mysockaddr *addr, unsigned char *mac, int lazy, time_t now)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Not in cache in child, no go. */
|
||||||
|
if (daemon->pipe_to_parent != -1)
|
||||||
|
return 0;
|
||||||
|
|
||||||
/* Not found, try the kernel */
|
/* Not found, try the kernel */
|
||||||
if (!updated)
|
if (!updated)
|
||||||
{
|
{
|
||||||
|
|||||||
+16
-3
@@ -530,8 +530,8 @@ static DBusMessage *dbus_add_lease(DBusMessage* message)
|
|||||||
union all_addr addr;
|
union all_addr addr;
|
||||||
time_t now = dnsmasq_time();
|
time_t now = dnsmasq_time();
|
||||||
unsigned char dhcp_chaddr[DHCP_CHADDR_MAX];
|
unsigned char dhcp_chaddr[DHCP_CHADDR_MAX];
|
||||||
|
|
||||||
DBusMessageIter iter, array_iter;
|
DBusMessageIter iter, array_iter;
|
||||||
|
|
||||||
if (!dbus_message_iter_init(message, &iter))
|
if (!dbus_message_iter_init(message, &iter))
|
||||||
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
|
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
|
||||||
"Failed to initialize dbus message iter");
|
"Failed to initialize dbus message iter");
|
||||||
@@ -599,6 +599,10 @@ static DBusMessage *dbus_add_lease(DBusMessage* message)
|
|||||||
|
|
||||||
if (inet_pton(AF_INET, ipaddr, &addr.addr4))
|
if (inet_pton(AF_INET, ipaddr, &addr.addr4))
|
||||||
{
|
{
|
||||||
|
if (!daemon->dhcp)
|
||||||
|
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
|
||||||
|
"DHCPv4 not configured");
|
||||||
|
|
||||||
if (ia_id != 0 || is_temporary)
|
if (ia_id != 0 || is_temporary)
|
||||||
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
|
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
|
||||||
"ia_id and is_temporary must be zero for IPv4 lease");
|
"ia_id and is_temporary must be zero for IPv4 lease");
|
||||||
@@ -609,16 +613,25 @@ static DBusMessage *dbus_add_lease(DBusMessage* message)
|
|||||||
#ifdef HAVE_DHCP6
|
#ifdef HAVE_DHCP6
|
||||||
else if (inet_pton(AF_INET6, ipaddr, &addr.addr6))
|
else if (inet_pton(AF_INET6, ipaddr, &addr.addr6))
|
||||||
{
|
{
|
||||||
|
if (!daemon->doing_dhcp6)
|
||||||
|
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
|
||||||
|
"DHCPv6 not configured");
|
||||||
|
|
||||||
if (!(lease = lease6_find_by_addr(&addr.addr6, 128, 0)))
|
if (!(lease = lease6_find_by_addr(&addr.addr6, 128, 0)))
|
||||||
lease = lease6_allocate(&addr.addr6,
|
lease = lease6_allocate(&addr.addr6,
|
||||||
is_temporary ? LEASE_TA : LEASE_NA);
|
is_temporary ? LEASE_TA : LEASE_NA);
|
||||||
lease_set_iaid(lease, ia_id);
|
if (lease)
|
||||||
|
lease_set_iaid(lease, ia_id);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
else
|
else
|
||||||
return dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS,
|
return dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS,
|
||||||
"Invalid IP address '%s'", ipaddr);
|
"Invalid IP address '%s'", ipaddr);
|
||||||
|
|
||||||
|
if (!lease)
|
||||||
|
return dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS,
|
||||||
|
"unable to allocate lease for IP address '%s'", ipaddr);
|
||||||
|
|
||||||
hw_len = parse_hex((char*)hwaddr, dhcp_chaddr, DHCP_CHADDR_MAX, NULL, &hw_type);
|
hw_len = parse_hex((char*)hwaddr, dhcp_chaddr, DHCP_CHADDR_MAX, NULL, &hw_type);
|
||||||
if (hw_len < 0)
|
if (hw_len < 0)
|
||||||
return dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS,
|
return dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS,
|
||||||
|
|||||||
+3
-1
@@ -709,7 +709,8 @@ static const struct opttab_t {
|
|||||||
{ "client-machine-id", 97, 0 },
|
{ "client-machine-id", 97, 0 },
|
||||||
{ "posix-timezone", 100, OT_NAME }, /* RFC 4833, Sec. 2 */
|
{ "posix-timezone", 100, OT_NAME }, /* RFC 4833, Sec. 2 */
|
||||||
{ "tzdb-timezone", 101, OT_NAME }, /* RFC 4833, Sec. 2 */
|
{ "tzdb-timezone", 101, OT_NAME }, /* RFC 4833, Sec. 2 */
|
||||||
{ "ipv6-only", 108, 4 | OT_DEC }, /* RFC 8925 */
|
{ "ipv6-only", 108, 4 | OT_DEC }, /* RFC 8925 */
|
||||||
|
{ "captive-portal", 114, OT_NAME }, /* RFC 8910 */
|
||||||
{ "subnet-select", 118, OT_INTERNAL },
|
{ "subnet-select", 118, OT_INTERNAL },
|
||||||
{ "domain-search", 119, OT_RFC1035_NAME },
|
{ "domain-search", 119, OT_RFC1035_NAME },
|
||||||
{ "sip-server", 120, 0 },
|
{ "sip-server", 120, 0 },
|
||||||
@@ -751,6 +752,7 @@ static const struct opttab_t opttab6[] = {
|
|||||||
{ "ntp-server", 56, 0 /* OT_ADDR_LIST | OT_RFC1035_NAME */ },
|
{ "ntp-server", 56, 0 /* OT_ADDR_LIST | OT_RFC1035_NAME */ },
|
||||||
{ "bootfile-url", 59, OT_NAME },
|
{ "bootfile-url", 59, OT_NAME },
|
||||||
{ "bootfile-param", 60, OT_CSTRING },
|
{ "bootfile-param", 60, OT_CSTRING },
|
||||||
|
{ "captive-portal", 103, OT_NAME }, /* RFC 8910 */
|
||||||
{ NULL, 0, 0 }
|
{ NULL, 0, 0 }
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+1
-1
@@ -696,7 +696,7 @@ struct dhcp_context *address_available(struct dhcp_context *context,
|
|||||||
struct dhcp_context *tmp;
|
struct dhcp_context *tmp;
|
||||||
|
|
||||||
for (tmp = context; tmp; tmp = tmp->current)
|
for (tmp = context; tmp; tmp = tmp->current)
|
||||||
if (taddr.s_addr == context->router.s_addr)
|
if (taddr.s_addr == tmp->router.s_addr)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
for (tmp = context; tmp; tmp = tmp->current)
|
for (tmp = context; tmp; tmp = tmp->current)
|
||||||
|
|||||||
+16
-1
@@ -2033,9 +2033,24 @@ static void do_tcp_connection(struct listener *listener, time_t now, int slot)
|
|||||||
|
|
||||||
if (!option_bool(OPT_DEBUG))
|
if (!option_bool(OPT_DEBUG))
|
||||||
{
|
{
|
||||||
|
/* The code in edns0.c qthat decorates queries with the source MAC address depends
|
||||||
|
on the code in arp.c, which populates a cache with the contents of the ARP table
|
||||||
|
using netlink. Since the child process can't use netlink, we pre-populate
|
||||||
|
the cache with the ARP table entry for our source here, including a negative entry
|
||||||
|
if there is nothing for our address in the ARP table.
|
||||||
|
|
||||||
|
When the edns0 code calls find_mac() in the child process, it will
|
||||||
|
get the correct answer from the cache inherited from the parent
|
||||||
|
without having to use netlink to consult the kernel ARP table.
|
||||||
|
|
||||||
|
edns0_needs_mac() simply calls find_mac if any EDNS0 options
|
||||||
|
which need a MAC address are enabled. */
|
||||||
|
|
||||||
|
edns0_needs_mac(&tcp_addr, now);
|
||||||
|
|
||||||
if (pipe(pipefd) == -1)
|
if (pipe(pipefd) == -1)
|
||||||
goto closeconandreturn; /* pipe failed */
|
goto closeconandreturn; /* pipe failed */
|
||||||
|
|
||||||
if ((p = fork()) == -1)
|
if ((p = fork()) == -1)
|
||||||
{
|
{
|
||||||
/* fork failed */
|
/* fork failed */
|
||||||
|
|||||||
+4
-3
@@ -1109,7 +1109,7 @@ struct ping_result {
|
|||||||
|
|
||||||
struct tftp_file {
|
struct tftp_file {
|
||||||
int refcount, fd;
|
int refcount, fd;
|
||||||
off_t size;
|
off_t size, posn;
|
||||||
dev_t dev;
|
dev_t dev;
|
||||||
ino_t inode;
|
ino_t inode;
|
||||||
char filename[];
|
char filename[];
|
||||||
@@ -1124,7 +1124,7 @@ struct tftp_transfer {
|
|||||||
union mysockaddr peer;
|
union mysockaddr peer;
|
||||||
union all_addr source;
|
union all_addr source;
|
||||||
int if_index;
|
int if_index;
|
||||||
unsigned char opt_blocksize, opt_transize, opt_windowsize, opt_timeout, netascii, carrylf, backoff;
|
unsigned char opt_blocksize, opt_transize, opt_windowsize, opt_timeout, netascii, carrylf, lastcarrylf, backoff;
|
||||||
struct tftp_file *file;
|
struct tftp_file *file;
|
||||||
struct tftp_transfer *next;
|
struct tftp_transfer *next;
|
||||||
};
|
};
|
||||||
@@ -1278,7 +1278,7 @@ extern struct daemon {
|
|||||||
struct serverfd *sfds;
|
struct serverfd *sfds;
|
||||||
struct irec *interfaces;
|
struct irec *interfaces;
|
||||||
struct listener *listeners;
|
struct listener *listeners;
|
||||||
struct server *srv_save; /* Used for resend on DoD */
|
void *srv_save; /* Used for resend on DoD and tftp prefetch */
|
||||||
size_t packet_len; /* " " */
|
size_t packet_len; /* " " */
|
||||||
int fd_save; /* " " */
|
int fd_save; /* " " */
|
||||||
pid_t *tcp_pids;
|
pid_t *tcp_pids;
|
||||||
@@ -1918,6 +1918,7 @@ unsigned char *find_pseudoheader(struct dns_header *header, size_t plen,
|
|||||||
size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *limit,
|
size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *limit,
|
||||||
int optno, unsigned char *opt, size_t optlen, int set_do, int replace);
|
int optno, unsigned char *opt, size_t optlen, int set_do, int replace);
|
||||||
size_t add_do_bit(struct dns_header *header, size_t plen, unsigned char *limit);
|
size_t add_do_bit(struct dns_header *header, size_t plen, unsigned char *limit);
|
||||||
|
void edns0_needs_mac(union mysockaddr *addr, time_t now);
|
||||||
size_t add_edns0_config(struct dns_header *header, size_t plen, unsigned char *limit,
|
size_t add_edns0_config(struct dns_header *header, size_t plen, unsigned char *limit,
|
||||||
union mysockaddr *source, time_t now, int *cacheable);
|
union mysockaddr *source, time_t now, int *cacheable);
|
||||||
int check_source(struct dns_header *header, size_t plen, unsigned char *pseudoheader, union mysockaddr *peer);
|
int check_source(struct dns_header *header, size_t plen, unsigned char *pseudoheader, union mysockaddr *peer);
|
||||||
|
|||||||
+10
@@ -265,6 +265,15 @@ static void encoder(unsigned char *in, char *out)
|
|||||||
out[3] = char64(in[2]);
|
out[3] = char64(in[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* This function needs to call find_mac if any option which requires a MAC address is enabled
|
||||||
|
and used below. If you add a new MAC consumer, modify this, otherwise your
|
||||||
|
new EDNS0 option won't work in TCP mode. */
|
||||||
|
void edns0_needs_mac(union mysockaddr *addr, time_t now)
|
||||||
|
{
|
||||||
|
if (option_bool(OPT_MAC_B64) || option_bool(OPT_MAC_HEX) || option_bool(OPT_ADD_MAC))
|
||||||
|
find_mac(addr, NULL, 0, now);
|
||||||
|
}
|
||||||
|
|
||||||
/* OPT_ADD_MAC = MAC is added (if available)
|
/* OPT_ADD_MAC = MAC is added (if available)
|
||||||
OPT_ADD_MAC + OPT_STRIP_MAC = MAC is replaced, if not available, it is only removed
|
OPT_ADD_MAC + OPT_STRIP_MAC = MAC is replaced, if not available, it is only removed
|
||||||
OPT_STRIP_MAC = MAC is removed */
|
OPT_STRIP_MAC = MAC is removed */
|
||||||
@@ -562,3 +571,4 @@ size_t add_edns0_config(struct dns_header *header, size_t plen, unsigned char *l
|
|||||||
|
|
||||||
return plen;
|
return plen;
|
||||||
}
|
}
|
||||||
|
|
||||||
+108
-88
@@ -18,12 +18,12 @@
|
|||||||
|
|
||||||
#ifdef HAVE_TFTP
|
#ifdef HAVE_TFTP
|
||||||
|
|
||||||
static void handle_tftp(time_t now, struct tftp_transfer *transfer, ssize_t len);
|
static void handle_tftp(char *packet, time_t now, struct tftp_transfer *transfer, ssize_t len);
|
||||||
static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix, char *client);
|
static struct tftp_file *check_tftp_fileperm(char *packet, 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, char *arg2);
|
static ssize_t tftp_err(int err, char *packet, char *message, char *file, char *arg2);
|
||||||
static ssize_t tftp_err_oops(char *packet, const 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(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);
|
||||||
|
|
||||||
@@ -41,10 +41,9 @@ static void sanitise(char *buf);
|
|||||||
#define ERR_ILL 4
|
#define ERR_ILL 4
|
||||||
#define ERR_TID 5
|
#define ERR_TID 5
|
||||||
|
|
||||||
static void tftp_request(struct listener *listen, time_t now)
|
static void tftp_request(char *packet, ssize_t plen, struct listener *listen, time_t now)
|
||||||
{
|
{
|
||||||
ssize_t len;
|
ssize_t len;
|
||||||
char *packet = daemon->packet;
|
|
||||||
char *filename, *mode, *p, *end;
|
char *filename, *mode, *p, *end;
|
||||||
union mysockaddr addr, peer;
|
union mysockaddr addr, peer;
|
||||||
struct msghdr msg;
|
struct msghdr msg;
|
||||||
@@ -87,12 +86,10 @@ static void tftp_request(struct listener *listen, time_t now)
|
|||||||
msg.msg_iov = &iov;
|
msg.msg_iov = &iov;
|
||||||
msg.msg_iovlen = 1;
|
msg.msg_iovlen = 1;
|
||||||
|
|
||||||
|
/* packet buff is DNS name workspace. */
|
||||||
iov.iov_base = packet;
|
iov.iov_base = packet;
|
||||||
iov.iov_len = daemon->packet_buff_sz;
|
iov.iov_len = plen;
|
||||||
|
|
||||||
/* we overwrote the buffer... */
|
|
||||||
daemon->srv_save = NULL;
|
|
||||||
|
|
||||||
if ((len = recvmsg(listen->tftpfd, &msg, 0)) < 2)
|
if ((len = recvmsg(listen->tftpfd, &msg, 0)) < 2)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -267,7 +264,7 @@ static void tftp_request(struct listener *listen, time_t now)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
handle_tftp(now, transfer, len);
|
handle_tftp(packet, now, transfer, len);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -501,17 +498,21 @@ static 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, daemon->addrbuff)))
|
if ((transfer->file = check_tftp_fileperm(packet, &len, prefix, daemon->addrbuff)))
|
||||||
{
|
{
|
||||||
transfer->lastack = transfer->block;
|
transfer->lastack = transfer->block;
|
||||||
transfer->retransmit = now + transfer->timeout;
|
transfer->retransmit = now + transfer->timeout;
|
||||||
/* This packet is may be the first data packet, but only if windowsize == 1
|
/* This packet is may be the first data packet, but only if windowsize == 1
|
||||||
To get windowsize greater then one requires an option negotiation,
|
To get windowsize greater then one requires an option negotiation,
|
||||||
in which case this packet is the OACK. */
|
in which case this packet is the OACK. */
|
||||||
if ((len = get_block(packet, transfer)) == -1)
|
if ((len = get_block(transfer)) == -1)
|
||||||
len = tftp_err_oops(packet, daemon->namebuff);
|
len = tftp_err_oops(packet, daemon->namebuff);
|
||||||
else
|
else
|
||||||
is_err = 0;
|
{
|
||||||
|
is_err = 0;
|
||||||
|
/* get_block put the packet to send in a different buffer. */
|
||||||
|
packet = daemon->packet;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -534,9 +535,9 @@ static void tftp_request(struct listener *listen, time_t now)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix, char *client)
|
static struct tftp_file *check_tftp_fileperm(char *packet, ssize_t *len, char *prefix, char *client)
|
||||||
{
|
{
|
||||||
char *packet = daemon->packet, *namebuff = daemon->namebuff;
|
char *namebuff = daemon->namebuff;
|
||||||
struct tftp_file *file;
|
struct tftp_file *file;
|
||||||
struct tftp_transfer *t;
|
struct tftp_transfer *t;
|
||||||
uid_t uid = geteuid();
|
uid_t uid = geteuid();
|
||||||
@@ -599,6 +600,7 @@ static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix, char *c
|
|||||||
file->size = statbuf.st_size;
|
file->size = statbuf.st_size;
|
||||||
file->dev = statbuf.st_dev;
|
file->dev = statbuf.st_dev;
|
||||||
file->inode = statbuf.st_ino;
|
file->inode = statbuf.st_ino;
|
||||||
|
file->posn = 0;
|
||||||
file->refcount = 1;
|
file->refcount = 1;
|
||||||
strcpy(file->filename, namebuff);
|
strcpy(file->filename, namebuff);
|
||||||
return file;
|
return file;
|
||||||
@@ -618,12 +620,15 @@ static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix, char *c
|
|||||||
|
|
||||||
void check_tftp_listeners(time_t now)
|
void check_tftp_listeners(time_t now)
|
||||||
{
|
{
|
||||||
|
/* Use workspace to receive (small) request/ACK, to avoid overwriting precomputed reply */
|
||||||
|
char *packet = daemon->workspacename;
|
||||||
|
ssize_t plen = MAXDNAME * 2;
|
||||||
struct listener *listener;
|
struct listener *listener;
|
||||||
struct tftp_transfer *transfer, *tmp, **up;
|
struct tftp_transfer *transfer, *tmp, **up;
|
||||||
|
|
||||||
for (listener = daemon->listeners; listener; listener = listener->next)
|
for (listener = daemon->listeners; listener; listener = listener->next)
|
||||||
if (listener->tftpfd != -1 && poll_check(listener->tftpfd, POLLIN))
|
if (listener->tftpfd != -1 && poll_check(listener->tftpfd, POLLIN))
|
||||||
tftp_request(listener, now);
|
tftp_request(packet, plen, listener, now);
|
||||||
|
|
||||||
/* In single port mode, all packets come via port 69 and tftp_request() */
|
/* In single port mode, all packets come via port 69 and tftp_request() */
|
||||||
if (!option_bool(OPT_SINGLE_PORT))
|
if (!option_bool(OPT_SINGLE_PORT))
|
||||||
@@ -634,26 +639,23 @@ void check_tftp_listeners(time_t now)
|
|||||||
socklen_t addr_len = sizeof(union mysockaddr);
|
socklen_t addr_len = sizeof(union mysockaddr);
|
||||||
ssize_t len;
|
ssize_t len;
|
||||||
|
|
||||||
/* we overwrote the buffer... */
|
if ((len = recvfrom(transfer->sockfd, packet, plen, 0, &peer.sa, &addr_len)) > 0)
|
||||||
daemon->srv_save = NULL;
|
|
||||||
|
|
||||||
if ((len = recvfrom(transfer->sockfd, daemon->packet, daemon->packet_buff_sz, 0, &peer.sa, &addr_len)) > 0)
|
|
||||||
{
|
{
|
||||||
#ifdef HAVE_DUMPFILE
|
#ifdef HAVE_DUMPFILE
|
||||||
dump_packet_udp(DUMP_TFTP, (void *)daemon->packet, len, (union mysockaddr *)&peer, NULL, transfer->sockfd);
|
dump_packet_udp(DUMP_TFTP, (void *)packet, len, (union mysockaddr *)&peer, NULL, transfer->sockfd);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (sockaddr_isequal(&peer, &transfer->peer))
|
if (sockaddr_isequal(&peer, &transfer->peer))
|
||||||
handle_tftp(now, transfer, len);
|
handle_tftp(packet, now, transfer, len);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* Wrong source address. See rfc1350 para 4. */
|
/* Wrong source address. See rfc1350 para 4. */
|
||||||
prettyprint_addr(&peer, daemon->addrbuff);
|
prettyprint_addr(&peer, daemon->addrbuff);
|
||||||
len = tftp_err(ERR_TID, daemon->packet, _("ignoring packet from %s (TID mismatch)"), daemon->addrbuff, NULL);
|
len = tftp_err(ERR_TID, 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))));
|
while(retry_send(sendto(transfer->sockfd, packet, len, 0, &peer.sa, sa_len(&peer))));
|
||||||
|
|
||||||
#ifdef HAVE_DUMPFILE
|
#ifdef HAVE_DUMPFILE
|
||||||
dump_packet_udp(DUMP_TFTP, (void *)daemon->packet, len, NULL, (union mysockaddr *)&peer, transfer->sockfd);
|
dump_packet_udp(DUMP_TFTP, (void *)packet, len, NULL, (union mysockaddr *)&peer, transfer->sockfd);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -673,7 +675,7 @@ void check_tftp_listeners(time_t now)
|
|||||||
endcon = 1;
|
endcon = 1;
|
||||||
/* don't complain about timeout when we're awaiting the last
|
/* don't complain about timeout when we're awaiting the last
|
||||||
ACK, some clients never send it */
|
ACK, some clients never send it */
|
||||||
if (get_block(daemon->packet, transfer) > 0)
|
if (get_block(transfer) > 0)
|
||||||
error = timeout = 1;
|
error = timeout = 1;
|
||||||
}
|
}
|
||||||
else if (difftime(now, transfer->retransmit) >= 0.0)
|
else if (difftime(now, transfer->retransmit) >= 0.0)
|
||||||
@@ -682,41 +684,43 @@ void check_tftp_listeners(time_t now)
|
|||||||
bumps transfer->lastack and trips the retransmit timer so that we send the next block(s)
|
bumps transfer->lastack and trips the retransmit timer so that we send the next block(s)
|
||||||
here. */
|
here. */
|
||||||
ssize_t len;
|
ssize_t len;
|
||||||
|
unsigned int i, winsize;
|
||||||
|
|
||||||
transfer->retransmit += transfer->timeout + (1<<(transfer->backoff/2));
|
transfer->retransmit += transfer->timeout + (1<<(transfer->backoff/2));
|
||||||
transfer->backoff++;
|
transfer->backoff++;
|
||||||
transfer->block = transfer->lastack;
|
transfer->block = transfer->lastack;
|
||||||
|
|
||||||
if ((len = get_block(daemon->packet, transfer)) == 0)
|
/* send a window'a worth of blocks unless we're retransmitting OACK */
|
||||||
endcon = 1; /* got last ACK */
|
winsize = transfer->block ? transfer->windowsize : 1;
|
||||||
else
|
|
||||||
|
for (i = 0; i < winsize; i++, transfer->block++)
|
||||||
{
|
{
|
||||||
/* send a window'a worth of blocks unless we're retransmitting OACK */
|
if ((len = get_block(transfer)) == 0)
|
||||||
unsigned int i, winsize = transfer->block ? transfer->windowsize : 1;
|
|
||||||
|
|
||||||
for (i = 0; i < winsize && !endcon; i++, transfer->block++)
|
|
||||||
{
|
{
|
||||||
if (i != 0)
|
if (i == 0)
|
||||||
len = get_block(daemon->packet, transfer);
|
endcon = 1; /* got last ACK */
|
||||||
|
|
||||||
if (len == 0)
|
break;
|
||||||
break;
|
|
||||||
|
|
||||||
if (len == -1)
|
|
||||||
{
|
|
||||||
len = tftp_err_oops(daemon->packet, transfer->file->filename);
|
|
||||||
endcon = error = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 (len == -1)
|
||||||
|
{
|
||||||
|
len = tftp_err_oops(daemon->packet, transfer->file->filename);
|
||||||
|
endcon = error = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
/* prefetch the block we'll probably need when we get an ACK. */
|
||||||
|
if (!endcon)
|
||||||
|
get_block(transfer);
|
||||||
|
}
|
||||||
|
|
||||||
if (endcon)
|
if (endcon)
|
||||||
{
|
{
|
||||||
strcpy(daemon->namebuff, transfer->file->filename);
|
strcpy(daemon->namebuff, transfer->file->filename);
|
||||||
@@ -745,12 +749,11 @@ void check_tftp_listeners(time_t now)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* packet in daemon->packet as this is called. */
|
static void handle_tftp(char *packet, time_t now, struct tftp_transfer *transfer, ssize_t len)
|
||||||
static void handle_tftp(time_t now, struct tftp_transfer *transfer, ssize_t len)
|
|
||||||
{
|
{
|
||||||
struct ack {
|
struct ack {
|
||||||
unsigned short op, block;
|
unsigned short op, block;
|
||||||
} *mess = (struct ack *)daemon->packet;
|
} *mess = (struct ack *)packet;
|
||||||
|
|
||||||
if (len >= (ssize_t)sizeof(struct ack))
|
if (len >= (ssize_t)sizeof(struct ack))
|
||||||
{
|
{
|
||||||
@@ -774,7 +777,7 @@ static void handle_tftp(time_t now, struct tftp_transfer *transfer, ssize_t len)
|
|||||||
|
|
||||||
transfer->ackprev = new;
|
transfer->ackprev = new;
|
||||||
block = (((u32)transfer->block_hi) << 16) + (u32)new;
|
block = (((u32)transfer->block_hi) << 16) + (u32)new;
|
||||||
|
|
||||||
/* Ignore duplicate ACKs and ACKs for blocks we've not yet sent. */
|
/* Ignore duplicate ACKs and ACKs for blocks we've not yet sent. */
|
||||||
if (block >= transfer->lastack &&
|
if (block >= transfer->lastack &&
|
||||||
block <= transfer->block)
|
block <= transfer->block)
|
||||||
@@ -783,20 +786,23 @@ static void handle_tftp(time_t now, struct tftp_transfer *transfer, ssize_t len)
|
|||||||
transfer->retransmit = transfer->start = now;
|
transfer->retransmit = transfer->start = now;
|
||||||
transfer->backoff = 0;
|
transfer->backoff = 0;
|
||||||
transfer->lastack = block + 1;
|
transfer->lastack = block + 1;
|
||||||
|
|
||||||
/* We have no easy function from block no. to file offset when
|
/* We have no easy function from block no. to file offset when
|
||||||
expanding line breaks in netascii mode, so we update the offset here
|
expanding line breaks in netascii mode, so we update the offset here
|
||||||
as each block is acknowledged. This explains why the window size must be
|
as each block is acknowledged. This explains why the window size must be
|
||||||
one for a netascii transfer; to avoid the block no. doing anything
|
one for a netascii transfer; to avoid the block no. doing anything
|
||||||
other than incrementing by one. */
|
other than incrementing by one. */
|
||||||
if (transfer->netascii && block != 0)
|
if (transfer->netascii && block != 0)
|
||||||
transfer->offset += transfer->blocksize - transfer->expansion;
|
{
|
||||||
|
transfer->offset += (off_t)transfer->blocksize - (off_t)transfer->expansion;
|
||||||
|
transfer->lastcarrylf = transfer->carrylf;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (ntohs(mess->op) == OP_ERR)
|
else if (ntohs(mess->op) == OP_ERR)
|
||||||
{
|
{
|
||||||
char *p = daemon->packet + sizeof(struct ack);
|
char *p = packet + sizeof(struct ack);
|
||||||
char *end = daemon->packet + len;
|
char *end = packet + len;
|
||||||
char *err = next(&p, end);
|
char *err = next(&p, end);
|
||||||
|
|
||||||
(void)prettyprint_addr(&transfer->peer, daemon->addrbuff);
|
(void)prettyprint_addr(&transfer->peer, daemon->addrbuff);
|
||||||
@@ -873,10 +879,6 @@ static ssize_t tftp_err(int err, char *packet, char *message, char *file, char *
|
|||||||
} *mess = (struct errmess *)packet;
|
} *mess = (struct errmess *)packet;
|
||||||
ssize_t len, ret = 4;
|
ssize_t len, ret = 4;
|
||||||
|
|
||||||
/* we overwrote the buffer... */
|
|
||||||
daemon->srv_save = NULL;
|
|
||||||
|
|
||||||
memset(packet, 0, daemon->packet_buff_sz);
|
|
||||||
if (file)
|
if (file)
|
||||||
sanitise(file);
|
sanitise(file);
|
||||||
|
|
||||||
@@ -900,12 +902,10 @@ static ssize_t tftp_err_oops(char *packet, const char *file)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* return -1 for error, zero for done. */
|
/* return -1 for error, zero for done. */
|
||||||
static ssize_t get_block(char *packet, struct tftp_transfer *transfer)
|
static ssize_t get_block(struct tftp_transfer *transfer)
|
||||||
{
|
{
|
||||||
memset(packet, 0, daemon->packet_buff_sz);
|
static off_t saved_offset = 0;
|
||||||
|
static ssize_t saved_len = 0;
|
||||||
/* we overwrote the buffer... */
|
|
||||||
daemon->srv_save = NULL;
|
|
||||||
|
|
||||||
if (transfer->block == 0)
|
if (transfer->block == 0)
|
||||||
{
|
{
|
||||||
@@ -914,8 +914,12 @@ static ssize_t get_block(char *packet, struct tftp_transfer *transfer)
|
|||||||
struct oackmess {
|
struct oackmess {
|
||||||
unsigned short op;
|
unsigned short op;
|
||||||
char data[];
|
char data[];
|
||||||
} *mess = (struct oackmess *)packet;
|
} *mess = (struct oackmess *)daemon->packet;
|
||||||
|
|
||||||
|
/* we overwrote the buffer... */
|
||||||
|
daemon->srv_save = NULL;
|
||||||
|
memset(daemon->packet, 0, daemon->packet_buff_sz);
|
||||||
|
|
||||||
p = mess->data;
|
p = mess->data;
|
||||||
mess->op = htons(OP_OACK);
|
mess->op = htons(OP_OACK);
|
||||||
if (transfer->opt_blocksize)
|
if (transfer->opt_blocksize)
|
||||||
@@ -939,7 +943,7 @@ static ssize_t get_block(char *packet, struct tftp_transfer *transfer)
|
|||||||
p += (sprintf(p, "%u", (unsigned int)transfer->windowsize) + 1);
|
p += (sprintf(p, "%u", (unsigned int)transfer->windowsize) + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return p - packet;
|
return p - daemon->packet;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -947,7 +951,7 @@ static ssize_t get_block(char *packet, struct tftp_transfer *transfer)
|
|||||||
struct datamess {
|
struct datamess {
|
||||||
unsigned short op, block;
|
unsigned short op, block;
|
||||||
unsigned char data[];
|
unsigned char data[];
|
||||||
} *mess = (struct datamess *)packet;
|
} *mess = (struct datamess *)daemon->packet;
|
||||||
|
|
||||||
size_t size;
|
size_t size;
|
||||||
|
|
||||||
@@ -956,35 +960,49 @@ static ssize_t get_block(char *packet, struct tftp_transfer *transfer)
|
|||||||
|
|
||||||
if (transfer->offset > transfer->file->size)
|
if (transfer->offset > transfer->file->size)
|
||||||
return 0; /* finished */
|
return 0; /* finished */
|
||||||
|
|
||||||
|
/* We may have a prefetched block already in the buffer. */
|
||||||
|
if (daemon->srv_save == transfer && saved_offset == transfer->offset)
|
||||||
|
return saved_len;
|
||||||
|
|
||||||
|
/* we overwrote the buffer... */
|
||||||
|
daemon->srv_save = NULL;
|
||||||
|
|
||||||
if ((size = transfer->file->size - transfer->offset) > (size_t)transfer->blocksize)
|
if ((size = transfer->file->size - transfer->offset) > (size_t)transfer->blocksize)
|
||||||
size = (size_t)transfer->blocksize;
|
size = (size_t)transfer->blocksize;
|
||||||
|
|
||||||
mess->op = htons(OP_DATA);
|
mess->op = htons(OP_DATA);
|
||||||
mess->block = htons((unsigned short)(transfer->block));
|
mess->block = htons((unsigned short)(transfer->block));
|
||||||
|
|
||||||
if (size != 0 &&
|
if (size != 0)
|
||||||
(lseek(transfer->file->fd, transfer->offset, SEEK_SET) == (off_t)-1 ||
|
{
|
||||||
!read_write(transfer->file->fd, mess->data, size, RW_READ)))
|
if (transfer->file->posn != transfer->offset &&
|
||||||
return -1;
|
lseek(transfer->file->fd, transfer->offset, SEEK_SET) == (off_t)-1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (!read_write(transfer->file->fd, mess->data, size, RW_READ))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
transfer->file->posn = transfer->offset + size;
|
||||||
|
}
|
||||||
|
|
||||||
/* Map '\n' to CR-LF in netascii mode */
|
/* Map '\n' to CR-LF in netascii mode */
|
||||||
if (transfer->netascii)
|
if (transfer->netascii)
|
||||||
{
|
{
|
||||||
size_t i;
|
size_t i;
|
||||||
int newcarrylf;
|
|
||||||
|
/* Map '\n' to CR-LF in netascii mode */
|
||||||
|
transfer->expansion = transfer->carrylf = 0;
|
||||||
|
|
||||||
transfer->expansion = 0;
|
for (i = 0; i < size; i++)
|
||||||
|
if (mess->data[i] == '\n' && (i != 0 || !transfer->lastcarrylf))
|
||||||
for (i = 0, newcarrylf = 0; i < size; i++)
|
|
||||||
if (mess->data[i] == '\n' && (i != 0 || !transfer->carrylf))
|
|
||||||
{
|
{
|
||||||
transfer->expansion++;
|
transfer->expansion++;
|
||||||
|
|
||||||
if (size != transfer->blocksize)
|
if (size != transfer->blocksize)
|
||||||
size++; /* room in this block */
|
size++; /* room in this block */
|
||||||
else if (i == size - 1)
|
else if (i == size - 1)
|
||||||
newcarrylf = 1; /* don't expand LF again if it moves to the next block */
|
transfer->carrylf = 1; /* don't expand LF again if it moves to the next block */
|
||||||
|
|
||||||
/* make space and insert CR */
|
/* make space and insert CR */
|
||||||
memmove(&mess->data[i+1], &mess->data[i], size - (i + 1));
|
memmove(&mess->data[i+1], &mess->data[i], size - (i + 1));
|
||||||
@@ -992,11 +1010,13 @@ static ssize_t get_block(char *packet, struct tftp_transfer *transfer)
|
|||||||
|
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
transfer->carrylf = newcarrylf;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return size + 4;
|
daemon->srv_save = transfer;
|
||||||
|
saved_offset = transfer->offset;
|
||||||
|
saved_len = size + 4;
|
||||||
|
|
||||||
|
return saved_len;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user