diff --git a/src/ip_utils.h b/src/ip_utils.h
index 77e8965ea078016174547c8f4d4e13e085fb1580..0d4f41074d59995999afec376ba528542e97c4a5 100644
--- a/src/ip_utils.h
+++ b/src/ip_utils.h
@@ -67,37 +67,52 @@ namespace ring {
  */
 class IpAddr {
 public:
-    IpAddr(uint16_t family = AF_UNSPEC) : addr() {
+    IpAddr() : IpAddr(AF_UNSPEC) {}
+    IpAddr(const IpAddr&) = default;
+    IpAddr(IpAddr&&) = default;
+    IpAddr& operator=(const IpAddr&) = default;
+    IpAddr& operator=(IpAddr&&) = default;
+
+    explicit IpAddr(uint16_t family) : addr() {
         addr.addr.sa_family = family;
     }
 
-    // From a sockaddr-type structure
-    IpAddr(const IpAddr& other) : addr(other.addr) {}
     IpAddr(const pj_sockaddr& ip) : addr(ip) {}
-    IpAddr(const sockaddr* ip, socklen_t len) : addr() {
-        memcpy(&addr, ip, len);
+
+    IpAddr(const pj_sockaddr& ip, socklen_t len) : addr() {
+        if (len > sizeof(addr))
+            throw std::invalid_argument("IpAddr(): length overflows internal storage type");
+        memcpy(&addr, &ip, len);
+    }
+
+    IpAddr(const sockaddr& ip) : addr() {
+        memcpy(&addr, &ip, ip.sa_family == AF_INET6 ? sizeof addr.ipv6 : sizeof addr.ipv4);
     }
 
     IpAddr(const sockaddr_in& ip) : addr() {
+        static_assert(sizeof(ip) <= sizeof(addr), "sizeof(sockaddr_in) too large");
         memcpy(&addr, &ip, sizeof(sockaddr_in));
     }
+
     IpAddr(const sockaddr_in6& ip) : addr() {
+        static_assert(sizeof(ip) <= sizeof(addr), "sizeof(sockaddr_in6) too large");
         memcpy(&addr, &ip, sizeof(sockaddr_in6));
     }
-    IpAddr(const sockaddr& ip) : addr() {
-        memcpy(&addr, &ip, ip.sa_family == AF_INET6 ? sizeof addr.ipv6 : sizeof addr.ipv4);
-    }
+
     IpAddr(const sockaddr_storage& ip) : IpAddr(*reinterpret_cast<const sockaddr*>(&ip)) {}
-    IpAddr(const in6_addr& ip) : addr() {
-        addr.addr.sa_family = AF_INET6;
-        memcpy(&addr.ipv6.sin6_addr, &ip, sizeof(in6_addr));
-    }
+
     IpAddr(const in_addr& ip) : addr() {
+        static_assert(sizeof(ip) <= sizeof(addr), "sizeof(in_addr) too large");
         addr.addr.sa_family = AF_INET;
         memcpy(&addr.ipv4.sin_addr, &ip, sizeof(in_addr));
     }
 
-    // From a string
+    IpAddr(const in6_addr& ip) : addr() {
+        static_assert(sizeof(ip) <= sizeof(addr), "sizeof(in6_addr) too large");
+        addr.addr.sa_family = AF_INET6;
+        memcpy(&addr.ipv6.sin6_addr, &ip, sizeof(in6_addr));
+    }
+
     IpAddr(const std::string& str, pj_uint16_t family = AF_UNSPEC) : addr() {
         if (str.empty()) {
             addr.addr.sa_family = AF_UNSPEC;
@@ -115,6 +130,10 @@ public:
         return isIpv4() or isIpv6();
     }
 
+    inline explicit operator bool() {
+        return isIpv4() or isIpv6();
+    }
+
     inline operator pj_sockaddr& () {
         return addr;
     }
@@ -141,12 +160,12 @@ public:
         return addr.ipv6;
     }
 
-    inline operator sockaddr& (){
-        return reinterpret_cast<sockaddr&>(addr);
+    inline operator const sockaddr& () const {
+        return reinterpret_cast<const sockaddr&>(addr);
     }
 
-    inline explicit operator sockaddr* (){
-        return reinterpret_cast<sockaddr*>(&addr);
+    inline operator const sockaddr* () const {
+        return reinterpret_cast<const sockaddr*>(&addr);
     }
 
     inline operator sockaddr_storage (){
@@ -229,12 +248,16 @@ public:
     static bool isValid(const std::string& address, pj_uint16_t family = pj_AF_UNSPEC());
 
 private:
-    pj_sockaddr addr;
+    pj_sockaddr addr {};
 };
 
 // IpAddr helpers
 inline bool operator==(const IpAddr& lhs, const IpAddr& rhs) { return !pj_sockaddr_cmp(&lhs, &rhs); }
 inline bool operator!=(const IpAddr& lhs, const IpAddr& rhs) { return !(lhs == rhs); }
+inline bool operator<(const IpAddr& lhs, const IpAddr& rhs) { return pj_sockaddr_cmp(&lhs, &rhs) < 0; }
+inline bool operator>(const IpAddr& lhs, const IpAddr& rhs) { return pj_sockaddr_cmp(&lhs, &rhs) > 0; }
+inline bool operator<=(const IpAddr& lhs, const IpAddr& rhs) { return pj_sockaddr_cmp(&lhs, &rhs) <= 0; }
+inline bool operator>=(const IpAddr& lhs, const IpAddr& rhs) { return pj_sockaddr_cmp(&lhs, &rhs) >= 0; }
 
 namespace ip_utils {
 
diff --git a/src/media/socket_pair.cpp b/src/media/socket_pair.cpp
index f4ce5c6fc1cb0ad5db3ee02386d1a0837c105ecc..ff32403803c3d1a4157acf5b6caf1196982d7153 100644
--- a/src/media/socket_pair.cpp
+++ b/src/media/socket_pair.cpp
@@ -150,7 +150,7 @@ udp_socket_create(int family, int port)
 
     bind_addr.setPort(port);
     RING_DBG("use local address: %s", bind_addr.toString(true, true).c_str());
-    if (bind(udp_fd, (sockaddr*)bind_addr, bind_addr.getLength()) < 0) {
+    if (::bind(udp_fd, bind_addr, bind_addr.getLength()) < 0) {
         RING_ERR("bind() failed");
         strErr();
         close(udp_fd);
@@ -455,8 +455,8 @@ SocketPair::writeData(uint8_t* buf, int buf_size)
 
         if (noWrite_)
             return buf_size;
-        return sendto(fd, reinterpret_cast<const char*>(buf), buf_size, 0,
-                      (sockaddr*)*dest_addr, dest_addr->getLength());
+        return ::sendto(fd, reinterpret_cast<const char*>(buf), buf_size, 0,
+                        *dest_addr, dest_addr->getLength());
     }
 
     if (noWrite_)