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
34 bool 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
43 bool 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
54 bool 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
71 void GNet::SocketBase::destroy() noexcept
72{
73 if( m_domain == PF_UNIX && !m_accepted ) unlink() ;
74 ::close( m_fd.fd() ) ;
75}
76
77 void 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
95 bool GNet::SocketBase::error( int rc )
96{
97 return rc < 0 ;
98}
99
100 void GNet::SocketBase::saveReason()
101{
102 m_reason = G::Process::errno_() ;
103}
104
105 bool 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
115 bool GNet::SocketBase::sizeError( ssize_t size )
116{
117 return size < 0 ;
118}
119
120 bool GNet::SocketBase::eNotConn() const
121{
122 return m_reason == ENOTCONN ;
123}
124
125 bool GNet::SocketBase::eWouldBlock() const
126{
127 return m_reason == EWOULDBLOCK || m_reason == EAGAIN || m_reason == EINTR ;
128}
129
130 bool GNet::SocketBase::eInProgress() const
131{
132 return m_reason == EINPROGRESS ;
133}
134
135 bool GNet::SocketBase::eMsgSize() const
136{
137 return m_reason == EMSGSIZE ;
138}
139
140 bool GNet::SocketBase::eTooMany() const
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
173 void 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
179 void GNet::Socket::setOptionExclusive()
180{
181 // no-op
182}
183
184 void 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
193 bool 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
200 bool 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
208 GNet::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
GNet::Address
The GNet::Address class encapsulates a TCP/UDP transport address.
Definition: gaddress.h:53
GNet::Address::family
Family family() const
Returns the address family enumeration.
Definition: gaddress.cpp:491
GNet::Address::domain
static int domain(Family)
Returns the address 'domain' for the given family, eg.
Definition: gaddress.cpp:483
GNet::DatagramSocket
A derivation of GNet::Socket for a datagram socket.
Definition: gsocket.h:360
GNet::Descriptor
A class that encapsulates a network socket file descriptor and an associated windows event handle.
Definition: gdescriptor.h:37
GNet::Descriptor::invalid
static Descriptor invalid() noexcept
Returns a descriptor with an invalid socket part and a zero handle.
Definition: gdescriptor.h:108
GNet::RawSocket::RawSocket
RawSocket(int domain, int type, int protocol)
Constructor.
Definition: gsocket_unix.cpp:208
GNet::RawSocket::write
ssize_type write(const char *buf, size_type len) override
Writes to the socket.
Definition: gsocket_unix.cpp:227
GNet::RawSocket::read
ssize_type read(char *buffer, size_type buffer_length) override
Reads from the socket.
Definition: gsocket_unix.cpp:213
GNet::SocketBase
A socket base class that holds a non-blocking socket file descriptor and interfaces to the event loop...
Definition: gsocket.h:52
GNet::SocketBase::eNotConn
bool eNotConn() const
Returns true if the previous socket operation failed with the ENOTCONN error status,...
Definition: gsocket_unix.cpp:120
GNet::SocketBase::eInProgress
bool eInProgress() const
Returns true if the previous socket operation failed with the EINPROGRESS error status.
Definition: gsocket_unix.cpp:130
GNet::SocketBase::reason
std::string reason() const
Returns the reason for the previous error.
Definition: gsocket.cpp:216
GNet::SocketBase::fd
SOCKET fd() const noexcept override
Returns the socket file descriptor.
Definition: gsocket.cpp:211
GNet::SocketBase::error
static bool error(int rc)
Returns true if the given return code indicates an error.
Definition: gsocket_unix.cpp:95
GNet::SocketBase::eMsgSize
bool eMsgSize() const
Returns true if the previous socket operation failed with the EMSGSIZE error status.
Definition: gsocket_unix.cpp:135
GNet::SocketBase::sizeError
static bool sizeError(ssize_type size)
Returns true if the given write() return value indicates an error.
Definition: gsocket_unix.cpp:115
GNet::SocketBase::supports
static bool supports(Address::Family, int type, int protocol)
Returns true if sockets can be created with the given parameters.
Definition: gsocket_unix.cpp:34
GNet::SocketBase::eWouldBlock
bool eWouldBlock() const override
Returns true if the previous socket operation failed because the socket would have blocked.
Definition: gsocket_unix.cpp:125
GNet::SocketBase::eTooMany
bool eTooMany() const
Returns true if the previous socket operation failed with the EMFILE error status,...
Definition: gsocket_unix.cpp:140
GNet::SocketBase::saveReason
void saveReason()
Saves the current errno following error()/sizeError().
Definition: gsocket_unix.cpp:100
GNet::Socket::bind
void bind(const Address &)
Binds the socket with the given address.
Definition: gsocket.cpp:241
GNet::Socket::canBindHint
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...
Definition: gsocket_unix.cpp:152
GNet::StreamSocket
A derivation of GNet::Socket for a stream socket.
Definition: gsocket.h:311
G::Cleanup::init
static void init()
An optional early-initialisation function. May be called more than once.
Definition: gcleanup_unix.cpp:111
G::File::remove
static bool remove(const Path &path, std::nothrow_t) noexcept
Deletes the file or directory. Returns false on error.
Definition: gfile.cpp:29
G::Msg::recv
static ssize_t recv(SOCKET, void *, std::size_t, int flags)
A recv() wrapper.
Definition: gmsg_unix.cpp:45
G::Process::strerror
static std::string strerror(int errno_)
Translates an 'errno' value into a meaningful diagnostic string.
Definition: gprocess_unix.cpp:127
G::Process::errno_
static int errno_(const SignalSafe &=G::SignalSafe()) noexcept
Returns the process's current 'errno' value.
Definition: gprocess_unix.cpp:115
G::Str::printable
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
gcleanup.h
gfile.h
glog.h
gmsg.h
gprocess.h
gsocket.h
gstr.h
GNet::SocketBase::Raw
Overload discriminator class for GNet::SocketBase.
Definition: gsocket.h:63