From 7e8c60cfe54060860e5ce20b1c3b8ec6aa5c54da Mon Sep 17 00:00:00 2001 From: Samba Release Account Date: Sat, 29 Jun 1996 18:49:20 +0000 Subject: [PATCH] luke's first attempt at using cvs accidentally updated the Makefile updated the name database structure (again!). this time, there is one name database per local interface. there is also a pseudo-interface on ip 255.255.255.255. its purpose is to store WINS name entries. all the local interface name databases store SELF names only. the WINS name database stores non-special browser names. added wins.dat file: records WINS entries in ascii format. this is reloaded when nmbd restarts. added repeating code for response packets. timer is in seconds only at the moment. updated the response queue code to deal with samba registering with a WINS server a bit better (added more cases when a response isn't received). tidied up the response packet processing code and expire_response_queue() code. added cross references between response received and await-response expired code. added over-zealous code that checks all machines that register with samba as a WINS server (every 10 minutes i think): to see whether they are still alive or not (see rfc1001.txt) bug reported by terry@ren.pc.athabascau.ca: DNSFAILed names _stay_ as DNSFAIL, even though the machine may come back up and REGISTER. removed update_from_reg() function. it's not necessary, and it does too much. added code that announces on each local interface samba's ttl as zero and servertype as zero when nmbd is kill -TERMed first attempt at putting the first functionality of samba browsing back in (remote subnets should have samba appear in a workgroup specified through the lmhosts file) lots of other miscellaneous tidying up / chopping about. --- source/include/nameserv.h | 109 +++- source/include/proto.h | 659 ++++++++++++++++++++ source/include/smb.h | 9 + source/nameannounce.c | 196 +++--- source/namedb.c | 165 +++-- source/nameelect.c | 67 +- source/nameresp.c | 444 +++++++++----- source/nameserv.c | 1227 +++++++++++++++++++++++++++++-------- source/namework.c | 118 ++-- source/nmbd/nmbd.c | 29 +- source/nmbsync.c | 10 +- source/param/loadparm.c | 3 + source/script/mkproto.awk | 5 - source/utils/nmblookup.c | 2 +- 14 files changed, 2379 insertions(+), 664 deletions(-) diff --git a/source/include/nameserv.h b/source/include/nameserv.h index 8882948ff4c..03bb5215586 100644 --- a/source/include/nameserv.h +++ b/source/include/nameserv.h @@ -29,8 +29,12 @@ #define NMB_QUERY 0x20 #define NMB_STATUS 0x21 -#define NMB_REG 0x05 -#define NMB_REL 0x06 + +#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.17 */ +/* XXXX what about all the other types?? 0x1, 0x2, 0x3, 0x4, 0x8? */ #define NB_GROUP 0x80 #define NB_PERM 0x02 @@ -44,6 +48,8 @@ #define NB_FLGMSK 0x60 #define REFRESH_TIME (15*60) +#define NAME_POLL_REFRESH_TIME (5*60) +#define NAME_POLL_INTERVAL 15 #define NAME_PERMANENT(p) ((p) & NB_PERM) #define NAME_ACTIVE(p) ((p) & NB_ACTIVE) @@ -58,7 +64,6 @@ #define MSBROWSE "\001\002__MSBROWSE__\002" -enum name_search { FIND_SELF, FIND_GLOBAL }; enum name_source {STATUS_QUERY, LMHOSTS, REGISTER, SELF, DNS, DNSFAIL}; enum node_type {B_NODE=0, P_NODE=1, M_NODE=2, NBDD_NODE=3}; enum packet_type {NMB_PACKET, DGRAM_PACKET}; @@ -66,13 +71,14 @@ enum cmd_type { NAME_STATUS_MASTER_CHECK, NAME_STATUS_CHECK, - MASTER_SERVER_CHECK, - SERVER_CHECK, - FIND_MASTER, - CHECK_MASTER, NAME_REGISTER, NAME_RELEASE, - NAME_CONFIRM_QUERY + NAME_QUERY_CONFIRM, + NAME_QUERY_SYNC, + NAME_QUERY_MST_SRV_CHK, + NAME_QUERY_SRV_CHK, + NAME_QUERY_FIND_MST, + NAME_QUERY_MST_CHK }; /* a netbios name structure */ @@ -87,11 +93,15 @@ struct name_record { struct name_record *next; struct name_record *prev; - struct nmb_name name; - time_t death_time; - struct in_addr ip; - int nb_flags; - enum name_source source; + + struct nmb_name name; /* the netbios name */ + struct in_addr ip; /* ip address of host that owns this name */ + int nb_flags; /* netbios flags */ + + enum name_source source; /* where the name came from */ + + time_t death_time; /* time record must be removed (do not remove if 0) */ + time_t refresh_time; /* time record should be refreshed */ }; /* browse and backup server cache for synchronising browse list */ @@ -144,13 +154,63 @@ struct work_record uint32 ElectionCriterion; }; -/* a subnet structure. it contains a list of workgroups */ +/* initiated name queries recorded in this list to track any responses... */ +struct response_record +{ + struct response_record *next; + struct response_record *prev; + + uint16 response_id; + enum cmd_type cmd_type; + + int fd; + int quest_type; + struct nmb_name name; + int nb_flags; + time_t ttl; + + BOOL bcast; + BOOL recurse; + struct in_addr to_ip; + + int num_msgs; + + time_t repeat_time; + time_t repeat_interval; + 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). + + 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 +*/ + struct subnet_record { struct subnet_record *next; struct subnet_record *prev; - struct work_record *workgrouplist; + struct work_record *workgrouplist; /* list of workgroups */ + struct name_record *namelist; /* list of netbios names */ + struct response_record *responselist; /* list of responses expected */ struct in_addr bcast_ip; struct in_addr mask_ip; @@ -202,25 +262,6 @@ struct nmb_packet }; -/* initiated name queries recorded in this list to track any responses... */ -struct name_response_record -{ - struct name_response_record *next; - struct name_response_record *prev; - - uint16 response_id; - enum cmd_type cmd_type; - - int fd; - struct nmb_name name; - BOOL bcast; - BOOL recurse; - struct in_addr to_ip; - - time_t start_time; - int num_msgs; -}; - /* a datagram - this normally contains SMB data in the data[] array */ struct dgram_packet { struct { diff --git a/source/include/proto.h b/source/include/proto.h index e69de29bb2d..ce02be129fe 100644 --- a/source/include/proto.h +++ b/source/include/proto.h @@ -0,0 +1,659 @@ +/* This file is automatically generated with "make proto". DO NOT EDIT */ +BOOL check_access(int snum); +BOOL allow_access(char *deny_list,char *allow_list,struct from_host *client); +BOOL fromhost(int sock,struct from_host *f); +char *unix2dos_format(char *str,BOOL overwrite); +char *dos2unix_format(char *str, BOOL overwrite); +int interpret_character_set(char *str, int def); +void charset_initialise(void); +void add_char_string(char *s); +BOOL chat_with_program(char *passwordprogram,char *name,char *chatsequence); +BOOL chgpasswd(char *name,char *oldpass,char *newpass); +BOOL chgpasswd(char *name,char *oldpass,char *newpass); +void setup_pkt(char *outbuf); +void do_dir(char *inbuf,char *outbuf,char *Mask,int attribute,void (*fn)(),BOOL recurse_dir); +void cmd_help(void); +BOOL reopen_connection(char *inbuf,char *outbuf); +char *smb_errstr(char *inbuf); +void cli_setup_pkt(char *outbuf); +BOOL cli_receive_trans_response(char *inbuf,int trans,int *data_len, + int *param_len, char **data,char **param); +BOOL cli_send_session_request(char *inbuf, char *outbuf); +BOOL cli_send_login(char *inbuf, char *outbuf, BOOL start_session, BOOL use_setup); +void cli_send_logout(void); +BOOL cli_call_api(int prcnt,int drcnt,int mprcnt,int mdrcnt,int *rprcnt, + int *rdrcnt, char *param,char *data, char **rparam,char **rdata); +BOOL cli_send_trans_request(char *outbuf, int trans, char *name, int fid, int flags, + char *data,char *param,uint16 *setup, int ldata,int lparam, + int lsetup,int mdata,int mparam,int msetup); +BOOL cli_open_sockets(int port); +BOOL cli_reopen_connection(char *inbuf,char *outbuf); +char *smb_errstr(char *inbuf); +int strslashcmp(const char *s1, const char *s2); +void cmd_block(void); +void cmd_tarmode(void); +void cmd_setmode(void); +void cmd_tar(char *inbuf, char *outbuf); +int process_tar(char *inbuf, char *outbuf); +int clipfind(char **aret, int ret, char *tok); +int tar_parseargs(int argc, char *argv[], char *Optarg, int Optind); +void init_dptrs(void); +char *dptr_path(int key); +char *dptr_wcard(int key); +BOOL dptr_set_wcard(int key, char *wcard); +BOOL dptr_set_attr(int key, uint16 attr); +uint16 dptr_attr(int key); +void dptr_close(int key); +void dptr_closecnum(int cnum); +void dptr_idlecnum(int cnum); +void dptr_closepath(char *path,int pid); +int dptr_create(int cnum,char *path, BOOL expect_close,int pid); +BOOL dptr_fill(char *buf1,unsigned int key); +BOOL dptr_zero(char *buf); +void *dptr_fetch(char *buf,int *num); +void *dptr_fetch_lanman2(char *params,int dptr_num); +BOOL dir_check_ftype(int cnum,int mode,struct stat *st,int dirtype); +BOOL get_dir_entry(int cnum,char *mask,int dirtype,char *fname,int *size,int *mode,time_t *date,BOOL check_descend); +void *OpenDir(char *name); +void CloseDir(void *p); +char *ReadDirName(void *p); +BOOL SeekDir(void *p,int pos); +int TellDir(void *p); +void DirCacheAdd(char *path,char *name,char *dname,int snum); +char *DirCacheCheck(char *path,char *name,int snum); +void DirCacheFlush(int snum); +void fault_setup(void (*fn)()); +char *getsmbpass(char *prompt) ; +void load_interfaces(void); +void add_subnet_interfaces(void); +void iface_set_default(char *ip,char *bcast,char *nmask); +BOOL ismyip(struct in_addr ip); +BOOL ismybcast(struct in_addr bcast); +int iface_count(void); +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); +struct in_addr *iface_ip(struct in_addr ip); +int reply_trans(char *inbuf,char *outbuf); +int interpret_coding_system(char *str, int def); +char *lp_string(char *s); +char *lp_logfile(void); +char *lp_smbrun(void); +char *lp_configfile(void); +char *lp_smb_passwd_file(void); +char *lp_serverstring(void); +char *lp_printcapname(void); +char *lp_lockdir(void); +char *lp_rootdir(void); +char *lp_defaultservice(void); +char *lp_msg_command(void); +char *lp_dfree_command(void); +char *lp_hosts_equiv(void); +char *lp_auto_services(void); +char *lp_passwd_program(void); +char *lp_passwd_chat(void); +char *lp_passwordserver(void); +char *lp_workgroup(void); +char *lp_domain_controller(void); +char *lp_username_map(void); +char *lp_character_set(void); +char *lp_logon_script(void); +char *lp_wins_server(void); +char *lp_interfaces(void); +char *lp_remote_interfaces(void); +BOOL lp_wins_support(void); +BOOL lp_wins_proxy(void); +BOOL lp_domain_master(void); +BOOL lp_domain_logons(void); +BOOL lp_preferred_master(void); +BOOL lp_load_printers(void); +BOOL lp_use_rhosts(void); +BOOL lp_getwdcache(void); +BOOL lp_readprediction(void); +BOOL lp_readbmpx(void); +BOOL lp_readraw(void); +BOOL lp_writeraw(void); +BOOL lp_null_passwords(void); +BOOL lp_strip_dot(void); +BOOL lp_encrypted_passwords(void); +BOOL lp_syslog_only(void); +BOOL lp_browse_list(void); +int lp_os_level(void); +int lp_max_ttl(void); +int lp_max_log_size(void); +int lp_mangledstack(void); +int lp_maxxmit(void); +int lp_maxmux(void); +int lp_maxpacket(void); +int lp_keepalive(void); +int lp_passwordlevel(void); +int lp_readsize(void); +int lp_deadtime(void); +int lp_maxprotocol(void); +int lp_security(void); +int lp_printing(void); +int lp_maxdisksize(void); +int lp_lpqcachetime(void); +int lp_syslog(void); +char *lp_preexec(int ); +char *lp_postexec(int ); +char *lp_rootpreexec(int ); +char *lp_rootpostexec(int ); +char *lp_servicename(int ); +char *lp_pathname(int ); +char *lp_dontdescend(int ); +char *lp_username(int ); +char *lp_guestaccount(int ); +char *lp_invalid_users(int ); +char *lp_valid_users(int ); +char *lp_admin_users(int ); +char *lp_printcommand(int ); +char *lp_lpqcommand(int ); +char *lp_lprmcommand(int ); +char *lp_lppausecommand(int ); +char *lp_lpresumecommand(int ); +char *lp_printername(int ); +char *lp_hostsallow(int ); +char *lp_hostsdeny(int ); +char *lp_magicscript(int ); +char *lp_magicoutput(int ); +char *lp_comment(int ); +char *lp_force_user(int ); +char *lp_force_group(int ); +char *lp_readlist(int ); +char *lp_writelist(int ); +char *lp_volume(int ); +char *lp_mangled_map(int ); +BOOL lp_alternate_permissions(int ); +BOOL lp_revalidate(int ); +BOOL lp_casesensitive(int ); +BOOL lp_preservecase(int ); +BOOL lp_shortpreservecase(int ); +BOOL lp_casemangle(int ); +BOOL lp_status(int ); +BOOL lp_hide_dot_files(int ); +BOOL lp_browseable(int ); +BOOL lp_readonly(int ); +BOOL lp_no_set_dir(int ); +BOOL lp_guest_ok(int ); +BOOL lp_guest_only(int ); +BOOL lp_print_ok(int ); +BOOL lp_postscript(int ); +BOOL lp_map_hidden(int ); +BOOL lp_map_archive(int ); +BOOL lp_locking(int ); +BOOL lp_strict_locking(int ); +BOOL lp_share_modes(int ); +BOOL lp_onlyuser(int ); +BOOL lp_manglednames(int ); +BOOL lp_widelinks(int ); +BOOL lp_syncalways(int ); +BOOL lp_map_system(int ); +BOOL lp_delete_readonly(int ); +int lp_create_mode(int ); +int lp_max_connections(int ); +int lp_defaultcase(int ); +int lp_minprintspace(int ); +char lp_magicchar(int ); +BOOL lp_add_home(char *pszHomename, int iDefaultService, char *pszHomedir); +int lp_add_service(char *pszService, int iDefaultService); +BOOL lp_add_printer(char *pszPrintername, int iDefaultService); +BOOL lp_file_list_changed(void); +BOOL lp_snum_ok(int iService); +BOOL lp_loaded(void); +void lp_killunused(BOOL (*snumused)(int )); +BOOL lp_load(char *pszFname,BOOL global_only); +int lp_numservices(void); +void lp_dump(void); +int lp_servicenumber(char *pszServiceName); +char *my_workgroup(void); +char *volume_label(int snum); +BOOL fcntl_lock(int fd,int op,uint32 offset,uint32 count,int type); +int file_lock(char *name,int timeout); +void file_unlock(int fd); +BOOL is_locked(int fnum,int cnum,uint32 count,uint32 offset); +BOOL do_lock(int fnum,int cnum,uint32 count,uint32 offset,int *eclass,uint32 *ecode); +BOOL do_unlock(int fnum,int cnum,uint32 count,uint32 offset,int *eclass,uint32 *ecode); +int get_share_mode_by_fnum(int cnum,int fnum,int *pid); +int get_share_mode_byname(int cnum,char *fname,int *pid); +int get_share_mode(int cnum,struct stat *sbuf,int *pid); +void del_share_mode(int fnum); +BOOL set_share_mode(int fnum,int mode); +void clean_share_files(void); +int str_checksum(char *s); +BOOL is_8_3(char *fname); +void create_mangled_stack(int size); +BOOL check_mangled_stack(char *s); +BOOL is_mangled(char *s); +void mangle_name_83(char *s); +BOOL name_map_mangle(char *OutName,BOOL need83,int snum); +int reply_sends(char *inbuf,char *outbuf); +int reply_sendstrt(char *inbuf,char *outbuf); +int reply_sendtxt(char *inbuf,char *outbuf); +int reply_sendend(char *inbuf,char *outbuf); +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 announce_backup(void); +void announce_host(void); +void announce_master(void); +struct work_record *remove_workgroup(struct subnet_record *d, + struct work_record *work); +void expire_browse_cache(time_t t); +struct work_record *find_workgroupstruct(struct subnet_record *d, + fstring name, BOOL add); +struct subnet_record *find_subnet(struct in_addr source_ip); +void dump_workgroups(void); +struct subnet_record *add_subnet_entry(struct in_addr source_ip, + struct in_addr source_mask, + char *name, BOOL add, BOOL lmhosts); +struct browse_cache_record *add_browser_entry(char *name, int type, char *wg, + time_t ttl, struct in_addr ip); +void remove_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 write_browse_list(void); +void expire_servers(time_t t); +void query_refresh_names(void); +void check_master_browser(void); +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 become_nonmaster(struct subnet_record *d, struct work_record *work, + int remove_type); +void run_elections(void); +void process_election(struct packet_struct *p,char *buf); +BOOL check_elections(void); +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)()); +void expire_netbios_response_entries(void); +void reply_netbios_packet(struct packet_struct *p1,int trn_id, + int rcode,int opcode,BOOL recurse, + struct nmb_name *rr_name,int rr_type,int rr_class,int ttl, + char *data,int len); +void queue_netbios_pkt_wins(struct subnet_record *d, + int fd,int quest_type,enum cmd_type cmd, + char *name,int name_type, int nb_flags, time_t ttl, + BOOL bcast,BOOL recurse,struct in_addr to_ip); +void queue_netbios_packet(struct subnet_record *d, + int fd,int quest_type,enum cmd_type cmd,char *name, + int name_type,int nb_flags,time_t ttl, + BOOL bcast,BOOL recurse, struct in_addr to_ip); +struct response_record *find_response_record(struct subnet_record *d, + uint16 id); +void queue_packet(struct packet_struct *packet); +void run_packet_queue(); +void listen_for_packets(BOOL run_election); +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); +BOOL send_mailslot_reply(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 remove_name(struct subnet_record *d, struct name_record *n); +void dump_names(void); +void remove_netbios_name(struct subnet_record *d, char *name,int type, + enum name_source source, + struct in_addr ip); +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, BOOL wins); +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 do_announce_host(int command, + char *from, int from_type, struct in_addr from_ip, + char *to , int to_type , struct in_addr to_ip, + int update_count, time_t announce_interval, + char *server_name, int server_type, char *server_comment); +void load_netbios_names(void); +void add_my_names(void); +void remove_my_names(void); +void remove_my_servers(void); +void announce_server(struct subnet_record *d, struct work_record *work, + char *name, char *comment, time_t ttl, int server_type); +void refresh_my_names(time_t t); +void expire_names(time_t t); +void response_name_release(struct subnet_record *d,struct packet_struct *p); +void reply_name_release(struct packet_struct *p); +void response_name_reg(struct subnet_record *d,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 process_nmb(struct packet_struct *p); +void reset_server(char *name, int state, struct in_addr ip); +void tell_become_backup(void); +void do_browser_lists(void); +void sync_server(enum cmd_type cmd, char *serv_name, char *work_name, + int name_type, + struct in_addr ip); +void add_my_subnets(char *group); +BOOL same_context(struct dgram_packet *dgram); +BOOL listening_name(struct work_record *work, struct nmb_name *n); +void process_logon_packet(struct packet_struct *p,char *buf,int len); +BOOL listening_type(struct packet_struct *p, int command); +void process_browse_packet(struct packet_struct *p,char *buf,int len); +void process_dgram(struct packet_struct *p); +BOOL reload_services(BOOL test); +void debug_nmb_packet(struct packet_struct *p); +char *namestr(struct nmb_name *n); +void free_nmb_packet(struct nmb_packet *nmb); +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 send_packet(struct packet_struct *p); +struct packet_struct *receive_packet(int fd,enum packet_type type,int t); +int main(int argc,char *argv[]); +char *getsmbpass(char *pass); +void sync_browse_lists(struct subnet_record *d, struct work_record *work, + char *name, int nm_type, struct in_addr ip); +BOOL pm_process(char *pszFileName,BOOL (*sfunc)(char *),BOOL (*pfunc)(char *,char *)); +void generate_next_challenge(char *challenge); +BOOL set_challenge(char *challenge); +BOOL last_challenge(char *challenge); +int valid_uid(int uid); +user_struct *get_valid_user_struct(int uid); +void invalidate_uid(int uid); +char *validated_username(int vuid); +void register_uid(int uid,int gid, char *name,BOOL guest); +void add_session_user(char *user); +void dfs_unlogin(void); +BOOL password_check(char *password); +BOOL smb_password_check(char *password, unsigned char *part_passwd, unsigned char *c8); +BOOL password_ok(char *user,char *password, int pwlen, struct passwd *pwd, BOOL is_nt_password); +BOOL user_ok(char *user,int snum); +BOOL authorise_login(int snum,char *user,char *password, int pwlen, + BOOL *guest,BOOL *force,int vuid); +BOOL check_hosts_equiv(char *user); +BOOL server_cryptkey(char *buf); +BOOL server_validate(char *buf); +BOOL pcap_printername_ok(char *pszPrintername, char *pszPrintcapname); +void pcap_printer_fn(void (*fn)()); +int read_predict(int fd,int offset,char *buf,char **ptr,int num); +void do_read_prediction(); +void invalidate_read_prediction(int fd); +void lpq_reset(int snum); +void print_file(int fnum); +int get_printqueue(int snum,int cnum,print_queue_struct **queue, + print_status_struct *status); +void del_printqueue(int cnum,int snum,int jobid); +void status_printjob(int cnum,int snum,int jobid,int status); +BOOL disk_quotas(char *path, int *bsize, int *dfree, int *dsize); +char *Strstr(char *s, char *p); +time_t Mktime(struct tm *t); +int InNetGr(char *group,char *host,char *user,char *dom); +void *malloc_wrapped(int size,char *file,int line); +void *realloc_wrapped(void *ptr,int size,char *file,int line); +void free_wrapped(void *ptr,char *file,int line); +void *memcpy_wrapped(void *d,void *s,int l,char *fname,int line); +int reply_special(char *inbuf,char *outbuf); +int reply_tcon(char *inbuf,char *outbuf); +int reply_tcon_and_X(char *inbuf,char *outbuf,int length,int bufsize); +int reply_unknown(char *inbuf,char *outbuf); +int reply_ioctl(char *inbuf,char *outbuf); +int reply_sesssetup_and_X(char *inbuf,char *outbuf,int length,int bufsize); +int reply_chkpth(char *inbuf,char *outbuf); +int reply_getatr(char *inbuf,char *outbuf); +int reply_setatr(char *inbuf,char *outbuf); +int reply_dskattr(char *inbuf,char *outbuf); +int reply_search(char *inbuf,char *outbuf); +int reply_fclose(char *inbuf,char *outbuf); +int reply_open(char *inbuf,char *outbuf); +int reply_open_and_X(char *inbuf,char *outbuf,int length,int bufsize); +int reply_ulogoffX(char *inbuf,char *outbuf,int length,int bufsize); +int reply_mknew(char *inbuf,char *outbuf); +int reply_ctemp(char *inbuf,char *outbuf); +int reply_unlink(char *inbuf,char *outbuf); +int reply_readbraw(char *inbuf, char *outbuf); +int reply_lockread(char *inbuf,char *outbuf); +int reply_read(char *inbuf,char *outbuf); +int reply_read_and_X(char *inbuf,char *outbuf,int length,int bufsize); +int reply_writebraw(char *inbuf,char *outbuf); +int reply_writeunlock(char *inbuf,char *outbuf); +int reply_write(char *inbuf,char *outbuf,int dum1,int dum2); +int reply_write_and_X(char *inbuf,char *outbuf,int length,int bufsize); +int reply_lseek(char *inbuf,char *outbuf); +int reply_flush(char *inbuf,char *outbuf); +int reply_exit(char *inbuf,char *outbuf); +int reply_close(char *inbuf,char *outbuf); +int reply_writeclose(char *inbuf,char *outbuf); +int reply_lock(char *inbuf,char *outbuf); +int reply_unlock(char *inbuf,char *outbuf); +int reply_tdis(char *inbuf,char *outbuf); +int reply_echo(char *inbuf,char *outbuf); +int reply_printopen(char *inbuf,char *outbuf); +int reply_printclose(char *inbuf,char *outbuf); +int reply_printqueue(char *inbuf,char *outbuf); +int reply_printwrite(char *inbuf,char *outbuf); +int reply_mkdir(char *inbuf,char *outbuf); +int reply_rmdir(char *inbuf,char *outbuf); +int reply_mv(char *inbuf,char *outbuf); +int reply_copy(char *inbuf,char *outbuf); +int reply_setdir(char *inbuf,char *outbuf); +int reply_lockingX(char *inbuf,char *outbuf,int length,int bufsize); +int reply_readbmpx(char *inbuf,char *outbuf,int length,int bufsize); +int reply_writebmpx(char *inbuf,char *outbuf); +int reply_writebs(char *inbuf,char *outbuf); +int reply_setattrE(char *inbuf,char *outbuf); +int reply_getattrE(char *inbuf,char *outbuf); +mode_t unix_mode(int cnum,int dosmode); +int dos_mode(int cnum,char *path,struct stat *sbuf); +int dos_chmod(int cnum,char *fname,int dosmode,struct stat *st); +BOOL unix_convert(char *name,int cnum); +int disk_free(char *path,int *bsize,int *dfree,int *dsize); +int sys_disk_free(char *path,int *bsize,int *dfree,int *dsize); +BOOL check_name(char *name,int cnum); +void open_file(int fnum,int cnum,char *fname1,int flags,int mode); +void sync_file(int fnum); +void close_file(int fnum); +BOOL check_file_sharing(int cnum,char *fname); +void open_file_shared(int fnum,int cnum,char *fname,int share_mode,int ofun, + int mode,int *Access,int *action); +int seek_file(int fnum,int pos); +int read_file(int fnum,char *data,int pos,int mincnt,int maxcnt,int timeout,BOOL exact); +int write_file(int fnum,char *data,int n); +BOOL become_service(int cnum,BOOL do_chdir); +int find_service(char *service); +int cached_error_packet(char *inbuf,char *outbuf,int fnum,int line); +int unix_error_packet(char *inbuf,char *outbuf,int def_class,uint32 def_code,int line); +int error_packet(char *inbuf,char *outbuf,int error_class,uint32 error_code,int line); +BOOL snum_used(int snum); +BOOL reload_services(BOOL test); +int setup_groups(char *user, int uid, int gid, int *p_ngroups, + int **p_igroups, gid_t **p_groups); +int make_connection(char *service,char *user,char *password, int pwlen, char *dev,int vuid); +int find_free_file(void ); +int reply_corep(char *outbuf); +int reply_coreplus(char *outbuf); +int reply_lanman1(char *outbuf); +int reply_lanman2(char *outbuf); +int reply_nt1(char *outbuf); +void parse_connect(char *buf,char *service,char *user,char *password,int *pwlen,char *dev); +void close_cnum(int cnum, int uid); +BOOL yield_connection(int cnum,char *name,int max_connections); +BOOL claim_connection(int cnum,char *name,int max_connections,BOOL Clear); +void exit_server(char *reason); +void standard_sub(int cnum,char *s); +char *smb_fn_name(int type); +int chain_reply(int type,char *inbuf,char *inbuf2,char *outbuf,char *outbuf2,int size,int bufsize); +int construct_reply(char *inbuf,char *outbuf,int size,int bufsize); +void str_to_key(uchar *str,uchar *key); +void D1(uchar *k, uchar *d, uchar *out); +void E1(uchar *k, uchar *d, uchar *out); +void E_P16(uchar *p14,uchar *p16); +void E_P24(uchar *p21, uchar *c8, uchar *p24); +void SMBencrypt(uchar *passwd, uchar *c8, uchar *p24); +void E_md4hash(uchar *passwd, uchar *p16); +void SMBNTencrypt(uchar *passwd, uchar *c8, uchar *p24); +void Ucrit_addUsername(pstring username); +unsigned int Ucrit_checkUsername(pstring username); +void Ucrit_addPid(int pid); +unsigned int Ucrit_checkPid(int pid); +int sys_select(fd_set *fds,struct timeval *tval); +int sys_select(fd_set *fds,struct timeval *tval); +int sys_unlink(char *fname); +int sys_open(char *fname,int flags,int mode); +DIR *sys_opendir(char *dname); +int sys_stat(char *fname,struct stat *sbuf); +int sys_lstat(char *fname,struct stat *sbuf); +int sys_mkdir(char *dname,int mode); +int sys_rmdir(char *dname); +int sys_chdir(char *dname); +int sys_utime(char *fname,struct utimbuf *times); +int sys_rename(char *from, char *to); +int sys_chown(char *fname,int uid,int gid); +int sys_chroot(char *dname); +int main(int argc, char *argv[]); +void GetTimeOfDay(struct timeval *tval); +void TimeInit(void); +int TimeDiff(time_t t); +struct tm *LocalTime(time_t *t); +time_t interpret_long_date(char *p); +void put_long_date(char *p,time_t t); +void put_dos_date(char *buf,int offset,time_t unixdate); +void put_dos_date2(char *buf,int offset,time_t unixdate); +void put_dos_date3(char *buf,int offset,time_t unixdate); +time_t make_unix_date(void *date_ptr); +time_t make_unix_date2(void *date_ptr); +time_t make_unix_date3(void *date_ptr); +BOOL set_filetime(char *fname,time_t mtime); +char *timestring(void ); +int reply_findclose(char *inbuf,char *outbuf,int length,int bufsize); +int reply_findnclose(char *inbuf,char *outbuf,int length,int bufsize); +int reply_transs2(char *inbuf,char *outbuf,int length,int bufsize); +int reply_trans2(char *inbuf,char *outbuf,int length,int bufsize); +char *ufc_crypt(char *key,char *salt); +void init_uid(void); +BOOL become_guest(void); +BOOL become_user(int cnum, int uid); +BOOL unbecome_user(void ); +int smbrun(char *cmd,char *outfile); +char *get_home_dir(char *user); +void map_username(char *user); +struct passwd *Get_Pwnam(char *user,BOOL allow_change); +BOOL user_in_list(char *user,char *list); +void setup_logging(char *pname,BOOL interactive); +void reopen_logs(void); +BOOL is_a_socket(int fd); +BOOL next_token(char **ptr,char *buff,char *sep); +char **toktocliplist(int *ctok, char *sep); +void *MemMove(void *dest,void *src,int size); +void array_promote(char *array,int elsize,int element); +void set_socket_options(int fd, char *options); +void close_sockets(void ); +BOOL in_group(gid_t group, int current_gid, int ngroups, int *groups); +char *StrCpy(char *dest,char *src); +char *StrnCpy(char *dest,const char *src,int n); +void putip(void *dest,void *src); +int name_mangle(char *In,char *Out,char name_type); +BOOL file_exist(char *fname,struct stat *sbuf); +time_t file_modtime(char *fname); +BOOL directory_exist(char *dname,struct stat *st); +uint32 file_size(char *file_name); +char *attrib_string(int mode); +int StrCaseCmp(char *s, char *t); +int StrnCaseCmp(char *s, char *t, int n); +BOOL strequal(char *s1,char *s2); +BOOL strnequal(char *s1,char *s2,int n); +BOOL strcsequal(char *s1,char *s2); +void strlower(char *s); +void strupper(char *s); +void strnorm(char *s); +BOOL strisnormal(char *s); +void string_replace(char *s,char oldc,char newc); +void unix_format(char *fname); +void dos_format(char *fname); +void show_msg(char *buf); +int smb_len(char *buf); +void _smb_setlen(char *buf,int len); +void smb_setlen(char *buf,int len); +int set_message(char *buf,int num_words,int num_bytes,BOOL zero); +int smb_numwords(char *buf); +int smb_buflen(char *buf); +int smb_buf_ofs(char *buf); +char *smb_buf(char *buf); +int smb_offset(char *p,char *buf); +char *skip_string(char *buf,int n); +BOOL trim_string(char *s,char *front,char *back); +void dos_clean_name(char *s); +void unix_clean_name(char *s); +int ChDir(char *path); +char *GetWd(char *str); +BOOL reduce_name(char *s,char *dir,BOOL widelinks); +void expand_mask(char *Mask,BOOL doext); +BOOL strhasupper(char *s); +BOOL strhaslower(char *s); +int count_chars(char *s,char c); +void make_dir_struct(char *buf,char *mask,char *fname,unsigned int size,int mode,time_t date); +void close_low_fds(void); +int write_socket(int fd,char *buf,int len); +int read_udp_socket(int fd,char *buf,int len); +int set_blocking(int fd, BOOL set); +int read_with_timeout(int fd,char *buf,int mincnt,int maxcnt,long time_out,BOOL exact); +int read_max_udp(int fd,char *buffer,int bufsize,int maxtime); +int TvalDiff(struct timeval *tvalold,struct timeval *tvalnew); +BOOL send_keepalive(int client); +int read_data(int fd,char *buffer,int N); +int write_data(int fd,char *buffer,int N); +int transfer_file(int infd,int outfd,int n,char *header,int headlen,int align); +int read_smb_length(int fd,char *inbuf,int timeout); +BOOL receive_smb(int fd,char *buffer,int timeout); +BOOL send_smb(int fd,char *buffer); +char *name_ptr(char *buf,int ofs); +int name_extract(char *buf,int ofs,char *name); +int name_len(char *s); +BOOL send_one_packet(char *buf,int len,struct in_addr ip,int port,int type); +void msleep(int t); +BOOL in_list(char *s,char *list,BOOL casesensitive); +BOOL string_init(char **dest,char *src); +void string_free(char **s); +BOOL string_set(char **dest,char *src); +BOOL string_sub(char *s,char *pattern,char *insert); +BOOL do_match(char *str, char *regexp, int case_sig); +BOOL mask_match(char *str, char *regexp, int case_sig,BOOL trans2); +void become_daemon(void); +BOOL yesno(char *p); +char *fgets_slash(char *s2,int maxlen,FILE *f); +int set_filelen(int fd, long len); +int byte_checksum(char *buf,int len); +char *dirname_dos(char *path,char *buf); +void *Realloc(void *p,int size); +void Abort(void ); +BOOL get_myname(char *myname,struct in_addr *ip); +BOOL ip_equal(struct in_addr ip1,struct in_addr ip2); +int open_socket_in(int type, int port, int dlevel); +int open_socket_out(int type, struct in_addr *addr, int port ); +int interpret_protocol(char *str,int def); +int interpret_security(char *str,int def); +unsigned long interpret_addr(char *str); +struct in_addr *interpret_addr2(char *str); +BOOL zero_ip(struct in_addr ip); +void standard_sub_basic(char *s); +BOOL same_net(struct in_addr ip1,struct in_addr ip2,struct in_addr mask); +int PutUniCode(char *dst,char *src); +struct hostent *Get_Hostbyname(char *name); +BOOL process_exists(int pid); +char *uidtoname(int uid); +char *gidtoname(int gid); +void BlockSignals(BOOL block); +void ajt_panic(void); +char *readdirname(void *p); +int VT_Check(char *buffer); +int VT_Start_utmp(void); +int VT_Stop_utmp(void); +void VT_AtExit(void); +void VT_SigCLD(int sig); +void VT_SigEXIT(int sig); +int VT_Start(void); +int VT_Output(char *Buffer); +int VT_Input(char *Buffer,int Size); +void VT_Process(void); diff --git a/source/include/smb.h b/source/include/smb.h index b9dd13a802b..0e9c9983d25 100644 --- a/source/include/smb.h +++ b/source/include/smb.h @@ -381,6 +381,15 @@ struct server_info_struct }; +/* used for network interfaces */ +struct interface +{ + struct interface *next; + struct in_addr ip; + struct in_addr bcast; + struct in_addr nmask; +}; + /* this is used for smbstatus */ struct connect_record { diff --git a/source/nameannounce.c b/source/nameannounce.c index 9b0ef7067f2..5163c4aea9a 100644 --- a/source/nameannounce.c +++ b/source/nameannounce.c @@ -122,7 +122,8 @@ void announce_backup(void) int tok; if (!lastrun) lastrun = t; - if (t < lastrun + CHECK_TIME_ANNOUNCE_BACKUP * 60) return; + if (t < lastrun + CHECK_TIME_ANNOUNCE_BACKUP * 60) + return; lastrun = t; for (tok = 0; tok <= workgroup_count; tok++) @@ -178,20 +179,103 @@ void announce_backup(void) } +/**************************************************************************** + send a host announcement packet + **************************************************************************/ +void do_announce_host(int command, + char *from_name, int from_type, struct in_addr from_ip, + char *to_name , int to_type , struct in_addr to_ip, + int updatecount, time_t announce_interval, + 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) = 2; /* major version */ + CVAL(p,22) = 2; /* minor version */ + + SIVAL(p,23,server_type); + SSVAL(p,27,0xaa55); /* browse signature */ + SSVAL(p,29,1); /* browse version */ + + strcpy(p+31,server_comment); + p += 31; + p = skip_string(p,1); + + /* send the announcement */ + send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf, + PTR_DIFF(p,outbuf), + from_name, to_name, + from_type, to_type, + to_ip, from_ip); +} + + +/**************************************************************************** + 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) +{ + if (AM_MASTER(work)) + { + DEBUG(3,("sending local master announce to %s for %s(1e)\n", + inet_ntoa(d->bcast_ip),work->work_group)); + + do_announce_host(ANN_LocalMasterAnnouncement, + name , 0x00, d->myip, + work->work_group, 0x1e, d->bcast_ip, + updatecount, ttl*1000, + name, server_type, comment); + + DEBUG(3,("sending domain announce to %s for %s\n", + inet_ntoa(d->bcast_ip),work->work_group)); + + /* XXXX should we do a domain-announce-kill? */ + if (server_type != 0) + { + do_announce_host(ANN_DomainAnnouncement, + work->work_group, 0x00, d->myip, + MSBROWSE , 0x01, d->bcast_ip, + updatecount, ttl*1000, + name, server_type ? SV_TYPE_DOMAIN_ENUM : 0, comment); + } + } + else + { + DEBUG(3,("sending host announce to %s for %s(1d)\n", + inet_ntoa(d->bcast_ip),work->work_group)); + + do_announce_host(ANN_HostAnnouncement, + name , 0x00, d->myip, + work->work_group, 0x1d, d->bcast_ip, + updatecount, ttl*1000, + name, server_type, comment); + } +} + /**************************************************************************** construct a host announcement unicast **************************************************************************/ void announce_host(void) { time_t t = time(NULL); - pstring outbuf; - char *p; - char *namep; - char *stypep; - char *commentp; + struct subnet_record *d; pstring comment; char *my_name; - struct subnet_record *d; StrnCpy(comment, *ServerComment ? ServerComment : "NoComment", 43); @@ -225,9 +309,6 @@ void announce_host(void) work->lastannounce_time = t; - /* when announcing to remote networks we make sure we don't - claim to be any sort of special server, otherwise we may - stuff up their browsing */ if (!d->my_interface) { stype &= ~(SV_TYPE_POTENTIAL_BROWSER | SV_TYPE_MASTER_BROWSER | SV_TYPE_DOMAIN_MASTER | SV_TYPE_BACKUP_BROWSER | @@ -243,78 +324,15 @@ void announce_host(void) if (announce) { - bzero(outbuf,sizeof(outbuf)); - p = outbuf+1; - - CVAL(p,0) = updatecount; - /* ms - despite the spec */ - SIVAL(p,1,work->announce_interval*1000); - namep = p+5; - StrnCpy(namep,my_name,16); - strupper(namep); - CVAL(p,21) = 2; /* major version */ - CVAL(p,22) = 2; /* minor version */ - stypep = p+23; - SIVAL(p,23,stype); - SSVAL(p,27,0xaa55); /* browse signature */ - SSVAL(p,29,1); /* browse version */ - commentp = p+31; - strcpy(commentp,comment); - p = p+31; - p = skip_string(p,1); - - if (d->my_interface && AM_MASTER(work)) - { - SIVAL(stypep,0,work->ServerType); - - DEBUG(2,("sending local master announce to %s for %s\n", - inet_ntoa(d->bcast_ip),work->work_group)); - - CVAL(outbuf,0) = ANN_LocalMasterAnnouncement; - - send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf, - PTR_DIFF(p,outbuf), - my_name,work->work_group,0, - 0x1e,d->bcast_ip, - *iface_ip(d->bcast_ip)); - - DEBUG(2,("sending domain announce to %s for %s\n", - inet_ntoa(d->bcast_ip),work->work_group)); - - CVAL(outbuf,0) = ANN_DomainAnnouncement; - - StrnCpy(namep,work->work_group,15); - strupper(namep); - StrnCpy(commentp,myname,15); - strupper(commentp); - - SIVAL(stypep,0,(unsigned)0x80000000); - p = commentp + strlen(commentp) + 1; - - send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf, - PTR_DIFF(p,outbuf), - my_name,MSBROWSE,0,0x01,d->bcast_ip, - *iface_ip(d->bcast_ip)); - } - else - { - DEBUG(2,("sending host announce to %s for %s\n", - inet_ntoa(d->bcast_ip),work->work_group)); - - CVAL(outbuf,0) = ANN_HostAnnouncement; - - send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf, - PTR_DIFF(p,outbuf), - my_name,work->work_group,0,0x1d, - d->bcast_ip,*iface_ip(d->bcast_ip)); - } + announce_server(d,work,my_name,comment,work->announce_interval,stype); } - if (work->needannounce) { - work->needannounce = False; - break; - /* sorry: can't do too many announces. do some more later */ - } + if (work->needannounce) + { + work->needannounce = False; + break; + /* sorry: can't do too many announces. do some more later */ + } } } } @@ -328,7 +346,7 @@ void announce_host(void) least 15 minutes. this actually gets done in search_and_sync_workgroups() via the - MASTER_SERVER_CHECK command, if there is a response from the + NAME_QUERY_MST_SRV_CHK command, if there is a response from the name query initiated here. see response_name_query() **************************************************************************/ void announce_master(void) @@ -382,9 +400,9 @@ void announce_master(void) struct in_addr ip; ip = ipzero; - queue_netbios_pkt_wins(ClientNMB,NMB_QUERY, - MASTER_SERVER_CHECK, - work->work_group,0x1b,0, + queue_netbios_pkt_wins(d,ClientNMB,NMB_QUERY, + NAME_QUERY_MST_SRV_CHK, + work->work_group,0x1b,0,0, False, False, ip); } else @@ -392,9 +410,9 @@ void announce_master(void) struct subnet_record *d2; for (d2 = subnetlist; d2; d2 = d2->next) { - queue_netbios_packet(ClientNMB,NMB_QUERY, - MASTER_SERVER_CHECK, - work->work_group,0x1b,0, + queue_netbios_packet(d,ClientNMB,NMB_QUERY, + NAME_QUERY_MST_SRV_CHK, + work->work_group,0x1b,0,0, True, False, d2->bcast_ip); } } @@ -427,8 +445,8 @@ void announce_master(void) /* check the existence of a pdc for this workgroup, and if one exists at the specified ip, sync with it and announce ourselves as a master browser to it */ - queue_netbios_pkt_wins(ClientNMB, NMB_QUERY,MASTER_SERVER_CHECK, - work->work_group,0x1b, 0, + queue_netbios_pkt_wins(d,ClientNMB, NMB_QUERY,NAME_QUERY_MST_SRV_CHK, + work->work_group,0x1b, 0, 0, bcast, False, ip); } } diff --git a/source/namedb.c b/source/namedb.c index ea5b13a8009..a1442c0f03b 100644 --- a/source/namedb.c +++ b/source/namedb.c @@ -38,10 +38,17 @@ extern pstring myname; extern pstring scope; extern struct in_addr ipgrp; +extern struct in_addr ipzero; /* this is our browse master/backup cache database */ struct browse_cache_record *browserlist = NULL; +/* local interfaces structure */ +extern struct interface *local_interfaces; + +/* remote interfaces structure */ +extern struct interface *remote_interfaces; + /* this is our domain/workgroup/server database */ struct subnet_record *subnetlist = NULL; @@ -317,8 +324,8 @@ struct work_record *find_workgroupstruct(struct subnet_record *d, { DEBUG(2,("add any workgroups: initiating browser search on %s\n", inet_ntoa(d->bcast_ip))); - queue_netbios_pkt_wins(ClientNMB,NMB_QUERY, FIND_MASTER, - MSBROWSE,0x1,0, + queue_netbios_pkt_wins(d,ClientNMB,NMB_QUERY, NAME_QUERY_FIND_MST, + MSBROWSE,0x1,0,0, True,False, d->bcast_ip); return NULL; } @@ -339,8 +346,6 @@ struct work_record *find_workgroupstruct(struct subnet_record *d, if ((work = make_workgroup(name))) { - work->needelection = False; - if (lp_preferred_master() && strequal(lp_workgroup(), name) && d->my_interface) @@ -349,6 +354,10 @@ struct work_record *find_workgroupstruct(struct subnet_record *d, work->needelection = True; work->ElectionCriterion |= (1<<3); } + if (!d->my_interface) + { + work->needelection = False; + } add_workgroup(work, d); return(work); } @@ -356,20 +365,31 @@ struct work_record *find_workgroupstruct(struct subnet_record *d, } /**************************************************************************** - find a domain in the subnetlist + find a subnet in the subnetlist **************************************************************************/ -struct subnet_record *find_domain(struct in_addr ip) +struct subnet_record *find_subnet(struct in_addr bcast_ip) { struct subnet_record *d; + struct in_addr wins_ip = ipgrp; - /* search through domain list for broadcast/netmask that matches - the source ip address */ + /* search through subnet list for broadcast/netmask that matches + the source ip address. a subnet 255.255.255.255 represents the + WINS list. */ for (d = subnetlist; d; d = d->next) { - if (same_net(ip, d->bcast_ip, d->mask_ip)) + if (ip_equal(bcast_ip, wins_ip)) + { + if (ip_equal(bcast_ip, d->bcast_ip)) + { + return d; + } + } + else if (same_net(bcast_ip, d->bcast_ip, d->mask_ip)) + { return(d); } + } return (NULL); } @@ -411,8 +431,7 @@ void dump_workgroups(void) /**************************************************************************** create a domain entry ****************************************************************************/ -static struct subnet_record *make_subnet(struct in_addr bcast_ip, - struct in_addr mask) +static struct subnet_record *make_subnet(struct in_addr bcast_ip, struct in_addr mask_ip) { struct subnet_record *d; d = (struct subnet_record *)malloc(sizeof(*d)); @@ -421,54 +440,107 @@ static struct subnet_record *make_subnet(struct in_addr bcast_ip, bzero((char *)d,sizeof(*d)); - DEBUG(4,("making subnet %s ", inet_ntoa(bcast_ip))); - DEBUG(4,("%s\n", inet_ntoa(mask))); + DEBUG(4, ("making domain %s ", inet_ntoa(bcast_ip))); + DEBUG(4, ("%s\n", inet_ntoa(mask_ip))); d->bcast_ip = bcast_ip; - d->mask_ip = mask; + d->mask_ip = mask_ip; d->workgrouplist = NULL; - d->my_interface = ismybcast(d->bcast_ip); + d->my_interface = False; /* True iff the interface is on the samba host */ add_subnet(d); return d; } + +/**************************************************************************** + add the remote interfaces from lp_remote_interfaces() and lp_interfaces() + to the netbios subnet database. + ****************************************************************************/ +void add_subnet_interfaces(void) +{ + struct interface *i; + + /* loop on all local interfaces */ + for (i = local_interfaces; i; i = i->next) + { + /* add the interface into our subnet database */ + if (!find_subnet(i->bcast)) + { + struct subnet_record *d = make_subnet(i->bcast,i->nmask); + if (d) + { + /* short-cut method to identifying local interfaces */ + d->my_interface = True; + } + } + } + + /* loop on all remote interfaces */ + for (i = remote_interfaces; i; i = i->next) + { + /* add the interface into our subnet database */ + if (!find_subnet(i->bcast)) + { + make_subnet(i->bcast,i->nmask); + } + } + + /* add the pseudo-ip interface for WINS: 255.255.255.255 */ + if (lp_wins_support()) + { + struct in_addr wins_bcast = ipgrp; + struct in_addr wins_nmask = ipzero; + make_subnet(wins_bcast, wins_nmask); + } +} + + /**************************************************************************** add a domain entry. creates a workgroup, if necessary, and adds the domain to the named a workgroup. ****************************************************************************/ -struct subnet_record *add_subnet_entry(struct in_addr source_ip, - struct in_addr source_mask, - char *name, BOOL add) +struct subnet_record *add_subnet_entry(struct in_addr bcast_ip, + struct in_addr mask_ip, + char *name, BOOL add, BOOL lmhosts) { struct subnet_record *d; - struct in_addr ip; - ip = ipgrp; + /* XXXX andrew: struct in_addr ip appears not to be referenced at all except + in the DEBUG comment. i assume that the DEBUG comment below actually + intends to refer to bcast_ip? i don't know. + + struct in_addr ip = ipgrp; + + */ - if (zero_ip(source_ip)) - source_ip = *iface_bcast(source_ip); + if (zero_ip(bcast_ip)) + bcast_ip = *iface_bcast(bcast_ip); /* add the domain into our domain database */ - if ((d = find_domain(source_ip)) || - (d = make_subnet(source_ip, source_mask))) + if ((d = find_subnet(bcast_ip)) || + (d = make_subnet(bcast_ip, mask_ip))) { struct work_record *w = find_workgroupstruct(d, name, add); + extern pstring ServerComment; if (!w) return NULL; /* add WORKGROUP(1e) and WORKGROUP(00) entries into name database or register with WINS server, if it's our workgroup */ - if (strequal(lp_workgroup(), name)) + if (strequal(lp_workgroup(), name) && d->my_interface) + { + add_my_name_entry(d,name,0x1e,NB_ACTIVE|NB_GROUP); + add_my_name_entry(d,name,0x0 ,NB_ACTIVE|NB_GROUP); + } + /* add samba server name to workgroup list */ + if ((strequal(lp_workgroup(), name) && d->my_interface) || lmhosts) { - extern pstring ServerComment; - add_name_entry(name,0x1e,NB_ACTIVE|NB_GROUP); - add_name_entry(name,0x0 ,NB_ACTIVE|NB_GROUP); add_server_entry(d,w,myname,w->ServerType,0,ServerComment,True); } - DEBUG(3,("Added domain name entry %s at %s\n", name,inet_ntoa(ip))); + DEBUG(3,("Added domain name entry %s at %s\n", name,inet_ntoa(bcast_ip))); return d; } return NULL; @@ -541,6 +613,28 @@ struct browse_cache_record *add_browser_entry(char *name, int type, char *wg, } +/**************************************************************************** + remove all samba's server entries + ****************************************************************************/ +void remove_my_servers(void) +{ + struct subnet_record *d; + for (d = subnetlist; d; d = d->next) + { + struct work_record *work; + for (work = d->workgrouplist; work; work = work->next) + { + struct server_record *s; + for (s = work->serverlist; s; s = s->next) + { + if (!strequal(myname,s->serv.name)) continue; + announce_server(d, work, s->serv.name, s->serv.comment, 0, 0); + } + } + } +} + + /**************************************************************************** add a server entry ****************************************************************************/ @@ -569,6 +663,7 @@ struct server_record *add_server_entry(struct subnet_record *d, return(s); } + if (!s || s->serv.type != servertype || !strequal(s->serv.comment, comment)) updatedlists=True; if (!s) @@ -581,8 +676,8 @@ struct server_record *add_server_entry(struct subnet_record *d, bzero((char *)s,sizeof(*s)); } - if (d->my_interface && - strequal(lp_workgroup(),work->work_group)) + + if (d->my_interface && strequal(lp_workgroup(),work->work_group)) { if (servertype) servertype |= SV_TYPE_LOCAL_LIST_ONLY; @@ -597,10 +692,7 @@ struct server_record *add_server_entry(struct subnet_record *d, StrnCpy(s->serv.comment,comment,sizeof(s->serv.comment)-1); strupper(s->serv.name); s->serv.type = servertype; - s->death_time = ttl?time(NULL)+ttl*3:0; - - if (servertype == 0) - s->death_time = time(NULL)-1; + 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 */ @@ -669,8 +761,7 @@ void write_browse_list(void) 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]) + if ((s->serv.type & SV_TYPE_DOMAIN_ENUM) && !s->serv.comment[0]) { continue; } diff --git a/source/nameelect.c b/source/nameelect.c index 1832240a116..c841d9b7a60 100644 --- a/source/nameelect.c +++ b/source/nameelect.c @@ -42,8 +42,6 @@ extern pstring ServerComment; extern time_t StartupTime; -#define AM_MASTER(work) (work->ServerType & SV_TYPE_MASTER_BROWSER) - #define BROWSE_MAILSLOT "\\MAILSLOT\\BROWSE" extern struct subnet_record *subnetlist; @@ -61,7 +59,6 @@ void check_master_browser(void) if (!lastrun) lastrun = t; if (t < lastrun + CHECK_TIME_MST_BROWSE * 60) return; - lastrun = t; dump_workgroups(); @@ -77,8 +74,8 @@ void check_master_browser(void) if (!AM_MASTER(work)) { - queue_netbios_packet(ClientNMB,NMB_QUERY,CHECK_MASTER, - work->work_group,0x1d,0, + queue_netbios_packet(d,ClientNMB,NMB_QUERY,NAME_QUERY_MST_CHK, + work->work_group,0x1d,0,0, True,False,d->bcast_ip); } } @@ -91,13 +88,13 @@ void check_master_browser(void) ******************************************************************/ void browser_gone(char *work_name, struct in_addr ip) { - struct subnet_record *d = find_domain(ip); + struct subnet_record *d = find_subnet(ip); struct work_record *work = find_workgroupstruct(d, work_name, False); if (!work || !d) return; if (strequal(work->work_group, lp_workgroup()) && - d->my_interface) + ismybcast(d->bcast_ip)) { DEBUG(2,("Forcing election on %s %s\n", @@ -121,6 +118,7 @@ void browser_gone(char *work_name, struct in_addr ip) } } + /**************************************************************************** send an election packet **************************************************************************/ @@ -169,15 +167,16 @@ static void become_master(struct subnet_record *d, struct work_record *work) work->ElectionCriterion |= 0x5; /* add browse, master and general names to database or register with WINS */ - add_name_entry(MSBROWSE ,0x01,NB_ACTIVE|NB_GROUP); - add_name_entry(work->work_group,0x1d,NB_ACTIVE ); + add_my_name_entry(d,MSBROWSE ,0x01,NB_ACTIVE|NB_GROUP); + add_my_name_entry(d,work->work_group,0x1d,NB_ACTIVE ); if (lp_domain_master()) { DEBUG(4,("Domain master: adding names...\n")); /* add domain master and domain member names or register with WINS */ - add_name_entry(work->work_group,0x1b,NB_ACTIVE); + add_my_name_entry(d,work->work_group,0x1b,NB_ACTIVE ); + work->ServerType |= SV_TYPE_DOMAIN_MASTER; if (lp_domain_logons()) @@ -200,21 +199,44 @@ static void become_master(struct subnet_record *d, struct work_record *work) /******************************************************************* - unbecome the master browser + unbecome the master browser. initates removal of necessary netbios + names, and tells the world that we are no longer a master browser. ******************************************************************/ -void become_nonmaster(struct subnet_record *d, struct work_record *work) +void become_nonmaster(struct subnet_record *d, struct work_record *work, + int remove_type) { + int new_server_type = work->ServerType; + DEBUG(2,("Becoming non-master for %s\n",work->work_group)); - work->ServerType &= ~SV_TYPE_MASTER_BROWSER; - work->ServerType &= ~SV_TYPE_DOMAIN_MASTER; - work->ServerType |= SV_TYPE_POTENTIAL_BROWSER; + /* can only remove master or domain types with this function */ + remove_type &= ~(SV_TYPE_MASTER_BROWSER|SV_TYPE_DOMAIN_MASTER); + + /* unbecome a master browser; unbecome a domain master, too :-( */ + if (remove_type & SV_TYPE_MASTER_BROWSER) + remove_type |= SV_TYPE_DOMAIN_MASTER; + new_server_type &= ~remove_type; + + if (!(new_server_type & (SV_TYPE_MASTER_BROWSER|SV_TYPE_DOMAIN_MASTER))) + { + /* no longer a master browser of any sort */ + + work->ServerType |= SV_TYPE_POTENTIAL_BROWSER; work->ElectionCriterion &= ~0x4; - remove_name_entry(work->work_group,0x1b); - remove_name_entry(work->work_group,0x1d); - remove_name_entry(MSBROWSE ,0x01); + /* 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); + } + + work->ServerType = new_server_type; + + if (!(work->ServerType & SV_TYPE_DOMAIN_MASTER)) + remove_name_entry(d,work->work_group,0x1b); + + if (!(work->ServerType & SV_TYPE_DOMAIN_MASTER)) + remove_name_entry(d,work->work_group,0x1d); } @@ -292,7 +314,7 @@ 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_domain(ip); + struct subnet_record *d = find_subnet(ip); int version = CVAL(buf,0); uint32 criterion = IVAL(buf,1); int timeup = IVAL(buf,5)/1000; @@ -335,7 +357,8 @@ void process_election(struct packet_struct *p,char *buf) /* if we are the master then remove our masterly names */ if (AM_MASTER(work)) { - become_nonmaster(d, work); + become_nonmaster(d, work, + SV_TYPE_MASTER_BROWSER|SV_TYPE_DOMAIN_MASTER); } } } @@ -355,10 +378,6 @@ BOOL check_elections(void) for (d = subnetlist; d; d = d->next) { struct work_record *work; - - /* we only want to run elections on our own interfaces */ - if (!d->my_interface) continue; - for (work = d->workgrouplist; work; work = work->next) { run_any_election |= work->RunningElection; diff --git a/source/nameresp.c b/source/nameresp.c index b244d811597..31df7996913 100644 --- a/source/nameresp.c +++ b/source/nameresp.c @@ -25,8 +25,7 @@ extern int ClientNMB; extern int ClientDGRAM; -/* this is our initiated name query response database */ -struct name_response_record *nameresponselist = NULL; +extern struct subnet_record *subnetlist; extern int DEBUGLEVEL; @@ -35,24 +34,29 @@ BOOL CanRecurse = True; extern pstring scope; extern pstring myname; extern struct in_addr ipzero; +extern struct in_addr ipgrp; +int num_response_packets = 0; /*************************************************************************** add an initated name query into the list **************************************************************************/ -extern void add_response_record(struct name_response_record *n) +static void add_response_record(struct subnet_record *d, + struct response_record *n) { - struct name_response_record *n2; + struct response_record *n2; - if (!nameresponselist) + if (!d) return; + + if (!d->responselist) { - nameresponselist = n; + d->responselist = n; n->prev = NULL; n->next = NULL; return; } - for (n2 = nameresponselist; n2->next; n2 = n2->next) ; + for (n2 = d->responselist; n2->next; n2 = n2->next) ; n2->next = n; n->next = NULL; @@ -60,41 +64,243 @@ extern void add_response_record(struct name_response_record *n) } +/*************************************************************************** + 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->to_ip), namestr(&n->name), n->num_msgs)); + + switch (n->cmd_type) + { + 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 *d = find_subnet(ipgrp); + if (d) + { + /* 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(d, n->name.name, n->name.name_type, + REGISTER, n->to_ip); + } + } +} + 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->to_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->to_ip)) + { + remove_netbios_name(d,n->name.name,n->name.name_type,SELF,n->to_ip); + } + if (!n->bcast) + { + DEBUG(1,("WINS server did not respond to name release!\n")); + } + 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->bcast) + { + /* broadcast method: implicit acceptance of the name registration + by not receiving any objections. */ + + /* IMPORTANT: see response_name_reg() */ + + enum name_source source = ismyip(n->to_ip) ? SELF : REGISTER; + + add_netbios_entry(d,n->name.name,n->name.name_type, + n->nb_flags, n->ttl, source,n->to_ip, True,!n->bcast); + } + else + { + /* XXXX oops. this is where i wish this code could retry DGRAM + packets. we directed a name registration at a WINS server, and + received no response. rfc1001.txt states that after retrying, + we should assume the WINS server is dead, and fall back to + broadcasting. */ + + DEBUG(1,("WINS server did not respond to name registration!\n")); + } + break; + } + + default: + { + /* nothing to do but delete the dead expected-response structure */ + /* this is normal. */ + break; + } + } +} + + +/**************************************************************************** + initiate a netbios packet + ****************************************************************************/ +static 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_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 (!name_trn_id) name_trn_id = (time(NULL)%(unsigned)0x7FFF) + + (getpid()%(unsigned)100); + name_trn_id = (name_trn_id+1) % (unsigned)0x7FFF; + + if (*id == 0xffff) *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 = CanRecurse; + 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) ? 1 : 0; + + make_nmb_name(&nmb->question.question_name,name,name_type,scope); + + nmb->question.question_type = quest_type; + nmb->question.question_class = 0x1; + + if (quest_type == NMB_REG || 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 = nmb->question.question_type; + nmb->additional->rr_class = nmb->question.question_class; + + nmb->additional->ttl = quest_type == NMB_REG ? lp_max_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; + + if (!send_packet(&p)) *id = 0xffff; + + return; +} + + /******************************************************************* 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) +void expire_netbios_response_entries() { - struct name_response_record *n; - struct name_response_record *nextn; + struct response_record *n; + struct response_record *nextn; + struct subnet_record *d; - for (n = nameresponselist; n; n = nextn) + for (d = subnetlist; d; d = d->next) + for (n = d->responselist; n; n = nextn) { - if (n->start_time < t) - { - DEBUG(3,("Removing dead name query for %s %s (num_msgs=%d)\n", - inet_ntoa(n->to_ip), namestr(&n->name), n->num_msgs)); + if (n->repeat_time < time(NULL)) + { + 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->to_ip); - if (n->cmd_type == CHECK_MASTER) - { - /* if no response received, the master browser must have gone */ - if (n->num_msgs == 0) - browser_gone(n->name.name, n->to_ip); - } - + n->repeat_time += n->repeat_interval; /* XXXX ms needed */ + n->repeat_count--; + } + else + { + dead_netbios_entry(d,n); + + nextn = n->next; + + 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--; + + continue; + } + } nextn = n->next; - - if (n->prev) n->prev->next = n->next; - if (n->next) n->next->prev = n->prev; - - if (nameresponselist == n) nameresponselist = n->next; - - free(n); - } - else - { - nextn = n->next; - } } } @@ -102,9 +308,10 @@ void expire_netbios_response_entries(time_t t) /**************************************************************************** reply to a netbios name packet ****************************************************************************/ -void reply_netbios_packet(struct packet_struct *p1,int trn_id,int rcode, - int opcode,BOOL recurse,struct nmb_name *rr_name, - int rr_type,int rr_class,int ttl,char *data,int len) +void reply_netbios_packet(struct packet_struct *p1,int trn_id, + int rcode,int opcode, BOOL recurse, + 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; @@ -160,89 +367,14 @@ void reply_netbios_packet(struct packet_struct *p1,int trn_id,int rcode, } -/**************************************************************************** - initiate a netbios packet - ****************************************************************************/ -uint16 initiate_netbios_packet(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 (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_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 False; - - bzero((char *)&p,sizeof(p)); - - if (!name_trn_id) name_trn_id = (time(NULL)%(unsigned)0x7FFF) + - (getpid()%(unsigned)100); - name_trn_id = (name_trn_id+1) % (unsigned)0x7FFF; - - nmb->header.name_trn_id = name_trn_id; - nmb->header.opcode = opcode; - nmb->header.response = False; - nmb->header.nm_flags.bcast = bcast; - nmb->header.nm_flags.recursion_available = CanRecurse; - 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) ? 1 : 0; - - make_nmb_name(&nmb->question.question_name,name,name_type,scope); - - nmb->question.question_type = quest_type; - nmb->question.question_class = 0x1; - - if (quest_type == NMB_REG || 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 = nmb->question.question_type; - nmb->additional->rr_class = nmb->question.question_class; - - nmb->additional->ttl = quest_type == NMB_REG ? lp_max_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; - - if (!send_packet(&p)) - return(0); - - return(name_trn_id); -} - - /**************************************************************************** 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. ****************************************************************************/ -void queue_netbios_pkt_wins(int fd,int quest_type,enum cmd_type cmd, - char *name,int name_type,int nb_flags, +void queue_netbios_pkt_wins(struct subnet_record *d, + int fd,int quest_type,enum cmd_type cmd, + char *name,int name_type,int nb_flags, time_t ttl, BOOL bcast,BOOL recurse,struct in_addr to_ip) { if ((!lp_wins_support()) && (*lp_wins_server())) @@ -266,35 +398,45 @@ void queue_netbios_pkt_wins(int fd,int quest_type,enum cmd_type cmd, if (zero_ip(to_ip)) return; - queue_netbios_packet(fd, quest_type, cmd, - name, name_type, nb_flags, + queue_netbios_packet(d,fd, quest_type, cmd, + name, name_type, nb_flags, ttl, bcast, recurse, to_ip); } /**************************************************************************** create a name query response record **************************************************************************/ -static struct name_response_record * -make_name_query_record(enum cmd_type cmd,int id,int fd,char *name,int type, +static struct response_record * +make_response_queue_record(enum cmd_type cmd,int id,int fd, + int quest_type, char *name,int type, int nb_flags, time_t ttl, BOOL bcast,BOOL recurse,struct in_addr ip) { - struct name_response_record *n; + struct response_record *n; if (!name || !name[0]) return NULL; - if (!(n = (struct name_response_record *)malloc(sizeof(*n)))) + if (!(n = (struct response_record *)malloc(sizeof(*n)))) return(NULL); n->response_id = id; n->cmd_type = cmd; 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->bcast = bcast; n->recurse = recurse; n->to_ip = ip; - n->start_time = time(NULL); + + n->repeat_interval = 1; /* XXXX should be in ms */ + n->repeat_count = 4; + n->repeat_time = time(NULL) + n->repeat_interval; + n->num_msgs = 0; + num_response_packets++; /* count of total number of packets still around */ + return n; } @@ -305,32 +447,43 @@ make_name_query_record(enum cmd_type cmd,int id,int fd,char *name,int type, master browsers (WORKGROUP(1d or 1b) or __MSBROWSE__(1)) to get complete lists across a wide area network ****************************************************************************/ -void queue_netbios_packet(int fd,int quest_type,enum cmd_type cmd,char *name, - int name_type,int nb_flags,BOOL bcast,BOOL recurse, - struct in_addr to_ip) +void queue_netbios_packet(struct subnet_record *d, + int fd,int quest_type,enum cmd_type cmd,char *name, + int name_type,int nb_flags, time_t ttl, + BOOL bcast,BOOL recurse, struct in_addr to_ip) { - uint16 id = initiate_netbios_packet(fd, quest_type, name, name_type, - nb_flags, bcast, recurse, to_ip); - struct name_response_record *n; + struct in_addr wins_ip = ipgrp; + struct response_record *n; + uint16 id = 0xffff; - if (id == 0) return; + /* ha ha. no. do NOT broadcast to 255.255.255.255: it's a pseudo address */ + if (ip_equal(wins_ip, to_ip)) return; + + initiate_netbios_packet(&id, fd, quest_type, name, name_type, + nb_flags, bcast, recurse, to_ip); + + if (id == 0xffff) return; - if ((n = - make_name_query_record(cmd,id,fd,name,name_type,bcast,recurse,to_ip))) + if ((n = make_response_queue_record(cmd,id,fd, + quest_type,name,name_type,nb_flags,ttl, + bcast,recurse,to_ip))) { - add_response_record(n); + add_response_record(d,n); } } /**************************************************************************** - find a response in the name query response list + find a response in a subnet's name query response list. **************************************************************************/ -struct name_response_record *find_name_query(uint16 id) +struct response_record *find_response_record(struct subnet_record *d, + uint16 id) { - struct name_response_record *n; + struct response_record *n; - for (n = nameresponselist; n; n = n->next) + if (!d) return NULL; + + for (n = d->responselist; n; n = n->next) { if (n->response_id == id) { return n; @@ -409,10 +562,12 @@ void listen_for_packets(BOOL run_election) FD_SET(ClientNMB,&fds); FD_SET(ClientDGRAM,&fds); - /* during elections we need to send election packets at one - second intervals */ + /* during elections and when expecting a netbios response packet we need + to send election packets at one second intervals. + XXXX actually, 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 ? 1 : NMBD_SELECT_LOOP; + timeout.tv_sec = (run_election||num_response_packets) ? 1 : NMBD_SELECT_LOOP; timeout.tv_usec = 0; selrtn = sys_select(&fds,&timeout); @@ -461,8 +616,9 @@ 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. ****************************************************************************/ -BOOL interpret_node_status(char *p, struct nmb_name *name,int t, - char *serv_name, struct in_addr ip) +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 level = t==0x20 ? 4 : 0; int numnames = CVAL(p,0); @@ -516,7 +672,7 @@ BOOL interpret_node_status(char *p, struct nmb_name *name,int t, nameip = ip; src = STATUS_QUERY; } - add_netbios_entry(qname,type,nb_flags,2*60*60,src,nameip,True); + add_netbios_entry(d,qname,type,nb_flags,2*60*60,src,nameip,True,bcast); } /* we want the server name */ @@ -559,9 +715,13 @@ BOOL send_mailslot_reply(char *mailslot,int fd,char *buf,int len,char *srcname, { struct packet_struct p; struct dgram_packet *dgram = &p.packet.dgram; + struct in_addr wins_ip = ipgrp; 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)); dgram->header.msg_type = 0x11; /* DIRECT GROUP DATAGRAM */ diff --git a/source/nameserv.c b/source/nameserv.c index 22c1e7dbbaa..7b72f93952a 100644 --- a/source/nameserv.c +++ b/source/nameserv.c @@ -30,6 +30,10 @@ extern int ClientNMB; extern int ClientDGRAM; +#define FIND_SELF 0x01 +#define FIND_WINS 0x02 +#define FIND_LOCAL 0x04 + extern int DEBUGLEVEL; extern pstring scope; @@ -38,11 +42,27 @@ extern pstring myname; extern struct in_addr ipzero; extern struct in_addr ipgrp; -/* netbios names database */ -struct name_record *namelist; +extern struct subnet_record *subnetlist; + +#define WINS_LIST "wins.dat" #define GET_TTL(ttl) ((ttl)?MIN(ttl,lp_max_ttl()):lp_max_ttl()) +/**************************************************************************** + finds the appropriate subnet structure. directed packets (non-bcast) are + assumed to come from a point-to-point (P or M node), and so the subnet we + return in this instance is the WINS 'pseudo-subnet' with ip 255.255.255.255 + ****************************************************************************/ +static struct subnet_record *find_req_subnet(struct in_addr ip, BOOL bcast) +{ + if (bcast) + { + /* identify the subnet the broadcast request came from */ + return find_subnet(*iface_bcast(ip)); + } + /* find the subnet under the pseudo-ip of 255.255.255.255 */ + return find_subnet(ipgrp); +} /**************************************************************************** true if two netbios names are equal @@ -57,19 +77,21 @@ static BOOL name_equal(struct nmb_name *n1,struct nmb_name *n2) /**************************************************************************** add a netbios name into the namelist **************************************************************************/ -static void add_name(struct name_record *n) +static void add_name(struct subnet_record *d, struct name_record *n) { struct name_record *n2; - if (!namelist) + if (!d) return; + + if (!d->namelist) { - namelist = n; + d->namelist = n; n->prev = NULL; n->next = NULL; return; } - for (n2 = namelist; n2->next; n2 = n2->next) ; + for (n2 = d->namelist; n2->next; n2 = n2->next) ; n2->next = n; n->next = NULL; @@ -80,9 +102,12 @@ static void add_name(struct name_record *n) remove a name from the namelist. The pointer must be an element just retrieved **************************************************************************/ -void remove_name(struct name_record *n) +void remove_name(struct subnet_record *d, struct name_record *n) { - struct name_record *nlist = namelist; + struct name_record *nlist; + if (!d) return; + + nlist = d->namelist; while (nlist && nlist != n) nlist = nlist->next; @@ -96,29 +121,70 @@ void remove_name(struct name_record *n) /**************************************************************************** - find a name in the domain database namelist - search can be: - FIND_SELF - look for names the samba server has added for itself - FIND_GLOBAL - the name can be anyone. first look on the client's - subnet, then the server's subnet, then all subnets. + find a name in a namelist **************************************************************************/ -static struct name_record *find_name_search(struct nmb_name *name, - enum name_search search, - struct in_addr ip) +static struct name_record *find_name(struct name_record *n, + struct nmb_name *name, + int search, struct in_addr ip) { struct name_record *ret; - for (ret = namelist; ret; ret = ret->next) + for (ret = n; ret; ret = ret->next) { - if (!name_equal(&ret->name,name)) continue; - - if (search == FIND_SELF && ret->source != SELF) continue; + if (name_equal(&ret->name,name)) + { + /* self search: self names only */ + if ((search&FIND_SELF) == FIND_SELF && ret->source != SELF) + continue; + if (zero_ip(ip) || ip_equal(ip, ret->ip)) + { return ret; } + } + } + return NULL; +} + + +/**************************************************************************** + find a name in the domain database namelist + search can be any of: + FIND_SELF - look exclusively for names the samba server has added for itself + FIND_LOCAL - look for names in the local subnet record. + FIND_WINS - look for names in the WINS record + **************************************************************************/ +static struct name_record *find_name_search(struct subnet_record **d, + struct nmb_name *name, + int search, struct in_addr ip) +{ + if (d == NULL) return NULL; /* bad error! */ + if ((search & FIND_LOCAL) == FIND_LOCAL) + { + if (*d != NULL) + { + return find_name((*d)->namelist, name, search, ip); + } + else + { + DEBUG(4,("local find_name_search with a NULL subnet pointer\n")); return NULL; } + } + + if ((search & FIND_WINS) != FIND_WINS) return NULL; + + if (*d == NULL) + { + /* find WINS subnet record */ + *d = find_subnet(ipgrp); + } + + if (*d == NULL) return NULL; + + return find_name((*d)->namelist, name, search, ip); +} /**************************************************************************** @@ -127,48 +193,232 @@ static struct name_record *find_name_search(struct nmb_name *name, void dump_names(void) { struct name_record *n; + struct subnet_record *d; + fstring fname, fnamenew; time_t t = time(NULL); + FILE *f; + + strcpy(fname,lp_lockdir()); + trim_string(fname,NULL,"/"); + strcat(fname,"/"); + strcat(fname,WINS_LIST); + strcpy(fnamenew,fname); + strcat(fnamenew,"."); + + f = fopen(fnamenew,"w"); + + if (!f) + { + DEBUG(4,("Can't open %s - %s\n",fnamenew,strerror(errno))); + } + DEBUG(3,("Dump of local name table:\n")); - for (n = namelist; n; n = n->next) + for (d = subnetlist; d; d = d->next) + for (n = d->namelist; n; n = n->next) { - DEBUG(3,("%s %s TTL=%d NBFLAGS=%2x\n", + if (f && ip_equal(d->bcast_ip, ipgrp) && n->source == REGISTER) + { + fstring data; + + /* XXXX i have little imagination as to how to output nb_flags as + anything other than a hexadecimal number :-) */ + + sprintf(data, "%s#%02x %s %ld %2x", + n->name.name,n->name.name_type, /* XXXX ignore the scope for now */ + inet_ntoa(n->ip), + n->death_time, + n->nb_flags); + fprintf(f, "%s\n", data); + } + + DEBUG(3,("%15s ", inet_ntoa(d->bcast_ip))); + DEBUG(3,("%15s ", inet_ntoa(d->mask_ip))); + DEBUG(3,("%s %15s TTL=%15d NBFLAGS=%2x\n", namestr(&n->name), inet_ntoa(n->ip), n->death_time?n->death_time-t:0, n->nb_flags)); } + + fclose(f); + unlink(fname); + chmod(fnamenew,0644); + rename(fnamenew,fname); + + DEBUG(3,("Wrote wins database %s\n",fname)); +} + +/**************************************************************************** +load a netbios name database file +****************************************************************************/ +void load_netbios_names(void) +{ + struct subnet_record *d = find_subnet(ipgrp); + fstring fname; + + FILE *f; + pstring line; + + if (!d) return; + + strcpy(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; + 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,ip_str ,NULL)) ++count; + if (next_token(&ptr,ttd_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 ip nb_flags abs_time\n",line)); + continue; + } + + /* netbios name. # divides the name from the type (hex): netbios#xx */ + strcpy(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 %s %ld %2x\n", + name,type, inet_ntoa(ipaddr), ttd, nb_flags)); + + /* add all entries that have 60 seconds or more to live */ + if (ttd - 10 < 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,True); + } + } + + fclose(f); } /**************************************************************************** remove an entry from the name list ****************************************************************************/ -void remove_netbios_name(char *name,int type, enum name_source source, +void remove_netbios_name(struct subnet_record *d, + char *name,int type, enum name_source source, struct in_addr ip) { struct nmb_name nn; struct name_record *n; + int search = FIND_LOCAL; + + /* if it's not a special browser name, search the WINS database */ + if (type != 0x01 && type != 0x1d && type != 0x1e) + search |= FIND_WINS; make_nmb_name(&nn, name, type, scope); - n = find_name_search(&nn, FIND_GLOBAL, ip); + n = find_name_search(&d, &nn, search, ip); - if (n && n->source == source) remove_name(n); + if (n && n->source == source) remove_name(d,n); } /**************************************************************************** - add an entry to the name list + 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 (names of type 0x00, 0x20, 0x03 etc) + ****************************************************************************/ -struct name_record *add_netbios_entry(char *name, int type, int nb_flags, - int ttl, - enum name_source source, - struct in_addr ip, - BOOL new_only) +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,BOOL wins) { struct name_record *n; struct name_record *n2=NULL; + int search = 0; + BOOL self = source == SELF; + + /* add the name to the WINS list if the name comes from a directed query */ + search |= wins ? FIND_WINS : FIND_LOCAL; + /* search for SELF names only */ + search |= self ? FIND_SELF : 0; + + if (!self) + { + if (wins) + { + if (type == 0x01 || type == 0x1d || type == 0x1e) + { + /* XXXX WINS server supposed to ignore special browser names. hm. + but is a primary domain controller supposed to ignore special + browser names? luke doesn't think so, but can't test it! :-) + */ + return NULL; + } + } + else /* !wins */ + { + /* the only broadcast (non-WINS) names we are adding are ours (SELF) */ + return NULL; + } + } n = (struct name_record *)malloc(sizeof(*n)); if (!n) return(NULL); @@ -177,7 +427,7 @@ struct name_record *add_netbios_entry(char *name, int type, int nb_flags, make_nmb_name(&n->name,name,type,scope); - if ((n2 = find_name_search(&n->name, FIND_GLOBAL, new_only?ipzero:ip))) + if ((n2 = find_name_search(&d, &n->name, search, new_only?ipzero:ip))) { free(n); if (new_only || (n2->source==SELF && source!=SELF)) return n2; @@ -189,7 +439,7 @@ struct name_record *add_netbios_entry(char *name, int type, int nb_flags, n->nb_flags = nb_flags; n->source = source; - if (!n2) add_name(n); + if (!n2) add_name(d,n); DEBUG(3,("Added netbios name %s at %s ttl=%d nb_flags=%2x\n", namestr(&n->name),inet_ntoa(ip),ttl,nb_flags)); @@ -201,21 +451,39 @@ struct name_record *add_netbios_entry(char *name, int type, int nb_flags, /**************************************************************************** remove an entry from the name list ****************************************************************************/ -void remove_name_entry(char *name,int type) +void remove_name_entry(struct subnet_record *d, char *name,int type) { if (lp_wins_support()) { /* we are a WINS server. */ - remove_netbios_name(name,type,SELF,ipzero); + /* XXXX assume that if we are a WINS server that we are therefore + not pointing to another WINS server as well. this may later NOT + actually be true */ + remove_netbios_name(d,name,type,SELF,ipzero); } else { - struct in_addr ip; - ip = ipzero; + /* not a WINS server: cannot just remove our own names: we have to + ask permission from the WINS server, or if no reply is received, + _then_ we can remove the name */ - queue_netbios_pkt_wins(ClientNMB,NMB_REL,NAME_RELEASE, - name, type, 0, - False, True, ip); + struct name_record n; + struct name_record *n2=NULL; + + make_nmb_name(&n.name,name,type,scope); + + if ((n2 = find_name_search(&d, &n.name, FIND_SELF, ipzero))) + { + /* check name isn't already being de-registered */ + if (NAME_DEREG(n2->nb_flags)) + return; + + /* mark the name as in the process of deletion. */ + n2->nb_flags &= NB_DEREG; + } + queue_netbios_pkt_wins(d,ClientNMB,NMB_REL,NAME_RELEASE, + name, type, 0, 0, + False, True, ipzero); } } @@ -223,19 +491,32 @@ void remove_name_entry(char *name,int type) /**************************************************************************** add an entry to the name list ****************************************************************************/ -void add_name_entry(char *name,int type,int nb_flags) +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(d->namelist, &n, SELF, ipzero)) + re_reg = True; + /* always add our own entries */ - add_netbios_entry(name,type,nb_flags,0,SELF,ipzero,False); + add_netbios_entry(d,name,type,nb_flags,0,SELF,ipzero,False,lp_wins_support()); if (!lp_wins_support()) { - struct in_addr ip; - ip = ipzero; + /* we aren't supporting WINS: register name using broadcast or + contact WINS server */ - queue_netbios_pkt_wins(ClientNMB,NMB_REG,NAME_REGISTER, - name, type, nb_flags, - False, True, ip); + queue_netbios_pkt_wins(d,ClientNMB, + re_reg ? NMB_REG_REFRESH : NMB_REG, NAME_REGISTER, + name, type, nb_flags, GET_TTL(0), + False, True, ipzero); } } @@ -245,38 +526,53 @@ void add_name_entry(char *name,int type,int nb_flags) **************************************************************************/ void add_my_names(void) { - struct in_addr ip; + BOOL wins = lp_wins_support(); + struct subnet_record *d; - ip = ipzero; + struct in_addr ip = ipzero; - add_name_entry(myname,0x20,NB_ACTIVE); - add_name_entry(myname,0x03,NB_ACTIVE); - add_name_entry(myname,0x00,NB_ACTIVE); - add_name_entry(myname,0x1f,NB_ACTIVE); - - add_netbios_entry("*",0x0,NB_ACTIVE,0,SELF,ip,False); - add_netbios_entry("__SAMBA__",0x20,NB_ACTIVE,0,SELF,ip,False); - add_netbios_entry("__SAMBA__",0x00,NB_ACTIVE,0,SELF,ip,False); + /* each subnet entry, including the WINS one, must have its own + netbios name. */ + /* XXXX if there was a transport layer added to samba (ipx/spx, netbeui + etc) then there would be yet _another_ for-loop, this time on the + transport type */ + for (d = subnetlist; d; d = d->next) + { + add_my_name_entry(d, myname,0x20,NB_ACTIVE); + add_my_name_entry(d, myname,0x03,NB_ACTIVE); + add_my_name_entry(d, myname,0x00,NB_ACTIVE); + add_my_name_entry(d, myname,0x1f,NB_ACTIVE); - if (lp_wins_support()) { + add_netbios_entry(d,"*",0x0,NB_ACTIVE,0,SELF,ip,False,wins); + add_netbios_entry(d,"__SAMBA__",0x20,NB_ACTIVE,0,SELF,ip,False,wins); + add_netbios_entry(d,"__SAMBA__",0x00,NB_ACTIVE,0,SELF,ip,False,wins); + + if (wins) { /* the 0x1c name gets added by any WINS server it seems */ - add_name_entry(my_workgroup(),0x1c,NB_ACTIVE|NB_GROUP); + add_my_name_entry(d, my_workgroup(),0x1c,NB_ACTIVE|NB_GROUP); } } +} + /**************************************************************************** remove all the samba names... from a WINS server if necessary. **************************************************************************/ void remove_my_names() { + struct subnet_record *d; + + for (d = subnetlist; d; d = d->next) + { struct name_record *n; - for (n = namelist; n; n = n->next) + for (n = d->namelist; n; n = n->next) { if (n->source == SELF) { /* get all SELF names removed from the WINS server's database */ - remove_name_entry(n->name.name, n->name.name_type); + remove_name_entry(d,n->name.name, n->name.name_type); + } } } } @@ -296,6 +592,61 @@ void refresh_my_names(time_t t) add_my_names(); } +/******************************************************************* + queries names occasionally. an over-cautious, non-trusting WINS server! + ******************************************************************/ +void query_refresh_names(void) +{ + struct name_record *n; + struct subnet_record *d = find_subnet(ipgrp); + + static time_t lasttime = 0; + time_t t = time(NULL); + + 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 (t - lasttime < NAME_POLL_INTERVAL) return; + + for (n = d->namelist; n; n = n->next) + { + /* only do unique, registered names */ + + if (n->source != REGISTER) continue; + if (!NAME_GROUP(n->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, + False,False,n->ip); + 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; + } +} + + /******************************************************************* expires old names in the namelist ******************************************************************/ @@ -303,9 +654,12 @@ void expire_names(time_t t) { struct name_record *n; struct name_record *next; + struct subnet_record *d; /* expire old names */ - for (n = namelist; n; n = next) + for (d = subnetlist; d; d = d->next) + { + for (n = d->namelist; n; n = next) { if (n->death_time && n->death_time < t) { @@ -316,7 +670,7 @@ void expire_names(time_t t) if (n->prev) n->prev->next = n->next; if (n->next) n->next->prev = n->prev; - if (namelist == n) namelist = n->next; + if (d->namelist == n) d->namelist = n->next; free(n); } @@ -326,12 +680,14 @@ void expire_names(time_t t) } } } +} /**************************************************************************** - response for a reg release received + response for a reg release received. samba has asked a WINS server if it + could release a name. **************************************************************************/ -void response_name_release(struct packet_struct *p) +void response_name_release(struct subnet_record *d, struct packet_struct *p) { struct nmb_packet *nmb = &p->packet.nmb; char *name = nmb->question.question_name.name; @@ -341,18 +697,24 @@ void response_name_release(struct packet_struct *p) if (nmb->header.rcode == 0 && nmb->answers->rdata) { + /* IMPORTANT: see expire_netbios_response_entries() */ + struct in_addr found_ip; putip((char*)&found_ip,&nmb->answers->rdata[2]); if (ismyip(found_ip)) { - remove_netbios_name(name,type,SELF,found_ip); + remove_netbios_name(d,name,type,SELF,found_ip); } } else { - DEBUG(1,("name registration for %s rejected!\n", + DEBUG(2,("name release for %s rejected!\n", namestr(&nmb->question.question_name))); + + /* 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. */ } } @@ -369,14 +731,29 @@ void reply_name_release(struct packet_struct *p) int nb_flags = nmb->additional->rdata[0]; BOOL bcast = nmb->header.nm_flags.bcast; struct name_record *n; + struct subnet_record *d = NULL; char rdata[6]; + int search = 0; putip((char *)&ip,&nmb->additional->rdata[2]); DEBUG(3,("Name release on name %s rcode=%d\n", namestr(&nmb->question.question_name),rcode)); - n = find_name_search(&nmb->question.question_name, FIND_GLOBAL, ip); + if (!(d = find_req_subnet(p->ip, bcast))) + { + DEBUG(3,("response packet: bcast %s not known\n", + inet_ntoa(p->ip))); + return; + } + + if (bcast) + search &= FIND_LOCAL; + else + search &= FIND_WINS; + + n = find_name_search(&d, &nmb->question.question_name, + search, ip); /* XXXX under what conditions should we reject the removal?? */ if (n && n->nb_flags == nb_flags) @@ -384,7 +761,7 @@ void reply_name_release(struct packet_struct *p) /* success = True; rcode = 6; */ - remove_name(n); + remove_name(d,n); n = NULL; } @@ -408,16 +785,19 @@ void reply_name_release(struct packet_struct *p) /**************************************************************************** response for a reg request received **************************************************************************/ -void response_name_reg(struct packet_struct *p) +void response_name_reg(struct subnet_record *d, struct packet_struct *p) { struct nmb_packet *nmb = &p->packet.nmb; char *name = nmb->question.question_name.name; int type = nmb->question.question_name.name_type; + BOOL bcast = nmb->header.nm_flags.bcast; DEBUG(4,("response name registration received!\n")); if (nmb->header.rcode == 0 && nmb->answers->rdata) { + /* IMPORTANT: see expire_netbios_response_entries() */ + int nb_flags = nmb->answers->rdata[0]; struct in_addr found_ip; int ttl = nmb->answers->ttl; @@ -427,12 +807,31 @@ void response_name_reg(struct packet_struct *p) if (ismyip(found_ip)) source = SELF; - add_netbios_entry(name,type,nb_flags,ttl,source,found_ip,True); + add_netbios_entry(d, name,type,nb_flags,ttl,source,found_ip,True,!bcast); } else { + struct work_record *work; + DEBUG(1,("name registration for %s rejected!\n", namestr(&nmb->question.question_name))); + + /* XXXX oh dear. we have problems. must deal with our name having + been rejected: e.g if it was our GROUP(1d) name, we must unbecome + a master browser. */ + + if (!(work = find_workgroupstruct(d, name, False))) return; + + /* remove_netbios_name(d,name,type,SELF,ipzero); */ + + if (AM_MASTER(work) && (type == 0x1d || type == 0x1b)) + { + int remove_type = 0; + if (type == 0x1d) remove_type = SV_TYPE_MASTER_BROWSER; + if (type == 0x1b) remove_type = SV_TYPE_DOMAIN_MASTER; + + become_nonmaster(d, work, remove_type); + } } } @@ -458,20 +857,21 @@ void reply_name_reg(struct packet_struct *p) int rcode = 0; int opcode = nmb->header.opcode; + struct subnet_record *d = NULL; struct name_record *n = NULL; BOOL success = True; BOOL recurse = True; /* true if samba replies yes/no: false if caller */ - /* must challenge the current owner */ + /* must challenge the current owner of the unique name */ char rdata[6]; - struct in_addr ip, from_ip; - - DEBUG(3,("Name registration for name %s at %s rcode=%d\n", - namestr(question),inet_ntoa(ip),rcode)); + int search = 0; putip((char *)&from_ip,&nmb->additional->rdata[2]); ip = from_ip; + DEBUG(3,("Name registration for name %s at %s rcode=%d\n", + namestr(question),inet_ntoa(ip),rcode)); + if (group) { /* apparently we should return 255.255.255.255 for group queries @@ -479,8 +879,20 @@ void reply_name_reg(struct packet_struct *p) ip = ipgrp; } + if (!(d = find_req_subnet(p->ip, bcast))) + { + DEBUG(3,("response packet: bcast %s not known\n", + inet_ntoa(p->ip))); + return; + } + + if (bcast) + search &= FIND_LOCAL; + else + search &= FIND_WINS; + /* see if the name already exists */ - n = find_name_search(question, FIND_GLOBAL, from_ip); + n = find_name_search(&d, question, search, from_ip); if (n) { @@ -502,10 +914,10 @@ void reply_name_reg(struct packet_struct *p) * 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 is alive, the person who wants the name - * can't have it. if they are not alive, they can. + * 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. * - * if we are doing non-secure WINS (which is much simpler) then + * if we are doing non-secured WINS (which is much simpler) then * we send a message to the person wanting the name saying 'he * owns this name: i don't want to hear from you ever again * until you've checked with him if you can have it!'. we then @@ -535,8 +947,6 @@ void reply_name_reg(struct packet_struct *p) } else { - /* XXXX removed code that checked with the owner of a name */ - n->ip = ip; n->death_time = ttl?p->timestamp+ttl*3:0; DEBUG(3,("%s owner: %s\n",namestr(&n->name),inet_ntoa(n->ip))); @@ -550,27 +960,30 @@ void reply_name_reg(struct packet_struct *p) 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 { - /* add the name to our subnet/name database */ - n = add_netbios_entry(qname,name_type,nb_flags,ttl,REGISTER,ip,True); + /* add the name to our name/subnet, or WINS, database */ + n = add_netbios_entry(d,qname,name_type,nb_flags,ttl,REGISTER,ip, + True,!bcast); } if (bcast) return; - if (success) - { - update_from_reg(nmb->question.question_name.name, - nmb->question.question_name.name_type, from_ip); - } - rdata[0] = nb_flags; rdata[1] = 0; putip(&rdata[2],(char *)&ip); /* Send a NAME REGISTRATION RESPONSE (pos/neg) - or and END-NODE CHALLENGE REGISTRATION RESPONSE */ + or an END-NODE CHALLENGE REGISTRATION RESPONSE */ reply_netbios_packet(p,nmb->header.name_trn_id, rcode,opcode,recurse, reply_name, name_type, name_class, @@ -591,11 +1004,23 @@ void reply_name_status(struct packet_struct *p) char *countptr, *buf, *bufend; int names_added; struct name_record *n; + struct subnet_record *d = NULL; + + BOOL bcast = nmb->header.nm_flags.bcast; + + if (!(d = find_req_subnet(p->ip, bcast))) + { + DEBUG(3,("Name status req: bcast %s not known\n", + inet_ntoa(p->ip))); + return; + } DEBUG(3,("Name status for name %s %s\n", namestr(&nmb->question.question_name), inet_ntoa(p->ip))); - n = find_name_search(&nmb->question.question_name,FIND_GLOBAL, p->ip); + n = find_name_search(&d, &nmb->question.question_name, + FIND_SELF|FIND_LOCAL, + p->ip); if (!n) return; @@ -606,7 +1031,7 @@ void reply_name_status(struct packet_struct *p) names_added = 0; - for (n = namelist ; n && buf < bufend; n = n->next) + for (n = d->namelist ; n && buf < bufend; n = n->next) { int name_type = n->name.name_type; @@ -666,9 +1091,9 @@ void reply_name_status(struct packet_struct *p) /*************************************************************************** reply to a name query ****************************************************************************/ -static struct name_record *search_for_name(struct nmb_name *question, - struct in_addr ip, int Time, - enum name_search search) +struct name_record *search_for_name(struct subnet_record **d, + struct nmb_name *question, + struct in_addr ip, int Time, int search) { int name_type = question->name_type; char *qname = question->name; @@ -678,8 +1103,10 @@ static struct name_record *search_for_name(struct nmb_name *question, DEBUG(3,("Search for %s from %s - ", namestr(question), inet_ntoa(ip))); - /* first look up name in cache. use ip as well as name to locate it */ - n = find_name_search(question,search,ip); + /* first look up name in cache */ + n = find_name_search(d,question,search,ip); + + if (*d == NULL) return NULL; /* now try DNS lookup. */ if (!n) @@ -702,14 +1129,16 @@ static struct name_record *search_for_name(struct nmb_name *question, if (!a) { /* no luck with DNS. We could possibly recurse here XXXX */ - /* if this isn't a bcast then we should send a negative reply XXXX */ - DEBUG(3,("no recursion\n")); - add_netbios_entry(qname,name_type,NB_ACTIVE,60*60,DNSFAIL,dns_ip,True); + DEBUG(3,("no recursion.\n")); + /* add the fail to our WINS cache of names. give it 1 hour in the cache */ + add_netbios_entry(*d,qname,name_type,NB_ACTIVE,60*60,DNSFAIL,dns_ip, + True, True); return NULL; } - /* add it to our cache of names. give it 2 hours in the cache */ - n = add_netbios_entry(qname,name_type,NB_ACTIVE,2*60*60,DNS,dns_ip,True); + /* add it to our WINS cache of names. give it 2 hours in the cache */ + n = add_netbios_entry(*d,qname,name_type,NB_ACTIVE,2*60*60,DNS,dns_ip, + True,True); /* failed to add it? yikes! */ if (!n) return NULL; @@ -734,42 +1163,112 @@ static struct name_record *search_for_name(struct nmb_name *question, } - /*************************************************************************** - reply to a name query +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 dns_type = name_type == 0x20 || name_type == 0; BOOL bcast = nmb->header.nm_flags.bcast; 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; - enum name_search search = (dns_type || name_type == 0x1b) ? - FIND_GLOBAL : FIND_SELF; + int search = 0; + + if (name_type == 0x20 || name_type == 0x00 || name_type == 0x1b || + name_type == 0x1f || name_type == 0x03 || name_type == 0x01 || + name_type == 0x1c) + { + /* search for any of the non-'special browser' names, or for a PDC type + (0x1b) name in the WINS database. + XXXX should we include name type 0x1c: WINS server type? + */ + search |= FIND_WINS; + } + else + { + /* special browser name types e.g + ^1^2__MSBROWSE__^2^1, GROUP(1d) and GROUP(1e) + + name_type == 0x01 || name_type == 0x1d || name_type == 0x1e. + + XXXX luke reckons we should be able to search for any SELF name + in the WINS database, if we are a primary domain controller. + */ + + if (!(d = find_req_subnet(p->ip, bcast))) + { + DEBUG(3,("name query: bcast %s not known\n", + inet_ntoa(p->ip))); + success = False; + } + + /* XXXX delete if shouldn't search for SELF names in WINS database */ + search |= FIND_WINS; + } + + if (bcast) + { + /* a name query has been made by a non-WINS configured host. search the + local interface database as well */ + search |= FIND_LOCAL; + } DEBUG(3,("Name query ")); - if ((n = search_for_name(question,p->ip,p->timestamp, search))) + if (search == 0) + { + /* eh? no criterion for searching database. help! */ + success = False; + } + + if (success && (n = search_for_name(&d,question,p->ip,p->timestamp, search))) { /* 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 (bcast && n->source != SELF && name_type != 0x1b) - { + + if (bcast && n->source != SELF && name_type != 0x1b) { if (!lp_wins_proxy() || same_net(p->ip,n->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 PDC type name */ + /* name is directed query, or it's self, or it's a PDC 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 */ + ttl = n->death_time - p->timestamp; retip = n->ip; nb_flags = n->nb_flags; @@ -811,92 +1310,272 @@ void reply_name_query(struct packet_struct *p) } +/**************************************************************************** + response from a name query server check. commands of type NAME_QUERY_MST_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) +{ + /* issue another command: this time to do a name status check */ + + enum cmd_type cmd = (n->cmd_type == NAME_QUERY_MST_SRV_CHK) ? + NAME_STATUS_MASTER_CHECK : NAME_STATUS_CHECK; + + /* initiate a name status check on the server that replied */ + queue_netbios_packet(d,ClientNMB,NMB_STATUS, cmd, + ans_name->name, ans_name->name_type, + 0,0, + False,False,n->to_ip); +} /**************************************************************************** -response from a name query + response from a name status check. commands of type NAME_STATUS_MASTER_CHECK + and NAME_STATUS_CHECK dealt with here. ****************************************************************************/ -static void response_netbios_packet(struct packet_struct *p) +static void response_name_status_check(struct in_addr ip, + struct nmb_packet *nmb, BOOL bcast, + struct response_record *n, struct subnet_record *d) { - struct nmb_packet *nmb = &p->packet.nmb; - struct nmb_name *question = &nmb->question.question_name; - char *qname = question->name; - BOOL bcast = nmb->header.nm_flags.bcast; - struct name_response_record *n; + /* NMB_STATUS arrives: contains workgroup name and server name required. + amongst other things. */ - if (nmb->answers == NULL) + struct nmb_name name; + fstring serv_name; + + if (interpret_node_status(d,nmb->answers->rdata, + &name,0x1d,serv_name,ip,bcast)) { - DEBUG(3,("NMB packet response from %s (bcast=%s) - UNKNOWN\n", - inet_ntoa(p->ip), - BOOLSTR(bcast))); - return; + if (*serv_name) + { + sync_server(n->cmd_type,serv_name, + name.name,name.name_type, n->to_ip); } + } + else + { + DEBUG(1,("No 0x1d name type in interpret_node_status()\n")); + } + } + + +/**************************************************************************** + response from a name query to sync browse lists or to update our netbios + entry. commands 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->to_ip))); - if (nmb->answers->rr_type == NMB_STATUS) { - DEBUG(3,("Name status ")); - } - - if (nmb->answers->rr_type == NMB_QUERY) { - DEBUG(3,("Name query ")); - } - - if (nmb->answers->rr_type == NMB_REG) { - DEBUG(3,("Name registration ")); - } - - if (nmb->answers->rr_type == NMB_REL) { - DEBUG(3,("Name release ")); - } - - DEBUG(3,("response for %s from %s (bcast=%s)\n", - namestr(&nmb->answers->rr_name), - inet_ntoa(p->ip), - BOOLSTR(bcast))); - - if (!(n = find_name_query(nmb->header.name_trn_id))) { - DEBUG(3,("unknown response (received too late or from nmblookup?)\n")); - return; - } - - n->num_msgs++; /* count number of responses received */ - - switch (n->cmd_type) + if (nmb->header.rcode == 0 && nmb->answers->rdata) { - case MASTER_SERVER_CHECK : DEBUG(4,("MASTER_SVR_CHECK\n")); break; - case SERVER_CHECK : DEBUG(4,("SERVER_CHECK\n")); break; - case FIND_MASTER : DEBUG(4,("FIND_MASTER\n")); break; + int nb_flags = nmb->answers->rdata[0]; + struct in_addr found_ip; + + putip((char*)&found_ip,&nmb->answers->rdata[2]); + + DEBUG(4, (" OK: %s\n", inet_ntoa(found_ip))); + + if (n->cmd_type == NAME_QUERY_SYNC) + { + struct work_record *work = NULL; + if ((work = find_workgroupstruct(d, ans_name->name, False))) + { + /* the server is there: sync quick before it (possibly) dies! */ + sync_browse_lists(d, work, ans_name->name, ans_name->name_type, + found_ip); + } + } + else + { + /* update our netbios name list */ + add_netbios_entry(d, ans_name->name, ans_name->name_type, + nb_flags,GET_TTL(0),STATUS_QUERY, + found_ip,False,!bcast); + } + } + else + { + DEBUG(4, (" NEGATIVE RESPONSE!\n")); + + if (n->cmd_type == 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 + */ + } + } +} + +/**************************************************************************** + 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 command type + ****************************************************************************/ +static void debug_cmd_type(int cmd_type) +{ + /* report the command type to help debugging */ + switch (cmd_type) + { + case NAME_QUERY_MST_SRV_CHK : DEBUG(4,("MASTER_SVR_CHECK\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_STATUS_MASTER_CHECK: DEBUG(4,("NAME_STAT_MST_CHK\n")); break; case NAME_STATUS_CHECK : DEBUG(4,("NAME_STATUS_CHECK\n")); break; - case CHECK_MASTER : DEBUG(4,("CHECK_MASTER\n")); break; - case NAME_CONFIRM_QUERY : DEBUG(4,("NAME_CONFIRM_QUERY\n")); break; + case NAME_QUERY_MST_CHK : DEBUG(4,("NAME_QUERY_MST_CHK\n")); break; + case NAME_REGISTER : DEBUG(4,("NAME_REGISTER\n")); break; + case NAME_RELEASE : DEBUG(4,("NAME_RELEASE\n")); break; + case NAME_QUERY_CONFIRM : DEBUG(4,("NAME_QUERY_CONFIRM\n")); break; + case NAME_QUERY_SYNC : DEBUG(4,("NAME_QUERY_SYNC\n")); break; default: break; } - switch (n->cmd_type) +} + +/**************************************************************************** + 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 *qname) { - case MASTER_SERVER_CHECK: - case SERVER_CHECK: - case FIND_MASTER: - { - if (nmb->answers->rr_type == NMB_QUERY) - { - enum cmd_type cmd = (n->cmd_type == MASTER_SERVER_CHECK) ? - NAME_STATUS_MASTER_CHECK : - NAME_STATUS_CHECK; - if (n->num_msgs > 1 && !strequal(qname,n->name.name)) + switch (nmb->answers->rr_type) { - /* one subnet, one master browser per workgroup */ - /* XXXX force an election? */ - DEBUG(1,("more than one master browser replied!\n")); + case NMB_REL: + { + if (n->num_msgs > 1) + { + DEBUG(1,("more than one release name response received!\n")); + return True; } - - /* initiate a name status check on the server that replied */ - queue_netbios_packet(ClientNMB,NMB_STATUS, cmd, - nmb->answers->rr_name.name, - nmb->answers->rr_name.name_type,0, - False,False,n->to_ip); + break; } - else + + 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->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->cmd_type) + { + case NAME_QUERY_MST_SRV_CHK: + case NAME_QUERY_SRV_CHK: + case NAME_QUERY_MST_CHK: + /* don't do case NAME_QUERY_FIND_MST: MSBROWSE isn't a unique name. */ + { + if (!strequal(qname,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; +} + +/**************************************************************************** + 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->cmd_type) + { + case NAME_RELEASE: + { + if (nmb->answers->rr_type != NMB_REL) + { + DEBUG(1,("Name release reply has wrong answer rr_type\n")); + return False; + } + break; + } + + case NAME_REGISTER: + { + if (nmb->answers->rr_type != NMB_REG) + { + DEBUG(1,("Name register reply has wrong answer rr_type\n")); + return False; + } + break; + } + + case NAME_QUERY_CONFIRM: + case NAME_QUERY_SYNC: + case NAME_QUERY_MST_SRV_CHK: + case NAME_QUERY_SRV_CHK: + case NAME_QUERY_FIND_MST: + case NAME_QUERY_MST_CHK: + { + if (nmb->answers->rr_type != NMB_QUERY) { DEBUG(1,("Name query reply has wrong answer rr_type\n")); + return False; } break; } @@ -904,76 +1583,76 @@ static void response_netbios_packet(struct packet_struct *p) case NAME_STATUS_MASTER_CHECK: case NAME_STATUS_CHECK: { - if (nmb->answers->rr_type == NMB_STATUS) + if (nmb->answers->rr_type != NMB_STATUS) { - /* NMB_STATUS arrives: contains the workgroup name - and server name we require */ - struct nmb_name name; - fstring serv_name; + DEBUG(1,("Name status reply has wrong answer rr_type\n")); + return False; + } + break; + } - if (interpret_node_status(nmb->answers->rdata, - &name,0x1d,serv_name,p->ip)) - { - if (*serv_name) - { - sync_server(n->cmd_type,serv_name, - name.name,name.name_type, - n->to_ip); - } - } - else - { - DEBUG(1,("No 0x1d name type in interpret_node_status()\n")); - } + default: + { + DEBUG(0,("unknown command received in response_netbios_packet\n")); + break; } - else + } + return True; +} + + +/**************************************************************************** + 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->cmd_type) { - DEBUG(1,("Name status reply has wrong answer rr_type\n")); + case NAME_RELEASE: + { + response_name_release(d, p); + break; + } + + case NAME_REGISTER: + { + response_name_reg(d, p); + break; } + + case NAME_QUERY_MST_SRV_CHK: + case NAME_QUERY_SRV_CHK: + case NAME_QUERY_FIND_MST: + { + response_server_check(ans_name, n, d); break; } - case CHECK_MASTER: - { - /* no action required here. it's when NO responses are received - that we need to do something (see expire_name_query_entries) */ + case NAME_STATUS_MASTER_CHECK: + case NAME_STATUS_CHECK: + { + response_name_status_check(p->ip, nmb, bcast, n, d); + break; + } + + case NAME_QUERY_CONFIRM: + case NAME_QUERY_SYNC: + { + 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\n", - namestr(&n->name), - inet_ntoa(n->to_ip))); - if (n->num_msgs > 1) - { - DEBUG(1,("more than one master browser!\n")); - } - if (nmb->answers->rr_type != NMB_QUERY) - { - DEBUG(1,("Name query reply has wrong answer rr_type\n")); - } - break; - } - case NAME_CONFIRM_QUERY: - { - DEBUG(4, ("Name query at WINS server: %s at %s - ", - namestr(&n->name), - inet_ntoa(n->to_ip))); - if (nmb->header.rcode == 0 && nmb->answers->rdata) - { - int nb_flags = nmb->answers->rdata[0]; - struct in_addr found_ip; - putip((char*)&found_ip,&nmb->answers->rdata[2]); - - DEBUG(4, (" OK: %s\n", inet_ntoa(found_ip))); - add_netbios_entry(nmb->answers->rr_name.name, - nmb->answers->rr_name.name_type, - nb_flags,GET_TTL(0),STATUS_QUERY,found_ip,False); - } - else - { - DEBUG(4, (" NEGATIVE RESPONSE\n")); - } - - break; - } + DEBUG(4, ("Master browser exists for %s at %s (just checking!)\n", + namestr(&n->name), inet_ntoa(n->to_ip))); + break; + } + default: { DEBUG(0,("unknown command received in response_netbios_packet\n")); @@ -983,6 +1662,62 @@ static void response_netbios_packet(struct packet_struct *p) } +/**************************************************************************** + response from a netbios packet. + ****************************************************************************/ +static void response_netbios_packet(struct packet_struct *p) +{ + struct nmb_packet *nmb = &p->packet.nmb; + struct nmb_name *question = &nmb->question.question_name; + struct nmb_name *ans_name = NULL; + char *qname = question->name; + BOOL bcast = nmb->header.nm_flags.bcast; + struct response_record *n; + struct subnet_record *d = NULL; + + if (!(d = find_req_subnet(p->ip, bcast))) + { + DEBUG(3,("response packet: bcast %s not known\n", inet_ntoa(p->ip))); + return; + } + + if (nmb->answers == NULL) + { + /* hm. the packet received was a response, but with no answer. wierd! */ + DEBUG(2,("NMB packet response from %s (bcast=%s) - UNKNOWN\n", + inet_ntoa(p->ip), BOOLSTR(bcast))); + return; + } + + ans_name = &nmb->answers->rr_name; + DEBUG(3,("response for %s from %s (bcast=%s)\n", + namestr(ans_name), inet_ntoa(p->ip), BOOLSTR(bcast))); + + if (!(n = find_response_record(d,nmb->header.name_trn_id))) { + DEBUG(2,("unknown netbios response (received late or from nmblookup?)\n")); + return; + } + + debug_rr_type(nmb->answers->rr_type); + + n->num_msgs++; /* count number of responses received */ + n->repeat_count = 0; /* don't resend: see expire_netbios_packets() */ + + debug_cmd_type(n->cmd_type); + + /* problem checking: multiple responses etc */ + if (response_problem_check(n, nmb, qname)) + return; + + /* now check whether the command has received the correct type of response*/ + if (!response_compatible(n, nmb)) + return; + + /* now deal with the command */ + response_process(d, p, n, nmb, bcast, ans_name); +} + + /**************************************************************************** process a nmb packet ****************************************************************************/ @@ -994,13 +1729,13 @@ void process_nmb(struct packet_struct *p) switch (nmb->header.opcode) { - case 5: - case 8: - case 9: + case 8: /* what is this?? */ + case NMB_REG: + case NMB_REG_REFRESH: { if (nmb->header.qdcount==0 || nmb->header.arcount==0) break; if (nmb->header.response) - response_name_reg(p); + response_netbios_packet(p); /* response to registration dealt with here */ else reply_name_reg(p); break; @@ -1040,7 +1775,7 @@ void process_nmb(struct packet_struct *p) break; } - case 6: + case NMB_REL: { if (nmb->header.qdcount==0 || nmb->header.arcount==0) { @@ -1049,7 +1784,7 @@ void process_nmb(struct packet_struct *p) } if (nmb->header.response) - response_name_release(p); + response_netbios_packet(p); /* response to reply dealt with in here */ else reply_name_release(p); break; diff --git a/source/namework.c b/source/namework.c index e9e939dd37f..f0fca0071e3 100644 --- a/source/namework.c +++ b/source/namework.c @@ -48,6 +48,9 @@ extern int workgroup_count; /* total number of workgroups we know about */ /* this is our browse cache database */ extern struct browse_cache_record *browserlist; +/* this is our domain/workgroup/server database */ +extern struct interface *local_interfaces; + /* this is our domain/workgroup/server database */ extern struct subnet_record *subnetlist; @@ -67,9 +70,6 @@ extern int updatecount; extern time_t StartupTime; -#define AM_MASTER(work) (work->ServerType & SV_TYPE_MASTER_BROWSER) -#define AM_BACKUP(work) (work->ServerType & SV_TYPE_BACKUP_BROWSER) - #define BROWSE_MAILSLOT "\\MAILSLOT\\BROWSE" #define GET_TTL(ttl) ((ttl)?MIN(ttl,lp_max_ttl()):lp_max_ttl()) @@ -158,28 +158,26 @@ void tell_become_backup(void) /**************************************************************************** find a server responsible for a workgroup, and sync browse lists **************************************************************************/ -static BOOL sync_browse_entry(struct browse_cache_record *b) +static void start_sync_browse_entry(struct browse_cache_record *b) { struct subnet_record *d; struct work_record *work; - /* - if (!strequal(serv_name, b->name)) - { - DEBUG(0, ("browser's netbios name (%s) does not match %s (%s)", - b->name, inet_ntoa(b->ip), serv_name)); - } - */ - if (!(d = find_domain(b->ip))) return False; - if (!(work = find_workgroupstruct(d, b->group, False))) return False; + if (!(d = find_subnet(b->ip))) return; + + /* only sync if we are the master */ if (AM_MASTER(work)) { - /* only try to sync browse lists if we are the master, otherwise - the net could get a little bit too busy */ - sync_browse_lists(work,b->name,0x20,b->ip); + + /* first check whether the group 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() */ + queue_netbios_packet(d,ClientNMB,NMB_QUERY,NAME_QUERY_SYNC, + b->group,0x20,0,0, + False,False,b->ip); } - b->synced = True; - return True; + b->synced = True; } @@ -193,6 +191,8 @@ void do_browser_lists(void) time_t t = time(NULL); if (t-last < 20) 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; @@ -202,12 +202,13 @@ void do_browser_lists(void) if (b->sync_time < t && b->synced == False) break; } - if (!b || b->synced || sync_browse_entry(b)) + if (b && !b->synced) { - /* leave entries (even ones already sync'd) for up to a minute. - this stops them getting re-sync'd too often */ + /* sync with the selected entry then remove some dead entries */ + start_sync_browse_entry(b); expire_browse_cache(t - 60); } + } @@ -221,7 +222,7 @@ void sync_server(enum cmd_type cmd, char *serv_name, char *work_name, { add_browser_entry(serv_name, name_type, work_name, 0, ip); - if (cmd == MASTER_SERVER_CHECK) + if (cmd == NAME_QUERY_MST_SRV_CHK) { /* announce ourselves as a master browser to serv_name */ do_announce_request(myname, serv_name, ANN_MasterAnnouncement, @@ -230,56 +231,23 @@ void sync_server(enum cmd_type cmd, char *serv_name, char *work_name, } -/**************************************************************************** -update workgroup database from a name registration -**************************************************************************/ -void update_from_reg(char *name, int type, struct in_addr ip) -{ - /* default server type: minimum guess at requirement XXXX */ - - DEBUG(3,("update from registration: host %s ip %s type %0x\n", - name, inet_ntoa(ip), type)); - - /* workgroup types, but not a chat type */ - if (type >= 0x1b && type <= 0x1e) - { - struct work_record *work; - struct subnet_record *d; - - if (!(d = find_domain(ip))) return; - if (!(work = find_workgroupstruct(d, name, False))) return; - - /* request the server to announce if on our subnet */ - if (d->my_interface) announce_request(work, ip); - - /* domain master type or master browser type */ - if (type == 0x1b || type == 0x1d) - { - struct hostent *hp = gethostbyaddr((char*)&ip, sizeof(ip), AF_INET); - if (hp) { - /* gethostbyaddr name may not match netbios name but who cares */ - add_browser_entry(hp->h_name, type, work->work_group, 120, ip); - } - } - } -} - - /**************************************************************************** add the default workgroup into my domain **************************************************************************/ -void add_my_domains(char *group) +void add_my_subnets(char *group) { - int n,i; - struct in_addr *ip; + struct interface *i; + + /* add or find domain on our local subnet, in the default workgroup */ if (*group == '*') return; - n = iface_count(); - for (i=0;inext) + { + add_subnet_entry(i->bcast,i->nmask,group, True, False); } } @@ -464,7 +432,7 @@ static void process_announce(struct packet_struct *p,int command,char *buf) { struct dgram_packet *dgram = &p->packet.dgram; struct in_addr ip = dgram->header.source_ip; - struct subnet_record *d = find_domain(ip); + struct subnet_record *d = find_subnet(ip); int update_count = CVAL(buf,0); int ttl = IVAL(buf,1)/1000; char *name = buf+5; @@ -545,8 +513,8 @@ static void process_master_announce(struct packet_struct *p,char *buf) { struct dgram_packet *dgram = &p->packet.dgram; struct in_addr ip = dgram->header.source_ip; - struct subnet_record *d = find_domain(ip); - struct subnet_record *mydomain = find_domain(*iface_bcast(ip)); + struct subnet_record *d = find_subnet(ip); + struct subnet_record *mydomain = find_subnet(*iface_bcast(ip)); char *name = buf; struct work_record *work; name[15] = 0; @@ -613,7 +581,7 @@ static void process_rcv_backup_list(struct packet_struct *p,char *buf) DEBUG(4,("Found browser server at %s\n", inet_ntoa(back_ip))); - if ((d = find_domain(back_ip))) + if ((d = find_subnet(back_ip))) { struct subnet_record *d1; for (d1 = subnetlist; d1; d1 = d1->next) @@ -623,8 +591,8 @@ static void process_rcv_backup_list(struct packet_struct *p,char *buf) { if (work->token == Index) { - queue_netbios_packet(ClientNMB,NMB_QUERY,SERVER_CHECK, - work->work_group,0x1d,0, + queue_netbios_packet(d1,ClientNMB,NMB_QUERY,NAME_QUERY_SRV_CHK, + work->work_group,0x1d,0,0, False,False,back_ip); return; } @@ -714,7 +682,7 @@ static void process_reset_browser(struct packet_struct *p,char *buf) { if (AM_MASTER(work)) { - become_nonmaster(d,work); + become_nonmaster(d,work,SV_TYPE_DOMAIN_MASTER|SV_TYPE_MASTER_BROWSER); } } } @@ -729,7 +697,7 @@ static void process_reset_browser(struct packet_struct *p,char *buf) struct work_record *work; for (work=d->workgrouplist;work;work=remove_workgroup(d,work)); } - add_my_domains(lp_workgroup()); + add_my_subnets(lp_workgroup()); } /* stop browsing altogether. i don't think this is a good idea! */ @@ -751,7 +719,7 @@ 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_domain(ip); + struct subnet_record *d = find_subnet(ip); int token = CVAL(buf,0); char *name = buf+1; @@ -783,7 +751,7 @@ void process_logon_packet(struct packet_struct *p,char *buf,int len) { struct dgram_packet *dgram = &p->packet.dgram; struct in_addr ip = dgram->header.source_ip; - struct subnet_record *d = find_domain(ip); + struct subnet_record *d = find_subnet(ip); char *logname,*q; char *reply_name; BOOL add_slashes = False; diff --git a/source/nmbd/nmbd.c b/source/nmbd/nmbd.c index a20c4eb9993..1d541ea95f0 100644 --- a/source/nmbd/nmbd.c +++ b/source/nmbd/nmbd.c @@ -69,6 +69,9 @@ static int sig_term() /* remove all samba names, with wins server if necessary. */ remove_my_names(); + /* announce all server entries as 0 time-to-live, 0 type */ + remove_my_servers(); + /* XXXX don't care if we never receive a response back... yet */ /* XXXX other things: if we are a master browser, force an election? */ @@ -204,6 +207,7 @@ BOOL reload_services(BOOL test) } load_interfaces(); + add_subnet_interfaces(); return(ret); } @@ -286,9 +290,13 @@ static void load_hosts_file(char *fname) ipmask = *iface_nmask(ipaddr); if (group) { - add_subnet_entry(ipaddr, ipmask, name, True); + add_subnet_entry(ipaddr, ipmask, name, True, True); } else { - add_netbios_entry(name,0x20,NB_ACTIVE,0,source,ipaddr,True); + struct subnet_record *d = find_subnet(ipaddr); + if (d) + { + add_netbios_entry(d,name,0x20,NB_ACTIVE,0,source,ipaddr,True,True); + } } } } @@ -316,16 +324,19 @@ static void process(void) announce_host(); #if 0 - /* what was this stuff supposed to do? It sent + /* XXXX what was this stuff supposed to do? It sent ANN_GetBackupListReq packets which I think should only be sent when trying to find out who to browse with */ + announce_backup(); #endif announce_master(); + query_refresh_names(); + expire_names_and_servers(); - expire_netbios_response_entries(t-10); + expire_netbios_response_entries(); refresh_my_names(t); write_browse_list(); @@ -514,7 +525,7 @@ static void usage(char *pname) return(-1); if (*group) - add_my_domains(group); + add_my_subnets(group); if (!is_daemon && !is_a_socket(0)) { DEBUG(0,("standard input is not a socket, assuming -D option\n")); @@ -535,16 +546,22 @@ static void usage(char *pname) DEBUG(3,("Loaded hosts file\n")); } + + if (!*ServerComment) strcpy(ServerComment,"Samba %v"); string_sub(ServerComment,"%v",VERSION); string_sub(ServerComment,"%h",myhostname); add_my_names(); - add_my_domains(lp_workgroup()); + add_my_subnets(lp_workgroup()); DEBUG(3,("Checked names\n")); + load_netbios_names(); + + DEBUG(3,("Loaded names\n")); + write_browse_list(); DEBUG(3,("Dumped names\n")); diff --git a/source/nmbsync.c b/source/nmbsync.c index 55cc9a04e99..2a95c60d59a 100644 --- a/source/nmbsync.c +++ b/source/nmbsync.c @@ -102,6 +102,7 @@ static BOOL add_info(struct subnet_record *d, struct work_record *work, int serv uint32 stype = IVAL(p,18); int comment_offset = IVAL(p,22) & 0xFFFF; char *cmnt = comment_offset?(rdata+comment_offset-converter):""; + struct work_record *w = work; DEBUG(4, ("\t%-16.16s %08x %s\n", sname, stype, cmnt)); @@ -137,10 +138,11 @@ static BOOL add_info(struct subnet_record *d, struct work_record *work, int serv 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 work_record *work, char *name, int nm_type, - struct in_addr ip) +void sync_browse_lists(struct subnet_record *d, struct work_record *work, + char *name, int nm_type, struct in_addr ip) { - struct subnet_record *d; + if (!d || !work || !AM_MASTER(work)) return; + pid = getpid(); uid = getuid(); gid = getgid(); @@ -159,8 +161,6 @@ void sync_browse_lists(struct work_record *work, char *name, int nm_type, if (zero_ip(dest_ip)) return; have_ip = True; - if (!(d = find_domain(ip))) return; - connect_as_ipc = True; /* connect as server and get domains, then servers */ diff --git a/source/param/loadparm.c b/source/param/loadparm.c index f4aaa16e6a4..48b9d062e86 100644 --- a/source/param/loadparm.c +++ b/source/param/loadparm.c @@ -129,6 +129,7 @@ typedef struct char *szSmbrun; char *szWINSserver; char *szInterfaces; + char *szRemoteInterfaces; int max_log_size; int mangled_stack; int max_xmit; @@ -366,6 +367,7 @@ struct parm_struct {"null passwords", P_BOOL, P_GLOBAL, &Globals.bNullPasswords, NULL}, {"strip dot", P_BOOL, P_GLOBAL, &Globals.bStripDot, NULL}, {"interfaces", P_STRING, P_GLOBAL, &Globals.szInterfaces, NULL}, + {"remote interfaces",P_STRING, P_GLOBAL, &Globals.szRemoteInterfaces,NULL}, {"password server", P_STRING, P_GLOBAL, &Globals.szPasswordServer, NULL}, {"socket options", P_GSTRING, P_GLOBAL, user_socket_options, NULL}, {"smbrun", P_STRING, P_GLOBAL, &Globals.szSmbrun, NULL}, @@ -704,6 +706,7 @@ FN_GLOBAL_STRING(lp_character_set,&Globals.szCharacterSet) FN_GLOBAL_STRING(lp_logon_script,&Globals.szLogonScript) FN_GLOBAL_STRING(lp_wins_server,&Globals.szWINSserver) FN_GLOBAL_STRING(lp_interfaces,&Globals.szInterfaces) +FN_GLOBAL_STRING(lp_remote_interfaces,&Globals.szRemoteInterfaces) FN_GLOBAL_BOOL(lp_wins_support,&Globals.bWINSsupport) FN_GLOBAL_BOOL(lp_wins_proxy,&Globals.bWINSproxy) diff --git a/source/script/mkproto.awk b/source/script/mkproto.awk index 78f3fa080b3..200d5bd050b 100644 --- a/source/script/mkproto.awk +++ b/source/script/mkproto.awk @@ -1,8 +1,3 @@ -# generate prototypes for Samba C code -# tridge, June 1996 -# added comment for each source file for use as crude index -# dan, 17 June 1996 - BEGIN { inheader=0; current_file=""; diff --git a/source/utils/nmblookup.c b/source/utils/nmblookup.c index 4d7c6e85349..4fbd8390368 100644 --- a/source/utils/nmblookup.c +++ b/source/utils/nmblookup.c @@ -93,7 +93,7 @@ static void usage(void) int main(int argc,char *argv[]) { int opt; - unsigned int lookup_type = 0; + unsigned int lookup_type = 0x0; pstring lookup; extern int optind; extern char *optarg;