Freeciv-3.2
Loading...
Searching...
No Matches
netintf.c
Go to the documentation of this file.
1/***********************************************************************
2 Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2, or (at your option)
6 any later version.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12***********************************************************************/
13
14/***********************************************************************
15 Common network interface.
16***********************************************************************/
17
18#ifdef HAVE_CONFIG_H
19#include <fc_config.h>
20#endif
21
22#include "fc_prehdrs.h"
23
24#include <errno.h>
25#include <signal.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
29
30#ifdef HAVE_NETINET_IN_H
31#include <netinet/in.h>
32#endif
33#ifdef HAVE_ARPA_INET_H
34#include <arpa/inet.h>
35#endif
36#ifdef HAVE_FCNTL_H
37#include <fcntl.h>
38#endif
39#ifdef HAVE_NETDB_H
40#include <netdb.h>
41#endif
42#ifdef HAVE_SYS_IOCTL_H
43#include <sys/ioctl.h>
44#endif
45#ifdef HAVE_SIGNAL_H
46#include <signal.h>
47#elif defined(HAVE_SYS_SIGNAL_H)
48#include <sys/signal.h>
49#endif
50#ifdef FREECIV_MSWINDOWS
51#include <windows.h> /* GetTempPath */
52#endif
53
54/* utility */
55#include "bugs.h"
56#include "fcintl.h"
57#include "log.h"
58#include "mem.h"
59#include "support.h"
60
61#include "netintf.h"
62
63#ifndef INADDR_NONE
64#define INADDR_NONE 0xffffffff
65#endif
66
67#ifdef HAVE_GETADDRINFO
68#ifdef AI_NUMERICSERV
69#define FC_AI_NUMERICSERV AI_NUMERICSERV
70#else /* AI_NUMERICSERV */
71#define FC_AI_NUMERICSERV 0
72#endif /* AI_NUMERICSERV */
73#endif /* HAVE_GETADDRINFO */
74
75#ifdef FREECIV_HAVE_WINSOCK
76/*********************************************************************/
79static void set_socket_errno(void)
80{
81 int err = WSAGetLastError();
82
83 switch (err) {
84 /* these have mappings to symbolic errno names in net_types.h */
85 case WSAEINTR:
86 case WSAEINPROGRESS:
87 case WSAEWOULDBLOCK:
88 case WSAECONNRESET:
89 case WSAECONNREFUSED:
91 case WSAETIMEDOUT:
92 case WSAECONNABORTED:
93 case WSAENOTSOCK:
94 errno = err;
95 return;
96 default:
97 bugreport_request("Missing errno mapping for Winsock error #%d.", err);
98
99 errno = 0;
100 }
101}
102#endif /* FREECIV_HAVE_WINSOCK */
103
104/*********************************************************************/
108{
109 int result;
110
111 result = connect(sockfd, serv_addr, addrlen);
112
113#ifdef FREECIV_HAVE_WINSOCK
114 if (result == -1) {
116 }
117#endif /* FREECIV_HAVE_WINSOCK */
118
119 return result;
120}
121
122/*********************************************************************/
126 fc_timeval *timeout)
127{
128 int result;
129
130 result = select(n, readfds, writefds, exceptfds, timeout);
131
132#ifdef FREECIV_HAVE_WINSOCK
133 if (result == -1) {
135 }
136#endif /* FREECIV_HAVE_WINSOCK */
137
138 return result;
139}
140
141/*********************************************************************/
144int fc_readsocket(int sock, void *buf, size_t size)
145{
146 int result;
147
148#ifdef FREECIV_HAVE_WINSOCK
149 result = recv(sock, buf, size, 0);
150 if (result == -1) {
152 }
153#else /* FREECIV_HAVE_WINSOCK */
154 result = read(sock, buf, size);
155#endif /* FREECIV_HAVE_WINSOCK */
156
157 return result;
158}
159
160/*********************************************************************/
163int fc_writesocket(int sock, const void *buf, size_t size)
164{
165 int result;
166
167#ifdef FREECIV_HAVE_WINSOCK
168 result = send(sock, buf, size, 0);
169 if (result == -1) {
171 }
172#else /* FREECIV_HAVE_WINSOCK */
173# ifdef MSG_NOSIGNAL
174 result = send(sock, buf, size, MSG_NOSIGNAL);
175# else /* MSG_NOSIGNAL */
176 result = write(sock, buf, size);
177# endif /* MSG_NOSIGNAL */
178#endif /* FREECIV_HAVE_WINSOCK */
179
180 return result;
181}
182
183/*********************************************************************/
186void fc_closesocket(int sock)
187{
188#ifdef FREECIV_HAVE_WINSOCK
189 closesocket(sock);
190#else
191 close(sock);
192#endif
193}
194
195/*********************************************************************/
199{
200#ifdef FREECIV_HAVE_WINSOCK
201 WSADATA wsa;
202
203 if (WSAStartup(MAKEWORD(1, 1), &wsa) != 0) {
204 log_error("no usable WINSOCK.DLL: %s", fc_strerror(fc_get_errno()));
205 }
206#endif /* FREECIV_HAVE_WINSOCK */
207
208 /* broken pipes are ignored. */
209#ifdef HAVE_SIGPIPE
211#endif
212}
213
214/*********************************************************************/
218{
219#ifdef FREECIV_HAVE_WINSOCK
220 WSACleanup();
221#endif
222}
223
224/*********************************************************************/
228{
229#ifdef NONBLOCKING_SOCKETS
230#ifdef FREECIV_HAVE_WINSOCK
231#ifdef __LP64__
232 unsigned b = 1;
233#else /* __LP64__ */
234 u_long b = 1;
235#endif /* __LP64__ */
236
238#else /* FREECIV_HAVE_WINSOCK */
239#ifdef HAVE_FCNTL
240 int f_set;
241
242 if ((f_set = fcntl(sockfd, F_GETFL)) == -1) {
243 log_error("fcntl F_GETFL failed: %s", fc_strerror(fc_get_errno()));
244 }
245
246 f_set |= O_NONBLOCK;
247
248 if (fcntl(sockfd, F_SETFL, f_set) == -1) {
249 log_error("fcntl F_SETFL failed: %s", fc_strerror(fc_get_errno()));
250 }
251#else /* HAVE_FCNTL */
252#ifdef HAVE_IOCTL
253 long value = 1;
254
255 if (ioctl(sockfd, FIONBIO, (char*)&value) == -1) {
256 log_error("ioctl failed: %s", fc_strerror(fc_get_errno()));
257 }
258#endif /* HAVE_IOCTL */
259#endif /* HAVE_FCNTL */
260#endif /* FREECIV_HAVE_WINSOCK */
261#else /* NONBLOCKING_SOCKETS */
262 log_warn("NONBLOCKING_SOCKETS not available");
263#endif /* NONBLOCKING_SOCKETS */
264}
265
266/*********************************************************************/
270{
271#ifdef FREECIV_IPV6_SUPPORT
272 char buf[INET6_ADDRSTRLEN] = "Unknown";
273
274 if (addr->saddr.sa_family == AF_INET6) {
275 inet_ntop(AF_INET6, &addr->saddr_in6.sin6_addr, buf, INET6_ADDRSTRLEN);
276 log_base(lvl, "Host: %s, Port: %d (IPv6)",
277 buf, ntohs(addr->saddr_in6.sin6_port));
278 return;
279 } else if (addr->saddr.sa_family == AF_INET) {
280 inet_ntop(AF_INET, &addr->saddr_in4.sin_addr, buf, INET_ADDRSTRLEN);
281 log_base(lvl, "Host: %s, Port: %d (IPv4)",
282 buf, ntohs(addr->saddr_in4.sin_port));
283 return;
284 }
285#else /* IPv6 support */
286 if (addr->saddr.sa_family == AF_INET) {
287 char *buf;
288
289 buf = inet_ntoa(addr->saddr_in4.sin_addr);
290
291 log_base(lvl, "Host: %s, Port: %d",
292 buf, ntohs(addr->saddr_in4.sin_port));
293
294 return;
295 }
296#endif /* IPv6 support */
297
298 log_error("Unsupported address family in sockaddr_debug()");
299}
300
301/*********************************************************************/
306{
307#ifdef FREECIV_MSWINDOWS
308 return sizeof(*addr);
309#else
310#ifdef FREECIV_IPV6_SUPPORT
311 if (addr->saddr.sa_family == AF_INET6) {
312 return sizeof(addr->saddr_in6);
313 } else
314#endif /* FREECIV_IPV6_SUPPORT */
315 if (addr->saddr.sa_family == AF_INET) {
316 return sizeof(addr->saddr_in4);
317 } else {
319
320 log_error("Unsupported address family in sockaddr_size()");
321
322 return 0;
323 }
324#endif /* FREECIV_MSWINDOWS */
325}
326
327/*********************************************************************/
330bool sockaddr_ipv6(union fc_sockaddr *addr)
331{
332#ifdef FREECIV_IPV6_SUPPORT
333 if (addr->saddr.sa_family == AF_INET6) {
334 return TRUE;
335 }
336#endif /* IPv6 support */
337
338 return FALSE;
339}
340
341#ifdef HAVE_GETADDRINFO
342/*********************************************************************/
345static struct fc_sockaddr_list *net_lookup_getaddrinfo(const char *name,
346 int port,
348{
349 struct addrinfo hints;
350 struct addrinfo *res;
351 int err;
352 char servname[8];
353 int gafam;
354 struct fc_sockaddr_list *addrs =
356
357 switch (family) {
358 case FC_ADDR_IPV4:
359 gafam = AF_INET;
360 break;
361 case FC_ADDR_IPV6:
362 gafam = AF_INET6;
363 break;
364 case FC_ADDR_ANY:
365#ifndef FREECIV_IPV6_SUPPORT
366 gafam = AF_INET;
367#else
369#endif
370 break;
371 default:
373
374 return addrs;
375 }
376
377 /* Convert port to string for getaddrinfo() */
378 fc_snprintf(servname, sizeof(servname), "%d", port);
379
380 /* Use getaddrinfo() to lookup IPv6 addresses */
381 memset(&hints, 0, sizeof(hints));
382 hints.ai_family = gafam;
383 hints.ai_socktype = SOCK_DGRAM; /* any type that uses sin6_port */
386
387 if (err == 0) {
388 struct addrinfo *current = res;
389
390 while (current != NULL) {
391 union fc_sockaddr *caddr;
392
393 fc_assert_action(current->ai_addrlen <= sizeof(*caddr), continue);
394 caddr = fc_malloc(sizeof(*caddr));
395 memcpy(caddr, current->ai_addr, current->ai_addrlen);
396
398
399 current = current->ai_next;
400 }
402 }
403
404 return addrs;
405}
406#endif /* HAVE_GETADDRINFO */
407
408/*********************************************************************/
411struct fc_sockaddr_list *net_lookup_service(const char *name, int port,
413{
414 /* IPv6-enabled Freeciv always has HAVE_GETADDRINFO, IPv4-only Freeciv not
415 * necessarily */
416#ifdef HAVE_GETADDRINFO
417 return net_lookup_getaddrinfo(name, port, family);
418#else /* HAVE_GETADDRINFO */
419
420 struct sockaddr_in *sock4;
421 struct hostent *hp;
423 union fc_sockaddr *result = fc_malloc(sizeof(*result));
424
425 sock4 = &result->saddr_in4;
426
428
429 result->saddr.sa_family = AF_INET;
430 sock4->sin_port = htons(port);
431
432 if (!name) {
433 sock4->sin_addr.s_addr = htonl(INADDR_ANY);
435
436 return addrs;
437 }
438
439 if (fc_inet_aton(name, &sock4->sin_addr, FALSE)) {
441
442 return addrs;
443 }
444
445 hp = gethostbyname(name);
446 if (!hp || hp->h_addrtype != AF_INET) {
447 FC_FREE(result);
448
449 return addrs;
450 }
451
452 memcpy(&sock4->sin_addr, hp->h_addr, hp->h_length);
454
455 return addrs;
456
457#endif /* !HAVE_GETADDRINFO */
458
459}
460
461/*********************************************************************/
466bool fc_inet_aton(const char *cp, struct in_addr *inp, bool addr_none_ok)
467{
468#ifdef FREECIV_IPV6_SUPPORT
469 /* Use inet_pton() */
470 if (!inet_pton(AF_INET, cp, &inp->s_addr)) {
471 return FALSE;
472 }
473#else /* IPv6 Support */
474#ifdef HAVE_INET_ATON
475 if (!inet_aton(cp, inp)) {
476 return FALSE;
477 }
478#else /* HAVE_INET_ATON */
479 inp->s_addr = inet_addr(cp);
480 if (!addr_none_ok && inp->s_addr == INADDR_NONE) {
481 return FALSE;
482 }
483#endif /* HAVE_INET_ATON */
484#endif /* IPv6 Support */
485
486 return TRUE;
487}
488
489/*********************************************************************/
493fz_FILE *fc_querysocket(int sock, void *buf, size_t size)
494{
495 FILE *fp;
496
497#ifdef HAVE_FDOPEN
498 fp = fdopen(sock, "r+b");
499
500 if (fp == NULL) {
501 log_error("socket %d: failed to fdopen()", sock);
502
503 return NULL;
504 }
505
506 if (fwrite(buf, 1, size, fp) != size) {
507 log_error("socket %d: write error", sock);
508 }
509 fflush(fp);
510
511 /* We don't use fc_closesocket() on sock here since when fp is closed,
512 * sock will also be closed. fdopen() doesn't dup the socket descriptor. */
513#else /* HAVE_FDOPEN */
514 {
515 char tmp[4096];
516 int n;
517
518#ifdef FREECIV_MSWINDOWS
519 /* tmpfile() in mingw attempts to make a temp file in the root directory
520 * of the current drive, which we may not have write access to. */
521 {
522 char filename[MAX_PATH];
523
524 GetTempPath(sizeof(filename), filename);
525 sz_strlcat(filename, "fctmp");
526
527 fp = fc_fopen(filename, "w+b");
528 }
529#else /* FREECIV_MSWINDOWS */
530
531 fp = tmpfile();
532
533#endif /* FREECIV_MSWINDOWS */
534
535 if (fp == NULL) {
536 return NULL;
537 }
538
539 fc_writesocket(sock, buf, size);
540
541 while ((n = fc_readsocket(sock, tmp, sizeof(tmp))) > 0) {
542 if (fwrite(tmp, 1, n, fp) != n) {
543 log_error("socket %d: write error", sock);
544 }
545 }
546 fflush(fp);
547
548 fc_closesocket(sock);
549
550 rewind(fp);
551 }
552#endif /* HAVE_FDOPEN */
553
554 return fz_from_stream(fp);
555}
556
557/*********************************************************************/
562 char *net_interface, bool not_avail_ok)
563{
564 int port;
565 int s;
566 int gafamily;
567 bool found = FALSE;
568
569#ifndef FREECIV_IPV6_SUPPORT
571#endif
572
573 switch (family) {
574 case FC_ADDR_IPV4:
576 break;
577#ifdef FREECIV_IPV6_SUPPORT
578 case FC_ADDR_IPV6:
580 break;
581#endif /* FREECIV_IPV6_SUPPORT */
582 case FC_ADDR_ANY:
584 break;
585 default:
587 log_error("Port from unsupported address family requested!");
588
589 return -1;
590 }
591
592 for (port = starting_port; !found && highest_port > port; port++) {
593 /* HAVE_GETADDRINFO implies IPv6 support */
594#ifdef HAVE_GETADDRINFO
595 struct addrinfo hints;
596 int err;
597 char servname[8];
598 struct addrinfo *res;
599
600 fc_snprintf(servname, sizeof(servname), "%d", port);
601
602 memset(&hints, 0, sizeof(hints));
603 hints.ai_family = gafamily;
604 hints.ai_socktype = SOCK_DGRAM;
606
608 if (!err) {
609 struct addrinfo *current = res;
610 bool unusable = FALSE;
611
612 while (current != NULL && !unusable) {
613 s = socket(current->ai_family, SOCK_STREAM, 0);
614
615 if (s == -1) {
616 log_error("socket(): %s", fc_strerror(fc_get_errno()));
617 } else {
618 if (bind(s, current->ai_addr, current->ai_addrlen) != 0) {
620 unusable = TRUE;
621 }
622 }
623 }
624 current = current->ai_next;
626 }
627
629
630 if (!unusable && res != NULL) {
631 found = TRUE;
632 }
633 }
634#else /* HAVE_GETADDRINFO */
635 union fc_sockaddr tmp;
636 struct sockaddr_in *sock4;
637
638 s = socket(gafamily, SOCK_STREAM, 0);
639
640 sock4 = &tmp.saddr_in4;
641 memset(&tmp, 0, sizeof(tmp));
642 sock4->sin_family = AF_INET;
643 sock4->sin_port = htons(port);
644 if (net_interface != NULL) {
645 if (!fc_inet_aton(net_interface, &sock4->sin_addr, FALSE)) {
646 struct hostent *hp;
647
649 if (hp == NULL) {
650 log_error("No hostent for %s!", net_interface);
651
652 return -1;
653 }
654 if (hp->h_addrtype != AF_INET) {
655 log_error("Requested IPv4 address for %s, got something else! (%d)",
656 net_interface, hp->h_addrtype);
657
658 return -1;
659 }
660
661 memcpy(&sock4->sin_addr, hp->h_addr, hp->h_length);
662 }
663 } else {
664 sock4->sin_addr.s_addr = htonl(INADDR_ANY);
665 }
666
667 if (bind(s, &tmp.saddr, sockaddr_size(&tmp)) == 0) {
668 found = TRUE;
669 }
670
672#endif /* HAVE_GETADDRINFO */
673 }
674
675 if (!found) {
676 log_error("None of the ports %d - %d is available.",
678
679 return -1;
680 }
681
682 /* Rollback the last increment from the loop, back to the port
683 * number found to be free. */
684 port--;
685
686 return port;
687}
688
689/*********************************************************************/
693{
694 switch (announce) {
695 case ANNOUNCE_IPV6:
696 return AF_INET;
697 case ANNOUNCE_IPV4:
698 return AF_INET;
699 case ANNOUNCE_NONE:
700 return AF_UNSPEC;
701 }
702
704
705 return AF_UNSPEC;
706}
#define n
Definition astring.c:77
void bugreport_request(const char *reason_format,...)
Definition bugs.c:31
enum announce_type announce
char * incite_cost
Definition comments.c:75
const char * name
Definition inputfile.c:127
fz_FILE * fz_from_stream(FILE *stream)
Definition ioz.c:548
#define log_warn(message,...)
Definition log.h:105
#define fc_assert(condition)
Definition log.h:176
#define fc_assert_action(condition, action)
Definition log.h:187
#define log_base(level, message,...)
Definition log.h:94
log_level
Definition log.h:28
#define log_error(message,...)
Definition log.h:103
#define FC_FREE(ptr)
Definition mem.h:41
#define fc_malloc(sz)
Definition mem.h:34
announce_type
Definition net_types.h:53
@ ANNOUNCE_IPV6
Definition net_types.h:56
@ ANNOUNCE_IPV4
Definition net_types.h:55
@ ANNOUNCE_NONE
Definition net_types.h:54
fc_addr_family
Definition net_types.h:61
@ FC_ADDR_IPV4
Definition net_types.h:62
@ FC_ADDR_IPV6
Definition net_types.h:63
@ FC_ADDR_ANY
Definition net_types.h:64
void fc_shutdown_network(void)
Definition netintf.c:217
#define INADDR_NONE
Definition netintf.c:64
bool fc_inet_aton(const char *cp, struct in_addr *inp, bool addr_none_ok)
Definition netintf.c:466
bool sockaddr_ipv6(union fc_sockaddr *addr)
Definition netintf.c:330
struct fc_sockaddr_list * net_lookup_service(const char *name, int port, enum fc_addr_family family)
Definition netintf.c:411
void fc_init_network(void)
Definition netintf.c:198
void fc_closesocket(int sock)
Definition netintf.c:186
void sockaddr_debug(union fc_sockaddr *addr, enum log_level lvl)
Definition netintf.c:269
int find_next_free_port(int starting_port, int highest_port, enum fc_addr_family family, char *net_interface, bool not_avail_ok)
Definition netintf.c:560
int addr_family_for_announce_type(enum announce_type announce)
Definition netintf.c:692
int sockaddr_size(union fc_sockaddr *addr)
Definition netintf.c:305
int fc_connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen)
Definition netintf.c:107
int fc_readsocket(int sock, void *buf, size_t size)
Definition netintf.c:144
int fc_writesocket(int sock, const void *buf, size_t size)
Definition netintf.c:163
void fc_nonblock(int sockfd)
Definition netintf.c:227
int fc_select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, fc_timeval *timeout)
Definition netintf.c:125
fz_FILE * fc_querysocket(int sock, void *buf, size_t size)
Definition netintf.c:493
struct timeval fc_timeval
Definition netintf.h:90
int socklen_t
Definition netintf.h:67
size_t size
Definition specvec.h:72
int fc_snprintf(char *str, size_t n, const char *format,...)
Definition support.c:974
const char * fc_strerror(fc_errno err)
Definition support.c:611
FILE * fc_fopen(const char *filename, const char *opentype)
Definition support.c:507
fc_errno fc_get_errno(void)
Definition support.c:594
#define TRUE
Definition support.h:46
#define FALSE
Definition support.h:47
#define sz_strlcat(dest, src)
Definition support.h:196
struct sockaddr saddr
Definition netintf.h:71
struct sockaddr_in saddr_in4
Definition netintf.h:72