diff options
Diffstat (limited to 'source3/nmbd/nmbd_nameregister.c')
-rw-r--r-- | source3/nmbd/nmbd_nameregister.c | 539 |
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(®ister_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*)®istered_ip,&nmb->answers->rdata[2]); - ttl = nmb->answers->ttl; - } - } - - DEBUG(5,("register_name_response: %s in registering name %s on subnet %s.\n", - success ? "success" : "failure", 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(®ister_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(®istered_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); } |