/* $NetBSD: bootparam.c,v 1.11 1997/06/26 19:11:32 drochner Exp $ */12/*3* Copyright (c) 1995 Gordon W. Ross4* All rights reserved.5*6* Redistribution and use in source and binary forms, with or without7* modification, are permitted provided that the following conditions8* are met:9* 1. Redistributions of source code must retain the above copyright10* notice, this list of conditions and the following disclaimer.11* 2. Redistributions in binary form must reproduce the above copyright12* notice, this list of conditions and the following disclaimer in the13* documentation and/or other materials provided with the distribution.14* 3. The name of the author may not be used to endorse or promote products15* derived from this software without specific prior written permission.16* 4. All advertising materials mentioning features or use of this software17* must display the following acknowledgement:18* This product includes software developed by Gordon W. Ross19*20* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR21* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES22* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.23* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,24* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT25* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,26* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY27* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT28* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF29* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.30*/3132/*33* RPC/bootparams34*/3536#include <sys/param.h>37#include <sys/socket.h>3839#include <net/if.h>4041#include <netinet/in.h>42#include <netinet/in_systm.h>4344#include <string.h>4546#include "rpcv2.h"4748#include "stand.h"49#include "net.h"50#include "netif.h"51#include "rpc.h"52#include "bootparam.h"5354#ifdef DEBUG_RPC55#define RPC_PRINTF(a) printf a56#else57#define RPC_PRINTF(a)58#endif5960struct in_addr bp_server_addr; /* net order */61n_short bp_server_port; /* net order */6263/*64* RPC definitions for bootparamd65*/66#define BOOTPARAM_PROG 10002667#define BOOTPARAM_VERS 168#define BOOTPARAM_WHOAMI 169#define BOOTPARAM_GETFILE 27071/*72* Inet address in RPC messages73* (Note, really four ints, NOT chars. Blech.)74*/75struct xdr_inaddr {76uint32_t atype;77int32_t addr[4];78};7980int xdr_inaddr_encode(char **p, struct in_addr ia);81int xdr_inaddr_decode(char **p, struct in_addr *ia);8283int xdr_string_encode(char **p, char *str, int len);84int xdr_string_decode(char **p, char *str, int *len_p);858687/*88* RPC: bootparam/whoami89* Given client IP address, get:90* client name (hostname)91* domain name (domainname)92* gateway address93*94* The hostname and domainname are set here for convenience.95*96* Note - bpsin is initialized to the broadcast address,97* and will be replaced with the bootparam server address98* after this call is complete. Have to use PMAP_PROC_CALL99* to make sure we get responses only from a servers that100* know about us (don't want to broadcast a getport call).101*/102int103bp_whoami(int sockfd)104{105/* RPC structures for PMAPPROC_CALLIT */106struct args {107uint32_t prog;108uint32_t vers;109uint32_t proc;110uint32_t arglen;111struct xdr_inaddr xina;112} *args;113struct repl {114uint16_t _pad;115uint16_t port;116uint32_t encap_len;117/* encapsulated data here */118n_long capsule[64];119} *repl;120struct {121n_long h[RPC_HEADER_WORDS];122struct args d;123} sdata;124char *send_tail, *recv_head;125struct iodesc *d;126void *pkt;127int len, x, rc;128129RPC_PRINTF(("bp_whoami: myip=%s\n", inet_ntoa(myip)));130131rc = -1;132if (!(d = socktodesc(sockfd))) {133RPC_PRINTF(("bp_whoami: bad socket. %d\n", sockfd));134return (rc);135}136args = &sdata.d;137138/*139* Build request args for PMAPPROC_CALLIT.140*/141args->prog = htonl(BOOTPARAM_PROG);142args->vers = htonl(BOOTPARAM_VERS);143args->proc = htonl(BOOTPARAM_WHOAMI);144args->arglen = htonl(sizeof(struct xdr_inaddr));145send_tail = (char*) &args->xina;146147/*148* append encapsulated data (client IP address)149*/150if (xdr_inaddr_encode(&send_tail, myip))151return (rc);152153/* RPC: portmap/callit */154d->myport = htons(--rpc_port);155d->destip.s_addr = INADDR_BROADCAST; /* XXX: subnet bcast? */156/* rpc_call will set d->destport */157158pkt = NULL;159len = rpc_call(d, PMAPPROG, PMAPVERS, PMAPPROC_CALLIT,160args, send_tail - (char*)args, (void **)&repl, &pkt);161if (len < 8) {162printf("bootparamd: 'whoami' call failed\n");163goto done;164}165166/* Save bootparam server address (from IP header). */167rpc_fromaddr(repl, &bp_server_addr, &bp_server_port);168169/*170* Note that bp_server_port is now 111 due to the171* indirect call (using PMAPPROC_CALLIT), so get the172* actual port number from the reply data.173*/174bp_server_port = repl->port;175176RPC_PRINTF(("bp_whoami: server at %s:%d\n",177inet_ntoa(bp_server_addr), ntohs(bp_server_port)));178179/* We have just done a portmap call, so cache the portnum. */180rpc_pmap_putcache(bp_server_addr,181BOOTPARAM_PROG,182BOOTPARAM_VERS,183(int)ntohs(bp_server_port));184185/*186* Parse the encapsulated results from bootparam/whoami187*/188x = ntohl(repl->encap_len);189if (len < x) {190printf("bp_whoami: short reply, %d < %d\n", len, x);191goto done;192}193recv_head = (char*) repl->capsule;194195/* client name */196hostnamelen = MAXHOSTNAMELEN-1;197if (xdr_string_decode(&recv_head, hostname, &hostnamelen)) {198RPC_PRINTF(("bp_whoami: bad hostname\n"));199goto done;200}201202/* domain name */203domainnamelen = MAXHOSTNAMELEN-1;204if (xdr_string_decode(&recv_head, domainname, &domainnamelen)) {205RPC_PRINTF(("bp_whoami: bad domainname\n"));206goto done;207}208209/* gateway address */210if (xdr_inaddr_decode(&recv_head, &gateip)) {211RPC_PRINTF(("bp_whoami: bad gateway\n"));212goto done;213}214215/* success */216rc = 0;217done:218free(pkt);219return (rc);220}221222223/*224* RPC: bootparam/getfile225* Given client name and file "key", get:226* server name227* server IP address228* server pathname229*/230int231bp_getfile(int sockfd, char *key, struct in_addr *serv_addr, char *pathname)232{233struct {234n_long h[RPC_HEADER_WORDS];235n_long d[64];236} sdata;237void *pkt;238char serv_name[FNAME_SIZE];239char *rdata, *send_tail;240/* misc... */241struct iodesc *d;242int rc = -1, sn_len, path_len, rlen;243244if (!(d = socktodesc(sockfd))) {245RPC_PRINTF(("bp_getfile: bad socket. %d\n", sockfd));246return (-1);247}248249send_tail = (char*) sdata.d;250251/*252* Build request message.253*/254255/* client name (hostname) */256if (xdr_string_encode(&send_tail, hostname, hostnamelen)) {257RPC_PRINTF(("bp_getfile: bad client\n"));258return (-1);259}260261/* key name (root or swap) */262if (xdr_string_encode(&send_tail, key, strlen(key))) {263RPC_PRINTF(("bp_getfile: bad key\n"));264return (-1);265}266267/* RPC: bootparam/getfile */268d->myport = htons(--rpc_port);269d->destip = bp_server_addr;270/* rpc_call will set d->destport */271pkt = NULL;272rlen = rpc_call(d,273BOOTPARAM_PROG, BOOTPARAM_VERS, BOOTPARAM_GETFILE,274sdata.d, send_tail - (char*)sdata.d,275(void **)&rdata, &pkt);276if (rlen < 4) {277RPC_PRINTF(("bp_getfile: short reply\n"));278errno = EBADRPC;279goto done;280}281282/*283* Parse result message.284*/285286/* server name */287sn_len = FNAME_SIZE-1;288if (xdr_string_decode(&rdata, serv_name, &sn_len)) {289RPC_PRINTF(("bp_getfile: bad server name\n"));290goto done;291}292293/* server IP address (mountd/NFS) */294if (xdr_inaddr_decode(&rdata, serv_addr)) {295RPC_PRINTF(("bp_getfile: bad server addr\n"));296goto done;297}298299/* server pathname */300path_len = MAXPATHLEN-1;301if (xdr_string_decode(&rdata, pathname, &path_len)) {302RPC_PRINTF(("bp_getfile: bad server path\n"));303goto done;304}305306/* success */307rc = 0;308done:309free(pkt);310return (rc);311}312313314/*315* eXternal Data Representation routines.316* (but with non-standard args...)317*/318319320int321xdr_string_encode(char **pkt, char *str, int len)322{323uint32_t *lenp;324char *datap;325int padlen = (len + 3) & ~3; /* padded length */326327/* The data will be int aligned. */328lenp = (uint32_t *) *pkt;329*pkt += sizeof(*lenp);330*lenp = htonl(len);331332datap = *pkt;333*pkt += padlen;334bcopy(str, datap, len);335336return (0);337}338339int340xdr_string_decode(char **pkt, char *str, int *len_p)341{342uint32_t *lenp;343char *datap;344int slen; /* string length */345int plen; /* padded length */346347/* The data will be int aligned. */348lenp = (uint32_t *) *pkt;349*pkt += sizeof(*lenp);350slen = ntohl(*lenp);351plen = (slen + 3) & ~3;352353if (slen > *len_p)354slen = *len_p;355datap = *pkt;356*pkt += plen;357bcopy(datap, str, slen);358359str[slen] = '\0';360*len_p = slen;361362return (0);363}364365366int367xdr_inaddr_encode(char **pkt, struct in_addr ia)368{369struct xdr_inaddr *xi;370u_char *cp;371int32_t *ip;372union {373n_long l; /* network order */374u_char c[4];375} uia;376377/* The data will be int aligned. */378xi = (struct xdr_inaddr *) *pkt;379*pkt += sizeof(*xi);380xi->atype = htonl(1);381uia.l = ia.s_addr;382cp = uia.c;383ip = xi->addr;384/*385* Note: the htonl() calls below DO NOT386* imply that uia.l is in host order.387* In fact this needs it in net order.388*/389*ip++ = htonl((unsigned int)*cp++);390*ip++ = htonl((unsigned int)*cp++);391*ip++ = htonl((unsigned int)*cp++);392*ip++ = htonl((unsigned int)*cp++);393394return (0);395}396397int398xdr_inaddr_decode(char **pkt, struct in_addr *ia)399{400struct xdr_inaddr *xi;401u_char *cp;402int32_t *ip;403union {404n_long l; /* network order */405u_char c[4];406} uia;407408/* The data will be int aligned. */409xi = (struct xdr_inaddr *) *pkt;410*pkt += sizeof(*xi);411if (xi->atype != htonl(1)) {412RPC_PRINTF(("xdr_inaddr_decode: bad addrtype=%d\n",413ntohl(xi->atype)));414return(-1);415}416417cp = uia.c;418ip = xi->addr;419/*420* Note: the ntohl() calls below DO NOT421* imply that uia.l is in host order.422* In fact this needs it in net order.423*/424*cp++ = ntohl(*ip++);425*cp++ = ntohl(*ip++);426*cp++ = ntohl(*ip++);427*cp++ = ntohl(*ip++);428ia->s_addr = uia.l;429430return (0);431}432433434