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