root/source4/lib/socket/connect_multi.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. socket_connect_multi_send
  2. connect_multi_next_socket
  3. connect_multi_timer
  4. continue_resolve_name
  5. continue_one
  6. socket_connect_multi_recv
  7. socket_connect_multi

   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 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 */
  49 struct connect_one_state {
  50         struct composite_context *result;
  51         struct socket_context *sock;
  52         struct socket_address *addr;
  53 };
  54 
  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);
  61 
  62 /*
  63   setup an async socket_connect, with multiple ports
  64 */
  65 _PUBLIC_ struct composite_context *socket_connect_multi_send(
     /* [<][>][^][v][top][bottom][index][help] */
  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, &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 */
 129 static void connect_multi_next_socket(struct composite_context *result)
     /* [<][>][^][v][top][bottom][index][help] */
 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 */
 182 static void connect_multi_timer(struct tevent_context *ev,
     /* [<][>][^][v][top][bottom][index][help] */
 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 */
 194 static void continue_resolve_name(struct composite_context *creq)
     /* [<][>][^][v][top][bottom][index][help] */
 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 */
 214 static void continue_one(struct composite_context *creq)
     /* [<][>][^][v][top][bottom][index][help] */
 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,
     /* [<][>][^][v][top][bottom][index][help] */
 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 
 264 NTSTATUS socket_connect_multi(TALLOC_CTX *mem_ctx,
     /* [<][>][^][v][top][bottom][index][help] */
 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 }

/* [<][>][^][v][top][bottom][index][help] */