xref: /titanic_50/usr/src/lib/smbsrv/libsmbns/common/smbns_dyndns.c (revision 380789fc80376bd1573770361cb177a08c7e3524)
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 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <assert.h>
29 #include <errno.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <strings.h>
33 #include <sys/types.h>
34 #include <sys/socket.h>
35 #include <netinet/in.h>
36 #include <arpa/inet.h>
37 #include <sys/time.h>
38 #include <unistd.h>
39 #include <string.h>
40 #include <arpa/nameser.h>
41 #include <resolv.h>
42 #include <netdb.h>
43 #include <rpc/rpc.h>
44 #include <syslog.h>
45 #include <gssapi/gssapi.h>
46 #include <kerberosv5/krb5.h>
47 #include <net/if.h>
48 
49 #include <smbns_dyndns.h>
50 #include <smbns_krb.h>
51 
52 /* internal use, in dyndns_add_entry */
53 #define	DEL_NONE		2
54 /* Maximum retires if not authoritative */
55 #define	MAX_AUTH_RETRIES 3
56 
57 
58 static int
59 dyndns_enabled(void)
60 {
61 	int enabled;
62 
63 	smb_config_rdlock();
64 	enabled = smb_config_getyorn(SMB_CI_DYNDNS_ENABLE);
65 	smb_config_unlock();
66 
67 	return ((enabled) ? 1 : 0);
68 }
69 
70 /*
71  * XXX The following should be removed once head/arpa/nameser_compat.h
72  * defines BADSIG, BADKEY, BADTIME macros
73  */
74 #ifndef	BADSIG
75 #define	BADSIG ns_r_badsig
76 #endif /* BADSIG */
77 
78 #ifndef	BADKEY
79 #define	BADKEY ns_r_badkey
80 #endif /* BADKEY */
81 
82 #ifndef	BADTIME
83 #define	BADTIME ns_r_badtime
84 #endif /* BADTIME */
85 
86 /*
87  * dyndns_msg_err
88  * Display error message for DNS error code found in the DNS header in
89  * reply message.
90  * Parameters:
91  *   err: DNS errer code
92  * Returns:
93  *   None
94  */
95 void
96 dyndns_msg_err(int err)
97 {
98 	switch (err) {
99 	case NOERROR:
100 		break;
101 	case FORMERR:
102 		syslog(LOG_ERR, "DNS message format error\n");
103 		break;
104 	case SERVFAIL:
105 		syslog(LOG_ERR, "DNS server internal error\n");
106 		break;
107 	case NXDOMAIN:
108 		syslog(LOG_ERR, "DNS entry should exist but does not exist\n");
109 		break;
110 	case NOTIMP:
111 		syslog(LOG_ERR, "DNS opcode not supported\n");
112 		break;
113 	case REFUSED:
114 		syslog(LOG_ERR, "DNS operation refused\n");
115 		break;
116 	case YXDOMAIN:
117 		syslog(LOG_ERR, "DNS entry shouldn't exist but does exist\n");
118 		break;
119 	case YXRRSET:
120 		syslog(LOG_ERR, "DNS RRSet shouldn't exist but does exist\n");
121 		break;
122 	case NXRRSET:
123 		syslog(LOG_ERR, "DNS RRSet should exist but does not exist\n");
124 		break;
125 	case NOTAUTH:
126 		syslog(LOG_ERR, "DNS server is not authoritative "
127 		    "for specified zone\n");
128 		break;
129 	case NOTZONE:
130 		syslog(LOG_ERR, "Name in Prereq or Update section not "
131 		    "within specified zone\n");
132 		break;
133 	case BADSIG:
134 		syslog(LOG_ERR, "Bad transaction signature (TSIG)");
135 		break;
136 	case BADKEY:
137 		syslog(LOG_ERR, "Bad transaction key (TKEY)");
138 		break;
139 	case BADTIME:
140 		syslog(LOG_ERR, "Time not synchronized");
141 		break;
142 
143 	default:
144 		syslog(LOG_ERR, "Unknown DNS error\n");
145 	}
146 }
147 
148 /*
149  * display_stat
150  * Display GSS error message from error code.  This routine is used to display
151  * the mechanism independent and mechanism specific error messages for GSS
152  * routines.  The major status error code is the mechanism independent error
153  * code and the minor status error code is the mechanism specific error code.
154  * Parameters:
155  *   maj: GSS major status
156  *   min: GSS minor status
157  * Returns:
158  *   None
159  */
160 static void
161 display_stat(OM_uint32 maj, OM_uint32 min)
162 {
163 	gss_buffer_desc msg;
164 	OM_uint32 msg_ctx = 0;
165 	OM_uint32 min2;
166 	(void) gss_display_status(&min2, maj, GSS_C_GSS_CODE, GSS_C_NULL_OID,
167 	    &msg_ctx, &msg);
168 	syslog(LOG_ERR, "dyndns: GSS major status error: %s\n",
169 	    (char *)msg.value);
170 	(void) gss_display_status(&min2, min, GSS_C_MECH_CODE, GSS_C_NULL_OID,
171 	    &msg_ctx, &msg);
172 	syslog(LOG_ERR, "dyndns: GSS minor status error: %s\n",
173 	    (char *)msg.value);
174 }
175 
176 static char *
177 dyndns_put_nshort(char *buf, uint16_t val)
178 {
179 	uint16_t nval;
180 
181 	nval = htons(val);
182 	(void) memcpy(buf, &nval, sizeof (uint16_t));
183 	buf += sizeof (uint16_t);
184 	return (buf);
185 }
186 
187 char *
188 dyndns_get_nshort(char *buf, uint16_t *val)
189 {
190 	uint16_t nval;
191 
192 	(void) memcpy(&nval, buf, sizeof (uint16_t));
193 	*val = ntohs(nval);
194 	buf += sizeof (uint16_t);
195 	return (buf);
196 }
197 
198 static char *
199 dyndns_put_nlong(char *buf, uint32_t val)
200 {
201 	uint32_t lval;
202 
203 	lval = htonl(val);
204 	(void) memcpy(buf, &lval, sizeof (uint32_t));
205 	buf += sizeof (uint32_t);
206 	return (buf);
207 }
208 
209 static char *
210 dyndns_put_byte(char *buf, char val)
211 {
212 	*buf = val;
213 	buf++;
214 	return (buf);
215 }
216 
217 static char *
218 dyndns_put_int(char *buf, int val)
219 {
220 	(void) memcpy(buf, &val, sizeof (int));
221 	buf += sizeof (int);
222 	return (buf);
223 }
224 
225 char *
226 dyndns_get_int(char *buf, int *val)
227 {
228 	(void) memcpy(val, buf, sizeof (int));
229 	buf += sizeof (int);
230 	return (buf);
231 }
232 
233 
234 /*
235  * dyndns_stuff_str
236  * Converts a domain string by removing periods and replacing with a byte value
237  * of how many characters following period.  A byte value is placed in front
238  * to indicate how many characters before first period.  A NULL character is
239  * placed at the end. i.e. host.procom.com -> 4host5procom3com0
240  * Buffer space checking is done by caller.
241  * Parameters:
242  *   ptr : address of pointer to buffer to store converted string
243  *   zone: domain name string
244  * Returns:
245  *   ptr: address of pointer to next available buffer space
246  *   -1 : error
247  *    0 : success
248  */
249 static int
250 dyndns_stuff_str(char **ptr, char *zone)
251 {
252 	int len;
253 	char *lenPtr, *zonePtr;
254 
255 	for (zonePtr = zone; *zonePtr; ) {
256 		lenPtr = *ptr;
257 		*ptr = *ptr + 1;
258 		len = 0;
259 		while (*zonePtr != '.' && *zonePtr != 0) {
260 			*ptr = dyndns_put_byte(*ptr, *zonePtr);
261 			zonePtr++;
262 			len++;
263 		}
264 		*lenPtr = len;
265 		if (*zonePtr == '.')
266 			zonePtr++;
267 	}
268 	*ptr = dyndns_put_byte(*ptr, 0);
269 	return (0);
270 }
271 
272 /*
273  * dyndns_build_header
274  * Build the header for DNS query and DNS update request message.
275  * Parameters:
276  *   ptr               : address of pointer to buffer to store header
277  *   buf_len           : buffer length
278  *   msg_id            : message id
279  *   query_req         : use REQ_QUERY for query message or REQ_UPDATE for
280  *                       update message
281  *   quest_zone_cnt    : number of question record for query message or
282  *                       number of zone record for update message
283  *   ans_prereq_cnt    : number of answer record for query message or
284  *                       number of prerequisite record for update message
285  *   nameser_update_cnt: number of name server for query message or
286  *                       number of update record for update message
287  *   addit_cnt         : number of additional record
288  *   flags             : query flags word
289  * Returns:
290  *   ptr: address of pointer to next available buffer space
291  *   -1 : error
292  *    0 : success
293  */
294 int
295 dyndns_build_header(char **ptr, int buf_len, uint16_t msg_id, int query_req,
296     uint16_t quest_zone_cnt, uint16_t ans_prereq_cnt,
297     uint16_t nameser_update_cnt, uint16_t addit_cnt, int flags)
298 {
299 	uint16_t opcode;
300 
301 	if (buf_len < 12) {
302 		syslog(LOG_ERR, "dyndns: no more buf for header section\n");
303 		return (-1);
304 	}
305 
306 	*ptr = dyndns_put_nshort(*ptr, msg_id);	/* mesg ID */
307 	if (query_req == REQ_QUERY)
308 		opcode = ns_o_query;	/* query msg */
309 	else
310 		opcode = ns_o_update << 11;	/* update msg */
311 	opcode |= flags;
312 	/* mesg opcode */
313 	*ptr = dyndns_put_nshort(*ptr, opcode);
314 	/* zone record count */
315 	*ptr = dyndns_put_nshort(*ptr, quest_zone_cnt);
316 	/* prerequiste record count */
317 	*ptr = dyndns_put_nshort(*ptr, ans_prereq_cnt);
318 	/* update record count */
319 	*ptr = dyndns_put_nshort(*ptr, nameser_update_cnt);
320 	/* additional record count */
321 	*ptr = dyndns_put_nshort(*ptr, addit_cnt);
322 
323 	return (0);
324 }
325 
326 /*
327  * dyndns_build_quest_zone
328  * Build the question section for query message or zone section for
329  * update message.
330  * Parameters:
331  *   ptr    : address of pointer to buffer to store question or zone section
332  *   buf_len: buffer length
333  *   name   : question or zone name
334  *   type   : type of question or zone
335  *   class  : class of question or zone
336  * Returns:
337  *   ptr: address of pointer to next available buffer space
338  *   -1 : error
339  *    0 : success
340  */
341 int
342 dyndns_build_quest_zone(char **ptr, int buf_len, char *name, int type,
343 	int class)
344 {
345 	char *zonePtr;
346 
347 	if ((strlen(name) + 6) > buf_len) {
348 		syslog(LOG_ERR, "dyndns: no more buf "
349 		    "for question/zone section\n");
350 		return (-1);
351 	}
352 
353 	zonePtr = *ptr;
354 	(void) dyndns_stuff_str(&zonePtr, name);
355 	*ptr = zonePtr;
356 	*ptr = dyndns_put_nshort(*ptr, type);
357 	*ptr = dyndns_put_nshort(*ptr, class);
358 	return (0);
359 }
360 
361 /*
362  * dyndns_build_update
363  * Build update section of update message for adding and removing a record.
364  * If the ttl value is 0 then this message is for record deletion.
365  *
366  * Parameters:
367  *   ptr     : address of pointer to buffer to store update section
368  *   buf_len : buffer length
369  *   name    : resource name of this record
370  *   type    : type of this record
371  *   class   : class of this record
372  *   ttl     : time-to-live, cached time of this entry by others and not
373  *             within DNS database, a zero value for record(s) deletion
374  *   data    : data of this resource record
375  *   forw_rev: UPDATE_FORW for forward zone, UPDATE_REV for reverse zone
376  *   add_del : UPDATE_ADD for adding entry, UPDATE_DEL for removing zone
377  *   del_type: DEL_ONE for deleting one entry, DEL_ALL for deleting all
378  *             entries of the same resource name.  Only valid for UPDATE_DEL.
379  * Returns:
380  *   ptr: address of pointer to next available buffer space
381  *   -1 : error
382  *    0 : success
383  */
384 static int
385 dyndns_build_update(char **ptr, int buf_len, char *name, int type, int class,
386 	uint32_t ttl, char *data, int forw_rev, int add_del, int del_type)
387 {
388 	char *namePtr;
389 	int rec_len, data_len;
390 
391 	rec_len = strlen(name) + 10;
392 	if (add_del == UPDATE_ADD) {
393 		if (forw_rev == UPDATE_FORW)
394 			data_len = 4;
395 		else
396 			data_len = strlen(data) + 2;
397 	} else {
398 		if (del_type == DEL_ALL)
399 			data_len = 0;
400 		else if (forw_rev == UPDATE_FORW)
401 			data_len = 4;
402 		else
403 			data_len = strlen(data) + 2;
404 	}
405 
406 	if (rec_len + data_len > buf_len) {
407 		syslog(LOG_ERR, "dyndns: no more buf for update section\n");
408 		return (-1);
409 	}
410 
411 	namePtr = *ptr;
412 	(void) dyndns_stuff_str(&namePtr, name);
413 	*ptr = namePtr;
414 	*ptr = dyndns_put_nshort(*ptr, type);
415 	*ptr = dyndns_put_nshort(*ptr, class);
416 	*ptr = dyndns_put_nlong(*ptr, ttl);
417 
418 	if (add_del == UPDATE_DEL && del_type == DEL_ALL) {
419 		*ptr = dyndns_put_nshort(*ptr, 0);
420 		return (0);
421 	}
422 
423 	if (forw_rev == UPDATE_FORW) {
424 		*ptr = dyndns_put_nshort(*ptr, 4);
425 		*ptr = dyndns_put_int(*ptr, inet_addr(data));	/* ip address */
426 	} else {
427 		*ptr = dyndns_put_nshort(*ptr, strlen(data)+2);
428 		namePtr = *ptr;
429 		(void) dyndns_stuff_str(&namePtr, data);	/* hostname */
430 		*ptr = namePtr;
431 	}
432 	return (0);
433 }
434 
435 /*
436  * dyndns_build_tkey
437  * Build TKEY section to establish security context for secure dynamic DNS
438  * update.  DNS header and question sections need to be build before this
439  * section.  The TKEY data are the tokens generated during security context
440  * establishment and the TKEY message is used to transmit those tokens, one
441  * at a time, to the DNS server.
442  * Parameters:
443  *   ptr       : address of pointer to buffer to store TKEY
444  *   buf_len   : buffer length
445  *   name      : key name, must be unique and same as for TSIG record
446  *   key_expire: expiration time of this key in second
447  *   data      : TKEY data
448  *   data_size : data size
449  * Returns:
450  *   ptr: address of the pointer to the next available buffer space
451  *   -1 : error
452  *    0 : success
453  */
454 static int
455 dyndns_build_tkey(char **ptr, int buf_len, char *name, int key_expire,
456 	char *data, int data_size)
457 {
458 	char *namePtr;
459 	struct timeval tp;
460 
461 	if (strlen(name)+2 + 45 + data_size > buf_len) {
462 		syslog(LOG_ERR, "dyndns: no more buf for TKEY record\n");
463 		return (-1);
464 	}
465 
466 	namePtr = *ptr;
467 	(void) dyndns_stuff_str(&namePtr, name);	/* unique global name */
468 	*ptr = namePtr;
469 	*ptr = dyndns_put_nshort(*ptr, ns_t_tkey);
470 	*ptr = dyndns_put_nshort(*ptr, ns_c_any);
471 	*ptr = dyndns_put_nlong(*ptr, 0);
472 	/* 19 + 14 + data_size + 2 */
473 	*ptr = dyndns_put_nshort(*ptr, 35 + data_size);
474 	namePtr = *ptr;
475 	(void) dyndns_stuff_str(&namePtr, "gss.microsoft.com");
476 	*ptr = namePtr;
477 	(void) gettimeofday(&tp, 0);
478 	*ptr = dyndns_put_nlong(*ptr, tp.tv_sec);	/* inception */
479 	/* expiration, 86400 */
480 	*ptr = dyndns_put_nlong(*ptr, tp.tv_sec + key_expire);
481 	*ptr = dyndns_put_nshort(*ptr, MODE_GSS_API);	/* mode: gss-api */
482 	*ptr = dyndns_put_nshort(*ptr, 0);		/* error */
483 	*ptr = dyndns_put_nshort(*ptr, data_size);	/* key size */
484 	(void) memcpy(*ptr, data, data_size);	/* key data */
485 	*ptr += data_size;
486 	*ptr = dyndns_put_nshort(*ptr, 0);	/* other */
487 	return (0);
488 }
489 
490 /*
491  * dyndns_build_tsig
492  * Build TSIG section for secure dynamic DNS update.  This routine will be
493  * called twice.  First called with TSIG_UNSIGNED, and second with TSIG_SIGNED.
494  * The TSIG data is NULL and ignored for TSIG_UNSIGNED and is the update request
495  * message encrypted for TSIG_SIGNED.  The message id must be the same id as the
496  * one in the update request before it is encrypted.
497  * Parameters:
498  *   ptr        : address of pointer to buffer to store TSIG
499  *   buf_len    : buffer length
500  *   msg_id     : message id
501  *   name       : key name, must be the same as in TKEY record
502  *   fudge_time : amount of error time allow in seconds
503  *   data       : TSIG data if TSIG_SIGNED, otherwise NULL
504  *   data_size  : size of data, otherwise 0 if data is NULL
505  *   data_signed: TSIG_SIGNED to indicate data is signed and encrypted,
506  *                otherwise TSIG_UNSIGNED
507  * Returns:
508  *   ptr: address of pointer to next available buffer space
509  *   -1 : error
510  *    0 : success
511  */
512 static int
513 dyndns_build_tsig(char **ptr, int buf_len, int msg_id, char *name,
514 	int fudge_time, char *data, int data_size, int data_signed)
515 {
516 	char *namePtr;
517 	struct timeval tp;
518 	int signtime, fudge, rec_len;
519 
520 	if (data_signed == TSIG_UNSIGNED)
521 		rec_len = strlen(name)+2 + 37;
522 	else
523 		rec_len = strlen(name)+2 + 45 + data_size;
524 
525 	if (rec_len > buf_len) {
526 		syslog(LOG_ERR, "dyndns: no more buf for TSIG record\n");
527 		return (-1);
528 	}
529 
530 	namePtr = *ptr;
531 	(void) dyndns_stuff_str(&namePtr, name);	/* unique global name */
532 	*ptr = namePtr;
533 	if (data_signed == TSIG_SIGNED)
534 		*ptr = dyndns_put_nshort(*ptr, ns_t_tsig);
535 	*ptr = dyndns_put_nshort(*ptr, ns_c_any);
536 	*ptr = dyndns_put_nlong(*ptr, 0);
537 	if (data_signed == TSIG_SIGNED) {
538 		/* 19 + 10 + data_size + 6 */
539 		*ptr = dyndns_put_nshort(*ptr, 35 + data_size);
540 	}
541 	namePtr = *ptr;
542 	(void) dyndns_stuff_str(&namePtr, "gss.microsoft.com");
543 	*ptr = namePtr;
544 	(void) gettimeofday(&tp, 0);
545 	signtime = tp.tv_sec >> 16;
546 	*ptr = dyndns_put_nlong(*ptr, signtime);	/* sign time */
547 	fudge = tp.tv_sec << 16;
548 	fudge |= fudge_time;
549 	*ptr = dyndns_put_nlong(*ptr, fudge);	/* fudge time */
550 	if (data_signed == TSIG_SIGNED) {
551 		/* signed data size */
552 		*ptr = dyndns_put_nshort(*ptr, data_size);
553 		(void) memcpy(*ptr, data, data_size);	/* signed data */
554 		*ptr += data_size;
555 		*ptr = dyndns_put_nshort(*ptr, msg_id);	/* original id */
556 	}
557 	*ptr = dyndns_put_nshort(*ptr, 0);	/* error */
558 	*ptr = dyndns_put_nshort(*ptr, 0);	/* other */
559 	return (0);
560 }
561 
562 /*
563  * dyndns_open_init_socket
564  * This routine creates a SOCK_STREAM or SOCK_DGRAM socket and initializes it
565  * by doing bind() and setting linger option to off.
566  *
567  * Parameters:
568  *   sock_type: SOCK_STREAM for TCP or SOCK_DGRAM for UDP
569  *   dest_addr: destination address in network byte order
570  *   port     : destination port number
571  * Returns:
572  *   descriptor: descriptor referencing the created socket
573  *   -1        : error
574  */
575 int
576 dyndns_open_init_socket(int sock_type, unsigned long dest_addr, int port)
577 {
578 	int s;
579 	struct sockaddr_in my_addr;
580 	struct linger l;
581 	struct sockaddr_in serv_addr;
582 
583 	if ((s = socket(AF_INET, sock_type, 0)) == -1) {
584 		syslog(LOG_ERR, "dyndns: socket err\n");
585 		return (-1);
586 	}
587 
588 	l.l_onoff = 0;
589 	if (setsockopt(s, SOL_SOCKET, SO_LINGER,
590 	    (char *)&l, sizeof (l)) == -1) {
591 		syslog(LOG_ERR, "dyndns: setsocket err\n");
592 		(void) close(s);
593 		return (-1);
594 	}
595 
596 	bzero(&my_addr, sizeof (my_addr));
597 	my_addr.sin_family = AF_INET;
598 	my_addr.sin_port = htons(0);
599 	my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
600 
601 	if (bind(s, (struct sockaddr *)&my_addr, sizeof (my_addr)) < 0) {
602 		syslog(LOG_ERR, "dyndns: client bind err\n");
603 		(void) close(s);
604 		return (-1);
605 	}
606 
607 	serv_addr.sin_family = AF_INET;
608 	serv_addr.sin_port = htons(port);
609 	serv_addr.sin_addr.s_addr = dest_addr;
610 
611 	if (connect(s, (struct sockaddr *)&serv_addr,
612 	    sizeof (struct sockaddr_in)) < 0) {
613 		syslog(LOG_ERR, "dyndns: client connect err (%s)\n",
614 		    strerror(errno));
615 		(void) close(s);
616 		return (-1);
617 	}
618 
619 	return (s);
620 }
621 
622 /*
623  * dyndns_acquire_cred
624  * This routine is used to acquire a GSS credential handle to a user's Kerberos
625  * ticket-granting ticket (TGT) stored locally on the system.  If getting a
626  * handle fails, then a new TGT will be obtained again before trying to get a
627  * handle once more.
628  * The user's password is taken from the environment variable
629  * lookup.dns.dynamic.passwd and is encrypted.
630  * Paramaters:
631  *   kinit_retry: if 0 then a new TGT can be obtained before second attempt to
632  *                get a handle to TGT if first attempt fails
633  * Returns:
634  * user_name  : name of user to get credential handle from
635  * credHandle : handle to user's credential (TGT)
636  * oid        : contains Kerberos 5 object identifier
637  * kinit_retry: 1 if a new TGT has been acquired in this routine, otherwise 0
638  *   -1         : error
639  */
640 static int
641 dyndns_acquire_cred(gss_cred_id_t *credHandle, char *user_name,
642 	gss_OID *oid, int *kinit_retry)
643 {
644 	char *p, pwd[100];
645 
646 	smb_config_rdlock();
647 	p = smb_config_getstr(SMB_CI_ADS_USER);
648 	if (p == NULL || *p == 0) {
649 		syslog(LOG_ERR, "No user configured for "
650 		    "secure dynamic DNS update.\n");
651 		smb_config_unlock();
652 		return (-1);
653 	}
654 	(void) strcpy(user_name, p);
655 
656 	p = smb_config_getstr(SMB_CI_ADS_PASSWD);
657 	if (p == NULL || *p == 0) {
658 		syslog(LOG_ERR, "No password configured for "
659 		    "secure dynamic DNS update.\n");
660 		smb_config_unlock();
661 		return (-1);
662 	}
663 	smb_config_unlock();
664 
665 	(void) strcpy(pwd, p);
666 
667 	return krb5_acquire_cred_kinit(user_name, pwd, credHandle,
668 	    oid, kinit_retry, "dyndns");
669 }
670 
671 /*
672  * dyndns_build_tkey_msg
673  * This routine is used to build the TKEY message to transmit GSS tokens
674  * during GSS security context establishment for secure DNS update.  The
675  * TKEY message format uses the DNS query message format.  The TKEY section
676  * is the answer section of the query message format.
677  * Microsoft uses a value of 86400 seconds (24 hours) for key expiration time.
678  * Parameters:
679  *   buf     : buffer to build and store TKEY message
680  *   key_name: a unique key name, this same key name must be also be used in
681  *             the TSIG message
682  *   out_tok : TKEY message data (GSS tokens)
683  * Returns:
684  *   id          : message id of this TKEY message
685  *   message size: the size of the TKEY message
686  *   -1          : error
687  */
688 static int
689 dyndns_build_tkey_msg(char *buf, char *key_name, uint16_t *id,
690 	gss_buffer_desc *out_tok)
691 {
692 	int queryReq, zoneCount, preqCount, updateCount, additionalCount;
693 	int zoneType, zoneClass;
694 	char *bufptr;
695 
696 	queryReq = REQ_QUERY;
697 	/* query section of query request */
698 	zoneCount = 1;
699 	/* answer section of query request */
700 	preqCount = 1;
701 	updateCount = 0;
702 	additionalCount = 0;
703 
704 	(void) memset(buf, 0, MAX_TCP_SIZE);
705 	bufptr = buf;
706 	*id = smb_get_next_resid();
707 
708 	/* add TCP length info that follows this field */
709 	bufptr = dyndns_put_nshort(bufptr,
710 	    26 + (strlen(key_name)+2)*2 + 35 + out_tok->length);
711 
712 	if (dyndns_build_header(&bufptr, BUFLEN_TCP(bufptr, buf), *id, queryReq,
713 	    zoneCount, preqCount, updateCount, additionalCount, 0) == -1) {
714 		return (-1);
715 	}
716 
717 	zoneType = ns_t_tkey;
718 	zoneClass = ns_c_in;
719 	if (dyndns_build_quest_zone(&bufptr, BUFLEN_TCP(bufptr, buf), key_name,
720 	    zoneType, zoneClass) == -1) {
721 		return (-1);
722 	}
723 
724 	if (dyndns_build_tkey(&bufptr, BUFLEN_TCP(bufptr, buf), key_name,
725 	    86400, out_tok->value, out_tok->length) == -1) {
726 		return (-1);
727 	}
728 
729 	return (bufptr - buf);
730 }
731 
732 /*
733  * dyndns_establish_sec_ctx
734  * This routine is used to establish a security context with the DNS server
735  * by building TKEY messages and sending them to the DNS server.  TKEY messages
736  * are also received from the DNS server for processing.   The security context
737  * establishment is done with the GSS client on the system producing a token
738  * and sending the token within the TKEY message to the GSS server on the DNS
739  * server.  The GSS server then processes the token and then send a TKEY reply
740  * message with a new token to be processed by the GSS client.  The GSS client
741  * processes the new token and then generates a new token to be sent to the
742  * GSS server.  This cycle is continued until the security establishment is
743  * done.  TCP is used to send and receive TKEY messages.
744  * If gss_init_sec_context fails then a new TGT will be acquired so that
745  * security establishment can be retry once more by the caller after getting
746  * a handle to the new TGT (credential).
747  * Parameters:
748  *   credHandle  : handle to credential
749  *   s           : socket descriptor to DNS server
750  *   key_name    : TKEY key name
751  *   dns_hostname: fully qualified DNS hostname
752  *   oid         : contains Kerberos 5 object identifier
753  *   user_name   : name of user to perform DNS update
754  *   kinit_retry : if 0 and gss_init_sec_context fails then get new TGT so
755  *                 the caller can restart doing security context establishment
756  * Returns:
757  *   gss_context    : handle to security context
758  *   kinit_retry    : 1 if a new TGT has been acquired in this routine,
759  *                    otherwise 0
760  *   do_acquire_cred: if 1 then caller will restart security context
761  *                    establishment
762  *   -1             : error
763  */
764 static int
765 dyndns_establish_sec_ctx(gss_ctx_id_t *gss_context, gss_cred_id_t credHandle,
766     int s, char *key_name, char *dns_hostname, gss_OID oid, char *user_name,
767     int *kinit_retry, int *do_acquire_cred)
768 {
769 	uint16_t id, rid, rsz;
770 	char buf[MAX_TCP_SIZE], buf2[MAX_TCP_SIZE];
771 	int ret;
772 	char *service_name, *tmpptr;
773 	int service_sz;
774 	OM_uint32 min, maj, time_rec;
775 	gss_buffer_desc service_buf, in_tok, out_tok;
776 	gss_name_t target_name;
777 	gss_buffer_desc *inputptr;
778 	int gss_flags;
779 	OM_uint32 ret_flags;
780 	int buf_sz;
781 	char *p, pwd[100];
782 
783 	smb_config_rdlock();
784 	p = smb_config_getstr(SMB_CI_ADS_PASSWD);
785 	if (p == NULL || *p == 0) {
786 		syslog(LOG_ERR, "No password configured for "
787 		    "secure dynamic DNS update.\n");
788 		smb_config_unlock();
789 		return (-1);
790 	}
791 	smb_config_unlock();
792 	(void) strcpy(pwd, p);
793 
794 	service_sz = strlen(dns_hostname) + 5;
795 	service_name = (char *)malloc(sizeof (char) * service_sz);
796 	if (service_name == NULL) {
797 		syslog(LOG_ERR, "Malloc failed for %d bytes ", service_sz);
798 		smb_config_unlock();
799 		return (-1);
800 	}
801 	(void) snprintf(service_name, service_sz, "DNS@%s", dns_hostname);
802 	service_buf.value = service_name;
803 	service_buf.length = strlen(service_name)+1;
804 	if ((maj = gss_import_name(&min, &service_buf,
805 	    (gss_OID) gss_nt_service_name,
806 	    &target_name)) != GSS_S_COMPLETE) {
807 		display_stat(maj, min);
808 		(void) gss_release_oid(&min, &oid);
809 		(void) free(service_name);
810 		return (-1);
811 	}
812 	(void) free(service_name);
813 
814 	inputptr = GSS_C_NO_BUFFER;
815 	*gss_context = GSS_C_NO_CONTEXT;
816 	gss_flags = GSS_C_MUTUAL_FLAG | GSS_C_DELEG_FLAG | GSS_C_REPLAY_FLAG |
817 	    GSS_C_SEQUENCE_FLAG | GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG;
818 	do {
819 		if (krb5_establish_sec_ctx_kinit(user_name, pwd, credHandle,
820 		    gss_context, target_name, oid, gss_flags, inputptr,
821 		    &out_tok, &ret_flags, &time_rec, kinit_retry,
822 		    do_acquire_cred, &maj, "dyndns") == -1) {
823 			(void) gss_release_oid(&min, &oid);
824 			(void) gss_release_name(&min, &target_name);
825 			return (-1);
826 		}
827 
828 		if ((maj == GSS_S_COMPLETE) &&
829 		    !(ret_flags & GSS_C_REPLAY_FLAG)) {
830 			syslog(LOG_ERR, "dyndns: No GSS_C_REPLAY_FLAG\n");
831 			if (out_tok.length > 0)
832 				(void) gss_release_buffer(&min, &out_tok);
833 			(void) gss_release_oid(&min, &oid);
834 			(void) gss_release_name(&min, &target_name);
835 			return (-1);
836 		}
837 
838 		if ((maj == GSS_S_COMPLETE) &&
839 		    !(ret_flags & GSS_C_MUTUAL_FLAG)) {
840 			syslog(LOG_ERR, "dyndns: No GSS_C_MUTUAL_FLAG\n");
841 			if (out_tok.length > 0)
842 				(void) gss_release_buffer(&min, &out_tok);
843 			(void) gss_release_oid(&min, &oid);
844 			(void) gss_release_name(&min, &target_name);
845 			return (-1);
846 		}
847 
848 		if (out_tok.length > 0) {
849 			if ((buf_sz = dyndns_build_tkey_msg(buf, key_name,
850 			    &id, &out_tok)) <= 0) {
851 				(void) gss_release_buffer(&min, &out_tok);
852 				(void) gss_release_oid(&min, &oid);
853 				(void) gss_release_name(&min, &target_name);
854 				return (-1);
855 			}
856 
857 			(void) gss_release_buffer(&min, &out_tok);
858 
859 			if (send(s, buf, buf_sz, 0) == -1) {
860 				syslog(LOG_ERR, "dyndns: TKEY send error\n");
861 				(void) gss_release_oid(&min, &oid);
862 				(void) gss_release_name(&min, &target_name);
863 				return (-1);
864 			}
865 
866 			bzero(buf2, MAX_TCP_SIZE);
867 			if (recv(s, buf2, MAX_TCP_SIZE, 0) == -1) {
868 				syslog(LOG_ERR, "dyndns: TKEY "
869 				    "reply recv error\n");
870 				(void) gss_release_oid(&min, &oid);
871 				(void) gss_release_name(&min, &target_name);
872 				return (-1);
873 			}
874 
875 			ret = buf2[5] & 0xf;	/* error field in TCP */
876 			if (ret != NOERROR) {
877 				syslog(LOG_ERR, "dyndns: Error in "
878 				    "TKEY reply: %d: ", ret);
879 				dyndns_msg_err(ret);
880 				(void) gss_release_oid(&min, &oid);
881 				(void) gss_release_name(&min, &target_name);
882 				return (-1);
883 			}
884 
885 			tmpptr = &buf2[2];
886 			(void) dyndns_get_nshort(tmpptr, &rid);
887 			if (id != rid) {
888 				(void) gss_release_oid(&min, &oid);
889 				(void) gss_release_name(&min, &target_name);
890 				return (-1);
891 			}
892 
893 			tmpptr = &buf2[59+(strlen(key_name)+2)*2];
894 			(void) dyndns_get_nshort(tmpptr, &rsz);
895 			in_tok.length = rsz;
896 
897 			/* bsd38 -> 2*7=14 */
898 			in_tok.value = &buf2[61+(strlen(key_name)+2)*2];
899 			inputptr = &in_tok;
900 		}
901 
902 	} while (maj != GSS_S_COMPLETE);
903 
904 	(void) gss_release_oid(&min, &oid);
905 	(void) gss_release_name(&min, &target_name);
906 
907 	return (0);
908 }
909 
910 /*
911  * dyndns_get_sec_context
912  * Get security context for secure dynamic DNS update.  This routine opens
913  * a TCP socket to the DNS server and calls routines to get a handle to a
914  * locally cached user's credential and establish a security context with
915  * the DNS server to perform secure dynamic DNS update.  If getting security
916  * context fails then a retry may be done after reobtaining new credential and
917  * getting a new credential handle.  If obtaining new credential has been
918  * done earlier during getting a handle to credential then there is no need to
919  * do a retry for security context.
920  * Parameters:
921  *   hostname: fully qualified hostname
922  *   dns_ip  : ip address of hostname in network byte order
923  * Returns:
924  *   gss_handle: gss credential handle
925  *   gss_context: gss security context
926  *   -1: error
927  *    0: success
928  */
929 static gss_ctx_id_t
930 dyndns_get_sec_context(const char *hostname, int dns_ip)
931 {
932 	int s;
933 	gss_cred_id_t credHandle;
934 	gss_ctx_id_t gss_context;
935 	gss_OID oid;
936 	OM_uint32 min;
937 	struct hostent *hentry;
938 	int kinit_retry, do_acquire_cred;
939 	char *key_name, dns_hostname[255], user_name[50];
940 
941 	key_name = (char *)hostname;
942 
943 	hentry = gethostbyaddr((char *)&dns_ip, 4, AF_INET);
944 	if (hentry == NULL) {
945 		syslog(LOG_ERR, "dyndns: Can't get DNS "
946 		    "hostname from DNS ip.\n");
947 		return (NULL);
948 	}
949 	(void) strcpy(dns_hostname, hentry->h_name);
950 
951 	if ((s = dyndns_open_init_socket(SOCK_STREAM, dns_ip, 53)) < 0) {
952 		return (NULL);
953 	}
954 
955 	kinit_retry = 0;
956 	do_acquire_cred = 0;
957 	acquire_cred:
958 
959 	if (dyndns_acquire_cred(&credHandle, user_name, &oid, &kinit_retry)) {
960 		(void) close(s);
961 		return (NULL);
962 	}
963 
964 	if (dyndns_establish_sec_ctx(&gss_context, credHandle, s, key_name,
965 	    dns_hostname, oid, user_name, &kinit_retry, &do_acquire_cred)) {
966 		(void) gss_release_cred(&min, &credHandle);
967 		if (do_acquire_cred) {
968 			do_acquire_cred = 0;
969 			goto acquire_cred;
970 		}
971 		(void) close(s);
972 		return (NULL);
973 	}
974 
975 	(void) close(s);
976 
977 	(void) gss_release_cred(&min, &credHandle);
978 	return (gss_context);
979 }
980 
981 /*
982  * dyndns_build_add_remove_msg
983  * This routine builds the update request message for adding and removing DNS
984  * entries which is used for non-secure and secure DNS update.
985  * This routine builds an UDP message.
986  * Parameters:
987  *   buf        : buffer to build message
988  *   update_zone: the type of zone to update, use UPDATE_FORW for forward
989  *                lookup zone, use UPDATE_REV for reverse lookup zone
990  *   hostname   : fully qualified hostname to update DNS with
991  *   ip_addr    : IP address of hostname
992  *   life_time  : cached time of this entry by others and not within DNS
993  *                database
994  *   update_type: UPDATE_ADD to add entry, UPDATE_DEL to remove entry
995  *   del_type   : DEL_ONE for deleting one entry, DEL_ALL for deleting all
996  *                entries of the same resource name.  Only valid for UPDATE_DEL.
997  *   addit_cnt  : Indicate how many record is in the additional section of
998  *                the DNS message.  A value of zero is always used with
999  *                non-secure update message. For secure update message,
1000  *                the value will be one because the signed TSIG message
1001  *                is added as the additional record of the DNS update message.
1002  *   id         : DNS message ID.  If a positive value then this ID value is
1003  *                used, otherwise the next incremented value is used
1004  *   level      : This is the domain level which we send the request to, level
1005  *                zero is the default level, it can go upto 2 in reverse zone
1006  *                and virtually to any level in forward zone.
1007  * Returns:
1008  *   buf      : buffer containing update message
1009  *   id       : DNS message ID
1010  *   int      : size of update message
1011  *   -1       : error
1012  *
1013  * This function is changed to handle dynamic DNS update retires to higher
1014  * authoritative domains.
1015  */
1016 static int
1017 dyndns_build_add_remove_msg(char *buf, int update_zone, const char *hostname,
1018 	const char *ip_addr, int life_time, int update_type, int del_type,
1019 	int addit_cnt, uint16_t *id, int level)
1020 {
1021 	int a, b, c, d;
1022 	char *bufptr;
1023 	int queryReq, zoneCount, preqCount, updateCount, additionalCount;
1024 	char *zone, *resource, *data, zone_buf[100], resrc_buf[100];
1025 	int zoneType, zoneClass, type, class, ttl;
1026 	char *p;
1027 
1028 	queryReq = REQ_UPDATE;
1029 	zoneCount = 1;
1030 	preqCount = 0;
1031 	updateCount = 1;
1032 	additionalCount = addit_cnt;
1033 
1034 	(void) memset(buf, 0, NS_PACKETSZ);
1035 	bufptr = buf;
1036 
1037 	if (*id == 0)
1038 		*id = smb_get_next_resid();
1039 
1040 	if (dyndns_build_header(&bufptr, BUFLEN_UDP(bufptr, buf), *id, queryReq,
1041 	    zoneCount, preqCount, updateCount, additionalCount, 0) == -1) {
1042 		return (-1);
1043 	}
1044 
1045 	zoneType = ns_t_soa;
1046 	zoneClass = ns_c_in;
1047 
1048 	if (update_zone == UPDATE_FORW) {
1049 		p = (char *)hostname;
1050 
1051 		/* Try higher domains according to the level requested */
1052 		do {
1053 			/* domain */
1054 			if ((zone = (char *)strchr(p, '.')) == NULL)
1055 				return (-1);
1056 			zone += 1;
1057 			p = zone;
1058 		} while (--level >= 0);
1059 		resource = (char *)hostname;
1060 		data = (char *)ip_addr;
1061 	} else {
1062 		(void) sscanf(ip_addr, "%d.%d.%d.%d", &a, &b, &c, &d);
1063 		(void) sprintf(zone_buf, "%d.%d.%d.in-addr.arpa", c, b, a);
1064 		zone = p = zone_buf;
1065 
1066 		/* Try higher domains according to the level requested */
1067 		while (--level >= 0) {
1068 			/* domain */
1069 			if ((zone = (char *)strchr(p, '.')) == NULL) {
1070 				return (-1);
1071 			}
1072 			zone += 1;
1073 			p = zone;
1074 		}
1075 
1076 		(void) sprintf(resrc_buf, "%d.%d.%d.%d.in-addr.arpa",
1077 		    d, c, b, a);
1078 		resource = resrc_buf;	/* ip info */
1079 		data = (char *)hostname;
1080 	}
1081 
1082 	if (dyndns_build_quest_zone(&bufptr, BUFLEN_UDP(bufptr, buf), zone,
1083 	    zoneType, zoneClass) == -1) {
1084 		return (-1);
1085 	}
1086 
1087 	if (update_zone == UPDATE_FORW)
1088 		type = ns_t_a;
1089 	else
1090 		type = ns_t_ptr;
1091 
1092 	if (update_type == UPDATE_ADD) {
1093 		class = ns_c_in;
1094 		ttl = life_time;
1095 	} else {
1096 		if (del_type == DEL_ONE)
1097 			class = ns_c_none;	/* remove one */
1098 		else
1099 			class = ns_c_any;	/* remove all */
1100 		ttl = 0;
1101 	}
1102 	if (dyndns_build_update(&bufptr, BUFLEN_UDP(bufptr, buf),
1103 	    resource, type, class, ttl, data, update_zone,
1104 	    update_type, del_type) == -1) {
1105 		return (-1);
1106 	}
1107 
1108 	return (bufptr - buf);
1109 }
1110 
1111 /*
1112  * dyndns_build_unsigned_tsig_msg
1113  * This routine is used to build the unsigned TSIG message for signing.  The
1114  * unsigned TSIG message contains the update request message with certain TSIG
1115  * fields included.  An error time of 300 seconds is used for fudge time.  This
1116  * is the number used by Microsoft clients.
1117  * This routine builds a UDP message.
1118  * Parameters:
1119  *   buf        : buffer to build message
1120  *   update_zone: the type of zone to update, use UPDATE_FORW for forward
1121  *                lookup zone, use UPDATE_REV for reverse lookup zone
1122  *   hostname   : fully qualified hostname to update DNS with
1123  *   ip_addr    : IP address of hostname
1124  *   life_time  : cached time of this entry by others and not within DNS
1125  *                database
1126  *   update_type: UPDATE_ADD to add entry, UPDATE_DEL to remove entry
1127  *   del_type   : DEL_ONE for deleting one entry, DEL_ALL for deleting all
1128  *                entries of the same resource name.  Only valid for UPDATE_DEL.
1129  *   key_name   : same key name used in TKEY message
1130  *   id         : DNS message ID.  If a positive value then this ID value is
1131  *                used, otherwise the next incremented value is used
1132  *   level      : This is the domain level which we send the request to, level
1133  *                zero is the default level, it can go upto 2 in reverse zone
1134  *                and virtually to any level in forward zone.
1135  * Returns:
1136  *   buf      : buffer containing update message
1137  *   id       : DNS message ID
1138  *   int      : size of update message
1139  *   -1       : error
1140  */
1141 static int
1142 dyndns_build_unsigned_tsig_msg(char *buf, int update_zone, const char *hostname,
1143 	const char *ip_addr, int life_time, int update_type, int del_type,
1144 	char *key_name, uint16_t *id, int level)
1145 {
1146 	char *bufptr;
1147 	int buf_sz;
1148 
1149 	if ((buf_sz = dyndns_build_add_remove_msg(buf, update_zone, hostname,
1150 	    ip_addr, life_time, update_type, del_type, 0, id, level)) <= 0) {
1151 		return (-1);
1152 	}
1153 
1154 	bufptr = buf + buf_sz;
1155 
1156 	if (dyndns_build_tsig(&bufptr, BUFLEN_UDP(bufptr, buf), 0,
1157 	    key_name, 300, NULL, 0, TSIG_UNSIGNED) == -1) {
1158 		return (-1);
1159 	}
1160 
1161 	return (bufptr - buf);
1162 }
1163 
1164 /*
1165  * dyndns_build_signed_tsig_msg
1166  * This routine build the signed TSIG message which contains the update
1167  * request message encrypted.  An error time of 300 seconds is used for fudge
1168  * time.  This is the number used by Microsoft clients.
1169  * This routine builds a UDP message.
1170  * Parameters:
1171  *   buf        : buffer to build message
1172  *   update_zone: the type of zone to update, use UPDATE_FORW for forward
1173  *                lookup zone, use UPDATE_REV for reverse lookup zone
1174  *   hostname   : fully qualified hostname to update DNS with
1175  *   ip_addr    : IP address of hostname
1176  *   life_time  : cached time of this entry by others and not within DNS
1177  *                database
1178  *   update_type: UPDATE_ADD to add entry, UPDATE_DEL to remove entry
1179  *   del_type   : DEL_ONE for deleting one entry, DEL_ALL for deleting all
1180  *                entries of the same resource name.  Only valid for UPDATE_DEL.
1181  *   key_name   : same key name used in TKEY message
1182  *   id         : DNS message ID.  If a positive value then this ID value is
1183  *                used, otherwise the next incremented value is used
1184  *   in_mic     : the update request message encrypted
1185  *   level      : This is the domain level which we send the request to, level
1186  *                zero is the default level, it can go upto 2 in reverse zone
1187  *                and virtually to any level in forward zone.
1188  *
1189  * Returns:
1190  *   buf      : buffer containing update message
1191  *   id       : DNS message ID
1192  *   int      : size of update message
1193  *   -1       : error
1194  */
1195 static int
1196 dyndns_build_signed_tsig_msg(char *buf, int update_zone, const char *hostname,
1197 	const char *ip_addr, int life_time, int update_type, int del_type,
1198 	char *key_name, uint16_t *id, gss_buffer_desc *in_mic, int level)
1199 {
1200 	char *bufptr;
1201 	int buf_sz;
1202 
1203 	if ((buf_sz = dyndns_build_add_remove_msg(buf, update_zone, hostname,
1204 	    ip_addr, life_time, update_type, del_type, 1, id, level)) <= 0) {
1205 		return (-1);
1206 	}
1207 
1208 	bufptr = buf + buf_sz;
1209 
1210 	if (dyndns_build_tsig(&bufptr, BUFLEN_UDP(bufptr, buf),
1211 	    *id, key_name, 300, in_mic->value,
1212 	    in_mic->length, TSIG_SIGNED) == -1) {
1213 		return (-1);
1214 	}
1215 
1216 	return (bufptr - buf);
1217 }
1218 
1219 /*
1220  * dyndns_udp_send_recv
1221  * This routine sends and receives UDP DNS request and reply messages.  Time
1222  * out value and retry count is indicated by two environment variables:
1223  * lookup_dns_retry_cnt
1224  * lookup_dns_retry_sec
1225  * If either of these two variables are undefined or their value exceed the
1226  * value of 10 then a default value of 3 retry and/or a default value of 3
1227  * secs are used.
1228  *
1229  * Pre-condition: Caller must call dyndns_open_init_socket() before calling
1230  * this function.
1231  *
1232  * Parameters:
1233  *   s        : socket descriptor
1234  *   buf      : buffer containing data to send
1235  *   buf_sz   : size of data to send
1236  * Returns:
1237  *   -1     : error
1238  *   rec_buf: reply dat
1239  *    0     : success
1240  */
1241 int
1242 dyndns_udp_send_recv(int s, char *buf, int buf_sz, char *rec_buf)
1243 {
1244 	int i, retval, addr_len, max_retries;
1245 	struct timeval tv, timeout;
1246 	fd_set rfds;
1247 	struct sockaddr_in from_addr;
1248 	char *p;
1249 
1250 	smb_config_rdlock();
1251 	p = smb_config_getstr(SMB_CI_DYNDNS_RETRY_COUNT);
1252 	if (p == NULL || *p == 0) {
1253 		max_retries = 3;
1254 	} else {
1255 		max_retries = atoi(p);
1256 		if (max_retries < 1 || max_retries > 10)
1257 			max_retries = 3;
1258 	}
1259 
1260 	p = smb_config_getstr(SMB_CI_DYNDNS_RETRY_SEC);
1261 	timeout.tv_usec = 0;
1262 	if (p == NULL || *p == 0) {
1263 		timeout.tv_sec = 3;
1264 	} else {
1265 		timeout.tv_sec = atoi(p);
1266 		if (timeout.tv_sec < 1 || timeout.tv_sec > 10)
1267 			timeout.tv_sec = 3;
1268 	}
1269 	smb_config_unlock();
1270 
1271 	for (i = 0; i < max_retries + 1; i++) {
1272 		if (send(s, buf, buf_sz, 0) == -1) {
1273 			syslog(LOG_ERR, "dyndns: UDP send error (%s)\n",
1274 			    strerror(errno));
1275 			return (-1);
1276 		}
1277 
1278 		FD_ZERO(&rfds);
1279 		FD_SET(s, &rfds);
1280 
1281 		tv = timeout;
1282 
1283 		retval = select(s+1, &rfds, NULL, NULL, &tv);
1284 
1285 		if (retval == -1) {
1286 			return (-1);
1287 		} else if (retval > 0) {
1288 			bzero(rec_buf, NS_PACKETSZ);
1289 			/* required by recvfrom */
1290 			addr_len = sizeof (struct sockaddr_in);
1291 			if (recvfrom(s, rec_buf, NS_PACKETSZ, 0,
1292 			    (struct sockaddr *)&from_addr, &addr_len) == -1) {
1293 				syslog(LOG_ERR, "dyndns: UDP recv err\n");
1294 				return (-1);
1295 			}
1296 			break;
1297 		}
1298 	}
1299 
1300 	if (i == (max_retries + 1)) { /* did not receive anything */
1301 		syslog(LOG_ERR, "dyndns: max retries for UDP recv reached\n");
1302 		return (-1);
1303 	}
1304 
1305 	return (0);
1306 }
1307 
1308 /*
1309  * dyndns_sec_add_remove_entry
1310  * Perform secure dynamic DNS update after getting security context.
1311  * This routine opens a UDP socket to the DNS sever, gets the security context,
1312  * builds the unsigned TSIG message and signed TSIG message.  The signed TSIG
1313  * message containing the encrypted update request message is sent to the DNS
1314  * server.  The response is received and check for error.  If there is no
1315  * error then credential handle and security context are released and the local
1316  * NSS cached is purged.
1317  * Parameters:
1318  *   update_zone : UPDATE_FORW for forward zone, UPDATE_REV for reverse zone
1319  *   hostname    : fully qualified hostname
1320  *   ip_addr     : ip address of hostname in string format
1321  *   life_time   : cached time of this entry by others and not within DNS
1322  *                 database
1323  *   max_retries : maximum retries for sending DNS update request
1324  *   recv_timeout: receive timeout
1325  *   update_type : UPDATE_ADD for adding entry, UPDATE_DEL for removing entry
1326  *   del_type    : DEL_ONE for deleting one entry, DEL_ALL for deleting all
1327  *                 entries of the same resource name.  Only valid for UPDATE_DEL
1328  *   dns_str     : DNS IP address in string format
1329  * Returns:
1330  *   -1: error
1331  *    0: success
1332  *
1333  * This function is enhanced to handle the case of NOTAUTH error when DNS server
1334  * is not authoritative for specified zone. In this case we need to resend the
1335  * same request to the higher authoritative domains.
1336  * This is true for both secure and unsecure dynamic DNS updates.
1337  */
1338 static int
1339 dyndns_sec_add_remove_entry(int update_zone, const char *hostname,
1340     const char *ip_addr, int life_time, int update_type, int del_type,
1341     char *dns_str)
1342 {
1343 	int s2;
1344 	uint16_t id, rid;
1345 	char buf[NS_PACKETSZ], buf2[NS_PACKETSZ];
1346 	int ret;
1347 	OM_uint32 min, maj;
1348 	gss_buffer_desc in_mic, out_mic;
1349 	gss_ctx_id_t gss_context;
1350 	int dns_ip;
1351 	char *key_name;
1352 	int buf_sz;
1353 	int level = 0;
1354 
1355 	assert(dns_str);
1356 	assert(*dns_str);
1357 
1358 	dns_ip = inet_addr(dns_str);
1359 
1360 sec_retry_higher:
1361 
1362 	if ((gss_context = dyndns_get_sec_context(hostname,
1363 	    dns_ip)) == NULL) {
1364 		return (-1);
1365 	}
1366 
1367 	key_name = (char *)hostname;
1368 
1369 	if ((s2 = dyndns_open_init_socket(SOCK_DGRAM, dns_ip, 53)) < 0) {
1370 		(void) gss_delete_sec_context(&min, &gss_context, NULL);
1371 		return (-1);
1372 	}
1373 
1374 	id = 0;
1375 	if ((buf_sz = dyndns_build_unsigned_tsig_msg(buf, update_zone, hostname,
1376 	    ip_addr, life_time, update_type, del_type,
1377 	    key_name, &id, level)) <= 0) {
1378 		(void) close(s2);
1379 		(void) gss_delete_sec_context(&min, &gss_context, NULL);
1380 		return (-1);
1381 	}
1382 
1383 	in_mic.length = buf_sz;
1384 	in_mic.value = buf;
1385 
1386 	/* sign update message */
1387 	if ((maj = gss_get_mic(&min, gss_context, 0, &in_mic, &out_mic)) !=
1388 	    GSS_S_COMPLETE) {
1389 		display_stat(maj, min);
1390 		(void) close(s2);
1391 		(void) gss_delete_sec_context(&min, &gss_context, NULL);
1392 		return (-1);
1393 	}
1394 
1395 	if ((buf_sz = dyndns_build_signed_tsig_msg(buf, update_zone, hostname,
1396 	    ip_addr, life_time, update_type, del_type, key_name, &id,
1397 	    &out_mic, level)) <= 0) {
1398 		(void) close(s2);
1399 		(void) gss_release_buffer(&min, &out_mic);
1400 		(void) gss_delete_sec_context(&min, &gss_context, NULL);
1401 		return (-1);
1402 	}
1403 
1404 	(void) gss_release_buffer(&min, &out_mic);
1405 
1406 	if (dyndns_udp_send_recv(s2, buf, buf_sz, buf2)) {
1407 		(void) close(s2);
1408 		(void) gss_delete_sec_context(&min, &gss_context, NULL);
1409 		return (-1);
1410 	}
1411 
1412 	(void) close(s2);
1413 
1414 	(void) gss_delete_sec_context(&min, &gss_context, NULL);
1415 
1416 	ret = buf2[3] & 0xf;	/* error field in UDP */
1417 
1418 	/*
1419 	 * If it is a NOTAUTH error we should retry with higher domains
1420 	 * until we get a successful reply or the maximum retries is met.
1421 	 */
1422 	if (ret == NOTAUTH && level++ < MAX_AUTH_RETRIES)
1423 		goto sec_retry_higher;
1424 
1425 	/* check here for update request is successful */
1426 	if (ret != NOERROR) {
1427 		syslog(LOG_ERR, "dyndns: Error in TSIG reply: %d: ", ret);
1428 		dyndns_msg_err(ret);
1429 		return (-1);
1430 	}
1431 
1432 	(void) dyndns_get_nshort(buf2, &rid);
1433 	if (id != rid)
1434 		return (-1);
1435 
1436 	return (0);
1437 }
1438 
1439 /*
1440  * dyndns_seach_entry
1441  * Query DNS server for entry.  This routine can indicate if an entry exist
1442  * or not during forward or reverse lookup.  Also can indicate if the data
1443  * of the entry matched.  For example, for forward lookup, the entry is
1444  * searched using the hostname and the data is the IP address.  For reverse
1445  * lookup, the entry is searched using the IP address and the data is the
1446  * hostname.
1447  * Parameters:
1448  *   update_zone: UPDATE_FORW for forward zone, UPDATE_REV for reverse zone
1449  *   hostname   : fully qualified hostname
1450  *   ip_addr    : ip address of hostname in string format
1451  *   update_type: UPDATE_ADD for adding entry, UPDATE_DEL for removing entry
1452  * Returns:
1453  *   time_out: no use
1454  *   is_match: is 1 for found matching entry, otherwise 0
1455  *   1       : an entry exist but not necessarily match
1456  *   0       : an entry does not exist
1457  */
1458 /*ARGSUSED*/
1459 static int
1460 dyndns_search_entry(int update_zone, const char *hostname, const char *ip_addr,
1461     int update_type, struct timeval *time_out, int *is_match)
1462 {
1463 	struct hostent *hentry;
1464 	struct in_addr in;
1465 	in_addr_t ip;
1466 	int i;
1467 
1468 	*is_match = 0;
1469 	if (update_zone == UPDATE_FORW) {
1470 		hentry = gethostbyname(hostname);
1471 		if (hentry) {
1472 			ip = inet_addr(ip_addr);
1473 			for (i = 0; hentry->h_addr_list[i]; i++) {
1474 				(void) memcpy(&in.s_addr,
1475 				    hentry->h_addr_list[i], sizeof (in.s_addr));
1476 				if (ip == in.s_addr) {
1477 					*is_match = 1;
1478 					break;
1479 				}
1480 			}
1481 			return (1);
1482 		}
1483 	} else {
1484 		int dns_ip = inet_addr(ip_addr);
1485 		hentry = gethostbyaddr((char *)&dns_ip, 4, AF_INET);
1486 		if (hentry) {
1487 			if (strncasecmp(hentry->h_name, hostname,
1488 			    strlen(hostname)) == 0) {
1489 				*is_match = 1;
1490 			}
1491 			return (1);
1492 		}
1493 	}
1494 
1495 	/* entry does not exist */
1496 	return (0);
1497 }
1498 
1499 /*
1500  * dyndns_add_remove_entry
1501  * Perform non-secure dynamic DNS update.  If fail then tries secure update.
1502  * This routine opens a UDP socket to the DNS sever, build the update request
1503  * message, and sends the message to the DNS server.  The response is received
1504  * and check for error.  If there is no error then the local NSS cached is
1505  * purged.  DNS may be used to check to see if an entry already exist before
1506  * adding or to see if an entry does exist before removing it.  Adding
1507  * duplicate entries or removing non-existing entries does not cause any
1508  * problems.  DNS is not check when doing a delete all.
1509  * Parameters:
1510  *   update_zone: UPDATE_FORW for forward zone, UPDATE_REV for reverse zone
1511  *   hostname   : fully qualified hostname
1512  *   ip_addr    : ip address of hostname in string format
1513  *   life_time  : cached time of this entry by others and not within DNS
1514  *                database
1515  *   update_type: UPDATE_ADD to add entry, UPDATE_DEL to remove entry
1516  *   do_check   : DNS_CHECK to check first in DNS, DNS_NOCHECK for no DNS
1517  *                checking before update
1518  *   del_type   : DEL_ONE for deleting one entry, DEL_ALL for deleting all
1519  *                entries of the same resource name.  Only valid for UPDATE_DEL.
1520  *   dns_str    : DNS IP address in string format
1521  * Returns:
1522  *   -1: error
1523  *    0: success
1524  *
1525  * This function is enhanced to handle the case of NOTAUTH error when DNS server
1526  * is not authoritative for specified zone. In this case we need to resend the
1527  * same request to the higher authoritative domains.
1528  * This is true for both secure and unsecure dynamic DNS updates.
1529  */
1530 static int
1531 dyndns_add_remove_entry(int update_zone, const char *hostname,
1532     const char *ip_addr, int life_time, int update_type,
1533     int do_check, int del_type, char *dns_str)
1534 {
1535 	int s;
1536 	uint16_t id, rid;
1537 	char buf[NS_PACKETSZ], buf2[NS_PACKETSZ];
1538 	int ret, dns_ip;
1539 	int is_exist, is_match;
1540 	struct timeval timeout;
1541 	int buf_sz;
1542 	int level = 0;
1543 
1544 	assert(dns_str);
1545 	assert(*dns_str);
1546 
1547 	dns_ip = inet_addr(dns_str);
1548 
1549 	if (do_check == DNS_CHECK && del_type != DEL_ALL) {
1550 		is_exist = dyndns_search_entry(update_zone, hostname, ip_addr,
1551 		    update_type, &timeout, &is_match);
1552 
1553 		if (update_type == UPDATE_ADD && is_exist && is_match) {
1554 			return (0);
1555 		} else if (update_type == UPDATE_DEL && !is_exist) {
1556 			return (0);
1557 		}
1558 	}
1559 
1560 retry_higher:
1561 	if ((s = dyndns_open_init_socket(SOCK_DGRAM, dns_ip, 53)) < 0) {
1562 		return (-1);
1563 	}
1564 
1565 	id = 0;
1566 	if ((buf_sz = dyndns_build_add_remove_msg(buf, update_zone, hostname,
1567 	    ip_addr, life_time, update_type, del_type, 0, &id, level)) <= 0) {
1568 		(void) close(s);
1569 		return (-1);
1570 	}
1571 
1572 	if (dyndns_udp_send_recv(s, buf, buf_sz, buf2)) {
1573 		(void) close(s);
1574 		return (-1);
1575 	}
1576 
1577 	(void) close(s);
1578 
1579 	ret = buf2[3] & 0xf;	/* error field in UDP */
1580 
1581 	/*
1582 	 * If it is a NOTAUTH error we should retry with higher domains
1583 	 * until we get a successful reply
1584 	 */
1585 	if (ret == NOTAUTH && level++ < MAX_AUTH_RETRIES)
1586 		goto retry_higher;
1587 
1588 	/* check here for update request is successful */
1589 	if (ret == NOERROR) {
1590 		(void) dyndns_get_nshort(buf2, &rid);
1591 		if (id != rid)
1592 			return (-1);
1593 		return (0);
1594 	}
1595 
1596 	if (ret == NOTIMP) {
1597 		syslog(LOG_ERR, "dyndns: DNS does not "
1598 		    "support dynamic update\n");
1599 		return (-1);
1600 	} else if (ret == NOTAUTH) {
1601 		syslog(LOG_ERR, "dyndns: DNS is not authoritative for "
1602 		    "zone name in zone section\n");
1603 		return (-1);
1604 	}
1605 
1606 	ret = dyndns_sec_add_remove_entry(update_zone, hostname,
1607 	    ip_addr, life_time, update_type, del_type, dns_str);
1608 
1609 	return (ret);
1610 }
1611 
1612 /*
1613  * dyndns_add_entry
1614  * Main routine to add an entry into DNS.  The attempt will be made on the
1615  * the servers returned by smb_get_nameserver().  Upon a successful
1616  * attempt on any one of the server, the function will exit with 0.
1617  * Otherwise, -1 is retuned to indicate the update attempt on all the
1618  * nameservers has failed.
1619  *
1620  * Parameters:
1621  *   update_zone: the type of zone to update, use UPDATE_FORW for forward
1622  *                lookup zone, use UPDATE_REV for reverse lookup zone
1623  *   hostname   : fully qualified hostname
1624  *   ip_addr    : ip address of hostname in string format
1625  *   life_time  : cached time of this entry by others and not within DNS
1626  *                database
1627  * Returns:
1628  *   -1: error
1629  *    0: success
1630  */
1631 static int
1632 dyndns_add_entry(int update_zone, const char *hostname, const char *ip_addr,
1633     int life_time)
1634 {
1635 	char *dns_str;
1636 	struct in_addr ns_list[MAXNS];
1637 	int i, cnt;
1638 	int addr, rc = 0;
1639 
1640 	if (hostname == NULL || ip_addr == NULL) {
1641 		return (-1);
1642 	}
1643 
1644 	addr = (int)inet_addr(ip_addr);
1645 	if ((addr == -1) || (addr == 0)) {
1646 		return (-1);
1647 	}
1648 
1649 	cnt = smb_get_nameservers(ns_list, MAXNS);
1650 
1651 	for (i = 0; i < cnt; i++) {
1652 		dns_str = inet_ntoa(ns_list[i]);
1653 		if ((dns_str == NULL) ||
1654 		    (strcmp(dns_str, "0.0.0.0") == 0)) {
1655 			continue;
1656 		}
1657 
1658 		if (update_zone == UPDATE_FORW) {
1659 			syslog(LOG_DEBUG, "Dynamic update on forward lookup "
1660 			    "zone for %s (%s)...\n", hostname, ip_addr);
1661 		} else {
1662 			syslog(LOG_DEBUG, "Dynamic update on reverse lookup "
1663 			    "zone for %s (%s)...\n", hostname, ip_addr);
1664 		}
1665 		if (dyndns_add_remove_entry(update_zone, hostname,
1666 		    ip_addr, life_time,
1667 		    UPDATE_ADD, DNS_NOCHECK, DEL_NONE, dns_str) != -1) {
1668 			rc = 1;
1669 			break;
1670 		}
1671 	}
1672 
1673 	return (rc ? 0 : -1);
1674 }
1675 
1676 /*
1677  * dyndns_remove_entry
1678  * Main routine to remove an entry or all entries of the same resource name
1679  * from DNS.  The update attempt will be made on the primary DNS server.  If
1680  * there is a failure then another attempt will be made on the secondary DNS
1681  * server.
1682  * Parameters:
1683  *   update_zone: the type of zone to update, use UPDATE_FORW for forward
1684  *                lookup zone, use UPDATE_REV for reverse lookup zone
1685  *   hostname   : fully qualified hostname
1686  *   ip_addr    : ip address of hostname in string format
1687  *   del_type   : DEL_ONE for deleting one entry, DEL_ALL for deleting all
1688  *                entries of the same resource name.  Only valid for UPDATE_DEL
1689  * Returns:
1690  *   -1: error
1691  *    0: success
1692  */
1693 static int
1694 dyndns_remove_entry(int update_zone, const char *hostname, const char *ip_addr,
1695 	int del_type)
1696 {
1697 	char *dns_str;
1698 	struct in_addr ns_list[MAXNS];
1699 	int i, cnt, scnt;
1700 	int addr;
1701 
1702 	if ((hostname == NULL || ip_addr == NULL)) {
1703 		return (-1);
1704 	}
1705 
1706 	addr = (int)inet_addr(ip_addr);
1707 	if ((addr == -1) || (addr == 0)) {
1708 		return (-1);
1709 	}
1710 
1711 	cnt = smb_get_nameservers(ns_list, MAXNS);
1712 	scnt = 0;
1713 
1714 	for (i = 0; i < cnt; i++) {
1715 		dns_str = inet_ntoa(ns_list[i]);
1716 		if ((dns_str == NULL) ||
1717 		    (strcmp(dns_str, "0.0.0.0") == 0)) {
1718 			continue;
1719 		}
1720 
1721 		if (update_zone == UPDATE_FORW) {
1722 			if (del_type == DEL_ONE) {
1723 				syslog(LOG_DEBUG, "Dynamic update "
1724 				    "on forward lookup "
1725 				    "zone for %s (%s)...\n", hostname, ip_addr);
1726 			} else {
1727 				syslog(LOG_DEBUG, "Removing all "
1728 				    "entries of %s "
1729 				    "in forward lookup zone...\n", hostname);
1730 			}
1731 		} else {
1732 			if (del_type == DEL_ONE) {
1733 				syslog(LOG_DEBUG, "Dynamic update "
1734 				    "on reverse lookup "
1735 				    "zone for %s (%s)...\n", hostname, ip_addr);
1736 			} else {
1737 				syslog(LOG_DEBUG, "Removing all "
1738 				    "entries of %s "
1739 				    "in reverse lookup zone...\n", ip_addr);
1740 			}
1741 		}
1742 		if (dyndns_add_remove_entry(update_zone, hostname, ip_addr, 0,
1743 		    UPDATE_DEL, DNS_NOCHECK, del_type, dns_str) != -1) {
1744 			scnt++;
1745 			break;
1746 		}
1747 	}
1748 	if (scnt)
1749 		return (0);
1750 	return (-1);
1751 }
1752 
1753 /*
1754  * dyndns_update
1755  * Perform dynamic update on both forward and reverse lookup zone using
1756  * current hostname and IP addresses.  Before updating DNS, existing host
1757  * entries with the same hostname in the forward lookup zone are removed
1758  * and existing pointer entries with the same IP addresses in the reverse
1759  * lookup zone are removed.  After DNS update, host entries for current
1760  * hostname will show current IP addresses and pointer entries for current
1761  * IP addresses will show current hostname.
1762  * Parameters:
1763  *   None
1764  * Returns:
1765  *   -1: some dynamic DNS updates errors
1766  *    0: successful
1767  */
1768 int
1769 dyndns_update(void)
1770 {
1771 	int i, forw_update_ok, error;
1772 	char fqdn[MAXHOSTNAMELEN];
1773 	char *my_ip;
1774 	int nc_cnt;
1775 	struct in_addr addr;
1776 	int rc;
1777 
1778 	if (!dyndns_enabled())
1779 		return (-1);
1780 
1781 	if (smb_getfqhostname(fqdn, MAXHOSTNAMELEN) != 0)
1782 		return (-1);
1783 
1784 	nc_cnt = smb_nic_get_num();
1785 
1786 	error = 0;
1787 	forw_update_ok = 0;
1788 
1789 	/*
1790 	 * Dummy IP is okay since we are removing all using the hostname.
1791 	 */
1792 	if (dyndns_remove_entry(UPDATE_FORW, fqdn, "1.1.1.1", DEL_ALL) == 0) {
1793 		forw_update_ok = 1;
1794 	} else {
1795 		error++;
1796 	}
1797 
1798 	for (i = 0; i < nc_cnt; i++) {
1799 		net_cfg_t cfg;
1800 		if (smb_nic_get_byind(i, &cfg) == NULL)
1801 			break;
1802 		addr.s_addr = cfg.ip;
1803 		if (addr.s_addr == 0)
1804 			continue;
1805 		if (smb_nic_status(cfg.ifname, IFF_STANDBY) ||
1806 		    smb_nic_status(cfg.ifname, IFF_PRIVATE))
1807 			continue;
1808 
1809 		my_ip = (char *)strdup(inet_ntoa(addr));
1810 		if (my_ip == NULL) {
1811 			error++;
1812 			continue;
1813 		}
1814 
1815 		if (forw_update_ok) {
1816 			rc = dyndns_add_entry(UPDATE_FORW, fqdn, my_ip,
1817 			    DDNS_TTL);
1818 
1819 			if (rc == -1)
1820 				error++;
1821 		}
1822 
1823 		rc = dyndns_remove_entry(UPDATE_REV, fqdn, my_ip, DEL_ALL);
1824 		if (rc == 0) {
1825 			rc = dyndns_add_entry(UPDATE_REV, fqdn, my_ip,
1826 			    DDNS_TTL);
1827 		}
1828 
1829 		if (rc == -1)
1830 			error++;
1831 
1832 		(void) free(my_ip);
1833 	}
1834 
1835 	return ((error == 0) ? 0 : -1);
1836 }
1837 
1838 /*
1839  * dyndns_clear_rev_zone
1840  * Clear the rev zone records. Must be called to clear the OLD if list
1841  * of down records prior to updating the list with new information.
1842  *
1843  * Parameters:
1844  *   None
1845  * Returns:
1846  *   -1: some dynamic DNS updates errors
1847  *    0: successful
1848  */
1849 int
1850 dyndns_clear_rev_zone(void)
1851 {
1852 	int i, error;
1853 	char fqdn[MAXHOSTNAMELEN];
1854 	char *my_ip;
1855 	int nc_cnt;
1856 	struct in_addr addr;
1857 	int rc;
1858 
1859 	if (!dyndns_enabled())
1860 		return (-1);
1861 
1862 	if (smb_getfqhostname(fqdn, MAXHOSTNAMELEN) != 0)
1863 		return (-1);
1864 
1865 	nc_cnt = smb_nic_get_num();
1866 
1867 	error = 0;
1868 
1869 	for (i = 0; i < nc_cnt; i++) {
1870 		net_cfg_t cfg;
1871 		if (smb_nic_get_byind(i, &cfg) == NULL)
1872 			break;
1873 		addr.s_addr = cfg.ip;
1874 		if (addr.s_addr == 0)
1875 			continue;
1876 		if (smb_nic_status(cfg.ifname, IFF_STANDBY) ||
1877 		    smb_nic_status(cfg.ifname, IFF_PRIVATE))
1878 			continue;
1879 
1880 		my_ip = (char *)strdup(inet_ntoa(addr));
1881 		if (my_ip == NULL) {
1882 			error++;
1883 			continue;
1884 		}
1885 
1886 		rc = dyndns_remove_entry(UPDATE_REV, fqdn, my_ip, DEL_ALL);
1887 		if (rc != 0)
1888 			error++;
1889 
1890 		(void) free(my_ip);
1891 	}
1892 
1893 	return ((error == 0) ? 0 : -1);
1894 }
1895