Unix SMB/Netbios implementation.
Version 1.9.
NBT netbios routines and daemon - version 2
- Copyright (C) Andrew Tridgell 1994-1996
+ Copyright (C) Andrew Tridgell 1994-1997
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
extern int DEBUGLEVEL;
-extern struct in_addr ipgrp;
-
-
-/****************************************************************************
-reply to a name release
-****************************************************************************/
-void reply_name_release(struct packet_struct *p)
-{
- struct nmb_packet *nmb = &p->packet.nmb;
- struct in_addr ip;
- int nb_flags = nmb->additional->rdata[0];
- BOOL bcast = nmb->header.nm_flags.bcast;
- struct name_record *n;
- struct subnet_record *d = NULL;
- int search = 0;
-
- putip((char *)&ip,&nmb->additional->rdata[2]);
-
- DEBUG(3,("Name release on name %s\n",
- namestr(&nmb->question.question_name)));
-
- 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)
- {
- /* success = True; */
-
- remove_name(d,n);
- n = NULL;
- }
-
- if (bcast) return;
-
- /* Send a NAME RELEASE RESPONSE */
- send_name_response(p->fd, nmb->header.name_trn_id, NMB_REL,
- True, False,
- &nmb->question.question_name, nb_flags, 0, ip);
-}
-
+extern struct in_addr wins_ip;
/****************************************************************************
send a registration / release response: pos/neg
**************************************************************************/
-void send_name_response(int fd,
+static void send_name_response(int fd, struct in_addr from_ip,
int name_trn_id, int opcode, BOOL success, BOOL recurse,
struct nmb_name *reply_name, int nb_flags, int ttl,
struct in_addr ip)
rdata[1] = 0;
putip(&rdata[2],(char *)&ip);
- p.ip = ip;
+ p.ip = from_ip;
p.port = NMB_PORT;
p.fd = fd;
p.timestamp = time(NULL);
p.packet_type = NMB_PACKET;
reply_netbios_packet(&p,name_trn_id,
- rcode,opcode,recurse,
+ rcode,opcode,opcode,recurse,
reply_name, 0x20, 0x1,
ttl,
rdata, 6);
}
+/****************************************************************************
+ add a netbios entry. respond to the (possibly new) owner.
+ **************************************************************************/
+void add_name_respond(struct subnet_record *d, int fd, struct in_addr from_ip,
+ uint16 response_id,
+ struct nmb_name *name,
+ int nb_flags, int ttl, struct in_addr register_ip,
+ BOOL new_owner, struct in_addr reply_to_ip)
+{
+ /* register the old or the new owners' ip */
+ add_netbios_entry(d,name->name,name->name_type,
+ nb_flags,ttl,REGISTER,register_ip,False,True);
+
+ /* reply yes or no to the host that requested the name */
+ send_name_response(fd,from_ip, response_id, NMB_REG,
+ new_owner, False,
+ name, nb_flags, ttl, reply_to_ip);
+}
+
+
+/****************************************************************************
+reply to a name release
+****************************************************************************/
+void reply_name_release(struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ struct in_addr ip;
+ int nb_flags = nmb->additional->rdata[0];
+ BOOL bcast = nmb->header.nm_flags.bcast;
+ struct name_record *n;
+ struct subnet_record *d = NULL;
+ int search = 0;
+ BOOL success = False;
+
+ putip((char *)&ip,&nmb->additional->rdata[2]);
+
+ DEBUG(3,("Name release on name %s\n",
+ namestr(&nmb->question.question_name)));
+
+ 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?? */
+ /* For now - remove if the names match and the group bit matches. */
+ if (n && (n->source != SELF) && (NAME_GROUP(n->ip_flgs[0].nb_flags) == NAME_GROUP(nb_flags)))
+ {
+ success = True;
+
+ DEBUG(5, ("reply_name_release: Removing name %s on subnet %s\n",
+ namestr(&nmb->question.question_name), inet_ntoa(d->bcast_ip)));
+ remove_name(d,n);
+ n = NULL;
+ }
+
+ if (bcast) return;
+
+ /* Send a NAME RELEASE RESPONSE (pos/neg) see rfc1002.txt 4.2.10-11 */
+ send_name_response(p->fd,p->ip, nmb->header.name_trn_id, NMB_REL,
+ success, nmb->header.nm_flags.recursion_desired,
+ &nmb->question.question_name, nb_flags, 0, ip);
+}
+
/****************************************************************************
reply to a reg request
BOOL success = True;
BOOL secured_redirect = False;
- BOOL recurse = True; /* true if samba replies yes/no: false if caller
- must challenge the current owner of the unique
- name: applies to non-secured WINS server only
- */
struct in_addr ip, from_ip;
int search = 0;
putip((char *)&from_ip,&nmb->additional->rdata[2]);
ip = from_ip;
- DEBUG(3,("Name registration for name %s at %s\n",
+ DEBUG(3,("Name registration for name %s at %s - ",
namestr(question),inet_ntoa(ip)));
if (group)
{
/* apparently we should return 255.255.255.255 for group queries
(email from MS) */
- ip = ipgrp;
+ ip = *interpret_addr2("255.255.255.255");
}
if (!(d = find_req_subnet(p->ip, bcast)))
{
- DEBUG(3,("response packet: bcast %s not known\n",
+ DEBUG(3,("reply_name_reg: subnet %s not known\n",
inet_ntoa(p->ip)));
return;
}
if (bcast)
- search &= FIND_LOCAL;
+ search |= FIND_LOCAL;
else
- search &= FIND_WINS;
+ search |= FIND_WINS;
/* see if the name already exists */
n = find_name_search(&d, question, search, from_ip);
if (n)
{
+ DEBUG(3,("found\n"));
if (!group) /* unique names */
{
- if (n->source == SELF || NAME_GROUP(n->nb_flags))
+ if (n->source == SELF || NAME_GROUP(n->ip_flgs[0].nb_flags))
{
/* no-one can register one of samba's names, nor can they
register a name that's a group name as a unique name */
success = False;
}
- else if(!ip_equal(ip, n->ip))
+ else if(!ip_equal(ip, n->ip_flgs[0].ip))
{
-#if 0
- /* hm. this unique name doesn't belong to them. */
-
- /* XXXX rfc1001.txt says:
- * 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
- * abandon the registration. once the person wanting the name
- * has checked with the current owner, they will repeat the
- * registration packet if the current owner is dead or doesn't
- * want the name.
- */
-
- /* non-secured WINS implementation: caller is responsible
- for checking with current owner of name, then getting back
- to us... IF current owner no longer owns the unique name */
-
- /* XXXX please note also that samba cannot cope with
- _receiving_ such redirecting, non-secured registration
- packets. code to do this needs to be added.
- */
-
- secured_redirect = False;
- success = False;
- recurse = False;
-
- /* we inform on the current owner to the caller (which is
- why it's non-secure */
-
- reply_name = &n->name;
-#else
/* XXXX rfc1001.txt says:
* if we are doing secured WINS, we must send a Wait-Acknowledge
* packet (WACK) to the person who wants the name, then do a
*/
secured_redirect = True;
- recurse = False;
reply_name = &n->name;
-
-#endif /* 0 */
-
}
else
{
- n->ip = ip;
+ n->ip_flgs[0].ip = ip;
n->death_time = ttl?p->timestamp+ttl*3:0;
- DEBUG(3,("%s owner: %s\n",namestr(&n->name),inet_ntoa(n->ip)));
+ DEBUG(3,("%s owner: %s\n",namestr(&n->name),inet_ntoa(n->ip_flgs[0].ip)));
}
}
else
}
else
{
+ DEBUG(3,("not found\n"));
/* add the name to our name/subnet, or WINS, database */
n = add_netbios_entry(d,qname,qname_type,nb_flags,ttl,REGISTER,ip,
True,!bcast);
{
char rdata[2];
- /* XXXX luke is confused. RSVAL or SSVAL? assume NMB byte ordering */
+ /* XXXX i am confused. RSVAL or SSVAL? assume NMB byte ordering */
RSSVAL(rdata,0,(nmb->header.opcode&0xf) + ((nb_flags&0xff) << 4));
/* XXXX mistake in rfc1002.txt? 4.2.16: NULL is 0xa see 4.2.1.3
/* send WAIT ACKNOWLEDGEMENT see rfc1002.txt 4.2.16 */
reply_netbios_packet(p,nmb->header.name_trn_id,
- 0,NMB_WAIT_ACK,False,
+ 0,NMB_WAIT_ACK,NMB_WAIT_ACK,False,
reply_name, 0x0a, 0x01,
15*1000, /* 15 seconds long enough to wait? */
rdata, 2);
/* initiate some enquiries to the current owner. */
queue_netbios_packet(d,ClientNMB,NMB_QUERY,
- NAME_REGISTER_CHALLENGE,
- reply_name->name,reply_name->name_type,nb_flags,0,
- False, False, n->ip, p->ip);
+ NAME_REGISTER_CHALLENGE,
+ reply_name->name,reply_name->name_type,
+ nb_flags,0,0,NULL,NULL,
+ False, False, n->ip_flgs[0].ip, p->ip);
}
else
{
or an END-NODE CHALLENGE REGISTRATION RESPONSE see rfc1002.txt 4.2.7
*/
- send_name_response(p->fd, nmb->header.name_trn_id, NMB_REG,
- success, recurse,
- reply_name, nb_flags, ttl, ip);
+ send_name_response(p->fd,p->ip, nmb->header.name_trn_id, NMB_REG,
+ success, nmb->header.nm_flags.recursion_desired,
+ reply_name, nb_flags, ttl, ip);
}
}
+/* this is used to sort names for a name status into a sensible order
+ we put our own names first, then in alphabetical order */
+static int status_compare(char *n1,char *n2)
+{
+ extern pstring myname;
+ int l1,l2,l3;
+
+ /* its a bit tricky because the names are space padded */
+ for (l1=0;l1<15 && n1[l1] && n1[l1] != ' ';l1++) ;
+ for (l2=0;l2<15 && n2[l2] && n2[l2] != ' ';l2++) ;
+ l3 = strlen(myname);
+
+ if ((l1==l3) && strncmp(n1,myname,l3) == 0 &&
+ (l2!=l3 || strncmp(n2,myname,l3) != 0))
+ return -1;
+
+ if ((l2==l3) && strncmp(n2,myname,l3) == 0 &&
+ (l1!=l3 || strncmp(n1,myname,l3) != 0))
+ return 1;
+
+ return memcmp(n1,n2,18);
+}
+
/****************************************************************************
-reply to a name status query
-****************************************************************************/
+ reply to a name status query
+
+ combine the list of the local interface on which the query was made with
+ the names registered via wins.
+ ****************************************************************************/
void reply_name_status(struct packet_struct *p)
{
struct nmb_packet *nmb = &p->packet.nmb;
char *qname = nmb->question.question_name.name;
int ques_type = nmb->question.question_name.name_type;
char rdata[MAX_DGRAM_SIZE];
- char *countptr, *buf, *bufend;
- int names_added;
+ char *countptr, *buf, *bufend, *buf0;
+ int names_added,i;
struct name_record *n;
struct subnet_record *d = NULL;
+ int search = FIND_SELF | FIND_WINS | FIND_LOCAL;
- BOOL bcast = nmb->header.nm_flags.bcast;
-
- if (!(d = find_req_subnet(p->ip, bcast)))
+ /* NOTE: we always treat a name status lookup as a bcast */
+ if (!(d = find_req_subnet(p->ip, True)))
{
DEBUG(3,("Name status req: bcast %s not known\n",
inet_ntoa(p->ip)));
}
DEBUG(3,("Name status for name %s %s\n",
- namestr(&nmb->question.question_name), inet_ntoa(p->ip)));
-
+ namestr(&nmb->question.question_name),
+ inet_ntoa(p->ip)));
+
n = find_name_search(&d, &nmb->question.question_name,
- FIND_SELF|FIND_LOCAL,
- p->ip);
+ search, p->ip);
if (!n) return;
bufend = &rdata[MAX_DGRAM_SIZE] - 18;
countptr = buf = rdata;
buf += 1;
-
+ buf0 = buf;
+
names_added = 0;
-
- for (n = d->namelist ; n && buf < bufend; n = n->next)
+
+ n = d->namelist;
+
+ while (buf < bufend)
+ {
+ if (n->source == SELF)
{
int name_type = n->name.name_type;
- if (n->source != SELF) continue;
-
- /* start with first bit of putting info in buffer: the name */
-
- bzero(buf,18);
- sprintf(buf,"%-15.15s",n->name.name);
- strupper(buf);
-
- /* now check if we want to exclude other workgroup names
- from the response. if we don't exclude them, windows clients
- get confused and will respond with an error for NET VIEW */
+ /* check if we want to exclude other workgroup names
+ from the response. if we don't exclude them, windows clients
+ get confused and will respond with an error for NET VIEW */
- if (name_type >= 0x1b && name_type <= 0x20 &&
- ques_type >= 0x1b && ques_type <= 0x20)
- {
- if (!strequal(qname, n->name.name)) continue;
- }
-
- /* carry on putting name info in buffer */
+ if (!strequal(n->name.name,"*") &&
+ !strequal(n->name.name,"__SAMBA__") &&
+ (name_type < 0x1b || name_type >= 0x20 ||
+ ques_type < 0x1b || ques_type >= 0x20 ||
+ strequal(qname, n->name.name)))
+ {
+ /* start with first bit of putting info in buffer: the name */
+ bzero(buf,18);
+ sprintf(buf,"%-15.15s",n->name.name);
+ strupper(buf);
+
+ /* put name type and netbios flags in buffer */
+ buf[15] = name_type;
+ buf[16] = n->ip_flgs[0].nb_flags;
+
+ buf += 18;
- buf[15] = name_type;
- buf[16] = n->nb_flags;
-
- buf += 18;
-
- names_added++;
+ names_added++;
+ }
}
+
+ /* remove duplicate names */
+ qsort(buf0,names_added,18,QSORT_CAST status_compare);
+
+ for (i=1;i<names_added;i++) {
+ if (memcmp(buf0 + 18*i,buf0 + 18*(i-1),16) == 0) {
+ names_added--;
+ if (names_added == i) break;
+ memmove(buf0 + 18*i,buf0 + 18*(i+1),18*(names_added-i));
+ i--;
+ }
+ }
+
+ buf = buf0 + 18*names_added;
+
+ n = n->next;
+
+ if (!n)
+ {
+ /* end of this name list: add wins names too? */
+ struct subnet_record *w_d;
+
+ if (!(w_d = wins_subnet)) break;
+
+ if (w_d != d)
+ {
+ d = w_d;
+ n = d->namelist; /* start on the wins name list */
+ }
+ }
+ if (!n) break;
+ }
SCVAL(countptr,0,names_added);
SIVAL(buf,24,num_good_receives);
}
- SIVAL(buf,46,0xFFB8E5); /* undocumented - used by NT */
-
- buf += 64;
+ buf += 46;
/* Send a POSITIVE NAME STATUS RESPONSE */
reply_netbios_packet(p,nmb->header.name_trn_id,
- 0,0,True,
+ 0,NMB_STATUS,0,True,
&nmb->question.question_name,
- nmb->question.question_type,
- nmb->question.question_class,
- 0,
- rdata,PTR_DIFF(buf,rdata));
+ 0x21, 0x01,
+ 0, rdata,PTR_DIFF(buf,rdata));
}
char rdata[6];
struct subnet_record *d = NULL;
BOOL success = True;
- struct name_record *n;
+ struct name_record *n = NULL;
/* directed queries are for WINS server: broadcasts are local SELF queries.
- the exception is PDC names. */
+ the exception is Domain Master names. */
- int search = bcast ? FIND_LOCAL | FIND_SELF : FIND_WINS;
-
- if (name_type == 0x1b)
+ int search = bcast ? FIND_LOCAL | FIND_WINS: FIND_WINS;
+
+ if (search & FIND_LOCAL)
{
- /* even if it's a broadcast, we don't ignore queries for PDC names */
- search |= FIND_WINS;
- search &= ~FIND_SELF;
+ if (!(d = find_req_subnet(p->ip, bcast)))
+ {
+ DEBUG(3,("name query: bcast %s not known\n",
+ inet_ntoa(p->ip)));
+ success = False;
+ }
}
-
- if (!(d = find_req_subnet(p->ip, bcast)))
+ else
{
- DEBUG(3,("name query: bcast %s not known\n",
- inet_ntoa(p->ip)));
- success = False;
+ if (!(d = wins_subnet))
+ {
+ DEBUG(3,("name query: wins search %s not known\n",
+ inet_ntoa(p->ip)));
+ success = False;
+ }
}
- DEBUG(3,("Name query "));
+ DEBUG(3,("Name query from %s for name %s<0x%x>\n",
+ inet_ntoa(p->ip), question->name, question->name_type));
if (search == 0)
{
success = False;
}
- if (success && (n = search_for_name(&d,question,p->ip,p->timestamp, search)))
+ if (!bcast && (name_type == 0x1d) && lp_wins_support())
{
- /* don't respond to broadcast queries unless the query is for
- a name we own or it is for a Primary Domain Controller name */
+ /* see WINS manager HELP - 'How WINS Handles Special Names' */
+ /* a WINS query (unicasted) for a 0x1d name must always return False */
+ success = False;
+ }
- if (bcast && n->source != SELF && name_type != 0x1b) {
- if (!lp_wins_proxy() || same_net(p->ip,n->ip,*iface_nmask(p->ip))) {
+ if (success)
+ {
+ /* look up the name in the cache */
+ n = find_name_search(&d, question, search, p->ip);
+
+ /* it is a name that already failed DNS lookup or it's expired */
+ if (n && (n->source == DNSFAIL ||
+ (n->death_time && n->death_time < p->timestamp)))
+ {
+ success = False;
+ }
+
+ /* do we want to do dns lookups? */
+ /* XXXX this DELAYS nmbd while it does a search. not a good idea
+ but there's no pleasant alternative. phil@hands.com suggested
+ making the name a full DNS name, which would succeed / fail
+ much quicker.
+ */
+ if (success && !n && (lp_wins_proxy() || !bcast))
+ {
+ n = dns_name_search(question, p->timestamp);
+ }
+ }
+
+ if (!n) success = False;
+
+ if (success)
+ {
+ if (bcast && n->source != SELF && name_type != 0x1b)
+ {
+ /* don't respond to broadcast queries unless the query is for
+ a name we own or it is for a Primary Domain Controller name */
+
+ if (!lp_wins_proxy() ||
+ same_net(p->ip,n->ip_flgs[0].ip,*iface_nmask(p->ip)))
+ {
/* never reply with a negative response to broadcast queries */
return;
- }
- }
+ }
+ }
- /* name is directed query, or it's self, or it's a 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 */
+ /* name is directed query, or it's self, or it's a Domain Master type
+ name, or we're replying on behalf of a caller because they are on a
+ different subnet and cannot hear the broadcast. XXXX lp_wins_proxy
+ should be switched off in environments where broadcasts are forwarded
+ */
/* XXXX note: for proxy servers, we should forward the query on to
another WINS server if the name is not in our database, or we are
not a WINS server ourselves
*/
- ttl = n->death_time - p->timestamp;
- retip = n->ip;
- nb_flags = n->nb_flags;
+ ttl = n->death_time ? n->death_time - p->timestamp : GET_TTL(0);
+ retip = n->ip_flgs[0].ip;
+ nb_flags = n->ip_flgs[0].nb_flags;
}
- else
- {
- if (bcast) return; /* never reply negative response to bcasts */
- success = False;
- }
-
+
+ if (!success && bcast) return; /* never reply negative response to bcasts */
+
/* if the IP is 0 then substitute my IP */
if (zero_ip(retip)) retip = *iface_ip(p->ip);
+ /* SPECIAL CASE... If we are a WINS server and the request is explicitly
+ *to* the WINS server and the name type is WORKGROUP<0x1e> we should
+ respond with the local broadcast address 255.255.255.255.
+ */
+ if(!bcast && (name_type == 0x1e) && lp_wins_support())
+ retip = *interpret_addr2("255.255.255.255");
+
if (success)
{
rcode = 0;
}
reply_netbios_packet(p,nmb->header.name_trn_id,
- rcode,0,True,
+ rcode,NMB_QUERY,0,True,
&nmb->question.question_name,
- nmb->question.question_type,
- nmb->question.question_class,
+ 0x20, 0x01,
ttl,
rdata, success ? 6 : 0);
}
-
-