Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 :
4 : Echo example async client library
5 :
6 : Copyright (C) 2010 Kai Blin <kai@samba.org>
7 :
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.
12 :
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.
17 :
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/>.
20 : */
21 :
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"
30 :
31 : /*
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().
35 : */
36 : struct echo_request_state {
37 : struct tevent_context *ev;
38 : ssize_t orig_len;
39 : struct tdgram_context *dgram;
40 : char *message;
41 : };
42 :
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);
46 :
47 1 : 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)
51 : {
52 0 : struct tevent_req *req, *subreq;
53 0 : struct echo_request_state *state;
54 0 : struct tsocket_address *local_addr, *server_addr;
55 0 : struct tdgram_context *dgram;
56 0 : int ret;
57 :
58 : /*
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().
62 : */
63 1 : req = tevent_req_create(mem_ctx, &state, struct echo_request_state);
64 1 : if (req == NULL) {
65 0 : return NULL;
66 : }
67 :
68 : /*
69 : * We need to dispatch new async functions in the callbacks, hold
70 : * on to the event context.
71 : */
72 1 : state->ev = ev;
73 :
74 : /* libecho uses connected UDP sockets, take care of this here */
75 1 : ret = tsocket_address_inet_from_strings(state, "ip", NULL, 0,
76 : &local_addr);
77 1 : if (ret != 0) {
78 0 : tevent_req_nterror(req, map_nt_error_from_unix_common(ret));
79 0 : return tevent_req_post(req, ev);
80 : }
81 :
82 1 : ret = tsocket_address_inet_from_strings(state, "ip", server_addr_string,
83 : ECHO_PORT, &server_addr);
84 1 : if (ret != 0) {
85 0 : tevent_req_nterror(req, map_nt_error_from_unix_common(ret));
86 0 : return tevent_req_post(req, ev);
87 : }
88 :
89 1 : ret = tdgram_inet_udp_socket(local_addr, server_addr, state, &dgram);
90 1 : if (ret != 0) {
91 0 : tevent_req_nterror(req, map_nt_error_from_unix_common(ret));
92 0 : return tevent_req_post(req, ev);
93 : }
94 :
95 1 : state->dgram = dgram;
96 1 : state->orig_len = strlen(message) + 1;
97 :
98 : /* Start of a subrequest for the actual data sending */
99 1 : subreq = tdgram_sendto_send(state, ev, dgram,
100 : (const uint8_t *) message,
101 1 : state->orig_len, NULL);
102 1 : if (tevent_req_nomem(subreq, req)) {
103 0 : return tevent_req_post(req, ev);
104 : }
105 :
106 : /*
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.
110 : */
111 1 : tevent_req_set_callback(subreq, echo_request_get_reply, req);
112 1 : return req;
113 : }
114 :
115 : /*
116 : * The following two callbacks both demonstrate the way of getting back the
117 : * state struct in a callback function.
118 : */
119 :
120 1 : static void echo_request_get_reply(struct tevent_req *subreq)
121 : {
122 : /* Get the parent request struct from the callback data */
123 1 : 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 1 : struct echo_request_state *state = tevent_req_data(req,
127 : struct echo_request_state);
128 0 : ssize_t len;
129 1 : int err = 0;
130 :
131 1 : len = tdgram_sendto_recv(subreq, &err);
132 1 : TALLOC_FREE(subreq);
133 :
134 1 : if (len == -1 && err != 0) {
135 0 : tevent_req_nterror(req, map_nt_error_from_unix_common(err));
136 0 : return;
137 : }
138 :
139 1 : if (len != state->orig_len) {
140 0 : tevent_req_nterror(req, NT_STATUS_UNEXPECTED_NETWORK_ERROR);
141 0 : return;
142 : }
143 :
144 : /* Send off the second subreq here, this time to receive the reply */
145 1 : subreq = tdgram_recvfrom_send(state, state->ev, state->dgram);
146 1 : if (tevent_req_nomem(subreq, req)) {
147 0 : return;
148 : }
149 :
150 : /* And set the new callback */
151 1 : tevent_req_set_callback(subreq, echo_request_done, req);
152 1 : return;
153 : }
154 :
155 1 : static void echo_request_done(struct tevent_req *subreq)
156 : {
157 1 : struct tevent_req *req = tevent_req_callback_data(subreq,
158 : struct tevent_req);
159 1 : struct echo_request_state *state = tevent_req_data(req,
160 : struct echo_request_state);
161 :
162 0 : ssize_t len;
163 1 : int err = 0;
164 :
165 1 : len = tdgram_recvfrom_recv(subreq, &err, state,
166 1 : (uint8_t **)&state->message,
167 : NULL);
168 1 : TALLOC_FREE(subreq);
169 :
170 1 : if (len == -1 && err != 0) {
171 0 : tevent_req_nterror(req, map_nt_error_from_unix_common(err));
172 0 : return;
173 : }
174 :
175 1 : if (len != state->orig_len) {
176 0 : tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
177 0 : return;
178 : }
179 :
180 1 : state->message[len-1] = '\0';
181 : /* Once the async function has completed, set tevent_req_done() */
182 1 : tevent_req_done(req);
183 : }
184 :
185 : /*
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.
190 : */
191 :
192 1 : NTSTATUS echo_request_recv(struct tevent_req *req,
193 : TALLOC_CTX *mem_ctx,
194 : char **message)
195 : {
196 1 : struct echo_request_state *state = tevent_req_data(req,
197 : struct echo_request_state);
198 0 : NTSTATUS status;
199 :
200 1 : if (tevent_req_is_nterror(req, &status)) {
201 0 : tevent_req_received(req);
202 0 : return status;
203 : }
204 :
205 1 : *message = talloc_move(mem_ctx, &state->message);
206 1 : tevent_req_received(req);
207 :
208 1 : return NT_STATUS_OK;
209 : }
|