source: box/trunk/lib/server/SocketStream.cpp @ 3066

Revision 3066, 11.9 KB checked in by chris, 4 months ago (diff)

Improve logging of socket errors (create, bind, accept, and poll)

  • Property svn:eol-style set to native
Line 
1// --------------------------------------------------------------------------
2//
3// File
4//              Name:    SocketStream.cpp
5//              Purpose: I/O stream interface for sockets
6//              Created: 2003/07/31
7//
8// --------------------------------------------------------------------------
9
10#include "Box.h"
11
12#ifdef HAVE_UNISTD_H
13        #include <unistd.h>
14#endif
15
16#include <sys/types.h>
17#include <errno.h>
18#include <string.h>
19
20#ifndef WIN32
21        #include <poll.h>
22#endif
23
24#ifdef HAVE_UCRED_H
25        #include <ucred.h>
26#endif
27
28#include "SocketStream.h"
29#include "ServerException.h"
30#include "CommonException.h"
31#include "Socket.h"
32
33#include "MemLeakFindOn.h"
34
35// --------------------------------------------------------------------------
36//
37// Function
38//              Name:    SocketStream::SocketStream()
39//              Purpose: Constructor (create stream ready for Open() call)
40//              Created: 2003/07/31
41//
42// --------------------------------------------------------------------------
43SocketStream::SocketStream()
44        : mSocketHandle(INVALID_SOCKET_VALUE),
45          mReadClosed(false),
46          mWriteClosed(false),
47          mBytesRead(0),
48          mBytesWritten(0)
49{
50}
51
52// --------------------------------------------------------------------------
53//
54// Function
55//              Name:    SocketStream::SocketStream(int)
56//              Purpose: Create stream from existing socket handle
57//              Created: 2003/07/31
58//
59// --------------------------------------------------------------------------
60SocketStream::SocketStream(int socket)
61        : mSocketHandle(socket),
62          mReadClosed(false),
63          mWriteClosed(false),
64          mBytesRead(0),
65          mBytesWritten(0)
66{
67        if(socket < 0)
68        {
69                THROW_EXCEPTION(ServerException, BadSocketHandle);
70        }
71}
72
73// --------------------------------------------------------------------------
74//
75// Function
76//              Name:    SocketStream::SocketStream(const SocketStream &)
77//              Purpose: Copy constructor (dup()s socket)
78//              Created: 2003/07/31
79//
80// --------------------------------------------------------------------------
81SocketStream::SocketStream(const SocketStream &rToCopy)
82        : mSocketHandle(::dup(rToCopy.mSocketHandle)),
83          mReadClosed(rToCopy.mReadClosed),
84          mWriteClosed(rToCopy.mWriteClosed),
85          mBytesRead(rToCopy.mBytesRead),
86          mBytesWritten(rToCopy.mBytesWritten)
87
88{
89        if(rToCopy.mSocketHandle < 0)
90        {
91                THROW_EXCEPTION(ServerException, BadSocketHandle);
92        }
93        if(mSocketHandle == INVALID_SOCKET_VALUE)
94        {
95                THROW_EXCEPTION(ServerException, DupError);
96        }
97}
98
99// --------------------------------------------------------------------------
100//
101// Function
102//              Name:    SocketStream::~SocketStream()
103//              Purpose: Destructor, closes stream if open
104//              Created: 2003/07/31
105//
106// --------------------------------------------------------------------------
107SocketStream::~SocketStream()
108{
109        if(mSocketHandle != INVALID_SOCKET_VALUE)
110        {
111                Close();
112        }
113}
114
115// --------------------------------------------------------------------------
116//
117// Function
118//              Name:    SocketStream::Attach(int)
119//              Purpose: Attach a socket handle to this stream
120//              Created: 11/12/03
121//
122// --------------------------------------------------------------------------
123void SocketStream::Attach(int socket)
124{
125        if(mSocketHandle != INVALID_SOCKET_VALUE) 
126        {
127                THROW_EXCEPTION(ServerException, SocketAlreadyOpen)
128        }
129
130        ResetCounters();
131
132        mSocketHandle = socket;
133        mReadClosed = false;
134        mWriteClosed = false;
135}
136
137
138// --------------------------------------------------------------------------
139//
140// Function
141//              Name:    SocketStream::Open(Socket::Type, char *, int)
142//              Purpose: Opens a connection to a listening socket (INET or UNIX)
143//              Created: 2003/07/31
144//
145// --------------------------------------------------------------------------
146void SocketStream::Open(Socket::Type Type, const std::string& rName, int Port)
147{
148        if(mSocketHandle != INVALID_SOCKET_VALUE) 
149        {
150                THROW_EXCEPTION(ServerException, SocketAlreadyOpen)
151        }
152       
153        // Setup parameters based on type, looking up names if required
154        int sockDomain = 0;
155        SocketAllAddr addr;
156        int addrLen = 0;
157        Socket::NameLookupToSockAddr(addr, sockDomain, Type, rName, Port,
158                addrLen);
159
160        // Create the socket
161        mSocketHandle = ::socket(sockDomain, SOCK_STREAM,
162                0 /* let OS choose protocol */);
163        if(mSocketHandle == INVALID_SOCKET_VALUE)
164        {
165                BOX_LOG_SOCKET_ERROR(Type, rName, Port,
166                        "Failed to create a network socket");
167                THROW_EXCEPTION(ServerException, SocketOpenError)
168        }
169       
170        // Connect it
171        if(::connect(mSocketHandle, &addr.sa_generic, addrLen) == -1)
172        {
173                // Dispose of the socket
174                BOX_LOG_SOCKET_ERROR(Type, rName, Port,
175                        "Failed to connect to socket");
176#ifdef WIN32
177                ::closesocket(mSocketHandle);
178#else // !WIN32
179                ::close(mSocketHandle);
180#endif // WIN32
181
182                mSocketHandle = INVALID_SOCKET_VALUE;
183                THROW_EXCEPTION(ConnectionException, Conn_SocketConnectError)
184        }
185
186        ResetCounters();
187
188        mReadClosed = false;
189        mWriteClosed = false;
190}
191
192// --------------------------------------------------------------------------
193//
194// Function
195//              Name:    SocketStream::Read(void *pBuffer, int NBytes)
196//              Purpose: Reads data from stream. Maybe returns less than asked for.
197//              Created: 2003/07/31
198//
199// --------------------------------------------------------------------------
200int SocketStream::Read(void *pBuffer, int NBytes, int Timeout)
201{
202        if(mSocketHandle == INVALID_SOCKET_VALUE) 
203        {
204                THROW_EXCEPTION(ServerException, BadSocketHandle)
205        }
206
207        if(Timeout != IOStream::TimeOutInfinite)
208        {
209                struct pollfd p;
210                p.fd = mSocketHandle;
211                p.events = POLLIN;
212                p.revents = 0;
213                switch(::poll(&p, 1, (Timeout == IOStream::TimeOutInfinite)?INFTIM:Timeout))
214                {
215                case -1:
216                        // error
217                        if(errno == EINTR)
218                        {
219                                // Signal. Just return 0 bytes
220                                return 0;
221                        }
222                        else
223                        {
224                                // Bad!
225                                BOX_LOG_SYS_ERROR("Failed to poll socket");
226                                THROW_EXCEPTION(ServerException,
227                                        SocketPollError)
228                        }
229                        break;
230                       
231                case 0:
232                        // no data
233                        return 0;
234                        break;
235                       
236                default:
237                        // good to go!
238                        break;
239                }
240        }
241
242#ifdef WIN32
243        int r = ::recv(mSocketHandle, (char*)pBuffer, NBytes, 0);
244#else
245        int r = ::read(mSocketHandle, pBuffer, NBytes);
246#endif
247        if(r == -1)
248        {
249                if(errno == EINTR)
250                {
251                        // Nothing could be read
252                        return 0;
253                }
254                else
255                {
256                        // Other error
257                        BOX_LOG_SYS_ERROR("Failed to read from socket");
258                        THROW_EXCEPTION(ConnectionException,
259                                Conn_SocketReadError);
260                }
261        }
262
263        // Closed for reading?
264        if(r == 0)
265        {
266                mReadClosed = true;
267        }
268       
269        mBytesRead += r;
270        return r;
271}
272
273// --------------------------------------------------------------------------
274//
275// Function
276//              Name:    SocketStream::Write(void *pBuffer, int NBytes)
277//              Purpose: Writes data, blocking until it's all done.
278//              Created: 2003/07/31
279//
280// --------------------------------------------------------------------------
281void SocketStream::Write(const void *pBuffer, int NBytes)
282{
283        if(mSocketHandle == INVALID_SOCKET_VALUE) 
284        {
285                THROW_EXCEPTION(ServerException, BadSocketHandle)
286        }
287       
288        // Buffer in byte sized type.
289        ASSERT(sizeof(char) == 1);
290        const char *buffer = (char *)pBuffer;
291       
292        // Bytes left to send
293        int bytesLeft = NBytes;
294       
295        while(bytesLeft > 0)
296        {
297                // Try to send.
298#ifdef WIN32
299                int sent = ::send(mSocketHandle, buffer, bytesLeft, 0);
300#else
301                int sent = ::write(mSocketHandle, buffer, bytesLeft);
302#endif
303                if(sent == -1)
304                {
305                        // Error.
306                        mWriteClosed = true;    // assume can't write again
307                        BOX_LOG_SYS_ERROR("Failed to write to socket");
308                        THROW_EXCEPTION(ConnectionException,
309                                Conn_SocketWriteError);
310                }
311               
312                // Knock off bytes sent
313                bytesLeft -= sent;
314                // Move buffer pointer
315                buffer += sent;
316
317                mBytesWritten += sent;
318               
319                // Need to wait until it can send again?
320                if(bytesLeft > 0)
321                {
322                        BOX_TRACE("Waiting to send data on socket " << 
323                                mSocketHandle << " (" << bytesLeft <<
324                                " of " << NBytes << " bytes left)");
325                       
326                        // Wait for data to send.
327                        struct pollfd p;
328                        p.fd = mSocketHandle;
329                        p.events = POLLOUT;
330                        p.revents = 0;
331                       
332                        if(::poll(&p, 1, 16000 /* 16 seconds */) == -1)
333                        {
334                                // Don't exception if it's just a signal
335                                if(errno != EINTR)
336                                {
337                                        BOX_LOG_SYS_ERROR("Failed to poll "
338                                                "socket");
339                                        THROW_EXCEPTION(ServerException,
340                                                SocketPollError)
341                                }
342                        }
343                }
344        }
345}
346
347// --------------------------------------------------------------------------
348//
349// Function
350//              Name:    SocketStream::Close()
351//              Purpose: Closes connection to remote socket
352//              Created: 2003/07/31
353//
354// --------------------------------------------------------------------------
355void SocketStream::Close()
356{
357        if(mSocketHandle == INVALID_SOCKET_VALUE) 
358        {
359                THROW_EXCEPTION(ServerException, BadSocketHandle)
360        }
361#ifdef WIN32
362        if(::closesocket(mSocketHandle) == -1)
363#else
364        if(::close(mSocketHandle) == -1)
365#endif
366        {
367                BOX_LOG_SYS_ERROR("Failed to close socket");
368                // don't throw an exception here, assume that the socket was
369                // already closed or closing.
370        }
371        mSocketHandle = INVALID_SOCKET_VALUE;
372}
373
374// --------------------------------------------------------------------------
375//
376// Function
377//              Name:    SocketStream::Shutdown(bool, bool)
378//              Purpose: Shuts down a socket for further reading and/or writing
379//              Created: 2003/07/31
380//
381// --------------------------------------------------------------------------
382void SocketStream::Shutdown(bool Read, bool Write)
383{
384        if(mSocketHandle == INVALID_SOCKET_VALUE) 
385        {
386                THROW_EXCEPTION(ServerException, BadSocketHandle)
387        }
388       
389        // Do anything?
390        if(!Read && !Write) return;
391       
392        int how = SHUT_RDWR;
393        if(Read && !Write) how = SHUT_RD;
394        if(!Read && Write) how = SHUT_WR;
395       
396        // Shut it down!
397        if(::shutdown(mSocketHandle, how) == -1)
398        {
399                BOX_LOG_SYS_ERROR("Failed to shutdown socket");
400                THROW_EXCEPTION(ConnectionException, Conn_SocketShutdownError)
401        }
402}
403
404
405// --------------------------------------------------------------------------
406//
407// Function
408//              Name:    SocketStream::StreamDataLeft()
409//              Purpose: Still capable of reading data?
410//              Created: 2003/08/02
411//
412// --------------------------------------------------------------------------
413bool SocketStream::StreamDataLeft()
414{
415        return !mReadClosed;
416}
417
418// --------------------------------------------------------------------------
419//
420// Function
421//              Name:    SocketStream::StreamClosed()
422//              Purpose: Connection been closed?
423//              Created: 2003/08/02
424//
425// --------------------------------------------------------------------------
426bool SocketStream::StreamClosed()
427{
428        return mWriteClosed;
429}
430
431
432// --------------------------------------------------------------------------
433//
434// Function
435//              Name:    SocketStream::GetSocketHandle()
436//              Purpose: Returns socket handle for this stream (derived classes only).
437//                               Will exception if there's no valid socket.
438//              Created: 2003/08/06
439//
440// --------------------------------------------------------------------------
441tOSSocketHandle SocketStream::GetSocketHandle()
442{
443        if(mSocketHandle == INVALID_SOCKET_VALUE) 
444        {
445                THROW_EXCEPTION(ServerException, BadSocketHandle)
446        }
447        return mSocketHandle;
448}
449
450
451// --------------------------------------------------------------------------
452//
453// Function
454//              Name:    SocketStream::GetPeerCredentials(uid_t &, gid_t &)
455//              Purpose: Returns true if the peer credientials are available.
456//                               (will work on UNIX domain sockets only)
457//              Created: 19/2/04
458//
459// --------------------------------------------------------------------------
460bool SocketStream::GetPeerCredentials(uid_t &rUidOut, gid_t &rGidOut)
461{
462#ifdef HAVE_GETPEEREID
463        uid_t remoteEUID = 0xffff;
464        gid_t remoteEGID = 0xffff;
465
466        if(::getpeereid(mSocketHandle, &remoteEUID, &remoteEGID) == 0)
467        {
468                rUidOut = remoteEUID;
469                rGidOut = remoteEGID;
470                return true;
471        }
472#endif
473
474#if HAVE_DECL_SO_PEERCRED
475        struct ucred cred;
476        socklen_t credLen = sizeof(cred);
477
478        if(::getsockopt(mSocketHandle, SOL_SOCKET, SO_PEERCRED, &cred,
479                &credLen) == 0)
480        {
481                rUidOut = cred.uid;
482                rGidOut = cred.gid;
483                return true;
484        }
485
486        BOX_LOG_SYS_ERROR("Failed to get peer credentials on socket");
487#endif
488
489#if defined HAVE_UCRED_H && HAVE_GETPEERUCRED
490        ucred_t *pucred = NULL;
491        if(::getpeerucred(mSocketHandle, &pucred) == 0)
492        {
493                rUidOut = ucred_geteuid(pucred);
494                rGidOut = ucred_getegid(pucred);
495                ucred_free(pucred);
496                if (rUidOut == -1 || rGidOut == -1)
497                {
498                        BOX_ERROR("Failed to get peer credentials on "
499                                "socket: insufficient information");
500                        return false;
501                }
502                return true;
503        }
504
505        BOX_LOG_SYS_ERROR("Failed to get peer credentials on socket");
506#endif
507
508        // Not available
509        return false;
510}
511
Note: See TracBrowser for help on using the repository browser.