diff options
Diffstat (limited to 'source3/nameresp.c')
-rw-r--r-- | source3/nameresp.c | 384 |
1 files changed, 272 insertions, 112 deletions
diff --git a/source3/nameresp.c b/source3/nameresp.c index b244d81159..31df799691 100644 --- a/source3/nameresp.c +++ b/source3/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,110 +64,123 @@ extern void add_response_record(struct name_response_record *n) } -/******************************************************************* - remove old name response entries - ******************************************************************/ -void expire_netbios_response_entries(time_t t) +/*************************************************************************** + deals with an entry before it dies + **************************************************************************/ +static void dead_netbios_entry(struct subnet_record *d, + struct response_record *n) { - struct name_response_record *n; - struct name_response_record *nextn; + DEBUG(3,("Removing dead netbios entry for %s %s (num_msgs=%d)\n", + inet_ntoa(n->to_ip), namestr(&n->name), n->num_msgs)); - for (n = nameresponselist; n; n = nextn) + switch (n->cmd_type) { - if (n->start_time < t) + case NAME_QUERY_CONFIRM: { - 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 (!lp_wins_support()) return; /* only if we're a WINS server */ - 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); - } - - nextn = n->next; + { + /* oops. name query had no response. check that the name is + unique and then remove it from our WINS database */ - if (n->prev) n->prev->next = n->next; - if (n->next) n->next->prev = n->prev; + /* IMPORTANT: see query_refresh_names() */ - if (nameresponselist == n) nameresponselist = n->next; - - free(n); - } - else + if ((!NAME_GROUP(n->nb_flags))) { - nextn = n->next; + 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; + } - -/**************************************************************************** - 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) + case NAME_QUERY_MST_CHK: { - struct packet_struct p; - struct nmb_packet *nmb = &p.packet.nmb; - struct res_rec answers; - char *packet_type = "unknown"; + /* if no response received, the master browser must have gone + down on that subnet, without telling anyone. */ - p = *p1; + /* IMPORTANT: see response_netbios_packet() */ - if (rr_type == NMB_STATUS) packet_type = "nmb_status"; - if (rr_type == NMB_QUERY ) packet_type = "nmb_query"; - if (rr_type == NMB_REG ) packet_type = "nmb_reg"; - if (rr_type == NMB_REL ) packet_type = "nmb_rel"; + if (n->num_msgs == 0) + browser_gone(n->name.name, n->to_ip); + break; + } - DEBUG(4,("replying netbios packet: %s %s\n", - packet_type, namestr(rr_name), inet_ntoa(p.ip))); + 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) */ - nmb->header.name_trn_id = trn_id; - nmb->header.opcode = opcode; - nmb->header.response = True; - nmb->header.nm_flags.bcast = False; - nmb->header.nm_flags.recursion_available = recurse; - nmb->header.nm_flags.recursion_desired = True; - nmb->header.nm_flags.trunc = False; - nmb->header.nm_flags.authoritative = True; + /* IMPORTANT: see response_name_release() */ - nmb->header.qdcount = 0; - nmb->header.ancount = 1; - nmb->header.nscount = 0; - nmb->header.arcount = 0; - nmb->header.rcode = 0; + 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; + } - bzero((char*)&nmb->question,sizeof(nmb->question)); + 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. */ - nmb->answers = &answers; - bzero((char*)nmb->answers,sizeof(*nmb->answers)); + /* IMPORTANT: see response_name_reg() */ - nmb->answers->rr_name = *rr_name; - nmb->answers->rr_type = rr_type; - nmb->answers->rr_class = rr_class; - nmb->answers->ttl = ttl; + enum name_source source = ismyip(n->to_ip) ? SELF : REGISTER; - if (data && len) - { - nmb->answers->rdlength = len; - memcpy(nmb->answers->rdata, data, len); + 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. */ - p.packet_type = NMB_PACKET; - - debug_nmb_packet(&p); + DEBUG(1,("WINS server did not respond to name registration!\n")); + } + break; + } - send_packet(&p); + default: + { + /* nothing to do but delete the dead expected-response structure */ + /* this is normal. */ + break; + } + } } /**************************************************************************** initiate a netbios packet ****************************************************************************/ -uint16 initiate_netbios_packet(int fd,int quest_type,char *name,int name_type, +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) { @@ -173,6 +190,8 @@ uint16 initiate_netbios_packet(int fd,int quest_type,char *name,int name_type, 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; } @@ -181,7 +200,7 @@ uint16 initiate_netbios_packet(int fd,int quest_type,char *name,int name_type, 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; + if (opcode == -1) return; bzero((char *)&p,sizeof(p)); @@ -189,7 +208,9 @@ uint16 initiate_netbios_packet(int fd,int quest_type,char *name,int name_type, (getpid()%(unsigned)100); name_trn_id = (name_trn_id+1) % (unsigned)0x7FFF; - nmb->header.name_trn_id = name_trn_id; + 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; @@ -229,10 +250,120 @@ uint16 initiate_netbios_packet(int fd,int quest_type,char *name,int name_type, p.timestamp = time(NULL); p.packet_type = NMB_PACKET; - if (!send_packet(&p)) - return(0); + if (!send_packet(&p)) *id = 0xffff; - return(name_trn_id); + 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() +{ + struct response_record *n; + struct response_record *nextn; + struct subnet_record *d; + + for (d = subnetlist; d; d = d->next) + for (n = d->responselist; n; n = nextn) + { + 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); + + 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; + } +} + + +/**************************************************************************** + 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) +{ + struct packet_struct p; + struct nmb_packet *nmb = &p.packet.nmb; + struct res_rec answers; + char *packet_type = "unknown"; + + p = *p1; + + if (rr_type == NMB_STATUS) packet_type = "nmb_status"; + if (rr_type == NMB_QUERY ) packet_type = "nmb_query"; + if (rr_type == NMB_REG ) packet_type = "nmb_reg"; + if (rr_type == NMB_REL ) packet_type = "nmb_rel"; + + DEBUG(4,("replying netbios packet: %s %s\n", + packet_type, namestr(rr_name), inet_ntoa(p.ip))); + + nmb->header.name_trn_id = trn_id; + nmb->header.opcode = opcode; + nmb->header.response = True; + nmb->header.nm_flags.bcast = False; + nmb->header.nm_flags.recursion_available = recurse; + nmb->header.nm_flags.recursion_desired = True; + nmb->header.nm_flags.trunc = False; + nmb->header.nm_flags.authoritative = True; + + nmb->header.qdcount = 0; + nmb->header.ancount = 1; + nmb->header.nscount = 0; + nmb->header.arcount = 0; + nmb->header.rcode = 0; + + bzero((char*)&nmb->question,sizeof(nmb->question)); + + nmb->answers = &answers; + bzero((char*)nmb->answers,sizeof(*nmb->answers)); + + nmb->answers->rr_name = *rr_name; + nmb->answers->rr_type = rr_type; + nmb->answers->rr_class = rr_class; + nmb->answers->ttl = ttl; + + if (data && len) + { + nmb->answers->rdlength = len; + memcpy(nmb->answers->rdata, data, len); + } + + p.packet_type = NMB_PACKET; + + debug_nmb_packet(&p); + + send_packet(&p); } @@ -241,8 +372,9 @@ uint16 initiate_netbios_packet(int fd,int quest_type,char *name,int name_type, 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, + struct in_addr wins_ip = ipgrp; + struct response_record *n; + uint16 id = 0xffff; + + /* ha ha. no. do NOT broadcast to 255.255.255.255: it's a pseudo address */ + if (ip_equal(wins_ip, to_ip)) return; + + initiate_netbios_packet(&id, fd, quest_type, name, name_type, nb_flags, bcast, recurse, to_ip); - struct name_response_record *n; - if (id == 0) return; + 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 */ |