xref: /illumos-gate/usr/src/lib/libresolv/res_mkquery.c (revision 789d94c2889bedf502063bc22addcabfa798a438)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 
23 /*
24  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
25  * Use is subject to license terms.
26  */
27 
28 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
29 /*	  All Rights Reserved  	*/
30 
31 /*
32  * University Copyright- Copyright (c) 1982, 1986, 1988
33  * The Regents of the University of California
34  * All Rights Reserved
35  *
36  * University Acknowledgment- Portions of this document are derived from
37  * software developed by the University of California, Berkeley, and its
38  * contributors.
39  */
40 
41 #pragma ident	"%Z%%M%	%I%	%E% SMI"
42 
43 #include "c_synonyms.h"
44 #include <stdio.h>
45 #include <sys/types.h>
46 #include <sys/socket.h>
47 #include <sys/stat.h>
48 #include <netinet/in.h>
49 #include <arpa/nameser.h>
50 #include <resolv.h>
51 #include <errno.h>
52 #include <netdb.h>
53 
54 /*
55  * Kludge to time out quickly if there is no /etc/resolv.conf
56  * and a TCP connection to the local DNS server fails.
57  *
58  * Moved function from res_send.c to res_mkquery.c.  This
59  * solves a long timeout problem with nslookup.
60  *
61  * __areweinnamed is needed because there is a possibility that the
62  * user might do bad things to resolv.conf and cause in.named to call
63  * _confcheck and deadlock the server.
64  */
65 
66 int __areweinnamed()
67 {
68 	return (0);
69 }
70 
71 static int _confcheck()
72 {
73 	int ns;
74 	struct stat rc_stat;
75 	struct sockaddr_in ns_sin;
76 
77 
78 	/* First, we check to see if /etc/resolv.conf exists.
79 	 * If it doesn't, then localhost is mostlikely to be
80 	 * the nameserver.
81 	 */
82 	if (stat(_PATH_RESCONF, &rc_stat) == -1 && errno == ENOENT) {
83 
84 		/* Next, we check to see if _res.nsaddr is set to loopback.
85 		 * If it isn't, it has been altered by the application
86 		 * explicitly and we then want to bail with success.
87 		 */
88 		if (__areweinnamed())
89 			return (0);
90 
91 		if (_res.nsaddr.sin_addr.S_un.S_addr == htonl(INADDR_LOOPBACK)) {
92 
93 			/* Lastly, we try to connect to the TCP port of the
94 			 * nameserver.  If this fails, then we know that
95 			 * DNS is misconfigured and we can quickly exit.
96 			 */
97 			ns = socket(AF_INET, SOCK_STREAM, 0);
98 			IN_SET_LOOPBACK_ADDR(&ns_sin);
99 			ns_sin.sin_port = htons(NAMESERVER_PORT);
100 			if (connect(ns, (struct sockaddr *) &ns_sin,
101 				    sizeof ns_sin) == -1) {
102 				close(ns);
103 				return(-1);
104 			}
105 			else {
106 				close(ns);
107 				return(0);
108 			}
109 		}
110 
111 		return(0);
112 	}
113 
114 	return (0);
115 }
116 
117 /*
118  * Form all types of queries.
119  * Returns the size of the result or -1.
120  */
121 int
122 res_mkquery(op, dname, class, type, data, datalen, newrr, buf, buflen)
123 	int op;			/* opcode of query */
124 	char *dname;		/* domain name */
125 	int class, type;	/* class and type of query */
126 	char *data;		/* resource record data */
127 	int datalen;		/* length of data */
128 	struct rrec *newrr;	/* new rr for modify or append */
129 	char *buf;		/* buffer to put query */
130 	int buflen;		/* size of buffer */
131 {
132 	register HEADER *hp;
133 	register char *cp;
134 	register int n;
135 	char *dnptrs[10], **dpp, **lastdnptr;
136 
137 #ifdef DEBUG
138 	if (_res.options & RES_DEBUG)
139 		printf("res_mkquery(%d, %s, %d, %d)\n", op, dname, class, type);
140 #endif /* DEBUG */
141 
142 	/*
143 	 * Check to see if we can bailout quickly.
144 	 * Also rerun res_init if we failed in the past.
145 	 */
146 
147 	if ((_res.options & RES_INIT) == 0 && res_init() == -1) {
148 		h_errno = NO_RECOVERY;
149 		return(-1);
150 	}
151 
152 	if (_confcheck() == -1) {
153 		_res.options &= ~RES_INIT;
154 		h_errno = NO_RECOVERY;
155 		return(-1);
156 	}
157 
158 	/*
159 	 * Initialize header fields.
160 	 */
161 	if ((buf == NULL) || (buflen < sizeof (HEADER)))
162 		return (-1);
163 #ifdef SYSV
164 	memset(buf, 0, sizeof (HEADER));
165 #else
166 	bzero(buf, sizeof (HEADER));
167 #endif
168 	hp = (HEADER *) buf;
169 	hp->id = htons(++_res.id);
170 	hp->opcode = op;
171 	hp->pr = (_res.options & RES_PRIMARY) != 0;
172 	hp->rd = (_res.options & RES_RECURSE) != 0;
173 	hp->rcode = NOERROR;
174 	cp = buf + sizeof (HEADER);
175 	buflen -= sizeof (HEADER);
176 	dpp = dnptrs;
177 	*dpp++ = buf;
178 	*dpp++ = NULL;
179 	lastdnptr = dnptrs + sizeof (dnptrs) / sizeof (dnptrs[0]);
180 	/*
181 	 * perform opcode specific processing
182 	 */
183 	switch (op) {
184 	case QUERY:
185 		if ((buflen -= QFIXEDSZ) < 0)
186 			return (-1);
187 		if ((n = dn_comp(dname, cp, buflen, dnptrs, lastdnptr)) < 0)
188 			return (-1);
189 		cp += n;
190 		buflen -= n;
191 		putshort(type, cp);
192 		cp += sizeof (u_short);
193 		putshort(class, cp);
194 		cp += sizeof (u_short);
195 		hp->qdcount = htons(1);
196 		if (op == QUERY || data == NULL)
197 			break;
198 		/*
199 		 * Make an additional record for completion domain.
200 		 */
201 		buflen -= RRFIXEDSZ;
202 		if ((n = dn_comp(data, cp, buflen, dnptrs, lastdnptr)) < 0)
203 			return (-1);
204 		cp += n;
205 		buflen -= n;
206 		putshort(T_NULL, cp);
207 		cp += sizeof (u_short);
208 		putshort(class, cp);
209 		cp += sizeof (u_short);
210 		putlong(0, cp);
211 		cp += sizeof (u_long);
212 		putshort(0, cp);
213 		cp += sizeof (u_short);
214 		hp->arcount = htons(1);
215 		break;
216 
217 	case IQUERY:
218 		/*
219 		 * Initialize answer section
220 		 */
221 		if (buflen < 1 + RRFIXEDSZ + datalen)
222 			return (-1);
223 		*cp++ = '\0';	/* no domain name */
224 		putshort(type, cp);
225 		cp += sizeof (u_short);
226 		putshort(class, cp);
227 		cp += sizeof (u_short);
228 		putlong(0, cp);
229 		cp += sizeof (u_long);
230 		putshort(datalen, cp);
231 		cp += sizeof (u_short);
232 		if (datalen) {
233 #ifdef SYSV
234 			memcpy((void *)cp, (void *)data, datalen);
235 #else
236 			bcopy(data, cp, datalen);
237 #endif
238 			cp += datalen;
239 		}
240 		hp->ancount = htons(1);
241 		break;
242 
243 #ifdef ALLOW_UPDATES
244 	/*
245 	 * For UPDATEM/UPDATEMA, do UPDATED/UPDATEDA followed by UPDATEA
246 	 * (Record to be modified is followed by its replacement in msg.)
247 	 */
248 	case UPDATEM:
249 	case UPDATEMA:
250 
251 	case UPDATED:
252 		/*
253 		 * The res code for UPDATED and UPDATEDA is the same; user
254 		 * calls them differently: specifies data for UPDATED; server
255 		 * ignores data if specified for UPDATEDA.
256 		 */
257 	case UPDATEDA:
258 		buflen -= RRFIXEDSZ + datalen;
259 		if ((n = dn_comp(dname, cp, buflen, dnptrs, lastdnptr)) < 0)
260 			return (-1);
261 		cp += n;
262 		putshort(type, cp);
263 		cp += sizeof (u_short);
264 		putshort(class, cp);
265 		cp += sizeof (u_short);
266 		putlong(0, cp);
267 		cp += sizeof (u_long);
268 		putshort(datalen, cp);
269 		cp += sizeof (u_short);
270 		if (datalen) {
271 #ifdef SYSV
272 			memcpy((void *)cp, (void *)data, datalen);
273 #else
274 			bcopy(data, cp, datalen);
275 #endif
276 			cp += datalen;
277 		}
278 		if ((op == UPDATED) || (op == UPDATEDA)) {
279 			hp->ancount = htons(0);
280 			break;
281 		}
282 		/* Else UPDATEM/UPDATEMA, so drop into code for UPDATEA */
283 
284 	case UPDATEA:	/* Add new resource record */
285 		buflen -= RRFIXEDSZ + datalen;
286 		if ((n = dn_comp(dname, cp, buflen, dnptrs, lastdnptr)) < 0)
287 			return (-1);
288 		cp += n;
289 		putshort(newrr->r_type, cp);
290 		cp += sizeof (u_short);
291 		putshort(newrr->r_class, cp);
292 		cp += sizeof (u_short);
293 		putlong(0, cp);
294 		cp += sizeof (u_long);
295 		putshort(newrr->r_size, cp);
296 		cp += sizeof (u_short);
297 		if (newrr->r_size) {
298 #ifdef SYSV
299 			memcpy((void *)cp, newrr->r_data, newrr->r_size);
300 #else
301 			bcopy(newrr->r_data, cp, newrr->r_size);
302 #endif
303 			cp += newrr->r_size;
304 		}
305 		hp->ancount = htons(0);
306 		break;
307 
308 #endif /* ALLOW_UPDATES */
309 	}
310 	return (cp - buf);
311 }
312