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