mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-23 12:52:46 +00:00
736 lines
18 KiB
C
736 lines
18 KiB
C
|
|
/*
|
|
net_ipx.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
|
|
|
|
*/
|
|
static const char rcsid[] =
|
|
"$Id$";
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <dpmi.h>
|
|
|
|
#include "dosisms.h"
|
|
#include "net_ipx.h"
|
|
|
|
#define EIO 5 /* I/O error */
|
|
|
|
#define AF_NETWARE 64
|
|
|
|
#define IPX_OPEN 0
|
|
#define IPX_CLOSE 1
|
|
#define IPX_GETROUTE 2
|
|
#define IPX_SEND 3
|
|
#define IPX_LISTEN 4
|
|
#define IPX_SCHEDULEEVENT 5
|
|
#define IPX_CANCEL 6
|
|
#define IPX_SCHEDULESPECIALEVENT 7
|
|
#define IPX_GETINTERVALMARKER 8
|
|
#define IPX_GETADDRESS 9
|
|
#define IPX_RELINQUISH 10
|
|
|
|
#define PTYPE_UNKNOWN 0
|
|
#define PTYPE_RIP 1
|
|
#define PTYPE_ECHO 2
|
|
#define PTYPE_ERROR 3
|
|
#define PTYPE_IPX 4
|
|
#define PTYPE_SPX 5
|
|
|
|
#pragma pack(1)
|
|
|
|
typedef struct {
|
|
byte network[4];
|
|
byte node[6];
|
|
short socket;
|
|
} IPXaddr;
|
|
|
|
struct sockaddr_ipx {
|
|
short sipx_family;
|
|
IPXaddr sipx_addr;
|
|
char sipx_zero[2];
|
|
};
|
|
|
|
#define sipx_port sipx_addr.socket
|
|
|
|
typedef struct {
|
|
short checkSum;
|
|
short length;
|
|
byte transportControl;
|
|
byte type;
|
|
IPXaddr destination;
|
|
IPXaddr source;
|
|
} IPXheader;
|
|
|
|
typedef struct ECBStructure {
|
|
struct ECBStructure *link;
|
|
unsigned short ESR_off;
|
|
unsigned short ESR_seg;
|
|
byte inUse;
|
|
byte completionCode;
|
|
short socket;
|
|
byte IPXWorkspace[4];
|
|
byte driverWorkspace[12];
|
|
byte immediateAddress[6];
|
|
short fragCount;
|
|
short fragOff;
|
|
short fragSeg;
|
|
short fragSize;
|
|
} ECB;
|
|
|
|
#pragma pack()
|
|
|
|
typedef struct {
|
|
ECB ecb;
|
|
IPXheader header;
|
|
int sequence;
|
|
char data[NET_DATAGRAMSIZE];
|
|
} ipx_lowmem_buffer_t;
|
|
|
|
#define LOWMEMSIZE (100 * 1024)
|
|
#define LOWMEMSAVE 256
|
|
#define IPXBUFFERS ((LOWMEMSIZE - LOWMEMSAVE)/ sizeof(ipx_lowmem_buffer_t))
|
|
#define IPXSOCKBUFFERS 5
|
|
#define IPXSOCKETS (IPXBUFFERS / IPXSOCKBUFFERS)
|
|
|
|
// each socket's socketbuffer 0 is used for sending, the others for listening
|
|
|
|
typedef struct {
|
|
char reserved[LOWMEMSAVE];
|
|
ipx_lowmem_buffer_t socketbuffer[IPXSOCKETS][IPXSOCKBUFFERS];
|
|
} ipx_lowmem_area_t;
|
|
|
|
|
|
static int ipxsocket[IPXSOCKETS];
|
|
static ECB *readlist[IPXSOCKETS];
|
|
static int sequence[IPXSOCKETS];
|
|
static int handlesInUse;
|
|
static ipx_lowmem_area_t *lma;
|
|
static char *lowmem_buffer;
|
|
static int lowmem_bufseg;
|
|
static int lowmem_bufoff;
|
|
static unsigned short ipx_cs;
|
|
static unsigned short ipx_ip;
|
|
static int net_acceptsocket = -1;
|
|
static int net_controlsocket;
|
|
|
|
static void IPX_PollProcedure (void);
|
|
static PollProcedure pollProcedure = { NULL, 0.0, IPX_PollProcedure };
|
|
|
|
//=============================================================================
|
|
|
|
static void
|
|
IPX_GetLocalAddress (IPXaddr * addr)
|
|
{
|
|
regs.x.cs = ipx_cs;
|
|
regs.x.ip = ipx_ip;
|
|
regs.x.bx = IPX_GETADDRESS;
|
|
regs.x.es = lowmem_bufseg;
|
|
regs.x.si = lowmem_bufoff;
|
|
__dpmi_simulate_real_mode_procedure_retf ((__dpmi_regs *) & regs);
|
|
Q_memcpy (addr, lowmem_buffer, 10);
|
|
}
|
|
|
|
//=============================================================================
|
|
|
|
static int
|
|
IPX_GetLocalTarget (IPXaddr * addr, byte * localTarget)
|
|
{
|
|
regs.x.cs = ipx_cs;
|
|
regs.x.ip = ipx_ip;
|
|
regs.x.bx = IPX_GETROUTE;
|
|
regs.x.es = lowmem_bufseg;
|
|
regs.x.si = lowmem_bufoff;
|
|
regs.x.di = lowmem_bufoff + sizeof (IPXaddr);
|
|
Q_memcpy (lowmem_buffer, addr, sizeof (IPXaddr));
|
|
__dpmi_simulate_real_mode_procedure_retf ((__dpmi_regs *) & regs);
|
|
if (regs.h.al)
|
|
return -1;
|
|
Q_memcpy (localTarget, lowmem_buffer + sizeof (IPXaddr), 6);
|
|
return 0;
|
|
}
|
|
|
|
//=============================================================================
|
|
|
|
static void
|
|
IPX_ListenForPacket (ECB * ecb)
|
|
{
|
|
regs.x.cs = ipx_cs;
|
|
regs.x.ip = ipx_ip;
|
|
regs.x.bx = IPX_LISTEN;
|
|
regs.x.es = ptr2real (ecb) >> 4;
|
|
regs.x.si = ptr2real (ecb) & 0xf;
|
|
__dpmi_simulate_real_mode_procedure_retf ((__dpmi_regs *) & regs);
|
|
}
|
|
|
|
//=============================================================================
|
|
|
|
static void
|
|
IPX_RelinquishControl (void)
|
|
{
|
|
regs.x.cs = ipx_cs;
|
|
regs.x.ip = ipx_ip;
|
|
regs.x.bx = IPX_RELINQUISH;
|
|
__dpmi_simulate_real_mode_procedure_retf ((__dpmi_regs *) & regs);
|
|
}
|
|
|
|
|
|
void
|
|
IPX_PollProcedure (void)
|
|
{
|
|
IPX_RelinquishControl ();
|
|
SchedulePollProcedure (&pollProcedure, 0.01);
|
|
}
|
|
|
|
//=============================================================================
|
|
|
|
static void
|
|
ProcessReadyList (int s)
|
|
{
|
|
int n;
|
|
ECB *ecb;
|
|
ECB *prev;
|
|
|
|
for (n = 1; n < IPXSOCKBUFFERS; n++) {
|
|
if (lma->socketbuffer[s][n].ecb.inUse == 0) {
|
|
for (ecb = readlist[s], prev = NULL; ecb; ecb = ecb->link) {
|
|
if (lma->socketbuffer[s][n].sequence <
|
|
((ipx_lowmem_buffer_t *) ecb)->sequence)
|
|
break;
|
|
prev = ecb;
|
|
}
|
|
if (ecb)
|
|
lma->socketbuffer[s][n].ecb.link = ecb;
|
|
else
|
|
lma->socketbuffer[s][n].ecb.link = NULL;
|
|
if (prev)
|
|
prev->link = &lma->socketbuffer[s][n].ecb;
|
|
else
|
|
readlist[s] = &lma->socketbuffer[s][n].ecb;
|
|
lma->socketbuffer[s][n].ecb.inUse = 0xff;
|
|
}
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
|
|
int
|
|
IPX_Init (void)
|
|
{
|
|
int s;
|
|
int n;
|
|
struct qsockaddr addr;
|
|
char *colon;
|
|
|
|
if (COM_CheckParm ("-noipx"))
|
|
return -1;
|
|
|
|
// find the IPX far call entry point
|
|
regs.x.ax = 0x7a00;
|
|
__dpmi_simulate_real_mode_interrupt (0x2f, (__dpmi_regs *) & regs);
|
|
if (regs.h.al != 0xff) {
|
|
Con_Printf ("IPX not detected\n");
|
|
return -1;
|
|
}
|
|
ipx_cs = regs.x.es;
|
|
ipx_ip = regs.x.di;
|
|
|
|
// grab a chunk of memory down in DOS land
|
|
lowmem_buffer = dos_getmemory (LOWMEMSIZE);
|
|
if (!lowmem_buffer) {
|
|
Con_Printf ("IPX_Init: Not enough low memory\n");
|
|
return -1;
|
|
}
|
|
lowmem_bufoff = ptr2real (lowmem_buffer) & 0xf;
|
|
lowmem_bufseg = ptr2real (lowmem_buffer) >> 4;
|
|
|
|
// init socket handles & buffers
|
|
handlesInUse = 0;
|
|
lma = (ipx_lowmem_area_t *) lowmem_buffer;
|
|
for (s = 0; s < IPXSOCKETS; s++) {
|
|
ipxsocket[s] = 0;
|
|
for (n = 0; n < IPXSOCKBUFFERS; n++) {
|
|
lma->socketbuffer[s][n].ecb.link = NULL;
|
|
lma->socketbuffer[s][n].ecb.ESR_off = 0;
|
|
lma->socketbuffer[s][n].ecb.ESR_seg = 0;
|
|
lma->socketbuffer[s][n].ecb.socket = 0;
|
|
lma->socketbuffer[s][n].ecb.inUse = 0xff;
|
|
lma->socketbuffer[s][n].ecb.completionCode = 0;
|
|
lma->socketbuffer[s][n].ecb.fragCount = 1;
|
|
lma->socketbuffer[s][n].ecb.fragOff =
|
|
ptr2real (&lma->socketbuffer[s][n].header) & 0xf;
|
|
lma->socketbuffer[s][n].ecb.fragSeg =
|
|
ptr2real (&lma->socketbuffer[s][n].header) >> 4;
|
|
lma->socketbuffer[s][n].ecb.fragSize =
|
|
sizeof (IPXheader) + sizeof (int) + NET_DATAGRAMSIZE;
|
|
}
|
|
}
|
|
|
|
if ((net_controlsocket = IPX_OpenSocket (0)) == -1) {
|
|
dos_freememory (lowmem_buffer);
|
|
Con_DPrintf ("IPX_Init: Unable to open control socket\n");
|
|
return -1;
|
|
}
|
|
|
|
SchedulePollProcedure (&pollProcedure, 0.01);
|
|
|
|
IPX_GetSocketAddr (net_controlsocket, &addr);
|
|
Q_strcpy (my_ipx_address, IPX_AddrToString (&addr));
|
|
colon = Q_strrchr (my_ipx_address, ':');
|
|
if (colon)
|
|
*colon = 0;
|
|
|
|
Con_Printf ("IPX initialized\n");
|
|
ipxAvailable = true;
|
|
return net_controlsocket;
|
|
}
|
|
|
|
//=============================================================================
|
|
|
|
void
|
|
IPX_Shutdown (void)
|
|
{
|
|
IPX_Listen (false);
|
|
IPX_CloseSocket (net_controlsocket);
|
|
dos_freememory (lowmem_buffer);
|
|
}
|
|
|
|
//=============================================================================
|
|
|
|
void
|
|
IPX_Listen (qboolean state)
|
|
{
|
|
// enable listening
|
|
if (state) {
|
|
if (net_acceptsocket != -1)
|
|
return;
|
|
if ((net_acceptsocket = IPX_OpenSocket (net_hostport)) == -1)
|
|
Sys_Error ("IPX_Listen: Unable to open accept socket\n");
|
|
return;
|
|
}
|
|
// disable listening
|
|
if (net_acceptsocket == -1)
|
|
return;
|
|
IPX_CloseSocket (net_acceptsocket);
|
|
net_acceptsocket = -1;
|
|
}
|
|
|
|
//=============================================================================
|
|
|
|
int
|
|
IPX_OpenSocket (int port)
|
|
{
|
|
int handle;
|
|
int n;
|
|
unsigned short socket;
|
|
|
|
if (handlesInUse == IPXSOCKETS)
|
|
return -1;
|
|
|
|
// open the IPX socket
|
|
regs.x.cs = ipx_cs;
|
|
regs.x.ip = ipx_ip;
|
|
regs.x.bx = IPX_OPEN;
|
|
regs.h.al = 0;
|
|
regs.x.dx = htons (port);
|
|
__dpmi_simulate_real_mode_procedure_retf ((__dpmi_regs *) & regs);
|
|
if (regs.h.al == 0xfe) {
|
|
Con_DPrintf ("IPX_OpenSocket: all sockets in use\n");
|
|
return -1;
|
|
}
|
|
if (regs.h.al == 0xff) {
|
|
Con_DPrintf ("IPX_OpenSocket: socket already open\n");
|
|
return -1;
|
|
}
|
|
if (regs.h.al != 0) {
|
|
Con_DPrintf ("IPX_OpenSocket: error %02x\n", regs.h.al);
|
|
return -1;
|
|
}
|
|
socket = regs.x.dx;
|
|
|
|
// grab a handle; fill in the ECBs, and get them listening
|
|
for (handle = 0; handle < IPXSOCKETS; handle++) {
|
|
if (ipxsocket[handle] == 0) {
|
|
ipxsocket[handle] = socket;
|
|
readlist[handle] = NULL;
|
|
sequence[handle] = 0;
|
|
for (n = 0; n < IPXSOCKBUFFERS; n++) {
|
|
lma->socketbuffer[handle][n].ecb.socket = socket;
|
|
lma->socketbuffer[handle][n].ecb.inUse = 0;
|
|
if (n)
|
|
IPX_ListenForPacket (&lma->socketbuffer[handle][n].ecb);
|
|
}
|
|
handlesInUse++;
|
|
return handle;
|
|
}
|
|
}
|
|
|
|
// "this will NEVER happen"
|
|
Sys_Error ("IPX_OpenSocket: handle allocation failed\n");
|
|
return -1;
|
|
}
|
|
|
|
//=============================================================================
|
|
|
|
int
|
|
IPX_CloseSocket (int handle)
|
|
{
|
|
// if there's a send in progress, give it one last chance
|
|
if (lma->socketbuffer[handle][0].ecb.inUse != 0)
|
|
IPX_RelinquishControl ();
|
|
|
|
// close the socket (all pending sends/received are cancelled)
|
|
regs.x.cs = ipx_cs;
|
|
regs.x.ip = ipx_ip;
|
|
regs.x.bx = IPX_CLOSE;
|
|
regs.x.dx = ipxsocket[handle];
|
|
__dpmi_simulate_real_mode_procedure_retf ((__dpmi_regs *) & regs);
|
|
|
|
ipxsocket[handle] = 0;
|
|
handlesInUse--;
|
|
|
|
return 0;
|
|
}
|
|
|
|
//=============================================================================
|
|
|
|
int
|
|
IPX_Connect (int handle, struct qsockaddr *addr)
|
|
{
|
|
IPXaddr ipxaddr;
|
|
|
|
Q_memcpy (&ipxaddr, &((struct sockaddr_ipx *) addr)->sipx_addr,
|
|
sizeof (IPXaddr));
|
|
if (IPX_GetLocalTarget
|
|
(&ipxaddr, lma->socketbuffer[handle][0].ecb.immediateAddress) != 0) {
|
|
Con_Printf ("Get Local Target failed\n");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
//=============================================================================
|
|
|
|
int
|
|
IPX_CheckNewConnections (void)
|
|
{
|
|
int n;
|
|
|
|
if (net_acceptsocket == -1)
|
|
return -1;
|
|
|
|
for (n = 1; n < IPXSOCKBUFFERS; n++)
|
|
if (lma->socketbuffer[net_acceptsocket][n].ecb.inUse == 0)
|
|
return net_acceptsocket;
|
|
return -1;
|
|
}
|
|
|
|
//=============================================================================
|
|
|
|
int
|
|
IPX_Read (int handle, byte * buf, int len, struct qsockaddr *addr)
|
|
{
|
|
ECB *ecb;
|
|
ipx_lowmem_buffer_t *rcvbuf;
|
|
int copylen;
|
|
|
|
ProcessReadyList (handle);
|
|
tryagain:
|
|
if (readlist[handle] == NULL)
|
|
return 0;
|
|
ecb = readlist[handle];
|
|
readlist[handle] = ecb->link;
|
|
|
|
if (ecb->completionCode != 0) {
|
|
Con_Printf ("Warning: IPX_Read error %02x\n", ecb->completionCode);
|
|
ecb->fragSize = sizeof (IPXheader) + sizeof (int) + NET_DATAGRAMSIZE;
|
|
|
|
IPX_ListenForPacket (ecb);
|
|
goto tryagain;
|
|
}
|
|
|
|
rcvbuf = (ipx_lowmem_buffer_t *) ecb;
|
|
|
|
// copy the data up to the buffer
|
|
copylen =
|
|
|
|
ntohs (rcvbuf->header.length) - (sizeof (int) + sizeof (IPXheader));
|
|
if (len < copylen)
|
|
Sys_Error ("IPX_Read: buffer too small (%d vs %d)\n", len, copylen);
|
|
Q_memcpy (buf, rcvbuf->data, copylen);
|
|
|
|
// fill in the addr if they want it
|
|
if (addr) {
|
|
((struct sockaddr_ipx *) addr)->sipx_family = AF_NETWARE;
|
|
Q_memcpy (&((struct sockaddr_ipx *) addr)->sipx_addr,
|
|
rcvbuf->header.source.network, sizeof (IPXaddr));
|
|
((struct sockaddr_ipx *) addr)->sipx_zero[0] = 0;
|
|
((struct sockaddr_ipx *) addr)->sipx_zero[1] = 0;
|
|
}
|
|
// update the send ecb's immediate address
|
|
Q_memcpy (lma->socketbuffer[handle][0].ecb.immediateAddress,
|
|
rcvbuf->ecb.immediateAddress, 6);
|
|
|
|
// get this ecb listening again
|
|
rcvbuf->ecb.fragSize = sizeof (IPXheader) + sizeof (int) + NET_DATAGRAMSIZE;
|
|
|
|
IPX_ListenForPacket (&rcvbuf->ecb);
|
|
return copylen;
|
|
}
|
|
|
|
//=============================================================================
|
|
|
|
int
|
|
IPX_Broadcast (int handle, byte * buf, int len)
|
|
{
|
|
struct sockaddr_ipx addr;
|
|
int ret;
|
|
|
|
Q_memset (addr.sipx_addr.network, 0x00, 4);
|
|
Q_memset (addr.sipx_addr.node, 0xff, 6);
|
|
addr.sipx_port = htons (net_hostport);
|
|
Q_memset (lma->socketbuffer[handle][0].ecb.immediateAddress, 0xff, 6);
|
|
ret = IPX_Write (handle, buf, len, (struct qsockaddr *) &addr);
|
|
return ret;
|
|
}
|
|
|
|
//=============================================================================
|
|
|
|
int
|
|
IPX_Write (int handle, byte * buf, int len, struct qsockaddr *addr)
|
|
{
|
|
// has the previous send completed?
|
|
while (lma->socketbuffer[handle][0].ecb.inUse != 0)
|
|
IPX_RelinquishControl ();
|
|
|
|
switch (lma->socketbuffer[handle][0].ecb.completionCode) {
|
|
case 0x00: // success
|
|
case 0xfc: // request cancelled
|
|
break;
|
|
|
|
case 0xfd: // malformed packet
|
|
default:
|
|
Con_Printf ("IPX driver send failure: %02x\n",
|
|
lma->socketbuffer[handle][0].ecb.completionCode);
|
|
break;
|
|
|
|
case 0xfe: // packet undeliverable
|
|
case 0xff: // unable to send packet
|
|
Con_Printf ("IPX lost route, trying to re-establish\n");
|
|
|
|
// look for a new route
|
|
if (IPX_GetLocalTarget
|
|
(&lma->socketbuffer[handle][0].header.destination,
|
|
lma->socketbuffer[handle][0].ecb.immediateAddress) != 0)
|
|
return -1;
|
|
|
|
// re-send the one that failed
|
|
regs.x.cs = ipx_cs;
|
|
regs.x.ip = ipx_ip;
|
|
regs.x.bx = IPX_SEND;
|
|
regs.x.es = ptr2real (&lma->socketbuffer[handle][0].ecb) >> 4;
|
|
regs.x.si = ptr2real (&lma->socketbuffer[handle][0].ecb) & 0xf;
|
|
__dpmi_simulate_real_mode_procedure_retf ((__dpmi_regs *) & regs);
|
|
|
|
// report that we did not send the current one
|
|
return 0;
|
|
}
|
|
|
|
// ecb : length
|
|
lma->socketbuffer[handle][0].ecb.fragSize =
|
|
sizeof (IPXheader) + sizeof (int) + len;
|
|
|
|
// ipx header : type
|
|
lma->socketbuffer[handle][0].header.type = PTYPE_IPX;
|
|
|
|
// ipx header : destination
|
|
Q_memcpy (&lma->socketbuffer[handle][0].header.destination,
|
|
&((struct sockaddr_ipx *) addr)->sipx_addr, sizeof (IPXaddr));
|
|
|
|
// sequence number
|
|
lma->socketbuffer[handle][0].sequence = sequence[handle];
|
|
sequence[handle]++;
|
|
|
|
// copy down the data
|
|
Q_memcpy (lma->socketbuffer[handle][0].data, buf, len);
|
|
|
|
regs.x.cs = ipx_cs;
|
|
regs.x.ip = ipx_ip;
|
|
regs.x.bx = IPX_SEND;
|
|
regs.x.es = ptr2real (&lma->socketbuffer[handle][0].ecb) >> 4;
|
|
regs.x.si = ptr2real (&lma->socketbuffer[handle][0].ecb) & 0xf;
|
|
__dpmi_simulate_real_mode_procedure_retf ((__dpmi_regs *) & regs);
|
|
|
|
return len;
|
|
}
|
|
|
|
//=============================================================================
|
|
|
|
char *
|
|
IPX_AddrToString (struct qsockaddr *addr)
|
|
{
|
|
static char buf[28];
|
|
|
|
snprintf (buf, sizeof (buf), "%02x%02x%02x%02x:%02x%02x%02x%02x%02x%02x:%u",
|
|
((struct sockaddr_ipx *) addr)->sipx_addr.network[0],
|
|
((struct sockaddr_ipx *) addr)->sipx_addr.network[1],
|
|
((struct sockaddr_ipx *) addr)->sipx_addr.network[2],
|
|
((struct sockaddr_ipx *) addr)->sipx_addr.network[3],
|
|
((struct sockaddr_ipx *) addr)->sipx_addr.node[0],
|
|
((struct sockaddr_ipx *) addr)->sipx_addr.node[1],
|
|
((struct sockaddr_ipx *) addr)->sipx_addr.node[2],
|
|
((struct sockaddr_ipx *) addr)->sipx_addr.node[3],
|
|
((struct sockaddr_ipx *) addr)->sipx_addr.node[4],
|
|
((struct sockaddr_ipx *) addr)->sipx_addr.node[5],
|
|
ntohs (((struct sockaddr_ipx *) addr)->sipx_port)
|
|
);
|
|
return buf;
|
|
}
|
|
|
|
//=============================================================================
|
|
|
|
int
|
|
IPX_StringToAddr (char *string, struct qsockaddr *addr)
|
|
{
|
|
int val;
|
|
char buf[3];
|
|
|
|
buf[2] = 0;
|
|
Q_memset (addr, 0, sizeof (struct qsockaddr));
|
|
|
|
addr->sa_family = AF_NETWARE;
|
|
|
|
#define DO(src,dest) \
|
|
buf[0] = string[src]; \
|
|
buf[1] = string[src + 1]; \
|
|
if (sscanf (buf, "%x", &val) != 1) \
|
|
return -1; \
|
|
((struct sockaddr_ipx *)addr)->sipx_addr.dest = val
|
|
|
|
DO (0, network[0]);
|
|
DO (2, network[1]);
|
|
DO (4, network[2]);
|
|
DO (6, network[3]);
|
|
DO (9, node[0]);
|
|
DO (11, node[1]);
|
|
DO (13, node[2]);
|
|
DO (15, node[3]);
|
|
DO (17, node[4]);
|
|
DO (19, node[5]);
|
|
#undef DO
|
|
|
|
sscanf (&string[22], "%u", &val);
|
|
((struct sockaddr_ipx *) addr)->sipx_port = htons (val);
|
|
|
|
return 0;
|
|
}
|
|
|
|
//=============================================================================
|
|
|
|
int
|
|
IPX_GetSocketAddr (int handle, struct qsockaddr *addr)
|
|
{
|
|
Q_memset (addr, 0, sizeof (struct qsockaddr));
|
|
|
|
addr->sa_family = AF_NETWARE;
|
|
IPX_GetLocalAddress (&((struct sockaddr_ipx *) addr)->sipx_addr);
|
|
((struct sockaddr_ipx *) addr)->sipx_port = ipxsocket[handle];
|
|
return 0;
|
|
}
|
|
|
|
//=============================================================================
|
|
|
|
int
|
|
IPX_GetNameFromAddr (struct qsockaddr *addr, char *name)
|
|
{
|
|
Q_strcpy (name, IPX_AddrToString (addr));
|
|
return 0;
|
|
}
|
|
|
|
//=============================================================================
|
|
|
|
int
|
|
IPX_GetAddrFromName (char *name, struct qsockaddr *addr)
|
|
{
|
|
int n;
|
|
char buf[32];
|
|
|
|
n = Q_strlen (name);
|
|
|
|
if (n == 12) {
|
|
snprintf (buf, sizeof (buf), "00000000:%s:%u", name, net_hostport);
|
|
return IPX_StringToAddr (buf, addr);
|
|
}
|
|
if (n == 21) {
|
|
snprintf (buf, sizeof (buf), "%s:%u", name, net_hostport);
|
|
return IPX_StringToAddr (buf, addr);
|
|
}
|
|
if (n > 21 && n <= 27)
|
|
return IPX_StringToAddr (name, addr);
|
|
|
|
return -1;
|
|
}
|
|
|
|
//=============================================================================
|
|
|
|
int
|
|
IPX_AddrCompare (struct qsockaddr *addr1, struct qsockaddr *addr2)
|
|
{
|
|
if (addr1->sa_family != addr2->sa_family)
|
|
return -1;
|
|
|
|
if (Q_memcmp
|
|
(&((struct sockaddr_ipx *) addr1)->sipx_addr,
|
|
&((struct sockaddr_ipx *) addr2)->sipx_addr, 10))
|
|
return -1;
|
|
|
|
if (((struct sockaddr_ipx *) addr1)->sipx_port !=
|
|
((struct sockaddr_ipx *) addr2)->sipx_port)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
//=============================================================================
|
|
|
|
int
|
|
IPX_GetSocketPort (struct qsockaddr *addr)
|
|
{
|
|
return ntohs (((struct sockaddr_ipx *) addr)->sipx_port);
|
|
}
|
|
|
|
|
|
int
|
|
IPX_SetSocketPort (struct qsockaddr *addr, int port)
|
|
{
|
|
((struct sockaddr_ipx *) addr)->sipx_port = htons (port);
|
|
return 0;
|
|
}
|
|
|
|
//=============================================================================
|