/system/jlib/jsocket.cpp
C++ | 5993 lines | 5423 code | 459 blank | 111 comment | 863 complexity | c2caf370a2f41ff6eb37f88a13a46a5a MD5 | raw file
Large files files are truncated, but you can click here to view the full file
- /*##############################################################################
- HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- ############################################################################## */
- // New IPv6 Version - IN PROGRESS
- /*
- TBD IPv6 connect
- multicast
- look at loopback
- */
- #include "platform.h"
- #ifdef _VER_C5
- #include <clwclib.h>
- #else
- #include "platform.h"
- #include <stdio.h>
- #endif
- #include <algorithm>
- #ifdef _WIN32
- #define WIN32_LEAN_AND_MEAN
- #include <windows.h>
- #include <winsock2.h>
- #include <ws2tcpip.h>
- #include <signal.h>
- #else
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <netinet/tcp.h>
- #include <netinet/in.h>
- #include <arpa/inet.h>
- #include <stddef.h>
- #include <errno.h>
- #include <net/if.h>
- #endif
- #include <limits.h>
- #include "jmutex.hpp"
- #include "jsocket.hpp"
- #include "jexcept.hpp"
- #include "jio.hpp"
- #include "jmisc.hpp"
- #include "jthread.hpp"
- #include "jqueue.tpp"
- #include "jtime.hpp"
- #include "jprop.hpp"
- #include "jregexp.hpp"
- #include "jdebug.hpp"
- #include "build-config.h"
- // epoll only with linux
- #ifndef __linux__
- # undef _HAS_EPOLL_SUPPORT
- #else
- # define _HAS_EPOLL_SUPPORT
- # ifdef _HAS_EPOLL_SUPPORT
- # include <unistd.h>
- # include <sys/epoll.h>
- # ifndef EPOLLRDHUP
- // Centos 5.x bug - epoll.h does not define but its in the kernel
- # define EPOLLRDHUP 0x2000
- # endif
- # endif
- #endif
- // various options
- #define CONNECT_TIMEOUT_REFUSED_WAIT 1000 // maximum to sleep on connect_timeout
- #define TRACE_SLOW_BLOCK_TRANSFER
- #define DEFAULT_CONNECT_TIME (100*1000) // for connect_wait
- #ifndef _WIN32
- #define BLOCK_POLLED_SINGLE_CONNECTS // NB this is much slower in windows
- #define CENTRAL_NODE_RANDOM_DELAY
- #else
- #define USERECVSEM // to singlethread BF_SYNC_TRANSFER_PUSH
- #endif
- #ifdef _DEBUG
- //#define SOCKTRACE
- //#define EPOLLTRACE
- #endif
- #ifdef _TESTING
- #define _TRACE
- #endif
- #ifdef _TRACE
- #define THROWJSOCKEXCEPTION(exc) \
- { StringBuffer msg; \
- msg.appendf("Target: %s, Raised in: %s, line %d",tracename ,__FILE__, __LINE__); \
- IJSOCK_Exception *e = new SocketException(exc,msg.str());\
- throw e; }
- #define THROWJSOCKEXCEPTION2(exc) \
- { StringBuffer msg; \
- msg.appendf("Raised in: %s, line %d",__FILE__, __LINE__); \
- IJSOCK_Exception *e = new SocketException(exc,msg.str());\
- throw e; }
- #define LOGERR(err,ref,info) LogErr(err,ref,info,__LINE__,NULL)
- #define LOGERR2(err,ref,info) LogErr(err,ref,info,__LINE__,tracename)
-
- #else
- #define THROWJSOCKEXCEPTION(exc) \
- { IJSOCK_Exception *e = new SocketException(exc);\
- throw e; }
- #define THROWJSOCKEXCEPTION2(exc) THROWJSOCKEXCEPTION(exc)
- #define LOGERR(err,ref,info)
- #define LOGERR2(err,ref,info)
- #endif
- JSocketStatistics STATS;
- static bool IP4only=false; // slighly faster if we know no IPv6
- static bool IP6preferred=false; // e.g. for DNS and socket create
- IpSubNet PreferredSubnet(NULL,NULL); // set this if you prefer a particular subnet for debugging etc
- // e.g. PreferredSubnet("192.168.16.0", "255.255.255.0")
- static atomic_t pre_conn_unreach_cnt = ATOMIC_INIT(0); // global count of pre_connect() ENETUNREACH error
- #define IPV6_SERIALIZE_PREFIX (0x00ff00ff)
- inline void LogErr(unsigned err,unsigned ref,const char *info,unsigned lineno,const char *tracename)
- {
- if (err)
- PROGLOG("jsocket(%d,%d)%s%s err = %d%s%s",ref,lineno,
- (info&&*info)?" ":"",(info&&*info)?info:"",err,
- (tracename&&*tracename)?" : ":"",(tracename&&*tracename)?tracename:"");
- }
-
- class jlib_thrown_decl SocketException: public CInterface, public IJSOCK_Exception
- {
- public:
- IMPLEMENT_IINTERFACE;
- SocketException(int code,const char *_msg=NULL) : errcode(code)
- {
- if (_msg)
- msg = strdup(_msg);
- else
- msg = NULL;
- };
- ~SocketException() { free(msg); }
-
- int errorCode() const { return errcode; }
- static StringBuffer & geterrormessage(int err,StringBuffer &str)
- {
- switch (err) {
- case JSOCKERR_ok: return str.append("ok");
- case JSOCKERR_not_opened: return str.append("socket not opened");
- case JSOCKERR_bad_address: return str.append("bad address");
- case JSOCKERR_connection_failed: return str.append("connection failed");
- case JSOCKERR_broken_pipe: return str.append("connection is broken");
- case JSOCKERR_graceful_close: return str.append("connection closed other end");
- case JSOCKERR_invalid_access_mode: return str.append("invalid access mode");
- case JSOCKERR_timeout_expired: return str.append("timeout expired");
- case JSOCKERR_port_in_use: return str.append("port in use");
- case JSOCKERR_cancel_accept: return str.append("cancel accept");
- case JSOCKERR_connectionless_socket: return str.append("connectionless socket");
- case JSOCKERR_handle_too_large: return str.append("handle too large");
- case JSOCKERR_bad_netaddr: return str.append("bad net addr");
- case JSOCKERR_ipv6_not_implemented: return str.append("IPv6 not implemented");
- // OS errors
- #ifdef _WIN32
- case WSAEINTR: return str.append("WSAEINTR(10004) - Interrupted system call.");
- case WSAEBADF: return str.append("WSAEBADF(10009) - Bad file number.");
- case WSAEACCES: return str.append("WSAEACCES(10013) - Permission denied.");
- case WSAEFAULT: return str.append("WSAEFAULT(10014) - Bad address.");
- case WSAEINVAL: return str.append("WSAEINVAL(10022) - Invalid argument.");
- case WSAEMFILE: return str.append("WSAEMFILE(10024) - Too many open files.");
- case WSAEWOULDBLOCK: return str.append("WSAEWOULDBLOCK(10035) - Operation would block.");
- case WSAEINPROGRESS: return str.append("WSAEINPROGRESS(10036) - Operation now in progress.");
- case WSAEALREADY: return str.append("WSAEALREADY(10037) - Operation already in progress.");
- case WSAENOTSOCK: return str.append("WSAENOTSOCK(10038) - Socket operation on nonsocket.");
- case WSAEDESTADDRREQ: return str.append("WSAEDESTADDRREQ(10039) - Destination address required.");
- case WSAEMSGSIZE: return str.append("WSAEMSGSIZE(10040) - Message too long.");
- case WSAEPROTOTYPE: return str.append("WSAEPROTOTYPE(10041) - Protocol wrong type for socket.");
- case WSAENOPROTOOPT: return str.append("WSAENOPROTOOPT(10042) - Protocol not available.");
- case WSAEPROTONOSUPPORT: return str.append("WSAEPROTONOSUPPORT(10043) - Protocol not supported.");
- case WSAESOCKTNOSUPPORT: return str.append("WSAESOCKTNOSUPPORT(10044) - Socket type not supported.");
- case WSAEOPNOTSUPP: return str.append("WSAEOPNOTSUPP(10045) - Operation not supported on socket.");
- case WSAEPFNOSUPPORT: return str.append("WSAEPFNOSUPPORT(10046) - Protocol family not supported.");
- case WSAEAFNOSUPPORT: return str.append("WSAEAFNOSUPPORT(10047) - Address family not supported by protocol family.");
- case WSAEADDRINUSE: return str.append("WSAEADDRINUSE(10048) - Address already in use.");
- case WSAEADDRNOTAVAIL: return str.append("WSAEADDRNOTAVAIL(10049) - Cannot assign requested address.");
- case WSAENETDOWN: return str.append("WSAENETDOWN(10050) - Network is down.");
- case WSAENETUNREACH: return str.append("WSAENETUNREACH(10051) - Network is unreachable.");
- case WSAENETRESET: return str.append("WSAENETRESET(10052) - Network dropped connection on reset.");
- case WSAECONNABORTED: return str.append("WSAECONNABORTED(10053) - Software caused connection abort.");
- case WSAECONNRESET: return str.append("WSAECONNRESET(10054) - Connection reset by peer.");
- case WSAENOBUFS: return str.append("WSAENOBUFS(10055) - No buffer space available.");
- case WSAEISCONN: return str.append("WSAEISCONN(10056) - Socket is already connected.");
- case WSAENOTCONN: return str.append("WSAENOTCONN(10057) - Socket is not connected.");
- case WSAESHUTDOWN: return str.append("WSAESHUTDOWN(10058) - Cannot send after socket shutdown.");
- case WSAETOOMANYREFS: return str.append("WSAETOOMANYREFS(10059) - Too many references: cannot splice.");
- case WSAETIMEDOUT: return str.append("WSAETIMEDOUT(10060) - Connection timed out.");
- case WSAECONNREFUSED: return str.append("WSAECONNREFUSED(10061) - Connection refused.");
- case WSAELOOP: return str.append("WSAELOOP(10062) - Too many levels of symbolic links.");
- case WSAENAMETOOLONG: return str.append("WSAENAMETOOLONG(10063) - File name too long.");
- case WSAEHOSTDOWN: return str.append("WSAEHOSTDOWN(10064) - Host is down.");
- case WSAEHOSTUNREACH: return str.append("WSAEHOSTUNREACH(10065) - No route to host.");
- case WSASYSNOTREADY: return str.append("WSASYSNOTREADY(10091) - The network subsystem is unusable.");
- case WSAVERNOTSUPPORTED: return str.append("WSAVERNOTSUPPORTED(10092) - The Windows Sockets DLL cannot support this application.");
- case WSANOTINITIALISED: return str.append("WSANOTINITIALISED(10093) - Winsock not initialized.");
- case WSAEDISCON: return str.append("WSAEDISCON(10101) - Disconnect.");
- case WSAHOST_NOT_FOUND: return str.append("WSAHOST_NOT_FOUND(11001) - Host not found.");
- case WSATRY_AGAIN: return str.append("WSATRY_AGAIN(11002) - Nonauthoritative host not found.");
- case WSANO_RECOVERY: return str.append("WSANO_RECOVERY(11003) - Nonrecoverable error.");
- case WSANO_DATA: return str.append("WSANO_DATA(11004) - Valid name, no data record of requested type.");
- #else
- case ENOTSOCK: return str.append("ENOTSOCK - Socket operation on non-socket ");
- case EDESTADDRREQ: return str.append("EDESTADDRREQ - Destination address required ");
- case EMSGSIZE: return str.append("EMSGSIZE - Message too long ");
- case EPROTOTYPE: return str.append("EPROTOTYPE - Protocol wrong type for socket ");
- case ENOPROTOOPT: return str.append("ENOPROTOOPT - Protocol not available ");
- case EPROTONOSUPPORT: return str.append("EPROTONOSUPPORT - Protocol not supported ");
- case ESOCKTNOSUPPORT: return str.append("ESOCKTNOSUPPORT - Socket type not supported ");
- case EOPNOTSUPP: return str.append("EOPNOTSUPP - Operation not supported on socket ");
- case EPFNOSUPPORT: return str.append("EPFNOSUPPORT - Protocol family not supported ");
- case EAFNOSUPPORT: return str.append("EAFNOSUPPORT - Address family not supported by protocol family ");
- case EADDRINUSE: return str.append("EADDRINUSE - Address already in use ");
- case EADDRNOTAVAIL: return str.append("EADDRNOTAVAIL - Can't assign requested address ");
- case ENETDOWN: return str.append("ENETDOWN - Network is down ");
- case ENETUNREACH: return str.append("ENETUNREACH - Network is unreachable ");
- case ENETRESET: return str.append("ENETRESET - Network dropped connection because of reset ");
- case ECONNABORTED: return str.append("ECONNABORTED - Software caused connection abort ");
- case ECONNRESET: return str.append("ECONNRESET - Connection reset by peer ");
- case ENOBUFS: return str.append("ENOBUFS - No buffer space available ");
- case EISCONN: return str.append("EISCONN - Socket is already connected ");
- case ENOTCONN: return str.append("ENOTCONN - Socket is not connected ");
- case ESHUTDOWN: return str.append("ESHUTDOWN - Can't send after socket shutdown ");
- case ETOOMANYREFS: return str.append("ETOOMANYREFS - Too many references: can't splice ");
- case ETIMEDOUT: return str.append("ETIMEDOUT - Connection timed out ");
- case ECONNREFUSED: return str.append("ECONNREFUSED - Connection refused ");
- case EHOSTDOWN: return str.append("EHOSTDOWN - Host is down ");
- case EHOSTUNREACH: return str.append("EHOSTUNREACH - No route to host ");
- case EWOULDBLOCK: return str.append("EWOULDBLOCK - operation already in progress");
- case EINPROGRESS: return str.append("EINPROGRESS - operation now in progress ");
- #endif
- }
- IException *ose = MakeOsException(err);
- ose->errorMessage(str);
- ose->Release();
- return str;
- }
- StringBuffer & errorMessage(StringBuffer &str) const
- {
- if (msg)
- return geterrormessage(errcode,str).append('\n').append(msg);
- return geterrormessage(errcode,str);
- }
- MessageAudience errorAudience() const
- {
- switch (errcode) {
- case JSOCKERR_port_in_use:
- return MSGAUD_operator;
- }
- return MSGAUD_user;
- }
- private:
- int errcode;
- char *msg;
- };
- IJSOCK_Exception *IPv6NotImplementedException(const char *filename,unsigned lineno)
- {
- StringBuffer msg;
- msg.appendf("%s(%d)",filename,lineno);
- return new SocketException(JSOCKERR_ipv6_not_implemented,msg.str());
- }
- struct MCASTREQ
- {
- struct in_addr imr_multiaddr; /* multicast group to join */
- struct in_addr imr_interface; /* interface to join on */
- MCASTREQ(const char *mcip)
- {
- imr_multiaddr.s_addr = inet_addr(mcip);
- imr_interface.s_addr = htonl(INADDR_ANY);
- }
- };
- #ifdef __APPLE__
- #ifndef MSG_NOSIGNAL
- #define MSG_NOSIGNAL 0x4000
- #endif
- #endif
- #if defined( _WIN32)
- #define T_SOCKET SOCKET
- #define T_FD_SET fd_set
- #define XFD_SETSIZE FD_SETSIZE
- #define ETIMEDOUT WSAETIMEDOUT
- #define ECONNREFUSED WSAECONNREFUSED
- #define XFD_ZERO(s) FD_ZERO(s)
- #define SEND_FLAGS 0
- #define BADSOCKERR(err) ((err==WSAEBADF)||(err==WSAENOTSOCK))
- #define CHECKSOCKRANGE(s)
- #elif defined(__FreeBSD__) || defined(__APPLE__)
- #define XFD_SETSIZE FD_SETSIZE
- #define T_FD_SET fd_set
- #define XFD_ZERO(s) FD_ZERO(s)
- #define T_SOCKET int
- #define SEND_FLAGS (MSG_NOSIGNAL)
- #define BADSOCKERR(err) ((err==EBADF)||(err==ENOTSOCK))
- #define CHECKSOCKRANGE(s)
- #else
- #define XFD_SETSIZE 32768
- struct xfd_set { __fd_mask fds_bits[XFD_SETSIZE / __NFDBITS]; }; // define our own
- // linux 64 bit
- #ifdef __linux__
- #ifdef __x86_64__
- #undef __FDMASK
- #define __FDMASK(d) (1UL << ((d) % __NFDBITS))
- #undef __FDELT
- #define __FDELT(d) ((d) / __NFDBITS)
- #undef __FD_SET
- #define __FD_SET(d, s) (__FDS_BITS (s)[__FDELT(d)] |= __FDMASK(d))
- #undef __FD_ISSET
- #define __FD_ISSET(d, s) ((__FDS_BITS (s)[__FDELT(d)] & __FDMASK(d)) != 0)
- #endif
- #define CHECKSOCKRANGE(s) { if (s>=XFD_SETSIZE) THROWJSOCKEXCEPTION2(JSOCKERR_handle_too_large); }
- #endif
- // end 64 bit
- #define T_FD_SET xfd_set
- #define XFD_ZERO(s) memset(s,0,sizeof(xfd_set))
- #define T_SOCKET int
- #define SEND_FLAGS (MSG_NOSIGNAL)
- #define BADSOCKERR(err) ((err==EBADF)||(err==ENOTSOCK))
- #endif
- #ifdef CENTRAL_NODE_RANDOM_DELAY
- static SocketEndpointArray CentralNodeArray;
- #endif
- enum SOCKETMODE { sm_tcp_server, sm_tcp, sm_udp_server, sm_udp, sm_multicast_server, sm_multicast};
- class CSocket: public CInterface, public ISocket
- {
- public:
- IMPLEMENT_IINTERFACE;
- static CriticalSection crit;
- protected:
- friend class CSocketConnectWait;
- enum { ss_open, ss_shutdown, ss_close, ss_pre_open } state;
- T_SOCKET sock;
- char* hostname; // host address
- unsigned short hostport; // host port
- SOCKETMODE sockmode;
- IpAddress targetip;
- SocketEndpoint returnep; // set by set_return_addr
-
- MCASTREQ * mcastreq;
- size32_t nextblocksize;
- unsigned blockflags;
- unsigned blocktimeoutms;
- bool owned;
- enum {accept_not_cancelled, accept_cancel_pending, accept_cancelled} accept_cancel_state;
- bool in_accept;
- bool nonblocking;
- bool nagling;
- static unsigned connectingcount;
- #ifdef USERECVSEM
- static Semaphore receiveblocksem;
- bool receiveblocksemowned; // owned by this socket
- #endif
- #ifdef _TRACE
- char * tracename;
- #endif
-
- public:
- void open(int listen_queue_size,bool reuseports=false);
- bool connect_timeout( unsigned timeout, bool noexception);
- void connect_wait( unsigned timems);
- void udpconnect();
- void read(void* buf, size32_t min_size, size32_t max_size, size32_t &size_read,unsigned timeoutsecs);
- void readtms(void* buf, size32_t min_size, size32_t max_size, size32_t &size_read, unsigned timedelaysecs);
- void read(void* buf, size32_t size);
- size32_t write(void const* buf, size32_t size);
- size32_t write_multiple(unsigned num,void const**buf, size32_t *size);
- size32_t udp_write_to(SocketEndpoint &ep,void const* buf, size32_t size);
- void close();
- void errclose();
- bool connectionless() { return (sockmode!=sm_tcp)&&(sockmode!=sm_tcp_server); }
- void shutdown(unsigned mode);
- ISocket* accept(bool allowcancel);
- int wait_read(unsigned timeout);
- int wait_write(unsigned timeout);
- int name(char *name,size32_t namemax);
- int peer_name(char *name,size32_t namemax);
- SocketEndpoint &getPeerEndpoint(SocketEndpoint &ep);
- IpAddress & getPeerAddress(IpAddress &addr);
- void set_return_addr(int port,const char *name); // sets returnep
- void cancel_accept();
- size32_t get_max_send_size();
- bool set_nonblock(bool on=true);
- bool set_nagle(bool on);
- void set_linger(int lingersecs);
- void set_keep_alive(bool set);
- virtual void set_inherit(bool inherit=false);
- virtual bool check_connection();
-
- // Block functions
- void set_block_mode(unsigned flags,size32_t recsize=0,unsigned timeoutms=0);
- bool send_block(const void *blk,size32_t sz);
- size32_t receive_block_size();
- size32_t receive_block(void *blk,size32_t sz);
- size32_t get_send_buffer_size();
- void set_send_buffer_size(size32_t sz);
- bool join_multicast_group(SocketEndpoint &ep); // for udp multicast
- bool leave_multicast_group(SocketEndpoint &ep); // for udp multicast
- size32_t get_receive_buffer_size();
- void set_receive_buffer_size(size32_t sz);
- size32_t avail_read();
- int pre_connect(bool block);
- int post_connect();
- CSocket(const SocketEndpoint &_ep,SOCKETMODE smode,const char *name);
- CSocket(T_SOCKET new_sock,SOCKETMODE smode,bool _owned);
- virtual ~CSocket();
- unsigned OShandle()
- {
- return (unsigned)sock;
- }
- private:
- int closesock()
- {
- if (sock!=INVALID_SOCKET) {
- T_SOCKET s = sock;
- sock = INVALID_SOCKET;
- STATS.activesockets--;
- #ifdef SOCKTRACE
- PROGLOG("SOCKTRACE: Closing socket %x %d (%x)", s, s, this);
- #endif
- #ifdef _WIN32
- return ::closesocket(s);
- #else
- return ::close(s);
- #endif
- }
- else
- return 0;
- }
- };
- CriticalSection CSocket::crit;
- unsigned CSocket::connectingcount=0;
- #ifdef USERECVSEM
- Semaphore CSocket::receiveblocksem(2);
- #endif
- #ifdef _WIN32
- class win_socket_library
- {
- static bool initdone; // to prevent dependancy probs very early on (e.g. jlog)
- public:
- win_socket_library() { init(); }
- bool init()
- {
- if (initdone)
- return true;
- WSADATA wsa;
- if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) {
- if (WSAStartup(MAKEWORD(1, 1), &wsa) != 0) {
- MessageBox(NULL,"Failed to initialize windows sockets","JLib Socket Error",MB_OK);
- return false;
- }
- }
- initdone = true;
- return true;
- }
- ~win_socket_library()
- {
- WSACleanup();
- }
- };
- bool win_socket_library::initdone = false;
- static win_socket_library ws32_lib;
- #define ERRNO() WSAGetLastError()
- #define EADDRINUSE WSAEADDRINUSE
- #define EINTRCALL WSAEINTR
- #define ECONNRESET WSAECONNRESET
- #define ECONNABORTED WSAECONNABORTED
- #define ENOTCONN WSAENOTCONN
- #define EWOULDBLOCK WSAEWOULDBLOCK
- #define EINPROGRESS WSAEINPROGRESS
- #define ENETUNREACH WSAENETUNREACH
- #define ENOTSOCK WSAENOTSOCK
- struct j_sockaddr_in6 {
- short sin6_family; /* AF_INET6 */
- u_short sin6_port; /* Transport level port number */
- u_long sin6_flowinfo; /* IPv6 flow information */
- struct in_addr6 sin6_addr; /* IPv6 address */
- u_long sin6_scope_id; /* set of interfaces for a scope */
- };
- typedef union {
- struct sockaddr sa;
- struct j_sockaddr_in6 sin6;
- struct sockaddr_in sin;
- } J_SOCKADDR;
- #define DEFINE_SOCKADDR(name) J_SOCKADDR name; memset(&name,0,sizeof(J_SOCKADDR))
- static int _inet_pton(int af, const char* src, void* dst)
- {
-
- DEFINE_SOCKADDR(u);
-
- int address_length;
-
- switch (af) {
- case AF_INET:
- u.sin.sin_family = AF_INET;
- address_length = sizeof (u.sin);
- break;
-
- case AF_INET6:
- u.sin6.sin6_family = AF_INET6;
- address_length = sizeof (u.sin6);
- break;
-
- default:
- #ifdef EAFNOSUPPORT
- errno = EAFNOSUPPORT;
- #else
- errno = 52;
- #endif
- return -1;
- }
- ws32_lib.init();
- int ret = WSAStringToAddress ((LPTSTR) src, af, NULL, &u.sa, &address_length);
- if (ret == 0) {
- switch (af) {
- case AF_INET:
- memcpy (dst, &u.sin.sin_addr, sizeof (struct in_addr));
- break;
-
- case AF_INET6:
- memcpy (dst, &u.sin6.sin6_addr, sizeof (u.sin6.sin6_addr));
- break;
- }
- return 1;
- }
- errno = WSAGetLastError();
- // PROGLOG("errno = %d",errno);
- return 0;
- }
- static const char * _inet_ntop (int af, const void *src, char *dst, socklen_t cnt)
- {
- /* struct sockaddr can't accomodate struct sockaddr_in6. */
- DEFINE_SOCKADDR(u);
- DWORD dstlen = cnt;
- size_t srcsize;
-
- memset(&u,0,sizeof(u));
- switch (af) {
- case AF_INET:
- u.sin.sin_family = AF_INET;
- u.sin.sin_addr = *(struct in_addr *) src;
- srcsize = sizeof (u.sin);
- break;
- case AF_INET6:
- u.sin6.sin6_family = AF_INET6;
- memcpy(&u.sin6.sin6_addr,src,sizeof(in_addr6));
- srcsize = sizeof (u.sin6);
- break;
- default:
- return NULL;
- }
-
- ws32_lib.init();
- if (WSAAddressToString (&u.sa, srcsize, NULL, dst, &dstlen) != 0) {
- errno = WSAGetLastError();
- return NULL;
- }
- return (const char *) dst;
- }
- int inet_aton (const char *name, struct in_addr *addr)
- {
- addr->s_addr = inet_addr (name);
- return (addr->s_addr == (u_long)-1)?1:0; // 255.255.255.255 has had it here
- }
- #else
- #define _inet_ntop inet_ntop
- #define _inet_pton inet_pton
- #define in_addr6 in6_addr
- typedef union {
- struct sockaddr sa;
- struct sockaddr_in6 sin6;
- struct sockaddr_in sin;
- } J_SOCKADDR;
- #define DEFINE_SOCKADDR(name) J_SOCKADDR name; memset(&name,0,sizeof(J_SOCKADDR))
- #define EINTRCALL EINTR
- #define ERRNO() (errno)
- #ifndef INADDR_NONE
- #define INADDR_NONE (-1)
- #endif
- #endif
- #ifndef INET6_ADDRSTRLEN
- #define INET6_ADDRSTRLEN 65
- #endif
- inline socklen_t setSockAddr(J_SOCKADDR &u, const IpAddress &ip,unsigned short port)
- {
- if (!IP6preferred) {
- if (ip.getNetAddress(sizeof(in_addr),&u.sin.sin_addr)==sizeof(in_addr)) {
- u.sin.sin_family = AF_INET;
- u.sin.sin_port = htons(port);
- return sizeof(u.sin);
- }
- }
- if (IP4only)
- IPV6_NOT_IMPLEMENTED();
- ip.getNetAddress(sizeof(in_addr6),&u.sin6.sin6_addr);
- u.sin6.sin6_family = AF_INET6;
- u.sin6.sin6_port = htons(port);
- return sizeof(u.sin6);
- }
- inline socklen_t setSockAddrAny(J_SOCKADDR &u, unsigned short port)
- {
- if (IP6preferred) {
- #ifdef _WIN32
- IN6ADDR_SETANY((PSOCKADDR_IN6)&u.sin6.sin6_addr);
- #else
- memcpy(&u.sin6.sin6_addr,&in6addr_any,sizeof(in_addr6));
- #endif
- u.sin6.sin6_family= AF_INET6;
- u.sin6.sin6_port = htons(port);
- return sizeof(u.sin6);
- }
- u.sin.sin_addr.s_addr = htonl(INADDR_ANY);
- u.sin.sin_family= AF_INET;
- u.sin.sin_port = htons(port);
- return sizeof(u.sin);
- }
- inline void getSockAddrEndpoint(const J_SOCKADDR &u, socklen_t ul, SocketEndpoint &ep)
- {
- if (ul==sizeof(u.sin)) {
- ep.setNetAddress(sizeof(in_addr),&u.sin.sin_addr);
- ep.port = htons(u.sin.sin_port);
- }
- else {
- ep.setNetAddress(sizeof(in_addr6),&u.sin6.sin6_addr);
- ep.port = htons(u.sin6.sin6_port);
- }
- }
- /* might need fcntl(F_SETFL), or ioctl(FIONBIO) */
- /* Posix.1g says fcntl */
- #if defined(O_NONBLOCK)
- bool CSocket::set_nonblock(bool on)
- {
- int flags = fcntl(sock, F_GETFL, 0);
- if (flags == -1)
- return nonblocking;
- if (on)
- flags |= O_NONBLOCK;
- else
- flags &= ~O_NONBLOCK;
- if (fcntl(sock, F_SETFL, flags)==0) {
- bool wasNonBlocking = nonblocking;
- nonblocking = on;
- return wasNonBlocking;
- }
- return nonblocking;
- }
- #else
- bool CSocket::set_nonblock(bool on)
- {
- #ifdef _WIN32
- u_long yes = on?1:0;
- if (ioctlsocket(sock, FIONBIO, &yes)==0) {
- #else
- int yes = on?1:0;
- if (ioctl(sock, FIONBIO, &yes)==0) {
- #endif
- bool wasNonBlocking = nonblocking;
- nonblocking = on;
- return wasNonBlocking;
- }
- return nonblocking;
- }
- #endif
- bool CSocket::set_nagle(bool on)
- {
- bool ret = nagling;
- nagling = on;
- int enabled = !on;
- if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char*)&enabled, sizeof(enabled)) != 0) {
- nagling = !on;
- }
- return ret;
- }
- void CSocket::set_inherit(bool inherit)
- {
- #ifndef _WIN32
- long flag = fcntl(sock, F_GETFD);
- if(inherit)
- flag &= ~FD_CLOEXEC;
- else
- flag |= FD_CLOEXEC;
- fcntl(sock, F_SETFD, flag);
- #endif
- }
- size32_t CSocket::avail_read()
- {
- #ifdef _WIN32
- u_long avail;
- if (ioctlsocket(sock, FIONREAD, &avail)==0)
- #else
- int avail;
- if (ioctl(sock, FIONREAD, &avail)==0)
- #endif
- return (size32_t)avail;
- int err = ERRNO();
- LOGERR2(err,1,"avail_read");
- return 0;
- }
- #define PRE_CONN_UNREACH_ELIM 100
- int CSocket::pre_connect (bool block)
- {
- assertex(hostname);
- DEFINE_SOCKADDR(u);
- if (targetip.isNull()) {
- set_return_addr(hostport,hostname);
- targetip.ipset(returnep);
- }
- socklen_t ul = setSockAddr(u,targetip,hostport);
- sock = ::socket(u.sa.sa_family, SOCK_STREAM, targetip.isIp4()?0:PF_INET6);
- owned = true;
- state = ss_pre_open; // will be set to open by post_connect
- if (sock == INVALID_SOCKET) {
- int err = ERRNO();
- THROWJSOCKEXCEPTION(err);
- }
- STATS.activesockets++;
- int err = 0;
- set_nonblock(!block);
- int rc = ::connect(sock, &u.sa, ul);
- if (rc==SOCKET_ERROR) {
- err = ERRNO();
- if ((err != EINPROGRESS)&&(err != EWOULDBLOCK)&&(err != ETIMEDOUT)&&(err!=ECONNREFUSED)) { // handled by caller
- if (err != ENETUNREACH) {
- atomic_set(&pre_conn_unreach_cnt, 0);
- LOGERR2(err,1,"pre_connect");
- } else {
- int ecnt = atomic_read(&pre_conn_unreach_cnt);
- if (ecnt <= PRE_CONN_UNREACH_ELIM) {
- atomic_inc(&pre_conn_unreach_cnt);
- LOGERR2(err,1,"pre_connect network unreachable");
- }
- }
- } else
- atomic_set(&pre_conn_unreach_cnt, 0);
- } else
- atomic_set(&pre_conn_unreach_cnt, 0);
- #ifdef SOCKTRACE
- PROGLOG("SOCKTRACE: pre-connected socket%s %x %d (%x) err=%d", block?"(block)":"", sock, sock, (int)this, err);
- #endif
- return err;
- }
- int CSocket::post_connect ()
- {
- set_nonblock(false);
- int err = 0;
- socklen_t errlen = sizeof(err);
- int rc = getsockopt(sock, SOL_SOCKET, SO_ERROR, (char *)&err, &errlen); // check for error
- if ((rc!=0)&&!err)
- err = ERRNO(); // some implementations of getsockopt duff
- if (err==0) {
- nagling = true;
- set_nagle(false);
- state = ss_open;
- }
- else if ((err!=ETIMEDOUT)&&(err!=ECONNREFUSED)) // handled by caller
- LOGERR2(err,1,"post_connect");
- return err;
- }
- void CSocket::open(int listen_queue_size,bool reuseports)
- {
- if (IP6preferred)
- sock = ::socket(AF_INET6, connectionless()?SOCK_DGRAM:SOCK_STREAM, PF_INET6);
- else
- sock = ::socket(AF_INET, connectionless()?SOCK_DGRAM:SOCK_STREAM, 0);
- if (sock == INVALID_SOCKET) {
- THROWJSOCKEXCEPTION(ERRNO());
- }
- STATS.activesockets++;
- #ifdef SOCKTRACE
- PROGLOG("SOCKTRACE: opened socket %x %d (%x)", sock,sock,this);
- #endif
- if ((hostport==0)&&(sockmode==sm_udp)) {
- state = ss_open;
- #ifdef SOCKTRACE
- PROGLOG("SOCKTRACE: opened socket return udp");
- #endif
- set_inherit(false);
- return;
- }
- #ifndef _WIN32
- reuseports = true; // for some reason linux requires reuse ports
- #endif
- if (reuseports) {
- int on = 1;
- setsockopt( sock, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on));
- }
- DEFINE_SOCKADDR(u);
- socklen_t ul;
- if (hostname) {
- if (targetip.isNull()) {
- set_return_addr(hostport,hostname);
- targetip.ipset(returnep);
- }
- ul = setSockAddr(u,targetip,hostport);
- }
- else
- ul = setSockAddrAny(u,hostport);
- int saverr;
- if (::bind(sock, &u.sa, ul) != 0) {
- saverr = ERRNO();
- if (saverr==EADDRINUSE) { // don't log as error (some usages probe ports)
- ErrPortInUse:
- closesock();
- char msg[1024];
- sprintf(msg,"Target: %s, port = %d, Raised in: %s, line %d",tracename,(int)hostport,__FILE__, __LINE__);
- IJSOCK_Exception *e = new SocketException(JSOCKERR_port_in_use,msg);
- throw e;
- }
- else {
- closesock();
- THROWJSOCKEXCEPTION(saverr);
- }
- }
- if (!connectionless()) {
- if (::listen(sock, listen_queue_size) != 0) {
- saverr = ERRNO();
- if (saverr==EADDRINUSE)
- goto ErrPortInUse;
- closesock();
- THROWJSOCKEXCEPTION(saverr);
- }
- }
- if (mcastreq) {
- if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,(char*)mcastreq, sizeof(*mcastreq))!=0) {
- saverr = ERRNO();
- closesock();
- THROWJSOCKEXCEPTION(saverr);
- }
- }
- state = ss_open;
- #ifdef SOCKTRACE
- PROGLOG("SOCKTRACE: opened socket return");
- #endif
- set_inherit(false);
- }
- ISocket* CSocket::accept(bool allowcancel)
- {
- if ((accept_cancel_state!=accept_not_cancelled) && allowcancel) {
- accept_cancel_state=accept_cancelled;
- return NULL;
- }
- if (state != ss_open) {
- ERRLOG("invalid accept, state = %d",(int)state);
- THROWJSOCKEXCEPTION(JSOCKERR_not_opened);
- }
- if (connectionless()) {
- THROWJSOCKEXCEPTION(JSOCKERR_connectionless_socket);
- }
- T_SOCKET newsock;
- loop {
- in_accept = true;
- newsock = (sock!=INVALID_SOCKET)?::accept(sock, NULL, NULL):INVALID_SOCKET;
- in_accept = false;
- #ifdef SOCKTRACE
- PROGLOG("SOCKTRACE: accept created socket %x %d (%x)", newsock,newsock,this);
- #endif
- if (newsock!=INVALID_SOCKET) {
- if ((sock==INVALID_SOCKET)||(accept_cancel_state==accept_cancel_pending)) {
- ::close(newsock);
- newsock=INVALID_SOCKET;
- }
- else {
- accept_cancel_state = accept_not_cancelled;
- break;
- }
- }
- int saverr;
- saverr = ERRNO();
- if ((sock==INVALID_SOCKET)||(accept_cancel_state==accept_cancel_pending)) {
- accept_cancel_state = accept_cancelled;
- if (allowcancel)
- return NULL;
- THROWJSOCKEXCEPTION(JSOCKERR_cancel_accept);
- }
- if (saverr != EINTRCALL) {
- accept_cancel_state = accept_not_cancelled;
- THROWJSOCKEXCEPTION(saverr);
- }
- }
- if (state != ss_open) {
- accept_cancel_state = accept_cancelled;
- if (allowcancel)
- return NULL;
- THROWJSOCKEXCEPTION(JSOCKERR_cancel_accept);
- }
- CSocket *ret = new CSocket(newsock,sm_tcp,true);
- ret->set_inherit(false);
- return ret;
- }
- void CSocket::set_linger(int lingertime)
- {
- struct linger l;
- l.l_onoff = (lingertime>=0)?1:0;
- l.l_linger = (lingertime>=0)?lingertime:0;
- if (setsockopt(sock, SOL_SOCKET, SO_LINGER, (char*)&l, sizeof(l)) != 0) {
- WARNLOG("Linger not set");
- }
- }
- void CSocket::set_keep_alive(bool set)
- {
- int on=set?1:0;
- if (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (char*)&on, sizeof(on)) != 0) {
- WARNLOG("KeepAlive not set");
- }
- }
- int CSocket::name(char *retname,size32_t namemax)
- {
- if (!retname)
- namemax = 0;
- if (namemax)
- retname[0] = 0;
- if (state != ss_open) {
- THROWJSOCKEXCEPTION(JSOCKERR_not_opened);
- }
- DEFINE_SOCKADDR(u);
- socklen_t ul = sizeof(u);
- if (::getsockname(sock,&u.sa, &ul)<0) {
- THROWJSOCKEXCEPTION(ERRNO());
- }
- SocketEndpoint ep;
- getSockAddrEndpoint(u,ul,ep);
- if (namemax>=1)
- {
- StringBuffer s;
- ep.getIpText(s);
- if (namemax-1<s.length())
- s.setLength(namemax-1);
- memcpy(retname,s.str(),s.length()+1);
- }
- return ep.port;
- }
- int CSocket::peer_name(char *retname,size32_t namemax)
- {
- // should not raise exceptions
- int ret = 0;
- if (!retname)
- namemax = 0;
- if (namemax)
- retname[0] = 0;
- if (state != ss_open) {
- return -1; // don't log as used to test socket
- }
- StringBuffer s;
- if (sockmode==sm_udp_server) { // udp server
- returnep.getIpText(s);
- ret = returnep.port;
- }
- else {
- DEFINE_SOCKADDR(u);
- socklen_t ul = sizeof(u);
- if (::getpeername(sock,&u.sa, &ul)<0)
- return -1; // don't log as used to test socket
- SocketEndpoint ep;
- getSockAddrEndpoint(u,ul,ep);
- ep.getIpText(s);
- ret = ep.port;
- }
- if (namemax>1) {
- if (namemax-1<s.length())
- s.setLength(namemax-1);
- memcpy(retname,s.str(),s.length()+1);
- }
- return ret;
- }
- SocketEndpoint &CSocket::getPeerEndpoint(SocketEndpoint &ep)
- {
- if (state != ss_open) {
- THROWJSOCKEXCEPTION(JSOCKERR_not_opened);
- }
- StringBuffer s;
- if (sockmode==sm_udp_server) { // udp server
- ep.set(returnep);
- }
- else {
- DEFINE_SOCKADDR(u);
- socklen_t ul = sizeof(u);
- if (::getpeername(sock,&u.sa, &ul)<0) {
- DBGLOG("getpeername failed %d",ERRNO());
- ep.set(NULL, 0);
- }
- else
- getSockAddrEndpoint(u,ul,ep);
- }
- return ep;
- }
- IpAddress & CSocket::getPeerAddress(IpAddress &addr)
- {
- SocketEndpoint ep;
- getPeerEndpoint(ep);
- addr = ep;
- return addr;
- }
- void CSocket::set_return_addr(int port,const char *retname)
- {
- if (!returnep.ipset(retname)) {
- IJSOCK_Exception *e = new SocketException(JSOCKERR_bad_address); // don't use THROWJSOCKEXCEPTION here
- throw e;
- }
- returnep.port = port;
- }
- void CSocket::cancel_accept()
- {
- if (connectionless()) {
- THROWJSOCKEXCEPTION(JSOCKERR_connectionless_socket);
- }
- #ifdef SOCKTRACE
- PROGLOG("SOCKTRACE: Cancel accept socket %x %d (%x)", sock, sock, this);
- #endif
- if (!in_accept) {
- accept_cancel_state = accept_cancelled;
- errclose();
- return;
- }
- accept_cancel_state = accept_cancel_pending;
- errclose(); // this should cause accept to terminate (not supported on all linux though)
- #ifdef _WIN32
- for (unsigned i=0;i<5;i++) { // windows closes on different lower priority thread
- Sleep(i);
- if (accept_cancel_state==accept_cancelled)
- return;
- }
- #else
- Sleep(0);
- if (accept_cancel_state==accept_cancelled)
- return;
- #endif
- // Wakeup listener using a connection
- SocketEndpoint ep(hostport,queryHostIP());
- Owned<CSocket> sock = new CSocket(ep,sm_tcp,NULL);
- try {
- sock->connect_timeout(100,true);
- }
- catch (IJSOCK_Exception *e) {
- EXCLOG(e,"CSocket::cancel_eccept");
- e->Release();
- }
- }
- // ================================================================================
- // connect versions
- ISocket* ISocket::connect( const SocketEndpoint &ep )
- {
- // general connect
- return ISocket::connect_wait(ep,DEFAULT_CONNECT_TIME);
- }
- inline void refused_sleep(CTimeMon &tm, unsigned &refuseddelay)
- {
- unsigned remaining;
- if (!tm.timedout(&remaining)) {
- if (refuseddelay<remaining/4) {
- Sleep(refuseddelay);
- if (refuseddelay<CONNECT_TIMEOUT_REFUSED_WAIT/2)
- refuseddelay *=2;
- else
- refuseddelay = CONNECT_TIMEOUT_REFUSED_WAIT;
- }
- else
- Sleep(remaining/4); // towards end of timeout approach gradually
- }
- }
- bool CSocket::connect_timeout( unsigned timeout, bool noexception)
- {
- // simple connect with timeout (no fancy stuff!)
- unsigned startt = usTick();
- CTimeMon tm(timeout);
- unsigned remaining;
- unsigned refuseddelay = 1;
- int err;
- while (!tm.timedout(&remaining)) {
- err = pre_connect(false);
- if ((err == EINPROGRESS)||(err == EWOULDBLOCK)) {
- T_FD_SET fds;
- struct timeval tv;
- XFD_ZERO(&fds);
- FD_SET((unsigned)sock, &fds);
- T_FD_SET except;
- XFD_ZERO(&except);
- FD_SET((unsigned)sock, &except);
- tv.tv_sec = remaining / 1000;
- tv.tv_usec = (remaining % 1000)*1000;
- CHECKSOCKRANGE(sock);
- int rc = ::select( sock + 1, NULL, (fd_set *)&fds, (fd_set *)&except, &tv );
- if (rc>0) {
- // select succeeded - return error from socket (0 if connected)
- socklen_t errlen = sizeof(err);
- rc = getsockopt(sock, SOL_SOCKET, SO_ERROR, (char *)&err, &errlen); // check for error
- if ((rc!=0)&&!err)
- err = ERRNO(); // some implementations of getsockopt duff
- if (err) // probably ECONNREFUSED but treat all errors same
- refused_sleep(tm,refuseddelay);
- }
- else if (rc<0) {
- err = ERRNO();
- LOGERR2(err,2,"::select");
- }
- }
- if (err==0) {
- err = post_connect();
- if (err==0) {
- STATS.connects++;
- STATS.connecttime+=usTick()-startt;
- #ifdef _TRACE
- char peer[256];
- peer[0] = 'C';
- peer[1] = '!';
- strcpy(peer+2,hostname?hostname:"(NULL)");
- free(tracename);
- tracename = strdup(peer);
- #endif
- return true;
- }
- }
- errclose();
- }
- #ifdef SOCKTRACE
- PROGLOG("connect_timeout: failed %d",err);
- #endif
- STATS.failedconnects++;
- STATS.failedconnecttime+=usTick()-startt;
- if (!noexception)
- THROWJSOCKEXCEPTION(JSOCKERR_connection_failed);
- return false;
- }
- ISocket* ISocket::connect_timeout(const SocketEndpoint &ep,unsigned timeout)
- {
- if (ep.isNull()||(ep.port==0))
- THROWJSOCKEXCEPTION2(JSOCKERR_bad_address);
- Owned<CSocket> sock = new CSocket(ep,sm_tcp,NULL);
- sock->connect_timeout(timeout,false);
- return sock.getClear();
- }
- #define POLLTIME 50
- void CSocket::connect_wait(unsigned timems)
- {
- // simple connect with timeout (no fancy stuff!)
- unsigned startt = usTick();
- CTimeMon tm(timems);
- bool exit = false;
- int err;
- unsigned refuseddelay = 1;
- while (!exit) {
- #ifdef CENTRAL_NODE_RANDOM_DELAY
- ForEachItemIn(cn,CentralNodeArray) {
- SocketEndpoint &ep=CentralNodeArray.item(cn);
- if (ep.ipequals(targetip)) {
- unsigned sleeptime = getRandom() % 1000;
- StringBuffer s;
- ep.getIpText(s);
- PrintLog("Connection to central node %s - sleeping %d milliseconds", s.str(), sleeptime);
- Sleep(sleeptime);
- break;
- }
- }
- #endif
- unsigned remaining;
- exit = tm.timedout(&remaining);
- bool blockselect = exit; // if last time round block
- {
- CriticalBlock block(crit);
- if (++connectingcount>4)
- blockselect = true;
- }
- err = pre_connect(blockselect);
- if (blockselect) {
- if (err&&!exit)
- refused_sleep(tm,refuseddelay); // probably ECONNREFUSED but treat all errors same
- }
- else {
- unsigned timeoutms = (exit||(remaining<10000))?10000:remaining;
- unsigned polltime = 1;
- while (!blockselect && ((err == EINPROGRESS)||(err == EWOULDBLOCK))) {
- T_FD_SET fds;
- struct timeval tv;
- XFD_ZERO(&fds);
- FD_SET((unsigned)sock, &fds);
- T_FD_SET except;
- XFD_ZERO(&except);
- FD_SET((unsigned)sock, &except);
- #ifdef BLOCK_POLLED_SINGLE_CONNECTS
- tv.tv_sec = timeoutms / 1000;
- tv.tv_usec = (timeoutms % 1000)*1000;
- #else
- tv.tv_sec = 0;
- tv.tv_usec = 0;
- #endif
- CHECKSOCKRANGE(sock);
- int rc = ::select( sock + 1, NULL, (fd_set *)&fds, (fd_set *)&except, &tv );
- if (rc>0) {
- // select succeeded - return error from socket (0 if connected)
- socklen_t errlen = sizeof(err);
- rc = getsockopt(sock, SOL_SOCKET, SO_ERROR, (char *)&err, &errlen); // check for error
- if ((rc!=0)&&!err)
- err = ERRNO(); // some implementations of getsockopt duff
- if (err)
- refused_sleep(tm,refuseddelay); // probably ECONNREFUSED but treat all errors same
- break;
- }
- if (rc<0) {
- err = ERRNO();
- LOGERR2(err,2,"::select");
- break;
- }
- if (!timeoutms) {
- #ifdef SOCKTRACE
- PROGLOG("connecttimeout: timed out");
- #endif
- err = -1;
- break;
- }
- #ifdef BLOCK_POLLED_SINGLE_CONNECTS
- break;
- #else
- if (timeoutms<polltime)
- polltime = timeoutms;
- Sleep(polltime); // sleeps 1-50ms (to let other threads run)
- timeoutms -= polltime;
- if (polltime>POLLTIME/2)
- polltime = POLLTIME;
- else
- polltime *= 2;
- #endif
- }
- }
- {
- CriticalBlock block(crit);
- --connectingcount;
- }
- if (err==0) {
- err = post_connect();
- if (err==0) {
- STATS.connects++;
- STATS.connecttime+=usTick()-startt;
- #ifdef _TRACE
- char peer[256];
- peer[0] = 'C';
- peer[1] = '!';
- strcpy(peer+2,hostname?hostname:"(NULL)");
- free(tracename);
- tracename = strdup(peer);
- #endif
- return;
- }
- }
- errclose();
- }
- #ifdef SOCKTRACE
- PROGLOG("connect_wait: failed %d",err);
- #endif
- STATS.failedconnects++;
- STATS.failedconnecttime+=usTick()-startt;
- THROWJSOCKEXCEPTION(JSOCKERR_connection_failed);
- }
- ISocket* ISocket::connect_wait( const SocketEndpoint &ep, unsigned timems)
- {
- if (ep.isNull()||(ep.port==0))
- THROWJSOCKEXCEPTION2(JSOCKERR_bad_address);
- Owned<CSocket> sock = new CSocket(ep,sm_tcp,NULL);
- sock->connect_wait(timems);
- return sock.getClear();
- }
- void CSocket::udpconnect()
- {
- DEFINE_SOCKADDR(u);
- if (targetip.isNull()) {
- set_return_addr(hostport,hostname);
- targetip.ipset(returnep);
- }
- socklen_t ul = setSockAddr(u,targetip,hostport);
- sock = ::socket(u.sa.sa_family, SOCK_DGRAM, targetip.isIp4()?0:PF_INET6);
- #ifdef SOCKTRACE
- PROGLOG("SOCKTRACE: udp connected socket %x %d (%x)", sock, sock, this);
- #endif
- STATS.activesockets++;
- if (sock == INVALID_SOCKET) {
- THROWJSOCKEXCEPTION(ERRNO());
- }
- int res = ::connect(sock, &u.sa, ul);
- if (res != 0) { // works for UDP
- closesock();
- THROWJSOCKEXCEPTION(JSOCKERR_connection_failed);
- }
- nagling = false; // means nothing for UDP
- state = ss_open;
- #ifdef _TRACE
- char peer[256];
- peer[0] = 'C';
- peer[1] = '!';
- strcpy(peer+2,hostname?hostname:"(NULL)");
- free(tracename);
- tracename = strdup(peer);
- #endif
- }
- int CSocket::wait_read(unsigned timeout)
- {
- int ret = 0;
- while (sock!=INVALID_SOCKET) {
- T_FD_SET fds;
- XFD_ZERO(&fds);
- FD_SET((unsigned)sock, &fds);
- CHECKSOCKRANGE(sock);
- if (timeout==WAIT_FOREVER) {
- ret = ::select( sock + 1, (fd_set *)&fds, NULL, NULL, NULL );
- }
- else {
- struct timeval tv;
- tv.tv_sec = timeout / 1000;
- tv.tv_usec = (timeout % 1000)*1000;
- ret = ::select( sock + 1, (fd_set *)&fds, NULL, NULL, &tv );
- }
- if (ret==SOCKET_ERROR) {
- int err = ERRNO();
- if (err!=EINTRCALL) { // else retry (should adjust time but for our usage don't think it matters that much)
- LOGERR2(err,1,"wait_read");
- break;
- }
- }
- else
- break;
- }
- return ret;
- }
- int CSocket::wait_write(unsigned timeout)
- {
- int ret = 0;
- while (sock!=INVALID_SOCKET) {
- T_FD_SET fds;
- XFD_ZERO(&fds);
- FD_SET((unsigned)sock, &fds);
- CHECKSOCKRANGE(sock);
- if (timeout==WAIT_FOREVER) {
- ret = ::select( sock + 1, NULL, (fd_set *)&fds, NULL, NULL );
- }
- else {
- struct timeval tv;
- tv.tv_sec = timeout / 1000;
- tv.tv_usec = (timeout % 1000)*1000;
- ret = ::select( sock + 1, NULL, (fd_set *)&fds, NULL, &tv );
- }
- if (ret==SOCKET_ERROR) {
- int err = ERRNO();
- if (err!=EINTRCALL) { // else retry (should adjust time but for our usage don't think it matters that much)
- LOGERR2(err,1,"wait_write");
- break;
- }
- }
- else
- break;
- }
- return ret;
- }
- void CSocket::readtms(void* buf, size32_t min_size, size32_t max_size, size32_t &size_read,
- unsigned timeoutms)
- {
- if (timeoutms == WAIT_FOREVER) {
- read(buf,min_size, max_size, size_read,WAIT_FOREVER);
- return;
- }
-
- unsigned startt=usTick();
- size_read = 0;
- if (state != ss_open) {
- THROWJSOCKEXCEPTION(JSOCKERR_not_opened);
- }
- unsigned start;
- unsigned timeleft;
- start = msTick();
- timeleft = timeoutms;
- do {
- int rc = wait_read(timeleft);
- if (rc < 0) {
- THROWJSOCKEXCEPTION(ERRNO());
- }
- if (rc == 0) {
- THROWJSOCKEXCEPTION(JSOCKERR_timeout_expired);
- }
- unsigned elapsed = (msTick()-start);
- if (elapsed<timeoutms)
- timeleft = timeoutms-elapsed;
- else
- timeleft = 0;
- unsigned retrycount=100;
- EintrRetry:
- if (sockmode==sm_udp_server) { // udp server
- DEFINE_SOCKADDR(u);
- socklen_t ul=sizeof(u);
- rc = recvfrom(sock, (char*)buf + size_read, max_size - size_read, 0, &u.sa,&ul);
- getSockAddrEndpoint(u,ul,returnep);
- }
- else {
- rc = recv(sock, (char*)buf + size_read, max_size - size_read, 0);
- }
- if (rc < 0) {
- int err = ERRNO();
- if (BADSOCKERR(err)) {
- // don't think this should happen but convert to same as shutdown while investigation
- LOGERR2(err,1,"Soc…
Large files files are truncated, but you can click here to view the full file