Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
Socket.cpp
1/*
2* Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3*/
4#include "Stroika/Foundation/StroikaPreComp.h"
5
8#include "Stroika/Foundation/Execution/Activity.h"
10
11#include "Socket-Private_.h"
12
13#include "Socket.h"
14
15// Comment this in to turn on aggressive noisy DbgTrace in this module
16//#define USE_NOISY_TRACE_IN_THIS_MODULE_ 1
17
18using namespace Stroika::Foundation;
20using namespace Stroika::Foundation::Execution;
21using namespace Stroika::Foundation::Memory;
22using namespace Stroika::Foundation::IO;
24
25using namespace Stroika::Foundation::IO::Network::PRIVATE_;
26
28
29/*
30 * Notes:
31 * http://stackoverflow.com/questions/2693709/what-was-the-motivation-for-adding-the-ipv6-v6only-flag
32 * Windows:
33 * https://msdn.microsoft.com/en-us/library/windows/desktop/bb513665(v=vs.85).aspx
34 * Windows Vista and later only
35 *
36 * not sure how to handle this best cuz not every OS will support dual-stack (or will it?)
37 *
38 * So assume no dual-stack sockets. That seems best --LGP 2017-04-24
39 */
40namespace {
41 constexpr bool kUseDualStackSockets_ = false; // opposite of IPV6_V6ONLY
42}
43
44/*
45 ********************************************************************************
46 ******************************** Network::Socket *******************************
47 ********************************************************************************
48 */
49Socket::PlatformNativeHandle Socket::_Protected::mkLowLevelSocket_ (SocketAddress::FamilyType family, Socket::Type socketKind,
50 const optional<IPPROTO>& protocol)
51{
52#if qStroika_Foundation_Common_Platform_Windows
53 IO::Network::Platform::Windows::WinSock::AssureStarted ();
54#endif
56#if qStroika_Foundation_Common_Platform_POSIX
57 sfd = Handle_ErrNoResultInterruption ([=] () -> int {
58 return socket (static_cast<int> (family), static_cast<int> (socketKind), static_cast<int> (NullCoalesce (protocol)));
59 });
60#elif qStroika_Foundation_Common_Platform_Windows
61 DISABLE_COMPILER_MSC_WARNING_START (28193) // dump warning about examining sfd
62 ThrowWSASystemErrorIfSOCKET_ERROR (
63 sfd = ::socket (static_cast<int> (family), static_cast<int> (socketKind), static_cast<int> (NullCoalesce (protocol))));
64 DISABLE_COMPILER_MSC_WARNING_END (28193)
65#else
67#endif
68 if (family == SocketAddress::FamilyType::INET6) {
69 int useIPV6Only = not kUseDualStackSockets_;
70#if qStroika_Foundation_Common_Platform_Linux
71 // Linux follows the RFC, and uses dual-stack mode by default
72 constexpr bool kOSDefaultIPV6Only_{false};
73 bool mustSet = useIPV6Only != kOSDefaultIPV6Only_;
74#elif qPlatfom_Windows
75 // Windows defaults to NOT dual sockets, so nothing todo for windows
76 constexpr bool kOSDefaultIPV6Only_{true};
77 bool mustSet = useIPV6Only != kOSDefaultIPV6Only_;
78#else
79 bool mustSet = true;
80#endif
81 if (mustSet) {
82 if (::setsockopt (sfd, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<const char*> (&useIPV6Only), sizeof (useIPV6Only)) < 0) {
84 }
85 }
86 }
87 return sfd;
88}
89
90namespace {
91 // Not all operating systems support all flavors of socketpair (e.g. MacOS only supports for AF_UNIX)
92 auto mkLowLevelSocketPair_BackCompat_ (SocketAddress::FamilyType family, Socket::Type socketKind, const optional<IPPROTO>& protocol)
93 -> tuple<PlatformNativeHandle, PlatformNativeHandle>
94 {
95 // auto connectionOrientedMaster = ConnectionOrientedMasterSocket::New (SocketAddress::FamilyType::INET, Socket::Type::STREAM);
96 PlatformNativeHandle masterSocket = Socket::_Protected::mkLowLevelSocket_ (family, socketKind, protocol);
97#if qStroika_Foundation_Common_Platform_POSIX
98 [[maybe_unused]] auto&& cleanup = Execution::Finally ([&] () noexcept { ::close (masterSocket); });
99#elif qStroika_Foundation_Common_Platform_Windows
100 [[maybe_unused]] auto&& cleanup = Execution::Finally ([&] () noexcept { ::closesocket (masterSocket); });
101#endif
102
103 // connectionOrientedMaster.Bind (SocketAddress{IO::Network::V4::kLocalhost});
104 SocketAddress localhost =
105 (family == SocketAddress::INET or family == SocketAddress::INET6) ? SocketAddress{LocalHost (family)} : SocketAddress{};
106 sockaddr_storage localhost_ss = localhost.As<sockaddr_storage> ();
107
108 if (false) {
109 const int one = 1;
110 Verify (::setsockopt (masterSocket, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<const char*> (&one), sizeof (one)) == 0);
111 }
112#if qStroika_Foundation_Common_Platform_POSIX
113 Handle_ErrNoResultInterruption ([masterSocket, &localhost, &localhost_ss] () -> int {
114 return ::bind (masterSocket, (sockaddr*)&localhost_ss, static_cast<int> (localhost.GetRequiredSize ()));
115 });
116#elif qStroika_Foundation_Common_Platform_Windows
117 ThrowWSASystemErrorIfSOCKET_ERROR (::bind (masterSocket, (sockaddr*)&localhost_ss, static_cast<int> (localhost.GetRequiredSize ())));
118#endif
119
120 // connectionOrientedMaster.Listen (1);
121#if qStroika_Foundation_Common_Platform_POSIX
122 Handle_ErrNoResultInterruption ([masterSocket] () -> int { return ::listen (masterSocket, 1); });
123#elif qStroika_Foundation_Common_Platform_Windows
124 ThrowWSASystemErrorIfSOCKET_ERROR (::listen (masterSocket, 1));
125#endif
126
127 // fReadSocket_ = ConnectionOrientedStreamSocket::NewConnection (*connectionOrientedMaster.GetLocalAddress ());
128 SocketAddress masterSocketLocalAddress;
129 sockaddr_storage masterSocketLocalAddress_ss;
130 {
131 socklen_t len = sizeof (masterSocketLocalAddress_ss);
132 Verify (::getsockname (static_cast<int> (masterSocket), (sockaddr*)&masterSocketLocalAddress_ss, &len) == 0);
133 masterSocketLocalAddress = masterSocketLocalAddress_ss;
134 }
135 PlatformNativeHandle endOne = Socket::_Protected::mkLowLevelSocket_ (family, socketKind, protocol);
136 bool succeeded = false;
137 [[maybe_unused]] auto&& cleanup2 = Execution::Finally ([&] () noexcept {
138 if (not succeeded) {
139#if qStroika_Foundation_Common_Platform_POSIX
140 ::close (endOne);
141#elif qStroika_Foundation_Common_Platform_Windows
142 ::closesocket (endOne);
143#endif
144 }
145 });
146#if qStroika_Foundation_Common_Platform_POSIX
147 Handle_ErrNoResultInterruption ([&] () -> int {
148 return ::connect (endOne, (sockaddr*)&masterSocketLocalAddress_ss, static_cast<int> (masterSocketLocalAddress.GetRequiredSize ()));
149 });
150#elif qStroika_Foundation_Common_Platform_Windows
151 ThrowWSASystemErrorIfSOCKET_ERROR (
152 ::connect (endOne, (sockaddr*)&masterSocketLocalAddress_ss, static_cast<int> (masterSocketLocalAddress.GetRequiredSize ())));
153#endif
154
155 // fWriteSocket_ = connectionOrientedMaster.Accept ();
156 sockaddr_storage peer{};
157 socklen_t sz = sizeof (peer);
158#if qStroika_Foundation_Common_Platform_POSIX
159 PlatformNativeHandle endTwo =
160 Handle_ErrNoResultInterruption ([&] () -> int { return ::accept (masterSocket, reinterpret_cast<sockaddr*> (&peer), &sz); });
161#elif qStroika_Foundation_Common_Platform_Windows
162 PlatformNativeHandle endTwo = ThrowWSASystemErrorIfSOCKET_ERROR (::accept (masterSocket, reinterpret_cast<sockaddr*> (&peer), &sz));
163#endif
164 succeeded = true; // so endOne not closed
165 return make_tuple (endOne, endTwo);
166 }
167}
168
169auto Socket::_Protected::mkLowLevelSocketPair_ (SocketAddress::FamilyType family, Socket::Type socketKind, const optional<IPPROTO>& protocol)
170 -> tuple<PlatformNativeHandle, PlatformNativeHandle>
171{
172#if qStroika_Foundation_Common_Platform_POSIX
173 // docs in https://man7.org/linux/man-pages/man2/socketpair.2.html suggest dont have ot worry about EINTR
174 int sfd[2];
175 auto r = ::socketpair (static_cast<int> (family), static_cast<int> (socketKind), static_cast<int> (NullCoalesce (protocol)), sfd);
176 Assert (r == 0 or r == -1);
177 if (r == -1) {
178 if (errno == EOPNOTSUPP) {
179 return mkLowLevelSocketPair_BackCompat_ (family, socketKind, protocol);
180 }
181 Assert (errno != EINTR);
183 }
184 return make_tuple (sfd[0], sfd[1]);
185#elif qStroika_Foundation_Common_Platform_Windows
186 return mkLowLevelSocketPair_BackCompat_ (family, socketKind, protocol);
187#endif
188}
189
190/*
191 ********************************************************************************
192 ***************************** Network::Socket::Ptr *****************************
193 ********************************************************************************
194 */
196{
197 Debug::AssertExternallySynchronizedMutex::WriteContext declareContext{this->_fThisAssertExternallySynchronized};
198 PlatformNativeHandle h = kINVALID_NATIVE_HANDLE_;
199 if (fRep_ != nullptr) {
200 h = fRep_->Detach ();
201 }
202 fRep_.reset ();
203 return h;
204}
205
207{
208 Debug::AssertExternallySynchronizedMutex::ReadContext declareContext{this->_fThisAssertExternallySynchronized};
209 return getsockopt<Type> (SOL_SOCKET, SO_TYPE);
210}
211
212void Socket::Ptr::Bind (const SocketAddress& sockAddr, BindFlags bindFlags)
213{
214 Debug::TraceContextBumper ctx{"IO::Network::Socket::Bind", "sockAddr={} bindFlags.fReUseAddr={}"_f, sockAddr, bindFlags.fSO_REUSEADDR};
215 Debug::AssertExternallySynchronizedMutex::WriteContext declareContext{this->_fThisAssertExternallySynchronized};
216 RequireNotNull (fRep_); // Construct with Socket::Kind::SOCKET_STREAM?
217
218 auto bindingActivity =
219 Execution::LazyEvalActivity{[&] () -> Characters::String { return "binding to "sv + Characters::ToString (sockAddr); }};
220 [[maybe_unused]] auto&& declareActivity = Execution::DeclareActivity{&bindingActivity};
221
222 // Indicates that the rules used in validating addresses supplied in a bind(2) call should allow
223 // reuse of local addresses. For AF_INET sockets this means that a socket may bind, except when
224 // there is an active listening socket bound to the address. When the listening socket is bound
225 // to INADDR_ANY with a specific port then it is not possible to bind to this port for any local address.
226 setsockopt<int> (SOL_SOCKET, SO_REUSEADDR, bindFlags.fSO_REUSEADDR ? 1 : 0);
227
228 sockaddr_storage useSockAddr = sockAddr.As<sockaddr_storage> ();
229 PlatformNativeHandle sfd = fRep_->GetNativeSocket ();
230 try {
231#if qStroika_Foundation_Common_Platform_Windows
232 ThrowWSASystemErrorIfSOCKET_ERROR (::bind (sfd, (sockaddr*)&useSockAddr, static_cast<int> (sockAddr.GetRequiredSize ())));
233#else
235 [sfd, &useSockAddr, &sockAddr] () -> int { return ::bind (sfd, (sockaddr*)&useSockAddr, sockAddr.GetRequiredSize ()); });
236#endif
237 }
238 catch (const Execution::SystemErrorException<>& e) {
239 if (e.code () == errc::permission_denied) {
240 Throw (SystemErrorException<>{e.code (), e.GetBasicErrorMessage () + "(probably already bound with SO_EXCLUSIVEADDRUSE)"sv});
241 }
242 else {
243 ReThrow ();
244 }
245 }
246 catch (const system_error& e) {
247 if (e.code () == errc::permission_denied) {
248 Throw (SystemErrorException<>{e.code (), "(probably already bound with SO_EXCLUSIVEADDRUSE)"sv});
249 }
250 else {
251 ReThrow ();
252 }
253 }
254}
255
257{
258 Debug::AssertExternallySynchronizedMutex::ReadContext declareContext{this->_fThisAssertExternallySynchronized};
259 if (fRep_ != nullptr) {
260 return fRep_->GetNativeSocket () != kINVALID_NATIVE_HANDLE_;
261 }
262 return false;
263}
264
266{
267 Debug::AssertExternallySynchronizedMutex::ReadContext declareContext{this->_fThisAssertExternallySynchronized};
268 StringBuilder sb;
269 if (fRep_ == nullptr) {
270 sb << "nullptr"sv;
271 }
272 else {
273 sb << "{"sv;
274 sb << "Native-Socket: "sv
275 << ((fRep_->GetNativeSocket () == kINVALID_NATIVE_HANDLE_) ? "CLOSED"sv : Characters::ToString (fRep_->GetNativeSocket ()));
276 if (auto ola = GetLocalAddress ()) {
277 sb << ", Local-Address: "sv << *ola;
278 }
279 sb << "}"sv;
280 }
281 return sb;
282}
#define AssertNotImplemented()
Definition Assertions.h:401
#define RequireNotNull(p)
Definition Assertions.h:347
#define AssertNotReached()
Definition Assertions.h:355
#define Verify(c)
Definition Assertions.h:419
Similar to String, but intended to more efficiently construct a String. Mutable type (String is large...
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
shared_lock< const AssertExternallySynchronizedMutex > ReadContext
Instantiate AssertExternallySynchronizedMutex::ReadContext to designate an area of code where protect...
unique_lock< AssertExternallySynchronizedMutex > WriteContext
Instantiate AssertExternallySynchronizedMutex::WriteContext to designate an area of code where protec...
nonvirtual Characters::String GetBasicErrorMessage() const
nonvirtual PlatformNativeHandle Detach()
Definition Socket.cpp:195
nonvirtual void Bind(const SocketAddress &sockAddr, BindFlags bindFlags=BindFlags{})
Definition Socket.cpp:212
FamilyType
Socket address family - also sometimes referred to as domain (argument to ::socket calls it domain)
String ToString(T &&t, ARGS... args)
Return a debug-friendly, display version of the argument: not guaranteed parsable or usable except fo...
Definition ToString.inl:465
void Throw(T &&e2Throw)
identical to builtin C++ 'throw' except that it does helpful, type dependent DbgTrace() messages firs...
Definition Throw.inl:43
void ThrowPOSIXErrNo(errno_t errNo=errno)
treats errNo as a POSIX errno value, and throws a SystemError (subclass of @std::system_error) except...
auto Handle_ErrNoResultInterruption(CALL call) -> decltype(call())
Handle UNIX EINTR system call behavior - fairly transparently - just effectively removes them from th...
auto Finally(FUNCTION &&f) -> Private_::FinallySentry< FUNCTION >
Definition Finally.inl:31
constexpr InternetAddress LocalHost(SocketAddress::FamilyType fm)
return V4::kLocalhost or V6::kLocalhost depending on argument address family