1/*
2Copyright (C) 1996-1997 Id Software, Inc.
3
4This program is free software; you can redistribute it and/or
5modify it under the terms of the GNU General Public License
6as published by the Free Software Foundation; either version 2
7of the License, or (at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13See the GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19*/
20// net_ipx.c
21
22#include <stdio.h>
23#include <stdlib.h>
24#include <dpmi.h>
25
26#include "quakedef.h"
27#include "dosisms.h"
28#include "net_ipx.h"
29
30#define	EIO		 		5	/* I/O error */
31
32#define AF_NETWARE 		64
33
34#define IPX_OPEN					0
35#define IPX_CLOSE					1
36#define IPX_GETROUTE				2
37#define IPX_SEND					3
38#define IPX_LISTEN					4
39#define IPX_SCHEDULEEVENT			5
40#define IPX_CANCEL					6
41#define IPX_SCHEDULESPECIALEVENT	7
42#define IPX_GETINTERVALMARKER		8
43#define IPX_GETADDRESS				9
44#define IPX_RELINQUISH				10
45
46#define PTYPE_UNKNOWN				0
47#define PTYPE_RIP					1
48#define PTYPE_ECHO					2
49#define PTYPE_ERROR					3
50#define PTYPE_IPX					4
51#define PTYPE_SPX					5
52
53#pragma pack(1)
54
55typedef struct
56{
57	byte	network[4];
58	byte	node[6];
59	short	socket;
60} IPXaddr;
61
62struct sockaddr_ipx
63{
64    short			sipx_family;
65	IPXaddr			sipx_addr;
66    char			sipx_zero[2];
67};
68#define sipx_port sipx_addr.socket
69
70typedef struct
71{
72	short			checkSum;
73	short			length;
74	byte			transportControl;
75	byte			type;
76	IPXaddr			destination;
77	IPXaddr			source;
78} IPXheader;
79
80typedef struct ECBStructure
81{
82	struct ECBStructure *link;
83	unsigned short	ESR_off;
84	unsigned short	ESR_seg;
85	byte	inUse;
86	byte	completionCode;
87	short	socket;
88	byte	IPXWorkspace[4];
89	byte	driverWorkspace[12];
90	byte	immediateAddress[6];
91	short	fragCount;
92	short	fragOff;
93	short	fragSeg;
94	short	fragSize;
95} ECB;
96
97#pragma pack()
98
99typedef struct
100{
101	ECB			ecb;
102	IPXheader	header;
103	int			sequence;
104	char		data[NET_DATAGRAMSIZE];
105} ipx_lowmem_buffer_t;
106
107#define LOWMEMSIZE		(100 * 1024)
108#define LOWMEMSAVE		256
109#define IPXBUFFERS		((LOWMEMSIZE - LOWMEMSAVE)/ sizeof(ipx_lowmem_buffer_t))
110#define IPXSOCKBUFFERS	5
111#define IPXSOCKETS		(IPXBUFFERS / IPXSOCKBUFFERS)
112
113// each socket's socketbuffer 0 is used for sending, the others for listening
114
115typedef struct
116{
117	char				reserved[LOWMEMSAVE];
118	ipx_lowmem_buffer_t	socketbuffer[IPXSOCKETS][IPXSOCKBUFFERS];
119} ipx_lowmem_area_t;
120
121
122static int ipxsocket[IPXSOCKETS];
123static ECB *readlist[IPXSOCKETS];
124static int sequence[IPXSOCKETS];
125static int handlesInUse;
126static ipx_lowmem_area_t *lma;
127static char *lowmem_buffer;
128static int lowmem_bufseg;
129static int lowmem_bufoff;
130static unsigned short ipx_cs;
131static unsigned short ipx_ip;
132static int net_acceptsocket = -1;
133static int net_controlsocket;
134
135static void IPX_PollProcedure(void);
136static PollProcedure pollProcedure = {NULL, 0.0, IPX_PollProcedure};
137
138//=============================================================================
139
140static void IPX_GetLocalAddress(IPXaddr *addr)
141{
142	regs.x.cs = ipx_cs;
143	regs.x.ip = ipx_ip;
144	regs.x.bx = IPX_GETADDRESS;
145	regs.x.es = lowmem_bufseg;
146	regs.x.si = lowmem_bufoff;
147	__dpmi_simulate_real_mode_procedure_retf((__dpmi_regs *)&regs);
148	Q_memcpy(addr, lowmem_buffer, 10);
149}
150
151//=============================================================================
152
153static int IPX_GetLocalTarget(IPXaddr *addr, byte *localTarget)
154{
155	regs.x.cs = ipx_cs;
156	regs.x.ip = ipx_ip;
157	regs.x.bx = IPX_GETROUTE;
158	regs.x.es = lowmem_bufseg;
159	regs.x.si = lowmem_bufoff;
160	regs.x.di = lowmem_bufoff + sizeof(IPXaddr);
161	Q_memcpy(lowmem_buffer, addr, sizeof(IPXaddr));
162	__dpmi_simulate_real_mode_procedure_retf((__dpmi_regs *)&regs);
163	if (regs.h.al)
164		return -1;
165	Q_memcpy(localTarget, lowmem_buffer + sizeof(IPXaddr), 6);
166	return 0;
167}
168
169//=============================================================================
170
171static void IPX_ListenForPacket(ECB *ecb)
172{
173	regs.x.cs = ipx_cs;
174	regs.x.ip = ipx_ip;
175	regs.x.bx = IPX_LISTEN;
176	regs.x.es = ptr2real(ecb) >> 4;
177	regs.x.si = ptr2real(ecb) & 0xf;
178	__dpmi_simulate_real_mode_procedure_retf((__dpmi_regs *)&regs);
179}
180
181//=============================================================================
182
183static void IPX_RelinquishControl(void)
184{
185	regs.x.cs = ipx_cs;
186	regs.x.ip = ipx_ip;
187	regs.x.bx = IPX_RELINQUISH;
188	__dpmi_simulate_real_mode_procedure_retf((__dpmi_regs *)&regs);
189}
190
191
192void IPX_PollProcedure(void)
193{
194	IPX_RelinquishControl();
195	SchedulePollProcedure(&pollProcedure, 0.01);
196}
197
198//=============================================================================
199
200static void ProcessReadyList(int s)
201{
202	int n;
203	ECB *ecb;
204	ECB *prev;
205
206	for (n = 1; n < IPXSOCKBUFFERS; n++)
207	{
208		if (lma->socketbuffer[s][n].ecb.inUse == 0)
209		{
210			for (ecb = readlist[s], prev = NULL; ecb; ecb = ecb->link)
211			{
212				if (lma->socketbuffer[s][n].sequence < ((ipx_lowmem_buffer_t *) ecb)->sequence)
213					break;
214				prev = ecb;
215			}
216			if (ecb)
217				lma->socketbuffer[s][n].ecb.link = ecb;
218			else
219				lma->socketbuffer[s][n].ecb.link = NULL;
220			if (prev)
221				prev->link = &lma->socketbuffer[s][n].ecb;
222			else
223				readlist[s] = &lma->socketbuffer[s][n].ecb;
224			lma->socketbuffer[s][n].ecb.inUse = 0xff;
225		}
226	}
227}
228
229//=============================================================================
230
231int IPX_Init(void)
232{
233	int s;
234	int n;
235	struct qsockaddr addr;
236	char *colon;
237
238	if (COM_CheckParm ("-noipx"))
239		return -1;
240
241	// find the IPX far call entry point
242	regs.x.ax = 0x7a00;
243	__dpmi_simulate_real_mode_interrupt (0x2f, (__dpmi_regs *)&regs);
244	if (regs.h.al != 0xff)
245	{
246		Con_Printf("IPX not detected\n");
247		return -1;
248	}
249	ipx_cs = regs.x.es;
250	ipx_ip = regs.x.di;
251
252	// grab a chunk of memory down in DOS land
253	lowmem_buffer = dos_getmemory(LOWMEMSIZE);
254	if (!lowmem_buffer)
255	{
256		Con_Printf("IPX_Init: Not enough low memory\n");
257		return -1;
258	}
259	lowmem_bufoff = ptr2real(lowmem_buffer) & 0xf;
260	lowmem_bufseg = ptr2real(lowmem_buffer) >> 4;
261
262	// init socket handles & buffers
263	handlesInUse = 0;
264	lma = (ipx_lowmem_area_t *)lowmem_buffer;
265	for (s = 0; s < IPXSOCKETS; s++)
266	{
267		ipxsocket[s] = 0;
268		for (n = 0; n < IPXSOCKBUFFERS; n++)
269		{
270			lma->socketbuffer[s][n].ecb.link = NULL;
271			lma->socketbuffer[s][n].ecb.ESR_off = 0;
272			lma->socketbuffer[s][n].ecb.ESR_seg = 0;
273			lma->socketbuffer[s][n].ecb.socket = 0;
274			lma->socketbuffer[s][n].ecb.inUse = 0xff;
275			lma->socketbuffer[s][n].ecb.completionCode = 0;
276			lma->socketbuffer[s][n].ecb.fragCount = 1;
277			lma->socketbuffer[s][n].ecb.fragOff = ptr2real(&lma->socketbuffer[s][n].header) & 0xf;
278			lma->socketbuffer[s][n].ecb.fragSeg = ptr2real(&lma->socketbuffer[s][n].header) >> 4;
279			lma->socketbuffer[s][n].ecb.fragSize = sizeof(IPXheader) + sizeof(int) + NET_DATAGRAMSIZE;
280		}
281	}
282
283	if ((net_controlsocket = IPX_OpenSocket (0)) == -1)
284	{
285		dos_freememory(lowmem_buffer);
286		Con_DPrintf ("IPX_Init: Unable to open control socket\n");
287		return -1;
288	}
289
290	SchedulePollProcedure(&pollProcedure, 0.01);
291
292	IPX_GetSocketAddr (net_controlsocket, &addr);
293	Q_strcpy(my_ipx_address,  IPX_AddrToString (&addr));
294	colon = Q_strrchr (my_ipx_address, ':');
295	if (colon)
296		*colon = 0;
297
298	Con_Printf("IPX initialized\n");
299	ipxAvailable = true;
300	return net_controlsocket;
301}
302
303//=============================================================================
304
305void IPX_Shutdown(void)
306{
307	IPX_Listen (false);
308	IPX_CloseSocket (net_controlsocket);
309	dos_freememory(lowmem_buffer);
310}
311
312//=============================================================================
313
314void IPX_Listen (qboolean state)
315{
316	// enable listening
317	if (state)
318	{
319		if (net_acceptsocket != -1)
320			return;
321		if ((net_acceptsocket = IPX_OpenSocket (net_hostport)) == -1)
322			Sys_Error ("IPX_Listen: Unable to open accept socket\n");
323		return;
324	}
325
326	// disable listening
327	if (net_acceptsocket == -1)
328		return;
329	IPX_CloseSocket (net_acceptsocket);
330	net_acceptsocket = -1;
331}
332
333//=============================================================================
334
335int IPX_OpenSocket(int port)
336{
337	int handle;
338	int n;
339	unsigned short socket;
340
341	if (handlesInUse == IPXSOCKETS)
342		return -1;
343
344	// open the IPX socket
345	regs.x.cs = ipx_cs;
346	regs.x.ip = ipx_ip;
347	regs.x.bx = IPX_OPEN;
348	regs.h.al = 0;
349	regs.x.dx = htons(port);
350	__dpmi_simulate_real_mode_procedure_retf((__dpmi_regs *)&regs);
351	if (regs.h.al == 0xfe)
352	{
353		Con_DPrintf("IPX_OpenSocket: all sockets in use\n");
354		return -1;
355	}
356	if (regs.h.al == 0xff)
357	{
358		Con_DPrintf("IPX_OpenSocket: socket already open\n");
359		return -1;
360	}
361	if (regs.h.al != 0)
362	{
363		Con_DPrintf("IPX_OpenSocket: error %02x\n", regs.h.al);
364		return -1;
365	}
366	socket = regs.x.dx;
367
368// grab a handle; fill in the ECBs, and get them listening
369	for (handle = 0; handle < IPXSOCKETS; handle++)
370	{
371		if (ipxsocket[handle] == 0)
372		{
373			ipxsocket[handle] = socket;
374			readlist[handle] = NULL;
375			sequence[handle] = 0;
376			for (n = 0; n < IPXSOCKBUFFERS; n ++)
377			{
378				lma->socketbuffer[handle][n].ecb.socket = socket;
379				lma->socketbuffer[handle][n].ecb.inUse = 0;
380				if (n)
381					IPX_ListenForPacket(&lma->socketbuffer[handle][n].ecb);
382			}
383			handlesInUse++;
384			return handle;
385		}
386	}
387
388	// "this will NEVER happen"
389	Sys_Error("IPX_OpenSocket: handle allocation failed\n");
390	return -1;
391}
392
393//=============================================================================
394
395int IPX_CloseSocket(int handle)
396{
397	// if there's a send in progress, give it one last chance
398	if (lma->socketbuffer[handle][0].ecb.inUse != 0)
399		IPX_RelinquishControl();
400
401	// close the socket (all pending sends/received are cancelled)
402	regs.x.cs = ipx_cs;
403	regs.x.ip = ipx_ip;
404	regs.x.bx = IPX_CLOSE;
405	regs.x.dx = ipxsocket[handle];
406	__dpmi_simulate_real_mode_procedure_retf((__dpmi_regs *)&regs);
407
408	ipxsocket[handle] = 0;
409	handlesInUse--;
410
411	return 0;
412}
413
414//=============================================================================
415
416int IPX_Connect (int handle, struct qsockaddr *addr)
417{
418	IPXaddr ipxaddr;
419
420	Q_memcpy(&ipxaddr, &((struct sockaddr_ipx *)addr)->sipx_addr, sizeof(IPXaddr));
421	if (IPX_GetLocalTarget(&ipxaddr, lma->socketbuffer[handle][0].ecb.immediateAddress) != 0)
422	{
423		Con_Printf("Get Local Target failed\n");
424		return -1;
425	}
426
427	return 0;
428}
429
430//=============================================================================
431
432int IPX_CheckNewConnections (void)
433{
434	int n;
435
436	if (net_acceptsocket == -1)
437		return -1;
438
439	for (n = 1; n < IPXSOCKBUFFERS; n ++)
440		if (lma->socketbuffer[net_acceptsocket][n].ecb.inUse == 0)
441			return net_acceptsocket;
442	return -1;
443}
444
445//=============================================================================
446
447int IPX_Read (int handle, byte *buf, int len, struct qsockaddr *addr)
448{
449	ECB		*ecb;
450	ipx_lowmem_buffer_t *rcvbuf;
451	int		copylen;
452
453	ProcessReadyList(handle);
454tryagain:
455	if (readlist[handle] == NULL)
456		return 0;
457	ecb = readlist[handle];
458	readlist[handle] = ecb->link;
459
460	if (ecb->completionCode != 0)
461	{
462		Con_Printf("Warning: IPX_Read error %02x\n", ecb->completionCode);
463		ecb->fragSize = sizeof(IPXheader) + sizeof(int) + NET_DATAGRAMSIZE;
464		IPX_ListenForPacket(ecb);
465		goto tryagain;
466	}
467
468	rcvbuf = (ipx_lowmem_buffer_t *)ecb;
469
470	// copy the data up to the buffer
471	copylen = ntohs(rcvbuf->header.length) - (sizeof(int) + sizeof(IPXheader));
472	if (len < copylen)
473		Sys_Error("IPX_Read: buffer too small (%d vs %d)\n", len, copylen);
474	Q_memcpy(buf, rcvbuf->data, copylen);
475
476	// fill in the addr if they want it
477	if (addr)
478	{
479		((struct sockaddr_ipx *)addr)->sipx_family = AF_NETWARE;
480		Q_memcpy(&((struct sockaddr_ipx *)addr)->sipx_addr, rcvbuf->header.source.network, sizeof(IPXaddr));
481		((struct sockaddr_ipx *)addr)->sipx_zero[0] = 0;
482		((struct sockaddr_ipx *)addr)->sipx_zero[1] = 0;
483	}
484
485	// update the send ecb's immediate address
486	Q_memcpy(lma->socketbuffer[handle][0].ecb.immediateAddress, rcvbuf->ecb.immediateAddress, 6);
487
488	// get this ecb listening again
489	rcvbuf->ecb.fragSize = sizeof(IPXheader) + sizeof(int) + NET_DATAGRAMSIZE;
490	IPX_ListenForPacket(&rcvbuf->ecb);
491	return copylen;
492}
493
494//=============================================================================
495
496int IPX_Broadcast (int handle, byte *buf, int len)
497{
498	struct sockaddr_ipx addr;
499	int ret;
500
501	Q_memset(addr.sipx_addr.network, 0x00, 4);
502	Q_memset(addr.sipx_addr.node, 0xff, 6);
503	addr.sipx_port = htons(net_hostport);
504	Q_memset(lma->socketbuffer[handle][0].ecb.immediateAddress, 0xff, 6);
505	ret = IPX_Write (handle, buf, len, (struct qsockaddr *)&addr);
506	return ret;
507}
508
509//=============================================================================
510
511int IPX_Write (int handle, byte *buf, int len, struct qsockaddr *addr)
512{
513	// has the previous send completed?
514	while (lma->socketbuffer[handle][0].ecb.inUse != 0)
515		IPX_RelinquishControl();
516
517	switch (lma->socketbuffer[handle][0].ecb.completionCode)
518	{
519		case 0x00: // success
520		case 0xfc: // request cancelled
521			break;
522
523		case 0xfd: // malformed packet
524		default:
525			Con_Printf("IPX driver send failure: %02x\n", lma->socketbuffer[handle][0].ecb.completionCode);
526			break;
527
528		case 0xfe: // packet undeliverable
529		case 0xff: // unable to send packet
530			Con_Printf("IPX lost route, trying to re-establish\n");
531
532			// look for a new route
533			if (IPX_GetLocalTarget (&lma->socketbuffer[handle][0].header.destination, lma->socketbuffer[handle][0].ecb.immediateAddress) != 0)
534				return -1;
535
536			// re-send the one that failed
537			regs.x.cs = ipx_cs;
538			regs.x.ip = ipx_ip;
539			regs.x.bx = IPX_SEND;
540			regs.x.es = ptr2real(&lma->socketbuffer[handle][0].ecb) >> 4;
541			regs.x.si = ptr2real(&lma->socketbuffer[handle][0].ecb) & 0xf;
542			__dpmi_simulate_real_mode_procedure_retf((__dpmi_regs *)&regs);
543
544			// report that we did not send the current one
545			return 0;
546	}
547
548	// ecb : length
549	lma->socketbuffer[handle][0].ecb.fragSize = sizeof(IPXheader) + sizeof(int) + len;
550
551	// ipx header : type
552	lma->socketbuffer[handle][0].header.type = PTYPE_IPX;
553
554	// ipx header : destination
555	Q_memcpy(&lma->socketbuffer[handle][0].header.destination, &((struct sockaddr_ipx *)addr)->sipx_addr, sizeof(IPXaddr));
556
557	// sequence number
558	lma->socketbuffer[handle][0].sequence = sequence[handle];
559	sequence[handle]++;
560
561	// copy down the data
562	Q_memcpy(lma->socketbuffer[handle][0].data, buf, len);
563
564	regs.x.cs = ipx_cs;
565	regs.x.ip = ipx_ip;
566	regs.x.bx = IPX_SEND;
567	regs.x.es = ptr2real(&lma->socketbuffer[handle][0].ecb) >> 4;
568	regs.x.si = ptr2real(&lma->socketbuffer[handle][0].ecb) & 0xf;
569	__dpmi_simulate_real_mode_procedure_retf((__dpmi_regs *)&regs);
570
571	return len;
572}
573
574//=============================================================================
575
576char *IPX_AddrToString (struct qsockaddr *addr)
577{
578	static char buf[28];
579
580	sprintf(buf, "%02x%02x%02x%02x:%02x%02x%02x%02x%02x%02x:%u",
581		((struct sockaddr_ipx *)addr)->sipx_addr.network[0],
582		((struct sockaddr_ipx *)addr)->sipx_addr.network[1],
583		((struct sockaddr_ipx *)addr)->sipx_addr.network[2],
584		((struct sockaddr_ipx *)addr)->sipx_addr.network[3],
585		((struct sockaddr_ipx *)addr)->sipx_addr.node[0],
586		((struct sockaddr_ipx *)addr)->sipx_addr.node[1],
587		((struct sockaddr_ipx *)addr)->sipx_addr.node[2],
588		((struct sockaddr_ipx *)addr)->sipx_addr.node[3],
589		((struct sockaddr_ipx *)addr)->sipx_addr.node[4],
590		((struct sockaddr_ipx *)addr)->sipx_addr.node[5],
591		ntohs(((struct sockaddr_ipx *)addr)->sipx_port)
592		);
593	return buf;
594}
595
596//=============================================================================
597
598int IPX_StringToAddr (char *string, struct qsockaddr *addr)
599{
600	int  val;
601	char buf[3];
602
603	buf[2] = 0;
604	Q_memset(addr, 0, sizeof(struct qsockaddr));
605	addr->sa_family = AF_NETWARE;
606
607#define DO(src,dest)	\
608	buf[0] = string[src];	\
609	buf[1] = string[src + 1];	\
610	if (sscanf (buf, "%x", &val) != 1)	\
611		return -1;	\
612	((struct sockaddr_ipx *)addr)->sipx_addr.dest = val
613
614	DO(0, network[0]);
615	DO(2, network[1]);
616	DO(4, network[2]);
617	DO(6, network[3]);
618	DO(9, node[0]);
619	DO(11, node[1]);
620	DO(13, node[2]);
621	DO(15, node[3]);
622	DO(17, node[4]);
623	DO(19, node[5]);
624#undef DO
625
626	sscanf (&string[22], "%u", &val);
627	((struct sockaddr_ipx *)addr)->sipx_port = htons(val);
628
629	return 0;
630}
631
632//=============================================================================
633
634int IPX_GetSocketAddr (int handle, struct qsockaddr *addr)
635{
636	Q_memset(addr, 0, sizeof(struct qsockaddr));
637	addr->sa_family = AF_NETWARE;
638	IPX_GetLocalAddress(&((struct sockaddr_ipx *)addr)->sipx_addr);
639	((struct sockaddr_ipx *)addr)->sipx_port = ipxsocket[handle];
640	return 0;
641}
642
643//=============================================================================
644
645int IPX_GetNameFromAddr (struct qsockaddr *addr, char *name)
646{
647	Q_strcpy(name, IPX_AddrToString(addr));
648	return 0;
649}
650
651//=============================================================================
652
653int IPX_GetAddrFromName (char *name, struct qsockaddr *addr)
654{
655	int n;
656	char buf[32];
657
658	n = Q_strlen(name);
659
660	if (n == 12)
661	{
662		sprintf(buf, "00000000:%s:%u", name, net_hostport);
663		return IPX_StringToAddr (buf, addr);
664	}
665	if (n == 21)
666	{
667		sprintf(buf, "%s:%u", name, net_hostport);
668		return IPX_StringToAddr (buf, addr);
669	}
670	if (n > 21 && n <= 27)
671		return IPX_StringToAddr (name, addr);
672
673	return -1;
674}
675
676//=============================================================================
677
678int IPX_AddrCompare (struct qsockaddr *addr1, struct qsockaddr *addr2)
679{
680	if (addr1->sa_family != addr2->sa_family)
681		return -1;
682
683	if(Q_memcmp(&((struct sockaddr_ipx *)addr1)->sipx_addr, &((struct sockaddr_ipx *)addr2)->sipx_addr, 10))
684		return -1;
685
686	if (((struct sockaddr_ipx *)addr1)->sipx_port != ((struct sockaddr_ipx *)addr2)->sipx_port)
687		return 1;
688
689	return 0;
690}
691
692//=============================================================================
693
694int IPX_GetSocketPort (struct qsockaddr *addr)
695{
696	return ntohs(((struct sockaddr_ipx *)addr)->sipx_port);
697}
698
699
700int IPX_SetSocketPort (struct qsockaddr *addr, int port)
701{
702	((struct sockaddr_ipx *)addr)->sipx_port = htons(port);
703	return 0;
704}
705
706//=============================================================================
707