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