248cd7cd387955987b141e3caf291fcbb00cc5f7
[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->connect_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:
69 continue;
70
71 case EFAULT:
72 case ENOMEM:
73 return memcached_set_error(*server, MEMCACHED_MEMORY_ALLOCATION_FAILURE, MEMCACHED_AT);
74
75 case EINVAL:
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"));
80
81 default: // This should not happen
82 break;
83 }
84
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;
88
89 return memcached_set_errno(*server, local_errno, MEMCACHED_AT);
90 }
91
92 if (number_of == 0) {
93 if (connection_error == EINPROGRESS) {
94 int err;
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)"));
101 }
102
103 // If Zero, my hero, we just fail to a generic MEMCACHED_TIMEOUT error
104 if (err) {
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."));
109 }
110 }
111
112 return memcached_set_error(*server, MEMCACHED_TIMEOUT, MEMCACHED_AT,
113 memcached_literal_param("(number_of == 0)"));
114 }
115
116 assert(number_of == 1);
117
118 if (fds[0].revents & POLLERR or fds[0].revents & POLLHUP or fds[0].revents & POLLNVAL) {
119 int err;
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()"));
126 }
127
128 // We check the value to see what happened with the socket.
129 if (err == 0) // Should not happen
130 {
131 return MEMCACHED_SUCCESS;
132 }
133 errno = err;
134
135 return memcached_set_errno(
136 *server, err, MEMCACHED_AT,
137 memcached_literal_param("getsockopt() found the error from poll() during connect."));
138 }
139 assert(fds[0].revents & POLLOUT);
140
141 if (fds[0].revents & POLLOUT and connection_error == EINPROGRESS) {
142 int err;
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);
146 }
147
148 if (err == 0) {
149 return MEMCACHED_SUCCESS;
150 }
151
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."));
156 }
157
158 break; // We only have the loop setup for errno types that require restart
159 }
160
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"));
164 }
165
166 static memcached_return_t set_hostinfo(memcached_instance_st *server) {
167 assert(server->type != MEMCACHED_CONNECTION_UNIX_SOCKET);
168 server->clear_addrinfo();
169
170 char str_port[MEMCACHED_NI_MAXSERV] = {0};
171 errno = 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)"));
176 }
177
178 struct addrinfo hints;
179 memset(&hints, 0, sizeof(struct addrinfo));
180
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;
185 } else {
186 hints.ai_socktype = SOCK_STREAM;
187 hints.ai_protocol = IPPROTO_TCP;
188 }
189
190 assert(server->address_info == NULL);
191 assert(server->address_info_next == NULL);
192 int errcode;
193 assert(server->hostname());
194 switch (errcode = getaddrinfo(server->hostname(), str_port, &hints, &server->address_info)) {
195 case 0:
196 server->address_info_next = server->address_info;
197 server->state = MEMCACHED_SERVER_STATE_ADDRINFO;
198 break;
199
200 case EAI_AGAIN:
201 return memcached_set_error(*server, MEMCACHED_TIMEOUT, MEMCACHED_AT,
202 memcached_string_make_from_cstr(gai_strerror(errcode)));
203
204 case EAI_SYSTEM:
205 server->clear_addrinfo();
206 return memcached_set_errno(*server, errno, MEMCACHED_AT,
207 memcached_literal_param("getaddrinfo(EAI_SYSTEM)"));
208
209 case EAI_BADFLAGS:
210 server->clear_addrinfo();
211 return memcached_set_error(*server, MEMCACHED_INVALID_ARGUMENTS, MEMCACHED_AT,
212 memcached_literal_param("getaddrinfo(EAI_BADFLAGS)"));
213
214 case EAI_MEMORY:
215 server->clear_addrinfo();
216 return memcached_set_error(*server, MEMCACHED_MEMORY_ALLOCATION_FAILURE, MEMCACHED_AT,
217 memcached_literal_param("getaddrinfo(EAI_MEMORY)"));
218
219 default: {
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)));
223 }
224 }
225
226 return MEMCACHED_SUCCESS;
227 }
228
229 static inline void set_socket_nonblocking(memcached_instance_st *server) {
230 #if defined(_WIN32)
231 u_long arg = 1;
232 if (ioctlsocket(server->fd, FIONBIO, &arg) == SOCKET_ERROR) {
233 memcached_set_errno(*server, get_socket_errno(), NULL);
234 }
235 #else
236 int flags;
237
238 if (SOCK_NONBLOCK == 0) {
239 do {
240 flags = fcntl(server->fd, F_GETFL, 0);
241 } while (flags == -1 && (errno == EINTR || errno == EAGAIN));
242
243 if (flags == -1) {
244 memcached_set_errno(*server, errno, NULL);
245 } else if ((flags & O_NONBLOCK) == 0) {
246 int rval;
247
248 do {
249 rval = fcntl(server->fd, F_SETFL, flags | O_NONBLOCK);
250 } while (rval == -1 && (errno == EINTR or errno == EAGAIN));
251
252 if (rval == -1) {
253 memcached_set_errno(*server, errno, NULL);
254 }
255 }
256 }
257 #endif
258 }
259
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()");
262
263 #ifdef HAVE_FCNTL
264 // If SOCK_CLOEXEC exists then we don't need to call the following
265 if (SOCK_CLOEXEC == 0) {
266 if (FD_CLOEXEC) {
267 int flags;
268 do {
269 flags = fcntl(server->fd, F_GETFD, 0);
270 } while (flags == -1 and (errno == EINTR or errno == EAGAIN));
271
272 if (flags != -1) {
273 int rval;
274 do {
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
278 }
279 }
280 }
281 #endif
282
283 if (memcached_is_udp(server->root)) {
284 return true;
285 }
286
287 #ifdef HAVE_SO_SNDTIMEO
288 if (server->root->snd_timeout > 0) {
289 struct timeval waittime;
290
291 waittime.tv_sec = server->root->snd_timeout / 1000000;
292 waittime.tv_usec = server->root->snd_timeout % 1000000;
293
294 int error = setsockopt(server->fd, SOL_SOCKET, SO_SNDTIMEO, (char *) &waittime,
295 (socklen_t) sizeof(struct timeval));
296 (void) error;
297 assert(error == 0);
298 }
299 #endif
300
301 #ifdef HAVE_SO_RCVTIMEO
302 if (server->root->rcv_timeout > 0) {
303 struct timeval waittime;
304
305 waittime.tv_sec = server->root->rcv_timeout / 1000000;
306 waittime.tv_usec = server->root->rcv_timeout % 1000000;
307
308 int error = setsockopt(server->fd, SOL_SOCKET, SO_RCVTIMEO, (char *) &waittime,
309 (socklen_t) sizeof(struct timeval));
310 (void) (error);
311 assert(error == 0);
312 }
313 #endif
314
315 #if defined(_WIN32)
316 #else
317 # if defined(SO_NOSIGPIPE)
318 if (SO_NOSIGPIPE) {
319 int set = 1;
320 int error = setsockopt(server->fd, SOL_SOCKET, SO_NOSIGPIPE, (void *) &set, sizeof(int));
321
322 assert(error == 0);
323
324 // This is not considered a fatal error
325 if (error == -1) {
326 # if 0
327 perror("setsockopt(SO_NOSIGPIPE)");
328 # endif
329 }
330 }
331 # endif // SO_NOSIGPIPE
332 #endif // _WIN32
333
334 if (server->root->flags.no_block) {
335 struct linger linger;
336
337 linger.l_onoff = 1;
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));
341 (void) (error);
342 assert(error == 0);
343 }
344
345 if (TCP_NODELAY) {
346 if (server->root->flags.tcp_nodelay) {
347 int flag = 1;
348
349 int error =
350 setsockopt(server->fd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, (socklen_t) sizeof(int));
351 (void) (error);
352 assert(error == 0);
353 }
354 }
355
356 if (server->root->flags.tcp_keepalive) {
357 int flag = 1;
358
359 int error =
360 setsockopt(server->fd, SOL_SOCKET, SO_KEEPALIVE, (char *) &flag, (socklen_t) sizeof(int));
361 (void) (error);
362 assert(error == 0);
363 }
364
365 if (TCP_KEEPIDLE) {
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));
369 (void) (error);
370 assert(error == 0);
371 }
372 }
373
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));
377 (void) (error);
378 assert(error == 0);
379 }
380
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));
384 (void) (error);
385 assert(error == 0);
386 }
387
388 /* libmemcached will always use nonblocking IO to avoid write deadlocks */
389 set_socket_nonblocking(server);
390
391 return true;
392 }
393
394 static memcached_return_t unix_socket_connect(memcached_instance_st *server) {
395 #ifndef _WIN32
396 WATCHPOINT_ASSERT(server->fd == INVALID_SOCKET);
397
398 do {
399 int type = SOCK_STREAM;
400 if (SOCK_CLOEXEC) {
401 type |= SOCK_CLOEXEC;
402 }
403
404 if (SOCK_NONBLOCK) {
405 type |= SOCK_NONBLOCK;
406 }
407
408 if ((server->fd = socket(AF_UNIX, type, 0)) == -1) {
409 return memcached_set_errno(*server, errno, NULL);
410 }
411
412 struct sockaddr_un servAddr;
413
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);
418 }
419 strncpy(servAddr.sun_path, server->hostname(),
420 sizeof(servAddr.sun_path) - 1); /* Copy filename */
421
422 if (connect(server->fd, (struct sockaddr *) &servAddr, sizeof(servAddr)) == -1) {
423 switch (errno) {
424 case EINPROGRESS:
425 case EALREADY:
426 case EAGAIN:
427 server->events(POLLOUT);
428 break;
429
430 case EINTR:
431 server->reset_socket();
432 continue;
433
434 case EISCONN: /* We were spinning waiting on connect */
435 {
436 assert(0); // Programmer error
437 server->reset_socket();
438 continue;
439 }
440
441 default:
442 WATCHPOINT_ERRNO(errno);
443 server->reset_socket();
444 return memcached_set_errno(*server, errno, MEMCACHED_AT);
445 }
446 }
447 } while (0);
448 server->state = MEMCACHED_SERVER_STATE_CONNECTED;
449
450 WATCHPOINT_ASSERT(server->fd != INVALID_SOCKET);
451
452 return MEMCACHED_SUCCESS;
453 #else
454 (void) server;
455 return MEMCACHED_NOT_SUPPORTED;
456 #endif
457 }
458
459 static memcached_return_t network_connect(memcached_instance_st *server) {
460 bool timeout_error_occured = false;
461
462 WATCHPOINT_ASSERT(server->fd == INVALID_SOCKET);
463 WATCHPOINT_ASSERT(server->cursor_active_ == 0);
464
465 /*
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.
468 */
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);
473
474 if (memcached_failed(rc)) {
475 return rc;
476 }
477 }
478
479 assert(server->address_info_next);
480 assert(server->address_info);
481
482 /* Create the socket */
483 while (server->address_info_next and server->fd == INVALID_SOCKET) {
484 int type = server->address_info_next->ai_socktype;
485 if (SOCK_CLOEXEC) {
486 type |= SOCK_CLOEXEC;
487 }
488
489 if (SOCK_NONBLOCK) {
490 type |= SOCK_NONBLOCK;
491 }
492
493 server->fd =
494 socket(server->address_info_next->ai_family, type, server->address_info_next->ai_protocol);
495
496 if (int(server->fd) == SOCKET_ERROR) {
497 return memcached_set_errno(*server, get_socket_errno(), NULL);
498 }
499
500 if (set_socket_options(server) == false) {
501 server->reset_socket();
502 return MEMCACHED_CONNECTION_FAILURE;
503 }
504
505 /* connect to server */
506 if ((connect(server->fd, server->address_info_next->ai_addr,
507 server->address_info_next->ai_addrlen)
508 != SOCKET_ERROR))
509 {
510 server->state = MEMCACHED_SERVER_STATE_CONNECTED;
511 return MEMCACHED_SUCCESS;
512 }
513
514 /* An error occurred */
515 int local_error = get_socket_errno();
516 switch (local_error) {
517 case ETIMEDOUT:
518 timeout_error_occured = true;
519 break;
520
521 #if EWOULDBLOCK != EAGAIN
522 case EWOULDBLOCK:
523 #endif
524 case EINPROGRESS: // nonblocking mode - first return
525 case EALREADY: // nonblocking mode - subsequent returns
526 {
527 server->events(POLLOUT);
528 server->state = MEMCACHED_SERVER_STATE_IN_PROGRESS;
529 memcached_return_t rc = connect_poll(server, local_error);
530
531 if (memcached_success(rc)) {
532 server->state = MEMCACHED_SERVER_STATE_CONNECTED;
533 return MEMCACHED_SUCCESS;
534 }
535
536 // A timeout here is treated as an error, we will not retry
537 if (rc == MEMCACHED_TIMEOUT) {
538 timeout_error_occured = true;
539 }
540 } break;
541
542 case EISCONN: // we are connected :-)
543 WATCHPOINT_ASSERT(0); // This is a programmer's error
544 break;
545
546 case EINTR: // Special case, we retry ai_addr
547 WATCHPOINT_ASSERT(server->fd != INVALID_SOCKET);
548 server->reset_socket();
549 continue;
550
551 case ECONNREFUSED:
552 // Probably not running service
553
554 default:
555 memcached_set_errno(*server, local_error, MEMCACHED_AT);
556 break;
557 }
558
559 WATCHPOINT_ASSERT(server->fd != INVALID_SOCKET);
560 server->reset_socket();
561 server->address_info_next = server->address_info_next->ai_next;
562 }
563
564 WATCHPOINT_ASSERT(server->fd == INVALID_SOCKET);
565
566 if (timeout_error_occured) {
567 server->reset_socket();
568 }
569
570 WATCHPOINT_STRING("Never got a good file descriptor");
571
572 if (memcached_has_current_error(*server)) {
573 return memcached_instance_error_return(server);
574 }
575
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)"));
581 }
582
583 return memcached_set_error(*server, MEMCACHED_CONNECTION_FAILURE,
584 MEMCACHED_AT); /* The last error should be from connect() */
585 }
586
587 /*
588 backoff_handling()
589
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.
592 */
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);
596
597 /*
598 If we hit server_failure_limit then something is completely wrong about the server.
599
600 1) If autoeject is enabled we do that.
601 2) If not? We go into timeout again, there is much else to do :(
602 */
603 if (server->server_failure_counter >= server->root->server_failure_limit) {
604 /*
605 We just auto_eject if we hit this point
606 */
607 if (_is_auto_eject_host(server->root)) {
608 set_last_disconnected_host(server);
609
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;
613
614 // We only retry dead servers once before assuming failure again
615 server->server_failure_counter = server->root->server_failure_limit - 1;
616 }
617
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"));
623 }
624
625 return memcached_set_error(*server, MEMCACHED_SERVER_MARKED_DEAD, MEMCACHED_AT);
626 }
627
628 server->state = MEMCACHED_SERVER_STATE_IN_TIMEOUT;
629
630 // Sanity check/setting
631 if (server->next_retry == 0) {
632 server->next_retry = 1;
633 }
634 }
635
636 if (server->state == MEMCACHED_SERVER_STATE_IN_TIMEOUT) {
637 /*
638 If next_retry is less then our current time, then we reset and try everything again.
639 */
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;
643 } else {
644 return memcached_set_error(*server, MEMCACHED_SERVER_TEMPORARILY_DISABLED, MEMCACHED_AT);
645 }
646
647 in_timeout = true;
648 }
649
650 return MEMCACHED_SUCCESS;
651 }
652
653 static memcached_return_t _memcached_connect(memcached_instance_st *server,
654 const bool set_last_disconnected) {
655 assert(server);
656 if (server->fd != INVALID_SOCKET) {
657 return MEMCACHED_SUCCESS;
658 }
659
660 LIBMEMCACHED_MEMCACHED_CONNECT_START();
661
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);
666 return rc;
667 }
668
669 if (LIBMEMCACHED_WITH_SASL_SUPPORT and server->root->sasl.callbacks
670 and memcached_is_udp(server->root))
671 {
672 return memcached_set_error(
673 *server, MEMCACHED_INVALID_HOST_PROTOCOL, MEMCACHED_AT,
674 memcached_literal_param("SASL is not supported for UDP connections"));
675 }
676
677 if (server->hostname()[0] == '/') {
678 server->type = MEMCACHED_CONNECTION_UNIX_SOCKET;
679 }
680
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);
686
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();
694 }
695 }
696 }
697 #endif
698 break;
699
700 case MEMCACHED_CONNECTION_UNIX_SOCKET:
701 rc = unix_socket_connect(server);
702 break;
703 }
704
705 if (memcached_success(rc)) {
706 server->mark_server_as_clean();
707 memcached_version_instance(server);
708 return rc;
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)));
714 } else {
715 memcached_set_error(*server, rc, MEMCACHED_AT);
716 memcached_mark_server_for_timeout(server);
717 }
718
719 LIBMEMCACHED_MEMCACHED_CONNECT_END();
720
721 if (in_timeout) {
722 char buffer[1024];
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);
727 }
728 }
729
730 return rc;
731 }
732
733 memcached_return_t memcached_connect(memcached_instance_st *server) {
734 return _memcached_connect(server, true);
735 }