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/>.
23 #include "system/network.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
;
39 struct tdgram_context
*dgram
;
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
,
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
;
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
);
69 * We need to dispatch new async functions in the callbacks, hold
70 * on to the event context.
74 /* libecho uses connected UDP sockets, take care of this here */
75 ret
= tsocket_address_inet_from_strings(state
, "ip", NULL
, 0,
78 tevent_req_nterror(req
, map_nt_error_from_unix_common(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
);
85 tevent_req_nterror(req
, map_nt_error_from_unix_common(ret
));
86 return tevent_req_post(req
, ev
);
89 ret
= tdgram_inet_udp_socket(local_addr
, server_addr
, state
, &dgram
);
91 tevent_req_nterror(req
, map_nt_error_from_unix_common(ret
));
92 return tevent_req_post(req
, ev
);
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
);
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
,
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
);
131 len
= tdgram_sendto_recv(subreq
, &err
);
134 if (len
== -1 && err
!= 0) {
135 tevent_req_nterror(req
, map_nt_error_from_unix_common(err
));
139 if (len
!= state
->orig_len
) {
140 tevent_req_nterror(req
, NT_STATUS_UNEXPECTED_NETWORK_ERROR
);
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
)) {
150 /* And set the new callback */
151 tevent_req_set_callback(subreq
, echo_request_done
, req
);
155 static void echo_request_done(struct tevent_req
*subreq
)
157 struct tevent_req
*req
= tevent_req_callback_data(subreq
,
159 struct echo_request_state
*state
= tevent_req_data(req
,
160 struct echo_request_state
);
165 len
= tdgram_recvfrom_recv(subreq
, &err
, state
,
166 (uint8_t **)&state
->message
,
170 if (len
== -1 && err
!= 0) {
171 tevent_req_nterror(req
, map_nt_error_from_unix_common(err
));
175 if (len
!= state
->orig_len
) {
176 tevent_req_nterror(req
, NT_STATUS_INVALID_NETWORK_RESPONSE
);
180 state
->message
[len
-1] = '\0';
181 /* Once the async function has completed, set tevent_req_done() */
182 tevent_req_done(req
);
186 * In the recv function, we usually need to move the data from the state struct
187 * to the memory area owned by the caller. Also, the function
188 * tevent_req_received() is called to take care of freeing the memory still
189 * associated with the request.
192 NTSTATUS
echo_request_recv(struct tevent_req
*req
,
196 struct echo_request_state
*state
= tevent_req_data(req
,
197 struct echo_request_state
);
200 if (tevent_req_is_nterror(req
, &status
)) {
201 tevent_req_received(req
);
205 *message
= talloc_move(mem_ctx
, &state
->message
);
206 tevent_req_received(req
);