libcli/util: add tstream_read_pdu_blob_send/recv
[metze/samba/wip.git] / libcli / util / tstream.c
1 /*
2  *  Unix SMB/CIFS implementation.
3  *
4  *  Copyright (C) Stefan Metzmacher 2009
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 3 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include "includes.h"
21 #include <tevent.h>
22 #include <tsocket.h>
23 #include <tevent_ntstatus.h>
24
25 struct tstream_read_pdu_blob_state {
26         /* this structs are owned by the caller */
27         struct {
28                 struct tevent_context *ev;
29                 struct tstream_context *stream;
30                 NTSTATUS (*full_fn_t)(void *private_data,
31                                       DATA_BLOB blob,
32                                       size_t *packet_size);
33                 void *full_private;
34         } caller;
35
36         DATA_BLOB pdu_blob;
37         struct iovec tmp_vector;
38 };
39
40 static void tstream_read_pdu_blob_done(struct tevent_req *subreq);
41
42 struct tevent_req *tstream_read_pdu_blob_send(TALLOC_CTX *mem_ctx,
43                                 struct tevent_context *ev,
44                                 struct tstream_context *stream,
45                                 size_t inital_read_size,
46                                 NTSTATUS (*full_fn_t)(void *private_data,
47                                                       DATA_BLOB blob,
48                                                       size_t *packet_size);
49                                 void *full_fn_private)
50 {
51         struct tevent_req *req;
52         struct tstream_read_pdu_blob_state *state;
53         struct tevent_req *subreq;
54         uint8_t *buf;
55
56         req = tevent_req_create(mem_ctx, &state,
57                                 struct tstream_read_pdu_blob_state);
58         if (!req) {
59                 return NULL;
60         }
61
62         state->caller.ev                = ev;
63         state->caller.stream            = stream;
64         state->caller.full_fn           = full_fn;
65         state->caller.full_private      = full_private;
66
67         if (initial_read_size == 0) {
68                 tevent_req_error(req, EINVAL);
69                 return tevent_req_post(req, ev);
70         }
71
72         buf = talloc_array(state, uint8_t, initial_read_size);
73         if (tevent_req_nomem(buf, req)) {
74                 return tevent_req_post(req, ev);
75         }
76         state->pdu_blob.data = buf;
77         state->pdu_blob.length = initial_read_size;
78
79         state->tmp_vector.iov_base = buf;
80         state->tmp_vector.iov_len = initial_read_size;
81
82         subreq = tstream_readv_send(state, ev, stream, &state->tmp_vector, 1);
83         if (tevent_req_nomem(subreq, req)) {
84                 return tevent_req_post(req, ev);
85         }
86         tevent_req_set_callback(subreq, tstream_read_pdu_blob_done, req);
87
88         return req;
89 }
90
91 static void tstream_read_pdu_blob_done(struct tevent_req *subreq)
92 {
93         struct tevent_req *req = tevent_req_callback_data(subreq,
94                                  struct tevent_req);
95         struct tdgram_read_pdu_blob_state *state = tevent_req_data(req,
96                                         struct tdgram_read_pdu_blob_state);
97         ssize_t ret;
98         int sys_errno;
99         size_t pdu_size;
100
101         ret = tstream_readv_recv(subreq, &sys_errno);
102         TALLOC_FREE(subreq);
103         if (ret == -1) {
104                 status = map_nt_error_from_unix(sys_errno);
105                 tevent_req_ntstatus(req, status);
106                 return;
107         }
108
109         status = state->caller.full_fn(state->caller.full_private,
110                                        pdu_blob, &pdu_size);
111         if (NT_STATUS_IS_OK(status)) {
112                 tevent_req_done(req);
113                 return;
114         } else if (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
115                 /* more to get */
116         } else if (!NT_STATUS_IS_OK(status)) {
117                 tevent_req_ntstatus(req, status);
118                 return;
119         }
120
121         buf = talloc_realloc(state, state->pdu_blob.data, uint8_t, pdu_size);
122         if (tevent_req_nomem(buf, req)) {
123                 return;
124         }
125         state->pdu_blob.data = buf;
126         state->pdu_blob.length = pdu_size;
127
128         state->tmp_vector.iov_base = buf + state->tmp_vector.iov_len;
129         state->tmp_vector.iov_len = pdu_size - state->tmp_vector.iov_len;
130
131         subreq = tstream_readv_send(state,
132                                     state->caller.ev,
133                                     state->caller.stream,
134                                     &state->tmp_vector,
135                                     1);
136         if (tevent_req_nomem(subreq, req)) {
137                 return;
138         }
139         tevent_req_set_callback(subreq, tstream_read_pdu_blob_done, req);
140 }
141
142 NTSTATUS tstream_read_pdu_blob_recv(struct tevent_req *req,
143                                     TALLOC_CTX *mem_ctx,
144                                     DATA_BLOB *pdu_blob)
145 {
146         struct tstream_read_pdu_blob_state *state = tevent_req_data(req,
147                                         struct tstream_read_pdu_blob_state);
148         int ret;
149
150         if (tevent_req_is_nterror(req, &status)) {
151                 tevent_req_received(req);
152                 return status;
153         }
154
155         *pdu_blob = state->pdu_blob;
156         talloc_steal(mem_ctx, pdu_blob->data);
157
158         tevent_req_received(req);
159         return NT_STATUS_OK;
160 }
161