1998-08-17 17:11:34 +04:00
/*
2002-01-30 09:08:46 +03:00
Unix SMB / CIFS implementation .
1998-08-17 17:11:34 +04:00
process incoming packets - main loop
Copyright ( C ) Andrew Tridgell 1992 - 1998
2007-08-02 22:28:41 +04:00
Copyright ( C ) Volker Lendecke 2005 - 2007
1998-08-17 17:11:34 +04:00
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-09 23:25:36 +04:00
the Free Software Foundation ; either version 3 of the License , or
1998-08-17 17:11:34 +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 04:52:41 +04:00
along with this program . If not , see < http : //www.gnu.org/licenses/>.
1998-08-17 17:11:34 +04:00
*/
# include "includes.h"
2009-01-08 14:03:45 +03:00
# include "smbd/globals.h"
1998-08-17 17:11:34 +04:00
2007-10-19 04:40:25 +04:00
extern bool global_machine_password_needs_changing ;
1998-08-17 17:11:34 +04:00
2008-12-19 20:07:44 +03:00
static void construct_reply_common ( struct smb_request * req , const char * inbuf ,
char * outbuf ) ;
2008-11-07 23:02:11 +03:00
2007-11-05 22:12:56 +03:00
/* Accessor function for smb_read_error for smbd functions. */
2008-01-04 23:56:23 +03:00
/****************************************************************************
Send an smb to a fd .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2009-03-09 11:47:59 +03:00
bool srv_send_smb ( int fd , char * buffer ,
bool do_signing , uint32_t seqnum ,
bool do_encrypt ,
struct smb_perfcount_data * pcd )
2008-01-04 23:56:23 +03:00
{
2009-02-09 10:10:34 +03:00
size_t len = 0 ;
2008-01-04 23:56:23 +03:00
size_t nwritten = 0 ;
ssize_t ret ;
char * buf_out = buffer ;
2009-03-09 11:47:59 +03:00
if ( do_signing ) {
/* Sign the outgoing packet if required. */
srv_calculate_sign_mac ( smbd_server_conn , buf_out , seqnum ) ;
}
2008-01-04 23:56:23 +03:00
if ( do_encrypt ) {
NTSTATUS status = srv_encrypt_buffer ( buffer , & buf_out ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 0 , ( " send_smb: SMB encryption failed "
" on outgoing packet! Error %s \n " ,
nt_errstr ( status ) ) ) ;
2009-02-09 10:10:34 +03:00
goto out ;
2008-01-04 23:56:23 +03:00
}
}
len = smb_len ( buf_out ) + 4 ;
2009-04-12 16:05:58 +04:00
ret = write_data ( fd , buf_out + nwritten , len - nwritten ) ;
if ( ret < = 0 ) {
DEBUG ( 0 , ( " Error writing %d bytes to client. %d. (%s) \n " ,
( int ) len , ( int ) ret , strerror ( errno ) ) ) ;
srv_free_enc_buffer ( buf_out ) ;
goto out ;
2008-01-04 23:56:23 +03:00
}
2009-02-09 10:10:34 +03:00
SMB_PERFCOUNT_SET_MSGLEN_OUT ( pcd , len ) ;
2008-01-04 23:56:23 +03:00
srv_free_enc_buffer ( buf_out ) ;
2009-02-09 10:10:34 +03:00
out :
SMB_PERFCOUNT_END ( pcd ) ;
2008-01-04 23:56:23 +03:00
return true ;
}
2007-12-27 04:12:36 +03:00
/*******************************************************************
Setup the word count and byte count for a smb message .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2008-01-04 23:56:23 +03:00
int srv_set_message ( char * buf ,
2007-12-27 04:12:36 +03:00
int num_words ,
int num_bytes ,
bool zero )
{
if ( zero & & ( num_words | | num_bytes ) ) {
memset ( buf + smb_size , ' \0 ' , num_words * 2 + num_bytes ) ;
}
SCVAL ( buf , smb_wct , num_words ) ;
SSVAL ( buf , smb_vwv + num_words * SIZEOFWORD , num_bytes ) ;
2008-01-04 23:56:23 +03:00
smb_setlen ( buf , ( smb_size + num_words * 2 + num_bytes - 4 ) ) ;
2007-12-27 04:12:36 +03:00
return ( smb_size + num_words * 2 + num_bytes ) ;
}
2008-01-04 23:56:23 +03:00
static bool valid_smb_header ( const uint8_t * inbuf )
2007-12-27 04:12:36 +03:00
{
2008-01-04 23:56:23 +03:00
if ( is_encrypted_packet ( inbuf ) ) {
return true ;
2007-12-27 04:12:36 +03:00
}
2008-10-19 16:50:55 +04:00
/*
* This used to be ( strncmp ( smb_base ( inbuf ) , " \377 SMB " , 4 ) = = 0 )
* but it just looks weird to call strncmp for this one .
*/
return ( IVAL ( smb_base ( inbuf ) , 0 ) = = 0x424D53FF ) ;
2007-12-27 04:12:36 +03:00
}
2007-11-01 00:24:52 +03:00
/* Socket functions for smbd packet processing. */
2007-11-05 04:15:35 +03:00
static bool valid_packet_size ( size_t len )
2007-11-01 00:24:52 +03:00
{
/*
* A WRITEX with CAP_LARGE_WRITEX can be 64 k worth of data plus 65 bytes
* of header . Don ' t print the error if this fits . . . . JRA .
*/
if ( len > ( BUFFER_SIZE + LARGE_WRITEX_HDR_SIZE ) ) {
DEBUG ( 0 , ( " Invalid packet length! (%lu bytes). \n " ,
( unsigned long ) len ) ) ;
2008-05-28 20:31:42 +04:00
return false ;
2007-11-01 00:24:52 +03:00
}
return true ;
}
2008-01-26 00:27:59 +03:00
static NTSTATUS read_packet_remainder ( int fd , char * buffer ,
unsigned int timeout , ssize_t len )
2007-11-01 00:24:52 +03:00
{
2008-01-26 00:21:38 +03:00
if ( len < = 0 ) {
2008-01-26 00:27:59 +03:00
return NT_STATUS_OK ;
2008-01-26 00:21:38 +03:00
}
2007-11-01 00:24:52 +03:00
2008-01-26 01:43:50 +03:00
return read_socket_with_timeout ( fd , buffer , len , len , timeout , NULL ) ;
2007-11-01 00:24:52 +03:00
}
/****************************************************************************
Attempt a zerocopy writeX read . We know here that len > smb_size - 4
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*
* Unfortunately , earlier versions of smbclient / libsmbclient
* don ' t send this " standard " writeX header . I ' ve fixed this
* for 3.2 but we ' ll use the old method with earlier versions .
* Windows and CIFSFS at least use this standard size . Not
* sure about MacOSX .
*/
# define STANDARD_WRITE_AND_X_HEADER_SIZE (smb_size - 4 + /* basic header */ \
( 2 * 14 ) + /* word count (including bcc) */ \
1 /* pad byte */ )
2008-01-26 01:12:04 +03:00
static NTSTATUS receive_smb_raw_talloc_partial_read ( TALLOC_CTX * mem_ctx ,
const char lenbuf [ 4 ] ,
int fd , char * * buffer ,
unsigned int timeout ,
size_t * p_unread ,
size_t * len_ret )
2007-11-01 00:24:52 +03:00
{
/* Size of a WRITEX call (+4 byte len). */
char writeX_header [ 4 + STANDARD_WRITE_AND_X_HEADER_SIZE ] ;
ssize_t len = smb_len_large ( lenbuf ) ; /* Could be a UNIX large writeX. */
ssize_t toread ;
2008-01-26 01:12:04 +03:00
NTSTATUS status ;
2007-11-01 00:24:52 +03:00
2008-09-01 15:46:27 +04:00
memcpy ( writeX_header , lenbuf , 4 ) ;
2007-11-01 00:24:52 +03:00
2008-01-26 01:43:50 +03:00
status = read_socket_with_timeout (
2008-01-26 01:12:04 +03:00
fd , writeX_header + 4 ,
STANDARD_WRITE_AND_X_HEADER_SIZE ,
STANDARD_WRITE_AND_X_HEADER_SIZE ,
timeout , NULL ) ;
2007-11-01 00:24:52 +03:00
2008-01-26 01:12:04 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
2007-11-01 00:24:52 +03:00
}
/*
* Ok - now try and see if this is a possible
* valid writeX call .
*/
2008-01-04 23:56:23 +03:00
if ( is_valid_writeX_buffer ( ( uint8_t * ) writeX_header ) ) {
2007-11-01 00:24:52 +03:00
/*
* If the data offset is beyond what
* we ' ve read , drain the extra bytes .
*/
uint16_t doff = SVAL ( writeX_header , smb_vwv11 ) ;
ssize_t newlen ;
if ( doff > STANDARD_WRITE_AND_X_HEADER_SIZE ) {
size_t drain = doff - STANDARD_WRITE_AND_X_HEADER_SIZE ;
if ( drain_socket ( smbd_server_fd ( ) , drain ) ! = drain ) {
smb_panic ( " receive_smb_raw_talloc_partial_read: "
" failed to drain pending bytes " ) ;
}
} else {
doff = STANDARD_WRITE_AND_X_HEADER_SIZE ;
}
/* Spoof down the length and null out the bcc. */
set_message_bcc ( writeX_header , 0 ) ;
newlen = smb_len ( writeX_header ) ;
/* Copy the header we've written. */
2007-11-09 16:23:16 +03:00
* buffer = ( char * ) TALLOC_MEMDUP ( mem_ctx ,
2007-11-01 00:24:52 +03:00
writeX_header ,
sizeof ( writeX_header ) ) ;
if ( * buffer = = NULL ) {
DEBUG ( 0 , ( " Could not allocate inbuf of length %d \n " ,
( int ) sizeof ( writeX_header ) ) ) ;
2008-01-26 01:12:04 +03:00
return NT_STATUS_NO_MEMORY ;
2007-11-01 00:24:52 +03:00
}
/* Work out the remaining bytes. */
* p_unread = len - STANDARD_WRITE_AND_X_HEADER_SIZE ;
2008-01-26 01:12:04 +03:00
* len_ret = newlen + 4 ;
return NT_STATUS_OK ;
2007-11-01 00:24:52 +03:00
}
if ( ! valid_packet_size ( len ) ) {
2008-01-26 01:12:04 +03:00
return NT_STATUS_INVALID_PARAMETER ;
2007-11-01 00:24:52 +03:00
}
/*
* Not a valid writeX call . Just do the standard
* talloc and return .
*/
* buffer = TALLOC_ARRAY ( mem_ctx , char , len + 4 ) ;
if ( * buffer = = NULL ) {
DEBUG ( 0 , ( " Could not allocate inbuf of length %d \n " ,
( int ) len + 4 ) ) ;
2008-01-26 01:12:04 +03:00
return NT_STATUS_NO_MEMORY ;
2007-11-01 00:24:52 +03:00
}
/* Copy in what we already read. */
memcpy ( * buffer ,
writeX_header ,
4 + STANDARD_WRITE_AND_X_HEADER_SIZE ) ;
toread = len - STANDARD_WRITE_AND_X_HEADER_SIZE ;
if ( toread > 0 ) {
2008-01-26 00:27:59 +03:00
status = read_packet_remainder (
fd , ( * buffer ) + 4 + STANDARD_WRITE_AND_X_HEADER_SIZE ,
timeout , toread ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2008-05-20 23:09:48 +04:00
DEBUG ( 10 , ( " receive_smb_raw_talloc_partial_read: %s \n " ,
nt_errstr ( status ) ) ) ;
2008-01-26 01:12:04 +03:00
return status ;
2007-11-01 00:24:52 +03:00
}
}
2008-01-26 01:12:04 +03:00
* len_ret = len + 4 ;
return NT_STATUS_OK ;
2007-11-01 00:24:52 +03:00
}
2008-01-26 01:18:56 +03:00
static NTSTATUS receive_smb_raw_talloc ( TALLOC_CTX * mem_ctx , int fd ,
char * * buffer , unsigned int timeout ,
size_t * p_unread , size_t * plen )
2007-11-01 00:24:52 +03:00
{
char lenbuf [ 4 ] ;
2008-01-25 23:24:48 +03:00
size_t len ;
2007-11-01 00:24:52 +03:00
int min_recv_size = lp_min_receive_file_size ( ) ;
2008-01-25 23:24:48 +03:00
NTSTATUS status ;
2007-11-01 00:24:52 +03:00
* p_unread = 0 ;
2008-01-25 23:24:48 +03:00
status = read_smb_length_return_keepalive ( fd , lenbuf , timeout , & len ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 10 , ( " receive_smb_raw: %s \n " , nt_errstr ( status ) ) ) ;
2008-01-26 01:18:56 +03:00
return status ;
2007-11-01 00:24:52 +03:00
}
2009-02-10 10:43:08 +03:00
if ( CVAL ( lenbuf , 0 ) = = 0 & & min_recv_size & &
( smb_len_large ( lenbuf ) > /* Could be a UNIX large writeX. */
( min_recv_size + STANDARD_WRITE_AND_X_HEADER_SIZE ) ) & &
2009-03-09 11:47:59 +03:00
! srv_is_signing_active ( smbd_server_conn ) ) {
2007-11-01 00:24:52 +03:00
2008-05-20 23:09:48 +04:00
return receive_smb_raw_talloc_partial_read (
mem_ctx , lenbuf , fd , buffer , timeout , p_unread , plen ) ;
2007-11-01 00:24:52 +03:00
}
if ( ! valid_packet_size ( len ) ) {
2008-01-26 01:18:56 +03:00
return NT_STATUS_INVALID_PARAMETER ;
2007-11-01 00:24:52 +03:00
}
/*
* The + 4 here can ' t wrap , we ' ve checked the length above already .
*/
* buffer = TALLOC_ARRAY ( mem_ctx , char , len + 4 ) ;
if ( * buffer = = NULL ) {
DEBUG ( 0 , ( " Could not allocate inbuf of length %d \n " ,
( int ) len + 4 ) ) ;
2008-01-26 01:18:56 +03:00
return NT_STATUS_NO_MEMORY ;
2007-11-01 00:24:52 +03:00
}
memcpy ( * buffer , lenbuf , sizeof ( lenbuf ) ) ;
2008-01-26 00:27:59 +03:00
status = read_packet_remainder ( fd , ( * buffer ) + 4 , timeout , len ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2008-01-26 01:18:56 +03:00
return status ;
2007-11-01 00:24:52 +03:00
}
2008-01-26 01:18:56 +03:00
* plen = len + 4 ;
return NT_STATUS_OK ;
2007-11-01 00:24:52 +03:00
}
2008-01-26 01:28:22 +03:00
static NTSTATUS receive_smb_talloc ( TALLOC_CTX * mem_ctx , int fd ,
char * * buffer , unsigned int timeout ,
size_t * p_unread , bool * p_encrypted ,
2009-03-09 11:47:59 +03:00
size_t * p_len ,
uint32_t * seqnum )
2007-11-01 00:24:52 +03:00
{
2008-02-06 00:36:17 +03:00
size_t len = 0 ;
2008-01-26 01:18:56 +03:00
NTSTATUS status ;
2007-11-01 00:24:52 +03:00
2008-01-04 23:56:23 +03:00
* p_encrypted = false ;
2008-01-26 01:18:56 +03:00
status = receive_smb_raw_talloc ( mem_ctx , fd , buffer , timeout ,
p_unread , & len ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2008-01-26 01:28:22 +03:00
return status ;
2007-11-01 00:24:52 +03:00
}
2008-01-04 23:56:23 +03:00
if ( is_encrypted_packet ( ( uint8_t * ) * buffer ) ) {
2008-01-26 01:18:56 +03:00
status = srv_decrypt_buffer ( * buffer ) ;
2007-12-27 04:12:36 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 0 , ( " receive_smb_talloc: SMB decryption failed on "
" incoming packet! Error %s \n " ,
nt_errstr ( status ) ) ) ;
2008-01-26 01:28:22 +03:00
return status ;
2007-12-27 04:12:36 +03:00
}
2008-01-04 23:56:23 +03:00
* p_encrypted = true ;
2007-12-27 04:12:36 +03:00
}
2007-11-01 00:24:52 +03:00
/* Check the incoming SMB signature. */
2009-03-09 11:47:59 +03:00
if ( ! srv_check_sign_mac ( smbd_server_conn , * buffer , seqnum ) ) {
2007-11-01 00:24:52 +03:00
DEBUG ( 0 , ( " receive_smb: SMB Signature verification failed on "
" incoming packet! \n " ) ) ;
2008-01-26 01:28:22 +03:00
return NT_STATUS_INVALID_NETWORK_RESPONSE ;
2007-11-01 00:24:52 +03:00
}
2008-01-26 01:28:22 +03:00
* p_len = len ;
return NT_STATUS_OK ;
2007-11-01 00:24:52 +03:00
}
2007-07-05 20:26:27 +04:00
/*
* Initialize a struct smb_request from an inbuf
*/
2007-10-31 02:22:24 +03:00
void init_smb_request ( struct smb_request * req ,
const uint8 * inbuf ,
2008-01-04 23:56:23 +03:00
size_t unread_bytes ,
bool encrypted )
2007-07-05 20:26:27 +04:00
{
2009-05-27 13:15:44 +04:00
struct smbd_server_connection * sconn = smbd_server_conn ;
2007-08-15 23:43:26 +04:00
size_t req_size = smb_len ( inbuf ) + 4 ;
/* Ensure we have at least smb_size bytes. */
2007-08-15 23:25:38 +04:00
if ( req_size < smb_size ) {
DEBUG ( 0 , ( " init_smb_request: invalid request size %u \n " ,
( unsigned int ) req_size ) ) ;
exit_server_cleanly ( " Invalid SMB request " ) ;
}
2008-11-03 00:33:20 +03:00
req - > cmd = CVAL ( inbuf , smb_com ) ;
2007-07-05 20:26:27 +04:00
req - > flags2 = SVAL ( inbuf , smb_flg2 ) ;
req - > smbpid = SVAL ( inbuf , smb_pid ) ;
req - > mid = SVAL ( inbuf , smb_mid ) ;
2009-03-09 11:47:59 +03:00
req - > seqnum = 0 ;
2007-07-05 20:26:27 +04:00
req - > vuid = SVAL ( inbuf , smb_uid ) ;
2007-07-23 13:36:09 +04:00
req - > tid = SVAL ( inbuf , smb_tid ) ;
req - > wct = CVAL ( inbuf , smb_wct ) ;
2008-11-02 14:20:47 +03:00
req - > vwv = ( uint16_t * ) ( inbuf + smb_vwv ) ;
2008-11-01 18:24:42 +03:00
req - > buflen = smb_buflen ( inbuf ) ;
2008-11-01 19:35:48 +03:00
req - > buf = ( const uint8_t * ) smb_buf ( inbuf ) ;
2007-10-31 02:22:24 +03:00
req - > unread_bytes = unread_bytes ;
2008-01-04 23:56:23 +03:00
req - > encrypted = encrypted ;
2009-05-27 13:15:44 +04:00
req - > conn = conn_find ( sconn , req - > tid ) ;
2008-10-09 18:55:56 +04:00
req - > chain_fsp = NULL ;
2008-11-10 12:01:26 +03:00
req - > chain_outbuf = NULL ;
2009-07-27 16:47:41 +04:00
req - > done = false ;
2009-02-09 10:10:34 +03:00
smb_init_perfcount_data ( & req - > pcd ) ;
2007-10-31 02:22:24 +03:00
2007-08-17 03:53:51 +04:00
/* Ensure we have at least wct words and 2 bytes of bcc. */
2007-08-15 23:25:38 +04:00
if ( smb_size + req - > wct * 2 > req_size ) {
DEBUG ( 0 , ( " init_smb_request: invalid wct number %u (size %u) \n " ,
( unsigned int ) req - > wct ,
( unsigned int ) req_size ) ) ;
exit_server_cleanly ( " Invalid SMB request " ) ;
}
2007-08-17 03:53:51 +04:00
/* Ensure bcc is correct. */
2008-11-01 18:24:42 +03:00
if ( ( ( uint8 * ) smb_buf ( inbuf ) ) + req - > buflen > inbuf + req_size ) {
2007-08-17 03:53:51 +04:00
DEBUG ( 0 , ( " init_smb_request: invalid bcc number %u "
" (wct = %u, size %u) \n " ,
2008-11-01 18:24:42 +03:00
( unsigned int ) req - > buflen ,
2007-08-17 03:53:51 +04:00
( unsigned int ) req - > wct ,
( unsigned int ) req_size ) ) ;
exit_server_cleanly ( " Invalid SMB request " ) ;
}
2009-02-09 10:10:34 +03:00
2007-07-23 13:36:09 +04:00
req - > outbuf = NULL ;
2007-07-05 20:26:27 +04:00
}
2009-01-08 17:38:47 +03:00
static void process_smb ( struct smbd_server_connection * conn ,
uint8_t * inbuf , size_t nread , size_t unread_bytes ,
2009-03-09 11:47:59 +03:00
uint32_t seqnum , bool encrypted ,
struct smb_perfcount_data * deferred_pcd ) ;
2009-01-08 17:38:47 +03:00
static void smbd_deferred_open_timer ( struct event_context * ev ,
struct timed_event * te ,
struct timeval _tval ,
void * private_data )
{
struct pending_message_list * msg = talloc_get_type ( private_data ,
struct pending_message_list ) ;
TALLOC_CTX * mem_ctx = talloc_tos ( ) ;
uint8_t * inbuf ;
2009-01-10 15:02:43 +03:00
inbuf = ( uint8_t * ) talloc_memdup ( mem_ctx , msg - > buf . data ,
msg - > buf . length ) ;
2009-01-08 17:38:47 +03:00
if ( inbuf = = NULL ) {
exit_server ( " smbd_deferred_open_timer: talloc failed \n " ) ;
return ;
}
/* We leave this message on the queue so the open code can
know this is a retry . */
DEBUG ( 5 , ( " smbd_deferred_open_timer: trigger mid %u. \n " ,
( unsigned int ) SVAL ( msg - > buf . data , smb_mid ) ) ) ;
process_smb ( smbd_server_conn , inbuf ,
msg - > buf . length , 0 ,
2009-03-09 11:47:59 +03:00
msg - > seqnum , msg - > encrypted , & msg - > pcd ) ;
2009-01-08 17:38:47 +03:00
}
1998-09-23 05:48:45 +04:00
/****************************************************************************
2007-10-11 00:34:30 +04:00
Function to push a message onto the tail of a linked list of smb messages ready
for processing .
1998-09-23 05:48:45 +04:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2007-10-19 04:40:25 +04:00
static bool push_queued_message ( struct smb_request * req ,
2005-09-30 21:13:37 +04:00
struct timeval request_time ,
struct timeval end_time ,
char * private_data , size_t private_len )
1998-09-23 05:48:45 +04:00
{
2007-08-27 16:04:09 +04:00
int msg_len = smb_len ( req - > inbuf ) + 4 ;
2005-09-30 21:13:37 +04:00
struct pending_message_list * msg ;
msg = TALLOC_ZERO_P ( NULL , struct pending_message_list ) ;
1998-09-23 05:48:45 +04:00
2003-07-16 22:06:27 +04:00
if ( msg = = NULL ) {
DEBUG ( 0 , ( " push_message: malloc fail (1) \n " ) ) ;
return False ;
}
1998-09-23 05:48:45 +04:00
2007-08-27 16:04:09 +04:00
msg - > buf = data_blob_talloc ( msg , req - > inbuf , msg_len ) ;
2004-06-08 20:14:31 +04:00
if ( msg - > buf . data = = NULL ) {
2003-07-16 22:06:27 +04:00
DEBUG ( 0 , ( " push_message: malloc fail (2) \n " ) ) ;
2006-02-20 20:59:58 +03:00
TALLOC_FREE ( msg ) ;
2003-07-16 22:06:27 +04:00
return False ;
}
1998-09-23 05:48:45 +04:00
2005-09-30 21:13:37 +04:00
msg - > request_time = request_time ;
2009-03-09 11:47:59 +03:00
msg - > seqnum = req - > seqnum ;
2008-01-04 23:56:23 +03:00
msg - > encrypted = req - > encrypted ;
2009-02-09 10:10:34 +03:00
SMB_PERFCOUNT_DEFER_OP ( & req - > pcd , & msg - > pcd ) ;
2004-06-08 20:14:31 +04:00
2005-06-25 00:25:18 +04:00
if ( private_data ) {
2005-09-30 21:13:37 +04:00
msg - > private_data = data_blob_talloc ( msg , private_data ,
private_len ) ;
2004-06-08 20:14:31 +04:00
if ( msg - > private_data . data = = NULL ) {
DEBUG ( 0 , ( " push_message: malloc fail (3) \n " ) ) ;
2006-02-20 20:59:58 +03:00
TALLOC_FREE ( msg ) ;
2005-06-06 19:21:09 +04:00
return False ;
2004-06-08 20:14:31 +04:00
}
}
1998-09-23 05:48:45 +04:00
2009-01-08 17:38:47 +03:00
msg - > te = event_add_timed ( smbd_event_context ( ) ,
msg ,
end_time ,
smbd_deferred_open_timer ,
msg ) ;
if ( ! msg - > te ) {
DEBUG ( 0 , ( " push_message: event_add_timed failed \n " ) ) ;
TALLOC_FREE ( msg ) ;
return false ;
}
2006-09-18 11:52:16 +04:00
DLIST_ADD_END ( deferred_open_queue , msg , struct pending_message_list * ) ;
1998-09-23 05:48:45 +04:00
2005-09-30 21:13:37 +04:00
DEBUG ( 10 , ( " push_message: pushed message length %u on "
" deferred_open_queue \n " , ( unsigned int ) msg_len ) ) ;
2004-06-08 20:14:31 +04:00
2003-07-16 22:06:27 +04:00
return True ;
1998-09-23 05:48:45 +04:00
}
2004-06-08 20:14:31 +04:00
/****************************************************************************
Function to delete a sharing violation open message by mid .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2005-09-30 21:13:37 +04:00
void remove_deferred_open_smb_message ( uint16 mid )
2004-06-08 20:14:31 +04:00
{
struct pending_message_list * pml ;
2005-09-30 21:13:37 +04:00
for ( pml = deferred_open_queue ; pml ; pml = pml - > next ) {
2004-06-08 20:14:31 +04:00
if ( mid = = SVAL ( pml - > buf . data , smb_mid ) ) {
2007-10-11 00:34:30 +04:00
DEBUG ( 10 , ( " remove_sharing_violation_open_smb_message: "
2005-09-30 21:13:37 +04:00
" deleting mid %u len %u \n " ,
( unsigned int ) mid ,
( unsigned int ) pml - > buf . length ) ) ;
DLIST_REMOVE ( deferred_open_queue , pml ) ;
2006-02-20 20:59:58 +03:00
TALLOC_FREE ( pml ) ;
2004-06-08 20:14:31 +04:00
return ;
}
}
}
/****************************************************************************
Move a sharing violation open retry message to the front of the list and
schedule it for immediate processing .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2005-09-30 21:13:37 +04:00
void schedule_deferred_open_smb_message ( uint16 mid )
2004-06-08 20:14:31 +04:00
{
struct pending_message_list * pml ;
int i = 0 ;
2005-09-30 21:13:37 +04:00
for ( pml = deferred_open_queue ; pml ; pml = pml - > next ) {
2004-06-08 20:14:31 +04:00
uint16 msg_mid = SVAL ( pml - > buf . data , smb_mid ) ;
2009-01-08 17:38:47 +03:00
2007-10-11 00:34:30 +04:00
DEBUG ( 10 , ( " schedule_deferred_open_smb_message: [%d] msg_mid = %u \n " , i + + ,
( unsigned int ) msg_mid ) ) ;
2009-01-08 17:38:47 +03:00
2004-06-08 20:14:31 +04:00
if ( mid = = msg_mid ) {
2009-01-08 17:38:47 +03:00
struct timed_event * te ;
2007-10-11 00:34:30 +04:00
DEBUG ( 10 , ( " schedule_deferred_open_smb_message: scheduling mid %u \n " ,
mid ) ) ;
2009-01-08 17:38:47 +03:00
te = event_add_timed ( smbd_event_context ( ) ,
pml ,
timeval_zero ( ) ,
smbd_deferred_open_timer ,
pml ) ;
if ( ! te ) {
DEBUG ( 10 , ( " schedule_deferred_open_smb_message: "
" event_add_timed() failed, skipping mid %u \n " ,
mid ) ) ;
}
TALLOC_FREE ( pml - > te ) ;
pml - > te = te ;
2005-09-30 21:13:37 +04:00
DLIST_PROMOTE ( deferred_open_queue , pml ) ;
2004-06-08 20:14:31 +04:00
return ;
}
}
2007-10-11 00:34:30 +04:00
DEBUG ( 10 , ( " schedule_deferred_open_smb_message: failed to find message mid %u \n " ,
mid ) ) ;
2004-06-08 20:14:31 +04:00
}
/****************************************************************************
Return true if this mid is on the deferred queue .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2007-10-19 04:40:25 +04:00
bool open_was_deferred ( uint16 mid )
2004-06-08 20:14:31 +04:00
{
struct pending_message_list * pml ;
2004-06-26 05:04:02 +04:00
2005-09-30 21:13:37 +04:00
for ( pml = deferred_open_queue ; pml ; pml = pml - > next ) {
2004-06-08 20:14:31 +04:00
if ( SVAL ( pml - > buf . data , smb_mid ) = = mid ) {
return True ;
}
}
return False ;
}
/****************************************************************************
Return the message queued by this mid .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
struct pending_message_list * get_open_deferred_message ( uint16 mid )
{
struct pending_message_list * pml ;
2004-06-26 05:04:02 +04:00
2005-09-30 21:13:37 +04:00
for ( pml = deferred_open_queue ; pml ; pml = pml - > next ) {
2004-06-08 20:14:31 +04:00
if ( SVAL ( pml - > buf . data , smb_mid ) = = mid ) {
return pml ;
}
}
return NULL ;
}
/****************************************************************************
2005-09-30 21:13:37 +04:00
Function to push a deferred open smb message onto a linked list of local smb
messages ready for processing .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2007-10-19 04:40:25 +04:00
bool push_deferred_smb_message ( struct smb_request * req ,
2005-09-30 21:13:37 +04:00
struct timeval request_time ,
struct timeval timeout ,
char * private_data , size_t priv_len )
{
struct timeval end_time ;
2007-10-31 02:22:24 +03:00
if ( req - > unread_bytes ) {
DEBUG ( 0 , ( " push_deferred_smb_message: logic error ! "
" unread_bytes = %u \n " ,
( unsigned int ) req - > unread_bytes ) ) ;
smb_panic ( " push_deferred_smb_message: "
" logic error unread_bytes != 0 " ) ;
}
2005-09-30 21:13:37 +04:00
end_time = timeval_sum ( & request_time , & timeout ) ;
DEBUG ( 10 , ( " push_deferred_open_smb_message: pushing message len %u mid %u "
" timeout time [%u.%06u] \n " ,
2007-08-27 16:04:09 +04:00
( unsigned int ) smb_len ( req - > inbuf ) + 4 , ( unsigned int ) req - > mid ,
2005-09-30 21:13:37 +04:00
( unsigned int ) end_time . tv_sec ,
( unsigned int ) end_time . tv_usec ) ) ;
2007-08-27 16:04:09 +04:00
return push_queued_message ( req , request_time , end_time ,
2005-09-30 21:13:37 +04:00
private_data , priv_len ) ;
}
struct idle_event {
struct timed_event * te ;
struct timeval interval ;
2007-03-18 13:57:46 +03:00
char * name ;
2007-10-19 04:40:25 +04:00
bool ( * handler ) ( const struct timeval * now , void * private_data ) ;
2005-09-30 21:13:37 +04:00
void * private_data ;
} ;
2009-01-05 12:22:50 +03:00
static void smbd_idle_event_handler ( struct event_context * ctx ,
struct timed_event * te ,
struct timeval now ,
void * private_data )
2005-09-30 21:13:37 +04:00
{
struct idle_event * event =
talloc_get_type_abort ( private_data , struct idle_event ) ;
2006-02-20 20:59:58 +03:00
TALLOC_FREE ( event - > te ) ;
2005-09-30 21:13:37 +04:00
2009-01-05 12:22:50 +03:00
DEBUG ( 10 , ( " smbd_idle_event_handler: %s %p called \n " ,
event - > name , event - > te ) ) ;
if ( ! event - > handler ( & now , event - > private_data ) ) {
DEBUG ( 10 , ( " smbd_idle_event_handler: %s %p stopped \n " ,
event - > name , event - > te ) ) ;
2005-09-30 21:13:37 +04:00
/* Don't repeat, delete ourselves */
2006-02-20 20:59:58 +03:00
TALLOC_FREE ( event ) ;
2005-09-30 21:13:37 +04:00
return ;
}
2009-01-05 12:22:50 +03:00
DEBUG ( 10 , ( " smbd_idle_event_handler: %s %p rescheduled \n " ,
event - > name , event - > te ) ) ;
2007-03-18 13:57:46 +03:00
event - > te = event_add_timed ( ctx , event ,
2009-01-05 12:22:50 +03:00
timeval_sum ( & now , & event - > interval ) ,
smbd_idle_event_handler , event ) ;
2005-09-30 21:13:37 +04:00
/* We can't do much but fail here. */
SMB_ASSERT ( event - > te ! = NULL ) ;
}
2007-03-18 13:57:46 +03:00
struct idle_event * event_add_idle ( struct event_context * event_ctx ,
TALLOC_CTX * mem_ctx ,
2005-09-30 21:13:37 +04:00
struct timeval interval ,
2007-03-18 13:57:46 +03:00
const char * name ,
2007-10-19 04:40:25 +04:00
bool ( * handler ) ( const struct timeval * now ,
2005-09-30 21:13:37 +04:00
void * private_data ) ,
void * private_data )
{
struct idle_event * result ;
struct timeval now = timeval_current ( ) ;
result = TALLOC_P ( mem_ctx , struct idle_event ) ;
if ( result = = NULL ) {
DEBUG ( 0 , ( " talloc failed \n " ) ) ;
return NULL ;
}
result - > interval = interval ;
result - > handler = handler ;
result - > private_data = private_data ;
2007-03-18 13:57:46 +03:00
if ( ! ( result - > name = talloc_asprintf ( result , " idle_evt(%s) " , name ) ) ) {
DEBUG ( 0 , ( " talloc failed \n " ) ) ;
TALLOC_FREE ( result ) ;
return NULL ;
}
result - > te = event_add_timed ( event_ctx , result ,
2007-01-17 15:59:14 +03:00
timeval_sum ( & now , & interval ) ,
2009-01-05 12:22:50 +03:00
smbd_idle_event_handler , result ) ;
2005-09-30 21:13:37 +04:00
if ( result - > te = = NULL ) {
2007-01-17 15:59:14 +03:00
DEBUG ( 0 , ( " event_add_timed failed \n " ) ) ;
2006-02-20 20:59:58 +03:00
TALLOC_FREE ( result ) ;
2005-09-30 21:13:37 +04:00
return NULL ;
}
2009-01-05 12:22:50 +03:00
DEBUG ( 10 , ( " event_add_idle: %s %p \n " , result - > name , result - > te ) ) ;
2005-09-30 21:13:37 +04:00
return result ;
}
2006-02-13 07:07:15 +03:00
2009-01-22 01:24:18 +03:00
static void smbd_sig_term_handler ( struct tevent_context * ev ,
struct tevent_signal * se ,
int signum ,
int count ,
void * siginfo ,
void * private_data )
{
exit_server_cleanly ( " termination signal " ) ;
}
void smbd_setup_sig_term_handler ( void )
{
struct tevent_signal * se ;
se = tevent_add_signal ( smbd_event_context ( ) ,
smbd_event_context ( ) ,
SIGTERM , 0 ,
smbd_sig_term_handler ,
NULL ) ;
if ( ! se ) {
exit_server ( " failed to setup SIGTERM handler " ) ;
}
}
static void smbd_sig_hup_handler ( struct tevent_context * ev ,
struct tevent_signal * se ,
int signum ,
int count ,
void * siginfo ,
void * private_data )
{
change_to_root_user ( ) ;
DEBUG ( 1 , ( " Reloading services after SIGHUP \n " ) ) ;
reload_services ( False ) ;
}
void smbd_setup_sig_hup_handler ( void )
{
struct tevent_signal * se ;
se = tevent_add_signal ( smbd_event_context ( ) ,
smbd_event_context ( ) ,
SIGHUP , 0 ,
smbd_sig_hup_handler ,
NULL ) ;
if ( ! se ) {
exit_server ( " failed to setup SIGHUP handler " ) ;
}
}
2009-01-08 17:38:47 +03:00
static NTSTATUS smbd_server_connection_loop_once ( struct smbd_server_connection * conn )
1998-09-23 05:48:45 +04:00
{
2007-01-17 15:59:14 +03:00
fd_set r_fds , w_fds ;
2000-06-11 09:57:58 +04:00
int selrtn ;
2006-04-14 07:55:42 +04:00
struct timeval to ;
2006-02-13 07:07:15 +03:00
int maxfd = 0 ;
1998-09-23 05:48:45 +04:00
2008-10-04 01:18:35 +04:00
to . tv_sec = SMBD_SELECT_TIMEOUT ;
to . tv_usec = 0 ;
2004-06-08 20:14:31 +04:00
2000-06-11 09:57:58 +04:00
/*
2007-01-17 15:59:14 +03:00
* Setup the select fd sets .
2000-06-11 09:57:58 +04:00
*/
2007-01-17 15:59:14 +03:00
FD_ZERO ( & r_fds ) ;
FD_ZERO ( & w_fds ) ;
2002-07-15 14:35:28 +04:00
2006-04-14 07:55:42 +04:00
/*
* Are there any timed events waiting ? If so , ensure we don ' t
* select for longer than it would take to wait for them .
*/
2005-09-30 21:13:37 +04:00
{
2007-01-17 15:59:14 +03:00
struct timeval now ;
GetTimeOfDay ( & now ) ;
event_add_to_select_args ( smbd_event_context ( ) , & now ,
& r_fds , & w_fds , & to , & maxfd ) ;
}
2009-01-20 06:14:20 +03:00
/* Process a signal and timed events now... */
if ( run_events ( smbd_event_context ( ) , 0 , NULL , NULL ) ) {
return NT_STATUS_RETRY ;
2005-09-30 21:13:37 +04:00
}
2009-01-20 06:14:20 +03:00
2006-07-11 22:01:26 +04:00
{
int sav ;
START_PROFILE ( smbd_idle ) ;
2007-01-17 15:59:14 +03:00
selrtn = sys_select ( maxfd + 1 , & r_fds , & w_fds , NULL , & to ) ;
2006-07-11 22:01:26 +04:00
sav = errno ;
END_PROFILE ( smbd_idle ) ;
errno = sav ;
}
2000-06-11 09:57:58 +04:00
2007-01-17 15:59:14 +03:00
if ( run_events ( smbd_event_context ( ) , selrtn , & r_fds , & w_fds ) ) {
2008-12-20 12:42:02 +03:00
return NT_STATUS_RETRY ;
2007-01-17 15:59:14 +03:00
}
2000-06-11 09:57:58 +04:00
/* Check if error */
totally rewrote the async signal, notification and oplock notification
handling in Samba. This was needed due to several limitations and
races in the previous code - as a side effect the new code is much
cleaner :)
in summary:
- changed sys_select() to avoid a signal/select race condition. It is a
rare race but once we have signals doing notification and oplocks it
is important.
- changed our main processing loop to take advantage of the new
sys_select semantics
- split the notify code into implementaion dependent and general
parts. Added the following structure that defines an implementation:
struct cnotify_fns {
void * (*register_notify)(connection_struct *conn, char *path, uint32 flags);
BOOL (*check_notify)(connection_struct *conn, uint16 vuid, char *path, uint32 flags, void *data, time_t t);
void (*remove_notify)(void *data);
};
then I wrote two implementations, one using hash/poll (like our old
code) and the other using the new Linux kernel change notify. It
should be easy to add other change notify implementations by creating
a sructure of the above type.
- fixed a bug in change notify where we were returning the wrong error
code.
- rewrote the core change notify code to be much simpler
- moved to real-time signals for leases and change notify
Amazingly, it all seems to work. I was very surprised!
(This used to be commit 44766c39e0027c762bee8b33b12c621c109a3267)
2000-06-12 19:53:31 +04:00
if ( selrtn = = - 1 ) {
2000-06-11 09:57:58 +04:00
/* something is wrong. Maybe the socket is dead? */
2008-01-26 17:18:33 +03:00
return map_nt_error_from_unix ( errno ) ;
2008-04-01 03:56:21 +04:00
}
2000-06-11 09:57:58 +04:00
/* Did we timeout ? */
if ( selrtn = = 0 ) {
2008-12-20 12:42:02 +03:00
return NT_STATUS_RETRY ;
2000-06-11 09:57:58 +04:00
}
totally rewrote the async signal, notification and oplock notification
handling in Samba. This was needed due to several limitations and
races in the previous code - as a side effect the new code is much
cleaner :)
in summary:
- changed sys_select() to avoid a signal/select race condition. It is a
rare race but once we have signals doing notification and oplocks it
is important.
- changed our main processing loop to take advantage of the new
sys_select semantics
- split the notify code into implementaion dependent and general
parts. Added the following structure that defines an implementation:
struct cnotify_fns {
void * (*register_notify)(connection_struct *conn, char *path, uint32 flags);
BOOL (*check_notify)(connection_struct *conn, uint16 vuid, char *path, uint32 flags, void *data, time_t t);
void (*remove_notify)(void *data);
};
then I wrote two implementations, one using hash/poll (like our old
code) and the other using the new Linux kernel change notify. It
should be easy to add other change notify implementations by creating
a sructure of the above type.
- fixed a bug in change notify where we were returning the wrong error
code.
- rewrote the core change notify code to be much simpler
- moved to real-time signals for leases and change notify
Amazingly, it all seems to work. I was very surprised!
(This used to be commit 44766c39e0027c762bee8b33b12c621c109a3267)
2000-06-12 19:53:31 +04:00
2009-01-21 09:33:19 +03:00
/* should not be reached */
return NT_STATUS_INTERNAL_ERROR ;
1998-09-23 05:48:45 +04:00
}
1998-08-17 17:11:34 +04:00
2006-04-10 19:33:04 +04:00
/*
* Only allow 5 outstanding trans requests . We ' re allocating memory , so
* prevent a DoS .
*/
1998-09-23 05:48:45 +04:00
2006-04-10 19:33:04 +04:00
NTSTATUS allow_new_trans ( struct trans_state * list , int mid )
1998-08-17 17:11:34 +04:00
{
2006-04-10 19:33:04 +04:00
int count = 0 ;
for ( ; list ! = NULL ; list = list - > next ) {
1998-08-17 17:11:34 +04:00
2006-04-10 19:33:04 +04:00
if ( list - > mid = = mid ) {
return NT_STATUS_INVALID_PARAMETER ;
}
count + = 1 ;
}
if ( count > 5 ) {
return NT_STATUS_INSUFFICIENT_RESOURCES ;
}
1998-08-17 17:11:34 +04:00
2006-04-10 19:33:04 +04:00
return NT_STATUS_OK ;
1998-08-17 17:11:34 +04:00
}
/*
These flags determine some of the permissions required to do an operation
Note that I don ' t set NEED_WRITE on some write operations because they
are used by some brain - dead clients when printing , and I don ' t want to
force write permissions on print services .
*/
# define AS_USER (1<<0)
2006-06-20 06:38:28 +04:00
# define NEED_WRITE (1<<1) /* Must be paired with AS_USER */
1998-08-17 17:11:34 +04:00
# define TIME_INIT (1<<2)
2006-06-20 06:38:28 +04:00
# define CAN_IPC (1<<3) /* Must be paired with AS_USER */
# define AS_GUEST (1<<5) /* Must *NOT* be paired with AS_USER */
2005-09-30 21:13:37 +04:00
# define DO_CHDIR (1<<6)
1998-08-17 17:11:34 +04:00
/*
define a list of possible SMB messages and their corresponding
functions . Any message that has a NULL function is unimplemented -
please feel free to contribute implementations !
*/
2003-07-16 22:06:27 +04:00
static const struct smb_message_struct {
const char * name ;
2008-10-19 17:17:12 +04:00
void ( * fn ) ( struct smb_request * req ) ;
2003-07-16 22:06:27 +04:00
int flags ;
} smb_messages [ 256 ] = {
2001-01-24 22:04:56 +03:00
2007-08-27 16:04:09 +04:00
/* 0x00 */ { " SMBmkdir " , reply_mkdir , AS_USER | NEED_WRITE } ,
/* 0x01 */ { " SMBrmdir " , reply_rmdir , AS_USER | NEED_WRITE } ,
/* 0x02 */ { " SMBopen " , reply_open , AS_USER } ,
/* 0x03 */ { " SMBcreate " , reply_mknew , AS_USER } ,
/* 0x04 */ { " SMBclose " , reply_close , AS_USER | CAN_IPC } ,
/* 0x05 */ { " SMBflush " , reply_flush , AS_USER } ,
/* 0x06 */ { " SMBunlink " , reply_unlink , AS_USER | NEED_WRITE } ,
/* 0x07 */ { " SMBmv " , reply_mv , AS_USER | NEED_WRITE } ,
/* 0x08 */ { " SMBgetatr " , reply_getatr , AS_USER } ,
/* 0x09 */ { " SMBsetatr " , reply_setatr , AS_USER | NEED_WRITE } ,
/* 0x0a */ { " SMBread " , reply_read , AS_USER } ,
/* 0x0b */ { " SMBwrite " , reply_write , AS_USER | CAN_IPC } ,
/* 0x0c */ { " SMBlock " , reply_lock , AS_USER } ,
/* 0x0d */ { " SMBunlock " , reply_unlock , AS_USER } ,
/* 0x0e */ { " SMBctemp " , reply_ctemp , AS_USER } ,
/* 0x0f */ { " SMBmknew " , reply_mknew , AS_USER } ,
/* 0x10 */ { " SMBcheckpath " , reply_checkpath , AS_USER } ,
/* 0x11 */ { " SMBexit " , reply_exit , DO_CHDIR } ,
/* 0x12 */ { " SMBlseek " , reply_lseek , AS_USER } ,
/* 0x13 */ { " SMBlockread " , reply_lockread , AS_USER } ,
/* 0x14 */ { " SMBwriteunlock " , reply_writeunlock , AS_USER } ,
/* 0x15 */ { NULL , NULL , 0 } ,
/* 0x16 */ { NULL , NULL , 0 } ,
/* 0x17 */ { NULL , NULL , 0 } ,
/* 0x18 */ { NULL , NULL , 0 } ,
/* 0x19 */ { NULL , NULL , 0 } ,
/* 0x1a */ { " SMBreadbraw " , reply_readbraw , AS_USER } ,
/* 0x1b */ { " SMBreadBmpx " , reply_readbmpx , AS_USER } ,
/* 0x1c */ { " SMBreadBs " , reply_readbs , AS_USER } ,
/* 0x1d */ { " SMBwritebraw " , reply_writebraw , AS_USER } ,
/* 0x1e */ { " SMBwriteBmpx " , reply_writebmpx , AS_USER } ,
/* 0x1f */ { " SMBwriteBs " , reply_writebs , AS_USER } ,
/* 0x20 */ { " SMBwritec " , NULL , 0 } ,
/* 0x21 */ { NULL , NULL , 0 } ,
/* 0x22 */ { " SMBsetattrE " , reply_setattrE , AS_USER | NEED_WRITE } ,
/* 0x23 */ { " SMBgetattrE " , reply_getattrE , AS_USER } ,
/* 0x24 */ { " SMBlockingX " , reply_lockingX , AS_USER } ,
/* 0x25 */ { " SMBtrans " , reply_trans , AS_USER | CAN_IPC } ,
/* 0x26 */ { " SMBtranss " , reply_transs , AS_USER | CAN_IPC } ,
/* 0x27 */ { " SMBioctl " , reply_ioctl , 0 } ,
/* 0x28 */ { " SMBioctls " , NULL , AS_USER } ,
/* 0x29 */ { " SMBcopy " , reply_copy , AS_USER | NEED_WRITE } ,
/* 0x2a */ { " SMBmove " , NULL , AS_USER | NEED_WRITE } ,
/* 0x2b */ { " SMBecho " , reply_echo , 0 } ,
/* 0x2c */ { " SMBwriteclose " , reply_writeclose , AS_USER } ,
/* 0x2d */ { " SMBopenX " , reply_open_and_X , AS_USER | CAN_IPC } ,
/* 0x2e */ { " SMBreadX " , reply_read_and_X , AS_USER | CAN_IPC } ,
/* 0x2f */ { " SMBwriteX " , reply_write_and_X , AS_USER | CAN_IPC } ,
/* 0x30 */ { NULL , NULL , 0 } ,
/* 0x31 */ { NULL , NULL , 0 } ,
/* 0x32 */ { " SMBtrans2 " , reply_trans2 , AS_USER | CAN_IPC } ,
2009-03-23 13:44:00 +03:00
/* 0x33 */ { " SMBtranss2 " , reply_transs2 , AS_USER | CAN_IPC } ,
2007-08-27 16:04:09 +04:00
/* 0x34 */ { " SMBfindclose " , reply_findclose , AS_USER } ,
/* 0x35 */ { " SMBfindnclose " , reply_findnclose , AS_USER } ,
/* 0x36 */ { NULL , NULL , 0 } ,
/* 0x37 */ { NULL , NULL , 0 } ,
/* 0x38 */ { NULL , NULL , 0 } ,
/* 0x39 */ { NULL , NULL , 0 } ,
/* 0x3a */ { NULL , NULL , 0 } ,
/* 0x3b */ { NULL , NULL , 0 } ,
/* 0x3c */ { NULL , NULL , 0 } ,
/* 0x3d */ { NULL , NULL , 0 } ,
/* 0x3e */ { NULL , NULL , 0 } ,
/* 0x3f */ { NULL , NULL , 0 } ,
/* 0x40 */ { NULL , NULL , 0 } ,
/* 0x41 */ { NULL , NULL , 0 } ,
/* 0x42 */ { NULL , NULL , 0 } ,
/* 0x43 */ { NULL , NULL , 0 } ,
/* 0x44 */ { NULL , NULL , 0 } ,
/* 0x45 */ { NULL , NULL , 0 } ,
/* 0x46 */ { NULL , NULL , 0 } ,
/* 0x47 */ { NULL , NULL , 0 } ,
/* 0x48 */ { NULL , NULL , 0 } ,
/* 0x49 */ { NULL , NULL , 0 } ,
/* 0x4a */ { NULL , NULL , 0 } ,
/* 0x4b */ { NULL , NULL , 0 } ,
/* 0x4c */ { NULL , NULL , 0 } ,
/* 0x4d */ { NULL , NULL , 0 } ,
/* 0x4e */ { NULL , NULL , 0 } ,
/* 0x4f */ { NULL , NULL , 0 } ,
/* 0x50 */ { NULL , NULL , 0 } ,
/* 0x51 */ { NULL , NULL , 0 } ,
/* 0x52 */ { NULL , NULL , 0 } ,
/* 0x53 */ { NULL , NULL , 0 } ,
/* 0x54 */ { NULL , NULL , 0 } ,
/* 0x55 */ { NULL , NULL , 0 } ,
/* 0x56 */ { NULL , NULL , 0 } ,
/* 0x57 */ { NULL , NULL , 0 } ,
/* 0x58 */ { NULL , NULL , 0 } ,
/* 0x59 */ { NULL , NULL , 0 } ,
/* 0x5a */ { NULL , NULL , 0 } ,
/* 0x5b */ { NULL , NULL , 0 } ,
/* 0x5c */ { NULL , NULL , 0 } ,
/* 0x5d */ { NULL , NULL , 0 } ,
/* 0x5e */ { NULL , NULL , 0 } ,
/* 0x5f */ { NULL , NULL , 0 } ,
/* 0x60 */ { NULL , NULL , 0 } ,
/* 0x61 */ { NULL , NULL , 0 } ,
/* 0x62 */ { NULL , NULL , 0 } ,
/* 0x63 */ { NULL , NULL , 0 } ,
/* 0x64 */ { NULL , NULL , 0 } ,
/* 0x65 */ { NULL , NULL , 0 } ,
/* 0x66 */ { NULL , NULL , 0 } ,
/* 0x67 */ { NULL , NULL , 0 } ,
/* 0x68 */ { NULL , NULL , 0 } ,
/* 0x69 */ { NULL , NULL , 0 } ,
/* 0x6a */ { NULL , NULL , 0 } ,
/* 0x6b */ { NULL , NULL , 0 } ,
/* 0x6c */ { NULL , NULL , 0 } ,
/* 0x6d */ { NULL , NULL , 0 } ,
/* 0x6e */ { NULL , NULL , 0 } ,
/* 0x6f */ { NULL , NULL , 0 } ,
/* 0x70 */ { " SMBtcon " , reply_tcon , 0 } ,
/* 0x71 */ { " SMBtdis " , reply_tdis , DO_CHDIR } ,
/* 0x72 */ { " SMBnegprot " , reply_negprot , 0 } ,
/* 0x73 */ { " SMBsesssetupX " , reply_sesssetup_and_X , 0 } ,
/* 0x74 */ { " SMBulogoffX " , reply_ulogoffX , 0 } , /* ulogoff doesn't give a valid TID */
/* 0x75 */ { " SMBtconX " , reply_tcon_and_X , 0 } ,
/* 0x76 */ { NULL , NULL , 0 } ,
/* 0x77 */ { NULL , NULL , 0 } ,
/* 0x78 */ { NULL , NULL , 0 } ,
/* 0x79 */ { NULL , NULL , 0 } ,
/* 0x7a */ { NULL , NULL , 0 } ,
/* 0x7b */ { NULL , NULL , 0 } ,
/* 0x7c */ { NULL , NULL , 0 } ,
/* 0x7d */ { NULL , NULL , 0 } ,
/* 0x7e */ { NULL , NULL , 0 } ,
/* 0x7f */ { NULL , NULL , 0 } ,
/* 0x80 */ { " SMBdskattr " , reply_dskattr , AS_USER } ,
/* 0x81 */ { " SMBsearch " , reply_search , AS_USER } ,
/* 0x82 */ { " SMBffirst " , reply_search , AS_USER } ,
/* 0x83 */ { " SMBfunique " , reply_search , AS_USER } ,
/* 0x84 */ { " SMBfclose " , reply_fclose , AS_USER } ,
/* 0x85 */ { NULL , NULL , 0 } ,
/* 0x86 */ { NULL , NULL , 0 } ,
/* 0x87 */ { NULL , NULL , 0 } ,
/* 0x88 */ { NULL , NULL , 0 } ,
/* 0x89 */ { NULL , NULL , 0 } ,
/* 0x8a */ { NULL , NULL , 0 } ,
/* 0x8b */ { NULL , NULL , 0 } ,
/* 0x8c */ { NULL , NULL , 0 } ,
/* 0x8d */ { NULL , NULL , 0 } ,
/* 0x8e */ { NULL , NULL , 0 } ,
/* 0x8f */ { NULL , NULL , 0 } ,
/* 0x90 */ { NULL , NULL , 0 } ,
/* 0x91 */ { NULL , NULL , 0 } ,
/* 0x92 */ { NULL , NULL , 0 } ,
/* 0x93 */ { NULL , NULL , 0 } ,
/* 0x94 */ { NULL , NULL , 0 } ,
/* 0x95 */ { NULL , NULL , 0 } ,
/* 0x96 */ { NULL , NULL , 0 } ,
/* 0x97 */ { NULL , NULL , 0 } ,
/* 0x98 */ { NULL , NULL , 0 } ,
/* 0x99 */ { NULL , NULL , 0 } ,
/* 0x9a */ { NULL , NULL , 0 } ,
/* 0x9b */ { NULL , NULL , 0 } ,
/* 0x9c */ { NULL , NULL , 0 } ,
/* 0x9d */ { NULL , NULL , 0 } ,
/* 0x9e */ { NULL , NULL , 0 } ,
/* 0x9f */ { NULL , NULL , 0 } ,
/* 0xa0 */ { " SMBnttrans " , reply_nttrans , AS_USER | CAN_IPC } ,
/* 0xa1 */ { " SMBnttranss " , reply_nttranss , AS_USER | CAN_IPC } ,
/* 0xa2 */ { " SMBntcreateX " , reply_ntcreate_and_X , AS_USER | CAN_IPC } ,
/* 0xa3 */ { NULL , NULL , 0 } ,
/* 0xa4 */ { " SMBntcancel " , reply_ntcancel , 0 } ,
/* 0xa5 */ { " SMBntrename " , reply_ntrename , AS_USER | NEED_WRITE } ,
/* 0xa6 */ { NULL , NULL , 0 } ,
/* 0xa7 */ { NULL , NULL , 0 } ,
/* 0xa8 */ { NULL , NULL , 0 } ,
/* 0xa9 */ { NULL , NULL , 0 } ,
/* 0xaa */ { NULL , NULL , 0 } ,
/* 0xab */ { NULL , NULL , 0 } ,
/* 0xac */ { NULL , NULL , 0 } ,
/* 0xad */ { NULL , NULL , 0 } ,
/* 0xae */ { NULL , NULL , 0 } ,
/* 0xaf */ { NULL , NULL , 0 } ,
/* 0xb0 */ { NULL , NULL , 0 } ,
/* 0xb1 */ { NULL , NULL , 0 } ,
/* 0xb2 */ { NULL , NULL , 0 } ,
/* 0xb3 */ { NULL , NULL , 0 } ,
/* 0xb4 */ { NULL , NULL , 0 } ,
/* 0xb5 */ { NULL , NULL , 0 } ,
/* 0xb6 */ { NULL , NULL , 0 } ,
/* 0xb7 */ { NULL , NULL , 0 } ,
/* 0xb8 */ { NULL , NULL , 0 } ,
/* 0xb9 */ { NULL , NULL , 0 } ,
/* 0xba */ { NULL , NULL , 0 } ,
/* 0xbb */ { NULL , NULL , 0 } ,
/* 0xbc */ { NULL , NULL , 0 } ,
/* 0xbd */ { NULL , NULL , 0 } ,
/* 0xbe */ { NULL , NULL , 0 } ,
/* 0xbf */ { NULL , NULL , 0 } ,
/* 0xc0 */ { " SMBsplopen " , reply_printopen , AS_USER } ,
/* 0xc1 */ { " SMBsplwr " , reply_printwrite , AS_USER } ,
/* 0xc2 */ { " SMBsplclose " , reply_printclose , AS_USER } ,
/* 0xc3 */ { " SMBsplretq " , reply_printqueue , AS_USER } ,
/* 0xc4 */ { NULL , NULL , 0 } ,
/* 0xc5 */ { NULL , NULL , 0 } ,
/* 0xc6 */ { NULL , NULL , 0 } ,
/* 0xc7 */ { NULL , NULL , 0 } ,
/* 0xc8 */ { NULL , NULL , 0 } ,
/* 0xc9 */ { NULL , NULL , 0 } ,
/* 0xca */ { NULL , NULL , 0 } ,
/* 0xcb */ { NULL , NULL , 0 } ,
/* 0xcc */ { NULL , NULL , 0 } ,
/* 0xcd */ { NULL , NULL , 0 } ,
/* 0xce */ { NULL , NULL , 0 } ,
/* 0xcf */ { NULL , NULL , 0 } ,
/* 0xd0 */ { " SMBsends " , reply_sends , AS_GUEST } ,
/* 0xd1 */ { " SMBsendb " , NULL , AS_GUEST } ,
/* 0xd2 */ { " SMBfwdname " , NULL , AS_GUEST } ,
/* 0xd3 */ { " SMBcancelf " , NULL , AS_GUEST } ,
/* 0xd4 */ { " SMBgetmac " , NULL , AS_GUEST } ,
/* 0xd5 */ { " SMBsendstrt " , reply_sendstrt , AS_GUEST } ,
/* 0xd6 */ { " SMBsendend " , reply_sendend , AS_GUEST } ,
/* 0xd7 */ { " SMBsendtxt " , reply_sendtxt , AS_GUEST } ,
/* 0xd8 */ { NULL , NULL , 0 } ,
/* 0xd9 */ { NULL , NULL , 0 } ,
/* 0xda */ { NULL , NULL , 0 } ,
/* 0xdb */ { NULL , NULL , 0 } ,
/* 0xdc */ { NULL , NULL , 0 } ,
/* 0xdd */ { NULL , NULL , 0 } ,
/* 0xde */ { NULL , NULL , 0 } ,
/* 0xdf */ { NULL , NULL , 0 } ,
/* 0xe0 */ { NULL , NULL , 0 } ,
/* 0xe1 */ { NULL , NULL , 0 } ,
/* 0xe2 */ { NULL , NULL , 0 } ,
/* 0xe3 */ { NULL , NULL , 0 } ,
/* 0xe4 */ { NULL , NULL , 0 } ,
/* 0xe5 */ { NULL , NULL , 0 } ,
/* 0xe6 */ { NULL , NULL , 0 } ,
/* 0xe7 */ { NULL , NULL , 0 } ,
/* 0xe8 */ { NULL , NULL , 0 } ,
/* 0xe9 */ { NULL , NULL , 0 } ,
/* 0xea */ { NULL , NULL , 0 } ,
/* 0xeb */ { NULL , NULL , 0 } ,
/* 0xec */ { NULL , NULL , 0 } ,
/* 0xed */ { NULL , NULL , 0 } ,
/* 0xee */ { NULL , NULL , 0 } ,
/* 0xef */ { NULL , NULL , 0 } ,
/* 0xf0 */ { NULL , NULL , 0 } ,
/* 0xf1 */ { NULL , NULL , 0 } ,
/* 0xf2 */ { NULL , NULL , 0 } ,
/* 0xf3 */ { NULL , NULL , 0 } ,
/* 0xf4 */ { NULL , NULL , 0 } ,
/* 0xf5 */ { NULL , NULL , 0 } ,
/* 0xf6 */ { NULL , NULL , 0 } ,
/* 0xf7 */ { NULL , NULL , 0 } ,
/* 0xf8 */ { NULL , NULL , 0 } ,
/* 0xf9 */ { NULL , NULL , 0 } ,
/* 0xfa */ { NULL , NULL , 0 } ,
/* 0xfb */ { NULL , NULL , 0 } ,
/* 0xfc */ { NULL , NULL , 0 } ,
/* 0xfd */ { NULL , NULL , 0 } ,
/* 0xfe */ { NULL , NULL , 0 } ,
/* 0xff */ { NULL , NULL , 0 }
2001-01-24 22:04:56 +03:00
} ;
1998-08-17 17:11:34 +04:00
2007-07-23 13:36:09 +04:00
/*******************************************************************
allocate and initialize a reply packet
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2008-12-19 20:07:44 +03:00
static bool create_outbuf ( TALLOC_CTX * mem_ctx , struct smb_request * req ,
const char * inbuf , char * * outbuf , uint8_t num_words ,
uint32_t num_bytes )
2007-07-23 13:36:09 +04:00
{
2007-08-12 16:15:32 +04:00
/*
* Protect against integer wrap
*/
if ( ( num_bytes > 0xffffff )
| | ( ( num_bytes + smb_size + num_words * 2 ) > 0xffffff ) ) {
char * msg ;
2008-04-02 17:54:49 +04:00
if ( asprintf ( & msg , " num_bytes too large: %u " ,
( unsigned ) num_bytes ) = = - 1 ) {
msg = CONST_DISCARD ( char * , " num_bytes too large " ) ;
}
2007-08-12 16:15:32 +04:00
smb_panic ( msg ) ;
}
2008-04-02 17:34:29 +04:00
* outbuf = TALLOC_ARRAY ( mem_ctx , char ,
smb_size + num_words * 2 + num_bytes ) ;
if ( * outbuf = = NULL ) {
return false ;
2007-07-23 13:36:09 +04:00
}
2008-12-19 20:07:44 +03:00
construct_reply_common ( req , inbuf , * outbuf ) ;
2008-04-02 17:34:29 +04:00
srv_set_message ( * outbuf , num_words , num_bytes , false ) ;
2007-07-23 13:36:09 +04:00
/*
* Zero out the word area , the caller has to take care of the bcc area
* himself
*/
if ( num_words ! = 0 ) {
2008-04-02 17:34:29 +04:00
memset ( * outbuf + smb_vwv0 , 0 , num_words * 2 ) ;
2007-07-23 13:36:09 +04:00
}
2008-04-02 17:34:29 +04:00
return true ;
}
void reply_outbuf ( struct smb_request * req , uint8 num_words , uint32 num_bytes )
{
char * outbuf ;
2008-12-19 20:07:44 +03:00
if ( ! create_outbuf ( req , req , ( char * ) req - > inbuf , & outbuf , num_words ,
2008-04-02 17:34:29 +04:00
num_bytes ) ) {
smb_panic ( " could not allocate output buffer \n " ) ;
}
req - > outbuf = ( uint8_t * ) outbuf ;
2007-07-23 13:36:09 +04:00
}
2000-05-27 13:19:57 +04:00
/*******************************************************************
2002-09-25 19:19:00 +04:00
Dump a packet to a file .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2007-07-23 13:36:09 +04:00
static void smb_dump ( const char * name , int type , const char * data , ssize_t len )
2000-05-27 13:19:57 +04:00
{
int fd , i ;
2007-11-12 08:46:52 +03:00
char * fname = NULL ;
if ( DEBUGLEVEL < 50 ) {
return ;
}
2000-05-27 13:19:57 +04:00
2000-05-29 00:11:04 +04:00
if ( len < 4 ) len = smb_len ( data ) + 4 ;
2000-05-27 13:19:57 +04:00
for ( i = 1 ; i < 100 ; i + + ) {
2008-04-02 17:54:49 +04:00
if ( asprintf ( & fname , " /tmp/%s.%d.%s " , name , i ,
type ? " req " : " resp " ) = = - 1 ) {
2007-11-12 08:46:52 +03:00
return ;
}
2000-05-27 13:19:57 +04:00
fd = open ( fname , O_WRONLY | O_CREAT | O_EXCL , 0644 ) ;
if ( fd ! = - 1 | | errno ! = EEXIST ) break ;
}
if ( fd ! = - 1 ) {
2002-01-20 00:29:20 +03:00
ssize_t ret = write ( fd , data , len ) ;
if ( ret ! = len )
DEBUG ( 0 , ( " smb_dump: problem: write returned %d \n " , ( int ) ret ) ) ;
2000-05-27 13:19:57 +04:00
close ( fd ) ;
2003-11-05 03:16:01 +03:00
DEBUG ( 0 , ( " created %s len %lu \n " , fname , ( unsigned long ) len ) ) ;
2000-05-27 13:19:57 +04:00
}
2007-11-12 08:46:52 +03:00
SAFE_FREE ( fname ) ;
2000-05-27 13:19:57 +04:00
}
1998-08-17 17:11:34 +04:00
/****************************************************************************
2007-07-23 13:36:09 +04:00
Prepare everything for calling the actual request function , and potentially
call the request function via the " new " interface .
Return False if the " legacy " function needs to be called , everything is
prepared .
Return True if we ' re done .
I know this API sucks , but it is the one with the least code change I could
find .
1998-08-17 17:11:34 +04:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2002-09-25 19:19:00 +04:00
2008-01-04 23:56:23 +03:00
static connection_struct * switch_message ( uint8 type , struct smb_request * req , int size )
1998-08-17 17:11:34 +04:00
{
2007-07-22 02:29:55 +04:00
int flags ;
uint16 session_tag ;
2008-01-04 23:56:23 +03:00
connection_struct * conn = NULL ;
2009-05-26 17:21:16 +04:00
struct smbd_server_connection * sconn = smbd_server_conn ;
2001-01-24 22:34:53 +03:00
2002-09-25 19:19:00 +04:00
errno = 0 ;
2005-04-02 03:11:28 +04:00
2007-07-23 13:36:09 +04:00
/* Make sure this is an SMB packet. smb_size contains NetBIOS header
* so subtract 4 from it . */
2008-01-04 23:56:23 +03:00
if ( ! valid_smb_header ( req - > inbuf )
2007-07-23 13:36:09 +04:00
| | ( size < ( smb_size - 4 ) ) ) {
DEBUG ( 2 , ( " Non-SMB packet of length %d. Terminating server \n " ,
smb_len ( req - > inbuf ) ) ) ;
2006-04-11 02:47:09 +04:00
exit_server_cleanly ( " Non-SMB packet " ) ;
2002-09-25 19:19:00 +04:00
}
1998-08-17 17:11:34 +04:00
2008-10-19 17:17:12 +04:00
if ( smb_messages [ type ] . fn = = NULL ) {
2002-09-25 19:19:00 +04:00
DEBUG ( 0 , ( " Unknown message type %d! \n " , type ) ) ;
2007-07-23 13:36:09 +04:00
smb_dump ( " Unknown " , 1 , ( char * ) req - > inbuf , size ) ;
reply_unknown_new ( req , type ) ;
2008-01-04 23:56:23 +03:00
return NULL ;
2007-07-22 02:29:55 +04:00
}
2002-09-25 19:19:00 +04:00
2007-07-22 02:29:55 +04:00
flags = smb_messages [ type ] . flags ;
2002-09-25 19:19:00 +04:00
2007-07-22 02:29:55 +04:00
/* In share mode security we must ignore the vuid. */
session_tag = ( lp_security ( ) = = SEC_SHARE )
2007-07-23 13:36:09 +04:00
? UID_FIELD_INVALID : req - > vuid ;
2008-01-04 23:56:23 +03:00
conn = req - > conn ;
2001-01-24 22:04:56 +03:00
2007-07-22 15:38:11 +04:00
DEBUG ( 3 , ( " switch message %s (pid %d) conn 0x%lx \n " , smb_fn_name ( type ) ,
( int ) sys_getpid ( ) , ( unsigned long ) conn ) ) ;
2007-07-22 02:29:55 +04:00
2007-07-23 13:36:09 +04:00
smb_dump ( smb_fn_name ( type ) , 1 , ( char * ) req - > inbuf , size ) ;
2001-01-24 22:04:56 +03:00
2007-07-22 02:29:55 +04:00
/* Ensure this value is replaced in the incoming packet. */
2007-07-23 13:36:09 +04:00
SSVAL ( req - > inbuf , smb_uid , session_tag ) ;
2001-01-24 22:04:56 +03:00
2007-07-22 02:29:55 +04:00
/*
* Ensure the correct username is in current_user_info . This is a
* really ugly bugfix for problems with multiple session_setup_and_X ' s
* being done and allowing % U and % G substitutions to work correctly .
* There is a reason this code is done here , don ' t move it unless you
2007-07-22 15:38:11 +04:00
* know what you ' re doing . . . : - ) .
* JRA .
2007-07-22 02:29:55 +04:00
*/
2009-05-26 17:21:16 +04:00
if ( session_tag ! = sconn - > smb1 . sessions . last_session_tag ) {
2007-07-22 02:29:55 +04:00
user_struct * vuser = NULL ;
2009-05-26 17:21:16 +04:00
sconn - > smb1 . sessions . last_session_tag = session_tag ;
2007-07-22 02:29:55 +04:00
if ( session_tag ! = UID_FIELD_INVALID ) {
2009-05-26 18:38:45 +04:00
vuser = get_valid_user_struct ( sconn , session_tag ) ;
2007-07-22 02:29:55 +04:00
if ( vuser ) {
2008-04-30 19:42:39 +04:00
set_current_user_info (
vuser - > server_info - > sanitized_username ,
vuser - > server_info - > unix_name ,
pdb_get_domain ( vuser - > server_info
- > sam_account ) ) ;
2005-06-14 00:42:21 +04:00
}
}
2007-07-22 02:29:55 +04:00
}
2009-07-14 22:25:45 +04:00
2007-07-22 02:29:55 +04:00
/* Does this call need to be run as the connected user? */
if ( flags & AS_USER ) {
2001-06-20 07:05:09 +04:00
2007-07-22 02:29:55 +04:00
/* Does this call need a valid tree connection? */
if ( ! conn ) {
2007-07-22 15:38:11 +04:00
/*
* Amazingly , the error code depends on the command
* ( from Samba4 ) .
*/
2007-07-22 02:29:55 +04:00
if ( type = = SMBntcreateX ) {
2007-07-23 13:36:09 +04:00
reply_nterror ( req , NT_STATUS_INVALID_HANDLE ) ;
2007-07-22 02:29:55 +04:00
} else {
2007-07-23 13:36:09 +04:00
reply_doserror ( req , ERRSRV , ERRinvnid ) ;
2006-06-20 06:38:28 +04:00
}
2008-01-04 23:56:23 +03:00
return NULL ;
2007-07-22 02:29:55 +04:00
}
2009-07-14 22:25:45 +04:00
2007-07-22 02:29:55 +04:00
if ( ! change_to_user ( conn , session_tag ) ) {
2007-07-23 13:36:09 +04:00
reply_nterror ( req , NT_STATUS_DOS ( ERRSRV , ERRbaduid ) ) ;
2008-11-03 17:25:02 +03:00
remove_deferred_open_smb_message ( req - > mid ) ;
2008-01-04 23:56:23 +03:00
return conn ;
2007-07-22 02:29:55 +04:00
}
1998-08-17 17:11:34 +04:00
2007-07-22 02:29:55 +04:00
/* All NEED_WRITE and CAN_IPC flags must also have AS_USER. */
2001-01-24 22:04:56 +03:00
2007-07-22 02:29:55 +04:00
/* Does it need write permission? */
if ( ( flags & NEED_WRITE ) & & ! CAN_WRITE ( conn ) ) {
2007-07-23 13:36:09 +04:00
reply_nterror ( req , NT_STATUS_MEDIA_WRITE_PROTECTED ) ;
2008-01-04 23:56:23 +03:00
return conn ;
2006-06-20 06:38:28 +04:00
}
2001-01-24 22:04:56 +03:00
2007-07-22 02:29:55 +04:00
/* IPC services are limited */
if ( IS_IPC ( conn ) & & ! ( flags & CAN_IPC ) ) {
2007-07-23 13:36:09 +04:00
reply_doserror ( req , ERRSRV , ERRaccess ) ;
2008-01-04 23:56:23 +03:00
return conn ;
2005-04-15 03:32:56 +04:00
}
2007-07-22 02:29:55 +04:00
} else {
/* This call needs to be run as root */
change_to_root_user ( ) ;
}
2001-01-24 22:04:56 +03:00
2007-07-22 02:29:55 +04:00
/* load service specific parameters */
if ( conn ) {
2008-01-04 23:56:23 +03:00
if ( req - > encrypted ) {
conn - > encrypted_tid = true ;
/* encrypted required from now on. */
conn - > encrypt_level = Required ;
} else if ( ENCRYPTION_REQUIRED ( conn ) ) {
2008-11-03 00:33:20 +03:00
if ( req - > cmd ! = SMBtrans2 & & req - > cmd ! = SMBtranss2 ) {
2008-01-04 23:56:23 +03:00
exit_server_cleanly ( " encryption required "
" on connection " ) ;
return conn ;
}
}
2007-07-23 13:36:09 +04:00
if ( ! set_current_service ( conn , SVAL ( req - > inbuf , smb_flg ) ,
2007-07-22 15:38:11 +04:00
( flags & ( AS_USER | DO_CHDIR )
? True : False ) ) ) {
2007-07-23 13:36:09 +04:00
reply_doserror ( req , ERRSRV , ERRaccess ) ;
2008-01-04 23:56:23 +03:00
return conn ;
2007-12-28 10:51:03 +03:00
}
2007-07-22 02:29:55 +04:00
conn - > num_smb_operations + + ;
}
2001-01-24 22:04:56 +03:00
2007-07-22 02:29:55 +04:00
/* does this protocol need to be run as guest? */
if ( ( flags & AS_GUEST )
2007-07-22 15:38:11 +04:00
& & ( ! change_to_guest ( ) | |
2007-07-22 02:29:55 +04:00
! check_access ( smbd_server_fd ( ) , lp_hostsallow ( - 1 ) ,
lp_hostsdeny ( - 1 ) ) ) ) {
2007-07-23 13:36:09 +04:00
reply_doserror ( req , ERRSRV , ERRaccess ) ;
2008-01-04 23:56:23 +03:00
return conn ;
2007-07-23 13:36:09 +04:00
}
2008-10-19 17:17:12 +04:00
smb_messages [ type ] . fn ( req ) ;
2008-01-04 23:56:23 +03:00
return req - > conn ;
1998-09-05 09:07:05 +04:00
}
/****************************************************************************
2002-09-25 19:19:00 +04:00
Construct a reply to the incoming packet .
1998-09-05 09:07:05 +04:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2002-09-25 19:19:00 +04:00
2009-02-09 10:10:34 +03:00
static void construct_reply ( char * inbuf , int size , size_t unread_bytes ,
2009-03-09 11:47:59 +03:00
uint32_t seqnum , bool encrypted ,
2009-02-09 10:10:34 +03:00
struct smb_perfcount_data * deferred_pcd )
1998-09-05 09:07:05 +04:00
{
2008-01-04 23:56:23 +03:00
connection_struct * conn ;
2007-07-23 13:36:09 +04:00
struct smb_request * req ;
1998-09-05 09:07:05 +04:00
2007-08-30 23:48:31 +04:00
if ( ! ( req = talloc ( talloc_tos ( ) , struct smb_request ) ) ) {
2007-07-23 13:36:09 +04:00
smb_panic ( " could not allocate smb_request " ) ;
}
2009-02-09 10:10:34 +03:00
2008-01-04 23:56:23 +03:00
init_smb_request ( req , ( uint8 * ) inbuf , unread_bytes , encrypted ) ;
2008-11-03 23:55:05 +03:00
req - > inbuf = ( uint8_t * ) talloc_move ( req , & inbuf ) ;
2009-03-09 11:47:59 +03:00
req - > seqnum = seqnum ;
1998-09-05 09:07:05 +04:00
2009-02-09 10:10:34 +03:00
/* we popped this message off the queue - keep original perf data */
if ( deferred_pcd )
req - > pcd = * deferred_pcd ;
else {
SMB_PERFCOUNT_START ( & req - > pcd ) ;
SMB_PERFCOUNT_SET_OP ( & req - > pcd , req - > cmd ) ;
SMB_PERFCOUNT_SET_MSGLEN_IN ( & req - > pcd , size ) ;
}
2008-11-03 00:33:20 +03:00
conn = switch_message ( req - > cmd , req , size ) ;
1998-09-05 09:07:05 +04:00
2007-10-31 02:22:24 +03:00
if ( req - > unread_bytes ) {
/* writeX failed. drain socket. */
if ( drain_socket ( smbd_server_fd ( ) , req - > unread_bytes ) ! =
req - > unread_bytes ) {
smb_panic ( " failed to drain pending bytes " ) ;
}
req - > unread_bytes = 0 ;
}
2009-07-27 16:47:41 +04:00
if ( req - > done ) {
TALLOC_FREE ( req ) ;
return ;
}
2007-08-27 16:04:09 +04:00
if ( req - > outbuf = = NULL ) {
return ;
2007-07-23 13:36:09 +04:00
}
1998-09-05 09:07:05 +04:00
2007-08-27 16:04:09 +04:00
if ( CVAL ( req - > outbuf , 0 ) = = 0 ) {
show_msg ( ( char * ) req - > outbuf ) ;
}
1998-09-05 09:07:05 +04:00
2008-01-04 23:56:23 +03:00
if ( ! srv_send_smb ( smbd_server_fd ( ) ,
( char * ) req - > outbuf ,
2009-03-09 11:47:59 +03:00
true , req - > seqnum + 1 ,
2009-02-09 10:10:34 +03:00
IS_CONN_ENCRYPTED ( conn ) | | req - > encrypted ,
& req - > pcd ) ) {
2008-01-04 23:56:23 +03:00
exit_server_cleanly ( " construct_reply: srv_send_smb failed. " ) ;
2007-04-20 00:50:49 +04:00
}
2007-07-23 13:36:09 +04:00
TALLOC_FREE ( req ) ;
return ;
1998-09-05 09:07:05 +04:00
}
/****************************************************************************
2006-03-19 14:11:37 +03:00
Process an smb from the client
1998-09-05 09:07:05 +04:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2009-01-08 17:38:47 +03:00
static void process_smb ( struct smbd_server_connection * conn ,
uint8_t * inbuf , size_t nread , size_t unread_bytes ,
2009-03-09 11:47:59 +03:00
uint32_t seqnum , bool encrypted ,
struct smb_perfcount_data * deferred_pcd )
1998-09-05 09:07:05 +04:00
{
2003-07-16 22:06:27 +04:00
int msg_type = CVAL ( inbuf , 0 ) ;
DO_PROFILE_INC ( smb_count ) ;
2007-07-23 13:36:09 +04:00
DEBUG ( 6 , ( " got message type 0x%x of len 0x%x \n " , msg_type ,
smb_len ( inbuf ) ) ) ;
2007-10-31 02:22:24 +03:00
DEBUG ( 3 , ( " Transaction %d of length %d (%u toread) \n " , trans_num ,
( int ) nread ,
( unsigned int ) unread_bytes ) ) ;
2003-07-16 22:06:27 +04:00
2007-07-23 13:36:09 +04:00
if ( msg_type ! = 0 ) {
/*
* NetBIOS session request , keepalive , etc .
*/
2009-01-08 17:38:47 +03:00
reply_special ( ( char * ) inbuf ) ;
goto done ;
2007-07-23 13:36:09 +04:00
}
2003-07-16 22:06:27 +04:00
2009-05-14 16:17:28 +04:00
if ( smbd_server_conn - > allow_smb2 ) {
if ( smbd_is_smb2_header ( inbuf , nread ) ) {
smbd_smb2_first_negprot ( smbd_server_conn , inbuf , nread ) ;
return ;
}
smbd_server_conn - > allow_smb2 = false ;
}
2009-01-08 17:38:47 +03:00
show_msg ( ( char * ) inbuf ) ;
2007-07-23 13:36:09 +04:00
2009-03-09 11:47:59 +03:00
construct_reply ( ( char * ) inbuf , nread , unread_bytes , seqnum , encrypted , deferred_pcd ) ;
2003-07-16 22:06:27 +04:00
trans_num + + ;
2009-01-08 17:38:47 +03:00
done :
2009-05-26 12:48:12 +04:00
conn - > smb1 . num_requests + + ;
2009-01-08 17:38:47 +03:00
/* The timeout_processing function isn't run nearly
often enough to implement ' max log size ' without
overrunning the size of the file by many megabytes .
This is especially true if we are running at debug
level 10. Checking every 50 SMBs is a nice
tradeoff of performance vs log file size overrun . */
2009-05-26 12:48:12 +04:00
if ( ( conn - > smb1 . num_requests % 50 ) = = 0 & &
2009-01-08 17:38:47 +03:00
need_to_check_log_size ( ) ) {
change_to_root_user ( ) ;
check_log_size ( ) ;
}
1998-09-05 09:07:05 +04:00
}
/****************************************************************************
2003-07-16 22:06:27 +04:00
Return a string containing the function name of a SMB command .
1998-09-05 09:07:05 +04:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2003-07-16 22:06:27 +04:00
2002-08-17 19:27:10 +04:00
const char * smb_fn_name ( int type )
1998-09-05 09:07:05 +04:00
{
2003-01-03 11:28:12 +03:00
const char * unknown_name = " SMBunknown " ;
1998-09-05 09:07:05 +04:00
2001-01-24 22:04:56 +03:00
if ( smb_messages [ type ] . name = = NULL )
1998-09-05 09:07:05 +04:00
return ( unknown_name ) ;
2001-01-24 22:04:56 +03:00
return ( smb_messages [ type ] . name ) ;
1998-08-17 17:11:34 +04:00
}
1998-08-20 23:28:37 +04:00
/****************************************************************************
2003-12-01 05:25:56 +03:00
Helper functions for contruct_reply .
1998-08-20 23:28:37 +04:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2004-08-20 00:03:41 +04:00
void add_to_common_flags2 ( uint32 v )
{
common_flags2 | = v ;
}
2003-12-01 05:25:56 +03:00
2003-12-01 09:19:17 +03:00
void remove_from_common_flags2 ( uint32 v )
2003-12-01 05:25:56 +03:00
{
2003-12-01 09:19:17 +03:00
common_flags2 & = ~ v ;
2003-12-01 05:25:56 +03:00
}
2008-12-19 20:07:44 +03:00
static void construct_reply_common ( struct smb_request * req , const char * inbuf ,
char * outbuf )
1998-08-20 23:28:37 +04:00
{
2008-01-04 23:56:23 +03:00
srv_set_message ( outbuf , 0 , 0 , false ) ;
2007-10-11 00:34:30 +04:00
2008-12-19 20:07:44 +03:00
SCVAL ( outbuf , smb_com , req - > cmd ) ;
2006-04-11 05:43:13 +04:00
SIVAL ( outbuf , smb_rcls , 0 ) ;
2001-10-17 12:54:19 +04:00
SCVAL ( outbuf , smb_flg , FLAG_REPLY | ( CVAL ( inbuf , smb_flg ) & FLAG_CASELESS_PATHNAMES ) ) ;
SSVAL ( outbuf , smb_flg2 ,
2003-12-01 05:25:56 +03:00
( SVAL ( inbuf , smb_flg2 ) & FLAGS2_UNICODE_STRINGS ) |
common_flags2 ) ;
2006-04-11 05:43:13 +04:00
memset ( outbuf + smb_pidhigh , ' \0 ' , ( smb_tid - smb_pidhigh ) ) ;
2001-10-17 12:54:19 +04:00
SSVAL ( outbuf , smb_tid , SVAL ( inbuf , smb_tid ) ) ;
SSVAL ( outbuf , smb_pid , SVAL ( inbuf , smb_pid ) ) ;
SSVAL ( outbuf , smb_uid , SVAL ( inbuf , smb_uid ) ) ;
SSVAL ( outbuf , smb_mid , SVAL ( inbuf , smb_mid ) ) ;
1998-08-20 23:28:37 +04:00
}
1998-08-17 17:11:34 +04:00
2008-11-03 22:16:09 +03:00
void construct_reply_common_req ( struct smb_request * req , char * outbuf )
{
2008-12-19 20:07:44 +03:00
construct_reply_common ( req , ( char * ) req - > inbuf , outbuf ) ;
2008-11-03 22:16:09 +03:00
}
2008-11-22 01:17:31 +03:00
/*
* How many bytes have we already accumulated up to the current wct field
* offset ?
*/
size_t req_wct_ofs ( struct smb_request * req )
{
size_t buf_size ;
if ( req - > chain_outbuf = = NULL ) {
return smb_wct - 4 ;
}
buf_size = talloc_get_size ( req - > chain_outbuf ) ;
if ( ( buf_size % 4 ) ! = 0 ) {
buf_size + = ( 4 - ( buf_size % 4 ) ) ;
}
return buf_size - 4 ;
}
2008-11-10 12:01:26 +03:00
/*
* Hack around reply_nterror & friends not being aware of chained requests ,
* generating illegal ( i . e . wct = = 0 ) chain replies .
*/
static void fixup_chain_error_packet ( struct smb_request * req )
{
uint8_t * outbuf = req - > outbuf ;
req - > outbuf = NULL ;
reply_outbuf ( req , 2 , 0 ) ;
memcpy ( req - > outbuf , outbuf , smb_wct ) ;
TALLOC_FREE ( outbuf ) ;
SCVAL ( req - > outbuf , smb_vwv0 , 0xff ) ;
}
2009-01-16 13:26:05 +03:00
/****************************************************************************
Construct a chained reply and add it to the already made reply
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2008-11-10 12:01:26 +03:00
void chain_reply ( struct smb_request * req )
{
size_t smblen = smb_len ( req - > inbuf ) ;
size_t already_used , length_needed ;
uint8_t chain_cmd ;
uint32_t chain_offset ; /* uint32_t to avoid overflow */
uint8_t wct ;
uint16_t * vwv ;
uint16_t buflen ;
uint8_t * buf ;
if ( IVAL ( req - > outbuf , smb_rcls ) ! = 0 ) {
fixup_chain_error_packet ( req ) ;
}
/*
* Any of the AndX requests and replies have at least a wct of
* 2. vwv [ 0 ] is the next command , vwv [ 1 ] is the offset from the
* beginning of the SMB header to the next wct field .
*
* None of the AndX requests put anything valuable in vwv [ 0 ] and [ 1 ] ,
* so we can overwrite it here to form the chain .
*/
if ( ( req - > wct < 2 ) | | ( CVAL ( req - > outbuf , smb_wct ) < 2 ) ) {
goto error ;
}
/*
* Here we assume that this is the end of the chain . For that we need
* to set " next command " to 0xff and the offset to 0. If we later find
* more commands in the chain , this will be overwritten again .
*/
SCVAL ( req - > outbuf , smb_vwv0 , 0xff ) ;
SCVAL ( req - > outbuf , smb_vwv0 + 1 , 0 ) ;
SSVAL ( req - > outbuf , smb_vwv1 , 0 ) ;
if ( req - > chain_outbuf = = NULL ) {
/*
* In req - > chain_outbuf we collect all the replies . Start the
* chain by copying in the first reply .
2009-01-25 14:22:20 +03:00
*
* We do the realloc because later on we depend on
* talloc_get_size to determine the length of
* chain_outbuf . The reply_xxx routines might have
* over - allocated ( reply_pipe_read_and_X used to be such an
* example ) .
2008-11-10 12:01:26 +03:00
*/
2009-01-25 14:22:20 +03:00
req - > chain_outbuf = TALLOC_REALLOC_ARRAY (
req , req - > outbuf , uint8_t , smb_len ( req - > outbuf ) + 4 ) ;
if ( req - > chain_outbuf = = NULL ) {
goto error ;
}
2008-11-10 12:01:26 +03:00
req - > outbuf = NULL ;
} else {
2009-03-25 03:24:55 +03:00
/*
* Update smb headers where subsequent chained commands
* may have updated them .
*/
SCVAL ( req - > chain_outbuf , smb_tid , CVAL ( req - > outbuf , smb_tid ) ) ;
SCVAL ( req - > chain_outbuf , smb_uid , CVAL ( req - > outbuf , smb_uid ) ) ;
2008-11-10 12:01:26 +03:00
if ( ! smb_splice_chain ( & req - > chain_outbuf ,
CVAL ( req - > outbuf , smb_com ) ,
CVAL ( req - > outbuf , smb_wct ) ,
( uint16_t * ) ( req - > outbuf + smb_vwv ) ,
0 , smb_buflen ( req - > outbuf ) ,
( uint8_t * ) smb_buf ( req - > outbuf ) ) ) {
goto error ;
}
TALLOC_FREE ( req - > outbuf ) ;
}
/*
* We use the old request ' s vwv field to grab the next chained command
* and offset into the chained fields .
*/
chain_cmd = CVAL ( req - > vwv + 0 , 0 ) ;
chain_offset = SVAL ( req - > vwv + 1 , 0 ) ;
if ( chain_cmd = = 0xff ) {
/*
* End of chain , no more requests from the client . So ship the
* replies .
*/
smb_setlen ( ( char * ) ( req - > chain_outbuf ) ,
talloc_get_size ( req - > chain_outbuf ) - 4 ) ;
2009-02-09 10:10:34 +03:00
2008-11-10 12:01:26 +03:00
if ( ! srv_send_smb ( smbd_server_fd ( ) , ( char * ) req - > chain_outbuf ,
2009-03-09 11:47:59 +03:00
true , req - > seqnum + 1 ,
2008-11-10 12:01:26 +03:00
IS_CONN_ENCRYPTED ( req - > conn )
2009-02-09 10:10:34 +03:00
| | req - > encrypted ,
& req - > pcd ) ) {
2008-11-10 12:01:26 +03:00
exit_server_cleanly ( " chain_reply: srv_send_smb "
" failed. " ) ;
}
2009-07-27 16:47:41 +04:00
TALLOC_FREE ( req - > chain_outbuf ) ;
req - > done = true ;
2008-11-10 12:01:26 +03:00
return ;
}
2009-02-09 10:10:34 +03:00
/* add a new perfcounter for this element of chain */
SMB_PERFCOUNT_ADD ( & req - > pcd ) ;
SMB_PERFCOUNT_SET_OP ( & req - > pcd , chain_cmd ) ;
SMB_PERFCOUNT_SET_MSGLEN_IN ( & req - > pcd , smblen ) ;
2008-11-10 12:01:26 +03:00
/*
* Check if the client tries to fool us . The request so far uses the
* space to the end of the byte buffer in the request just
* processed . The chain_offset can ' t point into that area . If that was
* the case , we could end up with an endless processing of the chain ,
* we would always handle the same request .
*/
already_used = PTR_DIFF ( req - > buf + req - > buflen , smb_base ( req - > inbuf ) ) ;
if ( chain_offset < already_used ) {
goto error ;
}
/*
* Next check : Make sure the chain offset does not point beyond the
* overall smb request length .
*/
length_needed = chain_offset + 1 ; /* wct */
if ( length_needed > smblen ) {
goto error ;
}
/*
* Now comes the pointer magic . Goal here is to set up req - > vwv and
* req - > buf correctly again to be able to call the subsequent
* switch_message ( ) . The chain offset ( the former vwv [ 1 ] ) points at
* the new wct field .
*/
wct = CVAL ( smb_base ( req - > inbuf ) , chain_offset ) ;
/*
* Next consistency check : Make the new vwv array fits in the overall
* smb request .
*/
length_needed + = ( wct + 1 ) * sizeof ( uint16_t ) ; /* vwv+buflen */
if ( length_needed > smblen ) {
goto error ;
}
vwv = ( uint16_t * ) ( smb_base ( req - > inbuf ) + chain_offset + 1 ) ;
/*
* Now grab the new byte buffer . . . .
*/
buflen = SVAL ( vwv + wct , 0 ) ;
/*
* . . and check that it fits .
*/
length_needed + = buflen ;
if ( length_needed > smblen ) {
goto error ;
}
buf = ( uint8_t * ) ( vwv + wct + 1 ) ;
req - > cmd = chain_cmd ;
req - > wct = wct ;
req - > vwv = vwv ;
req - > buflen = buflen ;
req - > buf = buf ;
switch_message ( chain_cmd , req , smblen ) ;
if ( req - > outbuf = = NULL ) {
/*
* This happens if the chained command has suspended itself or
* if it has called srv_send_smb ( ) itself .
*/
return ;
}
/*
* We end up here if the chained command was not itself chained or
* suspended , but for example a close ( ) command . We now need to splice
* the chained commands ' outbuf into the already built up chain_outbuf
* and ship the result .
*/
goto done ;
error :
/*
* We end up here if there ' s any error in the chain syntax . Report a
* DOS error , just like Windows does .
*/
reply_nterror ( req , NT_STATUS_DOS ( ERRSRV , ERRerror ) ) ;
fixup_chain_error_packet ( req ) ;
done :
2009-07-19 22:52:07 +04:00
/*
* This scary statement intends to set the
* FLAGS2_32_BIT_ERROR_CODES flg2 field in req - > chain_outbuf
* to the value req - > outbuf carries
*/
SSVAL ( req - > chain_outbuf , smb_flg2 ,
( SVAL ( req - > chain_outbuf , smb_flg2 ) & ~ FLAGS2_32_BIT_ERROR_CODES )
| ( SVAL ( req - > outbuf , smb_flg2 ) & FLAGS2_32_BIT_ERROR_CODES ) ) ;
/*
* Transfer the error codes from the subrequest to the main one
*/
SSVAL ( req - > chain_outbuf , smb_rcls , SVAL ( req - > outbuf , smb_rcls ) ) ;
SSVAL ( req - > chain_outbuf , smb_err , SVAL ( req - > outbuf , smb_err ) ) ;
2008-11-10 12:01:26 +03:00
if ( ! smb_splice_chain ( & req - > chain_outbuf ,
CVAL ( req - > outbuf , smb_com ) ,
CVAL ( req - > outbuf , smb_wct ) ,
( uint16_t * ) ( req - > outbuf + smb_vwv ) ,
0 , smb_buflen ( req - > outbuf ) ,
( uint8_t * ) smb_buf ( req - > outbuf ) ) ) {
exit_server_cleanly ( " chain_reply: smb_splice_chain failed \n " ) ;
}
TALLOC_FREE ( req - > outbuf ) ;
smb_setlen ( ( char * ) ( req - > chain_outbuf ) ,
talloc_get_size ( req - > chain_outbuf ) - 4 ) ;
show_msg ( ( char * ) ( req - > chain_outbuf ) ) ;
if ( ! srv_send_smb ( smbd_server_fd ( ) , ( char * ) req - > chain_outbuf ,
2009-03-09 11:47:59 +03:00
true , req - > seqnum + 1 ,
2009-02-09 10:10:34 +03:00
IS_CONN_ENCRYPTED ( req - > conn ) | | req - > encrypted ,
& req - > pcd ) ) {
2008-11-10 12:01:26 +03:00
exit_server_cleanly ( " construct_reply: srv_send_smb failed. " ) ;
}
2009-07-27 16:47:41 +04:00
TALLOC_FREE ( req - > chain_outbuf ) ;
req - > done = true ;
2008-11-10 12:01:26 +03:00
}
1999-12-13 16:27:58 +03:00
/****************************************************************************
Check if services need reloading .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2005-09-30 21:13:37 +04:00
void check_reload ( time_t t )
1999-12-13 16:27:58 +03:00
{
2004-05-20 20:23:17 +04:00
time_t printcap_cache_time = ( time_t ) lp_printcap_cache_time ( ) ;
1999-12-13 16:27:58 +03:00
2004-05-20 20:23:17 +04:00
if ( last_smb_conf_reload_time = = 0 ) {
2003-07-16 22:06:27 +04:00
last_smb_conf_reload_time = t ;
2004-05-20 20:23:17 +04:00
/* Our printing subsystem might not be ready at smbd start up.
Then no printer is available till the first printers check
is performed . A lower initial interval circumvents this . */
if ( printcap_cache_time > 60 )
2005-01-05 19:20:35 +03:00
last_printer_reload_time = t - printcap_cache_time + 60 ;
2004-05-20 20:23:17 +04:00
else
2005-01-05 19:20:35 +03:00
last_printer_reload_time = t ;
2004-05-20 20:23:17 +04:00
}
1998-08-17 17:11:34 +04:00
2005-06-28 02:44:57 +04:00
if ( mypid ! = getpid ( ) ) { /* First time or fork happened meanwhile */
/* randomize over 60 second the printcap reload to avoid all
* process hitting cupsd at the same time */
int time_range = 60 ;
last_printer_reload_time + = random ( ) % time_range ;
mypid = getpid ( ) ;
}
2009-01-22 01:24:18 +03:00
if ( t > = last_smb_conf_reload_time + SMBD_RELOAD_CHECK ) {
2003-07-16 22:06:27 +04:00
reload_services ( True ) ;
last_smb_conf_reload_time = t ;
}
2004-05-20 20:23:17 +04:00
/* 'printcap cache time = 0' disable the feature */
if ( printcap_cache_time ! = 0 )
{
/* see if it's time to reload or if the clock has been set back */
2005-01-05 19:20:35 +03:00
if ( ( t > = last_printer_reload_time + printcap_cache_time )
| | ( t - last_printer_reload_time < 0 ) )
2004-05-20 20:23:17 +04:00
{
DEBUG ( 3 , ( " Printcap cache time expired. \n " ) ) ;
2005-01-05 19:20:35 +03:00
reload_printers ( ) ;
last_printer_reload_time = t ;
2004-05-20 20:23:17 +04:00
}
}
1999-12-13 16:27:58 +03:00
}
1998-08-17 17:11:34 +04:00
2009-01-08 17:38:47 +03:00
static void smbd_server_connection_write_handler ( struct smbd_server_connection * conn )
{
/* TODO: make write nonblocking */
}
static void smbd_server_connection_read_handler ( struct smbd_server_connection * conn )
{
uint8_t * inbuf = NULL ;
size_t inbuf_len = 0 ;
size_t unread_bytes = 0 ;
bool encrypted = false ;
TALLOC_CTX * mem_ctx = talloc_tos ( ) ;
NTSTATUS status ;
2009-03-09 11:47:59 +03:00
uint32_t seqnum ;
2009-01-08 17:38:47 +03:00
/* TODO: make this completely nonblocking */
status = receive_smb_talloc ( mem_ctx , smbd_server_fd ( ) ,
2009-01-10 15:04:33 +03:00
( char * * ) ( void * ) & inbuf ,
2009-01-08 17:38:47 +03:00
0 , /* timeout */
& unread_bytes ,
& encrypted ,
2009-03-09 11:47:59 +03:00
& inbuf_len , & seqnum ) ;
2009-01-08 17:38:47 +03:00
if ( NT_STATUS_EQUAL ( status , NT_STATUS_RETRY ) ) {
goto process ;
}
if ( NT_STATUS_IS_ERR ( status ) ) {
exit_server_cleanly ( " failed to receive smb request " ) ;
}
if ( ! NT_STATUS_IS_OK ( status ) ) {
return ;
}
process :
2009-03-09 11:47:59 +03:00
process_smb ( conn , inbuf , inbuf_len , unread_bytes ,
seqnum , encrypted , NULL ) ;
2009-01-08 17:38:47 +03:00
}
static void smbd_server_connection_handler ( struct event_context * ev ,
struct fd_event * fde ,
uint16_t flags ,
void * private_data )
{
struct smbd_server_connection * conn = talloc_get_type ( private_data ,
struct smbd_server_connection ) ;
if ( flags & EVENT_FD_WRITE ) {
smbd_server_connection_write_handler ( conn ) ;
} else if ( flags & EVENT_FD_READ ) {
smbd_server_connection_read_handler ( conn ) ;
}
}
2009-01-22 14:36:42 +03:00
/****************************************************************************
received when we should release a specific IP
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void release_ip ( const char * ip , void * priv )
{
char addr [ INET6_ADDRSTRLEN ] ;
2009-05-08 17:13:21 +04:00
char * p = addr ;
2009-01-22 14:36:42 +03:00
2009-05-08 03:11:43 +04:00
client_socket_addr ( get_client_fd ( ) , addr , sizeof ( addr ) ) ;
if ( strncmp ( " ::ffff: " , addr , 7 ) = = 0 ) {
p = addr + 7 ;
}
2009-05-08 17:13:21 +04:00
if ( ( strcmp ( p , ip ) = = 0 ) | | ( ( p ! = addr ) & & strcmp ( addr , ip ) = = 0 ) ) {
2009-01-22 14:36:42 +03:00
/* we can't afford to do a clean exit - that involves
database writes , which would potentially mean we
are still running after the failover has finished -
we have to get rid of this process ID straight
away */
DEBUG ( 0 , ( " Got release IP message for our IP %s - exiting immediately \n " ,
ip ) ) ;
/* note we must exit with non-zero status so the unclean handler gets
called in the parent , so that the brl database is tickled */
_exit ( 1 ) ;
}
}
static void msg_release_ip ( struct messaging_context * msg_ctx , void * private_data ,
uint32_t msg_type , struct server_id server_id , DATA_BLOB * data )
{
release_ip ( ( char * ) data - > data , NULL ) ;
}
# ifdef CLUSTER_SUPPORT
static int client_get_tcp_info ( struct sockaddr_storage * server ,
struct sockaddr_storage * client )
{
socklen_t length ;
if ( server_fd = = - 1 ) {
return - 1 ;
}
length = sizeof ( * server ) ;
if ( getsockname ( server_fd , ( struct sockaddr * ) server , & length ) ! = 0 ) {
return - 1 ;
}
length = sizeof ( * client ) ;
if ( getpeername ( server_fd , ( struct sockaddr * ) client , & length ) ! = 0 ) {
return - 1 ;
}
return 0 ;
}
# endif
/*
* Send keepalive packets to our client
*/
static bool keepalive_fn ( const struct timeval * now , void * private_data )
{
if ( ! send_keepalive ( smbd_server_fd ( ) ) ) {
DEBUG ( 2 , ( " Keepalive failed - exiting. \n " ) ) ;
return False ;
}
return True ;
}
/*
* Do the recurring check if we ' re idle
*/
static bool deadtime_fn ( const struct timeval * now , void * private_data )
{
2009-05-27 13:15:44 +04:00
struct smbd_server_connection * sconn = smbd_server_conn ;
if ( ( conn_num_open ( sconn ) = = 0 )
| | ( conn_idle_all ( sconn , now - > tv_sec ) ) ) {
2009-01-22 14:36:42 +03:00
DEBUG ( 2 , ( " Closing idle connection \n " ) ) ;
messaging_send ( smbd_messaging_context ( ) , procid_self ( ) ,
MSG_SHUTDOWN , & data_blob_null ) ;
return False ;
}
return True ;
}
/*
* Do the recurring log file and smb . conf reload checks .
*/
static bool housekeeping_fn ( const struct timeval * now , void * private_data )
{
change_to_root_user ( ) ;
/* update printer queue caches if necessary */
update_monitored_printq_cache ( ) ;
/* check if we need to reload services */
check_reload ( time ( NULL ) ) ;
/* Change machine password if neccessary. */
attempt_machine_password_change ( ) ;
/*
* Force a log file check .
*/
force_check_log_size ( ) ;
check_log_size ( ) ;
return true ;
}
2005-06-28 02:53:56 +04:00
/****************************************************************************
Process commands from the client
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void smbd_process ( void )
{
2009-01-22 14:36:42 +03:00
TALLOC_CTX * frame = talloc_stackframe ( ) ;
char remaddr [ INET6_ADDRSTRLEN ] ;
2009-05-27 19:48:23 +04:00
if ( lp_maxprotocol ( ) = = PROTOCOL_SMB2 & &
lp_security ( ) ! = SEC_SHARE ) {
2009-05-14 16:17:28 +04:00
smbd_server_conn - > allow_smb2 = true ;
}
2009-01-22 14:36:42 +03:00
/* Ensure child is set to blocking mode */
set_blocking ( smbd_server_fd ( ) , True ) ;
set_socket_options ( smbd_server_fd ( ) , " SO_KEEPALIVE " ) ;
set_socket_options ( smbd_server_fd ( ) , lp_socket_options ( ) ) ;
/* this is needed so that we get decent entries
in smbstatus for port 445 connects */
set_remote_machine_name ( get_peer_addr ( smbd_server_fd ( ) ,
remaddr ,
sizeof ( remaddr ) ) ,
false ) ;
reload_services ( true ) ;
2008-10-19 15:44:11 +04:00
/*
* Before the first packet , check the global hosts allow / hosts deny
* parameters before doing any parsing of packets passed to us by the
* client . This prevents attacks on our parsing code from hosts not in
* the hosts allow list .
*/
if ( ! check_access ( smbd_server_fd ( ) , lp_hostsallow ( - 1 ) ,
lp_hostsdeny ( - 1 ) ) ) {
2009-01-08 17:38:47 +03:00
char addr [ INET6_ADDRSTRLEN ] ;
2008-10-19 15:44:11 +04:00
/*
* send a negative session response " not listening on calling
* name "
*/
unsigned char buf [ 5 ] = { 0x83 , 0 , 0 , 1 , 0x81 } ;
DEBUG ( 1 , ( " Connection denied from %s \n " ,
client_addr ( get_client_fd ( ) , addr , sizeof ( addr ) ) ) ) ;
2009-03-09 11:47:59 +03:00
( void ) srv_send_smb ( smbd_server_fd ( ) , ( char * ) buf , false ,
0 , false , NULL ) ;
2008-10-19 15:44:11 +04:00
exit_server_cleanly ( " connection denied " ) ;
}
2009-01-22 14:36:42 +03:00
static_init_rpc ;
1998-08-17 17:11:34 +04:00
2009-01-22 14:36:42 +03:00
init_modules ( ) ;
2009-02-09 10:10:34 +03:00
smb_perfcount_init ( ) ;
2009-01-22 14:36:42 +03:00
if ( ! init_account_policy ( ) ) {
exit_server ( " Could not open account policy tdb. \n " ) ;
}
if ( * lp_rootdir ( ) ) {
if ( chroot ( lp_rootdir ( ) ) ! = 0 ) {
2009-02-14 02:02:32 +03:00
DEBUG ( 0 , ( " Failed to change root to %s \n " , lp_rootdir ( ) ) ) ;
exit_server ( " Failed to chroot() " ) ;
}
if ( chdir ( " / " ) = = - 1 ) {
DEBUG ( 0 , ( " Failed to chdir to / on chroot to %s \n " , lp_rootdir ( ) ) ) ;
2009-01-22 14:36:42 +03:00
exit_server ( " Failed to chroot() " ) ;
}
DEBUG ( 0 , ( " Changed root to %s \n " , lp_rootdir ( ) ) ) ;
}
2009-03-09 11:47:59 +03:00
if ( ! srv_init_signing ( smbd_server_conn ) ) {
exit_server ( " Failed to init smb_signing " ) ;
}
2009-01-22 14:36:42 +03:00
/* Setup oplocks */
if ( ! init_oplocks ( smbd_messaging_context ( ) ) )
exit_server ( " Failed to init oplocks " ) ;
/* Setup aio signal handler. */
initialize_async_io_handler ( ) ;
/* register our message handlers */
messaging_register ( smbd_messaging_context ( ) , NULL ,
MSG_SMB_FORCE_TDIS , msg_force_tdis ) ;
messaging_register ( smbd_messaging_context ( ) , NULL ,
MSG_SMB_RELEASE_IP , msg_release_ip ) ;
messaging_register ( smbd_messaging_context ( ) , NULL ,
MSG_SMB_CLOSE_FILE , msg_close_file ) ;
2009-06-17 02:11:32 +04:00
/*
* Use the default MSG_DEBUG handler to avoid rebroadcasting
* MSGs to all child processes
*/
messaging_deregister ( smbd_messaging_context ( ) ,
MSG_DEBUG , NULL ) ;
messaging_register ( smbd_messaging_context ( ) , NULL ,
MSG_DEBUG , debug_message ) ;
2009-01-22 14:36:42 +03:00
if ( ( lp_keepalive ( ) ! = 0 )
& & ! ( event_add_idle ( smbd_event_context ( ) , NULL ,
timeval_set ( lp_keepalive ( ) , 0 ) ,
" keepalive " , keepalive_fn ,
NULL ) ) ) {
DEBUG ( 0 , ( " Could not add keepalive event \n " ) ) ;
exit ( 1 ) ;
}
if ( ! ( event_add_idle ( smbd_event_context ( ) , NULL ,
timeval_set ( IDLE_CLOSED_TIMEOUT , 0 ) ,
" deadtime " , deadtime_fn , NULL ) ) ) {
DEBUG ( 0 , ( " Could not add deadtime event \n " ) ) ;
exit ( 1 ) ;
2009-01-08 17:38:47 +03:00
}
2009-01-22 14:36:42 +03:00
if ( ! ( event_add_idle ( smbd_event_context ( ) , NULL ,
timeval_set ( SMBD_SELECT_TIMEOUT , 0 ) ,
" housekeeping " , housekeeping_fn , NULL ) ) ) {
DEBUG ( 0 , ( " Could not add housekeeping event \n " ) ) ;
exit ( 1 ) ;
}
# ifdef CLUSTER_SUPPORT
if ( lp_clustering ( ) ) {
/*
* We need to tell ctdb about our client ' s TCP
* connection , so that for failover ctdbd can send
* tickle acks , triggering a reconnection by the
* client .
*/
struct sockaddr_storage srv , clnt ;
if ( client_get_tcp_info ( & srv , & clnt ) = = 0 ) {
NTSTATUS status ;
status = ctdbd_register_ips (
messaging_ctdbd_connection ( ) ,
& srv , & clnt , release_ip , NULL ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 0 , ( " ctdbd_register_ips failed: %s \n " ,
nt_errstr ( status ) ) ) ;
}
} else
{
DEBUG ( 0 , ( " Unable to get tcp info for "
" CTDB_CONTROL_TCP_CLIENT: %s \n " ,
strerror ( errno ) ) ) ;
}
}
# endif
2009-05-26 17:20:36 +04:00
smbd_server_conn - > nbt . got_session = false ;
2009-05-26 16:56:08 +04:00
smbd_server_conn - > smb1 . negprot . max_recv = MIN ( lp_maxxmit ( ) , BUFFER_SIZE ) ;
2009-01-22 14:36:42 +03:00
2009-05-26 17:21:16 +04:00
smbd_server_conn - > smb1 . sessions . done_sesssetup = false ;
smbd_server_conn - > smb1 . sessions . max_send = BUFFER_SIZE ;
smbd_server_conn - > smb1 . sessions . last_session_tag = UID_FIELD_INVALID ;
2009-05-26 18:38:45 +04:00
/* users from session setup */
smbd_server_conn - > smb1 . sessions . session_userlist = NULL ;
/* workgroup from session setup. */
smbd_server_conn - > smb1 . sessions . session_workgroup = NULL ;
/* this holds info on user ids that are already validated for this VC */
smbd_server_conn - > smb1 . sessions . validated_users = NULL ;
smbd_server_conn - > smb1 . sessions . next_vuid = VUID_OFFSET ;
smbd_server_conn - > smb1 . sessions . num_validated_vuids = 0 ;
# ifdef HAVE_NETGROUP
smbd_server_conn - > smb1 . sessions . my_yp_domain = NULL ;
# endif
2009-05-26 17:21:16 +04:00
2009-05-27 13:15:44 +04:00
conn_init ( smbd_server_conn ) ;
2009-08-06 14:15:51 +04:00
if ( ! init_dptrs ( smbd_server_conn ) ) {
exit_server ( " init_dptrs() failed " ) ;
}
2009-05-27 13:15:44 +04:00
2009-05-26 12:48:12 +04:00
smbd_server_conn - > smb1 . fde = event_add_fd ( smbd_event_context ( ) ,
smbd_server_conn ,
smbd_server_fd ( ) ,
EVENT_FD_READ ,
smbd_server_connection_handler ,
smbd_server_conn ) ;
if ( ! smbd_server_conn - > smb1 . fde ) {
2009-01-08 17:38:47 +03:00
exit_server ( " failed to create smbd_server_connection fde " ) ;
}
2009-01-22 14:36:42 +03:00
TALLOC_FREE ( frame ) ;
totally rewrote the async signal, notification and oplock notification
handling in Samba. This was needed due to several limitations and
races in the previous code - as a side effect the new code is much
cleaner :)
in summary:
- changed sys_select() to avoid a signal/select race condition. It is a
rare race but once we have signals doing notification and oplocks it
is important.
- changed our main processing loop to take advantage of the new
sys_select semantics
- split the notify code into implementaion dependent and general
parts. Added the following structure that defines an implementation:
struct cnotify_fns {
void * (*register_notify)(connection_struct *conn, char *path, uint32 flags);
BOOL (*check_notify)(connection_struct *conn, uint16 vuid, char *path, uint32 flags, void *data, time_t t);
void (*remove_notify)(void *data);
};
then I wrote two implementations, one using hash/poll (like our old
code) and the other using the new Linux kernel change notify. It
should be easy to add other change notify implementations by creating
a sructure of the above type.
- fixed a bug in change notify where we were returning the wrong error
code.
- rewrote the core change notify code to be much simpler
- moved to real-time signals for leases and change notify
Amazingly, it all seems to work. I was very surprised!
(This used to be commit 44766c39e0027c762bee8b33b12c621c109a3267)
2000-06-12 19:53:31 +04:00
while ( True ) {
2008-10-04 01:18:35 +04:00
NTSTATUS status ;
2009-01-22 14:36:42 +03:00
frame = talloc_stackframe_pool ( 8192 ) ;
1998-08-20 23:28:37 +04:00
2007-10-31 02:22:24 +03:00
errno = 0 ;
2009-01-08 17:38:47 +03:00
status = smbd_server_connection_loop_once ( smbd_server_conn ) ;
if ( ! NT_STATUS_EQUAL ( status , NT_STATUS_RETRY ) & &
! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 3 , ( " smbd_server_connection_loop_once failed: %s, "
" exiting \n " , nt_errstr ( status ) ) ) ;
2009-01-22 14:36:42 +03:00
break ;
totally rewrote the async signal, notification and oplock notification
handling in Samba. This was needed due to several limitations and
races in the previous code - as a side effect the new code is much
cleaner :)
in summary:
- changed sys_select() to avoid a signal/select race condition. It is a
rare race but once we have signals doing notification and oplocks it
is important.
- changed our main processing loop to take advantage of the new
sys_select semantics
- split the notify code into implementaion dependent and general
parts. Added the following structure that defines an implementation:
struct cnotify_fns {
void * (*register_notify)(connection_struct *conn, char *path, uint32 flags);
BOOL (*check_notify)(connection_struct *conn, uint16 vuid, char *path, uint32 flags, void *data, time_t t);
void (*remove_notify)(void *data);
};
then I wrote two implementations, one using hash/poll (like our old
code) and the other using the new Linux kernel change notify. It
should be easy to add other change notify implementations by creating
a sructure of the above type.
- fixed a bug in change notify where we were returning the wrong error
code.
- rewrote the core change notify code to be much simpler
- moved to real-time signals for leases and change notify
Amazingly, it all seems to work. I was very surprised!
(This used to be commit 44766c39e0027c762bee8b33b12c621c109a3267)
2000-06-12 19:53:31 +04:00
}
2007-08-30 23:48:31 +04:00
TALLOC_FREE ( frame ) ;
totally rewrote the async signal, notification and oplock notification
handling in Samba. This was needed due to several limitations and
races in the previous code - as a side effect the new code is much
cleaner :)
in summary:
- changed sys_select() to avoid a signal/select race condition. It is a
rare race but once we have signals doing notification and oplocks it
is important.
- changed our main processing loop to take advantage of the new
sys_select semantics
- split the notify code into implementaion dependent and general
parts. Added the following structure that defines an implementation:
struct cnotify_fns {
void * (*register_notify)(connection_struct *conn, char *path, uint32 flags);
BOOL (*check_notify)(connection_struct *conn, uint16 vuid, char *path, uint32 flags, void *data, time_t t);
void (*remove_notify)(void *data);
};
then I wrote two implementations, one using hash/poll (like our old
code) and the other using the new Linux kernel change notify. It
should be easy to add other change notify implementations by creating
a sructure of the above type.
- fixed a bug in change notify where we were returning the wrong error
code.
- rewrote the core change notify code to be much simpler
- moved to real-time signals for leases and change notify
Amazingly, it all seems to work. I was very surprised!
(This used to be commit 44766c39e0027c762bee8b33b12c621c109a3267)
2000-06-12 19:53:31 +04:00
}
2009-01-22 14:36:42 +03:00
exit_server_cleanly ( NULL ) ;
1998-08-17 17:11:34 +04:00
}
2009-01-31 01:44:21 +03:00
bool req_is_in_chain ( struct smb_request * req )
{
if ( req - > vwv ! = ( uint16_t * ) ( req - > inbuf + smb_vwv ) ) {
/*
* We ' re right now handling a subsequent request , so we must
* be in a chain
*/
return true ;
}
if ( ! is_andx_req ( req - > cmd ) ) {
return false ;
}
if ( req - > wct < 2 ) {
/*
* Okay , an illegal request , but definitely not chained : - )
*/
return false ;
}
return ( CVAL ( req - > vwv + 0 , 0 ) ! = 0xFF ) ;
}