1 /*
2  * This file is Copyright (c) 2010-2018 by the GPSD project
3  * SPDX-License-Identifier: BSD-2-clause
4  */
5 
6 #include "gpsd_config.h"  /* must be before all includes */
7 
8 #include <string.h>
9 #include <fcntl.h>
10 #ifdef HAVE_NETDB_H
11 #include <netdb.h>
12 #endif /* HAVE_NETDB_H */
13 #ifndef AF_UNSPEC
14 #include <sys/types.h>
15 #include <sys/stat.h>
16 #ifdef HAVE_SYS_SOCKET_H
17 #include <sys/socket.h>
18 #endif /* HAVE_SYS_SOCKET_H */
19 #endif /* AF_UNSPEC */
20 #ifdef HAVE_SYS_UN_H
21 #include <sys/un.h>
22 #endif /* HAVE_SYS_UN_H */
23 #ifndef INADDR_ANY
24 #ifdef HAVE_NETINET_IN_H
25 #include <netinet/in.h>
26 #endif /* HAVE_NETINET_IN_H */
27 #endif /* INADDR_ANY */
28 #ifdef HAVE_ARPA_INET_H
29 #include <arpa/inet.h>     /* for htons() and friends */
30 #endif /* HAVE_ARPA_INET_H */
31 #include <unistd.h>
32 #ifdef HAVE_NETINET_IN_H
33 #include <netinet/ip.h>
34 #endif /* HAVE_NETINET_IN_H */
35 #ifdef HAVE_WINSOCK2_H
36 #include <winsock2.h>
37 #include <ws2tcpip.h>
38 #endif
39 
40 #include "gpsd.h"
41 #include "sockaddr.h"
42 
43 /* work around the unfinished ipv6 implementation on hurd and OSX <10.6 */
44 #ifndef IPV6_TCLASS
45 # if defined(__GNU__)
46 #  define IPV6_TCLASS 61
47 # elif defined(__APPLE__)
48 #  define IPV6_TCLASS 36
49 # endif
50 #endif
51 
netlib_connectsock(int af,const char * host,const char * service,const char * protocol)52 socket_t netlib_connectsock(int af, const char *host, const char *service,
53 			    const char *protocol)
54 {
55     struct protoent *ppe;
56     struct addrinfo hints;
57     struct addrinfo *result, *rp;
58     int ret, type, proto, one = 1;
59     socket_t s;
60     bool bind_me;
61 
62     INVALIDATE_SOCKET(s);
63     ppe = getprotobyname(protocol);
64     if (strcmp(protocol, "udp") == 0) {
65 	type = SOCK_DGRAM;
66 	proto = (ppe) ? ppe->p_proto : IPPROTO_UDP;
67     } else {
68 	type = SOCK_STREAM;
69 	proto = (ppe) ? ppe->p_proto : IPPROTO_TCP;
70     }
71 
72     /* we probably ought to pass this in as an explicit flag argument */
73     bind_me = (type == SOCK_DGRAM);
74 
75     memset(&hints, 0, sizeof(struct addrinfo));
76     hints.ai_family = af;
77     hints.ai_socktype = type;
78     hints.ai_protocol = proto;
79     if (bind_me)
80 	hints.ai_flags = AI_PASSIVE;
81     if ((ret = getaddrinfo(host, service, &hints, &result))) {
82 	return NL_NOHOST;
83     }
84 
85     /*
86      * From getaddrinfo(3):
87      *     Normally, the application should try using the addresses in the
88      *     order in which they are returned.  The sorting function used within
89      *     getaddrinfo() is defined in RFC 3484).
90      * From RFC 3484 (Section 10.3):
91      *     The default policy table gives IPv6 addresses higher precedence than
92      *     IPv4 addresses.
93      * Thus, with the default parameters, we get IPv6 addresses first.
94      */
95     for (rp = result; rp != NULL; rp = rp->ai_next) {
96 	ret = NL_NOCONNECT;
97 	if ((s = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol)) < 0)
98 	    ret = NL_NOSOCK;
99 	else if (setsockopt
100 		 (s, SOL_SOCKET, SO_REUSEADDR, (char *)&one,
101 		  sizeof(one)) == -1) {
102 	    ret = NL_NOSOCKOPT;
103 	} else {
104 	    if (bind_me) {
105 		if (bind(s, rp->ai_addr, rp->ai_addrlen) == 0) {
106 		    ret = 0;
107 		    break;
108 		}
109 	    } else {
110 		if (connect(s, rp->ai_addr, rp->ai_addrlen) == 0) {
111 		    ret = 0;
112 		    break;
113 		}
114 	    }
115 	}
116 
117 	if (!BAD_SOCKET(s)) {
118 #ifdef HAVE_WINSOCK2_H
119 	  (void)closesocket(s);
120 #else
121 	  (void)close(s);
122 #endif
123 	}
124     }
125     freeaddrinfo(result);
126     if (ret != 0 || BAD_SOCKET(s))
127 	return ret;
128 
129 #ifdef IPTOS_LOWDELAY
130     {
131 	int opt = IPTOS_LOWDELAY;
132 	(void)setsockopt(s, IPPROTO_IP, IP_TOS, &opt, sizeof(opt));
133 #ifdef IPV6_TCLASS
134 	(void)setsockopt(s, IPPROTO_IPV6, IPV6_TCLASS, &opt, sizeof(opt));
135 #endif
136     }
137 #endif
138 #ifdef TCP_NODELAY
139     /*
140      * This is a good performance enhancement when the socket is going to
141      * be used to pass a lot of short commands.  It prevents them from being
142      * delayed by the Nagle algorithm until they can be aggreagated into
143      * a large packet.  See https://en.wikipedia.org/wiki/Nagle%27s_algorithm
144      * for discussion.
145      */
146     if (type == SOCK_STREAM)
147 	setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char *)&one, sizeof(one));
148 #endif
149 
150     /* set socket to noblocking */
151 #ifdef HAVE_FCNTL
152     (void)fcntl(s, F_SETFL, fcntl(s, F_GETFL) | O_NONBLOCK);
153 #elif defined(HAVE_WINSOCK2_H)
154     u_long one1 = 1;
155     (void)ioctlsocket(s, FIONBIO, &one1);
156 #endif
157     return s;
158 }
159 
160 
netlib_errstr(const int err)161 const char *netlib_errstr(const int err)
162 {
163     switch (err) {
164     case NL_NOSERVICE:
165 	return "can't get service entry";
166     case NL_NOHOST:
167 	return "can't get host entry";
168     case NL_NOPROTO:
169 	return "can't get protocol entry";
170     case NL_NOSOCK:
171 	return "can't create socket";
172     case NL_NOSOCKOPT:
173 	return "error SETSOCKOPT SO_REUSEADDR";
174     case NL_NOCONNECT:
175 	return "can't connect to host/port pair";
176     default:
177 	return "unknown error";
178     }
179 }
180 
netlib_localsocket(const char * sockfile,int socktype)181 socket_t netlib_localsocket(const char *sockfile, int socktype)
182 /* acquire a connection to an existing Unix-domain socket */
183 {
184 #ifdef HAVE_SYS_UN_H
185     int sock;
186 
187     if ((sock = socket(AF_UNIX, socktype, 0)) < 0) {
188 	return -1;
189     } else {
190 	struct sockaddr_un saddr;
191 
192 	memset(&saddr, 0, sizeof(struct sockaddr_un));
193 	saddr.sun_family = AF_UNIX;
194 	(void)strlcpy(saddr.sun_path,
195 		      sockfile,
196 		      sizeof(saddr.sun_path));
197 
198 	if (connect(sock, (struct sockaddr *)&saddr, SUN_LEN(&saddr)) < 0) {
199 	    (void)close(sock);
200 	    return -2;
201 	}
202 
203 	return sock;
204     }
205 #else
206     return -1;
207 #endif /* HAVE_SYS_UN_H */
208 }
209 
netlib_sock2ip(socket_t fd)210 char *netlib_sock2ip(socket_t fd)
211 /* retrieve the IP address corresponding to a socket */
212 {
213     static char ip[INET6_ADDRSTRLEN];
214 #ifdef HAVE_INET_NTOP
215     int r;
216     sockaddr_t fsin;
217     socklen_t alen = (socklen_t) sizeof(fsin);
218     r = getpeername(fd, &(fsin.sa), &alen);
219     if (r == 0) {
220 	switch (fsin.sa.sa_family) {
221 	case AF_INET:
222 	    r = !inet_ntop(fsin.sa_in.sin_family, &(fsin.sa_in.sin_addr),
223 			   ip, sizeof(ip));
224 	    break;
225 	case AF_INET6:
226 	    r = !inet_ntop((int)fsin.sa_in6.sin6_family,
227                            &(fsin.sa_in6.sin6_addr), ip, sizeof(ip));
228 	    break;
229 	default:
230 	    (void)strlcpy(ip, "<unknown AF>", sizeof(ip));
231 	    return ip;
232 	}
233     }
234     /* Ugh... */
235     if (r != 0)
236 #endif /* HAVE_INET_NTOP */
237 	(void)strlcpy(ip, "<unknown>", sizeof(ip));
238     return ip;
239 }
240