diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 428bde8c..3f250fc0 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -6,17 +6,25 @@ set (CMAKE_AUTORCC ON) set (CMAKE_AUTOUIC ON) set (CMAKE_INCLUDE_CURRENT_DIR ON) -file (GLOB GUI_SOURCE_FILES src/*.cpp src/*.h) -file (GLOB GUI_UI_FILES src/*.ui) - if (WIN32) - set (GUI_RC_FILES res/win/Barrier.rc) + set (resources res/win/Barrier.rc) + set (arch "win32") +else() + set (arch "unix") +endif() + +file (GLOB sources src/*.cpp src/${arch}/*.cpp) +file (GLOB headers src/*.h src/${arch}/*.h) +file (GLOB designs src/*.ui) + +if (BARRIER_ADD_HEADERS) + list (APPEND sources ${headers}) endif() add_executable (barrier WIN32 - ${GUI_SOURCE_FILES} - ${GUI_UI_FILES} - ${GUI_RC_FILES} + ${sources} + ${designs} + ${resources} res/Barrier.qrc ) @@ -32,7 +40,7 @@ if (WIN32) HINTS ENV BONJOUR_SDK_HOME PATH_SUFFIXES "Lib/x64") set_target_properties (barrier PROPERTIES LINK_FLAGS "/NODEFAULTLIB:LIBCMT") - target_link_libraries (barrier ${DNSSD_LIB}) + target_link_libraries (barrier ${DNSSD_LIB} Ws2_32.lib Iphlpapi.lib) elseif (APPLE) find_library(APPSERVICES_LIB ApplicationServices) target_link_libraries(barrier ${APPSERVICES_LIB}) diff --git a/src/gui/src/DefaultInterfaceIP.h b/src/gui/src/DefaultInterfaceIP.h new file mode 100644 index 00000000..a3c14571 --- /dev/null +++ b/src/gui/src/DefaultInterfaceIP.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +namespace Debauchee +{ + +std::string default_interface_ip(); + +} diff --git a/src/gui/src/ZeroconfService.cpp b/src/gui/src/ZeroconfService.cpp index 02902ee5..66706253 100644 --- a/src/gui/src/ZeroconfService.cpp +++ b/src/gui/src/ZeroconfService.cpp @@ -20,6 +20,7 @@ #include "MainWindow.h" #include "ZeroconfRegister.h" #include "ZeroconfBrowser.h" +#include "DefaultInterfaceIP.h" #include #include @@ -34,12 +35,6 @@ #include #endif -static const QStringList preferedIPAddress( - QStringList() << - "192.168." << - "10." << - "172."); - const char* ZeroconfService:: m_ServerServiceName = "_barrierServerZeroconf._tcp"; const char* ZeroconfService:: m_ClientServiceName = "_barrierClientZeroconf._tcp"; @@ -124,27 +119,6 @@ void ZeroconfService::errorHandle(DNSServiceErrorType errorCode) tr("Error code: %1.").arg(errorCode)); } -QString ZeroconfService::getLocalIPAddresses() -{ - QStringList addresses; - foreach (const QHostAddress& address, QNetworkInterface::allAddresses()) { - if (address.protocol() == QAbstractSocket::IPv4Protocol && - address != QHostAddress(QHostAddress::LocalHost)) { - addresses.append(address.toString()); - } - } - - foreach (const QString& preferedIP, preferedIPAddress) { - foreach (const QString& address, addresses) { - if (address.startsWith(preferedIP)) { - return address; - } - } - } - - return ""; -} - bool ZeroconfService::registerService(bool server) { bool result = true; @@ -159,7 +133,7 @@ bool ZeroconfService::registerService(bool server) else { m_pZeroconfRegister = new ZeroconfRegister(this); if (server) { - QString localIP = getLocalIPAddresses(); + QString localIP = QString::fromStdString(Debauchee::default_interface_ip()); if (localIP.isEmpty()) { QMessageBox::warning(m_pMainWindow, tr("Barrier"), tr("Failed to get local IP address. " diff --git a/src/gui/src/ZeroconfService.h b/src/gui/src/ZeroconfService.h index 929cd055..8a3b8beb 100644 --- a/src/gui/src/ZeroconfService.h +++ b/src/gui/src/ZeroconfService.h @@ -42,7 +42,6 @@ private slots: void errorHandle(DNSServiceErrorType errorCode); private: - QString getLocalIPAddresses(); bool registerService(bool server); private: diff --git a/src/gui/src/main.cpp b/src/gui/src/main.cpp index 73251d52..b42f90ce 100644 --- a/src/gui/src/main.cpp +++ b/src/gui/src/main.cpp @@ -67,6 +67,13 @@ int main(int argc, char* argv[]) /* Workaround for QTBUG-40332 - "High ping when QNetworkAccessManager is instantiated" */ ::setenv ("QT_BEARER_POLL_TIMEOUT", "-1", 1); #endif + +#if _WIN32 + // winsock needs to be initialized for DefaultInterfaceIP + WSADATA wd; + WSAStartup(MAKEWORD(2, 0), &wd); +#endif + QCoreApplication::setOrganizationName("Debauchee"); QCoreApplication::setOrganizationDomain("github.com"); QCoreApplication::setApplicationName("Barrier"); diff --git a/src/gui/src/unix/DefaultInterfaceIP.cpp b/src/gui/src/unix/DefaultInterfaceIP.cpp new file mode 100644 index 00000000..ed82e327 --- /dev/null +++ b/src/gui/src/unix/DefaultInterfaceIP.cpp @@ -0,0 +1,79 @@ +#include "IfAddrsResource.h" +#include "SocketResource.h" +#include +#include +#include + +#ifdef __linux__ +#include +#else +#include +#include +#endif + +#include +#include + +namespace Debauchee +{ + +static bool is_wireless(const char * ifname, const SocketResource& sock) +{ +#ifdef __linux__ + struct iwreq req; + ::memset(&req, 0, sizeof(struct iwreq)); + ::strncpy(req.ifr_name, ifname, IFNAMSIZ - 1); + return ioctl(sock, SIOCGIWMODE, &req) >= 0; +#else + struct ifmediareq req; + ::memset(&req, 0, sizeof(struct ifmediareq)); + ::strncpy(req.ifm_name, ifname, IFNAMSIZ - 1); + if (ioctl(sock, SIOCGIFMEDIA, &req) >= 0 && + (req.ifm_status & IFM_AVALID) + ) { + return IFM_TYPE(req.ifm_active) == IFM_IEEE80211; + } + return false; +#endif +} + +static bool is_wireless(const char * ifname) +{ + if (ifname) { + SocketResource sock(AF_INET, SOCK_DGRAM, 0); + if (sock.is_valid()) { + return is_wireless(ifname, sock); + } + } + return false; +} + +std::string default_interface_ip() +{ + std::string wirelessAddress; + IfAddrsResource ifa; + if (ifa.is_valid()) { + for (struct ifaddrs * next = ifa; next; next = next->ifa_next) { + auto sain = (struct sockaddr_in *)next->ifa_addr; + if (!sain || sain->sin_family != AF_INET || + !(next->ifa_flags & IFF_RUNNING) || + (next->ifa_flags & IFF_LOOPBACK) + ) { + continue; + } + std::string address = inet_ntoa(sain->sin_addr); + // take first wired address right away + if (!is_wireless(next->ifa_name)) { + return address; + } + // save first wireless address to be used if we don't find a wired one + if (wirelessAddress.empty()) { + wirelessAddress = address; + } + } + } + return wirelessAddress; +} + +} + diff --git a/src/gui/src/unix/IfAddrsResource.h b/src/gui/src/unix/IfAddrsResource.h new file mode 100644 index 00000000..57c1a2ba --- /dev/null +++ b/src/gui/src/unix/IfAddrsResource.h @@ -0,0 +1,32 @@ +#include +#include + +namespace Debauchee +{ + +class IfAddrsResource +{ +public: + explicit IfAddrsResource() : + _valid(getifaddrs(&_ifa) == 0) + { + } + + ~IfAddrsResource() + { + if (_valid) { + freeifaddrs(_ifa); + } + } + + bool is_valid() const { return _valid; } + + operator struct ifaddrs * () const { return _ifa; } + +private: + bool _valid; + struct ifaddrs * _ifa; +}; + +} + diff --git a/src/gui/src/unix/SocketResource.h b/src/gui/src/unix/SocketResource.h new file mode 100644 index 00000000..57eb1727 --- /dev/null +++ b/src/gui/src/unix/SocketResource.h @@ -0,0 +1,32 @@ +#include +#include +#include + +namespace Debauchee +{ + +class SocketResource +{ +public: + explicit SocketResource(int domain, int type, int protocol) : + _fd(socket(domain, type, protocol)) + { + } + + ~SocketResource() + { + if (is_valid()) { + close(_fd); + } + } + + bool is_valid() const { return _fd >= 0; } + + operator int() const { return _fd; } + +private: + int _fd; +}; + +} + diff --git a/src/gui/src/win32/DefaultInterfaceIP.cpp b/src/gui/src/win32/DefaultInterfaceIP.cpp new file mode 100644 index 00000000..81796953 --- /dev/null +++ b/src/gui/src/win32/DefaultInterfaceIP.cpp @@ -0,0 +1,100 @@ +// REQUIRES: Ws2_32.lib, Iphlpapi.lib +// REQUIRES: WSAStartup(..) + +// see comments regarding WSAAddressToStringA in address_string(..) +#define _WINSOCK_DEPRECATED_NO_WARNINGS + +#include +#include +#include +#include "HeapResource.h" + +namespace Debauchee +{ + +static ULONG get_addresses(IP_ADAPTER_ADDRESSES * addresses, PULONG sz) +{ + return GetAdaptersAddresses(AF_INET, + GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME, + NULL, addresses, sz); +} + +static std::string address_string(const SOCKET_ADDRESS& address) +{ + // planning to use this with Qt but QString does not have a c'tor + // for wide strings so we need to force the ANSI API here + DWORD szStrAddress = 1023; + char strAddress[1024]; + if (WSAAddressToStringA(address.lpSockaddr, address.iSockaddrLength, NULL, strAddress, &szStrAddress)) { + return ""; + } + return strAddress; +} + +static std::string first_unicast_address(IP_ADAPTER_UNICAST_ADDRESS * unicast) +{ + while (unicast) { + auto address = address_string(unicast->Address); + if (!address.empty()) { + return address; + } + unicast = unicast->Next; + } + return std::string(); +} + +static bool is_useable_interface(const IP_ADAPTER_ADDRESSES * ifa) +{ + return + // is an ethernet or wireless interface AND + (ifa->IfType == IF_TYPE_ETHERNET_CSMACD || ifa->IfType == IF_TYPE_IEEE80211) && + // is up + ifa->OperStatus == IfOperStatusUp; +} + +static std::string default_ip_address(const IP_ADAPTER_ADDRESSES * next) +{ + std::string wirelessAddress; + for ( ; next; next = next->Next) { + if (!is_useable_interface(next)) { + continue; + } + std::string address = first_unicast_address(next->FirstUnicastAddress); + if (address.empty()) { + continue; + } + // take first wired address right away + if (next->IfType == IF_TYPE_ETHERNET_CSMACD) { + return address; + } + // save first wireless address to be used if we don't find a wired one + if (wirelessAddress.empty()) { + wirelessAddress = address; + } + } + return wirelessAddress; +} + +std::string default_interface_ip() +{ + const ULONG DefaultSzAddresses = 15 * 1024; // 15k recommended by msdn + ULONG szAddresses = DefaultSzAddresses; + HeapResource addresses(GetProcessHeap(), 0, DefaultSzAddresses); + if (addresses.is_valid()) { + ULONG result; + while (ERROR_BUFFER_OVERFLOW == (result = get_addresses(addresses, &szAddresses))) { + // add more space in case more adapters have shown up + szAddresses += DefaultSzAddresses; + swap(addresses, HeapResource(GetProcessHeap(), 0, szAddresses)); + if (!addresses.is_valid()) { + break; + } + } + if (result == ERROR_SUCCESS) { + return default_ip_address(addresses); + } + } + return std::string(); +} + +} diff --git a/src/gui/src/win32/HeapResource.h b/src/gui/src/win32/HeapResource.h new file mode 100644 index 00000000..aedce90e --- /dev/null +++ b/src/gui/src/win32/HeapResource.h @@ -0,0 +1,48 @@ +#pragma once + +namespace Debauchee +{ + +template +class HeapResource +{ +public: + explicit HeapResource(HANDLE heap, DWORD flags, SIZE_T sz) : + _heap(heap), + _flags(flags), + _mem((T*)HeapAlloc(heap, flags, sz)) + { + } + + HeapResource(HeapResource&& other) + : _mem(NULL) + { + swap(*this, other); + } + + ~HeapResource() + { + if (is_valid()) { + HeapFree(_heap, _flags, _mem); + } + } + + friend void swap(HeapResource& first, HeapResource& second) + { + using std::swap; + swap(first._heap, second._heap); + swap(first._flags, second._flags); + swap(first._mem, second._mem); + } + + bool is_valid() const { return _mem != NULL; } + + operator T*() const { return _mem; } + +private: + HANDLE _heap; + DWORD _flags; + T * _mem; +}; + +}