I think I've finally got the ascii/unicode issues right in trans2 find
[amitay/samba.git] / source4 / 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         if (IVAL(req->out.buffer, 0) == 0) {
273                 _smb_setlen(req->out.buffer, req->out.size - NBT_HDR_SIZE);
274         }
275
276         cli_request_calculate_sign_mac(req);
277
278         if (req->out.size != cli_sock_write(req->transport->socket, req->out.buffer, req->out.size)) {
279                 req->transport->error.etype = ETYPE_SOCKET;
280                 req->transport->error.e.socket_error = SOCKET_WRITE_ERROR;
281                 DEBUG(0,("Error writing %d bytes to server - %s\n",
282                          (int)req->out.size, strerror(errno)));
283                 return False;
284         }
285
286         /* add it to the list of pending requests */
287         DLIST_ADD(req->transport->pending_requests, req);
288         
289         return True;
290 }
291
292
293 /*
294   receive a response to a packet
295 */
296 BOOL cli_request_receive(struct cli_request *req)
297 {
298         /* req can be NULL when a send has failed. This eliminates lots of NULL
299            checks in each module */
300         if (!req) return False;
301
302         /* keep receiving packets until this one is replied to */
303         while (!req->in.buffer) {
304                 if (!cli_transport_select(req->transport)) {
305                         return False;
306                 }
307
308                 cli_request_receive_next(req->transport);
309         }
310
311         return True;
312 }
313
314
315 /*
316   handle oplock break requests from the server - return True if the request was
317   an oplock break
318 */
319 static BOOL handle_oplock_break(struct cli_transport *transport, uint_t len, const char *hdr, const char *vwv)
320 {
321         /* we must be very fussy about what we consider an oplock break to avoid
322            matching readbraw replies */
323         if (len != MIN_SMB_SIZE + VWV(8) ||
324             (CVAL(hdr, HDR_FLG) & FLAG_REPLY) ||
325             CVAL(hdr,HDR_COM) != SMBlockingX ||
326             SVAL(hdr, HDR_MID) != 0xFFFF ||
327             SVAL(vwv,VWV(6)) != 0 ||
328             SVAL(vwv,VWV(7)) != 0) {
329                 return False;
330         }
331
332         if (transport->oplock.handler) {
333                 uint16 tid = SVAL(hdr, HDR_TID);
334                 uint16 fnum = SVAL(vwv,VWV(2));
335                 uint8 level = CVAL(vwv,VWV(3));
336                 transport->oplock.handler(transport, tid, fnum, level, transport->oplock.private);
337         }
338
339         return True;
340 }
341
342
343 /*
344   receive an async message from the server
345   this function assumes that the caller already knows that the socket is readable
346   and that there is a packet waiting
347
348   The packet is not actually returned by this function, instead any
349   registered async message handlers are called
350
351   return True if a packet was successfully received and processed
352   return False if the socket appears to be dead
353 */
354 BOOL cli_request_receive_next(struct cli_transport *transport)
355 {
356         BOOL ret;
357         int len;
358         char header[NBT_HDR_SIZE];
359         char *buffer, *hdr, *vwv;
360         TALLOC_CTX *mem_ctx;
361         struct cli_request *req;
362         uint16 wct, mid = 0;
363
364         len = cli_sock_read(transport->socket, header, 4);
365         if (len != 4) {
366                 return False;
367         }
368         
369         len = smb_len(header);
370
371         mem_ctx = talloc_init("cli_request_receive_next");
372         
373         /* allocate the incoming buffer at the right size */
374         buffer = talloc(mem_ctx, len+NBT_HDR_SIZE);
375         if (!buffer) {
376                 talloc_destroy(mem_ctx);
377                 return False;
378         }
379
380         /* fill in the already received header */
381         memcpy(buffer, header, NBT_HDR_SIZE);
382
383         ret = cli_sock_read(transport->socket, buffer + NBT_HDR_SIZE, len);
384         /* If the server is not responding, note that now */
385         if (ret != len) {
386                 return False;
387         }
388
389         hdr = buffer+NBT_HDR_SIZE;
390         vwv = hdr + HDR_VWV;
391
392         /* see if it could be an oplock break request */
393         if (handle_oplock_break(transport, len, hdr, vwv)) {
394                 goto done;
395         }
396
397         /* at this point we need to check for a readbraw reply, as these can be any length */
398         if (transport->readbraw_pending) {
399                 transport->readbraw_pending = 0;
400
401                 /* it must match the first entry in the pending queue as the client is not allowed
402                    to have outstanding readbraw requests */
403                 req = transport->pending_requests;
404                 if (!req) goto done;
405
406                 req->in.buffer = buffer;
407                 talloc_steal(mem_ctx, req->mem_ctx, buffer);
408                 req->in.size = len + NBT_HDR_SIZE;
409                 req->in.allocated = req->in.size;
410                 goto async;
411         }
412
413         if (len >= MIN_SMB_SIZE) {
414                 /* extract the mid for matching to pending requests */
415                 mid = SVAL(hdr, HDR_MID);
416                 wct = CVAL(hdr, HDR_WCT);
417         }
418
419         /* match the incoming request against the list of pending requests */
420         for (req=transport->pending_requests; req; req=req->next) {
421                 if (req->mid == mid) break;
422         }
423
424         if (!req) {
425                 DEBUG(3,("Discarding unmatched reply with mid %d\n", mid));
426                 goto done;
427         }
428
429         /* fill in the 'in' portion of the matching request */
430         req->in.buffer = buffer;
431         talloc_steal(mem_ctx, req->mem_ctx, buffer);
432         req->in.size = len + NBT_HDR_SIZE;
433         req->in.allocated = req->in.size;
434
435         /* handle non-SMB replies */
436         if (req->in.size < NBT_HDR_SIZE + MIN_SMB_SIZE) {
437                 goto done;
438         }
439
440         if (req->in.size < NBT_HDR_SIZE + MIN_SMB_SIZE + VWV(wct)) {
441                 DEBUG(2,("bad reply size for mid %d\n", mid));
442                 req->status = NT_STATUS_UNSUCCESSFUL;
443                 goto done;
444         }
445
446         req->in.hdr = hdr;
447         req->in.vwv = vwv;
448         req->in.wct = wct;
449         if (req->in.size >= NBT_HDR_SIZE + MIN_SMB_SIZE + VWV(wct)) {
450                 req->in.data = req->in.vwv + VWV(wct) + 2;
451                 req->in.data_size = SVAL(req->in.vwv, VWV(wct));
452                 if (req->in.size < NBT_HDR_SIZE + MIN_SMB_SIZE + VWV(wct) + req->in.data_size) {
453                         DEBUG(3,("bad data size for mid %d\n", mid));
454                         /* blergh - w2k3 gives a bogus data size values in some
455                            openX replies */
456                         req->in.data_size = req->in.size - (NBT_HDR_SIZE + MIN_SMB_SIZE + VWV(wct));
457                 }
458         }
459         req->in.ptr = req->in.data;
460         req->flags2 = SVAL(req->in.hdr, HDR_FLG2);
461
462         if (!(req->flags2 & FLAGS2_32_BIT_ERROR_CODES)) {
463                 transport->error.etype = ETYPE_DOS;
464                 transport->error.e.dos.eclass = CVAL(req->in.hdr,HDR_RCLS);
465                 transport->error.e.dos.ecode = SVAL(req->in.hdr,HDR_ERR);
466                 req->status = dos_to_ntstatus(transport->error.e.dos.eclass, 
467                                               transport->error.e.dos.ecode);
468         } else {
469                 transport->error.etype = ETYPE_NT;
470                 transport->error.e.nt_status = NT_STATUS(IVAL(req->in.hdr, HDR_RCLS));
471                 req->status = transport->error.e.nt_status;
472         }
473
474         if (!cli_request_check_sign_mac(req)) {
475                 transport->error.etype = ETYPE_SOCKET;
476                 transport->error.e.socket_error = SOCKET_READ_BAD_SIG;
477                 return False;
478         };
479
480 async:
481         /* if this request has an async handler then call that to
482            notify that the reply has been received. This might destroy
483            the request so it must happen last */
484         if (req->async.fn) {
485                 req->async.fn(req);
486         }
487
488 done:
489         talloc_destroy(mem_ctx);
490         return True;
491 }
492
493
494 /*
495   wait for a reply to be received for a packet that just returns an error
496   code and nothing more
497 */
498 NTSTATUS cli_request_simple_recv(struct cli_request *req)
499 {
500         cli_request_receive(req);
501         return cli_request_destroy(req);
502 }
503
504
505 /* Return true if the last packet was in error */
506 BOOL cli_request_is_error(struct cli_request *req)
507 {
508         return NT_STATUS_IS_ERR(req->status);
509 }
510
511 /*
512   append a string into the data portion of the request packet
513
514   return the number of bytes added to the packet
515 */
516 size_t cli_req_append_string(struct cli_request *req, const char *str, unsigned flags)
517 {
518         size_t len;
519
520         /* determine string type to use */
521         if (!(flags & (STR_ASCII|STR_UNICODE))) {
522                 flags |= (req->transport->negotiate.capabilities & CAP_UNICODE) ? STR_UNICODE : STR_ASCII;
523         }
524
525         len = (strlen(str)+2) * MAX_BYTES_PER_CHAR;             
526
527         cli_req_grow_allocation(req, len + req->out.data_size);
528
529         len = push_string(NULL, req->out.data + req->out.data_size, str, len, flags);
530
531         cli_req_grow_data(req, len + req->out.data_size);
532
533         return len;
534 }
535
536 /*
537   this is like cli_req_append_string but it also return the
538   non-terminated string byte length, which can be less than the number
539   of bytes consumed in the packet for 2 reasons:
540
541    1) the string in the packet may be null terminated
542    2) the string in the packet may need a 1 byte UCS2 alignment
543
544  this is used in places where the non-terminated string byte length is
545  placed in the packet as a separate field  
546 */
547 size_t cli_req_append_string_len(struct cli_request *req, const char *str, unsigned flags, int *len)
548 {
549         int diff = 0;
550         size_t ret;
551
552         /* determine string type to use */
553         if (!(flags & (STR_ASCII|STR_UNICODE))) {
554                 flags |= (req->transport->negotiate.capabilities & CAP_UNICODE) ? STR_UNICODE : STR_ASCII;
555         }
556
557         /* see if an alignment byte will be used */
558         if ((flags & STR_UNICODE) && !(flags & STR_NOALIGN)) {
559                 diff = ucs2_align(NULL, req->out.data + req->out.data_size, flags);
560         }
561
562         /* do the hard work */
563         ret = cli_req_append_string(req, str, flags);
564
565         /* see if we need to subtract the termination */
566         if (flags & STR_TERMINATE) {
567                 diff += (flags & STR_UNICODE) ? 2 : 1;
568         }
569
570         if (ret >= diff) {
571                 (*len) = ret - diff;
572         } else {
573                 (*len) = ret;
574         }
575
576         return ret;
577 }
578
579
580 /*
581   push a string into the data portion of the request packet, growing it if necessary
582   this gets quite tricky - please be very careful to cover all cases when modifying this
583
584   if dest is NULL, then put the string at the end of the data portion of the packet
585
586   if dest_len is -1 then no limit applies
587 */
588 size_t cli_req_append_ascii4(struct cli_request *req, const char *str, unsigned flags)
589 {
590         size_t size;
591         cli_req_append_bytes(req, (const uint8 *)"\4", 1);
592         size = cli_req_append_string(req, str, flags);
593         return size + 1;
594 }
595
596
597 /*
598   push a blob into the data portion of the request packet, growing it if necessary
599   this gets quite tricky - please be very careful to cover all cases when modifying this
600
601   if dest is NULL, then put the blob at the end of the data portion of the packet
602 */
603 size_t cli_req_append_blob(struct cli_request *req, const DATA_BLOB *blob)
604 {
605         cli_req_grow_allocation(req, req->out.data_size + blob->length);
606         memcpy(req->out.data + req->out.data_size, blob->data, blob->length);
607         cli_req_grow_data(req, req->out.data_size + blob->length);
608         return blob->length;
609 }
610
611 /*
612   append raw bytes into the data portion of the request packet
613   return the number of bytes added
614 */
615 size_t cli_req_append_bytes(struct cli_request *req, const uint8 *bytes, size_t byte_len)
616 {
617         cli_req_grow_allocation(req, byte_len + req->out.data_size);
618         memcpy(req->out.data + req->out.data_size, bytes, byte_len);
619         cli_req_grow_data(req, byte_len + req->out.data_size);
620         return byte_len;
621 }
622
623 /*
624   append variable block (type 5 buffer) into the data portion of the request packet
625   return the number of bytes added
626 */
627 size_t cli_req_append_var_block(struct cli_request *req, const uint8 *bytes, uint16 byte_len)
628 {
629         cli_req_grow_allocation(req, byte_len + 3 + req->out.data_size);
630         SCVAL(req->out.data + req->out.data_size, 0, 5);
631         SSVAL(req->out.data + req->out.data_size, 1, byte_len);         /* add field length */
632         if (byte_len > 0) {
633                 memcpy(req->out.data + req->out.data_size + 3, bytes, byte_len);
634         }
635         cli_req_grow_data(req, byte_len + 3 + req->out.data_size);
636         return byte_len + 3;
637 }
638
639
640 /*
641   pull a UCS2 string from a request packet, returning a talloced unix string
642
643   the string length is limited by the 3 things:
644    - the data size in the request (end of packet)
645    - the passed 'byte_len' if it is not -1
646    - the end of string (null termination)
647
648   Note that 'byte_len' is the number of bytes in the packet
649
650   on failure zero is returned and *dest is set to NULL, otherwise the number
651   of bytes consumed in the packet is returned
652 */
653 static size_t cli_req_pull_ucs2(struct cli_request *req, TALLOC_CTX *mem_ctx,
654                                 char **dest, const char *src, int byte_len, unsigned flags)
655 {
656         int src_len, src_len2, alignment=0;
657         ssize_t ret;
658
659         if (!(flags & STR_NOALIGN) && ucs2_align(req->in.buffer, src, flags)) {
660                 src++;
661                 alignment=1;
662                 if (byte_len != -1) {
663                         byte_len--;
664                 }
665         }
666
667         src_len = req->in.data_size - PTR_DIFF(src, req->in.data);
668         if (src_len < 0) {
669                 *dest = NULL;
670                 return 0;
671         }
672         if (byte_len != -1 && src_len > byte_len) {
673                 src_len = byte_len;
674         }
675
676         src_len2 = strnlen_w((const smb_ucs2_t *)src, src_len/2) * 2;
677         if (src_len2 < src_len - 2) {
678                 /* include the termination if we didn't reach the end of the packet */
679                 src_len2 += 2;
680         }
681
682         /* ucs2 strings must be at least 2 bytes long */
683         if (src_len2 < 2) {
684                 *dest = NULL;
685                 return 0;
686         }
687
688         ret = convert_string_talloc(mem_ctx, CH_UCS2, CH_UNIX, src, src_len2, (const void **)dest);
689         if (ret == -1) {
690                 *dest = NULL;
691                 return 0;
692         }
693
694         return src_len2 + alignment;
695 }
696
697 /*
698   pull a ascii string from a request packet, returning a talloced string
699
700   the string length is limited by the 3 things:
701    - the data size in the request (end of packet)
702    - the passed 'byte_len' if it is not -1
703    - the end of string (null termination)
704
705   Note that 'byte_len' is the number of bytes in the packet
706
707   on failure zero is returned and *dest is set to NULL, otherwise the number
708   of bytes consumed in the packet is returned
709 */
710 size_t cli_req_pull_ascii(struct cli_request *req, TALLOC_CTX *mem_ctx,
711                           char **dest, const char *src, int byte_len, unsigned flags)
712 {
713         int src_len, src_len2;
714         ssize_t ret;
715
716         src_len = req->in.data_size - PTR_DIFF(src, req->in.data);
717         if (src_len < 0) {
718                 *dest = NULL;
719                 return 0;
720         }
721         if (byte_len != -1 && src_len > byte_len) {
722                 src_len = byte_len;
723         }
724         src_len2 = strnlen(src, src_len);
725         if (src_len2 < src_len - 1) {
726                 /* include the termination if we didn't reach the end of the packet */
727                 src_len2++;
728         }
729
730         ret = convert_string_talloc(mem_ctx, CH_DOS, CH_UNIX, src, src_len2, (const void **)dest);
731
732         if (ret == -1) {
733                 *dest = NULL;
734                 return 0;
735         }
736
737         return ret;
738 }
739
740 /*
741   pull a string from a request packet, returning a talloced string
742
743   the string length is limited by the 3 things:
744    - the data size in the request (end of packet)
745    - the passed 'byte_len' if it is not -1
746    - the end of string (null termination)
747
748   Note that 'byte_len' is the number of bytes in the packet
749
750   on failure zero is returned and *dest is set to NULL, otherwise the number
751   of bytes consumed in the packet is returned
752 */
753 size_t cli_req_pull_string(struct cli_request *req, TALLOC_CTX *mem_ctx, 
754                            char **dest, const char *src, int byte_len, unsigned flags)
755 {
756         if (!(flags & STR_ASCII) && 
757             (((flags & STR_UNICODE) || (req->flags2 & FLAGS2_UNICODE_STRINGS)))) {
758                 return cli_req_pull_ucs2(req, mem_ctx, dest, src, byte_len, flags);
759         }
760
761         return cli_req_pull_ascii(req, mem_ctx, dest, src, byte_len, flags);
762 }
763
764
765 /*
766   pull a DATA_BLOB from a reply packet, returning a talloced blob
767   make sure we don't go past end of packet
768
769   if byte_len is -1 then limit the blob only by packet size
770 */
771 DATA_BLOB cli_req_pull_blob(struct cli_request *req, TALLOC_CTX *mem_ctx, const char *src, int byte_len)
772 {
773         int src_len;
774
775         src_len = req->in.data_size - PTR_DIFF(src, req->in.data);
776
777         if (src_len < 0) {
778                 return data_blob(NULL, 0);
779         }
780
781         if (byte_len != -1 && src_len > byte_len) {
782                 src_len = byte_len;
783         }
784
785         return data_blob_talloc(mem_ctx, src, src_len);
786 }
787
788 /* check that a lump of data in a request is within the bounds of the data section of
789    the packet */
790 static BOOL cli_req_data_oob(struct cli_request *req, const char *ptr, uint32 count)
791 {
792         /* be careful with wraparound! */
793         if (ptr < req->in.data ||
794             ptr >= req->in.data + req->in.data_size ||
795             count > req->in.data_size ||
796             ptr + count > req->in.data + req->in.data_size) {
797                 return True;
798         }
799         return False;
800 }
801
802 /*
803   pull a lump of data from a request packet
804
805   return False if any part is outside the data portion of the packet
806 */
807 BOOL cli_raw_pull_data(struct cli_request *req, const char *src, int len, char *dest)
808 {
809         if (len == 0) return True;
810
811         if (cli_req_data_oob(req, src, len)) {
812                 return False;
813         }
814
815         memcpy(dest, src, len);
816         return True;
817 }
818
819
820 /*
821   put a NTTIME into a packet
822 */
823
824 void cli_push_nttime(void *base, uint16 offset, NTTIME *t)
825 {
826         SIVAL(base, offset,   t->low);
827         SIVAL(base, offset+4, t->high);
828 }
829
830 /*
831   pull a NTTIME from a packet
832 */
833 NTTIME cli_pull_nttime(void *base, uint16 offset)
834 {
835         NTTIME ret;
836         ret.low = IVAL(base, offset);
837         ret.high = IVAL(base, offset+4);
838         return ret;
839 }
840
841 /*
842   pull a UCS2 string from a blob, returning a talloced unix string
843
844   the string length is limited by the 3 things:
845    - the data size in the blob
846    - the passed 'byte_len' if it is not -1
847    - the end of string (null termination)
848
849   Note that 'byte_len' is the number of bytes in the packet
850
851   on failure zero is returned and *dest is set to NULL, otherwise the number
852   of bytes consumed in the blob is returned
853 */
854 static size_t cli_blob_pull_ucs2(TALLOC_CTX* mem_ctx,
855                                  DATA_BLOB *blob, const char **dest, 
856                                  const char *src, int byte_len, unsigned flags)
857 {
858         int src_len, src_len2, alignment=0;
859         ssize_t ret;
860
861         if (src < (const char *)blob->data ||
862             src >= (const char *)(blob->data + blob->length)) {
863                 *dest = NULL;
864                 return 0;
865         }
866
867         src_len = blob->length - PTR_DIFF(src, blob->data);
868
869         if (byte_len != -1 && src_len > byte_len) {
870                 src_len = byte_len;
871         }
872
873         if (!(flags & STR_NOALIGN) && ucs2_align(blob->data, src, flags)) {
874                 src++;
875                 alignment=1;
876                 src_len--;
877         }
878
879         if (src_len < 2) {
880                 *dest = NULL;
881                 return 0;
882         }
883
884         src_len2 = strnlen_w((const smb_ucs2_t *)src, src_len/2) * 2;
885
886         if (src_len2 < src_len - 2) {
887                 /* include the termination if we didn't reach the end of the packet */
888                 src_len2 += 2;
889         }
890
891         ret = convert_string_talloc(mem_ctx, CH_UCS2, CH_UNIX, src, src_len2, (const void **)dest);
892         if (ret == -1) {
893                 *dest = NULL;
894                 return 0;
895         }
896
897         return src_len2 + alignment;
898 }
899
900 /*
901   pull a ascii string from a blob, returning a talloced string
902
903   the string length is limited by the 3 things:
904    - the data size in the blob
905    - the passed 'byte_len' if it is not -1
906    - the end of string (null termination)
907
908   Note that 'byte_len' is the number of bytes in the blob
909
910   on failure zero is returned and *dest is set to NULL, otherwise the number
911   of bytes consumed in the blob is returned
912 */
913 static size_t cli_blob_pull_ascii(TALLOC_CTX *mem_ctx,
914                                   DATA_BLOB *blob, const char **dest, 
915                                   const char *src, int byte_len, unsigned flags)
916 {
917         int src_len, src_len2;
918         ssize_t ret;
919
920         src_len = blob->length - PTR_DIFF(src, blob->data);
921         if (src_len < 0) {
922                 *dest = NULL;
923                 return 0;
924         }
925         if (byte_len != -1 && src_len > byte_len) {
926                 src_len = byte_len;
927         }
928         src_len2 = strnlen(src, src_len);
929
930         if (src_len2 < src_len - 1) {
931                 /* include the termination if we didn't reach the end of the packet */
932                 src_len2++;
933         }
934
935         ret = convert_string_talloc(mem_ctx, CH_DOS, CH_UNIX, src, src_len2, (const void **)dest);
936
937         if (ret == -1) {
938                 *dest = NULL;
939                 return 0;
940         }
941
942         return ret;
943 }
944
945 /*
946   pull a string from a blob, returning a talloced WIRE_STRING
947
948   the string length is limited by the 3 things:
949    - the data size in the blob
950    - length field on the wire
951    - the end of string (null termination)
952
953    if STR_LEN8BIT is set in the flags then assume the length field is
954    8 bits, instead of 32
955
956   on failure zero is returned and dest->s is set to NULL, otherwise the number
957   of bytes consumed in the blob is returned
958 */
959 size_t cli_blob_pull_string(struct cli_session *session,
960                             TALLOC_CTX *mem_ctx,
961                             DATA_BLOB *blob, 
962                             WIRE_STRING *dest, 
963                             uint16 len_offset, uint16 str_offset, 
964                             unsigned flags)
965 {
966         int extra;
967         dest->s = NULL;
968         
969         if (len_offset > blob->length-4) {
970                 return 0;
971         }
972         if (flags & STR_LEN8BIT) {
973                 dest->private_length = CVAL(blob->data, len_offset);
974         } else {
975                 dest->private_length = IVAL(blob->data, len_offset);
976         }
977         extra = 0;
978         dest->s = NULL;
979         if (!(flags & STR_ASCII) && 
980             ((flags & STR_UNICODE) || 
981              (session->transport->negotiate.capabilities & CAP_UNICODE))) {
982                 int align = 0;
983                 if ((str_offset&1) && !(flags & STR_NOALIGN)) {
984                         align = 1;
985                 }
986                 if (flags & STR_LEN_NOTERM) {
987                         extra = 2;
988                 }
989                 return align + extra + cli_blob_pull_ucs2(mem_ctx, blob, &dest->s, 
990                                                           blob->data+str_offset+align, 
991                                                           dest->private_length, flags);
992         }
993
994         if (flags & STR_LEN_NOTERM) {
995                 extra = 1;
996         }
997
998         return extra + cli_blob_pull_ascii(mem_ctx, blob, &dest->s, 
999                                            blob->data+str_offset, dest->private_length, flags);
1000 }
1001
1002 /*
1003   append a string into a blob
1004 */
1005 size_t cli_blob_append_string(struct cli_session *session,
1006                               TALLOC_CTX *mem_ctx, DATA_BLOB *blob, 
1007                               const char *str, unsigned flags)
1008 {
1009         size_t max_len;
1010         int len;
1011
1012         if (!str) return 0;
1013
1014         /* determine string type to use */
1015         if (!(flags & (STR_ASCII|STR_UNICODE))) {
1016                 flags |= (session->transport->negotiate.capabilities & CAP_UNICODE) ? STR_UNICODE : STR_ASCII;
1017         }
1018
1019         max_len = (strlen(str)+2) * MAX_BYTES_PER_CHAR;         
1020
1021         blob->data = talloc_realloc(mem_ctx, blob->data, blob->length + max_len);
1022         if (!blob->data) {
1023                 return 0;
1024         }
1025
1026         len = push_string(NULL, blob->data + blob->length, str, max_len, flags);
1027
1028         blob->length += len;
1029
1030         return len;
1031 }