/*- * Copyright 2006-2007 Guram Dukashvili * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ //--------------------------------------------------------------------------- #include //------------------------------------------------------------------------------ namespace ksock { //------------------------------------------------------------------------------ //////////////////////////////////////////////////////////////////////////////// //------------------------------------------------------------------------------ /*#elif HAVE_SIGNAL_H int32_t err; if( fcntl(descriptor_,F_SETOWN,getpid()) < 0 ) goto l1; #ifdef F_SETSIG if( fcntl(descriptor_,F_SETSIG,sig_) < 0 ) goto l1; #endif if( fcntl(descriptor_,F_SETFL,fcntl(descriptor_,F_GETFL) | O_NONBLOCK | O_ASYNC) < 0 ) goto l1; #ifdef O_ONESIGFD if( fcntl(descriptor_,F_SETAUXFL,O_ONESIGFD) < 0 ) goto l1; #endif l2:; #endif*/ //------------------------------------------------------------------------------ AsyncSocket::~AsyncSocket() { close(); ksys::Requester::requester().detachDescriptor(*this); } //------------------------------------------------------------------------------ AsyncSocket::AsyncSocket() : maxRecvSize_(~uintptr_t(0)), maxSendSize_(~uintptr_t(0)), recvTimeout_(~uint64_t(0)), sendTimeout_(~uint64_t(0)) { #if defined(__WIN32__) || defined(__WIN64__) socket_ = INVALID_SOCKET; #endif ksys::Requester::requester().attachDescriptor(*this); } //------------------------------------------------------------------------------ bool AsyncSocket::isSocket() const { return true; } //------------------------------------------------------------------------------ AsyncSocket & AsyncSocket::open(int domain, int type, int protocol) { int32_t err; if( socket_ == INVALID_SOCKET ){ api.open(); #if defined(__WIN32__) || defined(__WIN64__) // WSAPROTOCOL_INFO protocolInfo; // memset(&protocolInfo,0,sizeof(protocolInfo)); // protocolInfo.dwMessageSize = 1; if( ksys::isWin9x() ) socket_ = api.WSASocketA(domain,type,protocol,NULL,0,WSA_FLAG_OVERLAPPED); else socket_ = api.WSASocketW(domain,type,protocol,NULL,0,WSA_FLAG_OVERLAPPED); #else socket_ = api.socket(domain,type,protocol); #endif if( socket_ == INVALID_SOCKET ){ err = errNo(); api.close(); newObjectV1C2(err,__PRETTY_FUNCTION__)->throwSP(); } try { #if !defined(__WIN32__) && !defined(__WIN64__) int flags = fcntl(socket_,F_GETFL,0); if( flags == -1 || fcntl(socket_,F_SETFL,flags | O_NONBLOCK) == -1 ){ err = errno; newObjectV1C2(err,__PRETTY_FUNCTION__)->throwSP(); } #endif #if defined(IP_TOS) && defined(IPTOS_LOWDELAY) int tos = IPTOS_LOWDELAY; setsockopt(IPPROTO_IP,IP_TOS,&tos,sizeof(tos)); #endif #ifdef TCP_NODELAY int on = 1; setsockopt(IPPROTO_TCP,TCP_NODELAY,&on,sizeof(on)); #endif if( type == SOCK_STREAM ){ #if defined(__WIN32__) || defined(__WIN64__) int bufLen = 0; setsockopt(SOL_SOCKET,SO_RCVBUF,&bufLen,sizeof(bufLen)); setsockopt(SOL_SOCKET,SO_SNDBUF,&bufLen,sizeof(bufLen)); #else /* socklen_t optlen; int lowait; optlen = sizeof(lowait); getsockopt(SOL_SOCKET,SO_SNDLOWAT,&lowait,optlen); if( lowait < 1024 ){ lowait = 2048; setsockopt(SOL_SOCKET,SO_SNDLOWAT,&lowait,sizeof(lowait)); } getsockopt(SOL_SOCKET,SO_RCVLOWAT,&lowait,optlen); lowait = 1; setsockopt(SOL_SOCKET,SO_RCVLOWAT,&lowait,sizeof(lowait)); struct timeval timeo; optlen = sizeof(timeo); getsockopt(SOL_SOCKET,SO_SNDTIMEO,&timeo,optlen); getsockopt(SOL_SOCKET,SO_RCVTIMEO,&timeo,optlen); timeo.tv_sec = LONG_MAX >> 10; timeo.tv_usec = 0; setsockopt(SOL_SOCKET,SO_RCVTIMEO,&timeo,sizeof(timeo)); setsockopt(SOL_SOCKET,SO_SNDTIMEO,&timeo,sizeof(timeo));*/ #endif int ka = true; setsockopt(SOL_SOCKET,SO_KEEPALIVE,&ka,sizeof(ka)); struct linger lg; lg.l_onoff = 0; lg.l_linger = 0; setsockopt(SOL_SOCKET,SO_LINGER,&lg,sizeof(lg)); int reuse = true; // socklen_t rlen = sizeof(reuse); // getsockopt(SOL_SOCKET,SO_REUSEADDR,&reuse,rlen); setsockopt(SOL_SOCKET,SO_REUSEADDR,&reuse,(socklen_t) sizeof(reuse)); } } catch( ... ){ close(); throw; } } return *this; } //------------------------------------------------------------------------------ AsyncSocket & AsyncSocket::close() { bool closed = false; { ksys::AutoLock lock(api.mutex()); if( socket_ != INVALID_SOCKET ){ if( api.closesocket(socket_) != 0 ){ int32_t err = errNo(); newObjectV1C2(err,__PRETTY_FUNCTION__)->throwSP(); } socket_ = INVALID_SOCKET; closed = true; } } if( closed ){ api.close(); } return *this; } //------------------------------------------------------------------------------ AsyncSocket & AsyncSocket::shutdown(int how) { ksys::AutoLock lock(api.mutex()); if( socket_ != INVALID_SOCKET ){ if( api.shutdown(socket_,how) != 0 ){ int32_t err = errNo(); if( err != ENOTCONN && err != ENOTSOCK && err != EWSANOTINITIALISED ) newObjectV1C2(err,__PRETTY_FUNCTION__)->throwSP(); } #if HAVE_KQUEUE ksys::Fiber * fiber = ksys::currentFiber(); if( fiber->event_.type_ == ksys::etAccept ){ ksys::AsyncIoSlave * slave = dynamic_cast(fiber->event_.ioSlave_); if( slave != NULL ) slave->cancelEvent(fiber->event_); } #endif } return *this; } //------------------------------------------------------------------------------ AsyncSocket & AsyncSocket::getsockopt(int level,int optname,void * optval,socklen_t & optlen) { if( api.getsockopt(socket_,level,optname,(char *) optval,&optlen) != 0 ){ int32_t err = errNo(); newObjectV1C2(err,__PRETTY_FUNCTION__)->throwSP(); } return *this; } //------------------------------------------------------------------------------ AsyncSocket & AsyncSocket::setsockopt(int level,int optname,const void * optval,socklen_t optlen) { if( api.setsockopt(socket_,level,optname,(const char *) optval,optlen) != 0 ){ int32_t err = errNo(); newObjectV1C2(err,__PRETTY_FUNCTION__)->throwSP(); } return *this; } //------------------------------------------------------------------------------ AsyncSocket & AsyncSocket::bind(const SockAddr & sockAddr) { if( api.bind(socket_,(struct sockaddr *) &sockAddr.addr4_,sockAddr.sockAddrSize()) != 0 ){ int32_t err = errNo(); newObjectV1C2(err,__PRETTY_FUNCTION__)->throwSP(); } return *this; } //------------------------------------------------------------------------------ AsyncSocket & AsyncSocket::listen() { #if defined(__WIN32__) || defined(__WIN64__) pAcceptExBuffer_.realloc(sizeof(AcceptExBuffer)); #endif if( api.listen(socket_,SOMAXCONN) != 0 ){ int32_t err = errNo(); newObjectV1C2(err,__PRETTY_FUNCTION__)->throwSP(); } return *this; } //------------------------------------------------------------------------------ const SockAddr & AsyncSocket::remoteAddress() const { if( remoteAddress_ == NULL ) #if defined(__WIN32__) || defined(__WIN64__) newObjectV1C2(ERROR_INVALID_DATA + ksys::errorOffset,__PRETTY_FUNCTION__)->throwSP(); #else newObjectV1C2(EINVAL,__PRETTY_FUNCTION__)->throwSP(); #endif return remoteAddress_; } //--------------------------------------------------------------------------- AsyncSocket & AsyncSocket::accept(AsyncSocket & socket) { if( socket.remoteAddress_ == NULL ) socket.remoteAddress_.alloc(sizeof(SockAddr)); #if defined(__WIN32__) || defined(__WIN64__) if( pAcceptExBuffer_ == NULL ) pAcceptExBuffer_.alloc(sizeof(AcceptExBuffer)); assert( socket.socket_ == INVALID_SOCKET ); socket.open(); ksys::Fiber * fiber = ksys::currentFiber(); assert( fiber != NULL ); fiber->event_.timeout_ = ~uint64_t(0); fiber->event_.socket_ = socket.socket_; fiber->event_.type_ = ksys::etAccept; fiber->thread()->postRequest(this); fiber->switchFiber(fiber->mainFiber()); assert( fiber->event_.type_ == ksys::etAccept ); if( fiber->event_.errno_ != 0 ){ socket.close(); newObjectV1C2(fiber->event_.errno_ + ksys::errorOffset,__PRETTY_FUNCTION__)->throwSP(); } LPSOCKADDR plsa, prsa; INT lsaLen, rsaLen; apiEx.GetAcceptExSockaddrs( pAcceptExBuffer_, 0, sizeof(pAcceptExBuffer_->pLocalAddr4_), sizeof(pAcceptExBuffer_->pRemoteAddr4_), &plsa, &lsaLen, &prsa, &rsaLen ); assert( lsaLen == sizeof(pAcceptExBuffer_->localAddress_.addr4_) && rsaLen == sizeof(pAcceptExBuffer_->remoteAddress_.addr4_) ); //memmove(&socket.localAddress_.addr4_,plsa,sizeof(socket.localAddress_.addr4_)); memmove(&socket.remoteAddress_->addr4_,prsa,sizeof(socket.remoteAddress_->addr4_)); #elif HAVE_KQUEUE ksys::Fiber * fiber = ksys::currentFiber(); assert( fiber != NULL ); assert( socket.socket_ == INVALID_SOCKET ); fiber->event_.timeout_ = ~uint64_t(0); fiber->event_.type_ = ksys::etAccept; fiber->thread()->postRequest(this); fiber->switchFiber(fiber->mainFiber()); assert( fiber->event_.type_ == ksys::etAccept ); if( fiber->event_.errno_ != 0 ) newObjectV1C2(fiber->event_.errno_ + ksys::errorOffset,__PRETTY_FUNCTION__)->throwSP(); socket.socket_ = (int) fiber->event_.data_; if( fcntl(socket.socket_,F_SETFL,fcntl(socket.socket_,F_GETFL,0) | O_NONBLOCK) != 0 ){ int32_t err = errno; newObjectV1C2(err,__PRETTY_FUNCTION__)->throwSP(); } int ka = true; socket.setsockopt(SOL_SOCKET,SO_KEEPALIVE,&ka,sizeof(ka)); struct linger lg; lg.l_onoff = 0; lg.l_linger = 0; setsockopt(SOL_SOCKET,SO_LINGER,&lg,sizeof(lg)); int reuse = true; // socklen_t rlen = sizeof(reuse); // getsockopt(SOL_SOCKET,SO_REUSEADDR,&reuse,rlen); setsockopt(SOL_SOCKET,SO_REUSEADDR,&reuse,(socklen_t) sizeof(reuse)); #endif socket.deActivateCompression(); socket.deActivateEncryption(); socket.clearStatistic(); return *this; } //------------------------------------------------------------------------------ #if __BCPLUSPLUS__ #pragma option push -w-8004 #endif //------------------------------------------------------------------------------ AsyncSocket & AsyncSocket::connect(const SockAddr & addr) { open(); ksys::Fiber * fiber = ksys::currentFiber(); assert( fiber != NULL ); fiber->event_.timeout_ = ~uint64_t(0); fiber->event_.position_ = 0; fiber->event_.address_ = addr; fiber->event_.type_ = ksys::etConnect; fiber->thread()->postRequest(this); fiber->switchFiber(fiber->mainFiber()); assert( ksys::currentFiber()->event_.type_ == ksys::etConnect ); if( fiber->event_.errno_ != 0 ) newObjectV1C2(fiber->event_.errno_ + ksys::errorOffset,__PRETTY_FUNCTION__)->throwSP(); deActivateCompression(); deActivateEncryption(); clearStatistic(); return *this; } //------------------------------------------------------------------------------ uint64_t AsyncSocket::sysRecv(void * buf,uint64_t len) { uint64_t r = 0; if( len > maxRecvSize_ ) len = maxRecvSize_; #if HAVE_KQUEUE || __linux__ l1: #endif ksys::Fiber * fiber = ksys::currentFiber(); fiber->event_.timeout_ = recvTimeout_; fiber->event_.buffer_ = buf; fiber->event_.length_ = len; fiber->event_.type_ = ksys::etRead; fiber->thread()->postRequest(this); fiber->switchFiber(fiber->mainFiber()); assert( fiber->event_.type_ == ksys::etRead ); #if defined(__WIN32__) || defined(__WIN64__) if( fiber->event_.errno_ != 0 || fiber->event_.count_ == 0 ){ #elif HAVE_KQUEUE || __linux__ switch( fiber->event_.errno_ ){ case 0 : if( fiber->event_.count_ == 0 ) goto l2; break; case EMSGSIZE : if( len > 0 ){ len >>= 1; goto l1; } default : l2: #endif newObjectV1C2(fiber->event_.errno_ + ksys::errorOffset,__PRETTY_FUNCTION__)->throwSP(); } r = fiber->event_.count_; nrb_ += r; return r; } //------------------------------------------------------------------------------ #if __BCPLUSPLUS__ #pragma option pop #endif //------------------------------------------------------------------------------ uint64_t AsyncSocket::recv(void * buf,uint64_t len) { uint64_t r = 0; if( ksys::LZO1X::active() ){ flush(); while( (r = ksys::LZO1X::read(buf,len)) == 0 ){ // read next compressed packet union { void * vp; uint8_t * ui8p; }; int32_t l, cps; // compressed packet size for( vp = &cps, l = 0; l < (int32_t) sizeof(cps); l += (int32_t) sysRecv(ui8p + l,sizeof(cps) - l) ); if( ksys::SHA256Filter::active() ) decrypt(&cps,sizeof(cps)); if( cps < 0 ){ rBufSize(-cps); for( l = 0; l < -cps; l += (int32_t) sysRecv(rBuf() + l,-cps - l) ); if( ksys::SHA256Filter::active() ) decrypt(rBuf(),-cps); } else if( cps >= 0 ){ ksys::AutoPtr b; b.alloc(cps); for( l = sizeof(int32_t); l < cps; l += (int32_t) sysRecv(b.ptr() + l,cps - l) ); *(int32_t *) b.ptr() = cps; if( ksys::SHA256Filter::active() ) decrypt(b.ptr() + sizeof(cps),cps - sizeof(cps)); decompress(b); } rBufPos(0); } } else if( ksys::SHA256Filter::active() ){ r = sysRecv(buf,len); decrypt(buf,(uintptr_t) r); } else { r = sysRecv(buf,len); } srb_ += r; return r; } //------------------------------------------------------------------------------ AsyncSocket & AsyncSocket::read(void * buf,uint64_t len) { for(;;){ if( ksys::currentFiber()->terminated() ) newObjectV1C2(ECONNABORTED,__PRETTY_FUNCTION__)->throwSP(); if( len <= 0 ) break; uint64_t l = recv(buf,len); buf = (uint8_t *) buf + (size_t) l; len -= l; } return *this; } //------------------------------------------------------------------------------ #if __BCPLUSPLUS__ #pragma option push -w-8004 #endif //------------------------------------------------------------------------------ uint64_t AsyncSocket::sysSend(const void * buf,uint64_t len) { uint64_t w = 0; if( len > maxSendSize_ ) len = maxSendSize_; #if HAVE_KQUEUE || __linux__ l1: #endif ksys::Fiber * fiber = ksys::currentFiber(); fiber->event_.timeout_ = sendTimeout_; fiber->event_.cbuffer_ = buf; fiber->event_.length_ = len; fiber->event_.type_ = ksys::etWrite; fiber->thread()->postRequest(this); fiber->switchFiber(fiber->mainFiber()); assert( fiber->event_.type_ == ksys::etWrite ); #if defined(__WIN32__) || defined(__WIN64__) if( fiber->event_.errno_ != 0 || fiber->event_.count_ == 0 ){ #elif HAVE_KQUEUE || __linux__ switch( fiber->event_.errno_ ){ case 0 : if( fiber->event_.count_ == 0 ) goto l2; break; case EMSGSIZE : if( len > 0 ){ len >>= 1; goto l1; } default : l2: #endif newObjectV1C2(fiber->event_.errno_ + ksys::errorOffset,__PRETTY_FUNCTION__)->throwSP(); } w = fiber->event_.count_; nsb_ += w; return w; } //------------------------------------------------------------------------------ #if __BCPLUSPLUS__ #pragma option pop #endif //------------------------------------------------------------------------------ uint64_t AsyncSocket::send(const void * buf,uint64_t len) { uint64_t w = 0; if( ksys::LZO1X::active() ){ while( (w = ksys::LZO1X::write(buf,len)) == 0 ) flush(); } else if( ksys::SHA256Filter::active() ){ len = len > 1024 * 1024 * 1024 ? 1024 * 1024 * 1024 : len; ksys::AutoPtr p; p.alloc((size_t) len); encrypt(p,buf,(uintptr_t) len); buf = p.ptr(); w = len; while( len > 0 ){ uint64_t l = sysSend(buf,len); buf = (uint8_t *) buf + (size_t) l; len -= l; } } else { w = sysSend(buf,len); } ssb_ += w; return w; } //------------------------------------------------------------------------------ AsyncSocket & AsyncSocket::write(const void * buf,uint64_t len) { for(;;){ if( ksys::currentFiber()->terminated() ) newObjectV1C2(ECONNABORTED,__PRETTY_FUNCTION__)->throwSP(); if( len <= 0 ) break; uint64_t l = send(buf,len); buf = (uint8_t *) buf + (size_t) l; len -= l; } return *this; } //------------------------------------------------------------------------------ AsyncSocket & AsyncSocket::flush() { if( ksys::LZO1X::active() && wBufPos() > 0 ){ ksys::AutoPtr buf; uint8_t * p; int32_t l, ll; compress(buf,p,ll); if( ksys::SHA256Filter::active() ) encrypt(p,ll); for( l = 0; l < ll; l += (uint32_t) sysSend(p + l,ll - l) ); wBufPos(0); } return *this; } //------------------------------------------------------------------------------ utf8::String AsyncSocket::readString() { utf8::String s; *this >> s; return s; } //------------------------------------------------------------------------------ AsyncSocket & AsyncSocket::operator << (const utf8::String & s) { union { uint64_t l; uint8_t b[sizeof(uint64_t)]; }; uint64_t ll; ll = l = s.size(); if( l > (~uint64_t(0) >> 1) ) newObjectV1C2(EINVAL,__PRETTY_FUNCTION__)->throwSP(); if( l > 32767 ){ for( uintptr_t i = sizeof(b); i > sizeof(b) / 2; i-- ) ksys::xchg(b[sizeof(b) - i],((uint8_t *) &l)[i - 1]); b[0] |= uint8_t(0x80); return write(b,sizeof(uint64_t)).write(s.c_str(),ll); } ksys::xchg(b[0],b[1]); return write(&l,sizeof(int16_t)).write(s.c_str(),ll); } //--------------------------------------------------------------------------- AsyncSocket & AsyncSocket::operator >> (utf8::String & s) { union { uint64_t l; uint8_t b[sizeof(uint64_t)]; }; l = 0; read(b,sizeof(int16_t)); if( b[0] & 0x80 ){ b[0] &= ~0x80; read(b + sizeof(int16_t),sizeof(uint64_t) - sizeof(int16_t)); for( uintptr_t i = sizeof(b); i > sizeof(b) / 2; i-- ) ksys::xchg(b[sizeof(b) - i],((uint8_t *) &l)[i - 1]); } else { ksys::xchg(b[0],b[1]); } uintptr_t q = 0; q = ~q; if( l > q ) newObjectV1C2(EINVAL,__PRETTY_FUNCTION__)->throwSP(); utf8::String t; t.resize((size_t) l); read(t.c_str(),l); s = t; return *this; } //--------------------------------------------------------------------------- const uint8_t AsyncSocket::authMagic_[16] = { 0x39, 0x0A, 0x34, 0x9F, 0xED, 0xB9, 0x4F, 0x86, 0xB0, 0xBC, 0x04, 0xC7, 0x19, 0xFC, 0x4A, 0x7E }; //------------------------------------------------------------------------------ enum rad { radRequired, radAllow, radDisabled }; //------------------------------------------------------------------------------ static uint8_t authChannelHelper(const utf8::String & s) { if( s.strcasecmp("required") == 0 ) return radRequired; if( s.strcasecmp("allow") == 0 ) return radAllow; if( s.strcasecmp("disabled") == 0) return radDisabled; if( s.strcasecmp("default") == 0 || s.strlen() == 0 ) return radAllow; newObjectV1C2(EINVAL,__PRETTY_FUNCTION__)->throwSP(); exit(ENOSYS); } //------------------------------------------------------------------------------ static uint8_t authChannelHelper2(const utf8::String & s) { if( s.strcasecmp("LZO1X_1") == 0 ) return ksys::LZO1X::LZO1X_1; if( s.strcasecmp("LZO1X_1_11") == 0 ) return ksys::LZO1X::LZO1X_1_11; if( s.strcasecmp("LZO1X_1_12") == 0 ) return ksys::LZO1X::LZO1X_1_12; if( s.strcasecmp("LZO1X_1_15") == 0 ) return ksys::LZO1X::LZO1X_1_15; // fastest if( s.strcasecmp("LZO1X_999") == 0 ) return ksys::LZO1X::LZO1X_999; // slowest, best compression if( s.strcasecmp("default") == 0 || s.strlen() == 0 ) return ksys::LZO1X::LZO1X_1_15; newObjectV1C2(EINVAL,__PRETTY_FUNCTION__)->throwSP(); exit(ENOSYS); } //------------------------------------------------------------------------------ static uint8_t authChannelHelper3(const utf8::String & s) { if( s.strcasecmp("disabled") == 0 ) return ksys::LZO1X::CRCNone; if( s.strcasecmp("CRC32") == 0 ) return ksys::LZO1X::CRC32; if( s.strcasecmp("ADLER32") == 0 ) return ksys::LZO1X::ADLER32; if( s.strcasecmp("default") == 0 || s.strlen() == 0 ) return ksys::LZO1X::ADLER32; newObjectV1C2(EINVAL,__PRETTY_FUNCTION__)->throwSP(); exit(ENOSYS); } //------------------------------------------------------------------------------ AsyncSocket::AuthParams::~AuthParams() { } //------------------------------------------------------------------------------ AsyncSocket::AuthParams::AuthParams() : maxRecvSize_(~uintptr_t(0)), maxSendSize_(~uintptr_t(0)), recvTimeout_(~uint64_t(0)), sendTimeout_(~uint64_t(0)), threshold_(1024u * 1024u), level_(9), optimize_(true), noAuth_(false) { } //------------------------------------------------------------------------------ AsyncSocket::AuthErrorType AsyncSocket::serverAuth(const AuthParams & ap) { maxRecvSize_ = ap.maxRecvSize_; maxSendSize_ = ap.maxSendSize_; recvTimeout_ = ap.recvTimeout_; sendTimeout_ = ap.sendTimeout_; uint8_t passwordSHA256[32]; uint8_t se = radDisabled, ce = radDisabled; if( !ap.noAuth_ ){ uint8_t authMagic[sizeof(authMagic_)]; read(authMagic,sizeof(authMagic)); if( memcmp(authMagic,authMagic_,sizeof(authMagic_)) != 0 ){ *this << int32_t(aeMagic); return aeMagic; } *this << int32_t(aeOK); utf8::String user(readString()); if( !isValidUser(user) ){ utf8::String::Stream stream; stream << "Invalid user: " << user << ", " << __PRETTY_FUNCTION__ << "\n"; ksys::stdErr.debug(99,stream); *this << int32_t(aeUser); return aeUser; } *this << int32_t(aeOK); utf8::String password(getUserPassword(user)); memset(passwordSHA256,0,sizeof(passwordSHA256)); ksys::SHA256 SHA256; if( password.strncasecmp("sha256:",7) == 0 ){ ksys::base64Decode( utf8::String::Iterator(password) + 7, passwordSHA256, sizeof(passwordSHA256) ); } else { SHA256.make(password.c_str(),password.size()); memcpy(passwordSHA256,SHA256.sha256(),sizeof(passwordSHA256)); } SHA256.make(passwordSHA256,sizeof(passwordSHA256)); uint8_t rpassword2SHA256[32]; read(rpassword2SHA256,sizeof(rpassword2SHA256)); if( memcmp(rpassword2SHA256,SHA256.sha256(),sizeof(rpassword2SHA256)) != 0 ){ utf8::String::Stream stream; stream << "Invalid password: " << password << " for user: " << user << ", " << __PRETTY_FUNCTION__ << "\n"; ksys::stdErr.debug(99,stream); *this << int32_t(aePassword); return aePassword; } *this << int32_t(aeOK); se = authChannelHelper(ap.encryption_); *this >> ce; *this << se; if( se == radRequired && ce == radDisabled ){ *this << int32_t(aeEncryptionServerRequiredButClientDisabled); return aeEncryptionServerRequiredButClientDisabled; } if( se == radDisabled && ce == radRequired ){ *this << int32_t(aeEncryptionServerDisabledButClientRequired); return aeEncryptionServerRequiredButClientDisabled; } *this << int32_t(aeOK); } uint8_t sc, cc, smethod, cmethod, scrc, ccrc, clevel; sc = authChannelHelper(ap.compression_); smethod = authChannelHelper2(ap.compressionType_); scrc = authChannelHelper3(ap.crc_); uint32_t cbs, cthreshold; *this >> cc >> cmethod >> ccrc >> clevel >> cbs >> cthreshold; if( (uintptr_t) clevel < ap.level_ ) ap.level_ = clevel; if( cbs > 0 && cbs < (uint32_t) ap.bufferSize_ ) ap.bufferSize_ = cbs; if( cthreshold < (uint32_t) ap.threshold_ ) ap.threshold_ = cthreshold; *this << sc << smethod << scrc << uint8_t(ap.level_) << uint32_t(ap.bufferSize_) << uint32_t(ap.threshold_); if( sc == radRequired && cc == radDisabled ){ *this << int32_t(aeCompressionServerRequiredButClientDisabled); return aeCompressionServerRequiredButClientDisabled; } if( sc == radDisabled && cc == radRequired ){ *this << int32_t(aeCompressionServerDisabledButClientRequired); return aeCompressionServerDisabledButClientRequired; } *this << int32_t(aeOK); if( (se == radRequired || se == radAllow) && (ce == radRequired || ce == radAllow) ){ this->threshold(ap.threshold_); activateEncryption(passwordSHA256); } if( (sc == radRequired || sc == radAllow) && (cc == radRequired || cc == radAllow) ) activateCompression(smethod,scrc,ap.level_,ap.optimize_,ap.bufferSize_); return aeOK; } //------------------------------------------------------------------------------ AsyncSocket::AuthErrorType AsyncSocket::clientAuth(const AuthParams & ap) { maxRecvSize_ = ap.maxRecvSize_; maxSendSize_ = ap.maxSendSize_; recvTimeout_ = ap.recvTimeout_; sendTimeout_ = ap.sendTimeout_; union { int32_t e; AuthErrorType ae; }; e = ae = aeOK; uint8_t passwordSHA256[32]; uint8_t se = radDisabled, ce = radDisabled; if( !ap.noAuth_ ){ write(authMagic_,sizeof(authMagic_)); *this >> e; if( e != aeOK ) return ae; *this << ap.user_; *this >> e; if( e != aeOK ) return ae; memset(passwordSHA256,0,sizeof(passwordSHA256)); ksys::SHA256 SHA256; if( ap.password_.strncasecmp("sha256:",7) == 0 ){ ksys::base64Decode( utf8::String::Iterator(ap.password_) + 7, passwordSHA256, sizeof(passwordSHA256) ); } else { SHA256.make(ap.password_.c_str(),ap.password_.size()); memcpy(passwordSHA256,SHA256.sha256(),sizeof(passwordSHA256)); } SHA256.make(passwordSHA256,sizeof(passwordSHA256)); write(SHA256.sha256(),sizeof(passwordSHA256)); *this >> e; if( e != aeOK ) return ae; ce = authChannelHelper(ap.encryption_); *this << ce; *this >> se >> e; if( e != aeOK ) return ae; } uint8_t sc, cc, smethod, cmethod, scrc, ccrc, slevel; cc = authChannelHelper(ap.compression_); cmethod = authChannelHelper2(ap.compressionType_); ccrc = authChannelHelper3(ap.crc_); *this << cc << cmethod << ccrc << uint8_t(ap.level_) << uint32_t(ap.bufferSize_) << uint32_t(ap.threshold_); uint32_t sbs, sthreshold; *this >> sc >> smethod >> scrc >> slevel >> sbs >> sthreshold; *this >> e; if( e != aeOK ) return ae; if( (se == radRequired || se == radAllow) && (ce == radRequired || ce == radAllow) ){ this->threshold(sthreshold); activateEncryption(passwordSHA256); } if( (sc == radRequired || sc == radAllow) && (cc == radRequired || cc == radAllow) ) activateCompression(cmethod,scrc,ap.level_,ap.optimize_,ap.bufferSize_); return aeOK; } //------------------------------------------------------------------------------ #if defined(__WIN32__) || defined(__WIN64__) //--------------------------------------------------------------------------- int AsyncSocket::WSAEnumNetworkEvents(WSAEVENT hEventObject,DWORD event) { WSANETWORKEVENTS networkEvents; memset(&networkEvents,0,sizeof(networkEvents)); if( api.WSAEnumNetworkEvents(socket_,hEventObject,&networkEvents) == 0 ){ SetLastError(networkEvents.iErrorCode[event]); return 0; } return SOCKET_ERROR; } //--------------------------------------------------------------------------- BOOL AsyncSocket::AcceptEx( SOCKET sAcceptSocket, PVOID lpOutputBuffer, DWORD dwReceiveDataLength, DWORD dwLocalAddressLength, DWORD dwRemoteAddressLength, LPDWORD lpdwBytesReceived, LPOVERLAPPED lpOverlapped) { if( lpOutputBuffer == NULL ){ lpOutputBuffer = pAcceptExBuffer_; dwReceiveDataLength = 0; dwLocalAddressLength = sizeof(pAcceptExBuffer_->pLocalAddr4_); dwRemoteAddressLength = sizeof(pAcceptExBuffer_->pRemoteAddr4_); } return apiEx.AcceptEx(socket_,sAcceptSocket,lpOutputBuffer,dwReceiveDataLength,dwLocalAddressLength,dwRemoteAddressLength,lpdwBytesReceived,lpOverlapped); } //--------------------------------------------------------------------------- BOOL AsyncSocket::Connect(HANDLE event,ksys::AsyncEvent * request) { if( api.WSAEventSelect(socket_,event,FD_CONNECT) != 0 ) return FALSE; SetLastError(ERROR_SUCCESS); return api.connect( socket_, (struct sockaddr *) &request->address_, request->address_.sockAddrSize() ) != SOCKET_ERROR ? TRUE : FALSE; } //--------------------------------------------------------------------------- BOOL AsyncSocket::Read( LPVOID lpBuffer, DWORD nNumberOfBytesToRead, LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped) { WSABUF buf; buf.buf = (char *) lpBuffer; buf.len = nNumberOfBytesToRead; DWORD Flags = 0; return api.WSARecv(socket_,&buf,1,lpNumberOfBytesRead,&Flags,lpOverlapped,NULL) != SOCKET_ERROR ? TRUE : FALSE; } //--------------------------------------------------------------------------- BOOL AsyncSocket::Write( LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped) { WSABUF buf; buf.buf = (char *) lpBuffer; buf.len = nNumberOfBytesToWrite; return api.WSASend(socket_,&buf,1,lpNumberOfBytesWritten,0,lpOverlapped,NULL) != SOCKET_ERROR ? TRUE : FALSE; } //--------------------------------------------------------------------------- BOOL AsyncSocket::GetOverlappedResult( LPOVERLAPPED lpOverlapped, LPDWORD lpNumberOfBytesTransferred, BOOL bWait, LPDWORD lpdwFlags) { DWORD dwFlags = 0; if( lpdwFlags == NULL ) lpdwFlags = &dwFlags; return api.WSAGetOverlappedResult(socket_,lpOverlapped,lpNumberOfBytesTransferred,bWait,lpdwFlags); } //--------------------------------------------------------------------------- #elif HAVE_KQUEUE || __linux__ //--------------------------------------------------------------------------- int AsyncSocket::accept() { errno = 0; return api.accept(socket_,NULL,0); } //--------------------------------------------------------------------------- void AsyncSocket::connect(ksys::AsyncEvent * request) { errno = 0; api.connect( socket_, (const struct sockaddr *) &request->address_, (socklen_t) request->address_.sockAddrSize() ); } //--------------------------------------------------------------------------- int64_t AsyncSocket::read2(void * buf,uint64_t len) { return api.recv(socket_,(char *) buf,(size_t) len,0); } //--------------------------------------------------------------------------- int64_t AsyncSocket::write2(const void * buf,uint64_t len) { return api.send(socket_,(const char *) buf,(size_t) len,0); } //--------------------------------------------------------------------------- #endif //--------------------------------------------------------------------------- void AsyncSocket::shutdown2() { shutdown(); } //------------------------------------------------------------------------------ void AsyncSocket::flush2() { flush(); } //------------------------------------------------------------------------------ void AsyncSocket::close2() { close(); } //------------------------------------------------------------------------------ void AsyncSocket::openAPI() { api.open(); } //------------------------------------------------------------------------------ void AsyncSocket::closeAPI() { api.close(); } //------------------------------------------------------------------------------ AsyncSocket & AsyncSocket::getSockAddr(SockAddr & addr) const { int32_t err = -1; api.open(); socklen_t len; #if HAVE_STRUCT_SOCKADDR_IN6 len = sizeof(addr.addr6_); err = api.getsockname(socket_,(sockaddr *) &addr.addr6_,&len); #endif if( err != 0 ){ len = sizeof(addr.addr4_); err = api.getsockname(socket_,(sockaddr *) &addr.addr4_,&len); } if( err != 0 ) err = errNo(); api.close(); if( err != 0 ) newObjectV1C2(err,__PRETTY_FUNCTION__)->throwSP(); return *const_cast(this); } //------------------------------------------------------------------------------ AsyncSocket & AsyncSocket::getPeerAddr(SockAddr & addr) const { int32_t err = -1; api.open(); socklen_t len; #if HAVE_STRUCT_SOCKADDR_IN6 len = sizeof(addr.addr6_); err = api.getpeername(socket_,(sockaddr *) &addr.addr6_,&len); #endif if( err != 0 ){ len = sizeof(addr.addr4_); err = api.getpeername(socket_,(sockaddr *) &addr.addr4_,&len); } if( err != 0 ) err = errNo(); api.close(); if( err != 0 ) newObjectV1C2(err,__PRETTY_FUNCTION__)->throwSP(); return *const_cast(this); } //------------------------------------------------------------------------------ } // namespace ksock //------------------------------------------------------------------------------