Use tevent_req_oom
[ira/wip.git] / source3 / libsmb / namequery.c
index 0da495dbb976f89d8163dd0abb94d033506a924e..dca740d3e6bb8d737c1d95ad2b82f411f33774c4 100644 (file)
 */
 
 #include "includes.h"
+#include "../lib/util/tevent_ntstatus.h"
 #include "libads/sitename_cache.h"
 #include "libads/dns.h"
 #include "../libcli/netlogon/netlogon.h"
-#include "librpc/gen_ndr/messaging.h"
 #include "lib/async_req/async_sock.h"
+#include "libsmb/nmblib.h"
 
 /* nmbd.c sets this to True. */
 bool global_in_nmbd = False;
@@ -196,6 +197,26 @@ char *saf_fetch( const char *domain )
        return server;
 }
 
+static void set_socket_addr_v4(struct sockaddr_storage *addr)
+{
+       if (!interpret_string_addr(addr, lp_socket_address(),
+                                  AI_NUMERICHOST|AI_PASSIVE)) {
+               zero_sockaddr(addr);
+       }
+       if (addr->ss_family != AF_INET) {
+               zero_sockaddr(addr);
+       }
+}
+
+static struct in_addr my_socket_addr_v4(void)
+{
+       struct sockaddr_storage my_addr;
+       struct sockaddr_in *in_addr = (struct sockaddr_in *)((char *)&my_addr);
+
+       set_socket_addr_v4(&my_addr);
+       return in_addr->sin_addr;
+}
+
 /****************************************************************************
  Generate a random trn_id.
 ****************************************************************************/
@@ -225,7 +246,7 @@ static struct node_status *parse_node_status(TALLOC_CTX *mem_ctx, char *p,
        if (*num_names == 0)
                return NULL;
 
-       ret = TALLOC_ARRAY(mem_ctx, struct node_status,*num_names);
+       ret = talloc_array(mem_ctx, struct node_status,*num_names);
        if (!ret)
                return NULL;
 
@@ -704,10 +725,7 @@ struct tevent_req *node_status_query_send(TALLOC_CTX *mem_ctx,
        in_addr = (struct sockaddr_in *)(void *)&state->addr;
        in_addr->sin_port = htons(NMB_PORT);
 
-       if (!interpret_string_addr(&state->my_addr, lp_socket_address(),
-                                  AI_NUMERICHOST|AI_PASSIVE)) {
-               zero_sockaddr(&state->my_addr);
-       }
+       set_socket_addr_v4(&state->my_addr);
 
        ZERO_STRUCT(p);
        nmb->header.name_trn_id = generate_trn_id();
@@ -891,10 +909,7 @@ bool name_status_find(const char *q_name,
                return false;
        }
 
-       if (!interpret_string_addr(&ss, lp_socket_address(),
-                               AI_NUMERICHOST|AI_PASSIVE)) {
-               zero_sockaddr(&ss);
-       }
+       set_socket_addr_v4(&ss);
 
        /* W2K PDC's seem not to respond to '*'#0. JRA */
        make_nmb_name(&nname, q_name, q_type);
@@ -962,9 +977,9 @@ static int addr_compare(const struct sockaddr_storage *ss1,
 
        for (i=0;i<num_interfaces;i++) {
                const struct sockaddr_storage *pss = iface_n_bcast(i);
-               unsigned char *p_ss1 = NULL;
-               unsigned char *p_ss2 = NULL;
-               unsigned char *p_if = NULL;
+               const unsigned char *p_ss1 = NULL;
+               const unsigned char *p_ss2 = NULL;
+               const unsigned char *p_if = NULL;
                size_t len = 0;
                int bits1, bits2;
 
@@ -973,21 +988,21 @@ static int addr_compare(const struct sockaddr_storage *ss1,
                        continue;
                }
                if (pss->ss_family == AF_INET) {
-                       p_if = (unsigned char *)
+                       p_if = (const unsigned char *)
                                &((const struct sockaddr_in *)pss)->sin_addr;
-                       p_ss1 = (unsigned char *)
+                       p_ss1 = (const unsigned char *)
                                &((const struct sockaddr_in *)ss1)->sin_addr;
-                       p_ss2 = (unsigned char *)
+                       p_ss2 = (const unsigned char *)
                                &((const struct sockaddr_in *)ss2)->sin_addr;
                        len = 4;
                }
 #if defined(HAVE_IPV6)
                if (pss->ss_family == AF_INET6) {
-                       p_if = (unsigned char *)
+                       p_if = (const unsigned char *)
                                &((const struct sockaddr_in6 *)pss)->sin6_addr;
-                       p_ss1 = (unsigned char *)
+                       p_ss1 = (const unsigned char *)
                                &((const struct sockaddr_in6 *)ss1)->sin6_addr;
-                       p_ss2 = (unsigned char *)
+                       p_ss2 = (const unsigned char *)
                                &((const struct sockaddr_in6 *)ss2)->sin6_addr;
                        len = 16;
                }
@@ -1002,14 +1017,14 @@ static int addr_compare(const struct sockaddr_storage *ss1,
        }
 
        /* Bias towards directly reachable IPs */
-       if (iface_local((struct sockaddr *)ss1)) {
+       if (iface_local((const struct sockaddr *)ss1)) {
                if (ss1->ss_family == AF_INET) {
                        max_bits1 += 32;
                } else {
                        max_bits1 += 128;
                }
        }
-       if (iface_local((struct sockaddr *)ss2)) {
+       if (iface_local((const struct sockaddr *)ss2)) {
                if (ss2->ss_family == AF_INET) {
                        max_bits2 += 32;
                } else {
@@ -1079,12 +1094,13 @@ static int remove_duplicate_addrs2(struct ip_service *iplist, int count )
 
        /* one loop to remove duplicates */
        for ( i=0; i<count; i++ ) {
-               if ( is_zero_addr((struct sockaddr *)&iplist[i].ss)) {
+               if ( is_zero_addr(&iplist[i].ss)) {
                        continue;
                }
 
                for ( j=i+1; j<count; j++ ) {
-                       if (sockaddr_equal((struct sockaddr *)&iplist[i].ss, (struct sockaddr *)&iplist[j].ss) &&
+                       if (sockaddr_equal((struct sockaddr *)(void *)&iplist[i].ss,
+                                          (struct sockaddr *)(void *)&iplist[j].ss) &&
                                        iplist[i].port == iplist[j].port) {
                                zero_sockaddr(&iplist[j].ss);
                        }
@@ -1094,7 +1110,7 @@ static int remove_duplicate_addrs2(struct ip_service *iplist, int count )
        /* one loop to clean up any holes we left */
        /* first ip should never be a zero_ip() */
        for (i = 0; i<count; ) {
-               if (is_zero_addr((struct sockaddr *)&iplist[i].ss) ) {
+               if (is_zero_addr(&iplist[i].ss) ) {
                        if (i != count-1) {
                                memmove(&iplist[i], &iplist[i+1],
                                        (count - i - 1)*sizeof(iplist[i]));
@@ -1111,7 +1127,7 @@ static int remove_duplicate_addrs2(struct ip_service *iplist, int count )
 static bool prioritize_ipv4_list(struct ip_service *iplist, int count)
 {
        TALLOC_CTX *frame = talloc_stackframe();
-       struct ip_service *iplist_new = TALLOC_ARRAY(frame, struct ip_service, count);
+       struct ip_service *iplist_new = talloc_array(frame, struct ip_service, count);
        int i, j;
 
        if (iplist_new == NULL) {
@@ -1177,7 +1193,6 @@ struct tevent_req *name_query_send(TALLOC_CTX *mem_ctx,
        struct packet_struct p;
        struct nmb_packet *nmb = &p.packet.nmb;
        struct sockaddr_in *in_addr;
-       struct timeval timeout;
 
        req = tevent_req_create(mem_ctx, &state, struct name_query_state);
        if (req == NULL) {
@@ -1202,10 +1217,7 @@ struct tevent_req *name_query_send(TALLOC_CTX *mem_ctx,
        in_addr = (struct sockaddr_in *)(void *)&state->addr;
        in_addr->sin_port = htons(NMB_PORT);
 
-       if (!interpret_string_addr(&state->my_addr, lp_socket_address(),
-                                  AI_NUMERICHOST|AI_PASSIVE)) {
-               zero_sockaddr(&state->my_addr);
-       }
+       set_socket_addr_v4(&state->my_addr);
 
        ZERO_STRUCT(p);
        nmb->header.name_trn_id = generate_trn_id();
@@ -1243,14 +1255,6 @@ struct tevent_req *name_query_send(TALLOC_CTX *mem_ctx,
                DEBUG(10, ("nb_trans_send failed\n"));
                return tevent_req_post(req, ev);
        }
-       if (bcast) {
-               timeout = timeval_current_ofs(0, 250000);
-       } else {
-               timeout = timeval_current_ofs(2, 0);
-       }
-       if (!tevent_req_set_endtime(req, ev, timeout)) {
-               return tevent_req_post(req, ev);
-       }
        tevent_req_set_callback(subreq, name_query_done, req);
        return req;
 }
@@ -1261,6 +1265,7 @@ static bool name_query_validator(struct packet_struct *p, void *private_data)
                private_data, struct name_query_state);
        struct nmb_packet *nmb = &p->packet.nmb;
        struct sockaddr_storage *tmp_addrs;
+       bool got_unique_netbios_name = false;
        int i;
 
        debug_nmb_packet(p);
@@ -1322,7 +1327,7 @@ static bool name_query_validator(struct packet_struct *p, void *private_data)
                return false;
        }
 
-       tmp_addrs = TALLOC_REALLOC_ARRAY(
+       tmp_addrs = talloc_realloc(
                state, state->addrs, struct sockaddr_storage,
                state->num_addrs + nmb->answers->rdlength/6);
        if (tmp_addrs == NULL) {
@@ -1335,11 +1340,32 @@ static bool name_query_validator(struct packet_struct *p, void *private_data)
                 "from %s ( ", inet_ntoa(p->ip)));
 
        for (i=0; i<nmb->answers->rdlength/6; i++) {
+               uint16_t flags;
                struct in_addr ip;
+               struct sockaddr_storage addr;
+               int j;
+
+               flags = RSVAL(&nmb->answers->rdata[i*6], 0);
+               got_unique_netbios_name |= ((flags & 0x8000) == 0);
+
                putip((char *)&ip,&nmb->answers->rdata[2+i*6]);
-               in_addr_to_sockaddr_storage(
-                       &state->addrs[state->num_addrs], ip);
+               in_addr_to_sockaddr_storage(&addr, ip);
+
+               for (j=0; j<state->num_addrs; j++) {
+                       if (sockaddr_equal(
+                                   (struct sockaddr *)(void *)&addr,
+                                   (struct sockaddr *)(void *)&state->addrs[j])) {
+                               break;
+                       }
+               }
+               if (j < state->num_addrs) {
+                       /* Already got it */
+                       continue;
+               }
+
                DEBUGADD(2,("%s ",inet_ntoa(ip)));
+
+               state->addrs[state->num_addrs] = addr;
                state->num_addrs += 1;
        }
        DEBUGADD(2,(")\n"));
@@ -1360,10 +1386,10 @@ static bool name_query_validator(struct packet_struct *p, void *private_data)
 
        if (state->bcast) {
                /*
-                * We have to collect all entries coming in from
-                * broadcast queries
+                * We have to collect all entries coming in from broadcast
+                * queries. If we got a unique name, we're done.
                 */
-               return false;
+               return got_unique_netbios_name;
        }
        /*
         * WINS responses are accepted when they are received
@@ -1407,9 +1433,18 @@ NTSTATUS name_query_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
                req, struct name_query_state);
        NTSTATUS status;
 
-       if (tevent_req_is_nterror(req, &status)
-           && !NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
-               return status;
+       if (tevent_req_is_nterror(req, &status)) {
+               if (state->bcast &&
+                   NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
+                       /*
+                        * In the broadcast case we collect replies until the
+                        * timeout.
+                        */
+                       status = NT_STATUS_OK;
+               }
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
        }
        if (state->num_addrs == 0) {
                return NT_STATUS_NOT_FOUND;
@@ -1433,6 +1468,7 @@ NTSTATUS name_query(const char *name, int name_type,
        TALLOC_CTX *frame = talloc_stackframe();
        struct tevent_context *ev;
        struct tevent_req *req;
+       struct timeval timeout;
        NTSTATUS status = NT_STATUS_NO_MEMORY;
 
        ev = tevent_context_init(frame);
@@ -1443,6 +1479,14 @@ NTSTATUS name_query(const char *name, int name_type,
        if (req == NULL) {
                goto fail;
        }
+       if (bcast) {
+               timeout = timeval_current_ofs(0, 250000);
+       } else {
+               timeout = timeval_current_ofs(2, 0);
+       }
+       if (!tevent_req_set_endtime(req, ev, timeout)) {
+               goto fail;
+       }
        if (!tevent_req_poll_ntstatus(req, ev, &status)) {
                goto fail;
        }
@@ -1482,113 +1526,507 @@ static bool convert_ss2service(struct ip_service **return_iplist,
        return true;
 }
 
+struct name_queries_state {
+       struct tevent_context *ev;
+       const char *name;
+       int name_type;
+       bool bcast;
+       bool recurse;
+       const struct sockaddr_storage *addrs;
+       int num_addrs;
+       int wait_msec;
+       int timeout_msec;
+
+       struct tevent_req **subreqs;
+       int num_received;
+       int num_sent;
+
+       int received_index;
+       struct sockaddr_storage *result_addrs;
+       int num_result_addrs;
+       uint8_t flags;
+};
+
+static void name_queries_done(struct tevent_req *subreq);
+static void name_queries_next(struct tevent_req *subreq);
+
+/*
+ * Send a name query to multiple destinations with a wait time in between
+ */
+
+static struct tevent_req *name_queries_send(
+       TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+       const char *name, int name_type,
+       bool bcast, bool recurse,
+       const struct sockaddr_storage *addrs,
+       int num_addrs, int wait_msec, int timeout_msec)
+{
+       struct tevent_req *req, *subreq;
+       struct name_queries_state *state;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct name_queries_state);
+       if (req == NULL) {
+               return NULL;
+       }
+       state->ev = ev;
+       state->name = name;
+       state->name_type = name_type;
+       state->bcast = bcast;
+       state->recurse = recurse;
+       state->addrs = addrs;
+       state->num_addrs = num_addrs;
+       state->wait_msec = wait_msec;
+       state->timeout_msec = timeout_msec;
+
+       state->subreqs = talloc_zero_array(
+               state, struct tevent_req *, num_addrs);
+       if (tevent_req_nomem(state->subreqs, req)) {
+               return tevent_req_post(req, ev);
+       }
+       state->num_sent = 0;
+
+       subreq = name_query_send(
+               state->subreqs, state->ev, name, name_type, bcast, recurse,
+               &state->addrs[state->num_sent]);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       if (!tevent_req_set_endtime(
+                   subreq, state->ev,
+                   timeval_current_ofs(0, state->timeout_msec * 1000))) {
+               tevent_req_oom(req);
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, name_queries_done, req);
+
+       state->subreqs[state->num_sent] = subreq;
+       state->num_sent += 1;
+
+       if (state->num_sent < state->num_addrs) {
+               subreq = tevent_wakeup_send(
+                       state, state->ev,
+                       timeval_current_ofs(0, state->wait_msec * 1000));
+               if (tevent_req_nomem(subreq, req)) {
+                       return tevent_req_post(req, ev);
+               }
+               tevent_req_set_callback(subreq, name_queries_next, req);
+       }
+       return req;
+}
+
+static void name_queries_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct name_queries_state *state = tevent_req_data(
+               req, struct name_queries_state);
+       int i;
+       NTSTATUS status;
+
+       status = name_query_recv(subreq, state, &state->result_addrs,
+                                &state->num_result_addrs, &state->flags);
+
+       for (i=0; i<state->num_sent; i++) {
+               if (state->subreqs[i] == subreq) {
+                       break;
+               }
+       }
+       if (i == state->num_sent) {
+               tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+               return;
+       }
+       TALLOC_FREE(state->subreqs[i]);
+
+       state->num_received += 1;
+
+       if (!NT_STATUS_IS_OK(status)) {
+
+               if (state->num_received >= state->num_addrs) {
+                       tevent_req_nterror(req, status);
+                       return;
+               }
+               /*
+                * Still outstanding requests, just wait
+                */
+               return;
+       }
+       state->received_index = i;
+       tevent_req_done(req);
+}
+
+static void name_queries_next(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct name_queries_state *state = tevent_req_data(
+               req, struct name_queries_state);
+
+       if (!tevent_wakeup_recv(subreq)) {
+               tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+               return;
+       }
+
+       subreq = name_query_send(
+               state->subreqs, state->ev,
+               state->name, state->name_type, state->bcast, state->recurse,
+               &state->addrs[state->num_sent]);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
+       }
+       tevent_req_set_callback(subreq, name_queries_done, req);
+       if (!tevent_req_set_endtime(
+                   subreq, state->ev,
+                   timeval_current_ofs(0, state->timeout_msec * 1000))) {
+               tevent_req_oom(req);
+               return;
+       }
+       state->subreqs[state->num_sent] = subreq;
+       state->num_sent += 1;
+
+       if (state->num_sent < state->num_addrs) {
+               subreq = tevent_wakeup_send(
+                       state, state->ev,
+                       timeval_current_ofs(0, state->wait_msec * 1000));
+               if (tevent_req_nomem(subreq, req)) {
+                       return;
+               }
+               tevent_req_set_callback(subreq, name_queries_next, req);
+       }
+}
+
+static NTSTATUS name_queries_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+                                 struct sockaddr_storage **result_addrs,
+                                 int *num_result_addrs, uint8_t *flags,
+                                 int *received_index)
+{
+       struct name_queries_state *state = tevent_req_data(
+               req, struct name_queries_state);
+       NTSTATUS status;
+
+       if (tevent_req_is_nterror(req, &status)) {
+               return status;
+       }
+
+       if (result_addrs != NULL) {
+               *result_addrs = talloc_move(mem_ctx, &state->result_addrs);
+       }
+       if (num_result_addrs != NULL) {
+               *num_result_addrs = state->num_result_addrs;
+       }
+       if (flags != NULL) {
+               *flags = state->flags;
+       }
+       if (received_index != NULL) {
+               *received_index = state->received_index;
+       }
+       return NT_STATUS_OK;
+}
+
 /********************************************************
  Resolve via "bcast" method.
 *********************************************************/
 
-NTSTATUS name_resolve_bcast(const char *name,
-                       int name_type,
-                       struct ip_service **return_iplist,
-                       int *return_count)
+struct name_resolve_bcast_state {
+       struct sockaddr_storage *addrs;
+       int num_addrs;
+};
+
+static void name_resolve_bcast_done(struct tevent_req *subreq);
+
+struct tevent_req *name_resolve_bcast_send(TALLOC_CTX *mem_ctx,
+                                          struct tevent_context *ev,
+                                          const char *name,
+                                          int name_type)
 {
-       int i;
-       int num_interfaces = iface_count();
-       struct sockaddr_storage *ss_list;
-       struct sockaddr_storage ss;
-       NTSTATUS status = NT_STATUS_NOT_FOUND;
+       struct tevent_req *req, *subreq;
+       struct name_resolve_bcast_state *state;
+       struct sockaddr_storage *bcast_addrs;
+       int i, num_addrs, num_bcast_addrs;
 
-       if (lp_disable_netbios()) {
-               DEBUG(5,("name_resolve_bcast(%s#%02x): netbios is disabled\n",
-                                       name, name_type));
-               return NT_STATUS_INVALID_PARAMETER;
+       req = tevent_req_create(mem_ctx, &state,
+                               struct name_resolve_bcast_state);
+       if (req == NULL) {
+               return NULL;
        }
 
-       *return_iplist = NULL;
-       *return_count = 0;
+       if (lp_disable_netbios()) {
+               DEBUG(5, ("name_resolve_bcast(%s#%02x): netbios is disabled\n",
+                         name, name_type));
+               tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+               return tevent_req_post(req, ev);
+       }
 
        /*
         * "bcast" means do a broadcast lookup on all the local interfaces.
         */
 
-       DEBUG(3,("name_resolve_bcast: Attempting broadcast lookup "
-               "for name %s<0x%x>\n", name, name_type));
+       DEBUG(3, ("name_resolve_bcast: Attempting broadcast lookup "
+                 "for name %s<0x%x>\n", name, name_type));
 
-       if (!interpret_string_addr(&ss, lp_socket_address(),
-                               AI_NUMERICHOST|AI_PASSIVE)) {
-               zero_sockaddr(&ss);
+       num_addrs = iface_count();
+       bcast_addrs = talloc_array(state, struct sockaddr_storage, num_addrs);
+       if (tevent_req_nomem(bcast_addrs, req)) {
+               return tevent_req_post(req, ev);
        }
 
        /*
         * Lookup the name on all the interfaces, return on
         * the first successful match.
         */
-       for( i = num_interfaces-1; i >= 0; i--) {
+       num_bcast_addrs = 0;
+
+       for (i=0; i<num_addrs; i++) {
                const struct sockaddr_storage *pss = iface_n_bcast(i);
 
-               /* Done this way to fix compiler error on IRIX 5.x */
-               if (!pss) {
+               if (pss->ss_family != AF_INET) {
                        continue;
                }
-               status = name_query(name, name_type, true, true, pss,
-                                   talloc_tos(), &ss_list, return_count,
-                                   NULL);
-               if (NT_STATUS_IS_OK(status)) {
-                       goto success;
-               }
+               bcast_addrs[num_bcast_addrs] = *pss;
+               num_bcast_addrs += 1;
        }
 
-       /* failed - no response */
+       subreq = name_queries_send(state, ev, name, name_type, true, true,
+                                  bcast_addrs, num_bcast_addrs, 0, 1000);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, name_resolve_bcast_done, req);
+       return req;
+}
 
-       return status;
+static void name_resolve_bcast_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct name_resolve_bcast_state *state = tevent_req_data(
+               req, struct name_resolve_bcast_state);
+       NTSTATUS status;
 
-success:
+       status = name_queries_recv(subreq, state,
+                                  &state->addrs, &state->num_addrs,
+                                  NULL, NULL);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+       tevent_req_done(req);
+}
 
-       if (!convert_ss2service(return_iplist, ss_list, *return_count) )
-               status = NT_STATUS_NO_MEMORY;
+NTSTATUS name_resolve_bcast_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+                                struct sockaddr_storage **addrs,
+                                int *num_addrs)
+{
+       struct name_resolve_bcast_state *state = tevent_req_data(
+               req, struct name_resolve_bcast_state);
+       NTSTATUS status;
+
+       if (tevent_req_is_nterror(req, &status)) {
+               return status;
+       }
+       *addrs = talloc_move(mem_ctx, &state->addrs);
+       *num_addrs = state->num_addrs;
+       return NT_STATUS_OK;
+}
 
-       TALLOC_FREE(ss_list);
+NTSTATUS name_resolve_bcast(const char *name,
+                       int name_type,
+                       TALLOC_CTX *mem_ctx,
+                       struct sockaddr_storage **return_iplist,
+                       int *return_count)
+{
+       TALLOC_CTX *frame = talloc_stackframe();
+       struct event_context *ev;
+       struct tevent_req *req;
+       NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+       ev = event_context_init(frame);
+       if (ev == NULL) {
+               goto fail;
+       }
+       req = name_resolve_bcast_send(frame, ev, name, name_type);
+       if (req == NULL) {
+               goto fail;
+       }
+       if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+               goto fail;
+       }
+       status = name_resolve_bcast_recv(req, mem_ctx, return_iplist,
+                                        return_count);
+ fail:
+       TALLOC_FREE(frame);
        return status;
 }
 
-/********************************************************
- Resolve via "wins" method.
-*********************************************************/
+struct query_wins_list_state {
+       struct tevent_context *ev;
+       const char *name;
+       uint8_t name_type;
+       struct in_addr *servers;
+       uint32_t num_servers;
+       struct sockaddr_storage server;
+       uint32_t num_sent;
 
-NTSTATUS resolve_wins(const char *name,
-               int name_type,
-               struct ip_service **return_iplist,
-               int *return_count)
+       struct sockaddr_storage *addrs;
+       int num_addrs;
+       uint8_t flags;
+};
+
+static void query_wins_list_done(struct tevent_req *subreq);
+
+/*
+ * Query a list of (replicating) wins servers in sequence, call them
+ * dead if they don't reply
+ */
+
+static struct tevent_req *query_wins_list_send(
+       TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+       struct in_addr src_ip, const char *name, uint8_t name_type,
+       struct in_addr *servers, int num_servers)
 {
-       int t, i;
-       char **wins_tags;
-       struct sockaddr_storage src_ss, *ss_list = NULL;
-       struct in_addr src_ip;
+       struct tevent_req *req, *subreq;
+       struct query_wins_list_state *state;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct query_wins_list_state);
+       if (req == NULL) {
+               return NULL;
+       }
+       state->ev = ev;
+       state->name = name;
+       state->name_type = name_type;
+       state->servers = servers;
+       state->num_servers = num_servers;
+
+       if (state->num_servers == 0) {
+               tevent_req_nterror(req, NT_STATUS_NOT_FOUND);
+               return tevent_req_post(req, ev);
+       }
+
+       in_addr_to_sockaddr_storage(
+               &state->server, state->servers[state->num_sent]);
+
+       subreq = name_query_send(state, state->ev,
+                                state->name, state->name_type,
+                                false, true, &state->server);
+       state->num_sent += 1;
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       if (!tevent_req_set_endtime(subreq, state->ev,
+                                   timeval_current_ofs(2, 0))) {
+               tevent_req_oom(req);
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, query_wins_list_done, req);
+       return req;
+}
+
+static void query_wins_list_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct query_wins_list_state *state = tevent_req_data(
+               req, struct query_wins_list_state);
        NTSTATUS status;
 
-       if (lp_disable_netbios()) {
-               DEBUG(5,("resolve_wins(%s#%02x): netbios is disabled\n",
-                                       name, name_type));
-               return NT_STATUS_INVALID_PARAMETER;
+       status = name_query_recv(subreq, state,
+                                &state->addrs, &state->num_addrs,
+                                &state->flags);
+       TALLOC_FREE(subreq);
+       if (NT_STATUS_IS_OK(status)) {
+               tevent_req_done(req);
+               return;
+       }
+       if (!NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
+               tevent_req_nterror(req, status);
+               return;
        }
+       wins_srv_died(state->servers[state->num_sent-1],
+                     my_socket_addr_v4());
 
-       *return_iplist = NULL;
-       *return_count = 0;
+       if (state->num_sent == state->num_servers) {
+               tevent_req_nterror(req, NT_STATUS_NOT_FOUND);
+               return;
+       }
 
-       DEBUG(3,("resolve_wins: Attempting wins lookup for name %s<0x%x>\n",
-                               name, name_type));
+       in_addr_to_sockaddr_storage(
+               &state->server, state->servers[state->num_sent]);
 
-       if (wins_srv_count() < 1) {
-               DEBUG(3,("resolve_wins: WINS server resolution selected "
-                       "and no WINS servers listed.\n"));
-               return NT_STATUS_INVALID_PARAMETER;
+       subreq = name_query_send(state, state->ev,
+                                state->name, state->name_type,
+                                false, true, &state->server);
+       state->num_sent += 1;
+       if (tevent_req_nomem(subreq, req)) {
+               return;
        }
+       if (!tevent_req_set_endtime(subreq, state->ev,
+                                   timeval_current_ofs(2, 0))) {
+               tevent_req_oom(req);
+               return;
+       }
+       tevent_req_set_callback(subreq, query_wins_list_done, req);
+}
 
-       /* we try a lookup on each of the WINS tags in turn */
-       wins_tags = wins_srv_tags();
+static NTSTATUS query_wins_list_recv(struct tevent_req *req,
+                                    TALLOC_CTX *mem_ctx,
+                                    struct sockaddr_storage **addrs,
+                                    int *num_addrs,
+                                    uint8_t *flags)
+{
+       struct query_wins_list_state *state = tevent_req_data(
+               req, struct query_wins_list_state);
+       NTSTATUS status;
 
-       if (!wins_tags) {
-               /* huh? no tags?? give up in disgust */
-               return NT_STATUS_INVALID_PARAMETER;
+       if (tevent_req_is_nterror(req, &status)) {
+               return status;
+       }
+       if (addrs != NULL) {
+               *addrs = talloc_move(mem_ctx, &state->addrs);
+       }
+       if (num_addrs != NULL) {
+               *num_addrs = state->num_addrs;
+       }
+       if (flags != NULL) {
+               *flags = state->flags;
+       }
+       return NT_STATUS_OK;
+}
+
+struct resolve_wins_state {
+       int num_sent;
+       int num_received;
+
+       struct sockaddr_storage *addrs;
+       int num_addrs;
+       uint8_t flags;
+};
+
+static void resolve_wins_done(struct tevent_req *subreq);
+
+struct tevent_req *resolve_wins_send(TALLOC_CTX *mem_ctx,
+                                    struct tevent_context *ev,
+                                    const char *name,
+                                    int name_type)
+{
+       struct tevent_req *req, *subreq;
+       struct resolve_wins_state *state;
+       char **wins_tags = NULL;
+       struct sockaddr_storage src_ss;
+       struct in_addr src_ip;
+       int i, num_wins_tags;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct resolve_wins_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       if (wins_srv_count() < 1) {
+               DEBUG(3,("resolve_wins: WINS server resolution selected "
+                       "and no WINS servers listed.\n"));
+               tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+               goto fail;
        }
 
        /* the address we will be sending from */
@@ -1603,80 +2041,164 @@ NTSTATUS resolve_wins(const char *name,
                DEBUG(3,("resolve_wins: cannot receive WINS replies "
                        "on IPv6 address %s\n",
                        addr));
-               wins_srv_tags_free(wins_tags);
-               return NT_STATUS_INVALID_PARAMETER;
+               tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+               goto fail;
        }
 
-       src_ip = ((struct sockaddr_in *)&src_ss)->sin_addr;
+       src_ip = ((const struct sockaddr_in *)(void *)&src_ss)->sin_addr;
 
-       /* in the worst case we will try every wins server with every
-          tag! */
-       for (t=0; wins_tags && wins_tags[t]; t++) {
-               int srv_count = wins_srv_count_tag(wins_tags[t]);
-               for (i=0; i<srv_count; i++) {
-                       struct sockaddr_storage wins_ss;
-                       struct in_addr wins_ip;
+       wins_tags = wins_srv_tags();
+       if (wins_tags == NULL) {
+               tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+               goto fail;
+       }
+
+       num_wins_tags = 0;
+       while (wins_tags[num_wins_tags] != NULL) {
+               num_wins_tags += 1;
+       }
+
+       for (i=0; i<num_wins_tags; i++) {
+               int num_servers, num_alive;
+               struct in_addr *servers, *alive;
+               int j;
+
+               if (!wins_server_tag_ips(wins_tags[i], talloc_tos(),
+                                        &servers, &num_servers)) {
+                       DEBUG(10, ("wins_server_tag_ips failed for tag %s\n",
+                                  wins_tags[i]));
+                       continue;
+               }
+
+               alive = talloc_array(state, struct in_addr, num_servers);
+               if (tevent_req_nomem(alive, req)) {
+                       goto fail;
+               }
 
-                       wins_ip = wins_srv_ip_tag(wins_tags[t], src_ip);
+               num_alive = 0;
+               for (j=0; j<num_servers; j++) {
+                       struct in_addr wins_ip = servers[j];
 
                        if (global_in_nmbd && ismyip_v4(wins_ip)) {
                                /* yikes! we'll loop forever */
                                continue;
                        }
-
                        /* skip any that have been unresponsive lately */
                        if (wins_srv_is_dead(wins_ip, src_ip)) {
                                continue;
                        }
+                       DEBUG(3, ("resolve_wins: using WINS server %s "
+                                "and tag '%s'\n",
+                                 inet_ntoa(wins_ip), wins_tags[i]));
+                       alive[num_alive] = wins_ip;
+                       num_alive += 1;
+               }
+               TALLOC_FREE(servers);
 
-                       DEBUG(3,("resolve_wins: using WINS server %s "
-                               "and tag '%s'\n",
-                               inet_ntoa(wins_ip), wins_tags[t]));
+               if (num_alive == 0) {
+                       continue;
+               }
 
-                       in_addr_to_sockaddr_storage(&wins_ss, wins_ip);
-                       status = name_query(name,
-                                               name_type,
-                                               false,
-                                               true,
-                                               &wins_ss,
-                                               talloc_tos(),
-                                               &ss_list,
-                                               return_count,
-                                               NULL);
+               subreq = query_wins_list_send(
+                       state, ev, src_ip, name, name_type,
+                       alive, num_alive);
+               if (tevent_req_nomem(subreq, req)) {
+                       goto fail;
+               }
+               tevent_req_set_callback(subreq, resolve_wins_done, req);
+               state->num_sent += 1;
+       }
 
-                       /* exit loop if we got a list of addresses */
+       if (state->num_sent == 0) {
+               tevent_req_nterror(req, NT_STATUS_NOT_FOUND);
+               goto fail;
+       }
 
-                       if (NT_STATUS_IS_OK(status)) {
-                               goto success;
-                       }
+       wins_srv_tags_free(wins_tags);
+       return req;
+fail:
+       wins_srv_tags_free(wins_tags);
+       return tevent_req_post(req, ev);
+}
 
-                       if (NT_STATUS_EQUAL(status,
-                                           NT_STATUS_IO_TIMEOUT)) {
-                               /* Timed out waiting for WINS server to
-                                * respond.
-                                * Mark it dead. */
-                               wins_srv_died(wins_ip, src_ip);
-                       } else {
-                               /* The name definitely isn't in this
-                                  group of WINS servers.
-                                  goto the next group  */
-                               break;
-                       }
-               }
+static void resolve_wins_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct resolve_wins_state *state = tevent_req_data(
+               req, struct resolve_wins_state);
+       NTSTATUS status;
+
+       status = query_wins_list_recv(subreq, state, &state->addrs,
+                                     &state->num_addrs, &state->flags);
+       if (NT_STATUS_IS_OK(status)) {
+               tevent_req_done(req);
+               return;
        }
 
-       wins_srv_tags_free(wins_tags);
-       return NT_STATUS_NO_LOGON_SERVERS;
+       state->num_received += 1;
 
-success:
+       if (state->num_received < state->num_sent) {
+               /*
+                * Wait for the others
+                */
+               return;
+       }
+       tevent_req_nterror(req, status);
+}
 
-       status = NT_STATUS_OK;
-       if (!convert_ss2service(return_iplist, ss_list, *return_count))
-               status = NT_STATUS_INVALID_PARAMETER;
+NTSTATUS resolve_wins_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+                          struct sockaddr_storage **addrs,
+                          int *num_addrs, uint8_t *flags)
+{
+       struct resolve_wins_state *state = tevent_req_data(
+               req, struct resolve_wins_state);
+       NTSTATUS status;
 
-       TALLOC_FREE(ss_list);
-       wins_srv_tags_free(wins_tags);
+       if (tevent_req_is_nterror(req, &status)) {
+               return status;
+       }
+       if (addrs != NULL) {
+               *addrs = talloc_move(mem_ctx, &state->addrs);
+       }
+       if (num_addrs != NULL) {
+               *num_addrs = state->num_addrs;
+       }
+       if (flags != NULL) {
+               *flags = state->flags;
+       }
+       return NT_STATUS_OK;
+}
 
+/********************************************************
+ Resolve via "wins" method.
+*********************************************************/
+
+NTSTATUS resolve_wins(const char *name,
+               int name_type,
+               TALLOC_CTX *mem_ctx,
+               struct sockaddr_storage **return_iplist,
+               int *return_count)
+{
+       struct tevent_context *ev;
+       struct tevent_req *req;
+       NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+       ev = tevent_context_init(talloc_tos());
+       if (ev == NULL) {
+               goto fail;
+       }
+       req = resolve_wins_send(ev, ev, name, name_type);
+       if (req == NULL) {
+               goto fail;
+       }
+       if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+               goto fail;
+       }
+       status = resolve_wins_recv(req, mem_ctx, return_iplist, return_count,
+                                  NULL);
+fail:
+       TALLOC_FREE(ev);
        return status;
 }
 
@@ -1744,6 +2266,7 @@ static NTSTATUS resolve_hosts(const char *name, int name_type,
        struct addrinfo *res = NULL;
        int ret = -1;
        int i = 0;
+       const char *dns_hosts_file;
 
        if ( name_type != 0x20 && name_type != 0x0) {
                DEBUG(5, ("resolve_hosts: not appropriate "
@@ -1768,6 +2291,32 @@ static NTSTATUS resolve_hosts(const char *name, int name_type,
        hints.ai_family = AF_INET;
 #endif
 
+       dns_hosts_file = lp_parm_const_string(-1, "resolv", "host file", NULL);
+       if (dns_hosts_file) {
+               struct sockaddr_storage *ss_list;
+               NTSTATUS status;
+               TALLOC_CTX *ctx = talloc_stackframe();
+               if (!ctx) {
+                       return NT_STATUS_NO_MEMORY;
+               }
+
+               status = resolve_dns_hosts_file_as_sockaddr(dns_hosts_file, name, false,
+                                                           ctx, &ss_list, return_count);
+               if (NT_STATUS_IS_OK(status)) {
+                       if (convert_ss2service(return_iplist,
+                                              ss_list,
+                                              *return_count)) {
+                               talloc_free(ctx);
+                               return NT_STATUS_OK;
+                       } else {
+                               talloc_free(ctx);
+                               return NT_STATUS_NO_MEMORY;
+                       }
+               }
+               talloc_free(ctx);
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
        ret = getaddrinfo(name,
                        NULL,
                        &hints,
@@ -1921,7 +2470,7 @@ static NTSTATUS resolve_ads(const char *name,
                 * for falling back to netbios lookups is that our DNS server
                 * doesn't know anything about the DC's   -- jerry */
 
-               if (!is_zero_addr((struct sockaddr *)&r->ss)) {
+               if (!is_zero_addr(&r->ss)) {
                        (*return_count)++;
                }
        }
@@ -2044,19 +2593,32 @@ NTSTATUS internal_resolve_name(const char *name,
                        }
                } else if(strequal( tok, "wins")) {
                        /* don't resolve 1D via WINS */
+                       struct sockaddr_storage *ss_list;
                        if (name_type != 0x1D) {
                                status = resolve_wins(name, name_type,
-                                                     return_iplist,
+                                                     talloc_tos(),
+                                                     &ss_list,
                                                      return_count);
                                if (NT_STATUS_IS_OK(status)) {
+                                       if (!convert_ss2service(return_iplist,
+                                                               ss_list,
+                                                               *return_count)) {
+                                               status = NT_STATUS_NO_MEMORY;
+                                       }
                                        goto done;
                                }
                        }
                } else if(strequal( tok, "bcast")) {
-                       status = name_resolve_bcast(name, name_type,
-                                                   return_iplist,
-                                                   return_count);
+                       struct sockaddr_storage *ss_list;
+                       status = name_resolve_bcast(
+                               name, name_type, talloc_tos(),
+                               &ss_list, return_count);
                        if (NT_STATUS_IS_OK(status)) {
+                               if (!convert_ss2service(return_iplist,
+                                                       ss_list,
+                                                       *return_count)) {
+                                       status = NT_STATUS_NO_MEMORY;
+                               }
                                goto done;
                        }
                } else {
@@ -2151,8 +2713,8 @@ bool resolve_name(const char *name,
 
                if (prefer_ipv4) {
                        for (i=0; i<count; i++) {
-                               if (!is_zero_addr((struct sockaddr *)&ss_list[i].ss) &&
-                                               !is_broadcast_addr((struct sockaddr *)&ss_list[i].ss) &&
+                               if (!is_zero_addr(&ss_list[i].ss) &&
+                                   !is_broadcast_addr((struct sockaddr *)(void *)&ss_list[i].ss) &&
                                                (ss_list[i].ss.ss_family == AF_INET)) {
                                        *return_ss = ss_list[i].ss;
                                        SAFE_FREE(ss_list);
@@ -2164,8 +2726,8 @@ bool resolve_name(const char *name,
 
                /* only return valid addresses for TCP connections */
                for (i=0; i<count; i++) {
-                       if (!is_zero_addr((struct sockaddr *)&ss_list[i].ss) &&
-                                       !is_broadcast_addr((struct sockaddr *)&ss_list[i].ss)) {
+                       if (!is_zero_addr(&ss_list[i].ss) &&
+                           !is_broadcast_addr((struct sockaddr *)(void *)&ss_list[i].ss)) {
                                *return_ss = ss_list[i].ss;
                                SAFE_FREE(ss_list);
                                SAFE_FREE(sitename);
@@ -2203,7 +2765,7 @@ NTSTATUS resolve_name_list(TALLOC_CTX *ctx,
        *return_ss_arr = NULL;
 
        if (is_ipaddress(name)) {
-               *return_ss_arr = TALLOC_P(ctx, struct sockaddr_storage);
+               *return_ss_arr = talloc(ctx, struct sockaddr_storage);
                if (!*return_ss_arr) {
                        return NT_STATUS_NO_MEMORY;
                }
@@ -2228,8 +2790,8 @@ NTSTATUS resolve_name_list(TALLOC_CTX *ctx,
 
        /* only return valid addresses for TCP connections */
        for (i=0, num_entries = 0; i<count; i++) {
-               if (!is_zero_addr((struct sockaddr *)&ss_list[i].ss) &&
-                               !is_broadcast_addr((struct sockaddr *)&ss_list[i].ss)) {
+               if (!is_zero_addr(&ss_list[i].ss) &&
+                   !is_broadcast_addr((struct sockaddr *)(void *)&ss_list[i].ss)) {
                        num_entries++;
                }
        }
@@ -2238,7 +2800,7 @@ NTSTATUS resolve_name_list(TALLOC_CTX *ctx,
                return NT_STATUS_BAD_NETWORK_NAME;
        }
 
-       *return_ss_arr = TALLOC_ARRAY(ctx,
+       *return_ss_arr = talloc_array(ctx,
                                struct sockaddr_storage,
                                num_entries);
        if (!(*return_ss_arr)) {
@@ -2247,8 +2809,8 @@ NTSTATUS resolve_name_list(TALLOC_CTX *ctx,
        }
 
        for (i=0, num_entries = 0; i<count; i++) {
-               if (!is_zero_addr((struct sockaddr *)&ss_list[i].ss) &&
-                               !is_broadcast_addr((struct sockaddr *)&ss_list[i].ss)) {
+               if (!is_zero_addr(&ss_list[i].ss) &&
+                   !is_broadcast_addr((struct sockaddr *)(void *)&ss_list[i].ss)) {
                        (*return_ss_arr)[num_entries++] = ss_list[i].ss;
                }
        }