/src/samba/source4/lib/stream/packet.c
Line | Count | Source |
1 | | /* |
2 | | Unix SMB/CIFS Implementation. |
3 | | |
4 | | helper layer for breaking up streams into discrete requests |
5 | | |
6 | | Copyright (C) Andrew Tridgell 2005 |
7 | | |
8 | | This program is free software; you can redistribute it and/or modify |
9 | | it under the terms of the GNU General Public License as published by |
10 | | the Free Software Foundation; either version 3 of the License, or |
11 | | (at your option) any later version. |
12 | | |
13 | | This program is distributed in the hope that it will be useful, |
14 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | | GNU General Public License for more details. |
17 | | |
18 | | You should have received a copy of the GNU General Public License |
19 | | along with this program. If not, see <http://www.gnu.org/licenses/>. |
20 | | |
21 | | */ |
22 | | |
23 | | #include "includes.h" |
24 | | #include "../lib/util/dlinklist.h" |
25 | | #include "lib/events/events.h" |
26 | | #include "lib/socket/socket.h" |
27 | | #include "lib/stream/packet.h" |
28 | | #include "libcli/raw/smb.h" |
29 | | |
30 | | struct packet_context { |
31 | | packet_callback_fn_t callback; |
32 | | packet_full_request_fn_t full_request; |
33 | | packet_error_handler_fn_t error_handler; |
34 | | DATA_BLOB partial; |
35 | | uint32_t num_read; |
36 | | uint32_t initial_read; |
37 | | struct socket_context *sock; |
38 | | struct tevent_context *ev; |
39 | | size_t packet_size; |
40 | | void *private_data; |
41 | | struct tevent_fd *fde; |
42 | | bool serialise; |
43 | | int processing; |
44 | | bool recv_disable; |
45 | | bool recv_need_enable; |
46 | | bool nofree; |
47 | | |
48 | | bool busy; |
49 | | bool destructor_called; |
50 | | |
51 | | bool unreliable_select; |
52 | | |
53 | | struct send_element { |
54 | | struct send_element *next, *prev; |
55 | | DATA_BLOB blob; |
56 | | size_t nsent; |
57 | | packet_send_callback_fn_t send_callback; |
58 | | void *send_callback_private; |
59 | | } *send_queue; |
60 | | }; |
61 | | |
62 | | /* |
63 | | a destructor used when we are processing packets to prevent freeing of this |
64 | | context while it is being used |
65 | | */ |
66 | | static int packet_destructor(struct packet_context *pc) |
67 | 0 | { |
68 | 0 | if (pc->busy) { |
69 | 0 | pc->destructor_called = true; |
70 | | /* now we refuse the talloc_free() request. The free will |
71 | | happen again in the packet_recv() code */ |
72 | 0 | return -1; |
73 | 0 | } |
74 | | |
75 | 0 | return 0; |
76 | 0 | } |
77 | | |
78 | | |
79 | | /* |
80 | | initialise a packet receiver |
81 | | */ |
82 | | _PUBLIC_ struct packet_context *packet_init(TALLOC_CTX *mem_ctx) |
83 | 0 | { |
84 | 0 | struct packet_context *pc = talloc_zero(mem_ctx, struct packet_context); |
85 | 0 | if (pc != NULL) { |
86 | 0 | talloc_set_destructor(pc, packet_destructor); |
87 | 0 | } |
88 | 0 | return pc; |
89 | 0 | } |
90 | | |
91 | | |
92 | | /* |
93 | | set the request callback, called when a full request is ready |
94 | | */ |
95 | | _PUBLIC_ void packet_set_callback(struct packet_context *pc, packet_callback_fn_t callback) |
96 | 0 | { |
97 | 0 | pc->callback = callback; |
98 | 0 | } |
99 | | |
100 | | /* |
101 | | set the error handler |
102 | | */ |
103 | | _PUBLIC_ void packet_set_error_handler(struct packet_context *pc, packet_error_handler_fn_t handler) |
104 | 0 | { |
105 | 0 | pc->error_handler = handler; |
106 | 0 | } |
107 | | |
108 | | /* |
109 | | set the private pointer passed to the callback functions |
110 | | */ |
111 | | _PUBLIC_ void packet_set_private(struct packet_context *pc, void *private_data) |
112 | 0 | { |
113 | 0 | pc->private_data = private_data; |
114 | 0 | } |
115 | | |
116 | | /* |
117 | | set the full request callback. Should return as follows: |
118 | | NT_STATUS_OK == blob is a full request. |
119 | | STATUS_MORE_ENTRIES == blob is not complete yet |
120 | | any error == blob is not a valid |
121 | | */ |
122 | | _PUBLIC_ void packet_set_full_request(struct packet_context *pc, packet_full_request_fn_t callback) |
123 | 0 | { |
124 | 0 | pc->full_request = callback; |
125 | 0 | } |
126 | | |
127 | | /* |
128 | | set a socket context to use. You must set a socket_context |
129 | | */ |
130 | | _PUBLIC_ void packet_set_socket(struct packet_context *pc, struct socket_context *sock) |
131 | 0 | { |
132 | 0 | pc->sock = sock; |
133 | 0 | } |
134 | | |
135 | | /* |
136 | | set an event context. If this is set then the code will ensure that |
137 | | packets arrive with separate events, by creating a immediate event |
138 | | for any secondary packets when more than one packet is read at one |
139 | | time on a socket. This can matter for code that relies on not |
140 | | getting more than one packet per event |
141 | | */ |
142 | | _PUBLIC_ void packet_set_event_context(struct packet_context *pc, struct tevent_context *ev) |
143 | 0 | { |
144 | 0 | pc->ev = ev; |
145 | 0 | } |
146 | | |
147 | | /* |
148 | | tell the packet layer the fde for the socket |
149 | | */ |
150 | | _PUBLIC_ void packet_set_fde(struct packet_context *pc, struct tevent_fd *fde) |
151 | 0 | { |
152 | 0 | pc->fde = fde; |
153 | 0 | } |
154 | | |
155 | | /* |
156 | | tell the packet layer to serialise requests, so we don't process two |
157 | | requests at once on one connection. You must have set the |
158 | | event_context and fde |
159 | | */ |
160 | | _PUBLIC_ void packet_set_serialise(struct packet_context *pc) |
161 | 0 | { |
162 | 0 | pc->serialise = true; |
163 | 0 | } |
164 | | |
165 | | /* |
166 | | tell the packet layer how much to read when starting a new packet |
167 | | this ensures it doesn't overread |
168 | | */ |
169 | | _PUBLIC_ void packet_set_initial_read(struct packet_context *pc, uint32_t initial_read) |
170 | 0 | { |
171 | 0 | pc->initial_read = initial_read; |
172 | 0 | } |
173 | | |
174 | | /* |
175 | | tell the packet system not to steal/free blobs given to packet_send() |
176 | | */ |
177 | | _PUBLIC_ void packet_set_nofree(struct packet_context *pc) |
178 | 0 | { |
179 | 0 | pc->nofree = true; |
180 | 0 | } |
181 | | |
182 | | /* |
183 | | tell the packet system that select/poll/epoll on the underlying |
184 | | socket may not be a reliable way to determine if data is available |
185 | | for receive. This happens with underlying socket systems such as the |
186 | | one implemented on top of GNUTLS, where there may be data in |
187 | | encryption/compression buffers that could be received by |
188 | | socket_recv(), while there is no data waiting at the real socket |
189 | | level as seen by select/poll/epoll. The GNUTLS library is supposed |
190 | | to cope with this by always leaving some data sitting in the socket |
191 | | buffer, but it does not seem to be reliable. |
192 | | */ |
193 | | _PUBLIC_ void packet_set_unreliable_select(struct packet_context *pc) |
194 | 0 | { |
195 | 0 | pc->unreliable_select = true; |
196 | 0 | } |
197 | | |
198 | | /* |
199 | | tell the caller we have an error |
200 | | */ |
201 | | static void packet_error(struct packet_context *pc, NTSTATUS status) |
202 | 0 | { |
203 | 0 | pc->sock = NULL; |
204 | 0 | if (pc->error_handler) { |
205 | 0 | pc->error_handler(pc->private_data, status); |
206 | 0 | return; |
207 | 0 | } |
208 | | /* default error handler is to free the callers private pointer */ |
209 | 0 | if (!NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE)) { |
210 | 0 | DEBUG(0,("packet_error on %s - %s\n", |
211 | 0 | talloc_get_name(pc->private_data), nt_errstr(status))); |
212 | 0 | } |
213 | 0 | talloc_free(pc->private_data); |
214 | 0 | return; |
215 | 0 | } |
216 | | |
217 | | |
218 | | /* |
219 | | tell the caller we have EOF |
220 | | */ |
221 | | static void packet_eof(struct packet_context *pc) |
222 | 0 | { |
223 | 0 | packet_error(pc, NT_STATUS_END_OF_FILE); |
224 | 0 | } |
225 | | |
226 | | |
227 | | /* |
228 | | used to put packets on event boundaries |
229 | | */ |
230 | | static void packet_next_event(struct tevent_context *ev, struct tevent_timer *te, |
231 | | struct timeval t, void *private_data) |
232 | 0 | { |
233 | 0 | struct packet_context *pc = talloc_get_type(private_data, struct packet_context); |
234 | 0 | if (pc->num_read != 0 && pc->packet_size != 0 && |
235 | 0 | pc->packet_size <= pc->num_read) { |
236 | 0 | packet_recv(pc); |
237 | 0 | } |
238 | 0 | } |
239 | | |
240 | | |
241 | | /* |
242 | | call this when the socket becomes readable to kick off the whole |
243 | | stream parsing process |
244 | | */ |
245 | | _PUBLIC_ void packet_recv(struct packet_context *pc) |
246 | 0 | { |
247 | 0 | size_t npending; |
248 | 0 | NTSTATUS status; |
249 | 0 | size_t nread = 0; |
250 | 0 | DATA_BLOB blob; |
251 | 0 | bool recv_retry = false; |
252 | |
|
253 | 0 | if (pc->processing) { |
254 | 0 | TEVENT_FD_NOT_READABLE(pc->fde); |
255 | 0 | pc->processing++; |
256 | 0 | return; |
257 | 0 | } |
258 | | |
259 | 0 | if (pc->recv_disable) { |
260 | 0 | pc->recv_need_enable = true; |
261 | 0 | TEVENT_FD_NOT_READABLE(pc->fde); |
262 | 0 | return; |
263 | 0 | } |
264 | | |
265 | 0 | if (pc->packet_size != 0 && pc->num_read >= pc->packet_size) { |
266 | 0 | goto next_partial; |
267 | 0 | } |
268 | | |
269 | 0 | if (pc->packet_size != 0) { |
270 | | /* we've already worked out how long this next packet is, so skip the |
271 | | socket_pending() call */ |
272 | 0 | npending = pc->packet_size - pc->num_read; |
273 | 0 | } else if (pc->initial_read != 0) { |
274 | 0 | npending = pc->initial_read - pc->num_read; |
275 | 0 | } else { |
276 | 0 | if (pc->sock) { |
277 | 0 | status = socket_pending(pc->sock, &npending); |
278 | 0 | } else { |
279 | 0 | status = NT_STATUS_CONNECTION_DISCONNECTED; |
280 | 0 | } |
281 | 0 | if (!NT_STATUS_IS_OK(status)) { |
282 | 0 | packet_error(pc, status); |
283 | 0 | return; |
284 | 0 | } |
285 | 0 | } |
286 | | |
287 | 0 | if (npending == 0) { |
288 | 0 | packet_eof(pc); |
289 | 0 | return; |
290 | 0 | } |
291 | | |
292 | 0 | again: |
293 | |
|
294 | 0 | if (npending + pc->num_read < npending) { |
295 | 0 | packet_error(pc, NT_STATUS_INVALID_PARAMETER); |
296 | 0 | return; |
297 | 0 | } |
298 | | |
299 | 0 | if (npending + pc->num_read < pc->num_read) { |
300 | 0 | packet_error(pc, NT_STATUS_INVALID_PARAMETER); |
301 | 0 | return; |
302 | 0 | } |
303 | | |
304 | | /* possibly expand the partial packet buffer */ |
305 | 0 | if (npending + pc->num_read > pc->partial.length) { |
306 | 0 | if (!data_blob_realloc(pc, &pc->partial, npending+pc->num_read)) { |
307 | 0 | packet_error(pc, NT_STATUS_NO_MEMORY); |
308 | 0 | return; |
309 | 0 | } |
310 | 0 | } |
311 | | |
312 | 0 | if (pc->partial.length < pc->num_read + npending) { |
313 | 0 | packet_error(pc, NT_STATUS_INVALID_PARAMETER); |
314 | 0 | return; |
315 | 0 | } |
316 | | |
317 | 0 | if ((uint8_t *)pc->partial.data + pc->num_read < (uint8_t *)pc->partial.data) { |
318 | 0 | packet_error(pc, NT_STATUS_INVALID_PARAMETER); |
319 | 0 | return; |
320 | 0 | } |
321 | 0 | if ((uint8_t *)pc->partial.data + pc->num_read + npending < (uint8_t *)pc->partial.data) { |
322 | 0 | packet_error(pc, NT_STATUS_INVALID_PARAMETER); |
323 | 0 | return; |
324 | 0 | } |
325 | | |
326 | 0 | status = socket_recv(pc->sock, pc->partial.data + pc->num_read, |
327 | 0 | npending, &nread); |
328 | |
|
329 | 0 | if (NT_STATUS_IS_ERR(status)) { |
330 | 0 | packet_error(pc, status); |
331 | 0 | return; |
332 | 0 | } |
333 | 0 | if (recv_retry && NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) { |
334 | 0 | nread = 0; |
335 | 0 | status = NT_STATUS_OK; |
336 | 0 | } |
337 | 0 | if (!NT_STATUS_IS_OK(status)) { |
338 | 0 | return; |
339 | 0 | } |
340 | | |
341 | 0 | if (nread == 0 && !recv_retry) { |
342 | 0 | packet_eof(pc); |
343 | 0 | return; |
344 | 0 | } |
345 | | |
346 | 0 | pc->num_read += nread; |
347 | |
|
348 | 0 | if (pc->unreliable_select && nread != 0) { |
349 | 0 | recv_retry = true; |
350 | 0 | status = socket_pending(pc->sock, &npending); |
351 | 0 | if (!NT_STATUS_IS_OK(status)) { |
352 | 0 | packet_error(pc, status); |
353 | 0 | return; |
354 | 0 | } |
355 | 0 | if (npending != 0) { |
356 | 0 | goto again; |
357 | 0 | } |
358 | 0 | } |
359 | | |
360 | 0 | next_partial: |
361 | 0 | if (pc->partial.length != pc->num_read) { |
362 | 0 | if (!data_blob_realloc(pc, &pc->partial, pc->num_read)) { |
363 | 0 | packet_error(pc, NT_STATUS_NO_MEMORY); |
364 | 0 | return; |
365 | 0 | } |
366 | 0 | } |
367 | | |
368 | | /* see if its a full request */ |
369 | 0 | blob = pc->partial; |
370 | 0 | blob.length = pc->num_read; |
371 | 0 | status = pc->full_request(pc->private_data, blob, &pc->packet_size); |
372 | 0 | if (NT_STATUS_IS_ERR(status)) { |
373 | 0 | packet_error(pc, status); |
374 | 0 | return; |
375 | 0 | } |
376 | 0 | if (!NT_STATUS_IS_OK(status)) { |
377 | 0 | return; |
378 | 0 | } |
379 | | |
380 | 0 | if (pc->packet_size > pc->num_read) { |
381 | | /* the caller made an error */ |
382 | 0 | DEBUG(0,("Invalid packet_size %lu greater than num_read %lu\n", |
383 | 0 | (long)pc->packet_size, (long)pc->num_read)); |
384 | 0 | packet_error(pc, NT_STATUS_INVALID_PARAMETER); |
385 | 0 | return; |
386 | 0 | } |
387 | | |
388 | | /* it is a full request - give it to the caller */ |
389 | 0 | blob = pc->partial; |
390 | 0 | blob.length = pc->num_read; |
391 | |
|
392 | 0 | if (pc->packet_size < pc->num_read) { |
393 | 0 | pc->partial = data_blob_talloc(pc, blob.data + pc->packet_size, |
394 | 0 | pc->num_read - pc->packet_size); |
395 | 0 | if (pc->partial.data == NULL) { |
396 | 0 | packet_error(pc, NT_STATUS_NO_MEMORY); |
397 | 0 | return; |
398 | 0 | } |
399 | | /* Trunate the blob sent to the caller to only the packet length */ |
400 | 0 | if (!data_blob_realloc(pc, &blob, pc->packet_size)) { |
401 | 0 | packet_error(pc, NT_STATUS_NO_MEMORY); |
402 | 0 | return; |
403 | 0 | } |
404 | 0 | } else { |
405 | 0 | pc->partial = data_blob(NULL, 0); |
406 | 0 | } |
407 | 0 | pc->num_read -= pc->packet_size; |
408 | 0 | pc->packet_size = 0; |
409 | | |
410 | 0 | if (pc->serialise) { |
411 | 0 | pc->processing = 1; |
412 | 0 | } |
413 | |
|
414 | 0 | pc->busy = true; |
415 | |
|
416 | 0 | status = pc->callback(pc->private_data, blob); |
417 | |
|
418 | 0 | pc->busy = false; |
419 | |
|
420 | 0 | if (pc->destructor_called) { |
421 | 0 | talloc_free(pc); |
422 | 0 | return; |
423 | 0 | } |
424 | | |
425 | 0 | if (pc->processing) { |
426 | 0 | if (pc->processing > 1) { |
427 | 0 | TEVENT_FD_READABLE(pc->fde); |
428 | 0 | } |
429 | 0 | pc->processing = 0; |
430 | 0 | } |
431 | |
|
432 | 0 | if (!NT_STATUS_IS_OK(status)) { |
433 | 0 | packet_error(pc, status); |
434 | 0 | return; |
435 | 0 | } |
436 | | |
437 | | /* Have we consumed the whole buffer yet? */ |
438 | 0 | if (pc->partial.length == 0) { |
439 | 0 | return; |
440 | 0 | } |
441 | | |
442 | | /* we got multiple packets in one tcp read */ |
443 | 0 | if (pc->ev == NULL) { |
444 | 0 | goto next_partial; |
445 | 0 | } |
446 | | |
447 | 0 | blob = pc->partial; |
448 | 0 | blob.length = pc->num_read; |
449 | |
|
450 | 0 | status = pc->full_request(pc->private_data, blob, &pc->packet_size); |
451 | 0 | if (NT_STATUS_IS_ERR(status)) { |
452 | 0 | packet_error(pc, status); |
453 | 0 | return; |
454 | 0 | } |
455 | | |
456 | 0 | if (!NT_STATUS_IS_OK(status)) { |
457 | 0 | return; |
458 | 0 | } |
459 | | |
460 | 0 | tevent_add_timer(pc->ev, pc, timeval_zero(), packet_next_event, pc); |
461 | 0 | } |
462 | | |
463 | | |
464 | | /* |
465 | | temporarily disable receiving |
466 | | */ |
467 | | _PUBLIC_ void packet_recv_disable(struct packet_context *pc) |
468 | 0 | { |
469 | 0 | pc->recv_disable = true; |
470 | 0 | } |
471 | | |
472 | | /* |
473 | | re-enable receiving |
474 | | */ |
475 | | _PUBLIC_ void packet_recv_enable(struct packet_context *pc) |
476 | 0 | { |
477 | 0 | if (pc->recv_need_enable) { |
478 | 0 | pc->recv_need_enable = false; |
479 | 0 | TEVENT_FD_READABLE(pc->fde); |
480 | 0 | } |
481 | 0 | pc->recv_disable = false; |
482 | 0 | if (pc->num_read != 0 && pc->packet_size >= pc->num_read) { |
483 | 0 | tevent_add_timer(pc->ev, pc, timeval_zero(), packet_next_event, pc); |
484 | 0 | } |
485 | 0 | } |
486 | | |
487 | | /* |
488 | | trigger a run of the send queue |
489 | | */ |
490 | | _PUBLIC_ void packet_queue_run(struct packet_context *pc) |
491 | 0 | { |
492 | 0 | while (pc->send_queue) { |
493 | 0 | struct send_element *el = pc->send_queue; |
494 | 0 | NTSTATUS status; |
495 | 0 | size_t nwritten; |
496 | 0 | DATA_BLOB blob = data_blob_const(el->blob.data + el->nsent, |
497 | 0 | el->blob.length - el->nsent); |
498 | |
|
499 | 0 | status = socket_send(pc->sock, &blob, &nwritten); |
500 | |
|
501 | 0 | if (NT_STATUS_IS_ERR(status)) { |
502 | 0 | packet_error(pc, status); |
503 | 0 | return; |
504 | 0 | } |
505 | 0 | if (!NT_STATUS_IS_OK(status)) { |
506 | 0 | return; |
507 | 0 | } |
508 | 0 | el->nsent += nwritten; |
509 | 0 | if (el->nsent == el->blob.length) { |
510 | 0 | DLIST_REMOVE(pc->send_queue, el); |
511 | 0 | if (el->send_callback) { |
512 | 0 | pc->busy = true; |
513 | 0 | el->send_callback(el->send_callback_private); |
514 | 0 | pc->busy = false; |
515 | 0 | if (pc->destructor_called) { |
516 | 0 | talloc_free(pc); |
517 | 0 | return; |
518 | 0 | } |
519 | 0 | } |
520 | 0 | talloc_free(el); |
521 | 0 | } |
522 | 0 | } |
523 | | |
524 | | /* we're out of requests to send, so don't wait for write |
525 | | events any more */ |
526 | 0 | TEVENT_FD_NOT_WRITEABLE(pc->fde); |
527 | 0 | } |
528 | | |
529 | | /* |
530 | | put a packet in the send queue. When the packet is actually sent, |
531 | | call send_callback. |
532 | | |
533 | | Useful for operations that must occur after sending a message, such |
534 | | as the switch to SASL encryption after as successful LDAP bind reply. |
535 | | */ |
536 | | _PUBLIC_ NTSTATUS packet_send_callback(struct packet_context *pc, DATA_BLOB blob, |
537 | | packet_send_callback_fn_t send_callback, |
538 | | void *private_data) |
539 | 0 | { |
540 | 0 | struct send_element *el; |
541 | 0 | el = talloc(pc, struct send_element); |
542 | 0 | NT_STATUS_HAVE_NO_MEMORY(el); |
543 | | |
544 | 0 | DLIST_ADD_END(pc->send_queue, el); |
545 | 0 | el->blob = blob; |
546 | 0 | el->nsent = 0; |
547 | 0 | el->send_callback = send_callback; |
548 | 0 | el->send_callback_private = private_data; |
549 | | |
550 | | /* if we aren't going to free the packet then we must reference it |
551 | | to ensure it doesn't disappear before going out */ |
552 | 0 | if (pc->nofree) { |
553 | 0 | if (!talloc_reference(el, blob.data)) { |
554 | 0 | return NT_STATUS_NO_MEMORY; |
555 | 0 | } |
556 | 0 | } else { |
557 | 0 | talloc_steal(el, blob.data); |
558 | 0 | } |
559 | | |
560 | 0 | if (private_data && !talloc_reference(el, private_data)) { |
561 | 0 | return NT_STATUS_NO_MEMORY; |
562 | 0 | } |
563 | | |
564 | 0 | TEVENT_FD_WRITEABLE(pc->fde); |
565 | |
|
566 | 0 | return NT_STATUS_OK; |
567 | 0 | } |
568 | | |
569 | | /* |
570 | | put a packet in the send queue |
571 | | */ |
572 | | _PUBLIC_ NTSTATUS packet_send(struct packet_context *pc, DATA_BLOB blob) |
573 | 0 | { |
574 | 0 | return packet_send_callback(pc, blob, NULL, NULL); |
575 | 0 | } |
576 | | |
577 | | |
578 | | /* |
579 | | a full request checker for NBT formatted packets (first 3 bytes are length) |
580 | | */ |
581 | | _PUBLIC_ NTSTATUS packet_full_request_nbt(void *private_data, DATA_BLOB blob, size_t *size) |
582 | 0 | { |
583 | 0 | if (blob.length < 4) { |
584 | 0 | return STATUS_MORE_ENTRIES; |
585 | 0 | } |
586 | | /* |
587 | | * Note: that we use smb_len_tcp() instead |
588 | | * of smb_len_nbt() as this function is not |
589 | | * used for nbt and the source4 copy |
590 | | * of smb_len() was smb_len_tcp() |
591 | | */ |
592 | 0 | *size = 4 + smb_len_tcp(blob.data); |
593 | 0 | if (*size > blob.length) { |
594 | 0 | return STATUS_MORE_ENTRIES; |
595 | 0 | } |
596 | 0 | return NT_STATUS_OK; |
597 | 0 | } |
598 | | |
599 | | |
600 | | /* |
601 | | work out if a packet is complete for protocols that use a 32 bit network byte |
602 | | order length |
603 | | */ |
604 | | _PUBLIC_ NTSTATUS packet_full_request_u32(void *private_data, DATA_BLOB blob, size_t *size) |
605 | 0 | { |
606 | 0 | if (blob.length < 4) { |
607 | 0 | return STATUS_MORE_ENTRIES; |
608 | 0 | } |
609 | 0 | *size = 4 + RIVAL(blob.data, 0); |
610 | 0 | if (*size > blob.length) { |
611 | 0 | return STATUS_MORE_ENTRIES; |
612 | 0 | } |
613 | 0 | return NT_STATUS_OK; |
614 | 0 | } |