3 Buffer access functions for the object management protocol... */
6 * Copyright (C) 2004-2022 Internet Systems Consortium, Inc. ("ISC")
7 * Copyright (c) 1999-2003 by Internet Software Consortium
9 * This Source Code Form is subject to the terms of the Mozilla Public
10 * License, v. 2.0. If a copy of the MPL was not distributed with this
11 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
13 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
19 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21 * Internet Systems Consortium, Inc.
23 * Newmarket, NH 03857 USA
25 * https://www.isc.org/
31 #include <omapip/omapip_p.h>
35 static void trace_connection_input_input (trace_type_t
*, unsigned, char *);
36 static void trace_connection_input_stop (trace_type_t
*);
37 static void trace_connection_output_input (trace_type_t
*, unsigned, char *);
38 static void trace_connection_output_stop (trace_type_t
*);
39 static trace_type_t
*trace_connection_input
;
40 static trace_type_t
*trace_connection_output
;
41 static isc_result_t
omapi_connection_reader_trace (omapi_object_t
*,
44 extern omapi_array_t
*omapi_connections
;
46 void omapi_buffer_trace_setup ()
48 trace_connection_input
=
49 trace_type_register ("connection-input",
51 trace_connection_input_input
,
52 trace_connection_input_stop
, MDL
);
53 trace_connection_output
=
54 trace_type_register ("connection-output",
56 trace_connection_output_input
,
57 trace_connection_output_stop
, MDL
);
60 static void trace_connection_input_input (trace_type_t
*ttype
,
61 unsigned length
, char *buf
)
63 unsigned left
, taken
, cc
= 0;
65 int32_t connect_index
;
67 omapi_connection_object_t
*c
= (omapi_connection_object_t
*)0;
69 memcpy (&connect_index
, buf
, sizeof connect_index
);
70 connect_index
= ntohl (connect_index
);
72 omapi_array_foreach_begin (omapi_connections
,
73 omapi_connection_object_t
, lp
) {
74 if (lp
-> index
== ntohl (connect_index
)) {
75 omapi_connection_reference (&c
, lp
, MDL
);
76 omapi_connection_dereference (&lp
, MDL
);
79 } omapi_array_foreach_end (omapi_connections
,
80 omapi_connection_object_t
, lp
);
83 log_error ("trace connection input: no connection index %ld",
84 (long int)connect_index
);
88 s
= buf
+ sizeof connect_index
;
89 left
= length
- sizeof connect_index
;
93 status
= omapi_connection_reader_trace ((omapi_object_t
*)c
,
95 if (status
!= ISC_R_SUCCESS
) {
96 log_error ("trace connection input: %s",
97 isc_result_totext (status
));
102 log_error ("trace connection_input: %s",
103 "input is not being consumed.");
112 omapi_connection_dereference (&c
, MDL
);
115 static void trace_connection_input_stop (trace_type_t
*ttype
) { }
117 static void trace_connection_output_input (trace_type_t
*ttype
,
118 unsigned length
, char *buf
)
120 /* We *could* check to see if the output is correct, but for now
121 we aren't going to do that. */
124 static void trace_connection_output_stop (trace_type_t
*ttype
) { }
128 /* Make sure that at least len bytes are in the input buffer, and if not,
129 read enough bytes to make up the difference. */
131 isc_result_t
omapi_connection_reader (omapi_object_t
*h
)
133 #if defined (TRACING)
134 return omapi_connection_reader_trace (h
, 0, (char *)0, (unsigned *)0);
137 static isc_result_t
omapi_connection_reader_trace (omapi_object_t
*h
,
140 unsigned *stuff_taken
)
143 omapi_buffer_t
*buffer
;
147 omapi_connection_object_t
*c
;
148 unsigned bytes_to_read
;
150 if (!h
|| h
-> type
!= omapi_type_connection
)
151 return DHCP_R_INVALIDARG
;
152 c
= (omapi_connection_object_t
*)h
;
154 /* See if there are enough bytes. */
155 if (c
-> in_bytes
>= OMAPI_BUF_SIZE
- 1 &&
156 c
-> in_bytes
> c
-> bytes_needed
)
157 return ISC_R_SUCCESS
;
161 for (buffer
= c
-> inbufs
; buffer
-> next
;
162 buffer
= buffer
-> next
)
164 if (!BUFFER_BYTES_FREE (buffer
)) {
165 status
= omapi_buffer_new (&buffer
-> next
, MDL
);
166 if (status
!= ISC_R_SUCCESS
)
168 buffer
= buffer
-> next
;
171 status
= omapi_buffer_new (&c
-> inbufs
, MDL
);
172 if (status
!= ISC_R_SUCCESS
)
174 buffer
= c
-> inbufs
;
177 bytes_to_read
= BUFFER_BYTES_FREE (buffer
);
179 while (bytes_to_read
) {
180 if (buffer
-> tail
> buffer
-> head
)
181 read_len
= sizeof (buffer
-> buf
) - buffer
-> tail
;
183 read_len
= buffer
-> head
- buffer
-> tail
;
185 #if defined (TRACING)
186 if (trace_playback()) {
188 if (read_len
> stuff_len
)
189 read_len
= stuff_len
;
191 *stuff_taken
+= read_len
;
192 memcpy (&buffer
-> buf
[buffer
-> tail
],
193 stuff_buf
, read_len
);
194 stuff_len
-= read_len
;
195 stuff_buf
+= read_len
;
196 read_status
= read_len
;
203 read_status
= read (c
-> socket
,
204 &buffer
-> buf
[buffer
-> tail
],
207 if (read_status
< 0) {
208 if (errno
== EWOULDBLOCK
)
210 else if (errno
== EIO
)
211 return ISC_R_IOERROR
;
212 else if (errno
== EINVAL
)
213 return DHCP_R_INVALIDARG
;
214 else if (errno
== ECONNRESET
) {
215 omapi_disconnect (h
, 1);
216 return ISC_R_SHUTTINGDOWN
;
218 return ISC_R_UNEXPECTED
;
221 /* If we got a zero-length read, as opposed to EWOULDBLOCK,
222 the remote end closed the connection. */
223 if (read_status
== 0) {
224 omapi_disconnect (h
, 0);
225 return ISC_R_SHUTTINGDOWN
;
227 #if defined (TRACING)
228 if (trace_record ()) {
230 int32_t connect_index
;
232 connect_index
= htonl (c
-> index
);
234 iov
[0].buf
= (char *)&connect_index
;
235 iov
[0].len
= sizeof connect_index
;
236 iov
[1].buf
= &buffer
-> buf
[buffer
-> tail
];
237 iov
[1].len
= read_status
;
239 status
= (trace_write_packet_iov
240 (trace_connection_input
, 2, iov
, MDL
));
241 if (status
!= ISC_R_SUCCESS
) {
243 log_error ("trace connection input: %s",
244 isc_result_totext (status
));
248 buffer
-> tail
+= read_status
;
249 c
-> in_bytes
+= read_status
;
250 if (buffer
-> tail
== sizeof buffer
-> buf
)
252 if (read_status
< read_len
)
254 bytes_to_read
-= read_status
;
257 if (c
-> bytes_needed
<= c
-> in_bytes
) {
258 omapi_signal (h
, "ready", c
);
260 return ISC_R_SUCCESS
;
263 /* Put some bytes into the output buffer for a connection. */
265 isc_result_t
omapi_connection_copyin (omapi_object_t
*h
,
266 const unsigned char *bufp
,
269 omapi_buffer_t
*buffer
;
271 int bytes_copied
= 0;
273 int sig_flags
= SIG_MODE_UPDATE
;
274 omapi_connection_object_t
*c
;
276 /* no need to verify len as it's unsigned */
277 if (!h
|| h
-> type
!= omapi_type_connection
)
278 return DHCP_R_INVALIDARG
;
279 c
= (omapi_connection_object_t
*)h
;
281 /* If the connection is closed, return an error if the caller
283 if (c
-> state
== omapi_connection_disconnecting
||
284 c
-> state
== omapi_connection_closed
)
285 return ISC_R_NOTCONNECTED
;
288 for (buffer
= c
-> outbufs
;
289 buffer
-> next
; buffer
= buffer
-> next
)
292 status
= omapi_buffer_new (&c
-> outbufs
, MDL
);
293 if (status
!= ISC_R_SUCCESS
)
295 buffer
= c
-> outbufs
;
298 while (bytes_copied
< len
) {
299 /* If there is no space available in this buffer,
300 allocate a new one. */
301 if (!BUFFER_BYTES_FREE (buffer
)) {
302 status
= (omapi_buffer_new (&buffer
-> next
, MDL
));
303 if (status
!= ISC_R_SUCCESS
)
305 buffer
= buffer
-> next
;
308 if (buffer
-> tail
> buffer
-> head
)
309 copy_len
= sizeof (buffer
-> buf
) - buffer
-> tail
;
311 copy_len
= buffer
-> head
- buffer
-> tail
;
313 if (copy_len
> (len
- bytes_copied
))
314 copy_len
= len
- bytes_copied
;
317 if (!c
-> out_context
)
318 sig_flags
|= SIG_MODE_INIT
;
319 status
= omapi_connection_sign_data
320 (sig_flags
, c
-> out_key
, &c
-> out_context
,
321 &bufp
[bytes_copied
], copy_len
,
322 (omapi_typed_data_t
**)0);
323 if (status
!= ISC_R_SUCCESS
)
327 memcpy (&buffer
-> buf
[buffer
-> tail
],
328 &bufp
[bytes_copied
], copy_len
);
329 buffer
-> tail
+= copy_len
;
330 c
-> out_bytes
+= copy_len
;
331 bytes_copied
+= copy_len
;
332 if (buffer
-> tail
== sizeof buffer
-> buf
)
336 status
= ISC_R_SUCCESS
;
340 * If we have any bytes to send and we have a proper io object
341 * inform the socket code that we would like to know when we
342 * can send more bytes.
344 if (c
->out_bytes
!= 0) {
345 if ((c
->outer
!= NULL
) &&
346 (c
->outer
->type
== omapi_type_io_object
)) {
347 omapi_io_object_t
*io
= (omapi_io_object_t
*)c
->outer
;
348 isc_socket_fdwatchpoke(io
->fd
,
349 ISC_SOCKFDWATCH_WRITE
);
356 /* Copy some bytes from the input buffer, and advance the input buffer
357 pointer beyond the bytes copied out. */
359 isc_result_t
omapi_connection_copyout (unsigned char *buf
,
363 unsigned bytes_remaining
;
364 unsigned bytes_this_copy
;
366 omapi_buffer_t
*buffer
;
368 int sig_flags
= SIG_MODE_UPDATE
;
369 omapi_connection_object_t
*c
;
372 if (!h
|| h
-> type
!= omapi_type_connection
)
373 return DHCP_R_INVALIDARG
;
374 c
= (omapi_connection_object_t
*)h
;
376 if (size
> c
-> in_bytes
)
379 bytes_remaining
= size
;
380 buffer
= c
-> inbufs
;
382 while (bytes_remaining
) {
384 return ISC_R_UNEXPECTED
;
385 if (BYTES_IN_BUFFER (buffer
)) {
386 if (buffer
-> head
== (sizeof buffer
-> buf
) - 1)
389 first_byte
= buffer
-> head
+ 1;
391 if (first_byte
> buffer
-> tail
) {
392 bytes_this_copy
= (sizeof buffer
-> buf
-
396 buffer
-> tail
- first_byte
;
398 if (bytes_this_copy
> bytes_remaining
)
399 bytes_this_copy
= bytes_remaining
;
402 if (!c
-> in_context
)
403 sig_flags
|= SIG_MODE_INIT
;
404 status
= omapi_connection_sign_data
409 &buffer
-> buf
[first_byte
],
411 (omapi_typed_data_t
**)0);
412 if (status
!= ISC_R_SUCCESS
)
416 memcpy (bufp
, &buffer
-> buf
[first_byte
],
418 bufp
+= bytes_this_copy
;
420 bytes_remaining
-= bytes_this_copy
;
421 buffer
-> head
= first_byte
+ bytes_this_copy
- 1;
422 c
-> in_bytes
-= bytes_this_copy
;
425 if (!BYTES_IN_BUFFER (buffer
))
426 buffer
= buffer
-> next
;
429 /* Get rid of any input buffers that we emptied. */
430 buffer
= (omapi_buffer_t
*)0;
431 while (c
-> inbufs
&&
432 !BYTES_IN_BUFFER (c
-> inbufs
)) {
433 if (c
-> inbufs
-> next
) {
434 omapi_buffer_reference (&buffer
,
435 c
-> inbufs
-> next
, MDL
);
436 omapi_buffer_dereference (&c
-> inbufs
-> next
, MDL
);
438 omapi_buffer_dereference (&c
-> inbufs
, MDL
);
440 omapi_buffer_reference
441 (&c
-> inbufs
, buffer
, MDL
);
442 omapi_buffer_dereference (&buffer
, MDL
);
445 return ISC_R_SUCCESS
;
448 isc_result_t
omapi_connection_writer (omapi_object_t
*h
)
450 unsigned bytes_this_write
;
453 omapi_buffer_t
*buffer
;
454 omapi_connection_object_t
*c
;
456 if (!h
|| h
-> type
!= omapi_type_connection
)
457 return DHCP_R_INVALIDARG
;
458 c
= (omapi_connection_object_t
*)h
;
460 /* Already flushed... */
462 return ISC_R_SUCCESS
;
464 buffer
= c
-> outbufs
;
466 while (c
-> out_bytes
) {
468 return ISC_R_UNEXPECTED
;
469 if (BYTES_IN_BUFFER (buffer
)) {
470 if (buffer
-> head
== (sizeof buffer
-> buf
) - 1)
473 first_byte
= buffer
-> head
+ 1;
475 if (first_byte
> buffer
-> tail
) {
476 bytes_this_write
= (sizeof buffer
-> buf
-
480 buffer
-> tail
- first_byte
;
482 bytes_written
= write (c
-> socket
,
483 &buffer
-> buf
[first_byte
],
485 /* If the write failed with EWOULDBLOCK or we wrote
486 zero bytes, a further write would block, so we have
487 flushed as much as we can for now. Other errors
488 are really errors. */
489 if (bytes_written
< 0) {
490 if (errno
== EWOULDBLOCK
|| errno
== EAGAIN
)
491 return ISC_R_INPROGRESS
;
492 else if (errno
== EPIPE
)
495 else if (errno
== EFBIG
|| errno
== EDQUOT
)
497 else if (errno
== EFBIG
)
499 return ISC_R_NORESOURCES
;
500 else if (errno
== ENOSPC
)
501 return ISC_R_NOSPACE
;
502 else if (errno
== EIO
)
503 return ISC_R_IOERROR
;
504 else if (errno
== EINVAL
)
505 return DHCP_R_INVALIDARG
;
506 else if (errno
== ECONNRESET
)
507 return ISC_R_SHUTTINGDOWN
;
509 return ISC_R_UNEXPECTED
;
511 if (bytes_written
== 0)
512 return ISC_R_INPROGRESS
;
514 #if defined (TRACING)
515 if (trace_record ()) {
518 int32_t connect_index
;
520 connect_index
= htonl (c
-> index
);
522 iov
[0].buf
= (char *)&connect_index
;
523 iov
[0].len
= sizeof connect_index
;
524 iov
[1].buf
= &buffer
-> buf
[buffer
-> tail
];
525 iov
[1].len
= bytes_written
;
527 status
= (trace_write_packet_iov
528 (trace_connection_input
, 2, iov
,
530 if (status
!= ISC_R_SUCCESS
) {
532 log_error ("trace %s output: %s",
534 isc_result_totext (status
));
539 buffer
-> head
= first_byte
+ bytes_written
- 1;
540 c
-> out_bytes
-= bytes_written
;
542 /* If we didn't finish out the write, we filled the
543 O.S. output buffer and a further write would block,
544 so stop trying to flush now. */
545 if (bytes_written
!= bytes_this_write
)
546 return ISC_R_INPROGRESS
;
549 if (!BYTES_IN_BUFFER (buffer
))
550 buffer
= buffer
-> next
;
553 /* Get rid of any output buffers we emptied. */
554 buffer
= (omapi_buffer_t
*)0;
555 while (c
-> outbufs
&&
556 !BYTES_IN_BUFFER (c
-> outbufs
)) {
557 if (c
-> outbufs
-> next
) {
558 omapi_buffer_reference (&buffer
,
559 c
-> outbufs
-> next
, MDL
);
560 omapi_buffer_dereference (&c
-> outbufs
-> next
, MDL
);
562 omapi_buffer_dereference (&c
-> outbufs
, MDL
);
564 omapi_buffer_reference (&c
-> outbufs
, buffer
, MDL
);
565 omapi_buffer_dereference (&buffer
, MDL
);
569 /* If we had data left to write when we're told to disconnect,
570 * we need recall disconnect, now that we're done writing.
572 if (c
->out_bytes
== 0 && c
->state
== omapi_connection_disconnecting
) {
573 omapi_disconnect (h
, 1);
574 return ISC_R_SHUTTINGDOWN
;
577 return ISC_R_SUCCESS
;
580 isc_result_t
omapi_connection_get_uint32 (omapi_object_t
*c
,
586 status
= omapi_connection_copyout ((unsigned char *)&inbuf
,
588 if (status
!= ISC_R_SUCCESS
)
591 *result
= ntohl (inbuf
);
592 return ISC_R_SUCCESS
;
595 isc_result_t
omapi_connection_put_uint32 (omapi_object_t
*c
,
600 inbuf
= htonl (value
);
602 return omapi_connection_copyin (c
, (unsigned char *)&inbuf
,
606 isc_result_t
omapi_connection_get_uint16 (omapi_object_t
*c
,
612 status
= omapi_connection_copyout ((unsigned char *)&inbuf
,
614 if (status
!= ISC_R_SUCCESS
)
617 *result
= ntohs (inbuf
);
618 return ISC_R_SUCCESS
;
621 isc_result_t
omapi_connection_put_uint16 (omapi_object_t
*c
,
626 inbuf
= htons (value
);
628 return omapi_connection_copyin (c
, (unsigned char *)&inbuf
,
632 isc_result_t
omapi_connection_write_typed_data (omapi_object_t
*c
,
633 omapi_typed_data_t
*data
)
636 omapi_handle_t handle
;
638 /* Null data is valid. */
640 return omapi_connection_put_uint32 (c
, 0);
642 switch (data
-> type
) {
643 case omapi_datatype_int
:
644 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
645 if (status
!= ISC_R_SUCCESS
)
647 return omapi_connection_put_uint32 (c
, ((u_int32_t
)
648 (data
-> u
.integer
)));
650 case omapi_datatype_string
:
651 case omapi_datatype_data
:
652 status
= omapi_connection_put_uint32 (c
, data
-> u
.buffer
.len
);
653 if (status
!= ISC_R_SUCCESS
)
655 if (data
-> u
.buffer
.len
)
656 return omapi_connection_copyin
657 (c
, data
-> u
.buffer
.value
,
658 data
-> u
.buffer
.len
);
659 return ISC_R_SUCCESS
;
661 case omapi_datatype_object
:
662 if (data
-> u
.object
) {
663 status
= omapi_object_handle (&handle
,
665 if (status
!= ISC_R_SUCCESS
)
669 status
= omapi_connection_put_uint32 (c
, sizeof handle
);
670 if (status
!= ISC_R_SUCCESS
)
672 return omapi_connection_put_uint32 (c
, handle
);
675 return DHCP_R_INVALIDARG
;
678 isc_result_t
omapi_connection_put_name (omapi_object_t
*c
, const char *name
)
681 unsigned len
= strlen (name
);
683 status
= omapi_connection_put_uint16 (c
, len
);
684 if (status
!= ISC_R_SUCCESS
)
686 return omapi_connection_copyin (c
, (const unsigned char *)name
, len
);
689 isc_result_t
omapi_connection_put_string (omapi_object_t
*c
,
696 len
= strlen (string
);
700 status
= omapi_connection_put_uint32 (c
, len
);
701 if (status
!= ISC_R_SUCCESS
)
704 return omapi_connection_copyin
705 (c
, (const unsigned char *)string
, len
);
706 return ISC_R_SUCCESS
;
709 isc_result_t
omapi_connection_put_handle (omapi_object_t
*c
, omapi_object_t
*h
)
712 omapi_handle_t handle
;
715 status
= omapi_object_handle (&handle
, h
);
716 if (status
!= ISC_R_SUCCESS
)
719 handle
= 0; /* The null handle. */
720 status
= omapi_connection_put_uint32 (c
, sizeof handle
);
721 if (status
!= ISC_R_SUCCESS
)
723 return omapi_connection_put_uint32 (c
, handle
);
726 isc_result_t
omapi_connection_put_named_uint32 (omapi_object_t
*c
,
732 status
= omapi_connection_put_name(c
, name
);
733 if (status
!= ISC_R_SUCCESS
)
736 status
= omapi_connection_put_uint32(c
, sizeof(u_int32_t
));
737 if (status
!= ISC_R_SUCCESS
)
740 status
= omapi_connection_put_uint32(c
, value
);