1 /* Copyright 2016, Ableton AG, Berlin. All rights reserved.
2  *
3  *  This program is free software: you can redistribute it and/or modify
4  *  it under the terms of the GNU General Public License as published by
5  *  the Free Software Foundation, either version 2 of the License, or
6  *  (at your option) any later version.
7  *
8  *  This program is distributed in the hope that it will be useful,
9  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  *  GNU General Public License for more details.
12  *
13  *  You should have received a copy of the GNU General Public License
14  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
15  *
16  *  If you would like to incorporate Link into a proprietary software application,
17  *  please contact <link-devs@ableton.com>.
18  */
19 
20 #pragma once
21 
22 #include <ableton/platforms/asio/AsioWrapper.hpp>
23 #include <ableton/platforms/asio/Util.hpp>
24 #include <iphlpapi.h>
25 #include <stdio.h>
26 #include <vector>
27 #include <winsock2.h>
28 #include <ws2tcpip.h>
29 
30 #pragma comment(lib, "iphlpapi.lib")
31 #pragma comment(lib, "ws2_32.lib")
32 
33 namespace ableton
34 {
35 namespace platforms
36 {
37 namespace windows
38 {
39 namespace detail
40 {
41 // RAII type to make [get,free]ifaddrs function pairs exception safe
42 class GetIfAddrs
43 {
44 public:
GetIfAddrs()45   GetIfAddrs()
46   {
47     const int MAX_TRIES = 3;               // MSFT recommendation
48     const int WORKING_BUFFER_SIZE = 15000; // MSFT recommendation
49 
50     DWORD adapter_addrs_buffer_size = WORKING_BUFFER_SIZE;
51     for (int i = 0; i < MAX_TRIES; i++)
52     {
53       adapter_addrs = (IP_ADAPTER_ADDRESSES*)malloc(adapter_addrs_buffer_size);
54       assert(adapter_addrs);
55 
56       DWORD error = ::GetAdaptersAddresses(AF_UNSPEC,
57         GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER
58           | GAA_FLAG_SKIP_FRIENDLY_NAME,
59         NULL, adapter_addrs, &adapter_addrs_buffer_size);
60 
61       if (error == ERROR_SUCCESS)
62       {
63         break;
64       }
65       // if buffer too small, use new buffer size in next iteration
66       if (error == ERROR_BUFFER_OVERFLOW)
67       {
68         free(adapter_addrs);
69         adapter_addrs = NULL;
70         continue;
71       }
72     }
73   }
~GetIfAddrs()74   ~GetIfAddrs()
75   {
76     if (adapter_addrs)
77       free(adapter_addrs);
78   }
79 
80   // RAII must not copy
81   GetIfAddrs(GetIfAddrs&) = delete;
82   GetIfAddrs& operator=(GetIfAddrs&) = delete;
83 
84   template <typename Function>
withIfAddrs(Function f)85   void withIfAddrs(Function f)
86   {
87     if (adapter_addrs)
88       f(*adapter_addrs);
89   }
90 
91 private:
92   IP_ADAPTER_ADDRESSES* adapter_addrs;
93   IP_ADAPTER_ADDRESSES* adapter;
94 };
95 
96 } // namespace detail
97 
98 struct ScanIpIfAddrs
99 {
100   // Scan active network interfaces and return corresponding addresses
101   // for all ip-based interfaces.
operator ()ableton::platforms::windows::ScanIpIfAddrs102   std::vector<::asio::ip::address> operator()()
103   {
104     std::vector<::asio::ip::address> addrs;
105 
106     detail::GetIfAddrs getIfAddrs;
107     getIfAddrs.withIfAddrs([&addrs](const IP_ADAPTER_ADDRESSES& interfaces) {
108       const IP_ADAPTER_ADDRESSES* networkInterface;
109       for (networkInterface = &interfaces; networkInterface;
110            networkInterface = networkInterface->Next)
111       {
112         for (IP_ADAPTER_UNICAST_ADDRESS* address = networkInterface->FirstUnicastAddress;
113              NULL != address; address = address->Next)
114         {
115           auto family = address->Address.lpSockaddr->sa_family;
116           if (AF_INET == family)
117           {
118             // IPv4
119             SOCKADDR_IN* addr4 =
120               reinterpret_cast<SOCKADDR_IN*>(address->Address.lpSockaddr);
121             auto bytes = reinterpret_cast<const char*>(&addr4->sin_addr);
122             addrs.emplace_back(asio::makeAddress<::asio::ip::address_v4>(bytes));
123           }
124           else if (AF_INET6 == family)
125           {
126             SOCKADDR_IN6* addr6 =
127               reinterpret_cast<SOCKADDR_IN6*>(address->Address.lpSockaddr);
128             auto bytes = reinterpret_cast<const char*>(&addr6->sin6_addr);
129             addrs.emplace_back(asio::makeAddress<::asio::ip::address_v6>(bytes));
130           }
131         }
132       }
133     });
134     return addrs;
135   }
136 };
137 
138 } // namespace windows
139 } // namespace platforms
140 } // namespace ableton
141