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