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