diff options
author | Jean-François Micouleau <jfm@samba.org> | 2002-01-25 22:50:15 +0000 |
---|---|---|
committer | Jean-François Micouleau <jfm@samba.org> | 2002-01-25 22:50:15 +0000 |
commit | 558e4cf0b87f393c2b0a52192c5051c258f281e0 (patch) | |
tree | d52802d7534e4458b9ce4fdd2ace3e42071121c0 | |
parent | 38ac78c3df67d8f187f83abb8fd1bcaeafe4e316 (diff) | |
download | samba-558e4cf0b87f393c2b0a52192c5051c258f281e0.tar.gz samba-558e4cf0b87f393c2b0a52192c5051c258f281e0.tar.bz2 samba-558e4cf0b87f393c2b0a52192c5051c258f281e0.zip |
rewrote nmbd's wins backend to use a tdb instead of a flat text file.
Changed the way the wins record are handled in memory. Now they are living
much longer with the different states: active, released and tombstone.
Also added a version ID, some wins flags and the wins owner ip address to
the namrec->data struct, and a function to process messages sent by the
wins replication daemon.
the initiate_wins_processing() function is not correct, I'll fix it later.
J.F.
(This used to be commit b902e087d06c32797af19021a7f56895d86d7364)
-rw-r--r-- | source3/include/nameserv.h | 56 | ||||
-rw-r--r-- | source3/nmbd/nmbd.c | 1 | ||||
-rw-r--r-- | source3/nmbd/nmbd_namelistdb.c | 1 | ||||
-rw-r--r-- | source3/nmbd/nmbd_winsserver.c | 892 |
4 files changed, 683 insertions, 267 deletions
diff --git a/source3/include/nameserv.h b/source3/include/nameserv.h index ff777fd1ca..9e62603594 100644 --- a/source3/include/nameserv.h +++ b/source3/include/nameserv.h @@ -22,6 +22,12 @@ */ +#define INFO_VERSION "INFO/version" +#define INFO_COUNT "INFO/num_entries" +#define INFO_ID_HIGH "INFO/id_high" +#define INFO_ID_LOW "INFO/id_low" +#define ENTRY_PREFIX "ENTRY/" + #define PERMANENT_TTL 0 /* NTAS uses 2, NT uses 1, WfWg uses 0 */ @@ -84,6 +90,33 @@ enum netbios_reply_type_code { NMB_QUERY, NMB_STATUS, NMB_REG, NMB_REG_REFRESH, /* Mask applied to outgoing NetBIOS flags. */ #define NB_FLGMSK 0xE0 +/* The wins flags. Looks like the nbflags ! */ +#define WINS_UNIQUE 0x00 /* Unique record */ +#define WINS_NGROUP 0x01 /* Normal Group eg: 1B */ +#define WINS_SGROUP 0x02 /* Special Group eg: 1C */ +#define WINS_MHOMED 0x03 /* MultiHomed */ + +#define WINS_ACTIVE 0x00 /* active record */ +#define WINS_RELEASED 0x04 /* released record */ +#define WINS_TOMBSTONED 0x08 /* tombstoned record */ +#define WINS_DELETED 0x0C /* deleted record */ + +#define WINS_STATE_MASK 0x0C + +#define WINS_LOCAL 0x00 /* local record */ +#define WINS_REMOTE 0x10 /* remote record */ + +#define WINS_BNODE 0x00 /* Broadcast node */ +#define WINS_PNODE 0x20 /* PtP node */ +#define WINS_MNODE 0x40 /* Mixed node */ +#define WINS_HNODE 0x60 /* Hybrid node */ + +#define WINS_NONSTATIC 0x00 /* dynamic record */ +#define WINS_STATIC 0x80 /* static record */ + +#define WINS_STATE_ACTIVE(p) (((p)->data.wins_flags & WINS_STATE_MASK) == WINS_ACTIVE) + + /* NetBIOS flag identifier. */ #define NAME_GROUP(p) ((p)->data.nb_flags & NB_GROUP) #define NAME_BFLAG(p) (((p)->data.nb_flags & NB_NODETYPEMASK) == NB_BFLAG) @@ -180,6 +213,11 @@ struct nmb_data time_t death_time; /* The time the record must be removed (do not remove if 0). */ time_t refresh_time; /* The time the record should be refreshed. */ + + SMB_BIG_UINT id; /* unique id */ + struct in_addr wins_ip; /* the adress of the wins server this record comes from */ + + int wins_flags; /* similar to the netbios flags but different ! */ }; /* This structure represents an entry in a local netbios name list. */ @@ -556,6 +594,12 @@ struct packet_struct affects non-permanent self names (in seconds) */ #define MAX_REFRESH_TIME (60*20) +/* The Extinction interval: 4 days, time a node will stay in released state */ +#define EXTINCTION_INTERVAL (4*24*60*60) + +/* The Extinction time-out: 1 day, time a node will stay in deleted state */ +#define EXTINCTION_TIMEOUT (24*60*60) + /* Macro's to enumerate subnets either with or without the UNICAST subnet. */ @@ -568,6 +612,18 @@ extern struct subnet_record *remote_broadcast_subnet; #define NEXT_SUBNET_EXCLUDING_UNICAST(x) ((x)->next) #define NEXT_SUBNET_INCLUDING_UNICAST(x) (get_next_subnet_maybe_unicast((x))) +/* wins replication record used between nmbd and wrepld */ +typedef struct _WINS_RECORD { + char name[17]; + char type; + int nb_flags; + int wins_flags; + SMB_BIG_UINT id; + int num_ips; + struct in_addr ip[25]; + struct in_addr wins_ip; +} WINS_RECORD; + /* To be removed. */ enum state_type { TEST }; #endif /* _NAMESERV_H_ */ diff --git a/source3/nmbd/nmbd.c b/source3/nmbd/nmbd.c index 57dacc7a05..a7abdc5da0 100644 --- a/source3/nmbd/nmbd.c +++ b/source3/nmbd/nmbd.c @@ -827,6 +827,7 @@ static void usage(char *pname) pidfile_create("nmbd"); message_init(); message_register(MSG_FORCE_ELECTION, nmbd_message_election); + message_register(MSG_WINS_NEW_ENTRY, nmbd_wins_new_entry); DEBUG( 3, ( "Opening sockets %d\n", global_nmb_port ) ); diff --git a/source3/nmbd/nmbd_namelistdb.c b/source3/nmbd/nmbd_namelistdb.c index aa9589e45a..fba56ef49f 100644 --- a/source3/nmbd/nmbd_namelistdb.c +++ b/source3/nmbd/nmbd_namelistdb.c @@ -213,6 +213,7 @@ struct name_record *add_name_to_subnet( struct subnet_record *subrec, /* Enter the name as active. */ namerec->data.nb_flags = nb_flags | NB_ACTIVE; + namerec->data.wins_flags = WINS_ACTIVE; /* If it's our primary name, flag it as so. */ if( strequal( my_netbios_names[0], name ) ) diff --git a/source3/nmbd/nmbd_winsserver.c b/source3/nmbd/nmbd_winsserver.c index ecb5f2da55..d732da62cf 100644 --- a/source3/nmbd/nmbd_winsserver.c +++ b/source3/nmbd/nmbd_winsserver.c @@ -23,10 +23,82 @@ #include "includes.h" -#define WINS_LIST "wins.dat" +#define WINS_LIST "wins.tdb" #define WINS_VERSION 1 /**************************************************************************** +change the wins owner address in the record. +*****************************************************************************/ +static void update_wins_owner(struct name_record *namerec, struct in_addr wins_ip) +{ + if (namerec==NULL) + return; + namerec->data.wins_ip=wins_ip; +} + +/**************************************************************************** +create the wins flags based on the nb flags and the input value. +*****************************************************************************/ +static void update_wins_flag(struct name_record *namerec, int flags) +{ + if (namerec==NULL) + return; + + namerec->data.wins_flags=0x0; + + /* if it's a group, it can be a normal or a special one */ + 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 { + /* can be unique or multi-homed */ + if (namerec->data.num_ips>1) + namerec->data.wins_flags|=WINS_MHOMED; + else + namerec->data.wins_flags|=WINS_UNIQUE; + } + + /* the node type are the same bits */ + namerec->data.wins_flags|=namerec->data.nb_flags&NB_NODETYPEMASK; + + /* the static bit is elsewhere */ + if (namerec->data.death_time == PERMANENT_TTL) + namerec->data.wins_flags|=WINS_STATIC; + + /* and add the given bits */ + 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)); + +} + +/**************************************************************************** +return the general ID value and increase it if requested +*****************************************************************************/ +static void get_global_id_and_update(SMB_BIG_UINT *current_id, BOOL update) +{ + /* + * it's kept as a static here, to prevent people from messing + * with the value directly + */ + + 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++; +} + +/**************************************************************************** possibly call the WINS hook external program when a WINS change is made *****************************************************************************/ static void wins_hook(char *operation, struct name_record *namerec, int ttl) @@ -168,178 +240,115 @@ Load or create the WINS database. BOOL initialise_wins(void) { - time_t time_now = time(NULL); - XFILE *fp; - pstring line; + time_t time_now = time(NULL); + TDB_CONTEXT *tdb; + TDB_DATA kbuf, dbuf, newkey; + struct name_record *namerec = NULL; + struct in_addr our_fake_ip = *interpret_addr2("0.0.0.0"); - if(!lp_we_are_a_wins_server()) - return True; + DEBUG(2,("initialise_wins: started\n")); - add_samba_names_to_subnet(wins_server_subnet); + if(!lp_we_are_a_wins_server()) + return True; - 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; - char *ptr; - char *p; - BOOL got_token; - BOOL was_ip; - int i; - unsigned hash; - int version; - - /* Read a line from the wins.dat file. Strips whitespace - from the beginning and end of the line. - */ - 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 || - hash != wins_hash()) { - DEBUG(0,("Discarding invalid wins.dat file [%s]\n",line)); - x_fclose(fp); - return True; - } - continue; - } - - ptr = line; + add_samba_names_to_subnet(wins_server_subnet); - /* - * Now we handle multiple IP addresses per name we need - * to iterate over the line twice. The first time to - * determine how many IP addresses there are, the second - * time to actually parse them into the ip_list array. - */ - - 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; - } + tdb = tdb_open_log(lock_path(WINS_LIST), 0, TDB_DEFAULT, O_RDONLY, 0600); + if (!tdb) { + DEBUG(2,("initialise_wins: Can't open wins database file %s. Error was %s\n", WINS_LIST, strerror(errno) )); + return True; + } - 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; - } + if (tdb_fetch_int(tdb, INFO_VERSION) != WINS_VERSION) { + DEBUG(0,("Discarding invalid wins.dat file\n")); + tdb_close(tdb); + return True; + } - /* - * Determine the number of IP addresses per line. - */ - num_ips = 0; - do - { - got_token = next_token(&ptr,ip_str,NULL,sizeof(ip_str)); - was_ip = False; + for (kbuf = tdb_firstkey(tdb); + kbuf.dptr; + newkey = tdb_nextkey(tdb, kbuf), safe_free(kbuf.dptr), kbuf=newkey) { + + pstring name_type, name, ip_str; + char *p; + int type = 0; + int nb_flags; + int ttl; + unsigned int num_ips; + int high, low; + struct in_addr wins_ip; + struct in_addr *ip_list; + int wins_flags; + int len,i; + + if (strncmp(kbuf.dptr, ENTRY_PREFIX, strlen(ENTRY_PREFIX)) != 0) + continue; + + dbuf = tdb_fetch(tdb, kbuf); + if (!dbuf.dptr) continue; + + fstrcpy(name_type, kbuf.dptr+strlen(ENTRY_PREFIX)); + + pstrcpy(name, name_type); + + if((p = strchr(name,'#')) != NULL) { + *p = 0; + sscanf(p+1,"%x",&type); + } - if(got_token && strchr_m(ip_str, '.')) - { - num_ips++; - was_ip = True; - } - } while( got_token && was_ip); + len = tdb_unpack(dbuf.dptr, dbuf.dsize, "dddfddd", + &nb_flags, &high, &low, + ip_str, &ttl, &num_ips, &wins_flags); - if(num_ips == 0) - { - DEBUG(0,("initialise_wins: Missing IP address when parsing line %s\n", line )); - continue; - } + wins_ip=*interpret_addr2(ip_str); - if(!got_token) - { - DEBUG(0,("initialise_wins: Missing nb_flags when parsing line %s\n", line )); - continue; - } + /* Don't reload replica records */ + if (!ip_equal(wins_ip, our_fake_ip)) + continue; - /* Allocate the space for the ip_list. */ - if((ip_list = (struct in_addr *)malloc( num_ips * sizeof(struct in_addr))) == NULL) - { - DEBUG(0,("initialise_wins: Malloc fail !\n")); - return False; - } - - /* Reset and re-parse the line. */ - 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)); + /* Don't reload released or tombstoned records */ + if ((wins_flags&WINS_STATE_MASK) != WINS_ACTIVE) + continue; - /* - * Deal with SELF or REGISTER name encoding. Default is REGISTER - * for compatibility with old nmbds. - */ + /* Allocate the space for the ip_list. */ + if((ip_list = (struct in_addr *)malloc( num_ips * sizeof(struct in_addr))) == NULL) { + DEBUG(0,("initialise_wins: Malloc fail !\n")); + return False; + } - 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'; - - /* Netbios name. # divides the name from the type (hex): netbios#xx */ - pstrcpy(name,name_str); - - if((p = strchr_m(name,'#')) != NULL) - { - *p = 0; - sscanf(p+1,"%x",&type); - } - - /* Decode the netbios flags (hex) and the time-to-live (in seconds). */ - sscanf(nb_flags_str,"%x",&nb_flags); - sscanf(ttl_str,"%d",&ttl); + for (i = 0; i < num_ips; i++) { + len += tdb_unpack(dbuf.dptr+len, dbuf.dsize-len, "f", ip_str); + ip_list[i] = *interpret_addr2(ip_str); + } - /* add all entries that have 60 seconds or more to live */ - if ((ttl - 60) > time_now || ttl == PERMANENT_TTL) - { - if(ttl != PERMANENT_TTL) - ttl -= time_now; + /* add all entries that have 60 seconds or more to live */ + 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)); - } + 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)); + + namerec=add_name_to_subnet( wins_server_subnet, name, type, nb_flags, + ttl, REGISTER_NAME, num_ips, ip_list); + if (namerec!=NULL) { + update_wins_owner(namerec, wins_ip); + update_wins_flag(namerec, wins_flags); + /* we don't reload the ID, on startup we restart at 1 */ + get_global_id_and_update(&namerec->data.id, True); + } + + } 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); - } + SAFE_FREE(ip_list); + } - x_fclose(fp); - return True; + tdb_close(tdb); + DEBUG(2,("initialise_wins: done\n")); + return True; } /**************************************************************************** @@ -409,6 +418,7 @@ void wins_process_name_refresh_request(struct subnet_record *subrec, 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]); @@ -450,6 +460,21 @@ the name does not exist. Treating as registration.\n", nmb_namestr(question) )); } /* + * if the name is present but not active, + * simply remove it and treat the request + * as a registration + */ + if (namerec != NULL && !WINS_STATE_ACTIVE(namerec)) + { + DEBUG(5,("wins_process_name_refresh_request: Name (%s) in WINS was \ +not active - removing it.\n", nmb_namestr(question) )); + remove_name_from_namelist( subrec, namerec ); + namerec = NULL; + wins_process_name_registration_request(subrec,p); + return; + } + + /* * Check that the group bits for the refreshing name and the * name in our database match. */ @@ -475,6 +500,16 @@ does not match group bit in WINS for this name.\n", nmb_namestr(question), group * Update the ttl. */ update_name_ttl(namerec, ttl); + + /* + * if the record is a replica: + * we take ownership and update the version ID. + */ + 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; @@ -658,6 +693,7 @@ void wins_process_name_registration_request(struct subnet_record *subrec, 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]); @@ -685,6 +721,18 @@ IP %s\n", registering_group_name ? "Group" : "Unique", nmb_namestr(question), in namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME); /* + * if the record exists but NOT in active state, + * consider it dead. + */ + 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; + } + + /* * Deal with the case where the name found was a dns entry. * Remove it as we now have a NetBIOS client registering the * name. @@ -759,8 +807,22 @@ to register name %s from IP %s.\n", nmb_namestr(question), inet_ntoa(p->ip) )); /* * Check the ip address is not already in the group. */ - if(!find_ip_in_name_record(namerec, from_ip)) + if(!find_ip_in_name_record(namerec, from_ip)) { add_ip_to_name_record(namerec, from_ip); + /* we need to update the record for replication */ + get_global_id_and_update(&namerec->data.id, True); + + /* + * if the record is a replica, we must change + * the wins owner to us to make the replication updates + * it on the other wins servers. + * And when the partner will receive this record, + * it will update its own record. + */ + + update_wins_owner(namerec, our_fake_ip); + + } update_name_ttl(namerec, ttl); send_wins_name_registration_response(0, ttl, p); return; @@ -770,6 +832,8 @@ to register name %s from IP %s.\n", nmb_namestr(question), inet_ntoa(p->ip) )); /* * If we are adding a unique name, the name exists in the WINS db * and is a group name then reject the registration. + * + * explanation: groups have a higher priority than unique names. */ DEBUG(3,("wins_process_name_registration_request: Attempt to register name %s. Name \ @@ -813,13 +877,18 @@ is one of our (WINS server) names. Denying registration.\n", nmb_namestr(questio /* * If the name exists and it is a unique registration and the registering IP - * is the same as the the (single) already registered IP then just update the ttl. + * is the same as the (single) already registered IP then just update the ttl. + * + * But not if the record is an active replica. IF it's a replica, it means it can be + * the same client which has moved and not yet expired. So we don't update + * the ttl in this case and go beyond to do a WACK and query the old client */ if( !registering_group_name && (namerec != NULL) && (namerec->data.num_ips == 1) - && ip_equal( namerec->data.ip[0], from_ip ) ) + && ip_equal( namerec->data.ip[0], from_ip ) + && ip_equal(namerec->data.wins_ip, our_fake_ip) ) { update_name_ttl( namerec, ttl ); send_wins_name_registration_response( 0, ttl, p ); @@ -880,9 +949,12 @@ is one of our (WINS server) names. Denying registration.\n", nmb_namestr(questio */ (void)add_name_to_subnet( subrec, question->name, question->name_type, - nb_flags, ttl, REGISTER_NAME, 1, &from_ip ); + nb_flags, ttl, REGISTER_NAME, 1, &from_ip); if ((namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME))) { - wins_hook("add", namerec, ttl); + 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); @@ -907,6 +979,7 @@ static void wins_multihomed_register_query_success(struct subnet_record *subrec, 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 *)); @@ -926,7 +999,7 @@ static void wins_multihomed_register_query_success(struct subnet_record *subrec, namerec = find_name_on_subnet(subrec, question_name, FIND_ANY_NAME); - if( (namerec == NULL) || (namerec->data.source != REGISTER_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 addess.\n", nmb_namestr(question_name) )); @@ -940,6 +1013,10 @@ a subsequent IP addess.\n", nmb_namestr(question_name) )); 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); send_wins_name_registration_response(0, ttl, orig_reg_packet); wins_hook("add", namerec, ttl); @@ -990,7 +1067,8 @@ void wins_process_multihomed_name_registration_request( struct subnet_record *su 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;; + BOOL group = (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]); @@ -1072,10 +1150,10 @@ to register name %s. Name already exists in WINS with source type %d.\n", } /* - * Reject if the name exists and is a GROUP name. + * Reject if the name exists and is a GROUP name and is active. */ - if((namerec != NULL) && NAME_GROUP(namerec)) + 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) )); @@ -1107,9 +1185,13 @@ is one of our (WINS server) names. Denying registration.\n", nmb_namestr(questio { /* * It's one of our names and one of our IP's. Ensure the IP is in the record and - * update the ttl. + * update the ttl. Update the version ID to force replication. */ 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("add", namerec, ttl); } else { @@ -1123,13 +1205,23 @@ is one of our (WINS server) names. Denying registration.\n", nmb_namestr(questio } /* - * If the name exists check if the IP address is already registered + * If the name exists and is active, check if the IP address is already registered * to that name. If so then update the ttl and reply success. */ - if((namerec != NULL) && find_ip_in_name_record(namerec, from_ip)) + if((namerec != NULL) && find_ip_in_name_record(namerec, from_ip) && WINS_STATE_ACTIVE(namerec)) { update_name_ttl(namerec, ttl); + /* + * If it's a replica, we need to become the wins owner + * to force the replication + */ + 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); + } + send_wins_name_registration_response(0, ttl, p); wins_hook("refresh", namerec, ttl); return; @@ -1192,9 +1284,12 @@ is one of our (WINS server) names. Denying registration.\n", nmb_namestr(questio */ (void)add_name_to_subnet( subrec, question->name, question->name_type, - nb_flags, ttl, REGISTER_NAME, 1, &from_ip ); + 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); } @@ -1213,7 +1308,7 @@ static void process_wins_dmb_query_request(struct subnet_record *subrec, int num_ips; /* - * Go through all the names in the WINS db looking for those + * Go through all the ACTIVE names in the WINS db looking for those * ending in <1b>. Use this to calculate the number of IP * addresses we need to return. */ @@ -1223,7 +1318,7 @@ static void process_wins_dmb_query_request(struct subnet_record *subrec, namerec; namerec = (struct name_record *)ubi_trNext( namerec ) ) { - if( namerec->name.name_type == 0x1b ) + if(WINS_STATE_ACTIVE(namerec) && namerec->name.name_type == 0x1b ) num_ips += namerec->data.num_ips; } @@ -1253,7 +1348,7 @@ static void process_wins_dmb_query_request(struct subnet_record *subrec, namerec; namerec = (struct name_record *)ubi_trNext( namerec ) ) { - if(namerec->name.name_type == 0x1b) + if(WINS_STATE_ACTIVE(namerec) && namerec->name.name_type == 0x1b) { int i; for(i = 0; i < namerec->data.num_ips; i++) @@ -1369,6 +1464,17 @@ void wins_process_name_query_request(struct subnet_record *subrec, if(namerec != NULL) { + /* + * If the name is not anymore in active state then reply not found. + * it's fair even if we keep it in the cache for days. + */ + 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 it's a DNSFAIL_NAME then reply name not found. */ @@ -1528,21 +1634,29 @@ release name %s as IP %s is not one of the known IP's for this name.\n", return; } + /* + * Check if the record is active. IF it's already released + * or tombstoned, refuse the release. + */ + if (!WINS_STATE_ACTIVE(namerec)) { + DEBUG(3,("wins_process_name_release_request: Refusing request to \ +release name %s as this record is not anymore active.\n", + nmb_namestr(question) )); + send_wins_name_release_response(NAM_ERR, p); + return; + } + /* - * Release the name and then remove the IP from the known list. + * Send a release response. + * Flag the name as released and update the ttl */ send_wins_name_release_response(0, p); - remove_ip_from_name_record(namerec, from_ip); + + namerec->data.wins_flags |= WINS_RELEASED; + update_name_ttl(namerec, EXTINCTION_INTERVAL); wins_hook("delete", namerec, 0); - - /* - * Remove the name entirely if no IP addresses left. - */ - if (namerec->data.num_ips == 0) - remove_name_from_namelist(subrec, namerec); - } /******************************************************************* @@ -1551,24 +1665,84 @@ release name %s as IP %s is not one of the known IP's for this name.\n", void initiate_wins_processing(time_t t) { - static time_t lasttime = 0; - - if (!lasttime) - lasttime = t; - if (t - lasttime < 20) - return; + static time_t lasttime = 0; + struct name_record *namerec; + struct name_record *next_namerec; + struct in_addr our_fake_ip = *interpret_addr2("0.0.0.0"); + + if (!lasttime) + lasttime = t; + if (t - lasttime < 20) + return; + + lasttime = t; + + if(!lp_we_are_a_wins_server()) + return; + + for( namerec = (struct name_record *)ubi_trFirst( wins_server_subnet->namelist ); + namerec; + namerec = next_namerec ) { + next_namerec = (struct name_record *)ubi_trNext( namerec ); + + if( (namerec->data.death_time != PERMANENT_TTL) + && (namerec->data.death_time < t) ) { + + if( namerec->data.source == SELF_NAME ) { + DEBUG( 3, ( "expire_names_on_subnet: Subnet %s not expiring SELF name %s\n", + wins_server_subnet->subnet_name, nmb_namestr(&namerec->name) ) ); + namerec->data.death_time += 300; + namerec->subnet->namelist_changed = True; + continue; + } + + /* handle records, samba is the wins owner */ + 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,("initiate_wins_processing: expiring %s\n", nmb_namestr(&namerec->name))); + break; + 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,("initiate_wins_processing: tombstoning %s\n", nmb_namestr(&namerec->name))); + break; + case WINS_TOMBSTONED: + DEBUG(3,("initiate_wins_processing: deleting %s\n", nmb_namestr(&namerec->name))); + remove_name_from_namelist( wins_server_subnet, namerec ); + break; + } + } else { + switch (namerec->data.wins_flags | WINS_STATE_MASK) { + case WINS_ACTIVE: + /* that's not as MS says it should be */ + namerec->data.wins_flags&=~WINS_STATE_MASK; + namerec->data.wins_flags|=WINS_TOMBSTONED; + namerec->data.death_time = t + EXTINCTION_TIMEOUT; + DEBUG(3,("initiate_wins_processing: tombstoning %s\n", nmb_namestr(&namerec->name))); + case WINS_TOMBSTONED: + DEBUG(3,("initiate_wins_processing: deleting %s\n", nmb_namestr(&namerec->name))); + remove_name_from_namelist( wins_server_subnet, namerec ); + break; + case WINS_RELEASED: + DEBUG(0,("initiate_wins_processing: %s is in released state and\ +we are not the wins owner !\n", nmb_namestr(&namerec->name))); + break; + } + } - lasttime = t; - - if(!lp_we_are_a_wins_server()) - return; - - expire_names_on_subnet(wins_server_subnet, t); + } + } - if(wins_server_subnet->namelist_changed) - wins_write_database(True); + if(wins_server_subnet->namelist_changed) + wins_write_database(True); - wins_server_subnet->namelist_changed = False; + wins_server_subnet->namelist_changed = False; } /******************************************************************* @@ -1576,84 +1750,268 @@ void initiate_wins_processing(time_t t) ******************************************************************/ void wins_write_database(BOOL background) { - struct name_record *namerec; - pstring fname, fnamenew; - XFILE *fp; - - if(!lp_we_are_a_wins_server()) - return; + struct name_record *namerec; + pstring fname, fnamenew; + TDB_CONTEXT *tdb; + TDB_DATA kbuf, dbuf; + pstring key, buf; + int len; + int num_record=0; + SMB_BIG_UINT id; + + if(!lp_we_are_a_wins_server()) + return; + + /* we will do the writing in a child process to ensure that the parent + doesn't block while this is done */ + if (background) { + CatchChild(); + if (sys_fork()) { + return; + } + } - /* we will do the writing in a child process to ensure that the parent - doesn't block while this is done */ - if (background) { - CatchChild(); - if (sys_fork()) { - 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()); - 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()); + tdb = tdb_open_log(fnamenew, 0, TDB_DEFAULT, O_RDWR|O_CREAT|O_TRUNC, 0644); + if (!tdb) { + DEBUG(0,("wins_write_database: Can't open %s. Error was %s\n", fnamenew, strerror(errno))); + if (background) + _exit(0); + return; + } - if((fp = x_fopen(fnamenew,O_WRONLY|O_CREAT|O_TRUNC, 0644)) == NULL) - { - DEBUG(0,("wins_write_database: Can't open %s. Error was %s\n", fnamenew, strerror(errno))); - if (background) { - _exit(0); - } - return; - } + DEBUG(3,("wins_write_database: Dump of WINS name list.\n")); - DEBUG(4,("wins_write_database: Dump of WINS name list.\n")); + tdb_store_int(tdb, INFO_VERSION, WINS_VERSION); - x_fprintf(fp,"VERSION %d %u\n", WINS_VERSION, wins_hash()); - - for( namerec - = (struct name_record *)ubi_trFirst( wins_server_subnet->namelist ); - namerec; - namerec = (struct name_record *)ubi_trNext( namerec ) ) - { - int i; - struct tm *tm; + for (namerec = (struct name_record *)ubi_trFirst( wins_server_subnet->namelist ); + namerec; + namerec = (struct name_record *)ubi_trNext( namerec ) ) { - DEBUGADD(4,("%-19s ", nmb_namestr(&namerec->name) )); + int i; + struct tm *tm; - if( namerec->data.death_time != PERMANENT_TTL ) - { - char *ts, *nl; - - tm = LocalTime(&namerec->data.death_time); - ts = asctime(tm); - nl = strrchr_m( ts, '\n' ); - if( NULL != nl ) - *nl = '\0'; - DEBUGADD(4,("TTL = %s ", ts )); - } - else - DEBUGADD(4,("TTL = PERMANENT ")); + DEBUGADD(3,("%-19s ", nmb_namestr(&namerec->name) )); - 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.death_time != PERMANENT_TTL ) { + char *ts, *nl; - if( namerec->data.source == REGISTER_NAME ) - { - x_fprintf(fp, "\"%s#%02x\" %d ", - namerec->name.name,namerec->name.name_type, /* Ignore scope. */ - (int)namerec->data.death_time); + tm = LocalTime(&namerec->data.death_time); + ts = asctime(tm); + nl = strrchr_m( ts, '\n' ); + if( NULL != nl ) + *nl = '\0'; - 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 ); - } - } - - x_fclose(fp); - chmod(fnamenew,0644); - unlink(fname); - rename(fnamenew,fname); - if (background) { - _exit(0); - } + DEBUGADD(3,("TTL = %s ", ts )); + } else + DEBUGADD(3,("TTL = PERMANENT ")); + + for (i = 0; i < namerec->data.num_ips; i++) + DEBUGADD(0,("%15s ", inet_ntoa(namerec->data.ip[i]) )); + + DEBUGADD(3,("0x%2x 0x%2x %15s\n", namerec->data.nb_flags, namerec->data.wins_flags, inet_ntoa(namerec->data.wins_ip))); + + if( namerec->data.source == REGISTER_NAME ) { + + /* store the type in the key to make the name unique */ + slprintf(key, sizeof(key), "%s%s#%02x", ENTRY_PREFIX, namerec->name.name, namerec->name.name_type); + + len = tdb_pack(buf, sizeof(buf), "dddfddd", + (int)namerec->data.nb_flags, + (int)(namerec->data.id>>32), + (int)(namerec->data.id&0xffffffff), + inet_ntoa(namerec->data.wins_ip), + (int)namerec->data.death_time, + namerec->data.num_ips, + namerec->data.wins_flags); + + for (i = 0; i < namerec->data.num_ips; i++) + len += tdb_pack(buf+len, sizeof(buf)-len, "f", inet_ntoa(namerec->data.ip[i])); + + kbuf.dsize = strlen(key)+1; + kbuf.dptr = key; + dbuf.dsize = len; + dbuf.dptr = buf; + if (tdb_store(tdb, kbuf, dbuf, TDB_INSERT) != 0) return; + + num_record++; + } + } + + /* store the number of records */ + tdb_store_int(tdb, INFO_COUNT, num_record); + + /* get and store the last used ID */ + get_global_id_and_update(&id, False); + tdb_store_int(tdb, INFO_ID_HIGH, id>>32); + tdb_store_int(tdb, INFO_ID_LOW, id&0xffffffff); + + tdb_close(tdb); + + chmod(fnamenew,0644); + unlink(fname); + rename(fnamenew,fname); + + if (background) + _exit(0); +} + +/**************************************************************************** +process a internal Samba message receiving a wins record +***************************************************************************/ +void nmbd_wins_new_entry(int msg_type, pid_t src, void *buf, size_t len) +{ + 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; + + ZERO_STRUCT(question); + memcpy(question.name, record->name, 16); + question.name_type=record->type; + + namerec = find_name_on_subnet(wins_server_subnet, &question, FIND_ANY_NAME); + + /* record doesn't exist, add it */ + 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; + } + } + + /* check if we have a conflict */ + if (namerec != NULL) { + /* both records are UNIQUE */ + if (namerec->data.wins_flags&WINS_UNIQUE && record->wins_flags&WINS_UNIQUE) { + + /* the database record is a replica */ + 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 { + /* we are the wins owner of the database record */ + /* the 2 records have the same IP address */ + 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 { + /* the 2 records have different IP address */ + 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) + /* send conflict challenge to the replica node */ + ; + } else + overwrite=True; + } + + } + } + + /* the replica is a standard group */ + if (record->wins_flags&WINS_NGROUP || record->wins_flags&WINS_SGROUP) { + /* if the database record is unique and active force a name release */ + if (namerec->data.wins_flags&WINS_UNIQUE) + /* send a release name to the unique node */ + ; + overwrite=True; + + } + + /* the replica is a special group */ + 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; + } + + /* the replica is a multihomed host */ + + /* I'm giving up on multi homed. Too much complex to understand */ + + 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 the old record and add a new one */ + 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; + } + + } } + + + + + + + + |