/* $NetBSD: bootparam.c,v 1.19 2009/10/21 23:12:10 snj 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 #include #include #include #include #ifdef _STANDALONE #include #else #include #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 */ int hostnamelen; char domainname[FNAME_SIZE]; /* our DNS domain */ int 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 { u_int32_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 *, int); int xdr_string_decode(char **, char *, int *); /* * 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 { u_int32_t prog; u_int32_t vers; u_int32_t proc; u_int32_t arglen; struct xdr_inaddr xina; } *args; struct repl { u_int16_t _pad; u_int16_t port; u_int32_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; int len, x; RPC_PRINTF(("bp_whoami: myip=%s\n", inet_ntoa(myip))); if (!(d = socktodesc(sockfd))) { RPC_PRINTF(("bp_whoami: bad socket. %d\n", sockfd)); return -1; } args = &sdata.d; repl = &rdata.d; /* * Build request args for PMAPPROC_CALLIT. */ args->prog = htonl(BOOTPARAM_PROG); args->vers = htonl(BOOTPARAM_VERS); args->proc = htonl(BOOTPARAM_WHOAMI); args->arglen = htonl(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 */ d->myport = htons(--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, send_tail - (char *)args, repl, sizeof(*repl)); if (len < 8) { printf("bootparamd: 'whoami' call failed\n"); 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(("bp_whoami: server at %s:%d\n", inet_ntoa(bp_server_addr), ntohs(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(bp_server_port)); /* * Parse the encapsulated results from bootparam/whoami */ x = ntohl(repl->encap_len); if (len < x) { printf("bp_whoami: short reply, %d < %d\n", len, x); return -1; } recv_head = (char *)repl->capsule; /* client name */ hostnamelen = MAXHOSTNAMELEN-1; if (xdr_string_decode(&recv_head, hostname, &hostnamelen)) { RPC_PRINTF(("bp_whoami: bad hostname\n")); return -1; } /* domain name */ domainnamelen = MAXHOSTNAMELEN-1; if (xdr_string_decode(&recv_head, domainname, &domainnamelen)) { RPC_PRINTF(("bp_whoami: bad domainname\n")); return -1; } /* gateway address */ if (xdr_inaddr_decode(&recv_head, &gateip)) { RPC_PRINTF(("bp_whoami: bad gateway\n")); 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; int sn_len, path_len, rlen; if (!(d = socktodesc(sockfd))) { RPC_PRINTF(("bp_getfile: bad socket. %d\n", 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(("bp_getfile: bad client\n")); return -1; } /* key name (root or swap) */ if (xdr_string_encode(&send_tail, key, strlen(key))) { RPC_PRINTF(("bp_getfile: bad key\n")); return -1; } /* RPC: bootparam/getfile */ d->myport = htons(--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, send_tail - (char *)sdata.d, rdata.d, sizeof(rdata.d)); if (rlen < 4) { RPC_PRINTF(("bp_getfile: short reply\n")); 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(("bp_getfile: bad server name\n")); return -1; } /* server IP address (mountd/NFS) */ if (xdr_inaddr_decode(&recv_head, serv_addr)) { RPC_PRINTF(("bp_getfile: bad server addr\n")); return -1; } /* server pathname */ path_len = MAXPATHLEN - 1; if (xdr_string_decode(&recv_head, pathname, &path_len)) { RPC_PRINTF(("bp_getfile: bad server path\n")); return -1; } /* success */ return 0; } /* * eXternal Data Representation routines. * (but with non-standard args...) */ int xdr_string_encode(char **pkt, char *str, int len) { u_int32_t *lenp; char *datap; int padlen = (len + 3) & ~3; /* padded length */ /* The data will be int aligned. */ lenp = (u_int32_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, int *len_p) { u_int32_t *lenp; char *datap; int slen; /* string length */ int plen; /* padded length */ /* The data will be int aligned. */ lenp = (u_int32_t *)*pkt; *pkt += sizeof(*lenp); slen = ntohl(*lenp); plen = (slen + 3) & ~3; 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; int32_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 = 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((unsigned int)*cp++); *ip++ = htonl((unsigned int)*cp++); *ip++ = htonl((unsigned int)*cp++); *ip++ = htonl((unsigned int)*cp++); return 0; } /* ia: network order */ int xdr_inaddr_decode(char **pkt, struct in_addr *ia) { struct xdr_inaddr *xi; u_char *cp; int32_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(1)) { RPC_PRINTF(("xdr_inaddr_decode: bad addrtype=%d\n", ntohl(xi->atype))); return -1; } cp = uia.c; ip = 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++ = ntohl(*ip++); *cp++ = ntohl(*ip++); *cp++ = ntohl(*ip++); *cp++ = ntohl(*ip++); ia->s_addr = uia.l; return 0; }