diff --git a/CMakeLists.txt b/CMakeLists.txt
index 82a3ab65d7f6e68d77ce385b436e5958f97d5732..9f15373a47b16e060ea87ddc1cf8c89a99997170 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -64,6 +64,7 @@ list (APPEND opendht_SOURCES
 
 list (APPEND opendht_HEADERS
     include/opendht/utils.h
+    include/opendht/sockaddr.h
     include/opendht/rng.h
     include/opendht/crypto.h
     include/opendht/infohash.h
diff --git a/include/opendht/dht.h b/include/opendht/dht.h
index 6fe39bae04714e2121443d3762e4b3157d9a6882..446e91140fe1f50daf48b430a23cf4d12d7ebd54 100644
--- a/include/opendht/dht.h
+++ b/include/opendht/dht.h
@@ -113,14 +113,20 @@ public:
      * The node is not pinged, so this should be
      * used to bootstrap efficiently from previously known nodes.
      */
-    void insertNode(const InfoHash& id, const sockaddr*, socklen_t);
+    void insertNode(const InfoHash& id, const SockAddr&);
+    void insertNode(const InfoHash& id, const sockaddr* sa, socklen_t salen) {
+        insertNode(id, SockAddr(sa, salen));
+    }
     void insertNode(const NodeExport& n) {
-        insertNode(n.id, reinterpret_cast<const sockaddr*>(&n.ss), n.sslen);
+        insertNode(n.id, SockAddr(n.ss, n.sslen));
     }
 
     void pingNode(const sockaddr*, socklen_t);
 
-    time_point periodic(const uint8_t *buf, size_t buflen, const sockaddr *from, socklen_t fromlen);
+    time_point periodic(const uint8_t *buf, size_t buflen, const SockAddr&);
+    time_point periodic(const uint8_t *buf, size_t buflen, const sockaddr* from, socklen_t fromlen) {
+        return periodic(buf, buflen, SockAddr(from, fromlen));
+    }
 
     /**
      * Get a value by searching on all available protocols (IPv4, IPv6),
@@ -276,7 +282,7 @@ public:
         return {total_store_size, total_values};
     }
 
-    std::vector<Address> getPublicAddress(sa_family_t family = 0);
+    std::vector<SockAddr> getPublicAddress(sa_family_t family = 0);
 
 protected:
     Logger DHT_LOG;
@@ -371,7 +377,7 @@ private:
     unsigned pending_pings4 {0};
     unsigned pending_pings6 {0};
 
-    using ReportedAddr = std::pair<unsigned, Address>;
+    using ReportedAddr = std::pair<unsigned, SockAddr>;
     std::vector<ReportedAddr> reported_addr;
 
     void rotateSecrets();
@@ -379,7 +385,7 @@ private:
     Blob makeToken(const sockaddr *sa, bool old) const;
     bool tokenMatch(const Blob& token, const sockaddr *sa) const;
 
-    void reportedAddr(const sockaddr *sa, socklen_t sa_len);
+    void reportedAddr(const SockAddr&);
 
     // Storage
     decltype(Dht::store)::iterator findStorage(const InfoHash& id);
@@ -511,11 +517,11 @@ private:
 
     bool neighbourhoodMaintenance(RoutingTable&);
 
-    void processMessage(const uint8_t *buf, size_t buflen, const sockaddr *from, socklen_t fromlen);
+    void processMessage(const uint8_t *buf, size_t buflen, const SockAddr&);
 
     void onError(std::shared_ptr<Request> node, DhtProtocolException e);
     /* when our address is reported by a distant peer. */
-    void onReportedAddr(const InfoHash& id, sockaddr* sa , socklen_t salen);
+    void onReportedAddr(const InfoHash& id, const SockAddr&);
     /* when we receive a ping request */
     NetworkEngine::RequestAnswer onPing(std::shared_ptr<Node> node);
     /* when we receive a "find node" request */
diff --git a/include/opendht/dhtrunner.h b/include/opendht/dhtrunner.h
index 0dd3ad26c5fcb35b4ab8abedb802b2494bc98034..42fd7f7153562ea80258e27694ed2bb917e967ed 100644
--- a/include/opendht/dhtrunner.h
+++ b/include/opendht/dhtrunner.h
@@ -24,6 +24,7 @@
 #include "infohash.h"
 #include "value.h"
 #include "callbacks.h"
+#include "sockaddr.h"
 
 #include <thread>
 #include <mutex>
@@ -217,7 +218,7 @@ public:
      * Returns the currently bound address.
      * @param f: address family of the bound address to retreive.
      */
-    const Address& getBound(sa_family_t f = AF_INET) const {
+    const SockAddr& getBound(sa_family_t f = AF_INET) const {
         return (f == AF_INET) ? bound4 : bound6;
     }
 
@@ -253,7 +254,7 @@ public:
     std::string getStorageLog() const;
     std::string getRoutingTablesLog(sa_family_t af) const;
     std::string getSearchesLog(sa_family_t af = 0) const;
-    std::vector<Address> getPublicAddress(sa_family_t af = 0);
+    std::vector<SockAddr> getPublicAddress(sa_family_t af = 0);
     std::vector<std::string> getPublicAddressStr(sa_family_t af = 0);
 
     // securedht methods
@@ -347,7 +348,7 @@ private:
 
     std::thread rcv_thread {};
     std::mutex sock_mtx {};
-    std::vector<std::pair<Blob, std::pair<sockaddr_storage, socklen_t>>> rcv {};
+    std::vector<std::pair<Blob, SockAddr>> rcv {};
 
     std::queue<std::function<void(SecureDht&)>> pending_ops_prio {};
     std::queue<std::function<void(SecureDht&)>> pending_ops {};
@@ -359,8 +360,8 @@ private:
                status6 {NodeStatus::Disconnected};
     StatusCallback statusCb {nullptr};
 
-    Address bound4 {};
-    Address bound6 {};
+    SockAddr bound4 {};
+    SockAddr bound6 {};
 };
 
 }
diff --git a/include/opendht/network_engine.h b/include/opendht/network_engine.h
index 184c450818c1f8b52c2ff5de5988e71b4745798c..388401827cdf16ffe904ed69020704ea777e0c4d 100644
--- a/include/opendht/network_engine.h
+++ b/include/opendht/network_engine.h
@@ -196,7 +196,7 @@ private:
      * @param saddr (type: sockaddr*) sockaddr* pointer containing address ip information.
      * @param saddr_len (type: socklen_t) lenght of the sockaddr struct.
      */
-    std::function<void(const InfoHash&, sockaddr*, socklen_t)> onReportedAddr;
+    std::function<void(const InfoHash&, const SockAddr&)> onReportedAddr;
     /**
      * @brief on ping request callback.
      *
@@ -347,10 +347,10 @@ public:
      * @param fromlen  The length of the corresponding sockaddr structure.
      * @param now  The time to adjust the clock in the network engine.
      */
-    void processMessage(const uint8_t *buf, size_t buflen, const sockaddr* from, socklen_t fromlen);
+    void processMessage(const uint8_t *buf, size_t buflen, const SockAddr& addr);
 
-    std::shared_ptr<Node> insertNode(const InfoHash& myid, const sockaddr* from, socklen_t fromlen) {
-        auto n = cache.getNode(myid, from, fromlen, scheduler.time(), 0);
+    std::shared_ptr<Node> insertNode(const InfoHash& myid, const SockAddr& addr) {
+        auto n = cache.getNode(myid, addr, scheduler.time(), 0);
         onNewNode(n, 0);
         return n;
     }
@@ -386,21 +386,10 @@ private:
     static const std::string my_v;
 
 
-    /* DHT info */
-    const InfoHash& myid;
-    const NetId network {0};
-    const int dht_socket {-1};
-    const int dht_socket6 {-1};
-    const Logger& DHT_LOG;
-
-    NodeCache cache {};
-    sockaddr_storage blacklist[BLACKLISTED_MAX] {};
-    unsigned next_blacklisted = 0;
-
     bool rateLimit();
 
-    static bool isMartian(const sockaddr* sa, socklen_t len);
-    bool isNodeBlacklisted(const sockaddr*, socklen_t) const;
+    static bool isMartian(const SockAddr& addr);
+    bool isNodeBlacklisted(const SockAddr& addr) const;
 
     void requestStep(std::shared_ptr<Request> req);
 
@@ -430,16 +419,15 @@ private:
 
 
     // basic wrapper for socket sendto function
-    int send(const char *buf, size_t len, int flags, const sockaddr *sa, socklen_t salen);
+    int send(const char *buf, size_t len, int flags, const SockAddr& addr);
 
     /*************
      *  Answers  *
      *************/
     /* answer to a ping  request */
-    void sendPong(const sockaddr* sa, socklen_t salen, TransId tid);
+    void sendPong(const SockAddr& addr, TransId tid);
     /* answer to findnodes/getvalues request */
-    void sendNodesValues(const sockaddr* sa,
-            socklen_t salen,
+    void sendNodesValues(const SockAddr& addr,
             TransId tid,
             const Blob& nodes,
             const Blob& nodes6,
@@ -454,12 +442,11 @@ private:
             std::vector<std::shared_ptr<Node>>& nodes,
             std::vector<std::shared_ptr<Node>>& nodes6);
     /* answer to a listen request */
-    void sendListenConfirmation(const sockaddr* sa, socklen_t salen, TransId tid);
+    void sendListenConfirmation(const SockAddr& addr, TransId tid);
     /* answer to put request */
-    void sendValueAnnounced(const sockaddr* sa, socklen_t salen, TransId, Value::Id);
+    void sendValueAnnounced(const SockAddr& addr, TransId, Value::Id);
     /* answer in case of error */
-    void sendError(const sockaddr* sa,
-            socklen_t salen,
+    void sendError(const SockAddr& addr,
             TransId tid,
             uint16_t code,
             const std::string& message,
@@ -467,6 +454,14 @@ private:
 
     void deserializeNodes(ParsedMessage& msg);
 
+    /* DHT info */
+    const InfoHash& myid;
+    const NetId network {0};
+    const int dht_socket {-1};
+    const int dht_socket6 {-1};
+    const Logger& DHT_LOG;
+
+    NodeCache cache {};
     std::queue<time_point> rate_limit_time {};
     static std::mt19937 rd_device;
 
@@ -474,6 +469,7 @@ private:
     uint16_t transaction_id {1};
     std::map<uint16_t, std::shared_ptr<Request>> requests {};
     MessageStats in_stats {}, out_stats {};
+    std::set<SockAddr> blacklist {};
 
     Scheduler& scheduler;
 };
diff --git a/include/opendht/node.h b/include/opendht/node.h
index 8760e7336104e99a0cdc451abb95e2e9a7e24a63..5ecb9a61e1e4853e2e01605840a26a409f79db99 100644
--- a/include/opendht/node.h
+++ b/include/opendht/node.h
@@ -22,6 +22,7 @@
 
 #include "infohash.h" // includes socket structures
 #include "utils.h"
+#include "sockaddr.h"
 
 #include <list>
 
@@ -30,40 +31,34 @@ namespace dht {
 struct Request;
 
 struct Node {
-    friend class NetworkEngine;
-
     InfoHash id;
-
-    socklen_t sslen {0};
-    sockaddr_storage ss;
+    SockAddr addr;
 
     time_point time {time_point::min()};            /* last time eared about */
     time_point reply_time {time_point::min()};      /* time of last correct reply received */
 
     Node(const InfoHash& id, const sockaddr* sa, socklen_t salen)
-        : id(id), sslen(salen), ss() {
-        std::copy_n((const uint8_t*)sa, salen, (uint8_t*)&ss);
-        if ((unsigned)salen < sizeof(ss))
-            std::fill_n((uint8_t*)&ss+salen, sizeof(ss)-salen, 0);
-    }
+        : id(id), addr(sa, salen) {}
+    Node(const InfoHash& id, const SockAddr& addr) : id(id), addr(addr) {}
+
     InfoHash getId() const {
         return id;
     }
     std::pair<const sockaddr*, socklen_t> getAddr() const {
-        return {(const sockaddr*)&ss, sslen};
+        return {(const sockaddr*)&addr.first, addr.second};
     }
     std::string getAddrStr() const {
-        return print_addr(ss, sslen);
+        return addr.toString();
     }
     bool isExpired() const { return expired_; }
     bool isGood(time_point now) const;
     bool isPendingMessage() const;
     size_t getPendingMessageCount() const;
 
-    NodeExport exportNode() const { return NodeExport {id, ss, sslen}; }
-    sa_family_t getFamily() const { return ss.ss_family; }
+    NodeExport exportNode() const { return NodeExport {id, addr.first, addr.second}; }
+    sa_family_t getFamily() const { return addr.getFamily(); }
 
-    void update(const sockaddr* sa, socklen_t salen);
+    void update(const SockAddr&);
 
     void requested(std::shared_ptr<Request>& req);
     void received(time_point now, std::shared_ptr<Request> req);
@@ -88,7 +83,6 @@ struct Node {
     static constexpr const std::chrono::seconds MAX_RESPONSE_TIME {1};
 
 private:
-
     std::list<std::weak_ptr<Request>> requests_ {};
     bool expired_ {false};
 
@@ -97,7 +91,6 @@ private:
             return w.expired();
         });
     }
-
 };
 
 }
diff --git a/include/opendht/node_cache.h b/include/opendht/node_cache.h
index 119f7d6091c212b25e67ee327da038b618979d3f..5fa582409aca46630a2531c2a9798e43f83df05b 100644
--- a/include/opendht/node_cache.h
+++ b/include/opendht/node_cache.h
@@ -28,7 +28,7 @@ namespace dht {
 
 struct NodeCache {
     std::shared_ptr<Node> getNode(const InfoHash& id, sa_family_t family);
-    std::shared_ptr<Node> getNode(const InfoHash& id, const sockaddr* sa, socklen_t sa_len, time_point now, bool confirmed);
+    std::shared_ptr<Node> getNode(const InfoHash& id, const SockAddr&, time_point now, bool confirmed);
     std::vector<std::shared_ptr<Node>> getCachedNodes(const InfoHash& id, sa_family_t sa_f, size_t count);
 
     /**
@@ -42,7 +42,7 @@ private:
     class NodeMap : public std::map<InfoHash, std::weak_ptr<Node>> {
     public:
         std::shared_ptr<Node> getNode(const InfoHash& id);
-        std::shared_ptr<Node> getNode(const InfoHash& id, const sockaddr* sa, socklen_t sa_len, time_point now, bool confirmed);
+        std::shared_ptr<Node> getNode(const InfoHash& id, const SockAddr&, time_point now, bool confirmed);
         void clearBadNodes();
     };
 
diff --git a/include/opendht/sockaddr.h b/include/opendht/sockaddr.h
new file mode 100644
index 0000000000000000000000000000000000000000..110b224ae214c995a0d7ae5d5e440556cd5c14d5
--- /dev/null
+++ b/include/opendht/sockaddr.h
@@ -0,0 +1,71 @@
+/*
+ *  Copyright (C) 2016 Savoir-faire Linux Inc.
+ *  Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#ifndef _WIN32
+#include <sys/socket.h>
+#else
+#include <ws2def.h>
+#include <ws2tcpip.h>
+#endif
+
+namespace dht {
+
+std::string print_addr(const sockaddr* sa, socklen_t slen);
+std::string print_addr(const sockaddr_storage& ss, socklen_t sslen);
+
+struct SockAddr : public std::pair<sockaddr_storage, socklen_t> {
+public:
+    using std::pair<sockaddr_storage, socklen_t>::pair;
+
+    SockAddr() : pair<sockaddr_storage, socklen_t>::pair({},0) {}
+    SockAddr(const SockAddr& o) : pair<sockaddr_storage, socklen_t>::pair({},o.second) {
+        std::copy_n((uint8_t*)&o.first, o.second, (uint8_t*)&first);
+    }
+    SockAddr(const sockaddr* sa, socklen_t len) : pair<sockaddr_storage, socklen_t>::pair({},len) {
+        if (len > sizeof(sockaddr_storage))
+            throw std::runtime_error("Socket address length is too large");
+        std::copy_n((uint8_t*)sa, len, (uint8_t*)&first);
+    }
+
+    bool operator<(const SockAddr& o) const {
+        if (second != o.second)
+            return second < o.second;
+        return std::memcmp((uint8_t*)&first, (uint8_t*)&o.first, second) < 0;
+    }
+
+    bool operator==(const SockAddr& o) const {
+        return second == o.second
+            && std::memcmp((uint8_t*)&first, (uint8_t*)&o.first, second) == 0;
+    }
+    SockAddr& operator=(const SockAddr& o) {
+        std::copy_n((const uint8_t*)&o.first, o.second, (uint8_t*)&first);
+        second = o.second;
+        return *this;
+    }
+
+    std::string toString() const {
+        return print_addr(first, second);
+    }
+    sa_family_t getFamily() const { return second > sizeof(sa_family_t) ? first.ss_family : AF_UNSPEC; }
+};
+
+std::string printAddr(const SockAddr& addr);
+
+}
diff --git a/include/opendht/utils.h b/include/opendht/utils.h
index 9dcb1660be57ba763beedfa52b879935ec8267d6..8d5e1c7cb92a71f35cef87e8d5d548fc9f0e9c4e 100644
--- a/include/opendht/utils.h
+++ b/include/opendht/utils.h
@@ -13,8 +13,7 @@
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ *  along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
 #pragma once
@@ -24,13 +23,6 @@
 
 #include <msgpack.hpp>
 
-#ifndef _WIN32
-#include <sys/socket.h>
-#else
-#include <ws2def.h>
-#include <ws2tcpip.h>
-#endif
-
 #include <chrono>
 #include <random>
 #include <functional>
@@ -40,14 +32,9 @@
 
 namespace dht {
 
-using Address = std::pair<sockaddr_storage, socklen_t>;
 using NetId = uint32_t;
 using want_t = int_fast8_t;
 
-std::string print_addr(const sockaddr* sa, socklen_t slen);
-std::string print_addr(const sockaddr_storage& ss, socklen_t sslen);
-std::string printAddr(const Address& addr);
-
 template <typename Key, typename Item, typename Condition>
 void erase_if(std::map<Key, Item>& map, const Condition& condition)
 {
diff --git a/src/Makefile.am b/src/Makefile.am
index ba0af9bad947198147e3ddda4c2ad8265a312fe4..e1cc7556121971261de0dd43c53ec8561b0b0430 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -36,6 +36,7 @@ nobase_include_HEADERS = \
         ../include/opendht/network_engine.h \
         ../include/opendht/scheduler.h \
         ../include/opendht/utils.h \
+        ../include/opendht/sockaddr.h \
         ../include/opendht/infohash.h \
         ../include/opendht/node.h \
         ../include/opendht/value.h \
diff --git a/src/dht.cpp b/src/dht.cpp
index bc50a54d7da3603aef9c06bfb751363fb82ff127..196c5e6436d82c98d014e00420e7476de994f77a 100644
--- a/src/dht.cpp
+++ b/src/dht.cpp
@@ -700,13 +700,13 @@ Dht::sendCachedPing(Bucket& b)
     return 0;
 }
 
-std::vector<Address>
+std::vector<SockAddr>
 Dht::getPublicAddress(sa_family_t family)
 {
     std::sort(reported_addr.begin(), reported_addr.end(), [](const ReportedAddr& a, const ReportedAddr& b) {
         return a.first > b.first;
     });
-    std::vector<Address> ret;
+    std::vector<SockAddr> ret;
     for (const auto& addr : reported_addr)
         if (!family || family == addr.second.first.ss_family)
             ret.emplace_back(addr.second);
@@ -733,15 +733,14 @@ Dht::trySearchInsert(const std::shared_ptr<Node>& node)
 }
 
 void
-Dht::reportedAddr(const sockaddr *sa, socklen_t sa_len)
+Dht::reportedAddr(const SockAddr& addr)
 {
-    auto it = std::find_if(reported_addr.begin(), reported_addr.end(), [=](const ReportedAddr& addr){
-        return (addr.second.second == sa_len) &&
-            std::equal((uint8_t*)&addr.second.first, (uint8_t*)&addr.second.first + addr.second.second, (uint8_t*)sa);
+    auto it = std::find_if(reported_addr.begin(), reported_addr.end(), [&](const ReportedAddr& a){
+        return a.second == addr;
     });
     if (it == reported_addr.end()) {
         if (reported_addr.size() < 32)
-            reported_addr.emplace_back(1, std::make_pair(*((sockaddr_storage*)sa), sa_len));
+            reported_addr.emplace_back(1, addr);
     } else
         it->first++;
 }
@@ -2166,7 +2165,7 @@ Dht::storageChanged(Storage& st, ValueStorage& v)
         DHT_LOG.DEBUG("[Storage %s] Sending update to %s.", st.id.toString().c_str(), l.first->toString().c_str());
         std::vector<std::shared_ptr<Value>> vals {};
         vals.push_back(v.data);
-        Blob ntoken = makeToken((const sockaddr*)&l.first->ss, false);
+        Blob ntoken = makeToken((const sockaddr*)&l.first->addr.first, false);
         network_engine.tellListener(l.first, l.second.rid, st.id, 0, ntoken, {}, {},
                 std::move(vals), l.second.query);
     }
@@ -2255,7 +2254,7 @@ Dht::storageAddListener(const InfoHash& id, const std::shared_ptr<Node>& node, s
     if (l == (*st)->listeners.end()) {
         auto vals = (*st)->get(query.where.getFilter());
         if (not vals.empty()) {
-            network_engine.tellListener(node, rid, id, WANT4 | WANT6, makeToken((sockaddr*)&node->ss, false),
+            network_engine.tellListener(node, rid, id, WANT4 | WANT6, makeToken((sockaddr*)&node->addr.first, false),
                     buckets.findClosestNodes(id, now, TARGET_NODES), buckets6.findClosestNodes(id, now, TARGET_NODES),
                     std::move(vals), query);
         }
@@ -2538,8 +2537,7 @@ Dht::dumpSearch(const Search& sr, std::ostream& out) const
                 out << "] ";
             }
         }
-        out << print_addr(n.node->ss, n.node->sslen);
-        out << std::endl;
+        out << n.node->addr.toString() << std::endl;
     }
 }
 
@@ -2629,7 +2627,7 @@ Dht::Dht(int s, int s6, Config config)
     network_engine(myid, config.network, s, s6, DHT_LOG, scheduler,
             std::bind(&Dht::onError, this, _1, _2),
             std::bind(&Dht::onNewNode, this, _1, _2),
-            std::bind(&Dht::onReportedAddr, this, _1, _2, _3),
+            std::bind(&Dht::onReportedAddr, this, _1, _2),
             std::bind(&Dht::onPing, this, _1),
             std::bind(&Dht::onFindNode, this, _1, _2, _3),
             std::bind(&Dht::onGetValues, this, _1, _2, _3, _4),
@@ -2827,15 +2825,15 @@ Dht::maintainStorage(InfoHash id, bool force, DoneCallback donecb) {
 }
 
 void
-Dht::processMessage(const uint8_t *buf, size_t buflen, const sockaddr *from, socklen_t fromlen)
+Dht::processMessage(const uint8_t *buf, size_t buflen, const SockAddr& from)
 {
     if (buflen == 0)
         return;
 
     try {
-        network_engine.processMessage(buf, buflen, from, fromlen);
+        network_engine.processMessage(buf, buflen, from);
     } catch (const std::exception& e) {
-        DHT_LOG.ERR("Can't parse message from %s: %s", print_addr(from, fromlen).c_str(), e.what());
+        DHT_LOG.ERR("Can't parse message from %s: %s", from.toString().c_str(), e.what());
         //auto code = e.getCode();
         //if (code == DhtProtocolException::INVALID_TID_SIZE or code == DhtProtocolException::WRONG_NODE_INFO_BUF_LEN) {
             /* This is really annoying, as it means that we will
@@ -2848,10 +2846,10 @@ Dht::processMessage(const uint8_t *buf, size_t buflen, const sockaddr *from, soc
 }
 
 time_point
-Dht::periodic(const uint8_t *buf, size_t buflen, const sockaddr *from, socklen_t fromlen)
+Dht::periodic(const uint8_t *buf, size_t buflen, const SockAddr& from)
 {
     scheduler.syncTime();
-    processMessage(buf, buflen, from, fromlen);
+    processMessage(buf, buflen, from);
     return scheduler.run();
 }
 
@@ -3002,12 +3000,12 @@ Dht::exportNodes()
 }
 
 void
-Dht::insertNode(const InfoHash& id, const sockaddr* sa, socklen_t salen)
+Dht::insertNode(const InfoHash& id, const SockAddr& addr)
 {
-    if (sa->sa_family != AF_INET && sa->sa_family != AF_INET6)
+    if (addr.getFamily() != AF_INET && addr.getFamily() != AF_INET6)
         return;
     scheduler.syncTime();
-    network_engine.insertNode(id, sa, salen);
+    network_engine.insertNode(id, addr);
 }
 
 void
@@ -3046,12 +3044,12 @@ Dht::onError(std::shared_ptr<Request> req, DhtProtocolException e) {
 }
 
 void
-Dht::onReportedAddr(const InfoHash& id, sockaddr* addr , socklen_t addr_length)
+Dht::onReportedAddr(const InfoHash& id, const SockAddr& addr)
 {
-    const auto& b = (addr->sa_family == AF_INET ? buckets : buckets6).findBucket(id);
+    const auto& b = (addr.getFamily() == AF_INET ? buckets : buckets6).findBucket(id);
     b->time = scheduler.time();
-    if (addr and addr_length)
-        reportedAddr(addr, addr_length);
+    if (addr.second)
+        reportedAddr(addr);
 }
 
 NetworkEngine::RequestAnswer
@@ -3065,7 +3063,7 @@ Dht::onFindNode(std::shared_ptr<Node> node, InfoHash& target, want_t want)
 {
     const auto& now = scheduler.time();
     NetworkEngine::RequestAnswer answer;
-    answer.ntoken = makeToken((sockaddr*)&node->ss, false);
+    answer.ntoken = makeToken((sockaddr*)&node->addr.first, false);
     if (want & WANT4)
         answer.nodes4 = buckets.findClosestNodes(target, now, TARGET_NODES);
     if (want & WANT6)
@@ -3086,7 +3084,7 @@ Dht::onGetValues(std::shared_ptr<Node> node, InfoHash& hash, want_t, const Query
     const auto& now = scheduler.time();
     NetworkEngine::RequestAnswer answer {};
     auto st = findStorage(hash);
-    answer.ntoken = makeToken((sockaddr*)&node->ss, false);
+    answer.ntoken = makeToken((sockaddr*)&node->addr.first, false);
     answer.nodes4 = buckets.findClosestNodes(hash, now, TARGET_NODES);
     answer.nodes6 = buckets6.findClosestNodes(hash, now, TARGET_NODES);
     if (st != store.end() && not (*st)->empty()) {
@@ -3180,7 +3178,7 @@ Dht::onListen(std::shared_ptr<Node> node, InfoHash& hash, Blob& token, size_t ri
             DhtProtocolException::LISTEN_NO_INFOHASH
         };
     }
-    if (!tokenMatch(token, (sockaddr*)&node->ss)) {
+    if (!tokenMatch(token, (sockaddr*)&node->addr.first)) {
         DHT_LOG.WARN("[node %s] incorrect token %s for 'listen'.", node->toString().c_str(), hash.toString().c_str());
         throw DhtProtocolException {DhtProtocolException::UNAUTHORIZED, DhtProtocolException::LISTEN_WRONG_TOKEN};
     }
@@ -3224,7 +3222,7 @@ Dht::onAnnounce(std::shared_ptr<Node> node,
             DhtProtocolException::PUT_NO_INFOHASH
         };
     }
-    if (!tokenMatch(token, (sockaddr*)&node->ss)) {
+    if (!tokenMatch(token, (sockaddr*)&node->addr.first)) {
         DHT_LOG.WARN("[node %s] incorrect token %s for 'put'.", node->toString().c_str(), hash.toString().c_str());
         throw DhtProtocolException {DhtProtocolException::UNAUTHORIZED, DhtProtocolException::PUT_WRONG_TOKEN};
     }
@@ -3254,7 +3252,7 @@ Dht::onAnnounce(std::shared_ptr<Node> node,
                 DHT_LOG.WARN("[value %s %lu] nothing to do.", hash.toString().c_str(), lv->id);
             } else {
                 const auto& type = getType(lv->type);
-                if (type.editPolicy(hash, lv, vc, node->id, (sockaddr*)&node->ss, node->sslen)) {
+                if (type.editPolicy(hash, lv, vc, node->id, (sockaddr*)&node->addr.first, node->addr.second)) {
                     DHT_LOG.DEBUG("[value %s %lu] editing %s.",
                             hash.toString().c_str(), lv->id, vc->toString().c_str());
                     storageStore(hash, vc, created);
@@ -3266,7 +3264,7 @@ Dht::onAnnounce(std::shared_ptr<Node> node,
         } else {
             // Allow the value to be edited by the storage policy
             const auto& type = getType(vc->type);
-            if (type.storePolicy(hash, vc, node->id, (sockaddr*)&node->ss, node->sslen)) {
+            if (type.storePolicy(hash, vc, node->id, (sockaddr*)&node->addr.first, node->addr.second)) {
                 DHT_LOG.DEBUG("[value %s %lu] storing %s.", hash.toString().c_str(), vc->id, vc->toString().c_str());
                 storageStore(hash, vc, created);
             } else {
diff --git a/src/dhtrunner.cpp b/src/dhtrunner.cpp
index 068883f513ff78852d1fe14fdf1b7545ed4a1b58..fd96c16ab7a9c3c6ece214d5bae52eeaa6e75bfe 100644
--- a/src/dhtrunner.cpp
+++ b/src/dhtrunner.cpp
@@ -252,7 +252,7 @@ DhtRunner::getSearchesLog(sa_family_t af) const
     std::lock_guard<std::mutex> lck(dht_mtx);
     return dht_->getSearchesLog(af);
 }
-std::vector<Address>
+std::vector<SockAddr>
 DhtRunner::getPublicAddress(sa_family_t af)
 {
     std::lock_guard<std::mutex> lck(dht_mtx);
@@ -315,7 +315,7 @@ DhtRunner::loop_()
         for (const auto& pck : received) {
             auto& buf = pck.first;
             auto& from = pck.second;
-            wakeup = dht_->periodic(buf.data(), buf.size()-1, (sockaddr*)&from.first, from.second);
+            wakeup = dht_->periodic(buf.data(), buf.size()-1, from);
         }
         received.clear();
     } else {
@@ -400,19 +400,19 @@ DhtRunner::doRun(const sockaddr_in* sin4, const sockaddr_in6* sin6, SecureDht::C
 
                 if(rc > 0) {
                     std::array<uint8_t, 1024 * 64> buf;
-                    sockaddr_storage from;
-                    socklen_t fromlen {sizeof(from)};
+                    SockAddr from;
+                    from.second = sizeof(from.first);
 
                     if(s4 >= 0 && FD_ISSET(s4, &readfds))
-                        rc = recvfrom(s4, (char*)buf.data(), buf.size(), 0, (struct sockaddr*)&from, &fromlen);
+                        rc = recvfrom(s4, (char*)buf.data(), buf.size(), 0, (struct sockaddr*)&from.first, &from.second);
                     else if(s6 >= 0 && FD_ISSET(s6, &readfds))
-                        rc = recvfrom(s6, (char*)buf.data(), buf.size(), 0, (struct sockaddr*)&from, &fromlen);
+                        rc = recvfrom(s6, (char*)buf.data(), buf.size(), 0, (struct sockaddr*)&from.first, &from.second);
                     else
                         break;
                     if (rc > 0) {
                         {
                             std::lock_guard<std::mutex> lck(sock_mtx);
-                            rcv.emplace_back(Blob {buf.begin(), buf.begin()+rc+1}, std::make_pair(from, fromlen));
+                            rcv.emplace_back(Blob {buf.begin(), buf.begin()+rc+1}, from);
                         }
                         cv.notify_all();
                     }
diff --git a/src/network_engine.cpp b/src/network_engine.cpp
index f973b985a80ad3d0dfecf85741a252371f4695c8..14aef4eced9953bdc840f0d72917420c5e24f04a 100644
--- a/src/network_engine.cpp
+++ b/src/network_engine.cpp
@@ -80,7 +80,7 @@ struct ParsedMessage {
     want_t want;                                              /* states if ipv4 or ipv6 request */
     uint16_t error_code;                                      /* error code in case of error */
     std::string ua;
-    Address addr;                                             /* reported address by the distant node */
+    SockAddr addr;                                            /* reported address by the distant node */
     void msgpack_unpack(msgpack::object o);
 };
 
@@ -96,7 +96,7 @@ NetworkEngine::tellListener(std::shared_ptr<Node> node, uint16_t rid, const Info
 {
     auto nnodes = bufferNodes(node->getFamily(), hash, want, nodes, nodes6);
     try {
-        sendNodesValues((const sockaddr*)&node->ss, node->sslen, TransId {TransPrefix::GET_VALUES, (uint16_t)rid}, nnodes.first, nnodes.second,
+        sendNodesValues(node->addr, TransId {TransPrefix::GET_VALUES, (uint16_t)rid}, nnodes.first, nnodes.second,
                 values, query, ntoken);
     } catch (const std::overflow_error& e) {
         DHT_LOG.ERR("Can't send value: buffer not large enough !");
@@ -162,7 +162,7 @@ NetworkEngine::requestStep(std::shared_ptr<Request> req)
 
     send((char*)req->msg.data(), req->msg.size(),
             (req->node->reply_time >= now - UDP_REPLY_TIME) ? 0 : MSG_CONFIRM,
-            (sockaddr*)&req->node->ss, req->node->sslen);
+            req->node->addr);
     ++req->attempt_count;
     req->last_try = now;
     std::weak_ptr<Request> wreq = req;
@@ -206,15 +206,15 @@ NetworkEngine::rateLimit()
 }
 
 bool
-NetworkEngine::isMartian(const sockaddr* sa, socklen_t len)
+NetworkEngine::isMartian(const SockAddr& addr)
 {
     // Check that sa_family can be accessed safely
-    if (!sa || len < sizeof(sockaddr_in))
+    if (addr.second < sizeof(sockaddr_in))
         return true;
 
-    switch(sa->sa_family) {
+    switch(addr.first.ss_family) {
     case AF_INET: {
-        sockaddr_in *sin = (sockaddr_in*)sa;
+        sockaddr_in *sin = (sockaddr_in*)&addr.first;
         const uint8_t *address = (const uint8_t*)&sin->sin_addr;
         return sin->sin_port == 0 ||
             (address[0] == 0) ||
@@ -222,9 +222,9 @@ NetworkEngine::isMartian(const sockaddr* sa, socklen_t len)
             ((address[0] & 0xE0) == 0xE0);
     }
     case AF_INET6: {
-        if (len < sizeof(sockaddr_in6))
+        if (addr.second < sizeof(sockaddr_in6))
             return true;
-        sockaddr_in6 *sin6 = (sockaddr_in6*)sa;
+        sockaddr_in6 *sin6 = (sockaddr_in6*)&addr.first;
         const uint8_t *address = (const uint8_t*)&sin6->sin6_addr;
         return sin6->sin6_port == 0 ||
             (address[0] == 0xFF) ||
@@ -253,37 +253,25 @@ NetworkEngine::blacklistNode(const std::shared_ptr<Node>& n)
             ++rit;
         }
     }
-    memcpy(&blacklist[next_blacklisted], &n->ss, n->sslen);
-    next_blacklisted = (next_blacklisted + 1) % BLACKLISTED_MAX;
+    blacklist.emplace(n->addr);
 }
 
 bool
-NetworkEngine::isNodeBlacklisted(const sockaddr *sa, socklen_t salen) const
+NetworkEngine::isNodeBlacklisted(const SockAddr& addr) const
 {
-    if (salen > sizeof(sockaddr_storage))
-        return true;
-
-    /*if (isBlacklisted(sa, salen))
-        return true;*/
-
-    for (unsigned i = 0; i < BLACKLISTED_MAX; i++) {
-        if (memcmp(&blacklist[i], sa, salen) == 0)
-            return true;
-    }
-
-    return false;
+    return blacklist.find(addr) != blacklist.end();
 }
 
 void
-NetworkEngine::processMessage(const uint8_t *buf, size_t buflen, const sockaddr* from, socklen_t fromlen)
+NetworkEngine::processMessage(const uint8_t *buf, size_t buflen, const SockAddr& from)
 {
-    if (isMartian(from, fromlen)) {
-        DHT_LOG.WARN("Received packet from martian node %s", print_addr(from, fromlen).c_str());
+    if (isMartian(from)) {
+        DHT_LOG.WARN("Received packet from martian node %s", from.toString().c_str());
         return;
     }
 
-    if (isNodeBlacklisted(from, fromlen)) {
-        DHT_LOG.WARN("Received packet from blacklisted node %s", print_addr(from, fromlen).c_str());
+    if (isNodeBlacklisted(from)) {
+        DHT_LOG.WARN("Received packet from blacklisted node %s", from.toString().c_str());
         return;
     }
 
@@ -322,7 +310,7 @@ NetworkEngine::processMessage(const uint8_t *buf, size_t buflen, const sockaddr*
     if (msg.tid.length != 4) {
         DHT_LOG.ERR("Broken node truncates transaction ids (len: %d): ", msg.tid.length);
         DHT_LOG.ERR.logPrintable(buf, buflen);
-        blacklistNode(cache.getNode(msg.id, from, fromlen, now, true));
+        blacklistNode(cache.getNode(msg.id, from, now, true));
         return;
     }
 
@@ -337,7 +325,7 @@ NetworkEngine::processMessage(const uint8_t *buf, size_t buflen, const sockaddr*
         auto node = req->node;
         if (node->id != msg.id) {
             bool unknown_node = node->id == zeroes;
-            node = cache.getNode(msg.id, from, fromlen, now, true);
+            node = cache.getNode(msg.id, from, now, true);
             if (unknown_node) {
                 // received reply to a message sent when we didn't know the node ID.
                 req->node = node;
@@ -349,11 +337,11 @@ NetworkEngine::processMessage(const uint8_t *buf, size_t buflen, const sockaddr*
                 return;
             }
         } else
-            node->update(from, fromlen);
+            node->update(from);
         node->received(now, req);
 
         onNewNode(node, 2);
-        onReportedAddr(msg.id, (sockaddr*)&msg.addr.first, msg.addr.second);
+        onReportedAddr(msg.id, msg.addr);
 
         if (req->cancelled() or req->expired() or (req->completed() and not req->persistent)) {
             DHT_LOG.WARN("[node %s] response to expired, cancelled or completed request", node->toString().c_str());
@@ -373,7 +361,7 @@ NetworkEngine::processMessage(const uint8_t *buf, size_t buflen, const sockaddr*
                 onError(req, DhtProtocolException {DhtProtocolException::UNAUTHORIZED});
             } else {
                 DHT_LOG.WARN("[node %s %s] received unknown error message %u",
-                        msg.id.toString().c_str(), print_addr(from, fromlen).c_str(), msg.error_code);
+                        msg.id.toString().c_str(), from.toString().c_str(), msg.error_code);
                 DHT_LOG.WARN.logPrintable(buf, buflen);
             }
             break;
@@ -391,7 +379,7 @@ NetworkEngine::processMessage(const uint8_t *buf, size_t buflen, const sockaddr*
             break;
         }
     } else {
-        auto node = cache.getNode(msg.id, from, fromlen, now, true);
+        auto node = cache.getNode(msg.id, from, now, true);
         node->received(now, {});
         onNewNode(node, 1);
         try {
@@ -400,29 +388,29 @@ NetworkEngine::processMessage(const uint8_t *buf, size_t buflen, const sockaddr*
                 ++in_stats.ping;
                 DHT_LOG.DEBUG("Sending pong.");
                 onPing(node);
-                sendPong(from, fromlen, msg.tid);
+                sendPong(from, msg.tid);
                 break;
             case MessageType::FindNode: {
                 DHT_LOG.DEBUG("[node %s %s] got 'find' request (%d).",
-                        msg.id.toString().c_str(), print_addr(from, fromlen).c_str(), msg.want);
+                        msg.id.toString().c_str(), from.toString().c_str(), msg.want);
                 ++in_stats.find;
                 RequestAnswer answer = onFindNode(node, msg.target, msg.want);
-                auto nnodes = bufferNodes(from->sa_family, msg.target, msg.want, answer.nodes4, answer.nodes6);
-                sendNodesValues(from, fromlen, msg.tid, nnodes.first, nnodes.second, {}, {}, answer.ntoken);
+                auto nnodes = bufferNodes(from.getFamily(), msg.target, msg.want, answer.nodes4, answer.nodes6);
+                sendNodesValues(from, msg.tid, nnodes.first, nnodes.second, {}, {}, answer.ntoken);
                 break;
             }
             case MessageType::GetValues: {
                 DHT_LOG.DEBUG("[node %s %s] got 'get' request for %s.",
-                        msg.id.toString().c_str(), print_addr(from, fromlen).c_str(), msg.info_hash.toString().c_str());
+                        msg.id.toString().c_str(), from.toString().c_str(), msg.info_hash.toString().c_str());
                 ++in_stats.get;
                 RequestAnswer answer = onGetValues(node, msg.info_hash, msg.want, msg.query);
-                auto nnodes = bufferNodes(from->sa_family, msg.info_hash, msg.want, answer.nodes4, answer.nodes6);
-                sendNodesValues(from, fromlen, msg.tid, nnodes.first, nnodes.second, answer.values, msg.query, answer.ntoken);
+                auto nnodes = bufferNodes(from.getFamily(), msg.info_hash, msg.want, answer.nodes4, answer.nodes6);
+                sendNodesValues(from, msg.tid, nnodes.first, nnodes.second, answer.values, msg.query, answer.ntoken);
                 break;
             }
             case MessageType::AnnounceValue: {
                 DHT_LOG.DEBUG("[node %s %s] got 'put' request for %s.",
-                    msg.id.toString().c_str(), print_addr(from, fromlen).c_str(),
+                    msg.id.toString().c_str(), from.toString().c_str(),
                     msg.info_hash.toString().c_str());
                 ++in_stats.put;
                 onAnnounce(node, msg.info_hash, msg.token, msg.values, msg.created);
@@ -431,16 +419,16 @@ NetworkEngine::processMessage(const uint8_t *buf, size_t buflen, const sockaddr*
                    This is to prevent them from backtracking, and hence
                    polluting the DHT. */
                 for (auto& v : msg.values) {
-                   sendValueAnnounced(from, fromlen, msg.tid, v->id);
+                   sendValueAnnounced(from, msg.tid, v->id);
                 }
                 break;
             }
             case MessageType::Listen: {
                 DHT_LOG.DEBUG("[node %s %s] got 'listen' request for %s.",
-                        msg.id.toString().c_str(), print_addr(from, fromlen).c_str(), msg.info_hash.toString().c_str());
+                        msg.id.toString().c_str(), from.toString().c_str(), msg.info_hash.toString().c_str());
                 ++in_stats.listen;
                 RequestAnswer answer = onListen(node, msg.info_hash, msg.token, msg.tid.getTid(), std::move(msg.query));
-                sendListenConfirmation(from, fromlen, msg.tid);
+                sendListenConfirmation(from, msg.tid);
                 break;
             }
             default:
@@ -449,7 +437,7 @@ NetworkEngine::processMessage(const uint8_t *buf, size_t buflen, const sockaddr*
         } catch (const std::overflow_error& e) {
             DHT_LOG.ERR("Can't send value: buffer not large enough !");
         } catch (DhtProtocolException& e) {
-            sendError(from, fromlen, msg.tid, e.getCode(), e.getMsg().c_str(), true);
+            sendError(from, msg.tid, e.getCode(), e.getMsg().c_str(), true);
         }
     }
 }
@@ -462,34 +450,34 @@ packToken(msgpack::packer<msgpack::sbuffer>& pk, Blob token)
 }
 
 void
-insertAddr(msgpack::packer<msgpack::sbuffer>& pk, const sockaddr *sa, socklen_t sa_len)
+insertAddr(msgpack::packer<msgpack::sbuffer>& pk, const SockAddr& addr)
 {
-    size_t addr_len = std::min<size_t>(sa_len,
-                     (sa->sa_family == AF_INET) ? sizeof(in_addr) : sizeof(in6_addr));
-    void* addr_ptr = (sa->sa_family == AF_INET) ? (void*)&((sockaddr_in*)sa)->sin_addr
-                                                : (void*)&((sockaddr_in6*)sa)->sin6_addr;
+    size_t addr_len = std::min<size_t>(addr.second,
+                     (addr.getFamily() == AF_INET) ? sizeof(in_addr) : sizeof(in6_addr));
+    void* addr_ptr = (addr.getFamily() == AF_INET) ? (void*)&((sockaddr_in*)&addr.first)->sin_addr
+                                                : (void*)&((sockaddr_in6*)&addr.first)->sin6_addr;
     pk.pack("sa");
     pk.pack_bin(addr_len);
     pk.pack_bin_body((char*)addr_ptr, addr_len);
 }
 
 int
-NetworkEngine::send(const char *buf, size_t len, int flags, const sockaddr *sa, socklen_t salen)
+NetworkEngine::send(const char *buf, size_t len, int flags, const SockAddr& addr)
 {
-    if (salen == 0)
+    if (addr.second == 0)
         return -1;
 
     int s;
-    if (sa->sa_family == AF_INET)
+    if (addr.getFamily() == AF_INET)
         s = dht_socket;
-    else if (sa->sa_family == AF_INET6)
+    else if (addr.getFamily() == AF_INET6)
         s = dht_socket6;
     else
         s = -1;
 
     if (s < 0)
         return -1;
-    return sendto(s, buf, len, flags, sa, salen);
+    return sendto(s, buf, len, flags, (const sockaddr*)&addr.first, addr.second);
 }
 
 std::shared_ptr<Request>
@@ -531,14 +519,14 @@ NetworkEngine::sendPing(std::shared_ptr<Node> node, RequestCb on_done, RequestEx
 }
 
 void
-NetworkEngine::sendPong(const sockaddr* sa, socklen_t salen, TransId tid) {
+NetworkEngine::sendPong(const SockAddr& addr, TransId tid) {
     msgpack::sbuffer buffer;
     msgpack::packer<msgpack::sbuffer> pk(&buffer);
     pk.pack_map(4+(network?1:0));
 
     pk.pack(std::string("r")); pk.pack_map(2);
       pk.pack(std::string("id")); pk.pack(myid);
-      insertAddr(pk, sa, salen);
+      insertAddr(pk, addr);
 
     pk.pack(std::string("t")); pk.pack_bin(tid.size());
                                pk.pack_bin_body((const char*)tid.data(), tid.size());
@@ -548,7 +536,7 @@ NetworkEngine::sendPong(const sockaddr* sa, socklen_t salen, TransId tid) {
         pk.pack(std::string("n")); pk.pack(network);
     }
 
-    send(buffer.data(), buffer.size(), 0, sa, salen);
+    send(buffer.data(), buffer.size(), 0, addr);
 }
 
 std::shared_ptr<Request>
@@ -652,19 +640,21 @@ NetworkEngine::deserializeNodes(ParsedMessage& msg) {
     } else {
         // deserialize nodes
         const auto& now = scheduler.time();
+        SockAddr addr;
         for (unsigned i = 0; i < msg.nodes4_raw.size() / NODE4_INFO_BUF_LEN; i++) {
             uint8_t *ni = msg.nodes4_raw.data() + i * NODE4_INFO_BUF_LEN;
             const InfoHash& ni_id = *reinterpret_cast<InfoHash*>(ni);
             if (ni_id == myid)
                 continue;
-            sockaddr_in sin;
-            std::fill_n((uint8_t*)&sin, sizeof(sockaddr_in), 0);
-            sin.sin_family = AF_INET;
-            memcpy(&sin.sin_addr, ni + ni_id.size(), 4);
-            memcpy(&sin.sin_port, ni + ni_id.size() + 4, 2);
-            if (isMartian((sockaddr*)&sin, sizeof(sin)) || isNodeBlacklisted((sockaddr*)&sin, sizeof(sin)))
+            auto sin = (sockaddr_in*)&addr.first;
+            std::fill_n((uint8_t*)sin, sizeof(sockaddr_in), 0);
+            sin->sin_family = AF_INET;
+            memcpy(&sin->sin_addr, ni + ni_id.size(), 4);
+            memcpy(&sin->sin_port, ni + ni_id.size() + 4, 2);
+            addr.second = sizeof(sockaddr_in);
+            if (isMartian(addr) || isNodeBlacklisted(addr))
                 continue;
-            msg.nodes4.emplace_back(cache.getNode(ni_id, (sockaddr*)&sin, sizeof(sin), now, false));
+            msg.nodes4.emplace_back(cache.getNode(ni_id, addr, now, false));
             onNewNode(msg.nodes4.back(), 0);
         }
         for (unsigned i = 0; i < msg.nodes6_raw.size() / NODE6_INFO_BUF_LEN; i++) {
@@ -672,21 +662,22 @@ NetworkEngine::deserializeNodes(ParsedMessage& msg) {
             const InfoHash& ni_id = *reinterpret_cast<InfoHash*>(ni);
             if (ni_id == myid)
                 continue;
-            sockaddr_in6 sin6;
-            std::fill_n((uint8_t*)&sin6, sizeof(sockaddr_in6), 0);
-            sin6.sin6_family = AF_INET6;
-            memcpy(&sin6.sin6_addr, ni + HASH_LEN, 16);
-            memcpy(&sin6.sin6_port, ni + HASH_LEN + 16, 2);
-            if (isMartian((sockaddr*)&sin6, sizeof(sin6)) || isNodeBlacklisted((sockaddr*)&sin6, sizeof(sin6)))
+            auto sin6 = (sockaddr_in6*)&addr.first;
+            std::fill_n((uint8_t*)sin6, sizeof(sockaddr_in6), 0);
+            sin6->sin6_family = AF_INET6;
+            memcpy(&sin6->sin6_addr, ni + HASH_LEN, 16);
+            memcpy(&sin6->sin6_port, ni + HASH_LEN + 16, 2);
+            addr.second = sizeof(sockaddr_in6);
+            if (isMartian(addr) || isNodeBlacklisted(addr))
                 continue;
-            msg.nodes6.emplace_back(cache.getNode(ni_id, (sockaddr*)&sin6, sizeof(sin6), now, false));
+            msg.nodes6.emplace_back(cache.getNode(ni_id, addr, now, false));
             onNewNode(msg.nodes6.back(), 0);
         }
     }
 }
 
 void
-NetworkEngine::sendNodesValues(const sockaddr* sa, socklen_t salen, TransId tid, const Blob& nodes, const Blob& nodes6,
+NetworkEngine::sendNodesValues(const SockAddr& addr, TransId tid, const Blob& nodes, const Blob& nodes6,
         const std::vector<std::shared_ptr<Value>>& st, const Query& query, const Blob& token) {
     msgpack::sbuffer buffer;
     msgpack::packer<msgpack::sbuffer> pk(&buffer);
@@ -695,7 +686,7 @@ NetworkEngine::sendNodesValues(const sockaddr* sa, socklen_t salen, TransId tid,
     pk.pack(std::string("r"));
     pk.pack_map(2 + (not st.empty()?1:0) + (nodes.size()>0?1:0) + (nodes6.size()>0?1:0) + (not token.empty()?1:0));
     pk.pack(std::string("id")); pk.pack(myid);
-    insertAddr(pk, sa, salen);
+    insertAddr(pk, addr);
     if (nodes.size() > 0) {
         pk.pack(std::string("n4"));
         pk.pack_bin(nodes.size());
@@ -760,7 +751,7 @@ NetworkEngine::sendNodesValues(const sockaddr* sa, socklen_t salen, TransId tid,
         pk.pack(std::string("n")); pk.pack(network);
     }
 
-    send(buffer.data(), buffer.size(), 0, sa, salen);
+    send(buffer.data(), buffer.size(), 0, addr);
 }
 
 Blob
@@ -776,7 +767,7 @@ NetworkEngine::bufferNodes(sa_family_t af, const InfoHash& id, std::vector<std::
         const constexpr size_t size = HASH_LEN + sizeof(in_addr) + sizeof(in_port_t); // 26
         for (size_t i=0; i<nnode; i++) {
             const Node& n = *nodes[i];
-            sockaddr_in *sin = (sockaddr_in*)&n.ss;
+            sockaddr_in *sin = (sockaddr_in*)&n.addr.first;
             auto dest = bnodes.data() + size * i;
             memcpy(dest, n.id.data(), HASH_LEN);
             memcpy(dest + HASH_LEN, &sin->sin_addr, sizeof(in_addr));
@@ -787,7 +778,7 @@ NetworkEngine::bufferNodes(sa_family_t af, const InfoHash& id, std::vector<std::
         const constexpr size_t size = HASH_LEN + sizeof(in6_addr) + sizeof(in_port_t); // 38
         for (size_t i=0; i<nnode; i++) {
             const Node& n = *nodes[i];
-            sockaddr_in6 *sin6 = (sockaddr_in6*)&n.ss;
+            sockaddr_in6 *sin6 = (sockaddr_in6*)&n.addr.first;
             auto dest = bnodes.data() + size * i;
             memcpy(dest, n.id.data(), HASH_LEN);
             memcpy(dest + HASH_LEN, &sin6->sin6_addr, sizeof(in6_addr));
@@ -857,14 +848,14 @@ NetworkEngine::sendListen(std::shared_ptr<Node> n, const InfoHash& infohash, con
 }
 
 void
-NetworkEngine::sendListenConfirmation(const sockaddr* sa, socklen_t salen, TransId tid) {
+NetworkEngine::sendListenConfirmation(const SockAddr& addr, TransId tid) {
     msgpack::sbuffer buffer;
     msgpack::packer<msgpack::sbuffer> pk(&buffer);
     pk.pack_map(4+(network?1:0));
 
     pk.pack(std::string("r")); pk.pack_map(2);
       pk.pack(std::string("id")); pk.pack(myid);
-      insertAddr(pk, sa, salen);
+      insertAddr(pk, addr);
 
     pk.pack(std::string("t")); pk.pack_bin(tid.size());
                                pk.pack_bin_body((const char*)tid.data(), tid.size());
@@ -874,7 +865,7 @@ NetworkEngine::sendListenConfirmation(const sockaddr* sa, socklen_t salen, Trans
         pk.pack(std::string("n")); pk.pack(network);
     }
 
-    send(buffer.data(), buffer.size(), 0, sa, salen);
+    send(buffer.data(), buffer.size(), 0, addr);
 }
 
 std::shared_ptr<Request>
@@ -929,7 +920,7 @@ NetworkEngine::sendAnnounceValue(std::shared_ptr<Node> n, const InfoHash& infoha
 }
 
 void
-NetworkEngine::sendValueAnnounced(const sockaddr* sa, socklen_t salen, TransId tid, Value::Id vid) {
+NetworkEngine::sendValueAnnounced(const SockAddr& addr, TransId tid, Value::Id vid) {
     msgpack::sbuffer buffer;
     msgpack::packer<msgpack::sbuffer> pk(&buffer);
     pk.pack_map(4+(network?1:0));
@@ -937,7 +928,7 @@ NetworkEngine::sendValueAnnounced(const sockaddr* sa, socklen_t salen, TransId t
     pk.pack(std::string("r")); pk.pack_map(3);
       pk.pack(std::string("id"));  pk.pack(myid);
       pk.pack(std::string("vid")); pk.pack(vid);
-      insertAddr(pk, sa, salen);
+      insertAddr(pk, addr);
 
     pk.pack(std::string("t")); pk.pack_bin(tid.size());
                                pk.pack_bin_body((const char*)tid.data(), tid.size());
@@ -947,12 +938,11 @@ NetworkEngine::sendValueAnnounced(const sockaddr* sa, socklen_t salen, TransId t
         pk.pack(std::string("n")); pk.pack(network);
     }
 
-    send(buffer.data(), buffer.size(), 0, sa, salen);
+    send(buffer.data(), buffer.size(), 0, addr);
 }
 
 void
-NetworkEngine::sendError(const sockaddr* sa,
-        socklen_t salen,
+NetworkEngine::sendError(const SockAddr& addr,
         TransId tid,
         uint16_t code,
         const std::string& message,
@@ -978,7 +968,7 @@ NetworkEngine::sendError(const sockaddr* sa,
         pk.pack(std::string("n")); pk.pack(network);
     }
 
-    send(buffer.data(), buffer.size(), 0, sa, salen);
+    send(buffer.data(), buffer.size(), 0, addr);
 }
 
 void
diff --git a/src/node.cpp b/src/node.cpp
index 74149decd63aed8d71b1cc63e0417e1c86c1183c..3476b8c66d1371c7423ee4f8c6e588cf3c0e5da2 100644
--- a/src/node.cpp
+++ b/src/node.cpp
@@ -65,10 +65,9 @@ Node::getPendingMessageCount() const
 }
 
 void
-Node::update(const sockaddr* sa, socklen_t salen)
+Node::update(const SockAddr& new_addr)
 {
-    std::copy_n((const uint8_t*)sa, salen, (uint8_t*)&ss);
-    sslen = salen;
+    addr = new_addr;
 }
 
 /** To be called when a message was sent to the node */
@@ -119,7 +118,7 @@ Node::toString() const
 
 std::ostream& operator<< (std::ostream& s, const Node& h)
 {
-    s << h.id << " " << print_addr(h.ss, h.sslen);
+    s << h.id << " " << h.addr.toString();
     return s;
 }
 
diff --git a/src/node_cache.cpp b/src/node_cache.cpp
index 72655ab438d795826519513aacfe20d61af9d602..1491812e5a1239127ef02520c3b20f2450700247 100644
--- a/src/node_cache.cpp
+++ b/src/node_cache.cpp
@@ -27,10 +27,10 @@ NodeCache::getNode(const InfoHash& id, sa_family_t family) {
 }
 
 std::shared_ptr<Node>
-NodeCache::getNode(const InfoHash& id, const sockaddr* sa, socklen_t sa_len, time_point now, bool confirm) {
+NodeCache::getNode(const InfoHash& id, const SockAddr& addr, time_point now, bool confirm) {
     if (id == zeroes)
-        return std::make_shared<Node>(id, sa, sa_len);
-    return (sa->sa_family == AF_INET ? cache_4 : cache_6).getNode(id, sa, sa_len, now, confirm);
+        return std::make_shared<Node>(id, addr);
+    return (addr.getFamily() == AF_INET ? cache_4 : cache_6).getNode(id, addr, now, confirm);
 }
 
 std::vector<std::shared_ptr<Node>>
@@ -89,15 +89,15 @@ NodeCache::NodeMap::getNode(const InfoHash& id)
 }
 
 std::shared_ptr<Node>
-NodeCache::NodeMap::getNode(const InfoHash& id, const sockaddr* sa, socklen_t sa_len, time_point now, bool confirm)
+NodeCache::NodeMap::getNode(const InfoHash& id, const SockAddr& addr, time_point now, bool confirm)
 {
     auto it = emplace(id, std::weak_ptr<Node>{});
     auto node = it.first->second.lock();
     if (not node) {
-        node = std::make_shared<Node>(id, sa, sa_len);
+        node = std::make_shared<Node>(id, addr);
         it.first->second = node;
     } else if (confirm || node->time < now - Node::NODE_EXPIRE_TIME) {
-        node->update(sa, sa_len);
+        node->update(addr);
     }
     return node;
 }
diff --git a/src/utils.cpp b/src/utils.cpp
index 1abf0fe0471048c55662c68ecbe714379d2ff503..a75ff99890923f1699a19df137eb1a8fbf2274e3 100644
--- a/src/utils.cpp
+++ b/src/utils.cpp
@@ -18,6 +18,7 @@
  */
 
 #include "utils.h"
+#include "sockaddr.h"
 #include "default_types.h"
 
 namespace dht {
@@ -47,7 +48,7 @@ print_addr(const sockaddr_storage& ss, socklen_t sslen)
 }
 
 std::string
-printAddr(const Address& addr) {
+printAddr(const SockAddr& addr) {
     return print_addr((const sockaddr*)&addr.first, addr.second);
 }