This repository has been archived on 2025-02-27. You can view files and clone it, but cannot push or open issues or pull requests.
CnC_Renegade/Code/Tools/wnet/udp.cpp

373 lines
7.8 KiB
C++
Raw Permalink Normal View History

/*
** 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 <http://www.gnu.org/licenses/>.
*/
#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="<<fd<<" givenMax="<<givenMax);
done=0;
while( ! done)
{
if (noTimeout)
retval=select(givenMax+1,&returnSet,0,0,NULL);
else
{
timeout.GetTimevalMT(tv);
retval=select(givenMax+1,&returnSet,0,0,&tv);
}
if (retval>=0)
done=1;
else if ((retval==-1)&&(errno==EINTR)) // in case of signal
{
if (noTimeout==FALSE)
{
timenow.Update();
timeout=timethen-timenow;
}
if ((noTimeout==FALSE)&&(timenow.GetSec()==0)&&(timenow.GetUsec()==0))
done=1;
else
returnSet=backupSet;
}
else // maybe out of memory?
{
done=1;
}
}
///DBGMSG("Wait retval: "<<retval);
return(retval);
}
// Set the kernel buffer sizes for incoming, and outgoing packets
//
// Linux seems to have a buffer max of 32767 bytes for this,
// (which is the default). If you try and set the size to
// greater than the default it just sets it to 32767.
bit8 UDP::SetInputBuffer(uint32 bytes)
{
#ifndef _WINDOWS
int retval,arg=bytes;
retval=setsockopt(fd,SOL_SOCKET,SO_RCVBUF,
(char *)&arg,sizeof(int));
if (retval==0)
return(TRUE);
else
return(FALSE);
#else
return(FALSE);
#endif
}
// Same note goes for the output buffer
bit8 UDP::SetOutputBuffer(uint32 bytes)
{
#ifndef _WINDOWS
int retval,arg=bytes;
retval=setsockopt(fd,SOL_SOCKET,SO_SNDBUF,
(char *)&arg,sizeof(int));
if (retval==0)
return(TRUE);
else
return(FALSE);
#else
return(FALSE);
#endif
}
// Get the system buffer sizes
int UDP::GetInputBuffer(void)
{
#ifndef _WINDOWS
int retval,arg=0,len=sizeof(int);
retval=getsockopt(fd,SOL_SOCKET,SO_RCVBUF,
(char *)&arg,&len);
return(arg);
#else
return(0);
#endif
}
int UDP::GetOutputBuffer(void)
{
#ifndef _WINDOWS
int retval,arg=0,len=sizeof(int);
retval=getsockopt(fd,SOL_SOCKET,SO_SNDBUF,
(char *)&arg,&len);
return(arg);
#else
return(0);
#endif
}