simple-socket library 1.5.0
Loading...
Searching...
No Matches
SocketUtils.cpp
1#include "SocketUtils.h"
2#include "SimpleSocket.h"
3
4#include <sys/socket.h>
5#include <sys/ioctl.h>
6#include <arpa/inet.h>
7#include <net/if.h>
8#include <net/if_arp.h>
9#include <netdb.h>
10#include <unistd.h>
11
12#include <cstring>
13#include <memory>
14
15using namespace NET;
16
17namespace
18{
19 // use as temporary RAII socket
20 class temp_socket : public SimpleSocket
21 {
22 public:
23 temp_socket() : SimpleSocket( AF_INET, SOCK_DGRAM, 0) {}
24 };
25
26 in_addr ipv4Address( const sockaddr& sa)
27 {
28 sockaddr_in addr = {};
29 std::memcpy( &addr, &sa, sizeof(addr));
30 return addr.sin_addr;
31 }
32
33 void assign_ipv4( sockaddr& sa, std::string_view address)
34 {
35 static_assert( sizeof(sockaddr_in) <= sizeof(sockaddr),
36 "sockaddr must be large enough for an IPv4 address");
37
38 std::string address_z(address);
39 sockaddr_in addr = {};
40 addr.sin_family = AF_INET;
41
42 if( inet_aton( address_z.c_str(), &addr.sin_addr) == 0)
43 throw SocketException("Invalid IPv4 address", false);
44
45 std::memcpy( &sa, &addr, sizeof(addr));
46 }
47
48 // check and copy the name of the interface
49 void assign_ifreq( struct ifreq& ifr, std::string_view interface)
50 {
51 const size_t len = interface.length();
52
53 if( len >= IF_NAMESIZE)
54 throw SocketException("Interface name is too long", false);
55
56 ifr = {};
57 ifr.ifr_addr.sa_family = AF_INET;
58 std::memcpy( ifr.ifr_name, interface.data(), len);
59 ifr.ifr_name[len] = 0;
60 }
61}
62
63std::string NET::resolveHostname( const std::string& hostname)
64{
65 addrinfo hints = {};
66 hints.ai_family = AF_INET;
67
68 addrinfo* result = nullptr;
69 int status = getaddrinfo( hostname.c_str(), nullptr, &hints, &result);
70 if( status != 0 || result == nullptr)
71 throw SocketException("Failed to resolve address (getaddrinfo)", false);
72
73 std::unique_ptr<addrinfo, decltype(&::freeaddrinfo)>
74 result_guard(result, &::freeaddrinfo);
75
76 if( result->ai_addr == nullptr || result->ai_addrlen < sizeof(sockaddr_in))
77 throw SocketException("Resolver returned an invalid IPv4 address", false);
78
79 sockaddr_in addr = {};
80 std::memcpy( &addr, result->ai_addr, sizeof(addr));
81 return inet_ntoa( addr.sin_addr);
82}
83
84uint16_t NET::resolveService( const std::string& service, const std::string& protocol)
85{
86 struct servent* serv;
87 if( protocol.empty())
88 serv = getservbyname( service.c_str(), nullptr);
89 else
90 serv = getservbyname( service.c_str(), protocol.c_str());
91
92 if(serv)
93 return ntohs( static_cast<uint16_t>(serv->s_port));
94 return 0;
95}
96
97/* This function does not understand interfaces with multiple addresses
98 * For example when using zeroconf (eth0, eth0:avahi) it would only list eth0
99 * In this case eth0 does not have an ip address yet and getInterfaceAddress("eth0") throws
100std::vector<std::string> NET::getNetworkInterfaces()
101{
102 std::vector<std::string> ret;
103 struct if_nameindex* index = if_nameindex();
104 if(index == nullptr) return ret;
105
106 for( int i = 0; (index[i].if_index != 0); ++i)
107 ret.push_back( std::string( index[i].if_name));
108
109 if_freenameindex(index);
110 return ret;
111}
112*/
113
114std::vector<std::string> NET::getNetworkInterfaces()
115{
116 std::vector<std::string> ret;
117 // maximum of number interfaces
118 char data[32 * sizeof(struct ifreq)];
119 struct ifconf conf;
120 struct ifreq *ifr;
121
122 conf.ifc_len = sizeof(data);
123 conf.ifc_buf = (caddr_t) data;
124
125 temp_socket sock;
126 if( ioctl( sock.nativeHandle(), SIOCGIFCONF, &conf) < 0)
127 throw SocketException("ioctl failed (getNetworkInterfaces)");
128
129 ifr = (struct ifreq*)data;
130 while( (char*)ifr < data+conf.ifc_len)
131 {
132 switch(ifr->ifr_addr.sa_family)
133 {
134 case AF_INET:
135 ret.emplace_back( ifr->ifr_name);
136 //printf("%s : %s\n", ifr->ifr_name, inet_ntop(ifr->ifr_addr.sa_family, &((struct sockaddr_in*)&ifr->ifr_addr)->sin_addr, addrbuf, sizeof(addrbuf)));
137 break;
138 #if 0
139 case AF_INET6:
140 printf("%s : %s\n", ifr->ifr_name, inet_ntop(ifr->ifr_addr.sa_family, &((struct sockaddr_in6*)&ifr->ifr_addr)->sin6_addr, addrbuf, sizeof(addrbuf)));
141 break;
142 #endif
143 default:
144 break;
145 }
146 ++ifr;
147 }
148 return ret;
149}
150
151std::string NET::getInterfaceAddress( std::string_view interface)
152{
153 struct ifreq ifr;
154 assign_ifreq( ifr, interface);
155
156 temp_socket sock;
157 if( ioctl( sock.nativeHandle(), SIOCGIFADDR, &ifr) < 0)
158 throw SocketException("ioctl failed (getInterfaceAddress)");
159
160 return inet_ntoa( ipv4Address(ifr.ifr_addr));
161}
162
163void NET::setInterfaceAddress( std::string_view interface, std::string_view address)
164{
165 struct ifreq ifr;
166 assign_ifreq( ifr, interface);
167 assign_ipv4( ifr.ifr_addr, address);
168
169 temp_socket sock;
170 if( ioctl( sock.nativeHandle(), SIOCSIFADDR, &ifr) < 0)
171 throw SocketException("ioctl failed (setInterfaceAddress)");
172}
173
174std::string NET::getBroadcastAddress( std::string_view interface)
175{
176 struct ifreq ifr;
177 assign_ifreq( ifr, interface);
178
179 temp_socket sock;
180 if( ioctl( sock.nativeHandle(), SIOCGIFBRDADDR, &ifr) < 0)
181 throw SocketException("ioctl failed (getBroadcastAddress)");
182
183 return inet_ntoa( ipv4Address(ifr.ifr_broadaddr));
184}
185
186void NET::setBroadcastAddress( std::string_view interface, std::string_view address)
187{
188 struct ifreq ifr;
189 assign_ifreq( ifr, interface);
190 assign_ipv4( ifr.ifr_broadaddr, address);
191
192 temp_socket sock;
193 if( ioctl( sock.nativeHandle(), SIOCSIFBRDADDR, &ifr) < 0)
194 throw SocketException("ioctl failed (setBroadcastAddress)");
195}
196
197std::string NET::getNetmask( std::string_view interface)
198{
199 struct ifreq ifr;
200 assign_ifreq( ifr, interface);
201
202 temp_socket sock;
203 if( ioctl( sock.nativeHandle(), SIOCGIFNETMASK, &ifr) < 0)
204 throw SocketException("ioctl failed (getNetmask)");
205
206 return inet_ntoa( ipv4Address(ifr.ifr_netmask));
207}
208
209void NET::setNetmask( std::string_view interface, std::string_view address)
210{
211 struct ifreq ifr;
212 assign_ifreq( ifr, interface);
213 assign_ipv4( ifr.ifr_netmask, address);
214
215 temp_socket sock;
216 if( ioctl( sock.nativeHandle(), SIOCSIFNETMASK, &ifr) < 0)
217 throw SocketException("ioctl failed (setNetmask)");
218}
219
220std::string NET::getDestinationAddress( std::string_view interface)
221{
222 struct ifreq ifr;
223 assign_ifreq( ifr, interface);
224
225 temp_socket sock;
226 if( ioctl( sock.nativeHandle(), SIOCGIFDSTADDR, &ifr) < 0)
227 throw SocketException("ioctl failed (getDestinationAddress)");
228
229 return inet_ntoa( ipv4Address(ifr.ifr_dstaddr));
230}
231
232void NET::setDestinationAddress( std::string_view interface, std::string_view address)
233{
234 struct ifreq ifr;
235 assign_ifreq( ifr, interface);
236 assign_ipv4( ifr.ifr_dstaddr, address);
237
238 temp_socket sock;
239 if( ioctl( sock.nativeHandle(), SIOCSIFDSTADDR, &ifr) < 0)
240 throw SocketException("ioctl failed (setDestinationAddress)");
241}
242
243int NET::getMTU( std::string_view interface)
244{
245 struct ifreq ifr;
246 assign_ifreq( ifr, interface);
247
248 temp_socket sock;
249 if( ioctl( sock.nativeHandle(), SIOCGIFMTU, &ifr) < 0)
250 throw SocketException("ioctl failed (getMTU)");
251
252 return ifr.ifr_mtu;
253}
254
255void NET::setMTU( std::string_view interface, int mtu)
256{
257 struct ifreq ifr;
258 assign_ifreq( ifr, interface);
259
260 ifr.ifr_mtu = mtu;
261
262 temp_socket sock;
263 if( ioctl( sock.nativeHandle(), SIOCSIFMTU, &ifr) < 0)
264 throw SocketException("ioctl failed (setMTU)");
265}
266
267std::string NET::getHardwareAddress( std::string_view interface)
268{
269 struct ifreq ifr;
270 assign_ifreq( ifr, interface);
271
272 temp_socket sock;
273 if( ioctl( sock.nativeHandle(), SIOCGIFHWADDR, &ifr) < 0)
274 throw SocketException("ioctl failed (getHardwareAddress)");
275
276 switch( ifr.ifr_hwaddr.sa_family) {
277 default:
278 throw SocketException("Invalid Hardware type (getHardwareAddress)");
279 break;
280 case ARPHRD_NETROM:
281 case ARPHRD_ETHER:
282 case ARPHRD_PPP:
283 case ARPHRD_EETHER:
284 case ARPHRD_IEEE802:
285 break;
286 }
287
288 char mac[20];
289 auto addr = reinterpret_cast<unsigned char*>( ifr.ifr_addr.sa_data);
290 int len = sprintf(mac, "%02x:%02x:%02x:%02x:%02x:%02x", addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
291 return std::string(mac, static_cast<size_t>(len));
292}
A socket representing a basic communication endpoint.
Signals a problem with the execution of a socket call.