2005-08-03 00:12:31 +04:00
/*
Unix SMB / CIFS implementation .
TDR ( Trivial Data Representation ) helper functions
Based loosely on ndr . c by Andrew Tridgell .
Copyright ( C ) Jelmer Vernooij 2005
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
2007-07-10 06:07:03 +04:00
the Free Software Foundation ; either version 3 of the License , or
2005-08-03 00:12:31 +04:00
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
2007-07-10 06:07:03 +04:00
along with this program . If not , see < http : //www.gnu.org/licenses/>.
2005-08-03 00:12:31 +04:00
*/
# include "includes.h"
2005-09-04 21:25:04 +04:00
# include "system/filesys.h"
2006-09-27 15:15:03 +04:00
# include "system/network.h"
2005-12-28 01:51:30 +03:00
# include "tdr/tdr.h"
2007-12-14 00:46:09 +03:00
# include "param/param.h"
2005-08-03 00:12:31 +04:00
# define TDR_BASE_MARSHALL_SIZE 1024
2005-09-04 21:25:04 +04:00
# define TDR_PUSH_NEED_BYTES(tdr, n) TDR_CHECK(tdr_push_expand(tdr, tdr->data.length+(n)))
2005-08-03 00:12:31 +04:00
# define TDR_PULL_NEED_BYTES(tdr, n) do { \
2005-09-02 18:05:28 +04:00
if ( ( n ) > tdr - > data . length | | tdr - > offset + ( n ) > tdr - > data . length ) { \
2005-08-03 00:12:31 +04:00
return NT_STATUS_BUFFER_TOO_SMALL ; \
} \
} while ( 0 )
2005-08-21 17:19:36 +04:00
# define TDR_BE(tdr) ((tdr)->flags & TDR_BIG_ENDIAN)
2005-08-03 00:12:31 +04:00
2005-09-03 23:43:35 +04:00
# define TDR_CVAL(tdr, ofs) CVAL(tdr->data.data,ofs)
2005-09-02 18:05:28 +04:00
# define TDR_SVAL(tdr, ofs) (TDR_BE(tdr)?RSVAL(tdr->data.data,ofs):SVAL(tdr->data.data,ofs))
# define TDR_IVAL(tdr, ofs) (TDR_BE(tdr)?RIVAL(tdr->data.data,ofs):IVAL(tdr->data.data,ofs))
2005-09-03 23:43:35 +04:00
# define TDR_SCVAL(tdr, ofs, v) SCVAL(tdr->data.data,ofs,v)
2005-09-02 18:05:28 +04:00
# define TDR_SSVAL(tdr, ofs, v) do { if (TDR_BE(tdr)) { RSSVAL(tdr->data.data,ofs,v); } else SSVAL(tdr->data.data,ofs,v); } while (0)
# define TDR_SIVAL(tdr, ofs, v) do { if (TDR_BE(tdr)) { RSIVAL(tdr->data.data,ofs,v); } else SIVAL(tdr->data.data,ofs,v); } while (0)
2005-08-03 00:12:31 +04:00
2007-06-19 01:48:57 +04:00
/**
2005-08-03 00:12:31 +04:00
expand the available space in the buffer to ' size '
*/
NTSTATUS tdr_push_expand ( struct tdr_push * tdr , uint32_t size )
{
2005-09-02 18:05:28 +04:00
if ( talloc_get_size ( tdr - > data . data ) > = size ) {
2005-08-03 00:12:31 +04:00
return NT_STATUS_OK ;
}
2005-09-02 18:05:28 +04:00
tdr - > data . data = talloc_realloc ( tdr , tdr - > data . data , uint8_t , tdr - > data . length + TDR_BASE_MARSHALL_SIZE ) ;
2005-09-04 06:01:42 +04:00
if ( tdr - > data . data = = NULL )
return NT_STATUS_NO_MEMORY ;
return NT_STATUS_OK ;
2005-08-03 00:12:31 +04:00
}
2005-09-05 00:17:21 +04:00
NTSTATUS tdr_pull_uint8 ( struct tdr_pull * tdr , TALLOC_CTX * ctx , uint8_t * v )
2005-08-03 00:12:31 +04:00
{
TDR_PULL_NEED_BYTES ( tdr , 1 ) ;
2005-09-03 23:43:35 +04:00
* v = TDR_CVAL ( tdr , tdr - > offset ) ;
2005-08-03 00:12:31 +04:00
tdr - > offset + = 1 ;
return NT_STATUS_OK ;
}
NTSTATUS tdr_push_uint8 ( struct tdr_push * tdr , const uint8_t * v )
{
TDR_PUSH_NEED_BYTES ( tdr , 1 ) ;
2005-09-04 21:25:04 +04:00
TDR_SCVAL ( tdr , tdr - > data . length , * v ) ;
tdr - > data . length + = 1 ;
2005-08-03 00:12:31 +04:00
return NT_STATUS_OK ;
}
NTSTATUS tdr_print_uint8 ( struct tdr_print * tdr , const char * name , uint8_t * v )
{
tdr - > print ( tdr , " %-25s: 0x%02x (%u) " , name , * v , * v ) ;
return NT_STATUS_OK ;
}
2005-09-05 00:17:21 +04:00
NTSTATUS tdr_pull_uint16 ( struct tdr_pull * tdr , TALLOC_CTX * ctx , uint16_t * v )
2005-08-03 00:12:31 +04:00
{
TDR_PULL_NEED_BYTES ( tdr , 2 ) ;
* v = TDR_SVAL ( tdr , tdr - > offset ) ;
tdr - > offset + = 2 ;
return NT_STATUS_OK ;
}
NTSTATUS tdr_push_uint16 ( struct tdr_push * tdr , const uint16_t * v )
{
TDR_PUSH_NEED_BYTES ( tdr , 2 ) ;
2005-09-04 21:25:04 +04:00
TDR_SSVAL ( tdr , tdr - > data . length , * v ) ;
tdr - > data . length + = 2 ;
2005-08-03 00:12:31 +04:00
return NT_STATUS_OK ;
}
NTSTATUS tdr_print_uint16 ( struct tdr_print * tdr , const char * name , uint16_t * v )
{
tdr - > print ( tdr , " %-25s: 0x%02x (%u) " , name , * v , * v ) ;
return NT_STATUS_OK ;
}
2005-09-05 00:17:21 +04:00
NTSTATUS tdr_pull_uint32 ( struct tdr_pull * tdr , TALLOC_CTX * ctx , uint32_t * v )
2005-08-03 00:12:31 +04:00
{
TDR_PULL_NEED_BYTES ( tdr , 4 ) ;
* v = TDR_IVAL ( tdr , tdr - > offset ) ;
tdr - > offset + = 4 ;
return NT_STATUS_OK ;
}
2005-08-21 06:12:25 +04:00
NTSTATUS tdr_push_uint32 ( struct tdr_push * tdr , const uint32_t * v )
2005-08-03 00:12:31 +04:00
{
TDR_PUSH_NEED_BYTES ( tdr , 4 ) ;
2005-09-04 21:25:04 +04:00
TDR_SIVAL ( tdr , tdr - > data . length , * v ) ;
tdr - > data . length + = 4 ;
2005-08-03 00:12:31 +04:00
return NT_STATUS_OK ;
}
NTSTATUS tdr_print_uint32 ( struct tdr_print * tdr , const char * name , uint32_t * v )
{
tdr - > print ( tdr , " %-25s: 0x%02x (%u) " , name , * v , * v ) ;
return NT_STATUS_OK ;
}
2006-03-17 00:36:36 +03:00
NTSTATUS tdr_pull_charset ( struct tdr_pull * tdr , TALLOC_CTX * ctx , const char * * v , uint32_t length , uint32_t el_size , charset_t chset )
2005-08-03 00:12:31 +04:00
{
int ret ;
2005-08-21 06:12:25 +04:00
2005-08-03 00:12:31 +04:00
if ( length = = - 1 ) {
switch ( chset ) {
2005-08-21 06:12:25 +04:00
case CH_DOS :
2005-09-02 18:05:28 +04:00
length = ascii_len_n ( ( const char * ) tdr - > data . data + tdr - > offset , tdr - > data . length - tdr - > offset ) ;
2005-08-21 06:12:25 +04:00
break ;
case CH_UTF16 :
2005-09-02 18:05:28 +04:00
length = utf16_len_n ( tdr - > data . data + tdr - > offset , tdr - > data . length - tdr - > offset ) ;
2005-08-21 06:12:25 +04:00
break ;
default :
return NT_STATUS_INVALID_PARAMETER ;
2005-08-03 00:12:31 +04:00
}
}
2005-08-21 06:12:25 +04:00
2005-09-04 02:56:02 +04:00
if ( length = = 0 ) {
2005-09-05 00:17:21 +04:00
* v = talloc_strdup ( ctx , " " ) ;
2005-09-04 02:56:02 +04:00
return NT_STATUS_OK ;
}
2005-08-03 00:12:31 +04:00
TDR_PULL_NEED_BYTES ( tdr , el_size * length ) ;
2007-12-14 00:46:09 +03:00
ret = convert_string_talloc ( ctx , lp_iconv_convenience ( global_loadparm ) , chset , CH_UNIX , tdr - > data . data + tdr - > offset , el_size * length , discard_const_p ( void * , v ) ) ;
2005-08-03 00:12:31 +04:00
if ( ret = = - 1 ) {
return NT_STATUS_INVALID_PARAMETER ;
}
2005-09-02 18:45:40 +04:00
tdr - > offset + = length * el_size ;
2005-08-03 00:12:31 +04:00
return NT_STATUS_OK ;
}
2006-03-17 00:36:36 +03:00
NTSTATUS tdr_push_charset ( struct tdr_push * tdr , const char * * v , uint32_t length , uint32_t el_size , charset_t chset )
2005-08-03 00:12:31 +04:00
{
2005-08-21 06:12:25 +04:00
ssize_t ret , required ;
2005-10-02 18:29:08 +04:00
if ( length = = - 1 ) {
length = strlen ( * v ) + 1 ; /* Extra element for null character */
}
2005-08-21 06:12:25 +04:00
required = el_size * length ;
TDR_PUSH_NEED_BYTES ( tdr , required ) ;
2005-08-03 00:12:31 +04:00
2007-12-14 00:46:09 +03:00
ret = convert_string ( lp_iconv_convenience ( global_loadparm ) , CH_UNIX , chset , * v , strlen ( * v ) , tdr - > data . data + tdr - > data . length , required ) ;
2005-08-03 00:12:31 +04:00
if ( ret = = - 1 ) {
return NT_STATUS_INVALID_PARAMETER ;
}
2005-08-21 06:12:25 +04:00
/* Make sure the remaining part of the string is filled with zeroes */
if ( ret < required ) {
2005-09-04 21:25:04 +04:00
memset ( tdr - > data . data + tdr - > data . length + ret , 0 , required - ret ) ;
2005-08-21 06:12:25 +04:00
}
2005-08-03 00:12:31 +04:00
2005-09-04 21:25:04 +04:00
tdr - > data . length + = required ;
2005-08-03 00:12:31 +04:00
return NT_STATUS_OK ;
}
2006-03-17 00:36:36 +03:00
NTSTATUS tdr_print_charset ( struct tdr_print * tdr , const char * name , const char * * v , uint32_t length , uint32_t el_size , charset_t chset )
2005-08-03 00:12:31 +04:00
{
tdr - > print ( tdr , " %-25s: %s " , name , * v ) ;
return NT_STATUS_OK ;
}
2007-06-19 01:48:57 +04:00
/**
2005-08-03 00:12:31 +04:00
pull a ipv4address
*/
2007-06-19 01:48:57 +04:00
NTSTATUS tdr_pull_ipv4address ( struct tdr_pull * tdr , TALLOC_CTX * ctx ,
const char * * address )
2005-08-03 00:12:31 +04:00
{
2007-10-13 22:24:37 +04:00
struct in_addr in ;
TDR_CHECK ( tdr_pull_uint32 ( tdr , ctx , & in . s_addr ) ) ;
in . s_addr = htonl ( in . s_addr ) ;
* address = talloc_strdup ( tdr , inet_ntoa ( in ) ) ;
2005-08-03 00:12:31 +04:00
NT_STATUS_HAVE_NO_MEMORY ( * address ) ;
return NT_STATUS_OK ;
}
2007-06-19 01:48:57 +04:00
/**
2005-08-03 00:12:31 +04:00
push a ipv4address
*/
NTSTATUS tdr_push_ipv4address ( struct tdr_push * tdr , const char * * address )
{
uint32_t addr = htonl ( interpret_addr ( * address ) ) ;
TDR_CHECK ( tdr_push_uint32 ( tdr , & addr ) ) ;
return NT_STATUS_OK ;
}
2007-06-19 01:48:57 +04:00
/**
2005-08-03 00:12:31 +04:00
print a ipv4address
*/
NTSTATUS tdr_print_ipv4address ( struct tdr_print * tdr , const char * name ,
const char * * address )
{
tdr - > print ( tdr , " %-25s: %s " , name , * address ) ;
return NT_STATUS_OK ;
}
2005-08-21 06:12:25 +04:00
2007-06-19 01:48:57 +04:00
/**
2005-08-21 06:12:25 +04:00
parse a hyper
*/
2005-09-05 00:17:21 +04:00
NTSTATUS tdr_pull_hyper ( struct tdr_pull * tdr , TALLOC_CTX * ctx , uint64_t * v )
2005-08-21 06:12:25 +04:00
{
TDR_PULL_NEED_BYTES ( tdr , 8 ) ;
* v = TDR_IVAL ( tdr , tdr - > offset ) ;
* v | = ( uint64_t ) ( TDR_IVAL ( tdr , tdr - > offset + 4 ) ) < < 32 ;
tdr - > offset + = 8 ;
return NT_STATUS_OK ;
}
2007-06-19 01:48:57 +04:00
/**
2005-08-21 06:12:25 +04:00
push a hyper
*/
NTSTATUS tdr_push_hyper ( struct tdr_push * tdr , uint64_t * v )
{
TDR_PUSH_NEED_BYTES ( tdr , 8 ) ;
2005-09-04 21:25:04 +04:00
TDR_SIVAL ( tdr , tdr - > data . length , ( ( * v ) & 0xFFFFFFFF ) ) ;
TDR_SIVAL ( tdr , tdr - > data . length + 4 , ( ( * v ) > > 32 ) ) ;
tdr - > data . length + = 8 ;
2005-08-21 06:12:25 +04:00
return NT_STATUS_OK ;
}
2007-06-19 01:48:57 +04:00
/**
2005-08-21 06:12:25 +04:00
push a NTTIME
*/
NTSTATUS tdr_push_NTTIME ( struct tdr_push * tdr , NTTIME * t )
{
TDR_CHECK ( tdr_push_hyper ( tdr , t ) ) ;
return NT_STATUS_OK ;
}
2007-06-19 01:48:57 +04:00
/**
2005-08-21 06:12:25 +04:00
pull a NTTIME
*/
2005-09-05 00:17:21 +04:00
NTSTATUS tdr_pull_NTTIME ( struct tdr_pull * tdr , TALLOC_CTX * ctx , NTTIME * t )
2005-08-21 06:12:25 +04:00
{
2005-09-05 00:17:21 +04:00
TDR_CHECK ( tdr_pull_hyper ( tdr , ctx , t ) ) ;
2005-08-21 06:12:25 +04:00
return NT_STATUS_OK ;
}
2007-06-19 01:48:57 +04:00
/**
2005-08-21 06:12:25 +04:00
push a time_t
*/
NTSTATUS tdr_push_time_t ( struct tdr_push * tdr , time_t * t )
{
return tdr_push_uint32 ( tdr , ( uint32_t * ) t ) ;
}
2007-06-19 01:48:57 +04:00
/**
2005-08-21 06:12:25 +04:00
pull a time_t
*/
2005-09-05 00:17:21 +04:00
NTSTATUS tdr_pull_time_t ( struct tdr_pull * tdr , TALLOC_CTX * ctx , time_t * t )
2005-08-21 06:12:25 +04:00
{
uint32_t tt ;
2005-09-05 00:17:21 +04:00
TDR_CHECK ( tdr_pull_uint32 ( tdr , ctx , & tt ) ) ;
2005-08-21 06:12:25 +04:00
* t = tt ;
return NT_STATUS_OK ;
}
2005-08-21 17:19:36 +04:00
NTSTATUS tdr_print_time_t ( struct tdr_print * tdr , const char * name , time_t * t )
{
if ( * t = = ( time_t ) - 1 | | * t = = 0 ) {
tdr - > print ( tdr , " %-25s: (time_t)%d " , name , ( int ) * t ) ;
} else {
tdr - > print ( tdr , " %-25s: %s " , name , timestring ( tdr , * t ) ) ;
}
return NT_STATUS_OK ;
}
NTSTATUS tdr_print_NTTIME ( struct tdr_print * tdr , const char * name , NTTIME * t )
{
tdr - > print ( tdr , " %-25s: %s " , name , nt_time_string ( tdr , * t ) ) ;
return NT_STATUS_OK ;
}
2005-08-21 06:12:25 +04:00
2005-08-21 17:19:36 +04:00
NTSTATUS tdr_print_DATA_BLOB ( struct tdr_print * tdr , const char * name , DATA_BLOB * r )
{
tdr - > print ( tdr , " %-25s: DATA_BLOB length=%u " , name , r - > length ) ;
if ( r - > length ) {
dump_data ( 10 , r - > data , r - > length ) ;
}
return NT_STATUS_OK ;
}
2005-09-04 21:25:04 +04:00
# define TDR_ALIGN(l,n) (((l) & ((n)-1)) == 0?0:((n)-((l)&((n)-1))))
2005-08-21 17:19:36 +04:00
/*
push a DATA_BLOB onto the wire .
*/
NTSTATUS tdr_push_DATA_BLOB ( struct tdr_push * tdr , DATA_BLOB * blob )
{
if ( tdr - > flags & TDR_ALIGN2 ) {
2005-09-04 21:25:04 +04:00
blob - > length = TDR_ALIGN ( tdr - > data . length , 2 ) ;
2005-08-21 17:19:36 +04:00
} else if ( tdr - > flags & TDR_ALIGN4 ) {
2005-09-04 21:25:04 +04:00
blob - > length = TDR_ALIGN ( tdr - > data . length , 4 ) ;
2005-08-21 17:19:36 +04:00
} else if ( tdr - > flags & TDR_ALIGN8 ) {
2005-09-04 21:25:04 +04:00
blob - > length = TDR_ALIGN ( tdr - > data . length , 8 ) ;
2005-08-21 17:19:36 +04:00
}
TDR_PUSH_NEED_BYTES ( tdr , blob - > length ) ;
2005-09-04 21:25:04 +04:00
memcpy ( tdr - > data . data + tdr - > data . length , blob - > data , blob - > length ) ;
2005-08-21 17:19:36 +04:00
return NT_STATUS_OK ;
}
/*
pull a DATA_BLOB from the wire .
*/
2005-09-05 00:17:21 +04:00
NTSTATUS tdr_pull_DATA_BLOB ( struct tdr_pull * tdr , TALLOC_CTX * ctx , DATA_BLOB * blob )
2005-08-21 17:19:36 +04:00
{
uint32_t length ;
if ( tdr - > flags & TDR_ALIGN2 ) {
2005-09-04 21:25:04 +04:00
length = TDR_ALIGN ( tdr - > offset , 2 ) ;
2005-08-21 17:19:36 +04:00
} else if ( tdr - > flags & TDR_ALIGN4 ) {
2005-09-04 21:25:04 +04:00
length = TDR_ALIGN ( tdr - > offset , 4 ) ;
2005-08-21 17:19:36 +04:00
} else if ( tdr - > flags & TDR_ALIGN8 ) {
2005-09-04 21:25:04 +04:00
length = TDR_ALIGN ( tdr - > offset , 8 ) ;
2005-08-21 17:19:36 +04:00
} else if ( tdr - > flags & TDR_REMAINING ) {
2005-09-02 18:05:28 +04:00
length = tdr - > data . length - tdr - > offset ;
2005-08-21 17:19:36 +04:00
} else {
return NT_STATUS_INVALID_PARAMETER ;
}
2005-09-02 18:05:28 +04:00
if ( tdr - > data . length - tdr - > offset < length ) {
length = tdr - > data . length - tdr - > offset ;
2005-08-21 17:19:36 +04:00
}
TDR_PULL_NEED_BYTES ( tdr , length ) ;
2005-09-02 18:05:28 +04:00
* blob = data_blob_talloc ( tdr , tdr - > data . data + tdr - > offset , length ) ;
2005-08-21 17:19:36 +04:00
tdr - > offset + = length ;
return NT_STATUS_OK ;
}
2005-09-04 21:25:04 +04:00
NTSTATUS tdr_push_to_fd ( int fd , tdr_push_fn_t push_fn , const void * p )
{
struct tdr_push * push = talloc_zero ( NULL , struct tdr_push ) ;
if ( push = = NULL )
return NT_STATUS_NO_MEMORY ;
if ( NT_STATUS_IS_ERR ( push_fn ( push , p ) ) ) {
DEBUG ( 1 , ( " Error pushing data \n " ) ) ;
talloc_free ( push ) ;
return NT_STATUS_UNSUCCESSFUL ;
}
if ( write ( fd , push - > data . data , push - > data . length ) < push - > data . length ) {
DEBUG ( 1 , ( " Error writing all data \n " ) ) ;
return NT_STATUS_UNSUCCESSFUL ;
}
talloc_free ( push ) ;
return NT_STATUS_OK ;
}
2005-10-02 18:29:08 +04:00
void tdr_print_debug_helper ( struct tdr_print * tdr , const char * format , . . . ) _PRINTF_ATTRIBUTE ( 2 , 3 )
{
va_list ap ;
char * s = NULL ;
int i ;
va_start ( ap , format ) ;
vasprintf ( & s , format , ap ) ;
va_end ( ap ) ;
2007-12-14 00:46:09 +03:00
for ( i = 0 ; i < tdr - > level ; i + + ) { DEBUG ( 0 , ( " " ) ) ; }
2005-10-02 18:29:08 +04:00
DEBUG ( 0 , ( " %s \n " , s ) ) ;
free ( s ) ;
}