#include "../lib/tsocket/tsocket_internal.h"
#include "../librpc/gen_ndr/ndr_named_pipe_auth.h"
#include "../libcli/named_pipe_auth/npa_tstream.h"
-#include "libcli/raw/smb.h"
+#include "../libcli/named_pipe_auth/tstream_u32_read.h"
+#include "../libcli/smb/smb_constants.h"
static const struct tstream_context_ops tstream_npa_ops;
struct tstream_npa_connect_state {
struct {
struct tevent_context *ev;
- struct smb_iconv_convenience *smb_iconv_c;
} caller;
const char *unix_path;
struct iovec auth_req_iov;
struct named_pipe_auth_rep auth_rep;
- DATA_BLOB auth_rep_blob;
};
static void tstream_npa_connect_unix_done(struct tevent_req *subreq);
struct tevent_req *tstream_npa_connect_send(TALLOC_CTX *mem_ctx,
- struct tevent_context *ev,
- struct smb_iconv_convenience *smb_iconv_c,
- const char *directory,
- const char *npipe,
- const struct tsocket_address *client,
- const char *client_name_in,
- const struct tsocket_address *server,
- const char *server_name,
- const struct netr_SamInfo3 *info3,
- DATA_BLOB session_key)
+ struct tevent_context *ev,
+ const char *directory,
+ const char *npipe,
+ const struct tsocket_address *remote_client_addr,
+ const char *remote_client_name_in,
+ const struct tsocket_address *local_server_addr,
+ const char *local_server_name,
+ const struct auth_session_info_transport *session_info)
{
struct tevent_req *req;
struct tstream_npa_connect_state *state;
struct tevent_req *subreq;
int ret;
enum ndr_err_code ndr_err;
+ char *lower_case_npipe;
+ struct named_pipe_auth_req_info4 *info4;
req = tevent_req_create(mem_ctx, &state,
struct tstream_npa_connect_state);
}
state->caller.ev = ev;
- state->caller.smb_iconv_c = smb_iconv_c;
+
+ lower_case_npipe = strlower_talloc(state, npipe);
+ if (tevent_req_nomem(lower_case_npipe, req)) {
+ goto post;
+ }
state->unix_path = talloc_asprintf(state, "%s/%s",
directory,
- npipe);
+ lower_case_npipe);
+ talloc_free(lower_case_npipe);
if (tevent_req_nomem(state->unix_path, req)) {
goto post;
}
}
ZERO_STRUCT(state->auth_req);
- if (client) {
- struct named_pipe_auth_req_info2 *info2;
- if (!server) {
- tevent_req_error(req, EINVAL);
- goto post;
- }
-
- state->auth_req.level = 2;
- info2 = &state->auth_req.info.info2;
+ if (!local_server_addr) {
+ tevent_req_error(req, EINVAL);
+ goto post;
+ }
- info2->client_name = client_name_in;
- info2->client_addr = tsocket_address_inet_addr_string(client, state);
- if (!info2->client_addr) {
- /* errno might be EINVAL */
- tevent_req_error(req, errno);
- goto post;
- }
- info2->client_port = tsocket_address_inet_port(client);
- if (!info2->client_name) {
- info2->client_name = info2->client_addr;
- }
+ state->auth_req.level = 4;
+ info4 = &state->auth_req.info.info4;
- info2->server_addr = tsocket_address_inet_addr_string(server, state);
- if (!info2->server_addr) {
- /* errno might be EINVAL */
- tevent_req_error(req, errno);
- goto post;
- }
- info2->server_port = tsocket_address_inet_port(server);
- if (!info2->server_name) {
- info2->server_name = info2->server_addr;
- }
+ info4->remote_client_name = remote_client_name_in;
+ info4->remote_client_addr = tsocket_address_inet_addr_string(remote_client_addr,
+ state);
+ if (!info4->remote_client_addr) {
+ /* errno might be EINVAL */
+ tevent_req_error(req, errno);
+ goto post;
+ }
+ info4->remote_client_port = tsocket_address_inet_port(remote_client_addr);
+ if (!info4->remote_client_name) {
+ info4->remote_client_name = info4->remote_client_addr;
+ }
- info2->sam_info3 = discard_const_p(struct netr_SamInfo3, info3);
- info2->session_key_length = session_key.length;
- info2->session_key = session_key.data;
- } else if (info3) {
- state->auth_req.level = 1;
- state->auth_req.info.info1 = *info3;
- } else {
- state->auth_req.level = 0;
+ info4->local_server_addr = tsocket_address_inet_addr_string(local_server_addr,
+ state);
+ if (!info4->local_server_addr) {
+ /* errno might be EINVAL */
+ tevent_req_error(req, errno);
+ goto post;
}
+ info4->local_server_port = tsocket_address_inet_port(local_server_addr);
+ if (!info4->local_server_name) {
+ info4->local_server_name = info4->local_server_addr;
+ }
+
+ info4->session_info = discard_const_p(struct auth_session_info_transport, session_info);
if (DEBUGLVL(10)) {
NDR_PRINT_DEBUG(named_pipe_auth_req, &state->auth_req);
}
ndr_err = ndr_push_struct_blob(&state->auth_req_blob,
- state, smb_iconv_c, &state->auth_req,
+ state, &state->auth_req,
(ndr_push_flags_fn_t)ndr_push_named_pipe_auth_req);
if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
tevent_req_error(req, EINVAL);
goto post;
}
- state->auth_req_iov.iov_base = state->auth_req_blob.data;
+ state->auth_req_iov.iov_base = (char *) state->auth_req_blob.data;
state->auth_req_iov.iov_len = state->auth_req_blob.length;
subreq = tstream_unix_connect_send(state,
tevent_req_set_callback(subreq, tstream_npa_connect_writev_done, req);
}
-static int tstream_npa_connect_next_vector(struct tstream_context *unix_stream,
- void *private_data,
- TALLOC_CTX *mem_ctx,
- struct iovec **_vector,
- size_t *_count);
static void tstream_npa_connect_readv_done(struct tevent_req *subreq);
static void tstream_npa_connect_writev_done(struct tevent_req *subreq)
return;
}
- state->auth_rep_blob = data_blob_const(NULL, 0);
-
- subreq = tstream_readv_pdu_send(state, state->caller.ev,
- state->unix_stream,
- tstream_npa_connect_next_vector,
- state);
- if (tevent_req_nomem(subreq, req)) {
- return;
- }
+ subreq = tstream_u32_read_send(
+ state, state->caller.ev, 0x00FFFFFF, state->unix_stream);
tevent_req_set_callback(subreq, tstream_npa_connect_readv_done, req);
}
-static int tstream_npa_connect_next_vector(struct tstream_context *unix_stream,
- void *private_data,
- TALLOC_CTX *mem_ctx,
- struct iovec **_vector,
- size_t *_count)
-{
- struct tstream_npa_connect_state *state = talloc_get_type_abort(private_data,
- struct tstream_npa_connect_state);
- struct iovec *vector;
- size_t count;
- off_t ofs = 0;
-
- if (state->auth_rep_blob.length == 0) {
- state->auth_rep_blob = data_blob_talloc(state, NULL, 4);
- if (!state->auth_rep_blob.data) {
- return -1;
- }
- } else if (state->auth_rep_blob.length == 4) {
- uint32_t msg_len;
-
- ofs = 4;
-
- msg_len = RIVAL(state->auth_rep_blob.data, 0);
-
- if (msg_len > 0x00FFFFFF) {
- errno = EMSGSIZE;
- return -1;
- }
-
- if (msg_len == 0) {
- errno = EMSGSIZE;
- return -1;
- }
-
- msg_len += ofs;
-
- state->auth_rep_blob.data = talloc_realloc(state,
- state->auth_rep_blob.data,
- uint8_t, msg_len);
- if (!state->auth_rep_blob.data) {
- return -1;
- }
- state->auth_rep_blob.length = msg_len;
- } else {
- *_vector = NULL;
- *_count = 0;
- return 0;
- }
-
- /* we need to get a message header */
- vector = talloc_array(mem_ctx, struct iovec, 1);
- if (!vector) {
- return -1;
- }
- vector[0].iov_base = state->auth_rep_blob.data + ofs;
- vector[0].iov_len = state->auth_rep_blob.length - ofs;
- count = 1;
-
- *_vector = vector;
- *_count = count;
- return 0;
-}
-
static void tstream_npa_connect_readv_done(struct tevent_req *subreq)
{
struct tevent_req *req =
struct tstream_npa_connect_state *state =
tevent_req_data(req,
struct tstream_npa_connect_state);
- int ret;
- int sys_errno;
+ DATA_BLOB in;
+ int err;
enum ndr_err_code ndr_err;
- ret = tstream_readv_pdu_recv(subreq, &sys_errno);
+ err = tstream_u32_read_recv(subreq, state, &in.data, &in.length);
TALLOC_FREE(subreq);
- if (ret == -1) {
- tevent_req_error(req, sys_errno);
+ if (err != 0) {
+ tevent_req_error(req, err);
return;
}
- DEBUG(10,("name_pipe_auth_rep(client)[%u]\n",
- (uint32_t)state->auth_rep_blob.length));
- dump_data(11, state->auth_rep_blob.data, state->auth_rep_blob.length);
+ DBG_DEBUG("name_pipe_auth_rep(client)[%zu]\n", in.length);
+ dump_data(11, in.data, in.length);
- ndr_err = ndr_pull_struct_blob(
- &state->auth_rep_blob, state,
- state->caller.smb_iconv_c, &state->auth_rep,
+ ndr_err = ndr_pull_struct_blob_all(
+ &in,
+ state,
+ &state->auth_rep,
(ndr_pull_flags_fn_t)ndr_pull_named_pipe_auth_rep);
if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
struct tstream_npa,
location);
if (!stream) {
+ *perrno = ENOMEM;
+ tevent_req_received(req);
return -1;
}
ZERO_STRUCTP(npas);
npas->unix_stream = talloc_move(stream, &state->unix_stream);
switch (state->auth_rep.level) {
- case 0:
- case 1:
- npas->file_type = FILE_TYPE_BYTE_MODE_PIPE;
- device_state = 0x00ff;
- allocation_size = 2048;
- break;
- case 2:
- npas->file_type = state->auth_rep.info.info2.file_type;
- device_state = state->auth_rep.info.info2.device_state;
- allocation_size = state->auth_rep.info.info2.allocation_size;
+ case 4:
+ npas->file_type = state->auth_rep.info.info4.file_type;
+ device_state = state->auth_rep.info.info4.device_state;
+ allocation_size = state->auth_rep.info.info4.allocation_size;
break;
}
case FILE_TYPE_MESSAGE_MODE_PIPE:
ret = npas->pending.iov_len;
break;
+
+ default:
+ ret = -1;
}
return ret;
memcpy(base, pbase + ofs, left);
base += left;
- state->vector[0].iov_base = base;
+ state->vector[0].iov_base = (char *) base;
state->vector[0].iov_len -= left;
ofs += left;
if (left > 0) {
memmove(pbase, pbase + ofs, left);
- npas->pending.iov_base = pbase;
+ npas->pending.iov_base = (char *) pbase;
npas->pending.iov_len = left;
/*
* this cannot fail and even if it
*/
pbase = talloc_realloc(npas, pbase, uint8_t, left);
if (pbase) {
- npas->pending.iov_base = pbase;
+ npas->pending.iov_base = (char *) pbase;
}
pbase = NULL;
}
return -1;
}
ZERO_STRUCT(state->hdr);
- vector[0].iov_base = state->hdr;
+ vector[0].iov_base = (char *) state->hdr;
vector[0].iov_len = sizeof(state->hdr);
count = 1;
if (left < state->vector[0].iov_len) {
uint8_t *base;
base = (uint8_t *)state->vector[0].iov_base;
- vector[count].iov_base = base;
+ vector[count].iov_base = (char *) base;
vector[count].iov_len = left;
count++;
base += left;
- state->vector[0].iov_base = base;
+ state->vector[0].iov_base = (char *) base;
state->vector[0].iov_len -= left;
break;
}
if (left > 0) {
/*
- * if the message if longer than the buffers the caller
+ * if the message is longer than the buffers the caller
* requested, we need to consume the rest of the message
* into the pending buffer, where the next readv can
* be served from.
*/
- npas->pending.iov_base = talloc_array(npas, uint8_t, left);
+ npas->pending.iov_base = talloc_array(npas, char, left);
if (!npas->pending.iov_base) {
return -1;
}
}
/*
- * we do not set state->ret here as ret includes the headr size.
+ * we do not set state->ret here as ret includes the header size.
* we set it in tstream_npa_readv_pdu_next_vector()
*/
size_t count;
/* the header for message mode */
+ bool hdr_used;
uint8_t hdr[2];
int ret;
switch (npas->file_type) {
case FILE_TYPE_BYTE_MODE_PIPE:
+ state->hdr_used = false;
state->vector = vector;
state->count = count;
break;
if (tevent_req_nomem(new_vector, req)) {
goto post;
}
- new_vector[0].iov_base = state->hdr;
+ new_vector[0].iov_base = (char *) state->hdr;
new_vector[0].iov_len = sizeof(state->hdr);
memcpy(new_vector + 1, vector, sizeof(struct iovec)*count);
+ state->hdr_used = true;
state->vector = new_vector;
state->count = count + 1;
msg_len = 0;
for (i=0; i < count; i++) {
+ /*
+ * overflow check already done in tstream_writev_send
+ */
msg_len += vector[i].iov_len;
}
return;
}
+ /*
+ * in message mode we need to hide the length
+ * of the hdr from the caller
+ */
+ if (state->hdr_used) {
+ ret -= sizeof(state->hdr);
+ }
+
state->ret = ret;
tevent_req_done(req);
return 0;
}
+
+struct tstream_npa_accept_state {
+ struct tevent_context *ev;
+ struct tstream_context *plain;
+ uint16_t file_type;
+ uint16_t device_state;
+ uint64_t alloc_size;
+
+ DATA_BLOB npa_blob;
+ struct iovec out_iov;
+
+ /* results */
+ NTSTATUS accept_status;
+ struct tsocket_address *remote_client_addr;
+ char *remote_client_name;
+ struct tsocket_address *local_server_addr;
+ char *local_server_name;
+ struct auth_session_info_transport *session_info;
+};
+
+static void tstream_npa_accept_existing_reply(struct tevent_req *subreq);
+static void tstream_npa_accept_existing_done(struct tevent_req *subreq);
+
+struct tevent_req *tstream_npa_accept_existing_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct tstream_context *plain,
+ uint16_t file_type,
+ uint16_t device_state,
+ uint64_t allocation_size)
+{
+ struct tstream_npa_accept_state *state;
+ struct tevent_req *req, *subreq;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct tstream_npa_accept_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ switch (file_type) {
+ case FILE_TYPE_BYTE_MODE_PIPE:
+ break;
+ case FILE_TYPE_MESSAGE_MODE_PIPE:
+ break;
+ default:
+ tevent_req_error(req, EINVAL);
+ goto post;
+ }
+
+ state->ev = ev;
+ state->plain = plain;
+ state->file_type = file_type;
+ state->device_state = device_state;
+ state->alloc_size = allocation_size;
+
+ subreq = tstream_u32_read_send(state, ev, 0x00FFFFFF, plain);
+ if (tevent_req_nomem(subreq, req)) {
+ goto post;
+ }
+
+ tevent_req_set_callback(subreq,
+ tstream_npa_accept_existing_reply, req);
+
+ return req;
+
+post:
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void tstream_npa_accept_existing_reply(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq, struct tevent_req);
+ struct tstream_npa_accept_state *state =
+ tevent_req_data(req, struct tstream_npa_accept_state);
+ struct named_pipe_auth_req *pipe_request;
+ struct named_pipe_auth_rep pipe_reply;
+ struct named_pipe_auth_req_info4 i4;
+ enum ndr_err_code ndr_err;
+ DATA_BLOB in, out;
+ int err;
+ int ret;
+
+ err = tstream_u32_read_recv(subreq, state, &in.data, &in.length);
+ if (err != 0) {
+ tevent_req_error(req, err);
+ return;
+ }
+ if (in.length < 8) {
+ tevent_req_error(req, EMSGSIZE);
+ return;
+ }
+
+ if (memcmp(&in.data[4], NAMED_PIPE_AUTH_MAGIC, 4) != 0) {
+ DBG_ERR("Wrong protocol\n");
+#if defined(EPROTONOSUPPORT)
+ err = EPROTONOSUPPORT;
+#elif defined(EPROTO)
+ err = EPROTO;
+#else
+ err = EINVAL;
+#endif
+ tevent_req_error(req, err);
+ return;
+ }
+
+ DBG_DEBUG("Received packet of length %zu\n", in.length);
+ dump_data(11, in.data, in.length);
+
+ ZERO_STRUCT(pipe_reply);
+ pipe_reply.level = 0;
+ pipe_reply.status = NT_STATUS_INTERNAL_ERROR;
+ /*
+ * TODO: check it's a root (uid == 0) pipe
+ */
+
+ pipe_request = talloc(state, struct named_pipe_auth_req);
+ if (!pipe_request) {
+ DEBUG(0, ("Out of memory!\n"));
+ goto reply;
+ }
+
+ /* parse the passed credentials */
+ ndr_err = ndr_pull_struct_blob_all(
+ &in,
+ pipe_request,
+ pipe_request,
+ (ndr_pull_flags_fn_t)ndr_pull_named_pipe_auth_req);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ pipe_reply.status = ndr_map_error2ntstatus(ndr_err);
+ DEBUG(2, ("Could not unmarshall named_pipe_auth_req: %s\n",
+ nt_errstr(pipe_reply.status)));
+ goto reply;
+ }
+
+ if (DEBUGLVL(10)) {
+ NDR_PRINT_DEBUG(named_pipe_auth_req, pipe_request);
+ }
+
+ ZERO_STRUCT(i4);
+
+ if (pipe_request->level != 4) {
+ DEBUG(0, ("Unknown level %u\n", pipe_request->level));
+ pipe_reply.level = 0;
+ pipe_reply.status = NT_STATUS_INVALID_LEVEL;
+ goto reply;
+ }
+
+ pipe_reply.level = 4;
+ pipe_reply.status = NT_STATUS_OK;
+ pipe_reply.info.info4.file_type = state->file_type;
+ pipe_reply.info.info4.device_state = state->device_state;
+ pipe_reply.info.info4.allocation_size = state->alloc_size;
+
+ i4 = pipe_request->info.info4;
+ if (i4.local_server_addr == NULL) {
+ pipe_reply.status = NT_STATUS_INVALID_ADDRESS;
+ DEBUG(2, ("Missing local server address\n"));
+ goto reply;
+ }
+ if (i4.remote_client_addr == NULL) {
+ pipe_reply.status = NT_STATUS_INVALID_ADDRESS;
+ DEBUG(2, ("Missing remote client address\n"));
+ goto reply;
+ }
+
+ state->local_server_name = discard_const_p(char,
+ talloc_move(state,
+ &i4.local_server_name));
+ ret = tsocket_address_inet_from_strings(state, "ip",
+ i4.local_server_addr,
+ i4.local_server_port,
+ &state->local_server_addr);
+ if (ret != 0) {
+ DEBUG(2, ("Invalid local server address[%s:%u] - %s\n",
+ i4.local_server_addr, i4.local_server_port,
+ strerror(errno)));
+ pipe_reply.status = NT_STATUS_INVALID_ADDRESS;
+ goto reply;
+ }
+
+ state->remote_client_name = discard_const_p(char,
+ talloc_move(state,
+ &i4.remote_client_name));
+ ret = tsocket_address_inet_from_strings(state, "ip",
+ i4.remote_client_addr,
+ i4.remote_client_port,
+ &state->remote_client_addr);
+ if (ret != 0) {
+ DEBUG(2, ("Invalid remote client address[%s:%u] - %s\n",
+ i4.remote_client_addr, i4.remote_client_port,
+ strerror(errno)));
+ pipe_reply.status = NT_STATUS_INVALID_ADDRESS;
+ goto reply;
+ }
+
+ state->session_info = talloc_move(state, &i4.session_info);
+reply:
+ /* create the output */
+ ndr_err = ndr_push_struct_blob(&out, state, &pipe_reply,
+ (ndr_push_flags_fn_t)ndr_push_named_pipe_auth_rep);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ DEBUG(2, ("Error encoding structure: %s",
+ ndr_map_error2string(ndr_err)));
+ tevent_req_error(req, EIO);
+ return;
+ }
+
+ DEBUG(10, ("named_pipe_auth reply[%u]\n", (unsigned)out.length));
+ dump_data(11, out.data, out.length);
+
+ if (DEBUGLVL(10)) {
+ NDR_PRINT_DEBUG(named_pipe_auth_rep, &pipe_reply);
+ }
+
+ state->accept_status = pipe_reply.status;
+
+ state->out_iov.iov_base = (char *) out.data;
+ state->out_iov.iov_len = out.length;
+
+ subreq = tstream_writev_send(state, state->ev,
+ state->plain,
+ &state->out_iov, 1);
+ if (tevent_req_nomem(subreq, req)) {
+ DEBUG(0, ("no memory for tstream_writev_send"));
+ return;
+ }
+
+ tevent_req_set_callback(subreq, tstream_npa_accept_existing_done, req);
+}
+
+static void tstream_npa_accept_existing_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq, struct tevent_req);
+ int sys_errno;
+ int ret;
+
+ ret = tstream_writev_recv(subreq, &sys_errno);
+ TALLOC_FREE(subreq);
+ if (ret == -1) {
+ tevent_req_error(req, sys_errno);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+int _tstream_npa_accept_existing_recv(struct tevent_req *req,
+ int *perrno,
+ TALLOC_CTX *mem_ctx,
+ struct tstream_context **stream,
+ struct tsocket_address **remote_client_addr,
+ char **_remote_client_name,
+ struct tsocket_address **local_server_addr,
+ char **local_server_name,
+ struct auth_session_info_transport **session_info,
+ const char *location)
+{
+ struct tstream_npa_accept_state *state =
+ tevent_req_data(req, struct tstream_npa_accept_state);
+ struct tstream_npa *npas;
+ int ret;
+
+ ret = tsocket_simple_int_recv(req, perrno);
+ if (ret != 0) {
+ DEBUG(2, ("Failed to accept named pipe connection: %s\n",
+ strerror(*perrno)));
+ tevent_req_received(req);
+ return -1;
+ }
+
+ if (!NT_STATUS_IS_OK(state->accept_status)) {
+#if defined(EPROTONOSUPPORT)
+ *perrno = EPROTONOSUPPORT;
+#elif defined(EPROTO)
+ *perrno = EPROTO;
+#else
+ *perrno = EINVAL;
+#endif
+ DEBUG(2, ("Failed to accept named pipe connection: %s => %s\n",
+ nt_errstr(state->accept_status),
+ strerror(*perrno)));
+ tevent_req_received(req);
+ return -1;
+ }
+
+ *stream = tstream_context_create(mem_ctx,
+ &tstream_npa_ops,
+ &npas,
+ struct tstream_npa,
+ location);
+ if (!*stream) {
+ *perrno = ENOMEM;
+ tevent_req_received(req);
+ return -1;
+ }
+ ZERO_STRUCTP(npas);
+ npas->unix_stream = state->plain;
+ npas->file_type = state->file_type;
+
+ *remote_client_addr = talloc_move(mem_ctx, &state->remote_client_addr);
+ *_remote_client_name = talloc_move(mem_ctx, &state->remote_client_name);
+ *local_server_addr = talloc_move(mem_ctx, &state->local_server_addr);
+ *local_server_name = talloc_move(mem_ctx, &state->local_server_name);
+ *session_info = talloc_move(mem_ctx, &state->session_info);
+
+ tevent_req_received(req);
+ return 0;
+}
+
+
+/* SOCKETPAIR for internal rpc communication */
+
+/* file_type is FILE_TYPE_BYTE_MODE_PIPE or FILE_TYPE_MESSAGE_MODE_PIPE */
+int _tstream_npa_socketpair(uint16_t file_type,
+ TALLOC_CTX *mem_ctx1,
+ struct tstream_context **pstream1,
+ TALLOC_CTX *mem_ctx2,
+ struct tstream_context **pstream2,
+ const char *location)
+{
+ struct tstream_context *stream1 = NULL;
+ struct tstream_context *stream2 = NULL;
+ int fds[2];
+ int fd1;
+ int fd2;
+ int rc;
+ bool ok;
+
+ rc = socketpair(AF_UNIX, SOCK_STREAM, 0, fds);
+ if (rc == -1) {
+ return -1;
+ }
+ fd1 = fds[0];
+ fd2 = fds[1];
+
+ ok = smb_set_close_on_exec(fd1);
+ if (!ok) {
+ goto close_fail;
+ }
+
+ ok = smb_set_close_on_exec(fd2);
+ if (!ok) {
+ goto close_fail;
+ }
+
+ rc = set_blocking(fd1, false);
+ if (rc == -1) {
+ goto close_fail;
+ }
+
+ rc = set_blocking(fd2, false);
+ if (rc == -1) {
+ goto close_fail;
+ }
+
+ rc = _tstream_npa_existing_socket(mem_ctx1,
+ fd1,
+ file_type,
+ &stream1,
+ location);
+ if (rc == -1) {
+ goto close_fail;
+ }
+
+ rc = _tstream_npa_existing_socket(mem_ctx2,
+ fd2,
+ file_type,
+ &stream2,
+ location);
+ if (rc == -1) {
+ int sys_errno = errno;
+ talloc_free(stream1);
+ close(fd2);
+ errno = sys_errno;
+ return -1;
+ }
+
+ *pstream1 = stream1;
+ *pstream2 = stream2;
+
+ return 0;
+
+close_fail:
+ {
+ int sys_errno = errno;
+ close(fd1);
+ close(fd2);
+ errno = sys_errno;
+ return -1;
+ }
+}