Traceroute visualizer written in python
python
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

tracer.py 9.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394
  1. # -*- coding: utf-8 -*-#
  2. # Copyright 2015 Weber Yann
  3. #
  4. # This file is part of pyMapTraceroute.
  5. #
  6. # pyMapTraceroute is free software: you can redistribute it and/or modify
  7. # it under the terms of the GNU General Public License as published by
  8. # the Free Software Foundation, either version 3 of the License, or
  9. # (at your option) any later version.
  10. #
  11. # pyMapTraceroute is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU General Public License
  17. # along with pyMapTraceroute. If not, see <http://www.gnu.org/licenses/>.
  18. #
  19. import os, re, GeoIP, time, pygame
  20. from subprocess import Popen, PIPE
  21. from pygame import gfxdraw
  22. import vtracemap
  23. import vtracert
  24. class Tracer:
  25. ## Tracer instanciation
  26. # @param vtr The Vtracert
  27. def __init__(self, vtr):
  28. #Check vtr argument
  29. if not isinstance(vtr, vtracert.Vtracert):
  30. raise TypeError('Vtracert waited')
  31. self.vtr = vtr
  32. #Init object properties
  33. self.initTrace()
  34. self.gi = GeoIP.open("/usr/share/GeoIP/GeoIPCity.dat",GeoIP.GEOIP_STANDARD)
  35. cproc = os.popen('curl --silent zered.org/getip.php')
  36. self.src = cproc.readline().strip()
  37. self.prev = None
  38. self.prevLonLat = None
  39. self.selected = [] #selected hop
  40. self.regetip = re.compile('^.*\(([^\)]*)\).*$');
  41. self.reghost = re.compile('^ *[0-9]* *([^\(]*)\(.*$');
  42. self.regHop = re.compile('^[^0-9]*([0-9]*).*$');
  43. pass
  44. ## Object trace initialisation
  45. def initTrace(self):
  46. self.ips = []
  47. self.hosts = []
  48. self.pts = []
  49. self.hops = []
  50. self.coords = []
  51. self.selected = []
  52. def progressStatus(self):
  53. self.vtr.drawStatus("Traceroute in progress : ...", self.vtr.font['status'],self.vtr.col['trace_progress'])
  54. pass
  55. ## Generator that yield ips from a running traceroute
  56. # @param The host to traceroute to
  57. # @yield Hop's IP
  58. def traceGen(self,dest_name):
  59. #Init tracer
  60. self.initTrace();
  61. #Store source ip as first hop
  62. self.storeIp(self.src)
  63. self.hosts.append('localhost')
  64. self.hops.append('0')
  65. #Update status bar
  66. self.progressStatus()
  67. self.vtr.flip()
  68. #Run traceroute process
  69. p = Popen(['traceroute', dest_name], stdout=PIPE)
  70. #First line give us destination ip
  71. line = p.stdout.readline()
  72. ml = self.regetip.match(line)
  73. if ml != None:
  74. dest_ip = ml.group(1)
  75. (dlon,dlat) = self.ip2coord(dest_ip)
  76. (x,y) = self.vtr.trmap.latlon2ortho(dlon, dlat)
  77. pygame.gfxdraw.circle(self.vtr.surf['trace'],x,y,3,self.vtr.col['trace_end'])
  78. else:
  79. dest_ip = None
  80. self.vtr.flip()
  81. yield self.src
  82. #Reading traceroute output
  83. while True:
  84. line = p.stdout.readline()
  85. if not line:
  86. break
  87. print line
  88. #Getting hop number
  89. ml = self.regHop.match(line)
  90. if ml != None:
  91. self.hops.append(ml.group(1))
  92. else:
  93. self.hops.append('?')
  94. #hop hostname
  95. ml = self.reghost.match(line)
  96. if ml != None:
  97. self.hosts.append(ml.group(1))
  98. else:
  99. self.hosts.append('?')
  100. #hop ip
  101. ml = self.regetip.match(line)
  102. if ml != None:
  103. ip = ml.group(1)
  104. else:
  105. ip = None
  106. self.storeIp(ip)
  107. strStatus = "Traceroute in progress : "
  108. strStatus += self.hops[-1]+' '+self.hosts[-1]
  109. if ip != None:
  110. strStatus += '('+ip+')'
  111. self.vtr.drawStatus(strStatus, self.vtr.font['status'],self.vtr.col['trace_progress'])
  112. self.drawRoute()
  113. yield self.ips[-1]
  114. if dest_ip != None:
  115. self.storeIp(dest_ip)
  116. self.hops.append('Dest')
  117. self.hosts.append(dest_name)
  118. yield dest_ip
  119. ## Store an hop's ip in object properties
  120. # @param ip The ip adress to store
  121. def storeIp(self,ip):
  122. nopts = False
  123. self.ips.append(ip)
  124. if ip != None:
  125. lonlat = self.ip2coord(ip)
  126. if lonlat != None:
  127. #print gir['latitude'], gir['longitude']
  128. self.pts.append(lonlat)
  129. else:
  130. nopts = True
  131. else:
  132. nopts = True
  133. if nopts:
  134. self.pts.append(None)
  135. pass
  136. ## Get geoip info for ip
  137. # @param ip to localisate
  138. def ip2coord(self,ip):
  139. gir = self.gi.record_by_addr(ip)
  140. if gir != None:
  141. return ( gir['longitude'], gir['latitude'])
  142. return None
  143. ## Populate data by running a traceroute
  144. # @param dest_name The host
  145. def trace(self,dest_name):
  146. #Traceroute generator
  147. tr = self.traceGen(dest_name)
  148. for ip in tr:
  149. if ip != None:
  150. pass #self.storeIp(ip)
  151. ## Run a traceroute and interactively update the screen
  152. # @param dest_name The host
  153. def liveTrace(self,dest_name):
  154. #Traceroute generator
  155. tr = self.traceGen(dest_name)
  156. self.initDraw()
  157. for ip in tr:
  158. if ip != None:
  159. #New hop
  160. pts = self.pts[-1]
  161. if pts != None:
  162. dcoord = self.drawNext(pts)
  163. self.coords.append(dcoord)
  164. else:
  165. self.coords.append(None)
  166. #Update the screen
  167. self.vtr.flip()
  168. pass
  169. ## Init a new trace drawing
  170. def initDraw(self):
  171. self.prev = None
  172. self.coords = []
  173. self.vtr.surf['trace'].fill((0,0,0,0))
  174. pass
  175. ## Draw the next hop in a route on the map
  176. # Hops are circle (filled when selected) and
  177. # the route is represented by line between circles
  178. # @param (lon,lat) Hop coordinates
  179. # @param selected A boolean
  180. # @return The coordinates of the hop on the screen as (x,y)
  181. def drawNext(self, (lon, lat), selected = False, last = False):
  182. if not selected :
  183. #If not selected draw a circle
  184. cdrawfun = pygame.gfxdraw.circle
  185. else:
  186. #If selected draw a filled circle
  187. cdrawfun = pygame.gfxdraw.filled_circle
  188. (x,y) = self.vtr.trmap.latlon2ortho(lon, lat)
  189. if self.prev == None:
  190. #Route begining
  191. cdrawfun(self.vtr.surf['trace'],x,y,3,self.vtr.col['trace_init'])
  192. else:
  193. #A new hop
  194. if last:
  195. cdrawfun(self.vtr.surf['trace'], x,y,3,self.vtr.col['trace_hop'])
  196. else:
  197. cdrawfun(self.vtr.surf['trace'], x,y,5,self.vtr.col['trace_hop'])
  198. (px,py) = self.prev
  199. #Trying to find a symetric closer from the new hop
  200. dw = self.vtr.trmap.scaledWidth
  201. spx1 = px - dw # x prev symetric 1
  202. spx2 = px + dw # x prev symetric 2
  203. (plon,_) = self.prevLonLat
  204. splon1 = plon - 360 # prev lon symetric 1
  205. splon2 = plon + 360 # prev lon symetric 2
  206. sd1 = abs(lon-splon1)
  207. sd2 = abs(lon-splon2)
  208. if sd1 < sd2:
  209. spx = spx1
  210. sx = x + dw
  211. sd = sd1
  212. else:
  213. spx = spx2
  214. sx = x - dw
  215. sd = sd2
  216. d = abs(lon-plon)
  217. #drawLine = Tracer.drawArcLine
  218. drawLine = pygame.gfxdraw.line
  219. if sd > d:
  220. drawLine(self.vtr.surf['trace'], px,py,x,y, self.vtr.col['trace_trace'])
  221. else:
  222. #symetric is closer
  223. drawLine(self.vtr.surf['trace'], spx,py,x,y, self.vtr.col['trace_trace'])
  224. drawLine(self.vtr.surf['trace'], px,py,sx,y, self.vtr.col['trace_trace'])
  225. self.prevLonLat = (lon,lat)
  226. self.prev = (x,y)
  227. return (x,y)
  228. ## Draw an existing route on the map
  229. # @param delay The time to wait between the draw of two hops (in float, seconds)
  230. def drawTrace(self, delay = 0.1):
  231. self.initDraw()
  232. cnt = 1
  233. for i,pt in enumerate(self.pts):
  234. if pt != None:
  235. dcoord = self.drawNext(pt, (i in self.selected), (i == len(self.pts)-1) )
  236. self.coords.append(dcoord)
  237. if delay > 0:
  238. self.drawRoute(cnt)
  239. self.vtr.flip()
  240. time.sleep(delay)
  241. else:
  242. self.coords.append(None)
  243. cnt += 1
  244. self.drawRoute()
  245. pass
  246. ## Draw the text representation of an existing route
  247. # One line per hop at the bottom left of the screen
  248. # @param stopAt Print the last 'stopAt' hops
  249. def drawRoute(self, stopAt = 32):
  250. strStatus=''
  251. self.vtr.surf['tracetxt'].fill((0,0,0,0))
  252. lh = self.vtr.font['trace_txt'].get_linesize()
  253. cy = self.vtr.surf['tracetxt'].get_height()-4
  254. cy -= lh
  255. if stopAt < 0 or stopAt > len(self.ips):
  256. rng = range(len(self.ips))
  257. else:
  258. rng = range(stopAt)
  259. rng.reverse()
  260. for i in rng:
  261. strStatus = self.hops[i]+' '+self.hosts[i]
  262. if self.ips[i] != None:
  263. strStatus += '('+self.ips[i]+')'
  264. if i in self.selected:
  265. col = self.vtr.col['trace_txtsel'] #selected color
  266. else:
  267. col = self.vtr.col['trace_txt'] #normal color
  268. statusSurf = self.vtr.font['trace_txt'].render(strStatus, 1, col)
  269. self.vtr.surf['tracetxt'].blit(statusSurf, (4,cy))
  270. cy -= lh
  271. pass
  272. ## Search if there is a hop near a screen coordinate
  273. # Will return mutliple host if the distance is equal
  274. # @param (x,y) The on screen coordinates
  275. # @param maxDist The maximum distance bellow wich a hop is selected
  276. # @return A list of hop index
  277. def searchHopByCoord(self, (x,y), maxDist = 10):
  278. maxDist = maxDist **2
  279. curRes = []
  280. curMin = maxDist+1
  281. for (i,pos) in enumerate(self.coords):
  282. if pos != None:
  283. (cx,cy) = pos
  284. d = ((cx-x)**2) + ((cy-y)**2)
  285. if d <= curMin:
  286. if d == curMin:
  287. curRes.append(i)
  288. else:
  289. curRes = [i]
  290. curMin = d
  291. return curRes
  292. ## Select hops given screen coordinate
  293. # @param pos On screen coordinate in a (x,y) tuple
  294. # @return A boolean set to True if hops were selected
  295. def select(self, pos):
  296. self.selected = self.searchHopByCoord(pos)
  297. return (len(self.selected)>0)
  298. ## Return a string representing selected hops ip
  299. # @return One ip on each line, preffixed by \t
  300. def selectedIp(self):
  301. res = ""
  302. for i in self.selected:
  303. res += '\t'+str(self.hosts[i])+'('+str(self.ips[i])+')\n'
  304. return res