libcli/smb: move source3/libsmb/read_smb.* to the toplevel
[kai/samba.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_large(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                 smb2cli_notify_pending(state->cli, map_nt_error_from_unix(err));
392                 return;
393         }
394 }
395
396 static NTSTATUS smb2cli_inbuf_parse_compound(uint8_t *buf, TALLOC_CTX *mem_ctx,
397                                              struct iovec **piov, int *pnum_iov)
398 {
399         struct iovec *iov;
400         int num_iov;
401         size_t buflen;
402         size_t taken;
403
404         num_iov = 1;
405
406         iov = talloc_array(mem_ctx, struct iovec, num_iov);
407         if (iov == NULL) {
408                 return NT_STATUS_NO_MEMORY;
409         }
410         iov[0].iov_base = buf;
411         iov[0].iov_len = 4;
412
413         buflen = smb_len_large(buf) + 4;
414         taken = 4;
415
416         while (taken < buflen) {
417                 size_t len = buflen - taken;
418                 uint8_t *hdr = buf + taken;
419                 struct iovec *cur;
420                 size_t full_size;
421                 size_t next_command_ofs;
422                 uint16_t body_size;
423                 struct iovec *iov_tmp;
424
425                 /*
426                  * We need the header plus the body length field
427                  */
428
429                 if (len < SMB2_HDR_BODY + 2) {
430                         DEBUG(10, ("%d bytes left, expected at least %d\n",
431                                    (int)len, SMB2_HDR_BODY));
432                         goto inval;
433                 }
434                 if (IVAL(hdr, 0) != SMB2_MAGIC) {
435                         DEBUG(10, ("Got non-SMB2 PDU: %x\n",
436                                    IVAL(hdr, 0)));
437                         goto inval;
438                 }
439                 if (SVAL(hdr, 4) != SMB2_HDR_BODY) {
440                         DEBUG(10, ("Got HDR len %d, expected %d\n",
441                                    SVAL(hdr, 4), SMB2_HDR_BODY));
442                         goto inval;
443                 }
444
445                 full_size = len;
446                 next_command_ofs = IVAL(hdr, SMB2_HDR_NEXT_COMMAND);
447                 body_size = SVAL(hdr, SMB2_HDR_BODY);
448
449                 if (next_command_ofs != 0) {
450                         if (next_command_ofs < (SMB2_HDR_BODY + 2)) {
451                                 goto inval;
452                         }
453                         if (next_command_ofs > full_size) {
454                                 goto inval;
455                         }
456                         full_size = next_command_ofs;
457                 }
458                 if (body_size < 2) {
459                         goto inval;
460                 }
461                 body_size &= 0xfffe;
462
463                 if (body_size > (full_size - SMB2_HDR_BODY)) {
464                         goto inval;
465                 }
466
467                 iov_tmp = talloc_realloc(mem_ctx, iov, struct iovec,
468                                          num_iov + 3);
469                 if (iov_tmp == NULL) {
470                         TALLOC_FREE(iov);
471                         return NT_STATUS_NO_MEMORY;
472                 }
473                 iov = iov_tmp;
474                 cur = &iov[num_iov];
475                 num_iov += 3;
476
477                 cur[0].iov_base = hdr;
478                 cur[0].iov_len  = SMB2_HDR_BODY;
479                 cur[1].iov_base = hdr + SMB2_HDR_BODY;
480                 cur[1].iov_len  = body_size;
481                 cur[2].iov_base = hdr + SMB2_HDR_BODY + body_size;
482                 cur[2].iov_len  = full_size - (SMB2_HDR_BODY + body_size);
483
484                 taken += full_size;
485         }
486
487         *piov = iov;
488         *pnum_iov = num_iov;
489         return NT_STATUS_OK;
490
491 inval:
492         TALLOC_FREE(iov);
493         return NT_STATUS_INVALID_NETWORK_RESPONSE;
494 }
495
496 static struct tevent_req *cli_smb2_find_pending(struct cli_state *cli,
497                                                 uint64_t mid)
498 {
499         int num_pending = talloc_array_length(cli->conn.pending);
500         int i;
501
502         for (i=0; i<num_pending; i++) {
503                 struct tevent_req *req = cli->conn.pending[i];
504                 struct smb2cli_req_state *state =
505                         tevent_req_data(req,
506                         struct smb2cli_req_state);
507
508                 if (mid == BVAL(state->hdr, SMB2_HDR_MESSAGE_ID)) {
509                         return req;
510                 }
511         }
512         return NULL;
513 }
514
515 static void smb2cli_inbuf_received(struct tevent_req *subreq)
516 {
517         struct cli_state *cli =
518                 tevent_req_callback_data(subreq,
519                 struct cli_state);
520         TALLOC_CTX *frame = talloc_stackframe();
521         struct tevent_req *req;
522         struct smb2cli_req_state *state = NULL;
523         struct iovec *iov;
524         int i, num_iov;
525         NTSTATUS status;
526         uint8_t *inbuf;
527         ssize_t received;
528         int err;
529         size_t num_pending;
530         bool defer = true;
531
532         received = read_smb_recv(subreq, frame, &inbuf, &err);
533         TALLOC_FREE(subreq);
534         if (received == -1) {
535                 /*
536                  * We need to close the connection and notify
537                  * all pending requests.
538                  */
539                 smb2cli_notify_pending(cli, map_nt_error_from_unix(err));
540                 TALLOC_FREE(frame);
541                 return;
542         }
543
544         status = smb2cli_inbuf_parse_compound(inbuf, frame,
545                                               &iov, &num_iov);
546         if (!NT_STATUS_IS_OK(status)) {
547                 /*
548                  * if we cannot parse the incoming pdu,
549                  * the connection becomes unusable.
550                  *
551                  * We need to close the connection and notify
552                  * all pending requests.
553                  */
554                 smb2cli_notify_pending(cli, status);
555                 TALLOC_FREE(frame);
556                 return;
557         }
558
559         for (i=1; i<num_iov; i+=3) {
560                 uint8_t *inbuf_ref = NULL;
561                 struct iovec *cur = &iov[i];
562                 uint8_t *inhdr = (uint8_t *)cur[0].iov_base;
563                 uint16_t opcode = SVAL(inhdr, SMB2_HDR_OPCODE);
564                 uint32_t flags = IVAL(inhdr, SMB2_HDR_FLAGS);
565                 uint64_t mid = BVAL(inhdr, SMB2_HDR_MESSAGE_ID);
566                 uint16_t req_opcode;
567
568                 req = cli_smb2_find_pending(cli, mid);
569                 if (req == NULL) {
570                         /*
571                          * TODO: handle oplock breaks and async responses
572                          */
573
574                         /*
575                          * We need to close the connection and notify
576                          * all pending requests.
577                          */
578                         status = NT_STATUS_INVALID_NETWORK_RESPONSE;
579                         smb2cli_notify_pending(cli, status);
580                         TALLOC_FREE(frame);
581                         return;
582                 }
583                 state = tevent_req_data(req, struct smb2cli_req_state);
584
585                 req_opcode = SVAL(state->hdr, SMB2_HDR_OPCODE);
586                 if (opcode != req_opcode) {
587                         status = NT_STATUS_INVALID_NETWORK_RESPONSE;
588                         smb2cli_notify_pending(cli, status);
589                         TALLOC_FREE(frame);
590                         return;
591                 }
592
593                 if (!(flags & SMB2_HDR_FLAG_REDIRECT)) {
594                         status = NT_STATUS_INVALID_NETWORK_RESPONSE;
595                         smb2cli_notify_pending(cli, status);
596                         TALLOC_FREE(frame);
597                         return;
598                 }
599
600                 status = NT_STATUS(IVAL(inhdr, SMB2_HDR_STATUS));
601                 if ((flags & SMB2_HDR_FLAG_ASYNC) &&
602                     NT_STATUS_EQUAL(status, STATUS_PENDING)) {
603                         uint32_t req_flags = IVAL(state->hdr, SMB2_HDR_FLAGS);
604                         uint64_t async_id = BVAL(inhdr, SMB2_HDR_ASYNC_ID);
605
606                         req_flags |= SMB2_HDR_FLAG_ASYNC;
607                         SBVAL(state->hdr, SMB2_HDR_FLAGS, req_flags);
608                         SBVAL(state->hdr, SMB2_HDR_ASYNC_ID, async_id);
609                         continue;
610                 }
611
612                 smb2cli_req_unset_pending(req);
613
614                 /*
615                  * There might be more than one response
616                  * we need to defer the notifications
617                  */
618                 if ((num_iov == 4) && (talloc_array_length(cli->conn.pending) == 0)) {
619                         defer = false;
620                 }
621
622                 if (defer) {
623                         tevent_req_defer_callback(req, state->ev);
624                 }
625
626                 /*
627                  * Note: here we use talloc_reference() in a way
628                  *       that does not expose it to the caller.
629                  */
630                 inbuf_ref = talloc_reference(state->recv_iov, inbuf);
631                 if (tevent_req_nomem(inbuf_ref, req)) {
632                         continue;
633                 }
634
635                 /* copy the related buffers */
636                 state->recv_iov[0] = cur[0];
637                 state->recv_iov[1] = cur[1];
638                 state->recv_iov[2] = cur[2];
639
640                 tevent_req_done(req);
641         }
642
643         TALLOC_FREE(frame);
644
645         if (!defer) {
646                 return;
647         }
648
649         num_pending = talloc_array_length(cli->conn.pending);
650         if (num_pending == 0) {
651                 if (state->cli->smb2.mid < UINT64_MAX) {
652                         /* no more pending requests, so we are done for now */
653                         return;
654                 }
655
656                 /*
657                  * If there are no more requests possible,
658                  * because we are out of message ids,
659                  * we need to disconnect.
660                  */
661                 smb2cli_notify_pending(cli, NT_STATUS_CONNECTION_ABORTED);
662                 return;
663         }
664         req = cli->conn.pending[0];
665         state = tevent_req_data(req, struct smb2cli_req_state);
666
667         /*
668          * add the read_smb request that waits for the
669          * next answer from the server
670          */
671         subreq = read_smb_send(cli->conn.pending, state->ev, cli->conn.fd);
672         if (subreq == NULL) {
673                 smb2cli_notify_pending(cli, NT_STATUS_NO_MEMORY);
674                 return;
675         }
676         tevent_req_set_callback(subreq, smb2cli_inbuf_received, cli);
677 }
678
679 NTSTATUS smb2cli_req_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
680                           struct iovec **piov,
681                           const struct smb2cli_req_expected_response *expected,
682                           size_t num_expected)
683 {
684         struct smb2cli_req_state *state =
685                 tevent_req_data(req,
686                 struct smb2cli_req_state);
687         NTSTATUS status;
688         size_t body_size;
689         bool found_status = false;
690         bool found_size = false;
691         size_t i;
692
693         if (piov != NULL) {
694                 *piov = NULL;
695         }
696
697         if (tevent_req_is_nterror(req, &status)) {
698                 for (i=0; i < num_expected; i++) {
699                         if (NT_STATUS_EQUAL(status, expected[i].status)) {
700                                 found_status = true;
701                                 break;
702                         }
703                 }
704
705                 if (found_status) {
706                         return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
707                 }
708
709                 return status;
710         }
711
712         if (num_expected == 0) {
713                 found_status = true;
714                 found_size = true;
715         }
716
717         status = NT_STATUS(IVAL(state->recv_iov[0].iov_base, SMB2_HDR_STATUS));
718         body_size = SVAL(state->recv_iov[1].iov_base, 0);
719
720         for (i=0; i < num_expected; i++) {
721                 if (!NT_STATUS_EQUAL(status, expected[i].status)) {
722                         continue;
723                 }
724
725                 found_status = true;
726                 if (expected[i].body_size == 0) {
727                         found_size = true;
728                         break;
729                 }
730
731                 if (expected[i].body_size == body_size) {
732                         found_size = true;
733                         break;
734                 }
735         }
736
737         if (!found_status) {
738                 return status;
739         }
740
741         if (!found_size) {
742                 return NT_STATUS_INVALID_NETWORK_RESPONSE;
743         }
744
745         if (piov != NULL) {
746                 *piov = talloc_move(mem_ctx, &state->recv_iov);
747         }
748
749         return status;
750 }