E-MailRelay
gsocket_unix.cpp
Go to the documentation of this file.
1//
2// Copyright (C) 2001-2021 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
136{
137 return m_reason == EMSGSIZE ;
138}
139
141{
142 return m_reason == EMFILE ;
143}
144
145std::string GNet::SocketBase::reasonString( int e )
146{
147 return G::Process::strerror( e ) ;
148}
149
150// ==
151
152std::string GNet::Socket::canBindHint( const Address & address , bool stream )
153{
154 if( address.family() == Address::Family::ipv4 || address.family() == Address::Family::ipv6 )
155 {
156 if( stream )
157 {
158 StreamSocket s( address.family() ) ;
159 return s.bind( address , std::nothrow ) ? std::string() : s.reason() ;
160 }
161 else
162 {
163 DatagramSocket s( address.family() ) ;
164 return s.bind( address , std::nothrow ) ? std::string() : s.reason() ;
165 }
166 }
167 else
168 {
169 return {} ; // could do better
170 }
171}
172
173void GNet::Socket::setOptionReuse()
174{
175 // allow bind on TIME_WAIT address -- see also SO_REUSEPORT
176 setOption( SOL_SOCKET , "so_reuseaddr" , SO_REUSEADDR , 1 ) ;
177}
178
179void GNet::Socket::setOptionExclusive()
180{
181 // no-op
182}
183
184void GNet::Socket::setOptionPureV6()
185{
186 #if GCONFIG_HAVE_IPV6
187 setOption( IPPROTO_IPV6 , "ipv6_v6only" , IPV6_V6ONLY , 1 ) ;
188 #else
189 throw SocketError( "cannot set socket option for pure ipv6" ) ;
190 #endif
191}
192
193bool GNet::Socket::setOptionPureV6( std::nothrow_t )
194{
195 #if GCONFIG_HAVE_IPV6
196 return setOption( IPPROTO_IPV6 , "ipv6_v6only" , IPV6_V6ONLY , 1 , std::nothrow ) ;
197 #endif
198}
199
200bool GNet::Socket::setOptionImp( int level , int op , const void * arg , socklen_t n )
201{
202 int rc = ::setsockopt( fd() , level , op , arg , n ) ;
203 return ! error(rc) ;
204}
205
206// ==
207
208GNet::RawSocket::RawSocket( int domain , int type , int protocol ) :
209 SocketBase(SocketBase::Raw(),domain,type,protocol)
210{
211}
212
213GNet::SocketBase::ssize_type GNet::RawSocket::read( char * buffer , size_type length )
214{
215 if( length == 0 ) return 0 ;
216 clearReason() ;
217 ssize_type nread = G::Msg::recv( fd() , buffer , length , 0 ) ;
218 if( sizeError(nread) )
219 {
220 saveReason() ;
221 G_DEBUG( "GNet::RawSocket::read: cannot read from " << fd() ) ;
222 return -1 ;
223 }
224 return nread ;
225}
226
227GNet::SocketBase::ssize_type GNet::RawSocket::write( const char * buffer , size_type length )
228{
229 return writeImp( buffer , length ) ; // SocketBase
230}
231
The GNet::Address class encapsulates a TCP/UDP transport address.
Definition: gaddress.h:53
Family family() const
Returns the address family enumeration.
Definition: gaddress.cpp:491
static int domain(Family)
Returns the address 'domain' for the given family, eg.
Definition: gaddress.cpp:483
A derivation of GNet::Socket for a datagram socket.
Definition: gsocket.h:360
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:52
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:216
SOCKET fd() const noexcept override
Returns the socket file descriptor.
Definition: gsocket.cpp:211
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().
void bind(const Address &)
Binds the socket with the given address.
Definition: gsocket.cpp:241
static std::string canBindHint(const Address &address, bool stream_socket=true)
Returns the empty string if a socket could probably be bound with the given address or a failure reas...
A derivation of GNet::Socket for a stream socket.
Definition: gsocket.h:311
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)
A recv() wrapper.
Definition: gmsg_unix.cpp:45
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:885
Overload discriminator class for GNet::SocketBase.
Definition: gsocket.h:63