Use the schema introduced in the previous commit when inserting ROAs into the database.
WARNING: This is one of a series of commits to use the updated schema. It is not the last in the series, so the code is still broken. addresses [#6] and [#7] --- lib/rpki/cms/roa_general.c | 195 +++++++++++++++++++++--------------------- lib/rpki/cms/roa_utils.h | 33 ++++++-- lib/rpki/scmf.h | 4 +- lib/rpki/sqcon.c | 4 +- lib/rpki/sqhl.c | 206 ++++++++++++++++++++++++++++++++++++++++++--- 5 files changed, 326 insertions(+), 116 deletions(-) diff --git a/lib/rpki/cms/roa_general.c b/lib/rpki/cms/roa_general.c index 9c71f92..e7e48e0 100644 --- a/lib/rpki/cms/roa_general.c +++ b/lib/rpki/cms/roa_general.c @@ -442,125 +442,132 @@ uint32_t roaAS_ID( return (uint32_t)iAS_ID; } -/* - * void roaFree(struct ROA *r) { if (NULL != r) { delete_casn(&(r->self)); - * free((void *)r); } } - */ -static int convertAddr( - int family, - struct ROAIPAddress *ipaddressp, - char *outbuf, - int outbufLth) -{ - memset(outbuf, 0, outbufLth); - uchar abuf[36]; - int addrLth = read_casn(&ipaddressp->address, abuf); - if (family == AF_INET) - { - uint32_t addrVal = 0; - uint32_t xx; - int ii; - if (outbufLth < INET_ADDRSTRLEN + 6) - return ERR_SCM_INVALSZ; - for (ii = 1; ii < addrLth; ii++) - addrVal = (addrVal << 8) + abuf[ii]; - while (ii++ <= (int)sizeof(unsigned int)) - addrVal <<= 8; - xx = htonl(addrVal); - if (!inet_ntop(family, &xx, outbuf, outbufLth)) - return ERR_SCM_INVALIPL; - // prefix length is ((addrLth - 1) * 8) - unused bits - sprintf(&outbuf[strlen(outbuf)], "/%d", ((addrLth - 1) * 8) - abuf[0]); - } - else if (family == AF_INET6) - { - if (outbufLth < INET6_ADDRSTRLEN + 6) - return ERR_SCM_INVALSZ; - uchar addrVal[16]; - memset(addrVal, 0, 16); - memcpy(addrVal, &abuf[1], addrLth - 1); - if (!inet_ntop(family, &addrVal, outbuf, outbufLth)) - return ERR_SCM_INVALIPL; - sprintf(&outbuf[strlen(outbuf)], "/%d", ((addrLth - 1) * 8) - abuf[0]); - } - else - return ERR_SCM_INVALIPB; - - int lth = strlen(outbuf); - if (vsize_casn(&ipaddressp->maxLength)) - { - long j; - read_casn_num(&ipaddressp->maxLength, &j); - sprintf(&outbuf[lth], "(%d)", (int)j); - lth = strlen(outbuf); - } - return lth; -} - -int roaGetIPAddresses( +ssize_t roaGetPrefixes( struct CMS *rp, - char **str) + struct roa_prefix * * prefixes) { struct ROAIPAddrBlocks *addrBlocksp = &rp->content.signedData.encapContentInfo.eContent.roa.ipAddrBlocks; struct ROAIPAddressFamily *famp; - int replysiz = 0, - lth; - char *replyp = NULL; - char tmpbuf[INET6_ADDRSTRLEN + 8]; // to be on safe side - *str = NULL; // in case of failure + + // Actual length of *prefixes + size_t prefixes_length = 0; + + // Allocated length of *prefixes. Start small, but big enough to + // avoid most reallocations. + size_t prefixes_allocated = 16; + + *prefixes = malloc(prefixes_allocated * sizeof(struct roa_prefix)); + if (*prefixes == NULL) + { + return ERR_SCM_NOMEM; + } + for (famp = (struct ROAIPAddressFamily *)member_casn(&addrBlocksp->self, 0); famp; famp = (struct ROAIPAddressFamily *)next_of(&famp->self)) { + // first two bytes are AFI in network byte order, third byte + // is the optional SAFI, fourth byte is ??? uchar famtyp[4]; - if (read_casn(&famp->addressFamily, famtyp) < 0) + // min length of AFI/SAFI is 2 + if (read_casn(&famp->addressFamily, famtyp) < 2) + { + free(*prefixes); + *prefixes = NULL; return -1; - int family; - if (famtyp[1] == 1) - family = AF_INET; - else if (famtyp[1] == 2) - family = AF_INET6; + } + + uint_fast16_t afi = ((uint_fast16_t)famtyp[0] << 8) + famtyp[1]; + uint_fast8_t prefix_family_length; + switch (afi) + { + case 1: + prefix_family_length = 4; + break; + + case 2: + prefix_family_length = 16; + break; + + default: + free(*prefixes); + *prefixes = NULL; + return -1; + } + struct ROAIPAddress *ipaddressp; for (ipaddressp = (struct ROAIPAddress *)member_casn(&famp->addresses.self, 0); ipaddressp; ipaddressp = (struct ROAIPAddress *)next_of(&ipaddressp->self)) { - lth = - convertAddr(family, ipaddressp, tmpbuf, INET6_ADDRSTRLEN + 8); - if (lth < 0) + if (prefixes_length >= prefixes_allocated) { - if (replyp) - free(replyp); - return lth; - } - if (!replysiz) - { // lth + 1 allows for null - if (!(replyp = (char *)calloc(1, lth + 1))) + prefixes_allocated *= 2; + + struct roa_prefix * new_prefixes = realloc( + *prefixes, + prefixes_allocated * sizeof(struct roa_prefix)); + if (new_prefixes == NULL) + { + free(*prefixes); + *prefixes = NULL; return ERR_SCM_NOMEM; - strcpy(replyp, tmpbuf); - replysiz = lth; // size without null + } + *prefixes = new_prefixes; + } + + (*prefixes)[prefixes_length].prefix_family_length = + prefix_family_length; + + // Buffer for a single IP prefix, stored as a BIT STRING. + // The first byte will hold the number of unused (padding) + // bits in the last byte. + /** + * \todo change the magic 16 to a symbolic constant (it's + * the maximum address length of all supported address + * families, in bytes) + */ + uint8_t prefix_buf[1+16]; + + int prefix_buflen = + read_casn(&ipaddressp->address, prefix_buf); + memset((*prefixes)[prefixes_length].prefix, 0, + prefix_family_length); + memcpy((*prefixes)[prefixes_length].prefix, + prefix_buf + 1, prefix_buflen - 1); + + (*prefixes)[prefixes_length].prefix_length = + ((prefix_buflen - 1) * 8) - prefix_buf[0]; + + int vsize = vsize_casn(&ipaddressp->maxLength); + if (vsize < 0) + { + free(*prefixes); + *prefixes = NULL; + return -1; + } + else if (vsize > 0) + { + long prefix_max_length; + read_casn_num(&ipaddressp->maxLength, + &prefix_max_length); + (*prefixes)[prefixes_length].prefix_max_length = + prefix_max_length; } else { - char *tmpp = (char *)realloc(replyp, replysiz + lth + 3); - if (!tmpp) - { - free(replyp); - return ERR_SCM_NOMEM;; - } - replyp = tmpp; - char *b = &replyp[replysiz]; - *b++ = ','; - *b++ = ' '; - strcpy(b, tmpbuf); - replysiz += lth + 2; // without null + // Default max length is equal to the prefix length + (*prefixes)[prefixes_length].prefix_max_length = + (*prefixes)[prefixes_length].prefix_length; } + + ++prefixes_length; } } - *str = replyp; - return 0; + + return prefixes_length; } int roaGenerateFilter( diff --git a/lib/rpki/cms/roa_utils.h b/lib/rpki/cms/roa_utils.h index 6c4ddc9..740f367 100644 --- a/lib/rpki/cms/roa_utils.h +++ b/lib/rpki/cms/roa_utils.h @@ -165,13 +165,36 @@ int roaGenerateFilter2( struct CMS *r, char **str); -/* - * Fills the IP addresses assigned by a ROA into a multiline string, where - * each line has the form address/prefix_length[/max_prefix_len] +/** + * @brief Represent a prefix from a ROA. + */ +struct roa_prefix +{ + /** + * @brief Length of #prefix, either 4 for IPv4 or 16 for IPv6. + */ + uint8_t prefix_family_length; + + uint8_t prefix[16]; + + uint8_t prefix_length; + + uint8_t prefix_max_length; +}; + +/** + * @brief Extract the prefixes from a ROA. + * + * @param[in] r The ROA. + * @param[out] prefixes On success, *prefixes will be set to point to + * an array of roa prefixes. This value must be free()ed by the + * caller. On failure, *prefixes will be set to NULL. + * @return On success, the number of prefixes. On failure, a negative + * number. */ -int roaGetIPAddresses( +ssize_t roaGetPrefixes( struct CMS *r, - char **str); + struct roa_prefix * * prefixes); /* * This utility function extracts the SKI from a ROA and formats it in the diff --git a/lib/rpki/scmf.h b/lib/rpki/scmf.h index 45aed30..2a2bea5 100644 --- a/lib/rpki/scmf.h +++ b/lib/rpki/scmf.h @@ -131,14 +131,14 @@ extern void freesrchscm( scmsrcha * srch); extern void *unhexify( int strnglen, - char *strng); + char const * strng); extern char *geterrorscm( scmcon * conp); extern char *gettablescm( scmcon * conp); extern char *hexify( int bytelen, - void *bytes, + void const * bytes, int useox); extern int getrowsscm( scmcon * conp); diff --git a/lib/rpki/sqcon.c b/lib/rpki/sqcon.c index 2a08ba8..c835c78 100644 --- a/lib/rpki/sqcon.c +++ b/lib/rpki/sqcon.c @@ -1496,7 +1496,7 @@ int setflagsscm( char *hexify( int bytelen, - void *ptr, + void const * ptr, int useox) { unsigned char *inptr; @@ -1551,7 +1551,7 @@ char *hexify( void *unhexify( int strnglen, - char *strng) + char const * strng) { unsigned char *oot; unsigned int x; diff --git a/lib/rpki/sqhl.c b/lib/rpki/sqhl.c index 5dc019a..91fda49 100644 --- a/lib/rpki/sqhl.c +++ b/lib/rpki/sqhl.c @@ -39,6 +39,7 @@ static scmtab *theCertTable = NULL; static scmtab *theROATable = NULL; +static scmtab *theROAPrefixTable = NULL; static scmtab *theCRLTable = NULL; static scmtab *theManifestTable = NULL; static scmtab *theGBRTable = NULL; @@ -89,6 +90,12 @@ static void initTables( LOG(LOG_ERR, "Error finding roa table"); exit(-1); } + theROAPrefixTable = findtablescm(scmp, "ROA_PREFIX"); + if (theROAPrefixTable == NULL) + { + LOG(LOG_ERR, "Error finding roa_prefix table"); + exit(-1); + } theManifestTable = findtablescm(scmp, "MANIFEST"); if (theManifestTable == NULL) { @@ -2965,13 +2972,14 @@ static int add_roa_internal( unsigned int dirid, char *ski, uint32_t asid, - char *ip_addrs, + size_t prefixes_length, + struct roa_prefix const * prefixes, char *sig, unsigned int flags) { unsigned int roa_id = 0; scmkva aone; - scmkv cols[8]; + scmkv cols[7]; char flagn[24]; char asn[24]; char lid[24]; @@ -2979,15 +2987,31 @@ static int add_roa_internal( int idx = 0; int sta; + // Buffer to hold a potentially large INSERT statement. This is + // used to insert multiple rows per statement into + // rpki_roa_prefix. + size_t const multiinsert_len = 32 * 1024; + char * multiinsert = malloc(multiinsert_len); + if (multiinsert == NULL) + { + return ERR_SCM_NOMEM; + } + initTables(scmp); conp->mystat.tabname = "ROA"; // first check for a duplicate signature sta = dupsigscm(scmp, conp, theROATable, sig); if (sta < 0) + { + free(multiinsert); return (sta); + } sta = getmaxidscm(scmp, conp, "local_id", theROATable, &roa_id); if (sta < 0) + { + free(multiinsert); return (sta); + } roa_id++; // fill in insertion structure cols[idx].column = "filename"; @@ -2999,8 +3023,6 @@ static int add_roa_internal( cols[idx++].value = ski; cols[idx].column = "sig"; cols[idx++].value = sig; - cols[idx].column = "ip_addrs"; - cols[idx++].value = ip_addrs; (void)snprintf(asn, sizeof(asn), "%" PRIu32, asid); cols[idx].column = "asn"; cols[idx++].value = asn; @@ -3011,11 +3033,164 @@ static int add_roa_internal( cols[idx].column = "local_id"; cols[idx++].value = lid; aone.vec = &cols[0]; - aone.ntot = 8; + aone.ntot = sizeof(cols)/sizeof(cols[0]); aone.nused = idx; aone.vald = 0; // add the ROA sta = insertscm(conp, theROATable, &aone); + if (sta < 0) + { + free(multiinsert); + return sta; + } + + // Prefix for the insert statement that inserts multiple rows + // into rpki_roa_prefix. + static char const multiinsert_pre[] = + "INSERT INTO rpki_roa_prefix " + "(roa_local_id, prefix, prefix_length, prefix_max_length) " + "VALUES "; + static size_t const multiinsert_pre_len = + sizeof(multiinsert_pre) - 1; + + // Index into multiinsert where the next character should be + // written. + size_t multiinsert_idx = 0; + + // String to represent a prefix as VARBINARY in SQL. + char * prefix; + + int snprintf_ret; + + size_t i; + for (i = 0; i < prefixes_length; ++i) + { + if (multiinsert_idx == 0) + { + memcpy(multiinsert, multiinsert_pre, multiinsert_pre_len); + multiinsert_idx += multiinsert_pre_len; + } + + prefix = hexify( + prefixes[i].prefix_family_length, prefixes[i].prefix, + HEXIFY_X); + if (prefix == NULL) + { + sta = ERR_SCM_NOMEM; + goto done; + } + + snprintf_ret = snprintf( + multiinsert + multiinsert_idx, + multiinsert_len - multiinsert_idx, + "(%u, %s, %" PRIu8 ", %" PRIu8 "),", + roa_id, + prefix, + prefixes[i].prefix_length, + prefixes[i].prefix_max_length); + + free(prefix); + prefix = NULL; + + if (snprintf_ret < 0) + { + sta = ERR_SCM_INTERNAL; + goto done; + } + else if ((size_t)snprintf_ret >= + multiinsert_len - multiinsert_idx) + { + // The above write was truncated. + + if (multiinsert_idx == multiinsert_pre_len) + { + // The write was truncated even though it's the first + // prefix in this statement. That's an internal error + // because multiinsert_len is too small. + sta = ERR_SCM_INTERNAL; + goto done; + } + + // Decrement i to ensure this prefix gets inserted + // eventually. + --i; + + // Overwrite the ',' from the previous prefix and + // terminate the statement. + --multiinsert_idx; + multiinsert[multiinsert_idx] = '\0'; + } + else if (i >= prefixes_length - 1) + { + // The write was not truncated, but this is the last + // prefix, so overwrite the ',' from this prefix and + // terminate the statement. + multiinsert_idx += snprintf_ret - 1; + multiinsert[multiinsert_idx] = '\0'; + } + else + { + // The above write was not truncated and this is not the + // last prefix, so attempt to add more prefixes before + // executing the statement. + multiinsert_idx += snprintf_ret; + continue; + } + + // Perform the insert. + sta = statementscm_no_data(conp, multiinsert); + if (sta < 0) + { + LOG(LOG_ERR, + "Error inserting ROA prefixes. SQL query: %s", + multiinsert); + goto done; + } + + // Start the statement at the beginning again with the next + // prefix. + multiinsert_idx = 0; + } + +done: + + if (sta < 0) + { + // There was an error, so delete the ROA we just inserted. + int delete_status; + + delete_status = deletescm(conp, theROATable, &aone); + if (delete_status < 0) + { + LOG(LOG_ERR, + "Error deleting row from rpki_roa: %s (%d)", + err2string(delete_status), delete_status); + } + + // Then delete the prefixes, if they weren't already deleted + // by the foreign key constraints. + scmkva roa_prefixes_aone; + scmkv roa_prefixes_cols[1]; + roa_prefixes_cols[0].column = "roa_local_id"; + roa_prefixes_cols[0].value = lid; + roa_prefixes_aone.vec = &cols[0]; + roa_prefixes_aone.ntot = + sizeof(roa_prefixes_cols)/sizeof(roa_prefixes_cols[0]); + roa_prefixes_aone.nused = roa_prefixes_aone.ntot; + roa_prefixes_aone.vald = 0; + delete_status = + deletescm(conp, theROAPrefixTable, &roa_prefixes_aone); + if (delete_status < 0) + { + LOG(LOG_ERR, + "Error deleting from rpki_roa_prefix with " + "roa_local_id %s: %s (%d)", + lid, err2string(delete_status), delete_status); + } + } + + free(multiinsert); + return (sta); } @@ -3037,8 +3212,9 @@ int add_roa( struct CMS roa; // note: roaFromFile constructs this char ski[60], *sig = NULL, - certfilename[PATH_MAX], - *ip_addrs = NULL; + certfilename[PATH_MAX]; + size_t prefixes_length = 0; + struct roa_prefix * prefixes = NULL; unsigned char *bsig = NULL; int sta, chainOK, @@ -3086,24 +3262,28 @@ int add_roa( if ((sta = verify_roa(conp, &roa, ski, &chainOK)) != 0) break; - // ip_addrs - sta = roaGetIPAddresses(&roa, &ip_addrs); - if (sta != 0) + // prefixes + ssize_t prefixes_ret = roaGetPrefixes(&roa, &prefixes); + if (prefixes_ret < 0) + { break; + } + prefixes_length = prefixes_ret; + sta = addStateToFlags(&flags, chainOK, outfile, outfull, scmp, conp); if (sta != 0) break; // add to database - sta = add_roa_internal(scmp, conp, outfile, id, ski, asid, ip_addrs, - sig, flags); + sta = add_roa_internal(scmp, conp, outfile, id, ski, asid, + prefixes_length, prefixes, sig, flags); if (sta < 0) break; } while (0); // clean up - free(ip_addrs); + free(prefixes); if (sta != 0 && cert_added) (void)delete_object(scmp, conp, certfilename, outdir, outfull, (unsigned int)0); -- 1.9.1 ------------------------------------------------------------------------------ _______________________________________________ rpstir-devel mailing list rpstir-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/rpstir-devel