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