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  * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
23  * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
24  */
25 
26 #include "ldap_common.h"
27 #include <malloc.h>
28 #include <synch.h>
29 #include <syslog.h>
30 #include <rpcsvc/ypclnt.h>
31 #include <rpcsvc/yp_prot.h>
32 #include <thread.h>
33 #include <ctype.h>
34 #include <stdlib.h>
35 #include <signal.h>
36 #include <sys/stat.h>
37 
38 /* getent attributes filters */
39 #define	_F_GETALIASENT		"(objectClass=rfc822MailGroup)"
40 #define	_F_GETAUTHNAME		"(objectClass=SolarisAuthAttr)"
41 #define	_F_GETAUUSERNAME	"(objectClass=SolarisAuditUser)"
42 #define	_F_GETEXECNAME		"(objectClass=SolarisExecAttr)"
43 #define	_F_GETGRENT		"(objectClass=posixGroup)"
44 #define	_F_GETHOSTENT		"(objectClass=ipHost)"
45 #define	_F_GETNETENT		"(objectClass=ipNetwork)"
46 #define	_F_GETPROFNAME \
47 "(&(objectClass=SolarisProfAttr)(!(SolarisKernelSecurityPolicy=*)))"
48 #define	_F_GETPROTOENT		"(objectClass=ipProtocol)"
49 #define	_F_GETPWENT		"(objectClass=posixAccount)"
50 #define	_F_GETPRINTERENT	"(objectClass=sunPrinter)"
51 #define	_F_GETRPCENT		"(objectClass=oncRpc)"
52 #define	_F_GETSERVENT		"(objectClass=ipService)"
53 #define	_F_GETSPENT		"(objectclass=shadowAccount)"
54 #define	_F_GETUSERNAME		"(objectClass=SolarisUserAttr)"
55 #define	_F_GETPROJENT		"(objectClass=SolarisProject)"
56 #define	_F_GETTNRHDB		"(objectClass=ipTnetHost)"
57 #define	_F_GETTNRHTP		"(&(objectClass=ipTnetTemplate)"\
58 				"(SolarisAttrKeyValue=*))"
59 #define	_F_GETENT_SSD		"(%s)"
60 
61 /* getent sort attributes */
62 #define	_A_UID			"uid"
63 #define	_A_GIDNUMBER		"gidnumber"
64 #define	_A_CN			"cn"
65 #define	_A_IPNETWORKNUM		"ipnetworknumber"
66 #define	_A_PROJECTNAM		"SolarisProjectName"
67 #define	_A_IPTNETNUM		"ipTnetNumber"
68 #define	_A_IPTNETTMPLNAM	"ipTnetTemplateName"
69 
70 static struct gettablefilter {
71 	char *tablename;
72 	char *tablefilter;
73 	char *sortattr;
74 } gettablefilterent[] = {
75 	{(char *)_PASSWD,	(char *)_F_GETPWENT,	(char *)_A_UID},
76 	{(char *)_SHADOW,	(char *)_F_GETSPENT,	(char *)_A_UID},
77 	{(char *)_GROUP,	(char *)_F_GETGRENT,	(char *)_A_GIDNUMBER},
78 	{(char *)_HOSTS,	(char *)_F_GETHOSTENT,	(char *)_A_CN},
79 	{(char *)_NETWORKS,	(char *)_F_GETNETENT,
80 						(char *)_A_IPNETWORKNUM},
81 	{(char *)_PROTOCOLS,	(char *)_F_GETPROTOENT,	(char *)_A_CN},
82 	{(char *)_RPC,		(char *)_F_GETRPCENT,	(char *)_A_CN},
83 	{(char *)_ALIASES,	(char *)_F_GETALIASENT,	(char *)_A_CN},
84 	{(char *)_SERVICES,	(char *)_F_GETSERVENT,	(char *)_A_CN},
85 	{(char *)_AUUSER,	(char *)_F_GETAUUSERNAME,
86 							(char *)_A_UID},
87 	{(char *)_AUTHATTR,	(char *)_F_GETAUTHNAME,	(char *)_A_CN},
88 	{(char *)_EXECATTR,	(char *)_F_GETEXECNAME,	(char *)_A_CN},
89 	{(char *)_PROFATTR,	(char *)_F_GETPROFNAME,	(char *)_A_CN},
90 	{(char *)_USERATTR,	(char *)_F_GETUSERNAME,	(char *)_A_UID},
91 	{(char *)_PROJECT,	(char *)_F_GETPROJENT,	(char *)_A_PROJECTNAM},
92 	{(char *)_PRINTERS,	(char *)_F_GETPRINTERENT, (char *)_A_CN},
93 	{(char *)_TNRHDB,	(char *)_F_GETTNRHDB,	(char *)_A_IPTNETNUM},
94 	{(char *)_TNRHTP,	(char *)_F_GETTNRHTP,
95 						(char *)_A_IPTNETTMPLNAM},
96 	{(char *)NULL,		(char *)NULL,		(char *)NULL}
97 };
98 
99 
100 nss_status_t
switch_err(int rc,ns_ldap_error_t * error)101 switch_err(int rc, ns_ldap_error_t *error)
102 {
103 	switch (rc) {
104 	case NS_LDAP_SUCCESS:
105 		return (NSS_SUCCESS);
106 
107 	case NS_LDAP_NOTFOUND:
108 		errno = 0;
109 		return (NSS_NOTFOUND);
110 
111 	case NS_LDAP_PARTIAL:
112 		return (NSS_TRYAGAIN);
113 
114 	case NS_LDAP_INTERNAL:
115 		if (error && (error->status == LDAP_SERVER_DOWN ||
116 		    error->status == LDAP_TIMEOUT))
117 			return (NSS_TRYAGAIN);
118 		else
119 			return (NSS_UNAVAIL);
120 
121 	default:
122 		return (NSS_UNAVAIL);
123 	}
124 }
125 /* ARGSUSED */
126 nss_status_t
_nss_ldap_lookup(ldap_backend_ptr be,nss_XbyY_args_t * argp,char * database,char * searchfilter,char * domain,int (* init_filter_cb)(const ns_ldap_search_desc_t * desc,char ** realfilter,const void * userdata),const void * userdata)127 _nss_ldap_lookup(ldap_backend_ptr be, nss_XbyY_args_t *argp,
128     char *database, char *searchfilter, char *domain,
129     int (*init_filter_cb)(const ns_ldap_search_desc_t *desc,
130     char **realfilter, const void *userdata),
131     const void *userdata)
132 {
133 	int		callbackstat = 0;
134 	ns_ldap_error_t	*error = NULL;
135 	int		rc;
136 
137 #ifdef	DEBUG
138 	(void) fprintf(stdout, "\n[ldap_common.c: _nss_ldap_lookup]\n");
139 	(void) fprintf(stdout, "\tsearchfilter: %s\n", searchfilter);
140 	(void) fprintf(stdout,
141 	    "\tuserdata: %s\n", userdata ? userdata : "NULL");
142 	(void) fprintf(stdout, "\tdatabase: %s\n", database);
143 #endif	/* DEBUG */
144 
145 	(void) __ns_ldap_freeResult(&be->result);
146 
147 	if ((rc = __ns_ldap_list(database, searchfilter, init_filter_cb,
148 	    be->attrs, NULL, 0, &be->result, &error, NULL,
149 	    userdata)) != NS_LDAP_SUCCESS) {
150 		argp->returnval = 0;
151 		rc = switch_err(rc, error);
152 		(void) __ns_ldap_freeError(&error);
153 
154 		return (rc);
155 	}
156 		(void) __ns_ldap_freeError(&error);
157 	/* callback function */
158 	if ((callbackstat =
159 	    be->ldapobj2str(be, argp)) != NSS_STR_PARSE_SUCCESS) {
160 		goto error_out;
161 	}
162 
163 	/*
164 	 * publickey does not have a front end marshaller and expects
165 	 * a string to be returned in NSS.
166 	 * No need to convert file format -> struct.
167 	 *
168 	 */
169 	if (be->db_type == NSS_LDAP_DB_PUBLICKEY) {
170 		argp->returnval = argp->buf.buffer;
171 		argp->returnlen = strlen(argp->buf.buffer);
172 		be->db_type = NSS_LDAP_DB_NONE;
173 		return (NSS_SUCCESS);
174 	}
175 	/*
176 	 *  Assume the switch engine wants the returned data in the file
177 	 *  format when argp->buf.result == NULL.
178 	 *  The front-end marshaller str2ether(ethers) uses
179 	 *  ent (argp->buf.result) and buffer (argp->buf.buffer)
180 	 *  for different purpose so ethers has to be treated differently.
181 	 */
182 	if (argp->buf.result != NULL ||
183 	    be->db_type == NSS_LDAP_DB_ETHERS) {
184 		/* file format -> struct */
185 		if (argp->str2ent == NULL) {
186 			callbackstat = NSS_STR_PARSE_PARSE;
187 			goto error_out;
188 		}
189 
190 		callbackstat = (*argp->str2ent)(be->buffer,
191 		    be->buflen,
192 		    argp->buf.result,
193 		    argp->buf.buffer,
194 		    argp->buf.buflen);
195 		if (callbackstat == NSS_STR_PARSE_SUCCESS) {
196 			if (be->db_type == NSS_LDAP_DB_ETHERS &&
197 			    argp->buf.buffer != NULL) {
198 				argp->returnval = argp->buf.buffer;
199 				argp->returnlen = strlen(argp->buf.buffer);
200 			} else {
201 				argp->returnval = argp->buf.result;
202 				argp->returnlen = 1; /* irrelevant */
203 			}
204 			if (be->buffer != NULL) {
205 				free(be->buffer);
206 				be->buffer = NULL;
207 				be->buflen = 0;
208 				be->db_type = NSS_LDAP_DB_NONE;
209 			}
210 			return ((nss_status_t)NSS_SUCCESS);
211 		}
212 	} else {
213 			/* return file format in argp->buf.buffer */
214 			argp->returnval = argp->buf.buffer;
215 			argp->returnlen = strlen(argp->buf.buffer);
216 			return ((nss_status_t)NSS_SUCCESS);
217 	}
218 
219 error_out:
220 	if (be->buffer != NULL) {
221 		free(be->buffer);
222 		be->buffer = NULL;
223 		be->buflen = 0;
224 		be->db_type = NSS_LDAP_DB_NONE;
225 	}
226 	/* error */
227 	if (callbackstat == NSS_STR_PARSE_PARSE) {
228 		argp->returnval = 0;
229 		return ((nss_status_t)NSS_NOTFOUND);
230 	}
231 	if (callbackstat == NSS_STR_PARSE_ERANGE) {
232 		argp->erange = 1;
233 		return ((nss_status_t)NSS_NOTFOUND);
234 	}
235 	if (callbackstat == NSS_STR_PARSE_NO_ADDR) {
236 		/* No IPV4 address is found */
237 		argp->h_errno = HOST_NOT_FOUND;
238 		return ((nss_status_t)NSS_NOTFOUND);
239 	}
240 	return ((nss_status_t)NSS_UNAVAIL);
241 }
242 
243 /*
244  *  This function is similar to _nss_ldap_lookup except it does not
245  *  do a callback.  It is only used by getnetgrent.c
246  */
247 
248 /* ARGSUSED */
249 nss_status_t
_nss_ldap_nocb_lookup(ldap_backend_ptr be,nss_XbyY_args_t * argp,char * database,char * searchfilter,const char * const * attrs,int (* init_filter_cb)(const ns_ldap_search_desc_t * desc,char ** realfilter,const void * userdata),const void * userdata)250 _nss_ldap_nocb_lookup(ldap_backend_ptr be, nss_XbyY_args_t *argp,
251     char *database, char *searchfilter, const char * const *attrs,
252     int (*init_filter_cb)(const ns_ldap_search_desc_t *desc,
253     char **realfilter, const void *userdata),
254     const void *userdata)
255 {
256 	ns_ldap_error_t	*error = NULL;
257 	int		rc;
258 
259 	if (attrs == NULL)
260 		attrs = be->attrs;
261 
262 #ifdef	DEBUG
263 	(void) fprintf(stdout, "\n[ldap_common.c: _nss_ldap_nocb_lookup]\n");
264 	(void) fprintf(stdout, "\tsearchfilter: %s\n", searchfilter);
265 	(void) fprintf(stdout, "\tdatabase: %s\n", database);
266 	(void) fprintf(stdout,
267 	    "\tuserdata: %s\n", userdata ? userdata : "NULL");
268 #endif	/* DEBUG */
269 
270 	(void) __ns_ldap_freeResult(&be->result);
271 
272 	if ((rc = __ns_ldap_list(database, searchfilter, init_filter_cb,
273 	    attrs, NULL, 0, &be->result, &error, NULL,
274 	    userdata)) != NS_LDAP_SUCCESS) {
275 		if (argp != NULL)
276 			argp->returnval = 0;
277 		rc = switch_err(rc, error);
278 		(void) __ns_ldap_freeError(&error);
279 		return (rc);
280 	}
281 
282 	return ((nss_status_t)NSS_SUCCESS);
283 }
284 
285 
286 /*
287  *
288  */
289 
290 void
_clean_ldap_backend(ldap_backend_ptr be)291 _clean_ldap_backend(ldap_backend_ptr be)
292 {
293 	ns_ldap_error_t *error;
294 
295 #ifdef	DEBUG
296 	(void) fprintf(stdout, "\n[ldap_common.c: _clean_ldap_backend]\n");
297 #endif	/* DEBUG */
298 
299 	if (be->tablename != NULL)
300 		free(be->tablename);
301 	if (be->result != NULL)
302 		(void) __ns_ldap_freeResult(&be->result);
303 	if (be->enumcookie != NULL)
304 		(void) __ns_ldap_endEntry(&be->enumcookie, &error);
305 	if (be->services_cookie != NULL)
306 		_nss_services_cookie_free((void **)&be->services_cookie);
307 	if (be->toglue != NULL) {
308 		free(be->toglue);
309 		be->toglue = NULL;
310 	}
311 	if (be->buffer != NULL) {
312 		free(be->buffer);
313 		be->buffer = NULL;
314 	}
315 	free(be);
316 }
317 
318 
319 /*
320  * _nss_ldap_destr will free all smalloc'ed variable strings and structures
321  * before exiting this nsswitch shared backend library. This function is
322  * called before returning control back to nsswitch.
323  */
324 
325 /*ARGSUSED1*/
326 nss_status_t
_nss_ldap_destr(ldap_backend_ptr be,void * a)327 _nss_ldap_destr(ldap_backend_ptr be, void *a)
328 {
329 
330 #ifdef DEBUG
331 	(void) fprintf(stdout, "\n[ldap_common.c: _nss_ldap_destr]\n");
332 #endif /* DEBUG */
333 
334 	(void) _clean_ldap_backend(be);
335 
336 	return ((nss_status_t)NSS_SUCCESS);
337 }
338 
339 
340 /*
341  * _nss_ldap_setent called before _nss_ldap_getent. This function is
342  * required by POSIX.
343  */
344 
345 nss_status_t
_nss_ldap_setent(ldap_backend_ptr be,void * a)346 _nss_ldap_setent(ldap_backend_ptr be, void *a)
347 {
348 	struct gettablefilter	*gtf;
349 
350 #ifdef DEBUG
351 	(void) fprintf(stdout, "\n[ldap_common.c: _nss_ldap_setent]\n");
352 #endif /* DEBUG */
353 
354 	if (be->setcalled == 1)
355 		(void) _nss_ldap_endent(be, a);
356 	be->filter = NULL;
357 	be->sortattr = NULL;
358 	for (gtf = gettablefilterent; gtf->tablename != (char *)NULL; gtf++) {
359 		if (strcmp(gtf->tablename, be->tablename))
360 			continue;
361 		be->filter = (char *)gtf->tablefilter;
362 		be->sortattr = (char *)gtf->sortattr;
363 		break;
364 	}
365 
366 	be->setcalled = 1;
367 	be->enumcookie = NULL;
368 	be->result = NULL;
369 	be->services_cookie = NULL;
370 	be->buffer = NULL;
371 	return ((nss_status_t)NSS_SUCCESS);
372 }
373 
374 
375 /*
376  * _nss_ldap_endent called after _nss_ldap_getent. This function is
377  * required by POSIX.
378  */
379 
380 /*ARGSUSED1*/
381 nss_status_t
_nss_ldap_endent(ldap_backend_ptr be,void * a)382 _nss_ldap_endent(ldap_backend_ptr be, void *a)
383 {
384 	ns_ldap_error_t	*error = NULL;
385 
386 #ifdef DEBUG
387 	(void) fprintf(stdout, "\n[ldap_common.c: _nss_ldap_endent]\n");
388 #endif /* DEBUG */
389 
390 	be->setcalled = 0;
391 	be->filter = NULL;
392 	be->sortattr = NULL;
393 	if (be->enumcookie != NULL) {
394 		(void) __ns_ldap_endEntry(&be->enumcookie, &error);
395 		(void) __ns_ldap_freeError(&error);
396 	}
397 	if (be->result != NULL) {
398 		(void) __ns_ldap_freeResult(&be->result);
399 	}
400 	if (be->services_cookie != NULL) {
401 		_nss_services_cookie_free((void **)&be->services_cookie);
402 	}
403 	if (be->buffer != NULL) {
404 		free(be->buffer);
405 		be->buffer = NULL;
406 	}
407 
408 	return ((nss_status_t)NSS_SUCCESS);
409 }
410 
411 
412 /*
413  *
414  */
415 
416 nss_status_t
_nss_ldap_getent(ldap_backend_ptr be,void * a)417 _nss_ldap_getent(ldap_backend_ptr be, void *a)
418 {
419 	nss_XbyY_args_t	*argp = (nss_XbyY_args_t *)a;
420 	ns_ldap_error_t	*error = NULL;
421 	int		parsestat = 0;
422 	int		retcode = 0;
423 
424 #ifdef	DEBUG
425 	(void) fprintf(stdout, "\n[ldap_common.c: _nss_ldap_getent]\n");
426 #endif	/* DEBUG */
427 
428 	if (be->setcalled == 0)
429 		(void) _nss_ldap_setent(be, a);
430 
431 next_entry:
432 	if (be->enumcookie == NULL) {
433 		retcode = __ns_ldap_firstEntry(be->tablename,
434 		    be->filter, be->sortattr, _merge_SSD_filter, be->attrs,
435 		    NULL, 0, &be->enumcookie,
436 		    &be->result, &error, _F_GETENT_SSD);
437 	} else {
438 		if (be->services_cookie == NULL) {
439 			retcode = __ns_ldap_nextEntry(be->enumcookie,
440 			    &be->result, &error);
441 		}
442 	}
443 	if (retcode != NS_LDAP_SUCCESS) {
444 		retcode = switch_err(retcode, error);
445 		(void) __ns_ldap_freeError(&error);
446 		(void) _nss_ldap_endent(be, a);
447 		return (retcode);
448 	}
449 
450 	if (be->result == NULL) {
451 		parsestat = NSS_STR_PARSE_NO_RESULT;
452 		goto error_out;
453 	}
454 	/* ns_ldap_entry_t -> file format */
455 	if ((parsestat = be->ldapobj2str(be, argp))
456 	    == NSS_STR_PARSE_SUCCESS) {
457 		if (argp->buf.result != NULL) {
458 			/* file format -> struct */
459 			if (argp->str2ent == NULL) {
460 				parsestat = NSS_STR_PARSE_NO_RESULT;
461 				goto error_out;
462 			}
463 			parsestat = (*argp->str2ent)(be->buffer,
464 			    be->buflen,
465 			    argp->buf.result,
466 			    argp->buf.buffer,
467 			    argp->buf.buflen);
468 			if (parsestat == NSS_STR_PARSE_SUCCESS) {
469 				if (be->buffer != NULL) {
470 					free(be->buffer);
471 					be->buffer = NULL;
472 					be->buflen = 0;
473 				}
474 				be->result = NULL;
475 				argp->returnval = argp->buf.result;
476 				argp->returnlen = 1; /* irrevelant */
477 				return ((nss_status_t)NSS_SUCCESS);
478 			}
479 		} else {
480 			/*
481 			 * nscd is not caching the enumerated
482 			 * entries. This code path would be dormant.
483 			 * Keep this path for the future references.
484 			 */
485 			argp->returnval = argp->buf.buffer;
486 			argp->returnlen =
487 			    strlen(argp->buf.buffer) + 1;
488 		}
489 	}
490 error_out:
491 	if (be->buffer != NULL) {
492 		free(be->buffer);
493 		be->buffer = NULL;
494 		be->buflen = 0;
495 	}
496 	be->result = NULL;
497 	if (parsestat == NSS_STR_PARSE_NO_RESULT) {
498 		argp->returnval = 0;
499 		(void) _nss_ldap_endent(be, a);
500 		return ((nss_status_t)NSS_NOTFOUND);
501 	}
502 
503 	if (parsestat == NSS_STR_PARSE_ERANGE) {
504 		argp->erange = 1;
505 		(void) _nss_ldap_endent(be, a);
506 		return ((nss_status_t)NSS_NOTFOUND);
507 	}
508 	if (parsestat == NSS_STR_PARSE_NO_ADDR)
509 		/*
510 		 * No IPV4 address is found in the current entry.
511 		 * It indicates that the entry contains IPV6 addresses
512 		 * only. Instead of calling _nss_ldap_endent to
513 		 * terminate, get next entry to continue enumeration.
514 		 * If it returned NSS_NOTFOUND here,
515 		 * gethostent() would return NULL
516 		 * and the enumeration would stop prematurely.
517 		 */
518 		goto next_entry;
519 
520 	if (parsestat == NSS_STR_PARSE_PARSE)
521 		/*
522 		 * There has been a parse error. Most likely some
523 		 * mandatory attributes are missing. Ignore the error
524 		 * and get the next entry. If we returned an error the
525 		 * enumeration would stop prematurely.
526 		 */
527 		goto next_entry;
528 
529 	return ((nss_status_t)NSS_SUCCESS);
530 }
531 
532 
533 /*
534  *
535  */
536 
537 nss_backend_t *
_nss_ldap_constr(ldap_backend_op_t ops[],int nops,char * tablename,const char ** attrs,fnf ldapobj2str)538 _nss_ldap_constr(ldap_backend_op_t ops[], int nops, char *tablename,
539     const char **attrs, fnf ldapobj2str)
540 {
541 	ldap_backend_ptr	be;
542 
543 #ifdef	DEBUG
544 	(void) fprintf(stdout, "\n[ldap_common.c: _nss_ldap_constr]\n");
545 #endif	/* DEBUG */
546 
547 	if ((be = (ldap_backend_ptr) calloc(1, sizeof (*be))) == 0)
548 		return (0);
549 	be->ops = ops;
550 	be->nops = (nss_dbop_t)nops;
551 	be->tablename = (char *)strdup(tablename);
552 	be->attrs = attrs;
553 	be->ldapobj2str = ldapobj2str;
554 
555 	return ((nss_backend_t *)be);
556 }
557 
558 
559 /*
560  *
561  */
562 int
chophostdomain(char * string,char * host,char * domain)563 chophostdomain(char *string, char *host, char *domain)
564 {
565 	char	*dot;
566 
567 	if (string == NULL)
568 		return (-1);
569 
570 	if ((dot = strchr(string, '.')) == NULL) {
571 		return (0);
572 	}
573 	*dot = '\0';
574 	(void) strcpy(host, string);
575 	(void) strcpy(domain, ++dot);
576 
577 	return (0);
578 }
579 
580 
581 /*
582  *
583  */
584 int
propersubdomain(char * domain,char * subdomain)585 propersubdomain(char *domain, char *subdomain)
586 {
587 	int	domainlen, subdomainlen;
588 
589 	/* sanity check */
590 	if (domain == NULL || subdomain == NULL)
591 		return (-1);
592 
593 	domainlen = strlen(domain);
594 	subdomainlen = strlen(subdomain);
595 
596 	/* is afterdot a substring of domain? */
597 	if ((strncasecmp(domain, subdomain, subdomainlen)) != 0)
598 		return (-1);
599 
600 	if (domainlen == subdomainlen)
601 		return (1);
602 
603 	if (subdomainlen > domainlen)
604 		return (-1);
605 
606 	if (*(domain + subdomainlen) != '.')
607 		return (-1);
608 
609 	return (1);
610 }
611