--- samba-3.3.10/source/nmbd/nmbd_winsserver.c.orig 2010-03-02 11:49:15.000000000 +1300 +++ samba-3.3.10/source/nmbd/nmbd_winsserver.c 2010-03-02 15:54:54.000000000 +1300 @@ -1138,9 +1138,11 @@ uint16 nb_flags = get_nb_flags(nmb->additional->rdata); int ttl = get_ttl_from_packet(nmb); struct name_record *namerec = NULL; + struct name_record *name1brec = NULL; struct in_addr from_ip; bool registering_group_name = (nb_flags & NB_GROUP) ? True : False; struct in_addr our_fake_ip; + int i; (void)interpret_addr2(&our_fake_ip, "0.0.0.0"); putip((char *)&from_ip,&nmb->additional->rdata[2]); @@ -1250,6 +1252,48 @@ */ 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) { + DEBUG(3,("wins_process_name_registration_request: More than 25 IPs already in the list. Looking for a 1b record\n")); + + /* Appears to be necessary, otherwise we won't always find a 0x1b record*/ + 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; /* out of the for loop, we've found what we're after */ + } + } + 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]); + } else { + for(i=0; idata.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; //out of the for loop so we don't keep deleting... + } + } + } + } + /* 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);