From c8d5155426eb628a2867a220d3641a35ab7a2208 Mon Sep 17 00:00:00 2001
From: Michal Kubecek <mkubecek@suse.cz>
Date: Fri, 27 Jul 2012 10:01:46 +0200
Subject: [PATCH 3/9] socket address abstraction class

Define class SockAddr as abstraction for struct sockaddr and
various sockaddr_* structures
---
 src/remote/SockAddr.h | 206 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 206 insertions(+)
 create mode 100644 src/remote/SockAddr.h

diff --git a/src/remote/SockAddr.h b/src/remote/SockAddr.h
new file mode 100644
index 0000000..03bf928
--- /dev/null
+++ b/src/remote/SockAddr.h
@@ -0,0 +1,206 @@
+/*
+ *  The contents of this file are subject to the Initial
+ *  Developer's Public License Version 1.0 (the "License");
+ *  you may not use this file except in compliance with the
+ *  License. You may obtain a copy of the License at
+ *  http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl.
+ *
+ *  Software distributed under the License is distributed AS IS,
+ *  WITHOUT WARRANTY OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing rights
+ *  and limitations under the License.
+ *
+ *  The Original Code was created by Michal Kubecek
+ *  for the Firebird Open Source RDBMS project.
+ *
+ *  Copyright (c) 2014 Michal Kubecek <mike@mk-sys.cz>
+ *  and all contributors signed below.
+ *
+ *  All Rights Reserved.
+ *  Contributor(s): ______________________________________.
+ */
+
+#ifndef REMOTE_SOCKADDR_H
+#define REMOTE_SOCKADDR_H
+
+#include <string.h>
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#ifndef WIN_NT
+#include <netinet/in.h>
+#endif
+
+#include "../remote/remote.h"
+
+
+class SockAddr {
+private:
+	static const unsigned maxLen = sizeof(struct sockaddr_in6);
+	char data[maxLen];
+	socklen_t len;
+
+public:
+	void clear();
+	const SockAddr& operator = (const SockAddr& x);
+
+	SockAddr() { clear(); }
+	SockAddr(const SockAddr& x) { *this = x; }
+	SockAddr(const unsigned char* p_data, unsigned p_len);
+	~SockAddr() {}
+
+	struct sockaddr* ptr() { return (struct sockaddr*) data; }
+	struct sockaddr_in* ptrIn() { return (struct sockaddr_in*) data; }
+	struct sockaddr_in6* ptrIn6() { return (struct sockaddr_in6*) data; }
+
+	unsigned length() const { return len; }
+	unsigned short family() const;
+	unsigned short port() const;
+	bool isLocalhost() const;
+	void setPort(unsigned short x);
+	int connect(SOCKET s) const;
+	int accept(SOCKET s);
+	int getsockname(SOCKET s);
+	int getpeername(SOCKET s);
+	void unmapV4();
+};
+
+
+inline void SockAddr::clear()
+{
+	len = 0;
+	memset(data, 0, maxLen);
+}
+
+
+inline const SockAddr& SockAddr::operator = (const SockAddr& x)
+{
+	memcpy(data, x.data, maxLen);
+	len = x.len;
+	return *this;
+}
+
+
+inline SockAddr::SockAddr(const unsigned char* p_data, unsigned p_len)
+{
+	if (p_len > maxLen)
+		p_len = maxLen;
+	memcpy(data, p_data, p_len);
+	len = p_len;
+}
+
+
+inline int SockAddr::connect(SOCKET s) const
+{
+	return ::connect(s, (struct sockaddr*) data, len);
+}
+
+
+inline int SockAddr::accept(SOCKET s)
+{
+	return ::accept(s, (struct sockaddr*) data, &len);
+}
+
+
+inline int SockAddr::getsockname(SOCKET s)
+{
+	len = maxLen;
+	int R = ::getsockname(s, (struct sockaddr*) data, &len);
+	if (R < 0)
+		clear();
+	return R;
+}
+
+
+inline int SockAddr::getpeername(SOCKET s)
+{
+	len = maxLen;
+	int R = ::getpeername(s, (struct sockaddr*) data, &len);
+	if (R < 0)
+		clear();
+	return R;
+}
+
+
+inline unsigned short SockAddr::family() const
+{
+	const struct sockaddr* sa = (const struct sockaddr*) data;
+	return sa->sa_family;
+}
+
+
+inline unsigned short SockAddr::port() const
+{
+	const struct sockaddr* sa = (const struct sockaddr*) data;
+	switch(sa->sa_family) {
+	case AF_INET:
+		return ntohs(((const struct sockaddr_in*) data)->sin_port);
+	case AF_INET6:
+		return ntohs(((const struct sockaddr_in6*) data)->sin6_port);
+	}
+	return 0; // exception?
+}
+
+
+inline void SockAddr::setPort(unsigned short x)
+{
+	const struct sockaddr* sa = (const struct sockaddr*) data;
+	switch(sa->sa_family) {
+	case AF_INET:
+		((struct sockaddr_in*) data)->sin_port = htons(x);
+		return;
+	case AF_INET6:
+		((struct sockaddr_in6*) data)->sin6_port = htons(x);
+		return;
+	}
+	// exception?
+}
+
+
+inline bool SockAddr::isLocalhost() const
+{
+	const struct sockaddr* sa = (const struct sockaddr*) data;
+	switch(sa->sa_family) {
+	case AF_INET:
+		{
+			const struct sockaddr_in* sa4 = (const struct sockaddr_in*) data;
+			return ((ntohl(sa4->sin_addr.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET);
+		}
+	case AF_INET6:
+		{
+			const struct sockaddr_in6* sa6 = (const struct sockaddr_in6*) data;
+			return (memcmp(&sa6->sin6_addr, &in6addr_loopback, sizeof(in6_addr)) == 0);
+		}
+	}
+	return 0; // exception?
+}
+
+
+// if address is embedded IPv4, convert it to normal IPv4
+inline void SockAddr::unmapV4()
+{
+	if (family() != AF_INET6)
+		return;
+
+	const struct sockaddr_in6* sa6 = (const struct sockaddr_in6*) data;
+	// IPv6 mapped IPv4 addresses are ::ffff:0:0/32
+	static const unsigned char v4mapped_pfx[12]
+		= { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff };
+	if (memcmp(sa6->sin6_addr.s6_addr, v4mapped_pfx, sizeof(v4mapped_pfx)) != 0)
+		return;
+
+	unsigned short port = ntohs(sa6->sin6_port);
+	struct in_addr addr;
+	memcpy(&addr, sa6->sin6_addr.s6_addr + sizeof(v4mapped_pfx), sizeof(addr));
+
+	struct sockaddr_in* sa4 = (struct sockaddr_in*) data;
+	sa4->sin_family = AF_INET;
+	sa4->sin_port = htons(port);
+	sa4->sin_addr.s_addr = addr.s_addr;
+	len = sizeof(struct sockaddr_in);
+}
+
+
+#endif // REMOTE_SOCKADDR_H
-- 
1.8.4.5

