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