]> icculus.org git repositories - btb/d2x.git/blob - arch/linux/ipx_mcast4.c
use the orientation parameter of g3_draw_bitmap
[btb/d2x.git] / arch / linux / ipx_mcast4.c
1
2 /*
3  *
4  * "ipx driver" for IPv4 multicasting
5  *
6  */
7
8 #ifdef HAVE_CONFIG_H
9 #include <conf.h>
10 #endif
11
12 #include <stdio.h>
13 #include <string.h>
14 #include <stdarg.h>
15 #ifdef HAVE_UNISTD_H
16 #include <unistd.h>
17 #endif
18 #include <stdlib.h>
19 #include <ctype.h>
20
21 #ifdef _WIN32
22 #include <winsock2.h>
23 #include <ws2tcpip.h>
24 #else
25 #include <arpa/inet.h>
26 #include <netinet/in.h>
27 #include <sys/socket.h>
28 #include <netdb.h>
29 #define closesocket(x) close(x)
30 #endif
31
32 #include "pstypes.h"
33 #include "ipx_mcast4.h"
34 #include "args.h"
35 #include "dxxerror.h"
36 #include "inferno.h"
37
38
39 //#define IPX_MCAST4_DEBUG
40
41 extern unsigned char ipx_MyAddress[10];
42
43 #define UDP_BASEPORT 28342
44 #define PORTSHIFT_TOLERANCE 0x100
45 #define MAX_PACKETSIZE 8192
46
47 /* OUR port. Can be changed by "@X[+=]..." argument (X is the shift value)
48  */
49 static int baseport=UDP_BASEPORT;
50
51 static struct in_addr game_addr;    // The game's multicast address
52
53 #define MSGHDR "IPX_mcast4: "
54
55 #ifdef IPX_MCAST4_DEBUG
56 static void msg(const char *fmt, ...)
57 {
58         va_list ap;
59
60         fputs(MSGHDR, stdout);
61         va_start(ap, fmt);
62         vprintf(fmt, ap);
63         va_end(ap);
64         putchar('\n');
65 }
66 #else
67 #define msg(...)
68 #endif
69
70 #define FAIL(...) do{ nm_messagebox("Error", 1, "Ok", __VA_ARGS__); return -1; } while (0)
71
72 #ifdef IPX_MCAST4_DEBUG
73 /* Dump raw form of IP address/port by fancy output to user
74  */
75 static void dumpraddr(unsigned char *a)
76 {
77         short port;
78
79         printf("[%u.%u.%u.%u]", a[0], a[1], a[2], a[3]);
80         port=(signed short)ntohs(*(unsigned short *)(a+4));
81         if (port) printf(":%+d",port);
82 }
83
84 /* Like dumpraddr() but for structure "sockaddr_in"
85  */
86 static void dumpaddr(struct sockaddr_in *sin)
87 {
88         unsigned short ports;
89         unsigned char qhbuf[8];
90
91         memcpy(qhbuf + 0, &sin->sin_addr, 4);
92         ports = htons(((short)ntohs(sin->sin_port)) - UDP_BASEPORT);
93         memcpy(qhbuf + 4, &ports, 2);
94         dumpraddr(qhbuf);
95 }
96 #endif
97
98 // The multicast address for Descent 2 game announcements.
99 // TODO: Pick a better address for this
100 #define DESCENT2_ANNOUNCE_ADDR inet_addr("239.255.1.2")
101
102 /* Open the socket and subscribe to the multicast session */
103 static int ipx_mcast4_OpenSocket(ipx_socket_t *sk, int port)
104 {
105         u_char loop;
106         struct ip_mreq mreq;
107         struct sockaddr_in sin;
108         int ttl = 128;
109
110         if((sk->fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
111         {
112                 sk->fd = -1;
113                 FAIL("socket() creation failed on port %d: %m", port);
114         }
115
116         // Bind to the port
117         sin.sin_family = AF_INET;
118         sin.sin_addr.s_addr = htonl(INADDR_ANY);
119         sin.sin_port = htons(baseport);
120         if (bind(sk->fd, (struct sockaddr *)&sin, sizeof(sin)))
121         {
122                 if (closesocket(sk->fd))
123                         msg("closesocket() failed during error recovery: %m");
124                 sk->fd = -1;
125                 FAIL("bind() to UDP port %d failed: %m", baseport);
126         }
127
128         // Set the TTL so the packets can get out of the local network.
129         if (setsockopt(sk->fd, IPPROTO_IP, IP_MULTICAST_TTL, (const void *)&ttl, sizeof(ttl)) < 0)
130                 FAIL("setsockopt() failed to set TTL to 128");
131
132         // Disable multicast loopback
133         loop = 0;
134         if (setsockopt(sk->fd, IPPROTO_IP, IP_MULTICAST_LOOP, (const void *)&loop, sizeof(loop)) < 0)
135                 FAIL("setsockopt() failed to disable multicast loopback: %m");
136
137         // Subscribe to the game announcement address
138         memset(&mreq, 0, sizeof(mreq));
139         mreq.imr_multiaddr.s_addr = DESCENT2_ANNOUNCE_ADDR;
140         mreq.imr_interface.s_addr = INADDR_ANY;
141         if (setsockopt(sk->fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const void *)&mreq, sizeof(mreq)) < 0)
142                 FAIL("setsockopt() failed to subscribe to the game announcement multicast group");
143
144         // We're not subscribed to a game address yet
145         game_addr.s_addr = 0;
146
147         sk->socket = port;
148         return 0;
149 }
150
151 static void ipx_mcast4_CloseSocket(ipx_socket_t *sk)
152 {
153         if(closesocket(sk->fd) < 0)
154                 msg("Close failed");
155         sk->fd = -1;
156 }
157
158 static int ipx_mcast4_SendPacket(ipx_socket_t *sk, IPXPacket_t *IPXHeader, u_char *data, int dataLen)
159 {
160         struct sockaddr_in toaddr;
161         int i;
162
163         msg("SendPacket enter, dataLen=%d", dataLen);
164
165         if(dataLen < 0 || dataLen > MAX_PACKETSIZE)
166                 return -1;
167
168         toaddr.sin_family = AF_INET;
169         memcpy(&toaddr.sin_addr, IPXHeader->Destination.Node + 0, 4);
170         //toaddr.sin_port = htons(((short)ntohs(*(unsigned short *)(IPXHeader->Destination.Node + 4))) + UDP_BASEPORT);
171         // For now, just use the same port for everything
172         toaddr.sin_port = htons(UDP_BASEPORT);
173
174         // If it's the broadcast address, then we want to send it to the
175         // GAME ANNOUNCEMENT address.
176         // Data to be sent to the GAME has the destination already set by
177         // ipx_mcast4_SendGamePacket
178         if(toaddr.sin_addr.s_addr == INADDR_BROADCAST)
179                 toaddr.sin_addr.s_addr = DESCENT2_ANNOUNCE_ADDR;
180
181 #ifdef IPX_MCAST4_DEBUG
182         printf(MSGHDR "sendto((%d),Node=[4] %02X %02X,Socket=%02X %02X,s_port=%u,",
183                dataLen,
184                IPXHeader->Destination.Node[4], IPXHeader->Destination.Node[5],
185                IPXHeader->Destination.Socket[0], IPXHeader->Destination.Socket[1],
186                ntohs(toaddr.sin_port));
187         dumpaddr(&toaddr);
188         puts(").");
189 #endif
190
191         i = (int)sendto(sk->fd, (const void *)data, dataLen, 0, (struct sockaddr *)&toaddr, sizeof(toaddr));
192         return i;
193 }
194
195 static int ipx_mcast4_ReceivePacket(ipx_socket_t *sk, char *outbuf, int outbufsize, struct ipx_recv_data *rd)
196 {
197         int size;
198         struct sockaddr_in fromaddr;
199         socklen_t fromaddrsize = sizeof(fromaddr);
200
201         if ((size = (int)recvfrom(sk->fd, outbuf, outbufsize, 0, (struct sockaddr*)&fromaddr, &fromaddrsize)) < 0)
202                 return -1;
203
204 #ifdef IPX_MCAST4_DEBUG
205         printf(MSGHDR "Got packet from ");
206         dumpaddr(&fromaddr);
207         puts("");
208 #endif
209
210         // We have the packet, now fill out the receive data.
211         memset(rd, 0, sizeof(*rd));
212         memcpy(rd->src_node, &fromaddr.sin_addr, 4);
213         // TODO: Include the port like in ipx_udp.c
214         rd->pkt_type = 0;
215
216         return size;
217 }
218
219 /* Handle the netgame aux data
220  * Byte 0 is the protocol version number.
221  * Bytes 1-4 are the IPv4 multicast session to join, in network byte order.
222  */
223 static int ipx_mcast4_HandleNetgameAuxData(ipx_socket_t *sk, const u_char buf[NETGAME_AUX_SIZE])
224 {
225         // Extract the multicast session and subscribe to it.  We should
226         // now be getting packets intended for the players of this game.
227
228         // Note that we stay subscribed to the game announcement session,
229         // so we can reply to game info requests
230         struct ip_mreq mreq;
231         int ttl = 128;
232
233         // Check the protocol version
234         if(buf[0] != IPX_MCAST4_VERSION)
235         {
236                 FAIL("mcast4 protocol\nversion mismatch!\nGame version is %02x,\nour version is %02x", buf[0], IPX_MCAST4_VERSION);
237         }
238
239         // Get the multicast session
240         memcpy(&game_addr, buf + 1, sizeof(game_addr));
241
242 #ifdef IPX_MCAST4_DEBUG
243         {
244                 struct sockaddr_in tmpaddr;
245                 tmpaddr.sin_addr = game_addr;
246                 tmpaddr.sin_port = 0;
247
248                 printf("Handling netgame aux data: Subscribing to ");
249                 dumpaddr(&tmpaddr);
250                 puts("");
251         }
252 #endif
253
254         // Set the TTL so the packets can get out of the local network.
255         if(setsockopt(sk->fd, IPPROTO_IP, IP_MULTICAST_TTL, (const void *)&ttl, sizeof(ttl)) < 0)
256                 FAIL("setsockopt() failed to set TTL to 128");
257
258         memset(&mreq, 0, sizeof(mreq));
259         mreq.imr_multiaddr = game_addr;
260         mreq.imr_interface.s_addr = INADDR_ANY;
261
262         if(setsockopt(sk->fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const void *)&mreq, sizeof(mreq)) < 0)
263                 FAIL("setsockopt() failed to subscribe to the game group");
264
265         return 0;
266 }
267
268 /* Create the netgame aux data.
269  * Byte 0 is the protcol version number.
270  * Bytes 1-4 hold the IPv4 multicast session for the game.
271  */
272 static void ipx_mcast4_InitNetgameAuxData(ipx_socket_t *sk, u_char buf[NETGAME_AUX_SIZE])
273 {
274         char addr[16];
275         Assert(game_addr.s_addr == 0);
276
277         // The first byte is the version number
278         buf[0] = IPX_MCAST4_VERSION;
279
280         // Generate a random session
281 //      game_addr = inet_makeaddr(239*256 + 255, d_rand() % 0xFFFF);
282         snprintf(addr, sizeof(addr)-1, "%i.%i.%i.%i", 239, 255, d_rand() % 0xFF, d_rand() % 0xFF);
283         game_addr.s_addr = inet_addr(addr);
284         memcpy(buf + 1, &game_addr, sizeof(game_addr));
285
286         // Since we're obviously the hosting machine, subscribe to this address
287         ipx_mcast4_HandleNetgameAuxData(sk, buf);
288 }
289
290 static void ipx_mcast4_HandleLeaveGame(ipx_socket_t *sk)
291 {
292         // We left the game, so unsubscribe from its multicast session
293         struct ip_mreq mreq;
294
295         Assert(game_addr.s_addr != 0);
296
297 #ifdef IPX_MCAST4_DEBUG
298         printf("Unsubscribing from game's multicast group: ");
299         dumpraddr(&game_addr.s_addr);
300         printf("\n");
301 #endif
302
303         mreq.imr_multiaddr = game_addr;
304         mreq.imr_interface.s_addr = INADDR_ANY;
305         if(setsockopt(sk->fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, (const void *)&mreq, sizeof(mreq)) < 0)
306                 msg("setsockopt() failed unsubscribing from previous group!");
307         game_addr.s_addr = 0;
308 }
309
310 // Send a packet to every member of the game.  We can just multicast it here.
311 static int ipx_mcast4_SendGamePacket(ipx_socket_t *sk, ubyte *data, int dataLen)
312 {
313         struct sockaddr_in toaddr;
314         int i;
315
316         memset(&toaddr, 0, sizeof(toaddr));
317         toaddr.sin_family = AF_INET;
318         toaddr.sin_addr = game_addr;
319         toaddr.sin_port = htons(UDP_BASEPORT);
320
321         msg("ipx_mcast4_SendGamePacket");
322
323         i = (int)sendto(sk->fd, (const void *)data, dataLen, 0, (struct sockaddr *)&toaddr, sizeof(toaddr));
324
325         return i;
326 }
327
328 // Pull this in from ipx_udp.c since it's the same for us.
329 extern int ipx_udp_GetMyAddress(void);
330
331 struct ipx_driver ipx_mcast4 = {
332         ipx_udp_GetMyAddress,
333         ipx_mcast4_OpenSocket,
334         ipx_mcast4_CloseSocket,
335         ipx_mcast4_SendPacket,
336         ipx_mcast4_ReceivePacket,
337         ipx_general_PacketReady,
338         ipx_mcast4_InitNetgameAuxData,
339         ipx_mcast4_HandleNetgameAuxData,
340         ipx_mcast4_HandleLeaveGame,
341         ipx_mcast4_SendGamePacket
342 };