4191d3775eeb48a88429ad9094e2c867a82524e0
[samba.git] / source / libcli / raw / rawrequest.c
1 /* 
2    Unix SMB/CIFS implementation.
3    
4    Copyright (C) Andrew Tridgell  2003
5    Copyright (C) James Myers 2003 <myersjj@samba.org>
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 /*
23   this file implements functions for manipulating the 'struct cli_request' structure in libsmb
24 */
25
26 #include "includes.h"
27
28 /* we over allocate the data buffer to prevent too many realloc calls */
29 #define REQ_OVER_ALLOCATION 256
30
31 /* assume that a character will not consume more than 3 bytes per char */
32 #define MAX_BYTES_PER_CHAR 3
33
34 /* destroy a request structure and return final status */
35 NTSTATUS cli_request_destroy(struct cli_request *req)
36 {
37         NTSTATUS status;
38
39         /* this is the error code we give the application for when a
40            _send() call fails completely */
41         if (!req) return NT_STATUS_UNSUCCESSFUL;
42
43         /* remove it from the list of pending requests (a null op if
44            its not in the list) */
45         DLIST_REMOVE(req->transport->pending_requests, req);
46
47         /* ahh, its so nice to destroy a complex structure in such a
48            simple way! */
49         status = req->status;
50         talloc_destroy(req->mem_ctx);
51         return status;
52 }
53
54
55 /*
56   low-level function to setup a request buffer for a non-SMB packet 
57   at the transport level
58 */
59 struct cli_request *cli_request_setup_nonsmb(struct cli_transport *transport, uint_t size)
60 {
61         struct cli_request *req;
62         TALLOC_CTX *mem_ctx;
63         
64         /* each request gets its own talloc context. The request
65            structure itself is also allocated inside this context,
66            so we need to allocate it before we construct the request
67         */
68         mem_ctx = talloc_init("cli_request");
69         if (!mem_ctx) {
70                 return NULL;
71         }
72
73         req = talloc(mem_ctx, sizeof(struct cli_request));
74         if (!req) {
75                 return NULL;
76         }
77         ZERO_STRUCTP(req);
78
79         /* setup the request context */
80         req->mem_ctx = mem_ctx;
81         req->transport = transport;
82         req->session = NULL;
83         req->tree = NULL;
84         req->out.size = size;
85
86         /* over allocate by a small amount */
87         req->out.allocated = req->out.size + REQ_OVER_ALLOCATION; 
88
89         req->out.buffer = talloc(req->mem_ctx, req->out.allocated);
90         if (!req->out.buffer) {
91                 return NULL;
92         }
93
94         SIVAL(req->out.buffer, 0, 0);
95
96         return req;
97 }
98
99
100 /*
101   setup a SMB packet at transport level
102 */
103 struct cli_request *cli_request_setup_transport(struct cli_transport *transport,
104                                                 uint8 command, unsigned wct, unsigned buflen)
105 {
106         struct cli_request *req;
107
108         req = cli_request_setup_nonsmb(transport, NBT_HDR_SIZE + MIN_SMB_SIZE + wct*2 + buflen);
109
110         if (!req) return NULL;
111         
112         req->out.hdr = req->out.buffer + NBT_HDR_SIZE;
113         req->out.vwv = req->out.hdr + HDR_VWV;
114         req->out.wct = wct;
115         req->out.data = req->out.vwv + VWV(wct) + 2;
116         req->out.data_size = buflen;
117         req->out.ptr = req->out.data;
118
119         SCVAL(req->out.hdr, HDR_WCT, wct);
120         SSVAL(req->out.vwv, VWV(wct), buflen);
121
122         memcpy(req->out.hdr, "\377SMB", 4);
123         SCVAL(req->out.hdr,HDR_COM,command);
124
125         SCVAL(req->out.hdr,HDR_FLG, FLAG_CASELESS_PATHNAMES);
126         SSVAL(req->out.hdr,HDR_FLG2, 0);
127
128         /* assign a mid */
129         req->mid = cli_transport_next_mid(transport);
130
131         /* copy the pid, uid and mid to the request */
132         SSVAL(req->out.hdr, HDR_PID, 0);
133         SSVAL(req->out.hdr, HDR_UID, 0);
134         SSVAL(req->out.hdr, HDR_MID, req->mid);
135         SSVAL(req->out.hdr, HDR_TID,0);
136         SSVAL(req->out.hdr, HDR_PIDHIGH,0);
137         SIVAL(req->out.hdr, HDR_RCLS, 0);
138         memset(req->out.hdr+HDR_SS_FIELD, 0, 10);
139         
140         return req;
141 }
142
143 /*
144   setup a reply in req->out with the given word count and initial data
145   buffer size.  the caller will then fill in the command words and
146   data before calling cli_request_send() to send the reply on its
147   way. This interface is used before a session is setup.
148 */
149 struct cli_request *cli_request_setup_session(struct cli_session *session,
150                                               uint8 command, unsigned wct, unsigned buflen)
151 {
152         struct cli_request *req;
153         uint16 flags2;
154         uint32 capabilities;
155
156         req = cli_request_setup_transport(session->transport, command, wct, buflen);
157
158         if (!req) return NULL;
159
160         req->session = session;
161         
162         flags2 = FLAGS2_LONG_PATH_COMPONENTS;
163         capabilities = session->transport->negotiate.capabilities;
164
165         if (capabilities & CAP_UNICODE) {
166                 flags2 |= FLAGS2_UNICODE_STRINGS;
167         }
168         if (capabilities & CAP_STATUS32) {
169                 flags2 |= FLAGS2_32_BIT_ERROR_CODES;
170         }
171         if (capabilities & CAP_EXTENDED_SECURITY) {
172                 flags2 |= FLAGS2_EXTENDED_SECURITY;
173         }
174         if (session->transport->negotiate.sign_info.doing_signing) {
175                 flags2 |= FLAGS2_SMB_SECURITY_SIGNATURES;
176         }
177
178         SSVAL(req->out.hdr, HDR_FLG2, flags2);
179         SSVAL(req->out.hdr, HDR_PID, session->pid & 0xFFFF);
180         SSVAL(req->out.hdr, HDR_PIDHIGH, session->pid >> 16);
181         SSVAL(req->out.hdr, HDR_UID, session->vuid);
182         
183         return req;
184 }
185
186 /*
187   setup a request for tree based commands
188 */
189 struct cli_request *cli_request_setup(struct cli_tree *tree,
190                                       uint8 command, 
191                                       unsigned wct, unsigned buflen)
192 {
193         struct cli_request *req;
194
195         req = cli_request_setup_session(tree->session, command, wct, buflen);
196         if (req) {
197                 req->tree = tree;
198                 SSVAL(req->out.hdr,HDR_TID,tree->tid);
199         }
200         return req;
201 }
202
203 /*
204   grow the allocation of the data buffer portion of a reply
205   packet. Note that as this can reallocate the packet buffer this
206   invalidates any local pointers into the packet.
207
208   To cope with this req->out.ptr is supplied. This will be updated to
209   point at the same offset into the packet as before this call
210 */
211 static void cli_req_grow_allocation(struct cli_request *req, unsigned new_size)
212 {
213         int delta;
214         char *buf2;
215
216         delta = new_size - req->out.data_size;
217         if (delta + req->out.size <= req->out.allocated) {
218                 /* it fits in the preallocation */
219                 return;
220         }
221
222         /* we need to realloc */
223         req->out.allocated = req->out.size + delta + REQ_OVER_ALLOCATION;
224         buf2 = talloc_realloc(req->mem_ctx, req->out.buffer, req->out.allocated);
225         if (buf2 == NULL) {
226                 smb_panic("out of memory in req_grow_allocation");
227         }
228
229         if (buf2 == req->out.buffer) {
230                 /* the malloc library gave us the same pointer */
231                 return;
232         }
233         
234         /* update the pointers into the packet */
235         req->out.data = buf2 + PTR_DIFF(req->out.data, req->out.buffer);
236         req->out.ptr  = buf2 + PTR_DIFF(req->out.ptr,  req->out.buffer);
237         req->out.vwv  = buf2 + PTR_DIFF(req->out.vwv,  req->out.buffer);
238         req->out.hdr  = buf2 + PTR_DIFF(req->out.hdr,  req->out.buffer);
239
240         req->out.buffer = buf2;
241 }
242
243
244 /*
245   grow the data buffer portion of a reply packet. Note that as this
246   can reallocate the packet buffer this invalidates any local pointers
247   into the packet. 
248
249   To cope with this req->out.ptr is supplied. This will be updated to
250   point at the same offset into the packet as before this call
251 */
252 static void cli_req_grow_data(struct cli_request *req, unsigned new_size)
253 {
254         int delta;
255
256         cli_req_grow_allocation(req, new_size);
257
258         delta = new_size - req->out.data_size;
259
260         req->out.size += delta;
261         req->out.data_size += delta;
262
263         /* set the BCC to the new data size */
264         SSVAL(req->out.vwv, VWV(req->out.wct), new_size);
265 }
266
267 /*
268   send a message
269 */
270 BOOL cli_request_send(struct cli_request *req)
271 {
272         uint_t ret;
273
274         if (IVAL(req->out.buffer, 0) == 0) {
275                 _smb_setlen(req->out.buffer, req->out.size - NBT_HDR_SIZE);
276         }
277
278         cli_request_calculate_sign_mac(req);
279
280         ret = cli_sock_write(req->transport->socket, req->out.buffer, req->out.size);
281
282         if (req->out.size != ret) {
283                 req->transport->error.etype = ETYPE_SOCKET;
284                 req->transport->error.e.socket_error = SOCKET_WRITE_ERROR;
285                 DEBUG(0,("Error writing %d bytes to server - %s\n",
286                          (int)req->out.size, strerror(errno)));
287                 return False;
288         }
289
290         /* add it to the list of pending requests */
291         DLIST_ADD(req->transport->pending_requests, req);
292         
293         return True;
294 }
295
296
297 /*
298   receive a response to a packet
299 */
300 BOOL cli_request_receive(struct cli_request *req)
301 {
302         /* req can be NULL when a send has failed. This eliminates lots of NULL
303            checks in each module */
304         if (!req) return False;
305
306         /* keep receiving packets until this one is replied to */
307         while (!req->in.buffer) {
308                 if (!cli_transport_select(req->transport)) {
309                         return False;
310                 }
311
312                 cli_request_receive_next(req->transport);
313         }
314
315         return True;
316 }
317
318
319 /*
320   handle oplock break requests from the server - return True if the request was
321   an oplock break
322 */
323 static BOOL handle_oplock_break(struct cli_transport *transport, uint_t len, const char *hdr, const char *vwv)
324 {
325         /* we must be very fussy about what we consider an oplock break to avoid
326            matching readbraw replies */
327         if (len != MIN_SMB_SIZE + VWV(8) ||
328             (CVAL(hdr, HDR_FLG) & FLAG_REPLY) ||
329             CVAL(hdr,HDR_COM) != SMBlockingX ||
330             SVAL(hdr, HDR_MID) != 0xFFFF ||
331             SVAL(vwv,VWV(6)) != 0 ||
332             SVAL(vwv,VWV(7)) != 0) {
333                 return False;
334         }
335
336         if (transport->oplock.handler) {
337                 uint16 tid = SVAL(hdr, HDR_TID);
338                 uint16 fnum = SVAL(vwv,VWV(2));
339                 uint8 level = CVAL(vwv,VWV(3));
340                 transport->oplock.handler(transport, tid, fnum, level, transport->oplock.private);
341         }
342
343         return True;
344 }
345
346
347 /*
348   receive an async message from the server
349   this function assumes that the caller already knows that the socket is readable
350   and that there is a packet waiting
351
352   The packet is not actually returned by this function, instead any
353   registered async message handlers are called
354
355   return True if a packet was successfully received and processed
356   return False if the socket appears to be dead
357 */
358 BOOL cli_request_receive_next(struct cli_transport *transport)
359 {
360         BOOL ret;
361         int len;
362         char header[NBT_HDR_SIZE];
363         char *buffer, *hdr, *vwv;
364         TALLOC_CTX *mem_ctx;
365         struct cli_request *req;
366         uint16 wct, mid = 0;
367
368         len = cli_sock_read(transport->socket, header, 4);
369         if (len != 4) {
370                 return False;
371         }
372         
373         len = smb_len(header);
374
375         mem_ctx = talloc_init("cli_request_receive_next");
376         
377         /* allocate the incoming buffer at the right size */
378         buffer = talloc(mem_ctx, len+NBT_HDR_SIZE);
379         if (!buffer) {
380                 talloc_destroy(mem_ctx);
381                 return False;
382         }
383
384         /* fill in the already received header */
385         memcpy(buffer, header, NBT_HDR_SIZE);
386
387         ret = cli_sock_read(transport->socket, buffer + NBT_HDR_SIZE, len);
388         /* If the server is not responding, note that now */
389         if (ret != len) {
390                 return False;
391         }
392
393         hdr = buffer+NBT_HDR_SIZE;
394         vwv = hdr + HDR_VWV;
395
396         /* see if it could be an oplock break request */
397         if (handle_oplock_break(transport, len, hdr, vwv)) {
398                 goto done;
399         }
400
401         /* at this point we need to check for a readbraw reply, as these can be any length */
402         if (transport->readbraw_pending) {
403                 transport->readbraw_pending = 0;
404
405                 /* it must match the first entry in the pending queue as the client is not allowed
406                    to have outstanding readbraw requests */
407                 req = transport->pending_requests;
408                 if (!req) goto done;
409
410                 req->in.buffer = buffer;
411                 talloc_steal(mem_ctx, req->mem_ctx, buffer);
412                 req->in.size = len + NBT_HDR_SIZE;
413                 req->in.allocated = req->in.size;
414                 goto async;
415         }
416
417         if (len >= MIN_SMB_SIZE) {
418                 /* extract the mid for matching to pending requests */
419                 mid = SVAL(hdr, HDR_MID);
420                 wct = CVAL(hdr, HDR_WCT);
421         }
422
423         /* match the incoming request against the list of pending requests */
424         for (req=transport->pending_requests; req; req=req->next) {
425                 if (req->mid == mid) break;
426         }
427
428         if (!req) {
429                 DEBUG(3,("Discarding unmatched reply with mid %d\n", mid));
430                 goto done;
431         }
432
433         /* fill in the 'in' portion of the matching request */
434         req->in.buffer = buffer;
435         talloc_steal(mem_ctx, req->mem_ctx, buffer);
436         req->in.size = len + NBT_HDR_SIZE;
437         req->in.allocated = req->in.size;
438
439         /* handle non-SMB replies */
440         if (req->in.size < NBT_HDR_SIZE + MIN_SMB_SIZE) {
441                 goto done;
442         }
443
444         if (req->in.size < NBT_HDR_SIZE + MIN_SMB_SIZE + VWV(wct)) {
445                 DEBUG(2,("bad reply size for mid %d\n", mid));
446                 req->status = NT_STATUS_UNSUCCESSFUL;
447                 goto done;
448         }
449
450         req->in.hdr = hdr;
451         req->in.vwv = vwv;
452         req->in.wct = wct;
453         if (req->in.size >= NBT_HDR_SIZE + MIN_SMB_SIZE + VWV(wct)) {
454                 req->in.data = req->in.vwv + VWV(wct) + 2;
455                 req->in.data_size = SVAL(req->in.vwv, VWV(wct));
456                 if (req->in.size < NBT_HDR_SIZE + MIN_SMB_SIZE + VWV(wct) + req->in.data_size) {
457                         DEBUG(3,("bad data size for mid %d\n", mid));
458                         /* blergh - w2k3 gives a bogus data size values in some
459                            openX replies */
460                         req->in.data_size = req->in.size - (NBT_HDR_SIZE + MIN_SMB_SIZE + VWV(wct));
461                 }
462         }
463         req->in.ptr = req->in.data;
464         req->flags2 = SVAL(req->in.hdr, HDR_FLG2);
465
466         if (!(req->flags2 & FLAGS2_32_BIT_ERROR_CODES)) {
467                 transport->error.etype = ETYPE_DOS;
468                 transport->error.e.dos.eclass = CVAL(req->in.hdr,HDR_RCLS);
469                 transport->error.e.dos.ecode = SVAL(req->in.hdr,HDR_ERR);
470                 req->status = dos_to_ntstatus(transport->error.e.dos.eclass, 
471                                               transport->error.e.dos.ecode);
472         } else {
473                 transport->error.etype = ETYPE_NT;
474                 transport->error.e.nt_status = NT_STATUS(IVAL(req->in.hdr, HDR_RCLS));
475                 req->status = transport->error.e.nt_status;
476         }
477
478         if (!cli_request_check_sign_mac(req)) {
479                 transport->error.etype = ETYPE_SOCKET;
480                 transport->error.e.socket_error = SOCKET_READ_BAD_SIG;
481                 return False;
482         };
483
484 async:
485         /* if this request has an async handler then call that to
486            notify that the reply has been received. This might destroy
487            the request so it must happen last */
488         if (req->async.fn) {
489                 req->async.fn(req);
490         }
491
492 done:
493         talloc_destroy(mem_ctx);
494         return True;
495 }
496
497
498 /*
499   wait for a reply to be received for a packet that just returns an error
500   code and nothing more
501 */
502 NTSTATUS cli_request_simple_recv(struct cli_request *req)
503 {
504         cli_request_receive(req);
505         return cli_request_destroy(req);
506 }
507
508
509 /* Return true if the last packet was in error */
510 BOOL cli_request_is_error(struct cli_request *req)
511 {
512         return NT_STATUS_IS_ERR(req->status);
513 }
514
515 /*
516   append a string into the data portion of the request packet
517
518   return the number of bytes added to the packet
519 */
520 size_t cli_req_append_string(struct cli_request *req, const char *str, unsigned flags)
521 {
522         size_t len;
523
524         /* determine string type to use */
525         if (!(flags & (STR_ASCII|STR_UNICODE))) {
526                 flags |= (req->transport->negotiate.capabilities & CAP_UNICODE) ? STR_UNICODE : STR_ASCII;
527         }
528
529         len = (strlen(str)+2) * MAX_BYTES_PER_CHAR;             
530
531         cli_req_grow_allocation(req, len + req->out.data_size);
532
533         len = push_string(NULL, req->out.data + req->out.data_size, str, len, flags);
534
535         cli_req_grow_data(req, len + req->out.data_size);
536
537         return len;
538 }
539
540 /*
541   this is like cli_req_append_string but it also return the
542   non-terminated string byte length, which can be less than the number
543   of bytes consumed in the packet for 2 reasons:
544
545    1) the string in the packet may be null terminated
546    2) the string in the packet may need a 1 byte UCS2 alignment
547
548  this is used in places where the non-terminated string byte length is
549  placed in the packet as a separate field  
550 */
551 size_t cli_req_append_string_len(struct cli_request *req, const char *str, unsigned flags, int *len)
552 {
553         int diff = 0;
554         size_t ret;
555
556         /* determine string type to use */
557         if (!(flags & (STR_ASCII|STR_UNICODE))) {
558                 flags |= (req->transport->negotiate.capabilities & CAP_UNICODE) ? STR_UNICODE : STR_ASCII;
559         }
560
561         /* see if an alignment byte will be used */
562         if ((flags & STR_UNICODE) && !(flags & STR_NOALIGN)) {
563                 diff = ucs2_align(NULL, req->out.data + req->out.data_size, flags);
564         }
565
566         /* do the hard work */
567         ret = cli_req_append_string(req, str, flags);
568
569         /* see if we need to subtract the termination */
570         if (flags & STR_TERMINATE) {
571                 diff += (flags & STR_UNICODE) ? 2 : 1;
572         }
573
574         if (ret >= diff) {
575                 (*len) = ret - diff;
576         } else {
577                 (*len) = ret;
578         }
579
580         return ret;
581 }
582
583
584 /*
585   push a string into the data portion of the request packet, growing it if necessary
586   this gets quite tricky - please be very careful to cover all cases when modifying this
587
588   if dest is NULL, then put the string at the end of the data portion of the packet
589
590   if dest_len is -1 then no limit applies
591 */
592 size_t cli_req_append_ascii4(struct cli_request *req, const char *str, unsigned flags)
593 {
594         size_t size;
595         cli_req_append_bytes(req, (const uint8 *)"\4", 1);
596         size = cli_req_append_string(req, str, flags);
597         return size + 1;
598 }
599
600
601 /*
602   push a blob into the data portion of the request packet, growing it if necessary
603   this gets quite tricky - please be very careful to cover all cases when modifying this
604
605   if dest is NULL, then put the blob at the end of the data portion of the packet
606 */
607 size_t cli_req_append_blob(struct cli_request *req, const DATA_BLOB *blob)
608 {
609         cli_req_grow_allocation(req, req->out.data_size + blob->length);
610         memcpy(req->out.data + req->out.data_size, blob->data, blob->length);
611         cli_req_grow_data(req, req->out.data_size + blob->length);
612         return blob->length;
613 }
614
615 /*
616   append raw bytes into the data portion of the request packet
617   return the number of bytes added
618 */
619 size_t cli_req_append_bytes(struct cli_request *req, const uint8 *bytes, size_t byte_len)
620 {
621         cli_req_grow_allocation(req, byte_len + req->out.data_size);
622         memcpy(req->out.data + req->out.data_size, bytes, byte_len);
623         cli_req_grow_data(req, byte_len + req->out.data_size);
624         return byte_len;
625 }
626
627 /*
628   append variable block (type 5 buffer) into the data portion of the request packet
629   return the number of bytes added
630 */
631 size_t cli_req_append_var_block(struct cli_request *req, const uint8 *bytes, uint16 byte_len)
632 {
633         cli_req_grow_allocation(req, byte_len + 3 + req->out.data_size);
634         SCVAL(req->out.data + req->out.data_size, 0, 5);
635         SSVAL(req->out.data + req->out.data_size, 1, byte_len);         /* add field length */
636         if (byte_len > 0) {
637                 memcpy(req->out.data + req->out.data_size + 3, bytes, byte_len);
638         }
639         cli_req_grow_data(req, byte_len + 3 + req->out.data_size);
640         return byte_len + 3;
641 }
642
643
644 /*
645   pull a UCS2 string from a request packet, returning a talloced unix string
646
647   the string length is limited by the 3 things:
648    - the data size in the request (end of packet)
649    - the passed 'byte_len' if it is not -1
650    - the end of string (null termination)
651
652   Note that 'byte_len' is the number of bytes in the packet
653
654   on failure zero is returned and *dest is set to NULL, otherwise the number
655   of bytes consumed in the packet is returned
656 */
657 static size_t cli_req_pull_ucs2(struct cli_request *req, TALLOC_CTX *mem_ctx,
658                                 char **dest, const char *src, int byte_len, unsigned flags)
659 {
660         int src_len, src_len2, alignment=0;
661         ssize_t ret;
662
663         if (!(flags & STR_NOALIGN) && ucs2_align(req->in.buffer, src, flags)) {
664                 src++;
665                 alignment=1;
666                 if (byte_len != -1) {
667                         byte_len--;
668                 }
669         }
670
671         src_len = req->in.data_size - PTR_DIFF(src, req->in.data);
672         if (src_len < 0) {
673                 *dest = NULL;
674                 return 0;
675         }
676         if (byte_len != -1 && src_len > byte_len) {
677                 src_len = byte_len;
678         }
679
680         src_len2 = strnlen_w((const smb_ucs2_t *)src, src_len/2) * 2;
681         if (src_len2 < src_len - 2) {
682                 /* include the termination if we didn't reach the end of the packet */
683                 src_len2 += 2;
684         }
685
686         /* ucs2 strings must be at least 2 bytes long */
687         if (src_len2 < 2) {
688                 *dest = NULL;
689                 return 0;
690         }
691
692         ret = convert_string_talloc(mem_ctx, CH_UCS2, CH_UNIX, src, src_len2, (const void **)dest);
693         if (ret == -1) {
694                 *dest = NULL;
695                 return 0;
696         }
697
698         return src_len2 + alignment;
699 }
700
701 /*
702   pull a ascii string from a request packet, returning a talloced string
703
704   the string length is limited by the 3 things:
705    - the data size in the request (end of packet)
706    - the passed 'byte_len' if it is not -1
707    - the end of string (null termination)
708
709   Note that 'byte_len' is the number of bytes in the packet
710
711   on failure zero is returned and *dest is set to NULL, otherwise the number
712   of bytes consumed in the packet is returned
713 */
714 size_t cli_req_pull_ascii(struct cli_request *req, TALLOC_CTX *mem_ctx,
715                           char **dest, const char *src, int byte_len, unsigned flags)
716 {
717         int src_len, src_len2;
718         ssize_t ret;
719
720         src_len = req->in.data_size - PTR_DIFF(src, req->in.data);
721         if (src_len < 0) {
722                 *dest = NULL;
723                 return 0;
724         }
725         if (byte_len != -1 && src_len > byte_len) {
726                 src_len = byte_len;
727         }
728         src_len2 = strnlen(src, src_len);
729         if (src_len2 < src_len - 1) {
730                 /* include the termination if we didn't reach the end of the packet */
731                 src_len2++;
732         }
733
734         ret = convert_string_talloc(mem_ctx, CH_DOS, CH_UNIX, src, src_len2, (const void **)dest);
735
736         if (ret == -1) {
737                 *dest = NULL;
738                 return 0;
739         }
740
741         return ret;
742 }
743
744 /*
745   pull a string from a request packet, returning a talloced string
746
747   the string length is limited by the 3 things:
748    - the data size in the request (end of packet)
749    - the passed 'byte_len' if it is not -1
750    - the end of string (null termination)
751
752   Note that 'byte_len' is the number of bytes in the packet
753
754   on failure zero is returned and *dest is set to NULL, otherwise the number
755   of bytes consumed in the packet is returned
756 */
757 size_t cli_req_pull_string(struct cli_request *req, TALLOC_CTX *mem_ctx, 
758                            char **dest, const char *src, int byte_len, unsigned flags)
759 {
760         if (!(flags & STR_ASCII) && 
761             (((flags & STR_UNICODE) || (req->flags2 & FLAGS2_UNICODE_STRINGS)))) {
762                 return cli_req_pull_ucs2(req, mem_ctx, dest, src, byte_len, flags);
763         }
764
765         return cli_req_pull_ascii(req, mem_ctx, dest, src, byte_len, flags);
766 }
767
768
769 /*
770   pull a DATA_BLOB from a reply packet, returning a talloced blob
771   make sure we don't go past end of packet
772
773   if byte_len is -1 then limit the blob only by packet size
774 */
775 DATA_BLOB cli_req_pull_blob(struct cli_request *req, TALLOC_CTX *mem_ctx, const char *src, int byte_len)
776 {
777         int src_len;
778
779         src_len = req->in.data_size - PTR_DIFF(src, req->in.data);
780
781         if (src_len < 0) {
782                 return data_blob(NULL, 0);
783         }
784
785         if (byte_len != -1 && src_len > byte_len) {
786                 src_len = byte_len;
787         }
788
789         return data_blob_talloc(mem_ctx, src, src_len);
790 }
791
792 /* check that a lump of data in a request is within the bounds of the data section of
793    the packet */
794 static BOOL cli_req_data_oob(struct cli_request *req, const char *ptr, uint32 count)
795 {
796         /* be careful with wraparound! */
797         if (ptr < req->in.data ||
798             ptr >= req->in.data + req->in.data_size ||
799             count > req->in.data_size ||
800             ptr + count > req->in.data + req->in.data_size) {
801                 return True;
802         }
803         return False;
804 }
805
806 /*
807   pull a lump of data from a request packet
808
809   return False if any part is outside the data portion of the packet
810 */
811 BOOL cli_raw_pull_data(struct cli_request *req, const char *src, int len, char *dest)
812 {
813         if (len == 0) return True;
814
815         if (cli_req_data_oob(req, src, len)) {
816                 return False;
817         }
818
819         memcpy(dest, src, len);
820         return True;
821 }
822
823
824 /*
825   put a NTTIME into a packet
826 */
827
828 void cli_push_nttime(void *base, uint16 offset, NTTIME *t)
829 {
830         SIVAL(base, offset,   t->low);
831         SIVAL(base, offset+4, t->high);
832 }
833
834 /*
835   pull a NTTIME from a packet
836 */
837 NTTIME cli_pull_nttime(void *base, uint16 offset)
838 {
839         NTTIME ret;
840         ret.low = IVAL(base, offset);
841         ret.high = IVAL(base, offset+4);
842         return ret;
843 }
844
845 /*
846   pull a UCS2 string from a blob, returning a talloced unix string
847
848   the string length is limited by the 3 things:
849    - the data size in the blob
850    - the passed 'byte_len' if it is not -1
851    - the end of string (null termination)
852
853   Note that 'byte_len' is the number of bytes in the packet
854
855   on failure zero is returned and *dest is set to NULL, otherwise the number
856   of bytes consumed in the blob is returned
857 */
858 static size_t cli_blob_pull_ucs2(TALLOC_CTX* mem_ctx,
859                                  DATA_BLOB *blob, const char **dest, 
860                                  const char *src, int byte_len, unsigned flags)
861 {
862         int src_len, src_len2, alignment=0;
863         ssize_t ret;
864
865         if (src < (const char *)blob->data ||
866             src >= (const char *)(blob->data + blob->length)) {
867                 *dest = NULL;
868                 return 0;
869         }
870
871         src_len = blob->length - PTR_DIFF(src, blob->data);
872
873         if (byte_len != -1 && src_len > byte_len) {
874                 src_len = byte_len;
875         }
876
877         if (!(flags & STR_NOALIGN) && ucs2_align(blob->data, src, flags)) {
878                 src++;
879                 alignment=1;
880                 src_len--;
881         }
882
883         if (src_len < 2) {
884                 *dest = NULL;
885                 return 0;
886         }
887
888         src_len2 = strnlen_w((const smb_ucs2_t *)src, src_len/2) * 2;
889
890         if (src_len2 < src_len - 2) {
891                 /* include the termination if we didn't reach the end of the packet */
892                 src_len2 += 2;
893         }
894
895         ret = convert_string_talloc(mem_ctx, CH_UCS2, CH_UNIX, src, src_len2, (const void **)dest);
896         if (ret == -1) {
897                 *dest = NULL;
898                 return 0;
899         }
900
901         return src_len2 + alignment;
902 }
903
904 /*
905   pull a ascii string from a blob, returning a talloced string
906
907   the string length is limited by the 3 things:
908    - the data size in the blob
909    - the passed 'byte_len' if it is not -1
910    - the end of string (null termination)
911
912   Note that 'byte_len' is the number of bytes in the blob
913
914   on failure zero is returned and *dest is set to NULL, otherwise the number
915   of bytes consumed in the blob is returned
916 */
917 static size_t cli_blob_pull_ascii(TALLOC_CTX *mem_ctx,
918                                   DATA_BLOB *blob, const char **dest, 
919                                   const char *src, int byte_len, unsigned flags)
920 {
921         int src_len, src_len2;
922         ssize_t ret;
923
924         src_len = blob->length - PTR_DIFF(src, blob->data);
925         if (src_len < 0) {
926                 *dest = NULL;
927                 return 0;
928         }
929         if (byte_len != -1 && src_len > byte_len) {
930                 src_len = byte_len;
931         }
932         src_len2 = strnlen(src, src_len);
933
934         if (src_len2 < src_len - 1) {
935                 /* include the termination if we didn't reach the end of the packet */
936                 src_len2++;
937         }
938
939         ret = convert_string_talloc(mem_ctx, CH_DOS, CH_UNIX, src, src_len2, (const void **)dest);
940
941         if (ret == -1) {
942                 *dest = NULL;
943                 return 0;
944         }
945
946         return ret;
947 }
948
949 /*
950   pull a string from a blob, returning a talloced WIRE_STRING
951
952   the string length is limited by the 3 things:
953    - the data size in the blob
954    - length field on the wire
955    - the end of string (null termination)
956
957    if STR_LEN8BIT is set in the flags then assume the length field is
958    8 bits, instead of 32
959
960   on failure zero is returned and dest->s is set to NULL, otherwise the number
961   of bytes consumed in the blob is returned
962 */
963 size_t cli_blob_pull_string(struct cli_session *session,
964                             TALLOC_CTX *mem_ctx,
965                             DATA_BLOB *blob, 
966                             WIRE_STRING *dest, 
967                             uint16 len_offset, uint16 str_offset, 
968                             unsigned flags)
969 {
970         int extra;
971         dest->s = NULL;
972         
973         if (len_offset > blob->length-4) {
974                 return 0;
975         }
976         if (flags & STR_LEN8BIT) {
977                 dest->private_length = CVAL(blob->data, len_offset);
978         } else {
979                 dest->private_length = IVAL(blob->data, len_offset);
980         }
981         extra = 0;
982         dest->s = NULL;
983         if (!(flags & STR_ASCII) && 
984             ((flags & STR_UNICODE) || 
985              (session->transport->negotiate.capabilities & CAP_UNICODE))) {
986                 int align = 0;
987                 if ((str_offset&1) && !(flags & STR_NOALIGN)) {
988                         align = 1;
989                 }
990                 if (flags & STR_LEN_NOTERM) {
991                         extra = 2;
992                 }
993                 return align + extra + cli_blob_pull_ucs2(mem_ctx, blob, &dest->s, 
994                                                           blob->data+str_offset+align, 
995                                                           dest->private_length, flags);
996         }
997
998         if (flags & STR_LEN_NOTERM) {
999                 extra = 1;
1000         }
1001
1002         return extra + cli_blob_pull_ascii(mem_ctx, blob, &dest->s, 
1003                                            blob->data+str_offset, dest->private_length, flags);
1004 }
1005
1006 /*
1007   append a string into a blob
1008 */
1009 size_t cli_blob_append_string(struct cli_session *session,
1010                               TALLOC_CTX *mem_ctx, DATA_BLOB *blob, 
1011                               const char *str, unsigned flags)
1012 {
1013         size_t max_len;
1014         int len;
1015
1016         if (!str) return 0;
1017
1018         /* determine string type to use */
1019         if (!(flags & (STR_ASCII|STR_UNICODE))) {
1020                 flags |= (session->transport->negotiate.capabilities & CAP_UNICODE) ? STR_UNICODE : STR_ASCII;
1021         }
1022
1023         max_len = (strlen(str)+2) * MAX_BYTES_PER_CHAR;         
1024
1025         blob->data = talloc_realloc(mem_ctx, blob->data, blob->length + max_len);
1026         if (!blob->data) {
1027                 return 0;
1028         }
1029
1030         len = push_string(NULL, blob->data + blob->length, str, max_len, flags);
1031
1032         blob->length += len;
1033
1034         return len;
1035 }