diff options
author | Craig Miskell <craig.miskell@opus.co.nz> | 2010-03-15 16:20:44 -0700 |
---|---|---|
committer | Jeremy Allison <jra@samba.org> | 2010-03-15 16:20:44 -0700 |
commit | f2cbc9fbb12d36f6ce86111848ab0ee05469cdba (patch) | |
tree | 45de163d259f5119a12ade2c8425fdb14db2a77a /source3/nmbd | |
parent | 9a825dc770bfcb0923572e8ad3beff2f86dce992 (diff) | |
download | samba-f2cbc9fbb12d36f6ce86111848ab0ee05469cdba.tar.gz samba-f2cbc9fbb12d36f6ce86111848ab0ee05469cdba.tar.bz2 samba-f2cbc9fbb12d36f6ce86111848ab0ee05469cdba.zip |
Fix bug #7191 - WINS doesn't respond after > 86 #1c registrations.
Diffstat (limited to 'source3/nmbd')
-rw-r--r-- | source3/nmbd/nmbd_winsserver.c | 119 |
1 files changed, 115 insertions, 4 deletions
diff --git a/source3/nmbd/nmbd_winsserver.c b/source3/nmbd/nmbd_winsserver.c index 0a5b1c8764..be2c6a2443 100644 --- a/source3/nmbd/nmbd_winsserver.c +++ b/source3/nmbd/nmbd_winsserver.c @@ -52,6 +52,26 @@ static void wins_delete_all_tmp_in_memory_records(void) } /**************************************************************************** + Delete all the temporary 1b name records on the in-memory linked list. +*****************************************************************************/ + +static void wins_delete_all_1b_in_memory_records(void) +{ + struct name_record *nr = NULL; + struct name_record *nrnext = NULL; + + /* Delete all temporary 1b name records on the wins subnet linked list. */ + for( nr = wins_server_subnet->namelist; nr; nr = nrnext) { + nrnext = nr->next; + if (nr->name.name_type == 0x1b) { + DLIST_REMOVE(wins_server_subnet->namelist, nr); + SAFE_FREE(nr->data.ip); + SAFE_FREE(nr); + } + } +} + +/**************************************************************************** Convert a wins.tdb record to a struct name_record. Add in our global_scope(). *****************************************************************************/ @@ -1250,6 +1270,84 @@ to register name %s from IP %s.\n", nmb_namestr(question), inet_ntoa(p->ip) )); */ if(!find_ip_in_name_record(namerec, from_ip)) { + /* + * Need to emulate the behaviour of Windows, as + * described in: + * http://lists.samba.org/archive/samba-technical/2001-October/016236.html + * (is there an MS reference for this + * somewhere?) because if the 1c list gets over + * 86 entries, the reply packet is too big + * (rdata>576 bytes) so no reply is sent. + * + * Keep only the "latest" 25 records, while + * ensuring that the PDC (0x1b) is never removed + * We do this by removing the first entry that + * isn't the 1b entry for the same name, + * on the grounds that insertion is at the end + * of the list, so the oldest entries are at + * the start. + * + */ + while(namerec->data.num_ips>=25) { + struct name_record *name1brec = NULL; + + /* We only do this for 1c types. */ + if (namerec->name.name_type != 0x1c) { + break; + } + DEBUG(3,("wins_process_name_registration_request: " + "More than 25 IPs already in " + "the list. Looking for a 1b " + "record\n")); + + /* Ensure we have all the active 1b + * names on the list. */ + wins_delete_all_1b_in_memory_records(); + fetch_all_active_wins_1b_names(); + + /* Per the above, find the 1b record, + and then remove the first IP that isn't the same */ + for(name1brec = subrec->namelist; + name1brec; + name1brec = name1brec->next ) { + if( WINS_STATE_ACTIVE(name1brec) && + name1brec->name.name_type == 0x1b) { + DEBUG(3,("wins_process_name_registration_request: " + "Found the #1b record " + "with ip %s\n", + inet_ntoa(name1brec->data.ip[0]))); + break; + } + } + if(!name1brec) { + DEBUG(3,("wins_process_name_registration_request: " + "Didn't find a #1b name record. " + "Removing the first available " + "entry %s\n", + inet_ntoa(namerec->data.ip[0]))); + remove_ip_from_name_record(namerec, namerec->data.ip[0]); + wins_hook("delete", namerec, 0); + } else { + int i; + for(i=0; i<namerec->data.num_ips; i++) { + /* The name1brec should only have + * the single IP address in it, + * so we only check against the first one*/ + if(!ip_equal_v4( namerec->data.ip[i], name1brec->data.ip[0])) { + /* The i'th entry isn't the 1b address; delete it */ + DEBUG(3,("wins_process_name_registration_request: " + "Entry at %d is not the #1b address. " + "About to remove it\n", + i)); + remove_ip_from_name_record(namerec, namerec->data.ip[i]); + wins_hook("delete", namerec, 0); + break; + } + } + } + } + /* The list is guaranteed to be < 25 entries now + * - safe to add a new one */ 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); @@ -1866,27 +1964,40 @@ void send_wins_name_query_response(int rcode, struct packet_struct *p, memset(rdata,'\0',6); if(rcode == 0) { + + int ip_count; + ttl = (namerec->data.death_time != PERMANENT_TTL) ? namerec->data.death_time - p->timestamp : lp_max_wins_ttl(); + /* The netbios reply packet data section is limited to 576 bytes. In theory + * this should give us space for 96 addresses, but in practice, 86 appears + * to be the max (don't know why). If we send any more than that, + * reply_netbios_packet will fail to send a reply to avoid a memcpy buffer + * overflow. Keep the count to 85 and it will be ok */ + ip_count=namerec->data.num_ips; + if(ip_count>85) { + ip_count=85; + } + /* 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->data.num_ips == 1 ) { + if( ip_count == 1 ) { prdata = rdata; } else { - if((prdata = (char *)SMB_MALLOC( namerec->data.num_ips * 6 )) == NULL) { + if((prdata = (char *)SMB_MALLOC( ip_count * 6 )) == NULL) { DEBUG(0,("send_wins_name_query_response: malloc fail !\n")); return; } } - for(i = 0; i < namerec->data.num_ips; i++) { + for(i = 0; i < ip_count; 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_data_len = ip_count * 6; } reply_netbios_packet(p, /* Packet to reply to. */ |