diff --git a/src/init.cpp b/src/init.cpp --- a/src/init.cpp +++ b/src/init.cpp @@ -1030,6 +1030,10 @@ argsman.AddArg("-uacomment=", "Append comment to the user agent string", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); + argsman.AddArg( + "-useragent=", + "Override user agent name to a specific string (overrides -uacomment)", + ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); SetupChainParamsBaseOptions(argsman); @@ -2283,6 +2287,19 @@ chainman, *node.mempool)); RegisterValidationInterface(node.peerman.get()); + if (args.IsArgSet("-useragent")) { + const std::string ua = args.GetArg("-useragent", ""); + if (ua != SanitizeString(ua, SAFE_CHARS_DEFAULT)) { + return InitError(strprintf( + _("User Agent (%s) contains invalid characters."), ua)); + } + if (ua.size() > MAX_SUBVERSION_LENGTH) { + return InitError(strprintf( + _("Length of explicitly set User Agent (%i) exceeds maximum " + "length (%i). Reduce the size of -useragent."), + ua.size(), MAX_SUBVERSION_LENGTH)); + } + } // sanitize comments per BIP-0014, format user agent and check total size std::vector uacomments; for (const std::string &cmt : args.GetArgs("-uacomment")) { diff --git a/src/net.cpp b/src/net.cpp --- a/src/net.cpp +++ b/src/net.cpp @@ -3143,6 +3143,9 @@ } std::string userAgent(const Config &config) { + if (gArgs.IsArgSet("-useragent")) { + return gArgs.GetArg("-useragent", ""); + } // format excessive blocksize value std::string eb = getSubVersionEB(config.GetMaxBlockSize()); std::vector uacomments; diff --git a/test/functional/feature_useragent.py b/test/functional/feature_useragent.py new file mode 100755 --- /dev/null +++ b/test/functional/feature_useragent.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 +# Copyright (c) 2021 The Bitcoin developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test the -uaclient option.""" + +import re + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.test_node import ErrorMatch +from test_framework.util import assert_equal + + +class UseragentTest(BitcoinTestFramework): + def set_test_params(self): + self.num_nodes = 1 + self.setup_clean_chain = True + + def run_test(self): + self.log.info("test -useragent") + default_useragent = self.nodes[0].getnetworkinfo()["subversion"] + expected = "/Bitcoin ABC:" + assert_equal(default_useragent[:len(expected)], expected) + + self.restart_node(0, ["-useragent=/Foo:2000/"]) + foo_ua = self.nodes[0].getnetworkinfo()["subversion"] + assert_equal(foo_ua, "/Foo:2000/") + + self.log.info("test -useragent overrides -uacomment") + self.restart_node(0, ["-useragent=/Bar:3000/", "-uacomment=eggs"]) + bar_ua = self.nodes[0].getnetworkinfo()["subversion"] + assert_equal(bar_ua, "/Bar:3000/") + + self.log.info("test -useragent max length") + self.stop_node(0) + expected = r"Error: Length of explicitly set User Agent \([0-9]+\) exceeds maximum length \([0-9]+\)\. Reduce the size of -useragent\." + self.nodes[0].assert_start_raises_init_error( + ["-useragent=" + 'a' * 257], expected, match=ErrorMatch.FULL_REGEX) + + self.log.info("test -uacomment invalid characters") + for invalid_char in ['*', '!', '₿', '🏃']: + expected = r"Error: User Agent \(" + \ + re.escape(invalid_char) + r"\) contains invalid characters\." + self.nodes[0].assert_start_raises_init_error( + ["-useragent=" + invalid_char], expected, match=ErrorMatch.FULL_REGEX) + + +if __name__ == '__main__': + UseragentTest().main()