Merge branch 'master' of ssh://git.samba.org/data/git/samba
[ira/wip.git] / source4 / libcli / resolve / host.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    async gethostbyname() name resolution module
5
6    Copyright (C) Andrew Tridgell 2005
7    
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 /*
23   this module uses a fork() per gethostbyname() call. At first that
24   might seem crazy, but it is actually very fast, and solves many of
25   the tricky problems of keeping a child hanging around in a library
26   (like what happens when the parent forks). We use a talloc
27   destructor to ensure that the child is cleaned up when we have
28   finished with this name resolution.
29 */
30
31 #include "includes.h"
32 #include "lib/events/events.h"
33 #include "system/network.h"
34 #include "system/filesys.h"
35 #include "lib/socket/socket.h"
36 #include "libcli/composite/composite.h"
37 #include "librpc/gen_ndr/ndr_nbt.h"
38 #include "libcli/resolve/resolve.h"
39
40 struct host_state {
41         struct nbt_name name;
42         struct socket_address **addrs;
43         pid_t child;
44         int child_fd;
45         struct fd_event *fde;
46         struct event_context *event_ctx;
47 };
48
49
50 /*
51   kill off a wayward child if needed. This allows us to stop an async
52   name resolution without leaving a potentially blocking call running
53   in a child
54 */
55 static int host_destructor(struct host_state *state)
56 {
57         int status;
58
59         kill(state->child, SIGTERM);
60         close(state->child_fd);
61         if (waitpid(state->child, &status, WNOHANG) == 0) {
62                 kill(state->child, SIGKILL);
63                 waitpid(state->child, &status, 0);
64         }
65
66         return 0;
67 }
68
69 /*
70   the blocking child
71 */
72 static void run_child(struct composite_context *c, int fd)
73 {
74         struct host_state *state = talloc_get_type(c->private_data, struct host_state);
75         struct in_addr ip;
76         const char *address;
77
78         /* this is the blocking call we are going to lots of trouble
79            to avoid in the parent */
80         ip = interpret_addr2(state->name.name);
81
82         address = inet_ntoa(ip);
83         if (address != NULL) {
84                 write(fd, address, strlen(address)+1);
85         }
86         close(fd);
87 }
88
89 /*
90   handle a read event on the pipe
91 */
92 static void pipe_handler(struct event_context *ev, struct fd_event *fde, 
93                          uint16_t flags, void *private_data)
94 {
95         struct composite_context *c = talloc_get_type(private_data, struct composite_context);
96         struct host_state *state = talloc_get_type(c->private_data, struct host_state);
97         char address[128];
98         int ret;
99         int status;
100
101         /* if we get any event from the child then we know that we
102            won't need to kill it off */
103         talloc_set_destructor(state, NULL);
104
105         /* yes, we don't care about EAGAIN or other niceities
106            here. They just can't happen with this parent/child
107            relationship, and even if they did then giving an error is
108            the right thing to do */
109         ret = read(state->child_fd, address, sizeof(address)-1);
110         close(state->child_fd);
111         if (waitpid(state->child, &status, WNOHANG) == 0) {
112                 kill(state->child, SIGKILL);
113                 waitpid(state->child, &status, 0);
114         }
115         if (ret <= 0) {
116                 composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND);
117                 return;
118         }
119
120         /* enusre the address looks good */
121         address[ret] = 0;
122         if (strcmp(address, "0.0.0.0") == 0 ||
123             inet_addr(address) == INADDR_NONE) {
124                 composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND);
125                 return;
126         }
127
128         state->addrs = talloc_array(state, struct socket_address *, 2);
129         if (composite_nomem(state->addrs, c)) return;
130
131         state->addrs[0] = socket_address_from_strings(state->addrs,
132                                                       "ipv4",
133                                                       address,
134                                                       0);
135         if (composite_nomem(state->addrs[0], c)) return;
136         state->addrs[1] = NULL;
137
138         composite_done(c);
139 }
140
141 /*
142   gethostbyname name resolution method - async send
143  */
144 struct composite_context *resolve_name_host_send(TALLOC_CTX *mem_ctx,
145                                                  struct event_context *event_ctx,
146                                                  void *privdata,
147                                                  struct nbt_name *name)
148 {
149         struct composite_context *c;
150         struct host_state *state;
151         int fd[2] = { -1, -1 };
152         int ret;
153
154         c = composite_create(mem_ctx, event_ctx);
155         if (c == NULL) return NULL;
156
157         if (composite_nomem(c->event_ctx, c)) return c;
158
159         state = talloc(c, struct host_state);
160         if (composite_nomem(state, c)) return c;
161         c->private_data = state;
162
163         c->status = nbt_name_dup(state, name, &state->name);
164         if (!composite_is_ok(c)) return c;
165
166         /* setup a pipe to chat to our child */
167         ret = pipe(fd);
168         if (ret == -1) {
169                 composite_error(c, map_nt_error_from_unix(errno));
170                 return c;
171         }
172
173         state->child_fd = fd[0];
174         state->event_ctx = c->event_ctx;
175
176         /* we need to put the child in our event context so
177            we know when the gethostbyname() has finished */
178         state->fde = event_add_fd(c->event_ctx, c, state->child_fd, EVENT_FD_READ, 
179                                   pipe_handler, c);
180         if (composite_nomem(state->fde, c)) {
181                 close(fd[0]);
182                 close(fd[1]);
183                 return c;
184         }
185
186         state->child = fork();
187         if (state->child == (pid_t)-1) {
188                 composite_error(c, map_nt_error_from_unix(errno));
189                 return c;
190         }
191
192
193         if (state->child == 0) {
194                 close(fd[0]);
195                 run_child(c, fd[1]);
196                 _exit(0);
197         }
198         close(fd[1]);
199
200         /* cleanup wayward children */
201         talloc_set_destructor(state, host_destructor);
202
203         return c;
204 }
205
206 /*
207   gethostbyname name resolution method - recv side
208 */
209 NTSTATUS resolve_name_host_recv(struct composite_context *c, 
210                                 TALLOC_CTX *mem_ctx,
211                                 struct socket_address ***addrs)
212 {
213         NTSTATUS status;
214
215         status = composite_wait(c);
216
217         if (NT_STATUS_IS_OK(status)) {
218                 struct host_state *state = talloc_get_type(c->private_data, struct host_state);
219                 *addrs = talloc_steal(mem_ctx, state->addrs);
220         }
221
222         talloc_free(c);
223         return status;
224 }
225
226 /*
227   gethostbyname name resolution method - sync call
228  */
229 NTSTATUS resolve_name_host(struct nbt_name *name, 
230                             TALLOC_CTX *mem_ctx,
231                             struct socket_address ***addrs)
232 {
233         struct composite_context *c = resolve_name_host_send(mem_ctx, NULL, NULL, name);
234         return resolve_name_host_recv(c, mem_ctx, addrs);
235 }
236
237 bool resolve_context_add_host_method(struct resolve_context *ctx)
238 {
239         return resolve_context_add_method(ctx, resolve_name_host_send, resolve_name_host_recv,
240                                           NULL);
241 }