s3: smbd: In aio_del_req_from_fsp() talloc_free(fsp->aio_requests[]) when fsp->num_ai...
[amitay/samba.git] / source3 / libsmb / climessage.c
1 /*
2    Unix SMB/CIFS implementation.
3    client message handling routines
4    Copyright (C) Andrew Tridgell 1994-1998
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "includes.h"
21 #include "../lib/util/tevent_ntstatus.h"
22 #include "async_smb.h"
23 #include "libsmb/libsmb.h"
24 #include "../libcli/smb/smbXcli_base.h"
25
26 struct cli_message_start_state {
27         uint16_t grp;
28 };
29
30 static void cli_message_start_done(struct tevent_req *subreq);
31
32 static struct tevent_req *cli_message_start_send(TALLOC_CTX *mem_ctx,
33                                                  struct tevent_context *ev,
34                                                  struct cli_state *cli,
35                                                  const char *host,
36                                                  const char *username)
37 {
38         struct tevent_req *req, *subreq;
39         struct cli_message_start_state *state;
40         char *htmp = NULL;
41         char *utmp = NULL;
42         size_t hlen, ulen;
43         uint8_t *bytes, *p;
44
45         req = tevent_req_create(mem_ctx, &state,
46                                 struct cli_message_start_state);
47         if (req == NULL) {
48                 return NULL;
49         }
50
51         if (!convert_string_talloc(talloc_tos(), CH_UNIX, CH_DOS,
52                                    username, strlen(username)+1,
53                                    &utmp, &ulen)) {
54                 goto fail;
55         }
56         if (!convert_string_talloc(talloc_tos(), CH_UNIX, CH_DOS,
57                                    host, strlen(host)+1,
58                                    &htmp, &hlen)) {
59                 goto fail;
60         }
61
62         bytes = talloc_array(state, uint8_t, ulen+hlen+2);
63         if (bytes == NULL) {
64                 goto fail;
65         }
66         p = bytes;
67
68         *p++ = 4;
69         memcpy(p, utmp, ulen);
70         p += ulen;
71         *p++ = 4;
72         memcpy(p, htmp, hlen);
73         TALLOC_FREE(htmp);
74         TALLOC_FREE(utmp);
75
76         subreq = cli_smb_send(state, ev, cli, SMBsendstrt, 0, 0, 0, NULL,
77                               talloc_get_size(bytes), bytes);
78         if (tevent_req_nomem(subreq, req)) {
79                 return tevent_req_post(req, ev);
80         }
81         tevent_req_set_callback(subreq, cli_message_start_done, req);
82         return req;
83 fail:
84         TALLOC_FREE(htmp);
85         TALLOC_FREE(utmp);
86         tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
87         return tevent_req_post(req, ev);
88 }
89
90 static void cli_message_start_done(struct tevent_req *subreq)
91 {
92         struct tevent_req *req = tevent_req_callback_data(
93                 subreq, struct tevent_req);
94         struct cli_message_start_state *state = tevent_req_data(
95                 req, struct cli_message_start_state);
96         NTSTATUS status;
97         uint8_t wct;
98         uint16_t *vwv;
99
100         status = cli_smb_recv(subreq, state, NULL, 0, &wct, &vwv,
101                               NULL, NULL);
102         TALLOC_FREE(subreq);
103         if (!NT_STATUS_IS_OK(status)) {
104                 TALLOC_FREE(subreq);
105                 tevent_req_nterror(req, status);
106                 return;
107         }
108         if (wct >= 1) {
109                 state->grp = SVAL(vwv+0, 0);
110         } else {
111                 state->grp = 0;
112         }
113         tevent_req_done(req);
114 }
115
116 static NTSTATUS cli_message_start_recv(struct tevent_req *req,
117                                        uint16_t *pgrp)
118 {
119         struct cli_message_start_state *state = tevent_req_data(
120                 req, struct cli_message_start_state);
121         NTSTATUS status;
122
123         if (tevent_req_is_nterror(req, &status)) {
124                 return status;
125         }
126         *pgrp = state->grp;
127         return NT_STATUS_OK;
128 }
129
130 struct cli_message_text_state {
131         uint16_t vwv;
132 };
133
134 static void cli_message_text_done(struct tevent_req *subreq);
135
136 static struct tevent_req *cli_message_text_send(TALLOC_CTX *mem_ctx,
137                                                 struct tevent_context *ev,
138                                                 struct cli_state *cli,
139                                                 uint16_t grp,
140                                                 const char *msg,
141                                                 int msglen)
142 {
143         struct tevent_req *req, *subreq;
144         struct cli_message_text_state *state;
145         char *tmp;
146         size_t tmplen;
147         uint8_t *bytes;
148
149         req = tevent_req_create(mem_ctx, &state,
150                                 struct cli_message_text_state);
151         if (req == NULL) {
152                 return NULL;
153         }
154
155         SSVAL(&state->vwv, 0, grp);
156
157         if (convert_string_talloc(talloc_tos(), CH_UNIX, CH_DOS, msg, msglen,
158                                   &tmp, &tmplen)) {
159                 msg = tmp;
160                 msglen = tmplen;
161         } else {
162                 DEBUG(3, ("Conversion failed, sending message in UNIX "
163                           "charset\n"));
164                 tmp = NULL;
165         }
166
167         bytes = talloc_array(state, uint8_t, msglen+3);
168         if (tevent_req_nomem(bytes, req)) {
169                 TALLOC_FREE(tmp);
170                 return tevent_req_post(req, ev);
171         }
172         SCVAL(bytes, 0, 1);     /* pad */
173         SSVAL(bytes+1, 0, msglen);
174         memcpy(bytes+3, msg, msglen);
175         TALLOC_FREE(tmp);
176
177         subreq = cli_smb_send(state, ev, cli, SMBsendtxt, 0, 0, 1, &state->vwv,
178                               talloc_get_size(bytes), bytes);
179         if (tevent_req_nomem(subreq, req)) {
180                 return tevent_req_post(req, ev);
181         }
182         tevent_req_set_callback(subreq, cli_message_text_done, req);
183         return req;
184 }
185
186 static void cli_message_text_done(struct tevent_req *subreq)
187 {
188         struct tevent_req *req = tevent_req_callback_data(
189                 subreq, struct tevent_req);
190         NTSTATUS status;
191
192         status = cli_smb_recv(subreq, NULL, NULL, 0, NULL, NULL, NULL, NULL);
193         TALLOC_FREE(subreq);
194         if (!NT_STATUS_IS_OK(status)) {
195                 tevent_req_nterror(req, status);
196                 return;
197         }
198         tevent_req_done(req);
199 }
200
201 static NTSTATUS cli_message_text_recv(struct tevent_req *req)
202 {
203         return tevent_req_simple_recv_ntstatus(req);
204 }
205
206 struct cli_message_end_state {
207         uint16_t vwv;
208 };
209
210 static void cli_message_end_done(struct tevent_req *subreq);
211
212 static struct tevent_req *cli_message_end_send(TALLOC_CTX *mem_ctx,
213                                                 struct tevent_context *ev,
214                                                 struct cli_state *cli,
215                                                 uint16_t grp)
216 {
217         struct tevent_req *req, *subreq;
218         struct cli_message_end_state *state;
219
220         req = tevent_req_create(mem_ctx, &state,
221                                 struct cli_message_end_state);
222         if (req == NULL) {
223                 return NULL;
224         }
225
226         SSVAL(&state->vwv, 0, grp);
227
228         subreq = cli_smb_send(state, ev, cli, SMBsendend, 0, 0, 1, &state->vwv,
229                               0, NULL);
230         if (tevent_req_nomem(subreq, req)) {
231                 return tevent_req_post(req, ev);
232         }
233         tevent_req_set_callback(subreq, cli_message_end_done, req);
234         return req;
235 }
236
237 static void cli_message_end_done(struct tevent_req *subreq)
238 {
239         struct tevent_req *req = tevent_req_callback_data(
240                 subreq, struct tevent_req);
241         NTSTATUS status;
242
243         status = cli_smb_recv(subreq, NULL, NULL, 0, NULL, NULL, NULL, NULL);
244         TALLOC_FREE(subreq);
245         if (!NT_STATUS_IS_OK(status)) {
246                 tevent_req_nterror(req, status);
247                 return;
248         }
249         tevent_req_done(req);
250 }
251
252 static NTSTATUS cli_message_end_recv(struct tevent_req *req)
253 {
254         return tevent_req_simple_recv_ntstatus(req);
255 }
256
257 struct cli_message_state {
258         struct tevent_context *ev;
259         struct cli_state *cli;
260         size_t sent;
261         const char *message;
262         uint16_t grp;
263 };
264
265 static void cli_message_started(struct tevent_req *subreq);
266 static void cli_message_sent(struct tevent_req *subreq);
267 static void cli_message_done(struct tevent_req *subreq);
268
269 struct tevent_req *cli_message_send(TALLOC_CTX *mem_ctx,
270                                     struct tevent_context *ev,
271                                     struct cli_state *cli,
272                                     const char *host, const char *username,
273                                     const char *message)
274 {
275         struct tevent_req *req, *subreq;
276         struct cli_message_state *state;
277
278         req = tevent_req_create(mem_ctx, &state, struct cli_message_state);
279         if (req == NULL) {
280                 return NULL;
281         }
282         state->ev = ev;
283         state->cli = cli;
284         state->sent = 0;
285         state->message = message;
286
287         subreq = cli_message_start_send(state, ev, cli, host, username);
288         if (tevent_req_nomem(subreq, req)) {
289                 return tevent_req_post(req, ev);
290         }
291         tevent_req_set_callback(subreq, cli_message_started, req);
292         return req;
293 }
294
295 static void cli_message_started(struct tevent_req *subreq)
296 {
297         struct tevent_req *req = tevent_req_callback_data(
298                 subreq, struct tevent_req);
299         struct cli_message_state *state = tevent_req_data(
300                 req, struct cli_message_state);
301         NTSTATUS status;
302         size_t thistime;
303
304         status = cli_message_start_recv(subreq, &state->grp);
305         TALLOC_FREE(subreq);
306         if (!NT_STATUS_IS_OK(status)) {
307                 tevent_req_nterror(req, status);
308                 return;
309         }
310
311         thistime = MIN(127, strlen(state->message));
312
313         subreq = cli_message_text_send(state, state->ev, state->cli,
314                                        state->grp, state->message, thistime);
315         if (tevent_req_nomem(subreq, req)) {
316                 return;
317         }
318         state->sent += thistime;
319         tevent_req_set_callback(subreq, cli_message_sent, req);
320 }
321
322 static void cli_message_sent(struct tevent_req *subreq)
323 {
324         struct tevent_req *req = tevent_req_callback_data(
325                 subreq, struct tevent_req);
326         struct cli_message_state *state = tevent_req_data(
327                 req, struct cli_message_state);
328         NTSTATUS status;
329         size_t left, thistime;
330
331         status = cli_message_text_recv(subreq);
332         TALLOC_FREE(subreq);
333         if (!NT_STATUS_IS_OK(status)) {
334                 tevent_req_nterror(req, status);
335                 return;
336         }
337
338         if (state->sent >= strlen(state->message)) {
339                 subreq = cli_message_end_send(state, state->ev, state->cli,
340                                               state->grp);
341                 if (tevent_req_nomem(subreq, req)) {
342                         return;
343                 }
344                 tevent_req_set_callback(subreq, cli_message_done, req);
345                 return;
346         }
347
348         left = strlen(state->message) - state->sent;
349         thistime = MIN(127, left);
350
351         subreq = cli_message_text_send(state, state->ev, state->cli,
352                                        state->grp,
353                                        state->message + state->sent,
354                                        thistime);
355         if (tevent_req_nomem(subreq, req)) {
356                 return;
357         }
358         state->sent += thistime;
359         tevent_req_set_callback(subreq, cli_message_sent, req);
360 }
361
362 static void cli_message_done(struct tevent_req *subreq)
363 {
364         struct tevent_req *req = tevent_req_callback_data(
365                 subreq, struct tevent_req);
366         NTSTATUS status;
367
368         status = cli_message_end_recv(subreq);
369         TALLOC_FREE(subreq);
370         if (!NT_STATUS_IS_OK(status)) {
371                 tevent_req_nterror(req, status);
372                 return;
373         }
374         tevent_req_done(req);
375 }
376
377 NTSTATUS cli_message_recv(struct tevent_req *req)
378 {
379         return tevent_req_simple_recv_ntstatus(req);
380 }
381
382 NTSTATUS cli_message(struct cli_state *cli, const char *host,
383                      const char *username, const char *message)
384 {
385         TALLOC_CTX *frame = talloc_stackframe();
386         struct tevent_context *ev;
387         struct tevent_req *req;
388         NTSTATUS status = NT_STATUS_OK;
389
390         if (smbXcli_conn_has_async_calls(cli->conn)) {
391                 /*
392                  * Can't use sync call while an async call is in flight
393                  */
394                 status = NT_STATUS_INVALID_PARAMETER;
395                 goto fail;
396         }
397
398         ev = samba_tevent_context_init(frame);
399         if (ev == NULL) {
400                 status = NT_STATUS_NO_MEMORY;
401                 goto fail;
402         }
403
404         req = cli_message_send(frame, ev, cli, host, username, message);
405         if (req == NULL) {
406                 status = NT_STATUS_NO_MEMORY;
407                 goto fail;
408         }
409
410         if (!tevent_req_poll_ntstatus(req, ev, &status)) {
411                 goto fail;
412         }
413
414         status = cli_message_recv(req);
415  fail:
416         TALLOC_FREE(frame);
417         return status;
418 }