socket_windows.cc
Go to the documentation of this file.
1 /*
2  * rcdiscover - the network discovery tool for Roboception devices
3  *
4  * Copyright (c) 2017 Roboception GmbH
5  * All rights reserved
6  *
7  * Author: Raphael Schaller
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright notice,
13  * this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright notice,
16  * this list of conditions and the following disclaimer in the documentation
17  * and/or other materials provided with the distribution.
18  *
19  * 3. Neither the name of the copyright holder nor the names of its contributors
20  * may be used to endorse or promote products derived from this software without
21  * specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
27  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33  * POSSIBILITY OF SUCH DAMAGE.
34  */
35 
36 #include "socket_windows.h"
37 
38 #include "socket_exception.h"
39 
40 #include <iphlpapi.h>
41 #include <iostream>
42 #include <map>
43 
44 namespace rcdiscover
45 {
46 
48 {
49  static const ULONG baddr = htonl(INADDR_BROADCAST);
50  return baddr;
51 }
52 
53 SocketWindows SocketWindows::create(const ULONG dst_ip, const uint16_t port, std::string iface_name)
54 {
55  return SocketWindows(AF_INET,
56  SOCK_DGRAM,
57  IPPROTO_UDP,
58  dst_ip,
59  port,
60  std::move(iface_name));
61 }
62 
63 static std::map<int, std::string> getInterfaceNames()
64 {
65  PIP_ADAPTER_INFO adapter_info;
66  adapter_info = static_cast<IP_ADAPTER_INFO *>(malloc(sizeof(IP_ADAPTER_INFO)));
67  ULONG buflen = sizeof(IP_ADAPTER_INFO);
68 
69  if(GetAdaptersInfo(adapter_info, &buflen) == ERROR_BUFFER_OVERFLOW)
70  {
71  free(adapter_info);
72  adapter_info = static_cast<IP_ADAPTER_INFO *>(malloc(buflen));
73  }
74 
75  std::map<int, std::string> result;
76  if(GetAdaptersInfo(adapter_info, &buflen) == NO_ERROR)
77  {
78  PIP_ADAPTER_INFO adapter = adapter_info;
79  while (adapter)
80  {
81  result.emplace(adapter->Index, adapter->AdapterName);
82  adapter = adapter->Next;
83  }
84  }
85  return result;
86 }
87 
89  const uint16_t port)
90 {
91  std::vector<SocketWindows> sockets;
92 
93  const auto interface_names = getInterfaceNames();
94 
95  {
96  // limited broadcast
97 
98  ULONG table_size = 0;
99  PMIB_IPFORWARDTABLE table = nullptr;
100 
101  int result = NO_ERROR;
102  for (int i = 0; i < 5; ++i)
103  {
104  result = GetIpForwardTable(table, &table_size, false);
105 
106  if (result == NO_ERROR)
107  {
108  break;
109  }
110  else if (result == ERROR_INSUFFICIENT_BUFFER)
111  {
112  free(table);
113  table = (PMIB_IPFORWARDTABLE)malloc(table_size);
114  }
115  }
116  if (result != NO_ERROR)
117  {
118  throw SocketException("Error while getting forward table",
119  ::WSAGetLastError());
120  }
121 
122  for (unsigned int i = 0; i < table->dwNumEntries; ++i)
123  {
124  PMIB_IPFORWARDROW row = &table->table[i];
125 
126  if (row->dwForwardDest == getBroadcastAddr() &&
127  row->dwForwardMask == ULONG_MAX &&
128  row->dwForwardType == MIB_IPROUTE_TYPE_DIRECT)
129  {
130  const auto iface = interface_names.find(row->dwForwardIfIndex);
131  if (iface != interface_names.end())
132  {
133  sockets.emplace_back(SocketWindows::create(getBroadcastAddr(), port, iface->second));
134 
135  sockaddr_in src_addr;
136  src_addr.sin_family = AF_INET;
137  src_addr.sin_port = 0;
138  src_addr.sin_addr.s_addr = row->dwForwardNextHop;
139 
140  sockets.back().bind(src_addr);
141  }
142  }
143  }
144  }
145 
146  {
147  // directed broadcast
148 
149  PMIB_IPADDRTABLE table = nullptr;
150  ULONG table_size = 0;
151 
152  int result = NO_ERROR;
153  for (int i = 0; i < 5; ++i)
154  {
155  result = GetIpAddrTable(table, &table_size, false);
156 
157  if (result == NO_ERROR)
158  {
159  break;
160  }
161  else if (result == ERROR_INSUFFICIENT_BUFFER)
162  {
163  free(table);
164  table = (PMIB_IPADDRTABLE)malloc(table_size);
165  }
166  }
167  if (result != NO_ERROR)
168  {
169  throw SocketException("Error while getting ip addr table",
170  ::WSAGetLastError());
171  }
172 
173  for (unsigned int i = 0; i < table->dwNumEntries; ++i)
174  {
175  PMIB_IPADDRROW row = &table->table[i];
176 
177  if (row->dwAddr == htonl(INADDR_LOOPBACK))
178  {
179  continue;
180  }
181 
182  const auto iface = interface_names.find(row->dwIndex);
183  if (iface != interface_names.end())
184  {
185  const ULONG baddr = row->dwAddr | (~row->dwMask);
186 
187  sockets.emplace_back(SocketWindows::create(baddr, port, iface->second));
188 
189  sockaddr_in src_addr;
190  src_addr.sin_family = AF_INET;
191  src_addr.sin_port = 0;
192  src_addr.sin_addr.s_addr = htonl(INADDR_ANY);
193 
194  sockets.back().bind(src_addr);
195  }
196  }
197  }
198  return sockets;
199 }
200 
202  int type,
203  int protocol,
204  const ULONG dst_ip,
205  const uint16_t port,
206  std::string iface_name) :
207  Socket(std::move(iface_name)),
208  sock_(INVALID_SOCKET),
209  dst_addr_()
210 {
211  sock_ = ::WSASocket(domain, type, protocol, nullptr, 0, 0);
212  if (sock_ == INVALID_SOCKET)
213  {
214  throw SocketException("Error while creating socket", ::WSAGetLastError());
215  }
216 
217  dst_addr_.sin_addr.s_addr = dst_ip;
218  dst_addr_.sin_family = AF_INET;
219  dst_addr_.sin_port = htons(port);
220 }
221 
223  Socket(std::move(other)),
224  sock_(INVALID_SOCKET),
225  dst_addr_(other.dst_addr_)
226 {
227  std::swap(sock_, other.sock_);
228 }
229 
231 {
232  std::swap(sock_, other.sock_);
233  return *this;
234 }
235 
237 {
238  if(sock_ != INVALID_SOCKET)
239  {
240  ::closesocket(sock_);
241  }
242 }
243 
244 const SOCKET &SocketWindows::getHandleImpl() const
245 {
246  return sock_;
247 }
248 
249 void SocketWindows::bindImpl(const sockaddr_in& addr)
250 {
251  if (::bind(sock_,
252  reinterpret_cast<const struct sockaddr *>(&addr),
253  sizeof(addr)) == SOCKET_ERROR)
254  {
255  throw SocketException("Error while binding to socket", ::WSAGetLastError());
256  }
257 }
258 
259 void SocketWindows::sendImpl(const std::vector<uint8_t>& sendbuf)
260 {
261  auto sb = sendbuf;
262 
263  WSABUF wsa_buffer;
264  wsa_buffer.len = static_cast<ULONG>(sb.size());
265  wsa_buffer.buf = reinterpret_cast<char *>(sb.data());
266 
267  DWORD len;
268  if (::WSASendTo(sock_,
269  &wsa_buffer,
270  1,
271  &len,
272  0,
273  reinterpret_cast<const struct sockaddr *>(&dst_addr_),
274  sizeof(dst_addr_),
275  nullptr,
276  nullptr) == SOCKET_ERROR)
277  {
278  int err = ::WSAGetLastError();
279  // WSAENETUNREACH=10051, WSAEHOSTUNREACH=10065
280  if (err == WSAENETUNREACH || err == WSAEHOSTUNREACH)
281  {
283  "Error while sending data - network unreachable", err);
284  }
285  throw SocketException("Error while sending data", err);
286  }
287 }
288 
290 {
291  const int yes = 1;
292  if (::setsockopt(sock_,
293  SOL_SOCKET,
294  SO_BROADCAST,
295  reinterpret_cast<const char *>(&yes),
296  sizeof(yes)) == SOCKET_ERROR)
297  {
298  throw SocketException("Error while setting socket options",
299  ::WSAGetLastError());
300  }
301 }
302 
304 {
305  ULONG imode = 1;
306  if (::ioctlsocket(sock_, FIONBIO, &imode) == SOCKET_ERROR)
307  {
308  throw SocketException("Error while setting socket options",
309  ::WSAGetLastError());
310  }
311 }
312 
313 }
rcdiscover::SocketWindows::enableNonBlockingImpl
void enableNonBlockingImpl()
Enables non-blocking operation for this socket.
Definition: socket_windows.cc:303
rcdiscover::NetworkUnreachableException
Exception representing a Network Unreachable error (code 101 on Unix).
Definition: socket_exception.h:79
rcdiscover::SocketWindows::createAndBindForAllInterfaces
static std::vector< SocketWindows > createAndBindForAllInterfaces(uint16_t port)
Creates sockets for all interfaces and binds them to the respective interface.
Definition: socket_windows.cc:88
rcdiscover::SocketWindows::SocketWindows
SocketWindows(int domain, int type, int protocol, ULONG dst_ip, uint16_t port, std::string iface_name)
Constructor.
Definition: socket_windows.cc:201
rcdiscover::SocketWindows::getBroadcastAddr
static const ULONG & getBroadcastAddr()
Returns the broadcast address.
Definition: socket_windows.cc:47
socket_windows.h
rcdiscover::getInterfaceNames
static std::map< int, std::string > getInterfaceNames()
Definition: socket_windows.cc:63
rcdiscover::SocketWindows::create
static SocketWindows create(ULONG dst_ip, uint16_t port, std::string iface_name)
Create a new socket.
Definition: socket_windows.cc:53
rcdiscover::SocketWindows::dst_addr_
sockaddr_in dst_addr_
Definition: socket_windows.h:126
rcdiscover::SocketWindows
Definition: socket_windows.h:46
rcdiscover
Definition: deviceinfo.cc:40
rcdiscover::SocketException
Exception representing an invalid socket operation.
Definition: socket_exception.h:47
rcdiscover::SocketWindows::enableBroadcastImpl
void enableBroadcastImpl()
Enables broadcast for this socket.
Definition: socket_windows.cc:289
rcdiscover::SocketWindows::~SocketWindows
~SocketWindows()
Definition: socket_windows.cc:236
socket_exception.h
rcdiscover::SocketWindows::sock_
SOCKET sock_
Definition: socket_windows.h:125
rcdiscover::SocketWindows::sendImpl
void sendImpl(const std::vector< uint8_t > &sendbuf)
Sends data.
Definition: socket_windows.cc:259
rcdiscover::SocketWindows::getHandleImpl
const SOCKET & getHandleImpl() const
Returns the native socket handle.
Definition: socket_windows.cc:244
rcdiscover::SocketWindows::bindImpl
void bindImpl(const sockaddr_in &addr)
Binds the socket to a specific sockaddr.
Definition: socket_windows.cc:249
rcdiscover::Socket< SocketWindows >::bind
void bind(const sockaddr_in &addr)
Binds the socket to an interface.
Definition: socket.h:89
rcdiscover::Socket
CRTP class for platform specific socket implementation.
Definition: socket.h:52
rcdiscover::SocketWindows::operator=
SocketWindows & operator=(SocketWindows &&other)
Definition: socket_windows.cc:230


rcdiscover
Author(s): Heiko Hirschmueller , Raphael Schaller
autogenerated on Thu Aug 1 2024 02:55:56