source: branches/samba-3.5.x/source4/lib/socket/connect_multi.c

Last change on this file was 414, checked in by Herwig Bauernfeind, 15 years ago

Samba 3.5.0: Initial import

File size: 8.2 KB
Line 
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*/
35struct connect_multi_state {
36 const char *server_address;
37 int num_ports;
38 uint16_t *ports;
39
40 struct socket_context *sock;
41 uint16_t result_port;
42
43 int num_connects_sent, num_connects_recv;
44};
45
46/*
47 state of an individual socket_connect_send() call
48*/
49struct connect_one_state {
50 struct composite_context *result;
51 struct socket_context *sock;
52 struct socket_address *addr;
53};
54
55static void continue_resolve_name(struct composite_context *creq);
56static void connect_multi_timer(struct tevent_context *ev,
57 struct tevent_timer *te,
58 struct timeval tv, void *p);
59static void connect_multi_next_socket(struct composite_context *result);
60static void continue_one(struct composite_context *creq);
61
62/*
63 setup an async socket_connect, with multiple ports
64*/
65_PUBLIC_ struct composite_context *socket_connect_multi_send(
66 TALLOC_CTX *mem_ctx,
67 const char *server_address,
68 int num_server_ports,
69 uint16_t *server_ports,
70 struct resolve_context *resolve_ctx,
71 struct tevent_context *event_ctx)
72{
73 struct composite_context *result;
74 struct connect_multi_state *multi;
75 int i;
76
77 result = talloc_zero(mem_ctx, struct composite_context);
78 if (result == NULL) return NULL;
79 result->state = COMPOSITE_STATE_IN_PROGRESS;
80 result->event_ctx = event_ctx;
81
82 multi = talloc_zero(result, struct connect_multi_state);
83 if (composite_nomem(multi, result)) goto failed;
84 result->private_data = multi;
85
86 multi->server_address = talloc_strdup(multi, server_address);
87 if (composite_nomem(multi->server_address, result)) goto failed;
88
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;
92
93 for (i=0; i<multi->num_ports; i++) {
94 multi->ports[i] = server_ports[i];
95 }
96
97 if (!is_ipaddress(server_address)) {
98 /*
99 we don't want to do the name resolution separately
100 for each port, so start it now, then only start on
101 the real sockets once we have an IP
102 */
103 struct nbt_name name;
104 struct composite_context *creq;
105 make_nbt_name_server(&name, server_address);
106 creq = resolve_name_send(resolve_ctx, multi, &name, result->event_ctx);
107 if (composite_nomem(creq, result)) goto failed;
108 composite_continue(result, creq, continue_resolve_name, result);
109 return result;
110 }
111
112 /* now we've setup the state we can process the first socket */
113 connect_multi_next_socket(result);
114
115 if (!NT_STATUS_IS_OK(result->status)) {
116 goto failed;
117 }
118
119 return result;
120
121 failed:
122 composite_error(result, result->status);
123 return result;
124}
125
126/*
127 start connecting to the next socket/port in the list
128*/
129static void connect_multi_next_socket(struct composite_context *result)
130{
131 struct connect_multi_state *multi = talloc_get_type(result->private_data,
132 struct connect_multi_state);
133 struct connect_one_state *state;
134 struct composite_context *creq;
135 int next = multi->num_connects_sent;
136
137 if (next == multi->num_ports) {
138 /* don't do anything, just wait for the existing ones to finish */
139 return;
140 }
141
142 multi->num_connects_sent += 1;
143
144 state = talloc(multi, struct connect_one_state);
145 if (composite_nomem(state, result)) return;
146
147 state->result = result;
148 result->status = socket_create("ipv4", SOCKET_TYPE_STREAM, &state->sock, 0);
149 if (!composite_is_ok(result)) return;
150
151 /* Form up the particular address we are interested in */
152 state->addr = socket_address_from_strings(state, state->sock->backend_name,
153 multi->server_address, multi->ports[next]);
154 if (composite_nomem(state->addr, result)) return;
155
156 talloc_steal(state, state->sock);
157
158 creq = socket_connect_send(state->sock, NULL,
159 state->addr, 0,
160 result->event_ctx);
161 if (composite_nomem(creq, result)) return;
162 talloc_steal(state, creq);
163
164 composite_continue(result, creq, continue_one, state);
165
166 /* if there are more ports to go then setup a timer to fire when we have waited
167 for a couple of milli-seconds, when that goes off we try the next port regardless
168 of whether this port has completed */
169 if (multi->num_ports > multi->num_connects_sent) {
170 /* note that this timer is a child of the single
171 connect attempt state, so it will go away when this
172 request completes */
173 event_add_timed(result->event_ctx, state,
174 timeval_current_ofs(0, MULTI_PORT_DELAY),
175 connect_multi_timer, result);
176 }
177}
178
179/*
180 a timer has gone off telling us that we should try the next port
181*/
182static void connect_multi_timer(struct tevent_context *ev,
183 struct tevent_timer *te,
184 struct timeval tv, void *p)
185{
186 struct composite_context *result = talloc_get_type(p, struct composite_context);
187 connect_multi_next_socket(result);
188}
189
190
191/*
192 recv name resolution reply then send the next connect
193*/
194static void continue_resolve_name(struct composite_context *creq)
195{
196 struct composite_context *result = talloc_get_type(creq->async.private_data,
197 struct composite_context);
198 struct connect_multi_state *multi = talloc_get_type(result->private_data,
199 struct connect_multi_state);
200 const char *addr;
201
202 result->status = resolve_name_recv(creq, multi, &addr);
203 if (!composite_is_ok(result)) return;
204
205 multi->server_address = addr;
206
207 connect_multi_next_socket(result);
208}
209
210/*
211 one of our socket_connect_send() calls hash finished. If it got a
212 connection or there are none left then we are done
213*/
214static void continue_one(struct composite_context *creq)
215{
216 struct connect_one_state *state = talloc_get_type(creq->async.private_data,
217 struct connect_one_state);
218 struct composite_context *result = state->result;
219 struct connect_multi_state *multi = talloc_get_type(result->private_data,
220 struct connect_multi_state);
221 NTSTATUS status;
222 multi->num_connects_recv++;
223
224 status = socket_connect_recv(creq);
225
226 if (NT_STATUS_IS_OK(status)) {
227 multi->sock = talloc_steal(multi, state->sock);
228 multi->result_port = state->addr->port;
229 }
230
231 talloc_free(state);
232
233 if (NT_STATUS_IS_OK(status) ||
234 multi->num_connects_recv == multi->num_ports) {
235 result->status = status;
236 composite_done(result);
237 return;
238 }
239
240 /* try the next port */
241 connect_multi_next_socket(result);
242}
243
244/*
245 async recv routine for socket_connect_multi()
246 */
247_PUBLIC_ NTSTATUS socket_connect_multi_recv(struct composite_context *ctx,
248 TALLOC_CTX *mem_ctx,
249 struct socket_context **sock,
250 uint16_t *port)
251{
252 NTSTATUS status = composite_wait(ctx);
253 if (NT_STATUS_IS_OK(status)) {
254 struct connect_multi_state *multi =
255 talloc_get_type(ctx->private_data,
256 struct connect_multi_state);
257 *sock = talloc_steal(mem_ctx, multi->sock);
258 *port = multi->result_port;
259 }
260 talloc_free(ctx);
261 return status;
262}
263
264NTSTATUS socket_connect_multi(TALLOC_CTX *mem_ctx,
265 const char *server_address,
266 int num_server_ports, uint16_t *server_ports,
267 struct resolve_context *resolve_ctx,
268 struct tevent_context *event_ctx,
269 struct socket_context **result,
270 uint16_t *result_port)
271{
272 struct composite_context *ctx =
273 socket_connect_multi_send(mem_ctx, server_address,
274 num_server_ports, server_ports,
275 resolve_ctx,
276 event_ctx);
277 return socket_connect_multi_recv(ctx, mem_ctx, result, result_port);
278}
Note: See TracBrowser for help on using the repository browser.