Stroika Library 3.0d25
 
Loading...
Searching...
No Matches
Socket-Private_.h
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#ifndef _Stroika_Foundation_IO_Network_Socket_Private_h_
5#define _Stroika_Foundation_IO_Network_Socket_Private_h_ 1
6
7#include "Stroika/Foundation/StroikaPreComp.h"
8
9// ONLY INCLUDED BY Network-Socket CPP implementation code. Do not include directly
10
11#include <cstdlib>
12#include <sys/types.h>
13
14#if qStroika_Platform_POSIX
15#include <netdb.h>
16#include <poll.h>
17#include <sys/ioctl.h>
18#include <sys/socket.h>
19#include <unistd.h>
20#elif qStroika_Platform_Windows
21#include <winsock2.h>
22
23#include <io.h>
24#include <ws2tcpip.h>
25
26#include <Mstcpip.h>
27#endif
28#if qStroika_Platform_Linux
29#include <netinet/in.h>
30#include <netinet/tcp.h>
31#endif
32
35#include "Stroika/Foundation/Execution/OperationNotSupportedException.h"
36#if qStroika_Platform_Windows
37#include "Stroika/Foundation/Execution/Platform/Windows/Exception.h"
38#include "Stroika/Foundation/IO/Network/Platform/Windows/WinSock.h"
39#endif
40
42
43/*
44 ********************************************************************************
45 ***************************** Implementation Details ***************************
46 ********************************************************************************
47 */
49
50 namespace PRIVATE_ {
51 using namespace Stroika::Foundation;
52 using namespace Stroika::Foundation::Characters;
53 using namespace Stroika::Foundation::Execution;
54 using namespace Stroika::Foundation::Memory;
55 using namespace Stroika::Foundation::IO;
57
59
60#if qStroika_Platform_POSIX
61 /*
62 * Internally we use this -1 value to mean invalid socket, but keep that a private implementation
63 * detail, since I'm not sure it will be good for all socket implementations?
64 */
65 constexpr Socket::PlatformNativeHandle kINVALID_NATIVE_HANDLE_ = -1; // right value??
66#elif qStroika_Platform_Windows
67 constexpr Socket::PlatformNativeHandle kINVALID_NATIVE_HANDLE_ = INVALID_SOCKET;
68#endif
69
70 template <typename T>
71 void BreakWriteIntoParts_ (span<const T> data, size_t maxSendAtATime, const function<size_t (span<const T>)>& writeFunc)
72 {
73#if USE_NOISY_TRACE_IN_THIS_MODULE_
74 Debug::TraceContextBumper ctx{Stroika_Foundation_Debug_OptionalizeTraceArgs ("{}::BreakWriteIntoParts_", "n={}"_f, data.size ())};
75#endif
76 ptrdiff_t amountToSend = data.size ();
77 ptrdiff_t amountRemainingToSend = amountToSend;
78 const T* remainingSendFrom = data.data ();
79 while (amountRemainingToSend > 0) {
80 size_t amountToSendThisIteration = min<size_t> (maxSendAtATime, amountRemainingToSend);
81 size_t amountSent = writeFunc (span{remainingSendFrom, amountToSendThisIteration});
82 Assert (amountSent <= amountToSendThisIteration);
83 Assert (static_cast<size_t> (amountRemainingToSend) >= amountSent);
84 amountRemainingToSend -= amountSent;
85 remainingSendFrom += amountSent;
86#if USE_NOISY_TRACE_IN_THIS_MODULE_
87 if (amountSent < amountToSendThisIteration) {
88 DbgTrace (
89 L"write broken into parts - amountSent=%lld out of amountToSendThisIteration=%lld, amountRemainingToSend=%lld",
90 static_cast<long long> (amountSent), static_cast<long long> (amountToSendThisIteration),
91 static_cast<long long> (amountRemainingToSend));
92 }
93#endif
94 }
95 }
96
97 template <typename BASE_REP>
98 struct BackSocketImpl_ : public BASE_REP {
100 BackSocketImpl_ (Socket::PlatformNativeHandle sd)
101 : fSD_{sd}
102 {
103 }
104 ~BackSocketImpl_ ()
105 {
106 if (fSD_ != kINVALID_NATIVE_HANDLE_) {
107 Close ();
108 }
109 }
110 virtual Socket::PlatformNativeHandle Detach () override
111 {
112 Debug::AssertExternallySynchronizedChecker::WriteContext declareContext{fThisAssertExternallySynchronized};
114 fSD_ = kINVALID_NATIVE_HANDLE_;
115 return h;
116 }
117 virtual void Shutdown (Socket::ShutdownTarget shutdownTarget) override
118 {
119 Debug::AssertExternallySynchronizedChecker::WriteContext declareContext{fThisAssertExternallySynchronized};
120 if (fSD_ != kINVALID_NATIVE_HANDLE_) {
121 // Intentionally ignore shutdown results because in most cases there is nothing todo (maybe in some cases we should log?)
122 switch (shutdownTarget) {
123 case Socket::ShutdownTarget::eReads:
124#if qStroika_Platform_POSIX
125 ::shutdown (fSD_, SHUT_RD);
126#elif qStroika_Platform_Windows
127 ::shutdown (fSD_, SD_RECEIVE);
128#endif
129 break;
130 case Socket::ShutdownTarget::eWrites:
131// I believe this triggers TCP FIN
132#if qStroika_Platform_POSIX
133 ::shutdown (fSD_, SHUT_WR);
134#elif qStroika_Platform_Windows
135 ::shutdown (fSD_, SD_SEND);
136#endif
137 break;
138 case Socket::ShutdownTarget::eBoth:
139#if qStroika_Platform_POSIX
140 ::shutdown (fSD_, SHUT_RDWR);
141#elif qStroika_Platform_Windows
142 ::shutdown (fSD_, SD_BOTH);
143#endif
144 break;
145 default:
147 }
148 }
149 }
150 virtual void Close () override
151 {
152 Debug::AssertExternallySynchronizedChecker::WriteContext declareContext{fThisAssertExternallySynchronized};
153 if (fSD_ != kINVALID_NATIVE_HANDLE_) {
154#if qStroika_Platform_POSIX
155 ::close (fSD_);
156#elif qStroika_Platform_Windows
157 ::closesocket (fSD_);
158#else
160#endif
161 fSD_ = kINVALID_NATIVE_HANDLE_;
162 }
163 }
164 virtual optional<IO::Network::SocketAddress> GetLocalAddress () const override
165 {
166 Debug::AssertExternallySynchronizedChecker::ReadContext declareContext{fThisAssertExternallySynchronized};
167 struct sockaddr_storage radr;
168 socklen_t len = sizeof (radr);
169 if (::getsockname (fSD_, reinterpret_cast<struct sockaddr*> (&radr), &len) == 0) {
171 return sa;
172 }
173 return nullopt;
174 }
175 virtual SocketAddress::FamilyType GetAddressFamily () const override
176 {
177 Debug::AssertExternallySynchronizedChecker::ReadContext declareContext{fThisAssertExternallySynchronized};
178#if defined(SO_DOMAIN)
179 return static_cast<SocketAddress::FamilyType> (getsockopt<int> (SOL_SOCKET, SO_DOMAIN)); // an int, per socket(7)
180#elif qStroika_Platform_Windows
181 // getsockname cannot stand in here: Windows fails it (WSAEINVAL) on a socket not yet bound
182 WSAPROTOCOL_INFOW info{};
183 socklen_t infoLen = sizeof (info);
184 getsockopt (SOL_SOCKET, SO_PROTOCOL_INFOW, &info, &infoLen);
185 return static_cast<SocketAddress::FamilyType> (info.iAddressFamily);
186#else
187 // No SO_DOMAIN (macOS, as of macOS 26): getsockname reports the family. POSIX leaves its result unspecified for
188 // a socket not yet bound, but macOS fills in the family then too.
189 sockaddr_storage radr{};
190 socklen_t len = sizeof (radr);
191 ThrowPOSIXErrNoIfNegative (::getsockname (fSD_, reinterpret_cast<sockaddr*> (&radr), &len));
192 return static_cast<SocketAddress::FamilyType> (radr.ss_family);
193#endif
194 }
195 virtual Socket::PlatformNativeHandle GetNativeSocket () const override
196 {
197 Debug::AssertExternallySynchronizedChecker::ReadContext declareContext{fThisAssertExternallySynchronized};
198 return fSD_;
199 }
200 virtual void getsockopt (int level, int optname, void* optval, socklen_t* optvallen) const override
201 {
202 Debug::AssertExternallySynchronizedChecker::ReadContext declareContext{fThisAssertExternallySynchronized};
203 // According to http://linux.die.net/man/2/getsockopt cannot return EINTR, so no need to retry
204 RequireNotNull (optval);
205#if qStroika_Platform_POSIX
206 ThrowPOSIXErrNoIfNegative (::getsockopt (fSD_, level, optname, reinterpret_cast<char*> (optval), optvallen));
207#elif qStroika_Platform_Windows
208 ThrowWSASystemErrorIfSOCKET_ERROR (::getsockopt (fSD_, level, optname, reinterpret_cast<char*> (optval), optvallen));
209#else
211#endif
212 }
213 template <typename RESULT_TYPE>
214 inline RESULT_TYPE getsockopt (int level, int optname) const
215 {
216 Debug::AssertExternallySynchronizedChecker::ReadContext declareContext{fThisAssertExternallySynchronized};
217 RESULT_TYPE r{};
218 socklen_t roptlen = sizeof (r);
219 this->getsockopt (level, optname, &r, &roptlen);
220 return r;
221 }
222 virtual void setsockopt (int level, int optname, const void* optval, socklen_t optvallen) override
223 {
224 Debug::AssertExternallySynchronizedChecker::WriteContext declareContext{fThisAssertExternallySynchronized};
225 // According to http://linux.die.net/man/2/setsockopt cannot return EINTR, so no need to retry
226 RequireNotNull (optval);
227#if qStroika_Platform_POSIX
228 ThrowPOSIXErrNoIfNegative (::setsockopt (fSD_, level, optname, optval, optvallen));
229#elif qStroika_Platform_Windows
230 ThrowWSASystemErrorIfSOCKET_ERROR (::setsockopt (fSD_, level, optname, reinterpret_cast<const char*> (optval), optvallen));
231#else
233#endif
234 }
235 template <typename ARG_TYPE>
236 inline void setsockopt (int level, int optname, ARG_TYPE arg)
237 {
238 socklen_t optvallen = sizeof (arg);
239 this->setsockopt (level, optname, &arg, optvallen);
240 }
242 };
243 }
244
245}
246
247#endif /*_Stroika_Foundation_IO_Network_Socket_Private_h_*/
#define AssertNotImplemented()
Definition Assertions.h:404
#define RequireNotReached()
Definition Assertions.h:388
#define RequireNotNull(p)
Definition Assertions.h:350
#define Stroika_ATTRIBUTE_NO_UNIQUE_ADDRESS_VCFORCE
[[msvc::no_unique_address]] isn't always broken in MSVC. Annotate with this on things where its not b...
Definition StdCompat.h:481
#define DbgTrace
Definition Trace.h:327
#define Stroika_Foundation_Debug_OptionalizeTraceArgs(...)
Definition Trace.h:288
NOT a real mutex - just a debugging infrastructure support tool so in debug builds can be assured thr...
unique_lock< AssertExternallySynchronizedChecker > WriteContext
Instantiate AssertExternallySynchronizedChecker::WriteContext to designate an area of code where prot...
shared_lock< const AssertExternallySynchronizedChecker > ReadContext
Instantiate AssertExternallySynchronizedChecker::ReadContext to designate an area of code where prote...
FamilyType
Socket address family - also sometimes referred to as domain (argument to ::socket calls it domain)
INT_TYPE ThrowPOSIXErrNoIfNegative(INT_TYPE returnCode)