s3: Explicitly handle inbuf in cli_message_start_done
[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
22 struct cli_message_start_state {
23         uint16_t grp;
24 };
25
26 static void cli_message_start_done(struct tevent_req *subreq);
27
28 static struct tevent_req *cli_message_start_send(TALLOC_CTX *mem_ctx,
29                                                  struct tevent_context *ev,
30                                                  struct cli_state *cli,
31                                                  const char *host,
32                                                  const char *username)
33 {
34         struct tevent_req *req, *subreq;
35         struct cli_message_start_state *state;
36         char *htmp = NULL;
37         char *utmp = NULL;
38         size_t hlen, ulen;
39         uint8_t *bytes, *p;
40
41         req = tevent_req_create(mem_ctx, &state,
42                                 struct cli_message_start_state);
43         if (req == NULL) {
44                 return NULL;
45         }
46
47         if (!convert_string_talloc(talloc_tos(), CH_UNIX, CH_DOS,
48                                    username, strlen(username)+1,
49                                    &utmp, &ulen, true)) {
50                 goto fail;
51         }
52         if (!convert_string_talloc(talloc_tos(), CH_UNIX, CH_DOS,
53                                    host, strlen(host)+1,
54                                    &htmp, &hlen, true)) {
55                 goto fail;
56         }
57
58         bytes = talloc_array(state, uint8_t, ulen+hlen+2);
59         if (bytes == NULL) {
60                 goto fail;
61         }
62         p = bytes;
63
64         *p++ = 4;
65         memcpy(p, utmp, ulen);
66         *p++ = 4;
67         memcpy(p, htmp, hlen);
68         TALLOC_FREE(htmp);
69         TALLOC_FREE(utmp);
70
71         subreq = cli_smb_send(state, ev, cli, SMBsendstrt, 0, 0, NULL,
72                               talloc_get_size(bytes), bytes);
73         if (tevent_req_nomem(subreq, req)) {
74                 return tevent_req_post(req, ev);
75         }
76         tevent_req_set_callback(subreq, cli_message_start_done, req);
77         return req;
78 fail:
79         TALLOC_FREE(htmp);
80         TALLOC_FREE(utmp);
81         tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
82         return tevent_req_post(req, ev);
83 }
84
85 static void cli_message_start_done(struct tevent_req *subreq)
86 {
87         struct tevent_req *req = tevent_req_callback_data(
88                 subreq, struct tevent_req);
89         struct cli_message_start_state *state = tevent_req_data(
90                 req, struct cli_message_start_state);
91         NTSTATUS status;
92         uint8_t wct;
93         uint16_t *vwv;
94         uint8_t *inbuf;
95
96         status = cli_smb_recv(subreq, state, &inbuf, 0, &wct, &vwv,
97                               NULL, NULL);
98         TALLOC_FREE(subreq);
99         if (!NT_STATUS_IS_OK(status)) {
100                 TALLOC_FREE(subreq);
101                 tevent_req_nterror(req, status);
102                 return;
103         }
104         if (wct >= 1) {
105                 state->grp = SVAL(vwv+0, 0);
106         } else {
107                 state->grp = 0;
108         }
109         tevent_req_done(req);
110 }
111
112 static NTSTATUS cli_message_start_recv(struct tevent_req *req,
113                                        uint16_t *pgrp)
114 {
115         struct cli_message_start_state *state = tevent_req_data(
116                 req, struct cli_message_start_state);
117         NTSTATUS status;
118
119         if (tevent_req_is_nterror(req, &status)) {
120                 return status;
121         }
122         *pgrp = state->grp;
123         return NT_STATUS_OK;
124 }
125
126 struct cli_message_text_state {
127         uint16_t vwv;
128 };
129
130 static void cli_message_text_done(struct tevent_req *subreq);
131
132 static struct tevent_req *cli_message_text_send(TALLOC_CTX *mem_ctx,
133                                                 struct tevent_context *ev,
134                                                 struct cli_state *cli,
135                                                 uint16_t grp,
136                                                 const char *msg,
137                                                 int msglen)
138 {
139         struct tevent_req *req, *subreq;
140         struct cli_message_text_state *state;
141         char *tmp;
142         size_t tmplen;
143         uint8_t *bytes;
144
145         req = tevent_req_create(mem_ctx, &state,
146                                 struct cli_message_text_state);
147         if (req == NULL) {
148                 return NULL;
149         }
150
151         SSVAL(&state->vwv, 0, grp);
152
153         if (convert_string_talloc(talloc_tos(), CH_UNIX, CH_DOS, msg, msglen,
154                                   &tmp, &tmplen, true)) {
155                 msg = tmp;
156                 msglen = tmplen;
157         } else {
158                 DEBUG(3, ("Conversion failed, sending message in UNIX "
159                           "charset\n"));
160                 tmp = NULL;
161         }
162
163         bytes = talloc_array(state, uint8_t, msglen+3);
164         if (tevent_req_nomem(bytes, req)) {
165                 TALLOC_FREE(tmp);
166                 return tevent_req_post(req, ev);
167         }
168         SCVAL(bytes, 0, 0);     /* pad */
169         SSVAL(bytes, 1, msglen);
170         memcpy(bytes+3, msg, msglen);
171         TALLOC_FREE(tmp);
172
173         subreq = cli_smb_send(state, ev, cli, SMBsendtxt, 0, 1, &state->vwv,
174                               talloc_get_size(bytes), bytes);
175         if (tevent_req_nomem(subreq, req)) {
176                 return tevent_req_post(req, ev);
177         }
178         tevent_req_set_callback(subreq, cli_message_text_done, req);
179         return req;
180 }
181
182 static void cli_message_text_done(struct tevent_req *subreq)
183 {
184         struct tevent_req *req = tevent_req_callback_data(
185                 subreq, struct tevent_req);
186         NTSTATUS status;
187
188         status = cli_smb_recv(subreq, NULL, NULL, 0, NULL, NULL, NULL, NULL);
189         TALLOC_FREE(subreq);
190         if (!NT_STATUS_IS_OK(status)) {
191                 tevent_req_nterror(req, status);
192                 return;
193         }
194         tevent_req_done(req);
195 }
196
197 static NTSTATUS cli_message_text_recv(struct tevent_req *req)
198 {
199         return tevent_req_simple_recv_ntstatus(req);
200 }
201
202 struct cli_message_end_state {
203         uint16_t vwv;
204 };
205
206 static void cli_message_end_done(struct tevent_req *subreq);
207
208 static struct tevent_req *cli_message_end_send(TALLOC_CTX *mem_ctx,
209                                                 struct tevent_context *ev,
210                                                 struct cli_state *cli,
211                                                 uint16_t grp)
212 {
213         struct tevent_req *req, *subreq;
214         struct cli_message_end_state *state;
215
216         req = tevent_req_create(mem_ctx, &state,
217                                 struct cli_message_end_state);
218         if (req == NULL) {
219                 return NULL;
220         }
221
222         SSVAL(&state->vwv, 0, grp);
223
224         subreq = cli_smb_send(state, ev, cli, SMBsendend, 0, 1, &state->vwv,
225                               0, NULL);
226         if (tevent_req_nomem(subreq, req)) {
227                 return tevent_req_post(req, ev);
228         }
229         tevent_req_set_callback(subreq, cli_message_end_done, req);
230         return req;
231 }
232
233 static void cli_message_end_done(struct tevent_req *subreq)
234 {
235         struct tevent_req *req = tevent_req_callback_data(
236                 subreq, struct tevent_req);
237         NTSTATUS status;
238
239         status = cli_smb_recv(subreq, NULL, NULL, 0, NULL, NULL, NULL, NULL);
240         TALLOC_FREE(subreq);
241         if (!NT_STATUS_IS_OK(status)) {
242                 tevent_req_nterror(req, status);
243                 return;
244         }
245         tevent_req_done(req);
246 }
247
248 static NTSTATUS cli_message_end_recv(struct tevent_req *req)
249 {
250         return tevent_req_simple_recv_ntstatus(req);
251 }
252
253 struct cli_message_state {
254         struct tevent_context *ev;
255         struct cli_state *cli;
256         size_t sent;
257         const char *message;
258         uint16_t grp;
259 };
260
261 static void cli_message_started(struct tevent_req *subreq);
262 static void cli_message_sent(struct tevent_req *subreq);
263 static void cli_message_done(struct tevent_req *subreq);
264
265 struct tevent_req *cli_message_send(TALLOC_CTX *mem_ctx,
266                                     struct tevent_context *ev,
267                                     struct cli_state *cli,
268                                     const char *host, const char *username,
269                                     const char *message)
270 {
271         struct tevent_req *req, *subreq;
272         struct cli_message_state *state;
273
274         req = tevent_req_create(mem_ctx, &state, struct cli_message_state);
275         if (req == NULL) {
276                 return NULL;
277         }
278         state->ev = ev;
279         state->cli = cli;
280         state->sent = 0;
281         state->message = message;
282
283         subreq = cli_message_start_send(state, ev, cli, host, username);
284         if (tevent_req_nomem(subreq, req)) {
285                 return tevent_req_post(req, ev);
286         }
287         tevent_req_set_callback(subreq, cli_message_started, req);
288         return req;
289 }
290
291 static void cli_message_started(struct tevent_req *subreq)
292 {
293         struct tevent_req *req = tevent_req_callback_data(
294                 subreq, struct tevent_req);
295         struct cli_message_state *state = tevent_req_data(
296                 req, struct cli_message_state);
297         NTSTATUS status;
298         size_t thistime;
299
300         status = cli_message_start_recv(subreq, &state->grp);
301         TALLOC_FREE(subreq);
302         if (!NT_STATUS_IS_OK(status)) {
303                 tevent_req_nterror(req, status);
304                 return;
305         }
306
307         thistime = MIN(127, strlen(state->message));
308
309         subreq = cli_message_text_send(state, state->ev, state->cli,
310                                        state->grp, state->message, thistime);
311         if (tevent_req_nomem(subreq, req)) {
312                 return;
313         }
314         state->sent += thistime;
315         tevent_req_set_callback(subreq, cli_message_sent, req);
316 }
317
318 static void cli_message_sent(struct tevent_req *subreq)
319 {
320         struct tevent_req *req = tevent_req_callback_data(
321                 subreq, struct tevent_req);
322         struct cli_message_state *state = tevent_req_data(
323                 req, struct cli_message_state);
324         NTSTATUS status;
325         size_t left, thistime;
326
327         status = cli_message_text_recv(subreq);
328         TALLOC_FREE(subreq);
329         if (!NT_STATUS_IS_OK(status)) {
330                 tevent_req_nterror(req, status);
331                 return;
332         }
333
334         if (state->sent >= strlen(state->message)) {
335                 subreq = cli_message_end_send(state, state->ev, state->cli,
336                                               state->grp);
337                 if (tevent_req_nomem(subreq, req)) {
338                         return;
339                 }
340                 tevent_req_set_callback(subreq, cli_message_done, req);
341                 return;
342         }
343
344         left = strlen(state->message) - state->sent;
345         thistime = MIN(127, left);
346
347         subreq = cli_message_text_send(state, state->ev, state->cli,
348                                        state->grp,
349                                        state->message + state->sent,
350                                        thistime);
351         if (tevent_req_nomem(subreq, req)) {
352                 return;
353         }
354         state->sent += thistime;
355         tevent_req_set_callback(subreq, cli_message_sent, req);
356 }
357
358 static void cli_message_done(struct tevent_req *subreq)
359 {
360         struct tevent_req *req = tevent_req_callback_data(
361                 subreq, struct tevent_req);
362         NTSTATUS status;
363
364         status = cli_message_end_recv(subreq);
365         TALLOC_FREE(subreq);
366         if (!NT_STATUS_IS_OK(status)) {
367                 tevent_req_nterror(req, status);
368                 return;
369         }
370         tevent_req_done(req);
371 }
372
373 NTSTATUS cli_message_recv(struct tevent_req *req)
374 {
375         return tevent_req_simple_recv_ntstatus(req);
376 }
377
378 NTSTATUS cli_message(struct cli_state *cli, const char *host,
379                      const char *username, const char *message)
380 {
381         TALLOC_CTX *frame = talloc_stackframe();
382         struct event_context *ev;
383         struct tevent_req *req;
384         NTSTATUS status = NT_STATUS_OK;
385
386         if (cli_has_async_calls(cli)) {
387                 /*
388                  * Can't use sync call while an async call is in flight
389                  */
390                 status = NT_STATUS_INVALID_PARAMETER;
391                 goto fail;
392         }
393
394         ev = event_context_init(frame);
395         if (ev == NULL) {
396                 status = NT_STATUS_NO_MEMORY;
397                 goto fail;
398         }
399
400         req = cli_message_send(frame, ev, cli, host, username, message);
401         if (req == NULL) {
402                 status = NT_STATUS_NO_MEMORY;
403                 goto fail;
404         }
405
406         if (!tevent_req_poll(req, ev)) {
407                 status = map_nt_error_from_unix(errno);
408                 goto fail;
409         }
410
411         status = cli_message_recv(req);
412  fail:
413         TALLOC_FREE(frame);
414         return status;
415 }