1 /*
2   Linux DNS client library implementation
3 
4   Copyright (C) 2006 Krishna Ganugapati <krishnag@centeris.com>
5   Copyright (C) 2006 Gerald Carter <jerry@samba.org>
6 
7      ** NOTE! The following LGPL license applies to the libaddns
8      ** library. This does NOT imply that all of Samba is released
9      ** under the LGPL
10 
11   This library is free software; you can redistribute it and/or
12   modify it under the terms of the GNU Lesser General Public
13   License as published by the Free Software Foundation; either
14   version 2.1 of the License, or (at your option) any later version.
15 
16   This library is distributed in the hope that it will be useful,
17   but WITHOUT ANY WARRANTY; without even the implied warranty of
18   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19   Lesser General Public License for more details.
20 
21   You should have received a copy of the GNU Lesser General Public
22   License along with this library; if not, see <http://www.gnu.org/licenses/>.
23 */
24 
25 #include "replace.h"
26 #include "dns.h"
27 #include <sys/time.h>
28 #include <unistd.h>
29 #include "system/select.h"
30 #include "../lib/util/debug.h"
31 
destroy_dns_connection(struct dns_connection * conn)32 static int destroy_dns_connection(struct dns_connection *conn)
33 {
34 	return close(conn->s);
35 }
36 
37 /********************************************************************
38 ********************************************************************/
39 
dns_open_helper(const char * nameserver,const char * service,struct addrinfo * hints,TALLOC_CTX * mem_ctx,struct dns_connection ** ret_conn)40 static DNS_ERROR dns_open_helper(const char *nameserver,
41 				 const char *service,
42 				 struct addrinfo *hints,
43 				 TALLOC_CTX *mem_ctx,
44 				 struct dns_connection **ret_conn)
45 {
46 	int ret;
47 	struct addrinfo *rp;
48 	struct addrinfo *ai_result = NULL;
49 	struct dns_connection *conn = NULL;
50 
51 	if (!(conn = talloc(mem_ctx, struct dns_connection))) {
52 		return ERROR_DNS_NO_MEMORY;
53 	}
54 
55 	ret = getaddrinfo(nameserver, service, hints, &ai_result);
56 	if (ret != 0) {
57 		DEBUG(1,("dns_tcp_open: getaddrinfo: %s\n", gai_strerror(ret)));
58 		TALLOC_FREE(conn);
59 		return ERROR_DNS_INVALID_NAME_SERVER;
60 	}
61 
62 	for (rp = ai_result; rp != NULL; rp = rp->ai_next) {
63 		conn->s = socket(rp->ai_family,
64 				rp->ai_socktype,
65 				rp->ai_protocol);
66 		if (conn->s == -1) {
67 			continue;
68 		}
69 		do {
70 			ret = connect(conn->s, rp->ai_addr, rp->ai_addrlen);
71 		} while ((ret == -1) && (errno == EINTR));
72 		if (ret != -1) {
73 			/* Successful connect */
74 			break;
75 		}
76 		close(conn->s);
77 	}
78 
79 	freeaddrinfo(ai_result);
80 
81 	if (rp == NULL) {
82 		TALLOC_FREE(conn);
83 		return ERROR_DNS_CONNECTION_FAILED;
84 	}
85 
86 	talloc_set_destructor(conn, destroy_dns_connection);
87 
88 	*ret_conn = conn;
89 	return ERROR_DNS_SUCCESS;
90 }
91 
dns_tcp_open(const char * nameserver,TALLOC_CTX * mem_ctx,struct dns_connection ** result)92 static DNS_ERROR dns_tcp_open( const char *nameserver,
93 			       TALLOC_CTX *mem_ctx,
94 			       struct dns_connection **result )
95 {
96 	struct addrinfo hints;
97 	struct dns_connection *conn;
98 	DNS_ERROR dns_ret;
99 	char service[16];
100 
101 	snprintf(service, sizeof(service), "%d", DNS_TCP_PORT);
102 
103 	memset(&hints, 0, sizeof(struct addrinfo));
104 	hints.ai_family = AF_UNSPEC;
105 	hints.ai_socktype = SOCK_STREAM;
106 	hints.ai_flags = 0;
107 	hints.ai_protocol = IPPROTO_TCP;
108 
109 	dns_ret = dns_open_helper(nameserver, service, &hints, mem_ctx, &conn);
110 	if (!ERR_DNS_IS_OK(dns_ret)) {
111 		return dns_ret;
112 	}
113 
114 	conn->hType = DNS_TCP;
115 	*result = conn;
116 	return ERROR_DNS_SUCCESS;
117 }
118 
119 /********************************************************************
120  * ********************************************************************/
121 
dns_udp_open(const char * nameserver,TALLOC_CTX * mem_ctx,struct dns_connection ** result)122 static DNS_ERROR dns_udp_open( const char *nameserver,
123 			       TALLOC_CTX *mem_ctx,
124 			       struct dns_connection **result )
125 {
126 	struct addrinfo hints;
127 	struct sockaddr_storage RecvAddr;
128 	struct dns_connection *conn = NULL;
129 	DNS_ERROR dns_ret;
130 	socklen_t RecvAddrLen;
131 	char service[16];
132 
133 	snprintf(service, sizeof(service), "%d", DNS_UDP_PORT);
134 
135 	memset(&hints, 0, sizeof(struct addrinfo));
136 	hints.ai_family = AF_UNSPEC;
137 	hints.ai_socktype = SOCK_DGRAM;
138 	hints.ai_flags = 0;
139 	hints.ai_protocol = IPPROTO_UDP;
140 
141 	dns_ret = dns_open_helper(nameserver, service, &hints, mem_ctx, &conn);
142 	if (!ERR_DNS_IS_OK(dns_ret)) {
143 		TALLOC_FREE(conn);
144 		return dns_ret;
145 	}
146 
147 	/* Set up the RecvAddr structure with the IP address of
148 	   the receiver and the specified port number. */
149 
150 	RecvAddrLen = sizeof(RecvAddr);
151 	if (getpeername(conn->s,
152 			(struct sockaddr *)&RecvAddr,
153 			&RecvAddrLen) == -1) {
154 		return ERROR_DNS_CONNECTION_FAILED;
155 	}
156 
157 	conn->hType = DNS_UDP;
158 	memcpy(&conn->RecvAddr, &RecvAddr, sizeof(struct sockaddr_storage));
159 
160 	*result = conn;
161 	return ERROR_DNS_SUCCESS;
162 }
163 
164 /********************************************************************
165 ********************************************************************/
166 
dns_open_connection(const char * nameserver,int32_t dwType,TALLOC_CTX * mem_ctx,struct dns_connection ** conn)167 DNS_ERROR dns_open_connection( const char *nameserver, int32_t dwType,
168 		    TALLOC_CTX *mem_ctx,
169 		    struct dns_connection **conn )
170 {
171 	switch ( dwType ) {
172 	case DNS_TCP:
173 		return dns_tcp_open( nameserver, mem_ctx, conn );
174 	case DNS_UDP:
175 		return dns_udp_open( nameserver, mem_ctx, conn );
176 	}
177 
178 	return ERROR_DNS_INVALID_PARAMETER;
179 }
180 
write_all(int fd,uint8_t * data,size_t len)181 static DNS_ERROR write_all(int fd, uint8_t *data, size_t len)
182 {
183 	size_t total = 0;
184 
185 	while (total < len) {
186 
187 		ssize_t ret;
188 
189 		do {
190 			ret = write(fd, data + total, len - total);
191 		} while ((ret == -1) && (errno == EINTR));
192 
193 		if (ret <= 0) {
194 			/*
195 			 * EOF or error
196 			 */
197 			return ERROR_DNS_SOCKET_ERROR;
198 		}
199 
200 		total += ret;
201 	}
202 
203 	return ERROR_DNS_SUCCESS;
204 }
205 
dns_send_tcp(struct dns_connection * conn,const struct dns_buffer * buf)206 static DNS_ERROR dns_send_tcp(struct dns_connection *conn,
207 			      const struct dns_buffer *buf)
208 {
209 	uint16_t len = htons(buf->offset);
210 	DNS_ERROR err;
211 
212 	err = write_all(conn->s, (uint8_t *)&len, sizeof(len));
213 	if (!ERR_DNS_IS_OK(err)) return err;
214 
215 	return write_all(conn->s, buf->data, buf->offset);
216 }
217 
dns_send_udp(struct dns_connection * conn,const struct dns_buffer * buf)218 static DNS_ERROR dns_send_udp(struct dns_connection *conn,
219 			      const struct dns_buffer *buf)
220 {
221 	ssize_t ret;
222 
223 	do {
224 		ret = send(conn->s, buf->data, buf->offset, 0);
225 	} while ((ret == -1) && (errno == EINTR));
226 
227 	if (ret != buf->offset) {
228 		return ERROR_DNS_SOCKET_ERROR;
229 	}
230 
231 	return ERROR_DNS_SUCCESS;
232 }
233 
dns_send(struct dns_connection * conn,const struct dns_buffer * buf)234 DNS_ERROR dns_send(struct dns_connection *conn, const struct dns_buffer *buf)
235 {
236 	if (conn->hType == DNS_TCP) {
237 		return dns_send_tcp(conn, buf);
238 	}
239 
240 	if (conn->hType == DNS_UDP) {
241 		return dns_send_udp(conn, buf);
242 	}
243 
244 	return ERROR_DNS_INVALID_PARAMETER;
245 }
246 
read_all(int fd,uint8_t * data,size_t len)247 static DNS_ERROR read_all(int fd, uint8_t *data, size_t len)
248 {
249 	size_t total = 0;
250 
251 	while (total < len) {
252 		struct pollfd pfd;
253 		ssize_t ret;
254 		int fd_ready;
255 
256 		ZERO_STRUCT(pfd);
257 		pfd.fd = fd;
258 		pfd.events = POLLIN|POLLHUP;
259 
260 		fd_ready = poll(&pfd, 1, 10000);
261 		if (fd_ready == -1) {
262 			if (errno == EINTR) {
263 				continue;
264 			}
265 			return ERROR_DNS_SOCKET_ERROR;
266 		}
267 		if ( fd_ready == 0 ) {
268 			/* read timeout */
269 			return ERROR_DNS_SOCKET_ERROR;
270 		}
271 
272 		do {
273 			ret = read(fd, data + total, len - total);
274 		} while ((ret == -1) && (errno == EINTR));
275 
276 		if (ret <= 0) {
277 			/* EOF or error */
278 			return ERROR_DNS_SOCKET_ERROR;
279 		}
280 
281 		total += ret;
282 	}
283 
284 	return ERROR_DNS_SUCCESS;
285 }
286 
dns_receive_tcp(TALLOC_CTX * mem_ctx,struct dns_connection * conn,struct dns_buffer ** presult)287 static DNS_ERROR dns_receive_tcp(TALLOC_CTX *mem_ctx,
288 				 struct dns_connection *conn,
289 				 struct dns_buffer **presult)
290 {
291 	struct dns_buffer *buf;
292 	DNS_ERROR err;
293 	uint16_t len;
294 
295 	if (!(buf = talloc_zero(mem_ctx, struct dns_buffer))) {
296 		return ERROR_DNS_NO_MEMORY;
297 	}
298 
299 	err = read_all(conn->s, (uint8_t *)&len, sizeof(len));
300 	if (!ERR_DNS_IS_OK(err)) {
301 		return err;
302 	}
303 
304 	buf->size = ntohs(len);
305 
306 	if (buf->size == 0) {
307 		*presult = buf;
308 		return ERROR_DNS_SUCCESS;
309 	}
310 
311 	if (!(buf->data = talloc_array(buf, uint8_t, buf->size))) {
312 		TALLOC_FREE(buf);
313 		return ERROR_DNS_NO_MEMORY;
314 	}
315 
316 	err = read_all(conn->s, buf->data, talloc_get_size(buf->data));
317 	if (!ERR_DNS_IS_OK(err)) {
318 		TALLOC_FREE(buf);
319 		return err;
320 	}
321 
322 	*presult = buf;
323 	return ERROR_DNS_SUCCESS;
324 }
325 
dns_receive_udp(TALLOC_CTX * mem_ctx,struct dns_connection * conn,struct dns_buffer ** presult)326 static DNS_ERROR dns_receive_udp(TALLOC_CTX *mem_ctx,
327 				 struct dns_connection *conn,
328 				 struct dns_buffer **presult)
329 {
330 	struct dns_buffer *buf;
331 	ssize_t received;
332 
333 	if (!(buf = talloc_zero(mem_ctx, struct dns_buffer))) {
334 		return ERROR_DNS_NO_MEMORY;
335 	}
336 
337 	/*
338 	 * UDP based DNS can only be 512 bytes
339 	 */
340 
341 	if (!(buf->data = talloc_array(buf, uint8_t, 512))) {
342 		TALLOC_FREE(buf);
343 		return ERROR_DNS_NO_MEMORY;
344 	}
345 
346 	do {
347 		received = recv(conn->s, (void *)buf->data, 512, 0);
348 	} while ((received == -1) && (errno == EINTR));
349 
350 	if (received == -1) {
351 		TALLOC_FREE(buf);
352 		return ERROR_DNS_SOCKET_ERROR;
353 	}
354 
355 	if (received > 512) {
356 		TALLOC_FREE(buf);
357 		return ERROR_DNS_BAD_RESPONSE;
358 	}
359 
360 	buf->size = received;
361 	buf->offset = 0;
362 
363 	*presult = buf;
364 	return ERROR_DNS_SUCCESS;
365 }
366 
dns_receive(TALLOC_CTX * mem_ctx,struct dns_connection * conn,struct dns_buffer ** presult)367 DNS_ERROR dns_receive(TALLOC_CTX *mem_ctx, struct dns_connection *conn,
368 		      struct dns_buffer **presult)
369 {
370 	if (conn->hType == DNS_TCP) {
371 		return dns_receive_tcp(mem_ctx, conn, presult);
372 	}
373 
374 	if (conn->hType == DNS_UDP) {
375 		return dns_receive_udp(mem_ctx, conn, presult);
376 	}
377 
378 	return ERROR_DNS_INVALID_PARAMETER;
379 }
380 
dns_transaction(TALLOC_CTX * mem_ctx,struct dns_connection * conn,const struct dns_request * req,struct dns_request ** resp)381 DNS_ERROR dns_transaction(TALLOC_CTX *mem_ctx, struct dns_connection *conn,
382 			  const struct dns_request *req,
383 			  struct dns_request **resp)
384 {
385 	struct dns_buffer *buf = NULL;
386 	DNS_ERROR err;
387 
388 	err = dns_marshall_request(mem_ctx, req, &buf);
389 	if (!ERR_DNS_IS_OK(err)) goto error;
390 
391 	err = dns_send(conn, buf);
392 	if (!ERR_DNS_IS_OK(err)) goto error;
393 	TALLOC_FREE(buf);
394 
395 	err = dns_receive(mem_ctx, conn, &buf);
396 	if (!ERR_DNS_IS_OK(err)) goto error;
397 
398 	err = dns_unmarshall_request(mem_ctx, buf, resp);
399 
400  error:
401 	TALLOC_FREE(buf);
402 	return err;
403 }
404 
dns_update_transaction(TALLOC_CTX * mem_ctx,struct dns_connection * conn,struct dns_update_request * up_req,struct dns_update_request ** up_resp)405 DNS_ERROR dns_update_transaction(TALLOC_CTX *mem_ctx,
406 				 struct dns_connection *conn,
407 				 struct dns_update_request *up_req,
408 				 struct dns_update_request **up_resp)
409 {
410 	struct dns_request *resp;
411 	DNS_ERROR err;
412 
413 	err = dns_transaction(mem_ctx, conn, dns_update2request(up_req),
414 			      &resp);
415 
416 	if (!ERR_DNS_IS_OK(err)) return err;
417 
418 	*up_resp = dns_request2update(resp);
419 	return ERROR_DNS_SUCCESS;
420 }
421