2 +--------------------------------------------------------------------+
3 | libmemcached - C/C++ Client Library for memcached |
4 +--------------------------------------------------------------------+
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, are permitted under the terms of the BSD license. |
7 | You should have received a copy of the license in a bundled file |
8 | named LICENSE; in case you did not receive a copy you can review |
9 | the terms online at: https://opensource.org/licenses/BSD-3-Clause |
10 +--------------------------------------------------------------------+
11 | Copyright (c) 2006-2014 Brian Aker https://datadifferential.com/ |
12 | Copyright (c) 2020 Michael Wallner <mike@php.net> |
13 +--------------------------------------------------------------------+
16 #include "libmemcached/common.h"
21 # define SOCK_CLOEXEC 0
25 # define SOCK_NONBLOCK 0
33 # define SO_NOSIGPIPE 0
37 # define TCP_NODELAY 0
41 # define TCP_KEEPIDLE 0
44 static memcached_return_t
connect_poll(memcached_instance_st
*server
, const int connection_error
) {
46 fds
[0].fd
= server
->fd
;
47 fds
[0].events
= server
->events();
52 if (server
->root
->poll_timeout
== 0) {
53 return memcached_set_error(
54 *server
, MEMCACHED_TIMEOUT
, MEMCACHED_AT
,
55 memcached_literal_param("The time to wait for a connection to be established was set to "
56 "zero which produces a timeout to every call to poll()."));
59 while (--loop_max
) // Should only loop on cases of ERESTART or EINTR
62 if ((number_of
= poll(fds
, 1, server
->root
->connect_timeout
)) == -1) {
63 int local_errno
= get_socket_errno(); // We cache in case closesocket() modifies errno
64 switch (local_errno
) {
73 return memcached_set_error(*server
, MEMCACHED_MEMORY_ALLOCATION_FAILURE
, MEMCACHED_AT
);
76 return memcached_set_error(
77 *server
, MEMCACHED_MEMORY_ALLOCATION_FAILURE
, MEMCACHED_AT
,
78 memcached_literal_param(
79 "RLIMIT_NOFILE exceeded, or if OSX the timeout value was invalid"));
81 default: // This should not happen
85 assert_msg(server
->fd
!= INVALID_SOCKET
, "poll() was passed an invalid file descriptor");
86 server
->reset_socket();
87 server
->state
= MEMCACHED_SERVER_STATE_NEW
;
89 return memcached_set_errno(*server
, local_errno
, MEMCACHED_AT
);
93 if (connection_error
== EINPROGRESS
) {
95 socklen_t len
= sizeof(err
);
96 if (getsockopt(server
->fd
, SOL_SOCKET
, SO_ERROR
, (char *) &err
, &len
) == -1) {
97 return memcached_set_errno(
98 *server
, errno
, MEMCACHED_AT
,
99 memcached_literal_param(
100 "getsockopt() error'ed while looking for error connect_poll(EINPROGRESS)"));
103 // If Zero, my hero, we just fail to a generic MEMCACHED_TIMEOUT error
105 return memcached_set_errno(
106 *server
, err
, MEMCACHED_AT
,
107 memcached_literal_param("getsockopt() found the error from poll() after connect() "
108 "returned EINPROGRESS."));
112 return memcached_set_error(*server
, MEMCACHED_TIMEOUT
, MEMCACHED_AT
,
113 memcached_literal_param("(number_of == 0)"));
116 assert(number_of
== 1);
118 if (fds
[0].revents
& POLLERR
or fds
[0].revents
& POLLHUP
or fds
[0].revents
& POLLNVAL
) {
120 socklen_t len
= sizeof(err
);
121 if (getsockopt(fds
[0].fd
, SOL_SOCKET
, SO_ERROR
, (char *) &err
, &len
) == -1) {
122 return memcached_set_errno(
123 *server
, errno
, MEMCACHED_AT
,
124 memcached_literal_param(
125 "getsockopt() errored while looking up error state from poll()"));
128 // We check the value to see what happened with the socket.
129 if (err
== 0) // Should not happen
131 return MEMCACHED_SUCCESS
;
135 return memcached_set_errno(
136 *server
, err
, MEMCACHED_AT
,
137 memcached_literal_param("getsockopt() found the error from poll() during connect."));
139 assert(fds
[0].revents
& POLLOUT
);
141 if (fds
[0].revents
& POLLOUT
and connection_error
== EINPROGRESS
) {
143 socklen_t len
= sizeof(err
);
144 if (getsockopt(server
->fd
, SOL_SOCKET
, SO_ERROR
, (char *) &err
, &len
) == -1) {
145 return memcached_set_errno(*server
, errno
, MEMCACHED_AT
);
149 return MEMCACHED_SUCCESS
;
152 return memcached_set_errno(
153 *server
, err
, MEMCACHED_AT
,
154 memcached_literal_param(
155 "getsockopt() found the error from poll() after connect() returned EINPROGRESS."));
158 break; // We only have the loop setup for errno types that require restart
161 // This should only be possible from ERESTART or EINTR;
162 return memcached_set_errno(*server
, connection_error
, MEMCACHED_AT
,
163 memcached_literal_param("connect_poll() was exhausted"));
166 static memcached_return_t
set_hostinfo(memcached_instance_st
*server
) {
167 assert(server
->type
!= MEMCACHED_CONNECTION_UNIX_SOCKET
);
168 server
->clear_addrinfo();
170 char str_port
[MEMCACHED_NI_MAXSERV
] = {0};
172 int length
= snprintf(str_port
, MEMCACHED_NI_MAXSERV
, "%u", uint32_t(server
->port()));
173 if (length
>= MEMCACHED_NI_MAXSERV
or length
<= 0 or errno
) {
174 return memcached_set_error(*server
, MEMCACHED_MEMORY_ALLOCATION_FAILURE
, MEMCACHED_AT
,
175 memcached_literal_param("snprintf(NI_MAXSERV)"));
178 struct addrinfo hints
;
179 memset(&hints
, 0, sizeof(struct addrinfo
));
181 hints
.ai_family
= AF_UNSPEC
;
182 if (memcached_is_udp(server
->root
)) {
183 hints
.ai_protocol
= IPPROTO_UDP
;
184 hints
.ai_socktype
= SOCK_DGRAM
;
186 hints
.ai_socktype
= SOCK_STREAM
;
187 hints
.ai_protocol
= IPPROTO_TCP
;
190 assert(server
->address_info
== NULL
);
191 assert(server
->address_info_next
== NULL
);
193 assert(server
->hostname());
194 switch (errcode
= getaddrinfo(server
->hostname(), str_port
, &hints
, &server
->address_info
)) {
196 server
->address_info_next
= server
->address_info
;
197 server
->state
= MEMCACHED_SERVER_STATE_ADDRINFO
;
201 return memcached_set_error(*server
, MEMCACHED_TIMEOUT
, MEMCACHED_AT
,
202 memcached_string_make_from_cstr(gai_strerror(errcode
)));
205 server
->clear_addrinfo();
206 return memcached_set_errno(*server
, errno
, MEMCACHED_AT
,
207 memcached_literal_param("getaddrinfo(EAI_SYSTEM)"));
210 server
->clear_addrinfo();
211 return memcached_set_error(*server
, MEMCACHED_INVALID_ARGUMENTS
, MEMCACHED_AT
,
212 memcached_literal_param("getaddrinfo(EAI_BADFLAGS)"));
215 server
->clear_addrinfo();
216 return memcached_set_error(*server
, MEMCACHED_MEMORY_ALLOCATION_FAILURE
, MEMCACHED_AT
,
217 memcached_literal_param("getaddrinfo(EAI_MEMORY)"));
220 server
->clear_addrinfo();
221 return memcached_set_error(*server
, MEMCACHED_HOST_LOOKUP_FAILURE
, MEMCACHED_AT
,
222 memcached_string_make_from_cstr(gai_strerror(errcode
)));
226 return MEMCACHED_SUCCESS
;
229 static inline void set_socket_nonblocking(memcached_instance_st
*server
) {
232 if (ioctlsocket(server
->fd
, FIONBIO
, &arg
) == SOCKET_ERROR
) {
233 memcached_set_errno(*server
, get_socket_errno(), NULL
);
238 if (SOCK_NONBLOCK
== 0) {
240 flags
= fcntl(server
->fd
, F_GETFL
, 0);
241 } while (flags
== -1 && (errno
== EINTR
|| errno
== EAGAIN
));
244 memcached_set_errno(*server
, errno
, NULL
);
245 } else if ((flags
& O_NONBLOCK
) == 0) {
249 rval
= fcntl(server
->fd
, F_SETFL
, flags
| O_NONBLOCK
);
250 } while (rval
== -1 && (errno
== EINTR
or errno
== EAGAIN
));
253 memcached_set_errno(*server
, errno
, NULL
);
260 static bool set_socket_options(memcached_instance_st
*server
) {
261 assert_msg(server
->fd
!= INVALID_SOCKET
, "invalid socket was passed to set_socket_options()");
264 // If SOCK_CLOEXEC exists then we don't need to call the following
265 if (SOCK_CLOEXEC
== 0) {
269 flags
= fcntl(server
->fd
, F_GETFD
, 0);
270 } while (flags
== -1 and (errno
== EINTR
or errno
== EAGAIN
));
275 rval
= fcntl(server
->fd
, F_SETFD
, flags
| FD_CLOEXEC
);
276 } while (rval
== -1 && (errno
== EINTR
or errno
== EAGAIN
));
277 // we currently ignore the case where rval is -1
283 if (memcached_is_udp(server
->root
)) {
287 #ifdef HAVE_SO_SNDTIMEO
288 if (server
->root
->snd_timeout
> 0) {
289 struct timeval waittime
;
291 waittime
.tv_sec
= server
->root
->snd_timeout
/ 1000000;
292 waittime
.tv_usec
= server
->root
->snd_timeout
% 1000000;
294 int error
= setsockopt(server
->fd
, SOL_SOCKET
, SO_SNDTIMEO
, (char *) &waittime
,
295 (socklen_t
) sizeof(struct timeval
));
301 #ifdef HAVE_SO_RCVTIMEO
302 if (server
->root
->rcv_timeout
> 0) {
303 struct timeval waittime
;
305 waittime
.tv_sec
= server
->root
->rcv_timeout
/ 1000000;
306 waittime
.tv_usec
= server
->root
->rcv_timeout
% 1000000;
308 int error
= setsockopt(server
->fd
, SOL_SOCKET
, SO_RCVTIMEO
, (char *) &waittime
,
309 (socklen_t
) sizeof(struct timeval
));
317 # if defined(SO_NOSIGPIPE)
320 int error
= setsockopt(server
->fd
, SOL_SOCKET
, SO_NOSIGPIPE
, (void *) &set
, sizeof(int));
324 // This is not considered a fatal error
327 perror("setsockopt(SO_NOSIGPIPE)");
331 # endif // SO_NOSIGPIPE
334 if (server
->root
->flags
.no_block
) {
335 struct linger linger
;
338 linger
.l_linger
= 0; /* By default on close() just drop the socket */
339 int error
= setsockopt(server
->fd
, SOL_SOCKET
, SO_LINGER
, (char *) &linger
,
340 (socklen_t
) sizeof(struct linger
));
346 if (server
->root
->flags
.tcp_nodelay
) {
350 setsockopt(server
->fd
, IPPROTO_TCP
, TCP_NODELAY
, (char *) &flag
, (socklen_t
) sizeof(int));
356 if (server
->root
->flags
.tcp_keepalive
) {
360 setsockopt(server
->fd
, SOL_SOCKET
, SO_KEEPALIVE
, (char *) &flag
, (socklen_t
) sizeof(int));
366 if (server
->root
->tcp_keepidle
> 0) {
367 int error
= setsockopt(server
->fd
, IPPROTO_TCP
, TCP_KEEPIDLE
,
368 (char *) &server
->root
->tcp_keepidle
, (socklen_t
) sizeof(int));
374 if (server
->root
->send_size
> 0) {
375 int error
= setsockopt(server
->fd
, SOL_SOCKET
, SO_SNDBUF
, (char *) &server
->root
->send_size
,
376 (socklen_t
) sizeof(int));
381 if (server
->root
->recv_size
> 0) {
382 int error
= setsockopt(server
->fd
, SOL_SOCKET
, SO_RCVBUF
, (char *) &server
->root
->recv_size
,
383 (socklen_t
) sizeof(int));
388 /* libmemcached will always use nonblocking IO to avoid write deadlocks */
389 set_socket_nonblocking(server
);
394 static memcached_return_t
unix_socket_connect(memcached_instance_st
*server
) {
396 WATCHPOINT_ASSERT(server
->fd
== INVALID_SOCKET
);
399 int type
= SOCK_STREAM
;
401 type
|= SOCK_CLOEXEC
;
405 type
|= SOCK_NONBLOCK
;
408 if ((server
->fd
= socket(AF_UNIX
, type
, 0)) == -1) {
409 return memcached_set_errno(*server
, errno
, NULL
);
412 struct sockaddr_un servAddr
;
414 memset(&servAddr
, 0, sizeof(struct sockaddr_un
));
415 servAddr
.sun_family
= AF_UNIX
;
416 if (strlen(server
->hostname()) >= sizeof(servAddr
.sun_path
)) {
417 return memcached_set_error(*server
, MEMCACHED_FAIL_UNIX_SOCKET
, MEMCACHED_AT
);
419 strncpy(servAddr
.sun_path
, server
->hostname(),
420 sizeof(servAddr
.sun_path
) - 1); /* Copy filename */
422 if (connect(server
->fd
, (struct sockaddr
*) &servAddr
, sizeof(servAddr
)) == -1) {
427 server
->events(POLLOUT
);
431 server
->reset_socket();
434 case EISCONN
: /* We were spinning waiting on connect */
436 assert(0); // Programmer error
437 server
->reset_socket();
442 WATCHPOINT_ERRNO(errno
);
443 server
->reset_socket();
444 return memcached_set_errno(*server
, errno
, MEMCACHED_AT
);
448 server
->state
= MEMCACHED_SERVER_STATE_CONNECTED
;
450 WATCHPOINT_ASSERT(server
->fd
!= INVALID_SOCKET
);
452 return MEMCACHED_SUCCESS
;
455 return MEMCACHED_NOT_SUPPORTED
;
459 static memcached_return_t
network_connect(memcached_instance_st
*server
) {
460 bool timeout_error_occured
= false;
462 WATCHPOINT_ASSERT(server
->fd
== INVALID_SOCKET
);
463 WATCHPOINT_ASSERT(server
->cursor_active_
== 0);
466 We want to check both of these because if address_info_next has been fully tried, we want to do
467 a new lookup to make sure we have picked up on any new DNS information.
469 if (server
->address_info
== NULL
or server
->address_info_next
== NULL
) {
470 WATCHPOINT_ASSERT(server
->state
== MEMCACHED_SERVER_STATE_NEW
);
471 server
->address_info_next
= NULL
;
472 memcached_return_t rc
= set_hostinfo(server
);
474 if (memcached_failed(rc
)) {
479 assert(server
->address_info_next
);
480 assert(server
->address_info
);
482 /* Create the socket */
483 while (server
->address_info_next
and server
->fd
== INVALID_SOCKET
) {
484 int type
= server
->address_info_next
->ai_socktype
;
486 type
|= SOCK_CLOEXEC
;
490 type
|= SOCK_NONBLOCK
;
494 socket(server
->address_info_next
->ai_family
, type
, server
->address_info_next
->ai_protocol
);
496 if (int(server
->fd
) == SOCKET_ERROR
) {
497 return memcached_set_errno(*server
, get_socket_errno(), NULL
);
500 if (set_socket_options(server
) == false) {
501 server
->reset_socket();
502 return MEMCACHED_CONNECTION_FAILURE
;
505 /* connect to server */
506 if ((connect(server
->fd
, server
->address_info_next
->ai_addr
,
507 server
->address_info_next
->ai_addrlen
)
510 server
->state
= MEMCACHED_SERVER_STATE_CONNECTED
;
511 return MEMCACHED_SUCCESS
;
514 /* An error occurred */
515 int local_error
= get_socket_errno();
516 switch (local_error
) {
518 timeout_error_occured
= true;
521 #if EWOULDBLOCK != EAGAIN
524 case EINPROGRESS
: // nonblocking mode - first return
525 case EALREADY
: // nonblocking mode - subsequent returns
527 server
->events(POLLOUT
);
528 server
->state
= MEMCACHED_SERVER_STATE_IN_PROGRESS
;
529 memcached_return_t rc
= connect_poll(server
, local_error
);
531 if (memcached_success(rc
)) {
532 server
->state
= MEMCACHED_SERVER_STATE_CONNECTED
;
533 return MEMCACHED_SUCCESS
;
536 // A timeout here is treated as an error, we will not retry
537 if (rc
== MEMCACHED_TIMEOUT
) {
538 timeout_error_occured
= true;
542 case EISCONN
: // we are connected :-)
543 WATCHPOINT_ASSERT(0); // This is a programmer's error
546 case EINTR
: // Special case, we retry ai_addr
547 WATCHPOINT_ASSERT(server
->fd
!= INVALID_SOCKET
);
548 server
->reset_socket();
552 // Probably not running service
555 memcached_set_errno(*server
, local_error
, MEMCACHED_AT
);
559 WATCHPOINT_ASSERT(server
->fd
!= INVALID_SOCKET
);
560 server
->reset_socket();
561 server
->address_info_next
= server
->address_info_next
->ai_next
;
564 WATCHPOINT_ASSERT(server
->fd
== INVALID_SOCKET
);
566 if (timeout_error_occured
) {
567 server
->reset_socket();
570 WATCHPOINT_STRING("Never got a good file descriptor");
572 if (memcached_has_current_error(*server
)) {
573 return memcached_instance_error_return(server
);
576 if (timeout_error_occured
and server
->state
< MEMCACHED_SERVER_STATE_IN_PROGRESS
) {
577 return memcached_set_error(
578 *server
, MEMCACHED_TIMEOUT
, MEMCACHED_AT
,
579 memcached_literal_param(
580 "if (timeout_error_occured and server->state < MEMCACHED_SERVER_STATE_IN_PROGRESS)"));
583 return memcached_set_error(*server
, MEMCACHED_CONNECTION_FAILURE
,
584 MEMCACHED_AT
); /* The last error should be from connect() */
590 Based on time/failure count fail the connect without trying. This prevents waiting in a state
591 where we get caught spending cycles just waiting.
593 static memcached_return_t
backoff_handling(memcached_instance_st
*server
, bool &in_timeout
) {
594 struct timeval curr_time
;
595 bool _gettime_success
= (gettimeofday(&curr_time
, NULL
) == 0);
598 If we hit server_failure_limit then something is completely wrong about the server.
600 1) If autoeject is enabled we do that.
601 2) If not? We go into timeout again, there is much else to do :(
603 if (server
->server_failure_counter
>= server
->root
->server_failure_limit
) {
605 We just auto_eject if we hit this point
607 if (_is_auto_eject_host(server
->root
)) {
608 set_last_disconnected_host(server
);
610 // Retry dead servers if requested
611 if (_gettime_success
and server
->root
->dead_timeout
> 0) {
612 server
->next_retry
= curr_time
.tv_sec
+ server
->root
->dead_timeout
;
614 // We only retry dead servers once before assuming failure again
615 server
->server_failure_counter
= server
->root
->server_failure_limit
- 1;
618 memcached_return_t rc
;
619 if (memcached_failed(rc
= run_distribution((memcached_st
*) server
->root
))) {
620 return memcached_set_error(
621 *server
, rc
, MEMCACHED_AT
,
622 memcached_literal_param("Backoff handling failed during run_distribution"));
625 return memcached_set_error(*server
, MEMCACHED_SERVER_MARKED_DEAD
, MEMCACHED_AT
);
628 server
->state
= MEMCACHED_SERVER_STATE_IN_TIMEOUT
;
630 // Sanity check/setting
631 if (server
->next_retry
== 0) {
632 server
->next_retry
= 1;
636 if (server
->state
== MEMCACHED_SERVER_STATE_IN_TIMEOUT
) {
638 If next_retry is less then our current time, then we reset and try everything again.
640 if (_gettime_success
and server
->next_retry
< curr_time
.tv_sec
) {
641 server
->state
= MEMCACHED_SERVER_STATE_NEW
;
642 server
->server_timeout_counter
= 0;
644 return memcached_set_error(*server
, MEMCACHED_SERVER_TEMPORARILY_DISABLED
, MEMCACHED_AT
);
650 return MEMCACHED_SUCCESS
;
653 static memcached_return_t
_memcached_connect(memcached_instance_st
*server
,
654 const bool set_last_disconnected
) {
656 if (server
->fd
!= INVALID_SOCKET
) {
657 return MEMCACHED_SUCCESS
;
660 LIBMEMCACHED_MEMCACHED_CONNECT_START();
662 bool in_timeout
= false;
663 memcached_return_t rc
;
664 if (memcached_failed(rc
= backoff_handling(server
, in_timeout
))) {
665 set_last_disconnected_host(server
);
669 if (LIBMEMCACHED_WITH_SASL_SUPPORT
and server
->root
->sasl
.callbacks
670 and memcached_is_udp(server
->root
))
672 return memcached_set_error(
673 *server
, MEMCACHED_INVALID_HOST_PROTOCOL
, MEMCACHED_AT
,
674 memcached_literal_param("SASL is not supported for UDP connections"));
677 if (server
->hostname()[0] == '/') {
678 server
->type
= MEMCACHED_CONNECTION_UNIX_SOCKET
;
681 /* We need to clean up the multi startup piece */
682 switch (server
->type
) {
683 case MEMCACHED_CONNECTION_UDP
:
684 case MEMCACHED_CONNECTION_TCP
:
685 rc
= network_connect(server
);
687 #if defined(LIBMEMCACHED_WITH_SASL_SUPPORT)
688 if (LIBMEMCACHED_WITH_SASL_SUPPORT
) {
689 if (server
->fd
!= INVALID_SOCKET
and server
->root
->sasl
.callbacks
) {
690 rc
= memcached_sasl_authenticate_connection(server
);
691 if (memcached_failed(rc
) and server
->fd
!= INVALID_SOCKET
) {
692 WATCHPOINT_ASSERT(server
->fd
!= INVALID_SOCKET
);
693 server
->reset_socket();
700 case MEMCACHED_CONNECTION_UNIX_SOCKET
:
701 rc
= unix_socket_connect(server
);
705 if (memcached_success(rc
)) {
706 server
->mark_server_as_clean();
707 memcached_version_instance(server
);
709 } else if (set_last_disconnected
) {
710 set_last_disconnected_host(server
);
711 if (memcached_has_current_error(*server
)) {
712 memcached_mark_server_for_timeout(server
);
713 assert(memcached_failed(memcached_instance_error_return(server
)));
715 memcached_set_error(*server
, rc
, MEMCACHED_AT
);
716 memcached_mark_server_for_timeout(server
);
719 LIBMEMCACHED_MEMCACHED_CONNECT_END();
723 int snprintf_length
=
724 snprintf(buffer
, sizeof(buffer
), "%s:%d", server
->hostname(), int(server
->port()));
725 return memcached_set_error(*server
, MEMCACHED_SERVER_TEMPORARILY_DISABLED
, MEMCACHED_AT
,
726 buffer
, snprintf_length
);
733 memcached_return_t
memcached_connect(memcached_instance_st
*server
) {
734 return _memcached_connect(server
, true);