1 /* $NetBSD: buffer.c,v 1.1.1.3 2014/07/12 11:57:58 spz Exp $ */
4 Buffer access functions for the object management protocol... */
7 * Copyright (c) 2009,2012-2014 by Internet Systems Consortium, Inc. ("ISC")
8 * Copyright (c) 2004,2005,2007 by Internet Systems Consortium, Inc. ("ISC")
9 * Copyright (c) 1999-2003 by Internet Software Consortium
11 * Permission to use, copy, modify, and distribute this software for any
12 * purpose with or without fee is hereby granted, provided that the above
13 * copyright notice and this permission notice appear in all copies.
15 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
16 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
17 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
18 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
20 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
21 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23 * Internet Systems Consortium, Inc.
25 * Redwood City, CA 94063
27 * https://www.isc.org/
31 #include <sys/cdefs.h>
32 __RCSID("$NetBSD: buffer.c,v 1.1.1.3 2014/07/12 11:57:58 spz Exp $");
36 #include <omapip/omapip_p.h>
40 static void trace_connection_input_input (trace_type_t
*, unsigned, char *);
41 static void trace_connection_input_stop (trace_type_t
*);
42 static void trace_connection_output_input (trace_type_t
*, unsigned, char *);
43 static void trace_connection_output_stop (trace_type_t
*);
44 static trace_type_t
*trace_connection_input
;
45 static trace_type_t
*trace_connection_output
;
46 static isc_result_t
omapi_connection_reader_trace (omapi_object_t
*,
49 extern omapi_array_t
*omapi_connections
;
51 void omapi_buffer_trace_setup ()
53 trace_connection_input
=
54 trace_type_register ("connection-input",
56 trace_connection_input_input
,
57 trace_connection_input_stop
, MDL
);
58 trace_connection_output
=
59 trace_type_register ("connection-output",
61 trace_connection_output_input
,
62 trace_connection_output_stop
, MDL
);
65 static void trace_connection_input_input (trace_type_t
*ttype
,
66 unsigned length
, char *buf
)
68 unsigned left
, taken
, cc
= 0;
70 int32_t connect_index
;
72 omapi_connection_object_t
*c
= (omapi_connection_object_t
*)0;
74 memcpy (&connect_index
, buf
, sizeof connect_index
);
75 connect_index
= ntohl (connect_index
);
77 omapi_array_foreach_begin (omapi_connections
,
78 omapi_connection_object_t
, lp
) {
79 if (lp
-> index
== ntohl (connect_index
)) {
80 omapi_connection_reference (&c
, lp
, MDL
);
81 omapi_connection_dereference (&lp
, MDL
);
84 } omapi_array_foreach_end (omapi_connections
,
85 omapi_connection_object_t
, lp
);
88 log_error ("trace connection input: no connection index %ld",
89 (long int)connect_index
);
93 s
= buf
+ sizeof connect_index
;
94 left
= length
- sizeof connect_index
;
98 status
= omapi_connection_reader_trace ((omapi_object_t
*)c
,
100 if (status
!= ISC_R_SUCCESS
) {
101 log_error ("trace connection input: %s",
102 isc_result_totext (status
));
107 log_error ("trace connection_input: %s",
108 "input is not being consumed.");
117 omapi_connection_dereference (&c
, MDL
);
120 static void trace_connection_input_stop (trace_type_t
*ttype
) { }
122 static void trace_connection_output_input (trace_type_t
*ttype
,
123 unsigned length
, char *buf
)
125 /* We *could* check to see if the output is correct, but for now
126 we aren't going to do that. */
129 static void trace_connection_output_stop (trace_type_t
*ttype
) { }
133 /* Make sure that at least len bytes are in the input buffer, and if not,
134 read enough bytes to make up the difference. */
136 isc_result_t
omapi_connection_reader (omapi_object_t
*h
)
138 #if defined (TRACING)
139 return omapi_connection_reader_trace (h
, 0, (char *)0, (unsigned *)0);
142 static isc_result_t
omapi_connection_reader_trace (omapi_object_t
*h
,
145 unsigned *stuff_taken
)
148 omapi_buffer_t
*buffer
;
152 omapi_connection_object_t
*c
;
153 unsigned bytes_to_read
;
155 if (!h
|| h
-> type
!= omapi_type_connection
)
156 return DHCP_R_INVALIDARG
;
157 c
= (omapi_connection_object_t
*)h
;
159 /* See if there are enough bytes. */
160 if (c
-> in_bytes
>= OMAPI_BUF_SIZE
- 1 &&
161 c
-> in_bytes
> c
-> bytes_needed
)
162 return ISC_R_SUCCESS
;
166 for (buffer
= c
-> inbufs
; buffer
-> next
;
167 buffer
= buffer
-> next
)
169 if (!BUFFER_BYTES_FREE (buffer
)) {
170 status
= omapi_buffer_new (&buffer
-> next
, MDL
);
171 if (status
!= ISC_R_SUCCESS
)
173 buffer
= buffer
-> next
;
176 status
= omapi_buffer_new (&c
-> inbufs
, MDL
);
177 if (status
!= ISC_R_SUCCESS
)
179 buffer
= c
-> inbufs
;
182 bytes_to_read
= BUFFER_BYTES_FREE (buffer
);
184 while (bytes_to_read
) {
185 if (buffer
-> tail
> buffer
-> head
)
186 read_len
= sizeof (buffer
-> buf
) - buffer
-> tail
;
188 read_len
= buffer
-> head
- buffer
-> tail
;
190 #if defined (TRACING)
191 if (trace_playback()) {
193 if (read_len
> stuff_len
)
194 read_len
= stuff_len
;
196 *stuff_taken
+= read_len
;
197 memcpy (&buffer
-> buf
[buffer
-> tail
],
198 stuff_buf
, read_len
);
199 stuff_len
-= read_len
;
200 stuff_buf
+= read_len
;
201 read_status
= read_len
;
208 read_status
= read (c
-> socket
,
209 &buffer
-> buf
[buffer
-> tail
],
212 if (read_status
< 0) {
213 if (errno
== EWOULDBLOCK
)
215 else if (errno
== EIO
)
216 return ISC_R_IOERROR
;
217 else if (errno
== EINVAL
)
218 return DHCP_R_INVALIDARG
;
219 else if (errno
== ECONNRESET
) {
220 omapi_disconnect (h
, 1);
221 return ISC_R_SHUTTINGDOWN
;
223 return ISC_R_UNEXPECTED
;
226 /* If we got a zero-length read, as opposed to EWOULDBLOCK,
227 the remote end closed the connection. */
228 if (read_status
== 0) {
229 omapi_disconnect (h
, 0);
230 return ISC_R_SHUTTINGDOWN
;
232 #if defined (TRACING)
233 if (trace_record ()) {
235 int32_t connect_index
;
237 connect_index
= htonl (c
-> index
);
239 iov
[0].buf
= (char *)&connect_index
;
240 iov
[0].len
= sizeof connect_index
;
241 iov
[1].buf
= &buffer
-> buf
[buffer
-> tail
];
242 iov
[1].len
= read_status
;
244 status
= (trace_write_packet_iov
245 (trace_connection_input
, 2, iov
, MDL
));
246 if (status
!= ISC_R_SUCCESS
) {
248 log_error ("trace connection input: %s",
249 isc_result_totext (status
));
253 buffer
-> tail
+= read_status
;
254 c
-> in_bytes
+= read_status
;
255 if (buffer
-> tail
== sizeof buffer
-> buf
)
257 if (read_status
< read_len
)
259 bytes_to_read
-= read_status
;
262 if (c
-> bytes_needed
<= c
-> in_bytes
) {
263 omapi_signal (h
, "ready", c
);
265 return ISC_R_SUCCESS
;
268 /* Put some bytes into the output buffer for a connection. */
270 isc_result_t
omapi_connection_copyin (omapi_object_t
*h
,
271 const unsigned char *bufp
,
274 omapi_buffer_t
*buffer
;
276 int bytes_copied
= 0;
278 int sig_flags
= SIG_MODE_UPDATE
;
279 omapi_connection_object_t
*c
;
281 /* no need to verify len as it's unsigned */
282 if (!h
|| h
-> type
!= omapi_type_connection
)
283 return DHCP_R_INVALIDARG
;
284 c
= (omapi_connection_object_t
*)h
;
286 /* If the connection is closed, return an error if the caller
288 if (c
-> state
== omapi_connection_disconnecting
||
289 c
-> state
== omapi_connection_closed
)
290 return ISC_R_NOTCONNECTED
;
293 for (buffer
= c
-> outbufs
;
294 buffer
-> next
; buffer
= buffer
-> next
)
297 status
= omapi_buffer_new (&c
-> outbufs
, MDL
);
298 if (status
!= ISC_R_SUCCESS
)
300 buffer
= c
-> outbufs
;
303 while (bytes_copied
< len
) {
304 /* If there is no space available in this buffer,
305 allocate a new one. */
306 if (!BUFFER_BYTES_FREE (buffer
)) {
307 status
= (omapi_buffer_new (&buffer
-> next
, MDL
));
308 if (status
!= ISC_R_SUCCESS
)
310 buffer
= buffer
-> next
;
313 if (buffer
-> tail
> buffer
-> head
)
314 copy_len
= sizeof (buffer
-> buf
) - buffer
-> tail
;
316 copy_len
= buffer
-> head
- buffer
-> tail
;
318 if (copy_len
> (len
- bytes_copied
))
319 copy_len
= len
- bytes_copied
;
322 if (!c
-> out_context
)
323 sig_flags
|= SIG_MODE_INIT
;
324 status
= omapi_connection_sign_data
325 (sig_flags
, c
-> out_key
, &c
-> out_context
,
326 &bufp
[bytes_copied
], copy_len
,
327 (omapi_typed_data_t
**)0);
328 if (status
!= ISC_R_SUCCESS
)
332 memcpy (&buffer
-> buf
[buffer
-> tail
],
333 &bufp
[bytes_copied
], copy_len
);
334 buffer
-> tail
+= copy_len
;
335 c
-> out_bytes
+= copy_len
;
336 bytes_copied
+= copy_len
;
337 if (buffer
-> tail
== sizeof buffer
-> buf
)
341 status
= ISC_R_SUCCESS
;
345 * If we have any bytes to send and we have a proper io object
346 * inform the socket code that we would like to know when we
347 * can send more bytes.
349 if (c
->out_bytes
!= 0) {
350 if ((c
->outer
!= NULL
) &&
351 (c
->outer
->type
== omapi_type_io_object
)) {
352 omapi_io_object_t
*io
= (omapi_io_object_t
*)c
->outer
;
353 isc_socket_fdwatchpoke(io
->fd
,
354 ISC_SOCKFDWATCH_WRITE
);
361 /* Copy some bytes from the input buffer, and advance the input buffer
362 pointer beyond the bytes copied out. */
364 isc_result_t
omapi_connection_copyout (unsigned char *buf
,
368 unsigned bytes_remaining
;
369 unsigned bytes_this_copy
;
371 omapi_buffer_t
*buffer
;
373 int sig_flags
= SIG_MODE_UPDATE
;
374 omapi_connection_object_t
*c
;
377 if (!h
|| h
-> type
!= omapi_type_connection
)
378 return DHCP_R_INVALIDARG
;
379 c
= (omapi_connection_object_t
*)h
;
381 if (size
> c
-> in_bytes
)
384 bytes_remaining
= size
;
385 buffer
= c
-> inbufs
;
387 while (bytes_remaining
) {
389 return ISC_R_UNEXPECTED
;
390 if (BYTES_IN_BUFFER (buffer
)) {
391 if (buffer
-> head
== (sizeof buffer
-> buf
) - 1)
394 first_byte
= buffer
-> head
+ 1;
396 if (first_byte
> buffer
-> tail
) {
397 bytes_this_copy
= (sizeof buffer
-> buf
-
401 buffer
-> tail
- first_byte
;
403 if (bytes_this_copy
> bytes_remaining
)
404 bytes_this_copy
= bytes_remaining
;
407 if (!c
-> in_context
)
408 sig_flags
|= SIG_MODE_INIT
;
409 status
= omapi_connection_sign_data
414 &buffer
-> buf
[first_byte
],
416 (omapi_typed_data_t
**)0);
417 if (status
!= ISC_R_SUCCESS
)
421 memcpy (bufp
, &buffer
-> buf
[first_byte
],
423 bufp
+= bytes_this_copy
;
425 bytes_remaining
-= bytes_this_copy
;
426 buffer
-> head
= first_byte
+ bytes_this_copy
- 1;
427 c
-> in_bytes
-= bytes_this_copy
;
430 if (!BYTES_IN_BUFFER (buffer
))
431 buffer
= buffer
-> next
;
434 /* Get rid of any input buffers that we emptied. */
435 buffer
= (omapi_buffer_t
*)0;
436 while (c
-> inbufs
&&
437 !BYTES_IN_BUFFER (c
-> inbufs
)) {
438 if (c
-> inbufs
-> next
) {
439 omapi_buffer_reference (&buffer
,
440 c
-> inbufs
-> next
, MDL
);
441 omapi_buffer_dereference (&c
-> inbufs
-> next
, MDL
);
443 omapi_buffer_dereference (&c
-> inbufs
, MDL
);
445 omapi_buffer_reference
446 (&c
-> inbufs
, buffer
, MDL
);
447 omapi_buffer_dereference (&buffer
, MDL
);
450 return ISC_R_SUCCESS
;
453 isc_result_t
omapi_connection_writer (omapi_object_t
*h
)
455 unsigned bytes_this_write
;
458 omapi_buffer_t
*buffer
;
459 omapi_connection_object_t
*c
;
461 if (!h
|| h
-> type
!= omapi_type_connection
)
462 return DHCP_R_INVALIDARG
;
463 c
= (omapi_connection_object_t
*)h
;
465 /* Already flushed... */
467 return ISC_R_SUCCESS
;
469 buffer
= c
-> outbufs
;
471 while (c
-> out_bytes
) {
473 return ISC_R_UNEXPECTED
;
474 if (BYTES_IN_BUFFER (buffer
)) {
475 if (buffer
-> head
== (sizeof buffer
-> buf
) - 1)
478 first_byte
= buffer
-> head
+ 1;
480 if (first_byte
> buffer
-> tail
) {
481 bytes_this_write
= (sizeof buffer
-> buf
-
485 buffer
-> tail
- first_byte
;
487 bytes_written
= write (c
-> socket
,
488 &buffer
-> buf
[first_byte
],
490 /* If the write failed with EWOULDBLOCK or we wrote
491 zero bytes, a further write would block, so we have
492 flushed as much as we can for now. Other errors
493 are really errors. */
494 if (bytes_written
< 0) {
495 if (errno
== EWOULDBLOCK
|| errno
== EAGAIN
)
496 return ISC_R_INPROGRESS
;
497 else if (errno
== EPIPE
)
500 else if (errno
== EFBIG
|| errno
== EDQUOT
)
502 else if (errno
== EFBIG
)
504 return ISC_R_NORESOURCES
;
505 else if (errno
== ENOSPC
)
506 return ISC_R_NOSPACE
;
507 else if (errno
== EIO
)
508 return ISC_R_IOERROR
;
509 else if (errno
== EINVAL
)
510 return DHCP_R_INVALIDARG
;
511 else if (errno
== ECONNRESET
)
512 return ISC_R_SHUTTINGDOWN
;
514 return ISC_R_UNEXPECTED
;
516 if (bytes_written
== 0)
517 return ISC_R_INPROGRESS
;
519 #if defined (TRACING)
520 if (trace_record ()) {
523 int32_t connect_index
;
525 connect_index
= htonl (c
-> index
);
527 iov
[0].buf
= (char *)&connect_index
;
528 iov
[0].len
= sizeof connect_index
;
529 iov
[1].buf
= &buffer
-> buf
[buffer
-> tail
];
530 iov
[1].len
= bytes_written
;
532 status
= (trace_write_packet_iov
533 (trace_connection_input
, 2, iov
,
535 if (status
!= ISC_R_SUCCESS
) {
537 log_error ("trace %s output: %s",
539 isc_result_totext (status
));
544 buffer
-> head
= first_byte
+ bytes_written
- 1;
545 c
-> out_bytes
-= bytes_written
;
547 /* If we didn't finish out the write, we filled the
548 O.S. output buffer and a further write would block,
549 so stop trying to flush now. */
550 if (bytes_written
!= bytes_this_write
)
551 return ISC_R_INPROGRESS
;
554 if (!BYTES_IN_BUFFER (buffer
))
555 buffer
= buffer
-> next
;
558 /* Get rid of any output buffers we emptied. */
559 buffer
= (omapi_buffer_t
*)0;
560 while (c
-> outbufs
&&
561 !BYTES_IN_BUFFER (c
-> outbufs
)) {
562 if (c
-> outbufs
-> next
) {
563 omapi_buffer_reference (&buffer
,
564 c
-> outbufs
-> next
, MDL
);
565 omapi_buffer_dereference (&c
-> outbufs
-> next
, MDL
);
567 omapi_buffer_dereference (&c
-> outbufs
, MDL
);
569 omapi_buffer_reference (&c
-> outbufs
, buffer
, MDL
);
570 omapi_buffer_dereference (&buffer
, MDL
);
573 return ISC_R_SUCCESS
;
576 isc_result_t
omapi_connection_get_uint32 (omapi_object_t
*c
,
582 status
= omapi_connection_copyout ((unsigned char *)&inbuf
,
584 if (status
!= ISC_R_SUCCESS
)
587 *result
= ntohl (inbuf
);
588 return ISC_R_SUCCESS
;
591 isc_result_t
omapi_connection_put_uint32 (omapi_object_t
*c
,
596 inbuf
= htonl (value
);
598 return omapi_connection_copyin (c
, (unsigned char *)&inbuf
,
602 isc_result_t
omapi_connection_get_uint16 (omapi_object_t
*c
,
608 status
= omapi_connection_copyout ((unsigned char *)&inbuf
,
610 if (status
!= ISC_R_SUCCESS
)
613 *result
= ntohs (inbuf
);
614 return ISC_R_SUCCESS
;
617 isc_result_t
omapi_connection_put_uint16 (omapi_object_t
*c
,
622 inbuf
= htons (value
);
624 return omapi_connection_copyin (c
, (unsigned char *)&inbuf
,
628 isc_result_t
omapi_connection_write_typed_data (omapi_object_t
*c
,
629 omapi_typed_data_t
*data
)
632 omapi_handle_t handle
;
634 /* Null data is valid. */
636 return omapi_connection_put_uint32 (c
, 0);
638 switch (data
-> type
) {
639 case omapi_datatype_int
:
640 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
641 if (status
!= ISC_R_SUCCESS
)
643 return omapi_connection_put_uint32 (c
, ((u_int32_t
)
644 (data
-> u
.integer
)));
646 case omapi_datatype_string
:
647 case omapi_datatype_data
:
648 status
= omapi_connection_put_uint32 (c
, data
-> u
.buffer
.len
);
649 if (status
!= ISC_R_SUCCESS
)
651 if (data
-> u
.buffer
.len
)
652 return omapi_connection_copyin
653 (c
, data
-> u
.buffer
.value
,
654 data
-> u
.buffer
.len
);
655 return ISC_R_SUCCESS
;
657 case omapi_datatype_object
:
658 if (data
-> u
.object
) {
659 status
= omapi_object_handle (&handle
,
661 if (status
!= ISC_R_SUCCESS
)
665 status
= omapi_connection_put_uint32 (c
, sizeof handle
);
666 if (status
!= ISC_R_SUCCESS
)
668 return omapi_connection_put_uint32 (c
, handle
);
671 return DHCP_R_INVALIDARG
;
674 isc_result_t
omapi_connection_put_name (omapi_object_t
*c
, const char *name
)
677 unsigned len
= strlen (name
);
679 status
= omapi_connection_put_uint16 (c
, len
);
680 if (status
!= ISC_R_SUCCESS
)
682 return omapi_connection_copyin (c
, (const unsigned char *)name
, len
);
685 isc_result_t
omapi_connection_put_string (omapi_object_t
*c
,
692 len
= strlen (string
);
696 status
= omapi_connection_put_uint32 (c
, len
);
697 if (status
!= ISC_R_SUCCESS
)
700 return omapi_connection_copyin
701 (c
, (const unsigned char *)string
, len
);
702 return ISC_R_SUCCESS
;
705 isc_result_t
omapi_connection_put_handle (omapi_object_t
*c
, omapi_object_t
*h
)
708 omapi_handle_t handle
;
711 status
= omapi_object_handle (&handle
, h
);
712 if (status
!= ISC_R_SUCCESS
)
715 handle
= 0; /* The null handle. */
716 status
= omapi_connection_put_uint32 (c
, sizeof handle
);
717 if (status
!= ISC_R_SUCCESS
)
719 return omapi_connection_put_uint32 (c
, handle
);
722 isc_result_t
omapi_connection_put_named_uint32 (omapi_object_t
*c
,
728 status
= omapi_connection_put_name(c
, name
);
729 if (status
!= ISC_R_SUCCESS
)
732 status
= omapi_connection_put_uint32(c
, sizeof(u_int32_t
));
733 if (status
!= ISC_R_SUCCESS
)
736 status
= omapi_connection_put_uint32(c
, value
);