diff --git a/src/ldapserver/__init__.py b/src/ldapserver/__init__.py
index 1cec6c213e5c01e8505c8ad297c1612ec57510b3..9ac0ff51a0ffd2893e3dbbc288b5175827cb26f1 100644
--- a/src/ldapserver/__init__.py
+++ b/src/ldapserver/__init__.py
@@ -1,5 +1,5 @@
 from . import ldap
 from . import dn
+from . import exceptions
 
-from .exceptions import *
 from .server import BaseLDAPRequestHandler, SimpleLDAPRequestHandler
diff --git a/src/ldapserver/server.py b/src/ldapserver/server.py
index 0e92de53990420da81f15b839ca3f7df4c61b459..101b849f3c5993977275189844da83b7a4106900 100644
--- a/src/ldapserver/server.py
+++ b/src/ldapserver/server.py
@@ -3,29 +3,25 @@ import hashlib
 import secrets
 import socket
 import ssl
-from socketserver import BaseRequestHandler
+import socketserver
 
-from .ldap import *
-from .asn1 import IncompleteBERError
-from .directory import RootDSE, Subschema, BaseDirectory
-from .schema import CORE_SUBSCHEMA
-from .exceptions import *
+from . import asn1, exceptions, ldap, schema, directory
 
 def decode_msg(shallowmsg):
 	try:
 		return shallowmsg.decode()[0]
 	except:
 		traceback.print_exc()
-		raise LDAPProtocolError()
+		raise exceptions.LDAPProtocolError()
 
 def reject_critical_controls(controls=None, supported_oids=[]):
 	for control in controls or []:
 		if not control.criticality:
 			continue
 		if control.controlType not in supported_oids:
-			raise LDAPUnavailableCriticalExtension()
+			raise exceptions.LDAPUnavailableCriticalExtension()
 
-class BaseLDAPRequestHandler(BaseRequestHandler):
+class BaseLDAPRequestHandler(socketserver.BaseRequestHandler):
 	def setup(self):
 		super().setup()
 		self.keep_running = True
@@ -35,10 +31,10 @@ class BaseLDAPRequestHandler(BaseRequestHandler):
 		buf = b''
 		while self.keep_running:
 			try:
-				shallowmsg, buf = ShallowLDAPMessage.from_ber(buf)
+				shallowmsg, buf = ldap.ShallowLDAPMessage.from_ber(buf)
 				for respmsg in self.handle_message(shallowmsg):
-					self.request.sendall(LDAPMessage.to_ber(respmsg))
-			except IncompleteBERError:
+					self.request.sendall(ldap.LDAPMessage.to_ber(respmsg))
+			except asn1.IncompleteBERError:
 				chunk = self.request.recv(5)
 				if not chunk:
 					self.keep_running = False
@@ -58,38 +54,38 @@ class BaseLDAPRequestHandler(BaseRequestHandler):
 		:rtype: iterable of LDAPMessage objects
 		'''
 		msgtypes = {
-			BindRequest: (self.handle_bind, BindResponse),
-			UnbindRequest: (self.handle_unbind, None),
-			SearchRequest: (self.handle_search, SearchResultDone),
-			ModifyRequest: (self.handle_modify, ModifyResponse),
-			AddRequest: (self.handle_add,  AddResponse),
-			DelRequest: (self.handle_delete, DelResponse),
-			ModifyDNRequest: (self.handle_modifydn, ModifyDNResponse),
-			CompareRequest: (self.handle_compare, CompareResponse),
-			AbandonRequest: (self.handle_abandon, None),
-			ExtendedRequest: (self.handle_extended, ExtendedResponse),
+			ldap.BindRequest: (self.handle_bind, ldap.BindResponse),
+			ldap.UnbindRequest: (self.handle_unbind, None),
+			ldap.SearchRequest: (self.handle_search, ldap.SearchResultDone),
+			ldap.ModifyRequest: (self.handle_modify, ldap.ModifyResponse),
+			ldap.AddRequest: (self.handle_add,  ldap.AddResponse),
+			ldap.DelRequest: (self.handle_delete, ldap.DelResponse),
+			ldap.ModifyDNRequest: (self.handle_modifydn, ldap.ModifyDNResponse),
+			ldap.CompareRequest: (self.handle_compare, ldap.CompareResponse),
+			ldap.AbandonRequest: (self.handle_abandon, None),
+			ldap.ExtendedRequest: (self.handle_extended, ldap.ExtendedResponse),
 		}
 		handler, response_type = msgtypes.get(shallowmsg.protocolOpType, (None, None))
 		try:
 			if handler is None:
-				raise LDAPProtocolError()
+				raise exceptions.LDAPProtocolError()
 			try:
 				msg = decode_msg(shallowmsg)
 			except ValueError:
 				self.on_recv_invalid(shallowmsg)
-				raise LDAPProtocolError()
+				raise exceptions.LDAPProtocolError()
 			self.on_recv(msg)
 			for args in handler(msg.protocolOp, msg.controls):
 				response, controls = args if isinstance(args, tuple) else (args, None)
-				yield LDAPMessage(shallowmsg.messageID, response, controls)
-		except LDAPError as e:
+				yield ldap.LDAPMessage(shallowmsg.messageID, response, controls)
+		except exceptions.LDAPError as e:
 			if response_type is not None:
-				respmsg = LDAPMessage(shallowmsg.messageID, response_type(e.code, diagnosticMessage=e.message))
+				respmsg = ldap.LDAPMessage(shallowmsg.messageID, response_type(e.code, diagnosticMessage=e.message))
 				self.on_send(respmsg)
 				yield respmsg
 		except Exception as e:
 			if response_type is not None:
-				respmsg = LDAPMessage(shallowmsg.messageID, response_type(LDAPResultCode.other))
+				respmsg = ldap.LDAPMessage(shallowmsg.messageID, response_type(ldap.LDAPResultCode.other))
 				self.on_send(respmsg)
 				yield respmsg
 			self.on_exception(e)
@@ -114,7 +110,7 @@ class BaseLDAPRequestHandler(BaseRequestHandler):
 
 	def handle_bind(self, op, controls=None):
 		reject_critical_controls(controls)
-		raise LDAPAuthMethodNotSupported()
+		raise exceptions.LDAPAuthMethodNotSupported()
 
 	def handle_unbind(self, op, controls=None):
 		reject_critical_controls(controls)
@@ -122,34 +118,34 @@ class BaseLDAPRequestHandler(BaseRequestHandler):
 
 	def handle_search(self, op, controls=None):
 		reject_critical_controls(controls)
-		yield SearchResultDone(LDAPResultCode.success)
+		yield ldap.SearchResultDone(ldap.LDAPResultCode.success)
 
 	def handle_modify(self, op, controls=None):
 		reject_critical_controls(controls)
-		raise LDAPInsufficientAccessRights()
+		raise exceptions.LDAPInsufficientAccessRights()
 
 	def handle_add(self, op, controls=None):
 		reject_critical_controls(controls)
-		raise LDAPInsufficientAccessRights()
+		raise exceptions.LDAPInsufficientAccessRights()
 
 	def handle_delete(self, op, controls=None):
 		reject_critical_controls(controls)
-		raise LDAPInsufficientAccessRights()
+		raise exceptions.LDAPInsufficientAccessRights()
 
 	def handle_modifydn(self, op, controls=None):
 		reject_critical_controls(controls)
-		raise LDAPInsufficientAccessRights()
+		raise exceptions.LDAPInsufficientAccessRights()
 
 	def handle_compare(self, op, controls=None):
 		reject_critical_controls(controls)
-		raise LDAPInsufficientAccessRights()
+		raise exceptions.LDAPInsufficientAccessRights()
 
 	def handle_abandon(self, op, controls=None):
 		reject_critical_controls(controls)
 
 	def handle_extended(self, op, controls=None):
 		reject_critical_controls(controls)
-		raise LDAPProtocolError()
+		raise exceptions.LDAPProtocolError()
 
 class SimpleLDAPRequestHandler(BaseLDAPRequestHandler):
 	'''
@@ -160,7 +156,7 @@ class SimpleLDAPRequestHandler(BaseLDAPRequestHandler):
 		mechansims. Attributes can be accessed in a dict-like fashion.
 	'''
 
-	subschema = CORE_SUBSCHEMA
+	subschema = schema.CORE_SUBSCHEMA
 	'''
 	.. py:attribute:: subschema
 
@@ -176,7 +172,7 @@ class SimpleLDAPRequestHandler(BaseLDAPRequestHandler):
 
 	def setup(self):
 		super().setup()
-		self.rootdse = RootDSE()
+		self.rootdse = directory.RootDSE()
 		self.rootdse['objectClass'] = [b'top']
 		self.rootdse['supportedSASLMechanisms'] = self.get_sasl_mechanisms
 		self.rootdse['supportedExtension'] = self.get_extentions
@@ -193,11 +189,11 @@ class SimpleLDAPRequestHandler(BaseLDAPRequestHandler):
 		Called whenever the root DSE attribute "supportedExtension" is queried.'''
 		res = []
 		if self.supports_starttls:
-			res.append(EXT_STARTTLS_OID.encode())
+			res.append(ldap.EXT_STARTTLS_OID.encode())
 		if self.supports_whoami:
-			res.append(EXT_WHOAMI_OID.encode())
+			res.append(ldap.EXT_WHOAMI_OID.encode())
 		if self.supports_password_modify:
-			res.append(EXT_PASSWORD_MODIFY_OID.encode())
+			res.append(ldap.EXT_PASSWORD_MODIFY_OID.encode())
 		return res
 
 	def get_sasl_mechanisms(self):
@@ -221,42 +217,42 @@ class SimpleLDAPRequestHandler(BaseLDAPRequestHandler):
 	def handle_bind(self, op, controls=None):
 		reject_critical_controls(controls)
 		if op.version != 3:
-			raise LDAPProtocolError('Unsupported protocol version')
+			raise exceptions.LDAPProtocolError('Unsupported protocol version')
 		auth = op.authentication
 		# Resume ongoing SASL dialog
-		if self.bind_sasl_state and isinstance(auth, SaslCredentials) \
+		if self.bind_sasl_state and isinstance(auth, ldap.SaslCredentials) \
 				and auth.mechanism == self.bind_sasl_state[0]:
 			mechanism, iterator = self.bind_sasl_state
 			self.bind_sasl_state = None
-			resp_code = LDAPResultCode.saslBindInProgress
+			resp_code = ldap.LDAPResultCode.saslBindInProgress
 			try:
 				resp = iterator.send(auth.credentials)
 				self.bind_sasl_state = (mechanism, iterator)
 			except StopIteration as e:
-				resp_code = LDAPResultCode.success
+				resp_code = ldap.LDAPResultCode.success
 				self.bind_object, resp = e.value
-			yield BindResponse(resp_code, serverSaslCreds=resp)
+			yield ldap.BindResponse(resp_code, serverSaslCreds=resp)
 			return
 		# If auth type or SASL method changed, abort SASL dialog
 		self.bind_sasl_state = None
-		if isinstance(auth, SimpleAuthentication):
+		if isinstance(auth, ldap.SimpleAuthentication):
 			self.bind_object = self.do_bind_simple(op.name, auth.password)
-			yield BindResponse(LDAPResultCode.success)
-		elif isinstance(auth, SaslCredentials):
+			yield ldap.BindResponse(ldap.LDAPResultCode.success)
+		elif isinstance(auth, ldap.SaslCredentials):
 			ret = self.do_bind_sasl(auth.mechanism, auth.credentials)
 			if isinstance(ret, tuple):
 				self.bind_object, resp = ret
-				yield BindResponse(LDAPResultCode.success, serverSaslCreds=resp)
+				yield ldap.BindResponse(ldap.LDAPResultCode.success, serverSaslCreds=resp)
 				return
 			iterator = iter(ret)
-			resp_code = LDAPResultCode.saslBindInProgress
+			resp_code = ldap.LDAPResultCode.saslBindInProgress
 			try:
 				resp = next(iterator)
 				self.bind_sasl_state = (auth.mechanism, iterator)
 			except StopIteration as e:
-				resp_code = LDAPResultCode.success
+				resp_code = ldap.LDAPResultCode.success
 				self.bind_object, resp = e.value
-			yield BindResponse(resp_code, serverSaslCreds=resp)
+			yield ldap.BindResponse(resp_code, serverSaslCreds=resp)
 		else:
 			yield from super().handle_bind(op, controls)
 
@@ -304,7 +300,7 @@ class SimpleLDAPRequestHandler(BaseLDAPRequestHandler):
 
 		Calld by :any:`do_bind_simple`. The default implementation always raises an
 		:any:`LDAPInvalidCredentials` exception.'''
-		raise LDAPInvalidCredentials()
+		raise exceptions.LDAPInvalidCredentials()
 
 	def do_bind_simple_authenticated(self, dn, password):
 		'''Do LDAP BIND with simple name/password authentication (`RFC 4513 5.1.3.`_)
@@ -321,7 +317,7 @@ class SimpleLDAPRequestHandler(BaseLDAPRequestHandler):
 
 		Calld by :any:`do_bind_simple`. The default implementation always raises an
 		`LDAPInvalidCredentials` exception.'''
-		raise LDAPInvalidCredentials()
+		raise exceptions.LDAPInvalidCredentials()
 
 	def do_bind_sasl(self, mechanism, credentials=None, dn=None):
 		'''Do LDAP BIND with SASL authentication (RFC 4513 and 4422)
@@ -354,21 +350,21 @@ class SimpleLDAPRequestHandler(BaseLDAPRequestHandler):
 		'''
 		if not mechanism:
 			# Request to abort current negotiation (RFC4513 5.2.1.2)
-			raise LDAPAuthMethodNotSupported()
+			raise exceptions.LDAPAuthMethodNotSupported()
 		if mechanism == 'ANONYMOUS' and self.supports_sasl_anonymous:
 			if credentials is not None:
 				credentials = credentials.decode()
 			return self.do_bind_sasl_anonymous(trace_info=credentials), None
 		if mechanism == 'PLAIN' and self.supports_sasl_plain:
 			if credentials is None:
-				raise LDAPProtocolError('Unsupported protocol version')
+				raise exceptions.LDAPProtocolError('Unsupported protocol version')
 			authzid, authcid, password = credentials.split(b'\0', 2)
 			return self.do_bind_sasl_plain(authcid.decode(), password.decode(), authzid.decode() or None), None
 		if mechanism == 'EXTERNAL' and self.supports_sasl_external:
 			if credentials is not None:
 				credentials = credentials.decode()
 			return self.do_bind_sasl_external(authzid=credentials), None
-		raise LDAPAuthMethodNotSupported()
+		raise exceptions.LDAPAuthMethodNotSupported()
 
 	supports_sasl_anonymous = False
 
@@ -386,7 +382,7 @@ class SimpleLDAPRequestHandler(BaseLDAPRequestHandler):
 
 		Calld by :any:`do_bind_sasl`. The default implementation raises an
 		:any:`LDAPAuthMethodNotSupported` exception.'''
-		raise LDAPAuthMethodNotSupported()
+		raise exceptions.LDAPAuthMethodNotSupported()
 
 	supports_sasl_plain = False
 
@@ -407,7 +403,7 @@ class SimpleLDAPRequestHandler(BaseLDAPRequestHandler):
 
 		Calld by :any:`do_bind_sasl`. The default implementation raises an
 		:any:`LDAPAuthMethodNotSupported` exception.'''
-		raise LDAPAuthMethodNotSupported()
+		raise exceptions.LDAPAuthMethodNotSupported()
 
 	supports_sasl_external = False
 
@@ -427,15 +423,15 @@ class SimpleLDAPRequestHandler(BaseLDAPRequestHandler):
 
 		Calld by :any:`do_bind_sasl`. The default implementation raises an
 		:any:`LDAPAuthMethodNotSupported` exception.'''
-		raise LDAPAuthMethodNotSupported()
+		raise exceptions.LDAPAuthMethodNotSupported()
 
 	def handle_search(self, op, controls=None):
 		for dn, attributes in self.do_search(op.baseObject, op.scope, op.filter):
-			pattributes = [PartialAttribute(name, values) for name, values in attributes.items()]
+			pattributes = [ldap.PartialAttribute(name, values) for name, values in attributes.items()]
 			if 'subschemaSubentry' not in attributes and self.subschema is not None:
-				pattributes.append(PartialAttribute('subschemaSubentry', [bytes(self.subschema.dn)]))
-			yield SearchResultEntry(dn, pattributes)
-		yield SearchResultDone(LDAPResultCode.success)
+				pattributes.append(ldap.PartialAttribute('subschemaSubentry', [bytes(self.subschema.dn)]))
+			yield ldap.SearchResultEntry(dn, pattributes)
+		yield ldap.SearchResultDone(ldap.LDAPResultCode.success)
 
 	def do_search(self, baseobj, scope, filter):
 		'''Do LDAP SEARCH operation
@@ -464,31 +460,31 @@ class SimpleLDAPRequestHandler(BaseLDAPRequestHandler):
 
 	def handle_extended(self, op, controls=None):
 		reject_critical_controls(controls)
-		if op.requestName == EXT_STARTTLS_OID and self.supports_starttls:
+		if op.requestName == ldap.EXT_STARTTLS_OID and self.supports_starttls:
 			# StartTLS (RFC 4511)
-			yield ExtendedResponse(LDAPResultCode.success, responseName=EXT_STARTTLS_OID)
+			yield ldap.ExtendedResponse(ldap.LDAPResultCode.success, responseName=ldap.EXT_STARTTLS_OID)
 			try:
 				self.do_starttls()
 			except Exception as e:
 				traceback.print_exc()
 				self.keep_running = False
-		elif op.requestName == EXT_WHOAMI_OID and self.supports_whoami:
+		elif op.requestName == ldap.EXT_WHOAMI_OID and self.supports_whoami:
 			# "Who am I?" Operation (RFC 4532)
 			identity = (self.do_whoami() or '').encode()
-			yield ExtendedResponse(LDAPResultCode.success, responseValue=identity)
-		elif op.requestName == EXT_PASSWORD_MODIFY_OID and self.supports_password_modify:
+			yield ldap.ExtendedResponse(ldap.LDAPResultCode.success, responseValue=identity)
+		elif op.requestName == ldap.EXT_PASSWORD_MODIFY_OID and self.supports_password_modify:
 			# Password Modify Extended Operation (RFC 3062)
 			newpw = None
 			if op.requestValue is None:
 				newpw = self.do_passwd()
 			else:
-				decoded, _ = PasswdModifyRequestValue.from_ber(op.requestValue)
+				decoded, _ = ldap.PasswdModifyRequestValue.from_ber(op.requestValue)
 				newpw = self.do_passwd(decoded.userIdentity, decoded.oldPasswd, decoded.newPasswd)
 			if newpw is None:
-				yield ExtendedResponse(LDAPResultCode.success)
+				yield ldap.ExtendedResponse(ldap.LDAPResultCode.success)
 			else:
-				encoded = PasswdModifyResponseValue.to_ber(PasswdModifyResponseValue(newpw))
-				yield ExtendedResponse(LDAPResultCode.success, responseValue=encoded)
+				encoded = ldap.PasswdModifyResponseValue.to_ber(ldap.PasswdModifyResponseValue(newpw))
+				yield ldap.ExtendedResponse(ldap.LDAPResultCode.success, responseValue=encoded)
 		else:
 			yield from super().handle_extended(op, controls)
 
@@ -542,4 +538,4 @@ class SimpleLDAPRequestHandler(BaseLDAPRequestHandler):
 		
 		Called by `handle_extended()` if :any:`supports_password_modify` is True. The
 		default implementation always raises an :any:`LDAPUnwillingToPerform` error.'''
-		raise LDAPUnwillingToPerform()
+		raise exceptions.LDAPUnwillingToPerform()