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