merge
authorEdouard Tisserant <edouard.tisserant@gmail.com>
Thu, 18 Oct 2018 18:37:01 +0200
changeset 2315 523559fe6352
parent 2309 d8fb90a2e11f (current diff)
parent 2314 e927c101ce6d (diff)
child 2317 b7330f3461dd
merge
Beremiz_service.py
runtime/PyroServer.py
runtime/WampClient.py
--- a/Beremiz_service.py	Wed Oct 03 00:05:32 2018 +0200
+++ b/Beremiz_service.py	Thu Oct 18 18:37:01 2018 +0200
@@ -75,7 +75,7 @@
     sys.exit(2)
 
 # default values
-given_ip = None
+interface = ''
 port = 3000
 webport = 8009
 wampsecret = None
@@ -97,8 +97,10 @@
         version()
         sys.exit()
     elif o == "-i":
-        if len(a.split(".")) == 4 or a == "localhost":
-            given_ip = a
+        if len(a.split(".")) == 4:
+            interface = a
+        elif a == "localhost":
+            interface = '127.0.0.1'
         else:
             usage()
             sys.exit()
@@ -493,7 +495,7 @@
 runtime.CreatePLCObjectSingleton(
     WorkingDir, argv, statuschange, evaluator, pyruntimevars)
 
-pyroserver = Server(servicename, given_ip, port)
+pyroserver = Server(servicename, interface, port)
 
 if havewx:
     taskbar_instance = BeremizTaskBarIcon(pyroserver)
@@ -501,7 +503,7 @@
 if havetwisted:
     if webport is not None:
         try:
-            website = NS.RegisterWebsite(webport)
+            website = NS.RegisterWebsite(interface, webport)
             pyruntimevars["website"] = website
             NS.SetServer(pyroserver)
             statuschange.append(NS.website_statuslistener_factory(website))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/connectors/PYRO/PSK_Adapter.py	Thu Oct 18 18:37:01 2018 +0200
@@ -0,0 +1,83 @@
+from __future__ import absolute_import
+from __future__ import print_function
+
+import socket
+import re
+import sslpsk
+import Pyro
+from Pyro.core import PyroURI
+from Pyro.protocol import _connect_socket,TCPConnection,PYROAdapter
+from Pyro.errors import ConnectionDeniedError, ProtocolError
+from Pyro.util import Log
+
+#
+# The TLS-PSK adapter that handles SSL connections instead of regular sockets,
+# but using Pre Shared Keys instead of Certificates
+#
+class PYROPSKAdapter(PYROAdapter):
+    # This is essentialy the same as in Pyro/protocol.py
+    # only raw_sock wrapping into sock through sslpsk.wrap_socket was added
+    # Pyro unfortunately doesn't allow cleaner customization
+    def bindToURI(self,URI):
+        with self.lock:   # only 1 thread at a time can bind the URI
+            try:
+                self.URI=URI
+
+                # This are the statements that differ from Pyro/protocol.py
+                raw_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+                _connect_socket(raw_sock, URI.address, URI.port, self.timeout)
+                sock = sslpsk.wrap_socket(
+                    raw_sock, psk=Pyro.config.PYROPSK, server_side=False,
+                    ciphers="PSK-AES256-GCM-SHA384:PSK-AES256-CBC-SHA")
+                # all the rest is the same as in Pyro/protocol.py 
+
+                conn=TCPConnection(sock, sock.getpeername())
+                # receive the authentication challenge string, and use that to build the actual identification string.
+                try:
+                    authChallenge=self.recvAuthChallenge(conn)
+                except ProtocolError,x:
+                    # check if we were denied
+                    if hasattr(x,"partialMsg") and x.partialMsg[:len(self.denyMSG)]==self.denyMSG:
+                        raise ConnectionDeniedError(Pyro.constants.deniedReasons[int(x.partialMsg[-1])])
+                    else:
+                        raise
+                # reply with our ident token, generated from the ident passphrase and the challenge
+                msg = self._sendConnect(sock,self.newConnValidator.createAuthToken(self.ident, authChallenge, conn.addr, self.URI, None) )
+                if msg==self.acceptMSG:
+                    self.conn=conn
+                    self.conn.connected=1
+                    Log.msg('PYROAdapter','connected to',str(URI))
+                    if URI.protocol=='PYROLOCPSK':
+                        self.resolvePYROLOC_URI("PYROPSK") # updates self.URI
+                elif msg[:len(self.denyMSG)]==self.denyMSG:
+                    try:
+                        raise ConnectionDeniedError(Pyro.constants.deniedReasons[int(msg[-1])])
+                    except (KeyError,ValueError):
+                        raise ConnectionDeniedError('invalid response')
+            except socket.error:
+                Log.msg('PYROAdapter','connection failed to URI',str(URI))
+                raise ProtocolError('connection failed')
+
+_getProtocolAdapter = Pyro.protocol.getProtocolAdapter
+def getProtocolAdapter(protocol):
+    if protocol in ('PYROPSK', 'PYROLOCPSK'):
+        return PYROPSKAdapter()
+    _getProtocolAdapter(protocol)
+
+Pyro.protocol.getProtocolAdapter = getProtocolAdapter
+
+_processStringURI = Pyro.core.processStringURI
+def processStringURI(URI):
+    x=re.match(r'(?P<protocol>PYROLOCPSK)://(?P<hostname>[^\s:]+):?(?P<port>\d+)?/(?P<name>\S*)',URI)
+    if x:
+        protocol=x.group('protocol')
+        hostname=x.group('hostname')
+        port=x.group('port')
+        if port:
+            port=int(port)
+        else:
+            port=0
+        name=x.group('name')
+        return PyroURI(hostname,name,port,protocol)
+    return _processStringURI(URI)
+Pyro.core.processStringURI = processStringURI
--- a/connectors/PYRO/__init__.py	Wed Oct 03 00:05:32 2018 +0200
+++ b/connectors/PYRO/__init__.py	Thu Oct 18 18:37:01 2018 +0200
@@ -36,8 +36,7 @@
 import Pyro.util
 from Pyro.errors import PyroError
 
-
-service_type = '_PYRO._tcp.local.'
+zeroconf_service_type = '_PYRO._tcp.local.'
 # this module attribute contains a list of DNS-SD (Zeroconf) service types
 # supported by this connector confnode.
 #
@@ -53,43 +52,26 @@
 
     servicetype, location = uri.split("://")
     if servicetype == "PYROS":
-        schemename = "PYROLOCSSL"
-        # Protect against name->IP substitution in Pyro3
-        Pyro.config.PYRO_DNS_URI = True
-        # Beware Pyro lib need str path, not unicode
-        # don't rely on PYRO_STORAGE ! see documentation
-        Pyro.config.PYROSSL_CERTDIR = os.path.abspath(str(confnodesroot.ProjectPath) + '/certs')
-        if not os.path.exists(Pyro.config.PYROSSL_CERTDIR):
+        import connectors.PYRO.PSK_Adapter
+        schemename = "PYROLOCPSK"
+        url, ID = location.split('#')
+        # load PSK from project
+        secpath = os.path.join(str(confnodesroot.ProjectPath), 'psk', ID+'.secret')
+        if not os.path.exists(secpath):
             confnodesroot.logger.write_error(
-                'Error : the directory %s is missing for SSL certificates (certs_dir).'
-                'Please fix it in your project.\n' % Pyro.config.PYROSSL_CERTDIR)
+                'Error: Pre-Shared-Key Secret in %s is missing!\n' % secpath)
             return None
-        else:
-            confnodesroot.logger.write(_("PYRO using certificates in '%s' \n")
-                                       % (Pyro.config.PYROSSL_CERTDIR))
-        Pyro.config.PYROSSL_CERT = "client.crt"
-        Pyro.config.PYROSSL_KEY = "client.key"
-
-        # Ugly Monkey Patching
-        def _gettimeout(self):
-            return self.timeout
-
-        def _settimeout(self, timeout):
-            self.timeout = timeout
-        from M2Crypto.SSL import Connection  # pylint: disable=import-error
-        Connection.timeout = None
-        Connection.gettimeout = _gettimeout
-        Connection.settimeout = _settimeout
-        # M2Crypto.SSL.Checker.WrongHost: Peer certificate commonName does not
-        # match host, expected 127.0.0.1, got server
-        Connection.clientPostConnectionCheck = None
+        Pyro.config.PYROPSK = (open(secpath).read(), ID)
+        # strip ID from URL, so that pyro can understand it.
+        location = url
     else:
         schemename = "PYROLOC"
-    if location.find(service_type) != -1:
+
+    if location.find(zeroconf_service_type) != -1:
         try:
             from zeroconf import Zeroconf
             r = Zeroconf()
-            i = r.get_service_info(service_type, location)
+            i = r.get_service_info(zeroconf_service_type, location)
             if i is None:
                 raise Exception("'%s' not found" % location)
             ip = str(socket.inet_ntoa(i.address))
@@ -161,3 +143,4 @@
             return member
 
     return PyroProxyProxy()
+
--- a/connectors/__init__.py	Wed Oct 03 00:05:32 2018 +0200
+++ b/connectors/__init__.py	Thu Oct 18 18:37:01 2018 +0200
@@ -32,7 +32,6 @@
 
 _base_path = paths.AbsDir(__file__)
 
-
 def _GetLocalConnectorClassFactory(name):
     return lambda: getattr(__import__(name, globals(), locals()), name + "_connector_factory")
 
--- a/runtime/NevowServer.py	Wed Oct 03 00:05:32 2018 +0200
+++ b/runtime/NevowServer.py	Thu Oct 18 18:37:01 2018 +0200
@@ -333,11 +333,11 @@
         # print "We will be called back when the client disconnects"
 
 
-def RegisterWebsite(port):
+def RegisterWebsite(iface, port):
     website = WebInterface()
     site = appserver.NevowSite(website)
 
-    reactor.listenTCP(port, site)
+    reactor.listenTCP(port, site, interface=iface)
     print(_('HTTP interface port :'), port)
     return website
 
--- a/runtime/PyroServer.py	Wed Oct 03 00:05:32 2018 +0200
+++ b/runtime/PyroServer.py	Thu Oct 18 18:37:01 2018 +0200
@@ -30,9 +30,7 @@
 
     def _to_be_published(self):
         return self.servicename is not None and \
-               self.ip_addr is not None and \
-               self.ip_addr != "localhost" and \
-               self.ip_addr != "127.0.0.1"
+               self.ip_addr not in ["", "localhost", "127.0.0.1"]
 
     def PrintServerInfo(self):
         print(_("Pyro port :"), self.port)