From 64f0348a3f994334abe64a4d4896109c3c8c9039 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Sat, 13 Dec 1997 14:16:07 +0000 Subject: This is it ! The mega-merge of the JRA_NMBD_REWRITE branch back into the main tree. For the cvs logs of all the files starting nmbd_*.c, look in the JRA_NMBD_REWRITE branch. That branch has now been discontinued. Jeremy. (This used to be commit d80b0cb645f81d16734929a0b27a91c6650499bb) --- source3/nmbd/asyncdns.c | 64 +- source3/nmbd/nmbd.c | 811 ++++++++------- source3/nmbd/nmbd_become_dmb.c | 471 +++++++++ source3/nmbd/nmbd_become_lmb.c | 534 ++++++++++ source3/nmbd/nmbd_browserdb.c | 184 ++++ source3/nmbd/nmbd_browsesync.c | 458 +++++++++ source3/nmbd/nmbd_elections.c | 348 +++++++ source3/nmbd/nmbd_incomingdgrams.c | 625 ++++++++++++ source3/nmbd/nmbd_incomingrequests.c | 556 +++++++++++ source3/nmbd/nmbd_lmhosts.c | 168 ++++ source3/nmbd/nmbd_logonnames.c | 166 +++ source3/nmbd/nmbd_mynames.c | 165 +++ source3/nmbd/nmbd_namelistdb.c | 586 +++++++++++ source3/nmbd/nmbd_namequery.c | 234 +++++ source3/nmbd/nmbd_nameregister.c | 390 ++++++++ source3/nmbd/nmbd_namerelease.c | 238 +++++ source3/nmbd/nmbd_nodestatus.c | 99 ++ source3/nmbd/nmbd_packets.c | 1775 +++++++++++++++++++++++++++++++++ source3/nmbd/nmbd_processlogon.c | 250 +++++ source3/nmbd/nmbd_responserecordsdb.c | 238 +++++ source3/nmbd/nmbd_sendannounce.c | 477 +++++++++ source3/nmbd/nmbd_serverlistdb.c | 454 +++++++++ source3/nmbd/nmbd_subnetdb.c | 291 ++++++ source3/nmbd/nmbd_winsproxy.c | 195 ++++ source3/nmbd/nmbd_winsserver.c | 1565 +++++++++++++++++++++++++++++ source3/nmbd/nmbd_workgroupdb.c | 356 +++++++ 26 files changed, 11318 insertions(+), 380 deletions(-) create mode 100644 source3/nmbd/nmbd_become_dmb.c create mode 100644 source3/nmbd/nmbd_become_lmb.c create mode 100644 source3/nmbd/nmbd_browserdb.c create mode 100644 source3/nmbd/nmbd_browsesync.c create mode 100644 source3/nmbd/nmbd_elections.c create mode 100644 source3/nmbd/nmbd_incomingdgrams.c create mode 100644 source3/nmbd/nmbd_incomingrequests.c create mode 100644 source3/nmbd/nmbd_lmhosts.c create mode 100644 source3/nmbd/nmbd_logonnames.c create mode 100644 source3/nmbd/nmbd_mynames.c create mode 100644 source3/nmbd/nmbd_namelistdb.c create mode 100644 source3/nmbd/nmbd_namequery.c create mode 100644 source3/nmbd/nmbd_nameregister.c create mode 100644 source3/nmbd/nmbd_namerelease.c create mode 100644 source3/nmbd/nmbd_nodestatus.c create mode 100644 source3/nmbd/nmbd_packets.c create mode 100644 source3/nmbd/nmbd_processlogon.c create mode 100644 source3/nmbd/nmbd_responserecordsdb.c create mode 100644 source3/nmbd/nmbd_sendannounce.c create mode 100644 source3/nmbd/nmbd_serverlistdb.c create mode 100644 source3/nmbd/nmbd_subnetdb.c create mode 100644 source3/nmbd/nmbd_winsproxy.c create mode 100644 source3/nmbd/nmbd_winsserver.c create mode 100644 source3/nmbd/nmbd_workgroupdb.c (limited to 'source3/nmbd') diff --git a/source3/nmbd/asyncdns.c b/source3/nmbd/asyncdns.c index 57d0eda9b3..1ee9dab065 100644 --- a/source3/nmbd/asyncdns.c +++ b/source3/nmbd/asyncdns.c @@ -22,28 +22,29 @@ extern int DEBUGLEVEL; - /*************************************************************************** - add a DNS result to the name cache - ****************************************************************************/ + Add a DNS result to the name cache. +****************************************************************************/ + static struct name_record *add_dns_result(struct nmb_name *question, struct in_addr addr) { int name_type = question->name_type; char *qname = question->name; - + + if (!addr.s_addr) { /* add the fail to WINS cache of names. give it 1 hour in the cache */ - DEBUG(3,("Negative DNS answer for %s\n", qname)); - add_netbios_entry(wins_client_subnet,qname,name_type,NB_ACTIVE,60*60, - DNSFAIL,addr,True); + DEBUG(3,("add_dns_result: Negative DNS answer for %s\n", qname)); + add_name_to_subnet(wins_server_subnet,qname,name_type, + NB_ACTIVE, 60*60, DNSFAIL_NAME, 1, &addr); return NULL; } /* add it to our WINS cache of names. give it 2 hours in the cache */ - DEBUG(3,("DNS gave answer for %s of %s\n", qname, inet_ntoa(addr))); + DEBUG(3,("add_dns_result: DNS gave answer for %s of %s\n", qname, inet_ntoa(addr))); - return add_netbios_entry(wins_client_subnet,qname,name_type,NB_ACTIVE, - 2*60*60,DNS,addr, True); + return add_name_to_subnet(wins_server_subnet,qname,name_type, + NB_ACTIVE, 2*60*60, DNS_NAME, 1, &addr); } @@ -101,6 +102,23 @@ static void asyncdns_process(void) _exit(0); } +/**************************************************************************** ** + catch a sigterm + We need a separate term handler here so we don't release any + names that our parent is going to release, or overwrite a + WINS db that our parent is going to write. + **************************************************************************** */ + +static int sig_term() +{ + BlockSignals(True,SIGTERM); + + DEBUG(0,("async dns child. Got SIGTERM: going down...\n")); + + exit(0); + /* Keep compiler happy.. */ + return 0; +} /*************************************************************************** create a child process to handle DNS lookups @@ -131,6 +149,7 @@ void start_async_dns(void) signal(SIGUSR2, SIG_IGN); signal(SIGUSR1, SIG_IGN); signal(SIGHUP, SIG_IGN); + signal( SIGTERM, SIGNAL_CAST sig_term ); asyncdns_process(); } @@ -142,7 +161,7 @@ check if a particular name is already being queried static BOOL query_current(struct query_record *r) { return dns_current && - name_equal(&r->name, + nmb_name_equal(&r->name, &dns_current->packet.nmb.question.question_name); } @@ -166,6 +185,7 @@ void run_dns_queue(void) { struct query_record r; struct packet_struct *p, *p2; + struct name_record *namerec; int size; if (fd_in == -1) @@ -184,13 +204,16 @@ void run_dns_queue(void) return; } - add_dns_result(&r.name, r.result); + namerec = add_dns_result(&r.name, r.result); if (dns_current) { if (query_current(&r)) { - DEBUG(3,("DNS calling reply_name_query\n")); + DEBUG(3,("DNS calling send_wins_name_query_response\n")); in_dns = 1; - reply_name_query(dns_current); + if(namerec == NULL) + send_wins_name_query_response(NAM_ERR, dns_current, NULL); + else + send_wins_name_query_response(0,dns_current,namerec); in_dns = 0; } @@ -205,10 +228,13 @@ void run_dns_queue(void) struct nmb_packet *nmb = &p->packet.nmb; struct nmb_name *question = &nmb->question.question_name; - if (name_equal(question, &r.name)) { - DEBUG(3,("DNS calling reply_name_query\n")); + if (nmb_name_equal(question, &r.name)) { + DEBUG(3,("DNS calling send_wins_name_query_response\n")); in_dns = 1; - reply_name_query(p); + if(namerec == NULL) + send_wins_name_query_response(NAM_ERR, p, NULL); + else + send_wins_name_query_response(0,p,namerec); in_dns = 0; p->locked = False; @@ -286,6 +312,10 @@ BOOL queue_dns_query(struct packet_struct *p,struct nmb_name *question, dns_ip.s_addr = interpret_addr(qname); *n = add_dns_result(question, dns_ip); + if(*n == NULL) + send_wins_name_query_response(NAM_ERR, p, NULL); + else + send_wins_name_query_response(0, p, *n); return False; } #endif diff --git a/source3/nmbd/nmbd.c b/source3/nmbd/nmbd.c index a34e2caf42..11cd50cd76 100644 --- a/source3/nmbd/nmbd.c +++ b/source3/nmbd/nmbd.c @@ -34,8 +34,8 @@ pstring servicesf = CONFIGFILE; extern pstring scope; -int ClientNMB = -1; -int ClientDGRAM = -1; +int ClientNMB = -1; +int ClientDGRAM = -1; int global_nmb_port = -1; extern pstring myhostname; @@ -49,47 +49,47 @@ static BOOL is_daemon = False; /* what server type are we currently */ -time_t StartupTime =0; +time_t StartupTime = 0; extern struct in_addr ipzero; - /**************************************************************************** +/**************************************************************************** ** catch a sigterm - ****************************************************************************/ + **************************************************************************** */ static int sig_term() { BlockSignals(True,SIGTERM); DEBUG(0,("Got SIGTERM: going down...\n")); - /* write out wins.dat file if samba is a WINS server */ - dump_names(); + /* Write out wins.dat file if samba is a WINS server */ + wins_write_database(); - /* remove all samba names, with wins server if necessary. */ - remove_my_names(); + /* Remove all SELF registered names. */ + release_my_names(); - /* announce all server entries as 0 time-to-live, 0 type */ - /* XXXX don't care if we never receive a response back... yet */ + /* Announce all server entries as 0 time-to-live, 0 type. */ announce_my_servers_removed(); - /* XXXX other things: if we are a master browser, force an election? */ - exit(0); /* Keep compiler happy.. */ return 0; -} +} /* sig_term */ -/**************************************************************************** -catch a sighup -****************************************************************************/ +/**************************************************************************** ** + catch a sighup + **************************************************************************** */ static int sig_hup(void) { - BlockSignals(True,SIGHUP); + BlockSignals( True, SIGHUP ); + + DEBUG( 0, ( "Got SIGHUP dumping debug info.\n" ) ); + + write_browse_list( 0, True ); - DEBUG(0,("Got SIGHUP (reload not implemented)\n")); - dump_names(); - reload_services(True); + dump_all_namelists(); + reload_services( True ); set_samba_nb_type(); @@ -98,239 +98,294 @@ static int sig_hup(void) signal(SIGHUP,SIGNAL_CAST sig_hup); #endif return(0); -} +} /* sig_hup */ -/**************************************************************************** -catch a sigpipe -****************************************************************************/ +/**************************************************************************** ** + catch a sigpipe + **************************************************************************** */ static int sig_pipe(void) { - BlockSignals(True,SIGPIPE); + BlockSignals( True, SIGPIPE ); - DEBUG(0,("Got SIGPIPE\n")); - if (!is_daemon) + DEBUG( 0, ("Got SIGPIPE\n") ); + if ( !is_daemon ) exit(1); - BlockSignals(False,SIGPIPE); + BlockSignals( False, SIGPIPE ); return(0); -} +} /* sig_pipe */ #if DUMP_CORE -/******************************************************************* -prepare to dump a core file - carefully! -********************************************************************/ +/**************************************************************************** ** + prepare to dump a core file - carefully! + **************************************************************************** */ static BOOL dump_core(void) { char *p; pstring dname; - pstrcpy(dname,debugf); - if ((p=strrchr(dname,'/'))) *p=0; - strcat(dname,"/corefiles"); - mkdir(dname,0700); - sys_chown(dname,getuid(),getgid()); - chmod(dname,0700); - if (chdir(dname)) return(False); - umask(~(0700)); + pstrcpy( dname, debugf ); + if ((p=strrchr(dname,'/'))) + *p=0; + strcat( dname, "/corefiles" ); + mkdir( dname, 0700 ); + sys_chown( dname, getuid(), getgid() ); + chmod( dname, 0700 ); + if ( chdir(dname) ) + return( False ); + umask( ~(0700) ); #ifndef NO_GETRLIMIT #ifdef RLIMIT_CORE { struct rlimit rlp; - getrlimit(RLIMIT_CORE, &rlp); - rlp.rlim_cur = MAX(4*1024*1024,rlp.rlim_cur); - setrlimit(RLIMIT_CORE, &rlp); - getrlimit(RLIMIT_CORE, &rlp); - DEBUG(3,("Core limits now %d %d\n",rlp.rlim_cur,rlp.rlim_max)); + getrlimit( RLIMIT_CORE, &rlp ); + rlp.rlim_cur = MAX( 4*1024*1024, rlp.rlim_cur ); + setrlimit( RLIMIT_CORE, &rlp ); + getrlimit( RLIMIT_CORE, &rlp ); + DEBUG( 3, ( "Core limits now %d %d\n", rlp.rlim_cur, rlp.rlim_max ) ); } #endif #endif - DEBUG(0,("Dumping core in %s\n",dname)); - return(True); -} + DEBUG( 0, ( "Dumping core in %s\n",dname ) ); + return( True ); +} /* dump_core */ #endif -/**************************************************************************** -possibly continue after a fault -****************************************************************************/ +/**************************************************************************** ** + possibly continue after a fault + **************************************************************************** */ static void fault_continue(void) { #if DUMP_CORE dump_core(); #endif -} +} /* fault_continue */ -/******************************************************************* - expire old names from the namelist and server list - ******************************************************************/ +/**************************************************************************** ** + expire old names from the namelist and server list + **************************************************************************** */ static void expire_names_and_servers(time_t t) { static time_t lastrun = 0; - if (!lastrun) lastrun = t; - if (t < lastrun + 5) return; + if ( !lastrun ) + lastrun = t; + if ( t < (lastrun + 5) ) + return; lastrun = t; - + + /* + * Expire any timed out names on all the broadcast + * subnets and those registered with the WINS server. + * (nmbd_namelistdb.c) + */ expire_names(t); - expire_servers(t); -} -/***************************************************************************** + /* + * Go through all the broadcast subnets and for each + * workgroup known on that subnet remove any expired + * server names. If a workgroup has an empty serverlist + * and has itself timed out then remove the workgroup. + * (nmbd_workgroupdb.c) + */ + expire_workgroups_and_servers(t); +} /* expire_names_and_servers */ + +/**************************************************************************** ** reload the services file - **************************************************************************/ + **************************************************************************** */ BOOL reload_services(BOOL test) { BOOL ret; extern fstring remote_machine; - strcpy(remote_machine,"nmbd"); + strcpy( remote_machine, "nmbd" ); - if (lp_loaded()) + if ( lp_loaded() ) + { + pstring fname; + pstrcpy( fname,lp_configfile()); + if (file_exist(fname,NULL) && !strcsequal(fname,servicesf)) { - pstring fname; - pstrcpy(fname,lp_configfile()); - if (file_exist(fname,NULL) && !strcsequal(fname,servicesf)) - { - pstrcpy(servicesf,fname); - test = False; - } + pstrcpy(servicesf,fname); + test = False; } + } - if (test && !lp_file_list_changed()) + if ( test && !lp_file_list_changed() ) return(True); - ret = lp_load(servicesf,True); + ret = lp_load( servicesf, True ); /* perhaps the config filename is now set */ - if (!test) { - DEBUG(3,("services not loaded\n")); - reload_services(True); + if ( !test ) + { + DEBUG( 3, ( "services not loaded\n" ) ); + reload_services( True ); } /* Do a sanity check for a misconfigured nmbd */ - if(lp_wins_support() && *lp_wins_server()) { + if( lp_wins_support() && *lp_wins_server() ) + { DEBUG(0,("ERROR: both 'wins support = true' and 'wins server = ' \ cannot be set in the smb.conf file. nmbd aborting.\n")); exit(10); } return(ret); -} - - - -/**************************************************************************** -load a netbios hosts file -****************************************************************************/ -static void load_hosts_file(char *fname) -{ - FILE *f = fopen(fname,"r"); - pstring line; - if (!f) { - DEBUG(2,("Can't open lmhosts file %s\n",fname)); - return; - } - - while (!feof(f)) - { - pstring ip,name,flags,extra; - struct subnet_record *d; - char *ptr; - int count = 0; - struct in_addr ipaddr; - enum name_source source = LMHOSTS; - - if (!fgets_slash(line,sizeof(pstring),f)) continue; - - if (*line == '#') continue; - - strcpy(ip,""); - strcpy(name,""); - strcpy(flags,""); - - ptr = line; - - if (next_token(&ptr,ip ,NULL)) ++count; - if (next_token(&ptr,name ,NULL)) ++count; - if (next_token(&ptr,flags,NULL)) ++count; - if (next_token(&ptr,extra,NULL)) ++count; - - if (count <= 0) continue; - - if (count > 0 && count < 2) { - DEBUG(0,("Ill formed hosts line [%s]\n",line)); - continue; - } - - if (count >= 4) { - DEBUG(0,("too many columns in %s (obsolete syntax)\n",fname)); - continue; - } - - DEBUG(4, ("lmhost entry: %s %s %s\n", ip, name, flags)); - - if (strchr(flags,'G') || strchr(flags,'S')) { - DEBUG(0,("group flag in %s ignored (obsolete)\n",fname)); - continue; - } - - if (strchr(flags,'M')) { - source = SELF; - pstrcpy(myname,name); - } - - ipaddr = *interpret_addr2(ip); - d = find_subnet_all(ipaddr); - if (d) { - add_netbios_entry(d,name,0x00,NB_ACTIVE,0,source,ipaddr,True); - add_netbios_entry(d,name,0x20,NB_ACTIVE,0,source,ipaddr,True); - } - } - - fclose(f); -} +} /* reload_services */ - -/**************************************************************************** - The main select loop. - ***************************************************************************/ +/**************************************************************************** ** + The main select loop. + **************************************************************************** */ static void process(void) { BOOL run_election; - while (True) - { - time_t t = time(NULL); - run_election = check_elections(); - if(listen_for_packets(run_election)) - return; - - run_packet_queue(); - run_elections(t); - - announce_host(t); - announce_master(t); - announce_remote(t); - browse_sync_remote(t); - - query_refresh_names(t); - - expire_names_and_servers(t); - expire_netbios_response_entries(t); - refresh_my_names(t); - - write_browse_list(t); - do_browser_lists(t); - check_master_browser(t); - add_domain_names(t); - } -} + while( True ) + { + time_t t = time(NULL); + + /* + * Check all broadcast subnets to see if + * we need to run an election on any of them. + * (nmbd_elections.c) + */ + run_election = check_elections(); + + /* + * Read incoming UDP packets. + * (nmbd_packets.c) + */ + if(listen_for_packets(run_election)) + return; + + /* + * Process all incoming packets + * read above. This calls the success and + * failure functions registered when response + * packets arrrive, and also deals with request + * packets from other sources. + * (nmbd_packets.c) + */ + run_packet_queue(); + + /* + * Run any elections - initiate becoming + * a local master browser if we have won. + * (nmbd_elections.c) + */ + run_elections(t); + + /* + * Send out any broadcast announcements + * of our server names. This also announces + * the workgroup name if we are a local + * master browser. + * (nmbd_sendannounce.c) + */ + announce_my_server_names(t); + + /* + * If we are a local master browser, periodically + * announce ourselves to the domain master browser. + * This also deals with syncronising the domain master + * browser server lists with ourselves as a local + * master browser. + * (nmbd_sendannounce.c) + */ + announce_myself_to_domain_master_browser(t); + + /* + * Fullfill any remote announce requests. + * (nmbd_sendannounce.c) + */ + announce_remote(t); + + /* + * Fullfill any remote browse sync announce requests. + * (nmbd_sendannounce.c) + */ + browse_sync_remote(t); + + /* + * Scan the broadcast subnets, and WINS client + * namelists and refresh any that need refreshing. + * (nmbd_mynames.c) + */ + refresh_my_names(t); + + /* + * Scan the subnet namelists and server lists and + * expire thos that have timed out. + * (nmbd.c) + */ + expire_names_and_servers(t); + + /* + * Write out a snapshot of our current browse list into + * the browse.dat file. This is used by smbd to service + * incoming NetServerEnum calls - used to synchronise + * browse lists over subnets. + * (nmbd_serverlistdb.c) + */ + write_browse_list(t, False); + + /* + * If we are a domain master browser, we have a list of + * local master browsers we should synchronise browse + * lists with (these are added by an incoming local + * master browser announcement packet). Expire any of + * these that are no longer current, and pull the server + * lists from each of these known local master browsers. + * (nmbd_browsesync.c) + */ + dmb_expire_and_sync_browser_lists(t); + + /* + * Check that there is a local master browser for our + * workgroup for all our broadcast subnets. If one + * is not found, start an election (which we ourselves + * may or may not participate in, depending on the + * setting of the 'local master' parameter. + * (nmbd_elections.c) + */ + check_master_browser_exists(t); + + /* + * If we are configured as a logon server, attempt to + * register the special NetBIOS names to become such + * (WORKGROUP<1c> name) on all broadcast subnets and + * with the WINS server (if used). If we are configured + * to become a domain master browser, attempt to register + * the special NetBIOS name (WORKGROUP<1b> name) to + * become such. + * (nmbd_become_dmb.c) + */ + add_domain_names(t); + + /* + * If we are a WINS server, do any timer dependent + * processing required. + * (nmbd_winsserver.c) + */ + initiate_wins_processing(t); + + /* + * Go through the repsonse record queue and time out or re-transmit + * and expired entries. + * (nmbd_packets.c) + */ + retransmit_or_expire_response_records(t); + } +} /* process */ -/**************************************************************************** - open the socket communication -****************************************************************************/ +/**************************************************************************** ** + open the socket communication + **************************************************************************** */ static BOOL open_sockets(BOOL isdaemon, int port) { /* The sockets opened here will be used to receive broadcast @@ -340,29 +395,29 @@ static BOOL open_sockets(BOOL isdaemon, int port) now deprecated. */ - if (isdaemon) + if ( isdaemon ) ClientNMB = open_socket_in(SOCK_DGRAM, port,0,0); else ClientNMB = 0; ClientDGRAM = open_socket_in(SOCK_DGRAM,DGRAM_PORT,3,0); - if (ClientNMB == -1) - return(False); + if ( ClientNMB == -1 ) + return( False ); - signal(SIGPIPE, SIGNAL_CAST sig_pipe); + signal( SIGPIPE, SIGNAL_CAST sig_pipe ); - set_socket_options(ClientNMB,"SO_BROADCAST"); - set_socket_options(ClientDGRAM,"SO_BROADCAST"); + set_socket_options( ClientNMB, "SO_BROADCAST" ); + set_socket_options( ClientDGRAM, "SO_BROADCAST" ); - DEBUG(3,("open_sockets: Broadcast sockets opened.\n")); - return True; -} + DEBUG( 3, ( "open_sockets: Broadcast sockets opened.\n" ) ); + return( True ); +} /* open_sockets */ -/**************************************************************************** - initialise connect, service and file structs -****************************************************************************/ +/**************************************************************************** ** + initialise connect, service and file structs + **************************************************************************** */ static BOOL init_structs() { extern fstring local_machine; @@ -372,102 +427,108 @@ static BOOL init_structs() int nodup; pstring nbname; - if (! *myname) { - fstrcpy(myname,myhostname); - p = strchr(myname,'.'); - if (p) *p = 0; + if (! *myname) + { + fstrcpy( myname, myhostname ); + p = strchr( myname, '.' ); + if (p) + *p = 0; } - strupper(myname); + strupper( myname ); /* Add any NETBIOS name aliases. Ensure that the first entry - is equal to myname. */ + is equal to myname. + */ /* Work out the max number of netbios aliases that we have */ - ptr=lp_netbios_aliases(); - for (namecount=0; next_token(&ptr,nbname,NULL); namecount++) + ptr = lp_netbios_aliases(); + for( namecount=0; next_token(&ptr,nbname,NULL); namecount++ ) ; - if (*myname) - namecount++; + if ( *myname ) + namecount++; /* Allocate space for the netbios aliases */ - if((my_netbios_names=(char **)malloc(sizeof(char *)*(namecount+1))) == NULL) + my_netbios_names = (char **)malloc( sizeof(char *) * (namecount+1) ); + if( NULL == my_netbios_names ) { - DEBUG(0,("init_structs: malloc fail.\n")); - return False; + DEBUG( 0, ( "init_structs: malloc fail.\n" ) ); + return( False ); } /* Use the myname string first */ namecount=0; - if (*myname) + if ( *myname ) my_netbios_names[namecount++] = myname; - ptr=lp_netbios_aliases(); - while (next_token(&ptr,nbname,NULL)) { - strupper(nbname); + ptr = lp_netbios_aliases(); + while ( next_token( &ptr, nbname, NULL ) ) + { + strupper( nbname ); /* Look for duplicates */ nodup=1; - for(n=0; n 1 && (*argv[1] != '-')) { + while (argc > 1 && (*argv[1] != '-')) + { argv++; argc--; } - fault_setup(fault_continue); + fault_setup( fault_continue ); - signal(SIGHUP ,SIGNAL_CAST sig_hup); - signal(SIGTERM,SIGNAL_CAST sig_term); + signal( SIGHUP, SIGNAL_CAST sig_hup ); + signal( SIGTERM, SIGNAL_CAST sig_term ); - while ((opt = getopt(argc, argv, "as:T:I:C:bAi:B:N:Rn:l:d:Dp:hSH:G:f:")) != EOF) + while((opt = getopt(argc, argv, "as:T:I:C:bAi:B:N:Rn:l:d:Dp:hSH:G:f:")) != EOF) { switch (opt) - { + { case 'f': strncpy(pidFile, optarg, sizeof(pidFile)); break; - case 's': - pstrcpy(servicesf,optarg); - break; - case 'N': - case 'B': - case 'I': - case 'C': - case 'G': - DEBUG(0,("Obsolete option '%c' used\n",opt)); - break; - case 'H': - pstrcpy(host_file,optarg); - break; - case 'n': - pstrcpy(myname,optarg); - strupper(myname); - break; - case 'l': - sprintf(debugf,"%s.nmb",optarg); - break; - case 'i': - pstrcpy(scope,optarg); - strupper(scope); - break; - case 'a': - { - extern BOOL append_log; - append_log = !append_log; - } - break; - case 'D': - is_daemon = True; - break; - case 'd': - DEBUGLEVEL = atoi(optarg); - break; - case 'p': - global_nmb_port = atoi(optarg); - break; - case 'h': - usage(argv[0]); - exit(0); - break; - default: - if (!is_a_socket(0)) { - usage(argv[0]); - } - break; - } + case 's': + pstrcpy(servicesf,optarg); + break; + case 'N': + case 'B': + case 'I': + case 'C': + case 'G': + DEBUG(0,("Obsolete option '%c' used\n",opt)); + break; + case 'H': + pstrcpy(host_file,optarg); + break; + case 'n': + pstrcpy(myname,optarg); + strupper(myname); + break; + case 'l': + sprintf(debugf,"%s.nmb",optarg); + break; + case 'i': + pstrcpy(scope,optarg); + strupper(scope); + break; + case 'a': + { + extern BOOL append_log; + append_log = !append_log; + } + break; + case 'D': + is_daemon = True; + break; + case 'd': + DEBUGLEVEL = atoi(optarg); + break; + case 'p': + global_nmb_port = atoi(optarg); + break; + case 'h': + usage(argv[0]); + exit(0); + break; + default: + if (!is_a_socket(0)) + { + usage(argv[0]); + } + break; + } } DEBUG(1,("%s netbios nameserver version %s started\n",timestring(),VERSION)); DEBUG(1,("Copyright Andrew Tridgell 1994-1997\n")); - if(!get_myname(myhostname,NULL)) + if( !get_myname( myhostname, NULL) ) { DEBUG(0,("Unable to get my hostname - exiting.\n")); return -1; } -#ifndef SYNC_DNS - start_async_dns(); -#endif - - if (!reload_services(False)) - return(-1); + if ( !reload_services(False) ) + return(-1); codepage_initialise(lp_client_code_page()); if(!init_structs()) return -1; - reload_services(True); + reload_services( True ); - pstrcpy(myworkgroup, lp_workgroup()); + fstrcpy( myworkgroup, lp_workgroup() ); - if (strequal(myworkgroup,"*")) { + if (strequal(myworkgroup,"*")) + { DEBUG(0,("ERROR: a workgroup name of * is no longer supported\n")); exit(1); } set_samba_nb_type(); - if (!is_daemon && !is_a_socket(0)) { + if (!is_daemon && !is_a_socket(0)) + { DEBUG(0,("standard input is not a socket, assuming -D option\n")); is_daemon = True; } - if (is_daemon) { + if (is_daemon) + { DEBUG(2,("%s becoming a daemon\n",timestring())); become_daemon(); } - if (!directory_exist(lp_lockdir(), NULL)) { - mkdir(lp_lockdir(), 0755); + if (!directory_exist(lp_lockdir(), NULL)) + { + mkdir(lp_lockdir(), 0755); } if (*pidFile) - { - int fd; - char buf[20]; + { + int fd; + char buf[20]; - if ((fd = open(pidFile, #ifdef O_NONBLOCK - O_NONBLOCK | + fd = open( pidFile, O_NONBLOCK | O_CREAT | O_WRONLY | O_TRUNC, 0644 ); +#else + fd = open( pidFile, O_CREAT | O_WRONLY | O_TRUNC, 0644 ); #endif - O_CREAT | O_WRONLY | O_TRUNC, 0644)) < 0) - { - DEBUG(0,("ERROR: can't open %s: %s\n", pidFile, strerror(errno))); - exit(1); - } - if (fcntl_lock(fd,F_SETLK,0,1,F_WRLCK)==False) - { - DEBUG(0,("ERROR: nmbd is already running\n")); - exit(1); - } - sprintf(buf, "%u\n", (unsigned int) getpid()); - if (write(fd, buf, strlen(buf)) < 0) - { - DEBUG(0,("ERROR: can't write to %s: %s\n", pidFile, strerror(errno))); - exit(1); - } - /* Leave pid file open & locked for the duration... */ + if ( fd < 0 ) + { + DEBUG(0,("ERROR: can't open %s: %s\n", pidFile, strerror(errno))); + exit(1); } + if (fcntl_lock(fd,F_SETLK,0,1,F_WRLCK)==False) + { + DEBUG(0,("ERROR: nmbd is already running\n")); + exit(1); + } + sprintf(buf, "%u\n", (unsigned int) getpid()); + if (write(fd, buf, strlen(buf)) < 0) + { + DEBUG(0,("ERROR: can't write to %s: %s\n", pidFile, strerror(errno))); + exit(1); + } + /* Leave pid file open & locked for the duration... */ + } - DEBUG(3,("Opening sockets %d\n", global_nmb_port)); + DEBUG( 3, ( "Opening sockets %d\n", global_nmb_port ) ); - if (!open_sockets(is_daemon,global_nmb_port)) return 1; + if ( !open_sockets( is_daemon, global_nmb_port ) ) + return 1; + /* Determine all the IP addresses we have. */ load_interfaces(); - add_my_subnets(myworkgroup); - - add_my_names(); - DEBUG(3,("Checked names\n")); - - load_netbios_names(); - - DEBUG(3,("Loaded names\n")); + /* Create an nmbd subnet record for each of the above. */ + if( False == create_subnets() ) + { + DEBUG(0,("ERROR: Failed when creating subnet lists. Exiting.\n")); + exit(1); + } - if (*host_file) { - load_hosts_file(host_file); + /* Load in any static local names. */ + if ( *host_file ) + { + load_lmhosts_file(host_file); DEBUG(3,("Loaded hosts file\n")); } - write_browse_list(time(NULL)); + /* If we are acting as a WINS server, initialise data structures. */ + if( !initialise_wins() ) + { + DEBUG( 0, ( "nmbd: Failed when initialising WINS server.\n" ) ); + exit(1); + } - DEBUG(3,("Dumped names\n")); + /* + * Register nmbd primary workgroup and nmbd names on all + * the broadcast subnets, and on the WINS server (if specified). + * Also initiate the startup of our primary workgroup (start + * elections if we are setup as being able to be a local + * master browser. + */ + + if( False == register_my_workgroup_and_names() ) + { + DEBUG(0,("ERROR: Failed when creating my my workgroup. Exiting.\n")); + exit(1); + } /* We can only take sigterm signals in the select. */ - BlockSignals(True,SIGTERM); + BlockSignals( True, SIGTERM ); process(); close_sockets(); @@ -663,4 +746,6 @@ static void usage(char *pname) if (dbf) fclose(dbf); return(0); -} +} /* main */ + +/* ========================================================================== */ diff --git a/source3/nmbd/nmbd_become_dmb.c b/source3/nmbd/nmbd_become_dmb.c new file mode 100644 index 0000000000..37fceb9bf1 --- /dev/null +++ b/source3/nmbd/nmbd_become_dmb.c @@ -0,0 +1,471 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NBT netbios routines and daemon - version 2 + Copyright (C) Andrew Tridgell 1994-1997 + Copyright (C) Luke Kenneth Casson Leighton 1994-1997 + Copyright (C) Jeremy Allison 1994-1997 + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "includes.h" + +extern int DEBUGLEVEL; + +extern pstring scope; +extern pstring myname; +extern fstring myworkgroup; +extern char **my_netbios_names; +extern struct in_addr ipzero; +extern struct in_addr allones_ip; + +extern uint16 samba_nb_type; /* Samba's NetBIOS type. */ + +static void become_domain_master_browser_bcast(char *); + +/******************************************************************* + Unbecome a domain master browser - name release success function. + ******************************************************************/ + +static void unbecome_dmb_success(struct subnet_record *subrec, + struct userdata_struct *userdata, + struct nmb_name *released_name, + struct in_addr released_ip) +{ + struct work_record *work = find_workgroup_on_subnet(subrec, released_name->name); + struct server_record *servrec; + + if(!work) + { + DEBUG(0,("unbecome_dmb_success: Cannot find workgroup %s on subnet %s\n", + released_name->name, subrec->subnet_name)); + return; + } + + if((servrec = find_server_in_workgroup( work, myname)) == NULL) + { + DEBUG(0,("unbecome_dmb_success: Error - cannot find server %s \ +in workgroup %s on subnet %s\n", + myname, released_name->name, subrec->subnet_name)); + return; + } + + /* Set the state in the workgroup structure. */ + work->dom_state = DOMAIN_NONE; + + /* Update our server status. */ + servrec->serv.type &= ~SV_TYPE_DOMAIN_MASTER; + + /* Tell the namelist writer to write out a change. */ + subrec->work_changed = True; + + /* Remove any list of local master browsers we are syncing with. */ + remove_workgroup_lmb_browsers(released_name->name); + + /* Delete the known domain master browser name from the workgroup + struct. */ + bzero((char *)&work->dmb_name, sizeof(work->dmb_name)); + putip((char *)&work->dmb_addr, &ipzero); + + DEBUG(0,("\n ***** Samba server %s has stopped being a domain master browser \ +for workgroup %s on subnet %s *****\n\n", myname, work->work_group, subrec->subnet_name)); + +} + +/******************************************************************* + Unbecome a domain master browser - name release fail function. + ******************************************************************/ + +static void unbecome_dmb_fail(struct subnet_record *subrec, + struct response_record *rrec, + struct nmb_name *released_name) +{ + DEBUG(0,("unbecome_dmb_fail: Failed to unbecome domain master browser for \ +workgroup %s on subnet %s.\n", released_name->name, subrec->subnet_name)); +} + +/******************************************************************* + Unbecome a domain master browser. + ******************************************************************/ + +void unbecome_domain_master(char *workgroup_name) +{ + struct subnet_record *subrec; + + for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec)) + { + struct work_record *work = find_workgroup_on_subnet(subrec, workgroup_name); + + if(work && (work->dom_state == DOMAIN_MST)) + { + struct name_record *namerec; + struct nmb_name nmbname; + make_nmb_name(&nmbname,workgroup_name,0x1b,scope); + + /* We can only do this if we are a domain master already. */ + DEBUG(2,("unbecome_domain_master: attempting to stop being a domain \ +master browser for workgroup %s on subnet %s\n", + work->work_group, subrec->subnet_name)); + + /* Find the WORKGROUP<1b> name on the subnet namelist. */ + if((namerec = find_name_on_subnet(subrec, &nmbname, FIND_SELF_NAME))==NULL) + { + DEBUG(0,("unbecome_domain_master: Cannot find name %s on subnet %s.\n", + namestr(&nmbname), subrec->subnet_name)); + continue; + } + release_name(subrec, namerec, + unbecome_dmb_success, + unbecome_dmb_fail, + NULL); + } + } +} + +/**************************************************************************** + 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) +{ + struct work_record *work = find_workgroup_on_subnet(subrec, fail_name->name); + struct server_record *servrec; + + if(!work) + { + DEBUG(0,("become_domain_master_fail: Error - cannot find \ +workgroup %s on subnet %s\n", fail_name->name, subrec->subnet_name)); + return; + } + + /* Set the state back to DOMAIN_NONE. */ + work->dom_state = DOMAIN_NONE; + + if((servrec = find_server_in_workgroup( work, myname)) == NULL) + { + DEBUG(0,("become_domain_master_fail: Error - cannot find server %s \ +in workgroup %s on subnet %s\n", + myname, 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, 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 nb_flags, + int ttl, struct in_addr registered_ip) +{ + struct work_record *work = find_workgroup_on_subnet( subrec, registered_name->name); + struct server_record *servrec; + + if(!work) + { + DEBUG(0,("become_domain_master_stage2: Error - cannot find \ +workgroup %s on subnet %s\n", registered_name->name, subrec->subnet_name)); + return; + } + + if((servrec = find_server_in_workgroup( work, myname)) == NULL) + { + DEBUG(0,("become_domain_master_stage2: Error - cannot find server %s \ +in workgroup %s on subnet %s\n", + myname, registered_name->name, 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; + + DEBUG(0,("\n ***** Samba server %s is now a domain master browser \ +for workgroup %s on subnet %s *****\n\n", myname, work->work_group, subrec->subnet_name)); + + if(subrec == unicast_subnet) + { + struct nmb_name nmbname; + struct in_addr my_first_ip; + + /* 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, myname, 0x20, scope); + + work->dmb_name = nmbname; + /* Pick the first interface ip address as the domain master browser ip. */ + my_first_ip = *iface_n_ip(0); + + 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); + } +} + +/**************************************************************************** + Start the name registration process when becoming a Domain Master Browser + on a subnet. + ****************************************************************************/ + +static void become_domain_master_stage1(struct subnet_record *subrec, 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) +{ + /* 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. */ + + if(ismyip(ip) || ip_equal(allones_ip, ip) || ip_equal(ipzero, ip)) + { + DEBUG(3,("become_domain_master_query_success: Our address (%s) returned \ +in query for name %s (domain master browser name) on subnet %s. \ +Continuing with domain master code.\n", + inet_ntoa(ip), namestr(nmbname), subrec->subnet_name)); + + become_domain_master_stage1(subrec, nmbname->name); + } + else + { + DEBUG(0,("become_domain_master_query_success: There is already a domain \ +master browser at IP %s for workgroup %s registered on subnet %s.\n", + inet_ntoa(ip), nmbname->name, 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) +{ + /* 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, namestr(question_name))); + return; + } + + /* Otherwise - not having the name allows us to register it. */ + become_domain_master_stage1(subrec, question_name->name); +} + +/**************************************************************************** + Attempt to become a domain master browser on all broadcast subnets. + ****************************************************************************/ + +static void become_domain_master_browser_bcast(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,scope); + + /* + * 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) + { + DEBUG(0,("become_domain_master_browser_bcast: At time %s attempting to become domain \ +master browser on workgroup %s on subnet %s\n", timestring(), + 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, nmbname.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(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,scope); + + /* + * 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) + { + DEBUG(0,("%s become_domain_master_browser_wins: attempting to become domain \ +master browser on workgroup %s, subnet %s.\n", + timestring(), 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 at IP %s \ +for domain master browser name %s on workgroup %s\n", + inet_ntoa(unicast_subnet->myip), namestr(&nmbname), workgroup_name)); + + query_name(unicast_subnet, nmbname.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 (lp_domain_logons()) + 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(myworkgroup); + } + else + become_domain_master_browser_bcast(myworkgroup); + } +} diff --git a/source3/nmbd/nmbd_become_lmb.c b/source3/nmbd/nmbd_become_lmb.c new file mode 100644 index 0000000000..7f54471a24 --- /dev/null +++ b/source3/nmbd/nmbd_become_lmb.c @@ -0,0 +1,534 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NBT netbios routines and daemon - version 2 + Copyright (C) Andrew Tridgell 1994-1997 + Copyright (C) Luke Kenneth Casson Leighton 1994-1997 + Copyright (C) Jeremy Allison 1994-1997 + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "includes.h" + +extern int DEBUGLEVEL; +extern pstring scope; +extern pstring myname; + +extern uint16 samba_nb_type; /* Samba's NetBIOS name type. */ + +/******************************************************************* + Utility function to add a name to the unicast subnet, or add in + our IP address if it already exists. +******************************************************************/ + +static void insert_permanent_name_into_unicast( struct subnet_record *subrec, + struct nmb_name *nmbname, uint16 nb_type ) +{ + struct name_record *namerec; + + if((namerec = find_name_on_subnet(unicast_subnet, nmbname, FIND_SELF_NAME)) == NULL) + { + /* The name needs to be created on the unicast subnet. */ + add_name_to_subnet( unicast_subnet, nmbname->name, nmbname->name_type, + nb_type, PERMANENT_TTL, PERMANENT_NAME, 1, &subrec->myip); + } + else + { + /* The name already exists on the unicast subnet. Add our local + IP for the given broadcast subnet to the name. */ + add_ip_to_name_record( namerec, subrec->myip); + } +} + +/******************************************************************* + Utility function to remove a name from the unicast subnet. +******************************************************************/ + +static void remove_permanent_name_from_unicast( struct subnet_record *subrec, + struct nmb_name *nmbname ) +{ + struct name_record *namerec; + + if((namerec = find_name_on_subnet(unicast_subnet, nmbname, FIND_SELF_NAME)) != NULL) + { + /* Remove this broadcast subnet IP address from the name. */ + remove_ip_from_name_record( namerec, subrec->myip); + if(namerec->num_ips == 0) + remove_name_from_namelist( unicast_subnet, namerec); + } +} + +/******************************************************************* + Utility function always called to set our workgroup and server + state back to potential browser, or none. +******************************************************************/ + +static void reset_workgroup_state( struct subnet_record *subrec, char *workgroup_name ) +{ + struct work_record *work; + struct server_record *servrec; + struct nmb_name nmbname; + + if((work = find_workgroup_on_subnet( subrec, workgroup_name)) == NULL) + { + DEBUG(0,("reset_workgroup_state: Error - cannot find workgroup %s on \ +subnet %s.\n", workgroup_name, subrec->subnet_name )); + return; + } + + if((servrec = find_server_in_workgroup( work, myname)) == NULL) + { + DEBUG(0,("reset_workgroup_state: Error - cannot find server %s \ +in workgroup %s on subnet %s\n", + myname, work->work_group, subrec->subnet_name)); + work->mst_state = lp_local_master() ? MST_POTENTIAL : MST_NONE; + return; + } + + /* Update our server status - remove any master flag and replace + it with the potential browser flag. */ + servrec->serv.type &= ~SV_TYPE_MASTER_BROWSER; + servrec->serv.type |= (lp_local_master() ? SV_TYPE_POTENTIAL_BROWSER : 0); + + /* Tell the namelist writer to write out a change. */ + subrec->work_changed = True; + + /* Reset our election flags. */ + work->ElectionCriterion &= ~0x4; + + work->mst_state = lp_local_master() ? MST_POTENTIAL : MST_NONE; + + /* Forget who the local master browser was for + this workgroup. */ + + *work->local_master_browser_name = '\0'; + + /* + * Ensure the IP address of this subnet is not registered as one + * of the IP addresses of the WORKGROUP<1d> name on the unicast + * subnet. This undoes what we did below when we became a local + * master browser. + */ + + make_nmb_name(&nmbname, work->work_group, 0x1d, scope); + + remove_permanent_name_from_unicast( subrec, &nmbname); + +} + +/******************************************************************* + Unbecome the local master browser name release success function. +******************************************************************/ + +void unbecome_local_master_success(struct subnet_record *subrec, + struct userdata_struct *userdata, + struct nmb_name *released_name, + struct in_addr released_ip) +{ + DEBUG(3,("unbecome_local_master_success: released name %s.\n", + namestr(released_name))); + + /* Now reset the workgroup and server state. */ + reset_workgroup_state( subrec, released_name->name ); + + DEBUG(0,("\n***** Samba name server %s has stopped being a local master browser for workgroup %s \ +on subnet %s *****\n\n", myname, released_name->name, subrec->subnet_name)); + +} + +/******************************************************************* + Unbecome the local master browser name release fail function. +******************************************************************/ + +void unbecome_local_master_fail(struct subnet_record *subrec, struct response_record *rrec, + struct nmb_name *fail_name) +{ + struct name_record *namerec; + + DEBUG(0,("unbecome_local_master_fail: failed to release name %s. \ +Removing from namelist anyway.\n", namestr(fail_name))); + + /* Do it anyway. */ + namerec = find_name_on_subnet(subrec, fail_name, FIND_SELF_NAME); + if(namerec) + remove_name_from_namelist(subrec, namerec); + + /* Now reset the workgroup and server state. */ + reset_workgroup_state( subrec, fail_name->name ); + + DEBUG(0,("\n***** Samba name server %s has stopped being a local master browser for workgroup %s \ +on subnet %s *****\n\n", myname, fail_name->name, subrec->subnet_name)); + +} + +/******************************************************************* + Utility function to remove the WORKGROUP<1d> name called by both + success and fail of releasing the MSBROWSE name. +******************************************************************/ + +void release_1d_name( struct subnet_record *subrec, char *workgroup_name) +{ + struct nmb_name nmbname; + struct name_record *namerec; + + make_nmb_name(&nmbname, workgroup_name, 0x1d, scope); + if((namerec = find_name_on_subnet( subrec, &nmbname, FIND_SELF_NAME))!=NULL) + { + release_name(subrec, namerec, + unbecome_local_master_success, + unbecome_local_master_fail, + NULL); + } +} + +/******************************************************************* + Unbecome the local master browser MSBROWSE name release success function. +******************************************************************/ + +static void release_msbrowse_name_success(struct subnet_record *subrec, + struct userdata_struct *userdata, + struct nmb_name *released_name, + struct in_addr released_ip) +{ + DEBUG(4,("release_msbrowse_name_success: Released name %s on subnet %s\n.", + namestr(released_name), subrec->subnet_name )); + + /* Remove the permanent MSBROWSE name added into the unicast subnet. */ + remove_permanent_name_from_unicast( subrec, released_name); + + release_1d_name( subrec, userdata->data ); +} + +/******************************************************************* + Unbecome the local master browser MSBROWSE name release fail function. +******************************************************************/ + +static void release_msbrowse_name_fail( struct subnet_record *subrec, + struct response_record *rrec, + struct nmb_name *fail_name) +{ + struct userdata_struct *userdata = rrec->userdata; + struct name_record *namerec; + + DEBUG(4,("release_msbrowse_name_fail: Failed to release name %s on subnet %s\n.", + namestr(fail_name), subrec->subnet_name )); + + /* Release the name anyway. */ + namerec = find_name_on_subnet(subrec, fail_name, FIND_SELF_NAME); + if(namerec) + remove_name_from_namelist(subrec, namerec); + + /* Remove the permanent MSBROWSE name added into the unicast subnet. */ + remove_permanent_name_from_unicast( subrec, fail_name); + + release_1d_name( subrec, userdata->data ); +} + +/******************************************************************* + Unbecome the local master browser. +******************************************************************/ + +void unbecome_local_master_browser(struct subnet_record *subrec, struct work_record *work) +{ + struct server_record *servrec; + struct name_record *namerec; + struct nmb_name nmbname; + struct userdata_struct *userdata; + char ud[sizeof(struct userdata_struct) + sizeof(fstring)+1]; + + /* Sanity check. */ + + DEBUG(2,("unbecome_local_master_browser: unbecoming local master for workgroup %s \ +on subnet %s\n",work->work_group, subrec->subnet_name)); + + if((servrec = find_server_in_workgroup( work, myname)) == NULL) + { + DEBUG(0,("unbecome_local_master_browser: Error - cannot find server %s \ +in workgroup %s on subnet %s\n", + myname, work->work_group, subrec->subnet_name)); + work->mst_state = lp_local_master() ? MST_POTENTIAL : MST_NONE; + return; + } + + /* Set the state to unbecoming. */ + work->mst_state = MST_UNBECOMING_MASTER; + + /* Setup the userdata for the MSBROWSE name release. */ + /* Setup the userdata_struct - this is copied so we can use + a stack variable for this. */ + userdata = (struct userdata_struct *)ud; + + userdata->copy_fn = NULL; + userdata->free_fn = NULL; + userdata->userdata_len = strlen(work->work_group)+1; + strcpy(userdata->data, work->work_group); + + /* Deregister any browser names we may have. */ + make_nmb_name(&nmbname, MSBROWSE, 0x1, scope); + if((namerec = find_name_on_subnet( subrec, &nmbname, FIND_SELF_NAME))!=NULL) + { + release_name(subrec, namerec, + release_msbrowse_name_success, + release_msbrowse_name_fail, + userdata); + } +} + +/**************************************************************************** + Success in registering the WORKGROUP<1d> name. + We are now *really* a local master browser. + ****************************************************************************/ + +static void become_local_master_stage2(struct subnet_record *subrec, + struct userdata_struct *userdata, + struct nmb_name *registered_name, + uint16 nb_flags, + int ttl, struct in_addr registered_ip) +{ + int i = 0; + struct server_record *sl; + struct work_record *work = find_workgroup_on_subnet( subrec, registered_name->name); + struct server_record *servrec; + + if(!work) + { + DEBUG(0,("become_local_master_stage2: Error - cannot find \ +workgroup %s on subnet %s\n", registered_name->name, subrec->subnet_name)); + return; + } + + if((servrec = find_server_in_workgroup( work, myname)) == NULL) + { + DEBUG(0,("become_local_master_stage2: Error - cannot find server %s \ +in workgroup %s on subnet %s\n", + myname, registered_name->name, subrec->subnet_name)); + work->mst_state = lp_local_master() ? MST_POTENTIAL : MST_NONE; + return; + } + + DEBUG(3,("become_local_master_stage2: registered as master browser for workgroup %s \ +on subnet %s\n", work->work_group, subrec->subnet_name)); + + work->mst_state = MST_BROWSER; /* registering WORKGROUP(1d) succeeded */ + + /* update our server status */ + servrec->serv.type |= SV_TYPE_MASTER_BROWSER; + servrec->serv.type &= ~SV_TYPE_POTENTIAL_BROWSER; + + /* Tell the namelist writer to write out a change. */ + subrec->work_changed = True; + + /* Add this name to the workgroup as local master browser. */ + StrnCpy(work->local_master_browser_name, myname, + sizeof(work->local_master_browser_name)-1); + + /* Count the number of servers we have on our list. If it's + less than 10 (just a heuristic) request the servers + to announce themselves. + */ + for( sl = work->serverlist; sl != NULL; sl = sl->next) + i++; + + if (i < 10) + { + /* Ask all servers on our local net to announce to us. */ + broadcast_announce_request(subrec, work); + } + + /* + * Now we are a local master on a broadcast subnet, we need to add + * the WORKGROUP<1d> name to the unicast subnet so that we can answer + * unicast requests sent to this name. We can create this name directly on + * the unicast subnet as a WINS server always returns true when registering + * this name, and discards the registration. We use the number of IP + * addresses registered to this name as a reference count, as we + * remove this broadcast subnet IP address from it when we stop becoming a local + * master browser for this broadcast subnet. + */ + + insert_permanent_name_into_unicast( subrec, registered_name, nb_flags); + + /* Reset the announce master browser timer so that we try and tell a domain + master browser as soon as possible that we are a local master browser. */ + reset_announce_timer(); + + DEBUG(0,("\n***** Samba name server %s is now a local master browser for workgroup %s \ +on subnet %s *****\n\n", myname, work->work_group, subrec->subnet_name)); + +} + +/**************************************************************************** + Failed to register the WORKGROUP<1d> name. + ****************************************************************************/ +static void become_local_master_fail2(struct subnet_record *subrec, + struct response_record *rrec, + struct nmb_name *fail_name) +{ + struct work_record *work = find_workgroup_on_subnet( subrec, fail_name->name); + + DEBUG(0,("become_local_master_fail2: failed to register name %s on subnet %s. \ +Failed to become a local master browser.\n", namestr(fail_name), subrec->subnet_name)); + + if(!work) + { + DEBUG(0,("become_local_master_fail2: Error - cannot find \ +workgroup %s on subnet %s\n", fail_name->name, subrec->subnet_name)); + return; + } + + /* Roll back all the way by calling unbecome_local_master_browser(). */ + unbecome_local_master_browser(subrec, work); +} + +/**************************************************************************** + Success in registering the MSBROWSE name. + ****************************************************************************/ + +static void become_local_master_stage1(struct subnet_record *subrec, + struct userdata_struct *userdata, + struct nmb_name *registered_name, + uint16 nb_flags, + int ttl, struct in_addr registered_ip) +{ + char *work_name = userdata->data; + struct work_record *work = find_workgroup_on_subnet( subrec, work_name); + + if(!work) + { + DEBUG(0,("become_local_master_stage1: Error - cannot find \ +workgroup %s on subnet %s\n", work_name, subrec->subnet_name)); + return; + } + + DEBUG(3,("become_local_master_stage1: go to stage 2: register the %s<1d> name.\n", + work->work_group)); + + work->mst_state = MST_MSB; /* Registering MSBROWSE was successful. */ + + /* + * We registered the MSBROWSE name on a broadcast subnet, now need to add + * the MSBROWSE name to the unicast subnet so that we can answer + * unicast requests sent to this name. We create this name directly on + * the unicast subnet. + */ + + insert_permanent_name_into_unicast( subrec, registered_name, nb_flags); + + /* Attempt to register the WORKGROUP<1d> name. */ + register_name(subrec, work->work_group,0x1d,samba_nb_type, + become_local_master_stage2, + become_local_master_fail2, + NULL); +} + +/**************************************************************************** + Failed to register the MSBROWSE name. + ****************************************************************************/ + +static void become_local_master_fail1(struct subnet_record *subrec, + struct response_record *rrec, + struct nmb_name *fail_name) +{ + char *work_name = rrec->userdata->data; + struct work_record *work = find_workgroup_on_subnet(subrec, work_name); + struct server_record *servrec; + + if(!work) + { + DEBUG(0,("become_local_master_fail1: Error - cannot find \ +workgroup %s on subnet %s\n", work_name, subrec->subnet_name)); + return; + } + + if((servrec = find_server_in_workgroup(work, myname)) == NULL) + { + DEBUG(0,("become_local_master_fail1: Error - cannot find server %s \ +in workgroup %s on subnet %s\n", + myname, work->work_group, subrec->subnet_name)); + return; + } + + reset_workgroup_state( subrec, work->work_group ); + + DEBUG(0,("become_local_master_fail1: Failed to become a local master browser for \ +workgroup %s on subnet %s. Couldn't register name %s.\n", + work->work_group, subrec->subnet_name, namestr(fail_name))); +} + +/****************************************************************** + Become the local master browser on a subnet. + This gets called if we win an election on this subnet. + + Stage 1: mst_state was MST_POTENTIAL - go to MST_BACK register ^1^2__MSBROWSE__^2^1. + Stage 2: mst_state was MST_BACKUP - go to MST_MSB and register WORKGROUP<1d>. + Stage 3: mst_state was MST_MSB - go to MST_BROWSER. +******************************************************************/ + +void become_local_master_browser(struct subnet_record *subrec, struct work_record *work) +{ + struct server_record *servrec; + struct userdata_struct *userdata; + char ud[sizeof(struct userdata_struct) + sizeof(fstring)+1]; + + /* Sanity check. */ + if (!lp_local_master()) + { + DEBUG(0,("become_local_master_browser: Samba not configured as a local master browser.\n")); + return; + } + + if(!AM_POTENTIAL_MASTER_BROWSER(work)) + { + DEBUG(2,("become_local_master_browser: Awaiting potential browser state. Current state is %d\n", + work->mst_state )); + return; + } + + if((servrec = find_server_in_workgroup( work, myname)) == NULL) + { + DEBUG(0,("become_local_master_browser: Error - cannot find server %s \ +in workgroup %s on subnet %s\n", + myname, work->work_group, subrec->subnet_name)); + return; + } + + DEBUG(2,("become_local_master_browser: Starting to become a master browser for workgroup \ +%s on subnet %s\n", work->work_group, subrec->subnet_name)); + + DEBUG(3,("become_local_master_browser: first stage - attempt to register ^1^2__MSBROWSE__^2^1\n")); + work->mst_state = MST_BACKUP; /* an election win was successful */ + + work->ElectionCriterion |= 0x5; + + /* Tell the namelist writer to write out a change. */ + subrec->work_changed = True; + + /* Setup the userdata_struct - this is copied so we can use + a stack variable for this. */ + userdata = (struct userdata_struct *)ud; + + userdata->copy_fn = NULL; + userdata->free_fn = NULL; + userdata->userdata_len = strlen(work->work_group)+1; + strcpy(userdata->data, work->work_group); + + /* Register the special browser group name. */ + register_name(subrec, MSBROWSE, 0x01, samba_nb_type|NB_GROUP, + become_local_master_stage1, + become_local_master_fail1, + userdata); +} diff --git a/source3/nmbd/nmbd_browserdb.c b/source3/nmbd/nmbd_browserdb.c new file mode 100644 index 0000000000..b2db744370 --- /dev/null +++ b/source3/nmbd/nmbd_browserdb.c @@ -0,0 +1,184 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NBT netbios routines and daemon - version 2 + Copyright (C) Andrew Tridgell 1994-1997 + Copyright (C) Luke Kenneth Casson Leighton 1994-1997 + Copyright (C) Jeremy Allison 1994-1997 + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "includes.h" +#include "smb.h" + +extern int DEBUGLEVEL; + +/* This is our local master browser list database. */ +struct browse_cache_record *lmb_browserlist = NULL; + +/*************************************************************************** +Add a browser into the lmb list. +**************************************************************************/ + +static void add_to_lmb_browse_cache(struct browse_cache_record *browc) +{ + struct browse_cache_record *browc2; + + if (lmb_browserlist == NULL) + { + lmb_browserlist = browc; + browc->prev = NULL; + browc->next = NULL; + return; + } + + for (browc2 = lmb_browserlist; browc2->next; browc2 = browc2->next) + ; + + browc2->next = browc; + browc->next = NULL; + browc->prev = browc2; +} + +/******************************************************************* +Remove a lmb browser entry. +******************************************************************/ + +void remove_lmb_browser_entry(struct browse_cache_record *browc) +{ + if (browc->prev) + browc->prev->next = browc->next; + if (browc->next) + browc->next->prev = browc->prev; + + if (lmb_browserlist == browc) + lmb_browserlist = browc->next; + + free((char *)browc); +} + +/**************************************************************************** +Update a browser death time. +****************************************************************************/ + +void update_browser_death_time(struct browse_cache_record *browc) +{ + /* Allow the new lmb to miss an announce period before we remove it. */ + browc->death_time = time(NULL) + (CHECK_TIME_MST_ANNOUNCE + 2)*60; +} + +/**************************************************************************** +Create a browser entry. +****************************************************************************/ + +struct browse_cache_record *create_browser_in_lmb_cache(char *work_name, char *browser_name, + struct in_addr ip) +{ + struct browse_cache_record *browc; + time_t now = time(NULL); + + browc = (struct browse_cache_record *)malloc(sizeof(*browc)); + + if (browc == NULL) + { + DEBUG(0,("create_browser_in_lmb_cache: malloc fail !\n")); + return(NULL); + } + + bzero((char *)browc,sizeof(*browc)); + + /* For a new lmb entry we want to sync with it after one minute. This + will allow it time to send out a local announce and build its + browse list. */ + + browc->sync_time = now + 60; + + /* Allow the new lmb to miss an announce period before we remove it. */ + browc->death_time = now + (CHECK_TIME_MST_ANNOUNCE + 2)*60; + + StrnCpy(browc->lmb_name, browser_name,sizeof(browc->lmb_name)-1); + StrnCpy(browc->work_group,work_name,sizeof(browc->work_group)-1); + strupper(browc->lmb_name); + strupper(browc->work_group); + + browc->ip = ip; + + add_to_lmb_browse_cache(browc); + + DEBUG(3,("create_browser_in_lmb_cache: Added lmb cache entry for workgroup %s name %s IP %s ttl %d\n", + browc->work_group, browc->lmb_name, inet_ntoa(ip), browc->death_time)); + + return(browc); +} + +/**************************************************************************** +Find a browser entry. +****************************************************************************/ + +struct browse_cache_record *find_browser_in_lmb_cache( char *browser_name ) +{ + struct browse_cache_record *browc = NULL; + + for( browc = lmb_browserlist; browc; browc = browc->next) + if(strequal( browser_name, browc->lmb_name)) + break; + + return browc; +} + +/******************************************************************* + Expire timed out browsers in the browserlist. +******************************************************************/ + +void expire_lmb_browsers(time_t t) +{ + struct browse_cache_record *browc; + struct browse_cache_record *nextbrowc; + + for (browc = lmb_browserlist; browc; browc = nextbrowc) + { + nextbrowc = browc->next; + + if (browc->death_time < t) + { + DEBUG(3,("expire_lmb_browsers: Removing timed out lmb entry %s\n",browc->lmb_name)); + remove_lmb_browser_entry(browc); + } + } +} + +/******************************************************************* + Remove browsers from a named workgroup in the browserlist. +******************************************************************/ + +void remove_workgroup_lmb_browsers(char *work_group) +{ + struct browse_cache_record *browc; + struct browse_cache_record *nextbrowc; + + for (browc = lmb_browserlist; browc; browc = nextbrowc) + { + nextbrowc = browc->next; + + if (strequal(work_group, browc->work_group)) + { + DEBUG(3,("remove_workgroup_browsers: Removing lmb entry %s\n",browc->lmb_name)); + remove_lmb_browser_entry(browc); + } + } +} + diff --git a/source3/nmbd/nmbd_browsesync.c b/source3/nmbd/nmbd_browsesync.c new file mode 100644 index 0000000000..b899e2e8bc --- /dev/null +++ b/source3/nmbd/nmbd_browsesync.c @@ -0,0 +1,458 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NBT netbios routines and daemon - version 2 + Copyright (C) Andrew Tridgell 1994-1997 + Copyright (C) Luke Kenneth Casson Leighton 1994-1997 + Copyright (C) Jeremy Allison 1994-1997 + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "includes.h" +#include "smb.h" + +extern int DEBUGLEVEL; +extern pstring scope; +extern struct in_addr ipzero; +extern pstring myname; + +/* This is our local master browser list database. */ +extern struct browse_cache_record *lmb_browserlist; + +static struct work_record *call_work; +static struct subnet_record *call_subrec; + +/******************************************************************* + This is the NetServerEnum callback. + ******************************************************************/ + +static void callback(char *sname, uint32 stype, char *comment) +{ + struct work_record *work; + + stype &= ~SV_TYPE_LOCAL_LIST_ONLY; + + if (stype & SV_TYPE_DOMAIN_ENUM) + { + /* See if we can find the workgroup on this subnet. */ + if(( work = find_workgroup_on_subnet( call_subrec, sname )) != NULL) + { + /* We already know about this workgroup - update the ttl. */ + update_workgroup_ttl( work, lp_max_ttl() ); + } + else + { + /* Create the workgroup on the subnet. */ + create_workgroup_on_subnet( call_subrec, sname, lp_max_ttl() ); + } + } + else + { + /* Server entry. */ + struct server_record *servrec; + + work = call_work; + + if(( servrec = find_server_in_workgroup( work, sname )) != NULL) + { + /* Check that this is not a locally known server - if so ignore the + entry. */ + if(!(servrec->serv.type & SV_TYPE_LOCAL_LIST_ONLY)) + { + /* We already know about this server - update the ttl. */ + update_server_ttl(servrec, lp_max_ttl() ); + /* Update the type. */ + servrec->serv.type = stype; + } + } + else + { + /* Create the server in the workgroup. */ + create_server_on_workgroup(work, sname,stype,lp_max_ttl(),comment); + } + } +} + +/******************************************************************* + Synchronise browse lists with another browse server. + Log in on the remote server's SMB port to their IPC$ service, + do a NetServerEnum and update our server and workgroup databases. +******************************************************************/ + +static void sync_browse_lists(struct subnet_record *subrec, struct work_record *work, + char *name, int nm_type, struct in_addr ip, BOOL local) +{ + extern fstring local_machine; + static struct cli_state cli; + uint32 local_type = local ? SV_TYPE_LOCAL_LIST_ONLY : 0; + + DEBUG(2,("%s: sync_browse_lists: Sync browse lists with server %s<%02x> at IP %s for workgroup %s\n", + timestring(), name, nm_type, inet_ntoa(ip), work->work_group )); + + /* Check we're not trying to sync with ourselves. This can happen if we are + a domain *and* a local master browser. */ + if(ismyip(ip)) + { + DEBUG(2,("sync_browse_lists: We are both a domain and a local master browser for workgroup %s. \ +Do not sync with ourselves.\n", work->work_group )); + return; + } + + if (!cli_initialise(&cli) || !cli_connect(&cli, name, &ip)) + { + DEBUG(0,("sync_browse_lists: Failed to start browse sync with %s\n", name)); + return; + } + + if (!cli_session_request(&cli, name, nm_type, local_machine)) + { + DEBUG(0,("sync_browse_lists: %s rejected the browse sync session\n",name)); + cli_shutdown(&cli); + return; + } + + if (!cli_negprot(&cli)) + { + DEBUG(0,("sync_browse_lists: %s rejected the negprot\n",name)); + cli_shutdown(&cli); + return; + } + + if (!cli_session_setup(&cli, "", "", 1, "", 0, work->work_group)) + { + DEBUG(0,("sync_browse_lists: %s rejected the browse sync sessionsetup\n", + name)); + cli_shutdown(&cli); + return; + } + + if (!cli_send_tconX(&cli, "IPC$", "IPC", "", 1)) + { + DEBUG(0,("sync_browse_lists: %s refused browse sync IPC$ connect\n", name)); + cli_shutdown(&cli); + return; + } + + call_work = work; + call_subrec = subrec; + + /* Fetch a workgroup list. */ + cli_NetServerEnum(&cli, work->work_group, + local_type|SV_TYPE_DOMAIN_ENUM, + callback); + + /* Now fetch a server list. */ + cli_NetServerEnum(&cli, work->work_group, + local?SV_TYPE_LOCAL_LIST_ONLY:SV_TYPE_ALL, + callback); + + cli_shutdown(&cli); +} + +/**************************************************************************** +As a domain master browser, do a sync with a local master browser. +**************************************************************************/ + +static void sync_with_lmb(struct browse_cache_record *browc) +{ + struct work_record *work; + + if (!(work = find_workgroup_on_subnet(unicast_subnet, browc->work_group))) { + DEBUG(0, ("sync_with_lmb: failed to get a \ +workgroup for a local master browser cache entry workgroup %s, server %s\n", + browc->work_group, browc->lmb_name)); + return; + } + + /* We should only be doing this if we are a domain master browser for + the given workgroup. Ensure this is so. */ + + if(!AM_DOMAIN_MASTER_BROWSER(work)) + { + DEBUG(0,("sync_with_lmb: We are trying to sync with a local master browser %s \ +for workgroup %s and we are not a domain master browser on this workgroup. Error !\n", + browc->lmb_name, browc->work_group)); + return; + } + + DEBUG(2, ("sync_with_lmb: Initiating sync with local master browser %s<0x20> at IP %s for \ +workgroup %s\n", browc->lmb_name, inet_ntoa(browc->ip), browc->work_group)); + + sync_browse_lists(unicast_subnet, work, browc->lmb_name, 0x20, browc->ip, True); + + browc->sync_time += (CHECK_TIME_DMB_TO_LMB_SYNC * 60); +} + +/**************************************************************************** +Sync or expire any local master browsers. +**************************************************************************/ + +void dmb_expire_and_sync_browser_lists(time_t t) +{ + static time_t last_run = 0; + struct browse_cache_record *browc; + + /* Only do this every 20 seconds. */ + if (t - last_run < 20) + return; + + last_run = t; + + expire_lmb_browsers(t); + + for (browc = lmb_browserlist; browc; browc = browc->next) + { + if (browc->sync_time < t) + sync_with_lmb(browc); + } +} + +/**************************************************************************** +As a local master browser, send an announce packet to the domain master browser. +**************************************************************************/ + +static void announce_local_master_browser_to_domain_master_browser( struct work_record *work) +{ + pstring outbuf; + char *p; + + if(ismyip(work->dmb_addr)) + { + DEBUG(2,("announce_local_master_browser_to_domain_master_browser: We are both a domain \ +and a local master browser for workgroup %s. \ +Do not announce to ourselves.\n", work->work_group )); + return; + } + + bzero(outbuf,sizeof(outbuf)); + p = outbuf; + CVAL(p,0) = ANN_MasterAnnouncement; + p++; + + StrnCpy(p,myname,15); + strupper(p); + p = skip_string(p,1); + + DEBUG(4,("announce_local_master_browser_to_domain_master_browser: Sending local master announce \ +to %s for workgroup %s.\n", namestr(&work->dmb_name), work->work_group )); + + send_mailslot(True, BROWSE_MAILSLOT, outbuf,PTR_DIFF(p,outbuf), + myname, 0x0, work->dmb_name.name, 0x20, work->dmb_addr, FIRST_SUBNET->myip); + +} + +/**************************************************************************** +As a local master browser, do a sync with a domain master browser. +**************************************************************************/ + +static void sync_with_dmb(struct work_record *work) +{ + DEBUG(2, ("sync_with_dmb: Initiating sync with domain master browser %s at IP %s for \ +workgroup %s\n", namestr(&work->dmb_name), inet_ntoa(work->dmb_addr), work->work_group)); + + sync_browse_lists(unicast_subnet, work, work->dmb_name.name, work->dmb_name.name_type, + work->dmb_addr, False); +} + +/**************************************************************************** + Function called when a node status query to a domain master browser IP succeeds. +****************************************************************************/ + +static void domain_master_node_status_success(struct subnet_record *subrec, + struct userdata_struct *userdata, + struct res_rec *answers, + struct in_addr from_ip) +{ + struct work_record *work = find_workgroup_on_subnet( subrec, userdata->data); + + if(work == NULL) + { + DEBUG(0,("domain_master_node_status_success: Unable to find workgroup %s on subnet %s.\n", + userdata->data, subrec->subnet_name)); + return; + } + + DEBUG(3,("domain_master_node_status_success: Success in node status for workgroup %s from ip %s\n", + work->work_group, inet_ntoa(from_ip) )); + + /* Go through the list of names found at answers->rdata and look for + the first SERVER<0x20> name. */ + + if(answers->rdata != NULL) + { + char *p = answers->rdata; + int numnames = CVAL(p, 0); + + p += 1; + + while (numnames--) + { + char qname[17]; + uint16 nb_flags; + int name_type; + + StrnCpy(qname,p,15); + name_type = CVAL(p,15); + nb_flags = get_nb_flags(&p[16]); + trim_string(qname,NULL," "); + + p += 18; + + if(!(nb_flags & NB_GROUP) && (name_type == 0x20)) + { + struct nmb_name nmbname; + + make_nmb_name(&nmbname, qname, name_type, scope); + + /* Copy the dmb name and IP address + into the workgroup struct. */ + + work->dmb_name = nmbname; + putip((char *)&work->dmb_addr, &from_ip); + + /* Do the local master browser announcement to the domain + master browser name and IP. */ + announce_local_master_browser_to_domain_master_browser( work ); + + /* Now synchronise lists with the domain master browser. */ + sync_with_dmb(work); + break; + } + } + } + else + DEBUG(0,("domain_master_node_status_success: Failed to find a SERVER<0x20> \ +name in reply from IP %s.\n", inet_ntoa(from_ip) )); +} + +/**************************************************************************** + Function called when a node status query to a domain master browser IP fails. +****************************************************************************/ + +static void domain_master_node_status_fail(struct subnet_record *subrec, + struct response_record *rrec) +{ + struct userdata_struct *userdata = rrec->userdata; + + DEBUG(0,("domain_master_node_status_fail: Doing a node status request to \ +the domain master browser for workgroup %s at IP %s failed. Cannot sync browser \ +lists.\n", userdata->data, inet_ntoa(rrec->packet->ip) )); + +} + +/**************************************************************************** + Function called when a query for a WORKGROUP<1b> name succeeds. +****************************************************************************/ + +static void find_domain_master_name_query_success(struct subnet_record *subrec, + struct userdata_struct *userdata_in, + struct nmb_name *q_name, struct in_addr answer_ip, struct res_rec *rrec) +{ + /* + * Unfortunately, finding the IP address of the Domain Master Browser, + * as we have here, is not enough. We need to now do a sync to the + * SERVERNAME<0x20> NetBIOS name, as only recent NT servers will + * respond to the SMBSERVER name. To get this name from IP + * address we do a Node status request, and look for the first + * NAME<0x20> in the response, and take that as the server name. + * We also keep a cache of the Domain Master Browser name for this + * workgroup in the Workgroup struct, so that if the same IP addess + * is returned every time, we don't need to do the node status + * request. + */ + + struct work_record *work; + struct nmb_name nmbname; + struct userdata_struct *userdata; + char ud[sizeof(struct userdata_struct) + sizeof(fstring)+1]; + + if (!(work = find_workgroup_on_subnet(subrec, q_name->name))) { + DEBUG(0, ("find_domain_master_name_query_success: failed to find \ +workgroup %s\n", q_name->name )); + return; + } + + /* First check if we already have a dmb for this workgroup. */ + + if(!ip_equal(work->dmb_addr, ipzero) && ip_equal(work->dmb_addr, answer_ip)) + { + /* Do the local master browser announcement to the domain + master browser name and IP. */ + announce_local_master_browser_to_domain_master_browser( work ); + + /* Now synchronise lists with the domain master browser. */ + sync_with_dmb(work); + return; + } + else + putip((char *)&work->dmb_addr, &ipzero); + + /* Now initiate the node status request. */ + bzero((char *)&nmbname, sizeof(nmbname)); + nmbname.name[0] = '*'; + + /* Put the workgroup name into the userdata so we know + what workgroup we're talking to when the reply comes + back. */ + + /* Setup the userdata_struct - this is copied so we can use + a stack variable for this. */ + userdata = (struct userdata_struct *)ud; + + userdata->copy_fn = NULL; + userdata->free_fn = NULL; + userdata->userdata_len = strlen(work->work_group)+1; + strcpy(userdata->data, work->work_group); + + node_status( subrec, &nmbname, answer_ip, + domain_master_node_status_success, + domain_master_node_status_fail, + userdata); +} + +/**************************************************************************** + Function called when a query for a WORKGROUP<1b> name fails. + ****************************************************************************/ +static void find_domain_master_name_query_fail(struct subnet_record *subrec, + struct response_record *rrec, + struct nmb_name *question_name, int fail_code) +{ + DEBUG(0,("find_domain_master_name_query_fail: Unable to find the Domain Master \ +Browser name %s for the workgroup %s. Unable to sync browse lists in this workgroup.\n", + namestr(question_name), question_name->name )); +} + +/**************************************************************************** +As a local master browser for a workgroup find the domain master browser +name, announce ourselves as local master browser to it and then pull the +full domain browse lists from it onto the given subnet. +**************************************************************************/ + +void announce_and_sync_with_domain_master_browser( struct subnet_record *subrec, + struct work_record *work) +{ + struct nmb_name nmbname; + + make_nmb_name(&nmbname,work->work_group,0x1b,scope); + + /* First, query for the WORKGROUP<1b> name from the WINS server. */ + query_name(unicast_subnet, nmbname.name, nmbname.name_type, + find_domain_master_name_query_success, + find_domain_master_name_query_fail, + NULL); + +} diff --git a/source3/nmbd/nmbd_elections.c b/source3/nmbd/nmbd_elections.c new file mode 100644 index 0000000000..5c3c4c7a01 --- /dev/null +++ b/source3/nmbd/nmbd_elections.c @@ -0,0 +1,348 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NBT netbios routines and daemon - version 2 + Copyright (C) Andrew Tridgell 1994-1997 + Copyright (C) Luke Kenneth Casson Leighton 1994-1997 + Copyright (C) Jeremy Allison 1994-1997 + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "includes.h" + +extern int DEBUGLEVEL; + +extern pstring myname; +extern fstring myworkgroup; + +/* Election parameters. */ +extern time_t StartupTime; + +/**************************************************************************** + Send an election datagram packet. +**************************************************************************/ +static void send_election_dgram(struct subnet_record *subrec, char *workgroup_name, + uint32 criterion, int timeup,char *server_name) +{ + pstring outbuf; + char *p; + + DEBUG(2,("send_election_dgram: Sending election packet for workgroup %s on subnet %s\n", + workgroup_name, subrec->subnet_name )); + + bzero(outbuf,sizeof(outbuf)); + p = outbuf; + CVAL(p,0) = ANN_Election; /* Election opcode. */ + p++; + + CVAL(p,0) = (criterion == 0 && timeup == 0) ? 0 : ELECTION_VERSION; + SIVAL(p,1,criterion); + SIVAL(p,5,timeup*1000); /* ms - Despite what the spec says. */ + p += 13; + pstrcpy(p,server_name); + strupper(p); + p = skip_string(p,1); + + send_mailslot(False, BROWSE_MAILSLOT, outbuf, PTR_DIFF(p,outbuf), + server_name, 0, + workgroup_name, 0x1e, + subrec->bcast_ip, subrec->myip); +} + +/******************************************************************* + We found a current master browser on one of our broadcast interfaces. +******************************************************************/ + +static void check_for_master_browser_success(struct subnet_record *subrec, + struct userdata_struct *userdata, + struct nmb_name *answer_name, + struct in_addr answer_ip, struct res_rec *rrec) +{ + DEBUG(3,("check_for_master_browser_success: Local master browser for workgroup %s exists at \ +IP %s (just checking).\n", answer_name->name, inet_ntoa(answer_ip) )); +} + +/******************************************************************* + We failed to find a current master browser on one of our broadcast interfaces. +******************************************************************/ + +static void check_for_master_browser_fail( struct subnet_record *subrec, + struct response_record *rrec, + struct nmb_name *question_name, + int fail_code) +{ + char *workgroup_name = question_name->name; + struct work_record *work = find_workgroup_on_subnet(subrec, workgroup_name); + + if(work == NULL) + { + DEBUG(0,("check_for_master_browser_fail: Unable to find workgroup %s on subnet %s.=\n", + workgroup_name, subrec->subnet_name )); + return; + } + + if (strequal(work->work_group, myworkgroup)) + { + + if (lp_local_master()) + { + /* We have discovered that there is no local master + browser, and we are configured to initiate + an election that we will participate in. + */ + DEBUG(2,("check_for_master_browser_fail: Forcing election on workgroup %s subnet %s\n", + work->work_group, subrec->subnet_name )); + + /* Setting this means we will participate when the + election is run in run_elections(). */ + work->needelection = True; + } + else + { + /* We need to force an election, because we are configured + not to become the local master, but we still need one, + having detected that one doesn't exist. + */ + send_election_dgram(subrec, work->work_group, 0, 0, myname); + } + } +} + +/******************************************************************* + Ensure there is a local master browser for a workgroup on our + broadcast interfaces. +******************************************************************/ + +void check_master_browser_exists(time_t t) +{ + static time_t lastrun=0; + struct subnet_record *subrec; + char *workgroup_name = myworkgroup; + + if (!lastrun) + lastrun = t; + + if (t < (lastrun + (CHECK_TIME_MST_BROWSE * 60))) + return; + + lastrun = t; + + dump_workgroups(); + + for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) + { + struct work_record *work; + + for (work = subrec->workgrouplist; work; work = work->next) + { + if (strequal(work->work_group, workgroup_name) && !AM_LOCAL_MASTER_BROWSER(work)) + { + /* Do a name query for the local master browser on this net. */ + query_name( subrec, work->work_group, 0x1d, + check_for_master_browser_success, + check_for_master_browser_fail, + NULL); + } + } + } +} + +/******************************************************************* + Run an election. +******************************************************************/ + +void run_elections(time_t t) +{ + static time_t lastime = 0; + + struct subnet_record *subrec; + + /* Send election packets once a second - note */ + if (lastime && (t - lastime <= 0)) + return; + + lastime = t; + + for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) + { + struct work_record *work; + + for (work = subrec->workgrouplist; work; work = work->next) + { + if (work->RunningElection) + { + send_election_dgram(subrec, work->work_group, work->ElectionCriterion, + t - StartupTime, myname); + + if (work->ElectionCount++ >= 4) + { + /* Won election (4 packets were sent out uncontested. */ + DEBUG(2,("run_elections: >>> Won election for workgroup %s on subnet %s <<<\n", + work->work_group, subrec->subnet_name )); + + work->RunningElection = False; + + become_local_master_browser(subrec, work); + } + } + } + } +} + +/******************************************************************* + Determine if I win an election. +******************************************************************/ + +static BOOL win_election(struct work_record *work, int version, + uint32 criterion, int timeup, char *server_name) +{ + int mytimeup = time(NULL) - StartupTime; + uint32 mycriterion = work->ElectionCriterion; + + /* If local master is false then never win + in election broadcasts. */ + if(!lp_local_master()) + { + DEBUG(3,("win_election: Losing election as local master == False\n")); + return False; + } + + DEBUG(4,("win_election: election comparison: %x:%x %x:%x %d:%d %s:%s\n", + version, ELECTION_VERSION, + criterion, mycriterion, + timeup, mytimeup, + server_name, myname)); + + if (version > ELECTION_VERSION) + return(False); + if (version < ELECTION_VERSION) + return(True); + + if (criterion > mycriterion) + return(False); + if (criterion < mycriterion) + return(True); + + if (timeup > mytimeup) + return(False); + if (timeup < mytimeup) + return(True); + + if (strcasecmp(myname, server_name) > 0) + return(False); + + return(True); +} + +/******************************************************************* + Process an incoming election datagram packet. +******************************************************************/ + +void process_election(struct subnet_record *subrec, struct packet_struct *p, char *buf) +{ + struct dgram_packet *dgram = &p->packet.dgram; + int version = CVAL(buf,0); + uint32 criterion = IVAL(buf,1); + int timeup = IVAL(buf,5)/1000; + char *server_name = buf+13; + struct work_record *work; + char *workgroup_name = dgram->dest_name.name; + + server_name[15] = 0; + + DEBUG(3,("process_election: Election request from %s at IP %s on subnet %s for workgroup %s.\n", + server_name,inet_ntoa(p->ip), subrec->subnet_name, workgroup_name )); + + DEBUG(5,("process_election: vers=%d criterion=%08x timeup=%d\n", version,criterion,timeup)); + + if(( work = find_workgroup_on_subnet(subrec, workgroup_name)) == NULL) + { + DEBUG(0,("process_election: Cannot find workgroup %s on subnet %s.\n", + workgroup_name, subrec->subnet_name )); + return; + } + + if (!strequal(work->work_group, myworkgroup)) + { + DEBUG(3,("process_election: ignoring election request for workgroup %s on subnet %s as this \ +is not my workgroup.\n", work->work_group, subrec->subnet_name )); + return; + } + + if (win_election(work, version,criterion,timeup,server_name)) + { + /* We take precedence over the requesting server. */ + if (!work->RunningElection) + { + /* We weren't running an election - start running one. */ + + work->needelection = True; + work->ElectionCount=0; + } + + /* Note that if we were running an election for this workgroup on this + subnet already, we just ignore the server we take precedence over. */ + } + else + { + /* We lost. Stop participating. */ + work->needelection = False; + + if (work->RunningElection || AM_LOCAL_MASTER_BROWSER(work)) + { + work->RunningElection = False; + DEBUG(3,("process_election: >>> Lost election for workgroup %s on subnet %s <<<\n", + work->work_group, subrec->subnet_name )); + if (AM_LOCAL_MASTER_BROWSER(work)) + unbecome_local_master_browser(subrec, work); + } + } +} + +/**************************************************************************** + This function looks over all the workgroups known on all the broadcast + subnets and decides if a browser election is to be run on that workgroup. + It returns True if any election packets need to be sent (this will then + be done by run_elections(). +***************************************************************************/ + +BOOL check_elections(void) +{ + struct subnet_record *subrec; + BOOL run_any_election = False; + + for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) + { + struct work_record *work; + for (work = subrec->workgrouplist; work; work = work->next) + { + run_any_election |= work->RunningElection; + + /* Only start an election if we are in the potential browser state. */ + if (work->needelection && !work->RunningElection && AM_POTENTIAL_MASTER_BROWSER(work)) + { + DEBUG(3,("check_elections: >>> Starting election for workgroup %s on subnet %s <<<\n", + work->work_group, subrec->subnet_name )); + + work->ElectionCount = 0; + work->RunningElection = True; + work->needelection = False; + } + } + } + return run_any_election; +} diff --git a/source3/nmbd/nmbd_incomingdgrams.c b/source3/nmbd/nmbd_incomingdgrams.c new file mode 100644 index 0000000000..24b4f33838 --- /dev/null +++ b/source3/nmbd/nmbd_incomingdgrams.c @@ -0,0 +1,625 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NBT netbios routines and daemon - version 2 + Copyright (C) Andrew Tridgell 1994-1997 + Copyright (C) Luke Kenneth Casson Leighton 1994-1997 + Copyright (C) Jeremy Allison 1994-1997 + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "includes.h" + +extern int DEBUGLEVEL; + +extern pstring myname; +extern fstring myworkgroup; + +#if 0 + +/* XXXX note: This function is currently unsuitable for use, as it + does not properly check that a server is in a fit state to become + a backup browser before asking it to be one. + The code is left here to be worked on at a later date. +*/ + +/**************************************************************************** +Tell a server to become a backup browser +**************************************************************************/ + +void tell_become_backup(void) +{ + struct subnet_record *subrec; + for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) + { + struct work_record *work; + for (work = subrec->workgrouplist; work; work = work->next) + { + struct server_record *servrec; + int num_servers = 0; + int num_backups = 0; + + for (servrec = work->serverlist; servrec; servrec = servrec->next) + { + num_servers++; + + if (is_myname(servrec->serv.name)) + continue; + + if (servrec->serv.type & SV_TYPE_BACKUP_BROWSER) + { + num_backups++; + continue; + } + + if (servrec->serv.type & SV_TYPE_MASTER_BROWSER) + continue; + + if (!(servrec->serv.type & SV_TYPE_POTENTIAL_BROWSER)) + continue; + + DEBUG(3,("num servers: %d num backups: %d\n", + num_servers, num_backups)); + + /* make first server a backup server. thereafter make every + tenth server a backup server */ + if (num_backups != 0 && (num_servers+9) / num_backups > 10) + continue; + + DEBUG(2,("sending become backup to %s %s for %s\n", + servrec->serv.name, inet_ntoa(subrec->bcast_ip), + work->work_group)); + + /* type 11 request from MYNAME(20) to WG(1e) for SERVER */ + do_announce_request(servrec->serv.name, work->work_group, + ANN_BecomeBackup, 0x20, 0x1e, subrec->bcast_ip); + } + } + } +} +#endif + +/******************************************************************* + Process an incoming host announcement packet. +*******************************************************************/ + +void process_host_announce(struct subnet_record *subrec, struct packet_struct *p, char *buf) +{ + struct dgram_packet *dgram = &p->packet.dgram; + int ttl = IVAL(buf,1)/1000; + char *announce_name = buf+5; + uint32 servertype = IVAL(buf,23); + char *comment = buf+31; + struct work_record *work; + struct server_record *servrec; + char *work_name; + char *source_name = dgram->source_name.name; + + comment[43] = 0; + + DEBUG(3,("process_host_announce: from %s<%02x> IP %s to \ +%s for server %s.\n", source_name, source_name[15], inet_ntoa(p->ip), + namestr(&dgram->dest_name),announce_name)); + + DEBUG(5,("process_host_announce: ttl=%d server type=%08x comment=%s\n", + ttl, servertype,comment)); + + /* Filter servertype to remove impossible bits. */ + servertype &= ~(SV_TYPE_LOCAL_LIST_ONLY|SV_TYPE_DOMAIN_ENUM); + + /* A host announcement must be sent to the name WORKGROUP<1d>. */ + if(dgram->dest_name.name_type != 0x1d) + { + DEBUG(2,("process_host_announce: incorrect name type for destination from IP %s \ +(was %02x) should be 0x1d. Allowing packet anyway.\n", + inet_ntoa(p->ip), dgram->dest_name.name_type)); + /* Change it so it was. */ + dgram->dest_name.name_type = 0x1d; + } + + /* For a host announce the workgroup name is the destination name. */ + work_name = dgram->dest_name.name; + + /* + * We are being very agressive here in adding a workgroup + * name on the basis of a host announcing itself as being + * in that workgroup. Maybe we should wait for the workgroup + * announce instead ? JRA. + */ + + if ((work = find_workgroup_on_subnet(subrec, work_name))==NULL) + { + /* We have no record of this workgroup. Add it. */ + if((work = create_workgroup_on_subnet(subrec, work_name, ttl))==NULL) + return; + } + + if((servrec = find_server_in_workgroup( work, announce_name))==NULL) + { + /* If this server is not already in the workgroup, add it. */ + create_server_on_workgroup(work, announce_name, + servertype|SV_TYPE_LOCAL_LIST_ONLY, + ttl, comment); + } + else + { + /* Update the record. */ + servrec->serv.type = servertype|SV_TYPE_LOCAL_LIST_ONLY; + update_server_ttl( servrec, ttl); + StrnCpy(servrec->serv.comment,comment,sizeof(servrec->serv.comment)-1); + } + + subrec->work_changed = True; +} + +/******************************************************************* + Process an incoming WORKGROUP announcement packet. +*******************************************************************/ + +void process_workgroup_announce(struct subnet_record *subrec, struct packet_struct *p, char *buf) +{ + struct dgram_packet *dgram = &p->packet.dgram; + int ttl = IVAL(buf,1)/1000; + char *workgroup_announce_name = buf+5; + uint32 servertype = IVAL(buf,23); + char *master_name = buf+31; + struct work_record *work; + char *source_name = dgram->source_name.name; + + master_name[43] = 0; + + DEBUG(3,("process_workgroup_announce: from %s<%02x> IP %s to \ +%s for workgroup %s.\n", source_name, source_name[15], inet_ntoa(p->ip), + namestr(&dgram->dest_name),workgroup_announce_name)); + + DEBUG(5,("process_workgroup_announce: ttl=%d server type=%08x master browser=%s\n", + ttl, servertype, master_name)); + + /* Workgroup announcements must only go to the MSBROWSE name. */ + if (!strequal(dgram->dest_name.name, MSBROWSE) || (dgram->dest_name.name_type != 0x1)) + { + DEBUG(0,("process_workgroup_announce: from IP %s should be to __MSBROWSE__<0x01> not %s\n", + inet_ntoa(p->ip), namestr(&dgram->dest_name))); + return; + } + + if ((work = find_workgroup_on_subnet(subrec, workgroup_announce_name))==NULL) + { + /* We have no record of this workgroup. Add it. */ + if((work = create_workgroup_on_subnet(subrec, workgroup_announce_name, ttl))==NULL) + return; + } + else + { + /* Update the workgroup death_time. */ + update_workgroup_ttl(work, ttl); + } + + if(*work->local_master_browser_name == '\0') + { + /* Set the master browser name. */ + StrnCpy(work->local_master_browser_name, master_name, + sizeof(work->local_master_browser_name)-1); + + } + + subrec->work_changed = True; +} + +/******************************************************************* + Process an incoming local master browser announcement packet. +*******************************************************************/ + +void process_local_master_announce(struct subnet_record *subrec, struct packet_struct *p, char *buf) +{ + struct dgram_packet *dgram = &p->packet.dgram; + int ttl = IVAL(buf,1)/1000; + char *server_name = buf+5; + uint32 servertype = IVAL(buf,23); + char *comment = buf+31; + char *work_name; + struct work_record *work; + struct server_record *servrec; + char *source_name = dgram->source_name.name; + + comment[43] = 0; + + DEBUG(3,("process_local_master_announce: from %s<%02x> IP %s to \ +%s for server %s.\n", source_name, source_name[15], inet_ntoa(p->ip), + namestr(&dgram->dest_name),server_name)); + + DEBUG(5,("process_local_master_announce: ttl=%d server type=%08x comment=%s\n", + ttl, servertype, comment)); + + /* A local master announcement must be sent to the name WORKGROUP<1e>. */ + if(dgram->dest_name.name_type != 0x1e) + { + DEBUG(0,("process_local_master_announce: incorrect name type for destination from IP %s \ +(was %02x) should be 0x1e. Ignoring packet.\n", + inet_ntoa(p->ip), dgram->dest_name.name_type)); + return; + } + + /* Filter servertype to remove impossible bits. */ + servertype &= ~(SV_TYPE_LOCAL_LIST_ONLY|SV_TYPE_DOMAIN_ENUM); + + /* For a local master announce the workgroup name is the destination name. */ + work_name = dgram->dest_name.name; + + if ((work = find_workgroup_on_subnet(subrec, work_name))==NULL) + { + /* We have no record of this workgroup. Add it. */ + if((work = create_workgroup_on_subnet(subrec, work_name, ttl))==NULL) + return; + } + + /* If we think we're the local master browser for this workgroup, + we should never have got this packet. We don't see our own + packets. + */ + if(AM_LOCAL_MASTER_BROWSER(work)) + { + DEBUG(0,("process_local_master_announce: Server %s at IP %s is announcing itself as \ +a local master browser for workgroup %s and we think we are master. Forcing election.\n", + server_name, inet_ntoa(p->ip), work_name)); + + /* Samba nmbd versions 1.9.17 to 1.9.17p4 have a bug in that when + they have become a local master browser once, they will never + stop sending local master announcements. To fix this we send + them a reset browser packet, with level 0x2 on the __SAMBA__ + name that only they should be listening to. */ + + send_browser_reset( 0x2, "__SAMBA__" , 0x20, p->ip); + + /* We should demote ourself and force an election. */ + + unbecome_local_master_browser( subrec, work); + + /* The actual election requests are handled in + nmbd_election.c */ + + work->needelection = True; + return; + } + + /* Find the server record on this workgroup. If it doesn't exist, add it. */ + + if((servrec = find_server_in_workgroup( work, server_name))==NULL) + { + /* If this server is not already in the workgroup, add it. */ + create_server_on_workgroup(work, server_name, + servertype|SV_TYPE_LOCAL_LIST_ONLY, + ttl, comment); + } + else + { + /* Update the record. */ + servrec->serv.type = servertype|SV_TYPE_LOCAL_LIST_ONLY; + update_server_ttl(servrec, ttl); + StrnCpy(servrec->serv.comment,comment,sizeof(servrec->serv.comment)-1); + } + + /* Set the master browser name. */ + StrnCpy(work->local_master_browser_name, server_name, + sizeof(work->local_master_browser_name)-1); + + subrec->work_changed = True; +} + +/******************************************************************* + Process a domain master announcement frame. + Domain master browsers receive these from local masters. The Domain + master should then issue a sync with the local master, asking for + that machines local server list. +******************************************************************/ + +void process_master_browser_announce(struct subnet_record *subrec, + struct packet_struct *p,char *buf) +{ + char *local_master_name = buf; + struct work_record *work; + struct browse_cache_record *browrec; + + local_master_name[15] = 0; + + DEBUG(3,("process_master_browser_announce: Local master announce from %s IP %s.\n", + local_master_name, inet_ntoa(p->ip))); + + if (!lp_domain_master()) + { + DEBUG(0,("process_master_browser_announce: Not configured as domain \ +master - ignoring master announce.\n")); + return; + } + + if((work = find_workgroup_on_subnet(subrec, myworkgroup)) == NULL) + { + DEBUG(0,("process_master_browser_announce: Cannot find workgroup %s on subnet %s\n", + myworkgroup, subrec->subnet_name)); + return; + } + + if(!AM_DOMAIN_MASTER_BROWSER(work)) + { + DEBUG(0,("process_master_browser_announce: Local master announce made to us from \ +%s IP %s and we are not a domain master browser.\n", local_master_name, inet_ntoa(p->ip))); + return; + } + + /* Add this host as a local master browser entry on the browse lists. + This causes a sync request to be made to it at a later date. + */ + + if((browrec = find_browser_in_lmb_cache( local_master_name )) == NULL) + { + /* Add it. */ + create_browser_in_lmb_cache( work->work_group, local_master_name, p->ip); + } + else + update_browser_death_time(browrec); +} + +/**************************************************************************** + Send a backup list response. +*****************************************************************************/ + +static void send_backup_list_response(struct subnet_record *subrec, + struct work_record *work, + struct nmb_name *send_to_name, + unsigned char max_number_requested, + uint32 token, struct in_addr sendto_ip) +{ + char outbuf[1024]; + char *p, *countptr, *nameptr; + int count = 0; + int len; + struct server_record *servrec; + + bzero(outbuf,sizeof(outbuf)); + + DEBUG(3,("send_backup_list_response: sending backup list for workgroup %s to %s IP %s\n", + work->work_group, namestr(send_to_name), inet_ntoa(sendto_ip))); + + p = outbuf; + + SCVAL(p,0,ANN_GetBackupListResp); /* Backup list response opcode. */ + p++; + + countptr = p; + p++; + + SIVAL(p,0,token); /* The sender's unique info. */ + p += 4; + + nameptr = p; + + /* We always return at least one name - our own. */ + count = 1; + StrnCpy(p,myname,15); + strupper(p); + p = skip_string(p,1); + + /* Look for backup browsers in this workgroup. */ + for (servrec = work->serverlist; servrec; servrec = servrec->next) + { + len = PTR_DIFF(p, outbuf); + if((sizeof(outbuf) - len) < 16) + break; + + if(count >= max_number_requested) + break; + + if(strnequal(servrec->serv.name, myname,15)) + continue; + + if(!(servrec->serv.type & SV_TYPE_BACKUP_BROWSER)) + continue; + + StrnCpy(p, servrec->serv.name, 15); + strupper(p); + count++; + + DEBUG(5,("send_backup_list_response: Adding server %s number %d\n", + p, count)); + + p = skip_string(p,1); + } + + SCVAL(countptr, 0, count); + + len = PTR_DIFF(p, outbuf); + + DEBUG(4,("send_backup_list_response: sending response to %s<00> IP %s with %d servers.\n", + send_to_name->name, inet_ntoa(sendto_ip), count)); + + send_mailslot(True, BROWSE_MAILSLOT, + outbuf,PTR_DIFF(p,outbuf), + myname, 0, + send_to_name->name,0, + sendto_ip, subrec->myip); +} + +/******************************************************************* + Process a send backup list request packet. + + A client sends a backup list request to ask for a list of servers on + the net that maintain server lists for a domain. A server is then + chosen from this list to send NetServerEnum commands to to list + available servers. + +********************************************************************/ + +void process_get_backup_list_request(struct subnet_record *subrec, + struct packet_struct *p,char *buf) +{ + struct dgram_packet *dgram = &p->packet.dgram; + struct work_record *work; + unsigned char max_number_requested = CVAL(buf,0); + uint32 token = IVAL(buf,1); /* Sender's key index for the workgroup. */ + int name_type = dgram->dest_name.name_type; + char *workgroup_name = dgram->dest_name.name; + + DEBUG(3,("process_get_backup_list_request: request from %s IP %s to %s.\n", + namestr(&dgram->source_name), inet_ntoa(p->ip), + namestr(&dgram->dest_name))); + + /* We have to be a master browser, or a domain master browser + for the requested workgroup. That means it must be our + workgroup. */ + + if(strequal(workgroup_name, myworkgroup) == False) + { + DEBUG(7,("process_get_backup_list_request: Ignoring announce request for workgroup %s.\n", + workgroup_name)); + return; + } + + if((work = find_workgroup_on_subnet(subrec, workgroup_name)) == NULL) + { + DEBUG(0,("process_get_backup_list_request: Cannot find workgroup %s on \ +subnet %s.\n", workgroup_name, subrec->subnet_name)); + return; + } + + if(name_type == 0x1b) + { + /* We must be a domain master browser in order to + process this packet. */ + + if(!AM_DOMAIN_MASTER_BROWSER(work)) + { + DEBUG(0,("process_get_backup_list_request: domain list requested for workgroup %s \ +and I am not a domain master browser.\n", workgroup_name)); + return; + } + } + else if (name_type == 0x1d) + { + /* We must be a local master browser in order to + process this packet. */ + + if(!AM_LOCAL_MASTER_BROWSER(work)) + { + DEBUG(0,("process_get_backup_list_request: domain list requested for workgroup %s \ +and I am not a local master browser.\n", workgroup_name)); + return; + } + } + else + { + DEBUG(0,("process_get_backup_list_request: Invalid name type %x - should be 0x1b or 0x1d.\n", + name_type)); + return; + } + + send_backup_list_response(subrec, work, &dgram->source_name, + max_number_requested, token, p->ip); +} + +/******************************************************************* + Process a reset browser state packet. + + Diagnostic packet: + 0x1 - Stop being a master browser and become a backup browser. + 0x2 - Discard browse lists, stop being a master browser, try again. + 0x4 - Stop being a master browser forever. + +******************************************************************/ + +void process_reset_browser(struct subnet_record *subrec, + struct packet_struct *p,char *buf) +{ + struct dgram_packet *dgram = &p->packet.dgram; + int state = CVAL(buf,0); + struct subnet_record *sr; + + DEBUG(1,("process_reset_browser: received diagnostic browser reset \ +request from %s IP %s state=0x%X\n", + namestr(&dgram->source_name), inet_ntoa(p->ip), state)); + + /* Stop being a local master browser on all our broadcast subnets. */ + if (state & 0x1) + { + for (sr = FIRST_SUBNET; sr; sr = NEXT_SUBNET_EXCLUDING_UNICAST(sr)) + { + struct work_record *work; + for (work = sr->workgrouplist; work; work = work->next) + { + if (AM_LOCAL_MASTER_BROWSER(work)) + { + unbecome_local_master_browser(sr, work); + work->needelection = True; + } + } + } + } + + /* Discard our browse lists. */ + if (state & 0x2) + { + /* + * Calling expire_workgroups_and_servers with a -1 + * time causes all servers not marked with a PERMANENT_TTL + * on the workgroup lists to be discarded, and all + * workgroups with empty server lists to be discarded. + * This means we keep our own server names and workgroup + * as these have a PERMANENT_TTL. + */ + + expire_workgroups_and_servers(-1); + } + + /* Request to stop browsing altogether. */ + if (state & 0x4) + DEBUG(1,("process_reset_browser: ignoring request to stop being a browser.\n")); +} + +/******************************************************************* + Process a announcement request packet. + We don't respond immediately, we just check it's a request for + out workgroup and then set the flag telling the announce code + in nmbd_sendannounce.c:announce_my_server_names that an + announcement is needed soon. + ******************************************************************/ + +void process_announce_request(struct subnet_record *subrec, struct packet_struct *p, char *buf) +{ + struct dgram_packet *dgram = &p->packet.dgram; + struct work_record *work; + char *workgroup_name = dgram->dest_name.name; + + DEBUG(3,("process_announce_request: Announce request from %s IP %s to %s.\n", + namestr(&dgram->source_name), inet_ntoa(p->ip), + namestr(&dgram->dest_name))); + + /* We only send announcement requests on our workgroup. */ + if(strequal(workgroup_name, myworkgroup) == False) + { + DEBUG(7,("process_announce_request: Ignoring announce request for workgroup %s.\n", + workgroup_name)); + return; + } + + if((work = find_workgroup_on_subnet(subrec, workgroup_name)) == NULL) + { + DEBUG(0,("process_announce_request: Unable to find workgroup %s on subnet !\n", + workgroup_name)); + return; + } + + work->needannounce = True; +} diff --git a/source3/nmbd/nmbd_incomingrequests.c b/source3/nmbd/nmbd_incomingrequests.c new file mode 100644 index 0000000000..ff4bb07a45 --- /dev/null +++ b/source3/nmbd/nmbd_incomingrequests.c @@ -0,0 +1,556 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NBT netbios routines and daemon - version 2 + Copyright (C) Andrew Tridgell 1994-1997 + Copyright (C) Luke Kenneth Casson Leighton 1994-1997 + Copyright (C) Jeremy Allison 1994-1997 + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + This file contains all the code to process NetBIOS requests coming + in on port 137. It does not deal with the code needed to service + WINS server requests, but only broadcast and unicast requests. + +*/ + +#include "includes.h" + +extern int DEBUGLEVEL; + +/**************************************************************************** +Send a name release response. +**************************************************************************/ + +static void send_name_release_response(int rcode, struct packet_struct *p) +{ + struct nmb_packet *nmb = &p->packet.nmb; + char rdata[6]; + + memcpy(&rdata[0], &nmb->additional->rdata[0], 6); + + reply_netbios_packet(p, /* Packet to reply to. */ + rcode, /* Result code. */ + NMB_REL, /* nmbd type code. */ + NMB_NAME_RELEASE_OPCODE, /* opcode. */ + 0, /* ttl. */ + rdata, /* data to send. */ + 6); /* data length. */ +} + +/**************************************************************************** +Process a name release packet on a broadcast subnet. +Ignore it if it's not one of our names. +****************************************************************************/ + +void process_name_release_request(struct subnet_record *subrec, + struct packet_struct *p) +{ + struct nmb_packet *nmb = &p->packet.nmb; + struct in_addr owner_ip; + struct nmb_name *question = &nmb->question.question_name; + BOOL bcast = nmb->header.nm_flags.bcast; + uint16 nb_flags = get_nb_flags(nmb->additional->rdata); + BOOL group = (nb_flags & NB_GROUP) ? True : False; + struct name_record *namerec; + int rcode = 0; + + putip((char *)&owner_ip,&nmb->additional->rdata[2]); + + if(!bcast) + { + /* We should only get broadcast name release packets here. + Anyone trying to release unicast should be going to a WINS + server. If the code gets here, then either we are not a wins + server and they sent it anyway, or we are a WINS server and + the request was malformed. Either way, log an error here. + and send an error reply back. + */ + DEBUG(0,("process_name_release_request: unicast name release request \ +received for name %s from IP %s on subnet %s. Error - should be sent to WINS server\n", + namestr(question), inet_ntoa(owner_ip), subrec->subnet_name)); + + send_name_release_response(FMT_ERR, p); + return; + } + + DEBUG(3,("process_name_release_request: Name release on name %s, \ +subnet %s from owner IP %s\n", + namestr(&nmb->question.question_name), + subrec->subnet_name, inet_ntoa(owner_ip))); + + /* If someone is releasing a broadcast group name, just ignore it. */ + if( group && !ismyip(owner_ip) ) + return; + + namerec = find_name_on_subnet(subrec, &nmb->question.question_name, FIND_ANY_NAME); + + /* We only care about someone trying to release one of our names. */ + if (namerec && ((namerec->source == SELF_NAME) || (namerec->source == PERMANENT_NAME))) + { + rcode = ACT_ERR; + DEBUG(0, ("process_name_release_request: Attempt to release name %s from IP %s \ +on subnet %s being rejected as it is one of our names.\n", + namestr(&nmb->question.question_name), inet_ntoa(owner_ip), subrec->subnet_name)); + } + + if(rcode == 0) + return; + + /* Send a NAME RELEASE RESPONSE (pos/neg) see rfc1002.txt 4.2.10-11 */ + send_name_release_response(rcode, p); +} + +/**************************************************************************** +Send a name registration response. +**************************************************************************/ + +static void send_name_registration_response(int rcode, int ttl, struct packet_struct *p) +{ + struct nmb_packet *nmb = &p->packet.nmb; + char rdata[6]; + + memcpy(&rdata[0], &nmb->additional->rdata[0], 6); + + reply_netbios_packet(p, /* Packet to reply to. */ + rcode, /* Result code. */ + NMB_REG, /* nmbd type code. */ + NMB_NAME_REG_OPCODE, /* opcode. */ + ttl, /* ttl. */ + rdata, /* data to send. */ + 6); /* data length. */ +} + +/**************************************************************************** +Process a name refresh request on a broadcast subnet. +**************************************************************************/ + +void process_name_refresh_request(struct subnet_record *subrec, + struct packet_struct *p) +{ + + struct nmb_packet *nmb = &p->packet.nmb; + struct nmb_name *question = &nmb->question.question_name; + BOOL bcast = nmb->header.nm_flags.bcast; + struct in_addr from_ip; + + putip((char *)&from_ip,&nmb->additional->rdata[2]); + + if(!bcast) + { + /* We should only get broadcast name refresh packets here. + Anyone trying to refresh unicast should be going to a WINS + server. If the code gets here, then either we are not a wins + server and they sent it anyway, or we are a WINS server and + the request was malformed. Either way, log an error here. + and send an error reply back. + */ + DEBUG(0,("process_name_refresh_request: unicast name registration request \ +received for name %s from IP %s on subnet %s. Error - should be sent to WINS server\n", + namestr(question), inet_ntoa(from_ip), subrec->subnet_name)); + + send_name_registration_response(FMT_ERR, 0, p); + return; + } + + /* Just log a message. We really don't care about broadcast name + refreshes. */ + + DEBUG(3,("process_name_refresh_request: Name refresh for name %s \ +IP %s on subnet %s\n", namestr(question), inet_ntoa(from_ip), subrec->subnet_name)); + +} + +/**************************************************************************** +Process a name registration request on a broadcast subnet. +**************************************************************************/ + +void process_name_registration_request(struct subnet_record *subrec, + struct packet_struct *p) +{ + struct nmb_packet *nmb = &p->packet.nmb; + struct nmb_name *question = &nmb->question.question_name; + BOOL bcast = nmb->header.nm_flags.bcast; + uint16 nb_flags = get_nb_flags(nmb->additional->rdata); + BOOL group = (nb_flags & NB_GROUP) ? True : False; + struct name_record *namerec = NULL; + int ttl = nmb->additional->ttl; + struct in_addr from_ip; + + putip((char *)&from_ip,&nmb->additional->rdata[2]); + + if(!bcast) + { + /* We should only get broadcast name registration packets here. + Anyone trying to register unicast should be going to a WINS + server. If the code gets here, then either we are not a wins + server and they sent it anyway, or we are a WINS server and + the request was malformed. Either way, log an error here. + and send an error reply back. + */ + DEBUG(0,("process_name_registration_request: unicast name registration request \ +received for name %s from IP %s on subnet %s. Error - should be sent to WINS server\n", + namestr(question), inet_ntoa(from_ip), subrec->subnet_name)); + + send_name_registration_response(FMT_ERR, 0, p); + return; + } + + DEBUG(3,("process_name_registration_request: Name registration for name %s \ +IP %s on subnet %s\n", namestr(question), inet_ntoa(from_ip), subrec->subnet_name)); + + /* See if the name already exists. */ + namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME); + + if (!group) + { + /* Unique name. */ + + if ((namerec != NULL) && + ((namerec->source == SELF_NAME) || (namerec->source == PERMANENT_NAME) || + NAME_GROUP(namerec)) + ) + { + /* No-one can register one of Samba's names, nor can they + register a name that's a group name as a unique name */ + + send_name_registration_response(ACT_ERR, 0, p); + return; + } + else if(namerec != NULL) + { + /* Update the namelist record with the new information. */ + namerec->ip[0] = from_ip; + update_name_ttl(namerec, ttl); + + DEBUG(3,("process_name_registration_request: Updated name record %s \ +with IP %s on subnet %s\n",namestr(&namerec->name),inet_ntoa(from_ip), subrec->subnet_name)); + return; + } + } + else + { + /* Group name. */ + + if((namerec != NULL) && !NAME_GROUP(namerec) && + ((namerec->source == SELF_NAME) || (namerec->source == PERMANENT_NAME)) + ) + { + /* Disallow group names when we have a unique name. */ + send_name_registration_response(ACT_ERR, 0, p); + return; + } + } +} + +/**************************************************************************** +This is used to sort names for a name status into a sensible order. +We put our own names first, then in alphabetical order. +**************************************************************************/ + +static int status_compare(char *n1,char *n2) +{ + extern pstring myname; + int l1,l2,l3; + + /* It's a bit tricky because the names are space padded */ + for (l1=0;l1<15 && n1[l1] && n1[l1] != ' ';l1++) ; + for (l2=0;l2<15 && n2[l2] && n2[l2] != ' ';l2++) ; + l3 = strlen(myname); + + if ((l1==l3) && strncmp(n1,myname,l3) == 0 && + (l2!=l3 || strncmp(n2,myname,l3) != 0)) + return -1; + + if ((l2==l3) && strncmp(n2,myname,l3) == 0 && + (l1!=l3 || strncmp(n1,myname,l3) != 0)) + return 1; + + return memcmp(n1,n2,18); +} + + +/**************************************************************************** + Process a node status query + ****************************************************************************/ + +void process_node_status_request(struct subnet_record *subrec, 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, *buf0; + int names_added,i; + struct name_record *namerec; + + DEBUG(3,("process_node_status_request: status request for name %s from IP %s on \ +subnet %s.\n", namestr(&nmb->question.question_name), inet_ntoa(p->ip), + subrec->subnet_name)); + + if((namerec = find_name_on_subnet(subrec, &nmb->question.question_name, + FIND_SELF_NAME)) == 0) + { + DEBUG(1,("process_node_status_request: status request for name %s from IP %s on \ +subnet %s - name not found.\n", namestr(&nmb->question.question_name), + inet_ntoa(p->ip), subrec->subnet_name)); + + return; + } + + /* XXXX hack, we should calculate exactly how many will fit. */ + bufend = &rdata[MAX_DGRAM_SIZE] - 18; + countptr = buf = rdata; + buf += 1; + buf0 = buf; + + names_added = 0; + + namerec = subrec->namelist; + + while (buf < bufend) + { + if ((namerec->source == SELF_NAME) || (namerec->source == PERMANENT_NAME)) + { + int name_type = namerec->name.name_type; + + if (!strequal(namerec->name.name,"*") && + !strequal(namerec->name.name,"__SAMBA__") && + (name_type < 0x1b || name_type >= 0x20 || + ques_type < 0x1b || ques_type >= 0x20 || + strequal(qname, namerec->name.name))) + { + /* Start with the name. */ + bzero(buf,18); + sprintf(buf,"%-15.15s",namerec->name.name); + strupper(buf); + + /* Put the name type and netbios flags in the buffer. */ + buf[15] = name_type; + set_nb_flags(&buf[16],namerec->nb_flags); + buf[16] |= NB_ACTIVE; /* all our names are active */ + + buf += 18; + + names_added++; + } + } + + /* Remove duplicate names. */ + qsort(buf0,names_added,18,QSORT_CAST status_compare); + + for (i=1;inext; + + if (!namerec) + { + /* End of the subnet specific name list. Now + add the names on the unicast subnet . */ + struct subnet_record *uni_subrec = unicast_subnet; + + if (uni_subrec != subrec) + { + subrec = uni_subrec; + namerec = subrec->namelist; + } + } + if (!namerec) + break; + + } + + SCVAL(countptr,0,names_added); + + /* We don't send any stats as they could be used to attack + the protocol. */ + bzero(buf,64); + + buf += 46; + + /* Send a NODE STATUS RESPONSE */ + reply_netbios_packet(p, /* Packet to reply to. */ + 0, /* Result code. */ + NMB_STATUS, /* nmbd type code. */ + NMB_NAME_QUERY_OPCODE, /* opcode. */ + 0, /* ttl. */ + rdata, /* data to send. */ + PTR_DIFF(buf,rdata)); /* data length. */ +} + + +/*************************************************************************** +Process a name query. + +For broadcast name queries: + + - Only reply if the query is for one of YOUR names. + - NEVER send a negative response to a broadcast query. + +****************************************************************************/ + +void process_name_query_request(struct subnet_record *subrec, 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; + char *prdata = NULL; + char rdata[6]; + BOOL success = False; + struct name_record *namerec = NULL; + int reply_data_len = 0; + int i; + + DEBUG(3,("process_name_query_request: Name query from %s on subnet %s for name %s\n", + inet_ntoa(p->ip), subrec->subnet_name, namestr(question))); + + /* Look up the name in the cache - if the request is a broadcast request that + came from a subnet we don't know about then search all the broadcast subnets + for a match (as we don't know what interface the request came in on). */ + + if(subrec == remote_broadcast_subnet) + namerec = find_name_for_remote_broadcast_subnet( question, FIND_ANY_NAME); + else + namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME); + + + /* Check if it is a name that expired */ + if (namerec && ((namerec->death_time != PERMANENT_TTL) && (namerec->death_time < p->timestamp))) + { + DEBUG(5,("process_name_query_request: expired name %s\n", namestr(&namerec->name))); + namerec = NULL; + } + + if (namerec) + { + + /* + * Always respond to unicast queries. + * Don't respond to broadcast queries unless the query is for + * a name we own, a Primary Domain Controller name, or a WINS_PROXY + * name with type 0 or 0x20. WINS_PROXY names are only ever added + * into the namelist if we were configured as a WINS proxy. + */ + + if (!bcast || + (bcast && ((name_type == 0x1b) || (namerec->source == SELF_NAME) || + (namerec->source == PERMANENT_NAME) || + ((namerec->source == WINS_PROXY_NAME) && ((name_type == 0) || (name_type == 0x20))))) + ) + { + + /* The requested name is a directed query, or it's SELF or PERMANENT or WINS_PROXY, + or it's a Domain Master type. */ + + ttl = (namerec->death_time != PERMANENT_TTL) ? + namerec->death_time - p->timestamp : lp_max_ttl(); + + /* Copy all known ip addresses into the return data. */ + /* Optimise for the common case of one IP address so + we don't need a malloc. */ + + if(namerec->num_ips == 1 ) + prdata = rdata; + else + { + if((prdata = (char *)malloc( namerec->num_ips * 6 )) == NULL) + { + DEBUG(0,("process_name_query_request: malloc fail !\n")); + return; + } + } + + for( i = 0; i < namerec->num_ips; i++) + { + set_nb_flags(&prdata[i*6],namerec->nb_flags); + putip((char *)&prdata[2+(i*6)], &namerec->ip[i]); + } + reply_data_len = namerec->num_ips * 6; + success = True; + } + } + + /* + * If a machine is broadcasting a name lookup request and we have lp_wins_proxy() + * set we should initiate a WINS query here. On success we add the resolved name + * into our namelist with a type of WINS_PROXY_NAME and then reply to the query. + */ + + if(!success && (namerec == NULL) && we_are_a_wins_client() && lp_wins_proxy() && + bcast && (subrec != remote_broadcast_subnet)) + { + make_wins_proxy_name_query_request( subrec, p, question ); + return; + } + + if (!success && bcast) + { + if((prdata != rdata) && (prdata != NULL)) + free(rdata); + return; /* Never reply with a negative response to broadcasts. */ + } + + /* + * Final check. From observation, if a unicast packet is sent + * to a non-WINS server with the recursion desired bit set + * then never send a negative response. + */ + + if(!success && !bcast && nmb->header.nm_flags.recursion_desired) + { + if((prdata != rdata) && (prdata != NULL)) + free(rdata); + return; + } + + if (success) + { + rcode = 0; + DEBUG(3,("OK\n")); + } + else + { + rcode = NAM_ERR; + DEBUG(3,("UNKNOWN\n")); + } + + /* See rfc1002.txt 4.2.13. */ + + reply_netbios_packet(p, /* Packet to reply to. */ + rcode, /* Result code. */ + NMB_QUERY, /* nmbd type code. */ + NMB_NAME_QUERY_OPCODE, /* opcode. */ + ttl, /* ttl. */ + prdata, /* data to send. */ + reply_data_len); /* data length. */ + + if((prdata != rdata) && (prdata != NULL)) + free(prdata); +} diff --git a/source3/nmbd/nmbd_lmhosts.c b/source3/nmbd/nmbd_lmhosts.c new file mode 100644 index 0000000000..2dd1db81cd --- /dev/null +++ b/source3/nmbd/nmbd_lmhosts.c @@ -0,0 +1,168 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NBT netbios routines and daemon - version 2 + Copyright (C) Jeremy Allison 1994-1997 + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + Revision History: + + Handle lmhosts file reading. + +*/ + +#include "includes.h" + +extern int DEBUGLEVEL; + +/**************************************************************************** +Load a lmhosts file. +****************************************************************************/ +void load_lmhosts_file(char *fname) +{ + FILE *fp = fopen(fname,"r"); + pstring line; + if (!fp) { + DEBUG(2,("load_lmhosts_file: Can't open lmhosts file %s. Error was %s\n", + fname, strerror(errno))); + return; + } + + while (!feof(fp)) + { + pstring ip,name,flags,extra; + struct subnet_record *subrec = NULL; + char *ptr; + int count = 0; + struct in_addr ipaddr; + enum name_source source = LMHOSTS_NAME; + int name_type = -1; + + if (!fgets_slash(line,sizeof(pstring),fp)) + continue; + + if (*line == '#') + continue; + + strcpy(ip,""); + strcpy(name,""); + strcpy(flags,""); + + ptr = line; + + if (next_token(&ptr,ip ,NULL)) + ++count; + if (next_token(&ptr,name ,NULL)) + ++count; + if (next_token(&ptr,flags,NULL)) + ++count; + if (next_token(&ptr,extra,NULL)) + ++count; + + if (count <= 0) + continue; + + if (count > 0 && count < 2) + { + DEBUG(0,("load_lmhosts_file: Ill formed hosts line [%s]\n",line)); + continue; + } + + if (count >= 4) + { + DEBUG(0,("load_lmhosts_file: too many columns in lmhosts file %s (obsolete syntax)\n", + fname)); + continue; + } + + DEBUG(4, ("load_lmhosts_file: lmhost entry: %s %s %s\n", ip, name, flags)); + + if (strchr(flags,'G') || strchr(flags,'S')) + { + DEBUG(0,("load_lmhosts_file: group flag in %s ignored (obsolete)\n",fname)); + continue; + } + + ipaddr = *interpret_addr2(ip); + + /* Extra feature. If the name ends in '#XX', where XX is a hex number, + then only add that name type. */ + if((ptr = strchr(name, '#')) != NULL) + { + char *endptr; + + ptr++; + name_type = (int)strtol(ptr, &endptr,0); + + if(!*ptr || (endptr == ptr)) + { + DEBUG(0,("load_lmhosts_file: invalid name %s containing '#'.\n", name)); + continue; + } + + *(--ptr) = '\0'; /* Truncate at the '#' */ + } + + /* We find a relevent subnet to put this entry on, then add it. */ + /* Go through all the broadcast subnets and see if the mask matches. */ + for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) + { + if(same_net(ipaddr, subrec->bcast_ip, subrec->mask_ip)) + break; + } + + /* If none match add the name to the remote_broadcast_subnet. */ + if(subrec == NULL) + subrec = remote_broadcast_subnet; + + if(name_type == -1) + { + /* Add the (0) and (0x20) names directly into the namelist for this subnet. */ + add_name_to_subnet(subrec,name,0x00,(uint16)NB_ACTIVE,PERMANENT_TTL,source,1,&ipaddr); + add_name_to_subnet(subrec,name,0x20,(uint16)NB_ACTIVE,PERMANENT_TTL,source,1,&ipaddr); + } + else + { + /* Add the given name type to the subnet namelist. */ + add_name_to_subnet(subrec,name,name_type,(uint16)NB_ACTIVE,PERMANENT_TTL,source,1,&ipaddr); + } + } + + fclose(fp); +} + +/**************************************************************************** + Find a name read from the lmhosts file. We secretly check the names on + the remote_broadcast_subnet as if the name was added to a regular broadcast + subnet it will be found by normal name query processing. +****************************************************************************/ + +BOOL find_name_in_lmhosts(struct nmb_name *nmbname, struct name_record **namerecp) +{ + struct name_record *namerec; + + *namerecp = NULL; + + if((namerec = find_name_on_subnet(remote_broadcast_subnet, nmbname, + FIND_ANY_NAME))==NULL) + return False; + + if(!NAME_IS_ACTIVE(namerec) || (namerec->source != LMHOSTS_NAME)) + return False; + + *namerecp = namerec; + return True; +} diff --git a/source3/nmbd/nmbd_logonnames.c b/source3/nmbd/nmbd_logonnames.c new file mode 100644 index 0000000000..b2431ec0a7 --- /dev/null +++ b/source3/nmbd/nmbd_logonnames.c @@ -0,0 +1,166 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NBT netbios routines and daemon - version 2 + Copyright (C) Andrew Tridgell 1994-1997 + Copyright (C) Luke Kenneth Casson Leighton 1994-1997 + Copyright (C) Jeremy Allison 1994-1997 + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "includes.h" + +extern int DEBUGLEVEL; + +extern pstring scope; +extern pstring myname; +extern fstring myworkgroup; +extern char **my_netbios_names; +extern struct in_addr ipzero; +extern struct in_addr allones_ip; + +extern uint16 samba_nb_type; /* Samba's NetBIOS type. */ + +/**************************************************************************** + Fail to become a Logon server on a subnet. + ****************************************************************************/ +static void become_logon_server_fail(struct subnet_record *subrec, + struct response_record *rrec, + struct nmb_name *fail_name) +{ + struct work_record *work = find_workgroup_on_subnet(subrec, fail_name->name); + struct server_record *servrec; + + if(!work) + { + DEBUG(0,("become_logon_server_fail: Error - cannot find \ +workgroup %s on subnet %s\n", fail_name->name, subrec->subnet_name)); + return; + } + + if((servrec = find_server_in_workgroup( work, myname)) == NULL) + { + DEBUG(0,("become_logon_server_fail: Error - cannot find server %s \ +in workgroup %s on subnet %s\n", + myname, fail_name->name, subrec->subnet_name)); + work->log_state = LOGON_NONE; + return; + } + + /* Set the state back to LOGON_NONE. */ + work->log_state = LOGON_NONE; + + servrec->serv.type &= ~SV_TYPE_DOMAIN_CTRL; + + DEBUG(0,("become_logon_server_fail: Failed to become a domain master for \ +workgroup %s on subnet %s. Couldn't register name %s.\n", + work->work_group, subrec->subnet_name, namestr(fail_name))); + +} + +/**************************************************************************** + Become a Logon server on a subnet. + ****************************************************************************/ + +static void become_logon_server_success(struct subnet_record *subrec, + struct userdata_struct *userdata, + struct nmb_name *registered_name, + uint16 nb_flags, + int ttl, struct in_addr registered_ip) +{ + struct work_record *work = find_workgroup_on_subnet( subrec, registered_name->name); + struct server_record *servrec; + + if(!work) + { + DEBUG(0,("become_logon_server_success: Error - cannot find \ +workgroup %s on subnet %s\n", registered_name->name, subrec->subnet_name)); + return; + } + + if((servrec = find_server_in_workgroup( work, myname)) == NULL) + { + DEBUG(0,("become_logon_server_success: Error - cannot find server %s \ +in workgroup %s on subnet %s\n", + myname, registered_name->name, subrec->subnet_name)); + work->log_state = LOGON_NONE; + return; + } + + /* Set the state in the workgroup structure. */ + work->log_state = LOGON_SRV; /* Become domain master. */ + + /* Update our server status. */ + servrec->serv.type |= (SV_TYPE_NT|SV_TYPE_DOMAIN_MEMBER); + /* To allow Win95 policies to load we need to set type domain + controller. + */ + servrec->serv.type |= SV_TYPE_DOMAIN_CTRL; + + /* Tell the namelist writer to write out a change. */ + subrec->work_changed = True; + + DEBUG(0,("become_logon_server_success: Samba is now a logon server\ +for workgroup %s on subnet %s\n", work->work_group, subrec->subnet_name)); +} + +/******************************************************************* + Become a logon server by attempting to register the WORKGROUP<1c> + group name. +******************************************************************/ + +static void become_logon_server(struct subnet_record *subrec, + struct work_record *work) +{ + DEBUG(2,("become_logon_server: Atempting to become logon server for workgroup %s \ +on subnet %s\n", work->work_group,subrec->subnet_name)); + + DEBUG(3,("become_logon_server: go to first stage: register %s<1c> name\n", + work->work_group)); + work->log_state = LOGON_WAIT; + + register_name(subrec, work->work_group,0x1c,samba_nb_type|NB_GROUP, + become_logon_server_success, + become_logon_server_fail, NULL); +} + +/***************************************************************************** + Add the internet group <1c> logon names by unicast and broadcast. + ****************************************************************************/ +void add_logon_names(void) +{ + struct subnet_record *subrec; + + for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec)) + { + struct work_record *work = find_workgroup_on_subnet(subrec, myworkgroup); + + if (work && (work->log_state == LOGON_NONE)) + { + struct nmb_name nmbname; + make_nmb_name(&nmbname,myworkgroup,0x1c,scope); + + if (find_name_on_subnet(subrec, &nmbname, FIND_SELF_NAME) == NULL) + { + DEBUG(0,("add_domain_logon_names: At time %s attempting to become \ +logon server for workgroup %s on subnet %s\n", timestring(), myworkgroup, + subrec->subnet_name)); + become_logon_server(subrec, work); + } + } + } +} diff --git a/source3/nmbd/nmbd_mynames.c b/source3/nmbd/nmbd_mynames.c new file mode 100644 index 0000000000..660b545069 --- /dev/null +++ b/source3/nmbd/nmbd_mynames.c @@ -0,0 +1,165 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NBT netbios routines and daemon - version 2 + Copyright (C) Andrew Tridgell 1994-1997 + Copyright (C) Luke Kenneth Casson Leighton 1994-1997 + Copyright (C) Jeremy Allison 1994-1997 + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "includes.h" + +extern int DEBUGLEVEL; + +extern char **my_netbios_names; +extern pstring myname; +extern fstring myworkgroup; + +extern uint16 samba_nb_type; /* Samba's NetBIOS type. */ + +/**************************************************************************** + Fail funtion when registering my netbios names. + **************************************************************************/ + +static void my_name_register_failed(struct subnet_record *subrec, + struct response_record *rrec, struct nmb_name *nmbname) +{ + DEBUG(0,("my_name_register_failed: Failed to register my name %s on subnet %s.\n", + namestr(nmbname), subrec->subnet_name)); +} + +/**************************************************************************** + Add my workgroup and my given names to the subnet lists. + Also add the magic Samba names. + **************************************************************************/ + +BOOL register_my_workgroup_and_names() +{ + struct subnet_record *subrec; + struct work_record *work; + int i; + + for(subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec)) + { + /* Create the workgroup on the subnet. */ + if((work = create_workgroup_on_subnet(subrec, myworkgroup, PERMANENT_TTL)) == NULL) + { + DEBUG(0,("register_my_workgroup_and_names: Failed to create my workgroup %s on subnet %s. \ +Exiting.\n", myworkgroup, subrec->subnet_name)); + return False; + } + + /* Each subnet entry, except for the remote_announce_broadcast subnet + and the wins_server_subnet has the magic Samba names. */ + add_samba_names_to_subnet(subrec); + + /* Register all our names including aliases. */ + for (i=0; my_netbios_names[i]; i++) + { + register_name(subrec, my_netbios_names[i],0x20,samba_nb_type, + NULL, + my_name_register_failed, NULL); + register_name(subrec, my_netbios_names[i],0x03,samba_nb_type, + NULL, + my_name_register_failed, NULL); + register_name(subrec, my_netbios_names[i],0x00,samba_nb_type, + NULL, + my_name_register_failed, NULL); + } + + /* Initiate election processing, register the workgroup names etc. */ + initiate_myworkgroup_startup(subrec, work); + } + + /* If we are not a WINS client, we still need to add the magic Samba + names and the netbios names to the unicast subnet directly. This is + to allow unicast node status requests and queries to still work + in a broadcast only environment. */ + + if(we_are_a_wins_client() == False) + { + add_samba_names_to_subnet(unicast_subnet); + + for (i=0; my_netbios_names[i]; i++) + { + add_name_to_subnet(unicast_subnet, my_netbios_names[i],0x20,samba_nb_type, PERMANENT_TTL, + SELF_NAME, 1, &FIRST_SUBNET->myip); + + add_name_to_subnet(unicast_subnet, my_netbios_names[i],0x3,samba_nb_type, PERMANENT_TTL, + SELF_NAME, 1, &FIRST_SUBNET->myip); + + add_name_to_subnet(unicast_subnet, my_netbios_names[i],0x0,samba_nb_type, PERMANENT_TTL, + SELF_NAME, 1, &FIRST_SUBNET->myip); + } + } + + return True; +} + +/**************************************************************************** + Remove all the names we registered. +**************************************************************************/ + +void release_my_names() +{ + struct subnet_record *subrec; + + for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec)) + { + struct name_record *namerec, *nextnamerec; + + for (namerec = subrec->namelist; namerec; namerec = nextnamerec) + { + nextnamerec = namerec->next; + if ((namerec->source == SELF_NAME) && !NAME_IS_DEREGISTERING(namerec)) + release_name(subrec, namerec, standard_success_release, + NULL, NULL); + } + } +} + +/******************************************************************* + Refresh our registered names. + ******************************************************************/ + +void refresh_my_names(time_t t) +{ + struct subnet_record *subrec; + + for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec)) + { + struct name_record *namerec; + + for (namerec = subrec->namelist; namerec; namerec = namerec->next) + { + /* Each SELF name has an individual time to be refreshed. */ + if ((namerec->source == SELF_NAME) && (namerec->refresh_time < t) && + (namerec->death_time != PERMANENT_TTL)) + { + /* We cheat here and pretend the refresh is going to be + successful & update the refresh times. This stops + multiple refresh calls being done. We actually + deal with refresh failure in the fail_fn. + */ + refresh_name(subrec, namerec, NULL, NULL, NULL); + namerec->death_time += lp_max_ttl(); + namerec->refresh_time += lp_max_ttl(); + } + } + } +} diff --git a/source3/nmbd/nmbd_namelistdb.c b/source3/nmbd/nmbd_namelistdb.c new file mode 100644 index 0000000000..dfd8a80baa --- /dev/null +++ b/source3/nmbd/nmbd_namelistdb.c @@ -0,0 +1,586 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NBT netbios routines and daemon - version 2 + Copyright (C) Andrew Tridgell 1994-1997 + Copyright (C) Luke Kenneth Casson Leighton 1994-1997 + Copyright (C) Jeremy Allison 1994-1997 + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "includes.h" + +extern int DEBUGLEVEL; + +extern pstring scope; +extern char **my_netbios_names; + +uint16 samba_nb_type = 0; /* samba's NetBIOS name type */ + + +/**************************************************************************** + Set Samba's NetBIOS name type. + ****************************************************************************/ + +void set_samba_nb_type(void) +{ + if (lp_wins_support() || (*lp_wins_server())) + samba_nb_type = NB_MFLAG; /* samba is a 'hybrid' node type */ + else + samba_nb_type = NB_BFLAG; /* samba is broadcast-only node type */ +} + +/**************************************************************************** + Returns 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()). + + **************************************************************************/ + +BOOL ms_browser_name(char *name, int type) +{ + return (strequal(name,MSBROWSE) && (type == 0x01)); +} + +/**************************************************************************** + Add a netbios name into a namelist. + **************************************************************************/ + +static void add_name_to_namelist(struct subnet_record *subrec, + struct name_record *namerec) +{ + struct name_record *namerec2; + + if (!subrec->namelist) + { + subrec->namelist = namerec; + namerec->prev = NULL; + namerec->next = NULL; + return; + } + + for (namerec2 = subrec->namelist; namerec2->next; namerec2 = namerec2->next) + ; + + namerec2->next = namerec; + namerec->next = NULL; + namerec->prev = namerec2; + namerec->subnet = subrec; + + subrec->namelist_changed = True; +} + +/**************************************************************************** + Remove a name from the namelist. + **************************************************************************/ + +void remove_name_from_namelist(struct subnet_record *subrec, + struct name_record *namerec) +{ + if (namerec->next) + namerec->next->prev = namerec->prev; + if (namerec->prev) + namerec->prev->next = namerec->next; + + if(namerec == subrec->namelist) + subrec->namelist = namerec->next; + + if(namerec->ip != NULL) + free((char *)namerec->ip); + free((char *)namerec); + + subrec->namelist_changed = True; +} + + +/**************************************************************************** + Find a name in a subnet. + **************************************************************************/ + +struct name_record *find_name_on_subnet(struct subnet_record *subrec, + struct nmb_name *nmbname, BOOL self_only) +{ + struct name_record *namerec = subrec->namelist; + struct name_record *name_ret; + + for (name_ret = namerec; name_ret; name_ret = name_ret->next) + { + if (nmb_name_equal(&name_ret->name, nmbname)) + { + /* Self names only - these include permanent names. */ + if (self_only && (name_ret->source != SELF_NAME) && + (name_ret->source != PERMANENT_NAME) ) + { + continue; + } + DEBUG(9,("find_name_on_subnet: on subnet %s - found name %s source=%d\n", + subrec->subnet_name, namestr(nmbname), name_ret->source)); + return name_ret; + } + } + DEBUG(9,("find_name_on_subnet: on subnet %s - name %s NOT FOUND\n", + subrec->subnet_name, namestr(nmbname))); + return NULL; +} + +/**************************************************************************** + Find a name over all known broadcast subnets. +**************************************************************************/ + +struct name_record *find_name_for_remote_broadcast_subnet( struct nmb_name *nmbname, + BOOL self_only) +{ + struct subnet_record *subrec; + struct name_record *namerec = NULL; + + for( subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) + { + if((namerec = find_name_on_subnet(subrec, nmbname, self_only))!= NULL) + break; + } + + return namerec; +} + +/**************************************************************************** + Update the ttl of an entry in a subnet name list. + ****************************************************************************/ + +void update_name_ttl(struct name_record *namerec, int ttl) +{ + time_t time_now = time(NULL); + + if(namerec->death_time != PERMANENT_TTL) + namerec->death_time = time_now + ttl; + + namerec->refresh_time = time_now + (ttl/2); + + namerec->subnet->namelist_changed = True; +} + +/**************************************************************************** + Add an entry to a subnet name list. + ****************************************************************************/ + +struct name_record *add_name_to_subnet(struct subnet_record *subrec, + char *name, int type, uint16 nb_flags, int ttl, + enum name_source source, int num_ips, struct in_addr *iplist) +{ + struct name_record *namerec; + time_t time_now = time(NULL); + + if((namerec = (struct name_record *)malloc(sizeof(*namerec))) == NULL) + { + DEBUG(0,("add_name_to_subnet: malloc fail.\n")); + return NULL; + } + + bzero((char *)namerec,sizeof(*namerec)); + + namerec->subnet = subrec; + + namerec->num_ips = num_ips; + namerec->ip = (struct in_addr *)malloc(sizeof(struct in_addr) * namerec->num_ips); + if (!namerec->ip) + { + DEBUG(0,("add_name_to_subnet: malloc fail when creating ip_flgs.\n")); + free((char *)namerec); + return NULL; + } + + bzero((char *)namerec->ip, sizeof(struct in_addr) * namerec->num_ips); + + memcpy(&namerec->ip[0], iplist, num_ips * sizeof(struct in_addr)); + + make_nmb_name(&namerec->name,name,type,scope); + + /* Setup the death_time and refresh_time. */ + if(ttl == PERMANENT_TTL) + namerec->death_time = PERMANENT_TTL; + else + namerec->death_time = time_now + ttl; + + namerec->refresh_time = time_now + (ttl/2); + + /* Enter the name as active. */ + namerec->nb_flags = nb_flags | NB_ACTIVE; + + /* If it's our primary name, flag it as so. */ + if(strequal(my_netbios_names[0],name)) + namerec->nb_flags |= NB_PERM; + + namerec->source = source; + + add_name_to_namelist(subrec,namerec); + + DEBUG(3,("add_name_to_subnet: Added netbios name %s with first IP %s ttl=%d nb_flags=%2x to subnet %s\n", + namestr(&namerec->name),inet_ntoa(*iplist),ttl,(unsigned int)nb_flags, + subrec->subnet_name)); + + subrec->namelist_changed = True; + + return(namerec); +} + +/******************************************************************* + Utility function automatically called when a name refresh or register + succeeds. By definition this is a SELF_NAME (or we wouldn't be registering + it). + ******************************************************************/ + +void standard_success_register(struct subnet_record *subrec, + struct userdata_struct *userdata, + struct nmb_name *nmbname, uint16 nb_flags, int ttl, + struct in_addr registered_ip) +{ + struct name_record *namerec = find_name_on_subnet(subrec, nmbname, FIND_SELF_NAME); + + if(namerec == NULL) + add_name_to_subnet(subrec, nmbname->name, nmbname->name_type, + nb_flags, ttl, SELF_NAME, 1, ®istered_ip); + else + update_name_ttl(namerec, ttl); +} + +/******************************************************************* + Utility function automatically called when a name refresh or register + fails. + ******************************************************************/ + +void standard_fail_register(struct subnet_record *subrec, + struct response_record *rrec, struct nmb_name *nmbname) +{ + struct name_record *namerec = find_name_on_subnet(subrec, nmbname, FIND_SELF_NAME); + + DEBUG(0,("standard_fail_register: Failed to register/refresh name %s on subnet %s\n", + namestr(nmbname), subrec->subnet_name)); + + /* Remove the name from the subnet. */ + if(namerec) + remove_name_from_namelist(subrec, namerec); +} + +/******************************************************************* + Utility function to remove an IP address from a name record. + ******************************************************************/ + +static void remove_nth_ip_in_record( struct name_record *namerec, int ind) +{ + if(ind != namerec->num_ips) + memmove( (char *)(&namerec->ip[ind]), (char *)(&namerec->ip[ind+1]), + ( namerec->num_ips - ind - 1) * sizeof(struct in_addr)); + + namerec->num_ips--; + namerec->subnet->namelist_changed = True; +} + +/******************************************************************* + Utility function to check if an IP address exists in a name record. + ******************************************************************/ + +BOOL find_ip_in_name_record(struct name_record *namerec, struct in_addr ip) +{ + int i; + + for(i = 0; i < namerec->num_ips; i++) + if(ip_equal( namerec->ip[i], ip)) + return True; + + return False; +} + +/******************************************************************* + Utility function to add an IP address to a name record. + ******************************************************************/ + +void add_ip_to_name_record(struct name_record *namerec, struct in_addr new_ip) +{ + struct in_addr *new_list; + + /* Don't add one we already have. */ + if(find_ip_in_name_record( namerec, new_ip)) + return; + + if((new_list = (struct in_addr *)malloc( (namerec->num_ips + 1)*sizeof(struct in_addr)) )== NULL) + { + DEBUG(0,("add_ip_to_name_record: Malloc fail !\n")); + return; + } + + memcpy((char *)new_list, (char *)namerec->ip, namerec->num_ips *sizeof(struct in_addr)); + new_list[namerec->num_ips] = new_ip; + + free((char *)namerec->ip); + namerec->ip = new_list; + namerec->num_ips += 1; + + namerec->subnet->namelist_changed = True; +} + +/******************************************************************* + Utility function to remove an IP address from a name record. + ******************************************************************/ + +void remove_ip_from_name_record( struct name_record *namerec, struct in_addr remove_ip) +{ + /* Try and find the requested ip address - remove it. */ + int i; + int orig_num = namerec->num_ips; + + for(i = 0; i < orig_num; i++) + if( ip_equal( remove_ip, namerec->ip[i]) ) + { + remove_nth_ip_in_record( namerec, i); + break; + } +} + +/******************************************************************* + Utility function that release_name callers can plug into as the + success function when a name release is successful. Used to save + duplication of success_function code. + ******************************************************************/ + +void standard_success_release(struct subnet_record *subrec, + struct userdata_struct *userdata, + struct nmb_name *nmbname, struct in_addr released_ip) +{ + struct name_record *namerec = find_name_on_subnet(subrec, nmbname, FIND_ANY_NAME); + + if(namerec == NULL) + { + DEBUG(0,("standard_success_release: Name release for name %s IP %s on subnet %s. Name \ +was not found on subnet.\n", namestr(nmbname), inet_ntoa(released_ip), subrec->subnet_name)); + return; + } + else + { + int orig_num = namerec->num_ips; + + remove_ip_from_name_record( namerec, released_ip); + + if(namerec->num_ips == orig_num) + DEBUG(0,("standard_success_release: Name release for name %s IP %s on subnet %s. This ip \ +is not known for this name.\n", namestr(nmbname), inet_ntoa(released_ip), subrec->subnet_name )); + } + + if (namerec->num_ips == 0) + remove_name_from_namelist(subrec, namerec); +} + +/******************************************************************* + Expires old names in a subnet namelist. + ******************************************************************/ + +void expire_names_on_subnet(struct subnet_record *subrec, time_t t) +{ + struct name_record *namerec; + struct name_record *next_namerec; + + for (namerec = subrec->namelist; namerec; namerec = next_namerec) + { + next_namerec = namerec->next; + if ((namerec->death_time != PERMANENT_TTL) && (namerec->death_time < t)) + { + if (namerec->source == SELF_NAME) + { + DEBUG(3,("expire_names_on_subnet: Subnet %s not expiring SELF name %s\n", + subrec->subnet_name, namestr(&namerec->name))); + namerec->death_time += 300; + namerec->subnet->namelist_changed = True; + continue; + } + DEBUG(3,("expire_names_on_subnet: Subnet %s - removing expired name %s\n", + subrec->subnet_name, namestr(&namerec->name))); + + remove_name_from_namelist(subrec, namerec); + } + } +} + +/******************************************************************* + Expires old names in all subnet namelists. + ******************************************************************/ + +void expire_names(time_t t) +{ + struct subnet_record *subrec; + + for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec)) + { + expire_names_on_subnet(subrec, t); + } +} + +/**************************************************************************** + Add the magic samba names, useful for finding samba servers. + These go directly into the name list for a particular subnet, + without going through the normal registration process. + When adding them to the unicast subnet, add them as a list of + all broadcast subnet IP addresses. +**************************************************************************/ + +void add_samba_names_to_subnet(struct subnet_record *subrec) +{ + struct in_addr *iplist = &subrec->myip; + int num_ips = 1; + + /* These names are added permanently (ttl of zero) and will NOT be + refreshed. */ + + if((subrec == unicast_subnet) || (subrec == wins_server_subnet)) + { + struct subnet_record *bcast_subrecs; + int i; + /* Create an IP list containing all our known subnets. */ + + num_ips = iface_count(); + if((iplist = (struct in_addr *)malloc( num_ips * sizeof(struct in_addr) )) == NULL) + { + DEBUG(0,("add_samba_names_to_subnet: Malloc fail !\n")); + return; + } + + for(bcast_subrecs = FIRST_SUBNET, i = 0; bcast_subrecs; + bcast_subrecs = NEXT_SUBNET_EXCLUDING_UNICAST(bcast_subrecs), i++) + iplist[i] = bcast_subrecs->myip; + + } + + add_name_to_subnet(subrec,"*",0x0,samba_nb_type, PERMANENT_TTL, + PERMANENT_NAME, num_ips, iplist); + add_name_to_subnet(subrec,"*",0x20,samba_nb_type,PERMANENT_TTL, + PERMANENT_NAME, num_ips, iplist); + add_name_to_subnet(subrec,"__SAMBA__",0x20,samba_nb_type,PERMANENT_TTL, + PERMANENT_NAME, num_ips, iplist); + add_name_to_subnet(subrec,"__SAMBA__",0x00,samba_nb_type,PERMANENT_TTL, + PERMANENT_NAME, num_ips, iplist); + + if(iplist != &subrec->myip) + free((char *)iplist); +} + +/**************************************************************************** + Dump the contents of the namelists on all the subnets (including unicast) + into a file. Initiated by SIGHUP - used to debug the state of the namelists. +**************************************************************************/ + +static void dump_subnet_namelist( struct subnet_record *subrec, FILE *fp) +{ + struct name_record *namerec; + char *src_type; + struct tm *tm; + int i; + + fprintf(fp, "Subnet %s\n----------------------\n", subrec->subnet_name); + for (namerec = subrec->namelist; namerec; namerec = namerec->next) + { + fprintf(fp,"\tName = %s\t", namestr(&namerec->name)); + switch(namerec->source) + { + case LMHOSTS_NAME: + src_type = "LMHOSTS_NAME"; + break; + case WINS_PROXY_NAME: + src_type = "WINS_PROXY_NAME"; + break; + case REGISTER_NAME: + src_type = "REGISTER_NAME"; + break; + case SELF_NAME: + src_type = "SELF_NAME"; + break; + case DNS_NAME: + src_type = "DNS_NAME"; + break; + case DNSFAIL_NAME: + src_type = "DNSFAIL_NAME"; + break; + case PERMANENT_NAME: + src_type = "PERMANENT_NAME"; + break; + default: + src_type = "unknown!"; + break; + } + fprintf(fp, "Source = %s\nb_flags = %x\t", src_type, namerec->nb_flags); + + if(namerec->death_time != PERMANENT_TTL) + { + tm = LocalTime(&namerec->death_time); + fprintf(fp, "death_time = %s\t", asctime(tm)); + } + else + fprintf(fp, "death_time = PERMANENT\t"); + + if(namerec->refresh_time != PERMANENT_TTL) + { + tm = LocalTime(&namerec->refresh_time); + fprintf(fp, "refresh_time = %s\n", asctime(tm)); + } + else + fprintf(fp, "refresh_time = PERMANENT\n"); + + fprintf(fp, "\t\tnumber of IPS = %d", namerec->num_ips); + for(i = 0; i < namerec->num_ips; i++) + fprintf(fp, "\t%s", inet_ntoa(namerec->ip[i])); + + fprintf(fp, "\n\n"); + } +} + +/**************************************************************************** + Dump the contents of the namelists on all the subnets (including unicast) + into a file. Initiated by SIGHUP - used to debug the state of the namelists. +**************************************************************************/ + +void dump_all_namelists() +{ + fstring fname; + FILE *fp; + struct subnet_record *subrec; + + pstrcpy(fname,lp_lockdir()); + trim_string(fname,NULL,"/"); + strcat(fname,"/"); + strcat(fname,"namelist.debug"); + + fp = fopen(fname,"w"); + + if (!fp) + { + DEBUG(0,("dump_all_namelists: Can't open file %s. Error was %s\n", + fname,strerror(errno))); + return; + } + + for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec)) + dump_subnet_namelist( subrec, fp); + + if(!we_are_a_wins_client()) + dump_subnet_namelist(unicast_subnet, fp); + + if(remote_broadcast_subnet->namelist != NULL) + dump_subnet_namelist(remote_broadcast_subnet, fp); + + if(wins_server_subnet != NULL) + dump_subnet_namelist( wins_server_subnet, fp); + fclose(fp); +} diff --git a/source3/nmbd/nmbd_namequery.c b/source3/nmbd/nmbd_namequery.c new file mode 100644 index 0000000000..5d98935fec --- /dev/null +++ b/source3/nmbd/nmbd_namequery.c @@ -0,0 +1,234 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NBT netbios routines and daemon - version 2 + Copyright (C) Andrew Tridgell 1994-1997 + Copyright (C) Luke Kenneth Casson Leighton 1994-1997 + Copyright (C) Jeremy Allison 1994-1997 + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "includes.h" + +extern int DEBUGLEVEL; + +extern pstring scope; + +/**************************************************************************** + Deal with a response packet when querying a name. +****************************************************************************/ + +static void query_name_response(struct subnet_record *subrec, + struct response_record *rrec, struct packet_struct *p) +{ + struct nmb_packet *nmb = &p->packet.nmb; + BOOL success = False; + struct nmb_name *question_name = &rrec->packet->packet.nmb.question.question_name; + struct in_addr answer_ip; + + /* Ensure we don't retry the query but leave the response record cleanup + to the timeout code. We may get more answer responses in which case + we should mark the name in conflict.. */ + rrec->repeat_count = 0; + + if(rrec->num_msgs == 1) + { + /* This is the first response. */ + + if(nmb->header.opcode == NMB_WACK_OPCODE) + { + /* WINS server is telling us to wait. Pretend we didn't get + the response but don't send out any more query requests. */ + + DEBUG(5,("query_name_response: WACK from WINS server %s in querying \ +name %s on subnet %s.\n", inet_ntoa(p->ip), namestr(question_name), subrec->subnet_name)); + + rrec->repeat_count = 0; + /* How long we should wait for. */ + rrec->repeat_time = p->timestamp + nmb->answers->ttl; + rrec->num_msgs--; + return; + } + else if(nmb->header.rcode != 0) + { + success = False; + + DEBUG(5,("query_name_response: On subnet %s - negative response \ +from IP %s for name %s. Error code was %d.\n", subrec->subnet_name, inet_ntoa(p->ip), + namestr(question_name), nmb->header.rcode)); + } + else + { + success = True; + + putip((char *)&answer_ip,&nmb->answers->rdata[2]); + DEBUG(5,("query_name_response: On subnet %s - positive response from IP %s\ +for name %s. IP of that name is %s\n", subrec->subnet_name, inet_ntoa(p->ip), + namestr(question_name), inet_ntoa(answer_ip))); + + /* Interestingly, we could add these names to our namelists, and + change nmbd to a model that checked its own name cache first, + before sending out a query. This is a task for another day, though. + */ + } + } + else if( rrec->num_msgs > 1) + { + DEBUG(0,("query_name_response: Multiple (%d) responses received for a query on \ +subnet %s for name %s. This response was from IP %s\n", + rrec->num_msgs, subrec->subnet_name, namestr(question_name), + inet_ntoa(rrec->packet->ip) )); + + /* We have already called the success or fail function, so we + don't call again here. Leave the response record around in + case we get more responses. */ + + return; + } + + if(success && rrec->success_fn) + (*rrec->success_fn)(subrec, rrec->userdata, question_name, answer_ip, nmb->answers); + else if( rrec->fail_fn) + (*rrec->fail_fn)(subrec, rrec, question_name, nmb->header.rcode); + +} + +/**************************************************************************** + Deal with a timeout when querying a name. +****************************************************************************/ + +static void query_name_timeout_response(struct subnet_record *subrec, + struct response_record *rrec) +{ + struct nmb_packet *sent_nmb = &rrec->packet->packet.nmb; + /* We can only fail here, never succeed. */ + BOOL failed = True; + struct nmb_name *question_name = &sent_nmb->question.question_name; + + if(rrec->num_msgs != 0) + { + /* We got at least one response, and have called the success/fail + function already. */ + + failed = False; + } + + if(failed) + { + DEBUG(5,("query_name_timeout_response: No response to querying name %s on subnet %s.\n", + namestr(question_name), subrec->subnet_name)); + + if(rrec->fail_fn) + (*rrec->fail_fn)(subrec, rrec, question_name, 0); + } + + remove_response_record(subrec, rrec); +} + +/**************************************************************************** + Lookup a name on our local namelists. We check the lmhosts file first. If the + name is not there we look for the name on the given subnet. +****************************************************************************/ + +static BOOL query_local_namelists(struct subnet_record *subrec, struct nmb_name *nmbname, + struct name_record **namerecp) +{ + struct name_record *namerec; + + *namerecp = NULL; + + if(find_name_in_lmhosts(nmbname, namerecp)) + return True; + + if((namerec = find_name_on_subnet(subrec, nmbname, FIND_ANY_NAME))==NULL) + return False; + + if(NAME_IS_ACTIVE(namerec) && ((namerec->source == SELF_NAME) || + (namerec->source == LMHOSTS_NAME)) ) + { + *namerecp = namerec; + return True; + } + return False; +} + +/**************************************************************************** + Try and query for a name. +****************************************************************************/ + +BOOL query_name(struct subnet_record *subrec, char *name, int type, + query_name_success_function success_fn, + query_name_fail_function fail_fn, + struct userdata_struct *userdata) +{ + struct nmb_name nmbname; + struct name_record *namerec; + + make_nmb_name(&nmbname, name, type, scope); + + /* + * We need to check our local namelists first. + * It may be an magic name, lmhosts name or just + * a name we have registered. + */ + + if(query_local_namelists(subrec, &nmbname, &namerec) == True) + { + struct res_rec rrec; + int i; + + bzero((char *)&rrec, sizeof(struct res_rec)); + + /* Fake up the needed res_rec just in case it's used. */ + rrec.rr_name = nmbname; + rrec.rr_type = RR_TYPE_NB; + rrec.rr_class = RR_CLASS_IN; + rrec.ttl = PERMANENT_TTL; + rrec.rdlength = namerec->num_ips * 6; + if(rrec.rdlength > MAX_DGRAM_SIZE) + { + DEBUG(0,("query_name: nmbd internal error - there are %d ip addresses for name %s.\n", + namerec->num_ips, namestr(&nmbname) )); + return False; + } + + for( i = 0; i < namerec->num_ips; i++) + { + set_nb_flags( &rrec.rdata[i*6], namerec->nb_flags ); + putip( &rrec.rdata[(i*6) + 2], (char *)&namerec->ip[i]); + } + + /* Call the success function directly. */ + if(success_fn) + (*success_fn)(subrec, userdata, &nmbname, namerec->ip[0], &rrec); + return False; + } + + if(queue_query_name( subrec, + query_name_response, + query_name_timeout_response, + success_fn, + fail_fn, + userdata, + &nmbname) == NULL) + { + DEBUG(0,("query_name: Failed to send packet trying to query name %s\n", + namestr(&nmbname))); + return True; + } + return False; +} diff --git a/source3/nmbd/nmbd_nameregister.c b/source3/nmbd/nmbd_nameregister.c new file mode 100644 index 0000000000..603daaa531 --- /dev/null +++ b/source3/nmbd/nmbd_nameregister.c @@ -0,0 +1,390 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NBT netbios routines and daemon - version 2 + Copyright (C) Andrew Tridgell 1994-1997 + Copyright (C) Luke Kenneth Casson Leighton 1994-1997 + Copyright (C) Jeremy Allison 1994-1997 + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "includes.h" + +extern int DEBUGLEVEL; + +extern pstring scope; +extern fstring myworkgroup; + +/**************************************************************************** + Deal with a response packet when registering one of our names. +****************************************************************************/ + +static void register_name_response(struct subnet_record *subrec, + struct response_record *rrec, struct packet_struct *p) +{ + /* + * If we are registering broadcast, then getting a response is an + * error - we do not have the name. If we are registering unicast, + * then we expect to get a response. + */ + + struct nmb_packet *nmb = &p->packet.nmb; + BOOL bcast = nmb->header.nm_flags.bcast; + BOOL success = True; + struct nmb_name *question_name = &rrec->packet->packet.nmb.question.question_name; + struct nmb_name *answer_name = &nmb->answers->rr_name; + int ttl; + uint16 nb_flags; + struct in_addr registered_ip; + + /* Sanity check. Ensure that the answer name in the incoming packet is the + same as the requested name in the outgoing packet. */ + + if(!nmb_name_equal(question_name, answer_name)) + { + DEBUG(0,("register_name_response: Answer name %s differs from question \ +name %s.\n", namestr(answer_name), namestr(question_name))); + return; + } + + if(bcast) + { + /* + * Special hack to cope with old Samba nmbd's. + * Earlier versions of Samba (up to 1.9.16p11) respond + * to a broadcast name registration of WORKGROUP<1b> when + * they should not. Hence, until these versions are gone, + * we should treat such errors as success for this particular + * case only. jallison@whistle.com. + */ + +#if 1 /* OLD_SAMBA_SERVER_HACK */ + if((nmb->header.rcode == ACT_ERR) && strequal(myworkgroup, answer_name->name) && + (answer_name->name_type == 0x1b)) + { + /* Pretend we did not get this. */ + rrec->num_msgs--; + + DEBUG(5,("register_name_response: Ignoring broadcast response to \ +registration of name %s due to old Samba server bug.\n", namestr(answer_name))); + return; + } +#endif /* OLD_SAMBA_SERVER_HACK */ + + /* Someone else has the name. Log the problem. */ + DEBUG(1,("register_name_response: Failed to register \ +name %s on subnet %s via broadcast. Error code was %d. Reject came from IP %s\n", + namestr(answer_name), + subrec->subnet_name, nmb->header.rcode, inet_ntoa(p->ip))); + success = False; + } + else + { + /* Unicast - check to see if the response allows us to have the name. */ + if(nmb->header.rcode != 0) + { + /* Error code - we didn't get the name. */ + success = False; + + DEBUG(0,("register_name_response: server at IP %s rejected our \ +name registration of %s with error code %d.\n", inet_ntoa(p->ip), + namestr(answer_name), nmb->header.rcode)); + + } + else if(nmb->header.opcode == NMB_WACK_OPCODE) + { + /* WINS server is telling us to wait. Pretend we didn't get + the response but don't send out any more register requests. */ + + DEBUG(5,("register_name_response: WACK from WINS server %s in registering \ +name %s on subnet %s.\n", inet_ntoa(p->ip), namestr(answer_name), subrec->subnet_name)); + + rrec->repeat_count = 0; + /* How long we should wait for. */ + rrec->repeat_time = p->timestamp + nmb->answers->ttl; + rrec->num_msgs--; + return; + } + else + { + success = True; + /* Get the data we need to pass to the success function. */ + nb_flags = get_nb_flags(nmb->answers->rdata); + putip((char*)®istered_ip,&nmb->answers->rdata[2]); + ttl = nmb->answers->ttl; + } + } + + DEBUG(5,("register_name_response: %s in registering name %s on subnet %s.\n", + success ? "success" : "failure", namestr(answer_name), subrec->subnet_name)); + + if(success) + { + /* Enter the registered name into the subnet name database before calling + the success function. */ + standard_success_register(subrec, rrec->userdata, answer_name, nb_flags, ttl, registered_ip); + if( rrec->success_fn) + (*rrec->success_fn)(subrec, rrec->userdata, answer_name, nb_flags, ttl, registered_ip); + } + else + { + if( rrec->fail_fn) + (*rrec->fail_fn)(subrec, rrec, question_name); + /* Remove the name. */ + standard_fail_register( subrec, rrec, question_name); + } + + /* Ensure we don't retry. */ + remove_response_record(subrec, rrec); +} + +/**************************************************************************** + Deal with a timeout when registering one of our names. +****************************************************************************/ + +static void register_name_timeout_response(struct subnet_record *subrec, + struct response_record *rrec) +{ + /* + * If we are registering unicast, then NOT getting a response is an + * error - we do not have the name. If we are registering broadcast, + * then we don't expect to get a response. + */ + + struct nmb_packet *sent_nmb = &rrec->packet->packet.nmb; + BOOL bcast = sent_nmb->header.nm_flags.bcast; + BOOL success = False; + struct nmb_name *question_name = &sent_nmb->question.question_name; + uint16 nb_flags; + int ttl; + struct in_addr registered_ip; + + if(bcast) + { + if(rrec->num_msgs == 0) + { + /* Not receiving a message is success for broadcast registration. */ + success = True; + + /* Pull the success values from the original request packet. */ + nb_flags = get_nb_flags(sent_nmb->additional->rdata); + ttl = sent_nmb->additional->ttl; + putip(®istered_ip,&sent_nmb->additional->rdata[2]); + } + } + else + { + /* Unicast - if no responses then it's an error. */ + if(rrec->num_msgs == 0) + { + DEBUG(2,("register_name_timeout_response: WINS server at address %s is not \ +responding.\n", inet_ntoa(rrec->packet->ip))); + + /* Keep trying to contact the WINS server periodically. This allows + us to work correctly if the WINS server is down temporarily when + we come up. */ + + /* Reset the number of attempts to zero and double the interval between + retries. Max out at 5 minutes. */ + rrec->repeat_count = 3; + rrec->repeat_interval *= 2; + if(rrec->repeat_interval > (5 * 60)) + rrec->repeat_interval = (5 * 60); + rrec->repeat_time = time(NULL) + rrec->repeat_interval; + + DEBUG(5,("register_name_timeout_response: increasing WINS timeout to %d seconds.\n", + rrec->repeat_interval)); + return; /* Don't remove the response record. */ + } + } + + DEBUG(5,("register_name_timeout_response: %s in registering name %s on subnet %s.\n", + success ? "success" : "failure", namestr(question_name), subrec->subnet_name)); + if(success) + { + /* Enter the registered name into the subnet name database before calling + the success function. */ + standard_success_register(subrec, rrec->userdata, question_name, nb_flags, ttl, registered_ip); + if( rrec->success_fn) + (*rrec->success_fn)(subrec, rrec->userdata, question_name, nb_flags, ttl, registered_ip); + } + else + { + if( rrec->fail_fn) + (*rrec->fail_fn)(subrec, rrec, question_name); + /* Remove the name. */ + standard_fail_register( subrec, rrec, question_name); + } + + /* Ensure we don't retry. */ + remove_response_record(subrec, rrec); +} + +/**************************************************************************** + Try and register one of our names on the unicast subnet - multihomed. +****************************************************************************/ + +static BOOL multihomed_register_name( struct nmb_name *nmbname, uint16 nb_flags, + register_name_success_function success_fn, + register_name_fail_function fail_fn, + struct userdata_struct *userdata) +{ + /* + If we are adding a group name, we just send multiple + register name packets to the WINS server (this is an + internet group name. + + If we are adding a unique name, We need first to add + our names to the unicast subnet namelist. This is + because when a WINS server receives a multihomed + registration request, the first thing it does is to + send a name query to the registering machine, to see + if it has put the name in it's local namelist. + We need the name there so the query response code in + nmbd_incomingrequests.c will find it. + + We are adding this name prematurely (we don't really + have it yet), but as this is on the unicast subnet + only we will get away with this (only the WINS server + will ever query names from us on this subnet). + */ + + int num_ips=0; + int i; + struct in_addr *ip_list = NULL; + struct subnet_record *subrec; + + for(subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec) ) + num_ips++; + + if((ip_list = (struct in_addr *)malloc(num_ips * sizeof(struct in_addr)))==NULL) + { + DEBUG(0,("multihomed_register_name: malloc fail !\n")); + return True; + } + + for(subrec = FIRST_SUBNET, i = 0; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec), i++ ) + ip_list[i] = subrec->myip; + + add_name_to_subnet(unicast_subnet, nmbname->name, nmbname->name_type, + nb_flags, lp_max_ttl(), SELF_NAME, num_ips, ip_list); + + free((char *)ip_list); + + /* Now try and register the name, num_ips times. On the last time use + the given success and fail functions. */ + + for( i = 0; i < num_ips; i++) + { + if(queue_register_multihomed_name( unicast_subnet, + register_name_response, + register_name_timeout_response, + (i == num_ips - 1) ? success_fn : NULL, + (i == num_ips - 1) ? fail_fn : NULL, + (i == num_ips - 1) ? userdata : NULL, + nmbname, + nb_flags, + ip_list[i]) == NULL) + { + DEBUG(0,("multihomed_register_name: Failed to send packet trying to \ +register name %s IP %s\n", namestr(nmbname), inet_ntoa(ip_list[i]) )); + return True; + } + } + + return False; +} + +/**************************************************************************** + Try and register one of our names. +****************************************************************************/ + +BOOL register_name(struct subnet_record *subrec, + char *name, int type, uint16 nb_flags, + register_name_success_function success_fn, + register_name_fail_function fail_fn, + struct userdata_struct *userdata) +{ + struct nmb_name nmbname; + + make_nmb_name(&nmbname, name, type, scope); + + /* Always set the NB_ACTIVE flag on the name we are + registering. Doesn't make sense without it. + */ + + nb_flags |= NB_ACTIVE; + + /* If this is the unicast subnet, and we are a multi-homed + host, then register a multi-homed name. */ + + if( (subrec == unicast_subnet) && we_are_multihomed()) + return multihomed_register_name(&nmbname, nb_flags, + success_fn, fail_fn, + userdata); + + if(queue_register_name( subrec, + register_name_response, + register_name_timeout_response, + success_fn, + fail_fn, + userdata, + &nmbname, + nb_flags) == NULL) + { + DEBUG(0,("register_name: Failed to send packet trying to register name %s\n", + namestr(&nmbname))); + return True; + } + return False; +} + +/**************************************************************************** + Try and refresh one of our names. +****************************************************************************/ + +BOOL refresh_name(struct subnet_record *subrec, struct name_record *namerec, + refresh_name_success_function success_fn, + refresh_name_fail_function fail_fn, + struct userdata_struct *userdata) +{ + int i; + + /* + * Go through and refresh the name for all known ip addresses. + * Only call the success/fail function on the last one (it should + * only be done once). + */ + + for( i = 0; i < namerec->num_ips; i++) + { + if(queue_refresh_name( subrec, + register_name_response, + register_name_timeout_response, + (i == (namerec->num_ips - 1)) ? success_fn : NULL, + (i == (namerec->num_ips - 1)) ? fail_fn : NULL, + (i == (namerec->num_ips - 1)) ? userdata : NULL, + namerec, + namerec->ip[i]) == NULL) + { + DEBUG(0,("refresh_name: Failed to send packet trying to refresh name %s\n", + namestr(&namerec->name))); + return True; + } + } + return False; +} diff --git a/source3/nmbd/nmbd_namerelease.c b/source3/nmbd/nmbd_namerelease.c new file mode 100644 index 0000000000..8632dd7655 --- /dev/null +++ b/source3/nmbd/nmbd_namerelease.c @@ -0,0 +1,238 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NBT netbios routines and daemon - version 2 + Copyright (C) Andrew Tridgell 1994-1997 + Copyright (C) Luke Kenneth Casson Leighton 1994-1997 + Copyright (C) Jeremy Allison 1994-1997 + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "includes.h" + +extern int DEBUGLEVEL; + +extern pstring scope; + +/**************************************************************************** + Deal with a response packet when releasing one of our names. +****************************************************************************/ + +static void release_name_response(struct subnet_record *subrec, + struct response_record *rrec, struct packet_struct *p) +{ + /* + * If we are releasing broadcast, then getting a response is an + * error. If we are releasing unicast, then we expect to get a response. + */ + + struct nmb_packet *nmb = &p->packet.nmb; + BOOL bcast = nmb->header.nm_flags.bcast; + BOOL success = True; + struct nmb_name *question_name = &rrec->packet->packet.nmb.question.question_name; + struct nmb_name *answer_name = &nmb->answers->rr_name; + struct in_addr released_ip; + + /* Sanity check. Ensure that the answer name in the incoming packet is the + same as the requested name in the outgoing packet. */ + + if(!nmb_name_equal(question_name, answer_name)) + { + DEBUG(0,("release_name_response: Answer name %s differs from question \ +name %s.\n", namestr(answer_name), namestr(question_name))); + return; + } + + if(bcast) + { + /* Someone sent a response. This shouldn't happen/ */ + DEBUG(1,("release_name_response: A response for releasing name %s was received on a\ +broadcast subnet %s. This should not happen !\n", namestr(answer_name), subrec->subnet_name)); + return; + } + else + { + /* Unicast - check to see if the response allows us to release the name. */ + if(nmb->header.rcode != 0) + { + /* Error code - we were told not to release the name ! What now ! */ + success = False; + + DEBUG(0,("release_name_response: WINS server at IP %s rejected our \ +name release of name %s with error code %d.\n", inet_ntoa(p->ip), + namestr(answer_name), nmb->header.rcode)); + + } + else if(nmb->header.opcode == NMB_WACK_OPCODE) + { + /* WINS server is telling us to wait. Pretend we didn't get + the response but don't send out any more release requests. */ + + DEBUG(5,("release_name_response: WACK from WINS server %s in releasing \ +name %s on subnet %s.\n", inet_ntoa(p->ip), namestr(answer_name), subrec->subnet_name)); + + rrec->repeat_count = 0; + /* How long we should wait for. */ + rrec->repeat_time = p->timestamp + nmb->answers->ttl; + rrec->num_msgs--; + return; + } + } + + DEBUG(5,("release_name_response: %s in releasing name %s on subnet %s.\n", + success ? "success" : "failure", namestr(answer_name), subrec->subnet_name)); + + if(success) + { + putip((char*)&released_ip ,&nmb->answers->rdata[2]); + + if(rrec->success_fn) + (*rrec->success_fn)(subrec, rrec->userdata, answer_name, released_ip); + standard_success_release( subrec, rrec->userdata, answer_name, released_ip); + } + else + { + /* We have no standard_fail_release - maybe we should add one ? */ + if(rrec->fail_fn) + (*rrec->fail_fn)(subrec, rrec, answer_name); + } + + remove_response_record(subrec, rrec); +} + +/**************************************************************************** + Deal with a timeout when releasing one of our names. +****************************************************************************/ + +static void release_name_timeout_response(struct subnet_record *subrec, + struct response_record *rrec) +{ + /* + * If we are releasing unicast, then NOT getting a response is an + * error - we could not release the name. If we are releasing broadcast, + * then we don't expect to get a response. + */ + + struct nmb_packet *sent_nmb = &rrec->packet->packet.nmb; + BOOL bcast = sent_nmb->header.nm_flags.bcast; + BOOL success = False; + struct nmb_name *question_name = &sent_nmb->question.question_name; + struct in_addr released_ip; + + if(bcast) + { + if(rrec->num_msgs == 0) + { + /* Not receiving a message is success for broadcast release. */ + success = True; + + /* Get the ip address we were trying to release. */ + putip((char*)&released_ip ,&sent_nmb->additional->rdata[2]); + } + } + else + { + /* Unicast - if no responses then it's an error. */ + if(rrec->num_msgs == 0) + { + DEBUG(2,("release_name_timeout_response: WINS server at address %s is not \ +responding.\n", inet_ntoa(rrec->packet->ip))); + + /* Keep trying to contact the WINS server periodically. This allows + us to work correctly if the WINS server is down temporarily when + we want to delete the name. */ + + /* Reset the number of attempts to zero and double the interval between + retries. Max out at 5 minutes. */ + rrec->repeat_count = 3; + rrec->repeat_interval *= 2; + if(rrec->repeat_interval > (5 * 60)) + rrec->repeat_interval = (5 * 60); + rrec->repeat_time = time(NULL) + rrec->repeat_interval; + + DEBUG(5,("release_name_timeout_response: increasing WINS timeout to %d seconds.\n", + rrec->repeat_interval)); + return; /* Don't remove the response record. */ + } + } + + DEBUG(5,("release_name_timeout_response: %s in releasing name %s on subnet %s.\n", + success ? "success" : "failure", namestr(question_name), subrec->subnet_name)); + + if(success && rrec->success_fn) + { + if(rrec->success_fn) + (*rrec->success_fn)(subrec, rrec->userdata, question_name, released_ip); + standard_success_release( subrec, rrec->userdata, question_name, released_ip); + } + else + { + /* We have no standard_fail_release - maybe we should add one ? */ + if( rrec->fail_fn) + (*rrec->fail_fn)(subrec, rrec, question_name); + } + + remove_response_record(subrec, rrec); +} + +/**************************************************************************** + Try and release one of our names. +****************************************************************************/ + +BOOL release_name(struct subnet_record *subrec, struct name_record *namerec, + release_name_success_function success_fn, + release_name_fail_function fail_fn, + struct userdata_struct *userdata) +{ + int i; + + /* Ensure it's a SELF name, and in the ACTIVE state. */ + if((namerec->source != SELF_NAME) || !NAME_IS_ACTIVE(namerec)) + { + DEBUG(0,("release_name: Cannot release name %s from subnet %s. Source was %d \n", + namestr(&namerec->name), subrec->subnet_name, namerec->source)); + return True; + } + + /* Set the name into the deregistering state. */ + namerec->nb_flags |= NB_DEREG; + + /* + * Go through and release the name for all known ip addresses. + * Only call the success/fail function on the last one (it should + * only be done once). + */ + + for( i = 0; i < namerec->num_ips; i++) + { + if(queue_release_name( subrec, + release_name_response, + release_name_timeout_response, + (i == (namerec->num_ips - 1)) ? success_fn : NULL, + (i == (namerec->num_ips - 1)) ? fail_fn : NULL, + (i == (namerec->num_ips - 1)) ? userdata : NULL, + &namerec->name, + namerec->nb_flags, + namerec->ip[i]) == NULL) + { + DEBUG(0,("release_name: Failed to send packet trying to release name %s IP %s\n", + namestr(&namerec->name), inet_ntoa(namerec->ip[i]) )); + return True; + } + } + return False; +} diff --git a/source3/nmbd/nmbd_nodestatus.c b/source3/nmbd/nmbd_nodestatus.c new file mode 100644 index 0000000000..267446c69d --- /dev/null +++ b/source3/nmbd/nmbd_nodestatus.c @@ -0,0 +1,99 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NBT netbios routines and daemon - version 2 + Copyright (C) Andrew Tridgell 1994-1997 + Copyright (C) Luke Kenneth Casson Leighton 1994-1997 + Copyright (C) Jeremy Allison 1994-1997 + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "includes.h" + +extern int DEBUGLEVEL; + +extern pstring scope; + +/**************************************************************************** + Deal with a successful node status response. +****************************************************************************/ +static void node_status_response(struct subnet_record *subrec, + struct response_record *rrec, struct packet_struct *p) +{ + struct nmb_packet *nmb = &p->packet.nmb; + struct nmb_name *question_name = &rrec->packet->packet.nmb.question.question_name; + struct nmb_name *answer_name = &nmb->answers->rr_name; + + /* Sanity check. Ensure that the answer name in the incoming packet is the + same as the requested name in the outgoing packet. */ + + if(!nmb_name_equal(question_name, answer_name)) + { + DEBUG(0,("node_status_response: Answer name %s differs from question \ +name %s.\n", namestr(answer_name), namestr(question_name))); + return; + } + + DEBUG(5,("node_status_response: response from name %s on subnet %s.\n", + namestr(answer_name), subrec->subnet_name)); + + /* Just send the whole answer resource record for the success function + to parse. */ + if(rrec->success_fn) + (*rrec->success_fn)(subrec, rrec->userdata, nmb->answers, p->ip); + + /* Ensure we don't retry. */ + remove_response_record(subrec, rrec); +} + +/**************************************************************************** + Deal with a timeout when requesting a node status. +****************************************************************************/ +static void node_status_timeout_response(struct subnet_record *subrec, + struct response_record *rrec) +{ + struct nmb_packet *sent_nmb = &rrec->packet->packet.nmb; + struct nmb_name *question_name = &sent_nmb->question.question_name; + + DEBUG(5,("node_status_timeout_response: failed to get node status from name %s on subnet %s\n", + namestr(question_name), subrec->subnet_name)); + + if( rrec->fail_fn) + (*rrec->fail_fn)(subrec, rrec); + + /* Ensure we don't retry. */ + remove_response_record(subrec, rrec); +} + +/**************************************************************************** + Try and do a node status to a name - given the name & IP address. +****************************************************************************/ + +BOOL node_status(struct subnet_record *subrec, struct nmb_name *nmbname, + struct in_addr send_ip, node_status_success_function success_fn, + node_status_fail_function fail_fn, struct userdata_struct *userdata) +{ + if(queue_node_status( subrec, + node_status_response, node_status_timeout_response, + success_fn, fail_fn, userdata, nmbname, send_ip)==NULL) + { + DEBUG(0,("node_status: Failed to send packet trying to get node status for \ +name %s, IP address %s\n", namestr(nmbname), inet_ntoa(send_ip))); + return True; + } + return False; +} diff --git a/source3/nmbd/nmbd_packets.c b/source3/nmbd/nmbd_packets.c new file mode 100644 index 0000000000..43249cc0a3 --- /dev/null +++ b/source3/nmbd/nmbd_packets.c @@ -0,0 +1,1775 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NBT netbios routines and daemon - version 2 + Copyright (C) Andrew Tridgell 1994-1997 + Copyright (C) Luke Kenneth Casson Leighton 1994-1997 + Copyright (C) Jeremy Allison 1994-1997 + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "includes.h" + +extern int ClientNMB; +extern int ClientDGRAM; +extern int global_nmb_port; + +extern int DEBUGLEVEL; + +extern int num_response_packets; + +extern pstring scope; +extern struct in_addr loopback_ip; + +/******************************************************************* + 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; + +/*************************************************************************** +Utility function to find the specific fd to send a packet out on. +**************************************************************************/ + +static int find_subnet_fd_for_address( struct in_addr local_ip ) +{ + struct subnet_record *subrec; + + for( subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) + if(ip_equal(local_ip, subrec->myip)) + return subrec->nmb_sock; + + return ClientNMB; +} + +/*************************************************************************** +Get/Set problematic nb_flags as network byte order 16 bit int. +**************************************************************************/ + +uint16 get_nb_flags(char *buf) +{ + return ((((uint16)*buf)&0xFFFF) & NB_FLGMSK); +} + +void set_nb_flags(char *buf, uint16 nb_flags) +{ + *buf++ = ((nb_flags & NB_FLGMSK) & 0xFF); + *buf = '\0'; +} + +/*************************************************************************** +Dumps out the browse packet data. +**************************************************************************/ + +static void debug_browse_data(char *outbuf, int len) +{ + int i,j; + for (i = 0; i < len; i+= 16) + { + DEBUG(4, ("%3x char ", i)); + + for (j = 0; j < 16; j++) + { + unsigned char x = outbuf[i+j]; + if (x < 32 || x > 127) + x = '.'; + + if (i+j >= len) + break; + DEBUG(4, ("%c", x)); + } + + DEBUG(4, (" hex ", i)); + + for (j = 0; j < 16; j++) + { + if (i+j >= len) + break; + DEBUG(4, (" %02x", (unsigned char)outbuf[i+j])); + } + + DEBUG(4, ("\n")); + } +} + +/*************************************************************************** + Generates the unique transaction identifier +**************************************************************************/ + +static uint16 name_trn_id=0; + +static uint16 generate_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; + return name_trn_id; +} + +/*************************************************************************** + Either loops back or sends out a completed NetBIOS packet. +**************************************************************************/ + +static BOOL send_netbios_packet(struct packet_struct *p) +{ + BOOL loopback_this_packet = False; + + /* Check if we are sending to or from ourselves as a WINS server. */ + if(ismyip(p->ip) && (p->port == global_nmb_port)) + loopback_this_packet = True; + + if(loopback_this_packet) + { + struct packet_struct *lo_packet = NULL; + DEBUG(5,("send_netbios_packet: sending packet to ourselves.\n")); + if((lo_packet = copy_packet(p)) == NULL) + return False; + queue_packet(lo_packet); + } + else if (!send_packet(p)) + { + DEBUG(0,("send_netbios_packet: send_packet() to IP %s port %d failed\n", + inet_ntoa(p->ip),p->port)); + return False; + } + + return True; +} + +/*************************************************************************** + Sets up the common elements of an outgoing NetBIOS packet. +**************************************************************************/ + +static struct packet_struct *create_and_init_netbios_packet(struct nmb_name *nmbname, + BOOL bcast, + struct in_addr to_ip) +{ + struct packet_struct *packet = NULL; + struct nmb_packet *nmb = NULL; + + /* Allocate the packet_struct we will return. */ + if((packet = (struct packet_struct *)malloc(sizeof(*packet))) == NULL) + { + DEBUG(0,("create_and_init_netbios_packet: malloc fail (1) for packet struct.\n")); + return NULL; + } + + bzero((char *)packet,sizeof(*packet)); + + nmb = &packet->packet.nmb; + + nmb->header.name_trn_id = generate_name_trn_id(); + nmb->header.response = False; + nmb->header.nm_flags.recursion_desired = False; + nmb->header.nm_flags.recursion_available = False; + nmb->header.nm_flags.trunc = False; + nmb->header.nm_flags.authoritative = False; + nmb->header.nm_flags.bcast = bcast; + + nmb->header.rcode = 0; + nmb->header.qdcount = 1; + nmb->header.ancount = 0; + nmb->header.nscount = 0; + + nmb->question.question_name = *nmbname; + nmb->question.question_type = QUESTION_TYPE_NB_QUERY; + nmb->question.question_class = QUESTION_CLASS_IN; + + packet->ip = to_ip; + packet->port = NMB_PORT; + packet->fd = ClientNMB; + packet->timestamp = time(NULL); + packet->packet_type = NMB_PACKET; + packet->locked = False; + + return packet; /* Caller must free. */ +} + +/*************************************************************************** + Sets up the common elements of register, refresh or release packet. +**************************************************************************/ + +static BOOL create_and_init_additional_record(struct packet_struct *packet, + uint16 nb_flags, + struct in_addr *register_ip) +{ + struct nmb_packet *nmb = &packet->packet.nmb; + + if((nmb->additional = (struct res_rec *)malloc(sizeof(struct res_rec))) == NULL) + { + DEBUG(0,("initiate_name_register_packet: malloc fail for additional record.\n")); + return False; + } + + bzero((char *)nmb->additional,sizeof(struct res_rec)); + + nmb->additional->rr_name = nmb->question.question_name; + nmb->additional->rr_type = RR_TYPE_NB; + nmb->additional->rr_class = RR_CLASS_IN; + + nmb->additional->ttl = lp_max_ttl(); + + nmb->additional->rdlength = 6; + + set_nb_flags(nmb->additional->rdata,nb_flags); + + /* Set the address for the name we are registering. */ + putip(&nmb->additional->rdata[2], register_ip); + + /* Ensure that we send out the file descriptor to give us the + the specific source address we are registering as our + IP source address. */ + + packet->fd = find_subnet_fd_for_address( *register_ip ); + + return True; +} + +/*************************************************************************** + Sends out a name query. +**************************************************************************/ + +static BOOL initiate_name_query_packet( struct packet_struct *packet) +{ + struct nmb_packet *nmb = NULL; + + nmb = &packet->packet.nmb; + + nmb->header.opcode = NMB_NAME_QUERY_OPCODE; + nmb->header.arcount = 0; + + nmb->header.nm_flags.recursion_desired = True; + + DEBUG(4,("initiate_name_query_packet: sending query for name %s (bcast=%s) to IP %s\n", + namestr(&nmb->question.question_name), + BOOLSTR(nmb->header.nm_flags.bcast), inet_ntoa(packet->ip))); + + return send_netbios_packet( packet ); +} + +/*************************************************************************** + Sends out a name register. +**************************************************************************/ + +static BOOL initiate_name_register_packet( struct packet_struct *packet, + uint16 nb_flags, struct in_addr *register_ip) +{ + struct nmb_packet *nmb = &packet->packet.nmb; + + nmb->header.opcode = NMB_NAME_REG_OPCODE; + nmb->header.arcount = 1; + + nmb->header.nm_flags.recursion_desired = True; + + if(create_and_init_additional_record(packet, nb_flags, register_ip) == False) + return False; + + DEBUG(4,("initiate_name_register_packet: sending registration for name %s (bcast=%s) to IP %s\n", + namestr(&nmb->additional->rr_name), + BOOLSTR(nmb->header.nm_flags.bcast), inet_ntoa(packet->ip))); + + return send_netbios_packet( packet ); +} + +/*************************************************************************** + Sends out a multihomed name register. +**************************************************************************/ + +static BOOL initiate_multihomed_name_register_packet( struct packet_struct *packet, + uint16 nb_flags, struct in_addr *register_ip) +{ + struct nmb_packet *nmb = &packet->packet.nmb; + char second_ip_buf[25]; + + strcpy(second_ip_buf, inet_ntoa(packet->ip)); + + nmb->header.opcode = NMB_NAME_MULTIHOMED_REG_OPCODE; + nmb->header.arcount = 1; + + nmb->header.nm_flags.recursion_desired = True; + + if(create_and_init_additional_record(packet, nb_flags, register_ip) == False) + return False; + + DEBUG(4,("initiate_multihomed_name_register_packet: sending registration \ +for name %s IP %s (bcast=%s) to IP %s\n", + namestr(&nmb->additional->rr_name), inet_ntoa(*register_ip), + BOOLSTR(nmb->header.nm_flags.bcast), second_ip_buf )); + + return send_netbios_packet( packet ); +} + +/*************************************************************************** + Sends out a name refresh. +**************************************************************************/ + +static BOOL initiate_name_refresh_packet( struct packet_struct *packet, + uint16 nb_flags, struct in_addr *refresh_ip) +{ + struct nmb_packet *nmb = &packet->packet.nmb; + + nmb->header.opcode = NMB_NAME_REFRESH_OPCODE_8; + nmb->header.arcount = 1; + + nmb->header.nm_flags.recursion_desired = False; + + if(create_and_init_additional_record(packet, nb_flags, refresh_ip) == False) + return False; + + DEBUG(4,("initiate_name_refresh_packet: sending refresh for name %s (bcast=%s) to IP %s\n", + namestr(&nmb->additional->rr_name), + BOOLSTR(nmb->header.nm_flags.bcast), inet_ntoa(packet->ip))); + + return send_netbios_packet( packet ); +} + +/*************************************************************************** + Sends out a name release. +**************************************************************************/ + +static BOOL initiate_name_release_packet( struct packet_struct *packet, + uint16 nb_flags, struct in_addr *release_ip) +{ + struct nmb_packet *nmb = &packet->packet.nmb; + + nmb->header.opcode = NMB_NAME_RELEASE_OPCODE; + nmb->header.arcount = 1; + + nmb->header.nm_flags.recursion_desired = False; + + if(create_and_init_additional_record(packet, nb_flags, release_ip) == False) + return False; + + DEBUG(4,("initiate_name_release_packet: sending release for name %s (bcast=%s) to IP %s\n", + namestr(&nmb->additional->rr_name), + BOOLSTR(nmb->header.nm_flags.bcast), inet_ntoa(packet->ip))); + + return send_netbios_packet( packet ); +} + +/*************************************************************************** + Sends out a node status. +**************************************************************************/ + +static BOOL initiate_node_status_packet( struct packet_struct *packet ) +{ + struct nmb_packet *nmb = &packet->packet.nmb; + + nmb->header.opcode = NMB_NAME_QUERY_OPCODE; + nmb->header.arcount = 0; + + nmb->header.nm_flags.recursion_desired = False; + + nmb->question.question_type = QUESTION_TYPE_NB_STATUS; + + DEBUG(4,("initiate_node_status_packet: sending node status request for name %s to IP %s\n", + namestr(&nmb->question.question_name), + inet_ntoa(packet->ip))); + + return send_netbios_packet( packet ); +} + +/**************************************************************************** + Simplification functions for queuing standard packets. + These should be the only publicly callable functions for sending + out packets. +****************************************************************************/ + +/**************************************************************************** + Assertion - we should never be sending nmbd packets on the remote + broadcast subnet. +****************************************************************************/ + +static BOOL assert_check_subnet(struct subnet_record *subrec) +{ + if( subrec == remote_broadcast_subnet) + { + DEBUG(0,("assert_check_subnet: Attempt to send packet on remote broadcast subnet. \ +This is a bug.\n")); + return True; + } + return False; +} + +/**************************************************************************** + Queue a register name packet to the broadcast address of a subnet. +****************************************************************************/ + +struct response_record *queue_register_name( struct subnet_record *subrec, + response_function resp_fn, + timeout_response_function timeout_fn, + register_name_success_function success_fn, + register_name_fail_function fail_fn, + struct userdata_struct *userdata, + struct nmb_name *nmbname, + uint16 nb_flags) +{ + struct packet_struct *p; + struct response_record *rrec; + BOOL bcast = (subrec == unicast_subnet) ? False : True; + + if(assert_check_subnet(subrec)) + return NULL; + + if(( p = create_and_init_netbios_packet(nmbname, bcast, + subrec->bcast_ip)) == NULL) + return NULL; + + if(initiate_name_register_packet( p, nb_flags, + iface_ip(subrec->bcast_ip)) == False) + { + p->locked = False; + free_packet(p); + return NULL; + } + + if((rrec = make_response_record(subrec, /* subnet record. */ + p, /* packet we sent. */ + resp_fn, /* function to call on response. */ + timeout_fn, /* function to call on timeout. */ + (success_function)success_fn, /* function to call on operation success. */ + (fail_function)fail_fn, /* function to call on operation fail. */ + userdata)) == NULL) + { + p->locked = False; + free_packet(p); + return NULL; + } + + return rrec; +} + +/**************************************************************************** + Queue a multihomed register name packet to the broadcast address of a subnet. +****************************************************************************/ + +struct response_record *queue_register_multihomed_name( struct subnet_record *subrec, + response_function resp_fn, + timeout_response_function timeout_fn, + register_name_success_function success_fn, + register_name_fail_function fail_fn, + struct userdata_struct *userdata, + struct nmb_name *nmbname, + uint16 nb_flags, + struct in_addr register_ip) +{ + struct packet_struct *p; + struct response_record *rrec; + BOOL bcast = False; + BOOL ret; + + /* Sanity check. */ + if(subrec != unicast_subnet) + { + DEBUG(0,("queue_register_multihomed_name: should only be done on \ +unicast subnet. subnet is %s\n.", subrec->subnet_name )); + return NULL; + } + + if(assert_check_subnet(subrec)) + return NULL; + + if(( p = create_and_init_netbios_packet(nmbname, bcast, + subrec->bcast_ip)) == NULL) + return NULL; + + if (nb_flags & NB_GROUP) + ret = initiate_name_register_packet( p, nb_flags, ®ister_ip); + else + ret = initiate_multihomed_name_register_packet( p, nb_flags, ®ister_ip); + + if(ret == False) + { + p->locked = False; + free_packet(p); + return NULL; + } + + if((rrec = make_response_record(subrec, /* subnet record. */ + p, /* packet we sent. */ + resp_fn, /* function to call on response. */ + timeout_fn, /* function to call on timeout. */ + (success_function)success_fn, /* function to call on operation success. */ + (fail_function)fail_fn, /* function to call on operation fail. */ + userdata)) == NULL) + { + p->locked = False; + free_packet(p); + return NULL; + } + + return rrec; +} + +/**************************************************************************** + Queue a release name packet to the broadcast address of a subnet. +****************************************************************************/ + +struct response_record *queue_release_name( struct subnet_record *subrec, + response_function resp_fn, + timeout_response_function timeout_fn, + release_name_success_function success_fn, + release_name_fail_function fail_fn, + struct userdata_struct *userdata, + struct nmb_name *nmbname, + uint16 nb_flags, + struct in_addr release_ip) +{ + BOOL bcast = (subrec == unicast_subnet) ? False : True; + struct packet_struct *p; + struct response_record *rrec; + + if(assert_check_subnet(subrec)) + return NULL; + + if(( p = create_and_init_netbios_packet(nmbname, bcast, + subrec->bcast_ip)) == NULL) + return NULL; + + if(initiate_name_release_packet( p, nb_flags, &release_ip) == False) + { + p->locked = False; + free_packet(p); + return NULL; + } + + if((rrec = make_response_record(subrec, /* subnet record. */ + p, /* packet we sent. */ + resp_fn, /* function to call on response. */ + timeout_fn, /* function to call on timeout. */ + (success_function)success_fn, /* function to call on operation success. */ + (fail_function)fail_fn, /* function to call on operation fail. */ + userdata)) == NULL) + { + p->locked = False; + free_packet(p); + return NULL; + } + + return rrec; +} + +/**************************************************************************** + Queue a refresh name packet to the broadcast address of a subnet. +****************************************************************************/ + +struct response_record *queue_refresh_name( struct subnet_record *subrec, + response_function resp_fn, + timeout_response_function timeout_fn, + refresh_name_success_function success_fn, + refresh_name_fail_function fail_fn, + struct userdata_struct *userdata, + struct name_record *namerec, + struct in_addr refresh_ip) +{ + BOOL bcast = (subrec == unicast_subnet) ? False : True; + struct packet_struct *p; + struct response_record *rrec; + + if(assert_check_subnet(subrec)) + return NULL; + + if(( p = create_and_init_netbios_packet(&namerec->name, bcast, + subrec->bcast_ip)) == NULL) + return NULL; + + if(initiate_name_refresh_packet( p, namerec->nb_flags, &refresh_ip) == False) + { + p->locked = False; + free_packet(p); + return NULL; + } + + if((rrec = make_response_record(subrec, /* subnet record. */ + p, /* packet we sent. */ + resp_fn, /* function to call on response. */ + timeout_fn, /* function to call on timeout. */ + (success_function)success_fn, /* function to call on operation success. */ + (fail_function)fail_fn, /* function to call on operation fail. */ + userdata)) == NULL) + { + p->locked = False; + free_packet(p); + return NULL; + } + + return rrec; +} + +/**************************************************************************** + Queue a query name packet to the broadcast address of a subnet. +****************************************************************************/ + +struct response_record *queue_query_name( struct subnet_record *subrec, + response_function resp_fn, + timeout_response_function timeout_fn, + query_name_success_function success_fn, + query_name_fail_function fail_fn, + struct userdata_struct *userdata, + struct nmb_name *nmbname) +{ + struct packet_struct *p; + struct response_record *rrec; + BOOL bcast = True; + + if ((subrec == unicast_subnet) || (subrec == wins_server_subnet)) + bcast = False; + + if(assert_check_subnet(subrec)) + return NULL; + + if(( p = create_and_init_netbios_packet(nmbname, bcast, + subrec->bcast_ip)) == NULL) + return NULL; + + if(initiate_name_query_packet( p ) == False) + { + p->locked = False; + free_packet(p); + return NULL; + } + + if((rrec = make_response_record(subrec, /* subnet record. */ + p, /* packet we sent. */ + resp_fn, /* function to call on response. */ + timeout_fn, /* function to call on timeout. */ + (success_function)success_fn, /* function to call on operation success. */ + (fail_function)fail_fn, /* function to call on operation fail. */ + userdata)) == NULL) + { + p->locked = False; + free_packet(p); + return NULL; + } + + return rrec; +} + +/**************************************************************************** + Queue a node status packet to a given name and address. +****************************************************************************/ + +struct response_record *queue_node_status( struct subnet_record *subrec, + response_function resp_fn, + timeout_response_function timeout_fn, + node_status_success_function success_fn, + node_status_fail_function fail_fn, + struct userdata_struct *userdata, + struct nmb_name *nmbname, + struct in_addr send_ip) +{ + struct packet_struct *p; + struct response_record *rrec; + BOOL bcast = False; + + /* Sanity check. */ + if(subrec != unicast_subnet) + { + DEBUG(0,("queue_register_multihomed_name: should only be done on \ +unicast subnet. subnet is %s\n.", subrec->subnet_name )); + return NULL; + } + + if(assert_check_subnet(subrec)) + return NULL; + + if(( p = create_and_init_netbios_packet(nmbname, bcast, + send_ip)) == NULL) + return NULL; + + if(initiate_node_status_packet(p) == False) + { + p->locked = False; + free_packet(p); + return NULL; + } + + if((rrec = make_response_record(subrec, /* subnet record. */ + p, /* packet we sent. */ + resp_fn, /* function to call on response. */ + timeout_fn, /* function to call on timeout. */ + (success_function)success_fn, /* function to call on operation success. */ + (fail_function)fail_fn, /* function to call on operation fail. */ + userdata)) == NULL) + { + p->locked = False; + free_packet(p); + return NULL; + } + + return rrec; +} + +/**************************************************************************** + Reply to a netbios name packet. see rfc1002.txt +****************************************************************************/ + +void reply_netbios_packet(struct packet_struct *orig_packet, + int rcode, enum netbios_reply_type_code rcv_code, int opcode, + int ttl, char *data,int len) +{ + struct packet_struct packet; + struct nmb_packet *nmb = NULL; + struct res_rec answers; + struct nmb_packet *orig_nmb = &orig_packet->packet.nmb; + BOOL loopback_this_packet = False; + char *packet_type = "unknown"; + + /* Check if we are sending to or from ourselves. */ + if(ismyip(orig_packet->ip) && (orig_packet->port == global_nmb_port)) + loopback_this_packet = True; + + nmb = &packet.packet.nmb; + + /* Do a partial copy of the packet. We clear the locked flag and + the resource record pointers. */ + packet = *orig_packet; /* Full structure copy. */ + packet.locked = False; + nmb->answers = NULL; + nmb->nsrecs = NULL; + nmb->additional = NULL; + + switch (rcv_code) + { + case NMB_STATUS: + { + packet_type = "nmb_status"; + nmb->header.nm_flags.recursion_desired = False; + nmb->header.nm_flags.recursion_available = False; + break; + } + case NMB_QUERY: + { + packet_type = "nmb_query"; + nmb->header.nm_flags.recursion_desired = True; + nmb->header.nm_flags.recursion_available = True; + break; + } + case NMB_REG: + case NMB_REG_REFRESH: + { + packet_type = "nmb_reg"; + nmb->header.nm_flags.recursion_desired = True; + nmb->header.nm_flags.recursion_available = True; + break; + } + case NMB_REL: + { + packet_type = "nmb_rel"; + nmb->header.nm_flags.recursion_desired = False; + nmb->header.nm_flags.recursion_available = False; + break; + } + case NMB_WAIT_ACK: + { + packet_type = "nmb_wack"; + nmb->header.nm_flags.recursion_desired = False; + nmb->header.nm_flags.recursion_available = False; + break; + } + case WINS_REG: + { + packet_type = "wins_reg"; + nmb->header.nm_flags.recursion_desired = True; + nmb->header.nm_flags.recursion_available = True; + break; + } + case WINS_QUERY: + { + packet_type = "wins_query"; + nmb->header.nm_flags.recursion_desired = True; + nmb->header.nm_flags.recursion_available = True; + break; + } + + default: + { + DEBUG(0,("reply_netbios_packet: Unknown packet type: %s %s to ip %s\n", + packet_type, namestr(&orig_nmb->question.question_name), + inet_ntoa(packet.ip))); + + return; + } + } + + DEBUG(4,("reply_netbios_packet: sending a reply of packet type: %s %s to ip %s \ +for id %hu\n", + packet_type, namestr(&orig_nmb->question.question_name), + inet_ntoa(packet.ip), orig_nmb->header.name_trn_id)); + + nmb->header.name_trn_id = orig_nmb->header.name_trn_id; + nmb->header.opcode = opcode; + nmb->header.response = True; + nmb->header.nm_flags.bcast = False; + nmb->header.nm_flags.trunc = False; + nmb->header.nm_flags.authoritative = True; + + nmb->header.rcode = rcode; + nmb->header.qdcount = 0; + nmb->header.ancount = 1; + nmb->header.nscount = 0; + nmb->header.arcount = 0; + + bzero((char*)&nmb->question,sizeof(nmb->question)); + + nmb->answers = &answers; + bzero((char*)nmb->answers,sizeof(*nmb->answers)); + + nmb->answers->rr_name = orig_nmb->question.question_name; + nmb->answers->rr_type = orig_nmb->question.question_type; + nmb->answers->rr_class = orig_nmb->question.question_class; + nmb->answers->ttl = ttl; + + if (data && len) + { + nmb->answers->rdlength = len; + memcpy(nmb->answers->rdata, data, len); + } + + packet.packet_type = NMB_PACKET; + /* Ensure we send out on the same fd that the original + packet came in on to give the correct source IP address. */ + packet.fd = orig_packet->fd; + packet.timestamp = time(NULL); + + debug_nmb_packet(&packet); + + if(loopback_this_packet) + { + struct packet_struct *lo_packet; + DEBUG(5,("reply_netbios_packet: sending packet to ourselves.\n")); + if((lo_packet = copy_packet(&packet)) == NULL) + return; + queue_packet(lo_packet); + } + else if (!send_packet(&packet)) + { + DEBUG(0,("reply_netbios_packet: send_packet to IP %s port %d failed\n", + inet_ntoa(packet.ip),packet.port)); + } +} + +/******************************************************************* + Queue a packet into a 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; +} + +/**************************************************************************** + Try and find a matching subnet record for a datagram port 138 packet. +****************************************************************************/ + +static struct subnet_record *find_subnet_for_dgram_browse_packet(struct packet_struct *p) +{ + struct subnet_record *subrec; + + /* Go through all the broadcast subnets and see if the mask matches. */ + for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) + { + if(same_net(p->ip, subrec->bcast_ip, subrec->mask_ip)) + return subrec; + } + + /* If the subnet record is the remote announce broadcast subnet, + hack it here to be the first subnet. This is really gross and + is needed due to people turning on port 137/138 broadcast + forwarding on their routers. May fire and brimstone rain + down upon them... + */ + + return FIRST_SUBNET; +} + +/**************************************************************************** +Dispatch a browse frame from port 138 to the correct processing function. +****************************************************************************/ + +void process_browse_packet(struct packet_struct *p, char *buf,int len) +{ + struct dgram_packet *dgram = &p->packet.dgram; + int command = CVAL(buf,0); + struct subnet_record *subrec = find_subnet_for_dgram_browse_packet(p); + + /* Drop the packet if it's a different NetBIOS scope, or + the source is from one of our names. */ + + if (!strequal(dgram->dest_name.scope,scope )) + { + DEBUG(7,("process_browse_packet: Discarding datagram from IP %s. Scope (%s) \ +mismatch with our scope (%s).\n", inet_ntoa(p->ip), dgram->dest_name.scope, scope)); + return; + } + + if (is_myname(dgram->source_name.name)) + { + DEBUG(0,("process_browse_packet: Discarding datagram from IP %s. Source name \ +%s is one of our names !\n", inet_ntoa(p->ip), namestr(&dgram->source_name))); + return; + } + + switch (command) + { + case ANN_HostAnnouncement: + { + debug_browse_data(buf, len); + process_host_announce(subrec, p, buf+1); + break; + } + case ANN_DomainAnnouncement: + { + debug_browse_data(buf, len); + process_workgroup_announce(subrec, p, buf+1); + break; + } + case ANN_LocalMasterAnnouncement: + { + debug_browse_data(buf, len); + process_local_master_announce(subrec, p, buf+1); + break; + } + case ANN_AnnouncementRequest: + { + process_announce_request(subrec, p, buf+1); + break; + } + case ANN_Election: + { + process_election(subrec, p, buf+1); + break; + } + case ANN_GetBackupListReq: + { + debug_browse_data(buf, len); + + /* This is one occasion where we change a subnet that is + given to us. If the packet was sent to WORKGROUP<1b> instead + of WORKGROUP<1d> then it was unicast to us a domain master + browser. Change subrec to unicast. + */ + if(dgram->dest_name.name_type == 0x1b) + subrec = unicast_subnet; + + process_get_backup_list_request(subrec, p, buf+1); + break; + } + case ANN_GetBackupListResp: + { + debug_browse_data(buf, len); + /* We never send ANN_GetBackupListReq so we + should never get these. */ + DEBUG(0,("process_browse_packet: Discarding GetBackupListResponse \ +packet from %s IP %s\n", namestr(&dgram->source_name), inet_ntoa(p->ip))); + break; + } + case ANN_ResetBrowserState: + { + process_reset_browser(subrec, p, buf+1); + break; + } + case ANN_MasterAnnouncement: + { + /* Master browser datagrams must be processed + on the unicast subnet. */ + subrec = unicast_subnet; + + process_master_browser_announce(subrec, p, buf+1); + break; + } + default: + { + DEBUG(0,("process_browse_packet: On subnet %s ignoring browse packet \ +command code %d from %s IP %s to %s\n", + subrec->subnet_name, command, namestr(&dgram->source_name), + inet_ntoa(p->ip), namestr(&dgram->dest_name))); + } + } +} + +/**************************************************************************** + Determine if a packet is for us on port 138. Note that to have any chance of + being efficient we need to drop as many packets as possible at this + stage as subsequent processing is expensive. +****************************************************************************/ + +static BOOL listening(struct packet_struct *p,struct nmb_name *nbname) +{ + struct subnet_record *subrec = NULL; + + for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) + { + if(same_net(p->ip, subrec->bcast_ip, subrec->mask_ip)) + break; + } + + if(subrec == NULL) + subrec = unicast_subnet; + + return (find_name_on_subnet(subrec, nbname, FIND_SELF_NAME) != NULL); +} + +/**************************************************************************** + Process udp 138 datagrams +****************************************************************************/ + +static void process_dgram(struct packet_struct *p) +{ + char *buf; + char *buf2; + int len; + struct dgram_packet *dgram = &p->packet.dgram; + + /* If we aren't listening to the destination name then ignore the packet */ + if (!listening(p,&dgram->dest_name)) + { + DEBUG(5,("process_dgram: ignoring dgram packet sent to name %s from %s\n", + namestr(&dgram->dest_name), inet_ntoa(p->ip))); + return; + } + + if (dgram->header.msg_type != 0x10 && + dgram->header.msg_type != 0x11 && + dgram->header.msg_type != 0x12) + { + /* Don't process error packets etc yet */ + DEBUG(5,("process_dgram: ignoring dgram packet sent to name %s from IP %s as it is \ + an error packet of type %x\n", + namestr(&dgram->dest_name), inet_ntoa(p->ip), dgram->header.msg_type)); + 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,("process_dgram: datagram from %s to %s IP %s for %s of type %d len=%d\n", + namestr(&dgram->source_name),namestr(&dgram->dest_name), + inet_ntoa(p->ip), 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 logon mailslot */ + if (strequal(smb_buf(buf),NET_LOGON_MAILSLOT)) + { + process_logon_packet(p,buf2,len,NET_LOGON_MAILSLOT); + return; + } + + /* Datagram packet received for the NT domain logon mailslot */ + if (strequal(smb_buf(buf),NT_LOGON_MAILSLOT)) + { + process_logon_packet(p,buf2,len,NT_LOGON_MAILSLOT); + return; + } +} + +/**************************************************************************** + Validate a response nmb packet. +****************************************************************************/ + +BOOL validate_nmb_response_packet( struct nmb_packet *nmb ) +{ + BOOL ignore = False; + + switch (nmb->header.opcode) + { + case NMB_NAME_REG_OPCODE: + case NMB_NAME_REFRESH_OPCODE_8: /* ambiguity in rfc1002 about which is correct. */ + case NMB_NAME_REFRESH_OPCODE_9: /* WinNT uses 8 by default. */ + if (nmb->header.ancount == 0) + { + DEBUG(0,("validate_nmb_response_packet: Bad REG/REFRESH Packet. ")); + ignore = True; + } + break; + + case NMB_NAME_QUERY_OPCODE: + if ((nmb->header.ancount != 0) && (nmb->header.ancount != 1)) + { + DEBUG(0,("validate_nmb_response_packet: Bad QUERY Packet. ")); + ignore = True; + } + break; + case NMB_NAME_RELEASE_OPCODE: + if (nmb->header.ancount == 0) + { + DEBUG(0,("validate_nmb_response_packet: Bad RELEASE Packet. ")); + ignore = True; + } + break; + case NMB_WACK_OPCODE: + /* Check WACK response here. */ + if (nmb->header.ancount != 1) + { + DEBUG(0,("validate_nmb_response_packet: Bad WACK Packet. ")); + ignore = True; + } + break; + default: + DEBUG(0,("validate_nmb_response_packet: Ignoring packet with unknown opcode %d.\n", + nmb->header.opcode)); + return True; + } + + if(ignore) + DEBUG(0,("Ignoring response packet with opcode %d.\n", nmb->header.opcode)); + + return ignore; +} + +/**************************************************************************** + Validate a request nmb packet. +****************************************************************************/ + +BOOL validate_nmb_packet( struct nmb_packet *nmb ) +{ + BOOL ignore = False; + + switch (nmb->header.opcode) + { + case NMB_NAME_REG_OPCODE: + case NMB_NAME_REFRESH_OPCODE_8: /* ambiguity in rfc1002 about which is correct. */ + case NMB_NAME_REFRESH_OPCODE_9: /* WinNT uses 8 by default. */ + case NMB_NAME_MULTIHOMED_REG_OPCODE: + if (nmb->header.qdcount==0 || nmb->header.arcount==0) + { + DEBUG(0,("validate_nmb_packet: Bad REG/REFRESH Packet. ")); + ignore = True; + } + break; + + case NMB_NAME_QUERY_OPCODE: + if ((nmb->header.qdcount == 0) || + ((nmb->question.question_type != QUESTION_TYPE_NB_QUERY) && + (nmb->question.question_type != QUESTION_TYPE_NB_STATUS))) + { + DEBUG(0,("validate_nmb_packet: Bad QUERY Packet. ")); + ignore = True; + } + break; + + case NMB_NAME_RELEASE_OPCODE: + if (nmb->header.qdcount==0 || nmb->header.arcount==0) + { + DEBUG(0,("validate_nmb_packet: Bad RELEASE Packet. ")); + ignore = True; + } + break; + default: + DEBUG(0,("validate_nmb_packet: Ignoring packet with unknown opcode %d.\n", + nmb->header.opcode)); + return True; + } + + if(ignore) + DEBUG(0,("validate_nmb_packet: Ignoring request packet with opcode %d.\n", nmb->header.opcode)); + + return ignore; +} + +/**************************************************************************** + Find a subnet (and potentially a response record) for a packet. +****************************************************************************/ + +static struct subnet_record *find_subnet_for_nmb_packet( struct packet_struct *p, + struct response_record **pprrec) +{ + struct nmb_packet *nmb = &p->packet.nmb; + struct response_record *rrec = NULL; + struct subnet_record *subrec = NULL; + + if(pprrec != NULL) + *pprrec = NULL; + + if(nmb->header.response) + { + /* It's a response packet. Find a record for it or it's an error. */ + + rrec = find_response_record( &subrec, nmb->header.name_trn_id); + if(rrec == NULL) + { + DEBUG(0,("find_subnet_for_nmb_packet: response record not found for response id %hu\n", + nmb->header.name_trn_id)); + return NULL; + } + + if(subrec == NULL) + { + DEBUG(0,("find_subnet_for_nmb_packet: subnet record not found for response id %hu\n", + nmb->header.name_trn_id)); + return NULL; + } + + if(pprrec != NULL) + *pprrec = rrec; + return subrec; + } + + /* Try and see what subnet this packet belongs to. */ + + /* WINS server ? */ + if(packet_is_for_wins_server(p)) + return wins_server_subnet; + + /* If it wasn't a broadcast packet then send to the UNICAST subnet. */ + if(nmb->header.nm_flags.bcast == False) + return unicast_subnet; + + /* Go through all the broadcast subnets and see if the mask matches. */ + for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) + { + if(same_net(p->ip, subrec->bcast_ip, subrec->mask_ip)) + return subrec; + } + + /* If none match it must have been a directed broadcast - assign + the remote_broadcast_subnet. */ + return remote_broadcast_subnet; +} + +/**************************************************************************** + Process a nmb request packet - validate the packet and route it. +****************************************************************************/ + +static void process_nmb_request(struct packet_struct *p) +{ + struct nmb_packet *nmb = &p->packet.nmb; + struct subnet_record *subrec = NULL; + + debug_nmb_packet(p); + + /* Ensure we have a good packet. */ + if(validate_nmb_packet(nmb)) + return; + + /* Allocate a subnet to this packet - if we cannot - fail. */ + if((subrec = find_subnet_for_nmb_packet(p, NULL))==NULL) + return; + + switch (nmb->header.opcode) + { + case NMB_NAME_REG_OPCODE: + if(subrec == wins_server_subnet) + wins_process_name_registration_request(subrec, p); + else + process_name_registration_request(subrec, p); + break; + + case NMB_NAME_REFRESH_OPCODE_8: /* ambiguity in rfc1002 about which is correct. */ + case NMB_NAME_REFRESH_OPCODE_9: + if(subrec == wins_server_subnet) + wins_process_name_refresh_request(subrec, p); + else + process_name_refresh_request(subrec, p); + break; + + case NMB_NAME_MULTIHOMED_REG_OPCODE: + if(subrec == wins_server_subnet) + wins_process_multihomed_name_registration_request(subrec, p); + else + { + DEBUG(0,("process_nmb_request: Multihomed registration request must be \ +directed at a WINS server.\n")); + } + break; + + case NMB_NAME_QUERY_OPCODE: + switch (nmb->question.question_type) + { + case QUESTION_TYPE_NB_QUERY: + { + if(subrec == wins_server_subnet) + wins_process_name_query_request(subrec, p); + else + process_name_query_request(subrec, p); + break; + } + case QUESTION_TYPE_NB_STATUS: + { + if(subrec == wins_server_subnet) + { + DEBUG(0,("process_nmb_request: NB_STATUS request directed at WINS server is \ +not allowed.\n")); + break; + } + else + process_node_status_request(subrec, p); + break; + } + } + break; + + case NMB_NAME_RELEASE_OPCODE: + if(subrec == wins_server_subnet) + wins_process_name_release_request(subrec, p); + else + process_name_release_request(subrec, p); + break; + } +} + +/**************************************************************************** + Process a nmb response packet - validate the packet and route it. + to either the WINS server or a normal response. +****************************************************************************/ + +static void process_nmb_response(struct packet_struct *p) +{ + struct nmb_packet *nmb = &p->packet.nmb; + struct subnet_record *subrec = NULL; + struct response_record *rrec = NULL; + + debug_nmb_packet(p); + + if(validate_nmb_response_packet(nmb)) + return; + + if((subrec = find_subnet_for_nmb_packet(p, &rrec))==NULL) + return; + + if(rrec == NULL) + { + DEBUG(0,("process_nmb_response: response packet received but no response record \ +found for id = %hu. Ignoring packet.\n", nmb->header.name_trn_id)); + return; + } + + /* Increment the number of responses received for this record. */ + rrec->num_msgs++; + /* Ensure we don't re-send the request. */ + rrec->repeat_count = 0; + + /* Call the response received function for this packet. */ + (*rrec->resp_fn)(subrec, rrec, p); +} + + +/******************************************************************* + Run elements off the packet queue till its empty +******************************************************************/ + +void run_packet_queue() +{ + struct packet_struct *p; + + while ((p = packet_queue)) + { + packet_queue = p->next; + if (packet_queue) + packet_queue->prev = NULL; + p->next = p->prev = NULL; + + switch (p->packet_type) + { + case NMB_PACKET: + if(p->packet.nmb.header.response) + process_nmb_response(p); + else + process_nmb_request(p); + break; + + case DGRAM_PACKET: + process_dgram(p); + break; + } + free_packet(p); + } +} + +/******************************************************************* + Retransmit or timeout elements from all the outgoing subnet response + record queues. +******************************************************************/ + +void retransmit_or_expire_response_records(time_t t) +{ + struct subnet_record *subrec; + + for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec)) + { + struct response_record *rrec, *nextrrec; + + for (rrec = subrec->responselist; rrec; rrec = nextrrec) + { + nextrrec = rrec->next; + + if (rrec->repeat_time <= t) + { + if (rrec->repeat_count > 0) + { + /* Resend while we have a non-zero repeat_count. */ + if(!send_packet(rrec->packet)) + { + DEBUG(0,("retransmit_or_expire_response_records: Failed to resend packet id %hu \ +to IP %s on subnet %s\n", rrec->response_id, inet_ntoa(rrec->packet->ip), + subrec->subnet_name)); + } + rrec->repeat_time += rrec->repeat_interval; + rrec->repeat_count--; + } + else + { + DEBUG(4,("retransmit_or_expire_response_records: timeout for packet id %hu to IP %s \ +on subnet %s\n", rrec->response_id, inet_ntoa(rrec->packet->ip), + subrec->subnet_name)); + + /* Call the timeout function. This will deal with removing the + timed out packet. */ + if(rrec->timeout_fn) + (*rrec->timeout_fn)(subrec, rrec); + else + { + /* We must remove the record ourself if there is + no timeout function. */ + remove_response_record(subrec, rrec); + } + } /* rrec->repeat_count > 0 */ + } /* rrec->repeat_time <= t */ + } /* end for rrec */ + } /* end for subnet */ +} + +/**************************************************************************** + Create an fd_set containing all the sockets in the subnet structures, + plus the broadcast sockets. +***************************************************************************/ + +static BOOL create_listen_fdset(fd_set **ppset, int **psock_array, int *listen_number) +{ + int *sock_array = NULL; + struct subnet_record *subrec = NULL; + int count = 0; + int num = 0; + fd_set *pset = (fd_set *)malloc(sizeof(fd_set)); + + if(pset == NULL) + { + DEBUG(0,("create_listen_fdset: malloc fail !\n")); + return True; + } + + /* Check that we can add all the fd's we need. */ + for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) + count++; + + if((count*2) + 2 > FD_SETSIZE) + { + DEBUG(0,("create_listen_fdset: Too many file descriptors needed (%d). We can \ +only use %d.\n", (count*2) + 2, FD_SETSIZE)); + return True; + } + + if((sock_array = (int *)malloc(((count*2) + 2)*sizeof(int))) == NULL) + { + DEBUG(0,("create_listen_fdset: malloc fail for socket array.\n")); + return True; + } + + FD_ZERO(pset); + + /* Add in the broadcast socket on 137. */ + FD_SET(ClientNMB,pset); + sock_array[num++] = ClientNMB; + + /* Add in the 137 sockets on all the interfaces. */ + for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) + { + FD_SET(subrec->nmb_sock,pset); + sock_array[num++] = subrec->nmb_sock; + } + + /* Add in the broadcast socket on 138. */ + FD_SET(ClientDGRAM,pset); + sock_array[num++] = ClientDGRAM; + + /* Add in the 138 sockets on all the interfaces. */ + for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) + { + FD_SET(subrec->dgram_sock,pset); + sock_array[num++] = subrec->dgram_sock; + } + + *listen_number = (count*2) + 2; + *ppset = pset; + *psock_array = sock_array; + + return False; +} + +/**************************************************************************** + Listens for NMB or DGRAM packets, and queues them. +***************************************************************************/ + +BOOL listen_for_packets(BOOL run_election) +{ + static fd_set *listen_set = NULL; + static int listen_number = 0; + static int *sock_array = NULL; + + fd_set fds; + int selrtn; + struct timeval timeout; +#ifndef SYNC_DNS + int dns_fd; +#endif + + if(listen_set == NULL) + { + if(create_listen_fdset(&listen_set, &sock_array, &listen_number)) + { + DEBUG(0,("listen_for_packets: Fatal error. unable to create listen set. Exiting.\n")); + return True; + } + } + + memcpy((char *)&fds, (char *)listen_set, sizeof(fd_set)); + +#ifndef SYNC_DNS + dns_fd = asyncdns_fd(); + if (dns_fd != -1) { + FD_SET(dns_fd, &fds); + } +#endif + + + /* + * During elections and when expecting a netbios response packet we + * need to send election packets at tighter intervals. + * Ideally 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; + + /* We can only take term signals when we are in the select. */ + BlockSignals(False, SIGTERM); + selrtn = sys_select(&fds,&timeout); + BlockSignals(True, SIGTERM); + + if(selrtn > 0) + { + int i; + +#ifndef SYNC_DNS + if (dns_fd != -1 && FD_ISSET(dns_fd,&fds)) { + run_dns_queue(); + } +#endif + + for(i = 0; i < listen_number; i++) + { + if(i < (listen_number/2)) + { + /* Processing a 137 socket. */ + if (FD_ISSET(sock_array[i],&fds)) + { + struct packet_struct *packet = read_packet(sock_array[i], NMB_PACKET); + if (packet) + { + /* + * If we got a packet on the broadcast socket and interfaces + * only is set then check it came from one of our local nets. + */ + if(lp_bind_interfaces_only() && (sock_array[i] == ClientNMB) && + (!is_local_net(packet->ip))) + { + DEBUG(7,("discarding nmb packet sent to broadcast socket from %s:%d\n", + inet_ntoa(packet->ip),packet->port)); + free_packet(packet); + } + else if ((ip_equal(loopback_ip, packet->ip) || + ismyip(packet->ip)) && packet->port == global_nmb_port) + { + DEBUG(7,("discarding own packet from %s:%d\n", + inet_ntoa(packet->ip),packet->port)); + free_packet(packet); + } + else + { + /* Save the file descriptor this packet came in on. */ + packet->fd = sock_array[i]; + queue_packet(packet); + } + } + } + } + else + { + /* Processing a 138 socket. */ + + if (FD_ISSET(sock_array[i],&fds)) + { + struct packet_struct *packet = read_packet(sock_array[i], DGRAM_PACKET); + if (packet) + { + /* + * If we got a packet on the broadcast socket and interfaces + * only is set then check it came from one of our local nets. + */ + if(lp_bind_interfaces_only() && (sock_array[i] == ClientDGRAM) && + (!is_local_net(packet->ip))) + { + DEBUG(7,("discarding dgram packet sent to broadcast socket from %s:%d\n", + inet_ntoa(packet->ip),packet->port)); + free_packet(packet); + } + else if ((ip_equal(loopback_ip, packet->ip) || + ismyip(packet->ip)) && packet->port == DGRAM_PORT) + { + DEBUG(7,("discarding own packet from %s:%d\n", + inet_ntoa(packet->ip),packet->port)); + free_packet(packet); + } + else + { + /* Save the file descriptor this packet came in on. */ + packet->fd = sock_array[i]; + queue_packet(packet); + } + } + } + } /* end processing 138 socket. */ + } /* end for */ + } /* end if selret > 0 */ + return False; +} + +/**************************************************************************** + Construct and send a netbios DGRAM. + Note that this currently sends all packets to port 138. +**************************************************************************/ + +BOOL send_mailslot(BOOL unique, char *mailslot,char *buf,int len, + char *srcname, int src_type, + char *dstname, int dest_type, + struct in_addr dest_ip,struct in_addr src_ip) +{ + BOOL loopback_this_packet = False; + struct packet_struct p; + struct dgram_packet *dgram = &p.packet.dgram; + char *ptr,*p2; + char tmp[4]; + + bzero((char *)&p,sizeof(p)); + + if(ismyip(dest_ip)) + loopback_this_packet = True; + + generate_name_trn_id(); + + /* DIRECT GROUP or UNIQUE datagram. */ + dgram->header.msg_type = unique ? 0x10 : 0x11; + 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,0,scope); + make_nmb_name(&dgram->dest_name,dstname,dest_type,scope); + + ptr = &dgram->data[0]; + + /* 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; + + DEBUG(4,("send_mailslot: Sending to mailslot %s from %s IP %s ", mailslot, + namestr(&dgram->source_name), inet_ntoa(src_ip))); + DEBUG(4,("to %s IP %s\n", namestr(&dgram->dest_name), inet_ntoa(dest_ip))); + + debug_browse_data(buf, len); + + if(loopback_this_packet) + { + struct packet_struct *lo_packet = NULL; + DEBUG(5,("send_mailslot: sending packet to ourselves.\n")); + if((lo_packet = copy_packet(&p)) == NULL) + return False; + queue_packet(lo_packet); + return True; + } + else + return(send_packet(&p)); +} diff --git a/source3/nmbd/nmbd_processlogon.c b/source3/nmbd/nmbd_processlogon.c new file mode 100644 index 0000000000..ae917564fe --- /dev/null +++ b/source3/nmbd/nmbd_processlogon.c @@ -0,0 +1,250 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NBT netbios routines and daemon - version 2 + Copyright (C) Andrew Tridgell 1994-1997 + Copyright (C) Luke Kenneth Casson Leighton 1994-1997 + Copyright (C) Jeremy Allison 1994-1997 + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + Revision History: + +*/ + +#include "includes.h" + +extern int DEBUGLEVEL; + +extern pstring myname; +extern fstring myworkgroup; + +/**************************************************************************** +Process a domain logon packet +**************************************************************************/ + +void process_logon_packet(struct packet_struct *p,char *buf,int len, + char *mailslot) +{ + struct dgram_packet *dgram = &p->packet.dgram; + pstring my_name; + fstring reply_name; + BOOL add_slashes = False; + pstring outbuf; + int code,reply_code; + char unknown_byte = 0; + uint16 request_count = 0; + uint16 token = 0; + + uint32 ntversion; + uint16 lmnttoken; + uint16 lm20token; + uint32 allowableaccount; /* Control bits, i.e. 0x80 == workstation trust a/c. */ + uint32 domainsidsize; + uint16 requestcount; + char *domainsid; + char *getdc; + char *uniuser; /* Unicode user name. */ + pstring ascuser; + char *unicomp; /* Unicode computer name. */ + struct smb_passwd *smb_pass; /* To check if machine account exists */ + + if (!lp_domain_logons()) + { + DEBUG(3,("process_logon_packet: Logon packet received from IP %S and domain \ +logons are not enabled.\n", inet_ntoa(p->ip) )); + return; + } + + strcpy(my_name, myname); + strupper(my_name); + + code = SVAL(buf,0); + DEBUG(1,("process_logon_packet: Logon from %s: code = %x\n", inet_ntoa(p->ip), code)); + + switch (code) + { + case 0: + { + char *q = buf + 2; + char *machine = q; + char *user = skip_string(machine,1); + + getdc = skip_string(user,1); + q = skip_string(getdc,1); + unknown_byte = CVAL(q,0); + request_count = SVAL(q,1); + token = SVAL(q,3); + + reply_code = 0x6; + strcpy(reply_name,my_name); + add_slashes = True; + + DEBUG(3,("process_logon_packet: Domain login request from %s at IP %s user=%s token=%x\n", + machine,inet_ntoa(p->ip),user,token)); + + q = outbuf; + SSVAL(q, 0, 6); q += 2; + + strcpy(reply_name, "\\\\"); + strcat(reply_name, my_name); + strcpy(q, reply_name); q = skip_string(q, 1); /* PDC name */ + + SSVAL(q, 0, token); q += 2; + + dump_data(4, outbuf, PTR_DIFF(q, outbuf)); + + send_mailslot(True, getdc, + outbuf,PTR_DIFF(q,outbuf), + dgram->dest_name.name, + dgram->dest_name.name_type, + dgram->source_name.name, + dgram->source_name.name_type, + p->ip, *iface_ip(p->ip)); + break; + } + + case QUERYFORPDC: + { + char *q = buf + 2; + char *machine = q; + + getdc = skip_string(machine,1); + unicomp = skip_string(getdc,1); + + q = align2(unicomp, buf); + + q = skip_unicode_string(q, 1); + + ntversion = IVAL(q, 0); q += 4; + lmnttoken = SVAL(q, 0); q += 2; + lm20token = SVAL(q, 0); q += 2; + + /* Construct reply. */ + + q = outbuf; + SSVAL(q, 0, QUERYFORPDC_R); q += 2; + + strcpy(reply_name,my_name); + strcpy(q, reply_name); q = skip_string(q, 1); /* PDC name */ + + if (strcmp(mailslot, NT_LOGON_MAILSLOT)==0) { + q = align2(q, buf); + + PutUniCode(q, my_name); /* PDC name */ + q = skip_unicode_string(q, 1); + PutUniCode(q, myworkgroup); /* Domain name*/ + q = skip_unicode_string(q, 1); + + SIVAL(q, 0, ntversion); q += 4; + SSVAL(q, 0, lmnttoken); q += 2; + SSVAL(q, 0, lm20token); q += 2; + } + + DEBUG(3,("process_logon_packet: GETDC request from %s at IP %s, \ +reporting %s domain %s 0x%x ntversion=%x lm_nt token=%x lm_20 token=%x\n", + machine,inet_ntoa(p->ip), reply_name, lp_workgroup(), + QUERYFORPDC_R, (uint32)ntversion, (uint32)lmnttoken, + (uint32)lm20token )); + + dump_data(4, outbuf, PTR_DIFF(q, outbuf)); + + send_mailslot(True, getdc, + outbuf,PTR_DIFF(q,outbuf), + dgram->dest_name.name, + dgram->dest_name.name_type, + dgram->source_name.name, + dgram->source_name.name_type, + p->ip, *iface_ip(p->ip)); + return; + } + + case SAMLOGON: + { + char *q = buf + 2; + + requestcount = SVAL(q, 0); q += 2; + unicomp = q; + uniuser = skip_unicode_string(unicomp,1); + getdc = skip_unicode_string(uniuser,1); + q = skip_string(getdc,1); + allowableaccount = IVAL(q, 0); q += 4; + domainsidsize = IVAL(q, 0); q += 4; + domainsid = q; + q += domainsidsize + 3; + ntversion = IVAL(q, 0); q += 4; + lmnttoken = SVAL(q, 0); q += 2; + lm20token = SVAL(q, 0); q += 2; + + DEBUG(3,("process_logon_packet: SAMLOGON sidsize %d ntv %d\n", domainsidsize, ntversion)); + + /* + * If MACHINE$ is in our password database then respond, else ignore. + * Let's ignore the SID. + */ + + strcpy(ascuser, unistr(uniuser)); + DEBUG(3,("process_logon_packet: SAMLOGON user %s\n", ascuser)); + + strcpy(reply_name,"\\\\"); /* Here it wants \\LOGONSERVER. */ + strcpy(reply_name+2,my_name); + + smb_pass = get_smbpwd_entry(ascuser, 0); + + if(!smb_pass) + { + DEBUG(3,("process_logon_packet: SAMLOGON request from %s(%s) for %s, not in password file\n", + unistr(unicomp),inet_ntoa(p->ip), ascuser)); + return; + } + else + { + DEBUG(3,("process_logon_packet: SAMLOGON request from %s(%s) for %s, returning logon svr %s domain %s code %x token=%x\n", + unistr(unicomp),inet_ntoa(p->ip), ascuser, reply_name, myworkgroup, + SAMLOGON_R ,lmnttoken)); + } + + /* Construct reply. */ + + q = outbuf; + SSVAL(q, 0, SAMLOGON_R); q += 2; + + PutUniCode(q, reply_name); q = skip_unicode_string(q, 1); + unistrcpy(q, uniuser); q = skip_unicode_string(q, 1); /* User name (workstation trust account) */ + PutUniCode(q, lp_workgroup()); q = skip_unicode_string(q, 1); /* Domain name. */ + + SIVAL(q, 0, ntversion); q += 4; + SSVAL(q, 0, lmnttoken); q += 2; + SSVAL(q, 0, lm20token); q += 2; + + dump_data(4, outbuf, PTR_DIFF(q, outbuf)); + + send_mailslot(True, getdc, + outbuf,PTR_DIFF(q,outbuf), + dgram->dest_name.name, + dgram->dest_name.name_type, + dgram->source_name.name, + dgram->source_name.name_type, + p->ip, *iface_ip(p->ip)); + break; + } + + default: + { + DEBUG(3,("process_logon_packet: Unknown domain request %d\n",code)); + return; + } + } +} diff --git a/source3/nmbd/nmbd_responserecordsdb.c b/source3/nmbd/nmbd_responserecordsdb.c new file mode 100644 index 0000000000..bc0c0745f5 --- /dev/null +++ b/source3/nmbd/nmbd_responserecordsdb.c @@ -0,0 +1,238 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NBT netbios library routines + Copyright (C) Andrew Tridgell 1994-1997 + Copyright (C) Luke Kenneth Casson Leighton 1994-1997 + Copyright (C) Jeremy Allison 1994-1997 + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "includes.h" + +extern int ClientNMB; + +extern int DEBUGLEVEL; + +extern pstring scope; +extern pstring myname; +extern struct in_addr ipzero; + +int num_response_packets = 0; + +/*************************************************************************** + Add an expected response record into the list + **************************************************************************/ + +void add_response_record(struct subnet_record *subrec, + struct response_record *rrec) +{ + struct response_record *rrec2; + + num_response_packets++; /* count of total number of packets still around */ + + DEBUG(4,("add_response_record: adding response record id:%hu to subnet %s. num_records:%d\n", + rrec->response_id, subrec->subnet_name, num_response_packets)); + + if (!subrec->responselist) + { + subrec->responselist = rrec; + rrec->prev = NULL; + rrec->next = NULL; + return; + } + + for (rrec2 = subrec->responselist; rrec2->next; rrec2 = rrec2->next) + ; + + rrec2->next = rrec; + rrec->next = NULL; + rrec->prev = rrec2; +} + +/*************************************************************************** + Remove an expected response record from the list + **************************************************************************/ + +void remove_response_record(struct subnet_record *subrec, + struct response_record *rrec) +{ + if (rrec->prev) + rrec->prev->next = rrec->next; + if (rrec->next) + rrec->next->prev = rrec->prev; + + if (subrec->responselist == rrec) + subrec->responselist = rrec->next; + + if(rrec->userdata) + { + if(rrec->userdata->free_fn) + (*rrec->userdata->free_fn)(rrec->userdata); + else + free((char *)rrec->userdata); + } + + /* Ensure we can delete. */ + rrec->packet->locked = False; + free_packet(rrec->packet); + + free((char *)rrec); + + num_response_packets--; /* count of total number of packets still around */ +} + +/**************************************************************************** + Create a response record for an outgoing packet. + **************************************************************************/ + +struct response_record *make_response_record( struct subnet_record *subrec, + struct packet_struct *p, + response_function resp_fn, + timeout_response_function timeout_fn, + success_function success_fn, + fail_function fail_fn, + struct userdata_struct *userdata) +{ + struct response_record *rrec; + struct nmb_packet *nmb = &p->packet.nmb; + + if (!(rrec = (struct response_record *)malloc(sizeof(*rrec)))) + { + DEBUG(0,("make_response_queue_record: malloc fail for response_record.\n")); + return NULL; + } + + bzero((char *)rrec, sizeof(*rrec)); + + rrec->response_id = nmb->header.name_trn_id; + + rrec->resp_fn = resp_fn; + rrec->timeout_fn = timeout_fn; + rrec->success_fn = success_fn; + rrec->fail_fn = fail_fn; + + rrec->packet = p; + + if(userdata) + { + /* Intelligent userdata. */ + if(userdata->copy_fn) + { + if((rrec->userdata = (*userdata->copy_fn)(userdata)) == NULL) + { + DEBUG(0,("make_response_queue_record: copy fail for userdata.\n")); + free(rrec); + return NULL; + } + } + else + { + /* Primitive userdata, do a memcpy. */ + if((rrec->userdata = (struct userdata_struct *) + malloc(sizeof(struct userdata_struct)+userdata->userdata_len)) == NULL) + { + DEBUG(0,("make_response_queue_record: malloc fail for userdata.\n")); + free(rrec); + return NULL; + } + rrec->userdata->copy_fn = userdata->copy_fn; + rrec->userdata->free_fn = userdata->free_fn; + rrec->userdata->userdata_len = userdata->userdata_len; + memcpy(rrec->userdata->data, userdata->data, userdata->userdata_len); + } + } + else + rrec->userdata = NULL; + + rrec->num_msgs = 0; + + if(!nmb->header.nm_flags.bcast) + rrec->repeat_interval = 5; /* 5 seconds for unicast packets. */ + else + rrec->repeat_interval = 1; /* XXXX should be in ms */ + rrec->repeat_count = 3; /* 3 retries */ + rrec->repeat_time = time(NULL) + rrec->repeat_interval; /* initial retry time */ + + /* Lock the packet so we won't lose it while it's on the list. */ + p->locked = True; + + add_response_record(subrec, rrec); + + return rrec; +} + +/**************************************************************************** + Find a response in a subnet's name query response list. + **************************************************************************/ + +static struct response_record *find_response_record_on_subnet( + struct subnet_record *subrec, uint16 id) +{ + struct response_record *rrec = NULL; + + for (rrec = subrec->responselist; rrec; rrec = rrec->next) + { + if (rrec->response_id == id) + { + DEBUG(4, ("find_response_record: found response record id = %hu on subnet %s\n", + id, subrec->subnet_name)); + break; + } + } + return rrec; +} + +/**************************************************************************** + Find a response in any subnet's name query response list. + **************************************************************************/ + +struct response_record *find_response_record(struct subnet_record **ppsubrec, + uint16 id) +{ + struct response_record *rrec = NULL; + + for ((*ppsubrec) = FIRST_SUBNET; (*ppsubrec); + (*ppsubrec) = NEXT_SUBNET_INCLUDING_UNICAST(*ppsubrec)) + { + if((rrec = find_response_record_on_subnet(*ppsubrec, id)) != NULL) + return rrec; + } + + /* There should never be response records on the remote_broadcast subnet. + Sanity check to ensure this is so. */ + if(remote_broadcast_subnet->responselist != NULL) + { + DEBUG(0,("find_response_record: response record found on subnet %s. This should \ +never happen !\n", remote_broadcast_subnet->subnet_name)); + } + + /* Now check the WINS server subnet if it exists. */ + if(wins_server_subnet != NULL) + { + *ppsubrec = wins_server_subnet; + if((rrec = find_response_record_on_subnet(*ppsubrec, id))!= NULL) + return rrec; + } + + DEBUG(0,("find_response_record: repsonse packet id %hu received with no \ +matching record.\n", id)); + + *ppsubrec = NULL; + + return NULL; +} diff --git a/source3/nmbd/nmbd_sendannounce.c b/source3/nmbd/nmbd_sendannounce.c new file mode 100644 index 0000000000..e4b288aea5 --- /dev/null +++ b/source3/nmbd/nmbd_sendannounce.c @@ -0,0 +1,477 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NBT netbios routines and daemon - version 2 + Copyright (C) Andrew Tridgell 1994-1997 + Copyright (C) Luke Kenneth Casson Leighton 1994-1997 + Copyright (C) Jeremy Allison 1994-1997 + + SMB Version handling + Copyright (C) John H Terpstra 1995-1997 + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "includes.h" + +extern int DEBUGLEVEL; +extern pstring myname; +extern fstring myworkgroup; +extern char **my_netbios_names; +extern int updatecount; + +/**************************************************************************** + Send a browser reset packet. +**************************************************************************/ + +void send_browser_reset(int reset_type, char *to_name, int to_type, struct in_addr to_ip) +{ + pstring outbuf; + char *p; + + DEBUG(3,("send_browser_reset: sending reset request type %d to %s<%02x> IP %s.\n", + reset_type, to_name, to_type, inet_ntoa(to_ip) )); + + bzero(outbuf,sizeof(outbuf)); + p = outbuf; + CVAL(p,0) = ANN_ResetBrowserState; + p++; + CVAL(p,0) = reset_type; + p++; + + send_mailslot(True, BROWSE_MAILSLOT, outbuf,PTR_DIFF(p,outbuf), + myname, 0x0, to_name, to_type, to_ip, FIRST_SUBNET->myip); +} + +/**************************************************************************** + Broadcast a packet to the local net requesting that all servers in this + workgroup announce themselves to us. + **************************************************************************/ + +void broadcast_announce_request(struct subnet_record *subrec, struct work_record *work) +{ + pstring outbuf; + char *p; + + work->needannounce = True; + + DEBUG(3,("broadcast_announce_request: sending announce request for workgroup %s \ +to subnet %s\n", work->work_group, subrec->subnet_name)); + + bzero(outbuf,sizeof(outbuf)); + p = outbuf; + CVAL(p,0) = ANN_AnnouncementRequest; + p++; + + CVAL(p,0) = work->token; /* (local) Unique workgroup token id. */ + p++; + StrnCpy(p,myname,15); + strupper(p); + p = skip_string(p,1); + + send_mailslot(False, BROWSE_MAILSLOT, outbuf,PTR_DIFF(p,outbuf), + myname, 0x0, work->work_group,0x1e, subrec->bcast_ip, subrec->myip); +} + +/**************************************************************************** + Broadcast an announcement. + **************************************************************************/ + +static void send_announcement(struct subnet_record *subrec, int announce_type, + char *from_name, char *to_name, int to_type, struct in_addr to_ip, + time_t announce_interval, + char *server_name, int server_type, char *server_comment) +{ + pstring outbuf; + char *p; + + bzero(outbuf,sizeof(outbuf)); + p = outbuf+1; + + CVAL(outbuf,0) = announce_type; + + /* Announcement parameters. */ + CVAL(p,0) = updatecount; + SIVAL(p,1,announce_interval*1000); /* Milliseconds - despite the spec. */ + + StrnCpy(p+5,server_name,15); + strupper(p+5); + + CVAL(p,21) = lp_major_announce_version(); /* Major version. */ + CVAL(p,22) = lp_minor_announce_version(); /* Minor version. */ + + SIVAL(p,23,server_type & ~SV_TYPE_LOCAL_LIST_ONLY); + /* Browse version: got from NT/AS 4.00 - Value defined in smb.h (JHT). */ + SSVAL(p,27,BROWSER_ELECTION_VERSION); + SSVAL(p,29,BROWSER_CONSTANT); /* Browse signature. */ + + pstrcpy(p+31,server_comment); + p += 31; + p = skip_string(p,1); + + send_mailslot(False,BROWSE_MAILSLOT, outbuf, PTR_DIFF(p,outbuf), + from_name, 0x0, to_name, to_type, to_ip, subrec->myip); +} + +/**************************************************************************** + We are a local master browser. Announce this to WORKGROUP<1e>. +****************************************************************************/ + +static void send_local_master_announcement(struct subnet_record *subrec, struct work_record *work, + struct server_record *servrec) +{ + /* Ensure we don't have the prohibited bit set. */ + uint32 type = servrec->serv.type & ~SV_TYPE_LOCAL_LIST_ONLY; + + DEBUG(3,("send_local_master_announcement: type %x for name %s on subnet %s for workgroup %s\n", + type, myname, subrec->subnet_name, work->work_group)); + + send_announcement(subrec, ANN_LocalMasterAnnouncement, + myname, /* From nbt name. */ + work->work_group, 0x1e, /* To nbt name. */ + subrec->bcast_ip, /* To ip. */ + work->announce_interval, /* Time until next announce. */ + myname, /* Name to announce. */ + type, /* Type field. */ + servrec->serv.comment); +} + +/**************************************************************************** + Announce the workgroup WORKGROUP to MSBROWSE<01>. +****************************************************************************/ + +static void send_workgroup_announcement(struct subnet_record *subrec, struct work_record *work) +{ + DEBUG(3,("send_workgroup_announcement: on subnet %s for workgroup %s\n", + subrec->subnet_name, work->work_group)); + + send_announcement(subrec, ANN_DomainAnnouncement, + myname, /* From nbt name. */ + MSBROWSE, 0x1, /* To nbt name. */ + subrec->bcast_ip, /* To ip. */ + work->announce_interval, /* Time until next announce. */ + work->work_group, /* Name to announce. */ + SV_TYPE_DOMAIN_ENUM|SV_TYPE_NT, /* workgroup announce flags. */ + myname); /* From name as comment. */ +} + +/**************************************************************************** + Announce the given host to WORKGROUP<1d>. +****************************************************************************/ + +static void send_host_announcement(struct subnet_record *subrec, struct work_record *work, + struct server_record *servrec) +{ + /* Ensure we don't have the prohibited bits set. */ + uint32 type = servrec->serv.type & ~SV_TYPE_LOCAL_LIST_ONLY; + + DEBUG(3,("send_host_announcement: type %x for host %s on subnet %s for workgroup %s\n", + type, servrec->serv.name, subrec->subnet_name, work->work_group)); + + send_announcement(subrec, ANN_HostAnnouncement, + servrec->serv.name, /* From nbt name. */ + work->work_group, 0x1d, /* To nbt name. */ + subrec->bcast_ip, /* To ip. */ + work->announce_interval, /* Time until next announce. */ + servrec->serv.name, /* Name to announce. */ + type, /* Type field. */ + servrec->serv.comment); +} + +/**************************************************************************** + Announce a server record. + ****************************************************************************/ + +static void announce_server(struct subnet_record *subrec, struct work_record *work, + struct server_record *servrec) +{ + /* Only do domain announcements if we are a master and it's + our primary name we're being asked to announce. */ + + if (AM_LOCAL_MASTER_BROWSER(work) && strequal(myname,servrec->serv.name)) + { + send_local_master_announcement(subrec, work, servrec); + send_workgroup_announcement(subrec, work); + } + else + { + send_host_announcement(subrec, work, servrec); + } +} + +/**************************************************************************** + Go through all my registered names on all broadcast subnets and announce + them if the timeout requires it. + **************************************************************************/ + +void announce_my_server_names(time_t t) +{ + struct subnet_record *subrec; + + for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) + { + struct work_record *work = find_workgroup_on_subnet(subrec, myworkgroup); + + if(work) + { + struct server_record *servrec; + + if (work->needannounce) + { + /* Drop back to a max 3 minute announce. This is to prevent a + single lost packet from breaking things for too long. */ + + work->announce_interval = MIN(work->announce_interval, + CHECK_TIME_MIN_HOST_ANNCE*60); + work->lastannounce_time = t - (work->announce_interval+1); + work->needannounce = False; + } + + /* Announce every minute at first then progress to every 12 mins */ + if ((t - work->lastannounce_time) < work->announce_interval) + continue; + + if (work->announce_interval < (CHECK_TIME_MAX_HOST_ANNCE * 60)) + work->announce_interval += 60; + + work->lastannounce_time = t; + + for (servrec = work->serverlist; servrec; servrec = servrec->next) + { + if (is_myname(servrec->serv.name)) + announce_server(subrec, work, servrec); + } + } /* if work */ + } /* for subrec */ +} + +/* Announce timer. Moved into global static so it can be reset + when a machine becomes a local master browser. */ +static time_t announce_timer_last=0; + +/**************************************************************************** + Reset the announce_timer so that a local master browser announce will be done + immediately. + ****************************************************************************/ + +void reset_announce_timer() +{ + announce_timer_last = time(NULL) - (CHECK_TIME_MST_ANNOUNCE * 60); +} + +/**************************************************************************** + Announce myself as a local master browser to a domain master browser. + **************************************************************************/ + +void announce_myself_to_domain_master_browser(time_t t) +{ + struct subnet_record *subrec; + struct work_record *work; + + if(!we_are_a_wins_client()) + { + DEBUG(10,("announce_myself_to_domain_master_browser: no unicast subnet, ignoring.\n")); + return; + } + + if (!announce_timer_last) + announce_timer_last = t; + + if ((t-announce_timer_last) < (CHECK_TIME_MST_ANNOUNCE * 60)) + { + DEBUG(10,("announce_myself_to_domain_master_browser: t (%d) - last(%d) < %d\n", + t, announce_timer_last, CHECK_TIME_MST_ANNOUNCE * 60 )); + return; + } + + announce_timer_last = t; + + /* Look over all our broadcast subnets to see if any of them + has the state set as local master browser. */ + + for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) + { + for (work = subrec->workgrouplist; work; work = work->next) + { + if (AM_LOCAL_MASTER_BROWSER(work)) + { + DEBUG(4,( "announce_myself_to_domain_master_browser: I am a local master browser for \ +workgroup %s on subnet %s\n", work->work_group, subrec->subnet_name)); + + /* Look in nmbd_browsersync.c for the rest of this code. */ + announce_and_sync_with_domain_master_browser(subrec, work); + } + } + } +} + +/**************************************************************************** +Announce all samba's server entries as 'gone'. +This must *only* be called on shutdown. +****************************************************************************/ + +void announce_my_servers_removed(void) +{ + struct subnet_record *subrec; + for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) + { + struct work_record *work; + for (work = subrec->workgrouplist; work; work = work->next) + { + struct server_record *servrec; + + work->announce_interval = 0; + for (servrec = work->serverlist; servrec; servrec = servrec->next) + { + if (!is_myname(servrec->serv.name)) + continue; + servrec->serv.type = 0; + if(AM_LOCAL_MASTER_BROWSER(work)) + send_local_master_announcement(subrec, work, servrec); + send_host_announcement(subrec, work, servrec); + } + } + } +} + +/**************************************************************************** + Do all the "remote" announcements. These are used to put ourselves + on a remote browse list. They are done blind, no checking is done to + see if there is actually a local master browser at the other end. + **************************************************************************/ + +void announce_remote(time_t t) +{ + char *s,*ptr; + static time_t last_time = 0; + pstring s2; + struct in_addr addr; + char *comment; + int stype = lp_default_server_announce(); + + if (last_time && (t < (last_time + REMOTE_ANNOUNCE_INTERVAL))) + return; + + last_time = t; + + s = lp_remote_announce(); + if (!*s) + return; + + comment = lp_serverstring(); + + for (ptr=s; next_token(&ptr,s2,NULL); ) + { + /* The entries are of the form a.b.c.d/WORKGROUP with + WORKGROUP being optional */ + char *wgroup; + int i; + + wgroup = strchr(s2,'/'); + if (wgroup) + *wgroup++ = 0; + if (!wgroup || !*wgroup) + wgroup = myworkgroup; + + addr = *interpret_addr2(s2); + + /* Announce all our names including aliases */ + /* Give the ip address as the address of our first + broadcast subnet. */ + + for(i=0; my_netbios_names[i]; i++) + { + char *name = my_netbios_names[i]; + + DEBUG(5,("announce_remote: Doing remote announce for server %s to IP %s.\n", + name, inet_ntoa(addr) )); + + send_announcement(FIRST_SUBNET, ANN_HostAnnouncement, + name, /* From nbt name. */ + wgroup, 0x1e, /* To nbt name. */ + addr, /* To ip. */ + REMOTE_ANNOUNCE_INTERVAL, /* Time until next announce. */ + name, /* Name to announce. */ + stype, /* Type field. */ + comment); + } + } +} + +/**************************************************************************** + Implement the 'remote browse sync' feature Andrew added. + These are used to put our browse lists into remote browse lists. + **************************************************************************/ + +void browse_sync_remote(time_t t) +{ + char *s,*ptr; + static time_t last_time = 0; + pstring s2; + struct in_addr addr; + struct work_record *work; + pstring outbuf; + char *p; + + if (last_time && (t < (last_time + REMOTE_ANNOUNCE_INTERVAL))) + return; + + last_time = t; + + s = lp_remote_browse_sync(); + if (!*s) + return; + + /* + * We only do this if we are the local master browser + * for our workgroup on the firsst subnet. + */ + + if((work = find_workgroup_on_subnet(FIRST_SUBNET, myworkgroup)) == NULL) + { + DEBUG(0,("browse_sync_remote: Cannot find workgroup %s on subnet %s\n", + myworkgroup, FIRST_SUBNET->subnet_name )); + return; + } + + if(!AM_LOCAL_MASTER_BROWSER(work)) + { + DEBUG(5,("browse_sync_remote: We can only do this if we are a local master browser \ +for workgroup %s on subnet %s.\n", myworkgroup, FIRST_SUBNET->subnet_name )); + return; + } + + bzero(outbuf,sizeof(outbuf)); + p = outbuf; + CVAL(p,0) = ANN_MasterAnnouncement; + p++; + + StrnCpy(p,myname,15); + strupper(p); + p = skip_string(p,1); + + for (ptr=s; next_token(&ptr,s2,NULL); ) + { + /* The entries are of the form a.b.c.d */ + addr = *interpret_addr2(s2); + + DEBUG(5,("announce_remote: Doing remote browse sync announce for server %s to IP %s.\n", + myname, inet_ntoa(addr) )); + + send_mailslot(True, BROWSE_MAILSLOT, outbuf,PTR_DIFF(p,outbuf), + myname, 0x0, "*", 0x0, addr, FIRST_SUBNET->myip); + } +} diff --git a/source3/nmbd/nmbd_serverlistdb.c b/source3/nmbd/nmbd_serverlistdb.c new file mode 100644 index 0000000000..1281fe2ee3 --- /dev/null +++ b/source3/nmbd/nmbd_serverlistdb.c @@ -0,0 +1,454 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NBT netbios routines and daemon - version 2 + Copyright (C) Andrew Tridgell 1994-1997 + Copyright (C) Luke Kenneth Casson Leighton 1994-1997 + Copyright (C) Jeremy Allison 1994-1997 + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "includes.h" +#include "smb.h" + +extern int ClientNMB; + +extern int DEBUGLEVEL; + +extern pstring myname; +extern fstring myworkgroup; +extern char **my_netbios_names; + +int updatecount = 0; + +/******************************************************************* + Remove all the servers in a work group. + ******************************************************************/ + +void remove_all_servers(struct work_record *work) +{ + struct server_record *servrec; + struct server_record *nexts; + + for (servrec = work->serverlist; servrec; servrec = nexts) + { + DEBUG(7,("remove_all_servers: Removing server %s\n",servrec->serv.name)); + nexts = servrec->next; + + if (servrec->prev) + servrec->prev->next = servrec->next; + if (servrec->next) + servrec->next->prev = servrec->prev; + + if (work->serverlist == servrec) + work->serverlist = servrec->next; + + free((char *)servrec); + + } + + work->subnet->work_changed = True; +} + +/*************************************************************************** + Add a server into the a workgroup serverlist. + **************************************************************************/ + +static void add_server_to_workgroup(struct work_record *work, + struct server_record *servrec) +{ + struct server_record *servrec2; + + if (!work->serverlist) + { + work->serverlist = servrec; + servrec->prev = NULL; + servrec->next = NULL; + return; + } + + for (servrec2 = work->serverlist; servrec2->next; servrec2 = servrec2->next) + ; + + servrec2->next = servrec; + servrec->next = NULL; + servrec->prev = servrec2; + work->subnet->work_changed = True; +} + +/**************************************************************************** + Find a server in a server list. + **************************************************************************/ + +struct server_record *find_server_in_workgroup(struct work_record *work, char *name) +{ + struct server_record *ret; + + for (ret = work->serverlist; ret; ret = ret->next) + { + if (strequal(ret->serv.name,name)) + return ret; + } + return NULL; +} + + +/**************************************************************************** + Remove a server entry from this workgroup. + ****************************************************************************/ + +static void remove_server_from_workgroup(struct work_record *work, struct server_record *servrec) +{ + if (servrec->prev) + servrec->prev->next = servrec->next; + if (servrec->next) + servrec->next->prev = servrec->prev; + + if (work->serverlist == servrec) + work->serverlist = servrec->next; + + free((char *)servrec); + work->subnet->work_changed = True; +} + +/**************************************************************************** + Create a server entry on this workgroup. + ****************************************************************************/ + +struct server_record *create_server_on_workgroup(struct work_record *work, + char *name,int servertype, + int ttl,char *comment) +{ + struct server_record *servrec; + + if (name[0] == '*') + { + DEBUG(7,("create_server_on_workgroup: not adding name starting with '*' (%s)\n", + name)); + return (NULL); + } + + if((servrec = find_server_in_workgroup(work, name)) != NULL) + { + DEBUG(0,("create_server_on_workgroup: Server %s already exists on \ +workgroup %s. This is a bug.\n", name, work->work_group)); + return NULL; + } + + if((servrec = (struct server_record *)malloc(sizeof(*servrec))) == NULL) + { + DEBUG(0,("create_server_entry_on_workgroup: malloc fail !\n")); + return NULL; + } + + bzero((char *)servrec,sizeof(*servrec)); + + servrec->subnet = work->subnet; + + StrnCpy(servrec->serv.name,name,sizeof(servrec->serv.name)-1); + StrnCpy(servrec->serv.comment,comment,sizeof(servrec->serv.comment)-1); + strupper(servrec->serv.name); + servrec->serv.type = servertype; + + update_server_ttl(servrec, ttl); + + add_server_to_workgroup(work, servrec); + + DEBUG(3,("create_server_on_workgroup: Created server entry %s of type %x (%s) on \ +workgroup %s.\n", name,servertype,comment, work->work_group)); + + work->subnet->work_changed = True; + + return(servrec); +} + +/******************************************************************* + Update the ttl field of a server record. +*******************************************************************/ + +void update_server_ttl(struct server_record *servrec, int ttl) +{ + if(ttl > lp_max_ttl()) + ttl = lp_max_ttl(); + + if(is_myname(servrec->serv.name)) + servrec->death_time = PERMANENT_TTL; + else + servrec->death_time = (ttl != PERMANENT_TTL) ? time(NULL)+(ttl*3) : PERMANENT_TTL; + + servrec->subnet->work_changed = True; +} + +/******************************************************************* + Expire old servers in the serverlist. A time of -1 indicates + everybody dies except those with a death_time of PERMANENT_TTL (which is 0). + This should only be called from expire_workgroups_and_servers(). + ******************************************************************/ + +void expire_servers(struct work_record *work, time_t t) +{ + struct server_record *servrec; + struct server_record *nexts; + + for (servrec = work->serverlist; servrec; servrec = nexts) + { + nexts = servrec->next; + + if ((servrec->death_time != PERMANENT_TTL) && ((t == -1) || (servrec->death_time < t))) + { + DEBUG(3,("expire_old_servers: Removing timed out server %s\n",servrec->serv.name)); + remove_server_from_workgroup(work, servrec); + work->subnet->work_changed = True; + } + } +} + +/******************************************************************* + Decide if we should write out a server record for this server. + We return zero if we should not. Check if we've already written + out this server record from an earlier subnet. +******************************************************************/ + +static uint32 write_this_server_name( struct subnet_record *subrec, + struct work_record *work, + struct server_record *servrec) +{ + struct subnet_record *ssub; + struct work_record *iwork; + struct server_record *sserv; + + /* Go through all the subnets we have already seen. */ + for (ssub = FIRST_SUBNET; ssub != subrec; ssub = NEXT_SUBNET_INCLUDING_UNICAST(ssub)) + { + for(iwork = ssub->workgrouplist; iwork; iwork = iwork->next) + { + if((sserv = find_server_in_workgroup( iwork, servrec->serv.name)) != NULL) + { + /* + * We have already written out this server record, don't + * do it again. This gives precedence to servers we have seen + * on the broadcast subnets over servers that may have been + * added via a sync on the unicast_subet. + * + * The correct way to do this is to have a serverlist file + * per subnet - this means changes to smbd as well. I may + * add this at a later date (JRA). + */ + + return 0; + } + } + } + + return servrec->serv.type; +} + +/******************************************************************* + Decide if we should write out a workgroup record for this workgroup. + We return zero if we should not. Don't write out myworkgroup (we've + already done it) and also don't write out a second workgroup record + on the unicast subnet that we've already written out on one of the + broadcast subnets. +******************************************************************/ + +static uint32 write_this_workgroup_name( struct subnet_record *subrec, + struct work_record *work) +{ + struct subnet_record *ssub; + + if(strequal(myworkgroup, work->work_group)) + return 0; + + /* This is a workgroup we have seen on a broadcast subnet. All + these have the same type. */ + + if(subrec != unicast_subnet) + return (SV_TYPE_DOMAIN_ENUM|SV_TYPE_NT|SV_TYPE_LOCAL_LIST_ONLY); + + for(ssub = FIRST_SUBNET; ssub; ssub = NEXT_SUBNET_EXCLUDING_UNICAST(ssub)) + { + /* This is the unicast subnet so check if we've already written out + this subnet when we passed over the broadcast subnets. */ + + if(find_workgroup_on_subnet( ssub, work->work_group) != NULL) + return 0; + } + + /* All workgroups on the unicast subnet (except our own, which we + have already written out) cannot be local. */ + + return (SV_TYPE_DOMAIN_ENUM|SV_TYPE_NT); +} + +/******************************************************************* + Write out the browse.dat file. + ******************************************************************/ + +void write_browse_list(time_t t, BOOL force_write) +{ + struct subnet_record *subrec; + struct work_record *work; + struct server_record *servrec; + pstring fname,fnamenew; + uint32 stype; + fstring tmp; + int i; + FILE *fp; + BOOL list_changed = force_write; + static time_t lasttime = 0; + + if (!lasttime) + lasttime = t; + if (t - lasttime < 5) + return; + + for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec)) + { + if(subrec->work_changed) + { + list_changed = True; + break; + } + } + + if(!list_changed) + return; + + lasttime = t; + updatecount++; + + dump_workgroups(); + + pstrcpy(fname,lp_lockdir()); + trim_string(fname,NULL,"/"); + strcat(fname,"/"); + strcat(fname,SERVER_LIST); + pstrcpy(fnamenew,fname); + strcat(fnamenew,"."); + + fp = fopen(fnamenew,"w"); + + if (!fp) + { + DEBUG(0,("write_browse_list: Can't open file %s. Error was %s\n", + fnamenew,strerror(errno))); + return; + } + + /* + * Write out a record for our workgroup. Use the record from the first + * subnet. + */ + + if((work = find_workgroup_on_subnet(FIRST_SUBNET, myworkgroup)) == NULL) + { + DEBUG(0,("write_browse_list: Fatal error - cannot find my workgroup %s\n", + myworkgroup)); + fclose(fp); + return; + } + + sprintf(tmp, "\"%s\"", work->work_group); + fprintf(fp, "%-25s ", tmp); + fprintf(fp, "%08x ", SV_TYPE_DOMAIN_ENUM|SV_TYPE_NT|SV_TYPE_LOCAL_LIST_ONLY); + sprintf(tmp, "\"%s\" ", *work->local_master_browser_name ? work->local_master_browser_name : "Unknown"); + fprintf(fp, "%-30s", tmp); + fprintf(fp, "\"%s\"\n", work->work_group); + + /* + * We need to do something special for our own names. + * This is due to the fact that we may be a local master browser on + * one of our broadcast subnets, and a domain master on the unicast + * subnet. We iterate over the subnets and only write out the name + * once. + */ + + for (i=0; my_netbios_names[i]; i++) + { + stype = 0; + for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec)) + { + if((work = find_workgroup_on_subnet( subrec, myworkgroup )) == NULL) + continue; + if((servrec = find_server_in_workgroup( work, my_netbios_names[i])) == NULL) + continue; + + stype |= servrec->serv.type; + } + + /* Output server details, plus what workgroup they're in. */ + sprintf(tmp, "\"%s\"", my_netbios_names[i]); + fprintf(fp, "%-25s ", tmp); + fprintf(fp, "%08x ", stype); + sprintf(tmp, "\"%s\" ", lp_serverstring()); + fprintf(fp, "%-30s", tmp); + fprintf(fp, "\"%s\"\n", myworkgroup); + } + + for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec)) + { + subrec->work_changed = False; + + for (work = subrec->workgrouplist; work ; work = work->next) + { + /* Write out a workgroup record for a workgroup. */ + uint32 wg_type = write_this_workgroup_name( subrec, work); + + if(wg_type) + { + sprintf(tmp, "\"%s\"", work->work_group); + fprintf(fp, "%-25s ", tmp); + + fprintf(fp, "%08x ", wg_type); + sprintf(tmp, "\"%s\" ", *work->local_master_browser_name ? + work->local_master_browser_name : "Unknown" ); + fprintf(fp, "%-30s", tmp); + fprintf(fp, "\"%s\"\n", work->work_group); + } + + /* Now write out any server records a workgroup may have. */ + + for (servrec = work->serverlist; servrec ; servrec = servrec->next) + { + uint32 serv_type; + + /* We have already written our names here. */ + if(is_myname(servrec->serv.name)) + continue; + + serv_type = write_this_server_name(subrec, work, servrec); + + if(serv_type) + { + /* Output server details, plus what workgroup they're in. */ + sprintf(tmp, "\"%s\"", servrec->serv.name); + fprintf(fp, "%-25s ", tmp); + fprintf(fp, "%08x ", serv_type); + sprintf(tmp, "\"%s\" ", servrec->serv.comment); + fprintf(fp, "%-30s", tmp); + fprintf(fp, "\"%s\"\n", work->work_group); + } + } + } + } + + fclose(fp); + unlink(fname); + chmod(fnamenew,0644); + rename(fnamenew,fname); + DEBUG(3,("write_browse_list: Wrote browse list into file %s\n",fname)); +} diff --git a/source3/nmbd/nmbd_subnetdb.c b/source3/nmbd/nmbd_subnetdb.c new file mode 100644 index 0000000000..93aecc21f2 --- /dev/null +++ b/source3/nmbd/nmbd_subnetdb.c @@ -0,0 +1,291 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NBT netbios routines and daemon - version 2 + Copyright (C) Andrew Tridgell 1994-1997 + Copyright (C) Luke Kenneth Casson Leighton 1994-1997 + Copyright (C) Jeremy Allison 1994-1997 + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + Revision History: + +*/ + +#include "includes.h" +#include "smb.h" + +extern int ClientNMB; +extern int ClientDGRAM; +extern int global_nmb_port; + +extern int DEBUGLEVEL; + +extern pstring myname; +extern fstring myworkgroup; +extern char **my_netbios_names; +extern struct in_addr ipzero; + +/* This is the broadcast subnets database. */ +struct subnet_record *subnetlist = NULL; + +/* Extra subnets - keep these separate so enumeration code doesn't + run onto it by mistake. */ + +struct subnet_record *unicast_subnet = NULL; +struct subnet_record *remote_broadcast_subnet = NULL; +struct subnet_record *wins_server_subnet = NULL; + +extern uint16 samba_nb_type; /* Samba's NetBIOS name type. */ + +/**************************************************************************** + Add a subnet into the list. + **************************************************************************/ + +static void add_subnet(struct subnet_record *subrec) +{ + struct subnet_record *subrec2; + + if (!subnetlist) + { + subnetlist = subrec; + subrec->prev = NULL; + subrec->next = NULL; + return; + } + + for (subrec2 = subnetlist; subrec2->next; subrec2 = subrec2->next) + ; + + subrec2->next = subrec; + subrec->next = NULL; + subrec->prev = subrec2; +} + +/**************************************************************************** + Create a subnet entry. + ****************************************************************************/ + +static struct subnet_record *make_subnet(char *name, enum subnet_type type, + struct in_addr myip, struct in_addr bcast_ip, + struct in_addr mask_ip) +{ + struct subnet_record *subrec = NULL; + int nmb_sock, dgram_sock; + + /* Check if we are creating a non broadcast subnet - if so don't create + sockets. + */ + + if(type != NORMAL_SUBNET) + { + nmb_sock = -1; + dgram_sock = -1; + } + else + { + /* + * Attempt to open the sockets on port 137/138 for this interface + * and bind them. + * Fail the subnet creation if this fails. + */ + + if((nmb_sock = open_socket_in(SOCK_DGRAM, global_nmb_port,0, myip.s_addr)) == -1) + { + DEBUG(0,("make_subnet: Failed to open nmb socket on interface %s \ +for port %d. Error was %s\n", inet_ntoa(myip), global_nmb_port, strerror(errno))); + return NULL; + } + + if((dgram_sock = open_socket_in(SOCK_DGRAM,DGRAM_PORT,3, myip.s_addr)) == -1) + { + DEBUG(0,("make_subnet: Failed to open dgram socket on interface %s \ +for port %d. Error was %s\n", inet_ntoa(myip), DGRAM_PORT, strerror(errno))); + return NULL; + } + + /* Make sure we can broadcast from these sockets. */ + set_socket_options(nmb_sock,"SO_BROADCAST"); + set_socket_options(dgram_sock,"SO_BROADCAST"); + + } + + subrec = (struct subnet_record *)malloc(sizeof(*subrec)); + + if (!subrec) + { + DEBUG(0,("make_subnet: malloc fail !\n")); + close(nmb_sock); + close(dgram_sock); + return(NULL); + } + + bzero((char *)subrec,sizeof(*subrec)); + + if((subrec->subnet_name = strdup(name)) == NULL) + { + DEBUG(0,("make_subnet: malloc fail for subnet name !\n")); + close(nmb_sock); + close(dgram_sock); + free((char *)subrec); + return(NULL); + } + + DEBUG(2, ("making subnet name:%s ", name )); + DEBUG(2, ("Broadcast address:%s ", inet_ntoa(bcast_ip))); + DEBUG(2, ("Subnet mask:%s\n", inet_ntoa(mask_ip))); + + subrec->namelist_changed = False; + subrec->work_changed = False; + + subrec->bcast_ip = bcast_ip; + subrec->mask_ip = mask_ip; + subrec->myip = myip; + subrec->type = type; + subrec->nmb_sock = nmb_sock; + subrec->dgram_sock = dgram_sock; + + return subrec; +} + +/**************************************************************************** + Create subnet entries. +**************************************************************************/ + +BOOL create_subnets() +{ + int num_interfaces = iface_count(); + int i; + struct in_addr unicast_ip; + + if(num_interfaces == 0) + { + DEBUG(0,("create_subnets: No local interfaces !\n")); + return False; + } + + /* + * Create subnets from all the local interfaces and thread them onto + * the linked list. + */ + + for (i = 0 ; i < num_interfaces; i++) + { + struct subnet_record *subrec; + struct interface *iface = get_interface(i); + + if((subrec = make_subnet(inet_ntoa(iface->ip), NORMAL_SUBNET, + iface->ip, iface->bcast,iface->nmask)) == NULL) + return False; + add_subnet(subrec); + } + + /* + * If we have been configured to use a WINS server, then try and + * get the ip address of it here. If we are the WINS server then + * set the unicast subnet address to be the first of our own real + * addresses. + */ + + if(*lp_wins_server()) + { + struct in_addr real_wins_ip; + real_wins_ip = *interpret_addr2(lp_wins_server()); + + if (!zero_ip(real_wins_ip)) + { + unicast_ip = real_wins_ip; + } + else + { + /* The smb.conf's wins server parameter MUST be a host_name + or an ip_address. */ + DEBUG(0,("invalid smb.conf parameter 'wins server'\n")); + return False; + } + } + else if(lp_we_are_a_wins_server()) + { + /* Pick the first interface ip address as the WINS server ip. */ + unicast_ip = *iface_n_ip(0); + } + else + { + /* We should not be using a WINS server at all. Set the + ip address of the subnet to be zero. */ + unicast_ip = ipzero; + } + + /* + * Create the unicast and remote broadcast subnets. + * Don't put these onto the linked list. + * The ip address of the unicast subnet is set to be + * the WINS server address, if it exists, or ipzero if not. + */ + + unicast_subnet = make_subnet( "UNICAST_SUBNET", UNICAST_SUBNET, + unicast_ip, unicast_ip, unicast_ip); + + remote_broadcast_subnet = make_subnet( "REMOTE_BROADCAST_SUBNET", + REMOTE_BROADCAST_SUBNET, + ipzero, ipzero, ipzero); + + if((unicast_subnet == NULL) || (remote_broadcast_subnet == NULL)) + return False; + + /* + * If we are WINS server, create the WINS_SERVER_SUBNET - don't put on + * the linked list. + */ + + if (lp_we_are_a_wins_server()) + { + if((wins_server_subnet = make_subnet("WINS_SERVER_SUBNET", + WINS_SERVER_SUBNET, + ipzero, ipzero, ipzero)) == NULL) + return False; + } + + return True; +} + +/******************************************************************* +Function to tell us if we can use the unicast subnet. +******************************************************************/ + +BOOL we_are_a_wins_client() +{ + static int cache_we_are_a_wins_client = -1; + + if(cache_we_are_a_wins_client == -1) + cache_we_are_a_wins_client = (ip_equal(ipzero, unicast_subnet->myip) ? + False : True); + + return cache_we_are_a_wins_client; +} + +/******************************************************************* +Access function used by NEXT_SUBNET_INCLUDING_UNICAST +******************************************************************/ + +struct subnet_record *get_next_subnet_maybe_unicast(struct subnet_record *subrec) +{ + if(subrec == unicast_subnet) + return NULL; + else if((subrec->next == NULL) && we_are_a_wins_client()) + return unicast_subnet; + else + return subrec->next; +} diff --git a/source3/nmbd/nmbd_winsproxy.c b/source3/nmbd/nmbd_winsproxy.c new file mode 100644 index 0000000000..ed8653b8bf --- /dev/null +++ b/source3/nmbd/nmbd_winsproxy.c @@ -0,0 +1,195 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NBT netbios routines and daemon - version 2 + + Copyright (C) Jeremy Allison 1994-1997 + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "includes.h" + +extern int DEBUGLEVEL; + +/**************************************************************************** +Function called when the name lookup succeeded. +****************************************************************************/ + +static void wins_proxy_name_query_request_success( struct subnet_record *subrec, + struct userdata_struct *userdata, + struct nmb_name *nmbname, struct in_addr ip, struct res_rec *rrec) +{ + struct packet_struct *original_packet; + struct subnet_record *orig_broadcast_subnet; + uint16 nb_flags; + int num_ips; + int i; + int ttl; + struct in_addr *iplist; + + /* Extract the original packet and the original broadcast subnet from + the userdata. */ + + memcpy( (char *)&orig_broadcast_subnet, userdata->data, sizeof(struct subnet_record *) ); + memcpy( (char *)&original_packet, &userdata->data[sizeof(struct subnet_record *)], + sizeof(struct packet_struct *) ); + + nb_flags = get_nb_flags( rrec->rdata ); + + num_ips = rrec->rdlength / 6; + if(num_ips == 0) + { + DEBUG(0,("wins_proxy_name_query_request_success: Invalid number of IP records (0) \ +returned for name %s.\n", namestr(nmbname) )); + return; + } + + if(num_ips == 1) + iplist = &ip; + else + { + if((iplist = (struct in_addr *)malloc( num_ips * sizeof(struct in_addr) )) == NULL) + { + DEBUG(0,("wins_proxy_name_query_request_success: malloc fail !\n")); + return; + } + + for(i = 0; i < num_ips; i++) + putip( (char *)&iplist[i], (char *)&rrec->rdata[ (i*6) + 2]); + } + + /* Add the queried name to the original subnet as a WINS_PROXY_NAME. */ + + if(rrec == PERMANENT_TTL) + ttl = lp_max_ttl(); + + add_name_to_subnet( orig_broadcast_subnet, nmbname->name, nmbname->name_type, + nb_flags, ttl, WINS_PROXY_NAME, num_ips, iplist); + + if(iplist != &ip) + free((char *)iplist); + + /* Finally reply to the original name query. */ + reply_netbios_packet(original_packet, /* Packet to reply to. */ + 0, /* Result code. */ + NMB_QUERY, /* nmbd type code. */ + NMB_NAME_QUERY_OPCODE, /* opcode. */ + ttl, /* ttl. */ + rrec->rdata, /* data to send. */ + rrec->rdlength); /* data length. */ +} + +/**************************************************************************** +Function called when the name lookup failed. +****************************************************************************/ + +static void wins_proxy_name_query_request_fail(struct subnet_record *subrec, + struct response_record *rrec, + struct nmb_name *question_name, int fail_code) +{ + DEBUG(4,("wins_proxy_name_query_request_fail: WINS server returned error code %d for lookup \ +of name %s.\n", fail_code, namestr(question_name) )); +} + +/**************************************************************************** +Function to make a deep copy of the userdata we will need when the WINS +proxy query returns. +****************************************************************************/ + +static struct userdata_struct *wins_proxy_userdata_copy_fn(struct userdata_struct *userdata) +{ + struct packet_struct *p, *copy_of_p; + struct userdata_struct *new_userdata = + (struct userdata_struct *)malloc( userdata->userdata_len ); + + if(new_userdata == NULL) + return NULL; + + new_userdata->copy_fn = userdata->copy_fn; + new_userdata->free_fn = userdata->free_fn; + new_userdata->userdata_len = userdata->userdata_len; + + /* Copy the subnet_record pointer. */ + memcpy( new_userdata->data, userdata->data, sizeof(struct subnet_record *) ); + + /* Extract the pointer to the packet struct */ + memcpy((char *)&p, &userdata->data[sizeof(struct subnet_record *)], + sizeof(struct packet_struct *) ); + + /* Do a deep copy of the packet. */ + if((copy_of_p = copy_packet(p)) == NULL) + { + free((char *)new_userdata); + return NULL; + } + + /* Lock the copy. */ + copy_of_p->locked = True; + + memcpy( &new_userdata->data[sizeof(struct subnet_record *)], (char *)©_of_p, + sizeof(struct packet_struct *) ); + + return new_userdata; +} + +/**************************************************************************** +Function to free the deep copy of the userdata we used when the WINS +proxy query returned. +****************************************************************************/ + +static void wins_proxy_userdata_free_fn(struct userdata_struct *userdata) +{ + struct packet_struct *p; + + /* Extract the pointer to the packet struct */ + memcpy((char *)&p, &userdata->data[sizeof(struct subnet_record *)], + sizeof(struct packet_struct *)); + + /* Unlock the packet. */ + p->locked = False; + + free_packet(p); + free((char *)userdata); +} + +/**************************************************************************** + Make a WINS query on behalf of a broadcast client name query request. +****************************************************************************/ + +void make_wins_proxy_name_query_request( struct subnet_record *subrec, + struct packet_struct *incoming_packet, + struct nmb_name *question_name) +{ + char ud[sizeof(struct userdata_struct) + sizeof(struct subrec *) + + sizeof(struct packet_struct *)]; + struct userdata_struct *userdata = (struct userdata_struct *)ud; + + bzero(ud, sizeof(ud)); + + userdata->copy_fn = wins_proxy_userdata_copy_fn; + userdata->free_fn = wins_proxy_userdata_free_fn; + userdata->userdata_len = sizeof(ud); + memcpy( userdata->data, (char *)&subrec, sizeof(struct subnet_record *)); + memcpy( &userdata->data[sizeof(struct subnet_record *)], (char *)&incoming_packet, + sizeof(struct packet_struct *)); + + /* Now use the unicast subnet to query the name with the WINS server. */ + query_name( unicast_subnet, question_name->name, question_name->name_type, + wins_proxy_name_query_request_success, + wins_proxy_name_query_request_fail, + userdata); +} diff --git a/source3/nmbd/nmbd_winsserver.c b/source3/nmbd/nmbd_winsserver.c new file mode 100644 index 0000000000..ba7b62e5ab --- /dev/null +++ b/source3/nmbd/nmbd_winsserver.c @@ -0,0 +1,1565 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NBT netbios routines and daemon - version 2 + + Copyright (C) Jeremy Allison 1994-1997 + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "includes.h" + +#define WINS_LIST "wins.dat" + +extern int DEBUGLEVEL; +extern struct in_addr ipzero; + +/**************************************************************************** +Determine if this packet should be allocated to the WINS server. +*****************************************************************************/ + +BOOL packet_is_for_wins_server(struct packet_struct *packet) +{ + struct nmb_packet *nmb = &packet->packet.nmb; + + /* Only unicast packets go to a WINS server. */ + if((wins_server_subnet == NULL) || (nmb->header.nm_flags.bcast == True)) + { + DEBUG(10, ("packet_is_for_wins_server: failing WINS test #1.\n")); + return False; + } + + /* Check for node status requests. */ + if (nmb->question.question_type != QUESTION_TYPE_NB_QUERY) + return False; + + switch(nmb->header.opcode) + { + /* + * A WINS server issues WACKS, not receives them. + */ + case NMB_WACK_OPCODE: + DEBUG(10, ("packet_is_for_wins_server: failing WINS test #2 (WACK).\n")); + return False; + /* + * A WINS server only processes registration and + * release requests, not responses. + */ + case NMB_NAME_REG_OPCODE: + case NMB_NAME_MULTIHOMED_REG_OPCODE: + case NMB_NAME_REFRESH_OPCODE_8: /* ambiguity in rfc1002 about which is correct. */ + case NMB_NAME_REFRESH_OPCODE_9: /* WinNT uses 8 by default. */ + if(nmb->header.response) + { + DEBUG(10, ("packet_is_for_wins_server: failing WINS test #3 (response = 1).\n")); + return False; + } + break; + + case NMB_NAME_RELEASE_OPCODE: + if(nmb->header.response) + { + DEBUG(10, ("packet_is_for_wins_server: failing WINS test #4 (response = 1).\n")); + return False; + } + break; + + /* + * Only process unicast name queries with rd = 1. + */ + case NMB_NAME_QUERY_OPCODE: + if(!nmb->header.response && !nmb->header.nm_flags.recursion_desired) + { + DEBUG(10, ("packet_is_for_wins_server: failing WINS test #5 (response = 1).\n")); + return False; + } + break; + } + + return True; +} + +/**************************************************************************** +Utility function to decide what ttl to give a register/refresh request. +*****************************************************************************/ + +static int get_ttl_from_packet(struct nmb_packet *nmb) +{ + int ttl = nmb->additional->ttl; + + if(ttl < lp_min_wins_ttl() ) + ttl = lp_min_wins_ttl(); + + if(ttl > lp_max_wins_ttl() ) + ttl = lp_max_wins_ttl(); + + return ttl; +} + +/**************************************************************************** +Load or create the WINS database. +*****************************************************************************/ + +BOOL initialise_wins(void) +{ + fstring fname; + time_t time_now = time(NULL); + FILE *fp; + pstring line; + + if(!lp_we_are_a_wins_server()) + return True; + + add_samba_names_to_subnet(wins_server_subnet); + +#ifndef SYNC_DNS + /* Setup the async dns. */ + start_async_dns(); +#endif + + fstrcpy(fname,lp_lockdir()); + trim_string(fname,NULL,"/"); + strcat(fname,"/"); + strcat(fname,WINS_LIST); + + if((fp = fopen(fname,"r")) == NULL) + { + DEBUG(2,("initialise_wins: Can't open wins database file %s. Error was %s\n", + fname, strerror(errno) )); + return True; + } + + while (!feof(fp)) + { + pstring name_str, ip_str, ttl_str, nb_flags_str; + unsigned int num_ips; + pstring name; + struct in_addr *ip_list; + int type = 0; + uint16 nb_flags; + time_t ttl; + enum name_source source; + char *ptr; + char *p; + BOOL got_token; + BOOL was_ip; + int i; + + /* Read a line from the wins.dat file. Strips whitespace + from the beginning and end of the line. + */ + if (!fgets_slash(line,sizeof(pstring),fp)) + continue; + + if (*line == '#') + continue; + + ptr = line; + + /* + * Now we handle multiple IP addresses per name we need + * to iterate over the line twice. The first time to + * determine how many IP addresses there are, the second + * time to actually parse them into the ip_list array. + */ + + if (!next_token(&ptr,name_str,NULL)) + { + DEBUG(0,("initialise_wins: Failed to parse name when parsing line %s\n", line )); + continue; + } + + if (!next_token(&ptr,ttl_str,NULL)) + { + DEBUG(0,("initialise_wins: Failed to parse time to live when parsing line %s\n", line )); + continue; + } + + /* + * Determine the number of IP addresses per line. + */ + num_ips = 0; + do + { + got_token = next_token(&ptr,ip_str,NULL); + was_ip = False; + + if(got_token && strchr(ip_str, '.')) + { + num_ips++; + was_ip = True; + } + } while( got_token && was_ip); + + if(num_ips == 0) + { + DEBUG(0,("initialise_wins: Missing IP address when parsing line %s\n", line )); + continue; + } + + if(!got_token) + { + DEBUG(0,("initialise_wins: Missing nb_flags when parsing line %s\n", line )); + continue; + } + + /* Allocate the space for the ip_list. */ + if((ip_list = (struct in_addr *)malloc( num_ips * sizeof(struct in_addr))) == NULL) + { + DEBUG(0,("initialise_wins: Malloc fail !\n")); + return False; + } + + /* Reset and re-parse the line. */ + ptr = line; + next_token(&ptr,name_str,NULL); + next_token(&ptr,ttl_str,NULL); + for(i = 0; i < num_ips; i++) + { + next_token(&ptr, ip_str, NULL); + ip_list[i] = *interpret_addr2(ip_str); + if (ip_equal(ip_list[i], ipzero)) + source = SELF_NAME; + } + next_token(&ptr,nb_flags_str,NULL); + + /* + * Deal with SELF or REGISTER name encoding. Default is REGISTER + * for compatibility with old nmbds. + */ + + if(nb_flags_str[strlen(nb_flags_str)-1] == 'S') + { + DEBUG(5,("initialise_wins: Ignoring SELF name %s\n", line)); + free((char *)ip_list); + continue; + } + + if(nb_flags_str[strlen(nb_flags_str)-1] == 'R') + nb_flags_str[strlen(nb_flags_str)-1] = '\0'; + + /* Netbios name. # divides the name from the type (hex): netbios#xx */ + pstrcpy(name,name_str); + + if((p = strchr(name,'#')) != NULL) + { + *p = 0; + sscanf(p+1,"%x",&type); + } + + /* Decode the netbios flags (hex) and the time-to-live (in seconds). */ + sscanf(nb_flags_str,"%hx",&nb_flags); + sscanf(ttl_str,"%ld",&ttl); + + /* add all entries that have 60 seconds or more to live */ + if ((ttl - 60) > time_now || ttl == PERMANENT_TTL) + { + struct name_record *namerec; + + if(ttl != PERMANENT_TTL) + ttl -= time_now; + + DEBUG(4, ("initialise_wins: add name: %s#%02x ttl = %ld first IP %s flags = %2hx\n", + name, type, ttl, inet_ntoa(ip_list[0]), nb_flags)); + + namerec = add_name_to_subnet(wins_server_subnet, name, type, nb_flags, + ttl, REGISTER_NAME, num_ips, ip_list); + + } + else + { + DEBUG(4, ("initialise_wins: not adding name (ttl problem) %s#%02x ttl = %ld first IP %s flags = %2hx\n", + name, type, ttl, inet_ntoa(ip_list[0]), nb_flags)); + } + + free((char *)ip_list); + } + + fclose(fp); + return True; +} + +/**************************************************************************** +Send a WINS WACK (Wait ACKnowledgement) response. +**************************************************************************/ + +static void send_wins_wack_response(int ttl, struct packet_struct *p) +{ + struct nmb_packet *nmb = &p->packet.nmb; + unsigned char rdata[2]; + + rdata[0] = rdata[1] = 0; + + /* Taken from nmblib.c - we need to send back almost + identical bytes from the requesting packet header. */ + + rdata[0] = (nmb->header.opcode & 0xF) << 3; + if (nmb->header.nm_flags.authoritative && + nmb->header.response) rdata[0] |= 0x4; + if (nmb->header.nm_flags.trunc) rdata[0] |= 0x2; + if (nmb->header.nm_flags.recursion_desired) rdata[0] |= 0x1; + if (nmb->header.nm_flags.recursion_available && + nmb->header.response) rdata[1] |= 0x80; + if (nmb->header.nm_flags.bcast) rdata[1] |= 0x10; + + reply_netbios_packet(p, /* Packet to reply to. */ + 0, /* Result code. */ + NMB_WAIT_ACK, /* nmbd type code. */ + NMB_WACK_OPCODE, /* opcode. */ + ttl, /* ttl. */ + (char *)rdata, /* data to send. */ + 2); /* data length. */ +} + +/**************************************************************************** +Send a WINS name registration response. +**************************************************************************/ + +static void send_wins_name_registration_response(int rcode, int ttl, struct packet_struct *p) +{ + struct nmb_packet *nmb = &p->packet.nmb; + char rdata[6]; + + memcpy(&rdata[0], &nmb->additional->rdata[0], 6); + + reply_netbios_packet(p, /* Packet to reply to. */ + rcode, /* Result code. */ + WINS_REG, /* nmbd type code. */ + NMB_NAME_REG_OPCODE, /* opcode. */ + ttl, /* ttl. */ + rdata, /* data to send. */ + 6); /* data length. */ +} + +/*********************************************************************** + Deal with a name refresh request to a WINS server. +************************************************************************/ + +void wins_process_name_refresh_request(struct subnet_record *subrec, + struct packet_struct *p) +{ + struct nmb_packet *nmb = &p->packet.nmb; + struct nmb_name *question = &nmb->question.question_name; + BOOL bcast = nmb->header.nm_flags.bcast; + uint16 nb_flags = get_nb_flags(nmb->additional->rdata); + BOOL group = (nb_flags & NB_GROUP) ? True : False; + struct name_record *namerec = NULL; + int ttl = get_ttl_from_packet(nmb); + struct in_addr from_ip; + + putip((char *)&from_ip,&nmb->additional->rdata[2]); + + if(bcast) + { + /* + * We should only get unicast name refresh packets here. + * Anyone trying to refresh broadcast should not be going to a WINS + * server. Log an error here. + */ + + DEBUG(0,("wins_process_name_refresh_request: broadcast name refresh request \ +received for name %s from IP %s on subnet %s. Error - should not be sent to WINS server\n", + namestr(question), inet_ntoa(from_ip), subrec->subnet_name)); + return; + } + + DEBUG(3,("wins_process_name_refresh_request: Name refresh for name %s \ +IP %s\n", namestr(question), inet_ntoa(from_ip) )); + + /* + * See if the name already exists. + */ + + namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME); + + /* + * If this is a refresh request and the name doesn't exist then + * fail it. + */ + + if(namerec == NULL) + { + DEBUG(3,("wins_process_name_refresh_request: Name refresh for name %s and \ +the name does not exist.\n", namestr(question) )); + send_wins_name_registration_response(NAM_ERR, 0, p); + return; + } + + /* + * Check that the group bits for the refreshing name and the + * name in our database match. + */ + + if((namerec != NULL) && ((group && !NAME_GROUP(namerec)) || (!group && NAME_GROUP(namerec))) ) + { + DEBUG(3,("wins_process_name_refresh_request: Name %s group bit = %s \ +does not match group bit in WINS for this name.\n", namestr(question), group ? "True" : "False" )); + send_wins_name_registration_response(RFS_ERR, 0, p); + return; + } + + /* + * For a unique name check that the person refreshing the name is one of the registered IP + * addresses. If not - fail the refresh. Do the same for group names with a type of 0x1c. + * Just return success for unique 0x1d refreshes. For normal group names update the ttl + * and return success. + */ + + if((!group || (group && (question->name_type == 0x1c))) && find_ip_in_name_record(namerec, from_ip )) + { + /* + * Update the ttl. + */ + update_name_ttl(namerec, ttl); + send_wins_name_registration_response(0, ttl, p); + return; + } + else if(group) + { + /* + * Normal groups are all registered with an IP address of 255.255.255.255 + * so we can't search for the IP address. + */ + update_name_ttl(namerec, ttl); + send_wins_name_registration_response(0, ttl, p); + return; + } + else if(!group && (question->name_type == 0x1d)) + { + /* + * Special name type - just pretend the refresh succeeded. + */ + send_wins_name_registration_response(0, ttl, p); + return; + } + else + { + /* + * Fail the refresh. + */ + + DEBUG(3,("wins_process_name_refresh_request: Name refresh for name %s with IP %s and \ +is IP is not known to the name.\n", namestr(question), inet_ntoa(from_ip) )); + send_wins_name_registration_response(RFS_ERR, 0, p); + return; + } +} + +/*********************************************************************** + Deal with a name registration request query success to a client that + owned the name. + + We have a locked pointer to the original packet stashed away in the + userdata pointer. The success here is actually a failure as it means + the client we queried wants to keep the name, so we must return + a registration failure to the original requestor. +************************************************************************/ + +static void wins_register_query_success(struct subnet_record *subrec, + struct userdata_struct *userdata, + struct nmb_name *question_name, + struct in_addr ip, + struct res_rec *answers) +{ + struct packet_struct *orig_reg_packet; + + memcpy((char *)&orig_reg_packet, userdata->data, sizeof(struct packet_struct *)); + + DEBUG(3,("wins_register_query_success: Original client at IP %s still wants the \ +name %s. Rejecting registration request.\n", inet_ntoa(ip), namestr(question_name) )); + + send_wins_name_registration_response(RFS_ERR, 0, orig_reg_packet); + + orig_reg_packet->locked = False; + free_packet(orig_reg_packet); +} + +/*********************************************************************** + Deal with a name registration request query failure to a client that + owned the name. + + We have a locked pointer to the original packet stashed away in the + userdata pointer. The failure here is actually a success as it means + the client we queried didn't want to keep the name, so we can remove + the old name record and then successfully add the new name. +************************************************************************/ + +static void wins_register_query_fail(struct subnet_record *subrec, + struct response_record *rrec, + struct nmb_name *question_name, + int rcode) +{ + struct userdata_struct *userdata = rrec->userdata; + struct packet_struct *orig_reg_packet; + struct nmb_packet *nmb; + struct name_record *namerec = NULL; + uint16 nb_flags; + BOOL group; + + memcpy((char *)&orig_reg_packet, userdata->data, sizeof(struct packet_struct *)); + + nmb = &orig_reg_packet->packet.nmb; + + nb_flags = get_nb_flags(nmb->additional->rdata); + group = (nb_flags & NB_GROUP) ? True : False; + + /* + * We want to just add the name, as we now know the original owner + * didn't want it. But we can't just do that as an arbitary + * amount of time may have taken place between the name query + * request and this timeout/error response. So we check that + * the name still exists and is in the same state - if so + * we remove it and call wins_process_name_registration_request() + * as we know it will do the right thing now. + */ + + namerec = find_name_on_subnet(subrec, question_name, FIND_ANY_NAME); + + if((namerec != NULL) && (namerec->source == REGISTER_NAME) && + ip_equal(rrec->packet->ip, *namerec->ip) ) + { + remove_name_from_namelist( subrec, namerec); + namerec = NULL; + } + + if(namerec == NULL) + wins_process_name_registration_request(subrec, orig_reg_packet); + else + DEBUG(2,("wins_register_query_fail: The state of the WINS database changed between \ +querying for name %s in order to replace it and this reply.\n", namestr(question_name) )); + + orig_reg_packet->locked = False; + free_packet(orig_reg_packet); +} + +/*********************************************************************** + Deal with a name registration request to a WINS server. + + Use the following pseudocode : + + registering_group + | + | + +--------name exists + | | + | | + | +--- existing name is group + | | | + | | | + | | +--- add name (return). + | | + | | + | +--- exiting name is unique + | | + | | + | +--- query existing owner (return). + | + | + +--------name doesn't exist + | + | + +--- add name (return). + + registering_unique + | + | + +--------name exists + | | + | | + | +--- existing name is group + | | | + | | | + | | +--- fail add (return). + | | + | | + | +--- exiting name is unique + | | + | | + | +--- query existing owner (return). + | + | + +--------name doesn't exist + | + | + +--- add name (return). + + As can be seen from the above, the two cases may be collapsed onto each + other with the exception of the case where the name already exists and + is a group name. This case we handle with an if statement. + +************************************************************************/ + +void wins_process_name_registration_request(struct subnet_record *subrec, + struct packet_struct *p) +{ + struct nmb_packet *nmb = &p->packet.nmb; + struct nmb_name *question = &nmb->question.question_name; + BOOL bcast = nmb->header.nm_flags.bcast; + uint16 nb_flags = get_nb_flags(nmb->additional->rdata); + int ttl = get_ttl_from_packet(nmb); + struct name_record *namerec = NULL; + struct in_addr from_ip; + BOOL registering_group_name = (nb_flags & NB_GROUP) ? True : False;; + + putip((char *)&from_ip,&nmb->additional->rdata[2]); + + if(bcast) + { + /* + * We should only get unicast name registration packets here. + * Anyone trying to register broadcast should not be going to a WINS + * server. Log an error here. + */ + + DEBUG(0,("wins_process_name_registration_request: broadcast name registration request \ +received for name %s from IP %s on subnet %s. Error - should not be sent to WINS server\n", + namestr(question), inet_ntoa(from_ip), subrec->subnet_name)); + return; + } + + DEBUG(3,("wins_process_name_registration_request: %s name registration for name %s \ +IP %s\n", registering_group_name ? "Group" : "Unique", namestr(question), inet_ntoa(from_ip) )); + + /* + * See if the name already exists. + */ + + namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME); + + /* + * Deal with the case where the name found was a dns entry. + * Remove it as we now have a NetBIOS client registering the + * name. + */ + + if((namerec != NULL) && ((namerec->source == DNS_NAME) || (namerec->source == DNSFAIL_NAME))) + { + DEBUG(5,("wins_process_name_registration_request: Name (%s) in WINS was a dns lookup \ +- removing it.\n", namestr(question) )); + remove_name_from_namelist( subrec, namerec); + namerec = NULL; + } + + /* + * Reject if the name exists and is not a REGISTER_NAME. + * (ie. Don't allow any static names to be overwritten. + */ + + if((namerec != NULL) && (namerec->source != REGISTER_NAME)) + { + DEBUG(3,("wins_process_name_registration_request: Attempt to register name %s. Name \ +already exists in WINS with source type %d.\n", namestr(question), namerec->source )); + send_wins_name_registration_response(RFS_ERR, 0, p); + return; + } + + /* + * Special policy decisions based on MS documentation. + * 1). All group names (except names ending in 0x1c) are added as 255.255.255.255. + * 2). All unique names ending in 0x1d are ignored, although a positive response is sent. + */ + + /* + * A group name is always added as the local broadcast address, except + * for group names ending in 0x1c. + * Group names with type 0x1c are registered with individual IP addresses. + */ + + if(registering_group_name && (question->name_type != 0x1c)) + from_ip = *interpret_addr2("255.255.255.255"); + + /* + * Ignore all attempts to register a unique 0x1d name, although return success. + */ + + if(!registering_group_name && (question->name_type == 0x1d)) + { + DEBUG(3,("wins_process_name_registration_request: Ignoring request \ +to register name %s from IP %s.", namestr(question), inet_ntoa(p->ip) )); + send_wins_name_registration_response(0, ttl, p); + return; + } + + /* + * Next two cases are the 'if statement' mentioned above. + */ + + if((namerec != NULL) && NAME_GROUP(namerec)) + { + if(registering_group_name) + { + /* + * If we are adding a group name, the name exists and is also a group entry just add this + * IP address to it and update the ttl. + */ + + DEBUG(3,("wins_process_name_registration_request: Adding IP %s to group name %s.\n", + inet_ntoa(from_ip), namestr(question) )); + /* + * Check the ip address is not already in the group. + */ + if(!find_ip_in_name_record(namerec, from_ip)) + add_ip_to_name_record(namerec, from_ip); + update_name_ttl(namerec, ttl); + send_wins_name_registration_response(0, ttl, p); + return; + } + else + { + /* + * If we are adding a unique name, the name exists in the WINS db + * and is a group name then reject the registration. + */ + + DEBUG(3,("wins_process_name_registration_request: Attempt to register name %s. Name \ +already exists in WINS as a GROUP name.\n", namestr(question) )); + send_wins_name_registration_response(RFS_ERR, 0, p); + return; + } + } + + /* + * From here on down we know that if the name exists in the WINS db it is + * a unique name, not a group name. + */ + + /* + * If the name exists and is one of our names then check the + * registering IP address. If it's not one of ours then automatically + * reject without doing the query - we know we will reject it. + */ + + if((namerec != NULL) && (is_myname(namerec->name.name)) ) + { + if(!ismyip(from_ip)) + { + DEBUG(3,("wins_process_name_registration_request: Attempt to register name %s. Name \ +is one of our (WINS server) names. Denying registration.\n", namestr(question) )); + send_wins_name_registration_response(RFS_ERR, 0, p); + return; + } + else + { + /* + * It's one of our names and one of our IP's - update the ttl. + */ + update_name_ttl(namerec, ttl); + send_wins_name_registration_response(0, ttl, p); + return; + } + } + + /* + * If the name exists and it is a unique registration and the registering IP + * is the same as the the (single) already registered IP then just update the ttl. + */ + + if(!registering_group_name && (namerec != NULL) && (namerec->num_ips == 1) && + ip_equal(namerec->ip[0], from_ip)) + { + update_name_ttl(namerec, ttl); + send_wins_name_registration_response(0, ttl, p); + return; + } + + /* + * Finally if the name exists do a query to the registering machine + * to see if they still claim to have the name. + */ + + if(namerec != NULL) + { + char ud[sizeof(struct userdata_struct) + sizeof(struct packet_struct *)]; + struct userdata_struct *userdata = (struct userdata_struct *)&ud; + + /* + * First send a WACK to the registering machine. + */ + + send_wins_wack_response(60, p); + + /* + * When the reply comes back we need the original packet. + * Lock this so it won't be freed and then put it into + * the userdata structure. + */ + + p->locked = True; + + userdata = (struct userdata_struct *)ud; + + userdata->copy_fn = NULL; + userdata->free_fn = NULL; + userdata->userdata_len = sizeof(struct packet_struct *); + memcpy(userdata->data, (char *)&p, sizeof(struct packet_struct *) ); + + /* + * As query_name uses the subnet broadcast address as the destination + * of the packet we temporarily change the subnet broadcast address to + * be the first IP address of the name owner and send the packet. This + * is a *horrible* hack but the alternative is to add the destination + * address parameter to all query_name() calls. I hate this code :-). + */ + + subrec->bcast_ip = *namerec->ip; + + query_name( subrec, question->name, question->name_type, + wins_register_query_success, + wins_register_query_fail, + userdata); + subrec->bcast_ip = ipzero; + return; + } + + /* + * Name did not exist - add it. + */ + + add_name_to_subnet(subrec, question->name, question->name_type, + nb_flags, ttl, REGISTER_NAME, 1, &from_ip); + + send_wins_name_registration_response(0, ttl, p); +} + +/*********************************************************************** + Deal with a mutihomed name query success to the machine that + requested the multihomed name registration. + + We have a locked pointer to the original packet stashed away in the + userdata pointer. +************************************************************************/ + +static void wins_multihomed_register_query_success(struct subnet_record *subrec, + struct userdata_struct *userdata, + struct nmb_name *question_name, + struct in_addr ip, + struct res_rec *answers) +{ + struct packet_struct *orig_reg_packet; + struct nmb_packet *nmb; + struct name_record *namerec = NULL; + struct in_addr from_ip; + int ttl; + + memcpy((char *)&orig_reg_packet, userdata->data, sizeof(struct packet_struct *)); + + nmb = &orig_reg_packet->packet.nmb; + + putip((char *)&from_ip,&nmb->additional->rdata[2]); + ttl = get_ttl_from_packet(nmb); + + /* + * We want to just add the new IP, as we now know the requesting + * machine claims to own it. But we can't just do that as an arbitary + * amount of time may have taken place between the name query + * request and this response. So we check that + * the name still exists and is in the same state - if so + * we just add the extra IP and update the ttl. + */ + + namerec = find_name_on_subnet(subrec, question_name, FIND_ANY_NAME); + + if( (namerec == NULL) || (namerec->source != REGISTER_NAME) ) + { + DEBUG(3,("wins_multihomed_register_query_success: name %s is not in the correct state to add \ +a subsequent IP addess.\n", namestr(question_name) )); + send_wins_name_registration_response(RFS_ERR, 0, orig_reg_packet); + return; + } + + if(!find_ip_in_name_record(namerec, from_ip)) + add_ip_to_name_record(namerec, from_ip); + update_name_ttl(namerec, ttl); + send_wins_name_registration_response(0, ttl, orig_reg_packet); + +} + +/*********************************************************************** + Deal with a name registration request query failure to a client that + owned the name. + + We have a locked pointer to the original packet stashed away in the + userdata pointer. +************************************************************************/ + +static void wins_multihomed_register_query_fail(struct subnet_record *subrec, + struct response_record *rrec, + struct nmb_name *question_name, + int rcode) +{ + struct userdata_struct *userdata = rrec->userdata; + struct packet_struct *orig_reg_packet; + + memcpy((char *)&orig_reg_packet, userdata->data, sizeof(struct packet_struct *)); + + DEBUG(3,("wins_multihomed_register_query_fail: Registering machine at IP %s failed to answer \ +query successfully for name %s.\n", inet_ntoa(orig_reg_packet->ip), namestr(question_name) )); + send_wins_name_registration_response(RFS_ERR, 0, orig_reg_packet); + return; +} + +/*********************************************************************** + Deal with a multihomed name registration request to a WINS server. + These cannot be group name registrations. +***********************************************************************/ + +void wins_process_multihomed_name_registration_request( struct subnet_record *subrec, + struct packet_struct *p) +{ + struct nmb_packet *nmb = &p->packet.nmb; + struct nmb_name *question = &nmb->question.question_name; + BOOL bcast = nmb->header.nm_flags.bcast; + uint16 nb_flags = get_nb_flags(nmb->additional->rdata); + int ttl = get_ttl_from_packet(nmb); + struct name_record *namerec = NULL; + struct in_addr from_ip; + BOOL group = (nb_flags & NB_GROUP) ? True : False;; + + putip((char *)&from_ip,&nmb->additional->rdata[2]); + + if(bcast) + { + /* + * We should only get unicast name registration packets here. + * Anyone trying to register broadcast should not be going to a WINS + * server. Log an error here. + */ + + DEBUG(0,("wins_process_multihomed_name_registration_request: broadcast name registration request \ +received for name %s from IP %s on subnet %s. Error - should not be sent to WINS server\n", + namestr(question), inet_ntoa(from_ip), subrec->subnet_name)); + return; + } + + /* + * Only unique names should be registered multihomed. + */ + + if(group) + { + DEBUG(0,("wins_process_multihomed_name_registration_request: group name registration request \ +received for name %s from IP %s on subnet %s. Errror - group names should not be multihomed.\n", + namestr(question), inet_ntoa(from_ip), subrec->subnet_name)); + return; + } + + DEBUG(3,("wins_process_multihomed_name_registration_request: name registration for name %s \ +IP %s\n", namestr(question), inet_ntoa(from_ip) )); + + /* + * Deal with policy regarding 0x1d names. + */ + + if(question->name_type == 0x1d) + { + DEBUG(3,("wins_process_multihomed_name_registration_request: Ignoring request \ +to register name %s from IP %s.", namestr(question), inet_ntoa(p->ip) )); + send_wins_name_registration_response(0, ttl, p); + return; + } + + /* + * See if the name already exists. + */ + + namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME); + + /* + * Deal with the case where the name found was a dns entry. + * Remove it as we now have a NetBIOS client registering the + * name. + */ + + if((namerec != NULL) && ((namerec->source == DNS_NAME) || (namerec->source == DNSFAIL_NAME))) + { + DEBUG(5,("wins_process_multihomed_name_registration_request: Name (%s) in WINS was a dns lookup \ +- removing it.\n", namestr(question) )); + remove_name_from_namelist( subrec, namerec); + namerec = NULL; + } + + /* + * Reject if the name exists and is not a REGISTER_NAME. + * (ie. Don't allow any static names to be overwritten. + */ + + if((namerec != NULL) && (namerec->source != REGISTER_NAME)) + { + DEBUG(3,("wins_process_multihomed_name_registration_request: Attempt to register name %s. Name \ +already exists in WINS with source type %d.\n", namestr(question), namerec->source )); + send_wins_name_registration_response(RFS_ERR, 0, p); + return; + } + + /* + * Reject if the name exists and is a GROUP name. + */ + + if((namerec != NULL) && NAME_GROUP(namerec)) + { + DEBUG(3,("wins_process_multihomed_name_registration_request: Attempt to register name %s. Name \ +already exists in WINS as a GROUP name.\n", namestr(question) )); + send_wins_name_registration_response(RFS_ERR, 0, p); + return; + } + + /* + * From here on down we know that if the name exists in the WINS db it is + * a unique name, not a group name. + */ + + /* + * If the name exists and is one of our names then check the + * registering IP address. If it's not one of ours then automatically + * reject without doing the query - we know we will reject it. + */ + + if((namerec != NULL) && (is_myname(namerec->name.name)) ) + { + if(!ismyip(from_ip)) + { + DEBUG(3,("wins_process_multihomed_name_registration_request: Attempt to register name %s. Name \ +is one of our (WINS server) names. Denying registration.\n", namestr(question) )); + send_wins_name_registration_response(RFS_ERR, 0, p); + return; + } + else + { + /* + * It's one of our names and one of our IP's. Ensure the IP is in the record and + * update the ttl. + */ + if(!find_ip_in_name_record(namerec, from_ip)) + add_ip_to_name_record(namerec, from_ip); + update_name_ttl(namerec, ttl); + send_wins_name_registration_response(0, ttl, p); + return; + } + } + + /* + * If the name exists do a query to the owner + * to see if they still want the name. + */ + + if(namerec != NULL) + { + char ud[sizeof(struct userdata_struct) + sizeof(struct packet_struct *)]; + struct userdata_struct *userdata = (struct userdata_struct *)&ud; + + /* + * First send a WACK to the registering machine. + */ + + send_wins_wack_response(60, p); + + /* + * When the reply comes back we need the original packet. + * Lock this so it won't be freed and then put it into + * the userdata structure. + */ + + p->locked = True; + + userdata = (struct userdata_struct *)ud; + + userdata->copy_fn = NULL; + userdata->free_fn = NULL; + userdata->userdata_len = sizeof(struct packet_struct *); + memcpy(userdata->data, (char *)&p, sizeof(struct packet_struct *) ); + + /* + * As query_name uses the subnet broadcast address as the destination + * of the packet we temporarily change the subnet broadcast address to + * be the IP address of the requesting machine and send the packet. This + * is a *horrible* hack but the alternative is to add the destination + * address parameter to all query_name() calls. I hate this code :-). + */ + + subrec->bcast_ip = p->ip; + query_name( subrec, question->name, question->name_type, + wins_multihomed_register_query_success, + wins_multihomed_register_query_fail, + userdata); + subrec->bcast_ip = ipzero; + return; + } + + /* + * Name did not exist - add it. + */ + + add_name_to_subnet(subrec, question->name, question->name_type, + nb_flags, ttl, REGISTER_NAME, 1, &from_ip); + + send_wins_name_registration_response(0, ttl, p); +} + +/*********************************************************************** + Deal with the special name query for *<1b>. +***********************************************************************/ + +static void process_wins_dmb_query_request(struct subnet_record *subrec, + struct packet_struct *p) +{ + struct name_record *namerec = NULL; + char *prdata; + int num_ips; + + /* + * Go through all the names in the WINS db looking for those + * ending in <1b>. Use this to calculate the number of IP + * addresses we need to return. + */ + + num_ips = 0; + for(namerec = subrec->namelist; namerec; namerec = namerec->next) + { + if(namerec->name.name_type == 0x1b) + num_ips += namerec->num_ips; + } + + if(num_ips == 0) + { + /* + * There are no 0x1b names registered. Return name query fail. + */ + send_wins_name_query_response(NAM_ERR, p, NULL); + return; + } + + if((prdata = (char *)malloc( num_ips * 6 )) == NULL) + { + DEBUG(0,("process_wins_dmb_query_request: Malloc fail !.\n")); + return; + } + + /* + * Go through all the names again in the WINS db looking for those + * ending in <1b>. Add their IP addresses into the list we will + * return. + */ + + num_ips = 0; + for(namerec = subrec->namelist; namerec; namerec = namerec->next) + { + if(namerec->name.name_type == 0x1b) + { + int i; + for(i = 0; i < namerec->num_ips; i++) + { + set_nb_flags(&prdata[num_ips * 6],namerec->nb_flags); + putip((char *)&prdata[(num_ips * 6) + 2], &namerec->ip[i]); + num_ips++; + } + } + } + + /* + * Send back the reply containing the IP list. + */ + + reply_netbios_packet(p, /* Packet to reply to. */ + 0, /* Result code. */ + WINS_QUERY, /* nmbd type code. */ + NMB_NAME_QUERY_OPCODE, /* opcode. */ + lp_min_wins_ttl(), /* ttl. */ + prdata, /* data to send. */ + num_ips*6); /* data length. */ + + free(prdata); +} + +/**************************************************************************** +Send a WINS name query response. +**************************************************************************/ + +void send_wins_name_query_response(int rcode, struct packet_struct *p, + struct name_record *namerec) +{ + char rdata[6]; + char *prdata = rdata; + int reply_data_len = 0; + int ttl = 0; + int i = 0; + int j; + + bzero(rdata,6); + + if(rcode == 0) + { + int same_net_index; + + ttl = (namerec->death_time != PERMANENT_TTL) ? + namerec->death_time - p->timestamp : lp_max_wins_ttl(); + + /* Copy all known ip addresses into the return data. */ + /* Optimise for the common case of one IP address so + we don't need a malloc. */ + + if(namerec->num_ips == 1 ) + prdata = rdata; + else + { + if((prdata = (char *)malloc( namerec->num_ips * 6 )) == NULL) + { + DEBUG(0,("send_wins_name_query_response: malloc fail !\n")); + return; + } + + /* + * Look over the known IP addresses and see if one of them + * is on the same (local) net as the requesting IP address. If so then + * put that IP address into the packet as the first IP. + * We can only do this for local nets as they're the only + * ones we know the netmask for. + */ + + same_net_index = -1; + i = 0; + + if(is_local_net(p->ip)) + { + struct in_addr *n_mask = iface_nmask(p->ip); + + for( j = 0; j < namerec->num_ips; j++) + { + if(same_net( namerec->ip[j], p->ip, *n_mask)) + { + set_nb_flags(&prdata[0],namerec->nb_flags); + putip((char *)&prdata[2], &namerec->ip[j]); + same_net_index = j; + i = 1; + } + } + } + } + + for(; i < namerec->num_ips; i++) + { + if(i == same_net_index) + continue; + set_nb_flags(&prdata[i*6],namerec->nb_flags); + putip((char *)&prdata[2+(i*6)], &namerec->ip[i]); + } + reply_data_len = namerec->num_ips * 6; + + } + + reply_netbios_packet(p, /* Packet to reply to. */ + rcode, /* Result code. */ + WINS_QUERY, /* nmbd type code. */ + NMB_NAME_QUERY_OPCODE, /* opcode. */ + ttl, /* ttl. */ + prdata, /* data to send. */ + reply_data_len); /* data length. */ + + if((prdata != rdata) && (prdata != NULL)) + free(prdata); +} + +/*********************************************************************** + Deal with a name query. +***********************************************************************/ + +void wins_process_name_query_request(struct subnet_record *subrec, + struct packet_struct *p) +{ + struct nmb_packet *nmb = &p->packet.nmb; + struct nmb_name *question = &nmb->question.question_name; + struct name_record *namerec = NULL; + + DEBUG(3,("wins_process_name_query: name query for name %s from IP %s\n", + namestr(question), inet_ntoa(p->ip) )); + + /* + * Special name code. If the queried name is *<1b> then search + * the entire WINS database and return a list of all the IP addresses + * registered to any <1b> name. This is to allow domain master browsers + * to discover other domains that may not have a presence on their subnet. + */ + + if(strequal( question->name, "*") && (question->name_type == 0x1b)) + { + process_wins_dmb_query_request( subrec, p); + return; + } + + namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME); + + if(namerec != NULL) + { + /* + * If it's a DNSFAIL_NAME then reply name not found. + */ + + if(namerec->source == DNSFAIL_NAME) + { + DEBUG(3,("wins_process_name_query: name query for name %s returning DNS fail.\n", + namestr(question) )); + send_wins_name_query_response(NAM_ERR, p, namerec); + return; + } + + /* + * If the name has expired then reply name not found. + */ + + if((namerec->death_time != PERMANENT_TTL) && (namerec->death_time < p->timestamp)) + { + DEBUG(3,("wins_process_name_query: name query for name %s - name expired. Returning fail.\n", + namestr(question) )); + send_wins_name_query_response(NAM_ERR, p, namerec); + return; + } + + DEBUG(3,("wins_process_name_query: name query for name %s returning first IP %s.\n", + namestr(question), inet_ntoa(namerec->ip[0]) )); + + send_wins_name_query_response(0, p, namerec); + return; + } + + /* + * Name not found in WINS - try a dns query if it's a 0x20 name. + */ + + if(lp_dns_proxy() && (question->name_type == 0x20)) + { + + DEBUG(3,("wins_process_name_query: name query for name %s not found - doing dns lookup.\n", + namestr(question) )); + + queue_dns_query(p, question, &namerec); + return; + } + + /* + * Name not found - return error. + */ + + send_wins_name_query_response(NAM_ERR, p, NULL); +} + +/**************************************************************************** +Send a WINS name release response. +**************************************************************************/ + +static void send_wins_name_release_response(int rcode, struct packet_struct *p) +{ + struct nmb_packet *nmb = &p->packet.nmb; + char rdata[6]; + + memcpy(&rdata[0], &nmb->additional->rdata[0], 6); + + reply_netbios_packet(p, /* Packet to reply to. */ + rcode, /* Result code. */ + NMB_REL, /* nmbd type code. */ + NMB_NAME_RELEASE_OPCODE, /* opcode. */ + 0, /* ttl. */ + rdata, /* data to send. */ + 6); /* data length. */ +} + +/*********************************************************************** + Deal with a name release. +***********************************************************************/ + +void wins_process_name_release_request(struct subnet_record *subrec, + struct packet_struct *p) +{ + struct nmb_packet *nmb = &p->packet.nmb; + struct nmb_name *question = &nmb->question.question_name; + BOOL bcast = nmb->header.nm_flags.bcast; + uint16 nb_flags = get_nb_flags(nmb->additional->rdata); + struct name_record *namerec = NULL; + struct in_addr from_ip; + BOOL releasing_group_name = (nb_flags & NB_GROUP) ? True : False;; + + putip((char *)&from_ip,&nmb->additional->rdata[2]); + + if(bcast) + { + /* + * We should only get unicast name registration packets here. + * Anyone trying to register broadcast should not be going to a WINS + * server. Log an error here. + */ + + DEBUG(0,("wins_process_name_release_request: broadcast name registration request \ +received for name %s from IP %s on subnet %s. Error - should not be sent to WINS server\n", + namestr(question), inet_ntoa(from_ip), subrec->subnet_name)); + return; + } + + DEBUG(3,("wins_process_name_release_request: %s name release for name %s \ +IP %s\n", releasing_group_name ? "Group" : "Unique", namestr(question), inet_ntoa(from_ip) )); + + /* + * Deal with policy regarding 0x1d names. + */ + + if(!releasing_group_name && (question->name_type == 0x1d)) + { + DEBUG(3,("wins_process_name_release_request: Ignoring request \ +to release name %s from IP %s.", namestr(question), inet_ntoa(p->ip) )); + send_wins_name_release_response(0, p); + return; + } + + /* + * See if the name already exists. + */ + + namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME); + + if((namerec == NULL) || ((namerec != NULL) && (namerec->source != REGISTER_NAME)) ) + { + send_wins_name_release_response(NAM_ERR, p); + return; + } + + /* + * Check that the sending machine has permission to release this name. + * If it's a group name not ending in 0x1c then just say yes and let + * the group time out. + */ + + if(releasing_group_name && (question->name_type != 0x1c)) + { + send_wins_name_release_response(0, p); + return; + } + + /* + * Check that the releasing node is on the list of IP addresses + * for this name. Disallow the release if not. + */ + + if(!find_ip_in_name_record(namerec, from_ip)) + { + DEBUG(3,("wins_process_name_release_request: Refusing request to \ +release name %s as IP %s is not one of the known IP's for this name.\n", + namestr(question), inet_ntoa(from_ip) )); + send_wins_name_release_response(NAM_ERR, p); + return; + } + + /* + * Release the name and then remove the IP from the known list. + */ + + send_wins_name_release_response(0, p); + remove_ip_from_name_record(namerec, from_ip); + + /* + * Remove the name entirely if no IP addresses left. + */ + if (namerec->num_ips == 0) + remove_name_from_namelist(subrec, namerec); + +} + +/******************************************************************* + WINS time dependent processing. +******************************************************************/ + +void initiate_wins_processing(time_t t) +{ + static time_t lasttime = 0; + + if (!lasttime) + lasttime = t; + if (t - lasttime < 5) + return; + + if(!lp_we_are_a_wins_server()) + return; + + expire_names_on_subnet(wins_server_subnet, t); + + if(wins_server_subnet->namelist_changed) + wins_write_database(); + + wins_server_subnet->namelist_changed = False; +} + +/******************************************************************* + Write out the current WINS database. +******************************************************************/ + +void wins_write_database(void) +{ + struct name_record *namerec; + fstring fname, fnamenew; + + FILE *fp; + + if(!lp_we_are_a_wins_server()) + return; + + fstrcpy(fname,lp_lockdir()); + trim_string(fname,NULL,"/"); + strcat(fname,"/"); + strcat(fname,WINS_LIST); + fstrcpy(fnamenew,fname); + strcat(fnamenew,"."); + + if((fp = fopen(fnamenew,"w")) == NULL) + { + DEBUG(0,("wins_write_database: Can't open %s. Error was %s\n", fnamenew, strerror(errno))); + return; + } + + DEBUG(4,("wins_write_database: Dump of WINS name list.\n")); + + for (namerec = wins_server_subnet->namelist; namerec; namerec = namerec->next) + { + int i; + struct tm *tm; + + DEBUG(4,("%-19s ", namestr(&namerec->name) )); + + if(namerec->death_time != PERMANENT_TTL) + { + tm = LocalTime(&namerec->death_time); + DEBUG(4,("TTL = %s", asctime(tm) )); + } + else + DEBUG(4,("TTL = PERMANENT\t")); + + for (i = 0; i < namerec->num_ips; i++) + DEBUG(4,("%15s ", inet_ntoa(namerec->ip[i]) )); + DEBUG(4,("%2x\n", namerec->nb_flags )); + + if (namerec->source == REGISTER_NAME) + { + fprintf(fp, "%s#%02x %ld ", + namerec->name.name,namerec->name.name_type, /* Ignore scope. */ + namerec->death_time); + + for (i = 0; i < namerec->num_ips; i++) + fprintf(fp, "%s ", inet_ntoa(namerec->ip[i])); + fprintf(fp, "%2xR\n", namerec->nb_flags); + } + } + + fclose(fp); + unlink(fname); + chmod(fnamenew,0644); + rename(fnamenew,fname); +} diff --git a/source3/nmbd/nmbd_workgroupdb.c b/source3/nmbd/nmbd_workgroupdb.c new file mode 100644 index 0000000000..828e29a024 --- /dev/null +++ b/source3/nmbd/nmbd_workgroupdb.c @@ -0,0 +1,356 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NBT netbios routines and daemon - version 2 + Copyright (C) Andrew Tridgell 1994-1997 + Copyright (C) Luke Kenneth Casson Leighton 1994-1997 + Copyright (C) Jeremy Allison 1994-1997 + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "includes.h" +#include "smb.h" + +extern int ClientNMB; + +extern int DEBUGLEVEL; + +extern pstring myname; +extern fstring myworkgroup; +extern char **my_netbios_names; +extern uint16 samba_nb_type; +extern struct in_addr ipzero; + +int workgroup_count = 0; /* unique index key: one for each workgroup */ + +/**************************************************************************** + Add a workgroup into the list. + **************************************************************************/ + +static void add_workgroup(struct subnet_record *subrec, struct work_record *work) +{ + struct work_record *w2; + + work->subnet = subrec; + + if (!subrec->workgrouplist) + { + subrec->workgrouplist = work; + work->prev = NULL; + work->next = NULL; + return; + } + + for (w2 = subrec->workgrouplist; w2->next; w2 = w2->next) + ; + + w2->next = work; + work->next = NULL; + work->prev = w2; + + subrec->work_changed = True; +} + +/**************************************************************************** + Create an empty workgroup. + **************************************************************************/ + +static struct work_record *create_workgroup(char *name, int ttl) +{ + struct work_record *work; + struct subnet_record *subrec; + int t = -1; + + if((work = (struct work_record *)malloc(sizeof(*work))) == NULL) + { + DEBUG(0,("create_workgroup: malloc fail !\n")); + return NULL; + } + bzero((char *)work, sizeof(*work)); + + StrnCpy(work->work_group,name,sizeof(work->work_group)-1); + work->serverlist = NULL; + + work->RunningElection = False; + work->ElectionCount = 0; + work->announce_interval = 0; + work->needelection = False; + work->needannounce = True; + work->lastannounce_time = time(NULL); + work->mst_state = lp_local_master() ? MST_POTENTIAL : MST_NONE; + work->dom_state = DOMAIN_NONE; + work->log_state = LOGON_NONE; + + work->death_time = (ttl != PERMANENT_TTL) ? time(NULL)+(ttl*3) : PERMANENT_TTL; + + /* Make sure all token representations of workgroups are unique. */ + + for (subrec = FIRST_SUBNET; subrec && (t == -1); + subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec)) + { + struct work_record *w; + for (w = subrec->workgrouplist; w && t == -1; w = w->next) + { + if (strequal(w->work_group, work->work_group)) + t = w->token; + } + } + + if (t == -1) + work->token = ++workgroup_count; + else + work->token = t; + + /* No known local master browser as yet. */ + *work->local_master_browser_name = '\0'; + + /* No known domain master browser as yet. */ + *work->dmb_name.name = '\0'; + putip((char *)&work->dmb_addr, &ipzero); + + /* WfWg uses 01040b01 */ + /* Win95 uses 01041501 */ + /* NTAS uses ???????? */ + work->ElectionCriterion = (MAINTAIN_LIST)|(ELECTION_VERSION<<8); + work->ElectionCriterion |= (lp_os_level() << 24); + if (lp_domain_master()) + work->ElectionCriterion |= 0x80; + + return work; +} + +/******************************************************************* + Remove a workgroup. + ******************************************************************/ + +static struct work_record *remove_workgroup_from_subnet(struct subnet_record *subrec, + struct work_record *work) +{ + struct work_record *ret_work = NULL; + + DEBUG(3,("remove_workgroup: Removing workgroup %s\n", work->work_group)); + + ret_work = work->next; + + remove_all_servers(work); + + if (!work->serverlist) + { + if (work->prev) + work->prev->next = work->next; + if (work->next) + work->next->prev = work->prev; + + if (subrec->workgrouplist == work) + subrec->workgrouplist = work->next; + + free((char *)work); + } + + subrec->work_changed = True; + + return ret_work; +} + + +/**************************************************************************** + Find a workgroup in the workgroup list of a subnet. + **************************************************************************/ + +struct work_record *find_workgroup_on_subnet(struct subnet_record *subrec, + fstring name) +{ + struct work_record *ret; + + DEBUG(4, ("find_workgroup_on_subnet: workgroup search for %s on subnet %s: ", + name, subrec->subnet_name)); + + for (ret = subrec->workgrouplist; ret; ret = ret->next) + { + if (!strcmp(ret->work_group,name)) + { + DEBUG(4, ("found\n")); + return(ret); + } + } + DEBUG(4, ("not found\n")); + return NULL; +} + +/**************************************************************************** + Create a workgroup in the workgroup list of the subnet. + **************************************************************************/ + +struct work_record *create_workgroup_on_subnet(struct subnet_record *subrec, + fstring name, int ttl) +{ + struct work_record *work = NULL; + + DEBUG(4,("create_workgroup_on_subnet: creating group %s on subnet %s\n", + name, subrec->subnet_name)); + + if ((work = create_workgroup(name, ttl))) + { + add_workgroup(subrec, work); + + subrec->work_changed = True; + + return(work); + } + + return NULL; +} + +/**************************************************************************** + Update a workgroup ttl. + **************************************************************************/ + +void update_workgroup_ttl(struct work_record *work, int ttl) +{ + if(work->death_time != PERMANENT_TTL) + work->death_time = time(NULL)+(ttl*3); + work->subnet->work_changed = True; +} + +/**************************************************************************** + Fail function called if we cannot register the WORKGROUP<0> and + WORKGROUP<1e> names on the net. +**************************************************************************/ + +static void fail_register(struct subnet_record *subrec, struct response_record *rrec, + struct nmb_name *nmbname) +{ + DEBUG(0,("fail_register: Failed to register name %s on subnet %s.\n", + namestr(nmbname), subrec->subnet_name)); +} + +/**************************************************************************** + If the workgroup is our primary workgroup, add the required names to it. +**************************************************************************/ + +void initiate_myworkgroup_startup(struct subnet_record *subrec, struct work_record *work) +{ + int i; + + if(!strequal(myworkgroup, work->work_group)) + return; + + /* If this is a broadcast subnet then start elections on it + if we are so configured. */ + + if ((subrec != unicast_subnet) && (subrec != remote_broadcast_subnet) && + (subrec != wins_server_subnet) && lp_preferred_master() && + lp_local_master()) + { + DEBUG(3, ("initiate_myworkgroup_startup: preferred master startup for \ +workgroup %s on subnet %s\n", work->work_group, subrec->subnet_name)); + work->needelection = True; + work->ElectionCriterion |= (1<<3); + } + + /* Register the WORKGROUP<0> and WORKGROUP<1e> names on the network. */ + + register_name(subrec,myworkgroup,0x0,samba_nb_type|NB_GROUP, + NULL, + fail_register,NULL); + + register_name(subrec,myworkgroup,0x1e,samba_nb_type|NB_GROUP, + NULL, + fail_register,NULL); + + for( i = 0; my_netbios_names[i]; i++) + { + char *name = my_netbios_names[i]; + int stype = lp_default_server_announce() | (lp_local_master() ? + SV_TYPE_POTENTIAL_BROWSER : 0 ); + + if(!strequal(myname, name)) + stype &= ~(SV_TYPE_MASTER_BROWSER|SV_TYPE_POTENTIAL_BROWSER| + SV_TYPE_DOMAIN_MASTER|SV_TYPE_DOMAIN_MEMBER); + + create_server_on_workgroup(work,name,stype|SV_TYPE_LOCAL_LIST_ONLY, + PERMANENT_TTL, lp_serverstring()); + DEBUG(3,("initiate_myworkgroup_startup: Added server name entry %s \ +on subnet %s\n", name, subrec->subnet_name)); + } +} + +/**************************************************************************** + Dump a copy of the workgroup database into the log file. + **************************************************************************/ + +void dump_workgroups(void) +{ + struct subnet_record *subrec; + + for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec)) + { + if (subrec->workgrouplist) + { + struct work_record *work; + + DEBUG(4,("dump_workgroups: dump workgroup on subnet %15s: ", subrec->subnet_name)); + DEBUG(4,(" netmask=%15s:\n", inet_ntoa(subrec->mask_ip))); + + for (work = subrec->workgrouplist; work; work = work->next) + { + DEBUG(4,("\t%s(%d) current master browser = %s\n", work->work_group, + work->token, + *work->local_master_browser_name ? work->local_master_browser_name : "UNKNOWN" )); + if (work->serverlist) + { + struct server_record *servrec; + for (servrec = work->serverlist; servrec; servrec = servrec->next) + { + DEBUG(4,("\t\t%s %8x (%s)\n", + servrec->serv.name, servrec->serv.type, servrec->serv.comment)); + } + } + } + } + } +} + +/**************************************************************************** + Expire any dead servers on all workgroups. If the workgroup has expired + remove it. + **************************************************************************/ + +void expire_workgroups_and_servers(time_t t) +{ + struct subnet_record *subrec; + + for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec)) + { + struct work_record *work; + struct work_record *nextwork; + + for (work = subrec->workgrouplist; work; work = nextwork) + { + nextwork = work->next; + expire_servers(work, t); + + if ((work->serverlist == NULL) && (work->death_time != PERMANENT_TTL) && + ((t == -1) || (work->death_time < t))) + { + DEBUG(3,("expire_workgroups_and_servers: Removing timed out workgroup %s\n", + work->work_group)); + remove_workgroup_from_subnet(subrec, work); + } + } + } +} -- cgit