/*** Copyright (C) 2022 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: AuNetSocket.NT.cpp Date: 2022-8-16 Author: Reece ***/ #include "Networking.hpp" #include "AuNetSocket.hpp" #include "AuNetEndpoint.hpp" #include "AuNetWorker.hpp" #include "AuIPAddress.hpp" #include "AuNetError.hpp" 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) { ::closesocket(this->osHandle_); this->osHandle_ = 0; } } void Socket::FinishConstructAsync() { if (!this->SendPreestablish()) { SysPushErrorIO("Preestablish drop"); return; } this->osHandle_ = ::WSASocketW( IPToDomain(this->remoteEndpoint_), TransportToPlatformType(this->remoteEndpoint_), IPPROTO_IP, nullptr, 0, WSA_FLAG_OVERLAPPED ); if (this->osHandle_ == -1) { this->SendErrorNoStream(GetLastNetError()); return; } if (!this->PrepareConnectOperations()) { this->bForceFailConstruct_ = true; return; } #if !defined(AURORA_IS_MODERNNT_DERIVED) this->osHandleOwner_->Init((int)this->osHandle_, (int)this->osHandle_); #endif } bool Socket::PrepareConnectOperations() { ::setsockopt(this->osHandle_, SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT, nullptr, 0); return true; } void Socket::UpdateNagleAnyThread(bool bDisableNagle) { } bool Socket::UpdateLocalEndpoint() { int 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() { int 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() { DWORD dwNumBytes {}; GUID guid = WSAID_CONNECTEX; LPFN_CONNECTEX lpConnectEx {}; if (::WSAIoctl(this->osHandle_, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid, sizeof(guid), &lpConnectEx, sizeof(lpConnectEx), &dwNumBytes, nullptr, nullptr) != 0) { SysPushErrorNet("Couldn't yoink extension function pointer"); return false; } if (!TryBindAnyLocal()) { SysPushErrorNet("Couldn't bind locally"); return false; } bool bStatus = lpConnectEx(this->osHandle_, AuReinterpretCast(this->remoteEndpoint_.hint), this->endpointSize_, nullptr, 0, nullptr, &this->connectOperation.overlapped); return this->connectOperation.FinishOperation(AuSharedFromThis(), AuSPtr(AuSharedFromThis(), this->ToWorker()), bStatus); } bool Socket::ConnectNonblocking() { return false; } bool Socket::ConnectBlocking() { return false; } bool Socket::MakeNonblocking() { u_long iMode { 1 }; return ::ioctlsocket(this->osHandle_, FIONBIO, &iMode) == 0; } void Socket::Shutdown(bool bNow) { if (bNow) { this->SendEnd(); ::shutdown(this->osHandle_, SD_BOTH); } else { if (!this->socketChannel_.outputChannel.AsWritableByteBuffer()->RemainingBytes()) { this->Shutdown(true); } else { this->socketChannel_.outputChannel.bShutdownOnComplete = true; this->socketChannel_.ScheduleOutOfFrameWrite(); } } } }