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