From: Stefan Metzmacher Date: Thu, 11 Jun 2026 13:53:07 +0000 (+0200) Subject: s3:rpc_server: add a simple npsd_echo_server.c X-Git-Tag: talloc-2.5.0~51 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=6526c9a3f4f3abe19dcd0a97bdb67b5e79671f87;p=thirdparty%2Fsamba.git s3:rpc_server: add a simple npsd_echo_server.c This uses the new nps_worker_main() infrastructure in order to implement a trivial named pipe echo service. For now it provides two named pipes both with message mode, but different allocation sizes limiting the max size of an echo request. Signed-off-by: Stefan Metzmacher Reviewed-by: Volker Lendecke --- diff --git a/source3/rpc_server/npsd_echo_server.c b/source3/rpc_server/npsd_echo_server.c new file mode 100644 index 00000000000..052d5e66440 --- /dev/null +++ b/source3/rpc_server/npsd_echo_server.c @@ -0,0 +1,359 @@ +/* + * Unix SMB/CIFS implementation. + * + * Simple Named Pipe Echo service + * + * Copyright (C) Stefan Metzmacher 2026 + * + * 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 . + */ + +#include "includes.h" +#include "lib/global_contexts.h" +#include "lib/tsocket/tsocket.h" +#include "lib/util/tevent_unix.h" +#include "rpc_server/rpc_worker_nps.h" + +static const struct nps_interface npsd_echo_msg8_iface = { + .pipe_name = "nps_echo_msg8", + .file_type = FILE_TYPE_MESSAGE_MODE_PIPE, + .device_state = 0xff | 0x0400 | 0x0100, + .allocation_size = UINT8_MAX, +}; + +static const struct nps_interface npsd_echo_msg16_iface = { + .pipe_name = "nps_echo_msg16", + .file_type = FILE_TYPE_MESSAGE_MODE_PIPE, + .device_state = 0xff | 0x0400 | 0x0100, + .allocation_size = UINT16_MAX, +}; + +static size_t npsd_echo_get_interfaces(const struct nps_interface ***pifaces, + void *private_data) +{ + static const struct nps_interface *ifaces[] = { + &npsd_echo_msg8_iface, + &npsd_echo_msg16_iface, + }; + + *pifaces = ifaces; + return ARRAY_SIZE(ifaces); +} + +static NTSTATUS npsd_echo_setup_servers(struct rpc_worker *worker, + void *private_data) +{ + DBG_NOTICE("Started (%d)\n", getpid()); + return NT_STATUS_OK; +} + +struct npsd_echo_connection { + struct tevent_context *ev; + struct rpc_worker_connection *worker_conn; + struct auth_session_info *session_info; + struct tstream_context *tstream; + struct tsocket_address *remote_client_addr; + struct tsocket_address *local_server_addr; + size_t max_size; + + struct tevent_req *error_subreq; + struct tevent_req *in_subreq; + DATA_BLOB blob; + struct iovec out_vec; + struct tevent_req *out_subreq; +}; + +static int npsd_echo_connection_destructor( + struct npsd_echo_connection *echo_conn) +{ + /* + * First cleanup all subreqs + */ + TALLOC_FREE(echo_conn->error_subreq); + TALLOC_FREE(echo_conn->in_subreq); + TALLOC_FREE(echo_conn->out_subreq); + + /* + * Disconnect the connection + */ + TALLOC_FREE(echo_conn->tstream); + + /* + * This lets the rpc_worker_connection_destructor + * to call rpc_worker_report_status()... + */ + TALLOC_FREE(echo_conn->worker_conn); + return 0; +} + +static void npsd_echo_connection_error(struct tevent_req *subreq); +static int npsd_echo_connection_next_vector(struct tstream_context *stream, + void *private_data, + TALLOC_CTX *mem_ctx, + struct iovec **_vector, + size_t *_count); +static void npsd_echo_connection_in_done(struct tevent_req *subreq); +static void npsd_echo_connection_out_done(struct tevent_req *subreq); + +static NTSTATUS npsd_echo_accept_client( + struct rpc_worker *worker, + void *private_data, + struct rpc_worker_connection *worker_conn, + struct auth_session_info **transport_session_info, + const char *pipe_name, + struct tstream_context **tstream, + struct tsocket_address **remote_client_addr, + struct tsocket_address **local_server_addr) +{ + struct tevent_context *ev_ctx = global_event_context(); + struct npsd_echo_connection *echo_conn = NULL; + struct tevent_req *subreq = NULL; + size_t max_size; + + if (strcmp(npsd_echo_msg8_iface.pipe_name, pipe_name) == 0) { + max_size = npsd_echo_msg8_iface.allocation_size; + } else if (strcmp(npsd_echo_msg16_iface.pipe_name, pipe_name) == 0) { + max_size = npsd_echo_msg16_iface.allocation_size; + } else { + return NT_STATUS_REQUEST_NOT_ACCEPTED; + } + + echo_conn = talloc_zero(worker_conn, struct npsd_echo_connection); + if (echo_conn == NULL) { + return NT_STATUS_NO_MEMORY; + } + + echo_conn->ev = ev_ctx; + echo_conn->worker_conn = worker_conn; + echo_conn->session_info = talloc_move(echo_conn, transport_session_info); + echo_conn->tstream = talloc_move(echo_conn, tstream); + echo_conn->remote_client_addr = talloc_move(echo_conn, remote_client_addr); + echo_conn->local_server_addr = talloc_move(echo_conn, local_server_addr); + echo_conn->max_size = max_size; + + DBG_NOTICE("starting echo server loop max_size=%zu\n", max_size); + + subreq = tstream_monitor_send(echo_conn, + echo_conn->ev, + echo_conn->tstream); + if (subreq == NULL) { + return NT_STATUS_NO_MEMORY; + } + echo_conn->error_subreq = subreq; + tevent_req_set_callback(echo_conn->error_subreq, + npsd_echo_connection_error, + echo_conn); + + subreq = tstream_readv_pdu_send(echo_conn, + echo_conn->ev, + echo_conn->tstream, + npsd_echo_connection_next_vector, + echo_conn); + if (subreq == NULL) { + return NT_STATUS_NO_MEMORY; + } + echo_conn->in_subreq = subreq; + tevent_req_set_callback(echo_conn->in_subreq, + npsd_echo_connection_in_done, + echo_conn); + + talloc_set_destructor(echo_conn, npsd_echo_connection_destructor); + + return NT_STATUS_OK; +} + +static void npsd_echo_connection_error(struct tevent_req *subreq) +{ + struct npsd_echo_connection *echo_conn = + tevent_req_callback_data(subreq, + struct npsd_echo_connection); + int ret; + int err; + + SMB_ASSERT(echo_conn->error_subreq == subreq); + echo_conn->error_subreq = NULL; + + ret = tstream_monitor_recv(subreq, &err); + TALLOC_FREE(subreq); + + DBG_NOTICE("Error: ret=%d err=%d (%s)\n", ret, err, strerror(err)); + TALLOC_FREE(echo_conn); +} + +static int npsd_echo_connection_next_vector(struct tstream_context *stream, + void *private_data, + TALLOC_CTX *mem_ctx, + struct iovec **_vector, + size_t *_count) +{ + struct npsd_echo_connection *echo_conn = + talloc_get_type_abort(private_data, + struct npsd_echo_connection); + struct iovec *vector = NULL; + size_t ofs = echo_conn->blob.length; + ssize_t pending; + + pending = tstream_pending_bytes(stream); + if (pending < 0) { + return pending; + } + + if (pending == 0) { + if (ofs != 0) { + return 0; + } + + echo_conn->blob = data_blob_talloc(echo_conn, + NULL, + 1); + if (echo_conn->blob.length == 0) { + return -ENOMEM; + } + } else { + size_t max_size = MIN(UINT16_MAX, echo_conn->max_size); + size_t full_length; + bool ok; + + if (pending > max_size) { + return -EPROTO; + } + if (ofs > max_size) { + return -EPROTO; + } + full_length = ofs + pending; + if (full_length > max_size) { + return -EPROTO; + } + + ok = data_blob_realloc(echo_conn, + &echo_conn->blob, + full_length); + if (!ok) { + return -ENOMEM; + } + } + + vector = talloc(mem_ctx, struct iovec); + if (vector == NULL) { + return -ENOMEM; + } + vector->iov_base = echo_conn->blob.data + ofs; + vector->iov_len = echo_conn->blob.length - ofs; + + *_vector = vector; + *_count = 1; + return 0; +} + +static void npsd_echo_connection_in_done(struct tevent_req *subreq) +{ + struct npsd_echo_connection *echo_conn = + tevent_req_callback_data(subreq, + struct npsd_echo_connection); + int ret; + int err; + + SMB_ASSERT(echo_conn->in_subreq == subreq); + echo_conn->in_subreq = NULL; + + ret = tstream_readv_pdu_recv(subreq, &err); + TALLOC_FREE(subreq); + if (ret < 0) { + DBG_ERR("tstream_readv_pdu_recv() ret=%d err=%d (%s)\n", + ret, err, strerror(err)); + TALLOC_FREE(echo_conn); + return; + } + + echo_conn->out_vec.iov_base = echo_conn->blob.data; + echo_conn->out_vec.iov_len = echo_conn->blob.length; + + subreq = tstream_writev_send(echo_conn, + echo_conn->ev, + echo_conn->tstream, + &echo_conn->out_vec, + 1); + if (subreq == NULL) { + DBG_ERR("tstream_writev_send() failed\n"); + TALLOC_FREE(echo_conn); + return; + } + echo_conn->out_subreq = subreq; + tevent_req_set_callback(echo_conn->out_subreq, + npsd_echo_connection_out_done, + echo_conn); + return; +} + +static void npsd_echo_connection_out_done(struct tevent_req *subreq) +{ + struct npsd_echo_connection *echo_conn = + tevent_req_callback_data(subreq, + struct npsd_echo_connection); + int ret; + int err; + + SMB_ASSERT(echo_conn->out_subreq == subreq); + echo_conn->out_subreq = NULL; + + echo_conn->out_vec = (struct iovec) { .iov_len = 0, }; + data_blob_free(&echo_conn->blob); + + ret = tstream_writev_recv(subreq, &err); + TALLOC_FREE(subreq); + if (ret < 0) { + DBG_ERR("tstream_writev_recv() ret=%d err=%d (%s)\n", + ret, err, strerror(err)); + TALLOC_FREE(echo_conn); + return; + } + + subreq = tstream_readv_pdu_send(echo_conn, + echo_conn->ev, + echo_conn->tstream, + npsd_echo_connection_next_vector, + echo_conn); + if (subreq == NULL) { + DBG_ERR("tstream_readv_pdu_send() failed\n"); + TALLOC_FREE(echo_conn); + return; + } + echo_conn->in_subreq = subreq; + tevent_req_set_callback(echo_conn->in_subreq, + npsd_echo_connection_in_done, + echo_conn); + return; +} + +static NTSTATUS npsd_echo_shutdown_servers(struct rpc_worker *worker, + void *private_data) +{ + DBG_DEBUG("server exiting\n"); + return NT_STATUS_OK; +} + +int main(int argc, const char *argv[]) +{ + return nps_worker_main( + argc, + argv, + "npsd_echo_server", + 5, + 60, + npsd_echo_get_interfaces, + npsd_echo_setup_servers, + npsd_echo_accept_client, + npsd_echo_shutdown_servers, + NULL); +} diff --git a/source3/rpc_server/wscript_build b/source3/rpc_server/wscript_build index 7e2d480ba89..2b9e5d94adc 100644 --- a/source3/rpc_server/wscript_build +++ b/source3/rpc_server/wscript_build @@ -34,6 +34,14 @@ bld.SAMBA_LIBRARY('RPC_WORKER', npa_tstream ''') +bld.SAMBA3_BINARY('npsd_echo_server', + source='npsd_echo_server.c', + deps=''' + RPC_WORKER + ''', + for_selftest=True, + install_path='${SAMBA_LIBEXECDIR}') + bld.SAMBA3_BINARY('rpcd_rpcecho', source='rpcd_rpcecho.c', deps='''