libcli/echo: validate the message length
[nivanova/samba-autobuild/.git] / libcli / echo / echo.c
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 }