webperimental: killstack decides stack protects.
[freeciv.git] / utility / netintf.c
blobb383639d87c64c3e10d205e170b8724c0eda58e1
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.
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 ***********************************************************************/
18 #ifdef HAVE_CONFIG_H
19 #include <fc_config.h>
20 #endif
22 #include "fc_prehdrs.h"
24 #include <errno.h>
25 #include <signal.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
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_SYS_SIGNAL_H
46 #include <sys/signal.h>
47 #endif
48 #ifdef FREECIV_MSWINDOWS
49 #include <windows.h> /* GetTempPath */
50 #endif
52 /* utility */
53 #include "bugs.h"
54 #include "fcintl.h"
55 #include "log.h"
56 #include "mem.h"
57 #include "support.h"
59 #include "netintf.h"
61 #ifndef INADDR_NONE
62 #define INADDR_NONE 0xffffffff
63 #endif
65 #ifdef HAVE_GETADDRINFO
66 #ifdef AI_NUMERICSERV
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();
81 switch(err) {
82 /* these have mappings to symbolic errno names in netintf.h */
83 case WSAEINTR:
84 case WSAEWOULDBLOCK:
85 case WSAECONNRESET:
86 case WSAECONNREFUSED:
87 case WSAETIMEDOUT:
88 case WSAECONNABORTED:
89 errno = err;
90 return;
91 default:
92 bugreport_request("Missing errno mapping for Winsock error #%d.", err);
94 errno = 0;
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)
104 int result;
106 result = connect(sockfd, serv_addr, addrlen);
108 #ifdef FREECIV_HAVE_WINSOCK
109 if (result == -1) {
110 set_socket_errno();
112 #endif /* FREECIV_HAVE_WINSOCK */
114 return result;
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,
121 fc_timeval *timeout)
123 int result;
125 result = select(n, readfds, writefds, exceptfds, timeout);
127 #ifdef FREECIV_HAVE_WINSOCK
128 if (result == -1) {
129 set_socket_errno();
131 #endif /* FREECIV_HAVE_WINSOCK */
133 return result;
136 /***************************************************************
137 Read from a socket.
138 ***************************************************************/
139 int fc_readsocket(int sock, void *buf, size_t size)
141 int result;
143 #ifdef FREECIV_HAVE_WINSOCK
144 result = recv(sock, buf, size, 0);
145 if (result == -1) {
146 set_socket_errno();
148 #else /* FREECIV_HAVE_WINSOCK */
149 result = read(sock, buf, size);
150 #endif /* FREECIV_HAVE_WINSOCK */
152 return result;
155 /***************************************************************
156 Write to a socket.
157 ***************************************************************/
158 int fc_writesocket(int sock, const void *buf, size_t size)
160 int result;
162 #ifdef FREECIV_HAVE_WINSOCK
163 result = send(sock, buf, size, 0);
164 if (result == -1) {
165 set_socket_errno();
167 #else /* FREECIV_HAVE_WINSOCK */
168 # ifdef MSG_NOSIGNAL
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 */
175 return result;
178 /***************************************************************
179 Close a socket.
180 ***************************************************************/
181 void fc_closesocket(int sock)
183 #ifdef FREECIV_HAVE_WINSOCK
184 closesocket(sock);
185 #else
186 close(sock);
187 #endif
190 /***************************************************************
191 Initialize network stuff.
192 ***************************************************************/
193 void fc_init_network(void)
195 #ifdef FREECIV_HAVE_WINSOCK
196 WSADATA wsa;
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. */
204 #ifdef HAVE_SIGPIPE
205 (void) signal(SIGPIPE, SIG_IGN);
206 #endif
209 /***************************************************************
210 Shutdown network stuff.
211 ***************************************************************/
212 void fc_shutdown_network(void)
214 #ifdef FREECIV_HAVE_WINSOCK
215 WSACleanup();
216 #endif
219 /***************************************************************
220 Set socket to non-blocking.
221 ***************************************************************/
222 void fc_nonblock(int sockfd)
224 #ifdef NONBLOCKING_SOCKETS
225 #ifdef FREECIV_HAVE_WINSOCK
226 #ifdef __LP64__
227 unsigned b = 1;
228 #else /* __LP64__ */
229 u_long b = 1;
230 #endif /* __LP64__ */
232 ioctlsocket(sockfd, FIONBIO, &b);
233 #else /* FREECIV_HAVE_WINSOCK */
234 #ifdef HAVE_FCNTL
235 int f_set;
237 if ((f_set = fcntl(sockfd, F_GETFL)) == -1) {
238 log_error("fcntl F_GETFL failed: %s", fc_strerror(fc_get_errno()));
241 f_set |= O_NONBLOCK;
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 */
247 #ifdef HAVE_IOCTL
248 long value=1;
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));
273 return;
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));
278 return;
280 #else /* IPv6 support */
281 if (addr->saddr.sa_family == AF_INET) {
282 char *buf;
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));
289 return;
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
298 calling this.
299 ***************************************************************************/
300 int sockaddr_size(union fc_sockaddr *addr)
302 #ifdef FREECIV_MSWINDOWS
303 return sizeof(*addr);
304 #else
305 #ifdef FREECIV_IPV6_SUPPORT
306 if (addr->saddr.sa_family == AF_INET6) {
307 return sizeof(addr->saddr_in6);
308 } else
309 #endif /* FREECIV_IPV6_SUPPORT */
310 if (addr->saddr.sa_family == AF_INET) {
311 return sizeof(addr->saddr_in4);
312 } else {
313 fc_assert(FALSE);
315 log_error("Unsupported address family in sockaddr_size()");
317 return 0;
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) {
329 return TRUE;
331 #endif /* IPv6 support */
333 return FALSE;
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,
341 int port,
342 enum fc_addr_family family)
344 struct addrinfo hints;
345 struct addrinfo *res;
346 int err;
347 char servname[8];
348 int gafam;
349 struct fc_sockaddr_list *addrs =
350 fc_sockaddr_list_new_full((fc_sockaddr_list_free_fn_t) free);
352 switch (family) {
353 case FC_ADDR_IPV4:
354 gafam = AF_INET;
355 break;
356 case FC_ADDR_IPV6:
357 gafam = AF_INET6;
358 break;
359 case FC_ADDR_ANY:
360 #ifndef FREECIV_IPV6_SUPPORT
361 gafam = AF_INET;
362 #else
363 gafam = AF_UNSPEC;
364 #endif
365 break;
366 default:
367 fc_assert(FALSE);
369 return addrs;
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);
382 if (err == 0) {
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;
396 freeaddrinfo(res);
399 return addrs;
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
410 * necessarily */
411 #ifdef HAVE_GETADDRINFO
412 return net_lookup_getaddrinfo(name, port, family);
413 #else /* HAVE_GETADDRINFO */
415 struct sockaddr_in *sock4;
416 struct hostent *hp;
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);
427 if (!name) {
428 sock4->sin_addr.s_addr = htonl(INADDR_ANY);
429 fc_sockaddr_list_append(addrs, result);
431 return addrs;
434 if (fc_inet_aton(name, &sock4->sin_addr, FALSE)) {
435 fc_sockaddr_list_append(addrs, result);
437 return addrs;
440 hp = gethostbyname(name);
441 if (!hp || hp->h_addrtype != AF_INET) {
442 FC_FREE(result);
444 return addrs;
447 memcpy(&sock4->sin_addr, hp->h_addr, hp->h_length);
448 fc_sockaddr_list_append(addrs, result);
450 return addrs;
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)) {
466 return FALSE;
468 #else /* IPv6 Support */
469 #ifdef HAVE_INET_ATON
470 if (!inet_aton(cp, inp)) {
471 return FALSE;
473 #else /* HAVE_INET_ATON */
474 inp->s_addr = inet_addr(cp);
475 if (!addr_none_ok && inp->s_addr == INADDR_NONE) {
476 return FALSE;
478 #endif /* HAVE_INET_ATON */
479 #endif /* IPv6 Support */
481 return TRUE;
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)
490 FILE *fp;
492 #ifdef HAVE_FDOPEN
493 fp = fdopen(sock, "r+b");
494 if (fwrite(buf, 1, size, fp) != size) {
495 log_error("socket %d: write error", sock);
497 fflush(fp);
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 */
503 char tmp[4096];
504 int n;
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 */
519 fp = tmpfile();
521 #endif /* FREECIV_MSWINDOWS */
523 if (fp == NULL) {
524 return NULL;
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);
534 fflush(fp);
536 fc_closesocket(sock);
538 rewind(fp);
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)
552 int port;
553 int s;
554 int gafamily;
555 bool found = FALSE;
557 #ifndef FREECIV_IPV6_SUPPORT
558 fc_assert(family == FC_ADDR_IPV4 || family == FC_ADDR_ANY);
559 #endif
561 switch (family) {
562 case FC_ADDR_IPV4:
563 gafamily = AF_INET;
564 break;
565 #ifdef FREECIV_IPV6_SUPPORT
566 case FC_ADDR_IPV6:
567 gafamily = AF_INET6;
568 break;
569 #endif /* FREECIV_IPV6_SUPPORT */
570 case FC_ADDR_ANY:
571 gafamily = AF_UNSPEC;
572 break;
573 default:
574 fc_assert(FALSE);
575 log_error("Port from unsupported address family requested!");
577 return -1;
580 for (port = starting_port; !found && highest_port > port; port++) {
581 /* HAVE_GETADDRINFO implies IPv6 support */
582 #ifdef HAVE_GETADDRINFO
583 struct addrinfo hints;
584 int err;
585 char servname[8];
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);
596 if (!err) {
597 struct addrinfo *current = res;
598 bool unusable = FALSE;
600 while (current != NULL && !unusable) {
601 s = socket(current->ai_family, SOCK_STREAM, 0);
603 if (s == -1) {
604 log_error("socket(): %s", fc_strerror(fc_get_errno()));
605 } else {
606 if (bind(s, current->ai_addr, current->ai_addrlen) != 0) {
607 if (!not_avail_ok || fc_get_errno() != EADDRNOTAVAIL) {
608 unusable = TRUE;
612 current = current->ai_next;
613 fc_closesocket(s);
616 freeaddrinfo(res);
618 if (!unusable && res != NULL) {
619 found = TRUE;
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)) {
634 struct hostent *hp;
636 hp = gethostbyname(net_interface);
637 if (hp == NULL) {
638 log_error("No hostent for %s!", net_interface);
640 return -1;
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);
646 return -1;
649 memcpy(&sock4->sin_addr, hp->h_addr, hp->h_length);
651 } else {
652 sock4->sin_addr.s_addr = htonl(INADDR_ANY);
655 if (bind(s, &tmp.saddr, sockaddr_size(&tmp)) == 0) {
656 found = TRUE;
659 fc_closesocket(s);
660 #endif /* HAVE_GETADDRINFO */
663 if (!found) {
664 log_error("None of the ports %d - %d is available.",
665 starting_port, highest_port);
667 return -1;
670 /* Rollback the last increment from the loop, back to the port
671 * number found to be free. */
672 port--;
674 return port;