diff --git a/tools/dhtchat.cpp b/tools/dhtchat.cpp
index bbf27a5473a1f28e0c081433205bc6faa6541695..27c6e30384def75e036713311a9d12dba38f60af 100644
--- a/tools/dhtchat.cpp
+++ b/tools/dhtchat.cpp
@@ -57,8 +57,8 @@ main(int argc, char **argv)
     DhtRunner dht;
     dht.run(params.port, dht::crypto::generateIdentity("DHT Chat Node"), true);
 
-    if (not params.bootstrap.empty())
-        dht.bootstrap(params.bootstrap[0].c_str(), params.bootstrap[1].c_str());
+    if (not params.bootstrap.first.empty())
+        dht.bootstrap(params.bootstrap.first.c_str(), params.bootstrap.second.c_str());
 
     std::cout << "OpenDht node " << dht.getNodeId() << " running on port " <<  params.port << std::endl;
     std::cout << "Public key ID " << dht.getId() << std::endl;
diff --git a/tools/dhtnode.cpp b/tools/dhtnode.cpp
index 62caf441c2d14d1007d874242a1c09faa3d1b0fd..94903500dee91e567dba8808cc48843594f3c89f 100644
--- a/tools/dhtnode.cpp
+++ b/tools/dhtnode.cpp
@@ -83,9 +83,9 @@ main(int argc, char **argv)
         if (params.log)
             enableLogging(dht);
 
-        if (not params.bootstrap.empty()) {
-            std::cout << "Bootstrap: " << params.bootstrap[0] << ":" << params.bootstrap[1] << std::endl;
-            dht.bootstrap(params.bootstrap[0].c_str(), params.bootstrap[1].c_str());
+        if (not params.bootstrap.first.empty()) {
+            std::cout << "Bootstrap: " << params.bootstrap.first << ":" << params.bootstrap.second << std::endl;
+            dht.bootstrap(params.bootstrap.first.c_str(), params.bootstrap.second.c_str());
         }
 
         print_node_info(dht, params);
diff --git a/tools/dhtscanner.cpp b/tools/dhtscanner.cpp
index ee572bd00cbe3066a97bde7cbb4add4eb0a52024..f2c6ecd47dff077b7755d4a69eab40b1b3d52cd2 100644
--- a/tools/dhtscanner.cpp
+++ b/tools/dhtscanner.cpp
@@ -90,8 +90,8 @@ main(int argc, char **argv)
     DhtRunner dht;
     dht.run(params.port, crt_tmp, true, [](dht::Dht::Status /* ipv4 */, dht::Dht::Status /* ipv6 */) {});
 
-    if (not params.bootstrap.empty())
-        dht.bootstrap(params.bootstrap[0].c_str(), params.bootstrap[1].c_str());
+    if (not params.bootstrap.first.empty())
+        dht.bootstrap(params.bootstrap.first.c_str(), params.bootstrap.second.c_str());
 
     std::cout << "OpenDht node " << dht.getNodeId() << " running on port " <<  params.port << std::endl;
     std::cout << "Scanning network..." << std::endl;
diff --git a/tools/tools_common.h b/tools/tools_common.h
index e107188a918764cf509101bac4916c4547891684..4c7cc750180bc0ec598a52d751171b77693b307a 100644
--- a/tools/tools_common.h
+++ b/tools/tools_common.h
@@ -112,16 +112,26 @@ print_dt(DT d) {
 }
 
 /**
- * Split string with delimiter
+ * Split "[host]:port" or "host:port" to pair<"host", "port">.
  */
-std::vector<std::string>
-split(const std::string& s, char delim) {
-    std::vector<std::string> elems;
-    std::stringstream ss(s);
-    std::string item;
-    while (std::getline(ss, item, delim))
-        elems.emplace_back(std::move(item));
-    return elems;
+std::pair<std::string, std::string>
+splitPort(const std::string& s) {
+    if (s.empty())
+        return {};
+    if (s[0] == '[') {
+        std::size_t closure = s.find_first_of(']');
+        std::size_t found = s.find_last_of(':');
+        if (closure == std::string::npos)
+            return {s, ""};
+        if (found == std::string::npos or found < closure)
+            return {s.substr(1,closure-1), ""};
+        return {s.substr(1,closure-1), s.substr(found+1)};
+    }
+    std::size_t found = s.find_last_of(':');
+    std::size_t first = s.find_first_of(':');
+    if (found == std::string::npos or found != first)
+        return {s, ""};
+    return {s.substr(0,found), s.substr(found+1)};
 }
 
 static const constexpr in_port_t DHT_DEFAULT_PORT = 4222;
@@ -132,7 +142,7 @@ struct dht_params {
     in_port_t port {DHT_DEFAULT_PORT};
     bool is_bootstrap_node {false};
     bool generate_identity {false};
-    std::vector<std::string> bootstrap {};
+    std::pair<std::string, std::string> bootstrap {};
 };
 
 static const struct option long_options[] = {
@@ -160,9 +170,9 @@ parseArgs(int argc, char **argv) {
             break;
         case 'b':
             if (optarg) {
-                params.bootstrap = split((optarg[0] == '=') ? optarg+1 : optarg, ':');
-                if (params.bootstrap.size() == 1)
-                    params.bootstrap.emplace_back(std::to_string(DHT_DEFAULT_PORT));
+                params.bootstrap = splitPort((optarg[0] == '=') ? optarg+1 : optarg);
+                if (not params.bootstrap.first.empty() and params.bootstrap.second.empty())
+                    params.bootstrap.second = std::to_string(DHT_DEFAULT_PORT);
             }
             else
                 params.is_bootstrap_node = true;