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 22struct cli_message_start_state { 23 uint16_t grp; 24}; 25 26static void cli_message_start_done(struct tevent_req *subreq); 27 28static 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 += ulen; 67 *p++ = 4; 68 memcpy(p, htmp, hlen); 69 p += hlen; 70 TALLOC_FREE(htmp); 71 TALLOC_FREE(utmp); 72 73 subreq = cli_smb_send(state, ev, cli, SMBsendstrt, 0, 0, NULL, 74 talloc_get_size(bytes), bytes); 75 if (tevent_req_nomem(subreq, req)) { 76 return tevent_req_post(req, ev); 77 } 78 tevent_req_set_callback(subreq, cli_message_start_done, req); 79 return req; 80fail: 81 TALLOC_FREE(htmp); 82 TALLOC_FREE(utmp); 83 tevent_req_nterror(req, NT_STATUS_NO_MEMORY); 84 return tevent_req_post(req, ev); 85} 86 87static void cli_message_start_done(struct tevent_req *subreq) 88{ 89 struct tevent_req *req = tevent_req_callback_data( 90 subreq, struct tevent_req); 91 struct cli_message_start_state *state = tevent_req_data( 92 req, struct cli_message_start_state); 93 NTSTATUS status; 94 uint8_t wct; 95 uint16_t *vwv; 96 97 status = cli_smb_recv(subreq, 0, &wct, &vwv, NULL, NULL); 98 if (!NT_STATUS_IS_OK(status)) { 99 TALLOC_FREE(subreq); 100 tevent_req_nterror(req, status); 101 return; 102 } 103 if (wct >= 1) { 104 state->grp = SVAL(vwv+0, 0); 105 } else { 106 state->grp = 0; 107 } 108 TALLOC_FREE(subreq); 109 tevent_req_done(req); 110} 111 112static 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 126struct cli_message_text_state { 127 uint16_t vwv; 128}; 129 130static void cli_message_text_done(struct tevent_req *subreq); 131 132static 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, 1); /* pad */ 169 SSVAL(bytes+1, 0, 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 182static 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, 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 197static NTSTATUS cli_message_text_recv(struct tevent_req *req) 198{ 199 return tevent_req_simple_recv_ntstatus(req); 200} 201 202struct cli_message_end_state { 203 uint16_t vwv; 204}; 205 206static void cli_message_end_done(struct tevent_req *subreq); 207 208static 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 233static 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, 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 248static NTSTATUS cli_message_end_recv(struct tevent_req *req) 249{ 250 return tevent_req_simple_recv_ntstatus(req); 251} 252 253struct 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 261static void cli_message_started(struct tevent_req *subreq); 262static void cli_message_sent(struct tevent_req *subreq); 263static void cli_message_done(struct tevent_req *subreq); 264 265struct 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 291static 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 318static 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 358static 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 373NTSTATUS cli_message_recv(struct tevent_req *req) 374{ 375 return tevent_req_simple_recv_ntstatus(req); 376} 377 378NTSTATUS 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} 416