Index: configure.ac ================================================================== --- configure.ac +++ configure.ac @@ -107,13 +107,20 @@ AC_DEFINE([NANO_TCL_HAVE_OPENMP], [1], [Define if you have support for OpenMP]) fi dnl Random number generation mechanisms -AC_CHECK_FUNC(getrandom,, [ - AC_CHECK_FUNC(getentropy,, [ - AC_CHECK_FUNC(CryptGenRandom) +AC_CHECK_FUNCS(getrandom,, [ + AC_CHECK_FUNCS(getentropy,, [ + AC_CHECK_FUNCS(CryptGenRandom) + ]) +]) + +dnl Check for name resolution capabilities +AC_CHECK_FUNCS(getaddrinfo, [ + AC_CHECK_FUNCS(getnameinfo, [ + AC_CHECK_HEADERS(sys/types.h sys/socket.h netdb.h) ]) ]) dnl Handle specifying where TCLLIB is, for testing AC_SUBST(TCLLIB_PATH) @@ -156,9 +163,8 @@ TCL_NANO_AMALGAMATION='1' else TCL_NANO_AMALGAMATION='0' fi AC_SUBST(TCL_NANO_AMALGAMATION) - dnl Produce output AC_OUTPUT(Makefile pkgIndex.tcl-${TCLEXT_BUILD} nano.syms) Index: nano.c ================================================================== --- nano.c +++ nano.c @@ -9,10 +9,31 @@ #include #ifdef NANO_TCL_HAVE_OPENMP # include #endif +/* + * We need both getaddrinfo and inet_ntop for DNS resolution + */ +#if defined(HAVE_GETNAMEINFO) && defined(HAVE_GETADDRINFO) +# define NANO_TCL_CAN_RESOLVE_NAMES 1 +#else +# undef NANO_TCL_CAN_RESOLVE_NAMES +#endif + +#ifdef NANO_TCL_CAN_RESOLVE_NAMES +# ifdef HAVE_SYS_SOCKET_H +# include +# endif +# ifdef HAVE_SYS_TYPES_H +# include +# endif +# ifdef HAVE_NETDB_H +# include +# endif +#endif + #include "randombytes.h" #include "monocypher.h" #include "argon2.h" #include "aes.h" @@ -565,10 +586,107 @@ return(TCL_OK); /* NOTREACH */ clientData = clientData; } + +#ifdef NANO_TCL_CAN_RESOLVE_NAMES +static int nano_tcl_resolve_name(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { + Tcl_Encoding ascii_encoding; + Tcl_Obj *list_of_hostnames, *hostname_obj; + struct addrinfo *gai_data, *addr_current; + char *hostname_utf8, hostname[256]; + int hostname_utf8_length, hostname_utf8_length_processed, hostname_length; + int tute_ret, gai_ret, gni_ret; + + if (objc != 2) { + Tcl_WrongNumArgs(interp, 1, objv, "hostname"); + + return(TCL_ERROR); + } + + hostname_utf8 = Tcl_GetStringFromObj(objv[1], &hostname_utf8_length); + if (!hostname_utf8) { + return(TCL_ERROR); + } + + /* + * getaddrinfo() accepts ASCII input, so convert to that encoding + */ + ascii_encoding = Tcl_GetEncoding(interp, "ascii"); + if (ascii_encoding == NULL) { + return(TCL_ERROR); + } + + tute_ret = Tcl_UtfToExternal(interp, ascii_encoding, hostname_utf8, + hostname_utf8_length, TCL_ENCODING_STOPONERROR, NULL, + hostname, sizeof(hostname), + &hostname_utf8_length_processed, + &hostname_length, NULL + ); + + Tcl_FreeEncoding(ascii_encoding); + + if (tute_ret != TCL_OK) { + Tcl_SetResult(interp, "Failed to convert to ASCII", NULL); + + return(TCL_ERROR); + } + + if (hostname_utf8_length_processed != hostname_utf8_length) { + Tcl_SetResult(interp, "Failed to convert entire buffer", NULL); + + return(TCL_ERROR); + } + + gai_ret = getaddrinfo(hostname, NULL, NULL, &gai_data); + if (gai_ret == EAI_NODATA || gai_ret == EAI_NONAME) { + Tcl_SetResult(interp, "", NULL); + + return(TCL_OK); + } + if (gai_ret != 0) { + Tcl_SetResult(interp, (char *) gai_strerror(gai_ret), NULL); + + return(TCL_ERROR); + } + + list_of_hostnames = Tcl_NewObj(); + for (addr_current = gai_data; addr_current; addr_current = addr_current->ai_next) { + if (addr_current->ai_family != AF_INET && addr_current->ai_family != AF_INET6) { + continue; + } + + gni_ret = getnameinfo(addr_current->ai_addr, addr_current->ai_addrlen, + hostname, sizeof(hostname), + NULL, 0, NI_NUMERICHOST + ); + if (gni_ret != 0) { + continue; + } + + hostname_obj = Tcl_NewStringObj(hostname, -1); + + Tcl_ListObjAppendElement(NULL, list_of_hostnames, hostname_obj); + } + + freeaddrinfo(gai_data); + + Tcl_SetObjResult(interp, list_of_hostnames); + + return(TCL_OK); + + /* NOTREACH */ + clientData = clientData; +} +#else +static int nano_tcl_resolve_name(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { + Tcl_SetResult(interp, "Not supported on this platform", NULL); + + return(TCL_ERROR); +} +#endif static int nano_tcl_self_test(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { if (objc != 1) { Tcl_WrongNumArgs(interp, 1, objv, ""); @@ -601,10 +719,11 @@ if (!interp) { return(TCL_OK); } TclNano_CreateNamespace(interp, "::nano"); + TclNano_CreateNamespace(interp, "::nano::internal"); TclNano_CreateNamespace(interp, "::nano::block"); TclNano_CreateNamespace(interp, "::nano::key"); TclNano_CreateNamespace(interp, "::nano::work"); TclNano_SetIntVar(interp, "::nano::block::hashLength", NANO_BLOCK_HASH_LENGTH); @@ -612,10 +731,15 @@ TclNano_SetIntVar(interp, "::nano::key::publicKeyLength", NANO_PUBLIC_KEY_LENGTH); TclNano_SetIntVar(interp, "::nano::key::privateKeyLength", NANO_SECRET_KEY_LENGTH); TclNano_SetIntVar(interp, "::nano::key::seedLength", NANO_SECRET_KEY_LENGTH); TclNano_SetIntVar(interp, "::nano::work::workValueLength", NANO_WORK_VALUE_LENGTH); TclNano_SetIntVar(interp, "::nano::work::workHashLength", NANO_WORK_HASH_LENGTH); +#ifdef NANO_TCL_CAN_RESOLVE_NAMES + TclNano_SetIntVar(interp, "::nano::internal::haveResolveName", 1); +#else + TclNano_SetIntVar(interp, "::nano::internal::haveResolveName", 0); +#endif TclNano_CreateObjCommand(interp, "::nano::internal::selfTest", nano_tcl_self_test); TclNano_CreateObjCommand(interp, "::nano::internal::generateKey", nano_tcl_generate_keypair); TclNano_CreateObjCommand(interp, "::nano::internal::generateSeed", nano_tcl_generate_seed); TclNano_CreateObjCommand(interp, "::nano::internal::publicKey", nano_tcl_secret_key_to_public_key); @@ -625,12 +749,13 @@ TclNano_CreateObjCommand(interp, "::nano::internal::deriveKeyFromPassword", nano_tcl_derive_key_from_password); TclNano_CreateObjCommand(interp, "::nano::internal::AES256-CTR", nano_tcl_aes256_ctr); TclNano_CreateObjCommand(interp, "::nano::internal::validateWork", nano_tcl_validate_work); TclNano_CreateObjCommand(interp, "::nano::internal::generateWork", nano_tcl_generate_work); TclNano_CreateObjCommand(interp, "::nano::internal::randomBytes", nano_tcl_random_bytes); + TclNano_CreateObjCommand(interp, "::nano::internal::resolveName", nano_tcl_resolve_name); TclNano_Eval(interp, nanoInitScript); TclNano_PkgProvide(interp, "nano", PACKAGE_VERSION); return(TCL_OK); } Index: nano.tcl ================================================================== --- nano.tcl +++ nano.tcl @@ -28,11 +28,11 @@ namespace eval ::nano::network::client {} namespace eval ::nano::network::server {} namespace eval ::nano::protocol::create {} namespace eval ::nano::protocol::parse {} namespace eval ::nano::protocol::extensions {} -namespace eval ::nano::network::_dns {} +namespace eval ::nano::internal::dns {} namespace eval ::nano::wallet {} namespace eval ::nano::_cli {} # Constants set ::nano::block::genesis(main) {{ @@ -1978,11 +1978,10 @@ } } proc ::nano::node::configure {network args} { package require ip - package require dns # Set default options switch -- $network { "main" { set info(-configDirectory) [file normalize ~/RaiBlocks] @@ -3005,14 +3004,22 @@ } return } -proc ::nano::network::_dns::toIPList {name} { +proc ::nano::internal::dns::resolve {name} { if {[::ip::version $name] > 0} { return [list $name] } + + if {$::nano::internal::haveResolveName} { + set retval [::nano::internal::resolveName $name] + set retval [lsort -unique $retval] + return $retval + } + + package require dns set retval [list] foreach addressType {A AAAA} { set dnsQueryID [::dns::resolve $name -type $addressType] for {set dnsCheck 0} {$dnsCheck < 100} {incr dnsCheck} { @@ -3242,11 +3249,11 @@ set completePeers [list] foreach peer $peers { lassign [::nano::internal::parseAddress $peer $defaultPeerPort] peer peerPort catch { - foreach peer [::nano::network::_dns::toIPList $peer] { + foreach peer [::nano::internal::dns::resolve $peer] { lappend completePeers [dict create address $peer port $peerPort] } } }