s4:lib/socket: we need to lookup the #20 netbios name when we connect to a remote...
[samba.git] / source4 / lib / socket / connect_multi.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Fire connect requests to a host and a number of ports, with a timeout
5    between the connect request. Return if the first connect comes back
6    successfully or return the last error.
7
8    Copyright (C) Volker Lendecke 2005
9    
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 3 of the License, or
13    (at your option) any later version.
14    
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19    
20    You should have received a copy of the GNU General Public License
21    along with this program.  If not, see <http://www.gnu.org/licenses/>.
22 */
23
24 #include "includes.h"
25 #include "lib/socket/socket.h"
26 #include "lib/events/events.h"
27 #include "libcli/composite/composite.h"
28 #include "libcli/resolve/resolve.h"
29
30 #define MULTI_PORT_DELAY 2000 /* microseconds */
31
32 /*
33   overall state
34 */
35 struct connect_multi_state {
36         const char *server_address;
37         int num_ports;
38         uint16_t *ports;
39
40         struct resolve_context *resolve_ctx;
41
42         struct socket_context *sock;
43         uint16_t result_port;
44
45         int num_connects_sent, num_connects_recv;
46 };
47
48 /*
49   state of an individual socket_connect_send() call
50 */
51 struct connect_one_state {
52         struct composite_context *result;
53         struct socket_context *sock;
54         struct socket_address *addr;
55 };
56
57 static void continue_resolve_name(struct composite_context *creq);
58 static void connect_multi_timer(struct event_context *ev,
59                                     struct timed_event *te,
60                                     struct timeval tv, void *p);
61 static void connect_multi_next_socket(struct composite_context *result);
62 static void continue_one(struct composite_context *creq);
63
64 /*
65   setup an async socket_connect, with multiple ports
66 */
67 _PUBLIC_ struct composite_context *socket_connect_multi_send(
68                                                     TALLOC_CTX *mem_ctx,
69                                                     const char *server_address,
70                                                     int num_server_ports,
71                                                     uint16_t *server_ports,
72                                                     struct resolve_context *resolve_ctx,
73                                                     struct event_context *event_ctx)
74 {
75         struct composite_context *result;
76         struct connect_multi_state *multi;
77         int i;
78
79         result = talloc_zero(mem_ctx, struct composite_context);
80         if (result == NULL) return NULL;
81         result->state = COMPOSITE_STATE_IN_PROGRESS;
82         result->event_ctx = event_ctx;
83
84         multi = talloc_zero(result, struct connect_multi_state);
85         if (composite_nomem(multi, result)) goto failed;
86         result->private_data = multi;
87
88         multi->server_address = talloc_strdup(multi, server_address);
89         if (composite_nomem(multi->server_address, result)) goto failed;
90
91         multi->num_ports = num_server_ports;
92         multi->resolve_ctx = talloc_reference(multi, resolve_ctx);
93         multi->ports = talloc_array(multi, uint16_t, multi->num_ports);
94         if (composite_nomem(multi->ports, result)) goto failed;
95
96         for (i=0; i<multi->num_ports; i++) {
97                 multi->ports[i] = server_ports[i];
98         }
99
100         if (!is_ipaddress(server_address)) {
101                 /*  
102                     we don't want to do the name resolution separately
103                     for each port, so start it now, then only start on
104                     the real sockets once we have an IP
105                  */
106                 struct nbt_name name;
107                 struct composite_context *creq;
108                 make_nbt_name_server(&name, server_address);
109                 creq = resolve_name_send(resolve_ctx, &name, result->event_ctx);
110                 if (composite_nomem(creq, result)) goto failed;
111                 composite_continue(result, creq, continue_resolve_name, result);
112                 return result;
113         }
114
115         /* now we've setup the state we can process the first socket */
116         connect_multi_next_socket(result);
117
118         if (!NT_STATUS_IS_OK(result->status)) {
119                 goto failed;
120         }
121
122         return result;
123
124  failed:
125         composite_error(result, result->status);
126         return result;
127 }
128
129 /*
130   start connecting to the next socket/port in the list
131 */
132 static void connect_multi_next_socket(struct composite_context *result)
133 {
134         struct connect_multi_state *multi = talloc_get_type(result->private_data, 
135                                                             struct connect_multi_state);
136         struct connect_one_state *state;
137         struct composite_context *creq;
138         int next = multi->num_connects_sent;
139
140         if (next == multi->num_ports) {
141                 /* don't do anything, just wait for the existing ones to finish */
142                 return;
143         }
144
145         multi->num_connects_sent += 1;
146
147         state = talloc(multi, struct connect_one_state);
148         if (composite_nomem(state, result)) return;
149
150         state->result = result;
151         result->status = socket_create("ipv4", SOCKET_TYPE_STREAM, &state->sock, 0);
152         if (!composite_is_ok(result)) return;
153
154         /* Form up the particular address we are interested in */
155         state->addr = socket_address_from_strings(state, state->sock->backend_name, 
156                                                   multi->server_address, multi->ports[next]);
157         if (composite_nomem(state->addr, result)) return;
158
159         talloc_steal(state, state->sock);
160
161         creq = socket_connect_send(state->sock, NULL, 
162                                    state->addr, 0, multi->resolve_ctx, 
163                                    result->event_ctx);
164         if (composite_nomem(creq, result)) return;
165         talloc_steal(state, creq);
166
167         composite_continue(result, creq, continue_one, state);
168
169         /* if there are more ports to go then setup a timer to fire when we have waited
170            for a couple of milli-seconds, when that goes off we try the next port regardless
171            of whether this port has completed */
172         if (multi->num_ports > multi->num_connects_sent) {
173                 /* note that this timer is a child of the single
174                    connect attempt state, so it will go away when this
175                    request completes */
176                 event_add_timed(result->event_ctx, state,
177                                 timeval_current_ofs(0, MULTI_PORT_DELAY),
178                                 connect_multi_timer, result);
179         }
180 }
181
182 /*
183   a timer has gone off telling us that we should try the next port
184 */
185 static void connect_multi_timer(struct event_context *ev,
186                                 struct timed_event *te,
187                                 struct timeval tv, void *p)
188 {
189         struct composite_context *result = talloc_get_type(p, struct composite_context);
190         connect_multi_next_socket(result);
191 }
192
193
194 /*
195   recv name resolution reply then send the next connect
196 */
197 static void continue_resolve_name(struct composite_context *creq)
198 {
199         struct composite_context *result = talloc_get_type(creq->async.private_data, 
200                                                            struct composite_context);
201         struct connect_multi_state *multi = talloc_get_type(result->private_data, 
202                                                             struct connect_multi_state);
203         const char *addr;
204
205         result->status = resolve_name_recv(creq, multi, &addr);
206         if (!composite_is_ok(result)) return;
207
208         multi->server_address = addr;
209
210         connect_multi_next_socket(result);
211 }
212
213 /*
214   one of our socket_connect_send() calls hash finished. If it got a
215   connection or there are none left then we are done
216 */
217 static void continue_one(struct composite_context *creq)
218 {
219         struct connect_one_state *state = talloc_get_type(creq->async.private_data, 
220                                                           struct connect_one_state);
221         struct composite_context *result = state->result;
222         struct connect_multi_state *multi = talloc_get_type(result->private_data, 
223                                                             struct connect_multi_state);
224         NTSTATUS status;
225         multi->num_connects_recv++;
226
227         status = socket_connect_recv(creq);
228
229         if (NT_STATUS_IS_OK(status)) {
230                 multi->sock = talloc_steal(multi, state->sock);
231                 multi->result_port = state->addr->port;
232         }
233
234         talloc_free(state);
235
236         if (NT_STATUS_IS_OK(status) || 
237             multi->num_connects_recv == multi->num_ports) {
238                 result->status = status;
239                 composite_done(result);
240                 return;
241         }
242
243         /* try the next port */
244         connect_multi_next_socket(result);
245 }
246
247 /*
248   async recv routine for socket_connect_multi()
249  */
250 _PUBLIC_ NTSTATUS socket_connect_multi_recv(struct composite_context *ctx,
251                                    TALLOC_CTX *mem_ctx,
252                                    struct socket_context **sock,
253                                    uint16_t *port)
254 {
255         NTSTATUS status = composite_wait(ctx);
256         if (NT_STATUS_IS_OK(status)) {
257                 struct connect_multi_state *multi =
258                         talloc_get_type(ctx->private_data,
259                                         struct connect_multi_state);
260                 *sock = talloc_steal(mem_ctx, multi->sock);
261                 *port = multi->result_port;
262         }
263         talloc_free(ctx);
264         return status;
265 }
266
267 NTSTATUS socket_connect_multi(TALLOC_CTX *mem_ctx,
268                               const char *server_address,
269                               int num_server_ports, uint16_t *server_ports,
270                               struct resolve_context *resolve_ctx,
271                               struct event_context *event_ctx,
272                               struct socket_context **result,
273                               uint16_t *result_port)
274 {
275         struct composite_context *ctx =
276                 socket_connect_multi_send(mem_ctx, server_address,
277                                           num_server_ports, server_ports,
278                                           resolve_ctx,
279                                           event_ctx);
280         return socket_connect_multi_recv(ctx, mem_ctx, result, result_port);
281 }