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