2010-12-12 20:55:06 +03:00
/*
Unix SMB / CIFS implementation .
Connect to 445 and 139 / nbsesssetup
Copyright ( C ) Volker Lendecke 2010
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
the Free Software Foundation ; either version 3 of the License , or
( 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
along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
# include "includes.h"
2011-08-28 18:53:56 +04:00
# include "../lib/async_req/async_sock.h"
2011-04-28 19:38:09 +04:00
# include "../lib/util/tevent_ntstatus.h"
2011-08-28 18:53:56 +04:00
# include "../lib/util/tevent_unix.h"
2011-05-06 14:53:38 +04:00
# include "client.h"
2010-12-12 20:55:06 +03:00
# include "async_smb.h"
2011-10-24 10:42:10 +04:00
# include "../libcli/smb/read_smb.h"
2011-03-23 16:18:59 +03:00
# include "libsmb/nmblib.h"
2010-12-12 20:55:06 +03:00
2011-08-28 18:53:56 +04:00
struct cli_session_request_state {
struct tevent_context * ev ;
int sock ;
2015-05-10 02:59:45 +03:00
uint32_t len_hdr ;
2011-08-28 18:53:56 +04:00
struct iovec iov [ 3 ] ;
uint8_t nb_session_response ;
} ;
static void cli_session_request_sent ( struct tevent_req * subreq ) ;
static void cli_session_request_recvd ( struct tevent_req * subreq ) ;
2011-08-28 18:55:57 +04:00
static struct tevent_req * cli_session_request_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
int sock ,
const struct nmb_name * called ,
const struct nmb_name * calling )
2011-08-28 18:53:56 +04:00
{
struct tevent_req * req , * subreq ;
struct cli_session_request_state * state ;
req = tevent_req_create ( mem_ctx , & state ,
struct cli_session_request_state ) ;
if ( req = = NULL ) {
return NULL ;
}
state - > ev = ev ;
state - > sock = sock ;
state - > iov [ 1 ] . iov_base = name_mangle (
state , called - > name , called - > name_type ) ;
if ( tevent_req_nomem ( state - > iov [ 1 ] . iov_base , req ) ) {
return tevent_req_post ( req , ev ) ;
}
state - > iov [ 1 ] . iov_len = name_len (
( unsigned char * ) state - > iov [ 1 ] . iov_base ,
talloc_get_size ( state - > iov [ 1 ] . iov_base ) ) ;
state - > iov [ 2 ] . iov_base = name_mangle (
state , calling - > name , calling - > name_type ) ;
if ( tevent_req_nomem ( state - > iov [ 2 ] . iov_base , req ) ) {
return tevent_req_post ( req , ev ) ;
}
state - > iov [ 2 ] . iov_len = name_len (
( unsigned char * ) state - > iov [ 2 ] . iov_base ,
talloc_get_size ( state - > iov [ 2 ] . iov_base ) ) ;
_smb_setlen ( ( ( char * ) & state - > len_hdr ) ,
state - > iov [ 1 ] . iov_len + state - > iov [ 2 ] . iov_len ) ;
SCVAL ( ( char * ) & state - > len_hdr , 0 , 0x81 ) ;
state - > iov [ 0 ] . iov_base = & state - > len_hdr ;
state - > iov [ 0 ] . iov_len = sizeof ( state - > len_hdr ) ;
subreq = writev_send ( state , ev , NULL , sock , true , state - > iov , 3 ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return tevent_req_post ( req , ev ) ;
}
tevent_req_set_callback ( subreq , cli_session_request_sent , req ) ;
return req ;
}
static void cli_session_request_sent ( struct tevent_req * subreq )
{
struct tevent_req * req = tevent_req_callback_data (
subreq , struct tevent_req ) ;
struct cli_session_request_state * state = tevent_req_data (
req , struct cli_session_request_state ) ;
ssize_t ret ;
int err ;
ret = writev_recv ( subreq , & err ) ;
TALLOC_FREE ( subreq ) ;
if ( ret = = - 1 ) {
tevent_req_error ( req , err ) ;
return ;
}
subreq = read_smb_send ( state , state - > ev , state - > sock ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return ;
}
tevent_req_set_callback ( subreq , cli_session_request_recvd , req ) ;
}
static void cli_session_request_recvd ( struct tevent_req * subreq )
{
struct tevent_req * req = tevent_req_callback_data (
subreq , struct tevent_req ) ;
struct cli_session_request_state * state = tevent_req_data (
req , struct cli_session_request_state ) ;
uint8_t * buf ;
ssize_t ret ;
int err ;
ret = read_smb_recv ( subreq , talloc_tos ( ) , & buf , & err ) ;
TALLOC_FREE ( subreq ) ;
if ( ret < 4 ) {
ret = - 1 ;
err = EIO ;
}
if ( ret = = - 1 ) {
tevent_req_error ( req , err ) ;
return ;
}
/*
* In case of an error there is more information in the data
* portion according to RFC1002 . We ' re not subtle enough to
* respond to the different error conditions , so drop the
* error info here .
*/
state - > nb_session_response = CVAL ( buf , 0 ) ;
tevent_req_done ( req ) ;
}
2011-08-28 18:55:57 +04:00
static bool cli_session_request_recv ( struct tevent_req * req , int * err , uint8_t * resp )
2011-08-28 18:53:56 +04:00
{
struct cli_session_request_state * state = tevent_req_data (
req , struct cli_session_request_state ) ;
if ( tevent_req_is_unix_error ( req , err ) ) {
return false ;
}
* resp = state - > nb_session_response ;
return true ;
}
2010-12-12 20:55:06 +03:00
struct nb_connect_state {
struct tevent_context * ev ;
2010-12-21 20:52:53 +03:00
const struct sockaddr_storage * addr ;
const char * called_name ;
2010-12-12 20:55:06 +03:00
int sock ;
2015-05-21 04:00:17 +03:00
struct tevent_req * session_subreq ;
2010-12-12 20:55:06 +03:00
struct nmb_name called ;
struct nmb_name calling ;
} ;
2015-05-21 04:00:17 +03:00
static void nb_connect_cleanup ( struct tevent_req * req ,
enum tevent_req_state req_state ) ;
2010-12-12 20:55:06 +03:00
static void nb_connect_connected ( struct tevent_req * subreq ) ;
static void nb_connect_done ( struct tevent_req * subreq ) ;
static struct tevent_req * nb_connect_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
const struct sockaddr_storage * addr ,
const char * called_name ,
int called_type ,
const char * calling_name ,
int calling_type )
{
struct tevent_req * req , * subreq ;
struct nb_connect_state * state ;
req = tevent_req_create ( mem_ctx , & state , struct nb_connect_state ) ;
if ( req = = NULL ) {
return NULL ;
}
state - > ev = ev ;
2010-12-21 20:52:53 +03:00
state - > called_name = called_name ;
state - > addr = addr ;
state - > sock = - 1 ;
2010-12-12 20:55:06 +03:00
make_nmb_name ( & state - > called , called_name , called_type ) ;
make_nmb_name ( & state - > calling , calling_name , calling_type ) ;
2015-05-21 04:00:17 +03:00
tevent_req_set_cleanup_fn ( req , nb_connect_cleanup ) ;
2010-12-12 20:55:06 +03:00
2012-02-15 07:51:35 +04:00
subreq = open_socket_out_send ( state , ev , addr , NBT_SMB_PORT , 5000 ) ;
2010-12-12 20:55:06 +03:00
if ( tevent_req_nomem ( subreq , req ) ) {
return tevent_req_post ( req , ev ) ;
}
tevent_req_set_callback ( subreq , nb_connect_connected , req ) ;
return req ;
}
2015-05-21 04:00:17 +03:00
static void nb_connect_cleanup ( struct tevent_req * req ,
enum tevent_req_state req_state )
2010-12-12 20:55:06 +03:00
{
2015-05-21 04:00:17 +03:00
struct nb_connect_state * state = tevent_req_data (
req , struct nb_connect_state ) ;
/*
* we need to free a pending request before closing the
* socket , see bug # 11141
*/
TALLOC_FREE ( state - > session_subreq ) ;
if ( req_state = = TEVENT_REQ_DONE ) {
/*
* we keep the socket open for the caller to use
*/
return ;
}
2010-12-12 20:55:06 +03:00
if ( state - > sock ! = - 1 ) {
close ( state - > sock ) ;
2015-05-21 04:00:17 +03:00
state - > sock = - 1 ;
2010-12-12 20:55:06 +03:00
}
2015-05-21 04:00:17 +03:00
return ;
2010-12-12 20:55:06 +03:00
}
static void nb_connect_connected ( struct tevent_req * subreq )
{
struct tevent_req * req = tevent_req_callback_data (
subreq , struct tevent_req ) ;
struct nb_connect_state * state = tevent_req_data (
req , struct nb_connect_state ) ;
NTSTATUS status ;
status = open_socket_out_recv ( subreq , & state - > sock ) ;
TALLOC_FREE ( subreq ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
tevent_req_nterror ( req , status ) ;
return ;
}
subreq = cli_session_request_send ( state , state - > ev , state - > sock ,
& state - > called , & state - > calling ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return ;
}
tevent_req_set_callback ( subreq , nb_connect_done , req ) ;
2015-05-21 04:00:17 +03:00
state - > session_subreq = subreq ;
2010-12-12 20:55:06 +03:00
}
static void nb_connect_done ( struct tevent_req * subreq )
{
struct tevent_req * req = tevent_req_callback_data (
subreq , struct tevent_req ) ;
2010-12-21 20:52:53 +03:00
struct nb_connect_state * state = tevent_req_data (
req , struct nb_connect_state ) ;
2010-12-12 20:55:06 +03:00
bool ret ;
int err ;
uint8_t resp ;
2015-05-21 04:00:17 +03:00
state - > session_subreq = NULL ;
2010-12-12 20:55:06 +03:00
ret = cli_session_request_recv ( subreq , & err , & resp ) ;
TALLOC_FREE ( subreq ) ;
if ( ! ret ) {
tevent_req_nterror ( req , map_nt_error_from_unix ( err ) ) ;
return ;
}
2010-12-21 20:52:53 +03:00
/*
* RFC1002 : 0x82 - POSITIVE SESSION RESPONSE
*/
2010-12-12 20:55:06 +03:00
if ( resp ! = 0x82 ) {
2010-12-21 20:52:53 +03:00
/*
* The server did not like our session request
*/
close ( state - > sock ) ;
state - > sock = - 1 ;
if ( strequal ( state - > called_name , " *SMBSERVER " ) ) {
/*
* Here we could try a name status request and
* use the first 0x20 type name .
*/
tevent_req_nterror (
req , NT_STATUS_RESOURCE_NAME_NOT_FOUND ) ;
return ;
}
/*
* We could be subtle and distinguish between
* different failure modes , but what we do here
* instead is just retry with * SMBSERVER type 0x20 .
*/
state - > called_name = " *SMBSERVER " ;
make_nmb_name ( & state - > called , state - > called_name , 0x20 ) ;
subreq = open_socket_out_send ( state , state - > ev , state - > addr ,
2012-02-15 07:51:35 +04:00
NBT_SMB_PORT , 5000 ) ;
2010-12-21 20:52:53 +03:00
if ( tevent_req_nomem ( subreq , req ) ) {
return ;
}
tevent_req_set_callback ( subreq , nb_connect_connected , req ) ;
2010-12-12 20:55:06 +03:00
return ;
}
2010-12-21 20:52:53 +03:00
2010-12-12 20:55:06 +03:00
tevent_req_done ( req ) ;
2010-12-21 20:52:53 +03:00
return ;
2010-12-12 20:55:06 +03:00
}
static NTSTATUS nb_connect_recv ( struct tevent_req * req , int * sock )
{
struct nb_connect_state * state = tevent_req_data (
req , struct nb_connect_state ) ;
NTSTATUS status ;
if ( tevent_req_is_nterror ( req , & status ) ) {
2015-05-21 04:00:17 +03:00
tevent_req_received ( req ) ;
2010-12-12 20:55:06 +03:00
return status ;
}
* sock = state - > sock ;
state - > sock = - 1 ;
2015-05-21 04:00:17 +03:00
tevent_req_received ( req ) ;
2010-12-12 20:55:06 +03:00
return NT_STATUS_OK ;
}
struct smbsock_connect_state {
struct tevent_context * ev ;
const struct sockaddr_storage * addr ;
const char * called_name ;
2010-12-23 17:20:22 +03:00
uint8_t called_type ;
2010-12-12 20:55:06 +03:00
const char * calling_name ;
2010-12-23 17:20:22 +03:00
uint8_t calling_type ;
2010-12-12 20:55:06 +03:00
struct tevent_req * req_139 ;
struct tevent_req * req_445 ;
int sock ;
uint16_t port ;
} ;
2015-05-21 04:01:05 +03:00
static void smbsock_connect_cleanup ( struct tevent_req * req ,
enum tevent_req_state req_state ) ;
2010-12-12 20:55:06 +03:00
static void smbsock_connect_connected ( struct tevent_req * subreq ) ;
static void smbsock_connect_do_139 ( struct tevent_req * subreq ) ;
struct tevent_req * smbsock_connect_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
const struct sockaddr_storage * addr ,
2010-12-23 17:20:22 +03:00
uint16_t port ,
2010-12-12 20:55:06 +03:00
const char * called_name ,
2010-12-23 17:20:22 +03:00
int called_type ,
const char * calling_name ,
int calling_type )
2010-12-12 20:55:06 +03:00
{
2012-05-30 15:20:19 +04:00
struct tevent_req * req ;
2010-12-12 20:55:06 +03:00
struct smbsock_connect_state * state ;
req = tevent_req_create ( mem_ctx , & state , struct smbsock_connect_state ) ;
if ( req = = NULL ) {
return NULL ;
}
state - > ev = ev ;
state - > addr = addr ;
state - > sock = - 1 ;
state - > called_name =
( called_name ! = NULL ) ? called_name : " *SMBSERVER " ;
2010-12-23 17:20:22 +03:00
state - > called_type =
( called_type ! = - 1 ) ? called_type : 0x20 ;
2010-12-12 20:55:06 +03:00
state - > calling_name =
2011-06-09 09:31:03 +04:00
( calling_name ! = NULL ) ? calling_name : lp_netbios_name ( ) ;
2010-12-23 17:20:22 +03:00
state - > calling_type =
( calling_type ! = - 1 ) ? calling_type : 0x00 ;
2010-12-12 20:55:06 +03:00
2015-05-21 04:01:05 +03:00
tevent_req_set_cleanup_fn ( req , smbsock_connect_cleanup ) ;
2010-12-12 20:55:06 +03:00
2012-02-15 07:51:35 +04:00
if ( port = = NBT_SMB_PORT ) {
2019-01-03 20:07:01 +03:00
if ( lp_disable_netbios ( ) ) {
tevent_req_nterror ( req , NT_STATUS_NOT_SUPPORTED ) ;
return tevent_req_post ( req , ev ) ;
}
2012-05-30 15:20:19 +04:00
state - > req_139 = nb_connect_send ( state , state - > ev , state - > addr ,
state - > called_name ,
state - > called_type ,
state - > calling_name ,
state - > calling_type ) ;
if ( tevent_req_nomem ( state - > req_139 , req ) ) {
2010-12-23 17:20:22 +03:00
return tevent_req_post ( req , ev ) ;
}
2012-05-30 15:20:19 +04:00
tevent_req_set_callback (
state - > req_139 , smbsock_connect_connected , req ) ;
2010-12-23 17:20:22 +03:00
return req ;
}
if ( port ! = 0 ) {
state - > req_445 = open_socket_out_send ( state , ev , addr , port ,
5000 ) ;
if ( tevent_req_nomem ( state - > req_445 , req ) ) {
return tevent_req_post ( req , ev ) ;
}
tevent_req_set_callback (
state - > req_445 , smbsock_connect_connected , req ) ;
return req ;
}
/*
* port = = 0 , try both
*/
2012-02-15 07:51:35 +04:00
state - > req_445 = open_socket_out_send ( state , ev , addr , TCP_SMB_PORT , 5000 ) ;
2010-12-12 20:55:06 +03:00
if ( tevent_req_nomem ( state - > req_445 , req ) ) {
return tevent_req_post ( req , ev ) ;
}
tevent_req_set_callback ( state - > req_445 , smbsock_connect_connected ,
req ) ;
2019-01-14 18:36:47 +03:00
/*
* Check for disable_netbios
*/
if ( lp_disable_netbios ( ) ) {
return req ;
}
2010-12-12 20:55:06 +03:00
/*
2012-02-15 07:51:35 +04:00
* After 5 msecs , fire the 139 ( NBT ) request
2010-12-12 20:55:06 +03:00
*/
2011-01-22 18:22:42 +03:00
state - > req_139 = tevent_wakeup_send (
state , ev , timeval_current_ofs ( 0 , 5000 ) ) ;
if ( tevent_req_nomem ( state - > req_139 , req ) ) {
2010-12-12 20:55:06 +03:00
TALLOC_FREE ( state - > req_445 ) ;
return tevent_req_post ( req , ev ) ;
}
2011-01-22 18:22:42 +03:00
tevent_req_set_callback ( state - > req_139 , smbsock_connect_do_139 ,
req ) ;
2010-12-12 20:55:06 +03:00
return req ;
}
2015-05-21 04:01:05 +03:00
static void smbsock_connect_cleanup ( struct tevent_req * req ,
enum tevent_req_state req_state )
2010-12-12 20:55:06 +03:00
{
2015-05-21 04:01:05 +03:00
struct smbsock_connect_state * state = tevent_req_data (
req , struct smbsock_connect_state ) ;
/*
* we need to free a pending request before closing the
* socket , see bug # 11141
*/
TALLOC_FREE ( state - > req_445 ) ;
TALLOC_FREE ( state - > req_139 ) ;
if ( req_state = = TEVENT_REQ_DONE ) {
/*
* we keep the socket open for the caller to use
*/
return ;
}
2010-12-12 20:55:06 +03:00
if ( state - > sock ! = - 1 ) {
close ( state - > sock ) ;
2011-05-14 15:15:19 +04:00
state - > sock = - 1 ;
2010-12-12 20:55:06 +03:00
}
2015-05-21 04:01:05 +03:00
return ;
2010-12-12 20:55:06 +03:00
}
static void smbsock_connect_do_139 ( struct tevent_req * subreq )
{
struct tevent_req * req = tevent_req_callback_data (
subreq , struct tevent_req ) ;
struct smbsock_connect_state * state = tevent_req_data (
req , struct smbsock_connect_state ) ;
bool ret ;
ret = tevent_wakeup_recv ( subreq ) ;
TALLOC_FREE ( subreq ) ;
if ( ! ret ) {
tevent_req_nterror ( req , NT_STATUS_INTERNAL_ERROR ) ;
return ;
}
state - > req_139 = nb_connect_send ( state , state - > ev , state - > addr ,
2010-12-23 17:20:22 +03:00
state - > called_name ,
state - > called_type ,
state - > calling_name ,
state - > calling_type ) ;
2010-12-12 20:55:06 +03:00
if ( tevent_req_nomem ( state - > req_139 , req ) ) {
return ;
}
tevent_req_set_callback ( state - > req_139 , smbsock_connect_connected ,
req ) ;
}
static void smbsock_connect_connected ( struct tevent_req * subreq )
{
struct tevent_req * req = tevent_req_callback_data (
subreq , struct tevent_req ) ;
struct smbsock_connect_state * state = tevent_req_data (
req , struct smbsock_connect_state ) ;
struct tevent_req * unfinished_req ;
NTSTATUS status ;
if ( subreq = = state - > req_445 ) {
status = open_socket_out_recv ( subreq , & state - > sock ) ;
TALLOC_FREE ( state - > req_445 ) ;
unfinished_req = state - > req_139 ;
2012-02-15 07:51:35 +04:00
state - > port = TCP_SMB_PORT ;
2010-12-12 20:55:06 +03:00
} else if ( subreq = = state - > req_139 ) {
status = nb_connect_recv ( subreq , & state - > sock ) ;
TALLOC_FREE ( state - > req_139 ) ;
unfinished_req = state - > req_445 ;
2012-02-15 07:51:35 +04:00
state - > port = NBT_SMB_PORT ;
2010-12-12 20:55:06 +03:00
} else {
tevent_req_nterror ( req , NT_STATUS_INTERNAL_ERROR ) ;
return ;
}
if ( NT_STATUS_IS_OK ( status ) ) {
TALLOC_FREE ( unfinished_req ) ;
state - > req_139 = NULL ;
state - > req_445 = NULL ;
tevent_req_done ( req ) ;
return ;
}
if ( unfinished_req = = NULL ) {
/*
* Both requests failed
*/
tevent_req_nterror ( req , status ) ;
return ;
}
/*
* Do nothing , wait for the second request to come here .
*/
}
NTSTATUS smbsock_connect_recv ( struct tevent_req * req , int * sock ,
2010-12-23 17:20:22 +03:00
uint16_t * ret_port )
2010-12-12 20:55:06 +03:00
{
struct smbsock_connect_state * state = tevent_req_data (
req , struct smbsock_connect_state ) ;
NTSTATUS status ;
if ( tevent_req_is_nterror ( req , & status ) ) {
2015-05-21 04:01:05 +03:00
tevent_req_received ( req ) ;
2010-12-12 20:55:06 +03:00
return status ;
}
* sock = state - > sock ;
state - > sock = - 1 ;
2010-12-23 17:20:22 +03:00
if ( ret_port ! = NULL ) {
* ret_port = state - > port ;
2010-12-13 19:17:51 +03:00
}
2015-05-21 04:01:05 +03:00
tevent_req_received ( req ) ;
2010-12-12 20:55:06 +03:00
return NT_STATUS_OK ;
}
2010-12-23 17:20:22 +03:00
NTSTATUS smbsock_connect ( const struct sockaddr_storage * addr , uint16_t port ,
const char * called_name , int called_type ,
const char * calling_name , int calling_type ,
2011-04-17 22:16:07 +04:00
int * pfd , uint16_t * ret_port , int sec_timeout )
2010-12-12 20:55:06 +03:00
{
TALLOC_CTX * frame = talloc_stackframe ( ) ;
2011-08-23 18:50:19 +04:00
struct tevent_context * ev ;
2010-12-12 20:55:06 +03:00
struct tevent_req * req ;
NTSTATUS status = NT_STATUS_NO_MEMORY ;
2013-02-18 12:08:19 +04:00
ev = samba_tevent_context_init ( frame ) ;
2010-12-12 20:55:06 +03:00
if ( ev = = NULL ) {
goto fail ;
}
2010-12-23 17:20:22 +03:00
req = smbsock_connect_send ( frame , ev , addr , port ,
called_name , called_type ,
calling_name , calling_type ) ;
2010-12-12 20:55:06 +03:00
if ( req = = NULL ) {
goto fail ;
}
2011-04-17 22:16:07 +04:00
if ( ( sec_timeout ! = 0 ) & &
! tevent_req_set_endtime (
req , ev , timeval_current_ofs ( sec_timeout , 0 ) ) ) {
goto fail ;
}
2010-12-12 20:55:06 +03:00
if ( ! tevent_req_poll_ntstatus ( req , ev , & status ) ) {
goto fail ;
}
2010-12-23 17:20:22 +03:00
status = smbsock_connect_recv ( req , pfd , ret_port ) ;
2010-12-12 20:55:06 +03:00
fail :
TALLOC_FREE ( frame ) ;
return status ;
}
2010-12-13 19:17:51 +03:00
struct smbsock_any_connect_state {
struct tevent_context * ev ;
const struct sockaddr_storage * addrs ;
const char * * called_names ;
2010-12-23 17:20:22 +03:00
int * called_types ;
const char * * calling_names ;
int * calling_types ;
2010-12-13 19:17:51 +03:00
size_t num_addrs ;
2010-12-23 17:20:22 +03:00
uint16_t port ;
2010-12-13 19:17:51 +03:00
struct tevent_req * * requests ;
size_t num_sent ;
size_t num_received ;
int fd ;
2010-12-23 17:20:22 +03:00
uint16_t chosen_port ;
2010-12-13 19:17:51 +03:00
size_t chosen_index ;
} ;
2015-05-21 04:01:30 +03:00
static void smbsock_any_connect_cleanup ( struct tevent_req * req ,
enum tevent_req_state req_state ) ;
2010-12-13 19:17:51 +03:00
static bool smbsock_any_connect_send_next (
struct tevent_req * req , struct smbsock_any_connect_state * state ) ;
static void smbsock_any_connect_trynext ( struct tevent_req * subreq ) ;
static void smbsock_any_connect_connected ( struct tevent_req * subreq ) ;
struct tevent_req * smbsock_any_connect_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
const struct sockaddr_storage * addrs ,
const char * * called_names ,
2010-12-23 17:20:22 +03:00
int * called_types ,
const char * * calling_names ,
int * calling_types ,
size_t num_addrs , uint16_t port )
2010-12-13 19:17:51 +03:00
{
struct tevent_req * req , * subreq ;
struct smbsock_any_connect_state * state ;
req = tevent_req_create ( mem_ctx , & state ,
struct smbsock_any_connect_state ) ;
if ( req = = NULL ) {
return NULL ;
}
state - > ev = ev ;
state - > addrs = addrs ;
state - > num_addrs = num_addrs ;
state - > called_names = called_names ;
2010-12-23 17:20:22 +03:00
state - > called_types = called_types ;
state - > calling_names = calling_names ;
state - > calling_types = calling_types ;
state - > port = port ;
2015-05-21 04:01:30 +03:00
state - > fd = - 1 ;
tevent_req_set_cleanup_fn ( req , smbsock_any_connect_cleanup ) ;
2010-12-13 19:17:51 +03:00
if ( num_addrs = = 0 ) {
tevent_req_nterror ( req , NT_STATUS_INVALID_PARAMETER ) ;
return tevent_req_post ( req , ev ) ;
}
state - > requests = talloc_zero_array ( state , struct tevent_req * ,
num_addrs ) ;
if ( tevent_req_nomem ( state - > requests , req ) ) {
return tevent_req_post ( req , ev ) ;
}
if ( ! smbsock_any_connect_send_next ( req , state ) ) {
return tevent_req_post ( req , ev ) ;
}
if ( state - > num_sent > = state - > num_addrs ) {
return req ;
}
subreq = tevent_wakeup_send ( state , ev , timeval_current_ofs ( 0 , 10000 ) ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return tevent_req_post ( req , ev ) ;
}
tevent_req_set_callback ( subreq , smbsock_any_connect_trynext , req ) ;
return req ;
}
2015-05-21 04:01:30 +03:00
static void smbsock_any_connect_cleanup ( struct tevent_req * req ,
enum tevent_req_state req_state )
{
struct smbsock_any_connect_state * state = tevent_req_data (
req , struct smbsock_any_connect_state ) ;
TALLOC_FREE ( state - > requests ) ;
if ( req_state = = TEVENT_REQ_DONE ) {
/*
* Keep the socket open for the caller .
*/
return ;
}
if ( state - > fd ! = - 1 ) {
close ( state - > fd ) ;
state - > fd = - 1 ;
}
}
2010-12-13 19:17:51 +03:00
static void smbsock_any_connect_trynext ( struct tevent_req * subreq )
{
struct tevent_req * req = tevent_req_callback_data (
subreq , struct tevent_req ) ;
struct smbsock_any_connect_state * state = tevent_req_data (
req , struct smbsock_any_connect_state ) ;
bool ret ;
ret = tevent_wakeup_recv ( subreq ) ;
TALLOC_FREE ( subreq ) ;
if ( ! ret ) {
tevent_req_nterror ( req , NT_STATUS_INTERNAL_ERROR ) ;
return ;
}
if ( ! smbsock_any_connect_send_next ( req , state ) ) {
return ;
}
if ( state - > num_sent > = state - > num_addrs ) {
return ;
}
subreq = tevent_wakeup_send ( state , state - > ev ,
tevent_timeval_set ( 0 , 10000 ) ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return ;
}
tevent_req_set_callback ( subreq , smbsock_any_connect_trynext , req ) ;
}
static bool smbsock_any_connect_send_next (
struct tevent_req * req , struct smbsock_any_connect_state * state )
{
struct tevent_req * subreq ;
if ( state - > num_sent > = state - > num_addrs ) {
tevent_req_nterror ( req , NT_STATUS_INTERNAL_ERROR ) ;
return false ;
}
subreq = smbsock_connect_send (
state - > requests , state - > ev , & state - > addrs [ state - > num_sent ] ,
2010-12-23 17:20:22 +03:00
state - > port ,
2010-12-13 19:17:51 +03:00
( state - > called_names ! = NULL )
? state - > called_names [ state - > num_sent ] : NULL ,
2010-12-23 17:20:22 +03:00
( state - > called_types ! = NULL )
? state - > called_types [ state - > num_sent ] : - 1 ,
( state - > calling_names ! = NULL )
? state - > calling_names [ state - > num_sent ] : NULL ,
( state - > calling_types ! = NULL )
? state - > calling_types [ state - > num_sent ] : - 1 ) ;
2010-12-13 19:17:51 +03:00
if ( tevent_req_nomem ( subreq , req ) ) {
return false ;
}
tevent_req_set_callback ( subreq , smbsock_any_connect_connected , req ) ;
state - > requests [ state - > num_sent ] = subreq ;
state - > num_sent + = 1 ;
return true ;
}
static void smbsock_any_connect_connected ( struct tevent_req * subreq )
{
struct tevent_req * req = tevent_req_callback_data (
subreq , struct tevent_req ) ;
struct smbsock_any_connect_state * state = tevent_req_data (
req , struct smbsock_any_connect_state ) ;
NTSTATUS status ;
2019-08-13 17:43:30 +03:00
int fd = 0 ;
uint16_t chosen_port = 0 ;
2010-12-13 19:17:51 +03:00
size_t i ;
size_t chosen_index = 0 ;
for ( i = 0 ; i < state - > num_sent ; i + + ) {
if ( state - > requests [ i ] = = subreq ) {
chosen_index = i ;
break ;
}
}
if ( i = = state - > num_sent ) {
tevent_req_nterror ( req , NT_STATUS_INTERNAL_ERROR ) ;
return ;
}
2010-12-23 17:20:22 +03:00
status = smbsock_connect_recv ( subreq , & fd , & chosen_port ) ;
2010-12-13 19:17:51 +03:00
TALLOC_FREE ( subreq ) ;
state - > requests [ chosen_index ] = NULL ;
if ( NT_STATUS_IS_OK ( status ) ) {
/*
2015-05-21 04:01:30 +03:00
* tevent_req_done ( ) will kill all the other requests
* via smbsock_any_connect_cleanup ( ) .
2010-12-13 19:17:51 +03:00
*/
state - > fd = fd ;
2010-12-23 17:20:22 +03:00
state - > chosen_port = chosen_port ;
2010-12-13 19:17:51 +03:00
state - > chosen_index = chosen_index ;
tevent_req_done ( req ) ;
return ;
}
state - > num_received + = 1 ;
2011-05-28 20:55:47 +04:00
if ( state - > num_received < state - > num_addrs ) {
2010-12-13 19:17:51 +03:00
/*
* More addrs pending , wait for the others
*/
return ;
}
/*
* This is the last response , none succeeded .
*/
tevent_req_nterror ( req , status ) ;
return ;
}
NTSTATUS smbsock_any_connect_recv ( struct tevent_req * req , int * pfd ,
2010-12-23 17:20:22 +03:00
size_t * chosen_index ,
uint16_t * chosen_port )
2010-12-13 19:17:51 +03:00
{
struct smbsock_any_connect_state * state = tevent_req_data (
req , struct smbsock_any_connect_state ) ;
NTSTATUS status ;
if ( tevent_req_is_nterror ( req , & status ) ) {
2015-05-21 04:01:30 +03:00
tevent_req_received ( req ) ;
2010-12-13 19:17:51 +03:00
return status ;
}
* pfd = state - > fd ;
2015-05-21 04:01:30 +03:00
state - > fd = - 1 ;
2010-12-13 19:17:51 +03:00
if ( chosen_index ! = NULL ) {
* chosen_index = state - > chosen_index ;
}
2010-12-23 17:20:22 +03:00
if ( chosen_port ! = NULL ) {
* chosen_port = state - > chosen_port ;
2010-12-13 19:17:51 +03:00
}
2015-05-21 04:01:30 +03:00
tevent_req_received ( req ) ;
2010-12-13 19:17:51 +03:00
return NT_STATUS_OK ;
}
NTSTATUS smbsock_any_connect ( const struct sockaddr_storage * addrs ,
2010-12-23 17:20:22 +03:00
const char * * called_names ,
int * called_types ,
const char * * calling_names ,
int * calling_types ,
size_t num_addrs ,
uint16_t port ,
2011-04-17 22:16:07 +04:00
int sec_timeout ,
2010-12-23 17:20:22 +03:00
int * pfd , size_t * chosen_index ,
uint16_t * chosen_port )
2010-12-13 19:17:51 +03:00
{
TALLOC_CTX * frame = talloc_stackframe ( ) ;
2011-08-23 18:50:32 +04:00
struct tevent_context * ev ;
2010-12-13 19:17:51 +03:00
struct tevent_req * req ;
NTSTATUS status = NT_STATUS_NO_MEMORY ;
2013-02-18 12:08:19 +04:00
ev = samba_tevent_context_init ( frame ) ;
2010-12-13 19:17:51 +03:00
if ( ev = = NULL ) {
goto fail ;
}
2010-12-23 17:20:22 +03:00
req = smbsock_any_connect_send ( frame , ev , addrs ,
called_names , called_types ,
calling_names , calling_types ,
num_addrs , port ) ;
2010-12-13 19:17:51 +03:00
if ( req = = NULL ) {
goto fail ;
}
2011-04-17 22:16:07 +04:00
if ( ( sec_timeout ! = 0 ) & &
! tevent_req_set_endtime (
req , ev , timeval_current_ofs ( sec_timeout , 0 ) ) ) {
goto fail ;
}
2010-12-13 19:17:51 +03:00
if ( ! tevent_req_poll_ntstatus ( req , ev , & status ) ) {
goto fail ;
}
2010-12-23 17:20:22 +03:00
status = smbsock_any_connect_recv ( req , pfd , chosen_index , chosen_port ) ;
2010-12-13 19:17:51 +03:00
fail :
TALLOC_FREE ( frame ) ;
return status ;
}