/*-1* SPDX-License-Identifier: BSD-3-Clause2*3* Copyright (c) 1988, 1992 The University of Utah and the Center4* for Software Science (CSS).5* Copyright (c) 1992, 19936* The Regents of the University of California. All rights reserved.7*8* This code is derived from software contributed to Berkeley by9* the Center for Software Science of the University of Utah Computer10* Science Department. CSS requests users of this software to return11* to [email protected] any improvements that they make and grant12* CSS redistribution rights.13*14* Redistribution and use in source and binary forms, with or without15* modification, are permitted provided that the following conditions16* are met:17* 1. Redistributions of source code must retain the above copyright18* notice, this list of conditions and the following disclaimer.19* 2. Redistributions in binary form must reproduce the above copyright20* notice, this list of conditions and the following disclaimer in the21* documentation and/or other materials provided with the distribution.22* 3. Neither the name of the University nor the names of its contributors23* may be used to endorse or promote products derived from this software24* without specific prior written permission.25*26* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND27* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE28* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE29* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE30* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL31* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS32* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)33* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT34* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY35* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF36* SUCH DAMAGE.37*38* From: Utah Hdr: rmpproto.c 3.1 92/07/0639* Author: Jeff Forys, University of Utah CSS40*/4142#include <sys/param.h>43#include <sys/time.h>44#include <netinet/in.h>4546#include <errno.h>47#include <fcntl.h>48#include <stdio.h>49#include <string.h>50#include <syslog.h>51#include <unistd.h>52#include "defs.h"5354/*55** ProcessPacket -- determine packet type and do what's required.56**57** An RMP BOOT packet has been received. Look at the type field58** and process Boot Requests, Read Requests, and Boot Complete59** packets. Any other type will be dropped with a warning msg.60**61** Parameters:62** rconn - the new connection63** client - list of files available to this host64**65** Returns:66** Nothing.67**68** Side Effects:69** - If this is a valid boot request, it will be added to70** the linked list of outstanding requests (RmpConns).71** - If this is a valid boot complete, its associated72** entry in RmpConns will be deleted.73** - Also, unless we run out of memory, a reply will be74** sent to the host that sent the packet.75*/76void77ProcessPacket(RMPCONN *rconn, CLIENT *client)78{79struct rmp_packet *rmp;80RMPCONN *rconnout;8182rmp = &rconn->rmp; /* cache pointer to RMP packet */8384switch(rmp->r_type) { /* do what we came here to do */85case RMP_BOOT_REQ: /* boot request */86if ((rconnout = NewConn(rconn)) == NULL)87return;8889/*90* If the Session ID is 0xffff, this is a "probe"91* packet and we do not want to add the connection92* to the linked list of active connections. There93* are two types of probe packets, if the Sequence94* Number is 0 they want to know our host name, o/w95* they want the name of the file associated with96* the number spec'd by the Sequence Number.97*98* If this is an actual boot request, open the file99* and send a reply. If SendBootRepl() does not100* return 0, add the connection to the linked list101* of active connections, otherwise delete it since102* an error was encountered.103*/104if (ntohs(rmp->r_brq.rmp_session) == RMP_PROBESID) {105if (WORDZE(rmp->r_brq.rmp_seqno))106(void) SendServerID(rconnout);107else108(void) SendFileNo(rmp, rconnout,109client? client->files:110BootFiles);111FreeConn(rconnout);112} else {113if (SendBootRepl(rmp, rconnout,114client? client->files: BootFiles))115AddConn(rconnout);116else117FreeConn(rconnout);118}119break;120121case RMP_BOOT_REPL: /* boot reply (not valid) */122syslog(LOG_WARNING, "%s: sent a boot reply",123EnetStr(rconn));124break;125126case RMP_READ_REQ: /* read request */127/*128* Send a portion of the boot file.129*/130(void) SendReadRepl(rconn);131break;132133case RMP_READ_REPL: /* read reply (not valid) */134syslog(LOG_WARNING, "%s: sent a read reply",135EnetStr(rconn));136break;137138case RMP_BOOT_DONE: /* boot complete */139/*140* Remove the entry from the linked list of active141* connections.142*/143(void) BootDone(rconn);144break;145146default: /* unknown RMP packet type */147syslog(LOG_WARNING, "%s: unknown packet type (%u)",148EnetStr(rconn), rmp->r_type);149}150}151152/*153** SendServerID -- send our host name to who ever requested it.154**155** Parameters:156** rconn - the reply packet to be formatted.157**158** Returns:159** 1 on success, 0 on failure.160**161** Side Effects:162** none.163*/164int165SendServerID(RMPCONN *rconn)166{167struct rmp_packet *rpl;168char *src, *dst;169u_int8_t *size;170171rpl = &rconn->rmp; /* cache ptr to RMP packet */172173/*174* Set up assorted fields in reply packet.175*/176rpl->r_brpl.rmp_type = RMP_BOOT_REPL;177rpl->r_brpl.rmp_retcode = RMP_E_OKAY;178ZEROWORD(rpl->r_brpl.rmp_seqno);179rpl->r_brpl.rmp_session = 0;180rpl->r_brpl.rmp_version = htons(RMP_VERSION);181182size = &rpl->r_brpl.rmp_flnmsize; /* ptr to length of host name */183184/*185* Copy our host name into the reply packet incrementing the186* length as we go. Stop at RMP_HOSTLEN or the first dot.187*/188src = MyHost;189dst = (char *) &rpl->r_brpl.rmp_flnm;190for (*size = 0; *size < RMP_HOSTLEN; (*size)++) {191if (*src == '.' || *src == '\0')192break;193*dst++ = *src++;194}195196rconn->rmplen = RMPBOOTSIZE(*size); /* set packet length */197198return(SendPacket(rconn)); /* send packet */199}200201/*202** SendFileNo -- send the name of a bootable file to the requester.203**204** Parameters:205** req - RMP BOOT packet containing the request.206** rconn - the reply packet to be formatted.207** filelist - list of files available to the requester.208**209** Returns:210** 1 on success, 0 on failure.211**212** Side Effects:213** none.214*/215int216SendFileNo(struct rmp_packet *req, RMPCONN *rconn, char *filelist[])217{218struct rmp_packet *rpl;219char *src, *dst;220u_int8_t *size;221int i;222223GETWORD(req->r_brpl.rmp_seqno, i); /* SeqNo is really FileNo */224rpl = &rconn->rmp; /* cache ptr to RMP packet */225226/*227* Set up assorted fields in reply packet.228*/229rpl->r_brpl.rmp_type = RMP_BOOT_REPL;230PUTWORD(i, rpl->r_brpl.rmp_seqno);231i--;232rpl->r_brpl.rmp_session = 0;233rpl->r_brpl.rmp_version = htons(RMP_VERSION);234235size = &rpl->r_brpl.rmp_flnmsize; /* ptr to length of filename */236*size = 0; /* init length to zero */237238/*239* Copy the file name into the reply packet incrementing the240* length as we go. Stop at end of string or when RMPBOOTDATA241* characters have been copied. Also, set return code to242* indicate success or "no more files".243*/244if (i < C_MAXFILE && filelist[i] != NULL) {245src = filelist[i];246dst = (char *)&rpl->r_brpl.rmp_flnm;247for (; *src && *size < RMPBOOTDATA; (*size)++) {248if (*src == '\0')249break;250*dst++ = *src++;251}252rpl->r_brpl.rmp_retcode = RMP_E_OKAY;253} else254rpl->r_brpl.rmp_retcode = RMP_E_NODFLT;255256rconn->rmplen = RMPBOOTSIZE(*size); /* set packet length */257258return(SendPacket(rconn)); /* send packet */259}260261/*262** SendBootRepl -- open boot file and respond to boot request.263**264** Parameters:265** req - RMP BOOT packet containing the request.266** rconn - the reply packet to be formatted.267** filelist - list of files available to the requester.268**269** Returns:270** 1 on success, 0 on failure.271**272** Side Effects:273** none.274*/275int276SendBootRepl(struct rmp_packet *req, RMPCONN *rconn, char *filelist[])277{278int retval;279char *filename, filepath[RMPBOOTDATA+1];280RMPCONN *oldconn;281struct rmp_packet *rpl;282char *src, *dst1, *dst2;283u_int8_t i;284285/*286* If another connection already exists, delete it since we287* are obviously starting again.288*/289if ((oldconn = FindConn(rconn)) != NULL) {290syslog(LOG_WARNING, "%s: dropping existing connection",291EnetStr(oldconn));292RemoveConn(oldconn);293}294295rpl = &rconn->rmp; /* cache ptr to RMP packet */296297/*298* Set up assorted fields in reply packet.299*/300rpl->r_brpl.rmp_type = RMP_BOOT_REPL;301COPYWORD(req->r_brq.rmp_seqno, rpl->r_brpl.rmp_seqno);302rpl->r_brpl.rmp_session = htons(GenSessID());303rpl->r_brpl.rmp_version = htons(RMP_VERSION);304rpl->r_brpl.rmp_flnmsize = req->r_brq.rmp_flnmsize;305306/*307* Copy file name to `filepath' string, and into reply packet.308*/309src = &req->r_brq.rmp_flnm;310dst1 = filepath;311dst2 = &rpl->r_brpl.rmp_flnm;312for (i = 0; i < req->r_brq.rmp_flnmsize; i++)313*dst1++ = *dst2++ = *src++;314*dst1 = '\0';315316/*317* If we are booting HP-UX machines, their secondary loader will318* ask for files like "/hp-ux". As a security measure, we do not319* allow boot files to lay outside the boot directory (unless they320* are purposely link'd out. So, make `filename' become the path-321* stripped file name and spoof the client into thinking that it322* really got what it wanted.323*/324filename = strrchr(filepath,'/');325filename = filename? filename + 1: filepath;326327/*328* Check that this is a valid boot file name.329*/330for (i = 0; i < C_MAXFILE && filelist[i] != NULL; i++)331if (STREQN(filename, filelist[i]))332goto match;333334/*335* Invalid boot file name, set error and send reply packet.336*/337rpl->r_brpl.rmp_retcode = RMP_E_NOFILE;338retval = 0;339goto sendpkt;340341match:342/*343* This is a valid boot file. Open the file and save the file344* descriptor associated with this connection and set success345* indication. If the file couldnt be opened, set error:346* "no such file or dir" - RMP_E_NOFILE347* "file table overflow" - RMP_E_BUSY348* "too many open files" - RMP_E_BUSY349* anything else - RMP_E_OPENFILE350*/351if ((rconn->bootfd = open(filename, O_RDONLY, 0600)) < 0) {352rpl->r_brpl.rmp_retcode = (errno == ENOENT)? RMP_E_NOFILE:353(errno == EMFILE || errno == ENFILE)? RMP_E_BUSY:354RMP_E_OPENFILE;355retval = 0;356} else {357rpl->r_brpl.rmp_retcode = RMP_E_OKAY;358retval = 1;359}360361sendpkt:362syslog(LOG_INFO, "%s: request to boot %s (%s)",363EnetStr(rconn), filename, retval? "granted": "denied");364365rconn->rmplen = RMPBOOTSIZE(rpl->r_brpl.rmp_flnmsize);366367return (retval & SendPacket(rconn));368}369370/*371** SendReadRepl -- send a portion of the boot file to the requester.372**373** Parameters:374** rconn - the reply packet to be formatted.375**376** Returns:377** 1 on success, 0 on failure.378**379** Side Effects:380** none.381*/382int383SendReadRepl(RMPCONN *rconn)384{385int retval = 0;386RMPCONN *oldconn;387struct rmp_packet *rpl, *req;388int size = 0;389int madeconn = 0;390391/*392* Find the old connection. If one doesn't exist, create one only393* to return the error code.394*/395if ((oldconn = FindConn(rconn)) == NULL) {396if ((oldconn = NewConn(rconn)) == NULL)397return(0);398syslog(LOG_ERR, "SendReadRepl: no active connection (%s)",399EnetStr(rconn));400madeconn++;401}402403req = &rconn->rmp; /* cache ptr to request packet */404rpl = &oldconn->rmp; /* cache ptr to reply packet */405406if (madeconn) { /* no active connection above; abort */407rpl->r_rrpl.rmp_retcode = RMP_E_ABORT;408retval = 1;409goto sendpkt;410}411412/*413* Make sure Session ID's match.414*/415if (ntohs(req->r_rrq.rmp_session) !=416((rpl->r_type == RMP_BOOT_REPL)? ntohs(rpl->r_brpl.rmp_session):417ntohs(rpl->r_rrpl.rmp_session))) {418syslog(LOG_ERR, "SendReadRepl: bad session id (%s)",419EnetStr(rconn));420rpl->r_rrpl.rmp_retcode = RMP_E_BADSID;421retval = 1;422goto sendpkt;423}424425/*426* If the requester asks for more data than we can fit,427* silently clamp the request size down to RMPREADDATA.428*429* N.B. I do not know if this is "legal", however it seems430* to work. This is necessary for bpfwrite() on machines431* with MCLBYTES less than 1514.432*/433if (ntohs(req->r_rrq.rmp_size) > RMPREADDATA)434req->r_rrq.rmp_size = htons(RMPREADDATA);435436/*437* Position read head on file according to info in request packet.438*/439GETWORD(req->r_rrq.rmp_offset, size);440if (lseek(oldconn->bootfd, (off_t)size, SEEK_SET) < 0) {441syslog(LOG_ERR, "SendReadRepl: lseek: %m (%s)",442EnetStr(rconn));443rpl->r_rrpl.rmp_retcode = RMP_E_ABORT;444retval = 1;445goto sendpkt;446}447448/*449* Read data directly into reply packet.450*/451if ((size = read(oldconn->bootfd, &rpl->r_rrpl.rmp_data,452(int) ntohs(req->r_rrq.rmp_size))) <= 0) {453if (size < 0) {454syslog(LOG_ERR, "SendReadRepl: read: %m (%s)",455EnetStr(rconn));456rpl->r_rrpl.rmp_retcode = RMP_E_ABORT;457} else {458rpl->r_rrpl.rmp_retcode = RMP_E_EOF;459}460retval = 1;461goto sendpkt;462}463464/*465* Set success indication.466*/467rpl->r_rrpl.rmp_retcode = RMP_E_OKAY;468469sendpkt:470/*471* Set up assorted fields in reply packet.472*/473rpl->r_rrpl.rmp_type = RMP_READ_REPL;474COPYWORD(req->r_rrq.rmp_offset, rpl->r_rrpl.rmp_offset);475rpl->r_rrpl.rmp_session = req->r_rrq.rmp_session;476477oldconn->rmplen = RMPREADSIZE(size); /* set size of packet */478479retval &= SendPacket(oldconn); /* send packet */480481if (madeconn) /* clean up after ourself */482FreeConn(oldconn);483484return (retval);485}486487/*488** BootDone -- free up memory allocated for a connection.489**490** Parameters:491** rconn - incoming boot complete packet.492**493** Returns:494** 1 on success, 0 on failure.495**496** Side Effects:497** none.498*/499int500BootDone(RMPCONN *rconn)501{502RMPCONN *oldconn;503struct rmp_packet *rpl;504505/*506* If we can't find the connection, ignore the request.507*/508if ((oldconn = FindConn(rconn)) == NULL) {509syslog(LOG_ERR, "BootDone: no existing connection (%s)",510EnetStr(rconn));511return(0);512}513514rpl = &oldconn->rmp; /* cache ptr to RMP packet */515516/*517* Make sure Session ID's match.518*/519if (ntohs(rconn->rmp.r_rrq.rmp_session) !=520((rpl->r_type == RMP_BOOT_REPL)? ntohs(rpl->r_brpl.rmp_session):521ntohs(rpl->r_rrpl.rmp_session))) {522syslog(LOG_ERR, "BootDone: bad session id (%s)",523EnetStr(rconn));524return(0);525}526527RemoveConn(oldconn); /* remove connection */528529syslog(LOG_INFO, "%s: boot complete", EnetStr(rconn));530531return(1);532}533534/*535** SendPacket -- send an RMP packet to a remote host.536**537** Parameters:538** rconn - packet to be sent.539**540** Returns:541** 1 on success, 0 on failure.542**543** Side Effects:544** none.545*/546int547SendPacket(RMPCONN *rconn)548{549/*550* Set Ethernet Destination address to Source (BPF and the enet551* driver will take care of getting our source address set).552*/553memmove((char *)&rconn->rmp.hp_hdr.daddr[0],554(char *)&rconn->rmp.hp_hdr.saddr[0], RMP_ADDRLEN);555rconn->rmp.hp_hdr.len = htons(rconn->rmplen - sizeof(struct hp_hdr));556557/*558* Reverse 802.2/HP Extended Source & Destination Access Pts.559*/560rconn->rmp.hp_llc.dxsap = htons(HPEXT_SXSAP);561rconn->rmp.hp_llc.sxsap = htons(HPEXT_DXSAP);562563/*564* Last time this connection was active.565*/566(void)gettimeofday(&rconn->tstamp, NULL);567568if (DbgFp != NULL) /* display packet */569DispPkt(rconn,DIR_SENT);570571/*572* Send RMP packet to remote host.573*/574return(BpfWrite(rconn));575}576577578