| @@ -1,777 +0,0 @@ |
| 1 | implement Bootpd; | | |
| 2 | | | |
| 3 | include "sys.m"; | | |
| 4 | sys: Sys; | | |
| 5 | | | |
| 6 | include "draw.m"; | | |
| 7 | | | |
| 8 | include "bufio.m"; | | |
| 9 | bufio: Bufio; | | |
| 10 | Iobuf: import bufio; | | |
| 11 | | | |
| 12 | include "attrdb.m"; | | |
| 13 | attrdb: Attrdb; | | |
| 14 | Db, Dbentry: import attrdb; | | |
| 15 | | | |
| 16 | include "ip.m"; | | |
| 17 | ip: IP; | | |
| 18 | IPaddr, Udphdr: import ip; | | |
| 19 | | | |
| 20 | include "ether.m"; | | |
| 21 | ether: Ether; | | |
| 22 | | | |
| 23 | include "arg.m"; | | |
| 24 | | | |
| 25 | Bootpd: module | | |
| 26 | { | | |
| 27 | init: fn(nil: ref Draw->Context, argv: list of string); | | |
| 28 | }; | | |
| 29 | | | |
| 30 | stderr: ref Sys->FD; | | |
| 31 | debug: int; | | |
| 32 | sniff: int; | | |
| 33 | verbose: int; | | |
| 34 | | | |
| 35 | siaddr: array of byte; | | |
| 36 | sysname: string; | | |
| 37 | progname := "bootpd"; | | |
| 38 | net := "/net"; | | |
| 39 | | | |
| 40 | Udphdrsize: con IP->OUdphdrlen; | | |
| 41 | | | |
| 42 | NEED_HA: con 1; | | |
| 43 | NEED_IP: con 0; | | |
| 44 | NEED_BF: con 0; | | |
| 45 | NEED_SM: con 0; | | |
| 46 | NEED_GW: con 0; | | |
| 47 | NEED_FS: con 0; | | |
| 48 | NEED_AU: con 0; | | |
| 49 | | | |
| 50 | init(nil: ref Draw->Context, args: list of string) | | |
| 51 | { | | |
| 52 | sys = load Sys Sys->PATH; | | |
| 53 | stderr = sys->fildes(2); | | |
| 54 | bufio = load Bufio Bufio->PATH; | | |
| 55 | if(bufio == nil) | | |
| 56 | loadfail(Bufio->PATH); | | |
| 57 | attrdb = load Attrdb Attrdb->PATH; | | |
| 58 | if(attrdb == nil) | | |
| 59 | loadfail(Attrdb->PATH); | | |
| 60 | attrdb->init(); | | |
| 61 | ip = load IP IP->PATH; | | |
| 62 | if(ip == nil) | | |
| 63 | loadfail(IP->PATH); | | |
| 64 | ip->init(); | | |
| 65 | ether = load Ether Ether->PATH; | | |
| 66 | if(ether == nil) | | |
| 67 | loadfail(Ether->PATH); | | |
| 68 | ether->init(); | | |
| 69 | | | |
| 70 | fname := "/services/bootp/db"; | | |
| 71 | verbose = 1; | | |
| 72 | sniff = 0; | | |
| 73 | debug = 0; | | |
| 74 | arg := load Arg Arg->PATH; | | |
| 75 | if(arg == nil) | | |
| 76 | raise "fail: load Arg"; | | |
| 77 | arg->init(args); | | |
| 78 | arg->setusage("bootpd [-dsqv] [-f file] [-x network]"); | | |
| 79 | progname = arg->progname(); | | |
| 80 | while((o := arg->opt()) != 0) | | |
| 81 | case o { | | |
| 82 | 'd' => debug++; | | |
| 83 | 's' => sniff = 1; debug = 255; | | |
| 84 | 'q' => verbose = 0; | | |
| 85 | 'v' => verbose = 1; | | |
| 86 | 'x' => net = arg->earg(); | | |
| 87 | 'f' => fname = arg->earg(); | | |
| 88 | * => arg->usage(); | | |
| 89 | } | | |
| 90 | args = arg->argv(); | | |
| 91 | if(args != nil) | | |
| 92 | arg->usage(); | | |
| 93 | arg = nil; | | |
| 94 | | | |
| 95 | sys->pctl(Sys->FORKFD|Sys->FORKNS, nil); | | |
| 96 | if(tabopen(fname)) | | |
| 97 | raise "fail: open database"; | | |
| 98 | | | |
| 99 | if(!sniff && (err := dbread()) != nil) | | |
| 100 | error(sys->sprint("error in %s: %s", fname, err)); | | |
| 101 | | | |
| 102 | addr := net+"/udp!*!67"; | | |
| 103 | if(debug) | | |
| 104 | sys->fprint(stderr, "bootp: announcing %s\n", addr); | | |
| 105 | (ok, c) := sys->announce(addr); | | |
| 106 | if(ok < 0) | | |
| 107 | error(sys->sprint("can't announce %s: %r", addr)); | | |
| 108 | get_sysname(); | | |
| 109 | get_ip(); | | |
| 110 | | | |
| 111 | if(sys->fprint(c.cfd, "headers") < 0) | | |
| 112 | error(sys->sprint("can't set headers mode: %r")); | | |
| 113 | sys->fprint(c.cfd, "oldheaders"); | | |
| 114 | | | |
| 115 | if(debug) | | |
| 116 | sys->fprint(stderr, "bootp: opening %s/data\n", c.dir); | | |
| 117 | c.dfd = sys->open(c.dir+"/data", sys->ORDWR); | | |
| 118 | if(c.dfd == nil) | | |
| 119 | error(sys->sprint("can't open %s/data: %r", c.dir)); | | |
| 120 | | | |
| 121 | spawn server(c); | | |
| 122 | } | | |
| 123 | | | |
| 124 | loadfail(s: string) | | |
| 125 | { | | |
| 126 | error(sys->sprint("can't load %s: %r", s)); | | |
| 127 | } | | |
| 128 | | | |
| 129 | error(s: string) | | |
| 130 | { | | |
| 131 | sys->fprint(stderr, "bootp: %s\n", s); | | |
| 132 | raise "fail:error"; | | |
| 133 | } | | |
| 134 | | | |
| 135 | server(c: Sys->Connection) | | |
| 136 | { | | |
| 137 | buf := array[2048] of byte; | | |
| 138 | badread := 0; | | |
| 139 | for(;;) { | | |
| 140 | if(debug) | | |
| 141 | sys->fprint(stderr, "bootp: listening for bootp requests...\n"); | | |
| 142 | n := sys->read(c.dfd, buf, len buf); | | |
| 143 | if(n <0) { | | |
| 144 | if (badread++ > 10) | | |
| 145 | break; | | |
| 146 | continue; | | |
| 147 | } | | |
| 148 | badread = 0; | | |
| 149 | if(n < Udphdrsize) { | | |
| 150 | if(debug) | | |
| 151 | sys->fprint(stderr, "bootp: short Udphdr: %d bytes\n", n); | | |
| 152 | continue; | | |
| 153 | } | | |
| 154 | hdr := Udphdr.unpack(buf, Udphdrsize); | | |
| 155 | if(debug) | | |
| 156 | sys->fprint(stderr, "bootp: received request from udp!%s!%d\n", hdr.raddr.text(), hdr.rport); | | |
| 157 | if(n < Udphdrsize+300) { | | |
| 158 | if(debug) | | |
| 159 | sys->fprint(stderr, "bootp: short request of %d bytes\n", n - Udphdrsize); | | |
| 160 | continue; | | |
| 161 | } | | |
| 162 | | | |
| 163 | (err, bootp) := M2S(buf[Udphdrsize:]); | | |
| 164 | if(err != nil) { | | |
| 165 | if(debug) | | |
| 166 | sys->fprint(stderr, "bootp: M2S failed: %s\n", err); | | |
| 167 | continue; | | |
| 168 | } | | |
| 169 | if(debug >= 2) | | |
| 170 | ppkt(bootp); | | |
| 171 | if(sniff) | | |
| 172 | continue; | | |
| 173 | if(bootp.htype != byte 1 || bootp.hlen != byte 6) { | | |
| 174 | # if it isn't ether, we don't do it | | |
| 175 | if(debug) | | |
| 176 | sys->fprint(stderr, "bootp: hardware type not ether; ignoring.\n"); | | |
| 177 | continue; | | |
| 178 | } | | |
| 179 | if((err = dbread()) != nil) { | | |
| 180 | sys->fprint(stderr, "bootp: getreply: dbread failed: %s\n", err); | | |
| 181 | continue; | | |
| 182 | } | | |
| 183 | rec := lookup(bootp); | | |
| 184 | if(rec == nil) { | | |
| 185 | # we can't answer this request | | |
| 186 | if(debug) | | |
| 187 | sys->fprint(stderr, "bootp: cannot answer request.\n"); | | |
| 188 | continue; | | |
| 189 | } | | |
| 190 | if(debug){ | | |
| 191 | sys->fprint(stderr, "bootp: found a matching entry:\n"); | | |
| 192 | pinfbp(rec); | | |
| 193 | } | | |
| 194 | mkreply(bootp, rec); | | |
| 195 | if(verbose) sys->print("bootp: %s -> %s %s\n", ether->text(rec.ha), rec.hostname, iptoa(rec.ip)); | | |
| 196 | if(debug >= 2) { | | |
| 197 | sys->fprint(stderr, "bootp: reply message:\n"); | | |
| 198 | ppkt(bootp); | | |
| 199 | } | | |
| 200 | repl:= S2M(bootp); | | |
| 201 | | | |
| 202 | if(debug) | | |
| 203 | sys->fprint(stderr, "bootp: sending reply.\n"); | | |
| 204 | arpenter(iptoa(rec.ip), ether->text(rec.ha)); | | |
| 205 | send(repl); | | |
| 206 | } | | |
| 207 | sys->fprint(stderr, "bootp: %d read errors: %r\n", badread); | | |
| 208 | } | | |
| 209 | | | |
| 210 | arpenter(ip, ha: string) | | |
| 211 | { | | |
| 212 | if(debug) sys->fprint(stderr, "bootp: arp: %s -> %s\n", ip, ha); | | |
| 213 | fd := sys->open(net+"/arp", Sys->OWRITE); | | |
| 214 | if(fd == nil) { | | |
| 215 | if(debug) | | |
| 216 | sys->fprint(stderr, "bootp: arp open failed: %r\n"); | | |
| 217 | return; | | |
| 218 | } | | |
| 219 | if(sys->fprint(fd, "add %s %s", ip, ha) < 0){ | | |
| 220 | if(debug) | | |
| 221 | sys->fprint(stderr, "bootp: error writing arp: %r\n"); | | |
| 222 | } | | |
| 223 | } | | |
| 224 | | | |
| 225 | get_sysname() | | |
| 226 | { | | |
| 227 | fd := sys->open("/dev/sysname", sys->OREAD); | | |
| 228 | if(fd == nil) { | | |
| 229 | sysname = "anon"; | | |
| 230 | return; | | |
| 231 | } | | |
| 232 | buf := array[128] of byte; | | |
| 233 | n := sys->read(fd, buf, len buf); | | |
| 234 | if(n <= 0) { | | |
| 235 | sysname = "anon"; | | |
| 236 | return; | | |
| 237 | } | | |
| 238 | sysname = string buf[0:n]; | | |
| 239 | } | | |
| 240 | | | |
| 241 | get_ip() | | |
| 242 | { | | |
| 243 | siaddr = array[4] of { * => byte 0 }; | | |
| 244 | # get a local IP address by translating our sysname with cs(8) | | |
| 245 | fd := sys->open(net+"/cs", Sys->ORDWR); | | |
| 246 | if(fd == nil){ | | |
| 247 | if(debug) | | |
| 248 | sys->fprint(stderr, "bootp: cannot open %s/cs for reading: %r.\n", net); | | |
| 249 | return; | | |
| 250 | } | | |
| 251 | if(sys->fprint(fd, "net!%s!0", sysname) < 0){ | | |
| 252 | if(debug) | | |
| 253 | sys->fprint(stderr, "bootp: can't translate net!%s!0 via %s/cs: %r\n", sysname, net); | | |
| 254 | return; | | |
| 255 | } | | |
| 256 | sys->seek(fd, big 0, 0); | | |
| 257 | a := array[1024] of byte; | | |
| 258 | n := sys->read(fd, a, len a); | | |
| 259 | if(n < 0) { | | |
| 260 | if(debug) sys->fprint(stderr, "bootp: read from /net/cs: %r.\n"); | | |
| 261 | return; | | |
| 262 | } | | |
| 263 | reply := string a[0:n]; | | |
| 264 | if(debug) sys->fprint(stderr, "bootp: read %s from /net/cs\n", reply); | | |
| 265 | | | |
| 266 | (l, addr):= sys->tokenize(reply, " "); | | |
| 267 | if(l != 2) { | | |
| 268 | if(debug) sys->fprint(stderr, "bootp: bad format from cs\n"); | | |
| 269 | return; | | |
| 270 | } | | |
| 271 | (l, addr) = sys->tokenize(hd tl addr, "!"); | | |
| 272 | if(l < 2) { | | |
| 273 | if(debug) sys->fprint(stderr, "bootp: short addr from cs\n"); | | |
| 274 | return; | | |
| 275 | } | | |
| 276 | err:= ""; | | |
| 277 | (err, siaddr) = get_ipaddr(hd addr); | | |
| 278 | if(err != nil || siaddr == nil) { | | |
| 279 | if(debug) sys->fprint(stderr, "bootp: invalid local IP addr %s.\n", hd tl addr); | | |
| 280 | siaddr = array[4] of { * => byte 0 }; | | |
| 281 | }; | | |
| 282 | if(debug) sys->fprint(stderr, "bootp: local IP address is %s.\n", iptoa(siaddr)); | | |
| 283 | } | | |
| 284 | | | |
| 285 | # byte op; /* opcode */ | | |
| 286 | # byte htype; /* hardware type */ | | |
| 287 | # byte hlen; /* hardware address len */ | | |
| 288 | # byte hops; /* hops */ | | |
| 289 | # byte xid[4]; /* a random number */ | | |
| 290 | # byte secs[2]; /* elapsed snce client started booting */ | | |
| 291 | # byte pad[2]; | | |
| 292 | # byte ciaddr[4]; /* client IP address (client tells server) */ | | |
| 293 | # byte yiaddr[4]; /* client IP address (server tells client) */ | | |
| 294 | # byte siaddr[4]; /* server IP address */ | | |
| 295 | # byte giaddr[4]; /* gateway IP address */ | | |
| 296 | # byte chaddr[16]; /* client hardware address */ | | |
| 297 | # byte sname[64]; /* server host name (optional) */ | | |
| 298 | # byte file[128]; /* boot file name */ | | |
| 299 | # byte vend[128]; /* vendor-specific goo */ | | |
| 300 | | | |
| 301 | BootpPKT: adt | | |
| 302 | { | | |
| 303 | op: byte; # Start of udp datagram | | |
| 304 | htype: byte; | | |
| 305 | hlen: byte; | | |
| 306 | hops: byte; | | |
| 307 | xid: int; | | |
| 308 | secs: int; | | |
| 309 | ciaddr: array of byte; | | |
| 310 | yiaddr: array of byte; | | |
| 311 | siaddr: array of byte; | | |
| 312 | giaddr: array of byte; | | |
| 313 | chaddr: array of byte; | | |
| 314 | sname: string; | | |
| 315 | file: string; | | |
| 316 | vend: array of byte; | | |
| 317 | }; | | |
| 318 | | | |
| 319 | InfBP: adt { | | |
| 320 | hostname: string; | | |
| 321 | | | |
| 322 | ha: array of byte; # hardware addr | | |
| 323 | ip: array of byte; # client IP addr | | |
| 324 | bf: array of byte; # boot file path | | |
| 325 | sm: array of byte; # subnet mask | | |
| 326 | gw: array of byte; # gateway IP addr | | |
| 327 | fs: array of byte; # file server IP addr | | |
| 328 | au: array of byte; # authentication server IP addr | | |
| 329 | }; | | |
| 330 | | | |
| 331 | records: array of ref InfBP; | | |
| 332 | | | |
| 333 | tabbio: ref Bufio->Iobuf; | | |
| 334 | tabname: string; | | |
| 335 | mtime: int; | | |
| 336 | | | |
| 337 | tabopen(fname: string): int | | |
| 338 | { | | |
| 339 | if(sniff) return 0; | | |
| 340 | tabname = fname; | | |
| 341 | if((tabbio = bufio->open(tabname, bufio->OREAD)) == nil) { | | |
| 342 | sys->fprint(stderr, "bootp: cannot open %s: %r\n", tabname); | | |
| 343 | return 1; | | |
| 344 | } | | |
| 345 | return 0; | | |
| 346 | } | | |
| 347 | | | |
| 348 | send(msg: array of byte) | | |
| 349 | { | | |
| 350 | if(debug) sys->fprint(stderr, "bootp: dialing udp!broadcast!68\n"); | | |
| 351 | (n, c) := sys->dial(net+"/udp!255.255.255.255!68", "67"); | | |
| 352 | # (n, c) := sys->dial(net+"/udp!255.255.255.255!68", "192.168.129.1!67"); | | |
| 353 | if(n < 0) { | | |
| 354 | sys->fprint(stderr, "bootp: send: error calling dial: %r\n"); | | |
| 355 | return; | | |
| 356 | } | | |
| 357 | if(debug) sys->fprint(stderr, "bootp: writing to %s/data\n", c.dir); | | |
| 358 | n = sys->write(c.dfd, msg, len msg); | | |
| 359 | if(n <=0) { | | |
| 360 | sys->fprint(stderr, "bootp: send: error writing to %s/data: %r\n", c.dir); | | |
| 361 | return; | | |
| 362 | } | | |
| 363 | if(debug) sys->fprint(stderr, "bootp: successfully wrote %d bytes to %s/data\n", n, c.dir); | | |
| 364 | } | | |
| 365 | | | |
| 366 | mkreply(bootp: ref BootpPKT, rec: ref InfBP) | | |
| 367 | { | | |
| 368 | bootp.op = byte 2; # boot reply | | |
| 369 | bootp.yiaddr = rec.ip; | | |
| 370 | bootp.siaddr = siaddr; | | |
| 371 | bootp.giaddr = array[4] of { * => byte 0 }; | | |
| 372 | bootp.sname = sysname; | | |
| 373 | bootp.file = string rec.bf; | | |
| 374 | bootp.vend = array of byte sys->sprint("p9 %s %s %s %s", iptoa(rec.sm), iptoa(rec.fs), iptoa(rec.au), iptoa(rec.gw)); | | |
| 375 | } | | |
| 376 | | | |
| 377 | lookup(bootp: ref BootpPKT): ref InfBP | | |
| 378 | { | | |
| 379 | for(i := 0; i < len records; i++) | | |
| 380 | if(eqa(bootp.chaddr[0:6], records[i].ha) || eqa(bootp.ciaddr, records[i].ip)) | | |
| 381 | return records[i]; | | |
| 382 | return nil; | | |
| 383 | } | | |
| 384 | | | |
| 385 | dbread(): string | | |
| 386 | { | | |
| 387 | (n, dir) := sys->fstat(tabbio.fd); | | |
| 388 | if(n < 0) | | |
| 389 | return sys->sprint("cannot fstat %s: %r", tabname); | | |
| 390 | if(mtime == 0 || mtime != dir.mtime) { | | |
| 391 | if(bufio->tabbio.seek(big 0, Sys->SEEKSTART) < big 0) | | |
| 392 | return sys->sprint("error seeking to start of %s.", tabname); | | |
| 393 | mtime = dir.mtime; | | |
| 394 | lnum: int = 0; | | |
| 395 | trecs: list of ref InfBP; | | |
| 396 | LINES: while((line := bufio->tabbio.gets('\n')) != nil) { | | |
| 397 | lnum++; | | |
| 398 | if(line[0] == '#') # comment | | |
| 399 | continue LINES; | | |
| 400 | fields: list of string; | | |
| 401 | (n, fields) = sys->tokenize(line, ":\r\n"); | | |
| 402 | if(n <= 0) { # blank line or colons | | |
| 403 | if(len line > 0) { | | |
| 404 | sys->fprint(stderr, "bootp: %s: %d empty entry.\n", tabname, lnum); | | |
| 405 | } | | |
| 406 | continue LINES; | | |
| 407 | } | | |
| 408 | rec := ref InfBP; | | |
| 409 | rec.hostname = hd fields; | | |
| 410 | fields = tl fields; | | |
| 411 | err: string; | | |
| 412 | FIELDS: for(; fields != nil; fields = tl fields) { | | |
| 413 | field := hd fields; | | |
| 414 | if(len field <= len "xx=") { | | |
| 415 | sys->fprint(stderr, "bootp: %s:%d invalid field \"%s\" in entry for %s", | | |
| 416 | tabname, lnum, field, rec.hostname); | | |
| 417 | continue FIELDS; | | |
| 418 | } | | |
| 419 | err = nil; | | |
| 420 | case field[0:3] { | | |
| 421 | "ha=" => | | |
| 422 | if(rec.ha != nil) { | | |
| 423 | sys->fprint(stderr, | | |
| 424 | "bootp: warning: %s:%d hardware address redefined for %s.\n", | | |
| 425 | tabname, lnum, rec.hostname); | | |
| 426 | } | | |
| 427 | (err, rec.ha) = get_haddr(field[3:]); | | |
| 428 | "ip=" => | | |
| 429 | if(rec.ip != nil) { | | |
| 430 | sys->fprint(stderr, "bootp: warning: %s:%d IP address redefined for %s.\n", | | |
| 431 | tabname, lnum, rec.hostname); | | |
| 432 | } | | |
| 433 | (err, rec.ip) = get_ipaddr(field[3:]); | | |
| 434 | "bf=" => | | |
| 435 | if(rec.bf != nil) { | | |
| 436 | sys->fprint(stderr, "bootp: warning: %s:%d bootfile redefined for %s.\n", | | |
| 437 | tabname, lnum, rec.hostname); | | |
| 438 | } | | |
| 439 | (err, rec.bf) = get_path(field[3:]); | | |
| 440 | "sm=" => | | |
| 441 | if(rec.sm != nil) { | | |
| 442 | sys->fprint(stderr, "bootp: warning: %s:%d subnet mask redefined for %s.\n", | | |
| 443 | tabname, lnum, rec.hostname); | | |
| 444 | } | | |
| 445 | (err, rec.sm) = get_ipaddr(field[3:]); | | |
| 446 | "gw=" => | | |
| 447 | if(rec.gw != nil) { | | |
| 448 | sys->fprint(stderr, "bootp: warning: %s:%d gateway redefined for %s.\n", | | |
| 449 | tabname, lnum, rec.hostname); | | |
| 450 | } | | |
| 451 | (err, rec.gw) = get_ipaddr(field[3:]); | | |
| 452 | "fs=" => | | |
| 453 | if(rec.fs != nil) { | | |
| 454 | sys->fprint(stderr, "bootp: warning: %s:%d file server redefined for %s.\n", | | |
| 455 | tabname, lnum, rec.hostname); | | |
| 456 | } | | |
| 457 | (err, rec.fs) = get_ipaddr(field[3:]); | | |
| 458 | "au=" => | | |
| 459 | if(rec.au != nil) { | | |
| 460 | sys->fprint(stderr, | | |
| 461 | "bootp: warning: %s:%d authentication server redefined for %s.\n", | | |
| 462 | tabname, lnum, rec.hostname); | | |
| 463 | } | | |
| 464 | (err, rec.au) = get_ipaddr(field[3:]); | | |
| 465 | * => | | |
| 466 | sys->fprint(stderr, | | |
| 467 | "bootp: %s:%d invalid or unsupported tag \"%s\" in entry for %s.\n", | | |
| 468 | tabname, lnum, field[0:2], rec.hostname); | | |
| 469 | continue FIELDS; | | |
| 470 | } | | |
| 471 | if(err != nil) { | | |
| 472 | sys->fprint(stderr, | | |
| 473 | "bootp: %s:%d %s for %s.\nbootp: skipping entry for %s.\n", | | |
| 474 | tabname, lnum, err, rec.hostname, | | |
| 475 | rec.hostname); | | |
| 476 | continue LINES; | | |
| 477 | } | | |
| 478 | } | | |
| 479 | if(rec.ha == nil) { | | |
| 480 | if(NEED_HA) { | | |
| 481 | sys->fprint(stderr, "bootp: %s:%d no hardware address defined for %s.\n", | | |
| 482 | tabname, lnum, rec.hostname); | | |
| 483 | sys->fprint(stderr, "bootp: skipping entry for %s.\n", rec.hostname); | | |
| 484 | continue LINES; | | |
| 485 | } | | |
| 486 | } | | |
| 487 | if(rec.ip == nil) { | | |
| 488 | if(NEED_IP) { | | |
| 489 | sys->fprint(stderr, "bootp: %s:%d no IP address defined for %s.\n", | | |
| 490 | tabname, lnum, rec.hostname); | | |
| 491 | sys->fprint(stderr, "bootp: skipping entry for %s.\n", rec.hostname); | | |
| 492 | continue LINES; | | |
| 493 | } | | |
| 494 | } | | |
| 495 | if(rec.bf == nil) { | | |
| 496 | if(NEED_BF) { | | |
| 497 | sys->fprint(stderr, "bootp: %s:%d no bootfile defined for %s.\n", | | |
| 498 | tabname, lnum, rec.hostname); | | |
| 499 | sys->fprint(stderr, "bootp: skipping entry for %s.\n", rec.hostname); | | |
| 500 | continue LINES; | | |
| 501 | } | | |
| 502 | } | | |
| 503 | if(rec.sm == nil) { | | |
| 504 | if(NEED_SM) { | | |
| 505 | sys->fprint(stderr, "bootp: %s:%d no subnet mask defined for %s.\n", | | |
| 506 | tabname, lnum, rec.hostname); | | |
| 507 | sys->fprint(stderr, "bootp: skipping entry for %s.\n", rec.hostname); | | |
| 508 | continue LINES; | | |
| 509 | } | | |
| 510 | } | | |
| 511 | if(rec.gw == nil) { | | |
| 512 | if(NEED_GW) { | | |
| 513 | sys->fprint(stderr, "bootp: %s:%d no gateway defined for %s.\n", | | |
| 514 | tabname, lnum, rec.hostname); | | |
| 515 | sys->fprint(stderr, "bootp: skipping entry for %s.\n", rec.hostname); | | |
| 516 | continue LINES; | | |
| 517 | } | | |
| 518 | } | | |
| 519 | if(rec.fs == nil) { | | |
| 520 | if(NEED_FS) { | | |
| 521 | sys->fprint(stderr, "bootp: %s:%d no file server defined for %s.\n", | | |
| 522 | tabname, lnum, rec.hostname); | | |
| 523 | sys->fprint(stderr, "bootp: skipping entry for %s.\n", rec.hostname); | | |
| 524 | continue LINES; | | |
| 525 | } | | |
| 526 | } | | |
| 527 | if(rec.au == nil) { | | |
| 528 | if(NEED_AU) { | | |
| 529 | sys->fprint(stderr, | | |
| 530 | "bootp: %s:%d no authentication server defined for %s.\n", | | |
| 531 | tabname, lnum, rec.hostname); | | |
| 532 | sys->fprint(stderr, "bootp: skipping entry for %s.\n", rec.hostname); | | |
| 533 | continue LINES; | | |
| 534 | } | | |
| 535 | } | | |
| 536 | if(debug) pinfbp(rec); | | |
| 537 | trecs = rec :: trecs; | | |
| 538 | } | | |
| 539 | if(trecs == nil) { | | |
| 540 | sys->fprint(stderr, "bootp: no valid entries in %s.\n", tabname); | | |
| 541 | if(records != nil) { | | |
| 542 | sys->fprint(stderr, "bootp: reverting to previous state.\n"); | | |
| 543 | return nil; | | |
| 544 | } | | |
| 545 | return "no entries."; | | |
| 546 | } | | |
| 547 | records = array[len trecs] of ref InfBP; | | |
| 548 | for(n = len records; n > 0; trecs = tl trecs) | | |
| 549 | records[--n] = hd trecs; | | |
| 550 | } | | |
| 551 | return nil; | | |
| 552 | } | | |
| 553 | | | |
| 554 | get_haddr(str: string): (string, array of byte) | | |
| 555 | { | | |
| 556 | addr := ether->parse(str); | | |
| 557 | if(addr == nil) | | |
| 558 | return (sys->sprint("invalid hardware address \"%s\"", str), nil); | | |
| 559 | return (nil, addr); | | |
| 560 | } | | |
| 561 | | | |
| 562 | get_ipaddr(str: string): (string, array of byte) | | |
| 563 | { | | |
| 564 | (ok, a) := IPaddr.parse(str); | | |
| 565 | if(ok < 0) | | |
| 566 | return (sys->sprint("invalid address: %s", str), nil); | | |
| 567 | return (nil, a.v4()); | | |
| 568 | } | | |
| 569 | | | |
| 570 | get_path(str: string): (string, array of byte) | | |
| 571 | { | | |
| 572 | if(str == nil) { | | |
| 573 | return ("nil path", nil); | | |
| 574 | } | | |
| 575 | path := array of byte str; | | |
| 576 | if(len path > 128) | | |
| 577 | return (sys->sprint("path too long (>128 bytes) \"%s...\"", string path[0:16]), nil); | | |
| 578 | return (nil, path); | | |
| 579 | } | | |
| 580 | | | |
| 581 | iptoa(addr: array of byte): string | | |
| 582 | { | | |
| 583 | if(len addr != 4) | | |
| 584 | return "0.0.0.0"; | | |
| 585 | return sys->sprint("%d.%d.%d.%d", | | |
| 586 | int addr[0], | | |
| 587 | int addr[1], | | |
| 588 | int addr[2], | | |
| 589 | int addr[3]); | | |
| 590 | } | | |
| 591 | | | |
| 592 | dtoa(data: array of byte): string | | |
| 593 | { | | |
| 594 | if(data == nil) | | |
| 595 | return nil; | | |
| 596 | result: string; | | |
| 597 | for(i:=0; i < len data; i++) | | |
| 598 | result += sys->sprint(".%d", int data[i]); | | |
| 599 | return result[1:]; | | |
| 600 | } | | |
| 601 | | | |
| 602 | bptohw(bp: ref BootpPKT): string | | |
| 603 | { | | |
| 604 | l := int bp.hlen; | | |
| 605 | if(l > 0 && l < len bp.chaddr) | | |
| 606 | return ether->text(bp.chaddr[0:l]); | | |
| 607 | return ""; | | |
| 608 | } | | |
| 609 | | | |
| 610 | ctostr(cstr: array of byte): string | | |
| 611 | { | | |
| 612 | for(i:=0; i<len cstr; i++) | | |
| 613 | if(cstr[i] == byte 0) | | |
| 614 | break; | | |
| 615 | return string cstr[0:i]; | | |
| 616 | } | | |
| 617 | | | |
| 618 | strtoc(s: string): array of byte | | |
| 619 | { | | |
| 620 | as := array of byte s; | | |
| 621 | cs := array[1 + len as] of byte; | | |
| 622 | cs[0:] = as; | | |
| 623 | cs[len cs - 1] = byte 0; | | |
| 624 | return cs; | | |
| 625 | } | | |
| 626 | | | |
| 627 | ppkt(bootp: ref BootpPKT) | | |
| 628 | { | | |
| 629 | sys->fprint(stderr, "BootpPKT {\n"); | | |
| 630 | sys->fprint(stderr, "\top == %d\n", int bootp.op); | | |
| 631 | sys->fprint(stderr, "\thtype == %d\n", int bootp.htype); | | |
| 632 | sys->fprint(stderr, "\thlen == %d\n", int bootp.hlen); | | |
| 633 | sys->fprint(stderr, "\thops == %d\n", int bootp.hops); | | |
| 634 | sys->fprint(stderr, "\txid == %d\n", bootp.xid); | | |
| 635 | sys->fprint(stderr, "\tsecs == %d\n", bootp.secs); | | |
| 636 | sys->fprint(stderr, "\tC client == %s\n", dtoa(bootp.ciaddr)); | | |
| 637 | sys->fprint(stderr, "\tY client == %s\n", dtoa(bootp.yiaddr)); | | |
| 638 | sys->fprint(stderr, "\tserver == %s\n", dtoa(bootp.siaddr)); | | |
| 639 | sys->fprint(stderr, "\tgateway == %s\n", dtoa(bootp.giaddr)); | | |
| 640 | sys->fprint(stderr, "\thwaddr == %s\n", bptohw(bootp)); | | |
| 641 | sys->fprint(stderr, "\thost == %s\n", bootp.sname); | | |
| 642 | sys->fprint(stderr, "\tfile == %s\n", bootp.file); | | |
| 643 | sys->fprint(stderr, "\tmagic == %s\n", magic(bootp.vend[0:4])); | | |
| 644 | if(magic(bootp.vend[0:4]) == "plan9") { | | |
| 645 | (n, strs) := sys->tokenize(string bootp.vend[4:], " \r\n"); | | |
| 646 | if(strs != nil) { | | |
| 647 | sys->fprint(stderr, "\t\tsm == %s\n", hd strs); | | |
| 648 | strs = tl strs; | | |
| 649 | } | | |
| 650 | if(strs != nil) { | | |
| 651 | sys->fprint(stderr, "\t\tfs == %s\n", hd strs); | | |
| 652 | strs = tl strs; | | |
| 653 | } | | |
| 654 | if(strs != nil) { | | |
| 655 | sys->fprint(stderr, "\t\tau == %s\n", hd strs); | | |
| 656 | strs = tl strs; | | |
| 657 | } | | |
| 658 | if(strs != nil) { | | |
| 659 | sys->fprint(stderr, "\t\tgw == %s\n", hd strs); | | |
| 660 | strs = tl strs; | | |
| 661 | } | | |
| 662 | } | | |
| 663 | sys->fprint(stderr, "}\n\n"); | | |
| 664 | } | | |
| 665 | | | |
| 666 | eqa(a1: array of byte, a2: array of byte): int | | |
| 667 | { | | |
| 668 | if(len a1 != len a2) | | |
| 669 | return 0; | | |
| 670 | for(i := 0; i < len a1; i++) | | |
| 671 | if(a1[i] != a2[i]) | | |
| 672 | return 0; | | |
| 673 | return 1; | | |
| 674 | } | | |
| 675 | | | |
| 676 | magic(cookie: array of byte): string | | |
| 677 | { | | |
| 678 | if(eqa(cookie, array[] of { byte 'p', byte '9', byte ' ', byte ' ' })) | | |
| 679 | return "plan9"; | | |
| 680 | if(eqa(cookie, array[] of { byte 99, byte 130, byte 83, byte 99 })) | | |
| 681 | return "rfc1048"; | | |
| 682 | if(eqa(cookie, array[] of { byte 'C', byte 'M', byte 'U', byte 0 })) | | |
| 683 | return "cmu"; | | |
| 684 | return dtoa(cookie); | | |
| 685 | } | | |
| 686 | | | |
| 687 | pinfbp(rec: ref InfBP) | | |
| 688 | { | | |
| 689 | sys->fprint(stderr, "Bootp entry {\n"); | | |
| 690 | sys->fprint(stderr, "\tha == %s\n", ether->text(rec.ha)); | | |
| 691 | sys->fprint(stderr, "\tip == %s\n", dtoa(rec.ip)); | | |
| 692 | sys->fprint(stderr, "\tbf == %s\n", string rec.bf); | | |
| 693 | sys->fprint(stderr, "\tsm == %s\n", dtoa(rec.sm)); | | |
| 694 | sys->fprint(stderr, "\tgw == %s\n", dtoa(rec.gw)); | | |
| 695 | sys->fprint(stderr, "\tfs == %s\n", dtoa(rec.fs)); | | |
| 696 | sys->fprint(stderr, "\tau == %s\n", dtoa(rec.au)); | | |
| 697 | sys->fprint(stderr, "}\n"); | | |
| 698 | } | | |
| 699 | | | |
| 700 | M2S(data: array of byte): (string, ref BootpPKT) | | |
| 701 | { | | |
| 702 | if(len data < 300) | | |
| 703 | return ("too short", nil); | | |
| 704 | | | |
| 705 | bootp := ref BootpPKT; | | |
| 706 | | | |
| 707 | bootp.op = data[0]; | | |
| 708 | bootp.htype = data[1]; | | |
| 709 | bootp.hlen = data[2]; | | |
| 710 | bootp.hops = data[3]; | | |
| 711 | bootp.xid = nhgetl(data[4:8]); | | |
| 712 | bootp.secs = nhgets(data[8:10]); | | |
| 713 | # data[10:12] unused | | |
| 714 | bootp.ciaddr = data[12:16]; | | |
| 715 | bootp.yiaddr = data[16:20]; | | |
| 716 | bootp.siaddr = data[20:24]; | | |
| 717 | bootp.giaddr = data[24:28]; | | |
| 718 | bootp.chaddr = data[28:44]; | | |
| 719 | bootp.sname = ctostr(data[44:108]); | | |
| 720 | bootp.file = ctostr(data[108:236]); | | |
| 721 | bootp.vend = data[236:300]; | | |
| 722 | | | |
| 723 | return (nil, bootp); | | |
| 724 | } | | |
| 725 | | | |
| 726 | S2M(bootp: ref BootpPKT): array of byte | | |
| 727 | { | | |
| 728 | data := array[364] of { * => byte 0 }; | | |
| 729 | | | |
| 730 | data[0] = bootp.op; | | |
| 731 | data[1] = bootp.htype; | | |
| 732 | data[2] = bootp.hlen; | | |
| 733 | data[3] = bootp.hops; | | |
| 734 | data[4:] = nhputl(bootp.xid); | | |
| 735 | data[8:] = nhputs(bootp.secs); | | |
| 736 | # data[10:12] unused | | |
| 737 | data[12:] = bootp.ciaddr; | | |
| 738 | data[16:] = bootp.yiaddr; | | |
| 739 | data[20:] = bootp.siaddr; | | |
| 740 | data[24:] = bootp.giaddr; | | |
| 741 | data[28:] = bootp.chaddr; | | |
| 742 | data[44:] = array of byte bootp.sname; | | |
| 743 | data[108:] = array of byte bootp.file; | | |
| 744 | data[236:] = bootp.vend; | | |
| 745 | | | |
| 746 | return data; | | |
| 747 | } | | |
| 748 | | | |
| 749 | nhgetl(data: array of byte): int | | |
| 750 | { | | |
| 751 | return (int data[0]<<24) | (int data[1]<<16) | | | |
| 752 | (int data[2]<<8) | int data[3]; | | |
| 753 | } | | |
| 754 | | | |
| 755 | nhgets(data: array of byte): int | | |
| 756 | { | | |
| 757 | return (int data[0]<<8) | int data[1]; | | |
| 758 | } | | |
| 759 | | | |
| 760 | nhputl(value: int): array of byte | | |
| 761 | { | | |
| 762 | return array[] of { | | |
| 763 | byte (value >> 24), | | |
| 764 | byte (value >> 16), | | |
| 765 | byte (value >> 8), | | |
| 766 | byte (value >> 0), | | |
| 767 | }; | | |
| 768 | } | | |
| 769 | | | |
| 770 | nhputs(value: int): array of byte | | |
| 771 | { | | |
| 772 | return array[] of { | | |
| 773 | byte (value >> 8), | | |
| 774 | byte (value >> 0), | | |
| 775 | }; | | |
| 776 | } | | |
| 777 | | | |