2005-10-14 16:38:07 +04:00
/*
Unix SMB / CIFS implementation .
WINS Replication server
Copyright ( C ) Stefan Metzmacher 2005
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 2 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
# include "includes.h"
# include "dlinklist.h"
# include "lib/events/events.h"
# include "lib/socket/socket.h"
# include "smbd/service_task.h"
# include "smbd/service_stream.h"
# include "lib/messaging/irpc.h"
# include "librpc/gen_ndr/ndr_winsrepl.h"
2005-10-14 16:54:26 +04:00
# include "librpc/gen_ndr/ndr_nbt.h"
2005-10-14 16:38:07 +04:00
# include "wrepl_server/wrepl_server.h"
2005-10-14 16:52:51 +04:00
# include "nbt_server/wins/winsdb.h"
2005-10-14 16:54:26 +04:00
# include "lib/ldb/include/ldb.h"
2005-10-14 16:38:07 +04:00
static NTSTATUS wreplsrv_in_start_association ( struct wreplsrv_in_call * call )
{
2005-10-14 16:44:47 +04:00
struct wrepl_start * start = & call - > req_packet . message . start ;
struct wrepl_start * start_reply = & call - > rep_packet . message . start_reply ;
if ( call - > req_packet . opcode & WREPL_OPCODE_BITS ) {
/*
* if the assoc_ctx doesn ' t match ignore the packet
*/
if ( ( call - > req_packet . assoc_ctx ! = call - > wreplconn - > assoc_ctx . our_ctx )
& & ( call - > req_packet . assoc_ctx ! = 0 ) ) {
return ERROR_INVALID_PARAMETER ;
}
} else {
call - > wreplconn - > assoc_ctx . our_ctx = WREPLSRV_INVALID_ASSOC_CTX ;
return NT_STATUS_OK ;
}
2005-10-14 16:38:07 +04:00
2005-10-14 16:44:47 +04:00
if ( start - > minor_version ! = 2 | | start - > major_version ! = 5 ) {
/* w2k terminate the connection if the versions doesn't match */
return NT_STATUS_UNKNOWN_REVISION ;
}
call - > wreplconn - > assoc_ctx . stopped = False ;
call - > wreplconn - > assoc_ctx . our_ctx = WREPLSRV_VALID_ASSOC_CTX ;
call - > wreplconn - > assoc_ctx . peer_ctx = start - > assoc_ctx ;
call - > rep_packet . mess_type = WREPL_START_ASSOCIATION_REPLY ;
start_reply - > assoc_ctx = call - > wreplconn - > assoc_ctx . our_ctx ;
start_reply - > minor_version = 2 ;
start_reply - > major_version = 5 ;
2005-10-14 16:38:07 +04:00
return NT_STATUS_OK ;
}
2005-10-14 16:44:47 +04:00
static NTSTATUS wreplsrv_in_stop_assoc_ctx ( struct wreplsrv_in_call * call )
{
struct wrepl_stop * stop_out = & call - > rep_packet . message . stop ;
call - > wreplconn - > assoc_ctx . stopped = True ;
call - > rep_packet . mess_type = WREPL_STOP_ASSOCIATION ;
stop_out - > reason = 4 ;
return NT_STATUS_OK ;
}
static NTSTATUS wreplsrv_in_stop_association ( struct wreplsrv_in_call * call )
{
/*
* w2k only check the assoc_ctx if the opcode has the 0x00007800 bits are set
*/
if ( call - > req_packet . opcode & WREPL_OPCODE_BITS ) {
/*
* if the assoc_ctx doesn ' t match ignore the packet
*/
if ( call - > req_packet . assoc_ctx ! = call - > wreplconn - > assoc_ctx . our_ctx ) {
return ERROR_INVALID_PARAMETER ;
}
/* when the opcode bits are set the connection should be directly terminated */
return NT_STATUS_CONNECTION_RESET ;
}
if ( call - > wreplconn - > assoc_ctx . stopped ) {
/* this causes the connection to be directly terminated */
return NT_STATUS_CONNECTION_RESET ;
}
/* this will cause to not receive packets anymore and terminate the connection if the reply is send */
call - > wreplconn - > terminate = True ;
return wreplsrv_in_stop_assoc_ctx ( call ) ;
}
2005-10-14 16:52:51 +04:00
static NTSTATUS wreplsrv_in_table_query ( struct wreplsrv_in_call * call )
{
struct wreplsrv_service * service = call - > wreplconn - > service ;
struct wrepl_replication * repl_out = & call - > rep_packet . message . replication ;
struct wrepl_table * table_out = & call - > rep_packet . message . replication . info . table ;
struct wreplsrv_owner * cur ;
uint64_t local_max_version ;
uint32_t i = 0 ;
repl_out - > command = WREPL_REPL_TABLE_REPLY ;
table_out - > partner_count = 0 ;
table_out - > partners = NULL ;
table_out - > initiator = WINSDB_OWNER_LOCAL ;
local_max_version = wreplsrv_local_max_version ( service ) ;
if ( local_max_version > 0 ) {
table_out - > partner_count + + ;
}
for ( cur = service - > table ; cur ; cur = cur - > next ) {
table_out - > partner_count + + ;
}
table_out - > partners = talloc_array ( call , struct wrepl_wins_owner , table_out - > partner_count ) ;
NT_STATUS_HAVE_NO_MEMORY ( table_out - > partners ) ;
if ( local_max_version > 0 ) {
table_out - > partners [ i ] . address = call - > wreplconn - > our_ip ;
table_out - > partners [ i ] . min_version = 0 ;
table_out - > partners [ i ] . max_version = local_max_version ;
table_out - > partners [ i ] . type = 1 ;
i + + ;
}
for ( cur = service - > table ; cur ; cur = cur - > next ) {
table_out - > partners [ i ] = cur - > owner ;
i + + ;
}
return NT_STATUS_OK ;
}
2005-10-14 16:54:26 +04:00
static int wreplsrv_in_sort_wins_name ( struct wrepl_wins_name * n1 ,
struct wrepl_wins_name * n2 )
{
if ( n1 - > id < n2 - > id ) return - 1 ;
if ( n1 - > id > n2 - > id ) return 1 ;
return 0 ;
}
static NTSTATUS wreplsrv_record2wins_name ( TALLOC_CTX * mem_ctx , struct wrepl_wins_name * name , struct winsdb_record * rec )
{
uint8_t * namebuf ;
uint32_t namebuf_len ;
uint32_t name_len ;
name_len = strlen ( rec - > name - > name ) ;
if ( name_len > 15 ) {
return NT_STATUS_INVALID_PARAMETER_MIX ;
}
namebuf = ( uint8_t * ) talloc_asprintf ( mem_ctx , " %-15s%c%s " ,
rec - > name - > name , rec - > name - > type ,
( rec - > name - > scope ? rec - > name - > scope : " " ) ) ;
NT_STATUS_HAVE_NO_MEMORY ( namebuf ) ;
namebuf_len = strlen ( ( char * ) namebuf ) + 1 ;
/* oh wow, what a nasty bug in windows ... */
if ( namebuf [ 15 ] = = 0x1b & & namebuf_len > = 16 ) {
namebuf [ 15 ] = namebuf [ 0 ] ;
namebuf [ 0 ] = 0x1b ;
}
name - > name_len = namebuf_len ;
name - > name = namebuf ;
name - > id = rec - > version ;
name - > unknown = WINSDB_GROUP_ADDRESS ;
name - > flags = rec - > nb_flags ;
name - > group_flag = 0 ;
switch ( name - > flags & 2 ) {
case 0 :
name - > addresses . ip = rec - > addresses [ 0 ] - > address ;
talloc_steal ( mem_ctx , rec - > addresses [ 0 ] - > address ) ;
break ;
case 2 :
name - > addresses . addresses . num_ips = 0 ;
name - > addresses . addresses . ips = NULL ;
break ;
}
return NT_STATUS_OK ;
}
2005-10-14 16:52:51 +04:00
static NTSTATUS wreplsrv_in_send_request ( struct wreplsrv_in_call * call )
{
2005-10-14 16:54:26 +04:00
struct wreplsrv_service * service = call - > wreplconn - > service ;
struct wrepl_wins_owner * owner_in = & call - > req_packet . message . replication . info . owner ;
2005-10-14 16:52:51 +04:00
struct wrepl_replication * repl_out = & call - > rep_packet . message . replication ;
struct wrepl_send_reply * reply_out = & call - > rep_packet . message . replication . info . reply ;
2005-10-14 16:54:26 +04:00
struct wreplsrv_owner local_owner ;
struct wreplsrv_owner * owner ;
const char * filter ;
struct ldb_message * * res = NULL ;
int ret ;
struct wrepl_wins_name * names ;
struct winsdb_record * rec ;
NTSTATUS status ;
uint32_t i ;
if ( strcmp ( call - > wreplconn - > our_ip , owner_in - > address ) = = 0 ) {
ZERO_STRUCT ( local_owner ) ;
local_owner . owner . address = WINSDB_OWNER_LOCAL ;
local_owner . owner . min_version = 0 ;
local_owner . owner . max_version = wreplsrv_local_max_version ( service ) ;
local_owner . owner . type = 1 ;
owner = & local_owner ;
} else {
owner = wreplsrv_find_owner ( service - > table , owner_in - > address ) ;
}
2005-10-14 16:52:51 +04:00
2005-10-14 16:54:26 +04:00
repl_out - > command = WREPL_REPL_SEND_REPLY ;
2005-10-14 16:52:51 +04:00
reply_out - > num_names = 0 ;
reply_out - > names = NULL ;
2005-10-14 16:54:26 +04:00
/*
* if we didn ' t know this owner , must be a bug in the partners client code . . .
* return an empty list .
*/
if ( ! owner ) {
return NT_STATUS_OK ;
}
/*
* if the partner ask for nothing , or give invalid ranges ,
* return an empty list .
*/
if ( owner_in - > min_version > = owner_in - > max_version ) {
return NT_STATUS_OK ;
}
/*
* if the partner has already all records for nothing , or give invalid ranges ,
* return an empty list .
*/
if ( owner_in - > min_version > = owner - > owner . max_version ) {
return NT_STATUS_OK ;
}
filter = talloc_asprintf ( call , " (&(winsOwner=%s)(objectClass=wins)(active=1)(version>=%llu)(version<=%llu)) " ,
owner - > owner . address , owner_in - > min_version , owner_in - > max_version ) ;
NT_STATUS_HAVE_NO_MEMORY ( filter ) ;
ret = ldb_search ( service - > wins_db , NULL , LDB_SCOPE_SUBTREE , filter , NULL , & res ) ;
if ( res ! = NULL ) {
talloc_steal ( call , res ) ;
}
if ( ret < 0 ) return NT_STATUS_INTERNAL_DB_CORRUPTION ;
if ( ret = = 0 ) return NT_STATUS_OK ;
names = talloc_array ( call , struct wrepl_wins_name , ret ) ;
NT_STATUS_HAVE_NO_MEMORY ( names ) ;
for ( i = 0 ; i < ret ; i + + ) {
rec = winsdb_record ( res [ i ] , call ) ;
NT_STATUS_HAVE_NO_MEMORY ( rec ) ;
rec - > name = winsdb_nbt_name ( names , res [ i ] - > dn ) ;
if ( ! rec - > name ) {
return NT_STATUS_INTERNAL_DB_CORRUPTION ;
}
status = wreplsrv_record2wins_name ( names , & names [ i ] , rec ) ;
NT_STATUS_NOT_OK_RETURN ( status ) ;
talloc_free ( rec ) ;
talloc_free ( res [ i ] ) ;
}
/* sort the names before we send them */
qsort ( names , ret , sizeof ( struct wrepl_wins_name ) , ( comparison_fn_t ) wreplsrv_in_sort_wins_name ) ;
reply_out - > num_names = ret ;
reply_out - > names = names ;
2005-10-14 16:52:51 +04:00
return NT_STATUS_OK ;
}
2005-10-14 16:38:07 +04:00
static NTSTATUS wreplsrv_in_replication ( struct wreplsrv_in_call * call )
{
struct wrepl_replication * repl_in = & call - > req_packet . message . replication ;
2005-10-14 16:52:51 +04:00
NTSTATUS status ;
2005-10-14 16:44:47 +04:00
/*
* w2k only check the assoc_ctx if the opcode has the 0x00007800 bits are set
*/
if ( call - > req_packet . opcode & WREPL_OPCODE_BITS ) {
/*
* if the assoc_ctx doesn ' t match ignore the packet
*/
if ( call - > req_packet . assoc_ctx ! = call - > wreplconn - > assoc_ctx . our_ctx ) {
return ERROR_INVALID_PARAMETER ;
}
}
if ( ! call - > wreplconn - > partner ) {
return wreplsrv_in_stop_assoc_ctx ( call ) ;
}
2005-10-14 16:38:07 +04:00
switch ( repl_in - > command ) {
case WREPL_REPL_TABLE_QUERY :
2005-10-14 16:52:51 +04:00
status = wreplsrv_in_table_query ( call ) ;
2005-10-14 16:38:07 +04:00
break ;
2005-10-14 16:52:51 +04:00
2005-10-14 16:38:07 +04:00
case WREPL_REPL_TABLE_REPLY :
2005-10-14 16:52:51 +04:00
return ERROR_INVALID_PARAMETER ;
2005-10-14 16:38:07 +04:00
case WREPL_REPL_SEND_REQUEST :
2005-10-14 16:52:51 +04:00
status = wreplsrv_in_send_request ( call ) ;
2005-10-14 16:38:07 +04:00
break ;
2005-10-14 16:52:51 +04:00
2005-10-14 16:38:07 +04:00
case WREPL_REPL_SEND_REPLY :
2005-10-14 16:52:51 +04:00
return ERROR_INVALID_PARAMETER ;
2005-10-14 16:38:07 +04:00
case WREPL_REPL_UPDATE :
2005-10-14 16:52:51 +04:00
return ERROR_INVALID_PARAMETER ;
2005-10-14 16:38:07 +04:00
case WREPL_REPL_5 :
2005-10-14 16:52:51 +04:00
return ERROR_INVALID_PARAMETER ;
2005-10-14 16:38:07 +04:00
case WREPL_REPL_INFORM :
2005-10-14 16:52:51 +04:00
return ERROR_INVALID_PARAMETER ;
2005-10-14 16:38:07 +04:00
case WREPL_REPL_9 :
2005-10-14 16:52:51 +04:00
return ERROR_INVALID_PARAMETER ;
default :
return ERROR_INVALID_PARAMETER ;
}
if ( NT_STATUS_IS_OK ( status ) ) {
call - > rep_packet . mess_type = WREPL_REPLICATION ;
2005-10-14 16:38:07 +04:00
}
2005-10-14 16:52:51 +04:00
return status ;
2005-10-14 16:44:47 +04:00
}
static NTSTATUS wreplsrv_in_invalid_assoc_ctx ( struct wreplsrv_in_call * call )
{
struct wrepl_start * start = & call - > rep_packet . message . start ;
call - > rep_packet . opcode = 0x00008583 ;
2005-10-14 16:38:07 +04:00
call - > rep_packet . assoc_ctx = 0 ;
2005-10-14 16:44:47 +04:00
call - > rep_packet . mess_type = WREPL_START_ASSOCIATION ;
start - > assoc_ctx = 0x0000000a ;
start - > minor_version = 0x0001 ;
start - > major_version = 0x0000 ;
call - > rep_packet . padding = data_blob_talloc ( call , NULL , 4 ) ;
memset ( call - > rep_packet . padding . data , ' \0 ' , call - > rep_packet . padding . length ) ;
2005-10-14 16:38:07 +04:00
return NT_STATUS_OK ;
}
NTSTATUS wreplsrv_in_call ( struct wreplsrv_in_call * call )
{
2005-10-14 16:44:47 +04:00
NTSTATUS status ;
2005-10-14 16:38:07 +04:00
2005-10-14 16:44:47 +04:00
if ( ! ( call - > req_packet . opcode & WREPL_OPCODE_BITS )
& & ( call - > wreplconn - > assoc_ctx . our_ctx = = WREPLSRV_INVALID_ASSOC_CTX ) ) {
return wreplsrv_in_invalid_assoc_ctx ( call ) ;
}
2005-10-14 16:38:07 +04:00
switch ( call - > req_packet . mess_type ) {
case WREPL_START_ASSOCIATION :
2005-10-14 16:44:47 +04:00
status = wreplsrv_in_start_association ( call ) ;
2005-10-14 16:38:07 +04:00
break ;
2005-10-14 16:44:47 +04:00
case WREPL_START_ASSOCIATION_REPLY :
/* this is not valid here, so we ignore it */
return ERROR_INVALID_PARAMETER ;
2005-10-14 16:38:07 +04:00
case WREPL_STOP_ASSOCIATION :
2005-10-14 16:44:47 +04:00
status = wreplsrv_in_stop_association ( call ) ;
2005-10-14 16:38:07 +04:00
break ;
case WREPL_REPLICATION :
2005-10-14 16:44:47 +04:00
status = wreplsrv_in_replication ( call ) ;
break ;
default :
/* everythingelse is also not valid here, so we ignore it */
return ERROR_INVALID_PARAMETER ;
2005-10-14 16:38:07 +04:00
}
2005-10-14 16:44:47 +04:00
if ( call - > wreplconn - > assoc_ctx . our_ctx = = WREPLSRV_INVALID_ASSOC_CTX ) {
return wreplsrv_in_invalid_assoc_ctx ( call ) ;
}
2005-10-14 16:38:07 +04:00
2005-10-14 16:44:47 +04:00
if ( NT_STATUS_IS_OK ( status ) ) {
call - > rep_packet . opcode = WREPL_OPCODE_BITS ;
call - > rep_packet . assoc_ctx = call - > wreplconn - > assoc_ctx . peer_ctx ;
}
2005-10-14 16:38:07 +04:00
2005-10-14 16:44:47 +04:00
return status ;
}