Freeciv-3.3
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 if (fwrite(buf, 1, size, fp) != size) {
500 log_error("socket %d: write error", sock);
501 }
502 fflush(fp);
503
504 /* we don't use fc_closesocket on sock here since when fp is closed
505 * sock will also be closed. fdopen doesn't dup the socket descriptor. */
506#else /* HAVE_FDOPEN */
507 {
508 char tmp[4096];
509 int n;
510
511#ifdef FREECIV_MSWINDOWS
512 /* tmpfile() in mingw attempts to make a temp file in the root directory
513 * of the current drive, which we may not have write access to. */
514 {
515 char filename[MAX_PATH];
516
517 GetTempPath(sizeof(filename), filename);
518 sz_strlcat(filename, "fctmp");
519
520 fp = fc_fopen(filename, "w+b");
521 }
522#else /* FREECIV_MSWINDOWS */
523
524 fp = tmpfile();
525
526#endif /* FREECIV_MSWINDOWS */
527
528 if (fp == NULL) {
529 return NULL;
530 }
531
532 fc_writesocket(sock, buf, size);
533
534 while ((n = fc_readsocket(sock, tmp, sizeof(tmp))) > 0) {
535 if (fwrite(tmp, 1, n, fp) != n) {
536 log_error("socket %d: write error", sock);
537 }
538 }
539 fflush(fp);
540
541 fc_closesocket(sock);
542
543 rewind(fp);
544 }
545#endif /* HAVE_FDOPEN */
546
547 return fz_from_stream(fp);
548}
549
550/*********************************************************************/
555 char *net_interface, bool not_avail_ok)
556{
557 int port;
558 int s;
559 int gafamily;
560 bool found = FALSE;
561
562#ifndef FREECIV_IPV6_SUPPORT
564#endif
565
566 switch (family) {
567 case FC_ADDR_IPV4:
569 break;
570#ifdef FREECIV_IPV6_SUPPORT
571 case FC_ADDR_IPV6:
573 break;
574#endif /* FREECIV_IPV6_SUPPORT */
575 case FC_ADDR_ANY:
577 break;
578 default:
580 log_error("Port from unsupported address family requested!");
581
582 return -1;
583 }
584
585 for (port = starting_port; !found && highest_port > port; port++) {
586 /* HAVE_GETADDRINFO implies IPv6 support */
587#ifdef HAVE_GETADDRINFO
588 struct addrinfo hints;
589 int err;
590 char servname[8];
591 struct addrinfo *res;
592
593 fc_snprintf(servname, sizeof(servname), "%d", port);
594
595 memset(&hints, 0, sizeof(hints));
596 hints.ai_family = gafamily;
597 hints.ai_socktype = SOCK_DGRAM;
599
601 if (!err) {
602 struct addrinfo *current = res;
603 bool unusable = FALSE;
604
605 while (current != NULL && !unusable) {
606 s = socket(current->ai_family, SOCK_STREAM, 0);
607
608 if (s == -1) {
609 log_error("socket(): %s", fc_strerror(fc_get_errno()));
610 } else {
611 if (bind(s, current->ai_addr, current->ai_addrlen) != 0) {
613 unusable = TRUE;
614 }
615 }
616 }
617 current = current->ai_next;
619 }
620
622
623 if (!unusable && res != NULL) {
624 found = TRUE;
625 }
626 }
627#else /* HAVE_GETADDRINFO */
628 union fc_sockaddr tmp;
629 struct sockaddr_in *sock4;
630
631 s = socket(gafamily, SOCK_STREAM, 0);
632
633 sock4 = &tmp.saddr_in4;
634 memset(&tmp, 0, sizeof(tmp));
635 sock4->sin_family = AF_INET;
636 sock4->sin_port = htons(port);
637 if (net_interface != NULL) {
638 if (!fc_inet_aton(net_interface, &sock4->sin_addr, FALSE)) {
639 struct hostent *hp;
640
642 if (hp == NULL) {
643 log_error("No hostent for %s!", net_interface);
644
645 return -1;
646 }
647 if (hp->h_addrtype != AF_INET) {
648 log_error("Requested IPv4 address for %s, got something else! (%d)",
649 net_interface, hp->h_addrtype);
650
651 return -1;
652 }
653
654 memcpy(&sock4->sin_addr, hp->h_addr, hp->h_length);
655 }
656 } else {
657 sock4->sin_addr.s_addr = htonl(INADDR_ANY);
658 }
659
660 if (bind(s, &tmp.saddr, sockaddr_size(&tmp)) == 0) {
661 found = TRUE;
662 }
663
665#endif /* HAVE_GETADDRINFO */
666 }
667
668 if (!found) {
669 log_error("None of the ports %d - %d is available.",
671
672 return -1;
673 }
674
675 /* Rollback the last increment from the loop, back to the port
676 * number found to be free. */
677 port--;
678
679 return port;
680}
681
682/*********************************************************************/
686{
687 switch (announce) {
688 case ANNOUNCE_IPV6:
689 return AF_INET;
690 case ANNOUNCE_IPV4:
691 return AF_INET;
692 case ANNOUNCE_NONE:
693 return AF_UNSPEC;
694 }
695
697
698 return AF_UNSPEC;
699}
#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:74
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:553
int addr_family_for_announce_type(enum announce_type announce)
Definition netintf.c:685
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:960
const char * fc_strerror(fc_errno err)
Definition support.c:609
FILE * fc_fopen(const char *filename, const char *opentype)
Definition support.c:505
fc_errno fc_get_errno(void)
Definition support.c:592
#define TRUE
Definition support.h:46
#define FALSE
Definition support.h:47
#define sz_strlcat(dest, src)
Definition support.h:190
struct sockaddr saddr
Definition netintf.h:71
struct sockaddr_in saddr_in4
Definition netintf.h:72