netlib.c 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. /*
  2. * This file is Copyright 2010 by the GPSD project
  3. * SPDX-License-Identifier: BSD-2-clause
  4. */
  5. #include "../include/gpsd_config.h" // must be before all includes
  6. #ifdef HAVE_ARPA_INET_H
  7. # include <arpa/inet.h> // for htons() and friends
  8. #endif // HAVE_ARPA_INET_H
  9. #include <errno.h> // for errno
  10. #include <fcntl.h>
  11. #include <sys/types.h> // FreeBSD needs it for netinet/ip.h
  12. #ifdef HAVE_NETDB_H
  13. # include <netdb.h>
  14. #endif // HAVE_NETDB_H
  15. #ifndef INADDR_ANY
  16. # ifdef HAVE_NETINET_IN_H
  17. # include <netinet/in.h>
  18. # endif // HAVE_NETINET_IN_H
  19. #endif // INADDR_ANY
  20. #ifdef HAVE_NETINET_IN_H
  21. # include <netinet/ip.h>
  22. #endif // HAVE_NETINET_IN_H
  23. #include <string.h>
  24. #ifndef AF_UNSPEC
  25. # include <sys/stat.h>
  26. # ifdef HAVE_SYS_SOCKET_H
  27. # include <sys/socket.h>
  28. # endif // HAVE_SYS_SOCKET_H
  29. #endif // AF_UNSPEC
  30. #ifdef HAVE_SYS_UN_H
  31. # include <sys/un.h>
  32. #endif // HAVE_SYS_UN_H
  33. #include <unistd.h>
  34. #ifdef HAVE_WINSOCK2_H
  35. # include <winsock2.h>
  36. # include <ws2tcpip.h>
  37. #endif // HAVE_WINSOCK2_H
  38. #include "../include/gpsd.h"
  39. #include "../include/sockaddr.h"
  40. /* work around the unfinished ipv6 implementation on hurd and OSX <10.6 */
  41. #ifndef IPV6_TCLASS
  42. # if defined(__GNU__)
  43. # define IPV6_TCLASS 61
  44. # elif defined(__APPLE__)
  45. # define IPV6_TCLASS 36
  46. # endif
  47. #endif
  48. /* connect to host, using service (port) on protocol (TCP/UDP)
  49. * af - Adress Family
  50. * host - host to connect to
  51. * service -- aka port
  52. * protocol
  53. * flags -- can be SOCK_NONBLOCK for non-blocking connect
  54. * Note: macOS does not have SOCK_NONBLOCK
  55. * addrbuf -- 50 char buf to put string of IP address conencting
  56. * INET6_ADDRSTRLEN
  57. * addrbuf_sz -- sizeof(adddrbuf)
  58. *
  59. * Notes on nonblocking:
  60. * The connect may be non-blocking, but the DNS lookup is blocking.
  61. * On non-blocking connect only the first DNS entry is ever used.
  62. * FIXME: cache DNS to avoid DNS lookup on re-connect
  63. *
  64. * return socket on success
  65. * less than zero on error (NL_*)
  66. */
  67. socket_t netlib_connectsock1(int af, const char *host, const char *service,
  68. const char *protocol, int flags,
  69. char *addrbuf, size_t addrbuf_sz)
  70. {
  71. struct protoent *ppe;
  72. struct addrinfo hints;
  73. struct addrinfo *result = NULL;
  74. struct addrinfo *rp;
  75. int ret, type, proto, one = 1;
  76. socket_t s;
  77. bool bind_me;
  78. if (NULL != addrbuf) {
  79. addrbuf[0] = '\0';
  80. }
  81. INVALIDATE_SOCKET(s);
  82. ppe = getprotobyname(protocol);
  83. if (0 == strcmp(protocol, "udp")) {
  84. type = SOCK_DGRAM;
  85. proto = (ppe) ? ppe->p_proto : IPPROTO_UDP;
  86. } else if (0 == strcmp(protocol, "tcp")) {
  87. type = SOCK_STREAM;
  88. proto = (ppe) ? ppe->p_proto : IPPROTO_TCP;
  89. } else {
  90. // Unknown protocol (sctp, etc.)
  91. return NL_NOPROTO;
  92. }
  93. /* we probably ought to pass this in as an explicit flag argument */
  94. bind_me = (SOCK_DGRAM == type);
  95. memset(&hints, 0, sizeof(struct addrinfo));
  96. hints.ai_family = af;
  97. hints.ai_socktype = type;
  98. hints.ai_protocol = proto;
  99. if (bind_me) {
  100. hints.ai_flags = AI_PASSIVE;
  101. }
  102. // FIXME: need a way to bypass these DNS calls if host is an IP.
  103. if ((ret = getaddrinfo(host, service, &hints, &result))) {
  104. // result is unchanged on error, so we need to have set it to NULL
  105. // freeaddrinfo() checks for NULL, the NULL we provided.
  106. freeaddrinfo(result);
  107. result = NULL;
  108. // quick check to see if the problem was host or service
  109. ret = getaddrinfo(NULL, service, &hints, &result);
  110. freeaddrinfo(result);
  111. if (ret) {
  112. return NL_NOSERVICE;
  113. }
  114. return NL_NOHOST;
  115. }
  116. /*
  117. * Try to connect to each of the DNS returned addresses, one at a time.
  118. * Until success, or no more addresses.
  119. *
  120. * From getaddrinfo(3):
  121. * Normally, the application should try using the addresses in the
  122. * order in which they are returned. The sorting function used within
  123. * getaddrinfo() is defined in RFC 3484).
  124. * From RFC 3484 (Section 10.3):
  125. * The default policy table gives IPv6 addresses higher precedence than
  126. * IPv4 addresses.
  127. * Thus, with the default parameters, we get IPv6 addresses first.
  128. */
  129. for (rp = result; rp != NULL; rp = rp->ai_next) {
  130. ret = NL_NOCONNECT;
  131. // flags might be zero or SOCK_NONBLOCK
  132. if (0 > (s = socket(rp->ai_family, rp->ai_socktype | flags,
  133. rp->ai_protocol))) {
  134. // can't get a socket. Maybe should give up right away?
  135. ret = NL_NOSOCK;
  136. continue;
  137. }
  138. // allow reuse of local address is in TIMEWAIT state
  139. // useful on a quick gpsd restart to reuse the address.
  140. if (-1 == setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&one,
  141. sizeof(one))) {
  142. ret = NL_NOSOCKOPT;
  143. } else if (bind_me) {
  144. // want a passive socket (SOCK_DGRAM), UDP.
  145. if (0 == bind(s, rp->ai_addr, rp->ai_addrlen)) {
  146. // got a good one
  147. ret = 0;
  148. break;
  149. }
  150. } else if (0 == connect(s, rp->ai_addr, rp->ai_addrlen)) {
  151. // got a good connection
  152. ret = 0;
  153. break;
  154. } else if (EINPROGRESS == errno) {
  155. // EINPROGRESS means non-blocking connect() in progress
  156. // we will not try next address...
  157. // separate case from 0 == connect() to ease debug.
  158. ret = 0;
  159. break;
  160. }
  161. if (NULL != addrbuf) {
  162. // save the IP used, as a string. into addrbuf
  163. if (NULL == inet_ntop(af, rp->ai_addr, addrbuf, addrbuf_sz)) {
  164. addrbuf[0] = '\0';
  165. }
  166. }
  167. if (!BAD_SOCKET(s)) {
  168. #ifdef HAVE_WINSOCK2_H
  169. (void)closesocket(s);
  170. #else
  171. (void)close(s);
  172. #endif
  173. }
  174. }
  175. freeaddrinfo(result);
  176. if (0 != ret ||
  177. BAD_SOCKET(s)) {
  178. return ret;
  179. }
  180. #ifdef IPTOS_LOWDELAY
  181. {
  182. int opt = IPTOS_LOWDELAY;
  183. (void)setsockopt(s, IPPROTO_IP, IP_TOS, &opt, sizeof(opt));
  184. #ifdef IPV6_TCLASS
  185. (void)setsockopt(s, IPPROTO_IPV6, IPV6_TCLASS, &opt, sizeof(opt));
  186. #endif
  187. }
  188. #endif
  189. #ifdef TCP_NODELAY
  190. /*
  191. * This is a good performance enhancement when the socket is going to
  192. * be used to pass a lot of short commands. It prevents them from being
  193. * delayed by the Nagle algorithm until they can be aggreagated into
  194. * a large packet. See https://en.wikipedia.org/wiki/Nagle%27s_algorithm
  195. * for discussion.
  196. */
  197. if (SOCK_STREAM == type) {
  198. setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char *)&one, sizeof(one));
  199. }
  200. #endif
  201. // set socket to noblocking
  202. #ifdef HAVE_FCNTL
  203. (void)fcntl(s, F_SETFL, fcntl(s, F_GETFL) | O_NONBLOCK);
  204. #elif defined(HAVE_WINSOCK2_H)
  205. u_long one1 = 1;
  206. (void)ioctlsocket(s, FIONBIO, &one1);
  207. #endif
  208. return s;
  209. }
  210. // legacy entry point
  211. // just call netlib_connectsock() with options = 0;
  212. // return the result
  213. socket_t netlib_connectsock(int af, const char *host, const char *service,
  214. const char *protocol)
  215. {
  216. return netlib_connectsock1(af, host, service, protocol, 0, NULL, 0);
  217. }
  218. // Convert NL_* error code to a string
  219. const char *netlib_errstr(const int err)
  220. {
  221. switch (err) {
  222. case NL_NOSERVICE:
  223. return "can't get service entry";
  224. case NL_NOHOST:
  225. return "can't get host entry";
  226. case NL_NOPROTO:
  227. return "can't get protocol entry";
  228. case NL_NOSOCK:
  229. return "can't create socket";
  230. case NL_NOSOCKOPT:
  231. return "error SETSOCKOPT SO_REUSEADDR";
  232. case NL_NOCONNECT:
  233. return "can't connect to host/port pair";
  234. default:
  235. return "unknown error";
  236. }
  237. }
  238. // acquire a connection to an existing Unix-domain socket
  239. socket_t netlib_localsocket(const char *sockfile, int socktype)
  240. {
  241. #ifdef HAVE_SYS_UN_H
  242. int sock;
  243. if (0 > (sock = socket(AF_UNIX, socktype, 0))) {
  244. return -1;
  245. } else {
  246. struct sockaddr_un saddr;
  247. memset(&saddr, 0, sizeof(struct sockaddr_un));
  248. saddr.sun_family = AF_UNIX;
  249. (void)strlcpy(saddr.sun_path, sockfile, sizeof(saddr.sun_path));
  250. if (0 < connect(sock, (struct sockaddr *)&saddr, SUN_LEN(&saddr))) {
  251. (void)close(sock);
  252. return -2;
  253. }
  254. return sock;
  255. }
  256. #else
  257. return -1;
  258. #endif // HAVE_SYS_UN_H
  259. }
  260. // retrieve the IP address corresponding to a socket
  261. char *netlib_sock2ip(socket_t fd)
  262. {
  263. static char ip[INET6_ADDRSTRLEN];
  264. #ifdef HAVE_INET_NTOP
  265. int r;
  266. sockaddr_t fsin;
  267. socklen_t alen = (socklen_t) sizeof(fsin);
  268. r = getpeername(fd, &(fsin.sa), &alen);
  269. if (0 == r) {
  270. switch (fsin.sa.sa_family) {
  271. case AF_INET:
  272. r = !inet_ntop(fsin.sa_in.sin_family, &(fsin.sa_in.sin_addr),
  273. ip, sizeof(ip));
  274. break;
  275. case AF_INET6:
  276. r = !inet_ntop((int)fsin.sa_in6.sin6_family,
  277. &(fsin.sa_in6.sin6_addr), ip, sizeof(ip));
  278. break;
  279. default:
  280. (void)strlcpy(ip, "<unknown AF>", sizeof(ip));
  281. return ip;
  282. }
  283. }
  284. // Ugh...
  285. if (0 != r)
  286. #endif // HAVE_INET_NTOP
  287. (void)strlcpy(ip, "<unknown>", sizeof(ip));
  288. return ip;
  289. }
  290. // vim: set expandtab shiftwidth=4