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