]> git.ipfire.org Git - thirdparty/dhcp.git/blob - omapip/buffer.c
Support for asynchronous ddns per ticket 19216 - convert to using isclib and
[thirdparty/dhcp.git] / omapip / buffer.c
1 /* buffer.c
2
3 Buffer access functions for the object management protocol... */
4
5 /*
6 * Copyright (c) 2004,2005,2007 by Internet Systems Consortium, Inc. ("ISC")
7 * Copyright (c) 1999-2003 by Internet Software Consortium
8 *
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.
12 *
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.
20 *
21 * Internet Systems Consortium, Inc.
22 * 950 Charter Street
23 * Redwood City, CA 94063
24 * <info@isc.org>
25 * https://www.isc.org/
26 *
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''.
33 */
34
35 #include "dhcpd.h"
36
37 #include <omapip/omapip_p.h>
38 #include <errno.h>
39
40 #if defined (TRACING)
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 *,
48 unsigned, char *,
49 unsigned *);
50 extern omapi_array_t *omapi_connections;
51
52 void omapi_buffer_trace_setup ()
53 {
54 trace_connection_input =
55 trace_type_register ("connection-input",
56 (void *)0,
57 trace_connection_input_input,
58 trace_connection_input_stop, MDL);
59 trace_connection_output =
60 trace_type_register ("connection-output",
61 (void *)0,
62 trace_connection_output_input,
63 trace_connection_output_stop, MDL);
64 }
65
66 static void trace_connection_input_input (trace_type_t *ttype,
67 unsigned length, char *buf)
68 {
69 unsigned left, taken, cc = 0;
70 char *s;
71 int32_t connect_index;
72 isc_result_t status;
73 omapi_connection_object_t *c = (omapi_connection_object_t *)0;
74
75 memcpy (&connect_index, buf, sizeof connect_index);
76 connect_index = ntohl (connect_index);
77
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);
83 break;
84 }
85 } omapi_array_foreach_end (omapi_connections,
86 omapi_connection_object_t, lp);
87
88 if (!c) {
89 log_error ("trace connection input: no connection index %ld",
90 (long int)connect_index);
91 return;
92 }
93
94 s = buf + sizeof connect_index;
95 left = length - sizeof connect_index;
96
97 while (left) {
98 taken = 0;
99 status = omapi_connection_reader_trace ((omapi_object_t *)c,
100 left, s, &taken);
101 if (status != ISC_R_SUCCESS) {
102 log_error ("trace connection input: %s",
103 isc_result_totext (status));
104 break;
105 }
106 if (!taken) {
107 if (cc > 0) {
108 log_error ("trace connection_input: %s",
109 "input is not being consumed.");
110 break;
111 }
112 cc++;
113 } else {
114 cc = 0;
115 left -= taken;
116 }
117 }
118 omapi_connection_dereference (&c, MDL);
119 }
120
121 static void trace_connection_input_stop (trace_type_t *ttype) { }
122
123 static void trace_connection_output_input (trace_type_t *ttype,
124 unsigned length, char *buf)
125 {
126 /* We *could* check to see if the output is correct, but for now
127 we aren't going to do that. */
128 }
129
130 static void trace_connection_output_stop (trace_type_t *ttype) { }
131
132 #endif
133
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. */
136
137 isc_result_t omapi_connection_reader (omapi_object_t *h)
138 {
139 #if defined (TRACING)
140 return omapi_connection_reader_trace (h, 0, (char *)0, (unsigned *)0);
141 }
142
143 static isc_result_t omapi_connection_reader_trace (omapi_object_t *h,
144 unsigned stuff_len,
145 char *stuff_buf,
146 unsigned *stuff_taken)
147 {
148 #endif
149 omapi_buffer_t *buffer;
150 isc_result_t status;
151 unsigned read_len;
152 int read_status;
153 omapi_connection_object_t *c;
154 unsigned bytes_to_read;
155
156 if (!h || h -> type != omapi_type_connection)
157 return DHCP_R_INVALIDARG;
158 c = (omapi_connection_object_t *)h;
159
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;
164
165
166 if (c -> inbufs) {
167 for (buffer = c -> inbufs; buffer -> next;
168 buffer = buffer -> next)
169 ;
170 if (!BUFFER_BYTES_FREE (buffer)) {
171 status = omapi_buffer_new (&buffer -> next, MDL);
172 if (status != ISC_R_SUCCESS)
173 return status;
174 buffer = buffer -> next;
175 }
176 } else {
177 status = omapi_buffer_new (&c -> inbufs, MDL);
178 if (status != ISC_R_SUCCESS)
179 return status;
180 buffer = c -> inbufs;
181 }
182
183 bytes_to_read = BUFFER_BYTES_FREE (buffer);
184
185 while (bytes_to_read) {
186 if (buffer -> tail > buffer -> head)
187 read_len = sizeof (buffer -> buf) - buffer -> tail;
188 else
189 read_len = buffer -> head - buffer -> tail;
190
191 #if defined (TRACING)
192 if (trace_playback()) {
193 if (stuff_len) {
194 if (read_len > stuff_len)
195 read_len = stuff_len;
196 if (stuff_taken)
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;
203 } else {
204 break;
205 }
206 } else
207 #endif
208 {
209 read_status = read (c -> socket,
210 &buffer -> buf [buffer -> tail],
211 read_len);
212 }
213 if (read_status < 0) {
214 if (errno == EWOULDBLOCK)
215 break;
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;
223 } else
224 return ISC_R_UNEXPECTED;
225 }
226
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;
232 }
233 #if defined (TRACING)
234 if (trace_record ()) {
235 trace_iov_t iov [2];
236 int32_t connect_index;
237
238 connect_index = htonl (c -> index);
239
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;
244
245 status = (trace_write_packet_iov
246 (trace_connection_input, 2, iov, MDL));
247 if (status != ISC_R_SUCCESS) {
248 trace_stop ();
249 log_error ("trace connection input: %s",
250 isc_result_totext (status));
251 }
252 }
253 #endif
254 buffer -> tail += read_status;
255 c -> in_bytes += read_status;
256 if (buffer -> tail == sizeof buffer -> buf)
257 buffer -> tail = 0;
258 if (read_status < read_len)
259 break;
260 bytes_to_read -= read_status;
261 }
262
263 if (c -> bytes_needed <= c -> in_bytes) {
264 omapi_signal (h, "ready", c);
265 }
266 return ISC_R_SUCCESS;
267 }
268
269 /* Put some bytes into the output buffer for a connection. */
270
271 isc_result_t omapi_connection_copyin (omapi_object_t *h,
272 const unsigned char *bufp,
273 unsigned len)
274 {
275 omapi_buffer_t *buffer;
276 isc_result_t status;
277 int bytes_copied = 0;
278 unsigned copy_len;
279 int sig_flags = SIG_MODE_UPDATE;
280 omapi_connection_object_t *c;
281
282 /* Make sure len is valid. */
283 if (len < 0)
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;
288
289 /* If the connection is closed, return an error if the caller
290 tries to copy in. */
291 if (c -> state == omapi_connection_disconnecting ||
292 c -> state == omapi_connection_closed)
293 return ISC_R_NOTCONNECTED;
294
295 if (c -> outbufs) {
296 for (buffer = c -> outbufs;
297 buffer -> next; buffer = buffer -> next)
298 ;
299 } else {
300 status = omapi_buffer_new (&c -> outbufs, MDL);
301 if (status != ISC_R_SUCCESS)
302 goto leave;
303 buffer = c -> outbufs;
304 }
305
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)
312 goto leave;
313 buffer = buffer -> next;
314 }
315
316 if (buffer -> tail > buffer -> head)
317 copy_len = sizeof (buffer -> buf) - buffer -> tail;
318 else
319 copy_len = buffer -> head - buffer -> tail;
320
321 if (copy_len > (len - bytes_copied))
322 copy_len = len - bytes_copied;
323
324 if (c -> out_key) {
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)
332 goto leave;
333 }
334
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)
341 buffer -> tail = 0;
342 }
343
344 status = ISC_R_SUCCESS;
345
346 leave:
347 /*
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.
351 */
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);
358 }
359 }
360
361 return (status);
362 }
363
364 /* Copy some bytes from the input buffer, and advance the input buffer
365 pointer beyond the bytes copied out. */
366
367 isc_result_t omapi_connection_copyout (unsigned char *buf,
368 omapi_object_t *h,
369 unsigned size)
370 {
371 unsigned bytes_remaining;
372 unsigned bytes_this_copy;
373 unsigned first_byte;
374 omapi_buffer_t *buffer;
375 unsigned char *bufp;
376 int sig_flags = SIG_MODE_UPDATE;
377 omapi_connection_object_t *c;
378 isc_result_t status;
379
380 if (!h || h -> type != omapi_type_connection)
381 return DHCP_R_INVALIDARG;
382 c = (omapi_connection_object_t *)h;
383
384 if (size > c -> in_bytes)
385 return ISC_R_NOMORE;
386 bufp = buf;
387 bytes_remaining = size;
388 buffer = c -> inbufs;
389
390 while (bytes_remaining) {
391 if (!buffer)
392 return ISC_R_UNEXPECTED;
393 if (BYTES_IN_BUFFER (buffer)) {
394 if (buffer -> head == (sizeof buffer -> buf) - 1)
395 first_byte = 0;
396 else
397 first_byte = buffer -> head + 1;
398
399 if (first_byte > buffer -> tail) {
400 bytes_this_copy = (sizeof buffer -> buf -
401 first_byte);
402 } else {
403 bytes_this_copy =
404 buffer -> tail - first_byte;
405 }
406 if (bytes_this_copy > bytes_remaining)
407 bytes_this_copy = bytes_remaining;
408 if (bufp) {
409 if (c -> in_key) {
410 if (!c -> in_context)
411 sig_flags |= SIG_MODE_INIT;
412 status = omapi_connection_sign_data
413 (sig_flags,
414 c -> in_key,
415 &c -> in_context,
416 (unsigned char *)
417 &buffer -> buf [first_byte],
418 bytes_this_copy,
419 (omapi_typed_data_t **)0);
420 if (status != ISC_R_SUCCESS)
421 return status;
422 }
423
424 memcpy (bufp, &buffer -> buf [first_byte],
425 bytes_this_copy);
426 bufp += bytes_this_copy;
427 }
428 bytes_remaining -= bytes_this_copy;
429 buffer -> head = first_byte + bytes_this_copy - 1;
430 c -> in_bytes -= bytes_this_copy;
431 }
432
433 if (!BYTES_IN_BUFFER (buffer))
434 buffer = buffer -> next;
435 }
436
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);
445 }
446 omapi_buffer_dereference (&c -> inbufs, MDL);
447 if (buffer) {
448 omapi_buffer_reference
449 (&c -> inbufs, buffer, MDL);
450 omapi_buffer_dereference (&buffer, MDL);
451 }
452 }
453 return ISC_R_SUCCESS;
454 }
455
456 isc_result_t omapi_connection_writer (omapi_object_t *h)
457 {
458 unsigned bytes_this_write;
459 int bytes_written;
460 unsigned first_byte;
461 omapi_buffer_t *buffer;
462 omapi_connection_object_t *c;
463
464 if (!h || h -> type != omapi_type_connection)
465 return DHCP_R_INVALIDARG;
466 c = (omapi_connection_object_t *)h;
467
468 /* Already flushed... */
469 if (!c -> out_bytes)
470 return ISC_R_SUCCESS;
471
472 buffer = c -> outbufs;
473
474 while (c -> out_bytes) {
475 if (!buffer)
476 return ISC_R_UNEXPECTED;
477 if (BYTES_IN_BUFFER (buffer)) {
478 if (buffer -> head == (sizeof buffer -> buf) - 1)
479 first_byte = 0;
480 else
481 first_byte = buffer -> head + 1;
482
483 if (first_byte > buffer -> tail) {
484 bytes_this_write = (sizeof buffer -> buf -
485 first_byte);
486 } else {
487 bytes_this_write =
488 buffer -> tail - first_byte;
489 }
490 bytes_written = write (c -> socket,
491 &buffer -> buf [first_byte],
492 bytes_this_write);
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)
501 return ISC_R_NOCONN;
502 #ifdef EDQUOT
503 else if (errno == EFBIG || errno == EDQUOT)
504 #else
505 else if (errno == EFBIG)
506 #endif
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;
516 else
517 return ISC_R_UNEXPECTED;
518 }
519 if (bytes_written == 0)
520 return ISC_R_INPROGRESS;
521
522 #if defined (TRACING)
523 if (trace_record ()) {
524 isc_result_t status;
525 trace_iov_t iov [2];
526 int32_t connect_index;
527
528 connect_index = htonl (c -> index);
529
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;
534
535 status = (trace_write_packet_iov
536 (trace_connection_input, 2, iov,
537 MDL));
538 if (status != ISC_R_SUCCESS) {
539 trace_stop ();
540 log_error ("trace %s output: %s",
541 "connection",
542 isc_result_totext (status));
543 }
544 }
545 #endif
546
547 buffer -> head = first_byte + bytes_written - 1;
548 c -> out_bytes -= bytes_written;
549
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;
555 }
556
557 if (!BYTES_IN_BUFFER (buffer))
558 buffer = buffer -> next;
559 }
560
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);
569 }
570 omapi_buffer_dereference (&c -> outbufs, MDL);
571 if (buffer) {
572 omapi_buffer_reference (&c -> outbufs, buffer, MDL);
573 omapi_buffer_dereference (&buffer, MDL);
574 }
575 }
576 return ISC_R_SUCCESS;
577 }
578
579 isc_result_t omapi_connection_get_uint32 (omapi_object_t *c,
580 u_int32_t *result)
581 {
582 u_int32_t inbuf;
583 isc_result_t status;
584
585 status = omapi_connection_copyout ((unsigned char *)&inbuf,
586 c, sizeof inbuf);
587 if (status != ISC_R_SUCCESS)
588 return status;
589
590 *result = ntohl (inbuf);
591 return ISC_R_SUCCESS;
592 }
593
594 isc_result_t omapi_connection_put_uint32 (omapi_object_t *c,
595 u_int32_t value)
596 {
597 u_int32_t inbuf;
598
599 inbuf = htonl (value);
600
601 return omapi_connection_copyin (c, (unsigned char *)&inbuf,
602 sizeof inbuf);
603 }
604
605 isc_result_t omapi_connection_get_uint16 (omapi_object_t *c,
606 u_int16_t *result)
607 {
608 u_int16_t inbuf;
609 isc_result_t status;
610
611 status = omapi_connection_copyout ((unsigned char *)&inbuf,
612 c, sizeof inbuf);
613 if (status != ISC_R_SUCCESS)
614 return status;
615
616 *result = ntohs (inbuf);
617 return ISC_R_SUCCESS;
618 }
619
620 isc_result_t omapi_connection_put_uint16 (omapi_object_t *c,
621 u_int32_t value)
622 {
623 u_int16_t inbuf;
624
625 inbuf = htons (value);
626
627 return omapi_connection_copyin (c, (unsigned char *)&inbuf,
628 sizeof inbuf);
629 }
630
631 isc_result_t omapi_connection_write_typed_data (omapi_object_t *c,
632 omapi_typed_data_t *data)
633 {
634 isc_result_t status;
635 omapi_handle_t handle;
636
637 /* Null data is valid. */
638 if (!data)
639 return omapi_connection_put_uint32 (c, 0);
640
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)
645 return status;
646 return omapi_connection_put_uint32 (c, ((u_int32_t)
647 (data -> u.integer)));
648
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)
653 return status;
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;
659
660 case omapi_datatype_object:
661 if (data -> u.object) {
662 status = omapi_object_handle (&handle,
663 data -> u.object);
664 if (status != ISC_R_SUCCESS)
665 return status;
666 } else
667 handle = 0;
668 status = omapi_connection_put_uint32 (c, sizeof handle);
669 if (status != ISC_R_SUCCESS)
670 return status;
671 return omapi_connection_put_uint32 (c, handle);
672
673 }
674 return DHCP_R_INVALIDARG;
675 }
676
677 isc_result_t omapi_connection_put_name (omapi_object_t *c, const char *name)
678 {
679 isc_result_t status;
680 unsigned len = strlen (name);
681
682 status = omapi_connection_put_uint16 (c, len);
683 if (status != ISC_R_SUCCESS)
684 return status;
685 return omapi_connection_copyin (c, (const unsigned char *)name, len);
686 }
687
688 isc_result_t omapi_connection_put_string (omapi_object_t *c,
689 const char *string)
690 {
691 isc_result_t status;
692 unsigned len;
693
694 if (string)
695 len = strlen (string);
696 else
697 len = 0;
698
699 status = omapi_connection_put_uint32 (c, len);
700 if (status != ISC_R_SUCCESS)
701 return status;
702 if (len)
703 return omapi_connection_copyin
704 (c, (const unsigned char *)string, len);
705 return ISC_R_SUCCESS;
706 }
707
708 isc_result_t omapi_connection_put_handle (omapi_object_t *c, omapi_object_t *h)
709 {
710 isc_result_t status;
711 omapi_handle_t handle;
712
713 if (h) {
714 status = omapi_object_handle (&handle, h);
715 if (status != ISC_R_SUCCESS)
716 return status;
717 } else
718 handle = 0; /* The null handle. */
719 status = omapi_connection_put_uint32 (c, sizeof handle);
720 if (status != ISC_R_SUCCESS)
721 return status;
722 return omapi_connection_put_uint32 (c, handle);
723 }