/* Unix SMB/CIFS implementation. raw trans/trans2/nttrans operations Copyright (C) James Myers 2003 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "includes.h" #include #include "libcli/raw/libcliraw.h" #include "libcli/raw/raw_proto.h" #include "../libcli/smb/smbXcli_base.h" static void smb_raw_trans_backend_done(struct tevent_req *subreq); static struct smbcli_request *smb_raw_trans_backend_send(struct smbcli_tree *tree, struct smb_trans2 *parms, uint8_t command) { struct smbcli_request *req; uint8_t additional_flags; uint8_t clear_flags; uint16_t additional_flags2; uint16_t clear_flags2; uint32_t pid; struct smbXcli_tcon *tcon = NULL; struct smbXcli_session *session = NULL; const char *pipe_name = NULL; uint8_t s; uint32_t timeout_msec; uint32_t tmp; tmp = parms->in.params.length + parms->in.data.length; req = smbcli_request_setup(tree, command, parms->in.setup_count, tmp); if (req == NULL) { return NULL; } additional_flags = CVAL(req->out.hdr, HDR_FLG); additional_flags2 = SVAL(req->out.hdr, HDR_FLG2); pid = SVAL(req->out.hdr, HDR_PID); pid |= SVAL(req->out.hdr, HDR_PIDHIGH)<<16; if (req->session) { session = req->session->smbXcli; } if (req->tree) { tcon = req->tree->smbXcli; } clear_flags = ~additional_flags; clear_flags2 = ~additional_flags2; timeout_msec = req->transport->options.request_timeout * 1000; for (s=0; s < parms->in.setup_count; s++) { SSVAL(req->out.vwv, VWV(s), parms->in.setup[s]); } if (parms->in.params.length > 0) { memcpy(req->out.data, parms->in.params.data, parms->in.params.length); } if (parms->in.data.length > 0) { memcpy(req->out.data + parms->in.params.length, parms->in.data.data, parms->in.data.length); } if (command == SMBtrans && parms->in.trans_name) { pipe_name = parms->in.trans_name; } req->subreqs[0] = smb1cli_trans_send(req, req->transport->ev, req->transport->conn, command, additional_flags, clear_flags, additional_flags2, clear_flags2, timeout_msec, pid, tcon, session, pipe_name, 0xFFFF, /* fid */ 0, /* function */ parms->in.flags, (uint16_t *)req->out.vwv, parms->in.setup_count, parms->in.max_setup, req->out.data, parms->in.params.length, parms->in.max_param, req->out.data+ parms->in.params.length, parms->in.data.length, parms->in.max_data); if (req->subreqs[0] == NULL) { talloc_free(req); return NULL; } tevent_req_set_callback(req->subreqs[0], smb_raw_trans_backend_done, req); return req; } static void smb_raw_trans_backend_done(struct tevent_req *subreq) { struct smbcli_request *req = tevent_req_callback_data(subreq, struct smbcli_request); struct smbcli_transport *transport = req->transport; uint16_t *setup = NULL; uint8_t num_setup = 0; uint8_t s; uint8_t *param = NULL; uint32_t num_param = 0; uint8_t *data = NULL; uint32_t num_data = 0; req->status = smb1cli_trans_recv(req->subreqs[0], req, &req->flags2, &setup, 0, /* min_setup */ &num_setup, ¶m, 0, /* min_param */ &num_param, &data, 0, /* min_data */ &num_data); TALLOC_FREE(req->subreqs[0]); if (NT_STATUS_IS_ERR(req->status)) { req->state = SMBCLI_REQUEST_ERROR; transport->error.e.nt_status = req->status; transport->error.etype = ETYPE_SMB; if (req->async.fn) { req->async.fn(req); } return; } req->trans2.out.setup_count = num_setup; req->trans2.out.setup = talloc_array(req, uint16_t, num_setup); if (req->trans2.out.setup == NULL) { req->state = SMBCLI_REQUEST_ERROR; req->status = NT_STATUS_NO_MEMORY; transport->error.e.nt_status = req->status; transport->error.etype = ETYPE_SMB; if (req->async.fn) { req->async.fn(req); } return; } for (s = 0; s < num_setup; s++) { req->trans2.out.setup[s] = SVAL(setup, VWV(s)); } req->trans2.out.params.data = param; req->trans2.out.params.length = num_param; req->trans2.out.data.data = data; req->trans2.out.data.length = num_data; transport->error.e.nt_status = req->status; if (NT_STATUS_IS_OK(req->status)) { transport->error.etype = ETYPE_NONE; } else { transport->error.etype = ETYPE_SMB; } req->state = SMBCLI_REQUEST_DONE; if (req->async.fn) { req->async.fn(req); } } static NTSTATUS smb_raw_trans_backend_recv(struct smbcli_request *req, TALLOC_CTX *mem_ctx, struct smb_trans2 *parms) { if (!smbcli_request_receive(req) || smbcli_request_is_error(req)) { goto failed; } parms->out = req->trans2.out; talloc_steal(mem_ctx, parms->out.setup); talloc_steal(mem_ctx, parms->out.params.data); talloc_steal(mem_ctx, parms->out.data.data); failed: return smbcli_request_destroy(req); } _PUBLIC_ struct smbcli_request *smb_raw_trans_send(struct smbcli_tree *tree, struct smb_trans2 *parms) { return smb_raw_trans_backend_send(tree, parms, SMBtrans); } _PUBLIC_ NTSTATUS smb_raw_trans_recv(struct smbcli_request *req, TALLOC_CTX *mem_ctx, struct smb_trans2 *parms) { return smb_raw_trans_backend_recv(req, mem_ctx, parms); } _PUBLIC_ NTSTATUS smb_raw_trans(struct smbcli_tree *tree, TALLOC_CTX *mem_ctx, struct smb_trans2 *parms) { struct smbcli_request *req; req = smb_raw_trans_send(tree, parms); if (!req) return NT_STATUS_UNSUCCESSFUL; return smb_raw_trans_recv(req, mem_ctx, parms); } struct smbcli_request *smb_raw_trans2_send(struct smbcli_tree *tree, struct smb_trans2 *parms) { return smb_raw_trans_backend_send(tree, parms, SMBtrans2); } NTSTATUS smb_raw_trans2_recv(struct smbcli_request *req, TALLOC_CTX *mem_ctx, struct smb_trans2 *parms) { return smb_raw_trans_backend_recv(req, mem_ctx, parms); } NTSTATUS smb_raw_trans2(struct smbcli_tree *tree, TALLOC_CTX *mem_ctx, struct smb_trans2 *parms) { struct smbcli_request *req; req = smb_raw_trans2_send(tree, parms); if (!req) return NT_STATUS_UNSUCCESSFUL; return smb_raw_trans2_recv(req, mem_ctx, parms); } static void smb_raw_nttrans_done(struct tevent_req *subreq); struct smbcli_request *smb_raw_nttrans_send(struct smbcli_tree *tree, struct smb_nttrans *parms) { struct smbcli_request *req; uint8_t additional_flags; uint8_t clear_flags; uint16_t additional_flags2; uint16_t clear_flags2; uint32_t pid; struct smbXcli_tcon *tcon = NULL; struct smbXcli_session *session = NULL; uint32_t timeout_msec; uint32_t tmp; tmp = parms->in.params.length + parms->in.data.length; req = smbcli_request_setup(tree, SMBnttrans, parms->in.setup_count, tmp); if (req == NULL) { return NULL; } additional_flags = CVAL(req->out.hdr, HDR_FLG); additional_flags2 = SVAL(req->out.hdr, HDR_FLG2); pid = SVAL(req->out.hdr, HDR_PID); pid |= SVAL(req->out.hdr, HDR_PIDHIGH)<<16; if (req->session) { session = req->session->smbXcli; } if (req->tree) { tcon = req->tree->smbXcli; } clear_flags = ~additional_flags; clear_flags2 = ~additional_flags2; timeout_msec = req->transport->options.request_timeout * 1000; if (parms->in.setup_count > 0) { memcpy( req->out.vwv, parms->in.setup, parms->in.setup_count * 2); } if (parms->in.params.length > 0) { memcpy(req->out.data, parms->in.params.data, parms->in.params.length); } if (parms->in.data.length > 0) { memcpy(req->out.data + parms->in.params.length, parms->in.data.data, parms->in.data.length); } req->subreqs[0] = smb1cli_trans_send(req, req->transport->ev, req->transport->conn, SMBnttrans, additional_flags, clear_flags, additional_flags2, clear_flags2, timeout_msec, pid, tcon, session, NULL, /* pipe_name */ 0xFFFF, /* fid */ parms->in.function, 0, /* flags */ (uint16_t *)req->out.vwv, parms->in.setup_count, parms->in.max_setup, req->out.data, parms->in.params.length, parms->in.max_param, req->out.data+ parms->in.params.length, parms->in.data.length, parms->in.max_data); if (req->subreqs[0] == NULL) { talloc_free(req); return NULL; } tevent_req_set_callback(req->subreqs[0], smb_raw_nttrans_done, req); return req; } static void smb_raw_nttrans_done(struct tevent_req *subreq) { struct smbcli_request *req = tevent_req_callback_data(subreq, struct smbcli_request); struct smbcli_transport *transport = req->transport; uint16_t *setup = NULL; uint8_t num_setup = 0; uint8_t *param = NULL; uint32_t num_param = 0; uint8_t *data = NULL; uint32_t num_data = 0; req->status = smb1cli_trans_recv(req->subreqs[0], req, &req->flags2, &setup, 0, /* min_setup */ &num_setup, ¶m, 0, /* min_param */ &num_param, &data, 0, /* min_data */ &num_data); TALLOC_FREE(req->subreqs[0]); if (NT_STATUS_IS_ERR(req->status)) { req->state = SMBCLI_REQUEST_ERROR; transport->error.e.nt_status = req->status; transport->error.etype = ETYPE_SMB; if (req->async.fn) { req->async.fn(req); } return; } req->nttrans.out.setup_count = num_setup; req->nttrans.out.setup = (uint8_t *)setup; req->nttrans.out.params.data = param; req->nttrans.out.params.length = num_param; req->nttrans.out.data.data = data; req->nttrans.out.data.length = num_data; transport->error.e.nt_status = req->status; if (NT_STATUS_IS_OK(req->status)) { transport->error.etype = ETYPE_NONE; } else { transport->error.etype = ETYPE_SMB; } req->state = SMBCLI_REQUEST_DONE; if (req->async.fn) { req->async.fn(req); } } NTSTATUS smb_raw_nttrans_recv(struct smbcli_request *req, TALLOC_CTX *mem_ctx, struct smb_nttrans *parms) { if (!smbcli_request_receive(req) || smbcli_request_is_error(req)) { goto failed; } parms->out = req->nttrans.out; talloc_steal(mem_ctx, parms->out.setup); talloc_steal(mem_ctx, parms->out.params.data); talloc_steal(mem_ctx, parms->out.data.data); failed: return smbcli_request_destroy(req); } /**************************************************************************** receive a SMB nttrans response allocating the necessary memory ****************************************************************************/ NTSTATUS smb_raw_nttrans(struct smbcli_tree *tree, TALLOC_CTX *mem_ctx, struct smb_nttrans *parms) { struct smbcli_request *req; req = smb_raw_nttrans_send(tree, parms); if (!req) { return NT_STATUS_UNSUCCESSFUL; } return smb_raw_nttrans_recv(req, mem_ctx, parms); }