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