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"
2005-10-14 16:55:41 +04:00
# include "smbd/service_task.h"
# include "smbd/service_stream.h"
2006-03-07 15:08:58 +03:00
# include "smbd/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"
2006-03-07 14:07:23 +03:00
# include "smbd/process_model.h"
# include "system/network.h"
2006-08-17 17:37:04 +04:00
# include "lib/socket/netif.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 ) ;
}
2006-05-24 11:35:06 +04:00
static int terminate_after_send_destructor ( struct wreplsrv_in_connection * * tas )
2005-10-14 16:55:41 +04:00
{
2005-12-13 00:31:42 +03:00
wreplsrv_terminate_in_connection ( * tas , " wreplsrv_in_connection: terminate_after_send " ) ;
return 0 ;
2005-10-14 16:55:41 +04:00
}
/*
receive some data on a WREPL connection
*/
2005-12-13 00:31:42 +03:00
static NTSTATUS wreplsrv_recv_request ( void * private , DATA_BLOB blob )
2005-10-14 16:55:41 +04:00
{
2005-12-13 00:31:42 +03:00
struct wreplsrv_in_connection * wreplconn = talloc_get_type ( private , struct wreplsrv_in_connection ) ;
2005-10-14 16:55:41 +04:00
struct wreplsrv_in_call * call ;
DATA_BLOB packet_in_blob ;
DATA_BLOB packet_out_blob ;
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 ;
2005-10-14 16:55:41 +04:00
call = talloc_zero ( wreplconn , struct wreplsrv_in_call ) ;
2005-12-13 00:31:42 +03:00
NT_STATUS_HAVE_NO_MEMORY ( call ) ;
2005-10-14 16:55:41 +04:00
call - > wreplconn = wreplconn ;
2005-12-13 00:31:42 +03:00
talloc_steal ( call , blob . data ) ;
2005-10-14 16:55:41 +04:00
2005-12-13 00:31:42 +03:00
packet_in_blob . data = blob . data + 4 ;
packet_in_blob . length = blob . length - 4 ;
2005-10-14 16:55:41 +04:00
2008-01-02 07:05:13 +03:00
ndr_err = ndr_pull_struct_blob ( & packet_in_blob , call ,
lp_iconv_convenience ( global_loadparm ) ,
& 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 ) ) {
2006-09-09 14:05:58 +04:00
DEBUG ( 10 , ( " Received WINS-Replication packet of length %u \n " ,
( unsigned ) packet_in_blob . length + 4 ) ) ;
2005-10-14 16:55:41 +04:00
NDR_PRINT_DEBUG ( wrepl_packet , & call - > req_packet ) ;
}
status = wreplsrv_in_call ( call ) ;
2005-12-13 00:31:42 +03:00
NT_STATUS_IS_ERR_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 " ) ) ;
talloc_free ( call ) ;
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 ;
2008-01-02 07:05:05 +03:00
ndr_err = ndr_push_struct_blob ( & packet_out_blob , call ,
lp_iconv_convenience ( global_loadparm ) ,
& packet_out_wrap ,
2005-10-14 16:55:41 +04: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 ) ) {
DEBUG ( 10 , ( " Sending WINS-Replication packet of length %d \n " , ( int ) packet_out_blob . length ) ) ;
NDR_PRINT_DEBUG ( wrepl_packet , & call - > rep_packet ) ;
}
2005-12-13 00:31:42 +03:00
if ( call - > terminate_after_send ) {
struct wreplsrv_in_connection * * tas ;
tas = talloc ( packet_out_blob . data , struct wreplsrv_in_connection * ) ;
NT_STATUS_HAVE_NO_MEMORY ( tas ) ;
* tas = wreplconn ;
talloc_set_destructor ( tas , terminate_after_send_destructor ) ;
2005-10-14 16:55:41 +04:00
}
2005-12-13 00:31:42 +03:00
status = packet_send ( wreplconn - > packet , packet_out_blob ) ;
NT_STATUS_NOT_OK_RETURN ( status ) ;
2005-10-14 16:55:41 +04:00
2005-12-13 00:31:42 +03:00
talloc_free ( call ) ;
return NT_STATUS_OK ;
}
2005-10-14 16:55:41 +04:00
2005-12-13 00:31:42 +03:00
/*
called when the socket becomes readable
*/
static void wreplsrv_recv ( struct stream_connection * conn , uint16_t flags )
{
struct wreplsrv_in_connection * wreplconn = talloc_get_type ( conn - > private ,
struct wreplsrv_in_connection ) ;
2005-10-14 16:55:41 +04:00
2005-12-13 00:31:42 +03:00
packet_recv ( wreplconn - > packet ) ;
2005-10-14 16:55:41 +04:00
}
/*
2005-12-13 00:31:42 +03:00
called when the socket becomes writable
2005-10-14 16:55:41 +04:00
*/
static void wreplsrv_send ( struct stream_connection * conn , uint16_t flags )
{
2005-12-13 00:31:42 +03:00
struct wreplsrv_in_connection * wreplconn = talloc_get_type ( conn - > private ,
struct wreplsrv_in_connection ) ;
packet_queue_run ( wreplconn - > packet ) ;
}
2005-10-14 16:55:41 +04:00
2005-12-13 00:31:42 +03:00
/*
handle socket recv errors
*/
static void wreplsrv_recv_error ( void * private , NTSTATUS status )
{
struct wreplsrv_in_connection * wreplconn = talloc_get_type ( private ,
struct wreplsrv_in_connection ) ;
wreplsrv_terminate_in_connection ( wreplconn , nt_errstr ( status ) ) ;
}
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 )
{
struct wreplsrv_service * service = talloc_get_type ( conn - > private , struct wreplsrv_service ) ;
struct wreplsrv_in_connection * wreplconn ;
2006-01-10 01:12:53 +03:00
struct socket_address * peer_ip ;
2005-10-14 16:55:41 +04:00
2005-12-13 00:31:42 +03:00
wreplconn = talloc_zero ( conn , struct wreplsrv_in_connection ) ;
if ( ! wreplconn ) {
stream_terminate_connection ( conn , " wreplsrv_accept: out of memory " ) ;
return ;
}
2005-10-14 16:55:41 +04:00
2005-12-13 00:31:42 +03:00
wreplconn - > packet = packet_init ( wreplconn ) ;
if ( ! wreplconn - > packet ) {
wreplsrv_terminate_in_connection ( wreplconn , " wreplsrv_accept: out of memory " ) ;
return ;
2005-10-14 16:55:41 +04:00
}
2005-12-13 00:31:42 +03:00
packet_set_private ( wreplconn - > packet , wreplconn ) ;
packet_set_socket ( wreplconn - > packet , conn - > socket ) ;
packet_set_callback ( wreplconn - > packet , wreplsrv_recv_request ) ;
packet_set_full_request ( wreplconn - > packet , packet_full_request_u32 ) ;
packet_set_error_handler ( wreplconn - > packet , wreplsrv_recv_error ) ;
packet_set_event_context ( wreplconn - > packet , conn - > event . ctx ) ;
packet_set_fde ( wreplconn - > packet , conn - > event . fde ) ;
packet_set_serialise ( wreplconn - > packet ) ;
2005-10-14 16:55:41 +04:00
2005-12-13 00:31:42 +03:00
wreplconn - > conn = conn ;
wreplconn - > service = service ;
2005-10-14 16:55:41 +04:00
2005-12-13 00:31:42 +03:00
peer_ip = socket_get_peer_addr ( conn - > socket , wreplconn ) ;
if ( ! peer_ip ) {
2006-01-10 01:12:53 +03:00
wreplsrv_terminate_in_connection ( wreplconn , " wreplsrv_accept: could not obtain peer IP from kernel " ) ;
2005-12-13 00:31:42 +03:00
return ;
}
2005-10-14 16:55:41 +04:00
2006-01-10 01:12:53 +03:00
wreplconn - > partner = wreplsrv_find_partner ( service , peer_ip - > addr ) ;
2005-12-13 00:31:42 +03:00
conn - > private = wreplconn ;
irpc_add_name ( conn - > msg_ctx , " wreplsrv_connection " ) ;
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 ,
struct socket_context * sock ,
2005-12-13 00:31:42 +03:00
struct packet_context * packet ,
2005-10-14 18:02:47 +04:00
struct wreplsrv_in_connection * * _wrepl_in )
{
struct wreplsrv_service * service = partner - > service ;
struct wreplsrv_in_connection * wrepl_in ;
const struct model_ops * model_ops ;
struct stream_connection * conn ;
NTSTATUS status ;
/* within the wrepl task we want to be a single process, so
ask for the single process model ops and pass these to the
stream_setup_socket ( ) call . */
model_ops = process_model_byname ( " single " ) ;
if ( ! model_ops ) {
DEBUG ( 0 , ( " Can't find 'single' process model_ops " ) ) ;
return NT_STATUS_INTERNAL_ERROR ;
}
wrepl_in = talloc_zero ( partner , struct wreplsrv_in_connection ) ;
NT_STATUS_HAVE_NO_MEMORY ( wrepl_in ) ;
wrepl_in - > service = service ;
wrepl_in - > partner = partner ;
status = stream_new_connection_merge ( service - > task - > event_ctx , model_ops ,
sock , & wreplsrv_stream_ops , service - > task - > msg_ctx ,
wrepl_in , & conn ) ;
NT_STATUS_NOT_OK_RETURN ( status ) ;
2005-12-13 00:31:42 +03:00
/*
* make the wreplsrv_in_connection structure a child of the
* stream_connection , to match the hierachie of wreplsrv_accept
*/
2005-10-14 18:02:47 +04:00
wrepl_in - > conn = conn ;
talloc_steal ( conn , wrepl_in ) ;
2005-12-13 00:31:42 +03:00
/*
* now update the packet handling callback , . . .
*/
wrepl_in - > packet = talloc_steal ( wrepl_in , packet ) ;
packet_set_private ( wrepl_in - > packet , wrepl_in ) ;
packet_set_socket ( wrepl_in - > packet , conn - > socket ) ;
packet_set_callback ( wrepl_in - > packet , wreplsrv_recv_request ) ;
packet_set_full_request ( wrepl_in - > packet , packet_full_request_u32 ) ;
packet_set_error_handler ( wrepl_in - > packet , wreplsrv_recv_error ) ;
packet_set_event_context ( wrepl_in - > packet , conn - > event . ctx ) ;
packet_set_fde ( wrepl_in - > packet , conn - > event . fde ) ;
packet_set_serialise ( wrepl_in - > packet ) ;
2005-10-14 18:02:47 +04:00
* _wrepl_in = wrepl_in ;
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 struct model_ops * model_ops ;
const char * address ;
uint16_t port = WINS_REPLICATION_PORT ;
/* within the wrepl task we want to be a single process, so
ask for the single process model ops and pass these to the
stream_setup_socket ( ) call . */
model_ops = process_model_byname ( " single " ) ;
if ( ! model_ops ) {
DEBUG ( 0 , ( " Can't find 'single' process model_ops " ) ) ;
return NT_STATUS_INTERNAL_ERROR ;
}
2007-12-02 21:27:49 +03:00
if ( lp_interfaces ( lp_ctx ) & & lp_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 ;
2007-12-12 00:23:20 +03:00
load_interfaces ( task , lp_interfaces ( lp_ctx ) , & ifaces ) ;
2007-12-12 00:23:14 +03:00
num_interfaces = iface_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 + + ) {
2007-12-12 00:23:14 +03:00
address = iface_n_ip ( ifaces , i ) ;
2005-10-14 16:55:41 +04:00
status = stream_setup_socket ( task - > event_ctx , model_ops , & wreplsrv_stream_ops ,
2007-12-06 18:54:34 +03:00
" ipv4 " , address , & port ,
lp_socket_options ( task - > lp_ctx ) ,
service ) ;
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 {
2007-12-02 21:27:49 +03:00
address = lp_socket_address ( lp_ctx ) ;
2005-10-14 16:55:41 +04:00
status = stream_setup_socket ( task - > event_ctx , model_ops , & wreplsrv_stream_ops ,
2007-12-06 18:54:34 +03:00
" ipv4 " , address , & port , lp_socket_options ( task - > lp_ctx ) ,
service ) ;
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 ;
}