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