Unix SMB/Netbios implementation.
Version 1.9.
NBT netbios routines and daemon - version 2
- Copyright (C) Andrew Tridgell 1994-1995
+ Copyright (C) Andrew Tridgell 1994-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
extern struct in_addr ipzero;
extern pstring myname;
+extern fstring myworkgroup;
extern int ClientDGRAM;
extern int ClientNMB;
/* this is our domain/workgroup/server database */
extern struct subnet_record *subnetlist;
-/* machine comment for host announcements */
-extern pstring ServerComment;
-
extern int updatecount;
extern int workgroup_count;
-/* what server type are we currently */
+extern struct in_addr wins_ip;
-#define BROWSE_MAILSLOT "\\MAILSLOT\\BROWSE"
+extern pstring scope;
/****************************************************************************
send a announce request to the local net
CVAL(p,0) = ANN_AnnouncementRequest;
p++;
- CVAL(p,0) = work->token; /* flags?? XXXX probably a token*/
+ CVAL(p,0) = work->token; /* (local) unique workgroup token id */
p++;
StrnCpy(p,myname,16);
strupper(p);
p = skip_string(p,1);
- send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,PTR_DIFF(p,outbuf),
+ /* XXXX note: if we sent the announcement request to 0x1d instead
+ of 0x1e, then we could get the master browser to announce to
+ us instead of the members of the workgroup. wha-hey! */
+
+ send_mailslot_reply(False, BROWSE_MAILSLOT,ClientDGRAM,
+ outbuf,PTR_DIFF(p,outbuf),
myname,work->work_group,0x20,0x1e,ip,*iface_ip(ip));
}
strupper(p);
p = skip_string(p,1);
- send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,PTR_DIFF(p,outbuf),
+ send_mailslot_reply(False,BROWSE_MAILSLOT,ClientDGRAM,
+ outbuf,PTR_DIFF(p,outbuf),
myname,to_name,from,to,dest_ip,*iface_ip(dest_ip));
}
+
/****************************************************************************
- construct a host announcement unicast
+ find a server responsible for a workgroup, and sync browse lists
+ control ends up back here via response_name_query.
**************************************************************************/
-void announce_backup(void)
-{
- static time_t lastrun = 0;
- time_t t = time(NULL);
- pstring outbuf;
- char *p;
- struct subnet_record *d1;
- int tok;
-
- if (!lastrun) lastrun = t;
- if (t < lastrun + CHECK_TIME_ANNOUNCE_BACKUP * 60)
- return;
- lastrun = t;
-
- for (tok = 0; tok <= workgroup_count; tok++)
- {
- for (d1 = subnetlist; d1; d1 = d1->next)
- {
- struct work_record *work;
- struct subnet_record *d;
-
- /* search for unique workgroup: only the name matters */
- for (work = d1->workgrouplist;
- work && (tok != work->token);
- work = work->next);
-
- if (!work) continue;
-
- /* found one: announce it across all domains */
- for (d = subnetlist; d; d = d->next)
- {
- int type=0;
-
- if (AM_DOMCTL(work)) {
- type = 0x1b;
- } else if (AM_MASTER(work)) {
- type = 0x1d;
- } else {
- continue;
- }
-
- DEBUG(2,("sending announce backup %s workgroup %s(%d)\n",
- inet_ntoa(d->bcast_ip),work->work_group,
- work->token));
-
- bzero(outbuf,sizeof(outbuf));
- p = outbuf;
- CVAL(p,0) = ANN_GetBackupListReq;
- p++;
-
- CVAL(p,0) = 1; /* count? */
- SIVAL(p,1,work->token); /* workgroup unique key index */
- p += 5;
- p++;
-
- send_mailslot_reply(BROWSE_MAILSLOT,
- ClientDGRAM,outbuf,
- PTR_DIFF(p,outbuf),
- myname, work->work_group,
- 0x0,type,d->bcast_ip,
- *iface_ip(d->bcast_ip));
- }
- }
- }
+void sync_server(enum state_type state, char *serv_name, char *work_name,
+ int name_type,
+ struct subnet_record *d,
+ struct in_addr ip)
+{
+ /* with a domain master we can get the whole list (not local only list) */
+ BOOL local_only = (state != NAME_STATUS_DOM_SRV_CHK);
+
+ add_browser_entry(serv_name, name_type, work_name, 0, d, ip, local_only);
+
+ if (state == NAME_STATUS_DOM_SRV_CHK)
+ {
+ /* announce ourselves as a master browser to serv_name */
+ do_announce_request(myname, serv_name, ANN_MasterAnnouncement,
+ 0x20, 0, ip);
+ }
}
void do_announce_host(int command,
char *from_name, int from_type, struct in_addr from_ip,
char *to_name , int to_type , struct in_addr to_ip,
- int updatecount, time_t announce_interval,
+ time_t announce_interval,
char *server_name, int server_type, char *server_comment)
{
pstring outbuf;
StrnCpy(p+5,server_name,16);
strupper(p+5);
- CVAL(p,21) = 2; /* major version */
- CVAL(p,22) = 2; /* minor version */
+ CVAL(p,21) = MAJOR_VERSION; /* major version */
+ CVAL(p,22) = MINOR_VERSION; /* minor version */
SIVAL(p,23,server_type);
- SSVAL(p,27,0xaa55); /* browse signature */
- SSVAL(p,29,1); /* browse version */
+ /* 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 */
strcpy(p+31,server_comment);
p += 31;
p = skip_string(p,1);
+ debug_browse_data(outbuf, PTR_DIFF(p,outbuf));
+
/* send the announcement */
- send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,
+ send_mailslot_reply(False,BROWSE_MAILSLOT,ClientDGRAM,outbuf,
PTR_DIFF(p,outbuf),
from_name, to_name,
from_type, to_type,
}
+/****************************************************************************
+announce all samba's server entries as 'gone'.
+****************************************************************************/
+void announce_my_servers_removed(void)
+{
+ struct subnet_record *d;
+ for (d = FIRST_SUBNET; d; d = NEXT_SUBNET_EXCLUDING_WINS(d))
+ {
+ struct work_record *work;
+ for (work = d->workgrouplist; work; work = work->next)
+ {
+ struct server_record *s;
+ for (s = work->serverlist; s; s = s->next)
+ {
+ if (!strequal(myname,s->serv.name)) continue;
+ announce_server(d, work, s->serv.name, s->serv.comment, 0, 0);
+ }
+ }
+ }
+}
+
+
/****************************************************************************
announce a server entry
****************************************************************************/
void announce_server(struct subnet_record *d, struct work_record *work,
- char *name, char *comment, time_t ttl, int server_type)
+ char *name, char *comment, time_t ttl, int server_type)
{
- if (AM_MASTER(work))
+ /* domain type cannot have anything in it that might confuse
+ a client into thinking that the domain is in fact a server.
+ (SV_TYPE_SERVER_UNIX, for example)
+ */
+ uint32 domain_type = SV_TYPE_DOMAIN_ENUM|SV_TYPE_NT;
+ BOOL wins_iface = ip_equal(d->bcast_ip, wins_ip);
+
+ if (wins_iface && server_type != 0)
{
- DEBUG(3,("sending local master announce to %s for %s(1e)\n",
- inet_ntoa(d->bcast_ip),work->work_group));
-
- do_announce_host(ANN_LocalMasterAnnouncement,
- name , 0x00, d->myip,
- work->work_group, 0x1e, d->bcast_ip,
- updatecount, ttl*1000,
- name, server_type, comment);
-
- DEBUG(3,("sending domain announce to %s for %s\n",
- inet_ntoa(d->bcast_ip),work->work_group));
-
- /* XXXX should we do a domain-announce-kill? */
- if (server_type != 0)
+ /* wins pseudo-ip interface */
+ if (!AM_MASTER(work))
{
- do_announce_host(ANN_DomainAnnouncement,
- work->work_group, 0x00, d->myip,
- MSBROWSE , 0x01, d->bcast_ip,
- updatecount, ttl*1000,
- name, server_type ? SV_TYPE_DOMAIN_ENUM : 0, comment);
+ /* non-master announce by unicast to the domain
+ master */
+ if (!lp_wins_support() && *lp_wins_server())
+ {
+ /* look up the domain master with the WINS server */
+ queue_netbios_pkt_wins(ClientNMB,NMB_QUERY,
+ NAME_QUERY_ANNOUNCE_HOST,
+ work->work_group,0x1b,0,ttl*1000,
+ server_type,name,comment,
+ False, False, ipzero, d->bcast_ip);
+ }
+ else
+ {
+ /* we are the WINS server, but not the domain master. */
+ /* XXXX we need to look up the domain master in our
+ WINS database list, and do_announce_host(). maybe
+ we could do a name query on the unsuspecting domain
+ master just to make sure it's awake. */
+ }
}
+
+ /* XXXX any other kinds of announcements we need to consider here?
+ e.g local master browsers... no. local master browsers do
+ local master announcements to their domain master. they even
+ use WINS lookup of the domain master if another wins server
+ is being used!
+ */
}
else
{
- DEBUG(3,("sending host announce to %s for %s(1d)\n",
- inet_ntoa(d->bcast_ip),work->work_group));
-
- do_announce_host(ANN_HostAnnouncement,
- name , 0x00, d->myip,
- work->work_group, 0x1d, d->bcast_ip,
- updatecount, ttl*1000,
- name, server_type, comment);
+ if (AM_MASTER(work))
+ {
+ DEBUG(3,("sending local master announce to %s for %s(1e)\n",
+ inet_ntoa(d->bcast_ip),work->work_group));
+
+ do_announce_host(ANN_LocalMasterAnnouncement,
+ name , 0x00, d->myip,
+ work->work_group, 0x1e, d->bcast_ip,
+ ttl,
+ name, server_type, comment);
+
+ DEBUG(3,("sending domain announce to %s for %s\n",
+ inet_ntoa(d->bcast_ip),work->work_group));
+
+ /* XXXX should we do a domain-announce-kill? */
+ if (server_type != 0)
+ {
+ do_announce_host(ANN_DomainAnnouncement,
+ name , 0x00, d->myip,
+ MSBROWSE, 0x01, d->bcast_ip,
+ ttl,
+ work->work_group, server_type ? domain_type : 0,
+ name);
+ }
+ }
+ else
+ {
+ DEBUG(3,("sending host announce to %s for %s(1d)\n",
+ inet_ntoa(d->bcast_ip),work->work_group));
+
+ do_announce_host(ANN_HostAnnouncement,
+ name , 0x00, d->myip,
+ work->work_group, 0x1d, d->bcast_ip,
+ ttl,
+ name, server_type, comment);
+ }
}
}
/****************************************************************************
construct a host announcement unicast
**************************************************************************/
-void announce_host(void)
+void announce_host(time_t t)
{
- time_t t = time(NULL);
struct subnet_record *d;
pstring comment;
char *my_name;
- StrnCpy(comment, *ServerComment ? ServerComment : "NoComment", 43);
+ StrnCpy(comment, lp_serverstring(), 43);
my_name = *myname ? myname : "NoName";
- for (d = subnetlist; d; d = d->next)
+ for (d = FIRST_SUBNET; d; d = NEXT_SUBNET_EXCLUDING_WINS(d))
{
struct work_record *work;
struct server_record *s;
BOOL announce = False;
+ /* must work on the code that does announcements at up to
+ 30 seconds later if a master browser sends us a request
+ announce.
+ */
+
if (work->needannounce) {
/* drop back to a max 3 minute announce - this is to prevent a
single lost packet from stuffing things up for too long */
- work->announce_interval = MIN(work->announce_interval,
+ work->announce_interval = MIN(work->announce_interval,
CHECK_TIME_MIN_HOST_ANNCE*60);
work->lastannounce_time = t - (work->announce_interval+1);
}
work->announce_interval += 60;
work->lastannounce_time = t;
-
- if (!d->my_interface) {
- 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);
- }
-
+
for (s = work->serverlist; s; s = s->next) {
if (strequal(myname, s->serv.name)) {
announce = True;
}
}
- if (announce)
- {
- announce_server(d,work,my_name,comment,work->announce_interval,stype);
- }
+ if (announce) {
+ announce_server(d,work,my_name,comment,
+ work->announce_interval,stype);
+ }
if (work->needannounce)
{
}
}
+/* Announce timer. Moved into global static so it can be reset
+ when a machine becomes a master browser. */
+static time_t announce_timer_last=0;
/****************************************************************************
- announce myself as a master to all other primary domain conrollers.
+ Reset the announce_timer so that a 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 master to all other domain master browsers.
- BIG NOTE: this code will remain untested until some kind soul that has access
- to a couple of windows NT advanced servers runs this version of nmbd for at
- least 15 minutes.
-
this actually gets done in search_and_sync_workgroups() via the
- NAME_QUERY_MST_SRV_CHK command, if there is a response from the
+ NAME_QUERY_DOM_SRV_CHK command, if there is a response from the
name query initiated here. see response_name_query()
**************************************************************************/
-void announce_master(void)
+void announce_master(time_t t)
{
struct subnet_record *d;
- static time_t last=0;
- time_t t = time(NULL);
+ struct work_record *work;
BOOL am_master = False; /* are we a master of some sort? :-) */
- if (last && (t-last < CHECK_TIME_MST_ANNOUNCE * 60))
- return;
+ if (!announce_timer_last) announce_timer_last = t;
+ if (t-announce_timer_last < CHECK_TIME_MST_ANNOUNCE * 60)
+ {
+ DEBUG(10,("announce_master: t (%d) - last(%d) < %d\n",
+ t, announce_timer_last, CHECK_TIME_MST_ANNOUNCE * 60 ));
+ return;
+ }
- last = t;
+ if(wins_subnet == NULL)
+ {
+ DEBUG(10,("announce_master: no wins subnet, ignoring.\n"));
+ return;
+ }
- for (d = subnetlist; d; d = d->next)
+ announce_timer_last = t;
+
+ for (d = FIRST_SUBNET; d; d = NEXT_SUBNET_EXCLUDING_WINS(d))
{
struct work_record *work;
for (work = d->workgrouplist; work; work = work->next)
if (AM_MASTER(work))
{
am_master = True;
+ DEBUG(4,( "announce_master: am_master = %d for \
+workgroup %s\n", am_master, work->work_group));
}
}
}
-
+
if (!am_master) return; /* only proceed if we are a master browser */
- for (d = subnetlist; d; d = d->next)
+ /* Note that we don't do this if we are domain master browser
+ and that we *only* do this on the WINS subnet. */
+
+ /* Try and find our workgroup on the WINS subnet */
+ work = find_workgroupstruct(wins_subnet, myworkgroup, False);
+
+ if (work)
{
- struct work_record *work;
- for (work = d->workgrouplist; work; work = work->next)
- {
- struct server_record *s;
- for (s = work->serverlist; s; s = s->next)
- {
- if (strequal(s->serv.name, myname)) continue;
-
- /* all PDCs (which should also be master browsers) */
- if (s->serv.type & SV_TYPE_DOMAIN_CTRL)
- {
- /* check the existence of a pdc for this workgroup, and if
- one exists at the specified ip, sync with it and announce
- ourselves as a master browser to it */
-
- if (!*lp_domain_controller() ||
- !strequal(lp_domain_controller(), s->serv.name))
- {
- if (!lp_wins_support() && *lp_wins_server())
- {
- struct in_addr ip;
- ip = ipzero;
-
- queue_netbios_pkt_wins(d,ClientNMB,NMB_QUERY,
- NAME_QUERY_MST_SRV_CHK,
- work->work_group,0x1b,0,0,
- False, False, ip);
- }
- else
- {
- struct subnet_record *d2;
- for (d2 = subnetlist; d2; d2 = d2->next)
- {
- queue_netbios_packet(d,ClientNMB,NMB_QUERY,
- NAME_QUERY_MST_SRV_CHK,
- work->work_group,0x1b,0,0,
- True, False, d2->bcast_ip);
- }
- }
- }
- }
- }
-
- /* now do primary domain controller - the one that's not
- necessarily in our browse lists, although it ought to be
- this pdc is the one that we get TOLD about through smb.conf.
- basically, if it's on a subnet that we know about, it may end
- up in our browse lists (which is why it's explicitly excluded
- in the code above) */
-
- if (*lp_domain_controller())
- {
- struct in_addr ip;
- BOOL bcast = False;
-
- ip = *interpret_addr2(lp_domain_controller());
-
- if (zero_ip(ip)) {
- ip = d->bcast_ip;
- bcast = True;
- }
-
- DEBUG(2, ("Searching for PDC %s at %s\n",
- lp_domain_controller(), inet_ntoa(ip)));
-
- /* check the existence of a pdc for this workgroup, and if
- one exists at the specified ip, sync with it and announce
- ourselves as a master browser to it */
- queue_netbios_pkt_wins(d,ClientNMB, NMB_QUERY,NAME_QUERY_MST_SRV_CHK,
- work->work_group,0x1b, 0, 0,
- bcast, False, ip);
- }
- }
+ char *name;
+ int type;
+
+#if 0 /* I don't think this option should be used for this purpose.
+ JRA.
+ */
+ if (*lp_domain_controller())
+ {
+ /* the domain controller option is used to manually specify
+ the domain master browser to sync with
+ */
+
+ /* XXXX i'm not sure we should be using the domain controller
+ option for this purpose.
+ */
+
+ name = lp_domain_controller();
+ type = 0x20;
+ }
+ else
+#endif /* REMOVE SUSPECT CODE. */
+ {
+ /* assume that the domain master browser we want to sync
+ with is our own domain.
+ */
+ name = work->work_group;
+ type = 0x1b;
+ }
+
+ /* check the existence of a dmb for this workgroup, and if
+ one exists at the specified ip, sync with it and announce
+ ourselves as a master browser to it
+ */
+
+ if (!lp_wins_support() && *lp_wins_server() )
+ {
+ DEBUG(4, ("Local Announce: find %s<%02x> from WINS server %s\n",
+ name, type, lp_wins_server()));
+
+ queue_netbios_pkt_wins(ClientNMB,
+ NMB_QUERY,NAME_QUERY_DOM_SRV_CHK,
+ name, type, 0,0,0,
+ work->work_group,NULL,
+ False, False, ipzero, ipzero);
+ }
+ else if(lp_wins_support())
+ {
+ /* We are the WINS server - query ourselves for the dmb name. */
+
+ struct nmb_name netb_name;
+ struct subnet_record *d = 0;
+ struct name_record *nr = 0;
+
+ make_nmb_name(&netb_name, name, type, scope);
+
+ if ((nr = find_name_search(&d, &netb_name, FIND_WINS, ipzero)) == 0)
+ {
+ DEBUG(0, ("announce_master: unable to find domain master browser for workgroup %s \
+in our own WINS database.\n", work->work_group));
+ return;
+ }
+
+ /* Check that this isn't one of our addresses (ie. we are not domain master
+ ourselves) */
+ if(ismyip(nr->ip_flgs[0].ip) || ip_equal(nr->ip_flgs[0].ip, ipzero))
+ {
+ DEBUG(4, ("announce_master: domain master ip found (%s) for workgroup %s \
+is one of our interfaces.\n", work->work_group, inet_ntoa(nr->ip_flgs[0].ip) ));
+ return;
+ }
+
+ /* Issue a NAME_STATUS_DOM_SRV_CHK immediately - short circuit the
+ NAME_QUERY_DOM_SRV_CHK which is done only if we are talking to a
+ remote WINS server. */
+
+ DEBUG(4, ("announce_master: doing name status for %s<%02x> to domain master ip %s \
+for workgroup %s\n", name, type, inet_ntoa(nr->ip_flgs[0].ip), work->work_group ));
+
+ queue_netbios_packet(wins_subnet, ClientNMB,
+ NMB_QUERY,NAME_STATUS_DOM_SRV_CHK,
+ name, type, 0,0,0,
+ work->work_group,NULL,
+ False, False, nr->ip_flgs[0].ip, nr->ip_flgs[0].ip);
+ }
+
}
}
+
+/****************************************************************************
+ 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 browse master 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,*workgroup;
+ int stype = DFLT_SERVER_TYPE;
+
+ if (last_time && t < last_time + REMOTE_ANNOUNCE_INTERVAL)
+ return;
+
+ last_time = t;
+
+ s = lp_remote_announce();
+ if (!*s) return;
+
+ comment = lp_serverstring();
+ workgroup = myworkgroup;
+
+ 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;
+
+ wgroup = strchr(s2,'/');
+ if (wgroup) *wgroup++ = 0;
+ if (!wgroup || !*wgroup)
+ wgroup = workgroup;
+
+ addr = *interpret_addr2(s2);
+
+ do_announce_host(ANN_HostAnnouncement,myname,0x20,*iface_ip(addr),
+ wgroup,0x1e,addr,
+ REMOTE_ANNOUNCE_INTERVAL,
+ myname,stype,comment);
+ }
+
+}