#include "includes.h"
#define WINS_LIST "wins.dat"
#define WINS_VERSION 1
#define WINSDB_VERSION 1
TDB_CONTEXT *wins_tdb;
static void wins_delete_all_tmp_in_memory_records(void)
{
struct name_record *nr = NULL;
struct name_record *nrnext = NULL;
for( nr = wins_server_subnet->namelist; nr; nr = nrnext) {
nrnext = nr->next;
DLIST_REMOVE(wins_server_subnet->namelist, nr);
SAFE_FREE(nr->data.ip);
SAFE_FREE(nr);
}
}
static struct name_record *wins_record_to_name_record(TDB_DATA key, TDB_DATA data)
{
struct name_record *namerec = NULL;
uint16 nb_flags;
unsigned char nr_src;
uint32 death_time, refresh_time;
uint32 id_low, id_high;
uint32 saddr;
uint32 wins_flags;
uint32 num_ips;
size_t len;
int i;
if (data.dptr == NULL || data.dsize == 0) {
return NULL;
}
if (data.dsize < 2 + 1 + (7*4) + 4) {
return NULL;
}
len = tdb_unpack(data.dptr, data.dsize,
"wbddddddd",
&nb_flags,
&nr_src,
&death_time,
&refresh_time,
&id_low,
&id_high,
&saddr,
&wins_flags,
&num_ips );
namerec = SMB_MALLOC_P(struct name_record);
if (!namerec) {
return NULL;
}
ZERO_STRUCTP(namerec);
namerec->data.ip = SMB_MALLOC_ARRAY(struct in_addr, num_ips);
if (!namerec->data.ip) {
SAFE_FREE(namerec);
return NULL;
}
namerec->subnet = wins_server_subnet;
push_ascii_nstring(namerec->name.name, key.dptr);
namerec->name.name_type = key.dptr[sizeof(unstring)];
push_ascii(namerec->name.scope, global_scope(), 64, STR_TERMINATE);
for( i = strlen( namerec->name.name ); i < sizeof( namerec->name.name ); i++ ) {
namerec->name.name[i] = '\0';
}
for( i = strlen( namerec->name.scope ); i < sizeof( namerec->name.scope ); i++ ) {
namerec->name.scope[i] = '\0';
}
namerec->data.nb_flags = nb_flags;
namerec->data.source = (enum name_source)nr_src;
namerec->data.death_time = (time_t)death_time;
namerec->data.refresh_time = (time_t)refresh_time;
namerec->data.id = id_low;
#if defined(HAVE_LONGLONG)
namerec->data.id |= ((SMB_BIG_UINT)id_high << 32);
#endif
namerec->data.wins_ip.s_addr = saddr;
namerec->data.wins_flags = wins_flags,
namerec->data.num_ips = num_ips;
for (i = 0; i < num_ips; i++) {
namerec->data.ip[i].s_addr = IVAL(data.dptr, len + (i*4));
}
return namerec;
}
static TDB_DATA name_record_to_wins_record(const struct name_record *namerec)
{
TDB_DATA data;
size_t len = 0;
int i;
uint32 id_low = (namerec->data.id & 0xFFFFFFFF);
#if defined(HAVE_LONGLONG)
uint32 id_high = (namerec->data.id >> 32) & 0xFFFFFFFF;
#else
uint32 id_high = 0;
#endif
ZERO_STRUCT(data);
len = (2 + 1 + (7*4));
len += (namerec->data.num_ips * 4);
data.dptr = (char *)SMB_MALLOC(len);
if (!data.dptr) {
return data;
}
data.dsize = len;
len = tdb_pack(data.dptr, data.dsize, "wbddddddd",
namerec->data.nb_flags,
(unsigned char)namerec->data.source,
(uint32)namerec->data.death_time,
(uint32)namerec->data.refresh_time,
id_low,
id_high,
(uint32)namerec->data.wins_ip.s_addr,
(uint32)namerec->data.wins_flags,
(uint32)namerec->data.num_ips );
for (i = 0; i < namerec->data.num_ips; i++) {
SIVAL(data.dptr, len + (i*4), namerec->data.ip[i].s_addr);
}
return data;
}
static TDB_DATA name_to_key(const struct nmb_name *nmbname)
{
static char keydata[sizeof(unstring) + 1];
TDB_DATA key;
memset(keydata, '\0', sizeof(keydata));
pull_ascii_nstring(keydata, sizeof(unstring), nmbname->name);
strupper_m(keydata);
keydata[sizeof(unstring)] = nmbname->name_type;
key.dptr = keydata;
key.dsize = sizeof(keydata);
return key;
}
struct name_record *find_name_on_wins_subnet(const struct nmb_name *nmbname, BOOL self_only)
{
TDB_DATA data, key;
struct name_record *nr = NULL;
struct name_record *namerec = NULL;
if (!wins_tdb) {
return NULL;
}
key = name_to_key(nmbname);
data = tdb_fetch(wins_tdb, key);
if (data.dsize == 0) {
return NULL;
}
namerec = wins_record_to_name_record(key, data);
SAFE_FREE( data.dptr );
if (!namerec) {
return NULL;
}
if( self_only && (namerec->data.source != SELF_NAME) && (namerec->data.source != PERMANENT_NAME) ) {
DEBUG( 9, ( "find_name_on_wins_subnet: self name %s NOT FOUND\n", nmb_namestr(nmbname) ) );
SAFE_FREE(namerec->data.ip);
SAFE_FREE(namerec);
return NULL;
}
for( nr = wins_server_subnet->namelist; nr; nr = nr->next) {
if (memcmp(nmbname->name, nr->name.name, 16) == 0) {
DLIST_REMOVE(wins_server_subnet->namelist, nr);
SAFE_FREE(nr->data.ip);
SAFE_FREE(nr);
break;
}
}
DLIST_ADD(wins_server_subnet->namelist, namerec);
return namerec;
}
static BOOL store_or_replace_wins_namerec(const struct name_record *namerec, int tdb_flag)
{
TDB_DATA key, data;
int ret;
if (!wins_tdb) {
return False;
}
key = name_to_key(&namerec->name);
data = name_record_to_wins_record(namerec);
if (data.dptr == NULL) {
return False;
}
ret = tdb_store(wins_tdb, key, data, tdb_flag);
SAFE_FREE(data.dptr);
return (ret == 0) ? True : False;
}
BOOL wins_store_changed_namerec(const struct name_record *namerec)
{
return store_or_replace_wins_namerec(namerec, TDB_REPLACE);
}
BOOL add_name_to_wins_subnet(const struct name_record *namerec)
{
return store_or_replace_wins_namerec(namerec, TDB_INSERT);
}
BOOL remove_name_from_wins_namelist(struct name_record *namerec)
{
TDB_DATA key;
int ret;
if (!wins_tdb) {
return False;
}
key = name_to_key(&namerec->name);
ret = tdb_delete(wins_tdb, key);
DLIST_REMOVE(wins_server_subnet->namelist, namerec);
return (ret == 0) ? True : False;
}
static int traverse_fn(TDB_CONTEXT *tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
{
struct name_record *namerec = NULL;
XFILE *fp = (XFILE *)state;
if (kbuf.dsize != sizeof(unstring) + 1) {
return 0;
}
namerec = wins_record_to_name_record(kbuf, dbuf);
if (!namerec) {
return 0;
}
dump_name_record(namerec, fp);
SAFE_FREE(namerec->data.ip);
SAFE_FREE(namerec);
return 0;
}
void dump_wins_subnet_namelist(XFILE *fp)
{
tdb_traverse(wins_tdb, traverse_fn, (void *)fp);
}
static void update_wins_owner(struct name_record *namerec, struct in_addr wins_ip)
{
namerec->data.wins_ip=wins_ip;
}
static void update_wins_flag(struct name_record *namerec, int flags)
{
namerec->data.wins_flags=0x0;
if (namerec->data.nb_flags & NB_GROUP) {
if (namerec->name.name_type==0x1C) {
namerec->data.wins_flags|=WINS_SGROUP;
} else {
if (namerec->data.num_ips>1) {
namerec->data.wins_flags|=WINS_SGROUP;
} else {
namerec->data.wins_flags|=WINS_NGROUP;
}
}
} else {
if (namerec->data.num_ips>1) {
namerec->data.wins_flags|=WINS_MHOMED;
} else {
namerec->data.wins_flags|=WINS_UNIQUE;
}
}
namerec->data.wins_flags|=namerec->data.nb_flags&NB_NODETYPEMASK;
if (namerec->data.death_time == PERMANENT_TTL) {
namerec->data.wins_flags|=WINS_STATIC;
}
namerec->data.wins_flags|=flags;
DEBUG(8,("update_wins_flag: nbflags: 0x%x, ttl: 0x%d, flags: 0x%x, winsflags: 0x%x\n",
namerec->data.nb_flags, (int)namerec->data.death_time, flags, namerec->data.wins_flags));
}
static void get_global_id_and_update(SMB_BIG_UINT *current_id, BOOL update)
{
static SMB_BIG_UINT general_id = 1;
DEBUG(5,("get_global_id_and_update: updating version ID: %d\n", (int)general_id));
*current_id = general_id;
if (update) {
general_id++;
}
}
static void wins_hook(const char *operation, struct name_record *namerec, int ttl)
{
pstring command;
char *cmd = lp_wins_hook();
char *p, *namestr;
int i;
wins_store_changed_namerec(namerec);
if (!cmd || !*cmd) {
return;
}
for (p=namerec->name.name; *p; p++) {
if (!(isalnum((int)*p) || strchr_m("._-",*p))) {
DEBUG(3,("not calling wins hook for invalid name %s\n", nmb_namestr(&namerec->name)));
return;
}
}
namestr = nmb_namestr(&namerec->name);
if ((p = strchr(namestr, '<'))) {
*p = 0;
}
p = command;
p += slprintf(p, sizeof(command)-1, "%s %s %s %02x %d",
cmd,
operation,
namestr,
namerec->name.name_type,
ttl);
for (i=0;i<namerec->data.num_ips;i++) {
p += slprintf(p, sizeof(command) - (p-command) -1, " %s", inet_ntoa(namerec->data.ip[i]));
}
DEBUG(3,("calling wins hook for %s\n", nmb_namestr(&namerec->name)));
smbrun(command, NULL);
}
BOOL packet_is_for_wins_server(struct packet_struct *packet)
{
struct nmb_packet *nmb = &packet->packet.nmb;
if((wins_server_subnet == NULL) || (nmb->header.nm_flags.bcast == True)) {
DEBUG(10, ("packet_is_for_wins_server: failing WINS test #1.\n"));
return False;
}
if (nmb->question.question_type != QUESTION_TYPE_NB_QUERY) {
return False;
}
switch(nmb->header.opcode) {
case NMB_WACK_OPCODE:
DEBUG(10, ("packet_is_for_wins_server: failing WINS test #2 (WACK).\n"));
return False;
case NMB_NAME_REG_OPCODE:
case NMB_NAME_MULTIHOMED_REG_OPCODE:
case NMB_NAME_REFRESH_OPCODE_8:
case NMB_NAME_REFRESH_OPCODE_9:
if(nmb->header.response) {
DEBUG(10, ("packet_is_for_wins_server: failing WINS test #3 (response = 1).\n"));
return False;
}
break;
case NMB_NAME_RELEASE_OPCODE:
if(nmb->header.response) {
DEBUG(10, ("packet_is_for_wins_server: failing WINS test #4 (response = 1).\n"));
return False;
}
break;
case NMB_NAME_QUERY_OPCODE:
if(!nmb->header.response && !nmb->header.nm_flags.recursion_desired) {
DEBUG(10, ("packet_is_for_wins_server: failing WINS test #5 (response = 1).\n"));
return False;
}
break;
}
return True;
}
static int get_ttl_from_packet(struct nmb_packet *nmb)
{
int ttl = nmb->additional->ttl;
if (ttl < lp_min_wins_ttl()) {
ttl = lp_min_wins_ttl();
}
if (ttl > lp_max_wins_ttl()) {
ttl = lp_max_wins_ttl();
}
return ttl;
}
BOOL initialise_wins(void)
{
time_t time_now = time(NULL);
XFILE *fp;
pstring line;
if(!lp_we_are_a_wins_server()) {
return True;
}
wins_tdb = tdb_open_log(lock_path("wins.tdb"), 0, TDB_DEFAULT|TDB_CLEAR_IF_FIRST, O_CREAT|O_RDWR, 0600);
if (!wins_tdb) {
DEBUG(0,("initialise_wins: failed to open wins.tdb. Error was %s\n",
strerror(errno) ));
return False;
}
tdb_store_int32(wins_tdb, "WINSDB_VERSION", WINSDB_VERSION);
add_samba_names_to_subnet(wins_server_subnet);
if((fp = x_fopen(lock_path(WINS_LIST),O_RDONLY,0)) == NULL) {
DEBUG(2,("initialise_wins: Can't open wins database file %s. Error was %s\n",
WINS_LIST, strerror(errno) ));
return True;
}
while (!x_feof(fp)) {
pstring name_str, ip_str, ttl_str, nb_flags_str;
unsigned int num_ips;
pstring name;
struct in_addr *ip_list;
int type = 0;
int nb_flags;
int ttl;
const char *ptr;
char *p;
BOOL got_token;
BOOL was_ip;
int i;
unsigned int hash;
int version;
if (!fgets_slash(line,sizeof(pstring),fp))
continue;
if (*line == '#')
continue;
if (strncmp(line,"VERSION ", 8) == 0) {
if (sscanf(line,"VERSION %d %u", &version, &hash) != 2 ||
version != WINS_VERSION) {
DEBUG(0,("Discarding invalid wins.dat file [%s]\n",line));
x_fclose(fp);
return True;
}
continue;
}
ptr = line;
if (!next_token(&ptr,name_str,NULL,sizeof(name_str))) {
DEBUG(0,("initialise_wins: Failed to parse name when parsing line %s\n", line ));
continue;
}
if (!next_token(&ptr,ttl_str,NULL,sizeof(ttl_str))) {
DEBUG(0,("initialise_wins: Failed to parse time to live when parsing line %s\n", line ));
continue;
}
num_ips = 0;
do {
got_token = next_token(&ptr,ip_str,NULL,sizeof(ip_str));
was_ip = False;
if(got_token && strchr(ip_str, '.')) {
num_ips++;
was_ip = True;
}
} while( got_token && was_ip);
if(num_ips == 0) {
DEBUG(0,("initialise_wins: Missing IP address when parsing line %s\n", line ));
continue;
}
if(!got_token) {
DEBUG(0,("initialise_wins: Missing nb_flags when parsing line %s\n", line ));
continue;
}
if((ip_list = SMB_MALLOC_ARRAY( struct in_addr, num_ips)) == NULL) {
DEBUG(0,("initialise_wins: Malloc fail !\n"));
x_fclose(fp);
return False;
}
ptr = line;
next_token(&ptr,name_str,NULL,sizeof(name_str));
next_token(&ptr,ttl_str,NULL,sizeof(ttl_str));
for(i = 0; i < num_ips; i++) {
next_token(&ptr, ip_str, NULL, sizeof(ip_str));
ip_list[i] = *interpret_addr2(ip_str);
}
next_token(&ptr,nb_flags_str,NULL, sizeof(nb_flags_str));
if(nb_flags_str[strlen(nb_flags_str)-1] == 'S') {
DEBUG(5,("initialise_wins: Ignoring SELF name %s\n", line));
SAFE_FREE(ip_list);
continue;
}
if(nb_flags_str[strlen(nb_flags_str)-1] == 'R') {
nb_flags_str[strlen(nb_flags_str)-1] = '\0';
}
pstrcpy(name,name_str);
if((p = strchr(name,'#')) != NULL) {
*p = 0;
sscanf(p+1,"%x",&type);
}
sscanf(nb_flags_str,"%x",&nb_flags);
sscanf(ttl_str,"%d",&ttl);
if ((ttl - 60) > time_now || ttl == PERMANENT_TTL) {
if(ttl != PERMANENT_TTL) {
ttl -= time_now;
}
DEBUG( 4, ("initialise_wins: add name: %s#%02x ttl = %d first IP %s flags = %2x\n",
name, type, ttl, inet_ntoa(ip_list[0]), nb_flags));
(void)add_name_to_subnet( wins_server_subnet, name, type, nb_flags,
ttl, REGISTER_NAME, num_ips, ip_list );
} else {
DEBUG(4, ("initialise_wins: not adding name (ttl problem) "
"%s#%02x ttl = %d first IP %s flags = %2x\n",
name, type, ttl, inet_ntoa(ip_list[0]), nb_flags));
}
SAFE_FREE(ip_list);
}
x_fclose(fp);
return True;
}
static void send_wins_wack_response(int ttl, struct packet_struct *p)
{
struct nmb_packet *nmb = &p->packet.nmb;
unsigned char rdata[2];
rdata[0] = rdata[1] = 0;
rdata[0] = (nmb->header.opcode & 0xF) << 3;
if (nmb->header.nm_flags.authoritative && nmb->header.response) {
rdata[0] |= 0x4;
}
if (nmb->header.nm_flags.trunc) {
rdata[0] |= 0x2;
}
if (nmb->header.nm_flags.recursion_desired) {
rdata[0] |= 0x1;
}
if (nmb->header.nm_flags.recursion_available && nmb->header.response) {
rdata[1] |= 0x80;
}
if (nmb->header.nm_flags.bcast) {
rdata[1] |= 0x10;
}
reply_netbios_packet(p,
0,
NMB_WAIT_ACK,
NMB_WACK_OPCODE,
ttl,
(char *)rdata,
2);
}
static void send_wins_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,
rcode,
WINS_REG,
NMB_NAME_REG_OPCODE,
ttl,
rdata,
6);
}
void wins_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;
uint16 nb_flags = get_nb_flags(nmb->additional->rdata);
BOOL group = (nb_flags & NB_GROUP) ? True : False;
struct name_record *namerec = NULL;
int ttl = get_ttl_from_packet(nmb);
struct in_addr from_ip;
struct in_addr our_fake_ip = *interpret_addr2("0.0.0.0");
putip( (char *)&from_ip, &nmb->additional->rdata[2] );
if(bcast) {
if( DEBUGLVL( 0 ) ) {
dbgtext( "wins_process_name_refresh_request: " );
dbgtext( "Broadcast name refresh request received " );
dbgtext( "for name %s ", nmb_namestr(question) );
dbgtext( "from IP %s ", inet_ntoa(from_ip) );
dbgtext( "on subnet %s. ", subrec->subnet_name );
dbgtext( "Error - Broadcasts should not be sent " );
dbgtext( "to a WINS server\n" );
}
return;
}
if( DEBUGLVL( 3 ) ) {
dbgtext( "wins_process_name_refresh_request: " );
dbgtext( "Name refresh for name %s IP %s\n",
nmb_namestr(question), inet_ntoa(from_ip) );
}
namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME);
if(namerec == NULL) {
if( DEBUGLVL( 3 ) ) {
dbgtext( "wins_process_name_refresh_request: " );
dbgtext( "Name refresh for name %s ",
nmb_namestr( question ) );
dbgtext( "and the name does not exist. Treating " );
dbgtext( "as registration.\n" );
}
wins_process_name_registration_request(subrec,p);
return;
}
if (namerec != NULL && !WINS_STATE_ACTIVE(namerec)) {
if( DEBUGLVL( 5 ) ) {
dbgtext( "wins_process_name_refresh_request: " );
dbgtext( "Name (%s) in WINS ", nmb_namestr(question) );
dbgtext( "was not active - removing it.\n" );
}
remove_name_from_namelist( subrec, namerec );
namerec = NULL;
wins_process_name_registration_request( subrec, p );
return;
}
if( (namerec != NULL) &&
( (group && !NAME_GROUP(namerec))
|| (!group && NAME_GROUP(namerec)) ) ) {
if( DEBUGLVL( 3 ) ) {
dbgtext( "wins_process_name_refresh_request: " );
dbgtext( "Name %s ", nmb_namestr(question) );
dbgtext( "group bit = %s does not match ",
group ? "True" : "False" );
dbgtext( "group bit in WINS for this name.\n" );
}
send_wins_name_registration_response(RFS_ERR, 0, p);
return;
}
if( (!group || (group && (question->name_type == 0x1c)))
&& find_ip_in_name_record(namerec, from_ip) ) {
update_name_ttl(namerec, ttl);
if (!ip_equal(namerec->data.wins_ip, our_fake_ip)) {
update_wins_owner(namerec, our_fake_ip);
get_global_id_and_update(&namerec->data.id, True);
}
send_wins_name_registration_response(0, ttl, p);
wins_hook("refresh", namerec, ttl);
return;
} else if((group && (question->name_type == 0x1c))) {
if( DEBUGLVL( 3 ) ) {
dbgtext( "wins_process_name_refresh_request: " );
dbgtext( "Name refresh for name %s, ",
nmb_namestr(question) );
dbgtext( "but IP address %s ", inet_ntoa(from_ip) );
dbgtext( "is not yet associated with " );
dbgtext( "that name. Treating as registration.\n" );
}
wins_process_name_registration_request(subrec,p);
return;
} else if(group) {
update_name_ttl(namerec, ttl);
wins_hook("refresh", namerec, ttl);
send_wins_name_registration_response(0, ttl, p);
return;
} else if(!group && (question->name_type == 0x1d)) {
send_wins_name_registration_response(0, ttl, p);
return;
} else {
if( DEBUGLVL( 3 ) ) {
dbgtext( "wins_process_name_refresh_request: " );
dbgtext( "Name refresh for name %s with IP %s ",
nmb_namestr(question), inet_ntoa(from_ip) );
dbgtext( "and is IP is not known to the name.\n" );
}
send_wins_name_registration_response(RFS_ERR, 0, p);
return;
}
}
static void wins_register_query_success(struct subnet_record *subrec,
struct userdata_struct *userdata,
struct nmb_name *question_name,
struct in_addr ip,
struct res_rec *answers)
{
struct packet_struct *orig_reg_packet;
memcpy((char *)&orig_reg_packet, userdata->data, sizeof(struct packet_struct *));
DEBUG(3,("wins_register_query_success: Original client at IP %s still wants the \
name %s. Rejecting registration request.\n", inet_ntoa(ip), nmb_namestr(question_name) ));
send_wins_name_registration_response(RFS_ERR, 0, orig_reg_packet);
orig_reg_packet->locked = False;
free_packet(orig_reg_packet);
}
static void wins_register_query_fail(struct subnet_record *subrec,
struct response_record *rrec,
struct nmb_name *question_name,
int rcode)
{
struct userdata_struct *userdata = rrec->userdata;
struct packet_struct *orig_reg_packet;
struct name_record *namerec = NULL;
memcpy((char *)&orig_reg_packet, userdata->data, sizeof(struct packet_struct *));
namerec = find_name_on_subnet(subrec, question_name, FIND_ANY_NAME);
if ((namerec != NULL) && (namerec->data.source == REGISTER_NAME) &&
ip_equal(rrec->packet->ip, *namerec->data.ip)) {
remove_name_from_namelist( subrec, namerec);
namerec = NULL;
}
if(namerec == NULL) {
wins_process_name_registration_request(subrec, orig_reg_packet);
} else {
DEBUG(2,("wins_register_query_fail: The state of the WINS database changed between "
"querying for name %s in order to replace it and this reply.\n",
nmb_namestr(question_name) ));
}
orig_reg_packet->locked = False;
free_packet(orig_reg_packet);
}
void wins_process_name_registration_request(struct subnet_record *subrec,
struct packet_struct *p)
{
unstring name;
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);
int ttl = get_ttl_from_packet(nmb);
struct name_record *namerec = NULL;
struct in_addr from_ip;
BOOL registering_group_name = (nb_flags & NB_GROUP) ? True : False;
struct in_addr our_fake_ip = *interpret_addr2("0.0.0.0");
putip((char *)&from_ip,&nmb->additional->rdata[2]);
if(bcast) {
DEBUG(0,("wins_process_name_registration_request: broadcast name registration request \
received for name %s from IP %s on subnet %s. Error - should not be sent to WINS server\n",
nmb_namestr(question), inet_ntoa(from_ip), subrec->subnet_name));
return;
}
DEBUG(3,("wins_process_name_registration_request: %s name registration for name %s \
IP %s\n", registering_group_name ? "Group" : "Unique", nmb_namestr(question), inet_ntoa(from_ip) ));
namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME);
if ( (namerec != NULL) && !WINS_STATE_ACTIVE(namerec)) {
DEBUG(5,("wins_process_name_registration_request: Name (%s) in WINS was \
not active - removing it.\n", nmb_namestr(question) ));
remove_name_from_namelist( subrec, namerec );
namerec = NULL;
}
if( (namerec != NULL) && ( (namerec->data.source == DNS_NAME) || (namerec->data.source == DNSFAIL_NAME) ) ) {
DEBUG(5,("wins_process_name_registration_request: Name (%s) in WINS was \
a dns lookup - removing it.\n", nmb_namestr(question) ));
remove_name_from_namelist( subrec, namerec );
namerec = NULL;
}
if((namerec != NULL) && (namerec->data.source != REGISTER_NAME)) {
DEBUG( 3, ( "wins_process_name_registration_request: Attempt \
to register name %s. Name already exists in WINS with source type %d.\n",
nmb_namestr(question), namerec->data.source ));
send_wins_name_registration_response(RFS_ERR, 0, p);
return;
}
if(registering_group_name && (question->name_type != 0x1c)) {
from_ip = *interpret_addr2("255.255.255.255");
}
if(!registering_group_name && (question->name_type == 0x1d)) {
DEBUG(3,("wins_process_name_registration_request: Ignoring request \
to register name %s from IP %s.\n", nmb_namestr(question), inet_ntoa(p->ip) ));
send_wins_name_registration_response(0, ttl, p);
return;
}
if((namerec != NULL) && NAME_GROUP(namerec)) {
if(registering_group_name) {
DEBUG(3,("wins_process_name_registration_request: Adding IP %s to group name %s.\n",
inet_ntoa(from_ip), nmb_namestr(question) ));
if(!find_ip_in_name_record(namerec, from_ip)) {
add_ip_to_name_record(namerec, from_ip);
get_global_id_and_update(&namerec->data.id, True);
update_wins_owner(namerec, our_fake_ip);
}
update_name_ttl(namerec, ttl);
wins_hook("refresh", namerec, ttl);
send_wins_name_registration_response(0, ttl, p);
return;
} else {
DEBUG(3,("wins_process_name_registration_request: Attempt to register name %s. Name \
already exists in WINS as a GROUP name.\n", nmb_namestr(question) ));
send_wins_name_registration_response(RFS_ERR, 0, p);
return;
}
}
if ( namerec != NULL ) {
pull_ascii_nstring(name, sizeof(name), namerec->name.name);
if( is_myname(name) ) {
if(!ismyip(from_ip)) {
DEBUG(3,("wins_process_name_registration_request: Attempt to register name %s. Name \
is one of our (WINS server) names. Denying registration.\n", nmb_namestr(question) ));
send_wins_name_registration_response(RFS_ERR, 0, p);
return;
} else {
update_name_ttl(namerec, ttl);
wins_hook("refresh", namerec, ttl);
send_wins_name_registration_response(0, ttl, p);
return;
}
}
} else {
name[0] = '\0';
}
if( !registering_group_name
&& (namerec != NULL)
&& (namerec->data.num_ips == 1)
&& ip_equal( namerec->data.ip[0], from_ip )
&& ip_equal(namerec->data.wins_ip, our_fake_ip) ) {
update_name_ttl( namerec, ttl );
wins_hook("refresh", namerec, ttl);
send_wins_name_registration_response( 0, ttl, p );
return;
}
if( namerec != NULL ) {
long *ud[(sizeof(struct userdata_struct) + sizeof(struct packet_struct *))/sizeof(long *) + 1];
struct userdata_struct *userdata = (struct userdata_struct *)ud;
send_wins_wack_response(60, p);
p->locked = True;
userdata = (struct userdata_struct *)ud;
userdata->copy_fn = NULL;
userdata->free_fn = NULL;
userdata->userdata_len = sizeof(struct packet_struct *);
memcpy(userdata->data, (char *)&p, sizeof(struct packet_struct *) );
pull_ascii_nstring(name, sizeof(name), question->name);
query_name_from_wins_server( *namerec->data.ip,
name,
question->name_type,
wins_register_query_success,
wins_register_query_fail,
userdata );
return;
}
pull_ascii_nstring(name, sizeof(name), question->name);
add_name_to_subnet( subrec, name, question->name_type,
nb_flags, ttl, REGISTER_NAME, 1, &from_ip);
if ((namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME))) {
get_global_id_and_update(&namerec->data.id, True);
update_wins_owner(namerec, our_fake_ip);
update_wins_flag(namerec, WINS_ACTIVE);
wins_hook("add", namerec, ttl);
}
send_wins_name_registration_response(0, ttl, p);
}
static void wins_multihomed_register_query_success(struct subnet_record *subrec,
struct userdata_struct *userdata,
struct nmb_name *question_name,
struct in_addr ip,
struct res_rec *answers)
{
struct packet_struct *orig_reg_packet;
struct nmb_packet *nmb;
struct name_record *namerec = NULL;
struct in_addr from_ip;
int ttl;
struct in_addr our_fake_ip = *interpret_addr2("0.0.0.0");
memcpy((char *)&orig_reg_packet, userdata->data, sizeof(struct packet_struct *));
nmb = &orig_reg_packet->packet.nmb;
putip((char *)&from_ip,&nmb->additional->rdata[2]);
ttl = get_ttl_from_packet(nmb);
namerec = find_name_on_subnet(subrec, question_name, FIND_ANY_NAME);
if( (namerec == NULL) || (namerec->data.source != REGISTER_NAME) || !WINS_STATE_ACTIVE(namerec) ) {
DEBUG(3,("wins_multihomed_register_query_success: name %s is not in the correct state to add \
a subsequent IP address.\n", nmb_namestr(question_name) ));
send_wins_name_registration_response(RFS_ERR, 0, orig_reg_packet);
orig_reg_packet->locked = False;
free_packet(orig_reg_packet);
return;
}
if(!find_ip_in_name_record(namerec, from_ip)) {
add_ip_to_name_record(namerec, from_ip);
}
get_global_id_and_update(&namerec->data.id, True);
update_wins_owner(namerec, our_fake_ip);
update_wins_flag(namerec, WINS_ACTIVE);
update_name_ttl(namerec, ttl);
wins_hook("add", namerec, ttl);
send_wins_name_registration_response(0, ttl, orig_reg_packet);
orig_reg_packet->locked = False;
free_packet(orig_reg_packet);
}
static void wins_multihomed_register_query_fail(struct subnet_record *subrec,
struct response_record *rrec,
struct nmb_name *question_name,
int rcode)
{
struct userdata_struct *userdata = rrec->userdata;
struct packet_struct *orig_reg_packet;
memcpy((char *)&orig_reg_packet, userdata->data, sizeof(struct packet_struct *));
DEBUG(3,("wins_multihomed_register_query_fail: Registering machine at IP %s failed to answer \
query successfully for name %s.\n", inet_ntoa(orig_reg_packet->ip), nmb_namestr(question_name) ));
send_wins_name_registration_response(RFS_ERR, 0, orig_reg_packet);
orig_reg_packet->locked = False;
free_packet(orig_reg_packet);
return;
}
void wins_process_multihomed_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);
int ttl = get_ttl_from_packet(nmb);
struct name_record *namerec = NULL;
struct in_addr from_ip;
BOOL group = (nb_flags & NB_GROUP) ? True : False;
struct in_addr our_fake_ip = *interpret_addr2("0.0.0.0");
unstring qname;
putip((char *)&from_ip,&nmb->additional->rdata[2]);
if(bcast) {
DEBUG(0,("wins_process_multihomed_name_registration_request: broadcast name registration request \
received for name %s from IP %s on subnet %s. Error - should not be sent to WINS server\n",
nmb_namestr(question), inet_ntoa(from_ip), subrec->subnet_name));
return;
}
if(group) {
DEBUG(0,("wins_process_multihomed_name_registration_request: group name registration request \
received for name %s from IP %s on subnet %s. Errror - group names should not be multihomed.\n",
nmb_namestr(question), inet_ntoa(from_ip), subrec->subnet_name));
return;
}
DEBUG(3,("wins_process_multihomed_name_registration_request: name registration for name %s \
IP %s\n", nmb_namestr(question), inet_ntoa(from_ip) ));
if(question->name_type == 0x1d) {
DEBUG(3,("wins_process_multihomed_name_registration_request: Ignoring request \
to register name %s from IP %s.", nmb_namestr(question), inet_ntoa(p->ip) ));
send_wins_name_registration_response(0, ttl, p);
return;
}
namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME);
if ((namerec != NULL) && !WINS_STATE_ACTIVE(namerec)) {
DEBUG(5,("wins_process_multihomed_name_registration_request: Name (%s) in WINS was not active - removing it.\n", nmb_namestr(question)));
remove_name_from_namelist(subrec, namerec);
namerec = NULL;
}
if( (namerec != NULL) && ( (namerec->data.source == DNS_NAME) || (namerec->data.source == DNSFAIL_NAME) ) ) {
DEBUG(5,("wins_process_multihomed_name_registration_request: Name (%s) in WINS was a dns lookup \
- removing it.\n", nmb_namestr(question) ));
remove_name_from_namelist( subrec, namerec);
namerec = NULL;
}
if( (namerec != NULL) && (namerec->data.source != REGISTER_NAME) ) {
DEBUG( 3, ( "wins_process_multihomed_name_registration_request: Attempt \
to register name %s. Name already exists in WINS with source type %d.\n",
nmb_namestr(question), namerec->data.source ));
send_wins_name_registration_response(RFS_ERR, 0, p);
return;
}
if((namerec != NULL) && NAME_GROUP(namerec) && WINS_STATE_ACTIVE(namerec)) {
DEBUG(3,("wins_process_multihomed_name_registration_request: Attempt to register name %s. Name \
already exists in WINS as a GROUP name.\n", nmb_namestr(question) ));
send_wins_name_registration_response(RFS_ERR, 0, p);
return;
}
if((namerec != NULL) && (is_myname(namerec->name.name)) ) {
if(!ismyip(from_ip)) {
DEBUG(3,("wins_process_multihomed_name_registration_request: Attempt to register name %s. Name \
is one of our (WINS server) names. Denying registration.\n", nmb_namestr(question) ));
send_wins_name_registration_response(RFS_ERR, 0, p);
return;
} else {
update_name_ttl(namerec, ttl);
if(!find_ip_in_name_record(namerec, from_ip)) {
get_global_id_and_update(&namerec->data.id, True);
update_wins_owner(namerec, our_fake_ip);
update_wins_flag(namerec, WINS_ACTIVE);
add_ip_to_name_record(namerec, from_ip);
}
wins_hook("refresh", namerec, ttl);
send_wins_name_registration_response(0, ttl, p);
return;
}
}
if((namerec != NULL) && find_ip_in_name_record(namerec, from_ip) && WINS_STATE_ACTIVE(namerec)) {
update_name_ttl(namerec, ttl);
if (!ip_equal(namerec->data.wins_ip, our_fake_ip)) {
get_global_id_and_update(&namerec->data.id, True);
update_wins_owner(namerec, our_fake_ip);
update_wins_flag(namerec, WINS_ACTIVE);
}
wins_hook("refresh", namerec, ttl);
send_wins_name_registration_response(0, ttl, p);
return;
}
if(namerec != NULL) {
long *ud[(sizeof(struct userdata_struct) + sizeof(struct packet_struct *))/sizeof(long *) + 1];
struct userdata_struct *userdata = (struct userdata_struct *)ud;
send_wins_wack_response(60, p);
p->locked = True;
userdata = (struct userdata_struct *)ud;
userdata->copy_fn = NULL;
userdata->free_fn = NULL;
userdata->userdata_len = sizeof(struct packet_struct *);
memcpy(userdata->data, (char *)&p, sizeof(struct packet_struct *) );
pull_ascii_nstring( qname, sizeof(qname), question->name);
query_name_from_wins_server( namerec->data.ip[0],
qname,
question->name_type,
wins_multihomed_register_query_success,
wins_multihomed_register_query_fail,
userdata );
return;
}
pull_ascii_nstring( qname, sizeof(qname), question->name);
add_name_to_subnet( subrec, qname, question->name_type,
nb_flags, ttl, REGISTER_NAME, 1, &from_ip);
if ((namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME))) {
get_global_id_and_update(&namerec->data.id, True);
update_wins_owner(namerec, our_fake_ip);
update_wins_flag(namerec, WINS_ACTIVE);
wins_hook("add", namerec, ttl);
}
send_wins_name_registration_response(0, ttl, p);
}
static int fetch_1b_traverse_fn(TDB_CONTEXT *tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
{
struct name_record *namerec = NULL;
if (kbuf.dsize != sizeof(unstring) + 1) {
return 0;
}
if (kbuf.dptr[sizeof(unstring)] != 0x1b) {
return 0;
}
namerec = wins_record_to_name_record(kbuf, dbuf);
if (!namerec) {
return 0;
}
DLIST_ADD(wins_server_subnet->namelist, namerec);
return 0;
}
void fetch_all_active_wins_1b_names(void)
{
tdb_traverse(wins_tdb, fetch_1b_traverse_fn, NULL);
}
static void process_wins_dmb_query_request(struct subnet_record *subrec,
struct packet_struct *p)
{
struct name_record *namerec = NULL;
char *prdata;
int num_ips;
num_ips = 0;
wins_delete_all_tmp_in_memory_records();
fetch_all_active_wins_1b_names();
for( namerec = subrec->namelist; namerec; namerec = namerec->next ) {
if( WINS_STATE_ACTIVE(namerec) && namerec->name.name_type == 0x1b) {
num_ips += namerec->data.num_ips;
}
}
if(num_ips == 0) {
send_wins_name_query_response(NAM_ERR, p, NULL);
return;
}
if((prdata = (char *)SMB_MALLOC( num_ips * 6 )) == NULL) {
DEBUG(0,("process_wins_dmb_query_request: Malloc fail !.\n"));
return;
}
num_ips = 0;
for( namerec = subrec->namelist; namerec; namerec = namerec->next ) {
if( WINS_STATE_ACTIVE(namerec) && namerec->name.name_type == 0x1b) {
int i;
for(i = 0; i < namerec->data.num_ips; i++) {
set_nb_flags(&prdata[num_ips * 6],namerec->data.nb_flags);
putip((char *)&prdata[(num_ips * 6) + 2], &namerec->data.ip[i]);
num_ips++;
}
}
}
reply_netbios_packet(p,
0,
WINS_QUERY,
NMB_NAME_QUERY_OPCODE,
lp_min_wins_ttl(),
prdata,
num_ips*6);
SAFE_FREE(prdata);
}
void send_wins_name_query_response(int rcode, struct packet_struct *p,
struct name_record *namerec)
{
char rdata[6];
char *prdata = rdata;
int reply_data_len = 0;
int ttl = 0;
int i;
memset(rdata,'\0',6);
if(rcode == 0) {
ttl = (namerec->data.death_time != PERMANENT_TTL) ? namerec->data.death_time - p->timestamp : lp_max_wins_ttl();
if( namerec->data.num_ips == 1 ) {
prdata = rdata;
} else {
if((prdata = (char *)SMB_MALLOC( namerec->data.num_ips * 6 )) == NULL) {
DEBUG(0,("send_wins_name_query_response: malloc fail !\n"));
return;
}
}
for(i = 0; i < namerec->data.num_ips; i++) {
set_nb_flags(&prdata[i*6],namerec->data.nb_flags);
putip((char *)&prdata[2+(i*6)], &namerec->data.ip[i]);
}
sort_query_replies(prdata, i, p->ip);
reply_data_len = namerec->data.num_ips * 6;
}
reply_netbios_packet(p,
rcode,
WINS_QUERY,
NMB_NAME_QUERY_OPCODE,
ttl,
prdata,
reply_data_len);
if(prdata != rdata) {
SAFE_FREE(prdata);
}
}
void wins_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;
struct name_record *namerec = NULL;
unstring qname;
DEBUG(3,("wins_process_name_query: name query for name %s from IP %s\n",
nmb_namestr(question), inet_ntoa(p->ip) ));
pull_ascii_nstring(qname, sizeof(qname), question->name);
if(strequal( qname, "*") && (question->name_type == 0x1b)) {
process_wins_dmb_query_request( subrec, p);
return;
}
namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME);
if(namerec != NULL) {
if (!WINS_STATE_ACTIVE(namerec)) {
DEBUG(3,("wins_process_name_query: name query for name %s - name expired. Returning fail.\n",
nmb_namestr(question) ));
send_wins_name_query_response(NAM_ERR, p, namerec);
return;
}
if( namerec->data.source == DNSFAIL_NAME ) {
DEBUG(3,("wins_process_name_query: name query for name %s returning DNS fail.\n",
nmb_namestr(question) ));
send_wins_name_query_response(NAM_ERR, p, namerec);
return;
}
if( (namerec->data.death_time != PERMANENT_TTL) && (namerec->data.death_time < p->timestamp) ) {
DEBUG(3,("wins_process_name_query: name query for name %s - name expired. Returning fail.\n",
nmb_namestr(question) ));
send_wins_name_query_response(NAM_ERR, p, namerec);
return;
}
DEBUG(3,("wins_process_name_query: name query for name %s returning first IP %s.\n",
nmb_namestr(question), inet_ntoa(namerec->data.ip[0]) ));
send_wins_name_query_response(0, p, namerec);
return;
}
if(lp_dns_proxy() && ((question->name_type == 0x20) || question->name_type == 0)) {
DEBUG(3,("wins_process_name_query: name query for name %s not found - doing dns lookup.\n",
nmb_namestr(question) ));
queue_dns_query(p, question);
return;
}
send_wins_name_query_response(NAM_ERR, p, NULL);
}
static void send_wins_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,
rcode,
NMB_REL,
NMB_NAME_RELEASE_OPCODE,
0,
rdata,
6);
}
void wins_process_name_release_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);
struct name_record *namerec = NULL;
struct in_addr from_ip;
BOOL releasing_group_name = (nb_flags & NB_GROUP) ? True : False;;
putip((char *)&from_ip,&nmb->additional->rdata[2]);
if(bcast) {
DEBUG(0,("wins_process_name_release_request: broadcast name registration request \
received for name %s from IP %s on subnet %s. Error - should not be sent to WINS server\n",
nmb_namestr(question), inet_ntoa(from_ip), subrec->subnet_name));
return;
}
DEBUG(3,("wins_process_name_release_request: %s name release for name %s \
IP %s\n", releasing_group_name ? "Group" : "Unique", nmb_namestr(question), inet_ntoa(from_ip) ));
if(!releasing_group_name && (question->name_type == 0x1d)) {
DEBUG(3,("wins_process_name_release_request: Ignoring request \
to release name %s from IP %s.", nmb_namestr(question), inet_ntoa(p->ip) ));
send_wins_name_release_response(0, p);
return;
}
namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME);
if( (namerec == NULL) || ((namerec != NULL) && (namerec->data.source != REGISTER_NAME)) ) {
send_wins_name_release_response(NAM_ERR, p);
return;
}
if(releasing_group_name && (question->name_type != 0x1c)) {
send_wins_name_release_response(0, p);
return;
}
if(!find_ip_in_name_record(namerec, from_ip)) {
DEBUG(3,("wins_process_name_release_request: Refusing request to \
release name %s as IP %s is not one of the known IP's for this name.\n",
nmb_namestr(question), inet_ntoa(from_ip) ));
send_wins_name_release_response(NAM_ERR, p);
return;
}
if (!WINS_STATE_ACTIVE(namerec)) {
DEBUG(3,("wins_process_name_release_request: Refusing request to \
release name %s as this record is not active anymore.\n", nmb_namestr(question) ));
send_wins_name_release_response(NAM_ERR, p);
return;
}
if(releasing_group_name && (question->name_type == 0x1c) && (namerec->data.num_ips > 1)) {
remove_ip_from_name_record(namerec, from_ip);
DEBUG(3,("wins_process_name_release_request: Remove IP %s from NAME: %s\n",
inet_ntoa(from_ip),nmb_namestr(question)));
wins_hook("delete", namerec, 0);
send_wins_name_release_response(0, p);
return;
}
namerec->data.wins_flags |= WINS_RELEASED;
update_name_ttl(namerec, EXTINCTION_INTERVAL);
wins_hook("delete", namerec, 0);
send_wins_name_release_response(0, p);
}
static int wins_processing_traverse_fn(TDB_CONTEXT *tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
{
time_t t = *(time_t *)state;
BOOL store_record = False;
struct name_record *namerec = NULL;
struct in_addr our_fake_ip = *interpret_addr2("0.0.0.0");
if (kbuf.dsize != sizeof(unstring) + 1) {
return 0;
}
namerec = wins_record_to_name_record(kbuf, dbuf);
if (!namerec) {
return 0;
}
if( (namerec->data.death_time != PERMANENT_TTL) && (namerec->data.death_time < t) ) {
if( namerec->data.source == SELF_NAME ) {
DEBUG( 3, ( "wins_processing_traverse_fn: Subnet %s not expiring SELF name %s\n",
wins_server_subnet->subnet_name, nmb_namestr(&namerec->name) ) );
namerec->data.death_time += 300;
store_record = True;
goto done;
} else if (namerec->data.source == DNS_NAME || namerec->data.source == DNSFAIL_NAME) {
DEBUG(3,("wins_processing_traverse_fn: deleting timed out DNS name %s\n",
nmb_namestr(&namerec->name)));
remove_name_from_wins_namelist(namerec );
goto done;
}
if (ip_equal(namerec->data.wins_ip, our_fake_ip)) {
switch (namerec->data.wins_flags & WINS_STATE_MASK) {
case WINS_ACTIVE:
namerec->data.wins_flags&=~WINS_STATE_MASK;
namerec->data.wins_flags|=WINS_RELEASED;
namerec->data.death_time = t + EXTINCTION_INTERVAL;
DEBUG(3,("wins_processing_traverse_fn: expiring %s\n",
nmb_namestr(&namerec->name)));
store_record = True;
goto done;
case WINS_RELEASED:
namerec->data.wins_flags&=~WINS_STATE_MASK;
namerec->data.wins_flags|=WINS_TOMBSTONED;
namerec->data.death_time = t + EXTINCTION_TIMEOUT;
get_global_id_and_update(&namerec->data.id, True);
DEBUG(3,("wins_processing_traverse_fn: tombstoning %s\n",
nmb_namestr(&namerec->name)));
store_record = True;
goto done;
case WINS_TOMBSTONED:
DEBUG(3,("wins_processing_traverse_fn: deleting %s\n",
nmb_namestr(&namerec->name)));
remove_name_from_wins_namelist(namerec );
goto done;
}
} else {
switch (namerec->data.wins_flags & WINS_STATE_MASK) {
case WINS_ACTIVE:
namerec->data.wins_flags&=~WINS_STATE_MASK;
namerec->data.wins_flags|=WINS_TOMBSTONED;
namerec->data.death_time = t + EXTINCTION_TIMEOUT;
DEBUG(3,("wins_processing_traverse_fn: tombstoning %s\n",
nmb_namestr(&namerec->name)));
store_record = True;
goto done;
case WINS_TOMBSTONED:
DEBUG(3,("wins_processing_traverse_fn: deleting %s\n",
nmb_namestr(&namerec->name)));
remove_name_from_wins_namelist(namerec );
goto done;
case WINS_RELEASED:
DEBUG(0,("wins_processing_traverse_fn: %s is in released state and\
we are not the wins owner !\n", nmb_namestr(&namerec->name)));
goto done;
}
}
}
done:
if (store_record) {
wins_store_changed_namerec(namerec);
}
SAFE_FREE(namerec->data.ip);
SAFE_FREE(namerec);
return 0;
}
void initiate_wins_processing(time_t t)
{
static time_t lasttime = 0;
if (!lasttime) {
lasttime = t;
}
if (t - lasttime < 20) {
return;
}
if(!lp_we_are_a_wins_server()) {
lasttime = t;
return;
}
tdb_traverse(wins_tdb, wins_processing_traverse_fn, &t);
wins_delete_all_tmp_in_memory_records();
wins_write_database(t, True);
lasttime = t;
}
void wins_write_name_record(struct name_record *namerec, XFILE *fp)
{
int i;
struct tm *tm;
DEBUGADD(4,("%-19s ", nmb_namestr(&namerec->name) ));
if( namerec->data.death_time != PERMANENT_TTL ) {
char *ts, *nl;
tm = localtime(&namerec->data.death_time);
if (!tm) {
return;
}
ts = asctime(tm);
if (!ts) {
return;
}
nl = strrchr( ts, '\n' );
if( NULL != nl ) {
*nl = '\0';
}
DEBUGADD(4,("TTL = %s ", ts ));
} else {
DEBUGADD(4,("TTL = PERMANENT "));
}
for (i = 0; i < namerec->data.num_ips; i++) {
DEBUGADD(4,("%15s ", inet_ntoa(namerec->data.ip[i]) ));
}
DEBUGADD(4,("%2x\n", namerec->data.nb_flags ));
if( namerec->data.source == REGISTER_NAME ) {
unstring name;
pull_ascii_nstring(name, sizeof(name), namerec->name.name);
x_fprintf(fp, "\"%s#%02x\" %d ", name,namerec->name.name_type,
(int)namerec->data.death_time);
for (i = 0; i < namerec->data.num_ips; i++)
x_fprintf( fp, "%s ", inet_ntoa( namerec->data.ip[i] ) );
x_fprintf( fp, "%2xR\n", namerec->data.nb_flags );
}
}
static int wins_writedb_traverse_fn(TDB_CONTEXT *tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
{
struct name_record *namerec = NULL;
XFILE *fp = (XFILE *)state;
if (kbuf.dsize != sizeof(unstring) + 1) {
return 0;
}
namerec = wins_record_to_name_record(kbuf, dbuf);
if (!namerec) {
return 0;
}
wins_write_name_record(namerec, fp);
SAFE_FREE(namerec->data.ip);
SAFE_FREE(namerec);
return 0;
}
void wins_write_database(time_t t, BOOL background)
{
static time_t last_write_time = 0;
pstring fname, fnamenew;
XFILE *fp;
if (background) {
if (!last_write_time) {
last_write_time = t;
}
if (t - last_write_time < 120) {
return;
}
}
if(!lp_we_are_a_wins_server()) {
return;
}
if (background) {
CatchChild();
if (sys_fork()) {
return;
}
if (tdb_reopen(wins_tdb)) {
DEBUG(0,("wins_write_database: tdb_reopen failed. Error was %s\n",
strerror(errno)));
_exit(0);
return;
}
}
slprintf(fname,sizeof(fname)-1,"%s/%s", lp_lockdir(), WINS_LIST);
all_string_sub(fname,"//", "/", 0);
slprintf(fnamenew,sizeof(fnamenew)-1,"%s.%u", fname, (unsigned int)sys_getpid());
if((fp = x_fopen(fnamenew,O_WRONLY|O_CREAT,0644)) == NULL) {
DEBUG(0,("wins_write_database: Can't open %s. Error was %s\n", fnamenew, strerror(errno)));
if (background) {
_exit(0);
}
return;
}
DEBUG(4,("wins_write_database: Dump of WINS name list.\n"));
x_fprintf(fp,"VERSION %d %u\n", WINS_VERSION, 0);
tdb_traverse(wins_tdb, wins_writedb_traverse_fn, fp);
x_fclose(fp);
chmod(fnamenew,0644);
unlink(fname);
rename(fnamenew,fname);
if (background) {
_exit(0);
}
}
#if 0
Until winsrepl is done.
void nmbd_wins_new_entry(int msg_type, struct process_id src,
void *buf, size_t len, void *private_data)
{
WINS_RECORD *record;
struct name_record *namerec = NULL;
struct name_record *new_namerec = NULL;
struct nmb_name question;
BOOL overwrite=False;
struct in_addr our_fake_ip = *interpret_addr2("0.0.0.0");
int i;
if (buf==NULL) {
return;
}
record=(WINS_RECORD *)buf;
make_nmb_name(&question, record->name, record->type);
namerec = find_name_on_subnet(wins_server_subnet, &question, FIND_ANY_NAME);
if (namerec == NULL) {
DEBUG(3,("nmbd_wins_new_entry: adding new replicated record: %s<%02x> for wins server: %s\n",
record->name, record->type, inet_ntoa(record->wins_ip)));
new_namerec=add_name_to_subnet( wins_server_subnet,
record->name,
record->type,
record->nb_flags,
EXTINCTION_INTERVAL,
REGISTER_NAME,
record->num_ips,
record->ip);
if (new_namerec!=NULL) {
update_wins_owner(new_namerec, record->wins_ip);
update_wins_flag(new_namerec, record->wins_flags);
new_namerec->data.id=record->id;
wins_server_subnet->namelist_changed = True;
}
}
if (namerec != NULL) {
if (namerec->data.wins_flags&WINS_UNIQUE && record->wins_flags&WINS_UNIQUE) {
if (!ip_equal(namerec->data.wins_ip, our_fake_ip)) {
if (namerec->data.wins_flags&WINS_ACTIVE && record->wins_flags&WINS_TOMBSTONED) {
if (ip_equal(namerec->data.wins_ip, record->wins_ip))
overwrite=True;
} else
overwrite=True;
} else {
if (ip_equal(namerec->data.ip[0], record->ip[0])) {
if (namerec->data.wins_flags&WINS_ACTIVE && record->wins_flags&WINS_TOMBSTONED)
get_global_id_and_update(&namerec->data.id, True);
else
overwrite=True;
} else {
if (namerec->data.wins_flags&WINS_ACTIVE) {
if (record->wins_flags&WINS_TOMBSTONED)
get_global_id_and_update(&namerec->data.id, True);
if (record->wins_flags&WINS_ACTIVE)
;
} else
overwrite=True;
}
}
}
if (record->wins_flags&WINS_NGROUP || record->wins_flags&WINS_SGROUP) {
if (namerec->data.wins_flags&WINS_UNIQUE)
;
overwrite=True;
}
if (record->wins_flags&WINS_SGROUP && namerec->data.wins_flags&WINS_SGROUP) {
if (namerec->data.wins_flags&WINS_ACTIVE) {
for (i=0; i<record->num_ips; i++)
if(!find_ip_in_name_record(namerec, record->ip[i]))
add_ip_to_name_record(namerec, record->ip[i]);
} else {
overwrite=True;
}
}
if (record->wins_flags&WINS_MHOMED) {
if (! (namerec->data.wins_flags&WINS_ACTIVE)) {
if ( !(namerec->data.wins_flags&WINS_RELEASED) && !(namerec->data.wins_flags&WINS_NGROUP))
overwrite=True;
}
else {
if (ip_equal(record->wins_ip, namerec->data.wins_ip))
overwrite=True;
if (ip_equal(namerec->data.wins_ip, our_fake_ip))
if (namerec->data.wins_flags&WINS_UNIQUE)
get_global_id_and_update(&namerec->data.id, True);
}
if (record->wins_flags&WINS_ACTIVE && namerec->data.wins_flags&WINS_ACTIVE)
if (namerec->data.wins_flags&WINS_UNIQUE ||
namerec->data.wins_flags&WINS_MHOMED)
if (ip_equal(record->wins_ip, namerec->data.wins_ip))
overwrite=True;
}
if (overwrite == False)
DEBUG(3, ("nmbd_wins_new_entry: conflict in adding record: %s<%02x> from wins server: %s\n",
record->name, record->type, inet_ntoa(record->wins_ip)));
else {
DEBUG(3, ("nmbd_wins_new_entry: replacing record: %s<%02x> from wins server: %s\n",
record->name, record->type, inet_ntoa(record->wins_ip)));
remove_name_from_namelist( wins_server_subnet, namerec );
new_namerec=add_name_to_subnet( wins_server_subnet, record->name, record->type, record->nb_flags,
EXTINCTION_INTERVAL, REGISTER_NAME, record->num_ips, record->ip);
if (new_namerec!=NULL) {
update_wins_owner(new_namerec, record->wins_ip);
update_wins_flag(new_namerec, record->wins_flags);
new_namerec->data.id=record->id;
wins_server_subnet->namelist_changed = True;
}
wins_server_subnet->namelist_changed = True;
}
}
}
#endif