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