0001-01-01 02:30:17 +02:30
/*
Unix SMB / Netbios implementation .
Version 1.9 .
NBT netbios routines and daemon - version 2
0001-01-01 02:30:17 +02:30
Copyright ( C ) Andrew Tridgell 1994 - 1998
Copyright ( C ) Luke Kenneth Casson Leighton 1994 - 1998
Copyright ( C ) Jeremy Allison 1994 - 1998
0001-01-01 02:30:17 +02:30
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 .
This file contains all the code to process NetBIOS requests coming
in on port 137. It does not deal with the code needed to service
WINS server requests , but only broadcast and unicast requests .
*/
# include "includes.h"
extern int DEBUGLEVEL ;
0001-01-01 02:30:17 +02:30
extern fstring global_myworkgroup ;
0001-01-01 02:30:17 +02:30
/****************************************************************************
Send a name release response .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void send_name_release_response ( int rcode , struct packet_struct * p )
{
struct nmb_packet * nmb = & p - > packet . nmb ;
char rdata [ 6 ] ;
memcpy ( & rdata [ 0 ] , & nmb - > additional - > rdata [ 0 ] , 6 ) ;
reply_netbios_packet ( p , /* Packet to reply to. */
rcode , /* Result code. */
NMB_REL , /* nmbd type code. */
NMB_NAME_RELEASE_OPCODE , /* opcode. */
0 , /* ttl. */
rdata , /* data to send. */
6 ) ; /* data length. */
}
/****************************************************************************
Process a name release packet on a broadcast subnet .
Ignore it if it ' s not one of our names .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void process_name_release_request ( struct subnet_record * subrec ,
struct packet_struct * p )
{
struct nmb_packet * nmb = & p - > packet . nmb ;
struct in_addr owner_ip ;
struct nmb_name * question = & nmb - > question . question_name ;
BOOL bcast = nmb - > header . nm_flags . bcast ;
uint16 nb_flags = get_nb_flags ( nmb - > additional - > rdata ) ;
BOOL group = ( nb_flags & NB_GROUP ) ? True : False ;
struct name_record * namerec ;
int rcode = 0 ;
putip ( ( char * ) & owner_ip , & nmb - > additional - > rdata [ 2 ] ) ;
if ( ! bcast )
{
/* We should only get broadcast name release packets here.
Anyone trying to release unicast should be going to a WINS
server . If the code gets here , then either we are not a wins
server and they sent it anyway , or we are a WINS server and
the request was malformed . Either way , log an error here .
and send an error reply back .
*/
DEBUG ( 0 , ( " process_name_release_request: unicast name release request \
received for name % s from IP % s on subnet % s . Error - should be sent to WINS server \ n " ,
namestr ( question ) , inet_ntoa ( owner_ip ) , subrec - > subnet_name ) ) ;
send_name_release_response ( FMT_ERR , p ) ;
return ;
}
DEBUG ( 3 , ( " process_name_release_request: Name release on name %s, \
subnet % s from owner IP % s \ n " ,
namestr ( & nmb - > question . question_name ) ,
subrec - > subnet_name , inet_ntoa ( owner_ip ) ) ) ;
/* If someone is releasing a broadcast group name, just ignore it. */
if ( group & & ! ismyip ( owner_ip ) )
return ;
0001-01-01 02:30:17 +02:30
/*
* Code to work around a bug in FTP OnNet software NBT implementation .
* They do a broadcast name release for WORKGROUP < 0 > and WORKGROUP < 1 e >
* names and * don ' t set the group bit * ! ! ! ! !
*/
0001-01-01 02:30:17 +02:30
if ( ! group & & ! ismyip ( owner_ip ) & & strequal ( question - > name , global_myworkgroup ) & &
0001-01-01 02:30:17 +02:30
( ( question - > name_type = = 0x0 ) | | ( question - > name_type = = 0x1e ) ) )
{
DEBUG ( 6 , ( " process_name_release_request: FTP OnNet bug workaround. Ignoring \
group release name % s from IP % s on subnet % s with no group bit set . \ n " ,
namestr ( question ) , inet_ntoa ( owner_ip ) , subrec - > subnet_name ) ) ;
return ;
}
0001-01-01 02:30:17 +02:30
namerec = find_name_on_subnet ( subrec , & nmb - > question . question_name , FIND_ANY_NAME ) ;
/* We only care about someone trying to release one of our names. */
if ( namerec & & ( ( namerec - > source = = SELF_NAME ) | | ( namerec - > source = = PERMANENT_NAME ) ) )
{
rcode = ACT_ERR ;
DEBUG ( 0 , ( " process_name_release_request: Attempt to release name %s from IP %s \
on subnet % s being rejected as it is one of our names . \ n " ,
namestr ( & nmb - > question . question_name ) , inet_ntoa ( owner_ip ) , subrec - > subnet_name ) ) ;
}
if ( rcode = = 0 )
return ;
/* Send a NAME RELEASE RESPONSE (pos/neg) see rfc1002.txt 4.2.10-11 */
send_name_release_response ( rcode , p ) ;
}
/****************************************************************************
Send a name registration response .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void send_name_registration_response ( int rcode , int ttl , struct packet_struct * p )
{
struct nmb_packet * nmb = & p - > packet . nmb ;
char rdata [ 6 ] ;
memcpy ( & rdata [ 0 ] , & nmb - > additional - > rdata [ 0 ] , 6 ) ;
reply_netbios_packet ( p , /* Packet to reply to. */
rcode , /* Result code. */
NMB_REG , /* nmbd type code. */
NMB_NAME_REG_OPCODE , /* opcode. */
ttl , /* ttl. */
rdata , /* data to send. */
6 ) ; /* data length. */
}
/****************************************************************************
Process a name refresh request on a broadcast subnet .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void process_name_refresh_request ( struct subnet_record * subrec ,
struct packet_struct * p )
{
struct nmb_packet * nmb = & p - > packet . nmb ;
struct nmb_name * question = & nmb - > question . question_name ;
BOOL bcast = nmb - > header . nm_flags . bcast ;
struct in_addr from_ip ;
putip ( ( char * ) & from_ip , & nmb - > additional - > rdata [ 2 ] ) ;
if ( ! bcast )
{
/* We should only get broadcast name refresh packets here.
Anyone trying to refresh unicast should be going to a WINS
server . If the code gets here , then either we are not a wins
server and they sent it anyway , or we are a WINS server and
the request was malformed . Either way , log an error here .
and send an error reply back .
*/
DEBUG ( 0 , ( " process_name_refresh_request: unicast name registration request \
received for name % s from IP % s on subnet % s . Error - should be sent to WINS server \ n " ,
namestr ( question ) , inet_ntoa ( from_ip ) , subrec - > subnet_name ) ) ;
send_name_registration_response ( FMT_ERR , 0 , p ) ;
return ;
}
/* Just log a message. We really don't care about broadcast name
refreshes . */
DEBUG ( 3 , ( " process_name_refresh_request: Name refresh for name %s \
IP % s on subnet % s \ n " , namestr(question), inet_ntoa(from_ip), subrec->subnet_name));
}
/****************************************************************************
Process a name registration request on a broadcast subnet .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void process_name_registration_request ( struct subnet_record * subrec ,
struct packet_struct * p )
{
struct nmb_packet * nmb = & p - > packet . nmb ;
struct nmb_name * question = & nmb - > question . question_name ;
BOOL bcast = nmb - > header . nm_flags . bcast ;
uint16 nb_flags = get_nb_flags ( nmb - > additional - > rdata ) ;
BOOL group = ( nb_flags & NB_GROUP ) ? True : False ;
struct name_record * namerec = NULL ;
int ttl = nmb - > additional - > ttl ;
struct in_addr from_ip ;
putip ( ( char * ) & from_ip , & nmb - > additional - > rdata [ 2 ] ) ;
if ( ! bcast )
{
/* We should only get broadcast name registration packets here.
Anyone trying to register unicast should be going to a WINS
server . If the code gets here , then either we are not a wins
server and they sent it anyway , or we are a WINS server and
the request was malformed . Either way , log an error here .
and send an error reply back .
*/
DEBUG ( 0 , ( " process_name_registration_request: unicast name registration request \
received for name % s from IP % s on subnet % s . Error - should be sent to WINS server \ n " ,
namestr ( question ) , inet_ntoa ( from_ip ) , subrec - > subnet_name ) ) ;
send_name_registration_response ( FMT_ERR , 0 , p ) ;
return ;
}
DEBUG ( 3 , ( " process_name_registration_request: Name registration for name %s \
IP % s on subnet % s \ n " , namestr(question), inet_ntoa(from_ip), subrec->subnet_name));
/* See if the name already exists. */
namerec = find_name_on_subnet ( subrec , question , FIND_ANY_NAME ) ;
0001-01-01 02:30:17 +02:30
/*
* If the name being registered exists and is a WINS_PROXY_NAME
* then delete the WINS proxy name entry so we don ' t reply erroneously
* later to queries .
*/
if ( ( namerec ! = NULL ) & & ( namerec - > source = = WINS_PROXY_NAME ) )
{
remove_name_from_namelist ( subrec , namerec ) ;
namerec = NULL ;
}
0001-01-01 02:30:17 +02:30
if ( ! group )
{
/* Unique name. */
if ( ( namerec ! = NULL ) & &
( ( namerec - > source = = SELF_NAME ) | | ( namerec - > source = = PERMANENT_NAME ) | |
NAME_GROUP ( namerec ) )
)
{
/* No-one can register one of Samba's names, nor can they
register a name that ' s a group name as a unique name */
send_name_registration_response ( ACT_ERR , 0 , p ) ;
return ;
}
else if ( namerec ! = NULL )
{
/* Update the namelist record with the new information. */
namerec - > ip [ 0 ] = from_ip ;
update_name_ttl ( namerec , ttl ) ;
DEBUG ( 3 , ( " process_name_registration_request: Updated name record %s \
with IP % s on subnet % s \ n " ,namestr(&namerec->name),inet_ntoa(from_ip), subrec->subnet_name));
return ;
}
}
else
{
/* Group name. */
if ( ( namerec ! = NULL ) & & ! NAME_GROUP ( namerec ) & &
( ( namerec - > source = = SELF_NAME ) | | ( namerec - > source = = PERMANENT_NAME ) )
)
{
/* Disallow group names when we have a unique name. */
send_name_registration_response ( ACT_ERR , 0 , p ) ;
return ;
}
}
}
/****************************************************************************
This is used to sort names for a name status into a sensible order .
We put our own names first , then in alphabetical order .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int status_compare ( char * n1 , char * n2 )
{
0001-01-01 02:30:17 +02:30
extern pstring global_myname ;
0001-01-01 02:30:17 +02:30
int l1 , l2 , l3 ;
/* It's a bit tricky because the names are space padded */
for ( l1 = 0 ; l1 < 15 & & n1 [ l1 ] & & n1 [ l1 ] ! = ' ' ; l1 + + ) ;
for ( l2 = 0 ; l2 < 15 & & n2 [ l2 ] & & n2 [ l2 ] ! = ' ' ; l2 + + ) ;
0001-01-01 02:30:17 +02:30
l3 = strlen ( global_myname ) ;
0001-01-01 02:30:17 +02:30
0001-01-01 02:30:17 +02:30
if ( ( l1 = = l3 ) & & strncmp ( n1 , global_myname , l3 ) = = 0 & &
( l2 ! = l3 | | strncmp ( n2 , global_myname , l3 ) ! = 0 ) )
0001-01-01 02:30:17 +02:30
return - 1 ;
0001-01-01 02:30:17 +02:30
if ( ( l2 = = l3 ) & & strncmp ( n2 , global_myname , l3 ) = = 0 & &
( l1 ! = l3 | | strncmp ( n1 , global_myname , l3 ) ! = 0 ) )
0001-01-01 02:30:17 +02:30
return 1 ;
return memcmp ( n1 , n2 , 18 ) ;
}
/****************************************************************************
Process a node status query
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void process_node_status_request ( struct subnet_record * subrec , struct packet_struct * p )
{
struct nmb_packet * nmb = & p - > packet . nmb ;
char * qname = nmb - > question . question_name . name ;
int ques_type = nmb - > question . question_name . name_type ;
char rdata [ MAX_DGRAM_SIZE ] ;
char * countptr , * buf , * bufend , * buf0 ;
int names_added , i ;
struct name_record * namerec ;
DEBUG ( 3 , ( " process_node_status_request: status request for name %s from IP %s on \
subnet % s . \ n " , namestr(&nmb->question.question_name), inet_ntoa(p->ip),
subrec - > subnet_name ) ) ;
if ( ( namerec = find_name_on_subnet ( subrec , & nmb - > question . question_name ,
FIND_SELF_NAME ) ) = = 0 )
{
DEBUG ( 1 , ( " process_node_status_request: status request for name %s from IP %s on \
subnet % s - name not found . \ n " , namestr(&nmb->question.question_name),
inet_ntoa ( p - > ip ) , subrec - > subnet_name ) ) ;
return ;
}
/* XXXX hack, we should calculate exactly how many will fit. */
bufend = & rdata [ MAX_DGRAM_SIZE ] - 18 ;
countptr = buf = rdata ;
buf + = 1 ;
buf0 = buf ;
names_added = 0 ;
namerec = subrec - > namelist ;
while ( buf < bufend )
{
if ( ( namerec - > source = = SELF_NAME ) | | ( namerec - > source = = PERMANENT_NAME ) )
{
int name_type = namerec - > name . name_type ;
if ( ! strequal ( namerec - > name . name , " * " ) & &
! strequal ( namerec - > name . name , " __SAMBA__ " ) & &
( name_type < 0x1b | | name_type > = 0x20 | |
ques_type < 0x1b | | ques_type > = 0x20 | |
strequal ( qname , namerec - > name . name ) ) )
{
/* Start with the name. */
bzero ( buf , 18 ) ;
sprintf ( buf , " %-15.15s " , namerec - > name . name ) ;
strupper ( buf ) ;
/* Put the name type and netbios flags in the buffer. */
buf [ 15 ] = name_type ;
set_nb_flags ( & buf [ 16 ] , namerec - > nb_flags ) ;
buf [ 16 ] | = NB_ACTIVE ; /* all our names are active */
buf + = 18 ;
names_added + + ;
}
}
/* Remove duplicate names. */
qsort ( buf0 , names_added , 18 , QSORT_CAST status_compare ) ;
for ( i = 1 ; i < names_added ; i + + )
{
if ( memcmp ( buf0 + 18 * i , buf0 + 18 * ( i - 1 ) , 16 ) = = 0 )
{
names_added - - ;
if ( names_added = = i )
break ;
memmove ( buf0 + 18 * i , buf0 + 18 * ( i + 1 ) , 18 * ( names_added - i ) ) ;
i - - ;
}
}
buf = buf0 + 18 * names_added ;
namerec = namerec - > next ;
if ( ! namerec )
{
/* End of the subnet specific name list. Now
add the names on the unicast subnet . */
struct subnet_record * uni_subrec = unicast_subnet ;
if ( uni_subrec ! = subrec )
{
subrec = uni_subrec ;
namerec = subrec - > namelist ;
}
}
if ( ! namerec )
break ;
}
SCVAL ( countptr , 0 , names_added ) ;
/* We don't send any stats as they could be used to attack
the protocol . */
bzero ( buf , 64 ) ;
buf + = 46 ;
/* Send a NODE STATUS RESPONSE */
reply_netbios_packet ( p , /* Packet to reply to. */
0 , /* Result code. */
NMB_STATUS , /* nmbd type code. */
NMB_NAME_QUERY_OPCODE , /* opcode. */
0 , /* ttl. */
rdata , /* data to send. */
PTR_DIFF ( buf , rdata ) ) ; /* data length. */
}
/***************************************************************************
Process a name query .
For broadcast name queries :
- Only reply if the query is for one of YOUR names .
- NEVER send a negative response to a broadcast query .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void process_name_query_request ( struct subnet_record * subrec , struct packet_struct * p )
{
struct nmb_packet * nmb = & p - > packet . nmb ;
struct nmb_name * question = & nmb - > question . question_name ;
int name_type = question - > name_type ;
BOOL bcast = nmb - > header . nm_flags . bcast ;
int ttl = 0 ;
int rcode = 0 ;
char * prdata = NULL ;
char rdata [ 6 ] ;
BOOL success = False ;
struct name_record * namerec = NULL ;
int reply_data_len = 0 ;
int i ;
DEBUG ( 3 , ( " process_name_query_request: Name query from %s on subnet %s for name %s \n " ,
inet_ntoa ( p - > ip ) , subrec - > subnet_name , namestr ( question ) ) ) ;
/* Look up the name in the cache - if the request is a broadcast request that
came from a subnet we don ' t know about then search all the broadcast subnets
for a match ( as we don ' t know what interface the request came in on ) . */
if ( subrec = = remote_broadcast_subnet )
namerec = find_name_for_remote_broadcast_subnet ( question , FIND_ANY_NAME ) ;
else
namerec = find_name_on_subnet ( subrec , question , FIND_ANY_NAME ) ;
/* Check if it is a name that expired */
if ( namerec & & ( ( namerec - > death_time ! = PERMANENT_TTL ) & & ( namerec - > death_time < p - > timestamp ) ) )
{
DEBUG ( 5 , ( " process_name_query_request: expired name %s \n " , namestr ( & namerec - > name ) ) ) ;
namerec = NULL ;
}
if ( namerec )
{
/*
* Always respond to unicast queries .
* Don ' t respond to broadcast queries unless the query is for
* a name we own , a Primary Domain Controller name , or a WINS_PROXY
* name with type 0 or 0x20 . WINS_PROXY names are only ever added
* into the namelist if we were configured as a WINS proxy .
*/
if ( ! bcast | |
( bcast & & ( ( name_type = = 0x1b ) | | ( namerec - > source = = SELF_NAME ) | |
( namerec - > source = = PERMANENT_NAME ) | |
( ( namerec - > source = = WINS_PROXY_NAME ) & & ( ( name_type = = 0 ) | | ( name_type = = 0x20 ) ) ) ) )
)
{
/* The requested name is a directed query, or it's SELF or PERMANENT or WINS_PROXY,
or it ' s a Domain Master type . */
0001-01-01 02:30:17 +02:30
/*
* If this is a WINS_PROXY_NAME , then ceck that none of the IP
* addresses we are returning is on the same broadcast subnet
* as the requesting packet . If it is then don ' t reply as the
* actual machine will be replying also and we don ' t want two
* replies to a broadcast query .
*/
if ( namerec - > source = = WINS_PROXY_NAME )
{
for ( i = 0 ; i < namerec - > num_ips ; i + + )
{
if ( same_net ( namerec - > ip [ i ] , subrec - > myip , subrec - > mask_ip ) )
{
DEBUG ( 5 , ( " process_name_query_request: name %s is a WINS proxy name and is also \
on the same subnet ( % s ) as the requestor . Not replying . \ n " ,
namestr ( & namerec - > name ) , subrec - > subnet_name ) ) ;
return ;
}
}
}
0001-01-01 02:30:17 +02:30
ttl = ( namerec - > death_time ! = PERMANENT_TTL ) ?
namerec - > death_time - p - > timestamp : lp_max_ttl ( ) ;
/* Copy all known ip addresses into the return data. */
/* Optimise for the common case of one IP address so
we don ' t need a malloc . */
if ( namerec - > num_ips = = 1 )
prdata = rdata ;
else
{
if ( ( prdata = ( char * ) malloc ( namerec - > num_ips * 6 ) ) = = NULL )
{
DEBUG ( 0 , ( " process_name_query_request: malloc fail ! \n " ) ) ;
return ;
}
}
for ( i = 0 ; i < namerec - > num_ips ; i + + )
{
set_nb_flags ( & prdata [ i * 6 ] , namerec - > nb_flags ) ;
putip ( ( char * ) & prdata [ 2 + ( i * 6 ) ] , & namerec - > ip [ i ] ) ;
}
reply_data_len = namerec - > num_ips * 6 ;
success = True ;
}
}
/*
* If a machine is broadcasting a name lookup request and we have lp_wins_proxy ( )
* set we should initiate a WINS query here . On success we add the resolved name
* into our namelist with a type of WINS_PROXY_NAME and then reply to the query .
*/
if ( ! success & & ( namerec = = NULL ) & & we_are_a_wins_client ( ) & & lp_wins_proxy ( ) & &
bcast & & ( subrec ! = remote_broadcast_subnet ) )
{
make_wins_proxy_name_query_request ( subrec , p , question ) ;
return ;
}
if ( ! success & & bcast )
{
if ( ( prdata ! = rdata ) & & ( prdata ! = NULL ) )
free ( rdata ) ;
return ; /* Never reply with a negative response to broadcasts. */
}
/*
* Final check . From observation , if a unicast packet is sent
* to a non - WINS server with the recursion desired bit set
* then never send a negative response .
*/
if ( ! success & & ! bcast & & nmb - > header . nm_flags . recursion_desired )
{
if ( ( prdata ! = rdata ) & & ( prdata ! = NULL ) )
free ( rdata ) ;
return ;
}
if ( success )
{
rcode = 0 ;
DEBUG ( 3 , ( " OK \n " ) ) ;
}
else
{
rcode = NAM_ERR ;
DEBUG ( 3 , ( " UNKNOWN \n " ) ) ;
}
/* See rfc1002.txt 4.2.13. */
reply_netbios_packet ( p , /* Packet to reply to. */
rcode , /* Result code. */
NMB_QUERY , /* nmbd type code. */
NMB_NAME_QUERY_OPCODE , /* opcode. */
ttl , /* ttl. */
prdata , /* data to send. */
reply_data_len ) ; /* data length. */
if ( ( prdata ! = rdata ) & & ( prdata ! = NULL ) )
free ( prdata ) ;
}