r11715: added SMB2 read and write requests
[samba.git] / source4 / libcli / smb2 / request.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    SMB2 client request handling
5
6    Copyright (C) Andrew Tridgell 2005
7    
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 #include "includes.h"
24 #include "libcli/raw/libcliraw.h"
25 #include "libcli/smb2/smb2.h"
26 #include "libcli/smb2/smb2_calls.h"
27 #include "include/dlinklist.h"
28 #include "lib/events/events.h"
29
30 /*
31   initialise a smb2 request
32 */
33 struct smb2_request *smb2_request_init(struct smb2_transport *transport, 
34                                        uint16_t opcode, uint32_t body_size)
35 {
36         struct smb2_request *req;
37
38         req = talloc(transport, struct smb2_request);
39         if (req == NULL) return NULL;
40
41         req->state     = SMB2_REQUEST_INIT;
42         req->transport = transport;
43         req->session   = NULL;
44         req->tree      = NULL;
45         req->seqnum    = transport->seqnum++;
46         req->status    = NT_STATUS_OK;
47         req->async.fn  = NULL;
48         req->next = req->prev = NULL;
49
50         ZERO_STRUCT(req->in);
51         
52         req->out.allocated = SMB2_HDR_BODY+NBT_HDR_SIZE+body_size;
53         req->out.buffer    = talloc_size(req, req->out.allocated);
54         if (req->out.buffer == NULL) {
55                 talloc_free(req);
56                 return NULL;
57         }
58
59         req->out.size      = SMB2_HDR_BODY+NBT_HDR_SIZE + body_size;
60         req->out.hdr       = req->out.buffer + NBT_HDR_SIZE;
61         req->out.body      = req->out.hdr + SMB2_HDR_BODY;
62         req->out.body_size = body_size;
63         req->out.ptr       = req->out.body;
64
65         SIVAL(req->out.hdr, 0,                SMB2_MAGIC);
66         SSVAL(req->out.hdr, SMB2_HDR_LENGTH,  SMB2_HDR_BODY);
67         SSVAL(req->out.hdr, SMB2_HDR_PAD1,    0);
68         SIVAL(req->out.hdr, SMB2_HDR_STATUS,  0);
69         SSVAL(req->out.hdr, SMB2_HDR_OPCODE,  opcode);
70         SSVAL(req->out.hdr, SMB2_HDR_PAD2,    0);
71         SIVAL(req->out.hdr, SMB2_HDR_FLAGS,   0);
72         SIVAL(req->out.hdr, SMB2_HDR_UNKNOWN, 0);
73         SBVAL(req->out.hdr, SMB2_HDR_SEQNUM,  req->seqnum);
74         SIVAL(req->out.hdr, SMB2_HDR_PID,     0);
75         SIVAL(req->out.hdr, SMB2_HDR_TID,     0);
76         SBVAL(req->out.hdr, SMB2_HDR_UID,     0);
77         memset(req->out.hdr+SMB2_HDR_SIG, 0, 16);
78
79         return req;
80 }
81
82 /*
83     initialise a smb2 request for tree operations
84 */
85 struct smb2_request *smb2_request_init_tree(struct smb2_tree *tree, 
86                                             uint16_t opcode, uint32_t body_size)
87 {
88         struct smb2_request *req = smb2_request_init(tree->session->transport, opcode, 
89                                                      body_size);
90         if (req == NULL) return NULL;
91
92         SBVAL(req->out.hdr,  SMB2_HDR_UID, tree->session->uid);
93         SIVAL(req->out.hdr,  SMB2_HDR_TID, tree->tid);
94         req->session = tree->session;
95         req->tree = tree;
96
97         return req;     
98 }
99
100 /* destroy a request structure and return final status */
101 NTSTATUS smb2_request_destroy(struct smb2_request *req)
102 {
103         NTSTATUS status;
104
105         /* this is the error code we give the application for when a
106            _send() call fails completely */
107         if (!req) return NT_STATUS_UNSUCCESSFUL;
108
109         if (req->transport) {
110                 /* remove it from the list of pending requests (a null op if
111                    its not in the list) */
112                 DLIST_REMOVE(req->transport->pending_recv, req);
113         }
114
115         if (req->state == SMBCLI_REQUEST_ERROR &&
116             NT_STATUS_IS_OK(req->status)) {
117                 req->status = NT_STATUS_INTERNAL_ERROR;
118         }
119
120         status = req->status;
121         talloc_free(req);
122         return status;
123 }
124
125 /*
126   receive a response to a packet
127 */
128 BOOL smb2_request_receive(struct smb2_request *req)
129 {
130         /* req can be NULL when a send has failed. This eliminates lots of NULL
131            checks in each module */
132         if (!req) return False;
133
134         /* keep receiving packets until this one is replied to */
135         while (req->state <= SMB2_REQUEST_RECV) {
136                 if (event_loop_once(req->transport->socket->event.ctx) != 0) {
137                         return False;
138                 }
139         }
140
141         return req->state == SMB2_REQUEST_DONE;
142 }
143
144 /* Return true if the last packet was in error */
145 BOOL smb2_request_is_error(struct smb2_request *req)
146 {
147         return NT_STATUS_IS_ERR(req->status);
148 }
149
150 /*
151   check if a range in the reply body is out of bounds
152 */
153 BOOL smb2_oob_in(struct smb2_request *req, const uint8_t *ptr, uint_t size)
154 {
155         /* be careful with wraparound! */
156         if (ptr < req->in.body ||
157             ptr >= req->in.body + req->in.body_size ||
158             size > req->in.body_size ||
159             ptr + size > req->in.body + req->in.body_size) {
160                 return True;
161         }
162         return False;
163 }
164
165 /*
166   check if a range in the outgoing body is out of bounds
167 */
168 BOOL smb2_oob_out(struct smb2_request *req, const uint8_t *ptr, uint_t size)
169 {
170         /* be careful with wraparound! */
171         if (ptr < req->out.body ||
172             ptr >= req->out.body + req->out.body_size ||
173             size > req->out.body_size ||
174             ptr + size > req->out.body + req->out.body_size) {
175                 return True;
176         }
177         return False;
178 }
179
180 /*
181   pull a data blob from the body of a reply
182 */
183 DATA_BLOB smb2_pull_blob(struct smb2_request *req, uint8_t *ptr, uint_t size)
184 {
185         if (smb2_oob_in(req, ptr, size)) {
186                 return data_blob(NULL, 0);
187         }
188         return data_blob_talloc(req, ptr, size);
189 }
190
191 /*
192   pull a ofs/length/blob triple from a data blob
193   the ptr points to the start of the offset/length pair
194 */
195 NTSTATUS smb2_pull_ofs_blob(struct smb2_request *req, uint8_t *ptr, DATA_BLOB *blob)
196 {
197         uint16_t ofs, size;
198         if (smb2_oob_in(req, ptr, 4)) {
199                 return NT_STATUS_BUFFER_TOO_SMALL;
200         }
201         ofs  = SVAL(ptr, 0);
202         size = SVAL(ptr, 2);
203         if (smb2_oob_in(req, req->in.hdr + ofs, size)) {
204                 return NT_STATUS_BUFFER_TOO_SMALL;
205         }
206         *blob = data_blob_talloc(req, req->in.hdr+ofs, size);
207         NT_STATUS_HAVE_NO_MEMORY(blob->data);
208         return NT_STATUS_OK;
209 }
210
211 /*
212   push a ofs/length/blob triple into a data blob
213   the ptr points to the start of the offset/length pair
214
215   NOTE: assumes blob goes immediately after the offset/length pair. Needs 
216         to be generalised
217 */
218 NTSTATUS smb2_push_ofs_blob(struct smb2_request *req, uint8_t *ptr, DATA_BLOB blob)
219 {
220         if (smb2_oob_out(req, ptr, 4+blob.length)) {
221                 return NT_STATUS_BUFFER_TOO_SMALL;
222         }
223         SSVAL(ptr, 0, 4 + (ptr - req->out.hdr));
224         SSVAL(ptr, 2, blob.length);
225         memcpy(ptr+4, blob.data, blob.length);
226         return NT_STATUS_OK;
227 }
228
229 /*
230   pull a string in a ofs/length/blob format
231 */
232 NTSTATUS smb2_pull_ofs_string(struct smb2_request *req, uint8_t *ptr, 
233                               const char **str)
234 {
235         DATA_BLOB blob;
236         NTSTATUS status;
237         ssize_t size;
238         void *vstr;
239         status = smb2_pull_ofs_blob(req, ptr, &blob);
240         NT_STATUS_NOT_OK_RETURN(status);
241         size = convert_string_talloc(req, CH_UTF16, CH_UNIX, 
242                                      blob.data, blob.length, &vstr);
243         data_blob_free(&blob);
244         (*str) = vstr;
245         if (size == -1) {
246                 return NT_STATUS_ILLEGAL_CHARACTER;
247         }
248         return NT_STATUS_OK;
249 }
250
251 /*
252   create a UTF16 string in a blob from a char*
253 */
254 NTSTATUS smb2_string_blob(TALLOC_CTX *mem_ctx, const char *str, DATA_BLOB *blob)
255 {
256         ssize_t size;
257         size = convert_string_talloc(mem_ctx, CH_UNIX, CH_UTF16, 
258                                      str, strlen(str), (void **)&blob->data);
259         if (size == -1) {
260                 return NT_STATUS_ILLEGAL_CHARACTER;
261         }
262         blob->length = size;
263         return NT_STATUS_OK;    
264 }
265
266
267 /*
268   put a file handle into a buffer
269 */
270 void smb2_put_handle(uint8_t *data, struct smb2_handle h)
271 {
272         SBVAL(data, 0, h.data[0]);
273         SBVAL(data, 8, h.data[1]);
274 }
275