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)
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 ***********************************************************************/
14 /***********************************************************************
15 Common network interface.
16 ***********************************************************************/
19 #include <fc_config.h>
22 #include "fc_prehdrs.h"
30 #ifdef HAVE_NETINET_IN_H
31 #include <netinet/in.h>
33 #ifdef HAVE_ARPA_INET_H
34 #include <arpa/inet.h>
42 #ifdef HAVE_SYS_IOCTL_H
43 #include <sys/ioctl.h>
45 #ifdef HAVE_SYS_SIGNAL_H
46 #include <sys/signal.h>
48 #ifdef FREECIV_MSWINDOWS
49 #include <windows.h> /* GetTempPath */
62 #define INADDR_NONE 0xffffffff
65 #ifdef HAVE_GETADDRINFO
67 #define FC_AI_NUMERICSERV AI_NUMERICSERV
68 #else /* AI_NUMERICSERV */
69 #define FC_AI_NUMERICSERV 0
70 #endif /* AI_NUMERICSERV */
71 #endif /* HAVE_GETADDRINFO */
73 #ifdef FREECIV_HAVE_WINSOCK
74 /***************************************************************
75 Set errno variable on Winsock error
76 ***************************************************************/
77 static void set_socket_errno(void)
79 int err
= WSAGetLastError();
82 /* these have mappings to symbolic errno names in netintf.h */
92 bugreport_request("Missing errno mapping for Winsock error #%d.", err
);
97 #endif /* FREECIV_HAVE_WINSOCK */
99 /***************************************************************
100 Connect a socket to an address
101 ***************************************************************/
102 int fc_connect(int sockfd
, const struct sockaddr
*serv_addr
, socklen_t addrlen
)
106 result
= connect(sockfd
, serv_addr
, addrlen
);
108 #ifdef FREECIV_HAVE_WINSOCK
112 #endif /* FREECIV_HAVE_WINSOCK */
117 /***************************************************************
118 Wait for a number of sockets to change status
119 **************************************************************/
120 int fc_select(int n
, fd_set
*readfds
, fd_set
*writefds
, fd_set
*exceptfds
,
125 result
= select(n
, readfds
, writefds
, exceptfds
, timeout
);
127 #ifdef FREECIV_HAVE_WINSOCK
131 #endif /* FREECIV_HAVE_WINSOCK */
136 /***************************************************************
138 ***************************************************************/
139 int fc_readsocket(int sock
, void *buf
, size_t size
)
143 #ifdef FREECIV_HAVE_WINSOCK
144 result
= recv(sock
, buf
, size
, 0);
148 #else /* FREECIV_HAVE_WINSOCK */
149 result
= read(sock
, buf
, size
);
150 #endif /* FREECIV_HAVE_WINSOCK */
155 /***************************************************************
157 ***************************************************************/
158 int fc_writesocket(int sock
, const void *buf
, size_t size
)
162 #ifdef FREECIV_HAVE_WINSOCK
163 result
= send(sock
, buf
, size
, 0);
167 #else /* FREECIV_HAVE_WINSOCK */
169 result
= send(sock
, buf
, size
, MSG_NOSIGNAL
);
170 # else /* MSG_NOSIGNAL */
171 result
= write(sock
, buf
, size
);
172 # endif /* MSG_NOSIGNAL */
173 #endif /* FREECIV_HAVE_WINSOCK */
178 /***************************************************************
180 ***************************************************************/
181 void fc_closesocket(int sock
)
183 #ifdef FREECIV_HAVE_WINSOCK
190 /***************************************************************
191 Initialize network stuff.
192 ***************************************************************/
193 void fc_init_network(void)
195 #ifdef FREECIV_HAVE_WINSOCK
198 if (WSAStartup(MAKEWORD(1, 1), &wsa
) != 0) {
199 log_error("no usable WINSOCK.DLL: %s", fc_strerror(fc_get_errno()));
201 #endif /* FREECIV_HAVE_WINSOCK */
203 /* broken pipes are ignored. */
205 (void) signal(SIGPIPE
, SIG_IGN
);
209 /***************************************************************
210 Shutdown network stuff.
211 ***************************************************************/
212 void fc_shutdown_network(void)
214 #ifdef FREECIV_HAVE_WINSOCK
219 /***************************************************************
220 Set socket to non-blocking.
221 ***************************************************************/
222 void fc_nonblock(int sockfd
)
224 #ifdef NONBLOCKING_SOCKETS
225 #ifdef FREECIV_HAVE_WINSOCK
230 #endif /* __LP64__ */
232 ioctlsocket(sockfd
, FIONBIO
, &b
);
233 #else /* FREECIV_HAVE_WINSOCK */
237 if ((f_set
= fcntl(sockfd
, F_GETFL
)) == -1) {
238 log_error("fcntl F_GETFL failed: %s", fc_strerror(fc_get_errno()));
243 if (fcntl(sockfd
, F_SETFL
, f_set
) == -1) {
244 log_error("fcntl F_SETFL failed: %s", fc_strerror(fc_get_errno()));
246 #else /* HAVE_FCNTL */
250 if (ioctl(sockfd
, FIONBIO
, (char*)&value
) == -1) {
251 log_error("ioctl failed: %s", fc_strerror(fc_get_errno()));
253 #endif /* HAVE_IOCTL */
254 #endif /* HAVE_FCNTL */
255 #endif /* FREECIV_HAVE_WINSOCK */
256 #else /* NONBLOCKING_SOCKETS */
257 log_debug("NONBLOCKING_SOCKETS not available");
258 #endif /* NONBLOCKING_SOCKETS */
261 /***************************************************************************
262 Write information about sockaddr to debug log.
263 ***************************************************************************/
264 void sockaddr_debug(union fc_sockaddr
*addr
, enum log_level lvl
)
266 #ifdef FREECIV_IPV6_SUPPORT
267 char buf
[INET6_ADDRSTRLEN
] = "Unknown";
269 if (addr
->saddr
.sa_family
== AF_INET6
) {
270 inet_ntop(AF_INET6
, &addr
->saddr_in6
.sin6_addr
, buf
, INET6_ADDRSTRLEN
);
271 log_base(lvl
, "Host: %s, Port: %d (IPv6)",
272 buf
, ntohs(addr
->saddr_in6
.sin6_port
));
274 } else if (addr
->saddr
.sa_family
== AF_INET
) {
275 inet_ntop(AF_INET
, &addr
->saddr_in4
.sin_addr
, buf
, INET_ADDRSTRLEN
);
276 log_base(lvl
, "Host: %s, Port: %d (IPv4)",
277 buf
, ntohs(addr
->saddr_in4
.sin_port
));
280 #else /* IPv6 support */
281 if (addr
->saddr
.sa_family
== AF_INET
) {
284 buf
= inet_ntoa(addr
->saddr_in4
.sin_addr
);
286 log_base(lvl
, "Host: %s, Port: %d",
287 buf
, ntohs(addr
->saddr_in4
.sin_port
));
291 #endif /* IPv6 support */
293 log_error("Unsupported address family in sockaddr_debug()");
296 /***************************************************************************
297 Gets size of address to fc_sockaddr. IPv6/IPv4 must be selected before
299 ***************************************************************************/
300 int sockaddr_size(union fc_sockaddr
*addr
)
302 #ifdef FREECIV_MSWINDOWS
303 return sizeof(*addr
);
305 #ifdef FREECIV_IPV6_SUPPORT
306 if (addr
->saddr
.sa_family
== AF_INET6
) {
307 return sizeof(addr
->saddr_in6
);
309 #endif /* FREECIV_IPV6_SUPPORT */
310 if (addr
->saddr
.sa_family
== AF_INET
) {
311 return sizeof(addr
->saddr_in4
);
315 log_error("Unsupported address family in sockaddr_size()");
319 #endif /* FREECIV_MSWINDOWS */
322 /***************************************************************************
323 Returns wether address is IPv6 address.
324 ***************************************************************************/
325 bool sockaddr_ipv6(union fc_sockaddr
*addr
)
327 #ifdef FREECIV_IPV6_SUPPORT
328 if (addr
->saddr
.sa_family
== AF_INET6
) {
331 #endif /* IPv6 support */
336 #ifdef HAVE_GETADDRINFO
337 /***************************************************************************
338 Look up the service at hostname:port using getaddrinfo().
339 ***************************************************************************/
340 static struct fc_sockaddr_list
*net_lookup_getaddrinfo(const char *name
,
342 enum fc_addr_family family
)
344 struct addrinfo hints
;
345 struct addrinfo
*res
;
349 struct fc_sockaddr_list
*addrs
=
350 fc_sockaddr_list_new_full((fc_sockaddr_list_free_fn_t
) free
);
360 #ifndef FREECIV_IPV6_SUPPORT
372 /* Convert port to string for getaddrinfo() */
373 fc_snprintf(servname
, sizeof(servname
), "%d", port
);
375 /* Use getaddrinfo() to lookup IPv6 addresses */
376 memset(&hints
, 0, sizeof(hints
));
377 hints
.ai_family
= gafam
;
378 hints
.ai_socktype
= SOCK_DGRAM
; /* any type that uses sin6_port */
379 hints
.ai_flags
= AI_PASSIVE
| FC_AI_NUMERICSERV
;
380 err
= getaddrinfo(name
, servname
, &hints
, &res
);
383 struct addrinfo
*current
= res
;
385 while (current
!= NULL
) {
386 union fc_sockaddr
*caddr
;
388 fc_assert_action(current
->ai_addrlen
<= sizeof(*caddr
), continue);
389 caddr
= fc_malloc(sizeof(*caddr
));
390 memcpy(caddr
, current
->ai_addr
, current
->ai_addrlen
);
392 fc_sockaddr_list_append(addrs
, caddr
);
394 current
= current
->ai_next
;
401 #endif /* HAVE_GETADDRINFO */
403 /***************************************************************************
404 Look up the service at hostname:port.
405 ***************************************************************************/
406 struct fc_sockaddr_list
*net_lookup_service(const char *name
, int port
,
407 enum fc_addr_family family
)
409 /* IPv6-enabled Freeciv always has HAVE_GETADDRINFO, IPv4-only Freeciv not
411 #ifdef HAVE_GETADDRINFO
412 return net_lookup_getaddrinfo(name
, port
, family
);
413 #else /* HAVE_GETADDRINFO */
415 struct sockaddr_in
*sock4
;
417 struct fc_sockaddr_list
*addrs
= fc_sockaddr_list_new();
418 union fc_sockaddr
*result
= fc_malloc(sizeof(*result
));
420 sock4
= &result
->saddr_in4
;
422 fc_assert(family
!= FC_ADDR_IPV6
);
424 result
->saddr
.sa_family
= AF_INET
;
425 sock4
->sin_port
= htons(port
);
428 sock4
->sin_addr
.s_addr
= htonl(INADDR_ANY
);
429 fc_sockaddr_list_append(addrs
, result
);
434 if (fc_inet_aton(name
, &sock4
->sin_addr
, FALSE
)) {
435 fc_sockaddr_list_append(addrs
, result
);
440 hp
= gethostbyname(name
);
441 if (!hp
|| hp
->h_addrtype
!= AF_INET
) {
447 memcpy(&sock4
->sin_addr
, hp
->h_addr
, hp
->h_length
);
448 fc_sockaddr_list_append(addrs
, result
);
452 #endif /* !HAVE_GETADDRINFO */
456 /*************************************************************************
457 Convert internet IPv4 host address to binary form and store it to inp.
458 Return FALSE on failure if possible, i.e., FALSE is guarantee that it
459 failed but TRUE is not guarantee that it succeeded.
460 *************************************************************************/
461 bool fc_inet_aton(const char *cp
, struct in_addr
*inp
, bool addr_none_ok
)
463 #ifdef FREECIV_IPV6_SUPPORT
464 /* Use inet_pton() */
465 if (!inet_pton(AF_INET
, cp
, &inp
->s_addr
)) {
468 #else /* IPv6 Support */
469 #ifdef HAVE_INET_ATON
470 if (!inet_aton(cp
, inp
)) {
473 #else /* HAVE_INET_ATON */
474 inp
->s_addr
= inet_addr(cp
);
475 if (!addr_none_ok
&& inp
->s_addr
== INADDR_NONE
) {
478 #endif /* HAVE_INET_ATON */
479 #endif /* IPv6 Support */
484 /*************************************************************************
485 Writes buf to socket and returns the response in an fz_FILE.
486 Use only on blocking sockets.
487 *************************************************************************/
488 fz_FILE
*fc_querysocket(int sock
, void *buf
, size_t size
)
493 fp
= fdopen(sock
, "r+b");
494 if (fwrite(buf
, 1, size
, fp
) != size
) {
495 log_error("socket %d: write error", sock
);
499 /* we don't use fc_closesocket on sock here since when fp is closed
500 * sock will also be closed. fdopen doesn't dup the socket descriptor. */
501 #else /* HAVE_FDOPEN */
506 #ifdef FREECIV_MSWINDOWS
507 /* tmpfile() in mingw attempts to make a temp file in the root directory
508 * of the current drive, which we may not have write access to. */
510 char filename
[MAX_PATH
];
512 GetTempPath(sizeof(filename
), filename
);
513 sz_strlcat(filename
, "fctmp");
515 fp
= fc_fopen(filename
, "w+b");
517 #else /* FREECIV_MSWINDOWS */
521 #endif /* FREECIV_MSWINDOWS */
527 fc_writesocket(sock
, buf
, size
);
529 while ((n
= fc_readsocket(sock
, tmp
, sizeof(tmp
))) > 0) {
530 if (fwrite(tmp
, 1, n
, fp
) != n
) {
531 log_error("socket %d: write error", sock
);
536 fc_closesocket(sock
);
540 #endif /* HAVE_FDOPEN */
542 return fz_from_stream(fp
);
545 /**************************************************************************
546 Finds the next (lowest) free port.
547 **************************************************************************/
548 int find_next_free_port(int starting_port
, int highest_port
,
549 enum fc_addr_family family
,
550 char *net_interface
, bool not_avail_ok
)
557 #ifndef FREECIV_IPV6_SUPPORT
558 fc_assert(family
== FC_ADDR_IPV4
|| family
== FC_ADDR_ANY
);
565 #ifdef FREECIV_IPV6_SUPPORT
569 #endif /* FREECIV_IPV6_SUPPORT */
571 gafamily
= AF_UNSPEC
;
575 log_error("Port from unsupported address family requested!");
580 for (port
= starting_port
; !found
&& highest_port
> port
; port
++) {
581 /* HAVE_GETADDRINFO implies IPv6 support */
582 #ifdef HAVE_GETADDRINFO
583 struct addrinfo hints
;
586 struct addrinfo
*res
;
588 fc_snprintf(servname
, sizeof(servname
), "%d", port
);
590 memset(&hints
, 0, sizeof(hints
));
591 hints
.ai_family
= gafamily
;
592 hints
.ai_socktype
= SOCK_DGRAM
;
593 hints
.ai_flags
= AI_PASSIVE
| FC_AI_NUMERICSERV
;
595 err
= getaddrinfo(net_interface
, servname
, &hints
, &res
);
597 struct addrinfo
*current
= res
;
598 bool unusable
= FALSE
;
600 while (current
!= NULL
&& !unusable
) {
601 s
= socket(current
->ai_family
, SOCK_STREAM
, 0);
604 log_error("socket(): %s", fc_strerror(fc_get_errno()));
606 if (bind(s
, current
->ai_addr
, current
->ai_addrlen
) != 0) {
607 if (!not_avail_ok
|| fc_get_errno() != EADDRNOTAVAIL
) {
612 current
= current
->ai_next
;
618 if (!unusable
&& res
!= NULL
) {
622 #else /* HAVE_GETADDRINFO */
623 union fc_sockaddr tmp
;
624 struct sockaddr_in
*sock4
;
626 s
= socket(gafamily
, SOCK_STREAM
, 0);
628 sock4
= &tmp
.saddr_in4
;
629 memset(&tmp
, 0, sizeof(tmp
));
630 sock4
->sin_family
= AF_INET
;
631 sock4
->sin_port
= htons(port
);
632 if (net_interface
!= NULL
) {
633 if (!fc_inet_aton(net_interface
, &sock4
->sin_addr
, FALSE
)) {
636 hp
= gethostbyname(net_interface
);
638 log_error("No hostent for %s!", net_interface
);
642 if (hp
->h_addrtype
!= AF_INET
) {
643 log_error("Requested IPv4 address for %s, got something else! (%d)",
644 net_interface
, hp
->h_addrtype
);
649 memcpy(&sock4
->sin_addr
, hp
->h_addr
, hp
->h_length
);
652 sock4
->sin_addr
.s_addr
= htonl(INADDR_ANY
);
655 if (bind(s
, &tmp
.saddr
, sockaddr_size(&tmp
)) == 0) {
660 #endif /* HAVE_GETADDRINFO */
664 log_error("None of the ports %d - %d is available.",
665 starting_port
, highest_port
);
670 /* Rollback the last increment from the loop, back to the port
671 * number found to be free. */