aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas "Cakeisalie5" Touhey <thomas@touhey.fr>2019-02-07 11:54:48 +0100
committerThomas "Cakeisalie5" Touhey <thomas@touhey.fr>2019-02-07 11:54:48 +0100
commit464e337b4446f8d37a37218bb5f4a4d4f7463a5f (patch)
treed3ba3bbc8eebc9021c4f1d74b4e731be913bfee2
parent425681857d9d2af3d2e27994b8a3a00439c579f5 (diff)
Corrected port decoding a little
-rwxr-xr-xfingerd/__init__.py2
-rwxr-xr-xfingerd/__main__.py2
-rw-r--r--fingerd/_binds.py232
-rw-r--r--fingerd/_exceptions.py26
-rwxr-xr-xfingerd/_fiction.py2
-rwxr-xr-xfingerd/_server.py31
-rwxr-xr-xfingerd/_util.py206
-rw-r--r--fingerd/client/__init__.py6
8 files changed, 281 insertions, 226 deletions
diff --git a/fingerd/__init__.py b/fingerd/__init__.py
index 018fa7c..bcf392e 100755
--- a/fingerd/__init__.py
+++ b/fingerd/__init__.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#******************************************************************************
-# Copyright (C) 2017-2018 Thomas Touhey <thomas@touhey.fr>
+# Copyright (C) 2017-2019 Thomas Touhey <thomas@touhey.fr>
# This file is part of the fingerd Python 3.x module, which is MIT-licensed.
#******************************************************************************
""" finger is both a protocol and a utility to get the information and
diff --git a/fingerd/__main__.py b/fingerd/__main__.py
index 3910283..2d8b442 100755
--- a/fingerd/__main__.py
+++ b/fingerd/__main__.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#******************************************************************************
-# Copyright (C) 2017-2018 Thomas Touhey <thomas@touhey.fr>
+# Copyright (C) 2017-2019 Thomas Touhey <thomas@touhey.fr>
# This file is part of the fingerd Python 3.x module, which is MIT-licensed.
#******************************************************************************
""" Main script of the module.
diff --git a/fingerd/_binds.py b/fingerd/_binds.py
new file mode 100644
index 0000000..1885ed0
--- /dev/null
+++ b/fingerd/_binds.py
@@ -0,0 +1,232 @@
+#!/usr/bin/env python3
+#******************************************************************************
+# Copyright (C) 2017-2019 Thomas Touhey <thomas@touhey.fr>
+# This file is part of the fingerd Python 3.x module, which is MIT-licensed.
+#******************************************************************************
+""" This file serves for managing binds. """
+
+from enum import Enum as _Enum
+
+import socket as _socket
+
+from ._exceptions import InvalidBindAddressError as _InvalidBindAddressError
+
+__all__ = ["BindAddressType", "BindsDecoder"]
+
+# ---
+# Bind adresses and address types.
+# ---
+
+class BindAddressType(_Enum):
+ """ Bind address type. """
+
+ """ TCP on IPv4 bind. """
+ TCP_IPv4 = 1
+
+ """ TCP on IPv6 bind. """
+ TCP_IPv6 = 2
+
+ """ IPC bind. """
+ IPC = 3
+
+class _BindAddress:
+ """ Bind address for fingerd. """
+
+ def __init__(self, family):
+ self.__family = BindAddressType(family)
+
+ def __repr__(self):
+ return f"{self.__class__.__name__}(family = {self.__family})"
+
+ @property
+ def family(self):
+ """ Family as one of the `BindAddressType` enumeration values. """
+
+ return self.__family
+
+class _TCP4Address(_BindAddress):
+ """ IPv4 TCP Address. """
+
+ def __init__(self, address, port):
+ # XXX: normalize the address to the binary representation in a
+ # better way.
+
+ try:
+ self.__addr = _socket.inet_pton(_socket.AF_INET, address)
+ except:
+ self.__addr = address
+
+ self.__port = port
+
+ @property
+ def runserver_params(self):
+ """ Return the data as `_runserver` parameters. """
+
+ return (_socket.AF_INET, _socket.inet_ntop(_socket.AF_INET,
+ self.__addr), self.__port)
+
+class _TCP6Address(_BindAddress):
+ """ IPv6 TCP Address. """
+
+ def __init__(self, address, port):
+ # XXX: normalize the address to the binary representation in a
+ # better way.
+
+ try:
+ self.__addr = _socket.inet_pton(_socket.AF_INET6, address)
+ except:
+ self.__addr = address
+
+ self.__port = port
+
+ @property
+ def runserver_params(self):
+ """ Return the data as `_runserver` parameters. """
+
+ return (_socket.AF_INET6, _socket.inet_ntop(_socket.AF_INET6,
+ self.__addr), self.__port)
+
+# ---
+# Decoding from strings.
+# ---
+
+def _decode_tcp_host(x):
+ """ Decode suitable hosts for a TCP bind. """
+
+ addrs = ()
+ addr = x
+
+ # Get the host part first, we'll decode it later.
+
+ if x[0] == '[':
+ # The host part is an IPv6, look for the closing ']' and
+ # decode it later.
+
+ to = x.find(']')
+ if to < 0:
+ raise _InvalidBindAddressError(addr, "expected " \
+ "closing ']'")
+
+ host = x[1:to]
+ x = x[to + 1:]
+
+ is_ipv6 = True
+ else:
+ # The host part is either an IPv4 or a host name, look for
+ # the ':' and decode it later.
+
+ host, *x = x.split(':')
+ x = ':' + ':'.join(x)
+
+ is_ipv6 = False
+
+ # Decode the port part.
+
+ if x == '':
+ port = 79
+ elif x[0] == ':':
+ try:
+ port = int(x[1:])
+ except:
+ try:
+ assert x[1:] != ''
+ port = _socket.getservbyname(x[1:])
+ except:
+ raise _InvalidBindAddressError(addr,
+ "expected a valid port number or name " \
+ f"(got {repr(x[1:])})") from None
+ else:
+ raise _InvalidBindAddressError(addr,
+ "garbage found after the host")
+
+ # Decode the host part and get the addresses.
+
+ addrs = ()
+ if is_ipv6:
+ # Decode the IPv6 address (validate it using `_socket.inet_pton`).
+
+ ip6 = host
+ _socket.inet_pton(_socket.AF_INET6, host)
+ addrs += (_TCP6Address(ip6, port),)
+ else:
+ # Decode the host (try IPv4, otherwise, resolve domain).
+
+ try:
+ ip = host.split('.')
+ assert 2 <= len(ip) <= 4
+
+ ip = list(map(int, ip))
+ assert all(lambda x: 0 <= x < 256, ip)
+
+ if len(ip) == 2:
+ ip = [ip[0], 0, 0, ip[1]]
+ elif len(ip) == 3:
+ ip = [ip[0], 0, ip[1], ip[2]]
+
+ addrs += (_TCP4Address(ip, port),)
+ except:
+ entries = _socket.getaddrinfo(host, port,
+ proto = _socket.IPPROTO_TCP,
+ type = _socket.SOCK_STREAM)
+
+ for ent in entries:
+ if ent[0] not in (_socket.AF_INET, _socket.AF_INET6) \
+ or ent[1] not in (_socket.SOCK_STREAM,):
+ continue
+
+ if ent[0] == _socket.AF_INET:
+ ip = ent[4][0]
+ _socket.inet_pton(_socket.AF_INET, ent[4][0])
+ addrs += (_TCP4Address(ip, port),)
+ else:
+ ip6 = ent[4][0]
+ _socket.inet_pton(_socket.AF_INET6, ent[4][0])
+ addrs += (_TCP6Address(ip6, port),)
+
+ return addrs
+
+class BindsDecoder:
+ """ Binds decoder for fingerd.
+ Takes a raw string and the protocol name, either 'finger' (the base
+ protocol managed by the class) or 'fingerd-control' (the protocol used
+ for controlling the live fingerd interface). """
+
+ def __init__(self, raw, proto = 'finger'):
+ proto = proto.casefold()
+ if proto not in ('finger', 'fingerd-control'):
+ raise NotImplementedError(f"unsupported protocol {proto}")
+
+ self.__binds = set()
+
+ for x in map(lambda x: x.strip(), raw.split(',')):
+ addr = x
+
+ # Try to find a scheme.
+
+ scheme, *rest = x.split(':/')
+ if not rest:
+ x = scheme
+ scheme = {'finger': 'tcp', 'fingerd-control': 'ipc'}[proto]
+ else:
+ # just don't add the ':' of ':/' again
+ x = '/' + ':/'.join(rest)
+
+ if (proto == 'finger' and scheme != 'tcp') \
+ or scheme not in ('tcp', 'ipc'):
+ raise _InvalidBindError("unsupported scheme {repr(scheme)} " \
+ f"for protocol {repr(proto)}")
+
+ # Decode the address data.
+
+ if scheme == "tcp":
+ self.__binds.update(_decode_tcp_host(x))
+
+ self.__binds = tuple(self.__binds)
+
+ def __iter__(self):
+ return iter(self.__binds)
+
+ def __repr__(self):
+ return f"{self.__class__.__name__}(binds = {self.__binds})"
+
+# End of file.
diff --git a/fingerd/_exceptions.py b/fingerd/_exceptions.py
new file mode 100644
index 0000000..0d03e6f
--- /dev/null
+++ b/fingerd/_exceptions.py
@@ -0,0 +1,26 @@
+#!/usr/bin/env python3
+#******************************************************************************
+# Copyright (C) 2017-2019 Thomas Touhey <thomas@touhey.fr>
+# This file is part of the fingerd Python 3.x module, which is MIT-licensed.
+#******************************************************************************
+""" This file defines the exceptions used throughout the module. """
+
+class ConfigurationError(Exception):
+ """ Generic exception for when an invalid configuration
+ exception occurs. """
+
+ pass
+
+class BindError(ConfigurationError):
+ """ Exception raised when there were errors in the binds. """
+
+ def __init__(self):
+ super().__init__("invalid bind")
+
+class InvalidBindAddressError(ConfigurationError):
+ """ Exception raised when a bind could not be understood. """
+
+ def __init__(self, addr, text):
+ super().__init__(f"{text} for address {addr}")
+
+# End of file.
diff --git a/fingerd/_fiction.py b/fingerd/_fiction.py
index 84262b6..2b71640 100755
--- a/fingerd/_fiction.py
+++ b/fingerd/_fiction.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#******************************************************************************
-# Copyright (C) 2017-2018 Thomas Touhey <thomas@touhey.fr>
+# Copyright (C) 2017-2019 Thomas Touhey <thomas@touhey.fr>
# This file is part of the fingerd Python 3.x module, which is MIT-licensed.
#******************************************************************************
""" Definitions for the finger server fiction interface.
diff --git a/fingerd/_server.py b/fingerd/_server.py
index 2772586..3833837 100755
--- a/fingerd/_server.py
+++ b/fingerd/_server.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#******************************************************************************
-# Copyright (C) 2017-2018 Thomas Touhey <thomas@touhey.fr>
+# Copyright (C) 2017-2019 Thomas Touhey <thomas@touhey.fr>
# This file is part of the fingerd Python 3.x module, which is MIT-licensed.
#******************************************************************************
""" The main server class for the server, with the related utilities that
@@ -237,7 +237,7 @@ _ldh = _string.ascii_letters + _string.digits + '.-'
class FingerServer:
""" The Finger Server class. """
- def __init__(self, bind = 'localhost:79', host = 'EXAMPLE.ORG',
+ def __init__(self, bind = 'localhost:79', host = 'LOCALHOST',
interface = _FingerInterface(), formatter = _FingerFormatter(),
logger = _FingerLogger()):
@@ -284,12 +284,7 @@ class FingerServer:
self._servers = []
for bind in _BindsDecoder(addresses):
- if bind.family == _BindAddressType.TCP_IPv4:
- fam = _socket.AF_INET
- elif bind.family == _BindAddressType.TCP_IPv6:
- fam = _socket.AF_INET6
-
- self._servers.append([fam, bind.data[0], bind.data[1], None])
+ self._servers.append([bind.runserver_params, None])
return bool(self._servers)
@@ -297,18 +292,16 @@ class FingerServer:
""" Bind and start the underlying servers. """
for entry in self._servers:
- family, addr, port, _ = entry
-
# Check if the thread already exists and runs.
- if entry[3] != None and not entry[3].is_alive():
+ if entry[1] != None and not entry[1].is_alive():
continue
# Start the thread.
- entry[3] = _multip.Process(target = _runserver,
- args = (family, addr, port, self._params))
- entry[3].start()
+ entry[1] = _multip.Process(target = _runserver,
+ args = entry[0] + (self._params,))
+ entry[1].start()
def stop(self):
""" Stop the underlying servers. """
@@ -316,16 +309,16 @@ class FingerServer:
for entry in self._servers:
# Check if the thread is still here.
- if entry[3] == None:
+ if entry[1] == None:
continue
- if not entry[3].is_alive():
- entry[3] = None
+ if not entry[1].is_alive():
+ entry[1] = None
continue
# Stop it.
- entry[3].join()
- entry[3] = None
+ entry[1].join()
+ entry[1] = None
def serve_forever(self):
""" Serve forever. """
diff --git a/fingerd/_util.py b/fingerd/_util.py
index 9e8a971..8454827 100755
--- a/fingerd/_util.py
+++ b/fingerd/_util.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#******************************************************************************
-# Copyright (C) 2017-2018 Thomas Touhey <thomas@touhey.fr>
+# Copyright (C) 2017-2019 Thomas Touhey <thomas@touhey.fr>
# This file is part of the fingerd Python 3.x module, which is MIT-licensed.
#******************************************************************************
""" This file defines the base classes for what is used by the
@@ -8,39 +8,18 @@
import sys as _sys, copy as _copy
import multiprocessing as _multip
-import socket as _socket
-from enum import Enum as _Enum
from datetime import datetime as _dt, timedelta as _td
+from ._exceptions import ConfigurationError, BindError
+from ._binds import BindAddressType, BindsDecoder
+
__all__ = ["ConfigurationError", "BindError",
"FingerUser", "FingerSession",
"FingerFormatter", "FingerInterface", "FingerLogger"
"BindAddressType", "BindsDecoder"]
# ---
-# Exceptions.
-# ---
-
-class ConfigurationError(Exception):
- """ Generic exception for when an invalid configuration
- exception occurs. """
-
- pass
-
-class BindError(ConfigurationError):
- """ Exception raised when there were errors in the binds. """
-
- def __init__(self):
- super().__init__("invalid bind")
-
-class InvalidBindAddressError(ConfigurationError):
- """ Exception raised when a bind could not be understood. """
-
- def __init__(self, addr, text):
- super().__init__(f"{text} for address {addr}")
-
-# ---
# User-related classes.
# ---
@@ -525,181 +504,4 @@ class FingerLogger:
def list(self, source):
self._logr(source, "list connected users.")
-# ---
-# Bind decoders.
-# ---
-
-class BindAddressType(_Enum):
- """ Bind address type. """
-
- """ TCP on IPv4 bind. """
- TCP_IPv4 = 1
-
- """ TCP on IPv6 bind. """
- TCP_IPv6 = 2
-
- """ IPC bind. """
- IPC = 3
-
-class _BindAddress:
- """ Bind address for fingerd. """
-
- def __init__(self, family, data):
- self.__family = BindAddressType(family)
- self.__data = data
-
- def __repr__(self):
- return f"{self.__class__.__name__}(family = {self.__family}, " \
- f"data = {self.__data})"
-
- @property
- def family(self):
- """ Family as one of the `BindAddressType` enumeration values. """
-
- return self.__family
-
- @property
- def data(self):
- """ Data to be sent for binding. """
-
- return self.__data
-
-def _decode_tcp_host(x):
- """ Decode suitable hosts for a TCP bind. """
-
- addrs = ()
- addr = x
-
- # Get the host part first, we'll decode it later.
-
- if x[0] == '[':
- # The host part is an IPv6, look for the closing ']' and
- # decode it later.
-
- to = x.find(']')
- if to < 0:
- raise InvalidBindAddressError(addr, "expected " \
- "closing ']'")
-
- host = x[1:to]
- x = x[to + 1:]
-
- is_ipv6 = True
- else:
- # The host part is either an IPv4 or a host name, look for
- # the ':' and decode it later.
-
- host, *x = x.split(':')
- x = ':' + ':'.join(x)
-
- is_ipv6 = False
-
- # Decode the port part.
-
- if x == '':
- port = 79
- elif x[0] == ':':
- try:
- port = int(x[1:])
- except:
- port = None if x[1:] == "" else _socket.getservbyname(x[1:])
- if port is None:
- raise InvalidBindAddressError(addr,
- "expected a port number or name") from None
- else:
- raise InvalidBindAddressError(addr,
- "garbage found after the host")
-
- # Decode the host part and get the addresses.
-
- addrs = ()
- if is_ipv6:
- # Decode the IPv6 address (validate it using `_socket.inet_pton`).
-
- ip6 = host
- _socket.inet_pton(_socket.AF_INET6, host)
- addrs += (_BindAddress(BindAddressType.TCP_IPv6, (ip6, port)),)
- else:
- # Decode the host (try IPv4, otherwise, resolve domain).
-
- try:
- ip = host.split('.')
- assert 2 <= len(ip) <= 4
-
- ip = list(map(int, ip))
- assert all(lambda x: 0 <= x < 256, ip)
-
- if len(ip) == 2:
- ip = [ip[0], 0, 0, ip[1]]
- elif len(ip) == 3:
- ip = [ip[0], 0, ip[1], ip[2]]
-
- addrs += (_BindAddress(BindAddressType.TCP_IPv4, (ip, port)),)
- except:
- entries = _socket.getaddrinfo(host, port,
- proto = _socket.IPPROTO_TCP,
- type = _socket.SOCK_STREAM)
-
- for ent in entries:
- if ent[0] not in (_socket.AF_INET, _socket.AF_INET6) \
- or ent[1] not in (_socket.SOCK_STREAM,):
- continue
-
- if ent[0] == _socket.AF_INET:
- ip = ent[4][0]
- _socket.inet_pton(_socket.AF_INET, ent[4][0])
- addrs += (_BindAddress(BindAddressType.TCP_IPv4,
- (ip, port)),)
- else:
- ip6 = ent[4][0]
- _socket.inet_pton(_socket.AF_INET6, ent[4][0])
- addrs += (_BindAddress(BindAddressType.TCP_IPv6,
- (ip6, port)),)
-
- return addrs
-
-class BindsDecoder:
- """ Binds decoder for fingerd.
- Takes a raw string and the protocol name, either 'finger' (the base
- protocol managed by the class) or 'fingerd-control' (the protocol used
- for controlling the live fingerd interface). """
-
- def __init__(self, raw, proto = 'finger'):
- proto = proto.casefold()
- if proto not in ('finger', 'fingerd-control'):
- raise NotImplementedError(f"unsupported protocol {proto}")
-
- self.__binds = set()
-
- for x in map(lambda x: x.strip(), raw.split(',')):
- addr = x
-
- # Try to find a scheme.
-
- scheme, *rest = x.split(':/')
- if not rest:
- x = scheme
- scheme = {'finger': 'tcp', 'fingerd-control': 'ipc'}[proto]
- else:
- # just don't add the ':' of ':/' again
- x = '/' + ':/'.join(rest)
-
- if (proto == 'finger' and scheme != 'tcp') \
- or scheme not in ('tcp', 'ipc'):
- raise _InvalidBindError("unsupported scheme {repr(scheme)} " \
- f"for protocol {repr(proto)}")
-
- # Decode the address data.
-
- if scheme == "tcp":
- self.__binds.update(_decode_tcp_host(x))
-
- self.__binds = tuple(self.__binds)
-
- def __iter__(self):
- return iter(self.__binds)
-
- def __repr__(self):
- return f"{self.__class__.__name__}(binds = {self.__binds})"
-
# End of file.
diff --git a/fingerd/client/__init__.py b/fingerd/client/__init__.py
index ca92dca..a008005 100644
--- a/fingerd/client/__init__.py
+++ b/fingerd/client/__init__.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#******************************************************************************
-# Copyright (C) 2017-2018 Thomas Touhey <thomas@touhey.fr>
+# Copyright (C) 2017-2019 Thomas Touhey <thomas@touhey.fr>
# This file is part of the fingerd Python 3.x module, which is MIT-licensed.
#******************************************************************************
""" The `fingerd.client` submodule is a module for adding actions to the
@@ -23,7 +23,7 @@ DELETE = _DeleteClass()
# The client.
# ---
-class FingerLiveClient:
+class FingerControlClient:
""" Client for sending actions to the fingerd live interface. """
def __init__(self, url):
@@ -45,4 +45,6 @@ class FingerLiveClient:
def login(self, login, session):
""" Login to a finger session. """
+ raise NotImplementedError
+
# End of file.