1 /* $NetBSD: rmpproto.c,v 1.15 2011/02/08 20:20:28 rmind 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. Neither the name of the University nor the names of its contributors 24 * may be used to endorse or promote products derived from this software 25 * without specific prior written permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 37 * SUCH DAMAGE. 38 * 39 * from: @(#)rmpproto.c 8.1 (Berkeley) 6/4/93 40 * 41 * From: Utah Hdr: rmpproto.c 3.1 92/07/06 42 * Author: Jeff Forys, University of Utah CSS 43 */ 44 45 #include <sys/cdefs.h> 46 #ifndef lint 47 #if 0 48 static char sccsid[] = "@(#)rmpproto.c 8.1 (Berkeley) 6/4/93"; 49 #else 50 __RCSID("$NetBSD: rmpproto.c,v 1.15 2011/02/08 20:20:28 rmind Exp $"); 51 #endif 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 struct rmp_packet *rpl; 182 char *src, *dst; 183 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 struct rmp_packet *rpl; 236 char *src, *dst; 237 u_int8_t *size; 238 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 struct rmp_packet *rpl; 302 char *src, *dst1, *dst2; 303 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 dst1 = filepath; 330 dst2 = &rpl->r_brpl.rmp_flnm; 331 if (req->r_brq.rmp_flnmsize) 332 src = &req->r_brq.rmp_flnm; 333 else { 334 /* no file supplied, substitute the first one */ 335 src = filelist[0]; 336 req->r_brq.rmp_flnmsize = strlen(src); 337 } 338 for (i = 0; i < req->r_brq.rmp_flnmsize; i++) 339 *dst1++ = *dst2++ = *src++; 340 *dst1 = '\0'; 341 342 /* 343 * If we are booting HP-UX machines, their secondary loader will 344 * ask for files like "/hp-ux". As a security measure, we do not 345 * allow boot files to lay outside the boot directory (unless they 346 * are purposely link'd out. So, make `filename' become the path- 347 * stripped file name and spoof the client into thinking that it 348 * really got what it wanted. 349 */ 350 if ((filename = strrchr(filepath,'/')) != NULL) 351 filename++; 352 else 353 filename = filepath; 354 355 /* 356 * Check that this is a valid boot file name. 357 */ 358 for (i = 0; i < C_MAXFILE && filelist[i] != NULL; i++) 359 if (STREQN(filename, filelist[i])) 360 goto match; 361 362 /* 363 * Invalid boot file name, set error and send reply packet. 364 */ 365 rpl->r_brpl.rmp_retcode = RMP_E_NOFILE; 366 retval = 0; 367 goto sendpkt; 368 369 match: 370 /* 371 * This is a valid boot file. Open the file and save the file 372 * descriptor associated with this connection and set success 373 * indication. If the file couldnt be opened, set error: 374 * "no such file or dir" - RMP_E_NOFILE 375 * "file table overflow" - RMP_E_BUSY 376 * "too many open files" - RMP_E_BUSY 377 * anything else - RMP_E_OPENFILE 378 */ 379 if ((rconn->bootfd = open(filename, O_RDONLY, 0600)) < 0) { 380 rpl->r_brpl.rmp_retcode = (errno == ENOENT)? RMP_E_NOFILE: 381 (errno == EMFILE || errno == ENFILE)? RMP_E_BUSY: 382 RMP_E_OPENFILE; 383 retval = 0; 384 } else { 385 rpl->r_brpl.rmp_retcode = RMP_E_OKAY; 386 retval = 1; 387 } 388 389 sendpkt: 390 syslog(LOG_INFO, "%s: request to boot %s (%s)", 391 EnetStr(rconn), filename, retval? "granted": "denied"); 392 393 rconn->rmplen = RMPBOOTSIZE(rpl->r_brpl.rmp_flnmsize); 394 395 return (retval & SendPacket(rconn)); 396 } 397 398 /* 399 ** SendReadRepl -- send a portion of the boot file to the requester. 400 ** 401 ** Parameters: 402 ** rconn - the reply packet to be formatted. 403 ** 404 ** Returns: 405 ** 1 on success, 0 on failure. 406 ** 407 ** Side Effects: 408 ** none. 409 */ 410 int 411 SendReadRepl(rconn) 412 RMPCONN *rconn; 413 { 414 int retval = 0; 415 RMPCONN *oldconn; 416 struct rmp_packet *rpl, *req; 417 int size = 0; 418 int madeconn = 0; 419 420 /* 421 * Find the old connection. If one doesnt exist, create one only 422 * to return the error code. 423 */ 424 if ((oldconn = FindConn(rconn)) == NULL) { 425 if ((oldconn = NewConn(rconn)) == NULL) 426 return(0); 427 syslog(LOG_ERR, "SendReadRepl: no active connection (%s)", 428 EnetStr(rconn)); 429 madeconn++; 430 } 431 432 req = &rconn->rmp; /* cache ptr to request packet */ 433 rpl = &oldconn->rmp; /* cache ptr to reply packet */ 434 435 if (madeconn) { /* no active connection above; abort */ 436 rpl->r_rrpl.rmp_retcode = RMP_E_ABORT; 437 retval = 1; 438 goto sendpkt; 439 } 440 441 /* 442 * Make sure Session ID's match. 443 */ 444 if (ntohs(req->r_rrq.rmp_session) != 445 ((rpl->r_type == RMP_BOOT_REPL)? ntohs(rpl->r_brpl.rmp_session): 446 ntohs(rpl->r_rrpl.rmp_session))) { 447 syslog(LOG_ERR, "SendReadRepl: bad session id (%s)", 448 EnetStr(rconn)); 449 rpl->r_rrpl.rmp_retcode = RMP_E_BADSID; 450 retval = 1; 451 goto sendpkt; 452 } 453 454 /* 455 * If the requester asks for more data than we can fit, 456 * silently clamp the request size down to RMPREADDATA. 457 * 458 * N.B. I do not know if this is "legal", however it seems 459 * to work. This is necessary for bpfwrite() on machines 460 * with MCLBYTES less than 1514. 461 */ 462 if (ntohs(req->r_rrq.rmp_size) > RMPREADDATA) 463 req->r_rrq.rmp_size = htons(RMPREADDATA); 464 465 /* 466 * Position read head on file according to info in request packet. 467 */ 468 GETWORD(req->r_rrq.rmp_offset, size); 469 if (lseek(oldconn->bootfd, (off_t)size, SEEK_SET) < 0) { 470 syslog(LOG_ERR, "SendReadRepl: lseek: %m (%s)", 471 EnetStr(rconn)); 472 rpl->r_rrpl.rmp_retcode = RMP_E_ABORT; 473 retval = 1; 474 goto sendpkt; 475 } 476 477 /* 478 * Read data directly into reply packet. 479 */ 480 if ((size = read(oldconn->bootfd, &rpl->r_rrpl.rmp_data, 481 (int) ntohs(req->r_rrq.rmp_size))) <= 0) { 482 if (size < 0) { 483 syslog(LOG_ERR, "SendReadRepl: read: %m (%s)", 484 EnetStr(rconn)); 485 rpl->r_rrpl.rmp_retcode = RMP_E_ABORT; 486 } else { 487 rpl->r_rrpl.rmp_retcode = RMP_E_EOF; 488 } 489 retval = 1; 490 goto sendpkt; 491 } 492 493 /* 494 * Set success indication. 495 */ 496 rpl->r_rrpl.rmp_retcode = RMP_E_OKAY; 497 498 sendpkt: 499 /* 500 * Set up assorted fields in reply packet. 501 */ 502 rpl->r_rrpl.rmp_type = RMP_READ_REPL; 503 COPYWORD(req->r_rrq.rmp_offset, rpl->r_rrpl.rmp_offset); 504 rpl->r_rrpl.rmp_session = req->r_rrq.rmp_session; 505 506 oldconn->rmplen = RMPREADSIZE(size); /* set size of packet */ 507 508 retval &= SendPacket(oldconn); /* send packet */ 509 510 if (madeconn) /* clean up after ourself */ 511 FreeConn(oldconn); 512 513 return (retval); 514 } 515 516 /* 517 ** BootDone -- free up memory allocated for a connection. 518 ** 519 ** Parameters: 520 ** rconn - incoming boot complete packet. 521 ** 522 ** Returns: 523 ** 1 on success, 0 on failure. 524 ** 525 ** Side Effects: 526 ** none. 527 */ 528 int 529 BootDone(rconn) 530 RMPCONN *rconn; 531 { 532 RMPCONN *oldconn; 533 struct rmp_packet *rpl; 534 535 /* 536 * If we cant find the connection, ignore the request. 537 */ 538 if ((oldconn = FindConn(rconn)) == NULL) { 539 syslog(LOG_ERR, "BootDone: no existing connection (%s)", 540 EnetStr(rconn)); 541 return(0); 542 } 543 544 rpl = &oldconn->rmp; /* cache ptr to RMP packet */ 545 546 /* 547 * Make sure Session ID's match. 548 */ 549 if (ntohs(rconn->rmp.r_rrq.rmp_session) != 550 ((rpl->r_type == RMP_BOOT_REPL)? ntohs(rpl->r_brpl.rmp_session): 551 ntohs(rpl->r_rrpl.rmp_session))) { 552 syslog(LOG_ERR, "BootDone: bad session id (%s)", 553 EnetStr(rconn)); 554 return(0); 555 } 556 557 RemoveConn(oldconn); /* remove connection */ 558 559 syslog(LOG_INFO, "%s: boot complete", EnetStr(rconn)); 560 561 return(1); 562 } 563 564 /* 565 ** SendPacket -- send an RMP packet to a remote host. 566 ** 567 ** Parameters: 568 ** rconn - packet to be sent. 569 ** 570 ** Returns: 571 ** 1 on success, 0 on failure. 572 ** 573 ** Side Effects: 574 ** none. 575 */ 576 int 577 SendPacket(rconn) 578 RMPCONN *rconn; 579 { 580 /* 581 * Set Ethernet Destination address to Source (BPF and the enet 582 * driver will take care of getting our source address set). 583 */ 584 memmove((char *)&rconn->rmp.hp_hdr.daddr[0], 585 (char *)&rconn->rmp.hp_hdr.saddr[0], RMP_ADDRLEN); 586 rconn->rmp.hp_hdr.len = htons(rconn->rmplen - sizeof(struct hp_hdr)); 587 588 /* 589 * Reverse 802.2/HP Extended Source & Destination Access Pts. 590 */ 591 rconn->rmp.hp_llc.dxsap = htons(HPEXT_SXSAP); 592 rconn->rmp.hp_llc.sxsap = htons(HPEXT_DXSAP); 593 594 /* 595 * Last time this connection was active. 596 */ 597 (void) gettimeofday(&rconn->tstamp, (struct timezone *)0); 598 599 if (DbgFp != NULL) /* display packet */ 600 DispPkt(rconn,DIR_SENT); 601 602 /* 603 * Send RMP packet to remote host. 604 */ 605 return(BpfWrite(rconn)); 606 } 607