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