xref: /illumos-gate/usr/src/lib/libresolv/res_mkquery.c (revision 2c5ec7a875dcd76853e6618614e990f1e8cdd56d)
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 /*
23  * Copyright 2015 Gary Mills
24  * Copyright 2008 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 #include <stdio.h>
42 #include <sys/types.h>
43 #include <sys/socket.h>
44 #include <sys/stat.h>
45 #include <netinet/in.h>
46 #include <arpa/nameser.h>
47 #include <resolv.h>
48 #include <string.h>
49 #include <stdlib.h>
50 #include <unistd.h>
51 #include <errno.h>
52 #include <netdb.h>
53 #include "crossl.h"
54 
55 /*
56  * Kludge to time out quickly if there is no /etc/resolv.conf
57  * and a TCP connection to the local DNS server fails.
58  *
59  * Moved function from res_send.c to res_mkquery.c.  This
60  * solves a long timeout problem with nslookup.
61  *
62  * __areweinnamed is needed because there is a possibility that the
63  * user might do bad things to resolv.conf and cause in.named to call
64  * _confcheck and deadlock the server.
65  */
66 
67 int __areweinnamed()
68 {
69 	return (0);
70 }
71 
72 static int _confcheck()
73 {
74 	int ns;
75 	struct stat rc_stat;
76 	struct sockaddr_in ns_sin;
77 
78 
79 	/* First, we check to see if /etc/resolv.conf exists.
80 	 * If it doesn't, then localhost is mostlikely to be
81 	 * the nameserver.
82 	 */
83 	if (stat(_PATH_RESCONF, &rc_stat) == -1 && errno == ENOENT) {
84 
85 		/* Next, we check to see if _res.nsaddr is set to loopback.
86 		 * If it isn't, it has been altered by the application
87 		 * explicitly and we then want to bail with success.
88 		 */
89 		if (__areweinnamed())
90 			return (0);
91 
92 		if (_res.nsaddr.sin_addr.S_un.S_addr == htonl(INADDR_LOOPBACK)) {
93 
94 			/* Lastly, we try to connect to the TCP port of the
95 			 * nameserver.  If this fails, then we know that
96 			 * DNS is misconfigured and we can quickly exit.
97 			 */
98 			ns = socket(AF_INET, SOCK_STREAM, 0);
99 			IN_SET_LOOPBACK_ADDR(&ns_sin);
100 			ns_sin.sin_port = htons(NAMESERVER_PORT);
101 			if (connect(ns, (struct sockaddr *) &ns_sin,
102 				    sizeof ns_sin) == -1) {
103 				(void) close(ns);
104 				return(-1);
105 			}
106 			else {
107 				(void) close(ns);
108 				return(0);
109 			}
110 		}
111 
112 		return(0);
113 	}
114 
115 	return (0);
116 }
117 
118 /*
119  * Form all types of queries.
120  * Returns the size of the result or -1.
121  */
122 int
123 res_mkquery(op, dname, class, type, data, datalen, newrr, buf, buflen)
124 	int op;			/* opcode of query */
125 	char *dname;		/* domain name */
126 	int class, type;	/* class and type of query */
127 	char *data;		/* resource record data */
128 	int datalen;		/* length of data */
129 	struct rrec *newrr;	/* new rr for modify or append */
130 	char *buf;		/* buffer to put query */
131 	int buflen;		/* size of buffer */
132 {
133 	register HEADER *hp;
134 	register u_char *cp;
135 	register int n;
136 	u_char *dnptrs[10], **dpp, **lastdnptr;
137 
138 #ifdef DEBUG
139 	if (_res.options & RES_DEBUG)
140 		printf("res_mkquery(%d, %s, %d, %d)\n", op, dname, class, type);
141 #endif /* DEBUG */
142 
143 	/*
144 	 * Check to see if we can bailout quickly.
145 	 * Also rerun res_init if we failed in the past.
146 	 */
147 
148 	if ((_res.options & RES_INIT) == 0 && res_init() == -1) {
149 		h_errno = NO_RECOVERY;
150 		return(-1);
151 	}
152 
153 	if (_confcheck() == -1) {
154 		_res.options &= ~RES_INIT;
155 		h_errno = NO_RECOVERY;
156 		return(-1);
157 	}
158 
159 	/*
160 	 * Initialize header fields.
161 	 */
162 	if ((buf == NULL) || (buflen < sizeof (HEADER)))
163 		return (-1);
164 #ifdef SYSV
165 	(void) memset(buf, 0, sizeof (HEADER));
166 #else
167 	bzero(buf, sizeof (HEADER));
168 #endif
169 	hp = (HEADER *) buf;
170 	hp->id = htons(++_res.id);
171 	hp->opcode = op;
172 	hp->pr = (_res.options & RES_PRIMARY) != 0;
173 	hp->rd = (_res.options & RES_RECURSE) != 0;
174 	hp->rcode = NOERROR;
175 	cp = (u_char *)(buf + sizeof (HEADER));
176 	buflen -= sizeof (HEADER);
177 	dpp = dnptrs;
178 	*dpp++ = (u_char *)buf;
179 	*dpp++ = NULL;
180 	lastdnptr = dnptrs + sizeof (dnptrs) / sizeof (dnptrs[0]);
181 	/*
182 	 * perform opcode specific processing
183 	 */
184 	switch (op) {
185 	case QUERY:
186 		if ((buflen -= QFIXEDSZ) < 0)
187 			return (-1);
188 		if ((n = dn_comp((u_char *)dname, cp, buflen,
189 		    dnptrs, lastdnptr)) < 0)
190 			return (-1);
191 		cp += n;
192 		buflen -= n;
193 		putshort(type, cp);
194 		cp += sizeof (u_short);
195 		putshort(class, cp);
196 		cp += sizeof (u_short);
197 		hp->qdcount = htons(1);
198 		if (op == QUERY || data == NULL)
199 			break;
200 		/*
201 		 * Make an additional record for completion domain.
202 		 */
203 		buflen -= RRFIXEDSZ;
204 		if ((n = dn_comp((u_char *)data, cp, buflen,
205 		    dnptrs, lastdnptr)) < 0)
206 			return (-1);
207 		cp += n;
208 		buflen -= n;
209 		putshort(T_NULL, cp);
210 		cp += sizeof (u_short);
211 		putshort(class, cp);
212 		cp += sizeof (u_short);
213 		putlong(0, cp);
214 		cp += sizeof (u_long);
215 		putshort(0, cp);
216 		cp += sizeof (u_short);
217 		hp->arcount = htons(1);
218 		break;
219 
220 	case IQUERY:
221 		/*
222 		 * Initialize answer section
223 		 */
224 		if (buflen < 1 + RRFIXEDSZ + datalen)
225 			return (-1);
226 		*cp++ = '\0';	/* no domain name */
227 		putshort(type, cp);
228 		cp += sizeof (u_short);
229 		putshort(class, cp);
230 		cp += sizeof (u_short);
231 		putlong(0, cp);
232 		cp += sizeof (u_long);
233 		putshort(datalen, cp);
234 		cp += sizeof (u_short);
235 		if (datalen) {
236 #ifdef SYSV
237 			(void) memcpy((void *)cp, (void *)data, datalen);
238 #else
239 			bcopy(data, cp, datalen);
240 #endif
241 			cp += datalen;
242 		}
243 		hp->ancount = htons(1);
244 		break;
245 
246 #ifdef ALLOW_UPDATES
247 	/*
248 	 * For UPDATEM/UPDATEMA, do UPDATED/UPDATEDA followed by UPDATEA
249 	 * (Record to be modified is followed by its replacement in msg.)
250 	 */
251 	case UPDATEM:
252 	case UPDATEMA:
253 
254 	case UPDATED:
255 		/*
256 		 * The res code for UPDATED and UPDATEDA is the same; user
257 		 * calls them differently: specifies data for UPDATED; server
258 		 * ignores data if specified for UPDATEDA.
259 		 */
260 	case UPDATEDA:
261 		buflen -= RRFIXEDSZ + datalen;
262 		if ((n = dn_comp((u_char *)dname, cp, buflen,
263 		    dnptrs, lastdnptr)) < 0)
264 			return (-1);
265 		cp += n;
266 		putshort(type, cp);
267 		cp += sizeof (u_short);
268 		putshort(class, cp);
269 		cp += sizeof (u_short);
270 		putlong(0, cp);
271 		cp += sizeof (u_long);
272 		putshort(datalen, cp);
273 		cp += sizeof (u_short);
274 		if (datalen) {
275 #ifdef SYSV
276 			memcpy((void *)cp, (void *)data, datalen);
277 #else
278 			bcopy(data, cp, datalen);
279 #endif
280 			cp += datalen;
281 		}
282 		if ((op == UPDATED) || (op == UPDATEDA)) {
283 			hp->ancount = htons(0);
284 			break;
285 		}
286 		/* Else UPDATEM/UPDATEMA, so drop into code for UPDATEA */
287 
288 	case UPDATEA:	/* Add new resource record */
289 		buflen -= RRFIXEDSZ + datalen;
290 		if ((n = dn_comp((u_char *)dname, cp, buflen,
291 		    dnptrs, lastdnptr)) < 0)
292 			return (-1);
293 		cp += n;
294 		putshort(newrr->r_type, cp);
295 		cp += sizeof (u_short);
296 		putshort(newrr->r_class, cp);
297 		cp += sizeof (u_short);
298 		putlong(0, cp);
299 		cp += sizeof (u_long);
300 		putshort(newrr->r_size, cp);
301 		cp += sizeof (u_short);
302 		if (newrr->r_size) {
303 #ifdef SYSV
304 			memcpy((void *)cp, newrr->r_data, newrr->r_size);
305 #else
306 			bcopy(newrr->r_data, cp, newrr->r_size);
307 #endif
308 			cp += newrr->r_size;
309 		}
310 		hp->ancount = htons(0);
311 		break;
312 
313 #endif /* ALLOW_UPDATES */
314 	}
315 	return ((char *)cp - buf);
316 }
317