chiark / gitweb /
(Python): Use more modern `raise' syntax.
[tripe] / mon / tripemon.in
index 96cf9cc2d7e07189479402d727db4b4c51632214..24709344da4ce205fe9496a263fca57569adbc95 100644 (file)
 ###
 ### This file is part of Trivial IP Encryption (TrIPE).
 ###
-### TrIPE is free software; you can redistribute it and/or modify
-### it under the terms of the GNU General Public License as published by
-### the Free Software Foundation; either version 2 of the License, or
-### (at your option) any later version.
+### TrIPE is free software: you can redistribute it and/or modify it under
+### the terms of the GNU General Public License as published by the Free
+### Software Foundation; either version 3 of the License, or (at your
+### option) any later version.
 ###
-### TrIPE is distributed in the hope that it will be useful,
-### but WITHOUT ANY WARRANTY; without even the implied warranty of
-### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-### GNU General Public License for more details.
+### TrIPE is distributed in the hope that it will be useful, but WITHOUT
+### ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+### FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+### for more details.
 ###
 ### You should have received a copy of the GNU General Public License
-### along with TrIPE; if not, write to the Free Software Foundation,
-### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+### along with TrIPE.  If not, see <https://www.gnu.org/licenses/>.
 
 ###--------------------------------------------------------------------------
 ### Dependencies.
@@ -41,7 +40,7 @@ import re as RX
 from cStringIO import StringIO
 
 try:
-  if OS.getenv('TRIPEMON_FORCE_GI'): raise ImportError
+  if OS.getenv('TRIPEMON_FORCE_GI'): raise ImportError()
   import pygtk
   pygtk.require('2.0')
   import gtk as G
@@ -312,6 +311,7 @@ class Peer (MonitorObject):
     """Initialize the object with the given name."""
     MonitorObject.__init__(me, name)
     me.pinghook = HookList()
+    me.__dict__.update(conn.algs(name))
     me.update()
 
   def update(me, hunoz = None):
@@ -773,7 +773,7 @@ class ValidatingEntry (G.Entry):
     ValidationError.
     """
     if not me.validp:
-      raise ValidationError
+      raise ValidationError()
     return G.Entry.get_text(me)
 
 def numericvalidate(min = None, max = None):
@@ -790,19 +790,19 @@ def numericvalidate(min = None, max = None):
 ###--------------------------------------------------------------------------
 ### Various minor dialog boxen.
 
-GPL = """This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2 of the License, or
-(at your option) any later version.
+GPL = """\
+TrIPE is free software: you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3 of the License, or (at your
+option) any later version.
 
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
+TrIPE is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
 
 You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software Foundation,
-Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA."""
+along with TrIPE.  If not, see <https://www.gnu.org/licenses/>."""
 
 class AboutBox (G.AboutDialog, TrivialWindowMixin):
   """The program `About' box."""
@@ -1064,48 +1064,67 @@ class AddPeerDialog (MyDialog):
                                ValidatingEntry(numericvalidate(0, 65535),
                                                '4070',
                                                5))
-    me.c_keepalive = G.CheckButton('Keepalives')
     me.l_tunnel = table.labelled('Tunnel', combo_box_text(),
                                  newlinep = True, width = 3)
-    me.tuns = conn.tunnels()
+    me.tuns = ['(Default)'] + conn.tunnels()
     for t in me.tuns:
       me.l_tunnel.append_text(t)
     me.l_tunnel.set_active(0)
-    table.pack(me.c_keepalive, newlinep = True, xopt = G.FILL)
-    me.c_keepalive.connect('toggled',
-                           lambda t: me.e_keepalive.set_sensitive\
-                                      (t.get_active()))
-    me.e_keepalive = ValidatingEntry(r'^\d+[hms]?$', '', 5)
-    me.e_keepalive.set_sensitive(False)
-    table.pack(me.e_keepalive, width = 3)
+
+    def tickybox_sensitivity(tickybox, target):
+      tickybox.connect('toggled',
+                       lambda t: target.set_sensitive (t.get_active()))
+
+    def optional_entry(label, rx_valid, width):
+      c = G.CheckButton(label)
+      table.pack(c, newlinep = True, xopt = G.FILL)
+      e = ValidatingEntry(rx_valid, '', width)
+      e.set_sensitive(False)
+      tickybox_sensitivity(c, e)
+      table.pack(e, width = 3)
+      return c, e
+
+    me.c_keepalive, me.e_keepalive = \
+      optional_entry('Keepalives', r'^\d+[hms]?$', 5)
+
+    me.c_cork = G.CheckButton('Cork')
+    table.pack(me.c_cork, newlinep = True, width = 4, xopt = G.FILL)
+
+    me.c_mobile = G.CheckButton('Mobile')
+    table.pack(me.c_mobile, newlinep = True, width = 4, xopt = G.FILL)
+
+    me.c_peerkey, me.e_peerkey = \
+      optional_entry('Peer key tag', r'^[^.:\s]+$', 16)
+    me.c_privkey, me.e_privkey = \
+      optional_entry('Private key tag', r'^[^.:\s]+$', 16)
+
     me.show_all()
 
   def ok(me):
     """Handle an OK press: create the peer."""
     try:
-      if me.c_keepalive.get_active():
-        ka = me.e_keepalive.get_text()
-      else:
-        ka = None
       t = me.l_tunnel.get_active()
-      if t == 0:
-        tun = None
-      else:
-        tun = me.tuns[t]
-        me._addpeer(me.e_name.get_text(),
-                    me.e_addr.get_text(),
-                    me.e_port.get_text(),
-                    ka,
-                    tun)
+      me._addpeer(me.e_name.get_text(),
+                  me.e_addr.get_text(),
+                  me.e_port.get_text(),
+                  keepalive = (me.c_keepalive.get_active() and
+                               me.e_keepalive.get_text() or None),
+                  tunnel = t and me.tuns[t] or None,
+                  cork = me.c_cork.get_active() or None,
+                  mobile = me.c_mobile.get_active() or None,
+                  key = (me.c_peerkey.get_active() and
+                         me.e_peerkey.get_text() or None),
+                  priv = (me.c_privkey.get_active() and
+                          me.e_privkey.get_text() or None))
     except ValidationError:
       GDK.beep()
       return
 
   @incr
-  def _addpeer(me, name, addr, port, keepalive, tunnel):
+  def _addpeer(me, *args, **kw):
     """Coroutine function: actually do the ADD command."""
     try:
-      conn.add(name, addr, port, keepalive = keepalive, tunnel = tunnel)
+      conn.add(*args, **kw)
       me.destroy()
     except T.TripeError, exc:
       T.defer(moanbox, ' '.join(exc))
@@ -1211,7 +1230,7 @@ def xlate_time(t):
   return '%04d:%02d:%02d %02d:%02d:%02d (%.1f %s ago)' % \
          (YY, MM, DD, hh, mm, ss, ago, unit)
 def xlate_bytes(b):
-  """Translate a number of bytes into something a human might want to read."""
+  """Translate a raw byte count into something a human might want to read."""
   suff = 'B'
   b = int(b)
   for s in 'KMG':
@@ -1233,12 +1252,40 @@ statsxlate = \
    ('ip-bytes-in', xlate_bytes),
    ('ip-bytes-out', xlate_bytes)]
 
+def format_stat(format, dict):
+  if callable(format): return format(dict)
+  else: return format % dict
+
 ## How to lay out the stats dialog.  Format is (LABEL, FORMAT): LABEL is
 ## the label to give the entry box; FORMAT is the format string to write into
 ## the entry.
+cryptolayout = \
+  [('Diffie-Hellman group',
+    '%(kx-group)s '
+    '(%(kx-group-order-bits)s-bit order, '
+    '%(kx-group-elt-bits)s-bit elements)'),
+   ('Bulk crypto transform',
+    '%(bulk-transform)s (%(bulk-overhead)s byte overhead)'),
+   ('Data encryption', lambda d: '%s (%s; %s)' % (
+     d['cipher'],
+     '%d-bit key' % (8*int(d['cipher-keysz'])),
+     d.get('cipher-blksz', '0') == '0'
+       and 'stream cipher'
+       or '%d-bit block' % (8*int(d['cipher-blksz'])))),
+   ('Message authentication', lambda d: '%s (%s; %s)' % (
+     d['mac'],
+     d.get('mac-keysz') is None
+       and 'one-time MAC'
+       or '%d-bit key' % (8*int(d['mac-keysz'])),
+     '%d-bit tag' % (8*int(d['mac-tagsz'])))),
+   ('Hash', lambda d: '%s (%d-bit output)' %
+    (d['hash'], 8*int(d['hash-sz'])))]
+
 statslayout = \
   [('Start time', '%(start-time)s'),
-   ('Last key-exchange', '%(last-keyexch-time)s'),
+   ('Private key', '%(current-key)s')] + \
+  cryptolayout + \
+  [('Last key-exchange', '%(last-keyexch-time)s'),
    ('Last packet', '%(last-packet-time)s'),
    ('Packets in/out',
     '%(packets-in)s (%(bytes-in)s) / %(packets-out)s (%(bytes-out)s)'),
@@ -1313,6 +1360,7 @@ class PeerWindow (TrivialWindow):
   def change(me):
     """Update the display in response to a notification."""
     me.e['Interface'].set_text(me.peer.ifname)
+    me.e['Address'].set_text(me.peer.addr)
 
   def _update(me):
     """
@@ -1325,8 +1373,9 @@ class PeerWindow (TrivialWindow):
       stat = conn.stats(me.peer.name)
       for s, trans in statsxlate:
         stat[s] = trans(stat[s])
+      stat.update(me.peer.__dict__)
       for label, format in statslayout:
-        me.e[label].set_text(format % stat)
+        me.e[label].set_text(format_stat(format, stat))
       GL.timeout_add(1000, lambda: me.cr.switch() and False)
       me.cr.parent.switch()
     me.cr = None
@@ -1371,31 +1420,11 @@ class CryptoInfo (TrivialWindow):
     me.add(table)
 
     crypto = conn.algs()
-    table.info('Diffie-Hellman group',
-               '%s (%d-bit order, %d-bit elements)' %
-               (crypto['kx-group'],
-                int(crypto['kx-group-order-bits']),
-                int(crypto['kx-group-elt-bits'])),
-               len = 32)
-    table.info('Data encryption',
-               '%s (%d-bit key; %s)' %
-               (crypto['cipher'],
-                int(crypto['cipher-keysz']) * 8,
-                crypto['cipher-blksz'] == '0'
-                  and 'stream cipher'
-                  or '%d-bit block' % (int(crypto['cipher-blksz']) * 8)),
-               newlinep = True)
-    table.info('Message authentication',
-               '%s (%d-bit key; %d-bit tag)' %
-               (crypto['mac'],
-                int(crypto['mac-keysz']) * 8,
-                int(crypto['mac-tagsz']) * 8),
-               newlinep = True)
-    table.info('Hash function',
-               '%s (%d-bit output)' %
-               (crypto['hash'],
-                int(crypto['hash-sz']) * 8),
-               newlinep = True)
+    firstp = True
+    for label, format in cryptolayout:
+      table.info(label, format_stat(format, crypto),
+                 len = 42, newlinep = not firstp)
+      firstp = False
 
     me.show_all()
 
@@ -1583,6 +1612,7 @@ class MonitorWindow (MyWindow):
                                   '???', 'green', '???', 'green'])
     peer.win = WindowSlot(lambda: PeerWindow(peer))
     me.hook(peer.pinghook, me._ping)
+    me.hook(peer.changehook, lambda: me._change(peer))
     me.apchange()
 
   def delpeer(me, peer):
@@ -1723,6 +1753,10 @@ class MonitorWindow (MyWindow):
       me.listmodel[p.i][textcol] = '%.1f ms' % ps.tlast
       me.listmodel[p.i][colourcol] = 'black'
 
+  def _change(me, p):
+    """Hook: notified when the peer changes state."""
+    me.listmodel[p.i][1] = p.addr
+
   def setstatus(me, status):
     """Update the message in the status bar."""
     me.status.pop(0)