E-MailRelay
gsocket_unix.cpp
Go to the documentation of this file.
1//
2// Copyright (C) 2001-2024 Graeme Walker <graeme_walker@users.sourceforge.net>
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program. If not, see <http://www.gnu.org/licenses/>.
16// ===
17///
18/// \file gsocket_unix.cpp
19///
20
21#include "gdef.h"
22#include "gsocket.h"
23#include "gmsg.h"
24#include "gprocess.h"
25#include "gstr.h"
26#include "gfile.h"
27#include "gcleanup.h"
28#include "glog.h"
29#include <cerrno> // EWOULDBLOCK etc
30#include <sys/types.h>
31#include <sys/stat.h>
32#include <fcntl.h>
33
34bool GNet::SocketBase::supports( Address::Family af , int type , int protocol )
35{
36 int fd = ::socket( Address::domain(af) , type , protocol ) ;
37 if( fd < 0 )
38 return false ;
39 ::close( fd ) ;
40 return true ;
41}
42
43bool GNet::SocketBase::create( int domain , int type , int protocol )
44{
45 m_fd = Descriptor( ::socket(domain,type,protocol) ) ;
46 if( m_fd == Descriptor::invalid() )
47 {
48 saveReason() ;
49 return false ;
50 }
51 return true ;
52}
53
54bool GNet::SocketBase::prepare( bool /*accepted*/ )
55{
56 static bool first = true ;
57 if( first )
58 {
59 first = false ;
60 G::Cleanup::init() ; // ignore SIGPIPE
61 }
62
63 if( !setNonBlocking() )
64 {
65 saveReason() ;
66 return false ;
67 }
68 return true ;
69}
70
71void GNet::SocketBase::destroy() noexcept
72{
73 if( m_domain == PF_UNIX && !m_accepted ) unlink() ;
74 ::close( m_fd.fd() ) ;
75}
76
77void GNet::SocketBase::unlink() noexcept
78{
79 try
80 {
81 AddressStorage address_storage ;
82 int rc = ::getsockname( m_fd.fd() , address_storage.p1() , address_storage.p2() ) ;
83 std::string path = rc == 0 ? Address(address_storage).hostPartString() : std::string() ;
84 if( !path.empty() && path.at(0U) == '/' )
85 {
86 G_DEBUG( "GNet::SocketBase::unlink: deleting unix-domain socket: fd=" << m_fd.fd() << " path=[" << G::Str::printable(path) << "]" ) ;
87 G::File::remove( G::Path(path) , std::nothrow ) ; // best-effort -- see also G::Root
88 }
89 }
90 catch(...)
91 {
92 }
93}
94
96{
97 return rc < 0 ;
98}
99
101{
102 m_reason = G::Process::errno_() ;
103}
104
105bool GNet::SocketBase::setNonBlocking()
106{
107 int mode = ::fcntl( m_fd.fd() , F_GETFL ) ; // NOLINT
108 if( mode < 0 )
109 return false ;
110
111 int rc = ::fcntl( m_fd.fd() , F_SETFL , mode | O_NONBLOCK ) ; // NOLINT
112 return rc == 0 ;
113}
114
115bool GNet::SocketBase::sizeError( ssize_t size )
116{
117 return size < 0 ;
118}
119
121{
122 return m_reason == ENOTCONN ;
123}
124
126{
127 return m_reason == EWOULDBLOCK || m_reason == EAGAIN || m_reason == EINTR ;
128}
129
131{
132 return m_reason == EINPROGRESS ;
133}
134
136{
137 return m_reason == EADDRINUSE ;
138}
139
140#ifndef G_LIB_SMALL
142{
143 return m_reason == EMSGSIZE ;
144}
145#endif
146
148{
149 return m_reason == EMFILE ;
150}
151
152std::string GNet::SocketBase::reasonString( int e )
153{
154 return G::Process::strerror( e ) ;
155}
156
157// ==
158
159#ifndef G_LIB_SMALL
160std::string GNet::Socket::canBindHint( const Address & address , bool stream , const Config & config )
161{
162 if( address.family() == Address::Family::ipv4 || address.family() == Address::Family::ipv6 )
163 {
164 if( stream )
165 {
166 StreamSocket s( address.family() , StreamSocket::Config(config) ) ;
167 return s.bind( address , std::nothrow ) ? std::string() : s.reason() ;
168 }
169 else
170 {
171 int protocol = 0 ;
172 DatagramSocket s( address.family() , protocol , DatagramSocket::Config(config) ) ;
173 return s.bind( address , std::nothrow ) ? std::string() : s.reason() ;
174 }
175 }
176 else
177 {
178 return {} ; // could do better
179 }
180}
181#endif
182
183void GNet::Socket::setOptionReuse()
184{
185 // allow bind on TIME_WAIT address -- see also SO_REUSEPORT
186 setOption( SOL_SOCKET , "so_reuseaddr" , SO_REUSEADDR , 1 ) ;
187}
188
189void GNet::Socket::setOptionExclusive()
190{
191 // no-op
192}
193
194void GNet::Socket::setOptionPureV6()
195{
196 #if GCONFIG_HAVE_IPV6
197 setOption( IPPROTO_IPV6 , "ipv6_v6only" , IPV6_V6ONLY , 1 ) ;
198 #else
199 throw SocketError( "cannot set socket option for pure ipv6" ) ;
200 #endif
201}
202
203bool GNet::Socket::setOptionPureV6( std::nothrow_t )
204{
205 #if GCONFIG_HAVE_IPV6
206 return setOption( IPPROTO_IPV6 , "ipv6_v6only" , IPV6_V6ONLY , 1 , std::nothrow ) ;
207 #endif
208}
209
210bool GNet::Socket::setOptionImp( int level , int op , const void * arg , socklen_t n )
211{
212 int rc = ::setsockopt( fd() , level , op , arg , n ) ;
213 return ! error(rc) ;
214}
215
216// ==
217
218GNet::RawSocket::RawSocket( int domain , int type , int protocol ) :
219 SocketBase(SocketBase::Raw(),domain,type,protocol)
220{
221}
222
223GNet::SocketBase::ssize_type GNet::RawSocket::read( char * buffer , size_type length )
224{
225 if( length == 0 ) return 0 ;
226 clearReason() ;
227 ssize_type nread = G::Msg::recv( fd() , buffer , length , 0 ) ;
228 if( sizeError(nread) )
229 {
230 saveReason() ;
231 G_DEBUG( "GNet::RawSocket::read: cannot read from " << fd() ) ;
232 return -1 ;
233 }
234 return nread ;
235}
236
237GNet::SocketBase::ssize_type GNet::RawSocket::write( const char * buffer , size_type length )
238{
239 return writeImp( buffer , length ) ; // SocketBase
240}
241
242// ==
243
244#ifndef G_LIB_SMALL
245std::size_t GNet::DatagramSocket::limit( std::size_t default_in ) const
246{
247 int value = 0 ;
248 socklen_t size = sizeof(int) ;
249 int rc = ::getsockopt( fd() , SOL_SOCKET , SO_SNDBUF , &value , &size ) ;
250 if( rc == 0 && size == sizeof(int) && value >= 0 && static_cast<std::size_t>(value) > default_in )
251 return static_cast<std::size_t>(value) ;
252 else
253 return default_in ;
254}
255#endif
256
257#ifndef G_LIB_SMALL
258GNet::Socket::ssize_type GNet::DatagramSocket::writeto( const std::vector<std::string_view> & data , const Address & dst )
259{
260 ssize_type nsent = G::Msg::sendto( fd() , data , MSG_NOSIGNAL , dst.address() , dst.length() ) ;
261 if( nsent < 0 )
262 {
263 saveReason() ;
264 G_DEBUG( "GNet::DatagramSocket::write: write error " << reason() ) ;
265 return -1 ;
266 }
267 return nsent ;
268}
269#endif
270
The GNet::Address class encapsulates a TCP/UDP transport address.
Definition: gaddress.h:63
static int domain(Family) noexcept
Returns the address 'domain' for the given family, eg.
Definition: gaddress.cpp:466
socklen_t length() const
Returns the size of the sockaddr address. See address().
Definition: gaddress.cpp:426
const sockaddr * address() const
Returns the sockaddr address.
Definition: gaddress.cpp:417
Family family() const noexcept
Returns the address family enumeration.
Definition: gaddress.cpp:474
A derivation of GNet::Socket for a datagram socket.
Definition: gsocket.h:430
ssize_type writeto(const char *buffer, size_type len, const Address &dst)
Sends a datagram to the given address.
Definition: gsocket.cpp:632
std::size_t limit(std::size_t default_=1024U) const
Returns the systems's maximum datagram size if the value is known and greater than the given default ...
A class that encapsulates a network socket file descriptor and an associated windows event handle.
Definition: gdescriptor.h:37
static Descriptor invalid() noexcept
Returns a descriptor with an invalid socket part and a zero handle.
Definition: gdescriptor.h:108
RawSocket(int domain, int type, int protocol)
Constructor.
ssize_type write(const char *buf, size_type len) override
Writes to the socket.
ssize_type read(char *buffer, size_type buffer_length) override
Reads from the socket.
A socket base class that holds a non-blocking socket file descriptor and interfaces to the event loop...
Definition: gsocket.h:53
bool eNotConn() const
Returns true if the previous socket operation failed with the ENOTCONN error status,...
bool eInProgress() const
Returns true if the previous socket operation failed with the EINPROGRESS error status.
std::string reason() const
Returns the reason for the previous error.
Definition: gsocket.cpp:212
SOCKET fd() const noexcept override
Returns the socket file descriptor.
Definition: gsocket.cpp:200
static bool error(int rc)
Returns true if the given return code indicates an error.
bool eMsgSize() const
Returns true if the previous socket operation failed with the EMSGSIZE error status.
static bool sizeError(ssize_type size)
Returns true if the given write() return value indicates an error.
bool eInUse() const
Returns true if the previous socket bind operation failed because the socket was already in use.
static bool supports(Address::Family, int type, int protocol)
Returns true if sockets can be created with the given parameters.
bool eWouldBlock() const override
Returns true if the previous socket operation failed because the socket would have blocked.
bool eTooMany() const
Returns true if the previous socket operation failed with the EMFILE error status,...
void saveReason()
Saves the current errno following error()/sizeError().
static std::string canBindHint(const Address &address, bool stream_socket, const Config &)
Returns the empty string if a socket could probably be bound with the given address or a failure reas...
void bind(const Address &)
Binds the socket with the given address.
Definition: gsocket.cpp:248
A derivation of GNet::Socket for a stream socket.
Definition: gsocket.h:356
static void init()
An optional early-initialisation function. May be called more than once.
static bool remove(const Path &path, std::nothrow_t) noexcept
Deletes the file or directory. Returns false on error.
Definition: gfile_unix.cpp:177
static ssize_t recv(SOCKET, void *, std::size_t, int flags) noexcept
A recv() wrapper.
Definition: gmsg_mac.cpp:39
static ssize_t sendto(SOCKET, const void *, std::size_t, int flags, const sockaddr *, socklen_t) noexcept
A sendto() wrapper.
Definition: gmsg_mac.cpp:32
A Path object represents a file system path.
Definition: gpath.h:82
static std::string strerror(int errno_)
Translates an 'errno' value into a meaningful diagnostic string.
static int errno_(const SignalSafe &=G::SignalSafe()) noexcept
Returns the process's current 'errno' value.
static std::string printable(const std::string &in, char escape='\\')
Returns a printable representation of the given input string, using chacter code ranges 0x20 to 0x7e ...
Definition: gstr.cpp:913
A configuration structure for GNet::DatagramSocket.
Definition: gsocket.h:433
Overload discriminator class for GNet::SocketBase.
Definition: gsocket.h:72
A configuration structure for GNet::Socket.
Definition: gsocket.h:232
A configuration structure for GNet::StreamSocket.
Definition: gsocket.h:363