summaryrefslogtreecommitdiff
path: root/source3/nmbd
diff options
context:
space:
mode:
authorCraig Miskell <craig.miskell@opus.co.nz>2010-03-15 16:20:44 -0700
committerJeremy Allison <jra@samba.org>2010-03-15 16:20:44 -0700
commitf2cbc9fbb12d36f6ce86111848ab0ee05469cdba (patch)
tree45de163d259f5119a12ade2c8425fdb14db2a77a /source3/nmbd
parent9a825dc770bfcb0923572e8ad3beff2f86dce992 (diff)
downloadsamba-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.c119
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. */