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