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