123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240 |
- /*
- * This file is Copyright (c) 2010-2018 by the GPSD project
- * SPDX-License-Identifier: BSD-2-clause
- */
- #include "gpsd_config.h" /* must be before all includes */
- #include <string.h>
- #include <fcntl.h>
- #ifdef HAVE_NETDB_H
- #include <netdb.h>
- #endif /* HAVE_NETDB_H */
- #ifndef AF_UNSPEC
- #include <sys/types.h>
- #include <sys/stat.h>
- #ifdef HAVE_SYS_SOCKET_H
- #include <sys/socket.h>
- #endif /* HAVE_SYS_SOCKET_H */
- #endif /* AF_UNSPEC */
- #ifdef HAVE_SYS_UN_H
- #include <sys/un.h>
- #endif /* HAVE_SYS_UN_H */
- #ifndef INADDR_ANY
- #ifdef HAVE_NETINET_IN_H
- #include <netinet/in.h>
- #endif /* HAVE_NETINET_IN_H */
- #endif /* INADDR_ANY */
- #ifdef HAVE_ARPA_INET_H
- #include <arpa/inet.h> /* for htons() and friends */
- #endif /* HAVE_ARPA_INET_H */
- #include <unistd.h>
- #ifdef HAVE_NETINET_IN_H
- #include <netinet/ip.h>
- #endif /* HAVE_NETINET_IN_H */
- #ifdef HAVE_WINSOCK2_H
- #include <winsock2.h>
- #include <ws2tcpip.h>
- #endif
- #include "gpsd.h"
- #include "sockaddr.h"
- /* work around the unfinished ipv6 implementation on hurd and OSX <10.6 */
- #ifndef IPV6_TCLASS
- # if defined(__GNU__)
- # define IPV6_TCLASS 61
- # elif defined(__APPLE__)
- # define IPV6_TCLASS 36
- # endif
- #endif
- socket_t netlib_connectsock(int af, const char *host, const char *service,
- const char *protocol)
- {
- struct protoent *ppe;
- struct addrinfo hints;
- struct addrinfo *result, *rp;
- int ret, type, proto, one = 1;
- socket_t s;
- bool bind_me;
- INVALIDATE_SOCKET(s);
- ppe = getprotobyname(protocol);
- if (strcmp(protocol, "udp") == 0) {
- type = SOCK_DGRAM;
- proto = (ppe) ? ppe->p_proto : IPPROTO_UDP;
- } else {
- type = SOCK_STREAM;
- proto = (ppe) ? ppe->p_proto : IPPROTO_TCP;
- }
- /* we probably ought to pass this in as an explicit flag argument */
- bind_me = (type == SOCK_DGRAM);
- memset(&hints, 0, sizeof(struct addrinfo));
- hints.ai_family = af;
- hints.ai_socktype = type;
- hints.ai_protocol = proto;
- if (bind_me)
- hints.ai_flags = AI_PASSIVE;
- if ((ret = getaddrinfo(host, service, &hints, &result))) {
- return NL_NOHOST;
- }
- /*
- * From getaddrinfo(3):
- * Normally, the application should try using the addresses in the
- * order in which they are returned. The sorting function used within
- * getaddrinfo() is defined in RFC 3484).
- * From RFC 3484 (Section 10.3):
- * The default policy table gives IPv6 addresses higher precedence than
- * IPv4 addresses.
- * Thus, with the default parameters, we get IPv6 addresses first.
- */
- for (rp = result; rp != NULL; rp = rp->ai_next) {
- ret = NL_NOCONNECT;
- if ((s = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol)) < 0)
- ret = NL_NOSOCK;
- else if (setsockopt
- (s, SOL_SOCKET, SO_REUSEADDR, (char *)&one,
- sizeof(one)) == -1) {
- ret = NL_NOSOCKOPT;
- } else {
- if (bind_me) {
- if (bind(s, rp->ai_addr, rp->ai_addrlen) == 0) {
- ret = 0;
- break;
- }
- } else {
- if (connect(s, rp->ai_addr, rp->ai_addrlen) == 0) {
- ret = 0;
- break;
- }
- }
- }
- if (!BAD_SOCKET(s)) {
- #ifdef HAVE_WINSOCK2_H
- (void)closesocket(s);
- #else
- (void)close(s);
- #endif
- }
- }
- freeaddrinfo(result);
- if (ret != 0 || BAD_SOCKET(s))
- return ret;
- #ifdef IPTOS_LOWDELAY
- {
- int opt = IPTOS_LOWDELAY;
- (void)setsockopt(s, IPPROTO_IP, IP_TOS, &opt, sizeof(opt));
- #ifdef IPV6_TCLASS
- (void)setsockopt(s, IPPROTO_IPV6, IPV6_TCLASS, &opt, sizeof(opt));
- #endif
- }
- #endif
- #ifdef TCP_NODELAY
- /*
- * This is a good performance enhancement when the socket is going to
- * be used to pass a lot of short commands. It prevents them from being
- * delayed by the Nagle algorithm until they can be aggreagated into
- * a large packet. See https://en.wikipedia.org/wiki/Nagle%27s_algorithm
- * for discussion.
- */
- if (type == SOCK_STREAM)
- setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char *)&one, sizeof(one));
- #endif
- /* set socket to noblocking */
- #ifdef HAVE_FCNTL
- (void)fcntl(s, F_SETFL, fcntl(s, F_GETFL) | O_NONBLOCK);
- #elif defined(HAVE_WINSOCK2_H)
- u_long one1 = 1;
- (void)ioctlsocket(s, FIONBIO, &one1);
- #endif
- return s;
- }
- const char *netlib_errstr(const int err)
- {
- switch (err) {
- case NL_NOSERVICE:
- return "can't get service entry";
- case NL_NOHOST:
- return "can't get host entry";
- case NL_NOPROTO:
- return "can't get protocol entry";
- case NL_NOSOCK:
- return "can't create socket";
- case NL_NOSOCKOPT:
- return "error SETSOCKOPT SO_REUSEADDR";
- case NL_NOCONNECT:
- return "can't connect to host/port pair";
- default:
- return "unknown error";
- }
- }
- socket_t netlib_localsocket(const char *sockfile, int socktype)
- /* acquire a connection to an existing Unix-domain socket */
- {
- #ifdef HAVE_SYS_UN_H
- int sock;
- if ((sock = socket(AF_UNIX, socktype, 0)) < 0) {
- return -1;
- } else {
- struct sockaddr_un saddr;
- memset(&saddr, 0, sizeof(struct sockaddr_un));
- saddr.sun_family = AF_UNIX;
- (void)strlcpy(saddr.sun_path,
- sockfile,
- sizeof(saddr.sun_path));
- if (connect(sock, (struct sockaddr *)&saddr, SUN_LEN(&saddr)) < 0) {
- (void)close(sock);
- return -2;
- }
- return sock;
- }
- #else
- return -1;
- #endif /* HAVE_SYS_UN_H */
- }
- char *netlib_sock2ip(socket_t fd)
- /* retrieve the IP address corresponding to a socket */
- {
- static char ip[INET6_ADDRSTRLEN];
- #ifdef HAVE_INET_NTOP
- int r;
- sockaddr_t fsin;
- socklen_t alen = (socklen_t) sizeof(fsin);
- r = getpeername(fd, &(fsin.sa), &alen);
- if (r == 0) {
- switch (fsin.sa.sa_family) {
- case AF_INET:
- r = !inet_ntop(fsin.sa_in.sin_family, &(fsin.sa_in.sin_addr),
- ip, sizeof(ip));
- break;
- case AF_INET6:
- r = !inet_ntop((int)fsin.sa_in6.sin6_family,
- &(fsin.sa_in6.sin6_addr), ip, sizeof(ip));
- break;
- default:
- (void)strlcpy(ip, "<unknown AF>", sizeof(ip));
- return ip;
- }
- }
- /* Ugh... */
- if (r != 0)
- #endif /* HAVE_INET_NTOP */
- (void)strlcpy(ip, "<unknown>", sizeof(ip));
- return ip;
- }
|