2005-01-21 06:54:10 +00:00
/*
Unix SMB / CIFS implementation .
low level socket handling for nbt requests
Copyright ( C ) Andrew Tridgell 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
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"
# include "events.h"
# include "dlinklist.h"
# include "libcli/nbt/libnbt.h"
# define NBT_MAX_PACKET_SIZE 2048
# define NBT_MAX_REPLIES 1000
2005-01-21 11:18:56 +00:00
/*
destroy a nbt socket
*/
static int nbtsock_destructor ( void * ptr )
{
struct nbt_name_socket * nbtsock = talloc_get_type ( ptr , struct nbt_name_socket ) ;
event_remove_fd ( nbtsock - > event_ctx , nbtsock - > fde ) ;
return 0 ;
}
2005-01-21 06:54:10 +00:00
/*
destroy a pending request
*/
static int nbt_name_request_destructor ( void * ptr )
{
struct nbt_name_request * req = talloc_get_type ( ptr , struct nbt_name_request ) ;
if ( req - > state = = NBT_REQUEST_SEND ) {
DLIST_REMOVE ( req - > nbtsock - > send_queue , req ) ;
}
if ( req - > state = = NBT_REQUEST_WAIT ) {
req - > nbtsock - > num_pending - - ;
}
if ( req - > request - > name_trn_id ! = 0 ) {
idr_remove ( req - > nbtsock - > idr , req - > request - > name_trn_id ) ;
req - > request - > name_trn_id = 0 ;
}
if ( req - > te ) {
event_remove_timed ( req - > nbtsock - > event_ctx , req - > te ) ;
req - > te = NULL ;
}
if ( req - > nbtsock - > send_queue = = NULL ) {
req - > nbtsock - > fde - > flags & = ~ EVENT_FD_WRITE ;
}
if ( req - > nbtsock - > num_pending = = 0 ) {
req - > nbtsock - > fde - > flags & = ~ EVENT_FD_READ ;
}
return 0 ;
}
/*
handle send events on a nbt name socket
*/
static void nbt_name_socket_send ( struct nbt_name_socket * nbtsock )
{
struct nbt_name_request * req = nbtsock - > send_queue ;
TALLOC_CTX * tmp_ctx = talloc_new ( req ) ;
NTSTATUS status ;
while ( ( req = nbtsock - > send_queue ) ) {
DATA_BLOB blob ;
size_t len ;
if ( DEBUGLVL ( 10 ) ) {
2005-01-21 13:39:33 +00:00
DEBUG ( 10 , ( " Sending nbt packet to %s:%d \n " ,
req - > dest_addr , req - > dest_port ) ) ;
2005-01-21 06:54:10 +00:00
NDR_PRINT_DEBUG ( nbt_name_packet , req - > request ) ;
}
status = ndr_push_struct_blob ( & blob , tmp_ctx , req - > request ,
( ndr_push_flags_fn_t )
ndr_push_nbt_name_packet ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) goto failed ;
if ( req - > request - > operation & NBT_FLAG_BROADCAST ) {
socket_set_option ( nbtsock - > sock , " SO_BROADCAST " , " 1 " ) ;
}
len = blob . length ;
status = socket_sendto ( nbtsock - > sock , & blob , & len , 0 ,
req - > dest_addr , req - > dest_port ) ;
if ( NT_STATUS_IS_ERR ( status ) ) goto failed ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
talloc_free ( tmp_ctx ) ;
return ;
}
DLIST_REMOVE ( nbtsock - > send_queue , req ) ;
req - > state = NBT_REQUEST_WAIT ;
nbtsock - > fde - > flags | = EVENT_FD_READ ;
nbtsock - > num_pending + + ;
}
nbtsock - > fde - > flags & = ~ EVENT_FD_WRITE ;
talloc_free ( tmp_ctx ) ;
return ;
failed :
DLIST_REMOVE ( nbtsock - > send_queue , req ) ;
nbt_name_request_destructor ( req ) ;
req - > status = status ;
req - > state = NBT_REQUEST_ERROR ;
2005-01-21 11:18:56 +00:00
if ( req - > async . fn ) {
req - > async . fn ( req ) ;
}
2005-01-21 06:54:10 +00:00
talloc_free ( tmp_ctx ) ;
return ;
}
/*
handle recv events on a nbt name socket
*/
static void nbt_name_socket_recv ( struct nbt_name_socket * nbtsock )
{
TALLOC_CTX * tmp_ctx = talloc_new ( nbtsock ) ;
NTSTATUS status ;
const char * src_addr ;
int src_port ;
DATA_BLOB blob ;
size_t nread ;
struct nbt_name_packet * packet ;
struct nbt_name_request * req ;
blob = data_blob_talloc ( tmp_ctx , NULL , NBT_MAX_PACKET_SIZE ) ;
if ( blob . data = = NULL ) {
talloc_free ( tmp_ctx ) ;
return ;
}
status = socket_recvfrom ( nbtsock - > sock , blob . data , blob . length , & nread , 0 ,
& src_addr , & src_port ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
talloc_free ( tmp_ctx ) ;
return ;
}
talloc_steal ( tmp_ctx , src_addr ) ;
2005-01-21 13:39:33 +00:00
blob . length = nread ;
2005-01-21 06:54:10 +00:00
packet = talloc ( tmp_ctx , struct nbt_name_packet ) ;
if ( packet = = NULL ) {
talloc_free ( tmp_ctx ) ;
return ;
}
status = ndr_pull_struct_blob ( & blob , packet , packet ,
( ndr_pull_flags_fn_t ) ndr_pull_nbt_name_packet ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 2 , ( " Failed to parse incoming NBT name packet - %s \n " ,
nt_errstr ( status ) ) ) ;
talloc_free ( tmp_ctx ) ;
return ;
}
if ( DEBUGLVL ( 10 ) ) {
2005-01-21 13:39:33 +00:00
DEBUG ( 10 , ( " Received nbt packet of length %d from %s:%d \n " ,
blob . length , src_addr , src_port ) ) ;
2005-01-21 06:54:10 +00:00
NDR_PRINT_DEBUG ( nbt_name_packet , packet ) ;
}
if ( ! ( packet - > operation & NBT_FLAG_REPLY ) ) {
talloc_free ( tmp_ctx ) ;
return ;
}
/* find the matching request */
req = idr_find ( nbtsock - > idr , packet - > name_trn_id ) ;
if ( req = = NULL ) {
DEBUG ( 2 , ( " Failed to match request for incoming name packet id 0x%04x \n " ,
packet - > name_trn_id ) ) ;
talloc_free ( tmp_ctx ) ;
return ;
}
req - > replies = talloc_realloc ( req , req - > replies , struct nbt_name_reply , req - > num_replies + 1 ) ;
if ( req - > replies = = NULL ) {
nbt_name_request_destructor ( req ) ;
req - > state = NBT_REQUEST_DONE ;
req - > status = NT_STATUS_NO_MEMORY ;
talloc_free ( tmp_ctx ) ;
2005-01-21 11:18:56 +00:00
if ( req - > async . fn ) {
req - > async . fn ( req ) ;
}
2005-01-21 06:54:10 +00:00
return ;
}
req - > replies [ req - > num_replies ] . reply_addr = talloc_steal ( req , src_addr ) ;
req - > replies [ req - > num_replies ] . reply_port = src_port ;
req - > replies [ req - > num_replies ] . packet = talloc_steal ( req , packet ) ;
req - > num_replies + + ;
2005-01-21 11:18:56 +00:00
talloc_free ( tmp_ctx ) ;
2005-01-21 06:54:10 +00:00
/* if we don't want multiple replies then we are done */
if ( ! req - > allow_multiple_replies | |
req - > num_replies = = NBT_MAX_REPLIES ) {
nbt_name_request_destructor ( req ) ;
req - > state = NBT_REQUEST_DONE ;
req - > status = NT_STATUS_OK ;
2005-01-21 11:18:56 +00:00
if ( req - > async . fn ) {
req - > async . fn ( req ) ;
}
2005-01-21 06:54:10 +00:00
}
}
/*
handle fd events on a nbt_name_socket
*/
static void nbt_name_socket_handler ( struct event_context * ev , struct fd_event * fde ,
struct timeval t , uint16_t flags )
{
struct nbt_name_socket * nbtsock = talloc_get_type ( fde - > private ,
struct nbt_name_socket ) ;
if ( flags & EVENT_FD_WRITE ) {
nbt_name_socket_send ( nbtsock ) ;
} else if ( flags & EVENT_FD_READ ) {
nbt_name_socket_recv ( nbtsock ) ;
}
}
/*
initialise a nbt_name_socket . The event_ctx is optional , if provided
then operations will use that event context
*/
struct nbt_name_socket * nbt_name_socket_init ( TALLOC_CTX * mem_ctx ,
struct event_context * event_ctx )
{
struct nbt_name_socket * nbtsock ;
NTSTATUS status ;
struct fd_event fde ;
nbtsock = talloc ( mem_ctx , struct nbt_name_socket ) ;
if ( nbtsock = = NULL ) goto failed ;
if ( event_ctx = = NULL ) {
nbtsock - > event_ctx = event_context_init ( nbtsock ) ;
} else {
nbtsock - > event_ctx = talloc_reference ( nbtsock , event_ctx ) ;
}
if ( nbtsock - > event_ctx = = NULL ) goto failed ;
status = socket_create ( " ip " , SOCKET_TYPE_DGRAM , & nbtsock - > sock , 0 ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) goto failed ;
talloc_steal ( nbtsock , nbtsock - > sock ) ;
nbtsock - > idr = idr_init ( nbtsock ) ;
if ( nbtsock - > idr = = NULL ) goto failed ;
nbtsock - > send_queue = NULL ;
nbtsock - > num_pending = 0 ;
fde . fd = socket_get_fd ( nbtsock - > sock ) ;
fde . flags = 0 ;
fde . handler = nbt_name_socket_handler ;
fde . private = nbtsock ;
nbtsock - > fde = event_add_fd ( nbtsock - > event_ctx , & fde ) ;
2005-01-21 11:18:56 +00:00
talloc_set_destructor ( nbtsock , nbtsock_destructor ) ;
2005-01-21 06:54:10 +00:00
return nbtsock ;
failed :
talloc_free ( nbtsock ) ;
return NULL ;
}
/*
handle a request timeout
*/
static void nbt_name_socket_timeout ( struct event_context * ev , struct timed_event * te ,
struct timeval t )
{
struct nbt_name_request * req = talloc_get_type ( te - > private ,
struct nbt_name_request ) ;
nbt_name_request_destructor ( req ) ;
2005-01-21 11:18:56 +00:00
if ( req - > num_replies = = 0 ) {
req - > state = NBT_REQUEST_TIMEOUT ;
req - > status = NT_STATUS_IO_TIMEOUT ;
} else {
req - > state = NBT_REQUEST_DONE ;
req - > status = NT_STATUS_OK ;
}
if ( req - > async . fn ) {
req - > async . fn ( req ) ;
}
2005-01-21 06:54:10 +00:00
}
/*
send off a nbt name request
*/
struct nbt_name_request * nbt_name_request_send ( struct nbt_name_socket * nbtsock ,
const char * dest_addr , int dest_port ,
struct nbt_name_packet * request ,
struct timeval timeout ,
BOOL allow_multiple_replies )
{
struct nbt_name_request * req ;
struct timed_event te ;
int id ;
req = talloc_zero ( nbtsock , struct nbt_name_request ) ;
if ( req = = NULL ) goto failed ;
req - > nbtsock = nbtsock ;
req - > dest_addr = dest_addr ;
req - > dest_port = dest_port ;
req - > request = talloc_reference ( req , request ) ;
req - > allow_multiple_replies = allow_multiple_replies ;
req - > state = NBT_REQUEST_SEND ;
2005-01-21 11:18:56 +00:00
/* we select a random transaction id unless the user supplied one */
2005-01-21 06:54:10 +00:00
if ( req - > request - > name_trn_id = = 0 ) {
2005-01-21 13:13:24 +00:00
req - > request - > name_trn_id = generate_random ( ) % UINT16_MAX ;
2005-01-21 06:54:10 +00:00
}
2005-01-21 11:18:56 +00:00
/* choose the next available transaction id >= the one asked for.
The strange 2 nd call is to try to make the ids less guessable
and less likely to collide . It ' s not possible to make NBT secure
to ID guessing , but this at least makes accidential collisions
less likely */
2005-01-21 06:54:10 +00:00
id = idr_get_new_above ( req - > nbtsock - > idr , req ,
req - > request - > name_trn_id , UINT16_MAX ) ;
if ( id = = - 1 ) {
2005-01-21 20:38:33 +00:00
id = idr_get_new_above ( req - > nbtsock - > idr , req ,
1 + ( generate_random ( ) % ( UINT16_MAX / 2 ) ) ,
2005-01-21 06:54:10 +00:00
UINT16_MAX ) ;
}
if ( id = = - 1 ) goto failed ;
2005-01-21 20:38:33 +00:00
req - > request - > name_trn_id = id ;
2005-01-21 06:54:10 +00:00
te . next_event = timeout ;
te . handler = nbt_name_socket_timeout ;
te . private = req ;
req - > te = event_add_timed ( nbtsock - > event_ctx , & te ) ;
talloc_set_destructor ( req , nbt_name_request_destructor ) ;
DLIST_ADD_END ( nbtsock - > send_queue , req , struct nbt_name_request * ) ;
nbtsock - > fde - > flags | = EVENT_FD_WRITE ;
return req ;
failed :
talloc_free ( req ) ;
return NULL ;
}
/*
wait for a nbt request to complete
*/
NTSTATUS nbt_name_request_recv ( struct nbt_name_request * req )
{
if ( ! req ) return NT_STATUS_NO_MEMORY ;
while ( req - > state < NBT_REQUEST_DONE ) {
if ( event_loop_once ( req - > nbtsock - > event_ctx ) ! = 0 ) {
req - > state = NBT_REQUEST_ERROR ;
req - > status = NT_STATUS_UNEXPECTED_NETWORK_ERROR ;
2005-01-21 11:18:56 +00:00
if ( req - > async . fn ) {
req - > async . fn ( req ) ;
}
2005-01-21 06:54:10 +00:00
}
}
return req - > status ;
}