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