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 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 	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;
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 	req = tevent_req_create(mem_ctx, &state, struct echo_request_state);
64 	if (req == NULL) {
65 		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 	state->ev = ev;
73 
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_common(ret));
79 		return tevent_req_post(req, ev);
80 	}
81 
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_common(ret));
86 		return tevent_req_post(req, ev);
87 	}
88 
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_common(ret));
92 		return tevent_req_post(req, ev);
93 	}
94 
95 	state->dgram = dgram;
96 	state->orig_len = strlen(message) + 1;
97 
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);
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 	tevent_req_set_callback(subreq, echo_request_get_reply, req);
112 	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 static void echo_request_get_reply(struct tevent_req *subreq)
121 {
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;
130 
131 	len = tdgram_sendto_recv(subreq, &err);
132 	TALLOC_FREE(subreq);
133 
134 	if (len == -1 && err != 0) {
135 		tevent_req_nterror(req, map_nt_error_from_unix_common(err));
136 		return;
137 	}
138 
139 	if (len != state->orig_len) {
140 		tevent_req_nterror(req, NT_STATUS_UNEXPECTED_NETWORK_ERROR);
141 		return;
142 	}
143 
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;
148 	}
149 
150 	/* And set the new callback */
151 	tevent_req_set_callback(subreq, echo_request_done, req);
152 	return;
153 }
154 
155 static void echo_request_done(struct tevent_req *subreq)
156 {
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);
161 
162 	ssize_t len;
163 	int err = 0;
164 
165 	len = tdgram_recvfrom_recv(subreq, &err, state,
166 				   (uint8_t **)&state->message,
167 				   NULL);
168 	TALLOC_FREE(subreq);
169 
170 	if (len == -1 && err != 0) {
171 		tevent_req_nterror(req, map_nt_error_from_unix_common(err));
172 		return;
173 	}
174 
175 	if (len != state->orig_len) {
176 		tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
177 		return;
178 	}
179 
180 	state->message[len-1] = '\0';
181 	/* Once the async function has completed, set tevent_req_done() */
182 	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 NTSTATUS echo_request_recv(struct tevent_req *req,
193 			   TALLOC_CTX *mem_ctx,
194 			   char **message)
195 {
196 	struct echo_request_state *state = tevent_req_data(req,
197 			struct echo_request_state);
198 	NTSTATUS status;
199 
200 	if (tevent_req_is_nterror(req, &status)) {
201 		tevent_req_received(req);
202 		return status;
203 	}
204 
205 	*message = talloc_move(mem_ctx, &state->message);
206 	tevent_req_received(req);
207 
208 	return NT_STATUS_OK;
209 }
210