Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
server.py683 linesDownload Raw Back to core
1"""2"""3 4# Created on 2014.05.315#6# Author: Giovanni Cannata7#8# Copyright 2014 - 2020 Giovanni Cannata9#10# This file is part of ldap3.11#12# ldap3 is free software: you can redistribute it and/or modify13# it under the terms of the GNU Lesser General Public License as published14# by the Free Software Foundation, either version 3 of the License, or15# (at your option) any later version.16#17# ldap3 is distributed in the hope that it will be useful,18# but WITHOUT ANY WARRANTY; without even the implied warranty of19# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the20# GNU Lesser General Public License for more details.21#22# You should have received a copy of the GNU Lesser General Public License23# along with ldap3 in the COPYING and COPYING.LESSER files.24# If not, see <http://www.gnu.org/licenses/>.25 26import socket27from threading import Lock28from datetime import datetime, MINYEAR29 30from .. import DSA, SCHEMA, ALL, BASE, get_config_parameter, OFFLINE_EDIR_8_8_8, OFFLINE_EDIR_9_1_4, OFFLINE_AD_2012_R2, OFFLINE_SLAPD_2_4, OFFLINE_DS389_1_3_3, SEQUENCE_TYPES, IP_SYSTEM_DEFAULT, IP_V4_ONLY, IP_V6_ONLY, IP_V4_PREFERRED, IP_V6_PREFERRED, STRING_TYPES31from .exceptions import LDAPInvalidServerError, LDAPDefinitionError, LDAPInvalidPortError, LDAPInvalidTlsSpecificationError, LDAPSocketOpenError, LDAPInfoError32from ..protocol.formatters.standard import format_attribute_values33from ..protocol.rfc4511 import LDAP_MAX_INT34from ..protocol.rfc4512 import SchemaInfo, DsaInfo35from .tls import Tls36from ..utils.log import log, log_enabled, ERROR, BASIC, PROTOCOL, NETWORK37from ..utils.conv import to_unicode38from ..utils.port_validators import check_port, check_port_and_port_list39 40try:41    from urllib.parse import unquote  # Python 342except ImportError:43    from urllib import unquote  # Python 244 45try:  # try to discover if unix sockets are available for LDAP over IPC (ldapi:// scheme)46    # noinspection PyUnresolvedReferences47    from socket import AF_UNIX48    unix_socket_available = True49except ImportError:50    unix_socket_available = False51 52 53class Server(object):54    """55    LDAP Server definition class56 57    Allowed_referral_hosts can be None (default), or a list of tuples of58    allowed servers ip address or names to contact while redirecting59    search to referrals.60 61    The second element of the tuple is a boolean to indicate if62    authentication to that server is allowed; if False only anonymous63    bind will be used.64 65    Per RFC 4516. Use [('*', False)] to allow any host with anonymous66    bind, use [('*', True)] to allow any host with same authentication of67    Server.68    """69 70    _message_counter = 071    _message_id_lock = Lock()  # global lock for message_id shared by all Server objects72 73    def __init__(self,74                 host,75                 port=None,76                 use_ssl=False,77                 allowed_referral_hosts=None,78                 get_info=SCHEMA,79                 tls=None,80                 formatter=None,81                 connect_timeout=None,82                 mode=IP_V6_PREFERRED,83                 validator=None):84 85        self.ipc = False86        url_given = False87        host = host.strip()88        if host.lower().startswith('ldap://'):89            self.host = host[7:]90            use_ssl = False91            url_given = True92        elif host.lower().startswith('ldaps://'):93            self.host = host[8:]94            use_ssl = True95            url_given = True96        elif host.lower().startswith('ldapi://') and unix_socket_available:97            self.ipc = True98            use_ssl = False99            url_given = True100        elif host.lower().startswith('ldapi://') and not unix_socket_available:101            raise LDAPSocketOpenError('LDAP over IPC not available - UNIX sockets non present')102        else:103            self.host = host104 105        if self.ipc:106            if str is bytes:  # Python 2107                self.host = unquote(host[7:]).decode('utf-8')108            else:  # Python 3109                self.host = unquote(host[7:])  # encoding defaults to utf-8 in python3110            self.port = None111        elif ':' in self.host and self.host.count(':') == 1:112            hostname, _, hostport = self.host.partition(':')113            try:114                port = int(hostport) or port115            except ValueError:116                if log_enabled(ERROR):117                    log(ERROR, 'port <%s> must be an integer', port)118                raise LDAPInvalidPortError('port must be an integer')119            self.host = hostname120        elif url_given and self.host.startswith('['):121            hostname, sep, hostport = self.host[1:].partition(']')122            if sep != ']' or not self._is_ipv6(hostname):123                if log_enabled(ERROR):124                    log(ERROR, 'invalid IPv6 server address for <%s>', self.host)125                raise LDAPInvalidServerError()126            if len(hostport):127                if not hostport.startswith(':'):128                    if log_enabled(ERROR):129                        log(ERROR, 'invalid URL in server name for <%s>', self.host)130                    raise LDAPInvalidServerError('invalid URL in server name')131                if not hostport[1:].isdecimal():132                    if log_enabled(ERROR):133                        log(ERROR, 'port must be an integer for <%s>', self.host)134                    raise LDAPInvalidPortError('port must be an integer')135                port = int(hostport[1:])136            self.host = hostname137        elif not url_given and self._is_ipv6(self.host):138            pass139        elif self.host.count(':') > 1:140            if log_enabled(ERROR):141                log(ERROR, 'invalid server address for <%s>', self.host)142            raise LDAPInvalidServerError()143 144        if not self.ipc:145            self.host.rstrip('/')146            if not use_ssl and not port:147                port = 389148            elif use_ssl and not port:149                port = 636150 151            port_err = check_port(port)152            if port_err:153                if log_enabled(ERROR):154                    log(ERROR, port_err)155                raise LDAPInvalidPortError(port_err)156            self.port = port157 158        if allowed_referral_hosts is None:  # defaults to any server with authentication159            allowed_referral_hosts = [('*', True)]160 161        if isinstance(allowed_referral_hosts, SEQUENCE_TYPES):162            self.allowed_referral_hosts = []163            for referral_host in allowed_referral_hosts:164                if isinstance(referral_host, tuple):165                    if isinstance(referral_host[1], bool):166                        self.allowed_referral_hosts.append(referral_host)167        elif isinstance(allowed_referral_hosts, tuple):168            if isinstance(allowed_referral_hosts[1], bool):169                self.allowed_referral_hosts = [allowed_referral_hosts]170        else:171            self.allowed_referral_hosts = []172 173        self.ssl = True if use_ssl else False174        if tls and not isinstance(tls, Tls):175            if log_enabled(ERROR):176                log(ERROR, 'invalid tls specification: <%s>', tls)177            raise LDAPInvalidTlsSpecificationError('invalid Tls object')178 179        self.tls = Tls() if self.ssl and not tls else tls180 181        if not self.ipc:182            if self._is_ipv6(self.host):183                self.name = ('ldaps' if self.ssl else 'ldap') + '://[' + self.host + ']:' + str(self.port)184            else:185                self.name = ('ldaps' if self.ssl else 'ldap') + '://' + self.host + ':' + str(self.port)186        else:187            self.name = host188 189        self.get_info = get_info190        self._dsa_info = None191        self._schema_info = None192        self.dit_lock = Lock()193        self.custom_formatter = formatter194        self.custom_validator = validator195        self._address_info = []  # property self.address_info resolved at open time (or when check_availability is called)196        self._address_info_resolved_time = datetime(MINYEAR, 1, 1)  # smallest date ever197        self.current_address = None198        self.connect_timeout = connect_timeout199        self.mode = mode200 201        self.get_info_from_server(None)  # load offline schema if needed202 203        if log_enabled(BASIC):204            log(BASIC, 'instantiated Server: <%r>', self)205 206    @staticmethod207    def _is_ipv6(host):208        try:209            socket.inet_pton(socket.AF_INET6, host)210        except (socket.error, AttributeError, ValueError):211            return False212        return True213 214    def __str__(self):215        if self.host:216            s = self.name + (' - ssl' if self.ssl else ' - cleartext') + (' - unix socket' if self.ipc else '')217        else:218            s = object.__str__(self)219        return s220 221    def __repr__(self):222        r = 'Server(host={0.host!r}, port={0.port!r}, use_ssl={0.ssl!r}'.format(self)223        r += '' if not self.allowed_referral_hosts else ', allowed_referral_hosts={0.allowed_referral_hosts!r}'.format(self)224        r += '' if self.tls is None else ', tls={0.tls!r}'.format(self)225        r += '' if not self.get_info else ', get_info={0.get_info!r}'.format(self)226        r += '' if not self.connect_timeout else ', connect_timeout={0.connect_timeout!r}'.format(self)227        r += '' if not self.mode else ', mode={0.mode!r}'.format(self)228        r += ')'229 230        return r231 232    @property233    def address_info(self):234        conf_refresh_interval = get_config_parameter('ADDRESS_INFO_REFRESH_TIME')235        if not self._address_info or (datetime.now() - self._address_info_resolved_time).seconds > conf_refresh_interval:236            # converts addresses tuple to list and adds a 6th parameter for availability (None = not checked, True = available, False=not available) and a 7th parameter for the checking time237            addresses = None238            try:239                if self.ipc:240                    addresses = [(socket.AF_UNIX, socket.SOCK_STREAM, 0, None, self.host, None)]241                else:242                    if self.mode == IP_V4_ONLY:243                        addresses = socket.getaddrinfo(self.host, self.port, socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP, socket.AI_ADDRCONFIG | socket.AI_V4MAPPED)244                    elif self.mode == IP_V6_ONLY:245                        addresses = socket.getaddrinfo(self.host, self.port, socket.AF_INET6, socket.SOCK_STREAM, socket.IPPROTO_TCP, socket.AI_ADDRCONFIG | socket.AI_V4MAPPED)246                    else:247                        addresses = socket.getaddrinfo(self.host, self.port, socket.AF_UNSPEC, socket.SOCK_STREAM, socket.IPPROTO_TCP, socket.AI_ADDRCONFIG | socket.AI_V4MAPPED)248            except (socket.gaierror, AttributeError):249                pass250 251            if not addresses:  # if addresses not found or raised an exception (for example for bad flags) tries again without flags252                try:253                    if self.mode == IP_V4_ONLY:254                        addresses = socket.getaddrinfo(self.host, self.port, socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP)255                    elif self.mode == IP_V6_ONLY:256                        addresses = socket.getaddrinfo(self.host, self.port, socket.AF_INET6, socket.SOCK_STREAM, socket.IPPROTO_TCP)257                    else:258                        addresses = socket.getaddrinfo(self.host, self.port, socket.AF_UNSPEC, socket.SOCK_STREAM, socket.IPPROTO_TCP)259                except socket.gaierror:260                    pass261 262            if addresses:263                self._address_info = [list(address) + [None, None] for address in addresses]264                self._address_info_resolved_time = datetime.now()265            else:266                self._address_info = []267                self._address_info_resolved_time = datetime(MINYEAR, 1, 1)  # smallest date268 269            if log_enabled(BASIC):270                for address in self._address_info:271                    log(BASIC, 'address for <%s> resolved as <%r>', self, address[:-2])272        return self._address_info273 274    def update_availability(self, address, available):275        cont = 0276        while cont < len(self._address_info):277            if self.address_info[cont] == address:278                self._address_info[cont][5] = True if available else False279                self._address_info[cont][6] = datetime.now()280                break281            cont += 1282 283    def reset_availability(self):284        for address in self._address_info:285            address[5] = None286            address[6] = None287 288    def check_availability(self, source_address=None, source_port=None, source_port_list=None):289        """290        Tries to open, connect and close a socket to specified address and port to check availability.291        Timeout in seconds is specified in CHECK_AVAILABITY_TIMEOUT if not specified in292        the Server object.293        If specified, use a specific address, port, or list of possible ports, when attempting to check availability.294        NOTE: This will only consider multiple ports from the source port list if the first ones we try to bind to are295              already in use. This will not attempt using different ports in the list if the server is unavailable,296              as that could result in the runtime of check_availability significantly exceeding the connection timeout.297        """298        source_port_err = check_port_and_port_list(source_port, source_port_list)299        if source_port_err:300            if log_enabled(ERROR):301                log(ERROR, source_port_err)302            raise LDAPInvalidPortError(source_port_err)303 304        # using an empty string to bind a socket means "use the default as if this wasn't provided" because socket305        # binding requires that you pass something for the ip if you want to pass a specific port306        bind_address = source_address if source_address is not None else ''307        # using 0 as the source port to bind a socket means "use the default behavior of picking a random port from308        # all ports as if this wasn't provided" because socket binding requires that you pass something for the port309        # if you want to pass a specific ip310        candidate_bind_ports = [0]311 312        # if we have either a source port or source port list, convert that into our candidate list313        if source_port is not None:314            candidate_bind_ports = [source_port]315        elif source_port_list is not None:316            candidate_bind_ports = source_port_list[:]317 318        conf_availability_timeout = get_config_parameter('CHECK_AVAILABILITY_TIMEOUT')319        available = False320        self.reset_availability()321        for address in self.candidate_addresses():322            available = True323            try:324                temp_socket = socket.socket(*address[:3])325 326                # Go through our candidate bind ports and try to bind our socket to our source address with them.327                # if no source address or ports were specified, this will have the same success/fail result as if we328                # tried to connect to the remote server without binding locally first.329                # This is actually a little bit better, as it lets us distinguish the case of "issue binding the socket330                # locally" from "remote server is unavailable" with more clarity, though this will only really be an331                # issue when no source address/port is specified if the system checking server availability is running332                # as a very unprivileged user.333                last_bind_exc = None334                socket_bind_succeeded = False335                for bind_port in candidate_bind_ports:336                    try:337                        temp_socket.bind((bind_address, bind_port))338                        socket_bind_succeeded = True339                        break340                    except Exception as bind_ex:341                        last_bind_exc = bind_ex342                        if log_enabled(NETWORK):343                            log(NETWORK, 'Unable to bind to local address <%s> with source port <%s> due to <%s>',344                                bind_address, bind_port, bind_ex)345                if not socket_bind_succeeded:346                    if log_enabled(ERROR):347                        log(ERROR, 'Unable to locally bind to local address <%s> with any of the source ports <%s> due to <%s>',348                            bind_address, candidate_bind_ports, last_bind_exc)349                    raise LDAPSocketOpenError('Unable to bind socket locally to address {} with any of the source ports {} due to {}'350                                              .format(bind_address, candidate_bind_ports, last_bind_exc))351 352                if self.connect_timeout:353                    temp_socket.settimeout(self.connect_timeout)354                else:355                    temp_socket.settimeout(conf_availability_timeout)  # set timeout for checking availability to default356                try:357                    temp_socket.connect(address[4])358                except socket.error:359                    available = False360                finally:361                    try:362                        temp_socket.shutdown(socket.SHUT_RDWR)363                    except socket.error:364                        available = False365                    finally:366                        temp_socket.close()367            except socket.gaierror:368                available = False369 370            if available:371                if log_enabled(BASIC):372                    log(BASIC, 'server <%s> available at <%r>', self, address)373                self.update_availability(address, True)374                break  # if an available address is found exits immediately375            else:376                self.update_availability(address, False)377                if log_enabled(ERROR):378                    log(ERROR, 'server <%s> not available at <%r>', self, address)379 380        return available381 382    @staticmethod383    def next_message_id():384        """385        LDAP messageId is unique for all connections to same server386        """387        with Server._message_id_lock:388            Server._message_counter += 1389            if Server._message_counter >= LDAP_MAX_INT:390                Server._message_counter = 1391            if log_enabled(PROTOCOL):392                log(PROTOCOL, 'new message id <%d> generated', Server._message_counter)393 394        return Server._message_counter395 396    def _get_dsa_info(self, connection):397        """398        Retrieve DSE operational attribute as per RFC4512 (5.1).399        """400        if connection.strategy.no_real_dsa:  # do not try for mock strategies401            return402 403        if not connection.strategy.pooled:  # in pooled strategies get_dsa_info is performed by the worker threads404            result = connection.search(search_base='',405                                       search_filter='(objectClass=*)',406                                       search_scope=BASE,407                                       attributes=['altServer',  # requests specific dsa info attributes408                                                   'namingContexts',409                                                   'supportedControl',410                                                   'supportedExtension',411                                                   'supportedFeatures',412                                                   'supportedCapabilities',413                                                   'supportedLdapVersion',414                                                   'supportedSASLMechanisms',415                                                   'vendorName',416                                                   'vendorVersion',417                                                   'subschemaSubentry',418                                                   '*',419                                                   '+'],  # requests all remaining attributes (other),420                                       get_operational_attributes=True)421 422            if connection.strategy.thread_safe:423                status, result, response, _ = result424            else:425                status = result426                result = connection.result427                response = connection.response428 429            with self.dit_lock:430                if connection.strategy.sync:  # sync request431                    self._dsa_info = DsaInfo(response[0]['attributes'], response[0]['raw_attributes']) if status else self._dsa_info432                elif status:  # asynchronous request, must check if attributes in response433                    results, _ = connection.get_response(status)434                    if len(results) == 1 and 'attributes' in results[0] and 'raw_attributes' in results[0]:435                        self._dsa_info = DsaInfo(results[0]['attributes'], results[0]['raw_attributes'])436 437            if log_enabled(BASIC):438                log(BASIC, 'DSA info read for <%s> via <%s>', self, connection)439 440    def _get_schema_info(self, connection, entry=''):441        """442        Retrieve schema from subschemaSubentry DSE attribute, per RFC443        4512 (4.4 and 5.1); entry = '' means DSE.444        """445        if connection.strategy.no_real_dsa:  # do not try for mock strategies446            return447 448        schema_entry = None449        if self._dsa_info and entry == '':  # subschemaSubentry already present in dsaInfo450            if isinstance(self._dsa_info.schema_entry, SEQUENCE_TYPES):451                schema_entry = self._dsa_info.schema_entry[0] if self._dsa_info.schema_entry else None452            else:453                schema_entry = self._dsa_info.schema_entry if self._dsa_info.schema_entry else None454        else:455            result = connection.search(entry, '(objectClass=*)', BASE, attributes=['subschemaSubentry'], get_operational_attributes=True)456            if connection.strategy.thread_safe:457                status, result, response, _ = result458            else:459                status = result460                result = connection.result461                response = connection.response462            if connection.strategy.sync:  # sync request463                if status and 'subschemaSubentry' in response[0]['raw_attributes']:464                    if len(response[0]['raw_attributes']['subschemaSubentry']) > 0:465                        schema_entry = response[0]['raw_attributes']['subschemaSubentry'][0]466            else:  # asynchronous request, must check if subschemaSubentry in attributes467                results, _ = connection.get_response(status)468                if len(results) == 1 and 'raw_attributes' in results[0] and 'subschemaSubentry' in results[0]['attributes']:469                    if len(results[0]['raw_attributes']['subschemaSubentry']) > 0:470                        schema_entry = results[0]['raw_attributes']['subschemaSubentry'][0]471 472        if schema_entry and not connection.strategy.pooled:  # in pooled strategies get_schema_info is performed by the worker threads473            if isinstance(schema_entry, bytes) and str is not bytes:  # Python 3474                schema_entry = to_unicode(schema_entry, from_server=True)475            result = connection.search(schema_entry,476                                       search_filter='(objectClass=subschema)',477                                       search_scope=BASE,478                                       attributes=['objectClasses',  # requests specific subschema attributes479                                                   'attributeTypes',480                                                   'ldapSyntaxes',481                                                   'matchingRules',482                                                   'matchingRuleUse',483                                                   'dITContentRules',484                                                   'dITStructureRules',485                                                   'nameForms',486                                                   'createTimestamp',487                                                   'modifyTimestamp',488                                                   '*'],  # requests all remaining attributes (other)489                                       get_operational_attributes=True490                                       )491            if connection.strategy.thread_safe:492                status, result, response, _ = result493            else:494                status = result495                result = connection.result496                response = connection.response497            with self.dit_lock:498                self._schema_info = None499                if status:500                    if connection.strategy.sync:  # sync request501                        self._schema_info = SchemaInfo(schema_entry, response[0]['attributes'], response[0]['raw_attributes'])502                    else:  # asynchronous request, must check if attributes in response503                        results, result = connection.get_response(status)504                        if len(results) == 1 and 'attributes' in results[0] and 'raw_attributes' in results[0]:505                            self._schema_info = SchemaInfo(schema_entry, results[0]['attributes'], results[0]['raw_attributes'])506                    if self._schema_info and not self._schema_info.is_valid():  # flaky servers can return an empty schema, checks if it is so and set schema to None507                        self._schema_info = None508                    if self._schema_info:  # if schema is valid tries to apply formatter to the "other" dict with raw values for schema and info509                        for attribute in self._schema_info.other:510                            self._schema_info.other[attribute] = format_attribute_values(self._schema_info, attribute, self._schema_info.raw[attribute], self.custom_formatter)511                        if self._dsa_info:  # try to apply formatter to the "other" dict with dsa info raw values512                            for attribute in self._dsa_info.other:513                                self._dsa_info.other[attribute] = format_attribute_values(self._schema_info, attribute, self._dsa_info.raw[attribute], self.custom_formatter)514            if log_enabled(BASIC):515                log(BASIC, 'schema read for <%s> via <%s>', self, connection)516 517    def get_info_from_server(self, connection):518        """519        reads info from DSE and from subschema520        """521        if connection and not connection.closed:522            if self.get_info in [DSA, ALL]:523                self._get_dsa_info(connection)524            if self.get_info in [SCHEMA, ALL]:525                    self._get_schema_info(connection)526        elif self.get_info == OFFLINE_EDIR_8_8_8:527            from ..protocol.schemas.edir888 import edir_8_8_8_schema, edir_8_8_8_dsa_info528            self.attach_schema_info(SchemaInfo.from_json(edir_8_8_8_schema))529            self.attach_dsa_info(DsaInfo.from_json(edir_8_8_8_dsa_info))530        elif self.get_info == OFFLINE_EDIR_9_1_4:531            from ..protocol.schemas.edir914 import edir_9_1_4_schema, edir_9_1_4_dsa_info532            self.attach_schema_info(SchemaInfo.from_json(edir_9_1_4_schema))533            self.attach_dsa_info(DsaInfo.from_json(edir_9_1_4_dsa_info))534        elif self.get_info == OFFLINE_AD_2012_R2:535            from ..protocol.schemas.ad2012R2 import ad_2012_r2_schema, ad_2012_r2_dsa_info536            self.attach_schema_info(SchemaInfo.from_json(ad_2012_r2_schema))537            self.attach_dsa_info(DsaInfo.from_json(ad_2012_r2_dsa_info))538        elif self.get_info == OFFLINE_SLAPD_2_4:539            from ..protocol.schemas.slapd24 import slapd_2_4_schema, slapd_2_4_dsa_info540            self.attach_schema_info(SchemaInfo.from_json(slapd_2_4_schema))541            self.attach_dsa_info(DsaInfo.from_json(slapd_2_4_dsa_info))542        elif self.get_info == OFFLINE_DS389_1_3_3:543            from ..protocol.schemas.ds389 import ds389_1_3_3_schema, ds389_1_3_3_dsa_info544            self.attach_schema_info(SchemaInfo.from_json(ds389_1_3_3_schema))545            self.attach_dsa_info(DsaInfo.from_json(ds389_1_3_3_dsa_info))546 547    def attach_dsa_info(self, dsa_info=None):548        if isinstance(dsa_info, DsaInfo):549            self._dsa_info = dsa_info550            if log_enabled(BASIC):551                log(BASIC, 'attached DSA info to Server <%s>', self)552 553    def attach_schema_info(self, dsa_schema=None):554        if isinstance(dsa_schema, SchemaInfo):555            self._schema_info = dsa_schema556        if log_enabled(BASIC):557            log(BASIC, 'attached schema info to Server <%s>', self)558 559    @property560    def info(self):561        return self._dsa_info562 563    @property564    def schema(self):565        return self._schema_info566 567    @staticmethod568    def from_definition(host, dsa_info, dsa_schema, port=None, use_ssl=False, formatter=None, validator=None):569        """570        Define a dummy server with preloaded schema and info571        :param host: host name572        :param dsa_info: DsaInfo preloaded object or a json formatted string or a file name573        :param dsa_schema: SchemaInfo preloaded object or a json formatted string or a file name574        :param port: fake port575        :param use_ssl: use_ssl576        :param formatter: custom formatters577        :return: Server object578        """579        if isinstance(host, SEQUENCE_TYPES):580            dummy = Server(host=host[0], port=port, use_ssl=use_ssl, formatter=formatter, validator=validator, get_info=ALL)  # for ServerPool object581        else:582            dummy = Server(host=host, port=port, use_ssl=use_ssl, formatter=formatter, validator=validator, get_info=ALL)583        if isinstance(dsa_info, DsaInfo):584            dummy._dsa_info = dsa_info585        elif isinstance(dsa_info, STRING_TYPES):586            try:587                dummy._dsa_info = DsaInfo.from_json(dsa_info)  # tries to use dsa_info as a json configuration string588            except Exception:589                dummy._dsa_info = DsaInfo.from_file(dsa_info)  # tries to use dsa_info as a file name590 591        if not dummy.info:592            if log_enabled(ERROR):593                log(ERROR, 'invalid DSA info for %s', host)594            raise LDAPDefinitionError('invalid dsa info')595 596        if isinstance(dsa_schema, SchemaInfo):597            dummy._schema_info = dsa_schema598        elif isinstance(dsa_schema, STRING_TYPES):599            try:600                dummy._schema_info = SchemaInfo.from_json(dsa_schema)601            except Exception:602                dummy._schema_info = SchemaInfo.from_file(dsa_schema)603 604        if not dummy.schema:605            if log_enabled(ERROR):606                log(ERROR, 'invalid schema info for %s', host)607            raise LDAPDefinitionError('invalid schema info')608 609        if log_enabled(BASIC):610            log(BASIC, 'created server <%s> from definition', dummy)611 612        return dummy613 614    def candidate_addresses(self):615        conf_reset_availability_timeout = get_config_parameter('RESET_AVAILABILITY_TIMEOUT')616        if self.ipc:617            candidates = self.address_info618            if log_enabled(BASIC):619                log(BASIC, 'candidate address for <%s>: <%s> with mode UNIX_SOCKET', self, self.name)620        else:621            # checks reset availability timeout622            for address in self.address_info:623                if address[6] and ((datetime.now() - address[6]).seconds > conf_reset_availability_timeout):624                    address[5] = None625                    address[6] = None626 627            # selects server address based on server mode and availability (in address[5])628            addresses = self.address_info[:]  # copy to avoid refreshing while searching candidates629            candidates = []630            if addresses:631                if self.mode == IP_SYSTEM_DEFAULT:632                    candidates.append(addresses[0])633                elif self.mode == IP_V4_ONLY:634                    candidates = [address for address in addresses if address[0] == socket.AF_INET and (address[5] or address[5] is None)]635                elif self.mode == IP_V6_ONLY:636                    candidates = [address for address in addresses if address[0] == socket.AF_INET6 and (address[5] or address[5] is None)]637                elif self.mode == IP_V4_PREFERRED:638                    candidates = [address for address in addresses if address[0] == socket.AF_INET and (address[5] or address[5] is None)]639                    candidates += [address for address in addresses if address[0] == socket.AF_INET6 and (address[5] or address[5] is None)]640                elif self.mode == IP_V6_PREFERRED:641                    candidates = [address for address in addresses if address[0] == socket.AF_INET6 and (address[5] or address[5] is None)]642                    candidates += [address for address in addresses if address[0] == socket.AF_INET and (address[5] or address[5] is None)]643                else:644                    if log_enabled(ERROR):645                        log(ERROR, 'invalid server mode for <%s>', self)646                    raise LDAPInvalidServerError('invalid server mode')647 648            if log_enabled(BASIC):649                for candidate in candidates:650                    log(BASIC, 'obtained candidate address for <%s>: <%r> with mode %s', self, candidate[:-2], self.mode)651        return candidates652 653    def _check_info_property(self, kind, name):654        if not self._dsa_info:655            raise LDAPInfoError('server info not loaded')656 657        if kind == 'control':658            properties = self.info.supported_controls659        elif kind == 'extension':660            properties = self.info.supported_extensions661        elif kind == 'feature':662            properties = self.info.supported_features663        else:664            raise LDAPInfoError('invalid info category')665 666        for prop in properties:667                if name == prop[0] or (prop[2] and name.lower() == prop[2].lower()):  # checks oid and description668                    return True669 670        return False671 672    def has_control(self, control):673        return self._check_info_property('control', control)674 675    def has_extension(self, extension):676        return self._check_info_property('extension', extension)677 678    def has_feature(self, feature):679        return self._check_info_property('feature', feature)680 681 682 683 
codekingpro/portable-devtools · Team Ai