Stun Server  Compliant with the latest RFCs including 5389, 5769, and 5780
discover the local host's own external IP address
stunsocket.cpp
Go to the documentation of this file.
1 /*
2  Copyright 2011 John Selbie
3 
4  Licensed under the Apache License, Version 2.0 (the "License");
5  you may not use this file except in compliance with the License.
6  You may obtain a copy of the License at
7 
8  http://www.apache.org/licenses/LICENSE-2.0
9 
10  Unless required by applicable law or agreed to in writing, software
11  distributed under the License is distributed on an "AS IS" BASIS,
12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  See the License for the specific language governing permissions and
14  limitations under the License.
15 */
16 
17 #include "commonincludes.hpp"
18 #include "stuncore.h"
19 #include "stunsocket.h"
20 
22 _sock(-1),
23 _role(RolePP)
24 {
25 
26 }
27 
29 {
30  Close();
31 }
32 
34 {
35  _sock = -1;
38  _role = RolePP;
39 }
40 
42 {
43  if (_sock != -1)
44  {
45  close(_sock);
46  _sock = -1;
47  }
48  Reset();
49 }
50 
52 {
53  return (_sock != -1);
54 }
55 
57 {
58  if (sock == -1)
59  {
60  ASSERT(false);
61  return E_INVALIDARG;
62  }
63 
64  if (sock != _sock)
65  {
66  // close any existing socket
67  Close(); // this will also call "Reset"
68  _sock = sock;
69  }
70 
72  return S_OK;
73 }
74 
76 {
77  int sock = _sock;
78  Reset();
79  return sock;
80 }
81 
83 {
84  return _sock;
85 }
86 
88 {
89  return _addrlocal;
90 }
91 
93 {
94  return _addrremote;
95 }
96 
97 
99 {
100  ASSERT(_sock != -1);
101  return _role;
102 }
103 
105 {
106  _role = role;
107 }
108 
109 
110 
111 // About the "packet info option"
112 // What we are trying to do is enable the socket to be able to provide the "destination address"
113 // for packets we receive. However, Linux, BSD, and MacOS all differ in what the
114 // socket option is. And it differs even differently between IPV4 and IPV6 across these operating systems.
115 // So we have the "try one or the other" implementation based on what's DEFINED
116 // On some operating systems, there's only one option defined. Other's have both, but only one works!
117 // So we have to try them both
118 
119 HRESULT CStunSocket::EnablePktInfoImpl(int level, int option1, int option2, bool fEnable)
120 {
121  HRESULT hr = S_OK;
122  int enable = fEnable?1:0;
123  int ret = -1;
124 
125 
126  ChkIfA((option1 == -1) && (option2 == -1), E_FAIL);
127 
128  if (option1 != -1)
129  {
130  ret = setsockopt(_sock, level, option1, &enable, sizeof(enable));
131  }
132 
133  if ((ret < 0) && (option2 != -1))
134  {
135  enable = fEnable?1:0;
136  ret = setsockopt(_sock, level, option2, &enable, sizeof(enable));
137  }
138 
139  ChkIfA(ret < 0, ERRNOHR);
140 
141 Cleanup:
142  return hr;
143 }
144 
146 {
147  int level = IPPROTO_IP;
148  int option1 = -1;
149  int option2 = -1;
150 
151 #ifdef IP_PKTINFO
152  option1 = IP_PKTINFO;
153 #endif
154 
155 #ifdef IP_RECVDSTADDR
156  option2 = IP_RECVDSTADDR;
157 #endif
158 
159  return EnablePktInfoImpl(level, option1, option2, fEnable);
160 }
161 
163 {
164  int level = IPPROTO_IPV6;
165  int option1 = -1;
166  int option2 = -1;
167 
168 #ifdef IPV6_RECVPKTINFO
169  option1 = IPV6_RECVPKTINFO;
170 #endif
171 
172 #ifdef IPV6_PKTINFO
173  option2 = IPV6_PKTINFO;
174 #endif
175 
176  return EnablePktInfoImpl(level, option1, option2, fEnable);
177 }
178 
180 {
181  int family = _addrlocal.GetFamily();
182  HRESULT hr;
183 
184  if (family == AF_INET)
185  {
186  hr = EnablePktInfo_IPV4(fEnable);
187  }
188  else
189  {
190  hr = EnablePktInfo_IPV6(fEnable);
191  }
192 
193  return hr;
194 }
195 
197 {
198  int optname = -1;
199  int result = 0;
200  HRESULT hr = S_OK;
201  int enabled = 1;
202 
203 #ifdef IPV6_BINDV6ONLY
204  optname = IPV6_BINDV6ONLY;
205 #elif IPV6_V6ONLY
206  optname = IPV6_V6ONLY;
207 #else
208  return E_NOTIMPL;
209 #endif
210 
211  result = setsockopt(sock, IPPROTO_IPV6, optname, (char *)&enabled, sizeof(enabled));
212  hr = (result == 0) ? S_OK : ERRNOHR ;
213 
214  return hr;
215 }
216 
218 {
219  HRESULT hr = S_OK;
220  int result;
221  int flags;
222 
223  flags = ::fcntl(_sock, F_GETFL, 0);
224 
225  ChkIf(flags == -1, ERRNOHR);
226 
227  if (fEnable)
228  {
229  flags |= O_NONBLOCK;
230  }
231  else
232  {
233  flags &= ~(O_NONBLOCK);
234  }
235 
236  result = fcntl(_sock , F_SETFL , flags);
237 
238  ChkIf(result == -1, ERRNOHR);
239 
240 Cleanup:
241  return hr;
242 }
243 
244 
246 {
247  sockaddr_storage addrLocal = {};
248  sockaddr_storage addrRemote = {};
249  socklen_t len;
250  int ret;
251 
252  ASSERT(_sock != -1);
253  if (_sock == -1)
254  {
255  return;
256  }
257 
258 
259  len = sizeof(addrLocal);
260  ret = ::getsockname(_sock, (sockaddr*)&addrLocal, &len);
261  if (ret != -1)
262  {
263  _addrlocal = addrLocal;
264  }
265 
266  len = sizeof(addrRemote);
267  ret = ::getpeername(_sock, (sockaddr*)&addrRemote, &len);
268  if (ret != -1)
269  {
270  _addrremote = addrRemote;
271  }
272 }
273 
274 
275 
276 HRESULT CStunSocket::InitCommon(int socktype, const CSocketAddress& addrlocal, SocketRole role, bool fSetReuseFlag)
277 {
278  int sock = -1;
279  int ret;
280  HRESULT hr = S_OK;
281 
282  ASSERT((socktype == SOCK_DGRAM)||(socktype==SOCK_STREAM));
283 
284  sock = socket(addrlocal.GetFamily(), socktype, 0);
285  ChkIf(sock < 0, ERRNOHR);
286 
287  if (addrlocal.GetFamily() == AF_INET6)
288  {
289  // Don't allow IPv6 socket to receive binding request from IPv4 client
290  // Because if we don't then an IPv4 client will get an IPv6 mapped address in the binding response
291  // I'm pretty sure you have to call this before bind()
292  // Intentionally ignoring result
293  (void)SetV6Only(sock);
294  }
295 
296  if (fSetReuseFlag)
297  {
298  int fAllow = 1;
299  ret = ::setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &fAllow, sizeof(fAllow));
300  ChkIf(ret == -1, ERRNOHR);
301  }
302 
303  ret = bind(sock, addrlocal.GetSockAddr(), addrlocal.GetSockAddrLength());
304  ChkIf(ret == -1, ERRNOHR);
305 
306  Attach(sock);
307  sock = -1;
308 
309  SetRole(role);
310 
311 Cleanup:
312  if (sock != -1)
313  {
314  close(sock);
315  sock = -1;
316  }
317  return hr;
318 }
319 
320 
321 
323 {
324  return InitCommon(SOCK_DGRAM, local, role, false);
325 }
326 
327 HRESULT CStunSocket::TCPInit(const CSocketAddress& local, SocketRole role, bool fSetReuseFlag)
328 {
329  return InitCommon(SOCK_STREAM, local, role, fSetReuseFlag);
330 }
331 
332 
#define S_OK
Definition: hresult.h:46
#define ASSERT(expr)
HRESULT EnablePktInfoImpl(int level, int option1, int option2, bool fEnable)
Definition: stunsocket.cpp:119
void UpdateAddresses()
Definition: stunsocket.cpp:245
socklen_t GetSockAddrLength() const
void Close()
Definition: stunsocket.cpp:41
void SetRole(SocketRole role)
Definition: stunsocket.cpp:104
CSocketAddress _addrlocal
Definition: stunsocket.h:26
#define ChkIf(expr, hrerror)
Definition: chkmacros.h:63
HRESULT SetV6Only(int sock)
Definition: stunsocket.cpp:196
HRESULT EnablePktInfo_IPV4(bool fEnable)
Definition: stunsocket.cpp:145
int GetSocketHandle() const
Definition: stunsocket.cpp:82
HRESULT Attach(int sock)
Definition: stunsocket.cpp:56
SocketRole GetRole() const
Definition: stunsocket.cpp:98
SocketRole
Definition: socketrole.h:22
int32_t HRESULT
Definition: hresult.h:22
int Detach()
Definition: stunsocket.cpp:75
bool IsValid()
Definition: stunsocket.cpp:51
const sockaddr * GetSockAddr() const
HRESULT EnablePktInfoOption(bool fEnable)
Definition: stunsocket.cpp:179
void Reset()
Definition: stunsocket.cpp:33
#define E_INVALIDARG
Definition: hresult.h:51
HRESULT InitCommon(int socktype, const CSocketAddress &addrlocal, SocketRole role, bool fSetReuseFlag)
Definition: stunsocket.cpp:276
#define E_FAIL
Definition: hresult.h:56
CSocketAddress _addrremote
Definition: stunsocket.h:27
const CSocketAddress & GetRemoteAddress() const
Definition: stunsocket.cpp:92
HRESULT EnablePktInfo_IPV6(bool fEnable)
Definition: stunsocket.cpp:162
HRESULT UDPInit(const CSocketAddress &local, SocketRole role)
Definition: stunsocket.cpp:322
uint16_t GetFamily() const
#define E_NOTIMPL
Definition: hresult.h:49
HRESULT TCPInit(const CSocketAddress &local, SocketRole role, bool fSetReuseFlag)
Definition: stunsocket.cpp:327
HRESULT SetNonBlocking(bool fEnable)
Definition: stunsocket.cpp:217
#define ChkIfA(expr, hrerror)
Definition: chkmacros.h:84
#define ERRNOHR
Definition: hresult.h:42
const CSocketAddress & GetLocalAddress() const
Definition: stunsocket.cpp:87
SocketRole _role
Definition: stunsocket.h:28