ff7772ca1c39bb97f14d5b6a396160fa59db1a67
[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)) == SOCKET_ERROR) {
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 != EALREADY) {
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 != EALREADY) {
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 EAGAIN:
525 case EINPROGRESS: // nonblocking mode - first return
526 case EALREADY: // nonblocking mode - subsequent returns
527 {
528 server->events(POLLOUT);
529 server->state = MEMCACHED_SERVER_STATE_IN_PROGRESS;
530 memcached_return_t rc = connect_poll(server, local_error);
531
532 if (memcached_success(rc)) {
533 server->state = MEMCACHED_SERVER_STATE_CONNECTED;
534 return MEMCACHED_SUCCESS;
535 }
536
537 // A timeout here is treated as an error, we will not retry
538 if (rc == MEMCACHED_TIMEOUT) {
539 timeout_error_occured = true;
540 }
541 } break;
542
543 case EISCONN: // we are connected :-)
544 WATCHPOINT_ASSERT(0); // This is a programmer's error
545 break;
546
547 case EINTR: // Special case, we retry ai_addr
548 WATCHPOINT_ASSERT(server->fd != INVALID_SOCKET);
549 server->reset_socket();
550 continue;
551
552 case ECONNREFUSED:
553 // Probably not running service
554
555 default:
556 memcached_set_errno(*server, local_error, MEMCACHED_AT);
557 break;
558 }
559
560 WATCHPOINT_ASSERT(server->fd != INVALID_SOCKET);
561 server->reset_socket();
562 server->address_info_next = server->address_info_next->ai_next;
563 }
564
565 WATCHPOINT_ASSERT(server->fd == INVALID_SOCKET);
566
567 if (timeout_error_occured) {
568 server->reset_socket();
569 }
570
571 WATCHPOINT_STRING("Never got a good file descriptor");
572
573 if (memcached_has_current_error(*server)) {
574 return memcached_instance_error_return(server);
575 }
576
577 if (timeout_error_occured and server->state < MEMCACHED_SERVER_STATE_IN_PROGRESS) {
578 return memcached_set_error(
579 *server, MEMCACHED_TIMEOUT, MEMCACHED_AT,
580 memcached_literal_param(
581 "if (timeout_error_occured and server->state < MEMCACHED_SERVER_STATE_IN_PROGRESS)"));
582 }
583
584 return memcached_set_error(*server, MEMCACHED_CONNECTION_FAILURE,
585 MEMCACHED_AT); /* The last error should be from connect() */
586 }
587
588 /*
589 backoff_handling()
590
591 Based on time/failure count fail the connect without trying. This prevents waiting in a state
592 where we get caught spending cycles just waiting.
593 */
594 static memcached_return_t backoff_handling(memcached_instance_st *server, bool &in_timeout) {
595 struct timeval curr_time;
596 bool _gettime_success = (gettimeofday(&curr_time, NULL) == 0);
597
598 /*
599 If we hit server_failure_limit then something is completely wrong about the server.
600
601 1) If autoeject is enabled we do that.
602 2) If not? We go into timeout again, there is much else to do :(
603 */
604 if (server->server_failure_counter >= server->root->server_failure_limit) {
605 /*
606 We just auto_eject if we hit this point
607 */
608 if (_is_auto_eject_host(server->root)) {
609 set_last_disconnected_host(server);
610
611 // Retry dead servers if requested
612 if (_gettime_success and server->root->dead_timeout > 0) {
613 server->next_retry = curr_time.tv_sec + server->root->dead_timeout;
614
615 // We only retry dead servers once before assuming failure again
616 server->server_failure_counter = server->root->server_failure_limit - 1;
617 }
618
619 memcached_return_t rc;
620 if (memcached_failed(rc = run_distribution((memcached_st *) server->root))) {
621 return memcached_set_error(
622 *server, rc, MEMCACHED_AT,
623 memcached_literal_param("Backoff handling failed during run_distribution"));
624 }
625
626 return memcached_set_error(*server, MEMCACHED_SERVER_MARKED_DEAD, MEMCACHED_AT);
627 }
628
629 server->state = MEMCACHED_SERVER_STATE_IN_TIMEOUT;
630
631 // Sanity check/setting
632 if (server->next_retry == 0) {
633 server->next_retry = 1;
634 }
635 }
636
637 if (server->state == MEMCACHED_SERVER_STATE_IN_TIMEOUT) {
638 /*
639 If next_retry is less then our current time, then we reset and try everything again.
640 */
641 if (_gettime_success and server->next_retry < curr_time.tv_sec) {
642 server->state = MEMCACHED_SERVER_STATE_NEW;
643 server->server_timeout_counter = 0;
644 } else {
645 return memcached_set_error(*server, MEMCACHED_SERVER_TEMPORARILY_DISABLED, MEMCACHED_AT);
646 }
647
648 in_timeout = true;
649 }
650
651 return MEMCACHED_SUCCESS;
652 }
653
654 static memcached_return_t _memcached_connect(memcached_instance_st *server,
655 const bool set_last_disconnected) {
656 assert(server);
657 if (server->fd != INVALID_SOCKET) {
658 return MEMCACHED_SUCCESS;
659 }
660
661 LIBMEMCACHED_MEMCACHED_CONNECT_START();
662
663 bool in_timeout = false;
664 memcached_return_t rc;
665 if (memcached_failed(rc = backoff_handling(server, in_timeout))) {
666 set_last_disconnected_host(server);
667 return rc;
668 }
669
670 if (LIBMEMCACHED_WITH_SASL_SUPPORT and server->root->sasl.callbacks
671 and memcached_is_udp(server->root))
672 {
673 return memcached_set_error(
674 *server, MEMCACHED_INVALID_HOST_PROTOCOL, MEMCACHED_AT,
675 memcached_literal_param("SASL is not supported for UDP connections"));
676 }
677
678 if (server->hostname()[0] == '/') {
679 server->type = MEMCACHED_CONNECTION_UNIX_SOCKET;
680 }
681
682 /* We need to clean up the multi startup piece */
683 switch (server->type) {
684 case MEMCACHED_CONNECTION_UDP:
685 case MEMCACHED_CONNECTION_TCP:
686 rc = network_connect(server);
687
688 #if defined(LIBMEMCACHED_WITH_SASL_SUPPORT)
689 if (LIBMEMCACHED_WITH_SASL_SUPPORT) {
690 if (server->fd != INVALID_SOCKET and server->root->sasl.callbacks) {
691 rc = memcached_sasl_authenticate_connection(server);
692 if (memcached_failed(rc) and server->fd != INVALID_SOCKET) {
693 WATCHPOINT_ASSERT(server->fd != INVALID_SOCKET);
694 server->reset_socket();
695 }
696 }
697 }
698 #endif
699 break;
700
701 case MEMCACHED_CONNECTION_UNIX_SOCKET:
702 rc = unix_socket_connect(server);
703 break;
704 }
705
706 if (memcached_success(rc)) {
707 server->mark_server_as_clean();
708 memcached_version_instance(server);
709 return rc;
710 } else if (set_last_disconnected) {
711 set_last_disconnected_host(server);
712 if (memcached_has_current_error(*server)) {
713 memcached_mark_server_for_timeout(server);
714 assert(memcached_failed(memcached_instance_error_return(server)));
715 } else {
716 memcached_set_error(*server, rc, MEMCACHED_AT);
717 memcached_mark_server_for_timeout(server);
718 }
719
720 LIBMEMCACHED_MEMCACHED_CONNECT_END();
721
722 if (in_timeout) {
723 char buffer[1024];
724 int snprintf_length =
725 snprintf(buffer, sizeof(buffer), "%s:%d", server->hostname(), int(server->port()));
726 return memcached_set_error(*server, MEMCACHED_SERVER_TEMPORARILY_DISABLED, MEMCACHED_AT,
727 buffer, snprintf_length);
728 }
729 }
730
731 return rc;
732 }
733
734 memcached_return_t memcached_connect(memcached_instance_st *server) {
735 return _memcached_connect(server, true);
736 }