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