s3-util: for convenience, provide format comments in tdb_unpack().
[jra/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                 FD_ZERO( &rfds );
223                 FD_SET( fd, &rfds );
224
225                 /* 10 second timeout */
226                 tv.tv_sec = 10;
227                 tv.tv_usec = 0;
228                 
229                 fd_ready = select( fd+1, &rfds, NULL, NULL, &tv );
230                 if ( fd_ready == 0 ) {
231                         /* read timeout */
232                         return ERROR_DNS_SOCKET_ERROR;
233                 }
234
235                 ret = read(fd, data + total, len - total);
236                 if (ret <= 0) {
237                         /* EOF or error */
238                         return ERROR_DNS_SOCKET_ERROR;
239                 }
240
241                 total += ret;
242         }
243
244         return ERROR_DNS_SUCCESS;
245 }
246
247 static DNS_ERROR dns_receive_tcp(TALLOC_CTX *mem_ctx,
248                                  struct dns_connection *conn,
249                                  struct dns_buffer **presult)
250 {
251         struct dns_buffer *buf;
252         DNS_ERROR err;
253         uint16 len;
254
255         if (!(buf = TALLOC_ZERO_P(mem_ctx, struct dns_buffer))) {
256                 return ERROR_DNS_NO_MEMORY;
257         }
258
259         err = read_all(conn->s, (uint8 *)&len, sizeof(len));
260         if (!ERR_DNS_IS_OK(err)) {
261                 return err;
262         }
263
264         buf->size = ntohs(len);
265
266         if (buf->size) {
267                 if (!(buf->data = TALLOC_ARRAY(buf, uint8, buf->size))) {
268                         TALLOC_FREE(buf);
269                         return ERROR_DNS_NO_MEMORY;
270                 }
271         } else {
272                 buf->data = NULL;
273         }
274
275         err = read_all(conn->s, buf->data, buf->size);
276         if (!ERR_DNS_IS_OK(err)) {
277                 TALLOC_FREE(buf);
278                 return err;
279         }
280
281         *presult = buf;
282         return ERROR_DNS_SUCCESS;
283 }
284
285 static DNS_ERROR dns_receive_udp(TALLOC_CTX *mem_ctx,
286                                  struct dns_connection *conn,
287                                  struct dns_buffer **presult)
288 {
289         struct dns_buffer *buf;
290         ssize_t received;
291
292         if (!(buf = TALLOC_ZERO_P(mem_ctx, struct dns_buffer))) {
293                 return ERROR_DNS_NO_MEMORY;
294         }
295
296         /*
297          * UDP based DNS can only be 512 bytes
298          */
299
300         if (!(buf->data = TALLOC_ARRAY(buf, uint8, 512))) {
301                 TALLOC_FREE(buf);
302                 return ERROR_DNS_NO_MEMORY;
303         }
304
305         received = recv(conn->s, (void *)buf->data, 512, 0);
306
307         if (received == -1) {
308                 TALLOC_FREE(buf);
309                 return ERROR_DNS_SOCKET_ERROR;
310         }
311
312         if (received > 512) {
313                 TALLOC_FREE(buf);
314                 return ERROR_DNS_BAD_RESPONSE;
315         }
316
317         buf->size = received;
318         buf->offset = 0;
319
320         *presult = buf;
321         return ERROR_DNS_SUCCESS;
322 }
323
324 DNS_ERROR dns_receive(TALLOC_CTX *mem_ctx, struct dns_connection *conn,
325                       struct dns_buffer **presult)
326 {
327         if (conn->hType == DNS_TCP) {
328                 return dns_receive_tcp(mem_ctx, conn, presult);
329         }
330
331         if (conn->hType == DNS_UDP) {
332                 return dns_receive_udp(mem_ctx, conn, presult);
333         }
334
335         return ERROR_DNS_INVALID_PARAMETER;
336 }
337
338 DNS_ERROR dns_transaction(TALLOC_CTX *mem_ctx, struct dns_connection *conn,
339                           const struct dns_request *req,
340                           struct dns_request **resp)
341 {
342         struct dns_buffer *buf = NULL;
343         DNS_ERROR err;
344
345         err = dns_marshall_request(conn, req, &buf);
346         if (!ERR_DNS_IS_OK(err)) goto error;
347
348         err = dns_send(conn, buf);
349         if (!ERR_DNS_IS_OK(err)) goto error;
350         TALLOC_FREE(buf);
351
352         err = dns_receive(mem_ctx, conn, &buf);
353         if (!ERR_DNS_IS_OK(err)) goto error;
354
355         err = dns_unmarshall_request(mem_ctx, buf, resp);
356
357  error:
358         TALLOC_FREE(buf);
359         return err;
360 }
361
362 DNS_ERROR dns_update_transaction(TALLOC_CTX *mem_ctx,
363                                  struct dns_connection *conn,
364                                  struct dns_update_request *up_req,
365                                  struct dns_update_request **up_resp)
366 {
367         struct dns_request *resp;
368         DNS_ERROR err;
369
370         err = dns_transaction(mem_ctx, conn, dns_update2request(up_req),
371                               &resp);
372
373         if (!ERR_DNS_IS_OK(err)) return err;
374
375         *up_resp = dns_request2update(resp);
376         return ERROR_DNS_SUCCESS;
377 }