From: Theo Buehler Subject: rpki-client: move most rfc 3779 parsing code To: tech@openbsd.org Date: Wed, 29 Apr 2026 08:43:23 +0200 This diff moves most of the RFC 3779 extension unpacking into ip.c and as.c and renames the two sbgp_* cert extension handler into cert_* for consistency inside cert.c. Two static cert_*as*() helpers stay there. The cert.c code is complex enough without them, and ip.c and as.c exist for this reason. The sbgp_* prototypes in extern.h are already grouped with the ip_* and as_* functions rather than the cert_* functions. The bulk of the diff is a plain move of code I reworked when migrating rpki-client to templated ASN.1. There is perhaps an argument that the functions should remain close to each other due to their similarity. I can't remember that this ever helped me. I dislike how append_as() and append_ip() rely on the caller many levels down to have allocated enough room, but that's for another round of bikeshedding. Index: as.c =================================================================== RCS file: /cvs/src/usr.sbin/rpki-client/as.c,v diff -u -p -r1.18 as.c --- as.c 9 Apr 2026 07:47:31 -0000 1.18 +++ as.c 29 Apr 2026 06:06:46 -0000 @@ -1,6 +1,7 @@ /* $OpenBSD: as.c,v 1.18 2026/04/09 07:47:31 tb Exp $ */ /* * Copyright (c) 2019 Kristaps Dzonsons + * Copyright (c) 2022 Theo Buehler * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -15,6 +16,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include #include #include #include @@ -154,4 +156,180 @@ as_warn(const char *fn, const char *msg, warnx("%s: corrupt cert", fn); break; } +} + +/* + * Append an AS identifier structure to our list of results. + * Makes sure that the identifiers do not overlap or improperly inherit + * as defined by RFC 3779 section 3.3. + */ +static int +append_as(const char *fn, struct cert_as *ases, size_t *num_ases, + const struct cert_as *as) +{ + if (!as_check_overlap(as, fn, ases, *num_ases, 0)) + return 0; + ases[(*num_ases)++] = *as; + return 1; +} + +/* + * Parse a range of AS identifiers as in 3.2.3.8. + * Returns zero on failure, non-zero on success. + */ +int +sbgp_as_range(const char *fn, struct cert_as *ases, size_t *num_ases, + const ASRange *range) +{ + struct cert_as as; + + memset(&as, 0, sizeof(struct cert_as)); + as.type = CERT_AS_RANGE; + + if (!as_id_parse(range->min, &as.range.min)) { + warnx("%s: RFC 3779 section 3.2.3.8 (via RFC 1930): " + "malformed AS identifier", fn); + return 0; + } + + if (!as_id_parse(range->max, &as.range.max)) { + warnx("%s: RFC 3779 section 3.2.3.8 (via RFC 1930): " + "malformed AS identifier", fn); + return 0; + } + + if (as.range.max == as.range.min) { + warnx("%s: RFC 3379 section 3.2.3.8: ASRange: " + "range is singular", fn); + return 0; + } else if (as.range.max < as.range.min) { + warnx("%s: RFC 3379 section 3.2.3.8: ASRange: " + "range is out of order", fn); + return 0; + } + + return append_as(fn, ases, num_ases, &as); +} + +/* + * Parse an entire 3.2.3.10 integer type. + */ +int +sbgp_as_id(const char *fn, struct cert_as *ases, size_t *num_ases, + const ASN1_INTEGER *i) +{ + struct cert_as as; + + memset(&as, 0, sizeof(struct cert_as)); + as.type = CERT_AS_ID; + + if (!as_id_parse(i, &as.id)) { + warnx("%s: RFC 3779 section 3.2.3.10 (via RFC 1930): " + "malformed AS identifier", fn); + return 0; + } + if (as.id == 0) { + warnx("%s: RFC 3779 section 3.2.3.10 (via RFC 1930): " + "AS identifier zero is reserved", fn); + return 0; + } + + return append_as(fn, ases, num_ases, &as); +} + +static int +sbgp_as_inherit(const char *fn, struct cert_as *ases, size_t *num_ases) +{ + struct cert_as as; + + memset(&as, 0, sizeof(struct cert_as)); + as.type = CERT_AS_INHERIT; + + return append_as(fn, ases, num_ases, &as); +} + +int +sbgp_parse_asids(const char *fn, const ASIdentifiers *asidentifiers, + struct cert_as **out_as, size_t *out_num_ases) +{ + const ASIdOrRanges *aors = NULL; + struct cert_as *as = NULL; + size_t num_ases = 0, num; + int i; + + assert(*out_as == NULL && *out_num_ases == 0); + + if (asidentifiers->rdi != NULL) { + warnx("%s: RFC 6487 section 4.8.11: autonomousSysIds: " + "should not have RDI values", fn); + goto out; + } + + if (asidentifiers->asnum == NULL) { + warnx("%s: RFC 6487 section 4.8.11: autonomousSysIds: " + "no AS number resource set", fn); + goto out; + } + + switch (asidentifiers->asnum->type) { + case ASIdentifierChoice_inherit: + num = 1; + break; + case ASIdentifierChoice_asIdsOrRanges: + aors = asidentifiers->asnum->u.asIdsOrRanges; + num = sk_ASIdOrRange_num(aors); + break; + default: + warnx("%s: RFC 3779 section 3.2.3.2: ASIdentifierChoice: " + "unknown type %d", fn, asidentifiers->asnum->type); + goto out; + } + + if (num == 0) { + warnx("%s: RFC 6487 section 4.8.11: empty asIdsOrRanges", fn); + goto out; + } + if (num >= MAX_AS_SIZE) { + warnx("%s: too many AS number entries: limit %d", + fn, MAX_AS_SIZE); + goto out; + } + as = calloc(num, sizeof(struct cert_as)); + if (as == NULL) + err(1, NULL); + + if (aors == NULL) { + if (!sbgp_as_inherit(fn, as, &num_ases)) + goto out; + } + + for (i = 0; i < sk_ASIdOrRange_num(aors); i++) { + const ASIdOrRange *aor; + + aor = sk_ASIdOrRange_value(aors, i); + switch (aor->type) { + case ASIdOrRange_id: + if (!sbgp_as_id(fn, as, &num_ases, aor->u.id)) + goto out; + break; + case ASIdOrRange_range: + if (!sbgp_as_range(fn, as, &num_ases, aor->u.range)) + goto out; + break; + default: + warnx("%s: RFC 3779 section 3.2.3.5: ASIdOrRange: " + "unknown type %d", fn, aor->type); + goto out; + } + } + + *out_as = as; + *out_num_ases = num_ases; + + return 1; + + out: + free(as); + + return 0; } Index: cert.c =================================================================== RCS file: /cvs/src/usr.sbin/rpki-client/cert.c,v diff -u -p -r1.232 cert.c --- cert.c 7 Apr 2026 10:59:19 -0000 1.232 +++ cert.c 8 Apr 2026 06:08:51 -0000 @@ -1024,217 +1024,12 @@ cert_policies(const char *fn, struct cer } /* - * Append an IP address structure to our list of results, ensuring there is - * at most one inheritance marker per AFI and no overlapping ranges. - */ -static int -append_ip(const char *fn, struct cert_ip *ips, size_t *num_ips, - const struct cert_ip *ip) -{ - if (!ip_addr_check_overlap(ip, fn, ips, *num_ips, 0)) - return 0; - ips[(*num_ips)++] = *ip; - return 1; -} - -/* - * Construct a RFC 3779 2.2.3.8 range from its bit string. - * Returns zero on failure, non-zero on success. - */ -int -sbgp_addr(const char *fn, struct cert_ip *ips, size_t *num_ips, enum afi afi, - const ASN1_BIT_STRING *bs) -{ - struct cert_ip ip; - - memset(&ip, 0, sizeof(struct cert_ip)); - - ip.afi = afi; - ip.type = CERT_IP_ADDR; - - if (!ip_addr_parse(bs, afi, fn, &ip.ip)) { - warnx("%s: RFC 3779 section 2.2.3.8: IPAddress: " - "invalid IP address", fn); - return 0; - } - - if (!ip_cert_compose_ranges(&ip)) { - warnx("%s: RFC 3779 section 2.2.3.8: IPAddress: " - "IP address range reversed", fn); - return 0; - } - - return append_ip(fn, ips, num_ips, &ip); -} - -/* - * Parse RFC 3779 2.2.3.9 range of addresses. - * Returns zero on failure, non-zero on success. - */ -int -sbgp_addr_range(const char *fn, struct cert_ip *ips, size_t *num_ips, - enum afi afi, const IPAddressRange *range) -{ - struct cert_ip ip; - - memset(&ip, 0, sizeof(struct cert_ip)); - - ip.afi = afi; - ip.type = CERT_IP_RANGE; - - if (!ip_addr_parse(range->min, afi, fn, &ip.range.min)) { - warnx("%s: RFC 3779 section 2.2.3.9: IPAddressRange: " - "invalid IP address", fn); - return 0; - } - - if (!ip_addr_parse(range->max, afi, fn, &ip.range.max)) { - warnx("%s: RFC 3779 section 2.2.3.9: IPAddressRange: " - "invalid IP address", fn); - return 0; - } - - if (!ip_cert_compose_ranges(&ip)) { - warnx("%s: RFC 3779 section 2.2.3.9: IPAddressRange: " - "IP address range reversed", fn); - return 0; - } - - return append_ip(fn, ips, num_ips, &ip); -} - -static int -sbgp_addr_inherit(const char *fn, struct cert_ip *ips, size_t *num_ips, - enum afi afi) -{ - struct cert_ip ip; - - memset(&ip, 0, sizeof(struct cert_ip)); - - ip.afi = afi; - ip.type = CERT_IP_INHERIT; - - return append_ip(fn, ips, num_ips, &ip); -} - -int -sbgp_parse_ipaddrblocks(const char *fn, const IPAddrBlocks *addrs, - struct cert_ip **out_ips, size_t *out_num_ips) -{ - const IPAddressFamily *af; - const IPAddressOrRanges *aors; - const IPAddressOrRange *aor; - enum afi afi; - struct cert_ip *ips = NULL; - size_t num_ips = 0, num; - int ipv4_seen = 0, ipv6_seen = 0; - int i, j, addrsz; - - assert(*out_ips == NULL && *out_num_ips == 0); - - addrsz = sk_IPAddressFamily_num(addrs); - if (addrsz != 1 && addrsz != 2) { - warnx("%s: RFC 6487 section 4.8.10: unexpected number of " - "ipAddrBlocks (got %d, expected 1 or 2)", fn, addrsz); - goto out; - } - - for (i = 0; i < addrsz; i++) { - af = sk_IPAddressFamily_value(addrs, i); - - switch (af->ipAddressChoice->type) { - case IPAddressChoice_inherit: - aors = NULL; - num = num_ips + 1; - break; - case IPAddressChoice_addressesOrRanges: - aors = af->ipAddressChoice->u.addressesOrRanges; - num = num_ips + sk_IPAddressOrRange_num(aors); - break; - default: - warnx("%s: RFC 3779: IPAddressChoice: unknown type %d", - fn, af->ipAddressChoice->type); - goto out; - } - if (num == num_ips) { - warnx("%s: RFC 6487 section 4.8.10: " - "empty ipAddressesOrRanges", fn); - goto out; - } - - if (num >= MAX_IP_SIZE) - goto out; - ips = recallocarray(ips, num_ips, num, sizeof(struct cert_ip)); - if (ips == NULL) - err(1, NULL); - - if (!ip_addr_afi_parse(fn, af->addressFamily, &afi)) { - warnx("%s: RFC 3779: invalid AFI", fn); - goto out; - } - - switch (afi) { - case AFI_IPV4: - if (ipv4_seen++ > 0) { - warnx("%s: RFC 6487 section 4.8.10: " - "IPv4 appears twice", fn); - goto out; - } - break; - case AFI_IPV6: - if (ipv6_seen++ > 0) { - warnx("%s: RFC 6487 section 4.8.10: " - "IPv6 appears twice", fn); - goto out; - } - break; - } - - if (aors == NULL) { - if (!sbgp_addr_inherit(fn, ips, &num_ips, afi)) - goto out; - continue; - } - - for (j = 0; j < sk_IPAddressOrRange_num(aors); j++) { - aor = sk_IPAddressOrRange_value(aors, j); - switch (aor->type) { - case IPAddressOrRange_addressPrefix: - if (!sbgp_addr(fn, ips, &num_ips, afi, - aor->u.addressPrefix)) - goto out; - break; - case IPAddressOrRange_addressRange: - if (!sbgp_addr_range(fn, ips, &num_ips, afi, - aor->u.addressRange)) - goto out; - break; - default: - warnx("%s: RFC 3779: IPAddressOrRange: " - "unknown type %d", fn, aor->type); - goto out; - } - } - } - - *out_ips = ips; - *out_num_ips = num_ips; - - return 1; - - out: - free(ips); - - return 0; -} - -/* * Parse an IP Resources X.509v3 extension, RFC 6487 4.8.10, with * syntax documented in RFC 3779 starting in section 2.2. * Returns zero on failure, non-zero on success. */ static int -sbgp_ipaddrblocks(const char *fn, struct cert *cert, const X509_EXTENSION *ext) +cert_ipaddrblocks(const char *fn, struct cert *cert, const X509_EXTENSION *ext) { IPAddrBlocks *addrs = NULL; int rc = 0; @@ -1266,96 +1061,6 @@ sbgp_ipaddrblocks(const char *fn, struct return rc; } -/* - * Append an AS identifier structure to our list of results. - * Makes sure that the identifiers do not overlap or improperly inherit - * as defined by RFC 3779 section 3.3. - */ -static int -append_as(const char *fn, struct cert_as *ases, size_t *num_ases, - const struct cert_as *as) -{ - if (!as_check_overlap(as, fn, ases, *num_ases, 0)) - return 0; - ases[(*num_ases)++] = *as; - return 1; -} - -/* - * Parse a range of AS identifiers as in 3.2.3.8. - * Returns zero on failure, non-zero on success. - */ -int -sbgp_as_range(const char *fn, struct cert_as *ases, size_t *num_ases, - const ASRange *range) -{ - struct cert_as as; - - memset(&as, 0, sizeof(struct cert_as)); - as.type = CERT_AS_RANGE; - - if (!as_id_parse(range->min, &as.range.min)) { - warnx("%s: RFC 3779 section 3.2.3.8 (via RFC 1930): " - "malformed AS identifier", fn); - return 0; - } - - if (!as_id_parse(range->max, &as.range.max)) { - warnx("%s: RFC 3779 section 3.2.3.8 (via RFC 1930): " - "malformed AS identifier", fn); - return 0; - } - - if (as.range.max == as.range.min) { - warnx("%s: RFC 3379 section 3.2.3.8: ASRange: " - "range is singular", fn); - return 0; - } else if (as.range.max < as.range.min) { - warnx("%s: RFC 3379 section 3.2.3.8: ASRange: " - "range is out of order", fn); - return 0; - } - - return append_as(fn, ases, num_ases, &as); -} - -/* - * Parse an entire 3.2.3.10 integer type. - */ -int -sbgp_as_id(const char *fn, struct cert_as *ases, size_t *num_ases, - const ASN1_INTEGER *i) -{ - struct cert_as as; - - memset(&as, 0, sizeof(struct cert_as)); - as.type = CERT_AS_ID; - - if (!as_id_parse(i, &as.id)) { - warnx("%s: RFC 3779 section 3.2.3.10 (via RFC 1930): " - "malformed AS identifier", fn); - return 0; - } - if (as.id == 0) { - warnx("%s: RFC 3779 section 3.2.3.10 (via RFC 1930): " - "AS identifier zero is reserved", fn); - return 0; - } - - return append_as(fn, ases, num_ases, &as); -} - -static int -sbgp_as_inherit(const char *fn, struct cert_as *ases, size_t *num_ases) -{ - struct cert_as as; - - memset(&as, 0, sizeof(struct cert_as)); - as.type = CERT_AS_INHERIT; - - return append_as(fn, ases, num_ases, &as); -} - static int cert_as_inherit(const struct cert *cert) { @@ -1374,99 +1079,13 @@ cert_has_one_as(const struct cert *cert) return cert->ases[0].type == CERT_AS_ID; } -int -sbgp_parse_asids(const char *fn, const ASIdentifiers *asidentifiers, - struct cert_as **out_as, size_t *out_num_ases) -{ - const ASIdOrRanges *aors = NULL; - struct cert_as *as = NULL; - size_t num_ases = 0, num; - int i; - - assert(*out_as == NULL && *out_num_ases == 0); - - if (asidentifiers->rdi != NULL) { - warnx("%s: RFC 6487 section 4.8.11: autonomousSysIds: " - "should not have RDI values", fn); - goto out; - } - - if (asidentifiers->asnum == NULL) { - warnx("%s: RFC 6487 section 4.8.11: autonomousSysIds: " - "no AS number resource set", fn); - goto out; - } - - switch (asidentifiers->asnum->type) { - case ASIdentifierChoice_inherit: - num = 1; - break; - case ASIdentifierChoice_asIdsOrRanges: - aors = asidentifiers->asnum->u.asIdsOrRanges; - num = sk_ASIdOrRange_num(aors); - break; - default: - warnx("%s: RFC 3779 section 3.2.3.2: ASIdentifierChoice: " - "unknown type %d", fn, asidentifiers->asnum->type); - goto out; - } - - if (num == 0) { - warnx("%s: RFC 6487 section 4.8.11: empty asIdsOrRanges", fn); - goto out; - } - if (num >= MAX_AS_SIZE) { - warnx("%s: too many AS number entries: limit %d", - fn, MAX_AS_SIZE); - goto out; - } - as = calloc(num, sizeof(struct cert_as)); - if (as == NULL) - err(1, NULL); - - if (aors == NULL) { - if (!sbgp_as_inherit(fn, as, &num_ases)) - goto out; - } - - for (i = 0; i < sk_ASIdOrRange_num(aors); i++) { - const ASIdOrRange *aor; - - aor = sk_ASIdOrRange_value(aors, i); - switch (aor->type) { - case ASIdOrRange_id: - if (!sbgp_as_id(fn, as, &num_ases, aor->u.id)) - goto out; - break; - case ASIdOrRange_range: - if (!sbgp_as_range(fn, as, &num_ases, aor->u.range)) - goto out; - break; - default: - warnx("%s: RFC 3779 section 3.2.3.5: ASIdOrRange: " - "unknown type %d", fn, aor->type); - goto out; - } - } - - *out_as = as; - *out_num_ases = num_ases; - - return 1; - - out: - free(as); - - return 0; -} - /* * Parse an AS Resources X.509v3 extension, RFC 6487 4.8.11, with * syntax documented in RFC 3779 starting in section 3.2. * Returns zero on failure, non-zero on success. */ static int -sbgp_asids(const char *fn, struct cert *cert, const X509_EXTENSION *ext) +cert_asids(const char *fn, struct cert *cert, const X509_EXTENSION *ext) { ASIdentifiers *asidentifiers = NULL; int rc = 0; @@ -1587,13 +1206,13 @@ cert_parse_extensions(const char *fn, st case NID_sbgp_ipAddrBlock: if (ip++ > 0) goto dup; - if (!sbgp_ipaddrblocks(fn, cert, ext)) + if (!cert_ipaddrblocks(fn, cert, ext)) goto out; break; case NID_sbgp_autonomousSysNum: if (as++ > 0) goto dup; - if (!sbgp_asids(fn, cert, ext)) + if (!cert_asids(fn, cert, ext)) goto out; break; default: Index: ip.c =================================================================== RCS file: /cvs/src/usr.sbin/rpki-client/ip.c,v diff -u -p -r1.37 ip.c --- ip.c 4 Dec 2025 06:11:44 -0000 1.37 +++ ip.c 29 Apr 2026 06:06:27 -0000 @@ -1,6 +1,7 @@ /* $OpenBSD: ip.c,v 1.37 2025/12/04 06:11:44 tb Exp $ */ /* * Copyright (c) 2019 Kristaps Dzonsons + * Copyright (c) 2022 Theo Buehler * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -18,6 +19,7 @@ #include #include +#include #include #include #include @@ -366,4 +368,209 @@ ip_warn(const char *fn, const char *msg, warnx("%s: corrupt cert", fn); break; } +} + +/* + * Append an IP address structure to our list of results, ensuring there is + * at most one inheritance marker per AFI and no overlapping ranges. + */ +static int +append_ip(const char *fn, struct cert_ip *ips, size_t *num_ips, + const struct cert_ip *ip) +{ + if (!ip_addr_check_overlap(ip, fn, ips, *num_ips, 0)) + return 0; + ips[(*num_ips)++] = *ip; + return 1; +} + +/* + * Construct a RFC 3779 2.2.3.8 range from its bit string. + * Returns zero on failure, non-zero on success. + */ +int +sbgp_addr(const char *fn, struct cert_ip *ips, size_t *num_ips, enum afi afi, + const ASN1_BIT_STRING *bs) +{ + struct cert_ip ip; + + memset(&ip, 0, sizeof(struct cert_ip)); + + ip.afi = afi; + ip.type = CERT_IP_ADDR; + + if (!ip_addr_parse(bs, afi, fn, &ip.ip)) { + warnx("%s: RFC 3779 section 2.2.3.8: IPAddress: " + "invalid IP address", fn); + return 0; + } + + if (!ip_cert_compose_ranges(&ip)) { + warnx("%s: RFC 3779 section 2.2.3.8: IPAddress: " + "IP address range reversed", fn); + return 0; + } + + return append_ip(fn, ips, num_ips, &ip); +} + +/* + * Parse RFC 3779 2.2.3.9 range of addresses. + * Returns zero on failure, non-zero on success. + */ +int +sbgp_addr_range(const char *fn, struct cert_ip *ips, size_t *num_ips, + enum afi afi, const IPAddressRange *range) +{ + struct cert_ip ip; + + memset(&ip, 0, sizeof(struct cert_ip)); + + ip.afi = afi; + ip.type = CERT_IP_RANGE; + + if (!ip_addr_parse(range->min, afi, fn, &ip.range.min)) { + warnx("%s: RFC 3779 section 2.2.3.9: IPAddressRange: " + "invalid IP address", fn); + return 0; + } + + if (!ip_addr_parse(range->max, afi, fn, &ip.range.max)) { + warnx("%s: RFC 3779 section 2.2.3.9: IPAddressRange: " + "invalid IP address", fn); + return 0; + } + + if (!ip_cert_compose_ranges(&ip)) { + warnx("%s: RFC 3779 section 2.2.3.9: IPAddressRange: " + "IP address range reversed", fn); + return 0; + } + + return append_ip(fn, ips, num_ips, &ip); +} + +static int +sbgp_addr_inherit(const char *fn, struct cert_ip *ips, size_t *num_ips, + enum afi afi) +{ + struct cert_ip ip; + + memset(&ip, 0, sizeof(struct cert_ip)); + + ip.afi = afi; + ip.type = CERT_IP_INHERIT; + + return append_ip(fn, ips, num_ips, &ip); +} + +int +sbgp_parse_ipaddrblocks(const char *fn, const IPAddrBlocks *addrs, + struct cert_ip **out_ips, size_t *out_num_ips) +{ + const IPAddressFamily *af; + const IPAddressOrRanges *aors; + const IPAddressOrRange *aor; + enum afi afi; + struct cert_ip *ips = NULL; + size_t num_ips = 0, num; + int ipv4_seen = 0, ipv6_seen = 0; + int i, j, addrsz; + + assert(*out_ips == NULL && *out_num_ips == 0); + + addrsz = sk_IPAddressFamily_num(addrs); + if (addrsz != 1 && addrsz != 2) { + warnx("%s: RFC 6487 section 4.8.10: unexpected number of " + "ipAddrBlocks (got %d, expected 1 or 2)", fn, addrsz); + goto out; + } + + for (i = 0; i < addrsz; i++) { + af = sk_IPAddressFamily_value(addrs, i); + + switch (af->ipAddressChoice->type) { + case IPAddressChoice_inherit: + aors = NULL; + num = num_ips + 1; + break; + case IPAddressChoice_addressesOrRanges: + aors = af->ipAddressChoice->u.addressesOrRanges; + num = num_ips + sk_IPAddressOrRange_num(aors); + break; + default: + warnx("%s: RFC 3779: IPAddressChoice: unknown type %d", + fn, af->ipAddressChoice->type); + goto out; + } + if (num == num_ips) { + warnx("%s: RFC 6487 section 4.8.10: " + "empty ipAddressesOrRanges", fn); + goto out; + } + + if (num >= MAX_IP_SIZE) + goto out; + ips = recallocarray(ips, num_ips, num, sizeof(struct cert_ip)); + if (ips == NULL) + err(1, NULL); + + if (!ip_addr_afi_parse(fn, af->addressFamily, &afi)) { + warnx("%s: RFC 3779: invalid AFI", fn); + goto out; + } + + switch (afi) { + case AFI_IPV4: + if (ipv4_seen++ > 0) { + warnx("%s: RFC 6487 section 4.8.10: " + "IPv4 appears twice", fn); + goto out; + } + break; + case AFI_IPV6: + if (ipv6_seen++ > 0) { + warnx("%s: RFC 6487 section 4.8.10: " + "IPv6 appears twice", fn); + goto out; + } + break; + } + + if (aors == NULL) { + if (!sbgp_addr_inherit(fn, ips, &num_ips, afi)) + goto out; + continue; + } + + for (j = 0; j < sk_IPAddressOrRange_num(aors); j++) { + aor = sk_IPAddressOrRange_value(aors, j); + switch (aor->type) { + case IPAddressOrRange_addressPrefix: + if (!sbgp_addr(fn, ips, &num_ips, afi, + aor->u.addressPrefix)) + goto out; + break; + case IPAddressOrRange_addressRange: + if (!sbgp_addr_range(fn, ips, &num_ips, afi, + aor->u.addressRange)) + goto out; + break; + default: + warnx("%s: RFC 3779: IPAddressOrRange: " + "unknown type %d", fn, aor->type); + goto out; + } + } + } + + *out_ips = ips; + *out_num_ips = num_ips; + + return 1; + + out: + free(ips); + + return 0; }