diff --git a/CMakeLists.txt b/CMakeLists.txt
index df83e7a07ebff06ba53a6b274b1c29fffc6c3131..5eca99740ab529d0691038adee43b3f202c35de0 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -150,6 +150,8 @@ list (APPEND opendht_SOURCES
     src/dhtrunner.cpp
     src/log.cpp
     src/peer_discovery.cpp
+    src/network_utils.h
+    src/network_utils.cpp
 )
 
 list (APPEND opendht_HEADERS
diff --git a/src/Makefile.am b/src/Makefile.am
index aee033e5c09175459fd269db0e6f19d6128b18df..e5b4e39bf9517062abc76ed6c7e1bb26e95267bd 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -26,7 +26,9 @@ libopendht_la_SOURCES  = \
         securedht.cpp \
         dhtrunner.cpp \
         default_types.cpp \
-        log.cpp
+        log.cpp \
+        network_utils.h \
+        network_utils.cpp
 
 if WIN32
 libopendht_la_SOURCES += rng.cpp
diff --git a/src/dhtrunner.cpp b/src/dhtrunner.cpp
index d0a1bf853e24b392165c9a17818c6e246c00477d..7c2b34f670917fe7d7a266e0dc0338432077631e 100644
--- a/src/dhtrunner.cpp
+++ b/src/dhtrunner.cpp
@@ -21,26 +21,12 @@
 #include "dhtrunner.h"
 #include "securedht.h"
 #include "peer_discovery.h"
+#include "network_utils.h"
 
 #ifdef OPENDHT_PROXY_CLIENT
 #include "dht_proxy_client.h"
 #endif
 
-#ifndef _WIN32
-#include <unistd.h>
-#else
-#include <io.h>
-#endif
-
-#ifndef _WIN32
-#include <sys/socket.h>
-#else
-#include <winsock2.h>
-#include <ws2tcpip.h>
-#define close(x) closesocket(x)
-#define write(s, b, f) send(s, b, (int)strlen(b), 0)
-#endif
-
 namespace dht {
 
 constexpr std::chrono::seconds DhtRunner::BOOTSTRAP_PERIOD;
@@ -181,17 +167,6 @@ DhtRunner::run(const SockAddr& local4, const SockAddr& local6, const DhtRunner::
     }
 }
 
-void DhtRunner::bootstrap(const InfoHash& id, const SockAddr& address)
-{
-    {
-        std::unique_lock<std::mutex> lck(storage_mtx);
-        pending_ops_prio.emplace([id, address](SecureDht& dht) mutable {
-            dht.insertNode(id, address);
-        });
-    }
-    cv.notify_all();
-}
-
 void
 DhtRunner::shutdown(ShutdownCallback cb) {
 #ifdef OPENDHT_PROXY_CLIENT
@@ -523,34 +498,6 @@ int bindSocket(const SockAddr& addr, SockAddr& bound)
     return sock;
 }
 
-#ifdef _WIN32
-inline void udpPipe(int fds[2])
-{
-    int lst = socket(AF_INET, SOCK_DGRAM, 0);
-    if (lst < 0)
-        throw DhtException(std::string("Can't open socket: ") + strerror(lst));
-    sockaddr_in inaddr;
-    sockaddr addr;
-    memset(&inaddr, 0, sizeof(inaddr));
-    memset(&addr, 0, sizeof(addr));
-    inaddr.sin_family = AF_INET;
-    inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
-    inaddr.sin_port = 0;
-    int yes=1;
-    setsockopt(lst, SOL_SOCKET, SO_REUSEADDR, (char*)&yes, sizeof(yes));
-    int rc = bind(lst, (sockaddr*)&inaddr, sizeof(inaddr));
-    if (rc < 0) {
-        close(lst);
-        throw DhtException("Can't bind socket on " + print_addr((sockaddr*)&addr, sizeof(inaddr)) + " " + strerror(rc));
-    }
-    socklen_t len = sizeof(inaddr);
-    getsockname(lst, &addr, &len);
-    fds[0] = lst;
-    fds[1] = socket(AF_INET, SOCK_DGRAM, 0);
-    connect(fds[1], &addr, len);
-}
-#endif
-
 void
 DhtRunner::stopNetwork()
 {
@@ -576,7 +523,7 @@ DhtRunner::startNetwork(const SockAddr sin4, const SockAddr sin6)
         throw DhtException(std::string("Can't open pipe: ") + strerror(errno));
     }
 #else
-    udpPipe(stopfds);
+    net::udpPipe(stopfds);
 #endif
     int stop_readfd = stopfds[0];
     stop_writefd = stopfds[1];
@@ -988,6 +935,18 @@ DhtRunner::bootstrap(const SockAddr& addr, DoneCallbackSimple&& cb)
     cv.notify_all();
 }
 
+void
+DhtRunner::bootstrap(const InfoHash& id, const SockAddr& address)
+{
+    {
+        std::unique_lock<std::mutex> lck(storage_mtx);
+        pending_ops_prio.emplace([id, address](SecureDht& dht) mutable {
+            dht.insertNode(id, address);
+        });
+    }
+    cv.notify_all();
+}
+
 void
 DhtRunner::bootstrap(const std::vector<NodeExport>& nodes)
 {
diff --git a/src/network_engine.cpp b/src/network_engine.cpp
index 6eed6584d71754e1bb360f675bb580d2d00ceb4a..27d80e9f3f5b813c0b55624cdbacbd5491a5c185 100644
--- a/src/network_engine.cpp
+++ b/src/network_engine.cpp
@@ -23,6 +23,7 @@
 #include "default_types.h"
 #include "log_enable.h"
 #include "parsed_message.h"
+#include "network_utils.h"
 
 #include <msgpack.hpp>
 
@@ -61,32 +62,6 @@ static const uint8_t v4prefix[16] = {
 
 constexpr unsigned SEND_NODES {8};
 
-#ifdef _WIN32
-
-static bool
-set_nonblocking(int fd, int nonblocking)
-{
-    unsigned long mode = !!nonblocking;
-    int rc = ioctlsocket(fd, FIONBIO, &mode);
-    return rc == 0;
-}
-
-extern const char *inet_ntop(int, const void *, char *, socklen_t);
-
-#else
-
-static bool
-set_nonblocking(int fd, int nonblocking)
-{
-    int rc = fcntl(fd, F_GETFL, 0);
-    if (rc < 0)
-        return false;
-    rc = fcntl(fd, F_SETFL, nonblocking?(rc | O_NONBLOCK):(rc & ~O_NONBLOCK));
-    return rc >= 0;
-}
-
-#endif
-
 
 /* Transaction-ids are 4-bytes long, with the first two bytes identifying
  * the kind of request, and the remaining two a sequence number in
@@ -165,11 +140,11 @@ NetworkEngine::NetworkEngine(InfoHash& myid, NetId net, const int& s, const int&
     myid(myid), network(net), dht_socket(s), dht_socket6(s6), DHT_LOG(log), scheduler(scheduler)
 {
     if (dht_socket >= 0) {
-        if (!set_nonblocking(dht_socket, 1))
+        if (!set_nonblocking(dht_socket))
             throw DhtException("Can't set socket to non-blocking mode");
     }
     if (dht_socket6 >= 0) {
-        if (!set_nonblocking(dht_socket6, 1))
+        if (!set_nonblocking(dht_socket6))
             throw DhtException("Can't set socket to non-blocking mode");
     }
 }
diff --git a/src/network_utils.cpp b/src/network_utils.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f542aa0b725c88790bdcfd68657086aae1aa15a9
--- /dev/null
+++ b/src/network_utils.cpp
@@ -0,0 +1,82 @@
+/*
+ *  Copyright (C) 2019 Savoir-faire Linux Inc.
+ *  Author(s) : 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 <https://www.gnu.org/licenses/>.
+ */
+
+#include "network_utils.h"
+
+#ifndef _WIN32
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#else
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <io.h>
+#define close(x) closesocket(x)
+#define write(s, b, f) send(s, b, (int)strlen(b), 0)
+#endif
+#include <fcntl.h>
+
+namespace dht {
+namespace net {
+
+bool
+set_nonblocking(int fd, bool nonblocking)
+{
+#ifdef _WIN32
+    unsigned long mode = !!nonblocking;
+    int rc = ioctlsocket(fd, FIONBIO, &mode);
+    return rc == 0;
+#else
+    int rc = fcntl(fd, F_GETFL, 0);
+    if (rc < 0)
+        return false;
+    rc = fcntl(fd, F_SETFL, nonblocking ? (rc | O_NONBLOCK) : (rc & ~O_NONBLOCK));
+    return rc >= 0;
+#endif
+}
+
+#ifdef _WIN32
+void udpPipe(int fds[2])
+{
+    int lst = socket(AF_INET, SOCK_DGRAM, 0);
+    if (lst < 0)
+        throw DhtException(std::string("Can't open socket: ") + strerror(lst));
+    sockaddr_in inaddr;
+    sockaddr addr;
+    memset(&inaddr, 0, sizeof(inaddr));
+    memset(&addr, 0, sizeof(addr));
+    inaddr.sin_family = AF_INET;
+    inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+    inaddr.sin_port = 0;
+    int yes=1;
+    setsockopt(lst, SOL_SOCKET, SO_REUSEADDR, (char*)&yes, sizeof(yes));
+    int rc = bind(lst, (sockaddr*)&inaddr, sizeof(inaddr));
+    if (rc < 0) {
+        close(lst);
+        throw DhtException("Can't bind socket on " + print_addr((sockaddr*)&addr, sizeof(inaddr)) + " " + strerror(rc));
+    }
+    socklen_t len = sizeof(inaddr);
+    getsockname(lst, &addr, &len);
+    fds[0] = lst;
+    fds[1] = socket(AF_INET, SOCK_DGRAM, 0);
+    connect(fds[1], &addr, len);
+}
+#endif
+
+}
+}
diff --git a/src/network_utils.h b/src/network_utils.h
new file mode 100644
index 0000000000000000000000000000000000000000..b9148237c9a5c1fd757b4967bfb1bdf5d3956df7
--- /dev/null
+++ b/src/network_utils.h
@@ -0,0 +1,37 @@
+/*
+ *  Copyright (C) 2019 Savoir-faire Linux Inc.
+ *  Author(s) : 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 <https://www.gnu.org/licenses/>.
+ */
+#pragma once
+
+#ifdef _WIN32
+#include <winsock2.h>
+#define close(x) closesocket(x)
+#define write(s, b, f) send(s, b, (int)strlen(b), 0)
+#endif
+
+namespace dht {
+namespace net {
+
+bool set_nonblocking(int fd, bool nonblocking = true);
+
+#ifdef _WIN32
+void udpPipe(int fds[2]);
+// extern const char *inet_ntop(int, const void *, char *, socklen_t);
+#endif
+
+}
+}
diff --git a/src/peer_discovery.cpp b/src/peer_discovery.cpp
index 984b7dbbfeb33fccc53cf35363506fa07177aeae..f78868fe89034d5683ce61214c8102cb277890ae 100644
--- a/src/peer_discovery.cpp
+++ b/src/peer_discovery.cpp
@@ -17,6 +17,7 @@
  */
 
 #include "peer_discovery.h"
+#include "network_utils.h"
 
 #ifdef _WIN32
 #include <Ws2tcpip.h> // needed for ip_mreq definition for multicast
@@ -31,36 +32,20 @@ namespace dht {
 constexpr char MULTICAST_ADDRESS_IPV4[10] = "224.0.0.1";
 constexpr char MULTICAST_ADDRESS_IPV6[8] = "ff05::2"; // Site-local multicast
 
-#ifdef _WIN32
-
-static bool
-set_nonblocking(int fd, int nonblocking)
+PeerDiscovery::PeerDiscovery(sa_family_t domain, in_port_t port)
+    : domain_(domain), port_(port), sockfd_(initialize_socket(domain))
 {
-    unsigned long mode = !!nonblocking;
-    int rc = ioctlsocket(fd, FIONBIO, &mode);
-    return rc == 0;
+    socketJoinMulticast(sockfd_, domain);
 }
 
-extern const char *inet_ntop(int, const void *, char *, socklen_t);
-
-#else
-
-static bool
-set_nonblocking(int fd, int nonblocking)
+PeerDiscovery::~PeerDiscovery()
 {
-    int rc = fcntl(fd, F_GETFL, 0);
-    if (rc < 0)
-        return false;
-    rc = fcntl(fd, F_SETFL, nonblocking?(rc | O_NONBLOCK):(rc & ~O_NONBLOCK));
-    return rc >= 0;
-}
+    if (sockfd_ != -1)
+        close(sockfd_);
 
+#ifdef _WIN32
+    WSACleanup();
 #endif
-
-PeerDiscovery::PeerDiscovery(sa_family_t domain, in_port_t port)
-    : domain_(domain), port_(port), sockfd_(initialize_socket(domain))
-{
-    socketJoinMulticast(sockfd_, domain);
 }
 
 int
@@ -77,7 +62,7 @@ PeerDiscovery::initialize_socket(sa_family_t domain)
     if (sockfd < 0) {
         throw std::runtime_error(std::string("Socket Creation Error: ") + strerror(errno));
     }
-    set_nonblocking(sockfd, 1);
+    net::set_nonblocking(sockfd);
     return sockfd;
 }
 
@@ -235,7 +220,7 @@ PeerDiscovery::listener_thread(PeerDiscoveredCallback callback)
         throw std::runtime_error(std::string("Can't open pipe: ") + strerror(errno));
     }
 #else
-    udpPipe(stopfds_pipe);
+    net::udpPipe(stopfds_pipe);
 #endif
     int stop_readfd = stopfds_pipe[0];
     stop_writefd_ = stopfds_pipe[1];
@@ -329,14 +314,4 @@ PeerDiscovery::stop()
     }
 }
 
-PeerDiscovery::~PeerDiscovery()
-{
-    if (sockfd_ != -1)
-        close(sockfd_);
-
-#ifdef _WIN32
-    WSACleanup();
-#endif
-}
-
 }