summaryrefslogtreecommitdiff
path: root/qpid/cpp/src/qpid/sys/posix/SocketAddress.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'qpid/cpp/src/qpid/sys/posix/SocketAddress.cpp')
-rw-r--r--qpid/cpp/src/qpid/sys/posix/SocketAddress.cpp353
1 files changed, 353 insertions, 0 deletions
diff --git a/qpid/cpp/src/qpid/sys/posix/SocketAddress.cpp b/qpid/cpp/src/qpid/sys/posix/SocketAddress.cpp
new file mode 100644
index 0000000000..4c860a7ef7
--- /dev/null
+++ b/qpid/cpp/src/qpid/sys/posix/SocketAddress.cpp
@@ -0,0 +1,353 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/sys/SocketAddress.h"
+
+#include "qpid/Exception.h"
+#include "qpid/Msg.h"
+#include "qpid/log/Logger.h"
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <iosfwd>
+
+namespace qpid {
+namespace sys {
+
+SocketAddress::SocketAddress(const std::string& host0, const std::string& port0) :
+ host(host0),
+ port(port0),
+ addrInfo(0),
+ currentAddrInfo(0)
+{
+}
+
+SocketAddress::SocketAddress(const SocketAddress& sa) :
+ host(sa.host),
+ port(sa.port),
+ addrInfo(0),
+ currentAddrInfo(0)
+{
+}
+
+SocketAddress& SocketAddress::operator=(const SocketAddress& sa)
+{
+ SocketAddress temp(sa);
+
+ std::swap(temp, *this);
+ return *this;
+}
+
+SocketAddress::~SocketAddress()
+{
+ if (addrInfo) {
+ ::freeaddrinfo(addrInfo);
+ }
+}
+
+std::string SocketAddress::asString(::sockaddr const * const addr, size_t addrlen, bool dispNameOnly, bool hideDecoration)
+{
+ char servName[NI_MAXSERV];
+ char dispName[NI_MAXHOST];
+ if (int rc=::getnameinfo(addr, addrlen,
+ dispName, sizeof(dispName),
+ servName, sizeof(servName),
+ NI_NUMERICHOST | NI_NUMERICSERV) != 0)
+ throw qpid::Exception(QPID_MSG(gai_strerror(rc)));
+ std::string s;
+ switch (addr->sa_family) {
+ case AF_INET: s += dispName; break;
+ case AF_INET6:
+ if (!hideDecoration) {
+ s += "["; s += dispName; s+= "]";
+ } else {
+ s += dispName;
+ }
+ break;
+ case AF_UNIX: s += "UNIX:"; break;
+ default: throw Exception(QPID_MSG("Unexpected socket type"));
+ }
+ if (!dispNameOnly) {
+ s += ":";
+ s += servName;
+ }
+ return s;
+}
+
+uint16_t SocketAddress::getPort(::sockaddr const * const addr)
+{
+ switch (addr->sa_family) {
+ case AF_INET: return ntohs(((const ::sockaddr_in*)(const void*)addr)->sin_port);
+ case AF_INET6: return ntohs(((const ::sockaddr_in6*)(const void*)addr)->sin6_port);
+ default:throw Exception(QPID_MSG("Unexpected socket type"));
+ }
+}
+
+std::string SocketAddress::asString(bool numeric, bool dispNameOnly, bool hideDecoration) const
+{
+ if (!numeric)
+ return host + ":" + port;
+ // Canonicalise into numeric id
+ const ::addrinfo& ai = getAddrInfo(*this);
+
+ return asString(ai.ai_addr, ai.ai_addrlen, dispNameOnly, hideDecoration);
+}
+
+std::string SocketAddress::getHost() const
+{
+ return host;
+}
+
+/**
+ * Return true if this SocketAddress is IPv4 or IPv6
+ */
+bool SocketAddress::isIp() const
+{
+ const ::addrinfo& ai = getAddrInfo(*this);
+ return ai.ai_family == AF_INET || ai.ai_family == AF_INET6;
+}
+
+/**
+ * this represents the low address of an ACL address range.
+ * Given rangeHi that represents the high address,
+ * return a string showing the numeric comparisons that the
+ * inRange checks will do for address pair.
+ */
+std::string SocketAddress::comparisonDetails(const SocketAddress& rangeHi) const
+{
+ std::ostringstream os;
+ SocketAddress thisSa(*this);
+ SocketAddress rangeHiSa(rangeHi);
+ (void) getAddrInfo(thisSa);
+ (void) getAddrInfo(rangeHiSa);
+ os << "(" << thisSa.asString(true, true, false) <<
+ "," << rangeHiSa.asString(true, true, false) << ")";
+ while (thisSa.nextAddress()) {
+ if (!rangeHiSa.nextAddress()) {
+ throw(Exception(QPID_MSG("Comparison iteration fails: " + (*this).asString() +
+ rangeHi.asString())));
+ }
+ os << ",(" << thisSa.asString(true, true, false) <<
+ "," << rangeHiSa.asString(true, true, false) << ")";
+ }
+ if (rangeHiSa.nextAddress()) {
+ throw(Exception(QPID_MSG("Comparison iteration fails: " + (*this).asString() +
+ rangeHi.asString())));
+ }
+ std::string result = os.str();
+ return result;
+}
+
+/**
+ * For ACL address matching make sure that the two addresses, *this
+ * which is the low address and hiPeer which is the high address, are
+ * both numeric ip addresses of the same family and that hi > *this.
+ *
+ * Note that if the addresses resolve to more than one struct addrinfo
+ * then this and the hiPeer must be equal. This avoids having to do
+ * difficult range checks where the this and hiPeer both resolve to
+ * multiple IPv4 or IPv6 addresses.
+ *
+ * This check is run at acl file load time and not at run tme.
+ */
+bool SocketAddress::isComparable(const SocketAddress& hiPeer) const {
+ try {
+ // May only compare if this socket is IPv4 or IPv6
+ SocketAddress lo(*this);
+ const ::addrinfo& peerLoInfo = getAddrInfo(lo);
+ if (!(peerLoInfo.ai_family == AF_INET || peerLoInfo.ai_family == AF_INET6)) {
+ return false;
+ }
+ try {
+ // May only compare if peer socket is same family
+ SocketAddress hi(hiPeer);
+ const ::addrinfo& peerHiInfo = getAddrInfo(hi);
+ if (peerLoInfo.ai_family != peerHiInfo.ai_family) {
+ return false;
+ }
+ // Host names that resolve to lists are allowed if they are equal.
+ // For example: localhost, or fjord.lab.example.com
+ if ((*this).asString() == hiPeer.asString()) {
+ return true;
+ }
+ // May only compare if this and peer resolve to single address.
+ if (lo.nextAddress() || hi.nextAddress()) {
+ return false;
+ }
+ // Make sure that the lo/hi relationship is ok
+ int res;
+ if (!compareAddresses(peerLoInfo, peerHiInfo, res) || res < 0) {
+ return false;
+ }
+ return true;
+ } catch (Exception) {
+ // failed to resolve hi
+ return false;
+ }
+ } catch (Exception) {
+ // failed to resolve lo
+ return false;
+ }
+}
+
+/**
+ * *this SocketAddress was created from the numeric IP address of a
+ * connecting host.
+ * The lo and hi addresses are the limit checks from the ACL file.
+ * Return true if this address is in range of any of the address pairs
+ * in the limit check range.
+ *
+ * This check is executed on every incoming connection.
+ */
+bool SocketAddress::inRange(const SocketAddress& lo,
+ const SocketAddress& hi) const
+{
+ (*this).firstAddress();
+ lo.firstAddress();
+ hi.firstAddress();
+ const ::addrinfo& thisInfo = getAddrInfo(*this);
+ const ::addrinfo& loInfo = getAddrInfo(lo);
+ const ::addrinfo& hiInfo = getAddrInfo(hi);
+ if (inRange(thisInfo, loInfo, hiInfo)) {
+ return true;
+ }
+ while (lo.nextAddress()) {
+ if (!hi.nextAddress()) {
+ assert (false);
+ throw(Exception(QPID_MSG("Comparison iteration fails: " +
+ lo.asString() + hi.asString())));
+ }
+ const ::addrinfo& loInfo = getAddrInfo(lo);
+ const ::addrinfo& hiInfo = getAddrInfo(hi);
+ if (inRange(thisInfo, loInfo, hiInfo)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * *this SocketAddress was created from the numeric IP address of a
+ * connecting host.
+ * The lo and hi addresses are one binary address pair from a range
+ * given in an ACL file.
+ * Return true if this binary address is '>= lo' and '<= hi'.
+ */
+bool SocketAddress::inRange(const ::addrinfo& thisInfo,
+ const ::addrinfo& lo,
+ const ::addrinfo& hi) const
+{
+ int resLo;
+ int resHi;
+ if (!compareAddresses(lo, thisInfo, resLo)) {
+ return false;
+ }
+ if (!compareAddresses(hi, thisInfo, resHi)) {
+ return false;
+ }
+ if (resLo < 0) {
+ return false;
+ }
+ if (resHi > 0) {
+ return false;
+ }
+ return true;
+}
+
+/**
+ * Compare this address against two binary low/high addresses.
+ * return true with result holding the comparison.
+ */
+bool SocketAddress::compareAddresses(const struct addrinfo& lo,
+ const struct addrinfo& hi,
+ int& result) const
+{
+ if (lo.ai_family != hi.ai_family) {
+ return false;
+ }
+ if (lo.ai_family == AF_INET) {
+ void* taddr;
+
+ taddr = (void*)lo.ai_addr;
+ struct sockaddr_in* sin4lo = (struct sockaddr_in*)taddr;
+ taddr = (void*)hi.ai_addr;
+ struct sockaddr_in* sin4hi = (struct sockaddr_in*)taddr;
+ result = memcmp(&sin4hi->sin_addr, &sin4lo->sin_addr, sizeof(in_addr));
+ } else if (lo.ai_family == AF_INET6) {
+ void* taddr;
+
+ taddr = (void*)lo.ai_addr;
+ struct sockaddr_in6* sin6lo = (struct sockaddr_in6*)taddr;
+ taddr = (void*)hi.ai_addr;
+ struct sockaddr_in6* sin6hi = (struct sockaddr_in6*)taddr;
+ result = memcmp(&sin6hi->sin6_addr, &sin6lo->sin6_addr, sizeof(in6_addr));
+ } else {
+ assert (false);
+ return false;
+ }
+ return true;
+}
+
+void SocketAddress::firstAddress() const {
+ if (addrInfo) {
+ currentAddrInfo = addrInfo;
+ } else {
+ (void) getAddrInfo(*this);
+ }
+}
+
+bool SocketAddress::nextAddress() const {
+ bool r = currentAddrInfo->ai_next != 0;
+ if (r)
+ currentAddrInfo = currentAddrInfo->ai_next;
+ return r;
+}
+
+const ::addrinfo& getAddrInfo(const SocketAddress& sa)
+{
+ if (!sa.addrInfo) {
+ ::addrinfo hints;
+ ::memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC; // Allow both IPv4 and IPv6
+ hints.ai_socktype = SOCK_STREAM;
+
+ const char* node = 0;
+ if (sa.host.empty()) {
+ hints.ai_flags = AI_PASSIVE;
+ } else {
+ hints.ai_flags = AI_ADDRCONFIG; // Only use protocols that we have configured interfaces for
+ node = sa.host.c_str();
+ }
+ const char* service = sa.port.empty() ? "0" : sa.port.c_str();
+
+ int n = ::getaddrinfo(node, service, &hints, &sa.addrInfo);
+ if (n != 0)
+ throw Exception(QPID_MSG("Cannot resolve " << sa.asString(false) << ": " << ::gai_strerror(n)));
+ sa.currentAddrInfo = sa.addrInfo;
+ }
+
+ return *sa.currentAddrInfo;
+}
+
+}}