1 /* $NetBSD: rmpproto.c,v 1.7 1996/02/01 21:27:46 mycroft Exp $ */ 2 3 /* 4 * Copyright (c) 1988, 1992 The University of Utah and the Center 5 * for Software Science (CSS). 6 * Copyright (c) 1992, 1993 7 * The Regents of the University of California. All rights reserved. 8 * 9 * This code is derived from software contributed to Berkeley by 10 * the Center for Software Science of the University of Utah Computer 11 * Science Department. CSS requests users of this software to return 12 * to css-dist@cs.utah.edu any improvements that they make and grant 13 * CSS redistribution rights. 14 * 15 * Redistribution and use in source and binary forms, with or without 16 * modification, are permitted provided that the following conditions 17 * are met: 18 * 1. Redistributions of source code must retain the above copyright 19 * notice, this list of conditions and the following disclaimer. 20 * 2. Redistributions in binary form must reproduce the above copyright 21 * notice, this list of conditions and the following disclaimer in the 22 * documentation and/or other materials provided with the distribution. 23 * 3. All advertising materials mentioning features or use of this software 24 * must display the following acknowledgement: 25 * This product includes software developed by the University of 26 * California, Berkeley and its contributors. 27 * 4. Neither the name of the University nor the names of its contributors 28 * may be used to endorse or promote products derived from this software 29 * without specific prior written permission. 30 * 31 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 32 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 33 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 34 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 35 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 36 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 37 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 38 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 39 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 40 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 41 * SUCH DAMAGE. 42 * 43 * from: @(#)rmpproto.c 8.1 (Berkeley) 6/4/93 44 * 45 * From: Utah Hdr: rmpproto.c 3.1 92/07/06 46 * Author: Jeff Forys, University of Utah CSS 47 */ 48 49 #ifndef lint 50 /*static char sccsid[] = "@(#)rmpproto.c 8.1 (Berkeley) 6/4/93";*/ 51 static char rcsid[] = "$NetBSD: rmpproto.c,v 1.7 1996/02/01 21:27:46 mycroft Exp $"; 52 #endif /* not lint */ 53 54 #include <sys/param.h> 55 #include <sys/time.h> 56 57 #include <errno.h> 58 #include <fcntl.h> 59 #include <stdio.h> 60 #include <string.h> 61 #include <syslog.h> 62 #include <unistd.h> 63 #include "defs.h" 64 65 /* 66 ** ProcessPacket -- determine packet type and do what's required. 67 ** 68 ** An RMP BOOT packet has been received. Look at the type field 69 ** and process Boot Requests, Read Requests, and Boot Complete 70 ** packets. Any other type will be dropped with a warning msg. 71 ** 72 ** Parameters: 73 ** rconn - the new connection 74 ** client - list of files available to this host 75 ** 76 ** Returns: 77 ** Nothing. 78 ** 79 ** Side Effects: 80 ** - If this is a valid boot request, it will be added to 81 ** the linked list of outstanding requests (RmpConns). 82 ** - If this is a valid boot complete, its associated 83 ** entry in RmpConns will be deleted. 84 ** - Also, unless we run out of memory, a reply will be 85 ** sent to the host that sent the packet. 86 */ 87 void 88 ProcessPacket(rconn, client) 89 RMPCONN *rconn; 90 CLIENT *client; 91 { 92 struct rmp_packet *rmp; 93 RMPCONN *rconnout; 94 95 rmp = &rconn->rmp; /* cache pointer to RMP packet */ 96 97 switch(rmp->r_type) { /* do what we came here to do */ 98 case RMP_BOOT_REQ: /* boot request */ 99 if ((rconnout = NewConn(rconn)) == NULL) 100 return; 101 102 /* 103 * If the Session ID is 0xffff, this is a "probe" 104 * packet and we do not want to add the connection 105 * to the linked list of active connections. There 106 * are two types of probe packets, if the Sequence 107 * Number is 0 they want to know our host name, o/w 108 * they want the name of the file associated with 109 * the number spec'd by the Sequence Number. 110 * 111 * If this is an actual boot request, open the file 112 * and send a reply. If SendBootRepl() does not 113 * return 0, add the connection to the linked list 114 * of active connections, otherwise delete it since 115 * an error was encountered. 116 */ 117 if (ntohs(rmp->r_brq.rmp_session) == RMP_PROBESID) { 118 if (WORDZE(rmp->r_brq.rmp_seqno)) 119 (void) SendServerID(rconnout); 120 else 121 (void) SendFileNo(rmp, rconnout, 122 client? client->files: 123 BootFiles); 124 FreeConn(rconnout); 125 } else { 126 if (SendBootRepl(rmp, rconnout, 127 client? client->files: BootFiles)) 128 AddConn(rconnout); 129 else 130 FreeConn(rconnout); 131 } 132 break; 133 134 case RMP_BOOT_REPL: /* boot reply (not valid) */ 135 syslog(LOG_WARNING, "%s: sent a boot reply", 136 EnetStr(rconn)); 137 break; 138 139 case RMP_READ_REQ: /* read request */ 140 /* 141 * Send a portion of the boot file. 142 */ 143 (void) SendReadRepl(rconn); 144 break; 145 146 case RMP_READ_REPL: /* read reply (not valid) */ 147 syslog(LOG_WARNING, "%s: sent a read reply", 148 EnetStr(rconn)); 149 break; 150 151 case RMP_BOOT_DONE: /* boot complete */ 152 /* 153 * Remove the entry from the linked list of active 154 * connections. 155 */ 156 (void) BootDone(rconn); 157 break; 158 159 default: /* unknown RMP packet type */ 160 syslog(LOG_WARNING, "%s: unknown packet type (%u)", 161 EnetStr(rconn), rmp->r_type); 162 } 163 } 164 165 /* 166 ** SendServerID -- send our host name to who ever requested it. 167 ** 168 ** Parameters: 169 ** rconn - the reply packet to be formatted. 170 ** 171 ** Returns: 172 ** 1 on success, 0 on failure. 173 ** 174 ** Side Effects: 175 ** none. 176 */ 177 int 178 SendServerID(rconn) 179 RMPCONN *rconn; 180 { 181 register struct rmp_packet *rpl; 182 register char *src, *dst; 183 register u_int8_t *size; 184 185 rpl = &rconn->rmp; /* cache ptr to RMP packet */ 186 187 /* 188 * Set up assorted fields in reply packet. 189 */ 190 rpl->r_brpl.rmp_type = RMP_BOOT_REPL; 191 rpl->r_brpl.rmp_retcode = RMP_E_OKAY; 192 ZEROWORD(rpl->r_brpl.rmp_seqno); 193 rpl->r_brpl.rmp_session = 0; 194 rpl->r_brpl.rmp_version = htons(RMP_VERSION); 195 196 size = &rpl->r_brpl.rmp_flnmsize; /* ptr to length of host name */ 197 198 /* 199 * Copy our host name into the reply packet incrementing the 200 * length as we go. Stop at RMP_HOSTLEN or the first dot. 201 */ 202 src = MyHost; 203 dst = (char *) &rpl->r_brpl.rmp_flnm; 204 for (*size = 0; *size < RMP_HOSTLEN; (*size)++) { 205 if (*src == '.' || *src == '\0') 206 break; 207 *dst++ = *src++; 208 } 209 210 rconn->rmplen = RMPBOOTSIZE(*size); /* set packet length */ 211 212 return(SendPacket(rconn)); /* send packet */ 213 } 214 215 /* 216 ** SendFileNo -- send the name of a bootable file to the requester. 217 ** 218 ** Parameters: 219 ** req - RMP BOOT packet containing the request. 220 ** rconn - the reply packet to be formatted. 221 ** filelist - list of files available to the requester. 222 ** 223 ** Returns: 224 ** 1 on success, 0 on failure. 225 ** 226 ** Side Effects: 227 ** none. 228 */ 229 int 230 SendFileNo(req, rconn, filelist) 231 struct rmp_packet *req; 232 RMPCONN *rconn; 233 char *filelist[]; 234 { 235 register struct rmp_packet *rpl; 236 register char *src, *dst; 237 register u_int8_t *size; 238 register int i; 239 240 GETWORD(req->r_brpl.rmp_seqno, i); /* SeqNo is really FileNo */ 241 rpl = &rconn->rmp; /* cache ptr to RMP packet */ 242 243 /* 244 * Set up assorted fields in reply packet. 245 */ 246 rpl->r_brpl.rmp_type = RMP_BOOT_REPL; 247 PUTWORD(i, rpl->r_brpl.rmp_seqno); 248 i--; 249 rpl->r_brpl.rmp_session = 0; 250 rpl->r_brpl.rmp_version = htons(RMP_VERSION); 251 252 size = &rpl->r_brpl.rmp_flnmsize; /* ptr to length of filename */ 253 *size = 0; /* init length to zero */ 254 255 /* 256 * Copy the file name into the reply packet incrementing the 257 * length as we go. Stop at end of string or when RMPBOOTDATA 258 * characters have been copied. Also, set return code to 259 * indicate success or "no more files". 260 */ 261 if (i < C_MAXFILE && filelist[i] != NULL) { 262 src = filelist[i]; 263 dst = (char *)&rpl->r_brpl.rmp_flnm; 264 for (; *src && *size < RMPBOOTDATA; (*size)++) { 265 if (*src == '\0') 266 break; 267 *dst++ = *src++; 268 } 269 rpl->r_brpl.rmp_retcode = RMP_E_OKAY; 270 } else 271 rpl->r_brpl.rmp_retcode = RMP_E_NODFLT; 272 273 rconn->rmplen = RMPBOOTSIZE(*size); /* set packet length */ 274 275 return(SendPacket(rconn)); /* send packet */ 276 } 277 278 /* 279 ** SendBootRepl -- open boot file and respond to boot request. 280 ** 281 ** Parameters: 282 ** req - RMP BOOT packet containing the request. 283 ** rconn - the reply packet to be formatted. 284 ** filelist - list of files available to the requester. 285 ** 286 ** Returns: 287 ** 1 on success, 0 on failure. 288 ** 289 ** Side Effects: 290 ** none. 291 */ 292 int 293 SendBootRepl(req, rconn, filelist) 294 struct rmp_packet *req; 295 RMPCONN *rconn; 296 char *filelist[]; 297 { 298 int retval; 299 char *filename, filepath[RMPBOOTDATA+1]; 300 RMPCONN *oldconn; 301 register struct rmp_packet *rpl; 302 register char *src, *dst1, *dst2; 303 register u_int8_t i; 304 305 /* 306 * If another connection already exists, delete it since we 307 * are obviously starting again. 308 */ 309 if ((oldconn = FindConn(rconn)) != NULL) { 310 syslog(LOG_WARNING, "%s: dropping existing connection", 311 EnetStr(oldconn)); 312 RemoveConn(oldconn); 313 } 314 315 rpl = &rconn->rmp; /* cache ptr to RMP packet */ 316 317 /* 318 * Set up assorted fields in reply packet. 319 */ 320 rpl->r_brpl.rmp_type = RMP_BOOT_REPL; 321 COPYWORD(req->r_brq.rmp_seqno, rpl->r_brpl.rmp_seqno); 322 rpl->r_brpl.rmp_session = htons(GenSessID()); 323 rpl->r_brpl.rmp_version = htons(RMP_VERSION); 324 rpl->r_brpl.rmp_flnmsize = req->r_brq.rmp_flnmsize; 325 326 /* 327 * Copy file name to `filepath' string, and into reply packet. 328 */ 329 src = &req->r_brq.rmp_flnm; 330 dst1 = filepath; 331 dst2 = &rpl->r_brpl.rmp_flnm; 332 for (i = 0; i < req->r_brq.rmp_flnmsize; i++) 333 *dst1++ = *dst2++ = *src++; 334 *dst1 = '\0'; 335 336 /* 337 * If we are booting HP-UX machines, their secondary loader will 338 * ask for files like "/hp-ux". As a security measure, we do not 339 * allow boot files to lay outside the boot directory (unless they 340 * are purposely link'd out. So, make `filename' become the path- 341 * stripped file name and spoof the client into thinking that it 342 * really got what it wanted. 343 */ 344 filename = (filename = rindex(filepath,'/'))? ++filename: filepath; 345 346 /* 347 * Check that this is a valid boot file name. 348 */ 349 for (i = 0; i < C_MAXFILE && filelist[i] != NULL; i++) 350 if (STREQN(filename, filelist[i])) 351 goto match; 352 353 /* 354 * Invalid boot file name, set error and send reply packet. 355 */ 356 rpl->r_brpl.rmp_retcode = RMP_E_NOFILE; 357 retval = 0; 358 goto sendpkt; 359 360 match: 361 /* 362 * This is a valid boot file. Open the file and save the file 363 * descriptor associated with this connection and set success 364 * indication. If the file couldnt be opened, set error: 365 * "no such file or dir" - RMP_E_NOFILE 366 * "file table overflow" - RMP_E_BUSY 367 * "too many open files" - RMP_E_BUSY 368 * anything else - RMP_E_OPENFILE 369 */ 370 if ((rconn->bootfd = open(filename, O_RDONLY, 0600)) < 0) { 371 rpl->r_brpl.rmp_retcode = (errno == ENOENT)? RMP_E_NOFILE: 372 (errno == EMFILE || errno == ENFILE)? RMP_E_BUSY: 373 RMP_E_OPENFILE; 374 retval = 0; 375 } else { 376 rpl->r_brpl.rmp_retcode = RMP_E_OKAY; 377 retval = 1; 378 } 379 380 sendpkt: 381 syslog(LOG_INFO, "%s: request to boot %s (%s)", 382 EnetStr(rconn), filename, retval? "granted": "denied"); 383 384 rconn->rmplen = RMPBOOTSIZE(rpl->r_brpl.rmp_flnmsize); 385 386 return (retval & SendPacket(rconn)); 387 } 388 389 /* 390 ** SendReadRepl -- send a portion of the boot file to the requester. 391 ** 392 ** Parameters: 393 ** rconn - the reply packet to be formatted. 394 ** 395 ** Returns: 396 ** 1 on success, 0 on failure. 397 ** 398 ** Side Effects: 399 ** none. 400 */ 401 int 402 SendReadRepl(rconn) 403 RMPCONN *rconn; 404 { 405 int retval = 0; 406 RMPCONN *oldconn; 407 register struct rmp_packet *rpl, *req; 408 register int size = 0; 409 int madeconn = 0; 410 411 /* 412 * Find the old connection. If one doesnt exist, create one only 413 * to return the error code. 414 */ 415 if ((oldconn = FindConn(rconn)) == NULL) { 416 if ((oldconn = NewConn(rconn)) == NULL) 417 return(0); 418 syslog(LOG_ERR, "SendReadRepl: no active connection (%s)", 419 EnetStr(rconn)); 420 madeconn++; 421 } 422 423 req = &rconn->rmp; /* cache ptr to request packet */ 424 rpl = &oldconn->rmp; /* cache ptr to reply packet */ 425 426 if (madeconn) { /* no active connection above; abort */ 427 rpl->r_rrpl.rmp_retcode = RMP_E_ABORT; 428 retval = 1; 429 goto sendpkt; 430 } 431 432 /* 433 * Make sure Session ID's match. 434 */ 435 if (ntohs(req->r_rrq.rmp_session) != 436 ((rpl->r_type == RMP_BOOT_REPL)? ntohs(rpl->r_brpl.rmp_session): 437 ntohs(rpl->r_rrpl.rmp_session))) { 438 syslog(LOG_ERR, "SendReadRepl: bad session id (%s)", 439 EnetStr(rconn)); 440 rpl->r_rrpl.rmp_retcode = RMP_E_BADSID; 441 retval = 1; 442 goto sendpkt; 443 } 444 445 /* 446 * If the requester asks for more data than we can fit, 447 * silently clamp the request size down to RMPREADDATA. 448 * 449 * N.B. I do not know if this is "legal", however it seems 450 * to work. This is necessary for bpfwrite() on machines 451 * with MCLBYTES less than 1514. 452 */ 453 if (ntohs(req->r_rrq.rmp_size) > RMPREADDATA) 454 req->r_rrq.rmp_size = htons(RMPREADDATA); 455 456 /* 457 * Position read head on file according to info in request packet. 458 */ 459 GETWORD(req->r_rrq.rmp_offset, size); 460 if (lseek(oldconn->bootfd, (off_t)size, L_SET) < 0) { 461 syslog(LOG_ERR, "SendReadRepl: lseek: %m (%s)", 462 EnetStr(rconn)); 463 rpl->r_rrpl.rmp_retcode = RMP_E_ABORT; 464 retval = 1; 465 goto sendpkt; 466 } 467 468 /* 469 * Read data directly into reply packet. 470 */ 471 if ((size = read(oldconn->bootfd, &rpl->r_rrpl.rmp_data, 472 (int) ntohs(req->r_rrq.rmp_size))) <= 0) { 473 if (size < 0) { 474 syslog(LOG_ERR, "SendReadRepl: read: %m (%s)", 475 EnetStr(rconn)); 476 rpl->r_rrpl.rmp_retcode = RMP_E_ABORT; 477 } else { 478 rpl->r_rrpl.rmp_retcode = RMP_E_EOF; 479 } 480 retval = 1; 481 goto sendpkt; 482 } 483 484 /* 485 * Set success indication. 486 */ 487 rpl->r_rrpl.rmp_retcode = RMP_E_OKAY; 488 489 sendpkt: 490 /* 491 * Set up assorted fields in reply packet. 492 */ 493 rpl->r_rrpl.rmp_type = RMP_READ_REPL; 494 COPYWORD(req->r_rrq.rmp_offset, rpl->r_rrpl.rmp_offset); 495 rpl->r_rrpl.rmp_session = req->r_rrq.rmp_session; 496 497 oldconn->rmplen = RMPREADSIZE(size); /* set size of packet */ 498 499 retval &= SendPacket(oldconn); /* send packet */ 500 501 if (madeconn) /* clean up after ourself */ 502 FreeConn(oldconn); 503 504 return (retval); 505 } 506 507 /* 508 ** BootDone -- free up memory allocated for a connection. 509 ** 510 ** Parameters: 511 ** rconn - incoming boot complete packet. 512 ** 513 ** Returns: 514 ** 1 on success, 0 on failure. 515 ** 516 ** Side Effects: 517 ** none. 518 */ 519 int 520 BootDone(rconn) 521 RMPCONN *rconn; 522 { 523 RMPCONN *oldconn; 524 struct rmp_packet *rpl; 525 526 /* 527 * If we cant find the connection, ignore the request. 528 */ 529 if ((oldconn = FindConn(rconn)) == NULL) { 530 syslog(LOG_ERR, "BootDone: no existing connection (%s)", 531 EnetStr(rconn)); 532 return(0); 533 } 534 535 rpl = &oldconn->rmp; /* cache ptr to RMP packet */ 536 537 /* 538 * Make sure Session ID's match. 539 */ 540 if (ntohs(rconn->rmp.r_rrq.rmp_session) != 541 ((rpl->r_type == RMP_BOOT_REPL)? ntohs(rpl->r_brpl.rmp_session): 542 ntohs(rpl->r_rrpl.rmp_session))) { 543 syslog(LOG_ERR, "BootDone: bad session id (%s)", 544 EnetStr(rconn)); 545 return(0); 546 } 547 548 RemoveConn(oldconn); /* remove connection */ 549 550 syslog(LOG_INFO, "%s: boot complete", EnetStr(rconn)); 551 552 return(1); 553 } 554 555 /* 556 ** SendPacket -- send an RMP packet to a remote host. 557 ** 558 ** Parameters: 559 ** rconn - packet to be sent. 560 ** 561 ** Returns: 562 ** 1 on success, 0 on failure. 563 ** 564 ** Side Effects: 565 ** none. 566 */ 567 int 568 SendPacket(rconn) 569 register RMPCONN *rconn; 570 { 571 /* 572 * Set Ethernet Destination address to Source (BPF and the enet 573 * driver will take care of getting our source address set). 574 */ 575 bcopy((char *)&rconn->rmp.hp_hdr.saddr[0], 576 (char *)&rconn->rmp.hp_hdr.daddr[0], RMP_ADDRLEN); 577 #ifdef __FreeBSD__ 578 /* BPF (incorrectly) wants this in host order. */ 579 rconn->rmp.hp_hdr.len = rconn->rmplen - sizeof(struct hp_hdr); 580 #else 581 rconn->rmp.hp_hdr.len = htons(rconn->rmplen - sizeof(struct hp_hdr)); 582 #endif 583 584 /* 585 * Reverse 802.2/HP Extended Source & Destination Access Pts. 586 */ 587 rconn->rmp.hp_llc.dxsap = htons(HPEXT_SXSAP); 588 rconn->rmp.hp_llc.sxsap = htons(HPEXT_DXSAP); 589 590 /* 591 * Last time this connection was active. 592 */ 593 (void) gettimeofday(&rconn->tstamp, (struct timezone *)0); 594 595 if (DbgFp != NULL) /* display packet */ 596 DispPkt(rconn,DIR_SENT); 597 598 /* 599 * Send RMP packet to remote host. 600 */ 601 return(BpfWrite(rconn)); 602 } 603