ec42b7ca6892428d9efbb3320c338584fe0174ee
[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_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
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
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;
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
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
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
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
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 = sendto(conn->s, buf->data, buf->offset, 0,
225                      (struct sockaddr *)&conn->RecvAddr,
226                      sizeof(conn->RecvAddr));
227         } while ((ret == -1) && (errno == EINTR));
228
229         if (ret != buf->offset) {
230                 return ERROR_DNS_SOCKET_ERROR;
231         }
232
233         return ERROR_DNS_SUCCESS;
234 }
235
236 DNS_ERROR dns_send(struct dns_connection *conn, const struct dns_buffer *buf)
237 {
238         if (conn->hType == DNS_TCP) {
239                 return dns_send_tcp(conn, buf);
240         }
241
242         if (conn->hType == DNS_UDP) {
243                 return dns_send_udp(conn, buf);
244         }
245
246         return ERROR_DNS_INVALID_PARAMETER;
247 }
248
249 static DNS_ERROR read_all(int fd, uint8_t *data, size_t len)
250 {
251         size_t total = 0;
252
253         while (total < len) {
254                 struct pollfd pfd;
255                 ssize_t ret;
256                 int fd_ready;
257
258                 ZERO_STRUCT(pfd);
259                 pfd.fd = fd;
260                 pfd.events = POLLIN|POLLHUP;
261
262                 fd_ready = poll(&pfd, 1, 10000);
263                 if (fd_ready == -1) {
264                         if (errno == EINTR) {
265                                 continue;
266                         }
267                         return ERROR_DNS_SOCKET_ERROR;
268                 }
269                 if ( fd_ready == 0 ) {
270                         /* read timeout */
271                         return ERROR_DNS_SOCKET_ERROR;
272                 }
273
274                 do {
275                         ret = read(fd, data + total, len - total);
276                 } while ((ret == -1) && (errno == EINTR));
277
278                 if (ret <= 0) {
279                         /* EOF or error */
280                         return ERROR_DNS_SOCKET_ERROR;
281                 }
282
283                 total += ret;
284         }
285
286         return ERROR_DNS_SUCCESS;
287 }
288
289 static DNS_ERROR dns_receive_tcp(TALLOC_CTX *mem_ctx,
290                                  struct dns_connection *conn,
291                                  struct dns_buffer **presult)
292 {
293         struct dns_buffer *buf;
294         DNS_ERROR err;
295         uint16_t len;
296
297         if (!(buf = talloc_zero(mem_ctx, struct dns_buffer))) {
298                 return ERROR_DNS_NO_MEMORY;
299         }
300
301         err = read_all(conn->s, (uint8_t *)&len, sizeof(len));
302         if (!ERR_DNS_IS_OK(err)) {
303                 return err;
304         }
305
306         buf->size = ntohs(len);
307
308         if (buf->size == 0) {
309                 *presult = buf;
310                 return ERROR_DNS_SUCCESS;
311         }
312
313         if (!(buf->data = talloc_array(buf, uint8_t, buf->size))) {
314                 TALLOC_FREE(buf);
315                 return ERROR_DNS_NO_MEMORY;
316         }
317
318         err = read_all(conn->s, buf->data, talloc_get_size(buf->data));
319         if (!ERR_DNS_IS_OK(err)) {
320                 TALLOC_FREE(buf);
321                 return err;
322         }
323
324         *presult = buf;
325         return ERROR_DNS_SUCCESS;
326 }
327
328 static DNS_ERROR dns_receive_udp(TALLOC_CTX *mem_ctx,
329                                  struct dns_connection *conn,
330                                  struct dns_buffer **presult)
331 {
332         struct dns_buffer *buf;
333         ssize_t received;
334
335         if (!(buf = talloc_zero(mem_ctx, struct dns_buffer))) {
336                 return ERROR_DNS_NO_MEMORY;
337         }
338
339         /*
340          * UDP based DNS can only be 512 bytes
341          */
342
343         if (!(buf->data = talloc_array(buf, uint8_t, 512))) {
344                 TALLOC_FREE(buf);
345                 return ERROR_DNS_NO_MEMORY;
346         }
347
348         do {
349                 received = recv(conn->s, (void *)buf->data, 512, 0);
350         } while ((received == -1) && (errno == EINTR));
351
352         if (received == -1) {
353                 TALLOC_FREE(buf);
354                 return ERROR_DNS_SOCKET_ERROR;
355         }
356
357         if (received > 512) {
358                 TALLOC_FREE(buf);
359                 return ERROR_DNS_BAD_RESPONSE;
360         }
361
362         buf->size = received;
363         buf->offset = 0;
364
365         *presult = buf;
366         return ERROR_DNS_SUCCESS;
367 }
368
369 DNS_ERROR dns_receive(TALLOC_CTX *mem_ctx, struct dns_connection *conn,
370                       struct dns_buffer **presult)
371 {
372         if (conn->hType == DNS_TCP) {
373                 return dns_receive_tcp(mem_ctx, conn, presult);
374         }
375
376         if (conn->hType == DNS_UDP) {
377                 return dns_receive_udp(mem_ctx, conn, presult);
378         }
379
380         return ERROR_DNS_INVALID_PARAMETER;
381 }
382
383 DNS_ERROR dns_transaction(TALLOC_CTX *mem_ctx, struct dns_connection *conn,
384                           const struct dns_request *req,
385                           struct dns_request **resp)
386 {
387         struct dns_buffer *buf = NULL;
388         DNS_ERROR err;
389
390         err = dns_marshall_request(mem_ctx, req, &buf);
391         if (!ERR_DNS_IS_OK(err)) goto error;
392
393         err = dns_send(conn, buf);
394         if (!ERR_DNS_IS_OK(err)) goto error;
395         TALLOC_FREE(buf);
396
397         err = dns_receive(mem_ctx, conn, &buf);
398         if (!ERR_DNS_IS_OK(err)) goto error;
399
400         err = dns_unmarshall_request(mem_ctx, buf, resp);
401
402  error:
403         TALLOC_FREE(buf);
404         return err;
405 }
406
407 DNS_ERROR dns_update_transaction(TALLOC_CTX *mem_ctx,
408                                  struct dns_connection *conn,
409                                  struct dns_update_request *up_req,
410                                  struct dns_update_request **up_resp)
411 {
412         struct dns_request *resp;
413         DNS_ERROR err;
414
415         err = dns_transaction(mem_ctx, conn, dns_update2request(up_req),
416                               &resp);
417
418         if (!ERR_DNS_IS_OK(err)) return err;
419
420         *up_resp = dns_request2update(resp);
421         return ERROR_DNS_SUCCESS;
422 }