From 64f0348a3f994334abe64a4d4896109c3c8c9039 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Sat, 13 Dec 1997 14:16:07 +0000 Subject: This is it ! The mega-merge of the JRA_NMBD_REWRITE branch back into the main tree. For the cvs logs of all the files starting nmbd_*.c, look in the JRA_NMBD_REWRITE branch. That branch has now been discontinued. Jeremy. (This used to be commit d80b0cb645f81d16734929a0b27a91c6650499bb) --- source3/client/clientutil.c | 11 +- source3/include/nameserv.h | 482 +++++---- source3/include/proto.h | 478 +++++---- source3/lib/interface.c | 37 +- source3/libsmb/namequery.c | 36 +- source3/libsmb/nmblib.c | 161 ++- source3/nameannounce.c | 554 ---------- source3/nameannounce.doc | 265 ----- source3/namebrowse.c | 241 ----- source3/namebrowse.doc | 149 --- source3/namedbname.c | 518 ---------- source3/namedbname.doc | 182 ---- source3/namedbresp.c | 167 ---- source3/namedbresp.doc | 100 -- source3/namedbserver.c | 207 ---- source3/namedbsubnet.c | 400 -------- source3/namedbwork.c | 248 ----- source3/nameelect.c | 845 ---------------- source3/nameelect.doc | 256 ----- source3/namelogon.c | 251 ----- source3/namelogon.doc | 36 - source3/namepacket.c | 777 --------------- source3/namepacket.doc | 133 --- source3/namequery.doc | 83 -- source3/nameresp.c | 318 ------ source3/nameresp.doc | 178 ---- source3/nameserv.c | 498 --------- source3/nameserv.doc | 159 --- source3/nameservreply.c | 685 ------------- source3/nameservreply.doc | 213 ---- source3/nameservresp.c | 851 ---------------- source3/nameservresp.doc | 191 ---- source3/namework.c | 724 -------------- source3/namework.doc | 363 ------- source3/nmbd/asyncdns.c | 64 +- source3/nmbd/nmbd.c | 811 ++++++++------- source3/nmbd/nmbd_become_dmb.c | 471 +++++++++ source3/nmbd/nmbd_become_lmb.c | 534 ++++++++++ source3/nmbd/nmbd_browserdb.c | 184 ++++ source3/nmbd/nmbd_browsesync.c | 458 +++++++++ source3/nmbd/nmbd_elections.c | 348 +++++++ source3/nmbd/nmbd_incomingdgrams.c | 625 ++++++++++++ source3/nmbd/nmbd_incomingrequests.c | 556 +++++++++++ source3/nmbd/nmbd_lmhosts.c | 168 ++++ source3/nmbd/nmbd_logonnames.c | 166 +++ source3/nmbd/nmbd_mynames.c | 165 +++ source3/nmbd/nmbd_namelistdb.c | 586 +++++++++++ source3/nmbd/nmbd_namequery.c | 234 +++++ source3/nmbd/nmbd_nameregister.c | 390 ++++++++ source3/nmbd/nmbd_namerelease.c | 238 +++++ source3/nmbd/nmbd_nodestatus.c | 99 ++ source3/nmbd/nmbd_packets.c | 1775 +++++++++++++++++++++++++++++++++ source3/nmbd/nmbd_processlogon.c | 250 +++++ source3/nmbd/nmbd_responserecordsdb.c | 238 +++++ source3/nmbd/nmbd_sendannounce.c | 477 +++++++++ source3/nmbd/nmbd_serverlistdb.c | 454 +++++++++ source3/nmbd/nmbd_subnetdb.c | 291 ++++++ source3/nmbd/nmbd_winsproxy.c | 195 ++++ source3/nmbd/nmbd_winsserver.c | 1565 +++++++++++++++++++++++++++++ source3/nmbd/nmbd_workgroupdb.c | 356 +++++++ source3/nmbsync.c | 117 --- source3/param/loadparm.c | 10 +- source3/utils/nmblookup.c | 38 +- 63 files changed, 12154 insertions(+), 10506 deletions(-) delete mode 100644 source3/nameannounce.c delete mode 100644 source3/nameannounce.doc delete mode 100644 source3/namebrowse.c delete mode 100644 source3/namebrowse.doc delete mode 100644 source3/namedbname.c delete mode 100644 source3/namedbname.doc delete mode 100644 source3/namedbresp.c delete mode 100644 source3/namedbresp.doc delete mode 100644 source3/namedbserver.c delete mode 100644 source3/namedbsubnet.c delete mode 100644 source3/namedbwork.c delete mode 100644 source3/nameelect.c delete mode 100644 source3/nameelect.doc delete mode 100644 source3/namelogon.c delete mode 100644 source3/namelogon.doc delete mode 100644 source3/namepacket.c delete mode 100644 source3/namepacket.doc delete mode 100644 source3/namequery.doc delete mode 100644 source3/nameresp.c delete mode 100644 source3/nameresp.doc delete mode 100644 source3/nameserv.c delete mode 100644 source3/nameserv.doc delete mode 100644 source3/nameservreply.c delete mode 100644 source3/nameservreply.doc delete mode 100644 source3/nameservresp.c delete mode 100644 source3/nameservresp.doc delete mode 100644 source3/namework.c delete mode 100644 source3/namework.doc create mode 100644 source3/nmbd/nmbd_become_dmb.c create mode 100644 source3/nmbd/nmbd_become_lmb.c create mode 100644 source3/nmbd/nmbd_browserdb.c create mode 100644 source3/nmbd/nmbd_browsesync.c create mode 100644 source3/nmbd/nmbd_elections.c create mode 100644 source3/nmbd/nmbd_incomingdgrams.c create mode 100644 source3/nmbd/nmbd_incomingrequests.c create mode 100644 source3/nmbd/nmbd_lmhosts.c create mode 100644 source3/nmbd/nmbd_logonnames.c create mode 100644 source3/nmbd/nmbd_mynames.c create mode 100644 source3/nmbd/nmbd_namelistdb.c create mode 100644 source3/nmbd/nmbd_namequery.c create mode 100644 source3/nmbd/nmbd_nameregister.c create mode 100644 source3/nmbd/nmbd_namerelease.c create mode 100644 source3/nmbd/nmbd_nodestatus.c create mode 100644 source3/nmbd/nmbd_packets.c create mode 100644 source3/nmbd/nmbd_processlogon.c create mode 100644 source3/nmbd/nmbd_responserecordsdb.c create mode 100644 source3/nmbd/nmbd_sendannounce.c create mode 100644 source3/nmbd/nmbd_serverlistdb.c create mode 100644 source3/nmbd/nmbd_subnetdb.c create mode 100644 source3/nmbd/nmbd_winsproxy.c create mode 100644 source3/nmbd/nmbd_winsserver.c create mode 100644 source3/nmbd/nmbd_workgroupdb.c delete mode 100644 source3/nmbsync.c (limited to 'source3') diff --git a/source3/client/clientutil.c b/source3/client/clientutil.c index 4064dbecd7..bf49c6b342 100644 --- a/source3/client/clientutil.c +++ b/source3/client/clientutil.c @@ -891,15 +891,18 @@ BOOL cli_open_sockets(int port ) { #ifdef USENMB /* Try and resolve the name with the netbios server */ - int bcast; + int bcast, count; + struct in_addr *ip_list; if ((bcast = open_socket_in(SOCK_DGRAM, 0, 3, interpret_addr(lp_socket_address()))) != -1) { set_socket_options(bcast, "SO_BROADCAST"); - if (name_query(bcast, host, name_type, True, True, *iface_bcast(dest_ip), - &dest_ip,0)) { - failed = False; + if ((ip_list = name_query(bcast, host, name_type, True, True, *iface_bcast(dest_ip), + &count,0)) { + dest_ip = ip_list[0]; + free(ip_list); + failed = False; } close (bcast); } diff --git a/source3/include/nameserv.h b/source3/include/nameserv.h index 2a7bb29070..4b7216fef6 100644 --- a/source3/include/nameserv.h +++ b/source3/include/nameserv.h @@ -1,3 +1,5 @@ +#ifndef _NAMESERV_H_ +#define _NAMESERV_H_ /* Unix SMB/Netbios implementation. Version 1.9. @@ -20,7 +22,7 @@ */ -#define GET_TTL(ttl) ((ttl)?MIN(ttl,lp_max_ttl()):lp_max_ttl()) +#define PERMANENT_TTL 0 /* NTAS uses 2, NT uses 1, WfWg uses 0 */ #define MAINTAIN_LIST 2 @@ -29,17 +31,44 @@ #define MAX_DGRAM_SIZE (576) /* tcp/ip datagram limit is 576 bytes */ #define MIN_DGRAM_SIZE 12 -#define NMB_QUERY 0x20 -#define NMB_STATUS 0x21 +/********************************************************* + Types of reply packet. +**********************************************************/ + +enum netbios_reply_type_code { NMB_QUERY, NMB_STATUS, NMB_REG, NMB_REG_REFRESH, + NMB_REL, NMB_WAIT_ACK, NMB_MULTIHOMED_REG, + WINS_REG, WINS_QUERY }; + +/* From rfc1002, 4.2.1.2 */ +/* Question types. */ +#define QUESTION_TYPE_NB_QUERY 0x20 +#define QUESTION_TYPE_NB_STATUS 0x21 + +/* Question class */ +#define QUESTION_CLASS_IN 0x1 + +/* Opcode definitions */ +#define NMB_NAME_QUERY_OPCODE 0x0 +#define NMB_NAME_REG_OPCODE 0x05 /* see rfc1002.txt 4.2.2,3,5,6,7,8 */ +#define NMB_NAME_RELEASE_OPCODE 0x06 /* see rfc1002.txt 4.2.9,10,11 */ +#define NMB_WACK_OPCODE 0x07 /* see rfc1002.txt 4.2.16 */ +/* Ambiguity in rfc1002 about which of these is correct. */ +/* WinNT uses 8 by default but can be made to use 9. */ +#define NMB_NAME_REFRESH_OPCODE_8 0x08 /* see rfc1002.txt 4.2.4 */ +#define NMB_NAME_REFRESH_OPCODE_9 0x09 /* see rfc1002.txt 4.2.4 */ +#define NMB_NAME_MULTIHOMED_REG_OPCODE 0x0F /* Invented by Microsoft. */ -#define NMB_REG 0x05 /* see rfc1002.txt 4.2.2,3,5,6,7,8 */ -#define NMB_REG_REFRESH 0x09 /* see rfc1002.txt 4.2.4 */ -#define NMB_REL 0x06 /* see rfc1002.txt 4.2.9,10,11 */ -#define NMB_WAIT_ACK 0x07 /* see rfc1002.txt 4.2.16 */ /* XXXX what about all the other types?? 0x1, 0x2, 0x3, 0x4, 0x8? */ -#define FIND_ANY_NAME 0 -#define FIND_SELF_NAME 1 +/* Resource record types. rfc1002 4.2.1.3 */ +#define RR_TYPE_A 0x1 +#define RR_TYPE_NS 0x2 +#define RR_TYPE_NULL 0xA +#define RR_TYPE_NB 0x20 +#define RR_TYPE_NBSTAT 0x21 + +/* Resource record class. */ +#define RR_CLASS_IN 0x1 /* NetBIOS flags */ #define NB_GROUP 0x80 @@ -47,206 +76,299 @@ #define NB_ACTIVE 0x04 #define NB_CONFL 0x08 #define NB_DEREG 0x10 -#define NB_BFLAG 0x00 /* broadcast node type */ -#define NB_PFLAG 0x20 /* point-to-point node type */ -#define NB_MFLAG 0x40 /* mixed bcast & p-p node type */ -#define NB_HFLAG 0x60 /* microsoft 'hybrid' node type */ -#define NB_FLGMSK 0x60 +#define NB_BFLAG 0x00 /* Broadcast node type. */ +#define NB_PFLAG 0x20 /* Point-to-point node type. */ +#define NB_MFLAG 0x40 /* Mixed bcast & p-p node type. */ +#define NB_HFLAG 0x60 /* Microsoft 'hybrid' node type. */ +#define NB_NODETYPEMASK 0x60 +/* Mask applied to outgoing NetBIOS flags. */ +#define NB_FLGMSK 0xE0 + +/* NetBIOS flag identifier. */ +#define NAME_GROUP(p) ((p)->nb_flags & NB_GROUP) +#define NAME_BFLAG(p) (((p)->nb_flags & NB_NODETYPEMASK) == NB_BFLAG) +#define NAME_PFLAG(p) (((p)->nb_flags & NB_NODETYPEMASK) == NB_PFLAG) +#define NAME_MFLAG(p) (((p)->nb_flags & NB_NODETYPEMASK) == NB_MFLAG) +#define NAME_HFLAG(p) (((p)->nb_flags & NB_NODETYPEMASK) == NB_HFLAG) + +/* Samba name state for a name in a namelist. */ +#define NAME_IS_ACTIVE(p) ((p)->nb_flags & NB_ACTIVE) +#define NAME_IN_CONFLICT(p) ((p)->nb_flags & NB_CONFL) +#define NAME_IS_DEREGISTERING(p) ((p)->nb_flags & NB_DEREG) + +/* Error codes for NetBIOS requests. */ +#define FMT_ERR 0x1 /* Packet format error. */ +#define SRV_ERR 0x2 /* Internal server error. */ +#define NAM_ERR 0x3 /* Name does not exist. */ +#define IMP_ERR 0x4 /* Request not implemented. */ +#define RFS_ERR 0x5 /* Request refused. */ +#define ACT_ERR 0x6 /* Active error - name owned by another host. */ +#define CFT_ERR 0x7 /* Name in conflict error. */ #define REFRESH_TIME (15*60) #define NAME_POLL_REFRESH_TIME (5*60) #define NAME_POLL_INTERVAL 15 -/* NetBIOS flag identifier */ -#define NAME_PERMANENT(p) ((p) & NB_PERM) -#define NAME_ACTIVE(p) ((p) & NB_ACTIVE) -#define NAME_CONFLICT(p) ((p) & NB_CONFL) -#define NAME_DEREG(p) ((p) & NB_DEREG) -#define NAME_GROUP(p) ((p) & NB_GROUP) - -#define NAME_BFLAG(p) (((p) & NB_FLGMSK) == NB_BFLAG) -#define NAME_PFLAG(p) (((p) & NB_FLGMSK) == NB_PFLAG) -#define NAME_MFLAG(p) (((p) & NB_FLGMSK) == NB_MFLAG) -#define NAME_HFLAG(p) (((p) & NB_FLGMSK) == NB_HFLAG) - -/* server type identifiers */ -#define AM_MASTER(work) (work->ServerType & SV_TYPE_MASTER_BROWSER) -#define AM_BACKUP(work) (work->ServerType & SV_TYPE_BACKUP_BROWSER) -#define AM_DOMMST(work) (work->ServerType & SV_TYPE_DOMAIN_MASTER) -#define AM_DOMMEM(work) (work->ServerType & SV_TYPE_DOMAIN_MEMBER) - -/* microsoft browser NetBIOS name */ +/* Workgroup state identifiers. */ +#define AM_POTENTIAL_MASTER_BROWSER(work) ((work)->mst_state == MST_POTENTIAL) +#define AM_LOCAL_MASTER_BROWSER(work) ((work)->mst_state == MST_BROWSER) +#define AM_DOMAIN_MASTER_BROWSER(work) ((work)->dom_state == DOMAIN_MST) +#define AM_DOMAIN_MEMBER(work) ((work)->log_state == LOGON_SRV) + +/* Microsoft browser NetBIOS name. */ #define MSBROWSE "\001\002__MSBROWSE__\002" -/* mail slots */ +/* Mail slots. */ #define BROWSE_MAILSLOT "\\MAILSLOT\\BROWSE" #define NET_LOGON_MAILSLOT "\\MAILSLOT\\NET\\NETLOGON" #define NT_LOGON_MAILSLOT "\\MAILSLOT\\NET\\NTLOGON" -enum name_source {STATUS_QUERY, LMHOSTS, REGISTER, SELF, DNS, DNSFAIL}; +/* Samba definitions for find_name_on_subnet(). */ +#define FIND_ANY_NAME 0 +#define FIND_SELF_NAME 1 + +/* + * The different name types that can be in namelists. + * + * SELF_NAME should only be on the broadcast and unicast subnets. + * LMHOSTS_NAME should only be in the remote_broadcast_subnet. + * REGISTER_NAME, DNS_NAME, DNSFAIL_NAME should only be in the wins_server_subnet. + * WINS_PROXY_NAME should only be on the broadcast subnets. + * PERMANENT_NAME can be on all subnets except remote_broadcast_subnet. + * + */ + +enum name_source {LMHOSTS_NAME, REGISTER_NAME, SELF_NAME, DNS_NAME, + DNSFAIL_NAME, PERMANENT_NAME, WINS_PROXY_NAME}; enum node_type {B_NODE=0, P_NODE=1, M_NODE=2, NBDD_NODE=3}; enum packet_type {NMB_PACKET, DGRAM_PACKET}; enum master_state { - MST_POTENTIAL, - MST_BACK, - MST_MSB, - MST_BROWSER + MST_NONE, + MST_POTENTIAL, + MST_BACKUP, + MST_MSB, + MST_BROWSER, + MST_UNBECOMING_MASTER }; enum domain_state { - DOMAIN_NONE, - DOMAIN_WAIT, - DOMAIN_MST + DOMAIN_NONE, + DOMAIN_WAIT, + DOMAIN_MST }; enum logon_state { - LOGON_NONE, - LOGON_WAIT, - LOGON_SRV -}; - -enum state_type -{ - NAME_STATUS_DOM_SRV_CHK, - NAME_STATUS_SRV_CHK, - NAME_REGISTER_CHALLENGE, - NAME_REGISTER, - NAME_RELEASE, - NAME_QUERY_CONFIRM, - NAME_QUERY_SYNC_LOCAL, - NAME_QUERY_SYNC_REMOTE, - NAME_QUERY_DOM_SRV_CHK, - NAME_QUERY_SRV_CHK, - NAME_QUERY_FIND_MST, - NAME_QUERY_MST_CHK, - NAME_QUERY_DOMAIN + LOGON_NONE, + LOGON_WAIT, + LOGON_SRV }; -/* a netbios name structure */ +/* A netbios name structure. */ struct nmb_name { char name[17]; char scope[64]; - int name_type; + unsigned int name_type; }; -/* a netbios flags + ip address structure */ -/* this is used for multi-homed systems and for internet group names */ -struct nmb_ip -{ - struct in_addr ip; /* ip address of host that owns this name */ - uint16 nb_flags; /* netbios flags */ -}; - -/* this is the structure used for the local netbios name list */ +/* This is the structure used for the local netbios name list. */ struct name_record { struct name_record *next; struct name_record *prev; - struct nmb_name name; /* the netbios name */ - struct nmb_ip *ip_flgs; /* the ip + flags */ - int num_ips; /* number of ip+flags entries */ + struct subnet_record *subnet; - enum name_source source; /* where the name came from */ + struct nmb_name name; /* The netbios name. */ + uint16 nb_flags; /* Netbios flags. */ + int num_ips; /* Number of ip entries. */ + struct in_addr *ip; /* The ip list for this name. */ - time_t death_time; /* time record must be removed (do not remove if 0) */ - time_t refresh_time; /* time record should be refreshed */ + enum name_source source; /* Where the name came from. */ + + time_t death_time; /* The time the record must be removed (do not remove if 0). */ + time_t refresh_time; /* The time the record should be refreshed. */ }; struct subnet_record; -/* browse and backup server cache for synchronising browse list */ +/* Browser cache for synchronising browse lists. */ struct browse_cache_record { - struct browse_cache_record *next; - struct browse_cache_record *prev; - - pstring name; - int type; - pstring group; - struct in_addr ip; - time_t sync_time; - BOOL synced; - BOOL local; - struct subnet_record *subnet; + struct browse_cache_record *next; + struct browse_cache_record *prev; + + pstring lmb_name; + pstring work_group; + struct in_addr ip; + time_t sync_time; + time_t death_time; /* The time the record must be removed. */ }; -/* this is used to hold the list of servers in my domain, and is */ -/* contained within lists of domains */ +/* This is used to hold the list of servers in my domain, and is + contained within lists of domains. */ + struct server_record { struct server_record *next; struct server_record *prev; + struct subnet_record *subnet; + struct server_info_struct serv; time_t death_time; }; -/* a workgroup structure. it contains a list of servers */ +/* A workgroup structure. It contains a list of servers. */ struct work_record { struct work_record *next; struct work_record *prev; + struct subnet_record *subnet; + struct server_record *serverlist; - /* stage of development from non-local-master up to local-master browser */ + /* Stage of development from non-local-master up to local-master browser. */ enum master_state mst_state; - /* stage of development from non-domain-master to domain master browser */ + /* Stage of development from non-domain-master to domain-master browser. */ enum domain_state dom_state; - /* stage of development from non-logon-server to logon server */ + /* Stage of development from non-logon-server to logon server. */ enum logon_state log_state; - /* work group info */ + /* Work group info. */ fstring work_group; - int token; /* used when communicating with backup browsers */ - int ServerType; + int token; /* Used when communicating with backup browsers. */ + fstring local_master_browser_name; /* Current local master browser. */ - /* announce info */ + /* Announce info. */ time_t lastannounce_time; int announce_interval; BOOL needannounce; + /* Timeout time for this workgroup. 0 means permanent. */ + time_t death_time; - /* election info */ + /* Election info */ BOOL RunningElection; BOOL needelection; int ElectionCount; uint32 ElectionCriterion; + + /* Domain master browser info. Used for efficient syncs. */ + struct nmb_name dmb_name; + struct in_addr dmb_addr; }; -/* initiated name queries recorded in this list to track any responses... */ -/* sadly, we need to group everything together. i suppose that if this - gets unwieldy, then a union ought to be considered. oh for c++... */ +/* typedefs needed to define copy & free functions for userdata. */ +struct userdata_struct; + +typedef struct userdata_struct * (*userdata_copy_fn)(struct userdata_struct *); +typedef void (*userdata_free_fn)(struct userdata_struct *); + +/* Structure to define any userdata passed around. */ + +struct userdata_struct { + userdata_copy_fn copy_fn; + userdata_free_fn free_fn; + unsigned int userdata_len; + char data[1]; +}; + +struct response_record; +struct packet_struct; +struct res_rec; + +/* typedef to define the function called when this response packet comes in. */ +typedef void (*response_function)(struct subnet_record *, struct response_record *, + struct packet_struct *); + +/* typedef to define the function called when this response record times out. */ +typedef void (*timeout_response_function)(struct subnet_record *, + struct response_record *); + +/* typedef to define the function called when the request that caused this + response record to be created is successful. */ +typedef void (*success_function)(struct subnet_record *, struct userdata_struct *, ...); + +/* typedef to define the function called when the request that caused this + response record to be created is unsuccessful. */ +typedef void (*fail_function)(struct subnet_record *, struct response_record *, ...); + +/* List of typedefs for success and fail functions of the different query + types. Used to catch any compile time prototype errors. */ + +typedef void (*register_name_success_function)( struct subnet_record *, + struct userdata_struct *, + struct nmb_name *, + uint16, + int, + struct in_addr); +typedef void (*register_name_fail_function)( struct subnet_record *, + struct response_record *, + struct nmb_name *); + +typedef void (*release_name_success_function)( struct subnet_record *, + struct userdata_struct *, + struct nmb_name *, + struct in_addr); +typedef void (*release_name_fail_function)( struct subnet_record *, + struct response_record *, + struct nmb_name *); + +typedef void (*refresh_name_success_function)( struct subnet_record *, + struct userdata_struct *, + struct nmb_name *, + uint16, + int, + struct in_addr); +typedef void (*refresh_name_fail_function)( struct subnet_record *, + struct response_record *, + struct nmb_name *); + +typedef void (*query_name_success_function)( struct subnet_record *, + struct userdata_struct *, + struct nmb_name *, + struct in_addr, + struct res_rec *answers); + +typedef void (*query_name_fail_function)( struct subnet_record *, + struct response_record *, + struct nmb_name *, + int); + +typedef void (*node_status_success_function)( struct subnet_record *, + struct userdata_struct *, + struct res_rec *, + struct in_addr); +typedef void (*node_status_fail_function)( struct subnet_record *, + struct response_record *); + +/* Initiated name queries are recorded in this list to track any responses. */ + struct response_record { struct response_record *next; struct response_record *prev; uint16 response_id; - enum state_type state; - int fd; - int quest_type; - struct nmb_name name; - int nb_flags; - time_t ttl; + /* Callbacks for packets received or not. */ + response_function resp_fn; + timeout_response_function timeout_fn; - int server_type; - fstring my_name; - fstring my_comment; + /* Callbacks for the request succeeding or not. */ + success_function success_fn; + fail_function fail_fn; + + struct packet_struct *packet; - BOOL bcast; - BOOL recurse; - struct in_addr send_ip; - struct in_addr reply_to_ip; - int reply_id; + struct userdata_struct *userdata; int num_msgs; @@ -255,37 +377,35 @@ struct response_record int repeat_count; }; -/* a subnet structure. it contains a list of workgroups and netbios names*/ - -/* note that a subnet of 255.255.255.255 contains all the WINS netbios names. - all communication from such nodes are on a non-broadcast basis: they - are point-to-point (P nodes) or mixed point-to-point and broadcast - (M nodes). M nodes use point-to-point as a preference, and will use - broadcasting for certain activities, or will resort to broadcasting as a - last resort, if the WINS server fails (users of wfwg will notice that their - machine often freezes for 30 seconds at a time intermittently, if the WINS - server is down). +/* A subnet structure. It contains a list of workgroups and netbios names. */ +/* B nodes will have their own, totally separate subnet record, with their - own netbios name set. these do NOT interact with other subnet records' - netbios names, INCLUDING the WINS one (with an ip "address", so called, - of 255.255.255.255) - - there is a separate response list for each subnet record. in the case of - the 255.255.255.255 subnet record (WINS), the WINS server will be able to - use this to poll (infrequently!) each of its entries, to ensure that the - names are still in use. - XXXX this polling is a planned feature for a really over-cautious WINS server + own netbios name set. These do NOT interact with other subnet records' + netbios names. */ +enum subnet_type { + NORMAL_SUBNET = 0, /* Subnet listed in interfaces list. */ + UNICAST_SUBNET = 1, /* Subnet for unicast packets. */ + REMOTE_BROADCAST_SUBNET = 2, /* Subnet for remote broadcasts. */ + WINS_SERVER_SUBNET = 3 /* Only created if we are a WINS server. */ +}; + struct subnet_record { struct subnet_record *next; struct subnet_record *prev; - struct work_record *workgrouplist; /* list of workgroups */ - struct name_record *namelist; /* list of netbios names */ - struct response_record *responselist; /* list of responses expected */ + char *subnet_name; /* For Debug identification. */ + enum subnet_type type; /* To catagorize the subnet. */ + + struct work_record *workgrouplist; /* List of workgroups. */ + struct name_record *namelist; /* List of netbios names. */ + struct response_record *responselist; /* List of responses expected. */ + + BOOL namelist_changed; + BOOL work_changed; struct in_addr bcast_ip; struct in_addr mask_ip; @@ -294,7 +414,7 @@ struct subnet_record int dgram_sock; /* socket to listen for unicast 138. */ }; -/* a resource record */ +/* A resource record. */ struct res_rec { struct nmb_name rr_name; int rr_type; @@ -304,7 +424,7 @@ struct res_rec { char rdata[MAX_DGRAM_SIZE]; }; -/* define a nmb packet. */ +/* An nmb packet. */ struct nmb_packet { struct { @@ -337,7 +457,8 @@ struct nmb_packet }; -/* a datagram - this normally contains SMB data in the data[] array */ +/* A datagram - this normally contains SMB data in the data[] array. */ + struct dgram_packet { struct { int msg_type; @@ -358,32 +479,35 @@ struct dgram_packet { char data[MAX_DGRAM_SIZE]; }; -/* define a structure used to queue packets. this will be a linked - list of nmb packets */ +/* Define a structure used to queue packets. This will be a linked + list of nmb packets. */ + struct packet_struct { - struct packet_struct *next; - struct packet_struct *prev; - BOOL locked; - struct in_addr ip; - int port; - int fd; - time_t timestamp; - enum packet_type packet_type; - union { - struct nmb_packet nmb; - struct dgram_packet dgram; - } packet; + struct packet_struct *next; + struct packet_struct *prev; + BOOL locked; + struct in_addr ip; + int port; + int fd; + time_t timestamp; + enum packet_type packet_type; + union { + struct nmb_packet nmb; + struct dgram_packet dgram; + } packet; }; /* NETLOGON opcodes */ -#define QUERYFORPDC 7 /* Query for PDC */ -#define QUERYFORPDC_R 12 /* Response to Query for PDC */ + +#define QUERYFORPDC 7 /* Query for PDC. */ +#define QUERYFORPDC_R 12 /* Response to Query for PDC. */ #define SAMLOGON 18 #define SAMLOGON_R 19 -/* ids for netbios packet types */ +/* Ids for netbios packet types. */ + #define ANN_HostAnnouncement 1 #define ANN_AnnouncementRequest 2 #define ANN_Election 8 @@ -396,42 +520,48 @@ struct packet_struct #define ANN_LocalMasterAnnouncement 15 -/* broadcast packet announcement intervals, in minutes */ +/* Broadcast packet announcement intervals, in minutes. */ -/* attempt to add domain logon and domain master names */ +/* Attempt to add domain logon and domain master names. */ #define CHECK_TIME_ADD_DOM_NAMES 5 -/* search for master browsers of workgroups samba knows about, - except default */ +/* Search for master browsers of workgroups samba knows about, + except default. */ #define CHECK_TIME_MST_BROWSE 5 -/* request backup browser announcements from other servers */ +/* Request backup browser announcements from other servers. */ #define CHECK_TIME_ANNOUNCE_BACKUP 15 -/* request host announcements from other servers: min and max of interval */ +/* Request host announcements from other servers: min and max of interval. */ #define CHECK_TIME_MIN_HOST_ANNCE 3 #define CHECK_TIME_MAX_HOST_ANNCE 12 -/* announce as master to WINS server and any Primary Domain Controllers */ +/* Announce as master to WINS server and any Primary Domain Controllers. */ #define CHECK_TIME_MST_ANNOUNCE 15 -/* do all remote announcements this often */ +/* Time between syncs from domain master browser to local master browsers. */ +#define CHECK_TIME_DMB_TO_LMB_SYNC 15 + +/* Do all remote announcements this often. */ #define REMOTE_ANNOUNCE_INTERVAL 180 -/* Types of machine we can announce as */ +/* Types of machine we can announce as. */ #define ANNOUNCE_AS_NT 1 #define ANNOUNCE_AS_WIN95 2 #define ANNOUNCE_AS_WFW 3 /* Macro's to enumerate subnets either with or without - the WINS subnet. */ + the UNICAST subnet. */ extern struct subnet_record *subnetlist; -extern struct subnet_record *wins_client_subnet; +extern struct subnet_record *unicast_subnet; +extern struct subnet_record *wins_server_subnet; +extern struct subnet_record *remote_broadcast_subnet; #define FIRST_SUBNET subnetlist -#define NEXT_SUBNET_EXCLUDING_WINS(x) ((x)->next) -#define NEXT_SUBNET_INCLUDING_WINS(x) ( ((x) == wins_client_subnet) ? NULL : \ - (((x)->next == NULL) ? wins_client_subnet : \ - (x)->next)) +#define NEXT_SUBNET_EXCLUDING_UNICAST(x) ((x)->next) +#define NEXT_SUBNET_INCLUDING_UNICAST(x) (get_next_subnet_maybe_unicast((x))) +/* To be removed. */ +enum state_type { TEST }; +#endif /* _NAMESERV_H_ */ diff --git a/source3/include/proto.h b/source3/include/proto.h index e6a0b1efd7..b249c9cb20 100644 --- a/source3/include/proto.h +++ b/source3/include/proto.h @@ -189,6 +189,8 @@ BOOL ismyip(struct in_addr ip); BOOL ismybcast(struct in_addr bcast); BOOL is_local_net(struct in_addr from); int iface_count(void); +BOOL we_are_multihomed(); +struct interface *get_interface(int n); struct in_addr *iface_n_ip(int n); struct in_addr *iface_bcast(struct in_addr ip); struct in_addr *iface_nmask(struct in_addr ip); @@ -276,6 +278,8 @@ BOOL lp_time_server(void); BOOL lp_bind_interfaces_only(void); int lp_os_level(void); int lp_max_ttl(void); +int lp_max_wins_ttl(void); +int lp_min_wins_ttl(void); int lp_max_log_size(void); int lp_mangledstack(void); int lp_maxxmit(void); @@ -451,225 +455,329 @@ int reply_sendstrt(char *inbuf,char *outbuf); int reply_sendtxt(char *inbuf,char *outbuf); int reply_sendend(char *inbuf,char *outbuf); -/*The following definitions come from nameannounce.c */ +/*The following definitions come from namequery.c */ -void announce_request(struct work_record *work, struct in_addr ip); -void do_announce_request(char *info, char *to_name, int announce_type, - int from, - int to, struct in_addr dest_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); -void announce_my_servers_removed(void); -void announce_server(struct subnet_record *d, struct work_record *work, - char *name, char *comment, time_t ttl, int server_type); -void announce_host(time_t t); -void reset_announce_timer(); -void announce_master(time_t t); -void announce_remote(time_t t); -void browse_sync_remote(time_t t); +BOOL name_status(int fd,char *name,int name_type,BOOL recurse, + struct in_addr to_ip,char *master,char *rname, + void (*fn)()); +struct in_addr *name_query(int fd,char *name,int name_type, + BOOL bcast,BOOL recurse, + struct in_addr to_ip, int *count, void (*fn)()); -/*The following definitions come from namebrowse.c */ +/*The following definitions come from nmbd.c */ -void expire_browse_cache(time_t t); -struct browse_cache_record *add_browser_entry(char *name, int type, char *wg, - time_t ttl, struct subnet_record *d, - struct in_addr ip, BOOL local); -void do_browser_lists(time_t t); +BOOL reload_services(BOOL test); +int main(int argc,char *argv[]); -/*The following definitions come from namedbname.c */ +/*The following definitions come from nmbd_become_dmb.c */ -void set_samba_nb_type(void); -BOOL name_equal(struct nmb_name *n1,struct nmb_name *n2); -BOOL ms_browser_name(char *name, int type); -void remove_name(struct subnet_record *d, struct name_record *n); -struct name_record *find_name_on_subnet(struct subnet_record *d, - struct nmb_name *name, BOOL self_only); -void dump_names(void); -void load_netbios_names(void); -void remove_netbios_name(struct subnet_record *d, - char *name,int type, enum name_source source); -struct name_record *add_netbios_entry(struct subnet_record *d, - char *name, int type, int nb_flags, int ttl, - enum name_source source, struct in_addr ip, BOOL new_only); -void expire_names(time_t t); +void unbecome_domain_master(char *workgroup_name); +void add_domain_names(time_t t); -/*The following definitions come from namedbresp.c */ - -void add_response_record(struct subnet_record *d, - struct response_record *n); -void remove_response_record(struct subnet_record *d, - struct response_record *n); -struct response_record *make_response_queue_record(enum state_type state, - int id,uint16 fd, - int quest_type, char *name,int type, int nb_flags, time_t ttl, - int server_type, char *my_name, char *my_comment, - BOOL bcast,BOOL recurse, - struct in_addr send_ip, struct in_addr reply_to_ip, - int reply_id); -struct response_record *find_response_record(struct subnet_record **d, - uint16 id); +/*The following definitions come from nmbd_become_lmb.c */ -/*The following definitions come from namedbserver.c */ - -void remove_old_servers(struct work_record *work, time_t t, - BOOL remove_all); -struct server_record *find_server(struct work_record *work, char *name); -struct server_record *add_server_entry(struct subnet_record *d, - struct work_record *work, - char *name,int servertype, - int ttl,char *comment, - BOOL replace); -void expire_servers(time_t t); - -/*The following definitions come from namedbsubnet.c */ - -struct subnet_record *find_subnet(struct in_addr ip); -struct subnet_record *find_subnet_all(struct in_addr ip); -void add_workgroup_to_subnet( struct subnet_record *d, char *group); -void add_my_subnets(char *group); -void write_browse_list(time_t t); - -/*The following definitions come from namedbwork.c */ - -struct work_record *remove_workgroup(struct subnet_record *d, - struct work_record *work, - BOOL remove_all_servers); -struct work_record *find_workgroupstruct(struct subnet_record *d, - fstring name, BOOL add); -void dump_workgroups(void); +void unbecome_local_master_success(struct subnet_record *subrec, + struct userdata_struct *userdata, + struct nmb_name *released_name, + struct in_addr released_ip); +void unbecome_local_master_fail(struct subnet_record *subrec, struct response_record *rrec, + struct nmb_name *fail_name); +void release_1d_name( struct subnet_record *subrec, char *workgroup_name); +void unbecome_local_master_browser(struct subnet_record *subrec, struct work_record *work); +void become_local_master_browser(struct subnet_record *subrec, struct work_record *work); + +/*The following definitions come from nmbd_browserdb.c */ + +void remove_lmb_browser_entry(struct browse_cache_record *browc); +void update_browser_death_time(struct browse_cache_record *browc); +struct browse_cache_record *create_browser_in_lmb_cache(char *work_name, char *browser_name, + struct in_addr ip); +struct browse_cache_record *find_browser_in_lmb_cache( char *browser_name ); +void expire_lmb_browsers(time_t t); +void remove_workgroup_lmb_browsers(char *work_group); + +/*The following definitions come from nmbd_browsesync.c */ + +void dmb_expire_and_sync_browser_lists(time_t t); +void announce_and_sync_with_domain_master_browser( struct subnet_record *subrec, + struct work_record *work); + +/*The following definitions come from nmbd_elections.c */ -/*The following definitions come from nameelect.c */ - -void check_master_browser(time_t t); -void browser_gone(char *work_name, struct in_addr ip); -void send_election(struct subnet_record *d, char *group,uint32 criterion, - int timeup,char *name); -void name_unregister_work(struct subnet_record *d, char *name, int name_type); -void name_register_work(struct subnet_record *d, char *name, int name_type, - int nb_flags, time_t ttl, struct in_addr ip, BOOL bcast); -void become_local_master(struct subnet_record *d, struct work_record *work); -void become_domain_master(struct subnet_record *d, struct work_record *work); -void become_logon_server(struct subnet_record *d, struct work_record *work); -void unbecome_local_master(struct subnet_record *d, struct work_record *work, - int remove_type); -void unbecome_domain_master(struct subnet_record *d, struct work_record *work, - int remove_type); -void unbecome_logon_server(struct subnet_record *d, struct work_record *work, - int remove_type); +void check_master_browser_exists(time_t t); void run_elections(time_t t); -void process_election(struct packet_struct *p,char *buf); +void process_election(struct subnet_record *subrec, struct packet_struct *p, char *buf); BOOL check_elections(void); -/*The following definitions come from namelogon.c */ +/*The following definitions come from nmbd_incomingdgrams.c */ -void process_logon_packet(struct packet_struct *p,char *buf,int len, - char *mailslot); - -/*The following definitions come from namepacket.c */ - -void debug_browse_data(char *outbuf, int len); -void initiate_netbios_packet(uint16 *id, - int fd,int quest_type,char *name,int name_type, - int nb_flags,BOOL bcast,BOOL recurse, - struct in_addr to_ip); -void reply_netbios_packet(struct packet_struct *p1,int trn_id, - int rcode, int rcv_code, int opcode, - BOOL recursion_available, - BOOL recursion_desired, - struct nmb_name *rr_name,int rr_type,int rr_class,int ttl, - char *data,int len); -void queue_packet(struct packet_struct *packet); -void run_packet_queue(); -BOOL listen_for_packets(BOOL run_election); -BOOL send_mailslot_reply(BOOL unique, char *mailslot,int fd,char *buf,int len,char *srcname, - char *dstname,int src_type,int dest_type, - struct in_addr dest_ip,struct in_addr src_ip); +void tell_become_backup(void); +void process_host_announce(struct subnet_record *subrec, struct packet_struct *p, char *buf); +void process_workgroup_announce(struct subnet_record *subrec, struct packet_struct *p, char *buf); +void process_local_master_announce(struct subnet_record *subrec, struct packet_struct *p, char *buf); +void process_master_browser_announce(struct subnet_record *subrec, + struct packet_struct *p,char *buf); +void process_get_backup_list_request(struct subnet_record *subrec, + struct packet_struct *p,char *buf); +void process_reset_browser(struct subnet_record *subrec, + struct packet_struct *p,char *buf); +void process_announce_request(struct subnet_record *subrec, struct packet_struct *p, char *buf); -/*The following definitions come from namequery.c */ +/*The following definitions come from nmbd_incomingrequests.c */ -BOOL name_status(int fd,char *name,int name_type,BOOL recurse, - struct in_addr to_ip,char *master,char *rname, - void (*fn)()); -BOOL name_query(int fd,char *name,int name_type, - BOOL bcast,BOOL recurse, - struct in_addr to_ip, struct in_addr *ip,void (*fn)()); - -/*The following definitions come from nameresp.c */ - -void expire_netbios_response_entries(time_t t); -struct response_record *queue_netbios_pkt_wins( - int fd,int quest_type,enum state_type state, - char *name,int name_type,int nb_flags, time_t ttl, - int server_type, char *my_name, char *my_comment, - struct in_addr send_ip, struct in_addr reply_to_ip); -struct response_record *queue_netbios_packet(struct subnet_record *d, - int fd,int quest_type,enum state_type state,char *name, - int name_type,int nb_flags, time_t ttl, - int server_type, char *my_name, char *my_comment, - BOOL bcast,BOOL recurse, - struct in_addr send_ip, struct in_addr reply_to_ip, - int reply_id); - -/*The following definitions come from nameserv.c */ - -void remove_name_entry(struct subnet_record *d, char *name,int type); -void add_my_name_entry(struct subnet_record *d,char *name,int type,int nb_flags); -void add_domain_logon_names(void); -void add_domain_master_bcast(void); -void add_domain_master_wins(void); -void add_domain_names(time_t t); -void add_my_names(void); -void remove_my_names(); -void refresh_my_names(time_t t); -void query_refresh_names(time_t t); +void process_name_release_request(struct subnet_record *subrec, + struct packet_struct *p); +void process_name_refresh_request(struct subnet_record *subrec, + struct packet_struct *p); +void process_name_registration_request(struct subnet_record *subrec, + struct packet_struct *p); +void process_node_status_request(struct subnet_record *subrec, struct packet_struct *p); +void process_name_query_request(struct subnet_record *subrec, struct packet_struct *p); -/*The following definitions come from nameservreply.c */ +/*The following definitions come from nmbd_lmhosts.c */ -void add_name_respond(struct subnet_record *d, int fd, struct in_addr from_ip, - uint16 response_id, - struct nmb_name *name, - int nb_flags, int ttl, struct in_addr register_ip, - BOOL new_owner, struct in_addr reply_to_ip); -void reply_name_release(struct packet_struct *p); -void reply_name_reg(struct packet_struct *p); -void reply_name_status(struct packet_struct *p); -void reply_name_query(struct packet_struct *p); +void load_lmhosts_file(char *fname); +BOOL find_name_in_lmhosts(struct nmb_name *nmbname, struct name_record **namerecp); -/*The following definitions come from nameservresp.c */ +/*The following definitions come from nmbd_logonnames.c */ -void debug_state_type(int state); -void response_netbios_packet(struct packet_struct *p); +void add_logon_names(void); -/*The following definitions come from namework.c */ +/*The following definitions come from nmbd_mynames.c */ -void reset_server(char *name, int state, struct in_addr ip); -void tell_become_backup(void); -BOOL same_context(struct dgram_packet *dgram); -void process_browse_packet(struct packet_struct *p,char *buf,int len); +BOOL register_my_workgroup_and_names(); +void release_my_names(); +void refresh_my_names(time_t t); -/*The following definitions come from nmbd.c */ +/*The following definitions come from nmbd_namelistdb.c */ -BOOL reload_services(BOOL test); +void set_samba_nb_type(void); +BOOL ms_browser_name(char *name, int type); +void remove_name_from_namelist(struct subnet_record *subrec, + struct name_record *namerec); +struct name_record *find_name_on_subnet(struct subnet_record *subrec, + struct nmb_name *nmbname, BOOL self_only); +struct name_record *find_name_for_remote_broadcast_subnet( struct nmb_name *nmbname, + BOOL self_only); +void update_name_ttl(struct name_record *namerec, int ttl); +struct name_record *add_name_to_subnet(struct subnet_record *subrec, + char *name, int type, uint16 nb_flags, int ttl, + enum name_source source, int num_ips, struct in_addr *iplist); +void standard_success_register(struct subnet_record *subrec, + struct userdata_struct *userdata, + struct nmb_name *nmbname, uint16 nb_flags, int ttl, + struct in_addr registered_ip); +void standard_fail_register(struct subnet_record *subrec, + struct response_record *rrec, struct nmb_name *nmbname); +BOOL find_ip_in_name_record(struct name_record *namerec, struct in_addr ip); +void add_ip_to_name_record(struct name_record *namerec, struct in_addr new_ip); +void remove_ip_from_name_record( struct name_record *namerec, struct in_addr remove_ip); +void standard_success_release(struct subnet_record *subrec, + struct userdata_struct *userdata, + struct nmb_name *nmbname, struct in_addr released_ip); +void expire_names_on_subnet(struct subnet_record *subrec, time_t t); +void expire_names(time_t t); +void add_samba_names_to_subnet(struct subnet_record *subrec); +void dump_all_namelists(); + +/*The following definitions come from nmbd_namequery.c */ + +BOOL query_name(struct subnet_record *subrec, char *name, int type, + query_name_success_function success_fn, + query_name_fail_function fail_fn, + struct userdata_struct *userdata); + +/*The following definitions come from nmbd_nameregister.c */ + +BOOL register_name(struct subnet_record *subrec, + char *name, int type, uint16 nb_flags, + register_name_success_function success_fn, + register_name_fail_function fail_fn, + struct userdata_struct *userdata); +BOOL refresh_name(struct subnet_record *subrec, struct name_record *namerec, + refresh_name_success_function success_fn, + refresh_name_fail_function fail_fn, + struct userdata_struct *userdata); + +/*The following definitions come from nmbd_namerelease.c */ + +BOOL release_name(struct subnet_record *subrec, struct name_record *namerec, + release_name_success_function success_fn, + release_name_fail_function fail_fn, + struct userdata_struct *userdata); + +/*The following definitions come from nmbd_nodestatus.c */ + +BOOL node_status(struct subnet_record *subrec, struct nmb_name *nmbname, + struct in_addr send_ip, node_status_success_function success_fn, + node_status_fail_function fail_fn, struct userdata_struct *userdata); + +/*The following definitions come from nmbd_packets.c */ + +uint16 get_nb_flags(char *buf); +void set_nb_flags(char *buf, uint16 nb_flags); +struct response_record *queue_register_name( struct subnet_record *subrec, + response_function resp_fn, + timeout_response_function timeout_fn, + register_name_success_function success_fn, + register_name_fail_function fail_fn, + struct userdata_struct *userdata, + struct nmb_name *nmbname, + uint16 nb_flags); +struct response_record *queue_register_multihomed_name( struct subnet_record *subrec, + response_function resp_fn, + timeout_response_function timeout_fn, + register_name_success_function success_fn, + register_name_fail_function fail_fn, + struct userdata_struct *userdata, + struct nmb_name *nmbname, + uint16 nb_flags, + struct in_addr register_ip); +struct response_record *queue_release_name( struct subnet_record *subrec, + response_function resp_fn, + timeout_response_function timeout_fn, + release_name_success_function success_fn, + release_name_fail_function fail_fn, + struct userdata_struct *userdata, + struct nmb_name *nmbname, + uint16 nb_flags, + struct in_addr release_ip); +struct response_record *queue_refresh_name( struct subnet_record *subrec, + response_function resp_fn, + timeout_response_function timeout_fn, + refresh_name_success_function success_fn, + refresh_name_fail_function fail_fn, + struct userdata_struct *userdata, + struct name_record *namerec, + struct in_addr refresh_ip); +struct response_record *queue_query_name( struct subnet_record *subrec, + response_function resp_fn, + timeout_response_function timeout_fn, + query_name_success_function success_fn, + query_name_fail_function fail_fn, + struct userdata_struct *userdata, + struct nmb_name *nmbname); +struct response_record *queue_node_status( struct subnet_record *subrec, + response_function resp_fn, + timeout_response_function timeout_fn, + node_status_success_function success_fn, + node_status_fail_function fail_fn, + struct userdata_struct *userdata, + struct nmb_name *nmbname, + struct in_addr send_ip); +void reply_netbios_packet(struct packet_struct *orig_packet, + int rcode, enum netbios_reply_type_code rcv_code, int opcode, + int ttl, char *data,int len); +void queue_packet(struct packet_struct *packet); +void process_browse_packet(struct packet_struct *p, char *buf,int len); +BOOL validate_nmb_response_packet( struct nmb_packet *nmb ); +BOOL validate_nmb_packet( struct nmb_packet *nmb ); +void run_packet_queue(); +void retransmit_or_expire_response_records(time_t t); +BOOL listen_for_packets(BOOL run_election); +BOOL send_mailslot(BOOL unique, char *mailslot,char *buf,int len, + char *srcname, int src_type, + char *dstname, int dest_type, + struct in_addr dest_ip,struct in_addr src_ip); + +/*The following definitions come from nmbd_processlogon.c */ + +void process_logon_packet(struct packet_struct *p,char *buf,int len, + char *mailslot); + +/*The following definitions come from nmbd_responserecordsdb.c */ + +void add_response_record(struct subnet_record *subrec, + struct response_record *rrec); +void remove_response_record(struct subnet_record *subrec, + struct response_record *rrec); +struct response_record *make_response_record( struct subnet_record *subrec, + struct packet_struct *p, + response_function resp_fn, + timeout_response_function timeout_fn, + success_function success_fn, + fail_function fail_fn, + struct userdata_struct *userdata); +struct response_record *find_response_record(struct subnet_record **ppsubrec, + uint16 id); + +/*The following definitions come from nmbd_sendannounce.c */ + +void send_browser_reset(int reset_type, char *to_name, int to_type, struct in_addr to_ip); +void broadcast_announce_request(struct subnet_record *subrec, struct work_record *work); +void announce_my_server_names(time_t t); +void reset_announce_timer(); +void announce_myself_to_domain_master_browser(time_t t); +void announce_my_servers_removed(void); +void announce_remote(time_t t); +void browse_sync_remote(time_t t); + +/*The following definitions come from nmbd_serverlistdb.c */ + +void remove_all_servers(struct work_record *work); +struct server_record *find_server_in_workgroup(struct work_record *work, char *name); +struct server_record *create_server_on_workgroup(struct work_record *work, + char *name,int servertype, + int ttl,char *comment); +void update_server_ttl(struct server_record *servrec, int ttl); +void expire_servers(struct work_record *work, time_t t); +void write_browse_list(time_t t, BOOL force_write); + +/*The following definitions come from nmbd_subnetdb.c */ + +BOOL create_subnets(); +BOOL we_are_a_wins_client(); +struct subnet_record *get_next_subnet_maybe_unicast(struct subnet_record *subrec); + +/*The following definitions come from nmbd_winsproxy.c */ + +void make_wins_proxy_name_query_request( struct subnet_record *subrec, + struct packet_struct *incoming_packet, + struct nmb_name *question_name); + +/*The following definitions come from nmbd_winsserver.c */ + +BOOL packet_is_for_wins_server(struct packet_struct *packet); +BOOL initialise_wins(void); +void wins_process_name_refresh_request(struct subnet_record *subrec, + struct packet_struct *p); +void wins_process_name_registration_request(struct subnet_record *subrec, + struct packet_struct *p); +void wins_process_multihomed_name_registration_request( struct subnet_record *subrec, + struct packet_struct *p); +void send_wins_name_query_response(int rcode, struct packet_struct *p, + struct name_record *namerec); +void wins_process_name_query_request(struct subnet_record *subrec, + struct packet_struct *p); +void wins_process_name_release_request(struct subnet_record *subrec, + struct packet_struct *p); +void initiate_wins_processing(time_t t); +void wins_write_database(void); + +/*The following definitions come from nmbd_workgroupdb.c */ + +struct work_record *find_workgroup_on_subnet(struct subnet_record *subrec, + fstring name); +struct work_record *create_workgroup_on_subnet(struct subnet_record *subrec, + fstring name, int ttl); +void update_workgroup_ttl(struct work_record *work, int ttl); +void initiate_myworkgroup_startup(struct subnet_record *subrec, struct work_record *work); +void dump_workgroups(void); +void expire_workgroups_and_servers(time_t t); /*The following definitions come from nmblib.c */ char *lookup_opcode_name( int opcode ); void debug_nmb_packet(struct packet_struct *p); char *namestr(struct nmb_name *n); -void free_nmb_packet(struct nmb_packet *nmb); +struct packet_struct *copy_packet(struct packet_struct *packet); void free_packet(struct packet_struct *packet); struct packet_struct *read_packet(int fd,enum packet_type packet_type); void make_nmb_name(struct nmb_name *n,char *name,int type,char *this_scope); +BOOL nmb_name_equal(struct nmb_name *n1, struct nmb_name *n2); BOOL send_packet(struct packet_struct *p); struct packet_struct *receive_packet(int fd,enum packet_type type,int t); -/*The following definitions come from nmbsync.c */ - -void sync_browse_lists(struct subnet_record *d, struct work_record *work, - char *name, int nm_type, struct in_addr ip, BOOL local); - /*The following definitions come from ntclient.c */ BOOL do_nt_login(char *desthost, char *myhostname, diff --git a/source3/lib/interface.c b/source3/lib/interface.c index 8af2696a44..0008ad889d 100644 --- a/source3/lib/interface.c +++ b/source3/lib/interface.c @@ -24,7 +24,7 @@ extern int DEBUGLEVEL; struct in_addr ipzero; -struct in_addr wins_ip; +struct in_addr allones_ip; struct in_addr loopback_ip; static struct in_addr default_ip; static struct in_addr default_bcast; @@ -33,7 +33,7 @@ static BOOL got_ip=False; static BOOL got_bcast=False; static BOOL got_nmask=False; -struct interface *local_interfaces = NULL; +static struct interface *local_interfaces = NULL; struct interface *last_iface; @@ -262,7 +262,7 @@ static void interpret_interfaces(char *s, struct interface **interfaces, struct in_addr ip; ipzero = *interpret_addr2("0.0.0.0"); - wins_ip = *interpret_addr2("255.255.255.255"); + allones_ip = *interpret_addr2("255.255.255.255"); loopback_ip = *interpret_addr2("127.0.0.1"); while (next_token(&ptr,token,NULL)) { @@ -424,6 +424,33 @@ int iface_count(void) return ret; } +/**************************************************************************** + True if we have two or more interfaces. + **************************************************************************/ +BOOL we_are_multihomed() +{ + static int multi = -1; + + if(multi == -1) + multi = (iface_count() > 1 ? True : False); + + return multi; +} + +/**************************************************************************** + return the Nth interface + **************************************************************************/ +struct interface *get_interface(int n) +{ + struct interface *i; + + for (i=local_interfaces;i && n;i=i->next) + n--; + + if (i) return i; + return NULL; +} + /**************************************************************************** return IP of the Nth interface **************************************************************************/ @@ -453,7 +480,9 @@ static struct interface *iface_find(struct in_addr ip) } /* these 3 functions return the ip/bcast/nmask for the interface - most appropriate for the given ip address */ + most appropriate for the given ip address. If they can't find + an appropriate interface they return the requested field of the + first known interface. */ struct in_addr *iface_bcast(struct in_addr ip) { diff --git a/source3/libsmb/namequery.c b/source3/libsmb/namequery.c index 15bf58bc55..9915ee92a8 100644 --- a/source3/libsmb/namequery.c +++ b/source3/libsmb/namequery.c @@ -189,21 +189,25 @@ BOOL name_status(int fd,char *name,int name_type,BOOL recurse, /**************************************************************************** do a netbios name query to find someones IP + returns an array of IP addresses or NULL if none + *count will be set to the number of addresses returned ****************************************************************************/ -BOOL name_query(int fd,char *name,int name_type, - BOOL bcast,BOOL recurse, - struct in_addr to_ip, struct in_addr *ip,void (*fn)()) +struct in_addr *name_query(int fd,char *name,int name_type, + BOOL bcast,BOOL recurse, + struct in_addr to_ip, int *count, void (*fn)()) { BOOL found=False; - int retries = 3; + int i, retries = 3; int retry_time = bcast?250:2000; struct timeval tval; struct packet_struct p; struct packet_struct *p2; struct nmb_packet *nmb = &p.packet.nmb; static int name_trn_id = 0; + struct in_addr *ip_list = NULL; bzero((char *)&p,sizeof(p)); + (*count) = 0; if (!name_trn_id) name_trn_id = (time(NULL)%(unsigned)0x7FFF) + (getpid()%(unsigned)100); @@ -237,7 +241,7 @@ BOOL name_query(int fd,char *name,int name_type, GetTimeOfDay(&tval); if (!send_packet(&p)) - return(False); + return NULL; retries--; @@ -248,7 +252,7 @@ BOOL name_query(int fd,char *name,int name_type, if (TvalDiff(&tval,&tval2) > retry_time) { if (!retries) break; if (!found && !send_packet(&p)) - return False; + return NULL; GetTimeOfDay(&tval); retries--; } @@ -256,7 +260,7 @@ BOOL name_query(int fd,char *name,int name_type, if ((p2=receive_packet(fd,NMB_PACKET,90))) { struct nmb_packet *nmb2 = &p2->packet.nmb; - debug_nmb_packet(p2); + debug_nmb_packet(p2); if (nmb->header.name_trn_id != nmb2->header.name_trn_id || !nmb2->header.response) { @@ -279,11 +283,17 @@ BOOL name_query(int fd,char *name,int name_type, continue; } - if (ip) { - putip((char *)ip,&nmb2->answers->rdata[2]); - DEBUG(fn?3:2,("Got a positive name query response from %s", - inet_ntoa(p2->ip))); - DEBUG(fn?3:2,(" (%s)\n",inet_ntoa(*ip))); + ip_list = (struct in_addr *)Realloc(ip_list, sizeof(ip_list[0]) * + ((*count)+nmb2->answers->rdlength/6)); + if (ip_list) { + DEBUG(fn?3:2,("Got a positive name query response from %s ( ", + inet_ntoa(p2->ip))); + for (i=0;ianswers->rdlength/6;i++) { + putip((char *)&ip_list[(*count)],&nmb2->answers->rdata[2+i*6]); + DEBUG(fn?3:2,("%s ",inet_ntoa(ip_list[(*count)]))); + (*count)++; + } + DEBUG(fn?3:2,(")\n")); } found=True; retries=0; free_packet(p2); @@ -291,5 +301,5 @@ BOOL name_query(int fd,char *name,int name_type, } } - return(found); + return ip_list; } diff --git a/source3/libsmb/nmblib.c b/source3/libsmb/nmblib.c index 6a91b20ea8..0335f01833 100644 --- a/source3/libsmb/nmblib.c +++ b/source3/libsmb/nmblib.c @@ -34,11 +34,13 @@ static struct opcode_names { char *nmb_opcode_name; int opcode; } nmb_header_opcode_names[] = { - { "Query", 0 }, + {"Query", 0 }, {"Registration", 5 }, {"Release", 6 }, {"WACK", 7 }, - {"refresh", 8 }, + {"Refresh", 8 }, + {"Refresh(altcode)", 9 }, + {"Multi-homed Registration", 15 }, {0, -1 } }; @@ -205,7 +207,7 @@ static int parse_nmb_name(char *inbuf,int offset,int length, struct nmb_name *na if (n==16) { /* parse out the name type, its always in the 16th byte of the name */ - name->name_type = name->name[15]; + name->name_type = ((unsigned char)name->name[15]) & 0xff; /* remove trailing spaces */ name->name[15] = 0; @@ -249,6 +251,7 @@ static int put_nmb_name(char *buf,int offset,struct nmb_name *name) /* special case for wildcard name */ bzero(buf1,20); buf1[0] = '*'; + buf1[15] = name->name_type; } else { sprintf(buf1,"%-15.15s%c",name->name,name->name_type); } @@ -292,9 +295,9 @@ char *namestr(struct nmb_name *n) char *p = ret[i]; if (!n->scope[0]) - sprintf(p,"%s(%x)",n->name,n->name_type); + sprintf(p,"%s<%02x>",n->name,n->name_type); else - sprintf(p,"%s(%x).%s",n->name,n->name_type,n->scope); + sprintf(p,"%s<%02x>.%s",n->name,n->name_type,n->scope); i = (i+1)%4; return(p); @@ -467,26 +470,146 @@ static BOOL parse_nmb(char *inbuf,int length,struct nmb_packet *nmb) return(True); } +/******************************************************************* + 'Copy constructor' for an nmb packet + ******************************************************************/ +static struct packet_struct *copy_nmb_packet(struct packet_struct *packet) +{ + struct nmb_packet *nmb; + struct nmb_packet *copy_nmb; + struct packet_struct *pkt_copy; + + if(( pkt_copy = (struct packet_struct *)malloc(sizeof(*packet))) == NULL) + { + DEBUG(0,("copy_nmb_packet: malloc fail.\n")); + return NULL; + } + + /* Structure copy of entire thing. */ + + *pkt_copy = *packet; + + /* Ensure this copy is not locked. */ + pkt_copy->locked = False; + + /* Ensure this copy has no resource records. */ + nmb = &packet->packet.nmb; + copy_nmb = &pkt_copy->packet.nmb; + + copy_nmb->answers = NULL; + copy_nmb->nsrecs = NULL; + copy_nmb->additional = NULL; + + /* Now copy any resource records. */ + + if (nmb->answers) + { + if((copy_nmb->answers = (struct res_rec *) + malloc(nmb->header.ancount * sizeof(struct res_rec))) == NULL) + goto free_and_exit; + memcpy((char *)copy_nmb->answers, (char *)nmb->answers, + nmb->header.ancount * sizeof(struct res_rec)); + } + if (nmb->nsrecs) + { + if((copy_nmb->nsrecs = (struct res_rec *) + malloc(nmb->header.nscount * sizeof(struct res_rec))) == NULL) + goto free_and_exit; + memcpy((char *)copy_nmb->nsrecs, (char *)nmb->nsrecs, + nmb->header.nscount * sizeof(struct res_rec)); + } + if (nmb->additional) + { + if((copy_nmb->additional = (struct res_rec *) + malloc(nmb->header.arcount * sizeof(struct res_rec))) == NULL) + goto free_and_exit; + memcpy((char *)copy_nmb->additional, (char *)nmb->additional, + nmb->header.arcount * sizeof(struct res_rec)); + } + + return pkt_copy; + +free_and_exit: + + if(copy_nmb->answers) + free((char *)copy_nmb->answers); + if(copy_nmb->nsrecs) + free((char *)copy_nmb->nsrecs); + if(copy_nmb->additional) + free((char *)copy_nmb->additional); + free((char *)pkt_copy); + + DEBUG(0,("copy_nmb_packet: malloc fail in resource records.\n")); + return NULL; +} + +/******************************************************************* + 'Copy constructor' for a dgram packet + ******************************************************************/ +static struct packet_struct *copy_dgram_packet(struct packet_struct *packet) +{ + struct packet_struct *pkt_copy; + + if(( pkt_copy = (struct packet_struct *)malloc(sizeof(*packet))) == NULL) + { + DEBUG(0,("copy_dgram_packet: malloc fail.\n")); + return NULL; + } + + /* Structure copy of entire thing. */ + + *pkt_copy = *packet; + + /* Ensure this copy is not locked. */ + pkt_copy->locked = False; + + /* There are no additional pointers in a dgram packet, + we are finished. */ + return pkt_copy; +} + +/******************************************************************* + 'Copy constructor' for a generic packet + ******************************************************************/ +struct packet_struct *copy_packet(struct packet_struct *packet) +{ + if(packet->packet_type == NMB_PACKET) + return copy_nmb_packet(packet); + else if (packet->packet_type == DGRAM_PACKET) + return copy_dgram_packet(packet); + return NULL; +} + /******************************************************************* free up any resources associated with an nmb packet ******************************************************************/ -void free_nmb_packet(struct nmb_packet *nmb) +static void free_nmb_packet(struct nmb_packet *nmb) { if (nmb->answers) free(nmb->answers); if (nmb->nsrecs) free(nmb->nsrecs); if (nmb->additional) free(nmb->additional); } +/******************************************************************* + free up any resources associated with a dgram packet + ******************************************************************/ +static void free_dgram_packet(struct dgram_packet *nmb) +{ + /* We have nothing to do for a dgram packet. */ +} + /******************************************************************* free up any resources associated with a packet ******************************************************************/ void free_packet(struct packet_struct *packet) { - if (packet->locked) - return; - if (packet->packet_type == NMB_PACKET) - free_nmb_packet(&packet->packet.nmb); - free(packet); + if (packet->locked) + return; + if (packet->packet_type == NMB_PACKET) + free_nmb_packet(&packet->packet.nmb); + else if (packet->packet_type == DGRAM_PACKET) + free_dgram_packet(&packet->packet.dgram); + free(packet); } /******************************************************************* @@ -619,12 +742,22 @@ static int build_dgram(char *buf,struct packet_struct *p) ******************************************************************/ void make_nmb_name(struct nmb_name *n,char *name,int type,char *this_scope) { - fstrcpy(n->name,name); + StrnCpy(n->name,name,15); strupper(n->name); - n->name_type = type; - fstrcpy(n->scope,this_scope); + n->name_type = (unsigned int)type & 0xFF; + StrnCpy(n->scope,this_scope,63); } +/******************************************************************* + Compare two nmb names + ******************************************************************/ + +BOOL nmb_name_equal(struct nmb_name *n1, struct nmb_name *n2) +{ + return ((n1->name_type == n2->name_type) && + strequal(n1->name ,n2->name ) && + strequal(n1->scope,n2->scope)); +} /******************************************************************* build a nmb packet ready for sending diff --git a/source3/nameannounce.c b/source3/nameannounce.c deleted file mode 100644 index 28ebe5da90..0000000000 --- a/source3/nameannounce.c +++ /dev/null @@ -1,554 +0,0 @@ -/* - Unix SMB/Netbios implementation. - Version 1.9. - NBT netbios routines and daemon - version 2 - Copyright (C) Andrew Tridgell 1994-1997 - - SMB Version handling - Copyright (C) John H Terpstra 1995-1997 - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - Revision History: - - 14 jan 96: lkcl@pires.co.uk - added multiple workgroup domain master support - -*/ - -#include "includes.h" - -#define TEST_CODE - -extern int DEBUGLEVEL; -extern BOOL CanRecurse; - -extern struct in_addr ipzero; - -extern pstring myname; -extern fstring myworkgroup; -extern char **my_netbios_names; - -extern int ClientDGRAM; -extern int ClientNMB; - -/* this is our domain/workgroup/server database */ -extern struct subnet_record *subnetlist; - -extern int updatecount; -extern int workgroup_count; - -extern struct in_addr wins_ip; - -extern pstring scope; - -/**************************************************************************** - send a announce request to the local net - **************************************************************************/ -void announce_request(struct work_record *work, struct in_addr ip) -{ - pstring outbuf; - char *p; - - if (!work) return; - - work->needannounce = True; - - DEBUG(2,("sending announce request to %s for workgroup %s\n", - inet_ntoa(ip),work->work_group)); - - bzero(outbuf,sizeof(outbuf)); - p = outbuf; - CVAL(p,0) = ANN_AnnouncementRequest; - p++; - - CVAL(p,0) = work->token; /* (local) unique workgroup token id */ - p++; - StrnCpy(p,myname,16); - strupper(p); - p = skip_string(p,1); - - /* 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)); -} - - -/**************************************************************************** - request an announcement - **************************************************************************/ -void do_announce_request(char *info, char *to_name, int announce_type, - int from, - int to, struct in_addr dest_ip) -{ - pstring outbuf; - char *p; - - bzero(outbuf,sizeof(outbuf)); - p = outbuf; - CVAL(p,0) = announce_type; - p++; - - DEBUG(2,("sending announce type %d: info %s to %s - server %s(%x)\n", - announce_type, info, inet_ntoa(dest_ip),to_name,to)); - - StrnCpy(p,info,16); - strupper(p); - p = skip_string(p,1); - - send_mailslot_reply(False,BROWSE_MAILSLOT, ClientDGRAM, - outbuf,PTR_DIFF(p,outbuf), - myname,to_name,from,to,dest_ip,*iface_ip(dest_ip)); -} - - -/**************************************************************************** - find a server responsible for a workgroup, and sync browse lists - control ends up back here via response_name_query. - **************************************************************************/ -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); - } -} - - -/**************************************************************************** - send a host announcement packet - **************************************************************************/ -static 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, - time_t announce_interval, - char *server_name, int server_type, char *server_comment) -{ - pstring outbuf; - char *p; - - bzero(outbuf,sizeof(outbuf)); - p = outbuf+1; - - /* command type */ - CVAL(outbuf,0) = command; - - /* announcement parameters */ - CVAL(p,0) = updatecount; - SIVAL(p,1,announce_interval*1000); /* ms - despite the spec */ - - StrnCpy(p+5,server_name,16); - strupper(p+5); - - CVAL(p,21) = lp_major_announce_version(); /* major version */ - CVAL(p,22) = lp_minor_announce_version(); /* minor version */ - - SIVAL(p,23,server_type & ~SV_TYPE_LOCAL_LIST_ONLY); - /* browse version: got from NT/AS 4.00 - Value defined in smb.h (JHT)*/ - SSVAL(p,27,BROWSER_ELECTION_VERSION); - SSVAL(p,29,BROWSER_CONSTANT); /* browse signature */ - - pstrcpy(p+31,server_comment); - p += 31; - p = skip_string(p,1); - - debug_browse_data(outbuf, PTR_DIFF(p,outbuf)); - - /* send the announcement */ - send_mailslot_reply(False,BROWSE_MAILSLOT, ClientDGRAM, outbuf, - PTR_DIFF(p,outbuf), - from_name, to_name, - from_type, to_type, - to_ip, from_ip); -} - - -/**************************************************************************** -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 (!is_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) -{ - /* 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) - { - DEBUG(0,("announce_server: error - announcement requested on WINS \ -interface for workgroup %s, name %s\n", work->work_group, name)); - return; - } - - /* Only do domain announcements if we are a master and it's - our name we're being asked to announce. */ - if (AM_MASTER(work) && strequal(myname,name)) - { - 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(time_t t) -{ - struct subnet_record *d; - pstring comment; - char *my_name; - - StrnCpy(comment, lp_serverstring(), 43); - - my_name = *myname ? myname : "NoName"; - - for (d = FIRST_SUBNET; d; d = NEXT_SUBNET_EXCLUDING_WINS(d)) - { - struct work_record *work; - - for (work = d->workgrouplist; work; work = work->next) - { - uint32 stype = work->ServerType; - struct server_record *s; - - /* 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, - CHECK_TIME_MIN_HOST_ANNCE*60); - work->lastannounce_time = t - (work->announce_interval+1); - } - - /* announce every minute at first then progress to every 12 mins */ - if (work->lastannounce_time && - (t - work->lastannounce_time) < work->announce_interval) - continue; - - if (work->announce_interval < CHECK_TIME_MAX_HOST_ANNCE * 60) - work->announce_interval += 60; - - work->lastannounce_time = t; - - for (s = work->serverlist; s; s = s->next) { - if (is_myname(s->serv.name)) { - /* If we are any kind of browser or logon server, only - announce it for our primary name, not our aliases. */ - if(!strequal(myname, s->serv.name)) - stype &= ~(SV_TYPE_MASTER_BROWSER|SV_TYPE_POTENTIAL_BROWSER| - SV_TYPE_DOMAIN_MASTER|SV_TYPE_DOMAIN_MEMBER); - announce_server(d,work,s->serv.name,comment, - work->announce_interval,stype); - } - } - - if (work->needannounce) - { - work->needannounce = False; - break; - /* sorry: can't do too many announces. do some more later */ - } - } - } -} - -/* 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; - -/**************************************************************************** - 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. - - this actually gets done in search_and_sync_workgroups() via 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(time_t t) -{ - struct subnet_record *d; - struct work_record *work; - BOOL am_master = False; /* are we a master of some sort? :-) */ - - 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; - } - - if(wins_client_subnet == NULL) - { - DEBUG(10,("announce_master: no wins subnet, ignoring.\n")); - return; - } - - announce_timer_last = t; - - for (d = FIRST_SUBNET; d; d = NEXT_SUBNET_EXCLUDING_WINS(d)) - { - 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 */ - - /* 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_client_subnet, myworkgroup, False); - - if (work) - { - /* assume that the domain master browser we want to sync - with is our own domain. - */ - char *name = work->work_group; - int 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, - ipzero, ipzero); - } - else if(lp_wins_support()) - { - /* We are the WINS server - query ourselves for the dmb name. */ - - struct nmb_name netb_name; - struct name_record *nr = 0; - - make_nmb_name(&netb_name, name, type, scope); - - if ((nr = find_name_on_subnet(wins_client_subnet, &netb_name, FIND_ANY_NAME)) == 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_client_subnet, ClientNMB, - NMB_STATUS,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, 0); - } - } -} - -/**************************************************************************** - 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 = lp_default_server_announce(); - - if (last_time && t < last_time + REMOTE_ANNOUNCE_INTERVAL) - return; - - last_time = t; - - s = lp_remote_announce(); - if (!*s) return; - - comment = lp_serverstring(); - 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; - int n; - - wgroup = strchr(s2,'/'); - if (wgroup) *wgroup++ = 0; - if (!wgroup || !*wgroup) - wgroup = workgroup; - - addr = *interpret_addr2(s2); - - /* Announce all our names including aliases */ - for (n=0; my_netbios_names[n]; n++) - { - char *name = my_netbios_names[n]; - do_announce_host(ANN_HostAnnouncement,name,0x20,*iface_ip(addr), - wgroup,0x1e,addr, - REMOTE_ANNOUNCE_INTERVAL, - name,stype,comment); - } - } -} - -/**************************************************************************** - do all the "remote" browse synchronisation stuff. - These are used to put our browse lists into remote browse lists. - **************************************************************************/ -void browse_sync_remote(time_t t) -{ - char *s,*ptr; - static time_t last_time = 0; - pstring s2; - struct in_addr addr; - - if (last_time && t < last_time + REMOTE_ANNOUNCE_INTERVAL) - return; - - last_time = t; - - s = lp_remote_browse_sync(); - if (!*s) return; - - for (ptr=s; next_token(&ptr,s2,NULL); ) - { - /* the entries are of the form a.b.c.d */ - int n; - - addr = *interpret_addr2(s2); - - /* Announce all our names including aliases */ - for (n=0; my_netbios_names[n]; n++) - { - char *name = my_netbios_names[n]; - do_announce_request(name, "*", ANN_MasterAnnouncement, 0x20, 0, addr); - } - } -} diff --git a/source3/nameannounce.doc b/source3/nameannounce.doc deleted file mode 100644 index e04a59209a..0000000000 --- a/source3/nameannounce.doc +++ /dev/null @@ -1,265 +0,0 @@ -/* - Unix SMB/Netbios documentation. - Version 0.2 - Copyright (C) Luke Leighton Andrew Tridgell 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. - - Document name: nameannounce.doc - - Revision History: - - 0.0 - 02jul96 : lkcl@pires.co.uk - created - - 0.1 - 22jul96 : Andrew.Tridgell@anu.edu.au - tridge's comments on first revision - - 0.2 - 05aug96 : lkcl@pires.co.uk - actioned tridge comments about pdc -> domain master - documented NAME_QUERY_ANNOUNCE_HOST - -*/ - - -this module deals with announcements: the sending of announcement requests -and the sending of announcements either to refresh other servers' records -or as a response to announcement requests. - - -/************************************************************************* - announce_master() - *************************************************************************/ - -this function is responsible for announcing samba as a master browser -to all known domain masters. - -this announcement is sent out at CHECK_TIME_MST_ANNOUNCE minute -intervals, only if samba is a master browser on one or more of -its local interfaces. - -if no domain controller has been specified (lp_domain_controller()) -samba goes through its list of servers looking for domain master -browsers. when it finds one (other than itself) it will either -initiate a NAME_QUERY_PDC_SRV_CHK by broadcast or with a WINS -server. this will result in a NAME_STATUS_PDC_SRV_CHK, which -will result in a sync browse list and an announcement -ANN_MasterAnnounce being sent (see sync_server()). - -if a domain controller has been specified, samba will search for -a domain master browser for its workgroup (either by directed -packet or by broadcast if it cannot resolve the domain controller -name using DNS), which results in the same action as listed above. - ------------- -NOTE FROM TRIDGE: - -PDC in the above should really be DMB (domain master browser). They -might be separate entities. - -I also propose a simpler scheme :-) - -If a DMB is not configured with lp_domain_controller() (perhaps -renamed to lp_domain_master()?) then just don't do master -announcements. Remember that most peoples networks are very simple and -don't need DMB capabilities. Those that do need them will have more -complex network topologies and they really need to choose themselves -which box will act as the "hub" for netbios name resolution. Doing it -via name queries will just lead to lag and propogation delays, because -if two parts of the net choose different DMBs then the data will be -very slow to propoogate. - -If a DMB is configured then just send the master announcemnt to that -box! Thats all that needs to be done. Just send a udp 138 packet and -forget it. If the recipient is indeed a DMB (as it should be if the -config file is correct) then it should initiate a browse list sync -with us at some later time, but that is take care of by smbd and nmbd -doesn't even need to know it happened. - -Additionally, if a DMB is configured we need to sync our workgroup -list and server list with them occasionally. Note that this is only -time a non-DMB should do a browse sync, and it should only do it with -a DMB. Essentially WAN based netbios is just a simple star. There is a -DMB in the centre, and the individual master browsers for each subnet -talk to it, but never talk to each other. If they start talking to -each other then the network load will go as the square of the number -of machines, which will result in meltdown :-) -------------- - - -/************************************************************************* - announce_host() - *************************************************************************/ - -this complex-looking function is responsible for announcing samba's -existence to other servers by broadcast. the actual announcement -is carried out by announce_server(). - -the time period between samba's announcement will stretch from one -minute to twelve minutes by one minute. if samba has received an -announce request from a master browser, then it should answer at -any random interval between zero and thirty seconds after the -request is received. this is to ensure that the master browser -does not get overloaded with responses! - - -/************************************************************************* - announce_server() - *************************************************************************/ - -this function is responsible for sending announcement packets. -these packets are received by other servers, which will then -update their records accordingly: what services we have, our -name, our comment field and our time to live (to name a few). - -if samba is a non-master then we need to see if there is a -domain master (on a remote subnet) that we need to announce to -it. - -if samba is not the WINS server (and it is using another -WINS server) then we need to do a name query to the WINS -server to ask it what the domain controller is. this is done -using a samba 'state' NAME_QUERY_ANNOUNCE_HOST, which passes -sufficient information on to be able to carry out the -host announcement using a unicasted do_announce_host() if and -when a reply comes back. if there is no reply to the name query, -this is not necessarily an error - there may genuinely be no -domain master currently up and running for samba's workgroup. - -if samba is a WINS server, then samba will need to look up the -domain controller for its workgroup in its WINS records. an -over-cautious samba could carry out a name query on that -domain controller to make sure that it is alive and that samba's -WINS records are up-to-date. in any event, it will send a unicast -do_announce_host() to inform the domain master browser, if one -exists, of samba's server status. - -if we are a master browser, then using do_announce_host() we -must send a broadcast announcement on the local interface -notifying members of that workgroup that we are their master -browser, and another announcement indicating to all backup -browsers and master browsers that we are a master browser. - -(note: if another master browser receives this broadcasted -announcement and thinks that it is also the master browser -for this workgroup, it stops being a master browser and forces -an election). - -if we are not a master browser, then we send a broacast -announcement notifying the master browser that we are a member -of its workgroup, on the local interface. - - -/************************************************************************* - remove_my_servers() - *************************************************************************/ - -this function is responsible for informing other servers that -samba is about to go down. it announces, on all subnets, that -samba's time to live is zero and that it has no services. - - -/************************************************************************* - do_announce_host() - *************************************************************************/ - -this function is responsible for sending out an announcement -MAILSLOT browse packet. it contains information such as the -time to live, name of the server, services that the server -offers etc. - -the format of this MAILSLOT browse packet is described in -draft-heizer-cifs-v1-spec-00.txt 3.9.50.4.1 page 165-6. - - -/************************************************************************* - announce_backup() - *************************************************************************/ - -this function is responsible for getting master browsers and domain -controllers to send us lists of backup servers. this is done by -sending an ANN_GetBackupListReq browse mailslot. - -the local master browser, or domain master browser, should respond -with an ANN_GetBackupListResp browse mailslot containing the list -of backup servers. - --------------- -NOTE FROM TRIDGE: I don't see why nmbd should ever send one of -these. The only reason I can see for any part of Samba sending one of -these is if we implement it in smbclient. - -This packet is used to request a list of backup master browsers from -the master browser. It is used by clients (not servers!) to spread the -browse load over more than one server. The only server that needs to -know what the list of backups is is the master browser, and as it is -also responsible for generating this list it will never ask anyone -else for it. --------------- - - -/************************************************************************* - sync_server() - *************************************************************************/ - -this function is responsible for initiating a sync browse list -sequence and, if necessary, carrying out an ANN_MasterAnnouncement -to the domain master browser (that we are also sync'ing browse lists -with). - -see nameservresp.c:response_name_status_check(). - - -/************************************************************************* - announce_request() - *************************************************************************/ - -this function is responsible for sending an announcement request to -another server. this server should respond with an announcement. - -if the announce request is sent to WORKGROUP(0x1e) then members of -the workgroup will respond (with ANN_HostAnnounce packets) - -if the announce request is sent to WORKGROUP(0x1d) then the master -browser of the workgroup should respond (ANN_LocalMasterAnnounce). -this is untested. - -if the announce request is sent to ^1^2__MSBROWSE__^2(0x1) then -(and this is pure speculation), all backup browsers and master -browsers should respond with ANN_DomainAnnounce packets. -this is untested. - ------------ -NOTE FROM TRIDGE: - -I had great trouble getting machines to actually respond to this -packet. Either we have the format wrong or MS chose not to implement -it. - -Not implementing it doesn't break anything, it just means a new master -browser won't get a complete server list as quickly. - -Also note that this packet should be used as little as possible as it -could easily cause meltdown if too many servers used it. Imagine a -dozen samba servers on a net all sending this packet! You will get 244 -responses all within 30 seconds. now imagine 50 samba servers .... - -So I think we should restrict ourselves to sending this packet only if -we are already the master browser for a workgroup. We could send a -single "announce request" when we become the master, just to prime our -server lists. From then on the normal announce cycles should take care -of keeping it uptodate. ------------ - diff --git a/source3/namebrowse.c b/source3/namebrowse.c deleted file mode 100644 index 2f883d4450..0000000000 --- a/source3/namebrowse.c +++ /dev/null @@ -1,241 +0,0 @@ -/* - Unix SMB/Netbios implementation. - Version 1.9. - NBT netbios routines and daemon - version 2 - 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 - 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" -#include "smb.h" - -extern int ClientNMB; - -extern int DEBUGLEVEL; - -extern struct in_addr wins_ip; - -/* this is our browse master/backup cache database */ -static struct browse_cache_record *browserlist = NULL; - - -/*************************************************************************** - add a browser into the list - **************************************************************************/ -static void add_browse_cache(struct browse_cache_record *b) -{ - struct browse_cache_record *b2; - - if (!browserlist) - { - browserlist = b; - b->prev = NULL; - b->next = NULL; - return; - } - - for (b2 = browserlist; b2->next; b2 = b2->next) ; - - b2->next = b; - b->next = NULL; - b->prev = b2; -} - - -/******************************************************************* - remove old browse entries - ******************************************************************/ -void expire_browse_cache(time_t t) -{ - struct browse_cache_record *b; - struct browse_cache_record *nextb; - - /* expire old entries in the serverlist */ - for (b = browserlist; b; b = nextb) - { - nextb = b->next; - if (b->synced && b->sync_time < t) { - DEBUG(3,("Removing dead cached browser %s\n",b->name)); - - if (b->prev) b->prev->next = b->next; - if (b->next) b->next->prev = b->prev; - - if (browserlist == b) browserlist = b->next; - - free(b); - } - } -} - -/**************************************************************************** - add a browser entry - ****************************************************************************/ -struct browse_cache_record *add_browser_entry(char *name, int type, char *wg, - time_t ttl, struct subnet_record *d, - struct in_addr ip, BOOL local) -{ - BOOL newentry=False; - - struct browse_cache_record *b; - - /* search for the entry: if it's already in the cache, update that entry */ - for (b = browserlist; b; b = b->next) - { - if (ip_equal(ip,b->ip) && strequal(b->group, wg)) break; - } - - if (b && b->synced) - { - /* entries get left in the cache for a while. this stops sync'ing too - often if the network is large */ - DEBUG(4, ("browser %s %s %s already sync'd at time %d\n", - b->name, b->group, inet_ntoa(b->ip), b->sync_time)); - return NULL; - } - - if (!b) - { - newentry = True; - b = (struct browse_cache_record *)malloc(sizeof(*b)); - - if (!b) return(NULL); - - bzero((char *)b,sizeof(*b)); - } - - /* update the entry */ - ttl = time(NULL)+ttl; - - StrnCpy(b->name ,name,sizeof(b->name )-1); - StrnCpy(b->group,wg ,sizeof(b->group)-1); - strupper(b->name); - strupper(b->group); - - b->ip = ip; - b->type = type; - b->local = local; /* local server list sync or complete sync required */ - b->subnet = d; - - if (newentry || ttl < b->sync_time) - b->sync_time = ttl; - - if (newentry) - { - b->synced = False; - add_browse_cache(b); - - DEBUG(3,("Added cache entry %s %s(%2x) %s ttl %d\n", - wg, name, type, inet_ntoa(ip),ttl)); - } - else - { - DEBUG(3,("Updated cache entry %s %s(%2x) %s ttl %d\n", - wg, name, type, inet_ntoa(ip),ttl)); - } - - return(b); -} - - -/**************************************************************************** -find a server responsible for a workgroup, and sync browse lists -**************************************************************************/ -static void start_sync_browse_entry(struct browse_cache_record *b) -{ - struct subnet_record *d = b->subnet; - struct work_record *work; - - /* Check panic conditions - these should not be true. */ - if(b->subnet != wins_client_subnet) { - DEBUG(0, - ("start_sync_browse_entry: ERROR sync requested on non-WINS subnet.\n")); - return; - } - - if (!(work = find_workgroupstruct(d, b->group, False))) { - DEBUG(0, ("start_sync_browse_entry: failed to get a \ -workgroup for a browse cache entry workgroup %s, server %s\n", - b->group, b->name)); - return; - } - - DEBUG(4, ("start_sync_browse_entry: Initiating %s sync with %s<0x20>, \ -workgroup %s\n", - b->local ? "local" : "remote", b->name, b->group)); - - /* first check whether the server we intend to sync with exists. if it - doesn't, the server must have died. o dear. */ - - /* see response_netbios_packet() or expire_netbios_response_entries() */ - /* We cheat here by using the my_comment field of the response_record - struct as the workgroup name we are going to do the sync for. - This is because the reply packet doesn't include the workgroup, but - we need it when the reply comes back. - */ - queue_netbios_packet(d,ClientNMB,NMB_QUERY, - b->local?NAME_QUERY_SYNC_LOCAL:NAME_QUERY_SYNC_REMOTE, - b->name,0x20,0,0,0,NULL,b->group, - False,False,b->ip,b->ip, 0); - - b->synced = True; -} - - -/**************************************************************************** - search through browser list for an entry to sync with - **************************************************************************/ -void do_browser_lists(time_t t) -{ - struct browse_cache_record *b; - static time_t last = 0; - - if (t-last < 20) - { - DEBUG(9,("do_browser_lists: returning due to t(%d) - last(%d) < 20\n", - t, last)); - return; /* don't do too many of these at once! */ - /* XXXX equally this period should not be too long - the server may die in the intervening gap */ - } - last = t; - - /* pick any entry in the list, preferably one whose time is up */ - for (b = browserlist; b && b->next; b = b->next) - { - if (b->sync_time < t && b->synced == False) break; - } - - if (b && !b->synced) - { - /* sync with the selected entry then remove some dead entries */ - DEBUG(4,("do_browser_lists: Initiating sync with %s, workgroup %s\n", - b->name, b->group)); - start_sync_browse_entry(b); - } - else - { - DEBUG(9, ("do_browser_lists: no entries to sync.\n")); - } - - expire_browse_cache(t - 60); -} - diff --git a/source3/namebrowse.doc b/source3/namebrowse.doc deleted file mode 100644 index 82713d8570..0000000000 --- a/source3/namebrowse.doc +++ /dev/null @@ -1,149 +0,0 @@ -/* - Unix SMB/Netbios documentation. - Version 0.1 - Copyright (C) Luke Leighton Andrew Tridgell 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. - - Document name: namebrowse.doc - - Revision History: - - 0.0 - 02jul96 : lkcl@pires.co.uk - created - - 0.1 - 22jul96 Andrew.Tridgell@anu.edu.au - tridge's comments on first revision -*/ - -this module deals with queueing servers that samba must sync browse -lists with. it will always issue a name query immediately before -actually carrying out the NetServerEnum call, to ensure that time -is not wasted by a remote server's failure. - -this module was created to minimise the amount of NetServerEnum calls -that samba may be asked to perform, by maintaining the name of a server -for up to a minute after the NetServerEnum call was issued, and -disallowing further NetServerEnum calls to this remote server until -the entry is removed. - -samba can ask for a NetServerEnum call to be issued to grab a remote -server's list of servers and workgroups either in its capacity as -a domain master browser, as a local master browser. - -samba does not deal with becoming a backup master browser properly -at present. - -------------- -NOTE FROM TRIDGE: - -Yes, samba can send these either in its capacity as a DMB or as a -MB. There are only two situations: - -- If samba is a DMB then it should sync with the "local only" bit set -with any master browser that has sent it a "master announce". - -- if samba is not a DMB then it can only sync with the DMB, and should -not set the "local only" bit. - -Note that samba should never sync with other non-DMB servers when it -is not a DMB. - -Try to do a sync under any other circumstances is dangerous without a -multi-threaded nmbd. I have a print server at home that knows some SMB -and NBT, but if you try to sync browse lists with it then it clogs up, -and also clogs up nmbd while it times out the connection. If we -follow the above two rules then we can't get into this sort of -trouble as: - -- if we are a DMB and a master browser sends us a "master announce" -then it is expecting to receive a NetServerEnum SMB connection soon, -and must be capabable of handling it. - -- if we are not a DMB then we will only sync with the DMB, which must -be capable of doing this stuff or things are really in a mess :-) --------------- - - -/************************************************************************* - do_browser_lists() - *************************************************************************/ - -this function is responsible for finding an appropriate entry in the -sync browser cache, initiating a name query (which results in a -NetServerEnum call if there is a positive response), and then -removing all entries that have been actioned and have been around -for over a minute. - - -/************************************************************************* - start_sync_browse_entry() - *************************************************************************/ - -this function is responsible for initiating a name query. if a -positive response is received, then this will result in a -NetServerEnum api call. - -samba will only initiate this process if it is a master browser -for this workgroup. - ------------ -NOTE FROM TRIDGE: - -I'd actually prefer to skip the name query completely if we can -resolve the DMBs name via gethostbyname(). For the name query to work -we either have to have WINS working, or we need to know the broadcast -address of the network that the DMB is on. This makes us too dependent -on too many thing being right. - -If the gethostbyname() fails then sure, go for a normal name query, -but if it works then we have saved ourselves a lot of trouble and -gained a lot of robustness. - -This is best handled by a generic "resolve netbios name" routine that -tries DNS first then resorts to WINS or bcast if that fails. It also -needs to cache the results. -------------- - - -/************************************************************************* - add_browser_entry() - *************************************************************************/ - -this function is responsible for adding a browser into the list of -servers to sync browse lists with. if the server entry has already -been added and syncing browse lists has already been initiated, it -will not be added again. - - -/************************************************************************* - expire_browse_cache() - *************************************************************************/ - -this function is responsible for removing entries that have had the -sync browse list initiated (whether that succeeded or not is beyond -this function's scope) and have been in the cache for a while. - - -/************************************************************************* - add_browse_entry() - *************************************************************************/ - -this function is responsible for adding a new entry into the list -of servers to sync browse lists with at some point in the near future. - - - - diff --git a/source3/namedbname.c b/source3/namedbname.c deleted file mode 100644 index 6ff20f4d45..0000000000 --- a/source3/namedbname.c +++ /dev/null @@ -1,518 +0,0 @@ -/* - Unix SMB/Netbios implementation. - Version 1.9. - NBT netbios routines and daemon - version 2 - 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 - 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. - - Module name: namedbname.c - - Revision History: - - 14 jan 96: lkcl@pires.co.uk - added multiple workgroup domain master support - - 04 jul 96: lkcl@pires.co.uk - created module namedbname containing name database functions -*/ - -#include "includes.h" - -extern int DEBUGLEVEL; - -extern pstring scope; -extern struct in_addr ipzero; -extern struct in_addr wins_ip; -extern BOOL updatedlists; - -extern struct subnet_record *subnetlist; - -#define WINS_LIST "wins.dat" - -uint16 nb_type = 0; /* samba's NetBIOS name type */ - - -/**************************************************************************** - samba's NetBIOS name type - - XXXX maybe functionality could be set: B, M, P or H name registration - and resolution could be set through nb_type. just a thought. - ****************************************************************************/ -void set_samba_nb_type(void) -{ - if (lp_wins_support() || (*lp_wins_server())) - { - nb_type = NB_MFLAG; /* samba is a 'hybrid' node type */ - } - else - { - nb_type = NB_BFLAG; /* samba is broadcast-only node type */ - } -} - - -/**************************************************************************** - true if two netbios names are equal -****************************************************************************/ -BOOL name_equal(struct nmb_name *n1,struct nmb_name *n2) -{ - return n1->name_type == n2->name_type && - strequal(n1->name ,n2->name ) && - strequal(n1->scope,n2->scope); -} - - -/**************************************************************************** - true if the netbios name is ^1^2__MSBROWSE__^2^1 - - note: this name is registered if as a master browser or backup browser - you are responsible for a workgroup (when you announce a domain by - broadcasting on your local subnet, you announce it as coming from this - name: see announce_host()). - - **************************************************************************/ -BOOL ms_browser_name(char *name, int type) -{ - return strequal(name,MSBROWSE) && type == 0x01; -} - - -/**************************************************************************** - add a netbios name into the namelist - **************************************************************************/ -static void add_name(struct subnet_record *d, struct name_record *n) -{ - struct name_record *n2; - - if (!d) return; - - if (!d->namelist) - { - d->namelist = n; - n->prev = NULL; - n->next = NULL; - return; - } - - for (n2 = d->namelist; n2->next; n2 = n2->next) ; - - n2->next = n; - n->next = NULL; - n->prev = n2; - - if((d == wins_client_subnet) && lp_wins_support()) - updatedlists = True; -} - - -/**************************************************************************** - remove a name from the namelist. The pointer must be an element just - retrieved - **************************************************************************/ -void remove_name(struct subnet_record *d, struct name_record *n) -{ - struct name_record *nlist; - if (!d) return; - - nlist = d->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; - - if(nlist == d->namelist) - d->namelist = nlist->next; - - if(nlist->ip_flgs != NULL) - free(nlist->ip_flgs); - free(nlist); - } - - if((d == wins_client_subnet) && lp_wins_support()) - updatedlists = True; -} - - -/**************************************************************************** - find a name in a subnet. - **************************************************************************/ -struct name_record *find_name_on_subnet(struct subnet_record *d, - struct nmb_name *name, BOOL self_only) -{ - struct name_record *n = d->namelist; - struct name_record *ret; - - for (ret = n; ret; ret = ret->next) - { - if (name_equal(&ret->name,name)) - { - /* self search: self names only */ - if (self_only && (ret->source != SELF)) - { - continue; - } - DEBUG(9,("find_name_on_subnet: on subnet %s - found name %s(%02x) source=%d\n", - inet_ntoa(d->bcast_ip), name->name, name->name_type, ret->source)); - return ret; - } - } - DEBUG(9,("find_name_on_subnet: on subnet %s - name %s(%02x) NOT FOUND\n", - inet_ntoa(d->bcast_ip), name->name, name->name_type)); - return NULL; -} - -/**************************************************************************** - dump a copy of the name table - **************************************************************************/ -void dump_names(void) -{ - struct name_record *n; - fstring fname, fnamenew; - time_t t = time(NULL); - - FILE *f; - - if(lp_wins_support() == False || wins_client_subnet == NULL) - return; - - fstrcpy(fname,lp_lockdir()); - trim_string(fname,NULL,"/"); - strcat(fname,"/"); - strcat(fname,WINS_LIST); - fstrcpy(fnamenew,fname); - strcat(fnamenew,"."); - - f = fopen(fnamenew,"w"); - - if (!f) - { - DEBUG(3,("Can't open %s - %s\n",fnamenew,strerror(errno))); - return; - } - - DEBUG(4,("Dump of WINS name table:\n")); - - for (n = wins_client_subnet->namelist; n; n = n->next) - { - int i; - - DEBUG(4,("%15s ", inet_ntoa(wins_client_subnet->bcast_ip))); - DEBUG(4,("%15s ", inet_ntoa(wins_client_subnet->mask_ip))); - DEBUG(4,("%-19s TTL=%ld ", - namestr(&n->name), - n->death_time?n->death_time-t:0)); - - for (i = 0; i < n->num_ips; i++) - { - DEBUG(4,("%15s NB=%2x source=%d", - inet_ntoa(n->ip_flgs[i].ip), - n->ip_flgs[i].nb_flags,n->source)); - - } - DEBUG(4,("\n")); - - if (f && ((n->source == REGISTER) || (n->source == SELF))) - { - /* XXXX i have little imagination as to how to output nb_flags as - anything other than as a hexadecimal number :-) */ - - fprintf(f, "%s#%02x %ld ", - n->name.name,n->name.name_type, /* XXXX ignore scope for now */ - n->death_time); - - for (i = 0; i < n->num_ips; i++) - { - fprintf(f, "%s %2x%c ", - inet_ntoa(n->ip_flgs[i].ip), - n->ip_flgs[i].nb_flags, (n->source == REGISTER ? 'R' : 'S')); - } - fprintf(f, "\n"); - } - - } - - fclose(f); - unlink(fname); - chmod(fnamenew,0644); - rename(fnamenew,fname); - - DEBUG(3,("Wrote wins database %s\n",fname)); -} - - -/**************************************************************************** - load a netbios name database file - - XXXX we cannot cope with loading Internet Group names, yet - ****************************************************************************/ -void load_netbios_names(void) -{ - struct subnet_record *d = wins_client_subnet; - fstring fname; - - FILE *f; - pstring line; - - if (!d) return; - - fstrcpy(fname,lp_lockdir()); - trim_string(fname,NULL,"/"); - strcat(fname,"/"); - strcat(fname,WINS_LIST); - - f = fopen(fname,"r"); - - if (!f) { - DEBUG(2,("Can't open wins database file %s\n",fname)); - return; - } - - while (!feof(f)) - { - pstring name_str, ip_str, ttd_str, nb_flags_str; - - pstring name; - int type = 0; - unsigned int nb_flags; - time_t ttd; - struct in_addr ipaddr; - - enum name_source source; - - char *ptr; - int count = 0; - - char *p; - - if (!fgets_slash(line,sizeof(pstring),f)) continue; - - if (*line == '#') continue; - - ptr = line; - - if (next_token(&ptr,name_str ,NULL)) ++count; - if (next_token(&ptr,ttd_str ,NULL)) ++count; - if (next_token(&ptr,ip_str ,NULL)) ++count; - if (next_token(&ptr,nb_flags_str,NULL)) ++count; - - if (count <= 0) continue; - - if (count != 4) { - DEBUG(0,("Ill formed wins line")); - DEBUG(0,("[%s]: name#type abs_time ip nb_flags\n",line)); - continue; - } - - /* Deal with SELF or REGISTER name encoding. Default is REGISTER - for compatibility with old nmbds. */ - if(nb_flags_str[strlen(nb_flags_str)-1] == 'S') - { - DEBUG(5,("Ignoring SELF name %s\n", line)); - continue; - } - - if(nb_flags_str[strlen(nb_flags_str)-1] == 'R') - nb_flags_str[strlen(nb_flags_str)-1] = '\0'; - - /* netbios name. # divides the name from the type (hex): netbios#xx */ - pstrcpy(name,name_str); - - p = strchr(name,'#'); - - if (p) { - *p = 0; - sscanf(p+1,"%x",&type); - } - - /* decode the netbios flags (hex) and the time-to-die (seconds) */ - sscanf(nb_flags_str,"%x",&nb_flags); - sscanf(ttd_str,"%ld",&ttd); - - ipaddr = *interpret_addr2(ip_str); - - if (ip_equal(ipaddr,ipzero)) { - source = SELF; - } - else - { - source = REGISTER; - } - - DEBUG(4, ("add WINS line: %s#%02x %ld %s %2x\n", - name,type, ttd, inet_ntoa(ipaddr), nb_flags)); - - /* add all entries that have 60 seconds or more to live */ - if (ttd - 60 > time(NULL) || ttd == 0) - { - time_t t = (ttd?ttd-time(NULL):0) / 3; - - /* add netbios entry read from the wins.dat file. IF it's ok */ - add_netbios_entry(d,name,type,nb_flags,t,source,ipaddr,True); - } - } - - fclose(f); -} - - -/**************************************************************************** - remove an entry from the name list - ****************************************************************************/ -void remove_netbios_name(struct subnet_record *d, - char *name,int type, enum name_source source) -{ - struct nmb_name nn; - struct name_record *n; - - make_nmb_name(&nn, name, type, scope); - n = find_name_on_subnet(d, &nn, FIND_ANY_NAME); - - if (n && n->source == source) remove_name(d,n); -} - - -/**************************************************************************** - add an entry to the name list. - - this is a multi-purpose function. - - it adds samba's own names in to its records on each interface, keeping a - record of whether it is a master browser, domain master, or WINS server. - - it also keeps a record of WINS entries. - - ****************************************************************************/ -struct name_record *add_netbios_entry(struct subnet_record *d, - char *name, int type, int nb_flags, int ttl, - enum name_source source, struct in_addr ip, BOOL new_only) -{ - struct name_record *n; - struct name_record *n2=NULL; - BOOL self = (source == SELF) ? FIND_SELF_NAME : FIND_ANY_NAME; - /* It's a WINS add if we're adding to the wins_client_subnet. */ - BOOL wins = ( wins_client_subnet && (d == wins_client_subnet)); - - if(d == NULL) - { - DEBUG(0,("add_netbios_entry: called with NULL subnet record. This is a bug - \ -please report this.!\n")); - return NULL; - } - - if (!self) - { - if (!wins && (type != 0x1b)) - { - /* the only broadcast (non-WINS) names we are adding are ours - (SELF) and Domain Master type names */ - return NULL; - } - if(wins && (type == 0x1d)) - { - /* Do not allow any 0x1d names to be registered in a WINS, - database although we return success for them. - */ - return NULL; - } - } - - n = (struct name_record *)malloc(sizeof(*n)); - if (!n) return(NULL); - - bzero((char *)n,sizeof(*n)); - - n->num_ips = 1; /* XXXX ONLY USE THIS FUNCTION FOR ONE ENTRY */ - n->ip_flgs = (struct nmb_ip*)malloc(sizeof(*n->ip_flgs) * n->num_ips); - if (!n->ip_flgs) - { - free(n); - return NULL; - } - - bzero((char *)n->ip_flgs, sizeof(*n->ip_flgs) * n->num_ips); - - make_nmb_name(&n->name,name,type,scope); - - if ((n2 = find_name_on_subnet(d, &n->name, self))) - { - free(n->ip_flgs); - free(n); - if (new_only || (n2->source==SELF && source!=SELF)) return n2; - n = n2; - } - - if (ttl) - n->death_time = time(NULL)+ttl*3; - n->refresh_time = time(NULL)+GET_TTL(ttl); - - /* XXXX only one entry expected with this function */ - n->ip_flgs[0].ip = ip; - n->ip_flgs[0].nb_flags = nb_flags; - - n->source = source; - - if (!n2) add_name(d,n); - - DEBUG(3,("Added netbios name %s at %s ttl=%d nb_flags=%2x to interface %s\n", - namestr(&n->name),inet_ntoa(ip),ttl,nb_flags, - wins ? "WINS" : (char *)inet_ntoa(d->bcast_ip))); - - return(n); -} - - -/******************************************************************* - expires old names in the namelist - ******************************************************************/ -void expire_names(time_t t) -{ - struct name_record *n; - struct name_record *next; - struct subnet_record *d; - - /* expire old names */ - for (d = FIRST_SUBNET; d; d = NEXT_SUBNET_INCLUDING_WINS(d)) - { - for (n = d->namelist; n; n = next) - { - next = n->next; - if (n->death_time && n->death_time < t) - { - if (n->source == SELF) - { - DEBUG(3,("not expiring SELF name %s\n", namestr(&n->name))); - n->death_time += 300; - continue; - } - DEBUG(3,("Removing dead name %s\n", namestr(&n->name))); - - if (n->prev) n->prev->next = n->next; - if (n->next) n->next->prev = n->prev; - - if (d->namelist == n) d->namelist = n->next; - - if(n->ip_flgs != NULL) - free(n->ip_flgs); - free(n); - } - } - } -} - - diff --git a/source3/namedbname.doc b/source3/namedbname.doc deleted file mode 100644 index 34a791dbb8..0000000000 --- a/source3/namedbname.doc +++ /dev/null @@ -1,182 +0,0 @@ -/* - Unix SMB/Netbios documentation. - Version 0.0 - Copyright (C) Luke Leighton Andrew Tridgell 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. - - Document name: namedbname.doc - - Revision History: - - 0.0 - 02jul96 : lkcl@pires.co.uk - created -*/ - -this module deals with the NetBIOS name database for samba. it deals -directly with adding, removing, finding, loading and saving of names. - -/************************************************************************* - search_for_name() - *************************************************************************/ - -this function is responsible for finding a name in the appropriate part -of samba's NetBIOS name database. if the name cannot be found, then it -should look the name up using DNS. later modifications will be to -forward the request on to another WINS server, should samba not be able -to find out about the requested name (this will be implemented through -issuing a new type of samba 'state'). - -the name is first searched for in the NetBIOS cache. if it cannot be -found, then it if the name looks like it's a server-type name (0x20 -0x0 or 0x1b) then DNS is used to look for the name. - -if DNS fails, then a record of this failure is kept. if it succeeds, then -a new NetBIOS entry is added. - -the successfully found name is returned. on failure, NULL is returned. - - -/************************************************************************* - expire_names() - *************************************************************************/ - -this function is responsible for removing old NetBIOS names from its -database. no further action is required. - -for over-zealous WINS systems, the use of query_refresh_names() is -recommended. this function initiates polling of hosts that have -registered with samba in its capacity as a WINS server. an alternative -means to achieve the same end as query_refresh_names() is to -reduce the time to live when the name is registered with samba, -except that in this instance the responsibility for refreshing the -name is with the owner of the name, not the server with which the name -is registered. - - -/************************************************************************* - add_netbios_entry() - *************************************************************************/ - -this function is responsible for adding or updating a NetBIOS name -in the database. into the local interface records, the only names -that will be added are those of domain master browsers and -samba's own names. into the WINS records, all names are added. - -the name to be added / updated will be looked up in the records. -if it is found, then we will not overwrite the entry if the flag -'newonly' is True, or if the name is being added as a non-SELF -(non-samba) name and the records indicate that samba owns the -name. - -otherwise, the name is added or updated with the new details. - - -/************************************************************************* - remove_netbios_entry() - *************************************************************************/ - -this function is responsible for removing a NetBIOS entry from -the database. the name is searched for in the records using -find_name_search(). if the ip is zero, then the ip is ignored. - -the name is removed if the expected source (e.g SELF, REGISTER) -matches that in the database. - - -/************************************************************************* - load_netbios_names() - *************************************************************************/ - -this function is responsible for loading any NetBIOS names that samba, -in its WINS capacity, has written out to disk. all the relevant details -are recorded in this file, including the time-to-live. should the -time left to live be small, the name is not added back in to samba's -WINS database. - - -/************************************************************************* - dump_names() - *************************************************************************/ - -this function is responsible for outputting NetBIOS names in two formats. -firstly, as debugging information, and secondly, all names that have been -registered with samba in its capacity as a WINS server are written to -disk. - -writing all WINS names allows two things. firstly, if samba's NetBIOS -daemon dies or is terminated, on restarting the daemon most if not all -of the registered WINS names will be preserved (which is a good reason -why query_netbios_names() should be used). - - -/************************************************************************* - find_name_search() - *************************************************************************/ - -this function is a wrapper around find_name(). find_name_search() can -be told whether to search for the name in a local subnet structure or -in the WINS database. on top of this, it can be told to search only -for samba's SELF names. - -if it finds the name in the WINS database, it will set the subnet_record -and also return the name it finds. - - -/************************************************************************* - find_name() - *************************************************************************/ - -this function is a low-level search function that searches a single -interface's NetBIOS records for a name. if the ip to be found is -zero then the ip address is ignored. this is to enable a name to -be found without knowing its ip address, and also to find the exact -name if a large number of group names are added with different ip -addresses. - - -/************************************************************************* - remove_name() - *************************************************************************/ - -this function is responsible for removing a specific NetBIOS entry -from a subnet list's records. only if the pointer to the entry is -in the list will the name be removed. - - -/************************************************************************* - add_name() - *************************************************************************/ - -this function is responsible for adding a NetBIOS entry into a -subnet list's records. - - -/************************************************************************* - ms_browser_name() - *************************************************************************/ - -this function returns True if the NetBIOS name passed to it is -^1^2__MSBROWSE__^2^1 - - -/************************************************************************* - name_equal() - *************************************************************************/ - -this function returns True if the two NetBIOS names passed to it -match in name, type and scope: the NetBIOS names are equal. - - diff --git a/source3/namedbresp.c b/source3/namedbresp.c deleted file mode 100644 index e9fe39c3d7..0000000000 --- a/source3/namedbresp.c +++ /dev/null @@ -1,167 +0,0 @@ -/* - Unix SMB/Netbios implementation. - Version 1.9. - NBT netbios library routines - 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 - 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. - - Module name: namedbresp.c - -*/ - -#include "includes.h" - -extern int ClientNMB; - -extern struct subnet_record *subnetlist; - -extern int DEBUGLEVEL; - -extern pstring scope; -extern pstring myname; -extern struct in_addr ipzero; - -int num_response_packets = 0; - -/*************************************************************************** - add an expected response record into the list - **************************************************************************/ -void add_response_record(struct subnet_record *d, - struct response_record *n) -{ - struct response_record *n2; - - if (!d) return; - - num_response_packets++; /* count of total number of packets still around */ - - DEBUG(4,("adding response record id:%d num_records:%d\n", - n->response_id, num_response_packets)); - - if (!d->responselist) - { - d->responselist = n; - n->prev = NULL; - n->next = NULL; - return; - } - - for (n2 = d->responselist; n2->next; n2 = n2->next) ; - - n2->next = n; - n->next = NULL; - n->prev = n2; -} - - -/*************************************************************************** - remove an expected response record from the list - **************************************************************************/ -void remove_response_record(struct subnet_record *d, - struct response_record *n) -{ - if (!d) return; - - if (n->prev) n->prev->next = n->next; - if (n->next) n->next->prev = n->prev; - - if (d->responselist == n) d->responselist = n->next; - - free(n); - - num_response_packets--; /* count of total number of packets still around */ -} - - -/**************************************************************************** - create a name query response record - **************************************************************************/ -struct response_record *make_response_queue_record(enum state_type state, - int id,uint16 fd, - int quest_type, char *name,int type, int nb_flags, time_t ttl, - int server_type, char *my_name, char *my_comment, - BOOL bcast,BOOL recurse, - struct in_addr send_ip, struct in_addr reply_to_ip, - int reply_id) -{ - struct response_record *n; - - if (!name || !name[0]) return NULL; - - if (!(n = (struct response_record *)malloc(sizeof(*n)))) - return(NULL); - - bzero((char *)n, sizeof(*n)); - - n->response_id = id; - n->state = state; - n->fd = fd; - n->quest_type = quest_type; - make_nmb_name(&n->name, name, type, scope); - n->nb_flags = nb_flags; - n->ttl = ttl; - n->server_type = server_type; - n->bcast = bcast; - n->recurse = recurse; - n->send_ip = send_ip; - n->reply_to_ip = reply_to_ip; - n->reply_id = reply_id; - if(my_name) - StrnCpy(n->my_name, my_name, sizeof(n->my_name)-1); - else - *n->my_name = 0; - if(my_comment) - StrnCpy(n->my_comment, my_comment, sizeof(n->my_comment)-1); - else - *n->my_comment = 0; - n->repeat_interval = 1; /* XXXX should be in ms */ - n->repeat_count = 3; /* 3 retries */ - n->repeat_time = time(NULL) + n->repeat_interval; /* initial retry time */ - - n->num_msgs = 0; - - return n; -} - - -/**************************************************************************** - find a response in a subnet's name query response list. - **************************************************************************/ -struct response_record *find_response_record(struct subnet_record **d, - uint16 id) -{ - struct response_record *n; - - if (!d) return NULL; - - for ((*d) = FIRST_SUBNET; (*d); (*d) = NEXT_SUBNET_INCLUDING_WINS(*d)) - { - for (n = (*d)->responselist; n; n = n->next) - { - if (n->response_id == id) { - DEBUG(4, ("found response record on %s: %d\n", - inet_ntoa((*d)->bcast_ip), id)); - return n; - } - } - } - - *d = NULL; - - return NULL; -} - - diff --git a/source3/namedbresp.doc b/source3/namedbresp.doc deleted file mode 100644 index a54c070275..0000000000 --- a/source3/namedbresp.doc +++ /dev/null @@ -1,100 +0,0 @@ -/* - Unix SMB/Netbios documentation. - Version 0.1 - Copyright (C) Luke Leighton Andrew Tridgell 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. - - Document name: namedbresp.doc - - Revision History: - - 0.0 - 02jul96 : lkcl@pires.co.uk - created - - 0.1 - 22jul96 Andrew.Tridgell@anu.edu.au - tridge's comments on first revision -*/ - -module namedbresp deals with the maintenance of the list of expected -responses - creating, finding and removal. - -module nameresp deals with the initial transmission, re-transmission -and time-out of netbios response records. - - -/************************************************************************* - find_response_record() - *************************************************************************/ - -this function is responsible for matching the unique response transaction -id with an expected response record. as a side-effect of this search, -it will find the subnet (or the WINS pseudo-subnet) that samba expected -the response to come from. - - -/************************************************************************* - make_response_queue_record() - *************************************************************************/ - -this function is responsible for creating a response record, which will -be queued awaiting a response. - -the number of retries is set to 3, and the retry period set to 1 second. -if no response is received, then the packet is re-transmitted, which is -why so much information is stored in the response record. - -the number of expected responses queued is kept, so listen_for_packets() -knows it must time-out after 1 second if one or more responses are -expected. - - -/************************************************************************* - remove_response_record() - *************************************************************************/ - -this function is responsible for removing a response record from the -expected response queue. the number of expected responses is decreased. - - -/************************************************************************* - add_response_record() - *************************************************************************/ - -this function is responsible for adding the response record created by -make_response_queue_record() into the appropriate response record queue. - - ------------------ -NOTE FROM TRIDGE: - -namedbresp.c is interesting because it implements a novel way of -getting most of the advantages of a multi-threaded nmbd daemon without -the portability problems. - -The NBT specs (rfc1001/1002) talk about the 16 bit IDs in the packets -as being used to ensure that packets are unique, and to stop packets -from being confused. It suggests incrementing the ID by 1 each time. - -Instead Luke uses these IDs to identify individual threads of control -in nmbd. So when nmbd sends out a NBT packet as part of some complex -processing, it adds to a linked list the information required to -continue the processing when the reply comes in (or it times -out). When a reply arrives this list can be searched to find the -matching query and the next step in the processing can be carried out. - -This is really good stuff, and allows for much more complex behaviour -than was possible with the old nmbd. ----------------- diff --git a/source3/namedbserver.c b/source3/namedbserver.c deleted file mode 100644 index c3f6076f49..0000000000 --- a/source3/namedbserver.c +++ /dev/null @@ -1,207 +0,0 @@ -/* - Unix SMB/Netbios implementation. - Version 1.9. - NBT netbios routines and daemon - version 2 - 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 - 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 - - 04 jul 96: lkcl@pires.co.uk - created module namedbserver containing server database functions - -*/ - -#include "includes.h" -#include "smb.h" - -extern int ClientNMB; - -extern int DEBUGLEVEL; - -extern pstring myname; -extern fstring myworkgroup; - -/* this is our domain/workgroup/server database */ -extern struct subnet_record *subnetlist; - -extern BOOL updatedlists; - - -/******************************************************************* - expire old servers in the serverlist - time of -1 indicates everybody dies except those with time of 0 - remove_all_servers indicates everybody dies. - ******************************************************************/ -void remove_old_servers(struct work_record *work, time_t t, - BOOL remove_all) -{ - struct server_record *s; - struct server_record *nexts; - - /* expire old entries in the serverlist */ - for (s = work->serverlist; s; s = nexts) - { - nexts = s->next; - if (remove_all || - (s->death_time && (t == -1 || s->death_time < t))) { - DEBUG(3,("Removing dead server %s\n",s->serv.name)); - updatedlists = True; - - if (s->prev) s->prev->next = s->next; - if (s->next) s->next->prev = s->prev; - - if (work->serverlist == s) - work->serverlist = s->next; - - free(s); - } - } -} - - -/*************************************************************************** - add a server into the list - **************************************************************************/ -static void add_server(struct work_record *work,struct server_record *s) -{ - struct server_record *s2; - - if (!work->serverlist) { - work->serverlist = s; - s->prev = NULL; - s->next = NULL; - return; - } - - for (s2 = work->serverlist; s2->next; s2 = s2->next) ; - - s2->next = s; - s->next = NULL; - s->prev = s2; -} - - -/**************************************************************************** - find a server in a server list. - **************************************************************************/ -struct server_record *find_server(struct work_record *work, char *name) -{ - struct server_record *ret; - - if (!work) return NULL; - - for (ret = work->serverlist; ret; ret = ret->next) - { - if (strequal(ret->serv.name,name)) - { - return ret; - } - } - return NULL; -} - - -/**************************************************************************** - add a server entry - ****************************************************************************/ -struct server_record *add_server_entry(struct subnet_record *d, - struct work_record *work, - char *name,int servertype, - int ttl,char *comment, - BOOL replace) -{ - BOOL newentry=False; - struct server_record *s; - - if (name[0] == '*') - { - return (NULL); - } - - s = find_server(work, name); - - if (s && !replace) - { - DEBUG(4,("Not replacing %s\n",name)); - return(s); - } - - if (!s || s->serv.type != servertype || !strequal(s->serv.comment, comment)) - 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->serv.name,name,sizeof(s->serv.name)-1); - StrnCpy(s->serv.comment,comment,sizeof(s->serv.comment)-1); - strupper(s->serv.name); - s->serv.type = servertype; - s->death_time = servertype ? (ttl?time(NULL)+ttl*3:0) : (time(NULL)-1); - - /* for a domain entry, the comment field refers to the server name */ - - if (s->serv.type & SV_TYPE_DOMAIN_ENUM) strupper(s->serv.comment); - - if (newentry) - { - add_server(work, s); - - DEBUG(3,("Added ")); - } - else - { - DEBUG(3,("Updated ")); - } - - DEBUG(3,("server entry %s of type %x (%s) to %s %s\n", - name,servertype,comment, - work->work_group,inet_ntoa(d->bcast_ip))); - - return(s); -} - - -/******************************************************************* - expire old servers in the serverlist - ******************************************************************/ -void expire_servers(time_t t) -{ - struct subnet_record *d; - - for (d = FIRST_SUBNET; d ; d = NEXT_SUBNET_INCLUDING_WINS(d)) - { - struct work_record *work; - - for (work = d->workgrouplist; work; work = work->next) - { - remove_old_servers(work, t, False); - } - } -} - diff --git a/source3/namedbsubnet.c b/source3/namedbsubnet.c deleted file mode 100644 index 816203ea3c..0000000000 --- a/source3/namedbsubnet.c +++ /dev/null @@ -1,400 +0,0 @@ -/* - Unix SMB/Netbios implementation. - Version 1.9. - NBT netbios routines and daemon - version 2 - 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 - 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 - - 04 jul 96: lkcl@pires.co.uk - created module namedbsubnet containing subnet database functions - -*/ - -#include "includes.h" -#include "smb.h" - -extern int ClientNMB; -extern int ClientDGRAM; -extern int global_nmb_port; - -extern int DEBUGLEVEL; - -extern struct in_addr wins_ip; -extern struct in_addr ipzero; - -extern pstring myname; -extern fstring myworkgroup; -extern char **my_netbios_names; - -BOOL updatedlists = True; -int updatecount = 0; - -/* local interfaces structure */ -extern struct interface *local_interfaces; - -/* this is our domain/workgroup/server database */ -struct subnet_record *subnetlist = NULL; - -/* WINS subnet - keep this separate so enumeration code doesn't - run onto it by mistake. */ -struct subnet_record *wins_client_subnet = NULL; - -extern uint16 nb_type; /* samba's NetBIOS name type */ - -/**************************************************************************** - add a domain into the list - **************************************************************************/ -static void add_subnet(struct subnet_record *d) -{ - struct subnet_record *d2; - - if (!subnetlist) - { - subnetlist = d; - d->prev = NULL; - d->next = NULL; - return; - } - - for (d2 = subnetlist; d2->next; d2 = d2->next); - - d2->next = d; - d->next = NULL; - d->prev = d2; -} - - -/**************************************************************************** - find a subnet in the subnetlist that a given IP address could - match - not including WINS. Returns NULL if no match. - **************************************************************************/ -struct subnet_record *find_subnet(struct in_addr ip) -{ - struct subnet_record *d = NULL; - - /* search through subnet list for broadcast/netmask that matches - the source ip address. */ - - for (d = FIRST_SUBNET; d; d = NEXT_SUBNET_EXCLUDING_WINS(d)) - { - if (same_net(ip, d->bcast_ip, d->mask_ip)) - break; - } - - return d; -} - -/**************************************************************************** - find a subnet in the subnetlist - if the subnet is not found - then return the WINS client subnet. - **************************************************************************/ -struct subnet_record *find_subnet_all(struct in_addr ip) -{ - struct subnet_record *d = find_subnet(ip); - if(!d) - return wins_client_subnet; - return d; -} - -/**************************************************************************** - create a subnet entry - ****************************************************************************/ -static struct subnet_record *make_subnet(struct in_addr myip, struct in_addr bcast_ip, - struct in_addr mask_ip, BOOL add) -{ - struct subnet_record *d = NULL; - int nmb_sock, dgram_sock; - - /* Check if we are creating the WINS subnet - if so don't create - sockets, use the ClientNMB and ClientDGRAM sockets instead. - */ - - if(ip_equal(bcast_ip, wins_ip)) - { - nmb_sock = -1; - dgram_sock = -1; - } - else - { - /* - * Attempt to open the sockets on port 137/138 for this interface - * and bind them. - * Fail the subnet creation if this fails. - */ - - if((nmb_sock = open_socket_in(SOCK_DGRAM, global_nmb_port,0, myip.s_addr)) == -1) - { - DEBUG(0,("make_subnet: Failed to open nmb socket on interface %s \ -for port %d. Error was %s\n", inet_ntoa(myip), global_nmb_port, strerror(errno))); - return NULL; - } - - if((dgram_sock = open_socket_in(SOCK_DGRAM,DGRAM_PORT,3, myip.s_addr)) == -1) - { - DEBUG(0,("make_subnet: Failed to open dgram socket on interface %s \ -for port %d. Error was %s\n", inet_ntoa(myip), DGRAM_PORT, strerror(errno))); - return NULL; - } - - /* Make sure we can broadcast from these sockets. */ - set_socket_options(nmb_sock,"SO_BROADCAST"); - set_socket_options(dgram_sock,"SO_BROADCAST"); - - } - - d = (struct subnet_record *)malloc(sizeof(*d)); - - if (!d) - { - DEBUG(0,("make_subnet: malloc fail !\n")); - close(nmb_sock); - close(dgram_sock); - return(NULL); - } - - bzero((char *)d,sizeof(*d)); - - DEBUG(4, ("making subnet %s ", inet_ntoa(bcast_ip))); - DEBUG(4, ("%s\n", inet_ntoa(mask_ip))); - - d->bcast_ip = bcast_ip; - d->mask_ip = mask_ip; - d->myip = myip; - d->nmb_sock = nmb_sock; - d->dgram_sock = dgram_sock; - d->workgrouplist = NULL; - - if(add) - add_subnet(d); - - return d; -} - -/**************************************************************************** - add a domain entry. creates a workgroup, if necessary, and adds the domain - to the named a workgroup. - ****************************************************************************/ -static struct subnet_record *add_subnet_entry(struct in_addr myip, - struct in_addr bcast_ip, - struct in_addr mask_ip, char *name, - BOOL create_subnets, BOOL add) -{ - struct subnet_record *d = NULL; - - if (zero_ip(bcast_ip)) - bcast_ip = *iface_bcast(bcast_ip); - - /* Note that we should also add into the WINS subnet as add_subnet_entry - should be called to add NetBIOS names and server entries on all - interfaces, including the WINS interface - */ - - if(create_subnets == True) - { - /* Create new subnets. */ - if((d = make_subnet(myip, bcast_ip, mask_ip, add)) == NULL) - { - DEBUG(0,("add_subnet_entry: Unable to create subnet %s\n", - inet_ntoa(bcast_ip) )); - return NULL; - } - return d; - } - if(ip_equal(bcast_ip, wins_ip)) - return wins_client_subnet; - return find_subnet(bcast_ip); -} - -/**************************************************************************** - Add a workgroup into a subnet, and if it's our primary workgroup, - add the required names to it. -**************************************************************************/ - -void add_workgroup_to_subnet( struct subnet_record *d, char *group) -{ - struct work_record *w = NULL; - - DEBUG(5,("add_workgroup_to_subnet: Adding workgroup %s to subnet %s\n", - group, inet_ntoa(d->bcast_ip))); - - /* This next statement creates the workgroup struct if it doesn't - already exist. - */ - if((w = find_workgroupstruct(d, group, True)) == NULL) - { - DEBUG(0,("add_workgroup_to_subnet: Unable to add workgroup %s to subnet %s\n", - group, inet_ntoa(d->bcast_ip) )); - return; - } - - /* add WORKGROUP(00) entries into name database - or register with WINS server, if it's our workgroup. - */ - if (strequal(myworkgroup, group)) - { - int n; - - add_my_name_entry(d,group,0x0 ,nb_type|NB_ACTIVE|NB_GROUP); - - /* Register the WORKGROUP<0x1e> name. */ - add_my_name_entry(d,group,0x1e,nb_type|NB_ACTIVE|NB_GROUP); - - /* Add all our server names to the workgroup list. We remove any - browser or logon server flags from all but the primary name. - */ - for( n = 0; my_netbios_names[n]; n++) - { - char *name = my_netbios_names[n]; - int stype = w->ServerType; - - if(!strequal(myname, name)) - stype &= ~(SV_TYPE_MASTER_BROWSER|SV_TYPE_POTENTIAL_BROWSER| - SV_TYPE_DOMAIN_MASTER|SV_TYPE_DOMAIN_MEMBER); - - add_server_entry(d,w,name,stype|SV_TYPE_LOCAL_LIST_ONLY,0, - lp_serverstring(),True); - DEBUG(3,("add_workgroup_to_subnet: Added server name entry %s \ -to subnet %s\n", name, inet_ntoa(d->bcast_ip))); - } - } -} - -/**************************************************************************** - create subnet / workgroup / server entries - - - add or create the subnet lists - - add or create the workgroup entries in each subnet entry - - register appropriate NetBIOS names for the workgroup entries - -**************************************************************************/ -void add_my_subnets(char *group) -{ - static BOOL create_subnets = True; - struct subnet_record *d = NULL; - struct interface *i = NULL; - - if (*group == '*') return; - - /* Create subnets from all the local interfaces and thread them onto - the linked list. - */ - for (i = local_interfaces; i; i = i->next) - { - add_subnet_entry(i->ip, i->bcast,i->nmask,group, create_subnets, True); - } - - /* If we are using WINS, then we must add the workgroup to the WINS - subnet. This is used as a place to keep collated server lists. - */ - - /* Create the WINS subnet if we are using WINS - but don't thread it - onto the linked subnet list. - */ - if (lp_wins_support() || lp_wins_server()) - { - struct in_addr wins_nmask = ipzero; - wins_client_subnet = add_subnet_entry(ipzero, wins_ip, wins_nmask, group, create_subnets, False); - } - - /* Ensure we only create the subnets once. */ - create_subnets = False; - - /* Now we have created all the subnets - we can add the names - that make us a client member in the workgroup. - */ - for (d = FIRST_SUBNET; d; d = NEXT_SUBNET_INCLUDING_WINS(d)) - add_workgroup_to_subnet(d, group); -} - -/******************************************************************* - write out browse.dat - ******************************************************************/ -void write_browse_list(time_t t) -{ - struct subnet_record *d; - pstring fname,fnamenew; - FILE *f; - - static time_t lasttime = 0; - - if (!lasttime) lasttime = t; - if (!updatedlists || t - lasttime < 5) return; - - lasttime = t; - updatedlists = False; - updatecount++; - - dump_names(); - dump_workgroups(); - - pstrcpy(fname,lp_lockdir()); - trim_string(fname,NULL,"/"); - strcat(fname,"/"); - strcat(fname,SERVER_LIST); - pstrcpy(fnamenew,fname); - strcat(fnamenew,"."); - - f = fopen(fnamenew,"w"); - - if (!f) - { - DEBUG(4,("Can't open %s - %s\n",fnamenew,strerror(errno))); - return; - } - - for (d = FIRST_SUBNET; d ; d = NEXT_SUBNET_INCLUDING_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) - { - fstring tmp; - - /* don't list domains I don't have a master for */ - if ((s->serv.type & SV_TYPE_DOMAIN_ENUM) && !s->serv.comment[0]) - { - continue; - } - - /* output server details, plus what workgroup/domain - they're in. without the domain information, the - combined list of all servers in all workgroups gets - sent to anyone asking about any workgroup! */ - - sprintf(tmp, "\"%s\"", s->serv.name); - fprintf(f, "%-25s ", tmp); - fprintf(f, "%08x ", s->serv.type); - sprintf(tmp, "\"%s\" ", s->serv.comment); - fprintf(f, "%-30s", tmp); - fprintf(f, "\"%s\"\n", work->work_group); - } - } - } - - fclose(f); - unlink(fname); - chmod(fnamenew,0644); - rename(fnamenew,fname); - DEBUG(3,("Wrote browse list %s\n",fname)); -} - diff --git a/source3/namedbwork.c b/source3/namedbwork.c deleted file mode 100644 index b01eb927e8..0000000000 --- a/source3/namedbwork.c +++ /dev/null @@ -1,248 +0,0 @@ -/* - Unix SMB/Netbios implementation. - Version 1.9. - NBT netbios routines and daemon - version 2 - 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 - 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 - - 04 jul 96: lkcl@pires.co.uk - created module namedbwork containing workgroup database functions - -*/ - -#include "includes.h" -#include "smb.h" - -extern int ClientNMB; - -extern int DEBUGLEVEL; - -/* this is our domain/workgroup/server database */ -extern struct subnet_record *subnetlist; - -extern struct in_addr wins_ip; - -extern fstring myworkgroup; - -int workgroup_count = 0; /* unique index key: one for each workgroup */ - - - -/**************************************************************************** - add a workgroup into the domain list - **************************************************************************/ -static void add_workgroup(struct work_record *work, struct subnet_record *d) -{ - struct work_record *w2; - - if (!work || !d) return; - - if (!d->workgrouplist) - { - d->workgrouplist = work; - work->prev = NULL; - work->next = NULL; - return; - } - - for (w2 = d->workgrouplist; w2->next; w2 = w2->next); - - w2->next = work; - work->next = NULL; - work->prev = w2; -} - - -/**************************************************************************** - create a blank workgroup - **************************************************************************/ -static struct work_record *make_workgroup(char *name) -{ - struct work_record *work; - struct subnet_record *d; - int t = -1; - - if (!name || !name[0]) return NULL; - - work = (struct work_record *)malloc(sizeof(*work)); - if (!work) return(NULL); - bzero((char *)work, sizeof(*work)); - - StrnCpy(work->work_group,name,sizeof(work->work_group)-1); - work->serverlist = NULL; - - /* set up initial value for server announce type */ - work->ServerType = lp_default_server_announce(); - work->ServerType |= lp_local_master() ? SV_TYPE_POTENTIAL_BROWSER : 0; - work->ServerType |= lp_domain_controller() ? SV_TYPE_DOMAIN_CTRL : 0; - - work->RunningElection = False; - work->ElectionCount = 0; - work->announce_interval = 0; - work->needelection = False; - work->needannounce = True; - work->mst_state = MST_POTENTIAL; - work->dom_state = DOMAIN_NONE; - work->log_state = LOGON_NONE; - - /* make sure all token representations of workgroups are unique */ - - for (d = FIRST_SUBNET; d && t == -1; d = NEXT_SUBNET_INCLUDING_WINS(d)) - { - struct work_record *w; - for (w = d->workgrouplist; w && t == -1; w = w->next) - { - if (strequal(w->work_group, work->work_group)) t = w->token; - } - } - - if (t == -1) - { - work->token = ++workgroup_count; - } - else - { - work->token = t; - } - - - /* WfWg uses 01040b01 */ - /* Win95 uses 01041501 */ - /* NTAS uses ???????? */ - work->ElectionCriterion = (MAINTAIN_LIST)|(ELECTION_VERSION<<8); - work->ElectionCriterion |= (lp_os_level() << 24); - if (lp_domain_master()) { - work->ElectionCriterion |= 0x80; - } - - return work; -} - - -/******************************************************************* - remove workgroups - ******************************************************************/ -struct work_record *remove_workgroup(struct subnet_record *d, - struct work_record *work, - BOOL remove_all_servers) -{ - struct work_record *ret_work = NULL; - - if (!d || !work) return NULL; - - DEBUG(3,("Removing old workgroup %s\n", work->work_group)); - - ret_work = work->next; - - remove_old_servers(work, -1, remove_all_servers); - - if (!work->serverlist) - { - if (work->prev) work->prev->next = work->next; - if (work->next) work->next->prev = work->prev; - - if (d->workgrouplist == work) d->workgrouplist = work->next; - - free(work); - } - - return ret_work; -} - - -/**************************************************************************** - find a workgroup in the workgrouplist - only create it if the domain allows it, or the parameter 'add' insists - that it get created/added anyway. this allows us to force entries in - lmhosts file to be added. - **************************************************************************/ -struct work_record *find_workgroupstruct(struct subnet_record *d, - fstring name, BOOL add) -{ - struct work_record *ret, *work; - - if (!d) return NULL; - - DEBUG(4, ("workgroup search for %s: ", name)); - - for (ret = d->workgrouplist; ret; ret = ret->next) { - if (!strcmp(ret->work_group,name)) { - DEBUG(4, ("found\n")); - return(ret); - } - } - - if (!add) { - DEBUG(4, ("not found\n")); - return NULL; - } - - DEBUG(4,("not found: creating\n")); - - if ((work = make_workgroup(name))) - { - if (!ip_equal(d->bcast_ip, wins_ip) && - lp_preferred_master() && lp_local_master() && - strequal(myworkgroup, name)) - { - DEBUG(3, ("preferred master startup for %s\n", work->work_group)); - work->needelection = True; - work->ElectionCriterion |= (1<<3); - } - add_workgroup(work, d); - return(work); - } - return NULL; -} - - -/**************************************************************************** - dump a copy of the workgroup/domain database - **************************************************************************/ -void dump_workgroups(void) -{ - struct subnet_record *d; - - for (d = FIRST_SUBNET; d; d = NEXT_SUBNET_INCLUDING_WINS(d)) - { - if (d->workgrouplist) - { - struct work_record *work; - - DEBUG(4,("dump domain bcast=%15s: ", inet_ntoa(d->bcast_ip))); - DEBUG(4,(" netmask=%15s:\n", inet_ntoa(d->mask_ip))); - - for (work = d->workgrouplist; work; work = work->next) - { - DEBUG(4,("\t%s(%d)\n", work->work_group, work->token)); - if (work->serverlist) - { - struct server_record *s; - for (s = work->serverlist; s; s = s->next) - { - DEBUG(4,("\t\t%s %8x (%s)\n", - s->serv.name, s->serv.type, s->serv.comment)); - } - } - } - } - } -} diff --git a/source3/nameelect.c b/source3/nameelect.c deleted file mode 100644 index b977741c5f..0000000000 --- a/source3/nameelect.c +++ /dev/null @@ -1,845 +0,0 @@ -/* - Unix SMB/Netbios implementation. - Version 1.9. - NBT netbios routines and daemon - version 2 - 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 - 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. - - Module name: nameelect.c - - Revision History: - - 14 jan 96: lkcl@pires.co.uk - added multiple workgroup domain master support - - 04 jul 96: lkcl@pires.co.uk - added system to become a master browser by stages. - - -*/ - -#include "includes.h" - -extern int ClientNMB; -extern int ClientDGRAM; - -extern int DEBUGLEVEL; -extern pstring scope; - -extern pstring myname; -extern fstring myworkgroup; -extern struct in_addr ipzero; -extern struct in_addr wins_ip; - -/* here are my election parameters */ - -extern time_t StartupTime; - -extern struct subnet_record *subnetlist; - -extern uint16 nb_type; /* samba's NetBIOS name type */ - - -/******************************************************************* - occasionally check to see if the master browser is around - ******************************************************************/ -void check_master_browser(time_t t) -{ - static time_t lastrun=0; - struct subnet_record *d; - - if (!lastrun) lastrun = t; - if (t < lastrun + CHECK_TIME_MST_BROWSE * 60) return; - - lastrun = t; - - dump_workgroups(); - - for (d = FIRST_SUBNET; d; d = NEXT_SUBNET_EXCLUDING_WINS(d)) - { - struct work_record *work; - - for (work = d->workgrouplist; work; work = work->next) - { - if (strequal(work->work_group, myworkgroup) && !AM_MASTER(work)) - { - if (lp_local_master() && lp_preferred_master()) - { - /* potential master browser - not a master browser. force - becoming a master browser, hence the log message. - */ - - DEBUG(2,("%s potential master for %s %s - force election\n", - timestring(), work->work_group, - inet_ntoa(d->bcast_ip))); - - browser_gone(work->work_group, d->bcast_ip); - } - else - { - /* if we are not the browse master of a workgroup, - and we can't find a browser on the subnet, do - something about it. - */ - - queue_netbios_packet(d,ClientNMB,NMB_QUERY,NAME_QUERY_MST_CHK, - work->work_group,0x1d,0,0,0,NULL,NULL, - True,False,d->bcast_ip,d->bcast_ip, 0); - } - } - } - } -} - - -/******************************************************************* - what to do if a master browser DOESN't exist. - - option 1: force an election, and participate in it - option 2: force an election, and let everyone else participate. - - ******************************************************************/ -void browser_gone(char *work_name, struct in_addr ip) -{ - struct subnet_record *d = find_subnet(ip); - struct work_record *work = find_workgroupstruct(d, work_name, False); - - /* i don't know about this workgroup, therefore i don't care */ - if (!work || !d) return; - - /* don't do election stuff on the WINS subnet */ - if (ip_equal(d->bcast_ip,wins_ip)) - return; - - if (strequal(work->work_group, myworkgroup)) - { - - if (lp_local_master()) - { - /* we have discovered that there is no local master - browser, and we are configured to initiate - an election under exactly such circumstances. - */ - DEBUG(2,("Forcing election on %s %s\n", - work->work_group,inet_ntoa(d->bcast_ip))); - - /* we can attempt to become master browser */ - work->needelection = True; - } - else - { - /* we need to force an election, because we are configured - not to _become_ the local master, but we still _need_ one, - having detected that one doesn't exist. - */ - - /* local interfaces: force an election */ - send_election(d, work->work_group, 0, 0, myname); - - /* only removes workgroup completely on a local interface - persistent lmhosts entries on a local interface _will_ be removed). - */ - remove_workgroup(d, work,True); - add_workgroup_to_subnet(d, work->work_group); - } - } -} - - -/**************************************************************************** - send an election packet - **************************************************************************/ -void send_election(struct subnet_record *d, char *group,uint32 criterion, - int timeup,char *name) -{ - pstring outbuf; - char *p; - - if (!d) return; - - DEBUG(2,("Sending election to %s for workgroup %s\n", - inet_ntoa(d->bcast_ip),group)); - - bzero(outbuf,sizeof(outbuf)); - p = outbuf; - CVAL(p,0) = ANN_Election; /* election */ - p++; - - CVAL(p,0) = (criterion == 0 && timeup == 0) ? 0 : ELECTION_VERSION; - SIVAL(p,1,criterion); - SIVAL(p,5,timeup*1000); /* ms - despite the spec */ - p += 13; - pstrcpy(p,name); - strupper(p); - p = skip_string(p,1); - - send_mailslot_reply(False,BROWSE_MAILSLOT,ClientDGRAM, - outbuf,PTR_DIFF(p,outbuf), - name,group,0,0x1e,d->bcast_ip,*iface_ip(d->bcast_ip)); -} - - -/**************************************************************************** - un-register a SELF name that got rejected. - - if this name happens to be rejected when samba is in the process - of becoming a master browser (registering __MSBROWSE__, WORKGROUP(1d) - or WORKGROUP(1b)) then we must stop being a master browser. sad. - - **************************************************************************/ -void name_unregister_work(struct subnet_record *d, char *name, int name_type) -{ - struct work_record *work; - int remove_type_local = 0; - int remove_type_domain = 0; - int remove_type_logon = 0; - - remove_netbios_name(d,name,name_type,SELF); - - if (!(work = find_workgroupstruct(d, name, False))) return; - - /* work out what to unbecome, from the name type being removed */ - - if (ms_browser_name(name, name_type)) - { - remove_type_local |= SV_TYPE_MASTER_BROWSER; - } - if (AM_MASTER(work) && strequal(name, myworkgroup) && name_type == 0x1d) - { - remove_type_local |= SV_TYPE_MASTER_BROWSER; - } - if (AM_DOMMST(work) && strequal(name, myworkgroup) && name_type == 0x1b) - { - remove_type_domain |= SV_TYPE_DOMAIN_MASTER; - } - if (AM_DOMMEM(work) && strequal(name, myworkgroup) && name_type == 0x1c) - { - remove_type_logon|= SV_TYPE_DOMAIN_MEMBER; - } - - if (remove_type_local ) unbecome_local_master (d, work, remove_type_local ); - if (remove_type_domain) unbecome_domain_master(d, work, remove_type_domain); - if (remove_type_logon ) unbecome_logon_server (d, work, remove_type_logon ); -} - - -/**************************************************************************** - registers a name. - - if the name being added is a SELF name, we must additionally check - whether to proceed to the next stage in samba becoming a master browser. - - **************************************************************************/ -void name_register_work(struct subnet_record *d, char *name, int name_type, - int nb_flags, time_t ttl, struct in_addr ip, BOOL bcast) -{ - enum name_source source = (ismyip(ip) || ip_equal(ip, ipzero)) ? - SELF : REGISTER; - - if (source == SELF) - { - struct work_record *work = find_workgroupstruct(d, - myworkgroup, False); - - struct subnet_record *add_subnet = (!bcast) ? wins_client_subnet : d; - add_netbios_entry(add_subnet,name,name_type,nb_flags,ttl,source,ip,True); - - if (work) - { - int add_type_local = False; - int add_type_domain = False; - int add_type_logon = False; - - DEBUG(4,("checking next stage: name_register_work %s\n", name)); - - /* work out what to become, from the name type being added */ - - if (ms_browser_name(name, name_type)) - { - add_type_local = True; - } - if (strequal(name, myworkgroup) && name_type == 0x1d) - { - add_type_local = True; - } - if (strequal(name, myworkgroup) && name_type == 0x1b) - { - add_type_domain = True; - } - if (strequal(name, myworkgroup) && name_type == 0x1c) - { - add_type_logon = True; - } - - if (add_type_local ) become_local_master (d, work); - if (add_type_domain) become_domain_master(d, work); - if (add_type_logon ) become_logon_server (d, work); - } - } -} - - -/******************************************************************* - become the local master browser. - - this is done in stages. note that this could take a while, - particularly on a broadcast subnet, as we have to wait for - the implicit registration of each name to be accepted. - - as each name is successfully registered, become_local_master() is - called again, in order to initiate the next stage. see - dead_netbios_entry() - deals with implicit name registration - and response_name_reg() - deals with explicit registration - with a WINS server. - - stage 1: was MST_POTENTIAL - go to MST_POTENTIAL and register ^1^2__MSBROWSE__^2^1. - stage 2: was MST_BACK - go to MST_MSB and register WORKGROUP(0x1d) - stage 3: was MST_MSB - go to MST_BROWSER and stay there - - XXXX note: this code still does not cope with the distinction - between different types of nodes, particularly between M and P - nodes. that comes later. - - ******************************************************************/ -void become_local_master(struct subnet_record *d, struct work_record *work) -{ - /* domain type must be limited to domain enum + server type. it must - not have SV_TYPE_SERVER or anything else with SERVER in it, else - clients get confused and start thinking this entry is a server - not a workgroup - */ - uint32 domain_type = SV_TYPE_DOMAIN_ENUM|SV_TYPE_NT; - - if (lp_domain_controller()) domain_type |= SV_TYPE_DOMAIN_CTRL; - - if (!work || !d) - return; - - if (!lp_local_master()) - { - DEBUG(0,("Samba not configured as a local master browser.\n")); - return; - } - - DEBUG(2,("Becoming master for %s %s (currently at stage %d)\n", - work->work_group,inet_ntoa(d->bcast_ip),work->mst_state)); - - switch (work->mst_state) - { - case MST_POTENTIAL: /* while we were nothing but a server... */ - { - DEBUG(3,("go to first stage: register ^1^2__MSBROWSE__^2^1\n")); - work->mst_state = MST_BACK; /* an election win was successful */ - - work->ElectionCriterion |= 0x5; - - /* update our server status */ - work->ServerType &= ~SV_TYPE_POTENTIAL_BROWSER; - add_server_entry(d,work,myname,work->ServerType|SV_TYPE_LOCAL_LIST_ONLY, - 0,lp_serverstring(),True); - - /* add special browser name */ - add_my_name_entry(d,MSBROWSE,0x01,nb_type|NB_ACTIVE|NB_GROUP); - - /* DON'T do anything else after calling add_my_name_entry() */ - break; - } - - case MST_BACK: /* while nothing had happened except we won an election... */ - { - DEBUG(3,("go to second stage: register as master browser\n")); - work->mst_state = MST_MSB; /* registering MSBROWSE was successful */ - - /* add server entry on successful registration of MSBROWSE */ - add_server_entry(d,work,work->work_group,domain_type|SV_TYPE_LOCAL_LIST_ONLY, - 0,myname,True); - - /* add master name */ - add_my_name_entry(d,work->work_group,0x1d,nb_type|NB_ACTIVE); - - /* DON'T do anything else after calling add_my_name_entry() */ - break; - } - - case MST_MSB: /* while we were still only registered MSBROWSE state... */ - { - int i = 0; - struct server_record *sl; - - DEBUG(3,("2nd stage complete: registered as master browser for workgroup %s \ -on subnet %s\n", work->work_group, inet_ntoa(d->bcast_ip))); - work->mst_state = MST_BROWSER; /* registering WORKGROUP(1d) succeeded */ - - /* update our server status */ - work->ServerType |= SV_TYPE_MASTER_BROWSER; - - DEBUG(3,("become_local_master: updating our server %s to type %x\n", - myname, work->ServerType)); - - add_server_entry(d,work,myname,work->ServerType|SV_TYPE_LOCAL_LIST_ONLY, - 0,lp_serverstring(),True); - - /* Count the number of servers we have on our list. If it's - less than 10 (just a heuristic) request the servers - to announce themselves. - */ - for( sl = work->serverlist; sl != NULL; sl = sl->next) - i++; - - if (i < 10) - { - /* ask all servers on our local net to announce to us */ - announce_request(work, d->bcast_ip); - } - - /* Reset the announce master timer so that we do an announce as soon as possible - now we are a master. */ - reset_announce_timer(); - - DEBUG(0,("Samba is now a local master browser for workgroup %s on subnet %s\n", - work->work_group, inet_ntoa(d->bcast_ip))); - - break; - } - - case MST_BROWSER: - { - /* don't have to do anything: just report success */ - DEBUG(3,("3rd stage: become master browser!\n")); - break; - } - } -} - - -/******************************************************************* - become the domain master browser. - - this is done in stages. note that this could take a while, - particularly on a broadcast subnet, as we have to wait for - the implicit registration of each name to be accepted. - - as each name is successfully registered, become_domain_master() is - called again, in order to initiate the next stage. see - dead_netbios_entry() - deals with implicit name registration - and response_name_reg() - deals with explicit registration - with a WINS server. - - stage 1: was DOMAIN_NONE - go to DOMAIN_MST - - XXXX note: this code still does not cope with the distinction - between different types of nodes, particularly between M and P - nodes. that comes later. - - ******************************************************************/ -void become_domain_master(struct subnet_record *d, struct work_record *work) -{ - /* domain type must be limited to domain enum + server type. it must - not have SV_TYPE_SERVER or anything else with SERVER in it, else - clients get confused and start thinking this entry is a server - not a workgroup - */ - - if (!work || !d) return; - - if (!lp_domain_master()) - { - DEBUG(0,("Samba not configured as a domain master browser.\n")); - return; - } - - DEBUG(2,("Becoming domain master for %s %s (currently at stage %d)\n", - work->work_group,inet_ntoa(d->bcast_ip),work->dom_state)); - - switch (work->dom_state) - { - case DOMAIN_NONE: /* while we were nothing but a server... */ - { - DEBUG(3,("become_domain_master: go to first stage: register <1b> name\n")); - work->dom_state = DOMAIN_WAIT; - - /* XXXX the 0x1b is domain master browser name */ - add_my_name_entry(d, work->work_group,0x1b,nb_type|NB_ACTIVE); - - /* DON'T do anything else after calling add_my_name_entry() */ - break; - } - - case DOMAIN_WAIT: - { - work->dom_state = DOMAIN_MST; /* ... become domain master */ - DEBUG(3,("become_domain_master: first stage - register as domain member\n")); - - /* update our server status */ - work->ServerType |= SV_TYPE_NT|SV_TYPE_DOMAIN_MASTER; - add_server_entry(d,work,myname,work->ServerType|SV_TYPE_LOCAL_LIST_ONLY, - 0, lp_serverstring(),True); - - DEBUG(0,("Samba is now a domain master browser for workgroup %s on subnet %s\n", - work->work_group, inet_ntoa(d->bcast_ip))); - - if (d == wins_client_subnet) - { - /* ok! we successfully registered by unicast with the - WINS server. we now expect to become the domain - master on the local subnets. if this fails, it's - probably a 1.9.16p2 to 1.9.16p11 server's fault. - - this is a configuration issue that should be addressed - by the network administrator - you shouldn't have - several machines configured as a domain master browser - for the same WINS scope (except if they are 1.9.17 or - greater, and you know what you're doing. - - see DOMAIN.txt. - - */ - add_domain_master_bcast(); - } - break; - } - - case DOMAIN_MST: - { - /* don't have to do anything: just report success */ - DEBUG(3,("domain second stage: there isn't one!\n")); - break; - } - } -} - - -/******************************************************************* - become a logon server. - ******************************************************************/ -void become_logon_server(struct subnet_record *d, struct work_record *work) -{ - if (!work || !d) return; - - if (!lp_domain_logons()) - { - DEBUG(0,("samba not configured as a logon master.\n")); - return; - } - - DEBUG(2,("Becoming logon server for %s %s (currently at stage %d)\n", - work->work_group,inet_ntoa(d->bcast_ip),work->log_state)); - - switch (work->log_state) - { - case LOGON_NONE: /* while we were nothing but a server... */ - { - DEBUG(3,("go to first stage: register <1c> name\n")); - work->log_state = LOGON_WAIT; - - /* XXXX the 0x1c is apparently something to do with domain logons */ - add_my_name_entry(d, myworkgroup,0x1c,nb_type|NB_ACTIVE|NB_GROUP); - - /* DON'T do anything else after calling add_my_name_entry() */ - break; - } - - case LOGON_WAIT: - { - work->log_state = LOGON_SRV; /* ... become logon server */ - DEBUG(3,("logon second stage: register \n")); - - /* update our server status */ - work->ServerType |= SV_TYPE_NT|SV_TYPE_DOMAIN_MEMBER; - add_server_entry(d,work,myname,work->ServerType|SV_TYPE_LOCAL_LIST_ONLY - ,0, lp_serverstring(),True); - - /* DON'T do anything else after calling add_my_name_entry() */ - break; - } - - case LOGON_SRV: - { - DEBUG(3,("logon third stage: there isn't one!\n")); - break; - } - } -} - - -/******************************************************************* - unbecome the local master browser. initates removal of necessary netbios - names, and tells the world that we are no longer a master browser. - - XXXX this _should_ be used to demote to a backup master browser, without - going straight to non-master browser. another time. - - ******************************************************************/ -void unbecome_local_master(struct subnet_record *d, struct work_record *work, - int remove_type) -{ - /* can only remove master types with this function */ - - if (remove_type & SV_TYPE_MASTER_BROWSER) - { - DEBUG(2,("Becoming local non-master for %s\n",work->work_group)); - - /* no longer a master browser of any sort */ - - work->ServerType &= ~SV_TYPE_MASTER_BROWSER; - work->ServerType |= SV_TYPE_POTENTIAL_BROWSER; - work->ElectionCriterion &= ~0x4; - work->mst_state = MST_POTENTIAL; - - /* announce ourselves as no longer active as a master browser. */ - announce_server(d, work, work->work_group, myname, 0, 0); - remove_name_entry(d,MSBROWSE ,0x01); - remove_name_entry(d,work->work_group,0x1d); - } -} - - -/******************************************************************* - unbecome the domain master browser. initates removal of necessary netbios - names, and tells the world that we are no longer a domain browser. - ******************************************************************/ -void unbecome_domain_master(struct subnet_record *d, struct work_record *work, - int remove_type) -{ - DEBUG(2,("Becoming domain non-master for %s\n",work->work_group)); - - /* can only remove master or domain types with this function */ - if (remove_type & SV_TYPE_DOMAIN_MASTER) - { - /* no longer a domain master browser of any sort */ - - work->ServerType &= ~SV_TYPE_DOMAIN_MASTER; - work->dom_state = DOMAIN_NONE; - - /* announce ourselves as no longer active as a master browser on - all our local subnets. */ - for (d = FIRST_SUBNET; d; d = NEXT_SUBNET_EXCLUDING_WINS(d)) - { - work = find_workgroupstruct(d, myworkgroup, False); - - /* Remove the name entry without any NetBIOS traffic as that's - how it was registered. */ - remove_name_entry(d,work->work_group,0x1b); - } - - /* Unregister the 1b name from the WINS server. */ - if(wins_client_subnet != NULL) - remove_name_entry(wins_client_subnet, myworkgroup, 0x1b); - } -} - - -/******************************************************************* - unbecome the logon server. initates removal of necessary netbios - names, and tells the world that we are no longer a logon server. - ******************************************************************/ -void unbecome_logon_server(struct subnet_record *d, struct work_record *work, - int remove_type) -{ - DEBUG(2,("Becoming logon non-server for %s\n",work->work_group)); - - /* can only remove master or domain types with this function */ - - if (remove_type & SV_TYPE_DOMAIN_MEMBER) - { - /* no longer a master browser of any sort */ - - work->ServerType &= ~SV_TYPE_DOMAIN_MEMBER; - work->log_state = LOGON_NONE; - - remove_name_entry(d,work->work_group,0x1c); - } -} - - -/******************************************************************* - run the election - ******************************************************************/ -void run_elections(time_t t) -{ - static time_t lastime = 0; - - struct subnet_record *d; - - /* send election packets once a second */ - if (lastime && t-lastime <= 0) return; - - lastime = 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 (work->RunningElection) - { - send_election(d,work->work_group, work->ElectionCriterion, - t-StartupTime,myname); - - if (work->ElectionCount++ >= 4) - { - /* I won! now what :-) */ - DEBUG(2,(">>> Won election on %s %s <<<\n", - work->work_group,inet_ntoa(d->bcast_ip))); - - work->RunningElection = False; - work->mst_state = MST_POTENTIAL; - - become_local_master(d, work); - } - } - } - } -} - - -/******************************************************************* - work out if I win an election - ******************************************************************/ -static BOOL win_election(struct work_record *work,int version,uint32 criterion, - int timeup,char *name) -{ - int mytimeup = time(NULL) - StartupTime; - uint32 mycriterion = work->ElectionCriterion; - - /* If local master is false then never win - in election broadcasts. */ - if(!lp_local_master()) - { - DEBUG(3,("win_election: Losing election as local master == False\n")); - return False; - } - - DEBUG(4,("election comparison: %x:%x %x:%x %d:%d %s:%s\n", - version,ELECTION_VERSION, - criterion,mycriterion, - timeup,mytimeup, - name,myname)); - - if (version > ELECTION_VERSION) return(False); - if (version < ELECTION_VERSION) return(True); - - if (criterion > mycriterion) return(False); - if (criterion < mycriterion) return(True); - - if (timeup > mytimeup) return(False); - if (timeup < mytimeup) return(True); - - if (strcasecmp(myname,name) > 0) return(False); - - return(True); -} - - -/******************************************************************* - process a election packet - - An election dynamically decides who will be the master. - ******************************************************************/ -void process_election(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); - int version = CVAL(buf,0); - uint32 criterion = IVAL(buf,1); - int timeup = IVAL(buf,5)/1000; - char *name = buf+13; - struct work_record *work; - - if (!d) return; - - if (ip_equal(d->bcast_ip,wins_ip)) - { - DEBUG(0,("Unexpected election request from %s %s on WINS net\n", - name, inet_ntoa(p->ip))); - return; - } - - name[15] = 0; - - DEBUG(3,("Election request from %s %s vers=%d criterion=%08x timeup=%d\n", - name,inet_ntoa(p->ip),version,criterion,timeup)); - - if (same_context(dgram)) return; - - for (work = d->workgrouplist; work; work = work->next) - { - if (!strequal(work->work_group, myworkgroup)) - continue; - - if (win_election(work, version,criterion,timeup,name)) - { - if (!work->RunningElection) - { - work->needelection = True; - work->ElectionCount=0; - work->mst_state = MST_POTENTIAL; - } - } - else - { - work->needelection = False; - - if (work->RunningElection || AM_MASTER(work)) - { - work->RunningElection = False; - DEBUG(3,(">>> Lost election on %s %s <<<\n", - work->work_group,inet_ntoa(d->bcast_ip))); - if (AM_MASTER(work)) - { - unbecome_local_master(d, work, SV_TYPE_MASTER_BROWSER); - } - } - } - } -} - - -/**************************************************************************** - checks whether a browser election is to be run on any workgroup - - this function really ought to return the time between election - packets (which depends on whether samba intends to be a domain - master or a master browser) in milliseconds. - - ***************************************************************************/ -BOOL check_elections(void) -{ - struct subnet_record *d; - BOOL run_any_election = False; - - for (d = FIRST_SUBNET; d; d = NEXT_SUBNET_EXCLUDING_WINS(d)) - { - struct work_record *work; - for (work = d->workgrouplist; work; work = work->next) - { - run_any_election |= work->RunningElection; - - if (work->needelection && !work->RunningElection) - { - DEBUG(3,(">>> Starting election on %s %s <<<\n", - work->work_group,inet_ntoa(d->bcast_ip))); - work->ElectionCount = 0; - work->RunningElection = True; - work->needelection = False; - } - } - } - return run_any_election; -} - diff --git a/source3/nameelect.doc b/source3/nameelect.doc deleted file mode 100644 index df025e2069..0000000000 --- a/source3/nameelect.doc +++ /dev/null @@ -1,256 +0,0 @@ -/* - Unix SMB/Netbios documentation. - Version 0.1 - Copyright (C) Luke Leighton Andrew Tridgell 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. - - Document name: nameelect.doc - - Revision History: - - 0.0 - 02jul96 : lkcl@pires.co.uk - created - - 0.1 - 22jul96 Andrew.Tridgell@anu.edu.au - tridge's comments on first revision -*/ - -the module nameelect.c deals with initiating, winning, losing -browsing elections, and checking if browsers are still around, -and the consequences of getting involved in all this. - -an election packet can be received at any time, which will initiate -an election. samba can also detect that there is no longer a -master browser and will initiate an election. - -there is one way to become a master browser, but there are two -ways to un-become a master browser. if you lose an election, you -must stop being a master browser. if you fail to register your -unique special browser names (either on your local subnet or with -the WINS server) then you must stop being a master browser. - -this is a double fail-safe mechanism to ensure that there is only -one master browser per workgroup per subnet (and one domain master -browser - per domain (workgroup) per wide area network). - -(a wide area network is created when one or more servers on a -broadcast-isolated subnet point to the same WINS server). - --------- -NOTE FROM TRIDGE: - -I'd say "domain master browser" not "WINS server" here. WINS doesn't -have much to do with browsing, it is the WAN varient of name -resolution. The name resolution and browsing functions of a netbios -network are almost entirely separate. Both grew out of systems that -could only be used on local networks. - -To adapt them to WANs, WINS was added for name resolution, and "domain -master browsers" were added for browse lists. It would be perfectly -possible to have a WINS server that doesn't even listen to UDP port -138. --------- - -/************************************************************************* - check_elections() - *************************************************************************/ - -this function returns True if samba is in the process of running an -election on any of its interfaces. a better version of this function -should return the time-out period in between election packets, in -milliseconds. - - -/************************************************************************* - process_election() - *************************************************************************/ - -this function is responsible for dealing with the receipt of an election -browse MAILSLOT packet. - -if samba is running an election, it checks the criteria in the packet -received using win_election() to see if it has lost the election or if -it should join in the election. - -if it loses the election, then it becomes a non-master. - - -/************************************************************************* - win_election() - *************************************************************************/ - -this function returns True if samba has won an election. the criteria -in order of precedence are: - -the election version; the election criteria; the time since samba was -started; and as a last resort, a name comparison is used. - - -/************************************************************************* - run_elections() - *************************************************************************/ - -this function is responsible for sending out election packets if -samba is running in an election. once the fourth packet is sent -out, it is assumed that we have won, and samba initiates becoming -a master browser. - -(it looks like samba sends out an extra packet just to be sure...) - - -/************************************************************************* - become_nonmaster() - *************************************************************************/ - -this function is responsible for down-grading samba's status from -either domain master to master browser or nothing, or master browser -to nothing, depending on its current status. - -samba can become a non-master in three ways: by losing an election - -see process_election(); by having one of its special browser names -de-registered - see name_unregister_work(); by receiving and -processing a browser reset packet - see process_reset_browser(). - -when samba stops being a domain master, it must release its unique -0x1b name. when samba stops being a master browser, it must release -its unique 0x1d name. - -becoming non-master is done on a per-subnet basis. - - -/************************************************************************* - become_master() - *************************************************************************/ - -this function is responsible for slowly turning samba into a -local master browser or a domain master browser. - - -this is done in stages. note that this could take a while, -particularly on a broadcast subnet, as we have to wait for -the implicit registration of each name to be accepted. - -as each name is successfully registered, become_master() is -called again via name_register_work(), in order to initiate -the next stage (see dead_netbios_entry() - deals with implicit -name registration and response_name_reg() - deals with explicit -registration with a WINS server). - -stage 1: was MST_NONE - go to MST_NONE and register ^1^2__MSBROWSE__^2^1. -stage 2: was MST_WON - go to MST_MSB and register WORKGROUP(0x1d) -stage 3: was MST_MSB - go to MST_BROWSER and register WORKGROUP(0x1b) -stage 4: was MST_BROWSER - go to MST_DOMAIN (do not pass GO, do not...) - -note that this code still does not cope with the distinction -between different types of nodes, particularly between M and P -nodes (see rfc1001.txt). that will be developed later. - - -/************************************************************************* - name_register_work() - *************************************************************************/ - -this function is called when a NetBIOS name is successfully -registered. it will add the registered name into samba's NetBIOS -records. - -it has the additional responsibility that when samba is becoming -a master browser, it must initiate the next stage in the progress -towards becoming a master browser. - -implicit name registration is done through dead_netbios_entry() -by time-out. explicit name registration is done through -response_name_reg() with a WINS server. - - -/************************************************************************* - name_unregister_work() - *************************************************************************/ - -this function is called when there is an objection to a NetBIOS -name being registered. this will always be done through a negative -response to a name registration, whether it be by a host that -already owns the unique name being registered on a subnet, or -by a WINS server. - -the name being objected to must be removed from samba's records. - -it has the additional responsibility of checking whether samba is -currently a master browser or not, and if so it should initiate -becoming a non-master. - - - -/************************************************************************* - send_election() - *************************************************************************/ - -this function is responsible for sending a browse mailslot -datagram election packet (of type ANN_Election). it constructs -the packet with all the relevant info needed to participate: -election version; election criteria; time since startup and -our name. - -this function can be used to ensure that initiate but lose an -election by specifying a criteria and time up of zero. this -is necessary if we are a master browser and we are about to -go down (politely!) - see nmbd.c:sig_term(). - - -/************************************************************************* - browser_gone() - *************************************************************************/ - -this function is responsible for dealing with the instance when -the master browser we thought was present on a subnet is no longer -responding. - -if it is samba's workgroup, and it's a local interface, samba -detects that it can participate in an election on that interface -and potentially become a master browser or domain master. - -if it's a local subnet and not one of samba's workgroups, then -samba will force an election (which it is not obliged to do). -remove_workgroup() will be expected to remove all references -to this workgroup and the servers in it from the database. - -if it's a remote subnet and not one of samba's workgroups then -no election is forced, and remove_workgroup() will be expected -to remove all server entries from this workgroup _except_ those -added from the lmhosts file. if there are entries added from -the lmhosts file, then the workgroup entry will remain, -otherwise it too will be removed. - - -/************************************************************************* - check_master_browser() - *************************************************************************/ - -this function is responsible for periodically checking whether -master browsers that samba expects to be alive are alive. this -is done every CHECK_TIME_MST_BROWSE minutes. - -for every workgroup record for which samba is not a master browser, -on both local and remote interfaces, samba will initiate a -broadcast query for a master browser on that subnet. - -(browser_gone() will be called to deal with the case where no -response is received to the NAME_QUERY_MST_CHK initiated here. -no action is required when a response _is_ received, however: -see nameservresp.c:response_process() and dead_netbios_entry() -for details) - - diff --git a/source3/namelogon.c b/source3/namelogon.c deleted file mode 100644 index 08e0f03607..0000000000 --- a/source3/namelogon.c +++ /dev/null @@ -1,251 +0,0 @@ -/* - Unix SMB/Netbios implementation. - Version 1.9. - NBT netbios routines and daemon - version 2 - 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 - 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 ClientDGRAM; - -extern int DEBUGLEVEL; - -extern pstring myname; - - -/**************************************************************************** - process a domain logon packet - **************************************************************************/ -void process_logon_packet(struct packet_struct *p,char *buf,int len, - char *mailslot) -{ - struct dgram_packet *dgram = &p->packet.dgram; - pstring my_name; - fstring reply_name; - BOOL add_slashes = False; - pstring outbuf; - int code,reply_code; - char unknown_byte = 0; - uint16 request_count = 0; - uint16 token = 0; - - uint32 ntversion; - uint16 lmnttoken; - uint16 lm20token; - uint32 allowableaccount; /* Control bits, i.e. 0x80 == workstation trust a/c */ - uint32 domainsidsize; - uint16 requestcount; - char *domainsid; - char *getdc; - char *uniuser; /* Unicode user name */ - pstring ascuser; - char *unicomp; /* Unicode computer name */ - struct smb_passwd *smb_pass; /* To check if machine account exists */ - - if (!lp_domain_logons()) - { - DEBUG(3,("No domain logons\n")); - return; - } - - strcpy(my_name, myname); - strupper(my_name); - - code = SVAL(buf,0); - DEBUG(1,("namelogon from %s: %x\n", inet_ntoa(p->ip), code)); - - switch (code) - { - case 0: - { - char *q = buf + 2; - char *machine = q; - char *user = skip_string(machine,1); - - getdc = skip_string(user,1); - q = skip_string(getdc,1); - unknown_byte = CVAL(q,0); - request_count = SVAL(q,1); - token = SVAL(q,3); - - reply_code = 0x6; - strcpy(reply_name,my_name); - add_slashes = True; - - DEBUG(3,("Domain login request from %s(%s) user=%s token=%x\n", - machine,inet_ntoa(p->ip),user,token)); - - q = outbuf; - SSVAL(q, 0, 6); q += 2; - - strcpy(reply_name, "\\\\"); - strcat(reply_name, my_name); - strcpy(q, reply_name); q = skip_string(q, 1); /* PDC name */ - - SSVAL(q, 0, token); q += 2; - - dump_data(4, outbuf, PTR_DIFF(q, outbuf)); - - send_mailslot_reply(True, getdc, ClientDGRAM, - outbuf,PTR_DIFF(q,outbuf), - dgram->dest_name.name, - dgram->source_name.name, - dgram->dest_name.name_type, - dgram->source_name.name_type, - p->ip, *iface_ip(p->ip)); - break; - } - - case QUERYFORPDC: - { - char *q = buf + 2; - char *machine = q; - - getdc = skip_string(machine,1); - unicomp = skip_string(getdc,1); - - q = align2(unicomp, buf); - - q = skip_unicode_string(q, 1); - - ntversion = IVAL(q, 0); q += 4; - lmnttoken = SVAL(q, 0); q += 2; - lm20token = SVAL(q, 0); q += 2; - - /* construct reply */ - - q = outbuf; - SSVAL(q, 0, QUERYFORPDC_R); q += 2; - - strcpy(reply_name,my_name); - strcpy(q, reply_name); q = skip_string(q, 1); /* PDC name */ - - if (strcmp(mailslot, NT_LOGON_MAILSLOT)==0) { - q = align2(q, buf); - - PutUniCode(q, my_name); /* PDC name */ - q = skip_unicode_string(q, 1); - PutUniCode(q, lp_workgroup()); /* Domain name*/ - q = skip_unicode_string(q, 1); - - SIVAL(q, 0, ntversion); q += 4; - SSVAL(q, 0, lmnttoken); q += 2; - SSVAL(q, 0, lm20token); q += 2; - } - - DEBUG(3,("GETDC request from %s(%s), reporting %s domain %s 0x%x ntversion=%x lm_nt token=%x lm_20 token=%x\n", - machine,inet_ntoa(p->ip), reply_name, lp_workgroup(), - QUERYFORPDC_R, (uint32)ntversion, (uint32)lmnttoken, - (uint32)lm20token)); - - dump_data(4, outbuf, PTR_DIFF(q, outbuf)); - - send_mailslot_reply(True, getdc,ClientDGRAM, - outbuf,PTR_DIFF(q,outbuf), - dgram->dest_name.name, - dgram->source_name.name, - dgram->dest_name.name_type, - dgram->source_name.name_type, - p->ip, *iface_ip(p->ip)); - return; - } - - case SAMLOGON: - { - char *q = buf + 2; - - requestcount = SVAL(q, 0); q += 2; - unicomp = q; - uniuser = skip_unicode_string(unicomp,1); - getdc = skip_unicode_string(uniuser,1); - q = skip_string(getdc,1); - allowableaccount = IVAL(q, 0); q += 4; - domainsidsize = IVAL(q, 0); q += 4; - domainsid = q; - q += domainsidsize + 3; - ntversion = IVAL(q, 0); q += 4; - lmnttoken = SVAL(q, 0); q += 2; - lm20token = SVAL(q, 0); q += 2; - - DEBUG(3,("SAMLOGON sidsize %d ntv %d\n", domainsidsize, ntversion)); - - /* - If MACHINE$ is in our password database then respond, else ignore. - Let's ignore the SID. - */ - - strcpy(ascuser, unistr(uniuser)); - DEBUG(3,("SAMLOGON user %s\n", ascuser)); - - strcpy(reply_name,"\\\\"); /* Here it wants \\LOGONSERVER */ - strcpy(reply_name+2,my_name); - - smb_pass = get_smbpwd_entry(ascuser, 0); - - if(!smb_pass) - { - DEBUG(3,("SAMLOGON request from %s(%s) for %s, not in password file\n", - unistr(unicomp),inet_ntoa(p->ip), ascuser)); - return; - } - else - { - DEBUG(3,("SAMLOGON request from %s(%s) for %s, returning logon svr %s domain %s code %x token=%x\n", - unistr(unicomp),inet_ntoa(p->ip), ascuser, reply_name, lp_workgroup(), - SAMLOGON_R ,lmnttoken)); - } - - /* construct reply */ - - q = outbuf; - SSVAL(q, 0, SAMLOGON_R); q += 2; - - PutUniCode(q, reply_name); q = skip_unicode_string(q, 1); - unistrcpy(q, uniuser); q = skip_unicode_string(q, 1); /* User name (workstation trust account) */ - PutUniCode(q, lp_workgroup()); q = skip_unicode_string(q, 1); /* Domain name. */ - - SIVAL(q, 0, ntversion); q += 4; - SSVAL(q, 0, lmnttoken); q += 2; - SSVAL(q, 0, lm20token); q += 2; - - dump_data(4, outbuf, PTR_DIFF(q, outbuf)); - - send_mailslot_reply(True, getdc,ClientDGRAM, - outbuf,PTR_DIFF(q,outbuf), - dgram->dest_name.name, - dgram->source_name.name, - dgram->dest_name.name_type, - dgram->source_name.name_type, - p->ip, *iface_ip(p->ip)); - break; - } - - default: - { - DEBUG(3,("Unknown domain request %d\n",code)); - return; - } - } - -} diff --git a/source3/namelogon.doc b/source3/namelogon.doc deleted file mode 100644 index c4a97d0cf1..0000000000 --- a/source3/namelogon.doc +++ /dev/null @@ -1,36 +0,0 @@ -/* - Unix SMB/Netbios documentation. - Version 0.0 - Copyright (C) Luke Leighton Andrew Tridgell 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. - - Document name: namelogon.doc - - Revision History: - - 0.0 - 02jul96 : lkcl@pires.co.uk - created -*/ - -this module deals with the first stage of domain logons. there is much -more work to be done on this: it's all totally undocumented. - - -/************************************************************************* - process_logon_packet() - *************************************************************************/ - -a function that processes logon packets (the most helpful comment yet :-). diff --git a/source3/namepacket.c b/source3/namepacket.c deleted file mode 100644 index be099340c3..0000000000 --- a/source3/namepacket.c +++ /dev/null @@ -1,777 +0,0 @@ -/* - Unix SMB/Netbios implementation. - Version 1.9. - NBT netbios routines and daemon - version 2 - 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 - 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; - -extern int DEBUGLEVEL; - -extern int num_response_packets; - -BOOL CanRecurse = True; -extern pstring scope; -extern struct in_addr wins_ip; -extern struct in_addr loopback_ip; - -static uint16 name_trn_id=0; - - -/*************************************************************************** - updates the unique transaction identifier - **************************************************************************/ -void debug_browse_data(char *outbuf, int len) -{ - int i,j; - for (i = 0; i < len; i+= 16) - { - DEBUG(4, ("%3x char ", i)); - - for (j = 0; j < 16; j++) - { - unsigned char x = outbuf[i+j]; - if (x < 32 || x > 127) x = '.'; - - if (i+j >= len) break; - DEBUG(4, ("%c", x)); - } - - DEBUG(4, (" hex ", i)); - - for (j = 0; j < 16; j++) - { - if (i+j >= len) break; - DEBUG(4, (" %02x", (unsigned char)outbuf[i+j])); - } - - DEBUG(4, ("\n")); - } - -} - - -/*************************************************************************** - updates the unique transaction identifier - **************************************************************************/ -static void update_name_trn_id(void) -{ - if (!name_trn_id) - { - name_trn_id = (time(NULL)%(unsigned)0x7FFF) + (getpid()%(unsigned)100); - } - name_trn_id = (name_trn_id+1) % (unsigned)0x7FFF; -} - - -/**************************************************************************** - initiate a netbios packet - ****************************************************************************/ -void initiate_netbios_packet(uint16 *id, - int fd,int quest_type,char *name,int name_type, - int nb_flags,BOOL bcast,BOOL recurse, - struct in_addr to_ip) -{ - struct packet_struct p; - struct nmb_packet *nmb = &p.packet.nmb; - struct res_rec additional_rec; - char *packet_type = "unknown"; - int opcode = -1; - - if (!id) return; - - if (quest_type == NMB_STATUS) { packet_type = "nmb_status"; opcode = 0; } - if (quest_type == NMB_QUERY ) { packet_type = "nmb_query"; opcode = 0; } - if (quest_type == NMB_REG ) { packet_type = "nmb_reg"; opcode = 5; } - if (quest_type == NMB_REG_REFRESH ) { packet_type = "nmb_reg_refresh"; opcode = 9; } - if (quest_type == NMB_REL ) { packet_type = "nmb_rel"; opcode = 6; } - - DEBUG(4,("initiating netbios packet: %s %s(%x) (bcast=%s) %s\n", - packet_type, name, name_type, BOOLSTR(bcast), inet_ntoa(to_ip))); - - if (opcode == -1) return; - - bzero((char *)&p,sizeof(p)); - - if (*id == 0xffff) { - update_name_trn_id(); - *id = name_trn_id; /* allow resending with same id */ - } - - nmb->header.name_trn_id = *id; - nmb->header.opcode = opcode; - nmb->header.response = False; - - nmb->header.nm_flags.bcast = bcast; - nmb->header.nm_flags.recursion_available = False; - nmb->header.nm_flags.recursion_desired = recurse; - nmb->header.nm_flags.trunc = False; - nmb->header.nm_flags.authoritative = False; - - nmb->header.rcode = 0; - nmb->header.qdcount = 1; - nmb->header.ancount = 0; - nmb->header.nscount = 0; - nmb->header.arcount = (quest_type==NMB_REG || - quest_type==NMB_REL || - quest_type==NMB_REG_REFRESH) ? 1 : 0; - - make_nmb_name(&nmb->question.question_name,name,name_type,scope); - - nmb->question.question_type = quest_type == NMB_STATUS ? 0x21 : 0x20; - nmb->question.question_class = 0x1; - - if (quest_type == NMB_REG || - quest_type == NMB_REG_REFRESH || - quest_type == NMB_REL) - { - nmb->additional = &additional_rec; - bzero((char *)nmb->additional,sizeof(*nmb->additional)); - - nmb->additional->rr_name = nmb->question.question_name; - nmb->additional->rr_type = 0x20; - nmb->additional->rr_class = 0x1; - - if (quest_type == NMB_REG || quest_type == NMB_REG_REFRESH) - nmb->additional->ttl = lp_max_ttl(); - else - nmb->additional->ttl = 0; - - nmb->additional->rdlength = 6; - nmb->additional->rdata[0] = nb_flags; - putip(&nmb->additional->rdata[2],(char *)iface_ip(to_ip)); - } - - p.ip = to_ip; - p.port = NMB_PORT; - p.fd = fd; - p.timestamp = time(NULL); - p.packet_type = NMB_PACKET; - p.locked = False; - - debug_nmb_packet(&p); - - if (!send_packet(&p)) { - DEBUG(3,("send_packet to %s %d failed\n",inet_ntoa(p.ip),p.port)); - *id = 0xffff; - } - - return; -} - - -/**************************************************************************** - reply to a netbios name packet. see rfc1002.txt - ****************************************************************************/ -void reply_netbios_packet(struct packet_struct *p1,int trn_id, - int rcode, int rcv_code, int opcode, - BOOL recursion_available, - BOOL recursion_desired, - struct nmb_name *rr_name,int rr_type,int rr_class,int ttl, - char *data,int len) -{ - struct packet_struct p; - struct nmb_packet *nmb = &p.packet.nmb; - struct res_rec answers; - char *packet_type = "unknown"; - - p = *p1; - - switch (rcv_code) - { - case NMB_STATUS: - { - packet_type = "nmb_status"; - break; - } - case NMB_QUERY: - { - packet_type = "nmb_query"; - break; - } - case NMB_REG: - { - packet_type = "nmb_reg"; - break; - } - case NMB_REL: - { - packet_type = "nmb_rel"; - break; - } - case NMB_WAIT_ACK: - { - packet_type = "nmb_wack"; - break; - } - default: - { - DEBUG(1,("replying netbios packet: %s %s %s\n", - packet_type, namestr(rr_name), inet_ntoa(p.ip))); - - return; - } - } - - DEBUG(4,("replying netbios packet: %s %s %s\n", - packet_type, namestr(rr_name), inet_ntoa(p.ip))); - - nmb->header.name_trn_id = trn_id; - nmb->header.opcode = opcode; - nmb->header.response = True; - nmb->header.nm_flags.bcast = False; - nmb->header.nm_flags.recursion_available = recursion_available; - nmb->header.nm_flags.recursion_desired = recursion_desired; - nmb->header.nm_flags.trunc = False; - nmb->header.nm_flags.authoritative = True; - - nmb->header.qdcount = 0; - nmb->header.ancount = 1; - nmb->header.nscount = 0; - nmb->header.arcount = 0; - nmb->header.rcode = rcode; - - bzero((char*)&nmb->question,sizeof(nmb->question)); - - nmb->answers = &answers; - bzero((char*)nmb->answers,sizeof(*nmb->answers)); - - nmb->answers->rr_name = *rr_name; - nmb->answers->rr_type = rr_type; - nmb->answers->rr_class = rr_class; - nmb->answers->ttl = ttl; - - if (data && len) - { - nmb->answers->rdlength = len; - memcpy(nmb->answers->rdata, data, len); - } - - p.packet_type = NMB_PACKET; - - debug_nmb_packet(&p); - - send_packet(&p); -} - - -/******************************************************************* - 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 - ******************************************************************/ -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; -} - -/**************************************************************************** - determine if a packet is for us. Note that to have any chance of - being efficient we need to drop as many packets as possible at this - stage as subsequent processing is expensive. - - We also must make absolutely sure we don't tread on another machines - property by answering a packet that is not for us. - ****************************************************************************/ -static BOOL listening(struct packet_struct *p,struct nmb_name *n) -{ - struct subnet_record *d; - struct name_record *n1 = NULL; - - if((d = find_subnet_all(p->ip)) != NULL) - n1 = find_name_on_subnet(d, n, FIND_SELF_NAME); - - return (n1 != NULL); -} - - -/**************************************************************************** - process udp 138 datagrams - ****************************************************************************/ -static void process_dgram(struct packet_struct *p) -{ - char *buf; - char *buf2; - int len; - struct dgram_packet *dgram = &p->packet.dgram; - - /* if we aren't listening to the destination name then ignore the packet */ - if (!listening(p,&dgram->dest_name)) - { - DEBUG(5,("process_dgram: ignoring dgram packet sent to name %s(%x) from %s\n", - dgram->dest_name.name, dgram->dest_name.name_type, inet_ntoa(p->ip))); - return; - } - - if (dgram->header.msg_type != 0x10 && - dgram->header.msg_type != 0x11 && - dgram->header.msg_type != 0x12) - { - /* don't process error packets etc yet */ - DEBUG(5,("process_dgram: ignoring dgram packet sent to name %s(%d) from %s as it is \ - an error packet of type %x\n", - dgram->dest_name.name, dgram->dest_name.name_type, - inet_ntoa(p->ip), dgram->header.msg_type)); - return; - } - - buf = &dgram->data[0]; - buf -= 4; /* XXXX for the pseudo tcp length - - someday I need to get rid of this */ - - if (CVAL(buf,smb_com) != SMBtrans) return; - - len = SVAL(buf,smb_vwv11); - buf2 = smb_base(buf) + SVAL(buf,smb_vwv12); - - DEBUG(4,("process_dgram: datagram from %s to %s(%s)for %s of type %d len=%d\n", - namestr(&dgram->source_name),namestr(&dgram->dest_name), - inet_ntoa(p->ip), smb_buf(buf),CVAL(buf2,0),len)); - - - if (len <= 0) return; - - /* datagram packet received for the browser mailslot */ - if (strequal(smb_buf(buf),BROWSE_MAILSLOT)) { - process_browse_packet(p,buf2,len); - return; - } - - /* datagram packet received for the domain log on mailslot */ - if (strequal(smb_buf(buf),NET_LOGON_MAILSLOT)) { - process_logon_packet(p,buf2,len, NET_LOGON_MAILSLOT); - return; - } - - /* datagram packet received for the NT domain log on mailslot */ - if (strequal(smb_buf(buf),NT_LOGON_MAILSLOT)) { - process_logon_packet(p,buf2,len, NT_LOGON_MAILSLOT); - return; - } -} - -/**************************************************************************** - process a nmb packet - ****************************************************************************/ -static void process_nmb(struct packet_struct *p) -{ - struct nmb_packet *nmb = &p->packet.nmb; - - debug_nmb_packet(p); - - switch (nmb->header.opcode) - { - case 8: /* what is this?? */ - case NMB_REG: - case NMB_REG_REFRESH: - { - if (nmb->header.response) - { - if (nmb->header.ancount ==0) break; - response_netbios_packet(p); /* response to registration dealt - with here */ - } - else - { - if (nmb->header.qdcount==0 || nmb->header.arcount==0) break; - reply_name_reg(p); - } - break; - } - - case 0: - { - if (nmb->header.response) - { - switch (nmb->question.question_type) - { - case 0x0: - { - response_netbios_packet(p); - break; - } - } - return; - } - else if (nmb->header.qdcount>0) - { - switch (nmb->question.question_type) - { - case NMB_QUERY: - { - reply_name_query(p); - break; - } - case NMB_STATUS: - { - reply_name_status(p); - break; - } - } - return; - } - break; - } - - case NMB_REL: - { - if (nmb->header.response) - { - if (nmb->header.ancount ==0) break; - response_netbios_packet(p); /* response to release dealt - with here */ - } - else - { - if (nmb->header.qdcount==0 || nmb->header.arcount==0) break; - reply_name_release(p); - } - break; - } - } -} - - -/******************************************************************* - run elements off the packet queue till its empty - ******************************************************************/ -void run_packet_queue() -{ - struct packet_struct *p; - - while ((p=packet_queue)) { - packet_queue = p->next; - if (packet_queue) packet_queue->prev = NULL; - p->next = p->prev = NULL; - - switch (p->packet_type) { - case NMB_PACKET: - process_nmb(p); - break; - - case DGRAM_PACKET: - process_dgram(p); - break; - } - free_packet(p); - } -} - - -/**************************************************************************** - Create an fd_set containing all the sockets in the subnet structures, - plus the broadcast sockets. - ***************************************************************************/ -static BOOL create_listen_fdset(fd_set **ppset, int **psock_array, int *listen_number) -{ - int *sock_array = NULL; - struct subnet_record *d = NULL; - int count = 0; - int num = 0; - fd_set *pset = (fd_set *)malloc(sizeof(fd_set)); - - if(pset == NULL) - { - DEBUG(0,("create_listen_fdset: malloc fail !\n")); - return True; - } - - /* Check that we can add all the fd's we need. */ - for (d = FIRST_SUBNET; d; d = NEXT_SUBNET_EXCLUDING_WINS(d)) - count++; - - if((count*2) + 2 > FD_SETSIZE) - { - DEBUG(0,("create_listen_fdset: Too many file descriptors needed (%d). We can \ -only use %d.\n", (count*2) + 2, FD_SETSIZE)); - return True; - } - - if((sock_array = (int *)malloc(((count*2) + 2)*sizeof(int))) == NULL) - { - DEBUG(0,("create_listen_fdset: malloc fail for socket array.\n")); - return True; - } - - FD_ZERO(pset); - - /* Add in the broadcast socket on 137. */ - FD_SET(ClientNMB,pset); - sock_array[num++] = ClientNMB; - - /* Add in the 137 sockets on all the interfaces. */ - for (d = FIRST_SUBNET; d; d = NEXT_SUBNET_EXCLUDING_WINS(d)) - { - FD_SET(d->nmb_sock,pset); - sock_array[num++] = d->nmb_sock; - } - - /* Add in the broadcast socket on 138. */ - FD_SET(ClientDGRAM,pset); - sock_array[num++] = ClientDGRAM; - - /* Add in the 138 sockets on all the interfaces. */ - for (d = FIRST_SUBNET; d; d = NEXT_SUBNET_EXCLUDING_WINS(d)) - { - FD_SET(d->dgram_sock,pset); - sock_array[num++] = d->dgram_sock; - } - - *listen_number = (count*2) + 2; - *ppset = pset; - *psock_array = sock_array; - - return False; -} - -/**************************************************************************** - listens for NMB or DGRAM packets, and queues them - ***************************************************************************/ -BOOL listen_for_packets(BOOL run_election) -{ - static fd_set *listen_set = NULL; - static int listen_number = 0; - static int *sock_array = NULL; - - fd_set fds; - int selrtn; - struct timeval timeout; -#ifndef SYNC_DNS - int dns_fd; -#endif - - if(listen_set == NULL) - { - if(create_listen_fdset(&listen_set, &sock_array, &listen_number)) - { - DEBUG(0,("listen_for_packets: Fatal error. unable to create listen set. Exiting.\n")); - return True; - } - } - - memcpy((char *)&fds, (char *)listen_set, sizeof(fd_set)); - -#ifndef SYNC_DNS - dns_fd = asyncdns_fd(); - if (dns_fd != -1) { - FD_SET(dns_fd, &fds); - } -#endif - - - /* during elections and when expecting a netbios response packet we - need to send election packets at tighter intervals - - ideally it needs to be the interval (in ms) between time now and - the time we are expecting the next netbios packet */ - - timeout.tv_sec = (run_election||num_response_packets) ? 1:NMBD_SELECT_LOOP; - timeout.tv_usec = 0; - - /* We can only take term signals when we are in the select. */ - BlockSignals(False, SIGTERM); - selrtn = sys_select(&fds,&timeout); - BlockSignals(True, SIGTERM); - - if(selrtn > 0) - { - int i; - -#ifndef SYNC_DNS - if (dns_fd != -1 && FD_ISSET(dns_fd,&fds)) { - run_dns_queue(); - } -#endif - - for(i = 0; i < listen_number; i++) - { - if(i < (listen_number/2)) - { - /* Processing a 137 socket. */ - if (FD_ISSET(sock_array[i],&fds)) - { - struct packet_struct *packet = read_packet(sock_array[i], NMB_PACKET); - if (packet) - { - /* - * If we got a packet on the broadcast socket and interfaces - * only is set then check it came from one of our local nets. - */ - if(lp_bind_interfaces_only() && (sock_array[i] == ClientNMB) && - (!is_local_net(packet->ip))) - { - DEBUG(7,("discarding nmb packet sent to broadcast socket from %s:%d\n", - inet_ntoa(packet->ip),packet->port)); - free_packet(packet); - } - else if ((ip_equal(loopback_ip, packet->ip) || - ismyip(packet->ip)) && packet->port == NMB_PORT) - { - DEBUG(7,("discarding own packet from %s:%d\n", - inet_ntoa(packet->ip),packet->port)); - free_packet(packet); - } - else - { - queue_packet(packet); - } - } - } - } - else - { - /* Processing a 138 socket. */ - - if (FD_ISSET(sock_array[i],&fds)) - { - struct packet_struct *packet = read_packet(sock_array[i], DGRAM_PACKET); - if (packet) - { - /* - * If we got a packet on the broadcast socket and interfaces - * only is set then check it came from one of our local nets. - */ - if(lp_bind_interfaces_only() && (sock_array[i] == ClientDGRAM) && - (!is_local_net(packet->ip))) - { - DEBUG(7,("discarding dgram packet sent to broadcast socket from %s:%d\n", - inet_ntoa(packet->ip),packet->port)); - free_packet(packet); - } - else if ((ip_equal(loopback_ip, packet->ip) || - ismyip(packet->ip)) && packet->port == DGRAM_PORT) - { - DEBUG(7,("discarding own packet from %s:%d\n", - inet_ntoa(packet->ip),packet->port)); - free_packet(packet); - } - else - { - queue_packet(packet); - } - } - } - } /* end processing 138 socket. */ - } /* end for */ - } /* end if selret > 0 */ - return False; -} - - - -/**************************************************************************** - construct and send a netbios DGRAM - - Note that this currently sends all answers to port 138. thats the - wrong things to do! I should send to the requestors port. XXX - **************************************************************************/ -BOOL send_mailslot_reply(BOOL unique, char *mailslot,int fd,char *buf,int len,char *srcname, - char *dstname,int src_type,int dest_type, - struct in_addr dest_ip,struct in_addr src_ip) -{ - struct packet_struct p; - struct dgram_packet *dgram = &p.packet.dgram; - char *ptr,*p2; - char tmp[4]; - - /* ha ha. no. do NOT send packets to 255.255.255.255: it's a pseudo address */ - if (ip_equal(wins_ip, dest_ip)) return False; - - bzero((char *)&p,sizeof(p)); - - update_name_trn_id(); - - /* DIRECT GROUP or UNIQUE datagram */ - dgram->header.msg_type = unique ? 0x10 : 0x11; - dgram->header.flags.node_type = M_NODE; - dgram->header.flags.first = True; - dgram->header.flags.more = False; - dgram->header.dgm_id = name_trn_id; - dgram->header.source_ip = src_ip; - dgram->header.source_port = DGRAM_PORT; - dgram->header.dgm_length = 0; /* let build_dgram() handle this */ - dgram->header.packet_offset = 0; - - make_nmb_name(&dgram->source_name,srcname,src_type,scope); - make_nmb_name(&dgram->dest_name,dstname,dest_type,scope); - - ptr = &dgram->data[0]; - - /* now setup the smb part */ - ptr -= 4; /* XXX ugliness because of handling of tcp SMB length */ - memcpy(tmp,ptr,4); - set_message(ptr,17,17 + len,True); - memcpy(ptr,tmp,4); - - CVAL(ptr,smb_com) = SMBtrans; - SSVAL(ptr,smb_vwv1,len); - SSVAL(ptr,smb_vwv11,len); - SSVAL(ptr,smb_vwv12,70 + strlen(mailslot)); - SSVAL(ptr,smb_vwv13,3); - SSVAL(ptr,smb_vwv14,1); - SSVAL(ptr,smb_vwv15,1); - SSVAL(ptr,smb_vwv16,2); - p2 = smb_buf(ptr); - strcpy(p2,mailslot); - p2 = skip_string(p2,1); - - memcpy(p2,buf,len); - p2 += len; - - dgram->datasize = PTR_DIFF(p2,ptr+4); /* +4 for tcp length */ - - p.ip = dest_ip; - p.port = DGRAM_PORT; - p.fd = fd; - p.timestamp = time(NULL); - p.packet_type = DGRAM_PACKET; - - DEBUG(4,("send mailslot %s from %s %s", mailslot, - inet_ntoa(src_ip),namestr(&dgram->source_name))); - DEBUG(4,("to %s %s\n", inet_ntoa(dest_ip),namestr(&dgram->dest_name))); - - return(send_packet(&p)); -} diff --git a/source3/namepacket.doc b/source3/namepacket.doc deleted file mode 100644 index 159a50738c..0000000000 --- a/source3/namepacket.doc +++ /dev/null @@ -1,133 +0,0 @@ -/* - Unix SMB/Netbios documentation. - Version 0.0 - Copyright (C) Luke Leighton Andrew Tridgell 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. - - Document name: namepacket.doc - - Revision History: - - 0.0 - 02jul96 : lkcl@pires.co.uk - created -*/ - -this module deals with packets: sending, receiving, queueing -and some basic interpretation (e.g it excludes datagram -error packets at the moment). - -the packet queueing mechanism was originally introduced when -samba dealt with responses by sending a packet, receiving -packets and queueing all packets that didn't match up with -the response expected. this is fine in a single-thread -environment, but samba now deals with response packets by -queueing the responses. to some extent, therefore, this -queue_packet mechanism is redundant. - - -/************************************************************************* - send_mailslot_reply() - *************************************************************************/ - -this function is responsible for sending a MAILSLOT packet. - -it will _not_ send packets to the pseudo WINS subnet's address of -255.255.255.255: this would be disastrous. - -each packet sent out has a unique transaction identifier. this is done -so that responses can be matched later with the original purpose for -the packet being sent out in the first place. - - -/************************************************************************* - listen_for_packets() - *************************************************************************/ - -this function is responsible for reading NMB and DGRAM packets, and then -queueing them. it will normally time-out for NMBD_SELECT_LOOP seconds, but -if there is an election currently running or we are expecting a response -then this time is reduced to 1 second. - -note: the time-out period needs refining to the millisecond level. - - -/************************************************************************* - queue_packet() - *************************************************************************/ - -this function is responsible for queueing any NMB and DGRAM packets passed -to it. these packets will be removed from the queue in run_packet_queue(). - - -/************************************************************************* - run_packet_queue() - *************************************************************************/ - -this function is responsible for taking a packet off the queue, -identifying whether it is an NMB or a DGRAM packet, processing -it accordingly and deleting it. this process continues until -there are no more packets on the queue. - - -/************************************************************************* - process_nmb() - *************************************************************************/ - -this function receives a packet identified as a netbios packet. -it further identifies whether it is a response or a query packet. -by identifying the type of packet (name registration, query etc) -process_nmb() will call the appropriate function to deal with the -type of packet received. - - -/************************************************************************* - process_dgram() - *************************************************************************/ - -this function is responsible for identifying whether the datagram -packet received is a browser packet or a domain logon packet. it -also does some filtering of certain types of packets (e.g it -filters out error packets). - - -/************************************************************************* - reply_netbios_packet() - *************************************************************************/ - -this function is responsible for sending a reply to another NetBIOS -packet from another host. it can be used to send a reply to a name -registration, name release, name query or name status request. - -the reply can be either a positive or a negative one. - - -/************************************************************************* - initiate_netbios_packet() - *************************************************************************/ - -this function is responsible for construction a netbios packet and sending -it. if the packet has not had a unique transaction id allocated to it, -then initiate_netbios_packet() will give it one. - - -/************************************************************************* - update_name_trn_id() - *************************************************************************/ - -this function is responsible for allocating unique transaction identifiers -for each new packet sent on the network. - - diff --git a/source3/namequery.doc b/source3/namequery.doc deleted file mode 100644 index 4337cfb7e2..0000000000 --- a/source3/namequery.doc +++ /dev/null @@ -1,83 +0,0 @@ -/* - Unix SMB/Netbios documentation. - Version 0.0 - Copyright (C) Luke Leighton Andrew Tridgell 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. - - Document name: namequery.doc - - Revision History: - - 0.0 - 02jul96 : lkcl@pires.co.uk - created -*/ - -this module contains non-threaded versions of name status and name -query functions. if a multi-threaded nmbd was to be written, these -functions would be the starting point. - -at the moment, the expected response queueing system is used to -replace these functions without needing to multi-thread nmbd. - -these functions are used in smbclient and nmblookup at present to -avoid having the vast quantities of complex and unused code needed -to support even a simple name query (or providing stubs for the -unused side of these functions). - -there is a down-side to these functions, which is all microsoft's -fault. microsoft machines always always reply to queries on the -priveleged ports, rather than following the usual tcp/ip mechanism -of replying on the client's port (the exception to this i am led -to believe is windows nt 3.50). - -as a result of this, in order to receive a response to a name -query from a microsoft machine, we must be able to listen on -the priveleged netbios name server ports. this is simply not -possible with some versions of unix, unless you have root access. - -it is also not possible if you run smbclient or nmblookup on an -interface that already has been claimed by the netbios name server -daemon nmbd. - -all in all, i wish that microsoft would fix this. - -a solution does exist: nmbd _does_ actually reply on the client's -port, so if smbclient and nmblookup were to use nmbd as a proxy -forwarder of queries (or to use samba's WINS capabilities) then -a query could be made without needing access to the priveleged -ports. in order to do this properly, samba must implement secured -netbios name server functionality (see rfc1001.txt 15.1.6). -(lkcl 01aug96: samba now supports secured name registration) - -/************************************************************************* - name_query() - *************************************************************************/ - - - -/************************************************************************* - name_status() - *************************************************************************/ - - - -/************************************************************************* - _interpret_node_status() - *************************************************************************/ - - -this is a older version of interpret_node_status(). - diff --git a/source3/nameresp.c b/source3/nameresp.c deleted file mode 100644 index de1f33c717..0000000000 --- a/source3/nameresp.c +++ /dev/null @@ -1,318 +0,0 @@ -/* - Unix SMB/Netbios implementation. - Version 1.9. - NBT netbios library routines - 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 - 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. - - Module name: nameresp.c - -*/ - -#include "includes.h" - -extern int ClientNMB; - -extern struct subnet_record *subnetlist; - -extern int DEBUGLEVEL; - -extern pstring scope; -extern struct in_addr ipzero; -extern struct in_addr wins_ip; - - -/*************************************************************************** - deals with an entry before it dies - **************************************************************************/ -static void dead_netbios_entry(struct subnet_record *d, - struct response_record *n) -{ - DEBUG(3,("Removing dead netbios entry for %s %s (num_msgs=%d)\n", - inet_ntoa(n->send_ip), namestr(&n->name), n->num_msgs)); - - debug_state_type(n->state); - - switch (n->state) - { - case NAME_QUERY_CONFIRM: - { - if (!lp_wins_support()) return; /* only if we're a WINS server */ - - if (n->num_msgs == 0) - { - /* oops. name query had no response. check that the name is - unique and then remove it from our WINS database */ - - /* IMPORTANT: see query_refresh_names() */ - - if ((!NAME_GROUP(n->nb_flags))) - { - struct subnet_record *d1 = wins_client_subnet; - if (d1) - { - /* remove the name that had been registered with us, - and we're now getting no response when challenging. - see rfc1001.txt 15.5.2 - */ - remove_netbios_name(d1, n->name.name, n->name.name_type, REGISTER); - } - } - } - break; - } - - case NAME_QUERY_MST_CHK: - { - /* if no response received, the master browser must have gone - down on that subnet, without telling anyone. */ - - /* IMPORTANT: see response_netbios_packet() */ - - if (n->num_msgs == 0) - browser_gone(n->name.name, n->send_ip); - break; - } - - case NAME_RELEASE: - { - /* if no response received, it must be OK for us to release the - name. nobody objected (including a potentially dead or deaf - WINS server) */ - - /* IMPORTANT: see response_name_release() */ - - if (ismyip(n->send_ip)) - { - name_unregister_work(d,n->name.name,n->name.name_type); - } - if (!n->bcast && n->num_msgs == 0) - { - DEBUG(0,("WINS server did not respond to name release!\n")); - /* XXXX whoops. we have problems. must deal with this */ - } - break; - } - - case NAME_REGISTER_CHALLENGE: - { - /* name challenge: no reply. we can reply to the person that - wanted the unique name and tell them that they can have it - */ - - add_name_respond(d,n->fd,d->myip, n->reply_id ,&n->name, - n->nb_flags, GET_TTL(0), - n->reply_to_ip, True, n->reply_to_ip); - break; - } - - case NAME_REGISTER: - { - /* if no response received, and we are using a broadcast registration - method, it must be OK for us to register the name: nobody objected - on that subnet. if we are using a WINS server, then the WINS - server must be dead or deaf. - */ - if (n->num_msgs == 0) - { - if (n->bcast) - { - /* broadcast method: implicit acceptance of the name registration - by not receiving any objections. */ - - /* IMPORTANT: see response_name_reg() */ - - name_register_work(d,n->name.name,n->name.name_type, - n->nb_flags, n->ttl, n->reply_to_ip, n->bcast); - } - else - { - /* received no response. rfc1001.txt states that after retrying, - we should assume the WINS server is dead, and fall back to - broadcasting (see bits about M nodes: can't find any right - now) */ - - DEBUG(1,("WINS server did not respond to name registration!\n")); - /* XXXX whoops. we have problems. must deal with this */ - } - } - break; - } - - case NAME_QUERY_DOMAIN: - { - /* if no response was received, there is no domain controller for - this DOMAIN registered within WINS. it's ok for us to register - the DOMAIN<1b> name. - */ - - if (n->num_msgs == 0) - { - struct work_record *work = find_workgroupstruct(d,n->name.name,False); - if (work && d) - { - become_domain_master(d,work); - } - } - else - { - DEBUG(1, ("nmbd configured as domain master and one already exists\n")); - } - break; - } - - default: - { - /* nothing to do but delete the dead expected-response structure */ - /* this is normal. */ - break; - } - } -} - - -/******************************************************************* - remove old name response entries - - XXXX retry code needs to be added, including a retry wait period and a count - see name_query() and name_status() for suggested implementation. - - ******************************************************************/ -void expire_netbios_response_entries(time_t t) -{ - struct subnet_record *d; - - for (d = FIRST_SUBNET; d; d = NEXT_SUBNET_INCLUDING_WINS(d)) - { - struct response_record *n, *nextn; - - for (n = d->responselist; n; n = nextn) - { - nextn = n->next; - - if (n->repeat_time <= t) - { - if (n->repeat_count > 0) - { - /* resend the entry */ - initiate_netbios_packet(&n->response_id, n->fd, n->quest_type, - n->name.name, n->name.name_type, - n->nb_flags, n->bcast, n->recurse, n->send_ip); - - n->repeat_time += n->repeat_interval; /* XXXX ms needed */ - n->repeat_count--; - - } - else - { - DEBUG(4,("timeout response %d for %s %s\n", - n->response_id, namestr(&n->name), - inet_ntoa(n->send_ip))); - - dead_netbios_entry(d,n); /* process the non-response */ - remove_response_record(d,n); /* remove the non-response */ - - continue; - } - } - } - } -} - - -/**************************************************************************** - wrapper function to override a broadcast message and send it to the WINS - name server instead, if it exists. if wins is false, and there has been no - WINS server specified, the packet will NOT be sent. - ****************************************************************************/ -struct response_record *queue_netbios_pkt_wins( - int fd,int quest_type,enum state_type state, - char *name,int name_type,int nb_flags, time_t ttl, - int server_type, char *my_name, char *my_comment, - struct in_addr send_ip, struct in_addr reply_to_ip) -{ - /* XXXX note: please see rfc1001.txt section 10 for details on this - function: it is currently inappropriate to use this - it will do - for now - once there is a clarification of B, M and P nodes and - which one samba is supposed to be - */ - - if ((!lp_wins_support()) && (*lp_wins_server())) - { - /* samba is not a WINS server, and we are using a WINS server */ - struct in_addr real_wins_ip; - real_wins_ip = *interpret_addr2(lp_wins_server()); - - if (!zero_ip(real_wins_ip)) - { - send_ip = real_wins_ip; - } - else - { - /* oops. smb.conf's wins server parameter MUST be a host_name - or an ip_address. */ - DEBUG(0,("invalid smb.conf parameter 'wins server'\n")); - } - } - - if (zero_ip(send_ip)) return NULL; - - return queue_netbios_packet(wins_client_subnet,fd, quest_type, state, - name, name_type, nb_flags, ttl, - server_type,my_name,my_comment, - False, True, send_ip, reply_to_ip, 0); -} - - -/**************************************************************************** - initiate a netbios name query to find someone's or someones' IP - this is intended to be used (not exclusively) for broadcasting to - master browsers (WORKGROUP(1d or 1b) or __MSBROWSE__(1)) to get - complete lists across a wide area network - ****************************************************************************/ -struct response_record *queue_netbios_packet(struct subnet_record *d, - int fd,int quest_type,enum state_type state,char *name, - int name_type,int nb_flags, time_t ttl, - int server_type, char *my_name, char *my_comment, - BOOL bcast,BOOL recurse, - struct in_addr send_ip, struct in_addr reply_to_ip, - int reply_id) -{ - struct response_record *n; - uint16 id = 0xffff; - - /* ha ha. no. do NOT broadcast to 255.255.255.255: it's a pseudo address */ - if (ip_equal(wins_ip, send_ip)) return NULL; - - initiate_netbios_packet(&id, fd, quest_type, name, name_type, - nb_flags, bcast, recurse, send_ip); - - if (id == 0xffff) { - DEBUG(4,("did not initiate netbios packet: %s\n", inet_ntoa(send_ip))); - return NULL; - } - - if ((n = make_response_queue_record(state,id,fd, - quest_type,name,name_type,nb_flags,ttl, - server_type,my_name, my_comment, - bcast,recurse,send_ip,reply_to_ip, - reply_id))) - { - add_response_record(d,n); - return n; - } - return NULL; -} diff --git a/source3/nameresp.doc b/source3/nameresp.doc deleted file mode 100644 index cfe63500c8..0000000000 --- a/source3/nameresp.doc +++ /dev/null @@ -1,178 +0,0 @@ -/* - Unix SMB/Netbios documentation. - Version 0.0 - Copyright (C) Luke Leighton Andrew Tridgell 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. - - Document name: nameresp.doc - - Revision History: - - 0.0 - 02jul96 : lkcl@pires.co.uk - created -*/ - -the netbios expected response code is a key part of samba's NetBIOS -handling capabilities. it allows samba to carry on dealing with -other things while expecting a response from one or more hosts. - -this allows samba to simultaneously deal with registering its names -with another WINS server, register its names on its local subnets, -query any hosts that have registered with samba in its capacity as -a WINS server, and at a later date it will be also be able handle -END-NODE CHALLENGES (see rfc1001.txt 15.2.2.2 and 15.2.2.3 - secured -NBNS functionality). - -all at once! - -when a netbios packet is sent out by samba and it expects a response, -a record of all the relevant information is kept (most importantly, -the unique transaction id associated which will come back to us in -a response packet is recorded, and also recorded is the reason that -the original packet was sent out by samba in the first place!). - -if a response is received, then the unique transaction identifier -returned in the response packet is searched for in the expected -response records. the record indicates why the initial request was -made (and therefore the type of response can be verified) and -appropriate action can be taken. - -when no responses, after a number of retries, are not received, then -samba may take appropriate action. this is a crucial part of samba's -operation: for a key number of NetBIOS operations, no response is an -implicit positive response. - -module nameresp deals with the initial transmission, re-transmission -and time-out of netbios response records. - -module namedbresp deals with the maintenance of the list of expected -responses - creation, finding and removal. - - -/************************************************************************* - queue_netbios_packet() - *************************************************************************/ - -this function is responsible for sending out a netbios packet, and then -making a record of the information that was sent out. a response will -be expected later (or not, as the case may be). - -if a response is received, response_netbios_packet() will deal with it. -otherwise, it will be dealt with in expire_netbios_response_entries(). - - -/************************************************************************* - queue_netbios_pkt_wins() - *************************************************************************/ - -this function is a wrapper around queue_netbios_packet(). there is -some confusion about B, M and P nodes (see rfc1001.txt section 10) - -confusion introduced by luke :-) - which needs sorting out. - -for example, rfc1001.txt 15.2.3 - an M node must attempt to register a -name first as a B node, then attempt to register as an M node. negative -responses on either of these attempts is a failure to register the -name. - -this is NOT the case with a P node. - - -/************************************************************************* - expire_netbios_response_entries() - *************************************************************************/ - -this function is responsible for dealing with queued response records -that have not received a response packet matching their unique -transaction id. - -if the retry count for any record is non-zero, and its time-out period -has expired, the retry count is reduced, the time-out period is stepped -forward and the packet is re-transmitted (from the information stored -in the queued response record) with the same unique transaction id of -the initial attempt at soliciting a response. - -if the retry count is zero, then the packet is assumed to have expired. -dead_netbios_entry() is called to deal with the possibility of an error -or a problem (or in certain instances, no answer is an implicit -positive response!). - -the expected response record is then deleted, and the number of expected -responses reduced. when this count gets to zero, listen_for_packets() -will no longer time-out for 1 second on account of expecting response -packets. - - -/************************************************************************* - dead_netbios_entry() - *************************************************************************/ - -this function is responsible for dealing with the case when a NetBIOS -response to a packet sent out by samba was not received. for certain -transactions, this may be normal. for others, under certain conditions -it may constitute either an error or a problem with or failure of one -or more hosts. - -- NAME_QUERY_CONFIRM - -when a samba 'state' of type NAME_QUERY_CONFIRM is sent, a response -may or may not be forthcoming. if no response is received to a unique -name, then the record is removed from samba's WINS database. non-unique -names are simply expected to die off on a time-to-live basis (see -rfc1001.txt 15.1.3.4) - -query_refresh_names() issues this samba 'state' -response_name_query_sync() deals with responses to NAME_QUERY_CONFIRM. - -- NAME_QUERY_MST_CHK - -when a samba 'state' of type NAME_QUERY_MST_CHK is sent, and a response -is not received, this implies that a master browser will have failed. -remedial action may need to be taken, for example if samba is a member -of that workgroup and it is also a potential master browser it could -force an election. - -check_master_browser() issues this samba 'state'. -response_process() does nothing if a response is received. this is normal. - -- NAME_RELEASE - -when a samba 'state' of type NAME_RELEASE is sent, and a response is -not received, it is assumed to be acceptable to release the name. if the -original response was sent to another WINS server, then that WINS server -may be inaccessible or may have failed. if so, then at a later date -samba should take this into account (see rfc1001.txt 10.3). - -remove_name_entry() issues this samba 'state' -response_name_rel() deals with responses to NAME_RELEASE. - -- NAME_REGISTER - -when a samba 'state' of type NAME_REGISTER is sent, and a response is -not received, if the registration was done by broadcast, it is assumed -that there are no objections to the registration of this name, and samba -adds the name to the appropriate subnet record name database. if the -registration was point-to-point (i.e with another WINS server) then that -WINS server may be inaccessible or may have failed. if so, then at a later -date samba should take this into account (see rfc1001.txt 10.3). - -add_my_name_entry() issues this samba 'state' -response_name_reg() deals with responses to NAME_REGISTER. - -no action is taken for any other kinds of samba 'states' if a response -is not received. this is not to say that action may not be appropriate, -just that it's not been looked at yet :-) - - diff --git a/source3/nameserv.c b/source3/nameserv.c deleted file mode 100644 index a05db3983e..0000000000 --- a/source3/nameserv.c +++ /dev/null @@ -1,498 +0,0 @@ -/* - Unix SMB/Netbios implementation. - Version 1.9. - NBT netbios routines and daemon - version 2 - 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 - 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. - - Module name: nameserv.c - - Revision History: - - 14 jan 96: lkcl@pires.co.uk - added multiple workgroup domain master support - - 04 jul 96: lkcl@pires.co.uk - module nameserv contains name server management functions -*/ - -#include "includes.h" - -extern int ClientNMB; - -extern int DEBUGLEVEL; - -extern pstring scope; -extern pstring myname; -extern fstring myworkgroup; -extern char **my_netbios_names; -extern struct in_addr ipzero; -extern struct in_addr wins_ip; - -extern struct subnet_record *subnetlist; - -extern uint16 nb_type; /* samba's NetBIOS type */ - -/**************************************************************************** - remove an entry from the name list - - note: the name will _always_ be removed - XXXX at present, the name is removed _even_ if a WINS server says keep it. - - ****************************************************************************/ -void remove_name_entry(struct subnet_record *d, char *name,int type) -{ - /* XXXX BUG: if samba is offering WINS support, it should still broadcast - a de-registration packet to the local subnet before removing the - name from its local-subnet name database. */ - - struct name_record n; - struct name_record *n2=NULL; - - make_nmb_name(&n.name,name,type,scope); - - if ((n2 = find_name_on_subnet(d, &n.name, FIND_SELF_NAME))) - { - /* check name isn't already being de-registered */ - if (NAME_DEREG(n2->ip_flgs[0].nb_flags)) - return; - - /* mark the name as in the process of deletion. */ - n2->ip_flgs[0].nb_flags &= NB_DEREG; - } - - if (!n2) return; - - /* Only remove names with non-zero death times. */ - if(n2->death_time == 0) - { - DEBUG(5,("remove_name_entry: Name %s(%d) has zero ttl - not removing.\n", - name, type)); - return; - } - - /* remove the name immediately. even if the spec says we should - first try to release them, this is too dangerous with our current - name structures as otherwise we will end up replying to names we - don't really own */ - remove_netbios_name(d,name,type,SELF); - - if (ip_equal(d->bcast_ip, wins_ip)) - { - if (!lp_wins_support()) - { - /* not a WINS server: we have to release them on the network */ - queue_netbios_pkt_wins(ClientNMB,NMB_REL,NAME_RELEASE, - name, type, 0, 0,0,NULL,NULL, - ipzero, ipzero); - } - } - else - { - /* local interface: release them on the network */ - queue_netbios_packet(d,ClientNMB,NMB_REL,NAME_RELEASE, - name, type, 0, 0,0,NULL,NULL, - True, False, d->bcast_ip, d->bcast_ip, 0); - } -} - - -/**************************************************************************** - add an entry to the name list - - big note: our name will _always_ be added (if there are no objections). - it's just a matter of when this will be done (e.g after a time-out). - - ****************************************************************************/ -void add_my_name_entry(struct subnet_record *d,char *name,int type,int nb_flags) -{ - BOOL re_reg = False; - struct nmb_name n; - - if (!d) return; - - /* not that it particularly matters, but if the SELF name already exists, - it must be re-registered, rather than just registered */ - - make_nmb_name(&n, name, type, scope); - if (find_name_on_subnet(d, &n, FIND_SELF_NAME)) - re_reg = True; - - /* XXXX BUG: if samba is offering WINS support, it should still add the - name entry to a local-subnet name database. see rfc1001.txt 15.1.1 p28 - regarding the point about M-nodes. */ - - if (ip_equal(d->bcast_ip, wins_ip)) - { - if (lp_wins_support()) - { - /* we are a WINS server. */ - if(lp_wins_support()) - { - DEBUG(4,("add_my_name_entry: samba as WINS server adding: ")); - } - - /* this will call add_netbios_entry() */ - name_register_work(d, name, type, nb_flags,0, ipzero, False); - } - else - { - DEBUG(4,("add_my_name_entry registering name %s with WINS server.\n", - name)); - - /* a time-to-live allows us to refresh this name with the WINS server. */ - queue_netbios_pkt_wins(ClientNMB, - re_reg ? NMB_REG_REFRESH : NMB_REG, NAME_REGISTER, - name, type, nb_flags, GET_TTL(0),0,NULL,NULL, - ipzero, ipzero); - } - } - else - { - /* broadcast the packet */ - queue_netbios_packet(d,ClientNMB, - re_reg ? NMB_REG_REFRESH : NMB_REG, NAME_REGISTER, - name, type, nb_flags, GET_TTL(0),0,NULL,NULL, - True, False, d->bcast_ip, ipzero, 0); - } -} - - -/**************************************************************************** - add the internet group <1c> domain logon names by wins unicast and broadcast. - ****************************************************************************/ -void add_domain_logon_names(void) -{ - struct subnet_record *d; - - if (!lp_domain_logons()) return; - - for (d = FIRST_SUBNET; d; d = NEXT_SUBNET_INCLUDING_WINS(d)) - { - struct work_record *work = find_workgroupstruct(d, myworkgroup, True); - - if (work && work->log_state == LOGON_NONE) - { - struct nmb_name n; - make_nmb_name(&n,myworkgroup,0x1c,scope); - - if (!find_name_on_subnet(d, &n, FIND_SELF_NAME)) - { - /* logon servers are group names. don't expect failure */ - - DEBUG(0,("%s attempting to become logon server for %s %s\n", - timestring(), myworkgroup, inet_ntoa(d->bcast_ip))); - - become_logon_server(d, work); - } - } - } -} - - -/**************************************************************************** - add the <1b> domain master names by broadcast. - ****************************************************************************/ -void add_domain_master_bcast(void) -{ - struct subnet_record *d; - - if (!lp_domain_master()) return; - - for (d = FIRST_SUBNET; d; d = NEXT_SUBNET_EXCLUDING_WINS(d)) - { - struct work_record *work = find_workgroupstruct(d, myworkgroup, True); - - if (work && work->dom_state == DOMAIN_NONE) - { - struct nmb_name n; - make_nmb_name(&n,myworkgroup,0x1b,scope); - - if (!find_name_on_subnet(d, &n, FIND_SELF_NAME)) - { - DEBUG(0,("%s add_domain_names: attempting to become domain \ -master browser on workgroup %s %s\n", timestring(), myworkgroup, inet_ntoa(d->bcast_ip))); - - /* send out a query to establish whether there's a - domain controller on the local subnet. if not, - we can become a domain controller. it's only - polite that we check, before claiming the - NetBIOS name 0x1b. - */ - - DEBUG(0,("add_domain_names:querying subnet %s \ -for domain master on workgroup %s\n", inet_ntoa(d->bcast_ip), myworkgroup)); - - queue_netbios_packet(d,ClientNMB,NMB_QUERY, - NAME_QUERY_DOMAIN, - myworkgroup, 0x1b, - 0, 0,0,NULL,NULL, - True, False, - d->bcast_ip, d->bcast_ip, 0); - } - } - } -} - - -/**************************************************************************** - add the <1b> domain master name by wins unicast. - ****************************************************************************/ -void add_domain_master_wins(void) -{ - struct work_record *work; - - if (!lp_domain_master() || wins_client_subnet == NULL) return; - - work = find_workgroupstruct(wins_client_subnet, myworkgroup, True); - - if (work && work->dom_state == DOMAIN_NONE) - { - struct nmb_name n; - make_nmb_name(&n,myworkgroup,0x1b,scope); - - if (!find_name_on_subnet(wins_client_subnet, &n, FIND_SELF_NAME)) - { - DEBUG(0,("%s add_domain_names: attempting to become domain \ -master browser on workgroup %s %s\n", - timestring(), myworkgroup, inet_ntoa(wins_client_subnet->bcast_ip))); - - if (lp_wins_support()) - { - /* use the wins server's capabilities (indirectly). if - someone has already registered the domain<1b> - name with the WINS server, then the WINS - server's job is to _check_ that the owner still - wants it, before giving it away. - */ - - DEBUG(1,("%s initiate become domain master for %s\n", - timestring(), myworkgroup)); - - become_domain_master(wins_client_subnet, work); - } - else - { - /* send out a query to establish whether there's a - domain controller on the WINS subnet. if not, - we can become a domain controller. it's only - polite that we check, before claiming the - NetBIOS name 0x1b. - */ - - DEBUG(0,("add_domain_names:querying WINS \ -for domain master on workgroup %s\n", myworkgroup)); - - queue_netbios_pkt_wins(ClientNMB,NMB_QUERY, - NAME_QUERY_DOMAIN, - myworkgroup, 0x1b, - 0, 0,0,NULL,NULL, - ipzero, ipzero); - } - } - } -} - - -/**************************************************************************** - add the domain logon server and domain master browser names - - this code was written so that several samba servers can co-operate in - sharing the task of (one server) being a domain master, and of being - domain logon servers. - - **************************************************************************/ -void add_domain_names(time_t t) -{ - static time_t lastrun = 0; - - if (lastrun != 0 && t < lastrun + CHECK_TIME_ADD_DOM_NAMES * 60) return; - lastrun = t; - - /* do the "internet group" - <1c> names */ - add_domain_logon_names(); - - /* do the domain master names */ - if (wins_client_subnet != NULL) - { - /* if the registration of the <1b> name is successful, then - add_domain_master_bcast() will be called. this will - result in domain logon services being gracefully provided, - as opposed to the aggressive nature of 1.9.16p2 to 1.9.16p11. - - which, due to a bug in namelogon.c from 1.9.16p2 to 1.9.16p11 - cannot _provide_ domain master / domain logon services!!! - - */ - add_domain_master_wins(); - } - else - { - add_domain_master_bcast(); - } -} - -/**************************************************************************** - add the magic samba names, useful for finding samba servers - **************************************************************************/ -void add_my_names(void) -{ - struct subnet_record *d; - /* each subnet entry, including WINS pseudo-subnet, has SELF names */ - - /* XXXX if there was a transport layer added to samba (ipx/spx etc) then - there would be yet _another_ for-loop, this time on the transport type - */ - - for (d = FIRST_SUBNET; d; d = NEXT_SUBNET_INCLUDING_WINS(d)) - { - int n; - - /* Add all our names including aliases. */ - for (n=0; my_netbios_names[n]; n++) - { - add_my_name_entry(d, my_netbios_names[n],0x20,nb_type|NB_ACTIVE); - add_my_name_entry(d, my_netbios_names[n],0x03,nb_type|NB_ACTIVE); - add_my_name_entry(d, my_netbios_names[n],0x00,nb_type|NB_ACTIVE); - } - - /* these names are added permanently (ttl of zero) and will NOT be - refreshed with the WINS server */ - add_netbios_entry(d,"*",0x0,nb_type|NB_ACTIVE,0,SELF,d->myip,False); - add_netbios_entry(d,"*",0x20,nb_type|NB_ACTIVE,0,SELF,d->myip,False); - add_netbios_entry(d,"__SAMBA__",0x20,nb_type|NB_ACTIVE,0,SELF,d->myip,False); - add_netbios_entry(d,"__SAMBA__",0x00,nb_type|NB_ACTIVE,0,SELF,d->myip,False); - } -} - - -/**************************************************************************** - remove all the samba names... from a WINS server if necessary. - **************************************************************************/ -void remove_my_names() -{ - struct subnet_record *d; - - for (d = FIRST_SUBNET; d; d = NEXT_SUBNET_INCLUDING_WINS(d)) - { - struct name_record *n, *next; - - for (n = d->namelist; n; n = next) - { - next = n->next; - if (n->source == SELF) - { - /* get all SELF names removed from the WINS server's database */ - /* XXXX note: problem occurs if this removes the wrong one! */ - - remove_name_entry(d,n->name.name, n->name.name_type); - } - } - } -} - - -/******************************************************************* - refresh my own names - ******************************************************************/ -void refresh_my_names(time_t t) -{ - struct subnet_record *d; - - for (d = FIRST_SUBNET; d; d = NEXT_SUBNET_INCLUDING_WINS(d)) - { - struct name_record *n; - - for (n = d->namelist; n; n = n->next) - { - /* each SELF name has an individual time to be refreshed */ - if (n->source == SELF && n->refresh_time < t && - n->death_time != 0) - { - add_my_name_entry(d,n->name.name,n->name.name_type, - n->ip_flgs[0].nb_flags); - /* they get a new lease on life :-) */ - n->death_time += GET_TTL(0); - n->refresh_time += GET_TTL(0); - } - } - } -} - - -/******************************************************************* - queries names occasionally. an over-cautious, non-trusting WINS server! - - this function has been added because nmbd could be restarted. it - is generally a good idea to check all the names that have been - reloaded from file. - - XXXX which names to poll and which not can be refined at a later date. - ******************************************************************/ -void query_refresh_names(time_t t) -{ - struct name_record *n; - struct subnet_record *d = wins_client_subnet; - - static time_t lasttime = 0; - - int count = 0; - int name_refresh_time = NAME_POLL_REFRESH_TIME; - int max_count = name_refresh_time * 2 / NAME_POLL_INTERVAL; - if (max_count > 10) max_count = 10; - - name_refresh_time = NAME_POLL_INTERVAL * max_count / 2; - - /* if (!lp_poll_wins()) return; polling of registered names allowed */ - - if (!d) return; - - if (!lasttime) lasttime = t; - if (t - lasttime < NAME_POLL_INTERVAL) return; - - lasttime = time(NULL); - - for (n = d->namelist; n; n = n->next) - { - /* only do unique, registered names */ - - if (n->source != REGISTER) continue; - if (!NAME_GROUP(n->ip_flgs[0].nb_flags)) continue; - - if (n->refresh_time < t) - { - DEBUG(3,("Polling name %s\n", namestr(&n->name))); - - queue_netbios_packet(d,ClientNMB,NMB_QUERY,NAME_QUERY_CONFIRM, - n->name.name, n->name.name_type, - 0,0,0,NULL,NULL, - False,False,n->ip_flgs[0].ip,n->ip_flgs[0].ip, - 0); - count++; - } - - if (count >= max_count) - { - /* don't do too many of these at once, but do enough to - cover everyone in the list */ - return; - } - - /* this name will be checked on again, if it's not removed */ - n->refresh_time += name_refresh_time; - } -} - diff --git a/source3/nameserv.doc b/source3/nameserv.doc deleted file mode 100644 index af4934ade2..0000000000 --- a/source3/nameserv.doc +++ /dev/null @@ -1,159 +0,0 @@ -/* - Unix SMB/Netbios documentation. - Version 0.0 - Copyright (C) Luke Leighton Andrew Tridgell 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. - - Document name: nameserv.doc - - Revision History: - - 0.0 - 02jul96 : lkcl@pires.co.uk - created -*/ - -this module deals with general maintenance of NetBIOS names. - -/************************************************************************* - query_refresh_names() - *************************************************************************/ - -this function is responsible for polling all names registered in the -WINS database. it is planned to enable this function should samba -detect an inconsistency on the network, which could occur if the -samba NetBIOS daemon dies and is restarted. - -polling is done very infrequently, but all names will be covered -within a period NAME_POLL_REFRESH_TIME. a group of at most ten names -will be queried at once, at intervals of NAME_POLL_INTERVAL seconds. -if the total number of names queried in this way will take too long, -then the time that an individual name will next be polled is -increased accordingly. - -name query polling is functionality over-and-above the normal -requirement (see rfc1001.txt 15.1.7 point 7). it is normally the -responsibility of the owner of a name to re-register the name at -regular intervals. - - -/************************************************************************* - refresh_my_names() - *************************************************************************/ - -this function is responsible for refreshing samba's names that have -been registered with other servers on a local subnet, or with another -WINS server if samba is using one. - -samba's names' refresh_time will be updated through the use of the function -add_my_name_entry(). - - -/************************************************************************* - remove_my_names() - *************************************************************************/ - -this function is responsible for removing all samba's SELF names. it -is used when samba receives a SIG_TERM. samba at present does not wait -for the WINS server to reply to the name releases sent out. - - -/************************************************************************* - add_my_names() - *************************************************************************/ - -this function is responsible for adding and registering if necessary all -samba's SELF names, on each of its local subnets and with another WINS -server if samba is using one. - -/************************************************************************* - add_my_name_entry() - *************************************************************************/ - -this function is responsible for registering or re-registering one of -samba's names, either on the local subnet or with another WINS server -if samba is using one. - -if the name is already in samba's database, then it is re-registered, -otherwise it is simply registered. - -if the name is being registered in a WINS capacity (the subnet to which -the name should be added is the WINS pseudo-subnet) then we add the entry -immediately if samba is a WINS server. it uses name_register_work() -because if the name is being added as part of becoming a master browser, -we want to carry on that process. if the name is registered with another -WINS server, we must wait for an answer from that WINS server. either -name_register_work() or name_unregister_work() will be called as a result. - -if the name is being registered on a local subnet, then it is -broadcast. an explicit rejection from another host will result -in name_unregister_work() being called. no response will, after -retrying, result in name_register_work() being called. - -what ever method is used, the name will either be registered -or rejected, and what ever process was taking place (becoming -a master browser for example) will carry on. - -expire_netbios_response_entries() is responsible for taking further -action if no response to the registration is received. - -note that there may be a large number of function calls on the -stack if become_master() is called and samba is configured as -a WINS server. the loop will be: - -become_master(), add_my_name_entry(), name_register_work() and -back to become_master() with the new value of the workgroup -'state'. - - -/************************************************************************* - remove_name_entry() - *************************************************************************/ - -this function is responsible for removing a NetBIOS name. if the name -being removed is registered on a local subnet, a name release should be -broadcast on the local subnet. - -if the name is being released in a WINS capacity (the subnet to -which the name should be added is the WINS pseudo-subnet) then we -remove the entry immediately if samba is a WINS server. it uses -name_unregister_work() because if the name is being added as part of -becoming a master browser, we want to terminate that process. if the -name is released from another WINS server, we must wait for an -answer from that WINS server. name_unregister_work() will -definitely be called as a result, because at present we ignore -negative responses for a name release from a WINS server. - -if the name is being releasedd on a local subnet, then it is -broadcast. name_unregister_work() will definitely be called -because we ignore negative name releases at present. - -what ever method is used, the name will be released. (NOT TRUE! -see response_name_release()) - -expire_netbios_response_entries() is responsible for taking further action -if no response to the name release is received. - - -/************************************************************************* - load_netbios_names() - *************************************************************************/ - -this function is responsible for loading any NetBIOS names that samba, -in its WINS capacity, has written out to disk. all the relevant details -are recorded in this file, including the time-to-live. should the -time left to live be small, the name is not added back in to samba's -WINS database. - diff --git a/source3/nameservreply.c b/source3/nameservreply.c deleted file mode 100644 index 6c7bfde03e..0000000000 --- a/source3/nameservreply.c +++ /dev/null @@ -1,685 +0,0 @@ -/* - Unix SMB/Netbios implementation. - Version 1.9. - NBT netbios routines and daemon - version 2 - 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 - 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. - - Module name: nameservreply.c - - Revision History: - - 14 jan 96: lkcl@pires.co.uk - added multiple workgroup domain master support - - 04 jul 96: lkcl@pires.co.uk - created module nameservreply containing NetBIOS reply functions - -*/ - -#include "includes.h" - -extern int ClientNMB; - -extern int DEBUGLEVEL; - -extern struct in_addr wins_ip; - -/**************************************************************************** -send a registration / release response: pos/neg -**************************************************************************/ -static void send_name_response(int fd, struct in_addr from_ip, - int name_trn_id, int opcode, BOOL success, - BOOL recursion_available, BOOL recursion_desired, - struct nmb_name *reply_name, int nb_flags, int ttl, - struct in_addr ip) -{ - char rdata[6]; - struct packet_struct p; - - int rcode = 0; - - if (success == False) - { - /* NEGATIVE RESPONSE */ - rcode = 6; - } - else if (opcode == NMB_REG && !recursion_available) - { - /* END-NODE CHALLENGE REGISTRATION RESPONSE */ - rcode = 0; - } - - rdata[0] = nb_flags; - rdata[1] = 0; - putip(&rdata[2],(char *)&ip); - - p.ip = from_ip; - p.port = NMB_PORT; - p.fd = fd; - p.timestamp = time(NULL); - p.packet_type = NMB_PACKET; - - reply_netbios_packet(&p,name_trn_id, - rcode,opcode,opcode, - recursion_available, recursion_desired, - reply_name, 0x20, 0x1, - ttl, - rdata, 6); -} - -/**************************************************************************** - add a netbios entry. respond to the (possibly new) owner. - **************************************************************************/ -void add_name_respond(struct subnet_record *d, int fd, struct in_addr from_ip, - uint16 response_id, - struct nmb_name *name, - int nb_flags, int ttl, struct in_addr register_ip, - BOOL new_owner, struct in_addr reply_to_ip) -{ - /* register the old or the new owners' ip */ - add_netbios_entry(wins_client_subnet,name->name,name->name_type, - nb_flags,ttl,REGISTER,register_ip,False); - - /* reply yes or no to the host that requested the name */ - /* see rfc1002.txt - 4.2.10 and 4.2.11 */ - - send_name_response(fd, reply_to_ip, response_id, NMB_REG, - new_owner, - True, True, - name, nb_flags, ttl, reply_to_ip); -} - - -/**************************************************************************** -reply to a name release -****************************************************************************/ -void reply_name_release(struct packet_struct *p) -{ - struct nmb_packet *nmb = &p->packet.nmb; - struct in_addr ip; - int nb_flags = nmb->additional->rdata[0]; - BOOL bcast = nmb->header.nm_flags.bcast; - struct name_record *n; - struct subnet_record *d = NULL; - BOOL success = False; - - putip((char *)&ip,&nmb->additional->rdata[2]); - - DEBUG(3,("Name release on name %s\n", - namestr(&nmb->question.question_name))); - - if(!bcast) - d = wins_client_subnet; - else - d = find_subnet(p->ip); - - if (!d) - { - DEBUG(3,("response packet: can't match address %s to subnet\n", - inet_ntoa(p->ip))); - return; - } - - n = find_name_on_subnet(d, &nmb->question.question_name, FIND_ANY_NAME); - - /* XXXX under what conditions should we reject the removal?? */ - /* For now - remove if the names match and the group bit matches. */ - if (n && (n->source != SELF) && (NAME_GROUP(n->ip_flgs[0].nb_flags) == NAME_GROUP(nb_flags))) - { - success = True; - - /* If it's a group name not ending in 1c (not an internet name) - then just allow it to fade out of existance by timing out. */ - if(NAME_GROUP(nb_flags) && (n->name.name_type != 0x1c)) - { - DEBUG(5, ("reply_name_release: Allow group name %s(%d) to fade out on \ -subnet %s\n", namestr(&nmb->question.question_name), n->name.name_type, - inet_ntoa(d->bcast_ip))); - } - else - { - DEBUG(5, ("reply_name_release: Removing name %s on subnet %s\n", - namestr(&nmb->question.question_name), inet_ntoa(d->bcast_ip))); - remove_name(d,n); - n = NULL; - } - } - - if (bcast) return; - - /* Send a NAME RELEASE RESPONSE (pos/neg) see rfc1002.txt 4.2.10-11 */ - send_name_response(p->fd,p->ip, nmb->header.name_trn_id, NMB_REL, - success, False, False, - &nmb->question.question_name, nb_flags, 0, ip); -} - - -/**************************************************************************** -reply to a reg request -**************************************************************************/ -void reply_name_reg(struct packet_struct *p) -{ - struct nmb_packet *nmb = &p->packet.nmb; - struct nmb_name *question = &nmb->question.question_name; - - struct nmb_name *reply_name = question; - - char *qname = question->name; - int qname_type = question->name_type; - - BOOL bcast = nmb->header.nm_flags.bcast; - - int ttl = GET_TTL(nmb->additional->ttl); - int nb_flags = nmb->additional->rdata[0]; - BOOL group = NAME_GROUP(nb_flags); - - struct subnet_record *d = NULL; - struct name_record *n = NULL; - - BOOL success = True; - BOOL secured_redirect = False; - - struct in_addr ip, from_ip; - - putip((char *)&from_ip,&nmb->additional->rdata[2]); - ip = from_ip; - - DEBUG(3,("Name registration for name %s at %s - ", - namestr(question),inet_ntoa(ip))); - - if (group && (qname_type != 0x1c)) - { - /* apparently we should return 255.255.255.255 for group queries - (email from MS) */ - ip = *interpret_addr2("255.255.255.255"); - } - - if (!bcast) - d = wins_client_subnet; - else - d = find_subnet(p->ip); - - if (!d) - { - DEBUG(3,("reply_name_reg: can't match address %s to subnet\n", - inet_ntoa(p->ip))); - return; - } - - /* see if the name already exists */ - n = find_name_on_subnet(d, question, FIND_ANY_NAME); - - if (n) - { - DEBUG(3,("found\n")); - if (!group) /* unique names */ - { - if (n->source == SELF || NAME_GROUP(n->ip_flgs[0].nb_flags)) - { - /* no-one can register one of samba's names, nor can they - register a name that's a group name as a unique name */ - - success = False; - } - else if(!ip_equal(ip, n->ip_flgs[0].ip)) - { - /* XXXX rfc1001.txt says: - * if we are doing secured WINS, we must send a Wait-Acknowledge - * packet (WACK) to the person who wants the name, then do a - * name query on the person who currently owns the unique name. - * if the current owner still says they own it, the person who wants - * the name can't have it. if they do not, or are not alive, they can. - */ - - secured_redirect = True; - - reply_name = &n->name; - } - else - { - n->ip_flgs[0].ip = ip; - n->death_time = ttl?p->timestamp+ttl*3:0; - DEBUG(3,("%s owner: %s\n",namestr(&n->name),inet_ntoa(n->ip_flgs[0].ip))); - } - } - else - { - /* refresh the name */ - if (n->source != SELF) - { - n->death_time = ttl?p->timestamp + ttl*3:0; - } - } - - /* XXXX bug reported by terryt@ren.pc.athabascau.ca */ - /* names that people have checked for and not found get DNSFAILed. - we need to update the name record if someone then registers */ - - if (n->source == DNSFAIL) - n->source = REGISTER; - - } - else - { - DEBUG(3,("not found\n")); - /* add the name to our name/subnet, or WINS, database */ - n = add_netbios_entry(d,qname,qname_type,nb_flags,ttl,REGISTER,ip,True); - } - - /* if samba owns a unique name on a subnet, then it must respond and - disallow the attempted registration. if the registration is - successful by broadcast, only then is there no need to respond - (implicit registration: see rfc1001.txt 15.2.1). - */ - - if (bcast && success) return; - - if (secured_redirect) - { - char rdata[2]; - - /* XXXX i am confused. RSVAL or SSVAL? assume NMB byte ordering */ - RSSVAL(rdata,0,(nmb->header.opcode&0xf) + ((nb_flags&0xff) << 4)); - - /* XXXX mistake in rfc1002.txt? 4.2.16: NULL is 0xa see 4.2.1.3 - type = 0x0a; see rfc1002.txt 4.2.1.3 - class = 0x01; see rfc1002.txt 4.2.16 - */ - - /* send WAIT ACKNOWLEDGEMENT see rfc1002.txt 4.2.16 */ - reply_netbios_packet(p,nmb->header.name_trn_id, - 0,NMB_WAIT_ACK,NMB_WAIT_ACK, - False,False, - reply_name, 0x0a, 0x01, - 15*1000, /* 15 seconds long enough to wait? */ - rdata, 2); - - /* initiate some enquiries to the current owner. */ - queue_netbios_packet(d,ClientNMB,NMB_QUERY, - NAME_REGISTER_CHALLENGE, - reply_name->name,reply_name->name_type, - nb_flags,0,0,NULL,NULL, - False, False, - n->ip_flgs[0].ip, p->ip, - nmb->header.name_trn_id); - } - else - { - /* Send a NAME REGISTRATION RESPONSE (pos/neg) see rfc1002.txt 4.2.5-6 - or an END-NODE CHALLENGE REGISTRATION RESPONSE see rfc1002.txt 4.2.7 - */ - - send_name_response(p->fd,p->ip, nmb->header.name_trn_id, NMB_REG, - success, - True, True, - reply_name, nb_flags, ttl, ip); - } -} - -/* this is used to sort names for a name status into a sensible order - we put our own names first, then in alphabetical order */ -static int status_compare(char *n1,char *n2) -{ - extern pstring myname; - int l1,l2,l3; - - /* its a bit tricky because the names are space padded */ - for (l1=0;l1<15 && n1[l1] && n1[l1] != ' ';l1++) ; - for (l2=0;l2<15 && n2[l2] && n2[l2] != ' ';l2++) ; - l3 = strlen(myname); - - if ((l1==l3) && strncmp(n1,myname,l3) == 0 && - (l2!=l3 || strncmp(n2,myname,l3) != 0)) - return -1; - - if ((l2==l3) && strncmp(n2,myname,l3) == 0 && - (l1!=l3 || strncmp(n1,myname,l3) != 0)) - return 1; - - return memcmp(n1,n2,18); -} - - -/**************************************************************************** - reply to a name status query - - combine the list of the local interface on which the query was made with - the names registered via wins. - ****************************************************************************/ -void reply_name_status(struct packet_struct *p) -{ - struct nmb_packet *nmb = &p->packet.nmb; - char *qname = nmb->question.question_name.name; - int ques_type = nmb->question.question_name.name_type; - char rdata[MAX_DGRAM_SIZE]; - char *countptr, *buf, *bufend, *buf0; - int names_added,i; - struct name_record *n; - struct subnet_record *d = wins_client_subnet; - BOOL bcast = nmb->header.nm_flags.bcast; - - /* This query shoud only be made point to point. */ - if(bcast) - { - DEBUG(3,("Name status req: ignoring bcast from %s\n", - inet_ntoa(p->ip))); - return; - } - - if(d == NULL) - { - /* We are working broadcast only (no wins_client_subnet). - Use the first matching subnet. If none matches - then return. - */ - if((d = find_subnet(p->ip)) == NULL) - { - DEBUG(3,("Name status req: can't match address %s to subnet\n", - inet_ntoa(p->ip))); - return; - } - } - - DEBUG(3,("Name status for name %s from ip %s\n", - namestr(&nmb->question.question_name), - inet_ntoa(p->ip))); - - n = find_name_on_subnet(d, &nmb->question.question_name, FIND_SELF_NAME); - - if (!n) return; - - /* XXXX hack, we should calculate exactly how many will fit */ - bufend = &rdata[MAX_DGRAM_SIZE] - 18; - countptr = buf = rdata; - buf += 1; - buf0 = buf; - - names_added = 0; - - n = d->namelist; - - while (buf < bufend) - { - if (n->source == SELF) - { - int name_type = n->name.name_type; - - /* check if we want to exclude other workgroup names - from the response. if we don't exclude them, windows clients - get confused and will respond with an error for NET VIEW */ - - if (!strequal(n->name.name,"*") && - !strequal(n->name.name,"__SAMBA__") && - (name_type < 0x1b || name_type >= 0x20 || - ques_type < 0x1b || ques_type >= 0x20 || - strequal(qname, n->name.name))) - { - /* start with first bit of putting info in buffer: the name */ - bzero(buf,18); - sprintf(buf,"%-15.15s",n->name.name); - strupper(buf); - - /* put name type and netbios flags in buffer */ - buf[15] = name_type; - buf[16] = n->ip_flgs[0].nb_flags; - - buf += 18; - - names_added++; - } - } - - /* remove duplicate names */ - qsort(buf0,names_added,18,QSORT_CAST status_compare); - - for (i=1;inext; - - if (!n) - { - /* end of this name list: add wins names too? */ - struct subnet_record *w_d; - - if (!(w_d = wins_client_subnet)) break; - - if (w_d != d) - { - d = w_d; - n = d->namelist; /* start on the wins name list */ - } - } - if (!n) break; - } - - SCVAL(countptr,0,names_added); - - /* we don't send any stats as they could be used to attack - the protocol */ - bzero(buf,64); - - buf += 46; - - /* Send a POSITIVE NAME STATUS RESPONSE */ - reply_netbios_packet(p,nmb->header.name_trn_id, - 0,NMB_STATUS,0,False, False, - &nmb->question.question_name, - 0x21, 0x01, - 0, rdata,PTR_DIFF(buf,rdata)); -} - - -/*************************************************************************** -reply to a name query. - -with broadcast name queries: - - - only reply if the query is for one of YOUR names. all other machines on - the network will be doing the same thing (that is, only replying to a - broadcast query if they own it) - NOTE: broadcast name queries should only be sent out by a machine - if they HAVEN'T been configured to use WINS. this is generally bad news - in a wide area tcp/ip network and should be rectified by the systems - administrator. USE WINS! :-) - - the exception to this is if the query is for a Primary Domain Controller - type name (0x1b), in which case, a reply is sent. - - - NEVER send a negative response to a broadcast query. no-one else will! - -with directed name queries: - - - if you are the WINS server, you are expected to respond with either - a negative response, a positive response, or a wait-for-acknowledgement - packet, and then later on a pos/neg response. - -****************************************************************************/ -void reply_name_query(struct packet_struct *p) -{ - struct nmb_packet *nmb = &p->packet.nmb; - struct nmb_name *question = &nmb->question.question_name; - int name_type = question->name_type; - - BOOL bcast = nmb->header.nm_flags.bcast; - BOOL query_is_to_wins_server = (!bcast && - nmb->header.nm_flags.recursion_desired); - int ttl=0; - int rcode = 0; - int nb_flags = 0; - struct in_addr retip; - char rdata[6]; - struct subnet_record *d = NULL; - BOOL success = True; - struct name_record *n = NULL; - BOOL acting_as_wins_server = lp_wins_support(); - - /* directed queries are for WINS server: broadcasts are local SELF queries. - the exception is Domain Master names. */ - - if (query_is_to_wins_server) - { - /* queries to the WINS server involve the WINS server subnet */ - if (!(d = wins_client_subnet)) - { - DEBUG(3,("name query: wins server query from %s and no wins subnet being used.\n", - inet_ntoa(p->ip))); - success = False; - } - } - else - { - /* queries to the WINS client involve, unfortunately, the WINS subnet - because it contains WINS client (SELF) entries, as _well_ as WINS - server entries. not good. - */ - - if (!(d = find_subnet_all(p->ip))) - { - DEBUG(3,("name query: can't match address %s to subnet\n", - inet_ntoa(p->ip))); - success = False; - } - } - - DEBUG(3,("Name query from %s for name %s<0x%x>\n", - inet_ntoa(p->ip), question->name, question->name_type)); - - if (!bcast && (name_type == 0x1d) && lp_wins_support()) - { - /* see WINS manager HELP - 'How WINS Handles Special Names' */ - /* a WINS query (unicasted) for a 0x1d name must always return False */ - success = False; - } - - if (success) - { - /* look up the name in the cache */ - n = find_name_on_subnet(d, question, FIND_ANY_NAME); - - /* check for a previous DNS lookup (these are stored - on the wins_client_subnet name list, if it exists */ - - if (!n && wins_client_subnet && (d != wins_client_subnet) && - (n = find_name_on_subnet(wins_client_subnet, question, FIND_ANY_NAME))) { - if (n->source != DNS && n->source != DNSFAIL) { - n = NULL; - } else { - DEBUG(5,("Found DNS cache entry %s\n", namestr(&n->name))); - } - } - - /* it is a name that already failed DNS lookup or it's expired */ - if (n && (n->source == DNSFAIL || - (n->death_time && n->death_time < p->timestamp))) { - DEBUG(5,("expired name %s\n", namestr(&n->name))); - success = False; - } - - - /* do we want to do dns lookups? */ - if (success && !n && (lp_dns_proxy() || !bcast)) { - BOOL dns_type = (name_type == 0x20 || name_type == 0); - if (dns_type && wins_client_subnet) { - /* add it to the dns name query queue */ - if (queue_dns_query(p, question, &n)) - return; - } - } - } - - if (!n) success = False; - - if (success) - { - if (bcast && n->source != SELF && name_type != 0x1b) - { - /* don't respond to broadcast queries unless the query is for - a name we own or it is for a Primary Domain Controller name */ - - if (!lp_wins_proxy() || - same_net(p->ip,n->ip_flgs[0].ip,*iface_nmask(p->ip))) - { - /* never reply with a negative response to broadcast queries */ - return; - } - } - - /* name is directed query, or it's self, or it's a Domain Master type - name, or we're replying on behalf of a caller because they are on a - different subnet and cannot hear the broadcast. XXXX lp_wins_proxy - should be switched off in environments where broadcasts are forwarded - */ - - /* XXXX note: for proxy servers, we should forward the query on to - another WINS server if the name is not in our database, or we are - not a WINS server ourselves - */ - ttl = n->death_time ? n->death_time - p->timestamp : GET_TTL(0); - retip = n->ip_flgs[0].ip; - nb_flags = n->ip_flgs[0].nb_flags; - } - - if (!success && bcast) return; /* never reply negative response to bcasts */ - - /* if the IP is 0 then substitute my IP */ - if (zero_ip(retip)) retip = *iface_ip(p->ip); - - /* SPECIAL CASE... If we are a WINS server and the request is explicitly - *to* the WINS server and the name type is WORKGROUP<0x1e> we should - respond with the local broadcast address 255.255.255.255. - */ - if(!bcast && (name_type == 0x1e) && lp_wins_support()) - retip = *interpret_addr2("255.255.255.255"); - - if (success) - { - rcode = 0; - DEBUG(3,("OK %s\n",inet_ntoa(retip))); - } - else - { - rcode = 3; - DEBUG(3,("UNKNOWN\n")); - } - - if (success) - { - rdata[0] = nb_flags; - rdata[1] = 0; - putip(&rdata[2],(char *)&retip); - } - - /* see rfc1002.txt 4.2.13 */ - - reply_netbios_packet(p,nmb->header.name_trn_id, - rcode,NMB_QUERY,0, - (query_is_to_wins_server && acting_as_wins_server ? - True : False), /* recursion_available flag */ - True, /* recursion_desired_flag */ - &nmb->question.question_name, - 0x20, 0x01, - ttl, - rdata, success ? 6 : 0); -} diff --git a/source3/nameservreply.doc b/source3/nameservreply.doc deleted file mode 100644 index a5acf8a9c2..0000000000 --- a/source3/nameservreply.doc +++ /dev/null @@ -1,213 +0,0 @@ -/* - Unix SMB/Netbios documentation. - Version 0.0 - Copyright (C) Luke Leighton Andrew Tridgell 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. - - Document name: nameservreply.doc - - Revision History: - - 0.0 - 02jul96 : lkcl@pires.co.uk - created -*/ - -/************************************************************************* - reply_name_query() - *************************************************************************/ - -this function is responsible for replying to a NetBIOS name query. - -there are two kinds of name queries: directed, and broadcast. directed -queries are usually sent to samba in its WINS capacity. such hosts are -termed 'point-to-point' hosts. broadcast queries are usually sent from -'broadcast' or 'mixed' hosts. - -broadcasting is used by either older NetBIOS hosts, new NetBIOS hosts that -have not had WINS capabilities added and new NetBIOS hosts that think the -WINS server has died. - -the samba NetBIOS name database is divided into sections, on a -per-subnet basis. there is also a WINS NetBIOS name database, and for -convenience this is added as a pseudo-subnet with the ip address of -255.255.255.255. - -the local subnet NetBIOS name databases only contain samba's names. -the reason for this is that if a broadcast query is received, a NetBIOS -hosts is only expected to respond if that query is for one of its own -names (the exception to this is if a host is configured as a 'proxy' -server, in which case, samba should redirect the query to another WINS -server). - -the WINS pseudo-subnet NetBIOS database contains all NetBIOS names -that are not 'special browser' type names (regarding this i am a -_bit_ confused :-). names of type 0x01, 0x1d and 0x1e i consider to -be 'special browser' names. at the moment. maybe. - -the type of search to be initiated is determined. if the NetBIOS name -type is a non-special-browser name, then the WINS database is included -in the search. - -if the name is not a special browser name, then we need to find the -right subnet that the query came from. this is done using -find_req_subnet(). this also has the benefit of stopping any queries -from subnets that samba does not know about. - -if the query is a broadcast query, then the database of the local subnet -is included in the search. - -the name is then searched for in the appropriate NetBIOS data structures. -if it is found, then we need to check whether it is appropriate for us -to reply to such a query. - -we will only reply if the query is a directed query, the name belongs to -samba on that subnet, or the name is a domain master browser type, -or we're doing replies on behalf of hosts on subnets not known to the -host issuing the query. in the latter instance, it would be appropriate -if samba is using a WINS server for it to forward the name query on to -this WINS server. - -reply_name_query() then takes note of all the information that is -needed to construct a reply to the caller. a negative reply (if the -name is unknown to samba) or a positive reply (the name is known to -samba) is then issued. - - -/************************************************************************* - reply_name_status() - *************************************************************************/ - -this function is responsible for constructing a reply to a NetBIOS -name status query. this response contains all samba's NetBIOS names -on the subnet that the query came in from. - -a reply will only be made if the NetBIOS name being queried exists. - -see rfc1001.txt and rfc1002.txt for details of the name status reply. - - -/************************************************************************* - reply_name_reg() - *************************************************************************/ - -this function is responsible for updating the NetBIOS name database -from registration packets sent out by hosts wishing to register a -name, and for informing them, if necessary, if this is acceptable -or not. - -name registration can be done by broadcast or by point-to-point, -i.e the registration is sent directly to samba in its capacity as -a WINS server. - -if the name registration is done by broadcast (see rfc1001.txt 15.2.1), -then samba's involvement in replying is limited to whether that name -is owned by samba or not, on the relevant subnet. - -if the name registration is done point-to-point (see rfc1001.txt 15.2.2) -then samba will first need to check its WINS name database records and -proceed accordingly. - -samba looks for the appropriate subnet record that the registration -should be added to / checked against, using find_req_subnet(). - -next, the name is searched for in the local database or the WINS -database as appropriate. - -if the name is not found, then it is added to the NetBIOS name database, -using add_netbios_entry(), which may choose not to add the name (not -that this affects the registration of the name on the network in any way). -it will only add names to the WINS database, and even then it will only -add non-special-browser type names. - -if the name is found, then samba must decide whether to accept the name -or not. a group name is always added. for unique names, further checks -need to be carried out. - -firstly, if the name in the database is one of samba's names, or if the -name in the database is a group name, then it cannot be added as a unique -name belonging to someone else. it is therefore rejected. - -secondly, if the ip address of the name being registered does not match -against the ip in the database, then the unique name may belong to -someone else. a check needs to be carried out with the owner in case -they still wish to keep this name. a detailed discussion of what action -to take is in rfc1001.txt 15.2.2.2 and 15.2.2.3. - -samba currently implements non-secured WINS, whereupon the responsibility -for checking the name is passed on to the host doing the registration. -rfc1001.txt refers to this as an END-NODE CHALLENGE REGISTRATION RESPONSE. -(samba itself cannot yet cope with receiving such responses if it -registers its names with another WINS server). - -having decided what kind of response to send (if any - acceptance of -name registrations by broadcast is implicit), samba will send either a -positive or negative NAME REGISTRATION RESPONSE, or an END-NODE CHALLENGE -REGISTRATION RESPONSE to the host that initially sent the registration. - -whew. - - -/************************************************************************* - reply_name_release() - *************************************************************************/ - -this function is responsible for removing a NetBIOS name from the -database when a server sends a release packet. - -samba looks for the appropriate subnet record that the release should -be removed from, using find_req_subnet(). next, the name is searched -for in the local database or the WINS database as appropriate. - -if the name is found, it is removed from the database and a -positive reply is sent confirming this. if the name is not -found, a negative reply is sent. - -a reply is _not_ sent if the release was done by broadcast: the -release is implicit, and we should be grateful that they bothered -to tell us. if the release was done by directed packet, then -we deal with it as a WINS server and must reply (pos / neg). - -at present, the criteria for removing a name have yet to be -developed / experimented with. at present, the only flags that -are checked are the NetBIOS flags. - - -/************************************************************************* - send_name_response() - *************************************************************************/ - -this function is a wrap around reply_netbios_packet(). it sends -a response to a name registration or release packet, minimising -the function parameters needed to do this. - -if the function is called with the parameter 'success' set to -True, then a positive response (to the registration or release) -is made (see rfc1002.txt 4.2.5 and 4.2.10). if this parameter -is False, then a negative response is issued (see rfc1002.txt -4.2.6 and 4.2.11) - -if the function is called with a registration code, and the -parameter 'recurse' is False, then an End-Node Challenge -Registration response is issued (see rfc1002.txt 4.2.7) - -note: this function could also easily be used for name conflict -demand (see rfc1002.txt 4.2.8). - -note: End-Node Challenge Registration response is only sent in -non-secured NetBIOS Name Server implementations. samba now -implements secured NetBIOS Name Server functionality (see -rfc1001.txt 15.1.6). - diff --git a/source3/nameservresp.c b/source3/nameservresp.c deleted file mode 100644 index 3349610da6..0000000000 --- a/source3/nameservresp.c +++ /dev/null @@ -1,851 +0,0 @@ -/* - Unix SMB/Netbios implementation. - Version 1.9. - NBT netbios routines and daemon - version 2 - 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 - 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: - - Module name: nameservresp.c - - 14 jan 96: lkcl@pires.co.uk - added multiple workgroup domain master support - - 05 jul 96: lkcl@pires.co.uk - created module nameservresp containing NetBIOS response functions - -*/ - -#include "includes.h" - -extern int ClientNMB; - -extern int DEBUGLEVEL; - -extern pstring scope; -extern fstring myworkgroup; -extern struct in_addr ipzero; -extern struct in_addr wins_ip; -extern struct in_addr ipzero; - - -#define GET_TTL(ttl) ((ttl)?MIN(ttl,lp_max_ttl()):lp_max_ttl()) - - -/**************************************************************************** - response for a reg release received. samba has asked a WINS server if it - could release a name. - **************************************************************************/ -static void response_name_release(struct nmb_name *ans_name, - struct subnet_record *d, struct packet_struct *p) -{ - struct nmb_packet *nmb = &p->packet.nmb; - char *name = ans_name->name; - int type = ans_name->name_type; - - DEBUG(4,("response name release received\n")); - - if (nmb->header.rcode == 0 && nmb->answers && nmb->answers->rdata) - { - /* IMPORTANT: see expire_netbios_response_entries() */ - - struct in_addr found_ip; - putip((char*)&found_ip,&nmb->answers->rdata[2]); - - /* NOTE: we only release our own names at present */ - if (ismyip(found_ip)) - { - name_unregister_work(d,name,type); - } - else - { - DEBUG(2,("name release for different ip! %s %s\n", - inet_ntoa(found_ip), namestr(ans_name))); - } - } - else - { - DEBUG(2,("name release for %s rejected!\n", namestr(ans_name))); - - /* XXXX PANIC! what to do if it's one of samba's own names? */ - - /* XXXX do we honestly care if our name release was rejected? - only if samba is issuing the release on behalf of some out-of-sync - server. if it's one of samba's SELF names, we don't care. */ - } -} - - -/**************************************************************************** -response for a reg request received -**************************************************************************/ -static void response_name_reg(struct nmb_name *ans_name, - struct subnet_record *d, struct packet_struct *p) -{ - struct nmb_packet *nmb = &p->packet.nmb; - BOOL bcast = nmb->header.nm_flags.bcast; - char *name = ans_name->name; - int type = ans_name->name_type; - - DEBUG(4,("response name registration received!\n")); - -#if 1 - /* This code is neccesitated due to bugs in earlier versions of - Samba (up to 1.9.16p11). They respond to a broadcast - name registration of WORKGROUP<1b> when they should - not. Hence, until these versions are gone, we should - treat such errors as success for this particular - case only. jallison@whistle.com. - */ - if ( ((d != wins_client_subnet) && (nmb->header.rcode == 6) && strequal(myworkgroup, name) && - (type == 0x1b)) || - (nmb->header.rcode == 0 && nmb->answers && nmb->answers->rdata)) -#else - if (nmb->header.rcode == 0 && nmb->answers && nmb->answers->rdata) -#endif - { - /* IMPORTANT: see expire_netbios_response_entries() */ - - int nb_flags = nmb->answers->rdata[0]; - int ttl = nmb->answers->ttl; - struct in_addr found_ip; - - putip((char*)&found_ip,&nmb->answers->rdata[2]); - - name_register_work(d,name,type,nb_flags,ttl,found_ip,bcast); - } - else - { - DEBUG(2,("name registration for %s rejected by ip %s!\n", - namestr(ans_name), inet_ntoa(p->ip))); - - /* oh dear. we have problems. possibly unbecome a master browser. */ - name_unregister_work(d,name,type); - } -} - -/**************************************************************************** - response from a name query server check. states of type NAME_QUERY_DOM_SRV_CHK, - NAME_QUERY_SRV_CHK, and NAME_QUERY_FIND_MST dealt with here. - ****************************************************************************/ -static void response_server_check(struct nmb_name *ans_name, - struct response_record *n, struct subnet_record *d, struct packet_struct *p) -{ - struct nmb_packet *nmb = &p->packet.nmb; - struct in_addr send_ip; - enum state_type cmd; - - /* This next fix was from Bernhard Laeser - who noticed we were replying directly back to the server - we sent to - rather than reading the response. - */ - - if (nmb->header.rcode == 0 && nmb->answers && nmb->answers->rdata) - putip((char*)&send_ip,&nmb->answers->rdata[2]); - else - { - - DEBUG(2,("response_server_check: name query for %s failed\n", - namestr(ans_name))); - return; - } - - /* issue another state: this time to do a name status check */ - - cmd = (n->state == NAME_QUERY_DOM_SRV_CHK) ? - NAME_STATUS_DOM_SRV_CHK : NAME_STATUS_SRV_CHK; - - /* initiate a name status check on address given in the reply - record. In addition, the workgroup being checked has been stored - in the response_record->my_name (see announce_master) we - also propagate this into the same field. */ - queue_netbios_packet(d,ClientNMB,NMB_STATUS, cmd, - ans_name->name, ans_name->name_type, - 0,0,0,n->my_name,NULL, - False,False,send_ip,n->reply_to_ip, 0); -} - - -/**************************************************************************** - interpret a node status response. this is pretty hacked: we need two bits of - info. a) the name of the workgroup b) the name of the server. it will also - add all the names it finds into the namelist. -****************************************************************************/ -static BOOL interpret_node_status(struct subnet_record *d, - char *p, struct nmb_name *name,int t, - char *serv_name, struct in_addr ip, BOOL bcast) -{ - int numnames = CVAL(p,0); - BOOL found = False; - - DEBUG(4,("received %d names\n",numnames)); - - p += 1; - - if (serv_name) *serv_name = 0; - - while (numnames--) - { - char qname[17]; - int type; - fstring flags; - int nb_flags; - - BOOL group = False; - BOOL add = False; - - *flags = 0; - - StrnCpy(qname,p,15); - type = CVAL(p,15); - nb_flags = p[16]; - trim_string(qname,NULL," "); - - p += 18; - - if (NAME_GROUP (nb_flags)) { strcat(flags," "); group=True;} - if (NAME_BFLAG (nb_flags)) { strcat(flags,"B "); } - if (NAME_PFLAG (nb_flags)) { strcat(flags,"P "); } - if (NAME_MFLAG (nb_flags)) { strcat(flags,"M "); } - if (NAME_HFLAG (nb_flags)) { strcat(flags,"H "); } - if (NAME_DEREG (nb_flags)) { strcat(flags," "); } - if (NAME_CONFLICT (nb_flags)) { strcat(flags," "); } - if (NAME_ACTIVE (nb_flags)) { strcat(flags," "); add=True; } - if (NAME_PERMANENT(nb_flags)) { strcat(flags," "); add=True;} - - /* we want the server name */ - if (serv_name && !*serv_name && !group && type == 0x20) - { - StrnCpy(serv_name,qname,15); - serv_name[15] = 0; - } - - /* looking for a name and type? */ - if (name && !found && (t == type)) - { - /* take a guess at some of the name types we're going to ask for. - evaluate whether they are group names or no... */ - if (((t == 0x1b || t == 0x1d || t == 0x20 ) && !group) || - ((t == 0x1c || t == 0x1e ) && group)) - { - found = True; - make_nmb_name(name,qname,type,scope); - } - } - - DEBUG(4,("\t%s(0x%x)\t%s\n",qname,type,flags)); - } - DEBUG(4,("num_good_sends=%d num_good_receives=%d\n", - IVAL(p,20),IVAL(p,24))); - return found; -} - - -/**************************************************************************** - response from a name status check. states of type NAME_STATUS_DOM_SRV_CHK - and NAME_STATUS_SRV_CHK dealt with here. - ****************************************************************************/ -static void response_name_status_check(struct in_addr ip, - struct nmb_packet *nmb, BOOL bcast, - struct response_record *n, struct subnet_record *d) -{ - /* NMB_STATUS arrives: contains workgroup name and server name required. - amongst other things. */ - - struct nmb_name name; - fstring serv_name; - - if (nmb->answers && - interpret_node_status(d,nmb->answers->rdata, - &name,0x20,serv_name,ip,bcast)) - { - if (*serv_name) - { - /* response_record->my_name contains the - workgroup name to sync with. See - response_server_check() */ - sync_server(n->state,serv_name, - n->my_name,name.name_type, d, n->send_ip); - } - } - else - { - DEBUG(1,("No 0x20 name type in interpret_node_status()\n")); - } -} - - -/**************************************************************************** - response from a name query for secured WINS registration. a state of - NAME_REGISTER_CHALLENGE is dealt with here. - ****************************************************************************/ -static void response_name_query_register(struct nmb_packet *nmb, - struct nmb_name *ans_name, - struct response_record *n, struct subnet_record *d) -{ - struct in_addr register_ip; - BOOL new_owner; - - DEBUG(4, ("Name query at %s ip %s - ", - namestr(&n->name), inet_ntoa(n->send_ip))); - - if (!name_equal(&n->name, ans_name)) - { - /* someone gave us the wrong name as a reply. oops. */ - /* XXXX should say to them 'oi! release that name!' */ - - DEBUG(4,("unexpected name received: %s\n", namestr(ans_name))); - return; - } - - if (nmb->header.rcode == 0 && nmb->answers && nmb->answers->rdata) - { - /* we had sent out a name query to the current owner - of a name because someone else wanted it. now they - have responded saying that they still want the name, - so the other host can't have it. - */ - - /* first check all the details are correct */ - - int nb_flags = nmb->answers->rdata[0]; - struct in_addr found_ip; - - putip((char*)&found_ip,&nmb->answers->rdata[2]); - - if (nb_flags != n->nb_flags) - { - /* someone gave us the wrong nb_flags as a reply. oops. */ - /* XXXX should say to them 'oi! release that name!' */ - - DEBUG(4,("expected nb_flags: %d\n", n->nb_flags)); - DEBUG(4,("unexpected nb_flags: %d\n", nb_flags)); - return; - } - - if (!ip_equal(n->send_ip, found_ip)) - { - /* someone gave us the wrong ip as a reply. oops. */ - /* XXXX should say to them 'oi! release that name!' */ - - DEBUG(4,("expected ip: %s\n", inet_ntoa(n->send_ip))); - DEBUG(4,("unexpected ip: %s\n", inet_ntoa(found_ip))); - return; - } - - DEBUG(4, (" OK: %s\n", inet_ntoa(found_ip))); - - /* fine: now tell the other host they can't have the name */ - register_ip = n->send_ip; - new_owner = False; - } - else - { - DEBUG(4, (" NEGATIVE RESPONSE!\n")); - - /* the owner didn't want the name: the other host can have it */ - register_ip = n->reply_to_ip; - new_owner = True; - } - - /* register the old or the new owners' ip */ - add_name_respond(d, n->fd, d->myip, n->reply_id,&n->name,n->nb_flags, - GET_TTL(0), register_ip, - new_owner, n->reply_to_ip); - - remove_response_record(d,n); /* remove the response record */ -} - - -/**************************************************************************** - response from a name query to sync browse lists or to update our netbios - entry. states of type NAME_QUERY_SYNC and NAME_QUERY_CONFIRM - ****************************************************************************/ -static void response_name_query_sync(struct nmb_packet *nmb, - struct nmb_name *ans_name, BOOL bcast, - struct response_record *n, struct subnet_record *d) -{ - DEBUG(4, ("Name query at %s ip %s - ", - namestr(&n->name), inet_ntoa(n->send_ip))); - - if (!name_equal(&n->name, ans_name)) - { - /* someone gave us the wrong name as a reply. oops. */ - DEBUG(4,("unexpected name received: %s\n", namestr(ans_name))); - return; - } - - if (nmb->header.rcode == 0 && nmb->answers && nmb->answers->rdata) - { - int nb_flags = nmb->answers->rdata[0]; - struct in_addr found_ip; - - putip((char*)&found_ip,&nmb->answers->rdata[2]); - - if (!ip_equal(n->send_ip, found_ip)) - { - /* someone gave us the wrong ip as a reply. oops. */ - DEBUG(4,("expected ip: %s\n", inet_ntoa(n->send_ip))); - DEBUG(4,("unexpected ip: %s\n", inet_ntoa(found_ip))); - return; - } - - DEBUG(4, (" OK: %s\n", inet_ntoa(found_ip))); - - if (n->state == NAME_QUERY_SYNC_LOCAL || - n->state == NAME_QUERY_SYNC_REMOTE) - { - struct work_record *work = NULL; - /* We cheat here as we know that the workgroup name has - been placed in the my_comment field of the - response_record struct by the code in - start_sync_browse_entry(). - */ - if ((work = find_workgroupstruct(d, n->my_comment, False))) - { - BOOL local_list_only = n->state == NAME_QUERY_SYNC_LOCAL; - - /* the server is there: sync quick before it (possibly) dies! */ - sync_browse_lists(d, work, ans_name->name, ans_name->name_type, - found_ip, local_list_only); - } - } - else - { - struct subnet_record *add_rec = (!bcast) ? wins_client_subnet : d; - - /* update our netbios name list (re-register it if necessary) */ - add_netbios_entry(add_rec, ans_name->name, ans_name->name_type, - nb_flags,GET_TTL(0),REGISTER, - found_ip,False); - } - } - else - { - DEBUG(4, (" NEGATIVE RESPONSE!\n")); - - if (n->state == NAME_QUERY_CONFIRM) - { - /* XXXX remove_netbios_entry()? */ - /* lots of things we ought to do, here. if we get here, - then we're in a mess: our name database doesn't match - reality. sort it out - */ - remove_netbios_name(d,n->name.name, n->name.name_type, REGISTER); - } - } -} - -/**************************************************************************** - response from a name query for DOMAIN<1b> - NAME_QUERY_DOMAIN is dealt with here - we are trying to become a domain - master browser and WINS replied - check it's our address. - ****************************************************************************/ -static void response_name_query_domain(struct nmb_name *ans_name, - struct nmb_packet *nmb, - struct response_record *n, struct subnet_record *d) -{ - DEBUG(4, ("response_name_query_domain: Got %s response from %s for query \ -for %s\n", nmb->header.rcode == 0 ? "success" : "failure", - inet_ntoa(n->send_ip), namestr(ans_name))); - - /* Check the name is correct and ip address returned is our own. If it is then we - just remove the response record. - */ - if (name_equal(&n->name, ans_name) && (nmb->header.rcode == 0) && nmb->answers && (nmb->answers->rdata)) - { - struct in_addr found_ip; - - putip((char*)&found_ip,&nmb->answers->rdata[2]); - /* Samba 1.9.16p11 servers seem to return the broadcast address for this - query. */ - if (ismyip(found_ip) || ip_equal(wins_ip, found_ip) || ip_equal(ipzero, found_ip)) - { - DEBUG(4, ("response_name_query_domain: WINS server returned our ip \ -address. Pretending we never received response.\n")); - n->num_msgs = 0; - n->repeat_count = 0; - n->repeat_time = 0; - } - else - { - DEBUG(0,("response_name_query_domain: WINS server already has a \ -domain master browser registered %s at address %s\n", - namestr(ans_name), inet_ntoa(found_ip))); - } - } - else - { - /* Negative/incorrect response. No domain master - browser was registered - pretend we didn't get this response. - */ - n->num_msgs = 0; - n->repeat_count = 0; - n->repeat_time = 0; - } - -} - -/**************************************************************************** - report the response record type - ****************************************************************************/ -static void debug_rr_type(int rr_type) -{ - switch (rr_type) - { - case NMB_STATUS: DEBUG(3,("Name status ")); break; - case NMB_QUERY : DEBUG(3,("Name query ")); break; - case NMB_REG : DEBUG(3,("Name registration ")); break; - case NMB_REL : DEBUG(3,("Name release ")); break; - default : DEBUG(1,("wrong response packet type received")); break; - } -} - -/**************************************************************************** - report the response record nmbd state - ****************************************************************************/ -void debug_state_type(int state) -{ - /* report the state type to help debugging */ - switch (state) - { - case NAME_QUERY_DOM_SRV_CHK : DEBUG(4,("NAME_QUERY_DOM_SRV_CHK\n")); break; - case NAME_QUERY_SRV_CHK : DEBUG(4,("NAME_QUERY_SRV_CHK\n")); break; - case NAME_QUERY_FIND_MST : DEBUG(4,("NAME_QUERY_FIND_MST\n")); break; - case NAME_QUERY_MST_CHK : DEBUG(4,("NAME_QUERY_MST_CHK\n")); break; - case NAME_QUERY_CONFIRM : DEBUG(4,("NAME_QUERY_CONFIRM\n")); break; - case NAME_QUERY_SYNC_LOCAL : DEBUG(4,("NAME_QUERY_SYNC_LOCAL\n")); break; - case NAME_QUERY_SYNC_REMOTE : DEBUG(4,("NAME_QUERY_SYNC_REMOTE\n")); break; - case NAME_QUERY_DOMAIN : DEBUG(4,("NAME_QUERY_DOMAIN\n")); break; - - case NAME_REGISTER : DEBUG(4,("NAME_REGISTER\n")); break; - case NAME_REGISTER_CHALLENGE : DEBUG(4,("NAME_REGISTER_CHALLENGE\n"));break; - - case NAME_RELEASE : DEBUG(4,("NAME_RELEASE\n")); break; - - case NAME_STATUS_DOM_SRV_CHK : DEBUG(4,("NAME_STATUS_DOM_SRV_CHK\n")); break; - case NAME_STATUS_SRV_CHK : DEBUG(4,("NAME_STATUS_SRV_CHK\n")); break; - - default: break; - } -} - -/**************************************************************************** - report any problems with the fact that a response has been received. - - (responses for certain types of operations are only expected from one host) - ****************************************************************************/ -static BOOL response_problem_check(struct response_record *n, - struct nmb_packet *nmb, char *ans_name) -{ - switch (nmb->answers->rr_type) - { - case NMB_REL: - { - if (n->num_msgs > 1) - { - DEBUG(1,("more than one release name response received!\n")); - return True; - } - break; - } - - case NMB_REG: - { - if (n->num_msgs > 1) - { - DEBUG(1,("more than one register name response received!\n")); - return True; - } - break; - } - - case NMB_QUERY: - { - if (n->num_msgs > 1) - { - if (nmb->header.rcode == 0 && nmb->answers && nmb->answers->rdata) - { - int nb_flags = nmb->answers->rdata[0]; - - if ((!NAME_GROUP(nb_flags))) - { - /* oh dear. more than one person responded to a - unique name. - there is either a network problem, a - configuration problem - or a server is mis-behaving */ - - /* XXXX mark the name as in conflict, and then let the - person who just responded know that they - must also mark it - as in conflict, and therefore must NOT use it. - see rfc1001.txt 15.1.3.5 */ - - /* this may cause problems for some - early versions of nmbd */ - - switch (n->state) - { - case NAME_QUERY_FIND_MST: - { - /* query for ^1^2__MSBROWSE__^2^1 expect - lots of responses */ - return False; - } - case NAME_QUERY_DOM_SRV_CHK: - case NAME_QUERY_SRV_CHK: - case NAME_QUERY_MST_CHK: - { - if (!strequal(ans_name,n->name.name)) - { - /* one subnet, one master browser - per workgroup */ - /* XXXX force an election? */ - - DEBUG(3,("more than one master browser replied!\n")); - return True; - } - break; - } - default: break; - } - DEBUG(3,("Unique Name conflict detected!\n")); - return True; - } - } - else - { - /* we have received a negative reply, - having already received - at least one response (pos/neg). - something's really wrong! */ - - DEBUG(3,("wierd name query problem detected!\n")); - return True; - } - } - } - } - return False; -} - -#if 0 -/**************************************************************************** - check that the response received is compatible with the response record - ****************************************************************************/ -static BOOL response_compatible(struct response_record *n, - struct nmb_packet *nmb) -{ - switch (n->state) - { - case NAME_RELEASE: - { - if (nmb->answers->rr_type != 0x20) - { - DEBUG(1,("Name release reply has wrong answer rr_type\n")); - return False; - } - break; - } - - case NAME_REGISTER: - { - if (nmb->answers->rr_type != 0x20) - { - DEBUG(1,("Name register reply has wrong answer rr_type\n")); - return False; - } - break; - } - - case NAME_REGISTER_CHALLENGE: /* this is a query: we then do a register */ - case NAME_QUERY_CONFIRM: - case NAME_QUERY_SYNC_LOCAL: - case NAME_QUERY_SYNC_REMOTE: - case NAME_QUERY_DOM_SRV_CHK: - case NAME_QUERY_SRV_CHK: - case NAME_QUERY_FIND_MST: - case NAME_QUERY_MST_CHK: - { - if (nmb->answers->rr_type != 0x20) - { - DEBUG(1,("Name query reply has wrong answer rr_type\n")); - return False; - } - break; - } - - case NAME_STATUS_DOM_SRV_CHK: - case NAME_STATUS_SRV_CHK: - { - if (nmb->answers->rr_type != 0x21) - { - DEBUG(1,("Name status reply has wrong answer rr_type\n")); - return False; - } - break; - } - - default: - { - DEBUG(1,("unknown state type received in response_netbios_packet\n")); - return False; - } - } - return True; -} -#endif - - -/**************************************************************************** - process the response packet received - ****************************************************************************/ -static void response_process(struct subnet_record *d, struct packet_struct *p, - struct response_record *n, struct nmb_packet *nmb, - BOOL bcast, struct nmb_name *ans_name) -{ - switch (n->state) - { - case NAME_RELEASE: - { - response_name_release(ans_name, d, p); - break; - } - - case NAME_REGISTER: - { - response_name_reg(ans_name, d, p); - break; - } - - case NAME_REGISTER_CHALLENGE: - { - response_name_query_register(nmb, ans_name, n, d); - break; - } - - case NAME_QUERY_DOM_SRV_CHK: - case NAME_QUERY_SRV_CHK: - case NAME_QUERY_FIND_MST: - { - response_server_check(ans_name, n, d, p); - break; - } - - case NAME_STATUS_DOM_SRV_CHK: - case NAME_STATUS_SRV_CHK: - { - response_name_status_check(p->ip, nmb, bcast, n, d); - break; - } - - case NAME_QUERY_CONFIRM: - case NAME_QUERY_SYNC_LOCAL: - case NAME_QUERY_SYNC_REMOTE: - { - response_name_query_sync(nmb, ans_name, bcast, n, d); - break; - } - case NAME_QUERY_MST_CHK: - { - /* no action required here. it's when NO responses are received - that we need to do something. see expire_name_query_entries() */ - - DEBUG(4, ("Master browser exists for %s at %s (just checking!)\n", - namestr(&n->name), inet_ntoa(n->send_ip))); - break; - } - - case NAME_QUERY_DOMAIN: - { - /* We were asking to be a domain master browser, and someone - replied. If it was the WINS server and the IP it is - returning is our own - then remove the record and pretend - we didn't get a response. Else we do nothing and let - dead_netbios_entry deal with it. - We can only become domain master browser - when no broadcast responses are received and WINS - either contains no entry for the DOMAIN<1b> name or - contains our IP address. - */ - response_name_query_domain(ans_name, nmb, n, d); - break; - } - default: - { - DEBUG(1,("unknown state type received in response_netbios_packet\n")); - break; - } - } -} - - -/**************************************************************************** - response from a netbios packet. - ****************************************************************************/ -void response_netbios_packet(struct packet_struct *p) -{ - struct nmb_packet *nmb = &p->packet.nmb; - struct nmb_name *ans_name = NULL; - BOOL bcast = nmb->header.nm_flags.bcast; - struct response_record *n; - struct subnet_record *d = NULL; - - if (!(n = find_response_record(&d,nmb->header.name_trn_id))) { - DEBUG(2,("unknown netbios response id %d (received late or from nmblookup?)\n", - nmb->header.name_trn_id)); - return; - } - - if (!d) - { - DEBUG(2,("response packet: subnet %s not known\n", inet_ntoa(p->ip))); - return; - } - - /* args wrong way round: spotted by ccm@shentel.net */ - if (!same_net(d->bcast_ip, p->ip, d->mask_ip)) /* copes with WINS 'subnet' */ - { - DEBUG(2,("response from %s. ", inet_ntoa(p->ip))); - DEBUG(2,("expected on subnet %s. hmm.\n", inet_ntoa(d->bcast_ip))); - } - - if (nmb->answers == NULL) { - /* if there is no name is the response then the name is the one - we queried on */ - ans_name = &n->name; - } else { - ans_name = &nmb->answers->rr_name; - debug_rr_type(nmb->answers->rr_type); - } - - DEBUG(3,("response for %s from %s(%d) (bcast=%s)\n", - namestr(ans_name), inet_ntoa(p->ip), p->port, BOOLSTR(bcast))); - - n->num_msgs++; /* count number of responses received */ - n->repeat_count = 0; /* don't resend: see expire_netbios_packets() */ - - debug_state_type(n->state); - - /* problem checking: multiple responses etc */ - if (nmb->answers && response_problem_check(n, nmb, ans_name->name)) - return; - - /* now deal with the current state */ - response_process(d, p, n, nmb, bcast, ans_name); -} diff --git a/source3/nameservresp.doc b/source3/nameservresp.doc deleted file mode 100644 index 635db45084..0000000000 --- a/source3/nameservresp.doc +++ /dev/null @@ -1,191 +0,0 @@ -/* - Unix SMB/Netbios documentation. - Version 0.0 - Copyright (C) Luke Leighton Andrew Tridgell 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. - - Document name: nameservresp.doc - - Revision History: - - 0.0 - 02jul96 : lkcl@pires.co.uk - created -*/ - -this module deals with the receipt of response packets. the -response packets are expected to be received, and there is a -record of this kept (see also: modules nameresp and namedbresp) - -point of interest to design purists: every function in this -module is static except response_netbios_packet(). - -/************************************************************************* - response_netbios_packet() - *************************************************************************/ - -this function receives netbios response packets. the samba server -(or a rogue tcp/ip system, or nmblookup) will have sent out a packet -requesting a response. a client (or a rogue tcp/ip system) responds -to that request. - -this function checks the validity of the packet it receives. -the expected response records are searched for the transaction id, -to see if it's a response expected by the samba server. if it isn't -it's reported as such, and ignored. - -if the response is found, then the subnet it was expected from will -also have been found. the subnet it actually came in on can be -checked against the subnet it was expected from and reported, -otherwise this function just carries on. - -the number of responses received is increased, and the number of -retries left to be sent is set to zero. - -after debug information is reported, and validation of the netbios -packet (e.g only one response from one machine is expected for some -functions) has occurred, the packet is processed. when the initial -request was sent out, the expected response record was flagged with, -for lack of a better word, a samba 'state' type. whenever a -response is received, the appropriate function is called to carry on -where the program control flow was interrupted while awaiting exactly -such a response. - -please note that _not_ receiving a response is dealt with in another -area of code - expire_netbios_response_entries(). - - -/************************************************************************* - response_name_query_sync() - *************************************************************************/ - -this function receives responses to samba 'states' NAME_QUERY_SYNC and -NAME_QUERY_CONFIRM. - -NAME_QUERY_SYNC: name query a server before synchronising browse lists. -NAME_QUERY_CONFIRM: name query a server to check that it's alive. - -a NAME_QUERY_SYNC will be carried out in order to check that a server -is alive before syncing browse lists. we don't want to delay the SMB -NetServerEnum api just because the server has gone down: we have too -much else to do. - -a NAME_QUERY_CONFIRM is just a name query to see whether the server is -alive. these queries are sent out by samba's WINS server side, to verify -its netbios name database of all machines that have registered with it. - -we don't normally expect a negative response from such a query, although -we may do so if the query was sent to another WINS server. the registered -entry should be removed if we receive a negative response. - - -/************************************************************************* - response_name_status_check() - *************************************************************************/ - -this function receives responses to samba 'states' NAME_STATUS_SRV_CHK -and NAME_STATUS_DOM_SRV_CHK - -NAME_STATUS_DOM_SRV_CHK: name status a domain master browser - confirm its domain and then initiate syncing - its browse list. - -NAME_STATUS_SRV_CHK: same as NAME_STATUS_DOM_SRV_CHK except the - name status is issued to a master browser. - -if we don't know what workgroup a server is responsible for, but we -know that there is a master browser at a certain ip, we can issue a -name status check. from the response received, there will be -a master browser netbios entry. this will allow us to synchronise -browse lists with that machine and then add the information to the -correct part of samba's workgroup - server database. - - -/************************************************************************* - response_server_check() - *************************************************************************/ - -this function receives responses to samba 'states' NAME_QUERY_DOM_SRV_CHK, -NAME_QUERY_SRV_CHK and NAME_QUERY_FIND_MST. - -NAME_QUERY_FIND_MST: issued as a broadcast when we wish to find out all - master browsers (i.e all servers that have registered - the NetBIOS name ^1^2__MSBROWSE__^2(0x1), and then - issue a NAME_STATUS_MASTER_CHECK on any servers that - respond, which will initiate a sync browse lists. - -NAME_QUERY_DOM_SRV_CHK: same as a NAME_QUERY_FIND_MST except this is sent - to a domain master browser. - -NAME_QUERY_SRV_CHK: same as a NAME_QUERY_DOM_SRV_CHK except this is sent to - a master browser. - -the purpose of each of these states is to do a broadcast name query, or -a name query directed at a WINS server, then to all hosts that respond, -we issue a name status check, which will confirm for us the workgroup -or domain name, and then initiate issuing a sync browse list call with -that server. - -a NAME_QUERY_SRV_CHK is sent when samba receives a list of backup -browsers. it checks to see if that server is alive (by doing a -name query on a server) and then syncs browse lists with it. - - -/************************************************************************* - response_name_reg() - *************************************************************************/ - -this function is responsible for dealing with samba's registration -attempts, by broadcast to a local subnet, or point-to-point with -another WINS server. - -please note that it cannot cope with END-NODE CHALLENGE REGISTRATION -RESPONSEs at present. - -when a response is received, samba determines if the response is a -positive or a negative one. if it is a positive response, the name -is added to samba's database. - -when a negative response is received, samba will remove the name -from its database. if, however, the name is a browser type (0x1b is -a domain master browser type name; or 0x1d, which is a local master -browser type name) then it must also stop being a domain master -browser or master browser respectively, depending on what kind -of name was rejected. - -(when no response is received, then expire_netbios_response_entries() -is expected to deal with this. the only case that is dealt with here -at present is when the registration was done by broadcast. if there -is no challenge to the broadcast registration, it is implicitly -assumed that claiming the name is acceptable). - - -/************************************************************************* - response_name_release() - *************************************************************************/ - -this function is responsible for removing samba's NetBIOS name when -samba contacts another WINS server with which it had registered the -name. - -only positive name releases are expected and dealt with. exactly what -to do if a negative name release (i.e someone says 'oi! you have to -keep that name!') is received is uncertain. - -(when no response is received, then expire_netbios_response_entries() -is expected to deal with this. if there is no challenge to the release -of the name, the name is then removed from that subnet's NetBIOS -name database). - diff --git a/source3/namework.c b/source3/namework.c deleted file mode 100644 index 540aec5bfe..0000000000 --- a/source3/namework.c +++ /dev/null @@ -1,724 +0,0 @@ -/* - Unix SMB/Netbios implementation. - Version 1.9. - NBT netbios routines and daemon - version 2 - 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 - 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 fstring myworkgroup; - -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; - -extern int updatecount; - -/* 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(False,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 = 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; - 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 (is_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 ( is_myname(dgram->source_name.name)) 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_localnet_announce(struct packet_struct *p,uint16 command,char *buf) -{ - struct dgram_packet *dgram = &p->packet.dgram; - struct subnet_record *d = find_subnet(p->ip); /* Explicitly exclude WINS - local nets only */ - 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(p->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; - - DEBUG(4,("search for workgroup: %s (add? %s)\n", - work_name, BOOLSTR(add))); - - 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|SV_TYPE_LOCAL_LIST_ONLY,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 -} - -/******************************************************************* - process a master announcement frame - Domain master browsers recieve these from local masters. The Domain - master should then issue a sync with the local master, asking for - that machines local server list. - ******************************************************************/ -static void process_master_announce(struct packet_struct *p,char *buf) -{ - struct dgram_packet *dgram = &p->packet.dgram; - char *name = buf; - struct work_record *work; - name[15] = 0; - - DEBUG(3,("process_master_announce: Master Announce from %s (%s)\n",name,inet_ntoa(p->ip))); - - if (same_context(dgram)) return; - - if (!wins_client_subnet) - { - DEBUG(3,("process_master_announce: No wins subnet !\n")); - return; - } - - if (!lp_domain_master()) - { - DEBUG(3,("process_master_announce: Not configured as domain master - ignoring master announce.\n")); - return; - } - - for (work = wins_client_subnet->workgrouplist; work; work = work->next) - { - if (AM_MASTER(work) || AM_DOMMST(work)) - { - /* merge browse lists with them */ - add_browser_entry(name,0x1d, work->work_group,30,wins_client_subnet,p->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; - 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(p->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(p->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")); - -#if 0 - /* 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, - 0); - return; - } - } - } - } -#endif - } -} - - -/**************************************************************************** - 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 = FIRST_SUBNET; d; d = NEXT_SUBNET_INCLUDING_WINS(d)) - { - 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(False,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 = 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)) - { - unbecome_local_master(d,work,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 = FIRST_SUBNET; d; d = NEXT_SUBNET_INCLUDING_WINS(d)) - { - struct work_record *work; - for (work=d->workgrouplist;work;work=remove_workgroup(d,work,True)); - } - add_my_subnets(myworkgroup); - } - - /* 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); /* Explicitly NO WINS */ - int token = CVAL(buf,0); - char *name = buf+1; - - name[15] = 0; - - DEBUG(3,("process_announce_request: Announce request from %s to %s token=0x%X\n", - name,namestr(&dgram->dest_name), token)); - - if (is_myname(dgram->source_name.name)) 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, myworkgroup) return; ??? - */ - - if (!d) - { - DEBUG(3,("process_announce_request: No local interface to announce to %s\n", - name)); - 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; - } - } -} - - - -/**************************************************************************** -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_localnet_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))); - } - } -} - - diff --git a/source3/namework.doc b/source3/namework.doc deleted file mode 100644 index 958a86c866..0000000000 --- a/source3/namework.doc +++ /dev/null @@ -1,363 +0,0 @@ -/* - Unix SMB/Netbios documentation. - Version 0.1 - Copyright (C) Luke Leighton Andrew Tridgell 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. - - Document name: namework.doc - - Revision History: - - 0.0 - 02jul96 : lkcl@pires.co.uk - created - - 0.1 - 22jul96 Andrew.Tridgell@anu.edu.au - tridge's comments on first revision -*/ - -the module namework.c deals with NetBIOS datagram packets, primarily. -it deals with nmbd's workgroup browser side and the domain log in -side. none of the functionality here has specification documents available. -empirical observation of packet traces has been the order of the day, -along with some guess-work. - -beware! - -the receipt of datagram packets for workgroup browsing are dealt with here. -some of the functions listed here will call others outside of this -module, or will activate functionality dealt with by other modules -(namedb, nameannounce, nameelect, namelogon, and namebrowse). - - -/************************************************************************* - process_browse_packet() - *************************************************************************/ - -this function is responsible for further identifying which type of -browser datagram packet has been received, and dealing with it -accordingly. if the packet is not dealt with, then an error is -logged along with the type of packet that has been received. - -if listening_type() was in use, then it would be used here. - -the types of packets received and dealt with are: - -- ANN_HostAnnouncement -- ANN_DomainAnnouncement -- ANN_LocalMasterAnnouncement - -these are all identical in format and can all be processed by -process_announce(). an announcement is received from a host -(either a master browser telling us about itself, a server -telling us about itself or a master browser telling us about -a domain / workgroup) - -- ANN_AnnouncementRequest - -these are sent by master browsers or by servers. it is a -request to announce ourselves as appropriate by sending -either a ANN_HostAnnouncement datagram or both an -ANN_DomainAnnouncement and an ANN_LocalMasterAnnouncement -if we are a master browser (but not both). - -- ANN_Election - -this is an election datagram. if samba has been configured -as a domain master then it will also send out election -datagrams. - -- ANN_GetBackupListReq - -this is a request from another server for us to send a -backup list of all servers that we know about. we respond -by sending a datagram ANN_GetBackupListResp. the protocol -here is a little dicey. - -- ANN_GetBackupListResp - -this is a response from another server that we have sent an -ANN_GetBackupListReq to. the protocol is a little dicey. - -- ANN_BecomeBackup - -this is a message sent by a master browser to a -potential master browser, indicating that it should become -a backup master browser for the workgroup it is a member -of. samba does not respond at present to such datagrams, -and it also sends out such datagrams for the wrong reasons -(this code has now been disabled until this is fixed). - -- ANN_ResetBrowserState - -this datagram is sent for trouble-shooting purposes. -it asks a browser to clear out its server lists, or to -stop becoming a master browser altogether. NT/AS and -samba do not implement this latter option. - -- ANN_MasterAnnouncement - -this datagram is sent by a master browser to a domain master -browser. it is a way to ensure that master browsers are kept in sync -with a domain master browser across a wide area network. on -receipt of an ANN_MasterAnnouncement we should sync browse lists with -the sender. - -(i never got the hang of this one when i was experimenting. -i forget exactly what it's for, and i never fully worked -out how to coax a server to send it. :-) - -NOTE FROM TRIDGE: The reason you didn't work out how to coax a server -into sending it is that you can't (or shouldn't try!). Basically these -"master announce" datagrams are the way that separate netbios subnets -are linked together to form a complete browse net. The way it works is -that the local master decides it is going to inform the domain master -of its presence, then sends this master announce to the domain -master. The domain master then syncs with the local master using a -"local only" sync. The whole transaction is initiated by the local -master, not the domain master, so the domain master should not do any -of this if it does not first receive a "master announcement". The -local domain masters need to be configured to know the IP address of -the domain master. - - -/************************************************************************* - listening_type() - *************************************************************************/ - - -a datagram packet is sent from one NetBIOS name of a specific type -to another NetBIOS name of a specific type. certain types of -datagrams are only expected from certain types of NetBIOS names. - -this function is intended to catch errors in the type of datagrams -received from different NetBIOS names. it is currently incomplete -due to lack of information on the types of names and the datagrams -they send. - - -/************************************************************************* - process_announce_request() - *************************************************************************/ - -this function is responsible for dealing with announcement requests. -if the type of name that the request is sent to matches our current -status, then we should respond. otherwise, the datagram should be -ignored. - -samba only responds on its local subnets. - -at present, just the name is checked to see if the packet is for us. -what should be done is that if we own the name (e.g WORGROUP(0x1d) -or WORKGROUP(0x1b) then we should respond, otherwise, ignore the -datagram. - -if the name is for us, and we are a member of that workgroup, then -samba should respond. - -note that samba does not respond immediately. this is to ensure that -if the master browser for the workgroup that samba is a member of -sends out a broadcast request announcement, that that master browser -is not swamped with replies. it is therefore up to samba to reply -at some random interval. hence, a flag is set indicating the need -to announce later. - - -/************************************************************************* - process_reset_browser() - *************************************************************************/ - -this function is responsible for dealing with reset state datagrams. -there are three kinds of diagnostic reset requests: - -- stop being a master browser -- discard browse lists, stop being a master browser, and run for re-election -- stop being a master browser forever. - -samba and windows nt do not implement the latter option. - -there appears to be a discrepancy between this description and the -code actually implemented. - - -/************************************************************************* - process_send_backup_list() - *************************************************************************/ - -this function is part of samba's domain master browser functionality. - -it is responsible for giving master browsers a list of other browsers -that maintain backup lists of servers for that master browser's workgroup. - -it is also responsible for giving master browsers a list of domain master -browsers for that local master browser's domain. - -a correct way to think of this function is that it is a 'request to -send out a backup list for the requested workgroup or domain'. - -i have some suspicions and intuitions about this function and how it -is to actually be used. there is no documentation on this, so it is a -matter of experimenting until it's right. - - -/************************************************************************* - send_backup_list() - *************************************************************************/ - -this function is responsible for compiling a list of either master -browsers and backup master browsers or domain master browsers and -backup domain master browsers. samba constructs this list from its -workgroup / server database. - -the list is then sent to the host that requested it by sending an -ANN_GetBackupListResp datagram to this host. - - -NOTE FROM TRIDGE: The "backup list" stuff is only relevant to -local subnets. It has nothing to do with PDCs or domain masters. Its -function is twofold: - -1) spread the browsing load over multiple servers so one server -doesn't get overloaded with browse requests -2) make sure the database doesn't get lost completely if the master -goes down - -To accomplish this a few things are supposed to be done: - -- the master browser maintains a list of "backup browsers". - -- backup browsers are are machines that are just like ordinary servers -but also maintain a browse list and respond to "NetServerEnum" -requests - -- when a server initially announces itself to the master it may set -its "maintain browse list" flag to auto. - -- when a master browser sees a server announcement with "auto" set it -may send a "become backup" to that server telling it to become a -backup. - -- the master has a simple algorithm to determine how many backups it wants -given the number of hosts on the net - -- when a client wishes to get a browse list it asks the master for a -backup list. The master sends it the current list of backup browsers, -including itself. The client caches this list. The client then sends -the NetServerEnum to a random member of this list easch time it wants -to browse. This spreads the load. - - - -/************************************************************************* - process_rcv_backup_list() - *************************************************************************/ - -this function is implemented with a slightly over-kill algorithm. -the correct functionality is to pick any three names at random from -the list that is received from this datagram, and then at intervals -contact _one_ of them for a list of browser, in order to update -samba's browse list. - -samba contacts every single one of the backup browsers listed, through -the use of a NAME_QUERY_SRV_CHK 'state'. - - -/************************************************************************* - process_master_announce() - *************************************************************************/ - -this function is responsible for synchronising browse lists with a -master browser that contacts samba in its capacity as a domain master -browser. - -the function add_browser_entry() is used to add the server that -contacts us to our list of browser to sync browse lists with at -some point in the near future. - - -/************************************************************************* - process_announce() - *************************************************************************/ - -this function is responsible for dealing with the three types of -announcement type datagrams that samba recognises. some appropriate -type-checking is done on the name that the datagram is sent to. - -samba does not at present deal with LanManager announcements. - -these announcements are for updating the browse entry records. -each browse entry has a time-to-live associated with it. each server -must refresh its entry with all other servers by broadcasting -Announcements. if it does not do so, then other servers will not -know about that machine, and the records on each server of that -other machine will die. - -if an ANN_DomainAnnouncement is received, then this will be from -a master browser. only one machine on any given broadcast area (e.g -a subnet) should be broadcasting such announcements. the information -it contains tells other servers that there is a master browser for -this workgroup. if another server thinks that it is also a master -browser for the same workgroup, then it should stop being a master -browser and force an election. - -if an ANN_LocalMasterAnnouncement is received, then a master browser -is telling us that it exists. i am uncertain that anything else -actually needs to be done with this, other than to shout 'hooray' and -'thank you for informing me of this fact'. - - -/************************************************************************* - listening_name() - *************************************************************************/ - -this function is an over-simplified way of identifying whether we -should be responding to a datagram that has been received. - - -/************************************************************************* - same_context() - *************************************************************************/ - -this function helps us to identify whether we should be responding to -a datagram that has been received. - - -/************************************************************************* - tell_become_backup() - *************************************************************************/ - -this function is part of samba's domain master browser capabilities. -it is responsible for finding appropriate servers to tell to become a -backup master browser for the domain that samba controls. - -other servers that contact samba asking for a list of backup browsers -will then be given that server's name, and that server can expect to -receive NetServerEnum requests for lists of servers and workgroups. - -this function must be updated before it is in a fit state to be used. -it must properly check whether a server is prepared to become a backup -browser before actually asking it to be one. - - -/************************************************************************* - reset_server() - *************************************************************************/ - -this function is responsible for issuing an ANN_ResetBrowserState to -the specified server, asking it to reset its browser information. - -see process_reset_browser() for details on this function. - - diff --git a/source3/nmbd/asyncdns.c b/source3/nmbd/asyncdns.c index 57d0eda9b3..1ee9dab065 100644 --- a/source3/nmbd/asyncdns.c +++ b/source3/nmbd/asyncdns.c @@ -22,28 +22,29 @@ extern int DEBUGLEVEL; - /*************************************************************************** - add a DNS result to the name cache - ****************************************************************************/ + Add a DNS result to the name cache. +****************************************************************************/ + static struct name_record *add_dns_result(struct nmb_name *question, struct in_addr addr) { int name_type = question->name_type; char *qname = question->name; - + + if (!addr.s_addr) { /* add the fail to WINS cache of names. give it 1 hour in the cache */ - DEBUG(3,("Negative DNS answer for %s\n", qname)); - add_netbios_entry(wins_client_subnet,qname,name_type,NB_ACTIVE,60*60, - DNSFAIL,addr,True); + DEBUG(3,("add_dns_result: Negative DNS answer for %s\n", qname)); + add_name_to_subnet(wins_server_subnet,qname,name_type, + NB_ACTIVE, 60*60, DNSFAIL_NAME, 1, &addr); return NULL; } /* add it to our WINS cache of names. give it 2 hours in the cache */ - DEBUG(3,("DNS gave answer for %s of %s\n", qname, inet_ntoa(addr))); + DEBUG(3,("add_dns_result: DNS gave answer for %s of %s\n", qname, inet_ntoa(addr))); - return add_netbios_entry(wins_client_subnet,qname,name_type,NB_ACTIVE, - 2*60*60,DNS,addr, True); + return add_name_to_subnet(wins_server_subnet,qname,name_type, + NB_ACTIVE, 2*60*60, DNS_NAME, 1, &addr); } @@ -101,6 +102,23 @@ static void asyncdns_process(void) _exit(0); } +/**************************************************************************** ** + catch a sigterm + We need a separate term handler here so we don't release any + names that our parent is going to release, or overwrite a + WINS db that our parent is going to write. + **************************************************************************** */ + +static int sig_term() +{ + BlockSignals(True,SIGTERM); + + DEBUG(0,("async dns child. Got SIGTERM: going down...\n")); + + exit(0); + /* Keep compiler happy.. */ + return 0; +} /*************************************************************************** create a child process to handle DNS lookups @@ -131,6 +149,7 @@ void start_async_dns(void) signal(SIGUSR2, SIG_IGN); signal(SIGUSR1, SIG_IGN); signal(SIGHUP, SIG_IGN); + signal( SIGTERM, SIGNAL_CAST sig_term ); asyncdns_process(); } @@ -142,7 +161,7 @@ check if a particular name is already being queried static BOOL query_current(struct query_record *r) { return dns_current && - name_equal(&r->name, + nmb_name_equal(&r->name, &dns_current->packet.nmb.question.question_name); } @@ -166,6 +185,7 @@ void run_dns_queue(void) { struct query_record r; struct packet_struct *p, *p2; + struct name_record *namerec; int size; if (fd_in == -1) @@ -184,13 +204,16 @@ void run_dns_queue(void) return; } - add_dns_result(&r.name, r.result); + namerec = add_dns_result(&r.name, r.result); if (dns_current) { if (query_current(&r)) { - DEBUG(3,("DNS calling reply_name_query\n")); + DEBUG(3,("DNS calling send_wins_name_query_response\n")); in_dns = 1; - reply_name_query(dns_current); + if(namerec == NULL) + send_wins_name_query_response(NAM_ERR, dns_current, NULL); + else + send_wins_name_query_response(0,dns_current,namerec); in_dns = 0; } @@ -205,10 +228,13 @@ void run_dns_queue(void) struct nmb_packet *nmb = &p->packet.nmb; struct nmb_name *question = &nmb->question.question_name; - if (name_equal(question, &r.name)) { - DEBUG(3,("DNS calling reply_name_query\n")); + if (nmb_name_equal(question, &r.name)) { + DEBUG(3,("DNS calling send_wins_name_query_response\n")); in_dns = 1; - reply_name_query(p); + if(namerec == NULL) + send_wins_name_query_response(NAM_ERR, p, NULL); + else + send_wins_name_query_response(0,p,namerec); in_dns = 0; p->locked = False; @@ -286,6 +312,10 @@ BOOL queue_dns_query(struct packet_struct *p,struct nmb_name *question, dns_ip.s_addr = interpret_addr(qname); *n = add_dns_result(question, dns_ip); + if(*n == NULL) + send_wins_name_query_response(NAM_ERR, p, NULL); + else + send_wins_name_query_response(0, p, *n); return False; } #endif diff --git a/source3/nmbd/nmbd.c b/source3/nmbd/nmbd.c index a34e2caf42..11cd50cd76 100644 --- a/source3/nmbd/nmbd.c +++ b/source3/nmbd/nmbd.c @@ -34,8 +34,8 @@ pstring servicesf = CONFIGFILE; extern pstring scope; -int ClientNMB = -1; -int ClientDGRAM = -1; +int ClientNMB = -1; +int ClientDGRAM = -1; int global_nmb_port = -1; extern pstring myhostname; @@ -49,47 +49,47 @@ static BOOL is_daemon = False; /* what server type are we currently */ -time_t StartupTime =0; +time_t StartupTime = 0; extern struct in_addr ipzero; - /**************************************************************************** +/**************************************************************************** ** catch a sigterm - ****************************************************************************/ + **************************************************************************** */ static int sig_term() { BlockSignals(True,SIGTERM); DEBUG(0,("Got SIGTERM: going down...\n")); - /* write out wins.dat file if samba is a WINS server */ - dump_names(); + /* Write out wins.dat file if samba is a WINS server */ + wins_write_database(); - /* remove all samba names, with wins server if necessary. */ - remove_my_names(); + /* Remove all SELF registered names. */ + release_my_names(); - /* announce all server entries as 0 time-to-live, 0 type */ - /* XXXX don't care if we never receive a response back... yet */ + /* Announce all server entries as 0 time-to-live, 0 type. */ announce_my_servers_removed(); - /* XXXX other things: if we are a master browser, force an election? */ - exit(0); /* Keep compiler happy.. */ return 0; -} +} /* sig_term */ -/**************************************************************************** -catch a sighup -****************************************************************************/ +/**************************************************************************** ** + catch a sighup + **************************************************************************** */ static int sig_hup(void) { - BlockSignals(True,SIGHUP); + BlockSignals( True, SIGHUP ); + + DEBUG( 0, ( "Got SIGHUP dumping debug info.\n" ) ); + + write_browse_list( 0, True ); - DEBUG(0,("Got SIGHUP (reload not implemented)\n")); - dump_names(); - reload_services(True); + dump_all_namelists(); + reload_services( True ); set_samba_nb_type(); @@ -98,239 +98,294 @@ static int sig_hup(void) signal(SIGHUP,SIGNAL_CAST sig_hup); #endif return(0); -} +} /* sig_hup */ -/**************************************************************************** -catch a sigpipe -****************************************************************************/ +/**************************************************************************** ** + catch a sigpipe + **************************************************************************** */ static int sig_pipe(void) { - BlockSignals(True,SIGPIPE); + BlockSignals( True, SIGPIPE ); - DEBUG(0,("Got SIGPIPE\n")); - if (!is_daemon) + DEBUG( 0, ("Got SIGPIPE\n") ); + if ( !is_daemon ) exit(1); - BlockSignals(False,SIGPIPE); + BlockSignals( False, SIGPIPE ); return(0); -} +} /* sig_pipe */ #if DUMP_CORE -/******************************************************************* -prepare to dump a core file - carefully! -********************************************************************/ +/**************************************************************************** ** + prepare to dump a core file - carefully! + **************************************************************************** */ static BOOL dump_core(void) { char *p; pstring dname; - pstrcpy(dname,debugf); - if ((p=strrchr(dname,'/'))) *p=0; - strcat(dname,"/corefiles"); - mkdir(dname,0700); - sys_chown(dname,getuid(),getgid()); - chmod(dname,0700); - if (chdir(dname)) return(False); - umask(~(0700)); + pstrcpy( dname, debugf ); + if ((p=strrchr(dname,'/'))) + *p=0; + strcat( dname, "/corefiles" ); + mkdir( dname, 0700 ); + sys_chown( dname, getuid(), getgid() ); + chmod( dname, 0700 ); + if ( chdir(dname) ) + return( False ); + umask( ~(0700) ); #ifndef NO_GETRLIMIT #ifdef RLIMIT_CORE { struct rlimit rlp; - getrlimit(RLIMIT_CORE, &rlp); - rlp.rlim_cur = MAX(4*1024*1024,rlp.rlim_cur); - setrlimit(RLIMIT_CORE, &rlp); - getrlimit(RLIMIT_CORE, &rlp); - DEBUG(3,("Core limits now %d %d\n",rlp.rlim_cur,rlp.rlim_max)); + getrlimit( RLIMIT_CORE, &rlp ); + rlp.rlim_cur = MAX( 4*1024*1024, rlp.rlim_cur ); + setrlimit( RLIMIT_CORE, &rlp ); + getrlimit( RLIMIT_CORE, &rlp ); + DEBUG( 3, ( "Core limits now %d %d\n", rlp.rlim_cur, rlp.rlim_max ) ); } #endif #endif - DEBUG(0,("Dumping core in %s\n",dname)); - return(True); -} + DEBUG( 0, ( "Dumping core in %s\n",dname ) ); + return( True ); +} /* dump_core */ #endif -/**************************************************************************** -possibly continue after a fault -****************************************************************************/ +/**************************************************************************** ** + possibly continue after a fault + **************************************************************************** */ static void fault_continue(void) { #if DUMP_CORE dump_core(); #endif -} +} /* fault_continue */ -/******************************************************************* - expire old names from the namelist and server list - ******************************************************************/ +/**************************************************************************** ** + expire old names from the namelist and server list + **************************************************************************** */ static void expire_names_and_servers(time_t t) { static time_t lastrun = 0; - if (!lastrun) lastrun = t; - if (t < lastrun + 5) return; + if ( !lastrun ) + lastrun = t; + if ( t < (lastrun + 5) ) + return; lastrun = t; - + + /* + * Expire any timed out names on all the broadcast + * subnets and those registered with the WINS server. + * (nmbd_namelistdb.c) + */ expire_names(t); - expire_servers(t); -} -/***************************************************************************** + /* + * Go through all the broadcast subnets and for each + * workgroup known on that subnet remove any expired + * server names. If a workgroup has an empty serverlist + * and has itself timed out then remove the workgroup. + * (nmbd_workgroupdb.c) + */ + expire_workgroups_and_servers(t); +} /* expire_names_and_servers */ + +/**************************************************************************** ** reload the services file - **************************************************************************/ + **************************************************************************** */ BOOL reload_services(BOOL test) { BOOL ret; extern fstring remote_machine; - strcpy(remote_machine,"nmbd"); + strcpy( remote_machine, "nmbd" ); - if (lp_loaded()) + if ( lp_loaded() ) + { + pstring fname; + pstrcpy( fname,lp_configfile()); + if (file_exist(fname,NULL) && !strcsequal(fname,servicesf)) { - pstring fname; - pstrcpy(fname,lp_configfile()); - if (file_exist(fname,NULL) && !strcsequal(fname,servicesf)) - { - pstrcpy(servicesf,fname); - test = False; - } + pstrcpy(servicesf,fname); + test = False; } + } - if (test && !lp_file_list_changed()) + if ( test && !lp_file_list_changed() ) return(True); - ret = lp_load(servicesf,True); + ret = lp_load( servicesf, True ); /* perhaps the config filename is now set */ - if (!test) { - DEBUG(3,("services not loaded\n")); - reload_services(True); + if ( !test ) + { + DEBUG( 3, ( "services not loaded\n" ) ); + reload_services( True ); } /* Do a sanity check for a misconfigured nmbd */ - if(lp_wins_support() && *lp_wins_server()) { + if( lp_wins_support() && *lp_wins_server() ) + { DEBUG(0,("ERROR: both 'wins support = true' and 'wins server = ' \ cannot be set in the smb.conf file. nmbd aborting.\n")); exit(10); } return(ret); -} - - - -/**************************************************************************** -load a netbios hosts file -****************************************************************************/ -static void load_hosts_file(char *fname) -{ - FILE *f = fopen(fname,"r"); - pstring line; - if (!f) { - DEBUG(2,("Can't open lmhosts file %s\n",fname)); - return; - } - - while (!feof(f)) - { - pstring ip,name,flags,extra; - struct subnet_record *d; - char *ptr; - int count = 0; - struct in_addr ipaddr; - enum name_source source = LMHOSTS; - - if (!fgets_slash(line,sizeof(pstring),f)) continue; - - if (*line == '#') continue; - - strcpy(ip,""); - strcpy(name,""); - strcpy(flags,""); - - ptr = line; - - if (next_token(&ptr,ip ,NULL)) ++count; - if (next_token(&ptr,name ,NULL)) ++count; - if (next_token(&ptr,flags,NULL)) ++count; - if (next_token(&ptr,extra,NULL)) ++count; - - if (count <= 0) continue; - - if (count > 0 && count < 2) { - DEBUG(0,("Ill formed hosts line [%s]\n",line)); - continue; - } - - if (count >= 4) { - DEBUG(0,("too many columns in %s (obsolete syntax)\n",fname)); - continue; - } - - DEBUG(4, ("lmhost entry: %s %s %s\n", ip, name, flags)); - - if (strchr(flags,'G') || strchr(flags,'S')) { - DEBUG(0,("group flag in %s ignored (obsolete)\n",fname)); - continue; - } - - if (strchr(flags,'M')) { - source = SELF; - pstrcpy(myname,name); - } - - ipaddr = *interpret_addr2(ip); - d = find_subnet_all(ipaddr); - if (d) { - add_netbios_entry(d,name,0x00,NB_ACTIVE,0,source,ipaddr,True); - add_netbios_entry(d,name,0x20,NB_ACTIVE,0,source,ipaddr,True); - } - } - - fclose(f); -} +} /* reload_services */ - -/**************************************************************************** - The main select loop. - ***************************************************************************/ +/**************************************************************************** ** + The main select loop. + **************************************************************************** */ static void process(void) { BOOL run_election; - while (True) - { - time_t t = time(NULL); - run_election = check_elections(); - if(listen_for_packets(run_election)) - return; - - run_packet_queue(); - run_elections(t); - - announce_host(t); - announce_master(t); - announce_remote(t); - browse_sync_remote(t); - - query_refresh_names(t); - - expire_names_and_servers(t); - expire_netbios_response_entries(t); - refresh_my_names(t); - - write_browse_list(t); - do_browser_lists(t); - check_master_browser(t); - add_domain_names(t); - } -} + while( True ) + { + time_t t = time(NULL); + + /* + * Check all broadcast subnets to see if + * we need to run an election on any of them. + * (nmbd_elections.c) + */ + run_election = check_elections(); + + /* + * Read incoming UDP packets. + * (nmbd_packets.c) + */ + if(listen_for_packets(run_election)) + return; + + /* + * Process all incoming packets + * read above. This calls the success and + * failure functions registered when response + * packets arrrive, and also deals with request + * packets from other sources. + * (nmbd_packets.c) + */ + run_packet_queue(); + + /* + * Run any elections - initiate becoming + * a local master browser if we have won. + * (nmbd_elections.c) + */ + run_elections(t); + + /* + * Send out any broadcast announcements + * of our server names. This also announces + * the workgroup name if we are a local + * master browser. + * (nmbd_sendannounce.c) + */ + announce_my_server_names(t); + + /* + * If we are a local master browser, periodically + * announce ourselves to the domain master browser. + * This also deals with syncronising the domain master + * browser server lists with ourselves as a local + * master browser. + * (nmbd_sendannounce.c) + */ + announce_myself_to_domain_master_browser(t); + + /* + * Fullfill any remote announce requests. + * (nmbd_sendannounce.c) + */ + announce_remote(t); + + /* + * Fullfill any remote browse sync announce requests. + * (nmbd_sendannounce.c) + */ + browse_sync_remote(t); + + /* + * Scan the broadcast subnets, and WINS client + * namelists and refresh any that need refreshing. + * (nmbd_mynames.c) + */ + refresh_my_names(t); + + /* + * Scan the subnet namelists and server lists and + * expire thos that have timed out. + * (nmbd.c) + */ + expire_names_and_servers(t); + + /* + * Write out a snapshot of our current browse list into + * the browse.dat file. This is used by smbd to service + * incoming NetServerEnum calls - used to synchronise + * browse lists over subnets. + * (nmbd_serverlistdb.c) + */ + write_browse_list(t, False); + + /* + * If we are a domain master browser, we have a list of + * local master browsers we should synchronise browse + * lists with (these are added by an incoming local + * master browser announcement packet). Expire any of + * these that are no longer current, and pull the server + * lists from each of these known local master browsers. + * (nmbd_browsesync.c) + */ + dmb_expire_and_sync_browser_lists(t); + + /* + * Check that there is a local master browser for our + * workgroup for all our broadcast subnets. If one + * is not found, start an election (which we ourselves + * may or may not participate in, depending on the + * setting of the 'local master' parameter. + * (nmbd_elections.c) + */ + check_master_browser_exists(t); + + /* + * If we are configured as a logon server, attempt to + * register the special NetBIOS names to become such + * (WORKGROUP<1c> name) on all broadcast subnets and + * with the WINS server (if used). If we are configured + * to become a domain master browser, attempt to register + * the special NetBIOS name (WORKGROUP<1b> name) to + * become such. + * (nmbd_become_dmb.c) + */ + add_domain_names(t); + + /* + * If we are a WINS server, do any timer dependent + * processing required. + * (nmbd_winsserver.c) + */ + initiate_wins_processing(t); + + /* + * Go through the repsonse record queue and time out or re-transmit + * and expired entries. + * (nmbd_packets.c) + */ + retransmit_or_expire_response_records(t); + } +} /* process */ -/**************************************************************************** - open the socket communication -****************************************************************************/ +/**************************************************************************** ** + open the socket communication + **************************************************************************** */ static BOOL open_sockets(BOOL isdaemon, int port) { /* The sockets opened here will be used to receive broadcast @@ -340,29 +395,29 @@ static BOOL open_sockets(BOOL isdaemon, int port) now deprecated. */ - if (isdaemon) + if ( isdaemon ) ClientNMB = open_socket_in(SOCK_DGRAM, port,0,0); else ClientNMB = 0; ClientDGRAM = open_socket_in(SOCK_DGRAM,DGRAM_PORT,3,0); - if (ClientNMB == -1) - return(False); + if ( ClientNMB == -1 ) + return( False ); - signal(SIGPIPE, SIGNAL_CAST sig_pipe); + signal( SIGPIPE, SIGNAL_CAST sig_pipe ); - set_socket_options(ClientNMB,"SO_BROADCAST"); - set_socket_options(ClientDGRAM,"SO_BROADCAST"); + set_socket_options( ClientNMB, "SO_BROADCAST" ); + set_socket_options( ClientDGRAM, "SO_BROADCAST" ); - DEBUG(3,("open_sockets: Broadcast sockets opened.\n")); - return True; -} + DEBUG( 3, ( "open_sockets: Broadcast sockets opened.\n" ) ); + return( True ); +} /* open_sockets */ -/**************************************************************************** - initialise connect, service and file structs -****************************************************************************/ +/**************************************************************************** ** + initialise connect, service and file structs + **************************************************************************** */ static BOOL init_structs() { extern fstring local_machine; @@ -372,102 +427,108 @@ static BOOL init_structs() int nodup; pstring nbname; - if (! *myname) { - fstrcpy(myname,myhostname); - p = strchr(myname,'.'); - if (p) *p = 0; + if (! *myname) + { + fstrcpy( myname, myhostname ); + p = strchr( myname, '.' ); + if (p) + *p = 0; } - strupper(myname); + strupper( myname ); /* Add any NETBIOS name aliases. Ensure that the first entry - is equal to myname. */ + is equal to myname. + */ /* Work out the max number of netbios aliases that we have */ - ptr=lp_netbios_aliases(); - for (namecount=0; next_token(&ptr,nbname,NULL); namecount++) + ptr = lp_netbios_aliases(); + for( namecount=0; next_token(&ptr,nbname,NULL); namecount++ ) ; - if (*myname) - namecount++; + if ( *myname ) + namecount++; /* Allocate space for the netbios aliases */ - if((my_netbios_names=(char **)malloc(sizeof(char *)*(namecount+1))) == NULL) + my_netbios_names = (char **)malloc( sizeof(char *) * (namecount+1) ); + if( NULL == my_netbios_names ) { - DEBUG(0,("init_structs: malloc fail.\n")); - return False; + DEBUG( 0, ( "init_structs: malloc fail.\n" ) ); + return( False ); } /* Use the myname string first */ namecount=0; - if (*myname) + if ( *myname ) my_netbios_names[namecount++] = myname; - ptr=lp_netbios_aliases(); - while (next_token(&ptr,nbname,NULL)) { - strupper(nbname); + ptr = lp_netbios_aliases(); + while ( next_token( &ptr, nbname, NULL ) ) + { + strupper( nbname ); /* Look for duplicates */ nodup=1; - for(n=0; n 1 && (*argv[1] != '-')) { + while (argc > 1 && (*argv[1] != '-')) + { argv++; argc--; } - fault_setup(fault_continue); + fault_setup( fault_continue ); - signal(SIGHUP ,SIGNAL_CAST sig_hup); - signal(SIGTERM,SIGNAL_CAST sig_term); + signal( SIGHUP, SIGNAL_CAST sig_hup ); + signal( SIGTERM, SIGNAL_CAST sig_term ); - while ((opt = getopt(argc, argv, "as:T:I:C:bAi:B:N:Rn:l:d:Dp:hSH:G:f:")) != EOF) + while((opt = getopt(argc, argv, "as:T:I:C:bAi:B:N:Rn:l:d:Dp:hSH:G:f:")) != EOF) { switch (opt) - { + { case 'f': strncpy(pidFile, optarg, sizeof(pidFile)); break; - case 's': - pstrcpy(servicesf,optarg); - break; - case 'N': - case 'B': - case 'I': - case 'C': - case 'G': - DEBUG(0,("Obsolete option '%c' used\n",opt)); - break; - case 'H': - pstrcpy(host_file,optarg); - break; - case 'n': - pstrcpy(myname,optarg); - strupper(myname); - break; - case 'l': - sprintf(debugf,"%s.nmb",optarg); - break; - case 'i': - pstrcpy(scope,optarg); - strupper(scope); - break; - case 'a': - { - extern BOOL append_log; - append_log = !append_log; - } - break; - case 'D': - is_daemon = True; - break; - case 'd': - DEBUGLEVEL = atoi(optarg); - break; - case 'p': - global_nmb_port = atoi(optarg); - break; - case 'h': - usage(argv[0]); - exit(0); - break; - default: - if (!is_a_socket(0)) { - usage(argv[0]); - } - break; - } + case 's': + pstrcpy(servicesf,optarg); + break; + case 'N': + case 'B': + case 'I': + case 'C': + case 'G': + DEBUG(0,("Obsolete option '%c' used\n",opt)); + break; + case 'H': + pstrcpy(host_file,optarg); + break; + case 'n': + pstrcpy(myname,optarg); + strupper(myname); + break; + case 'l': + sprintf(debugf,"%s.nmb",optarg); + break; + case 'i': + pstrcpy(scope,optarg); + strupper(scope); + break; + case 'a': + { + extern BOOL append_log; + append_log = !append_log; + } + break; + case 'D': + is_daemon = True; + break; + case 'd': + DEBUGLEVEL = atoi(optarg); + break; + case 'p': + global_nmb_port = atoi(optarg); + break; + case 'h': + usage(argv[0]); + exit(0); + break; + default: + if (!is_a_socket(0)) + { + usage(argv[0]); + } + break; + } } DEBUG(1,("%s netbios nameserver version %s started\n",timestring(),VERSION)); DEBUG(1,("Copyright Andrew Tridgell 1994-1997\n")); - if(!get_myname(myhostname,NULL)) + if( !get_myname( myhostname, NULL) ) { DEBUG(0,("Unable to get my hostname - exiting.\n")); return -1; } -#ifndef SYNC_DNS - start_async_dns(); -#endif - - if (!reload_services(False)) - return(-1); + if ( !reload_services(False) ) + return(-1); codepage_initialise(lp_client_code_page()); if(!init_structs()) return -1; - reload_services(True); + reload_services( True ); - pstrcpy(myworkgroup, lp_workgroup()); + fstrcpy( myworkgroup, lp_workgroup() ); - if (strequal(myworkgroup,"*")) { + if (strequal(myworkgroup,"*")) + { DEBUG(0,("ERROR: a workgroup name of * is no longer supported\n")); exit(1); } set_samba_nb_type(); - if (!is_daemon && !is_a_socket(0)) { + if (!is_daemon && !is_a_socket(0)) + { DEBUG(0,("standard input is not a socket, assuming -D option\n")); is_daemon = True; } - if (is_daemon) { + if (is_daemon) + { DEBUG(2,("%s becoming a daemon\n",timestring())); become_daemon(); } - if (!directory_exist(lp_lockdir(), NULL)) { - mkdir(lp_lockdir(), 0755); + if (!directory_exist(lp_lockdir(), NULL)) + { + mkdir(lp_lockdir(), 0755); } if (*pidFile) - { - int fd; - char buf[20]; + { + int fd; + char buf[20]; - if ((fd = open(pidFile, #ifdef O_NONBLOCK - O_NONBLOCK | + fd = open( pidFile, O_NONBLOCK | O_CREAT | O_WRONLY | O_TRUNC, 0644 ); +#else + fd = open( pidFile, O_CREAT | O_WRONLY | O_TRUNC, 0644 ); #endif - O_CREAT | O_WRONLY | O_TRUNC, 0644)) < 0) - { - DEBUG(0,("ERROR: can't open %s: %s\n", pidFile, strerror(errno))); - exit(1); - } - if (fcntl_lock(fd,F_SETLK,0,1,F_WRLCK)==False) - { - DEBUG(0,("ERROR: nmbd is already running\n")); - exit(1); - } - sprintf(buf, "%u\n", (unsigned int) getpid()); - if (write(fd, buf, strlen(buf)) < 0) - { - DEBUG(0,("ERROR: can't write to %s: %s\n", pidFile, strerror(errno))); - exit(1); - } - /* Leave pid file open & locked for the duration... */ + if ( fd < 0 ) + { + DEBUG(0,("ERROR: can't open %s: %s\n", pidFile, strerror(errno))); + exit(1); } + if (fcntl_lock(fd,F_SETLK,0,1,F_WRLCK)==False) + { + DEBUG(0,("ERROR: nmbd is already running\n")); + exit(1); + } + sprintf(buf, "%u\n", (unsigned int) getpid()); + if (write(fd, buf, strlen(buf)) < 0) + { + DEBUG(0,("ERROR: can't write to %s: %s\n", pidFile, strerror(errno))); + exit(1); + } + /* Leave pid file open & locked for the duration... */ + } - DEBUG(3,("Opening sockets %d\n", global_nmb_port)); + DEBUG( 3, ( "Opening sockets %d\n", global_nmb_port ) ); - if (!open_sockets(is_daemon,global_nmb_port)) return 1; + if ( !open_sockets( is_daemon, global_nmb_port ) ) + return 1; + /* Determine all the IP addresses we have. */ load_interfaces(); - add_my_subnets(myworkgroup); - - add_my_names(); - DEBUG(3,("Checked names\n")); - - load_netbios_names(); - - DEBUG(3,("Loaded names\n")); + /* Create an nmbd subnet record for each of the above. */ + if( False == create_subnets() ) + { + DEBUG(0,("ERROR: Failed when creating subnet lists. Exiting.\n")); + exit(1); + } - if (*host_file) { - load_hosts_file(host_file); + /* Load in any static local names. */ + if ( *host_file ) + { + load_lmhosts_file(host_file); DEBUG(3,("Loaded hosts file\n")); } - write_browse_list(time(NULL)); + /* If we are acting as a WINS server, initialise data structures. */ + if( !initialise_wins() ) + { + DEBUG( 0, ( "nmbd: Failed when initialising WINS server.\n" ) ); + exit(1); + } - DEBUG(3,("Dumped names\n")); + /* + * Register nmbd primary workgroup and nmbd names on all + * the broadcast subnets, and on the WINS server (if specified). + * Also initiate the startup of our primary workgroup (start + * elections if we are setup as being able to be a local + * master browser. + */ + + if( False == register_my_workgroup_and_names() ) + { + DEBUG(0,("ERROR: Failed when creating my my workgroup. Exiting.\n")); + exit(1); + } /* We can only take sigterm signals in the select. */ - BlockSignals(True,SIGTERM); + BlockSignals( True, SIGTERM ); process(); close_sockets(); @@ -663,4 +746,6 @@ static void usage(char *pname) if (dbf) fclose(dbf); return(0); -} +} /* main */ + +/* ========================================================================== */ diff --git a/source3/nmbd/nmbd_become_dmb.c b/source3/nmbd/nmbd_become_dmb.c new file mode 100644 index 0000000000..37fceb9bf1 --- /dev/null +++ b/source3/nmbd/nmbd_become_dmb.c @@ -0,0 +1,471 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NBT netbios routines and daemon - version 2 + Copyright (C) Andrew Tridgell 1994-1997 + Copyright (C) Luke Kenneth Casson Leighton 1994-1997 + Copyright (C) Jeremy Allison 1994-1997 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "includes.h" + +extern int DEBUGLEVEL; + +extern pstring scope; +extern pstring myname; +extern fstring myworkgroup; +extern char **my_netbios_names; +extern struct in_addr ipzero; +extern struct in_addr allones_ip; + +extern uint16 samba_nb_type; /* Samba's NetBIOS type. */ + +static void become_domain_master_browser_bcast(char *); + +/******************************************************************* + Unbecome a domain master browser - name release success function. + ******************************************************************/ + +static void unbecome_dmb_success(struct subnet_record *subrec, + struct userdata_struct *userdata, + struct nmb_name *released_name, + struct in_addr released_ip) +{ + struct work_record *work = find_workgroup_on_subnet(subrec, released_name->name); + struct server_record *servrec; + + if(!work) + { + DEBUG(0,("unbecome_dmb_success: Cannot find workgroup %s on subnet %s\n", + released_name->name, subrec->subnet_name)); + return; + } + + if((servrec = find_server_in_workgroup( work, myname)) == NULL) + { + DEBUG(0,("unbecome_dmb_success: Error - cannot find server %s \ +in workgroup %s on subnet %s\n", + myname, released_name->name, subrec->subnet_name)); + return; + } + + /* Set the state in the workgroup structure. */ + work->dom_state = DOMAIN_NONE; + + /* Update our server status. */ + servrec->serv.type &= ~SV_TYPE_DOMAIN_MASTER; + + /* Tell the namelist writer to write out a change. */ + subrec->work_changed = True; + + /* Remove any list of local master browsers we are syncing with. */ + remove_workgroup_lmb_browsers(released_name->name); + + /* Delete the known domain master browser name from the workgroup + struct. */ + bzero((char *)&work->dmb_name, sizeof(work->dmb_name)); + putip((char *)&work->dmb_addr, &ipzero); + + DEBUG(0,("\n ***** Samba server %s has stopped being a domain master browser \ +for workgroup %s on subnet %s *****\n\n", myname, work->work_group, subrec->subnet_name)); + +} + +/******************************************************************* + Unbecome a domain master browser - name release fail function. + ******************************************************************/ + +static void unbecome_dmb_fail(struct subnet_record *subrec, + struct response_record *rrec, + struct nmb_name *released_name) +{ + DEBUG(0,("unbecome_dmb_fail: Failed to unbecome domain master browser for \ +workgroup %s on subnet %s.\n", released_name->name, subrec->subnet_name)); +} + +/******************************************************************* + Unbecome a domain master browser. + ******************************************************************/ + +void unbecome_domain_master(char *workgroup_name) +{ + struct subnet_record *subrec; + + for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec)) + { + struct work_record *work = find_workgroup_on_subnet(subrec, workgroup_name); + + if(work && (work->dom_state == DOMAIN_MST)) + { + struct name_record *namerec; + struct nmb_name nmbname; + make_nmb_name(&nmbname,workgroup_name,0x1b,scope); + + /* We can only do this if we are a domain master already. */ + DEBUG(2,("unbecome_domain_master: attempting to stop being a domain \ +master browser for workgroup %s on subnet %s\n", + work->work_group, subrec->subnet_name)); + + /* Find the WORKGROUP<1b> name on the subnet namelist. */ + if((namerec = find_name_on_subnet(subrec, &nmbname, FIND_SELF_NAME))==NULL) + { + DEBUG(0,("unbecome_domain_master: Cannot find name %s on subnet %s.\n", + namestr(&nmbname), subrec->subnet_name)); + continue; + } + release_name(subrec, namerec, + unbecome_dmb_success, + unbecome_dmb_fail, + NULL); + } + } +} + +/**************************************************************************** + Fail to become a Domain Master Browser on a subnet. + ****************************************************************************/ + +static void become_domain_master_fail(struct subnet_record *subrec, + struct response_record *rrec, + struct nmb_name *fail_name) +{ + struct work_record *work = find_workgroup_on_subnet(subrec, fail_name->name); + struct server_record *servrec; + + if(!work) + { + DEBUG(0,("become_domain_master_fail: Error - cannot find \ +workgroup %s on subnet %s\n", fail_name->name, subrec->subnet_name)); + return; + } + + /* Set the state back to DOMAIN_NONE. */ + work->dom_state = DOMAIN_NONE; + + if((servrec = find_server_in_workgroup( work, myname)) == NULL) + { + DEBUG(0,("become_domain_master_fail: Error - cannot find server %s \ +in workgroup %s on subnet %s\n", + myname, work->work_group, subrec->subnet_name)); + return; + } + + /* Update our server status. */ + servrec->serv.type &= ~SV_TYPE_DOMAIN_MASTER; + + /* Tell the namelist writer to write out a change. */ + subrec->work_changed = True; + + DEBUG(0,("become_domain_master_fail: Failed to become a domain master browser for \ +workgroup %s on subnet %s. Couldn't register name %s.\n", + work->work_group, subrec->subnet_name, namestr(fail_name))); +} + +/**************************************************************************** + Become a Domain Master Browser on a subnet. + ****************************************************************************/ + +static void become_domain_master_stage2(struct subnet_record *subrec, + struct userdata_struct *userdata, + struct nmb_name *registered_name, + uint16 nb_flags, + int ttl, struct in_addr registered_ip) +{ + struct work_record *work = find_workgroup_on_subnet( subrec, registered_name->name); + struct server_record *servrec; + + if(!work) + { + DEBUG(0,("become_domain_master_stage2: Error - cannot find \ +workgroup %s on subnet %s\n", registered_name->name, subrec->subnet_name)); + return; + } + + if((servrec = find_server_in_workgroup( work, myname)) == NULL) + { + DEBUG(0,("become_domain_master_stage2: Error - cannot find server %s \ +in workgroup %s on subnet %s\n", + myname, registered_name->name, subrec->subnet_name)); + work->dom_state = DOMAIN_NONE; + return; + } + + /* Set the state in the workgroup structure. */ + work->dom_state = DOMAIN_MST; /* Become domain master. */ + + /* Update our server status. */ + servrec->serv.type |= (SV_TYPE_NT|SV_TYPE_DOMAIN_MASTER); + + /* Tell the namelist writer to write out a change. */ + subrec->work_changed = True; + + DEBUG(0,("\n ***** Samba server %s is now a domain master browser \ +for workgroup %s on subnet %s *****\n\n", myname, work->work_group, subrec->subnet_name)); + + if(subrec == unicast_subnet) + { + struct nmb_name nmbname; + struct in_addr my_first_ip; + + /* Put our name and first IP address into the + workgroup struct as domain master browser. This + will stop us syncing with ourself if we are also + a local master browser. */ + + make_nmb_name(&nmbname, myname, 0x20, scope); + + work->dmb_name = nmbname; + /* Pick the first interface ip address as the domain master browser ip. */ + my_first_ip = *iface_n_ip(0); + + putip((char *)&work->dmb_addr, &my_first_ip); + + /* We successfully registered by unicast with the + WINS server. We now expect to become the domain + master on the local subnets. If this fails, it's + probably a 1.9.16p2 to 1.9.16p11 server's fault. + + This is a configuration issue that should be addressed + by the network administrator - you shouldn't have + several machines configured as a domain master browser + for the same WINS scope (except if they are 1.9.17 or + greater, and you know what you're doing. + + see docs/DOMAIN.txt. + + */ + become_domain_master_browser_bcast(work->work_group); + } +} + +/**************************************************************************** + Start the name registration process when becoming a Domain Master Browser + on a subnet. + ****************************************************************************/ + +static void become_domain_master_stage1(struct subnet_record *subrec, char *wg_name) +{ + struct work_record *work; + + DEBUG(2,("become_domain_master_stage1: Becoming domain master browser for \ +workgroup %s on subnet %s\n", wg_name, subrec->subnet_name)); + + /* First, find the workgroup on the subnet. */ + if((work = find_workgroup_on_subnet( subrec, wg_name )) == NULL) + { + DEBUG(0,("become_domain_master_stage1: Error - unable to find workgroup %s on subnet %s.\n", + wg_name, subrec->subnet_name)); + return; + } + + DEBUG(3,("become_domain_master_stage1: go to first stage: register <1b> name\n")); + work->dom_state = DOMAIN_WAIT; + + /* WORKGROUP<1b> is the domain master browser name. */ + register_name(subrec, work->work_group,0x1b,samba_nb_type, + become_domain_master_stage2, + become_domain_master_fail, NULL); +} + +/**************************************************************************** + Function called when a query for a WORKGROUP<1b> name succeeds. + This is normally a fail condition as it means there is already + a domain master browser for a workgroup and we were trying to + become one. +****************************************************************************/ + +static void become_domain_master_query_success(struct subnet_record *subrec, + struct userdata_struct *userdata, + struct nmb_name *nmbname, struct in_addr ip, + struct res_rec *rrec) +{ + /* If the given ip is not ours, then we can't become a domain + controler as the name is already registered. + */ + + /* BUG note. Samba 1.9.16p11 servers seem to return the broadcast + address or zero ip for this query. Pretend this is ok. */ + + if(ismyip(ip) || ip_equal(allones_ip, ip) || ip_equal(ipzero, ip)) + { + DEBUG(3,("become_domain_master_query_success: Our address (%s) returned \ +in query for name %s (domain master browser name) on subnet %s. \ +Continuing with domain master code.\n", + inet_ntoa(ip), namestr(nmbname), subrec->subnet_name)); + + become_domain_master_stage1(subrec, nmbname->name); + } + else + { + DEBUG(0,("become_domain_master_query_success: There is already a domain \ +master browser at IP %s for workgroup %s registered on subnet %s.\n", + inet_ntoa(ip), nmbname->name, subrec->subnet_name)); + } +} + +/**************************************************************************** + Function called when a query for a WORKGROUP<1b> name fails. + This is normally a success condition as it then allows us to register + our own Domain Master Browser name. + ****************************************************************************/ + +static void become_domain_master_query_fail(struct subnet_record *subrec, + struct response_record *rrec, + struct nmb_name *question_name, int fail_code) +{ + /* If the query was unicast, and the error is not NAM_ERR (name didn't exist), + then this is a failure. Otherwise, not finding the name is what we want. */ + if((subrec == unicast_subnet) && (fail_code != NAM_ERR)) + { + DEBUG(0,("become_domain_master_query_fail: Error %d returned when \ +querying WINS server for name %s.\n", + fail_code, namestr(question_name))); + return; + } + + /* Otherwise - not having the name allows us to register it. */ + become_domain_master_stage1(subrec, question_name->name); +} + +/**************************************************************************** + Attempt to become a domain master browser on all broadcast subnets. + ****************************************************************************/ + +static void become_domain_master_browser_bcast(char *workgroup_name) +{ + struct subnet_record *subrec; + + for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) + { + struct work_record *work = find_workgroup_on_subnet(subrec, workgroup_name); + + if (work && (work->dom_state == DOMAIN_NONE)) + { + struct nmb_name nmbname; + make_nmb_name(&nmbname,workgroup_name,0x1b,scope); + + /* + * Check for our name on the given broadcast subnet first, only initiate + * further processing if we cannot find it. + */ + + if (find_name_on_subnet(subrec, &nmbname, FIND_SELF_NAME) == NULL) + { + DEBUG(0,("become_domain_master_browser_bcast: At time %s attempting to become domain \ +master browser on workgroup %s on subnet %s\n", timestring(), + workgroup_name, subrec->subnet_name)); + + /* Send out a query to establish whether there's a + domain controller on the local subnet. If not, + we can become a domain controller. + */ + + DEBUG(0,("become_domain_master_browser_bcast: querying subnet %s \ +for domain master browser on workgroup %s\n", subrec->subnet_name, workgroup_name)); + + query_name(subrec, nmbname.name, nmbname.name_type, + become_domain_master_query_success, + become_domain_master_query_fail, + NULL); + } + } + } +} + +/**************************************************************************** + Attempt to become a domain master browser by registering with WINS. + ****************************************************************************/ + +static void become_domain_master_browser_wins(char *workgroup_name) +{ + struct work_record *work; + + work = find_workgroup_on_subnet(unicast_subnet, workgroup_name); + + if (work && (work->dom_state == DOMAIN_NONE)) + { + struct nmb_name nmbname; + + make_nmb_name(&nmbname,workgroup_name,0x1b,scope); + + /* + * Check for our name on the unicast subnet first, only initiate + * further processing if we cannot find it. + */ + + if (find_name_on_subnet(unicast_subnet, &nmbname, FIND_SELF_NAME) == NULL) + { + DEBUG(0,("%s become_domain_master_browser_wins: attempting to become domain \ +master browser on workgroup %s, subnet %s.\n", + timestring(), workgroup_name, unicast_subnet->subnet_name)); + + /* Send out a query to establish whether there's a + domain master broswer registered with WINS. If not, + we can become a domain master browser. + */ + + DEBUG(0,("become_domain_master_browser_wins: querying WINS server at IP %s \ +for domain master browser name %s on workgroup %s\n", + inet_ntoa(unicast_subnet->myip), namestr(&nmbname), workgroup_name)); + + query_name(unicast_subnet, nmbname.name, nmbname.name_type, + become_domain_master_query_success, + become_domain_master_query_fail, + NULL); + } + } +} + +/**************************************************************************** + Add the domain logon server and domain master browser names + if we are set up to do so. + **************************************************************************/ + +void add_domain_names(time_t t) +{ + static time_t lastrun = 0; + + if ((lastrun != 0) && (t < lastrun + (CHECK_TIME_ADD_DOM_NAMES * 60))) + return; + + lastrun = t; + + /* Do the "internet group" - <1c> names. */ + if (lp_domain_logons()) + add_logon_names(); + + /* Do the domain master names. */ + if(lp_domain_master()) + { + if(we_are_a_wins_client()) + { + /* We register the WORKGROUP<1b> name with the WINS + server first, and call add_domain_master_bcast() + only if this is successful. + + This results in domain logon services being gracefully provided, + as opposed to the aggressive nature of 1.9.16p2 to 1.9.16p11. + 1.9.16p2 to 1.9.16p11 - due to a bug in namelogon.c, + cannot provide domain master / domain logon services. + */ + become_domain_master_browser_wins(myworkgroup); + } + else + become_domain_master_browser_bcast(myworkgroup); + } +} diff --git a/source3/nmbd/nmbd_become_lmb.c b/source3/nmbd/nmbd_become_lmb.c new file mode 100644 index 0000000000..7f54471a24 --- /dev/null +++ b/source3/nmbd/nmbd_become_lmb.c @@ -0,0 +1,534 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NBT netbios routines and daemon - version 2 + Copyright (C) Andrew Tridgell 1994-1997 + Copyright (C) Luke Kenneth Casson Leighton 1994-1997 + Copyright (C) Jeremy Allison 1994-1997 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "includes.h" + +extern int DEBUGLEVEL; +extern pstring scope; +extern pstring myname; + +extern uint16 samba_nb_type; /* Samba's NetBIOS name type. */ + +/******************************************************************* + Utility function to add a name to the unicast subnet, or add in + our IP address if it already exists. +******************************************************************/ + +static void insert_permanent_name_into_unicast( struct subnet_record *subrec, + struct nmb_name *nmbname, uint16 nb_type ) +{ + struct name_record *namerec; + + if((namerec = find_name_on_subnet(unicast_subnet, nmbname, FIND_SELF_NAME)) == NULL) + { + /* The name needs to be created on the unicast subnet. */ + add_name_to_subnet( unicast_subnet, nmbname->name, nmbname->name_type, + nb_type, PERMANENT_TTL, PERMANENT_NAME, 1, &subrec->myip); + } + else + { + /* The name already exists on the unicast subnet. Add our local + IP for the given broadcast subnet to the name. */ + add_ip_to_name_record( namerec, subrec->myip); + } +} + +/******************************************************************* + Utility function to remove a name from the unicast subnet. +******************************************************************/ + +static void remove_permanent_name_from_unicast( struct subnet_record *subrec, + struct nmb_name *nmbname ) +{ + struct name_record *namerec; + + if((namerec = find_name_on_subnet(unicast_subnet, nmbname, FIND_SELF_NAME)) != NULL) + { + /* Remove this broadcast subnet IP address from the name. */ + remove_ip_from_name_record( namerec, subrec->myip); + if(namerec->num_ips == 0) + remove_name_from_namelist( unicast_subnet, namerec); + } +} + +/******************************************************************* + Utility function always called to set our workgroup and server + state back to potential browser, or none. +******************************************************************/ + +static void reset_workgroup_state( struct subnet_record *subrec, char *workgroup_name ) +{ + struct work_record *work; + struct server_record *servrec; + struct nmb_name nmbname; + + if((work = find_workgroup_on_subnet( subrec, workgroup_name)) == NULL) + { + DEBUG(0,("reset_workgroup_state: Error - cannot find workgroup %s on \ +subnet %s.\n", workgroup_name, subrec->subnet_name )); + return; + } + + if((servrec = find_server_in_workgroup( work, myname)) == NULL) + { + DEBUG(0,("reset_workgroup_state: Error - cannot find server %s \ +in workgroup %s on subnet %s\n", + myname, work->work_group, subrec->subnet_name)); + work->mst_state = lp_local_master() ? MST_POTENTIAL : MST_NONE; + return; + } + + /* Update our server status - remove any master flag and replace + it with the potential browser flag. */ + servrec->serv.type &= ~SV_TYPE_MASTER_BROWSER; + servrec->serv.type |= (lp_local_master() ? SV_TYPE_POTENTIAL_BROWSER : 0); + + /* Tell the namelist writer to write out a change. */ + subrec->work_changed = True; + + /* Reset our election flags. */ + work->ElectionCriterion &= ~0x4; + + work->mst_state = lp_local_master() ? MST_POTENTIAL : MST_NONE; + + /* Forget who the local master browser was for + this workgroup. */ + + *work->local_master_browser_name = '\0'; + + /* + * Ensure the IP address of this subnet is not registered as one + * of the IP addresses of the WORKGROUP<1d> name on the unicast + * subnet. This undoes what we did below when we became a local + * master browser. + */ + + make_nmb_name(&nmbname, work->work_group, 0x1d, scope); + + remove_permanent_name_from_unicast( subrec, &nmbname); + +} + +/******************************************************************* + Unbecome the local master browser name release success function. +******************************************************************/ + +void unbecome_local_master_success(struct subnet_record *subrec, + struct userdata_struct *userdata, + struct nmb_name *released_name, + struct in_addr released_ip) +{ + DEBUG(3,("unbecome_local_master_success: released name %s.\n", + namestr(released_name))); + + /* Now reset the workgroup and server state. */ + reset_workgroup_state( subrec, released_name->name ); + + DEBUG(0,("\n***** Samba name server %s has stopped being a local master browser for workgroup %s \ +on subnet %s *****\n\n", myname, released_name->name, subrec->subnet_name)); + +} + +/******************************************************************* + Unbecome the local master browser name release fail function. +******************************************************************/ + +void unbecome_local_master_fail(struct subnet_record *subrec, struct response_record *rrec, + struct nmb_name *fail_name) +{ + struct name_record *namerec; + + DEBUG(0,("unbecome_local_master_fail: failed to release name %s. \ +Removing from namelist anyway.\n", namestr(fail_name))); + + /* Do it anyway. */ + namerec = find_name_on_subnet(subrec, fail_name, FIND_SELF_NAME); + if(namerec) + remove_name_from_namelist(subrec, namerec); + + /* Now reset the workgroup and server state. */ + reset_workgroup_state( subrec, fail_name->name ); + + DEBUG(0,("\n***** Samba name server %s has stopped being a local master browser for workgroup %s \ +on subnet %s *****\n\n", myname, fail_name->name, subrec->subnet_name)); + +} + +/******************************************************************* + Utility function to remove the WORKGROUP<1d> name called by both + success and fail of releasing the MSBROWSE name. +******************************************************************/ + +void release_1d_name( struct subnet_record *subrec, char *workgroup_name) +{ + struct nmb_name nmbname; + struct name_record *namerec; + + make_nmb_name(&nmbname, workgroup_name, 0x1d, scope); + if((namerec = find_name_on_subnet( subrec, &nmbname, FIND_SELF_NAME))!=NULL) + { + release_name(subrec, namerec, + unbecome_local_master_success, + unbecome_local_master_fail, + NULL); + } +} + +/******************************************************************* + Unbecome the local master browser MSBROWSE name release success function. +******************************************************************/ + +static void release_msbrowse_name_success(struct subnet_record *subrec, + struct userdata_struct *userdata, + struct nmb_name *released_name, + struct in_addr released_ip) +{ + DEBUG(4,("release_msbrowse_name_success: Released name %s on subnet %s\n.", + namestr(released_name), subrec->subnet_name )); + + /* Remove the permanent MSBROWSE name added into the unicast subnet. */ + remove_permanent_name_from_unicast( subrec, released_name); + + release_1d_name( subrec, userdata->data ); +} + +/******************************************************************* + Unbecome the local master browser MSBROWSE name release fail function. +******************************************************************/ + +static void release_msbrowse_name_fail( struct subnet_record *subrec, + struct response_record *rrec, + struct nmb_name *fail_name) +{ + struct userdata_struct *userdata = rrec->userdata; + struct name_record *namerec; + + DEBUG(4,("release_msbrowse_name_fail: Failed to release name %s on subnet %s\n.", + namestr(fail_name), subrec->subnet_name )); + + /* Release the name anyway. */ + namerec = find_name_on_subnet(subrec, fail_name, FIND_SELF_NAME); + if(namerec) + remove_name_from_namelist(subrec, namerec); + + /* Remove the permanent MSBROWSE name added into the unicast subnet. */ + remove_permanent_name_from_unicast( subrec, fail_name); + + release_1d_name( subrec, userdata->data ); +} + +/******************************************************************* + Unbecome the local master browser. +******************************************************************/ + +void unbecome_local_master_browser(struct subnet_record *subrec, struct work_record *work) +{ + struct server_record *servrec; + struct name_record *namerec; + struct nmb_name nmbname; + struct userdata_struct *userdata; + char ud[sizeof(struct userdata_struct) + sizeof(fstring)+1]; + + /* Sanity check. */ + + DEBUG(2,("unbecome_local_master_browser: unbecoming local master for workgroup %s \ +on subnet %s\n",work->work_group, subrec->subnet_name)); + + if((servrec = find_server_in_workgroup( work, myname)) == NULL) + { + DEBUG(0,("unbecome_local_master_browser: Error - cannot find server %s \ +in workgroup %s on subnet %s\n", + myname, work->work_group, subrec->subnet_name)); + work->mst_state = lp_local_master() ? MST_POTENTIAL : MST_NONE; + return; + } + + /* Set the state to unbecoming. */ + work->mst_state = MST_UNBECOMING_MASTER; + + /* Setup the userdata for the MSBROWSE name release. */ + /* Setup the userdata_struct - this is copied so we can use + a stack variable for this. */ + userdata = (struct userdata_struct *)ud; + + userdata->copy_fn = NULL; + userdata->free_fn = NULL; + userdata->userdata_len = strlen(work->work_group)+1; + strcpy(userdata->data, work->work_group); + + /* Deregister any browser names we may have. */ + make_nmb_name(&nmbname, MSBROWSE, 0x1, scope); + if((namerec = find_name_on_subnet( subrec, &nmbname, FIND_SELF_NAME))!=NULL) + { + release_name(subrec, namerec, + release_msbrowse_name_success, + release_msbrowse_name_fail, + userdata); + } +} + +/**************************************************************************** + Success in registering the WORKGROUP<1d> name. + We are now *really* a local master browser. + ****************************************************************************/ + +static void become_local_master_stage2(struct subnet_record *subrec, + struct userdata_struct *userdata, + struct nmb_name *registered_name, + uint16 nb_flags, + int ttl, struct in_addr registered_ip) +{ + int i = 0; + struct server_record *sl; + struct work_record *work = find_workgroup_on_subnet( subrec, registered_name->name); + struct server_record *servrec; + + if(!work) + { + DEBUG(0,("become_local_master_stage2: Error - cannot find \ +workgroup %s on subnet %s\n", registered_name->name, subrec->subnet_name)); + return; + } + + if((servrec = find_server_in_workgroup( work, myname)) == NULL) + { + DEBUG(0,("become_local_master_stage2: Error - cannot find server %s \ +in workgroup %s on subnet %s\n", + myname, registered_name->name, subrec->subnet_name)); + work->mst_state = lp_local_master() ? MST_POTENTIAL : MST_NONE; + return; + } + + DEBUG(3,("become_local_master_stage2: registered as master browser for workgroup %s \ +on subnet %s\n", work->work_group, subrec->subnet_name)); + + work->mst_state = MST_BROWSER; /* registering WORKGROUP(1d) succeeded */ + + /* update our server status */ + servrec->serv.type |= SV_TYPE_MASTER_BROWSER; + servrec->serv.type &= ~SV_TYPE_POTENTIAL_BROWSER; + + /* Tell the namelist writer to write out a change. */ + subrec->work_changed = True; + + /* Add this name to the workgroup as local master browser. */ + StrnCpy(work->local_master_browser_name, myname, + sizeof(work->local_master_browser_name)-1); + + /* Count the number of servers we have on our list. If it's + less than 10 (just a heuristic) request the servers + to announce themselves. + */ + for( sl = work->serverlist; sl != NULL; sl = sl->next) + i++; + + if (i < 10) + { + /* Ask all servers on our local net to announce to us. */ + broadcast_announce_request(subrec, work); + } + + /* + * Now we are a local master on a broadcast subnet, we need to add + * the WORKGROUP<1d> name to the unicast subnet so that we can answer + * unicast requests sent to this name. We can create this name directly on + * the unicast subnet as a WINS server always returns true when registering + * this name, and discards the registration. We use the number of IP + * addresses registered to this name as a reference count, as we + * remove this broadcast subnet IP address from it when we stop becoming a local + * master browser for this broadcast subnet. + */ + + insert_permanent_name_into_unicast( subrec, registered_name, nb_flags); + + /* Reset the announce master browser timer so that we try and tell a domain + master browser as soon as possible that we are a local master browser. */ + reset_announce_timer(); + + DEBUG(0,("\n***** Samba name server %s is now a local master browser for workgroup %s \ +on subnet %s *****\n\n", myname, work->work_group, subrec->subnet_name)); + +} + +/**************************************************************************** + Failed to register the WORKGROUP<1d> name. + ****************************************************************************/ +static void become_local_master_fail2(struct subnet_record *subrec, + struct response_record *rrec, + struct nmb_name *fail_name) +{ + struct work_record *work = find_workgroup_on_subnet( subrec, fail_name->name); + + DEBUG(0,("become_local_master_fail2: failed to register name %s on subnet %s. \ +Failed to become a local master browser.\n", namestr(fail_name), subrec->subnet_name)); + + if(!work) + { + DEBUG(0,("become_local_master_fail2: Error - cannot find \ +workgroup %s on subnet %s\n", fail_name->name, subrec->subnet_name)); + return; + } + + /* Roll back all the way by calling unbecome_local_master_browser(). */ + unbecome_local_master_browser(subrec, work); +} + +/**************************************************************************** + Success in registering the MSBROWSE name. + ****************************************************************************/ + +static void become_local_master_stage1(struct subnet_record *subrec, + struct userdata_struct *userdata, + struct nmb_name *registered_name, + uint16 nb_flags, + int ttl, struct in_addr registered_ip) +{ + char *work_name = userdata->data; + struct work_record *work = find_workgroup_on_subnet( subrec, work_name); + + if(!work) + { + DEBUG(0,("become_local_master_stage1: Error - cannot find \ +workgroup %s on subnet %s\n", work_name, subrec->subnet_name)); + return; + } + + DEBUG(3,("become_local_master_stage1: go to stage 2: register the %s<1d> name.\n", + work->work_group)); + + work->mst_state = MST_MSB; /* Registering MSBROWSE was successful. */ + + /* + * We registered the MSBROWSE name on a broadcast subnet, now need to add + * the MSBROWSE name to the unicast subnet so that we can answer + * unicast requests sent to this name. We create this name directly on + * the unicast subnet. + */ + + insert_permanent_name_into_unicast( subrec, registered_name, nb_flags); + + /* Attempt to register the WORKGROUP<1d> name. */ + register_name(subrec, work->work_group,0x1d,samba_nb_type, + become_local_master_stage2, + become_local_master_fail2, + NULL); +} + +/**************************************************************************** + Failed to register the MSBROWSE name. + ****************************************************************************/ + +static void become_local_master_fail1(struct subnet_record *subrec, + struct response_record *rrec, + struct nmb_name *fail_name) +{ + char *work_name = rrec->userdata->data; + struct work_record *work = find_workgroup_on_subnet(subrec, work_name); + struct server_record *servrec; + + if(!work) + { + DEBUG(0,("become_local_master_fail1: Error - cannot find \ +workgroup %s on subnet %s\n", work_name, subrec->subnet_name)); + return; + } + + if((servrec = find_server_in_workgroup(work, myname)) == NULL) + { + DEBUG(0,("become_local_master_fail1: Error - cannot find server %s \ +in workgroup %s on subnet %s\n", + myname, work->work_group, subrec->subnet_name)); + return; + } + + reset_workgroup_state( subrec, work->work_group ); + + DEBUG(0,("become_local_master_fail1: Failed to become a local master browser for \ +workgroup %s on subnet %s. Couldn't register name %s.\n", + work->work_group, subrec->subnet_name, namestr(fail_name))); +} + +/****************************************************************** + Become the local master browser on a subnet. + This gets called if we win an election on this subnet. + + Stage 1: mst_state was MST_POTENTIAL - go to MST_BACK register ^1^2__MSBROWSE__^2^1. + Stage 2: mst_state was MST_BACKUP - go to MST_MSB and register WORKGROUP<1d>. + Stage 3: mst_state was MST_MSB - go to MST_BROWSER. +******************************************************************/ + +void become_local_master_browser(struct subnet_record *subrec, struct work_record *work) +{ + struct server_record *servrec; + struct userdata_struct *userdata; + char ud[sizeof(struct userdata_struct) + sizeof(fstring)+1]; + + /* Sanity check. */ + if (!lp_local_master()) + { + DEBUG(0,("become_local_master_browser: Samba not configured as a local master browser.\n")); + return; + } + + if(!AM_POTENTIAL_MASTER_BROWSER(work)) + { + DEBUG(2,("become_local_master_browser: Awaiting potential browser state. Current state is %d\n", + work->mst_state )); + return; + } + + if((servrec = find_server_in_workgroup( work, myname)) == NULL) + { + DEBUG(0,("become_local_master_browser: Error - cannot find server %s \ +in workgroup %s on subnet %s\n", + myname, work->work_group, subrec->subnet_name)); + return; + } + + DEBUG(2,("become_local_master_browser: Starting to become a master browser for workgroup \ +%s on subnet %s\n", work->work_group, subrec->subnet_name)); + + DEBUG(3,("become_local_master_browser: first stage - attempt to register ^1^2__MSBROWSE__^2^1\n")); + work->mst_state = MST_BACKUP; /* an election win was successful */ + + work->ElectionCriterion |= 0x5; + + /* Tell the namelist writer to write out a change. */ + subrec->work_changed = True; + + /* Setup the userdata_struct - this is copied so we can use + a stack variable for this. */ + userdata = (struct userdata_struct *)ud; + + userdata->copy_fn = NULL; + userdata->free_fn = NULL; + userdata->userdata_len = strlen(work->work_group)+1; + strcpy(userdata->data, work->work_group); + + /* Register the special browser group name. */ + register_name(subrec, MSBROWSE, 0x01, samba_nb_type|NB_GROUP, + become_local_master_stage1, + become_local_master_fail1, + userdata); +} diff --git a/source3/nmbd/nmbd_browserdb.c b/source3/nmbd/nmbd_browserdb.c new file mode 100644 index 0000000000..b2db744370 --- /dev/null +++ b/source3/nmbd/nmbd_browserdb.c @@ -0,0 +1,184 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NBT netbios routines and daemon - version 2 + Copyright (C) Andrew Tridgell 1994-1997 + Copyright (C) Luke Kenneth Casson Leighton 1994-1997 + Copyright (C) Jeremy Allison 1994-1997 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "includes.h" +#include "smb.h" + +extern int DEBUGLEVEL; + +/* This is our local master browser list database. */ +struct browse_cache_record *lmb_browserlist = NULL; + +/*************************************************************************** +Add a browser into the lmb list. +**************************************************************************/ + +static void add_to_lmb_browse_cache(struct browse_cache_record *browc) +{ + struct browse_cache_record *browc2; + + if (lmb_browserlist == NULL) + { + lmb_browserlist = browc; + browc->prev = NULL; + browc->next = NULL; + return; + } + + for (browc2 = lmb_browserlist; browc2->next; browc2 = browc2->next) + ; + + browc2->next = browc; + browc->next = NULL; + browc->prev = browc2; +} + +/******************************************************************* +Remove a lmb browser entry. +******************************************************************/ + +void remove_lmb_browser_entry(struct browse_cache_record *browc) +{ + if (browc->prev) + browc->prev->next = browc->next; + if (browc->next) + browc->next->prev = browc->prev; + + if (lmb_browserlist == browc) + lmb_browserlist = browc->next; + + free((char *)browc); +} + +/**************************************************************************** +Update a browser death time. +****************************************************************************/ + +void update_browser_death_time(struct browse_cache_record *browc) +{ + /* Allow the new lmb to miss an announce period before we remove it. */ + browc->death_time = time(NULL) + (CHECK_TIME_MST_ANNOUNCE + 2)*60; +} + +/**************************************************************************** +Create a browser entry. +****************************************************************************/ + +struct browse_cache_record *create_browser_in_lmb_cache(char *work_name, char *browser_name, + struct in_addr ip) +{ + struct browse_cache_record *browc; + time_t now = time(NULL); + + browc = (struct browse_cache_record *)malloc(sizeof(*browc)); + + if (browc == NULL) + { + DEBUG(0,("create_browser_in_lmb_cache: malloc fail !\n")); + return(NULL); + } + + bzero((char *)browc,sizeof(*browc)); + + /* For a new lmb entry we want to sync with it after one minute. This + will allow it time to send out a local announce and build its + browse list. */ + + browc->sync_time = now + 60; + + /* Allow the new lmb to miss an announce period before we remove it. */ + browc->death_time = now + (CHECK_TIME_MST_ANNOUNCE + 2)*60; + + StrnCpy(browc->lmb_name, browser_name,sizeof(browc->lmb_name)-1); + StrnCpy(browc->work_group,work_name,sizeof(browc->work_group)-1); + strupper(browc->lmb_name); + strupper(browc->work_group); + + browc->ip = ip; + + add_to_lmb_browse_cache(browc); + + DEBUG(3,("create_browser_in_lmb_cache: Added lmb cache entry for workgroup %s name %s IP %s ttl %d\n", + browc->work_group, browc->lmb_name, inet_ntoa(ip), browc->death_time)); + + return(browc); +} + +/**************************************************************************** +Find a browser entry. +****************************************************************************/ + +struct browse_cache_record *find_browser_in_lmb_cache( char *browser_name ) +{ + struct browse_cache_record *browc = NULL; + + for( browc = lmb_browserlist; browc; browc = browc->next) + if(strequal( browser_name, browc->lmb_name)) + break; + + return browc; +} + +/******************************************************************* + Expire timed out browsers in the browserlist. +******************************************************************/ + +void expire_lmb_browsers(time_t t) +{ + struct browse_cache_record *browc; + struct browse_cache_record *nextbrowc; + + for (browc = lmb_browserlist; browc; browc = nextbrowc) + { + nextbrowc = browc->next; + + if (browc->death_time < t) + { + DEBUG(3,("expire_lmb_browsers: Removing timed out lmb entry %s\n",browc->lmb_name)); + remove_lmb_browser_entry(browc); + } + } +} + +/******************************************************************* + Remove browsers from a named workgroup in the browserlist. +******************************************************************/ + +void remove_workgroup_lmb_browsers(char *work_group) +{ + struct browse_cache_record *browc; + struct browse_cache_record *nextbrowc; + + for (browc = lmb_browserlist; browc; browc = nextbrowc) + { + nextbrowc = browc->next; + + if (strequal(work_group, browc->work_group)) + { + DEBUG(3,("remove_workgroup_browsers: Removing lmb entry %s\n",browc->lmb_name)); + remove_lmb_browser_entry(browc); + } + } +} + diff --git a/source3/nmbd/nmbd_browsesync.c b/source3/nmbd/nmbd_browsesync.c new file mode 100644 index 0000000000..b899e2e8bc --- /dev/null +++ b/source3/nmbd/nmbd_browsesync.c @@ -0,0 +1,458 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NBT netbios routines and daemon - version 2 + Copyright (C) Andrew Tridgell 1994-1997 + Copyright (C) Luke Kenneth Casson Leighton 1994-1997 + Copyright (C) Jeremy Allison 1994-1997 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "includes.h" +#include "smb.h" + +extern int DEBUGLEVEL; +extern pstring scope; +extern struct in_addr ipzero; +extern pstring myname; + +/* This is our local master browser list database. */ +extern struct browse_cache_record *lmb_browserlist; + +static struct work_record *call_work; +static struct subnet_record *call_subrec; + +/******************************************************************* + This is the NetServerEnum callback. + ******************************************************************/ + +static void callback(char *sname, uint32 stype, char *comment) +{ + struct work_record *work; + + stype &= ~SV_TYPE_LOCAL_LIST_ONLY; + + if (stype & SV_TYPE_DOMAIN_ENUM) + { + /* See if we can find the workgroup on this subnet. */ + if(( work = find_workgroup_on_subnet( call_subrec, sname )) != NULL) + { + /* We already know about this workgroup - update the ttl. */ + update_workgroup_ttl( work, lp_max_ttl() ); + } + else + { + /* Create the workgroup on the subnet. */ + create_workgroup_on_subnet( call_subrec, sname, lp_max_ttl() ); + } + } + else + { + /* Server entry. */ + struct server_record *servrec; + + work = call_work; + + if(( servrec = find_server_in_workgroup( work, sname )) != NULL) + { + /* Check that this is not a locally known server - if so ignore the + entry. */ + if(!(servrec->serv.type & SV_TYPE_LOCAL_LIST_ONLY)) + { + /* We already know about this server - update the ttl. */ + update_server_ttl(servrec, lp_max_ttl() ); + /* Update the type. */ + servrec->serv.type = stype; + } + } + else + { + /* Create the server in the workgroup. */ + create_server_on_workgroup(work, sname,stype,lp_max_ttl(),comment); + } + } +} + +/******************************************************************* + Synchronise browse lists with another browse server. + Log in on the remote server's SMB port to their IPC$ service, + do a NetServerEnum and update our server and workgroup databases. +******************************************************************/ + +static void sync_browse_lists(struct subnet_record *subrec, struct work_record *work, + char *name, int nm_type, struct in_addr ip, BOOL local) +{ + extern fstring local_machine; + static struct cli_state cli; + uint32 local_type = local ? SV_TYPE_LOCAL_LIST_ONLY : 0; + + DEBUG(2,("%s: sync_browse_lists: Sync browse lists with server %s<%02x> at IP %s for workgroup %s\n", + timestring(), name, nm_type, inet_ntoa(ip), work->work_group )); + + /* Check we're not trying to sync with ourselves. This can happen if we are + a domain *and* a local master browser. */ + if(ismyip(ip)) + { + DEBUG(2,("sync_browse_lists: We are both a domain and a local master browser for workgroup %s. \ +Do not sync with ourselves.\n", work->work_group )); + return; + } + + if (!cli_initialise(&cli) || !cli_connect(&cli, name, &ip)) + { + DEBUG(0,("sync_browse_lists: Failed to start browse sync with %s\n", name)); + return; + } + + if (!cli_session_request(&cli, name, nm_type, local_machine)) + { + DEBUG(0,("sync_browse_lists: %s rejected the browse sync session\n",name)); + cli_shutdown(&cli); + return; + } + + if (!cli_negprot(&cli)) + { + DEBUG(0,("sync_browse_lists: %s rejected the negprot\n",name)); + cli_shutdown(&cli); + return; + } + + if (!cli_session_setup(&cli, "", "", 1, "", 0, work->work_group)) + { + DEBUG(0,("sync_browse_lists: %s rejected the browse sync sessionsetup\n", + name)); + cli_shutdown(&cli); + return; + } + + if (!cli_send_tconX(&cli, "IPC$", "IPC", "", 1)) + { + DEBUG(0,("sync_browse_lists: %s refused browse sync IPC$ connect\n", name)); + cli_shutdown(&cli); + return; + } + + call_work = work; + call_subrec = subrec; + + /* Fetch a workgroup list. */ + cli_NetServerEnum(&cli, work->work_group, + local_type|SV_TYPE_DOMAIN_ENUM, + callback); + + /* Now fetch a server list. */ + cli_NetServerEnum(&cli, work->work_group, + local?SV_TYPE_LOCAL_LIST_ONLY:SV_TYPE_ALL, + callback); + + cli_shutdown(&cli); +} + +/**************************************************************************** +As a domain master browser, do a sync with a local master browser. +**************************************************************************/ + +static void sync_with_lmb(struct browse_cache_record *browc) +{ + struct work_record *work; + + if (!(work = find_workgroup_on_subnet(unicast_subnet, browc->work_group))) { + DEBUG(0, ("sync_with_lmb: failed to get a \ +workgroup for a local master browser cache entry workgroup %s, server %s\n", + browc->work_group, browc->lmb_name)); + return; + } + + /* We should only be doing this if we are a domain master browser for + the given workgroup. Ensure this is so. */ + + if(!AM_DOMAIN_MASTER_BROWSER(work)) + { + DEBUG(0,("sync_with_lmb: We are trying to sync with a local master browser %s \ +for workgroup %s and we are not a domain master browser on this workgroup. Error !\n", + browc->lmb_name, browc->work_group)); + return; + } + + DEBUG(2, ("sync_with_lmb: Initiating sync with local master browser %s<0x20> at IP %s for \ +workgroup %s\n", browc->lmb_name, inet_ntoa(browc->ip), browc->work_group)); + + sync_browse_lists(unicast_subnet, work, browc->lmb_name, 0x20, browc->ip, True); + + browc->sync_time += (CHECK_TIME_DMB_TO_LMB_SYNC * 60); +} + +/**************************************************************************** +Sync or expire any local master browsers. +**************************************************************************/ + +void dmb_expire_and_sync_browser_lists(time_t t) +{ + static time_t last_run = 0; + struct browse_cache_record *browc; + + /* Only do this every 20 seconds. */ + if (t - last_run < 20) + return; + + last_run = t; + + expire_lmb_browsers(t); + + for (browc = lmb_browserlist; browc; browc = browc->next) + { + if (browc->sync_time < t) + sync_with_lmb(browc); + } +} + +/**************************************************************************** +As a local master browser, send an announce packet to the domain master browser. +**************************************************************************/ + +static void announce_local_master_browser_to_domain_master_browser( struct work_record *work) +{ + pstring outbuf; + char *p; + + if(ismyip(work->dmb_addr)) + { + DEBUG(2,("announce_local_master_browser_to_domain_master_browser: We are both a domain \ +and a local master browser for workgroup %s. \ +Do not announce to ourselves.\n", work->work_group )); + return; + } + + bzero(outbuf,sizeof(outbuf)); + p = outbuf; + CVAL(p,0) = ANN_MasterAnnouncement; + p++; + + StrnCpy(p,myname,15); + strupper(p); + p = skip_string(p,1); + + DEBUG(4,("announce_local_master_browser_to_domain_master_browser: Sending local master announce \ +to %s for workgroup %s.\n", namestr(&work->dmb_name), work->work_group )); + + send_mailslot(True, BROWSE_MAILSLOT, outbuf,PTR_DIFF(p,outbuf), + myname, 0x0, work->dmb_name.name, 0x20, work->dmb_addr, FIRST_SUBNET->myip); + +} + +/**************************************************************************** +As a local master browser, do a sync with a domain master browser. +**************************************************************************/ + +static void sync_with_dmb(struct work_record *work) +{ + DEBUG(2, ("sync_with_dmb: Initiating sync with domain master browser %s at IP %s for \ +workgroup %s\n", namestr(&work->dmb_name), inet_ntoa(work->dmb_addr), work->work_group)); + + sync_browse_lists(unicast_subnet, work, work->dmb_name.name, work->dmb_name.name_type, + work->dmb_addr, False); +} + +/**************************************************************************** + Function called when a node status query to a domain master browser IP succeeds. +****************************************************************************/ + +static void domain_master_node_status_success(struct subnet_record *subrec, + struct userdata_struct *userdata, + struct res_rec *answers, + struct in_addr from_ip) +{ + struct work_record *work = find_workgroup_on_subnet( subrec, userdata->data); + + if(work == NULL) + { + DEBUG(0,("domain_master_node_status_success: Unable to find workgroup %s on subnet %s.\n", + userdata->data, subrec->subnet_name)); + return; + } + + DEBUG(3,("domain_master_node_status_success: Success in node status for workgroup %s from ip %s\n", + work->work_group, inet_ntoa(from_ip) )); + + /* Go through the list of names found at answers->rdata and look for + the first SERVER<0x20> name. */ + + if(answers->rdata != NULL) + { + char *p = answers->rdata; + int numnames = CVAL(p, 0); + + p += 1; + + while (numnames--) + { + char qname[17]; + uint16 nb_flags; + int name_type; + + StrnCpy(qname,p,15); + name_type = CVAL(p,15); + nb_flags = get_nb_flags(&p[16]); + trim_string(qname,NULL," "); + + p += 18; + + if(!(nb_flags & NB_GROUP) && (name_type == 0x20)) + { + struct nmb_name nmbname; + + make_nmb_name(&nmbname, qname, name_type, scope); + + /* Copy the dmb name and IP address + into the workgroup struct. */ + + work->dmb_name = nmbname; + putip((char *)&work->dmb_addr, &from_ip); + + /* Do the local master browser announcement to the domain + master browser name and IP. */ + announce_local_master_browser_to_domain_master_browser( work ); + + /* Now synchronise lists with the domain master browser. */ + sync_with_dmb(work); + break; + } + } + } + else + DEBUG(0,("domain_master_node_status_success: Failed to find a SERVER<0x20> \ +name in reply from IP %s.\n", inet_ntoa(from_ip) )); +} + +/**************************************************************************** + Function called when a node status query to a domain master browser IP fails. +****************************************************************************/ + +static void domain_master_node_status_fail(struct subnet_record *subrec, + struct response_record *rrec) +{ + struct userdata_struct *userdata = rrec->userdata; + + DEBUG(0,("domain_master_node_status_fail: Doing a node status request to \ +the domain master browser for workgroup %s at IP %s failed. Cannot sync browser \ +lists.\n", userdata->data, inet_ntoa(rrec->packet->ip) )); + +} + +/**************************************************************************** + Function called when a query for a WORKGROUP<1b> name succeeds. +****************************************************************************/ + +static void find_domain_master_name_query_success(struct subnet_record *subrec, + struct userdata_struct *userdata_in, + struct nmb_name *q_name, struct in_addr answer_ip, struct res_rec *rrec) +{ + /* + * Unfortunately, finding the IP address of the Domain Master Browser, + * as we have here, is not enough. We need to now do a sync to the + * SERVERNAME<0x20> NetBIOS name, as only recent NT servers will + * respond to the SMBSERVER name. To get this name from IP + * address we do a Node status request, and look for the first + * NAME<0x20> in the response, and take that as the server name. + * We also keep a cache of the Domain Master Browser name for this + * workgroup in the Workgroup struct, so that if the same IP addess + * is returned every time, we don't need to do the node status + * request. + */ + + struct work_record *work; + struct nmb_name nmbname; + struct userdata_struct *userdata; + char ud[sizeof(struct userdata_struct) + sizeof(fstring)+1]; + + if (!(work = find_workgroup_on_subnet(subrec, q_name->name))) { + DEBUG(0, ("find_domain_master_name_query_success: failed to find \ +workgroup %s\n", q_name->name )); + return; + } + + /* First check if we already have a dmb for this workgroup. */ + + if(!ip_equal(work->dmb_addr, ipzero) && ip_equal(work->dmb_addr, answer_ip)) + { + /* Do the local master browser announcement to the domain + master browser name and IP. */ + announce_local_master_browser_to_domain_master_browser( work ); + + /* Now synchronise lists with the domain master browser. */ + sync_with_dmb(work); + return; + } + else + putip((char *)&work->dmb_addr, &ipzero); + + /* Now initiate the node status request. */ + bzero((char *)&nmbname, sizeof(nmbname)); + nmbname.name[0] = '*'; + + /* Put the workgroup name into the userdata so we know + what workgroup we're talking to when the reply comes + back. */ + + /* Setup the userdata_struct - this is copied so we can use + a stack variable for this. */ + userdata = (struct userdata_struct *)ud; + + userdata->copy_fn = NULL; + userdata->free_fn = NULL; + userdata->userdata_len = strlen(work->work_group)+1; + strcpy(userdata->data, work->work_group); + + node_status( subrec, &nmbname, answer_ip, + domain_master_node_status_success, + domain_master_node_status_fail, + userdata); +} + +/**************************************************************************** + Function called when a query for a WORKGROUP<1b> name fails. + ****************************************************************************/ +static void find_domain_master_name_query_fail(struct subnet_record *subrec, + struct response_record *rrec, + struct nmb_name *question_name, int fail_code) +{ + DEBUG(0,("find_domain_master_name_query_fail: Unable to find the Domain Master \ +Browser name %s for the workgroup %s. Unable to sync browse lists in this workgroup.\n", + namestr(question_name), question_name->name )); +} + +/**************************************************************************** +As a local master browser for a workgroup find the domain master browser +name, announce ourselves as local master browser to it and then pull the +full domain browse lists from it onto the given subnet. +**************************************************************************/ + +void announce_and_sync_with_domain_master_browser( struct subnet_record *subrec, + struct work_record *work) +{ + struct nmb_name nmbname; + + make_nmb_name(&nmbname,work->work_group,0x1b,scope); + + /* First, query for the WORKGROUP<1b> name from the WINS server. */ + query_name(unicast_subnet, nmbname.name, nmbname.name_type, + find_domain_master_name_query_success, + find_domain_master_name_query_fail, + NULL); + +} diff --git a/source3/nmbd/nmbd_elections.c b/source3/nmbd/nmbd_elections.c new file mode 100644 index 0000000000..5c3c4c7a01 --- /dev/null +++ b/source3/nmbd/nmbd_elections.c @@ -0,0 +1,348 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NBT netbios routines and daemon - version 2 + Copyright (C) Andrew Tridgell 1994-1997 + Copyright (C) Luke Kenneth Casson Leighton 1994-1997 + Copyright (C) Jeremy Allison 1994-1997 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "includes.h" + +extern int DEBUGLEVEL; + +extern pstring myname; +extern fstring myworkgroup; + +/* Election parameters. */ +extern time_t StartupTime; + +/**************************************************************************** + Send an election datagram packet. +**************************************************************************/ +static void send_election_dgram(struct subnet_record *subrec, char *workgroup_name, + uint32 criterion, int timeup,char *server_name) +{ + pstring outbuf; + char *p; + + DEBUG(2,("send_election_dgram: Sending election packet for workgroup %s on subnet %s\n", + workgroup_name, subrec->subnet_name )); + + bzero(outbuf,sizeof(outbuf)); + p = outbuf; + CVAL(p,0) = ANN_Election; /* Election opcode. */ + p++; + + CVAL(p,0) = (criterion == 0 && timeup == 0) ? 0 : ELECTION_VERSION; + SIVAL(p,1,criterion); + SIVAL(p,5,timeup*1000); /* ms - Despite what the spec says. */ + p += 13; + pstrcpy(p,server_name); + strupper(p); + p = skip_string(p,1); + + send_mailslot(False, BROWSE_MAILSLOT, outbuf, PTR_DIFF(p,outbuf), + server_name, 0, + workgroup_name, 0x1e, + subrec->bcast_ip, subrec->myip); +} + +/******************************************************************* + We found a current master browser on one of our broadcast interfaces. +******************************************************************/ + +static void check_for_master_browser_success(struct subnet_record *subrec, + struct userdata_struct *userdata, + struct nmb_name *answer_name, + struct in_addr answer_ip, struct res_rec *rrec) +{ + DEBUG(3,("check_for_master_browser_success: Local master browser for workgroup %s exists at \ +IP %s (just checking).\n", answer_name->name, inet_ntoa(answer_ip) )); +} + +/******************************************************************* + We failed to find a current master browser on one of our broadcast interfaces. +******************************************************************/ + +static void check_for_master_browser_fail( struct subnet_record *subrec, + struct response_record *rrec, + struct nmb_name *question_name, + int fail_code) +{ + char *workgroup_name = question_name->name; + struct work_record *work = find_workgroup_on_subnet(subrec, workgroup_name); + + if(work == NULL) + { + DEBUG(0,("check_for_master_browser_fail: Unable to find workgroup %s on subnet %s.=\n", + workgroup_name, subrec->subnet_name )); + return; + } + + if (strequal(work->work_group, myworkgroup)) + { + + if (lp_local_master()) + { + /* We have discovered that there is no local master + browser, and we are configured to initiate + an election that we will participate in. + */ + DEBUG(2,("check_for_master_browser_fail: Forcing election on workgroup %s subnet %s\n", + work->work_group, subrec->subnet_name )); + + /* Setting this means we will participate when the + election is run in run_elections(). */ + work->needelection = True; + } + else + { + /* We need to force an election, because we are configured + not to become the local master, but we still need one, + having detected that one doesn't exist. + */ + send_election_dgram(subrec, work->work_group, 0, 0, myname); + } + } +} + +/******************************************************************* + Ensure there is a local master browser for a workgroup on our + broadcast interfaces. +******************************************************************/ + +void check_master_browser_exists(time_t t) +{ + static time_t lastrun=0; + struct subnet_record *subrec; + char *workgroup_name = myworkgroup; + + if (!lastrun) + lastrun = t; + + if (t < (lastrun + (CHECK_TIME_MST_BROWSE * 60))) + return; + + lastrun = t; + + dump_workgroups(); + + for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) + { + struct work_record *work; + + for (work = subrec->workgrouplist; work; work = work->next) + { + if (strequal(work->work_group, workgroup_name) && !AM_LOCAL_MASTER_BROWSER(work)) + { + /* Do a name query for the local master browser on this net. */ + query_name( subrec, work->work_group, 0x1d, + check_for_master_browser_success, + check_for_master_browser_fail, + NULL); + } + } + } +} + +/******************************************************************* + Run an election. +******************************************************************/ + +void run_elections(time_t t) +{ + static time_t lastime = 0; + + struct subnet_record *subrec; + + /* Send election packets once a second - note */ + if (lastime && (t - lastime <= 0)) + return; + + lastime = t; + + for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) + { + struct work_record *work; + + for (work = subrec->workgrouplist; work; work = work->next) + { + if (work->RunningElection) + { + send_election_dgram(subrec, work->work_group, work->ElectionCriterion, + t - StartupTime, myname); + + if (work->ElectionCount++ >= 4) + { + /* Won election (4 packets were sent out uncontested. */ + DEBUG(2,("run_elections: >>> Won election for workgroup %s on subnet %s <<<\n", + work->work_group, subrec->subnet_name )); + + work->RunningElection = False; + + become_local_master_browser(subrec, work); + } + } + } + } +} + +/******************************************************************* + Determine if I win an election. +******************************************************************/ + +static BOOL win_election(struct work_record *work, int version, + uint32 criterion, int timeup, char *server_name) +{ + int mytimeup = time(NULL) - StartupTime; + uint32 mycriterion = work->ElectionCriterion; + + /* If local master is false then never win + in election broadcasts. */ + if(!lp_local_master()) + { + DEBUG(3,("win_election: Losing election as local master == False\n")); + return False; + } + + DEBUG(4,("win_election: election comparison: %x:%x %x:%x %d:%d %s:%s\n", + version, ELECTION_VERSION, + criterion, mycriterion, + timeup, mytimeup, + server_name, myname)); + + if (version > ELECTION_VERSION) + return(False); + if (version < ELECTION_VERSION) + return(True); + + if (criterion > mycriterion) + return(False); + if (criterion < mycriterion) + return(True); + + if (timeup > mytimeup) + return(False); + if (timeup < mytimeup) + return(True); + + if (strcasecmp(myname, server_name) > 0) + return(False); + + return(True); +} + +/******************************************************************* + Process an incoming election datagram packet. +******************************************************************/ + +void process_election(struct subnet_record *subrec, struct packet_struct *p, char *buf) +{ + struct dgram_packet *dgram = &p->packet.dgram; + int version = CVAL(buf,0); + uint32 criterion = IVAL(buf,1); + int timeup = IVAL(buf,5)/1000; + char *server_name = buf+13; + struct work_record *work; + char *workgroup_name = dgram->dest_name.name; + + server_name[15] = 0; + + DEBUG(3,("process_election: Election request from %s at IP %s on subnet %s for workgroup %s.\n", + server_name,inet_ntoa(p->ip), subrec->subnet_name, workgroup_name )); + + DEBUG(5,("process_election: vers=%d criterion=%08x timeup=%d\n", version,criterion,timeup)); + + if(( work = find_workgroup_on_subnet(subrec, workgroup_name)) == NULL) + { + DEBUG(0,("process_election: Cannot find workgroup %s on subnet %s.\n", + workgroup_name, subrec->subnet_name )); + return; + } + + if (!strequal(work->work_group, myworkgroup)) + { + DEBUG(3,("process_election: ignoring election request for workgroup %s on subnet %s as this \ +is not my workgroup.\n", work->work_group, subrec->subnet_name )); + return; + } + + if (win_election(work, version,criterion,timeup,server_name)) + { + /* We take precedence over the requesting server. */ + if (!work->RunningElection) + { + /* We weren't running an election - start running one. */ + + work->needelection = True; + work->ElectionCount=0; + } + + /* Note that if we were running an election for this workgroup on this + subnet already, we just ignore the server we take precedence over. */ + } + else + { + /* We lost. Stop participating. */ + work->needelection = False; + + if (work->RunningElection || AM_LOCAL_MASTER_BROWSER(work)) + { + work->RunningElection = False; + DEBUG(3,("process_election: >>> Lost election for workgroup %s on subnet %s <<<\n", + work->work_group, subrec->subnet_name )); + if (AM_LOCAL_MASTER_BROWSER(work)) + unbecome_local_master_browser(subrec, work); + } + } +} + +/**************************************************************************** + This function looks over all the workgroups known on all the broadcast + subnets and decides if a browser election is to be run on that workgroup. + It returns True if any election packets need to be sent (this will then + be done by run_elections(). +***************************************************************************/ + +BOOL check_elections(void) +{ + struct subnet_record *subrec; + BOOL run_any_election = False; + + for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) + { + struct work_record *work; + for (work = subrec->workgrouplist; work; work = work->next) + { + run_any_election |= work->RunningElection; + + /* Only start an election if we are in the potential browser state. */ + if (work->needelection && !work->RunningElection && AM_POTENTIAL_MASTER_BROWSER(work)) + { + DEBUG(3,("check_elections: >>> Starting election for workgroup %s on subnet %s <<<\n", + work->work_group, subrec->subnet_name )); + + work->ElectionCount = 0; + work->RunningElection = True; + work->needelection = False; + } + } + } + return run_any_election; +} diff --git a/source3/nmbd/nmbd_incomingdgrams.c b/source3/nmbd/nmbd_incomingdgrams.c new file mode 100644 index 0000000000..24b4f33838 --- /dev/null +++ b/source3/nmbd/nmbd_incomingdgrams.c @@ -0,0 +1,625 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NBT netbios routines and daemon - version 2 + Copyright (C) Andrew Tridgell 1994-1997 + Copyright (C) Luke Kenneth Casson Leighton 1994-1997 + Copyright (C) Jeremy Allison 1994-1997 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "includes.h" + +extern int DEBUGLEVEL; + +extern pstring myname; +extern fstring myworkgroup; + +#if 0 + +/* XXXX note: This function is currently unsuitable for use, as it + does not properly check that a server is in a fit state to become + a backup browser before asking it to be one. + The code is left here to be worked on at a later date. +*/ + +/**************************************************************************** +Tell a server to become a backup browser +**************************************************************************/ + +void tell_become_backup(void) +{ + struct subnet_record *subrec; + for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) + { + struct work_record *work; + for (work = subrec->workgrouplist; work; work = work->next) + { + struct server_record *servrec; + int num_servers = 0; + int num_backups = 0; + + for (servrec = work->serverlist; servrec; servrec = servrec->next) + { + num_servers++; + + if (is_myname(servrec->serv.name)) + continue; + + if (servrec->serv.type & SV_TYPE_BACKUP_BROWSER) + { + num_backups++; + continue; + } + + if (servrec->serv.type & SV_TYPE_MASTER_BROWSER) + continue; + + if (!(servrec->serv.type & SV_TYPE_POTENTIAL_BROWSER)) + continue; + + DEBUG(3,("num servers: %d num backups: %d\n", + num_servers, num_backups)); + + /* make first server a backup server. thereafter make every + tenth server a backup server */ + if (num_backups != 0 && (num_servers+9) / num_backups > 10) + continue; + + DEBUG(2,("sending become backup to %s %s for %s\n", + servrec->serv.name, inet_ntoa(subrec->bcast_ip), + work->work_group)); + + /* type 11 request from MYNAME(20) to WG(1e) for SERVER */ + do_announce_request(servrec->serv.name, work->work_group, + ANN_BecomeBackup, 0x20, 0x1e, subrec->bcast_ip); + } + } + } +} +#endif + +/******************************************************************* + Process an incoming host announcement packet. +*******************************************************************/ + +void process_host_announce(struct subnet_record *subrec, struct packet_struct *p, char *buf) +{ + struct dgram_packet *dgram = &p->packet.dgram; + int ttl = IVAL(buf,1)/1000; + char *announce_name = buf+5; + uint32 servertype = IVAL(buf,23); + char *comment = buf+31; + struct work_record *work; + struct server_record *servrec; + char *work_name; + char *source_name = dgram->source_name.name; + + comment[43] = 0; + + DEBUG(3,("process_host_announce: from %s<%02x> IP %s to \ +%s for server %s.\n", source_name, source_name[15], inet_ntoa(p->ip), + namestr(&dgram->dest_name),announce_name)); + + DEBUG(5,("process_host_announce: ttl=%d server type=%08x comment=%s\n", + ttl, servertype,comment)); + + /* Filter servertype to remove impossible bits. */ + servertype &= ~(SV_TYPE_LOCAL_LIST_ONLY|SV_TYPE_DOMAIN_ENUM); + + /* A host announcement must be sent to the name WORKGROUP<1d>. */ + if(dgram->dest_name.name_type != 0x1d) + { + DEBUG(2,("process_host_announce: incorrect name type for destination from IP %s \ +(was %02x) should be 0x1d. Allowing packet anyway.\n", + inet_ntoa(p->ip), dgram->dest_name.name_type)); + /* Change it so it was. */ + dgram->dest_name.name_type = 0x1d; + } + + /* For a host announce the workgroup name is the destination name. */ + work_name = dgram->dest_name.name; + + /* + * We are being very agressive here in adding a workgroup + * name on the basis of a host announcing itself as being + * in that workgroup. Maybe we should wait for the workgroup + * announce instead ? JRA. + */ + + if ((work = find_workgroup_on_subnet(subrec, work_name))==NULL) + { + /* We have no record of this workgroup. Add it. */ + if((work = create_workgroup_on_subnet(subrec, work_name, ttl))==NULL) + return; + } + + if((servrec = find_server_in_workgroup( work, announce_name))==NULL) + { + /* If this server is not already in the workgroup, add it. */ + create_server_on_workgroup(work, announce_name, + servertype|SV_TYPE_LOCAL_LIST_ONLY, + ttl, comment); + } + else + { + /* Update the record. */ + servrec->serv.type = servertype|SV_TYPE_LOCAL_LIST_ONLY; + update_server_ttl( servrec, ttl); + StrnCpy(servrec->serv.comment,comment,sizeof(servrec->serv.comment)-1); + } + + subrec->work_changed = True; +} + +/******************************************************************* + Process an incoming WORKGROUP announcement packet. +*******************************************************************/ + +void process_workgroup_announce(struct subnet_record *subrec, struct packet_struct *p, char *buf) +{ + struct dgram_packet *dgram = &p->packet.dgram; + int ttl = IVAL(buf,1)/1000; + char *workgroup_announce_name = buf+5; + uint32 servertype = IVAL(buf,23); + char *master_name = buf+31; + struct work_record *work; + char *source_name = dgram->source_name.name; + + master_name[43] = 0; + + DEBUG(3,("process_workgroup_announce: from %s<%02x> IP %s to \ +%s for workgroup %s.\n", source_name, source_name[15], inet_ntoa(p->ip), + namestr(&dgram->dest_name),workgroup_announce_name)); + + DEBUG(5,("process_workgroup_announce: ttl=%d server type=%08x master browser=%s\n", + ttl, servertype, master_name)); + + /* Workgroup announcements must only go to the MSBROWSE name. */ + if (!strequal(dgram->dest_name.name, MSBROWSE) || (dgram->dest_name.name_type != 0x1)) + { + DEBUG(0,("process_workgroup_announce: from IP %s should be to __MSBROWSE__<0x01> not %s\n", + inet_ntoa(p->ip), namestr(&dgram->dest_name))); + return; + } + + if ((work = find_workgroup_on_subnet(subrec, workgroup_announce_name))==NULL) + { + /* We have no record of this workgroup. Add it. */ + if((work = create_workgroup_on_subnet(subrec, workgroup_announce_name, ttl))==NULL) + return; + } + else + { + /* Update the workgroup death_time. */ + update_workgroup_ttl(work, ttl); + } + + if(*work->local_master_browser_name == '\0') + { + /* Set the master browser name. */ + StrnCpy(work->local_master_browser_name, master_name, + sizeof(work->local_master_browser_name)-1); + + } + + subrec->work_changed = True; +} + +/******************************************************************* + Process an incoming local master browser announcement packet. +*******************************************************************/ + +void process_local_master_announce(struct subnet_record *subrec, struct packet_struct *p, char *buf) +{ + struct dgram_packet *dgram = &p->packet.dgram; + int ttl = IVAL(buf,1)/1000; + char *server_name = buf+5; + uint32 servertype = IVAL(buf,23); + char *comment = buf+31; + char *work_name; + struct work_record *work; + struct server_record *servrec; + char *source_name = dgram->source_name.name; + + comment[43] = 0; + + DEBUG(3,("process_local_master_announce: from %s<%02x> IP %s to \ +%s for server %s.\n", source_name, source_name[15], inet_ntoa(p->ip), + namestr(&dgram->dest_name),server_name)); + + DEBUG(5,("process_local_master_announce: ttl=%d server type=%08x comment=%s\n", + ttl, servertype, comment)); + + /* A local master announcement must be sent to the name WORKGROUP<1e>. */ + if(dgram->dest_name.name_type != 0x1e) + { + DEBUG(0,("process_local_master_announce: incorrect name type for destination from IP %s \ +(was %02x) should be 0x1e. Ignoring packet.\n", + inet_ntoa(p->ip), dgram->dest_name.name_type)); + return; + } + + /* Filter servertype to remove impossible bits. */ + servertype &= ~(SV_TYPE_LOCAL_LIST_ONLY|SV_TYPE_DOMAIN_ENUM); + + /* For a local master announce the workgroup name is the destination name. */ + work_name = dgram->dest_name.name; + + if ((work = find_workgroup_on_subnet(subrec, work_name))==NULL) + { + /* We have no record of this workgroup. Add it. */ + if((work = create_workgroup_on_subnet(subrec, work_name, ttl))==NULL) + return; + } + + /* If we think we're the local master browser for this workgroup, + we should never have got this packet. We don't see our own + packets. + */ + if(AM_LOCAL_MASTER_BROWSER(work)) + { + DEBUG(0,("process_local_master_announce: Server %s at IP %s is announcing itself as \ +a local master browser for workgroup %s and we think we are master. Forcing election.\n", + server_name, inet_ntoa(p->ip), work_name)); + + /* Samba nmbd versions 1.9.17 to 1.9.17p4 have a bug in that when + they have become a local master browser once, they will never + stop sending local master announcements. To fix this we send + them a reset browser packet, with level 0x2 on the __SAMBA__ + name that only they should be listening to. */ + + send_browser_reset( 0x2, "__SAMBA__" , 0x20, p->ip); + + /* We should demote ourself and force an election. */ + + unbecome_local_master_browser( subrec, work); + + /* The actual election requests are handled in + nmbd_election.c */ + + work->needelection = True; + return; + } + + /* Find the server record on this workgroup. If it doesn't exist, add it. */ + + if((servrec = find_server_in_workgroup( work, server_name))==NULL) + { + /* If this server is not already in the workgroup, add it. */ + create_server_on_workgroup(work, server_name, + servertype|SV_TYPE_LOCAL_LIST_ONLY, + ttl, comment); + } + else + { + /* Update the record. */ + servrec->serv.type = servertype|SV_TYPE_LOCAL_LIST_ONLY; + update_server_ttl(servrec, ttl); + StrnCpy(servrec->serv.comment,comment,sizeof(servrec->serv.comment)-1); + } + + /* Set the master browser name. */ + StrnCpy(work->local_master_browser_name, server_name, + sizeof(work->local_master_browser_name)-1); + + subrec->work_changed = True; +} + +/******************************************************************* + Process a domain master announcement frame. + Domain master browsers receive these from local masters. The Domain + master should then issue a sync with the local master, asking for + that machines local server list. +******************************************************************/ + +void process_master_browser_announce(struct subnet_record *subrec, + struct packet_struct *p,char *buf) +{ + char *local_master_name = buf; + struct work_record *work; + struct browse_cache_record *browrec; + + local_master_name[15] = 0; + + DEBUG(3,("process_master_browser_announce: Local master announce from %s IP %s.\n", + local_master_name, inet_ntoa(p->ip))); + + if (!lp_domain_master()) + { + DEBUG(0,("process_master_browser_announce: Not configured as domain \ +master - ignoring master announce.\n")); + return; + } + + if((work = find_workgroup_on_subnet(subrec, myworkgroup)) == NULL) + { + DEBUG(0,("process_master_browser_announce: Cannot find workgroup %s on subnet %s\n", + myworkgroup, subrec->subnet_name)); + return; + } + + if(!AM_DOMAIN_MASTER_BROWSER(work)) + { + DEBUG(0,("process_master_browser_announce: Local master announce made to us from \ +%s IP %s and we are not a domain master browser.\n", local_master_name, inet_ntoa(p->ip))); + return; + } + + /* Add this host as a local master browser entry on the browse lists. + This causes a sync request to be made to it at a later date. + */ + + if((browrec = find_browser_in_lmb_cache( local_master_name )) == NULL) + { + /* Add it. */ + create_browser_in_lmb_cache( work->work_group, local_master_name, p->ip); + } + else + update_browser_death_time(browrec); +} + +/**************************************************************************** + Send a backup list response. +*****************************************************************************/ + +static void send_backup_list_response(struct subnet_record *subrec, + struct work_record *work, + struct nmb_name *send_to_name, + unsigned char max_number_requested, + uint32 token, struct in_addr sendto_ip) +{ + char outbuf[1024]; + char *p, *countptr, *nameptr; + int count = 0; + int len; + struct server_record *servrec; + + bzero(outbuf,sizeof(outbuf)); + + DEBUG(3,("send_backup_list_response: sending backup list for workgroup %s to %s IP %s\n", + work->work_group, namestr(send_to_name), inet_ntoa(sendto_ip))); + + p = outbuf; + + SCVAL(p,0,ANN_GetBackupListResp); /* Backup list response opcode. */ + p++; + + countptr = p; + p++; + + SIVAL(p,0,token); /* The sender's unique info. */ + p += 4; + + nameptr = p; + + /* We always return at least one name - our own. */ + count = 1; + StrnCpy(p,myname,15); + strupper(p); + p = skip_string(p,1); + + /* Look for backup browsers in this workgroup. */ + for (servrec = work->serverlist; servrec; servrec = servrec->next) + { + len = PTR_DIFF(p, outbuf); + if((sizeof(outbuf) - len) < 16) + break; + + if(count >= max_number_requested) + break; + + if(strnequal(servrec->serv.name, myname,15)) + continue; + + if(!(servrec->serv.type & SV_TYPE_BACKUP_BROWSER)) + continue; + + StrnCpy(p, servrec->serv.name, 15); + strupper(p); + count++; + + DEBUG(5,("send_backup_list_response: Adding server %s number %d\n", + p, count)); + + p = skip_string(p,1); + } + + SCVAL(countptr, 0, count); + + len = PTR_DIFF(p, outbuf); + + DEBUG(4,("send_backup_list_response: sending response to %s<00> IP %s with %d servers.\n", + send_to_name->name, inet_ntoa(sendto_ip), count)); + + send_mailslot(True, BROWSE_MAILSLOT, + outbuf,PTR_DIFF(p,outbuf), + myname, 0, + send_to_name->name,0, + sendto_ip, subrec->myip); +} + +/******************************************************************* + Process a send backup list request packet. + + A client sends a backup list request to ask for a list of servers on + the net that maintain server lists for a domain. A server is then + chosen from this list to send NetServerEnum commands to to list + available servers. + +********************************************************************/ + +void process_get_backup_list_request(struct subnet_record *subrec, + struct packet_struct *p,char *buf) +{ + struct dgram_packet *dgram = &p->packet.dgram; + struct work_record *work; + unsigned char max_number_requested = CVAL(buf,0); + uint32 token = IVAL(buf,1); /* Sender's key index for the workgroup. */ + int name_type = dgram->dest_name.name_type; + char *workgroup_name = dgram->dest_name.name; + + DEBUG(3,("process_get_backup_list_request: request from %s IP %s to %s.\n", + namestr(&dgram->source_name), inet_ntoa(p->ip), + namestr(&dgram->dest_name))); + + /* We have to be a master browser, or a domain master browser + for the requested workgroup. That means it must be our + workgroup. */ + + if(strequal(workgroup_name, myworkgroup) == False) + { + DEBUG(7,("process_get_backup_list_request: Ignoring announce request for workgroup %s.\n", + workgroup_name)); + return; + } + + if((work = find_workgroup_on_subnet(subrec, workgroup_name)) == NULL) + { + DEBUG(0,("process_get_backup_list_request: Cannot find workgroup %s on \ +subnet %s.\n", workgroup_name, subrec->subnet_name)); + return; + } + + if(name_type == 0x1b) + { + /* We must be a domain master browser in order to + process this packet. */ + + if(!AM_DOMAIN_MASTER_BROWSER(work)) + { + DEBUG(0,("process_get_backup_list_request: domain list requested for workgroup %s \ +and I am not a domain master browser.\n", workgroup_name)); + return; + } + } + else if (name_type == 0x1d) + { + /* We must be a local master browser in order to + process this packet. */ + + if(!AM_LOCAL_MASTER_BROWSER(work)) + { + DEBUG(0,("process_get_backup_list_request: domain list requested for workgroup %s \ +and I am not a local master browser.\n", workgroup_name)); + return; + } + } + else + { + DEBUG(0,("process_get_backup_list_request: Invalid name type %x - should be 0x1b or 0x1d.\n", + name_type)); + return; + } + + send_backup_list_response(subrec, work, &dgram->source_name, + max_number_requested, token, p->ip); +} + +/******************************************************************* + Process a reset browser state packet. + + Diagnostic packet: + 0x1 - Stop being a master browser and become a backup browser. + 0x2 - Discard browse lists, stop being a master browser, try again. + 0x4 - Stop being a master browser forever. + +******************************************************************/ + +void process_reset_browser(struct subnet_record *subrec, + struct packet_struct *p,char *buf) +{ + struct dgram_packet *dgram = &p->packet.dgram; + int state = CVAL(buf,0); + struct subnet_record *sr; + + DEBUG(1,("process_reset_browser: received diagnostic browser reset \ +request from %s IP %s state=0x%X\n", + namestr(&dgram->source_name), inet_ntoa(p->ip), state)); + + /* Stop being a local master browser on all our broadcast subnets. */ + if (state & 0x1) + { + for (sr = FIRST_SUBNET; sr; sr = NEXT_SUBNET_EXCLUDING_UNICAST(sr)) + { + struct work_record *work; + for (work = sr->workgrouplist; work; work = work->next) + { + if (AM_LOCAL_MASTER_BROWSER(work)) + { + unbecome_local_master_browser(sr, work); + work->needelection = True; + } + } + } + } + + /* Discard our browse lists. */ + if (state & 0x2) + { + /* + * Calling expire_workgroups_and_servers with a -1 + * time causes all servers not marked with a PERMANENT_TTL + * on the workgroup lists to be discarded, and all + * workgroups with empty server lists to be discarded. + * This means we keep our own server names and workgroup + * as these have a PERMANENT_TTL. + */ + + expire_workgroups_and_servers(-1); + } + + /* Request to stop browsing altogether. */ + if (state & 0x4) + DEBUG(1,("process_reset_browser: ignoring request to stop being a browser.\n")); +} + +/******************************************************************* + Process a announcement request packet. + We don't respond immediately, we just check it's a request for + out workgroup and then set the flag telling the announce code + in nmbd_sendannounce.c:announce_my_server_names that an + announcement is needed soon. + ******************************************************************/ + +void process_announce_request(struct subnet_record *subrec, struct packet_struct *p, char *buf) +{ + struct dgram_packet *dgram = &p->packet.dgram; + struct work_record *work; + char *workgroup_name = dgram->dest_name.name; + + DEBUG(3,("process_announce_request: Announce request from %s IP %s to %s.\n", + namestr(&dgram->source_name), inet_ntoa(p->ip), + namestr(&dgram->dest_name))); + + /* We only send announcement requests on our workgroup. */ + if(strequal(workgroup_name, myworkgroup) == False) + { + DEBUG(7,("process_announce_request: Ignoring announce request for workgroup %s.\n", + workgroup_name)); + return; + } + + if((work = find_workgroup_on_subnet(subrec, workgroup_name)) == NULL) + { + DEBUG(0,("process_announce_request: Unable to find workgroup %s on subnet !\n", + workgroup_name)); + return; + } + + work->needannounce = True; +} diff --git a/source3/nmbd/nmbd_incomingrequests.c b/source3/nmbd/nmbd_incomingrequests.c new file mode 100644 index 0000000000..ff4bb07a45 --- /dev/null +++ b/source3/nmbd/nmbd_incomingrequests.c @@ -0,0 +1,556 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NBT netbios routines and daemon - version 2 + Copyright (C) Andrew Tridgell 1994-1997 + Copyright (C) Luke Kenneth Casson Leighton 1994-1997 + Copyright (C) Jeremy Allison 1994-1997 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + This file contains all the code to process NetBIOS requests coming + in on port 137. It does not deal with the code needed to service + WINS server requests, but only broadcast and unicast requests. + +*/ + +#include "includes.h" + +extern int DEBUGLEVEL; + +/**************************************************************************** +Send a name release response. +**************************************************************************/ + +static void send_name_release_response(int rcode, struct packet_struct *p) +{ + struct nmb_packet *nmb = &p->packet.nmb; + char rdata[6]; + + memcpy(&rdata[0], &nmb->additional->rdata[0], 6); + + reply_netbios_packet(p, /* Packet to reply to. */ + rcode, /* Result code. */ + NMB_REL, /* nmbd type code. */ + NMB_NAME_RELEASE_OPCODE, /* opcode. */ + 0, /* ttl. */ + rdata, /* data to send. */ + 6); /* data length. */ +} + +/**************************************************************************** +Process a name release packet on a broadcast subnet. +Ignore it if it's not one of our names. +****************************************************************************/ + +void process_name_release_request(struct subnet_record *subrec, + struct packet_struct *p) +{ + struct nmb_packet *nmb = &p->packet.nmb; + struct in_addr owner_ip; + struct nmb_name *question = &nmb->question.question_name; + BOOL bcast = nmb->header.nm_flags.bcast; + uint16 nb_flags = get_nb_flags(nmb->additional->rdata); + BOOL group = (nb_flags & NB_GROUP) ? True : False; + struct name_record *namerec; + int rcode = 0; + + putip((char *)&owner_ip,&nmb->additional->rdata[2]); + + if(!bcast) + { + /* We should only get broadcast name release packets here. + Anyone trying to release unicast should be going to a WINS + server. If the code gets here, then either we are not a wins + server and they sent it anyway, or we are a WINS server and + the request was malformed. Either way, log an error here. + and send an error reply back. + */ + DEBUG(0,("process_name_release_request: unicast name release request \ +received for name %s from IP %s on subnet %s. Error - should be sent to WINS server\n", + namestr(question), inet_ntoa(owner_ip), subrec->subnet_name)); + + send_name_release_response(FMT_ERR, p); + return; + } + + DEBUG(3,("process_name_release_request: Name release on name %s, \ +subnet %s from owner IP %s\n", + namestr(&nmb->question.question_name), + subrec->subnet_name, inet_ntoa(owner_ip))); + + /* If someone is releasing a broadcast group name, just ignore it. */ + if( group && !ismyip(owner_ip) ) + return; + + namerec = find_name_on_subnet(subrec, &nmb->question.question_name, FIND_ANY_NAME); + + /* We only care about someone trying to release one of our names. */ + if (namerec && ((namerec->source == SELF_NAME) || (namerec->source == PERMANENT_NAME))) + { + rcode = ACT_ERR; + DEBUG(0, ("process_name_release_request: Attempt to release name %s from IP %s \ +on subnet %s being rejected as it is one of our names.\n", + namestr(&nmb->question.question_name), inet_ntoa(owner_ip), subrec->subnet_name)); + } + + if(rcode == 0) + return; + + /* Send a NAME RELEASE RESPONSE (pos/neg) see rfc1002.txt 4.2.10-11 */ + send_name_release_response(rcode, p); +} + +/**************************************************************************** +Send a name registration response. +**************************************************************************/ + +static void send_name_registration_response(int rcode, int ttl, struct packet_struct *p) +{ + struct nmb_packet *nmb = &p->packet.nmb; + char rdata[6]; + + memcpy(&rdata[0], &nmb->additional->rdata[0], 6); + + reply_netbios_packet(p, /* Packet to reply to. */ + rcode, /* Result code. */ + NMB_REG, /* nmbd type code. */ + NMB_NAME_REG_OPCODE, /* opcode. */ + ttl, /* ttl. */ + rdata, /* data to send. */ + 6); /* data length. */ +} + +/**************************************************************************** +Process a name refresh request on a broadcast subnet. +**************************************************************************/ + +void process_name_refresh_request(struct subnet_record *subrec, + struct packet_struct *p) +{ + + struct nmb_packet *nmb = &p->packet.nmb; + struct nmb_name *question = &nmb->question.question_name; + BOOL bcast = nmb->header.nm_flags.bcast; + struct in_addr from_ip; + + putip((char *)&from_ip,&nmb->additional->rdata[2]); + + if(!bcast) + { + /* We should only get broadcast name refresh packets here. + Anyone trying to refresh unicast should be going to a WINS + server. If the code gets here, then either we are not a wins + server and they sent it anyway, or we are a WINS server and + the request was malformed. Either way, log an error here. + and send an error reply back. + */ + DEBUG(0,("process_name_refresh_request: unicast name registration request \ +received for name %s from IP %s on subnet %s. Error - should be sent to WINS server\n", + namestr(question), inet_ntoa(from_ip), subrec->subnet_name)); + + send_name_registration_response(FMT_ERR, 0, p); + return; + } + + /* Just log a message. We really don't care about broadcast name + refreshes. */ + + DEBUG(3,("process_name_refresh_request: Name refresh for name %s \ +IP %s on subnet %s\n", namestr(question), inet_ntoa(from_ip), subrec->subnet_name)); + +} + +/**************************************************************************** +Process a name registration request on a broadcast subnet. +**************************************************************************/ + +void process_name_registration_request(struct subnet_record *subrec, + struct packet_struct *p) +{ + struct nmb_packet *nmb = &p->packet.nmb; + struct nmb_name *question = &nmb->question.question_name; + BOOL bcast = nmb->header.nm_flags.bcast; + uint16 nb_flags = get_nb_flags(nmb->additional->rdata); + BOOL group = (nb_flags & NB_GROUP) ? True : False; + struct name_record *namerec = NULL; + int ttl = nmb->additional->ttl; + struct in_addr from_ip; + + putip((char *)&from_ip,&nmb->additional->rdata[2]); + + if(!bcast) + { + /* We should only get broadcast name registration packets here. + Anyone trying to register unicast should be going to a WINS + server. If the code gets here, then either we are not a wins + server and they sent it anyway, or we are a WINS server and + the request was malformed. Either way, log an error here. + and send an error reply back. + */ + DEBUG(0,("process_name_registration_request: unicast name registration request \ +received for name %s from IP %s on subnet %s. Error - should be sent to WINS server\n", + namestr(question), inet_ntoa(from_ip), subrec->subnet_name)); + + send_name_registration_response(FMT_ERR, 0, p); + return; + } + + DEBUG(3,("process_name_registration_request: Name registration for name %s \ +IP %s on subnet %s\n", namestr(question), inet_ntoa(from_ip), subrec->subnet_name)); + + /* See if the name already exists. */ + namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME); + + if (!group) + { + /* Unique name. */ + + if ((namerec != NULL) && + ((namerec->source == SELF_NAME) || (namerec->source == PERMANENT_NAME) || + NAME_GROUP(namerec)) + ) + { + /* No-one can register one of Samba's names, nor can they + register a name that's a group name as a unique name */ + + send_name_registration_response(ACT_ERR, 0, p); + return; + } + else if(namerec != NULL) + { + /* Update the namelist record with the new information. */ + namerec->ip[0] = from_ip; + update_name_ttl(namerec, ttl); + + DEBUG(3,("process_name_registration_request: Updated name record %s \ +with IP %s on subnet %s\n",namestr(&namerec->name),inet_ntoa(from_ip), subrec->subnet_name)); + return; + } + } + else + { + /* Group name. */ + + if((namerec != NULL) && !NAME_GROUP(namerec) && + ((namerec->source == SELF_NAME) || (namerec->source == PERMANENT_NAME)) + ) + { + /* Disallow group names when we have a unique name. */ + send_name_registration_response(ACT_ERR, 0, p); + return; + } + } +} + +/**************************************************************************** +This is used to sort names for a name status into a sensible order. +We put our own names first, then in alphabetical order. +**************************************************************************/ + +static int status_compare(char *n1,char *n2) +{ + extern pstring myname; + int l1,l2,l3; + + /* It's a bit tricky because the names are space padded */ + for (l1=0;l1<15 && n1[l1] && n1[l1] != ' ';l1++) ; + for (l2=0;l2<15 && n2[l2] && n2[l2] != ' ';l2++) ; + l3 = strlen(myname); + + if ((l1==l3) && strncmp(n1,myname,l3) == 0 && + (l2!=l3 || strncmp(n2,myname,l3) != 0)) + return -1; + + if ((l2==l3) && strncmp(n2,myname,l3) == 0 && + (l1!=l3 || strncmp(n1,myname,l3) != 0)) + return 1; + + return memcmp(n1,n2,18); +} + + +/**************************************************************************** + Process a node status query + ****************************************************************************/ + +void process_node_status_request(struct subnet_record *subrec, struct packet_struct *p) +{ + struct nmb_packet *nmb = &p->packet.nmb; + char *qname = nmb->question.question_name.name; + int ques_type = nmb->question.question_name.name_type; + char rdata[MAX_DGRAM_SIZE]; + char *countptr, *buf, *bufend, *buf0; + int names_added,i; + struct name_record *namerec; + + DEBUG(3,("process_node_status_request: status request for name %s from IP %s on \ +subnet %s.\n", namestr(&nmb->question.question_name), inet_ntoa(p->ip), + subrec->subnet_name)); + + if((namerec = find_name_on_subnet(subrec, &nmb->question.question_name, + FIND_SELF_NAME)) == 0) + { + DEBUG(1,("process_node_status_request: status request for name %s from IP %s on \ +subnet %s - name not found.\n", namestr(&nmb->question.question_name), + inet_ntoa(p->ip), subrec->subnet_name)); + + return; + } + + /* XXXX hack, we should calculate exactly how many will fit. */ + bufend = &rdata[MAX_DGRAM_SIZE] - 18; + countptr = buf = rdata; + buf += 1; + buf0 = buf; + + names_added = 0; + + namerec = subrec->namelist; + + while (buf < bufend) + { + if ((namerec->source == SELF_NAME) || (namerec->source == PERMANENT_NAME)) + { + int name_type = namerec->name.name_type; + + if (!strequal(namerec->name.name,"*") && + !strequal(namerec->name.name,"__SAMBA__") && + (name_type < 0x1b || name_type >= 0x20 || + ques_type < 0x1b || ques_type >= 0x20 || + strequal(qname, namerec->name.name))) + { + /* Start with the name. */ + bzero(buf,18); + sprintf(buf,"%-15.15s",namerec->name.name); + strupper(buf); + + /* Put the name type and netbios flags in the buffer. */ + buf[15] = name_type; + set_nb_flags(&buf[16],namerec->nb_flags); + buf[16] |= NB_ACTIVE; /* all our names are active */ + + buf += 18; + + names_added++; + } + } + + /* Remove duplicate names. */ + qsort(buf0,names_added,18,QSORT_CAST status_compare); + + for (i=1;inext; + + if (!namerec) + { + /* End of the subnet specific name list. Now + add the names on the unicast subnet . */ + struct subnet_record *uni_subrec = unicast_subnet; + + if (uni_subrec != subrec) + { + subrec = uni_subrec; + namerec = subrec->namelist; + } + } + if (!namerec) + break; + + } + + SCVAL(countptr,0,names_added); + + /* We don't send any stats as they could be used to attack + the protocol. */ + bzero(buf,64); + + buf += 46; + + /* Send a NODE STATUS RESPONSE */ + reply_netbios_packet(p, /* Packet to reply to. */ + 0, /* Result code. */ + NMB_STATUS, /* nmbd type code. */ + NMB_NAME_QUERY_OPCODE, /* opcode. */ + 0, /* ttl. */ + rdata, /* data to send. */ + PTR_DIFF(buf,rdata)); /* data length. */ +} + + +/*************************************************************************** +Process a name query. + +For broadcast name queries: + + - Only reply if the query is for one of YOUR names. + - NEVER send a negative response to a broadcast query. + +****************************************************************************/ + +void process_name_query_request(struct subnet_record *subrec, struct packet_struct *p) +{ + struct nmb_packet *nmb = &p->packet.nmb; + struct nmb_name *question = &nmb->question.question_name; + int name_type = question->name_type; + BOOL bcast = nmb->header.nm_flags.bcast; + int ttl=0; + int rcode = 0; + char *prdata = NULL; + char rdata[6]; + BOOL success = False; + struct name_record *namerec = NULL; + int reply_data_len = 0; + int i; + + DEBUG(3,("process_name_query_request: Name query from %s on subnet %s for name %s\n", + inet_ntoa(p->ip), subrec->subnet_name, namestr(question))); + + /* Look up the name in the cache - if the request is a broadcast request that + came from a subnet we don't know about then search all the broadcast subnets + for a match (as we don't know what interface the request came in on). */ + + if(subrec == remote_broadcast_subnet) + namerec = find_name_for_remote_broadcast_subnet( question, FIND_ANY_NAME); + else + namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME); + + + /* Check if it is a name that expired */ + if (namerec && ((namerec->death_time != PERMANENT_TTL) && (namerec->death_time < p->timestamp))) + { + DEBUG(5,("process_name_query_request: expired name %s\n", namestr(&namerec->name))); + namerec = NULL; + } + + if (namerec) + { + + /* + * Always respond to unicast queries. + * Don't respond to broadcast queries unless the query is for + * a name we own, a Primary Domain Controller name, or a WINS_PROXY + * name with type 0 or 0x20. WINS_PROXY names are only ever added + * into the namelist if we were configured as a WINS proxy. + */ + + if (!bcast || + (bcast && ((name_type == 0x1b) || (namerec->source == SELF_NAME) || + (namerec->source == PERMANENT_NAME) || + ((namerec->source == WINS_PROXY_NAME) && ((name_type == 0) || (name_type == 0x20))))) + ) + { + + /* The requested name is a directed query, or it's SELF or PERMANENT or WINS_PROXY, + or it's a Domain Master type. */ + + ttl = (namerec->death_time != PERMANENT_TTL) ? + namerec->death_time - p->timestamp : lp_max_ttl(); + + /* Copy all known ip addresses into the return data. */ + /* Optimise for the common case of one IP address so + we don't need a malloc. */ + + if(namerec->num_ips == 1 ) + prdata = rdata; + else + { + if((prdata = (char *)malloc( namerec->num_ips * 6 )) == NULL) + { + DEBUG(0,("process_name_query_request: malloc fail !\n")); + return; + } + } + + for( i = 0; i < namerec->num_ips; i++) + { + set_nb_flags(&prdata[i*6],namerec->nb_flags); + putip((char *)&prdata[2+(i*6)], &namerec->ip[i]); + } + reply_data_len = namerec->num_ips * 6; + success = True; + } + } + + /* + * If a machine is broadcasting a name lookup request and we have lp_wins_proxy() + * set we should initiate a WINS query here. On success we add the resolved name + * into our namelist with a type of WINS_PROXY_NAME and then reply to the query. + */ + + if(!success && (namerec == NULL) && we_are_a_wins_client() && lp_wins_proxy() && + bcast && (subrec != remote_broadcast_subnet)) + { + make_wins_proxy_name_query_request( subrec, p, question ); + return; + } + + if (!success && bcast) + { + if((prdata != rdata) && (prdata != NULL)) + free(rdata); + return; /* Never reply with a negative response to broadcasts. */ + } + + /* + * Final check. From observation, if a unicast packet is sent + * to a non-WINS server with the recursion desired bit set + * then never send a negative response. + */ + + if(!success && !bcast && nmb->header.nm_flags.recursion_desired) + { + if((prdata != rdata) && (prdata != NULL)) + free(rdata); + return; + } + + if (success) + { + rcode = 0; + DEBUG(3,("OK\n")); + } + else + { + rcode = NAM_ERR; + DEBUG(3,("UNKNOWN\n")); + } + + /* See rfc1002.txt 4.2.13. */ + + reply_netbios_packet(p, /* Packet to reply to. */ + rcode, /* Result code. */ + NMB_QUERY, /* nmbd type code. */ + NMB_NAME_QUERY_OPCODE, /* opcode. */ + ttl, /* ttl. */ + prdata, /* data to send. */ + reply_data_len); /* data length. */ + + if((prdata != rdata) && (prdata != NULL)) + free(prdata); +} diff --git a/source3/nmbd/nmbd_lmhosts.c b/source3/nmbd/nmbd_lmhosts.c new file mode 100644 index 0000000000..2dd1db81cd --- /dev/null +++ b/source3/nmbd/nmbd_lmhosts.c @@ -0,0 +1,168 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NBT netbios routines and daemon - version 2 + Copyright (C) Jeremy Allison 1994-1997 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + Revision History: + + Handle lmhosts file reading. + +*/ + +#include "includes.h" + +extern int DEBUGLEVEL; + +/**************************************************************************** +Load a lmhosts file. +****************************************************************************/ +void load_lmhosts_file(char *fname) +{ + FILE *fp = fopen(fname,"r"); + pstring line; + if (!fp) { + DEBUG(2,("load_lmhosts_file: Can't open lmhosts file %s. Error was %s\n", + fname, strerror(errno))); + return; + } + + while (!feof(fp)) + { + pstring ip,name,flags,extra; + struct subnet_record *subrec = NULL; + char *ptr; + int count = 0; + struct in_addr ipaddr; + enum name_source source = LMHOSTS_NAME; + int name_type = -1; + + if (!fgets_slash(line,sizeof(pstring),fp)) + continue; + + if (*line == '#') + continue; + + strcpy(ip,""); + strcpy(name,""); + strcpy(flags,""); + + ptr = line; + + if (next_token(&ptr,ip ,NULL)) + ++count; + if (next_token(&ptr,name ,NULL)) + ++count; + if (next_token(&ptr,flags,NULL)) + ++count; + if (next_token(&ptr,extra,NULL)) + ++count; + + if (count <= 0) + continue; + + if (count > 0 && count < 2) + { + DEBUG(0,("load_lmhosts_file: Ill formed hosts line [%s]\n",line)); + continue; + } + + if (count >= 4) + { + DEBUG(0,("load_lmhosts_file: too many columns in lmhosts file %s (obsolete syntax)\n", + fname)); + continue; + } + + DEBUG(4, ("load_lmhosts_file: lmhost entry: %s %s %s\n", ip, name, flags)); + + if (strchr(flags,'G') || strchr(flags,'S')) + { + DEBUG(0,("load_lmhosts_file: group flag in %s ignored (obsolete)\n",fname)); + continue; + } + + ipaddr = *interpret_addr2(ip); + + /* Extra feature. If the name ends in '#XX', where XX is a hex number, + then only add that name type. */ + if((ptr = strchr(name, '#')) != NULL) + { + char *endptr; + + ptr++; + name_type = (int)strtol(ptr, &endptr,0); + + if(!*ptr || (endptr == ptr)) + { + DEBUG(0,("load_lmhosts_file: invalid name %s containing '#'.\n", name)); + continue; + } + + *(--ptr) = '\0'; /* Truncate at the '#' */ + } + + /* We find a relevent subnet to put this entry on, then add it. */ + /* Go through all the broadcast subnets and see if the mask matches. */ + for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) + { + if(same_net(ipaddr, subrec->bcast_ip, subrec->mask_ip)) + break; + } + + /* If none match add the name to the remote_broadcast_subnet. */ + if(subrec == NULL) + subrec = remote_broadcast_subnet; + + if(name_type == -1) + { + /* Add the (0) and (0x20) names directly into the namelist for this subnet. */ + add_name_to_subnet(subrec,name,0x00,(uint16)NB_ACTIVE,PERMANENT_TTL,source,1,&ipaddr); + add_name_to_subnet(subrec,name,0x20,(uint16)NB_ACTIVE,PERMANENT_TTL,source,1,&ipaddr); + } + else + { + /* Add the given name type to the subnet namelist. */ + add_name_to_subnet(subrec,name,name_type,(uint16)NB_ACTIVE,PERMANENT_TTL,source,1,&ipaddr); + } + } + + fclose(fp); +} + +/**************************************************************************** + Find a name read from the lmhosts file. We secretly check the names on + the remote_broadcast_subnet as if the name was added to a regular broadcast + subnet it will be found by normal name query processing. +****************************************************************************/ + +BOOL find_name_in_lmhosts(struct nmb_name *nmbname, struct name_record **namerecp) +{ + struct name_record *namerec; + + *namerecp = NULL; + + if((namerec = find_name_on_subnet(remote_broadcast_subnet, nmbname, + FIND_ANY_NAME))==NULL) + return False; + + if(!NAME_IS_ACTIVE(namerec) || (namerec->source != LMHOSTS_NAME)) + return False; + + *namerecp = namerec; + return True; +} diff --git a/source3/nmbd/nmbd_logonnames.c b/source3/nmbd/nmbd_logonnames.c new file mode 100644 index 0000000000..b2431ec0a7 --- /dev/null +++ b/source3/nmbd/nmbd_logonnames.c @@ -0,0 +1,166 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NBT netbios routines and daemon - version 2 + Copyright (C) Andrew Tridgell 1994-1997 + Copyright (C) Luke Kenneth Casson Leighton 1994-1997 + Copyright (C) Jeremy Allison 1994-1997 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "includes.h" + +extern int DEBUGLEVEL; + +extern pstring scope; +extern pstring myname; +extern fstring myworkgroup; +extern char **my_netbios_names; +extern struct in_addr ipzero; +extern struct in_addr allones_ip; + +extern uint16 samba_nb_type; /* Samba's NetBIOS type. */ + +/**************************************************************************** + Fail to become a Logon server on a subnet. + ****************************************************************************/ +static void become_logon_server_fail(struct subnet_record *subrec, + struct response_record *rrec, + struct nmb_name *fail_name) +{ + struct work_record *work = find_workgroup_on_subnet(subrec, fail_name->name); + struct server_record *servrec; + + if(!work) + { + DEBUG(0,("become_logon_server_fail: Error - cannot find \ +workgroup %s on subnet %s\n", fail_name->name, subrec->subnet_name)); + return; + } + + if((servrec = find_server_in_workgroup( work, myname)) == NULL) + { + DEBUG(0,("become_logon_server_fail: Error - cannot find server %s \ +in workgroup %s on subnet %s\n", + myname, fail_name->name, subrec->subnet_name)); + work->log_state = LOGON_NONE; + return; + } + + /* Set the state back to LOGON_NONE. */ + work->log_state = LOGON_NONE; + + servrec->serv.type &= ~SV_TYPE_DOMAIN_CTRL; + + DEBUG(0,("become_logon_server_fail: Failed to become a domain master for \ +workgroup %s on subnet %s. Couldn't register name %s.\n", + work->work_group, subrec->subnet_name, namestr(fail_name))); + +} + +/**************************************************************************** + Become a Logon server on a subnet. + ****************************************************************************/ + +static void become_logon_server_success(struct subnet_record *subrec, + struct userdata_struct *userdata, + struct nmb_name *registered_name, + uint16 nb_flags, + int ttl, struct in_addr registered_ip) +{ + struct work_record *work = find_workgroup_on_subnet( subrec, registered_name->name); + struct server_record *servrec; + + if(!work) + { + DEBUG(0,("become_logon_server_success: Error - cannot find \ +workgroup %s on subnet %s\n", registered_name->name, subrec->subnet_name)); + return; + } + + if((servrec = find_server_in_workgroup( work, myname)) == NULL) + { + DEBUG(0,("become_logon_server_success: Error - cannot find server %s \ +in workgroup %s on subnet %s\n", + myname, registered_name->name, subrec->subnet_name)); + work->log_state = LOGON_NONE; + return; + } + + /* Set the state in the workgroup structure. */ + work->log_state = LOGON_SRV; /* Become domain master. */ + + /* Update our server status. */ + servrec->serv.type |= (SV_TYPE_NT|SV_TYPE_DOMAIN_MEMBER); + /* To allow Win95 policies to load we need to set type domain + controller. + */ + servrec->serv.type |= SV_TYPE_DOMAIN_CTRL; + + /* Tell the namelist writer to write out a change. */ + subrec->work_changed = True; + + DEBUG(0,("become_logon_server_success: Samba is now a logon server\ +for workgroup %s on subnet %s\n", work->work_group, subrec->subnet_name)); +} + +/******************************************************************* + Become a logon server by attempting to register the WORKGROUP<1c> + group name. +******************************************************************/ + +static void become_logon_server(struct subnet_record *subrec, + struct work_record *work) +{ + DEBUG(2,("become_logon_server: Atempting to become logon server for workgroup %s \ +on subnet %s\n", work->work_group,subrec->subnet_name)); + + DEBUG(3,("become_logon_server: go to first stage: register %s<1c> name\n", + work->work_group)); + work->log_state = LOGON_WAIT; + + register_name(subrec, work->work_group,0x1c,samba_nb_type|NB_GROUP, + become_logon_server_success, + become_logon_server_fail, NULL); +} + +/***************************************************************************** + Add the internet group <1c> logon names by unicast and broadcast. + ****************************************************************************/ +void add_logon_names(void) +{ + struct subnet_record *subrec; + + for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec)) + { + struct work_record *work = find_workgroup_on_subnet(subrec, myworkgroup); + + if (work && (work->log_state == LOGON_NONE)) + { + struct nmb_name nmbname; + make_nmb_name(&nmbname,myworkgroup,0x1c,scope); + + if (find_name_on_subnet(subrec, &nmbname, FIND_SELF_NAME) == NULL) + { + DEBUG(0,("add_domain_logon_names: At time %s attempting to become \ +logon server for workgroup %s on subnet %s\n", timestring(), myworkgroup, + subrec->subnet_name)); + become_logon_server(subrec, work); + } + } + } +} diff --git a/source3/nmbd/nmbd_mynames.c b/source3/nmbd/nmbd_mynames.c new file mode 100644 index 0000000000..660b545069 --- /dev/null +++ b/source3/nmbd/nmbd_mynames.c @@ -0,0 +1,165 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NBT netbios routines and daemon - version 2 + Copyright (C) Andrew Tridgell 1994-1997 + Copyright (C) Luke Kenneth Casson Leighton 1994-1997 + Copyright (C) Jeremy Allison 1994-1997 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "includes.h" + +extern int DEBUGLEVEL; + +extern char **my_netbios_names; +extern pstring myname; +extern fstring myworkgroup; + +extern uint16 samba_nb_type; /* Samba's NetBIOS type. */ + +/**************************************************************************** + Fail funtion when registering my netbios names. + **************************************************************************/ + +static void my_name_register_failed(struct subnet_record *subrec, + struct response_record *rrec, struct nmb_name *nmbname) +{ + DEBUG(0,("my_name_register_failed: Failed to register my name %s on subnet %s.\n", + namestr(nmbname), subrec->subnet_name)); +} + +/**************************************************************************** + Add my workgroup and my given names to the subnet lists. + Also add the magic Samba names. + **************************************************************************/ + +BOOL register_my_workgroup_and_names() +{ + struct subnet_record *subrec; + struct work_record *work; + int i; + + for(subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec)) + { + /* Create the workgroup on the subnet. */ + if((work = create_workgroup_on_subnet(subrec, myworkgroup, PERMANENT_TTL)) == NULL) + { + DEBUG(0,("register_my_workgroup_and_names: Failed to create my workgroup %s on subnet %s. \ +Exiting.\n", myworkgroup, subrec->subnet_name)); + return False; + } + + /* Each subnet entry, except for the remote_announce_broadcast subnet + and the wins_server_subnet has the magic Samba names. */ + add_samba_names_to_subnet(subrec); + + /* Register all our names including aliases. */ + for (i=0; my_netbios_names[i]; i++) + { + register_name(subrec, my_netbios_names[i],0x20,samba_nb_type, + NULL, + my_name_register_failed, NULL); + register_name(subrec, my_netbios_names[i],0x03,samba_nb_type, + NULL, + my_name_register_failed, NULL); + register_name(subrec, my_netbios_names[i],0x00,samba_nb_type, + NULL, + my_name_register_failed, NULL); + } + + /* Initiate election processing, register the workgroup names etc. */ + initiate_myworkgroup_startup(subrec, work); + } + + /* If we are not a WINS client, we still need to add the magic Samba + names and the netbios names to the unicast subnet directly. This is + to allow unicast node status requests and queries to still work + in a broadcast only environment. */ + + if(we_are_a_wins_client() == False) + { + add_samba_names_to_subnet(unicast_subnet); + + for (i=0; my_netbios_names[i]; i++) + { + add_name_to_subnet(unicast_subnet, my_netbios_names[i],0x20,samba_nb_type, PERMANENT_TTL, + SELF_NAME, 1, &FIRST_SUBNET->myip); + + add_name_to_subnet(unicast_subnet, my_netbios_names[i],0x3,samba_nb_type, PERMANENT_TTL, + SELF_NAME, 1, &FIRST_SUBNET->myip); + + add_name_to_subnet(unicast_subnet, my_netbios_names[i],0x0,samba_nb_type, PERMANENT_TTL, + SELF_NAME, 1, &FIRST_SUBNET->myip); + } + } + + return True; +} + +/**************************************************************************** + Remove all the names we registered. +**************************************************************************/ + +void release_my_names() +{ + struct subnet_record *subrec; + + for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec)) + { + struct name_record *namerec, *nextnamerec; + + for (namerec = subrec->namelist; namerec; namerec = nextnamerec) + { + nextnamerec = namerec->next; + if ((namerec->source == SELF_NAME) && !NAME_IS_DEREGISTERING(namerec)) + release_name(subrec, namerec, standard_success_release, + NULL, NULL); + } + } +} + +/******************************************************************* + Refresh our registered names. + ******************************************************************/ + +void refresh_my_names(time_t t) +{ + struct subnet_record *subrec; + + for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec)) + { + struct name_record *namerec; + + for (namerec = subrec->namelist; namerec; namerec = namerec->next) + { + /* Each SELF name has an individual time to be refreshed. */ + if ((namerec->source == SELF_NAME) && (namerec->refresh_time < t) && + (namerec->death_time != PERMANENT_TTL)) + { + /* We cheat here and pretend the refresh is going to be + successful & update the refresh times. This stops + multiple refresh calls being done. We actually + deal with refresh failure in the fail_fn. + */ + refresh_name(subrec, namerec, NULL, NULL, NULL); + namerec->death_time += lp_max_ttl(); + namerec->refresh_time += lp_max_ttl(); + } + } + } +} diff --git a/source3/nmbd/nmbd_namelistdb.c b/source3/nmbd/nmbd_namelistdb.c new file mode 100644 index 0000000000..dfd8a80baa --- /dev/null +++ b/source3/nmbd/nmbd_namelistdb.c @@ -0,0 +1,586 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NBT netbios routines and daemon - version 2 + Copyright (C) Andrew Tridgell 1994-1997 + Copyright (C) Luke Kenneth Casson Leighton 1994-1997 + Copyright (C) Jeremy Allison 1994-1997 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "includes.h" + +extern int DEBUGLEVEL; + +extern pstring scope; +extern char **my_netbios_names; + +uint16 samba_nb_type = 0; /* samba's NetBIOS name type */ + + +/**************************************************************************** + Set Samba's NetBIOS name type. + ****************************************************************************/ + +void set_samba_nb_type(void) +{ + if (lp_wins_support() || (*lp_wins_server())) + samba_nb_type = NB_MFLAG; /* samba is a 'hybrid' node type */ + else + samba_nb_type = NB_BFLAG; /* samba is broadcast-only node type */ +} + +/**************************************************************************** + Returns True if the netbios name is ^1^2__MSBROWSE__^2^1. + + Note: This name is registered if as a master browser or backup browser + you are responsible for a workgroup (when you announce a domain by + broadcasting on your local subnet, you announce it as coming from this + name: see announce_host()). + + **************************************************************************/ + +BOOL ms_browser_name(char *name, int type) +{ + return (strequal(name,MSBROWSE) && (type == 0x01)); +} + +/**************************************************************************** + Add a netbios name into a namelist. + **************************************************************************/ + +static void add_name_to_namelist(struct subnet_record *subrec, + struct name_record *namerec) +{ + struct name_record *namerec2; + + if (!subrec->namelist) + { + subrec->namelist = namerec; + namerec->prev = NULL; + namerec->next = NULL; + return; + } + + for (namerec2 = subrec->namelist; namerec2->next; namerec2 = namerec2->next) + ; + + namerec2->next = namerec; + namerec->next = NULL; + namerec->prev = namerec2; + namerec->subnet = subrec; + + subrec->namelist_changed = True; +} + +/**************************************************************************** + Remove a name from the namelist. + **************************************************************************/ + +void remove_name_from_namelist(struct subnet_record *subrec, + struct name_record *namerec) +{ + if (namerec->next) + namerec->next->prev = namerec->prev; + if (namerec->prev) + namerec->prev->next = namerec->next; + + if(namerec == subrec->namelist) + subrec->namelist = namerec->next; + + if(namerec->ip != NULL) + free((char *)namerec->ip); + free((char *)namerec); + + subrec->namelist_changed = True; +} + + +/**************************************************************************** + Find a name in a subnet. + **************************************************************************/ + +struct name_record *find_name_on_subnet(struct subnet_record *subrec, + struct nmb_name *nmbname, BOOL self_only) +{ + struct name_record *namerec = subrec->namelist; + struct name_record *name_ret; + + for (name_ret = namerec; name_ret; name_ret = name_ret->next) + { + if (nmb_name_equal(&name_ret->name, nmbname)) + { + /* Self names only - these include permanent names. */ + if (self_only && (name_ret->source != SELF_NAME) && + (name_ret->source != PERMANENT_NAME) ) + { + continue; + } + DEBUG(9,("find_name_on_subnet: on subnet %s - found name %s source=%d\n", + subrec->subnet_name, namestr(nmbname), name_ret->source)); + return name_ret; + } + } + DEBUG(9,("find_name_on_subnet: on subnet %s - name %s NOT FOUND\n", + subrec->subnet_name, namestr(nmbname))); + return NULL; +} + +/**************************************************************************** + Find a name over all known broadcast subnets. +**************************************************************************/ + +struct name_record *find_name_for_remote_broadcast_subnet( struct nmb_name *nmbname, + BOOL self_only) +{ + struct subnet_record *subrec; + struct name_record *namerec = NULL; + + for( subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) + { + if((namerec = find_name_on_subnet(subrec, nmbname, self_only))!= NULL) + break; + } + + return namerec; +} + +/**************************************************************************** + Update the ttl of an entry in a subnet name list. + ****************************************************************************/ + +void update_name_ttl(struct name_record *namerec, int ttl) +{ + time_t time_now = time(NULL); + + if(namerec->death_time != PERMANENT_TTL) + namerec->death_time = time_now + ttl; + + namerec->refresh_time = time_now + (ttl/2); + + namerec->subnet->namelist_changed = True; +} + +/**************************************************************************** + Add an entry to a subnet name list. + ****************************************************************************/ + +struct name_record *add_name_to_subnet(struct subnet_record *subrec, + char *name, int type, uint16 nb_flags, int ttl, + enum name_source source, int num_ips, struct in_addr *iplist) +{ + struct name_record *namerec; + time_t time_now = time(NULL); + + if((namerec = (struct name_record *)malloc(sizeof(*namerec))) == NULL) + { + DEBUG(0,("add_name_to_subnet: malloc fail.\n")); + return NULL; + } + + bzero((char *)namerec,sizeof(*namerec)); + + namerec->subnet = subrec; + + namerec->num_ips = num_ips; + namerec->ip = (struct in_addr *)malloc(sizeof(struct in_addr) * namerec->num_ips); + if (!namerec->ip) + { + DEBUG(0,("add_name_to_subnet: malloc fail when creating ip_flgs.\n")); + free((char *)namerec); + return NULL; + } + + bzero((char *)namerec->ip, sizeof(struct in_addr) * namerec->num_ips); + + memcpy(&namerec->ip[0], iplist, num_ips * sizeof(struct in_addr)); + + make_nmb_name(&namerec->name,name,type,scope); + + /* Setup the death_time and refresh_time. */ + if(ttl == PERMANENT_TTL) + namerec->death_time = PERMANENT_TTL; + else + namerec->death_time = time_now + ttl; + + namerec->refresh_time = time_now + (ttl/2); + + /* Enter the name as active. */ + namerec->nb_flags = nb_flags | NB_ACTIVE; + + /* If it's our primary name, flag it as so. */ + if(strequal(my_netbios_names[0],name)) + namerec->nb_flags |= NB_PERM; + + namerec->source = source; + + add_name_to_namelist(subrec,namerec); + + DEBUG(3,("add_name_to_subnet: Added netbios name %s with first IP %s ttl=%d nb_flags=%2x to subnet %s\n", + namestr(&namerec->name),inet_ntoa(*iplist),ttl,(unsigned int)nb_flags, + subrec->subnet_name)); + + subrec->namelist_changed = True; + + return(namerec); +} + +/******************************************************************* + Utility function automatically called when a name refresh or register + succeeds. By definition this is a SELF_NAME (or we wouldn't be registering + it). + ******************************************************************/ + +void standard_success_register(struct subnet_record *subrec, + struct userdata_struct *userdata, + struct nmb_name *nmbname, uint16 nb_flags, int ttl, + struct in_addr registered_ip) +{ + struct name_record *namerec = find_name_on_subnet(subrec, nmbname, FIND_SELF_NAME); + + if(namerec == NULL) + add_name_to_subnet(subrec, nmbname->name, nmbname->name_type, + nb_flags, ttl, SELF_NAME, 1, ®istered_ip); + else + update_name_ttl(namerec, ttl); +} + +/******************************************************************* + Utility function automatically called when a name refresh or register + fails. + ******************************************************************/ + +void standard_fail_register(struct subnet_record *subrec, + struct response_record *rrec, struct nmb_name *nmbname) +{ + struct name_record *namerec = find_name_on_subnet(subrec, nmbname, FIND_SELF_NAME); + + DEBUG(0,("standard_fail_register: Failed to register/refresh name %s on subnet %s\n", + namestr(nmbname), subrec->subnet_name)); + + /* Remove the name from the subnet. */ + if(namerec) + remove_name_from_namelist(subrec, namerec); +} + +/******************************************************************* + Utility function to remove an IP address from a name record. + ******************************************************************/ + +static void remove_nth_ip_in_record( struct name_record *namerec, int ind) +{ + if(ind != namerec->num_ips) + memmove( (char *)(&namerec->ip[ind]), (char *)(&namerec->ip[ind+1]), + ( namerec->num_ips - ind - 1) * sizeof(struct in_addr)); + + namerec->num_ips--; + namerec->subnet->namelist_changed = True; +} + +/******************************************************************* + Utility function to check if an IP address exists in a name record. + ******************************************************************/ + +BOOL find_ip_in_name_record(struct name_record *namerec, struct in_addr ip) +{ + int i; + + for(i = 0; i < namerec->num_ips; i++) + if(ip_equal( namerec->ip[i], ip)) + return True; + + return False; +} + +/******************************************************************* + Utility function to add an IP address to a name record. + ******************************************************************/ + +void add_ip_to_name_record(struct name_record *namerec, struct in_addr new_ip) +{ + struct in_addr *new_list; + + /* Don't add one we already have. */ + if(find_ip_in_name_record( namerec, new_ip)) + return; + + if((new_list = (struct in_addr *)malloc( (namerec->num_ips + 1)*sizeof(struct in_addr)) )== NULL) + { + DEBUG(0,("add_ip_to_name_record: Malloc fail !\n")); + return; + } + + memcpy((char *)new_list, (char *)namerec->ip, namerec->num_ips *sizeof(struct in_addr)); + new_list[namerec->num_ips] = new_ip; + + free((char *)namerec->ip); + namerec->ip = new_list; + namerec->num_ips += 1; + + namerec->subnet->namelist_changed = True; +} + +/******************************************************************* + Utility function to remove an IP address from a name record. + ******************************************************************/ + +void remove_ip_from_name_record( struct name_record *namerec, struct in_addr remove_ip) +{ + /* Try and find the requested ip address - remove it. */ + int i; + int orig_num = namerec->num_ips; + + for(i = 0; i < orig_num; i++) + if( ip_equal( remove_ip, namerec->ip[i]) ) + { + remove_nth_ip_in_record( namerec, i); + break; + } +} + +/******************************************************************* + Utility function that release_name callers can plug into as the + success function when a name release is successful. Used to save + duplication of success_function code. + ******************************************************************/ + +void standard_success_release(struct subnet_record *subrec, + struct userdata_struct *userdata, + struct nmb_name *nmbname, struct in_addr released_ip) +{ + struct name_record *namerec = find_name_on_subnet(subrec, nmbname, FIND_ANY_NAME); + + if(namerec == NULL) + { + DEBUG(0,("standard_success_release: Name release for name %s IP %s on subnet %s. Name \ +was not found on subnet.\n", namestr(nmbname), inet_ntoa(released_ip), subrec->subnet_name)); + return; + } + else + { + int orig_num = namerec->num_ips; + + remove_ip_from_name_record( namerec, released_ip); + + if(namerec->num_ips == orig_num) + DEBUG(0,("standard_success_release: Name release for name %s IP %s on subnet %s. This ip \ +is not known for this name.\n", namestr(nmbname), inet_ntoa(released_ip), subrec->subnet_name )); + } + + if (namerec->num_ips == 0) + remove_name_from_namelist(subrec, namerec); +} + +/******************************************************************* + Expires old names in a subnet namelist. + ******************************************************************/ + +void expire_names_on_subnet(struct subnet_record *subrec, time_t t) +{ + struct name_record *namerec; + struct name_record *next_namerec; + + for (namerec = subrec->namelist; namerec; namerec = next_namerec) + { + next_namerec = namerec->next; + if ((namerec->death_time != PERMANENT_TTL) && (namerec->death_time < t)) + { + if (namerec->source == SELF_NAME) + { + DEBUG(3,("expire_names_on_subnet: Subnet %s not expiring SELF name %s\n", + subrec->subnet_name, namestr(&namerec->name))); + namerec->death_time += 300; + namerec->subnet->namelist_changed = True; + continue; + } + DEBUG(3,("expire_names_on_subnet: Subnet %s - removing expired name %s\n", + subrec->subnet_name, namestr(&namerec->name))); + + remove_name_from_namelist(subrec, namerec); + } + } +} + +/******************************************************************* + Expires old names in all subnet namelists. + ******************************************************************/ + +void expire_names(time_t t) +{ + struct subnet_record *subrec; + + for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec)) + { + expire_names_on_subnet(subrec, t); + } +} + +/**************************************************************************** + Add the magic samba names, useful for finding samba servers. + These go directly into the name list for a particular subnet, + without going through the normal registration process. + When adding them to the unicast subnet, add them as a list of + all broadcast subnet IP addresses. +**************************************************************************/ + +void add_samba_names_to_subnet(struct subnet_record *subrec) +{ + struct in_addr *iplist = &subrec->myip; + int num_ips = 1; + + /* These names are added permanently (ttl of zero) and will NOT be + refreshed. */ + + if((subrec == unicast_subnet) || (subrec == wins_server_subnet)) + { + struct subnet_record *bcast_subrecs; + int i; + /* Create an IP list containing all our known subnets. */ + + num_ips = iface_count(); + if((iplist = (struct in_addr *)malloc( num_ips * sizeof(struct in_addr) )) == NULL) + { + DEBUG(0,("add_samba_names_to_subnet: Malloc fail !\n")); + return; + } + + for(bcast_subrecs = FIRST_SUBNET, i = 0; bcast_subrecs; + bcast_subrecs = NEXT_SUBNET_EXCLUDING_UNICAST(bcast_subrecs), i++) + iplist[i] = bcast_subrecs->myip; + + } + + add_name_to_subnet(subrec,"*",0x0,samba_nb_type, PERMANENT_TTL, + PERMANENT_NAME, num_ips, iplist); + add_name_to_subnet(subrec,"*",0x20,samba_nb_type,PERMANENT_TTL, + PERMANENT_NAME, num_ips, iplist); + add_name_to_subnet(subrec,"__SAMBA__",0x20,samba_nb_type,PERMANENT_TTL, + PERMANENT_NAME, num_ips, iplist); + add_name_to_subnet(subrec,"__SAMBA__",0x00,samba_nb_type,PERMANENT_TTL, + PERMANENT_NAME, num_ips, iplist); + + if(iplist != &subrec->myip) + free((char *)iplist); +} + +/**************************************************************************** + Dump the contents of the namelists on all the subnets (including unicast) + into a file. Initiated by SIGHUP - used to debug the state of the namelists. +**************************************************************************/ + +static void dump_subnet_namelist( struct subnet_record *subrec, FILE *fp) +{ + struct name_record *namerec; + char *src_type; + struct tm *tm; + int i; + + fprintf(fp, "Subnet %s\n----------------------\n", subrec->subnet_name); + for (namerec = subrec->namelist; namerec; namerec = namerec->next) + { + fprintf(fp,"\tName = %s\t", namestr(&namerec->name)); + switch(namerec->source) + { + case LMHOSTS_NAME: + src_type = "LMHOSTS_NAME"; + break; + case WINS_PROXY_NAME: + src_type = "WINS_PROXY_NAME"; + break; + case REGISTER_NAME: + src_type = "REGISTER_NAME"; + break; + case SELF_NAME: + src_type = "SELF_NAME"; + break; + case DNS_NAME: + src_type = "DNS_NAME"; + break; + case DNSFAIL_NAME: + src_type = "DNSFAIL_NAME"; + break; + case PERMANENT_NAME: + src_type = "PERMANENT_NAME"; + break; + default: + src_type = "unknown!"; + break; + } + fprintf(fp, "Source = %s\nb_flags = %x\t", src_type, namerec->nb_flags); + + if(namerec->death_time != PERMANENT_TTL) + { + tm = LocalTime(&namerec->death_time); + fprintf(fp, "death_time = %s\t", asctime(tm)); + } + else + fprintf(fp, "death_time = PERMANENT\t"); + + if(namerec->refresh_time != PERMANENT_TTL) + { + tm = LocalTime(&namerec->refresh_time); + fprintf(fp, "refresh_time = %s\n", asctime(tm)); + } + else + fprintf(fp, "refresh_time = PERMANENT\n"); + + fprintf(fp, "\t\tnumber of IPS = %d", namerec->num_ips); + for(i = 0; i < namerec->num_ips; i++) + fprintf(fp, "\t%s", inet_ntoa(namerec->ip[i])); + + fprintf(fp, "\n\n"); + } +} + +/**************************************************************************** + Dump the contents of the namelists on all the subnets (including unicast) + into a file. Initiated by SIGHUP - used to debug the state of the namelists. +**************************************************************************/ + +void dump_all_namelists() +{ + fstring fname; + FILE *fp; + struct subnet_record *subrec; + + pstrcpy(fname,lp_lockdir()); + trim_string(fname,NULL,"/"); + strcat(fname,"/"); + strcat(fname,"namelist.debug"); + + fp = fopen(fname,"w"); + + if (!fp) + { + DEBUG(0,("dump_all_namelists: Can't open file %s. Error was %s\n", + fname,strerror(errno))); + return; + } + + for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec)) + dump_subnet_namelist( subrec, fp); + + if(!we_are_a_wins_client()) + dump_subnet_namelist(unicast_subnet, fp); + + if(remote_broadcast_subnet->namelist != NULL) + dump_subnet_namelist(remote_broadcast_subnet, fp); + + if(wins_server_subnet != NULL) + dump_subnet_namelist( wins_server_subnet, fp); + fclose(fp); +} diff --git a/source3/nmbd/nmbd_namequery.c b/source3/nmbd/nmbd_namequery.c new file mode 100644 index 0000000000..5d98935fec --- /dev/null +++ b/source3/nmbd/nmbd_namequery.c @@ -0,0 +1,234 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NBT netbios routines and daemon - version 2 + Copyright (C) Andrew Tridgell 1994-1997 + Copyright (C) Luke Kenneth Casson Leighton 1994-1997 + Copyright (C) Jeremy Allison 1994-1997 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "includes.h" + +extern int DEBUGLEVEL; + +extern pstring scope; + +/**************************************************************************** + Deal with a response packet when querying a name. +****************************************************************************/ + +static void query_name_response(struct subnet_record *subrec, + struct response_record *rrec, struct packet_struct *p) +{ + struct nmb_packet *nmb = &p->packet.nmb; + BOOL success = False; + struct nmb_name *question_name = &rrec->packet->packet.nmb.question.question_name; + struct in_addr answer_ip; + + /* Ensure we don't retry the query but leave the response record cleanup + to the timeout code. We may get more answer responses in which case + we should mark the name in conflict.. */ + rrec->repeat_count = 0; + + if(rrec->num_msgs == 1) + { + /* This is the first response. */ + + if(nmb->header.opcode == NMB_WACK_OPCODE) + { + /* WINS server is telling us to wait. Pretend we didn't get + the response but don't send out any more query requests. */ + + DEBUG(5,("query_name_response: WACK from WINS server %s in querying \ +name %s on subnet %s.\n", inet_ntoa(p->ip), namestr(question_name), subrec->subnet_name)); + + rrec->repeat_count = 0; + /* How long we should wait for. */ + rrec->repeat_time = p->timestamp + nmb->answers->ttl; + rrec->num_msgs--; + return; + } + else if(nmb->header.rcode != 0) + { + success = False; + + DEBUG(5,("query_name_response: On subnet %s - negative response \ +from IP %s for name %s. Error code was %d.\n", subrec->subnet_name, inet_ntoa(p->ip), + namestr(question_name), nmb->header.rcode)); + } + else + { + success = True; + + putip((char *)&answer_ip,&nmb->answers->rdata[2]); + DEBUG(5,("query_name_response: On subnet %s - positive response from IP %s\ +for name %s. IP of that name is %s\n", subrec->subnet_name, inet_ntoa(p->ip), + namestr(question_name), inet_ntoa(answer_ip))); + + /* Interestingly, we could add these names to our namelists, and + change nmbd to a model that checked its own name cache first, + before sending out a query. This is a task for another day, though. + */ + } + } + else if( rrec->num_msgs > 1) + { + DEBUG(0,("query_name_response: Multiple (%d) responses received for a query on \ +subnet %s for name %s. This response was from IP %s\n", + rrec->num_msgs, subrec->subnet_name, namestr(question_name), + inet_ntoa(rrec->packet->ip) )); + + /* We have already called the success or fail function, so we + don't call again here. Leave the response record around in + case we get more responses. */ + + return; + } + + if(success && rrec->success_fn) + (*rrec->success_fn)(subrec, rrec->userdata, question_name, answer_ip, nmb->answers); + else if( rrec->fail_fn) + (*rrec->fail_fn)(subrec, rrec, question_name, nmb->header.rcode); + +} + +/**************************************************************************** + Deal with a timeout when querying a name. +****************************************************************************/ + +static void query_name_timeout_response(struct subnet_record *subrec, + struct response_record *rrec) +{ + struct nmb_packet *sent_nmb = &rrec->packet->packet.nmb; + /* We can only fail here, never succeed. */ + BOOL failed = True; + struct nmb_name *question_name = &sent_nmb->question.question_name; + + if(rrec->num_msgs != 0) + { + /* We got at least one response, and have called the success/fail + function already. */ + + failed = False; + } + + if(failed) + { + DEBUG(5,("query_name_timeout_response: No response to querying name %s on subnet %s.\n", + namestr(question_name), subrec->subnet_name)); + + if(rrec->fail_fn) + (*rrec->fail_fn)(subrec, rrec, question_name, 0); + } + + remove_response_record(subrec, rrec); +} + +/**************************************************************************** + Lookup a name on our local namelists. We check the lmhosts file first. If the + name is not there we look for the name on the given subnet. +****************************************************************************/ + +static BOOL query_local_namelists(struct subnet_record *subrec, struct nmb_name *nmbname, + struct name_record **namerecp) +{ + struct name_record *namerec; + + *namerecp = NULL; + + if(find_name_in_lmhosts(nmbname, namerecp)) + return True; + + if((namerec = find_name_on_subnet(subrec, nmbname, FIND_ANY_NAME))==NULL) + return False; + + if(NAME_IS_ACTIVE(namerec) && ((namerec->source == SELF_NAME) || + (namerec->source == LMHOSTS_NAME)) ) + { + *namerecp = namerec; + return True; + } + return False; +} + +/**************************************************************************** + Try and query for a name. +****************************************************************************/ + +BOOL query_name(struct subnet_record *subrec, char *name, int type, + query_name_success_function success_fn, + query_name_fail_function fail_fn, + struct userdata_struct *userdata) +{ + struct nmb_name nmbname; + struct name_record *namerec; + + make_nmb_name(&nmbname, name, type, scope); + + /* + * We need to check our local namelists first. + * It may be an magic name, lmhosts name or just + * a name we have registered. + */ + + if(query_local_namelists(subrec, &nmbname, &namerec) == True) + { + struct res_rec rrec; + int i; + + bzero((char *)&rrec, sizeof(struct res_rec)); + + /* Fake up the needed res_rec just in case it's used. */ + rrec.rr_name = nmbname; + rrec.rr_type = RR_TYPE_NB; + rrec.rr_class = RR_CLASS_IN; + rrec.ttl = PERMANENT_TTL; + rrec.rdlength = namerec->num_ips * 6; + if(rrec.rdlength > MAX_DGRAM_SIZE) + { + DEBUG(0,("query_name: nmbd internal error - there are %d ip addresses for name %s.\n", + namerec->num_ips, namestr(&nmbname) )); + return False; + } + + for( i = 0; i < namerec->num_ips; i++) + { + set_nb_flags( &rrec.rdata[i*6], namerec->nb_flags ); + putip( &rrec.rdata[(i*6) + 2], (char *)&namerec->ip[i]); + } + + /* Call the success function directly. */ + if(success_fn) + (*success_fn)(subrec, userdata, &nmbname, namerec->ip[0], &rrec); + return False; + } + + if(queue_query_name( subrec, + query_name_response, + query_name_timeout_response, + success_fn, + fail_fn, + userdata, + &nmbname) == NULL) + { + DEBUG(0,("query_name: Failed to send packet trying to query name %s\n", + namestr(&nmbname))); + return True; + } + return False; +} diff --git a/source3/nmbd/nmbd_nameregister.c b/source3/nmbd/nmbd_nameregister.c new file mode 100644 index 0000000000..603daaa531 --- /dev/null +++ b/source3/nmbd/nmbd_nameregister.c @@ -0,0 +1,390 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NBT netbios routines and daemon - version 2 + Copyright (C) Andrew Tridgell 1994-1997 + Copyright (C) Luke Kenneth Casson Leighton 1994-1997 + Copyright (C) Jeremy Allison 1994-1997 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "includes.h" + +extern int DEBUGLEVEL; + +extern pstring scope; +extern fstring myworkgroup; + +/**************************************************************************** + Deal with a response packet when registering one of our names. +****************************************************************************/ + +static void register_name_response(struct subnet_record *subrec, + struct response_record *rrec, struct packet_struct *p) +{ + /* + * If we are registering broadcast, then getting a response is an + * error - we do not have the name. If we are registering unicast, + * then we expect to get a response. + */ + + struct nmb_packet *nmb = &p->packet.nmb; + BOOL bcast = nmb->header.nm_flags.bcast; + BOOL success = True; + struct nmb_name *question_name = &rrec->packet->packet.nmb.question.question_name; + struct nmb_name *answer_name = &nmb->answers->rr_name; + int ttl; + uint16 nb_flags; + struct in_addr registered_ip; + + /* Sanity check. Ensure that the answer name in the incoming packet is the + same as the requested name in the outgoing packet. */ + + if(!nmb_name_equal(question_name, answer_name)) + { + DEBUG(0,("register_name_response: Answer name %s differs from question \ +name %s.\n", namestr(answer_name), namestr(question_name))); + return; + } + + if(bcast) + { + /* + * Special hack to cope with old Samba nmbd's. + * Earlier versions of Samba (up to 1.9.16p11) respond + * to a broadcast name registration of WORKGROUP<1b> when + * they should not. Hence, until these versions are gone, + * we should treat such errors as success for this particular + * case only. jallison@whistle.com. + */ + +#if 1 /* OLD_SAMBA_SERVER_HACK */ + if((nmb->header.rcode == ACT_ERR) && strequal(myworkgroup, answer_name->name) && + (answer_name->name_type == 0x1b)) + { + /* Pretend we did not get this. */ + rrec->num_msgs--; + + DEBUG(5,("register_name_response: Ignoring broadcast response to \ +registration of name %s due to old Samba server bug.\n", namestr(answer_name))); + return; + } +#endif /* OLD_SAMBA_SERVER_HACK */ + + /* Someone else has the name. Log the problem. */ + DEBUG(1,("register_name_response: Failed to register \ +name %s on subnet %s via broadcast. Error code was %d. Reject came from IP %s\n", + namestr(answer_name), + subrec->subnet_name, nmb->header.rcode, inet_ntoa(p->ip))); + success = False; + } + else + { + /* Unicast - check to see if the response allows us to have the name. */ + if(nmb->header.rcode != 0) + { + /* Error code - we didn't get the name. */ + success = False; + + DEBUG(0,("register_name_response: server at IP %s rejected our \ +name registration of %s with error code %d.\n", inet_ntoa(p->ip), + namestr(answer_name), nmb->header.rcode)); + + } + else if(nmb->header.opcode == NMB_WACK_OPCODE) + { + /* WINS server is telling us to wait. Pretend we didn't get + the response but don't send out any more register requests. */ + + DEBUG(5,("register_name_response: WACK from WINS server %s in registering \ +name %s on subnet %s.\n", inet_ntoa(p->ip), namestr(answer_name), subrec->subnet_name)); + + rrec->repeat_count = 0; + /* How long we should wait for. */ + rrec->repeat_time = p->timestamp + nmb->answers->ttl; + rrec->num_msgs--; + return; + } + else + { + success = True; + /* Get the data we need to pass to the success function. */ + nb_flags = get_nb_flags(nmb->answers->rdata); + putip((char*)®istered_ip,&nmb->answers->rdata[2]); + ttl = nmb->answers->ttl; + } + } + + DEBUG(5,("register_name_response: %s in registering name %s on subnet %s.\n", + success ? "success" : "failure", namestr(answer_name), subrec->subnet_name)); + + if(success) + { + /* Enter the registered name into the subnet name database before calling + the success function. */ + standard_success_register(subrec, rrec->userdata, answer_name, nb_flags, ttl, registered_ip); + if( rrec->success_fn) + (*rrec->success_fn)(subrec, rrec->userdata, answer_name, nb_flags, ttl, registered_ip); + } + else + { + if( rrec->fail_fn) + (*rrec->fail_fn)(subrec, rrec, question_name); + /* Remove the name. */ + standard_fail_register( subrec, rrec, question_name); + } + + /* Ensure we don't retry. */ + remove_response_record(subrec, rrec); +} + +/**************************************************************************** + Deal with a timeout when registering one of our names. +****************************************************************************/ + +static void register_name_timeout_response(struct subnet_record *subrec, + struct response_record *rrec) +{ + /* + * If we are registering unicast, then NOT getting a response is an + * error - we do not have the name. If we are registering broadcast, + * then we don't expect to get a response. + */ + + struct nmb_packet *sent_nmb = &rrec->packet->packet.nmb; + BOOL bcast = sent_nmb->header.nm_flags.bcast; + BOOL success = False; + struct nmb_name *question_name = &sent_nmb->question.question_name; + uint16 nb_flags; + int ttl; + struct in_addr registered_ip; + + if(bcast) + { + if(rrec->num_msgs == 0) + { + /* Not receiving a message is success for broadcast registration. */ + success = True; + + /* Pull the success values from the original request packet. */ + nb_flags = get_nb_flags(sent_nmb->additional->rdata); + ttl = sent_nmb->additional->ttl; + putip(®istered_ip,&sent_nmb->additional->rdata[2]); + } + } + else + { + /* Unicast - if no responses then it's an error. */ + if(rrec->num_msgs == 0) + { + DEBUG(2,("register_name_timeout_response: WINS server at address %s is not \ +responding.\n", inet_ntoa(rrec->packet->ip))); + + /* Keep trying to contact the WINS server periodically. This allows + us to work correctly if the WINS server is down temporarily when + we come up. */ + + /* Reset the number of attempts to zero and double the interval between + retries. Max out at 5 minutes. */ + rrec->repeat_count = 3; + rrec->repeat_interval *= 2; + if(rrec->repeat_interval > (5 * 60)) + rrec->repeat_interval = (5 * 60); + rrec->repeat_time = time(NULL) + rrec->repeat_interval; + + DEBUG(5,("register_name_timeout_response: increasing WINS timeout to %d seconds.\n", + rrec->repeat_interval)); + return; /* Don't remove the response record. */ + } + } + + DEBUG(5,("register_name_timeout_response: %s in registering name %s on subnet %s.\n", + success ? "success" : "failure", namestr(question_name), subrec->subnet_name)); + if(success) + { + /* Enter the registered name into the subnet name database before calling + the success function. */ + standard_success_register(subrec, rrec->userdata, question_name, nb_flags, ttl, registered_ip); + if( rrec->success_fn) + (*rrec->success_fn)(subrec, rrec->userdata, question_name, nb_flags, ttl, registered_ip); + } + else + { + if( rrec->fail_fn) + (*rrec->fail_fn)(subrec, rrec, question_name); + /* Remove the name. */ + standard_fail_register( subrec, rrec, question_name); + } + + /* Ensure we don't retry. */ + remove_response_record(subrec, rrec); +} + +/**************************************************************************** + Try and register one of our names on the unicast subnet - multihomed. +****************************************************************************/ + +static BOOL multihomed_register_name( struct nmb_name *nmbname, uint16 nb_flags, + register_name_success_function success_fn, + register_name_fail_function fail_fn, + struct userdata_struct *userdata) +{ + /* + If we are adding a group name, we just send multiple + register name packets to the WINS server (this is an + internet group name. + + If we are adding a unique name, We need first to add + our names to the unicast subnet namelist. This is + because when a WINS server receives a multihomed + registration request, the first thing it does is to + send a name query to the registering machine, to see + if it has put the name in it's local namelist. + We need the name there so the query response code in + nmbd_incomingrequests.c will find it. + + We are adding this name prematurely (we don't really + have it yet), but as this is on the unicast subnet + only we will get away with this (only the WINS server + will ever query names from us on this subnet). + */ + + int num_ips=0; + int i; + struct in_addr *ip_list = NULL; + struct subnet_record *subrec; + + for(subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec) ) + num_ips++; + + if((ip_list = (struct in_addr *)malloc(num_ips * sizeof(struct in_addr)))==NULL) + { + DEBUG(0,("multihomed_register_name: malloc fail !\n")); + return True; + } + + for(subrec = FIRST_SUBNET, i = 0; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec), i++ ) + ip_list[i] = subrec->myip; + + add_name_to_subnet(unicast_subnet, nmbname->name, nmbname->name_type, + nb_flags, lp_max_ttl(), SELF_NAME, num_ips, ip_list); + + free((char *)ip_list); + + /* Now try and register the name, num_ips times. On the last time use + the given success and fail functions. */ + + for( i = 0; i < num_ips; i++) + { + if(queue_register_multihomed_name( unicast_subnet, + register_name_response, + register_name_timeout_response, + (i == num_ips - 1) ? success_fn : NULL, + (i == num_ips - 1) ? fail_fn : NULL, + (i == num_ips - 1) ? userdata : NULL, + nmbname, + nb_flags, + ip_list[i]) == NULL) + { + DEBUG(0,("multihomed_register_name: Failed to send packet trying to \ +register name %s IP %s\n", namestr(nmbname), inet_ntoa(ip_list[i]) )); + return True; + } + } + + return False; +} + +/**************************************************************************** + Try and register one of our names. +****************************************************************************/ + +BOOL register_name(struct subnet_record *subrec, + char *name, int type, uint16 nb_flags, + register_name_success_function success_fn, + register_name_fail_function fail_fn, + struct userdata_struct *userdata) +{ + struct nmb_name nmbname; + + make_nmb_name(&nmbname, name, type, scope); + + /* Always set the NB_ACTIVE flag on the name we are + registering. Doesn't make sense without it. + */ + + nb_flags |= NB_ACTIVE; + + /* If this is the unicast subnet, and we are a multi-homed + host, then register a multi-homed name. */ + + if( (subrec == unicast_subnet) && we_are_multihomed()) + return multihomed_register_name(&nmbname, nb_flags, + success_fn, fail_fn, + userdata); + + if(queue_register_name( subrec, + register_name_response, + register_name_timeout_response, + success_fn, + fail_fn, + userdata, + &nmbname, + nb_flags) == NULL) + { + DEBUG(0,("register_name: Failed to send packet trying to register name %s\n", + namestr(&nmbname))); + return True; + } + return False; +} + +/**************************************************************************** + Try and refresh one of our names. +****************************************************************************/ + +BOOL refresh_name(struct subnet_record *subrec, struct name_record *namerec, + refresh_name_success_function success_fn, + refresh_name_fail_function fail_fn, + struct userdata_struct *userdata) +{ + int i; + + /* + * Go through and refresh the name for all known ip addresses. + * Only call the success/fail function on the last one (it should + * only be done once). + */ + + for( i = 0; i < namerec->num_ips; i++) + { + if(queue_refresh_name( subrec, + register_name_response, + register_name_timeout_response, + (i == (namerec->num_ips - 1)) ? success_fn : NULL, + (i == (namerec->num_ips - 1)) ? fail_fn : NULL, + (i == (namerec->num_ips - 1)) ? userdata : NULL, + namerec, + namerec->ip[i]) == NULL) + { + DEBUG(0,("refresh_name: Failed to send packet trying to refresh name %s\n", + namestr(&namerec->name))); + return True; + } + } + return False; +} diff --git a/source3/nmbd/nmbd_namerelease.c b/source3/nmbd/nmbd_namerelease.c new file mode 100644 index 0000000000..8632dd7655 --- /dev/null +++ b/source3/nmbd/nmbd_namerelease.c @@ -0,0 +1,238 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NBT netbios routines and daemon - version 2 + Copyright (C) Andrew Tridgell 1994-1997 + Copyright (C) Luke Kenneth Casson Leighton 1994-1997 + Copyright (C) Jeremy Allison 1994-1997 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "includes.h" + +extern int DEBUGLEVEL; + +extern pstring scope; + +/**************************************************************************** + Deal with a response packet when releasing one of our names. +****************************************************************************/ + +static void release_name_response(struct subnet_record *subrec, + struct response_record *rrec, struct packet_struct *p) +{ + /* + * If we are releasing broadcast, then getting a response is an + * error. If we are releasing unicast, then we expect to get a response. + */ + + struct nmb_packet *nmb = &p->packet.nmb; + BOOL bcast = nmb->header.nm_flags.bcast; + BOOL success = True; + struct nmb_name *question_name = &rrec->packet->packet.nmb.question.question_name; + struct nmb_name *answer_name = &nmb->answers->rr_name; + struct in_addr released_ip; + + /* Sanity check. Ensure that the answer name in the incoming packet is the + same as the requested name in the outgoing packet. */ + + if(!nmb_name_equal(question_name, answer_name)) + { + DEBUG(0,("release_name_response: Answer name %s differs from question \ +name %s.\n", namestr(answer_name), namestr(question_name))); + return; + } + + if(bcast) + { + /* Someone sent a response. This shouldn't happen/ */ + DEBUG(1,("release_name_response: A response for releasing name %s was received on a\ +broadcast subnet %s. This should not happen !\n", namestr(answer_name), subrec->subnet_name)); + return; + } + else + { + /* Unicast - check to see if the response allows us to release the name. */ + if(nmb->header.rcode != 0) + { + /* Error code - we were told not to release the name ! What now ! */ + success = False; + + DEBUG(0,("release_name_response: WINS server at IP %s rejected our \ +name release of name %s with error code %d.\n", inet_ntoa(p->ip), + namestr(answer_name), nmb->header.rcode)); + + } + else if(nmb->header.opcode == NMB_WACK_OPCODE) + { + /* WINS server is telling us to wait. Pretend we didn't get + the response but don't send out any more release requests. */ + + DEBUG(5,("release_name_response: WACK from WINS server %s in releasing \ +name %s on subnet %s.\n", inet_ntoa(p->ip), namestr(answer_name), subrec->subnet_name)); + + rrec->repeat_count = 0; + /* How long we should wait for. */ + rrec->repeat_time = p->timestamp + nmb->answers->ttl; + rrec->num_msgs--; + return; + } + } + + DEBUG(5,("release_name_response: %s in releasing name %s on subnet %s.\n", + success ? "success" : "failure", namestr(answer_name), subrec->subnet_name)); + + if(success) + { + putip((char*)&released_ip ,&nmb->answers->rdata[2]); + + if(rrec->success_fn) + (*rrec->success_fn)(subrec, rrec->userdata, answer_name, released_ip); + standard_success_release( subrec, rrec->userdata, answer_name, released_ip); + } + else + { + /* We have no standard_fail_release - maybe we should add one ? */ + if(rrec->fail_fn) + (*rrec->fail_fn)(subrec, rrec, answer_name); + } + + remove_response_record(subrec, rrec); +} + +/**************************************************************************** + Deal with a timeout when releasing one of our names. +****************************************************************************/ + +static void release_name_timeout_response(struct subnet_record *subrec, + struct response_record *rrec) +{ + /* + * If we are releasing unicast, then NOT getting a response is an + * error - we could not release the name. If we are releasing broadcast, + * then we don't expect to get a response. + */ + + struct nmb_packet *sent_nmb = &rrec->packet->packet.nmb; + BOOL bcast = sent_nmb->header.nm_flags.bcast; + BOOL success = False; + struct nmb_name *question_name = &sent_nmb->question.question_name; + struct in_addr released_ip; + + if(bcast) + { + if(rrec->num_msgs == 0) + { + /* Not receiving a message is success for broadcast release. */ + success = True; + + /* Get the ip address we were trying to release. */ + putip((char*)&released_ip ,&sent_nmb->additional->rdata[2]); + } + } + else + { + /* Unicast - if no responses then it's an error. */ + if(rrec->num_msgs == 0) + { + DEBUG(2,("release_name_timeout_response: WINS server at address %s is not \ +responding.\n", inet_ntoa(rrec->packet->ip))); + + /* Keep trying to contact the WINS server periodically. This allows + us to work correctly if the WINS server is down temporarily when + we want to delete the name. */ + + /* Reset the number of attempts to zero and double the interval between + retries. Max out at 5 minutes. */ + rrec->repeat_count = 3; + rrec->repeat_interval *= 2; + if(rrec->repeat_interval > (5 * 60)) + rrec->repeat_interval = (5 * 60); + rrec->repeat_time = time(NULL) + rrec->repeat_interval; + + DEBUG(5,("release_name_timeout_response: increasing WINS timeout to %d seconds.\n", + rrec->repeat_interval)); + return; /* Don't remove the response record. */ + } + } + + DEBUG(5,("release_name_timeout_response: %s in releasing name %s on subnet %s.\n", + success ? "success" : "failure", namestr(question_name), subrec->subnet_name)); + + if(success && rrec->success_fn) + { + if(rrec->success_fn) + (*rrec->success_fn)(subrec, rrec->userdata, question_name, released_ip); + standard_success_release( subrec, rrec->userdata, question_name, released_ip); + } + else + { + /* We have no standard_fail_release - maybe we should add one ? */ + if( rrec->fail_fn) + (*rrec->fail_fn)(subrec, rrec, question_name); + } + + remove_response_record(subrec, rrec); +} + +/**************************************************************************** + Try and release one of our names. +****************************************************************************/ + +BOOL release_name(struct subnet_record *subrec, struct name_record *namerec, + release_name_success_function success_fn, + release_name_fail_function fail_fn, + struct userdata_struct *userdata) +{ + int i; + + /* Ensure it's a SELF name, and in the ACTIVE state. */ + if((namerec->source != SELF_NAME) || !NAME_IS_ACTIVE(namerec)) + { + DEBUG(0,("release_name: Cannot release name %s from subnet %s. Source was %d \n", + namestr(&namerec->name), subrec->subnet_name, namerec->source)); + return True; + } + + /* Set the name into the deregistering state. */ + namerec->nb_flags |= NB_DEREG; + + /* + * Go through and release the name for all known ip addresses. + * Only call the success/fail function on the last one (it should + * only be done once). + */ + + for( i = 0; i < namerec->num_ips; i++) + { + if(queue_release_name( subrec, + release_name_response, + release_name_timeout_response, + (i == (namerec->num_ips - 1)) ? success_fn : NULL, + (i == (namerec->num_ips - 1)) ? fail_fn : NULL, + (i == (namerec->num_ips - 1)) ? userdata : NULL, + &namerec->name, + namerec->nb_flags, + namerec->ip[i]) == NULL) + { + DEBUG(0,("release_name: Failed to send packet trying to release name %s IP %s\n", + namestr(&namerec->name), inet_ntoa(namerec->ip[i]) )); + return True; + } + } + return False; +} diff --git a/source3/nmbd/nmbd_nodestatus.c b/source3/nmbd/nmbd_nodestatus.c new file mode 100644 index 0000000000..267446c69d --- /dev/null +++ b/source3/nmbd/nmbd_nodestatus.c @@ -0,0 +1,99 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NBT netbios routines and daemon - version 2 + Copyright (C) Andrew Tridgell 1994-1997 + Copyright (C) Luke Kenneth Casson Leighton 1994-1997 + Copyright (C) Jeremy Allison 1994-1997 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "includes.h" + +extern int DEBUGLEVEL; + +extern pstring scope; + +/**************************************************************************** + Deal with a successful node status response. +****************************************************************************/ +static void node_status_response(struct subnet_record *subrec, + struct response_record *rrec, struct packet_struct *p) +{ + struct nmb_packet *nmb = &p->packet.nmb; + struct nmb_name *question_name = &rrec->packet->packet.nmb.question.question_name; + struct nmb_name *answer_name = &nmb->answers->rr_name; + + /* Sanity check. Ensure that the answer name in the incoming packet is the + same as the requested name in the outgoing packet. */ + + if(!nmb_name_equal(question_name, answer_name)) + { + DEBUG(0,("node_status_response: Answer name %s differs from question \ +name %s.\n", namestr(answer_name), namestr(question_name))); + return; + } + + DEBUG(5,("node_status_response: response from name %s on subnet %s.\n", + namestr(answer_name), subrec->subnet_name)); + + /* Just send the whole answer resource record for the success function + to parse. */ + if(rrec->success_fn) + (*rrec->success_fn)(subrec, rrec->userdata, nmb->answers, p->ip); + + /* Ensure we don't retry. */ + remove_response_record(subrec, rrec); +} + +/**************************************************************************** + Deal with a timeout when requesting a node status. +****************************************************************************/ +static void node_status_timeout_response(struct subnet_record *subrec, + struct response_record *rrec) +{ + struct nmb_packet *sent_nmb = &rrec->packet->packet.nmb; + struct nmb_name *question_name = &sent_nmb->question.question_name; + + DEBUG(5,("node_status_timeout_response: failed to get node status from name %s on subnet %s\n", + namestr(question_name), subrec->subnet_name)); + + if( rrec->fail_fn) + (*rrec->fail_fn)(subrec, rrec); + + /* Ensure we don't retry. */ + remove_response_record(subrec, rrec); +} + +/**************************************************************************** + Try and do a node status to a name - given the name & IP address. +****************************************************************************/ + +BOOL node_status(struct subnet_record *subrec, struct nmb_name *nmbname, + struct in_addr send_ip, node_status_success_function success_fn, + node_status_fail_function fail_fn, struct userdata_struct *userdata) +{ + if(queue_node_status( subrec, + node_status_response, node_status_timeout_response, + success_fn, fail_fn, userdata, nmbname, send_ip)==NULL) + { + DEBUG(0,("node_status: Failed to send packet trying to get node status for \ +name %s, IP address %s\n", namestr(nmbname), inet_ntoa(send_ip))); + return True; + } + return False; +} diff --git a/source3/nmbd/nmbd_packets.c b/source3/nmbd/nmbd_packets.c new file mode 100644 index 0000000000..43249cc0a3 --- /dev/null +++ b/source3/nmbd/nmbd_packets.c @@ -0,0 +1,1775 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NBT netbios routines and daemon - version 2 + Copyright (C) Andrew Tridgell 1994-1997 + Copyright (C) Luke Kenneth Casson Leighton 1994-1997 + Copyright (C) Jeremy Allison 1994-1997 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "includes.h" + +extern int ClientNMB; +extern int ClientDGRAM; +extern int global_nmb_port; + +extern int DEBUGLEVEL; + +extern int num_response_packets; + +extern pstring scope; +extern struct in_addr loopback_ip; + +/******************************************************************* + The global packet linked-list. Incoming entries are + added to the end of this list. It is supposed to remain fairly + short so we won't bother with an end pointer. +******************************************************************/ + +static struct packet_struct *packet_queue = NULL; + +/*************************************************************************** +Utility function to find the specific fd to send a packet out on. +**************************************************************************/ + +static int find_subnet_fd_for_address( struct in_addr local_ip ) +{ + struct subnet_record *subrec; + + for( subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) + if(ip_equal(local_ip, subrec->myip)) + return subrec->nmb_sock; + + return ClientNMB; +} + +/*************************************************************************** +Get/Set problematic nb_flags as network byte order 16 bit int. +**************************************************************************/ + +uint16 get_nb_flags(char *buf) +{ + return ((((uint16)*buf)&0xFFFF) & NB_FLGMSK); +} + +void set_nb_flags(char *buf, uint16 nb_flags) +{ + *buf++ = ((nb_flags & NB_FLGMSK) & 0xFF); + *buf = '\0'; +} + +/*************************************************************************** +Dumps out the browse packet data. +**************************************************************************/ + +static void debug_browse_data(char *outbuf, int len) +{ + int i,j; + for (i = 0; i < len; i+= 16) + { + DEBUG(4, ("%3x char ", i)); + + for (j = 0; j < 16; j++) + { + unsigned char x = outbuf[i+j]; + if (x < 32 || x > 127) + x = '.'; + + if (i+j >= len) + break; + DEBUG(4, ("%c", x)); + } + + DEBUG(4, (" hex ", i)); + + for (j = 0; j < 16; j++) + { + if (i+j >= len) + break; + DEBUG(4, (" %02x", (unsigned char)outbuf[i+j])); + } + + DEBUG(4, ("\n")); + } +} + +/*************************************************************************** + Generates the unique transaction identifier +**************************************************************************/ + +static uint16 name_trn_id=0; + +static uint16 generate_name_trn_id(void) +{ + + if (!name_trn_id) + { + name_trn_id = (time(NULL)%(unsigned)0x7FFF) + (getpid()%(unsigned)100); + } + name_trn_id = (name_trn_id+1) % (unsigned)0x7FFF; + return name_trn_id; +} + +/*************************************************************************** + Either loops back or sends out a completed NetBIOS packet. +**************************************************************************/ + +static BOOL send_netbios_packet(struct packet_struct *p) +{ + BOOL loopback_this_packet = False; + + /* Check if we are sending to or from ourselves as a WINS server. */ + if(ismyip(p->ip) && (p->port == global_nmb_port)) + loopback_this_packet = True; + + if(loopback_this_packet) + { + struct packet_struct *lo_packet = NULL; + DEBUG(5,("send_netbios_packet: sending packet to ourselves.\n")); + if((lo_packet = copy_packet(p)) == NULL) + return False; + queue_packet(lo_packet); + } + else if (!send_packet(p)) + { + DEBUG(0,("send_netbios_packet: send_packet() to IP %s port %d failed\n", + inet_ntoa(p->ip),p->port)); + return False; + } + + return True; +} + +/*************************************************************************** + Sets up the common elements of an outgoing NetBIOS packet. +**************************************************************************/ + +static struct packet_struct *create_and_init_netbios_packet(struct nmb_name *nmbname, + BOOL bcast, + struct in_addr to_ip) +{ + struct packet_struct *packet = NULL; + struct nmb_packet *nmb = NULL; + + /* Allocate the packet_struct we will return. */ + if((packet = (struct packet_struct *)malloc(sizeof(*packet))) == NULL) + { + DEBUG(0,("create_and_init_netbios_packet: malloc fail (1) for packet struct.\n")); + return NULL; + } + + bzero((char *)packet,sizeof(*packet)); + + nmb = &packet->packet.nmb; + + nmb->header.name_trn_id = generate_name_trn_id(); + nmb->header.response = False; + nmb->header.nm_flags.recursion_desired = False; + nmb->header.nm_flags.recursion_available = False; + nmb->header.nm_flags.trunc = False; + nmb->header.nm_flags.authoritative = False; + nmb->header.nm_flags.bcast = bcast; + + nmb->header.rcode = 0; + nmb->header.qdcount = 1; + nmb->header.ancount = 0; + nmb->header.nscount = 0; + + nmb->question.question_name = *nmbname; + nmb->question.question_type = QUESTION_TYPE_NB_QUERY; + nmb->question.question_class = QUESTION_CLASS_IN; + + packet->ip = to_ip; + packet->port = NMB_PORT; + packet->fd = ClientNMB; + packet->timestamp = time(NULL); + packet->packet_type = NMB_PACKET; + packet->locked = False; + + return packet; /* Caller must free. */ +} + +/*************************************************************************** + Sets up the common elements of register, refresh or release packet. +**************************************************************************/ + +static BOOL create_and_init_additional_record(struct packet_struct *packet, + uint16 nb_flags, + struct in_addr *register_ip) +{ + struct nmb_packet *nmb = &packet->packet.nmb; + + if((nmb->additional = (struct res_rec *)malloc(sizeof(struct res_rec))) == NULL) + { + DEBUG(0,("initiate_name_register_packet: malloc fail for additional record.\n")); + return False; + } + + bzero((char *)nmb->additional,sizeof(struct res_rec)); + + nmb->additional->rr_name = nmb->question.question_name; + nmb->additional->rr_type = RR_TYPE_NB; + nmb->additional->rr_class = RR_CLASS_IN; + + nmb->additional->ttl = lp_max_ttl(); + + nmb->additional->rdlength = 6; + + set_nb_flags(nmb->additional->rdata,nb_flags); + + /* Set the address for the name we are registering. */ + putip(&nmb->additional->rdata[2], register_ip); + + /* Ensure that we send out the file descriptor to give us the + the specific source address we are registering as our + IP source address. */ + + packet->fd = find_subnet_fd_for_address( *register_ip ); + + return True; +} + +/*************************************************************************** + Sends out a name query. +**************************************************************************/ + +static BOOL initiate_name_query_packet( struct packet_struct *packet) +{ + struct nmb_packet *nmb = NULL; + + nmb = &packet->packet.nmb; + + nmb->header.opcode = NMB_NAME_QUERY_OPCODE; + nmb->header.arcount = 0; + + nmb->header.nm_flags.recursion_desired = True; + + DEBUG(4,("initiate_name_query_packet: sending query for name %s (bcast=%s) to IP %s\n", + namestr(&nmb->question.question_name), + BOOLSTR(nmb->header.nm_flags.bcast), inet_ntoa(packet->ip))); + + return send_netbios_packet( packet ); +} + +/*************************************************************************** + Sends out a name register. +**************************************************************************/ + +static BOOL initiate_name_register_packet( struct packet_struct *packet, + uint16 nb_flags, struct in_addr *register_ip) +{ + struct nmb_packet *nmb = &packet->packet.nmb; + + nmb->header.opcode = NMB_NAME_REG_OPCODE; + nmb->header.arcount = 1; + + nmb->header.nm_flags.recursion_desired = True; + + if(create_and_init_additional_record(packet, nb_flags, register_ip) == False) + return False; + + DEBUG(4,("initiate_name_register_packet: sending registration for name %s (bcast=%s) to IP %s\n", + namestr(&nmb->additional->rr_name), + BOOLSTR(nmb->header.nm_flags.bcast), inet_ntoa(packet->ip))); + + return send_netbios_packet( packet ); +} + +/*************************************************************************** + Sends out a multihomed name register. +**************************************************************************/ + +static BOOL initiate_multihomed_name_register_packet( struct packet_struct *packet, + uint16 nb_flags, struct in_addr *register_ip) +{ + struct nmb_packet *nmb = &packet->packet.nmb; + char second_ip_buf[25]; + + strcpy(second_ip_buf, inet_ntoa(packet->ip)); + + nmb->header.opcode = NMB_NAME_MULTIHOMED_REG_OPCODE; + nmb->header.arcount = 1; + + nmb->header.nm_flags.recursion_desired = True; + + if(create_and_init_additional_record(packet, nb_flags, register_ip) == False) + return False; + + DEBUG(4,("initiate_multihomed_name_register_packet: sending registration \ +for name %s IP %s (bcast=%s) to IP %s\n", + namestr(&nmb->additional->rr_name), inet_ntoa(*register_ip), + BOOLSTR(nmb->header.nm_flags.bcast), second_ip_buf )); + + return send_netbios_packet( packet ); +} + +/*************************************************************************** + Sends out a name refresh. +**************************************************************************/ + +static BOOL initiate_name_refresh_packet( struct packet_struct *packet, + uint16 nb_flags, struct in_addr *refresh_ip) +{ + struct nmb_packet *nmb = &packet->packet.nmb; + + nmb->header.opcode = NMB_NAME_REFRESH_OPCODE_8; + nmb->header.arcount = 1; + + nmb->header.nm_flags.recursion_desired = False; + + if(create_and_init_additional_record(packet, nb_flags, refresh_ip) == False) + return False; + + DEBUG(4,("initiate_name_refresh_packet: sending refresh for name %s (bcast=%s) to IP %s\n", + namestr(&nmb->additional->rr_name), + BOOLSTR(nmb->header.nm_flags.bcast), inet_ntoa(packet->ip))); + + return send_netbios_packet( packet ); +} + +/*************************************************************************** + Sends out a name release. +**************************************************************************/ + +static BOOL initiate_name_release_packet( struct packet_struct *packet, + uint16 nb_flags, struct in_addr *release_ip) +{ + struct nmb_packet *nmb = &packet->packet.nmb; + + nmb->header.opcode = NMB_NAME_RELEASE_OPCODE; + nmb->header.arcount = 1; + + nmb->header.nm_flags.recursion_desired = False; + + if(create_and_init_additional_record(packet, nb_flags, release_ip) == False) + return False; + + DEBUG(4,("initiate_name_release_packet: sending release for name %s (bcast=%s) to IP %s\n", + namestr(&nmb->additional->rr_name), + BOOLSTR(nmb->header.nm_flags.bcast), inet_ntoa(packet->ip))); + + return send_netbios_packet( packet ); +} + +/*************************************************************************** + Sends out a node status. +**************************************************************************/ + +static BOOL initiate_node_status_packet( struct packet_struct *packet ) +{ + struct nmb_packet *nmb = &packet->packet.nmb; + + nmb->header.opcode = NMB_NAME_QUERY_OPCODE; + nmb->header.arcount = 0; + + nmb->header.nm_flags.recursion_desired = False; + + nmb->question.question_type = QUESTION_TYPE_NB_STATUS; + + DEBUG(4,("initiate_node_status_packet: sending node status request for name %s to IP %s\n", + namestr(&nmb->question.question_name), + inet_ntoa(packet->ip))); + + return send_netbios_packet( packet ); +} + +/**************************************************************************** + Simplification functions for queuing standard packets. + These should be the only publicly callable functions for sending + out packets. +****************************************************************************/ + +/**************************************************************************** + Assertion - we should never be sending nmbd packets on the remote + broadcast subnet. +****************************************************************************/ + +static BOOL assert_check_subnet(struct subnet_record *subrec) +{ + if( subrec == remote_broadcast_subnet) + { + DEBUG(0,("assert_check_subnet: Attempt to send packet on remote broadcast subnet. \ +This is a bug.\n")); + return True; + } + return False; +} + +/**************************************************************************** + Queue a register name packet to the broadcast address of a subnet. +****************************************************************************/ + +struct response_record *queue_register_name( struct subnet_record *subrec, + response_function resp_fn, + timeout_response_function timeout_fn, + register_name_success_function success_fn, + register_name_fail_function fail_fn, + struct userdata_struct *userdata, + struct nmb_name *nmbname, + uint16 nb_flags) +{ + struct packet_struct *p; + struct response_record *rrec; + BOOL bcast = (subrec == unicast_subnet) ? False : True; + + if(assert_check_subnet(subrec)) + return NULL; + + if(( p = create_and_init_netbios_packet(nmbname, bcast, + subrec->bcast_ip)) == NULL) + return NULL; + + if(initiate_name_register_packet( p, nb_flags, + iface_ip(subrec->bcast_ip)) == False) + { + p->locked = False; + free_packet(p); + return NULL; + } + + if((rrec = make_response_record(subrec, /* subnet record. */ + p, /* packet we sent. */ + resp_fn, /* function to call on response. */ + timeout_fn, /* function to call on timeout. */ + (success_function)success_fn, /* function to call on operation success. */ + (fail_function)fail_fn, /* function to call on operation fail. */ + userdata)) == NULL) + { + p->locked = False; + free_packet(p); + return NULL; + } + + return rrec; +} + +/**************************************************************************** + Queue a multihomed register name packet to the broadcast address of a subnet. +****************************************************************************/ + +struct response_record *queue_register_multihomed_name( struct subnet_record *subrec, + response_function resp_fn, + timeout_response_function timeout_fn, + register_name_success_function success_fn, + register_name_fail_function fail_fn, + struct userdata_struct *userdata, + struct nmb_name *nmbname, + uint16 nb_flags, + struct in_addr register_ip) +{ + struct packet_struct *p; + struct response_record *rrec; + BOOL bcast = False; + BOOL ret; + + /* Sanity check. */ + if(subrec != unicast_subnet) + { + DEBUG(0,("queue_register_multihomed_name: should only be done on \ +unicast subnet. subnet is %s\n.", subrec->subnet_name )); + return NULL; + } + + if(assert_check_subnet(subrec)) + return NULL; + + if(( p = create_and_init_netbios_packet(nmbname, bcast, + subrec->bcast_ip)) == NULL) + return NULL; + + if (nb_flags & NB_GROUP) + ret = initiate_name_register_packet( p, nb_flags, ®ister_ip); + else + ret = initiate_multihomed_name_register_packet( p, nb_flags, ®ister_ip); + + if(ret == False) + { + p->locked = False; + free_packet(p); + return NULL; + } + + if((rrec = make_response_record(subrec, /* subnet record. */ + p, /* packet we sent. */ + resp_fn, /* function to call on response. */ + timeout_fn, /* function to call on timeout. */ + (success_function)success_fn, /* function to call on operation success. */ + (fail_function)fail_fn, /* function to call on operation fail. */ + userdata)) == NULL) + { + p->locked = False; + free_packet(p); + return NULL; + } + + return rrec; +} + +/**************************************************************************** + Queue a release name packet to the broadcast address of a subnet. +****************************************************************************/ + +struct response_record *queue_release_name( struct subnet_record *subrec, + response_function resp_fn, + timeout_response_function timeout_fn, + release_name_success_function success_fn, + release_name_fail_function fail_fn, + struct userdata_struct *userdata, + struct nmb_name *nmbname, + uint16 nb_flags, + struct in_addr release_ip) +{ + BOOL bcast = (subrec == unicast_subnet) ? False : True; + struct packet_struct *p; + struct response_record *rrec; + + if(assert_check_subnet(subrec)) + return NULL; + + if(( p = create_and_init_netbios_packet(nmbname, bcast, + subrec->bcast_ip)) == NULL) + return NULL; + + if(initiate_name_release_packet( p, nb_flags, &release_ip) == False) + { + p->locked = False; + free_packet(p); + return NULL; + } + + if((rrec = make_response_record(subrec, /* subnet record. */ + p, /* packet we sent. */ + resp_fn, /* function to call on response. */ + timeout_fn, /* function to call on timeout. */ + (success_function)success_fn, /* function to call on operation success. */ + (fail_function)fail_fn, /* function to call on operation fail. */ + userdata)) == NULL) + { + p->locked = False; + free_packet(p); + return NULL; + } + + return rrec; +} + +/**************************************************************************** + Queue a refresh name packet to the broadcast address of a subnet. +****************************************************************************/ + +struct response_record *queue_refresh_name( struct subnet_record *subrec, + response_function resp_fn, + timeout_response_function timeout_fn, + refresh_name_success_function success_fn, + refresh_name_fail_function fail_fn, + struct userdata_struct *userdata, + struct name_record *namerec, + struct in_addr refresh_ip) +{ + BOOL bcast = (subrec == unicast_subnet) ? False : True; + struct packet_struct *p; + struct response_record *rrec; + + if(assert_check_subnet(subrec)) + return NULL; + + if(( p = create_and_init_netbios_packet(&namerec->name, bcast, + subrec->bcast_ip)) == NULL) + return NULL; + + if(initiate_name_refresh_packet( p, namerec->nb_flags, &refresh_ip) == False) + { + p->locked = False; + free_packet(p); + return NULL; + } + + if((rrec = make_response_record(subrec, /* subnet record. */ + p, /* packet we sent. */ + resp_fn, /* function to call on response. */ + timeout_fn, /* function to call on timeout. */ + (success_function)success_fn, /* function to call on operation success. */ + (fail_function)fail_fn, /* function to call on operation fail. */ + userdata)) == NULL) + { + p->locked = False; + free_packet(p); + return NULL; + } + + return rrec; +} + +/**************************************************************************** + Queue a query name packet to the broadcast address of a subnet. +****************************************************************************/ + +struct response_record *queue_query_name( struct subnet_record *subrec, + response_function resp_fn, + timeout_response_function timeout_fn, + query_name_success_function success_fn, + query_name_fail_function fail_fn, + struct userdata_struct *userdata, + struct nmb_name *nmbname) +{ + struct packet_struct *p; + struct response_record *rrec; + BOOL bcast = True; + + if ((subrec == unicast_subnet) || (subrec == wins_server_subnet)) + bcast = False; + + if(assert_check_subnet(subrec)) + return NULL; + + if(( p = create_and_init_netbios_packet(nmbname, bcast, + subrec->bcast_ip)) == NULL) + return NULL; + + if(initiate_name_query_packet( p ) == False) + { + p->locked = False; + free_packet(p); + return NULL; + } + + if((rrec = make_response_record(subrec, /* subnet record. */ + p, /* packet we sent. */ + resp_fn, /* function to call on response. */ + timeout_fn, /* function to call on timeout. */ + (success_function)success_fn, /* function to call on operation success. */ + (fail_function)fail_fn, /* function to call on operation fail. */ + userdata)) == NULL) + { + p->locked = False; + free_packet(p); + return NULL; + } + + return rrec; +} + +/**************************************************************************** + Queue a node status packet to a given name and address. +****************************************************************************/ + +struct response_record *queue_node_status( struct subnet_record *subrec, + response_function resp_fn, + timeout_response_function timeout_fn, + node_status_success_function success_fn, + node_status_fail_function fail_fn, + struct userdata_struct *userdata, + struct nmb_name *nmbname, + struct in_addr send_ip) +{ + struct packet_struct *p; + struct response_record *rrec; + BOOL bcast = False; + + /* Sanity check. */ + if(subrec != unicast_subnet) + { + DEBUG(0,("queue_register_multihomed_name: should only be done on \ +unicast subnet. subnet is %s\n.", subrec->subnet_name )); + return NULL; + } + + if(assert_check_subnet(subrec)) + return NULL; + + if(( p = create_and_init_netbios_packet(nmbname, bcast, + send_ip)) == NULL) + return NULL; + + if(initiate_node_status_packet(p) == False) + { + p->locked = False; + free_packet(p); + return NULL; + } + + if((rrec = make_response_record(subrec, /* subnet record. */ + p, /* packet we sent. */ + resp_fn, /* function to call on response. */ + timeout_fn, /* function to call on timeout. */ + (success_function)success_fn, /* function to call on operation success. */ + (fail_function)fail_fn, /* function to call on operation fail. */ + userdata)) == NULL) + { + p->locked = False; + free_packet(p); + return NULL; + } + + return rrec; +} + +/**************************************************************************** + Reply to a netbios name packet. see rfc1002.txt +****************************************************************************/ + +void reply_netbios_packet(struct packet_struct *orig_packet, + int rcode, enum netbios_reply_type_code rcv_code, int opcode, + int ttl, char *data,int len) +{ + struct packet_struct packet; + struct nmb_packet *nmb = NULL; + struct res_rec answers; + struct nmb_packet *orig_nmb = &orig_packet->packet.nmb; + BOOL loopback_this_packet = False; + char *packet_type = "unknown"; + + /* Check if we are sending to or from ourselves. */ + if(ismyip(orig_packet->ip) && (orig_packet->port == global_nmb_port)) + loopback_this_packet = True; + + nmb = &packet.packet.nmb; + + /* Do a partial copy of the packet. We clear the locked flag and + the resource record pointers. */ + packet = *orig_packet; /* Full structure copy. */ + packet.locked = False; + nmb->answers = NULL; + nmb->nsrecs = NULL; + nmb->additional = NULL; + + switch (rcv_code) + { + case NMB_STATUS: + { + packet_type = "nmb_status"; + nmb->header.nm_flags.recursion_desired = False; + nmb->header.nm_flags.recursion_available = False; + break; + } + case NMB_QUERY: + { + packet_type = "nmb_query"; + nmb->header.nm_flags.recursion_desired = True; + nmb->header.nm_flags.recursion_available = True; + break; + } + case NMB_REG: + case NMB_REG_REFRESH: + { + packet_type = "nmb_reg"; + nmb->header.nm_flags.recursion_desired = True; + nmb->header.nm_flags.recursion_available = True; + break; + } + case NMB_REL: + { + packet_type = "nmb_rel"; + nmb->header.nm_flags.recursion_desired = False; + nmb->header.nm_flags.recursion_available = False; + break; + } + case NMB_WAIT_ACK: + { + packet_type = "nmb_wack"; + nmb->header.nm_flags.recursion_desired = False; + nmb->header.nm_flags.recursion_available = False; + break; + } + case WINS_REG: + { + packet_type = "wins_reg"; + nmb->header.nm_flags.recursion_desired = True; + nmb->header.nm_flags.recursion_available = True; + break; + } + case WINS_QUERY: + { + packet_type = "wins_query"; + nmb->header.nm_flags.recursion_desired = True; + nmb->header.nm_flags.recursion_available = True; + break; + } + + default: + { + DEBUG(0,("reply_netbios_packet: Unknown packet type: %s %s to ip %s\n", + packet_type, namestr(&orig_nmb->question.question_name), + inet_ntoa(packet.ip))); + + return; + } + } + + DEBUG(4,("reply_netbios_packet: sending a reply of packet type: %s %s to ip %s \ +for id %hu\n", + packet_type, namestr(&orig_nmb->question.question_name), + inet_ntoa(packet.ip), orig_nmb->header.name_trn_id)); + + nmb->header.name_trn_id = orig_nmb->header.name_trn_id; + nmb->header.opcode = opcode; + nmb->header.response = True; + nmb->header.nm_flags.bcast = False; + nmb->header.nm_flags.trunc = False; + nmb->header.nm_flags.authoritative = True; + + nmb->header.rcode = rcode; + nmb->header.qdcount = 0; + nmb->header.ancount = 1; + nmb->header.nscount = 0; + nmb->header.arcount = 0; + + bzero((char*)&nmb->question,sizeof(nmb->question)); + + nmb->answers = &answers; + bzero((char*)nmb->answers,sizeof(*nmb->answers)); + + nmb->answers->rr_name = orig_nmb->question.question_name; + nmb->answers->rr_type = orig_nmb->question.question_type; + nmb->answers->rr_class = orig_nmb->question.question_class; + nmb->answers->ttl = ttl; + + if (data && len) + { + nmb->answers->rdlength = len; + memcpy(nmb->answers->rdata, data, len); + } + + packet.packet_type = NMB_PACKET; + /* Ensure we send out on the same fd that the original + packet came in on to give the correct source IP address. */ + packet.fd = orig_packet->fd; + packet.timestamp = time(NULL); + + debug_nmb_packet(&packet); + + if(loopback_this_packet) + { + struct packet_struct *lo_packet; + DEBUG(5,("reply_netbios_packet: sending packet to ourselves.\n")); + if((lo_packet = copy_packet(&packet)) == NULL) + return; + queue_packet(lo_packet); + } + else if (!send_packet(&packet)) + { + DEBUG(0,("reply_netbios_packet: send_packet to IP %s port %d failed\n", + inet_ntoa(packet.ip),packet.port)); + } +} + +/******************************************************************* + Queue a packet into a packet queue +******************************************************************/ + +void queue_packet(struct packet_struct *packet) +{ + struct packet_struct *p; + + if (!packet_queue) + { + packet->prev = NULL; + packet->next = NULL; + packet_queue = packet; + return; + } + + /* find the bottom */ + for (p=packet_queue;p->next;p=p->next) + ; + + p->next = packet; + packet->next = NULL; + packet->prev = p; +} + +/**************************************************************************** + Try and find a matching subnet record for a datagram port 138 packet. +****************************************************************************/ + +static struct subnet_record *find_subnet_for_dgram_browse_packet(struct packet_struct *p) +{ + struct subnet_record *subrec; + + /* Go through all the broadcast subnets and see if the mask matches. */ + for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) + { + if(same_net(p->ip, subrec->bcast_ip, subrec->mask_ip)) + return subrec; + } + + /* If the subnet record is the remote announce broadcast subnet, + hack it here to be the first subnet. This is really gross and + is needed due to people turning on port 137/138 broadcast + forwarding on their routers. May fire and brimstone rain + down upon them... + */ + + return FIRST_SUBNET; +} + +/**************************************************************************** +Dispatch a browse frame from port 138 to the correct processing function. +****************************************************************************/ + +void process_browse_packet(struct packet_struct *p, char *buf,int len) +{ + struct dgram_packet *dgram = &p->packet.dgram; + int command = CVAL(buf,0); + struct subnet_record *subrec = find_subnet_for_dgram_browse_packet(p); + + /* Drop the packet if it's a different NetBIOS scope, or + the source is from one of our names. */ + + if (!strequal(dgram->dest_name.scope,scope )) + { + DEBUG(7,("process_browse_packet: Discarding datagram from IP %s. Scope (%s) \ +mismatch with our scope (%s).\n", inet_ntoa(p->ip), dgram->dest_name.scope, scope)); + return; + } + + if (is_myname(dgram->source_name.name)) + { + DEBUG(0,("process_browse_packet: Discarding datagram from IP %s. Source name \ +%s is one of our names !\n", inet_ntoa(p->ip), namestr(&dgram->source_name))); + return; + } + + switch (command) + { + case ANN_HostAnnouncement: + { + debug_browse_data(buf, len); + process_host_announce(subrec, p, buf+1); + break; + } + case ANN_DomainAnnouncement: + { + debug_browse_data(buf, len); + process_workgroup_announce(subrec, p, buf+1); + break; + } + case ANN_LocalMasterAnnouncement: + { + debug_browse_data(buf, len); + process_local_master_announce(subrec, p, buf+1); + break; + } + case ANN_AnnouncementRequest: + { + process_announce_request(subrec, p, buf+1); + break; + } + case ANN_Election: + { + process_election(subrec, p, buf+1); + break; + } + case ANN_GetBackupListReq: + { + debug_browse_data(buf, len); + + /* This is one occasion where we change a subnet that is + given to us. If the packet was sent to WORKGROUP<1b> instead + of WORKGROUP<1d> then it was unicast to us a domain master + browser. Change subrec to unicast. + */ + if(dgram->dest_name.name_type == 0x1b) + subrec = unicast_subnet; + + process_get_backup_list_request(subrec, p, buf+1); + break; + } + case ANN_GetBackupListResp: + { + debug_browse_data(buf, len); + /* We never send ANN_GetBackupListReq so we + should never get these. */ + DEBUG(0,("process_browse_packet: Discarding GetBackupListResponse \ +packet from %s IP %s\n", namestr(&dgram->source_name), inet_ntoa(p->ip))); + break; + } + case ANN_ResetBrowserState: + { + process_reset_browser(subrec, p, buf+1); + break; + } + case ANN_MasterAnnouncement: + { + /* Master browser datagrams must be processed + on the unicast subnet. */ + subrec = unicast_subnet; + + process_master_browser_announce(subrec, p, buf+1); + break; + } + default: + { + DEBUG(0,("process_browse_packet: On subnet %s ignoring browse packet \ +command code %d from %s IP %s to %s\n", + subrec->subnet_name, command, namestr(&dgram->source_name), + inet_ntoa(p->ip), namestr(&dgram->dest_name))); + } + } +} + +/**************************************************************************** + Determine if a packet is for us on port 138. Note that to have any chance of + being efficient we need to drop as many packets as possible at this + stage as subsequent processing is expensive. +****************************************************************************/ + +static BOOL listening(struct packet_struct *p,struct nmb_name *nbname) +{ + struct subnet_record *subrec = NULL; + + for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) + { + if(same_net(p->ip, subrec->bcast_ip, subrec->mask_ip)) + break; + } + + if(subrec == NULL) + subrec = unicast_subnet; + + return (find_name_on_subnet(subrec, nbname, FIND_SELF_NAME) != NULL); +} + +/**************************************************************************** + Process udp 138 datagrams +****************************************************************************/ + +static void process_dgram(struct packet_struct *p) +{ + char *buf; + char *buf2; + int len; + struct dgram_packet *dgram = &p->packet.dgram; + + /* If we aren't listening to the destination name then ignore the packet */ + if (!listening(p,&dgram->dest_name)) + { + DEBUG(5,("process_dgram: ignoring dgram packet sent to name %s from %s\n", + namestr(&dgram->dest_name), inet_ntoa(p->ip))); + return; + } + + if (dgram->header.msg_type != 0x10 && + dgram->header.msg_type != 0x11 && + dgram->header.msg_type != 0x12) + { + /* Don't process error packets etc yet */ + DEBUG(5,("process_dgram: ignoring dgram packet sent to name %s from IP %s as it is \ + an error packet of type %x\n", + namestr(&dgram->dest_name), inet_ntoa(p->ip), dgram->header.msg_type)); + return; + } + + buf = &dgram->data[0]; + buf -= 4; /* XXXX for the pseudo tcp length - + someday I need to get rid of this */ + + if (CVAL(buf,smb_com) != SMBtrans) + return; + + len = SVAL(buf,smb_vwv11); + buf2 = smb_base(buf) + SVAL(buf,smb_vwv12); + + DEBUG(4,("process_dgram: datagram from %s to %s IP %s for %s of type %d len=%d\n", + namestr(&dgram->source_name),namestr(&dgram->dest_name), + inet_ntoa(p->ip), smb_buf(buf),CVAL(buf2,0),len)); + + + if (len <= 0) + return; + + /* Datagram packet received for the browser mailslot */ + if (strequal(smb_buf(buf),BROWSE_MAILSLOT)) + { + process_browse_packet(p,buf2,len); + return; + } + + /* Datagram packet received for the domain logon mailslot */ + if (strequal(smb_buf(buf),NET_LOGON_MAILSLOT)) + { + process_logon_packet(p,buf2,len,NET_LOGON_MAILSLOT); + return; + } + + /* Datagram packet received for the NT domain logon mailslot */ + if (strequal(smb_buf(buf),NT_LOGON_MAILSLOT)) + { + process_logon_packet(p,buf2,len,NT_LOGON_MAILSLOT); + return; + } +} + +/**************************************************************************** + Validate a response nmb packet. +****************************************************************************/ + +BOOL validate_nmb_response_packet( struct nmb_packet *nmb ) +{ + BOOL ignore = False; + + switch (nmb->header.opcode) + { + case NMB_NAME_REG_OPCODE: + case NMB_NAME_REFRESH_OPCODE_8: /* ambiguity in rfc1002 about which is correct. */ + case NMB_NAME_REFRESH_OPCODE_9: /* WinNT uses 8 by default. */ + if (nmb->header.ancount == 0) + { + DEBUG(0,("validate_nmb_response_packet: Bad REG/REFRESH Packet. ")); + ignore = True; + } + break; + + case NMB_NAME_QUERY_OPCODE: + if ((nmb->header.ancount != 0) && (nmb->header.ancount != 1)) + { + DEBUG(0,("validate_nmb_response_packet: Bad QUERY Packet. ")); + ignore = True; + } + break; + case NMB_NAME_RELEASE_OPCODE: + if (nmb->header.ancount == 0) + { + DEBUG(0,("validate_nmb_response_packet: Bad RELEASE Packet. ")); + ignore = True; + } + break; + case NMB_WACK_OPCODE: + /* Check WACK response here. */ + if (nmb->header.ancount != 1) + { + DEBUG(0,("validate_nmb_response_packet: Bad WACK Packet. ")); + ignore = True; + } + break; + default: + DEBUG(0,("validate_nmb_response_packet: Ignoring packet with unknown opcode %d.\n", + nmb->header.opcode)); + return True; + } + + if(ignore) + DEBUG(0,("Ignoring response packet with opcode %d.\n", nmb->header.opcode)); + + return ignore; +} + +/**************************************************************************** + Validate a request nmb packet. +****************************************************************************/ + +BOOL validate_nmb_packet( struct nmb_packet *nmb ) +{ + BOOL ignore = False; + + switch (nmb->header.opcode) + { + case NMB_NAME_REG_OPCODE: + case NMB_NAME_REFRESH_OPCODE_8: /* ambiguity in rfc1002 about which is correct. */ + case NMB_NAME_REFRESH_OPCODE_9: /* WinNT uses 8 by default. */ + case NMB_NAME_MULTIHOMED_REG_OPCODE: + if (nmb->header.qdcount==0 || nmb->header.arcount==0) + { + DEBUG(0,("validate_nmb_packet: Bad REG/REFRESH Packet. ")); + ignore = True; + } + break; + + case NMB_NAME_QUERY_OPCODE: + if ((nmb->header.qdcount == 0) || + ((nmb->question.question_type != QUESTION_TYPE_NB_QUERY) && + (nmb->question.question_type != QUESTION_TYPE_NB_STATUS))) + { + DEBUG(0,("validate_nmb_packet: Bad QUERY Packet. ")); + ignore = True; + } + break; + + case NMB_NAME_RELEASE_OPCODE: + if (nmb->header.qdcount==0 || nmb->header.arcount==0) + { + DEBUG(0,("validate_nmb_packet: Bad RELEASE Packet. ")); + ignore = True; + } + break; + default: + DEBUG(0,("validate_nmb_packet: Ignoring packet with unknown opcode %d.\n", + nmb->header.opcode)); + return True; + } + + if(ignore) + DEBUG(0,("validate_nmb_packet: Ignoring request packet with opcode %d.\n", nmb->header.opcode)); + + return ignore; +} + +/**************************************************************************** + Find a subnet (and potentially a response record) for a packet. +****************************************************************************/ + +static struct subnet_record *find_subnet_for_nmb_packet( struct packet_struct *p, + struct response_record **pprrec) +{ + struct nmb_packet *nmb = &p->packet.nmb; + struct response_record *rrec = NULL; + struct subnet_record *subrec = NULL; + + if(pprrec != NULL) + *pprrec = NULL; + + if(nmb->header.response) + { + /* It's a response packet. Find a record for it or it's an error. */ + + rrec = find_response_record( &subrec, nmb->header.name_trn_id); + if(rrec == NULL) + { + DEBUG(0,("find_subnet_for_nmb_packet: response record not found for response id %hu\n", + nmb->header.name_trn_id)); + return NULL; + } + + if(subrec == NULL) + { + DEBUG(0,("find_subnet_for_nmb_packet: subnet record not found for response id %hu\n", + nmb->header.name_trn_id)); + return NULL; + } + + if(pprrec != NULL) + *pprrec = rrec; + return subrec; + } + + /* Try and see what subnet this packet belongs to. */ + + /* WINS server ? */ + if(packet_is_for_wins_server(p)) + return wins_server_subnet; + + /* If it wasn't a broadcast packet then send to the UNICAST subnet. */ + if(nmb->header.nm_flags.bcast == False) + return unicast_subnet; + + /* Go through all the broadcast subnets and see if the mask matches. */ + for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) + { + if(same_net(p->ip, subrec->bcast_ip, subrec->mask_ip)) + return subrec; + } + + /* If none match it must have been a directed broadcast - assign + the remote_broadcast_subnet. */ + return remote_broadcast_subnet; +} + +/**************************************************************************** + Process a nmb request packet - validate the packet and route it. +****************************************************************************/ + +static void process_nmb_request(struct packet_struct *p) +{ + struct nmb_packet *nmb = &p->packet.nmb; + struct subnet_record *subrec = NULL; + + debug_nmb_packet(p); + + /* Ensure we have a good packet. */ + if(validate_nmb_packet(nmb)) + return; + + /* Allocate a subnet to this packet - if we cannot - fail. */ + if((subrec = find_subnet_for_nmb_packet(p, NULL))==NULL) + return; + + switch (nmb->header.opcode) + { + case NMB_NAME_REG_OPCODE: + if(subrec == wins_server_subnet) + wins_process_name_registration_request(subrec, p); + else + process_name_registration_request(subrec, p); + break; + + case NMB_NAME_REFRESH_OPCODE_8: /* ambiguity in rfc1002 about which is correct. */ + case NMB_NAME_REFRESH_OPCODE_9: + if(subrec == wins_server_subnet) + wins_process_name_refresh_request(subrec, p); + else + process_name_refresh_request(subrec, p); + break; + + case NMB_NAME_MULTIHOMED_REG_OPCODE: + if(subrec == wins_server_subnet) + wins_process_multihomed_name_registration_request(subrec, p); + else + { + DEBUG(0,("process_nmb_request: Multihomed registration request must be \ +directed at a WINS server.\n")); + } + break; + + case NMB_NAME_QUERY_OPCODE: + switch (nmb->question.question_type) + { + case QUESTION_TYPE_NB_QUERY: + { + if(subrec == wins_server_subnet) + wins_process_name_query_request(subrec, p); + else + process_name_query_request(subrec, p); + break; + } + case QUESTION_TYPE_NB_STATUS: + { + if(subrec == wins_server_subnet) + { + DEBUG(0,("process_nmb_request: NB_STATUS request directed at WINS server is \ +not allowed.\n")); + break; + } + else + process_node_status_request(subrec, p); + break; + } + } + break; + + case NMB_NAME_RELEASE_OPCODE: + if(subrec == wins_server_subnet) + wins_process_name_release_request(subrec, p); + else + process_name_release_request(subrec, p); + break; + } +} + +/**************************************************************************** + Process a nmb response packet - validate the packet and route it. + to either the WINS server or a normal response. +****************************************************************************/ + +static void process_nmb_response(struct packet_struct *p) +{ + struct nmb_packet *nmb = &p->packet.nmb; + struct subnet_record *subrec = NULL; + struct response_record *rrec = NULL; + + debug_nmb_packet(p); + + if(validate_nmb_response_packet(nmb)) + return; + + if((subrec = find_subnet_for_nmb_packet(p, &rrec))==NULL) + return; + + if(rrec == NULL) + { + DEBUG(0,("process_nmb_response: response packet received but no response record \ +found for id = %hu. Ignoring packet.\n", nmb->header.name_trn_id)); + return; + } + + /* Increment the number of responses received for this record. */ + rrec->num_msgs++; + /* Ensure we don't re-send the request. */ + rrec->repeat_count = 0; + + /* Call the response received function for this packet. */ + (*rrec->resp_fn)(subrec, rrec, p); +} + + +/******************************************************************* + Run elements off the packet queue till its empty +******************************************************************/ + +void run_packet_queue() +{ + struct packet_struct *p; + + while ((p = packet_queue)) + { + packet_queue = p->next; + if (packet_queue) + packet_queue->prev = NULL; + p->next = p->prev = NULL; + + switch (p->packet_type) + { + case NMB_PACKET: + if(p->packet.nmb.header.response) + process_nmb_response(p); + else + process_nmb_request(p); + break; + + case DGRAM_PACKET: + process_dgram(p); + break; + } + free_packet(p); + } +} + +/******************************************************************* + Retransmit or timeout elements from all the outgoing subnet response + record queues. +******************************************************************/ + +void retransmit_or_expire_response_records(time_t t) +{ + struct subnet_record *subrec; + + for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec)) + { + struct response_record *rrec, *nextrrec; + + for (rrec = subrec->responselist; rrec; rrec = nextrrec) + { + nextrrec = rrec->next; + + if (rrec->repeat_time <= t) + { + if (rrec->repeat_count > 0) + { + /* Resend while we have a non-zero repeat_count. */ + if(!send_packet(rrec->packet)) + { + DEBUG(0,("retransmit_or_expire_response_records: Failed to resend packet id %hu \ +to IP %s on subnet %s\n", rrec->response_id, inet_ntoa(rrec->packet->ip), + subrec->subnet_name)); + } + rrec->repeat_time += rrec->repeat_interval; + rrec->repeat_count--; + } + else + { + DEBUG(4,("retransmit_or_expire_response_records: timeout for packet id %hu to IP %s \ +on subnet %s\n", rrec->response_id, inet_ntoa(rrec->packet->ip), + subrec->subnet_name)); + + /* Call the timeout function. This will deal with removing the + timed out packet. */ + if(rrec->timeout_fn) + (*rrec->timeout_fn)(subrec, rrec); + else + { + /* We must remove the record ourself if there is + no timeout function. */ + remove_response_record(subrec, rrec); + } + } /* rrec->repeat_count > 0 */ + } /* rrec->repeat_time <= t */ + } /* end for rrec */ + } /* end for subnet */ +} + +/**************************************************************************** + Create an fd_set containing all the sockets in the subnet structures, + plus the broadcast sockets. +***************************************************************************/ + +static BOOL create_listen_fdset(fd_set **ppset, int **psock_array, int *listen_number) +{ + int *sock_array = NULL; + struct subnet_record *subrec = NULL; + int count = 0; + int num = 0; + fd_set *pset = (fd_set *)malloc(sizeof(fd_set)); + + if(pset == NULL) + { + DEBUG(0,("create_listen_fdset: malloc fail !\n")); + return True; + } + + /* Check that we can add all the fd's we need. */ + for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) + count++; + + if((count*2) + 2 > FD_SETSIZE) + { + DEBUG(0,("create_listen_fdset: Too many file descriptors needed (%d). We can \ +only use %d.\n", (count*2) + 2, FD_SETSIZE)); + return True; + } + + if((sock_array = (int *)malloc(((count*2) + 2)*sizeof(int))) == NULL) + { + DEBUG(0,("create_listen_fdset: malloc fail for socket array.\n")); + return True; + } + + FD_ZERO(pset); + + /* Add in the broadcast socket on 137. */ + FD_SET(ClientNMB,pset); + sock_array[num++] = ClientNMB; + + /* Add in the 137 sockets on all the interfaces. */ + for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) + { + FD_SET(subrec->nmb_sock,pset); + sock_array[num++] = subrec->nmb_sock; + } + + /* Add in the broadcast socket on 138. */ + FD_SET(ClientDGRAM,pset); + sock_array[num++] = ClientDGRAM; + + /* Add in the 138 sockets on all the interfaces. */ + for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) + { + FD_SET(subrec->dgram_sock,pset); + sock_array[num++] = subrec->dgram_sock; + } + + *listen_number = (count*2) + 2; + *ppset = pset; + *psock_array = sock_array; + + return False; +} + +/**************************************************************************** + Listens for NMB or DGRAM packets, and queues them. +***************************************************************************/ + +BOOL listen_for_packets(BOOL run_election) +{ + static fd_set *listen_set = NULL; + static int listen_number = 0; + static int *sock_array = NULL; + + fd_set fds; + int selrtn; + struct timeval timeout; +#ifndef SYNC_DNS + int dns_fd; +#endif + + if(listen_set == NULL) + { + if(create_listen_fdset(&listen_set, &sock_array, &listen_number)) + { + DEBUG(0,("listen_for_packets: Fatal error. unable to create listen set. Exiting.\n")); + return True; + } + } + + memcpy((char *)&fds, (char *)listen_set, sizeof(fd_set)); + +#ifndef SYNC_DNS + dns_fd = asyncdns_fd(); + if (dns_fd != -1) { + FD_SET(dns_fd, &fds); + } +#endif + + + /* + * During elections and when expecting a netbios response packet we + * need to send election packets at tighter intervals. + * Ideally it needs to be the interval (in ms) between time now and + * the time we are expecting the next netbios packet. + */ + + timeout.tv_sec = (run_election||num_response_packets) ? 1 : NMBD_SELECT_LOOP; + timeout.tv_usec = 0; + + /* We can only take term signals when we are in the select. */ + BlockSignals(False, SIGTERM); + selrtn = sys_select(&fds,&timeout); + BlockSignals(True, SIGTERM); + + if(selrtn > 0) + { + int i; + +#ifndef SYNC_DNS + if (dns_fd != -1 && FD_ISSET(dns_fd,&fds)) { + run_dns_queue(); + } +#endif + + for(i = 0; i < listen_number; i++) + { + if(i < (listen_number/2)) + { + /* Processing a 137 socket. */ + if (FD_ISSET(sock_array[i],&fds)) + { + struct packet_struct *packet = read_packet(sock_array[i], NMB_PACKET); + if (packet) + { + /* + * If we got a packet on the broadcast socket and interfaces + * only is set then check it came from one of our local nets. + */ + if(lp_bind_interfaces_only() && (sock_array[i] == ClientNMB) && + (!is_local_net(packet->ip))) + { + DEBUG(7,("discarding nmb packet sent to broadcast socket from %s:%d\n", + inet_ntoa(packet->ip),packet->port)); + free_packet(packet); + } + else if ((ip_equal(loopback_ip, packet->ip) || + ismyip(packet->ip)) && packet->port == global_nmb_port) + { + DEBUG(7,("discarding own packet from %s:%d\n", + inet_ntoa(packet->ip),packet->port)); + free_packet(packet); + } + else + { + /* Save the file descriptor this packet came in on. */ + packet->fd = sock_array[i]; + queue_packet(packet); + } + } + } + } + else + { + /* Processing a 138 socket. */ + + if (FD_ISSET(sock_array[i],&fds)) + { + struct packet_struct *packet = read_packet(sock_array[i], DGRAM_PACKET); + if (packet) + { + /* + * If we got a packet on the broadcast socket and interfaces + * only is set then check it came from one of our local nets. + */ + if(lp_bind_interfaces_only() && (sock_array[i] == ClientDGRAM) && + (!is_local_net(packet->ip))) + { + DEBUG(7,("discarding dgram packet sent to broadcast socket from %s:%d\n", + inet_ntoa(packet->ip),packet->port)); + free_packet(packet); + } + else if ((ip_equal(loopback_ip, packet->ip) || + ismyip(packet->ip)) && packet->port == DGRAM_PORT) + { + DEBUG(7,("discarding own packet from %s:%d\n", + inet_ntoa(packet->ip),packet->port)); + free_packet(packet); + } + else + { + /* Save the file descriptor this packet came in on. */ + packet->fd = sock_array[i]; + queue_packet(packet); + } + } + } + } /* end processing 138 socket. */ + } /* end for */ + } /* end if selret > 0 */ + return False; +} + +/**************************************************************************** + Construct and send a netbios DGRAM. + Note that this currently sends all packets to port 138. +**************************************************************************/ + +BOOL send_mailslot(BOOL unique, char *mailslot,char *buf,int len, + char *srcname, int src_type, + char *dstname, int dest_type, + struct in_addr dest_ip,struct in_addr src_ip) +{ + BOOL loopback_this_packet = False; + struct packet_struct p; + struct dgram_packet *dgram = &p.packet.dgram; + char *ptr,*p2; + char tmp[4]; + + bzero((char *)&p,sizeof(p)); + + if(ismyip(dest_ip)) + loopback_this_packet = True; + + generate_name_trn_id(); + + /* DIRECT GROUP or UNIQUE datagram. */ + dgram->header.msg_type = unique ? 0x10 : 0x11; + dgram->header.flags.node_type = M_NODE; + dgram->header.flags.first = True; + dgram->header.flags.more = False; + dgram->header.dgm_id = name_trn_id; + dgram->header.source_ip = src_ip; + dgram->header.source_port = DGRAM_PORT; + dgram->header.dgm_length = 0; /* Let build_dgram() handle this. */ + dgram->header.packet_offset = 0; + + make_nmb_name(&dgram->source_name,srcname,0,scope); + make_nmb_name(&dgram->dest_name,dstname,dest_type,scope); + + ptr = &dgram->data[0]; + + /* Setup the smb part. */ + ptr -= 4; /* XXX Ugliness because of handling of tcp SMB length. */ + memcpy(tmp,ptr,4); + set_message(ptr,17,17 + len,True); + memcpy(ptr,tmp,4); + + CVAL(ptr,smb_com) = SMBtrans; + SSVAL(ptr,smb_vwv1,len); + SSVAL(ptr,smb_vwv11,len); + SSVAL(ptr,smb_vwv12,70 + strlen(mailslot)); + SSVAL(ptr,smb_vwv13,3); + SSVAL(ptr,smb_vwv14,1); + SSVAL(ptr,smb_vwv15,1); + SSVAL(ptr,smb_vwv16,2); + p2 = smb_buf(ptr); + strcpy(p2,mailslot); + p2 = skip_string(p2,1); + + memcpy(p2,buf,len); + p2 += len; + + dgram->datasize = PTR_DIFF(p2,ptr+4); /* +4 for tcp length. */ + + p.ip = dest_ip; + p.port = DGRAM_PORT; + p.fd = ClientDGRAM; + p.timestamp = time(NULL); + p.packet_type = DGRAM_PACKET; + + DEBUG(4,("send_mailslot: Sending to mailslot %s from %s IP %s ", mailslot, + namestr(&dgram->source_name), inet_ntoa(src_ip))); + DEBUG(4,("to %s IP %s\n", namestr(&dgram->dest_name), inet_ntoa(dest_ip))); + + debug_browse_data(buf, len); + + if(loopback_this_packet) + { + struct packet_struct *lo_packet = NULL; + DEBUG(5,("send_mailslot: sending packet to ourselves.\n")); + if((lo_packet = copy_packet(&p)) == NULL) + return False; + queue_packet(lo_packet); + return True; + } + else + return(send_packet(&p)); +} diff --git a/source3/nmbd/nmbd_processlogon.c b/source3/nmbd/nmbd_processlogon.c new file mode 100644 index 0000000000..ae917564fe --- /dev/null +++ b/source3/nmbd/nmbd_processlogon.c @@ -0,0 +1,250 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NBT netbios routines and daemon - version 2 + Copyright (C) Andrew Tridgell 1994-1997 + Copyright (C) Luke Kenneth Casson Leighton 1994-1997 + Copyright (C) Jeremy Allison 1994-1997 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + Revision History: + +*/ + +#include "includes.h" + +extern int DEBUGLEVEL; + +extern pstring myname; +extern fstring myworkgroup; + +/**************************************************************************** +Process a domain logon packet +**************************************************************************/ + +void process_logon_packet(struct packet_struct *p,char *buf,int len, + char *mailslot) +{ + struct dgram_packet *dgram = &p->packet.dgram; + pstring my_name; + fstring reply_name; + BOOL add_slashes = False; + pstring outbuf; + int code,reply_code; + char unknown_byte = 0; + uint16 request_count = 0; + uint16 token = 0; + + uint32 ntversion; + uint16 lmnttoken; + uint16 lm20token; + uint32 allowableaccount; /* Control bits, i.e. 0x80 == workstation trust a/c. */ + uint32 domainsidsize; + uint16 requestcount; + char *domainsid; + char *getdc; + char *uniuser; /* Unicode user name. */ + pstring ascuser; + char *unicomp; /* Unicode computer name. */ + struct smb_passwd *smb_pass; /* To check if machine account exists */ + + if (!lp_domain_logons()) + { + DEBUG(3,("process_logon_packet: Logon packet received from IP %S and domain \ +logons are not enabled.\n", inet_ntoa(p->ip) )); + return; + } + + strcpy(my_name, myname); + strupper(my_name); + + code = SVAL(buf,0); + DEBUG(1,("process_logon_packet: Logon from %s: code = %x\n", inet_ntoa(p->ip), code)); + + switch (code) + { + case 0: + { + char *q = buf + 2; + char *machine = q; + char *user = skip_string(machine,1); + + getdc = skip_string(user,1); + q = skip_string(getdc,1); + unknown_byte = CVAL(q,0); + request_count = SVAL(q,1); + token = SVAL(q,3); + + reply_code = 0x6; + strcpy(reply_name,my_name); + add_slashes = True; + + DEBUG(3,("process_logon_packet: Domain login request from %s at IP %s user=%s token=%x\n", + machine,inet_ntoa(p->ip),user,token)); + + q = outbuf; + SSVAL(q, 0, 6); q += 2; + + strcpy(reply_name, "\\\\"); + strcat(reply_name, my_name); + strcpy(q, reply_name); q = skip_string(q, 1); /* PDC name */ + + SSVAL(q, 0, token); q += 2; + + dump_data(4, outbuf, PTR_DIFF(q, outbuf)); + + send_mailslot(True, getdc, + outbuf,PTR_DIFF(q,outbuf), + dgram->dest_name.name, + dgram->dest_name.name_type, + dgram->source_name.name, + dgram->source_name.name_type, + p->ip, *iface_ip(p->ip)); + break; + } + + case QUERYFORPDC: + { + char *q = buf + 2; + char *machine = q; + + getdc = skip_string(machine,1); + unicomp = skip_string(getdc,1); + + q = align2(unicomp, buf); + + q = skip_unicode_string(q, 1); + + ntversion = IVAL(q, 0); q += 4; + lmnttoken = SVAL(q, 0); q += 2; + lm20token = SVAL(q, 0); q += 2; + + /* Construct reply. */ + + q = outbuf; + SSVAL(q, 0, QUERYFORPDC_R); q += 2; + + strcpy(reply_name,my_name); + strcpy(q, reply_name); q = skip_string(q, 1); /* PDC name */ + + if (strcmp(mailslot, NT_LOGON_MAILSLOT)==0) { + q = align2(q, buf); + + PutUniCode(q, my_name); /* PDC name */ + q = skip_unicode_string(q, 1); + PutUniCode(q, myworkgroup); /* Domain name*/ + q = skip_unicode_string(q, 1); + + SIVAL(q, 0, ntversion); q += 4; + SSVAL(q, 0, lmnttoken); q += 2; + SSVAL(q, 0, lm20token); q += 2; + } + + DEBUG(3,("process_logon_packet: GETDC request from %s at IP %s, \ +reporting %s domain %s 0x%x ntversion=%x lm_nt token=%x lm_20 token=%x\n", + machine,inet_ntoa(p->ip), reply_name, lp_workgroup(), + QUERYFORPDC_R, (uint32)ntversion, (uint32)lmnttoken, + (uint32)lm20token )); + + dump_data(4, outbuf, PTR_DIFF(q, outbuf)); + + send_mailslot(True, getdc, + outbuf,PTR_DIFF(q,outbuf), + dgram->dest_name.name, + dgram->dest_name.name_type, + dgram->source_name.name, + dgram->source_name.name_type, + p->ip, *iface_ip(p->ip)); + return; + } + + case SAMLOGON: + { + char *q = buf + 2; + + requestcount = SVAL(q, 0); q += 2; + unicomp = q; + uniuser = skip_unicode_string(unicomp,1); + getdc = skip_unicode_string(uniuser,1); + q = skip_string(getdc,1); + allowableaccount = IVAL(q, 0); q += 4; + domainsidsize = IVAL(q, 0); q += 4; + domainsid = q; + q += domainsidsize + 3; + ntversion = IVAL(q, 0); q += 4; + lmnttoken = SVAL(q, 0); q += 2; + lm20token = SVAL(q, 0); q += 2; + + DEBUG(3,("process_logon_packet: SAMLOGON sidsize %d ntv %d\n", domainsidsize, ntversion)); + + /* + * If MACHINE$ is in our password database then respond, else ignore. + * Let's ignore the SID. + */ + + strcpy(ascuser, unistr(uniuser)); + DEBUG(3,("process_logon_packet: SAMLOGON user %s\n", ascuser)); + + strcpy(reply_name,"\\\\"); /* Here it wants \\LOGONSERVER. */ + strcpy(reply_name+2,my_name); + + smb_pass = get_smbpwd_entry(ascuser, 0); + + if(!smb_pass) + { + DEBUG(3,("process_logon_packet: SAMLOGON request from %s(%s) for %s, not in password file\n", + unistr(unicomp),inet_ntoa(p->ip), ascuser)); + return; + } + else + { + DEBUG(3,("process_logon_packet: SAMLOGON request from %s(%s) for %s, returning logon svr %s domain %s code %x token=%x\n", + unistr(unicomp),inet_ntoa(p->ip), ascuser, reply_name, myworkgroup, + SAMLOGON_R ,lmnttoken)); + } + + /* Construct reply. */ + + q = outbuf; + SSVAL(q, 0, SAMLOGON_R); q += 2; + + PutUniCode(q, reply_name); q = skip_unicode_string(q, 1); + unistrcpy(q, uniuser); q = skip_unicode_string(q, 1); /* User name (workstation trust account) */ + PutUniCode(q, lp_workgroup()); q = skip_unicode_string(q, 1); /* Domain name. */ + + SIVAL(q, 0, ntversion); q += 4; + SSVAL(q, 0, lmnttoken); q += 2; + SSVAL(q, 0, lm20token); q += 2; + + dump_data(4, outbuf, PTR_DIFF(q, outbuf)); + + send_mailslot(True, getdc, + outbuf,PTR_DIFF(q,outbuf), + dgram->dest_name.name, + dgram->dest_name.name_type, + dgram->source_name.name, + dgram->source_name.name_type, + p->ip, *iface_ip(p->ip)); + break; + } + + default: + { + DEBUG(3,("process_logon_packet: Unknown domain request %d\n",code)); + return; + } + } +} diff --git a/source3/nmbd/nmbd_responserecordsdb.c b/source3/nmbd/nmbd_responserecordsdb.c new file mode 100644 index 0000000000..bc0c0745f5 --- /dev/null +++ b/source3/nmbd/nmbd_responserecordsdb.c @@ -0,0 +1,238 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NBT netbios library routines + Copyright (C) Andrew Tridgell 1994-1997 + Copyright (C) Luke Kenneth Casson Leighton 1994-1997 + Copyright (C) Jeremy Allison 1994-1997 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "includes.h" + +extern int ClientNMB; + +extern int DEBUGLEVEL; + +extern pstring scope; +extern pstring myname; +extern struct in_addr ipzero; + +int num_response_packets = 0; + +/*************************************************************************** + Add an expected response record into the list + **************************************************************************/ + +void add_response_record(struct subnet_record *subrec, + struct response_record *rrec) +{ + struct response_record *rrec2; + + num_response_packets++; /* count of total number of packets still around */ + + DEBUG(4,("add_response_record: adding response record id:%hu to subnet %s. num_records:%d\n", + rrec->response_id, subrec->subnet_name, num_response_packets)); + + if (!subrec->responselist) + { + subrec->responselist = rrec; + rrec->prev = NULL; + rrec->next = NULL; + return; + } + + for (rrec2 = subrec->responselist; rrec2->next; rrec2 = rrec2->next) + ; + + rrec2->next = rrec; + rrec->next = NULL; + rrec->prev = rrec2; +} + +/*************************************************************************** + Remove an expected response record from the list + **************************************************************************/ + +void remove_response_record(struct subnet_record *subrec, + struct response_record *rrec) +{ + if (rrec->prev) + rrec->prev->next = rrec->next; + if (rrec->next) + rrec->next->prev = rrec->prev; + + if (subrec->responselist == rrec) + subrec->responselist = rrec->next; + + if(rrec->userdata) + { + if(rrec->userdata->free_fn) + (*rrec->userdata->free_fn)(rrec->userdata); + else + free((char *)rrec->userdata); + } + + /* Ensure we can delete. */ + rrec->packet->locked = False; + free_packet(rrec->packet); + + free((char *)rrec); + + num_response_packets--; /* count of total number of packets still around */ +} + +/**************************************************************************** + Create a response record for an outgoing packet. + **************************************************************************/ + +struct response_record *make_response_record( struct subnet_record *subrec, + struct packet_struct *p, + response_function resp_fn, + timeout_response_function timeout_fn, + success_function success_fn, + fail_function fail_fn, + struct userdata_struct *userdata) +{ + struct response_record *rrec; + struct nmb_packet *nmb = &p->packet.nmb; + + if (!(rrec = (struct response_record *)malloc(sizeof(*rrec)))) + { + DEBUG(0,("make_response_queue_record: malloc fail for response_record.\n")); + return NULL; + } + + bzero((char *)rrec, sizeof(*rrec)); + + rrec->response_id = nmb->header.name_trn_id; + + rrec->resp_fn = resp_fn; + rrec->timeout_fn = timeout_fn; + rrec->success_fn = success_fn; + rrec->fail_fn = fail_fn; + + rrec->packet = p; + + if(userdata) + { + /* Intelligent userdata. */ + if(userdata->copy_fn) + { + if((rrec->userdata = (*userdata->copy_fn)(userdata)) == NULL) + { + DEBUG(0,("make_response_queue_record: copy fail for userdata.\n")); + free(rrec); + return NULL; + } + } + else + { + /* Primitive userdata, do a memcpy. */ + if((rrec->userdata = (struct userdata_struct *) + malloc(sizeof(struct userdata_struct)+userdata->userdata_len)) == NULL) + { + DEBUG(0,("make_response_queue_record: malloc fail for userdata.\n")); + free(rrec); + return NULL; + } + rrec->userdata->copy_fn = userdata->copy_fn; + rrec->userdata->free_fn = userdata->free_fn; + rrec->userdata->userdata_len = userdata->userdata_len; + memcpy(rrec->userdata->data, userdata->data, userdata->userdata_len); + } + } + else + rrec->userdata = NULL; + + rrec->num_msgs = 0; + + if(!nmb->header.nm_flags.bcast) + rrec->repeat_interval = 5; /* 5 seconds for unicast packets. */ + else + rrec->repeat_interval = 1; /* XXXX should be in ms */ + rrec->repeat_count = 3; /* 3 retries */ + rrec->repeat_time = time(NULL) + rrec->repeat_interval; /* initial retry time */ + + /* Lock the packet so we won't lose it while it's on the list. */ + p->locked = True; + + add_response_record(subrec, rrec); + + return rrec; +} + +/**************************************************************************** + Find a response in a subnet's name query response list. + **************************************************************************/ + +static struct response_record *find_response_record_on_subnet( + struct subnet_record *subrec, uint16 id) +{ + struct response_record *rrec = NULL; + + for (rrec = subrec->responselist; rrec; rrec = rrec->next) + { + if (rrec->response_id == id) + { + DEBUG(4, ("find_response_record: found response record id = %hu on subnet %s\n", + id, subrec->subnet_name)); + break; + } + } + return rrec; +} + +/**************************************************************************** + Find a response in any subnet's name query response list. + **************************************************************************/ + +struct response_record *find_response_record(struct subnet_record **ppsubrec, + uint16 id) +{ + struct response_record *rrec = NULL; + + for ((*ppsubrec) = FIRST_SUBNET; (*ppsubrec); + (*ppsubrec) = NEXT_SUBNET_INCLUDING_UNICAST(*ppsubrec)) + { + if((rrec = find_response_record_on_subnet(*ppsubrec, id)) != NULL) + return rrec; + } + + /* There should never be response records on the remote_broadcast subnet. + Sanity check to ensure this is so. */ + if(remote_broadcast_subnet->responselist != NULL) + { + DEBUG(0,("find_response_record: response record found on subnet %s. This should \ +never happen !\n", remote_broadcast_subnet->subnet_name)); + } + + /* Now check the WINS server subnet if it exists. */ + if(wins_server_subnet != NULL) + { + *ppsubrec = wins_server_subnet; + if((rrec = find_response_record_on_subnet(*ppsubrec, id))!= NULL) + return rrec; + } + + DEBUG(0,("find_response_record: repsonse packet id %hu received with no \ +matching record.\n", id)); + + *ppsubrec = NULL; + + return NULL; +} diff --git a/source3/nmbd/nmbd_sendannounce.c b/source3/nmbd/nmbd_sendannounce.c new file mode 100644 index 0000000000..e4b288aea5 --- /dev/null +++ b/source3/nmbd/nmbd_sendannounce.c @@ -0,0 +1,477 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NBT netbios routines and daemon - version 2 + Copyright (C) Andrew Tridgell 1994-1997 + Copyright (C) Luke Kenneth Casson Leighton 1994-1997 + Copyright (C) Jeremy Allison 1994-1997 + + SMB Version handling + Copyright (C) John H Terpstra 1995-1997 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "includes.h" + +extern int DEBUGLEVEL; +extern pstring myname; +extern fstring myworkgroup; +extern char **my_netbios_names; +extern int updatecount; + +/**************************************************************************** + Send a browser reset packet. +**************************************************************************/ + +void send_browser_reset(int reset_type, char *to_name, int to_type, struct in_addr to_ip) +{ + pstring outbuf; + char *p; + + DEBUG(3,("send_browser_reset: sending reset request type %d to %s<%02x> IP %s.\n", + reset_type, to_name, to_type, inet_ntoa(to_ip) )); + + bzero(outbuf,sizeof(outbuf)); + p = outbuf; + CVAL(p,0) = ANN_ResetBrowserState; + p++; + CVAL(p,0) = reset_type; + p++; + + send_mailslot(True, BROWSE_MAILSLOT, outbuf,PTR_DIFF(p,outbuf), + myname, 0x0, to_name, to_type, to_ip, FIRST_SUBNET->myip); +} + +/**************************************************************************** + Broadcast a packet to the local net requesting that all servers in this + workgroup announce themselves to us. + **************************************************************************/ + +void broadcast_announce_request(struct subnet_record *subrec, struct work_record *work) +{ + pstring outbuf; + char *p; + + work->needannounce = True; + + DEBUG(3,("broadcast_announce_request: sending announce request for workgroup %s \ +to subnet %s\n", work->work_group, subrec->subnet_name)); + + bzero(outbuf,sizeof(outbuf)); + p = outbuf; + CVAL(p,0) = ANN_AnnouncementRequest; + p++; + + CVAL(p,0) = work->token; /* (local) Unique workgroup token id. */ + p++; + StrnCpy(p,myname,15); + strupper(p); + p = skip_string(p,1); + + send_mailslot(False, BROWSE_MAILSLOT, outbuf,PTR_DIFF(p,outbuf), + myname, 0x0, work->work_group,0x1e, subrec->bcast_ip, subrec->myip); +} + +/**************************************************************************** + Broadcast an announcement. + **************************************************************************/ + +static void send_announcement(struct subnet_record *subrec, int announce_type, + char *from_name, char *to_name, int to_type, struct in_addr to_ip, + time_t announce_interval, + char *server_name, int server_type, char *server_comment) +{ + pstring outbuf; + char *p; + + bzero(outbuf,sizeof(outbuf)); + p = outbuf+1; + + CVAL(outbuf,0) = announce_type; + + /* Announcement parameters. */ + CVAL(p,0) = updatecount; + SIVAL(p,1,announce_interval*1000); /* Milliseconds - despite the spec. */ + + StrnCpy(p+5,server_name,15); + strupper(p+5); + + CVAL(p,21) = lp_major_announce_version(); /* Major version. */ + CVAL(p,22) = lp_minor_announce_version(); /* Minor version. */ + + SIVAL(p,23,server_type & ~SV_TYPE_LOCAL_LIST_ONLY); + /* Browse version: got from NT/AS 4.00 - Value defined in smb.h (JHT). */ + SSVAL(p,27,BROWSER_ELECTION_VERSION); + SSVAL(p,29,BROWSER_CONSTANT); /* Browse signature. */ + + pstrcpy(p+31,server_comment); + p += 31; + p = skip_string(p,1); + + send_mailslot(False,BROWSE_MAILSLOT, outbuf, PTR_DIFF(p,outbuf), + from_name, 0x0, to_name, to_type, to_ip, subrec->myip); +} + +/**************************************************************************** + We are a local master browser. Announce this to WORKGROUP<1e>. +****************************************************************************/ + +static void send_local_master_announcement(struct subnet_record *subrec, struct work_record *work, + struct server_record *servrec) +{ + /* Ensure we don't have the prohibited bit set. */ + uint32 type = servrec->serv.type & ~SV_TYPE_LOCAL_LIST_ONLY; + + DEBUG(3,("send_local_master_announcement: type %x for name %s on subnet %s for workgroup %s\n", + type, myname, subrec->subnet_name, work->work_group)); + + send_announcement(subrec, ANN_LocalMasterAnnouncement, + myname, /* From nbt name. */ + work->work_group, 0x1e, /* To nbt name. */ + subrec->bcast_ip, /* To ip. */ + work->announce_interval, /* Time until next announce. */ + myname, /* Name to announce. */ + type, /* Type field. */ + servrec->serv.comment); +} + +/**************************************************************************** + Announce the workgroup WORKGROUP to MSBROWSE<01>. +****************************************************************************/ + +static void send_workgroup_announcement(struct subnet_record *subrec, struct work_record *work) +{ + DEBUG(3,("send_workgroup_announcement: on subnet %s for workgroup %s\n", + subrec->subnet_name, work->work_group)); + + send_announcement(subrec, ANN_DomainAnnouncement, + myname, /* From nbt name. */ + MSBROWSE, 0x1, /* To nbt name. */ + subrec->bcast_ip, /* To ip. */ + work->announce_interval, /* Time until next announce. */ + work->work_group, /* Name to announce. */ + SV_TYPE_DOMAIN_ENUM|SV_TYPE_NT, /* workgroup announce flags. */ + myname); /* From name as comment. */ +} + +/**************************************************************************** + Announce the given host to WORKGROUP<1d>. +****************************************************************************/ + +static void send_host_announcement(struct subnet_record *subrec, struct work_record *work, + struct server_record *servrec) +{ + /* Ensure we don't have the prohibited bits set. */ + uint32 type = servrec->serv.type & ~SV_TYPE_LOCAL_LIST_ONLY; + + DEBUG(3,("send_host_announcement: type %x for host %s on subnet %s for workgroup %s\n", + type, servrec->serv.name, subrec->subnet_name, work->work_group)); + + send_announcement(subrec, ANN_HostAnnouncement, + servrec->serv.name, /* From nbt name. */ + work->work_group, 0x1d, /* To nbt name. */ + subrec->bcast_ip, /* To ip. */ + work->announce_interval, /* Time until next announce. */ + servrec->serv.name, /* Name to announce. */ + type, /* Type field. */ + servrec->serv.comment); +} + +/**************************************************************************** + Announce a server record. + ****************************************************************************/ + +static void announce_server(struct subnet_record *subrec, struct work_record *work, + struct server_record *servrec) +{ + /* Only do domain announcements if we are a master and it's + our primary name we're being asked to announce. */ + + if (AM_LOCAL_MASTER_BROWSER(work) && strequal(myname,servrec->serv.name)) + { + send_local_master_announcement(subrec, work, servrec); + send_workgroup_announcement(subrec, work); + } + else + { + send_host_announcement(subrec, work, servrec); + } +} + +/**************************************************************************** + Go through all my registered names on all broadcast subnets and announce + them if the timeout requires it. + **************************************************************************/ + +void announce_my_server_names(time_t t) +{ + struct subnet_record *subrec; + + for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) + { + struct work_record *work = find_workgroup_on_subnet(subrec, myworkgroup); + + if(work) + { + struct server_record *servrec; + + if (work->needannounce) + { + /* Drop back to a max 3 minute announce. This is to prevent a + single lost packet from breaking things for too long. */ + + work->announce_interval = MIN(work->announce_interval, + CHECK_TIME_MIN_HOST_ANNCE*60); + work->lastannounce_time = t - (work->announce_interval+1); + work->needannounce = False; + } + + /* Announce every minute at first then progress to every 12 mins */ + if ((t - work->lastannounce_time) < work->announce_interval) + continue; + + if (work->announce_interval < (CHECK_TIME_MAX_HOST_ANNCE * 60)) + work->announce_interval += 60; + + work->lastannounce_time = t; + + for (servrec = work->serverlist; servrec; servrec = servrec->next) + { + if (is_myname(servrec->serv.name)) + announce_server(subrec, work, servrec); + } + } /* if work */ + } /* for subrec */ +} + +/* Announce timer. Moved into global static so it can be reset + when a machine becomes a local master browser. */ +static time_t announce_timer_last=0; + +/**************************************************************************** + Reset the announce_timer so that a local master browser announce will be done + immediately. + ****************************************************************************/ + +void reset_announce_timer() +{ + announce_timer_last = time(NULL) - (CHECK_TIME_MST_ANNOUNCE * 60); +} + +/**************************************************************************** + Announce myself as a local master browser to a domain master browser. + **************************************************************************/ + +void announce_myself_to_domain_master_browser(time_t t) +{ + struct subnet_record *subrec; + struct work_record *work; + + if(!we_are_a_wins_client()) + { + DEBUG(10,("announce_myself_to_domain_master_browser: no unicast subnet, ignoring.\n")); + return; + } + + if (!announce_timer_last) + announce_timer_last = t; + + if ((t-announce_timer_last) < (CHECK_TIME_MST_ANNOUNCE * 60)) + { + DEBUG(10,("announce_myself_to_domain_master_browser: t (%d) - last(%d) < %d\n", + t, announce_timer_last, CHECK_TIME_MST_ANNOUNCE * 60 )); + return; + } + + announce_timer_last = t; + + /* Look over all our broadcast subnets to see if any of them + has the state set as local master browser. */ + + for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) + { + for (work = subrec->workgrouplist; work; work = work->next) + { + if (AM_LOCAL_MASTER_BROWSER(work)) + { + DEBUG(4,( "announce_myself_to_domain_master_browser: I am a local master browser for \ +workgroup %s on subnet %s\n", work->work_group, subrec->subnet_name)); + + /* Look in nmbd_browsersync.c for the rest of this code. */ + announce_and_sync_with_domain_master_browser(subrec, work); + } + } + } +} + +/**************************************************************************** +Announce all samba's server entries as 'gone'. +This must *only* be called on shutdown. +****************************************************************************/ + +void announce_my_servers_removed(void) +{ + struct subnet_record *subrec; + for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) + { + struct work_record *work; + for (work = subrec->workgrouplist; work; work = work->next) + { + struct server_record *servrec; + + work->announce_interval = 0; + for (servrec = work->serverlist; servrec; servrec = servrec->next) + { + if (!is_myname(servrec->serv.name)) + continue; + servrec->serv.type = 0; + if(AM_LOCAL_MASTER_BROWSER(work)) + send_local_master_announcement(subrec, work, servrec); + send_host_announcement(subrec, work, servrec); + } + } + } +} + +/**************************************************************************** + Do all the "remote" announcements. These are used to put ourselves + on a remote browse list. They are done blind, no checking is done to + see if there is actually a local master browser at the other end. + **************************************************************************/ + +void announce_remote(time_t t) +{ + char *s,*ptr; + static time_t last_time = 0; + pstring s2; + struct in_addr addr; + char *comment; + int stype = lp_default_server_announce(); + + if (last_time && (t < (last_time + REMOTE_ANNOUNCE_INTERVAL))) + return; + + last_time = t; + + s = lp_remote_announce(); + if (!*s) + return; + + comment = lp_serverstring(); + + for (ptr=s; next_token(&ptr,s2,NULL); ) + { + /* The entries are of the form a.b.c.d/WORKGROUP with + WORKGROUP being optional */ + char *wgroup; + int i; + + wgroup = strchr(s2,'/'); + if (wgroup) + *wgroup++ = 0; + if (!wgroup || !*wgroup) + wgroup = myworkgroup; + + addr = *interpret_addr2(s2); + + /* Announce all our names including aliases */ + /* Give the ip address as the address of our first + broadcast subnet. */ + + for(i=0; my_netbios_names[i]; i++) + { + char *name = my_netbios_names[i]; + + DEBUG(5,("announce_remote: Doing remote announce for server %s to IP %s.\n", + name, inet_ntoa(addr) )); + + send_announcement(FIRST_SUBNET, ANN_HostAnnouncement, + name, /* From nbt name. */ + wgroup, 0x1e, /* To nbt name. */ + addr, /* To ip. */ + REMOTE_ANNOUNCE_INTERVAL, /* Time until next announce. */ + name, /* Name to announce. */ + stype, /* Type field. */ + comment); + } + } +} + +/**************************************************************************** + Implement the 'remote browse sync' feature Andrew added. + These are used to put our browse lists into remote browse lists. + **************************************************************************/ + +void browse_sync_remote(time_t t) +{ + char *s,*ptr; + static time_t last_time = 0; + pstring s2; + struct in_addr addr; + struct work_record *work; + pstring outbuf; + char *p; + + if (last_time && (t < (last_time + REMOTE_ANNOUNCE_INTERVAL))) + return; + + last_time = t; + + s = lp_remote_browse_sync(); + if (!*s) + return; + + /* + * We only do this if we are the local master browser + * for our workgroup on the firsst subnet. + */ + + if((work = find_workgroup_on_subnet(FIRST_SUBNET, myworkgroup)) == NULL) + { + DEBUG(0,("browse_sync_remote: Cannot find workgroup %s on subnet %s\n", + myworkgroup, FIRST_SUBNET->subnet_name )); + return; + } + + if(!AM_LOCAL_MASTER_BROWSER(work)) + { + DEBUG(5,("browse_sync_remote: We can only do this if we are a local master browser \ +for workgroup %s on subnet %s.\n", myworkgroup, FIRST_SUBNET->subnet_name )); + return; + } + + bzero(outbuf,sizeof(outbuf)); + p = outbuf; + CVAL(p,0) = ANN_MasterAnnouncement; + p++; + + StrnCpy(p,myname,15); + strupper(p); + p = skip_string(p,1); + + for (ptr=s; next_token(&ptr,s2,NULL); ) + { + /* The entries are of the form a.b.c.d */ + addr = *interpret_addr2(s2); + + DEBUG(5,("announce_remote: Doing remote browse sync announce for server %s to IP %s.\n", + myname, inet_ntoa(addr) )); + + send_mailslot(True, BROWSE_MAILSLOT, outbuf,PTR_DIFF(p,outbuf), + myname, 0x0, "*", 0x0, addr, FIRST_SUBNET->myip); + } +} diff --git a/source3/nmbd/nmbd_serverlistdb.c b/source3/nmbd/nmbd_serverlistdb.c new file mode 100644 index 0000000000..1281fe2ee3 --- /dev/null +++ b/source3/nmbd/nmbd_serverlistdb.c @@ -0,0 +1,454 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NBT netbios routines and daemon - version 2 + Copyright (C) Andrew Tridgell 1994-1997 + Copyright (C) Luke Kenneth Casson Leighton 1994-1997 + Copyright (C) Jeremy Allison 1994-1997 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "includes.h" +#include "smb.h" + +extern int ClientNMB; + +extern int DEBUGLEVEL; + +extern pstring myname; +extern fstring myworkgroup; +extern char **my_netbios_names; + +int updatecount = 0; + +/******************************************************************* + Remove all the servers in a work group. + ******************************************************************/ + +void remove_all_servers(struct work_record *work) +{ + struct server_record *servrec; + struct server_record *nexts; + + for (servrec = work->serverlist; servrec; servrec = nexts) + { + DEBUG(7,("remove_all_servers: Removing server %s\n",servrec->serv.name)); + nexts = servrec->next; + + if (servrec->prev) + servrec->prev->next = servrec->next; + if (servrec->next) + servrec->next->prev = servrec->prev; + + if (work->serverlist == servrec) + work->serverlist = servrec->next; + + free((char *)servrec); + + } + + work->subnet->work_changed = True; +} + +/*************************************************************************** + Add a server into the a workgroup serverlist. + **************************************************************************/ + +static void add_server_to_workgroup(struct work_record *work, + struct server_record *servrec) +{ + struct server_record *servrec2; + + if (!work->serverlist) + { + work->serverlist = servrec; + servrec->prev = NULL; + servrec->next = NULL; + return; + } + + for (servrec2 = work->serverlist; servrec2->next; servrec2 = servrec2->next) + ; + + servrec2->next = servrec; + servrec->next = NULL; + servrec->prev = servrec2; + work->subnet->work_changed = True; +} + +/**************************************************************************** + Find a server in a server list. + **************************************************************************/ + +struct server_record *find_server_in_workgroup(struct work_record *work, char *name) +{ + struct server_record *ret; + + for (ret = work->serverlist; ret; ret = ret->next) + { + if (strequal(ret->serv.name,name)) + return ret; + } + return NULL; +} + + +/**************************************************************************** + Remove a server entry from this workgroup. + ****************************************************************************/ + +static void remove_server_from_workgroup(struct work_record *work, struct server_record *servrec) +{ + if (servrec->prev) + servrec->prev->next = servrec->next; + if (servrec->next) + servrec->next->prev = servrec->prev; + + if (work->serverlist == servrec) + work->serverlist = servrec->next; + + free((char *)servrec); + work->subnet->work_changed = True; +} + +/**************************************************************************** + Create a server entry on this workgroup. + ****************************************************************************/ + +struct server_record *create_server_on_workgroup(struct work_record *work, + char *name,int servertype, + int ttl,char *comment) +{ + struct server_record *servrec; + + if (name[0] == '*') + { + DEBUG(7,("create_server_on_workgroup: not adding name starting with '*' (%s)\n", + name)); + return (NULL); + } + + if((servrec = find_server_in_workgroup(work, name)) != NULL) + { + DEBUG(0,("create_server_on_workgroup: Server %s already exists on \ +workgroup %s. This is a bug.\n", name, work->work_group)); + return NULL; + } + + if((servrec = (struct server_record *)malloc(sizeof(*servrec))) == NULL) + { + DEBUG(0,("create_server_entry_on_workgroup: malloc fail !\n")); + return NULL; + } + + bzero((char *)servrec,sizeof(*servrec)); + + servrec->subnet = work->subnet; + + StrnCpy(servrec->serv.name,name,sizeof(servrec->serv.name)-1); + StrnCpy(servrec->serv.comment,comment,sizeof(servrec->serv.comment)-1); + strupper(servrec->serv.name); + servrec->serv.type = servertype; + + update_server_ttl(servrec, ttl); + + add_server_to_workgroup(work, servrec); + + DEBUG(3,("create_server_on_workgroup: Created server entry %s of type %x (%s) on \ +workgroup %s.\n", name,servertype,comment, work->work_group)); + + work->subnet->work_changed = True; + + return(servrec); +} + +/******************************************************************* + Update the ttl field of a server record. +*******************************************************************/ + +void update_server_ttl(struct server_record *servrec, int ttl) +{ + if(ttl > lp_max_ttl()) + ttl = lp_max_ttl(); + + if(is_myname(servrec->serv.name)) + servrec->death_time = PERMANENT_TTL; + else + servrec->death_time = (ttl != PERMANENT_TTL) ? time(NULL)+(ttl*3) : PERMANENT_TTL; + + servrec->subnet->work_changed = True; +} + +/******************************************************************* + Expire old servers in the serverlist. A time of -1 indicates + everybody dies except those with a death_time of PERMANENT_TTL (which is 0). + This should only be called from expire_workgroups_and_servers(). + ******************************************************************/ + +void expire_servers(struct work_record *work, time_t t) +{ + struct server_record *servrec; + struct server_record *nexts; + + for (servrec = work->serverlist; servrec; servrec = nexts) + { + nexts = servrec->next; + + if ((servrec->death_time != PERMANENT_TTL) && ((t == -1) || (servrec->death_time < t))) + { + DEBUG(3,("expire_old_servers: Removing timed out server %s\n",servrec->serv.name)); + remove_server_from_workgroup(work, servrec); + work->subnet->work_changed = True; + } + } +} + +/******************************************************************* + Decide if we should write out a server record for this server. + We return zero if we should not. Check if we've already written + out this server record from an earlier subnet. +******************************************************************/ + +static uint32 write_this_server_name( struct subnet_record *subrec, + struct work_record *work, + struct server_record *servrec) +{ + struct subnet_record *ssub; + struct work_record *iwork; + struct server_record *sserv; + + /* Go through all the subnets we have already seen. */ + for (ssub = FIRST_SUBNET; ssub != subrec; ssub = NEXT_SUBNET_INCLUDING_UNICAST(ssub)) + { + for(iwork = ssub->workgrouplist; iwork; iwork = iwork->next) + { + if((sserv = find_server_in_workgroup( iwork, servrec->serv.name)) != NULL) + { + /* + * We have already written out this server record, don't + * do it again. This gives precedence to servers we have seen + * on the broadcast subnets over servers that may have been + * added via a sync on the unicast_subet. + * + * The correct way to do this is to have a serverlist file + * per subnet - this means changes to smbd as well. I may + * add this at a later date (JRA). + */ + + return 0; + } + } + } + + return servrec->serv.type; +} + +/******************************************************************* + Decide if we should write out a workgroup record for this workgroup. + We return zero if we should not. Don't write out myworkgroup (we've + already done it) and also don't write out a second workgroup record + on the unicast subnet that we've already written out on one of the + broadcast subnets. +******************************************************************/ + +static uint32 write_this_workgroup_name( struct subnet_record *subrec, + struct work_record *work) +{ + struct subnet_record *ssub; + + if(strequal(myworkgroup, work->work_group)) + return 0; + + /* This is a workgroup we have seen on a broadcast subnet. All + these have the same type. */ + + if(subrec != unicast_subnet) + return (SV_TYPE_DOMAIN_ENUM|SV_TYPE_NT|SV_TYPE_LOCAL_LIST_ONLY); + + for(ssub = FIRST_SUBNET; ssub; ssub = NEXT_SUBNET_EXCLUDING_UNICAST(ssub)) + { + /* This is the unicast subnet so check if we've already written out + this subnet when we passed over the broadcast subnets. */ + + if(find_workgroup_on_subnet( ssub, work->work_group) != NULL) + return 0; + } + + /* All workgroups on the unicast subnet (except our own, which we + have already written out) cannot be local. */ + + return (SV_TYPE_DOMAIN_ENUM|SV_TYPE_NT); +} + +/******************************************************************* + Write out the browse.dat file. + ******************************************************************/ + +void write_browse_list(time_t t, BOOL force_write) +{ + struct subnet_record *subrec; + struct work_record *work; + struct server_record *servrec; + pstring fname,fnamenew; + uint32 stype; + fstring tmp; + int i; + FILE *fp; + BOOL list_changed = force_write; + static time_t lasttime = 0; + + if (!lasttime) + lasttime = t; + if (t - lasttime < 5) + return; + + for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec)) + { + if(subrec->work_changed) + { + list_changed = True; + break; + } + } + + if(!list_changed) + return; + + lasttime = t; + updatecount++; + + dump_workgroups(); + + pstrcpy(fname,lp_lockdir()); + trim_string(fname,NULL,"/"); + strcat(fname,"/"); + strcat(fname,SERVER_LIST); + pstrcpy(fnamenew,fname); + strcat(fnamenew,"."); + + fp = fopen(fnamenew,"w"); + + if (!fp) + { + DEBUG(0,("write_browse_list: Can't open file %s. Error was %s\n", + fnamenew,strerror(errno))); + return; + } + + /* + * Write out a record for our workgroup. Use the record from the first + * subnet. + */ + + if((work = find_workgroup_on_subnet(FIRST_SUBNET, myworkgroup)) == NULL) + { + DEBUG(0,("write_browse_list: Fatal error - cannot find my workgroup %s\n", + myworkgroup)); + fclose(fp); + return; + } + + sprintf(tmp, "\"%s\"", work->work_group); + fprintf(fp, "%-25s ", tmp); + fprintf(fp, "%08x ", SV_TYPE_DOMAIN_ENUM|SV_TYPE_NT|SV_TYPE_LOCAL_LIST_ONLY); + sprintf(tmp, "\"%s\" ", *work->local_master_browser_name ? work->local_master_browser_name : "Unknown"); + fprintf(fp, "%-30s", tmp); + fprintf(fp, "\"%s\"\n", work->work_group); + + /* + * We need to do something special for our own names. + * This is due to the fact that we may be a local master browser on + * one of our broadcast subnets, and a domain master on the unicast + * subnet. We iterate over the subnets and only write out the name + * once. + */ + + for (i=0; my_netbios_names[i]; i++) + { + stype = 0; + for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec)) + { + if((work = find_workgroup_on_subnet( subrec, myworkgroup )) == NULL) + continue; + if((servrec = find_server_in_workgroup( work, my_netbios_names[i])) == NULL) + continue; + + stype |= servrec->serv.type; + } + + /* Output server details, plus what workgroup they're in. */ + sprintf(tmp, "\"%s\"", my_netbios_names[i]); + fprintf(fp, "%-25s ", tmp); + fprintf(fp, "%08x ", stype); + sprintf(tmp, "\"%s\" ", lp_serverstring()); + fprintf(fp, "%-30s", tmp); + fprintf(fp, "\"%s\"\n", myworkgroup); + } + + for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec)) + { + subrec->work_changed = False; + + for (work = subrec->workgrouplist; work ; work = work->next) + { + /* Write out a workgroup record for a workgroup. */ + uint32 wg_type = write_this_workgroup_name( subrec, work); + + if(wg_type) + { + sprintf(tmp, "\"%s\"", work->work_group); + fprintf(fp, "%-25s ", tmp); + + fprintf(fp, "%08x ", wg_type); + sprintf(tmp, "\"%s\" ", *work->local_master_browser_name ? + work->local_master_browser_name : "Unknown" ); + fprintf(fp, "%-30s", tmp); + fprintf(fp, "\"%s\"\n", work->work_group); + } + + /* Now write out any server records a workgroup may have. */ + + for (servrec = work->serverlist; servrec ; servrec = servrec->next) + { + uint32 serv_type; + + /* We have already written our names here. */ + if(is_myname(servrec->serv.name)) + continue; + + serv_type = write_this_server_name(subrec, work, servrec); + + if(serv_type) + { + /* Output server details, plus what workgroup they're in. */ + sprintf(tmp, "\"%s\"", servrec->serv.name); + fprintf(fp, "%-25s ", tmp); + fprintf(fp, "%08x ", serv_type); + sprintf(tmp, "\"%s\" ", servrec->serv.comment); + fprintf(fp, "%-30s", tmp); + fprintf(fp, "\"%s\"\n", work->work_group); + } + } + } + } + + fclose(fp); + unlink(fname); + chmod(fnamenew,0644); + rename(fnamenew,fname); + DEBUG(3,("write_browse_list: Wrote browse list into file %s\n",fname)); +} diff --git a/source3/nmbd/nmbd_subnetdb.c b/source3/nmbd/nmbd_subnetdb.c new file mode 100644 index 0000000000..93aecc21f2 --- /dev/null +++ b/source3/nmbd/nmbd_subnetdb.c @@ -0,0 +1,291 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NBT netbios routines and daemon - version 2 + Copyright (C) Andrew Tridgell 1994-1997 + Copyright (C) Luke Kenneth Casson Leighton 1994-1997 + Copyright (C) Jeremy Allison 1994-1997 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + Revision History: + +*/ + +#include "includes.h" +#include "smb.h" + +extern int ClientNMB; +extern int ClientDGRAM; +extern int global_nmb_port; + +extern int DEBUGLEVEL; + +extern pstring myname; +extern fstring myworkgroup; +extern char **my_netbios_names; +extern struct in_addr ipzero; + +/* This is the broadcast subnets database. */ +struct subnet_record *subnetlist = NULL; + +/* Extra subnets - keep these separate so enumeration code doesn't + run onto it by mistake. */ + +struct subnet_record *unicast_subnet = NULL; +struct subnet_record *remote_broadcast_subnet = NULL; +struct subnet_record *wins_server_subnet = NULL; + +extern uint16 samba_nb_type; /* Samba's NetBIOS name type. */ + +/**************************************************************************** + Add a subnet into the list. + **************************************************************************/ + +static void add_subnet(struct subnet_record *subrec) +{ + struct subnet_record *subrec2; + + if (!subnetlist) + { + subnetlist = subrec; + subrec->prev = NULL; + subrec->next = NULL; + return; + } + + for (subrec2 = subnetlist; subrec2->next; subrec2 = subrec2->next) + ; + + subrec2->next = subrec; + subrec->next = NULL; + subrec->prev = subrec2; +} + +/**************************************************************************** + Create a subnet entry. + ****************************************************************************/ + +static struct subnet_record *make_subnet(char *name, enum subnet_type type, + struct in_addr myip, struct in_addr bcast_ip, + struct in_addr mask_ip) +{ + struct subnet_record *subrec = NULL; + int nmb_sock, dgram_sock; + + /* Check if we are creating a non broadcast subnet - if so don't create + sockets. + */ + + if(type != NORMAL_SUBNET) + { + nmb_sock = -1; + dgram_sock = -1; + } + else + { + /* + * Attempt to open the sockets on port 137/138 for this interface + * and bind them. + * Fail the subnet creation if this fails. + */ + + if((nmb_sock = open_socket_in(SOCK_DGRAM, global_nmb_port,0, myip.s_addr)) == -1) + { + DEBUG(0,("make_subnet: Failed to open nmb socket on interface %s \ +for port %d. Error was %s\n", inet_ntoa(myip), global_nmb_port, strerror(errno))); + return NULL; + } + + if((dgram_sock = open_socket_in(SOCK_DGRAM,DGRAM_PORT,3, myip.s_addr)) == -1) + { + DEBUG(0,("make_subnet: Failed to open dgram socket on interface %s \ +for port %d. Error was %s\n", inet_ntoa(myip), DGRAM_PORT, strerror(errno))); + return NULL; + } + + /* Make sure we can broadcast from these sockets. */ + set_socket_options(nmb_sock,"SO_BROADCAST"); + set_socket_options(dgram_sock,"SO_BROADCAST"); + + } + + subrec = (struct subnet_record *)malloc(sizeof(*subrec)); + + if (!subrec) + { + DEBUG(0,("make_subnet: malloc fail !\n")); + close(nmb_sock); + close(dgram_sock); + return(NULL); + } + + bzero((char *)subrec,sizeof(*subrec)); + + if((subrec->subnet_name = strdup(name)) == NULL) + { + DEBUG(0,("make_subnet: malloc fail for subnet name !\n")); + close(nmb_sock); + close(dgram_sock); + free((char *)subrec); + return(NULL); + } + + DEBUG(2, ("making subnet name:%s ", name )); + DEBUG(2, ("Broadcast address:%s ", inet_ntoa(bcast_ip))); + DEBUG(2, ("Subnet mask:%s\n", inet_ntoa(mask_ip))); + + subrec->namelist_changed = False; + subrec->work_changed = False; + + subrec->bcast_ip = bcast_ip; + subrec->mask_ip = mask_ip; + subrec->myip = myip; + subrec->type = type; + subrec->nmb_sock = nmb_sock; + subrec->dgram_sock = dgram_sock; + + return subrec; +} + +/**************************************************************************** + Create subnet entries. +**************************************************************************/ + +BOOL create_subnets() +{ + int num_interfaces = iface_count(); + int i; + struct in_addr unicast_ip; + + if(num_interfaces == 0) + { + DEBUG(0,("create_subnets: No local interfaces !\n")); + return False; + } + + /* + * Create subnets from all the local interfaces and thread them onto + * the linked list. + */ + + for (i = 0 ; i < num_interfaces; i++) + { + struct subnet_record *subrec; + struct interface *iface = get_interface(i); + + if((subrec = make_subnet(inet_ntoa(iface->ip), NORMAL_SUBNET, + iface->ip, iface->bcast,iface->nmask)) == NULL) + return False; + add_subnet(subrec); + } + + /* + * If we have been configured to use a WINS server, then try and + * get the ip address of it here. If we are the WINS server then + * set the unicast subnet address to be the first of our own real + * addresses. + */ + + if(*lp_wins_server()) + { + struct in_addr real_wins_ip; + real_wins_ip = *interpret_addr2(lp_wins_server()); + + if (!zero_ip(real_wins_ip)) + { + unicast_ip = real_wins_ip; + } + else + { + /* The smb.conf's wins server parameter MUST be a host_name + or an ip_address. */ + DEBUG(0,("invalid smb.conf parameter 'wins server'\n")); + return False; + } + } + else if(lp_we_are_a_wins_server()) + { + /* Pick the first interface ip address as the WINS server ip. */ + unicast_ip = *iface_n_ip(0); + } + else + { + /* We should not be using a WINS server at all. Set the + ip address of the subnet to be zero. */ + unicast_ip = ipzero; + } + + /* + * Create the unicast and remote broadcast subnets. + * Don't put these onto the linked list. + * The ip address of the unicast subnet is set to be + * the WINS server address, if it exists, or ipzero if not. + */ + + unicast_subnet = make_subnet( "UNICAST_SUBNET", UNICAST_SUBNET, + unicast_ip, unicast_ip, unicast_ip); + + remote_broadcast_subnet = make_subnet( "REMOTE_BROADCAST_SUBNET", + REMOTE_BROADCAST_SUBNET, + ipzero, ipzero, ipzero); + + if((unicast_subnet == NULL) || (remote_broadcast_subnet == NULL)) + return False; + + /* + * If we are WINS server, create the WINS_SERVER_SUBNET - don't put on + * the linked list. + */ + + if (lp_we_are_a_wins_server()) + { + if((wins_server_subnet = make_subnet("WINS_SERVER_SUBNET", + WINS_SERVER_SUBNET, + ipzero, ipzero, ipzero)) == NULL) + return False; + } + + return True; +} + +/******************************************************************* +Function to tell us if we can use the unicast subnet. +******************************************************************/ + +BOOL we_are_a_wins_client() +{ + static int cache_we_are_a_wins_client = -1; + + if(cache_we_are_a_wins_client == -1) + cache_we_are_a_wins_client = (ip_equal(ipzero, unicast_subnet->myip) ? + False : True); + + return cache_we_are_a_wins_client; +} + +/******************************************************************* +Access function used by NEXT_SUBNET_INCLUDING_UNICAST +******************************************************************/ + +struct subnet_record *get_next_subnet_maybe_unicast(struct subnet_record *subrec) +{ + if(subrec == unicast_subnet) + return NULL; + else if((subrec->next == NULL) && we_are_a_wins_client()) + return unicast_subnet; + else + return subrec->next; +} diff --git a/source3/nmbd/nmbd_winsproxy.c b/source3/nmbd/nmbd_winsproxy.c new file mode 100644 index 0000000000..ed8653b8bf --- /dev/null +++ b/source3/nmbd/nmbd_winsproxy.c @@ -0,0 +1,195 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NBT netbios routines and daemon - version 2 + + Copyright (C) Jeremy Allison 1994-1997 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "includes.h" + +extern int DEBUGLEVEL; + +/**************************************************************************** +Function called when the name lookup succeeded. +****************************************************************************/ + +static void wins_proxy_name_query_request_success( struct subnet_record *subrec, + struct userdata_struct *userdata, + struct nmb_name *nmbname, struct in_addr ip, struct res_rec *rrec) +{ + struct packet_struct *original_packet; + struct subnet_record *orig_broadcast_subnet; + uint16 nb_flags; + int num_ips; + int i; + int ttl; + struct in_addr *iplist; + + /* Extract the original packet and the original broadcast subnet from + the userdata. */ + + memcpy( (char *)&orig_broadcast_subnet, userdata->data, sizeof(struct subnet_record *) ); + memcpy( (char *)&original_packet, &userdata->data[sizeof(struct subnet_record *)], + sizeof(struct packet_struct *) ); + + nb_flags = get_nb_flags( rrec->rdata ); + + num_ips = rrec->rdlength / 6; + if(num_ips == 0) + { + DEBUG(0,("wins_proxy_name_query_request_success: Invalid number of IP records (0) \ +returned for name %s.\n", namestr(nmbname) )); + return; + } + + if(num_ips == 1) + iplist = &ip; + else + { + if((iplist = (struct in_addr *)malloc( num_ips * sizeof(struct in_addr) )) == NULL) + { + DEBUG(0,("wins_proxy_name_query_request_success: malloc fail !\n")); + return; + } + + for(i = 0; i < num_ips; i++) + putip( (char *)&iplist[i], (char *)&rrec->rdata[ (i*6) + 2]); + } + + /* Add the queried name to the original subnet as a WINS_PROXY_NAME. */ + + if(rrec == PERMANENT_TTL) + ttl = lp_max_ttl(); + + add_name_to_subnet( orig_broadcast_subnet, nmbname->name, nmbname->name_type, + nb_flags, ttl, WINS_PROXY_NAME, num_ips, iplist); + + if(iplist != &ip) + free((char *)iplist); + + /* Finally reply to the original name query. */ + reply_netbios_packet(original_packet, /* Packet to reply to. */ + 0, /* Result code. */ + NMB_QUERY, /* nmbd type code. */ + NMB_NAME_QUERY_OPCODE, /* opcode. */ + ttl, /* ttl. */ + rrec->rdata, /* data to send. */ + rrec->rdlength); /* data length. */ +} + +/**************************************************************************** +Function called when the name lookup failed. +****************************************************************************/ + +static void wins_proxy_name_query_request_fail(struct subnet_record *subrec, + struct response_record *rrec, + struct nmb_name *question_name, int fail_code) +{ + DEBUG(4,("wins_proxy_name_query_request_fail: WINS server returned error code %d for lookup \ +of name %s.\n", fail_code, namestr(question_name) )); +} + +/**************************************************************************** +Function to make a deep copy of the userdata we will need when the WINS +proxy query returns. +****************************************************************************/ + +static struct userdata_struct *wins_proxy_userdata_copy_fn(struct userdata_struct *userdata) +{ + struct packet_struct *p, *copy_of_p; + struct userdata_struct *new_userdata = + (struct userdata_struct *)malloc( userdata->userdata_len ); + + if(new_userdata == NULL) + return NULL; + + new_userdata->copy_fn = userdata->copy_fn; + new_userdata->free_fn = userdata->free_fn; + new_userdata->userdata_len = userdata->userdata_len; + + /* Copy the subnet_record pointer. */ + memcpy( new_userdata->data, userdata->data, sizeof(struct subnet_record *) ); + + /* Extract the pointer to the packet struct */ + memcpy((char *)&p, &userdata->data[sizeof(struct subnet_record *)], + sizeof(struct packet_struct *) ); + + /* Do a deep copy of the packet. */ + if((copy_of_p = copy_packet(p)) == NULL) + { + free((char *)new_userdata); + return NULL; + } + + /* Lock the copy. */ + copy_of_p->locked = True; + + memcpy( &new_userdata->data[sizeof(struct subnet_record *)], (char *)©_of_p, + sizeof(struct packet_struct *) ); + + return new_userdata; +} + +/**************************************************************************** +Function to free the deep copy of the userdata we used when the WINS +proxy query returned. +****************************************************************************/ + +static void wins_proxy_userdata_free_fn(struct userdata_struct *userdata) +{ + struct packet_struct *p; + + /* Extract the pointer to the packet struct */ + memcpy((char *)&p, &userdata->data[sizeof(struct subnet_record *)], + sizeof(struct packet_struct *)); + + /* Unlock the packet. */ + p->locked = False; + + free_packet(p); + free((char *)userdata); +} + +/**************************************************************************** + Make a WINS query on behalf of a broadcast client name query request. +****************************************************************************/ + +void make_wins_proxy_name_query_request( struct subnet_record *subrec, + struct packet_struct *incoming_packet, + struct nmb_name *question_name) +{ + char ud[sizeof(struct userdata_struct) + sizeof(struct subrec *) + + sizeof(struct packet_struct *)]; + struct userdata_struct *userdata = (struct userdata_struct *)ud; + + bzero(ud, sizeof(ud)); + + userdata->copy_fn = wins_proxy_userdata_copy_fn; + userdata->free_fn = wins_proxy_userdata_free_fn; + userdata->userdata_len = sizeof(ud); + memcpy( userdata->data, (char *)&subrec, sizeof(struct subnet_record *)); + memcpy( &userdata->data[sizeof(struct subnet_record *)], (char *)&incoming_packet, + sizeof(struct packet_struct *)); + + /* Now use the unicast subnet to query the name with the WINS server. */ + query_name( unicast_subnet, question_name->name, question_name->name_type, + wins_proxy_name_query_request_success, + wins_proxy_name_query_request_fail, + userdata); +} diff --git a/source3/nmbd/nmbd_winsserver.c b/source3/nmbd/nmbd_winsserver.c new file mode 100644 index 0000000000..ba7b62e5ab --- /dev/null +++ b/source3/nmbd/nmbd_winsserver.c @@ -0,0 +1,1565 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NBT netbios routines and daemon - version 2 + + Copyright (C) Jeremy Allison 1994-1997 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "includes.h" + +#define WINS_LIST "wins.dat" + +extern int DEBUGLEVEL; +extern struct in_addr ipzero; + +/**************************************************************************** +Determine if this packet should be allocated to the WINS server. +*****************************************************************************/ + +BOOL packet_is_for_wins_server(struct packet_struct *packet) +{ + struct nmb_packet *nmb = &packet->packet.nmb; + + /* Only unicast packets go to a WINS server. */ + if((wins_server_subnet == NULL) || (nmb->header.nm_flags.bcast == True)) + { + DEBUG(10, ("packet_is_for_wins_server: failing WINS test #1.\n")); + return False; + } + + /* Check for node status requests. */ + if (nmb->question.question_type != QUESTION_TYPE_NB_QUERY) + return False; + + switch(nmb->header.opcode) + { + /* + * A WINS server issues WACKS, not receives them. + */ + case NMB_WACK_OPCODE: + DEBUG(10, ("packet_is_for_wins_server: failing WINS test #2 (WACK).\n")); + return False; + /* + * A WINS server only processes registration and + * release requests, not responses. + */ + case NMB_NAME_REG_OPCODE: + case NMB_NAME_MULTIHOMED_REG_OPCODE: + case NMB_NAME_REFRESH_OPCODE_8: /* ambiguity in rfc1002 about which is correct. */ + case NMB_NAME_REFRESH_OPCODE_9: /* WinNT uses 8 by default. */ + if(nmb->header.response) + { + DEBUG(10, ("packet_is_for_wins_server: failing WINS test #3 (response = 1).\n")); + return False; + } + break; + + case NMB_NAME_RELEASE_OPCODE: + if(nmb->header.response) + { + DEBUG(10, ("packet_is_for_wins_server: failing WINS test #4 (response = 1).\n")); + return False; + } + break; + + /* + * Only process unicast name queries with rd = 1. + */ + case NMB_NAME_QUERY_OPCODE: + if(!nmb->header.response && !nmb->header.nm_flags.recursion_desired) + { + DEBUG(10, ("packet_is_for_wins_server: failing WINS test #5 (response = 1).\n")); + return False; + } + break; + } + + return True; +} + +/**************************************************************************** +Utility function to decide what ttl to give a register/refresh request. +*****************************************************************************/ + +static int get_ttl_from_packet(struct nmb_packet *nmb) +{ + int ttl = nmb->additional->ttl; + + if(ttl < lp_min_wins_ttl() ) + ttl = lp_min_wins_ttl(); + + if(ttl > lp_max_wins_ttl() ) + ttl = lp_max_wins_ttl(); + + return ttl; +} + +/**************************************************************************** +Load or create the WINS database. +*****************************************************************************/ + +BOOL initialise_wins(void) +{ + fstring fname; + time_t time_now = time(NULL); + FILE *fp; + pstring line; + + if(!lp_we_are_a_wins_server()) + return True; + + add_samba_names_to_subnet(wins_server_subnet); + +#ifndef SYNC_DNS + /* Setup the async dns. */ + start_async_dns(); +#endif + + fstrcpy(fname,lp_lockdir()); + trim_string(fname,NULL,"/"); + strcat(fname,"/"); + strcat(fname,WINS_LIST); + + if((fp = fopen(fname,"r")) == NULL) + { + DEBUG(2,("initialise_wins: Can't open wins database file %s. Error was %s\n", + fname, strerror(errno) )); + return True; + } + + while (!feof(fp)) + { + pstring name_str, ip_str, ttl_str, nb_flags_str; + unsigned int num_ips; + pstring name; + struct in_addr *ip_list; + int type = 0; + uint16 nb_flags; + time_t ttl; + enum name_source source; + char *ptr; + char *p; + BOOL got_token; + BOOL was_ip; + int i; + + /* Read a line from the wins.dat file. Strips whitespace + from the beginning and end of the line. + */ + if (!fgets_slash(line,sizeof(pstring),fp)) + continue; + + if (*line == '#') + continue; + + ptr = line; + + /* + * Now we handle multiple IP addresses per name we need + * to iterate over the line twice. The first time to + * determine how many IP addresses there are, the second + * time to actually parse them into the ip_list array. + */ + + if (!next_token(&ptr,name_str,NULL)) + { + DEBUG(0,("initialise_wins: Failed to parse name when parsing line %s\n", line )); + continue; + } + + if (!next_token(&ptr,ttl_str,NULL)) + { + DEBUG(0,("initialise_wins: Failed to parse time to live when parsing line %s\n", line )); + continue; + } + + /* + * Determine the number of IP addresses per line. + */ + num_ips = 0; + do + { + got_token = next_token(&ptr,ip_str,NULL); + was_ip = False; + + if(got_token && strchr(ip_str, '.')) + { + num_ips++; + was_ip = True; + } + } while( got_token && was_ip); + + if(num_ips == 0) + { + DEBUG(0,("initialise_wins: Missing IP address when parsing line %s\n", line )); + continue; + } + + if(!got_token) + { + DEBUG(0,("initialise_wins: Missing nb_flags when parsing line %s\n", line )); + continue; + } + + /* Allocate the space for the ip_list. */ + if((ip_list = (struct in_addr *)malloc( num_ips * sizeof(struct in_addr))) == NULL) + { + DEBUG(0,("initialise_wins: Malloc fail !\n")); + return False; + } + + /* Reset and re-parse the line. */ + ptr = line; + next_token(&ptr,name_str,NULL); + next_token(&ptr,ttl_str,NULL); + for(i = 0; i < num_ips; i++) + { + next_token(&ptr, ip_str, NULL); + ip_list[i] = *interpret_addr2(ip_str); + if (ip_equal(ip_list[i], ipzero)) + source = SELF_NAME; + } + next_token(&ptr,nb_flags_str,NULL); + + /* + * Deal with SELF or REGISTER name encoding. Default is REGISTER + * for compatibility with old nmbds. + */ + + if(nb_flags_str[strlen(nb_flags_str)-1] == 'S') + { + DEBUG(5,("initialise_wins: Ignoring SELF name %s\n", line)); + free((char *)ip_list); + continue; + } + + if(nb_flags_str[strlen(nb_flags_str)-1] == 'R') + nb_flags_str[strlen(nb_flags_str)-1] = '\0'; + + /* Netbios name. # divides the name from the type (hex): netbios#xx */ + pstrcpy(name,name_str); + + if((p = strchr(name,'#')) != NULL) + { + *p = 0; + sscanf(p+1,"%x",&type); + } + + /* Decode the netbios flags (hex) and the time-to-live (in seconds). */ + sscanf(nb_flags_str,"%hx",&nb_flags); + sscanf(ttl_str,"%ld",&ttl); + + /* add all entries that have 60 seconds or more to live */ + if ((ttl - 60) > time_now || ttl == PERMANENT_TTL) + { + struct name_record *namerec; + + if(ttl != PERMANENT_TTL) + ttl -= time_now; + + DEBUG(4, ("initialise_wins: add name: %s#%02x ttl = %ld first IP %s flags = %2hx\n", + name, type, ttl, inet_ntoa(ip_list[0]), nb_flags)); + + namerec = add_name_to_subnet(wins_server_subnet, name, type, nb_flags, + ttl, REGISTER_NAME, num_ips, ip_list); + + } + else + { + DEBUG(4, ("initialise_wins: not adding name (ttl problem) %s#%02x ttl = %ld first IP %s flags = %2hx\n", + name, type, ttl, inet_ntoa(ip_list[0]), nb_flags)); + } + + free((char *)ip_list); + } + + fclose(fp); + return True; +} + +/**************************************************************************** +Send a WINS WACK (Wait ACKnowledgement) response. +**************************************************************************/ + +static void send_wins_wack_response(int ttl, struct packet_struct *p) +{ + struct nmb_packet *nmb = &p->packet.nmb; + unsigned char rdata[2]; + + rdata[0] = rdata[1] = 0; + + /* Taken from nmblib.c - we need to send back almost + identical bytes from the requesting packet header. */ + + rdata[0] = (nmb->header.opcode & 0xF) << 3; + if (nmb->header.nm_flags.authoritative && + nmb->header.response) rdata[0] |= 0x4; + if (nmb->header.nm_flags.trunc) rdata[0] |= 0x2; + if (nmb->header.nm_flags.recursion_desired) rdata[0] |= 0x1; + if (nmb->header.nm_flags.recursion_available && + nmb->header.response) rdata[1] |= 0x80; + if (nmb->header.nm_flags.bcast) rdata[1] |= 0x10; + + reply_netbios_packet(p, /* Packet to reply to. */ + 0, /* Result code. */ + NMB_WAIT_ACK, /* nmbd type code. */ + NMB_WACK_OPCODE, /* opcode. */ + ttl, /* ttl. */ + (char *)rdata, /* data to send. */ + 2); /* data length. */ +} + +/**************************************************************************** +Send a WINS name registration response. +**************************************************************************/ + +static void send_wins_name_registration_response(int rcode, int ttl, struct packet_struct *p) +{ + struct nmb_packet *nmb = &p->packet.nmb; + char rdata[6]; + + memcpy(&rdata[0], &nmb->additional->rdata[0], 6); + + reply_netbios_packet(p, /* Packet to reply to. */ + rcode, /* Result code. */ + WINS_REG, /* nmbd type code. */ + NMB_NAME_REG_OPCODE, /* opcode. */ + ttl, /* ttl. */ + rdata, /* data to send. */ + 6); /* data length. */ +} + +/*********************************************************************** + Deal with a name refresh request to a WINS server. +************************************************************************/ + +void wins_process_name_refresh_request(struct subnet_record *subrec, + struct packet_struct *p) +{ + struct nmb_packet *nmb = &p->packet.nmb; + struct nmb_name *question = &nmb->question.question_name; + BOOL bcast = nmb->header.nm_flags.bcast; + uint16 nb_flags = get_nb_flags(nmb->additional->rdata); + BOOL group = (nb_flags & NB_GROUP) ? True : False; + struct name_record *namerec = NULL; + int ttl = get_ttl_from_packet(nmb); + struct in_addr from_ip; + + putip((char *)&from_ip,&nmb->additional->rdata[2]); + + if(bcast) + { + /* + * We should only get unicast name refresh packets here. + * Anyone trying to refresh broadcast should not be going to a WINS + * server. Log an error here. + */ + + DEBUG(0,("wins_process_name_refresh_request: broadcast name refresh request \ +received for name %s from IP %s on subnet %s. Error - should not be sent to WINS server\n", + namestr(question), inet_ntoa(from_ip), subrec->subnet_name)); + return; + } + + DEBUG(3,("wins_process_name_refresh_request: Name refresh for name %s \ +IP %s\n", namestr(question), inet_ntoa(from_ip) )); + + /* + * See if the name already exists. + */ + + namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME); + + /* + * If this is a refresh request and the name doesn't exist then + * fail it. + */ + + if(namerec == NULL) + { + DEBUG(3,("wins_process_name_refresh_request: Name refresh for name %s and \ +the name does not exist.\n", namestr(question) )); + send_wins_name_registration_response(NAM_ERR, 0, p); + return; + } + + /* + * Check that the group bits for the refreshing name and the + * name in our database match. + */ + + if((namerec != NULL) && ((group && !NAME_GROUP(namerec)) || (!group && NAME_GROUP(namerec))) ) + { + DEBUG(3,("wins_process_name_refresh_request: Name %s group bit = %s \ +does not match group bit in WINS for this name.\n", namestr(question), group ? "True" : "False" )); + send_wins_name_registration_response(RFS_ERR, 0, p); + return; + } + + /* + * For a unique name check that the person refreshing the name is one of the registered IP + * addresses. If not - fail the refresh. Do the same for group names with a type of 0x1c. + * Just return success for unique 0x1d refreshes. For normal group names update the ttl + * and return success. + */ + + if((!group || (group && (question->name_type == 0x1c))) && find_ip_in_name_record(namerec, from_ip )) + { + /* + * Update the ttl. + */ + update_name_ttl(namerec, ttl); + send_wins_name_registration_response(0, ttl, p); + return; + } + else if(group) + { + /* + * Normal groups are all registered with an IP address of 255.255.255.255 + * so we can't search for the IP address. + */ + update_name_ttl(namerec, ttl); + send_wins_name_registration_response(0, ttl, p); + return; + } + else if(!group && (question->name_type == 0x1d)) + { + /* + * Special name type - just pretend the refresh succeeded. + */ + send_wins_name_registration_response(0, ttl, p); + return; + } + else + { + /* + * Fail the refresh. + */ + + DEBUG(3,("wins_process_name_refresh_request: Name refresh for name %s with IP %s and \ +is IP is not known to the name.\n", namestr(question), inet_ntoa(from_ip) )); + send_wins_name_registration_response(RFS_ERR, 0, p); + return; + } +} + +/*********************************************************************** + Deal with a name registration request query success to a client that + owned the name. + + We have a locked pointer to the original packet stashed away in the + userdata pointer. The success here is actually a failure as it means + the client we queried wants to keep the name, so we must return + a registration failure to the original requestor. +************************************************************************/ + +static void wins_register_query_success(struct subnet_record *subrec, + struct userdata_struct *userdata, + struct nmb_name *question_name, + struct in_addr ip, + struct res_rec *answers) +{ + struct packet_struct *orig_reg_packet; + + memcpy((char *)&orig_reg_packet, userdata->data, sizeof(struct packet_struct *)); + + DEBUG(3,("wins_register_query_success: Original client at IP %s still wants the \ +name %s. Rejecting registration request.\n", inet_ntoa(ip), namestr(question_name) )); + + send_wins_name_registration_response(RFS_ERR, 0, orig_reg_packet); + + orig_reg_packet->locked = False; + free_packet(orig_reg_packet); +} + +/*********************************************************************** + Deal with a name registration request query failure to a client that + owned the name. + + We have a locked pointer to the original packet stashed away in the + userdata pointer. The failure here is actually a success as it means + the client we queried didn't want to keep the name, so we can remove + the old name record and then successfully add the new name. +************************************************************************/ + +static void wins_register_query_fail(struct subnet_record *subrec, + struct response_record *rrec, + struct nmb_name *question_name, + int rcode) +{ + struct userdata_struct *userdata = rrec->userdata; + struct packet_struct *orig_reg_packet; + struct nmb_packet *nmb; + struct name_record *namerec = NULL; + uint16 nb_flags; + BOOL group; + + memcpy((char *)&orig_reg_packet, userdata->data, sizeof(struct packet_struct *)); + + nmb = &orig_reg_packet->packet.nmb; + + nb_flags = get_nb_flags(nmb->additional->rdata); + group = (nb_flags & NB_GROUP) ? True : False; + + /* + * We want to just add the name, as we now know the original owner + * didn't want it. But we can't just do that as an arbitary + * amount of time may have taken place between the name query + * request and this timeout/error response. So we check that + * the name still exists and is in the same state - if so + * we remove it and call wins_process_name_registration_request() + * as we know it will do the right thing now. + */ + + namerec = find_name_on_subnet(subrec, question_name, FIND_ANY_NAME); + + if((namerec != NULL) && (namerec->source == REGISTER_NAME) && + ip_equal(rrec->packet->ip, *namerec->ip) ) + { + remove_name_from_namelist( subrec, namerec); + namerec = NULL; + } + + if(namerec == NULL) + wins_process_name_registration_request(subrec, orig_reg_packet); + else + DEBUG(2,("wins_register_query_fail: The state of the WINS database changed between \ +querying for name %s in order to replace it and this reply.\n", namestr(question_name) )); + + orig_reg_packet->locked = False; + free_packet(orig_reg_packet); +} + +/*********************************************************************** + Deal with a name registration request to a WINS server. + + Use the following pseudocode : + + registering_group + | + | + +--------name exists + | | + | | + | +--- existing name is group + | | | + | | | + | | +--- add name (return). + | | + | | + | +--- exiting name is unique + | | + | | + | +--- query existing owner (return). + | + | + +--------name doesn't exist + | + | + +--- add name (return). + + registering_unique + | + | + +--------name exists + | | + | | + | +--- existing name is group + | | | + | | | + | | +--- fail add (return). + | | + | | + | +--- exiting name is unique + | | + | | + | +--- query existing owner (return). + | + | + +--------name doesn't exist + | + | + +--- add name (return). + + As can be seen from the above, the two cases may be collapsed onto each + other with the exception of the case where the name already exists and + is a group name. This case we handle with an if statement. + +************************************************************************/ + +void wins_process_name_registration_request(struct subnet_record *subrec, + struct packet_struct *p) +{ + struct nmb_packet *nmb = &p->packet.nmb; + struct nmb_name *question = &nmb->question.question_name; + BOOL bcast = nmb->header.nm_flags.bcast; + uint16 nb_flags = get_nb_flags(nmb->additional->rdata); + int ttl = get_ttl_from_packet(nmb); + struct name_record *namerec = NULL; + struct in_addr from_ip; + BOOL registering_group_name = (nb_flags & NB_GROUP) ? True : False;; + + putip((char *)&from_ip,&nmb->additional->rdata[2]); + + if(bcast) + { + /* + * We should only get unicast name registration packets here. + * Anyone trying to register broadcast should not be going to a WINS + * server. Log an error here. + */ + + DEBUG(0,("wins_process_name_registration_request: broadcast name registration request \ +received for name %s from IP %s on subnet %s. Error - should not be sent to WINS server\n", + namestr(question), inet_ntoa(from_ip), subrec->subnet_name)); + return; + } + + DEBUG(3,("wins_process_name_registration_request: %s name registration for name %s \ +IP %s\n", registering_group_name ? "Group" : "Unique", namestr(question), inet_ntoa(from_ip) )); + + /* + * See if the name already exists. + */ + + namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME); + + /* + * Deal with the case where the name found was a dns entry. + * Remove it as we now have a NetBIOS client registering the + * name. + */ + + if((namerec != NULL) && ((namerec->source == DNS_NAME) || (namerec->source == DNSFAIL_NAME))) + { + DEBUG(5,("wins_process_name_registration_request: Name (%s) in WINS was a dns lookup \ +- removing it.\n", namestr(question) )); + remove_name_from_namelist( subrec, namerec); + namerec = NULL; + } + + /* + * Reject if the name exists and is not a REGISTER_NAME. + * (ie. Don't allow any static names to be overwritten. + */ + + if((namerec != NULL) && (namerec->source != REGISTER_NAME)) + { + DEBUG(3,("wins_process_name_registration_request: Attempt to register name %s. Name \ +already exists in WINS with source type %d.\n", namestr(question), namerec->source )); + send_wins_name_registration_response(RFS_ERR, 0, p); + return; + } + + /* + * Special policy decisions based on MS documentation. + * 1). All group names (except names ending in 0x1c) are added as 255.255.255.255. + * 2). All unique names ending in 0x1d are ignored, although a positive response is sent. + */ + + /* + * A group name is always added as the local broadcast address, except + * for group names ending in 0x1c. + * Group names with type 0x1c are registered with individual IP addresses. + */ + + if(registering_group_name && (question->name_type != 0x1c)) + from_ip = *interpret_addr2("255.255.255.255"); + + /* + * Ignore all attempts to register a unique 0x1d name, although return success. + */ + + if(!registering_group_name && (question->name_type == 0x1d)) + { + DEBUG(3,("wins_process_name_registration_request: Ignoring request \ +to register name %s from IP %s.", namestr(question), inet_ntoa(p->ip) )); + send_wins_name_registration_response(0, ttl, p); + return; + } + + /* + * Next two cases are the 'if statement' mentioned above. + */ + + if((namerec != NULL) && NAME_GROUP(namerec)) + { + if(registering_group_name) + { + /* + * If we are adding a group name, the name exists and is also a group entry just add this + * IP address to it and update the ttl. + */ + + DEBUG(3,("wins_process_name_registration_request: Adding IP %s to group name %s.\n", + inet_ntoa(from_ip), namestr(question) )); + /* + * Check the ip address is not already in the group. + */ + if(!find_ip_in_name_record(namerec, from_ip)) + add_ip_to_name_record(namerec, from_ip); + update_name_ttl(namerec, ttl); + send_wins_name_registration_response(0, ttl, p); + return; + } + else + { + /* + * If we are adding a unique name, the name exists in the WINS db + * and is a group name then reject the registration. + */ + + DEBUG(3,("wins_process_name_registration_request: Attempt to register name %s. Name \ +already exists in WINS as a GROUP name.\n", namestr(question) )); + send_wins_name_registration_response(RFS_ERR, 0, p); + return; + } + } + + /* + * From here on down we know that if the name exists in the WINS db it is + * a unique name, not a group name. + */ + + /* + * If the name exists and is one of our names then check the + * registering IP address. If it's not one of ours then automatically + * reject without doing the query - we know we will reject it. + */ + + if((namerec != NULL) && (is_myname(namerec->name.name)) ) + { + if(!ismyip(from_ip)) + { + DEBUG(3,("wins_process_name_registration_request: Attempt to register name %s. Name \ +is one of our (WINS server) names. Denying registration.\n", namestr(question) )); + send_wins_name_registration_response(RFS_ERR, 0, p); + return; + } + else + { + /* + * It's one of our names and one of our IP's - update the ttl. + */ + update_name_ttl(namerec, ttl); + send_wins_name_registration_response(0, ttl, p); + return; + } + } + + /* + * If the name exists and it is a unique registration and the registering IP + * is the same as the the (single) already registered IP then just update the ttl. + */ + + if(!registering_group_name && (namerec != NULL) && (namerec->num_ips == 1) && + ip_equal(namerec->ip[0], from_ip)) + { + update_name_ttl(namerec, ttl); + send_wins_name_registration_response(0, ttl, p); + return; + } + + /* + * Finally if the name exists do a query to the registering machine + * to see if they still claim to have the name. + */ + + if(namerec != NULL) + { + char ud[sizeof(struct userdata_struct) + sizeof(struct packet_struct *)]; + struct userdata_struct *userdata = (struct userdata_struct *)&ud; + + /* + * First send a WACK to the registering machine. + */ + + send_wins_wack_response(60, p); + + /* + * When the reply comes back we need the original packet. + * Lock this so it won't be freed and then put it into + * the userdata structure. + */ + + p->locked = True; + + userdata = (struct userdata_struct *)ud; + + userdata->copy_fn = NULL; + userdata->free_fn = NULL; + userdata->userdata_len = sizeof(struct packet_struct *); + memcpy(userdata->data, (char *)&p, sizeof(struct packet_struct *) ); + + /* + * As query_name uses the subnet broadcast address as the destination + * of the packet we temporarily change the subnet broadcast address to + * be the first IP address of the name owner and send the packet. This + * is a *horrible* hack but the alternative is to add the destination + * address parameter to all query_name() calls. I hate this code :-). + */ + + subrec->bcast_ip = *namerec->ip; + + query_name( subrec, question->name, question->name_type, + wins_register_query_success, + wins_register_query_fail, + userdata); + subrec->bcast_ip = ipzero; + return; + } + + /* + * Name did not exist - add it. + */ + + add_name_to_subnet(subrec, question->name, question->name_type, + nb_flags, ttl, REGISTER_NAME, 1, &from_ip); + + send_wins_name_registration_response(0, ttl, p); +} + +/*********************************************************************** + Deal with a mutihomed name query success to the machine that + requested the multihomed name registration. + + We have a locked pointer to the original packet stashed away in the + userdata pointer. +************************************************************************/ + +static void wins_multihomed_register_query_success(struct subnet_record *subrec, + struct userdata_struct *userdata, + struct nmb_name *question_name, + struct in_addr ip, + struct res_rec *answers) +{ + struct packet_struct *orig_reg_packet; + struct nmb_packet *nmb; + struct name_record *namerec = NULL; + struct in_addr from_ip; + int ttl; + + memcpy((char *)&orig_reg_packet, userdata->data, sizeof(struct packet_struct *)); + + nmb = &orig_reg_packet->packet.nmb; + + putip((char *)&from_ip,&nmb->additional->rdata[2]); + ttl = get_ttl_from_packet(nmb); + + /* + * We want to just add the new IP, as we now know the requesting + * machine claims to own it. But we can't just do that as an arbitary + * amount of time may have taken place between the name query + * request and this response. So we check that + * the name still exists and is in the same state - if so + * we just add the extra IP and update the ttl. + */ + + namerec = find_name_on_subnet(subrec, question_name, FIND_ANY_NAME); + + if( (namerec == NULL) || (namerec->source != REGISTER_NAME) ) + { + DEBUG(3,("wins_multihomed_register_query_success: name %s is not in the correct state to add \ +a subsequent IP addess.\n", namestr(question_name) )); + send_wins_name_registration_response(RFS_ERR, 0, orig_reg_packet); + return; + } + + if(!find_ip_in_name_record(namerec, from_ip)) + add_ip_to_name_record(namerec, from_ip); + update_name_ttl(namerec, ttl); + send_wins_name_registration_response(0, ttl, orig_reg_packet); + +} + +/*********************************************************************** + Deal with a name registration request query failure to a client that + owned the name. + + We have a locked pointer to the original packet stashed away in the + userdata pointer. +************************************************************************/ + +static void wins_multihomed_register_query_fail(struct subnet_record *subrec, + struct response_record *rrec, + struct nmb_name *question_name, + int rcode) +{ + struct userdata_struct *userdata = rrec->userdata; + struct packet_struct *orig_reg_packet; + + memcpy((char *)&orig_reg_packet, userdata->data, sizeof(struct packet_struct *)); + + DEBUG(3,("wins_multihomed_register_query_fail: Registering machine at IP %s failed to answer \ +query successfully for name %s.\n", inet_ntoa(orig_reg_packet->ip), namestr(question_name) )); + send_wins_name_registration_response(RFS_ERR, 0, orig_reg_packet); + return; +} + +/*********************************************************************** + Deal with a multihomed name registration request to a WINS server. + These cannot be group name registrations. +***********************************************************************/ + +void wins_process_multihomed_name_registration_request( struct subnet_record *subrec, + struct packet_struct *p) +{ + struct nmb_packet *nmb = &p->packet.nmb; + struct nmb_name *question = &nmb->question.question_name; + BOOL bcast = nmb->header.nm_flags.bcast; + uint16 nb_flags = get_nb_flags(nmb->additional->rdata); + int ttl = get_ttl_from_packet(nmb); + struct name_record *namerec = NULL; + struct in_addr from_ip; + BOOL group = (nb_flags & NB_GROUP) ? True : False;; + + putip((char *)&from_ip,&nmb->additional->rdata[2]); + + if(bcast) + { + /* + * We should only get unicast name registration packets here. + * Anyone trying to register broadcast should not be going to a WINS + * server. Log an error here. + */ + + DEBUG(0,("wins_process_multihomed_name_registration_request: broadcast name registration request \ +received for name %s from IP %s on subnet %s. Error - should not be sent to WINS server\n", + namestr(question), inet_ntoa(from_ip), subrec->subnet_name)); + return; + } + + /* + * Only unique names should be registered multihomed. + */ + + if(group) + { + DEBUG(0,("wins_process_multihomed_name_registration_request: group name registration request \ +received for name %s from IP %s on subnet %s. Errror - group names should not be multihomed.\n", + namestr(question), inet_ntoa(from_ip), subrec->subnet_name)); + return; + } + + DEBUG(3,("wins_process_multihomed_name_registration_request: name registration for name %s \ +IP %s\n", namestr(question), inet_ntoa(from_ip) )); + + /* + * Deal with policy regarding 0x1d names. + */ + + if(question->name_type == 0x1d) + { + DEBUG(3,("wins_process_multihomed_name_registration_request: Ignoring request \ +to register name %s from IP %s.", namestr(question), inet_ntoa(p->ip) )); + send_wins_name_registration_response(0, ttl, p); + return; + } + + /* + * See if the name already exists. + */ + + namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME); + + /* + * Deal with the case where the name found was a dns entry. + * Remove it as we now have a NetBIOS client registering the + * name. + */ + + if((namerec != NULL) && ((namerec->source == DNS_NAME) || (namerec->source == DNSFAIL_NAME))) + { + DEBUG(5,("wins_process_multihomed_name_registration_request: Name (%s) in WINS was a dns lookup \ +- removing it.\n", namestr(question) )); + remove_name_from_namelist( subrec, namerec); + namerec = NULL; + } + + /* + * Reject if the name exists and is not a REGISTER_NAME. + * (ie. Don't allow any static names to be overwritten. + */ + + if((namerec != NULL) && (namerec->source != REGISTER_NAME)) + { + DEBUG(3,("wins_process_multihomed_name_registration_request: Attempt to register name %s. Name \ +already exists in WINS with source type %d.\n", namestr(question), namerec->source )); + send_wins_name_registration_response(RFS_ERR, 0, p); + return; + } + + /* + * Reject if the name exists and is a GROUP name. + */ + + if((namerec != NULL) && NAME_GROUP(namerec)) + { + DEBUG(3,("wins_process_multihomed_name_registration_request: Attempt to register name %s. Name \ +already exists in WINS as a GROUP name.\n", namestr(question) )); + send_wins_name_registration_response(RFS_ERR, 0, p); + return; + } + + /* + * From here on down we know that if the name exists in the WINS db it is + * a unique name, not a group name. + */ + + /* + * If the name exists and is one of our names then check the + * registering IP address. If it's not one of ours then automatically + * reject without doing the query - we know we will reject it. + */ + + if((namerec != NULL) && (is_myname(namerec->name.name)) ) + { + if(!ismyip(from_ip)) + { + DEBUG(3,("wins_process_multihomed_name_registration_request: Attempt to register name %s. Name \ +is one of our (WINS server) names. Denying registration.\n", namestr(question) )); + send_wins_name_registration_response(RFS_ERR, 0, p); + return; + } + else + { + /* + * It's one of our names and one of our IP's. Ensure the IP is in the record and + * update the ttl. + */ + if(!find_ip_in_name_record(namerec, from_ip)) + add_ip_to_name_record(namerec, from_ip); + update_name_ttl(namerec, ttl); + send_wins_name_registration_response(0, ttl, p); + return; + } + } + + /* + * If the name exists do a query to the owner + * to see if they still want the name. + */ + + if(namerec != NULL) + { + char ud[sizeof(struct userdata_struct) + sizeof(struct packet_struct *)]; + struct userdata_struct *userdata = (struct userdata_struct *)&ud; + + /* + * First send a WACK to the registering machine. + */ + + send_wins_wack_response(60, p); + + /* + * When the reply comes back we need the original packet. + * Lock this so it won't be freed and then put it into + * the userdata structure. + */ + + p->locked = True; + + userdata = (struct userdata_struct *)ud; + + userdata->copy_fn = NULL; + userdata->free_fn = NULL; + userdata->userdata_len = sizeof(struct packet_struct *); + memcpy(userdata->data, (char *)&p, sizeof(struct packet_struct *) ); + + /* + * As query_name uses the subnet broadcast address as the destination + * of the packet we temporarily change the subnet broadcast address to + * be the IP address of the requesting machine and send the packet. This + * is a *horrible* hack but the alternative is to add the destination + * address parameter to all query_name() calls. I hate this code :-). + */ + + subrec->bcast_ip = p->ip; + query_name( subrec, question->name, question->name_type, + wins_multihomed_register_query_success, + wins_multihomed_register_query_fail, + userdata); + subrec->bcast_ip = ipzero; + return; + } + + /* + * Name did not exist - add it. + */ + + add_name_to_subnet(subrec, question->name, question->name_type, + nb_flags, ttl, REGISTER_NAME, 1, &from_ip); + + send_wins_name_registration_response(0, ttl, p); +} + +/*********************************************************************** + Deal with the special name query for *<1b>. +***********************************************************************/ + +static void process_wins_dmb_query_request(struct subnet_record *subrec, + struct packet_struct *p) +{ + struct name_record *namerec = NULL; + char *prdata; + int num_ips; + + /* + * Go through all the names in the WINS db looking for those + * ending in <1b>. Use this to calculate the number of IP + * addresses we need to return. + */ + + num_ips = 0; + for(namerec = subrec->namelist; namerec; namerec = namerec->next) + { + if(namerec->name.name_type == 0x1b) + num_ips += namerec->num_ips; + } + + if(num_ips == 0) + { + /* + * There are no 0x1b names registered. Return name query fail. + */ + send_wins_name_query_response(NAM_ERR, p, NULL); + return; + } + + if((prdata = (char *)malloc( num_ips * 6 )) == NULL) + { + DEBUG(0,("process_wins_dmb_query_request: Malloc fail !.\n")); + return; + } + + /* + * Go through all the names again in the WINS db looking for those + * ending in <1b>. Add their IP addresses into the list we will + * return. + */ + + num_ips = 0; + for(namerec = subrec->namelist; namerec; namerec = namerec->next) + { + if(namerec->name.name_type == 0x1b) + { + int i; + for(i = 0; i < namerec->num_ips; i++) + { + set_nb_flags(&prdata[num_ips * 6],namerec->nb_flags); + putip((char *)&prdata[(num_ips * 6) + 2], &namerec->ip[i]); + num_ips++; + } + } + } + + /* + * Send back the reply containing the IP list. + */ + + reply_netbios_packet(p, /* Packet to reply to. */ + 0, /* Result code. */ + WINS_QUERY, /* nmbd type code. */ + NMB_NAME_QUERY_OPCODE, /* opcode. */ + lp_min_wins_ttl(), /* ttl. */ + prdata, /* data to send. */ + num_ips*6); /* data length. */ + + free(prdata); +} + +/**************************************************************************** +Send a WINS name query response. +**************************************************************************/ + +void send_wins_name_query_response(int rcode, struct packet_struct *p, + struct name_record *namerec) +{ + char rdata[6]; + char *prdata = rdata; + int reply_data_len = 0; + int ttl = 0; + int i = 0; + int j; + + bzero(rdata,6); + + if(rcode == 0) + { + int same_net_index; + + ttl = (namerec->death_time != PERMANENT_TTL) ? + namerec->death_time - p->timestamp : lp_max_wins_ttl(); + + /* Copy all known ip addresses into the return data. */ + /* Optimise for the common case of one IP address so + we don't need a malloc. */ + + if(namerec->num_ips == 1 ) + prdata = rdata; + else + { + if((prdata = (char *)malloc( namerec->num_ips * 6 )) == NULL) + { + DEBUG(0,("send_wins_name_query_response: malloc fail !\n")); + return; + } + + /* + * Look over the known IP addresses and see if one of them + * is on the same (local) net as the requesting IP address. If so then + * put that IP address into the packet as the first IP. + * We can only do this for local nets as they're the only + * ones we know the netmask for. + */ + + same_net_index = -1; + i = 0; + + if(is_local_net(p->ip)) + { + struct in_addr *n_mask = iface_nmask(p->ip); + + for( j = 0; j < namerec->num_ips; j++) + { + if(same_net( namerec->ip[j], p->ip, *n_mask)) + { + set_nb_flags(&prdata[0],namerec->nb_flags); + putip((char *)&prdata[2], &namerec->ip[j]); + same_net_index = j; + i = 1; + } + } + } + } + + for(; i < namerec->num_ips; i++) + { + if(i == same_net_index) + continue; + set_nb_flags(&prdata[i*6],namerec->nb_flags); + putip((char *)&prdata[2+(i*6)], &namerec->ip[i]); + } + reply_data_len = namerec->num_ips * 6; + + } + + reply_netbios_packet(p, /* Packet to reply to. */ + rcode, /* Result code. */ + WINS_QUERY, /* nmbd type code. */ + NMB_NAME_QUERY_OPCODE, /* opcode. */ + ttl, /* ttl. */ + prdata, /* data to send. */ + reply_data_len); /* data length. */ + + if((prdata != rdata) && (prdata != NULL)) + free(prdata); +} + +/*********************************************************************** + Deal with a name query. +***********************************************************************/ + +void wins_process_name_query_request(struct subnet_record *subrec, + struct packet_struct *p) +{ + struct nmb_packet *nmb = &p->packet.nmb; + struct nmb_name *question = &nmb->question.question_name; + struct name_record *namerec = NULL; + + DEBUG(3,("wins_process_name_query: name query for name %s from IP %s\n", + namestr(question), inet_ntoa(p->ip) )); + + /* + * Special name code. If the queried name is *<1b> then search + * the entire WINS database and return a list of all the IP addresses + * registered to any <1b> name. This is to allow domain master browsers + * to discover other domains that may not have a presence on their subnet. + */ + + if(strequal( question->name, "*") && (question->name_type == 0x1b)) + { + process_wins_dmb_query_request( subrec, p); + return; + } + + namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME); + + if(namerec != NULL) + { + /* + * If it's a DNSFAIL_NAME then reply name not found. + */ + + if(namerec->source == DNSFAIL_NAME) + { + DEBUG(3,("wins_process_name_query: name query for name %s returning DNS fail.\n", + namestr(question) )); + send_wins_name_query_response(NAM_ERR, p, namerec); + return; + } + + /* + * If the name has expired then reply name not found. + */ + + if((namerec->death_time != PERMANENT_TTL) && (namerec->death_time < p->timestamp)) + { + DEBUG(3,("wins_process_name_query: name query for name %s - name expired. Returning fail.\n", + namestr(question) )); + send_wins_name_query_response(NAM_ERR, p, namerec); + return; + } + + DEBUG(3,("wins_process_name_query: name query for name %s returning first IP %s.\n", + namestr(question), inet_ntoa(namerec->ip[0]) )); + + send_wins_name_query_response(0, p, namerec); + return; + } + + /* + * Name not found in WINS - try a dns query if it's a 0x20 name. + */ + + if(lp_dns_proxy() && (question->name_type == 0x20)) + { + + DEBUG(3,("wins_process_name_query: name query for name %s not found - doing dns lookup.\n", + namestr(question) )); + + queue_dns_query(p, question, &namerec); + return; + } + + /* + * Name not found - return error. + */ + + send_wins_name_query_response(NAM_ERR, p, NULL); +} + +/**************************************************************************** +Send a WINS name release response. +**************************************************************************/ + +static void send_wins_name_release_response(int rcode, struct packet_struct *p) +{ + struct nmb_packet *nmb = &p->packet.nmb; + char rdata[6]; + + memcpy(&rdata[0], &nmb->additional->rdata[0], 6); + + reply_netbios_packet(p, /* Packet to reply to. */ + rcode, /* Result code. */ + NMB_REL, /* nmbd type code. */ + NMB_NAME_RELEASE_OPCODE, /* opcode. */ + 0, /* ttl. */ + rdata, /* data to send. */ + 6); /* data length. */ +} + +/*********************************************************************** + Deal with a name release. +***********************************************************************/ + +void wins_process_name_release_request(struct subnet_record *subrec, + struct packet_struct *p) +{ + struct nmb_packet *nmb = &p->packet.nmb; + struct nmb_name *question = &nmb->question.question_name; + BOOL bcast = nmb->header.nm_flags.bcast; + uint16 nb_flags = get_nb_flags(nmb->additional->rdata); + struct name_record *namerec = NULL; + struct in_addr from_ip; + BOOL releasing_group_name = (nb_flags & NB_GROUP) ? True : False;; + + putip((char *)&from_ip,&nmb->additional->rdata[2]); + + if(bcast) + { + /* + * We should only get unicast name registration packets here. + * Anyone trying to register broadcast should not be going to a WINS + * server. Log an error here. + */ + + DEBUG(0,("wins_process_name_release_request: broadcast name registration request \ +received for name %s from IP %s on subnet %s. Error - should not be sent to WINS server\n", + namestr(question), inet_ntoa(from_ip), subrec->subnet_name)); + return; + } + + DEBUG(3,("wins_process_name_release_request: %s name release for name %s \ +IP %s\n", releasing_group_name ? "Group" : "Unique", namestr(question), inet_ntoa(from_ip) )); + + /* + * Deal with policy regarding 0x1d names. + */ + + if(!releasing_group_name && (question->name_type == 0x1d)) + { + DEBUG(3,("wins_process_name_release_request: Ignoring request \ +to release name %s from IP %s.", namestr(question), inet_ntoa(p->ip) )); + send_wins_name_release_response(0, p); + return; + } + + /* + * See if the name already exists. + */ + + namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME); + + if((namerec == NULL) || ((namerec != NULL) && (namerec->source != REGISTER_NAME)) ) + { + send_wins_name_release_response(NAM_ERR, p); + return; + } + + /* + * Check that the sending machine has permission to release this name. + * If it's a group name not ending in 0x1c then just say yes and let + * the group time out. + */ + + if(releasing_group_name && (question->name_type != 0x1c)) + { + send_wins_name_release_response(0, p); + return; + } + + /* + * Check that the releasing node is on the list of IP addresses + * for this name. Disallow the release if not. + */ + + if(!find_ip_in_name_record(namerec, from_ip)) + { + DEBUG(3,("wins_process_name_release_request: Refusing request to \ +release name %s as IP %s is not one of the known IP's for this name.\n", + namestr(question), inet_ntoa(from_ip) )); + send_wins_name_release_response(NAM_ERR, p); + return; + } + + /* + * Release the name and then remove the IP from the known list. + */ + + send_wins_name_release_response(0, p); + remove_ip_from_name_record(namerec, from_ip); + + /* + * Remove the name entirely if no IP addresses left. + */ + if (namerec->num_ips == 0) + remove_name_from_namelist(subrec, namerec); + +} + +/******************************************************************* + WINS time dependent processing. +******************************************************************/ + +void initiate_wins_processing(time_t t) +{ + static time_t lasttime = 0; + + if (!lasttime) + lasttime = t; + if (t - lasttime < 5) + return; + + if(!lp_we_are_a_wins_server()) + return; + + expire_names_on_subnet(wins_server_subnet, t); + + if(wins_server_subnet->namelist_changed) + wins_write_database(); + + wins_server_subnet->namelist_changed = False; +} + +/******************************************************************* + Write out the current WINS database. +******************************************************************/ + +void wins_write_database(void) +{ + struct name_record *namerec; + fstring fname, fnamenew; + + FILE *fp; + + if(!lp_we_are_a_wins_server()) + return; + + fstrcpy(fname,lp_lockdir()); + trim_string(fname,NULL,"/"); + strcat(fname,"/"); + strcat(fname,WINS_LIST); + fstrcpy(fnamenew,fname); + strcat(fnamenew,"."); + + if((fp = fopen(fnamenew,"w")) == NULL) + { + DEBUG(0,("wins_write_database: Can't open %s. Error was %s\n", fnamenew, strerror(errno))); + return; + } + + DEBUG(4,("wins_write_database: Dump of WINS name list.\n")); + + for (namerec = wins_server_subnet->namelist; namerec; namerec = namerec->next) + { + int i; + struct tm *tm; + + DEBUG(4,("%-19s ", namestr(&namerec->name) )); + + if(namerec->death_time != PERMANENT_TTL) + { + tm = LocalTime(&namerec->death_time); + DEBUG(4,("TTL = %s", asctime(tm) )); + } + else + DEBUG(4,("TTL = PERMANENT\t")); + + for (i = 0; i < namerec->num_ips; i++) + DEBUG(4,("%15s ", inet_ntoa(namerec->ip[i]) )); + DEBUG(4,("%2x\n", namerec->nb_flags )); + + if (namerec->source == REGISTER_NAME) + { + fprintf(fp, "%s#%02x %ld ", + namerec->name.name,namerec->name.name_type, /* Ignore scope. */ + namerec->death_time); + + for (i = 0; i < namerec->num_ips; i++) + fprintf(fp, "%s ", inet_ntoa(namerec->ip[i])); + fprintf(fp, "%2xR\n", namerec->nb_flags); + } + } + + fclose(fp); + unlink(fname); + chmod(fnamenew,0644); + rename(fnamenew,fname); +} diff --git a/source3/nmbd/nmbd_workgroupdb.c b/source3/nmbd/nmbd_workgroupdb.c new file mode 100644 index 0000000000..828e29a024 --- /dev/null +++ b/source3/nmbd/nmbd_workgroupdb.c @@ -0,0 +1,356 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NBT netbios routines and daemon - version 2 + Copyright (C) Andrew Tridgell 1994-1997 + Copyright (C) Luke Kenneth Casson Leighton 1994-1997 + Copyright (C) Jeremy Allison 1994-1997 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "includes.h" +#include "smb.h" + +extern int ClientNMB; + +extern int DEBUGLEVEL; + +extern pstring myname; +extern fstring myworkgroup; +extern char **my_netbios_names; +extern uint16 samba_nb_type; +extern struct in_addr ipzero; + +int workgroup_count = 0; /* unique index key: one for each workgroup */ + +/**************************************************************************** + Add a workgroup into the list. + **************************************************************************/ + +static void add_workgroup(struct subnet_record *subrec, struct work_record *work) +{ + struct work_record *w2; + + work->subnet = subrec; + + if (!subrec->workgrouplist) + { + subrec->workgrouplist = work; + work->prev = NULL; + work->next = NULL; + return; + } + + for (w2 = subrec->workgrouplist; w2->next; w2 = w2->next) + ; + + w2->next = work; + work->next = NULL; + work->prev = w2; + + subrec->work_changed = True; +} + +/**************************************************************************** + Create an empty workgroup. + **************************************************************************/ + +static struct work_record *create_workgroup(char *name, int ttl) +{ + struct work_record *work; + struct subnet_record *subrec; + int t = -1; + + if((work = (struct work_record *)malloc(sizeof(*work))) == NULL) + { + DEBUG(0,("create_workgroup: malloc fail !\n")); + return NULL; + } + bzero((char *)work, sizeof(*work)); + + StrnCpy(work->work_group,name,sizeof(work->work_group)-1); + work->serverlist = NULL; + + work->RunningElection = False; + work->ElectionCount = 0; + work->announce_interval = 0; + work->needelection = False; + work->needannounce = True; + work->lastannounce_time = time(NULL); + work->mst_state = lp_local_master() ? MST_POTENTIAL : MST_NONE; + work->dom_state = DOMAIN_NONE; + work->log_state = LOGON_NONE; + + work->death_time = (ttl != PERMANENT_TTL) ? time(NULL)+(ttl*3) : PERMANENT_TTL; + + /* Make sure all token representations of workgroups are unique. */ + + for (subrec = FIRST_SUBNET; subrec && (t == -1); + subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec)) + { + struct work_record *w; + for (w = subrec->workgrouplist; w && t == -1; w = w->next) + { + if (strequal(w->work_group, work->work_group)) + t = w->token; + } + } + + if (t == -1) + work->token = ++workgroup_count; + else + work->token = t; + + /* No known local master browser as yet. */ + *work->local_master_browser_name = '\0'; + + /* No known domain master browser as yet. */ + *work->dmb_name.name = '\0'; + putip((char *)&work->dmb_addr, &ipzero); + + /* WfWg uses 01040b01 */ + /* Win95 uses 01041501 */ + /* NTAS uses ???????? */ + work->ElectionCriterion = (MAINTAIN_LIST)|(ELECTION_VERSION<<8); + work->ElectionCriterion |= (lp_os_level() << 24); + if (lp_domain_master()) + work->ElectionCriterion |= 0x80; + + return work; +} + +/******************************************************************* + Remove a workgroup. + ******************************************************************/ + +static struct work_record *remove_workgroup_from_subnet(struct subnet_record *subrec, + struct work_record *work) +{ + struct work_record *ret_work = NULL; + + DEBUG(3,("remove_workgroup: Removing workgroup %s\n", work->work_group)); + + ret_work = work->next; + + remove_all_servers(work); + + if (!work->serverlist) + { + if (work->prev) + work->prev->next = work->next; + if (work->next) + work->next->prev = work->prev; + + if (subrec->workgrouplist == work) + subrec->workgrouplist = work->next; + + free((char *)work); + } + + subrec->work_changed = True; + + return ret_work; +} + + +/**************************************************************************** + Find a workgroup in the workgroup list of a subnet. + **************************************************************************/ + +struct work_record *find_workgroup_on_subnet(struct subnet_record *subrec, + fstring name) +{ + struct work_record *ret; + + DEBUG(4, ("find_workgroup_on_subnet: workgroup search for %s on subnet %s: ", + name, subrec->subnet_name)); + + for (ret = subrec->workgrouplist; ret; ret = ret->next) + { + if (!strcmp(ret->work_group,name)) + { + DEBUG(4, ("found\n")); + return(ret); + } + } + DEBUG(4, ("not found\n")); + return NULL; +} + +/**************************************************************************** + Create a workgroup in the workgroup list of the subnet. + **************************************************************************/ + +struct work_record *create_workgroup_on_subnet(struct subnet_record *subrec, + fstring name, int ttl) +{ + struct work_record *work = NULL; + + DEBUG(4,("create_workgroup_on_subnet: creating group %s on subnet %s\n", + name, subrec->subnet_name)); + + if ((work = create_workgroup(name, ttl))) + { + add_workgroup(subrec, work); + + subrec->work_changed = True; + + return(work); + } + + return NULL; +} + +/**************************************************************************** + Update a workgroup ttl. + **************************************************************************/ + +void update_workgroup_ttl(struct work_record *work, int ttl) +{ + if(work->death_time != PERMANENT_TTL) + work->death_time = time(NULL)+(ttl*3); + work->subnet->work_changed = True; +} + +/**************************************************************************** + Fail function called if we cannot register the WORKGROUP<0> and + WORKGROUP<1e> names on the net. +**************************************************************************/ + +static void fail_register(struct subnet_record *subrec, struct response_record *rrec, + struct nmb_name *nmbname) +{ + DEBUG(0,("fail_register: Failed to register name %s on subnet %s.\n", + namestr(nmbname), subrec->subnet_name)); +} + +/**************************************************************************** + If the workgroup is our primary workgroup, add the required names to it. +**************************************************************************/ + +void initiate_myworkgroup_startup(struct subnet_record *subrec, struct work_record *work) +{ + int i; + + if(!strequal(myworkgroup, work->work_group)) + return; + + /* If this is a broadcast subnet then start elections on it + if we are so configured. */ + + if ((subrec != unicast_subnet) && (subrec != remote_broadcast_subnet) && + (subrec != wins_server_subnet) && lp_preferred_master() && + lp_local_master()) + { + DEBUG(3, ("initiate_myworkgroup_startup: preferred master startup for \ +workgroup %s on subnet %s\n", work->work_group, subrec->subnet_name)); + work->needelection = True; + work->ElectionCriterion |= (1<<3); + } + + /* Register the WORKGROUP<0> and WORKGROUP<1e> names on the network. */ + + register_name(subrec,myworkgroup,0x0,samba_nb_type|NB_GROUP, + NULL, + fail_register,NULL); + + register_name(subrec,myworkgroup,0x1e,samba_nb_type|NB_GROUP, + NULL, + fail_register,NULL); + + for( i = 0; my_netbios_names[i]; i++) + { + char *name = my_netbios_names[i]; + int stype = lp_default_server_announce() | (lp_local_master() ? + SV_TYPE_POTENTIAL_BROWSER : 0 ); + + if(!strequal(myname, name)) + stype &= ~(SV_TYPE_MASTER_BROWSER|SV_TYPE_POTENTIAL_BROWSER| + SV_TYPE_DOMAIN_MASTER|SV_TYPE_DOMAIN_MEMBER); + + create_server_on_workgroup(work,name,stype|SV_TYPE_LOCAL_LIST_ONLY, + PERMANENT_TTL, lp_serverstring()); + DEBUG(3,("initiate_myworkgroup_startup: Added server name entry %s \ +on subnet %s\n", name, subrec->subnet_name)); + } +} + +/**************************************************************************** + Dump a copy of the workgroup database into the log file. + **************************************************************************/ + +void dump_workgroups(void) +{ + struct subnet_record *subrec; + + for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec)) + { + if (subrec->workgrouplist) + { + struct work_record *work; + + DEBUG(4,("dump_workgroups: dump workgroup on subnet %15s: ", subrec->subnet_name)); + DEBUG(4,(" netmask=%15s:\n", inet_ntoa(subrec->mask_ip))); + + for (work = subrec->workgrouplist; work; work = work->next) + { + DEBUG(4,("\t%s(%d) current master browser = %s\n", work->work_group, + work->token, + *work->local_master_browser_name ? work->local_master_browser_name : "UNKNOWN" )); + if (work->serverlist) + { + struct server_record *servrec; + for (servrec = work->serverlist; servrec; servrec = servrec->next) + { + DEBUG(4,("\t\t%s %8x (%s)\n", + servrec->serv.name, servrec->serv.type, servrec->serv.comment)); + } + } + } + } + } +} + +/**************************************************************************** + Expire any dead servers on all workgroups. If the workgroup has expired + remove it. + **************************************************************************/ + +void expire_workgroups_and_servers(time_t t) +{ + struct subnet_record *subrec; + + for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec)) + { + struct work_record *work; + struct work_record *nextwork; + + for (work = subrec->workgrouplist; work; work = nextwork) + { + nextwork = work->next; + expire_servers(work, t); + + if ((work->serverlist == NULL) && (work->death_time != PERMANENT_TTL) && + ((t == -1) || (work->death_time < t))) + { + DEBUG(3,("expire_workgroups_and_servers: Removing timed out workgroup %s\n", + work->work_group)); + remove_workgroup_from_subnet(subrec, work); + } + } + } +} diff --git a/source3/nmbsync.c b/source3/nmbsync.c deleted file mode 100644 index c1db37ff5c..0000000000 --- a/source3/nmbsync.c +++ /dev/null @@ -1,117 +0,0 @@ -/* - Unix SMB/Netbios implementation. - Version 1.9. - NBT netbios routines to synchronise browse lists - 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -#include "includes.h" - -extern int DEBUGLEVEL; - -static struct work_record *call_w; -static struct subnet_record *call_d; - -/******************************************************************* - This is the NetServerEnum callback - ******************************************************************/ -static void callback(char *sname, uint32 stype, char *comment) -{ - struct work_record *w = call_w; - - stype &= ~SV_TYPE_LOCAL_LIST_ONLY; - - if (stype & SV_TYPE_DOMAIN_ENUM) { - /* creates workgroup on remote subnet */ - if ((w = find_workgroupstruct(call_d,sname,True))) { - announce_request(w, call_d->bcast_ip); - } - } - - if (w) { - add_server_entry(call_d,w,sname,stype, - lp_max_ttl(),comment,False); - } -} - - -/******************************************************************* - synchronise browse lists with another browse server. - - log in on the remote server's SMB port to their IPC$ service, - do a NetServerEnum and update our server and workgroup databases. - ******************************************************************/ -void sync_browse_lists(struct subnet_record *d, struct work_record *work, - char *name, int nm_type, struct in_addr ip, BOOL local) -{ - extern fstring local_machine; - static struct cli_state cli; - uint32 local_type = local ? SV_TYPE_LOCAL_LIST_ONLY : 0; - - if (!d || !work ) return; - - if(d != wins_client_subnet) { - DEBUG(0,("sync_browse_lists: ERROR sync requested on non-WINS subnet.\n")); - return; - } - - DEBUG(2,("sync_browse_lists: Sync browse lists with %s for %s %s\n", - name, work->work_group, inet_ntoa(ip))); - - if (!cli_initialise(&cli) || !cli_connect(&cli, name, &ip)) { - DEBUG(1,("Failed to start browse sync with %s\n", name)); - } - - if (!cli_session_request(&cli, name, nm_type, local_machine)) { - DEBUG(1,("%s rejected the browse sync session\n",name)); - cli_shutdown(&cli); - return; - } - - if (!cli_negprot(&cli)) { - DEBUG(1,("%s rejected the negprot\n",name)); - cli_shutdown(&cli); - return; - } - - if (!cli_session_setup(&cli, "", "", 1, "", 0, work->work_group)) { - DEBUG(1,("%s rejected the browse sync sessionsetup\n", - name)); - cli_shutdown(&cli); - return; - } - - if (!cli_send_tconX(&cli, "IPC$", "IPC", "", 1)) { - DEBUG(1,("%s refused browse sync IPC$ connect\n", name)); - cli_shutdown(&cli); - return; - } - - call_w = work; - call_d = d; - - cli_NetServerEnum(&cli, work->work_group, - local_type|SV_TYPE_DOMAIN_ENUM, - callback); - - cli_NetServerEnum(&cli, work->work_group, - local?SV_TYPE_LOCAL_LIST_ONLY:SV_TYPE_ALL, - callback); - - cli_shutdown(&cli); -} diff --git a/source3/param/loadparm.c b/source3/param/loadparm.c index a8e70717b6..76618e9a79 100644 --- a/source3/param/loadparm.c +++ b/source3/param/loadparm.c @@ -157,6 +157,8 @@ typedef struct int syslog; int os_level; int max_ttl; + int max_wins_ttl; + int min_wins_ttl; int ReadSize; int shmem_size; int client_code_page; @@ -503,6 +505,8 @@ static struct parm_struct {"client code page", P_INTEGER, P_GLOBAL, &Globals.client_code_page, NULL, NULL}, {"os level", P_INTEGER, P_GLOBAL, &Globals.os_level, NULL, NULL}, {"max ttl", P_INTEGER, P_GLOBAL, &Globals.max_ttl, NULL, NULL}, + {"max wins ttl", P_INTEGER, P_GLOBAL, &Globals.max_wins_ttl, NULL, NULL}, + {"min wins ttl", P_INTEGER, P_GLOBAL, &Globals.min_wins_ttl, NULL, NULL}, {"dns proxy", P_BOOL, P_GLOBAL, &Globals.bDNSproxy, NULL, NULL}, {"wins support", P_BOOL, P_GLOBAL, &Globals.bWINSsupport, NULL, NULL}, {"wins proxy", P_BOOL, P_GLOBAL, &Globals.bWINSproxy, NULL, NULL}, @@ -691,7 +695,9 @@ static void init_globals(void) Globals.syslog = 1; Globals.bSyslogOnly = False; Globals.os_level = 0; - Globals.max_ttl = 60*60*4; /* 2 hours default */ + Globals.max_ttl = 60*60*4; /* 4 hours default */ + Globals.max_wins_ttl = 60*60*24*3; /* 3 days default */ + Globals.min_wins_ttl = 60*60*6; /* 6 hours default */ Globals.ReadSize = 16*1024; Globals.shmem_size = SHMEM_SIZE; Globals.announce_as = ANNOUNCE_AS_NT; @@ -921,6 +927,8 @@ FN_GLOBAL_BOOL(lp_bind_interfaces_only,&Globals.bBindInterfacesOnly) FN_GLOBAL_INTEGER(lp_os_level,&Globals.os_level) FN_GLOBAL_INTEGER(lp_max_ttl,&Globals.max_ttl) +FN_GLOBAL_INTEGER(lp_max_wins_ttl,&Globals.max_wins_ttl) +FN_GLOBAL_INTEGER(lp_min_wins_ttl,&Globals.max_wins_ttl) FN_GLOBAL_INTEGER(lp_max_log_size,&Globals.max_log_size) FN_GLOBAL_INTEGER(lp_mangledstack,&Globals.mangled_stack) FN_GLOBAL_INTEGER(lp_maxxmit,&Globals.max_xmit) diff --git a/source3/utils/nmblookup.c b/source3/utils/nmblookup.c index 1f74d7a130..b14887afab 100644 --- a/source3/utils/nmblookup.c +++ b/source3/utils/nmblookup.c @@ -186,9 +186,10 @@ int main(int argc,char *argv[]) for (i=optind;i\n",inet_ntoa(ip_list[j]),lookup, lookup_type); + + /* We can only do find_status if the ip address returned + was valid - ie. name_query returned true. + */ + if (find_status) { + printf("Looking up status of %s\n",inet_ntoa(ip_list[0])); + name_status(ServerFD,lookup,lookup_type,True,ip_list[0],NULL,NULL,NULL); + printf("\n"); + } + } else { + printf("name_query failed to find name %s\n", lookup); } } - + return(0); } -- cgit