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@cs.utah.edu any improvements that they make and grant
11 * CSS redistribution rights.
12 *
13 * %sccs.include.redist.c%
14 *
15 * @(#)rmpproto.c 8.1 (Berkeley) 06/04/93
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 8.1 (Berkeley) 06/04/93";
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
ProcessPacket(rconn,client)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
SendServerID(rconn)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
SendFileNo(req,rconn,filelist)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
SendBootRepl(req,rconn,filelist)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
SendReadRepl(rconn)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
BootDone(rconn)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
SendPacket(rconn)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