Warning, file /sdk/dferry/transport/ipsocket.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002    Copyright (C) 2015 Andreas Hartmetz <ahartmetz@gmail.com>
0003 
0004    This library is free software; you can redistribute it and/or
0005    modify it under the terms of the GNU Library General Public
0006    License as published by the Free Software Foundation; either
0007    version 2 of the License, or (at your option) any later version.
0008 
0009    This library is distributed in the hope that it will be useful,
0010    but WITHOUT ANY WARRANTY; without even the implied warranty of
0011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012    Library General Public License for more details.
0013 
0014    You should have received a copy of the GNU Library General Public License
0015    along with this library; see the file COPYING.LGPL.  If not, write to
0016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0017    Boston, MA 02110-1301, USA.
0018 
0019    Alternatively, this file is available under the Mozilla Public License
0020    Version 1.1.  You may obtain a copy of the License at
0021    http://www.mozilla.org/MPL/
0022 */
0023 
0024 #include "ipsocket.h"
0025 
0026 #include "connectaddress.h"
0027 #include "ipresolver.h"
0028 
0029 #ifdef __unix__
0030 #include <errno.h>
0031 #include <netinet/in.h>
0032 #include <sys/ioctl.h>
0033 #include <sys/socket.h>
0034 #include <fcntl.h>
0035 #include <unistd.h>
0036 #endif
0037 #ifdef _WIN32
0038 #include <winsock2.h>
0039 typedef SSIZE_T ssize_t;
0040 #endif
0041 
0042 #include <cassert>
0043 #include <climits>
0044 #include <cstdlib>
0045 #include <cstring>
0046 
0047 #include <iostream>
0048 
0049 static bool errorTryAgainLater()
0050 {
0051 #ifdef _WIN32
0052     return WSAGetLastError() == WSAEWOULDBLOCK;
0053 #else
0054     const int en = errno; // re-fetching errno might have a small cost
0055     return en == EAGAIN || en == EWOULDBLOCK;
0056 #endif
0057 }
0058 
0059 static bool errorInterrupted()
0060 {
0061 #ifdef _WIN32
0062     return WSAGetLastError() == WSAEINTR;
0063 #else
0064     return errno == EINTR;
0065 #endif
0066 }
0067 
0068 static bool setNonBlocking(int fd)
0069 {
0070 #ifdef _WIN32
0071     unsigned long value = 1; // 0 blocking, != 0 non-blocking
0072     if (ioctlsocket(fd, FIONBIO, &value) != NO_ERROR) {
0073         return false;
0074     }
0075 #else
0076     // don't let forks inherit the file descriptor - that can cause confusion...
0077     fcntl(fd, F_SETFD, FD_CLOEXEC);
0078 
0079     // To be able to use the same send() and recv() calls as Windows, also set the non-blocking
0080     // property on the socket descriptor here instead of passing MSG_DONTWAIT to send() and recv().
0081     const int oldFlags = fcntl(fd, F_GETFL);
0082     if (oldFlags == -1) {
0083         return false;
0084     }
0085     fcntl(fd, F_SETFL, oldFlags | O_NONBLOCK);
0086 #endif
0087     return true;
0088 }
0089 
0090 static int sendFlags()
0091 {
0092 #ifdef _WIN32
0093     return 0;
0094 #else
0095     return MSG_NOSIGNAL;
0096 #endif
0097 }
0098 
0099 static void closeSocket(int fd)
0100 {
0101 #ifdef _WIN32
0102     closesocket(fd);
0103 #else
0104     ::close(fd);
0105 #endif
0106 }
0107 
0108 // TODO implement address family (IPv4 / IPv6) support
0109 IpSocket::IpSocket(const ConnectAddress &ca)
0110    : m_fd(-1)
0111 {
0112     assert(ca.type() == ConnectAddress::Type::Tcp || ca.type() == ConnectAddress::Type::Tcp4);
0113     if (ca.type() != ConnectAddress::Type::Tcp && ca.type() != ConnectAddress::Type::Tcp4) {
0114         std::cerr << "IpSocket contruction failed 0.\n";
0115         return;
0116     }
0117 #ifdef _WIN32
0118     WSAData wsadata;
0119     // IPv6 requires Winsock v2.0 or better (but we're not using IPv6 - yet!)
0120     if (WSAStartup(MAKEWORD(2, 0), &wsadata) != 0) {
0121         std::cerr << "IpSocket contruction failed A.\n";
0122         return;
0123     }
0124 #endif
0125     const FileDescriptor fd = socket(AF_INET, SOCK_STREAM, 0);
0126     if (!isValidFileDescriptor(fd)) {
0127         std::cerr << "IpSocket contruction failed B.\n";
0128         return;
0129     }
0130 
0131     IpResolver resolver(ca);
0132     bool ok = resolver.resultValid();
0133 
0134     ok = ok && connect(fd, resolver.resolved(), resolver.resolvedLength()) == 0;
0135 
0136     // only make it non-blocking after connect() because Winsock returns
0137     // WSAEWOULDBLOCK when connecting a non-blocking socket
0138     ok = ok && setNonBlocking(fd);
0139 
0140     if (ok) {
0141         m_fd = fd;
0142     } else {
0143         closeSocket(fd);
0144     }
0145 }
0146 
0147 IpSocket::IpSocket(FileDescriptor fd)
0148    : m_fd(fd)
0149 {
0150     if (!setNonBlocking(m_fd)) {
0151         closeSocket(fd);
0152         m_fd = -1;
0153     }
0154 }
0155 
0156 IpSocket::~IpSocket()
0157 {
0158     close();
0159 #ifdef _WIN32
0160     WSACleanup();
0161 #endif
0162 }
0163 
0164 void IpSocket::platformClose()
0165 {
0166     if (isValidFileDescriptor(m_fd)) {
0167         closeSocket(m_fd);
0168         m_fd = InvalidFileDescriptor;
0169     }
0170 }
0171 
0172 IO::Result IpSocket::write(chunk a)
0173 {
0174     IO::Result ret;
0175     if (!isValidFileDescriptor(m_fd)) {
0176         std::cerr << "\nIpSocket::write() failed A.\n\n";
0177         ret.status = IO::Status::InternalError;
0178         return ret;
0179     }
0180 
0181     const uint32 initialLength = a.length;
0182 
0183     while (a.length > 0) {
0184         ssize_t nbytes = send(m_fd, reinterpret_cast<char *>(a.ptr), a.length, sendFlags());
0185         if (nbytes < 0) {
0186             if (errorInterrupted()) {
0187                 continue;
0188             }
0189             // see EAGAIN comment in LocalSocket::read()
0190             if (errorTryAgainLater()) {
0191                 break;
0192             }
0193             close();
0194             ret.status = IO::Status::InternalError;
0195             return ret;
0196         } else if (nbytes == 0) {
0197             break;
0198         }
0199 
0200         a.ptr += nbytes;
0201         a.length -= uint32(nbytes);
0202     }
0203 
0204     ret.length = initialLength - a.length;
0205     return ret;
0206 }
0207 
0208 IO::Result IpSocket::read(byte *buffer, uint32 maxSize)
0209 {
0210     IO::Result ret;
0211     if (maxSize <= 0) {
0212         std::cerr << "\nIpSocket::read() failed A.\n\n";
0213         ret.status = IO::Status::InternalError;
0214         return ret;
0215     }
0216 
0217     while (maxSize > 0) {
0218         ssize_t nbytes = recv(m_fd, reinterpret_cast<char *>(buffer), maxSize, 0);
0219         if (nbytes < 0) {
0220             if (errorInterrupted()) {
0221                 continue;
0222             }
0223             // see comment in LocalSocket for rationale of EAGAIN behavior
0224             if (errorTryAgainLater()) {
0225                 break;
0226             }
0227             close();
0228             ret.status = IO::Status::RemoteClosed;
0229             break;
0230         } else if (nbytes == 0) {
0231             // orderly shutdown
0232             close();
0233             ret.status = IO::Status::RemoteClosed;
0234             break;
0235         }
0236         ret.length += uint32(nbytes);
0237         buffer += nbytes;
0238         maxSize -= uint32(nbytes);
0239     }
0240 
0241     return ret;
0242 }
0243 
0244 bool IpSocket::isOpen()
0245 {
0246     return isValidFileDescriptor(m_fd);
0247 }
0248 
0249 FileDescriptor IpSocket::fileDescriptor() const
0250 {
0251     return m_fd;
0252 }