diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 17:20:00 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 17:20:00 +0000 |
commit | 8daa83a594a2e98f39d764422bfbdbc62c9efd44 (patch) | |
tree | 4099e8021376c7d8c05bdf8503093d80e9c7bad0 /source4/libcli/resolve | |
parent | Initial commit. (diff) | |
download | samba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.tar.xz samba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.zip |
Adding upstream version 2:4.20.0+dfsg.upstream/2%4.20.0+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'source4/libcli/resolve')
-rw-r--r-- | source4/libcli/resolve/bcast.c | 117 | ||||
-rw-r--r-- | source4/libcli/resolve/dns_ex.c | 671 | ||||
-rw-r--r-- | source4/libcli/resolve/host.c | 60 | ||||
-rw-r--r-- | source4/libcli/resolve/lmhosts.c | 133 | ||||
-rw-r--r-- | source4/libcli/resolve/nbtlist.c | 223 | ||||
-rw-r--r-- | source4/libcli/resolve/resolve.c | 342 | ||||
-rw-r--r-- | source4/libcli/resolve/resolve.h | 53 | ||||
-rw-r--r-- | source4/libcli/resolve/resolve_lp.c | 52 | ||||
-rw-r--r-- | source4/libcli/resolve/testsuite.c | 92 | ||||
-rw-r--r-- | source4/libcli/resolve/wins.c | 83 |
10 files changed, 1826 insertions, 0 deletions
diff --git a/source4/libcli/resolve/bcast.c b/source4/libcli/resolve/bcast.c new file mode 100644 index 0000000..277a82d --- /dev/null +++ b/source4/libcli/resolve/bcast.c @@ -0,0 +1,117 @@ +/* + Unix SMB/CIFS implementation. + + broadcast name resolution module + + Copyright (C) Andrew Tridgell 2005 + Copyright (C) Jelmer Vernooij 2007 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libcli/resolve/resolve.h" +#include "system/network.h" +#include "lib/socket/netif.h" +#include "param/param.h" + +struct resolve_bcast_data { + struct interface *ifaces; + uint16_t nbt_port; + int nbt_timeout; +}; + +/** + broadcast name resolution method - async send + */ +static struct composite_context *resolve_name_bcast_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *event_ctx, + void *userdata, uint32_t flags, + uint16_t port, + struct nbt_name *name) +{ + int num_interfaces; + const char **address_list; + struct composite_context *c; + int i, count=0; + struct resolve_bcast_data *data = talloc_get_type(userdata, struct resolve_bcast_data); + + num_interfaces = iface_list_count(data->ifaces); + + address_list = talloc_array(mem_ctx, const char *, num_interfaces+1); + if (address_list == NULL) return NULL; + + for (i=0;i<num_interfaces;i++) { + bool ipv4 = iface_list_n_is_v4(data->ifaces, i); + const char *bcast; + + if (!ipv4) { + continue; + } + + bcast = iface_list_n_bcast(data->ifaces, i); + if (bcast == NULL) { + continue; + } + + address_list[count] = talloc_strdup(address_list, bcast); + if (address_list[count] == NULL) { + talloc_free(address_list); + return NULL; + } + count++; + } + address_list[count] = NULL; + + c = resolve_name_nbtlist_send(mem_ctx, event_ctx, flags, port, name, + address_list, data->ifaces, data->nbt_port, + data->nbt_timeout, true, false); + talloc_free(address_list); + + return c; +} + +/* + broadcast name resolution method - recv side + */ +static NTSTATUS resolve_name_bcast_recv(struct composite_context *c, + TALLOC_CTX *mem_ctx, + struct socket_address ***addrs, + char ***names) +{ + NTSTATUS status = resolve_name_nbtlist_recv(c, mem_ctx, addrs, names); + if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) { + /* this makes much more sense for a bcast name resolution + timeout */ + status = NT_STATUS_OBJECT_NAME_NOT_FOUND; + } + return status; +} + +bool resolve_context_add_bcast_method(struct resolve_context *ctx, struct interface *ifaces, uint16_t nbt_port, int nbt_timeout) +{ + struct resolve_bcast_data *data = talloc(ctx, struct resolve_bcast_data); + data->ifaces = ifaces; + data->nbt_port = nbt_port; + data->nbt_timeout = nbt_timeout; + return resolve_context_add_method(ctx, resolve_name_bcast_send, resolve_name_bcast_recv, data); +} + +bool resolve_context_add_bcast_method_lp(struct resolve_context *ctx, struct loadparm_context *lp_ctx) +{ + struct interface *ifaces; + load_interface_list(ctx, lp_ctx, &ifaces); + return resolve_context_add_bcast_method(ctx, ifaces, lpcfg_nbt_port(lp_ctx), lpcfg_parm_int(lp_ctx, NULL, "nbt", "timeout", 1)); +} diff --git a/source4/libcli/resolve/dns_ex.c b/source4/libcli/resolve/dns_ex.c new file mode 100644 index 0000000..09cc100 --- /dev/null +++ b/source4/libcli/resolve/dns_ex.c @@ -0,0 +1,671 @@ +/* + Unix SMB/CIFS implementation. + + async getaddrinfo()/dns_lookup() name resolution module + + Copyright (C) Andrew Tridgell 2005 + Copyright (C) Stefan Metzmacher 2008 + Copyright (C) Matthieu Patou 2011 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +/* + this module uses a fork() per getaddrinfo() or dns_looup() call. + At first that might seem crazy, but it is actually very fast, + and solves many of the tricky problems of keeping a child + hanging around in a librar (like what happens when the parent forks). + We use a talloc destructor to ensure that the child is cleaned up + when we have finished with this name resolution. +*/ + +#include "includes.h" +#include "system/network.h" +#include "system/filesys.h" +#include "lib/socket/socket.h" +#include "libcli/composite/composite.h" +#include "librpc/gen_ndr/ndr_nbt.h" +#include "libcli/resolve/resolve.h" +#include "lib/util/util_net.h" +#include "lib/addns/dnsquery.h" +#include "lib/addns/dns.h" +#include "lib/util/sys_rw.h" +#include "lib/util/smb_strtox.h" +#include <arpa/nameser.h> +#include <resolv.h> + +struct dns_ex_state { + bool do_fallback; + uint32_t flags; + uint16_t port; + struct nbt_name name; + struct socket_address **addrs; + char **names; + pid_t child; + int child_fd; + struct tevent_fd *fde; + struct tevent_context *event_ctx; +}; + +/* + kill off a wayward child if needed. This allows us to stop an async + name resolution without leaving a potentially blocking call running + in a child +*/ +static int dns_ex_destructor(struct dns_ex_state *state) +{ + int status; + + kill(state->child, SIGTERM); + if (waitpid(state->child, &status, WNOHANG) == 0) { + kill(state->child, SIGKILL); + waitpid(state->child, &status, 0); + } + + return 0; +} + +struct dns_records_container { + char **list; + uint32_t count; +}; + +static int reply_to_addrs(TALLOC_CTX *mem_ctx, uint32_t *a_num, + char ***cur_addrs, uint32_t total, + struct dns_request *reply, int port) +{ + char addrstr[INET6_ADDRSTRLEN]; + struct dns_rrec *rr; + char **addrs; + uint32_t i; + const char *addr; + + /* at most we over-allocate here, but not by much */ + addrs = talloc_realloc(mem_ctx, *cur_addrs, char *, + total + reply->num_answers); + if (!addrs) { + return 0; + } + *cur_addrs = addrs; + + for (i = 0; i < reply->num_answers; i++) { + rr = reply->answers[i]; + + /* we are only interested in the IN class */ + if (rr->r_class != DNS_CLASS_IN) { + continue; + } + + if (rr->type == QTYPE_NS) { + /* + * After the record for NS will come the A or AAAA + * record of the NS. + */ + break; + } + + /* verify we actually have a record here */ + if (!rr->data) { + continue; + } + + /* we are only interested in A and AAAA records */ + switch (rr->type) { + case QTYPE_A: + addr = inet_ntop(AF_INET, + (struct in_addr *)rr->data, + addrstr, sizeof(addrstr)); + if (addr == NULL) { + continue; + } + break; + case QTYPE_AAAA: +#ifdef HAVE_IPV6 + addr = inet_ntop(AF_INET6, + (struct in6_addr *)rr->data, + addrstr, sizeof(addrstr)); +#else + addr = NULL; +#endif + if (addr == NULL) { + continue; + } + break; + default: + continue; + } + + addrs[total] = talloc_asprintf(addrs, "%s@%u/%s", + addrstr, port, + rr->name->pLabelList->label); + if (addrs[total]) { + total++; + if (rr->type == QTYPE_A) { + (*a_num)++; + } + } + } + + return total; +} + +static DNS_ERROR dns_lookup(TALLOC_CTX *mem_ctx, const char* name, + uint16_t q_type, struct dns_request **reply) +{ + int len, rlen; + uint8_t *answer; + bool loop; + struct dns_buffer buf; + DNS_ERROR err; + + /* give space for a good sized answer by default */ + answer = NULL; + len = 1500; + do { + answer = talloc_realloc(mem_ctx, answer, uint8_t, len); + if (!answer) { + return ERROR_DNS_NO_MEMORY; + } + rlen = res_search(name, DNS_CLASS_IN, q_type, answer, len); + if (rlen == -1) { + if (len >= 65535) { + return ERROR_DNS_SOCKET_ERROR; + } + /* retry once with max packet size */ + len = 65535; + loop = true; + } else if (rlen > len) { + len = rlen; + loop = true; + } else { + loop = false; + } + } while(loop); + + buf.data = answer; + buf.size = rlen; + buf.offset = 0; + buf.error = ERROR_DNS_SUCCESS; + + err = dns_unmarshall_request(mem_ctx, &buf, reply); + + TALLOC_FREE(answer); + return err; +} + +static struct dns_records_container get_a_aaaa_records(TALLOC_CTX *mem_ctx, + const char* name, + int port) +{ + struct dns_request *reply; + struct dns_records_container ret; + char **addrs = NULL; + uint32_t a_num, total; + uint16_t qtype; + TALLOC_CTX *tmp_ctx; + DNS_ERROR err; + + memset(&ret, 0, sizeof(struct dns_records_container)); + + tmp_ctx = talloc_new(mem_ctx); + if (!tmp_ctx) { + return ret; + } + + qtype = QTYPE_AAAA; + + /* this is the blocking call we are going to lots of trouble + to avoid them in the parent */ + err = dns_lookup(tmp_ctx, name, qtype, &reply); + if (!ERR_DNS_IS_OK(err)) { + qtype = QTYPE_A; + err = dns_lookup(tmp_ctx, name, qtype, &reply); + if (!ERR_DNS_IS_OK(err)) { + goto done; + } + } + + a_num = total = 0; + total = reply_to_addrs(tmp_ctx, &a_num, &addrs, total, reply, port); + + if (qtype == QTYPE_AAAA && a_num == 0) { + /* + * DNS server didn't returned A when asked for AAAA records. + * Most of the server do it, let's ask for A specifically. + */ + err = dns_lookup(tmp_ctx, name, QTYPE_A, &reply); + if (ERR_DNS_IS_OK(err)) { + /* + * Ignore an error here and just return any AAAA + * records we already got. This may be an IPv6-only + * config. + */ + total = reply_to_addrs(tmp_ctx, &a_num, &addrs, total, + reply, port); + } + } + + if (total) { + talloc_steal(mem_ctx, addrs); + ret.count = total; + ret.list = addrs; + } + +done: + TALLOC_FREE(tmp_ctx); + return ret; +} + +static struct dns_records_container get_srv_records(TALLOC_CTX *mem_ctx, + const char* name) +{ + struct dns_records_container ret = {0}; + char **addrs = NULL; + struct dns_rr_srv *dclist; + NTSTATUS status; + size_t total; + size_t i; + size_t count = 0; + + memset(&ret, 0, sizeof(struct dns_records_container)); + /* this is the blocking call we are going to lots of trouble + to avoid them in the parent */ + status = ads_dns_lookup_srv(mem_ctx, name, &dclist, &count); + if (!NT_STATUS_IS_OK(status)) { + return ret; + } + total = 0; + if (count == 0) { + return ret; + } + + /* Loop over all returned records and pick the records */ + for (i = 0; i < count; i++) { + struct dns_records_container c; + const char* tmp_str; + + tmp_str = dclist[i].hostname; + if (strchr(tmp_str, '.') && tmp_str[strlen(tmp_str)-1] != '.') { + /* we are asking for a fully qualified name, but the + name doesn't end in a '.'. We need to prevent the + DNS library trying the search domains configured in + resolv.conf */ + tmp_str = talloc_asprintf(mem_ctx, "%s.", tmp_str); + } + + c = get_a_aaaa_records(mem_ctx, tmp_str, dclist[i].port); + + /* wrap check */ + if (total + c.count < total) { + /* possibly could just break here instead? */ + TALLOC_FREE(addrs); + return ret; + } + total += c.count; + if (addrs == NULL) { + addrs = c.list; + } else { + unsigned j; + + addrs = talloc_realloc(mem_ctx, addrs, char*, total); + for (j=0; j < c.count; j++) { + addrs[total - j - 1] = talloc_steal(addrs, c.list[j]); + } + } + } + + if (total) { + ret.count = total; + ret.list = addrs; + } + + return ret; +} +/* + the blocking child +*/ +static void run_child_dns_lookup(struct dns_ex_state *state, int fd) +{ + bool first; + bool do_srv = (state->flags & RESOLVE_NAME_FLAG_DNS_SRV); + struct dns_records_container c; + char* addrs = NULL; + unsigned int i; + + if (strchr(state->name.name, '.') && state->name.name[strlen(state->name.name)-1] != '.') { + /* we are asking for a fully qualified name, but the + name doesn't end in a '.'. We need to prevent the + DNS library trying the search domains configured in + resolv.conf */ + state->name.name = talloc_strdup_append(discard_const_p(char, state->name.name), + "."); + } + + + if (do_srv) { + c = get_srv_records(state, state->name.name); + } else { + c = get_a_aaaa_records(state, state->name.name, state->port); + } + + /* This line in critical - if we return without writing to the + * pipe, this is the signal that the name did not exist */ + if (c.count == 0) { + goto done; + } + + addrs = talloc_strdup(state, ""); + if (!addrs) { + goto done; + } + first = true; + + for (i=0; i < c.count; i++) { + addrs = talloc_asprintf_append_buffer(addrs, "%s%s", + first?"":",", + c.list[i]); + first = false; + } + + if (addrs) { + DEBUG(11, ("Addrs = %s\n", addrs)); + sys_write_v(fd, addrs, talloc_get_size(addrs)); + } + +done: + close(fd); +} + +/* + the blocking child +*/ +static void run_child_getaddrinfo(struct dns_ex_state *state, int fd) +{ + int ret; + struct addrinfo hints; + struct addrinfo *res; + struct addrinfo *res_list = NULL; + char *addrs; + bool first; + + ZERO_STRUCT(hints); + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV; + + ret = getaddrinfo(state->name.name, "0", &hints, &res_list); + /* try to fallback in case of error */ + if (state->do_fallback) { + switch (ret) { +#ifdef EAI_NODATA + case EAI_NODATA: +#endif + case EAI_FAIL: + /* Linux returns EAI_NODATA on non-RFC1034-compliant names. FreeBSD returns EAI_FAIL */ + case EAI_NONAME: + /* getaddrinfo() doesn't handle CNAME or non-RFC1034 compatible records */ + run_child_dns_lookup(state, fd); + return; + default: + break; + } + } + if (ret != 0) { + goto done; + } + + addrs = talloc_strdup(state, ""); + if (!addrs) { + goto done; + } + first = true; + for (res = res_list; res; res = res->ai_next) { + char addrstr[INET6_ADDRSTRLEN]; + if (!print_sockaddr_len(addrstr, sizeof(addrstr), (struct sockaddr *)res->ai_addr, res->ai_addrlen)) { + continue; + } + addrs = talloc_asprintf_append_buffer(addrs, "%s%s@%u/%s", + first?"":",", + addrstr, + state->port, + state->name.name); + if (!addrs) { + goto done; + } + first = false; + } + + if (addrs) { + sys_write_v(fd, addrs, talloc_get_size(addrs)); + } +done: + if (res_list) { + freeaddrinfo(res_list); + } + close(fd); +} + +/* + handle a read event on the pipe +*/ +static void pipe_handler(struct tevent_context *ev, struct tevent_fd *fde, + uint16_t flags, void *private_data) +{ + struct composite_context *c = talloc_get_type(private_data, struct composite_context); + struct dns_ex_state *state = talloc_get_type(c->private_data, + struct dns_ex_state); + char *address; + uint32_t num_addrs, i; + char **addrs; + int ret; + int status; + int value = 0; + + /* if we get any event from the child then we know that we + won't need to kill it off */ + talloc_set_destructor(state, NULL); + + if (ioctl(state->child_fd, FIONREAD, &value) != 0) { + value = 8192; + } + + address = talloc_array(state, char, value+1); + if (address) { + /* yes, we don't care about EAGAIN or other niceities + here. They just can't happen with this parent/child + relationship, and even if they did then giving an error is + the right thing to do */ + ret = read(state->child_fd, address, value); + } else { + ret = -1; + } + if (waitpid(state->child, &status, WNOHANG) == 0) { + kill(state->child, SIGKILL); + waitpid(state->child, &status, 0); + } + + if (ret <= 0) { + /* The check for ret == 0 here is important, if the + * name does not exist, then no bytes are written to + * the pipe */ + DEBUG(3,("dns child failed to find name '%s' of type %s\n", + state->name.name, (state->flags & RESOLVE_NAME_FLAG_DNS_SRV)?"SRV":"A")); + composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND); + return; + } + + /* ensure the address looks good */ + address[ret] = 0; + + addrs = str_list_make(state, address, ","); + if (composite_nomem(addrs, c)) return; + + num_addrs = str_list_length((const char * const *)addrs); + + state->addrs = talloc_array(state, struct socket_address *, + num_addrs+1); + if (composite_nomem(state->addrs, c)) return; + + state->names = talloc_array(state, char *, num_addrs+1); + if (composite_nomem(state->names, c)) return; + + for (i=0; i < num_addrs; i++) { + uint32_t port = 0; + char *p = strrchr(addrs[i], '@'); + char *n; + int error = 0; + + if (!p) { + composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND); + return; + } + + *p = '\0'; + p++; + + n = strrchr(p, '/'); + if (!n) { + composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND); + return; + } + + *n = '\0'; + n++; + + if (strcmp(addrs[i], "0.0.0.0") == 0) { + composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND); + return; + } + port = smb_strtoul(p, NULL, 10, &error, SMB_STR_STANDARD); + if (port > UINT16_MAX || error != 0) { + composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND); + return; + } + state->addrs[i] = socket_address_from_strings(state->addrs, + "ip", + addrs[i], + port); + if (composite_nomem(state->addrs[i], c)) return; + + state->names[i] = talloc_strdup(state->names, n); + if (composite_nomem(state->names[i], c)) return; + } + state->addrs[i] = NULL; + state->names[i] = NULL; + + composite_done(c); +} + +/* + getaddrinfo() or dns_lookup() name resolution method - async send + */ +struct composite_context *resolve_name_dns_ex_send(TALLOC_CTX *mem_ctx, + struct tevent_context *event_ctx, + void *privdata, + uint32_t flags, + uint16_t port, + struct nbt_name *name, + bool do_fallback) +{ + struct composite_context *c; + struct dns_ex_state *state; + int fd[2] = { -1, -1 }; + int ret; + + c = composite_create(mem_ctx, event_ctx); + if (c == NULL) return NULL; + + if (flags & RESOLVE_NAME_FLAG_FORCE_NBT) { + composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND); + return c; + } + + state = talloc_zero(c, struct dns_ex_state); + if (composite_nomem(state, c)) return c; + c->private_data = state; + + c->status = nbt_name_dup(state, name, &state->name); + if (!composite_is_ok(c)) return c; + + /* setup a pipe to chat to our child */ + ret = pipe(fd); + if (ret == -1) { + composite_error(c, map_nt_error_from_unix_common(errno)); + return c; + } + + state->do_fallback = do_fallback; + state->flags = flags; + state->port = port; + + state->child_fd = fd[0]; + state->event_ctx = c->event_ctx; + + /* we need to put the child in our event context so + we know when the dns_lookup() has finished */ + state->fde = tevent_add_fd(c->event_ctx, c, state->child_fd, TEVENT_FD_READ, + pipe_handler, c); + if (composite_nomem(state->fde, c)) { + close(fd[0]); + close(fd[1]); + return c; + } + tevent_fd_set_auto_close(state->fde); + + state->child = fork(); + if (state->child == (pid_t)-1) { + composite_error(c, map_nt_error_from_unix_common(errno)); + return c; + } + + if (state->child == 0) { + close(fd[0]); + if (state->flags & RESOLVE_NAME_FLAG_FORCE_DNS) { + run_child_dns_lookup(state, fd[1]); + } else { + run_child_getaddrinfo(state, fd[1]); + } + _exit(0); + } + close(fd[1]); + + /* cleanup wayward children */ + talloc_set_destructor(state, dns_ex_destructor); + + return c; +} + +/* + getaddrinfo() or dns_lookup() name resolution method - recv side +*/ +NTSTATUS resolve_name_dns_ex_recv(struct composite_context *c, + TALLOC_CTX *mem_ctx, + struct socket_address ***addrs, + char ***names) +{ + NTSTATUS status; + + status = composite_wait(c); + + if (NT_STATUS_IS_OK(status)) { + struct dns_ex_state *state = talloc_get_type(c->private_data, + struct dns_ex_state); + *addrs = talloc_steal(mem_ctx, state->addrs); + if (names) { + *names = talloc_steal(mem_ctx, state->names); + } + } + + talloc_free(c); + return status; +} diff --git a/source4/libcli/resolve/host.c b/source4/libcli/resolve/host.c new file mode 100644 index 0000000..755a4e8 --- /dev/null +++ b/source4/libcli/resolve/host.c @@ -0,0 +1,60 @@ +/* + Unix SMB/CIFS implementation. + + async "host" name resolution module + + Copyright (C) Andrew Tridgell 2005 + Copyright (C) Stefan Metzmacher 2008 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "lib/events/events.h" +#include "system/network.h" +#include "system/filesys.h" +#include "lib/socket/socket.h" +#include "libcli/composite/composite.h" +#include "librpc/gen_ndr/ndr_nbt.h" +#include "libcli/resolve/resolve.h" + +/* + getaddrinfo() (with fallback to dns_lookup()) name resolution method - async send + */ +struct composite_context *resolve_name_host_send(TALLOC_CTX *mem_ctx, + struct tevent_context *event_ctx, + void *privdata, uint32_t flags, + uint16_t port, + struct nbt_name *name) +{ + return resolve_name_dns_ex_send(mem_ctx, event_ctx, NULL, flags, + port, name, true); +} + +/* + getaddrinfo() (with fallback to dns_lookup()) name resolution method - recv side +*/ +NTSTATUS resolve_name_host_recv(struct composite_context *c, + TALLOC_CTX *mem_ctx, + struct socket_address ***addrs, + char ***names) +{ + return resolve_name_dns_ex_recv(c, mem_ctx, addrs, names); +} + +bool resolve_context_add_host_method(struct resolve_context *ctx) +{ + return resolve_context_add_method(ctx, resolve_name_host_send, resolve_name_host_recv, + NULL); +} diff --git a/source4/libcli/resolve/lmhosts.c b/source4/libcli/resolve/lmhosts.c new file mode 100644 index 0000000..244a9a3 --- /dev/null +++ b/source4/libcli/resolve/lmhosts.c @@ -0,0 +1,133 @@ +/* + Unix SMB/CIFS implementation. + + lmhosts name resolution module + + Copyright (C) Andrew Tridgell 1994-1998,2005 + Copyright (C) Jeremy Allison 2007 + Copyright (C) Jelmer Vernooij 2007 + Copyright (C) Andrew Bartlett <abartlet@samba.org> 2009-2014 + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libcli/composite/composite.h" +#include "libcli/resolve/resolve.h" +#include "lib/socket/socket.h" +#include "system/network.h" +#include "lib/socket/netif.h" +#include "param/param.h" +#include "lib/util/util_net.h" +#include "libcli/nbt/libnbt.h" +#include "dynconfig.h" + +struct resolve_lmhosts_state { + struct socket_address **addrs; + char **names; +}; + +/** + lmhosts name resolution method - async send + */ +/* + general name resolution - async send + */ +static struct composite_context *resolve_name_lmhosts_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *event_ctx, + void *userdata, uint32_t flags, + uint16_t port, + struct nbt_name *name) +{ + struct composite_context *c; + struct resolve_lmhosts_state *state; + struct sockaddr_storage *resolved_iplist; + size_t resolved_count = 0, i; + + if (event_ctx == NULL) { + return NULL; + } + + c = composite_create(mem_ctx, event_ctx); + if (c == NULL) return NULL; + + if (composite_nomem(c->event_ctx, c)) return c; + + state = talloc_zero(c, struct resolve_lmhosts_state); + if (composite_nomem(state, c)) return c; + c->private_data = state; + + c->status = resolve_lmhosts_file_as_sockaddr(state, + dyn_LMHOSTSFILE, + name->name, + name->type, + &resolved_iplist, + &resolved_count); + if (!composite_is_ok(c)) return c; + + for (i=0; i < resolved_count; i += 2) { + state->addrs = talloc_realloc(state, state->addrs, struct socket_address *, i+2); + if (composite_nomem(state->addrs, c)) return c; + + set_sockaddr_port((struct sockaddr *)&resolved_iplist[i], port); + + state->addrs[i] = socket_address_from_sockaddr(state->addrs, (struct sockaddr *)&resolved_iplist[i], sizeof(resolved_iplist[i])); + if (composite_nomem(state->addrs[i], c)) return c; + + state->addrs[i+1] = NULL; + + + state->names = talloc_realloc(state, state->names, char *, i+2); + if (composite_nomem(state->addrs, c)) return c; + + state->names[i] = talloc_strdup(state->names, name->name); + if (composite_nomem(state->names[i], c)) return c; + + state->names[i+1] = NULL; + + } + + composite_done(c); + return c; +} + +/* + general name resolution method - recv side + */ +static NTSTATUS resolve_name_lmhosts_recv(struct composite_context *c, + TALLOC_CTX *mem_ctx, + struct socket_address ***addrs, + char ***names) +{ + NTSTATUS status; + + status = composite_wait(c); + + if (NT_STATUS_IS_OK(status)) { + struct resolve_lmhosts_state *state = talloc_get_type(c->private_data, struct resolve_lmhosts_state); + *addrs = talloc_steal(mem_ctx, state->addrs); + if (names) { + *names = talloc_steal(mem_ctx, state->names); + } + } + + talloc_free(c); + return status; +} + + +bool resolve_context_add_lmhosts_method(struct resolve_context *ctx) +{ + return resolve_context_add_method(ctx, resolve_name_lmhosts_send, resolve_name_lmhosts_recv, NULL); +} diff --git a/source4/libcli/resolve/nbtlist.c b/source4/libcli/resolve/nbtlist.c new file mode 100644 index 0000000..13f38ff --- /dev/null +++ b/source4/libcli/resolve/nbtlist.c @@ -0,0 +1,223 @@ +/* + Unix SMB/CIFS implementation. + + nbt list of addresses name resolution module + + Copyright (C) Andrew Tridgell 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +/* + TODO: we should lower the timeout, and add retries for each name +*/ + +#include "includes.h" +#include "libcli/composite/composite.h" +#include "system/network.h" +#include "lib/socket/socket.h" +#include "lib/socket/netif.h" +#include "librpc/gen_ndr/ndr_nbt.h" +#include "../libcli/nbt/libnbt.h" +#include "param/param.h" +#include "libcli/resolve/resolve.h" + +struct nbtlist_state { + uint16_t flags; + uint16_t port; + struct nbt_name name; + struct nbt_name_socket *nbtsock; + int num_queries; + struct nbt_name_request **queries; + struct nbt_name_query *io_queries; + struct socket_address **addrs; + char **names; + struct interface *ifaces; +}; + +/* + handle events during nbtlist name resolution +*/ +static void nbtlist_handler(struct nbt_name_request *req) +{ + struct composite_context *c = talloc_get_type(req->async.private_data, + struct composite_context); + struct nbtlist_state *state = talloc_get_type(c->private_data, struct nbtlist_state); + struct nbt_name_query *q; + int i; + + for (i=0;i<state->num_queries;i++) { + if (req == state->queries[i]) break; + } + + if (i == state->num_queries) { + /* not for us?! */ + composite_error(c, NT_STATUS_INTERNAL_ERROR); + return; + } + + q = &state->io_queries[i]; + + c->status = nbt_name_query_recv(req, state, q); + + /* free the network resource directly */ + talloc_free(state->nbtsock); + if (!composite_is_ok(c)) return; + + if (q->out.num_addrs < 1) { + composite_error(c, NT_STATUS_UNEXPECTED_NETWORK_ERROR); + return; + } + + state->addrs = talloc_array(state, struct socket_address *, + q->out.num_addrs + 1); + if (composite_nomem(state->addrs, c)) return; + + state->names = talloc_array(state, char *, q->out.num_addrs + 1); + if (composite_nomem(state->names, c)) return; + + for (i=0;i<q->out.num_addrs;i++) { + state->addrs[i] = socket_address_from_strings(state->addrs, + "ipv4", + q->out.reply_addrs[i], + state->port); + if (composite_nomem(state->addrs[i], c)) return; + + state->names[i] = talloc_strdup(state->names, state->name.name); + if (composite_nomem(state->names[i], c)) return; + } + state->addrs[i] = NULL; + state->names[i] = NULL; + + composite_done(c); +} + +/* + nbtlist name resolution method - async send + */ +struct composite_context *resolve_name_nbtlist_send(TALLOC_CTX *mem_ctx, + struct tevent_context *event_ctx, + uint32_t flags, + uint16_t port, + struct nbt_name *name, + const char * const *address_list, + struct interface *ifaces, + uint16_t nbt_port, + int nbt_timeout, + bool broadcast, + bool wins_lookup) +{ + struct composite_context *c; + struct nbtlist_state *state; + int i; + + c = composite_create(mem_ctx, event_ctx); + if (c == NULL) return NULL; + + if (flags & RESOLVE_NAME_FLAG_FORCE_DNS) { + composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND); + return c; + } + + if (strlen(name->name) > 15) { + composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND); + return c; + } + + state = talloc(c, struct nbtlist_state); + if (composite_nomem(state, c)) return c; + c->private_data = state; + + state->flags = flags; + state->port = port; + + c->status = nbt_name_dup(state, name, &state->name); + if (!composite_is_ok(c)) return c; + + state->name.name = strupper_talloc(state, state->name.name); + if (composite_nomem(state->name.name, c)) return c; + if (state->name.scope) { + state->name.scope = strupper_talloc(state, state->name.scope); + if (composite_nomem(state->name.scope, c)) return c; + } + + state->ifaces = talloc_reference(state, ifaces); + + /* + * we can't push long names on the wire, + * so bail out here to give a useful error message + */ + if (strlen(state->name.name) > 15) { + composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND); + return c; + } + + state->nbtsock = nbt_name_socket_init(state, event_ctx); + if (composite_nomem(state->nbtsock, c)) return c; + + /* count the address_list size */ + for (i=0;address_list[i];i++) /* noop */ ; + + state->num_queries = i; + state->io_queries = talloc_array(state, struct nbt_name_query, state->num_queries); + if (composite_nomem(state->io_queries, c)) return c; + + state->queries = talloc_array(state, struct nbt_name_request *, state->num_queries); + if (composite_nomem(state->queries, c)) return c; + + for (i=0;i<state->num_queries;i++) { + state->io_queries[i].in.name = state->name; + state->io_queries[i].in.dest_addr = talloc_strdup(state->io_queries, address_list[i]); + state->io_queries[i].in.dest_port = nbt_port; + if (composite_nomem(state->io_queries[i].in.dest_addr, c)) return c; + + state->io_queries[i].in.broadcast = broadcast; + state->io_queries[i].in.wins_lookup = wins_lookup; + state->io_queries[i].in.timeout = nbt_timeout; + state->io_queries[i].in.retries = 2; + + state->queries[i] = nbt_name_query_send(state->nbtsock, &state->io_queries[i]); + if (composite_nomem(state->queries[i], c)) return c; + + state->queries[i]->async.fn = nbtlist_handler; + state->queries[i]->async.private_data = c; + } + + return c; +} + +/* + nbt list of addresses name resolution method - recv side + */ +NTSTATUS resolve_name_nbtlist_recv(struct composite_context *c, + TALLOC_CTX *mem_ctx, + struct socket_address ***addrs, + char ***names) +{ + NTSTATUS status; + + status = composite_wait(c); + + if (NT_STATUS_IS_OK(status)) { + struct nbtlist_state *state = talloc_get_type(c->private_data, struct nbtlist_state); + *addrs = talloc_steal(mem_ctx, state->addrs); + if (names) { + *names = talloc_steal(mem_ctx, state->names); + } + } + + talloc_free(c); + return status; +} + diff --git a/source4/libcli/resolve/resolve.c b/source4/libcli/resolve/resolve.c new file mode 100644 index 0000000..db2606b --- /dev/null +++ b/source4/libcli/resolve/resolve.c @@ -0,0 +1,342 @@ +/* + Unix SMB/CIFS implementation. + + general name resolution interface + + Copyright (C) Andrew Tridgell 2005 + Copyright (C) Jelmer Vernooij 2007 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libcli/composite/composite.h" +#include "libcli/resolve/resolve.h" +#include "librpc/gen_ndr/ndr_nbt.h" +#include "system/network.h" +#include "lib/socket/socket.h" +#include "../lib/util/dlinklist.h" +#include "lib/tsocket/tsocket.h" +#include "lib/util/util_net.h" + +#undef strcasecmp + +struct resolve_state { + struct resolve_context *ctx; + struct resolve_method *method; + uint32_t flags; + uint16_t port; + struct nbt_name name; + struct composite_context *creq; + struct socket_address **addrs; + char **names; +}; + +static struct composite_context *setup_next_method(struct composite_context *c); + + +struct resolve_context { + struct resolve_method { + resolve_name_send_fn send_fn; + resolve_name_recv_fn recv_fn; + void *privdata; + struct resolve_method *prev, *next; + } *methods; +}; + +/** + * Initialize a resolve context + */ +struct resolve_context *resolve_context_init(TALLOC_CTX *mem_ctx) +{ + return talloc_zero(mem_ctx, struct resolve_context); +} + +/** + * Add a resolve method + */ +bool resolve_context_add_method(struct resolve_context *ctx, resolve_name_send_fn send_fn, + resolve_name_recv_fn recv_fn, void *userdata) +{ + struct resolve_method *method = talloc_zero(ctx, struct resolve_method); + + if (method == NULL) + return false; + + method->send_fn = send_fn; + method->recv_fn = recv_fn; + method->privdata = userdata; + DLIST_ADD_END(ctx->methods, method); + return true; +} + +/** + handle completion of one name resolve method +*/ +static void resolve_handler(struct composite_context *creq) +{ + struct composite_context *c = (struct composite_context *)creq->async.private_data; + struct resolve_state *state = talloc_get_type(c->private_data, struct resolve_state); + const struct resolve_method *method = state->method; + + c->status = method->recv_fn(creq, state, &state->addrs, &state->names); + + if (!NT_STATUS_IS_OK(c->status)) { + state->method = state->method->next; + state->creq = setup_next_method(c); + if (state->creq != NULL) { + return; + } + } + + if (!NT_STATUS_IS_OK(c->status)) { + c->state = COMPOSITE_STATE_ERROR; + } else { + c->state = COMPOSITE_STATE_DONE; + } + if (c->async.fn) { + c->async.fn(c); + } +} + + +static struct composite_context *setup_next_method(struct composite_context *c) +{ + struct resolve_state *state = talloc_get_type(c->private_data, struct resolve_state); + struct composite_context *creq = NULL; + + do { + if (state->method) { + creq = state->method->send_fn(c, c->event_ctx, + state->method->privdata, + state->flags, + state->port, + &state->name); + } + if (creq == NULL && state->method) state->method = state->method->next; + + } while (!creq && state->method); + + if (creq) { + creq->async.fn = resolve_handler; + creq->async.private_data = c; + } + + return creq; +} + +/* + general name resolution - async send + */ +struct composite_context *resolve_name_all_send(struct resolve_context *ctx, + TALLOC_CTX *mem_ctx, + uint32_t flags, /* RESOLVE_NAME_FLAG_* */ + uint16_t port, + struct nbt_name *name, + struct tevent_context *event_ctx) +{ + struct composite_context *c; + struct resolve_state *state; + + if (event_ctx == NULL) { + return NULL; + } + + c = composite_create(mem_ctx, event_ctx); + if (c == NULL) return NULL; + + if (composite_nomem(c->event_ctx, c)) return c; + + state = talloc(c, struct resolve_state); + if (composite_nomem(state, c)) return c; + c->private_data = state; + + state->flags = flags; + state->port = port; + + c->status = nbt_name_dup(state, name, &state->name); + if (!composite_is_ok(c)) return c; + + state->ctx = talloc_reference(state, ctx); + if (composite_nomem(state->ctx, c)) return c; + + if (is_ipaddress(state->name.name) || + strcasecmp(state->name.name, "localhost") == 0) { + state->addrs = talloc_array(state, struct socket_address *, 2); + if (composite_nomem(state->addrs, c)) return c; + state->addrs[0] = socket_address_from_strings(state->addrs, "ip", + state->name.name, 0); + if (composite_nomem(state->addrs[0], c)) return c; + state->addrs[1] = NULL; + state->names = talloc_array(state, char *, 2); + if (composite_nomem(state->names, c)) return c; + state->names[0] = talloc_strdup(state->names, state->name.name); + if (composite_nomem(state->names[0], c)) return c; + state->names[1] = NULL; + composite_done(c); + return c; + } + + state->method = ctx->methods; + if (state->method == NULL) { + composite_error(c, NT_STATUS_BAD_NETWORK_NAME); + return c; + } + state->creq = setup_next_method(c); + if (composite_nomem(state->creq, c)) return c; + + return c; +} + +/* + general name resolution method - recv side + */ +NTSTATUS resolve_name_all_recv(struct composite_context *c, + TALLOC_CTX *mem_ctx, + struct socket_address ***addrs, + char ***names) +{ + NTSTATUS status; + + status = composite_wait(c); + + if (NT_STATUS_IS_OK(status)) { + struct resolve_state *state = talloc_get_type(c->private_data, struct resolve_state); + *addrs = talloc_steal(mem_ctx, state->addrs); + if (names) { + *names = talloc_steal(mem_ctx, state->names); + } + } + + talloc_free(c); + return status; +} + +struct composite_context *resolve_name_ex_send(struct resolve_context *ctx, + TALLOC_CTX *mem_ctx, + uint32_t flags, /* RESOLVE_NAME_FLAG_* */ + uint16_t port, + struct nbt_name *name, + struct tevent_context *event_ctx) +{ + return resolve_name_all_send(ctx, mem_ctx, flags, port, name, event_ctx); +} + +struct composite_context *resolve_name_send(struct resolve_context *ctx, + TALLOC_CTX *mem_ctx, + struct nbt_name *name, + struct tevent_context *event_ctx) +{ + return resolve_name_ex_send(ctx, mem_ctx, 0, 0, name, event_ctx); +} + +NTSTATUS resolve_name_recv(struct composite_context *c, + TALLOC_CTX *mem_ctx, + const char **reply_addr) +{ + NTSTATUS status; + struct socket_address **addrs = NULL; + + status = resolve_name_all_recv(c, mem_ctx, &addrs, NULL); + + if (NT_STATUS_IS_OK(status)) { + struct tsocket_address *t_addr = socket_address_to_tsocket_address(addrs, addrs[0]); + if (!t_addr) { + return NT_STATUS_NO_MEMORY; + } + + *reply_addr = tsocket_address_inet_addr_string(t_addr, mem_ctx); + talloc_free(addrs); + if (!*reply_addr) { + return NT_STATUS_NO_MEMORY; + } + } + + return status; +} + +/* + receive multiple responses from resolve_name_send() + */ +NTSTATUS resolve_name_multiple_recv(struct composite_context *c, + TALLOC_CTX *mem_ctx, + const char ***reply_addrs) +{ + NTSTATUS status; + struct socket_address **addrs = NULL; + int i; + + status = resolve_name_all_recv(c, mem_ctx, &addrs, NULL); + NT_STATUS_NOT_OK_RETURN(status); + + /* count the addresses */ + for (i=0; addrs[i]; i++) ; + + *reply_addrs = talloc_array(mem_ctx, const char *, i+1); + NT_STATUS_HAVE_NO_MEMORY(*reply_addrs); + + for (i=0; addrs[i]; i++) { + struct tsocket_address *t_addr = socket_address_to_tsocket_address(addrs, addrs[i]); + NT_STATUS_HAVE_NO_MEMORY(t_addr); + + (*reply_addrs)[i] = tsocket_address_inet_addr_string(t_addr, *reply_addrs); + NT_STATUS_HAVE_NO_MEMORY((*reply_addrs)[i]); + } + (*reply_addrs)[i] = NULL; + + talloc_free(addrs); + + return status; +} + +/* + general name resolution - sync call + */ +NTSTATUS resolve_name_ex(struct resolve_context *ctx, + uint32_t flags, /* RESOLVE_NAME_FLAG_* */ + uint16_t port, + struct nbt_name *name, + TALLOC_CTX *mem_ctx, + const char **reply_addr, + struct tevent_context *ev) +{ + struct composite_context *c = resolve_name_ex_send(ctx, mem_ctx, flags, port, name, ev); + return resolve_name_recv(c, mem_ctx, reply_addr); +} + + +/* Initialise a struct nbt_name with a NULL scope */ + +void make_nbt_name(struct nbt_name *nbt, const char *name, int type) +{ + nbt->name = name; + nbt->scope = NULL; + nbt->type = type; +} + +/* Initialise a struct nbt_name with a NBT_NAME_CLIENT (0x00) name */ + +void make_nbt_name_client(struct nbt_name *nbt, const char *name) +{ + make_nbt_name(nbt, name, NBT_NAME_CLIENT); +} + +/* Initialise a struct nbt_name with a NBT_NAME_SERVER (0x20) name */ + +void make_nbt_name_server(struct nbt_name *nbt, const char *name) +{ + make_nbt_name(nbt, name, NBT_NAME_SERVER); +} + + diff --git a/source4/libcli/resolve/resolve.h b/source4/libcli/resolve/resolve.h new file mode 100644 index 0000000..8ace574 --- /dev/null +++ b/source4/libcli/resolve/resolve.h @@ -0,0 +1,53 @@ +/* + Unix SMB/CIFS implementation. + + general name resolution interface + + Copyright (C) Andrew Tridgell 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __LIBCLI_RESOLVE_H__ +#define __LIBCLI_RESOLVE_H__ + +struct socket_address; +struct tevent_context; + +#include "../libcli/nbt/libnbt.h" + +/* force that only NBT name resolution is used */ +#define RESOLVE_NAME_FLAG_FORCE_NBT 0x00000001 +/* force that only DNS name resolution is used */ +#define RESOLVE_NAME_FLAG_FORCE_DNS 0x00000002 +/* tell the dns resolver to do a DNS SRV lookup */ +#define RESOLVE_NAME_FLAG_DNS_SRV 0x00000004 +/* allow the resolver to overwrite the given port, e.g. for DNS SRV */ +#define RESOLVE_NAME_FLAG_OVERWRITE_PORT 0x00000008 + +typedef struct composite_context *(*resolve_name_send_fn)(TALLOC_CTX *mem_ctx, + struct tevent_context *, + void *privdata, + uint32_t flags, + uint16_t port, + struct nbt_name *); +typedef NTSTATUS (*resolve_name_recv_fn)(struct composite_context *creq, + TALLOC_CTX *mem_ctx, + struct socket_address ***addrs, + char ***names); +#include "libcli/resolve/proto.h" +struct interface; +#include "libcli/resolve/lp_proto.h" + +#endif /* __LIBCLI_RESOLVE_H__ */ diff --git a/source4/libcli/resolve/resolve_lp.c b/source4/libcli/resolve/resolve_lp.c new file mode 100644 index 0000000..56c7b01 --- /dev/null +++ b/source4/libcli/resolve/resolve_lp.c @@ -0,0 +1,52 @@ +/* + Unix SMB/CIFS implementation. + Samba utility functions + Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libcli/resolve/resolve.h" +#include "param/param.h" + +struct resolve_context *lpcfg_resolve_context(struct loadparm_context *lp_ctx) +{ + const char **methods = lpcfg_name_resolve_order(lp_ctx); + int i; + struct resolve_context *ret = resolve_context_init(lp_ctx); + + if (ret == NULL) + return NULL; + + for (i = 0; methods != NULL && methods[i] != NULL; i++) { + if (!strcmp(methods[i], "wins")) { + if (lpcfg_disable_netbios(lp_ctx) == false) { + resolve_context_add_wins_method_lp(ret, lp_ctx); + } + } else if (!strcmp(methods[i], "bcast")) { + if (lpcfg_disable_netbios(lp_ctx) == false) { + resolve_context_add_bcast_method_lp(ret, lp_ctx); + } + } else if (!strcmp(methods[i], "lmhosts")) { + resolve_context_add_lmhosts_method(ret); + } else if (!strcmp(methods[i], "host")) { + resolve_context_add_host_method(ret); + } else { + DEBUG(0, ("Unknown resolve method '%s'\n", methods[i])); + } + } + + return ret; +} diff --git a/source4/libcli/resolve/testsuite.c b/source4/libcli/resolve/testsuite.c new file mode 100644 index 0000000..2bf6e46 --- /dev/null +++ b/source4/libcli/resolve/testsuite.c @@ -0,0 +1,92 @@ +/* + Unix SMB/CIFS implementation. + + local test for async resolve code + + Copyright (C) Andrew Tridgell 2004 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "lib/events/events.h" +#include "libcli/resolve/resolve.h" +#include "torture/torture.h" +#include "torture/local/proto.h" +#include "system/network.h" +#include "lib/util/util_net.h" + +static bool test_async_resolve(struct torture_context *tctx) +{ + struct nbt_name n; + struct tevent_context *ev; + int timelimit = torture_setting_int(tctx, "timelimit", 2); + const char *host = torture_setting_string(tctx, "host", NULL); + int count = 0; + struct timeval tv = timeval_current(); + TALLOC_CTX *mem_ctx = tctx; + + ev = tctx->ev; + + ZERO_STRUCT(n); + n.name = host; + + torture_comment(tctx, "Testing async resolve of '%s' for %d seconds\n", + host, timelimit); + while (timeval_elapsed(&tv) < timelimit) { + struct socket_address **s; + struct composite_context *c = resolve_name_host_send(mem_ctx, ev, NULL, 0, 0, &n); + torture_assert(tctx, c != NULL, "resolve_name_host_send"); + torture_assert_ntstatus_ok(tctx, resolve_name_host_recv(c, mem_ctx, &s, NULL), + "async resolve failed"); + count++; + } + + torture_comment(tctx, "async rate of %.1f resolves/sec\n", + count/timeval_elapsed(&tv)); + return true; +} + +/* + test resolution using sync method +*/ +static bool test_sync_resolve(struct torture_context *tctx) +{ + int timelimit = torture_setting_int(tctx, "timelimit", 2); + struct timeval tv = timeval_current(); + int count = 0; + const char *host = torture_setting_string(tctx, "host", NULL); + + torture_comment(tctx, "Testing sync resolve of '%s' for %d seconds\n", + host, timelimit); + while (timeval_elapsed(&tv) < timelimit) { + inet_ntoa(interpret_addr2(host)); + count++; + } + + torture_comment(tctx, "sync rate of %.1f resolves/sec\n", + count/timeval_elapsed(&tv)); + return true; +} + + +struct torture_suite *torture_local_resolve(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "resolve"); + + torture_suite_add_simple_test(suite, "async", test_async_resolve); + torture_suite_add_simple_test(suite, "sync", test_sync_resolve); + + return suite; +} diff --git a/source4/libcli/resolve/wins.c b/source4/libcli/resolve/wins.c new file mode 100644 index 0000000..541d3fe --- /dev/null +++ b/source4/libcli/resolve/wins.c @@ -0,0 +1,83 @@ +/* + Unix SMB/CIFS implementation. + + wins name resolution module + + Copyright (C) Andrew Tridgell 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "../libcli/nbt/libnbt.h" +#include "libcli/resolve/resolve.h" +#include "param/param.h" +#include "lib/socket/socket.h" +#include "lib/socket/netif.h" + +struct resolve_wins_data { + char **address_list; + struct interface *ifaces; + uint16_t nbt_port; + int nbt_timeout; +}; + +/** + wins name resolution method - async send + */ +struct composite_context *resolve_name_wins_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *event_ctx, + void *userdata, + uint32_t flags, + uint16_t port, + struct nbt_name *name) +{ + struct resolve_wins_data *wins_data = talloc_get_type(userdata, struct resolve_wins_data); + if (wins_data->address_list == NULL) return NULL; + return resolve_name_nbtlist_send(mem_ctx, event_ctx, flags, port, name, + (const char * const *)wins_data->address_list, + wins_data->ifaces, + wins_data->nbt_port, wins_data->nbt_timeout, + false, true); +} + +/* + wins name resolution method - recv side + */ +NTSTATUS resolve_name_wins_recv(struct composite_context *c, + TALLOC_CTX *mem_ctx, + struct socket_address ***addrs, + char ***names) +{ + return resolve_name_nbtlist_recv(c, mem_ctx, addrs, names); +} + +bool resolve_context_add_wins_method(struct resolve_context *ctx, const char **address_list, struct interface *ifaces, uint16_t nbt_port, int nbt_timeout) +{ + struct resolve_wins_data *wins_data = talloc(ctx, struct resolve_wins_data); + wins_data->address_list = str_list_copy(wins_data, address_list); + wins_data->ifaces = talloc_reference(wins_data, ifaces); + wins_data->nbt_port = nbt_port; + wins_data->nbt_timeout = nbt_timeout; + return resolve_context_add_method(ctx, resolve_name_wins_send, resolve_name_wins_recv, + wins_data); +} + +bool resolve_context_add_wins_method_lp(struct resolve_context *ctx, struct loadparm_context *lp_ctx) +{ + struct interface *ifaces; + load_interface_list(ctx, lp_ctx, &ifaces); + return resolve_context_add_wins_method(ctx, lpcfg_wins_server_list(lp_ctx), ifaces, lpcfg_nbt_port(lp_ctx), lpcfg_parm_int(lp_ctx, NULL, "nbt", "timeout", 1)); +} |