1 /*
2  * XPilot NG, a multiplayer space war game.
3  *
4  * Copyright (C) 1991-2001 by
5  *
6  *      Bj�rn Stabell        <bjoern@xpilot.org>
7  *      Ken Ronny Schouten   <ken@xpilot.org>
8  *      Bert Gijsbers        <bert@xpilot.org>
9  *      Dick Balaska         <dick@xpilot.org>
10  *
11  * Copyright (C) 2001 Uoti Urpala    <uau@users.sourceforge.net>
12  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 2 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26  */
27 
28 #include "xpclient.h"
29 
30 
31 #define MAX_LINE	MSG_LEN	/* should not be smaller than MSG_LEN */
32 
33 
34 extern int		dgram_one_socket;	/* from datagram.c */
35 
36 
37 /*
38  * just like fgets() but strips newlines like gets().
39  */
get_line(char * buf,int len,FILE * stream)40 static char* get_line(char* buf, int len, FILE* stream)
41 {
42     char		*nl;
43 
44     if (fgets(buf, len, stream)) {
45 	nl = strchr(buf, '\n');
46 	if (nl) {
47 	    *nl = '\0';		/* strip newline */
48 	    if (nl > buf && nl[-1] == '\r')
49 		nl[-1] = '\0';
50 	}
51     }
52     return buf;
53 }
54 
55 
56 /*
57  * Replace control characters with a space.
58  */
Clean_string(char * buf)59 static void Clean_string(char *buf)
60 {
61     char		*str;
62     int			c;
63 
64     for (str = buf; *str; str++) {
65 	c = (*str & 0xFF);
66 	if (!isascii(c) || iscntrl(c)) {
67 	    if (!strchr("\r\n", c))
68 		*str = ' ';
69 	}
70     }
71 }
72 
73 
Get_contact_message(sockbuf_t * sbuf,const char * contact_server,Connect_param_t * conpar)74 static int Get_contact_message(sockbuf_t *sbuf,
75 			       const char *contact_server,
76 			       Connect_param_t *conpar)
77 {
78     int			len;
79     int			server_version;
80     unsigned		magic;
81     unsigned char	reply_to, status;
82     int			readable = 0;
83 
84     sock_set_timeout(&sbuf->sock, 2, 0);
85     while (readable == false && sock_readable(&sbuf->sock) > 0) {
86 
87 	Sockbuf_clear(sbuf);
88 	len = sock_receive_any(&sbuf->sock, sbuf->buf, sbuf->size);
89 	if (len <= 0) {
90 	    if (len == 0)
91 		continue;
92 	    xpprintf("Error from sock_receive_any, contact message failed.\n");
93 	    /* exit(1);  no good since meta gui. */
94 	    return 0;
95 	}
96 	sbuf->len = len;
97 
98 	/*
99 	 * Get server's host and port.
100 	 */
101 	strlcpy(conpar->server_addr, sock_get_last_addr(&sbuf->sock),
102 		sizeof(conpar->server_addr));
103 	conpar->server_port = sock_get_last_port(&sbuf->sock);
104 	/*
105 	 * If the name of the server to contact is the same as its
106 	 * IP address then we don't want to do a reverse lookup.
107 	 * Doing a reverse lookup may result in a long and annoying delay.
108 	 */
109 	if (!strcmp(conpar->server_addr, contact_server))
110 	    strlcpy(conpar->server_name, conpar->server_addr,
111 		    sizeof(conpar->server_name));
112 	else
113 	    strlcpy(conpar->server_name, sock_get_last_name(&sbuf->sock),
114 		    sizeof(conpar->server_name));
115 
116 	if (Packet_scanf(sbuf, "%u%c%c", &magic, &reply_to, &status) <= 0)
117 	    warn("Incomplete contact reply message (%d)", len);
118 	else if ((magic & 0xFFFF) != (MAGIC & 0xFFFF))
119 	    warn("Bad magic on contact message (0x%x).", magic);
120 	else {
121 	    server_version = MAGIC2VERSION(magic);
122 	    if (!((server_version >= MIN_SERVER_VERSION &&
123 		   server_version <= MAX_SERVER_VERSION) ||
124 		  (server_version >= MIN_OLD_SERVER_VERSION &&
125 		   server_version <= MAX_OLD_SERVER_VERSION))) {
126 		warn("Incompatible version with server %s.",
127 		     conpar->server_name);
128 		warn("We run version %04x, while server is running %04x.",
129 		     MY_VERSION, server_version);
130 		if ((MY_VERSION >> 4) < (server_version >> 4))
131 		    warn("Time for us to upgrade?");
132 		readable = 2;
133 	    } else {
134 		/*
135 		 * Found one which we can talk to.
136 		 */
137 		xpinfo("Using protocol version 0x%04x.", server_version);
138 		conpar->server_version = server_version;
139 		readable = 1;
140 	    }
141 	}
142     }
143 
144     return readable;
145 }
146 
147 
148 
Get_reply_message(sockbuf_t * ibuf,Connect_param_t * conpar)149 static int Get_reply_message(sockbuf_t *ibuf,
150 			     Connect_param_t *conpar)
151 {
152     int			len;
153     unsigned		magic;
154 
155 
156     if (sock_readable(&ibuf->sock)) {
157 	Sockbuf_clear(ibuf);
158 	if ((len = sock_read(&ibuf->sock, ibuf->buf, ibuf->size)) == -1) {
159 	    error("Can't read reply message from %s/%d",
160 		  conpar->server_addr, conpar->server_port);
161 	    exit(1);
162 	}
163 
164 	ibuf->len = len;
165 	if (Packet_scanf(ibuf, "%u", &magic) <= 0) {
166 	    warn("Incomplete reply packet (%d)", len);
167 	    return 0;
168 	}
169 
170 	if ((magic & 0xFFFF) != (MAGIC & 0xFFFF)) {
171 	    warn("Wrong MAGIC in reply pack (0x%x).", magic);
172 	    return 0;
173 	}
174 
175 	if (MAGIC2VERSION(magic) != conpar->server_version) {
176 	    printf("Incompatible version with server on %s.\n",
177 		    conpar->server_name);
178 	    printf("We run version %04x, while server is running %04x.\n",
179 		   MY_VERSION, MAGIC2VERSION(magic));
180 	    return 0;
181 	}
182 
183 	return len;
184     }
185 
186     return 0;
187 }
188 
189 
190 
Command_help(void)191 static void Command_help(void)
192 {
193     printf("Supported commands are:\n"
194 	   "H/?  -   Help - this text.\n"
195 	   "N    -   Next server, skip this one.\n"
196 	   "S    -   list Status.\n"
197 	   "T    -   set Team.\n"
198 	   "Q    -   Quit.\n");
199     printf("K    -   Kick a player.                (only owner)\n"
200 	   "M    -   send a Message.               (only owner)\n"
201 	   "L    -   Lock/unLock server access.    (only owner)\n"
202 	   "D(*) -   shutDown/cancel shutDown.     (only owner)\n"
203 	   "O    -   Modify a server option.       (only owner)\n"
204 	   "V    -   View the server options.\n"
205 	   "J(&) or just Return enters the game.\n");
206     printf("(*) If you don't specify any delay, you will signal that\n"
207 	   "    the server should stop an ongoing shutdown.\n"
208 	   "(&) You may specify a team number after the J.\n");
209 }
210 
211 
212 
213 /*
214  * This is the routine that interactively (if not auto_connect) prompts
215  * the user on his/her next action.  Returns true if player joined this
216  * server (connected to server), or false if the player wants to have a
217  * look at the next server.
218  */
Process_commands(sockbuf_t * ibuf,int auto_connect,int list_servers,int auto_shutdown,char * shutdown_reason,Connect_param_t * conpar)219 static bool Process_commands(sockbuf_t *ibuf,
220 			     int auto_connect, int list_servers,
221 			     int auto_shutdown, char *shutdown_reason,
222 			     Connect_param_t *conpar)
223 {
224     int i, len, retries, delay, success, cmd_credentials = 0, max_replies;
225     char c, status, reply_to, linebuf[MAX_LINE];
226     unsigned short port, qpos;
227     bool has_credentials = false, privileged_cmd;
228     long key = 0;
229     time_t qsent = 0;
230     static char localhost[] = "127.0.0.1";
231 
232 #ifdef _WINDOWS
233     auto_connect = TRUE;	/* I want to join */
234     auto_shutdown = FALSE;
235 #endif
236 
237     if (auto_connect && !list_servers && !auto_shutdown)
238 	xpprintf("*** Connected to %s\n", conpar->server_name);
239 
240     for (;;) {
241 
242 	max_replies = 1;
243 
244 	/*
245 	 * Now, what do you want from the server?
246 	 */
247 	if (cmd_credentials) {
248 	    c = cmd_credentials;
249 	    cmd_credentials = 0;
250 	}
251 	else if (!auto_connect) {
252 	    printf("*** Server on %s. Enter command> ", conpar->server_name);
253 
254 	    get_line(linebuf, MAX_LINE, stdin);
255 	    if (feof(stdin)) {
256 		puts("");
257 		c = 'Q';
258 	    } else {
259 		c = linebuf[0];
260 		if (c == '\0')
261 		    c = 'J';
262 	    }
263 	    CAP_LETTER(c);
264 	} else {
265 	    if (list_servers)
266 		c = 'S';
267 	    else if (auto_shutdown)
268 		c = 'D';
269 	    else
270 		c = 'J';
271 	    linebuf[0] = linebuf[1] = '\0';
272 	}
273 
274 	/*
275 	 * For each command to the server create a new socket and bind
276 	 * the socket to the server's address and destination port.
277 	 * This assures us that we only get replies to the last command sent.
278 	 */
279 	if (ibuf->sock.fd != SOCK_FD_INVALID) {
280 	    close_dgram_socket(&ibuf->sock);
281 	    ibuf->sock.fd = SOCK_FD_INVALID;
282 	}
283 
284 	privileged_cmd = (strchr("DKLMO", c) != NULL) ? true : false;
285 	if (privileged_cmd) {
286 	    if (!has_credentials) {
287 		success = create_dgram_addr_socket(
288 		    &ibuf->sock, conpar->server_addr, 0);
289 		if (success == SOCK_IS_ERROR) {
290 		    printf("Server %s is not local, "
291 			   "privileged command not possible.\n",
292 			   conpar->server_addr);
293 		    continue;
294 		}
295 		close_dgram_socket(&ibuf->sock);
296 	    }
297 	    if ((success = create_dgram_addr_socket(
298 		&ibuf->sock, localhost, 0)) == SOCK_IS_ERROR) {
299 		error("Could not create localhost socket");
300 		exit(1);
301 	    }
302 	    if (sock_connect(&ibuf->sock, localhost, conpar->server_port)
303 		== SOCK_IS_ERROR) {
304 		error("Can't connect to local server %s on port %d\n",
305 		      localhost, conpar->server_port);
306 		return false;
307 	    }
308 	} else {
309 	    if ((success = create_dgram_socket(&ibuf->sock, 0))
310 		== SOCK_IS_ERROR) {
311 		error("Could not create socket");
312 		exit(1);
313 	    }
314 	    if (sock_connect(
315 		&ibuf->sock, conpar->server_addr, conpar->server_port)
316 		== SOCK_IS_ERROR
317 		&& !dgram_one_socket) {
318 		error("Can't connect to server %s on port %d\n",
319 		      conpar->server_addr, conpar->server_port);
320 		return false;
321 	    }
322 	}
323 
324 	Sockbuf_clear(ibuf);
325 	Packet_printf(ibuf, "%u%s%hu", VERSION2MAGIC(conpar->server_version),
326 		      conpar->user_name, sock_get_port(&ibuf->sock));
327 
328 	if (privileged_cmd && !has_credentials)
329 	    Packet_printf(ibuf, "%c%ld", CREDENTIALS_pack, 0L);
330 	else {
331 
332 	    switch (c) {
333 
334 		/*
335 		 * Owner only commands:
336 		 */
337 
338 	    case 'K':
339 		printf("Enter name of victim: ");
340 		fflush(stdout);
341 		if (!get_line(linebuf, MAX_LINE, stdin)) {
342 		    printf("Nothing changed.\n");
343 		    continue;
344 		}
345 		linebuf[MAX_NAME_LEN - 1] = '\0';
346 		Packet_printf(ibuf, "%c%ld%s", KICK_PLAYER_pack, key, linebuf);
347 		break;
348 
349 	    case 'M':				/* Send a message to server. */
350 		printf("Enter message: ");
351 		fflush(stdout);
352 		if (!get_line(linebuf, MAX_LINE, stdin) || !linebuf[0]) {
353 		    printf("No message sent.\n");
354 		    continue;
355 		}
356 		linebuf[MAX_CHARS - 1] = '\0';
357 		Packet_printf(ibuf, "%c%ld%s", MESSAGE_pack, key, linebuf);
358 		break;
359 
360 	    case 'L':				/* Lock the game. */
361 		Packet_printf(ibuf, "%c%ld", LOCK_GAME_pack, key);
362 		break;
363 
364 	    case 'D':				/* Shutdown */
365 		if (!auto_shutdown) {
366 		    printf("Enter delay in seconds or return for cancel: ");
367 		    get_line(linebuf, MAX_LINE, stdin);
368 		    /*
369 		     * No argument = cancel shutdown = arg_int=0
370 		     */
371 		    if (sscanf(linebuf, "%d", &delay) <= 0)
372 			delay = 0;
373 		    else
374 			if (delay <= 0)
375 			    delay = 1;
376 
377 		    printf("Enter reason: ");
378 		    get_line(linebuf, MAX_LINE, stdin);
379 		} else {
380 		    strlcpy(linebuf, shutdown_reason, sizeof(linebuf));
381 		    delay = 60;
382 		}
383 		linebuf[MAX_CHARS - 1] = '\0';
384 		Packet_printf(ibuf, "%c%ld%d%s",
385 			      SHUTDOWN_pack, key, delay, linebuf);
386 		break;
387 
388 	    case 'O':				/* Tune an option. */
389 		printf("Enter option: ");
390 		fflush(stdout);
391 		if (!get_line(linebuf, MAX_LINE, stdin)
392 		    || (len=strlen(linebuf)) == 0) {
393 		    printf("Nothing changed.\n");
394 		    continue;
395 		}
396 		printf("Enter new value for %s: ", linebuf);
397 		fflush(stdout);
398 		strcat(linebuf, ":"); len++;
399 		if (!get_line(&linebuf[len], MAX_LINE-len, stdin)
400 		    || linebuf[len] == '\0') {
401 		    printf("Nothing changed.\n");
402 		    continue;
403 		}
404 		printf("option \"%s\"\n", linebuf); fflush(stdout);
405 		Packet_printf(ibuf, "%c%ld%S", OPTION_TUNE_pack, key, linebuf);
406 		break;
407 
408 		/*
409 		 * Public commands:
410 		 */
411 
412 	    case 'J':				/* Trying to enter game. */
413 		if (linebuf[1] == '0') {
414 		    printf("Team '0' is reserved for robots.");
415 		    conpar->team = TEAM_NOT_SET;
416 		}
417 		else if (linebuf[1] > '0' && linebuf[1] <= '9') {
418 		    conpar->team = linebuf[1] - '0';
419 		    printf("Joining team %d\n", conpar->team);
420 		}
421 		else if (linebuf[1] == '-') {
422 		    conpar->team = TEAM_NOT_SET;
423 		    printf("Team set to unspecified\n");
424 		}
425 		else if (linebuf[1] != '\0')
426 		    conpar->team = TEAM_NOT_SET;
427 
428 		Packet_printf(ibuf, "%c%s%s%s%d", ENTER_QUEUE_pack,
429 			      conpar->nick_name, conpar->disp_name,
430 			      conpar->host_name, conpar->team);
431 		time(&qsent);
432 		break;
433 
434 	    case 'S':				/* Report status. */
435 		Packet_printf(ibuf, "%c", REPORT_STATUS_pack);
436 		break;
437 
438 	    case 'V':				/* View options. */
439 		Packet_printf(ibuf, "%c", OPTION_LIST_pack);
440 		max_replies = 5;
441 		break;
442 
443 		/*
444 		 * User interface commands:
445 		 */
446 
447 	    case 'N':				/* Next server. */
448 		return false;
449 
450 	    case 'T':				/* Set team. */
451 		printf("Enter team: ");
452 		fflush(stdout);
453 		if (!get_line(linebuf, MAX_LINE, stdin)
454 		    || (len = strlen(linebuf)) == 0)
455 		    printf("Nothing changed.\n");
456 		else {
457 		    int newteam;
458 		    if (sscanf(linebuf, " %d", &newteam) != 1)
459 			printf("Invalid team specification: %s.\n", linebuf);
460 		    else if (newteam >= 0 && newteam <= 9) {
461 			conpar->team = newteam;
462 			printf("Team set to %d\n", conpar->team);
463 		    } else {
464 			conpar->team = TEAM_NOT_SET;
465 			printf("Team set to unspecified\n");
466 		    }
467 		}
468 		continue;
469 
470 	    case 'Q':
471 		exit (0);
472 		break;
473 
474 	    case '?':
475 	    case 'H':				/* Help. */
476 	    default:
477 		Command_help();
478 
479 		/*
480 		 * Next command.
481 		 */
482 		continue;
483 	    }
484 	}
485 
486 	retries = (c == 'J' || c == 'S') ? 2 : 0;
487 	for (i = 0; i <= retries; i++) {
488 	    if (i > 0) {
489 		sock_set_timeout(&ibuf->sock, 1, 0);
490 		if (sock_readable(&ibuf->sock))
491 		    break;
492 	    }
493 	    if (sock_write(&ibuf->sock, ibuf->buf, ibuf->len) != ibuf->len) {
494 		error("Couldn't send request to server.");
495 		exit(1);
496 	    }
497 	}
498 
499 	/*
500 	 * Get reply message(s).  If we failed, return false (next server).
501 	 */
502 	sock_set_timeout(&ibuf->sock, 2, 0);
503 	do {
504 	    Sockbuf_clear(ibuf);
505 	    if (Get_reply_message(ibuf, conpar) <= 0) {
506 		warn("No answer from server");
507 		return false;
508 	    }
509 	    if (Packet_scanf(ibuf, "%c%c", &reply_to, &status) <= 0) {
510 		warn("Incomplete reply from server");
511 		return false;
512 	    }
513 
514 	    sock_set_timeout(&ibuf->sock, 0, 500*1000);
515 
516 	    /*
517 	     * Now try and interpret the result.
518 	     */
519 	    errno = 0;
520 	    switch (status) {
521 
522 	    case SUCCESS:
523 		/*
524 		 * Oh glorious success.
525 		 */
526 		switch (reply_to & 0xFF) {
527 
528 		case OPTION_LIST_pack:
529 		    while (Packet_scanf(ibuf, "%S", linebuf) > 0)
530 			printf("%s\n", linebuf);
531 		    break;
532 
533 		case REPORT_STATUS_pack:
534 		    /*
535 		     * Did the reply include a string?
536 		     */
537 		    if (ibuf->len > ibuf->ptr - ibuf->buf
538 			&& (!auto_connect || list_servers)) {
539 			if (list_servers)
540 			    printf("SERVER HOST......: %s\n",
541 				   conpar->server_name);
542 			if (*ibuf->ptr != '\0') {
543 			    if (ibuf->len < ibuf->size)
544 				ibuf->buf[ibuf->len] = '\0';
545 			    else
546 				ibuf->buf[ibuf->size - 1] = '\0';
547 			    Clean_string(ibuf->ptr);
548 			    printf("%s", ibuf->ptr);
549 			    if (ibuf->ptr[strlen(ibuf->ptr) - 1] != '\n')
550 				printf("\n");
551 			}
552 		    }
553 		    break;
554 
555 		case SHUTDOWN_pack:
556 		    if (delay == 0)
557 			puts("*** Shutdown stopped.");
558 		    else
559 			puts("*** Shutdown initiated.");
560 		    break;
561 
562 		case ENTER_GAME_pack:
563 		    if (Packet_scanf(ibuf, "%hu", &port) <= 0) {
564 			warn("Incomplete login reply from server");
565 			conpar->login_port = -1;
566 		    } else {
567 			conpar->login_port = port;
568 			printf("*** Login allowed.\n");
569 		    }
570 		    break;
571 
572 		case ENTER_QUEUE_pack:
573 		    if (Packet_scanf(ibuf, "%hu", &qpos) <= 0)
574 			warn("Incomplete queue reply from server");
575 		    else {
576 			printf("... queued at position %2d\n", qpos);
577 			IFWINDOWS(Progress("Queued at position %2d\n", qpos));
578 		    }
579 		    /*
580 		     * Acknowledge each 10 seconds that we are still
581 		     * interested to be on the waiting queue.
582 		     */
583 		    if (qsent + 10 <= time(NULL)) {
584 			Sockbuf_clear(ibuf);
585 			Packet_printf(ibuf, "%u%s%hu",
586 				      VERSION2MAGIC(conpar->server_version),
587 				      conpar->user_name,
588 				      sock_get_port(&ibuf->sock));
589 			Packet_printf(ibuf, "%c%s%s%s%d", ENTER_QUEUE_pack,
590 				      conpar->nick_name, conpar->disp_name,
591 				      conpar->host_name, conpar->team);
592 			if (sock_write(&ibuf->sock, ibuf->buf, ibuf->len)
593 			    != ibuf->len) {
594 			    error("Couldn't send request to server.");
595 			    exit(1);
596 			}
597 			time(&qsent);
598 		    }
599 		    sock_set_timeout(&ibuf->sock, 12, 0);
600 		    max_replies = 2;
601 		    break;
602 
603 		case CREDENTIALS_pack:
604 		    if (Packet_scanf(ibuf, "%ld", &key) <= 0)
605 			warn("Incomplete credentials reply from server");
606 		    else {
607 			has_credentials = true;
608 			cmd_credentials = c;
609 			continue;
610 		    }
611 		    break;
612 
613 		default:
614 		    puts("*** Operation successful.");
615 		    break;
616 		}
617 		break;
618 
619 	    case E_NOT_OWNER:
620 		warn("Permission denied, not owner");
621 		break;
622 	    case E_GAME_FULL:
623 		warn("Sorry, game full");
624 		break;
625 	    case E_TEAM_FULL:
626 		warn("Sorry, team %d is full", conpar->team);
627 		break;
628 	    case E_TEAM_NOT_SET:
629 		warn("Sorry, team play selected "
630 		     "and you haven't specified your team");
631 		break;
632 	    case E_GAME_LOCKED:
633 		warn("Sorry, game locked");
634 		break;
635 	    case E_NOT_FOUND:
636 		warn("That player is not logged on this server");
637 		break;
638 	    case E_IN_USE:
639 		warn("Your nick is already used");
640 		break;
641 	    case E_SOCKET:
642 		warn("Server can't setup socket");
643 		break;
644 	    case E_INVAL:
645 		warn("Invalid input parameters says the server");
646 		break;
647 	    case E_VERSION:
648 		warn("We have an incompatible version says the server");
649 		break;
650 	    case E_NOENT:
651 		warn("No such variable, says the server");
652 		break;
653 	    case E_UNDEFINED:
654 		warn("Requested operation is undefined, says the server");
655 		break;
656 	    default:
657 		warn("Server answers with unknown error status '%02x'",
658 		     status);
659 		break;
660 	    }
661 
662 	    if (list_servers)	/* If listing servers, go to next one */
663 		return false;
664 
665 	    if (auto_shutdown)	/* Do the same if we've sent a -shutdown */
666 		return false;
667 
668 	    if (auto_connect && status != SUCCESS)
669 		return false;
670 
671 	    /*
672 	     * If we wanted to enter the game and we were allowed to, return
673 	     * true (we are done).  If we weren't allowed, either return false
674 	     * (get next server) if we are auto_connecting or get next command
675 	     * if we aren't auto_connecting (interactive).
676 	     */
677 	    if (reply_to == ENTER_GAME_pack) {
678 		if (status == SUCCESS && conpar->login_port > 0)
679 		    return true;
680 		else {
681 		    if (auto_connect)
682 			return false;
683 		}
684 	    }
685 
686 	} while (--max_replies > 0 && sock_readable(&ibuf->sock));
687 
688 	/*
689 	 * Get next command.
690 	 */
691     }
692 
693     /*NOTREACHED*/
694 }
695 
696 
697 
698 /*
699  * Setup a socket and a buffer for client-server messages.
700  * We do this again for each server to prevent getting
701  * old messages from past servers.
702  */
Connect_to_server(int auto_connect,int list_servers,int auto_shutdown,char * shutdown_reason,Connect_param_t * conpar)703 int Connect_to_server(int auto_connect, int list_servers,
704 		      int auto_shutdown, char *shutdown_reason,
705 		      Connect_param_t *conpar)
706 {
707     sockbuf_t		ibuf;			/* info buffer */
708     int			result;
709 
710     if (Sockbuf_init(&ibuf, NULL, CLIENT_RECV_SIZE,
711 		     SOCKBUF_READ | SOCKBUF_WRITE | SOCKBUF_DGRAM) == -1) {
712 	error("No memory for info buffer");
713 	exit(1);
714     }
715     result = Process_commands(&ibuf,
716 			     auto_connect, list_servers,
717 			     auto_shutdown, shutdown_reason,
718 			     conpar);
719     if (ibuf.sock.fd != SOCK_FD_INVALID) {
720 	close_dgram_socket(&ibuf.sock);
721 	ibuf.sock.fd = SOCK_FD_INVALID;
722     }
723     Sockbuf_cleanup(&ibuf);
724 
725     return result;
726 }
727 
728 
Contact_servers(int count,char ** servers,int auto_connect,int list_servers,int auto_shutdown,char * shutdown_reason,int find_max,int * num_found,char ** server_addresses,char ** server_names,unsigned * server_versions,Connect_param_t * conpar)729 int Contact_servers(int count, char **servers,
730 		    int auto_connect, int list_servers,
731 		    int auto_shutdown, char *shutdown_reason,
732 		    int find_max, int *num_found,
733 		    char **server_addresses, char **server_names,
734 		    unsigned *server_versions,
735 		    Connect_param_t *conpar)
736 {
737     int			connected = false;
738     const int		max_retries = 2;
739     int			i, ret;
740     int			status;
741     sock_t		sock;
742     int			retries;
743     int			contacted;
744     bool		compat_mode = false;
745     sockbuf_t		sbuf;			/* contact buffer */
746 
747 
748     if ((status = create_dgram_socket(&sock, 0)) == SOCK_IS_ERROR) {
749 	error("Could not create connection socket");
750 	exit(1);
751     }
752     if (Sockbuf_init(&sbuf, &sock, CLIENT_RECV_SIZE,
753 		     SOCKBUF_READ | SOCKBUF_WRITE | SOCKBUF_DGRAM) == -1) {
754 	error("No memory for contact buffer");
755 	exit(1);
756     }
757     if (!count) {
758 	retries = 0;
759 	contacted = 0;
760 	compat_mode = false;
761 	do {
762 	    Sockbuf_clear(&sbuf);
763 	    Packet_printf(&sbuf, "%u%s%hu%c", MAGIC, conpar->user_name,
764 			  sock_get_port(&sbuf.sock), CONTACT_pack);
765 	    assert(sbuf.len >= 0);
766 	    if (Query_all(&sbuf.sock, conpar->contact_port,
767 			  sbuf.buf, (size_t)sbuf.len) == -1) {
768 		error("Couldn't send contact requests");
769 		exit(1);
770 	    }
771 	    if (retries == 0) {
772 		printf("Searching for an XPilot "
773 		       "server on the local net...\n");
774 		IFWINDOWS( Progress("Searching for an XPilot "
775 				    "server on the local net...") );
776 	    } else {
777 		printf("Searching once more...\n");
778 		IFWINDOWS( Progress("Searching once more...") );
779 	    }
780 	    while (Get_contact_message(&sbuf, "", conpar)) {
781 		contacted++;
782 		if (list_servers == 2) {
783 		    if (count < find_max) {
784 			if (server_names) {
785 			    strlcpy(server_names[count],
786 				    conpar->server_name,
787 				    MAX_HOST_LEN);
788 			}
789 			if (server_addresses) {
790 			    strlcpy(server_addresses[count],
791 				    conpar->server_addr,
792 				    MAX_HOST_LEN);
793 			}
794 			if (server_versions)
795 			    server_versions[count] = conpar->server_version;
796 			count++;
797 		    }
798 		    if (num_found)
799 			*num_found = count;
800 		} else {
801 		    connected = Connect_to_server(auto_connect,
802 						  list_servers,
803 						  auto_shutdown,
804 						  shutdown_reason,
805 						  conpar);
806 		    if (connected)
807 			break;
808 		}
809 	    }
810 	} while (!contacted && retries++ < max_retries);
811     }
812     else {
813 	for (i = 0; i < count && !connected; i++) {
814 	    retries = 0;
815 	    contacted = 0;
816 	    do {
817 		printf("Contacting server %s.\n", servers[i]);
818 		IFWINDOWS( Progress("Contacting server %s", servers[i]) );
819 		Sockbuf_clear(&sbuf);
820 		Packet_printf(&sbuf, "%u%s%hu%c",
821 			      compat_mode ? COMPATIBILITY_MAGIC : MAGIC,
822 			      conpar->user_name, sock_get_port(&sbuf.sock),
823 			      CONTACT_pack);
824 		if (sock_send_dest(&sbuf.sock, servers[i],
825 			      conpar->contact_port,
826 			      sbuf.buf, sbuf.len) == -1) {
827 		    if (sbuf.sock.error.call == SOCK_CALL_GETHOSTBYNAME) {
828 			printf("Can't find the server '%s'.\n", servers[i]);
829 			IFWINDOWS( Progress("Can't find the server '%s'.",
830 					    servers[i]) );
831 			break;
832 		    }
833 		    error("Can't contact %s on port %d",
834 			  servers[i], conpar->contact_port);
835 		}
836 		if (retries) {
837 		    printf("Retrying %s...\n", servers[i]);
838 		    IFWINDOWS( Progress("Retrying %s...", servers[i]) );
839 		}
840 		ret = Get_contact_message(&sbuf, servers[i], conpar);
841 		if (ret == 2 && !compat_mode) {
842 		    printf("Trying compatibility version %04x\n",
843 			   MAGIC2VERSION(COMPATIBILITY_MAGIC));
844 		    compat_mode = true;
845 		    retries--;	/* a bit ugly, cancels the loop ++ */
846 		    continue;
847 		}
848 		if (ret == 1) {
849 		    contacted++;
850 		    IFWINDOWS( Progress("Contacted %s", servers[i]) );
851 		    connected = Connect_to_server(auto_connect, list_servers,
852 						  auto_shutdown,
853 						  shutdown_reason,
854 						  conpar);
855 		    if (connected)
856 			break;
857 		}
858 	    } while (!contacted && retries++ < max_retries);
859 	}
860     }
861     Sockbuf_cleanup(&sbuf);
862     close_dgram_socket(&sock);
863 
864     return connected ? true : false;
865 }
866