connect.c 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. // (C) 2013, 2016 Cybozu.
  2. #include "yrmcds.h"
  3. #include <errno.h>
  4. #include <fcntl.h>
  5. #include <netdb.h>
  6. #include <netinet/in.h>
  7. #include <netinet/tcp.h>
  8. #include <poll.h>
  9. #include <stdio.h>
  10. #include <stdlib.h>
  11. #include <string.h>
  12. #include <sys/socket.h>
  13. #include <sys/types.h>
  14. #include <unistd.h>
  15. // workaround for a known-bug in NetBSD, from https://lists.gnu.org/archive/html/bug-gnulib/2010-02/msg00071.html
  16. #ifndef AI_V4MAPPED
  17. #define AI_V4MAPPED 0
  18. #endif
  19. static yrmcds_error connect_to_server(const char* node, uint16_t port, int* server_fd) {
  20. if( node == NULL )
  21. return YRMCDS_BAD_ARGUMENT;
  22. long fl;
  23. char sport[8];
  24. snprintf(sport, sizeof(sport), "%u", (unsigned int)port);
  25. struct addrinfo hint, *res;
  26. memset(&hint, 0, sizeof(hint));
  27. hint.ai_family = AF_INET; // prefer IPv4
  28. hint.ai_socktype = SOCK_STREAM;
  29. hint.ai_flags = AI_NUMERICSERV|AI_ADDRCONFIG;
  30. int e = getaddrinfo(node, sport, &hint, &res);
  31. if( e == EAI_FAMILY || e == EAI_NONAME
  32. #ifdef EAI_ADDRFAMILY
  33. || e == EAI_ADDRFAMILY
  34. #endif
  35. #ifdef EAI_NODATA
  36. || e == EAI_NODATA
  37. #endif
  38. ) {
  39. hint.ai_family = AF_INET6;
  40. // intentionally drop AI_ADDRCONFIG to support IPv6 link-local address.
  41. // see https://github.com/cybozu/yrmcds/issues/40
  42. hint.ai_flags = AI_NUMERICSERV|AI_V4MAPPED;
  43. e = getaddrinfo(node, sport, &hint, &res);
  44. }
  45. if( e == EAI_SYSTEM ) {
  46. return YRMCDS_SYSTEM_ERROR;
  47. } else if( e != 0 ) {
  48. return YRMCDS_NOT_RESOLVED;
  49. }
  50. int s = socket(res->ai_family,
  51. res->ai_socktype
  52. #ifdef __linux__
  53. | SOCK_NONBLOCK | SOCK_CLOEXEC
  54. #endif
  55. , res->ai_protocol);
  56. if( s == -1 ) {
  57. e = errno;
  58. freeaddrinfo(res);
  59. errno = e;
  60. return YRMCDS_SYSTEM_ERROR;
  61. }
  62. #ifndef __linux__
  63. fl = fcntl(s, F_GETFD, 0);
  64. fcntl(s, F_SETFD, fl | FD_CLOEXEC);
  65. fl = fcntl(s, F_GETFL, 0);
  66. fcntl(s, F_SETFL, fl | O_NONBLOCK);
  67. #endif
  68. e = connect(s, res->ai_addr, res->ai_addrlen);
  69. freeaddrinfo(res);
  70. if( e == -1 && errno != EINPROGRESS ) {
  71. e = errno;
  72. close(s);
  73. errno = e;
  74. return YRMCDS_SYSTEM_ERROR;
  75. }
  76. if( e != 0 ) {
  77. struct pollfd fds;
  78. fds.fd = s;
  79. fds.events = POLLOUT;
  80. int n = poll(&fds, 1, 5000);
  81. if( n == 0 ) { // timeout
  82. close(s);
  83. return YRMCDS_TIMEOUT;
  84. }
  85. if( n == -1 ) {
  86. e = errno;
  87. close(s);
  88. errno = e;
  89. return YRMCDS_SYSTEM_ERROR;
  90. }
  91. if( fds.revents & (POLLERR|POLLHUP|POLLNVAL) ) {
  92. close(s);
  93. return YRMCDS_DISCONNECTED;
  94. }
  95. socklen_t l = sizeof(e);
  96. if( getsockopt(s, SOL_SOCKET, SO_ERROR, &e, &l) == -1 ) {
  97. close(s);
  98. return YRMCDS_SYSTEM_ERROR;
  99. }
  100. if( e != 0 ) {
  101. close(s);
  102. errno = e;
  103. return YRMCDS_SYSTEM_ERROR;
  104. }
  105. }
  106. fl = fcntl(s, F_GETFL, 0);
  107. if( fcntl(s, F_SETFL, fl & ~O_NONBLOCK) == -1 ) {
  108. e = errno;
  109. close(s);
  110. errno = e;
  111. return YRMCDS_SYSTEM_ERROR;
  112. }
  113. int ok = 1;
  114. if( setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &ok, sizeof(ok)) == -1 ) {
  115. e = errno;
  116. close(s);
  117. errno = e;
  118. return YRMCDS_SYSTEM_ERROR;
  119. }
  120. *server_fd = s;
  121. return YRMCDS_OK;
  122. }
  123. yrmcds_error yrmcds_connect(yrmcds* c, const char* node, uint16_t port) {
  124. if( c == NULL )
  125. return YRMCDS_BAD_ARGUMENT;
  126. #ifndef LIBYRMCDS_NO_INTERNAL_LOCK
  127. int e = pthread_mutex_init(&(c->lock), NULL);
  128. if( e != 0 ) {
  129. errno = e;
  130. return YRMCDS_SYSTEM_ERROR;
  131. }
  132. #endif // ! LIBYRMCDS_NO_INTERNAL_LOCK
  133. int server_fd;
  134. yrmcds_error err = connect_to_server(node, port, &server_fd);
  135. if( err != YRMCDS_OK )
  136. return err;
  137. c->sock = server_fd;
  138. c->serial = 0;
  139. c->compress_size = 0;
  140. c->recvbuf = (char*)malloc(1 << 20);
  141. if( c->recvbuf == NULL ) {
  142. close(server_fd);
  143. #ifndef LIBYRMCDS_NO_INTERNAL_LOCK
  144. pthread_mutex_destroy(&(c->lock));
  145. #endif
  146. return YRMCDS_OUT_OF_MEMORY;
  147. }
  148. c->capacity = 1 << 20;
  149. c->used = 0;
  150. c->last_size = 0;
  151. c->decompressed = NULL;
  152. c->invalid = 0;
  153. c->text_mode = 0;
  154. c->rserial = 0;
  155. return YRMCDS_OK;
  156. }
  157. yrmcds_error yrmcds_cnt_connect(yrmcds_cnt* c, const char* node, uint16_t port) {
  158. if( c == NULL )
  159. return YRMCDS_BAD_ARGUMENT;
  160. #ifndef LIBYRMCDS_NO_INTERNAL_LOCK
  161. int e = pthread_mutex_init(&(c->lock), NULL);
  162. if( e != 0 ) {
  163. errno = e;
  164. return YRMCDS_SYSTEM_ERROR;
  165. }
  166. #endif // ! LIBYRMCDS_NO_INTERNAL_LOCK
  167. int server_fd;
  168. yrmcds_error err = connect_to_server(node, port, &server_fd);
  169. if( err != YRMCDS_OK )
  170. return err;
  171. c->sock = server_fd;
  172. c->serial = 0;
  173. c->recvbuf = (char*)malloc(4096);
  174. if( c->recvbuf == NULL ) {
  175. close(server_fd);
  176. #ifndef LIBYRMCDS_NO_INTERNAL_LOCK
  177. pthread_mutex_destroy(&(c->lock));
  178. #endif
  179. return YRMCDS_OUT_OF_MEMORY;
  180. }
  181. c->capacity = 4096;
  182. c->used = 0;
  183. c->last_size = 0;
  184. c->invalid = 0;
  185. c->stats.count = c->stats.capacity = 0;
  186. c->stats.records = NULL;
  187. return YRMCDS_OK;
  188. }