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