1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 1999-2003 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 * Copyright (c) 2016 by Delphix. All rights reserved. 26 */ 27 28 #pragma ident "%Z%%M% %I% %E% SMI" 29 30 /* 31 * Subroutines that implement the bootparam services. 32 */ 33 34 #include <rpcsvc/bootparam_prot.h> 35 #include <netdb.h> 36 #include <nlist.h> 37 #include <stdio.h> 38 #include <ctype.h> 39 #include <stdlib.h> 40 #include <string.h> 41 #include <nsswitch.h> 42 #include <sys/types.h> 43 #include <sys/file.h> 44 #include <sys/socket.h> 45 #define KERNEL /* to get RTHASHSIZ */ 46 #include <sys/stream.h> 47 #include <net/route.h> 48 #undef KERNEL 49 #include <net/if.h> /* for structs ifnet and ifaddr */ 50 #include <netinet/in.h> 51 #include <netinet/in_var.h> /* for struct in_ifaddr */ 52 #include <arpa/inet.h> 53 54 #include "bootparam_private.h" 55 56 #define LINESIZE 1024 57 58 extern int getdomainname(char *, int); 59 extern int bootparams_getbyname(char *, char *, int); 60 61 static char *wildcard = "*"; 62 static char domainkey[] = "domain="; 63 static void getf_printres(bp_getfile_res *); 64 static void copydomain(char *, char *, int); 65 66 /* 67 * Whoami turns a client address into a client name 68 * and suggested route machine. 69 */ 70 /*ARGSUSED1*/ 71 bp_whoami_res * 72 bootparamproc_whoami_1(bp_whoami_arg *argp, CLIENT *cl) 73 { 74 static bp_whoami_res res; 75 struct in_addr clnt_addr; 76 struct in_addr route_addr; 77 struct hostent *hp; 78 static char clnt_entry[LINESIZE]; 79 static char domain[MAX_MACHINE_NAME]; 80 char *cp; 81 82 if (argp->client_address.address_type != IP_ADDR_TYPE) { 83 if (debug) { 84 msgout("Whoami failed: unknown address type %d", 85 argp->client_address.address_type); 86 } 87 return (NULL); 88 } 89 (void) memcpy(&clnt_addr, &argp->client_address.bp_address_u.ip_addr, 90 sizeof (clnt_addr)); 91 hp = gethostbyaddr((char *)&clnt_addr, sizeof (clnt_addr), AF_INET); 92 if (hp == NULL) { 93 if (debug) { 94 msgout("Whoami failed: gethostbyaddr for %s.", 95 inet_ntoa(clnt_addr)); 96 } 97 return (NULL); 98 } 99 100 /* 101 * We only answer requests from clients listed in the database. 102 */ 103 if ((bootparams_getbyname(hp->h_name, clnt_entry, 104 sizeof (clnt_entry)) != __NSW_SUCCESS) && 105 (bootparams_getbyname(wildcard, clnt_entry, 106 sizeof (clnt_entry)) != __NSW_SUCCESS)) 107 return (NULL); 108 109 res.client_name = hp->h_name; 110 111 /* 112 * The algorithm for determining the client's domain name is: 113 * 1) look for "domain=" in the client's bootparams line. 114 * If found, use its value. 115 * 2) look for a "domain=" entry in the wildcard bootparams 116 * line (if any). If found, use its value. Otherwise, 117 * 3) return the domain name of the server answering the 118 * request. 119 */ 120 if (cp = strstr(clnt_entry, domainkey)) { 121 copydomain(cp + sizeof (domainkey) - 1, domain, 122 sizeof (domain)); 123 } else { 124 /* "domain=" not found - try for wildcard */ 125 if ((bootparams_getbyname(wildcard, clnt_entry, 126 sizeof (clnt_entry)) == __NSW_SUCCESS) && 127 (cp = strstr(clnt_entry, domainkey))) { 128 copydomain(cp + sizeof (domainkey) - 1, domain, 129 sizeof (domain)); 130 } else { 131 (void) getdomainname(domain, sizeof (domain)); 132 } 133 } 134 res.domain_name = domain; 135 136 res.router_address.address_type = IP_ADDR_TYPE; 137 route_addr.s_addr = get_ip_route(clnt_addr); 138 (void) memcpy(&res.router_address.bp_address_u.ip_addr, 139 &route_addr, 140 sizeof (res.router_address.bp_address_u.ip_addr)); 141 142 if (debug) { 143 struct in_addr in; 144 145 (void) memcpy(&in.s_addr, 146 &res.router_address.bp_address_u.ip_addr, 147 sizeof (in.s_addr)); 148 msgout("Whoami returning name = %s, router address = %s", 149 res.client_name, 150 inet_ntoa(in)); 151 } 152 return (&res); 153 } 154 155 /* 156 * Getfile gets the client name and the key and returns its server 157 * and the pathname for that key. 158 */ 159 /*ARGSUSED1*/ 160 bp_getfile_res * 161 bootparamproc_getfile_1(bp_getfile_arg *argp, CLIENT *cl) 162 { 163 static bp_getfile_res res; 164 static char clnt_entry[LINESIZE]; 165 struct hostent *hp; 166 char *cp; 167 char filekey[LINESIZE]; 168 char *server_hostname; 169 char *path_on_server; 170 int do_wildcard = 0; 171 static char *zero_len_string = ""; 172 173 /* 174 * The bootparams_getbyname() library function looks up a 175 * "client entry" using using the client's hostname as the 176 * key. A client entry consists of a string of "file entries" 177 * separated by white space. Each file entry is of the form: 178 * 179 * file_key=server_hostname:path_on_server 180 * 181 * In the getfile RPC call, the client gives us its hostname 182 * and a file_key. We lookup its client entry, then locate a 183 * file entry matching that file_key. We then parse out the 184 * server_hostname and path_on_server from the file entry, map 185 * the server_hostname to an IP address, and return both the 186 * IP address and path_on_server back to the client. 187 */ 188 189 /* make the client's file key int a string we can use for matching */ 190 (void) strncpy(filekey, argp->file_id, sizeof (filekey) - 2); 191 filekey[sizeof (filekey) - 2] = '\0'; 192 (void) strcat(filekey, "="); 193 194 if (bootparams_getbyname(argp->client_name, clnt_entry, 195 sizeof (clnt_entry)) == __NSW_SUCCESS) { 196 /* locate the file_key in the client's entry */ 197 cp = strstr(clnt_entry, filekey); 198 if (cp == NULL) 199 do_wildcard++; 200 201 } else 202 do_wildcard++; 203 204 if (do_wildcard) { 205 if (bootparams_getbyname(wildcard, clnt_entry, 206 sizeof (clnt_entry)) != __NSW_SUCCESS) 207 return (NULL); 208 209 /* locate the file_key in the client's entry */ 210 cp = strstr(clnt_entry, filekey); 211 if (cp == NULL) 212 return (NULL); 213 } 214 215 /* locate the "data" part of file entry (r.h.s. of "=") */ 216 cp = strchr(cp, '='); 217 if (cp == NULL) 218 return (NULL); 219 cp++; 220 if (*cp == '\0') 221 return (NULL); 222 server_hostname = cp; 223 224 /* null-terminate server_hostname and parse path_on_server */ 225 cp = strchr(server_hostname, ':'); 226 if (cp == NULL) 227 return (NULL); 228 *cp = '\0'; 229 cp++; 230 /* strtok() will null-terminate path_on_server */ 231 path_on_server = strtok(cp, "\t\n "); 232 if (path_on_server == NULL) 233 path_on_server = zero_len_string; 234 235 res.server_name = server_hostname; 236 res.server_path = path_on_server; 237 if (*res.server_name == 0) { 238 res.server_address.address_type = IP_ADDR_TYPE; 239 (void) memset(&res.server_address.bp_address_u.ip_addr, 0, 240 sizeof (res.server_address.bp_address_u.ip_addr)); 241 } else { 242 in_addr_t addr; 243 244 if ((hp = gethostbyname(server_hostname)) != NULL) { 245 addr = find_best_server_int(hp->h_addr_list, 246 argp->client_name); 247 } else { 248 addr = inet_addr(server_hostname); 249 if (addr == INADDR_BROADCAST) { 250 if (debug) { 251 msgout("getfile_1: gethostbyname(%s) " 252 "failed", res.server_name); 253 } 254 return (NULL); 255 } 256 } 257 res.server_address.address_type = IP_ADDR_TYPE; 258 (void) memcpy(&res.server_address.bp_address_u.ip_addr, 259 &addr, sizeof (res.server_address.bp_address_u.ip_addr)); 260 } 261 if (debug) { 262 getf_printres(&res); 263 } 264 return (&res); 265 } 266 267 void 268 getf_printres(bp_getfile_res *res) 269 { 270 struct in_addr in; 271 272 (void) memcpy(&in.s_addr, &res->server_address.bp_address_u.ip_addr, 273 sizeof (in.s_addr)); 274 msgout("getfile_1: file is \"%s\" %s \"%s\"", 275 res->server_name, 276 inet_ntoa(in), 277 res->server_path); 278 } 279 280 /* 281 * Used when we've found a "domain=" key, this function copies characters 282 * from source to target until we come upon either a NULL or whitespace is 283 * found in the source string, or we run out of room in the target. 284 * 285 */ 286 void 287 copydomain(char *source, char *target, int len) 288 { 289 int n; /* number of characters copies */; 290 291 len--; /* leave room for terminating '\0' */ 292 if (source) 293 for (n = 0; *source != '\0' && n < len; n++) 294 if (isspace((int)*source)) 295 break; 296 else 297 *target++ = *source++; 298 299 *target = '\0'; 300 } 301