Home | History | Annotate | Download | only in gen
      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 /*
     24  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
     25  * Use is subject to license terms.
     26  */
     27 
     28 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     29 
     30 #include "mt.h"
     31 #include <stdlib.h>
     32 #include <string.h>
     33 #include <rpcsvc/nis.h>
     34 #include <ctype.h>
     35 #include <malloc.h>
     36 #include "nis_local.h"
     37 
     38 #ifdef ORGDIR
     39 static void append_orgdir(nis_name nname);
     40 #endif
     41 static void check_dup_dnames(nis_name *namelist, int count);
     42 
     43 /* Counts and returns the number of dots in the given string.  */
     44 static int
     45 dots_in_name(char *name)
     46 {
     47 	int i, ndots;
     48 	size_t len = strlen(name);
     49 
     50 #ifdef QUOTES
     51 	int in_quotes = 0;
     52 /* Commented out the quoted quotes stuff for 1202807 */
     53 #endif
     54 
     55 	for (ndots = i = 0; i < len; i++) {
     56 
     57 #ifdef QUOTES
     58 /* Commented out the quoted quotes stuff for 1202807 */
     59 
     60 		/* Entering a quotation? */
     61 		if (name[i] == '"') {
     62 
     63 			if (name[i+1] == '"') {
     64 				/* No.  This is "", i.e. a quoted quote */
     65 
     66 				i++;	/* skip the next quote, else will */
     67 				continue;  /* think it's only one quote */
     68 
     69 			} else {
     70 
     71 				/* Entering quotes.  Ignore all dots in here. */
     72 
     73 				in_quotes = (in_quotes ? 0 : 1);
     74 			}
     75 		}
     76 
     77 		/* If not in quotes, then increment counter */
     78 		if ((!in_quotes) && (name[i] == '.'))
     79 #endif
     80 
     81 		/* Increment counter */
     82 		if (name[i] == '.')
     83 			++ndots;
     84 	}
     85 	return (ndots);
     86 }
     87 
     88 
     89 /*
     90  * parse_default()
     91  *
     92  * This function takes the default domain and creates a possible set of
     93  * candidate names from it. (similar to the DNS server)
     94  */
     95 static int
     96 parse_default(
     97 	nis_name	name,		/* name given by user	*/
     98 	nis_name	local,		/* nis_local_dir()	*/
     99 	nis_name	*result,	/* array of pointers 	*/
    100 	int		rmax)		/* max array value	*/
    101 {
    102 	char	buf[NIS_MAXSRCHLEN];	/* working name buffer	*/
    103 	char	tmpbuf[NIS_MAXSRCHLEN];	/* temp name buffer	*/
    104 	nis_name top;			/* ptr to free memory	*/
    105 	nis_name dom, tmpdom;	/* temp domain names	*/
    106 	nis_name tmpnam;	/* temp domain names	*/
    107 	nis_name mkr = NULL;		/* name in domain flag	*/
    108 	int comps = 0;		/* # of new components	*/
    109 
    110 	/* There is a trailing dot, we're done */
    111 	if (name[strlen(name)-1] == '.') {
    112 		char *temp = strdup(name);	/* very temporary */
    113 		if (!temp)
    114 			return (0);	/* out-of-memory or something */
    115 		result[comps++] = temp;
    116 		return (comps);	/* return how many we made */
    117 	}
    118 
    119 	top = dom = strdup(local);	/* local is never NULL */
    120 	if (!dom)
    121 		return (0);		/* out-of-memory or something */
    122 
    123 	result[0] = NULL; /* clear result list	*/
    124 	buf[0] = '\0';	/* clear name buffer	*/
    125 	tmpnam = name;	/* set tmpnam = name	*/
    126 	tmpdom = dom;	/* set tmpdom = dom	*/
    127 
    128 	/*
    129 	 * The main reason for the following if statement is to make an
    130 	 * intelligent "guess" as to what the user/application intended
    131 	 * as the fully-qualified (FQ) object pathname, given a partially-
    132 	 * qualified (PQ) one.  If there is a successful search found, a
    133 	 * single entry, result[0], is initialized with that object name
    134 	 * before proceeding with the normal method of creating names by
    135 	 * continuously stripping off parts of the domain name until we've
    136 	 * exhausted all possibilities.  If there is no resulting "guess,"
    137 	 * the program proceeds as normal.
    138 	 *
    139 	 * If 'name' is not a substring of domain, then keep stripping off
    140 	 * the labels to check for substrings of the domain.  If we've
    141 	 * reached the end with no matches, that's okay, we just proceed
    142 	 * as normal down below, otherwise, if there is a match, then make
    143 	 * the assign to the first result what we think the user intended
    144 	 * as the valid object.  Proceed as normal below.
    145 	 *
    146 	 * Examples:	PQ OBJ (dynam)	DOMAIN NAME (static)	ACTION
    147 	 *		--------------	--------------------	------
    148 	 *	1)	org_dir.foo	foo.bar.sun.com.	(continue)
    149 	 *		foo			"		(done)
    150 	 *		    (name is a substring of domainname, so
    151 	 *		    guess is org_dir.foo.bar.sun.com.)
    152 	 *
    153 	 *	2)	x.foo.eng	foo.bar.sun.com.	(continue)
    154 	 *		foo.eng		"		(done)
    155 	 *		    (guess is x.foo.bar.sun.com.)
    156 	 *
    157 	 *	3)	hosts		foo.bar.sun.com.	(continue)
    158 	 *					"		(done)
    159 	 *		    (reached end of name, no guess)
    160 	 */
    161 	if (!(mkr = strstr(dom, name))) {
    162 
    163 		/*
    164 		 * Don't go past end of 'name'!  The rest of this
    165 		 * routine incrementally takes labels from 'name'
    166 		 * and stores them in a buffer until what remains
    167 		 * of name is a substring of the domain name.
    168 		 *
    169 		 * 'tmpnam' is the temporary name pointer, and the
    170 		 * condition in the while statement checks whether
    171 		 * the address has passed the end of 'name'.
    172 		 */
    173 		while (tmpnam < (name + strlen(name))) {
    174 
    175 			/* save first label */
    176 			(void) nis_leaf_of_r(tmpnam, tmpbuf, NIS_MAXSRCHLEN);
    177 			if (strlcat(buf, tmpbuf, sizeof (buf)) >=
    178 					sizeof (buf)) {
    179 				free(top);
    180 				return (0);
    181 			}
    182 			if (strlcat(buf, ".", sizeof (buf)) >= sizeof (buf)) {
    183 				free(top);
    184 				return (0);
    185 			}
    186 
    187 			/* strip first label from rest of name */
    188 			if (!(tmpnam = (nis_name) __nis_nextsep_of(tmpnam)))
    189 				break;
    190 
    191 			/* if there's a '.' skip past it for next label */
    192 			if (*tmpnam == '.')
    193 				tmpnam++;
    194 
    195 			/* reached end without a dot, i.e. "hosts" */
    196 			if (*tmpnam == '\0')
    197 				break;
    198 
    199 			/*
    200 			 * If remainder is a substring of domain, break out.
    201 			 * Move the domain pointer up to just past where the
    202 			 * match from 'name' ends, and copy all of what
    203 			 * matches into 'name'.
    204 			 *
    205 			 * Example: (see example 1 in comments above)
    206 			 *
    207 			 *	buf    =   org_dir
    208 			 *	tmpnam =   foo ('org_dir' stripped off to buf)
    209 			 *	tmpdom =   foo.bar.sun.com.
    210 			 *			^
    211 			 *			mkr
    212 			 *
    213 			 *	We check to make sure that 'mkr' points to a
    214 			 *	'.', otherwise, the match isn't correct, i.e.
    215 			 *	"foo" is not a label of "foo500" though
    216 			 *	it's a substring, hence not-a-match here.
    217 			 */
    218 			if ((mkr = strstr(tmpdom, tmpnam)) != 0 &&
    219 			    *(mkr+strlen(tmpnam)) == '.') {
    220 				char *temp;	/* very temporary */
    221 
    222 				tmpdom = mkr + strlen(tmpnam);
    223 
    224 				/* add label:  buf = buf + tmpnam */
    225 				if (strlcat(buf, tmpnam, sizeof (buf)) >=
    226 						sizeof (buf)) {
    227 					free(top);
    228 					return (0);
    229 				}
    230 
    231 				/* replace name:  name = buf */
    232 				(void) strcpy(name, buf);
    233 
    234 				/* add domain:  buf = name + tmpdom */
    235 				if (snprintf(buf, sizeof (buf), "%s%s", name,
    236 						tmpdom) >= sizeof (buf)) {
    237 					free(top);
    238 					return (0);
    239 				}
    240 
    241 				/* write guess:  result[0] = buf */
    242 				temp = strdup(buf);
    243 				if (!temp) {
    244 					free(top);
    245 					return (0);	/* out-of-memory */
    246 				}
    247 				result[comps++] = temp;
    248 				break;
    249 			}
    250 		}
    251 	} else {
    252 
    253 		/*
    254 		 * If 'name' is a proper substring of the current domain name,
    255 		 * chop off the entire name from the domain name up to the point
    256 		 * where they do not match.  Store the item as the first result.
    257 		 *
    258 		 * Ex:	PQ OBJ (static)	DOMAIN NAME (dynamic)	ACTION
    259 		 *	---------------	---------------------	------
    260 		 *	foo.bar.sun	foo.bar.sun.com.	(continue)
    261 		 *			com.			(done)
    262 		 *	    (guess is foo.bar.sun.com.)
    263 		 */
    264 		if (*(tmpnam = mkr + strlen(name)) == '.')
    265 			tmpdom = tmpnam;
    266 
    267 		/*
    268 		 * Assign this one only if we've actually moved up the tmpdom
    269 		 * past the match *and* that the tail part of name was really
    270 		 * a label, i.e. "eng", instead of a substring, "en".  If it
    271 		 * was really a label, then the next character would be a '.'.
    272 		 */
    273 		if (*tmpdom == '.') {
    274 		    char *temp;		/* very temporary */
    275 		    if (snprintf(buf, sizeof (buf), "%s%s", name, tmpdom) >=
    276 				sizeof (buf)) {
    277 			free(top);
    278 			return (0);
    279 		    }
    280 		    temp = strdup(buf);
    281 		    if (!temp) {
    282 			free(top);
    283 			return (0);	/* out-of-memory or something */
    284 		    }
    285 		    result[comps++] = temp;
    286 		}
    287 	}
    288 
    289 	/* build the list */
    290 	for (; *dom && (comps < rmax); comps++) {
    291 
    292 		/* bail if domain name has less than 2 labels */
    293 		if (dots_in_name(dom) < 2)
    294 			break;
    295 
    296 		/* put (name + '.' + domain name) together */
    297 		if (snprintf(buf, sizeof (buf), "%s.%s", name, dom) >=
    298 				sizeof (buf)) {
    299 			free(top);
    300 			return (0);
    301 		}
    302 
    303 		/* bail if total name has less than 3 labels */
    304 		if (dots_in_name(buf) < 3)
    305 			break;
    306 
    307 		/* assign slot if it doesn't match the first elmt */
    308 		if ((result[0]) && (strcmp(result[0], buf) == 0)) {
    309 			comps--;
    310 		} else {
    311 			char *temp = strdup(buf);	/* very temporary */
    312 			if (!temp) {
    313 				free(top);
    314 				return (0);	/* out-of-memory or something */
    315 			}
    316 			result[comps] = temp;
    317 		}
    318 
    319 		/* out of memory or something */
    320 		if (!result[comps])
    321 			break;
    322 
    323 		/* truncate prefix domain, return rest with '.' in front */
    324 		dom = (nis_name) __nis_nextsep_of(dom);
    325 
    326 		/* skip past the "." in the new domain name */
    327 		if (*dom == '.')
    328 			dom++;
    329 	}
    330 	free(top);	/* free locally-allocated domain string */
    331 	return (comps);	/* return how many we made */
    332 }
    333 
    334 
    335 /*
    336  * __nis_parse_path()
    337  *
    338  * This function consumes "path" and parses it into a list of
    339  * names. Pointers to those names are stored in the array of nis_names
    340  * passed as 'list'. 'max' is the length of the array 'list'.
    341  *
    342  * It malloc's no memory, it only uses the array passed and the string
    343  * in path.
    344  */
    345 int
    346 __nis_parse_path(char *path, nis_name *list, int max)
    347 {
    348 	char	*s;
    349 	int		cur;
    350 
    351 	/* parse a colon separated list into individual table names */
    352 	for (s = path, cur = 0; (*s != '\0') && (cur < max); cur++) {
    353 		list[cur] = s;
    354 		/* walk through s until EOS or ':' */
    355 		while ((*s != ':') && (*s != '\0')) {
    356 			s++;
    357 #ifdef QUOTES
    358 /* Commented out the quoted quotes stuff for 1202807 */
    359 			if (*s == '"') {
    360 				if (*(s+1) == '"') { /* escaped quote */
    361 					s += 2;
    362 				} else {
    363 					/* skip quoted string */
    364 					s++;
    365 					while (1) {
    366 						if (*s == '\0')
    367 							break;
    368 						/* embedded quote quote */
    369 						if ((*s == '"') &&
    370 						    (*(s+1) == '"')) {
    371 							s = s+2;
    372 							continue;
    373 						}
    374 						if (*s == '"')
    375 							break;
    376 						s++;
    377 					}
    378 					if (*s == '"')
    379 						s++;
    380 				}
    381 			} else
    382 				s++;
    383 #endif
    384 		}
    385 		if (*s == ':') {
    386 			*s = '\0';
    387 			s++;
    388 		}
    389 	}
    390 	return (cur);
    391 }
    392 
    393 
    394 /*
    395  * parse_path()
    396  *
    397  * This function returns the number of names it parsed out
    398  * of the string.
    399  */
    400 static int
    401 parse_path(const nis_name name, const char *path, const nis_name local,
    402     nis_name *result, int rmax)
    403 {
    404 	int		i, comps, cur;
    405 	size_t		len, len1;
    406 	nis_name	list[NIS_MAXPATHDEPTH];
    407 	char		buf[NIS_MAXSRCHLEN], pbuf[NIS_MAXPATHLEN];
    408 
    409 	/* parse a colon separated list into individual path names */
    410 	(void) strncpy(pbuf, path, NIS_MAXPATHLEN); /* local copy of path */
    411 	comps = __nis_parse_path(pbuf, list, NIS_MAXPATHDEPTH);
    412 
    413 	/* expand "special" names in the path based on $ and + */
    414 	for (i = 0, cur = 0; (i < comps) && (cur < rmax); i++) {
    415 
    416 		/* if path element is just '$' by itself... */
    417 		if ((*(list[i]) == '$') && (*(list[i]+1) == '\0')) {
    418 			cur += parse_default(name, local, &result[cur],
    419 				rmax - cur);
    420 			if (cur > 0 && (!result[cur-1]))
    421 				break; /* finish early */
    422 
    423 		/* otherwise it's something like org_dir.$ or groups_dir.$ */
    424 		} else {
    425 			len = strlen((char *)(list[i]));
    426 			/* is last character a $? */
    427 			if (*(list[i] + (len - 1)) == '$') {
    428 				*(list[i] + (len - 1)) = '\0';
    429 				len1 = snprintf(buf, sizeof (buf), "%s.%s%s",
    430 				    name, list[i], local);
    431 			} else
    432 				len1 = snprintf(buf, sizeof (buf), "%s.%s",
    433 				    name, list[i]);
    434 
    435 			/* force ending dot */
    436 			if (buf[len1 - 1] != '.') {
    437 			    if (len1 < sizeof (buf) - 1)
    438 				(void) strcat(buf, ".");
    439 			    else
    440 				break;	/* finish early */
    441 			} else if (len1 >= sizeof (buf))
    442 				break;	/* finish early */
    443 
    444 			result[cur++] = (nis_name) strdup(buf);
    445 			if (!result[cur-1])
    446 				break; /* finish early */
    447 		}
    448 	}
    449 	return (cur);
    450 }
    451 
    452 
    453 #ifdef ORGDIR
    454 /*
    455  *	This function will append "org_dir" to the standard tables
    456  *	if already not present ie standard table name is not preceded
    457  *	by ANY directory name.
    458  */
    459 static void
    460 append_orgdir(nis_name nname)
    461 {
    462 char	*p1 = nname;
    463 int	append = 0;
    464 
    465 	if (strchr(nname, '.'))
    466 		return;
    467 
    468 	switch (*p1) {
    469 
    470 	/* auto_* tables */
    471 	case 'a':
    472 		p1++;
    473 
    474 		if (strcmp(p1, "uto_") == 0)
    475 				append = 1;
    476 		break;
    477 
    478 	/* bootparams */
    479 	case 'b':
    480 		p1++;
    481 		if (strcmp(p1, "ootparams") == 0)
    482 			append = 1;
    483 		break;
    484 
    485 	/* cred */
    486 	case 'c':
    487 		p1++;
    488 		if (strcmp(p1, "red") == 0)
    489 			append = 1;
    490 		break;
    491 
    492 	/* ethers */
    493 	case 'e':
    494 		p1++;
    495 		if (strcmp(p1, "thers") == 0)
    496 			append = 1;
    497 		break;
    498 
    499 	/* group */
    500 	case 'g':
    501 		p1++;
    502 		if (strcmp(p1, "roup") == 0)
    503 			append = 1;
    504 		break;
    505 
    506 	/* hosts */
    507 	case 'h':
    508 		p1++;
    509 		if (strcmp(p1, "osts") == 0)
    510 			append = 1;
    511 		break;
    512 
    513 	case 'm':
    514 		p1++;
    515 		if (strcmp(p1, "ail_aliases") == 0)
    516 			append = 1;
    517 		break;
    518 
    519 	/* netgroup, netmasks, networks */
    520 	case 'n':
    521 		p1++;
    522 		if (strcmp(p1, "et") == 0) {
    523 			p1 = p1 + 2;
    524 			switch (*p1) {
    525 			case 'g':
    526 				p1++;
    527 				if (strcmp(p1, "roup") == 0)
    528 					append = 1;
    529 				break;
    530 
    531 			case 'm':
    532 				p1++;
    533 				if (strcmp(p1, "asks") == 0)
    534 					append = 1;
    535 				break;
    536 
    537 			case 'w':
    538 				p1++;
    539 				if (strcmp(p1, "orks") == 0)
    540 					append = 1;
    541 				break;
    542 			}
    543 		}
    544 		break;
    545 
    546 	/* passwd, protocols */
    547 	case 'p':
    548 		p1++;
    549 		switch (*p1) {
    550 		case 'a':
    551 			p1++;
    552 			if (strcmp(p1, "sswd") == 0)
    553 				append = 1;
    554 			break;
    555 
    556 		case 'r':
    557 			p1++;
    558 			if (strcmp(p1, "otocols") == 0)
    559 				append = 1;
    560 			break;
    561 		}
    562 		break;
    563 
    564 	/* rpc */
    565 	case 'r':
    566 		p1++;
    567 		if (strcmp(p1, "pc") == 0)
    568 			append = 1;
    569 		break;
    570 
    571 	/* services */
    572 	case 's':
    573 		p1++;
    574 		if (strcmp(p1, "ervices") == 0)
    575 			append = 1;
    576 		break;
    577 
    578 	/* timezone */
    579 	case 't':
    580 		p1++;
    581 		if (strcmp(p1, "imezone") == 0)
    582 			append = 1;
    583 		break;
    584 
    585 	}
    586 
    587 	if (append)
    588 		(void) strlcat(nname, ".org_dir", NIS_MAXSRCHLEN);
    589 }
    590 #endif
    591 
    592 
    593 /*
    594  *	This function is called only when the NIS_PATH is set.
    595  *	It checks for org_dir.org_dir in the pathname. If "org_dir.org_dir"
    596  *	is present it either removes that entry from the name list(
    597  *	because if count > 1 there will be a  duplicate entry)
    598  *	otherwise substitute org_dir in place of org_dir.org_dir.
    599  *	In case the count is 1 we cannot remove the entry, rather
    600  *	use substitution. This would happen if NIS_PATH = org_dir.$.
    601  */
    602 static void
    603 check_dup_dnames(nis_name *namelist, int count)
    604 {
    605 	char	*p1, *p2;
    606 	int	i;
    607 
    608 	for (i = 0; i < count; i++) {
    609 		if (p1 = strstr(namelist[i], "org_dir.org_dir")) {
    610 			for (; i < count; i++) {
    611 				if (count > 1) {
    612 					namelist[i] = namelist[i+1];
    613 				} else {
    614 					p2 = strchr(p1, '.');
    615 					p2++;
    616 					while (*p1++ = *p2++);
    617 					*p2 = 0;
    618 				}
    619 			}
    620 		}
    621 	}
    622 }
    623 
    624 /*
    625  * __nis_getnames(nis_name name, nis_error *nis_err)
    626  *
    627  * This function extends the functionality as nis_getnames() to
    628  * return the appropriate error in the case of failure. nis_err
    629  * is undefined when a non-NULL value is returned.
    630  */
    631 nis_name *
    632 __nis_getnames(nis_name name, nis_error *nis_err)
    633 {
    634 	int			i = 0;
    635 	nis_name		*result;
    636 	char			*local = NULL,   /* The local directory */
    637 				*path = NULL,	 /* The search path */
    638 				*tmp_path = NULL; /* The search path */
    639 	char			buf[NIS_MAXSRCHLEN];
    640 
    641 	if (!name) {
    642 		*nis_err = NIS_BADNAME;
    643 		return (NULL);
    644 	}
    645 
    646 	if (name[strlen(name)-1] != '.') {
    647 		result = malloc(NIS_MAXPATHDEPTH * sizeof (nis_name));
    648 		if (result == NULL) {
    649 			*nis_err = NIS_NOMEMORY;
    650 			return (NULL);
    651 		}
    652 
    653 		tmp_path = path = (char *)getenv("NIS_PATH");
    654 
    655 		if (strlcpy(buf, name, sizeof (buf)) >= sizeof (buf)) {
    656 			*nis_err = NIS_BADNAME;
    657 			return (NULL);
    658 		}
    659 #ifdef ORGDIR
    660 		append_orgdir(buf);
    661 #endif
    662 
    663 		if (!path)
    664 			path = "$"; /* default path */
    665 
    666 		/* if can't get local_dir, no need to continue */
    667 		if (!(local = nis_local_directory())) {
    668 			*nis_err = NIS_NOMEMORY;
    669 			return (NULL);
    670 		}
    671 
    672 		/* parse the path into segments */
    673 		i = parse_path(buf, path, local, result, NIS_MAXPATHDEPTH);
    674 
    675 		/* check case were name is "near" the root. */
    676 		if (i == 0) {
    677 			if (strlcat(buf, ".", sizeof (buf)) >= sizeof (buf)) {
    678 				*nis_err = NIS_BADNAME;
    679 				return (NULL);
    680 			}
    681 			if (strlcat(buf, local, sizeof (buf)) >= sizeof (buf)) {
    682 				*nis_err = NIS_BADNAME;
    683 				return (NULL);
    684 			}
    685 			result[i++] = (nis_name) strdup(buf);
    686 		}
    687 		result[i] = NULL;
    688 
    689 		/* only if NIS_PATH is set */
    690 		if (tmp_path)
    691 			check_dup_dnames(result, i);
    692 
    693 	} else {
    694 		/* the simple case for a fully-qualified path */
    695 		result = malloc(2 * sizeof (nis_name));
    696 		if (!result) {
    697 			*nis_err = NIS_NOMEMORY;
    698 			return (NULL);
    699 		}
    700 		result[0] = (nis_name) strdup((char *)(name));
    701 		result[1] = NULL;
    702 	}
    703 
    704 	*nis_err = NIS_SUCCESS;
    705 	return (result);
    706 }
    707 
    708 
    709 /*
    710  * nis_getnames(name, nis_error *nis_err)
    711  *
    712  * This function returns a list of candidate NIS+ names given an
    713  * non fully qualified NIS name. Note it is HOST RFC compliant
    714  * in that it stops generating names when the resulting name would
    715  * have 2 or fewer dots in it. This helps avoid banging on the root
    716  * name servers.
    717  */
    718 nis_name *
    719 nis_getnames(nis_name name)
    720 {
    721 	nis_error nis_err;
    722 
    723 	return (__nis_getnames(name, &nis_err));
    724 }
    725 
    726 
    727 /* free an entire list, one at a time */
    728 void
    729 nis_freenames(nis_name *namelist)
    730 {
    731 	int i = 0;
    732 
    733 	while (namelist[i])
    734 		free(namelist[i++]);
    735 	free(namelist);
    736 }
    737