3 Buffer access functions for the object management protocol... */
6 * Copyright (c) 2004,2005,2007 by Internet Systems Consortium, Inc. ("ISC")
7 * Copyright (c) 1999-2003 by Internet Software Consortium
9 * Permission to use, copy, modify, and distribute this software for any
10 * purpose with or without fee is hereby granted, provided that the above
11 * copyright notice and this permission notice appear in all copies.
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 * Redwood City, CA 94063
25 * https://www.isc.org/
27 * This software has been written for Internet Systems Consortium
28 * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
29 * To learn more about Internet Systems Consortium, see
30 * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
31 * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
32 * ``http://www.nominum.com''.
37 #include <omapip/omapip_p.h>
41 static void trace_connection_input_input (trace_type_t
*, unsigned, char *);
42 static void trace_connection_input_stop (trace_type_t
*);
43 static void trace_connection_output_input (trace_type_t
*, unsigned, char *);
44 static void trace_connection_output_stop (trace_type_t
*);
45 static trace_type_t
*trace_connection_input
;
46 static trace_type_t
*trace_connection_output
;
47 static isc_result_t
omapi_connection_reader_trace (omapi_object_t
*,
50 extern omapi_array_t
*omapi_connections
;
52 void omapi_buffer_trace_setup ()
54 trace_connection_input
=
55 trace_type_register ("connection-input",
57 trace_connection_input_input
,
58 trace_connection_input_stop
, MDL
);
59 trace_connection_output
=
60 trace_type_register ("connection-output",
62 trace_connection_output_input
,
63 trace_connection_output_stop
, MDL
);
66 static void trace_connection_input_input (trace_type_t
*ttype
,
67 unsigned length
, char *buf
)
69 unsigned left
, taken
, cc
= 0;
71 int32_t connect_index
;
73 omapi_connection_object_t
*c
= (omapi_connection_object_t
*)0;
75 memcpy (&connect_index
, buf
, sizeof connect_index
);
76 connect_index
= ntohl (connect_index
);
78 omapi_array_foreach_begin (omapi_connections
,
79 omapi_connection_object_t
, lp
) {
80 if (lp
-> index
== ntohl (connect_index
)) {
81 omapi_connection_reference (&c
, lp
, MDL
);
82 omapi_connection_dereference (&lp
, MDL
);
85 } omapi_array_foreach_end (omapi_connections
,
86 omapi_connection_object_t
, lp
);
89 log_error ("trace connection input: no connection index %ld",
90 (long int)connect_index
);
94 s
= buf
+ sizeof connect_index
;
95 left
= length
- sizeof connect_index
;
99 status
= omapi_connection_reader_trace ((omapi_object_t
*)c
,
101 if (status
!= ISC_R_SUCCESS
) {
102 log_error ("trace connection input: %s",
103 isc_result_totext (status
));
108 log_error ("trace connection_input: %s",
109 "input is not being consumed.");
118 omapi_connection_dereference (&c
, MDL
);
121 static void trace_connection_input_stop (trace_type_t
*ttype
) { }
123 static void trace_connection_output_input (trace_type_t
*ttype
,
124 unsigned length
, char *buf
)
126 /* We *could* check to see if the output is correct, but for now
127 we aren't going to do that. */
130 static void trace_connection_output_stop (trace_type_t
*ttype
) { }
134 /* Make sure that at least len bytes are in the input buffer, and if not,
135 read enough bytes to make up the difference. */
137 isc_result_t
omapi_connection_reader (omapi_object_t
*h
)
139 #if defined (TRACING)
140 return omapi_connection_reader_trace (h
, 0, (char *)0, (unsigned *)0);
143 static isc_result_t
omapi_connection_reader_trace (omapi_object_t
*h
,
146 unsigned *stuff_taken
)
149 omapi_buffer_t
*buffer
;
153 omapi_connection_object_t
*c
;
154 unsigned bytes_to_read
;
156 if (!h
|| h
-> type
!= omapi_type_connection
)
157 return DHCP_R_INVALIDARG
;
158 c
= (omapi_connection_object_t
*)h
;
160 /* See if there are enough bytes. */
161 if (c
-> in_bytes
>= OMAPI_BUF_SIZE
- 1 &&
162 c
-> in_bytes
> c
-> bytes_needed
)
163 return ISC_R_SUCCESS
;
167 for (buffer
= c
-> inbufs
; buffer
-> next
;
168 buffer
= buffer
-> next
)
170 if (!BUFFER_BYTES_FREE (buffer
)) {
171 status
= omapi_buffer_new (&buffer
-> next
, MDL
);
172 if (status
!= ISC_R_SUCCESS
)
174 buffer
= buffer
-> next
;
177 status
= omapi_buffer_new (&c
-> inbufs
, MDL
);
178 if (status
!= ISC_R_SUCCESS
)
180 buffer
= c
-> inbufs
;
183 bytes_to_read
= BUFFER_BYTES_FREE (buffer
);
185 while (bytes_to_read
) {
186 if (buffer
-> tail
> buffer
-> head
)
187 read_len
= sizeof (buffer
-> buf
) - buffer
-> tail
;
189 read_len
= buffer
-> head
- buffer
-> tail
;
191 #if defined (TRACING)
192 if (trace_playback()) {
194 if (read_len
> stuff_len
)
195 read_len
= stuff_len
;
197 *stuff_taken
+= read_len
;
198 memcpy (&buffer
-> buf
[buffer
-> tail
],
199 stuff_buf
, read_len
);
200 stuff_len
-= read_len
;
201 stuff_buf
+= read_len
;
202 read_status
= read_len
;
209 read_status
= read (c
-> socket
,
210 &buffer
-> buf
[buffer
-> tail
],
213 if (read_status
< 0) {
214 if (errno
== EWOULDBLOCK
)
216 else if (errno
== EIO
)
217 return ISC_R_IOERROR
;
218 else if (errno
== EINVAL
)
219 return DHCP_R_INVALIDARG
;
220 else if (errno
== ECONNRESET
) {
221 omapi_disconnect (h
, 1);
222 return ISC_R_SHUTTINGDOWN
;
224 return ISC_R_UNEXPECTED
;
227 /* If we got a zero-length read, as opposed to EWOULDBLOCK,
228 the remote end closed the connection. */
229 if (read_status
== 0) {
230 omapi_disconnect (h
, 0);
231 return ISC_R_SHUTTINGDOWN
;
233 #if defined (TRACING)
234 if (trace_record ()) {
236 int32_t connect_index
;
238 connect_index
= htonl (c
-> index
);
240 iov
[0].buf
= (char *)&connect_index
;
241 iov
[0].len
= sizeof connect_index
;
242 iov
[1].buf
= &buffer
-> buf
[buffer
-> tail
];
243 iov
[1].len
= read_status
;
245 status
= (trace_write_packet_iov
246 (trace_connection_input
, 2, iov
, MDL
));
247 if (status
!= ISC_R_SUCCESS
) {
249 log_error ("trace connection input: %s",
250 isc_result_totext (status
));
254 buffer
-> tail
+= read_status
;
255 c
-> in_bytes
+= read_status
;
256 if (buffer
-> tail
== sizeof buffer
-> buf
)
258 if (read_status
< read_len
)
260 bytes_to_read
-= read_status
;
263 if (c
-> bytes_needed
<= c
-> in_bytes
) {
264 omapi_signal (h
, "ready", c
);
266 return ISC_R_SUCCESS
;
269 /* Put some bytes into the output buffer for a connection. */
271 isc_result_t
omapi_connection_copyin (omapi_object_t
*h
,
272 const unsigned char *bufp
,
275 omapi_buffer_t
*buffer
;
277 int bytes_copied
= 0;
279 int sig_flags
= SIG_MODE_UPDATE
;
280 omapi_connection_object_t
*c
;
282 /* Make sure len is valid. */
284 return DHCP_R_INVALIDARG
;
285 if (!h
|| h
-> type
!= omapi_type_connection
)
286 return DHCP_R_INVALIDARG
;
287 c
= (omapi_connection_object_t
*)h
;
289 /* If the connection is closed, return an error if the caller
291 if (c
-> state
== omapi_connection_disconnecting
||
292 c
-> state
== omapi_connection_closed
)
293 return ISC_R_NOTCONNECTED
;
296 for (buffer
= c
-> outbufs
;
297 buffer
-> next
; buffer
= buffer
-> next
)
300 status
= omapi_buffer_new (&c
-> outbufs
, MDL
);
301 if (status
!= ISC_R_SUCCESS
)
303 buffer
= c
-> outbufs
;
306 while (bytes_copied
< len
) {
307 /* If there is no space available in this buffer,
308 allocate a new one. */
309 if (!BUFFER_BYTES_FREE (buffer
)) {
310 status
= (omapi_buffer_new (&buffer
-> next
, MDL
));
311 if (status
!= ISC_R_SUCCESS
)
313 buffer
= buffer
-> next
;
316 if (buffer
-> tail
> buffer
-> head
)
317 copy_len
= sizeof (buffer
-> buf
) - buffer
-> tail
;
319 copy_len
= buffer
-> head
- buffer
-> tail
;
321 if (copy_len
> (len
- bytes_copied
))
322 copy_len
= len
- bytes_copied
;
325 if (!c
-> out_context
)
326 sig_flags
|= SIG_MODE_INIT
;
327 status
= omapi_connection_sign_data
328 (sig_flags
, c
-> out_key
, &c
-> out_context
,
329 &bufp
[bytes_copied
], copy_len
,
330 (omapi_typed_data_t
**)0);
331 if (status
!= ISC_R_SUCCESS
)
335 memcpy (&buffer
-> buf
[buffer
-> tail
],
336 &bufp
[bytes_copied
], copy_len
);
337 buffer
-> tail
+= copy_len
;
338 c
-> out_bytes
+= copy_len
;
339 bytes_copied
+= copy_len
;
340 if (buffer
-> tail
== sizeof buffer
-> buf
)
344 status
= ISC_R_SUCCESS
;
348 * If we have any bytes to send and we have a proper io object
349 * inform the socket code that we would like to know when we
350 * can send more bytes.
352 if (c
->out_bytes
!= 0) {
353 if ((c
->outer
!= NULL
) &&
354 (c
->outer
->type
== omapi_type_io_object
)) {
355 omapi_io_object_t
*io
= (omapi_io_object_t
*)c
->outer
;
356 isc_socket_fdwatchpoke(io
->fd
,
357 ISC_SOCKFDWATCH_WRITE
);
364 /* Copy some bytes from the input buffer, and advance the input buffer
365 pointer beyond the bytes copied out. */
367 isc_result_t
omapi_connection_copyout (unsigned char *buf
,
371 unsigned bytes_remaining
;
372 unsigned bytes_this_copy
;
374 omapi_buffer_t
*buffer
;
376 int sig_flags
= SIG_MODE_UPDATE
;
377 omapi_connection_object_t
*c
;
380 if (!h
|| h
-> type
!= omapi_type_connection
)
381 return DHCP_R_INVALIDARG
;
382 c
= (omapi_connection_object_t
*)h
;
384 if (size
> c
-> in_bytes
)
387 bytes_remaining
= size
;
388 buffer
= c
-> inbufs
;
390 while (bytes_remaining
) {
392 return ISC_R_UNEXPECTED
;
393 if (BYTES_IN_BUFFER (buffer
)) {
394 if (buffer
-> head
== (sizeof buffer
-> buf
) - 1)
397 first_byte
= buffer
-> head
+ 1;
399 if (first_byte
> buffer
-> tail
) {
400 bytes_this_copy
= (sizeof buffer
-> buf
-
404 buffer
-> tail
- first_byte
;
406 if (bytes_this_copy
> bytes_remaining
)
407 bytes_this_copy
= bytes_remaining
;
410 if (!c
-> in_context
)
411 sig_flags
|= SIG_MODE_INIT
;
412 status
= omapi_connection_sign_data
417 &buffer
-> buf
[first_byte
],
419 (omapi_typed_data_t
**)0);
420 if (status
!= ISC_R_SUCCESS
)
424 memcpy (bufp
, &buffer
-> buf
[first_byte
],
426 bufp
+= bytes_this_copy
;
428 bytes_remaining
-= bytes_this_copy
;
429 buffer
-> head
= first_byte
+ bytes_this_copy
- 1;
430 c
-> in_bytes
-= bytes_this_copy
;
433 if (!BYTES_IN_BUFFER (buffer
))
434 buffer
= buffer
-> next
;
437 /* Get rid of any input buffers that we emptied. */
438 buffer
= (omapi_buffer_t
*)0;
439 while (c
-> inbufs
&&
440 !BYTES_IN_BUFFER (c
-> inbufs
)) {
441 if (c
-> inbufs
-> next
) {
442 omapi_buffer_reference (&buffer
,
443 c
-> inbufs
-> next
, MDL
);
444 omapi_buffer_dereference (&c
-> inbufs
-> next
, MDL
);
446 omapi_buffer_dereference (&c
-> inbufs
, MDL
);
448 omapi_buffer_reference
449 (&c
-> inbufs
, buffer
, MDL
);
450 omapi_buffer_dereference (&buffer
, MDL
);
453 return ISC_R_SUCCESS
;
456 isc_result_t
omapi_connection_writer (omapi_object_t
*h
)
458 unsigned bytes_this_write
;
461 omapi_buffer_t
*buffer
;
462 omapi_connection_object_t
*c
;
464 if (!h
|| h
-> type
!= omapi_type_connection
)
465 return DHCP_R_INVALIDARG
;
466 c
= (omapi_connection_object_t
*)h
;
468 /* Already flushed... */
470 return ISC_R_SUCCESS
;
472 buffer
= c
-> outbufs
;
474 while (c
-> out_bytes
) {
476 return ISC_R_UNEXPECTED
;
477 if (BYTES_IN_BUFFER (buffer
)) {
478 if (buffer
-> head
== (sizeof buffer
-> buf
) - 1)
481 first_byte
= buffer
-> head
+ 1;
483 if (first_byte
> buffer
-> tail
) {
484 bytes_this_write
= (sizeof buffer
-> buf
-
488 buffer
-> tail
- first_byte
;
490 bytes_written
= write (c
-> socket
,
491 &buffer
-> buf
[first_byte
],
493 /* If the write failed with EWOULDBLOCK or we wrote
494 zero bytes, a further write would block, so we have
495 flushed as much as we can for now. Other errors
496 are really errors. */
497 if (bytes_written
< 0) {
498 if (errno
== EWOULDBLOCK
|| errno
== EAGAIN
)
499 return ISC_R_INPROGRESS
;
500 else if (errno
== EPIPE
)
503 else if (errno
== EFBIG
|| errno
== EDQUOT
)
505 else if (errno
== EFBIG
)
507 return ISC_R_NORESOURCES
;
508 else if (errno
== ENOSPC
)
509 return ISC_R_NOSPACE
;
510 else if (errno
== EIO
)
511 return ISC_R_IOERROR
;
512 else if (errno
== EINVAL
)
513 return DHCP_R_INVALIDARG
;
514 else if (errno
== ECONNRESET
)
515 return ISC_R_SHUTTINGDOWN
;
517 return ISC_R_UNEXPECTED
;
519 if (bytes_written
== 0)
520 return ISC_R_INPROGRESS
;
522 #if defined (TRACING)
523 if (trace_record ()) {
526 int32_t connect_index
;
528 connect_index
= htonl (c
-> index
);
530 iov
[0].buf
= (char *)&connect_index
;
531 iov
[0].len
= sizeof connect_index
;
532 iov
[1].buf
= &buffer
-> buf
[buffer
-> tail
];
533 iov
[1].len
= bytes_written
;
535 status
= (trace_write_packet_iov
536 (trace_connection_input
, 2, iov
,
538 if (status
!= ISC_R_SUCCESS
) {
540 log_error ("trace %s output: %s",
542 isc_result_totext (status
));
547 buffer
-> head
= first_byte
+ bytes_written
- 1;
548 c
-> out_bytes
-= bytes_written
;
550 /* If we didn't finish out the write, we filled the
551 O.S. output buffer and a further write would block,
552 so stop trying to flush now. */
553 if (bytes_written
!= bytes_this_write
)
554 return ISC_R_INPROGRESS
;
557 if (!BYTES_IN_BUFFER (buffer
))
558 buffer
= buffer
-> next
;
561 /* Get rid of any output buffers we emptied. */
562 buffer
= (omapi_buffer_t
*)0;
563 while (c
-> outbufs
&&
564 !BYTES_IN_BUFFER (c
-> outbufs
)) {
565 if (c
-> outbufs
-> next
) {
566 omapi_buffer_reference (&buffer
,
567 c
-> outbufs
-> next
, MDL
);
568 omapi_buffer_dereference (&c
-> outbufs
-> next
, MDL
);
570 omapi_buffer_dereference (&c
-> outbufs
, MDL
);
572 omapi_buffer_reference (&c
-> outbufs
, buffer
, MDL
);
573 omapi_buffer_dereference (&buffer
, MDL
);
576 return ISC_R_SUCCESS
;
579 isc_result_t
omapi_connection_get_uint32 (omapi_object_t
*c
,
585 status
= omapi_connection_copyout ((unsigned char *)&inbuf
,
587 if (status
!= ISC_R_SUCCESS
)
590 *result
= ntohl (inbuf
);
591 return ISC_R_SUCCESS
;
594 isc_result_t
omapi_connection_put_uint32 (omapi_object_t
*c
,
599 inbuf
= htonl (value
);
601 return omapi_connection_copyin (c
, (unsigned char *)&inbuf
,
605 isc_result_t
omapi_connection_get_uint16 (omapi_object_t
*c
,
611 status
= omapi_connection_copyout ((unsigned char *)&inbuf
,
613 if (status
!= ISC_R_SUCCESS
)
616 *result
= ntohs (inbuf
);
617 return ISC_R_SUCCESS
;
620 isc_result_t
omapi_connection_put_uint16 (omapi_object_t
*c
,
625 inbuf
= htons (value
);
627 return omapi_connection_copyin (c
, (unsigned char *)&inbuf
,
631 isc_result_t
omapi_connection_write_typed_data (omapi_object_t
*c
,
632 omapi_typed_data_t
*data
)
635 omapi_handle_t handle
;
637 /* Null data is valid. */
639 return omapi_connection_put_uint32 (c
, 0);
641 switch (data
-> type
) {
642 case omapi_datatype_int
:
643 status
= omapi_connection_put_uint32 (c
, sizeof (u_int32_t
));
644 if (status
!= ISC_R_SUCCESS
)
646 return omapi_connection_put_uint32 (c
, ((u_int32_t
)
647 (data
-> u
.integer
)));
649 case omapi_datatype_string
:
650 case omapi_datatype_data
:
651 status
= omapi_connection_put_uint32 (c
, data
-> u
.buffer
.len
);
652 if (status
!= ISC_R_SUCCESS
)
654 if (data
-> u
.buffer
.len
)
655 return omapi_connection_copyin
656 (c
, data
-> u
.buffer
.value
,
657 data
-> u
.buffer
.len
);
658 return ISC_R_SUCCESS
;
660 case omapi_datatype_object
:
661 if (data
-> u
.object
) {
662 status
= omapi_object_handle (&handle
,
664 if (status
!= ISC_R_SUCCESS
)
668 status
= omapi_connection_put_uint32 (c
, sizeof handle
);
669 if (status
!= ISC_R_SUCCESS
)
671 return omapi_connection_put_uint32 (c
, handle
);
674 return DHCP_R_INVALIDARG
;
677 isc_result_t
omapi_connection_put_name (omapi_object_t
*c
, const char *name
)
680 unsigned len
= strlen (name
);
682 status
= omapi_connection_put_uint16 (c
, len
);
683 if (status
!= ISC_R_SUCCESS
)
685 return omapi_connection_copyin (c
, (const unsigned char *)name
, len
);
688 isc_result_t
omapi_connection_put_string (omapi_object_t
*c
,
695 len
= strlen (string
);
699 status
= omapi_connection_put_uint32 (c
, len
);
700 if (status
!= ISC_R_SUCCESS
)
703 return omapi_connection_copyin
704 (c
, (const unsigned char *)string
, len
);
705 return ISC_R_SUCCESS
;
708 isc_result_t
omapi_connection_put_handle (omapi_object_t
*c
, omapi_object_t
*h
)
711 omapi_handle_t handle
;
714 status
= omapi_object_handle (&handle
, h
);
715 if (status
!= ISC_R_SUCCESS
)
718 handle
= 0; /* The null handle. */
719 status
= omapi_connection_put_uint32 (c
, sizeof handle
);
720 if (status
!= ISC_R_SUCCESS
)
722 return omapi_connection_put_uint32 (c
, handle
);