[or-cvs] r22598: {} Including project 113 (script to provide email alerts when t (arm/trunk/init)

Damian Johnson atagar1 at gmail.com
Mon Jul 5 01:06:41 UTC 2010


Author: atagar
Date: 2010-07-05 01:06:41 +0000 (Mon, 05 Jul 2010)
New Revision: 22598

Added:
   arm/trunk/init/project113.py
Log:
Including project 113 (script to provide email alerts when the number of exits radically change).



Added: arm/trunk/init/project113.py
===================================================================
--- arm/trunk/init/project113.py	                        (rev 0)
+++ arm/trunk/init/project113.py	2010-07-05 01:06:41 UTC (rev 22598)
@@ -0,0 +1,164 @@
+"""
+project113.py
+
+Quick, little script for periodically checking the relay count in the
+consensus. Queries are done every couple hours and this sends an email notice
+if it changes dramatically throughout the week.
+"""
+
+import sys
+import time
+import getpass
+import smtplib
+from email.mime.text import MIMEText
+
+sys.path[0] = sys.path[0][:-5]
+
+import util.torTools
+
+SAMPLING_INTERVAL = 7200 # two hours
+
+USERNAME = ""
+PASSWORD = ""
+RECEIVER = ""
+
+# size of change (+/-) at which an alert is sent
+BIHOURLY_THRESHOLD = 15
+DAILY_THRESHOLD = 50
+WEEKLY_THRESHOLD = 100
+
+def sendAlert(msg):
+  mimeMsg = MIMEText(msg)
+  mimeMsg['Subject'] = "Tor Relay Threshold Alert"
+  mimeMsg['From'] = USERNAME
+  mimeMsg['To'] = RECEIVER
+  
+  # Send the message via our own SMTP server, but don't include the
+  # envelope header.
+  try:
+    server = smtplib.SMTP('smtp.gmail.com:587')
+    server.starttls()
+    server.login(USERNAME, PASSWORD)
+    
+    server.sendmail(USERNAME, [RECEIVER], mimeMsg.as_string())
+    server.quit()
+  except smtplib.SMTPAuthenticationError:
+    print "Failed to sent alert"
+
+def getCount(conn):
+  nsEntries = conn.get_network_status()
+  return len(nsEntries)
+
+def getExits(conn):
+  # provides ns entries associated with exit relays
+  exitEntries = []
+  for nsEntry in conn.get_network_status():
+    queryParam = "desc/id/%s" % nsEntry.idhex
+    descEntry = conn.get_info(queryParam)[queryParam]
+    
+    isExit = False
+    for line in descEntry.split("\n"):
+      if line == "reject *:*": break # reject all before any accept entries
+      elif line.startswith("accept"):
+        # Guess this to be an exit (erroring on the side of inclusiveness)
+        isExit = True
+        break
+    
+    if isExit: exitEntries.append(nsEntry)
+  
+  return exitEntries
+
+def getExitsDiff(newEntries, oldEntries):
+  # provides relays in newEntries but not oldEntries
+  diffMapping = dict([(entry.idhex, entry) for entry in newEntries])
+  
+  for entry in oldEntries:
+    if entry.idhex in diffMapping.keys(): del diffMapping[entry.idhex]
+  
+  return diffMapping.values()
+
+if __name__ == '__main__':
+  if not PASSWORD: PASSWORD = getpass.getpass("GMail Password: ")
+  conn = util.torTools.connect()
+  counts = [] # has entries for up to the past week
+  nsEntries = [] # parallel listing for exiting ns entries
+  lastQuery = 0
+  
+  while True:
+    # sleep for a couple hours
+    while time.time() < (lastQuery + SAMPLING_INTERVAL):
+      sleepTime = max(1, SAMPLING_INTERVAL - (time.time() - lastQuery))
+      time.sleep(sleepTime)
+    
+    # adds new count to the beginning
+    #newCount = getCount(conn)
+    exitEntries = getExits(conn)
+    newCount = len(exitEntries)
+    
+    counts.insert(0, newCount)
+    nsEntries.insert(0, exitEntries)
+    if len(counts) > 84:
+      counts.pop()
+      nsEntries.pop()
+    
+    # check if we broke any thresholds (alert at the lowest increment)
+    alarmHourly, alarmDaily, alarmWeekly = False, False, False
+    
+    if len(counts) >= 2:
+      alarmHourly = abs(newCount - counts[1]) >= BIHOURLY_THRESHOLD
+    
+    if len(counts) >= 3:
+      dayMin, dayMax = min(counts[:12]), max(counts[:12])
+      alarmDaily = (dayMax - dayMin) > DAILY_THRESHOLD
+    
+    if len(counts) >= 12:
+      weekMin, weekMax = min(counts), max(counts)
+      alarmWeekly = (weekMax - weekMin) > WEEKLY_THRESHOLD
+    
+    # notes entry on terminal
+    lastQuery = time.time()
+    timeLabel = time.strftime("%H:%M %m/%d/%Y", time.localtime(lastQuery))
+    print "%s - %s exits" % (timeLabel, newCount)
+    
+    # sends a notice with counts for the last week
+    if alarmHourly or alarmDaily or alarmWeekly:
+      if alarmHourly: threshold = "hourly"
+      elif alarmDaily: threshold = "daily"
+      elif alarmWeekly: threshold = "weekly"
+      
+      msg = "%s threshold broken\n" % threshold
+      
+      msg += "\nexit counts:\n"
+      entryTime = lastQuery
+      for countEntry in counts:
+        timeLabel = time.strftime("%H:%M %m/%d/%Y", time.localtime(entryTime))
+        msg += "%s - %i\n" % (timeLabel, countEntry)
+        entryTime -= SAMPLING_INTERVAL
+      
+      msg += "\nnew exits (hourly):\n"
+      entriesDiff = getExitsDiff(nsEntries[0], nsEntries[1])
+      for entry in entriesDiff:
+        msg += "%s (%s:%s)\n" % (entry.idhex, entry.ip, entry.orport)
+        msg += "    nickname: %s\n    flags: %s\n\n" % (entry.nickname, ", ".join(entry.flags))
+      
+      if len(counts) >= 12:
+        msg += "\nnew exits (daily):\n"
+        entriesDiff = getExitsDiff(nsEntries[0], nsEntries[12])
+        for entry in entriesDiff:
+          msg += "%s (%s:%s)\n" % (entry.idhex, entry.ip, entry.orport)
+          msg += "    nickname: %s\n    flags: %s\n\n" % (entry.nickname, ", ".join(entry.flags))
+      
+      if len(counts) >= 48:
+        # require at least four days of data
+        msg += "\nnew exits (weekly):\n"
+        entriesDiff = getExitsDiff(nsEntries[0], nsEntries[-1])
+        for entry in entriesDiff:
+          msg += "%s (%s:%s)\n" % (entry.idhex, entry.ip, entry.orport)
+          msg += "    nickname: %s\n    flags: %s\n\n" % (entry.nickname, ", ".join(entry.flags))
+      
+      sendAlert(msg)
+      
+      # clears entries so we don't repeatidly send alarms for the same event
+      if alarmDaily: del counts[2:]
+      elif alarmWeekly: del counts[12:]
+



More information about the tor-commits mailing list