/src/samba/lib/addns/dnssock.c
Line | Count | Source |
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 | | |
32 | | static int destroy_dns_connection(struct dns_connection *conn) |
33 | 0 | { |
34 | 0 | return close(conn->s); |
35 | 0 | } |
36 | | |
37 | | /******************************************************************** |
38 | | ********************************************************************/ |
39 | | |
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 | 0 | { |
46 | 0 | int ret; |
47 | 0 | struct addrinfo *rp; |
48 | 0 | struct addrinfo *ai_result = NULL; |
49 | 0 | struct dns_connection *conn = NULL; |
50 | |
|
51 | 0 | if (!(conn = talloc(mem_ctx, struct dns_connection))) { |
52 | 0 | return ERROR_DNS_NO_MEMORY; |
53 | 0 | } |
54 | | |
55 | 0 | ret = getaddrinfo(nameserver, service, hints, &ai_result); |
56 | 0 | if (ret != 0) { |
57 | 0 | DEBUG(1,("dns_tcp_open: getaddrinfo: %s\n", gai_strerror(ret))); |
58 | 0 | TALLOC_FREE(conn); |
59 | 0 | return ERROR_DNS_INVALID_NAME_SERVER; |
60 | 0 | } |
61 | | |
62 | 0 | for (rp = ai_result; rp != NULL; rp = rp->ai_next) { |
63 | 0 | conn->s = socket(rp->ai_family, |
64 | 0 | rp->ai_socktype, |
65 | 0 | rp->ai_protocol); |
66 | 0 | if (conn->s == -1) { |
67 | 0 | continue; |
68 | 0 | } |
69 | 0 | do { |
70 | 0 | ret = connect(conn->s, rp->ai_addr, rp->ai_addrlen); |
71 | 0 | } while ((ret == -1) && (errno == EINTR)); |
72 | 0 | if (ret != -1) { |
73 | | /* Successful connect */ |
74 | 0 | break; |
75 | 0 | } |
76 | 0 | close(conn->s); |
77 | 0 | } |
78 | |
|
79 | 0 | freeaddrinfo(ai_result); |
80 | |
|
81 | 0 | if (rp == NULL) { |
82 | 0 | TALLOC_FREE(conn); |
83 | 0 | return ERROR_DNS_CONNECTION_FAILED; |
84 | 0 | } |
85 | | |
86 | 0 | talloc_set_destructor(conn, destroy_dns_connection); |
87 | |
|
88 | 0 | *ret_conn = conn; |
89 | 0 | return ERROR_DNS_SUCCESS; |
90 | 0 | } |
91 | | |
92 | | static DNS_ERROR dns_tcp_open( const char *nameserver, |
93 | | TALLOC_CTX *mem_ctx, |
94 | | struct dns_connection **result ) |
95 | 0 | { |
96 | 0 | struct addrinfo hints; |
97 | 0 | struct dns_connection *conn; |
98 | 0 | DNS_ERROR dns_ret; |
99 | 0 | char service[16]; |
100 | |
|
101 | 0 | snprintf(service, sizeof(service), "%d", DNS_TCP_PORT); |
102 | |
|
103 | 0 | memset(&hints, 0, sizeof(struct addrinfo)); |
104 | 0 | hints.ai_family = AF_UNSPEC; |
105 | 0 | hints.ai_socktype = SOCK_STREAM; |
106 | 0 | hints.ai_flags = 0; |
107 | 0 | hints.ai_protocol = IPPROTO_TCP; |
108 | |
|
109 | 0 | dns_ret = dns_open_helper(nameserver, service, &hints, mem_ctx, &conn); |
110 | 0 | if (!ERR_DNS_IS_OK(dns_ret)) { |
111 | 0 | return dns_ret; |
112 | 0 | } |
113 | | |
114 | 0 | conn->hType = DNS_TCP; |
115 | 0 | *result = conn; |
116 | 0 | return ERROR_DNS_SUCCESS; |
117 | 0 | } |
118 | | |
119 | | /******************************************************************** |
120 | | * ********************************************************************/ |
121 | | |
122 | | static DNS_ERROR dns_udp_open( const char *nameserver, |
123 | | TALLOC_CTX *mem_ctx, |
124 | | struct dns_connection **result ) |
125 | 0 | { |
126 | 0 | struct addrinfo hints; |
127 | 0 | struct sockaddr_storage RecvAddr; |
128 | 0 | struct dns_connection *conn = NULL; |
129 | 0 | DNS_ERROR dns_ret; |
130 | 0 | socklen_t RecvAddrLen; |
131 | 0 | char service[16]; |
132 | |
|
133 | 0 | snprintf(service, sizeof(service), "%d", DNS_UDP_PORT); |
134 | |
|
135 | 0 | memset(&hints, 0, sizeof(struct addrinfo)); |
136 | 0 | hints.ai_family = AF_UNSPEC; |
137 | 0 | hints.ai_socktype = SOCK_DGRAM; |
138 | 0 | hints.ai_flags = 0; |
139 | 0 | hints.ai_protocol = IPPROTO_UDP; |
140 | |
|
141 | 0 | dns_ret = dns_open_helper(nameserver, service, &hints, mem_ctx, &conn); |
142 | 0 | if (!ERR_DNS_IS_OK(dns_ret)) { |
143 | 0 | TALLOC_FREE(conn); |
144 | 0 | return dns_ret; |
145 | 0 | } |
146 | | |
147 | | /* Set up the RecvAddr structure with the IP address of |
148 | | the receiver and the specified port number. */ |
149 | | |
150 | 0 | RecvAddrLen = sizeof(RecvAddr); |
151 | 0 | if (getpeername(conn->s, |
152 | 0 | (struct sockaddr *)&RecvAddr, |
153 | 0 | &RecvAddrLen) == -1) { |
154 | 0 | return ERROR_DNS_CONNECTION_FAILED; |
155 | 0 | } |
156 | | |
157 | 0 | conn->hType = DNS_UDP; |
158 | 0 | memcpy(&conn->RecvAddr, &RecvAddr, sizeof(struct sockaddr_storage)); |
159 | |
|
160 | 0 | *result = conn; |
161 | 0 | return ERROR_DNS_SUCCESS; |
162 | 0 | } |
163 | | |
164 | | /******************************************************************** |
165 | | ********************************************************************/ |
166 | | |
167 | | DNS_ERROR dns_open_connection( const char *nameserver, int32_t dwType, |
168 | | TALLOC_CTX *mem_ctx, |
169 | | struct dns_connection **conn ) |
170 | 0 | { |
171 | 0 | switch ( dwType ) { |
172 | 0 | case DNS_TCP: |
173 | 0 | return dns_tcp_open( nameserver, mem_ctx, conn ); |
174 | 0 | case DNS_UDP: |
175 | 0 | return dns_udp_open( nameserver, mem_ctx, conn ); |
176 | 0 | } |
177 | | |
178 | 0 | return ERROR_DNS_INVALID_PARAMETER; |
179 | 0 | } |
180 | | |
181 | | static DNS_ERROR write_all(int fd, uint8_t *data, size_t len) |
182 | 0 | { |
183 | 0 | size_t total = 0; |
184 | |
|
185 | 0 | while (total < len) { |
186 | |
|
187 | 0 | ssize_t ret; |
188 | |
|
189 | 0 | do { |
190 | 0 | ret = write(fd, data + total, len - total); |
191 | 0 | } while ((ret == -1) && (errno == EINTR)); |
192 | |
|
193 | 0 | if (ret <= 0) { |
194 | | /* |
195 | | * EOF or error |
196 | | */ |
197 | 0 | return ERROR_DNS_SOCKET_ERROR; |
198 | 0 | } |
199 | | |
200 | 0 | total += ret; |
201 | 0 | } |
202 | | |
203 | 0 | return ERROR_DNS_SUCCESS; |
204 | 0 | } |
205 | | |
206 | | static DNS_ERROR dns_send_tcp(struct dns_connection *conn, |
207 | | const struct dns_buffer *buf) |
208 | 0 | { |
209 | 0 | uint16_t len = htons(buf->offset); |
210 | 0 | DNS_ERROR err; |
211 | |
|
212 | 0 | err = write_all(conn->s, (uint8_t *)&len, sizeof(len)); |
213 | 0 | if (!ERR_DNS_IS_OK(err)) return err; |
214 | | |
215 | 0 | return write_all(conn->s, buf->data, buf->offset); |
216 | 0 | } |
217 | | |
218 | | static DNS_ERROR dns_send_udp(struct dns_connection *conn, |
219 | | const struct dns_buffer *buf) |
220 | 0 | { |
221 | 0 | ssize_t ret; |
222 | |
|
223 | 0 | do { |
224 | 0 | ret = send(conn->s, buf->data, buf->offset, 0); |
225 | 0 | } while ((ret == -1) && (errno == EINTR)); |
226 | |
|
227 | 0 | if (ret != buf->offset) { |
228 | 0 | return ERROR_DNS_SOCKET_ERROR; |
229 | 0 | } |
230 | | |
231 | 0 | return ERROR_DNS_SUCCESS; |
232 | 0 | } |
233 | | |
234 | | DNS_ERROR dns_send(struct dns_connection *conn, const struct dns_buffer *buf) |
235 | 0 | { |
236 | 0 | if (conn->hType == DNS_TCP) { |
237 | 0 | return dns_send_tcp(conn, buf); |
238 | 0 | } |
239 | | |
240 | 0 | if (conn->hType == DNS_UDP) { |
241 | 0 | return dns_send_udp(conn, buf); |
242 | 0 | } |
243 | | |
244 | 0 | return ERROR_DNS_INVALID_PARAMETER; |
245 | 0 | } |
246 | | |
247 | | static DNS_ERROR read_all(int fd, uint8_t *data, size_t len) |
248 | 0 | { |
249 | 0 | size_t total = 0; |
250 | |
|
251 | 0 | while (total < len) { |
252 | 0 | struct pollfd pfd; |
253 | 0 | ssize_t ret; |
254 | 0 | int fd_ready; |
255 | |
|
256 | 0 | ZERO_STRUCT(pfd); |
257 | 0 | pfd.fd = fd; |
258 | 0 | pfd.events = POLLIN|POLLHUP; |
259 | |
|
260 | 0 | fd_ready = poll(&pfd, 1, 10000); |
261 | 0 | if (fd_ready == -1) { |
262 | 0 | if (errno == EINTR) { |
263 | 0 | continue; |
264 | 0 | } |
265 | 0 | return ERROR_DNS_SOCKET_ERROR; |
266 | 0 | } |
267 | 0 | if ( fd_ready == 0 ) { |
268 | | /* read timeout */ |
269 | 0 | return ERROR_DNS_SOCKET_ERROR; |
270 | 0 | } |
271 | | |
272 | 0 | do { |
273 | 0 | ret = read(fd, data + total, len - total); |
274 | 0 | } while ((ret == -1) && (errno == EINTR)); |
275 | |
|
276 | 0 | if (ret <= 0) { |
277 | | /* EOF or error */ |
278 | 0 | return ERROR_DNS_SOCKET_ERROR; |
279 | 0 | } |
280 | | |
281 | 0 | total += ret; |
282 | 0 | } |
283 | | |
284 | 0 | return ERROR_DNS_SUCCESS; |
285 | 0 | } |
286 | | |
287 | | static DNS_ERROR dns_receive_tcp(TALLOC_CTX *mem_ctx, |
288 | | struct dns_connection *conn, |
289 | | struct dns_buffer **presult) |
290 | 0 | { |
291 | 0 | struct dns_buffer *buf; |
292 | 0 | DNS_ERROR err; |
293 | 0 | uint16_t len; |
294 | |
|
295 | 0 | if (!(buf = talloc_zero(mem_ctx, struct dns_buffer))) { |
296 | 0 | return ERROR_DNS_NO_MEMORY; |
297 | 0 | } |
298 | | |
299 | 0 | err = read_all(conn->s, (uint8_t *)&len, sizeof(len)); |
300 | 0 | if (!ERR_DNS_IS_OK(err)) { |
301 | 0 | return err; |
302 | 0 | } |
303 | | |
304 | 0 | buf->size = ntohs(len); |
305 | |
|
306 | 0 | if (buf->size == 0) { |
307 | 0 | *presult = buf; |
308 | 0 | return ERROR_DNS_SUCCESS; |
309 | 0 | } |
310 | | |
311 | 0 | if (!(buf->data = talloc_array(buf, uint8_t, buf->size))) { |
312 | 0 | TALLOC_FREE(buf); |
313 | 0 | return ERROR_DNS_NO_MEMORY; |
314 | 0 | } |
315 | | |
316 | 0 | err = read_all(conn->s, buf->data, talloc_get_size(buf->data)); |
317 | 0 | if (!ERR_DNS_IS_OK(err)) { |
318 | 0 | TALLOC_FREE(buf); |
319 | 0 | return err; |
320 | 0 | } |
321 | | |
322 | 0 | *presult = buf; |
323 | 0 | return ERROR_DNS_SUCCESS; |
324 | 0 | } |
325 | | |
326 | | static DNS_ERROR dns_receive_udp(TALLOC_CTX *mem_ctx, |
327 | | struct dns_connection *conn, |
328 | | struct dns_buffer **presult) |
329 | 0 | { |
330 | 0 | struct dns_buffer *buf; |
331 | 0 | ssize_t received; |
332 | |
|
333 | 0 | if (!(buf = talloc_zero(mem_ctx, struct dns_buffer))) { |
334 | 0 | return ERROR_DNS_NO_MEMORY; |
335 | 0 | } |
336 | | |
337 | | /* |
338 | | * UDP based DNS can only be 512 bytes |
339 | | */ |
340 | | |
341 | 0 | if (!(buf->data = talloc_array(buf, uint8_t, 512))) { |
342 | 0 | TALLOC_FREE(buf); |
343 | 0 | return ERROR_DNS_NO_MEMORY; |
344 | 0 | } |
345 | | |
346 | 0 | do { |
347 | 0 | received = recv(conn->s, (void *)buf->data, 512, 0); |
348 | 0 | } while ((received == -1) && (errno == EINTR)); |
349 | |
|
350 | 0 | if (received == -1) { |
351 | 0 | TALLOC_FREE(buf); |
352 | 0 | return ERROR_DNS_SOCKET_ERROR; |
353 | 0 | } |
354 | | |
355 | 0 | if (received > 512) { |
356 | 0 | TALLOC_FREE(buf); |
357 | 0 | return ERROR_DNS_BAD_RESPONSE; |
358 | 0 | } |
359 | | |
360 | 0 | buf->size = received; |
361 | 0 | buf->offset = 0; |
362 | |
|
363 | 0 | *presult = buf; |
364 | 0 | return ERROR_DNS_SUCCESS; |
365 | 0 | } |
366 | | |
367 | | DNS_ERROR dns_receive(TALLOC_CTX *mem_ctx, struct dns_connection *conn, |
368 | | struct dns_buffer **presult) |
369 | 0 | { |
370 | 0 | if (conn->hType == DNS_TCP) { |
371 | 0 | return dns_receive_tcp(mem_ctx, conn, presult); |
372 | 0 | } |
373 | | |
374 | 0 | if (conn->hType == DNS_UDP) { |
375 | 0 | return dns_receive_udp(mem_ctx, conn, presult); |
376 | 0 | } |
377 | | |
378 | 0 | return ERROR_DNS_INVALID_PARAMETER; |
379 | 0 | } |
380 | | |
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 | 0 | { |
385 | 0 | struct dns_buffer *buf = NULL; |
386 | 0 | DNS_ERROR err; |
387 | |
|
388 | 0 | err = dns_marshall_request(mem_ctx, req, &buf); |
389 | 0 | if (!ERR_DNS_IS_OK(err)) goto error; |
390 | | |
391 | 0 | err = dns_send(conn, buf); |
392 | 0 | if (!ERR_DNS_IS_OK(err)) goto error; |
393 | 0 | TALLOC_FREE(buf); |
394 | |
|
395 | 0 | err = dns_receive(mem_ctx, conn, &buf); |
396 | 0 | if (!ERR_DNS_IS_OK(err)) goto error; |
397 | | |
398 | 0 | err = dns_unmarshall_request(mem_ctx, buf, resp); |
399 | |
|
400 | 0 | error: |
401 | 0 | TALLOC_FREE(buf); |
402 | 0 | return err; |
403 | 0 | } |
404 | | |
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 | 0 | { |
410 | 0 | struct dns_request *resp; |
411 | 0 | DNS_ERROR err; |
412 | |
|
413 | 0 | err = dns_transaction(mem_ctx, conn, dns_update2request(up_req), |
414 | 0 | &resp); |
415 | |
|
416 | 0 | if (!ERR_DNS_IS_OK(err)) return err; |
417 | | |
418 | 0 | *up_resp = dns_request2update(resp); |
419 | 0 | return ERROR_DNS_SUCCESS; |
420 | 0 | } |