Visual C, PocketPC fixes
[btb/d2x.git] / arch / win32 / ipx_udp.c
1 /* $Id: ipx_udp.c,v 1.4 2003-11-26 12:26:25 btb Exp $ */
2 /*
3  *
4  * IPX driver for native Linux TCP/IP networking (UDP implementation)
5  *   Version 0.99.2
6  * Contact Jan [Lace] Kratochvil <short@ucw.cz> for assistance
7  * (no "It somehow doesn't work! What should I do?" complaints, please)
8  * Special thanks to Vojtech Pavlik <vojtech@ucw.cz> for testing.
9  *
10  * Also you may see KIX - KIX kix out KaliNix (in Linux-Linux only):
11  *   http://atrey.karlin.mff.cuni.cz/~short/sw/kix.c.gz
12  *
13  * Primarily based on ipx_kali.c - "IPX driver for KaliNix interface"
14  *   which is probably mostly by Jay Cotton <jay@kali.net>.
15  * Parts shamelessly stolen from my KIX v0.99.2 and GGN v0.100
16  *
17  * Changes:
18  * --------
19  * 0.99.1 - now the default broadcast list also contains all point-to-point
20  *          links with their destination peer addresses
21  * 0.99.2 - commented a bit :-) 
22  *        - now adds to broadcast list each host it gets some packet from
23  *          which is already not covered by local physical ethernet broadcast
24  *        - implemented short-signature packet format
25  *        - compatibility mode for old D1X releases due to the previous bullet
26  *
27  * Configuration:
28  * --------------
29  * No network server software is needed, neither KIX nor KaliNIX.
30  *
31  *    NOTE: with the change to allow the user to choose the network
32  *    driver from the game menu, the following is not anymore precise:
33  *    the command line argument "-udp" is only necessary to supply udp
34  *    options!
35  *
36  * Add command line argument "-udp". In default operation D1X will send
37  * broadcasts too all the local interfaces found. But you may also use
38  * additional parameter specified after "-udp" to modify the default
39  * broadcasting style and UDP port numbers binding:
40  *
41  * ./d1x -udp [@SHIFT]=HOST_LIST   Broadcast ONLY to HOST_LIST
42  * ./d1x -udp [@SHIFT]+HOST_LIST   Broadcast both to local ifaces & to HOST_LIST
43  *
44  * HOST_LIST is a comma (',') separated list of HOSTs:
45  * HOST is an IPv4 address (so-called quad like 192.168.1.2) or regular hostname
46  * HOST can also be in form 'address:SHIFT'
47  * SHIFT sets the UDP port base offset (e.g. +2), can be used to run multiple
48  *       clients on one host simultaneously. This SHIFT has nothing to do
49  *       with the dynamic-sockets (PgUP/PgDOWN) option in Descent, it's another
50  *       higher-level differentiation option.
51  *
52  * Examples:
53  * ---------
54  * ./d1x -udp
55  *  - Run D1X to participate in normal local network (Linux only, of course)
56  *
57  * ./d1x -udp @1=localhost:2 & ./d1x -udp @2=localhost:1
58  *  - Run two clients simultaneously fighting each other (only each other)
59  *
60  * ./d1x -udp =192.168.4.255
61  *  - Run distant Descent which will participate with remote network
62  *    192.168.4.0 with netmask 255.255.255.0 (broadcast has 192.168.4.255)
63  *  - You'll have to also setup hosts in that network accordingly:
64  * ./d1x -udp +UPPER_DISTANT_MACHINE_NAME
65  *
66  * Have fun!
67  *
68  */
69
70 #ifdef HAVE_CONFIG_H
71 #include <conf.h>
72 #endif
73
74 #include <stdio.h>
75 #include <string.h>
76 #ifndef _MSC_VER
77 #include <unistd.h>
78 #endif
79 #include <stdarg.h>
80 #include <stdlib.h>
81 #include <ctype.h>
82 #include <malloc.h>
83
84 #include <winsock2.h>
85
86 #include "ipx_drv.h"
87 #include "args.h"
88
89 extern unsigned char ipx_MyAddress[10];
90
91 // #define UDPDEBUG
92
93 #define UDP_BASEPORT 28342
94 #define PORTSHIFT_TOLERANCE 0x100
95 #define MAX_PACKETSIZE 8192
96
97 /* Packet format: first is the signature { 0xD1,'X' } which can be also
98  * { 'D','1','X','u','d','p'} for old-fashioned packets.
99  * Then follows virtual socket number (as changed during PgDOWN/PgUP)
100  * in network-byte-order as 2 bytes (u_short). After such 4/8 byte header
101  * follows raw data as communicated with D1X network core functions.
102  */
103
104 // Length HAS TO BE 6!
105 #define D1Xudp "D1Xudp"
106 // Length HAS TO BE 2!
107 #define D1Xid "\xD1X"
108
109 static int open_sockets = 0;
110 static int dynamic_socket = 0x401;
111 static const int val_one=1;
112
113 /* OUR port. Can be changed by "@X[+=]..." argument (X is the shift value)
114  */
115
116 static int baseport=UDP_BASEPORT;
117
118 /* Have we some old D1X in network and have we to maintain compatibility?
119  * FIXME: Following scenario is braindead:
120  *                                           A  (NEW) , B  (OLD) , C  (NEW)
121  *   host A) We start new D1X.               A-newcomm, B-none   , C-none
122  *   host B) We start OLD D1X.               A-newcomm, B-oldcomm, C-none
123  *   Now host A hears host B and switches:   A-oldcomm, B-oldcomm, C-none
124  *   host C) We start new D1X.               A-oldcomm, B-oldcomm, C-newcomm
125  *   Now host C hears host A/B and switches: A-oldcomm, B-oldcomm, C-oldcomm
126  *   Now host B finishes:                    A-oldcomm, B-none   , C-oldcomm
127  *
128  * But right now we have hosts A and C, both new code equipped but
129  * communicating wastefully by the OLD protocol! Bummer.
130  */
131
132 static char compatibility=0;
133
134 static int have_empty_address() {
135         int i;
136
137         for (i = 0; i < 10 && !ipx_MyAddress[i]; i++) ;
138         return i == 10;
139 }
140
141 #define MSGHDR "IPX_udp: "
142
143 static void msg(const char *fmt,...)
144 {
145         va_list ap;
146
147         fputs(MSGHDR,stdout);
148         va_start(ap,fmt);
149         vprintf(fmt,ap);
150         va_end(ap);
151         putchar('\n');
152 }
153
154 static void chk(void *p)
155 {
156         if (p) return;
157         msg("FATAL: Virtual memory exhausted!");
158         exit(EXIT_FAILURE);
159 }
160
161 #ifdef _MSC_VER
162 #define FAIL Warning
163 #else
164 #define FAIL(m...) do { msg(#m); return -1; } while (0)
165 #endif
166
167 /* Find as much as MAX_BRDINTERFACES during local iface autoconfiguration.
168  * Note that more interfaces can be added during manual configuration
169  * or host-received-packet autoconfiguration
170  */
171
172 #define MAX_BRDINTERFACES 16
173
174 /* We require the interface to be UP and RUNNING to accept it.
175  */
176
177 #define IF_REQFLAGS (IFF_UP|IFF_RUNNING)
178
179 /* We reject any interfaces declared as LOOPBACK type.
180  */
181 #define IF_NOTFLAGS (IFF_LOOPBACK)
182
183 static struct sockaddr_in *broads,broadmasks[MAX_BRDINTERFACES];
184 static int broadnum,masksnum,broadsize;
185
186 /* We'll check whether the "broads" array of destination addresses is now
187  * full and so needs expanding.
188  */
189
190 static void chkbroadsize(void)
191 {
192         if (broadnum<broadsize) return;
193         broadsize=broadsize?broadsize*2:8;
194         chk(broads=realloc(broads,sizeof(*broads)*broadsize));
195 }
196
197 /* This function is called during init and has to grab all system interfaces
198  * and collect their broadcast-destination addresses (and their netmasks).
199  * Typically it founds only one ethernet card and so returns address in
200  * the style "192.168.1.255" with netmask "255.255.255.0".
201  * Broadcast addresses are filled into "broads", netmasks to "broadmasks".
202  */
203
204 /* Stolen from my GGN */
205 static int addiflist(void)
206 {
207 #if 0
208         unsigned cnt=MAX_BRDINTERFACES,i,j;
209         WSADATA info;
210         INTERFACE_INFO *ifo;
211         SOCKET sock;
212         struct sockaddr_in *sinp,*sinmp;
213
214         free(broads);
215         if ((sock=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP))<0)
216                 FAIL("Creating socket() failure during broadcast detection: %m");
217
218 #ifdef SIOCGIFCOUNT
219         if (ioctl(sock,SIOCGIFCOUNT,&cnt))
220                 { /* msg("Getting iterface count error: %m"); */ }
221         else
222                 cnt=cnt*2+2;
223 #endif
224
225         memset(&ifo[0], 0, sizeof(ifo));
226         chk(ifo = alloca(cnt * sizeof(INTERFACE_INFO)));
227
228         if (wsaioctl(sock, SIO_GET_INTERFACE_LIST, NULL, 0, &ifo[0], cnt * sizeof(INTERFACE_INFO), &br, NULL, NULL)) != 0) {
229                 closesocket(sock);
230                 FAIL("ioctl(SIOCGIFCONF) failure during broadcast detection: %m");
231                 }
232 #if 0
233         cnt=ifconf.ifc_len/sizeof(struct ifreq);
234 #endif
235         chk(broads=malloc(cnt*sizeof(*broads)));
236         broadsize=cnt;
237         for (i=j=0;i<cnt;i++) {
238                 if (ioctl(sock,SIOCGIFFLAGS,ifconf.ifc_req+i)) {
239                         closesocket(sock);
240                         FAIL("ioctl(udp,\"%s\",SIOCGIFFLAGS) error: %m",ifconf.ifc_req[i].ifr_name);
241                         }
242                 if (((ifconf.ifc_req[i].ifr_flags&IF_REQFLAGS)!=IF_REQFLAGS)||
243                                  (ifconf.ifc_req[i].ifr_flags&IF_NOTFLAGS))
244                         continue;
245                 if (ioctl(sock,(ifconf.ifc_req[i].ifr_flags&IFF_BROADCAST?SIOCGIFBRDADDR:SIOCGIFDSTADDR),ifconf.ifc_req+i)) {
246                         closesocket(sock);
247                         FAIL("ioctl(udp,\"%s\",SIOCGIF{DST/BRD}ADDR) error: %m",ifconf.ifc_req[i].ifr_name);
248                         }
249
250                 sinp = (struct sockaddr_in *)&ifconf.ifc_req[i].ifr_broadaddr;
251 #if 0 // old, not portable code
252                 sinmp = (struct sockaddr_in *)&ifconf.ifc_req[i].ifr_netmask;
253 #else // portable code
254                 if (ioctl(sock, SIOCGIFNETMASK, ifconf.ifc_req+i)) {
255                         closesocket(sock);
256                         FAIL("ioctl(udp,\"%s\",SIOCGIFNETMASK) error: %m", ifconf.ifc_req[i].ifr_name);
257                 }
258                 sinmp = (struct sockaddr_in *)&ifconf.ifc_req[i].ifr_addr;
259 #endif
260                 if (sinp->sin_family!=AF_INET || sinmp->sin_family!=AF_INET) continue;
261                 broads[j]=*sinp;
262                 broads[j].sin_port=UDP_BASEPORT; //FIXME: No possibility to override from cmdline
263                 broadmasks[j]=*sinmp;
264                 j++;
265                 }
266         broadnum=j;
267         masksnum=j;
268 #endif
269         return(0);
270 }
271
272 #define addreq(a,b) ((a).sin_port==(b).sin_port&&(a).sin_addr.s_addr==(b).sin_addr.s_addr)
273
274 /* Previous function addiflist() can (and probably will) report multiple
275  * same addresses. On some Linux boxes is present both device "eth0" and
276  * "dummy0" with the same IP addreesses - we'll filter it here.
277  */
278
279 static void unifyiflist(void)
280 {
281 int d=0,s,i;
282
283         for (s=0;s<broadnum;s++) {
284                 for (i=0;i<s;i++)
285                         if (addreq(broads[s],broads[i])) break;
286                 if (i>=s) broads[d++]=broads[s];
287                 }
288         broadnum=d;
289 }
290
291 static unsigned char qhbuf[6];
292
293 /* Parse PORTSHIFT numeric parameter
294  */
295
296 static void portshift(const char *cs)
297 {
298 long port;
299 unsigned short ports=0;
300
301         port=atol(cs);
302         if (port<-PORTSHIFT_TOLERANCE || port>+PORTSHIFT_TOLERANCE)
303                 msg("Invalid portshift in \"%s\", tolerance is +/-%d",cs,PORTSHIFT_TOLERANCE);
304         else ports=htons(port);
305         memcpy(qhbuf+4,&ports,2);
306 }
307
308 /* Do hostname resolve on name "buf" and return the address in buffer "qhbuf".
309  */
310 static unsigned char *queryhost(char *buf)
311 {
312 struct hostent *he;
313 char *s;
314 char c=0;
315
316         if ((s=strrchr(buf,':'))) {
317           c=*s;
318           *s='\0';
319           portshift(s+1);
320           }
321         else memset(qhbuf+4,0,2);
322         he=gethostbyname((char *)buf);
323         if (s) *s=c;
324         if (!he) {
325                 msg("Error resolving my hostname \"%s\"",buf);
326                 return(NULL);
327                 }
328         if (he->h_addrtype!=AF_INET || he->h_length!=4) {
329           msg("Error parsing resolved my hostname \"%s\"",buf);
330                 return(NULL);
331                 }
332         if (!*he->h_addr_list) {
333           msg("My resolved hostname \"%s\" address list empty",buf);
334                 return(NULL);
335                 }
336         memcpy(qhbuf,(*he->h_addr_list),4);
337         return(qhbuf);
338 }
339
340 /* Dump raw form of IP address/port by fancy output to user
341  */
342 static void dumpraddr(unsigned char *a)
343 {
344 short port;
345         printf("[%u.%u.%u.%u]",a[0],a[1],a[2],a[3]);
346         port=(signed short)ntohs(*(unsigned short *)(a+4));
347         if (port) printf(":%+d",port);
348 }
349
350 /* Like dumpraddr() but for structure "sockaddr_in"
351  */
352
353 static void dumpaddr(struct sockaddr_in *sin)
354 {
355 unsigned short ports;
356
357         memcpy(qhbuf+0,&sin->sin_addr,4);
358         ports=htons(((short)ntohs(sin->sin_port))-UDP_BASEPORT);
359         memcpy(qhbuf+4,&ports,2);
360         dumpraddr(qhbuf);
361 }
362
363 /* Startup... Uninteresting parsing...
364  */
365
366 int ipx_udp_GetMyAddress(void) {
367
368 char buf[256];
369 int i;
370 char *s,*s2,*ns;
371
372         if (!have_empty_address())
373                 return 0;
374
375         if (!((i=FindArg("-udp")) && (s=Args[i+1]) && (*s=='=' || *s=='+' || *s=='@'))) s=NULL;
376
377         if (gethostname(buf,sizeof(buf))) FAIL("Error getting my hostname");
378         if (!(queryhost(buf))) FAIL("Querying my own hostname \"%s\"",buf);
379
380         if (s) while (*s=='@') {
381                 portshift(++s);
382                 while (isdigit(*s)) s++;
383                 }
384
385         memset(ipx_MyAddress+0,0,4);
386         memcpy(ipx_MyAddress+4,qhbuf,6);
387         baseport+=(short)ntohs(*(unsigned short *)(qhbuf+4));
388
389         if (!s || (s && !*s)) addiflist();
390         else {
391                 if (*s=='+') addiflist();
392                 s++;
393                 for (;;) {
394 struct sockaddr_in *sin;
395                         while (isspace(*s)) s++;
396                         if (!*s) break;
397                         for (s2=s;*s2 && *s2!=',';s2++);
398                         chk(ns=malloc(s2-s+1));
399                         memcpy(ns,s,s2-s);
400                         ns[s2-s]='\0';
401                         if (!queryhost(ns)) msg("Ignored broadcast-destination \"%s\" as being invalid",ns);
402                         free(ns);
403                         chkbroadsize();
404                         sin=broads+(broadnum++);
405                         sin->sin_family=AF_INET;
406                         memcpy(&sin->sin_addr,qhbuf+0,4);
407                         sin->sin_port=htons(((short)ntohs(*(unsigned short *)(qhbuf+4)))+UDP_BASEPORT);
408                         s=s2+(*s2==',');
409                         }
410                 }
411
412         unifyiflist();
413
414         printf(MSGHDR "Using TCP/IP address ");
415         dumpraddr(ipx_MyAddress+4);
416         putchar('\n');
417         if (broadnum) {
418                 printf(MSGHDR "Using %u broadcast-dest%s:",broadnum,(broadnum==1?"":"s"));
419                 for (i=0;i<broadnum;i++) {
420                         putchar(' ');
421                         dumpaddr(broads+i);
422                         }
423                 putchar('\n');
424                 }
425
426         return 0;
427 }
428
429 /* We should probably avoid such insanity here as during PgUP/PgDOWN
430  * (virtual port number change) we wastefully destroy/create the same
431  * socket here (binded always to "baseport"). FIXME.
432  * "open_sockets" can be only 0 or 1 anyway.
433  */
434
435 static int ipx_udp_OpenSocket(ipx_socket_t *sk, int port) {
436         struct sockaddr_in sin;
437
438         if (!open_sockets)
439                 if (have_empty_address())
440                 if (ipx_udp_GetMyAddress() < 0) FAIL("Error getting my address");
441
442         msg("OpenSocket on D1X socket port %d",port);
443
444         if (!port)
445                 port = dynamic_socket++;
446
447         if ((sk->fd = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP)) < 0) {
448                 sk->fd = -1;
449                 FAIL("socket() creation failed on port %d: %m",port);
450                 }
451         if (setsockopt(sk->fd, SOL_SOCKET, SO_BROADCAST, (char *)&val_one, sizeof(val_one))) {
452                 if (closesocket(sk->fd)) msg("closesocket() failed during error recovery: %m");
453                 sk->fd=-1;
454                 FAIL("setsockopt(SO_BROADCAST) failed: %m");
455                 }
456         sin.sin_family=AF_INET;
457         sin.sin_addr.s_addr=htonl(INADDR_ANY);
458         sin.sin_port=htons(baseport);
459         if (bind(sk->fd,(struct sockaddr *)&sin,sizeof(sin))) {
460                 if (closesocket(sk->fd)) msg("closesocket() failed during error recovery: %m");
461                 sk->fd=-1;
462                 FAIL("bind() to UDP port %d failed: %m",baseport);
463                 }
464
465         open_sockets++;
466         sk->socket = port;
467         return 0;
468 }
469
470 /* The same comment as in previous "ipx_udp_OpenSocket"...
471  */
472
473 static void ipx_udp_CloseSocket(ipx_socket_t *mysock) {
474         if (!open_sockets) {
475                 msg("close w/o open");
476                 return;
477         }
478         msg("CloseSocket on D1X socket port %d",mysock->socket);
479         if (closesocket(mysock->fd))
480                 msg("closesocket() failed on CloseSocket D1X socket port %d: %m",mysock->socket);
481         mysock->fd=-1;
482         if (--open_sockets) {
483                 msg("(closesocket) %d sockets left", open_sockets);
484                 return;
485         }
486 }
487
488 /* Here we'll send the packet to our host. If it is unicast packet, send
489  * it to IP address/port as retrieved from IPX address. Otherwise (broadcast)
490  * we'll repeat the same data to each host in our broadcasting list.
491  */
492
493 static int ipx_udp_SendPacket(ipx_socket_t *mysock, IPXPacket_t *IPXHeader,
494  u_char *data, int dataLen) {
495         struct sockaddr_in toaddr,*dest;
496         int i=dataLen;
497         int bcast;
498         char *buf;
499
500 #ifdef UDPDEBUG
501         msg("SendPacket enter, dataLen=%d",dataLen);
502 #endif
503         if (dataLen<0 || dataLen>MAX_PACKETSIZE) return -1;
504         chk(buf=alloca(8+dataLen));
505         if (compatibility) memcpy(buf+0,D1Xudp,6),buf+=6;
506         else               memcpy(buf+0,D1Xid ,2),buf+=2;
507         memcpy(buf+0,IPXHeader->Destination.Socket,2);
508         memcpy(buf+2,data,dataLen);
509  
510         toaddr.sin_family=AF_INET;
511         memcpy(&toaddr.sin_addr,IPXHeader->Destination.Node+0,4);
512         toaddr.sin_port=htons(((short)ntohs(*(unsigned short *)(IPXHeader->Destination.Node+4)))+UDP_BASEPORT);
513
514         for (bcast=(toaddr.sin_addr.s_addr==htonl(INADDR_BROADCAST)?0:-1);bcast<broadnum;bcast++) {
515                 if (bcast>=0) dest=broads+bcast;
516                 else dest=&toaddr;
517         
518 #ifdef UDPDEBUG
519                 printf(MSGHDR "sendto((%d),Node=[4] %02X %02X,Socket=%02X %02X,s_port=%u,",
520                         dataLen,
521                         IPXHeader->Destination.Node  [4],IPXHeader->Destination.Node  [5],
522                         IPXHeader->Destination.Socket[0],IPXHeader->Destination.Socket[1],
523                         ntohs(dest->sin_port));
524                 dumpaddr(dest);
525                 puts(").");
526 #endif
527                 i=sendto(mysock->fd,buf-(compatibility?6:2),(compatibility?8:4)+dataLen,
528                         0,(struct sockaddr *)dest,sizeof(*dest));
529                 if (bcast==-1) return (i<8?-1:i-8);
530                 }
531         return(dataLen);
532 }
533
534 /* Here we will receive new packet to the given buffer. Both formats of packets
535  * are supported, we fallback to old format when first obsolete packet is seen.
536  * If the (valid) packet is received from unknown host, we will add it to our
537  * broadcasting list. FIXME: For now such autoconfigured hosts are NEVER removed.
538  */
539
540 static int ipx_udp_ReceivePacket(ipx_socket_t *s, char *outbuf, int outbufsize, 
541  struct ipx_recv_data *rd) {
542         int size;
543         struct sockaddr_in fromaddr;
544         int fromaddrsize=sizeof(fromaddr);
545         unsigned short ports;
546         size_t offs;
547         int i;
548
549         if ((size=recvfrom(s->fd,outbuf,outbufsize,0,(struct sockaddr *)&fromaddr,&fromaddrsize))<0)
550                 return -1;
551 #ifdef UDPDEBUG
552         printf(MSGHDR "recvfrom((%d-8=%d),",size,size-8);
553         dumpaddr(&fromaddr);
554         puts(").");
555 #endif
556         if (fromaddr.sin_family!=AF_INET) return -1;
557         if (size<4) return -1;
558         if (memcmp(outbuf+0,D1Xid,2)) {
559                 if (size<8 || memcmp(outbuf+0,D1Xudp,6)) return -1;
560                 if (!compatibility) {
561                         compatibility=1;
562                         fputs(MSGHDR "Received obsolete packet from ",stdout);
563                         dumpaddr(&fromaddr);
564                         puts(", upgrade that machine.\n" MSGHDR "Turning on compatibility mode...");
565                         }
566                 offs=6;
567                 }
568         else offs=2;
569
570         /* Lace: (dst_socket & src_socket) should be network-byte-order by comment in include/ipx_drv.h */
571         /*       This behaviour presented here is broken. It is not used anywhere, so why bother? */
572         rd->src_socket = ntohs(*(unsigned short *)(outbuf+offs));
573         if (rd->src_socket != s->socket) {
574 #ifdef UDPDEBUG
575                 msg(" - pkt was dropped (dst=%d,my=%d)",rd->src_socket,s->socket);
576 #endif
577                 return -1;
578                 }
579         rd->dst_socket = s->socket;
580
581         // check if we already have sender of this packet in broadcast list
582         for (i=0;i<broadnum;i++) {
583                 if (i>=masksnum) {
584                         if (addreq(fromaddr,broads[i])) break; 
585                         }
586                 else {
587                         if (fromaddr.sin_port==broads[i].sin_port
588                         &&( fromaddr.sin_addr.s_addr & broadmasks[i].sin_addr.s_addr)
589                         ==(broads[i].sin_addr.s_addr & broadmasks[i].sin_addr.s_addr)) break;
590                         }
591                 }
592         if (i>=broadnum) { // we don't have sender of this packet in our broadcast list
593                 chkbroadsize();
594                 broads[broadnum++]=fromaddr;
595                 fputs(MSGHDR "Adding host ",stdout);
596                 dumpaddr(&fromaddr);
597                 puts(" to broadcasting address list");
598                 }
599
600         memmove(outbuf,outbuf+offs+2,size-(offs+2));
601         size-=offs+2;
602
603         memcpy(rd->src_node+0,&fromaddr.sin_addr,4);
604         ports=htons(ntohs(fromaddr.sin_port)-UDP_BASEPORT);
605         memcpy(rd->src_node+4,&ports,2);
606         memset(rd->src_network, 0, 4);
607         rd->pkt_type = 0;
608 #ifdef UDPDEBUG
609         printf(MSGHDR "ReceivePacket: size=%d,from=",size);
610         dumpraddr(rd->src_node);
611         putchar('\n');
612 #endif
613
614         return size;
615 }
616
617 struct ipx_driver ipx_udp = {
618         ipx_udp_GetMyAddress,
619         ipx_udp_OpenSocket,
620         ipx_udp_CloseSocket,
621         ipx_udp_SendPacket,
622         ipx_udp_ReceivePacket,
623         ipx_general_PacketReady,
624         NULL,   // InitNetgameAuxData
625         NULL,   // HandleNetgameAuxData
626         NULL,   // HandleLeaveGame
627         NULL    // SendGamePacke
628 };