diff options
Diffstat (limited to 'source3')
-rw-r--r-- | source3/include/nameserv.h | 18 | ||||
-rw-r--r-- | source3/nameannounce.c | 101 | ||||
-rw-r--r-- | source3/nameannounce.doc | 137 | ||||
-rw-r--r-- | source3/namebrowse.c | 2 | ||||
-rw-r--r-- | source3/namebrowse.doc | 69 | ||||
-rw-r--r-- | source3/namedbname.doc | 155 | ||||
-rw-r--r-- | source3/namedbresp.doc | 48 | ||||
-rw-r--r-- | source3/nameelect.c | 35 | ||||
-rw-r--r-- | source3/nameelect.doc | 207 | ||||
-rw-r--r-- | source3/namelogon.doc | 9 | ||||
-rw-r--r-- | source3/namepacket.doc | 106 | ||||
-rw-r--r-- | source3/namequery.doc | 56 | ||||
-rw-r--r-- | source3/nameresp.c | 605 | ||||
-rw-r--r-- | source3/nameresp.doc | 144 | ||||
-rw-r--r-- | source3/nameserv.c | 1599 | ||||
-rw-r--r-- | source3/nameserv.doc | 384 | ||||
-rw-r--r-- | source3/nameservreply.doc | 189 | ||||
-rw-r--r-- | source3/nameservresp.doc | 188 | ||||
-rw-r--r-- | source3/namework.c | 52 | ||||
-rw-r--r-- | source3/namework.doc | 22 |
20 files changed, 1361 insertions, 2765 deletions
diff --git a/source3/include/nameserv.h b/source3/include/nameserv.h index 64a71b89ba..de5e492644 100644 --- a/source3/include/nameserv.h +++ b/source3/include/nameserv.h @@ -20,6 +20,8 @@ */ +#define GET_TTL(ttl) ((ttl)?MIN(ttl,lp_max_ttl()):lp_max_ttl()) + /* NTAS uses 2, NT uses 1, WfWg uses 0 */ #define MAINTAIN_LIST 2 #define ELECTION_VERSION 1 @@ -33,9 +35,13 @@ #define NMB_REG 0x05 /* see rfc1002.txt 4.2.2,3,5,6,7,8 */ #define NMB_REG_REFRESH 0x09 /* see rfc1002.txt 4.2.4 */ #define NMB_REL 0x06 /* see rfc1002.txt 4.2.9,10,11 */ -#define NMB_WAIT_ACK 0x07 /* see rfc1002.txt 4.2.17 */ +#define NMB_WAIT_ACK 0x07 /* see rfc1002.txt 4.2.16 */ /* XXXX what about all the other types?? 0x1, 0x2, 0x3, 0x4, 0x8? */ +#define FIND_SELF 0x01 +#define FIND_WINS 0x02 +#define FIND_LOCAL 0x04 + /* NetBIOS flags */ #define NB_GROUP 0x80 #define NB_PERM 0x02 @@ -83,13 +89,14 @@ enum master_state { MST_NONE, MST_WON, MST_MSB, MST_BROWSER, MST_DOMAIN }; enum state_type { - NAME_STATUS_MASTER_CHECK, - NAME_STATUS_CHECK, + NAME_STATUS_PDC_SRV_CHK, + NAME_STATUS_SRV_CHK, + NAME_REGISTER_CHALLENGE, NAME_REGISTER, NAME_RELEASE, NAME_QUERY_CONFIRM, NAME_QUERY_SYNC, - NAME_QUERY_MST_SRV_CHK, + NAME_QUERY_PDC_SRV_CHK, NAME_QUERY_SRV_CHK, NAME_QUERY_FIND_MST, NAME_QUERY_MST_CHK @@ -189,7 +196,8 @@ struct response_record BOOL bcast; BOOL recurse; - struct in_addr to_ip; + struct in_addr send_ip; + struct in_addr reply_to_ip; int num_msgs; diff --git a/source3/nameannounce.c b/source3/nameannounce.c index 14c16cadc1..e4ef868bb6 100644 --- a/source3/nameannounce.c +++ b/source3/nameannounce.c @@ -70,12 +70,16 @@ void announce_request(struct work_record *work, struct in_addr ip) CVAL(p,0) = ANN_AnnouncementRequest; p++; - CVAL(p,0) = work->token; /* flags?? XXXX probably a token*/ + CVAL(p,0) = work->token; /* (local) unique workgroup token id */ p++; StrnCpy(p,myname,16); strupper(p); p = skip_string(p,1); + /* XXXX note: if we sent the announcement request to 0x1d instead + of 0x1e, then we could get the master browser to announce to + us instead of the members of the workgroup. wha-hey! */ + send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,PTR_DIFF(p,outbuf), myname,work->work_group,0x20,0x1e,ip,*iface_ip(ip)); } @@ -118,17 +122,21 @@ void sync_server(enum state_type state, char *serv_name, char *work_name, { add_browser_entry(serv_name, name_type, work_name, 0, ip); - if (state == NAME_QUERY_MST_SRV_CHK) - { - /* announce ourselves as a master browser to serv_name */ - do_announce_request(myname, serv_name, ANN_MasterAnnouncement, + if (state == NAME_STATUS_PDC_SRV_CHK) + { + /* announce ourselves as a master browser to serv_name */ + do_announce_request(myname, serv_name, ANN_MasterAnnouncement, 0x20, 0, ip); - } + } } /**************************************************************************** construct a host announcement unicast + + this function should not be used heavily, and only when we are _not_ + a master browser and _not_ a primary domain controller. + **************************************************************************/ void announce_backup(void) { @@ -158,18 +166,11 @@ void announce_backup(void) if (!work) continue; + if (AM_MASTER(work) && AM_DOMCTL(work)) continue; + /* found one: announce it across all domains */ for (d = subnetlist; d; d = d->next) { - int type=0; - - if (AM_DOMCTL(work)) { - type = 0x1b; - } else if (AM_MASTER(work)) { - type = 0x1d; - } else { - continue; - } DEBUG(2,("sending announce backup %s workgroup %s(%d)\n", inet_ntoa(d->bcast_ip),work->work_group, @@ -185,12 +186,31 @@ void announce_backup(void) p += 5; p++; - send_mailslot_reply(BROWSE_MAILSLOT, + if (!AM_DOMCTL(work)) + { + /* only ask for a list of backup domain controllers + if we are not a domain controller ourselves */ + + send_mailslot_reply(BROWSE_MAILSLOT, + ClientDGRAM,outbuf, + PTR_DIFF(p,outbuf), + myname, work->work_group, + 0x0,0x1b,d->bcast_ip, + *iface_ip(d->bcast_ip)); + } + + if (!AM_MASTER(work)) + { + /* only ask for a list of master browsers if we + are not a master browser ourselves */ + + send_mailslot_reply(BROWSE_MAILSLOT, ClientDGRAM,outbuf, PTR_DIFF(p,outbuf), myname, work->work_group, - 0x0,type,d->bcast_ip, + 0x0,0x1b,d->bcast_ip, *iface_ip(d->bcast_ip)); + } } } } @@ -222,12 +242,12 @@ static void do_announce_host(int command, StrnCpy(p+5,server_name,16); strupper(p+5); - CVAL(p,21) = 2; /* major version */ - CVAL(p,22) = 2; /* minor version */ + CVAL(p,21) = 0x02; /* major version */ + CVAL(p,22) = 0x02; /* minor version */ SIVAL(p,23,server_type); SSVAL(p,27,0xaa55); /* browse signature */ - SSVAL(p,29,1); /* browse version */ + SSVAL(p,29,0x001f); /* browse version: CIFS draft 1.0 indicates 0x001f */ strcpy(p+31,server_comment); p += 31; @@ -243,6 +263,28 @@ static void do_announce_host(int command, /**************************************************************************** + remove all samba's server entries + ****************************************************************************/ +void remove_my_servers(void) +{ + struct subnet_record *d; + for (d = subnetlist; d; d = d->next) + { + struct work_record *work; + for (work = d->workgrouplist; work; work = work->next) + { + struct server_record *s; + for (s = work->serverlist; s; s = s->next) + { + if (!strequal(myname,s->serv.name)) continue; + announce_server(d, work, s->serv.name, s->serv.comment, 0, 0); + } + } + } +} + + +/**************************************************************************** announce a server entry ****************************************************************************/ void announce_server(struct subnet_record *d, struct work_record *work, @@ -309,6 +351,11 @@ void announce_host(void) struct server_record *s; BOOL announce = False; + /* must work on the code that does announcements at up to + 30 seconds later if a master browser sends us a request + announce. + */ + if (work->needannounce) { /* drop back to a max 3 minute announce - this is to prevent a single lost packet from stuffing things up for too long */ @@ -364,7 +411,7 @@ void announce_host(void) least 15 minutes. this actually gets done in search_and_sync_workgroups() via the - NAME_QUERY_MST_SRV_CHK command, if there is a response from the + NAME_QUERY_PDC_SRV_CHK command, if there is a response from the name query initiated here. see response_name_query() **************************************************************************/ void announce_master(void) @@ -419,9 +466,9 @@ void announce_master(void) ip = ipzero; queue_netbios_pkt_wins(d,ClientNMB,NMB_QUERY, - NAME_QUERY_MST_SRV_CHK, + NAME_QUERY_PDC_SRV_CHK, work->work_group,0x1b,0,0, - False, False, ip); + False, False, ip, ip); } else { @@ -429,9 +476,9 @@ void announce_master(void) for (d2 = subnetlist; d2; d2 = d2->next) { queue_netbios_packet(d,ClientNMB,NMB_QUERY, - NAME_QUERY_MST_SRV_CHK, + NAME_QUERY_PDC_SRV_CHK, work->work_group,0x1b,0,0, - True, False, d2->bcast_ip); + True, False, d2->bcast_ip, d2->bcast_ip); } } } @@ -463,9 +510,9 @@ void announce_master(void) /* check the existence of a pdc for this workgroup, and if one exists at the specified ip, sync with it and announce ourselves as a master browser to it */ - queue_netbios_pkt_wins(d,ClientNMB, NMB_QUERY,NAME_QUERY_MST_SRV_CHK, + queue_netbios_pkt_wins(d,ClientNMB, NMB_QUERY,NAME_QUERY_PDC_SRV_CHK, work->work_group,0x1b, 0, 0, - bcast, False, ip); + bcast, False, ip, ip); } } } diff --git a/source3/nameannounce.doc b/source3/nameannounce.doc new file mode 100644 index 0000000000..d550a8380d --- /dev/null +++ b/source3/nameannounce.doc @@ -0,0 +1,137 @@ +this module deals with announcements: the sending of announcement requests +and the sending of announcements either to refresh other servers' records +or as a response to announcement requests. + + +/************************************************************************* + announce_master() + *************************************************************************/ + +this function is responsible for announcing samba as a master browser +to all known primary domain controllers. + +this announcement is sent out at CHECK_TIME_MST_ANNOUNCE minute +intervals, only if samba is a master browser on one or more of +its local interfaces. + +if no domain controller has been specified (lp_domain_controller()) +samba goes through its list of servers looking for primary domain +controllers. when it finds one (other than itself) it will either +initiate a NAME_QUERY_PDC_SRV_CHK by broadcast or with a WINS +server. this will result in a NAME_STATUS_PDC_SRV_CHK, which +will result in a sync browse list and an announcement +ANN_MasterAnnounce being sent (see sync_server()). + +if a domain controller has been specified, samba will search for +a primary domain controller for its workgroup (either by directed +packet or by broadcast if it cannot resolve the domain controller +name using DNS), which results in the same action as listed above. + + +/************************************************************************* + announce_host() + *************************************************************************/ + +this complex-looking function is responsible for announcing samba's +existence to other servers by broadcast. the actual announcement +is carried out by announce_server(). + +the time period between samba's announcement will stretch from one +minute to twelve minutes by one minute. if samba has received an +announce request from a master browser, then it should answer at +any random interval between zero and thirty seconds after the +request is received. this is to ensure that the master browser +does not get overloaded with responses! + + +/************************************************************************* + announce_server() + *************************************************************************/ + +this function is responsible for sending announcement packets. +these packets are received by other servers, which will then +update their records accordingly: what services we have, our +name, our comment field and our time to live (to name a few). + +if we are a master browser, then using do_announce_host() we +must send an announcement notifying members of that workgroup +that we are their master browser, and another announcement +indicating to all backup browsers and master browsers that +we are a master browser. + +(note: if another master browser receives this announcement +and thinks that it is also the master browser for this +workgroup, it stops being a master browser and forces an +election). + +if we are not a master browser, then we send an announcement +notifying the master browser that we are a member of its +workgroup. + + +/************************************************************************* + remove_my_servers() + *************************************************************************/ + +this function is responsible for informing other servers that +samba is about to go down. it announces, on all subnets, that +samba's time to live is zero and that it has no services. + + +/************************************************************************* + do_announce_host() + *************************************************************************/ + +this function is responsible for sending out an announcement +MAILSLOT browse packet. it contains information such as the +time to live, name of the server, services that the server +offers etc. + +the format of this MAILSLOT browse packet is described in +draft-heizer-cifs-v1-spec-00.txt 3.9.50.4.1 page 165-6. + + +/************************************************************************* + announce_backup() + *************************************************************************/ + +this function is responsible for getting master browsers and domain +controllers to send us lists of backup servers. this is done by +sending an ANN_GetBackupListReq browse mailslot. + +the master browser, or primary domain controller, should respond +with an ANN_GetBackupListResp browse mailslot containing the list +of backup servers. + + +/************************************************************************* + sync_server() + *************************************************************************/ + +this function is responsible for initiating a sync browse list +sequence and, if necessary, carrying out an ANN_MasterAnnouncement +to the primary domain controller (that we are also sync'ing +browse lists with). + +see nameservresp.c:response_name_status_check(). + + +/************************************************************************* + announce_request() + *************************************************************************/ + +this function is responsible for sending an announcement request to +another server. this server should respond with an announcement. + +if the announce request is sent to WORKGROUP(0x1e) then members of +the workgroup will respond (with ANN_HostAnnounce packets) + +if the announce request is sent to WORKGROUP(0x1d) then the master +browser of the workgroup should respond (ANN_LocalMasterAnnounce). +this is untested. + +if the announce request is sent to ^1^2__MSBROWSE__^2(0x1) then +(and this is pure speculation), all backup browsers and master +browsers should respond with ANN_DomainAnnounce packets. +this is untested. + diff --git a/source3/namebrowse.c b/source3/namebrowse.c index 4b3e691ce0..e62aa1ec6d 100644 --- a/source3/namebrowse.c +++ b/source3/namebrowse.c @@ -178,7 +178,7 @@ static void start_sync_browse_entry(struct browse_cache_record *b) /* see response_netbios_packet() or expire_netbios_response_entries() */ queue_netbios_packet(d,ClientNMB,NMB_QUERY,NAME_QUERY_SYNC, b->group,0x20,0,0, - False,False,b->ip); + False,False,b->ip,b->ip); } b->synced = True; diff --git a/source3/namebrowse.doc b/source3/namebrowse.doc new file mode 100644 index 0000000000..889abdacae --- /dev/null +++ b/source3/namebrowse.doc @@ -0,0 +1,69 @@ +this module deals with queueing servers that samba must sync browse +lists with. it will always issue a name query immediately before +actually carrying out the NetServerEnum call, to ensure that time +is not wasted by a remote server's failure. + +this module was created to minimise the amount of NetServerEnum calls +that samba may be asked to perform, by maintaining the name of a server +for up to a minute after the NetServerEnum call was issued, and +disallowing further NetServerEnum calls to this remote server until +the entry is removed. + +samba can ask for a NetServerEnum call to be issued to grab a remote +server's list of servers and workgroups either in its capacity as +a primary domain controller (domain master browser), as a local +master browser. + +samba does not deal with becoming a backup master browser properly +at present. + + +/************************************************************************* + do_browser_lists() + *************************************************************************/ + +this function is responsible for finding an appropriate entry in the +sync browser cache, initiating a name query (which results in a +NetServerEnum call if there is a positive response), and then +removing all entries that have been actioned and have been around +for over a minute. + + +/************************************************************************* + start_sync_browse_entry() + *************************************************************************/ + +this function is responsible for initiating a name query. if a +positive response is received, then this will result in a +NetServerEnum api call. + +samba will only initiate this process if it is a master browser +for this workgroup. + + +/************************************************************************* + add_browser_entry() + *************************************************************************/ + +this function is responsible for adding a browser into the list of +servers to sync browse lists with. if the server entry has already +been added and syncing browse lists has already been initiated, it +will not be added again. + + +/************************************************************************* + expire_browse_cache() + *************************************************************************/ + +this function is responsible for removing entries that have had the +sync browse list initiated (whether that succeeded or not is beyond +this function's scope) and have been in the cache for a while. + + +/************************************************************************* + add_browse_entry() + *************************************************************************/ + +this function is responsible for adding a new entry into the list +of servers to sync browse lists with at some point in the near future. + diff --git a/source3/namedbname.doc b/source3/namedbname.doc new file mode 100644 index 0000000000..3419fe1818 --- /dev/null +++ b/source3/namedbname.doc @@ -0,0 +1,155 @@ +this module deals with the NetBIOS name database for samba. it deals +directly with adding, removing, finding, loading and saving of names. + +/************************************************************************* + search_for_name() + *************************************************************************/ + +this function is responsible for finding a name in the appropriate part +of samba's NetBIOS name database. if the name cannot be found, then it +should look the name up using DNS. later modifications will be to +forward the request on to another WINS server, should samba not be able +to find out about the requested name (this will be implemented through +issuing a new type of samba 'state'). + +the name is first searched for in the NetBIOS cache. if it cannot be +found, then it if the name looks like it's a server-type name (0x20 +0x0 or 0x1b) then DNS is used to look for the name. + +if DNS fails, then a record of this failure is kept. if it succeeds, then +a new NetBIOS entry is added. + +the successfully found name is returned. on failure, NULL is returned. + + +/************************************************************************* + expire_names() + *************************************************************************/ + +this function is responsible for removing old NetBIOS names from its +database. no further action is required. + +for over-zealous WINS systems, the use of query_refresh_names() is +recommended. this function initiates polling of hosts that have +registered with samba in its capacity as a WINS server. an alternative +means to achieve the same end as query_refresh_names() is to +reduce the time to live when the name is registered with samba, +except that in this instance the responsibility for refreshing the +name is with the owner of the name, not the server with which the name +is registered. + + +/************************************************************************* + add_netbios_entry() + *************************************************************************/ + +this function is responsible for adding or updating a NetBIOS name +in the database. into the local interface records, the only names +that will be added are those of primary domain controllers and +samba's own names. into the WINS records, all names are added. + +the name to be added / updated will be looked up in the records. +if it is found, then we will not overwrite the entry if the flag +'newonly' is True, or if the name is being added as a non-SELF +(non-samba) name and the records indicate that samba owns the +name. + +otherwise, the name is added or updated with the new details. + + +/************************************************************************* + remove_netbios_entry() + *************************************************************************/ + +this function is responsible for removing a NetBIOS entry from +the database. the name is searched for in the records using +find_name_search(). if the ip is zero, then the ip is ignored. + +the name is removed if the expected source (e.g SELF, REGISTER) +matches that in the database. + + +/************************************************************************* + load_netbios_names() + *************************************************************************/ + +this function is responsible for loading any NetBIOS names that samba, +in its WINS capacity, has written out to disk. all the relevant details +are recorded in this file, including the time-to-live. should the +time left to live be small, the name is not added back in to samba's +WINS database. + + +/************************************************************************* + dump_names() + *************************************************************************/ + +this function is responsible for outputting NetBIOS names in two formats. +firstly, as debugging information, and secondly, all names that have been +registered with samba in its capacity as a WINS server are written to +disk. + +writing all WINS names allows two things. firstly, if samba's NetBIOS +daemon dies or is terminated, on restarting the daemon most if not all +of the registered WINS names will be preserved (which is a good reason +why query_netbios_names() should be used). + + +/************************************************************************* + find_name_search() + *************************************************************************/ + +this function is a wrapper around find_name(). find_name_search() can +be told whether to search for the name in a local subnet structure or +in the WINS database. on top of this, it can be told to search only +for samba's SELF names. + +if it finds the name in the WINS database, it will set the subnet_record +and also return the name it finds. + + +/************************************************************************* + find_name() + *************************************************************************/ + +this function is a low-level search function that searches a single +interface's NetBIOS records for a name. if the ip to be found is +zero then the ip address is ignored. this is to enable a name to +be found without knowing its ip address, and also to find the exact +name if a large number of group names are added with different ip +addresses. + + +/************************************************************************* + remove_name() + *************************************************************************/ + +this function is responsible for removing a specific NetBIOS entry +from a subnet list's records. only if the pointer to the entry is +in the list will the name be removed. + + +/************************************************************************* + add_name() + *************************************************************************/ + +this function is responsible for adding a NetBIOS entry into a +subnet list's records. + + +/************************************************************************* + ms_browser_name() + *************************************************************************/ + +this function returns True if the NetBIOS name passed to it is +^1^2__MSBROWSE__^2^1 + + +/************************************************************************* + name_equal() + *************************************************************************/ + +this function returns True if the two NetBIOS names passed to it +match in name, type and scope: the NetBIOS names are equal. + + diff --git a/source3/namedbresp.doc b/source3/namedbresp.doc new file mode 100644 index 0000000000..68db43aec2 --- /dev/null +++ b/source3/namedbresp.doc @@ -0,0 +1,48 @@ +module namedbresp deals with the maintenance of the list of expected +responses - creating, finding and removal. + +module nameresp deals with the initial transmission, re-transmission +and time-out of netbios response records. + + +/************************************************************************* + find_response_record() + *************************************************************************/ + +this function is responsible for matching the unique response transaction +id with an expected response record. as a side-effect of this search, +it will find the subnet (or the WINS pseudo-subnet) that samba expected +the response to come from. + + +/************************************************************************* + make_response_queue_record() + *************************************************************************/ + +this function is responsible for creating a response record, which will +be queued awaiting a response. + +the number of retries is set to 4, and the retry period set to 1 second. +if no response is received, then the packet is re-transmitted, which is +why so much information is stored in the response record. + +the number of expected responses queued is kept, so listen_for_packets() +knows it must time-out after 1 second if one or more responses are +expected. + + +/************************************************************************* + remove_response_record() + *************************************************************************/ + +this function is responsible for removing a response record from the +expected response queue. the number of expected responses is decreased. + + +/************************************************************************* + add_response_record() + *************************************************************************/ + +this function is responsible for adding the response record created by +make_response_queue_record() into the appropriate response record queue. + diff --git a/source3/nameelect.c b/source3/nameelect.c index bb219415b8..386e7b0bab 100644 --- a/source3/nameelect.c +++ b/source3/nameelect.c @@ -2,7 +2,7 @@ Unix SMB/Netbios implementation. Version 1.9. NBT netbios routines and daemon - version 2 - Copyright (C) Andrew Tridgell 1994-1995 + Copyright (C) Andrew Tridgell 1994-1996 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -18,11 +18,17 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + Module name: nameelect.c + Revision History: 14 jan 96: lkcl@pires.co.uk added multiple workgroup domain master support + 04 jul 96: lkcl@pires.co.uk + added system to become a master browser by stages. + + */ #include "includes.h" @@ -75,7 +81,7 @@ void check_master_browser(void) { queue_netbios_packet(d,ClientNMB,NMB_QUERY,NAME_QUERY_MST_CHK, work->work_group,0x1d,0,0, - True,False,d->bcast_ip); + True,False,d->bcast_ip,d->bcast_ip); } } } @@ -134,7 +140,7 @@ void send_election(struct subnet_record *d, char *group,uint32 criterion, bzero(outbuf,sizeof(outbuf)); p = outbuf; - CVAL(p,0) = 8; /* election */ + CVAL(p,0) = ANN_Election; /* election */ p++; CVAL(p,0) = (criterion == 0 && timeup == 0) ? 0 : ELECTION_VERSION; @@ -166,13 +172,13 @@ void name_unregister_work(struct subnet_record *d, char *name, int name_type) if (!(work = find_workgroupstruct(d, name, False))) return; - if (special_browser_name(name, name_type) || + if (ms_browser_name(name, name_type) || (AM_MASTER(work) && strequal(name, lp_workgroup()) == 0 && (name_type == 0x1d || name_type == 0x1b))) { int remove_type = 0; - if (special_browser_name(name, name_type)) + if (ms_browser_name(name, name_type)) remove_type = SV_TYPE_MASTER_BROWSER|SV_TYPE_DOMAIN_MASTER; if (name_type == 0x1d) remove_type = SV_TYPE_MASTER_BROWSER; @@ -247,7 +253,7 @@ void become_master(struct subnet_record *d, struct work_record *work) { case MST_NONE: /* while we were nothing but a server... */ { - work->state = MST_WON; /* election win was successful */ + work->state = MST_WON; /* ... an election win was successful */ work->ElectionCriterion |= 0x5; @@ -264,7 +270,7 @@ void become_master(struct subnet_record *d, struct work_record *work) } case MST_WON: /* while nothing had happened except we won an election... */ { - work->state = MST_MSB; /* registering MSBROWSE was successful */ + work->state = MST_MSB; /* ... registering MSBROWSE was successful */ /* add server entry on successful registration of MSBROWSE */ add_server_entry(d,work,work->work_group,domain_type,0,myname,True); @@ -276,9 +282,9 @@ void become_master(struct subnet_record *d, struct work_record *work) break; } - case MST_MSB: /* while we were still only registered MSBROWSE state */ + case MST_MSB: /* while we were still only registered MSBROWSE state... */ { - work->state = MST_BROWSER; /* registering WORKGROUP(1d) was successful */ + work->state = MST_BROWSER; /* ... registering WORKGROUP(1d) succeeded */ /* update our server status */ work->ServerType |= SV_TYPE_MASTER_BROWSER; @@ -296,12 +302,16 @@ void become_master(struct subnet_record *d, struct work_record *work) /* add domain master name */ add_my_name_entry(d,work->work_group,0x1b,NB_ACTIVE ); } + else + { + DEBUG(2,("samba not configured as a domain master: no third stage.\n")); + } break; } case MST_BROWSER: /* while we were still a master browser... */ { - work->state = MST_DOMAIN; /* registering WORKGROUP(1b) was successful */ + work->state = MST_DOMAIN; /* ... registering WORKGROUP(1b) succeeded */ /* update our server status */ if (lp_domain_master()) @@ -513,6 +523,11 @@ void process_election(struct packet_struct *p,char *buf) /**************************************************************************** checks whether a browser election is to be run on any workgroup + + this function really ought to return the time between election + packets (which depends on whether samba intends to be a domain + master or a master browser) in milliseconds. + ***************************************************************************/ BOOL check_elections(void) { diff --git a/source3/nameelect.doc b/source3/nameelect.doc new file mode 100644 index 0000000000..b8e6b56ad9 --- /dev/null +++ b/source3/nameelect.doc @@ -0,0 +1,207 @@ + +the module nameelect.c deals with initiating, winning, losing +browsing elections, and checking if browsers are still around, +and the consequences of getting involved in all this. + +an election packet can be received at any time, which will initiate +an election. samba can also detect that there is no longer a +master browser and will initiate an election. + +there is one way to become a master browser, but there are two +ways to un-become a master browser. if you lose an election, you +must stop being a master browser. if you fail to register your +unique special browser names (either on your local subnet or with +the WINS server) then you must stop being a master browser. + +this is a double fail-safe mechanism to ensure that there is only +one master browser per workgroup per subnet (and one primary domain +controller - domain master browser - per domain (workgroup) per +wide area network). + +(a wide area network is created when one or more servers on a +broadcast-isolated subnet point to the same WINS server). + + +/************************************************************************* + check_elections() + *************************************************************************/ + +this function returns True if samba is in the process of running an +election on any of its interfaces. a better version of this function +should return the time-out period in between election packets, in +milliseconds. + + +/************************************************************************* + process_election() + *************************************************************************/ + +this function is responsible for dealing with the receipt of an election +browse MAILSLOT packet. + +if samba is running an election, it checks the criteria in the packet +received using win_election() to see if it has lost the election or if +it should join in the election. + +if it loses the election, then it becomes a non-master. + + +/************************************************************************* + win_election() + *************************************************************************/ + +this function returns True if samba has won an election. the criteria +in order of precedence are: + +the election version; the election criteria; the time since samba was +started; and as a last resort, a name comparison is used. + + +/************************************************************************* + run_elections() + *************************************************************************/ + +this function is responsible for sending out election packets if +samba is running in an election. once the fourth packet is sent +out, it is assumed that we have won, and samba initiates becoming +a master browser. + +(it looks like samba sends out an extra packet just to be sure...) + + +/************************************************************************* + become_nonmaster() + *************************************************************************/ + +this function is responsible for down-grading samba's status from +either domain master to master browser or nothing, or master browser +to nothing, depending on its current status. + +samba can become a non-master in three ways: by losing an election - +see process_election(); by having one of its special browser names +de-registered - see name_unregister_work(); by receiving and +processing a browser reset packet - see process_reset_browser(). + +when samba stops being a domain master, it must release its unique +0x1b name. when samba stops being a master browser, it must release +its unique 0x1d name. + +becoming non-master is done on a per-subnet basis. + + +/************************************************************************* + become_master() + *************************************************************************/ + +this function is responsible for slowly turning samba into a +master browser or a domain master (primary domain controller). + + +this is done in stages. note that this could take a while, +particularly on a broadcast subnet, as we have to wait for +the implicit registration of each name to be accepted. + +as each name is successfully registered, become_master() is +called again via name_register_work(), in order to initiate +the next stage (see dead_netbios_entry() - deals with implicit +name registration and response_name_reg() - deals with explicit +registration with a WINS server). + +stage 1: was MST_NONE - go to MST_NONE and register ^1^2__MSBROWSE__^2^1. +stage 2: was MST_WON - go to MST_MSB and register WORKGROUP(0x1d) +stage 3: was MST_MSB - go to MST_BROWSER and register WORKGROUP(0x1b) +stage 4: was MST_BROWSER - go to MST_DOMAIN (do not pass GO, do not...) + +note that this code still does not cope with the distinction +between different types of nodes, particularly between M and P +nodes (see rfc1001.txt). that will be developed later. + + +/************************************************************************* + name_register_work() + *************************************************************************/ + +this function is called when a NetBIOS name is successfully +registered. it will add the registered name into samba's NetBIOS +records. + +it has the additional responsibility that when samba is becoming +a master browser, it must initiate the next stage in the progress +towards becoming a master browser. + +implicit name registration is done through dead_netbios_entry() +by time-out. explicit name registration is done through +response_name_reg() with a WINS server. + + +/************************************************************************* + name_unregister_work() + *************************************************************************/ + +this function is called when there is an objection to a NetBIOS +name being registered. this will always be done through a negative +response to a name registration, whether it be by a host that +already owns the unique name being registered on a subnet, or +by a WINS server. + +the name being objected to must be removed from samba's records. + +it has the additional responsibility of checking whether samba is +currently a master browser or not, and if so it should initiate +becoming a non-master. + + + +/************************************************************************* + send_election() + *************************************************************************/ + +this function is responsible for sending a browse mailslot +datagram election packet (of type ANN_Election). it constructs +the packet with all the relevant info needed to participate: +election version; election criteria; time since startup and +our name. + +this function can be used to ensure that initiate but lose an +election by specifying a criteria and time up of zero. this +is necessary if we are a master browser and we are about to +go down (politely!) - see nmbd.c:sig_term(). + + +/************************************************************************* + browser_gone() + *************************************************************************/ + +this function is responsible for dealing with the instance when +the master browser we thought was present on a subnet is no longer +responding. + +if it is samba's workgroup, and it's a local interface, samba +detects that it can participate in an election on that interface +and potentially become a master browser or domain master. + +if it's a remote subnet or not one of samba's workgroups, then +samba will force an election (which it is not obliged to do) and +will remove that workgroup and the servers contained in it from +its records. maybe this functionality isn't a good idea. + + +/************************************************************************* + check_master_browser() + *************************************************************************/ + +this function is responsible for periodically checking whether +master browsers that samba expects to be alive are alive. this +is done every CHECK_TIME_MST_BROWSE minutes. + +for every workgroup record for which samba is not a master browser, +on both local and remote interfaces, samba will initiate a +broadcast query for a master browser on that subnet. + +(browser_gone() will be called to deal with the case where no +response is received to the NAME_QUERY_MST_CHK initiated here. +no action is required when a response _is_ received, however: +see nameservresp.c:response_process() and dead_netbios_entry() +for details) + + diff --git a/source3/namelogon.doc b/source3/namelogon.doc new file mode 100644 index 0000000000..0b17d135ab --- /dev/null +++ b/source3/namelogon.doc @@ -0,0 +1,9 @@ +this module deals with the first stage of domain logons. there is much +more work to be done on this: it's all totally undocumented. + + +/************************************************************************* + process_logon_packet() + *************************************************************************/ + +a function that processes logon packets (the most helpful comment yet :-). diff --git a/source3/namepacket.doc b/source3/namepacket.doc new file mode 100644 index 0000000000..c9ec19418b --- /dev/null +++ b/source3/namepacket.doc @@ -0,0 +1,106 @@ +this module deals with packets: sending, receiving, queueing +and some basic interpretation (e.g it excludes datagram +error packets at the moment). + +the packet queueing mechanism was originally introduced when +samba dealt with responses by sending a packet, receiving +packets and queueing all packets that didn't match up with +the response expected. this is fine in a single-thread +environment, but samba now deals with response packets by +queueing the responses. to some extent, therefore, this +queue_packet mechanism is redundant. + + +/************************************************************************* + send_mailslot_reply() + *************************************************************************/ + +this function is responsible for sending a MAILSLOT packet. + +it will _not_ send packets to the pseudo WINS subnet's address of +255.255.255.255: this would be disastrous. + +each packet sent out has a unique transaction identifier. this is done +so that responses can be matched later with the original purpose for +the packet being sent out in the first place. + + +/************************************************************************* + listen_for_packets() + *************************************************************************/ + +this function is responsible for reading NMB and DGRAM packets, and then +queueing them. it will normally time-out for NMBD_SELECT_LOOP seconds, but +if there is an election currently running or we are expecting a response +then this time is reduced to 1 second. + +note: the time-out period needs refining to the millisecond level. + + +/************************************************************************* + queue_packet() + *************************************************************************/ + +this function is responsible for queueing any NMB and DGRAM packets passed +to it. these packets will be removed from the queue in run_packet_queue(). + + +/************************************************************************* + run_packet_queue() + *************************************************************************/ + +this function is responsible for taking a packet off the queue, +identifying whether it is an NMB or a DGRAM packet, processing +it accordingly and deleting it. this process continues until +there are no more packets on the queue. + + +/************************************************************************* + process_nmb() + *************************************************************************/ + +this function receives a packet identified as a netbios packet. +it further identifies whether it is a response or a query packet. +by identifying the type of packet (name registration, query etc) +process_nmb() will call the appropriate function to deal with the +type of packet received. + + +/************************************************************************* + process_dgram() + *************************************************************************/ + +this function is responsible for identifying whether the datagram +packet received is a browser packet or a domain logon packet. it +also does some filtering of certain types of packets (e.g it +filters out error packets). + + +/************************************************************************* + reply_netbios_packet() + *************************************************************************/ + +this function is responsible for sending a reply to another NetBIOS +packet from another host. it can be used to send a reply to a name +registration, name release, name query or name status request. + +the reply can be either a positive or a negative one. + + +/************************************************************************* + initiate_netbios_packet() + *************************************************************************/ + +this function is responsible for construction a netbios packet and sending +it. if the packet has not had a unique transaction id allocated to it, +then initiate_netbios_packet() will give it one. + + +/************************************************************************* + update_name_trn_id() + *************************************************************************/ + +this function is responsible for allocating unique transaction identifiers +for each new packet sent on the network. + + diff --git a/source3/namequery.doc b/source3/namequery.doc new file mode 100644 index 0000000000..77a75714e5 --- /dev/null +++ b/source3/namequery.doc @@ -0,0 +1,56 @@ +this module contains non-threaded versions of name status and name +query functions. if a multi-threaded nmbd was to be written, these +functions would be the starting point. + +at the moment, the expected response queueing system is used to +replace these functions without needing to multi-thread nmbd. + +these functions are used in smbclient and nmblookup at present to +avoid having the vast quantities of complex and unused code needed +to support even a simple name query (or providing stubs for the +unused side of these functions). + +there is a down-side to these functions, which is all microsoft's +fault. microsoft machines always always reply to queries on the +priveleged ports, rather than following the usual tcp/ip mechanism +of replying on the client's port (the exception to this i am led +to believe is windows nt 3.50). + +as a result of this, in order to receive a response to a name +query from a microsoft machine, we must be able to listen on +the priveleged netbios name server ports. this is simply not +possible with some versions of unix, unless you have root access. + +it is also not possible if you run smbclient or nmblookup on an +interface that already has been claimed by the netbios name server +daemon nmbd. + +all in all, i wish that microsoft would fix this. + +a solution does exist: nmbd _does_ actually reply on the client's +port, so if smbclient and nmblookup were to use nmbd as a proxy +forwarder of queries (or to use samba's WINS capabilities) then +a query could be made without needing access to the priveleged +ports. in order to do this properly, samba must implement secured +netbios name server functionality (see rfc1001.txt 15.1.6). + + +/************************************************************************* + name_query() + *************************************************************************/ + + + +/************************************************************************* + name_status() + *************************************************************************/ + + + +/************************************************************************* + _interpret_node_status() + *************************************************************************/ + + +this is a older version of interpret_node_status(). + diff --git a/source3/nameresp.c b/source3/nameresp.c index b153575116..0b38c4bb9a 100644 --- a/source3/nameresp.c +++ b/source3/nameresp.c @@ -2,7 +2,7 @@ Unix SMB/Netbios implementation. Version 1.9. NBT netbios library routines - Copyright (C) Andrew Tridgell 1994-1995 + Copyright (C) Andrew Tridgell 1994-1996 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -18,6 +18,8 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + Module name: nameresp.c + */ #include "includes.h" @@ -29,68 +31,12 @@ extern struct subnet_record *subnetlist; extern int DEBUGLEVEL; -static uint16 name_trn_id=0; -BOOL CanRecurse = True; extern pstring scope; -extern pstring myname; extern struct in_addr ipzero; extern struct in_addr ipgrp; -int num_response_packets = 0; - -/*************************************************************************** - updates the unique transaction identifier - **************************************************************************/ -static void update_name_trn_id(void) -{ - if (!name_trn_id) - { - name_trn_id = (time(NULL)%(unsigned)0x7FFF) + (getpid()%(unsigned)100); - } - name_trn_id = (name_trn_id+1) % (unsigned)0x7FFF; -} - - -/*************************************************************************** - add an expected response record into the list - **************************************************************************/ -static void add_response_record(struct subnet_record *d, - struct response_record *n) -{ - struct response_record *n2; - - if (!d) return; - - if (!d->responselist) - { - d->responselist = n; - n->prev = NULL; - n->next = NULL; - return; - } - - for (n2 = d->responselist; n2->next; n2 = n2->next) ; - - n2->next = n; - n->next = NULL; - n->prev = n2; -} - - -/*************************************************************************** - remove an expected response record from the list - **************************************************************************/ -static void remove_response_record(struct subnet_record *d, - struct response_record *n) -{ - if (n->prev) n->prev->next = n->next; - if (n->next) n->next->prev = n->prev; +extern int num_response_packets; - if (d->responselist == n) d->responselist = n->next; - - free(n); -} - /*************************************************************************** deals with an entry before it dies @@ -99,7 +45,7 @@ static void dead_netbios_entry(struct subnet_record *d, struct response_record *n) { DEBUG(3,("Removing dead netbios entry for %s %s (num_msgs=%d)\n", - inet_ntoa(n->to_ip), namestr(&n->name), n->num_msgs)); + inet_ntoa(n->send_ip), namestr(&n->name), n->num_msgs)); switch (n->state) { @@ -124,7 +70,7 @@ static void dead_netbios_entry(struct subnet_record *d, see rfc1001.txt 15.5.2 */ remove_netbios_name(d1, n->name.name, n->name.name_type, - REGISTER, n->to_ip); + REGISTER, n->send_ip); } } } @@ -139,7 +85,7 @@ static void dead_netbios_entry(struct subnet_record *d, /* IMPORTANT: see response_netbios_packet() */ if (n->num_msgs == 0) - browser_gone(n->name.name, n->to_ip); + browser_gone(n->name.name, n->send_ip); break; } @@ -151,7 +97,7 @@ static void dead_netbios_entry(struct subnet_record *d, /* IMPORTANT: see response_name_release() */ - if (ismyip(n->to_ip)) + if (ismyip(n->send_ip)) { name_unregister_work(d,n->name.name,n->name.name_type); } @@ -163,6 +109,21 @@ static void dead_netbios_entry(struct subnet_record *d, break; } + case NAME_REGISTER_CHALLENGE: + { + /* name challenge: no reply. we can reply to the person that + wanted the unique name and tell them that they can have it + */ + + add_netbios_entry(d, n->name.name, n->name.name_type, + n->nb_flags, GET_TTL(0), REGISTER, + n->reply_to_ip, False, True); + + send_name_response(n->fd, n->response_id, NMB_REG, + True, True, + &n->name, n->nb_flags, GET_TTL(0), n->reply_to_ip); + break; + } case NAME_REGISTER: { /* if no response received, and we are using a broadcast registration @@ -178,7 +139,7 @@ static void dead_netbios_entry(struct subnet_record *d, /* IMPORTANT: see response_name_reg() */ name_register_work(d,n->name.name,n->name.name_type, - n->nb_flags, n->ttl, n->to_ip, n->bcast); + n->nb_flags, n->ttl, n->send_ip, n->bcast); } else { @@ -204,88 +165,12 @@ static void dead_netbios_entry(struct subnet_record *d, } -/**************************************************************************** - initiate a netbios packet - ****************************************************************************/ -static void initiate_netbios_packet(uint16 *id, - int fd,int quest_type,char *name,int name_type, - int nb_flags,BOOL bcast,BOOL recurse, - struct in_addr to_ip) -{ - struct packet_struct p; - struct nmb_packet *nmb = &p.packet.nmb; - struct res_rec additional_rec; - char *packet_type = "unknown"; - int opcode = -1; - - if (!id) return; - - if (quest_type == NMB_STATUS) { packet_type = "nmb_status"; opcode = 0; } - if (quest_type == NMB_QUERY ) { packet_type = "nmb_query"; opcode = 0; } - if (quest_type == NMB_REG ) { packet_type = "nmb_reg"; opcode = 5; } - if (quest_type == NMB_REL ) { packet_type = "nmb_rel"; opcode = 6; } - - DEBUG(4,("initiating netbios packet: %s %s(%x) (bcast=%s) %s\n", - packet_type, name, name_type, BOOLSTR(bcast), inet_ntoa(to_ip))); - - if (opcode == -1) return; - - bzero((char *)&p,sizeof(p)); - - update_name_trn_id(); - - if (*id == 0xffff) *id = name_trn_id; /* allow resending with same id */ - - nmb->header.name_trn_id = *id; - nmb->header.opcode = opcode; - nmb->header.response = False; - nmb->header.nm_flags.bcast = bcast; - nmb->header.nm_flags.recursion_available = CanRecurse; - nmb->header.nm_flags.recursion_desired = recurse; - nmb->header.nm_flags.trunc = False; - nmb->header.nm_flags.authoritative = False; - nmb->header.rcode = 0; - nmb->header.qdcount = 1; - nmb->header.ancount = 0; - nmb->header.nscount = 0; - nmb->header.arcount = (quest_type==NMB_REG || quest_type==NMB_REL) ? 1 : 0; - - make_nmb_name(&nmb->question.question_name,name,name_type,scope); - - nmb->question.question_type = quest_type; - nmb->question.question_class = 0x1; - - if (quest_type == NMB_REG || quest_type == NMB_REL) - { - nmb->additional = &additional_rec; - bzero((char *)nmb->additional,sizeof(*nmb->additional)); - - nmb->additional->rr_name = nmb->question.question_name; - nmb->additional->rr_type = nmb->question.question_type; - nmb->additional->rr_class = nmb->question.question_class; - - nmb->additional->ttl = quest_type == NMB_REG ? lp_max_ttl() : 0; - nmb->additional->rdlength = 6; - nmb->additional->rdata[0] = nb_flags; - putip(&nmb->additional->rdata[2],(char *)iface_ip(to_ip)); - } - - p.ip = to_ip; - p.port = NMB_PORT; - p.fd = fd; - p.timestamp = time(NULL); - p.packet_type = NMB_PACKET; - - if (!send_packet(&p)) *id = 0xffff; - - return; -} - - /******************************************************************* remove old name response entries + XXXX retry code needs to be added, including a retry wait period and a count see name_query() and name_status() for suggested implementation. + ******************************************************************/ void expire_netbios_response_entries() { @@ -303,7 +188,7 @@ void expire_netbios_response_entries() /* resend the entry */ initiate_netbios_packet(&n->response_id, n->fd, n->quest_type, n->name.name, n->name.name_type, - n->nb_flags, n->bcast, n->recurse, n->to_ip); + n->nb_flags, n->bcast, n->recurse, n->send_ip); n->repeat_time += n->repeat_interval; /* XXXX ms needed */ n->repeat_count--; @@ -311,7 +196,6 @@ void expire_netbios_response_entries() else { nextn = n->next; - num_response_packets--; dead_netbios_entry (d,n); /* process the non-response */ remove_response_record(d,n); /* remove the non-response */ @@ -325,76 +209,15 @@ void expire_netbios_response_entries() /**************************************************************************** - reply to a netbios name packet - ****************************************************************************/ -void reply_netbios_packet(struct packet_struct *p1,int trn_id, - int rcode,int opcode, BOOL recurse, - struct nmb_name *rr_name,int rr_type,int rr_class,int ttl, - char *data,int len) -{ - struct packet_struct p; - struct nmb_packet *nmb = &p.packet.nmb; - struct res_rec answers; - char *packet_type = "unknown"; - - p = *p1; - - if (rr_type == NMB_STATUS) packet_type = "nmb_status"; - if (rr_type == NMB_QUERY ) packet_type = "nmb_query"; - if (rr_type == NMB_REG ) packet_type = "nmb_reg"; - if (rr_type == NMB_REL ) packet_type = "nmb_rel"; - - DEBUG(4,("replying netbios packet: %s %s\n", - packet_type, namestr(rr_name), inet_ntoa(p.ip))); - - nmb->header.name_trn_id = trn_id; - nmb->header.opcode = opcode; - nmb->header.response = True; - nmb->header.nm_flags.bcast = False; - nmb->header.nm_flags.recursion_available = recurse; - nmb->header.nm_flags.recursion_desired = True; - nmb->header.nm_flags.trunc = False; - nmb->header.nm_flags.authoritative = True; - - nmb->header.qdcount = 0; - nmb->header.ancount = 1; - nmb->header.nscount = 0; - nmb->header.arcount = 0; - nmb->header.rcode = 0; - - bzero((char*)&nmb->question,sizeof(nmb->question)); - - nmb->answers = &answers; - bzero((char*)nmb->answers,sizeof(*nmb->answers)); - - nmb->answers->rr_name = *rr_name; - nmb->answers->rr_type = rr_type; - nmb->answers->rr_class = rr_class; - nmb->answers->ttl = ttl; - - if (data && len) - { - nmb->answers->rdlength = len; - memcpy(nmb->answers->rdata, data, len); - } - - p.packet_type = NMB_PACKET; - - debug_nmb_packet(&p); - - send_packet(&p); -} - - -/**************************************************************************** wrapper function to override a broadcast message and send it to the WINS name server instead, if it exists. if wins is false, and there has been no WINS server specified, the packet will NOT be sent. ****************************************************************************/ -void queue_netbios_pkt_wins(struct subnet_record *d, +struct response_record *queue_netbios_pkt_wins(struct subnet_record *d, int fd,int quest_type,enum state_type state, char *name,int name_type,int nb_flags, time_t ttl, - BOOL bcast,BOOL recurse,struct in_addr to_ip) + BOOL bcast,BOOL recurse, + struct in_addr send_ip, struct in_addr reply_to_ip) { /* XXXX note: please see rfc1001.txt section 10 for details on this function: it is currently inappropriate to use this - it will do @@ -411,7 +234,7 @@ void queue_netbios_pkt_wins(struct subnet_record *d, if (!zero_ip(wins_ip)) { bcast = False; - to_ip = wins_ip; + send_ip = wins_ip; } else { @@ -421,48 +244,11 @@ void queue_netbios_pkt_wins(struct subnet_record *d, } } - if (zero_ip(to_ip)) return; + if (zero_ip(send_ip)) return NULL; - queue_netbios_packet(d,fd, quest_type, state, + return queue_netbios_packet(d,fd, quest_type, state, name, name_type, nb_flags, ttl, - bcast, recurse, to_ip); -} - -/**************************************************************************** - create a name query response record - **************************************************************************/ -static struct response_record * -make_response_queue_record(enum state_type state,int id,int fd, - int quest_type, char *name,int type, int nb_flags, time_t ttl, - BOOL bcast,BOOL recurse, struct in_addr ip) -{ - struct response_record *n; - - if (!name || !name[0]) return NULL; - - if (!(n = (struct response_record *)malloc(sizeof(*n)))) - return(NULL); - - n->response_id = id; - n->state = state; - n->fd = fd; - n->quest_type = quest_type; - make_nmb_name(&n->name, name, type, scope); - n->nb_flags = nb_flags; - n->ttl = ttl; - n->bcast = bcast; - n->recurse = recurse; - n->to_ip = ip; - - n->repeat_interval = 1; /* XXXX should be in ms */ - n->repeat_count = 4; - n->repeat_time = time(NULL) + n->repeat_interval; - - n->num_msgs = 0; - - num_response_packets++; /* count of total number of packets still around */ - - return n; + bcast, recurse, send_ip, reply_to_ip); } @@ -472,335 +258,30 @@ make_response_queue_record(enum state_type state,int id,int fd, master browsers (WORKGROUP(1d or 1b) or __MSBROWSE__(1)) to get complete lists across a wide area network ****************************************************************************/ -void queue_netbios_packet(struct subnet_record *d, +struct response_record *queue_netbios_packet(struct subnet_record *d, int fd,int quest_type,enum state_type state,char *name, int name_type,int nb_flags, time_t ttl, - BOOL bcast,BOOL recurse, struct in_addr to_ip) + BOOL bcast,BOOL recurse, + struct in_addr send_ip, struct in_addr reply_to_ip) { struct in_addr wins_ip = ipgrp; struct response_record *n; uint16 id = 0xffff; /* ha ha. no. do NOT broadcast to 255.255.255.255: it's a pseudo address */ - if (ip_equal(wins_ip, to_ip)) return; + if (ip_equal(wins_ip, send_ip)) return NULL; initiate_netbios_packet(&id, fd, quest_type, name, name_type, - nb_flags, bcast, recurse, to_ip); + nb_flags, bcast, recurse, send_ip); - if (id == 0xffff) return; + if (id == 0xffff) return NULL; if ((n = make_response_queue_record(state,id,fd, quest_type,name,name_type,nb_flags,ttl, - bcast,recurse,to_ip))) + bcast,recurse,send_ip,reply_to_ip))) { add_response_record(d,n); + return n; } + return NULL; } - - -/**************************************************************************** - find a response in a subnet's name query response list. - **************************************************************************/ -struct response_record *find_response_record(struct subnet_record **d, - uint16 id) -{ - struct response_record *n; - - if (!d) return NULL; - - for ((*d) = subnetlist; (*d); (*d) = (*d)->next) - { - for (n = (*d)->responselist; n; n = n->next) - { - if (n->response_id == id) { - return n; - } - } - } - - *d = NULL; - - return NULL; -} - - -/******************************************************************* - the global packet linked-list. incoming entries are added to the - end of this list. it is supposed to remain fairly short so we - won't bother with an end pointer. - ******************************************************************/ -static struct packet_struct *packet_queue = NULL; - -/******************************************************************* - queue a packet into the packet queue - ******************************************************************/ -void queue_packet(struct packet_struct *packet) -{ - struct packet_struct *p; - - if (!packet_queue) { - packet->prev = NULL; - packet->next = NULL; - packet_queue = packet; - return; - } - - /* find the bottom */ - for (p=packet_queue;p->next;p=p->next) ; - - p->next = packet; - packet->next = NULL; - packet->prev = p; -} - -/******************************************************************* - run elements off the packet queue till its empty - ******************************************************************/ -void run_packet_queue() -{ - struct packet_struct *p; - - while ((p=packet_queue)) - { - switch (p->packet_type) - { - case NMB_PACKET: - process_nmb(p); - break; - - case DGRAM_PACKET: - process_dgram(p); - break; - } - - packet_queue = packet_queue->next; - if (packet_queue) packet_queue->prev = NULL; - free_packet(p); - } -} - -/**************************************************************************** - listens for NMB or DGRAM packets, and queues them - ***************************************************************************/ -void listen_for_packets(BOOL run_election) -{ - fd_set fds; - int selrtn; - struct timeval timeout; - - FD_ZERO(&fds); - FD_SET(ClientNMB,&fds); - FD_SET(ClientDGRAM,&fds); - - /* during elections and when expecting a netbios response packet we need - to send election packets at one second intervals. - XXXX actually, it needs to be the interval (in ms) between time now and the - time we are expecting the next netbios packet */ - - timeout.tv_sec = (run_election||num_response_packets) ? 1 : NMBD_SELECT_LOOP; - timeout.tv_usec = 0; - - selrtn = sys_select(&fds,&timeout); - - if (FD_ISSET(ClientNMB,&fds)) - { - struct packet_struct *packet = read_packet(ClientNMB, NMB_PACKET); - if (packet) { -#if 1 - if (ismyip(packet->ip) && - (packet->port == NMB_PORT || packet->port == DGRAM_PORT)) { - DEBUG(5,("discarding own packet from %s:%d\n", - inet_ntoa(packet->ip),packet->port)); - free_packet(packet); - } else -#endif - { - queue_packet(packet); - } - } - } - - if (FD_ISSET(ClientDGRAM,&fds)) - { - struct packet_struct *packet = read_packet(ClientDGRAM, DGRAM_PACKET); - if (packet) { -#if 1 - if (ismyip(packet->ip) && - (packet->port == NMB_PORT || packet->port == DGRAM_PORT)) { - DEBUG(5,("discarding own packet from %s:%d\n", - inet_ntoa(packet->ip),packet->port)); - free_packet(packet); - } else -#endif - { - queue_packet(packet); - } - } - } -} - - - -/**************************************************************************** -interpret a node status response. this is pretty hacked: we need two bits of -info. a) the name of the workgroup b) the name of the server. it will also -add all the names it finds into the namelist. -****************************************************************************/ -BOOL interpret_node_status(struct subnet_record *d, - char *p, struct nmb_name *name,int t, - char *serv_name, struct in_addr ip, BOOL bcast) -{ - int level = t==0x20 ? 4 : 0; - int numnames = CVAL(p,0); - BOOL found = False; - - DEBUG(level,("received %d names\n",numnames)); - - p += 1; - - if (serv_name) *serv_name = 0; - - while (numnames--) - { - char qname[17]; - int type; - fstring flags; - int nb_flags; - - BOOL group = False; - BOOL add = False; - - *flags = 0; - - StrnCpy(qname,p,15); - type = CVAL(p,15); - nb_flags = p[16]; - trim_string(qname,NULL," "); - - p += 18; - - if (NAME_GROUP (nb_flags)) { strcat(flags,"<GROUP> "); group=True;} - if (NAME_BFLAG (nb_flags)) { strcat(flags,"B "); } - if (NAME_PFLAG (nb_flags)) { strcat(flags,"P "); } - if (NAME_MFLAG (nb_flags)) { strcat(flags,"M "); } - if (NAME__FLAG (nb_flags)) { strcat(flags,"_ "); } - if (NAME_DEREG (nb_flags)) { strcat(flags,"<DEREGISTERING> "); } - if (NAME_CONFLICT (nb_flags)) { strcat(flags,"<CONFLICT> "); add=True;} - if (NAME_ACTIVE (nb_flags)) { strcat(flags,"<ACTIVE> "); add=True; } - if (NAME_PERMANENT(nb_flags)) { strcat(flags,"<PERMANENT> "); add=True;} - - /* might as well update our namelist while we're at it */ - if (add) - { - struct in_addr nameip; - enum name_source src; - - if (ismyip(ip)) { - nameip = ipzero; - src = SELF; - } else { - nameip = ip; - src = STATUS_QUERY; - } - add_netbios_entry(d,qname,type,nb_flags,2*60*60,src,nameip,True,bcast); - } - - /* we want the server name */ - if (serv_name && !*serv_name && !group && t == 0) - { - StrnCpy(serv_name,qname,15); - serv_name[15] = 0; - } - - /* looking for a name and type? */ - if (name && !found && (t == type)) - { - /* take a guess at some of the name types we're going to ask for. - evaluate whether they are group names or no... */ - if (((t == 0x1b || t == 0x1d ) && !group) || - ((t == 0x20 || t == 0x1c || t == 0x1e) && group)) - { - found = True; - make_nmb_name(name,qname,type,scope); - } - } - - DEBUG(level,("\t%s(0x%x)\t%s\n",qname,type,flags)); - } - DEBUG(level,("num_good_sends=%d num_good_receives=%d\n", - IVAL(p,20),IVAL(p,24))); - return found; -} - - -/**************************************************************************** - construct and send a netbios DGRAM - - Note that this currently sends all answers to port 138. thats the - wrong things to do! I should send to the requestors port. XXX - **************************************************************************/ -BOOL send_mailslot_reply(char *mailslot,int fd,char *buf,int len,char *srcname, - char *dstname,int src_type,int dest_type, - struct in_addr dest_ip,struct in_addr src_ip) -{ - struct packet_struct p; - struct dgram_packet *dgram = &p.packet.dgram; - struct in_addr wins_ip = ipgrp; - char *ptr,*p2; - char tmp[4]; - - /* ha ha. no. do NOT send packets to 255.255.255.255: it's a pseudo address */ - if (ip_equal(wins_ip, dest_ip)) return False; - - bzero((char *)&p,sizeof(p)); - - update_name_trn_id(); - - dgram->header.msg_type = 0x11; /* DIRECT GROUP DATAGRAM */ - dgram->header.flags.node_type = M_NODE; - dgram->header.flags.first = True; - dgram->header.flags.more = False; - dgram->header.dgm_id = name_trn_id; - dgram->header.source_ip = src_ip; - dgram->header.source_port = DGRAM_PORT; - dgram->header.dgm_length = 0; /* let build_dgram() handle this */ - dgram->header.packet_offset = 0; - - make_nmb_name(&dgram->source_name,srcname,src_type,scope); - make_nmb_name(&dgram->dest_name,dstname,dest_type,scope); - - ptr = &dgram->data[0]; - - /* now setup the smb part */ - ptr -= 4; /* XXX ugliness because of handling of tcp SMB length */ - memcpy(tmp,ptr,4); - set_message(ptr,17,17 + len,True); - memcpy(ptr,tmp,4); - - CVAL(ptr,smb_com) = SMBtrans; - SSVAL(ptr,smb_vwv1,len); - SSVAL(ptr,smb_vwv11,len); - SSVAL(ptr,smb_vwv12,70 + strlen(mailslot)); - SSVAL(ptr,smb_vwv13,3); - SSVAL(ptr,smb_vwv14,1); - SSVAL(ptr,smb_vwv15,1); - SSVAL(ptr,smb_vwv16,2); - p2 = smb_buf(ptr); - strcpy(p2,mailslot); - p2 = skip_string(p2,1); - - memcpy(p2,buf,len); - p2 += len; - - dgram->datasize = PTR_DIFF(p2,ptr+4); /* +4 for tcp length */ - - p.ip = dest_ip; - p.port = DGRAM_PORT; - p.fd = ClientDGRAM; - p.timestamp = time(NULL); - p.packet_type = DGRAM_PACKET; - - return(send_packet(&p)); -} - - diff --git a/source3/nameresp.doc b/source3/nameresp.doc index 3d4bc3c348..0349bd47c2 100644 --- a/source3/nameresp.doc +++ b/source3/nameresp.doc @@ -1,12 +1,15 @@ the netbios expected response code is a key part of samba's NetBIOS handling capabilities. it allows samba to carry on dealing with other things while expecting a response from one or more hosts. + this allows samba to simultaneously deal with registering its names with another WINS server, register its names on its local subnets, query any hosts that have registered with samba in its capacity as a WINS server, and at a later date it will be also be able handle END-NODE CHALLENGES (see rfc1001.txt 15.2.2.2 and 15.2.2.3 - secured -NBNS functionality). all at once! +NBNS functionality). + +all at once! when a netbios packet is sent out by samba and it expects a response, a record of all the relevant information is kept (most importantly, @@ -21,76 +24,15 @@ made (and therefore the type of response can be verified) and appropriate action can be taken. when no responses, after a number of retries, are not received, then -samba may take appropriate action. - -nameresp.c deals with the maintenance of the netbios response records: -their creation, retransmission, and eventual removal. - - -/************************************************************************* - send_mailslot_reply() - *************************************************************************/ - -this function is responsible for sending a MAILSLOT packet. - -it will _not_ send packets to the pseudo WINS subnet's address of -255.255.255.255: this would be disastrous. - -each packet sent out has a unique transaction identifier. this is done -so that responses can be matched with the original purpose for the packet -being sent out in the first place. - - -/************************************************************************* - interpret_node_status() - *************************************************************************/ - -this function is responsible for interpreting the raw data that comes in -from a node status reply. in the process, it identifies both the server -name and the workgroup name in the node status reply. - -it also updates information in the WINS name database if the names -received are not already in samba's records. - - -/************************************************************************* - listen_for_packets() - *************************************************************************/ - -this function is responsible for reading NMB and DGRAM packets, and then -queueing them. it will normally time-out for NMBD_SELECT_LOOP seconds, but -if there is an election currently running or we are expecting a response -then this time is reduced to 1 second. - -note: the time-out period needs refining to the millisecond level. - - -/************************************************************************* - queue_packet() - *************************************************************************/ - -this function is responsible for queueing any NMB and DGRAM packets passed -to it. these packets will be removed from the queue in run_packet_queue(). - - -/************************************************************************* - run_packet_queue() - *************************************************************************/ - -this function is responsible for taking a packet off the queue, -identifying whether it is an NMB or a DGRAM packet, processing -it accordingly and deleting it. this process continues until -there are no more packets on the queue. - +samba may take appropriate action. this is a crucial part of samba's +operation: for a key number of NetBIOS operations, no response is an +implicit positive response. -/************************************************************************* - find_response_record() - *************************************************************************/ +module nameresp deals with the initial transmission, re-transmission +and time-out of netbios response records. -this function is responsible for matching the unique response transaction -id with an expected response record. as a side-effect of this search, -it will find the subnet (or the WINS pseudo-subnet) that samba expected -the response to come from. +module namedbresp deals with the maintenance of the list of expected +responses - creation, finding and removal. /************************************************************************* @@ -106,22 +48,6 @@ otherwise, it will be dealt with in expire_netbios_response_entries(). /************************************************************************* - make_response_queue_record() - *************************************************************************/ - -this function is responsible for creating a response record, which will -be queued awaiting a response. - -the number of retries is set to 4, and the retry period set to 1 second. -if no response is received, then the packet is re-transmitted, which is -why so much information is stored in the response record. - -the number of expected responses queued is kept, so listen_for_packets() -knows it must time-out after 1 second if one or more responses are -expected. - - -/************************************************************************* queue_netbios_pkt_wins() *************************************************************************/ @@ -138,17 +64,6 @@ this is NOT the case with a P node. /************************************************************************* - reply_netbios_packet() - *************************************************************************/ - -this function is responsible for sending a reply to another NetBIOS -packet from another host. it can be used to send a reply to a name -registration, name release, name query or name status request. - -the reply can be either a positive or a negative one. - - -/************************************************************************* expire_netbios_response_entries() *************************************************************************/ @@ -174,15 +89,6 @@ packets. /************************************************************************* - initiate_netbios_packet() - *************************************************************************/ - -this function is responsible for construction a netbios packet and sending -it. if the packet has not had a unique transaction id allocated to it, -then initiate_netbios_packet() will give it one. - - -/************************************************************************* dead_netbios_entry() *************************************************************************/ @@ -225,6 +131,18 @@ samba should take this into account (see rfc1001.txt 10.3). remove_name_entry() issues this samba 'state' response_name_rel() deals with responses to NAME_RELEASE. +- NAME_REGISTER_CHALLENGE + +when a samba 'state' of type NAME_REGISTER_CHALLENGE is sent, and a +response is not received, it is assumed that the server being queried +is either dead, deaf or unreachable. the host that wanted this +unique name is then informed that it can have it (the name query +challenge went unanswered) and that its registration of this name +did in fact succeed. + +reply_name_reg() issues this samba 'state' +response_name_query_register() deals with responses. + - NAME_REGISTER when a samba 'state' of type NAME_REGISTER is sent, and a response is @@ -243,19 +161,3 @@ is not received. this is not to say that action may not be appropriate, just that it's not been looked at yet :-) -/************************************************************************* - add_response_record() - *************************************************************************/ - -this function is responsible for adding the response record created by -make_response_queue_record() into the appropriate response record queue. - - -/************************************************************************* - update_name_trn_id() - *************************************************************************/ - -this function is responsible for allocating unique transaction identifiers -for each new packet sent on the network. - - diff --git a/source3/nameserv.c b/source3/nameserv.c index 6ff2167271..056c943cd6 100644 --- a/source3/nameserv.c +++ b/source3/nameserv.c @@ -2,7 +2,7 @@ Unix SMB/Netbios implementation. Version 1.9. NBT netbios routines and daemon - version 2 - Copyright (C) Andrew Tridgell 1994-1995 + Copyright (C) Andrew Tridgell 1994-1996 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -18,462 +18,30 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + Module name: nameserv.c + Revision History: 14 jan 96: lkcl@pires.co.uk added multiple workgroup domain master support + 04 jul 96: lkcl@pires.co.uk + module nameserv contains name server management functions */ #include "includes.h" extern int ClientNMB; -extern int ClientDGRAM; - -#define FIND_SELF 0x01 -#define FIND_WINS 0x02 -#define FIND_LOCAL 0x04 extern int DEBUGLEVEL; extern pstring scope; -extern BOOL CanRecurse; extern pstring myname; extern struct in_addr ipzero; extern struct in_addr ipgrp; extern struct subnet_record *subnetlist; -#define WINS_LIST "wins.dat" - -#define GET_TTL(ttl) ((ttl)?MIN(ttl,lp_max_ttl()):lp_max_ttl()) - -/**************************************************************************** - finds the appropriate subnet structure. directed packets (non-bcast) are - assumed to come from a point-to-point (P or M node), and so the subnet we - return in this instance is the WINS 'pseudo-subnet' with ip 255.255.255.255 - ****************************************************************************/ -static struct subnet_record *find_req_subnet(struct in_addr ip, BOOL bcast) -{ - if (bcast) - { - /* identify the subnet the broadcast request came from */ - return find_subnet(*iface_bcast(ip)); - } - /* find the subnet under the pseudo-ip of 255.255.255.255 */ - return find_subnet(ipgrp); -} - - -/**************************************************************************** - true if two netbios names are equal -****************************************************************************/ -static BOOL name_equal(struct nmb_name *n1,struct nmb_name *n2) -{ - return n1->name_type == n2->name_type && - strequal(n1->name ,n2->name ) && - strequal(n1->scope,n2->scope); -} - - -/**************************************************************************** - true if the netbios name is ^1^2__MSBROWSE__^2^1 - - note: this name is registered if as a master browser or backup browser - you are responsible for a workgroup (when you announce a domain by - broadcasting on your local subnet, you announce it as coming from this - name: see announce_host()). - - WINS is configured to ignore this 'special browser name', presumably - because it's redundant: there's no such thing as an announceable - domain when dealing with a wide area network and a WINS server. - - **************************************************************************/ -BOOL special_browser_name(char *name, int type) -{ - return strequal(name,MSBROWSE) && type == 0x01; -} - - -/**************************************************************************** - add a netbios name into the namelist - **************************************************************************/ -static void add_name(struct subnet_record *d, struct name_record *n) -{ - struct name_record *n2; - - if (!d) return; - - if (!d->namelist) - { - d->namelist = n; - n->prev = NULL; - n->next = NULL; - return; - } - - for (n2 = d->namelist; n2->next; n2 = n2->next) ; - - n2->next = n; - n->next = NULL; - n->prev = n2; -} - - -/**************************************************************************** - remove a name from the namelist. The pointer must be an element just - retrieved - **************************************************************************/ -void remove_name(struct subnet_record *d, struct name_record *n) -{ - struct name_record *nlist; - if (!d) return; - - nlist = d->namelist; - - while (nlist && nlist != n) nlist = nlist->next; - - if (nlist) - { - if (nlist->next) nlist->next->prev = nlist->prev; - if (nlist->prev) nlist->prev->next = nlist->next; - free(nlist); - } -} - - -/**************************************************************************** - find a name in a namelist. - **************************************************************************/ -static struct name_record *find_name(struct name_record *n, - struct nmb_name *name, - int search, struct in_addr ip) -{ - struct name_record *ret; - - for (ret = n; ret; ret = ret->next) - { - if (name_equal(&ret->name,name)) - { - /* self search: self names only */ - if ((search&FIND_SELF) == FIND_SELF && ret->source != SELF) - continue; - - /* zero ip is either samba's ip or a way of finding a - name without needing to know the ip address */ - if (zero_ip(ip) || ip_equal(ip, ret->ip)) - { - return ret; - } - } - } - return NULL; -} - - -/**************************************************************************** - find a name in the domain database namelist - search can be any of: - FIND_SELF - look exclusively for names the samba server has added for itself - FIND_LOCAL - look for names in the local subnet record. - FIND_WINS - look for names in the WINS record - **************************************************************************/ -static struct name_record *find_name_search(struct subnet_record **d, - struct nmb_name *name, - int search, struct in_addr ip) -{ - if (d == NULL) return NULL; /* bad error! */ - - if ((search & FIND_LOCAL) == FIND_LOCAL) - { - if (*d != NULL) - { - return find_name((*d)->namelist, name, search, ip); - } - else - { - DEBUG(4,("local find_name_search with a NULL subnet pointer\n")); - return NULL; - } - } - - if ((search & FIND_WINS) != FIND_WINS) return NULL; - - if (*d == NULL) - { - /* find WINS subnet record */ - *d = find_subnet(ipgrp); - } - - if (*d == NULL) return NULL; - - return find_name((*d)->namelist, name, search, ip); -} - - -/**************************************************************************** - dump a copy of the name table - **************************************************************************/ -void dump_names(void) -{ - struct name_record *n; - struct subnet_record *d; - fstring fname, fnamenew; - time_t t = time(NULL); - - FILE *f; - - strcpy(fname,lp_lockdir()); - trim_string(fname,NULL,"/"); - strcat(fname,"/"); - strcat(fname,WINS_LIST); - strcpy(fnamenew,fname); - strcat(fnamenew,"."); - - f = fopen(fnamenew,"w"); - - if (!f) - { - DEBUG(4,("Can't open %s - %s\n",fnamenew,strerror(errno))); - } - - DEBUG(3,("Dump of local name table:\n")); - - for (d = subnetlist; d; d = d->next) - for (n = d->namelist; n; n = n->next) - { - if (f && ip_equal(d->bcast_ip, ipgrp) && n->source == REGISTER) - { - fstring data; - - /* XXXX i have little imagination as to how to output nb_flags as - anything other than as a hexadecimal number :-) */ - - sprintf(data, "%s#%02x %s %ld %2x", - n->name.name,n->name.name_type, /* XXXX ignore the scope for now */ - inet_ntoa(n->ip), - n->death_time, - n->nb_flags); - fprintf(f, "%s\n", data); - } - - DEBUG(3,("%15s ", inet_ntoa(d->bcast_ip))); - DEBUG(3,("%15s ", inet_ntoa(d->mask_ip))); - DEBUG(3,("%s %15s TTL=%15d NBFLAGS=%2x\n", - namestr(&n->name), - inet_ntoa(n->ip), - n->death_time?n->death_time-t:0, - n->nb_flags)); - } - - fclose(f); - unlink(fname); - chmod(fnamenew,0644); - rename(fnamenew,fname); - - DEBUG(3,("Wrote wins database %s\n",fname)); -} - -/**************************************************************************** -load a netbios name database file -****************************************************************************/ -void load_netbios_names(void) -{ - struct subnet_record *d = find_subnet(ipgrp); - fstring fname; - - FILE *f; - pstring line; - - if (!d) return; - - strcpy(fname,lp_lockdir()); - trim_string(fname,NULL,"/"); - strcat(fname,"/"); - strcat(fname,WINS_LIST); - - f = fopen(fname,"r"); - - if (!f) { - DEBUG(2,("Can't open wins database file %s\n",fname)); - return; - } - - while (!feof(f)) - { - pstring name_str, ip_str, ttd_str, nb_flags_str; - - pstring name; - int type = 0; - int nb_flags; - time_t ttd; - struct in_addr ipaddr; - - enum name_source source; - - char *ptr; - int count = 0; - - char *p; - - if (!fgets_slash(line,sizeof(pstring),f)) continue; - - if (*line == '#') continue; - - ptr = line; - - if (next_token(&ptr,name_str ,NULL)) ++count; - if (next_token(&ptr,ip_str ,NULL)) ++count; - if (next_token(&ptr,ttd_str ,NULL)) ++count; - if (next_token(&ptr,nb_flags_str,NULL)) ++count; - - if (count <= 0) continue; - - if (count != 4) { - DEBUG(0,("Ill formed wins line")); - DEBUG(0,("[%s]: name#type ip nb_flags abs_time\n",line)); - continue; - } - - /* netbios name. # divides the name from the type (hex): netbios#xx */ - strcpy(name,name_str); - - p = strchr(name,'#'); - - if (p) { - *p = 0; - sscanf(p+1,"%x",&type); - } - - /* decode the netbios flags (hex) and the time-to-die (seconds) */ - sscanf(nb_flags_str,"%x",&nb_flags); - sscanf(ttd_str,"%ld",&ttd); - - ipaddr = *interpret_addr2(ip_str); - - if (ip_equal(ipaddr,ipzero)) { - source = SELF; - } - else - { - source = REGISTER; - } - - DEBUG(4, ("add WINS line: %s#%02x %s %ld %2x\n", - name,type, inet_ntoa(ipaddr), ttd, nb_flags)); - - /* add all entries that have 60 seconds or more to live */ - if (ttd - 60 < time(NULL) || ttd == 0) - { - time_t t = (ttd?ttd-time(NULL):0) / 3; - - /* add netbios entry read from the wins.dat file. IF it's ok */ - add_netbios_entry(d,name,type,nb_flags,t,source,ipaddr,True,True); - } - } - - fclose(f); -} - - -/**************************************************************************** - remove an entry from the name list - ****************************************************************************/ -void remove_netbios_name(struct subnet_record *d, - char *name,int type, enum name_source source, - struct in_addr ip) -{ - struct nmb_name nn; - struct name_record *n; - int search = FIND_LOCAL; - - /* if it's not a special browser name, search the WINS database */ - if (!special_browser_name(name, type)) - search |= FIND_WINS; - - make_nmb_name(&nn, name, type, scope); - n = find_name_search(&d, &nn, search, ip); - - if (n && n->source == source) remove_name(d,n); -} - - -/**************************************************************************** - add an entry to the name list. - - this is a multi-purpose function. - - it adds samba's own names in to its records on each interface, keeping a - record of whether it is a master browser, domain master, or WINS server. - - it also keeps a record of WINS entries (names of type 0x00, 0x20, 0x03 etc) - - ****************************************************************************/ -struct name_record *add_netbios_entry(struct subnet_record *d, - char *name, int type, int nb_flags, - int ttl, enum name_source source, struct in_addr ip, - BOOL new_only,BOOL wins) -{ - struct name_record *n; - struct name_record *n2=NULL; - int search = 0; - BOOL self = source == SELF; - - /* add the name to the WINS list if the name comes from a directed query */ - search |= wins ? FIND_WINS : FIND_LOCAL; - /* search for SELF names only */ - search |= self ? FIND_SELF : 0; - - if (!self) - { - if (wins) - { - if (special_browser_name(name, type)) - { - /* XXXX WINS server supposed to ignore special browser names. hm. - but is a primary domain controller supposed to ignore special - browser names? luke doesn't think so, but can't test it! :-) - */ - return NULL; - } - } - else /* !wins */ - { - /* the only broadcast (non-WINS) names we are adding are ours (SELF) */ - return NULL; - } - } - - n = (struct name_record *)malloc(sizeof(*n)); - if (!n) return(NULL); - - bzero((char *)n,sizeof(*n)); - - make_nmb_name(&n->name,name,type,scope); - - if ((n2 = find_name_search(&d, &n->name, search, new_only?ipzero:ip))) - { - free(n); - if (new_only || (n2->source==SELF && source!=SELF)) return n2; - n = n2; - } - - if (ttl) - n->death_time = time(NULL)+ttl*3; - n->refresh_time = time(NULL)+GET_TTL(ttl); - - n->ip = ip; - n->nb_flags = nb_flags; - n->source = source; - - if (!n2) add_name(d,n); - - DEBUG(3,("Added netbios name %s at %s ttl=%d nb_flags=%2x\n", - namestr(&n->name),inet_ntoa(ip),ttl,nb_flags)); - - return(n); -} - /**************************************************************************** remove an entry from the name list @@ -514,7 +82,7 @@ void remove_name_entry(struct subnet_record *d, char *name,int type) } queue_netbios_pkt_wins(d,ClientNMB,NMB_REL,NAME_RELEASE, name, type, 0, 0, - False, True, ipzero); + False, True, ipzero, ipzero); } } @@ -557,7 +125,7 @@ void add_my_name_entry(struct subnet_record *d,char *name,int type,int nb_flags) queue_netbios_pkt_wins(d,ClientNMB, re_reg ? NMB_REG_REFRESH : NMB_REG, NAME_REGISTER, name, type, nb_flags, GET_TTL(0), - False, True, ipzero); + False, True, ipzero, ipzero); } } @@ -649,6 +217,7 @@ void refresh_my_names(time_t t) } } + /******************************************************************* queries names occasionally. an over-cautious, non-trusting WINS server! @@ -693,7 +262,7 @@ void query_refresh_names(void) queue_netbios_packet(d,ClientNMB,NMB_QUERY,NAME_QUERY_CONFIRM, n->name.name, n->name.name_type, 0,0, - False,False,n->ip); + False,False,n->ip,n->ip); count++; } @@ -708,1153 +277,3 @@ void query_refresh_names(void) n->refresh_time += name_refresh_time; } } - - -/******************************************************************* - expires old names in the namelist - ******************************************************************/ -void expire_names(time_t t) -{ - struct name_record *n; - struct name_record *next; - struct subnet_record *d; - - /* expire old names */ - for (d = subnetlist; d; d = d->next) - { - for (n = d->namelist; n; n = next) - { - if (n->death_time && n->death_time < t) - { - DEBUG(3,("Removing dead name %s\n", namestr(&n->name))); - - next = n->next; - - if (n->prev) n->prev->next = n->next; - if (n->next) n->next->prev = n->prev; - - if (d->namelist == n) d->namelist = n->next; - - free(n); - } - else - { - next = n->next; - } - } - } -} - - -/**************************************************************************** - response for a reg release received. samba has asked a WINS server if it - could release a name. - **************************************************************************/ -void response_name_release(struct subnet_record *d, struct packet_struct *p) -{ - struct nmb_packet *nmb = &p->packet.nmb; - char *name = nmb->question.question_name.name; - int type = nmb->question.question_name.name_type; - - DEBUG(4,("response name release received\n")); - - if (nmb->header.rcode == 0 && nmb->answers->rdata) - { - /* IMPORTANT: see expire_netbios_response_entries() */ - - struct in_addr found_ip; - putip((char*)&found_ip,&nmb->answers->rdata[2]); - - if (ismyip(found_ip)) - { - remove_netbios_name(d,name,type,SELF,found_ip); - } - } - else - { - DEBUG(2,("name release for %s rejected!\n", - namestr(&nmb->question.question_name))); - - /* XXXX do we honestly care if our name release was rejected? - only if samba is issuing the release on behalf of some out-of-sync - server. if it's one of samba's SELF names, we don't care. */ - } -} - - -/**************************************************************************** -reply to a name release -****************************************************************************/ -void reply_name_release(struct packet_struct *p) -{ - struct nmb_packet *nmb = &p->packet.nmb; - struct in_addr ip; - int rcode=0; - int opcode = nmb->header.opcode; - int nb_flags = nmb->additional->rdata[0]; - BOOL bcast = nmb->header.nm_flags.bcast; - struct name_record *n; - struct subnet_record *d = NULL; - char rdata[6]; - int search = 0; - - putip((char *)&ip,&nmb->additional->rdata[2]); - - DEBUG(3,("Name release on name %s rcode=%d\n", - namestr(&nmb->question.question_name),rcode)); - - if (!(d = find_req_subnet(p->ip, bcast))) - { - DEBUG(3,("response packet: bcast %s not known\n", - inet_ntoa(p->ip))); - return; - } - - if (bcast) - search &= FIND_LOCAL; - else - search &= FIND_WINS; - - n = find_name_search(&d, &nmb->question.question_name, - search, ip); - - /* XXXX under what conditions should we reject the removal?? */ - if (n && n->nb_flags == nb_flags) - { - /* success = True; - rcode = 6; */ - - remove_name(d,n); - n = NULL; - } - - if (bcast) return; - - rdata[0] = nb_flags; - rdata[1] = 0; - putip(&rdata[2],(char *)&ip); - - /* Send a NAME RELEASE RESPONSE */ - reply_netbios_packet(p,nmb->header.name_trn_id, - rcode,opcode,True, - &nmb->question.question_name, - nmb->question.question_type, - nmb->question.question_class, - 0, - rdata, 6); -} - - -/**************************************************************************** -response for a reg request received -**************************************************************************/ -void response_name_reg(struct subnet_record *d, struct packet_struct *p) -{ - struct nmb_packet *nmb = &p->packet.nmb; - char *name = nmb->question.question_name.name; - int type = nmb->question.question_name.name_type; - BOOL bcast = nmb->header.nm_flags.bcast; - - DEBUG(4,("response name registration received!\n")); - - if (nmb->header.rcode == 0 && nmb->answers->rdata) - { - /* IMPORTANT: see expire_netbios_response_entries() */ - - int nb_flags = nmb->answers->rdata[0]; - int ttl = nmb->answers->ttl; - struct in_addr found_ip; - - putip((char*)&found_ip,&nmb->answers->rdata[2]); - - name_register_work(d,name,type,nb_flags,ttl,found_ip,bcast); - } - else - { - DEBUG(1,("name registration for %s rejected!\n", - namestr(&nmb->question.question_name))); - - /* XXXX oh dear. we have problems. must deal with our name having - been rejected: e.g if it was our GROUP(1d) name, we must unbecome - a master browser. */ - - name_unregister_work(d,name,type); - } -} - - -/**************************************************************************** -reply to a reg request -**************************************************************************/ -void reply_name_reg(struct packet_struct *p) -{ - struct nmb_packet *nmb = &p->packet.nmb; - struct nmb_name *question = &nmb->question.question_name; - - struct nmb_name *reply_name = question; - char *qname = question->name; - int name_type = question->name_type; - int name_class = nmb->question.question_class; - - BOOL bcast = nmb->header.nm_flags.bcast; - - int ttl = GET_TTL(nmb->additional->ttl); - int nb_flags = nmb->additional->rdata[0]; - BOOL group = NAME_GROUP(nb_flags); - int rcode = 0; - int opcode = nmb->header.opcode; - - struct subnet_record *d = NULL; - struct name_record *n = NULL; - BOOL success = True; - BOOL recurse = True; /* true if samba replies yes/no: false if caller */ - /* must challenge the current owner of the unique name */ - char rdata[6]; - struct in_addr ip, from_ip; - int search = 0; - - putip((char *)&from_ip,&nmb->additional->rdata[2]); - ip = from_ip; - - DEBUG(3,("Name registration for name %s at %s rcode=%d\n", - namestr(question),inet_ntoa(ip),rcode)); - - if (group) - { - /* apparently we should return 255.255.255.255 for group queries - (email from MS) */ - ip = ipgrp; - } - - if (!(d = find_req_subnet(p->ip, bcast))) - { - DEBUG(3,("response packet: bcast %s not known\n", - inet_ntoa(p->ip))); - return; - } - - if (bcast) - search &= FIND_LOCAL; - else - search &= FIND_WINS; - - /* see if the name already exists */ - n = find_name_search(&d, question, search, from_ip); - - if (n) - { - if (!group) /* unique names */ - { - if (n->source == SELF || NAME_GROUP(n->nb_flags)) - { - /* no-one can register one of samba's names, nor can they - register a name that's a group name as a unique name */ - - rcode = 6; - success = False; - } - else if(!ip_equal(ip, n->ip)) - { - /* hm. this unique name doesn't belong to them. */ - - /* XXXX rfc1001.txt says: - * if we are doing secured WINS, we must send a Wait-Acknowledge - * packet (WACK) to the person who wants the name, then do a - * name query on the person who currently owns the unique name. - * if the current owner still says they own it, the person who wants - * the name can't have it. if they do not, or are not alive, they can. - * - * if we are doing non-secured WINS (which is much simpler) then - * we send a message to the person wanting the name saying 'he - * owns this name: i don't want to hear from you ever again - * until you've checked with him if you can have it!'. we then - * abandon the registration. once the person wanting the name - * has checked with the current owner, they will repeat the - * registration packet if the current owner is dead or doesn't - * want the name. - */ - - /* non-secured WINS implementation: caller is responsible - for checking with current owner of name, then getting back - to us... IF current owner no longer owns the unique name */ - - /* XXXX please note also that samba cannot cope with - _receiving_ such redirecting, non-secured registration - packets. code to do this needs to be added. - */ - - rcode = 0; - success = False; - recurse = False; - - /* we inform on the current owner to the caller (which is - why it's non-secure */ - - reply_name = &n->name; - - /* name_type = ?; - name_class = ?; - XXXX sorry, guys: i really can't see what name_type - and name_class should be set to according to rfc1001 */ - } - else - { - n->ip = ip; - n->death_time = ttl?p->timestamp+ttl*3:0; - DEBUG(3,("%s owner: %s\n",namestr(&n->name),inet_ntoa(n->ip))); - } - } - else - { - /* refresh the name */ - if (n->source != SELF) - { - n->death_time = ttl?p->timestamp + ttl*3:0; - } - } - - /* XXXX bug reported by terryt@ren.pc.athabascau.ca */ - /* names that people have checked for and not found get DNSFAILed. - we need to update the name record if someone then registers */ - - if (n->source == DNSFAIL) - n->source = REGISTER; - - } - else - { - /* add the name to our name/subnet, or WINS, database */ - n = add_netbios_entry(d,qname,name_type,nb_flags,ttl,REGISTER,ip, - True,!bcast); - } - - /* if samba owns a unique name on a subnet, then it must respond and - disallow the attempted registration. if the registration is - successful by broadcast, only then is there no need to respond - (implicit registration: see rfc1001.txt 15.2.1). - */ - - if (bcast || !success) return; - - rdata[0] = nb_flags; - rdata[1] = 0; - putip(&rdata[2],(char *)&ip); - - /* Send a NAME REGISTRATION RESPONSE (pos/neg) - or an END-NODE CHALLENGE REGISTRATION RESPONSE */ - reply_netbios_packet(p,nmb->header.name_trn_id, - rcode,opcode,recurse, - reply_name, name_type, name_class, - ttl, - rdata, 6); -} - - -/**************************************************************************** -reply to a name status query -****************************************************************************/ -void reply_name_status(struct packet_struct *p) -{ - struct nmb_packet *nmb = &p->packet.nmb; - char *qname = nmb->question.question_name.name; - int ques_type = nmb->question.question_name.name_type; - char rdata[MAX_DGRAM_SIZE]; - char *countptr, *buf, *bufend; - int names_added; - struct name_record *n; - struct subnet_record *d = NULL; - - BOOL bcast = nmb->header.nm_flags.bcast; - - if (!(d = find_req_subnet(p->ip, bcast))) - { - DEBUG(3,("Name status req: bcast %s not known\n", - inet_ntoa(p->ip))); - return; - } - - DEBUG(3,("Name status for name %s %s\n", - namestr(&nmb->question.question_name), inet_ntoa(p->ip))); - - n = find_name_search(&d, &nmb->question.question_name, - FIND_SELF|FIND_LOCAL, - p->ip); - - if (!n) return; - - /* XXXX hack, we should calculate exactly how many will fit */ - bufend = &rdata[MAX_DGRAM_SIZE] - 18; - countptr = buf = rdata; - buf += 1; - - names_added = 0; - - for (n = d->namelist ; n && buf < bufend; n = n->next) - { - int name_type = n->name.name_type; - - if (n->source != SELF) continue; - - /* start with first bit of putting info in buffer: the name */ - - bzero(buf,18); - sprintf(buf,"%-15.15s",n->name.name); - strupper(buf); - - /* now check if we want to exclude other workgroup names - from the response. if we don't exclude them, windows clients - get confused and will respond with an error for NET VIEW */ - - if (name_type >= 0x1b && name_type <= 0x20 && - ques_type >= 0x1b && ques_type <= 0x20) - { - if (!strequal(qname, n->name.name)) continue; - } - - /* carry on putting name info in buffer */ - - buf[15] = name_type; - buf[16] = n->nb_flags; - - buf += 18; - - names_added++; - } - - SCVAL(countptr,0,names_added); - - /* XXXXXXX we should fill in more fields of the statistics structure */ - bzero(buf,64); - { - extern int num_good_sends,num_good_receives; - SIVAL(buf,20,num_good_sends); - SIVAL(buf,24,num_good_receives); - } - - SIVAL(buf,46,0xFFB8E5); /* undocumented - used by NT */ - - buf += 64; - - /* Send a POSITIVE NAME STATUS RESPONSE */ - reply_netbios_packet(p,nmb->header.name_trn_id, - 0,0,True, - &nmb->question.question_name, - nmb->question.question_type, - nmb->question.question_class, - 0, - rdata,PTR_DIFF(buf,rdata)); -} - - -/*************************************************************************** - reply to a name query - ****************************************************************************/ -struct name_record *search_for_name(struct subnet_record **d, - struct nmb_name *question, - struct in_addr ip, int Time, int search) -{ - int name_type = question->name_type; - char *qname = question->name; - BOOL dns_type = name_type == 0x20 || name_type == 0; - - struct name_record *n; - - DEBUG(3,("Search for %s from %s - ", namestr(question), inet_ntoa(ip))); - - /* first look up name in cache */ - n = find_name_search(d,question,search,ip); - - if (*d == NULL) return NULL; - - /* now try DNS lookup. */ - if (!n) - { - struct in_addr dns_ip; - unsigned long a; - - /* only do DNS lookups if the query is for type 0x20 or type 0x0 */ - if (!dns_type) - { - DEBUG(3,("types 0x20 0x1b 0x0 only: name not found\n")); - return NULL; - } - - /* look it up with DNS */ - a = interpret_addr(qname); - - putip((char *)&dns_ip,(char *)&a); - - if (!a) - { - /* no luck with DNS. We could possibly recurse here XXXX */ - DEBUG(3,("no recursion.\n")); - /* add the fail to our WINS cache of names. give it 1 hour in the cache */ - add_netbios_entry(*d,qname,name_type,NB_ACTIVE,60*60,DNSFAIL,dns_ip, - True, True); - return NULL; - } - - /* add it to our WINS cache of names. give it 2 hours in the cache */ - n = add_netbios_entry(*d,qname,name_type,NB_ACTIVE,2*60*60,DNS,dns_ip, - True,True); - - /* failed to add it? yikes! */ - if (!n) return NULL; - } - - /* is our entry already dead? */ - if (n->death_time) - { - if (n->death_time < Time) return False; - } - - /* it may have been an earlier failure */ - if (n->source == DNSFAIL) - { - DEBUG(3,("DNSFAIL\n")); - return NULL; - } - - DEBUG(3,("OK %s\n",inet_ntoa(n->ip))); - - return n; -} - - -/*************************************************************************** -reply to a name query. - -with broadcast name queries: - - - only reply if the query is for one of YOUR names. all other machines on - the network will be doing the same thing (that is, only replying to a - broadcast query if they own it) - NOTE: broadcast name queries should only be sent out by a machine - if they HAVEN'T been configured to use WINS. this is generally bad news - in a wide area tcp/ip network and should be rectified by the systems - administrator. USE WINS! :-) - - the exception to this is if the query is for a Primary Domain Controller - type name (0x1b), in which case, a reply is sent. - - - NEVER send a negative response to a broadcast query. no-one else will! - -with directed name queries: - - - if you are the WINS server, you are expected to respond with either - a negative response, a positive response, or a wait-for-acknowledgement - packet, and then later on a pos/neg response. - -****************************************************************************/ -void reply_name_query(struct packet_struct *p) -{ - struct nmb_packet *nmb = &p->packet.nmb; - struct nmb_name *question = &nmb->question.question_name; - int name_type = question->name_type; - BOOL bcast = nmb->header.nm_flags.bcast; - int ttl=0; - int rcode = 0; - int nb_flags = 0; - struct in_addr retip; - char rdata[6]; - struct subnet_record *d = NULL; - - BOOL success = True; - - struct name_record *n; - int search = 0; - - if (!(d = find_req_subnet(p->ip, bcast))) - { - DEBUG(3,("name query: bcast %s not known\n", - inet_ntoa(p->ip))); - success = False; - } - - if (bcast) - { - /* a name query has been made by a non-WINS configured host. search the - local interface database as well */ - search |= FIND_LOCAL; - - } - else if (!special_browser_name(question->name, name_type)) - { - search |= FIND_WINS; - } - - DEBUG(3,("Name query ")); - - if (search == 0) - { - /* eh? no criterion for searching database. help! */ - success = False; - } - - if (success && (n = search_for_name(&d,question,p->ip,p->timestamp, search))) - { - /* don't respond to broadcast queries unless the query is for - a name we own or it is for a Primary Domain Controller name */ - - if (bcast && n->source != SELF && name_type != 0x1b) { - if (!lp_wins_proxy() || same_net(p->ip,n->ip,*iface_nmask(p->ip))) { - /* never reply with a negative response to broadcast queries */ - return; - } - } - - /* name is directed query, or it's self, or it's a PDC type name, or - we're replying on behalf of a caller because they are on a different - subnet and cannot hear the broadcast. XXXX lp_wins_proxy should be - switched off in environments where broadcasts are forwarded */ - - /* XXXX note: for proxy servers, we should forward the query on to - another WINS server if the name is not in our database, or we are - not a WINS server ourselves - */ - ttl = n->death_time - p->timestamp; - retip = n->ip; - nb_flags = n->nb_flags; - } - else - { - if (bcast) return; /* never reply negative response to bcasts */ - success = False; - } - - /* if the IP is 0 then substitute my IP */ - if (zero_ip(retip)) retip = *iface_ip(p->ip); - - if (success) - { - rcode = 0; - DEBUG(3,("OK %s\n",inet_ntoa(retip))); - } - else - { - rcode = 3; - DEBUG(3,("UNKNOWN\n")); - } - - if (success) - { - rdata[0] = nb_flags; - rdata[1] = 0; - putip(&rdata[2],(char *)&retip); - } - - reply_netbios_packet(p,nmb->header.name_trn_id, - rcode,0,True, - &nmb->question.question_name, - nmb->question.question_type, - nmb->question.question_class, - ttl, - rdata, success ? 6 : 0); -} - - -/**************************************************************************** - response from a name query server check. states of type NAME_QUERY_MST_SRV_CHK, - NAME_QUERY_SRV_CHK, and NAME_QUERY_FIND_MST dealt with here. - ****************************************************************************/ -static void response_server_check(struct nmb_name *ans_name, - struct response_record *n, struct subnet_record *d) -{ - /* issue another state: this time to do a name status check */ - - enum state_type cmd = (n->state == NAME_QUERY_MST_SRV_CHK) ? - NAME_STATUS_MASTER_CHECK : NAME_STATUS_CHECK; - - /* initiate a name status check on the server that replied */ - queue_netbios_packet(d,ClientNMB,NMB_STATUS, cmd, - ans_name->name, ans_name->name_type, - 0,0, - False,False,n->to_ip); -} - -/**************************************************************************** - response from a name status check. states of type NAME_STATUS_MASTER_CHECK - and NAME_STATUS_CHECK dealt with here. - ****************************************************************************/ -static void response_name_status_check(struct in_addr ip, - struct nmb_packet *nmb, BOOL bcast, - struct response_record *n, struct subnet_record *d) -{ - /* NMB_STATUS arrives: contains workgroup name and server name required. - amongst other things. */ - - struct nmb_name name; - fstring serv_name; - - if (interpret_node_status(d,nmb->answers->rdata, - &name,0x1d,serv_name,ip,bcast)) - { - if (*serv_name) - { - sync_server(n->state,serv_name, - name.name,name.name_type, n->to_ip); - } - } - else - { - DEBUG(1,("No 0x1d name type in interpret_node_status()\n")); - } -} - - -/**************************************************************************** - response from a name query to sync browse lists or to update our netbios - entry. states of type NAME_QUERY_SYNC and NAME_QUERY_CONFIRM - ****************************************************************************/ -static void response_name_query_sync(struct nmb_packet *nmb, - struct nmb_name *ans_name, BOOL bcast, - struct response_record *n, struct subnet_record *d) -{ - DEBUG(4, ("Name query at %s ip %s - ", - namestr(&n->name), inet_ntoa(n->to_ip))); - - if (!name_equal(&n->name, ans_name)) - { - /* someone gave us the wrong name as a reply. oops. */ - DEBUG(4,("unexpected name received: %s\n", namestr(ans_name))); - return; - } - - if (nmb->header.rcode == 0 && nmb->answers->rdata) - { - int nb_flags = nmb->answers->rdata[0]; - struct in_addr found_ip; - - putip((char*)&found_ip,&nmb->answers->rdata[2]); - - if (!ip_equal(n->to_ip, found_ip)) - { - /* someone gave us the wrong ip as a reply. oops. */ - DEBUG(4,("expected ip: %s\n", inet_ntoa(n->to_ip))); - DEBUG(4,("unexpected ip: %s\n", inet_ntoa(found_ip))); - return; - } - - DEBUG(4, (" OK: %s\n", inet_ntoa(found_ip))); - - if (n->state == NAME_QUERY_SYNC) - { - struct work_record *work = NULL; - if ((work = find_workgroupstruct(d, ans_name->name, False))) - { - /* the server is there: sync quick before it (possibly) dies! */ - sync_browse_lists(d, work, ans_name->name, ans_name->name_type, - found_ip); - } - } - else - { - /* update our netbios name list (re-register it if necessary) */ - add_netbios_entry(d, ans_name->name, ans_name->name_type, - nb_flags,GET_TTL(0),REGISTER, - found_ip,False,!bcast); - } - } - else - { - DEBUG(4, (" NEGATIVE RESPONSE!\n")); - - if (n->state == NAME_QUERY_CONFIRM) - { - /* XXXX remove_netbios_entry()? */ - /* lots of things we ought to do, here. if we get here, - then we're in a mess: our name database doesn't match - reality. sort it out - */ - remove_netbios_name(d,n->name.name, n->name.name_type, - REGISTER,n->to_ip); - } - } -} - -/**************************************************************************** - report the response record type - ****************************************************************************/ -static void debug_rr_type(int rr_type) -{ - switch (rr_type) - { - case NMB_STATUS: DEBUG(3,("Name status ")); break; - case NMB_QUERY : DEBUG(3,("Name query ")); break; - case NMB_REG : DEBUG(3,("Name registration ")); break; - case NMB_REL : DEBUG(3,("Name release ")); break; - default : DEBUG(1,("wrong response packet type received")); break; - } -} - -/**************************************************************************** - report the response record nmbd state - ****************************************************************************/ -static void debug_state_type(int state) -{ - /* report the state type to help debugging */ - switch (state) - { - case NAME_QUERY_MST_SRV_CHK : DEBUG(4,("MASTER_SVR_CHECK\n")); break; - case NAME_QUERY_SRV_CHK : DEBUG(4,("NAME_QUERY_SRV_CHK\n")); break; - case NAME_QUERY_FIND_MST : DEBUG(4,("NAME_QUERY_FIND_MST\n")); break; - case NAME_STATUS_MASTER_CHECK: DEBUG(4,("NAME_STAT_MST_CHK\n")); break; - case NAME_STATUS_CHECK : DEBUG(4,("NAME_STATUS_CHECK\n")); break; - case NAME_QUERY_MST_CHK : DEBUG(4,("NAME_QUERY_MST_CHK\n")); break; - case NAME_REGISTER : DEBUG(4,("NAME_REGISTER\n")); break; - case NAME_RELEASE : DEBUG(4,("NAME_RELEASE\n")); break; - case NAME_QUERY_CONFIRM : DEBUG(4,("NAME_QUERY_CONFIRM\n")); break; - case NAME_QUERY_SYNC : DEBUG(4,("NAME_QUERY_SYNC\n")); break; - default: break; - } -} - -/**************************************************************************** - report any problems with the fact that a response has been received. - - (responses for certain types of operations are only expected from one host) - ****************************************************************************/ -static BOOL response_problem_check(struct response_record *n, - struct nmb_packet *nmb, char *qname) -{ - switch (nmb->answers->rr_type) - { - case NMB_REL: - { - if (n->num_msgs > 1) - { - DEBUG(1,("more than one release name response received!\n")); - return True; - } - break; - } - - case NMB_REG: - { - if (n->num_msgs > 1) - { - DEBUG(1,("more than one register name response received!\n")); - return True; - } - break; - } - - case NMB_QUERY: - { - if (n->num_msgs > 1) - { - if (nmb->header.rcode == 0 && nmb->answers->rdata) - { - int nb_flags = nmb->answers->rdata[0]; - - if ((!NAME_GROUP(nb_flags))) - { - /* oh dear. more than one person responded to a unique name. - there is either a network problem, a configuration problem - or a server is mis-behaving */ - - /* XXXX mark the name as in conflict, and then let the - person who just responded know that they must also mark it - as in conflict, and therefore must NOT use it. - see rfc1001.txt 15.1.3.5 */ - - /* this may cause problems for some early versions of nmbd */ - - switch (n->state) - { - case NAME_QUERY_MST_SRV_CHK: - case NAME_QUERY_SRV_CHK: - case NAME_QUERY_MST_CHK: - /* don't do case NAME_QUERY_FIND_MST */ - { - if (!strequal(qname,n->name.name)) - { - /* one subnet, one master browser per workgroup */ - /* XXXX force an election? */ - - DEBUG(3,("more than one master browser replied!\n")); - return True; - } - break; - } - default: break; - } - DEBUG(3,("Unique Name conflict detected!\n")); - return True; - } - } - else - { - /* we have received a negative reply, having already received - at least one response (pos/neg). something's really wrong! */ - - DEBUG(3,("wierd name query problem detected!\n")); - return True; - } - } - } - } - return False; -} - -/**************************************************************************** - check that the response received is compatible with the response record - ****************************************************************************/ -static BOOL response_compatible(struct response_record *n, - struct nmb_packet *nmb) -{ - switch (n->state) - { - case NAME_RELEASE: - { - if (nmb->answers->rr_type != NMB_REL) - { - DEBUG(1,("Name release reply has wrong answer rr_type\n")); - return False; - } - break; - } - - case NAME_REGISTER: - { - if (nmb->answers->rr_type != NMB_REG) - { - DEBUG(1,("Name register reply has wrong answer rr_type\n")); - return False; - } - break; - } - - case NAME_QUERY_CONFIRM: - case NAME_QUERY_SYNC: - case NAME_QUERY_MST_SRV_CHK: - case NAME_QUERY_SRV_CHK: - case NAME_QUERY_FIND_MST: - case NAME_QUERY_MST_CHK: - { - if (nmb->answers->rr_type != NMB_QUERY) - { - DEBUG(1,("Name query reply has wrong answer rr_type\n")); - return False; - } - break; - } - - case NAME_STATUS_MASTER_CHECK: - case NAME_STATUS_CHECK: - { - if (nmb->answers->rr_type != NMB_STATUS) - { - DEBUG(1,("Name status reply has wrong answer rr_type\n")); - return False; - } - break; - } - - default: - { - DEBUG(1,("unknown state type received in response_netbios_packet\n")); - return False; - } - } - return True; -} - - -/**************************************************************************** - process the response packet received - ****************************************************************************/ -static void response_process(struct subnet_record *d, struct packet_struct *p, - struct response_record *n, struct nmb_packet *nmb, - BOOL bcast, struct nmb_name *ans_name) -{ - switch (n->state) - { - case NAME_RELEASE: - { - response_name_release(d, p); - break; - } - - case NAME_REGISTER: - { - response_name_reg(d, p); - break; - } - - case NAME_QUERY_MST_SRV_CHK: - case NAME_QUERY_SRV_CHK: - case NAME_QUERY_FIND_MST: - { - response_server_check(ans_name, n, d); - break; - } - - case NAME_STATUS_MASTER_CHECK: - case NAME_STATUS_CHECK: - { - response_name_status_check(p->ip, nmb, bcast, n, d); - break; - } - - case NAME_QUERY_CONFIRM: - case NAME_QUERY_SYNC: - { - response_name_query_sync(nmb, ans_name, bcast, n, d); - break; - } - case NAME_QUERY_MST_CHK: - { - /* no action required here. it's when NO responses are received - that we need to do something. see expire_name_query_entries() */ - - DEBUG(4, ("Master browser exists for %s at %s (just checking!)\n", - namestr(&n->name), inet_ntoa(n->to_ip))); - break; - } - - default: - { - DEBUG(1,("unknown state type received in response_netbios_packet\n")); - break; - } - } -} - - -/**************************************************************************** - response from a netbios packet. - ****************************************************************************/ -static void response_netbios_packet(struct packet_struct *p) -{ - struct nmb_packet *nmb = &p->packet.nmb; - struct nmb_name *question = &nmb->question.question_name; - struct nmb_name *ans_name = NULL; - char *qname = question->name; - BOOL bcast = nmb->header.nm_flags.bcast; - struct response_record *n; - struct subnet_record *d = NULL; - - if (!(n = find_response_record(&d,nmb->header.name_trn_id))) { - DEBUG(2,("unknown netbios response (received late or from nmblookup?)\n")); - return; - } - - if (!d) - { - DEBUG(2,("response packet: subnet %s not known\n", inet_ntoa(p->ip))); - return; - } - - if (!same_net(d->bcast_ip, d->mask_ip, p->ip)) /* copes with WINS 'subnet' */ - { - DEBUG(2,("response from %s. ", inet_ntoa(p->ip))); - DEBUG(2,("expected on subnet %s. hmm.\n", inet_ntoa(d->bcast_ip))); - return; - } - - if (nmb->answers == NULL) - { - /* hm. the packet received was a response, but with no answer. wierd! */ - DEBUG(2,("NMB packet response from %s (bcast=%s) - UNKNOWN\n", - inet_ntoa(p->ip), BOOLSTR(bcast))); - return; - } - - ans_name = &nmb->answers->rr_name; - DEBUG(3,("response for %s from %s (bcast=%s)\n", - namestr(ans_name), inet_ntoa(p->ip), BOOLSTR(bcast))); - - debug_rr_type(nmb->answers->rr_type); - - n->num_msgs++; /* count number of responses received */ - n->repeat_count = 0; /* don't resend: see expire_netbios_packets() */ - - debug_state_type(n->state); - - /* problem checking: multiple responses etc */ - if (response_problem_check(n, nmb, qname)) - return; - - /* now check whether the state has received the correct type of response */ - if (!response_compatible(n, nmb)) - return; - - /* now deal with the current state */ - response_process(d, p, n, nmb, bcast, ans_name); -} - - -/**************************************************************************** - process a nmb packet - ****************************************************************************/ -void process_nmb(struct packet_struct *p) -{ - struct nmb_packet *nmb = &p->packet.nmb; - - debug_nmb_packet(p); - - switch (nmb->header.opcode) - { - case 8: /* what is this?? */ - case NMB_REG: - case NMB_REG_REFRESH: - { - if (nmb->header.qdcount==0 || nmb->header.arcount==0) break; - if (nmb->header.response) - response_netbios_packet(p); /* response to registration dealt with here */ - else - reply_name_reg(p); - break; - } - - case 0: - { - if (nmb->header.response) - { - switch (nmb->question.question_type) - { - case 0x0: - { - response_netbios_packet(p); - break; - } - } - return; - } - else if (nmb->header.qdcount>0) - { - switch (nmb->question.question_type) - { - case NMB_QUERY: - { - reply_name_query(p); - break; - } - case NMB_STATUS: - { - reply_name_status(p); - break; - } - } - return; - } - break; - } - - case NMB_REL: - { - if (nmb->header.qdcount==0 || nmb->header.arcount==0) - { - DEBUG(2,("netbios release packet rejected\n")); - break; - } - - if (nmb->header.response) - response_netbios_packet(p); /* response to reply dealt with in here */ - else - reply_name_release(p); - break; - } - } -} - diff --git a/source3/nameserv.doc b/source3/nameserv.doc index 60ff52f4a3..8cb2bbc53d 100644 --- a/source3/nameserv.doc +++ b/source3/nameserv.doc @@ -1,344 +1,4 @@ -/************************************************************************* - process_nmb() - *************************************************************************/ - -this function receives a packet identified as a netbios packet. -it further identifies whether it is a response or a query packet. -by identifying the type of packet (name registration, query etc) -process_nmb() will call the appropriate function to deal with the -type of packet received. - - -/************************************************************************* - response_netbios_packet() - *************************************************************************/ - -this function received netbios response packets. the samba server -(or a rogue tcp/ip system, or nmblookup) will have sent out a packet -requesting a response. a client (or a rogue tcp/ip system) responds -to that request. - -this function checks the validity of the packet it receives. -the expected response records are searched for the transaction id, -to see if it's a response expected by the samba server. if it isn't -it's reported as such, and ignored. - -if the response is found, then the subnet it was expected from will -also have been found. the subnet it actually came in on can be -checked against the subnet it was expected from and reported, -otherwise this function just carries on. - -the number of responses received is increased, and the number of -retries left to be sent is set to zero. - -after debug information is reported, and validation of the netbios -packet (e.g only one response from one machine is expected for some -functions) has occurred, the packet is processed. when the initial -request was sent out, the expected response record was flagged with, -for lack of a better word, a samba 'state' type. whenever a -response is received, the appropriate function is called to carry on -where the program control flow was interrupted while awaiting exactly -such a response. - -please note that _not_ receiving a response is dealt with in another -area of code - expire_netbios_response_entries(). - - -/************************************************************************* - response_name_query_sync() - *************************************************************************/ - -this function receives responses to samba 'states' NAME_QUERY_SYNC and -NAME_QUERY_CONFIRM. - -NAME_QUERY_SYNC: name query a server before synchronising browse lists. -NAME_QUERY_CONFIRM: name query a server to check that it's alive. - -a NAME_QUERY_SYNC will be carried out in order to check that a server -is alive before syncing browse lists. we don't want to delay the SMB -NetServerEnum api just because the server has gone down: we have too -much else to do. - -a NAME_QUERY_CONFIRM is just a name query to see whether the server is -alive. these queries are sent out by samba's WINS server side, to verify -its netbios name database of all machines that have registered with it. - -we don't normally expect a negative response from such a query, although -we may do so if the query was sent to another WINS server. the registered -entry should be removed if we receive a negative response. - - -/************************************************************************* - response_name_status_check() - *************************************************************************/ - -this function receives responses to samba 'states' NAME_STATUS_CHECK -and NAME_STATUS_MASTER_CHECK - -NAME_STATUS_MASTER_CHECK: name status a primary domain controller, - confirm its domain and then initiate syncing - its browse list. - -NAME_STATUS_CHECK: same as NAME_STATUS_MASTER_CHECK except the name status - is issued to a master browser. - -if we don't know what workgroup a server is responsible for, but we -know that there is a master browser at a certain ip, we can issue a -name status check. from the response received, there will be -a master browser netbios entry. this will allow us to synchronise -browse lists with that machine and then add the information to the -correct part of samba's workgroup - server database. - - -/************************************************************************* - response_server_check() - *************************************************************************/ - -this function receives responses to samba 'states' NAME_QUERY_MST_SRV_CHK, -NAME_QUERY_SRV_CHK and NAME_QUERY_FIND_MST. - -NAME_QUERY_FIND_MST: issued as a broadcast when we wish to find out all - master browsers (i.e all servers that have registered - the NetBIOS name ^1^2__MSBROWSE__^2(0x1), and then - issue a NAME_STATUS_MASTER_CHECK on any servers that - respond, which will initiate a sync browse lists. - -NAME_QUERY_MST_SRV_CHK: same as a NAME_QUERY_FIND_MST except this is sent - to a primary domain controller. - -NAME_QUERY_SRV_CHK: same as a NAME_QUERY_MST_SRV_CHK except this is sent to - a master browser. - -the purpose of each of these states is to do a broadcast name query, or -a name query directed at a WINS server, then to all hosts that respond, -we issue a name status check, which will confirm for us the workgroup -or domain name, and then initiate issuing a sync browse list call with -that server. - -a NAME_QUERY_SRV_CHK is sent when samba receives a list of backup -browsers. it checks to see if that server is alive (by doing a -name query on a server) and then syncs browse lists with it. - - -/************************************************************************* - reply_name_query() - *************************************************************************/ - -this function is responsible for replying to a NetBIOS name query. - -there are two kinds of name queries: directed, and broadcast. directed -queries are usually sent to samba in its WINS capacity. such hosts are -termed 'point-to-point' hosts. broadcast queries are usually sent from -'broadcast' or 'mixed' hosts. - -broadcasting is used by either older NetBIOS hosts, new NetBIOS hosts that -have not had WINS capabilities added and new NetBIOS hosts that think the -WINS server has died. - -the samba NetBIOS name database is divided into sections, on a -per-subnet basis. there is also a WINS NetBIOS name database, and for -convenience this is added as a pseudo-subnet with the ip address of -255.255.255.255. - -the local subnet NetBIOS name databases only contain samba's names. -the reason for this is that if a broadcast query is received, a NetBIOS -hosts is only expected to respond if that query is for one of its own -names (the exception to this is if a host is configured as a 'proxy' -server, in which case, samba should redirect the query to another WINS -server). - -the WINS pseudo-subnet NetBIOS database contains all NetBIOS names -that are not 'special browser' type names (regarding this i am a -_bit_ confused :-). names of type 0x01, 0x1d and 0x1e i consider to -be 'special browser' names. at the moment. maybe. - -the type of search to be initiated is determined. if the NetBIOS name -type is a non-special-browser name, then the WINS database is included -in the search. - -if the name is not a special browser name, then we need to find the -right subnet that the query came from. this is done using -find_req_subnet(). this also has the benefit of stopping any queries -from subnets that samba does not know about. - -if the query is a broadcast query, then the database of the local subnet -is included in the search. - -the name is then searched for in the appropriate NetBIOS data structures. -if it is found, then we need to check whether it is appropriate for us -to reply to such a query. - -we will only reply if the query is a directed query, the name belongs to -samba on that subnet, or the name is a primary domain controller type, -or we're doing replies on behalf of hosts on subnets not known to the -host issuing the query. in the latter instance, it would be appropriate -if samba is using a WINS server for it to forward the name query on to -this WINS server. - -reply_name_query() then takes note of all the information that is -needed to construct a reply to the caller. a negative reply (if the -name is unknown to samba) or a positive reply (the name is known to -samba) is then issued. - - -/************************************************************************* - search_for_name() - *************************************************************************/ - -this function is responsible for finding a name in the appropriate part -of samba's NetBIOS name database. if the name cannot be found, then it -should look the name up using DNS. later modifications will be to -forward the request on to another WINS server, should samba not be able -to find out about the requested name (this will be implemented through -issuing a new type of samba 'state'). - -the name is first searched for in the NetBIOS cache. if it cannot be -found, then it if the name looks like it's a server-type name (0x20 -0x0 or 0x1b) then DNS is used to look for the name. - -if DNS fails, then a record of this failure is kept. if it succeeds, then -a new NetBIOS entry is added. - -the successfully found name is returned. on failure, NULL is returned. - - -/************************************************************************* - reply_name_status() - *************************************************************************/ - -this function is responsible for constructing a reply to a NetBIOS -name status query. this response contains all samba's NetBIOS names -on the subnet that the query came in from. - -a reply will only be made if the NetBIOS name being queried exists. - -see rfc1001.txt and rfc1002.txt for details of the name status reply. - - -/************************************************************************* - reply_name_reg() - *************************************************************************/ - -this function is responsible for updating the NetBIOS name database -from registration packets sent out by hosts wishing to register a -name, and for informing them, if necessary, if this is acceptable -or not. - -name registration can be done by broadcast or by point-to-point, -i.e the registration is sent directly to samba in its capacity as -a WINS server. - -if the name registration is done by broadcast (see rfc1001.txt 15.2.1), -then samba's involvement in replying is limited to whether that name -is owned by samba or not, on the relevant subnet. - -if the name registration is done point-to-point (see rfc1001.txt 15.2.2) -then samba will first need to check its WINS name database records and -proceed accordingly. - -samba looks for the appropriate subnet record that the registration -should be added to / checked against, using find_req_subnet(). - -next, the name is searched for in the local database or the WINS -database as appropriate. - -if the name is not found, then it is added to the NetBIOS name database, -using add_netbios_entry(), which may choose not to add the name (not -that this affects the registration of the name on the network in any way). -it will only add names to the WINS database, and even then it will only -add non-special-browser type names. - -if the name is found, then samba must decide whether to accept the name -or not. a group name is always added. for unique names, further checks -need to be carried out. - -firstly, if the name in the database is one of samba's names, or if the -name in the database is a group name, then it cannot be added as a unique -name belonging to someone else. it is therefore rejected. - -secondly, if the ip address of the name being registered does not match -against the ip in the database, then the unique name may belong to -someone else. a check needs to be carried out with the owner in case -they still wish to keep this name. a detailed discussion of what action -to take is in rfc1001.txt 15.2.2.2 and 15.2.2.3. - -samba currently implements non-secured WINS, whereupon the responsibility -for checking the name is passed on to the host doing the registration. -rfc1001.txt refers to this as an END-NODE CHALLENGE REGISTRATION RESPONSE. -(samba itself cannot yet cope with receiving such responses if it -registers its names with another WINS server). - -having decided what kind of response to send (if any - acceptance of -name registrations by broadcast is implicit), samba will send either a -positive or negative NAME REGISTRATION RESPONSE, or an END-NODE CHALLENGE -REGISTRATION RESPONSE to the host that initially sent the registration. - -whew. - - -/************************************************************************* - response_name_reg() - *************************************************************************/ - -this function is responsible for dealing with samba's registration -attempts, by broadcast to a local subnet, or point-to-point with -another WINS server. - -please note that it cannot cope with END-NODE CHALLENGE REGISTRATION -RESPONSEs at present. - -when a response is received, samba determines if the response is a -positive or a negative one. if it is a positive response, the name -is added to samba's database. - -when a negative response is received, samba will remove the name -from its database. if, however, the name is a browser type (0x1b is -a primary domain controller type name; or 0x1d, which is a master -browser type name) then it must also stop being a primary domain -controller or master browser respectively, depending on what kind -of name was rejected. - -(when no response is received, then expire_netbios_response_entries() -is expected to deal with this. the only case that is dealt with here -at present is when the registration was done by broadcast. if there -is no challenge to the broadcast registration, it is implicitly -assumed that claiming the name is acceptable). - - -/************************************************************************* - response_name_release() - *************************************************************************/ - -this function is responsible for removing samba's NetBIOS name when -samba contacts another WINS server with which it had registered the -name. - -only positive name releases are expected and dealt with. exactly what -to do if a negative name release (i.e someone says 'oi! you have to -keep that name!') is received is uncertain. - -(when no response is received, then expire_netbios_response_entries() -is expected to deal with this. if there is no challenge to the release -of the name, the name is then removed from that subnet's NetBIOS -name database). - - -/************************************************************************* - expire_names() - *************************************************************************/ - -this function is responsible for removing old NetBIOS names from its -database. no further action is required. - -for over-zealous WINS systems, the use of query_refresh_names() is -recommended. this function initiates polling of hosts that have -registered with samba in its capacity as a WINS server. an alternative -means to achieve the same end as query_refresh_names() is to -reduce the time to live when the name is registered with samba, -except that in this instance the responsibility for refreshing the -name is with the owner of the name, not the server with which the name -is registered. - +this module deals with general maintenance of NetBIOS names. /************************************************************************* query_refresh_names() @@ -436,45 +96,3 @@ are recorded in this file, including the time-to-live. should the time left to live be small, the name is not added back in to samba's WINS database. - -/************************************************************************* - dump_names() - *************************************************************************/ - -this function is responsible for outputting NetBIOS names in two formats. -firstly, as debugging information, and secondly, all names that have been -registered with samba in its capacity as a WINS server are written to -disk. - -writing all WINS names allows two things. firstly, if samba's NetBIOS -daemon dies or is terminated, on restarting the daemon most if not all -of the registered WINS names will be preserved (which is a good reason -why query_netbios_names() should be used). - - -/************************************************************************* - find_name_search() - *************************************************************************/ - -this function is a wrapper around find_name(). find_name_search() can -be told whether to search for the name in a local subnet structure or -in the WINS database. on top of this, it can be told to search only -for samba's SELF names. - -if it finds the name in the WINS database, it will set the subnet_record -and also return the name it finds. - - -/************************************************************************* - find_req_subnet() - *************************************************************************/ - -this function is responsible for finding the appropriate subnet record -to use. it is assumed that any directed packets are going to need to -use the WINS pseudo-subnet records, and that any broadcast transactions -received are going to need to use a local subnet record, which is found -from the ip address that the transaction was received on. - -a side-effect of this function is that any broadcast packet received -on a subnet not known to samba is ignored. - diff --git a/source3/nameservreply.doc b/source3/nameservreply.doc new file mode 100644 index 0000000000..aeb3d29f17 --- /dev/null +++ b/source3/nameservreply.doc @@ -0,0 +1,189 @@ +/************************************************************************* + reply_name_query() + *************************************************************************/ + +this function is responsible for replying to a NetBIOS name query. + +there are two kinds of name queries: directed, and broadcast. directed +queries are usually sent to samba in its WINS capacity. such hosts are +termed 'point-to-point' hosts. broadcast queries are usually sent from +'broadcast' or 'mixed' hosts. + +broadcasting is used by either older NetBIOS hosts, new NetBIOS hosts that +have not had WINS capabilities added and new NetBIOS hosts that think the +WINS server has died. + +the samba NetBIOS name database is divided into sections, on a +per-subnet basis. there is also a WINS NetBIOS name database, and for +convenience this is added as a pseudo-subnet with the ip address of +255.255.255.255. + +the local subnet NetBIOS name databases only contain samba's names. +the reason for this is that if a broadcast query is received, a NetBIOS +hosts is only expected to respond if that query is for one of its own +names (the exception to this is if a host is configured as a 'proxy' +server, in which case, samba should redirect the query to another WINS +server). + +the WINS pseudo-subnet NetBIOS database contains all NetBIOS names +registered with samba in its capacity as a WINS server. + +the type of search to be initiated is determined. if the packet +received is a point-to-point (unicast), then the WINS database +is included in the search. + +if the search is not in the WINS database, then we need to find the +right subnet that the query came from. this is done using +find_req_subnet(). this also has the benefit of stopping any queries +from subnets that samba does not know about. + +if the query is a broadcast query, then the database of the local subnet +is included in the search. + +the name is then searched for in the appropriate NetBIOS data structures. +if it is found, then we need to check whether it is appropriate for us +to reply to such a query. + +we will only reply if the query is a directed query, the name belongs to +samba on that subnet, or the name is a primary domain controller type, +or we're doing replies on behalf of hosts on subnets not known to the +host issuing the query. in the latter instance, it would be appropriate +if samba is using a WINS server for it to forward the name query on to +this WINS server. + +reply_name_query() then takes note of all the information that is +needed to construct a reply to the caller. a negative reply (if the +name is unknown to samba) or a positive reply (the name is known to +samba) is then issued. + + +/************************************************************************* + reply_name_status() + *************************************************************************/ + +this function is responsible for constructing a reply to a NetBIOS +name status query. this response contains all samba's NetBIOS names +on the subnet that the query came in from. + +a reply will only be made if the NetBIOS name being queried exists. + +see rfc1001.txt and rfc1002.txt for details of the name status reply. + + +/************************************************************************* + reply_name_reg() + *************************************************************************/ + +this function is responsible for updating the NetBIOS name database +from registration packets sent out by hosts wishing to register a +name, and for informing them, if necessary, if this is acceptable +or not. + +name registration can be done by broadcast or by point-to-point, +i.e the registration is sent directly to samba in its capacity as +a WINS server. + +if the name registration is done by broadcast (see rfc1001.txt 15.2.1), +then samba's involvement in replying is limited to whether that name +is owned by samba or not, on the relevant subnet. + +if the name registration is done point-to-point (see rfc1001.txt 15.2.2) +then samba will first need to check its WINS name database records and +proceed accordingly. + +samba looks for the appropriate subnet record that the registration +should be added to / checked against, using find_req_subnet(). + +next, the name is searched for in the local database or the WINS +database as appropriate. + +if the name is not found, then it is added to the NetBIOS name database, +using add_netbios_entry(), which may choose not to add the name (not +that this affects the registration of the name on the network in any way). +it will only add non-SELF names to the WINS database. + +if the name is found, then samba must decide whether to accept the name +or not. a group name is always added. for unique names, further checks +need to be carried out. + +firstly, if the name in the database is one of samba's names, or if the +name in the database is a group name, then it cannot be added as a unique +name belonging to someone else. it is therefore rejected. + +secondly, if the ip address of the name being registered does not match +against the ip in the database, then the unique name may belong to +someone else. a check needs to be carried out with the owner in case +they still wish to keep this name. a detailed discussion of what action +to take is in rfc1001.txt 15.2.2.2 and 15.2.2.3. + +#if 0 + +samba currently implements non-secured WINS, whereupon the responsibility +for checking the name is passed on to the host doing the registration. +rfc1001.txt refers to this as an END-NODE CHALLENGE REGISTRATION RESPONSE. +(samba itself cannot yet cope with receiving such responses if it +registers its names with another WINS server). + +#else + +samba currently implements secured WINS, whereupon it is samba's +responsibility to check the unique name before allowing it to be +registered by the new owner. + +as checking the unique name may take some time, samba must send a Wait +Acknowledge packet to the host that wishes to claim the name. a +NAME_REGISTER_CHALLENGE 'state' is then initiated, which will result +in a name query being issued to the current owner. + +if we receive a negative response or no response, the host wishing +to claim the name is informed that they can have it. if we receive +a positive response, this host is informed that it cannot have it. + +#endif + +having decided what kind of response to send (if any - acceptance of +name registrations by broadcast is implicit), samba will send a +positive or negative NAME REGISTRATION RESPONSE, or an END-NODE CHALLENGE +REGISTRATION RESPONSE or a WAIT ACKNOWLEDGEMENT to the host that +initially sent the registration. + + +whew. + + +/************************************************************************* + send_name_response() + *************************************************************************/ + +this function is responsible for sending out a positive or a negative +registration response or release, or an end-node challenge registration +response. + +it is called from reply_name_release(), reply_name_reg(), +dead_netbios_entry() and response_name_query_register(). + + +/************************************************************************* + reply_name_release() + *************************************************************************/ + +this function is responsible for removing a NetBIOS name from the +database when a server sends a release packet. + +samba looks for the appropriate subnet record that the release should +be removed from, using find_req_subnet(). next, the name is searched +for in the local database or the WINS database as appropriate. + +if the name is found, it is removed from the database and a +positive reply is sent confirming this. if the name is not +found, a negative reply is sent. + +a reply is _not_ sent if the release was done by broadcast: the +release is implicit, and we should be grateful that they bothered +to tell us. if the release was done by directed packet, then +we deal with it as a WINS server and must reply (pos / neg). + +at present, the criteria for removing a name have yet to be +developed / experimented with. at present, the only flags that +are checked are the NetBIOS flags. + diff --git a/source3/nameservresp.doc b/source3/nameservresp.doc new file mode 100644 index 0000000000..2f0d1912c8 --- /dev/null +++ b/source3/nameservresp.doc @@ -0,0 +1,188 @@ +this module deals with the receipt of response packets. the +response packets are expected to be received, and there is a +record of this kept (see also: modules nameresp and namedbresp) + +/************************************************************************* + response_netbios_packet() + *************************************************************************/ + +this function receives netbios response packets. the samba server +(or a rogue tcp/ip system, or nmblookup) will have sent out a packet +requesting a response. a client (or a rogue tcp/ip system) responds +to that request. + +this function checks the validity of the packet it receives. +the expected response records are searched for the transaction id, +to see if it's a response expected by the samba server. if it isn't +it's reported as such, and ignored. + +if the response is found, then the subnet it was expected from will +also have been found. the subnet it actually came in on can be +checked against the subnet it was expected from and reported, +otherwise this function just carries on. + +the number of responses received is increased, and the number of +retries left to be sent is set to zero. + +after debug information is reported, and validation of the netbios +packet (e.g only one response from one machine is expected for some +functions) has occurred, the packet is processed. when the initial +request was sent out, the expected response record was flagged with, +for lack of a better word, a samba 'state' type. whenever a +response is received, the appropriate function is called to carry on +where the program control flow was interrupted while awaiting exactly +such a response. + +please note that _not_ receiving a response is dealt with in another +area of code - expire_netbios_response_entries(). + + +/************************************************************************* + response_name_query_sync() + *************************************************************************/ + +this function receives responses to samba 'states' NAME_QUERY_SYNC and +NAME_QUERY_CONFIRM. + +NAME_QUERY_SYNC: name query a server before synchronising browse lists. +NAME_QUERY_CONFIRM: name query a server to check that it's alive. + +a NAME_QUERY_SYNC will be carried out in order to check that a server +is alive before syncing browse lists. we don't want to delay the SMB +NetServerEnum api just because the server has gone down: we have too +much else to do. + +a NAME_QUERY_CONFIRM is just a name query to see whether the server is +alive. these queries are sent out by samba's WINS server side, to verify +its netbios name database of all machines that have registered with it. + +we don't normally expect a negative response from such a query, although +we may do so if the query was sent to another WINS server. the registered +entry should be removed if we receive a negative response. + + +/************************************************************************* + response_name_query_register() + *************************************************************************/ + +this function receives responses to samba 'states' +NAME_REGISTER_CHALLENGE. + +NAME_REGISTER_CHALLENGE: name query a server to establish whether to +hand over a unique name to another server that asked for that name. + +if a positive response is received to the name query, this indicates +that the current owner still wants the name. we therefore refresh +the name records indicating that the current owner still wants it, +and we inform the potential owner (the other host) that they cannot +have it. + +if a negative response is received, this indicates that for some +reason (for example, it may have just released the name or the +WINS server may have had out-of-date records) the current owner +does not want the name. in this instance, the name records are +updated to give this unique name to the other host that wanted +it, and the other host is informed that they can have it. + +a failure to respond on the part of the current owner of the name +is dealt with in dead_netbios_entry(). + + +/************************************************************************* + response_name_status_check() + *************************************************************************/ + +this function receives responses to samba 'states' NAME_STATUS_CHECK +and NAME_STATUS_MASTER_CHECK + +NAME_STATUS_MASTER_CHECK: name status a primary domain controller, + confirm its domain and then initiate syncing + its browse list. + +NAME_STATUS_CHECK: same as NAME_STATUS_MASTER_CHECK except the name status + is issued to a master browser. + +if we don't know what workgroup a server is responsible for, but we +know that there is a master browser at a certain ip, we can issue a +name status check. from the response received, there will be +a master browser netbios entry. this will allow us to synchronise +browse lists with that machine and then add the information to the +correct part of samba's workgroup - server database. + + +/************************************************************************* + response_server_check() + *************************************************************************/ + +this function receives responses to samba 'states' NAME_QUERY_MST_SRV_CHK, +NAME_QUERY_SRV_CHK and NAME_QUERY_FIND_MST. + +NAME_QUERY_FIND_MST: issued as a broadcast when we wish to find out all + master browsers (i.e all servers that have registered + the NetBIOS name ^1^2__MSBROWSE__^2(0x1), and then + issue a NAME_STATUS_MASTER_CHECK on any servers that + respond, which will initiate a sync browse lists. + +NAME_QUERY_MST_SRV_CHK: same as a NAME_QUERY_FIND_MST except this is sent + to a primary domain controller. + +NAME_QUERY_SRV_CHK: same as a NAME_QUERY_MST_SRV_CHK except this is sent to + a master browser. + +the purpose of each of these states is to do a broadcast name query, or +a name query directed at a WINS server, then to all hosts that respond, +we issue a name status check, which will confirm for us the workgroup +or domain name, and then initiate issuing a sync browse list call with +that server. + +a NAME_QUERY_SRV_CHK is sent when samba receives a list of backup +browsers. it checks to see if that server is alive (by doing a +name query on a server) and then syncs browse lists with it. + + +/************************************************************************* + response_name_reg() + *************************************************************************/ + +this function is responsible for dealing with samba's registration +attempts, by broadcast to a local subnet, or point-to-point with +another WINS server. + +please note that it cannot cope with END-NODE CHALLENGE REGISTRATION +RESPONSEs at present. + +when a response is received, samba determines if the response is a +positive or a negative one. if it is a positive response, the name +is added to samba's database. + +when a negative response is received, samba will remove the name +from its database. if, however, the name is a browser type (0x1b is +a primary domain controller type name; or 0x1d, which is a master +browser type name) then it must also stop being a primary domain +controller or master browser respectively, depending on what kind +of name was rejected. + +(when no response is received, then expire_netbios_response_entries() +is expected to deal with this. the only case that is dealt with here +at present is when the registration was done by broadcast. if there +is no challenge to the broadcast registration, it is implicitly +assumed that claiming the name is acceptable). + + +/************************************************************************* + response_name_release() + *************************************************************************/ + +this function is responsible for removing samba's NetBIOS name when +samba contacts another WINS server with which it had registered the +name. + +only positive name releases are expected and dealt with. exactly what +to do if a negative name release (i.e someone says 'oi! you have to +keep that name!') is received is uncertain. + +(when no response is received, then expire_netbios_response_entries() +is expected to deal with this. if there is no challenge to the release +of the name, the name is then removed from that subnet's NetBIOS +name database). + diff --git a/source3/namework.c b/source3/namework.c index 7307ddce04..d6a94f17aa 100644 --- a/source3/namework.c +++ b/source3/namework.c @@ -2,7 +2,7 @@ Unix SMB/Netbios implementation. Version 1.9. NBT netbios routines and daemon - version 2 - Copyright (C) Andrew Tridgell 1994-1995 + Copyright (C) Andrew Tridgell 1994-1996 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -64,8 +64,6 @@ extern int updatecount; extern time_t StartupTime; -#define GET_TTL(ttl) ((ttl)?MIN(ttl,lp_max_ttl()):lp_max_ttl()) - /**************************************************************************** tell a server to become a backup browser @@ -385,7 +383,7 @@ static void process_rcv_backup_list(struct packet_struct *p,char *buf) { queue_netbios_packet(d1,ClientNMB,NMB_QUERY,NAME_QUERY_SRV_CHK, work->work_group,0x1d,0,0, - False,False,back_ip); + False,False,back_ip,back_ip); return; } } @@ -812,49 +810,3 @@ void process_browse_packet(struct packet_struct *p,char *buf,int len) } -/**************************************************************************** -process udp 138 datagrams -****************************************************************************/ -void process_dgram(struct packet_struct *p) -{ - char *buf; - char *buf2; - int len; - struct dgram_packet *dgram = &p->packet.dgram; - - if (dgram->header.msg_type != 0x10 && - dgram->header.msg_type != 0x11 && - dgram->header.msg_type != 0x12) { - /* don't process error packets etc yet */ - return; - } - - buf = &dgram->data[0]; - buf -= 4; /* XXXX for the pseudo tcp length - - someday I need to get rid of this */ - - if (CVAL(buf,smb_com) != SMBtrans) return; - - len = SVAL(buf,smb_vwv11); - buf2 = smb_base(buf) + SVAL(buf,smb_vwv12); - - DEBUG(4,("datagram from %s to %s for %s of type %d len=%d\n", - namestr(&dgram->source_name),namestr(&dgram->dest_name), - smb_buf(buf),CVAL(buf2,0),len)); - - - if (len <= 0) return; - - /* datagram packet received for the browser mailslot */ - if (strequal(smb_buf(buf),BROWSE_MAILSLOT)) { - process_browse_packet(p,buf2,len); - return; - } - - /* datagram packet received for the domain log on mailslot */ - if (strequal(smb_buf(buf),NET_LOGON_MAILSLOT)) { - process_logon_packet(p,buf2,len); - return; - } -} - diff --git a/source3/namework.doc b/source3/namework.doc index 4616b328da..8e7e82a000 100644 --- a/source3/namework.doc +++ b/source3/namework.doc @@ -2,26 +2,15 @@ the module namework.c deals with NetBIOS datagram packets, primarily. it deals with nmbd's workgroup browser side and the domain log in side. none of the functionality here has specification documents available. -empirical observation of packet traces has been the order of the day. +empirical observation of packet traces has been the order of the day, +along with some guess-work. beware! the receipt of datagram packets for workgroup browsing are dealt with here. some of the functions listed here will call others outside of this -module, or will activate functionality dealt with by other modules. - -these include: namedb.c, nameannounce.c, nameelect.c, - namelogon.c, and namebrowse.c. - - -/************************************************************************* - process_dgram() - *************************************************************************/ - -this function is responsible for identifying whether the datagram -packet received is a browser packet or a domain logon packet. it -also does some filtering of certain types of packets (e.g it -filters out error packets). +module, or will activate functionality dealt with by other modules +(namedb, nameannounce, nameelect, namelogon, and namebrowse). /************************************************************************* @@ -95,7 +84,8 @@ samba do not implement this latter option. this datagram is sent by a master browser to a primary domain controller. it is a way to ensure that master browsers are kept in sync with a primary domain controller across a wide -area network. +area network. on receipt of an ANN_MasterAnnouncement we +should sync browse lists with the sender. (i never got the hang of this one when i was experimenting. i forget exactly what it's for, and i never fully worked |