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