summaryrefslogtreecommitdiff
path: root/ldap3/strategy/base.py
diff options
context:
space:
mode:
Diffstat (limited to 'ldap3/strategy/base.py')
-rw-r--r--ldap3/strategy/base.py77
1 files changed, 45 insertions, 32 deletions
diff --git a/ldap3/strategy/base.py b/ldap3/strategy/base.py
index 568459e..1acc732 100644
--- a/ldap3/strategy/base.py
+++ b/ldap3/strategy/base.py
@@ -24,15 +24,20 @@
# If not, see <http://www.gnu.org/licenses/>.
import socket
+try: # try to discover if unix sockets are available for LDAP over IPC (ldapi:// scheme)
+ # noinspection PyUnresolvedReferences
+ from socket import AF_UNIX
+ unix_socket_available = True
+except ImportError:
+ unix_socket_available = False
from struct import pack
from platform import system
-from time import sleep
from random import choice
from .. import SYNC, ANONYMOUS, get_config_parameter, BASE, ALL_ATTRIBUTES, ALL_OPERATIONAL_ATTRIBUTES, NO_ATTRIBUTES
from ..core.results import DO_NOT_RAISE_EXCEPTIONS, RESULT_REFERRAL
from ..core.exceptions import LDAPOperationResult, LDAPSASLBindInProgressError, LDAPSocketOpenError, LDAPSessionTerminatedByServerError,\
- LDAPUnknownResponseError, LDAPUnknownRequestError, LDAPReferralError, communication_exception_factory, \
+ LDAPUnknownResponseError, LDAPUnknownRequestError, LDAPReferralError, communication_exception_factory, LDAPStartTLSError, \
LDAPSocketSendError, LDAPExceptionError, LDAPControlError, LDAPResponseTimeoutError, LDAPTransactionError
from ..utils.uri import parse_uri
from ..protocol.rfc4511 import LDAPMessage, ProtocolOp, MessageID, SearchResultEntry
@@ -78,6 +83,7 @@ class BaseStrategy(object):
self.pooled = None # Indicates a connection with a connection pool
self.can_stream = None # indicates if a strategy keeps a stream of responses (i.e. LdifProducer can accumulate responses with a single header). Stream must be initialized and closed in _start_listen() and _stop_listen()
self.referral_cache = {}
+ self.thread_safe = False # Indicates that connection can be used in a multithread application
if log_enabled(BASIC):
log(BASIC, 'instantiated <%s>: <%s>', self.__class__.__name__, self)
@@ -141,9 +147,6 @@ class BaseStrategy(object):
if log_enabled(ERROR):
log(ERROR, 'unable to open socket for <%s>', self.connection)
raise LDAPSocketOpenError('unable to open socket', exception_history)
- if log_enabled(ERROR):
- log(ERROR, 'unable to open socket for <%s>', self.connection)
- raise LDAPSocketOpenError('unable to open socket', exception_history)
elif not self.connection.server.current_address:
if log_enabled(ERROR):
log(ERROR, 'invalid server address for <%s>', self.connection)
@@ -151,7 +154,6 @@ class BaseStrategy(object):
self.connection._deferred_open = False
self._start_listen()
- # self.connection.do_auto_bind()
if log_enabled(NETWORK):
log(NETWORK, 'connection open for <%s>', self.connection)
@@ -199,7 +201,6 @@ class BaseStrategy(object):
log(ERROR, '<%s> for <%s>', self.connection.last_error, self.connection)
# raise communication_exception_factory(LDAPSocketOpenError, exc)(self.connection.last_error)
raise communication_exception_factory(LDAPSocketOpenError, type(e)(str(e)))(self.connection.last_error)
-
# Try to bind the socket locally before connecting to the remote address
# We go through our connection's source ports and try to bind our socket to our connection's source address
# with them.
@@ -210,25 +211,26 @@ class BaseStrategy(object):
# issue when no source address/port is specified if the system checking server availability is running
# as a very unprivileged user.
last_bind_exc = None
- socket_bind_succeeded = False
- for source_port in self.connection.source_port_list:
- try:
- self.connection.socket.bind((self.connection.source_address, source_port))
- socket_bind_succeeded = True
- break
- except Exception as bind_ex:
- last_bind_exc = bind_ex
- # we'll always end up logging at error level if we cannot bind any ports to the address locally.
- # but if some work and some don't you probably don't want the ones that don't at ERROR level
- if log_enabled(NETWORK):
- log(NETWORK, 'Unable to bind to local address <%s> with source port <%s> due to <%s>',
- self.connection.source_address, source_port, bind_ex)
- if not socket_bind_succeeded:
- self.connection.last_error = 'socket connection error while locally binding: ' + str(last_bind_exc)
- if log_enabled(ERROR):
- log(ERROR, 'Unable to locally bind to local address <%s> with any of the source ports <%s> for connection <%s due to <%s>',
- self.connection.source_address, self.connection.source_port_list, self.connection, last_bind_exc)
- raise communication_exception_factory(LDAPSocketOpenError, type(last_bind_exc)(str(last_bind_exc)))(last_bind_exc)
+ if unix_socket_available and self.connection.socket.family != socket.AF_UNIX:
+ socket_bind_succeeded = False
+ for source_port in self.connection.source_port_list:
+ try:
+ self.connection.socket.bind((self.connection.source_address, source_port))
+ socket_bind_succeeded = True
+ break
+ except Exception as bind_ex:
+ last_bind_exc = bind_ex
+ # we'll always end up logging at error level if we cannot bind any ports to the address locally.
+ # but if some work and some don't you probably don't want the ones that don't at ERROR level
+ if log_enabled(NETWORK):
+ log(NETWORK, 'Unable to bind to local address <%s> with source port <%s> due to <%s>',
+ self.connection.source_address, source_port, bind_ex)
+ if not socket_bind_succeeded:
+ self.connection.last_error = 'socket connection error while locally binding: ' + str(last_bind_exc)
+ if log_enabled(ERROR):
+ log(ERROR, 'Unable to locally bind to local address <%s> with any of the source ports <%s> for connection <%s due to <%s>',
+ self.connection.source_address, self.connection.source_port_list, self.connection, last_bind_exc)
+ raise communication_exception_factory(LDAPSocketOpenError, type(last_bind_exc)(str(last_bind_exc)))(last_bind_exc)
try: # set socket timeout for opening connection
if self.connection.server.connect_timeout:
@@ -273,7 +275,6 @@ class BaseStrategy(object):
self.connection.last_error = 'socket ssl wrapping error: ' + str(e)
if log_enabled(ERROR):
log(ERROR, '<%s> for <%s>', self.connection.last_error, self.connection)
- # raise communication_exception_factory(LDAPSocketOpenError, exc)(self.connection.last_error)
raise communication_exception_factory(LDAPSocketOpenError, type(e)(str(e)))(self.connection.last_error)
if self.connection.usage:
self.connection._usage.open_sockets += 1
@@ -693,13 +694,20 @@ class BaseStrategy(object):
search_scope=BASE,
dereference_aliases=request['dereferenceAlias'],
attributes=[attr_type + ';range=' + str(int(high_range) + 1) + '-*'])
- if isinstance(result, bool):
- if result:
- current_response = self.connection.response[0]
+ if self.connection.strategy.thread_safe:
+ status, result, _response, _ = result
+ else:
+ status = result
+ result = self.connection.result
+ _response = self.connection.response
+
+ if self.connection.strategy.sync:
+ if status:
+ current_response = _response[0]
else:
done = True
else:
- current_response, _ = self.get_response(result)
+ current_response, _ = self.get_response(status)
current_response = current_response[0]
if not done:
@@ -778,7 +786,12 @@ class BaseStrategy(object):
referral_connection.open()
referral_connection.strategy._referrals = self._referrals
if self.connection.tls_started and not referral_server.ssl: # if the original server was in start_tls mode and the referral server is not in ssl then start_tls on the referral connection
- referral_connection.start_tls()
+ if not referral_connection.start_tls():
+ error = 'start_tls in referral not successful' + (' - ' + referral_connection.last_error if referral_connection.last_error else '')
+ if log_enabled(ERROR):
+ log(ERROR, '%s for <%s>', error, self)
+ self.unbind()
+ raise LDAPStartTLSError(error)
if self.connection.bound:
referral_connection.bind()