3 Buffer access functions for the object management protocol... */
6 * Copyright (c) 2009,2012-2014 by Internet Systems Consortium, Inc. ("ISC")
7 * Copyright (c) 2004,2005,2007 by Internet Systems Consortium, Inc. ("ISC")
8 * Copyright (c) 1999-2003 by Internet Software Consortium
10 * Permission to use, copy, modify, and distribute this software for any
11 * purpose with or without fee is hereby granted, provided that the above
12 * copyright notice and this permission notice appear in all copies.
14 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
15 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
16 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
17 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
19 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
20 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22 * Internet Systems Consortium, Inc.
24 * Redwood City, CA 94063
26 * https://www.isc.org/
32 #include <omapip/omapip_p.h>
36 static void trace_connection_input_input (trace_type_t
*, unsigned, char *);
37 static void trace_connection_input_stop (trace_type_t
*);
38 static void trace_connection_output_input (trace_type_t
*, unsigned, char *);
39 static void trace_connection_output_stop (trace_type_t
*);
40 static trace_type_t
*trace_connection_input
;
41 static trace_type_t
*trace_connection_output
;
42 static isc_result_t
omapi_connection_reader_trace (omapi_object_t
*,
45 extern omapi_array_t
*omapi_connections
;
47 void omapi_buffer_trace_setup ()
49 trace_connection_input
=
50 trace_type_register ("connection-input",
52 trace_connection_input_input
,
53 trace_connection_input_stop
, MDL
);
54 trace_connection_output
=
55 trace_type_register ("connection-output",
57 trace_connection_output_input
,
58 trace_connection_output_stop
, MDL
);
61 static void trace_connection_input_input (trace_type_t
*ttype
,
62 unsigned length
, char *buf
)
64 unsigned left
, taken
, cc
= 0;
66 int32_t connect_index
;
68 omapi_connection_object_t
*c
= (omapi_connection_object_t
*)0;
70 memcpy (&connect_index
, buf
, sizeof connect_index
);
71 connect_index
= ntohl (connect_index
);
73 omapi_array_foreach_begin (omapi_connections
,
74 omapi_connection_object_t
, lp
) {
75 if (lp
-> index
== ntohl (connect_index
)) {
76 omapi_connection_reference (&c
, lp
, MDL
);
77 omapi_connection_dereference (&lp
, MDL
);
80 } omapi_array_foreach_end (omapi_connections
,
81 omapi_connection_object_t
, lp
);
84 log_error ("trace connection input: no connection index %ld",
85 (long int)connect_index
);
89 s
= buf
+ sizeof connect_index
;
90 left
= length
- sizeof connect_index
;
94 status
= omapi_connection_reader_trace ((omapi_object_t
*)c
,
96 if (status
!= ISC_R_SUCCESS
) {
97 log_error ("trace connection input: %s",
98 isc_result_totext (status
));
103 log_error ("trace connection_input: %s",
104 "input is not being consumed.");
113 omapi_connection_dereference (&c
, MDL
);
116 static void trace_connection_input_stop (trace_type_t
*ttype
) { }
118 static void trace_connection_output_input (trace_type_t
*ttype
,
119 unsigned length
, char *buf
)
121 /* We *could* check to see if the output is correct, but for now
122 we aren't going to do that. */
125 static void trace_connection_output_stop (trace_type_t
*ttype
) { }
129 /* Make sure that at least len bytes are in the input buffer, and if not,
130 read enough bytes to make up the difference. */
132 isc_result_t
omapi_connection_reader (omapi_object_t
*h
)
134 #if defined (TRACING)
135 return omapi_connection_reader_trace (h
, 0, (char *)0, (unsigned *)0);
138 static isc_result_t
omapi_connection_reader_trace (omapi_object_t
*h
,
141 unsigned *stuff_taken
)
144 omapi_buffer_t
*buffer
;
148 omapi_connection_object_t
*c
;
149 unsigned bytes_to_read
;
151 if (!h
|| h
-> type
!= omapi_type_connection
)
152 return DHCP_R_INVALIDARG
;
153 c
= (omapi_connection_object_t
*)h
;
155 /* See if there are enough bytes. */
156 if (c
-> in_bytes
>= OMAPI_BUF_SIZE
- 1 &&
157 c
-> in_bytes
> c
-> bytes_needed
)
158 return ISC_R_SUCCESS
;
162 for (buffer
= c
-> inbufs
; buffer
-> next
;
163 buffer
= buffer
-> next
)
165 if (!BUFFER_BYTES_FREE (buffer
)) {
166 status
= omapi_buffer_new (&buffer
-> next
, MDL
);
167 if (status
!= ISC_R_SUCCESS
)
169 buffer
= buffer
-> next
;
172 status
= omapi_buffer_new (&c
-> inbufs
, MDL
);
173 if (status
!= ISC_R_SUCCESS
)
175 buffer
= c
-> inbufs
;
178 bytes_to_read
= BUFFER_BYTES_FREE (buffer
);
180 while (bytes_to_read
) {
181 if (buffer
-> tail
> buffer
-> head
)
182 read_len
= sizeof (buffer
-> buf
) - buffer
-> tail
;
184 read_len
= buffer
-> head
- buffer
-> tail
;
186 #if defined (TRACING)
187 if (trace_playback()) {
189 if (read_len
> stuff_len
)
190 read_len
= stuff_len
;
192 *stuff_taken
+= read_len
;
193 memcpy (&buffer
-> buf
[buffer
-> tail
],
194 stuff_buf
, read_len
);
195 stuff_len
-= read_len
;
196 stuff_buf
+= read_len
;
197 read_status
= read_len
;
204 read_status
= read (c
-> socket
,
205 &buffer
-> buf
[buffer
-> tail
],
208 if (read_status
< 0) {
209 if (errno
== EWOULDBLOCK
)
211 else if (errno
== EIO
)
212 return ISC_R_IOERROR
;
213 else if (errno
== EINVAL
)
214 return DHCP_R_INVALIDARG
;
215 else if (errno
== ECONNRESET
) {
216 omapi_disconnect (h
, 1);
217 return ISC_R_SHUTTINGDOWN
;
219 return ISC_R_UNEXPECTED
;
222 /* If we got a zero-length read, as opposed to EWOULDBLOCK,
223 the remote end closed the connection. */
224 if (read_status
== 0) {
225 omapi_disconnect (h
, 0);
226 return ISC_R_SHUTTINGDOWN
;
228 #if defined (TRACING)
229 if (trace_record ()) {
231 int32_t connect_index
;
233 connect_index
= htonl (c
-> index
);
235 iov
[0].buf
= (char *)&connect_index
;
236 iov
[0].len
= sizeof connect_index
;
237 iov
[1].buf
= &buffer
-> buf
[buffer
-> tail
];
238 iov
[1].len
= read_status
;
240 status
= (trace_write_packet_iov
241 (trace_connection_input
, 2, iov
, MDL
));
242 if (status
!= ISC_R_SUCCESS
) {
244 log_error ("trace connection input: %s",
245 isc_result_totext (status
));
249 buffer
-> tail
+= read_status
;
250 c
-> in_bytes
+= read_status
;
251 if (buffer
-> tail
== sizeof buffer
-> buf
)
253 if (read_status
< read_len
)
255 bytes_to_read
-= read_status
;
258 if (c
-> bytes_needed
<= c
-> in_bytes
) {
259 omapi_signal (h
, "ready", c
);
261 return ISC_R_SUCCESS
;
264 /* Put some bytes into the output buffer for a connection. */
266 isc_result_t
omapi_connection_copyin (omapi_object_t
*h
,
267 const unsigned char *bufp
,
270 omapi_buffer_t
*buffer
;
272 int bytes_copied
= 0;
274 int sig_flags
= SIG_MODE_UPDATE
;
275 omapi_connection_object_t
*c
;
277 /* no need to verify len as it's unsigned */
278 if (!h
|| h
-> type
!= omapi_type_connection
)
279 return DHCP_R_INVALIDARG
;
280 c
= (omapi_connection_object_t
*)h
;
282 /* If the connection is closed, return an error if the caller
284 if (c
-> state
== omapi_connection_disconnecting
||
285 c
-> state
== omapi_connection_closed
)
286 return ISC_R_NOTCONNECTED
;
289 for (buffer
= c
-> outbufs
;
290 buffer
-> next
; buffer
= buffer
-> next
)
293 status
= omapi_buffer_new (&c
-> outbufs
, MDL
);
294 if (status
!= ISC_R_SUCCESS
)
296 buffer
= c
-> outbufs
;
299 while (bytes_copied
< len
) {
300 /* If there is no space available in this buffer,
301 allocate a new one. */
302 if (!BUFFER_BYTES_FREE (buffer
)) {
303 status
= (omapi_buffer_new (&buffer
-> next
, MDL
));
304 if (status
!= ISC_R_SUCCESS
)
306 buffer
= buffer
-> next
;
309 if (buffer
-> tail
> buffer
-> head
)
310 copy_len
= sizeof (buffer
-> buf
) - buffer
-> tail
;
312 copy_len
= buffer
-> head
- buffer
-> tail
;
314 if (copy_len
> (len
- bytes_copied
))
315 copy_len
= len
- bytes_copied
;
318 if (!c
-> out_context
)
319 sig_flags
|= SIG_MODE_INIT
;
320 status
= omapi_connection_sign_data
321 (sig_flags
, c
-> out_key
, &c
-> out_context
,
322 &bufp
[bytes_copied
], copy_len
,
323 (omapi_typed_data_t
**)0);
324 if (status
!= ISC_R_SUCCESS
)
328 memcpy (&buffer
-> buf
[buffer
-> tail
],
329 &bufp
[bytes_copied
], copy_len
);
330 buffer
-> tail
+= copy_len
;
331 c
-> out_bytes
+= copy_len
;
332 bytes_copied
+= copy_len
;
333 if (buffer
-> tail
== sizeof buffer
-> buf
)
337 status
= ISC_R_SUCCESS
;
341 * If we have any bytes to send and we have a proper io object
342 * inform the socket code that we would like to know when we
343 * can send more bytes.
345 if (c
->out_bytes
!= 0) {
346 if ((c
->outer
!= NULL
) &&
347 (c
->outer
->type
== omapi_type_io_object
)) {
348 omapi_io_object_t
*io
= (omapi_io_object_t
*)c
->outer
;
349 isc_socket_fdwatchpoke(io
->fd
,
350 ISC_SOCKFDWATCH_WRITE
);
357 /* Copy some bytes from the input buffer, and advance the input buffer
358 pointer beyond the bytes copied out. */
360 isc_result_t
omapi_connection_copyout (unsigned char *buf
,
364 unsigned bytes_remaining
;
365 unsigned bytes_this_copy
;
367 omapi_buffer_t
*buffer
;
369 int sig_flags
= SIG_MODE_UPDATE
;
370 omapi_connection_object_t
*c
;
373 if (!h
|| h
-> type
!= omapi_type_connection
)
374 return DHCP_R_INVALIDARG
;
375 c
= (omapi_connection_object_t
*)h
;
377 if (size
> c
-> in_bytes
)
380 bytes_remaining
= size
;
381 buffer
= c
-> inbufs
;
383 while (bytes_remaining
) {
385 return ISC_R_UNEXPECTED
;
386 if (BYTES_IN_BUFFER (buffer
)) {
387 if (buffer
-> head
== (sizeof buffer
-> buf
) - 1)
390 first_byte
= buffer
-> head
+ 1;
392 if (first_byte
> buffer
-> tail
) {
393 bytes_this_copy
= (sizeof buffer
-> buf
-
397 buffer
-> tail
- first_byte
;
399 if (bytes_this_copy
> bytes_remaining
)
400 bytes_this_copy
= bytes_remaining
;
403 if (!c
-> in_context
)
404 sig_flags
|= SIG_MODE_INIT
;
405 status
= omapi_connection_sign_data
410 &buffer
-> buf
[first_byte
],
412 (omapi_typed_data_t
**)0);
413 if (status
!= ISC_R_SUCCESS
)
417 memcpy (bufp
, &buffer
-> buf
[first_byte
],
419 bufp
+= bytes_this_copy
;
421 bytes_remaining
-= bytes_this_copy
;
422 buffer
-> head
= first_byte
+ bytes_this_copy
- 1;
423 c
-> in_bytes
-= bytes_this_copy
;
426 if (!BYTES_IN_BUFFER (buffer
))
427 buffer
= buffer
-> next
;
430 /* Get rid of any input buffers that we emptied. */
431 buffer
= (omapi_buffer_t
*)0;
432 while (c
-> inbufs
&&
433 !BYTES_IN_BUFFER (c
-> inbufs
)) {
434 if (c
-> inbufs
-> next
) {
435 omapi_buffer_reference (&buffer
,
436 c
-> inbufs
-> next
, MDL
);
437 omapi_buffer_dereference (&c
-> inbufs
-> next
, MDL
);
439 omapi_buffer_dereference (&c
-> inbufs
, MDL
);
441 omapi_buffer_reference
442 (&c
-> inbufs
, buffer
, MDL
);
443 omapi_buffer_dereference (&buffer
, MDL
);
446 return ISC_R_SUCCESS
;
449 isc_result_t
omapi_connection_writer (omapi_object_t
*h
)
451 unsigned bytes_this_write
;
454 omapi_buffer_t
*buffer
;
455 omapi_connection_object_t
*c
;
457 if (!h
|| h
-> type
!= omapi_type_connection
)
458 return DHCP_R_INVALIDARG
;
459 c
= (omapi_connection_object_t
*)h
;
461 /* Already flushed... */
463 return ISC_R_SUCCESS
;
465 buffer
= c
-> outbufs
;
467 while (c
-> out_bytes
) {
469 return ISC_R_UNEXPECTED
;
470 if (BYTES_IN_BUFFER (buffer
)) {
471 if (buffer
-> head
== (sizeof buffer
-> buf
) - 1)
474 first_byte
= buffer
-> head
+ 1;
476 if (first_byte
> buffer
-> tail
) {
477 bytes_this_write
= (sizeof buffer
-> buf
-
481 buffer
-> tail
- first_byte
;
483 bytes_written
= write (c
-> socket
,
484 &buffer
-> buf
[first_byte
],
486 /* If the write failed with EWOULDBLOCK or we wrote
487 zero bytes, a further write would block, so we have
488 flushed as much as we can for now. Other errors
489 are really errors. */
490 if (bytes_written
< 0) {
491 if (errno
== EWOULDBLOCK
|| errno
== EAGAIN
)
492 return ISC_R_INPROGRESS
;
493 else if (errno
== EPIPE
)
496 else if (errno
== EFBIG
|| errno
== EDQUOT
)
498 else if (errno
== EFBIG
)
500 return ISC_R_NORESOURCES
;
501 else if (errno
== ENOSPC
)
502 return ISC_R_NOSPACE
;
503 else if (errno
== EIO
)
504 return ISC_R_IOERROR
;
505 else if (errno
== EINVAL
)
506 return DHCP_R_INVALIDARG
;
507 else if (errno
== ECONNRESET
)
508 return ISC_R_SHUTTINGDOWN
;
510 return ISC_R_UNEXPECTED
;
512 if (bytes_written
== 0)
513 return ISC_R_INPROGRESS
;
515 #if defined (TRACING)
516 if (trace_record ()) {
519 int32_t connect_index
;
521 connect_index
= htonl (c
-> index
);
523 iov
[0].buf
= (char *)&connect_index
;
524 iov
[0].len
= sizeof connect_index
;
525 iov
[1].buf
= &buffer
-> buf
[buffer
-> tail
];
526 iov
[1].len
= bytes_written
;
528 status
= (trace_write_packet_iov
529 (trace_connection_input
, 2, iov
,
531 if (status
!= ISC_R_SUCCESS
) {
533 log_error ("trace %s output: %s",
535 isc_result_totext (status
));
540 buffer
-> head
= first_byte
+ bytes_written
- 1;
541 c
-> out_bytes
-= bytes_written
;
543 /* If we didn't finish out the write, we filled the
544 O.S. output buffer and a further write would block,
545 so stop trying to flush now. */
546 if (bytes_written
!= bytes_this_write
)
547 return ISC_R_INPROGRESS
;
550 if (!BYTES_IN_BUFFER (buffer
))
551 buffer
= buffer
-> next
;
554 /* Get rid of any output buffers we emptied. */
555 buffer
= (omapi_buffer_t
*)0;
556 while (c
-> outbufs
&&
557 !BYTES_IN_BUFFER (c
-> outbufs
)) {
558 if (c
-> outbufs
-> next
) {
559 omapi_buffer_reference (&buffer
,
560 c
-> outbufs
-> next
, MDL
);
561 omapi_buffer_dereference (&c
-> outbufs
-> next
, MDL
);
563 omapi_buffer_dereference (&c
-> outbufs
, MDL
);
565 omapi_buffer_reference (&c
-> outbufs
, buffer
, MDL
);
566 omapi_buffer_dereference (&buffer
, MDL
);
569 return ISC_R_SUCCESS
;
572 isc_result_t
omapi_connection_get_uint32 (omapi_object_t
*c
,
578 status
= omapi_connection_copyout ((unsigned char *)&inbuf
,
580 if (status
!= ISC_R_SUCCESS
)
583 *result
= ntohl (inbuf
);
584 return ISC_R_SUCCESS
;
587 isc_result_t
omapi_connection_put_uint32 (omapi_object_t
*c
,
592 inbuf
= htonl (value
);
594 return omapi_connection_copyin (c
, (unsigned char *)&inbuf
,
598 isc_result_t
omapi_connection_get_uint16 (omapi_object_t
*c
,
604 status
= omapi_connection_copyout ((unsigned char *)&inbuf
,
606 if (status
!= ISC_R_SUCCESS
)
609 *result
= ntohs (inbuf
);
610 return ISC_R_SUCCESS
;
613 isc_result_t
omapi_connection_put_uint16 (omapi_object_t
*c
,
618 inbuf
= htons (value
);
620 return omapi_connection_copyin (c
, (unsigned char *)&inbuf
,
624 isc_result_t
omapi_connection_write_typed_data (omapi_object_t
*c
,
625 omapi_typed_data_t
*data
)
628 omapi_handle_t handle
;
630 /* Null data is valid. */
632 return omapi_connection_put_uint32 (c
, 0);
634 switch (data
-> type
) {
635 case omapi_datatype_int
:
636 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
637 if (status
!= ISC_R_SUCCESS
)
639 return omapi_connection_put_uint32 (c
, ((u_int32_t
)
640 (data
-> u
.integer
)));
642 case omapi_datatype_string
:
643 case omapi_datatype_data
:
644 status
= omapi_connection_put_uint32 (c
, data
-> u
.buffer
.len
);
645 if (status
!= ISC_R_SUCCESS
)
647 if (data
-> u
.buffer
.len
)
648 return omapi_connection_copyin
649 (c
, data
-> u
.buffer
.value
,
650 data
-> u
.buffer
.len
);
651 return ISC_R_SUCCESS
;
653 case omapi_datatype_object
:
654 if (data
-> u
.object
) {
655 status
= omapi_object_handle (&handle
,
657 if (status
!= ISC_R_SUCCESS
)
661 status
= omapi_connection_put_uint32 (c
, sizeof handle
);
662 if (status
!= ISC_R_SUCCESS
)
664 return omapi_connection_put_uint32 (c
, handle
);
667 return DHCP_R_INVALIDARG
;
670 isc_result_t
omapi_connection_put_name (omapi_object_t
*c
, const char *name
)
673 unsigned len
= strlen (name
);
675 status
= omapi_connection_put_uint16 (c
, len
);
676 if (status
!= ISC_R_SUCCESS
)
678 return omapi_connection_copyin (c
, (const unsigned char *)name
, len
);
681 isc_result_t
omapi_connection_put_string (omapi_object_t
*c
,
688 len
= strlen (string
);
692 status
= omapi_connection_put_uint32 (c
, len
);
693 if (status
!= ISC_R_SUCCESS
)
696 return omapi_connection_copyin
697 (c
, (const unsigned char *)string
, len
);
698 return ISC_R_SUCCESS
;
701 isc_result_t
omapi_connection_put_handle (omapi_object_t
*c
, omapi_object_t
*h
)
704 omapi_handle_t handle
;
707 status
= omapi_object_handle (&handle
, h
);
708 if (status
!= ISC_R_SUCCESS
)
711 handle
= 0; /* The null handle. */
712 status
= omapi_connection_put_uint32 (c
, sizeof handle
);
713 if (status
!= ISC_R_SUCCESS
)
715 return omapi_connection_put_uint32 (c
, handle
);
718 isc_result_t
omapi_connection_put_named_uint32 (omapi_object_t
*c
,
724 status
= omapi_connection_put_name(c
, name
);
725 if (status
!= ISC_R_SUCCESS
)
728 status
= omapi_connection_put_uint32(c
, sizeof(u_int32_t
));
729 if (status
!= ISC_R_SUCCESS
)
732 status
= omapi_connection_put_uint32(c
, value
);