Home | History | Annotate | Line # | Download | only in libsa
      1 /*	$NetBSD: bootparam.c,v 1.21 2019/04/02 22:25:10 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1995 Gordon W. Ross
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     26  */
     27 
     28 /*
     29  * RPC/bootparams
     30  */
     31 
     32 #include <sys/param.h>
     33 #include <sys/socket.h>
     34 
     35 #include <net/if.h>
     36 
     37 #include <netinet/in.h>
     38 #include <netinet/in_systm.h>
     39 
     40 #ifdef _STANDALONE
     41 #include <lib/libkern/libkern.h>
     42 #else
     43 #include <string.h>
     44 #endif
     45 
     46 #include "rpcv2.h"
     47 
     48 #include "stand.h"
     49 #include "net.h"
     50 #include "rpc.h"
     51 #include "bootparam.h"
     52 
     53 #ifdef DEBUG_RPC
     54 #define RPC_PRINTF(a)	printf a
     55 #else
     56 #define RPC_PRINTF(a)
     57 #endif
     58 
     59 struct in_addr	bp_server_addr;	/* net order */
     60 n_short		bp_server_port;	/* net order */
     61 
     62 uint32_t	hostnamelen;
     63 char		domainname[FNAME_SIZE]; /* our DNS domain */
     64 uint32_t	domainnamelen;
     65 
     66 /*
     67  * RPC definitions for bootparamd
     68  */
     69 #define	BOOTPARAM_PROG		100026
     70 #define	BOOTPARAM_VERS		1
     71 #define BOOTPARAM_WHOAMI	1
     72 #define BOOTPARAM_GETFILE	2
     73 
     74 /*
     75  * Inet address in RPC messages
     76  * (Note, really four ints, NOT chars.  Blech.)
     77  */
     78 struct xdr_inaddr {
     79 	uint32_t  atype;
     80 	int32_t	addr[4];
     81 };
     82 
     83 int xdr_inaddr_encode(char **, struct in_addr);
     84 int xdr_inaddr_decode(char **, struct in_addr *);
     85 
     86 int xdr_string_encode(char **, char *, uint32_t);
     87 int xdr_string_decode(char **, char *, uint32_t *);
     88 
     89 
     90 /*
     91  * RPC: bootparam/whoami
     92  * Given client IP address, get:
     93  *	client name	(hostname)
     94  *	domain name (domainname)
     95  *	gateway address
     96  *
     97  * The hostname and domainname are set here for convenience.
     98  *
     99  * Note - bpsin is initialized to the broadcast address,
    100  * and will be replaced with the bootparam server address
    101  * after this call is complete.  Have to use PMAP_PROC_CALL
    102  * to make sure we get responses only from a servers that
    103  * know about us (don't want to broadcast a getport call).
    104  */
    105 int
    106 bp_whoami(int sockfd)
    107 {
    108 	/* RPC structures for PMAPPROC_CALLIT */
    109 	struct args {
    110 		uint32_t prog;
    111 		uint32_t vers;
    112 		uint32_t proc;
    113 		uint32_t arglen;
    114 		struct xdr_inaddr xina;
    115 	} *args;
    116 	struct repl {
    117 		uint16_t _pad;
    118 		uint16_t port;
    119 		uint32_t encap_len;
    120 		/* encapsulated data here */
    121 		n_long  capsule[64];
    122 	} *repl;
    123 	struct {
    124 		n_long	h[RPC_HEADER_WORDS];
    125 		struct args d;
    126 	} sdata;
    127 	struct {
    128 		n_long	h[RPC_HEADER_WORDS];
    129 		struct repl d;
    130 	} rdata;
    131 	char *send_tail, *recv_head;
    132 	struct iodesc *d;
    133 	uint32_t x;
    134 	ssize_t len;
    135 
    136 	RPC_PRINTF(("%s: myip=%s\n", __func__, inet_ntoa(myip)));
    137 
    138 	if (!(d = socktodesc(sockfd))) {
    139 		RPC_PRINTF(("%s: bad socket. %d\n", __func__, sockfd));
    140 		return -1;
    141 	}
    142 	args = &sdata.d;
    143 	repl = &rdata.d;
    144 
    145 	/*
    146 	 * Build request args for PMAPPROC_CALLIT.
    147 	 */
    148 	args->prog = htonl((uint32_t)BOOTPARAM_PROG);
    149 	args->vers = htonl((uint32_t)BOOTPARAM_VERS);
    150 	args->proc = htonl((uint32_t)BOOTPARAM_WHOAMI);
    151 	args->arglen = htonl((uint32_t)sizeof(struct xdr_inaddr));
    152 	send_tail = (char *)&args->xina;
    153 
    154 	/*
    155 	 * append encapsulated data (client IP address)
    156 	 */
    157 	if (xdr_inaddr_encode(&send_tail, myip))
    158 		return -1;
    159 
    160 	/* RPC: portmap/callit */
    161 	--rpc_port;
    162 	d->myport = (uint16_t)htons((uint16_t)rpc_port);
    163 	d->destip.s_addr = INADDR_BROADCAST;	/* XXX: subnet bcast? */
    164 	/* rpc_call will set d->destport */
    165 
    166 	len = rpc_call(d, PMAPPROG, PMAPVERS, PMAPPROC_CALLIT,
    167 				  args, (size_t)(send_tail - (char *)args),
    168 				  repl, sizeof(*repl));
    169 	if (len < 8) {
    170 		printf("%s: 'whoami' call failed\n", __func__);
    171 		return -1;
    172 	}
    173 
    174 	/* Save bootparam server address (from IP header). */
    175 	rpc_fromaddr(repl, &bp_server_addr, &bp_server_port);
    176 
    177 	/*
    178 	 * Note that bp_server_port is now 111 due to the
    179 	 * indirect call (using PMAPPROC_CALLIT), so get the
    180 	 * actual port number from the reply data.
    181 	 */
    182 	bp_server_port = repl->port;
    183 
    184 	RPC_PRINTF(("%s: server at %s:%d\n", __func__,
    185 	    inet_ntoa(bp_server_addr), ntohs((uint16_t)bp_server_port)));
    186 
    187 	/* We have just done a portmap call, so cache the portnum. */
    188 	rpc_pmap_putcache(bp_server_addr,
    189 			  BOOTPARAM_PROG,
    190 			  BOOTPARAM_VERS,
    191 			  (int)ntohs((uint16_t)bp_server_port));
    192 
    193 	/*
    194 	 * Parse the encapsulated results from bootparam/whoami
    195 	 */
    196 	x = ntohl((uint32_t)repl->encap_len);
    197 	if (len < x) {
    198 		printf("%s: short reply, %zd < %d\n", __func__, len, x);
    199 		return -1;
    200 	}
    201 	recv_head = (char *)repl->capsule;
    202 
    203 	/* client name */
    204 	hostnamelen = MAXHOSTNAMELEN-1;
    205 	if (xdr_string_decode(&recv_head, hostname, &hostnamelen)) {
    206 		RPC_PRINTF(("%s: bad hostname\n", __func__));
    207 		return -1;
    208 	}
    209 
    210 	/* domain name */
    211 	domainnamelen = MAXHOSTNAMELEN-1;
    212 	if (xdr_string_decode(&recv_head, domainname, &domainnamelen)) {
    213 		RPC_PRINTF(("%s: bad domainname\n", __func__));
    214 		return -1;
    215 	}
    216 
    217 	/* gateway address */
    218 	if (xdr_inaddr_decode(&recv_head, &gateip)) {
    219 		RPC_PRINTF(("%s: bad gateway\n", __func__));
    220 		return -1;
    221 	}
    222 
    223 	/* success */
    224 	return 0;
    225 }
    226 
    227 
    228 /*
    229  * RPC: bootparam/getfile
    230  * Given client name and file "key", get:
    231  *	server name
    232  *	server IP address
    233  *	server pathname
    234  */
    235 int
    236 bp_getfile(int sockfd, char *key, struct in_addr *serv_addr, char *pathname)
    237 {
    238 	struct {
    239 		n_long	h[RPC_HEADER_WORDS];
    240 		n_long  d[64];
    241 	} sdata;
    242 	struct {
    243 		n_long	h[RPC_HEADER_WORDS];
    244 		n_long  d[128];
    245 	} rdata;
    246 	char serv_name[FNAME_SIZE];
    247 	char *send_tail, *recv_head;
    248 	/* misc... */
    249 	struct iodesc *d;
    250 	uint32_t sn_len, path_len;
    251 	ssize_t rlen;
    252 
    253 	if (!(d = socktodesc(sockfd))) {
    254 		RPC_PRINTF(("%s: bad socket. %d\n", __func__, sockfd));
    255 		return -1;
    256 	}
    257 
    258 	send_tail = (char *)sdata.d;
    259 	recv_head = (char *)rdata.d;
    260 
    261 	/*
    262 	 * Build request message.
    263 	 */
    264 
    265 	/* client name (hostname) */
    266 	if (xdr_string_encode(&send_tail, hostname, hostnamelen)) {
    267 		RPC_PRINTF(("%s: bad client\n", __func__));
    268 		return -1;
    269 	}
    270 
    271 	/* key name (root or swap) */
    272 	if (xdr_string_encode(&send_tail, key, (uint32_t)strlen(key))) {
    273 		RPC_PRINTF(("%s: bad key\n", __func__));
    274 		return -1;
    275 	}
    276 
    277 	/* RPC: bootparam/getfile */
    278 	--rpc_port;
    279 	d->myport = htons((uint16_t)rpc_port);
    280 	d->destip = bp_server_addr;
    281 	/* rpc_call will set d->destport */
    282 
    283 	rlen = rpc_call(d,
    284 		BOOTPARAM_PROG, BOOTPARAM_VERS, BOOTPARAM_GETFILE,
    285 		sdata.d, (size_t)(send_tail - (char *)sdata.d),
    286 		rdata.d, sizeof(rdata.d));
    287 	if (rlen < 4) {
    288 		RPC_PRINTF(("%s: short reply\n", __func__));
    289 		errno = EBADRPC;
    290 		return -1;
    291 	}
    292 	recv_head = (char *)rdata.d;
    293 
    294 	/*
    295 	 * Parse result message.
    296 	 */
    297 
    298 	/* server name */
    299 	sn_len = FNAME_SIZE - 1;
    300 	if (xdr_string_decode(&recv_head, serv_name, &sn_len)) {
    301 		RPC_PRINTF(("%s: bad server name\n", __func__));
    302 		return -1;
    303 	}
    304 
    305 	/* server IP address (mountd/NFS) */
    306 	if (xdr_inaddr_decode(&recv_head, serv_addr)) {
    307 		RPC_PRINTF(("%s: bad server addr\n", __func__));
    308 		return -1;
    309 	}
    310 
    311 	/* server pathname */
    312 	path_len = MAXPATHLEN - 1;
    313 	if (xdr_string_decode(&recv_head, pathname, &path_len)) {
    314 		RPC_PRINTF(("%s: bad server path\n", __func__));
    315 		return -1;
    316 	}
    317 
    318 	/* success */
    319 	return 0;
    320 }
    321 
    322 
    323 /*
    324  * eXternal Data Representation routines.
    325  * (but with non-standard args...)
    326  */
    327 
    328 
    329 int
    330 xdr_string_encode(char **pkt, char *str, uint32_t len)
    331 {
    332 	uint32_t *lenp;
    333 	char *datap;
    334 	uint32_t padlen = (len + 3) & ~3U;	/* padded length */
    335 
    336 	/* The data will be int aligned. */
    337 	lenp = (uint32_t *)*pkt;
    338 	*pkt += sizeof(*lenp);
    339 	*lenp = htonl(len);
    340 
    341 	datap = *pkt;
    342 	*pkt += padlen;
    343 	(void)memcpy(datap, str, len);
    344 
    345 	return 0;
    346 }
    347 
    348 /* len_p: bufsize - 1 */
    349 int
    350 xdr_string_decode(char **pkt, char *str, uint32_t *len_p)
    351 {
    352 	uint32_t *lenp;
    353 	char *datap;
    354 	uint32_t slen;	/* string length */
    355 	uint32_t plen;	/* padded length */
    356 
    357 	/* The data will be int aligned. */
    358 	lenp = (uint32_t *)*pkt;
    359 	*pkt += sizeof(*lenp);
    360 	slen = ntohl(*lenp);
    361 	plen = (slen + 3) & ~3U;
    362 
    363 	if (slen > *len_p)
    364 		slen = *len_p;
    365 	datap = *pkt;
    366 	*pkt += plen;
    367 	(void)memcpy(str, datap, slen);
    368 
    369 	str[slen] = '\0';
    370 	*len_p = slen;
    371 
    372 	return 0;
    373 }
    374 
    375 
    376 /* ia: network order */
    377 int
    378 xdr_inaddr_encode(char **pkt, struct in_addr ia)
    379 {
    380 	struct xdr_inaddr *xi;
    381 	u_char *cp;
    382 	uint32_t *ip;
    383 	union {
    384 		n_long l;	/* network order */
    385 		u_char c[4];
    386 	} uia;
    387 
    388 	/* The data will be int aligned. */
    389 	xi = (struct xdr_inaddr *)*pkt;
    390 	*pkt += sizeof(*xi);
    391 	xi->atype = htonl(1);
    392 	uia.l = ia.s_addr;
    393 	cp = uia.c;
    394 	ip = (uint32_t *)xi->addr;
    395 	/*
    396 	 * Note: the htonl() calls below DO NOT
    397 	 * imply that uia.l is in host order.
    398 	 * In fact this needs it in net order.
    399 	 */
    400 	*ip++ = htonl((uint32_t)*cp++);
    401 	*ip++ = htonl((uint32_t)*cp++);
    402 	*ip++ = htonl((uint32_t)*cp++);
    403 	*ip++ = htonl((uint32_t)*cp++);
    404 
    405 	return 0;
    406 }
    407 
    408 /* ia: network order */
    409 int
    410 xdr_inaddr_decode(char **pkt, struct in_addr *ia)
    411 {
    412 	struct xdr_inaddr *xi;
    413 	u_char *cp;
    414 	uint32_t *ip;
    415 	union {
    416 		n_long l;	/* network order */
    417 		u_char c[4];
    418 	} uia;
    419 
    420 	/* The data will be int aligned. */
    421 	xi = (struct xdr_inaddr *)*pkt;
    422 	*pkt += sizeof(*xi);
    423 	if (xi->atype != htonl((uint32_t)1)) {
    424 		RPC_PRINTF(("%s: bad addrtype=%d\n", __func__,
    425 		    ntohl((uint32_t)nxi->atype)));
    426 		return -1;
    427 	}
    428 
    429 	cp = uia.c;
    430 	ip = (uint32_t *)xi->addr;
    431 	/*
    432 	 * Note: the ntohl() calls below DO NOT
    433 	 * imply that uia.l is in host order.
    434 	 * In fact this needs it in net order.
    435 	 */
    436 	*cp++ = (u_char)ntohl(*ip++);
    437 	*cp++ = (u_char)ntohl(*ip++);
    438 	*cp++ = (u_char)ntohl(*ip++);
    439 	*cp++ = (u_char)ntohl(*ip++);
    440 	ia->s_addr = uia.l;
    441 
    442 	return 0;
    443 }
    444