Home | History | Annotate | Line # | Download | only in libsa
bootparam.c revision 1.19
      1 /*	$NetBSD: bootparam.c,v 1.19 2009/10/21 23:12:10 snj 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 int		hostnamelen;
     63 char		domainname[FNAME_SIZE]; /* our DNS domain */
     64 int		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 	u_int32_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 *, int);
     87 int xdr_string_decode(char **, char *, int *);
     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 		u_int32_t prog;
    111 		u_int32_t vers;
    112 		u_int32_t proc;
    113 		u_int32_t arglen;
    114 		struct xdr_inaddr xina;
    115 	} *args;
    116 	struct repl {
    117 		u_int16_t _pad;
    118 		u_int16_t port;
    119 		u_int32_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 	int len, x;
    134 
    135 	RPC_PRINTF(("bp_whoami: myip=%s\n", inet_ntoa(myip)));
    136 
    137 	if (!(d = socktodesc(sockfd))) {
    138 		RPC_PRINTF(("bp_whoami: bad socket. %d\n", sockfd));
    139 		return -1;
    140 	}
    141 	args = &sdata.d;
    142 	repl = &rdata.d;
    143 
    144 	/*
    145 	 * Build request args for PMAPPROC_CALLIT.
    146 	 */
    147 	args->prog = htonl(BOOTPARAM_PROG);
    148 	args->vers = htonl(BOOTPARAM_VERS);
    149 	args->proc = htonl(BOOTPARAM_WHOAMI);
    150 	args->arglen = htonl(sizeof(struct xdr_inaddr));
    151 	send_tail = (char *)&args->xina;
    152 
    153 	/*
    154 	 * append encapsulated data (client IP address)
    155 	 */
    156 	if (xdr_inaddr_encode(&send_tail, myip))
    157 		return -1;
    158 
    159 	/* RPC: portmap/callit */
    160 	d->myport = htons(--rpc_port);
    161 	d->destip.s_addr = INADDR_BROADCAST;	/* XXX: subnet bcast? */
    162 	/* rpc_call will set d->destport */
    163 
    164 	len = rpc_call(d, PMAPPROG, PMAPVERS, PMAPPROC_CALLIT,
    165 				  args, send_tail - (char *)args,
    166 				  repl, sizeof(*repl));
    167 	if (len < 8) {
    168 		printf("bootparamd: 'whoami' call failed\n");
    169 		return -1;
    170 	}
    171 
    172 	/* Save bootparam server address (from IP header). */
    173 	rpc_fromaddr(repl, &bp_server_addr, &bp_server_port);
    174 
    175 	/*
    176 	 * Note that bp_server_port is now 111 due to the
    177 	 * indirect call (using PMAPPROC_CALLIT), so get the
    178 	 * actual port number from the reply data.
    179 	 */
    180 	bp_server_port = repl->port;
    181 
    182 	RPC_PRINTF(("bp_whoami: server at %s:%d\n",
    183 	    inet_ntoa(bp_server_addr), ntohs(bp_server_port)));
    184 
    185 	/* We have just done a portmap call, so cache the portnum. */
    186 	rpc_pmap_putcache(bp_server_addr,
    187 			  BOOTPARAM_PROG,
    188 			  BOOTPARAM_VERS,
    189 			  (int)ntohs(bp_server_port));
    190 
    191 	/*
    192 	 * Parse the encapsulated results from bootparam/whoami
    193 	 */
    194 	x = ntohl(repl->encap_len);
    195 	if (len < x) {
    196 		printf("bp_whoami: short reply, %d < %d\n", len, x);
    197 		return -1;
    198 	}
    199 	recv_head = (char *)repl->capsule;
    200 
    201 	/* client name */
    202 	hostnamelen = MAXHOSTNAMELEN-1;
    203 	if (xdr_string_decode(&recv_head, hostname, &hostnamelen)) {
    204 		RPC_PRINTF(("bp_whoami: bad hostname\n"));
    205 		return -1;
    206 	}
    207 
    208 	/* domain name */
    209 	domainnamelen = MAXHOSTNAMELEN-1;
    210 	if (xdr_string_decode(&recv_head, domainname, &domainnamelen)) {
    211 		RPC_PRINTF(("bp_whoami: bad domainname\n"));
    212 		return -1;
    213 	}
    214 
    215 	/* gateway address */
    216 	if (xdr_inaddr_decode(&recv_head, &gateip)) {
    217 		RPC_PRINTF(("bp_whoami: bad gateway\n"));
    218 		return -1;
    219 	}
    220 
    221 	/* success */
    222 	return 0;
    223 }
    224 
    225 
    226 /*
    227  * RPC: bootparam/getfile
    228  * Given client name and file "key", get:
    229  *	server name
    230  *	server IP address
    231  *	server pathname
    232  */
    233 int
    234 bp_getfile(int sockfd, char *key, struct in_addr *serv_addr, char *pathname)
    235 {
    236 	struct {
    237 		n_long	h[RPC_HEADER_WORDS];
    238 		n_long  d[64];
    239 	} sdata;
    240 	struct {
    241 		n_long	h[RPC_HEADER_WORDS];
    242 		n_long  d[128];
    243 	} rdata;
    244 	char serv_name[FNAME_SIZE];
    245 	char *send_tail, *recv_head;
    246 	/* misc... */
    247 	struct iodesc *d;
    248 	int sn_len, path_len, rlen;
    249 
    250 	if (!(d = socktodesc(sockfd))) {
    251 		RPC_PRINTF(("bp_getfile: bad socket. %d\n", sockfd));
    252 		return -1;
    253 	}
    254 
    255 	send_tail = (char *)sdata.d;
    256 	recv_head = (char *)rdata.d;
    257 
    258 	/*
    259 	 * Build request message.
    260 	 */
    261 
    262 	/* client name (hostname) */
    263 	if (xdr_string_encode(&send_tail, hostname, hostnamelen)) {
    264 		RPC_PRINTF(("bp_getfile: bad client\n"));
    265 		return -1;
    266 	}
    267 
    268 	/* key name (root or swap) */
    269 	if (xdr_string_encode(&send_tail, key, strlen(key))) {
    270 		RPC_PRINTF(("bp_getfile: bad key\n"));
    271 		return -1;
    272 	}
    273 
    274 	/* RPC: bootparam/getfile */
    275 	d->myport = htons(--rpc_port);
    276 	d->destip = bp_server_addr;
    277 	/* rpc_call will set d->destport */
    278 
    279 	rlen = rpc_call(d,
    280 		BOOTPARAM_PROG, BOOTPARAM_VERS, BOOTPARAM_GETFILE,
    281 		sdata.d, send_tail - (char *)sdata.d,
    282 		rdata.d, sizeof(rdata.d));
    283 	if (rlen < 4) {
    284 		RPC_PRINTF(("bp_getfile: short reply\n"));
    285 		errno = EBADRPC;
    286 		return -1;
    287 	}
    288 	recv_head = (char *)rdata.d;
    289 
    290 	/*
    291 	 * Parse result message.
    292 	 */
    293 
    294 	/* server name */
    295 	sn_len = FNAME_SIZE-1;
    296 	if (xdr_string_decode(&recv_head, serv_name, &sn_len)) {
    297 		RPC_PRINTF(("bp_getfile: bad server name\n"));
    298 		return -1;
    299 	}
    300 
    301 	/* server IP address (mountd/NFS) */
    302 	if (xdr_inaddr_decode(&recv_head, serv_addr)) {
    303 		RPC_PRINTF(("bp_getfile: bad server addr\n"));
    304 		return -1;
    305 	}
    306 
    307 	/* server pathname */
    308 	path_len = MAXPATHLEN - 1;
    309 	if (xdr_string_decode(&recv_head, pathname, &path_len)) {
    310 		RPC_PRINTF(("bp_getfile: bad server path\n"));
    311 		return -1;
    312 	}
    313 
    314 	/* success */
    315 	return 0;
    316 }
    317 
    318 
    319 /*
    320  * eXternal Data Representation routines.
    321  * (but with non-standard args...)
    322  */
    323 
    324 
    325 int
    326 xdr_string_encode(char **pkt, char *str, int len)
    327 {
    328 	u_int32_t *lenp;
    329 	char *datap;
    330 	int padlen = (len + 3) & ~3;	/* padded length */
    331 
    332 	/* The data will be int aligned. */
    333 	lenp = (u_int32_t *)*pkt;
    334 	*pkt += sizeof(*lenp);
    335 	*lenp = htonl(len);
    336 
    337 	datap = *pkt;
    338 	*pkt += padlen;
    339 	(void)memcpy(datap, str, len);
    340 
    341 	return 0;
    342 }
    343 
    344 /* len_p: bufsize - 1 */
    345 int
    346 xdr_string_decode(char **pkt, char *str, int *len_p)
    347 {
    348 	u_int32_t *lenp;
    349 	char *datap;
    350 	int slen;	/* string length */
    351 	int plen;	/* padded length */
    352 
    353 	/* The data will be int aligned. */
    354 	lenp = (u_int32_t *)*pkt;
    355 	*pkt += sizeof(*lenp);
    356 	slen = ntohl(*lenp);
    357 	plen = (slen + 3) & ~3;
    358 
    359 	if (slen > *len_p)
    360 		slen = *len_p;
    361 	datap = *pkt;
    362 	*pkt += plen;
    363 	(void)memcpy(str, datap, slen);
    364 
    365 	str[slen] = '\0';
    366 	*len_p = slen;
    367 
    368 	return 0;
    369 }
    370 
    371 
    372 /* ia: network order */
    373 int
    374 xdr_inaddr_encode(char **pkt, struct in_addr ia)
    375 {
    376 	struct xdr_inaddr *xi;
    377 	u_char *cp;
    378 	int32_t *ip;
    379 	union {
    380 		n_long l;	/* network order */
    381 		u_char c[4];
    382 	} uia;
    383 
    384 	/* The data will be int aligned. */
    385 	xi = (struct xdr_inaddr *)*pkt;
    386 	*pkt += sizeof(*xi);
    387 	xi->atype = htonl(1);
    388 	uia.l = ia.s_addr;
    389 	cp = uia.c;
    390 	ip = xi->addr;
    391 	/*
    392 	 * Note: the htonl() calls below DO NOT
    393 	 * imply that uia.l is in host order.
    394 	 * In fact this needs it in net order.
    395 	 */
    396 	*ip++ = htonl((unsigned int)*cp++);
    397 	*ip++ = htonl((unsigned int)*cp++);
    398 	*ip++ = htonl((unsigned int)*cp++);
    399 	*ip++ = htonl((unsigned int)*cp++);
    400 
    401 	return 0;
    402 }
    403 
    404 /* ia: network order */
    405 int
    406 xdr_inaddr_decode(char **pkt, struct in_addr *ia)
    407 {
    408 	struct xdr_inaddr *xi;
    409 	u_char *cp;
    410 	int32_t *ip;
    411 	union {
    412 		n_long l;	/* network order */
    413 		u_char c[4];
    414 	} uia;
    415 
    416 	/* The data will be int aligned. */
    417 	xi = (struct xdr_inaddr *)*pkt;
    418 	*pkt += sizeof(*xi);
    419 	if (xi->atype != htonl(1)) {
    420 		RPC_PRINTF(("xdr_inaddr_decode: bad addrtype=%d\n",
    421 		    ntohl(xi->atype)));
    422 		return -1;
    423 	}
    424 
    425 	cp = uia.c;
    426 	ip = xi->addr;
    427 	/*
    428 	 * Note: the ntohl() calls below DO NOT
    429 	 * imply that uia.l is in host order.
    430 	 * In fact this needs it in net order.
    431 	 */
    432 	*cp++ = ntohl(*ip++);
    433 	*cp++ = ntohl(*ip++);
    434 	*cp++ = ntohl(*ip++);
    435 	*cp++ = ntohl(*ip++);
    436 	ia->s_addr = uia.l;
    437 
    438 	return 0;
    439 }
    440