123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402 |
- # -*- coding: utf-8 -*-#
-
- # Copyright 2015 Weber Yann
- #
- # This file is part of pyMapTraceroute.
- #
- # pyMapTraceroute 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.
- #
- # pyMapTraceroute 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 pyMapTraceroute. If not, see <http://www.gnu.org/licenses/>.
- #
-
- import os, re, GeoIP, time, pygame
-
-
- from subprocess import Popen, PIPE
- from pygame import gfxdraw
-
- import vtracemap
- import vtracert
- import geo6
-
- class Tracer:
-
- ## Tracer instanciation
- # @param vtr The Vtracert
- def __init__(self, vtr):
-
- #Check vtr argument
- if not isinstance(vtr, vtracert.Vtracert):
- raise TypeError('Vtracert waited')
-
- self.vtr = vtr
-
- #Init object properties
- self.initTrace()
- self.gi = GeoIP.open("/usr/share/GeoIP/GeoIPCity.dat",GeoIP.GEOIP_STANDARD)
- cproc = os.popen('curl --silent zered.org/getip.php')
- self.src = cproc.readline().strip()
-
-
- self.prev = None
- self.prevLonLat = None
-
- self.selected = [] #selected hop
-
- self.regetip = re.compile('^.*\(([^\)]*)\).*$');
- self.reghost = re.compile('^ *[0-9]* *([^\(]*)\(.*$');
- self.regHop = re.compile('^[^0-9]*([0-9]*).*$');
-
- pass
-
- ## Object trace initialisation
- def initTrace(self):
- self.ips = []
- self.hosts = []
- self.pts = []
- self.hops = []
- self.coords = []
- self.selected = []
-
- def progressStatus(self):
- self.vtr.drawStatus("Traceroute in progress : ...", self.vtr.font['status'],self.vtr.col['trace_progress'])
- pass
-
-
- ## Generator that yield ips from a running traceroute
- # @param The host to traceroute to
- # @yield Hop's IP
- def traceGen(self,dest_name):
-
- #Init tracer
- self.initTrace();
-
- #Store source ip as first hop
- self.storeIp(self.src)
- self.hosts.append('localhost')
- self.hops.append('0')
-
- #Update status bar
- self.progressStatus()
- self.vtr.flip()
-
- #Run traceroute process
- p = Popen(['traceroute', dest_name], stdout=PIPE)
-
- #First line give us destination ip
- line = p.stdout.readline().decode('utf-8')
-
- ml = self.regetip.match(line)
- if ml != None:
- dest_ip = ml.group(1)
-
- if ':' in dest_ip:
- coord = geo6.ip62coord(dest_ip)
- else:
- coord = self.ip2coord(dest_ip)
- if coord is not None:
- dlon, dlat = coord
- (x,y) = self.vtr.trmap.latlon2ortho(dlon, dlat)
-
- pygame.gfxdraw.circle(self.vtr.surf['trace'],x,y,3,self.vtr.col['trace_end'])
-
- else:
- dest_ip = None
-
- self.vtr.flip()
- yield self.src
-
- #Reading traceroute output
- while True:
- line = p.stdout.readline().decode('utf-8')
- if not line:
- break
-
- print(line)
-
- #Getting hop number
- ml = self.regHop.match(line)
- if ml != None:
- self.hops.append(ml.group(1))
- else:
- self.hops.append('?')
-
- #hop hostname
- ml = self.reghost.match(line)
- if ml != None:
- self.hosts.append(ml.group(1))
- else:
- self.hosts.append('?')
- #hop ip
- ml = self.regetip.match(line)
- if ml != None:
- ip = ml.group(1)
- else:
- ip = None
-
- self.storeIp(ip)
-
-
- strStatus = "Traceroute in progress : "
- strStatus += self.hops[-1]+' '+self.hosts[-1]
- if ip != None:
- strStatus += '('+ip+')'
-
- self.vtr.drawStatus(strStatus, self.vtr.font['status'],self.vtr.col['trace_progress'])
-
- self.drawRoute()
- yield self.ips[-1]
-
- if dest_ip != None:
- self.storeIp(dest_ip)
- self.hops.append('Dest')
- self.hosts.append(dest_name)
- yield dest_ip
-
- ## Store an hop's ip in object properties
- # @param ip The ip adress to store
- def storeIp(self,ip):
-
- nopts = False
-
- self.ips.append(ip)
-
- if ip != None:
- lonlat = self.ip2coord(ip)
- if lonlat != None:
- #print gir['latitude'], gir['longitude']
- self.pts.append(lonlat)
- else:
- nopts = True
- else:
- nopts = True
-
- if nopts:
- self.pts.append(None)
- pass
-
- ## Get geoip info for ip
- # @param ip to localisate
- def ip2coord(self,ip):
- gir = self.gi.record_by_addr(ip)
- if gir != None:
- return ( gir['longitude'], gir['latitude'])
- return None
-
- ## Populate data by running a traceroute
- # @param dest_name The host
- def trace(self,dest_name):
- #Traceroute generator
- tr = self.traceGen(dest_name)
- for ip in tr:
- if ip != None:
- pass #self.storeIp(ip)
-
- ## Run a traceroute and interactively update the screen
- # @param dest_name The host
- def liveTrace(self,dest_name):
-
- #Traceroute generator
- tr = self.traceGen(dest_name)
- self.initDraw()
-
- for ip in tr:
- if ip != None:
- #New hop
- pts = self.pts[-1]
- if pts != None:
- dcoord = self.drawNext(pts)
- self.coords.append(dcoord)
- else:
- self.coords.append(None)
- #Update the screen
- self.vtr.flip()
- pass
-
- ## Init a new trace drawing
- def initDraw(self):
- self.prev = None
- self.coords = []
- self.vtr.surf['trace'].fill((0,0,0,0))
- pass
-
- ## Draw the next hop in a route on the map
- # Hops are circle (filled when selected) and
- # the route is represented by line between circles
- # @param (lon,lat) Hop coordinates
- # @param selected A boolean
- # @return The coordinates of the hop on the screen as (x,y)
- def drawNext(self, pos, selected = False, last = False):
- lon,lat=pos
-
- if not selected :
- #If not selected draw a circle
- cdrawfun = pygame.gfxdraw.circle
- else:
- #If selected draw a filled circle
- cdrawfun = pygame.gfxdraw.filled_circle
-
- (x,y) = self.vtr.trmap.latlon2ortho(lon, lat)
- if self.prev == None:
- #Route begining
- cdrawfun(self.vtr.surf['trace'],x,y,3,self.vtr.col['trace_init'])
- else:
- #A new hop
-
- if last:
- cdrawfun(self.vtr.surf['trace'], x,y,3,self.vtr.col['trace_hop'])
- else:
- cdrawfun(self.vtr.surf['trace'], x,y,5,self.vtr.col['trace_hop'])
-
- (px,py) = self.prev
-
- #Trying to find a symetric closer from the new hop
- dw = self.vtr.trmap.scaledWidth
- spx1 = px - dw # x prev symetric 1
- spx2 = px + dw # x prev symetric 2
-
- (plon,_) = self.prevLonLat
-
- splon1 = plon - 360 # prev lon symetric 1
- splon2 = plon + 360 # prev lon symetric 2
-
- sd1 = abs(lon-splon1)
- sd2 = abs(lon-splon2)
-
-
- if sd1 < sd2:
- spx = spx1
- sx = x + dw
- sd = sd1
- else:
- spx = spx2
- sx = x - dw
- sd = sd2
-
- d = abs(lon-plon)
-
- #drawLine = Tracer.drawArcLine
- drawLine = pygame.gfxdraw.line
-
- if sd > d:
- drawLine(self.vtr.surf['trace'], px,py,x,y, self.vtr.col['trace_trace'])
- else:
- #symetric is closer
- drawLine(self.vtr.surf['trace'], spx,py,x,y, self.vtr.col['trace_trace'])
- drawLine(self.vtr.surf['trace'], px,py,sx,y, self.vtr.col['trace_trace'])
-
- self.prevLonLat = (lon,lat)
- self.prev = (x,y)
-
- return (x,y)
-
- ## Draw an existing route on the map
- # @param delay The time to wait between the draw of two hops (in float, seconds)
- def drawTrace(self, delay = 0.1):
-
- self.initDraw()
-
- cnt = 1
- for i,pt in enumerate(self.pts):
-
- if pt != None:
- dcoord = self.drawNext(pt, (i in self.selected), (i == len(self.pts)-1) )
- self.coords.append(dcoord)
-
- if delay > 0:
- self.drawRoute(cnt)
- self.vtr.flip()
- time.sleep(delay)
-
- else:
- self.coords.append(None)
- cnt += 1
- self.drawRoute()
- pass
-
-
- ## Draw the text representation of an existing route
- # One line per hop at the bottom left of the screen
- # @param stopAt Print the last 'stopAt' hops
- def drawRoute(self, stopAt = 32):
-
- strStatus=''
-
- self.vtr.surf['tracetxt'].fill((0,0,0,0))
- lh = self.vtr.font['trace_txt'].get_linesize()
-
- cy = self.vtr.surf['tracetxt'].get_height()-4
- cy -= lh
-
- if stopAt < 0 or stopAt > len(self.ips):
- rng = range(len(self.ips))
- else:
- rng = range(stopAt)
- rng=list(rng)
- rng.reverse()
- for i in rng:
- strStatus = self.hops[i]+' '+self.hosts[i]
- if self.ips[i] != None:
- strStatus += '('+self.ips[i]+')'
-
- if i in self.selected:
- col = self.vtr.col['trace_txtsel'] #selected color
- else:
- col = self.vtr.col['trace_txt'] #normal color
-
- statusSurf = self.vtr.font['trace_txt'].render(strStatus, 1, col)
-
- self.vtr.surf['tracetxt'].blit(statusSurf, (4,cy))
- cy -= lh
- pass
-
- ## Search if there is a hop near a screen coordinate
- # Will return mutliple host if the distance is equal
- # @param (x,y) The on screen coordinates
- # @param maxDist The maximum distance bellow wich a hop is selected
- # @return A list of hop index
- def searchHopByCoord(self, coord, maxDist = 10):
- x,y=coord
- maxDist = maxDist **2
-
- curRes = []
- curMin = maxDist+1
-
- for (i,pos) in enumerate(self.coords):
- if pos != None:
- (cx,cy) = pos
- d = ((cx-x)**2) + ((cy-y)**2)
- if d <= curMin:
- if d == curMin:
- curRes.append(i)
- else:
- curRes = [i]
- curMin = d
-
-
- return curRes
-
- ## Select hops given screen coordinate
- # @param pos On screen coordinate in a (x,y) tuple
- # @return A boolean set to True if hops were selected
- def select(self, pos):
- self.selected = self.searchHopByCoord(pos)
- return (len(self.selected)>0)
-
- ## Return a string representing selected hops ip
- # @return One ip on each line, preffixed by \t
- def selectedIp(self):
- res = ""
- for i in self.selected:
- res += '\t'+str(self.hosts[i])+'('+str(self.ips[i])+')\n'
- return res
|