2005-10-28 15:02:42 +04:00
/*
Unix SMB / CIFS implementation .
Fire connect requests to a host and a number of ports , with a timeout
between the connect request . Return if the first connect comes back
successfully or return the last error .
Copyright ( C ) Volker Lendecke 2005
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
2007-07-10 06:07:03 +04:00
the Free Software Foundation ; either version 3 of the License , or
2005-10-28 15:02:42 +04:00
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
2007-07-10 06:07:03 +04:00
along with this program . If not , see < http : //www.gnu.org/licenses/>.
2005-10-28 15:02:42 +04:00
*/
# include "includes.h"
# include "lib/socket/socket.h"
# include "lib/events/events.h"
# include "libcli/composite/composite.h"
2006-03-07 14:07:23 +03:00
# include "libcli/resolve/resolve.h"
2005-10-28 15:02:42 +04:00
2005-11-21 12:20:32 +03:00
# define MULTI_PORT_DELAY 2000 /* microseconds */
2005-10-28 15:02:42 +04:00
2005-11-21 12:20:32 +03:00
/*
overall state
*/
2005-10-28 15:02:42 +04:00
struct connect_multi_state {
2011-11-10 18:36:22 +04:00
struct socket_address * * server_address ;
unsigned num_address , current_address , current_port ;
2005-10-28 15:02:42 +04:00
int num_ports ;
uint16_t * ports ;
2005-11-21 12:20:32 +03:00
struct socket_context * sock ;
2005-10-28 15:02:42 +04:00
uint16_t result_port ;
2005-11-21 12:20:32 +03:00
int num_connects_sent , num_connects_recv ;
2011-11-29 19:25:03 +04:00
struct socket_connect_multi_ex * ex ;
2005-11-21 12:20:32 +03:00
} ;
/*
state of an individual socket_connect_send ( ) call
*/
struct connect_one_state {
struct composite_context * result ;
struct socket_context * sock ;
2006-01-10 01:12:53 +03:00
struct socket_address * addr ;
2005-10-28 15:02:42 +04:00
} ;
2005-11-21 12:20:32 +03:00
static void continue_resolve_name ( struct composite_context * creq ) ;
2008-12-29 22:24:57 +03:00
static void connect_multi_timer ( struct tevent_context * ev ,
struct tevent_timer * te ,
2005-10-28 15:02:42 +04:00
struct timeval tv , void * p ) ;
2005-11-21 12:20:32 +03:00
static void connect_multi_next_socket ( struct composite_context * result ) ;
static void continue_one ( struct composite_context * creq ) ;
2011-11-29 19:25:03 +04:00
static void continue_one_ex ( struct tevent_req * subreq ) ;
2005-10-28 15:02:42 +04:00
2005-11-21 12:20:32 +03:00
/*
setup an async socket_connect , with multiple ports
*/
2011-11-29 19:25:03 +04:00
_PUBLIC_ struct composite_context * socket_connect_multi_ex_send (
2007-09-07 19:35:18 +04:00
TALLOC_CTX * mem_ctx ,
2010-03-10 08:04:44 +03:00
const char * server_name ,
2005-10-28 15:02:42 +04:00
int num_server_ports ,
uint16_t * server_ports ,
2007-12-10 20:41:19 +03:00
struct resolve_context * resolve_ctx ,
2011-11-29 19:25:03 +04:00
struct tevent_context * event_ctx ,
struct socket_connect_multi_ex * ex )
2005-10-28 15:02:42 +04:00
{
struct composite_context * result ;
2005-11-21 12:20:32 +03:00
struct connect_multi_state * multi ;
2005-10-28 15:02:42 +04:00
int i ;
2010-03-10 08:04:44 +03:00
struct nbt_name name ;
struct composite_context * creq ;
2005-10-28 15:02:42 +04:00
result = talloc_zero ( mem_ctx , struct composite_context ) ;
2005-11-21 12:20:32 +03:00
if ( result = = NULL ) return NULL ;
2005-10-28 15:02:42 +04:00
result - > state = COMPOSITE_STATE_IN_PROGRESS ;
result - > event_ctx = event_ctx ;
2005-11-21 12:20:32 +03:00
multi = talloc_zero ( result , struct connect_multi_state ) ;
if ( composite_nomem ( multi , result ) ) goto failed ;
result - > private_data = multi ;
2005-10-28 15:02:42 +04:00
2005-11-21 12:20:32 +03:00
multi - > num_ports = num_server_ports ;
multi - > ports = talloc_array ( multi , uint16_t , multi - > num_ports ) ;
if ( composite_nomem ( multi - > ports , result ) ) goto failed ;
2005-10-28 15:02:42 +04:00
2005-11-21 12:20:32 +03:00
for ( i = 0 ; i < multi - > num_ports ; i + + ) {
multi - > ports [ i ] = server_ports [ i ] ;
2005-10-28 15:02:42 +04:00
}
2011-11-29 19:25:03 +04:00
multi - > ex = ex ;
2010-03-10 08:04:44 +03:00
/*
we don ' t want to do the name resolution separately
2005-11-21 12:20:32 +03:00
for each port , so start it now , then only start on
the real sockets once we have an IP
2010-03-10 08:04:44 +03:00
*/
make_nbt_name_server ( & name , server_name ) ;
2005-10-28 15:02:42 +04:00
2010-03-10 08:04:44 +03:00
creq = resolve_name_all_send ( resolve_ctx , multi , 0 , multi - > ports [ 0 ] , & name , result - > event_ctx ) ;
if ( composite_nomem ( creq , result ) ) goto failed ;
2005-10-28 15:02:42 +04:00
2010-03-10 08:04:44 +03:00
composite_continue ( result , creq , continue_resolve_name , result ) ;
2005-10-28 15:02:42 +04:00
return result ;
2010-03-10 08:04:44 +03:00
2005-10-28 15:02:42 +04:00
failed :
2005-12-08 04:13:45 +03:00
composite_error ( result , result - > status ) ;
2005-11-21 12:20:32 +03:00
return result ;
2005-10-28 15:02:42 +04:00
}
2005-11-21 12:20:32 +03:00
/*
start connecting to the next socket / port in the list
*/
static void connect_multi_next_socket ( struct composite_context * result )
2005-10-28 15:02:42 +04:00
{
2005-11-21 12:20:32 +03:00
struct connect_multi_state * multi = talloc_get_type ( result - > private_data ,
struct connect_multi_state ) ;
struct connect_one_state * state ;
struct composite_context * creq ;
int next = multi - > num_connects_sent ;
2011-11-10 18:36:22 +04:00
if ( next = = multi - > num_address * multi - > num_ports ) {
2005-11-21 12:20:32 +03:00
/* don't do anything, just wait for the existing ones to finish */
return ;
2005-10-28 15:02:42 +04:00
}
2011-11-10 18:36:22 +04:00
if ( multi - > current_address = = multi - > num_address ) {
multi - > current_address = 0 ;
multi - > current_port + = 1 ;
}
2005-11-21 12:20:32 +03:00
multi - > num_connects_sent + = 1 ;
2005-10-28 15:02:42 +04:00
2011-11-10 18:36:22 +04:00
if ( multi - > server_address = = NULL | | multi - > server_address [ multi - > current_address ] = = NULL ) {
2011-11-07 08:19:09 +04:00
composite_error ( result , NT_STATUS_OBJECT_NAME_NOT_FOUND ) ;
return ;
}
2005-11-21 12:20:32 +03:00
state = talloc ( multi , struct connect_one_state ) ;
if ( composite_nomem ( state , result ) ) return ;
2005-10-28 15:02:42 +04:00
2005-11-21 12:20:32 +03:00
state - > result = result ;
2018-02-15 18:43:59 +03:00
result - > status = socket_create (
state , multi - > server_address [ multi - > current_address ] - > family ,
SOCKET_TYPE_STREAM , & state - > sock , 0 ) ;
2005-11-21 12:20:32 +03:00
if ( ! composite_is_ok ( result ) ) return ;
2005-10-28 15:02:42 +04:00
2011-11-10 18:36:22 +04:00
state - > addr = socket_address_copy ( state , multi - > server_address [ multi - > current_address ] ) ;
2006-01-10 01:12:53 +03:00
if ( composite_nomem ( state - > addr , result ) ) return ;
2011-11-10 18:36:22 +04:00
socket_address_set_port ( state - > addr , multi - > ports [ multi - > current_port ] ) ;
2010-03-10 08:04:44 +03:00
2006-01-10 01:12:53 +03:00
creq = socket_connect_send ( state - > sock , NULL ,
2008-12-18 01:13:44 +03:00
state - > addr , 0 ,
2007-12-07 18:04:17 +03:00
result - > event_ctx ) ;
2005-11-21 12:20:32 +03:00
if ( composite_nomem ( creq , result ) ) return ;
2005-11-21 12:31:36 +03:00
talloc_steal ( state , creq ) ;
2005-11-21 12:20:32 +03:00
2011-11-10 18:36:22 +04:00
multi - > current_address + + ;
2005-11-21 12:20:32 +03:00
composite_continue ( result , creq , continue_one , state ) ;
2005-10-28 15:02:42 +04:00
2011-11-10 18:36:22 +04:00
/* if there are more ports / addresses to go then setup a timer to fire when we have waited
2005-11-21 12:20:32 +03:00
for a couple of milli - seconds , when that goes off we try the next port regardless
of whether this port has completed */
2011-11-10 18:36:22 +04:00
if ( multi - > num_ports * multi - > num_address > multi - > num_connects_sent ) {
2005-11-21 12:20:32 +03:00
/* note that this timer is a child of the single
connect attempt state , so it will go away when this
request completes */
2010-05-25 23:28:35 +04:00
tevent_add_timer ( result - > event_ctx , state ,
2011-06-01 05:54:51 +04:00
timeval_current_ofs_usec ( MULTI_PORT_DELAY ) ,
2005-11-21 12:20:32 +03:00
connect_multi_timer , result ) ;
}
2005-10-28 15:02:42 +04:00
}
2005-11-21 12:20:32 +03:00
/*
a timer has gone off telling us that we should try the next port
*/
2008-12-29 22:24:57 +03:00
static void connect_multi_timer ( struct tevent_context * ev ,
struct tevent_timer * te ,
2005-11-21 12:20:32 +03:00
struct timeval tv , void * p )
2005-10-28 15:02:42 +04:00
{
2005-11-21 12:20:32 +03:00
struct composite_context * result = talloc_get_type ( p , struct composite_context ) ;
connect_multi_next_socket ( result ) ;
2005-10-28 15:02:42 +04:00
}
2005-11-21 12:20:32 +03:00
/*
recv name resolution reply then send the next connect
*/
static void continue_resolve_name ( struct composite_context * creq )
2005-10-28 15:02:42 +04:00
{
2005-11-21 12:20:32 +03:00
struct composite_context * result = talloc_get_type ( creq - > async . private_data ,
struct composite_context ) ;
struct connect_multi_state * multi = talloc_get_type ( result - > private_data ,
struct connect_multi_state ) ;
2010-03-10 08:04:44 +03:00
struct socket_address * * addr ;
2011-11-10 18:36:22 +04:00
unsigned i ;
2005-10-28 15:02:42 +04:00
2010-03-10 08:04:44 +03:00
result - > status = resolve_name_all_recv ( creq , multi , & addr , NULL ) ;
2005-11-21 12:20:32 +03:00
if ( ! composite_is_ok ( result ) ) return ;
2005-10-28 15:02:42 +04:00
2011-11-10 18:36:22 +04:00
for ( i = 0 ; addr [ i ] ; i + + ) ;
multi - > num_address = i ;
multi - > server_address = talloc_steal ( multi , addr ) ;
2005-10-28 15:02:42 +04:00
2005-11-21 12:20:32 +03:00
connect_multi_next_socket ( result ) ;
}
/*
one of our socket_connect_send ( ) calls hash finished . If it got a
connection or there are none left then we are done
*/
static void continue_one ( struct composite_context * creq )
{
struct connect_one_state * state = talloc_get_type ( creq - > async . private_data ,
struct connect_one_state ) ;
struct composite_context * result = state - > result ;
struct connect_multi_state * multi = talloc_get_type ( result - > private_data ,
struct connect_multi_state ) ;
NTSTATUS status ;
2005-10-28 15:02:42 +04:00
2005-11-21 12:20:32 +03:00
status = socket_connect_recv ( creq ) ;
2005-10-28 15:02:42 +04:00
2011-11-29 19:25:03 +04:00
if ( multi - > ex ) {
struct tevent_req * subreq ;
subreq = multi - > ex - > establish_send ( state ,
result - > event_ctx ,
state - > sock ,
state - > addr ,
multi - > ex - > private_data ) ;
if ( composite_nomem ( subreq , result ) ) return ;
tevent_req_set_callback ( subreq , continue_one_ex , state ) ;
return ;
}
multi - > num_connects_recv + + ;
if ( NT_STATUS_IS_OK ( status ) ) {
multi - > sock = talloc_steal ( multi , state - > sock ) ;
multi - > result_port = state - > addr - > port ;
}
talloc_free ( state ) ;
if ( NT_STATUS_IS_OK ( status ) | |
multi - > num_connects_recv = = ( multi - > num_address * multi - > num_ports ) ) {
result - > status = status ;
composite_done ( result ) ;
return ;
}
/* try the next port */
connect_multi_next_socket ( result ) ;
}
/*
one of our multi - > ex - > establish_send ( ) calls hash finished . If it got a
connection or there are none left then we are done
*/
static void continue_one_ex ( struct tevent_req * subreq )
{
struct connect_one_state * state =
tevent_req_callback_data ( subreq ,
struct connect_one_state ) ;
struct composite_context * result = state - > result ;
struct connect_multi_state * multi =
talloc_get_type_abort ( result - > private_data ,
struct connect_multi_state ) ;
NTSTATUS status ;
multi - > num_connects_recv + + ;
status = multi - > ex - > establish_recv ( subreq ) ;
TALLOC_FREE ( subreq ) ;
2005-11-21 12:20:32 +03:00
if ( NT_STATUS_IS_OK ( status ) ) {
multi - > sock = talloc_steal ( multi , state - > sock ) ;
2006-01-10 01:12:53 +03:00
multi - > result_port = state - > addr - > port ;
2005-10-28 15:02:42 +04:00
}
2005-11-21 12:20:32 +03:00
talloc_free ( state ) ;
2011-11-10 18:36:22 +04:00
if ( NT_STATUS_IS_OK ( status ) | |
multi - > num_connects_recv = = ( multi - > num_address * multi - > num_ports ) ) {
2005-11-21 12:20:32 +03:00
result - > status = status ;
composite_done ( result ) ;
2005-10-28 15:02:42 +04:00
return ;
}
2005-11-21 12:20:32 +03:00
/* try the next port */
connect_multi_next_socket ( result ) ;
2005-10-28 15:02:42 +04:00
}
2005-11-21 12:20:32 +03:00
/*
async recv routine for socket_connect_multi ( )
*/
2011-11-29 19:25:03 +04:00
_PUBLIC_ NTSTATUS socket_connect_multi_ex_recv ( struct composite_context * ctx ,
2005-10-28 15:02:42 +04:00
TALLOC_CTX * mem_ctx ,
2005-11-21 12:20:32 +03:00
struct socket_context * * sock ,
2005-10-28 15:02:42 +04:00
uint16_t * port )
{
NTSTATUS status = composite_wait ( ctx ) ;
if ( NT_STATUS_IS_OK ( status ) ) {
2005-11-21 12:20:32 +03:00
struct connect_multi_state * multi =
2005-10-28 15:02:42 +04:00
talloc_get_type ( ctx - > private_data ,
struct connect_multi_state ) ;
2005-11-21 12:20:32 +03:00
* sock = talloc_steal ( mem_ctx , multi - > sock ) ;
* port = multi - > result_port ;
2005-10-28 15:02:42 +04:00
}
talloc_free ( ctx ) ;
return status ;
}
2011-11-29 19:25:03 +04:00
NTSTATUS socket_connect_multi_ex ( TALLOC_CTX * mem_ctx ,
const char * server_address ,
int num_server_ports , uint16_t * server_ports ,
struct resolve_context * resolve_ctx ,
struct tevent_context * event_ctx ,
struct socket_connect_multi_ex * ex ,
struct socket_context * * result ,
uint16_t * result_port )
{
struct composite_context * ctx =
socket_connect_multi_ex_send ( mem_ctx , server_address ,
num_server_ports , server_ports ,
resolve_ctx ,
event_ctx ,
ex ) ;
return socket_connect_multi_ex_recv ( ctx , mem_ctx , result , result_port ) ;
}
/*
setup an async socket_connect , with multiple ports
*/
_PUBLIC_ struct composite_context * socket_connect_multi_send (
TALLOC_CTX * mem_ctx ,
const char * server_name ,
int num_server_ports ,
uint16_t * server_ports ,
struct resolve_context * resolve_ctx ,
struct tevent_context * event_ctx )
{
return socket_connect_multi_ex_send ( mem_ctx ,
server_name ,
num_server_ports ,
server_ports ,
resolve_ctx ,
event_ctx ,
NULL ) ; /* ex */
}
/*
async recv routine for socket_connect_multi ( )
*/
_PUBLIC_ NTSTATUS socket_connect_multi_recv ( struct composite_context * ctx ,
TALLOC_CTX * mem_ctx ,
struct socket_context * * sock ,
uint16_t * port )
{
return socket_connect_multi_ex_recv ( ctx , mem_ctx , sock , port ) ;
}
2005-10-28 15:02:42 +04:00
NTSTATUS socket_connect_multi ( TALLOC_CTX * mem_ctx ,
const char * server_address ,
int num_server_ports , uint16_t * server_ports ,
2007-12-10 20:41:19 +03:00
struct resolve_context * resolve_ctx ,
2008-12-29 22:24:57 +03:00
struct tevent_context * event_ctx ,
2005-10-28 15:02:42 +04:00
struct socket_context * * result ,
uint16_t * result_port )
{
2011-11-29 19:25:03 +04:00
return socket_connect_multi_ex ( mem_ctx ,
server_address ,
num_server_ports ,
server_ports ,
resolve_ctx ,
event_ctx ,
NULL , /* ex */
result ,
result_port ) ;
2005-10-28 15:02:42 +04:00
}