diff options
Diffstat (limited to 'source4/libcli')
-rw-r--r-- | source4/libcli/composite/composite.h | 2 | ||||
-rw-r--r-- | source4/libcli/config.mk | 4 | ||||
-rw-r--r-- | source4/libcli/ldap/ldap.c | 26 | ||||
-rw-r--r-- | source4/libcli/ldap/ldap_client.c | 2 | ||||
-rw-r--r-- | source4/libcli/ldap/ldap_controls.c | 115 | ||||
-rw-r--r-- | source4/libcli/ldap/ldap_ndr.c | 1 | ||||
-rw-r--r-- | source4/libcli/resolve/bcast.c | 32 | ||||
-rw-r--r-- | source4/libcli/resolve/dns_ex.c | 532 | ||||
-rw-r--r-- | source4/libcli/resolve/host.c | 209 | ||||
-rw-r--r-- | source4/libcli/resolve/nbtlist.c | 53 | ||||
-rw-r--r-- | source4/libcli/resolve/resolve.c | 45 | ||||
-rw-r--r-- | source4/libcli/resolve/resolve.h | 22 | ||||
-rw-r--r-- | source4/libcli/resolve/testsuite.c | 4 | ||||
-rw-r--r-- | source4/libcli/resolve/wins.c | 33 | ||||
-rw-r--r-- | source4/libcli/wrepl/winsrepl.c | 9 |
15 files changed, 780 insertions, 309 deletions
diff --git a/source4/libcli/composite/composite.h b/source4/libcli/composite/composite.h index 28cd6a88dc..25799d6a94 100644 --- a/source4/libcli/composite/composite.h +++ b/source4/libcli/composite/composite.h @@ -24,6 +24,8 @@ #include "libcli/raw/interfaces.h" +struct event_context; + /* this defines the structures associated with "composite" requests. Composite requests are libcli requests that are internally diff --git a/source4/libcli/config.mk b/source4/libcli/config.mk index 8c31077d8c..85cba421e2 100644 --- a/source4/libcli/config.mk +++ b/source4/libcli/config.mk @@ -117,8 +117,7 @@ LIBCLI_CLDAP_OBJ_FILES = $(libclisrcdir)/cldap/cldap.o # PUBLIC_HEADERS += $(libclisrcdir)/cldap/cldap.h [SUBSYSTEM::LIBCLI_WREPL] -PUBLIC_DEPENDENCIES = NDR_WINSREPL samba-socket LIBCLI_RESOLVE LIBEVENTS \ - LIBPACKET LIBNDR +PUBLIC_DEPENDENCIES = NDR_WINSREPL samba-socket LIBEVENTS LIBPACKET LIBCLI_WREPL_OBJ_FILES = $(libclisrcdir)/wrepl/winsrepl.o @@ -136,6 +135,7 @@ PRIVATE_DEPENDENCIES = LIBCLI_NBT LIBSAMBA-HOSTCONFIG LIBNETIF LP_RESOLVE_OBJ_FILES = $(addprefix $(libclisrcdir)/resolve/, \ bcast.o nbtlist.o wins.o \ + dns_ex.o \ host.o resolve_lp.o) $(eval $(call proto_header_template,$(libclisrcdir)/resolve/lp_proto.h,$(LP_RESOLVE_OBJ_FILES:.o=.c))) diff --git a/source4/libcli/ldap/ldap.c b/source4/libcli/ldap/ldap.c index 87c89daf9f..7a65cc5c27 100644 --- a/source4/libcli/ldap/ldap.c +++ b/source4/libcli/ldap/ldap.c @@ -504,9 +504,9 @@ static const char *blob2string_talloc(TALLOC_CTX *mem_ctx, return result; } -static bool asn1_read_OctetString_talloc(TALLOC_CTX *mem_ctx, - struct asn1_data *data, - const char **result) +bool asn1_read_OctetString_talloc(TALLOC_CTX *mem_ctx, + struct asn1_data *data, + const char **result) { DATA_BLOB string; if (!asn1_read_OctetString(data, mem_ctx, &string)) @@ -894,7 +894,7 @@ failed: return NULL; } - +/* Decode a single LDAP attribute, possibly containing multiple values */ static void ldap_decode_attrib(TALLOC_CTX *mem_ctx, struct asn1_data *data, struct ldb_message_element *attrib) { @@ -911,11 +911,11 @@ static void ldap_decode_attrib(TALLOC_CTX *mem_ctx, struct asn1_data *data, } -static void ldap_decode_attribs(TALLOC_CTX *mem_ctx, struct asn1_data *data, - struct ldb_message_element **attributes, - int *num_attributes) +/* Decode a set of LDAP attributes, as found in the dereference control */ +void ldap_decode_attribs_bare(TALLOC_CTX *mem_ctx, struct asn1_data *data, + struct ldb_message_element **attributes, + int *num_attributes) { - asn1_start_tag(data, ASN1_SEQUENCE(0)); while (asn1_peek_tag(data, ASN1_SEQUENCE(0))) { struct ldb_message_element attrib; ZERO_STRUCT(attrib); @@ -923,6 +923,16 @@ static void ldap_decode_attribs(TALLOC_CTX *mem_ctx, struct asn1_data *data, add_attrib_to_array_talloc(mem_ctx, &attrib, attributes, num_attributes); } +} + +/* Decode a set of LDAP attributes, as found in a search entry */ +void ldap_decode_attribs(TALLOC_CTX *mem_ctx, struct asn1_data *data, + struct ldb_message_element **attributes, + int *num_attributes) +{ + asn1_start_tag(data, ASN1_SEQUENCE(0)); + ldap_decode_attribs_bare(mem_ctx, data, + attributes, num_attributes); asn1_end_tag(data); } diff --git a/source4/libcli/ldap/ldap_client.c b/source4/libcli/ldap/ldap_client.c index 7f43e16c95..082f6fa3e4 100644 --- a/source4/libcli/ldap/ldap_client.c +++ b/source4/libcli/ldap/ldap_client.c @@ -358,7 +358,7 @@ _PUBLIC_ struct composite_context *ldap_connect_send(struct ldap_connection *con } ctx = socket_connect_send(conn->sock, NULL, unix_addr, - 0, lp_resolve_context(conn->lp_ctx), conn->event.event_ctx); + 0, conn->event.event_ctx); ctx->async.fn = ldap_connect_recv_unix_conn; ctx->async.private_data = state; return result; diff --git a/source4/libcli/ldap/ldap_controls.c b/source4/libcli/ldap/ldap_controls.c index 930d97c40d..109837c2bf 100644 --- a/source4/libcli/ldap/ldap_controls.c +++ b/source4/libcli/ldap/ldap_controls.c @@ -24,6 +24,7 @@ #include "libcli/ldap/ldap.h" #include "lib/ldb/include/ldb.h" #include "libcli/ldap/ldap_proto.h" +#include "dsdb/samdb/samdb.h" struct control_handler { const char *oid; @@ -1087,6 +1088,119 @@ static bool encode_vlv_response(void *mem_ctx, void *in, DATA_BLOB *out) return true; } +static bool encode_openldap_dereference(void *mem_ctx, void *in, DATA_BLOB *out) +{ + struct dsdb_openldap_dereference_control *control = talloc_get_type(in, struct dsdb_openldap_dereference_control); + int i,j; + struct asn1_data *data = asn1_init(mem_ctx); + + if (!data) return false; + + if (!control) return false; + + if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) { + return false; + } + + for (i=0; control->dereference && control->dereference[i]; i++) { + if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) { + return false; + } + if (!asn1_write_OctetString(data, control->dereference[i]->source_attribute, strlen(control->dereference[i]->source_attribute))) { + return false; + } + if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) { + return false; + } + for (j=0; control->dereference && control->dereference[i]->dereference_attribute[j]; j++) { + if (!asn1_write_OctetString(data, control->dereference[i]->dereference_attribute[j], + strlen(control->dereference[i]->dereference_attribute[j]))) { + return false; + } + } + + asn1_pop_tag(data); + asn1_pop_tag(data); + } + asn1_pop_tag(data); + + *out = data_blob_talloc(mem_ctx, data->data, data->length); + if (out->data == NULL) { + return false; + } + talloc_free(data); + return true; +} + +static bool decode_openldap_dereference(void *mem_ctx, DATA_BLOB in, void **out) +{ + struct asn1_data *data = asn1_init(mem_ctx); + struct dsdb_openldap_dereference_result_control *control; + struct dsdb_openldap_dereference_result **r = NULL; + int i = 0; + if (!data) return false; + + control = talloc(mem_ctx, struct dsdb_openldap_dereference_result_control); + if (!control) return false; + + if (!asn1_load(data, in)) { + return false; + } + + control = talloc(mem_ctx, struct dsdb_openldap_dereference_result_control); + if (!control) { + return false; + } + + if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) { + return false; + } + + while (asn1_tag_remaining(data) > 0) { + r = talloc_realloc(control, r, struct dsdb_openldap_dereference_result *, i + 2); + if (!r) { + return false; + } + r[i] = talloc_zero(r, struct dsdb_openldap_dereference_result); + if (!r[i]) { + return false; + } + + if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) { + return false; + } + + asn1_read_OctetString_talloc(r[i], data, &r[i]->source_attribute); + asn1_read_OctetString_talloc(r[i], data, &r[i]->dereferenced_dn); + if (asn1_peek_tag(data, ASN1_CONTEXT(0))) { + if (!asn1_start_tag(data, ASN1_CONTEXT(0))) { + return false; + } + + ldap_decode_attribs_bare(r, data, &r[i]->attributes, + &r[i]->num_attributes); + + if (!asn1_end_tag(data)) { + return false; + } + } + if (!asn1_end_tag(data)) { + return false; + } + i++; + r[i] = NULL; + } + + if (!asn1_end_tag(data)) { + return false; + } + + control->attributes = r; + *out = control; + + return true; +} + struct control_handler ldap_known_controls[] = { { "1.2.840.113556.1.4.319", decode_paged_results_request, encode_paged_results_request }, { "1.2.840.113556.1.4.529", decode_extended_dn_request, encode_extended_dn_request }, @@ -1107,6 +1221,7 @@ struct control_handler ldap_known_controls[] = { { "1.3.6.1.4.1.7165.4.3.2", NULL, NULL }, /* DSDB_EXTENDED_REPLICATED_OBJECTS_OID is internal only, and has no network representation */ { "1.3.6.1.4.1.7165.4.4.1", NULL, NULL }, + { DSDB_OPENLDAP_DEREFERENCE_CONTROL, decode_openldap_dereference, encode_openldap_dereference}, { NULL, NULL, NULL } }; diff --git a/source4/libcli/ldap/ldap_ndr.c b/source4/libcli/ldap/ldap_ndr.c index 5e938ea148..f0a11ba41f 100644 --- a/source4/libcli/ldap/ldap_ndr.c +++ b/source4/libcli/ldap/ldap_ndr.c @@ -21,7 +21,6 @@ */ #include "includes.h" -#include "lib/events/events.h" #include "libcli/ldap/ldap.h" #include "librpc/gen_ndr/ndr_security.h" #include "librpc/gen_ndr/ndr_misc.h" diff --git a/source4/libcli/resolve/bcast.c b/source4/libcli/resolve/bcast.c index 0a71ebed99..866ce7a152 100644 --- a/source4/libcli/resolve/bcast.c +++ b/source4/libcli/resolve/bcast.c @@ -37,7 +37,8 @@ struct resolve_bcast_data { */ struct composite_context *resolve_name_bcast_send(TALLOC_CTX *mem_ctx, struct event_context *event_ctx, - void *userdata, + void *userdata, uint32_t flags, + uint16_t port, struct nbt_name *name) { int num_interfaces; @@ -63,7 +64,9 @@ struct composite_context *resolve_name_bcast_send(TALLOC_CTX *mem_ctx, } address_list[count] = NULL; - c = resolve_name_nbtlist_send(mem_ctx, event_ctx, name, address_list, data->ifaces, data->nbt_port, data->nbt_timeout, true, false); + 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; @@ -74,9 +77,10 @@ struct composite_context *resolve_name_bcast_send(TALLOC_CTX *mem_ctx, */ NTSTATUS resolve_name_bcast_recv(struct composite_context *c, TALLOC_CTX *mem_ctx, - struct socket_address ***addrs) + struct socket_address ***addrs, + char ***names) { - NTSTATUS status = resolve_name_nbtlist_recv(c, mem_ctx, addrs); + 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 */ @@ -85,26 +89,6 @@ NTSTATUS resolve_name_bcast_recv(struct composite_context *c, return status; } -/* - broadcast name resolution method - sync call - */ -NTSTATUS resolve_name_bcast(struct nbt_name *name, - TALLOC_CTX *mem_ctx, - struct interface *ifaces, - uint16_t nbt_port, - int nbt_timeout, - struct socket_address ***addrs) -{ - struct resolve_bcast_data *data = talloc(mem_ctx, struct resolve_bcast_data); - struct composite_context *c; - data->ifaces = talloc_reference(data, ifaces); - data->nbt_port = nbt_port; - data->nbt_timeout = nbt_timeout; - - c = resolve_name_bcast_send(mem_ctx, NULL, data, name); - return resolve_name_bcast_recv(c, mem_ctx, addrs); -} - 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); diff --git a/source4/libcli/resolve/dns_ex.c b/source4/libcli/resolve/dns_ex.c new file mode 100644 index 0000000000..948ad9f134 --- /dev/null +++ b/source4/libcli/resolve/dns_ex.c @@ -0,0 +1,532 @@ +/* + Unix SMB/CIFS implementation. + + async getaddrinfo()/dns_lookup() 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/>. +*/ + +/* + 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 "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" +#include "heimdal/lib/roken/resolve.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 fd_event *fde; + struct event_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); + close(state->child_fd); + if (waitpid(state->child, &status, WNOHANG) == 0) { + kill(state->child, SIGKILL); + waitpid(state->child, &status, 0); + } + + return 0; +} + +/* + the blocking child +*/ +static void run_child_dns_lookup(struct dns_ex_state *state, int fd) +{ + struct dns_reply *reply; + struct resource_record *rr; + uint32_t count = 0; + uint32_t srv_valid = 0; + struct resource_record **srv_rr; + uint32_t addrs_valid = 0; + struct resource_record **addrs_rr; + char *addrs; + bool first; + uint32_t i; + bool do_srv = (state->flags & RESOLVE_NAME_FLAG_DNS_SRV); + + /* this is the blocking call we are going to lots of trouble + to avoid in the parent */ + reply = dns_lookup(state->name.name, do_srv?"SRV":"A"); + if (!reply) { + goto done; + } + + if (do_srv) { + dns_srv_order(reply); + } + + /* Loop over all returned records and pick the "srv" records */ + for (rr=reply->head; rr; rr=rr->next) { + /* we are only interested in the IN class */ + if (rr->class != C_IN) { + continue; + } + + if (do_srv) { + /* we are only interested in SRV records */ + if (rr->type != T_SRV) { + continue; + } + + /* verify we actually have a SRV record here */ + if (!rr->u.srv) { + continue; + } + + /* Verify we got a port */ + if (rr->u.srv->port == 0) { + continue; + } + } else { + /* we are only interested in A records */ + /* TODO: add AAAA support */ + if (rr->type != T_A) { + continue; + } + + /* verify we actually have a A record here */ + if (!rr->u.a) { + continue; + } + } + count++; + } + + if (count == 0) { + goto done; + } + + srv_rr = talloc_zero_array(state, + struct resource_record *, + count); + if (!srv_rr) { + goto done; + } + + addrs_rr = talloc_zero_array(state, + struct resource_record *, + count); + if (!addrs_rr) { + goto done; + } + + /* Loop over all returned records and pick the records */ + for (rr=reply->head;rr;rr=rr->next) { + /* we are only interested in the IN class */ + if (rr->class != C_IN) { + continue; + } + + if (do_srv) { + /* we are only interested in SRV records */ + if (rr->type != T_SRV) { + continue; + } + + /* verify we actually have a srv record here */ + if (!rr->u.srv) { + continue; + } + + /* Verify we got a port */ + if (rr->u.srv->port == 0) { + continue; + } + + srv_rr[srv_valid] = rr; + srv_valid++; + } else { + /* we are only interested in A records */ + /* TODO: add AAAA support */ + if (rr->type != T_A) { + continue; + } + + /* verify we actually have a A record here */ + if (!rr->u.a) { + continue; + } + + addrs_rr[addrs_valid] = rr; + addrs_valid++; + } + } + + for (i=0; i < srv_valid; i++) { + for (rr=reply->head;rr;rr=rr->next) { + + if (rr->class != C_IN) { + continue; + } + + /* we are only interested in SRV records */ + if (rr->type != T_A) { + continue; + } + + /* verify we actually have a srv record here */ + if (strcmp(&srv_rr[i]->u.srv->target[0], rr->domain) != 0) { + continue; + } + + addrs_rr[i] = rr; + addrs_valid++; + break; + } + } + + if (addrs_valid == 0) { + goto done; + } + + addrs = talloc_strdup(state, ""); + if (!addrs) { + goto done; + } + first = true; + for (i=0; i < count; i++) { + uint16_t port; + if (!addrs_rr[i]) { + continue; + } + + if (srv_rr[i] && + (state->flags & RESOLVE_NAME_FLAG_OVERWRITE_PORT)) { + port = srv_rr[i]->u.srv->port; + } else { + port = state->port; + } + + addrs = talloc_asprintf_append_buffer(addrs, "%s%s:%u/%s", + first?"":",", + inet_ntoa(*addrs_rr[i]->u.a), + port, + addrs_rr[i]->domain); + if (!addrs) { + goto done; + } + first = false; + } + + if (addrs) { + write(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_family = AF_INET;/* TODO: add AF_INET6 support */ + hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV; + + ret = getaddrinfo(state->name.name, "0", &hints, &res_list); + if (ret == EAI_NODATA && state->do_fallback) { + /* getaddrinfo() doesn't handle CNAME records */ + run_child_dns_lookup(state, fd); + return; + } + if (ret != 0) { + goto done; + } + + addrs = talloc_strdup(state, ""); + if (!addrs) { + goto done; + } + first = true; + for (res = res_list; res; res = res->ai_next) { + struct sockaddr_in *in; + + if (res->ai_family != AF_INET) { + continue; + } + in = (struct sockaddr_in *)res->ai_addr; + + addrs = talloc_asprintf_append_buffer(addrs, "%s%s:%u/%s", + first?"":",", + inet_ntoa(in->sin_addr), + state->port, + state->name.name); + if (!addrs) { + goto done; + } + first = false; + } + + if (addrs) { + write(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 event_context *ev, struct fd_event *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; + } + close(state->child_fd); + if (waitpid(state->child, &status, WNOHANG) == 0) { + kill(state->child, SIGKILL); + waitpid(state->child, &status, 0); + } + + if (ret <= 0) { + composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND); + return; + } + + /* enusre 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; + + 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 || + inet_addr(addrs[i]) == INADDR_NONE) { + composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND); + return; + } + port = strtoul(p, NULL, 10); + if (port > UINT16_MAX) { + composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND); + return; + } + state->addrs[i] = socket_address_from_strings(state->addrs, + "ipv4", + 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 event_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(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 = event_add_fd(c->event_ctx, c, state->child_fd, EVENT_FD_READ, + pipe_handler, c); + if (composite_nomem(state->fde, c)) { + close(fd[0]); + close(fd[1]); + return c; + } + + state->child = fork(); + if (state->child == (pid_t)-1) { + composite_error(c, map_nt_error_from_unix(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 index 7b1aef803e..b7eaf4bef5 100644 --- a/source4/libcli/resolve/host.c +++ b/source4/libcli/resolve/host.c @@ -1,33 +1,25 @@ /* Unix SMB/CIFS implementation. - async gethostbyname() name resolution module + 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/>. */ -/* - this module uses a fork() per gethostbyname() 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 library - (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 "lib/events/events.h" #include "system/network.h" @@ -37,201 +29,28 @@ #include "librpc/gen_ndr/ndr_nbt.h" #include "libcli/resolve/resolve.h" -struct host_state { - struct nbt_name name; - struct socket_address **addrs; - pid_t child; - int child_fd; - struct fd_event *fde; - struct event_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 host_destructor(struct host_state *state) -{ - int status; - - kill(state->child, SIGTERM); - close(state->child_fd); - if (waitpid(state->child, &status, WNOHANG) == 0) { - kill(state->child, SIGKILL); - waitpid(state->child, &status, 0); - } - - return 0; -} - -/* - the blocking child -*/ -static void run_child(struct composite_context *c, int fd) -{ - struct host_state *state = talloc_get_type(c->private_data, struct host_state); - struct in_addr ip; - const char *address; - - /* this is the blocking call we are going to lots of trouble - to avoid in the parent */ - ip = interpret_addr2(state->name.name); - - address = inet_ntoa(ip); - if (address != NULL) { - write(fd, address, strlen(address)+1); - } - close(fd); -} - -/* - handle a read event on the pipe -*/ -static void pipe_handler(struct event_context *ev, struct fd_event *fde, - uint16_t flags, void *private_data) -{ - struct composite_context *c = talloc_get_type(private_data, struct composite_context); - struct host_state *state = talloc_get_type(c->private_data, struct host_state); - char address[128]; - int ret; - int status; - - /* 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); - - /* 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, sizeof(address)-1); - close(state->child_fd); - if (waitpid(state->child, &status, WNOHANG) == 0) { - kill(state->child, SIGKILL); - waitpid(state->child, &status, 0); - } - if (ret <= 0) { - composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND); - return; - } - - /* enusre the address looks good */ - address[ret] = 0; - if (strcmp(address, "0.0.0.0") == 0 || - inet_addr(address) == INADDR_NONE) { - composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND); - return; - } - - state->addrs = talloc_array(state, struct socket_address *, 2); - if (composite_nomem(state->addrs, c)) return; - - state->addrs[0] = socket_address_from_strings(state->addrs, - "ipv4", - address, - 0); - if (composite_nomem(state->addrs[0], c)) return; - state->addrs[1] = NULL; - - composite_done(c); -} - -/* - gethostbyname name resolution method - async send + getaddrinfo() (with fallback to dns_lookup()) name resolution method - async send */ struct composite_context *resolve_name_host_send(TALLOC_CTX *mem_ctx, struct event_context *event_ctx, - void *privdata, + void *privdata, uint32_t flags, + uint16_t port, struct nbt_name *name) { - struct composite_context *c; - struct host_state *state; - int fd[2] = { -1, -1 }; - int ret; - - 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 host_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(errno)); - return c; - } - - 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 gethostbyname() has finished */ - state->fde = event_add_fd(c->event_ctx, c, state->child_fd, EVENT_FD_READ, - pipe_handler, c); - if (composite_nomem(state->fde, c)) { - close(fd[0]); - close(fd[1]); - return c; - } - - state->child = fork(); - if (state->child == (pid_t)-1) { - composite_error(c, map_nt_error_from_unix(errno)); - return c; - } - - - if (state->child == 0) { - close(fd[0]); - run_child(c, fd[1]); - _exit(0); - } - close(fd[1]); - - /* cleanup wayward children */ - talloc_set_destructor(state, host_destructor); - - return c; + return resolve_name_dns_ex_send(mem_ctx, event_ctx, NULL, flags, + port, name, true); } /* - gethostbyname name resolution method - recv side + 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) -{ - NTSTATUS status; - - status = composite_wait(c); - - if (NT_STATUS_IS_OK(status)) { - struct host_state *state = talloc_get_type(c->private_data, struct host_state); - *addrs = talloc_steal(mem_ctx, state->addrs); - } - - talloc_free(c); - return status; -} - -/* - gethostbyname name resolution method - sync call - */ -NTSTATUS resolve_name_host(struct nbt_name *name, - TALLOC_CTX *mem_ctx, - struct socket_address ***addrs) + struct socket_address ***addrs, + char ***names) { - struct composite_context *c = resolve_name_host_send(mem_ctx, NULL, NULL, name); - return resolve_name_host_recv(c, mem_ctx, addrs); + return resolve_name_dns_ex_recv(c, mem_ctx, addrs, names); } bool resolve_context_add_host_method(struct resolve_context *ctx) diff --git a/source4/libcli/resolve/nbtlist.c b/source4/libcli/resolve/nbtlist.c index 9c53fcb7ec..734fd5a5bc 100644 --- a/source4/libcli/resolve/nbtlist.c +++ b/source4/libcli/resolve/nbtlist.c @@ -34,12 +34,15 @@ #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; }; @@ -81,14 +84,21 @@ static void nbtlist_handler(struct nbt_name_request *req) 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], - 0); + 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); } @@ -98,6 +108,8 @@ static void nbtlist_handler(struct nbt_name_request *req) */ struct composite_context *resolve_name_nbtlist_send(TALLOC_CTX *mem_ctx, struct event_context *event_ctx, + uint32_t flags, + uint16_t port, struct nbt_name *name, const char **address_list, struct interface *ifaces, @@ -113,12 +125,23 @@ struct composite_context *resolve_name_nbtlist_send(TALLOC_CTX *mem_ctx, c = composite_create(mem_ctx, event_ctx); if (c == NULL) return NULL; - if (composite_nomem(c->event_ctx, c)) return c; + 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; @@ -180,7 +203,8 @@ struct composite_context *resolve_name_nbtlist_send(TALLOC_CTX *mem_ctx, */ NTSTATUS resolve_name_nbtlist_recv(struct composite_context *c, TALLOC_CTX *mem_ctx, - struct socket_address ***addrs) + struct socket_address ***addrs, + char ***names) { NTSTATUS status; @@ -189,29 +213,12 @@ NTSTATUS resolve_name_nbtlist_recv(struct composite_context *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; } -/* - nbt list of addresses name resolution method - sync call - */ -NTSTATUS resolve_name_nbtlist(struct nbt_name *name, - TALLOC_CTX *mem_ctx, - const char **address_list, - struct interface *ifaces, - uint16_t nbt_port, - int nbt_timeout, - bool broadcast, bool wins_lookup, - struct socket_address ***addrs) -{ - struct composite_context *c = resolve_name_nbtlist_send(mem_ctx, NULL, - name, address_list, - ifaces, nbt_port, - nbt_timeout, - broadcast, wins_lookup); - return resolve_name_nbtlist_recv(c, mem_ctx, addrs); -} - diff --git a/source4/libcli/resolve/resolve.c b/source4/libcli/resolve/resolve.c index 7d1c48cbee..752678abb8 100644 --- a/source4/libcli/resolve/resolve.c +++ b/source4/libcli/resolve/resolve.c @@ -21,7 +21,6 @@ */ #include "includes.h" -#include "lib/events/events.h" #include "libcli/composite/composite.h" #include "libcli/resolve/resolve.h" #include "librpc/gen_ndr/ndr_nbt.h" @@ -32,9 +31,12 @@ 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); @@ -84,7 +86,7 @@ static void resolve_handler(struct composite_context *creq) 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); + c->status = method->recv_fn(creq, state, &state->addrs, &state->names); if (!NT_STATUS_IS_OK(c->status)) { state->method = state->method->next; @@ -112,7 +114,11 @@ static struct composite_context *setup_next_method(struct composite_context *c) do { if (state->method) { - creq = state->method->send_fn(c, c->event_ctx, state->method->privdata, &state->name); + 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; @@ -130,6 +136,8 @@ static struct composite_context *setup_next_method(struct composite_context *c) general name resolution - async send */ struct composite_context *resolve_name_all_send(struct resolve_context *ctx, + uint32_t flags, + uint16_t port, struct nbt_name *name, struct event_context *event_ctx) { @@ -149,6 +157,9 @@ struct composite_context *resolve_name_all_send(struct resolve_context *ctx, 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; @@ -165,6 +176,11 @@ struct composite_context *resolve_name_all_send(struct resolve_context *ctx, inet_ntoa(ip), 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; } @@ -185,7 +201,8 @@ struct composite_context *resolve_name_all_send(struct resolve_context *ctx, */ NTSTATUS resolve_name_all_recv(struct composite_context *c, TALLOC_CTX *mem_ctx, - struct socket_address ***addrs) + struct socket_address ***addrs, + char ***names) { NTSTATUS status; @@ -194,30 +211,20 @@ NTSTATUS resolve_name_all_recv(struct composite_context *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; } -/* - general name resolution - sync call - */ -NTSTATUS resolve_all_name(struct resolve_context *ctx, - struct nbt_name *name, - TALLOC_CTX *mem_ctx, - struct socket_address ***addrs, - struct event_context *ev) -{ - struct composite_context *c = resolve_name_all_send(ctx, name, ev); - return resolve_name_all_recv(c, mem_ctx, addrs); -} - struct composite_context *resolve_name_send(struct resolve_context *ctx, struct nbt_name *name, struct event_context *event_ctx) { - return resolve_name_all_send(ctx, name, event_ctx); + return resolve_name_all_send(ctx, 0, 0, name, event_ctx); } NTSTATUS resolve_name_recv(struct composite_context *c, @@ -227,7 +234,7 @@ NTSTATUS resolve_name_recv(struct composite_context *c, NTSTATUS status; struct socket_address **addrs = NULL; - status = resolve_name_all_recv(c, mem_ctx, &addrs); + status = resolve_name_all_recv(c, mem_ctx, &addrs, NULL); if (NT_STATUS_IS_OK(status)) { *reply_addr = talloc_steal(mem_ctx, addrs[0]->addr); diff --git a/source4/libcli/resolve/resolve.h b/source4/libcli/resolve/resolve.h index 01fc930fce..b55ab83f2a 100644 --- a/source4/libcli/resolve/resolve.h +++ b/source4/libcli/resolve/resolve.h @@ -19,21 +19,35 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef __RESOLVE_H__ -#define __RESOLVE_H__ +#ifndef __LIBCLI_RESOLVE_H__ +#define __LIBCLI_RESOLVE_H__ struct socket_address; +struct event_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 event_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); + struct socket_address ***addrs, + char ***names); #include "libcli/resolve/proto.h" struct interface; #include "libcli/resolve/lp_proto.h" -#endif /* __RESOLVE_H__ */ +#endif /* __LIBCLI_RESOLVE_H__ */ diff --git a/source4/libcli/resolve/testsuite.c b/source4/libcli/resolve/testsuite.c index 34de1158a5..f1d1fbc85c 100644 --- a/source4/libcli/resolve/testsuite.c +++ b/source4/libcli/resolve/testsuite.c @@ -44,9 +44,9 @@ static bool test_async_resolve(struct torture_context *tctx) host, timelimit); while (timeval_elapsed(&tv) < timelimit) { struct socket_address **s; - struct composite_context *c = resolve_name_host_send(mem_ctx, ev, NULL, &n); + 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), + torture_assert_ntstatus_ok(tctx, resolve_name_host_recv(c, mem_ctx, &s, NULL), "async resolve failed"); count++; } diff --git a/source4/libcli/resolve/wins.c b/source4/libcli/resolve/wins.c index ae142f7054..1940688ecb 100644 --- a/source4/libcli/resolve/wins.c +++ b/source4/libcli/resolve/wins.c @@ -40,11 +40,16 @@ struct composite_context *resolve_name_wins_send( TALLOC_CTX *mem_ctx, struct event_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, name, wins_data->address_list, wins_data->ifaces, wins_data->nbt_port, wins_data->nbt_timeout, false, true); + return resolve_name_nbtlist_send(mem_ctx, event_ctx, flags, port, name, + wins_data->address_list, wins_data->ifaces, + wins_data->nbt_port, wins_data->nbt_timeout, + false, true); } /* @@ -52,30 +57,10 @@ struct composite_context *resolve_name_wins_send( */ NTSTATUS resolve_name_wins_recv(struct composite_context *c, TALLOC_CTX *mem_ctx, - struct socket_address ***addrs) + struct socket_address ***addrs, + char ***names) { - return resolve_name_nbtlist_recv(c, mem_ctx, addrs); -} - -/* - wins name resolution method - sync call - */ -NTSTATUS resolve_name_wins(struct nbt_name *name, - TALLOC_CTX *mem_ctx, - const char **address_list, - struct interface *ifaces, - uint16_t nbt_port, - int nbt_timeout, - struct socket_address ***addrs) -{ - struct composite_context *c; - struct resolve_wins_data *wins_data = talloc(mem_ctx, struct resolve_wins_data); - wins_data->address_list = address_list; - wins_data->ifaces = ifaces; - wins_data->nbt_port = nbt_port; - wins_data->nbt_timeout = nbt_timeout; - c = resolve_name_wins_send(mem_ctx, NULL, wins_data, name); - return resolve_name_wins_recv(c, mem_ctx, addrs); + 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) diff --git a/source4/libcli/wrepl/winsrepl.c b/source4/libcli/wrepl/winsrepl.c index 84bda8c858..930616d211 100644 --- a/source4/libcli/wrepl/winsrepl.c +++ b/source4/libcli/wrepl/winsrepl.c @@ -30,7 +30,6 @@ #include "system/network.h" #include "lib/socket/netif.h" #include "param/param.h" -#include "libcli/resolve/resolve.h" static struct wrepl_request *wrepl_request_finished(struct wrepl_request *req, NTSTATUS status); @@ -319,7 +318,6 @@ const char *wrepl_best_ip(struct loadparm_context *lp_ctx, const char *peer_ip) connect a wrepl_socket to a WINS server */ struct composite_context *wrepl_connect_send(struct wrepl_socket *wrepl_socket, - struct resolve_context *resolve_ctx, const char *our_ip, const char *peer_ip) { struct composite_context *result; @@ -347,8 +345,7 @@ struct composite_context *wrepl_connect_send(struct wrepl_socket *wrepl_socket, if (composite_nomem(peer, result)) return result; state->creq = socket_connect_send(wrepl_socket->sock, us, peer, - 0, resolve_ctx, - wrepl_socket->event.ctx); + 0, wrepl_socket->event.ctx); composite_continue(result, state->creq, wrepl_connect_handler, state); return result; } @@ -374,10 +371,10 @@ NTSTATUS wrepl_connect_recv(struct composite_context *result) /* connect a wrepl_socket to a WINS server - sync API */ -NTSTATUS wrepl_connect(struct wrepl_socket *wrepl_socket, struct resolve_context *resolve_ctx, +NTSTATUS wrepl_connect(struct wrepl_socket *wrepl_socket, const char *our_ip, const char *peer_ip) { - struct composite_context *c_req = wrepl_connect_send(wrepl_socket, resolve_ctx, our_ip, peer_ip); + struct composite_context *c_req = wrepl_connect_send(wrepl_socket, our_ip, peer_ip); return wrepl_connect_recv(c_req); } |