de4a06bd2d2dba71bb0622d85510e1f9cc37a658
[metze/samba/wip.git] / source3 / libsmb / smb2cli_base.c
1 /*
2    Unix SMB/CIFS implementation.
3    smb2 lib
4    Copyright (C) Volker Lendecke 2011
5    Copyright (C) Stefan Metzmacher 2011
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 3 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, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "includes.h"
22 #include "client.h"
23 #include "libcli/smb/read_smb.h"
24 #include "smb2cli_base.h"
25 #include "libsmb/proto.h"
26 #include "lib/async_req/async_sock.h"
27 #include "lib/util/tevent_ntstatus.h"
28
29 struct smb2cli_req_state {
30         struct tevent_context *ev;
31         struct cli_state *cli;
32
33         const uint8_t *fixed;
34         uint16_t fixed_len;
35         const uint8_t *dyn;
36         uint32_t dyn_len;
37
38         uint8_t nbt[4];
39         uint8_t hdr[64];
40         uint8_t pad[7]; /* padding space for compounding */
41
42         /* always an array of 3 talloc elements */
43         struct iovec *recv_iov;
44 };
45
46 static void smb2cli_req_unset_pending(struct tevent_req *req)
47 {
48         struct smb2cli_req_state *state =
49                 tevent_req_data(req,
50                 struct smb2cli_req_state);
51         struct cli_state *cli = state->cli;
52         int num_pending = talloc_array_length(cli->conn.pending);
53         int i;
54
55         talloc_set_destructor(req, NULL);
56
57         if (num_pending == 1) {
58                 /*
59                  * The pending read_smb tevent_req is a child of
60                  * cli->conn.pending. So if nothing is pending anymore,
61                  * we need to delete the socket read fde.
62                  */
63                 TALLOC_FREE(cli->conn.pending);
64                 return;
65         }
66
67         for (i=0; i<num_pending; i++) {
68                 if (req == cli->conn.pending[i]) {
69                         break;
70                 }
71         }
72         if (i == num_pending) {
73                 /*
74                  * Something's seriously broken. Just returning here is the
75                  * right thing nevertheless, the point of this routine is to
76                  * remove ourselves from cli->conn.pending.
77                  */
78                 return;
79         }
80
81         /*
82          * Remove ourselves from the cli->pending array
83          */
84         for (; i < (num_pending - 1); i++) {
85                 cli->conn.pending[i] = cli->conn.pending[i+1];
86         }
87
88         /*
89          * No NULL check here, we're shrinking by sizeof(void *), and
90          * talloc_realloc just adjusts the size for this.
91          */
92         cli->conn.pending = talloc_realloc(NULL, cli->conn.pending,
93                                            struct tevent_req *,
94                                            num_pending - 1);
95         return;
96 }
97
98 static int smb2cli_req_destructor(struct tevent_req *req)
99 {
100         smb2cli_req_unset_pending(req);
101         return 0;
102 }
103
104 static void smb2cli_inbuf_received(struct tevent_req *subreq);
105
106 static bool smb2cli_req_set_pending(struct tevent_req *req)
107 {
108         struct smb2cli_req_state *state =
109                 tevent_req_data(req,
110                 struct smb2cli_req_state);
111         struct cli_state *cli;
112         struct tevent_req **pending;
113         int num_pending;
114         struct tevent_req *subreq;
115
116         cli = state->cli;
117         num_pending = talloc_array_length(cli->conn.pending);
118
119         pending = talloc_realloc(cli, cli->conn.pending, struct tevent_req *,
120                                  num_pending+1);
121         if (pending == NULL) {
122                 return false;
123         }
124         pending[num_pending] = req;
125         cli->conn.pending = pending;
126         talloc_set_destructor(req, smb2cli_req_destructor);
127
128         if (num_pending > 0) {
129                 return true;
130         }
131
132         /*
133          * We're the first ones, add the read_smb request that waits for the
134          * answer from the server
135          */
136         subreq = read_smb_send(cli->conn.pending, state->ev, cli->conn.fd);
137         if (subreq == NULL) {
138                 smb2cli_req_unset_pending(req);
139                 return false;
140         }
141         tevent_req_set_callback(subreq, smb2cli_inbuf_received, cli);
142         return true;
143 }
144
145 static void smb2cli_notify_pending(struct cli_state *cli, NTSTATUS status)
146 {
147         if (cli->conn.fd != -1) {
148                 close(cli->conn.fd);
149         }
150         cli->conn.fd = -1;
151
152         /*
153          * Cancel all pending requests. We don't do a for-loop walking
154          * cli->conn.pending because that array changes in
155          * cli_smb_req_destructor().
156          */
157         while (talloc_array_length(cli->conn.pending) > 0) {
158                 struct tevent_req *req;
159                 struct smb2cli_req_state *state;
160
161                 req = cli->conn.pending[0];
162                 state = tevent_req_data(req, struct smb2cli_req_state);
163
164                 smb2cli_req_unset_pending(req);
165
166                 /*
167                  * we need to defer the callback, because we may notify more
168                  * then one caller.
169                  */
170                 tevent_req_defer_callback(req, state->ev);
171                 tevent_req_nterror(req, status);
172         }
173 }
174
175 struct tevent_req *smb2cli_req_create(TALLOC_CTX *mem_ctx,
176                                       struct tevent_context *ev,
177                                       struct cli_state *cli,
178                                       uint16_t cmd,
179                                       uint32_t additional_flags,
180                                       uint32_t clear_flags,
181                                       unsigned int timeout,
182                                       uint32_t pid,
183                                       uint32_t tid,
184                                       uint64_t uid,
185                                       const uint8_t *fixed,
186                                       uint16_t fixed_len,
187                                       const uint8_t *dyn,
188                                       uint32_t dyn_len)
189 {
190         struct tevent_req *req;
191         struct smb2cli_req_state *state;
192         uint32_t flags = 0;
193
194         req = tevent_req_create(mem_ctx, &state,
195                                 struct smb2cli_req_state);
196         if (req == NULL) {
197                 return NULL;
198         }
199         state->ev = ev;
200         state->cli = cli;
201
202         state->recv_iov = talloc_zero_array(state, struct iovec, 3);
203         if (state->recv_iov == NULL) {
204                 TALLOC_FREE(req);
205                 return NULL;
206         }
207
208         flags |= additional_flags;
209         flags &= ~clear_flags;
210
211         state->fixed = fixed;
212         state->fixed_len = fixed_len;
213         state->dyn = dyn;
214         state->dyn_len = dyn_len;
215
216         SIVAL(state->hdr, SMB2_HDR_PROTOCOL_ID, SMB2_MAGIC);
217         SSVAL(state->hdr, SMB2_HDR_LENGTH,      SMB2_HDR_BODY);
218         SSVAL(state->hdr, SMB2_HDR_EPOCH,       1);
219         SIVAL(state->hdr, SMB2_HDR_STATUS,      NT_STATUS_V(NT_STATUS_OK));
220         SSVAL(state->hdr, SMB2_HDR_OPCODE,      cmd);
221         SSVAL(state->hdr, SMB2_HDR_CREDIT,      31);
222         SIVAL(state->hdr, SMB2_HDR_FLAGS,       flags);
223         SIVAL(state->hdr, SMB2_HDR_PID,         pid);
224         SIVAL(state->hdr, SMB2_HDR_TID,         tid);
225         SBVAL(state->hdr, SMB2_HDR_SESSION_ID,  uid);
226
227         if (timeout > 0) {
228                 struct timeval endtime;
229
230                 endtime = timeval_current_ofs_msec(timeout);
231                 if (!tevent_req_set_endtime(req, ev, endtime)) {
232                         return req;
233                 }
234         }
235
236         return req;
237 }
238
239 static void smb2cli_writev_done(struct tevent_req *subreq);
240
241 NTSTATUS smb2cli_req_compound_submit(struct tevent_req **reqs,
242                                      int num_reqs)
243 {
244         struct smb2cli_req_state *state;
245         struct tevent_req *subreq;
246         struct iovec *iov;
247         int i, num_iov, nbt_len;
248
249         /*
250          * 1 for the nbt length
251          * per request: HDR, fixed, dyn, padding
252          * -1 because the last one does not need padding
253          */
254
255         iov = talloc_array(reqs[0], struct iovec, 1 + 4*num_reqs - 1);
256         if (iov == NULL) {
257                 return NT_STATUS_NO_MEMORY;
258         }
259
260         num_iov = 1;
261         nbt_len = 0;
262
263         for (i=0; i<num_reqs; i++) {
264                 size_t reqlen;
265                 bool ret;
266                 uint64_t mid;
267
268                 if (!tevent_req_is_in_progress(reqs[i])) {
269                         return NT_STATUS_INTERNAL_ERROR;
270                 }
271
272                 state = tevent_req_data(reqs[i], struct smb2cli_req_state);
273
274                 if (!cli_state_is_connected(state->cli)) {
275                         return NT_STATUS_CONNECTION_DISCONNECTED;
276                 }
277
278                 if (state->cli->smb2.mid == UINT64_MAX) {
279                         return NT_STATUS_CONNECTION_ABORTED;
280                 }
281
282                 mid = state->cli->smb2.mid;
283                 state->cli->smb2.mid += 1;
284
285                 SBVAL(state->hdr, SMB2_HDR_MESSAGE_ID, mid);
286
287                 iov[num_iov].iov_base = state->hdr;
288                 iov[num_iov].iov_len  = sizeof(state->hdr);
289                 num_iov += 1;
290
291                 iov[num_iov].iov_base = discard_const(state->fixed);
292                 iov[num_iov].iov_len  = state->fixed_len;
293                 num_iov += 1;
294
295                 if (state->dyn != NULL) {
296                         iov[num_iov].iov_base = discard_const(state->dyn);
297                         iov[num_iov].iov_len  = state->dyn_len;
298                         num_iov += 1;
299                 }
300
301                 reqlen = sizeof(state->hdr) + state->fixed_len +
302                         state->dyn_len;
303
304                 if (i < num_reqs-1) {
305                         if ((reqlen % 8) > 0) {
306                                 uint8_t pad = 8 - (reqlen % 8);
307                                 iov[num_iov].iov_base = state->pad;
308                                 iov[num_iov].iov_len = pad;
309                                 num_iov += 1;
310                                 reqlen += pad;
311                         }
312                         SIVAL(state->hdr, SMB2_HDR_NEXT_COMMAND, reqlen);
313                 }
314                 nbt_len += reqlen;
315
316                 ret = smb2cli_req_set_pending(reqs[i]);
317                 if (!ret) {
318                         return NT_STATUS_NO_MEMORY;
319                 }
320         }
321
322         /*
323          * TODO: Do signing here
324          */
325
326         state = tevent_req_data(reqs[0], struct smb2cli_req_state);
327         _smb_setlen_tcp(state->nbt, nbt_len);
328         iov[0].iov_base = state->nbt;
329         iov[0].iov_len  = sizeof(state->nbt);
330
331         subreq = writev_send(state, state->ev, state->cli->conn.outgoing,
332                              state->cli->conn.fd, false, iov, num_iov);
333         if (subreq == NULL) {
334                 return NT_STATUS_NO_MEMORY;
335         }
336         tevent_req_set_callback(subreq, smb2cli_writev_done, reqs[0]);
337         return NT_STATUS_OK;
338 }
339
340 struct tevent_req *smb2cli_req_send(TALLOC_CTX *mem_ctx,
341                                     struct tevent_context *ev,
342                                     struct cli_state *cli,
343                                     uint16_t cmd,
344                                     uint32_t additional_flags,
345                                     uint32_t clear_flags,
346                                     unsigned int timeout,
347                                     uint32_t pid,
348                                     uint32_t tid,
349                                     uint64_t uid,
350                                     const uint8_t *fixed,
351                                     uint16_t fixed_len,
352                                     const uint8_t *dyn,
353                                     uint32_t dyn_len)
354 {
355         struct tevent_req *req;
356         NTSTATUS status;
357
358         req = smb2cli_req_create(mem_ctx, ev, cli, cmd,
359                                  additional_flags, clear_flags,
360                                  timeout,
361                                  pid, tid, uid,
362                                  fixed, fixed_len, dyn, dyn_len);
363         if (req == NULL) {
364                 return NULL;
365         }
366         if (!tevent_req_is_in_progress(req)) {
367                 return tevent_req_post(req, ev);
368         }
369         status = smb2cli_req_compound_submit(&req, 1);
370         if (tevent_req_nterror(req, status)) {
371                 return tevent_req_post(req, ev);
372         }
373         return req;
374 }
375
376 static void smb2cli_writev_done(struct tevent_req *subreq)
377 {
378         struct tevent_req *req =
379                 tevent_req_callback_data(subreq,
380                 struct tevent_req);
381         struct smb2cli_req_state *state =
382                 tevent_req_data(req,
383                 struct smb2cli_req_state);
384         ssize_t nwritten;
385         int err;
386
387         nwritten = writev_recv(subreq, &err);
388         TALLOC_FREE(subreq);
389         if (nwritten == -1) {
390                 /* here, we need to notify all pending requests */
391                 NTSTATUS status = map_nt_error_from_unix_common(err);
392                 smb2cli_notify_pending(state->cli, status);
393                 return;
394         }
395 }
396
397 static NTSTATUS smb2cli_inbuf_parse_compound(uint8_t *buf, TALLOC_CTX *mem_ctx,
398                                              struct iovec **piov, int *pnum_iov)
399 {
400         struct iovec *iov;
401         int num_iov;
402         size_t buflen;
403         size_t taken;
404
405         num_iov = 1;
406
407         iov = talloc_array(mem_ctx, struct iovec, num_iov);
408         if (iov == NULL) {
409                 return NT_STATUS_NO_MEMORY;
410         }
411         iov[0].iov_base = buf;
412         iov[0].iov_len = 4;
413
414         buflen = smb_len_tcp(buf) + 4;
415         taken = 4;
416
417         while (taken < buflen) {
418                 size_t len = buflen - taken;
419                 uint8_t *hdr = buf + taken;
420                 struct iovec *cur;
421                 size_t full_size;
422                 size_t next_command_ofs;
423                 uint16_t body_size;
424                 struct iovec *iov_tmp;
425
426                 /*
427                  * We need the header plus the body length field
428                  */
429
430                 if (len < SMB2_HDR_BODY + 2) {
431                         DEBUG(10, ("%d bytes left, expected at least %d\n",
432                                    (int)len, SMB2_HDR_BODY));
433                         goto inval;
434                 }
435                 if (IVAL(hdr, 0) != SMB2_MAGIC) {
436                         DEBUG(10, ("Got non-SMB2 PDU: %x\n",
437                                    IVAL(hdr, 0)));
438                         goto inval;
439                 }
440                 if (SVAL(hdr, 4) != SMB2_HDR_BODY) {
441                         DEBUG(10, ("Got HDR len %d, expected %d\n",
442                                    SVAL(hdr, 4), SMB2_HDR_BODY));
443                         goto inval;
444                 }
445
446                 full_size = len;
447                 next_command_ofs = IVAL(hdr, SMB2_HDR_NEXT_COMMAND);
448                 body_size = SVAL(hdr, SMB2_HDR_BODY);
449
450                 if (next_command_ofs != 0) {
451                         if (next_command_ofs < (SMB2_HDR_BODY + 2)) {
452                                 goto inval;
453                         }
454                         if (next_command_ofs > full_size) {
455                                 goto inval;
456                         }
457                         full_size = next_command_ofs;
458                 }
459                 if (body_size < 2) {
460                         goto inval;
461                 }
462                 body_size &= 0xfffe;
463
464                 if (body_size > (full_size - SMB2_HDR_BODY)) {
465                         goto inval;
466                 }
467
468                 iov_tmp = talloc_realloc(mem_ctx, iov, struct iovec,
469                                          num_iov + 3);
470                 if (iov_tmp == NULL) {
471                         TALLOC_FREE(iov);
472                         return NT_STATUS_NO_MEMORY;
473                 }
474                 iov = iov_tmp;
475                 cur = &iov[num_iov];
476                 num_iov += 3;
477
478                 cur[0].iov_base = hdr;
479                 cur[0].iov_len  = SMB2_HDR_BODY;
480                 cur[1].iov_base = hdr + SMB2_HDR_BODY;
481                 cur[1].iov_len  = body_size;
482                 cur[2].iov_base = hdr + SMB2_HDR_BODY + body_size;
483                 cur[2].iov_len  = full_size - (SMB2_HDR_BODY + body_size);
484
485                 taken += full_size;
486         }
487
488         *piov = iov;
489         *pnum_iov = num_iov;
490         return NT_STATUS_OK;
491
492 inval:
493         TALLOC_FREE(iov);
494         return NT_STATUS_INVALID_NETWORK_RESPONSE;
495 }
496
497 static struct tevent_req *cli_smb2_find_pending(struct cli_state *cli,
498                                                 uint64_t mid)
499 {
500         int num_pending = talloc_array_length(cli->conn.pending);
501         int i;
502
503         for (i=0; i<num_pending; i++) {
504                 struct tevent_req *req = cli->conn.pending[i];
505                 struct smb2cli_req_state *state =
506                         tevent_req_data(req,
507                         struct smb2cli_req_state);
508
509                 if (mid == BVAL(state->hdr, SMB2_HDR_MESSAGE_ID)) {
510                         return req;
511                 }
512         }
513         return NULL;
514 }
515
516 static void smb2cli_inbuf_received(struct tevent_req *subreq)
517 {
518         struct cli_state *cli =
519                 tevent_req_callback_data(subreq,
520                 struct cli_state);
521         TALLOC_CTX *frame = talloc_stackframe();
522         struct tevent_req *req;
523         struct smb2cli_req_state *state = NULL;
524         struct iovec *iov;
525         int i, num_iov;
526         NTSTATUS status;
527         uint8_t *inbuf;
528         ssize_t received;
529         int err;
530         size_t num_pending;
531         bool defer = true;
532
533         received = read_smb_recv(subreq, frame, &inbuf, &err);
534         TALLOC_FREE(subreq);
535         if (received == -1) {
536                 /*
537                  * We need to close the connection and notify
538                  * all pending requests.
539                  */
540                 status = map_nt_error_from_unix_common(err);
541                 smb2cli_notify_pending(cli, status);
542                 TALLOC_FREE(frame);
543                 return;
544         }
545
546         status = smb2cli_inbuf_parse_compound(inbuf, frame,
547                                               &iov, &num_iov);
548         if (!NT_STATUS_IS_OK(status)) {
549                 /*
550                  * if we cannot parse the incoming pdu,
551                  * the connection becomes unusable.
552                  *
553                  * We need to close the connection and notify
554                  * all pending requests.
555                  */
556                 smb2cli_notify_pending(cli, status);
557                 TALLOC_FREE(frame);
558                 return;
559         }
560
561         for (i=1; i<num_iov; i+=3) {
562                 uint8_t *inbuf_ref = NULL;
563                 struct iovec *cur = &iov[i];
564                 uint8_t *inhdr = (uint8_t *)cur[0].iov_base;
565                 uint16_t opcode = SVAL(inhdr, SMB2_HDR_OPCODE);
566                 uint32_t flags = IVAL(inhdr, SMB2_HDR_FLAGS);
567                 uint64_t mid = BVAL(inhdr, SMB2_HDR_MESSAGE_ID);
568                 uint16_t req_opcode;
569
570                 req = cli_smb2_find_pending(cli, mid);
571                 if (req == NULL) {
572                         /*
573                          * TODO: handle oplock breaks and async responses
574                          */
575
576                         /*
577                          * We need to close the connection and notify
578                          * all pending requests.
579                          */
580                         status = NT_STATUS_INVALID_NETWORK_RESPONSE;
581                         smb2cli_notify_pending(cli, status);
582                         TALLOC_FREE(frame);
583                         return;
584                 }
585                 state = tevent_req_data(req, struct smb2cli_req_state);
586
587                 req_opcode = SVAL(state->hdr, SMB2_HDR_OPCODE);
588                 if (opcode != req_opcode) {
589                         status = NT_STATUS_INVALID_NETWORK_RESPONSE;
590                         smb2cli_notify_pending(cli, status);
591                         TALLOC_FREE(frame);
592                         return;
593                 }
594
595                 if (!(flags & SMB2_HDR_FLAG_REDIRECT)) {
596                         status = NT_STATUS_INVALID_NETWORK_RESPONSE;
597                         smb2cli_notify_pending(cli, status);
598                         TALLOC_FREE(frame);
599                         return;
600                 }
601
602                 status = NT_STATUS(IVAL(inhdr, SMB2_HDR_STATUS));
603                 if ((flags & SMB2_HDR_FLAG_ASYNC) &&
604                     NT_STATUS_EQUAL(status, STATUS_PENDING)) {
605                         uint32_t req_flags = IVAL(state->hdr, SMB2_HDR_FLAGS);
606                         uint64_t async_id = BVAL(inhdr, SMB2_HDR_ASYNC_ID);
607
608                         req_flags |= SMB2_HDR_FLAG_ASYNC;
609                         SBVAL(state->hdr, SMB2_HDR_FLAGS, req_flags);
610                         SBVAL(state->hdr, SMB2_HDR_ASYNC_ID, async_id);
611                         continue;
612                 }
613
614                 smb2cli_req_unset_pending(req);
615
616                 /*
617                  * There might be more than one response
618                  * we need to defer the notifications
619                  */
620                 if ((num_iov == 4) && (talloc_array_length(cli->conn.pending) == 0)) {
621                         defer = false;
622                 }
623
624                 if (defer) {
625                         tevent_req_defer_callback(req, state->ev);
626                 }
627
628                 /*
629                  * Note: here we use talloc_reference() in a way
630                  *       that does not expose it to the caller.
631                  */
632                 inbuf_ref = talloc_reference(state->recv_iov, inbuf);
633                 if (tevent_req_nomem(inbuf_ref, req)) {
634                         continue;
635                 }
636
637                 /* copy the related buffers */
638                 state->recv_iov[0] = cur[0];
639                 state->recv_iov[1] = cur[1];
640                 state->recv_iov[2] = cur[2];
641
642                 tevent_req_done(req);
643         }
644
645         TALLOC_FREE(frame);
646
647         if (!defer) {
648                 return;
649         }
650
651         num_pending = talloc_array_length(cli->conn.pending);
652         if (num_pending == 0) {
653                 if (state->cli->smb2.mid < UINT64_MAX) {
654                         /* no more pending requests, so we are done for now */
655                         return;
656                 }
657
658                 /*
659                  * If there are no more requests possible,
660                  * because we are out of message ids,
661                  * we need to disconnect.
662                  */
663                 smb2cli_notify_pending(cli, NT_STATUS_CONNECTION_ABORTED);
664                 return;
665         }
666         req = cli->conn.pending[0];
667         state = tevent_req_data(req, struct smb2cli_req_state);
668
669         /*
670          * add the read_smb request that waits for the
671          * next answer from the server
672          */
673         subreq = read_smb_send(cli->conn.pending, state->ev, cli->conn.fd);
674         if (subreq == NULL) {
675                 smb2cli_notify_pending(cli, NT_STATUS_NO_MEMORY);
676                 return;
677         }
678         tevent_req_set_callback(subreq, smb2cli_inbuf_received, cli);
679 }
680
681 NTSTATUS smb2cli_req_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
682                           struct iovec **piov,
683                           const struct smb2cli_req_expected_response *expected,
684                           size_t num_expected)
685 {
686         struct smb2cli_req_state *state =
687                 tevent_req_data(req,
688                 struct smb2cli_req_state);
689         NTSTATUS status;
690         size_t body_size;
691         bool found_status = false;
692         bool found_size = false;
693         size_t i;
694
695         if (piov != NULL) {
696                 *piov = NULL;
697         }
698
699         if (tevent_req_is_nterror(req, &status)) {
700                 for (i=0; i < num_expected; i++) {
701                         if (NT_STATUS_EQUAL(status, expected[i].status)) {
702                                 found_status = true;
703                                 break;
704                         }
705                 }
706
707                 if (found_status) {
708                         return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
709                 }
710
711                 return status;
712         }
713
714         if (num_expected == 0) {
715                 found_status = true;
716                 found_size = true;
717         }
718
719         status = NT_STATUS(IVAL(state->recv_iov[0].iov_base, SMB2_HDR_STATUS));
720         body_size = SVAL(state->recv_iov[1].iov_base, 0);
721
722         for (i=0; i < num_expected; i++) {
723                 if (!NT_STATUS_EQUAL(status, expected[i].status)) {
724                         continue;
725                 }
726
727                 found_status = true;
728                 if (expected[i].body_size == 0) {
729                         found_size = true;
730                         break;
731                 }
732
733                 if (expected[i].body_size == body_size) {
734                         found_size = true;
735                         break;
736                 }
737         }
738
739         if (!found_status) {
740                 return status;
741         }
742
743         if (!found_size) {
744                 return NT_STATUS_INVALID_NETWORK_RESPONSE;
745         }
746
747         if (piov != NULL) {
748                 *piov = talloc_move(mem_ctx, &state->recv_iov);
749         }
750
751         return status;
752 }