summaryrefslogtreecommitdiff
path: root/libcli/echo
diff options
context:
space:
mode:
Diffstat (limited to 'libcli/echo')
-rw-r--r--libcli/echo/echo.c204
-rw-r--r--libcli/echo/libecho.h56
-rw-r--r--libcli/echo/tests/echo.c93
-rw-r--r--libcli/echo/tests/wscript_build8
-rw-r--r--libcli/echo/wscript_build7
5 files changed, 368 insertions, 0 deletions
diff --git a/libcli/echo/echo.c b/libcli/echo/echo.c
new file mode 100644
index 0000000000..46d1e28b36
--- /dev/null
+++ b/libcli/echo/echo.c
@@ -0,0 +1,204 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Echo example async client library
+
+ Copyright (C) 2010 Kai Blin <kai@samba.org>
+
+ 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 "replace.h"
+#include "system/network.h"
+#include <tevent.h>
+#include "lib/tsocket/tsocket.h"
+#include "libcli/util/ntstatus.h"
+#include "libcli/echo/libecho.h"
+#include "lib/util/tevent_ntstatus.h"
+#include "libcli/util/error.h"
+
+/*
+ * Following the Samba convention for async functions, set up a state struct
+ * for this set of calls. The state is always called function_name_state for
+ * the set of async functions related to function_name_send().
+ */
+struct echo_request_state {
+ struct tevent_context *ev;
+ ssize_t orig_len;
+ struct tdgram_context *dgram;
+ char *message;
+};
+
+/* Declare callback functions used below. */
+static void echo_request_get_reply(struct tevent_req *subreq);
+static void echo_request_done(struct tevent_req *subreq);
+
+struct tevent_req *echo_request_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *server_addr_string,
+ const char *message)
+{
+ struct tevent_req *req, *subreq;
+ struct echo_request_state *state;
+ struct tsocket_address *local_addr, *server_addr;
+ struct tdgram_context *dgram;
+ int ret;
+
+ /*
+ * Creating the initial tevent_req is the only place where returning
+ * NULL is allowed. Everything after that should return a more
+ * meaningful error using tevent_req_post().
+ */
+ req = tevent_req_create(mem_ctx, &state, struct echo_request_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ /*
+ * We need to dispatch new async functions in the callbacks, hold
+ * on to the event context.
+ */
+ state->ev = ev;
+
+ /* libecho uses connected UDP sockets, take care of this here */
+ ret = tsocket_address_inet_from_strings(state, "ip", NULL, 0,
+ &local_addr);
+ if (ret != 0) {
+ tevent_req_nterror(req, map_nt_error_from_unix(ret));
+ return tevent_req_post(req, ev);
+ }
+
+ ret = tsocket_address_inet_from_strings(state, "ip", server_addr_string,
+ ECHO_PORT, &server_addr);
+ if (ret != 0) {
+ tevent_req_nterror(req, map_nt_error_from_unix(ret));
+ return tevent_req_post(req, ev);
+ }
+
+ ret = tdgram_inet_udp_socket(local_addr, server_addr, state, &dgram);
+ if (ret != 0) {
+ tevent_req_nterror(req, map_nt_error_from_unix(ret));
+ return tevent_req_post(req, ev);
+ }
+
+ state->dgram = dgram;
+ state->orig_len = strlen(message) + 1;
+
+ /* Start of a subrequest for the actual data sending */
+ subreq = tdgram_sendto_send(state, ev, dgram,
+ (const uint8_t *) message,
+ state->orig_len, NULL);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ /*
+ * And tell tevent what to call when the subreq is done. Note that the
+ * original req structure is passed into the callback as callback data.
+ * This is used to get to the state struct in callbacks.
+ */
+ tevent_req_set_callback(subreq, echo_request_get_reply, req);
+ return req;
+}
+
+/*
+ * The following two callbacks both demonstrate the way of getting back the
+ * state struct in a callback function.
+ */
+
+static void echo_request_get_reply(struct tevent_req *subreq)
+{
+ /* Get the parent request struct from the callback data */
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ /* And get the state struct from the parent request struct */
+ struct echo_request_state *state = tevent_req_data(req,
+ struct echo_request_state);
+ ssize_t len;
+ int err = 0;
+
+ len = tdgram_sendto_recv(subreq, &err);
+ TALLOC_FREE(subreq);
+
+ if (len == -1 && err != 0) {
+ tevent_req_nterror(req, map_nt_error_from_unix(err));
+ return;
+ }
+
+ if (len != state->orig_len) {
+ tevent_req_nterror(req, NT_STATUS_UNEXPECTED_NETWORK_ERROR);
+ return;
+ }
+
+ /* Send off the second subreq here, this time to receive the reply */
+ subreq = tdgram_recvfrom_send(state, state->ev, state->dgram);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+
+ /* And set the new callback */
+ tevent_req_set_callback(subreq, echo_request_done, req);
+ return;
+}
+
+static void echo_request_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct echo_request_state *state = tevent_req_data(req,
+ struct echo_request_state);
+
+ ssize_t len;
+ int err = 0;
+
+ len = tdgram_recvfrom_recv(subreq, &err, state,
+ (uint8_t **)&state->message,
+ NULL);
+ TALLOC_FREE(subreq);
+
+ if (len == -1 && err != 0) {
+ tevent_req_nterror(req, map_nt_error_from_unix(err));
+ return;
+ }
+
+ state->message[len] = '\0';
+ /* Once the async function has completed, set tevent_req_done() */
+ tevent_req_done(req);
+}
+
+/*
+ * In the recv function, we usually need to move the data from the state struct
+ * to the memory area owned by the caller. Also, the function
+ * tevent_req_received() is called to take care of freeing the memory still
+ * associated with the request.
+ */
+
+NTSTATUS echo_request_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ char **message)
+{
+ struct echo_request_state *state = tevent_req_data(req,
+ struct echo_request_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+
+ *message = talloc_move(mem_ctx, &state->message);
+ tevent_req_received(req);
+
+ return NT_STATUS_OK;
+}
diff --git a/libcli/echo/libecho.h b/libcli/echo/libecho.h
new file mode 100644
index 0000000000..35a0986a6f
--- /dev/null
+++ b/libcli/echo/libecho.h
@@ -0,0 +1,56 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Echo structures and headers, example async client library
+
+ Copyright (C) 2010 Kai Blin <kai@samba.org>
+
+ 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 __LIBECHO_H__
+#define __LIBECHO_H__
+
+/* The echo port is fixed, so just set a constant. */
+#define ECHO_PORT 7
+
+/** Send an echo request to an echo server
+ *
+ *@param mem_ctx talloc memory context to use
+ *@param ev tevent context to use
+ *@param server_address address of the server as a string
+ *@param message echo message to send
+ *@return tevent_req with the active request or NULL on out-of-memory
+ */
+struct tevent_req *echo_request_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *server_address,
+ const char *message);
+
+/** Get the echo response from an echo server
+ *
+ * Once the echo_request_send async request is finished, you can call
+ * this function to collect the echo reply.
+ *
+ *@param req tevent_req struct returned from echo_request_send
+ *@param mem_ctx talloc memory context to use for the reply string
+ *@param message pointer to a string that will be allocated and filled with
+ the echo reply.
+ *@return NTSTATUS code depending on the async request result
+ */
+NTSTATUS echo_request_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ char **message);
+
+#endif /*__LIBECHO_H__*/
diff --git a/libcli/echo/tests/echo.c b/libcli/echo/tests/echo.c
new file mode 100644
index 0000000000..931e8b6c02
--- /dev/null
+++ b/libcli/echo/tests/echo.c
@@ -0,0 +1,93 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Example echo torture tests
+
+ Copyright (C) 2010 Kai Blin <kai@samba.org>
+
+ 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 "torture/smbtorture.h"
+#include "libcli/resolve/resolve.h"
+#include <tevent.h>
+#include "libcli/util/ntstatus.h"
+#include "libcli/echo/libecho.h"
+
+/* Basic test function that sends an echo request and checks the reply */
+static bool echo_udp_basic(struct torture_context *tctx, const char *address)
+{
+ struct tevent_req *req;
+ NTSTATUS status;
+ const char *msg_send = "This is a test string\n";
+ char *msg_recv;
+
+ req = echo_request_send(tctx, tctx->ev, address, msg_send);
+ torture_assert(tctx, req != NULL,
+ "echo_request_send returned non-null tevent_req");
+
+ while(tevent_req_is_in_progress(req)) {
+ tevent_loop_once(tctx->ev);
+ }
+
+ status = echo_request_recv(req, tctx, &msg_recv);
+ torture_assert_ntstatus_ok(tctx, status,
+ "echo_request_recv returned ok");
+
+ torture_assert_str_equal(tctx, msg_recv, msg_send,
+ "Echo server echoed request string");
+
+ return true;
+}
+
+/*Test case to set up the environment and perform UDP-based echo tests */
+static bool torture_echo_udp(struct torture_context *tctx)
+{
+ const char *address;
+ struct nbt_name name;
+ NTSTATUS status;
+ bool ret = true;
+
+ make_nbt_name_server(&name,
+ torture_setting_string(tctx, "host", NULL));
+ status = resolve_name(lpcfg_resolve_context(tctx->lp_ctx), &name, tctx,
+ &address, tctx->ev);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Failed to resolve %s - %s\n", name.name,
+ nt_errstr(status));
+ return false;
+ }
+
+ /* All tests are now called here */
+ ret &= echo_udp_basic(tctx, address);
+
+ return ret;
+}
+
+/* Test suite that bundles all the libecho tests */
+NTSTATUS torture_libcli_echo_init(void)
+{
+ struct torture_suite *suite;
+
+ suite = torture_suite_create(talloc_autofree_context(), "ECHO");
+ NT_STATUS_HAVE_NO_MEMORY(suite);
+
+ torture_suite_add_simple_test(suite, "UDP", torture_echo_udp);
+
+ suite->description = talloc_strdup(suite, "libcli/echo interface tests");
+ torture_register_suite(suite);
+
+ return NT_STATUS_OK;
+}
diff --git a/libcli/echo/tests/wscript_build b/libcli/echo/tests/wscript_build
new file mode 100644
index 0000000000..7e58130708
--- /dev/null
+++ b/libcli/echo/tests/wscript_build
@@ -0,0 +1,8 @@
+#!/usr/bin/env python
+
+bld.SAMBA_MODULE('TORTURE_LIBCLI_ECHO',
+ source='echo.c',
+ subsystem='smbtorture',
+ init_function='torture_libcli_echo_init',
+ deps='LIBTSOCKET UTIL_TEVENT LIBCLI_ECHO',
+ internal_module=True);
diff --git a/libcli/echo/wscript_build b/libcli/echo/wscript_build
new file mode 100644
index 0000000000..eedb4ac593
--- /dev/null
+++ b/libcli/echo/wscript_build
@@ -0,0 +1,7 @@
+#!/usr/bin/env python
+
+bld.SAMBA_SUBSYSTEM('LIBCLI_ECHO',
+ source='echo.c',
+ deps='LIBTSOCKET UTIL_TEVENT');
+
+bld.RECURSE('tests')