E-MailRelay
gsocket_unix.cpp
Go to the documentation of this file.
1//
2// Copyright (C) 2001-2023 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( 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
135#ifndef G_LIB_SMALL
137{
138 return m_reason == EMSGSIZE ;
139}
140#endif
141
143{
144 return m_reason == EMFILE ;
145}
146
147std::string GNet::SocketBase::reasonString( int e )
148{
149 return G::Process::strerror( e ) ;
150}
151
152// ==
153
154#ifndef G_LIB_SMALL
155std::string GNet::Socket::canBindHint( const Address & address , bool stream , const Config & config )
156{
157 if( address.family() == Address::Family::ipv4 || address.family() == Address::Family::ipv6 )
158 {
159 if( stream )
160 {
161 StreamSocket s( address.family() , StreamSocket::Config(config) ) ;
162 return s.bind( address , std::nothrow ) ? std::string() : s.reason() ;
163 }
164 else
165 {
166 int protocol = 0 ;
167 DatagramSocket s( address.family() , protocol , DatagramSocket::Config(config) ) ;
168 return s.bind( address , std::nothrow ) ? std::string() : s.reason() ;
169 }
170 }
171 else
172 {
173 return {} ; // could do better
174 }
175}
176#endif
177
178void GNet::Socket::setOptionReuse()
179{
180 // allow bind on TIME_WAIT address -- see also SO_REUSEPORT
181 setOption( SOL_SOCKET , "so_reuseaddr" , SO_REUSEADDR , 1 ) ;
182}
183
184void GNet::Socket::setOptionExclusive()
185{
186 // no-op
187}
188
189void GNet::Socket::setOptionPureV6()
190{
191 #if GCONFIG_HAVE_IPV6
192 setOption( IPPROTO_IPV6 , "ipv6_v6only" , IPV6_V6ONLY , 1 ) ;
193 #else
194 throw SocketError( "cannot set socket option for pure ipv6" ) ;
195 #endif
196}
197
198bool GNet::Socket::setOptionPureV6( std::nothrow_t )
199{
200 #if GCONFIG_HAVE_IPV6
201 return setOption( IPPROTO_IPV6 , "ipv6_v6only" , IPV6_V6ONLY , 1 , std::nothrow ) ;
202 #endif
203}
204
205bool GNet::Socket::setOptionImp( int level , int op , const void * arg , socklen_t n )
206{
207 int rc = ::setsockopt( fd() , level , op , arg , n ) ;
208 return ! error(rc) ;
209}
210
211// ==
212
213GNet::RawSocket::RawSocket( int domain , int type , int protocol ) :
214 SocketBase(SocketBase::Raw(),domain,type,protocol)
215{
216}
217
218GNet::SocketBase::ssize_type GNet::RawSocket::read( char * buffer , size_type length )
219{
220 if( length == 0 ) return 0 ;
221 clearReason() ;
222 ssize_type nread = G::Msg::recv( fd() , buffer , length , 0 ) ;
223 if( sizeError(nread) )
224 {
225 saveReason() ;
226 G_DEBUG( "GNet::RawSocket::read: cannot read from " << fd() ) ;
227 return -1 ;
228 }
229 return nread ;
230}
231
232GNet::SocketBase::ssize_type GNet::RawSocket::write( const char * buffer , size_type length )
233{
234 return writeImp( buffer , length ) ; // SocketBase
235}
236
237// ==
238
239#ifndef G_LIB_SMALL
240std::size_t GNet::DatagramSocket::limit( std::size_t default_in ) const
241{
242 int value = 0 ;
243 socklen_t size = sizeof(int) ;
244 int rc = ::getsockopt( fd() , SOL_SOCKET , SO_SNDBUF , &value , &size ) ;
245 if( rc == 0 && size == sizeof(int) && value >= 0 && static_cast<std::size_t>(value) > default_in )
246 return static_cast<std::size_t>(value) ;
247 else
248 return default_in ;
249}
250#endif
251
252#ifndef G_LIB_SMALL
253GNet::Socket::ssize_type GNet::DatagramSocket::writeto( const std::vector<G::string_view> & data , const Address & dst )
254{
255 ssize_type nsent = G::Msg::sendto( fd() , data , MSG_NOSIGNAL , dst.address() , dst.length() ) ;
256 if( nsent < 0 )
257 {
258 saveReason() ;
259 G_DEBUG( "GNet::DatagramSocket::write: write error " << reason() ) ;
260 return -1 ;
261 }
262 return nsent ;
263}
264#endif
265
The GNet::Address class encapsulates a TCP/UDP transport address.
Definition: gaddress.h:62
static int domain(Family) noexcept
Returns the address 'domain' for the given family, eg.
Definition: gaddress.cpp:468
socklen_t length() const
Returns the size of the sockaddr address. See address().
Definition: gaddress.cpp:428
const sockaddr * address() const
Returns the sockaddr address.
Definition: gaddress.cpp:419
Family family() const noexcept
Returns the address family enumeration.
Definition: gaddress.cpp:476
A derivation of GNet::Socket for a datagram socket.
Definition: gsocket.h:415
ssize_type writeto(const char *buffer, size_type len, const Address &dst)
Sends a datagram to the given address.
Definition: gsocket.cpp:624
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:204
SOCKET fd() const noexcept override
Returns the socket file descriptor.
Definition: gsocket.cpp:199
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.
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:240
A derivation of GNet::Socket for a stream socket.
Definition: gsocket.h:341
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.cpp:29
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
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:916
A configuration structure for GNet::DatagramSocket.
Definition: gsocket.h:418
Overload discriminator class for GNet::SocketBase.
Definition: gsocket.h:64
A configuration structure for GNet::Socket.
Definition: gsocket.h:217
A configuration structure for GNet::StreamSocket.
Definition: gsocket.h:348