1/* $NetBSD: rmpproto.c,v 1.14 2004/07/06 13:05:25 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. 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 48static char sccsid[] = "@(#)rmpproto.c 8.1 (Berkeley) 6/4/93"; 49#else 50__RCSID("$NetBSD: rmpproto.c,v 1.14 2004/07/06 13:05:25 mycroft 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*/ 87void 88ProcessPacket(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*/ 177int 178SendServerID(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*/ 229int 230SendFileNo(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*/ 292int 293SendBootRepl(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 369match: 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 389sendpkt: 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*/ 410int 411SendReadRepl(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 498sendpkt: 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*/ 528int 529BootDone(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*/ 576int 577SendPacket(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