1 /* Unix/Cygwin network code for Xconq kernel.
2 Copyright (C) 1996, 1997, 1999 Stanley T. Shebs.
3
4 Xconq is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
7 any later version. See the file COPYING. */
8
9 /* Note that this file does not include all Xconq .h files, since
10 it may be used with auxiliary programs. */
11
12 #include "config.h"
13 #include "misc.h"
14 #include "dir.h"
15 #include "lisp.h"
16 #include "module.h"
17 #include "system.h"
18
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <signal.h>
22
23 #if (defined (UNIX) || defined (MAC) || defined (__CYGWIN32__) || defined (__MINGW32__))
24 #include <unistd.h>
25 #endif
26
27 #if (defined (UNIX) || defined (__CYGWIN32__) || defined (__MINGW32__))
28 #include <sys/file.h>
29 #include <sys/stat.h>
30 #endif
31
32 #if (defined (UNIX) || defined (__CYGWIN32__) || defined (MAC))
33 #include <netinet/in.h>
34 #include <sys/socket.h>
35 #include <netdb.h>
36 #endif
37
38 #if (defined (UNIX) || defined (__CYGWIN32__))
39 #include <sys/ioctl.h>
40 #include <pwd.h>
41 #endif
42
43 #if (defined (UNIX) || defined (MAC))
44 # include <netinet/tcp.h>
45 #endif
46
47 #if defined(_MSC_VER)
48 #include <io.h>
49 #endif
50
51 #if (defined (WIN32) && !defined (__CYGWIN32__) && !defined(__MWERKS__))
52
53 #include <winsock2.h>
54
55 /* Needed by MS Visual C. */
sleep(unsigned int seconds)56 static unsigned int sleep(unsigned int seconds)
57 {
58 Sleep(seconds * 1000);
59 return 0;
60 }
61
62 #endif
63
64 #if (defined (WIN32) && !defined (__CYGWIN32__))
65
66 #define ECONNREFUSED WSAECONNREFUSED
67
68 static int wsa_initialized = FALSE;
69
70 /* Initialize WinSock requesting Version 2.0 */
wsa_open()71 static int wsa_open ()
72 {
73 WORD wsaVersion;
74 WSADATA wsaData;
75
76 wsaVersion = MAKEWORD (2, 0);
77 return WSAStartup (wsaVersion, &wsaData);
78 }
79
80 /* Close WinSock */
wsa_close()81 static int wsa_close ()
82 {
83 return WSACleanup ();
84 }
85
86 #endif /* WIN32 */
87
88 /* Handle deficiencies of strict ANSI compliance. */
89 #ifndef O_RDONLY
90 #define O_RDONLY 0
91 #endif
92 #ifndef O_WRONLY
93 #define O_WRONLY 1
94 #endif
95 #ifndef O_RDWR
96 #define O_RDWR 2
97 #endif
98
99 static int accept_remote_connection(void);
100 static int wait_for(int timeout);
101
102 /* The file descriptor that a host makes available for other programs
103 to connect into. */
104
105 int public_fd;
106
107 /* Flag indicating whether the public file descriptor is currently
108 valid. */
109
110 int public_fd_valid;
111
112 /* The next id for a remote program. 0 means invalid, 1 is reserved for
113 the first host of a game, so we start at 2. */
114
115 int nextrid = 2;
116
117 /* Array of file descriptors used with connections to remote programs,
118 indexed by the remote programs' ids. */
119
120 int remote_fd[100];
121
122 int fd_valid[100];
123
124 namespace Xconq {
125 int host_is_localhost = FALSE;
126 }
127
128 /* Given a specification of how to open a remote connection (such as a
129 hostname:port), and a host/join flag, set up a connection. Return
130 a number for the method being used, or 0 for failure. */
131
132 int
open_remote_connection(char * methodname,int willhost)133 open_remote_connection(char *methodname, int willhost)
134 {
135 int port, i, tmp_fd, tmp;
136 char *port_str, hostname[100];
137 struct hostent *hostent;
138 struct sockaddr_in sockaddr;
139 struct protoent *protoent;
140
141 if (!strchr(methodname, ':')) {
142 /* Try opening a serial port. */
143 #if 0
144 struct sgttyb sg;
145 #endif
146 remote_fd[2] = open(methodname, O_RDWR);
147 fd_valid[2] = TRUE;
148 if (remote_fd[2] < 0) {
149 init_warning("Could not open remote device");
150 return 0;
151 }
152 #if 0
153 ioctl(remote_fd[2], TIOCGETP, &sg);
154 sg.sg_flags = RAW;
155 ioctl(remote_fd[2], TIOCSETP, &sg);
156 #endif
157 } else {
158 #if (defined (WIN32) && !defined (__CYGWIN32__))
159 /* Initialize winsock if neccessary. Testing is superfluous since this
160 seems to be called once only. Anyway we probably want some initialize
161 and shutdown hooks either in the network (socket.c) or the os glue
162 (unix.c and friends) implementation. */
163 if (!wsa_initialized)
164 {
165 if (wsa_open () != 0)
166 {
167 init_warning("Can't initialize winsock");
168 return 0;
169 }
170 wsa_initialized = TRUE;
171 }
172 #endif
173 /* Using TCP; extract the port number. */
174 port_str = strchr(methodname, ':');
175 port = atoi(port_str + 1);
176
177 tmp_fd = socket(PF_INET, SOCK_STREAM, 0);
178 if (tmp_fd < 0) {
179 init_warning("Can't open socket");
180 return 0;
181 }
182 /* Allow rapid reuse of this port. */
183 tmp = 1;
184 setsockopt (tmp_fd, SOL_SOCKET, SO_REUSEADDR, (char *) &tmp,
185 sizeof(tmp));
186
187 if (willhost) {
188 sockaddr.sin_family = PF_INET;
189 sockaddr.sin_port = htons(port);
190 sockaddr.sin_addr.s_addr = INADDR_ANY;
191
192 public_fd = tmp_fd;
193 public_fd_valid = TRUE;
194
195 if (bind(public_fd, (struct sockaddr *) &sockaddr,
196 sizeof(sockaddr))) {
197 init_warning("Can't bind address");
198 return 0;
199 }
200 if (listen (public_fd, 1)) {
201 init_warning("Can't listen");
202 return 0;
203 }
204 /* Our public socket is now available to receive
205 connection attempts. These will appear as input on
206 public_fd, and then accept_remote_connection will
207 finish making the link. */
208
209 return 1;
210 } else {
211 /* We're trying to join an existing game. */
212 tmp = min (port_str - methodname, (int) sizeof hostname - 1);
213 strncpy (hostname, methodname, tmp);
214 hostname[tmp] = '\0';
215 if (!strncmp(hostname, "localhost", 9))
216 Xconq::host_is_localhost = TRUE;
217 hostent = gethostbyname (hostname);
218 if (!hostent) {
219 init_warning("%s: unknown host", hostname);
220 return 0;
221 }
222 /* Try making the connection several times. */
223 for (i = 1; i <= 5; i++) {
224 tmp_fd = socket(PF_INET, SOCK_STREAM, 0);
225 if (tmp_fd < 0) {
226 init_warning("can't create a socket");
227 return 0;
228 }
229
230 /* Allow rapid reuse of this port. */
231 tmp = 1;
232 setsockopt(tmp_fd, SOL_SOCKET, SO_REUSEADDR, (char *) &tmp,
233 sizeof(tmp));
234
235 /* Enable TCP keep alive process. */
236 tmp = 1;
237 setsockopt(tmp_fd, SOL_SOCKET, SO_KEEPALIVE, (char *) &tmp,
238 sizeof(tmp));
239
240 sockaddr.sin_family = PF_INET;
241 sockaddr.sin_port = htons(port);
242 memcpy(&sockaddr.sin_addr.s_addr, hostent->h_addr,
243 sizeof (struct in_addr));
244
245 if (connect(tmp_fd, (struct sockaddr *) &sockaddr,
246 sizeof(sockaddr)) == 0)
247 /* Success! Break out of the retry loop. */
248 break;
249
250 close(tmp_fd);
251 tmp_fd = -1;
252
253 /* We retry for ECONNREFUSED because that is often a
254 temporary condition, which happens when the server
255 is being restarted. */
256 if (errno != ECONNREFUSED)
257 return 0;
258 /* Wait a moment before trying again. */
259 /* (should mention to the user) */
260 sleep(1);
261 }
262
263 /* We know the host will be rid 1. */
264 remote_fd[1] = tmp_fd;
265 fd_valid[1] = TRUE;
266
267 protoent = getprotobyname ("tcp");
268 if (!protoent)
269 return 0;
270
271 tmp = 1;
272 if (setsockopt(remote_fd[1], protoent->p_proto, TCP_NODELAY,
273 (char *) &tmp, sizeof(tmp)))
274 return 0;
275 #ifdef SIGPIPE
276 signal(SIGPIPE, SIG_IGN);
277 #endif
278 }
279 return 1;
280 }
281 return 0;
282 }
283
284 /* If a program has attempted to connect to our published socket, accept
285 the connection and add the program to our merry little band. */
286
287 static int
accept_remote_connection(void)288 accept_remote_connection(void)
289 {
290 struct hostent *hostent = NULL;
291 struct sockaddr_in sockaddr;
292 /* (probably _MSC_VER needs to be in the conditional below. If so, then
293 we should rewrite as:
294 (defined (WIN32))
295 */
296 #if (defined (WIN32) && (defined (__MWERKS__) || defined (__MINGW32__) || defined (__CYGWIN__)))
297 int tmp;
298 #else
299 unsigned int tmp;
300 #endif
301 char hostname[100];
302 struct protoent *protoent;
303 int new_fd;
304 int rid;
305
306 tmp = sizeof (sockaddr);
307 new_fd = accept(public_fd, (struct sockaddr *) &sockaddr, &tmp);
308 if (new_fd == -1) {
309 init_warning("Accept failed");
310 return 0;
311 }
312 /* Record the new file descriptor as a valid remote program. */
313 rid = nextrid++;
314 remote_fd[rid] = new_fd;
315 fd_valid[rid] = TRUE;
316
317 /* This takes more than one minute on the mac and always
318 * fails, so we skip it to speed up things. */
319
320 #ifndef MAC
321 hostent = gethostbyaddr((char*)&sockaddr.sin_addr,
322 sizeof(sizeof(sockaddr.sin_addr)),
323 AF_INET);
324 #endif
325
326 if (hostent) {
327 memcpy(hostname, hostent->h_name, hostent->h_length);
328 hostname[hostent->h_length] = '\0';
329 } else {
330 strcpy(hostname, "unknown");
331 }
332
333 protoent = getprotobyname("tcp");
334 if (!protoent)
335 init_warning("getprotobyname");
336
337 /* Enable TCP keep alive process. */
338 tmp = 1;
339 setsockopt(remote_fd[rid], SOL_SOCKET, SO_KEEPALIVE,
340 (char *) &tmp, sizeof(tmp));
341
342 /* Tell TCP not to delay small packets. This greatly speeds up
343 interactive response. */
344 tmp = 1;
345 setsockopt(remote_fd[rid], protoent->p_proto, TCP_NODELAY,
346 (char *) &tmp, sizeof(tmp));
347
348 /* If we don't do this, then program simply exits when the remote
349 side dies. */
350 #ifdef SIGPIPE
351 signal(SIGPIPE, SIG_IGN);
352 #endif
353 #if defined(FASYNC)
354 fcntl(remote_fd[rid], F_SETFL, FASYNC);
355 #endif
356
357 Dprintf("Remote connection from %s (#%d)\n", hostname, rid);
358 return 1;
359 }
360
361 /* Send a given string out to a given program. Return when the whole
362 string has been delivered. */
363
364 void
low_send(int rid,char * buf)365 low_send(int rid, char *buf)
366 {
367 int fd, len, cc;
368
369 fd = (rid > 0 ? remote_fd[rid] : remote_fd[1]);
370 Dprintf("low_send %d (fd %d) \"%s\"\n", rid, fd, buf);
371 len = strlen(buf);
372 while (len > 0) {
373 #if (defined (WIN32) && !defined (__CYGWIN32__))
374 cc = send(fd, buf, len, 0);
375 #else
376 cc = write(fd, buf, len);
377 #endif /* WIN32 */
378
379 if (cc < 0)
380 return;
381 len -= cc;
382 buf += cc;
383 }
384 }
385
386 /* Wait for input, for a given period of time. Return an rid or a
387 special code (negative values). */
388
389 static int
wait_for(int timeout)390 wait_for(int timeout)
391 {
392 int rid, numfds, maxfd;
393 struct timeval tv;
394 fd_set readfds, exceptfds;
395
396 FD_ZERO (&readfds);
397 FD_ZERO (&exceptfds);
398
399 tv.tv_sec = timeout;
400 tv.tv_usec = 0;
401
402 maxfd = -2;
403 if (public_fd_valid) {
404 FD_SET(public_fd, &readfds);
405 maxfd = max(public_fd, maxfd);
406 }
407 for (rid = 1; rid < nextrid; ++rid) {
408 if (fd_valid[rid]) {
409 FD_SET(remote_fd[rid], &readfds);
410 FD_SET(remote_fd[rid], &exceptfds);
411 maxfd = max(remote_fd[rid], maxfd);
412 }
413 }
414 /* (should test if maxfd < 0 still) */
415
416 while (1) {
417 numfds = select(maxfd+1, &readfds, 0, &exceptfds,
418 (timeout >= 0 ? &tv : 0));
419
420 if (numfds <= 0) {
421 if (numfds == 0)
422 return -1;
423 else if (errno == EINTR)
424 continue;
425 else
426 return -2; /* Got an error from select or poll */
427 }
428 if (public_fd_valid) {
429 if (FD_ISSET(public_fd, &readfds)) {
430 return 0;
431 }
432 }
433 for (rid = 1; rid < nextrid; ++rid) {
434 if (fd_valid[rid]) {
435 if (FD_ISSET(remote_fd[rid], &readfds)) {
436 return rid;
437 }
438 }
439 }
440 /* (should never happen) */
441 return -1;
442 }
443 }
444
445 /* For a given amount of time, listen for any data from our other
446 programs. */
447
448 int
low_receive(int * ridp,char * buf,int maxchars,int timeout)449 low_receive(int *ridp, char *buf, int maxchars, int timeout)
450 {
451 int status, rid, fd, n;
452 time_t start_time, now;
453
454 time(&start_time);
455
456 while (1) {
457 status = wait_for(timeout);
458 if (status < 0) {
459 return FALSE;
460 } else if (status == 0) {
461 accept_remote_connection();
462 continue;
463 }
464 /* We have data from one of the other programs. */
465 rid = status;
466 fd = remote_fd[rid] ;
467 #if (defined (WIN32) && !defined (__CYGWIN32__))
468 n = recv(fd, buf, maxchars, 0);
469 #else
470 n = read(fd, buf, maxchars);
471 #endif /* WIN32 */
472
473 if (n > 0) {
474 /* Make a string out of the received data. */
475 buf[n] = '\0';
476 *ridp = rid;
477 return TRUE;
478 } else if (timeout == 0) {
479 /* We were just sniffing for anything laying around, and
480 there wasn't anything, so return emptyhanded. */
481 return FALSE;
482 } else if (timeout == -1) {
483 /* Go around again. */
484 } else {
485 time(&now);
486 if (now > start_time + timeout) {
487 Dprintf("%ul > %ul + %d\n", now, start_time, timeout);
488 return FALSE /* timed out */;
489 }
490 }
491 }
492 }
493
494 void
close_remote_connection(int rid)495 close_remote_connection(int rid)
496 {
497 /* If we're closing before rids assigned, assume that an arg of 0
498 means we were trying to join and failed. */
499 if (rid == 0)
500 rid = 1;
501
502 if (!fd_valid[rid])
503 return;
504
505 close(remote_fd[rid]);
506 fd_valid[rid] = FALSE;
507 }
508