ip/tftpd: add -m argument for name substitution using regular expressions

This allows mapping incoming filenames to a different name
using regular expressions, followed by subtitutions
of the %[ICE] format strings.

I needed this to have individual cmdline.txt files for
netbooted raspberry pi's. In this example, i map cmdline.txt
to %C, which gets substituted for /cfg/pxe/$ether of the client.
This commit is contained in:
cinap_lenrek 2022-02-05 01:34:22 +00:00
parent dc72530159
commit e4f30c89f4
2 changed files with 106 additions and 6 deletions

View file

@ -43,7 +43,9 @@ dhcpd, dhcp6d, dhcpleases, rarpd, tftpd \- Internet booting
.RB [ -x
.IR netmtpt ]
.RB [ -n
.IR namespace-file ]
.IR nsfile ]
.RB [ -m
.IR mapfile ]
.SH DESCRIPTION
These programs support booting over the Internet.
They should all be run on the same server to
@ -323,6 +325,26 @@ Restricts access to only those files rooted in the
.TP
.B n
Sets the namespace file (default /lib/namespace).
.TP
.B m
Loads name substitutions from
.IR mapfile .
The format is a space or tab separated two-column text
with the first column being a regular expression
(see
.IR regexp (6))
that is matched against the requested file name
and the second column contains a subsitution.
Lines starting with
.B #
are ignored.
Occurances in the resulting filename of
.BR %I ,
.B %C
or
.B %E
are replaced with the ip, cfgpxe name or ether MAC of
of the client.
.PD
.SH FILES
.BR /lib/ndb/dhcp " directory of dynamic address files

View file

@ -6,6 +6,7 @@
#include <auth.h>
#include <bio.h>
#include <ip.h>
#include <regexp.h>
enum
{
@ -63,6 +64,13 @@ struct Opt {
int max;
};
typedef struct Map Map;
struct Map {
Reprog *re;
char *sub;
Map *next;
};
int dbg;
int restricted;
int pid;
@ -85,6 +93,7 @@ void ack(int, ushort);
void clrcon(void);
void setuser(void);
void remoteaddr(char*, char*, int);
void readmapfile(char*);
void doserve(int);
char bigbuf[32768];
@ -94,6 +103,7 @@ char *dirsl;
int dirsllen;
char *homedir = "/";
char *nsfile = nil;
Map *namemap = nil;
char flog[] = "ipboot";
char net[Maxpath];
@ -109,7 +119,7 @@ static char *opnames[] = {
void
usage(void)
{
fprint(2, "usage: %s [-dr] [-h homedir] [-s svc] [-x netmtpt]\n",
fprint(2, "usage: %s [-dr] [-h homedir] [-s svc] [-x netmtpt] [-n nsfile] [-m mapfile]\n",
argv0);
exits("usage");
}
@ -142,6 +152,9 @@ main(int argc, char **argv)
case 'n':
nsfile = EARGF(usage());
break;
case 'm':
readmapfile(EARGF(usage()));
break;
default:
usage();
}ARGEND
@ -177,7 +190,6 @@ main(int argc, char **argv)
if (cfd < 0)
sysfatal("announcing on %s: %r", buf);
syslog(dbg, flog, "tftpd started on %s dir %s", buf, adir);
// setuser();
for(;;) {
lcfd = listen(adir, ldir);
if(lcfd < 0)
@ -368,16 +380,27 @@ optlog(char *bytes, char *p, int dlen)
}
/*
* substitute name from namemap file and
* replace one occurrence of %[ICE] with ip, cfgpxe name, or ether mac, resp.
* we can't easily use $ because u-boot has stranger quoting rules than sh.
*/
char *
mapname(char *file)
{
int nf;
char *p, *newnm, *cur, *arpf, *ln, *remip, *bang;
char *fields[4];
char sub[1024], *p, *newnm, *cur, *arpf, *ln, *remip, *bang;
Biobuf *arp;
Map *map;
for(map = namemap; map != nil; map = map->next){
Resub subs[16];
memset(subs, 0, sizeof(subs));
if(regexec(map->re, file, subs, nelem(subs))){
regsub(map->sub, sub, sizeof(sub), subs, nelem(subs));
file = sub;
break;
}
}
p = strchr(file, '%');
if (p == nil || p[1] == '\0')
@ -411,6 +434,9 @@ mapname(char *file)
break;
/* read lines looking for remip in 3rd field of 4 */
while ((ln = Brdline(arp, '\n')) != nil) {
char *fields[4];
int nf;
ln[Blinelen(arp)-1] = 0;
nf = tokenize(ln, fields, nelem(fields));
if (nf >= 4 && strcmp(fields[2], remip) == 0) {
@ -448,8 +474,12 @@ doserve(int fd)
while(*p && dlen--)
p++;
syslog(dbg, flog, "tftpd %d mode %s file %s", pid, mode, file);
file = mapname(file); /* we don't free the result; minor leak */
syslog(dbg, flog, "tftpd %d file -> %s", pid, file);
if(dlen == 0) {
nak(fd, 0, "bad tftpmode");
close(fd);
@ -772,3 +802,51 @@ remoteaddr(char *dir, char *raddr, int len)
n--;
raddr[n] = 0;
}
void
readmapfile(char *file)
{
Map **link, *map;
Biobuf *bio;
char *s, *p, *d;
int line;
/* go to last entry */
for(link = &namemap; *link; link = &(*link)->next)
;
if((bio = Bopen(file, OREAD)) == nil)
sysfatal("open: %r");
for(line = 1; (s = Brdstr(bio, '\n', 1)) != nil; free(s), line++){
p = s;
while(strchr("\t ", *p))
p++;
if(*p == '#')
continue;
if(d = strchr(p, '\t'))
*d++ = '\0';
else if(d = strchr(p, ' '))
*d++ = '\0';
else {
fprint(2, "%s:%d: ignored: %s\n", file, line, p);
continue;
}
while(strchr("\t ", *d))
d++;
map = malloc(sizeof(Map));
map->re = regcomp(p);
if(map->re == nil){
fprint(2, "%s:%d: syntax error: %s\n", file, line, p);
free(map);
continue;
}
map->sub = strdup(d);
map->next = nil;
*link = map;
link = &map->next;
}
Bterm(bio);
}