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 (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
24 */
25
26 /*
27 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
28 * Use is subject to license terms.
29 */
30
31 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
32 /* All Rights Reserved */
33
34 #include <ctype.h>
35 #include <stdio.h>
36 #include <tiuser.h>
37 #include <netdir.h>
38 #include <netconfig.h>
39 #include <sys/utsname.h>
40 #include <sys/param.h>
41 #include <string.h>
42 #include <stdlib.h>
43
44 /*
45 * The generic name to address mappings for any transport that
46 * has strings for address (e.g., ISO Starlan).
47 *
48 * Address in ISO Starlan consist of arbitrary strings of
49 * characters. Because of this, the following routines
50 * create an "address" based on two strings, one gotten
51 * from a "host" file and one gotten from a "services" file.
52 * The two strings are catenated together (with a "." between
53 * them). The hosts file is /etc/net/starlan/hosts and
54 * contain lines of the form:
55 *
56 * arbitrary_string machname
57 *
58 * To make things simple, the "arbitrary string" should be the
59 * machine name.
60 *
61 * The services file is /etc/net/starlan/services and has lines
62 * of the form:
63 *
64 * service_name arbitrary_string
65 *
66 * Again, to make things easer, the "arbitrary name" should be the
67 * service name.
68 */
69
70 #define HOSTFILE "/etc/net/%s/hosts"
71 #define SERVICEFILE "/etc/net/%s/services"
72 #define FIELD1 1
73 #define FIELD2 2
74 #define LOCALHOST "localhost"
75
76 static int searchhost(struct netconfig *, char *, int, char *);
77 static int searchserv(struct netconfig *, char *, int, char *);
78
79 /*
80 * _netdir_getbyname() returns all of the addresses for
81 * a specified host and service.
82 */
83
84 struct nd_addrlist *
_netdir_getbyname(struct netconfig * netconfigp,struct nd_hostserv * nd_hostservp)85 _netdir_getbyname(struct netconfig *netconfigp,
86 struct nd_hostserv *nd_hostservp)
87 {
88 char fulladdr[BUFSIZ]; /* holds the full address string */
89 struct nd_addrlist *retp; /* the return structure */
90 struct netbuf *netbufp; /* indexes through the addresses */
91
92 /*
93 * HOST_BROADCAST is not supported.
94 */
95
96 if (strcmp(nd_hostservp->h_host, HOST_BROADCAST) == 0) {
97 _nderror = ND_NOHOST;
98 return (NULL);
99 }
100
101 if (searchhost(netconfigp, nd_hostservp->h_host, FIELD2,
102 fulladdr) == 0) {
103 _nderror = ND_NOHOST;
104 return (NULL);
105 }
106
107 /*
108 * Now simply fill in the address by forming strings of the
109 * form "string_from_hosts.string_from_services"
110 */
111
112 if (nd_hostservp->h_serv &&
113 (strcmp(nd_hostservp->h_serv, "rpcbind") == 0)) {
114 (void) strcat(fulladdr, ".");
115 (void) strcat(fulladdr, "rpc"); /* hard coded */
116 } else {
117 /*
118 * Get the address from the services file
119 */
120
121 if (nd_hostservp->h_serv && (nd_hostservp->h_serv[0] != '\0')) {
122 (void) strcat(fulladdr, ".");
123 if (searchserv(netconfigp, nd_hostservp->h_serv, FIELD1,
124 fulladdr + strlen(fulladdr)) == 0) {
125 _nderror = ND_NOSERV;
126 return (NULL);
127 }
128 }
129 }
130
131 if ((retp = malloc(sizeof (struct nd_addrlist))) == NULL) {
132 _nderror = ND_NOMEM;
133 return (NULL);
134 }
135
136 /*
137 * We do not worry about multiple addresses here. Loopbacks
138 * have only one interface.
139 */
140
141 retp->n_cnt = 1;
142 if ((retp->n_addrs = malloc(sizeof (struct netbuf))) == NULL) {
143 free(retp);
144 _nderror = ND_NOMEM;
145 return (NULL);
146 }
147
148 netbufp = retp->n_addrs;
149
150 /*
151 * Don't include the terminating NULL character in the
152 * length.
153 */
154
155 netbufp->len = netbufp->maxlen = (int)strlen(fulladdr);
156 if ((netbufp->buf = strdup(fulladdr)) == NULL) {
157 free(netbufp);
158 free(retp);
159 _nderror = ND_NOMEM;
160 return (NULL);
161 }
162 _nderror = ND_OK;
163 return (retp);
164 }
165
166 /*
167 * _netdir_getbyaddr() takes an address (hopefully obtained from
168 * someone doing a _netdir_getbyname()) and returns all hosts with
169 * that address.
170 */
171
172 struct nd_hostservlist *
_netdir_getbyaddr(struct netconfig * netconfigp,struct netbuf * netbufp)173 _netdir_getbyaddr(struct netconfig *netconfigp, struct netbuf *netbufp)
174 {
175 char fulladdr[BUFSIZ]; /* a copy of the address string */
176 char servbuf[BUFSIZ]; /* a buffer for service string */
177 char hostbuf[BUFSIZ]; /* points to list of host names */
178 char *hostname; /* the "first" path of the string */
179 char *servname; /* the "second" part of string */
180 struct nd_hostservlist *retp; /* the return structure */
181 char *serv; /* resultant service name obtained */
182 int nhost; /* the number of hosts in hostpp */
183 struct nd_hostserv *nd_hostservp; /* traverses the host structures */
184 char *nexttok; /* next token to process */
185
186 /*
187 * Separate the two parts of the address string.
188 */
189
190 (void) strlcpy(fulladdr, netbufp->buf, sizeof (fulladdr));
191 hostname = strtok_r(fulladdr, ".", &nexttok);
192 if (hostname == NULL) {
193 _nderror = ND_NOHOST;
194 return (NULL);
195 }
196 servname = strtok_r(NULL, " \n\t", &nexttok);
197
198 /*
199 * Search for all the hosts associated with the
200 * first part of the address string.
201 */
202
203 nhost = searchhost(netconfigp, hostname, FIELD1, hostbuf);
204 if (nhost == 0) {
205 _nderror = ND_NOHOST;
206 return (NULL);
207 }
208
209 /*
210 * Search for the service associated with the second
211 * path of the address string.
212 */
213
214 if (servname == NULL) {
215 _nderror = ND_NOSERV;
216 return (NULL);
217 }
218
219 servbuf[0] = '\0';
220 serv = servbuf;
221 if (searchserv(netconfigp, servname, FIELD2, servbuf) == 0) {
222 serv = _taddr2uaddr(netconfigp, netbufp);
223 (void) strcpy(servbuf, serv);
224 free(serv);
225 serv = servbuf;
226 while (*serv != '.')
227 serv++;
228 }
229
230 /*
231 * Allocate space to hold the return structure, set the number
232 * of hosts, and allocate space to hold them.
233 */
234
235 if ((retp = malloc(sizeof (struct nd_hostservlist))) == NULL) {
236 _nderror = ND_NOMEM;
237 return (NULL);
238 }
239
240 retp->h_cnt = nhost;
241 retp->h_hostservs = calloc(nhost, sizeof (struct nd_hostserv));
242 if (retp->h_hostservs == NULL) {
243 free(retp);
244 _nderror = ND_NOMEM;
245 return (NULL);
246 }
247
248 /*
249 * Loop through the host structues and fill them in with
250 * each host name (and service name).
251 */
252
253 nd_hostservp = retp->h_hostservs;
254 hostname = strtok_r(hostbuf, ",", &nexttok);
255 while (hostname && nhost--) {
256 if (((nd_hostservp->h_host = strdup(hostname)) == NULL) ||
257 ((nd_hostservp->h_serv = strdup(serv)) == NULL)) {
258 netdir_free(retp, ND_HOSTSERVLIST);
259 _nderror = ND_NOMEM;
260 return (NULL);
261 }
262 nd_hostservp++;
263 hostname = strtok_r(NULL, ",", &nexttok);
264 }
265
266 _nderror = ND_OK;
267 return (retp);
268 }
269
270 /*
271 * _taddr2uaddr() translates a address into a "universal" address.
272 * Since the address is a string, simply return the string as the
273 * universal address (but replace all non-printable characters with
274 * the \ddd form, where ddd is three octal digits). The '\n' character
275 * is also replace by \ddd and the '\' character is placed as two
276 * '\' characters.
277 */
278
279 /* ARGSUSED */
280 char *
_taddr2uaddr(struct netconfig * netconfigp,struct netbuf * netbufp)281 _taddr2uaddr(struct netconfig *netconfigp, struct netbuf *netbufp)
282 {
283 char *retp; /* pointer the return string */
284 char *to; /* traverses and populates the return string */
285 char *from; /* traverses the string to be converted */
286 int i; /* indexes through the given string */
287
288 /*
289 * BUFSIZ is perhaps too big for this one and there is a better
290 * way to optimize it, but for now we will just assume BUFSIZ
291 */
292 if ((retp = malloc(BUFSIZ)) == NULL) {
293 _nderror = ND_NOMEM;
294 return (NULL);
295 }
296 to = retp;
297 from = netbufp->buf;
298
299 for (i = 0; i < netbufp->len; i++) {
300 if (*from == '\\') {
301 *to++ = '\\';
302 *to++ = '\\';
303 } else {
304 if (*from == '\n' || !isprint((unsigned char)*from)) {
305 (void) sprintf(to, "\\%.3o", *from & 0xff);
306 to += 4;
307 } else {
308 *to++ = *from;
309 }
310 }
311 from++;
312 }
313 *to = '\0';
314 return (retp);
315 }
316
317 /*
318 * _uaddr2taddr() translates a universal address back into a
319 * netaddr structure. Since the universal address is a string,
320 * put that into the TLI buffer (making sure to change all \ddd
321 * characters back and strip off the trailing \0 character).
322 */
323
324 /* ARGSUSED */
325 struct netbuf *
_uaddr2taddr(struct netconfig * netconfigp,char * uaddr)326 _uaddr2taddr(struct netconfig *netconfigp, char *uaddr)
327 {
328 struct netbuf *retp; /* the return structure */
329 char *holdp; /* holds the converted address */
330 char *to; /* traverses and populates the new address */
331 char *from; /* traverses the universal address */
332
333 holdp = malloc(strlen(uaddr) + 1);
334 if (holdp == NULL) {
335 _nderror = ND_NOMEM;
336 return (NULL);
337 }
338 from = uaddr;
339 to = holdp;
340
341 while (*from) {
342 if (*from == '\\') {
343 if (*(from+1) == '\\') {
344 *to = '\\';
345 from += 2;
346 } else {
347 *to = ((*(from+1) - '0') << 6) +
348 ((*(from+2) - '0') << 3) +
349 (*(from+3) - '0');
350 from += 4;
351 }
352 } else {
353 *to = *from++;
354 }
355 to++;
356 }
357 *to = '\0';
358
359 if ((retp = malloc(sizeof (struct netbuf))) == NULL) {
360 free(holdp);
361 _nderror = ND_NOMEM;
362 return (NULL);
363 }
364 retp->maxlen = retp->len = (int)(to - holdp);
365 retp->buf = holdp;
366 return (retp);
367 }
368
369 /*
370 * _netdir_options() is a "catch-all" routine that does
371 * transport specific things. The only thing that these
372 * routines have to worry about is ND_MERGEADDR.
373 */
374
375 /* ARGSUSED */
376 int
_netdir_options(struct netconfig * netconfigp,int option,int fd,void * par)377 _netdir_options(struct netconfig *netconfigp, int option, int fd, void *par)
378 {
379 struct nd_mergearg *argp; /* the argument for mergeaddr */
380
381 switch (option) {
382 case ND_MERGEADDR:
383 /*
384 * Translate the universal address into something that
385 * makes sense to the caller. This is a no-op in
386 * loopback's case, so just return the universal address.
387 */
388 argp = (struct nd_mergearg *)par;
389 argp->m_uaddr = strdup(argp->s_uaddr);
390 if (argp->m_uaddr == NULL) {
391 _nderror = ND_NOMEM;
392 return (-1);
393 }
394 return (0);
395 default:
396 _nderror = ND_NOCTRL;
397 return (-1);
398 }
399 }
400
401 /*
402 * searchhost() looks for the specified token in the host file.
403 * The "field" parameter signifies which field to compare the token
404 * on, and returns all comma separated values associated with the token.
405 */
406
407 static int
searchhost(struct netconfig * netconfigp,char * token,int field,char * hostbuf)408 searchhost(struct netconfig *netconfigp, char *token, int field, char *hostbuf)
409 {
410 char searchfile[MAXPATHLEN]; /* the name of file to be opened */
411 char buf[BUFSIZ]; /* holds each line of the file */
412 char *fileaddr; /* the first token in each line */
413 char *filehost; /* the second token in each line */
414 char *cmpstr; /* the string to compare token to */
415 char *retstr; /* the string to return if compare succeeds */
416 char *nexttok; /* next token to process */
417 FILE *fp; /* the opened searchfile */
418 int nelements = 0; /* total number of elements found */
419 struct utsname utsname;
420
421 /*
422 * Unless /etc/netconfig has been altered, the only transport that
423 * will use straddr.so is loopback. In this case, we always
424 * return "localhost" if either our nodename, or "localhost", or
425 * some of special-case host names were passed, or we fail.
426 */
427
428 if ((strcmp(token, HOST_SELF_BIND) == 0) ||
429 (strcmp(token, HOST_SELF_CONNECT) == 0) ||
430 (strcmp(token, HOST_ANY) == 0) ||
431 (strcmp(token, LOCALHOST) == 0) ||
432 (uname(&utsname) >= 0 && strcmp(token, utsname.nodename) == 0)) {
433 (void) strcpy(hostbuf, LOCALHOST);
434 return (1);
435 }
436
437 if (strcmp(netconfigp->nc_protofmly, NC_LOOPBACK) == 0)
438 return (0);
439
440 /*
441 * We only get here if an administrator has modified
442 * /etc/netconfig to use straddr.so for a transport other than
443 * loopback (which is questionable but something we'll need to
444 * EOL at a later point in time). In this case, we fallback to
445 * searching for the associated key in the appropriate hosts
446 * file (based on nc_netid).
447 */
448
449 (void) snprintf(searchfile, sizeof (searchfile), HOSTFILE,
450 netconfigp->nc_netid);
451
452 fp = fopen(searchfile, "rF");
453 if (fp == NULL)
454 return (0);
455
456 /*
457 * Loop through the file looking for the tokens and creating
458 * the list of strings to be returned.
459 */
460
461 while (fgets(buf, BUFSIZ, fp) != NULL) {
462
463 /*
464 * Ignore comments and bad lines.
465 */
466
467 fileaddr = strtok_r(buf, " \t\n", &nexttok);
468 if (fileaddr == NULL || *fileaddr == '#')
469 continue;
470
471 if ((filehost = strtok_r(NULL, " \t\n", &nexttok)) == NULL)
472 continue;
473
474 /*
475 * determine which to compare the token to, then
476 * compare it, and if they match, add the return
477 * string to the list.
478 */
479
480 cmpstr = (field == FIELD1)? fileaddr : filehost;
481 retstr = (field == FIELD1)? filehost : fileaddr;
482
483 if (strcmp(token, cmpstr) == 0) {
484 nelements++;
485 if (field == FIELD2) {
486 /*
487 * called by _netdir_getbyname
488 */
489
490 (void) strcpy(hostbuf, retstr);
491 break;
492 }
493 if (nelements > 1) {
494 /*
495 * Assuming that "," will never be a part
496 * of any host name.
497 */
498 (void) strcat(hostbuf, ",");
499 }
500 (void) strcat(hostbuf, retstr);
501 }
502 }
503
504 (void) fclose(fp);
505 return (nelements);
506 }
507
508 /*
509 * searchserv() looks for the specified token in the service file.
510 * The "field" parameter signifies which field to compare the token
511 * on, and returns the string associated with the token in servname.
512 */
513
514 static int
searchserv(struct netconfig * netconfigp,char * token,int field,char * servname)515 searchserv(struct netconfig *netconfigp, char *token, int field, char *servname)
516 {
517 char searchfile[MAXPATHLEN]; /* the name of file to be opened */
518 char buf[BUFSIZ]; /* buffer space for lines in file */
519 char *fileservice; /* the first token in each line */
520 char *fileport; /* the second token in each line */
521 char *cmpstr; /* the string to compare the token to */
522 char *retstr; /* temporarily hold token in line of file */
523 char *nexttok; /* next token to process */
524 FILE *fp; /* the opened searchfile */
525
526 (void) snprintf(searchfile, sizeof (searchfile), SERVICEFILE,
527 netconfigp->nc_netid);
528
529 fp = fopen(searchfile, "rF");
530 if (fp == NULL)
531 return (0);
532
533 /*
534 * Loop through the services file looking for the token.
535 */
536
537 while (fgets(buf, BUFSIZ, fp) != NULL) {
538 /*
539 * If comment or bad line, continue.
540 */
541 fileservice = strtok_r(buf, " \t\n", &nexttok);
542 if (fileservice == NULL || *fileservice == '#')
543 continue;
544
545 if ((fileport = strtok_r(NULL, " \t\n", &nexttok)) == NULL)
546 continue;
547
548 cmpstr = (field == FIELD1)? fileservice : fileport;
549 retstr = (field == FIELD1)? fileport : fileservice;
550
551 if (strcmp(token, cmpstr) == 0) {
552 (void) strcpy(servname, retstr);
553 (void) fclose(fp);
554 return (1);
555 }
556 }
557
558 (void) fclose(fp);
559 return (0);
560 }
561