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