2004-10-17 14:04:49 +04:00
/*
Unix SMB / CIFS implementation .
Samba internal messaging functions
Copyright ( C ) Andrew Tridgell 2004
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 2 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 , write to the Free Software
Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
# include "includes.h"
2005-02-03 14:56:03 +03:00
# include "lib/events/events.h"
2005-02-10 08:09:35 +03:00
# include "system/filesys.h"
2006-01-03 18:40:05 +03:00
# include "messaging/messaging.h"
2004-11-02 09:42:15 +03:00
# include "dlinklist.h"
2005-02-10 09:59:29 +03:00
# include "lib/socket/socket.h"
2005-06-05 10:53:07 +04:00
# include "librpc/gen_ndr/ndr_irpc.h"
# include "lib/messaging/irpc.h"
2005-07-10 08:54:21 +04:00
# include "db_wrap.h"
# include "lib/tdb/include/tdbutil.h"
2006-03-19 05:23:52 +03:00
# include "lib/util/unix_privs.h"
2006-03-18 18:42:57 +03:00
# include "librpc/rpc/dcerpc.h"
2004-10-17 14:04:49 +04:00
/* change the message version with any incompatible changes in the protocol */
# define MESSAGING_VERSION 1
2004-10-27 02:45:33 +04:00
struct messaging_context {
2005-01-30 03:54:57 +03:00
uint32_t server_id ;
2004-10-17 14:04:49 +04:00
struct socket_context * sock ;
2005-06-05 11:37:27 +04:00
const char * base_path ;
2005-05-01 22:49:07 +04:00
const char * path ;
2006-04-12 10:08:24 +04:00
struct dispatch_fn * * dispatch ;
uint32_t num_types ;
struct idr_context * dispatch_tree ;
2005-06-03 08:21:25 +04:00
struct messaging_rec * pending ;
2005-06-05 10:53:07 +04:00
struct irpc_list * irpc ;
struct idr_context * idr ;
2005-07-10 08:54:21 +04:00
const char * * names ;
2005-07-19 13:28:13 +04:00
struct timeval start_time ;
2004-10-17 14:04:49 +04:00
struct {
struct event_context * ev ;
struct fd_event * fde ;
} event ;
} ;
2006-04-12 10:08:24 +04:00
/* we have a linked list of dispatch handlers for each msg_type that
this messaging server can deal with */
2004-10-17 14:04:49 +04:00
struct dispatch_fn {
struct dispatch_fn * next , * prev ;
uint32_t msg_type ;
void * private ;
2006-04-12 10:08:24 +04:00
msg_callback_t fn ;
2004-10-17 14:04:49 +04:00
} ;
/* an individual message */
struct messaging_rec {
2005-06-03 08:21:25 +04:00
struct messaging_rec * next , * prev ;
2004-10-27 02:45:33 +04:00
struct messaging_context * msg ;
2004-10-17 14:04:49 +04:00
const char * path ;
2005-06-03 10:04:34 +04:00
struct messaging_header {
2004-10-17 14:04:49 +04:00
uint32_t version ;
uint32_t msg_type ;
2005-01-30 03:54:57 +03:00
uint32_t from ;
uint32_t to ;
2004-10-17 14:04:49 +04:00
uint32_t length ;
2005-06-03 10:04:34 +04:00
} * header ;
2004-10-17 14:04:49 +04:00
2005-06-03 10:04:34 +04:00
DATA_BLOB packet ;
2004-10-17 14:04:49 +04:00
} ;
2005-06-03 08:21:25 +04:00
2005-06-05 10:53:07 +04:00
static void irpc_handler ( struct messaging_context * , void * ,
uint32_t , uint32_t , DATA_BLOB * ) ;
2004-10-17 14:04:49 +04:00
/*
A useful function for testing the message system .
*/
2004-10-27 02:45:33 +04:00
static void ping_message ( struct messaging_context * msg , void * private ,
2005-01-30 03:54:57 +03:00
uint32_t msg_type , uint32_t src , DATA_BLOB * data )
2004-10-17 14:04:49 +04:00
{
DEBUG ( 1 , ( " INFO: Received PING message from server %u [%.*s] \n " ,
2005-07-17 13:20:52 +04:00
( uint_t ) src , ( int ) data - > length ,
data - > data ? ( const char * ) data - > data : " " ) ) ;
2004-10-27 02:45:33 +04:00
messaging_send ( msg , src , MSG_PONG , data ) ;
2004-10-17 14:04:49 +04:00
}
2005-07-19 13:28:13 +04:00
/*
return uptime of messaging server via irpc
*/
static NTSTATUS irpc_uptime ( struct irpc_message * msg ,
struct irpc_uptime * r )
{
struct messaging_context * ctx = talloc_get_type ( msg - > private , struct messaging_context ) ;
* r - > out . start_time = timeval_to_nttime ( & ctx - > start_time ) ;
return NT_STATUS_OK ;
}
2004-10-17 14:04:49 +04:00
/*
return the path to a messaging socket
*/
2005-06-05 11:37:27 +04:00
static char * messaging_path ( struct messaging_context * msg , uint32_t server_id )
2004-10-17 14:04:49 +04:00
{
2005-06-05 11:37:27 +04:00
return talloc_asprintf ( msg , " %s/msg.%u " , msg - > base_path , ( unsigned ) server_id ) ;
2004-10-17 14:04:49 +04:00
}
/*
dispatch a fully received message
2006-01-09 01:58:59 +03:00
note that this deliberately can match more than one message handler
per message . That allows a single messasging context to register
( for example ) a debug handler for more than one piece of code
2004-10-17 14:04:49 +04:00
*/
2004-10-27 02:45:33 +04:00
static void messaging_dispatch ( struct messaging_context * msg , struct messaging_rec * rec )
2004-10-17 14:04:49 +04:00
{
2004-10-18 15:47:13 +04:00
struct dispatch_fn * d , * next ;
2006-04-12 10:08:24 +04:00
/* temporary IDs use an idtree, the rest use a array of pointers */
if ( rec - > header - > msg_type > = MSG_TMP_BASE ) {
d = idr_find ( msg - > dispatch_tree , rec - > header - > msg_type ) ;
} else if ( rec - > header - > msg_type < msg - > num_types ) {
d = msg - > dispatch [ rec - > header - > msg_type ] ;
} else {
d = NULL ;
}
for ( ; d ; d = next ) {
DATA_BLOB data ;
2004-10-18 15:47:13 +04:00
next = d - > next ;
2006-04-12 10:08:24 +04:00
data . data = rec - > packet . data + sizeof ( * rec - > header ) ;
data . length = rec - > header - > length ;
d - > fn ( msg , d - > private , d - > msg_type , rec - > header - > from , & data ) ;
2004-10-17 14:04:49 +04:00
}
2005-06-03 10:04:34 +04:00
rec - > header - > length = 0 ;
2004-10-17 14:04:49 +04:00
}
/*
2005-06-03 08:21:25 +04:00
try to send the message
2004-10-17 14:04:49 +04:00
*/
2005-06-03 08:21:25 +04:00
static NTSTATUS try_send ( struct messaging_rec * rec )
{
struct messaging_context * msg = rec - > msg ;
size_t nsent ;
void * priv ;
NTSTATUS status ;
2006-01-10 01:12:53 +03:00
struct socket_address * path ;
/* rec->path is the path of the *other* socket, where we want
* this to end up */
path = socket_address_from_strings ( msg , msg - > sock - > backend_name ,
rec - > path , 0 ) ;
if ( ! path ) {
return NT_STATUS_NO_MEMORY ;
}
2005-06-03 08:21:25 +04:00
/* we send with privileges so messages work from any context */
priv = root_privileges ( ) ;
2006-01-10 01:12:53 +03:00
status = socket_sendto ( msg - > sock , & rec - > packet , & nsent , 0 , path ) ;
talloc_free ( path ) ;
2005-06-03 08:21:25 +04:00
talloc_free ( priv ) ;
return status ;
}
/*
handle a socket write event
*/
static void messaging_send_handler ( struct messaging_context * msg )
{
while ( msg - > pending ) {
struct messaging_rec * rec = msg - > pending ;
NTSTATUS status ;
status = try_send ( rec ) ;
if ( NT_STATUS_EQUAL ( status , STATUS_MORE_ENTRIES ) ) {
break ;
}
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 1 , ( " messaging: Lost message from %u to %u of type %u - %s \n " ,
2005-06-03 10:04:34 +04:00
rec - > header - > from , rec - > header - > to , rec - > header - > msg_type ,
2005-06-03 08:21:25 +04:00
nt_errstr ( status ) ) ) ;
}
DLIST_REMOVE ( msg - > pending , rec ) ;
talloc_free ( rec ) ;
}
if ( msg - > pending = = NULL ) {
EVENT_FD_NOT_WRITEABLE ( msg - > event . fde ) ;
}
}
/*
handle a new incoming packet
*/
static void messaging_recv_handler ( struct messaging_context * msg )
2004-10-17 14:04:49 +04:00
{
2005-05-01 22:49:07 +04:00
struct messaging_rec * rec ;
2004-10-17 14:04:49 +04:00
NTSTATUS status ;
2005-06-03 10:04:34 +04:00
DATA_BLOB packet ;
2005-05-01 22:49:07 +04:00
size_t msize ;
2004-10-17 14:04:49 +04:00
2005-06-03 10:04:34 +04:00
/* see how many bytes are in the next packet */
2005-06-03 17:20:45 +04:00
status = socket_pending ( msg - > sock , & msize ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 0 , ( " socket_pending failed in messaging - %s \n " ,
nt_errstr ( status ) ) ) ;
2005-06-03 10:04:34 +04:00
return ;
}
2005-06-03 17:20:45 +04:00
packet = data_blob_talloc ( msg , NULL , msize ) ;
2005-06-03 10:04:34 +04:00
if ( packet . data = = NULL ) {
/* assume this is temporary and retry */
return ;
}
2005-06-03 17:20:45 +04:00
status = socket_recv ( msg - > sock , packet . data , msize , & msize , 0 ) ;
2005-05-01 22:49:07 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2005-06-03 10:04:34 +04:00
data_blob_free ( & packet ) ;
2005-05-01 22:49:07 +04:00
return ;
2004-10-17 14:04:49 +04:00
}
2005-06-03 10:04:34 +04:00
if ( msize < sizeof ( * rec - > header ) ) {
2005-07-17 13:20:52 +04:00
DEBUG ( 0 , ( " messaging: bad message of size %d \n " , ( int ) msize ) ) ;
2005-06-03 10:04:34 +04:00
data_blob_free ( & packet ) ;
2005-05-01 22:49:07 +04:00
return ;
2004-10-17 14:04:49 +04:00
}
2005-01-27 10:08:20 +03:00
rec = talloc ( msg , struct messaging_rec ) ;
2004-10-17 14:04:49 +04:00
if ( rec = = NULL ) {
smb_panic ( " Unable to allocate messaging_rec " ) ;
}
2005-06-03 10:04:34 +04:00
talloc_steal ( rec , packet . data ) ;
2005-02-03 05:35:52 +03:00
rec - > msg = msg ;
rec - > path = msg - > path ;
2005-06-03 10:04:34 +04:00
rec - > header = ( struct messaging_header * ) packet . data ;
rec - > packet = packet ;
2005-05-01 22:49:07 +04:00
2005-06-03 10:04:34 +04:00
if ( msize ! = sizeof ( * rec - > header ) + rec - > header - > length ) {
2005-05-01 22:49:07 +04:00
DEBUG ( 0 , ( " messaging: bad message header size %d should be %d \n " ,
2005-07-17 13:20:52 +04:00
rec - > header - > length , ( int ) ( msize - sizeof ( * rec - > header ) ) ) ) ;
2005-05-01 22:49:07 +04:00
talloc_free ( rec ) ;
return ;
}
messaging_dispatch ( msg , rec ) ;
talloc_free ( rec ) ;
2004-10-17 14:04:49 +04:00
}
2005-06-03 08:21:25 +04:00
/*
handle a socket event
*/
static void messaging_handler ( struct event_context * ev , struct fd_event * fde ,
uint16_t flags , void * private )
{
struct messaging_context * msg = talloc_get_type ( private ,
struct messaging_context ) ;
if ( flags & EVENT_FD_WRITE ) {
messaging_send_handler ( msg ) ;
}
if ( flags & EVENT_FD_READ ) {
messaging_recv_handler ( msg ) ;
}
}
2004-10-17 14:04:49 +04:00
/*
Register a dispatch function for a particular message type .
*/
2006-04-12 10:08:24 +04:00
NTSTATUS messaging_register ( struct messaging_context * msg , void * private ,
uint32_t msg_type , msg_callback_t fn )
2004-10-17 14:04:49 +04:00
{
struct dispatch_fn * d ;
2006-04-12 10:08:24 +04:00
/* possibly expand dispatch array */
if ( msg_type > = msg - > num_types ) {
struct dispatch_fn * * dp ;
int i ;
dp = talloc_realloc ( msg , msg - > dispatch , struct dispatch_fn * , msg_type + 1 ) ;
NT_STATUS_HAVE_NO_MEMORY ( dp ) ;
msg - > dispatch = dp ;
for ( i = msg - > num_types ; i < = msg_type ; i + + ) {
msg - > dispatch [ i ] = NULL ;
}
msg - > num_types = msg_type + 1 ;
}
d = talloc ( msg - > dispatch , struct dispatch_fn ) ;
NT_STATUS_HAVE_NO_MEMORY ( d ) ;
2004-10-17 14:04:49 +04:00
d - > msg_type = msg_type ;
d - > private = private ;
d - > fn = fn ;
2006-04-12 10:08:24 +04:00
DLIST_ADD ( msg - > dispatch [ msg_type ] , d ) ;
return NT_STATUS_OK ;
}
/*
register a temporary message handler . The msg_type is allocated
above MSG_TMP_BASE
*/
NTSTATUS messaging_register_tmp ( struct messaging_context * msg , void * private ,
msg_callback_t fn , uint32_t * msg_type )
{
struct dispatch_fn * d ;
int id ;
d = talloc_zero ( msg - > dispatch , struct dispatch_fn ) ;
NT_STATUS_HAVE_NO_MEMORY ( d ) ;
d - > private = private ;
d - > fn = fn ;
id = idr_get_new_above ( msg - > dispatch_tree , d , MSG_TMP_BASE , UINT16_MAX ) ;
if ( id = = - 1 ) {
talloc_free ( d ) ;
return NT_STATUS_TOO_MANY_CONTEXT_IDS ;
}
d - > msg_type = ( uint32_t ) id ;
( * msg_type ) = d - > msg_type ;
return NT_STATUS_OK ;
2004-10-17 14:04:49 +04:00
}
/*
De - register the function for a particular message type .
*/
2004-10-27 02:45:33 +04:00
void messaging_deregister ( struct messaging_context * msg , uint32_t msg_type , void * private )
2004-10-17 14:04:49 +04:00
{
2006-04-12 10:08:24 +04:00
struct dispatch_fn * d , * list , * next ;
2004-10-17 14:04:49 +04:00
2006-04-12 10:08:24 +04:00
if ( msg_type > = msg - > num_types ) {
list = idr_find ( msg - > dispatch_tree , msg_type ) ;
} else {
list = msg - > dispatch [ msg_type ] ;
}
if ( list = = NULL ) {
return ;
}
for ( d = list ; d ; d = next ) {
2004-10-17 14:04:49 +04:00
next = d - > next ;
2006-04-12 10:08:24 +04:00
if ( d - > private = = private ) {
DLIST_REMOVE ( list , d ) ;
2004-10-17 14:04:49 +04:00
talloc_free ( d ) ;
}
}
2006-04-12 10:08:24 +04:00
/* the list base possibly changed */
2006-04-12 13:38:07 +04:00
if ( msg_type > = msg - > num_types ) {
if ( list = = NULL ) {
2006-04-12 10:08:24 +04:00
idr_remove ( msg - > dispatch_tree , msg_type ) ;
}
2006-04-12 13:38:07 +04:00
} else {
msg - > dispatch [ msg_type ] = list ;
2006-04-12 10:08:24 +04:00
}
2004-10-17 14:04:49 +04:00
}
/*
Send a message to a particular server
*/
2005-06-03 08:21:25 +04:00
NTSTATUS messaging_send ( struct messaging_context * msg , uint32_t server ,
uint32_t msg_type , DATA_BLOB * data )
2004-10-17 14:04:49 +04:00
{
struct messaging_rec * rec ;
NTSTATUS status ;
2005-06-03 10:04:34 +04:00
size_t dlength = data ? data - > length : 0 ;
2004-10-17 14:04:49 +04:00
2005-01-27 10:08:20 +03:00
rec = talloc ( msg , struct messaging_rec ) ;
2004-10-17 14:04:49 +04:00
if ( rec = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
2005-06-03 10:04:34 +04:00
rec - > packet = data_blob_talloc ( rec , NULL , sizeof ( * rec - > header ) + dlength ) ;
if ( rec - > packet . data = = NULL ) {
talloc_free ( rec ) ;
return NT_STATUS_NO_MEMORY ;
}
rec - > msg = msg ;
rec - > header = ( struct messaging_header * ) rec - > packet . data ;
rec - > header - > version = MESSAGING_VERSION ;
rec - > header - > msg_type = msg_type ;
rec - > header - > from = msg - > server_id ;
rec - > header - > to = server ;
rec - > header - > length = dlength ;
if ( dlength ! = 0 ) {
memcpy ( rec - > packet . data + sizeof ( * rec - > header ) ,
data - > data , dlength ) ;
2004-10-17 14:04:49 +04:00
}
2005-06-05 11:37:27 +04:00
rec - > path = messaging_path ( msg , server ) ;
talloc_steal ( rec , rec - > path ) ;
2004-10-17 14:04:49 +04:00
2005-06-05 11:44:51 +04:00
if ( msg - > pending ! = NULL ) {
status = STATUS_MORE_ENTRIES ;
} else {
status = try_send ( rec ) ;
}
2004-10-18 02:28:26 +04:00
if ( NT_STATUS_EQUAL ( status , STATUS_MORE_ENTRIES ) ) {
2005-06-03 08:21:25 +04:00
if ( msg - > pending = = NULL ) {
EVENT_FD_WRITEABLE ( msg - > event . fde ) ;
}
2005-06-05 11:30:44 +04:00
DLIST_ADD_END ( msg - > pending , rec , struct messaging_rec * ) ;
2004-10-18 02:28:26 +04:00
return NT_STATUS_OK ;
}
2005-06-03 08:21:25 +04:00
talloc_free ( rec ) ;
2004-10-17 17:33:03 +04:00
2005-06-03 08:21:25 +04:00
return status ;
2004-10-17 14:04:49 +04:00
}
2004-11-03 13:09:48 +03:00
/*
Send a message to a particular server , with the message containing a single pointer
*/
2005-01-30 03:54:57 +03:00
NTSTATUS messaging_send_ptr ( struct messaging_context * msg , uint32_t server ,
2004-11-03 13:09:48 +03:00
uint32_t msg_type , void * ptr )
{
DATA_BLOB blob ;
blob . data = ( void * ) & ptr ;
blob . length = sizeof ( void * ) ;
return messaging_send ( msg , server , msg_type , & blob ) ;
}
2004-10-17 14:04:49 +04:00
/*
destroy the messaging context
*/
2004-10-27 02:45:33 +04:00
static int messaging_destructor ( void * ptr )
2004-10-17 14:04:49 +04:00
{
2004-10-27 02:45:33 +04:00
struct messaging_context * msg = ptr ;
2004-10-25 07:30:39 +04:00
unlink ( msg - > path ) ;
2005-07-10 08:54:21 +04:00
while ( msg - > names & & msg - > names [ 0 ] ) {
irpc_remove_name ( msg , msg - > names [ 0 ] ) ;
}
2004-10-17 14:04:49 +04:00
return 0 ;
}
/*
create the listening socket and setup the dispatcher
*/
2005-05-01 22:49:07 +04:00
struct messaging_context * messaging_init ( TALLOC_CTX * mem_ctx , uint32_t server_id ,
struct event_context * ev )
2004-10-17 14:04:49 +04:00
{
2004-10-27 02:45:33 +04:00
struct messaging_context * msg ;
2004-10-17 14:04:49 +04:00
NTSTATUS status ;
2006-01-10 01:12:53 +03:00
struct socket_address * path ;
char * dir ;
2004-10-17 14:04:49 +04:00
2006-04-12 10:08:24 +04:00
msg = talloc_zero ( mem_ctx , struct messaging_context ) ;
2004-10-17 14:04:49 +04:00
if ( msg = = NULL ) {
return NULL ;
}
2005-07-10 12:35:18 +04:00
if ( ev = = NULL ) {
ev = event_context_init ( msg ) ;
}
2004-10-17 14:04:49 +04:00
/* create the messaging directory if needed */
2006-01-10 01:12:53 +03:00
dir = smbd_tmp_path ( msg , " messaging " ) ;
mkdir ( dir , 0700 ) ;
talloc_free ( dir ) ;
2004-10-17 14:04:49 +04:00
2006-04-12 10:08:24 +04:00
msg - > base_path = smbd_tmp_path ( msg , " messaging " ) ;
msg - > path = messaging_path ( msg , server_id ) ;
msg - > server_id = server_id ;
msg - > idr = idr_init ( msg ) ;
msg - > dispatch_tree = idr_init ( msg ) ;
msg - > start_time = timeval_current ( ) ;
2004-10-17 14:04:49 +04:00
2005-05-01 22:49:07 +04:00
status = socket_create ( " unix " , SOCKET_TYPE_DGRAM , & msg - > sock , 0 ) ;
2004-10-17 14:04:49 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
talloc_free ( msg ) ;
return NULL ;
}
/* by stealing here we ensure that the socket is cleaned up (and even
deleted ) on exit */
talloc_steal ( msg , msg - > sock ) ;
2006-01-10 01:12:53 +03:00
path = socket_address_from_strings ( msg , msg - > sock - > backend_name ,
msg - > path , 0 ) ;
if ( ! path ) {
talloc_free ( msg ) ;
return NULL ;
}
status = socket_listen ( msg - > sock , path , 50 , 0 ) ;
2004-10-17 14:04:49 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2005-07-01 12:14:21 +04:00
DEBUG ( 0 , ( " Unable to setup messaging listener for '%s':%s \n " , msg - > path , nt_errstr ( status ) ) ) ;
2004-10-17 14:04:49 +04:00
talloc_free ( msg ) ;
return NULL ;
}
2005-06-03 08:21:25 +04:00
/* it needs to be non blocking for sends */
set_blocking ( socket_get_fd ( msg - > sock ) , False ) ;
2005-01-30 03:54:57 +03:00
msg - > event . ev = talloc_reference ( msg , ev ) ;
2005-02-03 05:35:52 +03:00
msg - > event . fde = event_add_fd ( ev , msg , socket_get_fd ( msg - > sock ) ,
2005-06-03 08:21:25 +04:00
EVENT_FD_READ , messaging_handler , msg ) ;
2004-10-17 14:04:49 +04:00
talloc_set_destructor ( msg , messaging_destructor ) ;
messaging_register ( msg , NULL , MSG_PING , ping_message ) ;
2005-06-05 10:53:07 +04:00
messaging_register ( msg , NULL , MSG_IRPC , irpc_handler ) ;
2005-07-19 13:28:13 +04:00
IRPC_REGISTER ( msg , irpc , IRPC_UPTIME , irpc_uptime , msg ) ;
2004-10-17 14:04:49 +04:00
return msg ;
}
2005-06-05 10:53:07 +04:00
2006-02-04 01:30:30 +03:00
/*
A hack , for the short term until we get ' client only ' messaging in place
*/
struct messaging_context * messaging_client_init ( TALLOC_CTX * mem_ctx ,
struct event_context * ev )
{
return messaging_init ( mem_ctx , random ( ) % 0x10000000 , ev ) ;
}
2005-06-05 10:53:07 +04:00
/*
a list of registered irpc server functions
*/
struct irpc_list {
struct irpc_list * next , * prev ;
struct GUID uuid ;
const struct dcerpc_interface_table * table ;
int callnum ;
irpc_function_t fn ;
2005-06-06 09:47:14 +04:00
void * private ;
2005-06-05 10:53:07 +04:00
} ;
/*
register a irpc server function
*/
NTSTATUS irpc_register ( struct messaging_context * msg_ctx ,
const struct dcerpc_interface_table * table ,
2005-06-06 09:47:14 +04:00
int callnum , irpc_function_t fn , void * private )
2005-06-05 10:53:07 +04:00
{
struct irpc_list * irpc ;
2005-06-05 11:30:44 +04:00
/* override an existing handler, if any */
for ( irpc = msg_ctx - > irpc ; irpc ; irpc = irpc - > next ) {
if ( irpc - > table = = table & & irpc - > callnum = = callnum ) {
break ;
}
}
if ( irpc = = NULL ) {
irpc = talloc ( msg_ctx , struct irpc_list ) ;
NT_STATUS_HAVE_NO_MEMORY ( irpc ) ;
DLIST_ADD ( msg_ctx - > irpc , irpc ) ;
}
2005-06-05 10:53:07 +04:00
irpc - > table = table ;
2005-06-05 11:30:44 +04:00
irpc - > callnum = callnum ;
2005-06-05 10:53:07 +04:00
irpc - > fn = fn ;
2005-06-06 09:47:14 +04:00
irpc - > private = private ;
2006-03-26 04:59:17 +04:00
irpc - > uuid = irpc - > table - > syntax_id . uuid ;
2005-06-05 10:53:07 +04:00
return NT_STATUS_OK ;
}
/*
handle an incoming irpc reply message
*/
2005-09-25 17:01:26 +04:00
static void irpc_handler_reply ( struct messaging_context * msg_ctx , struct irpc_message * m )
2005-06-05 10:53:07 +04:00
{
struct irpc_request * irpc ;
2005-09-25 17:01:26 +04:00
irpc = idr_find ( msg_ctx - > idr , m - > header . callid ) ;
2005-06-05 10:53:07 +04:00
if ( irpc = = NULL ) return ;
/* parse the reply data */
2005-09-25 17:01:26 +04:00
irpc - > status = irpc - > table - > calls [ irpc - > callnum ] . ndr_pull ( m - > ndr , NDR_OUT , irpc - > r ) ;
2005-06-05 10:53:07 +04:00
if ( NT_STATUS_IS_OK ( irpc - > status ) ) {
2005-09-25 17:01:26 +04:00
irpc - > status = m - > header . status ;
talloc_steal ( irpc - > mem_ctx , m ) ;
2005-08-01 21:33:43 +04:00
} else {
2005-09-25 17:01:26 +04:00
talloc_steal ( irpc , m ) ;
2005-06-05 10:53:07 +04:00
}
irpc - > done = True ;
if ( irpc - > async . fn ) {
irpc - > async . fn ( irpc ) ;
}
}
2005-09-25 17:01:26 +04:00
/*
send a irpc reply
*/
2005-09-25 17:17:03 +04:00
NTSTATUS irpc_send_reply ( struct irpc_message * m , NTSTATUS status )
2005-09-25 17:01:26 +04:00
{
struct ndr_push * push ;
DATA_BLOB packet ;
2005-09-25 17:17:03 +04:00
m - > header . status = status ;
2005-09-25 17:01:26 +04:00
/* setup the reply */
push = ndr_push_init_ctx ( m - > ndr ) ;
if ( push = = NULL ) {
status = NT_STATUS_NO_MEMORY ;
goto failed ;
}
m - > header . flags | = IRPC_FLAG_REPLY ;
/* construct the packet */
status = ndr_push_irpc_header ( push , NDR_SCALARS | NDR_BUFFERS , & m - > header ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) goto failed ;
status = m - > irpc - > table - > calls [ m - > irpc - > callnum ] . ndr_push ( push , NDR_OUT , m - > data ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) goto failed ;
/* send the reply message */
packet = ndr_push_blob ( push ) ;
status = messaging_send ( m - > msg_ctx , m - > from , MSG_IRPC , & packet ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) goto failed ;
failed :
talloc_free ( m ) ;
return status ;
}
2005-06-05 10:53:07 +04:00
/*
handle an incoming irpc request message
*/
static void irpc_handler_request ( struct messaging_context * msg_ctx ,
2005-09-25 17:01:26 +04:00
struct irpc_message * m )
2005-06-05 10:53:07 +04:00
{
struct irpc_list * i ;
void * r ;
NTSTATUS status ;
for ( i = msg_ctx - > irpc ; i ; i = i - > next ) {
2005-09-25 17:01:26 +04:00
if ( GUID_equal ( & i - > uuid , & m - > header . uuid ) & &
2006-03-26 04:59:17 +04:00
i - > table - > syntax_id . if_version = = m - > header . if_version & &
2005-09-25 17:01:26 +04:00
i - > callnum = = m - > header . callnum ) {
2005-06-05 10:53:07 +04:00
break ;
}
}
if ( i = = NULL ) {
/* no registered handler for this message */
2005-09-25 17:01:26 +04:00
talloc_free ( m ) ;
2005-06-05 10:53:07 +04:00
return ;
}
/* allocate space for the structure */
2005-09-25 17:01:26 +04:00
r = talloc_zero_size ( m - > ndr , i - > table - > calls [ m - > header . callnum ] . struct_size ) ;
2005-06-05 10:53:07 +04:00
if ( r = = NULL ) goto failed ;
/* parse the request data */
2005-09-25 17:01:26 +04:00
status = i - > table - > calls [ i - > callnum ] . ndr_pull ( m - > ndr , NDR_IN , r ) ;
2005-06-05 10:53:07 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) goto failed ;
/* make the call */
2005-09-25 17:01:26 +04:00
m - > private = i - > private ;
m - > defer_reply = False ;
m - > msg_ctx = msg_ctx ;
m - > irpc = i ;
m - > data = r ;
2005-09-25 17:17:03 +04:00
m - > ev = msg_ctx - > event . ev ;
2005-06-05 10:53:07 +04:00
2005-09-25 17:01:26 +04:00
m - > header . status = i - > fn ( m , r ) ;
2005-06-05 10:53:07 +04:00
2005-09-25 17:01:26 +04:00
if ( m - > defer_reply ) {
/* the server function has asked to defer the reply to later */
talloc_steal ( msg_ctx , m ) ;
return ;
}
2005-06-05 10:53:07 +04:00
2005-09-25 17:17:03 +04:00
irpc_send_reply ( m , m - > header . status ) ;
2005-09-25 17:01:26 +04:00
return ;
2005-06-05 10:53:07 +04:00
failed :
2005-09-25 17:01:26 +04:00
talloc_free ( m ) ;
2005-06-05 10:53:07 +04:00
}
/*
handle an incoming irpc message
*/
static void irpc_handler ( struct messaging_context * msg_ctx , void * private ,
uint32_t msg_type , uint32_t src , DATA_BLOB * packet )
{
2005-09-25 17:01:26 +04:00
struct irpc_message * m ;
2005-06-05 10:53:07 +04:00
NTSTATUS status ;
2005-09-25 17:01:26 +04:00
m = talloc ( msg_ctx , struct irpc_message ) ;
if ( m = = NULL ) goto failed ;
m - > from = src ;
2005-06-05 10:53:07 +04:00
2005-09-25 17:01:26 +04:00
m - > ndr = ndr_pull_init_blob ( packet , m ) ;
if ( m - > ndr = = NULL ) goto failed ;
2005-07-19 13:28:13 +04:00
2005-09-25 17:01:26 +04:00
m - > ndr - > flags | = LIBNDR_FLAG_REF_ALLOC ;
status = ndr_pull_irpc_header ( m - > ndr , NDR_BUFFERS | NDR_SCALARS , & m - > header ) ;
2005-06-05 10:53:07 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) goto failed ;
2005-09-25 17:01:26 +04:00
if ( m - > header . flags & IRPC_FLAG_REPLY ) {
irpc_handler_reply ( msg_ctx , m ) ;
2005-06-05 10:53:07 +04:00
} else {
2005-09-25 17:01:26 +04:00
irpc_handler_request ( msg_ctx , m ) ;
2005-06-05 10:53:07 +04:00
}
2005-07-10 12:35:18 +04:00
return ;
2005-06-05 10:53:07 +04:00
failed :
2005-09-25 17:01:26 +04:00
talloc_free ( m ) ;
2005-06-05 10:53:07 +04:00
}
/*
destroy a irpc request
*/
static int irpc_destructor ( void * ptr )
{
struct irpc_request * irpc = talloc_get_type ( ptr , struct irpc_request ) ;
idr_remove ( irpc - > msg_ctx - > idr , irpc - > callid ) ;
return 0 ;
}
/*
timeout a irpc request
*/
static void irpc_timeout ( struct event_context * ev , struct timed_event * te ,
struct timeval t , void * private )
{
struct irpc_request * irpc = talloc_get_type ( private , struct irpc_request ) ;
irpc - > status = NT_STATUS_IO_TIMEOUT ;
irpc - > done = True ;
if ( irpc - > async . fn ) {
irpc - > async . fn ( irpc ) ;
}
}
/*
make a irpc call - async send
*/
struct irpc_request * irpc_call_send ( struct messaging_context * msg_ctx ,
uint32_t server_id ,
const struct dcerpc_interface_table * table ,
2005-08-01 21:33:43 +04:00
int callnum , void * r , TALLOC_CTX * ctx )
2005-06-05 10:53:07 +04:00
{
struct irpc_header header ;
struct ndr_push * ndr ;
NTSTATUS status ;
DATA_BLOB packet ;
struct irpc_request * irpc ;
irpc = talloc ( msg_ctx , struct irpc_request ) ;
if ( irpc = = NULL ) goto failed ;
irpc - > msg_ctx = msg_ctx ;
irpc - > table = table ;
irpc - > callnum = callnum ;
irpc - > callid = idr_get_new ( msg_ctx - > idr , irpc , UINT16_MAX ) ;
if ( irpc - > callid = = - 1 ) goto failed ;
irpc - > r = r ;
irpc - > done = False ;
irpc - > async . fn = NULL ;
2005-08-01 21:33:43 +04:00
irpc - > mem_ctx = ctx ;
2005-06-05 10:53:07 +04:00
talloc_set_destructor ( irpc , irpc_destructor ) ;
/* setup the header */
2006-03-26 04:59:17 +04:00
header . uuid = table - > syntax_id . uuid ;
2005-06-05 10:53:07 +04:00
2006-03-26 04:59:17 +04:00
header . if_version = table - > syntax_id . if_version ;
2005-06-05 10:53:07 +04:00
header . callid = irpc - > callid ;
header . callnum = callnum ;
header . flags = 0 ;
header . status = NT_STATUS_OK ;
/* construct the irpc packet */
ndr = ndr_push_init_ctx ( irpc ) ;
if ( ndr = = NULL ) goto failed ;
status = ndr_push_irpc_header ( ndr , NDR_SCALARS | NDR_BUFFERS , & header ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) goto failed ;
status = table - > calls [ callnum ] . ndr_push ( ndr , NDR_IN , r ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) goto failed ;
/* and send it */
packet = ndr_push_blob ( ndr ) ;
status = messaging_send ( msg_ctx , server_id , MSG_IRPC , & packet ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) goto failed ;
event_add_timed ( msg_ctx - > event . ev , irpc ,
timeval_current_ofs ( IRPC_CALL_TIMEOUT , 0 ) ,
irpc_timeout , irpc ) ;
talloc_free ( ndr ) ;
return irpc ;
failed :
talloc_free ( irpc ) ;
return NULL ;
}
/*
wait for a irpc reply
*/
NTSTATUS irpc_call_recv ( struct irpc_request * irpc )
{
2005-12-02 10:30:34 +03:00
NTSTATUS status ;
2005-06-05 10:53:07 +04:00
NT_STATUS_HAVE_NO_MEMORY ( irpc ) ;
2005-12-02 10:30:34 +03:00
2005-06-05 10:53:07 +04:00
while ( ! irpc - > done ) {
if ( event_loop_once ( irpc - > msg_ctx - > event . ev ) ! = 0 ) {
return NT_STATUS_CONNECTION_DISCONNECTED ;
2005-12-02 10:30:34 +03:00
}
2005-06-05 10:53:07 +04:00
}
2005-12-02 10:30:34 +03:00
status = irpc - > status ;
talloc_free ( irpc ) ;
return status ;
2005-06-05 10:53:07 +04:00
}
/*
perform a synchronous irpc request
*/
NTSTATUS irpc_call ( struct messaging_context * msg_ctx ,
uint32_t server_id ,
const struct dcerpc_interface_table * table ,
2005-08-01 21:33:43 +04:00
int callnum , void * r ,
TALLOC_CTX * mem_ctx )
2005-06-05 10:53:07 +04:00
{
struct irpc_request * irpc = irpc_call_send ( msg_ctx , server_id ,
2005-08-01 21:33:43 +04:00
table , callnum , r , mem_ctx ) ;
2005-12-02 10:30:34 +03:00
return irpc_call_recv ( irpc ) ;
2005-06-05 10:53:07 +04:00
}
2005-07-10 05:08:10 +04:00
2005-07-10 08:54:21 +04:00
/*
open the naming database
*/
static struct tdb_wrap * irpc_namedb_open ( struct messaging_context * msg_ctx )
{
struct tdb_wrap * t ;
char * path = talloc_asprintf ( msg_ctx , " %s/names.tdb " , msg_ctx - > base_path ) ;
if ( path = = NULL ) {
return NULL ;
}
t = tdb_wrap_open ( msg_ctx , path , 0 , 0 , O_RDWR | O_CREAT , 0660 ) ;
talloc_free ( path ) ;
return t ;
}
2005-07-10 05:08:10 +04:00
/*
add a string name that this irpc server can be called on
*/
NTSTATUS irpc_add_name ( struct messaging_context * msg_ctx , const char * name )
{
2005-07-10 08:54:21 +04:00
struct tdb_wrap * t ;
TDB_DATA rec ;
int count ;
NTSTATUS status = NT_STATUS_OK ;
t = irpc_namedb_open ( msg_ctx ) ;
NT_STATUS_HAVE_NO_MEMORY ( t ) ;
2005-07-10 10:17:39 +04:00
if ( tdb_lock_bystring ( t - > tdb , name ) ! = 0 ) {
talloc_free ( t ) ;
return NT_STATUS_LOCK_NOT_GRANTED ;
}
2005-07-10 08:54:21 +04:00
rec = tdb_fetch_bystring ( t - > tdb , name ) ;
count = rec . dsize / sizeof ( uint32_t ) ;
2005-09-25 17:01:26 +04:00
rec . dptr = ( unsigned char * ) realloc_p ( rec . dptr , uint32_t , count + 1 ) ;
2005-07-10 08:54:21 +04:00
rec . dsize + = sizeof ( uint32_t ) ;
if ( rec . dptr = = NULL ) {
2005-07-10 10:17:39 +04:00
tdb_unlock_bystring ( t - > tdb , name ) ;
2005-07-10 08:54:21 +04:00
talloc_free ( t ) ;
return NT_STATUS_NO_MEMORY ;
}
( ( uint32_t * ) rec . dptr ) [ count ] = msg_ctx - > server_id ;
if ( tdb_store_bystring ( t - > tdb , name , rec , 0 ) ! = 0 ) {
status = NT_STATUS_INTERNAL_ERROR ;
}
free ( rec . dptr ) ;
2005-07-10 10:17:39 +04:00
tdb_unlock_bystring ( t - > tdb , name ) ;
2005-07-10 08:54:21 +04:00
talloc_free ( t ) ;
msg_ctx - > names = str_list_add ( msg_ctx - > names , name ) ;
talloc_steal ( msg_ctx , msg_ctx - > names ) ;
return status ;
2005-07-10 05:08:10 +04:00
}
2005-07-10 08:54:21 +04:00
/*
return a list of server ids for a server name
*/
uint32_t * irpc_servers_byname ( struct messaging_context * msg_ctx , const char * name )
{
struct tdb_wrap * t ;
TDB_DATA rec ;
int count , i ;
uint32_t * ret ;
t = irpc_namedb_open ( msg_ctx ) ;
if ( t = = NULL ) {
return NULL ;
}
2005-07-10 05:08:10 +04:00
2005-07-10 10:17:39 +04:00
if ( tdb_lock_bystring ( t - > tdb , name ) ! = 0 ) {
talloc_free ( t ) ;
return NULL ;
}
2005-07-10 08:54:21 +04:00
rec = tdb_fetch_bystring ( t - > tdb , name ) ;
if ( rec . dptr = = NULL ) {
2005-07-10 10:17:39 +04:00
tdb_unlock_bystring ( t - > tdb , name ) ;
2005-07-10 08:54:21 +04:00
talloc_free ( t ) ;
return NULL ;
}
count = rec . dsize / sizeof ( uint32_t ) ;
2005-07-10 10:17:39 +04:00
ret = talloc_array ( msg_ctx , uint32_t , count + 1 ) ;
2005-07-10 08:54:21 +04:00
if ( ret = = NULL ) {
2005-07-10 10:17:39 +04:00
tdb_unlock_bystring ( t - > tdb , name ) ;
2005-07-10 08:54:21 +04:00
talloc_free ( t ) ;
return NULL ;
}
for ( i = 0 ; i < count ; i + + ) {
ret [ i ] = ( ( uint32_t * ) rec . dptr ) [ i ] ;
}
2005-07-10 10:17:39 +04:00
ret [ i ] = 0 ;
2005-07-10 08:54:21 +04:00
free ( rec . dptr ) ;
2005-07-10 10:17:39 +04:00
tdb_unlock_bystring ( t - > tdb , name ) ;
2005-07-10 08:54:21 +04:00
talloc_free ( t ) ;
return ret ;
}
/*
remove a name from a messaging context
*/
void irpc_remove_name ( struct messaging_context * msg_ctx , const char * name )
{
struct tdb_wrap * t ;
TDB_DATA rec ;
int count , i ;
uint32_t * ids ;
str_list_remove ( msg_ctx - > names , name ) ;
t = irpc_namedb_open ( msg_ctx ) ;
if ( t = = NULL ) {
return ;
}
2005-07-10 10:17:39 +04:00
if ( tdb_lock_bystring ( t - > tdb , name ) ! = 0 ) {
talloc_free ( t ) ;
return ;
}
2005-07-10 08:54:21 +04:00
rec = tdb_fetch_bystring ( t - > tdb , name ) ;
count = rec . dsize / sizeof ( uint32_t ) ;
if ( count = = 0 ) {
2005-07-10 10:17:39 +04:00
tdb_unlock_bystring ( t - > tdb , name ) ;
2005-07-10 08:54:21 +04:00
talloc_free ( t ) ;
return ;
}
ids = ( uint32_t * ) rec . dptr ;
for ( i = 0 ; i < count ; i + + ) {
if ( ids [ i ] = = msg_ctx - > server_id ) {
if ( i < count - 1 ) {
memmove ( ids + i , ids + i + 1 , count - ( i + 1 ) ) ;
}
rec . dsize - = sizeof ( uint32_t ) ;
break ;
}
}
tdb_store_bystring ( t - > tdb , name , rec , 0 ) ;
free ( rec . dptr ) ;
2005-07-10 10:17:39 +04:00
tdb_unlock_bystring ( t - > tdb , name ) ;
2005-07-10 08:54:21 +04:00
talloc_free ( t ) ;
}