summaryrefslogtreecommitdiff
path: root/source3/nmbd/nmbd_nameregister.c
diff options
context:
space:
mode:
Diffstat (limited to 'source3/nmbd/nmbd_nameregister.c')
-rw-r--r--source3/nmbd/nmbd_nameregister.c539
1 files changed, 301 insertions, 238 deletions
diff --git a/source3/nmbd/nmbd_nameregister.c b/source3/nmbd/nmbd_nameregister.c
index 91b6f8b190..4ac5473d4a 100644
--- a/source3/nmbd/nmbd_nameregister.c
+++ b/source3/nmbd/nmbd_nameregister.c
@@ -25,6 +25,10 @@
extern fstring global_myworkgroup;
+/* forward declarations */
+static void wins_next_registration(struct response_record *rrec);
+
+
/****************************************************************************
Deal with a response packet when registering one of our names.
****************************************************************************/
@@ -32,127 +36,131 @@ extern fstring global_myworkgroup;
static void register_name_response(struct subnet_record *subrec,
struct response_record *rrec, struct packet_struct *p)
{
- /*
- * If we are registering broadcast, then getting a response is an
- * error - we do not have the name. If we are registering unicast,
- * then we expect to get a response.
- */
-
- struct nmb_packet *nmb = &p->packet.nmb;
- BOOL bcast = nmb->header.nm_flags.bcast;
- BOOL success = True;
- struct nmb_name *question_name = &rrec->packet->packet.nmb.question.question_name;
- struct nmb_name *answer_name = &nmb->answers->rr_name;
- int ttl = 0;
- uint16 nb_flags = 0;
- struct in_addr registered_ip;
-
- /* Sanity check. Ensure that the answer name in the incoming packet is the
- same as the requested name in the outgoing packet. */
-
- if(!question_name || !answer_name)
- {
- DEBUG(0,("register_name_response: malformed response (%s is NULL).\n",
- question_name ? "question_name" : "answer_name" ));
- return;
- }
-
- if(!nmb_name_equal(question_name, answer_name))
- {
- DEBUG(0,("register_name_response: Answer name %s differs from question \
-name %s.\n", nmb_namestr(answer_name), nmb_namestr(question_name)));
- return;
- }
-
- if(bcast)
- {
- /*
- * Special hack to cope with old Samba nmbd's.
- * Earlier versions of Samba (up to 1.9.16p11) respond
- * to a broadcast name registration of WORKGROUP<1b> when
- * they should not. Hence, until these versions are gone,
- * we should treat such errors as success for this particular
- * case only. jallison@whistle.com.
- */
+ /*
+ * If we are registering broadcast, then getting a response is an
+ * error - we do not have the name. If we are registering unicast,
+ * then we expect to get a response.
+ */
+
+ struct nmb_packet *nmb = &p->packet.nmb;
+ BOOL bcast = nmb->header.nm_flags.bcast;
+ BOOL success = True;
+ struct nmb_name *question_name = &rrec->packet->packet.nmb.question.question_name;
+ struct nmb_name *answer_name = &nmb->answers->rr_name;
+ struct nmb_packet *sent_nmb = &rrec->packet->packet.nmb;
+ int ttl = 0;
+ uint16 nb_flags = 0;
+ struct in_addr register_ip;
+ fstring reg_name;
+
+ putip(&register_ip,&sent_nmb->additional->rdata[2]);
+ fstrcpy(reg_name, inet_ntoa(register_ip));
+
+ if (subrec == unicast_subnet) {
+ /* we know that this wins server is definately alive - for the moment! */
+ wins_srv_alive(rrec->packet->ip, register_ip);
+ }
+
+ /* Sanity check. Ensure that the answer name in the incoming packet is the
+ same as the requested name in the outgoing packet. */
+
+ if(!question_name || !answer_name) {
+ DEBUG(0,("register_name_response: malformed response (%s is NULL).\n",
+ question_name ? "question_name" : "answer_name" ));
+ return;
+ }
+
+ if(!nmb_name_equal(question_name, answer_name)) {
+ DEBUG(0,("register_name_response: Answer name %s differs from question name %s.\n",
+ nmb_namestr(answer_name), nmb_namestr(question_name)));
+ return;
+ }
+ if(bcast) {
+ /*
+ * Special hack to cope with old Samba nmbd's.
+ * Earlier versions of Samba (up to 1.9.16p11) respond
+ * to a broadcast name registration of WORKGROUP<1b> when
+ * they should not. Hence, until these versions are gone,
+ * we should treat such errors as success for this particular
+ * case only. jallison@whistle.com.
+ */
+
#if 1 /* OLD_SAMBA_SERVER_HACK */
- if((nmb->header.rcode == ACT_ERR) && strequal(global_myworkgroup, answer_name->name) &&
- (answer_name->name_type == 0x1b))
- {
- /* Pretend we did not get this. */
- rrec->num_msgs--;
-
- DEBUG(5,("register_name_response: Ignoring broadcast response to \
-registration of name %s due to old Samba server bug.\n", nmb_namestr(answer_name)));
- return;
- }
+ if((nmb->header.rcode == ACT_ERR) && strequal(global_myworkgroup, answer_name->name) &&
+ (answer_name->name_type == 0x1b)) {
+ /* Pretend we did not get this. */
+ rrec->num_msgs--;
+
+ DEBUG(5,("register_name_response: Ignoring broadcast response to registration of name %s due to old Samba server bug.\n",
+ nmb_namestr(answer_name)));
+ return;
+ }
#endif /* OLD_SAMBA_SERVER_HACK */
- /* Someone else has the name. Log the problem. */
- DEBUG(1,("register_name_response: Failed to register \
-name %s on subnet %s via broadcast. Error code was %d. Reject came from IP %s\n",
- nmb_namestr(answer_name),
- subrec->subnet_name, nmb->header.rcode, inet_ntoa(p->ip)));
- success = False;
- }
- else
- {
- /* Unicast - check to see if the response allows us to have the name. */
- if(nmb->header.rcode != 0)
- {
- /* Error code - we didn't get the name. */
- success = False;
-
- DEBUG(0,("register_name_response: server at IP %s rejected our \
-name registration of %s with error code %d.\n", inet_ntoa(p->ip),
- nmb_namestr(answer_name), nmb->header.rcode));
-
- }
- else if(nmb->header.opcode == NMB_WACK_OPCODE)
- {
- /* WINS server is telling us to wait. Pretend we didn't get
- the response but don't send out any more register requests. */
-
- DEBUG(5,("register_name_response: WACK from WINS server %s in registering \
-name %s on subnet %s.\n", inet_ntoa(p->ip), nmb_namestr(answer_name), subrec->subnet_name));
-
- rrec->repeat_count = 0;
- /* How long we should wait for. */
- rrec->repeat_time = p->timestamp + nmb->answers->ttl;
- rrec->num_msgs--;
- return;
- }
- else
- {
- success = True;
- /* Get the data we need to pass to the success function. */
- nb_flags = get_nb_flags(nmb->answers->rdata);
- putip((char*)&registered_ip,&nmb->answers->rdata[2]);
- ttl = nmb->answers->ttl;
- }
- }
-
- DEBUG(5,("register_name_response: %s in registering name %s on subnet %s.\n",
- success ? "success" : "failure", nmb_namestr(answer_name), subrec->subnet_name));
-
- if(success)
- {
- /* Enter the registered name into the subnet name database before calling
- the success function. */
- standard_success_register(subrec, rrec->userdata, answer_name, nb_flags, ttl, registered_ip);
- if( rrec->success_fn)
- (*(register_name_success_function)rrec->success_fn)(subrec, rrec->userdata, answer_name, nb_flags, ttl, registered_ip);
- }
- else
- {
- if( rrec->fail_fn)
- (*(register_name_fail_function)rrec->fail_fn)(subrec, rrec, question_name);
- /* Remove the name. */
- standard_fail_register( subrec, rrec, question_name);
- }
-
- /* Ensure we don't retry. */
- remove_response_record(subrec, rrec);
+ /* Someone else has the name. Log the problem. */
+ DEBUG(1,("register_name_response: Failed to register name %s IP %s on subnet %s via broadcast. Error code was %d. Reject came from IP %s\n",
+ nmb_namestr(answer_name),
+ reg_name,
+ subrec->subnet_name, nmb->header.rcode, inet_ntoa(p->ip)));
+ success = False;
+ } else {
+ /* Unicast - check to see if the response allows us to have the name. */
+ if(nmb->header.rcode != 0) {
+ /* Error code - we didn't get the name. */
+ success = False;
+
+ DEBUG(0,("register_name_response: %sserver at IP %s rejected our name registration of %s IP %s with error code %d.\n",
+ subrec==unicast_subnet?"WINS ":"",
+ inet_ntoa(p->ip),
+ nmb_namestr(answer_name),
+ reg_name,
+ nmb->header.rcode));
+ } else if (nmb->header.opcode == NMB_WACK_OPCODE) {
+ /* WINS server is telling us to wait. Pretend we didn't get
+ the response but don't send out any more register requests. */
+
+ DEBUG(5,("register_name_response: WACK from WINS server %s in registering name %s IP %s\n",
+ inet_ntoa(p->ip), nmb_namestr(answer_name), reg_name));
+
+ rrec->repeat_count = 0;
+ /* How long we should wait for. */
+ rrec->repeat_time = p->timestamp + nmb->answers->ttl;
+ rrec->num_msgs--;
+ return;
+ } else {
+ success = True;
+ /* Get the data we need to pass to the success function. */
+ nb_flags = get_nb_flags(nmb->answers->rdata);
+ ttl = nmb->answers->ttl;
+
+ /* send off a registration for the next IP, if any */
+ wins_next_registration(rrec);
+ }
+ }
+
+ DEBUG(5,("register_name_response: %s in registering %sname %s IP %s with %s.\n",
+ success ? "success" : "failure",
+ subrec==unicast_subnet?"WINS ":"",
+ nmb_namestr(answer_name),
+ reg_name,
+ inet_ntoa(rrec->packet->ip)));
+
+ if(success) {
+ /* Enter the registered name into the subnet name database before calling
+ the success function. */
+ standard_success_register(subrec, rrec->userdata, answer_name, nb_flags, ttl, register_ip);
+ if( rrec->success_fn)
+ (*(register_name_success_function)rrec->success_fn)(subrec, rrec->userdata, answer_name, nb_flags, ttl, register_ip);
+ } else {
+ if( rrec->fail_fn)
+ (*(register_name_fail_function)rrec->fail_fn)(subrec, rrec, question_name);
+ /* Remove the name. */
+ standard_fail_register( subrec, rrec, question_name);
+ }
+
+ /* Ensure we don't retry. */
+ remove_response_record(subrec, rrec);
}
@@ -163,49 +171,68 @@ static void wins_registration_timeout(struct subnet_record *subrec,
struct response_record *rrec)
{
struct userdata_struct *userdata = rrec->userdata;
+ struct nmb_packet *sent_nmb = &rrec->packet->packet.nmb;
+ struct nmb_name *nmbname = &sent_nmb->question.question_name;
+ struct in_addr register_ip;
+ fstring src_addr;
- DEBUG(2,("register_name_timeout_response: WINS server at address %s is not responding.\n",
- inet_ntoa(rrec->packet->ip)));
+ putip(&register_ip,&sent_nmb->additional->rdata[2]);
+
+ fstrcpy(src_addr, inet_ntoa(register_ip));
+
+ DEBUG(2,("wins_registration_timeout: WINS server %s timed out registering IP %s\n",
+ inet_ntoa(rrec->packet->ip), src_addr));
- /* mark it temporarily dead */
- wins_srv_died(rrec->packet->ip);
+ /* mark it temporarily dead for this source address */
+ wins_srv_died(rrec->packet->ip, register_ip);
/* if we have some userdata then use that to work out what
wins server to try next */
if (userdata) {
const char *tag = (const char *)userdata->data;
- struct nmb_packet *sent_nmb = &rrec->packet->packet.nmb;
- struct nmb_name *nmbname = &sent_nmb->question.question_name;
- /* and try the next wins server in our failover list */
- rrec->packet->ip = wins_srv_ip_tag(tag);
+ /* try the next wins server in our failover list for
+ this tag */
+ rrec->packet->ip = wins_srv_ip_tag(tag, register_ip);
+ }
- /* also update the UNICODE subnet IPs */
- subrec->bcast_ip = subrec->mask_ip = subrec->myip = rrec->packet->ip;
+ /* if we have run out of wins servers for this tag then they
+ must all have timed out. We treat this as *success*, not
+ failure, and go into our standard name refresh mode. This
+ copes with all the wins servers being down */
+ if (wins_srv_is_dead(rrec->packet->ip, register_ip)) {
+ uint16 nb_flags = get_nb_flags(sent_nmb->additional->rdata);
+ int ttl = sent_nmb->additional->ttl;
+
+ standard_success_register(subrec, userdata, nmbname, nb_flags, ttl, register_ip);
+ if(rrec->success_fn) {
+ (*(register_name_success_function)rrec->success_fn)(subrec,
+ rrec->userdata,
+ nmbname,
+ nb_flags,
+ ttl,
+ register_ip);
+ }
- DEBUG(6,("Retrying register of name %s with WINS server %s using tag '%s'\n",
- nmb_namestr(nmbname), inet_ntoa(rrec->packet->ip), tag));
- }
+ /* send off a registration for the next IP, if any */
+ wins_next_registration(rrec);
- /* Keep trying to contact the WINS server periodically. This allows
- us to work correctly if the WINS server is down temporarily when
- we come up. */
+ /* don't need to send this packet any more */
+ remove_response_record(subrec, rrec);
+ return;
+ }
- /* Reset the number of attempts to zero and double the interval between
- retries. Max out at 5 minutes. */
- rrec->repeat_count = 3;
- rrec->repeat_interval *= 2;
- if(rrec->repeat_interval > (5 * 60))
- rrec->repeat_interval = (5 * 60);
- rrec->repeat_time = time(NULL) + rrec->repeat_interval;
+ /* we will be moving to the next WINS server for this group,
+ send it immediately */
+ rrec->repeat_count = 2;
+ rrec->repeat_time = time(NULL) + 1;
rrec->in_expiration_processing = False;
-
- DEBUG(5,("register_name_timeout_response: increasing WINS timeout to %d seconds.\n",
- (int)rrec->repeat_interval));
+
+ DEBUG(6,("Retrying register of name %s IP %s with WINS server %s\n",
+ nmb_namestr(nmbname), src_addr, inet_ntoa(rrec->packet->ip)));
/* notice that we don't remove the response record. This keeps
- us trying to register with each of our failover wins
- servers until we succeed */
+ us trying to register with each of our failover wins servers */
}
@@ -241,13 +268,9 @@ static void register_name_timeout_response(struct subnet_record *subrec,
putip(&registered_ip,&sent_nmb->additional->rdata[2]);
}
} else {
- /* Unicast - if no responses then it's an error. */
- if(rrec->num_msgs == 0) {
- wins_registration_timeout(subrec, rrec);
- return;
- }
-
- /* we got responses, but timed out?? bizarre. Treat it as failure. */
+ /* wins timeouts are special */
+ wins_registration_timeout(subrec, rrec);
+ return;
}
DEBUG(5,("register_name_timeout_response: %s in registering name %s on subnet %s.\n",
@@ -281,7 +304,8 @@ static void multihomed_register_one(struct nmb_name *nmbname,
const char *tag)
{
struct userdata_struct *userdata;
- struct in_addr wins_ip = wins_srv_ip_tag(tag);
+ struct in_addr wins_ip = wins_srv_ip_tag(tag, ip);
+ fstring ip_str;
userdata = (struct userdata_struct *)malloc(sizeof(*userdata) + strlen(tag) + 1);
if (!userdata) {
@@ -292,8 +316,10 @@ static void multihomed_register_one(struct nmb_name *nmbname,
userdata->userdata_len = strlen(tag) + 1;
strlcpy(userdata->data, tag, userdata->userdata_len);
- DEBUG(6,("Registering name %s with WINS server %s using tag '%s'\n",
- nmb_namestr(nmbname), inet_ntoa(wins_ip), tag));
+ fstrcpy(ip_str, inet_ntoa(ip));
+
+ DEBUG(6,("Registering name %s IP %s with WINS server %s using tag '%s'\n",
+ nmb_namestr(nmbname), ip_str, inet_ntoa(wins_ip), tag));
if (queue_register_multihomed_name(unicast_subnet,
register_name_response,
@@ -308,14 +334,65 @@ static void multihomed_register_one(struct nmb_name *nmbname,
DEBUG(0,("multihomed_register_one: Failed to send packet trying to register name %s IP %s\n",
nmb_namestr(nmbname), inet_ntoa(ip)));
}
+
+ free(userdata);
+}
+
+
+/****************************************************************************
+we have finished the registration of one IP and need to see if we have
+any more IPs left to register with this group of wins server for this name
+****************************************************************************/
+static void wins_next_registration(struct response_record *rrec)
+{
+ struct nmb_packet *sent_nmb = &rrec->packet->packet.nmb;
+ struct nmb_name *nmbname = &sent_nmb->question.question_name;
+ uint16 nb_flags = get_nb_flags(sent_nmb->additional->rdata);
+ struct userdata_struct *userdata = rrec->userdata;
+ const char *tag;
+ struct in_addr last_ip;
+ struct subnet_record *subrec;
+
+ putip(&last_ip,&sent_nmb->additional->rdata[2]);
+
+ if (!userdata) {
+ /* it wasn't multi-homed */
+ return;
+ }
+
+ tag = (const char *)userdata->data;
+
+ for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) {
+ if (ip_equal(last_ip, subrec->myip)) {
+ subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec);
+ break;
+ }
+ }
+
+ if (!subrec) {
+ /* no more to do! */
+ return;
+ }
+
+ switch (sent_nmb->header.opcode) {
+ case NMB_NAME_MULTIHOMED_REG_OPCODE:
+ multihomed_register_one(nmbname, nb_flags, NULL, NULL, subrec->myip, tag);
+ break;
+ case NMB_NAME_REFRESH_OPCODE_8:
+ queue_wins_refresh(nmbname,
+ register_name_response,
+ register_name_timeout_response,
+ nb_flags, subrec->myip, tag);
+ break;
+ }
}
/****************************************************************************
Try and register one of our names on the unicast subnet - multihomed.
****************************************************************************/
-static BOOL multihomed_register_name( struct nmb_name *nmbname, uint16 nb_flags,
- register_name_success_function success_fn,
- register_name_fail_function fail_fn)
+static void multihomed_register_name(struct nmb_name *nmbname, uint16 nb_flags,
+ register_name_success_function success_fn,
+ register_name_fail_function fail_fn)
{
/*
If we are adding a group name, we just send multiple
@@ -338,16 +415,16 @@ static BOOL multihomed_register_name( struct nmb_name *nmbname, uint16 nb_flags,
*/
int num_ips=0;
int i, t;
- struct in_addr *ip_list = NULL;
struct subnet_record *subrec;
char **wins_tags;
+ struct in_addr *ip_list;
for(subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec) )
num_ips++;
if((ip_list = (struct in_addr *)malloc(num_ips * sizeof(struct in_addr)))==NULL) {
DEBUG(0,("multihomed_register_name: malloc fail !\n"));
- return True;
+ return;
}
for (subrec = FIRST_SUBNET, i = 0;
@@ -356,106 +433,92 @@ static BOOL multihomed_register_name( struct nmb_name *nmbname, uint16 nb_flags,
ip_list[i] = subrec->myip;
}
- (void)add_name_to_subnet( unicast_subnet, nmbname->name, nmbname->name_type,
- nb_flags, lp_max_ttl(), SELF_NAME,
- num_ips, ip_list);
+ add_name_to_subnet(unicast_subnet, nmbname->name, nmbname->name_type,
+ nb_flags, lp_max_ttl(), SELF_NAME,
+ num_ips, ip_list);
/* get the list of wins tags - we try to register for each of them */
wins_tags = wins_srv_tags();
- /* Now try and register the name, num_ips times for each wins tag. */
- for (t=0; wins_tags[t]; t++) {
- for (i = 0; i < num_ips; i++) {
- multihomed_register_one(nmbname, nb_flags,
- success_fn, fail_fn,
- ip_list[i],
- wins_tags[t]);
- }
+ /* Now try and register the name for each wins tag. Note that
+ at this point we only register our first IP with each wins
+ group. We will register the rest from
+ wins_next_registration() when we get the reply for this
+ one. That follows the way W2K does things (tridge)
+ */
+ for (t=0; wins_tags && wins_tags[t]; t++) {
+ multihomed_register_one(nmbname, nb_flags,
+ success_fn, fail_fn,
+ ip_list[0],
+ wins_tags[t]);
}
wins_srv_tags_free(wins_tags);
SAFE_FREE(ip_list);
-
- return False;
}
+
/****************************************************************************
Try and register one of our names.
****************************************************************************/
-
-BOOL register_name(struct subnet_record *subrec,
+void register_name(struct subnet_record *subrec,
char *name, int type, uint16 nb_flags,
register_name_success_function success_fn,
register_name_fail_function fail_fn,
struct userdata_struct *userdata)
{
- struct nmb_name nmbname;
-
- make_nmb_name(&nmbname, name, type);
-
- /* Always set the NB_ACTIVE flag on the name we are
- registering. Doesn't make sense without it.
- */
-
- nb_flags |= NB_ACTIVE;
-
- /* If this is the unicast subnet, and we are a multi-homed
- host, then register a multi-homed name. */
-
- if( (subrec == unicast_subnet) && we_are_multihomed()) {
- return multihomed_register_name(&nmbname, nb_flags,
- success_fn, fail_fn);
- }
-
- if(queue_register_name( subrec,
- register_name_response,
- register_name_timeout_response,
- success_fn,
- fail_fn,
- userdata,
- &nmbname,
- nb_flags) == NULL)
- {
- DEBUG(0,("register_name: Failed to send packet trying to register name %s\n",
- nmb_namestr(&nmbname)));
- return True;
- }
- return False;
+ struct nmb_name nmbname;
+
+ make_nmb_name(&nmbname, name, type);
+
+ /* Always set the NB_ACTIVE flag on the name we are
+ registering. Doesn't make sense without it.
+ */
+
+ nb_flags |= NB_ACTIVE;
+
+ if (subrec == unicast_subnet) {
+ /* we now always do multi-homed registration if we are
+ registering to a WINS server. This copes much
+ better with complex WINS setups */
+ multihomed_register_name(&nmbname, nb_flags,
+ success_fn, fail_fn);
+ return;
+ }
+
+ if (queue_register_name(subrec,
+ register_name_response,
+ register_name_timeout_response,
+ success_fn,
+ fail_fn,
+ userdata,
+ &nmbname,
+ nb_flags) == NULL) {
+ DEBUG(0,("register_name: Failed to send packet trying to register name %s\n",
+ nmb_namestr(&nmbname)));
+ }
}
+
/****************************************************************************
- Try and refresh one of our names.
+ Try and refresh one of our names. This is *only* called for WINS refresh
****************************************************************************/
-
-BOOL refresh_name(struct subnet_record *subrec, struct name_record *namerec,
- refresh_name_success_function success_fn,
- refresh_name_fail_function fail_fn,
- struct userdata_struct *userdata)
+void wins_refresh_name(struct name_record *namerec)
{
- int i;
-
- /*
- * Go through and refresh the name for all known ip addresses.
- * Only call the success/fail function on the last one (it should
- * only be done once).
- */
-
- for( i = 0; i < namerec->data.num_ips; i++)
- {
- if(queue_refresh_name( subrec,
- register_name_response,
- register_name_timeout_response,
- (i == (namerec->data.num_ips - 1)) ? success_fn : NULL,
- (i == (namerec->data.num_ips - 1)) ? fail_fn : NULL,
- (i == (namerec->data.num_ips - 1)) ? userdata : NULL,
- namerec,
- namerec->data.ip[i]) == NULL)
- {
- DEBUG(0,("refresh_name: Failed to send packet trying to refresh name %s\n",
- nmb_namestr(&namerec->name)));
- return True;
- }
- }
- return False;
+ int t;
+ char **wins_tags;
+
+ /* get the list of wins tags - we try to refresh for each of them */
+ wins_tags = wins_srv_tags();
+
+ for (t=0; wins_tags && wins_tags[t]; t++) {
+ queue_wins_refresh(&namerec->name,
+ register_name_response,
+ register_name_timeout_response,
+ namerec->data.nb_flags,
+ namerec->data.ip[0], wins_tags[t]);
+ }
+
+ wins_srv_tags_free(wins_tags);
}