diff --git a/Makefile.am b/Makefile.am index 9f9c58848..4bd373e14 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,241 +1,245 @@ # Copyright (c) 2013-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. ACLOCAL_AMFLAGS = -I build-aux/m4 SUBDIRS = src if ENABLE_MAN SUBDIRS += doc/man endif export PYTHONPATH .PHONY: deploy FORCE GZIP_ENV="-9n" if BUILD_BITCOIN_LIBS pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = libbitcoinconsensus.pc endif BITCOIND_BIN=$(top_builddir)/src/$(BITCOIN_DAEMON_NAME)$(EXEEXT) BITCOIN_QT_BIN=$(top_builddir)/src/qt/$(BITCOIN_GUI_NAME)$(EXEEXT) BITCOIN_CLI_BIN=$(top_builddir)/src/$(BITCOIN_CLI_NAME)$(EXEEXT) +BITCOIN_SEEDER_BIN=$(top_builddir)/src/$(BITCOIN_SEEDER_NAME)$(EXEEXT) BITCOIN_WIN_INSTALLER=$(PACKAGE)-$(PACKAGE_VERSION)-win$(WINDOWS_BITS)-setup$(EXEEXT) empty := space := $(empty) $(empty) OSX_APP=BitcoinABC-Qt.app OSX_VOLNAME = $(subst $(space),-,$(PACKAGE_NAME)) OSX_DMG = $(OSX_VOLNAME).dmg OSX_BACKGROUND_SVG=background.svg OSX_BACKGROUND_IMAGE=background.tiff OSX_BACKGROUND_IMAGE_DPIS=36 72 OSX_DSSTORE_GEN=$(top_srcdir)/contrib/macdeploy/custom_dsstore.py OSX_DEPLOY_SCRIPT=$(top_srcdir)/contrib/macdeploy/macdeployqtplus OSX_FANCY_PLIST=$(top_srcdir)/contrib/macdeploy/fancy.plist OSX_INSTALLER_ICONS=$(top_srcdir)/src/qt/res/icons/bitcoin.icns OSX_PLIST=$(top_builddir)/share/qt/Info.plist #not installed OSX_QT_TRANSLATIONS = da,de,es,hu,ru,uk,zh_CN,zh_TW DIST_DOCS = $(wildcard doc/*.md) $(wildcard doc/release-notes/*.md) DIST_CONTRIB = $(top_srcdir)/contrib/bitcoin-cli.bash-completion \ $(top_srcdir)/contrib/bitcoin-tx.bash-completion \ $(top_srcdir)/contrib/bitcoind.bash-completion \ $(top_srcdir)/contrib/init \ $(top_srcdir)/contrib/rpm BIN_CHECKS=$(top_srcdir)/contrib/devtools/symbol-check.py \ $(top_srcdir)/contrib/devtools/security-check.py WINDOWS_PACKAGING = $(top_srcdir)/share/pixmaps/bitcoin.ico \ $(top_srcdir)/share/pixmaps/nsis-header.bmp \ $(top_srcdir)/share/pixmaps/nsis-wizard.bmp \ $(top_srcdir)/doc/README_windows.txt OSX_PACKAGING = $(OSX_DEPLOY_SCRIPT) $(OSX_FANCY_PLIST) $(OSX_INSTALLER_ICONS) \ $(top_srcdir)/contrib/macdeploy/$(OSX_BACKGROUND_SVG) \ $(OSX_DSSTORE_GEN) \ $(top_srcdir)/contrib/macdeploy/detached-sig-apply.sh \ $(top_srcdir)/contrib/macdeploy/detached-sig-create.sh COVERAGE_INFO = baseline_filtered_combined.info baseline.info \ leveldb_baseline.info test_bitcoin_filtered.info total_coverage.info \ baseline_filtered.info rpc_test.info rpc_test_filtered.info \ leveldb_baseline_filtered.info test_bitcoin_coverage.info test_bitcoin.info dist-hook: -$(GIT) archive --format=tar HEAD -- src/clientversion.cpp | $(AMTAR) -C $(top_distdir) -xf - $(BITCOIN_WIN_INSTALLER): all-recursive $(MKDIR_P) $(top_builddir)/release STRIPPROG="$(STRIP)" $(INSTALL_STRIP_PROGRAM) $(BITCOIND_BIN) $(top_builddir)/release STRIPPROG="$(STRIP)" $(INSTALL_STRIP_PROGRAM) $(BITCOIN_QT_BIN) $(top_builddir)/release STRIPPROG="$(STRIP)" $(INSTALL_STRIP_PROGRAM) $(BITCOIN_CLI_BIN) $(top_builddir)/release @test -f $(MAKENSIS) && $(MAKENSIS) -V2 $(top_builddir)/share/setup.nsi || \ echo error: could not build $@ @echo built $@ $(if $(findstring src/,$(MAKECMDGOALS)),$(MAKECMDGOALS), none): FORCE $(MAKE) -C src $(patsubst src/%,%,$@) $(OSX_APP)/Contents/PkgInfo: $(MKDIR_P) $(@D) @echo "APPL????" > $@ $(OSX_APP)/Contents/Resources/empty.lproj: $(MKDIR_P) $(@D) @touch $@ $(OSX_APP)/Contents/Info.plist: $(OSX_PLIST) $(MKDIR_P) $(@D) $(INSTALL_DATA) $< $@ $(OSX_APP)/Contents/Resources/bitcoin.icns: $(OSX_INSTALLER_ICONS) $(MKDIR_P) $(@D) $(INSTALL_DATA) $< $@ $(OSX_APP)/Contents/MacOS/BitcoinABC-Qt: $(BITCOIN_QT_BIN) $(MKDIR_P) $(@D) STRIPPROG="$(STRIP)" $(INSTALL_STRIP_PROGRAM) $< $@ $(OSX_APP)/Contents/Resources/Base.lproj/InfoPlist.strings: $(MKDIR_P) $(@D) echo '{ CFBundleDisplayName = "$(PACKAGE_NAME)"; CFBundleName = "$(PACKAGE_NAME)"; }' > $@ OSX_APP_BUILT=$(OSX_APP)/Contents/PkgInfo $(OSX_APP)/Contents/Resources/empty.lproj \ $(OSX_APP)/Contents/Resources/bitcoin.icns $(OSX_APP)/Contents/Info.plist \ $(OSX_APP)/Contents/MacOS/BitcoinABC-Qt $(OSX_APP)/Contents/Resources/Base.lproj/InfoPlist.strings osx_volname: echo $(OSX_VOLNAME) >$@ if BUILD_DARWIN $(OSX_DMG): $(OSX_APP_BUILT) $(OSX_PACKAGING) $(OSX_BACKGROUND_IMAGE) $(PYTHON) $(OSX_DEPLOY_SCRIPT) $(OSX_APP) -add-qt-tr $(OSX_QT_TRANSLATIONS) -translations-dir=$(QT_TRANSLATION_DIR) -dmg -fancy $(OSX_FANCY_PLIST) -verbose 2 -volname $(OSX_VOLNAME) $(OSX_BACKGROUND_IMAGE).png: contrib/macdeploy/$(OSX_BACKGROUND_SVG) sed 's/PACKAGE_NAME/$(PACKAGE_NAME)/' < "$<" | $(RSVG_CONVERT) -f png -d 36 -p 36 -o $@ $(OSX_BACKGROUND_IMAGE)@2x.png: contrib/macdeploy/$(OSX_BACKGROUND_SVG) sed 's/PACKAGE_NAME/$(PACKAGE_NAME)/' < "$<" | $(RSVG_CONVERT) -f png -d 72 -p 72 -o $@ $(OSX_BACKGROUND_IMAGE): $(OSX_BACKGROUND_IMAGE).png $(OSX_BACKGROUND_IMAGE)@2x.png tiffutil -cathidpicheck $^ -out $@ deploydir: $(OSX_DMG) else APP_DIST_DIR=$(top_builddir)/dist APP_DIST_EXTRAS=$(APP_DIST_DIR)/.background/$(OSX_BACKGROUND_IMAGE) $(APP_DIST_DIR)/.DS_Store $(APP_DIST_DIR)/Applications $(APP_DIST_DIR)/Applications: @rm -f $@ @cd $(@D); $(LN_S) /Applications $(@F) $(APP_DIST_EXTRAS): $(APP_DIST_DIR)/$(OSX_APP)/Contents/MacOS/BitcoinABC-Qt $(OSX_DMG): $(APP_DIST_EXTRAS) $(GENISOIMAGE) -no-cache-inodes -D -l -probe -V "$(OSX_VOLNAME)" -no-pad -r -dir-mode 0755 -apple -o $@ dist dpi%.$(OSX_BACKGROUND_IMAGE): contrib/macdeploy/$(OSX_BACKGROUND_SVG) sed 's/PACKAGE_NAME/$(PACKAGE_NAME)/' < "$<" | $(RSVG_CONVERT) -f png -d $* -p $* | $(IMAGEMAGICK_CONVERT) - $@ OSX_BACKGROUND_IMAGE_DPIFILES := $(foreach dpi,$(OSX_BACKGROUND_IMAGE_DPIS),dpi$(dpi).$(OSX_BACKGROUND_IMAGE)) $(APP_DIST_DIR)/.background/$(OSX_BACKGROUND_IMAGE): $(OSX_BACKGROUND_IMAGE_DPIFILES) $(MKDIR_P) $(@D) $(TIFFCP) -c none $(OSX_BACKGROUND_IMAGE_DPIFILES) $@ $(APP_DIST_DIR)/.DS_Store: $(OSX_DSSTORE_GEN) $(PYTHON) $< "$@" "$(OSX_VOLNAME)" $(APP_DIST_DIR)/$(OSX_APP)/Contents/MacOS/BitcoinABC-Qt: $(OSX_APP_BUILT) $(OSX_PACKAGING) INSTALLNAMETOOL=$(INSTALLNAMETOOL) OTOOL=$(OTOOL) STRIP=$(STRIP) $(PYTHON) $(OSX_DEPLOY_SCRIPT) $(OSX_APP) -translations-dir=$(QT_TRANSLATION_DIR) -add-qt-tr $(OSX_QT_TRANSLATIONS) -verbose 2 deploydir: $(APP_DIST_EXTRAS) endif if TARGET_DARWIN appbundle: $(OSX_APP_BUILT) deploy: $(OSX_DMG) endif if TARGET_WINDOWS deploy: $(BITCOIN_WIN_INSTALLER) endif $(BITCOIN_QT_BIN): FORCE $(MAKE) -C src qt/$(@F) $(BITCOIND_BIN): FORCE $(MAKE) -C src $(@F) $(BITCOIN_CLI_BIN): FORCE $(MAKE) -C src $(@F) +$(BITCOIN_SEEDER_BIN): FORCE + $(MAKE) -C src $(@F) + if USE_LCOV baseline.info: $(LCOV) -c -i -d $(abs_builddir)/src -o $@ baseline_filtered.info: baseline.info $(LCOV) -r $< "/usr/include/*" -o $@ leveldb_baseline.info: baseline_filtered.info $(LCOV) -c -i -d $(abs_builddir)/src/leveldb -b $(abs_builddir)/src/leveldb -o $@ leveldb_baseline_filtered.info: leveldb_baseline.info $(LCOV) -r $< "/usr/include/*" -o $@ baseline_filtered_combined.info: leveldb_baseline_filtered.info baseline_filtered.info $(LCOV) -a leveldb_baseline_filtered.info -a baseline_filtered.info -o $@ test_bitcoin.info: baseline_filtered_combined.info $(MAKE) -C src/ check $(LCOV) -c -d $(abs_builddir)/src -t test_bitcoin -o $@ $(LCOV) -z -d $(abs_builddir)/src $(LCOV) -z -d $(abs_builddir)/src/leveldb test_bitcoin_filtered.info: test_bitcoin.info $(LCOV) -r $< "/usr/include/*" -o $@ rpc_test.info: test_bitcoin_filtered.info -@TIMEOUT=15 python qa/pull-tester/rpc-tests.py $(EXTENDED_RPC_TESTS) $(LCOV) -c -d $(abs_builddir)/src --t rpc-tests -o $@ $(LCOV) -z -d $(abs_builddir)/src $(LCOV) -z -d $(abs_builddir)/src/leveldb rpc_test_filtered.info: rpc_test.info $(LCOV) -r $< "/usr/include/*" -o $@ test_bitcoin_coverage.info: baseline_filtered_combined.info test_bitcoin_filtered.info $(LCOV) -a baseline_filtered.info -a leveldb_baseline_filtered.info -a test_bitcoin_filtered.info -o $@ total_coverage.info: baseline_filtered_combined.info test_bitcoin_filtered.info rpc_test_filtered.info $(LCOV) -a baseline_filtered.info -a leveldb_baseline_filtered.info -a test_bitcoin_filtered.info -a rpc_test_filtered.info -o $@ | $(GREP) "\%" | $(AWK) '{ print substr($$3,2,50) "/" $$5 }' > coverage_percent.txt test_bitcoin.coverage/.dirstamp: test_bitcoin_coverage.info $(GENHTML) -s $< -o $(@D) @touch $@ total.coverage/.dirstamp: total_coverage.info $(GENHTML) -s $< -o $(@D) @touch $@ cov: test_bitcoin.coverage/.dirstamp total.coverage/.dirstamp endif dist_noinst_SCRIPTS = autogen.sh EXTRA_DIST = $(top_srcdir)/share/genbuild.sh qa/pull-tester/rpc-tests.py qa/rpc-tests $(DIST_CONTRIB) $(DIST_DOCS) $(WINDOWS_PACKAGING) $(OSX_PACKAGING) $(BIN_CHECKS) CLEANFILES = $(OSX_DMG) $(BITCOIN_WIN_INSTALLER) # This file is problematic for out-of-tree builds if it exists. DISTCLEANFILES = qa/pull-tester/tests_config.pyc .INTERMEDIATE: $(COVERAGE_INFO) DISTCHECK_CONFIGURE_FLAGS = --enable-man clean-local: rm -rf coverage_percent.txt test_bitcoin.coverage/ total.coverage/ qa/tmp/ cache/ $(OSX_APP) rm -rf qa/pull-tester/__pycache__ diff --git a/configure.ac b/configure.ac index 0c1b7300e..d236cf2a4 100644 --- a/configure.ac +++ b/configure.ac @@ -1,1285 +1,1297 @@ dnl require autoconf 2.60 (AS_ECHO/AS_ECHO_N) AC_PREREQ([2.60]) define(_CLIENT_VERSION_MAJOR, 0) define(_CLIENT_VERSION_MINOR, 15) define(_CLIENT_VERSION_REVISION, 0) define(_CLIENT_VERSION_BUILD, 0) define(_CLIENT_VERSION_IS_RELEASE, true) define(_COPYRIGHT_YEAR, 2017) define(_COPYRIGHT_HOLDERS,[The %s developers]) define(_COPYRIGHT_HOLDERS_SUBSTITUTION,[[Bitcoin]]) AC_INIT([Bitcoin ABC],[_CLIENT_VERSION_MAJOR._CLIENT_VERSION_MINOR._CLIENT_VERSION_REVISION],[https://github.com/Bitcoin-ABC/bitcoin-abc/issues],[bitcoin-abc],[https://bitcoinabc.org/]) AC_CONFIG_SRCDIR([src/validation.cpp]) AC_CONFIG_HEADERS([src/config/bitcoin-config.h]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_MACRO_DIR([build-aux/m4]) BITCOIN_DAEMON_NAME=bitcoind BITCOIN_GUI_NAME=bitcoin-qt BITCOIN_CLI_NAME=bitcoin-cli BITCOIN_TX_NAME=bitcoin-tx +BITCOIN_SEEDER_NAME=bitcoin-seeder AC_CANONICAL_HOST AH_TOP([#ifndef BITCOIN_BITCOIN_CONFIG_H]) AH_TOP([#define BITCOIN_BITCOIN_CONFIG_H]) AH_BOTTOM([#endif // BITCOIN_BITCOIN_CONFIG_H]) dnl faketime breaks configure and is only needed for make. Disable it here. unset FAKETIME dnl Automake init set-up and checks AM_INIT_AUTOMAKE([no-define subdir-objects foreign]) dnl faketime messes with timestamps and causes configure to be re-run. dnl --disable-maintainer-mode can be used to bypass this. AM_MAINTAINER_MODE([enable]) dnl make the compilation flags quiet unless V=1 is used m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) dnl Compiler checks (here before libtool). if test "x${CXXFLAGS+set}" = "xset"; then CXXFLAGS_overridden=yes else CXXFLAGS_overridden=no fi AC_PROG_CXX dnl By default, libtool for mingw refuses to link static libs into a dll for dnl fear of mixing pic/non-pic objects, and import/export complications. Since dnl we have those under control, re-enable that functionality. case $host in *mingw*) lt_cv_deplibs_check_method="pass_all" ;; esac dnl Require C++11 compiler (no GNU extensions) AX_CXX_COMPILE_STDCXX([11], [noext], [mandatory], [nodefault]) dnl Check if -latomic is required for CHECK_ATOMIC dnl Unless the user specified OBJCXX, force it to be the same as CXX. This ensures dnl that we get the same -std flags for both. m4_ifdef([AC_PROG_OBJCXX],[ if test "x${OBJCXX+set}" = "x"; then OBJCXX="${CXX}" fi AC_PROG_OBJCXX ]) dnl Libtool init checks. LT_INIT([pic-only]) dnl Check/return PATH for base programs. AC_PATH_TOOL(AR, ar) AC_PATH_TOOL(RANLIB, ranlib) AC_PATH_TOOL(STRIP, strip) AC_PATH_TOOL(GCOV, gcov) AC_PATH_PROG(LCOV, lcov) dnl Python 3.x is supported from 3.4 on (see https://github.com/bitcoin/bitcoin/issues/7893) AC_PATH_PROGS([PYTHON], [python3.6 python3.5 python3.4 python3 python2.7 python2 python]) AC_PATH_PROG(GENHTML, genhtml) AC_PATH_PROG([GIT], [git]) AC_PATH_PROG(CCACHE,ccache) AC_PATH_PROG(XGETTEXT,xgettext) AC_PATH_PROG(HEXDUMP,hexdump) AC_PATH_TOOL(READELF, readelf) AC_PATH_TOOL(CPPFILT, c++filt) AC_PATH_TOOL(OBJCOPY, objcopy) AC_ARG_VAR(PYTHONPATH, Augments the default search path for python module files) # Enable wallet AC_ARG_ENABLE([wallet], [AS_HELP_STRING([--disable-wallet], [disable wallet (enabled by default)])], [enable_wallet=$enableval], [enable_wallet=yes]) AC_ARG_WITH([miniupnpc], [AS_HELP_STRING([--with-miniupnpc], [enable UPNP (default is yes if libminiupnpc is found)])], [use_upnp=$withval], [use_upnp=auto]) AC_ARG_ENABLE([upnp-default], [AS_HELP_STRING([--enable-upnp-default], [if UPNP is enabled, turn it on at startup (default is no)])], [use_upnp_default=$enableval], [use_upnp_default=no]) AC_ARG_ENABLE(tests, AS_HELP_STRING([--disable-tests],[do not compile tests (default is to compile)]), [use_tests=$enableval], [use_tests=yes]) AC_ARG_ENABLE(gui-tests, AS_HELP_STRING([--disable-gui-tests],[do not compile GUI tests (default is to compile if GUI and tests enabled)]), [use_gui_tests=$enableval], [use_gui_tests=$use_tests]) AC_ARG_ENABLE(bench, AS_HELP_STRING([--disable-bench],[do not compile benchmarks (default is to compile)]), [use_bench=$enableval], [use_bench=yes]) AC_ARG_ENABLE([extended-rpc-tests], AS_HELP_STRING([--enable-extended-rpc-tests],[enable expensive RPC tests when using lcov (default no)]), [use_extended_rpc_tests=$enableval], [use_extended_rpc_tests=no]) AC_ARG_WITH([qrencode], [AS_HELP_STRING([--with-qrencode], [enable QR code support (default is yes if qt is enabled and libqrencode is found)])], [use_qr=$withval], [use_qr=auto]) AC_ARG_ENABLE([hardening], [AS_HELP_STRING([--disable-hardening], [do not attempt to harden the resulting executables (default is to harden)])], [use_hardening=$enableval], [use_hardening=yes]) AC_ARG_ENABLE([reduce-exports], [AS_HELP_STRING([--enable-reduce-exports], [attempt to reduce exported symbols in the resulting executables (default is no)])], [use_reduce_exports=$enableval], [use_reduce_exports=no]) AC_ARG_ENABLE([ccache], [AS_HELP_STRING([--disable-ccache], [do not use ccache for building (default is to use if found)])], [use_ccache=$enableval], [use_ccache=auto]) AC_ARG_ENABLE([lcov], [AS_HELP_STRING([--enable-lcov], [enable lcov testing (default is no)])], [use_lcov=yes], [use_lcov=no]) AC_ARG_ENABLE([glibc-back-compat], [AS_HELP_STRING([--enable-glibc-back-compat], [enable backwards compatibility with glibc])], [use_glibc_compat=$enableval], [use_glibc_compat=no]) AC_ARG_WITH([system-univalue], [AS_HELP_STRING([--with-system-univalue], [Build with system UniValue (default is no)])], [system_univalue=$withval], [system_univalue=no] ) AC_ARG_ENABLE([zmq], [AS_HELP_STRING([--disable-zmq], [disable ZMQ notifications])], [use_zmq=$enableval], [use_zmq=yes]) AC_ARG_WITH([protoc-bindir],[AS_HELP_STRING([--with-protoc-bindir=BIN_DIR],[specify protoc bin path])], [protoc_bin_path=$withval], []) AC_ARG_ENABLE(man, [AS_HELP_STRING([--disable-man], [do not install man pages (default is to install)])],, enable_man=yes) AM_CONDITIONAL(ENABLE_MAN, test "$enable_man" != no) # Enable debug AC_ARG_ENABLE([debug], [AS_HELP_STRING([--enable-debug], [use debug compiler flags and macros (default is no)])], [enable_debug=$enableval], [enable_debug=no]) # Enable ASAN AC_ARG_ENABLE([asan], [AS_HELP_STRING([--enable-asan], [enable address sanitizer compiler flags (implies --enable-debug, default is no)])], [enable_asan=$enableval], [enable_asan=no]) # Enable TSAN AC_ARG_ENABLE([tsan], [AS_HELP_STRING([--enable-tsan], [enable thread sanitizer compiler flags; requires 64-bit target architecture (implies --enable-debug, default is no)])], [enable_tsan=$enableval], [enable_tsan=no]) # Enable UBSAN AC_ARG_ENABLE([ubsan], [AS_HELP_STRING([--enable-ubsan], [enable undefined behavior sanitizer compiler flags (implies --enable-debug, default is no)])], [enable_ubsan=$enableval], [enable_ubsan=no]) # Turn warnings into errors AC_ARG_ENABLE([werror], [AS_HELP_STRING([--enable-werror], [Treat certain compiler warnings as errors (default is no)])], [enable_werror=$enableval], [enable_werror=no]) AC_LANG_PUSH([C++]) AX_CHECK_COMPILE_FLAG([-Werror],[CXXFLAG_WERROR="-Werror"],[CXXFLAG_WERROR=""]) if test "x$enable_asan" = xyes; then case $host in *mingw*) AC_MSG_ERROR("Windows builds cannot use ASAN") ;; esac enable_debug=yes CFLAGS="$CFLAGS -fsanitize=address -fno-omit-frame-pointer" CXXFLAGS="$CXXFLAGS -fsanitize=address -fno-omit-frame-pointer" LDFLAGS="$LDFLAGS -fsanitize=address" fi if test "x$enable_tsan" = xyes; then case $host in *mingw*) AC_MSG_ERROR("Windows builds cannot use TSAN") ;; esac enable_debug=yes CFLAGS="$CFLAGS -fsanitize=thread -fno-omit-frame-pointer" CXXFLAGS="$CXXFLAGS -fsanitize=thread -fno-omit-frame-pointer" LDFLAGS="$LDFLAGS -fsanitize=thread" fi if test "x$enable_ubsan" = xyes; then case $host in *mingw*) AC_MSG_ERROR("Windows builds cannot use UBSAN") ;; esac enable_debug=yes CFLAGS="$CFLAGS -fsanitize=undefined -fno-omit-frame-pointer" CXXFLAGS="$CXXFLAGS -fsanitize=undefined -fno-omit-frame-pointer" LDFLAGS="$LDFLAGS -fsanitize=undefined" fi if test "x$enable_debug" = xyes; then CPPFLAGS="$CPPFLAGS -DDEBUG -DDEBUG_LOCKORDER" if test "x$GCC" = xyes; then CFLAGS="$CFLAGS -g3 -O0" fi if test "x$GXX" = xyes; then CXXFLAGS="$CXXFLAGS -g3 -O0" fi fi ERROR_CXXFLAGS= if test "x$enable_werror" = "xyes"; then if test "x$CXXFLAG_WERROR" = "x"; then AC_MSG_ERROR("enable-werror set but -Werror is not usable") fi AX_CHECK_COMPILE_FLAG([-Werror=vla],[ERROR_CXXFLAGS="$ERROR_CXXFLAGS -Werror=vla"],,[[$CXXFLAG_WERROR]]) fi if test "x$CXXFLAGS_overridden" = "xno"; then AX_CHECK_COMPILE_FLAG([-Wall],[CXXFLAGS="$CXXFLAGS -Wall"],,[[$CXXFLAG_WERROR]]) AX_CHECK_COMPILE_FLAG([-Wextra],[CXXFLAGS="$CXXFLAGS -Wextra"],,[[$CXXFLAG_WERROR]]) AX_CHECK_COMPILE_FLAG([-Wformat],[CXXFLAGS="$CXXFLAGS -Wformat"],,[[$CXXFLAG_WERROR]]) AX_CHECK_COMPILE_FLAG([-Wvla],[CXXFLAGS="$CXXFLAGS -Wvla"],,[[$CXXFLAG_WERROR]]) AX_CHECK_COMPILE_FLAG([-Wformat-security],[CXXFLAGS="$CXXFLAGS -Wformat-security"],,[[$CXXFLAG_WERROR]]) ## Some compilers (gcc) ignore unknown -Wno-* options, but warn about all ## unknown options if any other warning is produced. Test the -Wfoo case, and ## set the -Wno-foo case if it works. AX_CHECK_COMPILE_FLAG([-Wunused-parameter],[CXXFLAGS="$CXXFLAGS -Wno-unused-parameter"],,[[$CXXFLAG_WERROR]]) AX_CHECK_COMPILE_FLAG([-Wself-assign],[CXXFLAGS="$CXXFLAGS -Wno-self-assign"],,[[$CXXFLAG_WERROR]]) AX_CHECK_COMPILE_FLAG([-Wunused-local-typedef],[CXXFLAGS="$CXXFLAGS -Wno-unused-local-typedef"],,[[$CXXFLAG_WERROR]]) AX_CHECK_COMPILE_FLAG([-Wdeprecated-register],[CXXFLAGS="$CXXFLAGS -Wno-deprecated-register"],,[[$CXXFLAG_WERROR]]) fi CPPFLAGS="$CPPFLAGS -DHAVE_BUILD_INFO -D__STDC_FORMAT_MACROS" AC_ARG_WITH([utils], [AS_HELP_STRING([--with-utils], [build bitcoin-cli bitcoin-tx (default=yes)])], [build_bitcoin_utils=$withval], [build_bitcoin_utils=yes]) AC_ARG_WITH([libs], [AS_HELP_STRING([--with-libs], [build libraries (default=yes)])], [build_bitcoin_libs=$withval], [build_bitcoin_libs=yes]) AC_ARG_WITH([daemon], [AS_HELP_STRING([--with-daemon], [build bitcoind daemon (default=yes)])], [build_bitcoind=$withval], [build_bitcoind=yes]) +AC_ARG_WITH([seeder], + [AS_HELP_STRING([--with-seeder], + [build seeder (default=yes)])], + [build_bitcoin_seeder=$withval], + [build_bitcoin_seeder=yes]) + use_pkgconfig=yes case $host in *mingw*) #pkgconfig does more harm than good with MinGW use_pkgconfig=no TARGET_OS=windows AC_CHECK_LIB([mingwthrd], [main],, AC_MSG_ERROR(lib missing)) AC_CHECK_LIB([kernel32], [main],, AC_MSG_ERROR(lib missing)) AC_CHECK_LIB([user32], [main],, AC_MSG_ERROR(lib missing)) AC_CHECK_LIB([gdi32], [main],, AC_MSG_ERROR(lib missing)) AC_CHECK_LIB([comdlg32], [main],, AC_MSG_ERROR(lib missing)) AC_CHECK_LIB([winspool], [main],, AC_MSG_ERROR(lib missing)) AC_CHECK_LIB([winmm], [main],, AC_MSG_ERROR(lib missing)) AC_CHECK_LIB([shell32], [main],, AC_MSG_ERROR(lib missing)) AC_CHECK_LIB([comctl32], [main],, AC_MSG_ERROR(lib missing)) AC_CHECK_LIB([ole32], [main],, AC_MSG_ERROR(lib missing)) AC_CHECK_LIB([oleaut32], [main],, AC_MSG_ERROR(lib missing)) AC_CHECK_LIB([uuid], [main],, AC_MSG_ERROR(lib missing)) AC_CHECK_LIB([rpcrt4], [main],, AC_MSG_ERROR(lib missing)) AC_CHECK_LIB([advapi32], [main],, AC_MSG_ERROR(lib missing)) AC_CHECK_LIB([ws2_32], [main],, AC_MSG_ERROR(lib missing)) AC_CHECK_LIB([mswsock], [main],, AC_MSG_ERROR(lib missing)) AC_CHECK_LIB([shlwapi], [main],, AC_MSG_ERROR(lib missing)) AC_CHECK_LIB([iphlpapi], [main],, AC_MSG_ERROR(lib missing)) AC_CHECK_LIB([crypt32], [main],, AC_MSG_ERROR(lib missing)) # -static is interpreted by libtool, where it has a different meaning. # In libtool-speak, it's -all-static. AX_CHECK_LINK_FLAG([[-static]],[LIBTOOL_APP_LDFLAGS="$LIBTOOL_APP_LDFLAGS -all-static"]) AC_PATH_PROG([MAKENSIS], [makensis], none) if test x$MAKENSIS = xnone; then AC_MSG_WARN("makensis not found. Cannot create installer.") fi AC_PATH_TOOL(WINDRES, windres, none) if test x$WINDRES = xnone; then AC_MSG_ERROR("windres not found") fi CPPFLAGS="$CPPFLAGS -D_MT -DWIN32 -D_WINDOWS -DBOOST_THREAD_USE_LIB" LEVELDB_TARGET_FLAGS="-DOS_WINDOWS" if test "x$CXXFLAGS_overridden" = "xno"; then CXXFLAGS="$CXXFLAGS -w" fi case $host in i?86-*) WINDOWS_BITS=32 ;; x86_64-*) WINDOWS_BITS=64 ;; *) AC_MSG_ERROR("Could not determine win32/win64 for installer") ;; esac AC_SUBST(WINDOWS_BITS) dnl libtool insists upon adding -nostdlib and a list of objects/libs to link against. dnl That breaks our ability to build dll's with static libgcc/libstdc++/libssp. Override dnl its command here, with the predeps/postdeps removed, and -static inserted. Postdeps are dnl also overridden to prevent their insertion later. dnl This should only affect dll's. archive_cmds_CXX="\$CC -shared \$libobjs \$deplibs \$compiler_flags -static -o \$output_objdir/\$soname \${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker \$lib" postdeps_CXX= ;; *darwin*) TARGET_OS=darwin LEVELDB_TARGET_FLAGS="-DOS_MACOSX" if test x$cross_compiling != xyes; then BUILD_OS=darwin AC_CHECK_PROG([PORT],port, port) if test x$PORT = xport; then dnl add default macports paths CPPFLAGS="$CPPFLAGS -isystem /opt/local/include" LIBS="$LIBS -L/opt/local/lib" if test -d /opt/local/include/db48; then CPPFLAGS="$CPPFLAGS -I/opt/local/include/db48" LIBS="$LIBS -L/opt/local/lib/db48" fi fi AC_PATH_PROGS([RSVG_CONVERT], [rsvg-convert rsvg],rsvg-convert) AC_CHECK_PROG([BREW],brew, brew) if test x$BREW = xbrew; then dnl These Homebrew packages may be keg-only, meaning that they won't be found dnl in expected paths because they may conflict with system files. Ask dnl Homebrew where each one is located, then adjust paths accordingly. dnl It's safe to add these paths even if the functionality is disabled by dnl the user (--without-wallet or --without-gui for example). openssl_prefix=`$BREW --prefix openssl 2>/dev/null` bdb_prefix=`$BREW --prefix berkeley-db4 2>/dev/null` qt5_prefix=`$BREW --prefix qt5 2>/dev/null` if test x$openssl_prefix != x; then PKG_CONFIG_PATH="$openssl_prefix/lib/pkgconfig:$PKG_CONFIG_PATH" export PKG_CONFIG_PATH fi if test x$bdb_prefix != x; then CPPFLAGS="$CPPFLAGS -I$bdb_prefix/include" LIBS="$LIBS -L$bdb_prefix/lib" fi if test x$qt5_prefix != x; then PKG_CONFIG_PATH="$qt5_prefix/lib/pkgconfig:$PKG_CONFIG_PATH" export PKG_CONFIG_PATH fi fi else case $build_os in *darwin*) BUILD_OS=darwin ;; *) AC_PATH_TOOL([INSTALLNAMETOOL], [install_name_tool], install_name_tool) AC_PATH_TOOL([OTOOL], [otool], otool) AC_PATH_PROGS([GENISOIMAGE], [genisoimage mkisofs],genisoimage) AC_PATH_PROGS([RSVG_CONVERT], [rsvg-convert rsvg],rsvg-convert) AC_PATH_PROGS([IMAGEMAGICK_CONVERT], [convert],convert) AC_PATH_PROGS([TIFFCP], [tiffcp],tiffcp) dnl libtool will try to strip the static lib, which is a problem for dnl cross-builds because strip attempts to call a hard-coded ld, dnl which may not exist in the path. Stripping the .a is not dnl necessary, so just disable it. old_striplib= ;; esac fi AX_CHECK_LINK_FLAG([[-Wl,-headerpad_max_install_names]], [LDFLAGS="$LDFLAGS -Wl,-headerpad_max_install_names"]) CPPFLAGS="$CPPFLAGS -DMAC_OSX" OBJCXXFLAGS="$CXXFLAGS" ;; *linux*) TARGET_OS=linux LEVELDB_TARGET_FLAGS="-DOS_LINUX" ;; *freebsd*) LEVELDB_TARGET_FLAGS="-DOS_FREEBSD" ;; *openbsd*) LEVELDB_TARGET_FLAGS="-DOS_OPENBSD" ;; *) OTHER_OS=`echo ${host_os} | awk '{print toupper($0)}'` AC_MSG_WARN([Guessing LevelDB OS as OS_${OTHER_OS}, please check whether this is correct, if not add an entry to configure.ac.]) LEVELDB_TARGET_FLAGS="-DOS_${OTHER_OS}" ;; esac if test x$use_pkgconfig = xyes; then m4_ifndef([PKG_PROG_PKG_CONFIG], [AC_MSG_ERROR(PKG_PROG_PKG_CONFIG macro not found. Please install pkg-config and re-run autogen.sh.)]) m4_ifdef([PKG_PROG_PKG_CONFIG], [ PKG_PROG_PKG_CONFIG if test x"$PKG_CONFIG" = "x"; then AC_MSG_ERROR(pkg-config not found.) fi ]) fi if test x$use_extended_rpc_tests != xno; then AC_SUBST(EXTENDED_RPC_TESTS, -extended) fi if test x$use_lcov = xyes; then if test x$LCOV = x; then AC_MSG_ERROR("lcov testing requested but lcov not found") fi if test x$GCOV = x; then AC_MSG_ERROR("lcov testing requested but gcov not found") fi if test x$PYTHON = x; then AC_MSG_ERROR("lcov testing requested but python not found") fi if test x$GENHTML = x; then AC_MSG_ERROR("lcov testing requested but genhtml not found") fi LCOV="$LCOV --gcov-tool=$GCOV" AX_CHECK_LINK_FLAG([[--coverage]], [LDFLAGS="$LDFLAGS --coverage"], [AC_MSG_ERROR("lcov testing requested but --coverage linker flag does not work")]) AX_CHECK_COMPILE_FLAG([--coverage],[CXXFLAGS="$CXXFLAGS --coverage"], [AC_MSG_ERROR("lcov testing requested but --coverage flag does not work")]) fi dnl Check for endianness AC_C_BIGENDIAN dnl Check for pthread compile/link requirements AX_PTHREAD # The following macro will add the necessary defines to bitcoin-config.h, but # they also need to be passed down to any subprojects. Pull the results out of # the cache and add them to CPPFLAGS. AC_SYS_LARGEFILE # detect POSIX or GNU variant of strerror_r AC_FUNC_STRERROR_R if test x$ac_cv_sys_file_offset_bits != x && test x$ac_cv_sys_file_offset_bits != xno && test x$ac_cv_sys_file_offset_bits != xunknown; then CPPFLAGS="$CPPFLAGS -D_FILE_OFFSET_BITS=$ac_cv_sys_file_offset_bits" fi if test x$ac_cv_sys_large_files != x && test x$ac_cv_sys_large_files != xno && test x$ac_cv_sys_large_files != xunknown; then CPPFLAGS="$CPPFLAGS -D_LARGE_FILES=$ac_cv_sys_large_files" fi AX_CHECK_LINK_FLAG([[-Wl,--large-address-aware]], [LDFLAGS="$LDFLAGS -Wl,--large-address-aware"]) AX_GCC_FUNC_ATTRIBUTE([visibility]) AX_GCC_FUNC_ATTRIBUTE([dllexport]) AX_GCC_FUNC_ATTRIBUTE([dllimport]) if test x$use_glibc_compat != xno; then #glibc absorbed clock_gettime in 2.17. librt (its previous location) is safe to link #in anyway for back-compat. AC_CHECK_LIB([rt],[clock_gettime],, AC_MSG_ERROR(lib missing)) #__fdelt_chk's params and return type have changed from long unsigned int to long int. # See which one is present here. AC_MSG_CHECKING(__fdelt_chk type) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#ifdef _FORTIFY_SOURCE #undef _FORTIFY_SOURCE #endif #define _FORTIFY_SOURCE 2 #include extern "C" long unsigned int __fdelt_warn(long unsigned int);]],[[]])], [ fdelt_type="long unsigned int"], [ fdelt_type="long int"]) AC_MSG_RESULT($fdelt_type) AC_DEFINE_UNQUOTED(FDELT_TYPE, $fdelt_type,[parameter and return value type for __fdelt_chk]) else AC_SEARCH_LIBS([clock_gettime],[rt]) fi if test x$TARGET_OS != xwindows; then # All windows code is PIC, forcing it on just adds useless compile warnings AX_CHECK_COMPILE_FLAG([-fPIC],[PIC_FLAGS="-fPIC"]) fi if test x$use_hardening != xno; then AX_CHECK_COMPILE_FLAG([-Wstack-protector],[HARDENED_CXXFLAGS="$HARDENED_CXXFLAGS -Wstack-protector"]) AX_CHECK_COMPILE_FLAG([-fstack-protector-all],[HARDENED_CXXFLAGS="$HARDENED_CXXFLAGS -fstack-protector-all"]) AX_CHECK_PREPROC_FLAG([-D_FORTIFY_SOURCE=2],[ AX_CHECK_PREPROC_FLAG([-U_FORTIFY_SOURCE],[ HARDENED_CPPFLAGS="$HARDENED_CPPFLAGS -U_FORTIFY_SOURCE" ]) HARDENED_CPPFLAGS="$HARDENED_CPPFLAGS -D_FORTIFY_SOURCE=2" ]) AX_CHECK_LINK_FLAG([[-Wl,--dynamicbase]], [HARDENED_LDFLAGS="$HARDENED_LDFLAGS -Wl,--dynamicbase"]) AX_CHECK_LINK_FLAG([[-Wl,--nxcompat]], [HARDENED_LDFLAGS="$HARDENED_LDFLAGS -Wl,--nxcompat"]) AX_CHECK_LINK_FLAG([[-Wl,--high-entropy-va]], [HARDENED_LDFLAGS="$HARDENED_LDFLAGS -Wl,--high-entropy-va"]) AX_CHECK_LINK_FLAG([[-Wl,-z,relro]], [HARDENED_LDFLAGS="$HARDENED_LDFLAGS -Wl,-z,relro"]) AX_CHECK_LINK_FLAG([[-Wl,-z,now]], [HARDENED_LDFLAGS="$HARDENED_LDFLAGS -Wl,-z,now"]) if test x$TARGET_OS != xwindows; then AX_CHECK_COMPILE_FLAG([-fPIE],[PIE_FLAGS="-fPIE"]) AX_CHECK_LINK_FLAG([[-pie]], [HARDENED_LDFLAGS="$HARDENED_LDFLAGS -pie"]) fi case $host in *mingw*) AC_CHECK_LIB([ssp], [main],, AC_MSG_ERROR(lib missing)) ;; esac fi dnl this flag screws up non-darwin gcc even when the check fails. special-case it. if test x$TARGET_OS = xdarwin; then AX_CHECK_LINK_FLAG([[-Wl,-dead_strip]], [LDFLAGS="$LDFLAGS -Wl,-dead_strip"]) fi AC_CHECK_HEADERS([endian.h sys/endian.h byteswap.h stdio.h stdlib.h unistd.h strings.h sys/types.h sys/stat.h sys/select.h sys/prctl.h]) AC_CHECK_DECLS([strnlen]) # Check for daemon(3), unrelated to --with-daemon (although used by it) AC_CHECK_DECLS([daemon]) AC_CHECK_DECLS([le16toh, le32toh, le64toh, htole16, htole32, htole64, be16toh, be32toh, be64toh, htobe16, htobe32, htobe64],,, [#if HAVE_ENDIAN_H #include #elif HAVE_SYS_ENDIAN_H #include #endif]) AC_CHECK_DECLS([bswap_16, bswap_32, bswap_64],,, [#if HAVE_BYTESWAP_H #include #endif]) AC_CHECK_DECLS([__builtin_clz, __builtin_clzl, __builtin_clzll]) dnl Check for MSG_NOSIGNAL AC_MSG_CHECKING(for MSG_NOSIGNAL) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], [[ int f = MSG_NOSIGNAL; ]])], [ AC_MSG_RESULT(yes); AC_DEFINE(HAVE_MSG_NOSIGNAL, 1,[Define this symbol if you have MSG_NOSIGNAL]) ], [ AC_MSG_RESULT(no)] ) dnl Check for mallopt(M_ARENA_MAX) (to set glibc arenas) AC_MSG_CHECKING(for mallopt M_ARENA_MAX) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], [[ mallopt(M_ARENA_MAX, 1); ]])], [ AC_MSG_RESULT(yes); AC_DEFINE(HAVE_MALLOPT_ARENA_MAX, 1,[Define this symbol if you have mallopt with M_ARENA_MAX]) ], [ AC_MSG_RESULT(no)] ) AC_MSG_CHECKING([for visibility attribute]) AC_LINK_IFELSE([AC_LANG_SOURCE([ int foo_def( void ) __attribute__((visibility("default"))); int main(){} ])], [ AC_DEFINE(HAVE_VISIBILITY_ATTRIBUTE,1,[Define if the visibility attribute is supported.]) AC_MSG_RESULT(yes) ], [ AC_MSG_RESULT(no) if test x$use_reduce_exports = xyes; then AC_MSG_ERROR([Cannot find a working visibility attribute. Use --disable-reduce-exports.]) fi ] ) # Check for different ways of gathering OS randomness AC_MSG_CHECKING(for Linux getrandom syscall) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include #include #include ]], [[ syscall(SYS_getrandom, nullptr, 32, 0); ]])], [ AC_MSG_RESULT(yes); AC_DEFINE(HAVE_SYS_GETRANDOM, 1,[Define this symbol if the Linux getrandom system call is available]) ], [ AC_MSG_RESULT(no)] ) AC_MSG_CHECKING(for getentropy) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], [[ getentropy(nullptr, 32) ]])], [ AC_MSG_RESULT(yes); AC_DEFINE(HAVE_GETENTROPY, 1,[Define this symbol if the BSD getentropy system call is available]) ], [ AC_MSG_RESULT(no)] ) AC_MSG_CHECKING(for sysctl KERN_ARND) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include #include ]], [[ static const int name[2] = {CTL_KERN, KERN_ARND}; sysctl(name, 2, nullptr, nullptr, nullptr, 0); ]])], [ AC_MSG_RESULT(yes); AC_DEFINE(HAVE_SYSCTL_ARND, 1,[Define this symbol if the BSD sysctl(KERN_ARND) is available]) ], [ AC_MSG_RESULT(no)] ) # Check for reduced exports if test x$use_reduce_exports = xyes; then AX_CHECK_COMPILE_FLAG([-fvisibility=hidden],[RE_CXXFLAGS="-fvisibility=hidden"], [AC_MSG_ERROR([Cannot set default symbol visibility. Use --disable-reduce-exports.])]) fi LEVELDB_CPPFLAGS= LIBLEVELDB= LIBMEMENV= AM_CONDITIONAL([EMBEDDED_LEVELDB],[true]) AC_SUBST(LEVELDB_CPPFLAGS) AC_SUBST(LIBLEVELDB) AC_SUBST(LIBMEMENV) if test x$enable_wallet != xno; then dnl Check for libdb_cxx only if wallet enabled BITCOIN_FIND_BDB48 fi dnl Check for libminiupnpc (optional) if test x$use_upnp != xno; then AC_CHECK_HEADERS( [miniupnpc/miniwget.h miniupnpc/miniupnpc.h miniupnpc/upnpcommands.h miniupnpc/upnperrors.h], [AC_CHECK_LIB([miniupnpc], [main],[MINIUPNPC_LIBS=-lminiupnpc], [have_miniupnpc=no])], [have_miniupnpc=no] ) fi BITCOIN_QT_INIT dnl sets $bitcoin_enable_qt, $bitcoin_enable_qt_test, $bitcoin_enable_qt_dbus BITCOIN_QT_CONFIGURE([$use_pkgconfig], [qt5]) -if test x$build_bitcoin_utils$build_bitcoind$bitcoin_enable_qt$use_tests$use_bench = xnonononono; then +if test x$build_bitcoin_utils$build_bitcoind$bitcoin_enable_qt$build_bitcoin_seeder$use_tests$use_bench = xnononononono; then use_boost=no else use_boost=yes fi if test x$use_boost = xyes; then dnl Minimum required Boost version define(MINIMUM_REQUIRED_BOOST, 1.47.0) dnl Check for boost libs AX_BOOST_BASE([MINIMUM_REQUIRED_BOOST]) AX_BOOST_SYSTEM AX_BOOST_FILESYSTEM AX_BOOST_PROGRAM_OPTIONS AX_BOOST_THREAD AX_BOOST_CHRONO if test x$use_reduce_exports = xyes; then AC_MSG_CHECKING([for working boost reduced exports]) TEMP_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$BOOST_CPPFLAGS $CPPFLAGS" AC_PREPROC_IFELSE([AC_LANG_PROGRAM([[ @%:@include ]], [[ #if BOOST_VERSION >= 104900 // Everything is okay #else # error Boost version is too old #endif ]])],[ AC_MSG_RESULT(yes) ],[ AC_MSG_ERROR([boost versions < 1.49 are known to be broken with reduced exports. Use --disable-reduce-exports.]) ]) CPPFLAGS="$TEMP_CPPFLAGS" fi fi if test x$use_reduce_exports = xyes; then CXXFLAGS="$CXXFLAGS $RE_CXXFLAGS" AX_CHECK_LINK_FLAG([[-Wl,--exclude-libs,ALL]], [RELDFLAGS="-Wl,--exclude-libs,ALL"]) fi if test x$use_tests = xyes; then if test x$HEXDUMP = x; then AC_MSG_ERROR(hexdump is required for tests) fi if test x$use_boost = xyes; then AX_BOOST_UNIT_TEST_FRAMEWORK dnl Determine if -DBOOST_TEST_DYN_LINK is needed AC_MSG_CHECKING([for dynamic linked boost test]) TEMP_LIBS="$LIBS" LIBS="$LIBS $BOOST_LDFLAGS $BOOST_UNIT_TEST_FRAMEWORK_LIB" TEMP_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" AC_LINK_IFELSE([AC_LANG_SOURCE([ #define BOOST_TEST_DYN_LINK #define BOOST_TEST_MAIN #include ])], [AC_MSG_RESULT(yes)] [TESTDEFS="$TESTDEFS -DBOOST_TEST_DYN_LINK"], [AC_MSG_RESULT(no)]) LIBS="$TEMP_LIBS" CPPFLAGS="$TEMP_CPPFLAGS" fi fi if test x$use_boost = xyes; then BOOST_LIBS="$BOOST_LDFLAGS $BOOST_SYSTEM_LIB $BOOST_FILESYSTEM_LIB $BOOST_PROGRAM_OPTIONS_LIB $BOOST_THREAD_LIB $BOOST_CHRONO_LIB" dnl If boost (prior to 1.57) was built without c++11, it emulated scoped enums dnl using c++98 constructs. Unfortunately, this implementation detail leaked into dnl the abi. This was fixed in 1.57. dnl When building against that installed version using c++11, the headers pick up dnl on the native c++11 scoped enum support and enable it, however it will fail to dnl link. This can be worked around by disabling c++11 scoped enums if linking will dnl fail. dnl BOOST_NO_SCOPED_ENUMS was changed to BOOST_NO_CXX11_SCOPED_ENUMS in 1.51. TEMP_LIBS="$LIBS" LIBS="$BOOST_LIBS $LIBS" TEMP_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" AC_MSG_CHECKING([for mismatched boost c++11 scoped enums]) AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include "boost/config.hpp" #include "boost/version.hpp" #if !defined(BOOST_NO_SCOPED_ENUMS) && !defined(BOOST_NO_CXX11_SCOPED_ENUMS) && BOOST_VERSION < 105700 #define BOOST_NO_SCOPED_ENUMS #define BOOST_NO_CXX11_SCOPED_ENUMS #define CHECK #endif #include "boost/filesystem.hpp" ]],[[ #if defined(CHECK) boost::filesystem::copy_file("foo", "bar"); #else choke; #endif ]])], [AC_MSG_RESULT(mismatched); BOOST_CPPFLAGS="$BOOST_CPPFLAGS -DBOOST_NO_SCOPED_ENUMS -DBOOST_NO_CXX11_SCOPED_ENUMS"], [AC_MSG_RESULT(ok)]) LIBS="$TEMP_LIBS" CPPFLAGS="$TEMP_CPPFLAGS" dnl Boost >= 1.50 uses sleep_for rather than the now-deprecated sleep, however dnl it was broken from 1.50 to 1.52 when backed by nanosleep. Use sleep_for if dnl a working version is available, else fall back to sleep. sleep was removed dnl after 1.56. dnl If neither is available, abort. TEMP_LIBS="$LIBS" LIBS="$BOOST_LIBS $LIBS" TEMP_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include #include ]],[[ #if BOOST_VERSION >= 105000 && (!defined(BOOST_HAS_NANOSLEEP) || BOOST_VERSION >= 105200) boost::this_thread::sleep_for(boost::chrono::milliseconds(0)); #else choke me #endif ]])], [boost_sleep=yes; AC_DEFINE(HAVE_WORKING_BOOST_SLEEP_FOR, 1, [Define this symbol if boost sleep_for works])], [boost_sleep=no]) LIBS="$TEMP_LIBS" CPPFLAGS="$TEMP_CPPFLAGS" if test x$boost_sleep != xyes; then TEMP_LIBS="$LIBS" LIBS="$BOOST_LIBS $LIBS" TEMP_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include #include #include ]],[[ #if BOOST_VERSION <= 105600 boost::this_thread::sleep(boost::posix_time::milliseconds(0)); #else choke me #endif ]])], [boost_sleep=yes; AC_DEFINE(HAVE_WORKING_BOOST_SLEEP, 1, [Define this symbol if boost sleep works])], [boost_sleep=no]) LIBS="$TEMP_LIBS" CPPFLAGS="$TEMP_CPPFLAGS" fi if test x$boost_sleep != xyes; then AC_MSG_ERROR(No working boost sleep implementation found.) fi fi if test x$use_pkgconfig = xyes; then : dnl m4_ifdef( [PKG_CHECK_MODULES], [ PKG_CHECK_MODULES([SSL], [libssl],, [AC_MSG_ERROR(openssl not found.)]) PKG_CHECK_MODULES([CRYPTO], [libcrypto],,[AC_MSG_ERROR(libcrypto not found.)]) BITCOIN_QT_CHECK([PKG_CHECK_MODULES([PROTOBUF], [protobuf], [have_protobuf=yes], [BITCOIN_QT_FAIL(libprotobuf not found)])]) if test x$use_qr != xno; then BITCOIN_QT_CHECK([PKG_CHECK_MODULES([QR], [libqrencode], [have_qrencode=yes], [have_qrencode=no])]) fi if test x$build_bitcoin_utils$build_bitcoind$bitcoin_enable_qt$use_tests != xnononono; then PKG_CHECK_MODULES([EVENT], [libevent],, [AC_MSG_ERROR(libevent not found.)]) if test x$TARGET_OS != xwindows; then PKG_CHECK_MODULES([EVENT_PTHREADS], [libevent_pthreads],, [AC_MSG_ERROR(libevent_pthreads not found.)]) fi fi if test "x$use_zmq" = "xyes"; then PKG_CHECK_MODULES([ZMQ],[libzmq >= 4], [AC_DEFINE([ENABLE_ZMQ],[1],[Define to 1 to enable ZMQ functions])], [AC_DEFINE([ENABLE_ZMQ],[0],[Define to 1 to enable ZMQ functions]) AC_MSG_WARN([libzmq version 4.x or greater not found, disabling]) use_zmq=no]) else AC_DEFINE_UNQUOTED([ENABLE_ZMQ],[0],[Define to 1 to enable ZMQ functions]) fi ] ) else AC_CHECK_HEADER([openssl/crypto.h],,AC_MSG_ERROR(libcrypto headers missing)) AC_CHECK_LIB([crypto], [main],CRYPTO_LIBS=-lcrypto, AC_MSG_ERROR(libcrypto missing)) AC_CHECK_HEADER([openssl/ssl.h],, AC_MSG_ERROR(libssl headers missing),) AC_CHECK_LIB([ssl], [main],SSL_LIBS=-lssl, AC_MSG_ERROR(libssl missing)) if test x$build_bitcoin_utils$build_bitcoind$bitcoin_enable_qt$use_tests != xnononono; then AC_CHECK_HEADER([event2/event.h],, AC_MSG_ERROR(libevent headers missing),) AC_CHECK_LIB([event],[main],EVENT_LIBS=-levent,AC_MSG_ERROR(libevent missing)) if test x$TARGET_OS != xwindows; then AC_CHECK_LIB([event_pthreads],[main],EVENT_PTHREADS_LIBS=-levent_pthreads,AC_MSG_ERROR(libevent_pthreads missing)) fi fi if test "x$use_zmq" = "xyes"; then AC_CHECK_HEADER([zmq.h], [AC_DEFINE([ENABLE_ZMQ],[1],[Define to 1 to enable ZMQ functions])], [AC_MSG_WARN([zmq.h not found, disabling zmq support]) use_zmq=no AC_DEFINE([ENABLE_ZMQ],[0],[Define to 1 to enable ZMQ functions])]) AC_CHECK_LIB([zmq],[zmq_ctx_shutdown],ZMQ_LIBS=-lzmq, [AC_MSG_WARN([libzmq >= 4.0 not found, disabling zmq support]) use_zmq=no AC_DEFINE([ENABLE_ZMQ],[0],[Define to 1 to enable ZMQ functions])]) else AC_DEFINE_UNQUOTED([ENABLE_ZMQ],[0],[Define to 1 to enable ZMQ functions]) fi if test "x$use_zmq" = "xyes"; then dnl Assume libzmq was built for static linking case $host in *mingw*) ZMQ_CFLAGS="$ZMQ_CFLAGS -DZMQ_STATIC" ;; esac fi BITCOIN_QT_CHECK(AC_CHECK_LIB([protobuf] ,[main],[PROTOBUF_LIBS=-lprotobuf], BITCOIN_QT_FAIL(libprotobuf not found))) if test x$use_qr != xno; then BITCOIN_QT_CHECK([AC_CHECK_LIB([qrencode], [main],[QR_LIBS=-lqrencode], [have_qrencode=no])]) BITCOIN_QT_CHECK([AC_CHECK_HEADER([qrencode.h],, have_qrencode=no)]) fi fi save_CXXFLAGS="${CXXFLAGS}" CXXFLAGS="${CXXFLAGS} ${CRYPTO_CFLAGS} ${SSL_CFLAGS}" AC_CHECK_DECLS([EVP_MD_CTX_new],,,[AC_INCLUDES_DEFAULT #include ]) CXXFLAGS="${save_CXXFLAGS}" dnl univalue check need_bundled_univalue=yes if test x$build_bitcoin_utils$build_bitcoind$bitcoin_enable_qt$use_tests$use_bench = xnonononono; then need_bundled_univalue=no else if test x$system_univalue != xno ; then found_univalue=no if test x$use_pkgconfig = xyes; then : #NOP m4_ifdef( [PKG_CHECK_MODULES], [ PKG_CHECK_MODULES([UNIVALUE],[libunivalue],[found_univalue=yes],[true]) ] ) else AC_CHECK_HEADER([univalue.h],[ AC_CHECK_LIB([univalue], [main],[ UNIVALUE_LIBS=-lunivalue found_univalue=yes ],[true]) ],[true]) fi if test x$found_univalue = xyes ; then system_univalue=yes need_bundled_univalue=no elif test x$system_univalue = xyes ; then AC_MSG_ERROR([univalue not found]) else system_univalue=no fi fi if test x$need_bundled_univalue = xyes ; then UNIVALUE_CFLAGS='-I$(srcdir)/univalue/include' UNIVALUE_LIBS='univalue/libunivalue.la' fi fi AM_CONDITIONAL([EMBEDDED_UNIVALUE],[test x$need_bundled_univalue = xyes]) AC_SUBST(UNIVALUE_CFLAGS) AC_SUBST(UNIVALUE_LIBS) BITCOIN_QT_PATH_PROGS([PROTOC], [protoc],$protoc_bin_path) AC_MSG_CHECKING([whether to build bitcoind]) AM_CONDITIONAL([BUILD_BITCOIND], [test x$build_bitcoind = xyes]) AC_MSG_RESULT($build_bitcoind) +AC_MSG_CHECKING([whether to build bitcoin-seeder]) +AM_CONDITIONAL([BUILD_BITCOIN_SEEDER], [test x$build_bitcoin_seeder = xyes]) +AC_MSG_RESULT($build_bitcoin_seeder) + AC_MSG_CHECKING([whether to build utils (bitcoin-cli bitcoin-tx)]) AM_CONDITIONAL([BUILD_BITCOIN_UTILS], [test x$build_bitcoin_utils = xyes]) AC_MSG_RESULT($build_bitcoin_utils) AC_MSG_CHECKING([whether to build libraries]) AM_CONDITIONAL([BUILD_BITCOIN_LIBS], [test x$build_bitcoin_libs = xyes]) if test x$build_bitcoin_libs = xyes; then AC_DEFINE(HAVE_CONSENSUS_LIB, 1, [Define this symbol if the consensus lib has been built]) AC_CONFIG_FILES([libbitcoinconsensus.pc:libbitcoinconsensus.pc.in]) fi AC_MSG_RESULT($build_bitcoin_libs) AC_LANG_POP if test "x$use_ccache" != "xno"; then AC_MSG_CHECKING(if ccache should be used) if test x$CCACHE = x; then if test "x$use_ccache" = "xyes"; then AC_MSG_ERROR([ccache not found.]); else use_ccache=no fi else use_ccache=yes CC="$ac_cv_path_CCACHE $CC" CXX="$ac_cv_path_CCACHE $CXX" fi AC_MSG_RESULT($use_ccache) fi if test "x$use_ccache" = "xyes"; then AX_CHECK_PREPROC_FLAG([-Qunused-arguments],[CPPFLAGS="-Qunused-arguments $CPPFLAGS"]) fi dnl enable wallet AC_MSG_CHECKING([if wallet should be enabled]) if test x$enable_wallet != xno; then AC_MSG_RESULT(yes) AC_DEFINE_UNQUOTED([ENABLE_WALLET],[1],[Define to 1 to enable wallet functions]) else AC_MSG_RESULT(no) fi dnl enable upnp support AC_MSG_CHECKING([whether to build with support for UPnP]) if test x$have_miniupnpc = xno; then if test x$use_upnp = xyes; then AC_MSG_ERROR("UPnP requested but cannot be built. use --without-miniupnpc") fi AC_MSG_RESULT(no) else if test x$use_upnp != xno; then AC_MSG_RESULT(yes) AC_MSG_CHECKING([whether to build with UPnP enabled by default]) use_upnp=yes upnp_setting=0 if test x$use_upnp_default != xno; then use_upnp_default=yes upnp_setting=1 fi AC_MSG_RESULT($use_upnp_default) AC_DEFINE_UNQUOTED([USE_UPNP],[$upnp_setting],[UPnP support not compiled if undefined, otherwise value (0 or 1) determines default state]) if test x$TARGET_OS = xwindows; then MINIUPNPC_CPPFLAGS="-DSTATICLIB -DMINIUPNP_STATICLIB" fi else AC_MSG_RESULT(no) fi fi dnl these are only used when qt is enabled BUILD_TEST_QT="" if test x$bitcoin_enable_qt != xno; then dnl enable dbus support AC_MSG_CHECKING([whether to build GUI with support for D-Bus]) if test x$bitcoin_enable_qt_dbus != xno; then AC_DEFINE([USE_DBUS],[1],[Define if dbus support should be compiled in]) fi AC_MSG_RESULT($bitcoin_enable_qt_dbus) dnl enable qr support AC_MSG_CHECKING([whether to build GUI with support for QR codes]) if test x$have_qrencode = xno; then if test x$use_qr = xyes; then AC_MSG_ERROR("QR support requested but cannot be built. use --without-qrencode") fi AC_MSG_RESULT(no) else if test x$use_qr != xno; then AC_MSG_RESULT(yes) AC_DEFINE([USE_QRCODE],[1],[Define if QR support should be compiled in]) use_qr=yes else AC_MSG_RESULT(no) fi fi if test x$XGETTEXT = x; then AC_MSG_WARN("xgettext is required to update qt translations") fi AC_MSG_CHECKING([whether to build test_bitcoin-qt]) if test x$use_gui_tests$bitcoin_enable_qt_test = xyesyes; then AC_MSG_RESULT([yes]) BUILD_TEST_QT="yes" else AC_MSG_RESULT([no]) fi fi AM_CONDITIONAL([ENABLE_ZMQ], [test "x$use_zmq" = "xyes"]) AC_MSG_CHECKING([whether to build test_bitcoin]) if test x$use_tests = xyes; then AC_MSG_RESULT([yes]) BUILD_TEST="yes" else AC_MSG_RESULT([no]) BUILD_TEST="" fi AC_MSG_CHECKING([whether to reduce exports]) if test x$use_reduce_exports = xyes; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) fi if test x$build_bitcoin_utils$build_bitcoin_libs$build_bitcoind$bitcoin_enable_qt$use_bench$use_tests = xnononononono; then - AC_MSG_ERROR([No targets! Please specify at least one of: --with-utils --with-libs --with-daemon --with-gui --enable-bench or --enable-tests]) + AC_MSG_ERROR([No targets! Please specify at least one of: --with-utils --with-libs --with-daemon --with-seeder --with-gui --enable-bench or --enable-tests]) fi AM_CONDITIONAL([TARGET_DARWIN], [test x$TARGET_OS = xdarwin]) AM_CONDITIONAL([BUILD_DARWIN], [test x$BUILD_OS = xdarwin]) AM_CONDITIONAL([TARGET_WINDOWS], [test x$TARGET_OS = xwindows]) AM_CONDITIONAL([ENABLE_WALLET],[test x$enable_wallet = xyes]) AM_CONDITIONAL([ENABLE_TESTS],[test x$BUILD_TEST = xyes]) AM_CONDITIONAL([ENABLE_QT],[test x$bitcoin_enable_qt = xyes]) AM_CONDITIONAL([ENABLE_QT_TESTS],[test x$BUILD_TEST_QT = xyes]) AM_CONDITIONAL([ENABLE_BENCH],[test x$use_bench = xyes]) AM_CONDITIONAL([USE_QRCODE], [test x$use_qr = xyes]) AM_CONDITIONAL([USE_LCOV],[test x$use_lcov = xyes]) AM_CONDITIONAL([GLIBC_BACK_COMPAT],[test x$use_glibc_compat = xyes]) AM_CONDITIONAL([HARDEN],[test x$use_hardening = xyes]) AC_DEFINE(CLIENT_VERSION_MAJOR, _CLIENT_VERSION_MAJOR, [Major version]) AC_DEFINE(CLIENT_VERSION_MINOR, _CLIENT_VERSION_MINOR, [Minor version]) AC_DEFINE(CLIENT_VERSION_REVISION, _CLIENT_VERSION_REVISION, [Build revision]) AC_DEFINE(CLIENT_VERSION_BUILD, _CLIENT_VERSION_BUILD, [Version Build]) AC_DEFINE(CLIENT_VERSION_IS_RELEASE, _CLIENT_VERSION_IS_RELEASE, [Version is release]) AC_DEFINE(COPYRIGHT_YEAR, _COPYRIGHT_YEAR, [Copyright year]) AC_DEFINE(COPYRIGHT_HOLDERS, "_COPYRIGHT_HOLDERS", [Copyright holder(s) before %s replacement]) AC_DEFINE(COPYRIGHT_HOLDERS_SUBSTITUTION, "_COPYRIGHT_HOLDERS_SUBSTITUTION", [Replacement for %s in copyright holders string]) define(_COPYRIGHT_HOLDERS_FINAL, [patsubst(_COPYRIGHT_HOLDERS, [%s], [_COPYRIGHT_HOLDERS_SUBSTITUTION])]) AC_DEFINE(COPYRIGHT_HOLDERS_FINAL, "_COPYRIGHT_HOLDERS_FINAL", [Copyright holder(s)]) AC_SUBST(CLIENT_VERSION_MAJOR, _CLIENT_VERSION_MAJOR) AC_SUBST(CLIENT_VERSION_MINOR, _CLIENT_VERSION_MINOR) AC_SUBST(CLIENT_VERSION_REVISION, _CLIENT_VERSION_REVISION) AC_SUBST(CLIENT_VERSION_BUILD, _CLIENT_VERSION_BUILD) AC_SUBST(CLIENT_VERSION_IS_RELEASE, _CLIENT_VERSION_IS_RELEASE) AC_SUBST(COPYRIGHT_YEAR, _COPYRIGHT_YEAR) AC_SUBST(COPYRIGHT_HOLDERS, "_COPYRIGHT_HOLDERS") AC_SUBST(COPYRIGHT_HOLDERS_SUBSTITUTION, "_COPYRIGHT_HOLDERS_SUBSTITUTION") AC_SUBST(COPYRIGHT_HOLDERS_FINAL, "_COPYRIGHT_HOLDERS_FINAL") AC_SUBST(BITCOIN_DAEMON_NAME) AC_SUBST(BITCOIN_GUI_NAME) AC_SUBST(BITCOIN_CLI_NAME) AC_SUBST(BITCOIN_TX_NAME) +AC_SUBST(BITCOIN_SEEDER_NAME) AC_SUBST(RELDFLAGS) AC_SUBST(ERROR_CXXFLAGS) AC_SUBST(HARDENED_CXXFLAGS) AC_SUBST(HARDENED_CPPFLAGS) AC_SUBST(HARDENED_LDFLAGS) AC_SUBST(PIC_FLAGS) AC_SUBST(PIE_FLAGS) AC_SUBST(LIBTOOL_APP_LDFLAGS) AC_SUBST(USE_UPNP) AC_SUBST(USE_QRCODE) AC_SUBST(BOOST_LIBS) AC_SUBST(TESTDEFS) AC_SUBST(LEVELDB_TARGET_FLAGS) AC_SUBST(MINIUPNPC_CPPFLAGS) AC_SUBST(MINIUPNPC_LIBS) AC_SUBST(CRYPTO_LIBS) AC_SUBST(SSL_LIBS) AC_SUBST(EVENT_LIBS) AC_SUBST(EVENT_PTHREADS_LIBS) AC_SUBST(ZMQ_LIBS) AC_SUBST(PROTOBUF_LIBS) AC_SUBST(QR_LIBS) AC_CONFIG_FILES([Makefile src/Makefile doc/man/Makefile share/setup.nsi share/qt/Info.plist src/test/buildenv.py]) AC_CONFIG_FILES([qa/pull-tester/tests_config.py],[chmod +x qa/pull-tester/tests_config.py]) AC_CONFIG_FILES([contrib/devtools/split-debug.sh],[chmod +x contrib/devtools/split-debug.sh]) AC_CONFIG_LINKS([qa/pull-tester/rpc-tests.py:qa/pull-tester/rpc-tests.py]) dnl boost's m4 checks do something really nasty: they export these vars. As a dnl result, they leak into secp256k1's configure and crazy things happen. dnl Until this is fixed upstream and we've synced, we'll just un-export them. CPPFLAGS_TEMP="$CPPFLAGS" unset CPPFLAGS CPPFLAGS="$CPPFLAGS_TEMP" LDFLAGS_TEMP="$LDFLAGS" unset LDFLAGS LDFLAGS="$LDFLAGS_TEMP" LIBS_TEMP="$LIBS" unset LIBS LIBS="$LIBS_TEMP" PKGCONFIG_PATH_TEMP="$PKG_CONFIG_PATH" unset PKG_CONFIG_PATH PKG_CONFIG_PATH="$PKGCONFIG_PATH_TEMP" PKGCONFIG_LIBDIR_TEMP="$PKG_CONFIG_LIBDIR" unset PKG_CONFIG_LIBDIR PKG_CONFIG_LIBDIR="$PKGCONFIG_LIBDIR_TEMP" if test x$need_bundled_univalue = xyes; then AC_CONFIG_SUBDIRS([src/univalue]) fi ac_configure_args="${ac_configure_args} --disable-shared --with-pic --with-bignum=no --enable-module-recovery" AC_CONFIG_SUBDIRS([src/secp256k1]) AC_OUTPUT dnl Taken from https://wiki.debian.org/RpathIssue case $host in *-*-linux-gnu) AC_MSG_RESULT([Fixing libtool for -rpath problems.]) sed < libtool > libtool-2 \ 's/^hardcode_libdir_flag_spec.*$'/'hardcode_libdir_flag_spec=" -D__LIBTOOL_IS_A_FOOL__ "/' mv libtool-2 libtool chmod 755 libtool ;; esac dnl Replace the BUILDDIR path with the correct Windows path if compiling on Native Windows case ${OS} in *Windows*) sed 's/BUILDDIR="\/\([[a-z]]\)/BUILDDIR="\1:/' qa/pull-tester/tests_config.py > qa/pull-tester/tests_config-2.py mv qa/pull-tester/tests_config-2.py qa/pull-tester/tests_config.py ;; esac echo echo "Options used to compile and link:" echo " with wallet = $enable_wallet" echo " with gui / qt = $bitcoin_enable_qt" if test x$bitcoin_enable_qt != xno; then echo " qt version = $bitcoin_qt_got_major_vers" echo " with qr = $use_qr" fi echo " with zmq = $use_zmq" echo " with test = $use_tests" echo " with bench = $use_bench" echo " with upnp = $use_upnp" echo " debug enabled = $enable_debug" echo " werror = $enable_werror" echo echo " sanitizers " echo " asan = $enable_asan" echo " tsan = $enable_tsan" echo " ubsan = $enable_ubsan" echo echo " target os = $TARGET_OS" echo " build os = $BUILD_OS" echo echo " CC = $CC" echo " CFLAGS = $CFLAGS" echo " CPPFLAGS = $CPPFLAGS" echo " CXX = $CXX" echo " CXXFLAGS = $CXXFLAGS" echo " LDFLAGS = $LDFLAGS" echo diff --git a/src/Makefile.am b/src/Makefile.am index 60a4e6bea..a02da9e6a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,521 +1,564 @@ # Copyright (c) 2013-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. DIST_SUBDIRS = secp256k1 univalue AM_LDFLAGS = $(PTHREAD_CFLAGS) $(LIBTOOL_LDFLAGS) $(HARDENED_LDFLAGS) AM_CXXFLAGS = $(HARDENED_CXXFLAGS) $(ERROR_CXXFLAGS) AM_CPPFLAGS = $(HARDENED_CPPFLAGS) EXTRA_LIBRARIES = if EMBEDDED_UNIVALUE LIBUNIVALUE = univalue/libunivalue.la $(LIBUNIVALUE): $(wildcard univalue/lib/*) $(wildcard univalue/include/*) $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C $(@D) $(@F) else LIBUNIVALUE = $(UNIVALUE_LIBS) endif BITCOIN_CONFIG_INCLUDES=-I$(builddir)/config BITCOIN_INCLUDES=-I$(builddir) -I$(builddir)/obj $(BDB_CPPFLAGS) $(BOOST_CPPFLAGS) $(LEVELDB_CPPFLAGS) $(CRYPTO_CFLAGS) $(SSL_CFLAGS) BITCOIN_INCLUDES += -I$(srcdir)/secp256k1/include BITCOIN_INCLUDES += $(UNIVALUE_CFLAGS) +BITCOIN_SEEDER_INCLUDES = -I$(srcdir)/seeder +BITCOIN_SEEDER_INCLUDES += $(BITCOIN_INCLUDES) + LIBBITCOIN_SERVER=libbitcoin_server.a LIBBITCOIN_COMMON=libbitcoin_common.a LIBBITCOIN_CONSENSUS=libbitcoin_consensus.a LIBBITCOIN_CLI=libbitcoin_cli.a LIBBITCOIN_UTIL=libbitcoin_util.a LIBBITCOIN_CRYPTO=crypto/libbitcoin_crypto.a LIBBITCOINQT=qt/libbitcoinqt.a LIBSECP256K1=secp256k1/libsecp256k1.la if ENABLE_ZMQ LIBBITCOIN_ZMQ=libbitcoin_zmq.a endif if BUILD_BITCOIN_LIBS LIBBITCOINCONSENSUS=libbitcoinconsensus.la endif +if BUILD_BITCOIN_SEEDER +LIBBITCOIN_SEEDER=libbitcoin_seeder.a +endif if ENABLE_WALLET LIBBITCOIN_WALLET=libbitcoin_wallet.a endif $(LIBSECP256K1): $(wildcard secp256k1/src/*) $(wildcard secp256k1/include/*) $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C $(@D) $(@F) # Make is not made aware of per-object dependencies to avoid limiting building parallelization # But to build the less dependent modules first, we manually select their order here: EXTRA_LIBRARIES += \ $(LIBBITCOIN_CRYPTO) \ $(LIBBITCOIN_UTIL) \ $(LIBBITCOIN_COMMON) \ $(LIBBITCOIN_CONSENSUS) \ $(LIBBITCOIN_SERVER) \ $(LIBBITCOIN_CLI) \ + $(LIBBITCOIN_SEEDER) \ $(LIBBITCOIN_WALLET) \ $(LIBBITCOIN_ZMQ) lib_LTLIBRARIES = $(LIBBITCOINCONSENSUS) bin_PROGRAMS = noinst_PROGRAMS = TESTS = BENCHMARKS = if BUILD_BITCOIND bin_PROGRAMS += bitcoind endif +if BUILD_BITCOIN_SEEDER + bin_PROGRAMS += bitcoin-seeder +endif + if BUILD_BITCOIN_UTILS bin_PROGRAMS += bitcoin-cli bitcoin-tx endif .PHONY: FORCE check-symbols check-security # bitcoin core # BITCOIN_CORE_H = \ addrdb.h \ addrman.h \ base58.h \ bloom.h \ blockencodings.h \ chain.h \ chainparams.h \ chainparamsbase.h \ chainparamsseeds.h \ checkpoints.h \ checkqueue.h \ clientversion.h \ coins.h \ compat.h \ compat/byteswap.h \ compat/endian.h \ compat/sanity.h \ compressor.h \ config.h \ consensus/consensus.h \ core_io.h \ core_memusage.h \ cuckoocache.h \ globals.h \ httprpc.h \ httpserver.h \ indirectmap.h \ init.h \ key.h \ keystore.h \ dbwrapper.h \ limitedmap.h \ memusage.h \ merkleblock.h \ miner.h \ net.h \ net_processing.h \ netaddress.h \ netbase.h \ netmessagemaker.h \ noui.h \ policy/fees.h \ policy/policy.h \ pow.h \ protocol.h \ random.h \ reverselock.h \ rpc/blockchain.h \ rpc/client.h \ rpc/misc.h \ rpc/protocol.h \ rpc/server.h \ rpc/register.h \ scheduler.h \ script/scriptcache.h \ script/sigcache.h \ script/sign.h \ script/standard.h \ script/ismine.h \ streams.h \ support/allocators/secure.h \ support/allocators/zeroafterfree.h \ support/cleanse.h \ support/events.h \ support/lockedpool.h \ sync.h \ threadsafety.h \ threadinterrupt.h \ timedata.h \ torcontrol.h \ txdb.h \ txmempool.h \ ui_interface.h \ undo.h \ util.h \ utilmoneystr.h \ utiltime.h \ validation.h \ validationinterface.h \ versionbits.h \ wallet/coincontrol.h \ wallet/crypter.h \ wallet/db.h \ wallet/finaltx.h \ wallet/rpcdump.h \ wallet/rpcwallet.h \ wallet/wallet.h \ wallet/walletdb.h \ warnings.h \ zmq/zmqabstractnotifier.h \ zmq/zmqconfig.h\ zmq/zmqnotificationinterface.h \ zmq/zmqpublishnotifier.h obj/build.h: FORCE @$(MKDIR_P) $(builddir)/obj @$(top_srcdir)/share/genbuild.sh $(abs_top_builddir)/src/obj/build.h \ $(abs_top_srcdir) libbitcoin_util_a-clientversion.$(OBJEXT): obj/build.h # server: shared between bitcoind and bitcoin-qt libbitcoin_server_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(MINIUPNPC_CPPFLAGS) $(EVENT_CFLAGS) $(EVENT_PTHREADS_CFLAGS) libbitcoin_server_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libbitcoin_server_a_SOURCES = \ addrman.cpp \ addrdb.cpp \ bloom.cpp \ blockencodings.cpp \ chain.cpp \ checkpoints.cpp \ config.cpp \ globals.cpp \ httprpc.cpp \ httpserver.cpp \ init.cpp \ dbwrapper.cpp \ merkleblock.cpp \ miner.cpp \ net.cpp \ net_processing.cpp \ noui.cpp \ policy/fees.cpp \ policy/policy.cpp \ pow.cpp \ rest.cpp \ rpc/abc.cpp \ rpc/blockchain.cpp \ rpc/mining.cpp \ rpc/misc.cpp \ rpc/net.cpp \ rpc/rawtransaction.cpp \ rpc/server.cpp \ script/scriptcache.cpp \ script/sigcache.cpp \ script/ismine.cpp \ timedata.cpp \ torcontrol.cpp \ txdb.cpp \ txmempool.cpp \ ui_interface.cpp \ validation.cpp \ validationinterface.cpp \ versionbits.cpp \ $(BITCOIN_CORE_H) if ENABLE_ZMQ libbitcoin_zmq_a_CPPFLAGS = $(BITCOIN_INCLUDES) $(ZMQ_CFLAGS) libbitcoin_zmq_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libbitcoin_zmq_a_SOURCES = \ zmq/zmqabstractnotifier.cpp \ zmq/zmqnotificationinterface.cpp \ zmq/zmqpublishnotifier.cpp endif # wallet: shared between bitcoind and bitcoin-qt, but only linked # when wallet enabled libbitcoin_wallet_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) libbitcoin_wallet_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libbitcoin_wallet_a_SOURCES = \ wallet/crypter.cpp \ wallet/db.cpp \ wallet/finaltx.cpp \ wallet/rpcdump.cpp \ wallet/rpcwallet.cpp \ wallet/wallet.cpp \ wallet/walletdb.cpp \ $(BITCOIN_CORE_H) # crypto primitives library crypto_libbitcoin_crypto_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_CONFIG_INCLUDES) crypto_libbitcoin_crypto_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) crypto_libbitcoin_crypto_a_SOURCES = \ crypto/aes.cpp \ crypto/aes.h \ crypto/chacha20.h \ crypto/chacha20.cpp \ crypto/common.h \ crypto/hmac_sha256.cpp \ crypto/hmac_sha256.h \ crypto/hmac_sha512.cpp \ crypto/hmac_sha512.h \ crypto/ripemd160.cpp \ crypto/ripemd160.h \ crypto/sha1.cpp \ crypto/sha1.h \ crypto/sha256.cpp \ crypto/sha256.h \ crypto/sha512.cpp \ crypto/sha512.h # consensus: shared between all executables that validate any consensus rules. libbitcoin_consensus_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) libbitcoin_consensus_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libbitcoin_consensus_a_SOURCES = \ amount.h \ arith_uint256.cpp \ arith_uint256.h \ consensus/merkle.cpp \ consensus/merkle.h \ consensus/params.h \ consensus/validation.h \ hash.cpp \ hash.h \ prevector.h \ primitives/block.cpp \ primitives/block.h \ primitives/transaction.cpp \ primitives/transaction.h \ pubkey.cpp \ pubkey.h \ script/bitcoinconsensus.cpp \ script/interpreter.cpp \ script/interpreter.h \ script/script.cpp \ script/script.h \ script/script_error.cpp \ script/script_error.h \ serialize.h \ tinyformat.h \ uint256.cpp \ uint256.h \ utilstrencodings.cpp \ utilstrencodings.h \ version.h # common: shared between bitcoind, and bitcoin-qt and non-server tools libbitcoin_common_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) libbitcoin_common_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libbitcoin_common_a_SOURCES = \ amount.cpp \ base58.cpp \ chainparams.cpp \ coins.cpp \ compressor.cpp \ core_read.cpp \ core_write.cpp \ key.cpp \ keystore.cpp \ netaddress.cpp \ netbase.cpp \ protocol.cpp \ scheduler.cpp \ script/sign.cpp \ script/standard.cpp \ warnings.cpp \ $(BITCOIN_CORE_H) # util: shared between all executables. # This library *must* be included to make sure that the glibc # backward-compatibility objects and their sanity checks are linked. libbitcoin_util_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) libbitcoin_util_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libbitcoin_util_a_SOURCES = \ support/lockedpool.cpp \ chainparamsbase.cpp \ clientversion.cpp \ compat/glibc_sanity.cpp \ compat/glibcxx_sanity.cpp \ compat/strnlen.cpp \ random.cpp \ rpc/protocol.cpp \ support/cleanse.cpp \ sync.cpp \ threadinterrupt.cpp \ util.cpp \ utilmoneystr.cpp \ utilstrencodings.cpp \ utiltime.cpp \ $(BITCOIN_CORE_H) if GLIBC_BACK_COMPAT libbitcoin_util_a_SOURCES += compat/glibc_compat.cpp endif # cli: shared between bitcoin-cli and bitcoin-qt libbitcoin_cli_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) libbitcoin_cli_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libbitcoin_cli_a_SOURCES = \ rpc/client.cpp \ $(BITCOIN_CORE_H) +# seeder library +libbitcoin_seeder_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_SEEDER_INCLUDES) +libbitcoin_seeder_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +libbitcoin_seeder_a_SOURCES = \ + seeder/bitcoin.cpp \ + seeder/bitcoin.h \ + seeder/compat.h \ + seeder/db.cpp \ + seeder/db.h \ + seeder/dns.c \ + seeder/dns.h \ + seeder/netbase.cpp \ + seeder/netbase.h \ + seeder/protocol.cpp \ + seeder/protocol.h \ + seeder/serialize.h \ + seeder/strlcpy.h \ + seeder/uint256.h \ + seeder/util.cpp \ + seeder/util.h + nodist_libbitcoin_util_a_SOURCES = $(srcdir)/obj/build.h # # bitcoind binary # bitcoind_SOURCES = bitcoind.cpp bitcoind_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) bitcoind_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) bitcoind_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) if TARGET_WINDOWS bitcoind_SOURCES += bitcoind-res.rc endif bitcoind_LDADD = \ $(LIBBITCOIN_SERVER) \ $(LIBBITCOIN_COMMON) \ $(LIBUNIVALUE) \ $(LIBBITCOIN_UTIL) \ $(LIBBITCOIN_WALLET) \ $(LIBBITCOIN_ZMQ) \ $(LIBBITCOIN_CONSENSUS) \ $(LIBBITCOIN_CRYPTO) \ $(LIBLEVELDB) \ $(LIBMEMENV) \ $(LIBSECP256K1) bitcoind_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(ZMQ_LIBS) # bitcoin-cli binary # bitcoin_cli_SOURCES = bitcoin-cli.cpp bitcoin_cli_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(EVENT_CFLAGS) bitcoin_cli_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) bitcoin_cli_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) if TARGET_WINDOWS bitcoin_cli_SOURCES += bitcoin-cli-res.rc endif bitcoin_cli_LDADD = \ $(LIBBITCOIN_CLI) \ $(LIBUNIVALUE) \ $(LIBBITCOIN_UTIL) \ $(LIBBITCOIN_CRYPTO) bitcoin_cli_LDADD += $(BOOST_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(EVENT_LIBS) # +# bitcoin-seeder binary # +bitcoin_seeder_SOURCES = seeder/main.cpp +bitcoin_seeder_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_SEEDER_INCLUDES) +bitcoin_seeder_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +bitcoin_seeder_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) + +bitcoin_seeder_LDADD = $(LIBBITCOIN_SEEDER) + +bitcoin_seeder_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(ZMQ_LIBS) +# + # bitcoin-tx binary # bitcoin_tx_SOURCES = bitcoin-tx.cpp bitcoin_tx_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) bitcoin_tx_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) bitcoin_tx_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) if TARGET_WINDOWS bitcoin_tx_SOURCES += bitcoin-tx-res.rc endif bitcoin_tx_LDADD = \ $(LIBUNIVALUE) \ $(LIBBITCOIN_COMMON) \ $(LIBBITCOIN_UTIL) \ $(LIBBITCOIN_CONSENSUS) \ $(LIBBITCOIN_CRYPTO) \ $(LIBSECP256K1) bitcoin_tx_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS) # # bitcoinconsensus library # if BUILD_BITCOIN_LIBS include_HEADERS = script/bitcoinconsensus.h libbitcoinconsensus_la_SOURCES = $(crypto_libbitcoin_crypto_a_SOURCES) $(libbitcoin_consensus_a_SOURCES) if GLIBC_BACK_COMPAT libbitcoinconsensus_la_SOURCES += compat/glibc_compat.cpp endif libbitcoinconsensus_la_LDFLAGS = $(AM_LDFLAGS) -no-undefined $(RELDFLAGS) libbitcoinconsensus_la_LIBADD = $(LIBSECP256K1) libbitcoinconsensus_la_CPPFLAGS = $(AM_CPPFLAGS) -I$(builddir)/obj -I$(srcdir)/secp256k1/include -DBUILD_BITCOIN_INTERNAL libbitcoinconsensus_la_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) endif # CTAES_DIST = crypto/ctaes/bench.c CTAES_DIST += crypto/ctaes/ctaes.c CTAES_DIST += crypto/ctaes/ctaes.h CTAES_DIST += crypto/ctaes/README.md CTAES_DIST += crypto/ctaes/test.c CLEANFILES = $(EXTRA_LIBRARIES) CLEANFILES += *.gcda *.gcno CLEANFILES += compat/*.gcda compat/*.gcno CLEANFILES += consensus/*.gcda consensus/*.gcno CLEANFILES += crypto/*.gcda crypto/*.gcno CLEANFILES += policy/*.gcda policy/*.gcno CLEANFILES += primitives/*.gcda primitives/*.gcno CLEANFILES += script/*.gcda script/*.gcno CLEANFILES += support/*.gcda support/*.gcno CLEANFILES += univalue/*.gcda univalue/*.gcno CLEANFILES += wallet/*.gcda wallet/*.gcno CLEANFILES += wallet/test/*.gcda wallet/test/*.gcno CLEANFILES += zmq/*.gcda zmq/*.gcno DISTCLEANFILES = obj/build.h EXTRA_DIST = $(CTAES_DIST) clean-local: -$(MAKE) -C secp256k1 clean -$(MAKE) -C univalue clean -rm -f leveldb/*/*.gcda leveldb/*/*.gcno leveldb/helpers/memenv/*.gcda leveldb/helpers/memenv/*.gcno -rm -rf test/__pycache__ .rc.o: @test -f $(WINDRES) ## FIXME: How to get the appropriate modulename_CPPFLAGS in here? $(AM_V_GEN) $(WINDRES) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(CPPFLAGS) -DWINDRES_PREPROC -i $< -o $@ .mm.o: $(AM_V_CXX) $(OBJCXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CXXFLAGS) $(QT_INCLUDES) $(AM_CXXFLAGS) $(PIE_FLAGS) $(CXXFLAGS) -c -o $@ $< check-symbols: $(bin_PROGRAMS) if GLIBC_BACK_COMPAT @echo "Checking glibc back compat..." $(AM_V_at) READELF=$(READELF) CPPFILT=$(CPPFILT) $(top_srcdir)/contrib/devtools/symbol-check.py < $(bin_PROGRAMS) endif check-security: $(bin_PROGRAMS) if HARDEN @echo "Checking binary security..." $(AM_V_at) READELF=$(READELF) OBJDUMP=$(OBJDUMP) $(top_srcdir)/contrib/devtools/security-check.py < $(bin_PROGRAMS) endif %.pb.cc %.pb.h: %.proto @test -f $(PROTOC) $(AM_V_GEN) $(PROTOC) --cpp_out=$(@D) --proto_path=$( + +#include "db.h" +#include "netbase.h" +#include "protocol.h" +#include "serialize.h" +#include "uint256.h" + +#define BITCOIN_SEED_NONCE 0x0539a019ca550825ULL + +using namespace std; + +class CNode { + SOCKET sock; + CDataStream vSend; + CDataStream vRecv; + unsigned int nHeaderStart; + unsigned int nMessageStart; + int nVersion; + string strSubVer; + int nStartingHeight; + vector *vAddr; + int ban; + int64 doneAfter; + CAddress you; + + int GetTimeout() { + if (you.IsTor()) + return 120; + else + return 30; + } + + void BeginMessage(const char *pszCommand) { + if (nHeaderStart != -1) AbortMessage(); + nHeaderStart = vSend.size(); + vSend << CMessageHeader(pszCommand, 0); + nMessageStart = vSend.size(); + // printf("%s: SEND %s\n", ToString(you).c_str(), pszCommand); + } + + void AbortMessage() { + if (nHeaderStart == -1) return; + vSend.resize(nHeaderStart); + nHeaderStart = -1; + nMessageStart = -1; + } + + void EndMessage() { + if (nHeaderStart == -1) return; + unsigned int nSize = vSend.size() - nMessageStart; + memcpy((char *)&vSend[nHeaderStart] + + offsetof(CMessageHeader, nMessageSize), + &nSize, sizeof(nSize)); + if (vSend.GetVersion() >= 209) { + uint256 hash = Hash(vSend.begin() + nMessageStart, vSend.end()); + unsigned int nChecksum = 0; + memcpy(&nChecksum, &hash, sizeof(nChecksum)); + assert(nMessageStart - nHeaderStart >= + offsetof(CMessageHeader, nChecksum) + sizeof(nChecksum)); + memcpy((char *)&vSend[nHeaderStart] + + offsetof(CMessageHeader, nChecksum), + &nChecksum, sizeof(nChecksum)); + } + nHeaderStart = -1; + nMessageStart = -1; + } + + void Send() { + if (sock == INVALID_SOCKET) return; + if (vSend.empty()) return; + int nBytes = send(sock, &vSend[0], vSend.size(), 0); + if (nBytes > 0) { + vSend.erase(vSend.begin(), vSend.begin() + nBytes); + } else { + close(sock); + sock = INVALID_SOCKET; + } + } + + void PushVersion() { + int64 nTime = time(NULL); + uint64 nLocalNonce = BITCOIN_SEED_NONCE; + int64 nLocalServices = 0; + CAddress me(CService("0.0.0.0")); + BeginMessage("version"); + int nBestHeight = GetRequireHeight(); + string ver = "/bitcoin-seeder:0.01/"; + vSend << PROTOCOL_VERSION << nLocalServices << nTime << you << me + << nLocalNonce << ver << nBestHeight; + EndMessage(); + } + + void GotVersion() { + // printf("\n%s: version %i\n", ToString(you).c_str(), nVersion); + if (vAddr) { + BeginMessage("getaddr"); + EndMessage(); + doneAfter = time(NULL) + GetTimeout(); + } else { + doneAfter = time(NULL) + 1; + } + } + + bool ProcessMessage(string strCommand, CDataStream &vRecv) { + // printf("%s: RECV %s\n", ToString(you).c_str(), + // strCommand.c_str()); + if (strCommand == "version") { + int64 nTime; + CAddress addrMe; + CAddress addrFrom; + uint64 nNonce = 1; + vRecv >> nVersion >> you.nServices >> nTime >> addrMe; + if (nVersion == 10300) nVersion = 300; + if (nVersion >= 106 && !vRecv.empty()) vRecv >> addrFrom >> nNonce; + if (nVersion >= 106 && !vRecv.empty()) vRecv >> strSubVer; + if (nVersion >= 209 && !vRecv.empty()) vRecv >> nStartingHeight; + + if (nVersion >= 209) { + BeginMessage("verack"); + EndMessage(); + } + vSend.SetVersion(min(nVersion, PROTOCOL_VERSION)); + if (nVersion < 209) { + this->vRecv.SetVersion(min(nVersion, PROTOCOL_VERSION)); + GotVersion(); + } + return false; + } + + if (strCommand == "verack") { + this->vRecv.SetVersion(min(nVersion, PROTOCOL_VERSION)); + GotVersion(); + return false; + } + + if (strCommand == "addr" && vAddr) { + vector vAddrNew; + vRecv >> vAddrNew; + // printf("%s: got %i addresses\n", ToString(you).c_str(), + // (int)vAddrNew.size()); + int64 now = time(NULL); + vector::iterator it = vAddrNew.begin(); + if (vAddrNew.size() > 1) { + if (doneAfter == 0 || doneAfter > now + 1) doneAfter = now + 1; + } + while (it != vAddrNew.end()) { + CAddress &addr = *it; + // printf("%s: got address %s\n", ToString(you).c_str(), + // addr.ToString().c_str(), (int)(vAddr->size())); + it++; + if (addr.nTime <= 100000000 || addr.nTime > now + 600) + addr.nTime = now - 5 * 86400; + if (addr.nTime > now - 604800) vAddr->push_back(addr); + // printf("%s: added address %s (#%i)\n", + // ToString(you).c_str(), addr.ToString().c_str(), + // (int)(vAddr->size())); + if (vAddr->size() > 1000) { + doneAfter = 1; + return true; + } + } + return false; + } + + return false; + } + + bool ProcessMessages() { + if (vRecv.empty()) return false; + do { + CDataStream::iterator pstart = + search(vRecv.begin(), vRecv.end(), BEGIN(pchMessageStart), + END(pchMessageStart)); + int nHeaderSize = vRecv.GetSerializeSize(CMessageHeader()); + if (vRecv.end() - pstart < nHeaderSize) { + if (vRecv.size() > nHeaderSize) { + vRecv.erase(vRecv.begin(), vRecv.end() - nHeaderSize); + } + break; + } + vRecv.erase(vRecv.begin(), pstart); + vector vHeaderSave(vRecv.begin(), + vRecv.begin() + nHeaderSize); + CMessageHeader hdr; + vRecv >> hdr; + if (!hdr.IsValid()) { + // printf("%s: BAD (invalid header)\n", ToString(you).c_str()); + ban = 100000; + return true; + } + string strCommand = hdr.GetCommand(); + unsigned int nMessageSize = hdr.nMessageSize; + if (nMessageSize > MAX_SIZE) { + // printf("%s: BAD (message too large)\n", + // ToString(you).c_str()); + ban = 100000; + return true; + } + if (nMessageSize > vRecv.size()) { + vRecv.insert(vRecv.begin(), vHeaderSave.begin(), + vHeaderSave.end()); + break; + } + if (vRecv.GetVersion() >= 209) { + uint256 hash = + Hash(vRecv.begin(), vRecv.begin() + nMessageSize); + unsigned int nChecksum = 0; + memcpy(&nChecksum, &hash, sizeof(nChecksum)); + if (nChecksum != hdr.nChecksum) continue; + } + CDataStream vMsg(vRecv.begin(), vRecv.begin() + nMessageSize, + vRecv.nType, vRecv.nVersion); + vRecv.ignore(nMessageSize); + if (ProcessMessage(strCommand, vMsg)) return true; + // printf("%s: done processing %s\n", ToString(you).c_str(), + // strCommand.c_str()); + } while (1); + return false; + } + +public: + CNode(const CService &ip, vector *vAddrIn) + : you(ip), nHeaderStart(-1), nMessageStart(-1), vAddr(vAddrIn), ban(0), + doneAfter(0), nVersion(0) { + vSend.SetType(SER_NETWORK); + vSend.SetVersion(0); + vRecv.SetType(SER_NETWORK); + vRecv.SetVersion(0); + if (time(NULL) > 1329696000) { + vSend.SetVersion(209); + vRecv.SetVersion(209); + } + } + bool Run() { + bool res = true; + if (!ConnectSocket(you, sock)) return false; + PushVersion(); + Send(); + int64 now; + while (now = time(NULL), ban == 0 && + (doneAfter == 0 || doneAfter > now) && + sock != INVALID_SOCKET) { + char pchBuf[0x10000]; + fd_set set; + FD_ZERO(&set); + FD_SET(sock, &set); + struct timeval wa; + if (doneAfter) { + wa.tv_sec = doneAfter - now; + wa.tv_usec = 0; + } else { + wa.tv_sec = GetTimeout(); + wa.tv_usec = 0; + } + int ret = select(sock + 1, &set, NULL, &set, &wa); + if (ret != 1) { + if (!doneAfter) res = false; + break; + } + int nBytes = recv(sock, pchBuf, sizeof(pchBuf), 0); + int nPos = vRecv.size(); + if (nBytes > 0) { + vRecv.resize(nPos + nBytes); + memcpy(&vRecv[nPos], pchBuf, nBytes); + } else if (nBytes == 0) { + // printf("%s: BAD (connection closed prematurely)\n", + // ToString(you).c_str()); + res = false; + break; + } else { + // printf("%s: BAD (connection error)\n", + // ToString(you).c_str()); + res = false; + break; + } + ProcessMessages(); + Send(); + } + if (sock == INVALID_SOCKET) res = false; + close(sock); + sock = INVALID_SOCKET; + return (ban == 0) && res; + } + + int GetBan() { return ban; } + + int GetClientVersion() { return nVersion; } + + std::string GetClientSubVersion() { return strSubVer; } + + int GetStartingHeight() { return nStartingHeight; } +}; + +bool TestNode(const CService &cip, int &ban, int &clientV, + std::string &clientSV, int &blocks, vector *vAddr) { + try { + CNode node(cip, vAddr); + bool ret = node.Run(); + if (!ret) { + ban = node.GetBan(); + } else { + ban = 0; + } + clientV = node.GetClientVersion(); + clientSV = node.GetClientSubVersion(); + blocks = node.GetStartingHeight(); + // printf("%s: %s!!!\n", cip.ToString().c_str(), ret ? "GOOD" : "BAD"); + return ret; + } catch (std::ios_base::failure &e) { + ban = 0; + return false; + } +} + +/* +int main(void) { + CService ip("bitcoin.sipa.be", 8333, true); + vector vAddr; + vAddr.clear(); + int ban = 0; + bool ret = TestNode(ip, ban, vAddr); + printf("ret=%s ban=%i vAddr.size()=%i\n", ret ? "good" : "bad", ban, +(int)vAddr.size()); +} +*/ diff --git a/src/seeder/bitcoin.h b/src/seeder/bitcoin.h new file mode 100644 index 000000000..2c47a6757 --- /dev/null +++ b/src/seeder/bitcoin.h @@ -0,0 +1,9 @@ +#ifndef _BITCOIN_H_ +#define _BITCOIN_H_ 1 + +#include "protocol.h" + +bool TestNode(const CService &cip, int &ban, int &client, std::string &clientSV, + int &blocks, std::vector *vAddr); + +#endif diff --git a/src/seeder/compat.h b/src/seeder/compat.h new file mode 100644 index 000000000..b773fe1a8 --- /dev/null +++ b/src/seeder/compat.h @@ -0,0 +1,64 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef _BITCOIN_COMPAT_H +#define _BITCOIN_COMPAT_H 1 + +#ifdef WIN32 +#define _WIN32_WINNT 0x0501 +#define WIN32_LEAN_AND_MEAN 1 +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include +#include +#include +#else +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +typedef u_int SOCKET; +#ifdef __APPLE__ +#define MSG_NOSIGNAL 0 +#endif +#ifdef WIN32 +#define MSG_NOSIGNAL 0 +#define MSG_DONTWAIT 0 +typedef int socklen_t; +#else +#include "errno.h" +#define WSAGetLastError() errno +#define WSAEINVAL EINVAL +#define WSAEALREADY EALREADY +#define WSAEWOULDBLOCK EWOULDBLOCK +#define WSAEMSGSIZE EMSGSIZE +#define WSAEINTR EINTR +#define WSAEINPROGRESS EINPROGRESS +#define WSAEADDRINUSE EADDRINUSE +#define WSAENOTSOCK EBADF +#define INVALID_SOCKET (SOCKET)(~0) +#define SOCKET_ERROR -1 +#endif + +inline int myclosesocket(SOCKET &hSocket) { + if (hSocket == INVALID_SOCKET) return WSAENOTSOCK; +#ifdef WIN32 + int ret = closesocket(hSocket); +#else + int ret = close(hSocket); +#endif + hSocket = INVALID_SOCKET; + return ret; +} +#define closesocket(s) myclosesocket(s) + +#endif diff --git a/src/seeder/db.cpp b/src/seeder/db.cpp new file mode 100644 index 000000000..eeaf6719f --- /dev/null +++ b/src/seeder/db.cpp @@ -0,0 +1,208 @@ +#include "db.h" +#include + +using namespace std; + +void CAddrInfo::Update(bool good) { + uint32_t now = time(NULL); + if (ourLastTry == 0) ourLastTry = now - MIN_RETRY; + int age = now - ourLastTry; + lastTry = now; + ourLastTry = now; + total++; + if (good) { + success++; + ourLastSuccess = now; + } + stat2H.Update(good, age, 3600 * 2); + stat8H.Update(good, age, 3600 * 8); + stat1D.Update(good, age, 3600 * 24); + stat1W.Update(good, age, 3600 * 24 * 7); + stat1M.Update(good, age, 3600 * 24 * 30); + int ign = GetIgnoreTime(); + if (ign && (ignoreTill == 0 || ignoreTill < ign + now)) + ignoreTill = ign + now; + // printf("%s: got %s result: success=%i/%i; 2H:%.2f%%-%.2f%%(%.2f) + // 8H:%.2f%%-%.2f%%(%.2f) 1D:%.2f%%-%.2f%%(%.2f) 1W:%.2f%%-%.2f%%(%.2f) + // \n", ToString(ip).c_str(), good ? "good" : "bad", success, total, + // 100.0 * stat2H.reliability, 100.0 * (stat2H.reliability + 1.0 - + // stat2H.weight), stat2H.count, + // 100.0 * stat8H.reliability, 100.0 * (stat8H.reliability + 1.0 - + // stat8H.weight), stat8H.count, + // 100.0 * stat1D.reliability, 100.0 * (stat1D.reliability + 1.0 - + // stat1D.weight), stat1D.count, + // 100.0 * stat1W.reliability, 100.0 * (stat1W.reliability + 1.0 - + // stat1W.weight), stat1W.count); +} + +bool CAddrDb::Get_(CServiceResult &ip, int &wait) { + int64 now = time(NULL); + int cont = 0; + int tot = unkId.size() + ourId.size(); + if (tot == 0) { + wait = 5; + return false; + } + do { + int rnd = rand() % tot; + int ret; + if (rnd < unkId.size()) { + set::iterator it = unkId.end(); + it--; + ret = *it; + unkId.erase(it); + } else { + ret = ourId.front(); + if (time(NULL) - idToInfo[ret].ourLastTry < MIN_RETRY) return false; + ourId.pop_front(); + } + if (idToInfo[ret].ignoreTill && idToInfo[ret].ignoreTill < now) { + ourId.push_back(ret); + idToInfo[ret].ourLastTry = now; + } else { + ip.service = idToInfo[ret].ip; + ip.ourLastSuccess = idToInfo[ret].ourLastSuccess; + break; + } + } while (1); + nDirty++; + return true; +} + +int CAddrDb::Lookup_(const CService &ip) { + if (ipToId.count(ip)) return ipToId[ip]; + return -1; +} + +void CAddrDb::Good_(const CService &addr, int clientV, std::string clientSV, + int blocks) { + int id = Lookup_(addr); + if (id == -1) return; + unkId.erase(id); + banned.erase(addr); + CAddrInfo &info = idToInfo[id]; + info.clientVersion = clientV; + info.clientSubVersion = clientSV; + info.blocks = blocks; + info.Update(true); + if (info.IsGood() && goodId.count(id) == 0) { + goodId.insert(id); + // printf("%s: good; %i good nodes now\n", ToString(addr).c_str(), + // (int)goodId.size()); + } + nDirty++; + ourId.push_back(id); +} + +void CAddrDb::Bad_(const CService &addr, int ban) { + int id = Lookup_(addr); + if (id == -1) return; + unkId.erase(id); + CAddrInfo &info = idToInfo[id]; + info.Update(false); + uint32_t now = time(NULL); + int ter = info.GetBanTime(); + if (ter) { + // printf("%s: terrible\n", ToString(addr).c_str()); + if (ban < ter) ban = ter; + } + if (ban > 0) { + // printf("%s: ban for %i seconds\n", ToString(addr).c_str(), ban); + banned[info.ip] = ban + now; + ipToId.erase(info.ip); + goodId.erase(id); + idToInfo.erase(id); + } else { + if (/*!info.IsGood() && */ goodId.count(id) == 1) { + goodId.erase(id); + // printf("%s: not good; %i good nodes left\n", + // ToString(addr).c_str(), (int)goodId.size()); + } + ourId.push_back(id); + } + nDirty++; +} + +void CAddrDb::Skipped_(const CService &addr) { + int id = Lookup_(addr); + if (id == -1) return; + unkId.erase(id); + ourId.push_back(id); + // printf("%s: skipped\n", ToString(addr).c_str()); + nDirty++; +} + +void CAddrDb::Add_(const CAddress &addr, bool force) { + if (!force && !addr.IsRoutable()) return; + CService ipp(addr); + if (banned.count(ipp)) { + time_t bantime = banned[ipp]; + if (force || (bantime < time(NULL) && addr.nTime > bantime)) + banned.erase(ipp); + else + return; + } + if (ipToId.count(ipp)) { + CAddrInfo &ai = idToInfo[ipToId[ipp]]; + if (addr.nTime > ai.lastTry || ai.services != addr.nServices) { + ai.lastTry = addr.nTime; + ai.services |= addr.nServices; + // printf("%s: updated\n", ToString(addr).c_str()); + } + if (force) { + ai.ignoreTill = 0; + } + return; + } + CAddrInfo ai; + ai.ip = ipp; + ai.services = addr.nServices; + ai.lastTry = addr.nTime; + ai.ourLastTry = 0; + ai.total = 0; + ai.success = 0; + int id = nId++; + idToInfo[id] = ai; + ipToId[ipp] = id; + // printf("%s: added\n", ToString(ipp).c_str(), ipToId[ipp]); + unkId.insert(id); + nDirty++; +} + +void CAddrDb::GetIPs_(set &ips, uint64_t requestedFlags, int max, + const bool *nets) { + if (goodId.size() == 0) { + int id = -1; + if (ourId.size() == 0) { + if (unkId.size() == 0) return; + id = *unkId.begin(); + } else { + id = *ourId.begin(); + } + if (id >= 0 && + (idToInfo[id].services & requestedFlags) == requestedFlags) { + ips.insert(idToInfo[id].ip); + } + return; + } + std::vector goodIdFiltered; + for (std::set::const_iterator it = goodId.begin(); it != goodId.end(); + it++) { + if ((idToInfo[*it].services & requestedFlags) == requestedFlags) + goodIdFiltered.push_back(*it); + } + + if (!goodIdFiltered.size()) return; + + if (max > goodIdFiltered.size() / 2) max = goodIdFiltered.size() / 2; + if (max < 1) max = 1; + + set ids; + while (ids.size() < max) { + ids.insert(goodIdFiltered[rand() % goodIdFiltered.size()]); + } + for (set::const_iterator it = ids.begin(); it != ids.end(); it++) { + CService &ip = idToInfo[*it].ip; + if (nets[ip.GetNetwork()]) ips.insert(ip); + } +} diff --git a/src/seeder/db.h b/src/seeder/db.h new file mode 100644 index 000000000..a8900bb2a --- /dev/null +++ b/src/seeder/db.h @@ -0,0 +1,397 @@ +#include +#include + +#include +#include +#include +#include + +#include "netbase.h" +#include "protocol.h" +#include "util.h" + +#define MIN_RETRY 1000 + +#define REQUIRE_VERSION 70001 + +static inline int GetRequireHeight(const bool testnet = fTestNet) { + return testnet ? 500000 : 350000; +} + +std::string static inline ToString(const CService &ip) { + std::string str = ip.ToString(); + while (str.size() < 22) + str += ' '; + return str; +} + +class CAddrStat { +private: + float weight; + float count; + float reliability; + +public: + CAddrStat() : weight(0), count(0), reliability(0) {} + + void Update(bool good, int64 age, double tau) { + double f = exp(-age / tau); + reliability = reliability * f + (good ? (1.0 - f) : 0); + count = count * f + 1; + weight = weight * f + (1.0 - f); + } + + IMPLEMENT_SERIALIZE(READWRITE(weight); READWRITE(count); + READWRITE(reliability);) + + friend class CAddrInfo; +}; + +class CAddrReport { +public: + CService ip; + int clientVersion; + int blocks; + double uptime[5]; + std::string clientSubVersion; + int64_t lastSuccess; + bool fGood; + uint64_t services; +}; + +class CAddrInfo { +private: + CService ip; + uint64_t services; + int64 lastTry; + int64 ourLastTry; + int64 ourLastSuccess; + int64 ignoreTill; + CAddrStat stat2H; + CAddrStat stat8H; + CAddrStat stat1D; + CAddrStat stat1W; + CAddrStat stat1M; + int clientVersion; + int blocks; + int total; + int success; + std::string clientSubVersion; + +public: + CAddrInfo() + : services(0), lastTry(0), ourLastTry(0), ourLastSuccess(0), + ignoreTill(0), clientVersion(0), blocks(0), total(0), success(0) {} + + CAddrReport GetReport() const { + CAddrReport ret; + ret.ip = ip; + ret.clientVersion = clientVersion; + ret.clientSubVersion = clientSubVersion; + ret.blocks = blocks; + ret.uptime[0] = stat2H.reliability; + ret.uptime[1] = stat8H.reliability; + ret.uptime[2] = stat1D.reliability; + ret.uptime[3] = stat1W.reliability; + ret.uptime[4] = stat1M.reliability; + ret.lastSuccess = ourLastSuccess; + ret.fGood = IsGood(); + ret.services = services; + return ret; + } + + bool IsGood() const { + if (ip.GetPort() != GetDefaultPort()) return false; + if (!(services & NODE_NETWORK)) return false; + if (!(services & NODE_BITCOIN_CASH)) return false; + if (!ip.IsRoutable()) return false; + if (clientVersion && clientVersion < REQUIRE_VERSION) return false; + if (blocks && blocks < GetRequireHeight()) return false; + + if (total <= 3 && success * 2 >= total) return true; + + if (stat2H.reliability > 0.85 && stat2H.count > 2) return true; + if (stat8H.reliability > 0.70 && stat8H.count > 4) return true; + if (stat1D.reliability > 0.55 && stat1D.count > 8) return true; + if (stat1W.reliability > 0.45 && stat1W.count > 16) return true; + if (stat1M.reliability > 0.35 && stat1M.count > 32) return true; + + return false; + } + int GetBanTime() const { + if (IsGood()) return 0; + if (clientVersion && clientVersion < 31900) { + return 604800; + } + if (stat1M.reliability - stat1M.weight + 1.0 < 0.15 && + stat1M.count > 32) { + return 30 * 86400; + } + if (stat1W.reliability - stat1W.weight + 1.0 < 0.10 && + stat1W.count > 16) { + return 7 * 86400; + } + if (stat1D.reliability - stat1D.weight + 1.0 < 0.05 && + stat1D.count > 8) { + return 1 * 86400; + } + return 0; + } + int GetIgnoreTime() const { + if (IsGood()) return 0; + if (stat1M.reliability - stat1M.weight + 1.0 < 0.20 && + stat1M.count > 2) { + return 10 * 86400; + } + if (stat1W.reliability - stat1W.weight + 1.0 < 0.16 && + stat1W.count > 2) { + return 3 * 86400; + } + if (stat1D.reliability - stat1D.weight + 1.0 < 0.12 && + stat1D.count > 2) { + return 8 * 3600; + } + if (stat8H.reliability - stat8H.weight + 1.0 < 0.08 && + stat8H.count > 2) { + return 2 * 3600; + } + return 0; + } + + void Update(bool good); + + friend class CAddrDb; + + IMPLEMENT_SERIALIZE(unsigned char version = 4; READWRITE(version); + READWRITE(ip); READWRITE(services); READWRITE(lastTry); + unsigned char tried = ourLastTry != 0; READWRITE(tried); + if (tried) { + READWRITE(ourLastTry); + READWRITE(ignoreTill); + READWRITE(stat2H); + READWRITE(stat8H); + READWRITE(stat1D); + READWRITE(stat1W); + if (version >= 1) + READWRITE(stat1M); + else if (!fWrite) + *((CAddrStat *)(&stat1M)) = stat1W; + READWRITE(total); + READWRITE(success); + READWRITE(clientVersion); + if (version >= 2) READWRITE(clientSubVersion); + if (version >= 3) READWRITE(blocks); + if (version >= 4) READWRITE(ourLastSuccess); + }) +}; + +class CAddrDbStats { +public: + int nBanned; + int nAvail; + int nTracked; + int nNew; + int nGood; + int nAge; +}; + +struct CServiceResult { + CService service; + bool fGood; + int nBanTime; + int nHeight; + int nClientV; + std::string strClientV; + int64 ourLastSuccess; +}; + +// seen nodes +// / \ +// (a) banned nodes available nodes-------------- +// / | \ +// tracked nodes (b) unknown nodes (e) active nodes +// / \ +// (d) good nodes (c) non-good nodes + +class CAddrDb { +private: + mutable CCriticalSection cs; + int nId; // number of address id's + std::map + idToInfo; // map address id to address info (b,c,d,e) + std::map ipToId; // map ip to id (b,c,d,e) + std::deque ourId; // sequence of tried nodes, in order we have tried + // connecting to them (c,d) + std::set unkId; // set of nodes not yet tried (b) + std::set goodId; // set of good nodes (d, good e) + int nDirty; + +protected: + // internal routines that assume proper locks are acquired + void Add_(const CAddress &addr, bool force); // add an address + bool Get_(CServiceResult &ip, int &wait); // get an IP to test (must call + // Good_, Bad_, or Skipped_ on + // result afterwards) + bool GetMany_(std::vector &ips, int max, int &wait); + void + Good_(const CService &ip, int clientV, std::string clientSV, + int blocks); // mark an IP as good (must have been returned by Get_) + void Bad_(const CService &ip, int ban); // mark an IP as bad (and optionally + // ban it) (must have been returned + // by Get_) + void Skipped_(const CService &ip); // mark an IP as skipped (must have been + // returned by Get_) + int Lookup_(const CService &ip); // look up id of an IP + void + GetIPs_(std::set &ips, uint64_t requestedFlags, int max, + const bool *nets); // get a random set of IPs (shared lock only) + +public: + std::map + banned; // nodes that are banned, with their unban time (a) + + void GetStats(CAddrDbStats &stats) { + SHARED_CRITICAL_BLOCK(cs) { + stats.nBanned = banned.size(); + stats.nAvail = idToInfo.size(); + stats.nTracked = ourId.size(); + stats.nGood = goodId.size(); + stats.nNew = unkId.size(); + stats.nAge = time(NULL) - idToInfo[ourId[0]].ourLastTry; + } + } + + void ResetIgnores() { + for (std::map::iterator it = idToInfo.begin(); + it != idToInfo.end(); it++) { + (*it).second.ignoreTill = 0; + } + } + + std::vector GetAll() { + std::vector ret; + SHARED_CRITICAL_BLOCK(cs) { + for (std::deque::const_iterator it = ourId.begin(); + it != ourId.end(); it++) { + const CAddrInfo &info = idToInfo[*it]; + if (info.success > 0) { + ret.push_back(info.GetReport()); + } + } + } + return ret; + } + + // serialization code + // format: + // nVersion (0 for now) + // n (number of ips in (b,c,d)) + // CAddrInfo[n] + // banned + // acquires a shared lock (this does not suffice for read mode, but we + // assume that only happens at startup, single-threaded) + // this way, dumping does not interfere with GetIPs_, which is called from + // the DNS thread + IMPLEMENT_SERIALIZE(({ + int nVersion = 0; + READWRITE(nVersion); + SHARED_CRITICAL_BLOCK(cs) { + if (fWrite) { + CAddrDb *db = const_cast(this); + int n = ourId.size() + unkId.size(); + READWRITE(n); + for (std::deque::const_iterator it = + ourId.begin(); + it != ourId.end(); it++) { + std::map::iterator ci = + db->idToInfo.find(*it); + READWRITE((*ci).second); + } + for (std::set::const_iterator it = + unkId.begin(); + it != unkId.end(); it++) { + std::map::iterator ci = + db->idToInfo.find(*it); + READWRITE((*ci).second); + } + } else { + CAddrDb *db = const_cast(this); + db->nId = 0; + int n; + READWRITE(n); + for (int i = 0; i < n; i++) { + CAddrInfo info; + READWRITE(info); + if (!info.GetBanTime()) { + int id = db->nId++; + db->idToInfo[id] = info; + db->ipToId[info.ip] = id; + if (info.ourLastTry) { + db->ourId.push_back(id); + if (info.IsGood()) + db->goodId.insert(id); + } else { + db->unkId.insert(id); + } + } + } + db->nDirty++; + } + READWRITE(banned); + } + });) + + void Add(const CAddress &addr, bool fForce = false) { + CRITICAL_BLOCK(cs) + Add_(addr, fForce); + } + void Add(const std::vector &vAddr, bool fForce = false) { + CRITICAL_BLOCK(cs) + for (int i = 0; i < vAddr.size(); i++) + Add_(vAddr[i], fForce); + } + void Good(const CService &addr, int clientVersion, + std::string clientSubVersion, int blocks) { + CRITICAL_BLOCK(cs) + Good_(addr, clientVersion, clientSubVersion, blocks); + } + void Skipped(const CService &addr) { + CRITICAL_BLOCK(cs) + Skipped_(addr); + } + void Bad(const CService &addr, int ban = 0) { + CRITICAL_BLOCK(cs) + Bad_(addr, ban); + } + bool Get(CServiceResult &ip, int &wait) { + CRITICAL_BLOCK(cs) + return Get_(ip, wait); + } + void GetMany(std::vector &ips, int max, int &wait) { + CRITICAL_BLOCK(cs) { + while (max > 0) { + CServiceResult ip = {}; + if (!Get_(ip, wait)) return; + ips.push_back(ip); + max--; + } + } + } + void ResultMany(const std::vector &ips) { + CRITICAL_BLOCK(cs) { + for (int i = 0; i < ips.size(); i++) { + if (ips[i].fGood) { + Good_(ips[i].service, ips[i].nClientV, ips[i].strClientV, + ips[i].nHeight); + } else { + Bad_(ips[i].service, ips[i].nBanTime); + } + } + } + } + void GetIPs(std::set &ips, uint64_t requestedFlags, int max, + const bool *nets) { + SHARED_CRITICAL_BLOCK(cs) + GetIPs_(ips, requestedFlags, max, nets); + } +}; diff --git a/src/seeder/dns.c b/src/seeder/dns.c new file mode 100644 index 000000000..7aa006223 --- /dev/null +++ b/src/seeder/dns.c @@ -0,0 +1,567 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dns.h" + +#define BUFLEN 512 + +#if defined IP_RECVDSTADDR +#define DSTADDR_SOCKOPT IP_RECVDSTADDR +#define DSTADDR_DATASIZE (CMSG_SPACE(sizeof(struct in6_addr))) +#define dstaddr(x) (CMSG_DATA(x)) +#elif defined IPV6_PKTINFO +struct in6_pktinfo { + struct in6_addr ipi6_addr; /* src/dst IPv6 address */ + unsigned int ipi6_ifindex; /* send/recv interface index */ +}; + +#define DSTADDR_SOCKOPT IPV6_PKTINFO +#define DSTADDR_DATASIZE (CMSG_SPACE(sizeof(struct in6_pktinfo))) +#define dstaddr(x) (&(((struct in6_pktinfo *)(CMSG_DATA(x)))->ipi6_addr)) +#else +#error "can't determine socket option" +#endif + +union control_data { + struct cmsghdr cmsg; + unsigned char data[DSTADDR_DATASIZE]; +}; + +typedef enum { CLASS_IN = 1, QCLASS_ANY = 255 } dns_class; + +typedef enum { + TYPE_A = 1, + TYPE_NS = 2, + TYPE_CNAME = 5, + TYPE_SOA = 6, + TYPE_MX = 15, + TYPE_AAAA = 28, + TYPE_SRV = 33, + QTYPE_ANY = 255 +} dns_type; + +// 0: ok +// -1: premature end of input, forward reference, component > 63 char, invalid +// character +// -2: insufficient space in output +int static parse_name(const unsigned char **inpos, const unsigned char *inend, + const unsigned char *inbuf, char *buf, size_t bufsize) { + size_t bufused = 0; + int init = 1; + do { + if (*inpos == inend) return -1; + // read length of next component + int octet = *((*inpos)++); + if (octet == 0) { + buf[bufused] = 0; + return 0; + } + // add dot in output + if (!init) { + if (bufused == bufsize - 1) return -2; + buf[bufused++] = '.'; + } else + init = 0; + // handle references + if ((octet & 0xC0) == 0xC0) { + if (*inpos == inend) return -1; + int ref = ((octet - 0xC0) << 8) + *((*inpos)++); + if (ref < 0 || ref >= (*inpos) - inbuf - 2) return -1; + const unsigned char *newbuf = inbuf + ref; + return parse_name(&newbuf, (*inpos) - 2, inbuf, buf + bufused, + bufsize - bufused); + } + if (octet > 63) return -1; + // copy label + while (octet) { + if (*inpos == inend) return -1; + if (bufused == bufsize - 1) return -2; + int c = *((*inpos)++); + if (c == '.') return -1; + octet--; + buf[bufused++] = c; + } + } while (1); +} + +// 0: k +// -1: component > 63 characters +// -2: insufficent space in output +// -3: two subsequent dots +int static write_name(unsigned char **outpos, const unsigned char *outend, + const char *name, int offset) { + while (*name != 0) { + char *dot = strchr(name, '.'); + const char *fin = dot; + if (!dot) fin = name + strlen(name); + if (fin - name > 63) return -1; + if (fin == name) return -3; + if (outend - *outpos < fin - name + 2) return -2; + *((*outpos)++) = fin - name; + memcpy(*outpos, name, fin - name); + *outpos += fin - name; + if (!dot) break; + name = dot + 1; + } + if (offset < 0) { + // no reference + if (outend == *outpos) return -2; + *((*outpos)++) = 0; + } else { + if (outend - *outpos < 2) return -2; + *((*outpos)++) = (offset >> 8) | 0xC0; + *((*outpos)++) = offset & 0xFF; + } + return 0; +} + +int static write_record(unsigned char **outpos, const unsigned char *outend, + const char *name, int offset, dns_type typ, + dns_class cls, int ttl) { + unsigned char *oldpos = *outpos; + int error = 0; + // name + int ret = write_name(outpos, outend, name, offset); + if (ret) { + error = ret; + goto error; + } + if (outend - *outpos < 8) { + error = -4; + goto error; + } + // type + *((*outpos)++) = typ >> 8; + *((*outpos)++) = typ & 0xFF; + // class + *((*outpos)++) = cls >> 8; + *((*outpos)++) = cls & 0xFF; + // ttl + *((*outpos)++) = (ttl >> 24) & 0xFF; + *((*outpos)++) = (ttl >> 16) & 0xFF; + *((*outpos)++) = (ttl >> 8) & 0xFF; + *((*outpos)++) = ttl & 0xFF; + return 0; +error: + *outpos = oldpos; + return error; +} + +int static write_record_a(unsigned char **outpos, const unsigned char *outend, + const char *name, int offset, dns_class cls, int ttl, + const addr_t *ip) { + if (ip->v != 4) return -6; + unsigned char *oldpos = *outpos; + int error = 0; + int ret = write_record(outpos, outend, name, offset, TYPE_A, cls, ttl); + if (ret) return ret; + if (outend - *outpos < 6) { + error = -5; + goto error; + } + // rdlength + *((*outpos)++) = 0; + *((*outpos)++) = 4; + // rdata + for (int i = 0; i < 4; i++) + *((*outpos)++) = ip->data.v4[i]; + return 0; +error: + *outpos = oldpos; + return error; +} + +int static write_record_aaaa(unsigned char **outpos, + const unsigned char *outend, const char *name, + int offset, dns_class cls, int ttl, + const addr_t *ip) { + if (ip->v != 6) return -6; + unsigned char *oldpos = *outpos; + int error = 0; + int ret = write_record(outpos, outend, name, offset, TYPE_AAAA, cls, ttl); + if (ret) return ret; + if (outend - *outpos < 6) { + error = -5; + goto error; + } + // rdlength + *((*outpos)++) = 0; + *((*outpos)++) = 16; + // rdata + for (int i = 0; i < 16; i++) + *((*outpos)++) = ip->data.v6[i]; + return 0; +error: + *outpos = oldpos; + return error; +} + +int static write_record_ns(unsigned char **outpos, const unsigned char *outend, + char *name, int offset, dns_class cls, int ttl, + const char *ns) { + unsigned char *oldpos = *outpos; + int ret = write_record(outpos, outend, name, offset, TYPE_NS, cls, ttl); + if (ret) return ret; + int error = 0; + if (outend - *outpos < 2) { + error = -5; + goto error; + } + (*outpos) += 2; + unsigned char *curpos = *outpos; + ret = write_name(outpos, outend, ns, -1); + if (ret) { + error = ret; + goto error; + } + curpos[-2] = (*outpos - curpos) >> 8; + curpos[-1] = (*outpos - curpos) & 0xFF; + return 0; +error: + *outpos = oldpos; + return error; +} + +int static write_record_soa(unsigned char **outpos, const unsigned char *outend, + char *name, int offset, dns_class cls, int ttl, + const char *mname, const char *rname, + uint32_t serial, uint32_t refresh, uint32_t retry, + uint32_t expire, uint32_t minimum) { + unsigned char *oldpos = *outpos; + int ret = write_record(outpos, outend, name, offset, TYPE_SOA, cls, ttl); + if (ret) return ret; + int error = 0; + if (outend - *outpos < 2) { + error = -5; + goto error; + } + (*outpos) += 2; + unsigned char *curpos = *outpos; + ret = write_name(outpos, outend, mname, -1); + if (ret) { + error = ret; + goto error; + } + ret = write_name(outpos, outend, rname, -1); + if (ret) { + error = ret; + goto error; + } + if (outend - *outpos < 20) { + error = -5; + goto error; + } + *((*outpos)++) = (serial >> 24) & 0xFF; + *((*outpos)++) = (serial >> 16) & 0xFF; + *((*outpos)++) = (serial >> 8) & 0xFF; + *((*outpos)++) = serial & 0xFF; + *((*outpos)++) = (refresh >> 24) & 0xFF; + *((*outpos)++) = (refresh >> 16) & 0xFF; + *((*outpos)++) = (refresh >> 8) & 0xFF; + *((*outpos)++) = refresh & 0xFF; + *((*outpos)++) = (retry >> 24) & 0xFF; + *((*outpos)++) = (retry >> 16) & 0xFF; + *((*outpos)++) = (retry >> 8) & 0xFF; + *((*outpos)++) = retry & 0xFF; + *((*outpos)++) = (expire >> 24) & 0xFF; + *((*outpos)++) = (expire >> 16) & 0xFF; + *((*outpos)++) = (expire >> 8) & 0xFF; + *((*outpos)++) = expire & 0xFF; + *((*outpos)++) = (minimum >> 24) & 0xFF; + *((*outpos)++) = (minimum >> 16) & 0xFF; + *((*outpos)++) = (minimum >> 8) & 0xFF; + *((*outpos)++) = minimum & 0xFF; + curpos[-2] = (*outpos - curpos) >> 8; + curpos[-1] = (*outpos - curpos) & 0xFF; + return 0; +error: + *outpos = oldpos; + return error; +} + +ssize_t static dnshandle(dns_opt_t *opt, const unsigned char *inbuf, + size_t insize, unsigned char *outbuf) { + int error = 0; + if (insize < 12) // DNS header + return -1; + // copy id + outbuf[0] = inbuf[0]; + outbuf[1] = inbuf[1]; + // copy flags; + outbuf[2] = inbuf[2]; + outbuf[3] = inbuf[3]; + // clear error + outbuf[3] &= ~15; + // check qr + if (inbuf[2] & 128) { /* printf("Got response?\n"); */ + error = 1; + goto error; + } + // check opcode + if (((inbuf[2] & 120) >> 3) != 0) { /* printf("Opcode nonzero?\n"); */ + error = 4; + goto error; + } + // unset TC + outbuf[2] &= ~2; + // unset RA + outbuf[3] &= ~128; + // check questions + int nquestion = (inbuf[4] << 8) + inbuf[5]; + if (nquestion == 0) { /* printf("No questions?\n"); */ + error = 0; + goto error; + } + if (nquestion > 1) { /* printf("Multiple questions %i?\n", nquestion); */ + error = 4; + goto error; + } + const unsigned char *inpos = inbuf + 12; + const unsigned char *inend = inbuf + insize; + char name[256]; + int offset = inpos - inbuf; + int ret = parse_name(&inpos, inend, inbuf, name, 256); + if (ret == -1) { + error = 1; + goto error; + } + if (ret == -2) { + error = 5; + goto error; + } + int namel = strlen(name), hostl = strlen(opt->host); + if (strcasecmp(name, opt->host) && + (namel < hostl + 2 || name[namel - hostl - 1] != '.' || + strcasecmp(name + namel - hostl, opt->host))) { + error = 5; + goto error; + } + if (inend - inpos < 4) { + error = 1; + goto error; + } + // copy question to output + memcpy(outbuf + 12, inbuf + 12, inpos + 4 - (inbuf + 12)); + // set counts + outbuf[4] = 0; + outbuf[5] = 1; + outbuf[6] = 0; + outbuf[7] = 0; + outbuf[8] = 0; + outbuf[9] = 0; + outbuf[10] = 0; + outbuf[11] = 0; + // set qr + outbuf[2] |= 128; + + int typ = (inpos[0] << 8) + inpos[1]; + int cls = (inpos[2] << 8) + inpos[3]; + inpos += 4; + + unsigned char *outpos = outbuf + (inpos - inbuf); + unsigned char *outend = outbuf + BUFLEN; + + // printf("DNS: Request host='%s' type=%i class=%i\n", name, typ, cls); + + // calculate max size of authority section + + int max_auth_size = 0; + + if (!((typ == TYPE_NS || typ == QTYPE_ANY) && + (cls == CLASS_IN || cls == QCLASS_ANY))) { + // authority section will be necessary, either NS or SOA + unsigned char *newpos = outpos; + write_record_ns(&newpos, outend, "", offset, CLASS_IN, 0, opt->ns); + max_auth_size = newpos - outpos; + + newpos = outpos; + write_record_soa(&newpos, outend, "", offset, CLASS_IN, opt->nsttl, + opt->ns, opt->mbox, time(NULL), 604800, 86400, 2592000, + 604800); + if (max_auth_size < newpos - outpos) max_auth_size = newpos - outpos; + // printf("Authority section will claim %i bytes max\n", + // max_auth_size); + } + + // Answer section + + int have_ns = 0; + + // NS records + if ((typ == TYPE_NS || typ == QTYPE_ANY) && + (cls == CLASS_IN || cls == QCLASS_ANY)) { + int ret2 = write_record_ns(&outpos, outend - max_auth_size, "", offset, + CLASS_IN, opt->nsttl, opt->ns); + // printf("wrote NS record: %i\n", ret2); + if (!ret2) { + outbuf[7]++; + have_ns++; + } + } + + // SOA records + if ((typ == TYPE_SOA || typ == QTYPE_ANY) && + (cls == CLASS_IN || cls == QCLASS_ANY) && opt->mbox) { + int ret2 = write_record_soa(&outpos, outend - max_auth_size, "", offset, + CLASS_IN, opt->nsttl, opt->ns, opt->mbox, + time(NULL), 604800, 86400, 2592000, 604800); + // printf("wrote SOA record: %i\n", ret2); + if (!ret2) { + outbuf[7]++; + } + } + + // A/AAAA records + if ((typ == TYPE_A || typ == TYPE_AAAA || typ == QTYPE_ANY) && + (cls == CLASS_IN || cls == QCLASS_ANY)) { + addr_t addr[32]; + int naddr = opt->cb((void *)opt, name, addr, 32, + typ == TYPE_A || typ == QTYPE_ANY, + typ == TYPE_AAAA || typ == QTYPE_ANY); + int n = 0; + while (n < naddr) { + int ret = 1; + if (addr[n].v == 4) + ret = write_record_a(&outpos, outend - max_auth_size, "", + offset, CLASS_IN, opt->datattl, &addr[n]); + else if (addr[n].v == 6) + ret = + write_record_aaaa(&outpos, outend - max_auth_size, "", + offset, CLASS_IN, opt->datattl, &addr[n]); + // printf("wrote A record: %i\n", ret); + if (!ret) { + n++; + outbuf[7]++; + } else + break; + } + } + + // Authority section + if (!have_ns && outbuf[7]) { + int ret2 = write_record_ns(&outpos, outend, "", offset, CLASS_IN, + opt->nsttl, opt->ns); + // printf("wrote NS record: %i\n", ret2); + if (!ret2) { + outbuf[9]++; + } + } else if (!outbuf[7]) { + // Didn't include any answers, so reply with SOA as this is a negative + // response. If we replied with NS above we'd create a bad horizontal + // referral loop, as the NS response indicates where the resolver should + // try next. + int ret2 = write_record_soa(&outpos, outend, "", offset, CLASS_IN, + opt->nsttl, opt->ns, opt->mbox, time(NULL), + 604800, 86400, 2592000, 604800); + // printf("wrote SOA record: %i\n", ret2); + if (!ret2) { + outbuf[9]++; + } + } + + // set AA + outbuf[2] |= 4; + + return outpos - outbuf; +error: + // set error + outbuf[3] |= error & 0xF; + // set counts + outbuf[4] = 0; + outbuf[5] = 0; + outbuf[6] = 0; + outbuf[7] = 0; + outbuf[8] = 0; + outbuf[9] = 0; + outbuf[10] = 0; + outbuf[11] = 0; + return 12; +} + +static int listenSocket = -1; + +int dnsserver(dns_opt_t *opt) { + struct sockaddr_in6 si_other; + int senderSocket = -1; + senderSocket = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); + if (senderSocket == -1) return -3; + + int replySocket; + if (listenSocket == -1) { + struct sockaddr_in6 si_me; + if ((listenSocket = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)) == -1) { + listenSocket = -1; + return -1; + } + replySocket = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); + if (replySocket == -1) { + close(listenSocket); + return -1; + } + int sockopt = 1; + setsockopt(listenSocket, IPPROTO_IPV6, DSTADDR_SOCKOPT, &sockopt, + sizeof sockopt); + memset((char *)&si_me, 0, sizeof(si_me)); + si_me.sin6_family = AF_INET6; + si_me.sin6_port = htons(opt->port); + si_me.sin6_addr = in6addr_any; + if (bind(listenSocket, (struct sockaddr *)&si_me, sizeof(si_me)) == -1) + return -2; + } + + unsigned char inbuf[BUFLEN], outbuf[BUFLEN]; + struct iovec iov[1] = { + { + .iov_base = inbuf, .iov_len = sizeof(inbuf), + }, + }; + union control_data cmsg; + struct msghdr msg = { + .msg_name = &si_other, + .msg_namelen = sizeof(si_other), + .msg_iov = iov, + .msg_iovlen = 1, + .msg_control = &cmsg, + .msg_controllen = sizeof(cmsg), + }; + for (; 1; ++(opt->nRequests)) { + ssize_t insize = recvmsg(listenSocket, &msg, 0); + // unsigned char *addr = (unsigned char*)&si_other.sin_addr.s_addr; + // printf("DNS: Request %llu from %i.%i.%i.%i:%i of %i bytes\n", + // (unsigned long long)(opt->nRequests), addr[0], addr[1], addr[2], + // addr[3], ntohs(si_other.sin_port), (int)insize); + if (insize <= 0) continue; + + ssize_t ret = dnshandle(opt, inbuf, insize, outbuf); + if (ret <= 0) continue; + + bool handled = false; + for (struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); hdr; + hdr = CMSG_NXTHDR(&msg, hdr)) { + if (hdr->cmsg_level == IPPROTO_IP && + hdr->cmsg_type == DSTADDR_SOCKOPT) { + msg.msg_iov[0].iov_base = outbuf; + msg.msg_iov[0].iov_len = ret; + sendmsg(listenSocket, &msg, 0); + msg.msg_iov[0].iov_base = inbuf; + msg.msg_iov[0].iov_len = sizeof(inbuf); + handled = true; + } + } + if (!handled) + sendto(listenSocket, outbuf, ret, 0, (struct sockaddr *)&si_other, + sizeof(si_other)); + } + return 0; +} diff --git a/src/seeder/dns.h b/src/seeder/dns.h new file mode 100644 index 000000000..d7805bac8 --- /dev/null +++ b/src/seeder/dns.h @@ -0,0 +1,29 @@ +#ifndef _DNS_H_ +#define _DNS_H_ 1 + +#include + +typedef struct { + int v; + union { + unsigned char v4[4]; + unsigned char v6[16]; + } data; +} addr_t; + +typedef struct { + int port; + int datattl; + int nsttl; + const char *host; + const char *ns; + const char *mbox; + int (*cb)(void *opt, char *requested_hostname, addr_t *addr, int max, + int ipv4, int ipv6); + // stats + uint64_t nRequests; +} dns_opt_t; + +extern int dnsserver(dns_opt_t *opt); + +#endif diff --git a/src/seeder/main.cpp b/src/seeder/main.cpp new file mode 100644 index 000000000..3b05b45be --- /dev/null +++ b/src/seeder/main.cpp @@ -0,0 +1,564 @@ +#include + +#define __STDC_FORMAT_MACROS +#include +#include +#include +#include +#include +#include +#include + +#include "bitcoin.h" +#include "db.h" + +using namespace std; + +bool fTestNet = false; + +class CDnsSeedOpts { +public: + int nThreads; + int nPort; + int nDnsThreads; + int fUseTestNet; + int fWipeBan; + int fWipeIgnore; + const char *mbox; + const char *ns; + const char *host; + const char *tor; + const char *ipv4_proxy; + const char *ipv6_proxy; + std::set filter_whitelist; + + CDnsSeedOpts() + : nThreads(96), nDnsThreads(4), nPort(53), mbox(NULL), ns(NULL), + host(NULL), tor(NULL), fUseTestNet(false), fWipeBan(false), + fWipeIgnore(false), ipv4_proxy(NULL), ipv6_proxy(NULL) {} + + void ParseCommandLine(int argc, char **argv) { + static const char *help = + "Bitcoin-seeder\n" + "Usage: %s -h -n [-m ] [-t ] [-p " + "]\n" + "\n" + "Options:\n" + "-h Hostname of the DNS seed\n" + "-n Hostname of the nameserver\n" + "-m E-Mail address reported in SOA records\n" + "-t Number of crawlers to run in parallel (default " + "96)\n" + "-d Number of DNS server threads (default 4)\n" + "-p UDP port to listen on (default 53)\n" + "-o Tor proxy IP/Port\n" + "-i IPV4 SOCKS5 proxy IP/Port\n" + "-k IPV6 SOCKS5 proxy IP/Port\n" + "-w f1,f2,... Allow these flag combinations as filters\n" + "--testnet Use testnet\n" + "--wipeban Wipe list of banned nodes\n" + "--wipeignore Wipe list of ignored nodes\n" + "-?, --help Show this text\n" + "\n"; + bool showHelp = false; + + while (1) { + static struct option long_options[] = { + {"host", required_argument, 0, 'h'}, + {"ns", required_argument, 0, 'n'}, + {"mbox", required_argument, 0, 'm'}, + {"threads", required_argument, 0, 't'}, + {"dnsthreads", required_argument, 0, 'd'}, + {"port", required_argument, 0, 'p'}, + {"onion", required_argument, 0, 'o'}, + {"proxyipv4", required_argument, 0, 'i'}, + {"proxyipv6", required_argument, 0, 'k'}, + {"filter", required_argument, 0, 'w'}, + {"testnet", no_argument, &fUseTestNet, 1}, + {"wipeban", no_argument, &fWipeBan, 1}, + {"wipeignore", no_argument, &fWipeBan, 1}, + {"help", no_argument, 0, 'h'}, + {0, 0, 0, 0}}; + int option_index = 0; + int c = getopt_long(argc, argv, "h:n:m:t:p:d:o:i:k:w:", + long_options, &option_index); + if (c == -1) break; + switch (c) { + case 'h': { + host = optarg; + break; + } + + case 'm': { + mbox = optarg; + break; + } + + case 'n': { + ns = optarg; + break; + } + + case 't': { + int n = strtol(optarg, NULL, 10); + if (n > 0 && n < 1000) nThreads = n; + break; + } + + case 'd': { + int n = strtol(optarg, NULL, 10); + if (n > 0 && n < 1000) nDnsThreads = n; + break; + } + + case 'p': { + int p = strtol(optarg, NULL, 10); + if (p > 0 && p < 65536) nPort = p; + break; + } + + case 'o': { + tor = optarg; + break; + } + + case 'i': { + ipv4_proxy = optarg; + break; + } + + case 'k': { + ipv6_proxy = optarg; + break; + } + + case 'w': { + char *ptr = optarg; + while (*ptr != 0) { + unsigned long l = strtoul(ptr, &ptr, 0); + if (*ptr == ',') { + ptr++; + } else if (*ptr != 0) { + break; + } + filter_whitelist.insert(l); + } + break; + } + + case '?': { + showHelp = true; + break; + } + } + } + if (filter_whitelist.empty()) { + filter_whitelist.insert(NODE_NETWORK | NODE_BITCOIN_CASH); + filter_whitelist.insert(NODE_NETWORK | NODE_BITCOIN_CASH | + NODE_BLOOM); + filter_whitelist.insert(NODE_NETWORK | NODE_BITCOIN_CASH | + NODE_XTHIN); + filter_whitelist.insert(NODE_NETWORK | NODE_BITCOIN_CASH | + NODE_BLOOM | NODE_XTHIN); + } + if (host != NULL && ns == NULL) showHelp = true; + if (showHelp) fprintf(stderr, help, argv[0]); + } +}; + +extern "C" { +#include "dns.h" +} + +CAddrDb db; + +extern "C" void *ThreadCrawler(void *data) { + int *nThreads = (int *)data; + do { + std::vector ips; + int wait = 5; + db.GetMany(ips, 16, wait); + int64 now = time(NULL); + if (ips.empty()) { + wait *= 1000; + wait += rand() % (500 * *nThreads); + Sleep(wait); + continue; + } + vector addr; + for (int i = 0; i < ips.size(); i++) { + CServiceResult &res = ips[i]; + res.nBanTime = 0; + res.nClientV = 0; + res.nHeight = 0; + res.strClientV = ""; + bool getaddr = res.ourLastSuccess + 86400 < now; + res.fGood = + TestNode(res.service, res.nBanTime, res.nClientV, + res.strClientV, res.nHeight, getaddr ? &addr : NULL); + } + db.ResultMany(ips); + db.Add(addr); + } while (1); + return nullptr; +} + +extern "C" int GetIPList(void *thread, char *requestedHostname, addr_t *addr, + int max, int ipv4, int ipv6); + +class CDnsThread { +public: + struct FlagSpecificData { + int nIPv4, nIPv6; + std::vector cache; + time_t cacheTime; + unsigned int cacheHits; + FlagSpecificData() : nIPv4(0), nIPv6(0), cacheTime(0), cacheHits(0) {} + }; + + dns_opt_t dns_opt; // must be first + const int id; + std::map perflag; + std::atomic dbQueries; + std::set filterWhitelist; + + void cacheHit(uint64_t requestedFlags, bool force = false) { + static bool nets[NET_MAX] = {}; + if (!nets[NET_IPV4]) { + nets[NET_IPV4] = true; + nets[NET_IPV6] = true; + } + time_t now = time(NULL); + FlagSpecificData &thisflag = perflag[requestedFlags]; + thisflag.cacheHits++; + if (force || + thisflag.cacheHits * 400 > + (thisflag.cache.size() * thisflag.cache.size()) || + (thisflag.cacheHits * thisflag.cacheHits * 20 > + thisflag.cache.size() && + (now - thisflag.cacheTime > 5))) { + set ips; + db.GetIPs(ips, requestedFlags, 1000, nets); + dbQueries++; + thisflag.cache.clear(); + thisflag.nIPv4 = 0; + thisflag.nIPv6 = 0; + thisflag.cache.reserve(ips.size()); + for (set::iterator it = ips.begin(); it != ips.end(); + it++) { + struct in_addr addr; + struct in6_addr addr6; + if ((*it).GetInAddr(&addr)) { + addr_t a; + a.v = 4; + memcpy(&a.data.v4, &addr, 4); + thisflag.cache.push_back(a); + thisflag.nIPv4++; + } else if ((*it).GetIn6Addr(&addr6)) { + addr_t a; + a.v = 6; + memcpy(&a.data.v6, &addr6, 16); + thisflag.cache.push_back(a); + thisflag.nIPv6++; + } + } + thisflag.cacheHits = 0; + thisflag.cacheTime = now; + } + } + + CDnsThread(CDnsSeedOpts *opts, int idIn) : id(idIn) { + dns_opt.host = opts->host; + dns_opt.ns = opts->ns; + dns_opt.mbox = opts->mbox; + dns_opt.datattl = 3600; + dns_opt.nsttl = 40000; + dns_opt.cb = GetIPList; + dns_opt.port = opts->nPort; + dns_opt.nRequests = 0; + dbQueries = 0; + perflag.clear(); + filterWhitelist = opts->filter_whitelist; + } + + void run() { dnsserver(&dns_opt); } +}; + +extern "C" int GetIPList(void *data, char *requestedHostname, addr_t *addr, + int max, int ipv4, int ipv6) { + CDnsThread *thread = (CDnsThread *)data; + + uint64_t requestedFlags = 0; + int hostlen = strlen(requestedHostname); + if (hostlen > 1 && requestedHostname[0] == 'x' && + requestedHostname[1] != '0') { + char *pEnd; + uint64_t flags = (uint64_t)strtoull(requestedHostname + 1, &pEnd, 16); + if (*pEnd == '.' && pEnd <= requestedHostname + 17 && + std::find(thread->filterWhitelist.begin(), + thread->filterWhitelist.end(), + flags) != thread->filterWhitelist.end()) + requestedFlags = flags; + else + return 0; + } else if (strcasecmp(requestedHostname, thread->dns_opt.host)) + return 0; + thread->cacheHit(requestedFlags); + auto &thisflag = thread->perflag[requestedFlags]; + unsigned int size = thisflag.cache.size(); + unsigned int maxmax = + (ipv4 ? thisflag.nIPv4 : 0) + (ipv6 ? thisflag.nIPv6 : 0); + if (max > size) max = size; + if (max > maxmax) max = maxmax; + int i = 0; + while (i < max) { + int j = i + (rand() % (size - i)); + do { + bool ok = (ipv4 && thisflag.cache[j].v == 4) || + (ipv6 && thisflag.cache[j].v == 6); + if (ok) break; + j++; + if (j == size) j = i; + } while (1); + addr[i] = thisflag.cache[j]; + thisflag.cache[j] = thisflag.cache[i]; + thisflag.cache[i] = addr[i]; + i++; + } + return max; +} + +vector dnsThread; + +extern "C" void *ThreadDNS(void *arg) { + CDnsThread *thread = (CDnsThread *)arg; + thread->run(); + return nullptr; +} + +int StatCompare(const CAddrReport &a, const CAddrReport &b) { + if (a.uptime[4] == b.uptime[4]) { + if (a.uptime[3] == b.uptime[3]) { + return a.clientVersion > b.clientVersion; + } else { + return a.uptime[3] > b.uptime[3]; + } + } else { + return a.uptime[4] > b.uptime[4]; + } +} + +extern "C" void *ThreadDumper(void *) { + int count = 0; + do { + Sleep(100000 << count); // First 100s, than 200s, 400s, 800s, 1600s, and + // then 3200s forever + if (count < 5) count++; + { + vector v = db.GetAll(); + sort(v.begin(), v.end(), StatCompare); + FILE *f = fopen("dnsseed.dat.new", "w+"); + if (f) { + { + CAutoFile cf(f); + cf << db; + } + rename("dnsseed.dat.new", "dnsseed.dat"); + } + FILE *d = fopen("dnsseed.dump", "w"); + fprintf(d, "# address good " + "lastSuccess %%(2h) %%(8h) %%(1d) %%(7d) " + "%%(30d) blocks svcs version\n"); + double stat[5] = {0, 0, 0, 0, 0}; + for (vector::const_iterator it = v.begin(); + it < v.end(); it++) { + CAddrReport rep = *it; + fprintf( + d, + "%-47s %4d %11" PRId64 + " %6.2f%% %6.2f%% %6.2f%% %6.2f%% %6.2f%% %6i %08" PRIx64 + " %5i \"%s\"\n", + rep.ip.ToString().c_str(), (int)rep.fGood, rep.lastSuccess, + 100.0 * rep.uptime[0], 100.0 * rep.uptime[1], + 100.0 * rep.uptime[2], 100.0 * rep.uptime[3], + 100.0 * rep.uptime[4], rep.blocks, rep.services, + rep.clientVersion, rep.clientSubVersion.c_str()); + stat[0] += rep.uptime[0]; + stat[1] += rep.uptime[1]; + stat[2] += rep.uptime[2]; + stat[3] += rep.uptime[3]; + stat[4] += rep.uptime[4]; + } + fclose(d); + FILE *ff = fopen("dnsstats.log", "a"); + fprintf(ff, "%llu %g %g %g %g %g\n", + (unsigned long long)(time(NULL)), stat[0], stat[1], stat[2], + stat[3], stat[4]); + fclose(ff); + } + } while (1); + return nullptr; +} + +extern "C" void *ThreadStats(void *) { + bool first = true; + do { + char c[256]; + time_t tim = time(NULL); + struct tm *tmp = localtime(&tim); + strftime(c, 256, "[%y-%m-%d %H:%M:%S]", tmp); + CAddrDbStats stats; + db.GetStats(stats); + if (first) { + first = false; + printf("\n\n\n\x1b[3A"); + } else + printf("\x1b[2K\x1b[u"); + printf("\x1b[s"); + uint64_t requests = 0; + uint64_t queries = 0; + for (unsigned int i = 0; i < dnsThread.size(); i++) { + requests += dnsThread[i]->dns_opt.nRequests; + queries += dnsThread[i]->dbQueries; + } + printf("%s %i/%i available (%i tried in %is, %i new, %i active), %i " + "banned; %llu DNS requests, %llu db queries", + c, stats.nGood, stats.nAvail, stats.nTracked, stats.nAge, + stats.nNew, stats.nAvail - stats.nTracked - stats.nNew, + stats.nBanned, (unsigned long long)requests, + (unsigned long long)queries); + Sleep(1000); + } while (1); + return nullptr; +} + +static const string mainnet_seeds[] = { + "seed.bitcoinabc.org", "seed-abc.bitcoinforks.org", "seed.bitprim.org", + "seed.deadalnix.me", "seeder.criptolayer.net", ""}; +static const string testnet_seeds[] = {"testnet-seed.bitcoinabc.org", + "testnet-seed-abc.bitcoinforks.org", + "testnet-seed.bitcoinunlimited.info", + "testnet-seed.bitprim.org", + "testnet-seed.deadalnix.me", + "testnet-seeder.criptolayer.net", + ""}; +static const string *seeds = mainnet_seeds; + +extern "C" void *ThreadSeeder(void *) { + if (!fTestNet) { + db.Add(CService("kjy2eqzk4zwi5zd3.onion", 8333), true); + } + do { + for (int i = 0; seeds[i] != ""; i++) { + vector ips; + LookupHost(seeds[i].c_str(), ips); + for (vector::iterator it = ips.begin(); it != ips.end(); + it++) { + db.Add(CService(*it, GetDefaultPort()), true); + } + } + Sleep(1800000); + } while (1); + return nullptr; +} + +int main(int argc, char **argv) { + signal(SIGPIPE, SIG_IGN); + setbuf(stdout, NULL); + CDnsSeedOpts opts; + opts.ParseCommandLine(argc, argv); + printf("Supporting whitelisted filters: "); + for (std::set::const_iterator it = opts.filter_whitelist.begin(); + it != opts.filter_whitelist.end(); it++) { + if (it != opts.filter_whitelist.begin()) { + printf(","); + } + printf("0x%lx", (unsigned long)*it); + } + printf("\n"); + if (opts.tor) { + CService service(opts.tor, 9050); + if (service.IsValid()) { + printf("Using Tor proxy at %s\n", service.ToStringIPPort().c_str()); + SetProxy(NET_TOR, service); + } + } + if (opts.ipv4_proxy) { + CService service(opts.ipv4_proxy, 9050); + if (service.IsValid()) { + printf("Using IPv4 proxy at %s\n", + service.ToStringIPPort().c_str()); + SetProxy(NET_IPV4, service); + } + } + if (opts.ipv6_proxy) { + CService service(opts.ipv6_proxy, 9050); + if (service.IsValid()) { + printf("Using IPv6 proxy at %s\n", + service.ToStringIPPort().c_str()); + SetProxy(NET_IPV6, service); + } + } + bool fDNS = true; + if (opts.fUseTestNet) { + printf("Using testnet.\n"); + pchMessageStart[0] = 0xf4; + pchMessageStart[1] = 0xe5; + pchMessageStart[2] = 0xf3; + pchMessageStart[3] = 0xf4; + seeds = testnet_seeds; + fTestNet = true; + } + if (!opts.ns) { + printf("No nameserver set. Not starting DNS server.\n"); + fDNS = false; + } + if (fDNS && !opts.host) { + fprintf(stderr, "No hostname set. Please use -h.\n"); + exit(1); + } + if (fDNS && !opts.mbox) { + fprintf(stderr, "No e-mail address set. Please use -m.\n"); + exit(1); + } + FILE *f = fopen("dnsseed.dat", "r"); + if (f) { + printf("Loading dnsseed.dat..."); + CAutoFile cf(f); + cf >> db; + if (opts.fWipeBan) db.banned.clear(); + if (opts.fWipeIgnore) db.ResetIgnores(); + printf("done\n"); + } + pthread_t threadDns, threadSeed, threadDump, threadStats; + if (fDNS) { + printf("Starting %i DNS threads for %s on %s (port %i)...", + opts.nDnsThreads, opts.host, opts.ns, opts.nPort); + dnsThread.clear(); + for (int i = 0; i < opts.nDnsThreads; i++) { + dnsThread.push_back(new CDnsThread(&opts, i)); + pthread_create(&threadDns, NULL, ThreadDNS, dnsThread[i]); + printf("."); + Sleep(20); + } + printf("done\n"); + } + printf("Starting seeder..."); + pthread_create(&threadSeed, NULL, ThreadSeeder, NULL); + printf("done\n"); + printf("Starting %i crawler threads...", opts.nThreads); + pthread_attr_t attr_crawler; + pthread_attr_init(&attr_crawler); + pthread_attr_setstacksize(&attr_crawler, 0x20000); + for (int i = 0; i < opts.nThreads; i++) { + pthread_t thread; + pthread_create(&thread, &attr_crawler, ThreadCrawler, &opts.nThreads); + } + pthread_attr_destroy(&attr_crawler); + printf("done\n"); + pthread_create(&threadStats, NULL, ThreadStats, NULL); + pthread_create(&threadDump, NULL, ThreadDumper, NULL); + void *res; + pthread_join(threadDump, &res); + return 0; +} diff --git a/src/seeder/netbase.cpp b/src/seeder/netbase.cpp new file mode 100644 index 000000000..3105190c2 --- /dev/null +++ b/src/seeder/netbase.cpp @@ -0,0 +1,1070 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "netbase.h" +#include "util.h" + +#ifndef WIN32 +#include +#endif + +#include "strlcpy.h" +#include // for to_lower() + +#define printf my_printf + +using namespace std; + +// Settings +typedef std::pair proxyType; +static proxyType proxyInfo[NET_MAX]; +static proxyType nameproxyInfo; +int nConnectTimeout = 5000; +bool fNameLookup = false; + +static const unsigned char pchIPv4[12] = {0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0xff, 0xff}; + +enum Network ParseNetwork(std::string net) { + boost::to_lower(net); + if (net == "ipv4") return NET_IPV4; + if (net == "ipv6") return NET_IPV6; + if (net == "tor") return NET_TOR; + if (net == "i2p") return NET_I2P; + return NET_UNROUTABLE; +} + +void SplitHostPort(std::string in, int &portOut, std::string &hostOut) { + size_t colon = in.find_last_of(':'); + // if a : is found, and it either follows a [...], or no other : is in the + // string, treat it as port separator + bool fHaveColon = colon != in.npos; + bool fBracketed = + fHaveColon && + (in[0] == '[' && in[colon - 1] == ']'); // if there is a colon, and + // in[0]=='[', colon is not 0, + // so in[colon-1] is safe + bool fMultiColon = + fHaveColon && (in.find_last_of(':', colon - 1) != in.npos); + if (fHaveColon && (colon == 0 || fBracketed || !fMultiColon)) { + char *endp = NULL; + int n = strtol(in.c_str() + colon + 1, &endp, 10); + if (endp && *endp == 0 && n >= 0) { + in = in.substr(0, colon); + if (n > 0 && n < 0x10000) portOut = n; + } + } + if (in.size() > 0 && in[0] == '[' && in[in.size() - 1] == ']') + hostOut = in.substr(1, in.size() - 2); + else + hostOut = in; +} + +bool static LookupIntern(const char *pszName, std::vector &vIP, + unsigned int nMaxSolutions, bool fAllowLookup) { + vIP.clear(); + + { + CNetAddr addr; + if (addr.SetSpecial(std::string(pszName))) { + vIP.push_back(addr); + return true; + } + } + + struct addrinfo aiHint; + memset(&aiHint, 0, sizeof(struct addrinfo)); + + aiHint.ai_socktype = SOCK_STREAM; + aiHint.ai_protocol = IPPROTO_TCP; +#ifdef WIN32 + aiHint.ai_family = AF_UNSPEC; + aiHint.ai_flags = fAllowLookup ? 0 : AI_NUMERICHOST; +#else + aiHint.ai_family = AF_UNSPEC; + aiHint.ai_flags = fAllowLookup ? AI_ADDRCONFIG : AI_NUMERICHOST; +#endif + struct addrinfo *aiRes = NULL; + int nErr = getaddrinfo(pszName, NULL, &aiHint, &aiRes); + if (nErr) return false; + + struct addrinfo *aiTrav = aiRes; + while (aiTrav != NULL && + (nMaxSolutions == 0 || vIP.size() < nMaxSolutions)) { + if (aiTrav->ai_family == AF_INET) { + assert(aiTrav->ai_addrlen >= sizeof(sockaddr_in)); + vIP.push_back( + CNetAddr(((struct sockaddr_in *)(aiTrav->ai_addr))->sin_addr)); + } + + if (aiTrav->ai_family == AF_INET6) { + assert(aiTrav->ai_addrlen >= sizeof(sockaddr_in6)); + vIP.push_back(CNetAddr( + ((struct sockaddr_in6 *)(aiTrav->ai_addr))->sin6_addr)); + } + + aiTrav = aiTrav->ai_next; + } + + freeaddrinfo(aiRes); + + return (vIP.size() > 0); +} + +bool LookupHost(const char *pszName, std::vector &vIP, + unsigned int nMaxSolutions, bool fAllowLookup) { + if (pszName[0] == 0) return false; + char psz[256]; + char *pszHost = psz; + strlcpy(psz, pszName, sizeof(psz)); + if (psz[0] == '[' && psz[strlen(psz) - 1] == ']') { + pszHost = psz + 1; + psz[strlen(psz) - 1] = 0; + } + + return LookupIntern(pszHost, vIP, nMaxSolutions, fAllowLookup); +} + +bool LookupHostNumeric(const char *pszName, std::vector &vIP, + unsigned int nMaxSolutions) { + return LookupHost(pszName, vIP, nMaxSolutions, false); +} + +bool Lookup(const char *pszName, std::vector &vAddr, int portDefault, + bool fAllowLookup, unsigned int nMaxSolutions) { + if (pszName[0] == 0) return false; + int port = portDefault; + std::string hostname = ""; + SplitHostPort(std::string(pszName), port, hostname); + + std::vector vIP; + bool fRet = + LookupIntern(hostname.c_str(), vIP, nMaxSolutions, fAllowLookup); + if (!fRet) return false; + vAddr.resize(vIP.size()); + for (unsigned int i = 0; i < vIP.size(); i++) + vAddr[i] = CService(vIP[i], port); + return true; +} + +bool Lookup(const char *pszName, CService &addr, int portDefault, + bool fAllowLookup) { + std::vector vService; + bool fRet = Lookup(pszName, vService, portDefault, fAllowLookup, 1); + if (!fRet) return false; + addr = vService[0]; + return true; +} + +bool LookupNumeric(const char *pszName, CService &addr, int portDefault) { + return Lookup(pszName, addr, portDefault, false); +} + +bool static Socks4(const CService &addrDest, SOCKET &hSocket) { + printf("SOCKS4 connecting %s\n", addrDest.ToString().c_str()); + if (!addrDest.IsIPv4()) { + closesocket(hSocket); + return error("Proxy destination is not IPv4"); + } + char pszSocks4IP[] = "\4\1\0\0\0\0\0\0user"; + struct sockaddr_in addr; + socklen_t len = sizeof(addr); + if (!addrDest.GetSockAddr((struct sockaddr *)&addr, &len) || + addr.sin_family != AF_INET) { + closesocket(hSocket); + return error("Cannot get proxy destination address"); + } + memcpy(pszSocks4IP + 2, &addr.sin_port, 2); + memcpy(pszSocks4IP + 4, &addr.sin_addr, 4); + char *pszSocks4 = pszSocks4IP; + int nSize = sizeof(pszSocks4IP); + + int ret = send(hSocket, pszSocks4, nSize, MSG_NOSIGNAL); + if (ret != nSize) { + closesocket(hSocket); + return error("Error sending to proxy"); + } + char pchRet[8]; + if (recv(hSocket, pchRet, 8, 0) != 8) { + closesocket(hSocket); + return error("Error reading proxy response"); + } + if (pchRet[1] != 0x5a) { + closesocket(hSocket); + if (pchRet[1] != 0x5b) + printf("ERROR: Proxy returned error %d\n", pchRet[1]); + return false; + } + printf("SOCKS4 connected %s\n", addrDest.ToString().c_str()); + return true; +} + +bool static Socks5(string strDest, int port, SOCKET &hSocket) { + printf("SOCKS5 connecting %s\n", strDest.c_str()); + if (strDest.size() > 255) { + closesocket(hSocket); + return error("Hostname too long"); + } + char pszSocks5Init[] = "\5\1\0"; + char *pszSocks5 = pszSocks5Init; + ssize_t nSize = sizeof(pszSocks5Init) - 1; + + ssize_t ret = send(hSocket, pszSocks5, nSize, MSG_NOSIGNAL); + if (ret != nSize) { + closesocket(hSocket); + return error("Error sending to proxy"); + } + char pchRet1[2]; + if (recv(hSocket, pchRet1, 2, 0) != 2) { + closesocket(hSocket); + return error("Error reading proxy response"); + } + if (pchRet1[0] != 0x05 || pchRet1[1] != 0x00) { + closesocket(hSocket); + return error("Proxy failed to initialize"); + } + string strSocks5("\5\1"); + strSocks5 += '\000'; + strSocks5 += '\003'; + strSocks5 += static_cast(std::min((int)strDest.size(), 255)); + strSocks5 += strDest; + strSocks5 += static_cast((port >> 8) & 0xFF); + strSocks5 += static_cast((port >> 0) & 0xFF); + ret = send(hSocket, strSocks5.c_str(), strSocks5.size(), MSG_NOSIGNAL); + if (ret != (ssize_t)strSocks5.size()) { + closesocket(hSocket); + return error("Error sending to proxy"); + } + char pchRet2[4]; + if (recv(hSocket, pchRet2, 4, 0) != 4) { + closesocket(hSocket); + return error("Error reading proxy response"); + } + if (pchRet2[0] != 0x05) { + closesocket(hSocket); + return error("Proxy failed to accept request"); + } + if (pchRet2[1] != 0x00) { + closesocket(hSocket); + switch (pchRet2[1]) { + case 0x01: + return error("Proxy error: general failure"); + case 0x02: + return error("Proxy error: connection not allowed"); + case 0x03: + return error("Proxy error: network unreachable"); + case 0x04: + return error("Proxy error: host unreachable"); + case 0x05: + return error("Proxy error: connection refused"); + case 0x06: + return error("Proxy error: TTL expired"); + case 0x07: + return error("Proxy error: protocol error"); + case 0x08: + return error("Proxy error: address type not supported"); + default: + return error("Proxy error: unknown"); + } + } + if (pchRet2[2] != 0x00) { + closesocket(hSocket); + return error("Error: malformed proxy response"); + } + char pchRet3[256]; + switch (pchRet2[3]) { + case 0x01: + ret = recv(hSocket, pchRet3, 4, 0) != 4; + break; + case 0x04: + ret = recv(hSocket, pchRet3, 16, 0) != 16; + break; + case 0x03: { + ret = recv(hSocket, pchRet3, 1, 0) != 1; + if (ret) return error("Error reading from proxy"); + int nRecv = pchRet3[0]; + ret = recv(hSocket, pchRet3, nRecv, 0) != nRecv; + break; + } + default: + closesocket(hSocket); + return error("Error: malformed proxy response"); + } + if (ret) { + closesocket(hSocket); + return error("Error reading from proxy"); + } + if (recv(hSocket, pchRet3, 2, 0) != 2) { + closesocket(hSocket); + return error("Error reading from proxy"); + } + printf("SOCKS5 connected %s\n", strDest.c_str()); + return true; +} + +bool static ConnectSocketDirectly(const CService &addrConnect, + SOCKET &hSocketRet, int nTimeout) { + hSocketRet = INVALID_SOCKET; + + struct sockaddr_storage sockaddr; + socklen_t len = sizeof(sockaddr); + if (!addrConnect.GetSockAddr((struct sockaddr *)&sockaddr, &len)) { + printf("Cannot connect to %s: unsupported network\n", + addrConnect.ToString().c_str()); + return false; + } + + SOCKET hSocket = socket(((struct sockaddr *)&sockaddr)->sa_family, + SOCK_STREAM, IPPROTO_TCP); + if (hSocket == INVALID_SOCKET) return false; +#ifdef SO_NOSIGPIPE + int set = 1; + setsockopt(hSocket, SOL_SOCKET, SO_NOSIGPIPE, (void *)&set, sizeof(int)); +#endif + +#ifdef WIN32 + u_long fNonblock = 1; + if (ioctlsocket(hSocket, FIONBIO, &fNonblock) == SOCKET_ERROR) +#else + int fFlags = fcntl(hSocket, F_GETFL, 0); + if (fcntl(hSocket, F_SETFL, fFlags | O_NONBLOCK) == -1) +#endif + { + closesocket(hSocket); + return false; + } + + if (connect(hSocket, (struct sockaddr *)&sockaddr, len) == SOCKET_ERROR) { + // WSAEINVAL is here because some legacy version of winsock uses it + if (WSAGetLastError() == WSAEINPROGRESS || + WSAGetLastError() == WSAEWOULDBLOCK || + WSAGetLastError() == WSAEINVAL) { + struct timeval timeout; + timeout.tv_sec = nTimeout / 1000; + timeout.tv_usec = (nTimeout % 1000) * 1000; + + fd_set fdset; + FD_ZERO(&fdset); + FD_SET(hSocket, &fdset); + int nRet = select(hSocket + 1, NULL, &fdset, NULL, &timeout); + if (nRet == 0) { + printf("connection timeout\n"); + closesocket(hSocket); + return false; + } + if (nRet == SOCKET_ERROR) { + printf("select() for connection failed: %i\n", + WSAGetLastError()); + closesocket(hSocket); + return false; + } + socklen_t nRetSize = sizeof(nRet); +#ifdef WIN32 + if (getsockopt(hSocket, SOL_SOCKET, SO_ERROR, (char *)(&nRet), + &nRetSize) == SOCKET_ERROR) +#else + if (getsockopt(hSocket, SOL_SOCKET, SO_ERROR, &nRet, &nRetSize) == + SOCKET_ERROR) +#endif + { + printf("getsockopt() for connection failed: %i\n", + WSAGetLastError()); + closesocket(hSocket); + return false; + } + if (nRet != 0) { + printf("connect() failed after select(): %s\n", strerror(nRet)); + closesocket(hSocket); + return false; + } + } +#ifdef WIN32 + else if (WSAGetLastError() != WSAEISCONN) +#else + else +#endif + { + printf("connect() failed: %i\n", WSAGetLastError()); + closesocket(hSocket); + return false; + } + } + +// this isn't even strictly necessary +// CNode::ConnectNode immediately turns the socket back to non-blocking +// but we'll turn it back to blocking just in case +#ifdef WIN32 + fNonblock = 0; + if (ioctlsocket(hSocket, FIONBIO, &fNonblock) == SOCKET_ERROR) +#else + fFlags = fcntl(hSocket, F_GETFL, 0); + if (fcntl(hSocket, F_SETFL, fFlags & !O_NONBLOCK) == SOCKET_ERROR) +#endif + { + closesocket(hSocket); + return false; + } + + hSocketRet = hSocket; + return true; +} + +bool SetProxy(enum Network net, CService addrProxy, int nSocksVersion) { + assert(net >= 0 && net < NET_MAX); + if (nSocksVersion != 0 && nSocksVersion != 4 && nSocksVersion != 5) + return false; + if (nSocksVersion != 0 && !addrProxy.IsValid()) return false; + proxyInfo[net] = std::make_pair(addrProxy, nSocksVersion); + return true; +} + +bool GetProxy(enum Network net, CService &addrProxy) { + assert(net >= 0 && net < NET_MAX); + if (!proxyInfo[net].second) return false; + addrProxy = proxyInfo[net].first; + return true; +} + +bool SetNameProxy(CService addrProxy, int nSocksVersion) { + if (nSocksVersion != 0 && nSocksVersion != 5) return false; + if (nSocksVersion != 0 && !addrProxy.IsValid()) return false; + nameproxyInfo = std::make_pair(addrProxy, nSocksVersion); + return true; +} + +bool GetNameProxy() { + return nameproxyInfo.second != 0; +} + +bool IsProxy(const CNetAddr &addr) { + for (int i = 0; i < NET_MAX; i++) { + if (proxyInfo[i].second && (addr == (CNetAddr)proxyInfo[i].first)) + return true; + } + return false; +} + +bool ConnectSocket(const CService &addrDest, SOCKET &hSocketRet, int nTimeout) { + const proxyType &proxy = proxyInfo[addrDest.GetNetwork()]; + + // no proxy needed + if (!proxy.second) + return ConnectSocketDirectly(addrDest, hSocketRet, nTimeout); + + SOCKET hSocket = INVALID_SOCKET; + + // first connect to proxy server + if (!ConnectSocketDirectly(proxy.first, hSocket, nTimeout)) return false; + + // do socks negotiation + switch (proxy.second) { + case 4: + if (!Socks4(addrDest, hSocket)) return false; + break; + case 5: + if (!Socks5(addrDest.ToStringIP(), addrDest.GetPort(), hSocket)) + return false; + break; + default: + return false; + } + + hSocketRet = hSocket; + return true; +} + +bool ConnectSocketByName(CService &addr, SOCKET &hSocketRet, + const char *pszDest, int portDefault, int nTimeout) { + string strDest; + int port = portDefault; + SplitHostPort(string(pszDest), port, strDest); + + SOCKET hSocket = INVALID_SOCKET; + CService addrResolved( + CNetAddr(strDest, fNameLookup && !nameproxyInfo.second), port); + if (addrResolved.IsValid()) { + addr = addrResolved; + return ConnectSocket(addr, hSocketRet, nTimeout); + } + addr = CService("0.0.0.0:0"); + if (!nameproxyInfo.second) return false; + if (!ConnectSocketDirectly(nameproxyInfo.first, hSocket, nTimeout)) + return false; + + switch (nameproxyInfo.second) { + default: + case 4: + return false; + case 5: + if (!Socks5(strDest, port, hSocket)) return false; + break; + } + + hSocketRet = hSocket; + return true; +} + +void CNetAddr::Init() { + memset(ip, 0, 16); +} + +void CNetAddr::SetIP(const CNetAddr &ipIn) { + memcpy(ip, ipIn.ip, sizeof(ip)); +} + +static const unsigned char pchOnionCat[] = {0xFD, 0x87, 0xD8, 0x7E, 0xEB, 0x43}; +static const unsigned char pchGarliCat[] = {0xFD, 0x60, 0xDB, 0x4D, 0xDD, 0xB5}; + +bool CNetAddr::SetSpecial(const std::string &strName) { + if (strName.size() > 6 && + strName.substr(strName.size() - 6, 6) == ".onion") { + std::vector vchAddr = + DecodeBase32(strName.substr(0, strName.size() - 6).c_str()); + if (vchAddr.size() != 16 - sizeof(pchOnionCat)) return false; + memcpy(ip, pchOnionCat, sizeof(pchOnionCat)); + for (unsigned int i = 0; i < 16 - sizeof(pchOnionCat); i++) + ip[i + sizeof(pchOnionCat)] = vchAddr[i]; + return true; + } + if (strName.size() > 11 && + strName.substr(strName.size() - 11, 11) == ".oc.b32.i2p") { + std::vector vchAddr = + DecodeBase32(strName.substr(0, strName.size() - 11).c_str()); + if (vchAddr.size() != 16 - sizeof(pchGarliCat)) return false; + memcpy(ip, pchOnionCat, sizeof(pchGarliCat)); + for (unsigned int i = 0; i < 16 - sizeof(pchGarliCat); i++) + ip[i + sizeof(pchGarliCat)] = vchAddr[i]; + return true; + } + return false; +} + +CNetAddr::CNetAddr() { + Init(); +} + +CNetAddr::CNetAddr(const struct in_addr &ipv4Addr) { + memcpy(ip, pchIPv4, 12); + memcpy(ip + 12, &ipv4Addr, 4); +} + +CNetAddr::CNetAddr(const struct in6_addr &ipv6Addr) { + memcpy(ip, &ipv6Addr, 16); +} + +CNetAddr::CNetAddr(const char *pszIp, bool fAllowLookup) { + Init(); + std::vector vIP; + if (LookupHost(pszIp, vIP, 1, fAllowLookup)) *this = vIP[0]; +} + +CNetAddr::CNetAddr(const std::string &strIp, bool fAllowLookup) { + Init(); + std::vector vIP; + if (LookupHost(strIp.c_str(), vIP, 1, fAllowLookup)) *this = vIP[0]; +} + +unsigned int CNetAddr::GetByte(int n) const { + return ip[15 - n]; +} + +bool CNetAddr::IsIPv4() const { + return (memcmp(ip, pchIPv4, sizeof(pchIPv4)) == 0); +} + +bool CNetAddr::IsIPv6() const { + return (!IsIPv4() && !IsTor() && !IsI2P()); +} + +bool CNetAddr::IsRFC1918() const { + return IsIPv4() && + (GetByte(3) == 10 || (GetByte(3) == 192 && GetByte(2) == 168) || + (GetByte(3) == 172 && (GetByte(2) >= 16 && GetByte(2) <= 31))); +} + +bool CNetAddr::IsReserved() const { + return IsIPv4() && (GetByte(3) >= 240); +} + +bool CNetAddr::IsRFC3927() const { + return IsIPv4() && (GetByte(3) == 169 && GetByte(2) == 254); +} + +bool CNetAddr::IsRFC3849() const { + return GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0x0D && + GetByte(12) == 0xB8; +} + +bool CNetAddr::IsRFC3964() const { + return (GetByte(15) == 0x20 && GetByte(14) == 0x02); +} + +bool CNetAddr::IsRFC6052() const { + static const unsigned char pchRFC6052[] = {0, 0x64, 0xFF, 0x9B, 0, 0, + 0, 0, 0, 0, 0, 0}; + return (memcmp(ip, pchRFC6052, sizeof(pchRFC6052)) == 0); +} + +bool CNetAddr::IsRFC4380() const { + return (GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0 && + GetByte(12) == 0); +} + +bool CNetAddr::IsRFC4862() const { + static const unsigned char pchRFC4862[] = {0xFE, 0x80, 0, 0, 0, 0, 0, 0}; + return (memcmp(ip, pchRFC4862, sizeof(pchRFC4862)) == 0); +} + +bool CNetAddr::IsRFC4193() const { + return ((GetByte(15) & 0xFE) == 0xFC); +} + +bool CNetAddr::IsRFC6145() const { + static const unsigned char pchRFC6145[] = {0, 0, 0, 0, 0, 0, + 0, 0, 0xFF, 0xFF, 0, 0}; + return (memcmp(ip, pchRFC6145, sizeof(pchRFC6145)) == 0); +} + +bool CNetAddr::IsRFC4843() const { + return (GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0x00 && + (GetByte(12) & 0xF0) == 0x10); +} + +bool CNetAddr::IsTor() const { + return (memcmp(ip, pchOnionCat, sizeof(pchOnionCat)) == 0); +} + +bool CNetAddr::IsI2P() const { + return (memcmp(ip, pchGarliCat, sizeof(pchGarliCat)) == 0); +} + +bool CNetAddr::IsLocal() const { + // IPv4 loopback + if (IsIPv4() && (GetByte(3) == 127 || GetByte(3) == 0)) return true; + + // IPv6 loopback (::1/128) + static const unsigned char pchLocal[16] = {0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1}; + if (memcmp(ip, pchLocal, 16) == 0) return true; + + return false; +} + +bool CNetAddr::IsMulticast() const { + return (IsIPv4() && (GetByte(3) & 0xF0) == 0xE0) || (GetByte(15) == 0xFF); +} + +bool CNetAddr::IsValid() const { + // Cleanup 3-byte shifted addresses caused by garbage in size field + // of addr messages from versions before 0.2.9 checksum. + // Two consecutive addr messages look like this: + // header20 vectorlen3 addr26 addr26 addr26 header20 vectorlen3 addr26 + // addr26 addr26... + // so if the first length field is garbled, it reads the second batch + // of addr misaligned by 3 bytes. + if (memcmp(ip, pchIPv4 + 3, sizeof(pchIPv4) - 3) == 0) return false; + + // unspecified IPv6 address (::/128) + unsigned char ipNone[16] = {}; + if (memcmp(ip, ipNone, 16) == 0) return false; + + // documentation IPv6 address + if (IsRFC3849()) return false; + + if (IsIPv4()) { + // INADDR_NONE + uint32_t ipNone = INADDR_NONE; + if (memcmp(ip + 12, &ipNone, 4) == 0) return false; + + // 0 + ipNone = 0; + if (memcmp(ip + 12, &ipNone, 4) == 0) return false; + } + + return true; +} + +bool CNetAddr::IsRoutable() const { + return IsValid() && + !(IsReserved() || IsRFC1918() || IsRFC3927() || IsRFC4862() || + (IsRFC4193() && !IsTor() && !IsI2P()) || IsRFC4843() || IsLocal()); +} + +enum Network CNetAddr::GetNetwork() const { + if (!IsRoutable()) return NET_UNROUTABLE; + + if (IsIPv4()) return NET_IPV4; + + if (IsTor()) return NET_TOR; + + if (IsI2P()) return NET_I2P; + + return NET_IPV6; +} + +std::string CNetAddr::ToStringIP() const { + if (IsTor()) return EncodeBase32(&ip[6], 10) + ".onion"; + if (IsI2P()) return EncodeBase32(&ip[6], 10) + ".oc.b32.i2p"; + CService serv(*this, 0); + struct sockaddr_storage sockaddr; + socklen_t socklen = sizeof(sockaddr); + if (serv.GetSockAddr((struct sockaddr *)&sockaddr, &socklen)) { + char name[1025] = ""; + if (!getnameinfo((const struct sockaddr *)&sockaddr, socklen, name, + sizeof(name), NULL, 0, NI_NUMERICHOST)) + return std::string(name); + } + if (IsIPv4()) + return strprintf("%u.%u.%u.%u", GetByte(3), GetByte(2), GetByte(1), + GetByte(0)); + else + return strprintf( + "%x:%x:%x:%x:%x:%x:%x:%x", GetByte(15) << 8 | GetByte(14), + GetByte(13) << 8 | GetByte(12), GetByte(11) << 8 | GetByte(10), + GetByte(9) << 8 | GetByte(8), GetByte(7) << 8 | GetByte(6), + GetByte(5) << 8 | GetByte(4), GetByte(3) << 8 | GetByte(2), + GetByte(1) << 8 | GetByte(0)); +} + +std::string CNetAddr::ToString() const { + return ToStringIP(); +} + +bool operator==(const CNetAddr &a, const CNetAddr &b) { + return (memcmp(a.ip, b.ip, 16) == 0); +} + +bool operator!=(const CNetAddr &a, const CNetAddr &b) { + return (memcmp(a.ip, b.ip, 16) != 0); +} + +bool operator<(const CNetAddr &a, const CNetAddr &b) { + return (memcmp(a.ip, b.ip, 16) < 0); +} + +bool CNetAddr::GetInAddr(struct in_addr *pipv4Addr) const { + if (!IsIPv4()) return false; + memcpy(pipv4Addr, ip + 12, 4); + return true; +} + +bool CNetAddr::GetIn6Addr(struct in6_addr *pipv6Addr) const { + memcpy(pipv6Addr, ip, 16); + return true; +} + +// get canonical identifier of an address' group +// no two connections will be attempted to addresses with the same group +std::vector CNetAddr::GetGroup() const { + std::vector vchRet; + int nClass = NET_IPV6; + int nStartByte = 0; + int nBits = 16; + + // all local addresses belong to the same group + if (IsLocal()) { + nClass = 255; + nBits = 0; + } + + // all unroutable addresses belong to the same group + if (!IsRoutable()) { + nClass = NET_UNROUTABLE; + nBits = 0; + } + // for IPv4 addresses, '1' + the 16 higher-order bits of the IP + // includes mapped IPv4, SIIT translated IPv4, and the well-known prefix + else if (IsIPv4() || IsRFC6145() || IsRFC6052()) { + nClass = NET_IPV4; + nStartByte = 12; + } + // for 6to4 tunnelled addresses, use the encapsulated IPv4 address + else if (IsRFC3964()) { + nClass = NET_IPV4; + nStartByte = 2; + } + // for Teredo-tunnelled IPv6 addresses, use the encapsulated IPv4 address + else if (IsRFC4380()) { + vchRet.push_back(NET_IPV4); + vchRet.push_back(GetByte(3) ^ 0xFF); + vchRet.push_back(GetByte(2) ^ 0xFF); + return vchRet; + } else if (IsTor()) { + nClass = NET_TOR; + nStartByte = 6; + nBits = 4; + } else if (IsI2P()) { + nClass = NET_I2P; + nStartByte = 6; + nBits = 4; + } + // for he.net, use /36 groups + else if (GetByte(15) == 0x20 && GetByte(14) == 0x11 && + GetByte(13) == 0x04 && GetByte(12) == 0x70) + nBits = 36; + // for the rest of the IPv6 network, use /32 groups + else + nBits = 32; + + vchRet.push_back(nClass); + while (nBits >= 8) { + vchRet.push_back(GetByte(15 - nStartByte)); + nStartByte++; + nBits -= 8; + } + if (nBits > 0) + vchRet.push_back(GetByte(15 - nStartByte) | ((1 << nBits) - 1)); + + return vchRet; +} + +uint64 CNetAddr::GetHash() const { + uint256 hash = Hash(&ip[0], &ip[16]); + uint64 nRet; + memcpy(&nRet, &hash, sizeof(nRet)); + return nRet; +} + +void CNetAddr::print() const { + printf("CNetAddr(%s)\n", ToString().c_str()); +} + +// private extensions to enum Network, only returned by GetExtNetwork, +// and only used in GetReachabilityFrom +static const int NET_UNKNOWN = NET_MAX + 0; +static const int NET_TEREDO = NET_MAX + 1; +int static GetExtNetwork(const CNetAddr *addr) { + if (addr == NULL) return NET_UNKNOWN; + if (addr->IsRFC4380()) return NET_TEREDO; + return addr->GetNetwork(); +} + +/** Calculates a metric for how reachable (*this) is from a given partner */ +int CNetAddr::GetReachabilityFrom(const CNetAddr *paddrPartner) const { + enum Reachability { + REACH_UNREACHABLE, + REACH_DEFAULT, + REACH_TEREDO, + REACH_IPV6_WEAK, + REACH_IPV4, + REACH_IPV6_STRONG, + REACH_PRIVATE + }; + + if (!IsRoutable()) return REACH_UNREACHABLE; + + int ourNet = GetExtNetwork(this); + int theirNet = GetExtNetwork(paddrPartner); + bool fTunnel = IsRFC3964() || IsRFC6052() || IsRFC6145(); + + switch (theirNet) { + case NET_IPV4: + switch (ourNet) { + default: + return REACH_DEFAULT; + case NET_IPV4: + return REACH_IPV4; + } + case NET_IPV6: + switch (ourNet) { + default: + return REACH_DEFAULT; + case NET_TEREDO: + return REACH_TEREDO; + case NET_IPV4: + return REACH_IPV4; + case NET_IPV6: + return fTunnel ? REACH_IPV6_WEAK + : REACH_IPV6_STRONG; // only prefer giving + // our IPv6 address if + // it's not tunnelled + } + case NET_TOR: + switch (ourNet) { + default: + return REACH_DEFAULT; + case NET_IPV4: + return REACH_IPV4; // Tor users can connect to IPv4 as well + case NET_TOR: + return REACH_PRIVATE; + } + case NET_I2P: + switch (ourNet) { + default: + return REACH_DEFAULT; + case NET_I2P: + return REACH_PRIVATE; + } + case NET_TEREDO: + switch (ourNet) { + default: + return REACH_DEFAULT; + case NET_TEREDO: + return REACH_TEREDO; + case NET_IPV6: + return REACH_IPV6_WEAK; + case NET_IPV4: + return REACH_IPV4; + } + case NET_UNKNOWN: + case NET_UNROUTABLE: + default: + switch (ourNet) { + default: + return REACH_DEFAULT; + case NET_TEREDO: + return REACH_TEREDO; + case NET_IPV6: + return REACH_IPV6_WEAK; + case NET_IPV4: + return REACH_IPV4; + case NET_I2P: + return REACH_PRIVATE; // assume connections from unroutable + // addresses are + case NET_TOR: + return REACH_PRIVATE; // either from Tor/I2P, or don't care + // about our address + } + } +} + +void CService::Init() { + port = 0; +} + +CService::CService() { + Init(); +} + +CService::CService(const CNetAddr &cip, unsigned short portIn) + : CNetAddr(cip), port(portIn) {} + +CService::CService(const struct in_addr &ipv4Addr, unsigned short portIn) + : CNetAddr(ipv4Addr), port(portIn) {} + +CService::CService(const struct in6_addr &ipv6Addr, unsigned short portIn) + : CNetAddr(ipv6Addr), port(portIn) {} + +CService::CService(const struct sockaddr_in &addr) + : CNetAddr(addr.sin_addr), port(ntohs(addr.sin_port)) { + assert(addr.sin_family == AF_INET); +} + +CService::CService(const struct sockaddr_in6 &addr) + : CNetAddr(addr.sin6_addr), port(ntohs(addr.sin6_port)) { + assert(addr.sin6_family == AF_INET6); +} + +bool CService::SetSockAddr(const struct sockaddr *paddr) { + switch (paddr->sa_family) { + case AF_INET: + *this = CService(*(const struct sockaddr_in *)paddr); + return true; + case AF_INET6: + *this = CService(*(const struct sockaddr_in6 *)paddr); + return true; + default: + return false; + } +} + +CService::CService(const char *pszIpPort, bool fAllowLookup) { + Init(); + CService ip; + if (Lookup(pszIpPort, ip, 0, fAllowLookup)) *this = ip; +} + +CService::CService(const char *pszIpPort, int portDefault, bool fAllowLookup) { + Init(); + CService ip; + if (Lookup(pszIpPort, ip, portDefault, fAllowLookup)) *this = ip; +} + +CService::CService(const std::string &strIpPort, bool fAllowLookup) { + Init(); + CService ip; + if (Lookup(strIpPort.c_str(), ip, 0, fAllowLookup)) *this = ip; +} + +CService::CService(const std::string &strIpPort, int portDefault, + bool fAllowLookup) { + Init(); + CService ip; + if (Lookup(strIpPort.c_str(), ip, portDefault, fAllowLookup)) *this = ip; +} + +unsigned short CService::GetPort() const { + return port; +} + +bool operator==(const CService &a, const CService &b) { + return (CNetAddr)a == (CNetAddr)b && a.port == b.port; +} + +bool operator!=(const CService &a, const CService &b) { + return (CNetAddr)a != (CNetAddr)b || a.port != b.port; +} + +bool operator<(const CService &a, const CService &b) { + return (CNetAddr)a < (CNetAddr)b || + ((CNetAddr)a == (CNetAddr)b && a.port < b.port); +} + +bool CService::GetSockAddr(struct sockaddr *paddr, socklen_t *addrlen) const { + if (IsIPv4()) { + if (*addrlen < (socklen_t)sizeof(struct sockaddr_in)) return false; + *addrlen = sizeof(struct sockaddr_in); + struct sockaddr_in *paddrin = (struct sockaddr_in *)paddr; + memset(paddrin, 0, *addrlen); + if (!GetInAddr(&paddrin->sin_addr)) return false; + paddrin->sin_family = AF_INET; + paddrin->sin_port = htons(port); + return true; + } + if (IsIPv6()) { + if (*addrlen < (socklen_t)sizeof(struct sockaddr_in6)) return false; + *addrlen = sizeof(struct sockaddr_in6); + struct sockaddr_in6 *paddrin6 = (struct sockaddr_in6 *)paddr; + memset(paddrin6, 0, *addrlen); + if (!GetIn6Addr(&paddrin6->sin6_addr)) return false; + paddrin6->sin6_family = AF_INET6; + paddrin6->sin6_port = htons(port); + return true; + } + return false; +} + +std::vector CService::GetKey() const { + std::vector vKey; + vKey.resize(18); + memcpy(&vKey[0], ip, 16); + vKey[16] = port / 0x100; + vKey[17] = port & 0x0FF; + return vKey; +} + +std::string CService::ToStringPort() const { + return strprintf("%u", port); +} + +std::string CService::ToStringIPPort() const { + if (IsIPv4() || IsTor() || IsI2P()) { + return ToStringIP() + ":" + ToStringPort(); + } else { + return "[" + ToStringIP() + "]:" + ToStringPort(); + } +} + +std::string CService::ToString() const { + return ToStringIPPort(); +} + +void CService::print() const { + printf("CService(%s)\n", ToString().c_str()); +} + +void CService::SetPort(unsigned short portIn) { + port = portIn; +} diff --git a/src/seeder/netbase.h b/src/seeder/netbase.h new file mode 100644 index 000000000..15ba42e4d --- /dev/null +++ b/src/seeder/netbase.h @@ -0,0 +1,149 @@ +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_NETBASE_H +#define BITCOIN_NETBASE_H + +#include +#include + +#include "compat.h" +#include "serialize.h" + +extern int nConnectTimeout; + +#ifdef WIN32 +// In MSVC, this is defined as a macro, undefine it to prevent a compile and +// link error +#undef SetPort +#endif + +enum Network { + NET_UNROUTABLE, + NET_IPV4, + NET_IPV6, + NET_TOR, + NET_I2P, + + NET_MAX, +}; + +extern int nConnectTimeout; +extern bool fNameLookup; + +/** IP address (IPv6, or IPv4 using mapped IPv6 range (::FFFF:0:0/96)) */ +class CNetAddr { +protected: + unsigned char ip[16]; // in network byte order + +public: + CNetAddr(); + CNetAddr(const struct in_addr &ipv4Addr); + explicit CNetAddr(const char *pszIp, bool fAllowLookup = false); + explicit CNetAddr(const std::string &strIp, bool fAllowLookup = false); + void Init(); + void SetIP(const CNetAddr &ip); + bool SetSpecial(const std::string &strName); // for Tor and I2P addresses + bool IsIPv4() const; // IPv4 mapped address (::FFFF:0:0/96, 0.0.0.0/0) + bool IsIPv6() const; // IPv6 address (not mapped IPv4, not Tor/I2P) + bool IsReserved() const; // Against Hetzners Abusal/Netscan Bot + bool IsRFC1918() const; // IPv4 private networks (10.0.0.0/8, + // 192.168.0.0/16, 172.16.0.0/12) + bool IsRFC3849() const; // IPv6 documentation address (2001:0DB8::/32) + bool IsRFC3927() const; // IPv4 autoconfig (169.254.0.0/16) + bool IsRFC3964() const; // IPv6 6to4 tunnelling (2002::/16) + bool IsRFC4193() const; // IPv6 unique local (FC00::/15) + bool IsRFC4380() const; // IPv6 Teredo tunnelling (2001::/32) + bool IsRFC4843() const; // IPv6 ORCHID (2001:10::/28) + bool IsRFC4862() const; // IPv6 autoconfig (FE80::/64) + bool IsRFC6052() const; // IPv6 well-known prefix (64:FF9B::/96) + bool IsRFC6145() const; // IPv6 IPv4-translated address (::FFFF:0:0:0/96) + bool IsTor() const; + bool IsI2P() const; + bool IsLocal() const; + bool IsRoutable() const; + bool IsValid() const; + bool IsMulticast() const; + enum Network GetNetwork() const; + std::string ToString() const; + std::string ToStringIP() const; + unsigned int GetByte(int n) const; + uint64 GetHash() const; + bool GetInAddr(struct in_addr *pipv4Addr) const; + std::vector GetGroup() const; + int GetReachabilityFrom(const CNetAddr *paddrPartner = NULL) const; + void print() const; + + CNetAddr(const struct in6_addr &pipv6Addr); + bool GetIn6Addr(struct in6_addr *pipv6Addr) const; + + friend bool operator==(const CNetAddr &a, const CNetAddr &b); + friend bool operator!=(const CNetAddr &a, const CNetAddr &b); + friend bool operator<(const CNetAddr &a, const CNetAddr &b); + + IMPLEMENT_SERIALIZE(READWRITE(FLATDATA(ip));) +}; + +/** A combination of a network address (CNetAddr) and a (TCP) port */ +class CService : public CNetAddr { +protected: + unsigned short port; // host order + +public: + CService(); + CService(const CNetAddr &ip, unsigned short port); + CService(const struct in_addr &ipv4Addr, unsigned short port); + CService(const struct sockaddr_in &addr); + explicit CService(const char *pszIpPort, int portDefault, + bool fAllowLookup = false); + explicit CService(const char *pszIpPort, bool fAllowLookup = false); + explicit CService(const std::string &strIpPort, int portDefault, + bool fAllowLookup = false); + explicit CService(const std::string &strIpPort, bool fAllowLookup = false); + void Init(); + void SetPort(unsigned short portIn); + unsigned short GetPort() const; + bool GetSockAddr(struct sockaddr *paddr, socklen_t *addrlen) const; + bool SetSockAddr(const struct sockaddr *paddr); + friend bool operator==(const CService &a, const CService &b); + friend bool operator!=(const CService &a, const CService &b); + friend bool operator<(const CService &a, const CService &b); + std::vector GetKey() const; + std::string ToString() const; + std::string ToStringPort() const; + std::string ToStringIPPort() const; + void print() const; + + CService(const struct in6_addr &ipv6Addr, unsigned short port); + CService(const struct sockaddr_in6 &addr); + + IMPLEMENT_SERIALIZE(CService *pthis = const_cast(this); + READWRITE(FLATDATA(ip)); + unsigned short portN = htons(port); READWRITE(portN); + if (fRead) pthis->port = ntohs(portN);) +}; + +enum Network ParseNetwork(std::string net); +void SplitHostPort(std::string in, int &portOut, std::string &hostOut); +bool SetProxy(enum Network net, CService addrProxy, int nSocksVersion = 5); +bool GetProxy(enum Network net, CService &addrProxy); +bool IsProxy(const CNetAddr &addr); +bool SetNameProxy(CService addrProxy, int nSocksVersion = 5); +bool GetNameProxy(); +bool LookupHost(const char *pszName, std::vector &vIP, + unsigned int nMaxSolutions = 0, bool fAllowLookup = true); +bool LookupHostNumeric(const char *pszName, std::vector &vIP, + unsigned int nMaxSolutions = 0); +bool Lookup(const char *pszName, CService &addr, int portDefault = 0, + bool fAllowLookup = true); +bool Lookup(const char *pszName, std::vector &vAddr, + int portDefault = 0, bool fAllowLookup = true, + unsigned int nMaxSolutions = 0); +bool LookupNumeric(const char *pszName, CService &addr, int portDefault = 0); +bool ConnectSocket(const CService &addr, SOCKET &hSocketRet, + int nTimeout = nConnectTimeout); +bool ConnectSocketByName(CService &addr, SOCKET &hSocketRet, + const char *pszDest, int portDefault = 0, + int nTimeout = nConnectTimeout); + +#endif diff --git a/src/seeder/protocol.cpp b/src/seeder/protocol.cpp new file mode 100644 index 000000000..9768ff397 --- /dev/null +++ b/src/seeder/protocol.cpp @@ -0,0 +1,134 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2011 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. + +#include +#include + +#include "netbase.h" +#include "protocol.h" +#include "util.h" + +#ifndef WIN32 +#include +#endif + +static const char *ppszTypeName[] = { + "ERROR", "tx", "block", +}; + +uint8_t pchMessageStart[4] = {0xe3, 0xe1, 0xf3, 0xe8}; + +CMessageHeader::CMessageHeader() { + memcpy(pchMessageStart, ::pchMessageStart, sizeof(pchMessageStart)); + memset(pchCommand, 0, sizeof(pchCommand)); + pchCommand[1] = 1; + nMessageSize = -1; + nChecksum = 0; +} + +CMessageHeader::CMessageHeader(const char *pszCommand, + unsigned int nMessageSizeIn) { + memcpy(pchMessageStart, ::pchMessageStart, sizeof(pchMessageStart)); + strncpy(pchCommand, pszCommand, COMMAND_SIZE); + nMessageSize = nMessageSizeIn; + nChecksum = 0; +} + +std::string CMessageHeader::GetCommand() const { + if (pchCommand[COMMAND_SIZE - 1] == 0) + return std::string(pchCommand, pchCommand + strlen(pchCommand)); + else + return std::string(pchCommand, pchCommand + COMMAND_SIZE); +} + +bool CMessageHeader::IsValid() const { + // Check start string + if (memcmp(pchMessageStart, ::pchMessageStart, sizeof(pchMessageStart)) != + 0) + return false; + + // Check the command string for errors + for (const char *p1 = pchCommand; p1 < pchCommand + COMMAND_SIZE; p1++) { + if (*p1 == 0) { + // Must be all zeros after the first zero + for (; p1 < pchCommand + COMMAND_SIZE; p1++) + if (*p1 != 0) return false; + } else if (*p1 < ' ' || *p1 > 0x7E) + return false; + } + + // Message size + if (nMessageSize > MAX_SIZE) { + printf("CMessageHeader::IsValid() : (%s, %u bytes) nMessageSize > " + "MAX_SIZE\n", + GetCommand().c_str(), nMessageSize); + return false; + } + + return true; +} + +CAddress::CAddress() : CService() { + Init(); +} + +CAddress::CAddress(CService ipIn, uint64_t nServicesIn) : CService(ipIn) { + Init(); + nServices = nServicesIn; +} + +void CAddress::Init() { + nServices = NODE_NETWORK | NODE_BITCOIN_CASH; + nTime = 100000000; +} + +void CAddress::print() const { + printf("CAddress(%s)\n", ToString().c_str()); +} + +CInv::CInv() { + type = 0; + hash = 0; +} + +CInv::CInv(int typeIn, const uint256 &hashIn) { + type = typeIn; + hash = hashIn; +} + +CInv::CInv(const std::string &strType, const uint256 &hashIn) { + int i; + for (i = 1; i < ARRAYLEN(ppszTypeName); i++) { + if (strType == ppszTypeName[i]) { + type = i; + break; + } + } + if (i == ARRAYLEN(ppszTypeName)) + throw std::out_of_range("CInv::CInv(string, uint256) : unknown type"); + hash = hashIn; +} + +bool operator<(const CInv &a, const CInv &b) { + return (a.type < b.type || (a.type == b.type && a.hash < b.hash)); +} + +bool CInv::IsKnownType() const { + return (type >= 1 && type < ARRAYLEN(ppszTypeName)); +} + +const char *CInv::GetCommand() const { + if (!IsKnownType()) + throw std::out_of_range("CInv::GetCommand() : unknown type"); + return ppszTypeName[type]; +} + +std::string CInv::ToString() const { + return "CInv()"; +} + +void CInv::print() const { + printf("CInv\n"); +} diff --git a/src/seeder/protocol.h b/src/seeder/protocol.h new file mode 100644 index 000000000..693296a01 --- /dev/null +++ b/src/seeder/protocol.h @@ -0,0 +1,111 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2011 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. + +#ifndef __cplusplus +#error This header can only be compiled as C++. +#endif + +#ifndef __INCLUDED_PROTOCOL_H__ +#define __INCLUDED_PROTOCOL_H__ + +#include "netbase.h" +#include "serialize.h" +#include "uint256.h" + +#include +#include + +extern bool fTestNet; +static inline unsigned short GetDefaultPort(const bool testnet = fTestNet) { + return testnet ? 18333 : 8333; +} + +// +// Message header +// (4) message start +// (12) command +// (4) size +// (4) checksum + +extern unsigned char pchMessageStart[4]; + +class CMessageHeader { +public: + CMessageHeader(); + CMessageHeader(const char *pszCommand, unsigned int nMessageSizeIn); + + std::string GetCommand() const; + bool IsValid() const; + + IMPLEMENT_SERIALIZE(READWRITE(FLATDATA(pchMessageStart)); + READWRITE(FLATDATA(pchCommand)); + READWRITE(nMessageSize); + if (nVersion >= 209) READWRITE(nChecksum);) + + // TODO: make private (improves encapsulation) +public: + enum { COMMAND_SIZE = 12 }; + char pchMessageStart[sizeof(::pchMessageStart)]; + char pchCommand[COMMAND_SIZE]; + unsigned int nMessageSize; + unsigned int nChecksum; +}; + +enum ServiceFlags : uint64_t { + NODE_NETWORK = (1 << 0), + NODE_BLOOM = (1 << 2), + NODE_XTHIN = (1 << 4), + NODE_BITCOIN_CASH = (1 << 5), +}; + +class CAddress : public CService { +public: + CAddress(); + CAddress(CService ipIn, + uint64_t nServicesIn = NODE_NETWORK | NODE_BITCOIN_CASH); + + void Init(); + + IMPLEMENT_SERIALIZE(CAddress *pthis = const_cast(this); + CService *pip = (CService *)pthis; + if (fRead) pthis->Init(); + if (nType & SER_DISK) READWRITE(nVersion); + if ((nType & SER_DISK) || + (nVersion >= 31402 && !(nType & SER_GETHASH))) + READWRITE(nTime); + READWRITE(nServices); READWRITE(*pip);) + + void print() const; + + // TODO: make private (improves encapsulation) +public: + uint64 nServices; + + // disk and network only + unsigned int nTime; +}; + +class CInv { +public: + CInv(); + CInv(int typeIn, const uint256 &hashIn); + CInv(const std::string &strType, const uint256 &hashIn); + + IMPLEMENT_SERIALIZE(READWRITE(type); READWRITE(hash);) + + friend bool operator<(const CInv &a, const CInv &b); + + bool IsKnownType() const; + const char *GetCommand() const; + std::string ToString() const; + void print() const; + + // TODO: make private (improves encapsulation) +public: + int type; + uint256 hash; +}; + +#endif // __INCLUDED_PROTOCOL_H__ diff --git a/src/seeder/serialize.h b/src/seeder/serialize.h new file mode 100644 index 000000000..36eeee7b6 --- /dev/null +++ b/src/seeder/serialize.h @@ -0,0 +1,1358 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2011 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_SERIALIZE_H +#define BITCOIN_SERIALIZE_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#if defined(_MSC_VER) || defined(__BORLANDC__) +typedef __int64 int64; +typedef unsigned __int64 uint64; +#else +typedef long long int64; +typedef unsigned long long uint64; +#endif +#if defined(_MSC_VER) && _MSC_VER < 1300 +#define for if (false); else for +#endif + +#ifdef WIN32 +#include +// This is used to attempt to keep keying material out of swap +// Note that VirtualLock does not provide this as a guarantee on Windows, +// but, in practice, memory that has been VirtualLock'd almost never gets +// written to +// the pagefile except in rare circumstances where memory is extremely low. +#include +#define mlock(p, n) VirtualLock((p), (n)); +#define munlock(p, n) VirtualUnlock((p), (n)); +#else +#include +#include +/* This comes from limits.h if it's not defined there set a sane default */ +#ifndef PAGESIZE +#include +#define PAGESIZE sysconf(_SC_PAGESIZE) +#endif +#define mlock(a, b) \ + mlock(((void *)(((size_t)(a)) & (~((PAGESIZE)-1)))), \ + (((((size_t)(a)) + (b)-1) | ((PAGESIZE)-1)) + 1) - \ + (((size_t)(a)) & (~((PAGESIZE)-1)))) +#define munlock(a, b) \ + munlock(((void *)(((size_t)(a)) & (~((PAGESIZE)-1)))), \ + (((((size_t)(a)) + (b)-1) | ((PAGESIZE)-1)) + 1) - \ + (((size_t)(a)) & (~((PAGESIZE)-1)))) +#endif + +class CScript; +class CDataStream; +class CAutoFile; +static const unsigned int MAX_SIZE = 0x02000000; + +static const int PROTOCOL_VERSION = 60000; + +// Used to bypass the rule against non-const reference to temporary +// where it makes sense with wrappers such as CFlatData or CTxDB +template inline T &REF(const T &val) { + return const_cast(val); +} + +///////////////////////////////////////////////////////////////// +// +// Templates for serializing to anything that looks like a stream, +// i.e. anything that supports .read(char*, int) and .write(char*, int) +// + +enum { + // primary actions + SER_NETWORK = (1 << 0), + SER_DISK = (1 << 1), + SER_GETHASH = (1 << 2), + + // modifiers + SER_SKIPSIG = (1 << 16), + SER_BLOCKHEADERONLY = (1 << 17), +}; + +#define IMPLEMENT_SERIALIZE(statements) \ + unsigned int GetSerializeSize(int nType = 0, \ + int nVersion = PROTOCOL_VERSION) const { \ + CSerActionGetSerializeSize ser_action; \ + const bool fGetSize = true; \ + const bool fWrite = false; \ + const bool fRead = false; \ + unsigned int nSerSize = 0; \ + ser_streamplaceholder s; \ + s.nType = nType; \ + s.nVersion = nVersion; \ + { statements } \ + return nSerSize; \ + } \ + template \ + void Serialize(Stream &s, int nType = 0, int nVersion = PROTOCOL_VERSION) \ + const { \ + CSerActionSerialize ser_action; \ + const bool fGetSize = false; \ + const bool fWrite = true; \ + const bool fRead = false; \ + unsigned int nSerSize = 0; \ + { statements } \ + } \ + template \ + void Unserialize(Stream &s, int nType = 0, \ + int nVersion = PROTOCOL_VERSION) { \ + CSerActionUnserialize ser_action; \ + const bool fGetSize = false; \ + const bool fWrite = false; \ + const bool fRead = true; \ + unsigned int nSerSize = 0; \ + { statements } \ + } + +#define READWRITE(obj) \ + (nSerSize += ::SerReadWrite(s, (obj), nType, nVersion, ser_action)) + +// +// Basic types +// +#define WRITEDATA(s, obj) s.write((char *)&(obj), sizeof(obj)) +#define READDATA(s, obj) s.read((char *)&(obj), sizeof(obj)) + +inline unsigned int GetSerializeSize(char a, int, int = 0) { + return sizeof(a); +} +inline unsigned int GetSerializeSize(signed char a, int, int = 0) { + return sizeof(a); +} +inline unsigned int GetSerializeSize(unsigned char a, int, int = 0) { + return sizeof(a); +} +inline unsigned int GetSerializeSize(signed short a, int, int = 0) { + return sizeof(a); +} +inline unsigned int GetSerializeSize(unsigned short a, int, int = 0) { + return sizeof(a); +} +inline unsigned int GetSerializeSize(signed int a, int, int = 0) { + return sizeof(a); +} +inline unsigned int GetSerializeSize(unsigned int a, int, int = 0) { + return sizeof(a); +} +inline unsigned int GetSerializeSize(signed long a, int, int = 0) { + return sizeof(a); +} +inline unsigned int GetSerializeSize(unsigned long a, int, int = 0) { + return sizeof(a); +} +inline unsigned int GetSerializeSize(int64 a, int, int = 0) { + return sizeof(a); +} +inline unsigned int GetSerializeSize(uint64 a, int, int = 0) { + return sizeof(a); +} +inline unsigned int GetSerializeSize(float a, int, int = 0) { + return sizeof(a); +} +inline unsigned int GetSerializeSize(double a, int, int = 0) { + return sizeof(a); +} + +template +inline void Serialize(Stream &s, char a, int, int = 0) { + WRITEDATA(s, a); +} +template +inline void Serialize(Stream &s, signed char a, int, int = 0) { + WRITEDATA(s, a); +} +template +inline void Serialize(Stream &s, unsigned char a, int, int = 0) { + WRITEDATA(s, a); +} +template +inline void Serialize(Stream &s, signed short a, int, int = 0) { + WRITEDATA(s, a); +} +template +inline void Serialize(Stream &s, unsigned short a, int, int = 0) { + WRITEDATA(s, a); +} +template +inline void Serialize(Stream &s, signed int a, int, int = 0) { + WRITEDATA(s, a); +} +template +inline void Serialize(Stream &s, unsigned int a, int, int = 0) { + WRITEDATA(s, a); +} +template +inline void Serialize(Stream &s, signed long a, int, int = 0) { + WRITEDATA(s, a); +} +template +inline void Serialize(Stream &s, unsigned long a, int, int = 0) { + WRITEDATA(s, a); +} +template +inline void Serialize(Stream &s, int64 a, int, int = 0) { + WRITEDATA(s, a); +} +template +inline void Serialize(Stream &s, uint64 a, int, int = 0) { + WRITEDATA(s, a); +} +template +inline void Serialize(Stream &s, float a, int, int = 0) { + WRITEDATA(s, a); +} +template +inline void Serialize(Stream &s, double a, int, int = 0) { + WRITEDATA(s, a); +} + +template +inline void Unserialize(Stream &s, char &a, int, int = 0) { + READDATA(s, a); +} +template +inline void Unserialize(Stream &s, signed char &a, int, int = 0) { + READDATA(s, a); +} +template +inline void Unserialize(Stream &s, unsigned char &a, int, int = 0) { + READDATA(s, a); +} +template +inline void Unserialize(Stream &s, signed short &a, int, int = 0) { + READDATA(s, a); +} +template +inline void Unserialize(Stream &s, unsigned short &a, int, int = 0) { + READDATA(s, a); +} +template +inline void Unserialize(Stream &s, signed int &a, int, int = 0) { + READDATA(s, a); +} +template +inline void Unserialize(Stream &s, unsigned int &a, int, int = 0) { + READDATA(s, a); +} +template +inline void Unserialize(Stream &s, signed long &a, int, int = 0) { + READDATA(s, a); +} +template +inline void Unserialize(Stream &s, unsigned long &a, int, int = 0) { + READDATA(s, a); +} +template +inline void Unserialize(Stream &s, int64 &a, int, int = 0) { + READDATA(s, a); +} +template +inline void Unserialize(Stream &s, uint64 &a, int, int = 0) { + READDATA(s, a); +} +template +inline void Unserialize(Stream &s, float &a, int, int = 0) { + READDATA(s, a); +} +template +inline void Unserialize(Stream &s, double &a, int, int = 0) { + READDATA(s, a); +} + +inline unsigned int GetSerializeSize(bool a, int, int = 0) { + return sizeof(char); +} +template +inline void Serialize(Stream &s, bool a, int, int = 0) { + char f = a; + WRITEDATA(s, f); +} +template +inline void Unserialize(Stream &s, bool &a, int, int = 0) { + char f; + READDATA(s, f); + a = f; +} + +// +// Compact size +// size < 253 -- 1 byte +// size <= USHRT_MAX -- 3 bytes (253 + 2 bytes) +// size <= UINT_MAX -- 5 bytes (254 + 4 bytes) +// size > UINT_MAX -- 9 bytes (255 + 8 bytes) +// +inline unsigned int GetSizeOfCompactSize(uint64 nSize) { + if (nSize < 253) + return sizeof(unsigned char); + else if (nSize <= USHRT_MAX) + return sizeof(unsigned char) + sizeof(unsigned short); + else if (nSize <= UINT_MAX) + return sizeof(unsigned char) + sizeof(unsigned int); + else + return sizeof(unsigned char) + sizeof(uint64); +} + +template void WriteCompactSize(Stream &os, uint64 nSize) { + if (nSize < 253) { + unsigned char chSize = nSize; + WRITEDATA(os, chSize); + } else if (nSize <= USHRT_MAX) { + unsigned char chSize = 253; + unsigned short xSize = nSize; + WRITEDATA(os, chSize); + WRITEDATA(os, xSize); + } else if (nSize <= UINT_MAX) { + unsigned char chSize = 254; + unsigned int xSize = nSize; + WRITEDATA(os, chSize); + WRITEDATA(os, xSize); + } else { + unsigned char chSize = 255; + uint64 xSize = nSize; + WRITEDATA(os, chSize); + WRITEDATA(os, xSize); + } + return; +} + +template uint64 ReadCompactSize(Stream &is) { + unsigned char chSize; + READDATA(is, chSize); + uint64 nSizeRet = 0; + if (chSize < 253) { + nSizeRet = chSize; + } else if (chSize == 253) { + unsigned short xSize; + READDATA(is, xSize); + nSizeRet = xSize; + } else if (chSize == 254) { + unsigned int xSize; + READDATA(is, xSize); + nSizeRet = xSize; + } else { + uint64 xSize; + READDATA(is, xSize); + nSizeRet = xSize; + } + if (nSizeRet > (uint64)MAX_SIZE) + throw std::ios_base::failure("ReadCompactSize() : size too large"); + return nSizeRet; +} + +// +// Wrapper for serializing arrays and POD +// There's a clever template way to make arrays serialize normally, but MSVC6 +// doesn't support it +// +#define FLATDATA(obj) \ + REF(CFlatData((char *)&(obj), (char *)&(obj) + sizeof(obj))) +class CFlatData { +protected: + char *pbegin; + char *pend; + +public: + CFlatData(void *pbeginIn, void *pendIn) + : pbegin((char *)pbeginIn), pend((char *)pendIn) {} + char *begin() { return pbegin; } + const char *begin() const { return pbegin; } + char *end() { return pend; } + const char *end() const { return pend; } + + unsigned int GetSerializeSize(int, int = 0) const { return pend - pbegin; } + + template void Serialize(Stream &s, int, int = 0) const { + s.write(pbegin, pend - pbegin); + } + + template void Unserialize(Stream &s, int, int = 0) { + s.read(pbegin, pend - pbegin); + } +}; + +// +// string stored as a fixed length field +// +template class CFixedFieldString { +protected: + const std::string *pcstr; + std::string *pstr; + +public: + explicit CFixedFieldString(const std::string &str) + : pcstr(&str), pstr(NULL) {} + explicit CFixedFieldString(std::string &str) : pcstr(&str), pstr(&str) {} + + unsigned int GetSerializeSize(int, int = 0) const { return LEN; } + + template void Serialize(Stream &s, int, int = 0) const { + char pszBuf[LEN]; + strncpy(pszBuf, pcstr->c_str(), LEN); + s.write(pszBuf, LEN); + } + + template void Unserialize(Stream &s, int, int = 0) { + if (pstr == NULL) + throw std::ios_base::failure("CFixedFieldString::Unserialize : " + "trying to unserialize to const " + "string"); + char pszBuf[LEN + 1]; + s.read(pszBuf, LEN); + pszBuf[LEN] = '\0'; + *pstr = pszBuf; + } +}; + +// +// Forward declarations +// + +// string +template +unsigned int GetSerializeSize(const std::basic_string &str, int, int = 0); +template +void Serialize(Stream &os, const std::basic_string &str, int, int = 0); +template +void Unserialize(Stream &is, std::basic_string &str, int, int = 0); + +// vector +template +unsigned int GetSerializeSize_impl(const std::vector &v, int nType, + int nVersion, const boost::true_type &); +template +unsigned int GetSerializeSize_impl(const std::vector &v, int nType, + int nVersion, const boost::false_type &); +template +inline unsigned int GetSerializeSize(const std::vector &v, int nType, + int nVersion = PROTOCOL_VERSION); +template +void Serialize_impl(Stream &os, const std::vector &v, int nType, + int nVersion, const boost::true_type &); +template +void Serialize_impl(Stream &os, const std::vector &v, int nType, + int nVersion, const boost::false_type &); +template +inline void Serialize(Stream &os, const std::vector &v, int nType, + int nVersion = PROTOCOL_VERSION); +template +void Unserialize_impl(Stream &is, std::vector &v, int nType, int nVersion, + const boost::true_type &); +template +void Unserialize_impl(Stream &is, std::vector &v, int nType, int nVersion, + const boost::false_type &); +template +inline void Unserialize(Stream &is, std::vector &v, int nType, + int nVersion = PROTOCOL_VERSION); + +// others derived from vector +extern inline unsigned int GetSerializeSize(const CScript &v, int nType, + int nVersion = PROTOCOL_VERSION); +template +void Serialize(Stream &os, const CScript &v, int nType, + int nVersion = PROTOCOL_VERSION); +template +void Unserialize(Stream &is, CScript &v, int nType, + int nVersion = PROTOCOL_VERSION); + +// pair +template +unsigned int GetSerializeSize(const std::pair &item, int nType, + int nVersion = PROTOCOL_VERSION); +template +void Serialize(Stream &os, const std::pair &item, int nType, + int nVersion = PROTOCOL_VERSION); +template +void Unserialize(Stream &is, std::pair &item, int nType, + int nVersion = PROTOCOL_VERSION); + +// 3 tuple +template +unsigned int GetSerializeSize(const boost::tuple &item, int nType, + int nVersion = PROTOCOL_VERSION); +template +void Serialize(Stream &os, const boost::tuple &item, int nType, + int nVersion = PROTOCOL_VERSION); +template +void Unserialize(Stream &is, boost::tuple &item, int nType, + int nVersion = PROTOCOL_VERSION); + +// 4 tuple +template +unsigned int GetSerializeSize(const boost::tuple &item, + int nType, int nVersion = PROTOCOL_VERSION); +template +void Serialize(Stream &os, const boost::tuple &item, int nType, + int nVersion = PROTOCOL_VERSION); +template +void Unserialize(Stream &is, boost::tuple &item, int nType, + int nVersion = PROTOCOL_VERSION); + +// map +template +unsigned int GetSerializeSize(const std::map &m, int nType, + int nVersion = PROTOCOL_VERSION); +template +void Serialize(Stream &os, const std::map &m, int nType, + int nVersion = PROTOCOL_VERSION); +template +void Unserialize(Stream &is, std::map &m, int nType, + int nVersion = PROTOCOL_VERSION); + +// set +template +unsigned int GetSerializeSize(const std::set &m, int nType, + int nVersion = PROTOCOL_VERSION); +template +void Serialize(Stream &os, const std::set &m, int nType, + int nVersion = PROTOCOL_VERSION); +template +void Unserialize(Stream &is, std::set &m, int nType, + int nVersion = PROTOCOL_VERSION); + +// +// If none of the specialized versions above matched, default to calling member +// function. +// "int nType" is changed to "long nType" to keep from getting an ambiguous +// overload error. +// The compiler will only cast int to long if none of the other templates +// matched. +// Thanks to Boost serialization for this idea. +// +template +inline unsigned int GetSerializeSize(const T &a, long nType, + int nVersion = PROTOCOL_VERSION) { + return a.GetSerializeSize((int)nType, nVersion); +} + +template +inline void Serialize(Stream &os, const T &a, long nType, + int nVersion = PROTOCOL_VERSION) { + a.Serialize(os, (int)nType, nVersion); +} + +template +inline void Unserialize(Stream &is, T &a, long nType, + int nVersion = PROTOCOL_VERSION) { + a.Unserialize(is, (int)nType, nVersion); +} + +// +// string +// +template +unsigned int GetSerializeSize(const std::basic_string &str, int, int) { + return GetSizeOfCompactSize(str.size()) + str.size() * sizeof(str[0]); +} + +template +void Serialize(Stream &os, const std::basic_string &str, int, int) { + WriteCompactSize(os, str.size()); + if (!str.empty()) os.write((char *)&str[0], str.size() * sizeof(str[0])); +} + +template +void Unserialize(Stream &is, std::basic_string &str, int, int) { + unsigned int nSize = ReadCompactSize(is); + str.resize(nSize); + if (nSize != 0) is.read((char *)&str[0], nSize * sizeof(str[0])); +} + +// +// vector +// +template +unsigned int GetSerializeSize_impl(const std::vector &v, int nType, + int nVersion, const boost::true_type &) { + return (GetSizeOfCompactSize(v.size()) + v.size() * sizeof(T)); +} + +template +unsigned int GetSerializeSize_impl(const std::vector &v, int nType, + int nVersion, const boost::false_type &) { + unsigned int nSize = GetSizeOfCompactSize(v.size()); + for (typename std::vector::const_iterator vi = v.begin(); + vi != v.end(); ++vi) + nSize += GetSerializeSize((*vi), nType, nVersion); + return nSize; +} + +template +inline unsigned int GetSerializeSize(const std::vector &v, int nType, + int nVersion) { + return GetSerializeSize_impl(v, nType, nVersion, + boost::is_fundamental()); +} + +template +void Serialize_impl(Stream &os, const std::vector &v, int nType, + int nVersion, const boost::true_type &) { + WriteCompactSize(os, v.size()); + if (!v.empty()) os.write((char *)&v[0], v.size() * sizeof(T)); +} + +template +void Serialize_impl(Stream &os, const std::vector &v, int nType, + int nVersion, const boost::false_type &) { + WriteCompactSize(os, v.size()); + for (typename std::vector::const_iterator vi = v.begin(); + vi != v.end(); ++vi) + ::Serialize(os, (*vi), nType, nVersion); +} + +template +inline void Serialize(Stream &os, const std::vector &v, int nType, + int nVersion) { + Serialize_impl(os, v, nType, nVersion, boost::is_fundamental()); +} + +template +void Unserialize_impl(Stream &is, std::vector &v, int nType, int nVersion, + const boost::true_type &) { + // unsigned int nSize = ReadCompactSize(is); + // v.resize(nSize); + // is.read((char*)&v[0], nSize * sizeof(T)); + + // Limit size per read so bogus size value won't cause out of memory + v.clear(); + unsigned int nSize = ReadCompactSize(is); + unsigned int i = 0; + while (i < nSize) { + unsigned int blk = + std::min(nSize - i, (unsigned int)(1 + 4999999 / sizeof(T))); + v.resize(i + blk); + is.read((char *)&v[i], blk * sizeof(T)); + i += blk; + } +} + +template +void Unserialize_impl(Stream &is, std::vector &v, int nType, int nVersion, + const boost::false_type &) { + // unsigned int nSize = ReadCompactSize(is); + // v.resize(nSize); + // for (std::vector::iterator vi = v.begin(); vi != v.end(); ++vi) + // Unserialize(is, (*vi), nType, nVersion); + + v.clear(); + unsigned int nSize = ReadCompactSize(is); + unsigned int i = 0; + unsigned int nMid = 0; + while (nMid < nSize) { + nMid += 5000000 / sizeof(T); + if (nMid > nSize) nMid = nSize; + v.resize(nMid); + for (; i < nMid; i++) + Unserialize(is, v[i], nType, nVersion); + } +} + +template +inline void Unserialize(Stream &is, std::vector &v, int nType, + int nVersion) { + Unserialize_impl(is, v, nType, nVersion, boost::is_fundamental()); +} + +// +// others derived from vector +// +inline unsigned int GetSerializeSize(const CScript &v, int nType, + int nVersion) { + return GetSerializeSize((const std::vector &)v, nType, + nVersion); +} + +template +void Serialize(Stream &os, const CScript &v, int nType, int nVersion) { + Serialize(os, (const std::vector &)v, nType, nVersion); +} + +template +void Unserialize(Stream &is, CScript &v, int nType, int nVersion) { + Unserialize(is, (std::vector &)v, nType, nVersion); +} + +// +// pair +// +template +unsigned int GetSerializeSize(const std::pair &item, int nType, + int nVersion) { + return GetSerializeSize(item.first, nType, nVersion) + + GetSerializeSize(item.second, nType, nVersion); +} + +template +void Serialize(Stream &os, const std::pair &item, int nType, + int nVersion) { + Serialize(os, item.first, nType, nVersion); + Serialize(os, item.second, nType, nVersion); +} + +template +void Unserialize(Stream &is, std::pair &item, int nType, int nVersion) { + Unserialize(is, item.first, nType, nVersion); + Unserialize(is, item.second, nType, nVersion); +} + +// +// 3 tuple +// +template +unsigned int GetSerializeSize(const boost::tuple &item, int nType, + int nVersion) { + unsigned int nSize = 0; + nSize += GetSerializeSize(boost::get<0>(item), nType, nVersion); + nSize += GetSerializeSize(boost::get<1>(item), nType, nVersion); + nSize += GetSerializeSize(boost::get<2>(item), nType, nVersion); + return nSize; +} + +template +void Serialize(Stream &os, const boost::tuple &item, int nType, + int nVersion) { + Serialize(os, boost::get<0>(item), nType, nVersion); + Serialize(os, boost::get<1>(item), nType, nVersion); + Serialize(os, boost::get<2>(item), nType, nVersion); +} + +template +void Unserialize(Stream &is, boost::tuple &item, int nType, + int nVersion) { + Unserialize(is, boost::get<0>(item), nType, nVersion); + Unserialize(is, boost::get<1>(item), nType, nVersion); + Unserialize(is, boost::get<2>(item), nType, nVersion); +} + +// +// 4 tuple +// +template +unsigned int GetSerializeSize(const boost::tuple &item, + int nType, int nVersion) { + unsigned int nSize = 0; + nSize += GetSerializeSize(boost::get<0>(item), nType, nVersion); + nSize += GetSerializeSize(boost::get<1>(item), nType, nVersion); + nSize += GetSerializeSize(boost::get<2>(item), nType, nVersion); + nSize += GetSerializeSize(boost::get<3>(item), nType, nVersion); + return nSize; +} + +template +void Serialize(Stream &os, const boost::tuple &item, int nType, + int nVersion) { + Serialize(os, boost::get<0>(item), nType, nVersion); + Serialize(os, boost::get<1>(item), nType, nVersion); + Serialize(os, boost::get<2>(item), nType, nVersion); + Serialize(os, boost::get<3>(item), nType, nVersion); +} + +template +void Unserialize(Stream &is, boost::tuple &item, int nType, + int nVersion) { + Unserialize(is, boost::get<0>(item), nType, nVersion); + Unserialize(is, boost::get<1>(item), nType, nVersion); + Unserialize(is, boost::get<2>(item), nType, nVersion); + Unserialize(is, boost::get<3>(item), nType, nVersion); +} + +// +// map +// +template +unsigned int GetSerializeSize(const std::map &m, int nType, + int nVersion) { + unsigned int nSize = GetSizeOfCompactSize(m.size()); + for (typename std::map::const_iterator mi = m.begin(); + mi != m.end(); ++mi) + nSize += GetSerializeSize((*mi), nType, nVersion); + return nSize; +} + +template +void Serialize(Stream &os, const std::map &m, int nType, + int nVersion) { + WriteCompactSize(os, m.size()); + for (typename std::map::const_iterator mi = m.begin(); + mi != m.end(); ++mi) + Serialize(os, (*mi), nType, nVersion); +} + +template +void Unserialize(Stream &is, std::map &m, int nType, + int nVersion) { + m.clear(); + unsigned int nSize = ReadCompactSize(is); + typename std::map::iterator mi = m.begin(); + for (unsigned int i = 0; i < nSize; i++) { + std::pair item; + Unserialize(is, item, nType, nVersion); + mi = m.insert(mi, item); + } +} + +// +// set +// +template +unsigned int GetSerializeSize(const std::set &m, int nType, + int nVersion) { + unsigned int nSize = GetSizeOfCompactSize(m.size()); + for (typename std::set::const_iterator it = m.begin(); + it != m.end(); ++it) + nSize += GetSerializeSize((*it), nType, nVersion); + return nSize; +} + +template +void Serialize(Stream &os, const std::set &m, int nType, + int nVersion) { + WriteCompactSize(os, m.size()); + for (typename std::set::const_iterator it = m.begin(); + it != m.end(); ++it) + Serialize(os, (*it), nType, nVersion); +} + +template +void Unserialize(Stream &is, std::set &m, int nType, int nVersion) { + m.clear(); + unsigned int nSize = ReadCompactSize(is); + typename std::set::iterator it = m.begin(); + for (unsigned int i = 0; i < nSize; i++) { + K key; + Unserialize(is, key, nType, nVersion); + it = m.insert(it, key); + } +} + +// +// Support for IMPLEMENT_SERIALIZE and READWRITE macro +// +class CSerActionGetSerializeSize {}; +class CSerActionSerialize {}; +class CSerActionUnserialize {}; + +template +inline unsigned int SerReadWrite(Stream &s, const T &obj, int nType, + int nVersion, + CSerActionGetSerializeSize ser_action) { + return ::GetSerializeSize(obj, nType, nVersion); +} + +template +inline unsigned int SerReadWrite(Stream &s, const T &obj, int nType, + int nVersion, CSerActionSerialize ser_action) { + ::Serialize(s, obj, nType, nVersion); + return 0; +} + +template +inline unsigned int SerReadWrite(Stream &s, T &obj, int nType, int nVersion, + CSerActionUnserialize ser_action) { + ::Unserialize(s, obj, nType, nVersion); + return 0; +} + +struct ser_streamplaceholder { + int nType; + int nVersion; +}; + +// +// Allocator that locks its contents from being paged +// out of memory and clears its contents before deletion. +// +template struct secure_allocator : public std::allocator { + // MSVC8 default copy constructor is broken + typedef std::allocator base; + typedef typename base::size_type size_type; + typedef typename base::difference_type difference_type; + typedef typename base::pointer pointer; + typedef typename base::const_pointer const_pointer; + typedef typename base::reference reference; + typedef typename base::const_reference const_reference; + typedef typename base::value_type value_type; + secure_allocator() throw() {} + secure_allocator(const secure_allocator &a) throw() : base(a) {} + template + secure_allocator(const secure_allocator &a) throw() : base(a) {} + ~secure_allocator() throw() {} + template struct rebind { + typedef secure_allocator<_Other> other; + }; + + T *allocate(std::size_t n, const void *hint = 0) { + T *p; + p = std::allocator::allocate(n, hint); + if (p != NULL) mlock(p, sizeof(T) * n); + return p; + } + + void deallocate(T *p, std::size_t n) { + if (p != NULL) { + memset(p, 0, sizeof(T) * n); + munlock(p, sizeof(T) * n); + } + std::allocator::deallocate(p, n); + } +}; + +// +// Double ended buffer combining vector and stream-like interfaces. +// >> and << read and write unformatted data using the above serialization +// templates. +// Fills with data in linear time; some stringstream implementations take N^2 +// time. +// +class CDataStream { +protected: + typedef std::vector> vector_type; + vector_type vch; + unsigned int nReadPos; + short state; + short exceptmask; + +public: + int nType; + int nVersion; + + typedef vector_type::allocator_type allocator_type; + typedef vector_type::size_type size_type; + typedef vector_type::difference_type difference_type; + typedef vector_type::reference reference; + typedef vector_type::const_reference const_reference; + typedef vector_type::value_type value_type; + typedef vector_type::iterator iterator; + typedef vector_type::const_iterator const_iterator; + typedef vector_type::reverse_iterator reverse_iterator; + + explicit CDataStream(int nTypeIn = SER_NETWORK, + int nVersionIn = PROTOCOL_VERSION) { + Init(nTypeIn, nVersionIn); + } + + CDataStream(const_iterator pbegin, const_iterator pend, + int nTypeIn = SER_NETWORK, int nVersionIn = PROTOCOL_VERSION) + : vch(pbegin, pend) { + Init(nTypeIn, nVersionIn); + } + +#if !defined(_MSC_VER) || _MSC_VER >= 1300 + CDataStream(const char *pbegin, const char *pend, int nTypeIn = SER_NETWORK, + int nVersionIn = PROTOCOL_VERSION) + : vch(pbegin, pend) { + Init(nTypeIn, nVersionIn); + } +#endif + + CDataStream(const vector_type &vchIn, int nTypeIn = SER_NETWORK, + int nVersionIn = PROTOCOL_VERSION) + : vch(vchIn.begin(), vchIn.end()) { + Init(nTypeIn, nVersionIn); + } + + CDataStream(const std::vector &vchIn, int nTypeIn = SER_NETWORK, + int nVersionIn = PROTOCOL_VERSION) + : vch(vchIn.begin(), vchIn.end()) { + Init(nTypeIn, nVersionIn); + } + + CDataStream(const std::vector &vchIn, + int nTypeIn = SER_NETWORK, int nVersionIn = PROTOCOL_VERSION) + : vch((char *)&vchIn.begin()[0], (char *)&vchIn.end()[0]) { + Init(nTypeIn, nVersionIn); + } + + void Init(int nTypeIn = SER_NETWORK, int nVersionIn = PROTOCOL_VERSION) { + nReadPos = 0; + nType = nTypeIn; + nVersion = nVersionIn; + state = 0; + exceptmask = std::ios::badbit | std::ios::failbit; + } + + CDataStream &operator+=(const CDataStream &b) { + vch.insert(vch.end(), b.begin(), b.end()); + return *this; + } + + friend CDataStream operator+(const CDataStream &a, const CDataStream &b) { + CDataStream ret = a; + ret += b; + return (ret); + } + + std::string str() const { return (std::string(begin(), end())); } + + // + // Vector subset + // + const_iterator begin() const { return vch.begin() + nReadPos; } + iterator begin() { return vch.begin() + nReadPos; } + const_iterator end() const { return vch.end(); } + iterator end() { return vch.end(); } + size_type size() const { return vch.size() - nReadPos; } + bool empty() const { return vch.size() == nReadPos; } + void resize(size_type n, value_type c = 0) { vch.resize(n + nReadPos, c); } + void reserve(size_type n) { vch.reserve(n + nReadPos); } + const_reference operator[](size_type pos) const { + return vch[pos + nReadPos]; + } + reference operator[](size_type pos) { return vch[pos + nReadPos]; } + void clear() { + vch.clear(); + nReadPos = 0; + } + iterator insert(iterator it, const char &x = char()) { + return vch.insert(it, x); + } + void insert(iterator it, size_type n, const char &x) { + vch.insert(it, n, x); + } + + void insert(iterator it, std::vector::const_iterator first, + std::vector::const_iterator last) { + if (it == vch.begin() + nReadPos && last - first <= nReadPos) { + // special case for inserting at the front when there's room + nReadPos -= (last - first); + memcpy(&vch[nReadPos], &first[0], last - first); + } else + vch.insert(it, first, last); + } + +#if !defined(_MSC_VER) || _MSC_VER >= 1300 + void insert(iterator it, const char *first, const char *last) { + if (it == vch.begin() + nReadPos && last - first <= nReadPos) { + // special case for inserting at the front when there's room + nReadPos -= (last - first); + memcpy(&vch[nReadPos], &first[0], last - first); + } else + vch.insert(it, first, last); + } +#endif + + iterator erase(iterator it) { + if (it == vch.begin() + nReadPos) { + // special case for erasing from the front + if (++nReadPos >= vch.size()) { + // whenever we reach the end, we take the opportunity to clear + // the buffer + nReadPos = 0; + return vch.erase(vch.begin(), vch.end()); + } + return vch.begin() + nReadPos; + } else + return vch.erase(it); + } + + iterator erase(iterator first, iterator last) { + if (first == vch.begin() + nReadPos) { + // special case for erasing from the front + if (last == vch.end()) { + nReadPos = 0; + return vch.erase(vch.begin(), vch.end()); + } else { + nReadPos = (last - vch.begin()); + return last; + } + } else + return vch.erase(first, last); + } + + inline void Compact() { + vch.erase(vch.begin(), vch.begin() + nReadPos); + nReadPos = 0; + } + + bool Rewind(size_type n) { + // Rewind by n characters if the buffer hasn't been compacted yet + if (n > nReadPos) return false; + nReadPos -= n; + return true; + } + + // + // Stream subset + // + void setstate(short bits, const char *psz) { + state |= bits; + if (state & exceptmask) throw std::ios_base::failure(psz); + } + + bool eof() const { return size() == 0; } + bool fail() const { return state & (std::ios::badbit | std::ios::failbit); } + bool good() const { return !eof() && (state == 0); } + void clear(short n) { state = n; } // name conflict with vector clear() + short exceptions() { return exceptmask; } + short exceptions(short mask) { + short prev = exceptmask; + exceptmask = mask; + setstate(0, "CDataStream"); + return prev; + } + CDataStream *rdbuf() { return this; } + int in_avail() { return size(); } + + void SetType(int n) { nType = n; } + int GetType() { return nType; } + void SetVersion(int n) { nVersion = n; } + int GetVersion() { return nVersion; } + void ReadVersion() { *this >> nVersion; } + void WriteVersion() { *this << nVersion; } + + CDataStream &read(char *pch, int nSize) { + // Read from the beginning of the buffer + assert(nSize >= 0); + unsigned int nReadPosNext = nReadPos + nSize; + if (nReadPosNext >= vch.size()) { + if (nReadPosNext > vch.size()) { + setstate(std::ios::failbit, + "CDataStream::read() : end of data"); + memset(pch, 0, nSize); + nSize = vch.size() - nReadPos; + } + memcpy(pch, &vch[nReadPos], nSize); + nReadPos = 0; + vch.clear(); + return (*this); + } + memcpy(pch, &vch[nReadPos], nSize); + nReadPos = nReadPosNext; + return (*this); + } + + CDataStream &ignore(int nSize) { + // Ignore from the beginning of the buffer + assert(nSize >= 0); + unsigned int nReadPosNext = nReadPos + nSize; + if (nReadPosNext >= vch.size()) { + if (nReadPosNext > vch.size()) { + setstate(std::ios::failbit, + "CDataStream::ignore() : end of data"); + nSize = vch.size() - nReadPos; + } + nReadPos = 0; + vch.clear(); + return (*this); + } + nReadPos = nReadPosNext; + return (*this); + } + + CDataStream &write(const char *pch, int nSize) { + // Write to the end of the buffer + assert(nSize >= 0); + vch.insert(vch.end(), pch, pch + nSize); + return (*this); + } + + template + void Serialize(Stream &s, int nType = 0, + int nVersion = PROTOCOL_VERSION) const { + // Special case: stream << stream concatenates like stream += stream + if (!vch.empty()) s.write((char *)&vch[0], vch.size() * sizeof(vch[0])); + } + + template unsigned int GetSerializeSize(const T &obj) { + // Tells the size of the object if serialized to this stream + return ::GetSerializeSize(obj, nType, nVersion); + } + + template CDataStream &operator<<(const T &obj) { + // Serialize to this stream + ::Serialize(*this, obj, nType, nVersion); + return (*this); + } + + template CDataStream &operator>>(T &obj) { + // Unserialize from this stream + ::Unserialize(*this, obj, nType, nVersion); + return (*this); + } +}; + +#ifdef TESTCDATASTREAM +// VC6sp6 +// CDataStream: +// n=1000 0 seconds +// n=2000 0 seconds +// n=4000 0 seconds +// n=8000 0 seconds +// n=16000 0 seconds +// n=32000 0 seconds +// n=64000 1 seconds +// n=128000 1 seconds +// n=256000 2 seconds +// n=512000 4 seconds +// n=1024000 8 seconds +// n=2048000 16 seconds +// n=4096000 32 seconds +// stringstream: +// n=1000 1 seconds +// n=2000 1 seconds +// n=4000 13 seconds +// n=8000 87 seconds +// n=16000 400 seconds +// n=32000 1660 seconds +// n=64000 6749 seconds +// n=128000 27241 seconds +// n=256000 109804 seconds +#include +int main(int argc, char *argv[]) { + vector vch(0xcc, 250); + printf("CDataStream:\n"); + for (int n = 1000; n <= 4500000; n *= 2) { + CDataStream ss; + time_t nStart = time(NULL); + for (int i = 0; i < n; i++) + ss.write((char *)&vch[0], vch.size()); + printf("n=%-10d %d seconds\n", n, time(NULL) - nStart); + } + printf("stringstream:\n"); + for (int n = 1000; n <= 4500000; n *= 2) { + stringstream ss; + time_t nStart = time(NULL); + for (int i = 0; i < n; i++) + ss.write((char *)&vch[0], vch.size()); + printf("n=%-10d %d seconds\n", n, time(NULL) - nStart); + } +} +#endif + +// +// Automatic closing wrapper for FILE* +// - Will automatically close the file when it goes out of scope if not null. +// - If you're returning the file pointer, return file.release(). +// - If you need to close the file early, use file.fclose() instead of +// fclose(file). +// +class CAutoFile { +protected: + FILE *file; + short state; + short exceptmask; + +public: + int nType; + int nVersion; + + typedef FILE element_type; + + CAutoFile(FILE *filenew = NULL, int nTypeIn = SER_DISK, + int nVersionIn = PROTOCOL_VERSION) { + file = filenew; + nType = nTypeIn; + nVersion = nVersionIn; + state = 0; + exceptmask = std::ios::badbit | std::ios::failbit; + } + + ~CAutoFile() { fclose(); } + + void fclose() { + if (file != NULL && file != stdin && file != stdout && file != stderr) + ::fclose(file); + file = NULL; + } + + FILE *release() { + FILE *ret = file; + file = NULL; + return ret; + } + operator FILE *() { return file; } + FILE *operator->() { return file; } + FILE &operator*() { return *file; } + FILE **operator&() { return &file; } + FILE *operator=(FILE *pnew) { return file = pnew; } + bool operator!() { return (file == NULL); } + + // + // Stream subset + // + void setstate(short bits, const char *psz) { + state |= bits; + if (state & exceptmask) throw std::ios_base::failure(psz); + } + + bool fail() const { return state & (std::ios::badbit | std::ios::failbit); } + bool good() const { return state == 0; } + void clear(short n = 0) { state = n; } + short exceptions() { return exceptmask; } + short exceptions(short mask) { + short prev = exceptmask; + exceptmask = mask; + setstate(0, "CAutoFile"); + return prev; + } + + void SetType(int n) { nType = n; } + int GetType() { return nType; } + void SetVersion(int n) { nVersion = n; } + int GetVersion() { return nVersion; } + void ReadVersion() { *this >> nVersion; } + void WriteVersion() { *this << nVersion; } + + CAutoFile &read(char *pch, int nSize) { + if (!file) + throw std::ios_base::failure( + "CAutoFile::read : file handle is NULL"); + if (fread(pch, 1, nSize, file) != nSize) + setstate(std::ios::failbit, feof(file) + ? "CAutoFile::read : end of file" + : "CAutoFile::read : fread failed"); + return (*this); + } + + CAutoFile &write(const char *pch, int nSize) { + if (!file) + throw std::ios_base::failure( + "CAutoFile::write : file handle is NULL"); + if (fwrite(pch, 1, nSize, file) != nSize) + setstate(std::ios::failbit, "CAutoFile::write : write failed"); + return (*this); + } + + template unsigned int GetSerializeSize(const T &obj) { + // Tells the size of the object if serialized to this stream + return ::GetSerializeSize(obj, nType, nVersion); + } + + template CAutoFile &operator<<(const T &obj) { + // Serialize to this stream + if (!file) + throw std::ios_base::failure( + "CAutoFile::operator<< : file handle is NULL"); + ::Serialize(*this, obj, nType, nVersion); + return (*this); + } + + template CAutoFile &operator>>(T &obj) { + // Unserialize from this stream + if (!file) + throw std::ios_base::failure( + "CAutoFile::operator>> : file handle is NULL"); + ::Unserialize(*this, obj, nType, nVersion); + return (*this); + } +}; + +#endif diff --git a/src/seeder/strlcpy.h b/src/seeder/strlcpy.h new file mode 100644 index 000000000..f33f685d3 --- /dev/null +++ b/src/seeder/strlcpy.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 1998 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef BITCOIN_STRLCPY_H +#define BITCOIN_STRLCPY_H + +#include +#include + +/* + * Copy src to string dst of size siz. At most siz-1 characters + * will be copied. Always NUL terminates (unless siz == 0). + * Returns strlen(src); if retval >= siz, truncation occurred. + */ +inline size_t strlcpy(char *dst, const char *src, size_t siz) { + char *d = dst; + const char *s = src; + size_t n = siz; + + /* Copy as many bytes as will fit */ + if (n != 0) { + while (--n != 0) { + if ((*d++ = *s++) == '\0') break; + } + } + + /* Not enough room in dst, add NUL and traverse rest of src */ + if (n == 0) { + if (siz != 0) *d = '\0'; /* NUL-terminate dst */ + while (*s++) + ; + } + + return (s - src - 1); /* count does not include NUL */ +} + +/* + * Appends src to string dst of size siz (unlike strncat, siz is the + * full size of dst, not space left). At most siz-1 characters + * will be copied. Always NUL terminates (unless siz <= strlen(dst)). + * Returns strlen(src) + MIN(siz, strlen(initial dst)). + * If retval >= siz, truncation occurred. + */ +inline size_t strlcat(char *dst, const char *src, size_t siz) { + char *d = dst; + const char *s = src; + size_t n = siz; + size_t dlen; + + /* Find the end of dst and adjust bytes left but don't go past end */ + while (n-- != 0 && *d != '\0') + d++; + dlen = d - dst; + n = siz - dlen; + + if (n == 0) return (dlen + strlen(s)); + while (*s != '\0') { + if (n != 1) { + *d++ = *s; + n--; + } + s++; + } + *d = '\0'; + + return (dlen + (s - src)); /* count does not include NUL */ +} +#endif diff --git a/src/seeder/uint256.h b/src/seeder/uint256.h new file mode 100644 index 000000000..2a907a53b --- /dev/null +++ b/src/seeder/uint256.h @@ -0,0 +1,832 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2011 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file license.txt or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_UINT256_H +#define BITCOIN_UINT256_H + +#include "serialize.h" + +#include +#include +#include + +#if defined(_MSC_VER) || defined(__BORLANDC__) +typedef __int64 int64; +typedef unsigned __int64 uint64; +#else +typedef long long int64; +typedef unsigned long long uint64; +#endif +#if defined(_MSC_VER) && _MSC_VER < 1300 +#define for if (false); else for +#endif + +inline int Testuint256AdHoc(std::vector vArg); + +// We have to keep a separate base class without constructors +// so the compiler will let us use it in a union +template class base_uint { +protected: + enum { WIDTH = BITS / 32 }; + unsigned int pn[WIDTH]; + +public: + bool operator!() const { + for (int i = 0; i < WIDTH; i++) + if (pn[i] != 0) return false; + return true; + } + + const base_uint operator~() const { + base_uint ret; + for (int i = 0; i < WIDTH; i++) + ret.pn[i] = ~pn[i]; + return ret; + } + + const base_uint operator-() const { + base_uint ret; + for (int i = 0; i < WIDTH; i++) + ret.pn[i] = ~pn[i]; + ret++; + return ret; + } + + base_uint &operator=(uint64 b) { + pn[0] = (unsigned int)b; + pn[1] = (unsigned int)(b >> 32); + for (int i = 2; i < WIDTH; i++) + pn[i] = 0; + return *this; + } + + base_uint &operator^=(const base_uint &b) { + for (int i = 0; i < WIDTH; i++) + pn[i] ^= b.pn[i]; + return *this; + } + + base_uint &operator&=(const base_uint &b) { + for (int i = 0; i < WIDTH; i++) + pn[i] &= b.pn[i]; + return *this; + } + + base_uint &operator|=(const base_uint &b) { + for (int i = 0; i < WIDTH; i++) + pn[i] |= b.pn[i]; + return *this; + } + + base_uint &operator^=(uint64 b) { + pn[0] ^= (unsigned int)b; + pn[1] ^= (unsigned int)(b >> 32); + return *this; + } + + base_uint &operator&=(uint64 b) { + pn[0] &= (unsigned int)b; + pn[1] &= (unsigned int)(b >> 32); + return *this; + } + + base_uint &operator|=(uint64 b) { + pn[0] |= (unsigned int)b; + pn[1] |= (unsigned int)(b >> 32); + return *this; + } + + base_uint &operator<<=(unsigned int shift) { + base_uint a(*this); + for (int i = 0; i < WIDTH; i++) + pn[i] = 0; + int k = shift / 32; + shift = shift % 32; + for (int i = 0; i < WIDTH; i++) { + if (i + k + 1 < WIDTH && shift != 0) + pn[i + k + 1] |= (a.pn[i] >> (32 - shift)); + if (i + k < WIDTH) pn[i + k] |= (a.pn[i] << shift); + } + return *this; + } + + base_uint &operator>>=(unsigned int shift) { + base_uint a(*this); + for (int i = 0; i < WIDTH; i++) + pn[i] = 0; + int k = shift / 32; + shift = shift % 32; + for (int i = 0; i < WIDTH; i++) { + if (i - k - 1 >= 0 && shift != 0) + pn[i - k - 1] |= (a.pn[i] << (32 - shift)); + if (i - k >= 0) pn[i - k] |= (a.pn[i] >> shift); + } + return *this; + } + + base_uint &operator+=(const base_uint &b) { + uint64 carry = 0; + for (int i = 0; i < WIDTH; i++) { + uint64 n = carry + pn[i] + b.pn[i]; + pn[i] = n & 0xffffffff; + carry = n >> 32; + } + return *this; + } + + base_uint &operator-=(const base_uint &b) { + *this += -b; + return *this; + } + + base_uint &operator+=(uint64 b64) { + base_uint b; + b = b64; + *this += b; + return *this; + } + + base_uint &operator-=(uint64 b64) { + base_uint b; + b = b64; + *this += -b; + return *this; + } + + base_uint &operator++() { + // prefix operator + int i = 0; + while (++pn[i] == 0 && i < WIDTH - 1) + i++; + return *this; + } + + const base_uint operator++(int) { + // postfix operator + const base_uint ret = *this; + ++(*this); + return ret; + } + + base_uint &operator--() { + // prefix operator + int i = 0; + while (--pn[i] == -1 && i < WIDTH - 1) + i++; + return *this; + } + + const base_uint operator--(int) { + // postfix operator + const base_uint ret = *this; + --(*this); + return ret; + } + + friend inline bool operator<(const base_uint &a, const base_uint &b) { + for (int i = base_uint::WIDTH - 1; i >= 0; i--) { + if (a.pn[i] < b.pn[i]) + return true; + else if (a.pn[i] > b.pn[i]) + return false; + } + return false; + } + + friend inline bool operator<=(const base_uint &a, const base_uint &b) { + for (int i = base_uint::WIDTH - 1; i >= 0; i--) { + if (a.pn[i] < b.pn[i]) + return true; + else if (a.pn[i] > b.pn[i]) + return false; + } + return true; + } + + friend inline bool operator>(const base_uint &a, const base_uint &b) { + for (int i = base_uint::WIDTH - 1; i >= 0; i--) { + if (a.pn[i] > b.pn[i]) + return true; + else if (a.pn[i] < b.pn[i]) + return false; + } + return false; + } + + friend inline bool operator>=(const base_uint &a, const base_uint &b) { + for (int i = base_uint::WIDTH - 1; i >= 0; i--) { + if (a.pn[i] > b.pn[i]) + return true; + else if (a.pn[i] < b.pn[i]) + return false; + } + return true; + } + + friend inline bool operator==(const base_uint &a, const base_uint &b) { + for (int i = 0; i < base_uint::WIDTH; i++) + if (a.pn[i] != b.pn[i]) return false; + return true; + } + + friend inline bool operator==(const base_uint &a, uint64 b) { + if (a.pn[0] != (unsigned int)b) return false; + if (a.pn[1] != (unsigned int)(b >> 32)) return false; + for (int i = 2; i < base_uint::WIDTH; i++) + if (a.pn[i] != 0) return false; + return true; + } + + friend inline bool operator!=(const base_uint &a, const base_uint &b) { + return (!(a == b)); + } + + friend inline bool operator!=(const base_uint &a, uint64 b) { + return (!(a == b)); + } + + std::string GetHex() const { + char psz[sizeof(pn) * 2 + 1]; + for (int i = 0; i < sizeof(pn); i++) + sprintf(psz + i * 2, "%02x", + ((unsigned char *)pn)[sizeof(pn) - i - 1]); + return std::string(psz, psz + sizeof(pn) * 2); + } + + void SetHex(const char *psz) { + for (int i = 0; i < WIDTH; i++) + pn[i] = 0; + + // skip leading spaces + while (isspace(*psz)) + psz++; + + // skip 0x + if (psz[0] == '0' && tolower(psz[1]) == 'x') psz += 2; + + // hex string to uint + static char phexdigit[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, + 0, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + const char *pbegin = psz; + while (phexdigit[(unsigned char)*psz] || *psz == '0') + psz++; + psz--; + unsigned char *p1 = (unsigned char *)pn; + unsigned char *pend = p1 + WIDTH * 4; + while (psz >= pbegin && p1 < pend) { + *p1 = phexdigit[(unsigned char)*psz--]; + if (psz >= pbegin) { + *p1 |= (phexdigit[(unsigned char)*psz--] << 4); + p1++; + } + } + } + + void SetHex(const std::string &str) { SetHex(str.c_str()); } + + std::string ToString() const { return (GetHex()); } + + unsigned char *begin() { return (unsigned char *)&pn[0]; } + + unsigned char *end() { return (unsigned char *)&pn[WIDTH]; } + + unsigned int size() { return sizeof(pn); } + + unsigned int GetSerializeSize(int nType = 0, + int nVersion = PROTOCOL_VERSION) const { + return sizeof(pn); + } + + template + void Serialize(Stream &s, int nType = 0, + int nVersion = PROTOCOL_VERSION) const { + s.write((char *)pn, sizeof(pn)); + } + + template + void Unserialize(Stream &s, int nType = 0, + int nVersion = PROTOCOL_VERSION) { + s.read((char *)pn, sizeof(pn)); + } + + friend class uint160; + friend class uint256; + friend inline int Testuint256AdHoc(std::vector vArg); +}; + +typedef base_uint<160> base_uint160; +typedef base_uint<256> base_uint256; + +// +// uint160 and uint256 could be implemented as templates, but to keep +// compile errors and debugging cleaner, they're copy and pasted. +// + +////////////////////////////////////////////////////////////////////////////// +// +// uint160 +// + +class uint160 : public base_uint160 { +public: + typedef base_uint160 basetype; + + uint160() { + for (int i = 0; i < WIDTH; i++) + pn[i] = 0; + } + + uint160(const basetype &b) { + for (int i = 0; i < WIDTH; i++) + pn[i] = b.pn[i]; + } + + uint160 &operator=(const basetype &b) { + for (int i = 0; i < WIDTH; i++) + pn[i] = b.pn[i]; + return *this; + } + + uint160(uint64 b) { + pn[0] = (unsigned int)b; + pn[1] = (unsigned int)(b >> 32); + for (int i = 2; i < WIDTH; i++) + pn[i] = 0; + } + + uint160 &operator=(uint64 b) { + pn[0] = (unsigned int)b; + pn[1] = (unsigned int)(b >> 32); + for (int i = 2; i < WIDTH; i++) + pn[i] = 0; + return *this; + } + + explicit uint160(const std::string &str) { SetHex(str); } + + explicit uint160(const std::vector &vch) { + if (vch.size() == sizeof(pn)) + memcpy(pn, &vch[0], sizeof(pn)); + else + *this = 0; + } +}; + +inline bool operator==(const uint160 &a, uint64 b) { + return (base_uint160)a == b; +} +inline bool operator!=(const uint160 &a, uint64 b) { + return (base_uint160)a != b; +} +inline const uint160 operator<<(const base_uint160 &a, unsigned int shift) { + return uint160(a) <<= shift; +} +inline const uint160 operator>>(const base_uint160 &a, unsigned int shift) { + return uint160(a) >>= shift; +} +inline const uint160 operator<<(const uint160 &a, unsigned int shift) { + return uint160(a) <<= shift; +} +inline const uint160 operator>>(const uint160 &a, unsigned int shift) { + return uint160(a) >>= shift; +} + +inline const uint160 operator^(const base_uint160 &a, const base_uint160 &b) { + return uint160(a) ^= b; +} +inline const uint160 operator&(const base_uint160 &a, const base_uint160 &b) { + return uint160(a) &= b; +} +inline const uint160 operator|(const base_uint160 &a, const base_uint160 &b) { + return uint160(a) |= b; +} +inline const uint160 operator+(const base_uint160 &a, const base_uint160 &b) { + return uint160(a) += b; +} +inline const uint160 operator-(const base_uint160 &a, const base_uint160 &b) { + return uint160(a) -= b; +} + +inline bool operator<(const base_uint160 &a, const uint160 &b) { + return (base_uint160)a < (base_uint160)b; +} +inline bool operator<=(const base_uint160 &a, const uint160 &b) { + return (base_uint160)a <= (base_uint160)b; +} +inline bool operator>(const base_uint160 &a, const uint160 &b) { + return (base_uint160)a > (base_uint160)b; +} +inline bool operator>=(const base_uint160 &a, const uint160 &b) { + return (base_uint160)a >= (base_uint160)b; +} +inline bool operator==(const base_uint160 &a, const uint160 &b) { + return (base_uint160)a == (base_uint160)b; +} +inline bool operator!=(const base_uint160 &a, const uint160 &b) { + return (base_uint160)a != (base_uint160)b; +} +inline const uint160 operator^(const base_uint160 &a, const uint160 &b) { + return (base_uint160)a ^ (base_uint160)b; +} +inline const uint160 operator&(const base_uint160 &a, const uint160 &b) { + return (base_uint160)a & (base_uint160)b; +} +inline const uint160 operator|(const base_uint160 &a, const uint160 &b) { + return (base_uint160)a | (base_uint160)b; +} +inline const uint160 operator+(const base_uint160 &a, const uint160 &b) { + return (base_uint160)a + (base_uint160)b; +} +inline const uint160 operator-(const base_uint160 &a, const uint160 &b) { + return (base_uint160)a - (base_uint160)b; +} + +inline bool operator<(const uint160 &a, const base_uint160 &b) { + return (base_uint160)a < (base_uint160)b; +} +inline bool operator<=(const uint160 &a, const base_uint160 &b) { + return (base_uint160)a <= (base_uint160)b; +} +inline bool operator>(const uint160 &a, const base_uint160 &b) { + return (base_uint160)a > (base_uint160)b; +} +inline bool operator>=(const uint160 &a, const base_uint160 &b) { + return (base_uint160)a >= (base_uint160)b; +} +inline bool operator==(const uint160 &a, const base_uint160 &b) { + return (base_uint160)a == (base_uint160)b; +} +inline bool operator!=(const uint160 &a, const base_uint160 &b) { + return (base_uint160)a != (base_uint160)b; +} +inline const uint160 operator^(const uint160 &a, const base_uint160 &b) { + return (base_uint160)a ^ (base_uint160)b; +} +inline const uint160 operator&(const uint160 &a, const base_uint160 &b) { + return (base_uint160)a & (base_uint160)b; +} +inline const uint160 operator|(const uint160 &a, const base_uint160 &b) { + return (base_uint160)a | (base_uint160)b; +} +inline const uint160 operator+(const uint160 &a, const base_uint160 &b) { + return (base_uint160)a + (base_uint160)b; +} +inline const uint160 operator-(const uint160 &a, const base_uint160 &b) { + return (base_uint160)a - (base_uint160)b; +} + +inline bool operator<(const uint160 &a, const uint160 &b) { + return (base_uint160)a < (base_uint160)b; +} +inline bool operator<=(const uint160 &a, const uint160 &b) { + return (base_uint160)a <= (base_uint160)b; +} +inline bool operator>(const uint160 &a, const uint160 &b) { + return (base_uint160)a > (base_uint160)b; +} +inline bool operator>=(const uint160 &a, const uint160 &b) { + return (base_uint160)a >= (base_uint160)b; +} +inline bool operator==(const uint160 &a, const uint160 &b) { + return (base_uint160)a == (base_uint160)b; +} +inline bool operator!=(const uint160 &a, const uint160 &b) { + return (base_uint160)a != (base_uint160)b; +} +inline const uint160 operator^(const uint160 &a, const uint160 &b) { + return (base_uint160)a ^ (base_uint160)b; +} +inline const uint160 operator&(const uint160 &a, const uint160 &b) { + return (base_uint160)a & (base_uint160)b; +} +inline const uint160 operator|(const uint160 &a, const uint160 &b) { + return (base_uint160)a | (base_uint160)b; +} +inline const uint160 operator+(const uint160 &a, const uint160 &b) { + return (base_uint160)a + (base_uint160)b; +} +inline const uint160 operator-(const uint160 &a, const uint160 &b) { + return (base_uint160)a - (base_uint160)b; +} + +////////////////////////////////////////////////////////////////////////////// +// +// uint256 +// + +class uint256 : public base_uint256 { +public: + typedef base_uint256 basetype; + + uint256() { + for (int i = 0; i < WIDTH; i++) + pn[i] = 0; + } + + uint256(const basetype &b) { + for (int i = 0; i < WIDTH; i++) + pn[i] = b.pn[i]; + } + + uint256 &operator=(const basetype &b) { + for (int i = 0; i < WIDTH; i++) + pn[i] = b.pn[i]; + return *this; + } + + uint256(uint64 b) { + pn[0] = (unsigned int)b; + pn[1] = (unsigned int)(b >> 32); + for (int i = 2; i < WIDTH; i++) + pn[i] = 0; + } + + uint256 &operator=(uint64 b) { + pn[0] = (unsigned int)b; + pn[1] = (unsigned int)(b >> 32); + for (int i = 2; i < WIDTH; i++) + pn[i] = 0; + return *this; + } + + explicit uint256(const std::string &str) { SetHex(str); } + + explicit uint256(const std::vector &vch) { + if (vch.size() == sizeof(pn)) + memcpy(pn, &vch[0], sizeof(pn)); + else + *this = 0; + } +}; + +inline bool operator==(const uint256 &a, uint64 b) { + return (base_uint256)a == b; +} +inline bool operator!=(const uint256 &a, uint64 b) { + return (base_uint256)a != b; +} +inline const uint256 operator<<(const base_uint256 &a, unsigned int shift) { + return uint256(a) <<= shift; +} +inline const uint256 operator>>(const base_uint256 &a, unsigned int shift) { + return uint256(a) >>= shift; +} +inline const uint256 operator<<(const uint256 &a, unsigned int shift) { + return uint256(a) <<= shift; +} +inline const uint256 operator>>(const uint256 &a, unsigned int shift) { + return uint256(a) >>= shift; +} + +inline const uint256 operator^(const base_uint256 &a, const base_uint256 &b) { + return uint256(a) ^= b; +} +inline const uint256 operator&(const base_uint256 &a, const base_uint256 &b) { + return uint256(a) &= b; +} +inline const uint256 operator|(const base_uint256 &a, const base_uint256 &b) { + return uint256(a) |= b; +} +inline const uint256 operator+(const base_uint256 &a, const base_uint256 &b) { + return uint256(a) += b; +} +inline const uint256 operator-(const base_uint256 &a, const base_uint256 &b) { + return uint256(a) -= b; +} + +inline bool operator<(const base_uint256 &a, const uint256 &b) { + return (base_uint256)a < (base_uint256)b; +} +inline bool operator<=(const base_uint256 &a, const uint256 &b) { + return (base_uint256)a <= (base_uint256)b; +} +inline bool operator>(const base_uint256 &a, const uint256 &b) { + return (base_uint256)a > (base_uint256)b; +} +inline bool operator>=(const base_uint256 &a, const uint256 &b) { + return (base_uint256)a >= (base_uint256)b; +} +inline bool operator==(const base_uint256 &a, const uint256 &b) { + return (base_uint256)a == (base_uint256)b; +} +inline bool operator!=(const base_uint256 &a, const uint256 &b) { + return (base_uint256)a != (base_uint256)b; +} +inline const uint256 operator^(const base_uint256 &a, const uint256 &b) { + return (base_uint256)a ^ (base_uint256)b; +} +inline const uint256 operator&(const base_uint256 &a, const uint256 &b) { + return (base_uint256)a & (base_uint256)b; +} +inline const uint256 operator|(const base_uint256 &a, const uint256 &b) { + return (base_uint256)a | (base_uint256)b; +} +inline const uint256 operator+(const base_uint256 &a, const uint256 &b) { + return (base_uint256)a + (base_uint256)b; +} +inline const uint256 operator-(const base_uint256 &a, const uint256 &b) { + return (base_uint256)a - (base_uint256)b; +} + +inline bool operator<(const uint256 &a, const base_uint256 &b) { + return (base_uint256)a < (base_uint256)b; +} +inline bool operator<=(const uint256 &a, const base_uint256 &b) { + return (base_uint256)a <= (base_uint256)b; +} +inline bool operator>(const uint256 &a, const base_uint256 &b) { + return (base_uint256)a > (base_uint256)b; +} +inline bool operator>=(const uint256 &a, const base_uint256 &b) { + return (base_uint256)a >= (base_uint256)b; +} +inline bool operator==(const uint256 &a, const base_uint256 &b) { + return (base_uint256)a == (base_uint256)b; +} +inline bool operator!=(const uint256 &a, const base_uint256 &b) { + return (base_uint256)a != (base_uint256)b; +} +inline const uint256 operator^(const uint256 &a, const base_uint256 &b) { + return (base_uint256)a ^ (base_uint256)b; +} +inline const uint256 operator&(const uint256 &a, const base_uint256 &b) { + return (base_uint256)a & (base_uint256)b; +} +inline const uint256 operator|(const uint256 &a, const base_uint256 &b) { + return (base_uint256)a | (base_uint256)b; +} +inline const uint256 operator+(const uint256 &a, const base_uint256 &b) { + return (base_uint256)a + (base_uint256)b; +} +inline const uint256 operator-(const uint256 &a, const base_uint256 &b) { + return (base_uint256)a - (base_uint256)b; +} + +inline bool operator<(const uint256 &a, const uint256 &b) { + return (base_uint256)a < (base_uint256)b; +} +inline bool operator<=(const uint256 &a, const uint256 &b) { + return (base_uint256)a <= (base_uint256)b; +} +inline bool operator>(const uint256 &a, const uint256 &b) { + return (base_uint256)a > (base_uint256)b; +} +inline bool operator>=(const uint256 &a, const uint256 &b) { + return (base_uint256)a >= (base_uint256)b; +} +inline bool operator==(const uint256 &a, const uint256 &b) { + return (base_uint256)a == (base_uint256)b; +} +inline bool operator!=(const uint256 &a, const uint256 &b) { + return (base_uint256)a != (base_uint256)b; +} +inline const uint256 operator^(const uint256 &a, const uint256 &b) { + return (base_uint256)a ^ (base_uint256)b; +} +inline const uint256 operator&(const uint256 &a, const uint256 &b) { + return (base_uint256)a & (base_uint256)b; +} +inline const uint256 operator|(const uint256 &a, const uint256 &b) { + return (base_uint256)a | (base_uint256)b; +} +inline const uint256 operator+(const uint256 &a, const uint256 &b) { + return (base_uint256)a + (base_uint256)b; +} +inline const uint256 operator-(const uint256 &a, const uint256 &b) { + return (base_uint256)a - (base_uint256)b; +} + +inline int Testuint256AdHoc(std::vector vArg) { + uint256 g(0); + + printf("%s\n", g.ToString().c_str()); + g--; + printf("g--\n"); + printf("%s\n", g.ToString().c_str()); + g--; + printf("g--\n"); + printf("%s\n", g.ToString().c_str()); + g++; + printf("g++\n"); + printf("%s\n", g.ToString().c_str()); + g++; + printf("g++\n"); + printf("%s\n", g.ToString().c_str()); + g++; + printf("g++\n"); + printf("%s\n", g.ToString().c_str()); + g++; + printf("g++\n"); + printf("%s\n", g.ToString().c_str()); + + uint256 a(7); + printf("a=7\n"); + printf("%s\n", a.ToString().c_str()); + + uint256 b; + printf("b undefined\n"); + printf("%s\n", b.ToString().c_str()); + int c = 3; + + a = c; + a.pn[3] = 15; + printf("%s\n", a.ToString().c_str()); + uint256 k(c); + + a = 5; + a.pn[3] = 15; + printf("%s\n", a.ToString().c_str()); + b = 1; + b <<= 52; + + a |= b; + + a ^= 0x500; + + printf("a %s\n", a.ToString().c_str()); + + a = a | b | (uint256)0x1000; + + printf("a %s\n", a.ToString().c_str()); + printf("b %s\n", b.ToString().c_str()); + + a = 0xfffffffe; + a.pn[4] = 9; + + printf("%s\n", a.ToString().c_str()); + a++; + printf("%s\n", a.ToString().c_str()); + a++; + printf("%s\n", a.ToString().c_str()); + a++; + printf("%s\n", a.ToString().c_str()); + a++; + printf("%s\n", a.ToString().c_str()); + + a--; + printf("%s\n", a.ToString().c_str()); + a--; + printf("%s\n", a.ToString().c_str()); + a--; + printf("%s\n", a.ToString().c_str()); + uint256 d = a--; + printf("%s\n", d.ToString().c_str()); + printf("%s\n", a.ToString().c_str()); + a--; + printf("%s\n", a.ToString().c_str()); + a--; + printf("%s\n", a.ToString().c_str()); + + d = a; + + printf("%s\n", d.ToString().c_str()); + for (int i = uint256::WIDTH - 1; i >= 0; i--) + printf("%08x", d.pn[i]); + printf("\n"); + + uint256 neg = d; + neg = ~neg; + printf("%s\n", neg.ToString().c_str()); + + uint256 e = uint256("0xABCDEF123abcdef12345678909832180000011111111"); + printf("\n"); + printf("%s\n", e.ToString().c_str()); + + printf("\n"); + uint256 x1 = uint256("0xABCDEF123abcdef12345678909832180000011111111"); + uint256 x2; + printf("%s\n", x1.ToString().c_str()); + for (int i = 0; i < 270; i += 4) { + x2 = x1 << i; + printf("%s\n", x2.ToString().c_str()); + } + + printf("\n"); + printf("%s\n", x1.ToString().c_str()); + for (int i = 0; i < 270; i += 4) { + x2 = x1; + x2 >>= i; + printf("%s\n", x2.ToString().c_str()); + } + + for (int i = 0; i < 100; i++) { + uint256 k = (~uint256(0) >> i); + printf("%s\n", k.ToString().c_str()); + } + + for (int i = 0; i < 100; i++) { + uint256 k = (~uint256(0) << i); + printf("%s\n", k.ToString().c_str()); + } + + return (0); +} + +#endif diff --git a/src/seeder/util.cpp b/src/seeder/util.cpp new file mode 100644 index 000000000..9b4f94e82 --- /dev/null +++ b/src/seeder/util.cpp @@ -0,0 +1,205 @@ +#include "util.h" +#include + +using namespace std; + +string vstrprintf(const std::string &format, va_list ap) { + char buffer[50000]; + char *p = buffer; + int limit = sizeof(buffer); + int ret; + loop { + va_list arg_ptr; + va_copy(arg_ptr, ap); + ret = vsnprintf(p, limit, format.c_str(), arg_ptr); + va_end(arg_ptr); + if (ret >= 0 && ret < limit) break; + if (p != buffer) delete[] p; + limit *= 2; + p = new char[limit]; + if (p == NULL) throw std::bad_alloc(); + } + string str(p, p + ret); + if (p != buffer) delete[] p; + return str; +} + +string EncodeBase32(const unsigned char *pch, size_t len) { + static const char *pbase32 = "abcdefghijklmnopqrstuvwxyz234567"; + + string strRet = ""; + strRet.reserve((len + 4) / 5 * 8); + + int mode = 0, left = 0; + const unsigned char *pchEnd = pch + len; + + while (pch < pchEnd) { + int enc = *(pch++); + switch (mode) { + case 0: // we have no bits + strRet += pbase32[enc >> 3]; + left = (enc & 7) << 2; + mode = 1; + break; + + case 1: // we have three bits + strRet += pbase32[left | (enc >> 6)]; + strRet += pbase32[(enc >> 1) & 31]; + left = (enc & 1) << 4; + mode = 2; + break; + + case 2: // we have one bit + strRet += pbase32[left | (enc >> 4)]; + left = (enc & 15) << 1; + mode = 3; + break; + + case 3: // we have four bits + strRet += pbase32[left | (enc >> 7)]; + strRet += pbase32[(enc >> 2) & 31]; + left = (enc & 3) << 3; + mode = 4; + break; + + case 4: // we have two bits + strRet += pbase32[left | (enc >> 5)]; + strRet += pbase32[enc & 31]; + mode = 0; + } + } + + static const int nPadding[5] = {0, 6, 4, 3, 1}; + if (mode) { + strRet += pbase32[left]; + for (int n = 0; n < nPadding[mode]; n++) + strRet += '='; + } + + return strRet; +} + +string EncodeBase32(const string &str) { + return EncodeBase32((const unsigned char *)str.c_str(), str.size()); +} + +vector DecodeBase32(const char *p, bool *pfInvalid) { + static const int decode32_table[256] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, + 30, 31, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1}; + + if (pfInvalid) *pfInvalid = false; + + vector vchRet; + vchRet.reserve((strlen(p)) * 5 / 8); + + int mode = 0; + int left = 0; + + while (1) { + int dec = decode32_table[(unsigned char)*p]; + if (dec == -1) break; + p++; + switch (mode) { + case 0: // we have no bits and get 5 + left = dec; + mode = 1; + break; + + case 1: // we have 5 bits and keep 2 + vchRet.push_back((left << 3) | (dec >> 2)); + left = dec & 3; + mode = 2; + break; + + case 2: // we have 2 bits and keep 7 + left = left << 5 | dec; + mode = 3; + break; + + case 3: // we have 7 bits and keep 4 + vchRet.push_back((left << 1) | (dec >> 4)); + left = dec & 15; + mode = 4; + break; + + case 4: // we have 4 bits, and keep 1 + vchRet.push_back((left << 4) | (dec >> 1)); + left = dec & 1; + mode = 5; + break; + + case 5: // we have 1 bit, and keep 6 + left = left << 5 | dec; + mode = 6; + break; + + case 6: // we have 6 bits, and keep 3 + vchRet.push_back((left << 2) | (dec >> 3)); + left = dec & 7; + mode = 7; + break; + + case 7: // we have 3 bits, and keep 0 + vchRet.push_back((left << 5) | dec); + mode = 0; + break; + } + } + + if (pfInvalid) switch (mode) { + case 0: // 8n base32 characters processed: ok + break; + + case 1: // 8n+1 base32 characters processed: impossible + case 3: // +3 + case 6: // +6 + *pfInvalid = true; + break; + + case 2: // 8n+2 base32 characters processed: require '======' + if (left || p[0] != '=' || p[1] != '=' || p[2] != '=' || + p[3] != '=' || p[4] != '=' || p[5] != '=' || + decode32_table[(unsigned char)p[6]] != -1) + *pfInvalid = true; + break; + + case 4: // 8n+4 base32 characters processed: require '====' + if (left || p[0] != '=' || p[1] != '=' || p[2] != '=' || + p[3] != '=' || decode32_table[(unsigned char)p[4]] != -1) + *pfInvalid = true; + break; + + case 5: // 8n+5 base32 characters processed: require '===' + if (left || p[0] != '=' || p[1] != '=' || p[2] != '=' || + decode32_table[(unsigned char)p[3]] != -1) + *pfInvalid = true; + break; + + case 7: // 8n+7 base32 characters processed: require '=' + if (left || p[0] != '=' || + decode32_table[(unsigned char)p[1]] != -1) + *pfInvalid = true; + break; + } + + return vchRet; +} + +string DecodeBase32(const string &str) { + vector vchRet = DecodeBase32(str.c_str()); + return string((const char *)&vchRet[0], vchRet.size()); +} diff --git a/src/seeder/util.h b/src/seeder/util.h new file mode 100644 index 000000000..e565ccf56 --- /dev/null +++ b/src/seeder/util.h @@ -0,0 +1,108 @@ +#ifndef _UTIL_H_ +#define _UTIL_H_ 1 + +#include +#include +#include +#include + +#include "uint256.h" + +#define loop for (;;) +#define BEGIN(a) ((char *)&(a)) +#define END(a) ((char *)&((&(a))[1])) +#define UBEGIN(a) ((unsigned char *)&(a)) +#define UEND(a) ((unsigned char *)&((&(a))[1])) +#define ARRAYLEN(array) (sizeof(array) / sizeof((array)[0])) + +#define WSAGetLastError() errno +#define WSAEINVAL EINVAL +#define WSAEALREADY EALREADY +#define WSAEWOULDBLOCK EWOULDBLOCK +#define WSAEMSGSIZE EMSGSIZE +#define WSAEINTR EINTR +#define WSAEINPROGRESS EINPROGRESS +#define WSAEADDRINUSE EADDRINUSE +#define WSAENOTSOCK EBADF +#define INVALID_SOCKET (SOCKET)(~0) +#define SOCKET_ERROR -1 + +// Wrapper to automatically initialize mutex +class CCriticalSection { +protected: + pthread_rwlock_t mutex; + +public: + explicit CCriticalSection() { pthread_rwlock_init(&mutex, NULL); } + ~CCriticalSection() { pthread_rwlock_destroy(&mutex); } + void Enter(bool fShared = false) { + if (fShared) { + pthread_rwlock_rdlock(&mutex); + } else { + pthread_rwlock_wrlock(&mutex); + } + } + void Leave() { pthread_rwlock_unlock(&mutex); } +}; + +// Automatically leave critical section when leaving block, needed for exception +// safety +class CCriticalBlock { +protected: + CCriticalSection *pcs; + +public: + CCriticalBlock(CCriticalSection &cs, bool fShared = false) : pcs(&cs) { + pcs->Enter(fShared); + } + operator bool() const { return true; } + ~CCriticalBlock() { pcs->Leave(); } +}; + +#define CRITICAL_BLOCK(cs) \ + if (CCriticalBlock criticalblock = CCriticalBlock(cs)) + +#define SHARED_CRITICAL_BLOCK(cs) \ + if (CCriticalBlock criticalblock = CCriticalBlock(cs, true)) + +template inline uint256 Hash(const T1 pbegin, const T1 pend) { + static unsigned char pblank[1]; + uint256 hash1; + SHA256((pbegin == pend ? pblank : (unsigned char *)&pbegin[0]), + (pend - pbegin) * sizeof(pbegin[0]), (unsigned char *)&hash1); + uint256 hash2; + SHA256((unsigned char *)&hash1, sizeof(hash1), (unsigned char *)&hash2); + return hash2; +} + +void static inline Sleep(int nMilliSec) { + struct timespec wa; + wa.tv_sec = nMilliSec / 1000; + wa.tv_nsec = (nMilliSec % 1000) * 1000000; + nanosleep(&wa, NULL); +} + +std::string vstrprintf(const std::string &format, va_list ap); + +std::string static inline strprintf(const std::string &format, ...) { + va_list arg_ptr; + va_start(arg_ptr, format); + std::string ret = vstrprintf(format, arg_ptr); + va_end(arg_ptr); + return ret; +} + +bool static inline error(std::string err, ...) { + return false; +} + +bool static inline my_printf(std::string err, ...) { + return true; +} + +std::vector DecodeBase32(const char *p, bool *pfInvalid = NULL); +std::string DecodeBase32(const std::string &str); +std::string EncodeBase32(const unsigned char *pch, size_t len); +std::string EncodeBase32(const std::string &str); + +#endif