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} 279