s4 libcli: Add libcli_echo lib and torture test
[Samba.git] / libcli / echo / echo.c
blob46d1e28b36a55794fd8ad1613dc9cfeaa8aaefd4
1 /*
2 Unix SMB/CIFS implementation.
4 Echo example async client library
6 Copyright (C) 2010 Kai Blin <kai@samba.org>
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "replace.h"
23 #include "system/network.h"
24 #include <tevent.h>
25 #include "lib/tsocket/tsocket.h"
26 #include "libcli/util/ntstatus.h"
27 #include "libcli/echo/libecho.h"
28 #include "lib/util/tevent_ntstatus.h"
29 #include "libcli/util/error.h"
32 * Following the Samba convention for async functions, set up a state struct
33 * for this set of calls. The state is always called function_name_state for
34 * the set of async functions related to function_name_send().
36 struct echo_request_state {
37 struct tevent_context *ev;
38 ssize_t orig_len;
39 struct tdgram_context *dgram;
40 char *message;
43 /* Declare callback functions used below. */
44 static void echo_request_get_reply(struct tevent_req *subreq);
45 static void echo_request_done(struct tevent_req *subreq);
47 struct tevent_req *echo_request_send(TALLOC_CTX *mem_ctx,
48 struct tevent_context *ev,
49 const char *server_addr_string,
50 const char *message)
52 struct tevent_req *req, *subreq;
53 struct echo_request_state *state;
54 struct tsocket_address *local_addr, *server_addr;
55 struct tdgram_context *dgram;
56 int ret;
59 * Creating the initial tevent_req is the only place where returning
60 * NULL is allowed. Everything after that should return a more
61 * meaningful error using tevent_req_post().
63 req = tevent_req_create(mem_ctx, &state, struct echo_request_state);
64 if (req == NULL) {
65 return NULL;
69 * We need to dispatch new async functions in the callbacks, hold
70 * on to the event context.
72 state->ev = ev;
74 /* libecho uses connected UDP sockets, take care of this here */
75 ret = tsocket_address_inet_from_strings(state, "ip", NULL, 0,
76 &local_addr);
77 if (ret != 0) {
78 tevent_req_nterror(req, map_nt_error_from_unix(ret));
79 return tevent_req_post(req, ev);
82 ret = tsocket_address_inet_from_strings(state, "ip", server_addr_string,
83 ECHO_PORT, &server_addr);
84 if (ret != 0) {
85 tevent_req_nterror(req, map_nt_error_from_unix(ret));
86 return tevent_req_post(req, ev);
89 ret = tdgram_inet_udp_socket(local_addr, server_addr, state, &dgram);
90 if (ret != 0) {
91 tevent_req_nterror(req, map_nt_error_from_unix(ret));
92 return tevent_req_post(req, ev);
95 state->dgram = dgram;
96 state->orig_len = strlen(message) + 1;
98 /* Start of a subrequest for the actual data sending */
99 subreq = tdgram_sendto_send(state, ev, dgram,
100 (const uint8_t *) message,
101 state->orig_len, NULL);
102 if (tevent_req_nomem(subreq, req)) {
103 return tevent_req_post(req, ev);
107 * And tell tevent what to call when the subreq is done. Note that the
108 * original req structure is passed into the callback as callback data.
109 * This is used to get to the state struct in callbacks.
111 tevent_req_set_callback(subreq, echo_request_get_reply, req);
112 return req;
116 * The following two callbacks both demonstrate the way of getting back the
117 * state struct in a callback function.
120 static void echo_request_get_reply(struct tevent_req *subreq)
122 /* Get the parent request struct from the callback data */
123 struct tevent_req *req = tevent_req_callback_data(subreq,
124 struct tevent_req);
125 /* And get the state struct from the parent request struct */
126 struct echo_request_state *state = tevent_req_data(req,
127 struct echo_request_state);
128 ssize_t len;
129 int err = 0;
131 len = tdgram_sendto_recv(subreq, &err);
132 TALLOC_FREE(subreq);
134 if (len == -1 && err != 0) {
135 tevent_req_nterror(req, map_nt_error_from_unix(err));
136 return;
139 if (len != state->orig_len) {
140 tevent_req_nterror(req, NT_STATUS_UNEXPECTED_NETWORK_ERROR);
141 return;
144 /* Send off the second subreq here, this time to receive the reply */
145 subreq = tdgram_recvfrom_send(state, state->ev, state->dgram);
146 if (tevent_req_nomem(subreq, req)) {
147 return;
150 /* And set the new callback */
151 tevent_req_set_callback(subreq, echo_request_done, req);
152 return;
155 static void echo_request_done(struct tevent_req *subreq)
157 struct tevent_req *req = tevent_req_callback_data(subreq,
158 struct tevent_req);
159 struct echo_request_state *state = tevent_req_data(req,
160 struct echo_request_state);
162 ssize_t len;
163 int err = 0;
165 len = tdgram_recvfrom_recv(subreq, &err, state,
166 (uint8_t **)&state->message,
167 NULL);
168 TALLOC_FREE(subreq);
170 if (len == -1 && err != 0) {
171 tevent_req_nterror(req, map_nt_error_from_unix(err));
172 return;
175 state->message[len] = '\0';
176 /* Once the async function has completed, set tevent_req_done() */
177 tevent_req_done(req);
181 * In the recv function, we usually need to move the data from the state struct
182 * to the memory area owned by the caller. Also, the function
183 * tevent_req_received() is called to take care of freeing the memory still
184 * associated with the request.
187 NTSTATUS echo_request_recv(struct tevent_req *req,
188 TALLOC_CTX *mem_ctx,
189 char **message)
191 struct echo_request_state *state = tevent_req_data(req,
192 struct echo_request_state);
193 NTSTATUS status;
195 if (tevent_req_is_nterror(req, &status)) {
196 tevent_req_received(req);
197 return status;
200 *message = talloc_move(mem_ctx, &state->message);
201 tevent_req_received(req);
203 return NT_STATUS_OK;