b1d794db45614de3292f11e429dedc531b70e9cf
[bbaumbach/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) {
325                 if (!(buf->data = talloc_array(buf, uint8_t, buf->size))) {
326                         TALLOC_FREE(buf);
327                         return ERROR_DNS_NO_MEMORY;
328                 }
329         } else {
330                 buf->data = NULL;
331         }
332
333         err = read_all(conn->s, buf->data, buf->size);
334         if (!ERR_DNS_IS_OK(err)) {
335                 TALLOC_FREE(buf);
336                 return err;
337         }
338
339         *presult = buf;
340         return ERROR_DNS_SUCCESS;
341 }
342
343 static DNS_ERROR dns_receive_udp(TALLOC_CTX *mem_ctx,
344                                  struct dns_connection *conn,
345                                  struct dns_buffer **presult)
346 {
347         struct dns_buffer *buf;
348         ssize_t received;
349
350         if (!(buf = talloc_zero(mem_ctx, struct dns_buffer))) {
351                 return ERROR_DNS_NO_MEMORY;
352         }
353
354         /*
355          * UDP based DNS can only be 512 bytes
356          */
357
358         if (!(buf->data = talloc_array(buf, uint8_t, 512))) {
359                 TALLOC_FREE(buf);
360                 return ERROR_DNS_NO_MEMORY;
361         }
362
363         do {
364                 received = recv(conn->s, (void *)buf->data, 512, 0);
365         } while ((received == -1) && (errno == EINTR));
366
367         if (received == -1) {
368                 TALLOC_FREE(buf);
369                 return ERROR_DNS_SOCKET_ERROR;
370         }
371
372         if (received > 512) {
373                 TALLOC_FREE(buf);
374                 return ERROR_DNS_BAD_RESPONSE;
375         }
376
377         buf->size = received;
378         buf->offset = 0;
379
380         *presult = buf;
381         return ERROR_DNS_SUCCESS;
382 }
383
384 DNS_ERROR dns_receive(TALLOC_CTX *mem_ctx, struct dns_connection *conn,
385                       struct dns_buffer **presult)
386 {
387         if (conn->hType == DNS_TCP) {
388                 return dns_receive_tcp(mem_ctx, conn, presult);
389         }
390
391         if (conn->hType == DNS_UDP) {
392                 return dns_receive_udp(mem_ctx, conn, presult);
393         }
394
395         return ERROR_DNS_INVALID_PARAMETER;
396 }
397
398 DNS_ERROR dns_transaction(TALLOC_CTX *mem_ctx, struct dns_connection *conn,
399                           const struct dns_request *req,
400                           struct dns_request **resp)
401 {
402         struct dns_buffer *buf = NULL;
403         DNS_ERROR err;
404
405         err = dns_marshall_request(mem_ctx, req, &buf);
406         if (!ERR_DNS_IS_OK(err)) goto error;
407
408         err = dns_send(conn, buf);
409         if (!ERR_DNS_IS_OK(err)) goto error;
410         TALLOC_FREE(buf);
411
412         err = dns_receive(mem_ctx, conn, &buf);
413         if (!ERR_DNS_IS_OK(err)) goto error;
414
415         err = dns_unmarshall_request(mem_ctx, buf, resp);
416
417  error:
418         TALLOC_FREE(buf);
419         return err;
420 }
421
422 DNS_ERROR dns_update_transaction(TALLOC_CTX *mem_ctx,
423                                  struct dns_connection *conn,
424                                  struct dns_update_request *up_req,
425                                  struct dns_update_request **up_resp)
426 {
427         struct dns_request *resp;
428         DNS_ERROR err;
429
430         err = dns_transaction(mem_ctx, conn, dns_update2request(up_req),
431                               &resp);
432
433         if (!ERR_DNS_IS_OK(err)) return err;
434
435         *up_resp = dns_request2update(resp);
436         return ERROR_DNS_SUCCESS;
437 }