Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
Socket-Private_.h
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. 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_Foundation_Common_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_Foundation_Common_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_Foundation_Common_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_Foundation_Common_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_Foundation_Common_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_Foundation_Common_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::AssertExternallySynchronizedMutex::WriteContext declareContext{fThisAssertExternallySynchronized};
114 fSD_ = kINVALID_NATIVE_HANDLE_;
115 return h;
116 }
117 virtual void Shutdown (Socket::ShutdownTarget shutdownTarget) override
118 {
119 Debug::AssertExternallySynchronizedMutex::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_Foundation_Common_Platform_POSIX
125 ::shutdown (fSD_, SHUT_RD);
126#elif qStroika_Foundation_Common_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_Foundation_Common_Platform_POSIX
133 ::shutdown (fSD_, SHUT_WR);
134#elif qStroika_Foundation_Common_Platform_Windows
135 ::shutdown (fSD_, SD_SEND);
136#endif
137 break;
138 case Socket::ShutdownTarget::eBoth:
139#if qStroika_Foundation_Common_Platform_POSIX
140 ::shutdown (fSD_, SHUT_RDWR);
141#elif qStroika_Foundation_Common_Platform_Windows
142 ::shutdown (fSD_, SD_BOTH);
143#endif
144 break;
145 default:
147 }
148 }
149 }
150 virtual void Close () override
151 {
152 Debug::AssertExternallySynchronizedMutex::WriteContext declareContext{fThisAssertExternallySynchronized};
153 if (fSD_ != kINVALID_NATIVE_HANDLE_) {
154#if qStroika_Foundation_Common_Platform_POSIX
155 ::close (fSD_);
156#elif qStroika_Foundation_Common_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::AssertExternallySynchronizedMutex::ReadContext declareContext{fThisAssertExternallySynchronized};
167 struct sockaddr_storage radr;
168 socklen_t len = sizeof (radr);
169 if (::getsockname (static_cast<int> (fSD_), (struct sockaddr*)&radr, &len) == 0) {
171 return sa;
172 }
173 return nullopt;
174 }
175 virtual SocketAddress::FamilyType GetAddressFamily () const override
176 {
177 Debug::AssertExternallySynchronizedMutex::ReadContext declareContext{fThisAssertExternallySynchronized};
178#if defined(SO_DOMAIN)
179 return getsockopt<SocketAddress::FamilyType> (SOL_SOCKET, SO_DOMAIN);
180#elif defined(SO_PROTOCOL)
181 return getsockopt<SocketAddress::FamilyType> (SOL_SOCKET, SO_PROTOCOL);
182#elif qStroika_Foundation_Common_Platform_Windows
183 /*
184 * According to https://msdn.microsoft.com/en-us/library/windows/desktop/ms741621(v=vs.85).aspx,
185 * WSAENOPROTOOPT The socket option is not supported on the specified protocol. For example,
186 * an attempt to use the SIO_GET_BROADCAST_ADDRESS IOCTL was made on an IPv6 socket
187 * or an attempt to use the TCP SIO_KEEPALIVE_VALS IOCTL was made on a datagram socket.
188 */
189 DWORD dwBytesRet;
190 sockaddr_storage bcast;
191 bool isV6 = (WSAIoctl (this->GetNativeSocket (), SIO_GET_BROADCAST_ADDRESS, NULL, 0, &bcast, sizeof (bcast), &dwBytesRet,
192 NULL, NULL) == SOCKET_ERROR);
193 if (isV6) {
194 Assert (::WSAGetLastError () == WSAENOPROTOOPT);
195 }
196 return isV6 ? SocketAddress::FamilyType::INET6 : SocketAddress::FamilyType::INET;
197#else
199#endif
200 }
201 virtual Socket::PlatformNativeHandle GetNativeSocket () const override
202 {
203 Debug::AssertExternallySynchronizedMutex::ReadContext declareContext{fThisAssertExternallySynchronized};
204 return fSD_;
205 }
206 virtual void getsockopt (int level, int optname, void* optval, socklen_t* optvallen) const override
207 {
208 Debug::AssertExternallySynchronizedMutex::ReadContext declareContext{fThisAssertExternallySynchronized};
209 // According to http://linux.die.net/man/2/getsockopt cannot return EINTR, so no need to retry
210 RequireNotNull (optval);
211#if qStroika_Foundation_Common_Platform_POSIX
212 ThrowPOSIXErrNoIfNegative (::getsockopt (fSD_, level, optname, reinterpret_cast<char*> (optval), optvallen));
213#elif qStroika_Foundation_Common_Platform_Windows
214 ThrowWSASystemErrorIfSOCKET_ERROR (::getsockopt (fSD_, level, optname, reinterpret_cast<char*> (optval), optvallen));
215#else
217#endif
218 }
219 template <typename RESULT_TYPE>
220 inline RESULT_TYPE getsockopt (int level, int optname) const
221 {
222 Debug::AssertExternallySynchronizedMutex::ReadContext declareContext{fThisAssertExternallySynchronized};
223 RESULT_TYPE r{};
224 socklen_t roptlen = sizeof (r);
225 this->getsockopt (level, optname, &r, &roptlen);
226 return r;
227 }
228 virtual void setsockopt (int level, int optname, const void* optval, socklen_t optvallen) override
229 {
230 Debug::AssertExternallySynchronizedMutex::WriteContext declareContext{fThisAssertExternallySynchronized};
231 // According to http://linux.die.net/man/2/setsockopt cannot return EINTR, so no need to retry
232 RequireNotNull (optval);
233#if qStroika_Foundation_Common_Platform_POSIX
234 ThrowPOSIXErrNoIfNegative (::setsockopt (fSD_, level, optname, optval, optvallen));
235#elif qStroika_Foundation_Common_Platform_Windows
236 ThrowWSASystemErrorIfSOCKET_ERROR (::setsockopt (fSD_, level, optname, reinterpret_cast<const char*> (optval), optvallen));
237#else
239#endif
240 }
241 template <typename ARG_TYPE>
242 inline void setsockopt (int level, int optname, ARG_TYPE arg)
243 {
244 socklen_t optvallen = sizeof (arg);
245 this->setsockopt (level, optname, &arg, optvallen);
246 }
247 [[no_unique_address]] Debug::AssertExternallySynchronizedMutex fThisAssertExternallySynchronized;
248 };
249 }
250
251}
252
253#endif /*_Stroika_Foundation_IO_Network_Socket_Private_h_*/
#define AssertNotImplemented()
Definition Assertions.h:401
#define RequireNotReached()
Definition Assertions.h:385
#define RequireNotNull(p)
Definition Assertions.h:347
#define DbgTrace
Definition Trace.h:309
#define Stroika_Foundation_Debug_OptionalizeTraceArgs(...)
Definition Trace.h:270
NOT a real mutex - just a debugging infrastructure support tool so in debug builds can be assured thr...
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...
FamilyType
Socket address family - also sometimes referred to as domain (argument to ::socket calls it domain)
void Throw(T &&e2Throw)
identical to builtin C++ 'throw' except that it does helpful, type dependent DbgTrace() messages firs...
Definition Throw.inl:43
INT_TYPE ThrowPOSIXErrNoIfNegative(INT_TYPE returnCode)