dns: The QCLASS is called IN, not IP
[Samba/gebeck_regimport.git] / source4 / lib / socket / connect_multi.c
blob96277a6139518f9f5172082e94453caf76c2bbab
1 /*
2 Unix SMB/CIFS implementation.
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.
8 Copyright (C) Volker Lendecke 2005
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.
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.
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/>.
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"
30 #define MULTI_PORT_DELAY 2000 /* microseconds */
33 overall state
35 struct connect_multi_state {
36 struct socket_address *server_address;
37 int num_ports;
38 uint16_t *ports;
40 struct socket_context *sock;
41 uint16_t result_port;
43 int num_connects_sent, num_connects_recv;
47 state of an individual socket_connect_send() call
49 struct connect_one_state {
50 struct composite_context *result;
51 struct socket_context *sock;
52 struct socket_address *addr;
55 static void continue_resolve_name(struct composite_context *creq);
56 static void connect_multi_timer(struct tevent_context *ev,
57 struct tevent_timer *te,
58 struct timeval tv, void *p);
59 static void connect_multi_next_socket(struct composite_context *result);
60 static void continue_one(struct composite_context *creq);
63 setup an async socket_connect, with multiple ports
65 _PUBLIC_ struct composite_context *socket_connect_multi_send(
66 TALLOC_CTX *mem_ctx,
67 const char *server_name,
68 int num_server_ports,
69 uint16_t *server_ports,
70 struct resolve_context *resolve_ctx,
71 struct tevent_context *event_ctx)
73 struct composite_context *result;
74 struct connect_multi_state *multi;
75 int i;
77 struct nbt_name name;
78 struct composite_context *creq;
80 result = talloc_zero(mem_ctx, struct composite_context);
81 if (result == NULL) return NULL;
82 result->state = COMPOSITE_STATE_IN_PROGRESS;
83 result->event_ctx = event_ctx;
85 multi = talloc_zero(result, struct connect_multi_state);
86 if (composite_nomem(multi, result)) goto failed;
87 result->private_data = multi;
89 multi->num_ports = num_server_ports;
90 multi->ports = talloc_array(multi, uint16_t, multi->num_ports);
91 if (composite_nomem(multi->ports, result)) goto failed;
93 for (i=0; i<multi->num_ports; i++) {
94 multi->ports[i] = server_ports[i];
97 /*
98 we don't want to do the name resolution separately
99 for each port, so start it now, then only start on
100 the real sockets once we have an IP
102 make_nbt_name_server(&name, server_name);
104 creq = resolve_name_all_send(resolve_ctx, multi, 0, multi->ports[0], &name, result->event_ctx);
105 if (composite_nomem(creq, result)) goto failed;
107 composite_continue(result, creq, continue_resolve_name, result);
109 return result;
112 failed:
113 composite_error(result, result->status);
114 return result;
118 start connecting to the next socket/port in the list
120 static void connect_multi_next_socket(struct composite_context *result)
122 struct connect_multi_state *multi = talloc_get_type(result->private_data,
123 struct connect_multi_state);
124 struct connect_one_state *state;
125 struct composite_context *creq;
126 int next = multi->num_connects_sent;
128 if (next == multi->num_ports) {
129 /* don't do anything, just wait for the existing ones to finish */
130 return;
133 multi->num_connects_sent += 1;
135 state = talloc(multi, struct connect_one_state);
136 if (composite_nomem(state, result)) return;
138 state->result = result;
139 result->status = socket_create(multi->server_address->family, SOCKET_TYPE_STREAM, &state->sock, 0);
140 if (!composite_is_ok(result)) return;
142 state->addr = socket_address_copy(state, multi->server_address);
143 if (composite_nomem(state->addr, result)) return;
145 socket_address_set_port(state->addr, multi->ports[next]);
147 talloc_steal(state, state->sock);
149 creq = socket_connect_send(state->sock, NULL,
150 state->addr, 0,
151 result->event_ctx);
152 if (composite_nomem(creq, result)) return;
153 talloc_steal(state, creq);
155 composite_continue(result, creq, continue_one, state);
157 /* if there are more ports to go then setup a timer to fire when we have waited
158 for a couple of milli-seconds, when that goes off we try the next port regardless
159 of whether this port has completed */
160 if (multi->num_ports > multi->num_connects_sent) {
161 /* note that this timer is a child of the single
162 connect attempt state, so it will go away when this
163 request completes */
164 tevent_add_timer(result->event_ctx, state,
165 timeval_current_ofs_usec(MULTI_PORT_DELAY),
166 connect_multi_timer, result);
171 a timer has gone off telling us that we should try the next port
173 static void connect_multi_timer(struct tevent_context *ev,
174 struct tevent_timer *te,
175 struct timeval tv, void *p)
177 struct composite_context *result = talloc_get_type(p, struct composite_context);
178 connect_multi_next_socket(result);
183 recv name resolution reply then send the next connect
185 static void continue_resolve_name(struct composite_context *creq)
187 struct composite_context *result = talloc_get_type(creq->async.private_data,
188 struct composite_context);
189 struct connect_multi_state *multi = talloc_get_type(result->private_data,
190 struct connect_multi_state);
191 struct socket_address **addr;
193 result->status = resolve_name_all_recv(creq, multi, &addr, NULL);
194 if (!composite_is_ok(result)) return;
196 /* Let's just go for the first for now */
197 multi->server_address = addr[0];
199 connect_multi_next_socket(result);
203 one of our socket_connect_send() calls hash finished. If it got a
204 connection or there are none left then we are done
206 static void continue_one(struct composite_context *creq)
208 struct connect_one_state *state = talloc_get_type(creq->async.private_data,
209 struct connect_one_state);
210 struct composite_context *result = state->result;
211 struct connect_multi_state *multi = talloc_get_type(result->private_data,
212 struct connect_multi_state);
213 NTSTATUS status;
214 multi->num_connects_recv++;
216 status = socket_connect_recv(creq);
218 if (NT_STATUS_IS_OK(status)) {
219 multi->sock = talloc_steal(multi, state->sock);
220 multi->result_port = state->addr->port;
223 talloc_free(state);
225 if (NT_STATUS_IS_OK(status) ||
226 multi->num_connects_recv == multi->num_ports) {
227 result->status = status;
228 composite_done(result);
229 return;
232 /* try the next port */
233 connect_multi_next_socket(result);
237 async recv routine for socket_connect_multi()
239 _PUBLIC_ NTSTATUS socket_connect_multi_recv(struct composite_context *ctx,
240 TALLOC_CTX *mem_ctx,
241 struct socket_context **sock,
242 uint16_t *port)
244 NTSTATUS status = composite_wait(ctx);
245 if (NT_STATUS_IS_OK(status)) {
246 struct connect_multi_state *multi =
247 talloc_get_type(ctx->private_data,
248 struct connect_multi_state);
249 *sock = talloc_steal(mem_ctx, multi->sock);
250 *port = multi->result_port;
252 talloc_free(ctx);
253 return status;
256 NTSTATUS socket_connect_multi(TALLOC_CTX *mem_ctx,
257 const char *server_address,
258 int num_server_ports, uint16_t *server_ports,
259 struct resolve_context *resolve_ctx,
260 struct tevent_context *event_ctx,
261 struct socket_context **result,
262 uint16_t *result_port)
264 struct composite_context *ctx =
265 socket_connect_multi_send(mem_ctx, server_address,
266 num_server_ports, server_ports,
267 resolve_ctx,
268 event_ctx);
269 return socket_connect_multi_recv(ctx, mem_ctx, result, result_port);