quakeforge/libs/net/nm/net_udp.c
Bill Currie c4feb0e0d6 attempt to get the external ip address from the system rather than relying
on hostname as hostname might resolve to 127.0.0.1, which isn't good for
trying to play networked games. if the attempt fails, fall back to the
hostname method
2005-04-27 03:34:38 +00:00

533 lines
11 KiB
C

/*
net_udp.c
@description@
Copyright (C) 1996-1997 Id Software, 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 2
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, write to:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
static __attribute__ ((unused)) const char rcsid[] =
"$Id$";
#include <sys/types.h>
#ifdef HAVE_STRING_H
# include <string.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif
#ifdef HAVE_NETINET_IN_H
# define model_t sun_model_t
# include <netinet/in.h>
# undef model_t
#endif
#ifdef HAVE_ARPA_INET_H
# include <arpa/inet.h>
#endif
#ifdef HAVE_NETDB_H
# include <netdb.h>
#endif
#ifdef HAVE_SYS_PARAM_H
# include <sys/param.h>
#endif
#ifdef HAVE_SYS_IOCTL_H
# include <sys/ioctl.h>
#endif
#ifdef HAVE_WINDOWS_H
# include <windows.h>
#endif
#ifdef HAVE_WINSOCK_H
# include <winsock.h>
#endif
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#ifdef __sun__
# include <sys/filio.h>
#endif
#ifdef NeXT
# include <libc.h>
#endif
#include <errno.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include "QF/console.h"
#include "QF/cvar.h"
#include "QF/dstring.h"
#include "QF/qargs.h"
#include "QF/sys.h"
#include "QF/va.h"
#include "compat.h"
#include "netmain.h"
#ifdef _WIN32
# undef EWOULDBLOCK
# define EWOULDBLOCK WSAEWOULDBLOCK
# undef ECONNREFUSED
# define ECONNREFUSED WSAECONNREFUSED
#endif
#ifndef MAXHOSTNAMELEN
# define MAXHOSTNAMELEN 512
#endif
#ifndef HAVE_SOCKLEN_T
# ifdef HAVE_SIZE
typedef size_t socklen_t;
# else
typedef unsigned int socklen_t;
# endif
#endif
static int net_acceptsocket = -1; // socket for fielding new
// connections
static int net_controlsocket;
static int net_broadcastsocket = 0;
static struct qsockaddr broadcastaddr;
static unsigned long myAddr;
#include "net_udp.h"
static int
get_address (int sock)
{
struct ifconf ifc;
struct ifreq *ifr;
char buf[8192];
int i, n;
struct sockaddr_in *in_addr;
unsigned long addr;
ifc.ifc_len = sizeof (buf);
ifc.ifc_buf = buf;
if (ioctl (sock, SIOCGIFCONF, &ifc) == -1)
return 0;
ifr = ifc.ifc_req;
n = ifc.ifc_len / sizeof (struct ifreq);
for (i = 0; i < n; i++) {
if (ioctl (sock, SIOCGIFADDR, &ifr[i]) == -1)
continue;
in_addr = (struct sockaddr_in *)&ifr[i].ifr_addr;
Con_DPrintf ("%s: %s\n", ifr[i].ifr_name,
inet_ntoa (in_addr->sin_addr));
addr = *(unsigned long *)&in_addr->sin_addr;
if (addr != htonl (0x7f000001)) {
myAddr = addr;
break;
}
}
return 1;
}
int
UDP_Init (void)
{
struct hostent *local;
char buff[MAXHOSTNAMELEN];
struct qsockaddr addr;
char *colon;
#ifdef _WIN32
WSADATA winsockdata;
int r;
r = WSAStartup (MAKEWORD (1, 1), &winsockdata);
if (r)
Sys_Error ("Winsock initialization failed.");
#endif
if (COM_CheckParm ("-noudp"))
return -1;
// determine my name & address
gethostname (buff, MAXHOSTNAMELEN);
local = gethostbyname (buff);
if (local)
myAddr = *(int *) local->h_addr_list[0];
else
myAddr = 0;
// if the quake hostname isn't set, set it to the machine name
if (strcmp (hostname->string, "UNNAMED") == 0) {
buff[15] = 0;
Cvar_Set (hostname, buff);
}
if ((net_controlsocket = UDP_OpenSocket (0)) == -1)
Sys_Error ("UDP_Init: Unable to open control socket");
get_address (net_controlsocket);
((struct sockaddr_in *) &broadcastaddr)->sin_family = AF_INET;
((struct sockaddr_in *) &broadcastaddr)->sin_addr.s_addr =
INADDR_BROADCAST;
((struct sockaddr_in *) &broadcastaddr)->sin_port = htons (net_hostport);
UDP_GetSocketAddr (net_controlsocket, &addr);
strcpy (my_tcpip_address, UDP_AddrToString (&addr));
colon = strrchr (my_tcpip_address, ':');
if (colon)
*colon = 0;
Con_Printf ("UDP (IPv4) Initialized\n");
tcpipAvailable = true;
return net_controlsocket;
}
void
UDP_Shutdown (void)
{
UDP_Listen (false);
UDP_CloseSocket (net_controlsocket);
}
void
UDP_Listen (qboolean state)
{
// enable listening
if (state) {
if (net_acceptsocket != -1)
return;
if ((net_acceptsocket = UDP_OpenSocket (net_hostport)) == -1)
Sys_Error ("UDP_Listen: Unable to open accept socket");
return;
}
// disable listening
if (net_acceptsocket == -1)
return;
UDP_CloseSocket (net_acceptsocket);
net_acceptsocket = -1;
}
int
UDP_OpenSocket (int port)
{
int newsocket;
struct sockaddr_in address;
#ifdef _WIN32
#define ioctl ioctlsocket
unsigned long _true = true;
#else
int _true = true;
#endif
if ((newsocket = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
return -1;
if (ioctl (newsocket, FIONBIO, &_true) == -1)
goto ErrorReturn;
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons (port);
if (bind (newsocket, (void *) &address, sizeof (address)) == -1)
goto ErrorReturn;
return newsocket;
ErrorReturn:
UDP_CloseSocket (newsocket);
return -1;
}
int
UDP_CloseSocket (int socket)
{
if (socket == net_broadcastsocket)
net_broadcastsocket = 0;
#ifdef _WIN32
closesocket (socket);
WSACleanup ();
return 0;
#else
return close (socket);
#endif
}
/*
PartialIPAddress
this lets you type only as much of the net address as required, using
the local network components to fill in the rest
*/
static int
PartialIPAddress (const char *in, struct qsockaddr *hostaddr)
{
char *buff;
char *b;
int addr, mask, num, port, run;
buff = nva (".%s", in);
b = buff;
if (buff[1] == '.')
b++;
addr = 0;
mask = -1;
while (*b == '.') {
b++;
num = 0;
run = 0;
while (!(*b < '0' || *b > '9')) {
num = num * 10 + *b++ - '0';
if (++run > 3)
goto error;
}
if ((*b < '0' || *b > '9') && *b != '.' && *b != ':' && *b != 0)
goto error;
if (num < 0 || num > 255)
goto error;
mask <<= 8;
addr = (addr << 8) + num;
}
if (*b++ == ':')
port = atoi (b);
else
port = net_hostport;
hostaddr->qsa_family = AF_INET;
((struct sockaddr_in *) hostaddr)->sin_port = htons ((short) port);
((struct sockaddr_in *) hostaddr)->sin_addr.s_addr =
(myAddr & htonl (mask)) | htonl (addr);
free (buff);
return 0;
error:
free (buff);
return -1;
}
int
UDP_Connect (int socket, struct qsockaddr *addr)
{
return 0;
}
int
UDP_CheckNewConnections (void)
{
unsigned long available;
struct sockaddr_in from;
socklen_t fromlen;
char buff[1];
if (net_acceptsocket == -1)
return -1;
if (ioctl (net_acceptsocket, FIONREAD, &available) == -1)
Sys_Error ("UDP: ioctlsocket (FIONREAD) failed");
if (available)
return net_acceptsocket;
// quietly absorb empty packets
recvfrom (net_acceptsocket, buff, 0, 0, (struct sockaddr *) &from,
&fromlen);
return -1;
}
int
UDP_Read (int socket, byte * buf, int len, struct qsockaddr *addr)
{
int addrlen = sizeof (struct qsockaddr);
int ret;
ret = recvfrom (socket, buf, len, 0, (struct sockaddr *) addr, &addrlen);
if (ret == -1 && (errno == EWOULDBLOCK || errno == ECONNREFUSED))
return 0;
return ret;
}
static int
UDP_MakeSocketBroadcastCapable (int socket)
{
int i = 1;
// make this socket broadcast capable
if (setsockopt (socket, SOL_SOCKET, SO_BROADCAST, (char *) &i,
sizeof (i)) < 0)
return -1;
net_broadcastsocket = socket;
return 0;
}
int
UDP_Broadcast (int socket, byte * buf, int len)
{
int ret;
if (socket != net_broadcastsocket) {
if (net_broadcastsocket != 0)
Sys_Error ("Attempted to use multiple broadcasts sockets");
ret = UDP_MakeSocketBroadcastCapable (socket);
if (ret == -1) {
Con_Printf ("Unable to make socket broadcast capable\n");
return ret;
}
}
return UDP_Write (socket, buf, len, &broadcastaddr);
}
int
UDP_Write (int socket, byte * buf, int len, struct qsockaddr *addr)
{
int ret;
ret = sendto (socket, buf, len, 0, (struct sockaddr *) addr,
sizeof (struct qsockaddr));
if (ret == -1 && errno == EWOULDBLOCK)
return 0;
return ret;
}
const char *
UDP_AddrToString (struct qsockaddr *addr)
{
static dstring_t *buffer;
int haddr;
if (!buffer)
buffer = dstring_new ();
haddr = ntohl (((struct sockaddr_in *) addr)->sin_addr.s_addr);
dsprintf (buffer, "%d.%d.%d.%d:%d", (haddr >> 24) & 0xff,
(haddr >> 16) & 0xff, (haddr >> 8) & 0xff, haddr & 0xff,
ntohs (((struct sockaddr_in *) addr)->sin_port));
return buffer->str;
}
int
UDP_StringToAddr (const char *string, struct qsockaddr *addr)
{
int ha1, ha2, ha3, ha4, hp, ipaddr;
sscanf (string, "%d.%d.%d.%d:%d", &ha1, &ha2, &ha3, &ha4, &hp);
ipaddr = (ha1 << 24) | (ha2 << 16) | (ha3 << 8) | ha4;
addr->qsa_family = AF_INET;
((struct sockaddr_in *) addr)->sin_addr.s_addr = htonl (ipaddr);
((struct sockaddr_in *) addr)->sin_port = htons (hp);
return 0;
}
int
UDP_GetSocketAddr (int socket, struct qsockaddr *addr)
{
unsigned int a;
int addrlen = sizeof (struct qsockaddr);
memset (addr, 0, sizeof (struct qsockaddr));
getsockname (socket, (struct sockaddr *) addr, &addrlen);
a = ((struct sockaddr_in *) addr)->sin_addr.s_addr;
if (a == 0 || a == inet_addr ("127.0.0.1"))
((struct sockaddr_in *) addr)->sin_addr.s_addr = myAddr;
return 0;
}
int
UDP_GetNameFromAddr (struct qsockaddr *addr, char *name)
{
struct hostent *hostentry;
hostentry =
gethostbyaddr ((char *) &((struct sockaddr_in *) addr)->sin_addr,
sizeof (struct in_addr), AF_INET);
if (hostentry) {
strncpy (name, (char *) hostentry->h_name, NET_NAMELEN - 1);
return 0;
}
strcpy (name, UDP_AddrToString (addr));
return 0;
}
int
UDP_GetAddrFromName (const char *name, struct qsockaddr *addr)
{
struct hostent *hostentry;
if (name[0] >= '0' && name[0] <= '9')
return PartialIPAddress (name, addr);
hostentry = gethostbyname (name);
if (!hostentry)
return -1;
addr->qsa_family = AF_INET;
((struct sockaddr_in *) addr)->sin_port = htons (net_hostport);
((struct sockaddr_in *) addr)->sin_addr.s_addr =
*(int *) hostentry->h_addr_list[0];
return 0;
}
int
UDP_AddrCompare (struct qsockaddr *addr1, struct qsockaddr *addr2)
{
if (addr1->qsa_family != addr2->qsa_family)
return -1;
if (((struct sockaddr_in *) addr1)->sin_addr.s_addr !=
((struct sockaddr_in *) addr2)->sin_addr.s_addr)
return -1;
if (((struct sockaddr_in *) addr1)->sin_port !=
((struct sockaddr_in *) addr2)->sin_port)
return 1;
return 0;
}
int
UDP_GetSocketPort (struct qsockaddr *addr)
{
return ntohs (((struct sockaddr_in *) addr)->sin_port);
}
int
UDP_SetSocketPort (struct qsockaddr *addr, int port)
{
((struct sockaddr_in *) addr)->sin_port = htons (port);
return 0;
}