1
0
mirror of https://github.com/samba-team/samba.git synced 2024-12-23 17:34:34 +03:00
samba-mirror/source3/nameserv.c

2324 lines
60 KiB
C
Raw Normal View History

/*
Unix SMB/Netbios implementation.
Version 1.9.
NBT netbios routines and daemon - version 2
Copyright (C) Andrew Tridgell 1994-1995
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 "loadparm.h"
#include "nameserv.h"
static void queue_packet(struct packet_struct *packet);
void process(void);
static void dump_names(void);
static void announce_request(char *group);
void sync_browse_lists(char *name,int name_type,char *myname,
char *domain,struct in_addr ip);
extern int DEBUGLEVEL;
extern pstring debugf;
pstring servicesf = CONFIGFILE;
extern pstring scope;
extern BOOL CanRecurse;
extern struct in_addr myip;
extern struct in_addr bcast_ip;
extern struct in_addr Netmask;
extern pstring myhostname;
static pstring host_file;
static pstring myname="";
static int ClientNMB= -1;
static int ClientDGRAM= -1;
static BOOL needannounce=True;
/* this is our name database */
static struct name_record *namelist = NULL;
/* list of servers to be returned by NetServerEnum */
static struct server_record *serverlist = NULL;
/* this is the domain list. For the moment we will assume that our
primary domain is the first one listed in this list */
static struct domain_record *domainlist = NULL;
/* are we running as a daemon ? */
static BOOL is_daemon = False;
/* machine comment for host announcements */
static pstring ServerComment="";
static BOOL got_bcast = False;
static BOOL got_myip = False;
static BOOL got_nmask = False;
static BOOL updatedlists = False;
static int updatecount=0;
/* what server type are we currently */
static int ServerType =
SV_TYPE_WORKSTATION | SV_TYPE_SERVER | SV_TYPE_TIME_SOURCE |
SV_TYPE_SERVER_UNIX |
SV_TYPE_PRINTQ_SERVER | SV_TYPE_POTENTIAL_BROWSER;
/* here are my election parameters */
/* NTAS uses 2, NT uses 1, WfWg uses 0 */
#define MAINTAIN_LIST 1
#define ELECTION_VERSION 1
static BOOL RunningElection = False;
static BOOL needelection = False;
static int ElectionCount = 0;
static int StartupTime =0;
/* WfWg uses 01040b01 */
/* Win95 uses 01041501 */
/* NTAS uses ?? */
static uint32 ElectionCriterion = (MAINTAIN_LIST<<1)|(ELECTION_VERSION<<8);
/* we currently support being the master for just one group. Being the
master for more than one group might be tricky as NetServerEnum is
often asked for a list without naming the group */
static fstring PrimaryGroup="";
#define AM_MASTER (PrimaryGroup[0] && (ServerType & SV_TYPE_MASTER_BROWSER))
#define MSBROWSE "\001\002__MSBROWSE__\002"
#define GET_TTL(ttl) ((ttl)?MIN(ttl,lp_max_ttl()):lp_max_ttl())
#define BROWSE_MAILSLOT "\\MAILSLOT\\BROWSE"
/****************************************************************************
catch a sighup
****************************************************************************/
static int sig_hup()
{
BlockSignals(True);
DEBUG(0,("Got SIGHUP (reload not implemented)\n"));
dump_names();
reload_services(True);
BlockSignals(False);
#ifndef DONT_REINSTALL_SIG
signal(SIGHUP,SIGNAL_CAST sig_hup);
#endif
return(0);
}
/****************************************************************************
catch a sigpipe
****************************************************************************/
static int sig_pipe()
{
BlockSignals(True);
DEBUG(0,("Got SIGPIPE\n"));
if (!is_daemon)
exit(1);
BlockSignals(False);
return(0);
}
#if DUMP_CORE
/*******************************************************************
prepare to dump a core file - carefully!
********************************************************************/
static BOOL dump_core(void)
{
char *p;
pstring dname;
strcpy(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));
}
#endif
#endif
DEBUG(0,("Dumping core in %s\n",dname));
return(True);
}
#endif
/****************************************************************************
possibly continue after a fault
****************************************************************************/
static void fault_continue(void)
{
static int errcount=1;
errcount--;
if (is_daemon && errcount)
process();
#if DUMP_CORE
if (dump_core()) return;
#endif
return;
}
/*******************************************************************
wrapper to get the DC
******************************************************************/
static char *domain_controller(void)
{
char *dc = lp_domain_controller();
/* so many people mistake this for a bool that we need to handle it. sigh. */
if (!*dc || strequal(dc,"yes") || strequal(dc,"true"))
strcpy(dc,myname);
return(dc);
}
/****************************************************************************
true if two netbios names are equal
****************************************************************************/
static BOOL name_equal(struct nmb_name *n1,struct nmb_name *n2)
{
if (n1->name_type != n2->name_type) return(False);
return(strequal(n1->name,n2->name) && strequal(n1->scope,n2->scope));
}
/****************************************************************************
add a netbios name into the namelist
**************************************************************************/
static void add_name(struct name_record *n)
{
struct name_record *n2;
if (!namelist) {
namelist = n;
n->prev = NULL;
n->next = NULL;
return;
}
for (n2 = namelist; n2->next; n2 = n2->next) ;
n2->next = n;
n->next = NULL;
n->prev = n2;
}
/****************************************************************************
add a domain into the list
**************************************************************************/
static void add_domain(struct domain_record *d)
{
struct domain_record *d2;
if (!domainlist) {
domainlist = d;
d->prev = NULL;
d->next = NULL;
return;
}
for (d2 = domainlist; d2->next; d2 = d2->next) ;
d2->next = d;
d->next = NULL;
d->prev = d2;
}
/****************************************************************************
add a server into the list
**************************************************************************/
static void add_server(struct server_record *s)
{
struct server_record *s2;
if (!serverlist) {
serverlist = s;
s->prev = NULL;
s->next = NULL;
return;
}
for (s2 = serverlist; s2->next; s2 = s2->next) ;
s2->next = s;
s->next = NULL;
s->prev = s2;
}
/****************************************************************************
remove a name from the namelist. The pointer must be an element just
retrieved
**************************************************************************/
static void remove_name(struct name_record *n)
{
struct name_record *nlist = namelist;
while (nlist && nlist != n) nlist = nlist->next;
if (nlist) {
if (nlist->next) nlist->next->prev = nlist->prev;
if (nlist->prev) nlist->prev->next = nlist->next;
free(nlist);
}
}
/****************************************************************************
find a name in the namelist
**************************************************************************/
static struct name_record *find_name(struct nmb_name *n)
{
struct name_record *ret;
for (ret = namelist; ret; ret = ret->next)
if (name_equal(&ret->name,n)) return(ret);
return(NULL);
}
/****************************************************************************
dump a copy of the name table
**************************************************************************/
static void dump_names(void)
{
time_t t = time(NULL);
struct name_record *n;
struct domain_record *d;
DEBUG(3,("Dump of local name table:\n"));
for (n = namelist; n; n = n->next) {
DEBUG(3,("%s %s TTL=%d Unique=%s\n",
namestr(&n->name),
inet_ntoa(n->ip),
n->death_time?n->death_time-t:0,
BOOLSTR(n->unique)));
}
DEBUG(3,("\nDump of domain list:\n"));
for (d = domainlist; d; d = d->next)
DEBUG(3,("%s %s\n",d->name,inet_ntoa(d->bcast_ip)));
}
/****************************************************************************
add a host entry to the name list
****************************************************************************/
static struct name_record *add_host_entry(char *name,int type,BOOL unique,int ttl,
enum name_source source,
struct in_addr ip)
{
struct name_record *n;
struct name_record *n2=NULL;
n = (struct name_record *)malloc(sizeof(*n));
if (!n) return(NULL);
bzero((char *)n,sizeof(*n));
make_nmb_name(&n->name,name,type,scope);
if ((n2=find_name(&n->name))) {
free(n);
n = n2;
}
if (ttl) n->death_time = time(NULL)+ttl*3;
n->ip = ip;
n->unique = unique;
n->source = source;
if (!n2) add_name(n);
DEBUG(3,("Added host entry %s at %s ttl=%d unique=%s\n",
namestr(&n->name),inet_ntoa(ip),ttl,BOOLSTR(unique)));
return(n);
}
/****************************************************************************
add a domain entry
****************************************************************************/
static struct domain_record *add_domain_entry(char *name,struct in_addr ip)
{
struct domain_record *d;
d = (struct domain_record *)malloc(sizeof(*d));
if (!d) return(NULL);
bzero((char *)d,sizeof(*d));
if (zero_ip(ip)) ip = bcast_ip;
StrnCpy(d->name,name,sizeof(d->name)-1);
d->bcast_ip = ip;
if (!PrimaryGroup[0] && ip_equal(bcast_ip,ip) && name[0] != '*') {
strcpy(PrimaryGroup,name);
strupper(PrimaryGroup);
DEBUG(3,("Setting primary group to %s (%s)\n",PrimaryGroup,inet_ntoa(ip)));
}
add_domain(d);
ip = *interpret_addr2("255.255.255.255");
if (name[0] != '*') add_host_entry(name,0x1e,False,0,SELF,ip);
DEBUG(3,("Added domain entry %s at %s\n",
name,inet_ntoa(ip)));
return(d);
}
/****************************************************************************
add a server entry
****************************************************************************/
struct server_record *add_server_entry(char *name,int servertype,
int ttl,char *comment,BOOL replace)
{
BOOL newentry=False;
struct server_record *s;
for (s = serverlist; s; s = s->next)
if (strequal(name,s->name)) break;
if (s && !replace) {
DEBUG(4,("Not replacing %s\n",name));
return(s);
}
updatedlists=True;
if (!s) {
newentry = True;
s = (struct server_record *)malloc(sizeof(*s));
if (!s) return(NULL);
bzero((char *)s,sizeof(*s));
}
/* update the entry */
StrnCpy(s->name,name,sizeof(s->name)-1);
StrnCpy(s->comment,comment,sizeof(s->comment)-1);
s->servertype = servertype;
s->death_time = ttl?time(NULL)+ttl*3:0;
strupper(s->name);
if (s->servertype & SV_TYPE_DOMAIN_ENUM) strupper(s->comment);
if (!newentry) return(s);
add_server(s);
if (newentry) {
DEBUG(3,("Added server entry %s of type %x (%s)\n",
name,servertype,comment));
} else {
DEBUG(3,("Updated server entry %s of type %x (%s)\n",
name,servertype,comment));
}
return(s);
}
/****************************************************************************
add the magic samba names, useful for finding samba servers
**************************************************************************/
static void add_my_names(void)
{
struct in_addr ip;
ip = *interpret_addr2("0.0.0.0");
add_host_entry(myname,0x20,True,0,SELF,ip);
add_host_entry(myname,0x0,True,0,SELF,ip);
add_host_entry(myname,0x1f,True,0,SELF,ip); /* used for chat?? */
add_host_entry(myname,0x3,True,0,SELF,ip); /* used for winpopup */
if (!domainlist)
add_domain_entry(lp_workgroup(),bcast_ip);
add_server_entry(myname,
ServerType,
0,ServerComment,True);
add_host_entry("__SAMBA__",0x20,True,0,SELF,ip);
add_host_entry("__SAMBA__",0x0,True,0,SELF,ip);
if (lp_preferred_master()) {
DEBUG(3,("Preferred master startup\n"));
needelection = True;
ElectionCriterion |= (1<<3);
}
ElectionCriterion |= (lp_os_level() << 24);
}
/*******************************************************************
write out browse.dat
******************************************************************/
static void write_browse_list(void)
{
struct server_record *s;
pstring fname,fnamenew;
FILE *f;
updatecount++;
strcpy(fname,lp_lockdir());
trim_string(fname,NULL,"/");
strcat(fname,"/");
strcat(fname,SERVER_LIST);
strcpy(fnamenew,fname);
strcat(fnamenew,".");
f = fopen(fnamenew,"w");
if (!f) {
DEBUG(4,("Can't open %s - %s\n",fnamenew,strerror(errno)));
return;
}
for (s=serverlist; s ; s = s->next) {
/* don't list domains I don't have a master for */
if ((s->servertype & SV_TYPE_DOMAIN_ENUM) && !s->comment[0]) continue;
fprintf(f,"\"%s\"\t%08x\t\"%s\"\n",s->name,s->servertype,s->comment);
}
fclose(f);
chmod(fnamenew,0644);
/* unlink(fname); */
rename(fnamenew,fname);
DEBUG(3,("Wrote browse list %s\n",fname));
}
/*******************************************************************
expire old names in the namelist and serverlist
******************************************************************/
static void expire_names(void)
{
static time_t lastrun=0;
time_t t = time(NULL);
struct name_record *n;
struct name_record *next;
struct server_record *s;
struct server_record *nexts;
if (!lastrun) lastrun = t;
if (t < lastrun + 5) return;
lastrun = t;
/* expire old names */
for (n = namelist; n; n = next) {
if (n->death_time && n->death_time < t) {
DEBUG(3,("Removing dead name %s\n",
namestr(&n->name)));
next = n->next;
if (n->prev) n->prev->next = n->next;
if (n->next) n->next->prev = n->prev;
if (namelist == n) namelist = n->next;
free(n);
} else {
next = n->next;
}
}
/* expire old entries in the serverlist */
for (s = serverlist; s; s = nexts) {
if (s->death_time && s->death_time < t) {
DEBUG(3,("Removing dead server %s\n",s->name));
updatedlists = True;
nexts = s->next;
if (s->prev) s->prev->next = s->next;
if (s->next) s->next->prev = s->prev;
if (serverlist == s) serverlist = s->next;
free(s);
} else {
nexts = s->next;
}
}
}
/*******************************************************************
delete old names from the namelist
******************************************************************/
static void housekeeping(void)
{
time_t t = time(NULL);
expire_names();
/* write out the browse.dat database for smbd to get */
if (updatedlists) {
write_browse_list();
updatedlists = False;
}
{
/* occasionally check to see if the master browser is around */
static time_t lastrun=0;
if (!lastrun) lastrun = t;
if (t < lastrun + 5*60) return;
lastrun = t;
if (!AM_MASTER && PrimaryGroup[0] &&
!name_query(ClientNMB,PrimaryGroup,0x1d,True,False,
bcast_ip,NULL,queue_packet)) {
DEBUG(2,("Forcing election on %s\n",PrimaryGroup));
needelection = True;
}
}
}
/****************************************************************************
reload the services file
**************************************************************************/
BOOL reload_services(BOOL test)
{
BOOL ret;
extern fstring remote_machine;
strcpy(remote_machine,"nmbd");
if (lp_loaded())
{
pstring fname;
strcpy(fname,lp_configfile());
if (file_exist(fname,NULL) && !strcsequal(fname,servicesf))
{
strcpy(servicesf,fname);
test = False;
}
}
if (test && !lp_file_list_changed())
return(True);
ret = lp_load(servicesf,True);
/* perhaps the config filename is now set */
if (!test)
reload_services(True);
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))
{
if (!fgets_slash(line,sizeof(pstring),f)) continue;
if (*line == '#') continue;
{
BOOL group=False;
string ip,name,flags,extra;
char *ptr;
int count = 0;
struct in_addr ipaddr;
enum name_source source = LMHOSTS;
*ip = *name = *flags = *extra = 0;
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 (strchr(flags,'G') || strchr(flags,'S'))
group = True;
if (strchr(flags,'M') && !group) {
source = SELF;
strcpy(myname,name);
}
ipaddr = *interpret_addr2(ip);
if (group) {
add_domain_entry(name,ipaddr);
} else {
add_host_entry(name,0x20,True,0,source,ipaddr);
}
}
}
fclose(f);
}
/*******************************************************************
check if 2 IPs are on the same net
we will assume the local netmask, although this could be wrong XXXX
******************************************************************/
static BOOL same_net(struct in_addr ip1,struct in_addr ip2)
{
unsigned long net1,net2,nmask;
nmask = ntohl(Netmask.s_addr);
net1 = ntohl(ip1.s_addr);
net2 = ntohl(ip2.s_addr);
return((net1 & nmask) == (net2 & nmask));
}
/****************************************************************************
send an election packet
**************************************************************************/
static void send_election(char *group,uint32 criterion,int timeup,char *name)
{
pstring outbuf;
char *p;
DEBUG(2,("Sending election to %s for workgroup %s\n",
inet_ntoa(bcast_ip),group));
bzero(outbuf,sizeof(outbuf));
p = outbuf;
CVAL(p,0) = 8; /* election */
p++;
CVAL(p,0) = ELECTION_VERSION;
SIVAL(p,1,criterion);
SIVAL(p,5,timeup*1000); /* ms - despite the spec */
p += 13;
strcpy(p,name);
strupper(p);
p = skip_string(p,1);
send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,PTR_DIFF(p,outbuf),
name,group,0,0x1e,bcast_ip,myip);
}
/****************************************************************************
send a backup list response
**************************************************************************/
static void send_backup_list(char *name,int token,struct nmb_name *to,
struct in_addr ip)
{
pstring outbuf;
char *p;
DEBUG(2,("Sending backup list to %s for workgroup %s\n",
inet_ntoa(ip),PrimaryGroup));
bzero(outbuf,sizeof(outbuf));
p = outbuf;
CVAL(p,0) = 10; /* backup list response */
p++;
CVAL(p,0) = 1; /* count */
SIVAL(p,1,token);
p += 5;
strcpy(p,name);
strupper(p);
p = skip_string(p,1) + 1;
send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,PTR_DIFF(p,outbuf),
myname,to->name,0,to->name_type,ip,myip);
}
/*******************************************************************
become the master browser
******************************************************************/
static void become_master(void)
{
uint32 domain_type = SV_TYPE_DOMAIN_ENUM | SV_TYPE_SERVER_UNIX;
DEBUG(2,("Becoming master for %s\n",PrimaryGroup));
ServerType |= SV_TYPE_MASTER_BROWSER;
ServerType |= SV_TYPE_BACKUP_BROWSER;
ElectionCriterion |= 0x5;
add_host_entry(PrimaryGroup,0x1d,True,0,SELF,myip);
add_host_entry(PrimaryGroup,0x0,False,0,SELF,myip);
add_host_entry(MSBROWSE,1,False,0,SELF,myip);
if (lp_domain_master()) {
add_host_entry(myname,0x1b,True,0,SELF,myip);
add_host_entry(PrimaryGroup,0x1b,True,0,SELF,myip);
add_host_entry(PrimaryGroup,0x1c,False,0,SELF,myip);
ServerType |= SV_TYPE_DOMAIN_MASTER;
if (lp_domain_logons()) {
ServerType |= SV_TYPE_DOMAIN_CTRL;
ServerType |= SV_TYPE_DOMAIN_MEMBER;
domain_type |= SV_TYPE_DOMAIN_CTRL;
}
}
add_server_entry(PrimaryGroup,domain_type,0,myname,True);
add_server_entry(myname,ServerType,0,ServerComment,True);
announce_request(PrimaryGroup);
needannounce = True;
}
/*******************************************************************
unbecome the master browser
******************************************************************/
static void become_nonmaster(void)
{
struct name_record *n;
struct nmb_name nn;
DEBUG(2,("Becoming non-master for %s\n",PrimaryGroup));
ServerType &= ~SV_TYPE_MASTER_BROWSER;
ServerType &= ~SV_TYPE_DOMAIN_CTRL;
ServerType &= ~SV_TYPE_DOMAIN_MASTER;
ElectionCriterion &= ~0x4;
make_nmb_name(&nn,PrimaryGroup,0x1d,scope);
n = find_name(&nn);
if (n && n->source == SELF) remove_name(n);
make_nmb_name(&nn,PrimaryGroup,0x1b,scope);
n = find_name(&nn);
if (n && n->source == SELF) remove_name(n);
make_nmb_name(&nn,MSBROWSE,1,scope);
n = find_name(&nn);
if (n && n->source == SELF) remove_name(n);
}
/*******************************************************************
run the election
******************************************************************/
static void run_election(void)
{
time_t t = time(NULL);
static time_t lastime = 0;
if (!PrimaryGroup[0] || !RunningElection) return;
/* send election packets once a second */
if (lastime &&
t-lastime <= 0) return;
lastime = t;
send_election(PrimaryGroup,ElectionCriterion,t-StartupTime,myname);
if (ElectionCount++ < 4) return;
/* I won! now what :-) */
RunningElection = False;
DEBUG(2,(">>> Won election on %s <<<\n",PrimaryGroup));
become_master();
}
/****************************************************************************
construct a host announcement unicast
**************************************************************************/
static void announce_host(struct domain_record *d,char *my_name,char *comment)
{
time_t t = time(NULL);
pstring outbuf;
char *p;
char *namep;
char *stypep;
char *commentp;
uint32 stype = ServerType;
if (needannounce) {
/* drop back to a max 3 minute announce - this is to prevent a
single lost packet from stuffing things up for too long */
d->announce_interval = MIN(d->announce_interval,3*60);
d->lastannounce_time = t - (d->announce_interval+1);
}
/* announce every minute at first then progress to every 12 mins */
if (d->lastannounce_time &&
(t - d->lastannounce_time) < d->announce_interval)
return;
if (d->announce_interval < 12*60) d->announce_interval += 60;
d->lastannounce_time = t;
DEBUG(2,("Sending announcement to %s for workgroup %s\n",
inet_ntoa(d->bcast_ip),d->name));
if (!strequal(PrimaryGroup,d->name) ||
!ip_equal(bcast_ip,d->bcast_ip)) {
stype &= ~(SV_TYPE_POTENTIAL_BROWSER | SV_TYPE_MASTER_BROWSER |
SV_TYPE_DOMAIN_MASTER | SV_TYPE_BACKUP_BROWSER |
SV_TYPE_DOMAIN_CTRL | SV_TYPE_DOMAIN_MEMBER);
}
if (!*comment) comment = "NoComment";
if (!*my_name) my_name = "NoName";
if (strlen(comment) > 43) comment[43] = 0;
bzero(outbuf,sizeof(outbuf));
CVAL(outbuf,0) = 1; /* host announce */
p = outbuf+1;
CVAL(p,0) = updatecount;
SIVAL(p,1,d->announce_interval*1000); /* ms - despite the spec */
namep = p+5;
StrnCpy(p+5,my_name,16);
strupper(p+5);
CVAL(p,21) = 2; /* major version */
CVAL(p,22) = 2; /* minor version */
stypep = p+23;
SIVAL(p,23,stype);
SSVAL(p,27,0xaa55); /* browse signature */
SSVAL(p,29,1); /* browse version */
commentp = p+31;
strcpy(p+31,comment);
p += 31;
p = skip_string(p,1);
send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,PTR_DIFF(p,outbuf),
my_name,d->name,0,0x1d,d->bcast_ip,myip);
/* if I'm the master then I also need to do a local master and
domain announcement */
if (AM_MASTER &&
strequal(d->name,PrimaryGroup) &&
ip_equal(bcast_ip,d->bcast_ip)) {
/* do master announcements as well */
SIVAL(stypep,0,ServerType);
CVAL(outbuf,0) = 15; /* local master announce */
send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,PTR_DIFF(p,outbuf),
my_name,PrimaryGroup,0,0x1e,d->bcast_ip,myip);
CVAL(outbuf,0) = 12; /* domain announce */
StrnCpy(namep,PrimaryGroup,15);
strupper(namep);
StrnCpy(commentp,myname,15);
strupper(commentp);
SIVAL(stypep,0,(unsigned)0x80000000);
p = commentp + strlen(commentp) + 1;
send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,PTR_DIFF(p,outbuf),
my_name,MSBROWSE,0,1,d->bcast_ip,myip);
}
}
/****************************************************************************
send a announce request to the local net
**************************************************************************/
static void announce_request(char *group)
{
pstring outbuf;
char *p;
DEBUG(2,("Sending announce request to %s for workgroup %s\n",
inet_ntoa(bcast_ip),group));
bzero(outbuf,sizeof(outbuf));
p = outbuf;
CVAL(p,0) = 2; /* announce request */
p++;
CVAL(p,0) = 0; /* flags?? */
p++;
StrnCpy(p,myname,16);
strupper(p);
p = skip_string(p,1);
send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,PTR_DIFF(p,outbuf),
myname,group,0,0,bcast_ip,myip);
}
/****************************************************************************
announce myself as a master to the PDC
**************************************************************************/
static void announce_master(char *group)
{
static time_t last=0;
time_t t = time(NULL);
pstring outbuf;
char *p;
struct in_addr ip,pdc_ip;
fstring pdcname;
*pdcname = 0;
if (strequal(domain_controller(),myname)) return;
if (!AM_MASTER || (last && (t-last < 10*60))) return;
last = t;
ip = *interpret_addr2(domain_controller());
if (zero_ip(ip)) ip = bcast_ip;
if (!name_query(ClientNMB,PrimaryGroup,
0x1b,False,False,ip,&pdc_ip,queue_packet)) {
DEBUG(2,("Failed to find PDC at %s\n",domain_controller()));
return;
}
name_status(ClientNMB,PrimaryGroup,0x1b,False,
pdc_ip,NULL,pdcname,queue_packet);
if (!pdcname[0]) {
DEBUG(3,("Can't find netbios name of PDC at %s\n",inet_ntoa(pdc_ip)));
} else {
sync_browse_lists(pdcname,0x20,myname,PrimaryGroup,pdc_ip);
}
DEBUG(2,("Sending master announce to %s for workgroup %s\n",
inet_ntoa(pdc_ip),group));
bzero(outbuf,sizeof(outbuf));
p = outbuf;
CVAL(p,0) = 13; /* announce request */
p++;
StrnCpy(p,myname,16);
strupper(p);
p = skip_string(p,1);
send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,PTR_DIFF(p,outbuf),
myname,PrimaryGroup,0x1b,0,pdc_ip,myip);
}
/*******************************************************************
am I listening on a name. Should check name_type as well
This is primarily used to prevent us gathering server lists from
other workgroups we aren't a part of
******************************************************************/
static BOOL listening(struct nmb_name *n)
{
if (!strequal(n->scope,scope)) return(False);
if (strequal(n->name,myname) ||
strequal(n->name,PrimaryGroup) ||
strequal(n->name,MSBROWSE))
return(True);
return(False);
}
/*******************************************************************
process a domain announcement frame
Announce frames come in 3 types. Servers send host announcements
(command=1) to let the master browswer know they are
available. Master browsers send local master announcements
(command=15) to let other masters and backups that they are the
master. They also send domain announcements (command=12) to register
the domain
The comment field of domain announcements contains the master
browser name. The servertype is used by NetServerEnum to select
resources. We just have to pass it to smbd (via browser.dat) and let
the client choose using bit masks.
******************************************************************/
static void process_announce(struct packet_struct *p,int command,char *buf)
{
struct dgram_packet *dgram = &p->packet.dgram;
int update_count = CVAL(buf,0);
int ttl = IVAL(buf,1)/1000;
char *name = buf+5;
int osmajor=CVAL(buf,21);
int osminor=CVAL(buf,22);
uint32 servertype = IVAL(buf,23);
char *comment = buf+31;
name[15] = 0;
comment[43] = 0;
DEBUG(3,("Announce(%d) %s count=%d ttl=%d OS=(%d,%d) type=%08x comment=%s\n",
command,name,update_count,ttl,osmajor,osminor,
servertype,comment));
if (strequal(dgram->source_name.name,myname)) return;
if (!listening(&dgram->dest_name)) return;
ttl = GET_TTL(ttl);
/* add them to our browse list */
add_server_entry(name,servertype,ttl,comment,True);
}
/*******************************************************************
process a master announcement frame
******************************************************************/
static void process_master_announce(struct packet_struct *p,char *buf)
{
struct dgram_packet *dgram = &p->packet.dgram;
char *name = buf;
name[15] = 0;
DEBUG(3,("Master Announce from %s (%s)\n",name,inet_ntoa(p->ip)));
if (strequal(dgram->source_name.name,myname)) return;
if (!AM_MASTER || !listening(&dgram->dest_name)) return;
/* merge browse lists with them */
if (lp_domain_master())
sync_browse_lists(name,0x20,myname,PrimaryGroup,p->ip);
}
/*******************************************************************
process a backup list request
A client send 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.
Currently samba only sends back one name in the backup list, its
wn. For larger nets we'll have to add backups and send "become
backup" requests occasionally.
******************************************************************/
static void process_backup_list(struct packet_struct *p,char *buf)
{
struct dgram_packet *dgram = &p->packet.dgram;
int count = CVAL(buf,0);
int token = IVAL(buf,1);
DEBUG(3,("Backup request to %s token=%d\n",
namestr(&dgram->dest_name),
token));
if (strequal(dgram->source_name.name,myname)) return;
if (count <= 0) return;
if (!AM_MASTER ||
!strequal(PrimaryGroup,dgram->dest_name.name))
return;
if (!listening(&dgram->dest_name)) return;
send_backup_list(myname,token,
&dgram->source_name,
p->ip);
}
/*******************************************************************
work out if I win an election
******************************************************************/
static BOOL win_election(int version,uint32 criterion,int timeup,char *name)
{
time_t t = time(NULL);
uint32 mycriterion;
if (version > ELECTION_VERSION) return(False);
if (version < ELECTION_VERSION) return(True);
mycriterion = ElectionCriterion;
if (criterion > mycriterion) return(False);
if (criterion < mycriterion) return(True);
if (timeup > (t - StartupTime)) return(False);
if (timeup < (t - StartupTime)) return(True);
if (strcasecmp(myname,name) > 0) return(False);
return(True);
}
/*******************************************************************
process a election packet
An election dynamically decides who will be the master.
******************************************************************/
static void process_election(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 *name = buf+13;
name[15] = 0;
DEBUG(3,("Election request from %s vers=%d criterion=%08x timeup=%d\n",
name,version,criterion,timeup));
if (strequal(dgram->source_name.name,myname)) return;
if (!listening(&dgram->dest_name)) return;
if (win_election(version,criterion,timeup,name)) {
if (!RunningElection) {
needelection = True;
ElectionCount=0;
}
} else {
needelection = False;
if (RunningElection) {
RunningElection = False;
DEBUG(3,(">>> Lost election on %s <<<\n",PrimaryGroup));
/* if we are the master then remove our masterly names */
if (AM_MASTER)
become_nonmaster();
}
}
}
/*******************************************************************
process a announcement request
clients send these when they want everyone to send an announcement
immediately. This can cause quite a storm of packets!
******************************************************************/
static void process_announce_request(struct packet_struct *p,char *buf)
{
struct dgram_packet *dgram = &p->packet.dgram;
int flags = CVAL(buf,0);
char *name = buf+1;
name[15] = 0;
DEBUG(3,("Announce request from %s flags=0x%X\n",name,flags));
if (strequal(dgram->source_name.name,myname)) return;
needannounce = True;
}
/****************************************************************************
process a browse frame
****************************************************************************/
static void process_browse_packet(struct packet_struct *p,char *buf,int len)
{
int command = CVAL(buf,0);
switch (command)
{
case 1: /* host announce */
case 12: /* domain announce */
case 15: /* local master announce */
process_announce(p,command,buf+1);
break;
case 2: /* announce request */
process_announce_request(p,buf+1);
break;
case 8: /* election */
process_election(p,buf+1);
break;
case 9: /* get backup list */
process_backup_list(p,buf+1);
break;
case 13: /* master announcement */
process_master_announce(p,buf+1);
break;
}
}
/****************************************************************************
process a domain logon packet
**************************************************************************/
static void process_logon_packet(struct packet_struct *p,char *buf,int len)
{
char *logname,*q;
pstring outbuf;
struct dgram_packet *dgram = &p->packet.dgram;
int code;
if (!lp_domain_logons()) {
DEBUG(3,("No domain logons\n"));
return;
}
if (!listening(&dgram->dest_name)) {
DEBUG(4,("Not listening to that domain\n"));
return;
}
q = outbuf;
bzero(outbuf,sizeof(outbuf));
code = SVAL(buf,0);
switch (code) {
case 0:
{
char *machine = buf+2;
char *user = skip_string(machine,1);
logname = skip_string(user,1);
SSVAL(q,0,6);
q += 2;
strcpy(q,"\\\\");
q += 2;
StrnCpy(q,myname,16);
strupper(q);
q = skip_string(q,1);
SSVAL(q,0,0xFFFF);
q += 2;
DEBUG(3,("Domain login request from %s(%s) user=%s\n",
machine,inet_ntoa(p->ip),user));
}
break;
case 7:
{
char *machine = buf+2;
logname = skip_string(machine,1);
SSVAL(q,0,0xc);
q += 2;
StrnCpy(q,domain_controller(),16);
strupper(q);
q = skip_string(q,1);
q += PutUniCode(q,domain_controller());
q += PutUniCode(q,dgram->dest_name.name);
SSVAL(q,0,0xFFFF);
q += 2;
DEBUG(3,("GETDC request from %s(%s)\n",
machine,inet_ntoa(p->ip)));
}
break;
default:
DEBUG(3,("Unknown domain request %d\n",code));
return;
}
send_mailslot_reply(logname,ClientDGRAM,outbuf,PTR_DIFF(q,outbuf),
myname,&dgram->source_name.name[0],0,0,p->ip,myip);
}
/****************************************************************************
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 (dgram->header.msg_type != 0x10 &&
dgram->header.msg_type != 0x11 &&
dgram->header.msg_type != 0x12) {
/* don't process error packets etc yet */
return;
}
buf = &dgram->data[0];
buf -= 4; /* XXXX for the pseudo tcp length -
someday I need to get rid of this */
if (CVAL(buf,smb_com) != SMBtrans) return;
len = SVAL(buf,smb_vwv11);
buf2 = smb_base(buf) + SVAL(buf,smb_vwv12);
DEBUG(3,("datagram from %s to %s for %s of type %d len=%d\n",
namestr(&dgram->source_name),namestr(&dgram->dest_name),
smb_buf(buf),CVAL(buf2,0),len));
if (len <= 0) return;
if (strequal(smb_buf(buf),"\\MAILSLOT\\BROWSE")) {
process_browse_packet(p,buf2,len);
} else if (strequal(smb_buf(buf),"\\MAILSLOT\\NET\\NETLOGON")) {
process_logon_packet(p,buf2,len);
}
}
/*******************************************************************
find a workgroup using the specified broadcast
******************************************************************/
static BOOL find_workgroup(char *name,struct in_addr ip)
{
fstring name1;
BOOL ret;
struct in_addr ipout;
strcpy(name1,MSBROWSE);
ret = name_query(ClientNMB,name1,0x1,True,False,ip,&ipout,queue_packet);
if (!ret) return(False);
name_status(ClientNMB,name1,0x1,False,ipout,name,NULL,queue_packet);
if (name[0] != '*') {
DEBUG(2,("Found workgroup %s on broadcast %s\n",name,inet_ntoa(ip)));
} else {
DEBUG(3,("Failed to find workgroup %s on broadcast %s\n",name,inet_ntoa(ip)));
}
return(name[0] != '*');
}
/****************************************************************************
a hook for announce handling - called every minute
**************************************************************************/
static void do_announcements(void)
{
struct domain_record *d;
for (d = domainlist; d; d = d->next) {
/* if the ip address is 0 then set to the broadcast */
if (zero_ip(d->bcast_ip)) d->bcast_ip = bcast_ip;
/* if the workgroup is '*' then find a workgroup to be part of */
if (d->name[0] == '*') {
if (!find_workgroup(d->name,d->bcast_ip)) continue;
add_host_entry(d->name,0x1e,False,0,SELF,
*interpret_addr2("255.255.255.255"));
if (!PrimaryGroup[0] && ip_equal(bcast_ip,d->bcast_ip)) {
strcpy(PrimaryGroup,d->name);
strupper(PrimaryGroup);
}
}
announce_host(d,myname,ServerComment);
}
/* if I have a domain controller then announce to it */
if (AM_MASTER)
announce_master(PrimaryGroup);
needannounce=False;
}
/*******************************************************************
check if someone still owns a name
******************************************************************/
static BOOL confirm_name(struct name_record *n)
{
struct in_addr ipout;
BOOL ret = name_query(ClientNMB,n->name.name,
n->name.name_type,False,
False,n->ip,&ipout,queue_packet);
return(ret && ip_equal(ipout,n->ip));
}
/****************************************************************************
reply to a name release
****************************************************************************/
static void reply_name_release(struct packet_struct *p)
{
struct nmb_packet *nmb = &p->packet.nmb;
struct packet_struct p2;
struct nmb_packet *nmb2;
struct res_rec answer_rec;
struct in_addr ip;
int rcode=0;
int nb_flags = nmb->additional->rdata[0];
BOOL bcast = nmb->header.nm_flags.bcast;
putip((char *)&ip,&nmb->additional->rdata[2]);
{
struct name_record *n = find_name(&nmb->question.question_name);
if (n && n->unique && n->source == REGISTER &&
ip_equal(ip,n->ip)) {
remove_name(n); n = NULL;
}
/* XXXX under what conditions should we reject the removal?? */
}
DEBUG(3,("Name release on name %s rcode=%d\n",
namestr(&nmb->question.question_name),rcode));
if (bcast) return;
/* Send a NAME RELEASE RESPONSE */
p2 = *p;
nmb2 = &p2.packet.nmb;
nmb2->header.response = True;
nmb2->header.nm_flags.bcast = False;
nmb2->header.nm_flags.recursion_available = CanRecurse;
nmb2->header.nm_flags.trunc = False;
nmb2->header.nm_flags.authoritative = True;
nmb2->header.qdcount = 0;
nmb2->header.ancount = 1;
nmb2->header.nscount = 0;
nmb2->header.arcount = 0;
nmb2->header.rcode = rcode;
nmb2->answers = &answer_rec;
bzero((char *)nmb2->answers,sizeof(*nmb2->answers));
nmb2->answers->rr_name = nmb->question.question_name;
nmb2->answers->rr_type = nmb->question.question_type;
nmb2->answers->rr_class = nmb->question.question_class;
nmb2->answers->ttl = 0;
nmb2->answers->rdlength = 6;
nmb2->answers->rdata[0] = nb_flags;
putip(&nmb2->answers->rdata[2],(char *)&ip);
send_packet(&p2);
}
/****************************************************************************
reply to a reg request
**************************************************************************/
static void reply_name_reg(struct packet_struct *p)
{
struct nmb_packet *nmb = &p->packet.nmb;
char *qname = nmb->question.question_name.name;
BOOL wildcard = (qname[0] == '*');
BOOL bcast = nmb->header.nm_flags.bcast;
int ttl = GET_TTL(nmb->additional->ttl);
int name_type = nmb->question.question_name.name_type;
int nb_flags = nmb->additional->rdata[0];
struct packet_struct p2;
struct nmb_packet *nmb2;
struct res_rec answer_rec;
struct in_addr ip;
BOOL group = (nb_flags&0x80)?True:False;
int rcode = 0;
if (wildcard) return;
putip((char *)&ip,&nmb->additional->rdata[2]);
if (group) {
/* apparently we should return 255.255.255.255 for group queries (email from MS) */
ip = *interpret_addr2("255.255.255.255");
}
{
struct name_record *n = find_name(&nmb->question.question_name);
if (n) {
if (!group && !ip_equal(ip,n->ip)) {
/* check if the previous owner still wants it,
if so reject the registration, otherwise change the owner
and refresh */
if (n->source != REGISTER || confirm_name(n)) {
rcode = 6;
} else {
n->ip = ip;
n->death_time = ttl?p->timestamp+ttl*3:0;
DEBUG(3,("%s changed owner to %s\n",
namestr(&n->name),inet_ntoa(n->ip)));
}
} else {
/* refresh the name */
if (n->source != SELF)
n->death_time = ttl?p->timestamp + ttl*3:0;
}
} else {
/* add the name to our database */
n = add_host_entry(qname,name_type,!group,ttl,REGISTER,ip);
}
}
if (bcast) return;
DEBUG(3,("Name registration for name %s at %s rcode=%d\n",
namestr(&nmb->question.question_name),
inet_ntoa(ip),rcode));
/* Send a NAME REGISTRATION RESPONSE */
/* a lot of fields get copied from the query. This gives us the IP
and port the reply will be sent to etc */
p2 = *p;
nmb2 = &p2.packet.nmb;
nmb2->header.opcode = 5;
nmb2->header.response = True;
nmb2->header.nm_flags.bcast = False;
nmb2->header.nm_flags.recursion_available = CanRecurse;
nmb2->header.nm_flags.trunc = False;
nmb2->header.nm_flags.authoritative = True;
nmb2->header.qdcount = 0;
nmb2->header.ancount = 1;
nmb2->header.nscount = 0;
nmb2->header.arcount = 0;
nmb2->header.rcode = rcode;
nmb2->answers = &answer_rec;
bzero((char *)nmb2->answers,sizeof(*nmb2->answers));
nmb2->answers->rr_name = nmb->question.question_name;
nmb2->answers->rr_type = nmb->question.question_type;
nmb2->answers->rr_class = nmb->question.question_class;
nmb2->answers->ttl = ttl;
nmb2->answers->rdlength = 6;
nmb2->answers->rdata[0] = nb_flags;
putip(&nmb2->answers->rdata[2],(char *)&ip);
send_packet(&p2);
}
/****************************************************************************
reply to a name status query
****************************************************************************/
static void reply_name_status(struct packet_struct *p)
{
struct nmb_packet *nmb = &p->packet.nmb;
char *qname = nmb->question.question_name.name;
BOOL wildcard = (qname[0] == '*');
struct packet_struct p2;
struct nmb_packet *nmb2;
struct res_rec answer_rec;
char *buf;
int count;
int rcode = 0;
struct name_record *n = find_name(&nmb->question.question_name);
DEBUG(3,("Name status for name %s\n",
namestr(&nmb->question.question_name)));
if (!wildcard && (!n || n->source != SELF))
return;
/* Send a POSITIVE NAME STATUS RESPONSE */
/* a lot of fields get copied from the query. This gives us the IP
and port the reply will be sent to etc */
p2 = *p;
nmb2 = &p2.packet.nmb;
nmb2->header.response = True;
nmb2->header.nm_flags.bcast = False;
nmb2->header.nm_flags.recursion_available = CanRecurse;
nmb2->header.nm_flags.trunc = False;
nmb2->header.nm_flags.authoritative = True; /* WfWg ignores
non-authoritative answers */
nmb2->header.qdcount = 0;
nmb2->header.ancount = 1;
nmb2->header.nscount = 0;
nmb2->header.arcount = 0;
nmb2->header.rcode = rcode;
nmb2->answers = &answer_rec;
bzero((char *)nmb2->answers,sizeof(*nmb2->answers));
nmb2->answers->rr_name = nmb->question.question_name;
nmb2->answers->rr_type = nmb->question.question_type;
nmb2->answers->rr_class = nmb->question.question_class;
nmb2->answers->ttl = 0;
for (count=0, n = namelist ; n; n = n->next) {
if (n->source != SELF) continue;
count++;
}
count = MIN(count,400/18); /* XXXX hack, we should calculate exactly
how many will fit */
buf = &nmb2->answers->rdata[0];
SCVAL(buf,0,count);
buf += 1;
for (n = namelist ; n; n = n->next)
{
if (n->source != SELF) continue;
bzero(buf,18);
strcpy(buf,n->name.name);
strupper(buf);
buf[15] = n->name.name_type;
buf += 16;
buf[0] = 0x4; /* active */
if (!n->unique) buf[0] |= 0x80; /* group */
buf += 2;
count--;
}
/* XXXXXXX we should fill in more fields of the statistics structure */
bzero(buf,64);
{
extern int num_good_sends,num_good_receives;
SIVAL(buf,20,num_good_sends);
SIVAL(buf,24,num_good_receives);
}
SIVAL(buf,46,0xFFB8E5); /* undocumented - used by NT */
buf += 64;
nmb2->answers->rdlength = PTR_DIFF(buf,&nmb2->answers->rdata[0]);
send_packet(&p2);
}
/****************************************************************************
reply to a name query
****************************************************************************/
static void reply_name_query(struct packet_struct *p)
{
struct nmb_packet *nmb = &p->packet.nmb;
char *qname = nmb->question.question_name.name;
BOOL wildcard = (qname[0] == '*');
BOOL bcast = nmb->header.nm_flags.bcast;
struct in_addr retip;
int name_type = nmb->question.question_name.name_type;
struct packet_struct p2;
struct nmb_packet *nmb2;
struct res_rec answer_rec;
int ttl=0;
int rcode=0;
BOOL unique = True;
DEBUG(3,("Name query for %s from %s (bcast=%s) - ",
namestr(&nmb->question.question_name),
inet_ntoa(p->ip),
BOOLSTR(bcast)));
if (wildcard)
retip = myip;
if (!wildcard) {
struct name_record *n = find_name(&nmb->question.question_name);
if (!n) {
struct in_addr ip;
unsigned long a;
/* only do DNS lookups if the query is for type 0x20 or type 0x0 */
if (name_type != 0x20 && name_type != 0) {
DEBUG(3,("not found\n"));
return;
}
/* look it up with DNS */
a = interpret_addr(qname);
putip((char *)&ip,(char *)&a);
if (!a) {
/* no luck with DNS. We could possibly recurse here XXXX */
/* if this isn't a bcast then we should send a negative reply XXXX */
DEBUG(3,("no recursion\n"));
add_host_entry(qname,name_type,True,60*60,DNSFAIL,ip);
return;
}
/* add it to our cache of names. give it 2 hours in the cache */
n = add_host_entry(qname,name_type,True,2*60*60,DNS,ip);
/* failed to add it? yikes! */
if (!n) return;
}
/* don't respond to bcast queries for group names unless we own them */
if (bcast && !n->unique && !n->source == SELF) {
DEBUG(3,("no bcast replies\n"));
return;
}
if (!lp_proxy_name_resolution() && n->source != SELF) {
DEBUG(3,("no proxy resolution\n"));
return;
}
/* don't respond to bcast queries for addresses on the same net as the
machine doing the querying unless its our IP */
if (bcast &&
n->source != SELF &&
same_net(n->ip,p->ip)) {
DEBUG(3,("same net\n"));
return;
}
/* is our entry already dead? */
if (n->death_time) {
if (n->death_time < p->timestamp) return;
ttl = n->death_time - p->timestamp;
}
retip = n->ip;
unique = n->unique;
/* it may have been an earlier failure */
if (n->source == DNSFAIL) {
DEBUG(3,("DNSFAIL\n"));
return;
}
}
/* if the IP is 0 then substitute my IP - we should see which one is on the
right interface for the caller to do this right XXX */
if (zero_ip(retip)) retip = myip;
DEBUG(3,("OK %s rcode=%d\n",inet_ntoa(retip),rcode));
/* a lot of fields get copied from the query. This gives us the IP
and port the reply will be sent to etc */
p2 = *p;
nmb2 = &p2.packet.nmb;
nmb2->header.response = True;
nmb2->header.nm_flags.bcast = False;
nmb2->header.nm_flags.recursion_available = CanRecurse;
nmb2->header.nm_flags.trunc = False;
nmb2->header.nm_flags.authoritative = True; /* WfWg ignores
non-authoritative answers */
nmb2->header.qdcount = 0;
nmb2->header.ancount = 1;
nmb2->header.nscount = 0;
nmb2->header.arcount = 0;
nmb2->header.rcode = rcode;
nmb2->answers = &answer_rec;
bzero((char *)nmb2->answers,sizeof(*nmb2->answers));
nmb2->answers->rr_name = nmb->question.question_name;
nmb2->answers->rr_type = nmb->question.question_type;
nmb2->answers->rr_class = nmb->question.question_class;
nmb2->answers->ttl = ttl;
nmb2->answers->rdlength = 6;
nmb2->answers->rdata[0] = unique?0:0x80;
nmb2->answers->rdata[1] = 0;
putip(&nmb2->answers->rdata[2],(char *)&retip);
send_packet(&p2);
}
/* the global packet linked-list. incoming entries are added to the
end of this list. it is supposed to remain fairly short so we
won't bother with an end pointer. */
static struct packet_struct *packet_queue = NULL;
/*******************************************************************
queue a packet into the packet queue
******************************************************************/
static 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;
}
/****************************************************************************
process a nmb packet
****************************************************************************/
static void process_nmb(struct packet_struct *p)
{
struct nmb_packet *nmb = &p->packet.nmb;
/* if this is a response then ignore it */
if (nmb->header.response) return;
switch (nmb->header.opcode)
{
case 5:
case 8:
case 9:
if (nmb->header.qdcount>0 &&
nmb->header.arcount>0) {
reply_name_reg(p);
return;
}
break;
case 0:
if (nmb->header.qdcount>0)
{
switch (nmb->question.question_type)
{
case 0x20:
reply_name_query(p);
break;
case 0x21:
reply_name_status(p);
break;
}
return;
}
break;
case 6:
if (nmb->header.qdcount>0 &&
nmb->header.arcount>0) {
reply_name_release(p);
return;
}
break;
}
}
/*******************************************************************
run elements off the packet queue till its empty
******************************************************************/
static void run_packet_queue(void)
{
struct packet_struct *p;
while ((p=packet_queue)) {
switch (p->packet_type)
{
case NMB_PACKET:
process_nmb(p);
break;
case DGRAM_PACKET:
process_dgram(p);
break;
}
packet_queue = packet_queue->next;
if (packet_queue) packet_queue->prev = NULL;
free_packet(p);
}
}
/****************************************************************************
The main select loop, listen for packets and respond
***************************************************************************/
void process(void)
{
while (True)
{
fd_set fds;
int selrtn;
struct timeval timeout;
if (needelection && PrimaryGroup[0] && !RunningElection) {
DEBUG(3,(">>> Starting election on %s <<<\n",PrimaryGroup));
ElectionCount = 0;
RunningElection = True;
needelection = False;
}
FD_ZERO(&fds);
FD_SET(ClientNMB,&fds);
FD_SET(ClientDGRAM,&fds);
/* during elections we need to send election packets at one
second intervals */
timeout.tv_sec = RunningElection?1:NMBD_SELECT_LOOP;
timeout.tv_usec = 0;
selrtn = sys_select(&fds,&timeout);
if (FD_ISSET(ClientNMB,&fds)) {
struct packet_struct *packet = read_packet(ClientNMB,NMB_PACKET);
if (packet) queue_packet(packet);
}
if (FD_ISSET(ClientDGRAM,&fds)) {
struct packet_struct *packet = read_packet(ClientDGRAM,DGRAM_PACKET);
if (packet) queue_packet(packet);
}
if (RunningElection)
run_election();
run_packet_queue();
do_announcements();
housekeeping();
}
}
/****************************************************************************
open the socket communication
****************************************************************************/
static BOOL open_sockets(BOOL isdaemon,int port)
{
struct hostent *hp;
/* get host info */
if ((hp = Get_Hostbyname(myhostname)) == 0)
{
DEBUG(0,( "Get_Hostbyname: Unknown host. %s\n",myhostname));
return False;
}
if (isdaemon)
ClientNMB = open_socket_in(SOCK_DGRAM, port,0);
else
ClientNMB = 0;
ClientDGRAM = open_socket_in(SOCK_DGRAM,DGRAM_PORT,3);
if (ClientNMB == -1)
return(False);
signal(SIGPIPE, SIGNAL_CAST sig_pipe);
set_socket_options(ClientNMB,"SO_BROADCAST");
set_socket_options(ClientDGRAM,"SO_BROADCAST");
DEBUG(3, ("Socket opened.\n"));
return True;
}
/*******************************************************************
check that a IP, bcast and netmask and consistent. Must be a 1s
broadcast
******************************************************************/
static BOOL ip_consistent(struct in_addr ip,struct in_addr bcast,
struct in_addr nmask)
{
unsigned long a_ip,a_bcast,a_nmask;
a_ip = ntohl(ip.s_addr);
a_bcast = ntohl(bcast.s_addr);
a_nmask = ntohl(nmask.s_addr);
/* check the netmask is sane */
if (((a_nmask>>24)&0xFF) != 0xFF) {
DEBUG(0,("Insane netmask %s\n",inet_ntoa(nmask)));
return(False);
}
/* check the IP and bcast are on the same net */
if ((a_ip&a_nmask) != (a_bcast&a_nmask)) {
DEBUG(0,("IP and broadcast are on different nets!\n"));
return(False);
}
/* check the IP and bcast are on the same net */
if ((a_bcast|a_nmask) != 0xFFFFFFFF) {
DEBUG(0,("Not a ones based broadcast %s\n",inet_ntoa(bcast)));
return(False);
}
return(True);
}
/****************************************************************************
initialise connect, service and file structs
****************************************************************************/
static BOOL init_structs(void )
{
if (!get_myname(myhostname,got_myip?NULL:&myip))
return(False);
/* Read the broadcast address from the interface */
{
struct in_addr ip0,ip1,ip2;
ip0 = myip;
if (!(got_bcast && got_nmask))
{
get_broadcast(&ip0,&ip1,&ip2);
if (!got_myip)
myip = ip0;
if (!got_bcast)
bcast_ip = ip1;
if (!got_nmask)
Netmask = ip2;
}
DEBUG(1,("Using IP %s ",inet_ntoa(myip)));
DEBUG(1,("broadcast %s ",inet_ntoa(bcast_ip)));
DEBUG(1,("netmask %s\n",inet_ntoa(Netmask)));
if (!ip_consistent(myip,bcast_ip,Netmask)) {
DEBUG(0,("WARNING: The IP address, broadcast and Netmask are not consistent\n"));
DEBUG(0,("You are likely to experience problems with this setup!\n"));
}
}
if (! *myname) {
char *p;
strcpy(myname,myhostname);
p = strchr(myname,'.');
if (p) *p = 0;
}
{
extern fstring local_machine;
strcpy(local_machine,myname);
strupper(local_machine);
}
return True;
}
/****************************************************************************
usage on the program
****************************************************************************/
static void usage(char *pname)
{
DEBUG(0,("Incorrect program usage - is the command line correct?\n"));
printf("Usage: %s [-n name] [-B bcast address] [-D] [-p port] [-d debuglevel] [-l log basename]\n",pname);
printf("Version %s\n",VERSION);
printf("\t-D become a daemon\n");
printf("\t-p port listen on the specified port\n");
printf("\t-d debuglevel set the debuglevel\n");
printf("\t-l log basename. Basename for log/debug files\n");
printf("\t-n netbiosname. the netbios name to advertise for this host\n");
printf("\t-B broadcast address the address to use for broadcasts\n");
printf("\t-N netmask the netmask to use for subnet determination\n");
printf("\t-H hosts file load a netbios hosts file\n");
printf("\t-I ip-address override the IP address\n");
printf("\t-G group name add a group name to be part of\n");
printf("\t-C comment sets the machine comment that appears in browse lists\n");
printf("\n");
}
/****************************************************************************
main program
**************************************************************************/
int main(int argc,char *argv[])
{
int port = NMB_PORT;
int opt;
extern FILE *dbf;
extern char *optarg;
*host_file = 0;
#if 0
sleep(10);
#endif
StartupTime = time(NULL);
TimeInit();
strcpy(debugf,NMBLOGFILE);
setup_logging(argv[0],False);
charset_initialise();
#ifdef LMHOSTSFILE
strcpy(host_file,LMHOSTSFILE);
#endif
/* this is for people who can't start the program correctly */
while (argc > 1 && (*argv[1] != '-'))
{
argv++;
argc--;
}
fault_setup(fault_continue);
signal(SIGHUP,SIGNAL_CAST sig_hup);
bcast_ip = *interpret_addr2("0.0.0.0");
myip = *interpret_addr2("0.0.0.0");
while ((opt = getopt (argc, argv, "s:T:I:C:bAi:B:N:Rn:l:d:Dp:hSH:G:")) != EOF)
switch (opt)
{
case 's':
strcpy(servicesf,optarg);
break;
case 'C':
strcpy(ServerComment,optarg);
break;
case 'G':
add_domain_entry(optarg,bcast_ip);
break;
case 'H':
strcpy(host_file,optarg);
break;
case 'I':
myip = *interpret_addr2(optarg);
got_myip = True;
break;
case 'B':
bcast_ip = *interpret_addr2(optarg);
got_bcast = True;
break;
case 'N':
Netmask = *interpret_addr2(optarg);
got_nmask = True;
break;
case 'n':
strcpy(myname,optarg);
break;
case 'l':
sprintf(debugf,"%s.nmb",optarg);
break;
case 'i':
strcpy(scope,optarg);
strupper(scope);
break;
case 'D':
is_daemon = True;
break;
case 'd':
DEBUGLEVEL = atoi(optarg);
break;
case 'p':
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\n"));
init_structs();
if (!reload_services(False))
return(-1);
if (*host_file)
{
load_hosts_file(host_file);
DEBUG(3,("Loaded hosts file\n"));
}
if (!*ServerComment)
strcpy(ServerComment,"Samba %v");
string_sub(ServerComment,"%v",VERSION);
string_sub(ServerComment,"%h",myhostname);
add_my_names();
DEBUG(3,("Checked names\n"));
dump_names();
DEBUG(3,("Dumped names\n"));
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) {
DEBUG(2,("%s becoming a daemon\n",timestring()));
become_daemon();
}
DEBUG(3,("Opening sockets\n"));
if (open_sockets(is_daemon,port))
{
process();
close_sockets();
}
if (dbf)
fclose(dbf);
return(0);
}