add IPv4 multicasting support
[btb/d2x.git] / arch / linux / ipx_bsd.c
1 /* $Id: ipx_bsd.c,v 1.7 2003-10-12 09:17:47 btb Exp $ */
2 /*
3  *
4  * IPX driver using BSD style sockets
5  * Mostly taken from dosemu
6  *
7  */
8
9 #ifdef HAVE_CONFIG_H
10 #include <conf.h>
11 #endif
12
13 #include <stdio.h>
14 #include <sys/types.h>
15 #include <sys/socket.h>
16 #include <string.h>
17
18 #ifdef HAVE_NETIPX_IPX_H
19 #include <netipx/ipx.h>
20 #else
21 # include <linux/ipx.h>
22 # ifndef IPX_TYPE
23 #  define IPX_TYPE 1
24 # endif
25 #endif
26
27 #include <netinet/in.h>
28 #include <unistd.h>
29 #include <errno.h>
30
31 #include "pstypes.h"
32 #include "ipx_drv.h"
33 #include "ipx_bsd.h"
34
35 #ifndef DOSEMU
36 #include "mono.h"
37 #define n_printf(format, args...) mprintf((1, format, ## args))
38 #define enter_priv_on()
39 #define leave_priv_setting()
40 #endif
41
42 static int ipx_bsd_GetMyAddress(void)
43 {
44         int sock;
45         struct sockaddr_ipx ipxs;
46         struct sockaddr_ipx ipxs2;
47         int len;
48         int i;
49
50         sock=socket(AF_IPX,SOCK_DGRAM,PF_IPX);
51         if (sock == -1) {
52                 n_printf("IPX: could not open socket in GetMyAddress\n");
53                 return(-1);
54         }
55
56         /* bind this socket to network 0 */
57         ipxs.sipx_family=AF_IPX;
58 #ifdef IPX_MANUAL_ADDRESS
59         memcpy(&ipxs.sipx_network, ipx_MyAddress, 4);
60 #else
61         ipxs.sipx_network=0;
62 #endif
63         ipxs.sipx_port=0;
64
65         if (bind(sock,(struct sockaddr *)&ipxs,sizeof(ipxs)) == -1) {
66                 n_printf("IPX: could bind to network 0 in GetMyAddress\n");
67                 close( sock );
68                 return(-1);
69         }
70
71         len = sizeof(ipxs2);
72         if (getsockname(sock,(struct sockaddr *)&ipxs2,&len) < 0) {
73                 n_printf("IPX: could not get socket name in GetMyAddress\n");
74                 close( sock );
75                 return(-1);
76         }
77
78         memcpy(ipx_MyAddress, &ipxs2.sipx_network, 4);
79         for (i = 0; i < 6; i++) {
80                 ipx_MyAddress[4+i] = ipxs2.sipx_node[i];
81         }
82         close( sock );
83         return(0);
84 }
85
86 static int ipx_bsd_OpenSocket(ipx_socket_t *sk, int port)
87 {
88         int sock;           /* sock here means Linux socket handle */
89         int opt;
90         struct sockaddr_ipx ipxs;
91         int len;
92         struct sockaddr_ipx ipxs2;
93
94         /* DANG_FIXTHIS - kludge to support broken linux IPX stack */
95         /* need to convert dynamic socket open into a real socket number */
96         /*
97         if (port == 0) {
98                 n_printf("IPX: using socket %x\n", nextDynamicSocket);
99                 port = nextDynamicSocket++;
100         }
101         */
102         /* do a socket call, then bind to this port */
103         sock = socket(AF_IPX, SOCK_DGRAM, PF_IPX);
104         if (sock == -1) {
105                 n_printf("IPX: could not open IPX socket.\n");
106                 return -1;
107         }
108
109 #ifdef DOSEMU
110         opt = 1;
111         /* turn on socket debugging */
112         if (d.network) {
113                 enter_priv_on();
114                 if (setsockopt(sock, SOL_SOCKET, SO_DEBUG, &opt, sizeof(opt)) == -1) {
115                         leave_priv_setting();
116                         n_printf("IPX: could not set socket option for debugging.\n");
117                         return -1;
118                 }
119                 leave_priv_setting();
120         }
121 #endif
122         opt = 1;
123         /* Permit broadcast output */
124         enter_priv_on();
125         if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST,
126                                    &opt, sizeof(opt)) == -1) {
127                 leave_priv_setting();
128                 n_printf("IPX: could not set socket option for broadcast.\n");
129                 return -1;
130         }
131 #ifdef DOSEMU
132         /* allow setting the type field in the IPX header */
133         opt = 1;
134 #if 0 /* this seems to be wrong: IPX_TYPE can only be set on level SOL_IPX */
135         if (setsockopt(sock, SOL_SOCKET, IPX_TYPE, &opt, sizeof(opt)) == -1) {
136 #else
137         /* the socket _is_ an IPX socket, hence it first passes ipx_setsockopt()
138          * in file linux/net/ipx/af_ipx.c. This one handles SOL_IPX itself and
139          * passes SOL_SOCKET-levels down to sock_setsockopt().
140          * Hence I guess the below is correct (can somebody please verify this?)
141          * -- Hans, June 14 1997
142          */
143         if (setsockopt(sock, SOL_IPX, IPX_TYPE, &opt, sizeof(opt)) == -1) {
144 #endif
145                 leave_priv_setting();
146                 n_printf("IPX: could not set socket option for type.\n");
147                 return -1;
148         }
149 #endif
150         ipxs.sipx_family = AF_IPX;
151         ipxs.sipx_network = *((unsigned int *)&ipx_MyAddress[0]);
152         /*  ipxs.sipx_network = htonl(MyNetwork); */
153         bzero(ipxs.sipx_node, 6);       /* Please fill in my node name */
154         ipxs.sipx_port = htons(port);
155
156         /* now bind to this port */
157         if (bind(sock, (struct sockaddr *) &ipxs, sizeof(ipxs)) == -1) {
158                 n_printf("IPX: could not bind socket to address\n");
159                 close( sock );
160                 leave_priv_setting();
161                 return -1;
162         }
163
164         if( port==0 ) {
165                 len = sizeof(ipxs2);
166                 if (getsockname(sock,(struct sockaddr *)&ipxs2,&len) < 0) {
167                         n_printf("IPX: could not get socket name in IPXOpenSocket\n");
168                         close( sock );
169                         leave_priv_setting();
170                         return -1;
171                 } else {
172                         port = htons(ipxs2.sipx_port);
173                         n_printf("IPX: opened dynamic socket %04x\n", port);
174                 }
175         }
176         leave_priv_setting();
177         sk->fd = sock;
178         sk->socket = port;
179         return 0;
180 }
181
182 static void ipx_bsd_CloseSocket(ipx_socket_t *mysock) {
183         /* now close the file descriptor for the socket, and free it */
184         n_printf("IPX: closing file descriptor on socket %x\n", mysock->socket);
185         close(mysock->fd);
186 }
187
188 static int ipx_bsd_SendPacket(ipx_socket_t *mysock, IPXPacket_t *IPXHeader,
189                                       u_char *data, int dataLen) {
190         struct sockaddr_ipx ipxs;
191
192         ipxs.sipx_family = AF_IPX;
193         /* get destination address from IPX packet header */
194         memcpy(&ipxs.sipx_network, IPXHeader->Destination.Network, 4);
195         /* if destination address is 0, then send to my net */
196         if (ipxs.sipx_network == 0) {
197                 ipxs.sipx_network = *((unsigned int *)&ipx_MyAddress[0]);
198                 /*  ipxs.sipx_network = htonl(MyNetwork); */
199         }
200         memcpy(&ipxs.sipx_node, IPXHeader->Destination.Node, 6);
201         memcpy(&ipxs.sipx_port, IPXHeader->Destination.Socket, 2);
202         ipxs.sipx_type = IPXHeader->PacketType;
203         /*      ipxs.sipx_port=htons(0x452); */
204         return sendto(mysock->fd, data, dataLen, 0,
205                       (struct sockaddr *) &ipxs, sizeof(ipxs));
206 }
207
208 static int ipx_bsd_ReceivePacket(ipx_socket_t *s, char *buffer, int bufsize,
209                                      struct ipx_recv_data *rd) {
210         int sz, size;
211         struct sockaddr_ipx ipxs;
212
213         sz = sizeof(ipxs);
214         if ((size = recvfrom(s->fd, buffer, bufsize, 0,
215                              (struct sockaddr *) &ipxs, &sz)) <= 0)
216                 return size;
217         memcpy(rd->src_network, &ipxs.sipx_network, 4);
218         memcpy(rd->src_node, ipxs.sipx_node, 6);
219         rd->src_socket = ipxs.sipx_port;
220         rd->dst_socket = s->socket;
221         rd->pkt_type = ipxs.sipx_type;
222
223         return size;
224 }
225
226 struct ipx_driver ipx_bsd = {
227         ipx_bsd_GetMyAddress,
228         ipx_bsd_OpenSocket,
229         ipx_bsd_CloseSocket,
230         ipx_bsd_SendPacket,
231         ipx_bsd_ReceivePacket,
232         ipx_general_PacketReady,
233         NULL,   // InitNetgameAuxData
234         NULL,   // HandleNetgameAuxData
235         NULL,   // HandleLeaveGame
236         NULL    // SendGamePacket
237 };