install.packages("Rcpp") Next, create a directory (perhaps ip2asn for this limited example) and put the following code block into the file txt.cpp (or just use the one you cloned above): // these three includes do a great deal of heavy lifting // by making the necessary structures, functions and macros // available to us for the rest of the code #include #include #include #ifdef __linux__ #include #endif // REF: http://www.nlnetlabs.nl/projects/ldns/ for API info #include // need this for 'wrap()' which *greatly* simplifies dealing // with return values using namespace Rcpp; // the sole function that does all the work. it accepts an // R character vector as input (even though we're only expecting // one string to lookuo) and returns a character vector (one row // of the DNS TXT records) RcppExport SEXP txt(SEXP ipPointer) { ldns_resolver *res = NULL; ldns_rdf *domain = NULL; ldns_pkt *p = NULL; ldns_rr_list *txt = NULL; ldns_status s; ldns_rr *answer; // SEXP passes in an R vector, we need this as a C++ StringVector Rcpp::StringVector ip(ipPointer); // we only passed in one IP address domain = ldns_dname_new_frm_str(ip[0]); if (!domain) { return(R_NilValue) ; } s = ldns_resolver_new_frm_file(&res, NULL); if (s != LDNS_STATUS_OK) { return(R_NilValue) ; } p = ldns_resolver_query(res, domain, LDNS_RR_TYPE_TXT, LDNS_RR_CLASS_IN, LDNS_RD); ldns_rdf_deep_free(domain); // no longer needed if (!p) { return(R_NilValue) ; } // get the TXT record(s) txt = ldns_pkt_rr_list_by_type(p, LDNS_RR_TYPE_TXT, LDNS_SECTION_ANSWER); if (!txt) { ldns_pkt_free(p); ldns_rr_list_deep_free(txt); return(R_NilValue) ; } // get the TXT record (could be more than one, but not for our IP->ASN) answer = ldns_rr_list_rr(txt, 0); // get the TXT record (could be more than one, but not for our IP->ASN) ldns_rdf *rd = ldns_rr_pop_rdf(answer) ; // get the character version via safe copy char *answer_str = ldns_rdf2str(rd) ; // Max TXT record length is 255 chars, but for this example // the Team CYMRU ASN resolver TXT records should never exceed // 80 characters (from bulk analysis of large sets of IPs) char ret[80] ; strlcpy(ret, answer_str, sizeof(ret)) ; Rcpp::StringVector result(1); result[0] = ret ; // clean up memory free(answer_str); ldns_rr_list_deep_free(txt); ldns_pkt_free(p); ldns_resolver_deep_free(res); // return the TXT answer string which is ridiculously // simple even for wonkier structures thanks to `wrap()` return(wrap(result)); } The code is commented pretty well and I won’t be covering all of the nuances of the individual ldns calls. Please note that the function has minimal error checking since it is serving first and foremost as a compact example. The full package version will have all i’s dotted and t’s crossed and I’ll make it a point to show the differences between a “toy” example and production-worthy code when we post the package follow-up. The code flow pattern will be the same for most of these API library mappings: define data types that need to be passed in and returned convert them to structures C/C++ can handle perform your calculations/operations on that converted data clean up after yourself return a value R can handle To compile that code into an object we can use in R, you need to do the following: export PKG_LIBS=`Rscript --vanilla -e 'Rcpp:::LdFlags()'` export PKG_CPPFLAGS=`Rscript --vanilla -e 'Rcpp:::CxxFlags()'` R CMD SHLIB -lldns txt.cpp The export lines setup environment variables that help R/Rcpp know where to look for libraries and define the proper compiler flags for your environment. The last line does the hard work of building the proper compilation and linking directives/commands. All three of them belong in a proper Makefile (or your build system of choice). Again, we’re taking a few shortcuts to make the overall concept a bit more digestible. Complexity coming soon! If the build was successful, you’ll have txt.o and txt.so files in your directory. Now, on to the good bits! Interfacing With R Having a compiled object is all well and good, but we need to be able to access the txt() function from R. It turns out that this part is pretty straightforward. Put the following into a file (perhaps ip2asn.R) or use the gist version: # yes, this (dyn.load) is all it takes to expose the function we # just created to R. and, yes, it's a bit more complicated than # that, but for now bask in the glow of simplicity dyn.load("txt.so") # this function should look more than vaguely familiar # http://dds.ec/blog/posts/2014/Apr/firewall-busting-asn-lookups/ ip2asn" />