summaryrefslogtreecommitdiff
path: root/source/namework.c
diff options
context:
space:
mode:
Diffstat (limited to 'source/namework.c')
-rw-r--r--source/namework.c800
1 files changed, 800 insertions, 0 deletions
diff --git a/source/namework.c b/source/namework.c
new file mode 100644
index 00000000000..108048d5001
--- /dev/null
+++ b/source/namework.c
@@ -0,0 +1,800 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ NBT netbios routines and daemon - version 2
+ Copyright (C) Andrew Tridgell 1994-1996
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ 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:
+
+ 14 jan 96: lkcl@pires.co.uk
+ added multiple workgroup domain master support
+
+*/
+
+#include "includes.h"
+
+extern int ClientNMB;
+extern int ClientDGRAM;
+
+#define TEST_CODE /* want to debug unknown browse packets */
+
+extern int DEBUGLEVEL;
+extern pstring scope;
+extern BOOL CanRecurse;
+
+extern pstring myname;
+
+extern int ClientNMB;
+extern int ClientDGRAM;
+
+extern struct in_addr ipzero;
+
+extern int workgroup_count; /* total number of workgroups we know about */
+
+/* this is our domain/workgroup/server database */
+extern struct subnet_record *subnetlist;
+
+/* machine comment for host announcements */
+extern pstring ServerComment;
+
+extern int updatecount;
+
+/* what server type are we currently */
+#define DFLT_SERVER_TYPE (SV_TYPE_WORKSTATION | SV_TYPE_SERVER | \
+ SV_TYPE_TIME_SOURCE | SV_TYPE_SERVER_UNIX |\
+ SV_TYPE_PRINTQ_SERVER | SV_TYPE_POTENTIAL_BROWSER)
+
+/* backup request types: which servers are to be included */
+#define MASTER_TYPE (SV_TYPE_MASTER_BROWSER)
+#define DOMCTL_TYPE (SV_TYPE_DOMAIN_CTRL )
+
+extern time_t StartupTime;
+
+extern BOOL updatedlists;
+
+/****************************************************************************
+tell a server to become a backup browser
+state - 0x01 become backup instead of master
+ - 0x02 remove all entries in browse list and become non-master
+ - 0x04 stop master browser service altogether. NT ignores this
+**************************************************************************/
+void reset_server(char *name, int state, struct in_addr ip)
+{
+ char outbuf[20];
+ char *p;
+
+ bzero(outbuf,sizeof(outbuf));
+ p = outbuf;
+
+ CVAL(p,0) = ANN_ResetBrowserState;
+ CVAL(p,2) = state;
+ p += 2;
+
+ DEBUG(2,("sending reset to %s %s of state %d\n",
+ name,inet_ntoa(ip),state));
+
+ send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,PTR_DIFF(p,outbuf),
+ myname,name,0x20,0x1d,ip,*iface_ip(ip));
+}
+
+
+/****************************************************************************
+tell a server to become a backup browser
+**************************************************************************/
+void tell_become_backup(void)
+{
+ /* 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.
+ */
+
+ struct subnet_record *d;
+ for (d = subnetlist; d; d = d->next)
+ {
+ struct work_record *work;
+ for (work = d->workgrouplist; work; work = work->next)
+ {
+ struct server_record *s;
+ int num_servers = 0;
+ int num_backups = 0;
+
+ for (s = work->serverlist; s; s = s->next)
+ {
+ if (s->serv.type & SV_TYPE_DOMAIN_ENUM) continue;
+
+ num_servers++;
+
+ if (strequal(myname, s->serv.name)) continue;
+
+ if (s->serv.type & SV_TYPE_BACKUP_BROWSER) {
+ num_backups++;
+ continue;
+ }
+
+ if (s->serv.type & SV_TYPE_MASTER_BROWSER) continue;
+
+ if (!(s->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",
+ s->serv.name, inet_ntoa(d->bcast_ip),
+ work->work_group));
+
+ /* type 11 request from MYNAME(20) to WG(1e) for SERVER */
+ do_announce_request(s->serv.name, work->work_group,
+ ANN_BecomeBackup, 0x20, 0x1e, d->bcast_ip);
+ }
+ }
+ }
+}
+
+
+/*******************************************************************
+ same context: scope. should check name_type as well, and makes sure
+ we don't process messages from ourselves
+ ******************************************************************/
+BOOL same_context(struct dgram_packet *dgram)
+{
+ if (!strequal(dgram->dest_name .scope,scope )) return(True);
+ if ( strequal(dgram->source_name.name ,myname)) return(True);
+
+ return(False);
+}
+
+
+/*******************************************************************
+ am I listening on a name. XXXX check the type of name as well.
+ ******************************************************************/
+BOOL listening_name(struct work_record *work, struct nmb_name *n)
+{
+ if (strequal(n->name,myname) ||
+ strequal(n->name,work->work_group) ||
+ 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,uint16 command,char *buf)
+{
+ struct dgram_packet *dgram = &p->packet.dgram;
+ struct in_addr ip = dgram->header.source_ip;
+ struct subnet_record *d = find_subnet(ip);
+ 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);
+ uint32 browse_type= CVAL(buf,27);
+ uint32 browse_sig = CVAL(buf,29);
+ char *comment = buf+31;
+
+ struct work_record *work;
+ char *work_name;
+ char *serv_name = dgram->source_name.name;
+ BOOL add = False;
+
+ comment[43] = 0;
+
+ DEBUG(4,("Announce(%d) %s(%x)",command,name,name[15]));
+ DEBUG(4,("%s count=%d ttl=%d OS=(%d,%d) type=%08x sig=%4x %4x comment=%s\n",
+ namestr(&dgram->dest_name),update_count,ttl,osmajor,osminor,
+ servertype,browse_type,browse_sig,comment));
+
+ name[15] = 0;
+
+ if (dgram->dest_name.name_type == 0 && command == ANN_HostAnnouncement)
+ {
+ DEBUG(2,("Announce to nametype(0) not supported yet\n"));
+ return;
+ }
+
+ if (command == ANN_DomainAnnouncement &&
+ ((!strequal(dgram->dest_name.name, MSBROWSE)) ||
+ dgram->dest_name.name_type != 0x1))
+ {
+ DEBUG(0,("Announce(%d) from %s should be __MSBROWSE__(1) not %s\n",
+ command, inet_ntoa(ip), namestr(&dgram->dest_name)));
+ return;
+ }
+
+ if (!strequal(dgram->dest_name.scope,scope )) return;
+
+ if (command == ANN_DomainAnnouncement) {
+ /* XXXX if we are a master browser for the workgroup work_name,
+ then there is a local subnet configuration problem. only
+ we should be sending out such domain announcements, because
+ as the master browser, that is our job.
+
+ stop being a master browser, and force an election. this will
+ sort out the network problem. hopefully.
+ */
+
+ work_name = name;
+ add = True;
+ } else {
+ work_name = dgram->dest_name.name;
+ }
+
+ /* we need some way of finding out about new workgroups
+ that appear to be sending packets to us. The name_type checks make
+ sure we don't add host names as workgroups */
+ if (command == ANN_HostAnnouncement &&
+ (dgram->dest_name.name_type == 0x1d ||
+ dgram->dest_name.name_type == 0x1e))
+ add = True;
+
+ if (!(work = find_workgroupstruct(d, work_name,add)))
+ return;
+
+ DEBUG(4, ("workgroup %s on %s\n", work->work_group, serv_name));
+
+ ttl = GET_TTL(ttl);
+
+ /* add them to our browse list, and update the browse.dat file */
+ add_server_entry(d,work,name,servertype,ttl,comment,True);
+ updatedlists = True;
+
+#if 0
+ /* the tell become backup code is broken, no great harm is done by
+ disabling it */
+ tell_become_backup();
+#endif
+
+ /* get the local_only browse list from the local master and add it
+ to ours. */
+ if (command == ANN_LocalMasterAnnouncement)
+ {
+ add_browser_entry(serv_name,dgram->dest_name.name_type,
+ work->work_group,30,ip,True);
+ }
+}
+
+/*******************************************************************
+ process a master announcement frame
+ ******************************************************************/
+static void process_master_announce(struct packet_struct *p,char *buf)
+{
+ struct dgram_packet *dgram = &p->packet.dgram;
+ struct in_addr ip = dgram->header.source_ip;
+ struct subnet_record *d = find_subnet(ip);
+ struct subnet_record *mydomain = find_subnet(*iface_bcast(ip));
+ char *name = buf;
+ struct work_record *work;
+ name[15] = 0;
+
+ DEBUG(3,("Master Announce from %s (%s)\n",name,inet_ntoa(ip)));
+
+ if (same_context(dgram)) return;
+
+ if (!d || !mydomain) return;
+
+ if (!lp_domain_master()) return;
+
+ for (work = mydomain->workgrouplist; work; work = work->next)
+ {
+ if (AM_MASTER(work))
+ {
+ /* merge browse lists with them */
+ add_browser_entry(name,0x1b, work->work_group,30,ip,True);
+ }
+ }
+}
+
+/*******************************************************************
+ process a receive backup list request
+
+ we receive a list of servers, and we attempt to locate them all on
+ our local subnet, and sync browse lists with them on the workgroup
+ they are said to be in.
+
+ XXXX NOTE: this function is in overdrive. it should not really do
+ half of what it actually does (it should pick _one_ name from the
+ list received and sync with it at regular intervals, rather than
+ sync with them all only once!)
+
+ ******************************************************************/
+static void process_rcv_backup_list(struct packet_struct *p,char *buf)
+{
+ struct dgram_packet *dgram = &p->packet.dgram;
+ struct in_addr ip = dgram->header.source_ip;
+ int count = CVAL(buf,0);
+ uint32 info = IVAL(buf,1); /* XXXX caller's incremental info */
+ char *buf1;
+
+ DEBUG(3,("Receive Backup ack for %s from %s total=%d info=%d\n",
+ namestr(&dgram->dest_name), inet_ntoa(ip),
+ count, info));
+
+ if (same_context(dgram)) return;
+
+ if (count <= 0) return;
+
+ /* go through the list of servers attempting to sync browse lists */
+ for (buf1 = buf+5; *buf1 && count; buf1 = skip_string(buf1, 1), --count)
+ {
+ struct in_addr back_ip;
+ struct subnet_record *d;
+
+ DEBUG(4,("Searching for backup browser %s at %s...\n",
+ buf1, inet_ntoa(ip)));
+
+ /* XXXX assume name is a DNS name NOT a netbios name. a more complete
+ approach is to use reply_name_query functionality to find the name */
+
+ back_ip = *interpret_addr2(buf1);
+
+ if (zero_ip(back_ip))
+ {
+ DEBUG(4,("Failed to find backup browser server using DNS\n"));
+ continue;
+ }
+
+ DEBUG(4,("Found browser server at %s\n", inet_ntoa(back_ip)));
+ DEBUG(4,("END THIS LOOP: CODE NEEDS UPDATING\n"));
+
+ /* XXXX function needs work */
+ continue;
+
+ if ((d = find_subnet(back_ip)))
+ {
+ struct subnet_record *d1;
+ for (d1 = subnetlist; d1; d1 = d1->next)
+ {
+ struct work_record *work;
+ for (work = d1->workgrouplist; work; work = work->next)
+ {
+ if (work->token == 0 /* token */)
+ {
+ queue_netbios_packet(d1,ClientNMB,NMB_QUERY,NAME_QUERY_SRV_CHK,
+ work->work_group,0x1d,
+ 0,0,0,NULL,NULL,
+ False,False,back_ip,back_ip);
+ return;
+ }
+ }
+ }
+ }
+ }
+}
+
+
+/****************************************************************************
+ send a backup list response.
+ **************************************************************************/
+static void send_backup_list(char *work_name, struct nmb_name *src_name,
+ int token, uint32 info,
+ int name_type, struct in_addr ip)
+{
+ char outbuf[1024];
+ char *p, *countptr, *nameptr;
+ int count = 0;
+ char *theirname = src_name->name;
+
+ DEBUG(3,("sending backup list of %s to %s: %s(%x) %s(%x)\n",
+ work_name, inet_ntoa(ip),
+ myname,0x0,theirname,0x0));
+
+ if (name_type == 0x1d)
+ {
+ DEBUG(4,("master browsers: "));
+ }
+ else if (name_type == 0x1b)
+ {
+ DEBUG(4,("domain controllers: "));
+ }
+ else
+ {
+ DEBUG(0,("backup request for unknown type %0x\n", name_type));
+ return;
+ }
+
+ bzero(outbuf,sizeof(outbuf));
+ p = outbuf;
+
+ CVAL(p,0) = ANN_GetBackupListResp; /* backup list response */
+
+ p++;
+ countptr = p;
+
+ SIVAL(p,1,info); /* the sender's unique info */
+
+ p += 5;
+
+ nameptr = p;
+
+#if 0
+
+ for (d = subnetlist; d; d = d->next)
+ {
+ struct work_record *work;
+
+ for (work = d->workgrouplist; work; work = work->next)
+ {
+ struct server_record *s;
+
+ if (!strequal(work->work_group, work_name)) continue;
+
+ for (s = work->serverlist; s; s = s->next)
+ {
+ BOOL found = False;
+ char *n;
+
+ if (s->serv.type & SV_TYPE_DOMAIN_ENUM) continue;
+
+ for (n = nameptr; n < p; n = skip_string(n, 1))
+ {
+ if (strequal(n, s->serv.name)) found = True;
+ }
+
+ if (found) continue; /* exclude names already added */
+
+ /* workgroup request: include all backup browsers in the list */
+ /* domain request: include all domain members in the list */
+
+ if ((name_type == 0x1d && (s->serv.type & MASTER_TYPE)) ||
+ (name_type == 0x1b && (s->serv.type & DOMCTL_TYPE)))
+ {
+ DEBUG(4, ("%s ", s->serv.name));
+
+ count++;
+ strcpy(p,s->serv.name);
+ strupper(p);
+ p = skip_string(p,1);
+ }
+ }
+ }
+ }
+
+#endif
+
+ count++;
+ strcpy(p,myname);
+ strupper(p);
+ p = skip_string(p,1);
+
+ if (count == 0)
+ {
+ DEBUG(4, ("none\n"));
+ }
+ else
+ {
+ DEBUG(4, (" - count %d\n", count));
+ }
+
+ CVAL(countptr, 0) = count;
+
+ {
+ int len = PTR_DIFF(p, outbuf);
+ debug_browse_data(outbuf, len);
+ }
+ send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,PTR_DIFF(p,outbuf),
+ myname,theirname,0x0,0x0,ip,*iface_ip(ip));
+}
+
+
+/*******************************************************************
+ process a send backup list request
+
+ 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.
+
+ Currently samba only sends back one name in the backup list, its
+ own. For larger nets we'll have to add backups and send "become
+ backup" requests occasionally.
+ ******************************************************************/
+static void process_send_backup_list(struct packet_struct *p,char *buf)
+{
+ struct dgram_packet *dgram = &p->packet.dgram;
+ struct in_addr ip = dgram->header.source_ip;
+ struct subnet_record *d;
+ struct work_record *work;
+
+ int token = CVAL(buf,0); /* sender's key index for the workgroup */
+ uint32 info = IVAL(buf,1); /* XXXX don't know: some sort of info */
+ int name_type = dgram->dest_name.name_type;
+
+ if (same_context(dgram)) return;
+
+ if (name_type != 0x1b && name_type != 0x1d) {
+ DEBUG(0,("backup request to wrong type %d from %s\n",
+ name_type,inet_ntoa(ip)));
+ return;
+ }
+
+ for (d = subnetlist; d; d = d->next)
+ {
+ for (work = d->workgrouplist; work; work = work->next)
+ {
+ if (strequal(work->work_group, dgram->dest_name.name))
+ {
+ DEBUG(2,("sending backup list to %s %s id=%x\n",
+ namestr(&dgram->dest_name),inet_ntoa(ip),info));
+
+ send_backup_list(work->work_group,&dgram->source_name,
+ token,info,name_type,ip);
+ return;
+ }
+ }
+ }
+}
+
+
+/*******************************************************************
+ process a reset browser state
+
+ 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. no way. ain't gonna.
+
+ ******************************************************************/
+static void process_reset_browser(struct packet_struct *p,char *buf)
+{
+ struct dgram_packet *dgram = &p->packet.dgram;
+ int state = CVAL(buf,0);
+
+ DEBUG(1,("received diagnostic browser reset request to %s state=0x%X\n",
+ namestr(&dgram->dest_name), state));
+
+ /* stop being a master but still deal with being a backup browser */
+ if (state & 0x1)
+ {
+ struct subnet_record *d;
+ for (d = subnetlist; d; d = d->next)
+ {
+ struct work_record *work;
+ for (work = d->workgrouplist; work; work = work->next)
+ {
+ if (AM_MASTER(work))
+ {
+ become_nonmaster(d,work,SV_TYPE_DOMAIN_MASTER|SV_TYPE_MASTER_BROWSER);
+ }
+ }
+ }
+ }
+
+ /* XXXX documentation inconsistency: the above description does not
+ exactly tally with what is implemented for state & 0x2
+ */
+
+ /* totally delete all servers and start afresh */
+ if (state & 0x2)
+ {
+ struct subnet_record *d;
+ for (d = subnetlist; d; d = d->next)
+ {
+ struct work_record *work;
+ for (work=d->workgrouplist;work;work=remove_workgroup(d,work,True));
+ }
+ add_my_subnets(lp_workgroup());
+ }
+
+ /* stop browsing altogether. i don't think this is a good idea! */
+ if (state & 0x4)
+ {
+ DEBUG(1,("ignoring request to stop being a browser. sorry!\n"));
+ }
+}
+
+/*******************************************************************
+ 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;
+ struct work_record *work;
+ struct in_addr ip = dgram->header.source_ip;
+ struct subnet_record *d = find_subnet(ip);
+ int token = CVAL(buf,0);
+ char *name = buf+1;
+
+ name[15] = 0;
+
+ DEBUG(3,("Announce request from %s to %s token=0x%X\n",
+ name,namestr(&dgram->dest_name), token));
+
+ if (strequal(dgram->source_name.name,myname)) return;
+
+ /* XXXX BUG or FEATURE?: need to ensure that we are a member of
+ this workgroup before announcing, particularly as we only
+ respond on local interfaces anyway.
+
+ if (strequal(dgram->dest_name, lp_workgroup()) return; ???
+ */
+
+ if (!d) return;
+
+ for (work = d->workgrouplist; work; work = work->next)
+ {
+ /* XXXX BUG: the destination name type should also be checked,
+ not just the name. e.g if the name is WORKGROUP(0x1d) then
+ we should only respond if we own that name */
+
+ if (strequal(dgram->dest_name.name,work->work_group))
+ {
+ work->needannounce = True;
+ }
+ }
+}
+
+
+/****************************************************************************
+depending on what announce has been made, we are only going to
+accept certain types of name announce. XXXX untested code
+
+check listening name type
+****************************************************************************/
+BOOL listening_type(struct packet_struct *p, int command)
+{
+ struct dgram_packet *dgram = &p->packet.dgram;
+ int type = dgram->dest_name.name_type;
+
+ switch (command)
+ {
+ case ANN_HostAnnouncement:
+ {
+ if (type != 0x0 || type != 0x20) return (False);
+ break;
+ }
+
+ case ANN_AnnouncementRequest:
+ {
+ return (True);
+ break;
+ }
+
+ case ANN_Election:
+ {
+ return (True);
+ break;
+ }
+
+ case ANN_GetBackupListReq:
+ {
+ return (True);
+ break;
+ }
+
+ case ANN_GetBackupListResp:
+ {
+ return (True);
+ break;
+ }
+
+ case ANN_DomainAnnouncement:
+ {
+ if (type != 0x1b || type != 0x1c) return (False);
+ break;
+ }
+
+ case ANN_MasterAnnouncement:
+ {
+ if (type != 0x1d) return (False);
+ break;
+ }
+
+ case ANN_LocalMasterAnnouncement:
+ {
+ if (type != 0x1c || type != 0x1d) return (False);
+ break;
+ }
+ }
+ return (True); /* we're not dealing with unknown packet types */
+}
+
+
+/****************************************************************************
+process a browse frame
+****************************************************************************/
+void process_browse_packet(struct packet_struct *p,char *buf,int len)
+{
+ int command = CVAL(buf,0);
+ switch (command)
+ {
+ case ANN_HostAnnouncement:
+ case ANN_DomainAnnouncement:
+ case ANN_LocalMasterAnnouncement:
+ {
+ debug_browse_data(buf, len);
+ process_announce(p,command,buf+1);
+ break;
+ }
+
+ case ANN_AnnouncementRequest:
+ {
+ process_announce_request(p,buf+1);
+ break;
+ }
+
+ case ANN_Election:
+ {
+ process_election(p,buf+1);
+ break;
+ }
+
+ case ANN_GetBackupListReq:
+ {
+ debug_browse_data(buf, len);
+ process_send_backup_list(p,buf+1);
+ break;
+ }
+
+ case ANN_GetBackupListResp:
+ {
+ debug_browse_data(buf, len);
+ process_rcv_backup_list(p, buf+1);
+ break;
+ }
+
+ case ANN_ResetBrowserState:
+ {
+ process_reset_browser(p, buf+1);
+ break;
+ }
+
+ case ANN_MasterAnnouncement:
+ {
+ process_master_announce(p,buf+1);
+ break;
+ }
+
+ default:
+ {
+ struct dgram_packet *dgram = &p->packet.dgram;
+ DEBUG(4,("ignoring browse packet %d from %s %s to %s\n",
+ command, namestr(&dgram->source_name),
+ inet_ntoa(p->ip), namestr(&dgram->dest_name)));
+ }
+ }
+}
+
+