ref: e4f30c89f4a97fd26a82fe9068d70790084b16de
parent: dc725301592117ad0e70a0ab21926dcf5302ed31
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Fri Feb 4 20:34:22 EST 2022
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.
--- a/sys/man/8/dhcpd
+++ b/sys/man/8/dhcpd
@@ -43,7 +43,9 @@
.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 @@
.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
--- a/sys/src/cmd/ip/tftpd.c
+++ b/sys/src/cmd/ip/tftpd.c
@@ -6,6 +6,7 @@
#include <auth.h>
#include <bio.h>
#include <ip.h>
+#include <regexp.h>
enum
{
@@ -63,6 +64,13 @@
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 clrcon(void);
void setuser(void);
void remoteaddr(char*, char*, int);
+void readmapfile(char*);
void doserve(int);
char bigbuf[32768];
@@ -94,6 +103,7 @@
int dirsllen;
char *homedir = "/";
char *nsfile = nil;
+Map *namemap = nil;
char flog[] = "ipboot";
char net[Maxpath];
@@ -109,7 +119,7 @@
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 @@
case 'n':
nsfile = EARGF(usage());
break;
+ case 'm':
+ readmapfile(EARGF(usage()));
+ break;
default:
usage();
}ARGEND
@@ -177,7 +190,6 @@
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,6 +380,7 @@
}
/*
+ * 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.
*/
@@ -374,11 +387,21 @@
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')
return strdup(file);
@@ -411,6 +434,9 @@
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 @@
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);
@@ -771,4 +801,52 @@
if(n > 0)
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);
}