/*** Copyright (C) 2022 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: AuNetSocket.Unix.cpp Date: 2022-8-26 Author: Reece ***/ #include "Networking.hpp" #include "AuNetSocket.hpp" #include "AuNetEndpoint.hpp" #include "AuNetWorker.hpp" #include "AuIPAddress.hpp" #include "AuNetError.hpp" #include #if !defined(SOCK_CLOEXEC) #define SOCK_CLOEXEC 0 #endif namespace Aurora::IO::Net { static NetError GetLastNetError() { NetError error; NetError_SetCurrent(error); return error; } Socket::Socket(struct NetInterface *pInterface, struct NetWorker *pWorker, const AuSPtr &pSocketDriver, AuUInt osHandle, AuSPtr pParent, SocketServer *pParent2) : SocketBase(pInterface, pWorker, pSocketDriver, osHandle, pParent, pParent2) { } Socket::Socket(struct NetInterface *pInterface, struct NetWorker *pWorker, const AuSPtr &pSocketDriver, const AuPair &endpoint, AuNet::ETransportProtocol eProtocol) : SocketBase(pInterface, pWorker, pSocketDriver, endpoint, eProtocol) { } Socket::Socket(struct NetInterface *pInterface, struct NetWorker *pWorker, const AuSPtr &pSocketDriver, const NetEndpoint &endpoint) : SocketBase(pInterface, pWorker, pSocketDriver, endpoint) { } Socket::Socket(NetInterface *pInterface, NetWorker *pWorker, const AuSPtr &pSocketDriver, const NetSocketConnectMany &connectMany) : SocketBase(pInterface, pWorker, pSocketDriver, connectMany) { } Socket::~Socket() { CloseSocket(); } void Socket::CloseSocket() { this->osHandle_ = 0; AuResetMember(this->osHandleOwner_); } void Socket::RenewSocket() { if (!this->SendPreestablish()) { SysPushErrorIO("Preestablish drop"); return; } if (this->bHasRemoteMany_ && this->connectMany_.names.size()) { if (this->connectMany_.names[0].byEndpoint) { this->remoteEndpoint_ = this->connectMany_.names[0].byEndpoint.value(); } else { auto val = this->connectMany_.names[0].byHost.value(); this->remoteEndpoint_.ip = val.netHostname.address; this->remoteEndpoint_.transportProtocol = val.protocol; } } this->CloseSocket(); this->osHandle_ = ::socket( IPToDomain(this->remoteEndpoint_), TransportToPlatformType(this->remoteEndpoint_) | SOCK_CLOEXEC, 0 ); bool bMakeCloseExec { !bool(SOCK_CLOEXEC) }; if (this->osHandle_ == -1) { this->osHandle_ = ::socket( IPToDomain(this->remoteEndpoint_), TransportToPlatformType(this->remoteEndpoint_), 0 ); bMakeCloseExec = true; } if (this->osHandle_ == -1) { this->SendErrorNoStream(GetLastNetError()); return; } if (bMakeCloseExec) { (void)this->MakeCloseonexec(); } this->osHandleOwner_ = AuIO::IOHandleShared(); this->osHandleOwner_->InitFromPairMove((int)this->osHandle_, (int)this->osHandle_); if (!this->PrepareConnectOperations()) { this->bForceFailConstruct_ = true; return; } } void Socket::FinishConstructAsync() { if (this->resolveLater.size() || this->bResolving_) { if (!this->TryStartResolve()) { this->SendErrorNoStream(GetLastNetError()); return; } return; } RenewSocket(); } bool Socket::PrepareConnectOperations() { return this->MakeNonblocking(); } void Socket::UpdateNagleAnyThread(bool bDisableNagle) { if (this->remoteEndpoint_.transportProtocol != ETransportProtocol::eProtocolTCP) { return; } int flag = bDisableNagle ? 1 : 0; int result = setsockopt((int)this->osHandle_, /* socket affected */ IPPROTO_TCP, /* set option at TCP level */ TCP_NODELAY, /* name of option */ (char *) &flag, /* the cast is historical cruft */ sizeof(int)); } bool Socket::UpdateLocalEndpoint() { socklen_t iLen { (int)this->endpointSize_ }; if (::getsockname(this->osHandle_, (sockaddr *)this->localEndpoint_.hint, &iLen) == -1) { SysPushErrorIO(); return false; } DeoptimizeEndpoint(this->localEndpoint_); return true; } bool Socket::UpdateRemoteEndpoint() { socklen_t iLen { (int)this->endpointSize_ }; if (::getpeername(this->osHandle_, (sockaddr *)this->remoteEndpoint_.hint, &iLen) == -1) { SysPushErrorIO(); return false; } DeoptimizeEndpoint(this->remoteEndpoint_); return true; } bool Socket::TryBindAnyLocal() { struct sockaddr_in addr {}; addr.sin_family = EIPProtocolIsValid(this->remoteEndpoint_.ip.ip) ? IPToDomain(this->remoteEndpoint_) : AF_UNSPEC; addr.sin_addr.s_addr = INADDR_ANY; addr.sin_port = 0; return ::bind(this->osHandle_, (sockaddr *)&addr, sizeof(addr)) == 0; } bool Socket::ConnectOverlapped() { return false; } bool Socket::ConnectNonblocking() { bool bStatus = ::connect(this->osHandle_, AuReinterpretCast(this->remoteEndpoint_.hint), this->endpointSize_) == 0; auto pWaitHandle = AuMakeShared(-1, this->osHandle_); if (!pWaitHandle) { return false; } this->connectOperation.UpdateTrigger(pWaitHandle); return this->connectOperation.FinishOperation(AuSharedFromThis(), AuSPtr(AuSharedFromThis(), this->ToWorker()), bStatus); } bool Socket::ConnectBlocking() { return false; } bool Socket::MakeNonblocking() { int flags = ::fcntl(this->osHandle_, F_GETFL, 0); if (flags == -1) { NetError error; NetError_SetCurrent(error); this->SendErrorNoStream(error); return false; } flags |= O_NONBLOCK; return ::fcntl(this->osHandle_, F_SETFL, flags) == 0; } bool Socket::MakeCloseonexec() { int flags = ::fcntl(this->osHandle_, F_GETFL, 0); if (flags == -1) { return false; } flags |= FD_CLOEXEC; return ::fcntl(this->osHandle_, F_SETFL, flags) == 0; } void Socket::Shutdown(bool bNow) { if (bNow) { this->SendEnd(); ::shutdown(this->osHandle_, SHUT_RDWR); } else { if (!this->socketChannel_.outputChannel.AsWritableByteBuffer()->RemainingBytes()) { this->Shutdown(true); } else { this->socketChannel_.outputChannel.bShutdownOnComplete = true; this->socketChannel_.ScheduleOutOfFrameWrite(); } } } }