2005-10-14 16:55:41 +04:00
/*
Unix SMB / CIFS implementation .
WINS Replication server
Copyright ( C ) Stefan Metzmacher 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-14 16:55:41 +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-14 16:55:41 +04:00
*/
# include "includes.h"
# include "lib/socket/socket.h"
2005-12-13 00:31:42 +03:00
# include "lib/stream/packet.h"
2020-11-20 17:27:17 +03:00
# include "samba/service_task.h"
# include "samba/service_stream.h"
# include "samba/service.h"
2005-10-14 16:55:41 +04:00
# include "lib/messaging/irpc.h"
# include "librpc/gen_ndr/ndr_winsrepl.h"
# include "wrepl_server/wrepl_server.h"
2020-11-20 17:27:17 +03:00
# include "samba/process_model.h"
2006-03-07 14:07:23 +03:00
# include "system/network.h"
2006-08-17 17:37:04 +04:00
# include "lib/socket/netif.h"
2010-01-20 16:21:47 +03:00
# include "lib/tsocket/tsocket.h"
# include "libcli/util/tstream.h"
2007-09-08 16:42:09 +04:00
# include "param/param.h"
2005-10-14 16:55:41 +04:00
2005-10-14 18:02:47 +04:00
void wreplsrv_terminate_in_connection ( struct wreplsrv_in_connection * wreplconn , const char * reason )
2005-10-14 16:55:41 +04:00
{
stream_terminate_connection ( wreplconn - > conn , reason ) ;
}
/*
receive some data on a WREPL connection
*/
2010-01-20 16:21:47 +03:00
static NTSTATUS wreplsrv_process ( struct wreplsrv_in_connection * wrepl_conn ,
struct wreplsrv_in_call * * _call )
2005-10-14 16:55:41 +04:00
{
struct wrepl_wrap packet_out_wrap ;
2005-12-13 00:31:42 +03:00
NTSTATUS status ;
2007-11-09 21:24:51 +03:00
enum ndr_err_code ndr_err ;
2010-01-20 16:21:47 +03:00
struct wreplsrv_in_call * call = * _call ;
2005-10-14 16:55:41 +04:00
2010-01-20 16:21:47 +03:00
ndr_err = ndr_pull_struct_blob ( & call - > in , call ,
2008-01-02 07:05:13 +03:00
& call - > req_packet ,
2007-11-09 21:24:51 +03:00
( ndr_pull_flags_fn_t ) ndr_pull_wrepl_packet ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
return ndr_map_error2ntstatus ( ndr_err ) ;
}
2005-10-14 16:55:41 +04:00
if ( DEBUGLVL ( 10 ) ) {
2010-01-20 16:21:47 +03:00
DEBUG ( 10 , ( " Received WINS-Replication packet of length %u \n " ,
( unsigned int ) call - > in . length + 4 ) ) ;
2005-10-14 16:55:41 +04:00
NDR_PRINT_DEBUG ( wrepl_packet , & call - > req_packet ) ;
}
status = wreplsrv_in_call ( call ) ;
2014-02-14 08:46:40 +04:00
if ( NT_STATUS_IS_ERR ( status ) ) {
return status ;
}
2005-10-14 16:55:41 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
/* w2k just ignores invalid packets, so we do */
DEBUG ( 10 , ( " Received WINS-Replication packet was invalid, we just ignore it \n " ) ) ;
2010-01-20 16:21:47 +03:00
TALLOC_FREE ( call ) ;
* _call = NULL ;
2005-12-13 00:31:42 +03:00
return NT_STATUS_OK ;
2005-10-14 16:55:41 +04:00
}
/* and now encode the reply */
packet_out_wrap . packet = call - > rep_packet ;
2010-01-20 16:21:47 +03:00
ndr_err = ndr_push_struct_blob ( & call - > out , call ,
2008-01-02 07:05:05 +03:00
& packet_out_wrap ,
2010-01-20 16:21:47 +03:00
( ndr_push_flags_fn_t ) ndr_push_wrepl_wrap ) ;
2007-11-09 21:24:51 +03:00
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
return ndr_map_error2ntstatus ( ndr_err ) ;
}
2005-10-14 16:55:41 +04:00
if ( DEBUGLVL ( 10 ) ) {
2010-01-20 16:21:47 +03:00
DEBUG ( 10 , ( " Sending WINS-Replication packet of length %u \n " ,
( unsigned int ) call - > out . length ) ) ;
2005-10-14 16:55:41 +04:00
NDR_PRINT_DEBUG ( wrepl_packet , & call - > rep_packet ) ;
}
2005-12-13 00:31:42 +03:00
return NT_STATUS_OK ;
}
2005-10-14 16:55:41 +04:00
2010-01-20 16:21:47 +03:00
static void wreplsrv_call_loop ( struct tevent_req * subreq ) ;
2005-10-14 16:55:41 +04:00
2005-12-13 00:31:42 +03:00
/*
called when we get a new connection
*/
static void wreplsrv_accept ( struct stream_connection * conn )
{
2009-02-02 12:30:03 +03:00
struct wreplsrv_service * service = talloc_get_type ( conn - > private_data , struct wreplsrv_service ) ;
2010-01-20 16:21:47 +03:00
struct wreplsrv_in_connection * wrepl_conn ;
2010-04-27 18:17:28 +04:00
struct tsocket_address * peer_addr ;
char * peer_ip ;
2010-01-20 16:21:47 +03:00
struct tevent_req * subreq ;
2010-09-28 04:33:23 +04:00
int rc ;
2005-10-14 16:55:41 +04:00
2010-01-20 16:21:47 +03:00
wrepl_conn = talloc_zero ( conn , struct wreplsrv_in_connection ) ;
if ( wrepl_conn = = NULL ) {
stream_terminate_connection ( conn ,
" wreplsrv_accept: out of memory " ) ;
2005-12-13 00:31:42 +03:00
return ;
}
2005-10-14 16:55:41 +04:00
2010-01-20 16:21:47 +03:00
wrepl_conn - > send_queue = tevent_queue_create ( conn , " wrepl_accept " ) ;
if ( wrepl_conn - > send_queue = = NULL ) {
stream_terminate_connection ( conn ,
" wrepl_accept: out of memory " ) ;
2005-12-13 00:31:42 +03:00
return ;
2005-10-14 16:55:41 +04:00
}
2010-01-20 16:21:47 +03:00
TALLOC_FREE ( conn - > event . fde ) ;
rc = tstream_bsd_existing_socket ( wrepl_conn ,
2010-09-28 04:33:23 +04:00
socket_get_fd ( conn - > socket ) ,
2010-01-20 16:21:47 +03:00
& wrepl_conn - > tstream ) ;
if ( rc < 0 ) {
stream_terminate_connection ( conn ,
" wrepl_accept: out of memory " ) ;
2005-12-13 00:31:42 +03:00
return ;
}
2010-09-28 04:33:23 +04:00
socket_set_flags ( conn - > socket , SOCKET_FLAG_NOCLOSE ) ;
2005-10-14 16:55:41 +04:00
2010-01-20 16:21:47 +03:00
wrepl_conn - > conn = conn ;
wrepl_conn - > service = service ;
2005-12-13 00:31:42 +03:00
2010-04-27 18:17:28 +04:00
peer_addr = conn - > remote_address ;
if ( ! tsocket_address_is_inet ( peer_addr , " ipv4 " ) ) {
DEBUG ( 0 , ( " wreplsrv_accept: non ipv4 peer addr '%s' \n " ,
tsocket_address_string ( peer_addr , wrepl_conn ) ) ) ;
wreplsrv_terminate_in_connection ( wrepl_conn , " wreplsrv_accept: "
" invalid peer IP " ) ;
return ;
}
peer_ip = tsocket_address_inet_addr_string ( peer_addr , wrepl_conn ) ;
2010-01-20 16:21:47 +03:00
if ( peer_ip = = NULL ) {
wreplsrv_terminate_in_connection ( wrepl_conn , " wreplsrv_accept: "
2010-04-27 18:17:28 +04:00
" could not convert peer IP into a string " ) ;
2010-01-20 16:21:47 +03:00
return ;
}
2005-12-13 00:31:42 +03:00
2010-04-27 18:17:28 +04:00
wrepl_conn - > partner = wreplsrv_find_partner ( service , peer_ip ) ;
2005-12-13 00:31:42 +03:00
irpc_add_name ( conn - > msg_ctx , " wreplsrv_connection " ) ;
2010-01-20 16:21:47 +03:00
/*
* The wrepl pdu ' s has the length as 4 byte ( initial_read_size ) ,
* packet_full_request_u32 provides the pdu length then .
*/
subreq = tstream_read_pdu_blob_send ( wrepl_conn ,
wrepl_conn - > conn - > event . ctx ,
wrepl_conn - > tstream ,
4 , /* initial_read_size */
packet_full_request_u32 ,
wrepl_conn ) ;
if ( subreq = = NULL ) {
wreplsrv_terminate_in_connection ( wrepl_conn , " wrepl_accept: "
" no memory for tstream_read_pdu_blob_send " ) ;
return ;
}
tevent_req_set_callback ( subreq , wreplsrv_call_loop , wrepl_conn ) ;
}
static void wreplsrv_call_writev_done ( struct tevent_req * subreq ) ;
static void wreplsrv_call_loop ( struct tevent_req * subreq )
{
struct wreplsrv_in_connection * wrepl_conn = tevent_req_callback_data ( subreq ,
struct wreplsrv_in_connection ) ;
struct wreplsrv_in_call * call ;
NTSTATUS status ;
call = talloc_zero ( wrepl_conn , struct wreplsrv_in_call ) ;
if ( call = = NULL ) {
wreplsrv_terminate_in_connection ( wrepl_conn , " wreplsrv_call_loop: "
" no memory for wrepl_samba3_call " ) ;
return ;
}
call - > wreplconn = wrepl_conn ;
status = tstream_read_pdu_blob_recv ( subreq ,
call ,
& call - > in ) ;
TALLOC_FREE ( subreq ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
const char * reason ;
reason = talloc_asprintf ( call , " wreplsrv_call_loop: "
" tstream_read_pdu_blob_recv() - %s " ,
nt_errstr ( status ) ) ;
if ( ! reason ) {
reason = nt_errstr ( status ) ;
}
wreplsrv_terminate_in_connection ( wrepl_conn , reason ) ;
return ;
}
DEBUG ( 10 , ( " Received wrepl packet of length %lu from %s \n " ,
( long ) call - > in . length ,
tsocket_address_string ( wrepl_conn - > conn - > remote_address , call ) ) ) ;
/* skip length header */
call - > in . data + = 4 ;
call - > in . length - = 4 ;
status = wreplsrv_process ( wrepl_conn , & call ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
const char * reason ;
reason = talloc_asprintf ( call , " wreplsrv_call_loop: "
" tstream_read_pdu_blob_recv() - %s " ,
nt_errstr ( status ) ) ;
if ( reason = = NULL ) {
reason = nt_errstr ( status ) ;
}
wreplsrv_terminate_in_connection ( wrepl_conn , reason ) ;
return ;
}
/* We handed over the connection so we're done here */
if ( wrepl_conn - > tstream = = NULL ) {
return ;
}
/* Invalid WINS-Replication packet, we just ignore it */
if ( call = = NULL ) {
goto noreply ;
}
2010-11-29 16:38:21 +03:00
call - > out_iov [ 0 ] . iov_base = ( char * ) call - > out . data ;
2010-01-20 16:21:47 +03:00
call - > out_iov [ 0 ] . iov_len = call - > out . length ;
subreq = tstream_writev_queue_send ( call ,
wrepl_conn - > conn - > event . ctx ,
wrepl_conn - > tstream ,
wrepl_conn - > send_queue ,
call - > out_iov , 1 ) ;
if ( subreq = = NULL ) {
wreplsrv_terminate_in_connection ( wrepl_conn , " wreplsrv_call_loop: "
" no memory for tstream_writev_queue_send " ) ;
return ;
}
tevent_req_set_callback ( subreq , wreplsrv_call_writev_done , call ) ;
noreply :
/*
* The wrepl pdu ' s has the length as 4 byte ( initial_read_size ) ,
* provides the pdu length then .
*/
subreq = tstream_read_pdu_blob_send ( wrepl_conn ,
wrepl_conn - > conn - > event . ctx ,
wrepl_conn - > tstream ,
4 , /* initial_read_size */
packet_full_request_u32 ,
wrepl_conn ) ;
if ( subreq = = NULL ) {
wreplsrv_terminate_in_connection ( wrepl_conn , " wreplsrv_call_loop: "
" no memory for tstream_read_pdu_blob_send " ) ;
return ;
}
tevent_req_set_callback ( subreq , wreplsrv_call_loop , wrepl_conn ) ;
}
static void wreplsrv_call_writev_done ( struct tevent_req * subreq )
{
struct wreplsrv_in_call * call = tevent_req_callback_data ( subreq ,
struct wreplsrv_in_call ) ;
int sys_errno ;
int rc ;
rc = tstream_writev_queue_recv ( subreq , & sys_errno ) ;
TALLOC_FREE ( subreq ) ;
if ( rc = = - 1 ) {
const char * reason ;
reason = talloc_asprintf ( call , " wreplsrv_call_writev_done: "
" tstream_writev_queue_recv() - %d:%s " ,
sys_errno , strerror ( sys_errno ) ) ;
if ( reason = = NULL ) {
reason = " wreplsrv_call_writev_done: "
" tstream_writev_queue_recv() failed " ;
}
wreplsrv_terminate_in_connection ( call - > wreplconn , reason ) ;
return ;
}
if ( call - > terminate_after_send ) {
wreplsrv_terminate_in_connection ( call - > wreplconn ,
" wreplsrv_in_connection: terminate_after_send " ) ;
return ;
}
talloc_free ( call ) ;
}
/*
called on a tcp recv
*/
static void wreplsrv_recv ( struct stream_connection * conn , uint16_t flags )
{
struct wreplsrv_in_connection * wrepl_conn = talloc_get_type ( conn - > private_data ,
struct wreplsrv_in_connection ) ;
/* this should never be triggered! */
DEBUG ( 0 , ( " Terminating connection - '%s' \n " , " wrepl_recv: called " ) ) ;
wreplsrv_terminate_in_connection ( wrepl_conn , " wrepl_recv: called " ) ;
}
/*
called when we can write to a connection
*/
static void wreplsrv_send ( struct stream_connection * conn , uint16_t flags )
{
struct wreplsrv_in_connection * wrepl_conn = talloc_get_type ( conn - > private_data ,
struct wreplsrv_in_connection ) ;
/* this should never be triggered! */
DEBUG ( 0 , ( " Terminating connection - '%s' \n " , " wrepl_send: called " ) ) ;
wreplsrv_terminate_in_connection ( wrepl_conn , " wrepl_send: called " ) ;
2005-10-14 16:55:41 +04:00
}
static const struct stream_server_ops wreplsrv_stream_ops = {
. name = " wreplsrv " ,
. accept_connection = wreplsrv_accept ,
. recv_handler = wreplsrv_recv ,
. send_handler = wreplsrv_send ,
} ;
2005-10-14 18:02:47 +04:00
/*
called when we get a new connection
*/
NTSTATUS wreplsrv_in_connection_merge ( struct wreplsrv_partner * partner ,
2010-03-05 20:30:10 +03:00
uint32_t peer_assoc_ctx ,
struct tstream_context * * stream ,
2017-09-14 22:09:23 +03:00
struct wreplsrv_in_connection * * _wrepl_in ,
void * process_context )
2005-10-14 18:02:47 +04:00
{
struct wreplsrv_service * service = partner - > service ;
struct wreplsrv_in_connection * wrepl_in ;
struct stream_connection * conn ;
2010-01-20 16:21:47 +03:00
struct tevent_req * subreq ;
2005-10-14 18:02:47 +04:00
NTSTATUS status ;
wrepl_in = talloc_zero ( partner , struct wreplsrv_in_connection ) ;
NT_STATUS_HAVE_NO_MEMORY ( wrepl_in ) ;
wrepl_in - > service = service ;
wrepl_in - > partner = partner ;
2010-03-05 20:30:10 +03:00
wrepl_in - > tstream = talloc_move ( wrepl_in , stream ) ;
wrepl_in - > assoc_ctx . peer_ctx = peer_assoc_ctx ;
2005-10-14 18:02:47 +04:00
2010-01-20 16:21:47 +03:00
status = stream_new_connection_merge ( service - > task - > event_ctx ,
service - > task - > lp_ctx ,
2017-09-18 04:05:24 +03:00
service - > task - > model_ops ,
2010-01-20 16:21:47 +03:00
& wreplsrv_stream_ops ,
service - > task - > msg_ctx ,
wrepl_in ,
2017-09-14 22:09:23 +03:00
& conn ,
process_context ) ;
2005-10-14 18:02:47 +04:00
NT_STATUS_NOT_OK_RETURN ( status ) ;
2005-12-13 00:31:42 +03:00
/*
2009-02-09 21:05:20 +03:00
* make the wreplsrv_in_connection structure a child of the
* stream_connection , to match the hierarchy of wreplsrv_accept
2005-12-13 00:31:42 +03:00
*/
2005-10-14 18:02:47 +04:00
wrepl_in - > conn = conn ;
talloc_steal ( conn , wrepl_in ) ;
2010-01-20 16:21:47 +03:00
wrepl_in - > send_queue = tevent_queue_create ( wrepl_in , " wreplsrv_in_connection_merge " ) ;
if ( wrepl_in - > send_queue = = NULL ) {
stream_terminate_connection ( conn ,
" wreplsrv_in_connection_merge: out of memory " ) ;
return NT_STATUS_NO_MEMORY ;
}
2005-12-13 00:31:42 +03:00
/*
2010-01-20 16:21:47 +03:00
* The wrepl pdu ' s has the length as 4 byte ( initial_read_size ) ,
* packet_full_request_u32 provides the pdu length then .
2005-12-13 00:31:42 +03:00
*/
2010-01-20 16:21:47 +03:00
subreq = tstream_read_pdu_blob_send ( wrepl_in ,
wrepl_in - > conn - > event . ctx ,
wrepl_in - > tstream ,
4 , /* initial_read_size */
packet_full_request_u32 ,
wrepl_in ) ;
if ( subreq = = NULL ) {
wreplsrv_terminate_in_connection ( wrepl_in , " wreplsrv_in_connection_merge: "
" no memory for tstream_read_pdu_blob_send " ) ;
return NT_STATUS_NO_MEMORY ;
}
tevent_req_set_callback ( subreq , wreplsrv_call_loop , wrepl_in ) ;
2005-12-13 00:31:42 +03:00
2005-10-14 18:02:47 +04:00
* _wrepl_in = wrepl_in ;
2010-01-20 16:21:47 +03:00
2005-10-14 18:02:47 +04:00
return NT_STATUS_OK ;
}
2005-10-14 16:55:41 +04:00
/*
startup the wrepl port 42 server sockets
*/
2007-12-02 21:27:49 +03:00
NTSTATUS wreplsrv_setup_sockets ( struct wreplsrv_service * service , struct loadparm_context * lp_ctx )
2005-10-14 16:55:41 +04:00
{
NTSTATUS status ;
struct task_server * task = service - > task ;
const char * address ;
uint16_t port = WINS_REPLICATION_PORT ;
2010-07-16 08:32:42 +04:00
if ( lpcfg_interfaces ( lp_ctx ) & & lpcfg_bind_interfaces_only ( lp_ctx ) ) {
2007-12-12 00:23:14 +03:00
int num_interfaces ;
2005-10-14 16:55:41 +04:00
int i ;
2007-12-12 00:23:14 +03:00
struct interface * ifaces ;
2011-06-02 09:40:28 +04:00
load_interface_list ( task , lp_ctx , & ifaces ) ;
2007-12-12 00:23:14 +03:00
2011-05-02 09:57:19 +04:00
num_interfaces = iface_list_count ( ifaces ) ;
2005-10-14 16:55:41 +04:00
/* We have been given an interfaces line, and been
told to only bind to those interfaces . Create a
socket per interface and bind to only these .
*/
for ( i = 0 ; i < num_interfaces ; i + + ) {
2011-05-12 15:08:45 +04:00
if ( ! iface_list_n_is_v4 ( ifaces , i ) ) {
continue ;
}
2011-05-02 09:57:19 +04:00
address = iface_list_n_ip ( ifaces , i ) ;
2017-09-18 04:05:24 +03:00
status = stream_setup_socket (
task , task - > event_ctx ,
task - > lp_ctx ,
task - > model_ops ,
& wreplsrv_stream_ops ,
" ipv4 " , address , & port ,
lpcfg_socket_options ( task - > lp_ctx ) ,
service , task - > process_context ) ;
2005-10-14 16:55:41 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 0 , ( " stream_setup_socket(address=%s,port=%u) failed - %s \n " ,
address , port , nt_errstr ( status ) ) ) ;
return status ;
}
}
} else {
2012-07-27 06:31:28 +04:00
address = " 0.0.0.0 " ;
2017-09-18 04:05:24 +03:00
status = stream_setup_socket ( task , task - > event_ctx ,
task - > lp_ctx , task - > model_ops ,
& wreplsrv_stream_ops ,
" ipv4 " , address , & port ,
lpcfg_socket_options ( task - > lp_ctx ) ,
2017-09-14 22:09:23 +03:00
service , task - > process_context ) ;
2005-10-14 16:55:41 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 0 , ( " stream_setup_socket(address=%s,port=%u) failed - %s \n " ,
address , port , nt_errstr ( status ) ) ) ;
return status ;
}
}
return NT_STATUS_OK ;
}