netlib.c 6.9 KB

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