]> git.ipfire.org Git - thirdparty/dhcp.git/blob - omapip/buffer.c
Added an AR configure variable and regen
[thirdparty/dhcp.git] / omapip / buffer.c
1 /* buffer.c
2
3 Buffer access functions for the object management protocol... */
4
5 /*
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
9 *
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.
13 *
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.
21 *
22 * Internet Systems Consortium, Inc.
23 * 950 Charter Street
24 * Redwood City, CA 94063
25 * <info@isc.org>
26 * https://www.isc.org/
27 *
28 */
29
30 #include "dhcpd.h"
31
32 #include <omapip/omapip_p.h>
33 #include <errno.h>
34
35 #if defined (TRACING)
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 *,
43 unsigned, char *,
44 unsigned *);
45 extern omapi_array_t *omapi_connections;
46
47 void omapi_buffer_trace_setup ()
48 {
49 trace_connection_input =
50 trace_type_register ("connection-input",
51 (void *)0,
52 trace_connection_input_input,
53 trace_connection_input_stop, MDL);
54 trace_connection_output =
55 trace_type_register ("connection-output",
56 (void *)0,
57 trace_connection_output_input,
58 trace_connection_output_stop, MDL);
59 }
60
61 static void trace_connection_input_input (trace_type_t *ttype,
62 unsigned length, char *buf)
63 {
64 unsigned left, taken, cc = 0;
65 char *s;
66 int32_t connect_index;
67 isc_result_t status;
68 omapi_connection_object_t *c = (omapi_connection_object_t *)0;
69
70 memcpy (&connect_index, buf, sizeof connect_index);
71 connect_index = ntohl (connect_index);
72
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);
78 break;
79 }
80 } omapi_array_foreach_end (omapi_connections,
81 omapi_connection_object_t, lp);
82
83 if (!c) {
84 log_error ("trace connection input: no connection index %ld",
85 (long int)connect_index);
86 return;
87 }
88
89 s = buf + sizeof connect_index;
90 left = length - sizeof connect_index;
91
92 while (left) {
93 taken = 0;
94 status = omapi_connection_reader_trace ((omapi_object_t *)c,
95 left, s, &taken);
96 if (status != ISC_R_SUCCESS) {
97 log_error ("trace connection input: %s",
98 isc_result_totext (status));
99 break;
100 }
101 if (!taken) {
102 if (cc > 0) {
103 log_error ("trace connection_input: %s",
104 "input is not being consumed.");
105 break;
106 }
107 cc++;
108 } else {
109 cc = 0;
110 left -= taken;
111 }
112 }
113 omapi_connection_dereference (&c, MDL);
114 }
115
116 static void trace_connection_input_stop (trace_type_t *ttype) { }
117
118 static void trace_connection_output_input (trace_type_t *ttype,
119 unsigned length, char *buf)
120 {
121 /* We *could* check to see if the output is correct, but for now
122 we aren't going to do that. */
123 }
124
125 static void trace_connection_output_stop (trace_type_t *ttype) { }
126
127 #endif
128
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. */
131
132 isc_result_t omapi_connection_reader (omapi_object_t *h)
133 {
134 #if defined (TRACING)
135 return omapi_connection_reader_trace (h, 0, (char *)0, (unsigned *)0);
136 }
137
138 static isc_result_t omapi_connection_reader_trace (omapi_object_t *h,
139 unsigned stuff_len,
140 char *stuff_buf,
141 unsigned *stuff_taken)
142 {
143 #endif
144 omapi_buffer_t *buffer;
145 isc_result_t status;
146 unsigned read_len;
147 int read_status;
148 omapi_connection_object_t *c;
149 unsigned bytes_to_read;
150
151 if (!h || h -> type != omapi_type_connection)
152 return DHCP_R_INVALIDARG;
153 c = (omapi_connection_object_t *)h;
154
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;
159
160
161 if (c -> inbufs) {
162 for (buffer = c -> inbufs; buffer -> next;
163 buffer = buffer -> next)
164 ;
165 if (!BUFFER_BYTES_FREE (buffer)) {
166 status = omapi_buffer_new (&buffer -> next, MDL);
167 if (status != ISC_R_SUCCESS)
168 return status;
169 buffer = buffer -> next;
170 }
171 } else {
172 status = omapi_buffer_new (&c -> inbufs, MDL);
173 if (status != ISC_R_SUCCESS)
174 return status;
175 buffer = c -> inbufs;
176 }
177
178 bytes_to_read = BUFFER_BYTES_FREE (buffer);
179
180 while (bytes_to_read) {
181 if (buffer -> tail > buffer -> head)
182 read_len = sizeof (buffer -> buf) - buffer -> tail;
183 else
184 read_len = buffer -> head - buffer -> tail;
185
186 #if defined (TRACING)
187 if (trace_playback()) {
188 if (stuff_len) {
189 if (read_len > stuff_len)
190 read_len = stuff_len;
191 if (stuff_taken)
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;
198 } else {
199 break;
200 }
201 } else
202 #endif
203 {
204 read_status = read (c -> socket,
205 &buffer -> buf [buffer -> tail],
206 read_len);
207 }
208 if (read_status < 0) {
209 if (errno == EWOULDBLOCK)
210 break;
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;
218 } else
219 return ISC_R_UNEXPECTED;
220 }
221
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;
227 }
228 #if defined (TRACING)
229 if (trace_record ()) {
230 trace_iov_t iov [2];
231 int32_t connect_index;
232
233 connect_index = htonl (c -> index);
234
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;
239
240 status = (trace_write_packet_iov
241 (trace_connection_input, 2, iov, MDL));
242 if (status != ISC_R_SUCCESS) {
243 trace_stop ();
244 log_error ("trace connection input: %s",
245 isc_result_totext (status));
246 }
247 }
248 #endif
249 buffer -> tail += read_status;
250 c -> in_bytes += read_status;
251 if (buffer -> tail == sizeof buffer -> buf)
252 buffer -> tail = 0;
253 if (read_status < read_len)
254 break;
255 bytes_to_read -= read_status;
256 }
257
258 if (c -> bytes_needed <= c -> in_bytes) {
259 omapi_signal (h, "ready", c);
260 }
261 return ISC_R_SUCCESS;
262 }
263
264 /* Put some bytes into the output buffer for a connection. */
265
266 isc_result_t omapi_connection_copyin (omapi_object_t *h,
267 const unsigned char *bufp,
268 unsigned len)
269 {
270 omapi_buffer_t *buffer;
271 isc_result_t status;
272 int bytes_copied = 0;
273 unsigned copy_len;
274 int sig_flags = SIG_MODE_UPDATE;
275 omapi_connection_object_t *c;
276
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;
281
282 /* If the connection is closed, return an error if the caller
283 tries to copy in. */
284 if (c -> state == omapi_connection_disconnecting ||
285 c -> state == omapi_connection_closed)
286 return ISC_R_NOTCONNECTED;
287
288 if (c -> outbufs) {
289 for (buffer = c -> outbufs;
290 buffer -> next; buffer = buffer -> next)
291 ;
292 } else {
293 status = omapi_buffer_new (&c -> outbufs, MDL);
294 if (status != ISC_R_SUCCESS)
295 goto leave;
296 buffer = c -> outbufs;
297 }
298
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)
305 goto leave;
306 buffer = buffer -> next;
307 }
308
309 if (buffer -> tail > buffer -> head)
310 copy_len = sizeof (buffer -> buf) - buffer -> tail;
311 else
312 copy_len = buffer -> head - buffer -> tail;
313
314 if (copy_len > (len - bytes_copied))
315 copy_len = len - bytes_copied;
316
317 if (c -> out_key) {
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)
325 goto leave;
326 }
327
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)
334 buffer -> tail = 0;
335 }
336
337 status = ISC_R_SUCCESS;
338
339 leave:
340 /*
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.
344 */
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);
351 }
352 }
353
354 return (status);
355 }
356
357 /* Copy some bytes from the input buffer, and advance the input buffer
358 pointer beyond the bytes copied out. */
359
360 isc_result_t omapi_connection_copyout (unsigned char *buf,
361 omapi_object_t *h,
362 unsigned size)
363 {
364 unsigned bytes_remaining;
365 unsigned bytes_this_copy;
366 unsigned first_byte;
367 omapi_buffer_t *buffer;
368 unsigned char *bufp;
369 int sig_flags = SIG_MODE_UPDATE;
370 omapi_connection_object_t *c;
371 isc_result_t status;
372
373 if (!h || h -> type != omapi_type_connection)
374 return DHCP_R_INVALIDARG;
375 c = (omapi_connection_object_t *)h;
376
377 if (size > c -> in_bytes)
378 return ISC_R_NOMORE;
379 bufp = buf;
380 bytes_remaining = size;
381 buffer = c -> inbufs;
382
383 while (bytes_remaining) {
384 if (!buffer)
385 return ISC_R_UNEXPECTED;
386 if (BYTES_IN_BUFFER (buffer)) {
387 if (buffer -> head == (sizeof buffer -> buf) - 1)
388 first_byte = 0;
389 else
390 first_byte = buffer -> head + 1;
391
392 if (first_byte > buffer -> tail) {
393 bytes_this_copy = (sizeof buffer -> buf -
394 first_byte);
395 } else {
396 bytes_this_copy =
397 buffer -> tail - first_byte;
398 }
399 if (bytes_this_copy > bytes_remaining)
400 bytes_this_copy = bytes_remaining;
401 if (bufp) {
402 if (c -> in_key) {
403 if (!c -> in_context)
404 sig_flags |= SIG_MODE_INIT;
405 status = omapi_connection_sign_data
406 (sig_flags,
407 c -> in_key,
408 &c -> in_context,
409 (unsigned char *)
410 &buffer -> buf [first_byte],
411 bytes_this_copy,
412 (omapi_typed_data_t **)0);
413 if (status != ISC_R_SUCCESS)
414 return status;
415 }
416
417 memcpy (bufp, &buffer -> buf [first_byte],
418 bytes_this_copy);
419 bufp += bytes_this_copy;
420 }
421 bytes_remaining -= bytes_this_copy;
422 buffer -> head = first_byte + bytes_this_copy - 1;
423 c -> in_bytes -= bytes_this_copy;
424 }
425
426 if (!BYTES_IN_BUFFER (buffer))
427 buffer = buffer -> next;
428 }
429
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);
438 }
439 omapi_buffer_dereference (&c -> inbufs, MDL);
440 if (buffer) {
441 omapi_buffer_reference
442 (&c -> inbufs, buffer, MDL);
443 omapi_buffer_dereference (&buffer, MDL);
444 }
445 }
446 return ISC_R_SUCCESS;
447 }
448
449 isc_result_t omapi_connection_writer (omapi_object_t *h)
450 {
451 unsigned bytes_this_write;
452 int bytes_written;
453 unsigned first_byte;
454 omapi_buffer_t *buffer;
455 omapi_connection_object_t *c;
456
457 if (!h || h -> type != omapi_type_connection)
458 return DHCP_R_INVALIDARG;
459 c = (omapi_connection_object_t *)h;
460
461 /* Already flushed... */
462 if (!c -> out_bytes)
463 return ISC_R_SUCCESS;
464
465 buffer = c -> outbufs;
466
467 while (c -> out_bytes) {
468 if (!buffer)
469 return ISC_R_UNEXPECTED;
470 if (BYTES_IN_BUFFER (buffer)) {
471 if (buffer -> head == (sizeof buffer -> buf) - 1)
472 first_byte = 0;
473 else
474 first_byte = buffer -> head + 1;
475
476 if (first_byte > buffer -> tail) {
477 bytes_this_write = (sizeof buffer -> buf -
478 first_byte);
479 } else {
480 bytes_this_write =
481 buffer -> tail - first_byte;
482 }
483 bytes_written = write (c -> socket,
484 &buffer -> buf [first_byte],
485 bytes_this_write);
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)
494 return ISC_R_NOCONN;
495 #ifdef EDQUOT
496 else if (errno == EFBIG || errno == EDQUOT)
497 #else
498 else if (errno == EFBIG)
499 #endif
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;
509 else
510 return ISC_R_UNEXPECTED;
511 }
512 if (bytes_written == 0)
513 return ISC_R_INPROGRESS;
514
515 #if defined (TRACING)
516 if (trace_record ()) {
517 isc_result_t status;
518 trace_iov_t iov [2];
519 int32_t connect_index;
520
521 connect_index = htonl (c -> index);
522
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;
527
528 status = (trace_write_packet_iov
529 (trace_connection_input, 2, iov,
530 MDL));
531 if (status != ISC_R_SUCCESS) {
532 trace_stop ();
533 log_error ("trace %s output: %s",
534 "connection",
535 isc_result_totext (status));
536 }
537 }
538 #endif
539
540 buffer -> head = first_byte + bytes_written - 1;
541 c -> out_bytes -= bytes_written;
542
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;
548 }
549
550 if (!BYTES_IN_BUFFER (buffer))
551 buffer = buffer -> next;
552 }
553
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);
562 }
563 omapi_buffer_dereference (&c -> outbufs, MDL);
564 if (buffer) {
565 omapi_buffer_reference (&c -> outbufs, buffer, MDL);
566 omapi_buffer_dereference (&buffer, MDL);
567 }
568 }
569 return ISC_R_SUCCESS;
570 }
571
572 isc_result_t omapi_connection_get_uint32 (omapi_object_t *c,
573 u_int32_t *result)
574 {
575 u_int32_t inbuf;
576 isc_result_t status;
577
578 status = omapi_connection_copyout ((unsigned char *)&inbuf,
579 c, sizeof inbuf);
580 if (status != ISC_R_SUCCESS)
581 return status;
582
583 *result = ntohl (inbuf);
584 return ISC_R_SUCCESS;
585 }
586
587 isc_result_t omapi_connection_put_uint32 (omapi_object_t *c,
588 u_int32_t value)
589 {
590 u_int32_t inbuf;
591
592 inbuf = htonl (value);
593
594 return omapi_connection_copyin (c, (unsigned char *)&inbuf,
595 sizeof inbuf);
596 }
597
598 isc_result_t omapi_connection_get_uint16 (omapi_object_t *c,
599 u_int16_t *result)
600 {
601 u_int16_t inbuf;
602 isc_result_t status;
603
604 status = omapi_connection_copyout ((unsigned char *)&inbuf,
605 c, sizeof inbuf);
606 if (status != ISC_R_SUCCESS)
607 return status;
608
609 *result = ntohs (inbuf);
610 return ISC_R_SUCCESS;
611 }
612
613 isc_result_t omapi_connection_put_uint16 (omapi_object_t *c,
614 u_int32_t value)
615 {
616 u_int16_t inbuf;
617
618 inbuf = htons (value);
619
620 return omapi_connection_copyin (c, (unsigned char *)&inbuf,
621 sizeof inbuf);
622 }
623
624 isc_result_t omapi_connection_write_typed_data (omapi_object_t *c,
625 omapi_typed_data_t *data)
626 {
627 isc_result_t status;
628 omapi_handle_t handle;
629
630 /* Null data is valid. */
631 if (!data)
632 return omapi_connection_put_uint32 (c, 0);
633
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)
638 return status;
639 return omapi_connection_put_uint32 (c, ((u_int32_t)
640 (data -> u.integer)));
641
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)
646 return status;
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;
652
653 case omapi_datatype_object:
654 if (data -> u.object) {
655 status = omapi_object_handle (&handle,
656 data -> u.object);
657 if (status != ISC_R_SUCCESS)
658 return status;
659 } else
660 handle = 0;
661 status = omapi_connection_put_uint32 (c, sizeof handle);
662 if (status != ISC_R_SUCCESS)
663 return status;
664 return omapi_connection_put_uint32 (c, handle);
665
666 }
667 return DHCP_R_INVALIDARG;
668 }
669
670 isc_result_t omapi_connection_put_name (omapi_object_t *c, const char *name)
671 {
672 isc_result_t status;
673 unsigned len = strlen (name);
674
675 status = omapi_connection_put_uint16 (c, len);
676 if (status != ISC_R_SUCCESS)
677 return status;
678 return omapi_connection_copyin (c, (const unsigned char *)name, len);
679 }
680
681 isc_result_t omapi_connection_put_string (omapi_object_t *c,
682 const char *string)
683 {
684 isc_result_t status;
685 unsigned len;
686
687 if (string)
688 len = strlen (string);
689 else
690 len = 0;
691
692 status = omapi_connection_put_uint32 (c, len);
693 if (status != ISC_R_SUCCESS)
694 return status;
695 if (len)
696 return omapi_connection_copyin
697 (c, (const unsigned char *)string, len);
698 return ISC_R_SUCCESS;
699 }
700
701 isc_result_t omapi_connection_put_handle (omapi_object_t *c, omapi_object_t *h)
702 {
703 isc_result_t status;
704 omapi_handle_t handle;
705
706 if (h) {
707 status = omapi_object_handle (&handle, h);
708 if (status != ISC_R_SUCCESS)
709 return status;
710 } else
711 handle = 0; /* The null handle. */
712 status = omapi_connection_put_uint32 (c, sizeof handle);
713 if (status != ISC_R_SUCCESS)
714 return status;
715 return omapi_connection_put_uint32 (c, handle);
716 }
717
718 isc_result_t omapi_connection_put_named_uint32 (omapi_object_t *c,
719 const char *name,
720 u_int32_t value)
721 {
722 isc_result_t status;
723
724 status = omapi_connection_put_name(c, name);
725 if (status != ISC_R_SUCCESS)
726 return (status);
727
728 status = omapi_connection_put_uint32(c, sizeof(u_int32_t));
729 if (status != ISC_R_SUCCESS)
730 return (status);
731
732 status = omapi_connection_put_uint32(c, value);
733 return (status);
734 }
735