diff --git a/CHANGELOG b/CHANGELOG index 7fa39b8..3ad5e47 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -79,6 +79,9 @@ version 2.77 Make --bogus-priv apply to IPv6, for the prefixes specified in RFC6303. Thanks to Kevin Darbyshire-Bryant for work on this. + + Allow use of MAC addresses with --tftp-unique-root. Thanks + to Floris Bos for the patch. version 2.76 diff --git a/man/dnsmasq.8 b/man/dnsmasq.8 index 2e5ef21..f7163e5 100644 --- a/man/dnsmasq.8 +++ b/man/dnsmasq.8 @@ -1808,12 +1808,16 @@ directory is only used for TFTP requests via that interface. .B --tftp-no-fail Do not abort startup if specified tftp root directories are inaccessible. .TP -.B --tftp-unique-root -Add the IP address of the TFTP client as a path component on the end -of the TFTP-root (in standard dotted-quad format). Only valid if a -tftp-root is set and the directory exists. For instance, if tftp-root is "/tftp" and client -1.2.3.4 requests file "myfile" then the effective path will be -"/tftp/1.2.3.4/myfile" if /tftp/1.2.3.4 exists or /tftp/myfile otherwise. +.B --tftp-unique-root[=ip|mac] +Add the IP or hardware address of the TFTP client as a path component on the end +of the TFTP-root. Only valid if a tftp-root is set and the directory exists. +Defaults to adding IP address (in standard dotted-quad format). +For instance, if tftp-root is "/tftp" and client 1.2.3.4 requests file "myfile" +then the effective path will be "/tftp/1.2.3.4/myfile" if /tftp/1.2.3.4 exists or /tftp/myfile otherwise. +When "=mac" is specified it will append the MAC address instead, using lowercase zero padded digits +separated by dashes, e.g.: 01-02-03-04-aa-bb +Note that resolving MAC addresses is only possible if the client is in the local network or obtained +a DHCP lease from us. .TP .B --tftp-secure Enable TFTP secure mode: without this, any file which is readable by diff --git a/src/dnsmasq.h b/src/dnsmasq.h index 6b44e53..1fc3bec 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -211,7 +211,7 @@ struct event_desc { #define OPT_TFTP_SECURE 26 #define OPT_TFTP_NOBLOCK 27 #define OPT_LOG_OPTS 28 -#define OPT_TFTP_APREF 29 +#define OPT_TFTP_APREF_IP 29 #define OPT_NO_OVERRIDE 30 #define OPT_NO_REBIND 31 #define OPT_ADD_MAC 32 @@ -238,7 +238,8 @@ struct event_desc { #define OPT_SCRIPT_ARP 53 #define OPT_MAC_B64 54 #define OPT_MAC_HEX 55 -#define OPT_LAST 56 +#define OPT_TFTP_APREF_MAC 56 +#define OPT_LAST 57 /* extra flags for my_syslog, we use a couple of facilities since they are known not to occupy the same bits as priorities, no matter how syslog.h is set up. */ diff --git a/src/option.c b/src/option.c index 0c38db3..a3ddec1 100644 --- a/src/option.c +++ b/src/option.c @@ -242,7 +242,7 @@ static const struct myoption opts[] = { "enable-tftp", 2, 0, LOPT_TFTP }, { "tftp-secure", 0, 0, LOPT_SECURE }, { "tftp-no-fail", 0, 0, LOPT_TFTP_NO_FAIL }, - { "tftp-unique-root", 0, 0, LOPT_APREF }, + { "tftp-unique-root", 2, 0, LOPT_APREF }, { "tftp-root", 1, 0, LOPT_PREFIX }, { "tftp-max", 1, 0, LOPT_TFTP_MAX }, { "tftp-mtu", 1, 0, LOPT_TFTP_MTU }, @@ -430,7 +430,7 @@ static struct { { LOPT_OVERRIDE, OPT_NO_OVERRIDE, NULL, gettext_noop("Do NOT reuse filename and server fields for extra DHCP options."), NULL }, { LOPT_TFTP, ARG_DUP, "[=[,]]", gettext_noop("Enable integrated read-only TFTP server."), NULL }, { LOPT_PREFIX, ARG_DUP, "[,]", gettext_noop("Export files by TFTP only from the specified subtree."), NULL }, - { LOPT_APREF, OPT_TFTP_APREF, NULL, gettext_noop("Add client IP address to tftp-root."), NULL }, + { LOPT_APREF, ARG_DUP, "[=ip|mac]", gettext_noop("Add client IP or hardware address to tftp-root."), NULL }, { LOPT_SECURE, OPT_TFTP_SECURE, NULL, gettext_noop("Allow access only to files owned by the user running dnsmasq."), NULL }, { LOPT_TFTP_NO_FAIL, OPT_TFTP_NO_FAIL, NULL, gettext_noop("Do not terminate the service if TFTP directories are inaccessible."), NULL }, { LOPT_TFTP_MAX, ARG_ONE, "", gettext_noop("Maximum number of concurrent TFTP transfers (defaults to %s)."), "#" }, @@ -2717,6 +2717,15 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma } break; + + case LOPT_APREF: /* --tftp-unique-root */ + if (!arg || strcasecmp(arg, "ip") == 0) + set_option_bool(OPT_TFTP_APREF_IP); + else if (strcasecmp(arg, "mac") == 0) + set_option_bool(OPT_TFTP_APREF_MAC); + else + ret_err(gen_err); + break; #endif case LOPT_BRIDGE: /* --bridge-interface */ diff --git a/src/tftp.c b/src/tftp.c index f8ce9d2..be0d474 100644 --- a/src/tftp.c +++ b/src/tftp.c @@ -382,7 +382,7 @@ void tftp_request(struct listener *listen, time_t now) if (prefix[strlen(prefix)-1] != '/') strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff)); - if (option_bool(OPT_TFTP_APREF)) + if (option_bool(OPT_TFTP_APREF_IP)) { size_t oldlen = strlen(daemon->namebuff); struct stat statbuf; @@ -394,6 +394,38 @@ void tftp_request(struct listener *listen, time_t now) if (stat(daemon->namebuff, &statbuf) == -1 || !S_ISDIR(statbuf.st_mode)) daemon->namebuff[oldlen] = 0; } + if (option_bool(OPT_TFTP_APREF_MAC)) + { + unsigned char *macaddr = NULL; + unsigned char macbuf[DHCP_CHADDR_MAX]; + +#ifdef HAVE_DHCP + if (daemon->dhcp && peer.sa.sa_family == AF_INET) + { + /* Check if the client IP is in our lease database */ + struct dhcp_lease *lease = lease_find_by_addr(peer.in.sin_addr); + if (lease && lease->hwaddr_type == ARPHRD_ETHER && lease->hwaddr_len == ETHER_ADDR_LEN) + macaddr = lease->hwaddr; + } +#endif + + /* If no luck, try to find in ARP table. This only works if client is in same (V)LAN */ + if (!macaddr && find_mac(&peer, macbuf, 1, now) > 0) + macaddr = macbuf; + + if (macaddr) + { + size_t oldlen = strlen(daemon->namebuff); + struct stat statbuf; + + snprintf(daemon->namebuff + oldlen, (MAXDNAME-1) - oldlen, "%.2x-%.2x-%.2x-%.2x-%.2x-%.2x/", + macaddr[0], macaddr[1], macaddr[2], macaddr[3], macaddr[4], macaddr[5]); + + /* remove unique-directory if it doesn't exist */ + if (stat(daemon->namebuff, &statbuf) == -1 || !S_ISDIR(statbuf.st_mode)) + daemon->namebuff[oldlen] = 0; + } + } /* Absolute pathnames OK if they match prefix */ if (filename[0] == '/')