/* ** Command & Conquer Renegade(tm) ** Copyright 2025 Electronic Arts Inc. ** ** This program is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . */ #include "tcpcon.h" #include #define DEFAULT_TCP_DELAY 15 TCPCon::TCPCon(SOCKET sock) : BufferedWrites_(false), TCPMgrPtr_(NULL) { Socket_=sock; isConnected(); // This will set the internal connect status setInputDelay(DEFAULT_TCP_DELAY); setOutputDelay(DEFAULT_TCP_DELAY); } TCPCon::~TCPCon() { close(); // will try and flush the write queue ReadQueue_.clear(); WriteQueue_.clear(); if ((BufferedWrites_) && (TCPMgrPtr_)) TCPMgrPtr_->setBufferedWrites(this, FALSE); } SOCKET TCPCon::getFD(void) { return(Socket_); } void TCPCon::close(void) { if (BufferedWrites_) { while(WriteQueue_.length()) pumpWrites(); } ::closesocket(Socket_); State_=TCPMgr::CLOSED; } // // Write data // // Returns 'n' bytes written, 0 if closed, or -1 for error. // sint32 TCPCon::write(IN uint8 *msg,uint32 len, sint32 wait_secs) { if (State_==TCPMgr::CLOSED) return(0); if (BufferedWrites_) { WriteQueue_.addMany(msg, WriteQueue_.length(), len); // add to tail pumpWrites(); // send what I can return(len); } else { return (normalWrite(msg, len, wait_secs)); } } // // set buffered status // void TCPCon::setBufferedWrites(TCPMgr *mgrptr, bit8 enabled) { if (enabled) BufferedWrites_=TRUE; else { while(WriteQueue_.length()) pumpWrites(); BufferedWrites_=FALSE; } TCPMgrPtr_=mgrptr; } // // Try and send queued data (PRIVATE) // void TCPCon::pumpWrites(void) { if (State_==TCPMgr::CLOSED) WriteQueue_.clear(); if (WriteQueue_.length()==0) return; int sendlen; int retval; uint8 *bufptr=NULL; while(1) { sendlen=WriteQueue_.length(); if (sendlen == 0) break; WriteQueue_.getPointer(&bufptr,0); // pointer to first byte retval=normalWrite((uint8 *)bufptr, sendlen, 0); if (retval <= 0) break; WriteQueue_.removeMany(NULL, 0, retval); // successful send } return; } // // Non-buffered write (PRIVATE METHOD) // // Returns 'n' bytes written, 0 if closed, or -1 for error. // sint32 TCPCon::normalWrite(IN uint8 *msg,uint32 len, sint32 wait_secs) { if (State_==TCPMgr::CLOSED) return(0); if (wait_secs < 0) wait_secs=OutputDelay_; sint32 retval=0; sint32 sendCount=0; time_t start=time(NULL); TCPMgr::STATUS status; while(1) { retval=send(Socket_,(const char *)(msg+sendCount),(len-sendCount),0); if ((retval > 0) && (retval+sendCount)==int(len)) break; if (retval==SOCKET_ERROR) { status=TCPMgr::getStatus(); if ((status != TCPMgr::INTR) && (status != TCPMgr::WOULDBLOCK) && (status != TCPMgr::INPROGRESS)) { if (sendCount) return(sendCount); else return(-1); } } else if (retval > 0) sendCount+=retval; sint32 remaining_wait=wait_secs - (time(NULL)-start); if ((remaining_wait > 0) && (TCPMgr::wait(remaining_wait,0,&Socket_,1,FALSE) > 0)) continue; // I can write now.... if (remaining_wait <= 0) break; } return(retval); } // // Read data // // Returns 'n' bytes read, 0 for close, or -1 for error. // This may return less than we asked for // sint32 TCPCon::read(OUT uint8 *msg,uint32 maxlen, sint32 wait_secs) { sint32 retval=0; sint32 recvCount=0; time_t start=time(NULL); char readBuffer[257]; if (wait_secs < 0) wait_secs=InputDelay_; if (State_==TCPMgr::CLOSED) return(0); TCPMgr::STATUS status; while(1) { // Do we even nead to read from the net? if (ReadQueue_.length() >= int(maxlen)) { ReadQueue_.removeMany(msg, 0, maxlen); return(maxlen); } do { retval=recv(Socket_,readBuffer, 256,0); if (retval > 0) // add to the tail of the list { readBuffer[retval]=0; DBGMSG("RECV: "< 0)&&(ReadQueue_.length() < int(maxlen))); if (ReadQueue_.length()) // OK, we'll take what we've got { uint8 *cptr; ReadQueue_.getPointer(&cptr,0); /******* fprintf(stderr,"ReadQueue(%d): '",ReadQueue_.length()); for (int i=0; i 0) && (TCPMgr::wait(remaining_wait,0,&Socket_,1,TRUE) > 0)) continue; // I can read now.... if (remaining_wait <= 0) break; } return(retval); } // Push data back onto the read queue bit8 TCPCon::unread(uint8 *data, int length) { ReadQueue_.addMany(data, 0, length); return(TRUE); } // Returns 0 on failure // Returns IP in host byte order! bit8 TCPCon::getRemoteAddr(uint32 *ip, uint16 *port) { struct sockaddr_in sin; int sinSize=sizeof(sin); if(getpeername(Socket_,(sockaddr *)&sin,&sinSize)==0) { if (ip) *ip=ntohl(sin.sin_addr.s_addr); if (port) *port=ntohs(sin.sin_port); return(TRUE); } return(FALSE); } // // only use for strings up to 4096 chars! // sint32 TCPCon::printf(const char *format, ...) { va_list arg; char string[4097]; sint32 retval; va_start(arg,format); vsprintf(string,format,arg); va_end(arg); string[4096]=0; retval=write((IN uint8 *)string,strlen(string), OutputDelay_); return(retval); } bit8 TCPCon::isConnected(void) { uint32 remoteIp; uint16 remotePort; if (getRemoteAddr(&remoteIp,&remotePort)==TRUE) { State_=TCPMgr::CONNECTED; return(TRUE); } else { State_=TCPMgr::CLOSED; return(FALSE); } } /********* // For the OutputDevice interface int TCPCon::print(IN char *str, int len) { return(write((IN uint8 *)str,len,0)); } ********/