r11674: SMB2 tree connect now works. We do 2 session setups and 2 tree
[rusty/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 "include/dlinklist.h"
27 #include "lib/events/events.h"
28
29 /*
30   initialise a smb2 request
31 */
32 struct smb2_request *smb2_request_init(struct smb2_transport *transport, 
33                                        uint16_t opcode, uint32_t body_size)
34 {
35         struct smb2_request *req;
36
37         req = talloc(transport, struct smb2_request);
38         if (req == NULL) return NULL;
39
40         req->state     = SMB2_REQUEST_INIT;
41         req->transport = transport;
42         req->seqnum    = transport->seqnum++;
43         req->status    = NT_STATUS_OK;
44         req->async.fn  = NULL;
45         req->next = req->prev = NULL;
46
47         ZERO_STRUCT(req->in);
48         
49         req->out.allocated = SMB2_HDR_BODY+NBT_HDR_SIZE+body_size;
50         req->out.buffer    = talloc_size(req, req->out.allocated);
51         if (req->out.buffer == NULL) {
52                 talloc_free(req);
53                 return NULL;
54         }
55
56         req->out.size      = SMB2_HDR_BODY+NBT_HDR_SIZE + body_size;
57         req->out.hdr       = req->out.buffer + NBT_HDR_SIZE;
58         req->out.body      = req->out.hdr + SMB2_HDR_BODY;
59         req->out.body_size = body_size;
60         req->out.ptr       = req->out.body;
61
62         SIVAL(req->out.hdr, 0,                SMB2_MAGIC);
63         SSVAL(req->out.hdr, SMB2_HDR_LENGTH,  SMB2_HDR_BODY);
64         SSVAL(req->out.hdr, SMB2_HDR_PAD1,    0);
65         SIVAL(req->out.hdr, SMB2_HDR_STATUS,  0);
66         SSVAL(req->out.hdr, SMB2_HDR_OPCODE,  opcode);
67         SSVAL(req->out.hdr, SMB2_HDR_PAD2,    0);
68         SIVAL(req->out.hdr, SMB2_HDR_FLAGS,   0);
69         SIVAL(req->out.hdr, SMB2_HDR_UNKNOWN, 0);
70         SBVAL(req->out.hdr, SMB2_HDR_SEQNUM,  req->seqnum);
71         SIVAL(req->out.hdr, SMB2_HDR_PID,     0);
72         SIVAL(req->out.hdr, SMB2_HDR_TID,     0);
73         SBVAL(req->out.hdr, SMB2_HDR_UID,     (uint64_t)0);
74         memset(req->out.hdr+SMB2_HDR_SIG, 0, 16);
75
76         return req;
77 }
78
79 /* destroy a request structure and return final status */
80 NTSTATUS smb2_request_destroy(struct smb2_request *req)
81 {
82         NTSTATUS status;
83
84         /* this is the error code we give the application for when a
85            _send() call fails completely */
86         if (!req) return NT_STATUS_UNSUCCESSFUL;
87
88         if (req->transport) {
89                 /* remove it from the list of pending requests (a null op if
90                    its not in the list) */
91                 DLIST_REMOVE(req->transport->pending_recv, req);
92         }
93
94         if (req->state == SMBCLI_REQUEST_ERROR &&
95             NT_STATUS_IS_OK(req->status)) {
96                 req->status = NT_STATUS_INTERNAL_ERROR;
97         }
98
99         status = req->status;
100         talloc_free(req);
101         return status;
102 }
103
104 /*
105   receive a response to a packet
106 */
107 BOOL smb2_request_receive(struct smb2_request *req)
108 {
109         /* req can be NULL when a send has failed. This eliminates lots of NULL
110            checks in each module */
111         if (!req) return False;
112
113         /* keep receiving packets until this one is replied to */
114         while (req->state <= SMB2_REQUEST_RECV) {
115                 if (event_loop_once(req->transport->socket->event.ctx) != 0) {
116                         return False;
117                 }
118         }
119
120         return req->state == SMB2_REQUEST_DONE;
121 }
122
123 /* Return true if the last packet was in error */
124 BOOL smb2_request_is_error(struct smb2_request *req)
125 {
126         return NT_STATUS_IS_ERR(req->status);
127 }
128
129 /*
130   check if a range in the reply body is out of bounds
131 */
132 BOOL smb2_oob_in(struct smb2_request *req, const uint8_t *ptr, uint_t size)
133 {
134         /* be careful with wraparound! */
135         if (ptr < req->in.body ||
136             ptr >= req->in.body + req->in.body_size ||
137             size > req->in.body_size ||
138             ptr + size > req->in.body + req->in.body_size) {
139                 return True;
140         }
141         return False;
142 }
143
144 /*
145   check if a range in the outgoing body is out of bounds
146 */
147 BOOL smb2_oob_out(struct smb2_request *req, const uint8_t *ptr, uint_t size)
148 {
149         /* be careful with wraparound! */
150         if (ptr < req->out.body ||
151             ptr >= req->out.body + req->out.body_size ||
152             size > req->out.body_size ||
153             ptr + size > req->out.body + req->out.body_size) {
154                 return True;
155         }
156         return False;
157 }
158
159 /*
160   pull a data blob from the body of a reply
161 */
162 DATA_BLOB smb2_pull_blob(struct smb2_request *req, uint8_t *ptr, uint_t size)
163 {
164         if (smb2_oob_in(req, ptr, size)) {
165                 return data_blob(NULL, 0);
166         }
167         return data_blob_talloc(req, ptr, size);
168 }
169
170 /*
171   pull a ofs/length/blob triple into a data blob
172   the ptr points to the start of the offset/length pair
173 */
174 NTSTATUS smb2_pull_ofs_blob(struct smb2_request *req, uint8_t *ptr, DATA_BLOB *blob)
175 {
176         uint16_t ofs, size;
177         if (smb2_oob_in(req, ptr, 4)) {
178                 return NT_STATUS_BUFFER_TOO_SMALL;
179         }
180         ofs  = SVAL(ptr, 0);
181         size = SVAL(ptr, 2);
182         if (smb2_oob_in(req, req->in.hdr + ofs, size)) {
183                 return NT_STATUS_BUFFER_TOO_SMALL;
184         }
185         *blob = data_blob_talloc(req, req->in.hdr+ofs, size);
186         NT_STATUS_HAVE_NO_MEMORY(blob->data);
187         return NT_STATUS_OK;
188 }
189
190 /*
191   push a ofs/length/blob triple into a data blob
192   the ptr points to the start of the offset/length pair
193
194   NOTE: assumes blob goes immediately after the offset/length pair. Needs 
195         to be generalised
196 */
197 NTSTATUS smb2_push_ofs_blob(struct smb2_request *req, uint8_t *ptr, DATA_BLOB blob)
198 {
199         if (smb2_oob_out(req, ptr, 4+blob.length)) {
200                 return NT_STATUS_BUFFER_TOO_SMALL;
201         }
202         SSVAL(ptr, 0, 4 + (ptr - req->out.hdr));
203         SSVAL(ptr, 2, blob.length);
204         memcpy(ptr+4, blob.data, blob.length);
205         return NT_STATUS_OK;
206 }
207
208 /*
209   pull a string in a ofs/length/blob format
210 */
211 NTSTATUS smb2_pull_ofs_string(struct smb2_request *req, uint8_t *ptr, 
212                               const char **str)
213 {
214         DATA_BLOB blob;
215         NTSTATUS status;
216         ssize_t size;
217         void *vstr;
218         status = smb2_pull_ofs_blob(req, ptr, &blob);
219         NT_STATUS_NOT_OK_RETURN(status);
220         size = convert_string_talloc(req, CH_UTF16, CH_UNIX, 
221                                      blob.data, blob.length, &vstr);
222         data_blob_free(&blob);
223         (*str) = vstr;
224         if (size == -1) {
225                 return NT_STATUS_ILLEGAL_CHARACTER;
226         }
227         return NT_STATUS_OK;
228 }
229
230 /*
231   create a UTF16 string in a blob from a char*
232 */
233 NTSTATUS smb2_string_blob(TALLOC_CTX *mem_ctx, const char *str, DATA_BLOB *blob)
234 {
235         ssize_t size;
236         size = convert_string_talloc(mem_ctx, CH_UNIX, CH_UTF16, 
237                                      str, strlen(str), (void **)&blob->data);
238         if (size == -1) {
239                 return NT_STATUS_ILLEGAL_CHARACTER;
240         }
241         blob->length = size;
242         return NT_STATUS_OK;    
243 }