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