From e4f30c89f4a97fd26a82fe9068d70790084b16de Mon Sep 17 00:00:00 2001 From: cinap_lenrek Date: Sat, 5 Feb 2022 01:34:22 +0000 Subject: [PATCH] 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. --- sys/man/8/dhcpd | 24 +++++++++++- sys/src/cmd/ip/tftpd.c | 88 +++++++++++++++++++++++++++++++++++++++--- 2 files changed, 106 insertions(+), 6 deletions(-) diff --git a/sys/man/8/dhcpd b/sys/man/8/dhcpd index 5ec8c5557..6c62b9b0f 100644 --- a/sys/man/8/dhcpd +++ b/sys/man/8/dhcpd @@ -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 diff --git a/sys/src/cmd/ip/tftpd.c b/sys/src/cmd/ip/tftpd.c index 3744647c7..44bab9b60 100644 --- a/sys/src/cmd/ip/tftpd.c +++ b/sys/src/cmd/ip/tftpd.c @@ -6,6 +6,7 @@ #include #include #include +#include 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); +}