p9y
[m6w6/libmemcached] / src / libmemcached / connect.cc
1 /*
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 +--------------------------------------------------------------------+
14 */
15
16 #include "libmemcached/common.h"
17 #include "p9y/socket.hpp"
18 #include "p9y/poll.hpp"
19
20 #include <cassert>
21
22
23 static memcached_return_t connect_poll(memcached_instance_st *server, const int connection_error) {
24 struct pollfd fds[1];
25 fds[0].fd = server->fd;
26 fds[0].events = server->events();
27 fds[0].revents = 0;
28
29 size_t loop_max = 5;
30
31 if (server->root->connect_timeout == 0) {
32 return memcached_set_error(
33 *server, MEMCACHED_TIMEOUT, MEMCACHED_AT,
34 memcached_literal_param("The time to wait for a connection to be established was set to "
35 "zero which produces a timeout to every call to poll()."));
36 }
37
38 while (--loop_max) // Should only loop on cases of ERESTART or EINTR
39 {
40 int number_of;
41 if ((number_of = poll(fds, 1, server->root->connect_timeout)) == SOCKET_ERROR) {
42 int local_errno = get_socket_errno(); // We cache in case closesocket() modifies errno
43 switch (local_errno) {
44 #ifdef __linux__
45 case ERESTART:
46 #endif
47 case EINTR:
48 continue;
49
50 case EFAULT:
51 case ENOMEM:
52 return memcached_set_error(*server, MEMCACHED_MEMORY_ALLOCATION_FAILURE, MEMCACHED_AT);
53
54 case EINVAL:
55 return memcached_set_error(
56 *server, MEMCACHED_MEMORY_ALLOCATION_FAILURE, MEMCACHED_AT,
57 memcached_literal_param(
58 "RLIMIT_NOFILE exceeded, or if OSX the timeout value was invalid"));
59
60 default: // This should not happen
61 break;
62 }
63
64 assert_msg(server->fd != INVALID_SOCKET, "poll() was passed an invalid file descriptor");
65 server->reset_socket();
66 server->state = MEMCACHED_SERVER_STATE_NEW;
67
68 return memcached_set_errno(*server, local_errno, MEMCACHED_AT);
69 }
70
71 if (number_of == 0) {
72 if (connection_error != EALREADY) {
73 int err;
74 socklen_t len = sizeof(err);
75 if (getsockopt(server->fd, SOL_SOCKET, SO_ERROR, (char *) &err, &len) == -1) {
76 return memcached_set_errno(
77 *server, errno, MEMCACHED_AT,
78 memcached_literal_param(
79 "getsockopt() error'ed while looking for error connect_poll(EINPROGRESS)"));
80 }
81
82 // If Zero, my hero, we just fail to a generic MEMCACHED_TIMEOUT error
83 if (err) {
84 return memcached_set_errno(
85 *server, err, MEMCACHED_AT,
86 memcached_literal_param("getsockopt() found the error from poll() after connect() "
87 "returned EINPROGRESS."));
88 }
89 }
90
91 return memcached_set_error(*server, MEMCACHED_TIMEOUT, MEMCACHED_AT,
92 memcached_literal_param("(number_of == 0)"));
93 }
94
95 assert(number_of == 1);
96
97 if (fds[0].revents & POLLERR or fds[0].revents & POLLHUP or fds[0].revents & POLLNVAL) {
98 int err;
99 socklen_t len = sizeof(err);
100 if (getsockopt(fds[0].fd, SOL_SOCKET, SO_ERROR, (char *) &err, &len) == -1) {
101 return memcached_set_errno(
102 *server, errno, MEMCACHED_AT,
103 memcached_literal_param(
104 "getsockopt() errored while looking up error state from poll()"));
105 }
106
107 // We check the value to see what happened with the socket.
108 if (err == 0) // Should not happen
109 {
110 return MEMCACHED_SUCCESS;
111 }
112 errno = err;
113
114 return memcached_set_errno(
115 *server, err, MEMCACHED_AT,
116 memcached_literal_param("getsockopt() found the error from poll() during connect."));
117 }
118 assert(fds[0].revents & POLLOUT);
119
120 if (fds[0].revents & POLLOUT and connection_error != EALREADY) {
121 int err;
122 socklen_t len = sizeof(err);
123 if (getsockopt(server->fd, SOL_SOCKET, SO_ERROR, (char *) &err, &len) == -1) {
124 return memcached_set_errno(*server, errno, MEMCACHED_AT);
125 }
126
127 if (err == 0) {
128 return MEMCACHED_SUCCESS;
129 }
130
131 return memcached_set_errno(
132 *server, err, MEMCACHED_AT,
133 memcached_literal_param(
134 "getsockopt() found the error from poll() after connect() returned EINPROGRESS."));
135 }
136
137 break; // We only have the loop setup for errno types that require restart
138 }
139
140 // This should only be possible from ERESTART or EINTR;
141 return memcached_set_errno(*server, connection_error, MEMCACHED_AT,
142 memcached_literal_param("connect_poll() was exhausted"));
143 }
144
145 static memcached_return_t set_hostinfo(memcached_instance_st *server) {
146 assert(server->type != MEMCACHED_CONNECTION_UNIX_SOCKET);
147 server->clear_addrinfo();
148
149 char str_port[MEMCACHED_NI_MAXSERV] = {0};
150 errno = 0;
151 int length = snprintf(str_port, MEMCACHED_NI_MAXSERV, "%u", uint32_t(server->port()));
152 if (length >= MEMCACHED_NI_MAXSERV or length <= 0 or errno) {
153 return memcached_set_error(*server, MEMCACHED_MEMORY_ALLOCATION_FAILURE, MEMCACHED_AT,
154 memcached_literal_param("snprintf(NI_MAXSERV)"));
155 }
156
157 struct addrinfo hints;
158 memset(&hints, 0, sizeof(struct addrinfo));
159
160 hints.ai_family = AF_UNSPEC;
161 if (memcached_is_udp(server->root)) {
162 hints.ai_protocol = IPPROTO_UDP;
163 hints.ai_socktype = SOCK_DGRAM;
164 } else {
165 hints.ai_socktype = SOCK_STREAM;
166 hints.ai_protocol = IPPROTO_TCP;
167 }
168
169 assert(server->address_info == NULL);
170 assert(server->address_info_next == NULL);
171 int errcode;
172 assert(server->hostname());
173 switch (errcode = getaddrinfo(server->hostname(), str_port, &hints, &server->address_info)) {
174 case 0:
175 server->address_info_next = server->address_info;
176 server->state = MEMCACHED_SERVER_STATE_ADDRINFO;
177 break;
178
179 case EAI_AGAIN:
180 return memcached_set_error(*server, MEMCACHED_TIMEOUT, MEMCACHED_AT,
181 memcached_string_make_from_cstr(gai_strerror(errcode)));
182
183 case EAI_SYSTEM:
184 server->clear_addrinfo();
185 return memcached_set_errno(*server, errno, MEMCACHED_AT,
186 memcached_literal_param("getaddrinfo(EAI_SYSTEM)"));
187
188 case EAI_BADFLAGS:
189 server->clear_addrinfo();
190 return memcached_set_error(*server, MEMCACHED_INVALID_ARGUMENTS, MEMCACHED_AT,
191 memcached_literal_param("getaddrinfo(EAI_BADFLAGS)"));
192
193 case EAI_MEMORY:
194 server->clear_addrinfo();
195 return memcached_set_error(*server, MEMCACHED_MEMORY_ALLOCATION_FAILURE, MEMCACHED_AT,
196 memcached_literal_param("getaddrinfo(EAI_MEMORY)"));
197
198 default: {
199 server->clear_addrinfo();
200 return memcached_set_error(*server, MEMCACHED_HOST_LOOKUP_FAILURE, MEMCACHED_AT,
201 memcached_string_make_from_cstr(gai_strerror(errcode)));
202 }
203 }
204
205 return MEMCACHED_SUCCESS;
206 }
207
208 static inline void set_socket_nonblocking(memcached_instance_st *server) {
209 #if defined(_WIN32)
210 u_long arg = 1;
211 if (ioctlsocket(server->fd, FIONBIO, &arg) == SOCKET_ERROR) {
212 memcached_set_errno(*server, get_socket_errno(), NULL);
213 }
214 #else
215 int flags;
216
217 if (SOCK_NONBLOCK == 0) {
218 do {
219 flags = fcntl(server->fd, F_GETFL, 0);
220 } while (flags == -1 && (errno == EINTR || errno == EAGAIN));
221
222 if (flags == -1) {
223 memcached_set_errno(*server, errno, NULL);
224 } else if ((flags & O_NONBLOCK) == 0) {
225 int rval;
226
227 do {
228 rval = fcntl(server->fd, F_SETFL, flags | O_NONBLOCK);
229 } while (rval == -1 && (errno == EINTR or errno == EAGAIN));
230
231 if (rval == -1) {
232 memcached_set_errno(*server, errno, NULL);
233 }
234 }
235 }
236 #endif
237 }
238
239 static bool set_socket_options(memcached_instance_st *server) {
240 assert_msg(server->fd != INVALID_SOCKET, "invalid socket was passed to set_socket_options()");
241
242 #ifdef HAVE_FCNTL
243 // If SOCK_CLOEXEC exists then we don't need to call the following
244 if (SOCK_CLOEXEC == 0) {
245 if (FD_CLOEXEC) {
246 int flags;
247 do {
248 flags = fcntl(server->fd, F_GETFD, 0);
249 } while (flags == -1 and (errno == EINTR or errno == EAGAIN));
250
251 if (flags != -1) {
252 int rval;
253 do {
254 rval = fcntl(server->fd, F_SETFD, flags | FD_CLOEXEC);
255 } while (rval == -1 && (errno == EINTR or errno == EAGAIN));
256 // we currently ignore the case where rval is -1
257 }
258 }
259 }
260 #endif
261
262 if (memcached_is_udp(server->root)) {
263 return true;
264 }
265
266 #ifdef HAVE_SO_SNDTIMEO
267 if (server->root->snd_timeout > 0) {
268 struct timeval waittime;
269
270 waittime.tv_sec = server->root->snd_timeout / 1000000;
271 waittime.tv_usec = server->root->snd_timeout % 1000000;
272
273 int error = setsockopt(server->fd, SOL_SOCKET, SO_SNDTIMEO, (char *) &waittime,
274 (socklen_t) sizeof(struct timeval));
275 (void) error;
276 assert(error == 0);
277 }
278 #endif
279
280 #ifdef HAVE_SO_RCVTIMEO
281 if (server->root->rcv_timeout > 0) {
282 struct timeval waittime;
283
284 waittime.tv_sec = server->root->rcv_timeout / 1000000;
285 waittime.tv_usec = server->root->rcv_timeout % 1000000;
286
287 int error = setsockopt(server->fd, SOL_SOCKET, SO_RCVTIMEO, (char *) &waittime,
288 (socklen_t) sizeof(struct timeval));
289 (void) (error);
290 assert(error == 0);
291 }
292 #endif
293
294 #if defined(_WIN32)
295 #else
296 # if defined(SO_NOSIGPIPE)
297 if (SO_NOSIGPIPE) {
298 int set = 1;
299 int error = setsockopt(server->fd, SOL_SOCKET, SO_NOSIGPIPE, (void *) &set, sizeof(int));
300
301 assert(error == 0);
302
303 // This is not considered a fatal error
304 if (error == -1) {
305 # if 0
306 perror("setsockopt(SO_NOSIGPIPE)");
307 # endif
308 }
309 }
310 # endif // SO_NOSIGPIPE
311 #endif // _WIN32
312
313 if (server->root->flags.no_block) {
314 struct linger linger;
315
316 linger.l_onoff = 1;
317 linger.l_linger = 0; /* By default on close() just drop the socket */
318 int error = setsockopt(server->fd, SOL_SOCKET, SO_LINGER, (char *) &linger,
319 (socklen_t) sizeof(struct linger));
320 (void) (error);
321 assert(error == 0);
322 }
323
324 if (TCP_NODELAY) {
325 if (server->root->flags.tcp_nodelay) {
326 int flag = 1;
327
328 int error =
329 setsockopt(server->fd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, (socklen_t) sizeof(int));
330 (void) (error);
331 assert(error == 0);
332 }
333 }
334
335 if (server->root->flags.tcp_keepalive) {
336 int flag = 1;
337
338 int error =
339 setsockopt(server->fd, SOL_SOCKET, SO_KEEPALIVE, (char *) &flag, (socklen_t) sizeof(int));
340 (void) (error);
341 assert(error == 0);
342 }
343
344 if (TCP_KEEPIDLE) {
345 if (server->root->tcp_keepidle > 0) {
346 int error = setsockopt(server->fd, IPPROTO_TCP, TCP_KEEPIDLE,
347 (char *) &server->root->tcp_keepidle, (socklen_t) sizeof(int));
348 (void) (error);
349 assert(error == 0);
350 }
351 }
352
353 if (server->root->send_size > 0) {
354 int error = setsockopt(server->fd, SOL_SOCKET, SO_SNDBUF, (char *) &server->root->send_size,
355 (socklen_t) sizeof(int));
356 (void) (error);
357 assert(error == 0);
358 }
359
360 if (server->root->recv_size > 0) {
361 int error = setsockopt(server->fd, SOL_SOCKET, SO_RCVBUF, (char *) &server->root->recv_size,
362 (socklen_t) sizeof(int));
363 (void) (error);
364 assert(error == 0);
365 }
366
367 /* libmemcached will always use nonblocking IO to avoid write deadlocks */
368 set_socket_nonblocking(server);
369
370 return true;
371 }
372
373 static memcached_return_t unix_socket_connect(memcached_instance_st *server) {
374 #ifndef _WIN32
375 WATCHPOINT_ASSERT(server->fd == INVALID_SOCKET);
376
377 do {
378 int type = SOCK_STREAM;
379 if (SOCK_CLOEXEC) {
380 type |= SOCK_CLOEXEC;
381 }
382
383 if (SOCK_NONBLOCK) {
384 type |= SOCK_NONBLOCK;
385 }
386
387 if ((server->fd = socket(AF_UNIX, type, 0)) == -1) {
388 return memcached_set_errno(*server, errno, NULL);
389 }
390
391 struct sockaddr_un servAddr;
392
393 memset(&servAddr, 0, sizeof(struct sockaddr_un));
394 servAddr.sun_family = AF_UNIX;
395 if (strlen(server->hostname()) >= sizeof(servAddr.sun_path)) {
396 return memcached_set_error(*server, MEMCACHED_FAIL_UNIX_SOCKET, MEMCACHED_AT);
397 }
398 strncpy(servAddr.sun_path, server->hostname(),
399 sizeof(servAddr.sun_path) - 1); /* Copy filename */
400
401 if (connect(server->fd, (struct sockaddr *) &servAddr, sizeof(servAddr)) == -1) {
402 switch (errno) {
403 case EINPROGRESS:
404 case EALREADY:
405 case EAGAIN:
406 server->events(POLLOUT);
407 break;
408
409 case EINTR:
410 server->reset_socket();
411 continue;
412
413 case EISCONN: /* We were spinning waiting on connect */
414 {
415 assert(0); // Programmer error
416 server->reset_socket();
417 continue;
418 }
419
420 default:
421 WATCHPOINT_ERRNO(errno);
422 server->reset_socket();
423 return memcached_set_errno(*server, errno, MEMCACHED_AT);
424 }
425 }
426 } while (0);
427 server->state = MEMCACHED_SERVER_STATE_CONNECTED;
428
429 WATCHPOINT_ASSERT(server->fd != INVALID_SOCKET);
430
431 return MEMCACHED_SUCCESS;
432 #else
433 (void) server;
434 return MEMCACHED_NOT_SUPPORTED;
435 #endif
436 }
437
438 static memcached_return_t network_connect(memcached_instance_st *server) {
439 bool timeout_error_occured = false;
440
441 WATCHPOINT_ASSERT(server->fd == INVALID_SOCKET);
442 WATCHPOINT_ASSERT(server->cursor_active_ == 0);
443
444 /*
445 We want to check both of these because if address_info_next has been fully tried, we want to do
446 a new lookup to make sure we have picked up on any new DNS information.
447 */
448 if (server->address_info == NULL or server->address_info_next == NULL) {
449 WATCHPOINT_ASSERT(server->state == MEMCACHED_SERVER_STATE_NEW);
450 server->address_info_next = NULL;
451 memcached_return_t rc = set_hostinfo(server);
452
453 if (memcached_failed(rc)) {
454 return rc;
455 }
456 }
457
458 assert(server->address_info_next);
459 assert(server->address_info);
460
461 /* Create the socket */
462 while (server->address_info_next and server->fd == INVALID_SOCKET) {
463 int type = server->address_info_next->ai_socktype;
464 if (SOCK_CLOEXEC) {
465 type |= SOCK_CLOEXEC;
466 }
467
468 if (SOCK_NONBLOCK) {
469 type |= SOCK_NONBLOCK;
470 }
471
472 server->fd =
473 socket(server->address_info_next->ai_family, type, server->address_info_next->ai_protocol);
474
475 if (int(server->fd) == SOCKET_ERROR) {
476 return memcached_set_errno(*server, get_socket_errno(), NULL);
477 }
478
479 if (set_socket_options(server) == false) {
480 server->reset_socket();
481 return MEMCACHED_CONNECTION_FAILURE;
482 }
483
484 /* connect to server */
485 if ((connect(server->fd, server->address_info_next->ai_addr,
486 server->address_info_next->ai_addrlen)
487 != SOCKET_ERROR))
488 {
489 server->state = MEMCACHED_SERVER_STATE_CONNECTED;
490 return MEMCACHED_SUCCESS;
491 }
492
493 /* An error occurred */
494 int local_error = get_socket_errno();
495 switch (local_error) {
496 case ETIMEDOUT:
497 timeout_error_occured = true;
498 break;
499
500 #if EWOULDBLOCK != EAGAIN
501 case EWOULDBLOCK:
502 #endif
503 case EAGAIN:
504 case EINPROGRESS: // nonblocking mode - first return
505 case EALREADY: // nonblocking mode - subsequent returns
506 {
507 server->events(POLLOUT);
508 server->state = MEMCACHED_SERVER_STATE_IN_PROGRESS;
509 memcached_return_t rc = connect_poll(server, local_error);
510
511 if (memcached_success(rc)) {
512 server->state = MEMCACHED_SERVER_STATE_CONNECTED;
513 return MEMCACHED_SUCCESS;
514 }
515
516 // A timeout here is treated as an error, we will not retry
517 if (rc == MEMCACHED_TIMEOUT) {
518 timeout_error_occured = true;
519 }
520 } break;
521
522 case EISCONN: // we are connected :-)
523 WATCHPOINT_ASSERT(0); // This is a programmer's error
524 break;
525
526 case EINTR: // Special case, we retry ai_addr
527 WATCHPOINT_ASSERT(server->fd != INVALID_SOCKET);
528 server->reset_socket();
529 continue;
530
531 case ECONNREFUSED:
532 // Probably not running service
533
534 default:
535 memcached_set_errno(*server, local_error, MEMCACHED_AT);
536 break;
537 }
538
539 WATCHPOINT_ASSERT(server->fd != INVALID_SOCKET);
540 server->reset_socket();
541 server->address_info_next = server->address_info_next->ai_next;
542 }
543
544 WATCHPOINT_ASSERT(server->fd == INVALID_SOCKET);
545
546 if (timeout_error_occured) {
547 server->reset_socket();
548 }
549
550 WATCHPOINT_STRING("Never got a good file descriptor");
551
552 if (memcached_has_current_error(*server)) {
553 return memcached_instance_error_return(server);
554 }
555
556 if (timeout_error_occured and server->state < MEMCACHED_SERVER_STATE_IN_PROGRESS) {
557 return memcached_set_error(
558 *server, MEMCACHED_TIMEOUT, MEMCACHED_AT,
559 memcached_literal_param(
560 "if (timeout_error_occured and server->state < MEMCACHED_SERVER_STATE_IN_PROGRESS)"));
561 }
562
563 return memcached_set_error(*server, MEMCACHED_CONNECTION_FAILURE,
564 MEMCACHED_AT); /* The last error should be from connect() */
565 }
566
567 /*
568 backoff_handling()
569
570 Based on time/failure count fail the connect without trying. This prevents waiting in a state
571 where we get caught spending cycles just waiting.
572 */
573 static memcached_return_t backoff_handling(memcached_instance_st *server, bool &in_timeout) {
574 struct timeval curr_time;
575 bool _gettime_success = (gettimeofday(&curr_time, NULL) == 0);
576
577 /*
578 If we hit server_failure_limit then something is completely wrong about the server.
579
580 1) If autoeject is enabled we do that.
581 2) If not? We go into timeout again, there is much else to do :(
582 */
583 if (server->server_failure_counter >= server->root->server_failure_limit) {
584 /*
585 We just auto_eject if we hit this point
586 */
587 if (_is_auto_eject_host(server->root)) {
588 set_last_disconnected_host(server);
589
590 // Retry dead servers if requested
591 if (_gettime_success and server->root->dead_timeout > 0) {
592 server->next_retry = curr_time.tv_sec + server->root->dead_timeout;
593
594 // We only retry dead servers once before assuming failure again
595 server->server_failure_counter = server->root->server_failure_limit - 1;
596 }
597
598 memcached_return_t rc;
599 if (memcached_failed(rc = run_distribution((memcached_st *) server->root))) {
600 return memcached_set_error(
601 *server, rc, MEMCACHED_AT,
602 memcached_literal_param("Backoff handling failed during run_distribution"));
603 }
604
605 return memcached_set_error(*server, MEMCACHED_SERVER_MARKED_DEAD, MEMCACHED_AT);
606 }
607
608 server->state = MEMCACHED_SERVER_STATE_IN_TIMEOUT;
609
610 // Sanity check/setting
611 if (server->next_retry == 0) {
612 server->next_retry = 1;
613 }
614 }
615
616 if (server->state == MEMCACHED_SERVER_STATE_IN_TIMEOUT) {
617 /*
618 If next_retry is less then our current time, then we reset and try everything again.
619 */
620 if (_gettime_success and server->next_retry < curr_time.tv_sec) {
621 server->state = MEMCACHED_SERVER_STATE_NEW;
622 server->server_timeout_counter = 0;
623 } else {
624 return memcached_set_error(*server, MEMCACHED_SERVER_TEMPORARILY_DISABLED, MEMCACHED_AT);
625 }
626
627 in_timeout = true;
628 }
629
630 return MEMCACHED_SUCCESS;
631 }
632
633 static memcached_return_t _memcached_connect(memcached_instance_st *server,
634 const bool set_last_disconnected) {
635 assert(server);
636 if (server->fd != INVALID_SOCKET) {
637 return MEMCACHED_SUCCESS;
638 }
639
640 LIBMEMCACHED_MEMCACHED_CONNECT_START();
641
642 bool in_timeout = false;
643 memcached_return_t rc;
644 if (memcached_failed(rc = backoff_handling(server, in_timeout))) {
645 set_last_disconnected_host(server);
646 return rc;
647 }
648
649 if (LIBMEMCACHED_WITH_SASL_SUPPORT and server->root->sasl.callbacks
650 and memcached_is_udp(server->root))
651 {
652 return memcached_set_error(
653 *server, MEMCACHED_INVALID_HOST_PROTOCOL, MEMCACHED_AT,
654 memcached_literal_param("SASL is not supported for UDP connections"));
655 }
656
657 if (server->hostname()[0] == '/') {
658 server->type = MEMCACHED_CONNECTION_UNIX_SOCKET;
659 }
660
661 /* We need to clean up the multi startup piece */
662 switch (server->type) {
663 case MEMCACHED_CONNECTION_UDP:
664 case MEMCACHED_CONNECTION_TCP:
665 rc = network_connect(server);
666
667 #if defined(LIBMEMCACHED_WITH_SASL_SUPPORT)
668 if (LIBMEMCACHED_WITH_SASL_SUPPORT) {
669 if (server->fd != INVALID_SOCKET and server->root->sasl.callbacks) {
670 rc = memcached_sasl_authenticate_connection(server);
671 if (memcached_failed(rc) and server->fd != INVALID_SOCKET) {
672 WATCHPOINT_ASSERT(server->fd != INVALID_SOCKET);
673 server->reset_socket();
674 }
675 }
676 }
677 #endif
678 break;
679
680 case MEMCACHED_CONNECTION_UNIX_SOCKET:
681 rc = unix_socket_connect(server);
682 break;
683 }
684
685 if (memcached_success(rc)) {
686 server->mark_server_as_clean();
687 memcached_version_instance(server);
688 return rc;
689 } else if (set_last_disconnected) {
690 set_last_disconnected_host(server);
691 if (memcached_has_current_error(*server)) {
692 memcached_mark_server_for_timeout(server);
693 assert(memcached_failed(memcached_instance_error_return(server)));
694 } else {
695 memcached_set_error(*server, rc, MEMCACHED_AT);
696 memcached_mark_server_for_timeout(server);
697 }
698
699 LIBMEMCACHED_MEMCACHED_CONNECT_END();
700
701 if (in_timeout) {
702 char buffer[1024];
703 int snprintf_length =
704 snprintf(buffer, sizeof(buffer), "%s:%d", server->hostname(), int(server->port()));
705 return memcached_set_error(*server, MEMCACHED_SERVER_TEMPORARILY_DISABLED, MEMCACHED_AT,
706 buffer, snprintf_length);
707 }
708 }
709
710 return rc;
711 }
712
713 memcached_return_t memcached_connect(memcached_instance_st *server) {
714 return _memcached_connect(server, true);
715 }