NetBSD/sys/lib/libsa/bootparam.c
2019-04-02 22:25:10 +00:00

444 lines
10 KiB
C

/* $NetBSD: bootparam.c,v 1.21 2019/04/02 22:25:10 christos Exp $ */
/*
* Copyright (c) 1995 Gordon W. Ross
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* RPC/bootparams
*/
#include <sys/param.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#ifdef _STANDALONE
#include <lib/libkern/libkern.h>
#else
#include <string.h>
#endif
#include "rpcv2.h"
#include "stand.h"
#include "net.h"
#include "rpc.h"
#include "bootparam.h"
#ifdef DEBUG_RPC
#define RPC_PRINTF(a) printf a
#else
#define RPC_PRINTF(a)
#endif
struct in_addr bp_server_addr; /* net order */
n_short bp_server_port; /* net order */
uint32_t hostnamelen;
char domainname[FNAME_SIZE]; /* our DNS domain */
uint32_t domainnamelen;
/*
* RPC definitions for bootparamd
*/
#define BOOTPARAM_PROG 100026
#define BOOTPARAM_VERS 1
#define BOOTPARAM_WHOAMI 1
#define BOOTPARAM_GETFILE 2
/*
* Inet address in RPC messages
* (Note, really four ints, NOT chars. Blech.)
*/
struct xdr_inaddr {
uint32_t atype;
int32_t addr[4];
};
int xdr_inaddr_encode(char **, struct in_addr);
int xdr_inaddr_decode(char **, struct in_addr *);
int xdr_string_encode(char **, char *, uint32_t);
int xdr_string_decode(char **, char *, uint32_t *);
/*
* RPC: bootparam/whoami
* Given client IP address, get:
* client name (hostname)
* domain name (domainname)
* gateway address
*
* The hostname and domainname are set here for convenience.
*
* Note - bpsin is initialized to the broadcast address,
* and will be replaced with the bootparam server address
* after this call is complete. Have to use PMAP_PROC_CALL
* to make sure we get responses only from a servers that
* know about us (don't want to broadcast a getport call).
*/
int
bp_whoami(int sockfd)
{
/* RPC structures for PMAPPROC_CALLIT */
struct args {
uint32_t prog;
uint32_t vers;
uint32_t proc;
uint32_t arglen;
struct xdr_inaddr xina;
} *args;
struct repl {
uint16_t _pad;
uint16_t port;
uint32_t encap_len;
/* encapsulated data here */
n_long capsule[64];
} *repl;
struct {
n_long h[RPC_HEADER_WORDS];
struct args d;
} sdata;
struct {
n_long h[RPC_HEADER_WORDS];
struct repl d;
} rdata;
char *send_tail, *recv_head;
struct iodesc *d;
uint32_t x;
ssize_t len;
RPC_PRINTF(("%s: myip=%s\n", __func__, inet_ntoa(myip)));
if (!(d = socktodesc(sockfd))) {
RPC_PRINTF(("%s: bad socket. %d\n", __func__, sockfd));
return -1;
}
args = &sdata.d;
repl = &rdata.d;
/*
* Build request args for PMAPPROC_CALLIT.
*/
args->prog = htonl((uint32_t)BOOTPARAM_PROG);
args->vers = htonl((uint32_t)BOOTPARAM_VERS);
args->proc = htonl((uint32_t)BOOTPARAM_WHOAMI);
args->arglen = htonl((uint32_t)sizeof(struct xdr_inaddr));
send_tail = (char *)&args->xina;
/*
* append encapsulated data (client IP address)
*/
if (xdr_inaddr_encode(&send_tail, myip))
return -1;
/* RPC: portmap/callit */
--rpc_port;
d->myport = (uint16_t)htons((uint16_t)rpc_port);
d->destip.s_addr = INADDR_BROADCAST; /* XXX: subnet bcast? */
/* rpc_call will set d->destport */
len = rpc_call(d, PMAPPROG, PMAPVERS, PMAPPROC_CALLIT,
args, (size_t)(send_tail - (char *)args),
repl, sizeof(*repl));
if (len < 8) {
printf("%s: 'whoami' call failed\n", __func__);
return -1;
}
/* Save bootparam server address (from IP header). */
rpc_fromaddr(repl, &bp_server_addr, &bp_server_port);
/*
* Note that bp_server_port is now 111 due to the
* indirect call (using PMAPPROC_CALLIT), so get the
* actual port number from the reply data.
*/
bp_server_port = repl->port;
RPC_PRINTF(("%s: server at %s:%d\n", __func__,
inet_ntoa(bp_server_addr), ntohs((uint16_t)bp_server_port)));
/* We have just done a portmap call, so cache the portnum. */
rpc_pmap_putcache(bp_server_addr,
BOOTPARAM_PROG,
BOOTPARAM_VERS,
(int)ntohs((uint16_t)bp_server_port));
/*
* Parse the encapsulated results from bootparam/whoami
*/
x = ntohl((uint32_t)repl->encap_len);
if (len < x) {
printf("%s: short reply, %zd < %d\n", __func__, len, x);
return -1;
}
recv_head = (char *)repl->capsule;
/* client name */
hostnamelen = MAXHOSTNAMELEN-1;
if (xdr_string_decode(&recv_head, hostname, &hostnamelen)) {
RPC_PRINTF(("%s: bad hostname\n", __func__));
return -1;
}
/* domain name */
domainnamelen = MAXHOSTNAMELEN-1;
if (xdr_string_decode(&recv_head, domainname, &domainnamelen)) {
RPC_PRINTF(("%s: bad domainname\n", __func__));
return -1;
}
/* gateway address */
if (xdr_inaddr_decode(&recv_head, &gateip)) {
RPC_PRINTF(("%s: bad gateway\n", __func__));
return -1;
}
/* success */
return 0;
}
/*
* RPC: bootparam/getfile
* Given client name and file "key", get:
* server name
* server IP address
* server pathname
*/
int
bp_getfile(int sockfd, char *key, struct in_addr *serv_addr, char *pathname)
{
struct {
n_long h[RPC_HEADER_WORDS];
n_long d[64];
} sdata;
struct {
n_long h[RPC_HEADER_WORDS];
n_long d[128];
} rdata;
char serv_name[FNAME_SIZE];
char *send_tail, *recv_head;
/* misc... */
struct iodesc *d;
uint32_t sn_len, path_len;
ssize_t rlen;
if (!(d = socktodesc(sockfd))) {
RPC_PRINTF(("%s: bad socket. %d\n", __func__, sockfd));
return -1;
}
send_tail = (char *)sdata.d;
recv_head = (char *)rdata.d;
/*
* Build request message.
*/
/* client name (hostname) */
if (xdr_string_encode(&send_tail, hostname, hostnamelen)) {
RPC_PRINTF(("%s: bad client\n", __func__));
return -1;
}
/* key name (root or swap) */
if (xdr_string_encode(&send_tail, key, (uint32_t)strlen(key))) {
RPC_PRINTF(("%s: bad key\n", __func__));
return -1;
}
/* RPC: bootparam/getfile */
--rpc_port;
d->myport = htons((uint16_t)rpc_port);
d->destip = bp_server_addr;
/* rpc_call will set d->destport */
rlen = rpc_call(d,
BOOTPARAM_PROG, BOOTPARAM_VERS, BOOTPARAM_GETFILE,
sdata.d, (size_t)(send_tail - (char *)sdata.d),
rdata.d, sizeof(rdata.d));
if (rlen < 4) {
RPC_PRINTF(("%s: short reply\n", __func__));
errno = EBADRPC;
return -1;
}
recv_head = (char *)rdata.d;
/*
* Parse result message.
*/
/* server name */
sn_len = FNAME_SIZE - 1;
if (xdr_string_decode(&recv_head, serv_name, &sn_len)) {
RPC_PRINTF(("%s: bad server name\n", __func__));
return -1;
}
/* server IP address (mountd/NFS) */
if (xdr_inaddr_decode(&recv_head, serv_addr)) {
RPC_PRINTF(("%s: bad server addr\n", __func__));
return -1;
}
/* server pathname */
path_len = MAXPATHLEN - 1;
if (xdr_string_decode(&recv_head, pathname, &path_len)) {
RPC_PRINTF(("%s: bad server path\n", __func__));
return -1;
}
/* success */
return 0;
}
/*
* eXternal Data Representation routines.
* (but with non-standard args...)
*/
int
xdr_string_encode(char **pkt, char *str, uint32_t len)
{
uint32_t *lenp;
char *datap;
uint32_t padlen = (len + 3) & ~3U; /* padded length */
/* The data will be int aligned. */
lenp = (uint32_t *)*pkt;
*pkt += sizeof(*lenp);
*lenp = htonl(len);
datap = *pkt;
*pkt += padlen;
(void)memcpy(datap, str, len);
return 0;
}
/* len_p: bufsize - 1 */
int
xdr_string_decode(char **pkt, char *str, uint32_t *len_p)
{
uint32_t *lenp;
char *datap;
uint32_t slen; /* string length */
uint32_t plen; /* padded length */
/* The data will be int aligned. */
lenp = (uint32_t *)*pkt;
*pkt += sizeof(*lenp);
slen = ntohl(*lenp);
plen = (slen + 3) & ~3U;
if (slen > *len_p)
slen = *len_p;
datap = *pkt;
*pkt += plen;
(void)memcpy(str, datap, slen);
str[slen] = '\0';
*len_p = slen;
return 0;
}
/* ia: network order */
int
xdr_inaddr_encode(char **pkt, struct in_addr ia)
{
struct xdr_inaddr *xi;
u_char *cp;
uint32_t *ip;
union {
n_long l; /* network order */
u_char c[4];
} uia;
/* The data will be int aligned. */
xi = (struct xdr_inaddr *)*pkt;
*pkt += sizeof(*xi);
xi->atype = htonl(1);
uia.l = ia.s_addr;
cp = uia.c;
ip = (uint32_t *)xi->addr;
/*
* Note: the htonl() calls below DO NOT
* imply that uia.l is in host order.
* In fact this needs it in net order.
*/
*ip++ = htonl((uint32_t)*cp++);
*ip++ = htonl((uint32_t)*cp++);
*ip++ = htonl((uint32_t)*cp++);
*ip++ = htonl((uint32_t)*cp++);
return 0;
}
/* ia: network order */
int
xdr_inaddr_decode(char **pkt, struct in_addr *ia)
{
struct xdr_inaddr *xi;
u_char *cp;
uint32_t *ip;
union {
n_long l; /* network order */
u_char c[4];
} uia;
/* The data will be int aligned. */
xi = (struct xdr_inaddr *)*pkt;
*pkt += sizeof(*xi);
if (xi->atype != htonl((uint32_t)1)) {
RPC_PRINTF(("%s: bad addrtype=%d\n", __func__,
ntohl((uint32_t)nxi->atype)));
return -1;
}
cp = uia.c;
ip = (uint32_t *)xi->addr;
/*
* Note: the ntohl() calls below DO NOT
* imply that uia.l is in host order.
* In fact this needs it in net order.
*/
*cp++ = (u_char)ntohl(*ip++);
*cp++ = (u_char)ntohl(*ip++);
*cp++ = (u_char)ntohl(*ip++);
*cp++ = (u_char)ntohl(*ip++);
ia->s_addr = uia.l;
return 0;
}