diff options
Diffstat (limited to 'source3/nmbd/nmbd_become_dmb.c')
-rw-r--r-- | source3/nmbd/nmbd_become_dmb.c | 398 |
1 files changed, 398 insertions, 0 deletions
diff --git a/source3/nmbd/nmbd_become_dmb.c b/source3/nmbd/nmbd_become_dmb.c new file mode 100644 index 0000000..8246f59 --- /dev/null +++ b/source3/nmbd/nmbd_become_dmb.c @@ -0,0 +1,398 @@ +/* + Unix SMB/CIFS implementation. + NBT netbios routines and daemon - version 2 + Copyright (C) Andrew Tridgell 1994-1998 + Copyright (C) Luke Kenneth Casson Leighton 1994-1998 + Copyright (C) Jeremy Allison 1994-2003 + + 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 + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "includes.h" +#include "../librpc/gen_ndr/svcctl.h" +#include "nmbd/nmbd.h" + +extern uint16_t samba_nb_type; /* Samba's NetBIOS type. */ + +static void become_domain_master_browser_bcast(const char *); + +/**************************************************************************** + Fail to become a Domain Master Browser on a subnet. + ****************************************************************************/ + +static void become_domain_master_fail(struct subnet_record *subrec, + struct response_record *rrec, + struct nmb_name *fail_name) +{ + unstring failname; + struct work_record *work; + struct server_record *servrec; + + pull_ascii_nstring(failname, sizeof(failname), fail_name->name); + work = find_workgroup_on_subnet(subrec, failname); + if(!work) { + DEBUG(0,("become_domain_master_fail: Error - cannot find \ +workgroup %s on subnet %s\n", failname, subrec->subnet_name)); + return; + } + + /* Set the state back to DOMAIN_NONE. */ + work->dom_state = DOMAIN_NONE; + + if((servrec = find_server_in_workgroup( work, lp_netbios_name())) == NULL) { + DEBUG(0,("become_domain_master_fail: Error - cannot find server %s \ +in workgroup %s on subnet %s\n", + lp_netbios_name(), work->work_group, subrec->subnet_name)); + return; + } + + /* Update our server status. */ + servrec->serv.type &= ~SV_TYPE_DOMAIN_MASTER; + + /* Tell the namelist writer to write out a change. */ + subrec->work_changed = True; + + DEBUG(0,("become_domain_master_fail: Failed to become a domain master browser for \ +workgroup %s on subnet %s. Couldn't register name %s.\n", + work->work_group, subrec->subnet_name, nmb_namestr(fail_name))); +} + +/**************************************************************************** + Become a Domain Master Browser on a subnet. + ****************************************************************************/ + +static void become_domain_master_stage2(struct subnet_record *subrec, + struct userdata_struct *userdata, + struct nmb_name *registered_name, + uint16_t nb_flags, + int ttl, struct in_addr registered_ip) +{ + unstring regname; + struct work_record *work; + struct server_record *servrec; + + pull_ascii_nstring(regname, sizeof(regname), registered_name->name); + work = find_workgroup_on_subnet( subrec, regname); + + if(!work) { + DEBUG(0,("become_domain_master_stage2: Error - cannot find \ +workgroup %s on subnet %s\n", regname, subrec->subnet_name)); + return; + } + + if((servrec = find_server_in_workgroup( work, lp_netbios_name())) == NULL) { + DEBUG(0,("become_domain_master_stage2: Error - cannot find server %s \ +in workgroup %s on subnet %s\n", + lp_netbios_name(), regname, subrec->subnet_name)); + work->dom_state = DOMAIN_NONE; + return; + } + + /* Set the state in the workgroup structure. */ + work->dom_state = DOMAIN_MST; /* Become domain master. */ + + /* Update our server status. */ + servrec->serv.type |= (SV_TYPE_NT|SV_TYPE_DOMAIN_MASTER); + + /* Tell the namelist writer to write out a change. */ + subrec->work_changed = True; + + if( DEBUGLVL( 0 ) ) { + dbgtext( "*****\n\nSamba server %s ", lp_netbios_name() ); + dbgtext( "is now a domain master browser for " ); + dbgtext( "workgroup %s ", work->work_group ); + dbgtext( "on subnet %s\n\n*****\n", subrec->subnet_name ); + } + + if( subrec == unicast_subnet ) { + struct nmb_name nmbname; + struct in_addr my_first_ip; + const struct in_addr *nip; + + /* Put our name and first IP address into the + workgroup struct as domain master browser. This + will stop us syncing with ourself if we are also + a local master browser. */ + + make_nmb_name(&nmbname, lp_netbios_name(), 0x20); + + work->dmb_name = nmbname; + + /* Pick the first interface IPv4 address as the domain master + * browser ip. */ + nip = first_ipv4_iface(); + if (!nip) { + DEBUG(0,("become_domain_master_stage2: " + "Error. get_interface returned NULL\n")); + return; + } + my_first_ip = *nip; + + putip((char *)&work->dmb_addr, &my_first_ip); + + /* We successfully registered by unicast with the + WINS server. We now expect to become the domain + master on the local subnets. If this fails, it's + probably a 1.9.16p2 to 1.9.16p11 server's fault. + + This is a configuration issue that should be addressed + by the network administrator - you shouldn't have + several machines configured as a domain master browser + for the same WINS scope (except if they are 1.9.17 or + greater, and you know what you're doing. + + see docs/DOMAIN.txt. + + */ + become_domain_master_browser_bcast(work->work_group); + } else { + /* + * Now we are a domain master on a broadcast subnet, we need to add + * the WORKGROUP<1b> name to the unicast subnet so that we can answer + * unicast requests sent to this name. This bug wasn't found for a while + * as it is strange to have a DMB without using WINS. JRA. + */ + insert_permanent_name_into_unicast(subrec, registered_name, nb_flags); + } +} + +/**************************************************************************** + Start the name registration process when becoming a Domain Master Browser + on a subnet. +****************************************************************************/ + +static void become_domain_master_stage1(struct subnet_record *subrec, const char *wg_name) +{ + struct work_record *work; + + DEBUG(2,("become_domain_master_stage1: Becoming domain master browser for \ +workgroup %s on subnet %s\n", wg_name, subrec->subnet_name)); + + /* First, find the workgroup on the subnet. */ + if((work = find_workgroup_on_subnet( subrec, wg_name )) == NULL) { + DEBUG(0,("become_domain_master_stage1: Error - unable to find workgroup %s on subnet %s.\n", + wg_name, subrec->subnet_name)); + return; + } + + DEBUG(3,("become_domain_master_stage1: go to first stage: register <1b> name\n")); + work->dom_state = DOMAIN_WAIT; + + /* WORKGROUP<1b> is the domain master browser name. */ + register_name(subrec, work->work_group,0x1b,samba_nb_type, + become_domain_master_stage2, + become_domain_master_fail, NULL); +} + +/**************************************************************************** + Function called when a query for a WORKGROUP<1b> name succeeds. + This is normally a fail condition as it means there is already + a domain master browser for a workgroup and we were trying to + become one. +****************************************************************************/ + +static void become_domain_master_query_success(struct subnet_record *subrec, + struct userdata_struct *userdata, + struct nmb_name *nmbname, struct in_addr ip, + struct res_rec *rrec) +{ + unstring name; + struct in_addr allones_ip; + + pull_ascii_nstring(name, sizeof(name), nmbname->name); + + /* If the given ip is not ours, then we can't become a domain + controler as the name is already registered. + */ + + /* BUG note. Samba 1.9.16p11 servers seem to return the broadcast + address or zero ip for this query. Pretend this is ok. */ + + allones_ip.s_addr = htonl(INADDR_BROADCAST); + + if(ismyip_v4(ip) || ip_equal_v4(allones_ip, ip) || is_zero_ip_v4(ip)) { + if( DEBUGLVL( 3 ) ) { + dbgtext( "become_domain_master_query_success():\n" ); + dbgtext( "Our address (%s) ", inet_ntoa(ip) ); + dbgtext( "returned in query for name %s ", nmb_namestr(nmbname) ); + dbgtext( "(domain master browser name) " ); + dbgtext( "on subnet %s.\n", subrec->subnet_name ); + dbgtext( "Continuing with domain master code.\n" ); + } + + become_domain_master_stage1(subrec, name); + } else { + if( DEBUGLVL( 0 ) ) { + dbgtext( "become_domain_master_query_success:\n" ); + dbgtext( "There is already a domain master browser at " ); + dbgtext( "IP %s for workgroup %s ", inet_ntoa(ip), name ); + dbgtext( "registered on subnet %s.\n", subrec->subnet_name ); + } + } +} + +/**************************************************************************** + Function called when a query for a WORKGROUP<1b> name fails. + This is normally a success condition as it then allows us to register + our own Domain Master Browser name. + ****************************************************************************/ + +static void become_domain_master_query_fail(struct subnet_record *subrec, + struct response_record *rrec, + struct nmb_name *question_name, int fail_code) +{ + unstring name; + + /* If the query was unicast, and the error is not NAM_ERR (name didn't exist), + then this is a failure. Otherwise, not finding the name is what we want. */ + + if((subrec == unicast_subnet) && (fail_code != NAM_ERR)) { + DEBUG(0,("become_domain_master_query_fail: Error %d returned when \ +querying WINS server for name %s.\n", + fail_code, nmb_namestr(question_name))); + return; + } + + /* Otherwise - not having the name allows us to register it. */ + pull_ascii_nstring(name, sizeof(name), question_name->name); + become_domain_master_stage1(subrec, name); +} + +/**************************************************************************** + Attempt to become a domain master browser on all broadcast subnets. + ****************************************************************************/ + +static void become_domain_master_browser_bcast(const char *workgroup_name) +{ + struct subnet_record *subrec; + + for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) { + struct work_record *work = find_workgroup_on_subnet(subrec, workgroup_name); + + if (work && (work->dom_state == DOMAIN_NONE)) { + struct nmb_name nmbname; + make_nmb_name(&nmbname,workgroup_name,0x1b); + + /* + * Check for our name on the given broadcast subnet first, only initiate + * further processing if we cannot find it. + */ + + if (find_name_on_subnet(subrec, &nmbname, FIND_SELF_NAME) == NULL) { + if( DEBUGLVL( 0 ) ) { + dbgtext( "become_domain_master_browser_bcast:\n" ); + dbgtext( "Attempting to become domain master browser on " ); + dbgtext( "workgroup %s on subnet %s\n", + workgroup_name, subrec->subnet_name ); + } + + /* Send out a query to establish whether there's a + domain controller on the local subnet. If not, + we can become a domain controller. + */ + + DEBUG(0,("become_domain_master_browser_bcast: querying subnet %s \ +for domain master browser on workgroup %s\n", subrec->subnet_name, workgroup_name)); + + query_name(subrec, workgroup_name, nmbname.name_type, + become_domain_master_query_success, + become_domain_master_query_fail, + NULL); + } + } + } +} + +/**************************************************************************** + Attempt to become a domain master browser by registering with WINS. + ****************************************************************************/ + +static void become_domain_master_browser_wins(const char *workgroup_name) +{ + struct work_record *work; + + work = find_workgroup_on_subnet(unicast_subnet, workgroup_name); + + if (work && (work->dom_state == DOMAIN_NONE)) { + struct nmb_name nmbname; + + make_nmb_name(&nmbname,workgroup_name,0x1b); + + /* + * Check for our name on the unicast subnet first, only initiate + * further processing if we cannot find it. + */ + + if (find_name_on_subnet(unicast_subnet, &nmbname, FIND_SELF_NAME) == NULL) { + if( DEBUGLVL( 0 ) ) { + dbgtext( "become_domain_master_browser_wins:\n" ); + dbgtext( "Attempting to become domain master browser " ); + dbgtext( "on workgroup %s, subnet %s.\n", + workgroup_name, unicast_subnet->subnet_name ); + } + + /* Send out a query to establish whether there's a + domain master broswer registered with WINS. If not, + we can become a domain master browser. + */ + + DEBUG(0,("become_domain_master_browser_wins: querying WINS server from IP %s \ +for domain master browser name %s on workgroup %s\n", + inet_ntoa(unicast_subnet->myip), nmb_namestr(&nmbname), workgroup_name)); + + query_name(unicast_subnet, workgroup_name, nmbname.name_type, + become_domain_master_query_success, + become_domain_master_query_fail, + NULL); + } + } +} + +/**************************************************************************** + Add the domain logon server and domain master browser names + if we are set up to do so. + **************************************************************************/ + +void add_domain_names(time_t t) +{ + static time_t lastrun = 0; + + if ((lastrun != 0) && (t < lastrun + (CHECK_TIME_ADD_DOM_NAMES * 60))) + return; + + lastrun = t; + + /* Do the "internet group" - <1c> names. */ + if (IS_DC) + add_logon_names(); + + /* Do the domain master names. */ + if(lp_domain_master()) { + if(we_are_a_wins_client()) { + /* We register the WORKGROUP<1b> name with the WINS + server first, and call add_domain_master_bcast() + only if this is successful. + + This results in domain logon services being gracefully provided, + as opposed to the aggressive nature of 1.9.16p2 to 1.9.16p11. + 1.9.16p2 to 1.9.16p11 - due to a bug in namelogon.c, + cannot provide domain master / domain logon services. + */ + become_domain_master_browser_wins(lp_workgroup()); + } else { + become_domain_master_browser_bcast(lp_workgroup()); + } + } +} |