r26376: Add context for libcli_resolve.
[jelmer/samba4-debian.git] / source / 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 "libcli/composite/composite.h"
36 #include "librpc/gen_ndr/ndr_nbt.h"
37 #include "libcli/resolve/resolve.h"
38
39 struct host_state {
40         struct nbt_name name;
41         const char *reply_addr;
42         pid_t child;
43         int child_fd;
44         struct fd_event *fde;
45         struct event_context *event_ctx;
46 };
47
48
49 /*
50   kill off a wayward child if needed. This allows us to stop an async
51   name resolution without leaving a potentially blocking call running
52   in a child
53 */
54 static int host_destructor(struct host_state *state)
55 {
56         close(state->child_fd);
57         if (state->child != (pid_t)-1) {
58                 kill(state->child, SIGTERM);
59         }
60         return 0;
61 }
62
63 /*
64   the blocking child
65 */
66 static void run_child(struct composite_context *c, int fd)
67 {
68         struct host_state *state = talloc_get_type(c->private_data, struct host_state);
69         struct in_addr ip;
70         const char *address;
71
72         /* this is the blocking call we are going to lots of trouble
73            to avoid in the parent */
74         ip = interpret_addr2(state->name.name);
75
76         address = inet_ntoa(ip);
77         if (address != NULL) {
78                 write(fd, address, strlen(address)+1);
79         }
80         close(fd);
81 }
82
83 /*
84   handle a read event on the pipe
85 */
86 static void pipe_handler(struct event_context *ev, struct fd_event *fde, 
87                          uint16_t flags, void *private_data)
88 {
89         struct composite_context *c = talloc_get_type(private_data, struct composite_context);
90         struct host_state *state = talloc_get_type(c->private_data, struct host_state);
91         char address[128];
92         int ret;
93
94         /* if we get any event from the child then we know that we
95            won't need to kill it off */
96         state->child = (pid_t)-1;
97
98         /* yes, we don't care about EAGAIN or other niceities
99            here. They just can't happen with this parent/child
100            relationship, and even if they did then giving an error is
101            the right thing to do */
102         ret = read(state->child_fd, address, sizeof(address)-1);
103         if (ret <= 0) {
104                 composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND);
105                 return;
106         }
107
108         /* enusre the address looks good */
109         address[ret] = 0;
110         if (strcmp(address, "0.0.0.0") == 0 ||
111             inet_addr(address) == INADDR_NONE) {
112                 composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND);
113                 return;
114         }
115
116         state->reply_addr = talloc_strdup(state, address);
117         if (composite_nomem(state->reply_addr, c)) return;
118
119         composite_done(c);
120 }
121
122 /*
123   gethostbyname name resolution method - async send
124  */
125 struct composite_context *resolve_name_host_send(TALLOC_CTX *mem_ctx,
126                                                  struct event_context *event_ctx,
127                                                  void *privdata,
128                                                  struct nbt_name *name)
129 {
130         struct composite_context *c;
131         struct host_state *state;
132         int fd[2] = { -1, -1 };
133         int ret;
134
135         c = composite_create(mem_ctx, event_ctx);
136         if (c == NULL) return NULL;
137
138         c->event_ctx = talloc_reference(c, event_ctx);
139         if (composite_nomem(c->event_ctx, c)) return c;
140
141         state = talloc(c, struct host_state);
142         if (composite_nomem(state, c)) return c;
143         c->private_data = state;
144
145         c->status = nbt_name_dup(state, name, &state->name);
146         if (!composite_is_ok(c)) return c;
147
148         /* setup a pipe to chat to our child */
149         ret = pipe(fd);
150         if (ret == -1) {
151                 composite_error(c, map_nt_error_from_unix(errno));
152                 return c;
153         }
154
155         state->child_fd = fd[0];
156         state->event_ctx = c->event_ctx;
157
158         /* we need to put the child in our event context so
159            we know when the gethostbyname() has finished */
160         state->fde = event_add_fd(c->event_ctx, c, state->child_fd, EVENT_FD_READ, 
161                                   pipe_handler, c);
162         if (composite_nomem(state->fde, c)) {
163                 close(fd[0]);
164                 close(fd[1]);
165                 return c;
166         }
167
168         /* signal handling in posix really sucks - doing this in a library
169            affects the whole app, but what else to do?? */
170         signal(SIGCHLD, SIG_IGN);
171
172         state->child = fork();
173         if (state->child == (pid_t)-1) {
174                 composite_error(c, map_nt_error_from_unix(errno));
175                 return c;
176         }
177
178
179         if (state->child == 0) {
180                 close(fd[0]);
181                 run_child(c, fd[1]);
182                 _exit(0);
183         }
184         close(fd[1]);
185
186         /* cleanup wayward children */
187         talloc_set_destructor(state, host_destructor);
188
189         return c;
190 }
191
192 /*
193   gethostbyname name resolution method - recv side
194 */
195 NTSTATUS resolve_name_host_recv(struct composite_context *c, 
196                                 TALLOC_CTX *mem_ctx, const char **reply_addr)
197 {
198         NTSTATUS status;
199
200         status = composite_wait(c);
201
202         if (NT_STATUS_IS_OK(status)) {
203                 struct host_state *state = talloc_get_type(c->private_data, struct host_state);
204                 *reply_addr = talloc_steal(mem_ctx, state->reply_addr);
205         }
206
207         talloc_free(c);
208         return status;
209 }
210
211 /*
212   gethostbyname name resolution method - sync call
213  */
214 NTSTATUS resolve_name_host(struct nbt_name *name, 
215                             TALLOC_CTX *mem_ctx,
216                             const char **reply_addr)
217 {
218         struct composite_context *c = resolve_name_host_send(mem_ctx, NULL, NULL, name);
219         return resolve_name_host_recv(c, mem_ctx, reply_addr);
220 }
221
222 bool resolve_context_add_host_method(struct resolve_context *ctx)
223 {
224         return resolve_context_add_method(ctx, resolve_name_host_send, resolve_name_host_recv,
225                                           NULL);
226 }