1 /* 2 * rdata.c 3 * 4 * rdata implementation 5 * 6 * a Net::DNS like library for C 7 * 8 * (c) NLnet Labs, 2004-2006 9 * 10 * See the file LICENSE for the license 11 */ 12 13 #include <ldns/config.h> 14 15 #include <ldns/ldns.h> 16 17 /* 18 * Access functions 19 * do this as functions to get type checking 20 */ 21 22 /* read */ 23 size_t 24 ldns_rdf_size(const ldns_rdf *rd) 25 { 26 assert(rd != NULL); 27 return rd->_size; 28 } 29 30 ldns_rdf_type 31 ldns_rdf_get_type(const ldns_rdf *rd) 32 { 33 assert(rd != NULL); 34 return rd->_type; 35 } 36 37 uint8_t * 38 ldns_rdf_data(const ldns_rdf *rd) 39 { 40 assert(rd != NULL); 41 return rd->_data; 42 } 43 44 /* write */ 45 void 46 ldns_rdf_set_size(ldns_rdf *rd, size_t size) 47 { 48 assert(rd != NULL); 49 rd->_size = size; 50 } 51 52 void 53 ldns_rdf_set_type(ldns_rdf *rd, ldns_rdf_type type) 54 { 55 assert(rd != NULL); 56 rd->_type = type; 57 } 58 59 void 60 ldns_rdf_set_data(ldns_rdf *rd, void *data) 61 { 62 /* only copy the pointer */ 63 assert(rd != NULL); 64 rd->_data = data; 65 } 66 67 /* for types that allow it, return 68 * the native/host order type */ 69 uint8_t 70 ldns_rdf2native_int8(const ldns_rdf *rd) 71 { 72 uint8_t data; 73 74 /* only allow 8 bit rdfs */ 75 if (ldns_rdf_size(rd) != LDNS_RDF_SIZE_BYTE) { 76 return 0; 77 } 78 79 memcpy(&data, ldns_rdf_data(rd), sizeof(data)); 80 return data; 81 } 82 83 uint16_t 84 ldns_rdf2native_int16(const ldns_rdf *rd) 85 { 86 uint16_t data; 87 88 /* only allow 16 bit rdfs */ 89 if (ldns_rdf_size(rd) != LDNS_RDF_SIZE_WORD) { 90 return 0; 91 } 92 93 memcpy(&data, ldns_rdf_data(rd), sizeof(data)); 94 return ntohs(data); 95 } 96 97 uint32_t 98 ldns_rdf2native_int32(const ldns_rdf *rd) 99 { 100 uint32_t data; 101 102 /* only allow 32 bit rdfs */ 103 if (ldns_rdf_size(rd) != LDNS_RDF_SIZE_DOUBLEWORD) { 104 return 0; 105 } 106 107 memcpy(&data, ldns_rdf_data(rd), sizeof(data)); 108 return ntohl(data); 109 } 110 111 time_t 112 ldns_rdf2native_time_t(const ldns_rdf *rd) 113 { 114 uint32_t data; 115 116 /* only allow 32 bit rdfs */ 117 if (ldns_rdf_size(rd) != LDNS_RDF_SIZE_DOUBLEWORD || 118 ldns_rdf_get_type(rd) != LDNS_RDF_TYPE_TIME) { 119 return 0; 120 } 121 memcpy(&data, ldns_rdf_data(rd), sizeof(data)); 122 return (time_t)ntohl(data); 123 } 124 125 ldns_rdf * 126 ldns_native2rdf_int8(ldns_rdf_type type, uint8_t value) 127 { 128 return ldns_rdf_new_frm_data(type, LDNS_RDF_SIZE_BYTE, &value); 129 } 130 131 ldns_rdf * 132 ldns_native2rdf_int16(ldns_rdf_type type, uint16_t value) 133 { 134 uint16_t *rdf_data = LDNS_XMALLOC(uint16_t, 1); 135 ldns_rdf* rdf; 136 if (!rdf_data) { 137 return NULL; 138 } 139 ldns_write_uint16(rdf_data, value); 140 rdf = ldns_rdf_new(type, LDNS_RDF_SIZE_WORD, rdf_data); 141 if(!rdf) 142 LDNS_FREE(rdf_data); 143 return rdf; 144 } 145 146 ldns_rdf * 147 ldns_native2rdf_int32(ldns_rdf_type type, uint32_t value) 148 { 149 uint32_t *rdf_data = LDNS_XMALLOC(uint32_t, 1); 150 ldns_rdf* rdf; 151 if (!rdf_data) { 152 return NULL; 153 } 154 ldns_write_uint32(rdf_data, value); 155 rdf = ldns_rdf_new(type, LDNS_RDF_SIZE_DOUBLEWORD, rdf_data); 156 if(!rdf) 157 LDNS_FREE(rdf_data); 158 return rdf; 159 } 160 161 ldns_rdf * 162 ldns_native2rdf_int16_data(size_t size, uint8_t *data) 163 { 164 uint8_t *rdf_data = LDNS_XMALLOC(uint8_t, size + 2); 165 ldns_rdf* rdf; 166 if (!rdf_data) { 167 return NULL; 168 } 169 ldns_write_uint16(rdf_data, size); 170 memcpy(rdf_data + 2, data, size); 171 rdf = ldns_rdf_new(LDNS_RDF_TYPE_INT16_DATA, size + 2, rdf_data); 172 if(!rdf) 173 LDNS_FREE(rdf_data); 174 return rdf; 175 } 176 177 /* note: data must be allocated memory */ 178 ldns_rdf * 179 ldns_rdf_new(ldns_rdf_type type, size_t size, void *data) 180 { 181 ldns_rdf *rd; 182 rd = LDNS_MALLOC(ldns_rdf); 183 if (!rd) { 184 return NULL; 185 } 186 ldns_rdf_set_size(rd, size); 187 ldns_rdf_set_type(rd, type); 188 ldns_rdf_set_data(rd, data); 189 return rd; 190 } 191 192 ldns_rdf * 193 ldns_rdf_new_frm_data(ldns_rdf_type type, size_t size, const void *data) 194 { 195 ldns_rdf *rdf; 196 197 /* if the size is too big, fail */ 198 if (size > LDNS_MAX_RDFLEN) { 199 return NULL; 200 } 201 202 /* allocate space */ 203 rdf = LDNS_MALLOC(ldns_rdf); 204 if (!rdf) { 205 return NULL; 206 } 207 rdf->_data = LDNS_XMALLOC(uint8_t, size); 208 if (!rdf->_data) { 209 LDNS_FREE(rdf); 210 return NULL; 211 } 212 213 /* set the values */ 214 ldns_rdf_set_type(rdf, type); 215 ldns_rdf_set_size(rdf, size); 216 memcpy(rdf->_data, data, size); 217 218 return rdf; 219 } 220 221 ldns_rdf * 222 ldns_rdf_clone(const ldns_rdf *rd) 223 { 224 assert(rd != NULL); 225 return (ldns_rdf_new_frm_data( ldns_rdf_get_type(rd), 226 ldns_rdf_size(rd), ldns_rdf_data(rd))); 227 } 228 229 void 230 ldns_rdf_deep_free(ldns_rdf *rd) 231 { 232 if (rd) { 233 if (rd->_data) { 234 LDNS_FREE(rd->_data); 235 } 236 LDNS_FREE(rd); 237 } 238 } 239 240 void 241 ldns_rdf_free(ldns_rdf *rd) 242 { 243 if (rd) { 244 LDNS_FREE(rd); 245 } 246 } 247 248 ldns_rdf * 249 ldns_rdf_new_frm_str(ldns_rdf_type type, const char *str) 250 { 251 ldns_rdf *rdf = NULL; 252 ldns_status status; 253 254 switch (type) { 255 case LDNS_RDF_TYPE_DNAME: 256 status = ldns_str2rdf_dname(&rdf, str); 257 break; 258 case LDNS_RDF_TYPE_INT8: 259 status = ldns_str2rdf_int8(&rdf, str); 260 break; 261 case LDNS_RDF_TYPE_INT16: 262 status = ldns_str2rdf_int16(&rdf, str); 263 break; 264 case LDNS_RDF_TYPE_INT32: 265 status = ldns_str2rdf_int32(&rdf, str); 266 break; 267 case LDNS_RDF_TYPE_A: 268 status = ldns_str2rdf_a(&rdf, str); 269 break; 270 case LDNS_RDF_TYPE_AAAA: 271 status = ldns_str2rdf_aaaa(&rdf, str); 272 break; 273 case LDNS_RDF_TYPE_STR: 274 status = ldns_str2rdf_str(&rdf, str); 275 break; 276 case LDNS_RDF_TYPE_APL: 277 status = ldns_str2rdf_apl(&rdf, str); 278 break; 279 case LDNS_RDF_TYPE_B64: 280 status = ldns_str2rdf_b64(&rdf, str); 281 break; 282 case LDNS_RDF_TYPE_B32_EXT: 283 status = ldns_str2rdf_b32_ext(&rdf, str); 284 break; 285 case LDNS_RDF_TYPE_HEX: 286 status = ldns_str2rdf_hex(&rdf, str); 287 break; 288 case LDNS_RDF_TYPE_NSEC: 289 status = ldns_str2rdf_nsec(&rdf, str); 290 break; 291 case LDNS_RDF_TYPE_TYPE: 292 status = ldns_str2rdf_type(&rdf, str); 293 break; 294 case LDNS_RDF_TYPE_CLASS: 295 status = ldns_str2rdf_class(&rdf, str); 296 break; 297 case LDNS_RDF_TYPE_CERT_ALG: 298 status = ldns_str2rdf_cert_alg(&rdf, str); 299 break; 300 case LDNS_RDF_TYPE_ALG: 301 status = ldns_str2rdf_alg(&rdf, str); 302 break; 303 case LDNS_RDF_TYPE_UNKNOWN: 304 status = ldns_str2rdf_unknown(&rdf, str); 305 break; 306 case LDNS_RDF_TYPE_TIME: 307 status = ldns_str2rdf_time(&rdf, str); 308 break; 309 case LDNS_RDF_TYPE_PERIOD: 310 status = ldns_str2rdf_period(&rdf, str); 311 break; 312 case LDNS_RDF_TYPE_HIP: 313 status = ldns_str2rdf_hip(&rdf, str); 314 break; 315 case LDNS_RDF_TYPE_SERVICE: 316 status = ldns_str2rdf_service(&rdf, str); 317 break; 318 case LDNS_RDF_TYPE_LOC: 319 status = ldns_str2rdf_loc(&rdf, str); 320 break; 321 case LDNS_RDF_TYPE_WKS: 322 status = ldns_str2rdf_wks(&rdf, str); 323 break; 324 case LDNS_RDF_TYPE_NSAP: 325 status = ldns_str2rdf_nsap(&rdf, str); 326 break; 327 case LDNS_RDF_TYPE_ATMA: 328 status = ldns_str2rdf_atma(&rdf, str); 329 break; 330 case LDNS_RDF_TYPE_IPSECKEY: 331 status = ldns_str2rdf_ipseckey(&rdf, str); 332 break; 333 case LDNS_RDF_TYPE_NSEC3_SALT: 334 status = ldns_str2rdf_nsec3_salt(&rdf, str); 335 break; 336 case LDNS_RDF_TYPE_NSEC3_NEXT_OWNER: 337 status = ldns_str2rdf_b32_ext(&rdf, str); 338 break; 339 case LDNS_RDF_TYPE_ILNP64: 340 status = ldns_str2rdf_ilnp64(&rdf, str); 341 break; 342 case LDNS_RDF_TYPE_EUI48: 343 status = ldns_str2rdf_eui48(&rdf, str); 344 break; 345 case LDNS_RDF_TYPE_EUI64: 346 status = ldns_str2rdf_eui64(&rdf, str); 347 break; 348 case LDNS_RDF_TYPE_TAG: 349 status = ldns_str2rdf_tag(&rdf, str); 350 break; 351 case LDNS_RDF_TYPE_LONG_STR: 352 status = ldns_str2rdf_long_str(&rdf, str); 353 break; 354 case LDNS_RDF_TYPE_NONE: 355 default: 356 /* default default ??? */ 357 status = LDNS_STATUS_ERR; 358 break; 359 } 360 if (LDNS_STATUS_OK == status) { 361 ldns_rdf_set_type(rdf, type); 362 return rdf; 363 } 364 if (rdf) { 365 LDNS_FREE(rdf); 366 } 367 return NULL; 368 } 369 370 ldns_status 371 ldns_rdf_new_frm_fp(ldns_rdf **rdf, ldns_rdf_type type, FILE *fp) 372 { 373 return ldns_rdf_new_frm_fp_l(rdf, type, fp, NULL); 374 } 375 376 ldns_status 377 ldns_rdf_new_frm_fp_l(ldns_rdf **rdf, ldns_rdf_type type, FILE *fp, int *line_nr) 378 { 379 char *line; 380 ldns_rdf *r; 381 ssize_t t; 382 383 line = LDNS_XMALLOC(char, LDNS_MAX_LINELEN + 1); 384 if (!line) { 385 return LDNS_STATUS_MEM_ERR; 386 } 387 388 /* read an entire line in from the file */ 389 if ((t = ldns_fget_token_l(fp, line, LDNS_PARSE_SKIP_SPACE, 0, line_nr)) == -1 || t == 0) { 390 LDNS_FREE(line); 391 return LDNS_STATUS_SYNTAX_RDATA_ERR; 392 } 393 r = ldns_rdf_new_frm_str(type, (const char*) line); 394 LDNS_FREE(line); 395 if (rdf) { 396 *rdf = r; 397 return LDNS_STATUS_OK; 398 } else { 399 return LDNS_STATUS_NULL; 400 } 401 } 402 403 ldns_rdf * 404 ldns_rdf_address_reverse(ldns_rdf *rd) 405 { 406 uint8_t buf_4[LDNS_IP4ADDRLEN]; 407 uint8_t buf_6[LDNS_IP6ADDRLEN * 2]; 408 ldns_rdf *rev; 409 ldns_rdf *in_addr; 410 ldns_rdf *ret_dname; 411 uint8_t octet; 412 uint8_t nnibble; 413 uint8_t nibble; 414 uint8_t i, j; 415 416 char *char_dname; 417 int nbit; 418 419 if (ldns_rdf_get_type(rd) != LDNS_RDF_TYPE_A && 420 ldns_rdf_get_type(rd) != LDNS_RDF_TYPE_AAAA) { 421 return NULL; 422 } 423 424 in_addr = NULL; 425 ret_dname = NULL; 426 427 switch(ldns_rdf_get_type(rd)) { 428 case LDNS_RDF_TYPE_A: 429 /* the length of the buffer is 4 */ 430 buf_4[3] = ldns_rdf_data(rd)[0]; 431 buf_4[2] = ldns_rdf_data(rd)[1]; 432 buf_4[1] = ldns_rdf_data(rd)[2]; 433 buf_4[0] = ldns_rdf_data(rd)[3]; 434 in_addr = ldns_dname_new_frm_str("in-addr.arpa."); 435 if (!in_addr) { 436 return NULL; 437 } 438 /* make a new rdf and convert that back */ 439 rev = ldns_rdf_new_frm_data( LDNS_RDF_TYPE_A, 440 LDNS_IP4ADDRLEN, (void*)&buf_4); 441 if (!rev) { 442 LDNS_FREE(in_addr); 443 return NULL; 444 } 445 446 /* convert rev to a string */ 447 char_dname = ldns_rdf2str(rev); 448 if (!char_dname) { 449 LDNS_FREE(in_addr); 450 ldns_rdf_deep_free(rev); 451 return NULL; 452 } 453 /* transform back to rdf with type dname */ 454 ret_dname = ldns_dname_new_frm_str(char_dname); 455 if (!ret_dname) { 456 LDNS_FREE(in_addr); 457 ldns_rdf_deep_free(rev); 458 LDNS_FREE(char_dname); 459 return NULL; 460 } 461 /* not needed anymore */ 462 ldns_rdf_deep_free(rev); 463 LDNS_FREE(char_dname); 464 break; 465 case LDNS_RDF_TYPE_AAAA: 466 /* some foo magic to reverse the nibbles ... */ 467 468 for (nbit = 127; nbit >= 0; nbit = nbit - 4) { 469 /* calculate octett (8 bit) */ 470 octet = ( ((unsigned int) nbit) & 0x78) >> 3; 471 /* calculate nibble */ 472 nnibble = ( ((unsigned int) nbit) & 0x04) >> 2; 473 /* extract nibble */ 474 nibble = (ldns_rdf_data(rd)[octet] & ( 0xf << (4 * (1 - 475 nnibble)) ) ) >> ( 4 * (1 - 476 nnibble)); 477 478 buf_6[(LDNS_IP6ADDRLEN * 2 - 1) - 479 (octet * 2 + nnibble)] = 480 (uint8_t)ldns_int_to_hexdigit((int)nibble); 481 } 482 483 char_dname = LDNS_XMALLOC(char, (LDNS_IP6ADDRLEN * 4)); 484 if (!char_dname) { 485 return NULL; 486 } 487 char_dname[LDNS_IP6ADDRLEN * 4 - 1] = '\0'; /* closure */ 488 489 /* walk the string and add . 's */ 490 for (i = 0, j = 0; i < LDNS_IP6ADDRLEN * 2; i++, j = j + 2) { 491 char_dname[j] = (char)buf_6[i]; 492 if (i != LDNS_IP6ADDRLEN * 2 - 1) { 493 char_dname[j + 1] = '.'; 494 } 495 } 496 in_addr = ldns_dname_new_frm_str("ip6.arpa."); 497 if (!in_addr) { 498 LDNS_FREE(char_dname); 499 return NULL; 500 } 501 502 /* convert rev to a string */ 503 ret_dname = ldns_dname_new_frm_str(char_dname); 504 LDNS_FREE(char_dname); 505 if (!ret_dname) { 506 ldns_rdf_deep_free(in_addr); 507 return NULL; 508 } 509 break; 510 default: 511 break; 512 } 513 /* add the suffix */ 514 rev = ldns_dname_cat_clone(ret_dname, in_addr); 515 516 ldns_rdf_deep_free(ret_dname); 517 ldns_rdf_deep_free(in_addr); 518 return rev; 519 } 520 521 ldns_status 522 ldns_rdf_hip_get_alg_hit_pk(ldns_rdf *rdf, uint8_t* alg, 523 uint8_t *hit_size, uint8_t** hit, 524 uint16_t *pk_size, uint8_t** pk) 525 { 526 uint8_t *data; 527 size_t rdf_size; 528 529 if (! rdf || ! alg || ! hit || ! hit_size || ! pk || ! pk_size) { 530 return LDNS_STATUS_INVALID_POINTER; 531 } else if (ldns_rdf_get_type(rdf) != LDNS_RDF_TYPE_HIP) { 532 return LDNS_STATUS_INVALID_RDF_TYPE; 533 } else if ((rdf_size = ldns_rdf_size(rdf)) < 6) { 534 return LDNS_STATUS_WIRE_RDATA_ERR; 535 } 536 data = ldns_rdf_data(rdf); 537 *hit_size = data[0]; 538 *alg = data[1]; 539 *pk_size = ldns_read_uint16(data + 2); 540 *hit = data + 4; 541 *pk = data + 4 + *hit_size; 542 if (*hit_size == 0 || *pk_size == 0 || 543 rdf_size < (size_t) *hit_size + *pk_size + 4) { 544 return LDNS_STATUS_WIRE_RDATA_ERR; 545 } 546 return LDNS_STATUS_OK; 547 } 548 549 ldns_status 550 ldns_rdf_hip_new_frm_alg_hit_pk(ldns_rdf** rdf, uint8_t alg, 551 uint8_t hit_size, uint8_t *hit, 552 uint16_t pk_size, uint8_t *pk) 553 { 554 uint8_t *data; 555 556 if (! rdf) { 557 return LDNS_STATUS_INVALID_POINTER; 558 } 559 if (4 + hit_size + pk_size > LDNS_MAX_RDFLEN) { 560 return LDNS_STATUS_RDATA_OVERFLOW; 561 } 562 data = LDNS_XMALLOC(uint8_t, 4 + hit_size + pk_size); 563 if (data == NULL) { 564 return LDNS_STATUS_MEM_ERR; 565 } 566 data[0] = hit_size; 567 data[1] = alg; 568 ldns_write_uint16(data + 2, pk_size); 569 memcpy(data + 4, hit, hit_size); 570 memcpy(data + 4 + hit_size, pk, pk_size); 571 *rdf = ldns_rdf_new(LDNS_RDF_TYPE_HIP, 4 + hit_size + pk_size, data); 572 if (! *rdf) { 573 LDNS_FREE(data); 574 return LDNS_STATUS_MEM_ERR; 575 } 576 return LDNS_STATUS_OK; 577 } 578 579 ldns_status 580 ldns_octet(char *word, size_t *length) 581 { 582 char *s; 583 char *p; 584 *length = 0; 585 586 for (s = p = word; *s != '\0'; s++,p++) { 587 switch (*s) { 588 case '.': 589 if (s[1] == '.') { 590 return LDNS_STATUS_EMPTY_LABEL; 591 } 592 *p = *s; 593 (*length)++; 594 break; 595 case '\\': 596 if ('0' <= s[1] && s[1] <= '9' && 597 '0' <= s[2] && s[2] <= '9' && 598 '0' <= s[3] && s[3] <= '9') { 599 /* \DDD seen */ 600 int val = ((s[1] - '0') * 100 + 601 (s[2] - '0') * 10 + (s[3] - '0')); 602 603 if (0 <= val && val <= 255) { 604 /* this also handles \0 */ 605 s += 3; 606 *p = val; 607 (*length)++; 608 } else { 609 return LDNS_STATUS_DDD_OVERFLOW; 610 } 611 } else { 612 /* an espaced character, like \<space> ? 613 * remove the '\' keep the rest */ 614 *p = *++s; 615 (*length)++; 616 } 617 break; 618 case '\"': 619 /* non quoted " Is either first or the last character in 620 * the string */ 621 622 *p = *++s; /* skip it */ 623 (*length)++; 624 /* I'm not sure if this is needed in libdns... MG */ 625 if ( *s == '\0' ) { 626 /* ok, it was the last one */ 627 *p = '\0'; 628 return LDNS_STATUS_OK; 629 } 630 break; 631 default: 632 *p = *s; 633 (*length)++; 634 break; 635 } 636 } 637 *p = '\0'; 638 return LDNS_STATUS_OK; 639 } 640 641 int 642 ldns_rdf_compare(const ldns_rdf *rd1, const ldns_rdf *rd2) 643 { 644 uint16_t i1, i2, i; 645 uint8_t *d1, *d2; 646 647 /* only when both are not NULL we can say anything about them */ 648 if (!rd1 && !rd2) { 649 return 0; 650 } 651 if (!rd1 || !rd2) { 652 return -1; 653 } 654 i1 = ldns_rdf_size(rd1); 655 i2 = ldns_rdf_size(rd2); 656 657 if (i1 < i2) { 658 return -1; 659 } else if (i1 > i2) { 660 return +1; 661 } else { 662 d1 = (uint8_t*)ldns_rdf_data(rd1); 663 d2 = (uint8_t*)ldns_rdf_data(rd2); 664 for(i = 0; i < i1; i++) { 665 if (d1[i] < d2[i]) { 666 return -1; 667 } else if (d1[i] > d2[i]) { 668 return +1; 669 } 670 } 671 } 672 return 0; 673 } 674 675 uint32_t 676 ldns_str2period(const char *nptr, const char **endptr) 677 { 678 int sign = 0; 679 uint32_t i = 0; 680 uint32_t seconds = 0; 681 682 for(*endptr = nptr; **endptr; (*endptr)++) { 683 switch (**endptr) { 684 case ' ': 685 case '\t': 686 break; 687 case '-': 688 if(sign == 0) { 689 sign = -1; 690 } else { 691 return seconds; 692 } 693 break; 694 case '+': 695 if(sign == 0) { 696 sign = 1; 697 } else { 698 return seconds; 699 } 700 break; 701 case 's': 702 case 'S': 703 seconds += i; 704 i = 0; 705 break; 706 case 'm': 707 case 'M': 708 seconds += i * 60; 709 i = 0; 710 break; 711 case 'h': 712 case 'H': 713 seconds += i * 60 * 60; 714 i = 0; 715 break; 716 case 'd': 717 case 'D': 718 seconds += i * 60 * 60 * 24; 719 i = 0; 720 break; 721 case 'w': 722 case 'W': 723 seconds += i * 60 * 60 * 24 * 7; 724 i = 0; 725 break; 726 case '0': 727 case '1': 728 case '2': 729 case '3': 730 case '4': 731 case '5': 732 case '6': 733 case '7': 734 case '8': 735 case '9': 736 i *= 10; 737 i += (**endptr - '0'); 738 break; 739 default: 740 seconds += i; 741 /* disregard signedness */ 742 return seconds; 743 } 744 } 745 seconds += i; 746 /* disregard signedness */ 747 return seconds; 748 } 749