/* append raw bytes into the data portion of the request packet return the number of bytes added */ size_t req_append_bytes(struct smbsrv_request *req, const uint8_t *bytes, size_t byte_len) { req_grow_allocation(req, byte_len + req->out.data_size); memcpy(req->out.data + req->out.data_size, bytes, byte_len); req_grow_data(req, byte_len + req->out.data_size); return byte_len; }
/* append variable block (type 5 buffer) into the data portion of the request packet return the number of bytes added */ size_t req_append_var_block(struct smbsrv_request *req, const uint8_t *bytes, uint16_t byte_len) { req_grow_allocation(req, byte_len + 3 + req->out.data_size); SCVAL(req->out.data + req->out.data_size, 0, 5); SSVAL(req->out.data + req->out.data_size, 1, byte_len); /* add field length */ if (byte_len > 0) { memcpy(req->out.data + req->out.data_size + 3, bytes, byte_len); } req_grow_data(req, byte_len + 3 + req->out.data_size); return byte_len + 3; }
/* construct and send an error packet, then destroy the request auto-converts to DOS error format when appropriate */ void smbsrv_send_error(struct smbsrv_request *req, NTSTATUS status) { if (req->smb_conn->connection->event.fde == NULL) { /* the socket has been destroyed - no point trying to send an error! */ talloc_free(req); return; } smbsrv_setup_reply(req, 0, 0); /* error returns never have any data */ req_grow_data(req, 0); smbsrv_setup_error(req, status); smbsrv_send_reply(req); }
/* push a string into the data portion of the request packet, growing it if necessary this gets quite tricky - please be very careful to cover all cases when modifying this if dest is NULL, then put the string at the end of the data portion of the packet if dest_len is -1 then no limit applies */ size_t req_push_str(struct smbsrv_request *req, uint8_t *dest, const char *str, int dest_len, size_t flags) { size_t len; unsigned int grow_size; uint8_t *buf0; const int max_bytes_per_char = 3; if (!(flags & (STR_ASCII|STR_UNICODE))) { flags |= (req->flags2 & FLAGS2_UNICODE_STRINGS) ? STR_UNICODE : STR_ASCII; } if (dest == NULL) { dest = req->out.data + req->out.data_size; } if (dest_len != -1) { len = dest_len; } else { len = (strlen(str)+2) * max_bytes_per_char; } grow_size = len + PTR_DIFF(dest, req->out.data); buf0 = req->out.buffer; req_grow_allocation(req, grow_size); if (buf0 != req->out.buffer) { dest = req->out.buffer + PTR_DIFF(dest, buf0); } len = push_string(dest, str, len, flags); grow_size = len + PTR_DIFF(dest, req->out.data); if (grow_size > req->out.data_size) { req_grow_data(req, grow_size); } return len; }
/* answer a reconstructed trans request */ static void reply_trans_send(struct ntvfs_request *ntvfs) { struct smbsrv_request *req; struct trans_op *op; struct smb_trans2 *trans; uint16_t params_left, data_left; uint8_t *params, *data; int i; SMBSRV_CHECK_ASYNC_STATUS_ERR(op, struct trans_op); trans = op->trans; /* if this function needs work to form the nttrans reply buffer, then call that now */ if (op->send_fn != NULL) { NTSTATUS status; status = op->send_fn(op); if (!NT_STATUS_IS_OK(status)) { smbsrv_send_error(req, status); return; } } params_left = trans->out.params.length; data_left = trans->out.data.length; params = trans->out.params.data; data = trans->out.data.data; smbsrv_setup_reply(req, 10 + trans->out.setup_count, 0); if (!NT_STATUS_IS_OK(req->ntvfs->async_states->status)) { smbsrv_setup_error(req, req->ntvfs->async_states->status); } /* we need to divide up the reply into chunks that fit into the negotiated buffer size */ do { uint16_t this_data, this_param, max_bytes; unsigned int align1 = 1, align2 = (params_left ? 2 : 0); struct smbsrv_request *this_req; max_bytes = req_max_data(req) - (align1 + align2); this_param = params_left; if (this_param > max_bytes) { this_param = max_bytes; } max_bytes -= this_param; this_data = data_left; if (this_data > max_bytes) { this_data = max_bytes; } /* don't destroy unless this is the last chunk */ if (params_left - this_param != 0 || data_left - this_data != 0) { this_req = smbsrv_setup_secondary_request(req); } else { this_req = req; } req_grow_data(this_req, this_param + this_data + (align1 + align2)); SSVAL(this_req->out.vwv, VWV(0), trans->out.params.length); SSVAL(this_req->out.vwv, VWV(1), trans->out.data.length); SSVAL(this_req->out.vwv, VWV(2), 0); SSVAL(this_req->out.vwv, VWV(3), this_param); SSVAL(this_req->out.vwv, VWV(4), align1 + PTR_DIFF(this_req->out.data, this_req->out.hdr)); SSVAL(this_req->out.vwv, VWV(5), PTR_DIFF(params, trans->out.params.data)); SSVAL(this_req->out.vwv, VWV(6), this_data); SSVAL(this_req->out.vwv, VWV(7), align1 + align2 + PTR_DIFF(this_req->out.data + this_param, this_req->out.hdr)); SSVAL(this_req->out.vwv, VWV(8), PTR_DIFF(data, trans->out.data.data)); SCVAL(this_req->out.vwv, VWV(9), trans->out.setup_count); SCVAL(this_req->out.vwv, VWV(9)+1, 0); /* reserved */ for (i=0;i<trans->out.setup_count;i++) { SSVAL(this_req->out.vwv, VWV(10+i), trans->out.setup[i]); } memset(this_req->out.data, 0, align1); if (this_param != 0) { memcpy(this_req->out.data + align1, params, this_param); } memset(this_req->out.data+this_param+align1, 0, align2); if (this_data != 0) { memcpy(this_req->out.data+this_param+align1+align2, data, this_data); } params_left -= this_param; data_left -= this_data; params += this_param; data += this_data; smbsrv_send_reply(this_req); } while (params_left != 0 || data_left != 0); }