/* ** 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 "udp.h" #include "wlib/wdebug.h" UDP::UDP() { fd=0; } UDP::~UDP() { } sint32 UDP::Bind(char *Host,uint16 port) { char hostName[100]; struct hostent *hostStruct; struct in_addr *hostNode; if (isdigit(Host[0])) return ( Bind( ntohl(inet_addr(Host)), port) ); strcpy(hostName, Host); hostStruct = gethostbyname(Host); if (hostStruct == NULL) return (0); hostNode = (struct in_addr *) hostStruct->h_addr; return ( Bind(ntohl(hostNode->s_addr),port) ); } // You must call bind, implicit binding is for sissies // Well... you can get implicit binding if you pass 0 for either arg sint32 UDP::Bind(uint32 IP,uint16 Port) { int retval; int status; IP=htonl(IP); Port=htons(Port); addr.sin_family=AF_INET; addr.sin_port=Port; addr.sin_addr.s_addr=IP; fd=socket(AF_INET,SOCK_DGRAM,DEFAULT_PROTOCOL); #ifdef _WINDOWS if (fd==SOCKET_ERROR) fd=-1; #endif if (fd==-1) return(UNKNOWN); retval=bind(fd,(struct sockaddr *)&addr,sizeof(addr)); #ifdef _WINDOWS if (retval==SOCKET_ERROR) retval=-1; #endif if (retval==-1) { status=GetStatus(); //CERR("Bind failure (" << status << ") IP " << IP << " PORT " << Port ) return(status); } int namelen=sizeof(addr); getsockname(fd, (struct sockaddr *)&addr, &namelen); myIP=ntohl(addr.sin_addr.s_addr); myPort=ntohs(addr.sin_port); retval=SetBlocking(FALSE); if (retval==-1) fprintf(stderr,"Couldn't set nonblocking mode!\n"); return(OK); } bit8 UDP::getLocalAddr(uint32 &ip, uint16 &port) { ip=myIP; port=myPort; return(OK); } // private function sint32 UDP::SetBlocking(bit8 block) { #ifdef _WINDOWS unsigned long flag=1; if (block) flag=0; int retval; retval=ioctlsocket(fd,FIONBIO,&flag); if (retval==SOCKET_ERROR) return(UNKNOWN); else return(OK); #else // UNIX int flags = fcntl(fd, F_GETFL, 0); if (block==FALSE) // set nonblocking flags |= O_NONBLOCK; else // set blocking flags &= ~(O_NONBLOCK); if (fcntl(fd, F_SETFL, flags) < 0) { return(UNKNOWN); } return(OK); #endif } sint32 UDP::Write(uint8 *msg,uint32 len,uint32 IP,uint16 port) { sint32 retval; struct sockaddr_in to; // This happens frequently if ((IP==0)||(port==0)) return(ADDRNOTAVAIL); errno=0; to.sin_port=htons(port); to.sin_addr.s_addr=htonl(IP); to.sin_family=AF_INET; ClearStatus(); retval=sendto(fd,(char *)msg,len,0,(struct sockaddr *)&to,sizeof(to)); #ifdef _WINDOWS if (retval==SOCKET_ERROR) retval=-1; #endif return(retval); } sint32 UDP::Read(uint8 *msg,uint32 len,sockaddr_in *from) { sint32 retval; int alen=sizeof(sockaddr_in); if (from!=NULL) { retval=recvfrom(fd,(char *)msg,len,0,(struct sockaddr *)from,&alen); #ifdef _WINDOWS if (retval==SOCKET_ERROR) retval=-1; #endif } else { retval=recvfrom(fd,(char *)msg,len,0,NULL,NULL); #ifdef _WINDOWS if (retval==SOCKET_ERROR) retval=-1; #endif } return(retval); } void UDP::ClearStatus(void) { #ifndef _WINDOWS errno=0; #endif } UDP::sockStat UDP::GetStatus(void) { #ifdef _WINDOWS int status=WSAGetLastError(); if (status==0) return(OK); else if (status==WSAEINTR) return(INTR); else if (status==WSAEINPROGRESS) return(INPROGRESS); else if (status==WSAECONNREFUSED) return(CONNREFUSED); else if (status==WSAEINVAL) return(INVAL); else if (status==WSAEISCONN) return(ISCONN); else if (status==WSAENOTSOCK) return(NOTSOCK); else if (status==WSAETIMEDOUT) return(TIMEDOUT); else if (status==WSAEALREADY) return(ALREADY); else if (status==WSAEWOULDBLOCK) return(WOULDBLOCK); else if (status==WSAEBADF) return(BADF); else return(UNKNOWN); #else int status=errno; if (status==0) return(OK); else if (status==EINTR) return(INTR); else if (status==EINPROGRESS) return(INPROGRESS); else if (status==ECONNREFUSED) return(CONNREFUSED); else if (status==EINVAL) return(INVAL); else if (status==EISCONN) return(ISCONN); else if (status==ENOTSOCK) return(NOTSOCK); else if (status==ETIMEDOUT) return(TIMEDOUT); else if (status==EALREADY) return(ALREADY); else if (status==EAGAIN) return(AGAIN); else if (status==EWOULDBLOCK) return(WOULDBLOCK); else if (status==EBADF) return(BADF); else return(UNKNOWN); #endif } // // Wait for net activity on this socket // int UDP::Wait(sint32 sec,sint32 usec,fd_set &returnSet) { fd_set inputSet; FD_ZERO(&inputSet); FD_SET(fd,&inputSet); return(Wait(sec,usec,inputSet,returnSet)); } // // Wait for net activity on a list of sockets // int UDP::Wait(sint32 sec,sint32 usec,fd_set &givenSet,fd_set &returnSet) { Wtime timeout,timenow,timethen; fd_set backupSet; int retval=0,done,givenMax; bit8 noTimeout=FALSE; timeval tv; returnSet=givenSet; backupSet=returnSet; if ((sec==-1)&&(usec==-1)) noTimeout=TRUE; timeout.SetSec(sec); timeout.SetUsec(usec); timethen+=timeout; givenMax=fd; for (uint32 i=0; i<(sizeof(fd_set)*8); i++) // i=maxFD+1 { if (FD_ISSET(i,&givenSet)) givenMax=i; } ///DBGMSG("WAIT fd="<