diff options
author | Stefan Metzmacher <metze@samba.org> | 2005-11-16 11:01:15 +0000 |
---|---|---|
committer | Gerald (Jerry) Carter <jerry@samba.org> | 2007-10-10 13:46:20 -0500 |
commit | e9eb56068573d89f8ce45f08220ca870b3daa669 (patch) | |
tree | 49e99d6c86f3e921c6b6a06570a6d7799f5064f7 /source4/libcli | |
parent | 43fa1b6dbd5e03251572fb6c2ee7c7f59f413c7d (diff) | |
download | samba-e9eb56068573d89f8ce45f08220ca870b3daa669.tar.gz samba-e9eb56068573d89f8ce45f08220ca870b3daa669.tar.bz2 samba-e9eb56068573d89f8ce45f08220ca870b3daa669.zip |
r11741: - the buffer code (first 2 bytes in the SMB2 body) seem to be the length
of the fixed body part, and +1 if there's a dynamic part
- there're 3 types of dynamic blobs
with uint16_t offset/uint16_t size
with uint16_t offset/uint32_t size
with uint32_t offset/uint32_t size /* aligned to 8 bytes */
- strings are transmitted in UTF-16 with no termination and
packet into a uint16/uint16 blob
metze
(This used to be commit 79103c51e5c752fbdb4d25a0047b65002828df89)
Diffstat (limited to 'source4/libcli')
-rw-r--r-- | source4/libcli/smb2/close.c | 11 | ||||
-rw-r--r-- | source4/libcli/smb2/connect.c | 4 | ||||
-rw-r--r-- | source4/libcli/smb2/create.c | 59 | ||||
-rw-r--r-- | source4/libcli/smb2/getinfo.c | 17 | ||||
-rw-r--r-- | source4/libcli/smb2/negprot.c | 31 | ||||
-rw-r--r-- | source4/libcli/smb2/read.c | 34 | ||||
-rw-r--r-- | source4/libcli/smb2/request.c | 290 | ||||
-rw-r--r-- | source4/libcli/smb2/session.c | 20 | ||||
-rw-r--r-- | source4/libcli/smb2/smb2.h | 31 | ||||
-rw-r--r-- | source4/libcli/smb2/smb2_calls.h | 132 | ||||
-rw-r--r-- | source4/libcli/smb2/tcon.c | 18 | ||||
-rw-r--r-- | source4/libcli/smb2/transport.c | 1 | ||||
-rw-r--r-- | source4/libcli/smb2/write.c | 27 |
13 files changed, 449 insertions, 226 deletions
diff --git a/source4/libcli/smb2/close.c b/source4/libcli/smb2/close.c index cf1cdbef5f..c851d60be4 100644 --- a/source4/libcli/smb2/close.c +++ b/source4/libcli/smb2/close.c @@ -32,13 +32,12 @@ struct smb2_request *smb2_close_send(struct smb2_tree *tree, struct smb2_close * { struct smb2_request *req; - req = smb2_request_init_tree(tree, SMB2_OP_CLOSE, 0x18); + req = smb2_request_init_tree(tree, SMB2_OP_CLOSE, 0x18, 0); if (req == NULL) return NULL; - SSVAL(req->out.body, 0x00, io->in.buffer_code); SSVAL(req->out.body, 0x02, io->in.flags); SIVAL(req->out.body, 0x04, io->in._pad); - smb2_put_handle(req->out.body+0x08, &io->in.handle); + smb2_push_handle(req->out.body+0x08, &io->in.handle); smb2_transport_send(req); @@ -56,11 +55,7 @@ NTSTATUS smb2_close_recv(struct smb2_request *req, struct smb2_close *io) return smb2_request_destroy(req); } - if (req->in.body_size < 0x3C) { - return NT_STATUS_BUFFER_TOO_SMALL; - } - - SMB2_CHECK_BUFFER_CODE(req, 0x3C); + SMB2_CHECK_PACKET_RECV(req, 0x3C, False); io->out.flags = SVAL(req->in.body, 0x02); io->out._pad = IVAL(req->in.body, 0x04); diff --git a/source4/libcli/smb2/connect.c b/source4/libcli/smb2/connect.c index 18f28539ea..7b538dc2e6 100644 --- a/source4/libcli/smb2/connect.c +++ b/source4/libcli/smb2/connect.c @@ -157,8 +157,8 @@ static void continue_socket(struct composite_context *creq) } ZERO_STRUCT(state->negprot); - state->negprot.in.unknown1 = 0x010024; - + state->negprot.in.unknown1 = 0x0001; + state->req = smb2_negprot_send(transport, &state->negprot); if (state->req == NULL) { composite_error(c, NT_STATUS_NO_MEMORY); diff --git a/source4/libcli/smb2/create.c b/source4/libcli/smb2/create.c index 9483f35ca1..79d3341bd0 100644 --- a/source4/libcli/smb2/create.c +++ b/source4/libcli/smb2/create.c @@ -32,18 +32,10 @@ struct smb2_request *smb2_create_send(struct smb2_tree *tree, struct smb2_create { struct smb2_request *req; NTSTATUS status; - DATA_BLOB path; - uint8_t *ptr; - status = smb2_string_blob(tree, io->in.fname, &path); - if (!NT_STATUS_IS_OK(status)) { - return NULL; - } - - req = smb2_request_init_tree(tree, SMB2_OP_CREATE, 0x50 + path.length); + req = smb2_request_init_tree(tree, SMB2_OP_CREATE, 0x38, 1); if (req == NULL) return NULL; - SSVAL(req->out.body, 0x00, io->in.buffer_code); SSVAL(req->out.body, 0x02, io->in.oplock_flags); SIVAL(req->out.body, 0x04, io->in.unknown2); SIVAL(req->out.body, 0x08, io->in.unknown3[0]); @@ -56,23 +48,17 @@ struct smb2_request *smb2_create_send(struct smb2_tree *tree, struct smb2_create SIVAL(req->out.body, 0x24, io->in.open_disposition); SIVAL(req->out.body, 0x28, io->in.create_options); - SSVAL(req->out.body, 0x2C, 0x40+0x38); /* offset to fname */ - SSVAL(req->out.body, 0x2E, path.length); - SIVAL(req->out.body, 0x30, 0x40+0x38+path.length); /* offset to 2nd buffer? */ - - SIVAL(req->out.body, 0x34, io->in.unknown6); - - memcpy(req->out.body+0x38, path.data, path.length); - - ptr = req->out.body+0x38+path.length; - - SIVAL(ptr, 0x00, io->in.unknown7); - SIVAL(ptr, 0x04, io->in.unknown8); - SIVAL(ptr, 0x08, io->in.unknown9); - SIVAL(ptr, 0x0C, io->in.unknown10); - SBVAL(ptr, 0x10, io->in.unknown11); + status = smb2_push_o16s16_string(&req->out, req->out.body+0x2C, io->in.fname); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(req); + return NULL; + } - data_blob_free(&path); + status = smb2_push_o32s32_blob(&req->out, req->out.body+0x30, io->in.blob); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(req); + return NULL; + } smb2_transport_send(req); @@ -83,18 +69,16 @@ struct smb2_request *smb2_create_send(struct smb2_tree *tree, struct smb2_create /* recv a create reply */ -NTSTATUS smb2_create_recv(struct smb2_request *req, struct smb2_create *io) +NTSTATUS smb2_create_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx, struct smb2_create *io) { + NTSTATUS status; + if (!smb2_request_receive(req) || smb2_request_is_error(req)) { return smb2_request_destroy(req); } - if (req->in.body_size < 0x54) { - return NT_STATUS_BUFFER_TOO_SMALL; - } - - SMB2_CHECK_BUFFER_CODE(req, 0x59); + SMB2_CHECK_PACKET_RECV(req, 0x58, True); io->out.oplock_flags = SVAL(req->in.body, 0x02); io->out.create_action = IVAL(req->in.body, 0x04); @@ -106,9 +90,12 @@ NTSTATUS smb2_create_recv(struct smb2_request *req, struct smb2_create *io) io->out.size = BVAL(req->in.body, 0x30); io->out.file_attr = IVAL(req->in.body, 0x38); io->out._pad = IVAL(req->in.body, 0x3C); - io->out.handle.data[0] = BVAL(req->in.body, 0x40); - io->out.handle.data[1] = BVAL(req->in.body, 0x48); - io->out.unknown4 = IVAL(req->in.body, 0x50); + smb2_pull_handle(req->in.body+0x40, &io->out.handle); + status = smb2_pull_o32s32_blob(&req->in, mem_ctx, req->in.body+0x50, &io->out.blob); + if (!NT_STATUS_IS_OK(status)) { + smb2_request_destroy(req); + return status; + } return smb2_request_destroy(req); } @@ -116,8 +103,8 @@ NTSTATUS smb2_create_recv(struct smb2_request *req, struct smb2_create *io) /* sync create request */ -NTSTATUS smb2_create(struct smb2_tree *tree, struct smb2_create *io) +NTSTATUS smb2_create(struct smb2_tree *tree, TALLOC_CTX *mem_ctx, struct smb2_create *io) { struct smb2_request *req = smb2_create_send(tree, io); - return smb2_create_recv(req, io); + return smb2_create_recv(req, mem_ctx, io); } diff --git a/source4/libcli/smb2/getinfo.c b/source4/libcli/smb2/getinfo.c index 4817f09d68..1594b8c1b4 100644 --- a/source4/libcli/smb2/getinfo.c +++ b/source4/libcli/smb2/getinfo.c @@ -32,17 +32,19 @@ struct smb2_request *smb2_getinfo_send(struct smb2_tree *tree, struct smb2_getin { struct smb2_request *req; - req = smb2_request_init_tree(tree, SMB2_OP_GETINFO, 0x28); + req = smb2_request_init_tree(tree, SMB2_OP_GETINFO, 0x28, 0); if (req == NULL) return NULL; - SSVAL(req->out.body, 0x00, io->in.buffer_code); + /* this seems to be a bug, they use 0x29 but only send 0x28 bytes */ + SSVAL(req->out.body, 0x00, 0x29); + SSVAL(req->out.body, 0x02, io->in.level); SIVAL(req->out.body, 0x04, io->in.max_response_size); SIVAL(req->out.body, 0x08, io->in.unknown1); SIVAL(req->out.body, 0x0C, io->in.flags); SIVAL(req->out.body, 0x10, io->in.unknown3); SIVAL(req->out.body, 0x14, io->in.unknown4); - smb2_put_handle(req->out.body+0x18, &io->in.handle); + smb2_push_handle(req->out.body+0x18, &io->in.handle); smb2_transport_send(req); @@ -63,13 +65,9 @@ NTSTATUS smb2_getinfo_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx, return smb2_request_destroy(req); } - if (req->in.body_size < 0x08) { - return NT_STATUS_BUFFER_TOO_SMALL; - } - - SMB2_CHECK_BUFFER_CODE(req, 0x09); + SMB2_CHECK_PACKET_RECV(req, 0x08, True); - status = smb2_pull_ofs_blob(&req->in, mem_ctx, req->in.body+0x02, &io->out.blob); + status = smb2_pull_o16s16_blob(&req->in, mem_ctx, req->in.body+0x02, &io->out.blob); if (!NT_STATUS_IS_OK(status)) { return status; } @@ -309,7 +307,6 @@ NTSTATUS smb2_getinfo_level(struct smb2_tree *tree, TALLOC_CTX *mem_ctx, struct smb2_request *req; ZERO_STRUCT(b); - b.in.buffer_code = 0x29; b.in.max_response_size = 0x10000; b.in.handle = handle; b.in.level = level; diff --git a/source4/libcli/smb2/negprot.c b/source4/libcli/smb2/negprot.c index 0dc8c8ca14..a3cf8eb018 100644 --- a/source4/libcli/smb2/negprot.c +++ b/source4/libcli/smb2/negprot.c @@ -33,12 +33,15 @@ struct smb2_request *smb2_negprot_send(struct smb2_transport *transport, { struct smb2_request *req; - req = smb2_request_init(transport, SMB2_OP_NEGPROT, 0x26); + req = smb2_request_init(transport, SMB2_OP_NEGPROT, 0x26, 0); if (req == NULL) return NULL; - SIVAL(req->out.body, 0x00, io->in.unknown1); - SSVAL(req->out.body, 0x04, io->in.unknown2); - memcpy(req->out.body+0x06, io->in.unknown3, 32); + /* this seems to be a bug, they use 0x24 but the length is 0x26 */ + SSVAL(req->out.body, 0x00, 0x24); + + SSVAL(req->out.body, 0x02, io->in.unknown1); + memcpy(req->out.body+0x04, io->in.unknown2, 32); + SSVAL(req->out.body, 0x24, io->in.unknown3); smb2_transport_send(req); @@ -51,18 +54,14 @@ struct smb2_request *smb2_negprot_send(struct smb2_transport *transport, NTSTATUS smb2_negprot_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx, struct smb2_negprot *io) { - uint16_t blobsize; + NTSTATUS status; - if (!smb2_request_receive(req) || + if (!smb2_request_receive(req) || smb2_request_is_error(req)) { return smb2_request_destroy(req); } - if (req->in.body_size < 0x40) { - return NT_STATUS_BUFFER_TOO_SMALL; - } - - SMB2_CHECK_BUFFER_CODE(req, 0x41); + SMB2_CHECK_PACKET_RECV(req, 0x40, True); io->out._pad = SVAL(req->in.body, 0x02); io->out.unknown2 = IVAL(req->in.body, 0x04); @@ -74,10 +73,14 @@ NTSTATUS smb2_negprot_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx, io->out.unknown7 = SVAL(req->in.body, 0x26); io->out.current_time = smbcli_pull_nttime(req->in.body, 0x28); io->out.boot_time = smbcli_pull_nttime(req->in.body, 0x30); - io->out.unknown8 = SVAL(req->in.body, 0x38); - blobsize = SVAL(req->in.body, 0x3A); + + status = smb2_pull_o16s16_blob(&req->in, mem_ctx, req->in.body+0x38, &io->out.secblob); + if (!NT_STATUS_IS_OK(status)) { + smb2_request_destroy(req); + return status; + } + io->out.unknown9 = IVAL(req->in.body, 0x3C); - io->out.secblob = smb2_pull_blob(&req->in, mem_ctx, req->in.body+0x40, blobsize); return smb2_request_destroy(req); } diff --git a/source4/libcli/smb2/read.c b/source4/libcli/smb2/read.c index 720d0bdbe0..f598a78cba 100644 --- a/source4/libcli/smb2/read.c +++ b/source4/libcli/smb2/read.c @@ -32,15 +32,16 @@ struct smb2_request *smb2_read_send(struct smb2_tree *tree, struct smb2_read *io { struct smb2_request *req; - req = smb2_request_init_tree(tree, SMB2_OP_READ, 0x31); + req = smb2_request_init_tree(tree, SMB2_OP_READ, 0x31, 0); if (req == NULL) return NULL; - SSVAL(req->out.body, 0x00, io->in.buffer_code); - SSVAL(req->out.body, 0x02, 0); + SSVAL(req->out.body, 0x02, io->in._pad); SIVAL(req->out.body, 0x04, io->in.length); SBVAL(req->out.body, 0x08, io->in.offset); - smb2_put_handle(req->out.body+0x10, &io->in.handle); - memcpy(req->out.body+0x20, io->in._pad, 17); + smb2_push_handle(req->out.body+0x10, &io->in.handle); + SBVAL(req->out.body, 0x20, io->in.unknown1); + SBVAL(req->out.body, 0x28, io->in.unknown2); + SCVAL(req->out.body, 0x30, io->in._bug); smb2_transport_send(req); @@ -54,30 +55,23 @@ struct smb2_request *smb2_read_send(struct smb2_tree *tree, struct smb2_read *io NTSTATUS smb2_read_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx, struct smb2_read *io) { - uint16_t ofs; - uint32_t nread; + NTSTATUS status; if (!smb2_request_receive(req) || smb2_request_is_error(req)) { return smb2_request_destroy(req); } - if (req->in.body_size < 16) { - return NT_STATUS_BUFFER_TOO_SMALL; - } - - SMB2_CHECK_BUFFER_CODE(req, 0x11); - - ofs = SVAL(req->in.body, 0x02); + SMB2_CHECK_PACKET_RECV(req, 0x10, True); - nread = IVAL(req->in.body, 0x04); - memcpy(io->out.unknown, req->in.body+0x08, 8); - - io->out.data = smb2_pull_blob(&req->in, mem_ctx, req->in.hdr+ofs, nread); - if (io->out.data.data == NULL) { - return NT_STATUS_NO_MEMORY; + status = smb2_pull_o16s32_blob(&req->in, mem_ctx, req->in.body+0x02, &io->out.data); + if (!NT_STATUS_IS_OK(status)) { + smb2_request_destroy(req); + return status; } + io->out.unknown1 = BVAL(req->in.body, 0x08); + return smb2_request_destroy(req); } diff --git a/source4/libcli/smb2/request.c b/source4/libcli/smb2/request.c index 457b7a4531..bb8ff06e2d 100644 --- a/source4/libcli/smb2/request.c +++ b/source4/libcli/smb2/request.c @@ -3,7 +3,8 @@ SMB2 client request handling - Copyright (C) Andrew Tridgell 2005 + Copyright (C) Andrew Tridgell 2005 + Copyright (C) Stefan Metzmacher 2005 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 @@ -30,8 +31,8 @@ /* initialise a smb2 request */ -struct smb2_request *smb2_request_init(struct smb2_transport *transport, - uint16_t opcode, uint32_t body_size) +struct smb2_request *smb2_request_init(struct smb2_transport *transport, uint16_t opcode, + uint16_t body_fixed_size, uint32_t body_dynamic_size) { struct smb2_request *req; @@ -49,18 +50,18 @@ struct smb2_request *smb2_request_init(struct smb2_transport *transport, ZERO_STRUCT(req->in); - req->out.allocated = SMB2_HDR_BODY+NBT_HDR_SIZE+body_size; + req->out.allocated = SMB2_HDR_BODY+NBT_HDR_SIZE+body_fixed_size+body_dynamic_size; req->out.buffer = talloc_size(req, req->out.allocated); if (req->out.buffer == NULL) { talloc_free(req); return NULL; } - req->out.size = SMB2_HDR_BODY+NBT_HDR_SIZE + body_size; + req->out.size = SMB2_HDR_BODY+NBT_HDR_SIZE+body_fixed_size+(body_dynamic_size?1:0); req->out.hdr = req->out.buffer + NBT_HDR_SIZE; req->out.body = req->out.hdr + SMB2_HDR_BODY; - req->out.body_size = body_size; - req->out.ptr = req->out.body; + req->out.body_size = body_fixed_size; + req->out.dynamic = (body_dynamic_size ? req->out.body + body_fixed_size : NULL); SIVAL(req->out.hdr, 0, SMB2_MAGIC); SSVAL(req->out.hdr, SMB2_HDR_LENGTH, SMB2_HDR_BODY); @@ -76,17 +77,28 @@ struct smb2_request *smb2_request_init(struct smb2_transport *transport, SBVAL(req->out.hdr, SMB2_HDR_UID, 0); memset(req->out.hdr+SMB2_HDR_SIG, 0, 16); + /* set the length of the fixed body part and +1 if there's a dynamic part also */ + SSVAL(req->out.body, 0, body_fixed_size + (body_dynamic_size?1:0)); + + /* + * if we have a dynamic part, make sure the first byte + * which is always be part of the packet is initialized + */ + if (body_dynamic_size) { + SCVAL(req->out.dynamic, 0, 0); + } + return req; } /* initialise a smb2 request for tree operations */ -struct smb2_request *smb2_request_init_tree(struct smb2_tree *tree, - uint16_t opcode, uint32_t body_size) +struct smb2_request *smb2_request_init_tree(struct smb2_tree *tree, uint16_t opcode, + uint16_t body_fixed_size, uint32_t body_dynamic_size) { struct smb2_request *req = smb2_request_init(tree->session->transport, opcode, - body_size); + body_fixed_size, body_dynamic_size); if (req == NULL) return NULL; SBVAL(req->out.hdr, SMB2_HDR_UID, tree->session->uid); @@ -162,41 +174,123 @@ BOOL smb2_oob(struct smb2_request_buffer *buf, const uint8_t *ptr, uint_t size) return False; } +static size_t smb2_padding_size(uint32_t offset, size_t n) +{ + if ((offset & (n-1)) == 0) return 0; + return n - (offset & (n-1)); +} + +static NTSTATUS smb2_grow_buffer(struct smb2_request_buffer *buf, size_t n) +{ + size_t dynamic_ofs; + uint8_t *buffer_ptr; + + /* a packet size should be limited a bit */ + if (n >= 0x00FFFFFF) return NT_STATUS_MARSHALL_OVERFLOW; + + if (n <= buf->allocated) return NT_STATUS_OK; + + dynamic_ofs = buf->dynamic - buf->buffer; + + buffer_ptr = talloc_realloc_size(buf, buf->buffer, n); + NT_STATUS_HAVE_NO_MEMORY(buffer_ptr); + + buf->buffer = buffer_ptr; + buf->hdr = buf->buffer + NBT_HDR_SIZE; + buf->body = buf->hdr + SMB2_HDR_BODY; + buf->dynamic = buf->buffer + dynamic_ofs; + buf->allocated = n; + + return NT_STATUS_OK; +} + /* - pull a data blob from the body of a reply + pull a uint16_t ofs/ uint16_t length/blob triple from a data blob + the ptr points to the start of the offset/length pair */ -DATA_BLOB smb2_pull_blob(struct smb2_request_buffer *buf, TALLOC_CTX *mem_ctx, uint8_t *ptr, uint_t size) +NTSTATUS smb2_pull_o16s16_blob(struct smb2_request_buffer *buf, TALLOC_CTX *mem_ctx, uint8_t *ptr, DATA_BLOB *blob) { - if (smb2_oob(buf, ptr, size)) { - return data_blob(NULL, 0); + uint16_t ofs, size; + if (smb2_oob(buf, ptr, 4)) { + return NT_STATUS_BUFFER_TOO_SMALL; + } + ofs = SVAL(ptr, 0); + size = SVAL(ptr, 2); + if (ofs == 0 || size == 0) { + *blob = data_blob(NULL, 0); + return NT_STATUS_OK; + } + if (smb2_oob(buf, buf->hdr + ofs, size)) { + return NT_STATUS_BUFFER_TOO_SMALL; } - return data_blob_talloc(mem_ctx, ptr, size); + *blob = data_blob_talloc(mem_ctx, buf->hdr + ofs, size); + NT_STATUS_HAVE_NO_MEMORY(blob->data); + return NT_STATUS_OK; } /* - push a data blob from the body of a reply + push a uint16_t ofs/ uint16_t length/blob triple into a data blob + the ptr points to the start of the offset/length pair */ -NTSTATUS smb2_push_blob(struct smb2_request_buffer *buf, uint8_t *ptr, DATA_BLOB blob) +NTSTATUS smb2_push_o16s16_blob(struct smb2_request_buffer *buf, uint8_t *ptr, DATA_BLOB blob) { - if (smb2_oob(buf, ptr, blob.length)) { + NTSTATUS status; + size_t offset; + size_t padding_length; + + if (!buf->dynamic) { + return NT_STATUS_INVALID_PARAMETER; + } + + /* we have only 16 bit for the size */ + if (blob.length > 0xFFFF) { return NT_STATUS_BUFFER_TOO_SMALL; } - memcpy(ptr, blob.data, blob.length); + + /* check if there're enough room for ofs and size */ + if (smb2_oob(buf, ptr, 4)) { + return NT_STATUS_BUFFER_TOO_SMALL; + } + + offset = buf->dynamic - buf->hdr; + padding_length = smb2_padding_size(offset, 2); + offset += padding_length; + + SSVAL(ptr, 0, offset); + SSVAL(ptr, 2, blob.length); + + status = smb2_grow_buffer(buf, NBT_HDR_SIZE + offset + blob.length); + NT_STATUS_NOT_OK_RETURN(status); + + memset(buf->dynamic, 0, padding_length); + buf->dynamic += padding_length; + + memcpy(buf->dynamic, blob.data, blob.length); + buf->dynamic += blob.length; + + buf->size = buf->dynamic - buf->buffer; + return NT_STATUS_OK; } /* - pull a ofs/length/blob triple from a data blob + pull a uint16_t ofs/ uint32_t length/blob triple from a data blob the ptr points to the start of the offset/length pair */ -NTSTATUS smb2_pull_ofs_blob(struct smb2_request_buffer *buf, TALLOC_CTX *mem_ctx, uint8_t *ptr, DATA_BLOB *blob) +NTSTATUS smb2_pull_o16s32_blob(struct smb2_request_buffer *buf, TALLOC_CTX *mem_ctx, uint8_t *ptr, DATA_BLOB *blob) { - uint16_t ofs, size; - if (smb2_oob(buf, ptr, 4)) { + uint16_t ofs; + uint32_t size; + + if (smb2_oob(buf, ptr, 6)) { return NT_STATUS_BUFFER_TOO_SMALL; } ofs = SVAL(ptr, 0); - size = SVAL(ptr, 2); + size = IVAL(ptr, 2); + if (ofs == 0 || size == 0) { + *blob = data_blob(NULL, 0); + return NT_STATUS_OK; + } if (smb2_oob(buf, buf->hdr + ofs, size)) { return NT_STATUS_BUFFER_TOO_SMALL; } @@ -206,35 +300,124 @@ NTSTATUS smb2_pull_ofs_blob(struct smb2_request_buffer *buf, TALLOC_CTX *mem_ctx } /* - push a ofs/length/blob triple into a data blob + push a uint16_t ofs/ uint32_t length/blob triple into a data blob the ptr points to the start of the offset/length pair +*/ +NTSTATUS smb2_push_o16s32_blob(struct smb2_request_buffer *buf, uint8_t *ptr, DATA_BLOB blob) +{ + NTSTATUS status; + size_t offset; + size_t padding_length; - NOTE: assumes blob goes immediately after the offset/length pair. Needs - to be generalised + if (!buf->dynamic) { + return NT_STATUS_INVALID_PARAMETER; + } + + /* check if there're enough room for ofs and size */ + if (smb2_oob(buf, ptr, 6)) { + return NT_STATUS_BUFFER_TOO_SMALL; + } + + offset = buf->dynamic - buf->hdr; + padding_length = smb2_padding_size(offset, 2); + offset += padding_length; + + SSVAL(ptr, 0, offset); + SIVAL(ptr, 2, blob.length); + + status = smb2_grow_buffer(buf, NBT_HDR_SIZE + offset + blob.length); + NT_STATUS_NOT_OK_RETURN(status); + + memset(buf->dynamic, 0, padding_length); + buf->dynamic += padding_length; + + memcpy(buf->dynamic, blob.data, blob.length); + buf->dynamic += blob.length; + + buf->size = buf->dynamic - buf->buffer; + + return NT_STATUS_OK; +} + +/* + pull a uint32_t ofs/ uint32_t length/blob triple from a data blob + the ptr points to the start of the offset/length pair */ -NTSTATUS smb2_push_ofs_blob(struct smb2_request_buffer *buf, uint8_t *ptr, DATA_BLOB blob) +NTSTATUS smb2_pull_o32s32_blob(struct smb2_request_buffer *buf, TALLOC_CTX *mem_ctx, uint8_t *ptr, DATA_BLOB *blob) { - if (smb2_oob(buf, ptr, 4+blob.length)) { + uint32_t ofs, size; + if (smb2_oob(buf, ptr, 8)) { return NT_STATUS_BUFFER_TOO_SMALL; } - SSVAL(ptr, 0, 4 + (ptr - buf->hdr)); - SSVAL(ptr, 2, blob.length); - memcpy(ptr+4, blob.data, blob.length); + ofs = IVAL(ptr, 0); + size = IVAL(ptr, 4); + if (ofs == 0 || size == 0) { + *blob = data_blob(NULL, 0); + return NT_STATUS_OK; + } + if (smb2_oob(buf, buf->hdr + ofs, size)) { + return NT_STATUS_BUFFER_TOO_SMALL; + } + *blob = data_blob_talloc(mem_ctx, buf->hdr + ofs, size); + NT_STATUS_HAVE_NO_MEMORY(blob->data); return NT_STATUS_OK; } /* - pull a string in a ofs/length/blob format + push a uint32_t ofs/ uint32_t length/blob triple into a data blob + the ptr points to the start of the offset/length pair */ -NTSTATUS smb2_pull_ofs_string(struct smb2_request_buffer *buf, TALLOC_CTX *mem_ctx, - uint8_t *ptr, const char **str) +NTSTATUS smb2_push_o32s32_blob(struct smb2_request_buffer *buf, uint8_t *ptr, DATA_BLOB blob) +{ + NTSTATUS status; + size_t offset; + size_t padding_length; + + if (!buf->dynamic) { + return NT_STATUS_INVALID_PARAMETER; + } + + /* check if there're enough room for ofs and size */ + if (smb2_oob(buf, ptr, 8)) { + return NT_STATUS_BUFFER_TOO_SMALL; + } + + offset = buf->dynamic - buf->hdr; + padding_length = smb2_padding_size(offset, 8); + offset += padding_length; + + SIVAL(ptr, 0, offset); + SIVAL(ptr, 4, blob.length); + + status = smb2_grow_buffer(buf, NBT_HDR_SIZE + offset + blob.length); + NT_STATUS_NOT_OK_RETURN(status); + + memset(buf->dynamic, 0, padding_length); + buf->dynamic += padding_length; + + memcpy(buf->dynamic, blob.data, blob.length); + buf->dynamic += blob.length; + + buf->size = buf->dynamic - buf->buffer; + + return NT_STATUS_OK; +} + +/* + pull a string in a uint16_t ofs/ uint16_t length/blob format + UTF-16 without termination +*/ +NTSTATUS smb2_pull_o16s16_string(struct smb2_request_buffer *buf, TALLOC_CTX *mem_ctx, + uint8_t *ptr, const char **str) { DATA_BLOB blob; NTSTATUS status; ssize_t size; void *vstr; - status = smb2_pull_ofs_blob(buf, mem_ctx, ptr, &blob); + + status = smb2_pull_o16s16_blob(buf, mem_ctx, ptr, &blob); NT_STATUS_NOT_OK_RETURN(status); + size = convert_string_talloc(mem_ctx, CH_UTF16, CH_UNIX, blob.data, blob.length, &vstr); data_blob_free(&blob); @@ -246,25 +429,44 @@ NTSTATUS smb2_pull_ofs_string(struct smb2_request_buffer *buf, TALLOC_CTX *mem_c } /* - create a UTF16 string in a blob from a char* + push a string in a uint16_t ofs/ uint16_t length/blob format + UTF-16 without termination */ -NTSTATUS smb2_string_blob(TALLOC_CTX *mem_ctx, const char *str, DATA_BLOB *blob) +NTSTATUS smb2_push_o16s16_string(struct smb2_request_buffer *buf, + uint8_t *ptr, const char *str) { + DATA_BLOB blob; + NTSTATUS status; ssize_t size; - size = convert_string_talloc(mem_ctx, CH_UNIX, CH_UTF16, - str, strlen(str), (void **)&blob->data); + + size = convert_string_talloc(buf->buffer, CH_UNIX, CH_UTF16, + str, strlen(str), (void **)&blob.data); if (size == -1) { return NT_STATUS_ILLEGAL_CHARACTER; } - blob->length = size; - return NT_STATUS_OK; + blob.length = size; + + status = smb2_push_o16s16_blob(buf, ptr, blob); + data_blob_free(&blob); + NT_STATUS_NOT_OK_RETURN(status); + + return NT_STATUS_OK; } /* - put a file handle into a buffer + push a file handle into a buffer */ -void smb2_put_handle(uint8_t *data, struct smb2_handle *h) +void smb2_push_handle(uint8_t *data, struct smb2_handle *h) { SBVAL(data, 0, h->data[0]); SBVAL(data, 8, h->data[1]); } + +/* + pull a file handle from a buffer +*/ +void smb2_pull_handle(uint8_t *ptr, struct smb2_handle *h) +{ + h->data[0] = BVAL(ptr, 0); + h->data[1] = BVAL(ptr, 8); +} diff --git a/source4/libcli/smb2/session.c b/source4/libcli/smb2/session.c index cb2797b9ad..e572227a48 100644 --- a/source4/libcli/smb2/session.c +++ b/source4/libcli/smb2/session.c @@ -67,17 +67,17 @@ struct smb2_request *smb2_session_setup_send(struct smb2_session *session, NTSTATUS status; req = smb2_request_init(session->transport, SMB2_OP_SESSSETUP, - 0x10 + io->in.secblob.length); + 0x10, io->in.secblob.length); if (req == NULL) return NULL; SBVAL(req->out.hdr, SMB2_HDR_UID, session->uid); - SIVAL(req->out.body, 0x00, io->in.unknown1); + SSVAL(req->out.body, 0x02, io->in._pad); SIVAL(req->out.body, 0x04, io->in.unknown2); SIVAL(req->out.body, 0x08, io->in.unknown3); - + req->session = session; - - status = smb2_push_ofs_blob(&req->out, req->out.body+0x0C, io->in.secblob); + + status = smb2_push_o16s16_blob(&req->out, req->out.body+0x0C, io->in.secblob); if (!NT_STATUS_IS_OK(status)) { talloc_free(req); return NULL; @@ -103,16 +103,12 @@ NTSTATUS smb2_session_setup_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx, return smb2_request_destroy(req); } - if (req->in.body_size < 0x08) { - return NT_STATUS_BUFFER_TOO_SMALL; - } - - SMB2_CHECK_BUFFER_CODE(req, 0x09); + SMB2_CHECK_PACKET_RECV(req, 0x08, True); io->out._pad = SVAL(req->in.body, 0x02); io->out.uid = BVAL(req->in.hdr, SMB2_HDR_UID); - status = smb2_pull_ofs_blob(&req->in, mem_ctx, req->in.body+0x04, &io->out.secblob); + status = smb2_pull_o16s16_blob(&req->in, mem_ctx, req->in.body+0x04, &io->out.secblob); if (!NT_STATUS_IS_OK(status)) { smb2_request_destroy(req); return status; @@ -203,7 +199,7 @@ struct composite_context *smb2_session_setup_spnego_send(struct smb2_session *se c->event_ctx = session->transport->socket->event.ctx; ZERO_STRUCT(state->io); - state->io.in.unknown1 = 0x11; + state->io.in._pad = 0x0; state->io.in.unknown2 = 0xF; state->io.in.unknown3 = 0x00; diff --git a/source4/libcli/smb2/smb2.h b/source4/libcli/smb2/smb2.h index e99e8d3945..47dd6fd272 100644 --- a/source4/libcli/smb2/smb2.h +++ b/source4/libcli/smb2/smb2.h @@ -87,13 +87,11 @@ struct smb2_request_buffer { /* the packet body */ uint8_t *body; uint_t body_size; - - /* ptr is used as a moving pointer into the data area - * of the packet. The reason its here and not a local - * variable in each function is that when a realloc of - * a send packet is done we need to move this - * pointer */ - uint8_t *ptr; + + /* this point to the next dynamic byte that can be used + * this will be moved when some dynamic data is pushed + */ + uint8_t *dynamic; }; @@ -176,13 +174,20 @@ struct smb2_request { #define SMB2_MAGIC 0x424D53FE /* 0xFE 'S' 'M' 'B' */ /* - check that a buffer code matches the expected value + check that a body has the expected size */ -#define SMB2_CHECK_BUFFER_CODE(req, code) do { \ - io->out.buffer_code = SVAL(req->in.body, 0); \ - if (io->out.buffer_code != (code)) { \ - DEBUG(0,("Unexpected buffer code 0x%x. Expected 0x%x\n", \ - io->out.buffer_code, code)); \ +#define SMB2_CHECK_PACKET_RECV(req, size, dynamic) do { \ + uint_t is_size = req->in.body_size; \ + uint16_t field_size = SVAL(req->in.body, 0); \ + uint16_t want_size = ((dynamic)?(size)+1:(size)); \ + if (is_size < (size)) { \ + DEBUG(0,("%s: buffer too small 0x%x. Expected 0x%x\n", \ + __location__, is_size, want_size)); \ + return NT_STATUS_BUFFER_TOO_SMALL; \ + }\ + if (field_size != want_size) { \ + DEBUG(0,("%s: unexpected fixed body size 0x%x. Expected 0x%x\n", \ + __location__, field_size, want_size)); \ return NT_STATUS_INVALID_PARAMETER; \ } \ } while (0) diff --git a/source4/libcli/smb2/smb2_calls.h b/source4/libcli/smb2/smb2_calls.h index 204ab7defb..d0a1cb8905 100644 --- a/source4/libcli/smb2/smb2_calls.h +++ b/source4/libcli/smb2/smb2_calls.h @@ -23,12 +23,15 @@ struct smb2_negprot { struct { - uint32_t unknown1; /* 0x00010024 */ - uint16_t unknown2; /* 0x00 */ - uint8_t unknown3[32]; /* all zero */ + /* static body buffer 38 (0x26) bytes */ + /* uint16_t buffer_code; 0x24 (why?) */ + uint16_t unknown1; /* 0x0001 */ + uint8_t unknown2[32]; /* all zero */ + uint16_t unknown3; /* 0x00000 */ } in; struct { - uint16_t buffer_code; + /* static body buffer 64 (0x40) bytes */ + /* uint16_t buffer_code; 0x41 = 0x40 + 1 */ uint16_t _pad; uint32_t unknown2; /* 0x06 */ uint8_t sessid[16]; @@ -39,41 +42,63 @@ struct smb2_negprot { uint16_t unknown7; /* 0x01 */ NTTIME current_time; NTTIME boot_time; - uint16_t unknown8; /* 0x80 */ - /* uint16_t secblob size here */ + /* uint16_t secblob_ofs */ + /* uint16_t secblob_size */ uint32_t unknown9; /* 0x204d4c20 */ + + /* dynamic body buffer */ DATA_BLOB secblob; } out; }; struct smb2_session_setup { struct { - uint32_t unknown1; /* 0x11 */ + /* static body buffer 16 (0x10) bytes */ + /* uint16_t buffer_code; 0x11 = 0x10 + 1 */ + uint16_t _pad; uint32_t unknown2; /* 0xF */ uint32_t unknown3; /* 0x00 */ - /* uint16_t secblob ofs/size here */ + /* uint16_t secblob_ofs */ + /* uint16_t secblob_size */ + + /* dynamic body */ DATA_BLOB secblob; } in; struct { - uint16_t buffer_code; + /* static body buffer 8 (0x08) bytes */ + /* uint16_t buffer_code; 0x09 = 0x08 +1 */ uint16_t _pad; - /* uint16_t secblob ofs/size here */ + /* uint16_t secblob_ofs */ + /* uint16_t secblob_size */ + + /* dynamic body */ DATA_BLOB secblob; - uint64_t uid; /* returned in header */ + + /* extracted from the SMB2 header */ + uint64_t uid; } out; }; struct smb2_tree_connect { struct { - uint32_t unknown1; /* 0x09 */ - const char *path; + /* static body buffer 8 (0x08) bytes */ + /* uint16_t buffer_code; 0x09 = 0x08 + 1 */ + uint16_t unknown1; /* 0x0000 */ + /* uint16_t path_ofs */ + /* uint16_t path_size */ + + /* dynamic body */ + const char *path; /* as non-terminated UTF-16 on the wire */ } in; struct { - uint16_t buffer_code; + /* static body buffer 16 (0x10) bytes */ + /* uint16_t buffer_code; 0x10 */ uint16_t unknown1; /* 0x02 */ uint32_t unknown2; /* 0x00 */ uint32_t unknown3; /* 0x00 */ uint32_t access_mask; + + /* extracted from the SMB2 header */ uint32_t tid; } out; }; @@ -93,27 +118,32 @@ struct smb2_handle { struct smb2_create { struct { - uint16_t buffer_code; /* 0x39 */ + /* static body buffer 56 (0x38) bytes */ + /* uint16_t buffer_code; 0x39 = 0x38 + 1 */ uint16_t oplock_flags; /* SMB2_CREATE_FLAG_* */ uint32_t unknown2; uint32_t unknown3[4]; uint32_t access_mask; + uint32_t file_attr; uint32_t share_access; uint32_t open_disposition; uint32_t create_options; - /* ofs/len of name here, 16 bits */ - uint32_t unknown6; + + /* uint16_t fname_ofs */ + /* uint16_t fname_size */ + /* uint32_t blob_ofs; */ + /* uint32_t blob_size; */ + + /* dynamic body */ const char *fname; - uint32_t unknown7; - uint32_t unknown8; - uint32_t unknown9; - uint32_t unknown10; - uint64_t unknown11; + + DATA_BLOB blob; } in; struct { - uint16_t buffer_code; /* 0x59 */ + /* static body buffer 88 (0x58) bytes */ + /* uint16_t buffer_code; 0x59 = 0x58 + 1 */ uint16_t oplock_flags; /* SMB2_CREATE_FLAG_* */ uint32_t create_action; NTTIME create_time; @@ -125,8 +155,11 @@ struct smb2_create { uint32_t file_attr; uint32_t _pad; struct smb2_handle handle; - uint32_t unknown4; - uint32_t unknown5; + /* uint32_t blob_ofs; */ + /* uint32_t blob_size; */ + + /* dynamic body */ + DATA_BLOB blob; } out; }; @@ -135,14 +168,16 @@ struct smb2_create { struct smb2_close { struct { - uint16_t buffer_code; + /* static body buffer 24 (0x18) bytes */ + /* uint16_t buffer_code; 0x18 */ uint16_t flags; /* SMB2_CLOSE_FLAGS_* */ uint32_t _pad; struct smb2_handle handle; } in; struct { - uint16_t buffer_code; + /* static body buffer 60 (0x3C) bytes */ + /* uint16_t buffer_code; 0x3C */ uint16_t flags; uint32_t _pad; NTTIME create_time; @@ -187,7 +222,8 @@ struct smb2_close { struct smb2_getinfo { struct { - uint16_t buffer_code; + /* static body buffer 40 (0x28) bytes */ + /* uint16_t buffer_code; 0x29 = 0x28 + 1 (why???) */ uint16_t level; uint32_t max_response_size; uint32_t unknown1; @@ -198,7 +234,12 @@ struct smb2_getinfo { } in; struct { - uint16_t buffer_code; + /* static body buffer 8 (0x08) bytes */ + /* uint16_t buffer_code; 0x09 = 0x08 + 1 */ + /* uint16_t blob_ofs; */ + /* uint16_t blob_size; */ + + /* dynamic body */ DATA_BLOB blob; } out; }; @@ -304,33 +345,50 @@ union smb2_fileinfo { struct smb2_write { struct { - uint16_t buffer_code; + /* static body buffer 48 (0x30) bytes */ + /* uint16_t buffer_code; 0x31 = 0x30 + 1 */ + /* uint16_t data_ofs; */ + /* uint32_t data_size; */ uint64_t offset; struct smb2_handle handle; - uint8_t _pad[16]; + uint64_t unknown1; /* 0xFFFFFFFFFFFFFFFF */ + uint64_t unknown2; /* 0xFFFFFFFFFFFFFFFF */ + + /* dynamic body */ DATA_BLOB data; } in; struct { - uint16_t buffer_code; + /* static body buffer 17 (0x11) bytes */ + /* uint16_t buffer_code; 0x11 */ uint16_t _pad; uint32_t nwritten; - uint8_t unknown[9]; + uint64_t unknown1; /* 0x0000000000000000 */ + uint8_t _bug; } out; }; struct smb2_read { struct { - uint16_t buffer_code; + /* static body buffer 48 (0x30) bytes */ + /* uint16_t buffer_code; 0x31 = 0x30 + 1 */ + uint16_t _pad; uint32_t length; uint64_t offset; struct smb2_handle handle; - uint8_t _pad[17]; + uint64_t unknown1; /* 0x0000000000000000 */ + uint64_t unknown2; /* 0x0000000000000000 */ + uint8_t _bug; } in; struct { - uint16_t buffer_code; - uint8_t unknown[8]; + /* static body buffer 16 (0x10) bytes */ + /* uint16_t buffer_code; 0x11 = 0x10 + 1 */ + /* uint16_t data_ofs; */ + /* uint32_t data_size; */ + uint64_t unknown1; /* 0x0000000000000000 */ + + /* dynamic body */ DATA_BLOB data; } out; }; diff --git a/source4/libcli/smb2/tcon.c b/source4/libcli/smb2/tcon.c index 5e53e11634..32ad05733e 100644 --- a/source4/libcli/smb2/tcon.c +++ b/source4/libcli/smb2/tcon.c @@ -53,21 +53,15 @@ struct smb2_request *smb2_tree_connect_send(struct smb2_tree *tree, { struct smb2_request *req; NTSTATUS status; - DATA_BLOB path; - status = smb2_string_blob(tree, io->in.path, &path); - if (!NT_STATUS_IS_OK(status)) { - return NULL; - } - req = smb2_request_init(tree->session->transport, SMB2_OP_TCON, - 0x8 + path.length); + 0x08, 1); if (req == NULL) return NULL; SBVAL(req->out.hdr, SMB2_HDR_UID, tree->session->uid); + SIVAL(req->out.body, 0x00, io->in.unknown1); - status = smb2_push_ofs_blob(&req->out, req->out.body+0x04, path); - data_blob_free(&path); + status = smb2_push_o16s16_string(&req->out, req->out.body+0x04, io->in.path); if (!NT_STATUS_IS_OK(status)) { talloc_free(req); return NULL; @@ -89,11 +83,7 @@ NTSTATUS smb2_tree_connect_recv(struct smb2_request *req, struct smb2_tree_conne return smb2_request_destroy(req); } - if (req->in.body_size < 0x10) { - return NT_STATUS_BUFFER_TOO_SMALL; - } - - SMB2_CHECK_BUFFER_CODE(req, 0x10); + SMB2_CHECK_PACKET_RECV(req, 0x10, False); io->out.tid = IVAL(req->in.hdr, SMB2_HDR_TID); diff --git a/source4/libcli/smb2/transport.c b/source4/libcli/smb2/transport.c index 083034a547..04ebb88d4e 100644 --- a/source4/libcli/smb2/transport.c +++ b/source4/libcli/smb2/transport.c @@ -181,7 +181,6 @@ static NTSTATUS smb2_transport_finish_recv(void *private, DATA_BLOB blob) req->in.hdr = hdr; req->in.body = hdr+SMB2_HDR_BODY; req->in.body_size = req->in.size - (SMB2_HDR_BODY+NBT_HDR_SIZE); - req->in.ptr = req->in.body; req->status = NT_STATUS(IVAL(hdr, SMB2_HDR_STATUS)); DEBUG(2, ("SMB2 RECV seqnum=0x%llx\n", req->seqnum)); diff --git a/source4/libcli/smb2/write.c b/source4/libcli/smb2/write.c index a8e644f2d1..0b28b820ec 100644 --- a/source4/libcli/smb2/write.c +++ b/source4/libcli/smb2/write.c @@ -33,21 +33,21 @@ struct smb2_request *smb2_write_send(struct smb2_tree *tree, struct smb2_write * NTSTATUS status; struct smb2_request *req; - req = smb2_request_init_tree(tree, SMB2_OP_WRITE, io->in.data.length + 0x30); + req = smb2_request_init_tree(tree, SMB2_OP_WRITE, 0x30, io->in.data.length); if (req == NULL) return NULL; - SSVAL(req->out.body, 0x00, io->in.buffer_code); - SSVAL(req->out.body, 0x02, req->out.body+0x30 - req->out.hdr); - SIVAL(req->out.body, 0x04, io->in.data.length); - SBVAL(req->out.body, 0x08, io->in.offset); - smb2_put_handle(req->out.body+0x10, &io->in.handle); - memcpy(req->out.body+0x20, io->in._pad, 0x10); - - status = smb2_push_blob(&req->out, req->out.body+0x30, io->in.data); + status = smb2_push_o16s32_blob(&req->out, req->out.body+0x02, io->in.data); if (!NT_STATUS_IS_OK(status)) { + talloc_free(req); return NULL; } + SBVAL(req->out.body, 0x08, io->in.offset); + smb2_push_handle(req->out.body+0x10, &io->in.handle); + + SBVAL(req->out.body, 0x20, io->in.unknown1); + SBVAL(req->out.body, 0x28, io->in.unknown2); + smb2_transport_send(req); return req; @@ -64,15 +64,12 @@ NTSTATUS smb2_write_recv(struct smb2_request *req, struct smb2_write *io) return smb2_request_destroy(req); } - if (req->in.body_size < 17) { - return NT_STATUS_BUFFER_TOO_SMALL; - } - - SMB2_CHECK_BUFFER_CODE(req, 0x11); + SMB2_CHECK_PACKET_RECV(req, 0x11, False); io->out._pad = SVAL(req->in.body, 0x02); io->out.nwritten = IVAL(req->in.body, 0x04); - memcpy(io->out.unknown, req->in.body+0x08, 9); + io->out.unknown1 = BVAL(req->in.body, 0x08); + io->out._bug = CVAL(req->in.body, 0x10); return smb2_request_destroy(req); } |