/*** 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 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) : SocketBase(pInterface, pWorker, pSocketDriver, osHandle) { } 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() { if (this->osHandle_ && this->osHandle_ != -1) { ::close(this->osHandle_); this->osHandle_ = 0; } } void Socket::FinishConstructAsync() { if (!this->SendPreestablish()) { SysPushErrorIO("Preestablish drop"); return; } this->osHandle_ = ::socket( IPToDomain(this->remoteEndpoint_), TransportToPlatformType(this->remoteEndpoint_), 0 ); if (this->osHandle_ == -1) { this->SendErrorNoStream(GetLastNetError()); return; } if (!this->PrepareConnectOperations()) { this->bForceFailConstruct_ = true; return; } this->osHandleOwner_->Init((int)this->osHandle_, (int)this->osHandle_); } bool Socket::PrepareConnectOperations() { (void)this->MakeCloseonexec(); 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(); } } } }