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 "system/filesys.h"
23 #include "system/network.h"
24 #include "../lib/tsocket/tsocket.h"
25 #include "../libcli/util/tstream.h"
26 #include "../lib/util/tevent_ntstatus.h"
27 
28 struct tstream_read_pdu_blob_state {
29 	/* this structs are owned by the caller */
30 	struct {
31 		struct tevent_context *ev;
32 		struct tstream_context *stream;
33 		tstream_read_pdu_blob_full_fn_t *full_fn;
34 		void *full_private;
35 	} caller;
36 
37 	DATA_BLOB pdu_blob;
38 	struct iovec tmp_vector;
39 };
40 
41 static void tstream_read_pdu_blob_done(struct tevent_req *subreq);
42 
tstream_read_pdu_blob_send(TALLOC_CTX * mem_ctx,struct tevent_context * ev,struct tstream_context * stream,size_t initial_read_size,tstream_read_pdu_blob_full_fn_t * full_fn,void * full_private)43 struct tevent_req *tstream_read_pdu_blob_send(TALLOC_CTX *mem_ctx,
44 				struct tevent_context *ev,
45 				struct tstream_context *stream,
46 				size_t initial_read_size,
47 				tstream_read_pdu_blob_full_fn_t *full_fn,
48 				void *full_private)
49 {
50 	struct tevent_req *req;
51 	struct tstream_read_pdu_blob_state *state;
52 	struct tevent_req *subreq;
53 	uint8_t *buf;
54 
55 	req = tevent_req_create(mem_ctx, &state,
56 				struct tstream_read_pdu_blob_state);
57 	if (!req) {
58 		return NULL;
59 	}
60 
61 	state->caller.ev		= ev;
62 	state->caller.stream		= stream;
63 	state->caller.full_fn		= full_fn;
64 	state->caller.full_private	= full_private;
65 
66 	if (initial_read_size == 0) {
67 		tevent_req_error(req, EINVAL);
68 		return tevent_req_post(req, ev);
69 	}
70 
71 	buf = talloc_array(state, uint8_t, initial_read_size);
72 	if (tevent_req_nomem(buf, req)) {
73 		return tevent_req_post(req, ev);
74 	}
75 	state->pdu_blob.data = buf;
76 	state->pdu_blob.length = initial_read_size;
77 
78 	state->tmp_vector.iov_base = (char *) buf;
79 	state->tmp_vector.iov_len = initial_read_size;
80 
81 	subreq = tstream_readv_send(state, ev, stream, &state->tmp_vector, 1);
82 	if (tevent_req_nomem(subreq, req)) {
83 		return tevent_req_post(req, ev);
84 	}
85 	tevent_req_set_callback(subreq, tstream_read_pdu_blob_done, req);
86 
87 	return req;
88 }
89 
tstream_read_pdu_blob_done(struct tevent_req * subreq)90 static void tstream_read_pdu_blob_done(struct tevent_req *subreq)
91 {
92 	struct tevent_req *req =
93 		tevent_req_callback_data(subreq,
94 		struct tevent_req);
95 	struct tstream_read_pdu_blob_state *state =
96 		tevent_req_data(req,
97 		struct tstream_read_pdu_blob_state);
98 	ssize_t ret;
99 	int sys_errno;
100 	size_t old_buf_size = state->pdu_blob.length;
101 	size_t new_buf_size = 0;
102 	size_t pdu_size = 0;
103 	NTSTATUS status;
104 	uint8_t *buf;
105 
106 	ret = tstream_readv_recv(subreq, &sys_errno);
107 	TALLOC_FREE(subreq);
108 	if (ret == -1) {
109 		status = map_nt_error_from_unix_common(sys_errno);
110 		tevent_req_nterror(req, status);
111 		return;
112 	}
113 
114 	status = state->caller.full_fn(state->caller.full_private,
115 				       state->pdu_blob, &pdu_size);
116 	if (NT_STATUS_IS_OK(status)) {
117 		tevent_req_done(req);
118 		return;
119 	} else if (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
120 		/* more to get */
121 		if (pdu_size > 0) {
122 			new_buf_size = pdu_size;
123 		} else {
124 			/* we don't know the size yet, so get one more byte */
125 			new_buf_size = old_buf_size + 1;
126 		}
127 	} else if (!NT_STATUS_IS_OK(status)) {
128 		tevent_req_nterror(req, status);
129 		return;
130 	}
131 
132 	if (new_buf_size <= old_buf_size) {
133 		tevent_req_nterror(req, NT_STATUS_INVALID_BUFFER_SIZE);
134 		return;
135 	}
136 
137 	buf = talloc_realloc(state, state->pdu_blob.data, uint8_t, new_buf_size);
138 	if (tevent_req_nomem(buf, req)) {
139 		return;
140 	}
141 	state->pdu_blob.data = buf;
142 	state->pdu_blob.length = new_buf_size;
143 
144 	state->tmp_vector.iov_base = (char *) (buf + old_buf_size);
145 	state->tmp_vector.iov_len = new_buf_size - old_buf_size;
146 
147 	subreq = tstream_readv_send(state,
148 				    state->caller.ev,
149 				    state->caller.stream,
150 				    &state->tmp_vector,
151 				    1);
152 	if (tevent_req_nomem(subreq, req)) {
153 		return;
154 	}
155 	tevent_req_set_callback(subreq, tstream_read_pdu_blob_done, req);
156 }
157 
tstream_read_pdu_blob_recv(struct tevent_req * req,TALLOC_CTX * mem_ctx,DATA_BLOB * pdu_blob)158 NTSTATUS tstream_read_pdu_blob_recv(struct tevent_req *req,
159 				    TALLOC_CTX *mem_ctx,
160 				    DATA_BLOB *pdu_blob)
161 {
162 	struct tstream_read_pdu_blob_state *state = tevent_req_data(req,
163 					struct tstream_read_pdu_blob_state);
164 	NTSTATUS status;
165 
166 	if (tevent_req_is_nterror(req, &status)) {
167 		tevent_req_received(req);
168 		return status;
169 	}
170 
171 	*pdu_blob = state->pdu_blob;
172 	talloc_steal(mem_ctx, pdu_blob->data);
173 
174 	tevent_req_received(req);
175 	return NT_STATUS_OK;
176 }
177 
178