|
48 | 48 | timeout as SocketTimeout,
|
49 | 49 | )
|
50 | 50 | from ssl import (
|
| 51 | + CertificateError, |
51 | 52 | HAS_SNI,
|
52 | 53 | SSLError,
|
53 | 54 | )
|
|
59 | 60 | from time import perf_counter
|
60 | 61 |
|
61 | 62 | from neo4j._exceptions import (
|
| 63 | + BoltError, |
62 | 64 | BoltHandshakeError,
|
63 | 65 | BoltProtocolError,
|
64 | 66 | BoltRoutingError,
|
|
77 | 79 | from neo4j.exceptions import (
|
78 | 80 | ClientError,
|
79 | 81 | ConfigurationError,
|
| 82 | + DriverError, |
80 | 83 | ReadServiceUnavailable,
|
81 | 84 | ServiceUnavailable,
|
82 | 85 | SessionExpired,
|
@@ -768,6 +771,7 @@ def update_routing_table_from(self, *routers, database=None,
|
768 | 771 | address, self.routing_tables[database]
|
769 | 772 | )
|
770 | 773 | return True
|
| 774 | + self.deactivate(router) |
771 | 775 | return False
|
772 | 776 |
|
773 | 777 | def update_routing_table(self, *, database, bookmarks):
|
@@ -963,25 +967,34 @@ def _connect(resolved_address, timeout, keep_alive):
|
963 | 967 | return s
|
964 | 968 |
|
965 | 969 |
|
966 |
| -def _secure(s, host, ssl_context): |
| 970 | +def _secure(s, hosts, ssl_context): |
967 | 971 | local_port = s.getsockname()[1]
|
968 | 972 | # Secure the connection if an SSL context has been provided
|
| 973 | + if not hosts: |
| 974 | + hosts = [None] |
969 | 975 | if ssl_context:
|
970 |
| - log.debug("[#%04X] C: <SECURE> %s", local_port, host) |
971 |
| - try: |
972 |
| - sni_host = host if HAS_SNI and host else None |
973 |
| - s = ssl_context.wrap_socket(s, server_hostname=sni_host) |
974 |
| - except (SSLError, OSError) as cause: |
975 |
| - _close_socket(s) |
976 |
| - error = BoltSecurityError(message="Failed to establish encrypted connection.", address=(host, local_port)) |
977 |
| - error.__cause__ = cause |
978 |
| - raise error |
979 |
| - else: |
980 |
| - # Check that the server provides a certificate |
981 |
| - der_encoded_server_certificate = s.getpeercert(binary_form=True) |
982 |
| - if der_encoded_server_certificate is None: |
983 |
| - s.close() |
984 |
| - raise BoltProtocolError("When using an encrypted socket, the server should always provide a certificate", address=(host, local_port)) |
| 976 | + last_error = None |
| 977 | + for host in hosts: |
| 978 | + log.debug("[#%04X] C: <SECURE> %s", local_port, host) |
| 979 | + try: |
| 980 | + sni_host = host if HAS_SNI and host else None |
| 981 | + s = ssl_context.wrap_socket(s, server_hostname=sni_host) |
| 982 | + except (SSLError, CertificateError) as cause: |
| 983 | + last_error = cause |
| 984 | + continue |
| 985 | + except OSError as cause: |
| 986 | + # No sense in trying another host name with a broken socket |
| 987 | + last_error = cause |
| 988 | + break |
| 989 | + else: |
| 990 | + # Check that the server provides a certificate |
| 991 | + der_encoded_server_certificate = s.getpeercert(binary_form=True) |
| 992 | + if der_encoded_server_certificate is None: |
| 993 | + raise BoltProtocolError("When using an encrypted socket, the server should always provide a certificate", address=(host, local_port)) |
| 994 | + return s |
| 995 | + raise BoltSecurityError( |
| 996 | + message="Failed to establish encrypted connection.", |
| 997 | + address=(hosts[0], local_port)) from last_error |
985 | 998 | return s
|
986 | 999 |
|
987 | 1000 |
|
@@ -1057,23 +1070,31 @@ def connect(address, *, timeout, custom_resolver, ssl_context, keep_alive):
|
1057 | 1070 | # Catches refused connections see:
|
1058 | 1071 | # https://docs.python.org/2/library/errno.html
|
1059 | 1072 |
|
1060 |
| - for resolved_address in Address(address).resolve(resolver=custom_resolver): |
| 1073 | + resolved_addresses = Address(address).resolve(resolver=custom_resolver) |
| 1074 | + for resolved_address in resolved_addresses: |
1061 | 1075 | s = None
|
1062 | 1076 | try:
|
1063 |
| - host = resolved_address[0] |
1064 | 1077 | s = _connect(resolved_address, timeout, keep_alive)
|
1065 |
| - s = _secure(s, host, ssl_context) |
| 1078 | + s = _secure(s, resolved_address.host_names, ssl_context) |
1066 | 1079 | return _handshake(s, resolved_address)
|
1067 |
| - except Exception as error: |
| 1080 | + except (BoltError, DriverError, OSError) as error: |
1068 | 1081 | if s:
|
1069 | 1082 | _close_socket(s)
|
1070 | 1083 | last_error = error
|
| 1084 | + except Exception: |
| 1085 | + if s: |
| 1086 | + _close_socket(s) |
| 1087 | + raise |
1071 | 1088 | if last_error is None:
|
1072 |
| - raise ServiceUnavailable("Failed to resolve addresses for %s" % |
1073 |
| - str(address)) |
| 1089 | + raise ServiceUnavailable( |
| 1090 | + "Couldn't connect to %s (resolved to %s)" % ( |
| 1091 | + str(address), tuple(map(str, resolved_addresses))) |
| 1092 | + ) |
1074 | 1093 | else:
|
1075 |
| - raise ServiceUnavailable("Failed to resolve addresses for %s" % |
1076 |
| - str(address)) from last_error |
| 1094 | + raise ServiceUnavailable( |
| 1095 | + "Couldn't connect to %s (resolved to %s)" % ( |
| 1096 | + str(address), tuple(map(str, resolved_addresses))) |
| 1097 | + ) from last_error |
1077 | 1098 |
|
1078 | 1099 |
|
1079 | 1100 | def check_supported_server_product(agent):
|
|
0 commit comments