r23799: updated old Franklin Street FSF addresses to new URL
[kai/samba.git] / source / 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         RecvAddr.sin_family = AF_INET;
122         RecvAddr.sin_port = htons( DNS_UDP_PORT );
123         RecvAddr.sin_addr.s_addr = ulAddress;
124
125         conn->hType = DNS_UDP;
126         memcpy( &conn->RecvAddr, &RecvAddr, sizeof( struct sockaddr_in ) );
127
128         *result = conn;
129         return ERROR_DNS_SUCCESS;
130 }
131
132 /********************************************************************
133 ********************************************************************/
134
135 DNS_ERROR dns_open_connection( const char *nameserver, int32 dwType,
136                     TALLOC_CTX *mem_ctx,
137                     struct dns_connection **conn )
138 {
139         switch ( dwType ) {
140         case DNS_TCP:
141                 return dns_tcp_open( nameserver, mem_ctx, conn );
142         case DNS_UDP:
143                 return dns_udp_open( nameserver, mem_ctx, conn );
144         }
145         
146         return ERROR_DNS_INVALID_PARAMETER;
147 }
148
149 static DNS_ERROR write_all(int fd, uint8 *data, size_t len)
150 {
151         size_t total = 0;
152
153         while (total < len) {
154
155                 ssize_t ret = write(fd, data + total, len - total);
156
157                 if (ret <= 0) {
158                         /*
159                          * EOF or error
160                          */
161                         return ERROR_DNS_SOCKET_ERROR;
162                 }
163
164                 total += ret;
165         }
166
167         return ERROR_DNS_SUCCESS;
168 }
169
170 static DNS_ERROR dns_send_tcp(struct dns_connection *conn,
171                               const struct dns_buffer *buf)
172 {
173         uint16 len = htons(buf->offset);
174         DNS_ERROR err;
175
176         err = write_all(conn->s, (uint8 *)&len, sizeof(len));
177         if (!ERR_DNS_IS_OK(err)) return err;
178
179         return write_all(conn->s, buf->data, buf->offset);
180 }
181
182 static DNS_ERROR dns_send_udp(struct dns_connection *conn,
183                               const struct dns_buffer *buf)
184 {
185         ssize_t ret;
186
187         ret = sendto(conn->s, buf->data, buf->offset, 0,
188                      (struct sockaddr *)&conn->RecvAddr,
189                      sizeof(conn->RecvAddr));
190
191         if (ret != buf->offset) {
192                 return ERROR_DNS_SOCKET_ERROR;
193         }
194
195         return ERROR_DNS_SUCCESS;
196 }
197
198 DNS_ERROR dns_send(struct dns_connection *conn, const struct dns_buffer *buf)
199 {
200         if (conn->hType == DNS_TCP) {
201                 return dns_send_tcp(conn, buf);
202         }
203
204         if (conn->hType == DNS_UDP) {
205                 return dns_send_udp(conn, buf);
206         }
207
208         return ERROR_DNS_INVALID_PARAMETER;
209 }
210
211 static DNS_ERROR read_all(int fd, uint8 *data, size_t len)
212 {
213         size_t total = 0;
214         fd_set rfds;
215         struct timeval tv;
216
217         while (total < len) {
218                 ssize_t ret;
219                 int fd_ready;
220                 
221                 FD_ZERO( &rfds );
222                 FD_SET( fd, &rfds );
223
224                 /* 10 second timeout */
225                 tv.tv_sec = 10;
226                 tv.tv_usec = 0;
227                 
228                 fd_ready = select( fd+1, &rfds, NULL, NULL, &tv );
229                 if ( fd_ready == 0 ) {
230                         /* read timeout */
231                         return ERROR_DNS_SOCKET_ERROR;
232                 }
233
234                 ret = read(fd, data + total, len - total);
235                 if (ret <= 0) {
236                         /* EOF or error */
237                         return ERROR_DNS_SOCKET_ERROR;
238                 }
239
240                 total += ret;
241         }
242
243         return ERROR_DNS_SUCCESS;
244 }
245
246 static DNS_ERROR dns_receive_tcp(TALLOC_CTX *mem_ctx,
247                                  struct dns_connection *conn,
248                                  struct dns_buffer **presult)
249 {
250         struct dns_buffer *buf;
251         DNS_ERROR err;
252         uint16 len;
253
254         if (!(buf = TALLOC_ZERO_P(mem_ctx, struct dns_buffer))) {
255                 return ERROR_DNS_NO_MEMORY;
256         }
257
258         err = read_all(conn->s, (uint8 *)&len, sizeof(len));
259         if (!ERR_DNS_IS_OK(err)) {
260                 return err;
261         }
262
263         buf->size = ntohs(len);
264
265         if (buf->size) {
266                 if (!(buf->data = TALLOC_ARRAY(buf, uint8, buf->size))) {
267                         TALLOC_FREE(buf);
268                         return ERROR_DNS_NO_MEMORY;
269                 }
270         } else {
271                 buf->data = NULL;
272         }
273
274         err = read_all(conn->s, buf->data, buf->size);
275         if (!ERR_DNS_IS_OK(err)) {
276                 TALLOC_FREE(buf);
277                 return err;
278         }
279
280         *presult = buf;
281         return ERROR_DNS_SUCCESS;
282 }
283
284 static DNS_ERROR dns_receive_udp(TALLOC_CTX *mem_ctx,
285                                  struct dns_connection *conn,
286                                  struct dns_buffer **presult)
287 {
288         struct dns_buffer *buf;
289         ssize_t received;
290
291         if (!(buf = TALLOC_ZERO_P(mem_ctx, struct dns_buffer))) {
292                 return ERROR_DNS_NO_MEMORY;
293         }
294
295         /*
296          * UDP based DNS can only be 512 bytes
297          */
298
299         if (!(buf->data = TALLOC_ARRAY(buf, uint8, 512))) {
300                 TALLOC_FREE(buf);
301                 return ERROR_DNS_NO_MEMORY;
302         }
303
304         received = recv(conn->s, (void *)buf->data, 512, 0);
305
306         if (received == -1) {
307                 TALLOC_FREE(buf);
308                 return ERROR_DNS_SOCKET_ERROR;
309         }
310
311         if (received > 512) {
312                 TALLOC_FREE(buf);
313                 return ERROR_DNS_BAD_RESPONSE;
314         }
315
316         buf->size = received;
317         buf->offset = 0;
318
319         *presult = buf;
320         return ERROR_DNS_SUCCESS;
321 }
322
323 DNS_ERROR dns_receive(TALLOC_CTX *mem_ctx, struct dns_connection *conn,
324                       struct dns_buffer **presult)
325 {
326         if (conn->hType == DNS_TCP) {
327                 return dns_receive_tcp(mem_ctx, conn, presult);
328         }
329
330         if (conn->hType == DNS_UDP) {
331                 return dns_receive_udp(mem_ctx, conn, presult);
332         }
333
334         return ERROR_DNS_INVALID_PARAMETER;
335 }
336
337 DNS_ERROR dns_transaction(TALLOC_CTX *mem_ctx, struct dns_connection *conn,
338                           const struct dns_request *req,
339                           struct dns_request **resp)
340 {
341         struct dns_buffer *buf = NULL;
342         DNS_ERROR err;
343
344         err = dns_marshall_request(conn, req, &buf);
345         if (!ERR_DNS_IS_OK(err)) goto error;
346
347         err = dns_send(conn, buf);
348         if (!ERR_DNS_IS_OK(err)) goto error;
349         TALLOC_FREE(buf);
350
351         err = dns_receive(mem_ctx, conn, &buf);
352         if (!ERR_DNS_IS_OK(err)) goto error;
353
354         err = dns_unmarshall_request(mem_ctx, buf, resp);
355
356  error:
357         TALLOC_FREE(buf);
358         return err;
359 }
360
361 DNS_ERROR dns_update_transaction(TALLOC_CTX *mem_ctx,
362                                  struct dns_connection *conn,
363                                  struct dns_update_request *up_req,
364                                  struct dns_update_request **up_resp)
365 {
366         struct dns_request *resp;
367         DNS_ERROR err;
368
369         err = dns_transaction(mem_ctx, conn, dns_update2request(up_req),
370                               &resp);
371
372         if (!ERR_DNS_IS_OK(err)) return err;
373
374         *up_resp = dns_request2update(resp);
375         return ERROR_DNS_SUCCESS;
376 }