Traceroute visualizer written in python
python
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

vtracert.py 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519
  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. control_summary = [
  20. "+----------------------+----------------------------------+",
  21. "| Controls summary |",
  22. "+----------------------+----------------------------------+",
  23. "+----------------------+----------------------------------+",
  24. "| ^h, ? | display this help |",
  25. "+----------------------+----------------------------------+",
  26. "| esc, ^q | exit the programm or this help |",
  27. "+----------------------+----------------------------------+",
  28. "+----------------------+----------------------------------+",
  29. "| [a-zA-Z0-9\.] | Host input |",
  30. "+----------------------+----------------------------------+",
  31. "| mid-mouse btn | paste clipboard into host input |",
  32. "+----------------------+----------------------------------+",
  33. "| enter | validate host input and |",
  34. "| | run traceroute |",
  35. "+----------------------+----------------------------------+",
  36. "| ^c | clear host input |",
  37. "+----------------------+----------------------------------+",
  38. "+----------------------+----------------------------------+",
  39. "| mouse scroll, +, - : zoom in/out |",
  40. "+----------------------+----------------------------------+",
  41. "| arrow keys : move on the map |",
  42. "+----------------------+----------------------------------+",
  43. "| left mouse btn press : select hops on the map |",
  44. "+----------------------+----------------------------------+"
  45. ]
  46. import random
  47. import time
  48. import os
  49. import pygame
  50. from pygame import gfxdraw
  51. import tracer
  52. import vtracemap
  53. class Vtracert:
  54. ## Instanciate a visual tracert
  55. # @param mapdatfile The filename containing the borders coordinates
  56. # @param width Window width
  57. # @param height Window height
  58. # @param cols Override default colors, its a dict of tuple (r,g,b) (r,g,b,a), valid keys are : bg, map, prompt, status, trace_init, trace_hop, trace_trace
  59. # @param fonts Override default font type and size its a dict of tuple (fontType, fontSize), valid keys are : prompt, status
  60. # @param promptText The prompt when reading the host to traceroute
  61. # @param defaultInput Default host to traceroute
  62. def __init__(self, mapdatfile, width = 1024, height = 768, cols = dict(), fonts = dict(), promptText = "Enter an ip or an hostname and press enter to traceroute : %s_", defaultInput = "gnu.org"):
  63. pygame.init() #Pygame init
  64. #Options
  65. self.mapfile = mapdatfile
  66. self.width = width
  67. self.height = height
  68. #Colors
  69. self.col = dict()
  70. self.col['alpha'] = pygame.Color(0,0,0,0)
  71. self.col['bg'] = pygame.Color(0,0,0)
  72. self.col['map'] = pygame.Color(15,8,63)
  73. self.col['prompt'] = pygame.Color(200,200,200)
  74. self.col['status'] = pygame.Color(200,200,200)
  75. self.col['cur_status'] = self.col['status']
  76. self.col['cur_statusline'] = self.col['status']
  77. self.col['trace_init'] = pygame.Color(0,0xf0,0)
  78. self.col['trace_end'] = pygame.Color(0xee,0x33,0xee)
  79. self.col['trace_hop'] = pygame.Color(0xf0,0xf0,0)
  80. self.col['trace_trace'] = pygame.Color(0xf0,0,0)
  81. self.col['trace_txt'] = pygame.Color(200, 200, 200, 125)
  82. self.col['trace_txtsel'] = self.col['trace_hop']
  83. self.col['trace_progress'] = pygame.Color(0,0xf0,0)
  84. self.col['help_txt'] = pygame.Color(200,200,200)
  85. self.col['help_title'] = pygame.Color(120,30,230)
  86. self.col['help_bg'] = pygame.Color(0,0,0)
  87. for n, c in cols:
  88. self.col[n] = pygame.Color(c)
  89. #Fonts
  90. self.font = dict()
  91. self.font['prompt'] = pygame.font.Font(None, 20)
  92. self.font['status'] = pygame.font.Font(None,20)
  93. self.font['cur_status'] = self.col['status']
  94. self.font['trace_txt'] = pygame.font.Font(None,20)
  95. self.font['help_title'] = pygame.font.Font(None,48)
  96. self.font['help_title'].set_underline(True);
  97. self.font['help_title'].set_bold(True);
  98. self.font['help'] = pygame.font.SysFont('monospace', 16)
  99. for n, (ft,fs) in fonts:
  100. self.font[n] = pygame.font.Font(ft,fs)
  101. #Prompt options
  102. self.promptText = promptText
  103. self.inputText = defaultInput
  104. self.inputText = '1.202.15.14'
  105. #Event filtering
  106. pygame.event.set_allowed(None)
  107. pygame.event.set_allowed([pygame.MOUSEBUTTONUP,pygame.KEYDOWN, pygame.KEYUP, pygame.QUIT])
  108. #Surface creation
  109. self.surf = dict()
  110. self.surf['screen'] = pygame.display.set_mode((width, height), pygame.DOUBLEBUF )
  111. self.surf['map'] = pygame.Surface((width, height),pygame.HWSURFACE)
  112. self.surf['trace'] = pygame.Surface((width, height),pygame.HWSURFACE | pygame.SRCALPHA)
  113. self.surf['tracetxt'] = pygame.Surface((width, height),pygame.HWSURFACE | pygame.SRCALPHA)
  114. self.surf['status'] = None
  115. for s in self.surf:
  116. if self.surf[s] != None:
  117. self.surf[s].fill(self.col['alpha'])
  118. self.help(0)
  119. self.drawStatus('Loading...');
  120. pygame.display.flip()
  121. #Clipboard handling module initialisation
  122. pygame.scrap.init()
  123. #Keyboard vars
  124. self.caps = False
  125. self.ctrl = False
  126. #Map info
  127. self.trmap = vtracemap.TraceMap(self)
  128. self.tracer = tracer.Tracer(self)
  129. ## Color accessor
  130. # @param cname Color name
  131. # @param v The value to set or False to get
  132. # @return The color named cname
  133. # @see Vtracert::opt()
  134. def Col(self, cname, v = False):
  135. return Vtracert.opt(cname, self.col, v)
  136. ## Surface accessor
  137. # @param sname surface name
  138. # @param v The value to set or False to get
  139. # @return The surface named sname
  140. # @see Vtracert::opt()
  141. def Surf(self, sname, v = False):
  142. return Vtracert.opt(sname, self.surf, v)
  143. ## Font accessor
  144. # @param fname font name
  145. # @param v The value to set or False to get
  146. # @return The font named fname
  147. # @see Vtracert::opt()
  148. def Font(self, fname, v = False):
  149. return Vtracert.opt(sname, self.font, v)
  150. ## A generic class to handle dict properties
  151. # @param cname The dict key
  152. # @param dopt The dict
  153. # @param v The value or False for get and not set
  154. # @return dopt[cname]
  155. # @see Vtracert::Col(), Vtracert::Surf(), Vtracert::Font()
  156. @classmethod
  157. def opt(c, cname, dopt, v):
  158. if not (not v and isinstance(v,bool)):
  159. dopt[cname] = v
  160. return dopt[cname]
  161. ## Return the unicode representation of the combiation of ctrl+a letter key
  162. @classmethod
  163. def ctrlUnicode(classo, c):
  164. return unicode(chr(ord(c.lower())-ord('a')+1))
  165. pass
  166. ##Display an help on the screen and wait for an KEYDOWN or QUIT event
  167. def help(self, ttl = None):
  168. scr = self.surf['screen']
  169. helpTxts = [
  170. "Vtraceroute help",
  171. "",
  172. "Vtraceroute is a traceroute program ( more info : man traceroute )",
  173. "that displays hops on a map using geoipLookup.",
  174. "",
  175. "Controls summary :",
  176. "",
  177. "^h, ? : display this help",
  178. "esc, ^q : exit the programm or this help",
  179. "",
  180. "[a-zA-Z0-9\.] : Host input",
  181. "mid-mouse btn : paste clipboard into host input",
  182. "enter : validate host input and",
  183. " run traceroute",
  184. "^c : clear host input",
  185. "",
  186. "",
  187. "mouse scroll, +, - : zoom in/out",
  188. "arrow keys : move on the map",
  189. "left mouse btn press : select hops on the map",
  190. "","",
  191. "All borders data extracted from data provided by http://thematicmapping.org and",
  192. "Licenced under Creatice Commons Attribution-Share Alike License 3.0"
  193. "",
  194. "Press any key or mouse button to exit this help"
  195. ]
  196. tFont = self.font['help_title']
  197. hFont = self.font['help']
  198. hCol = self.col['help_txt']
  199. tCol = self.col['help_title']
  200. ty = 50
  201. lspacing = 3
  202. hy = ty + tFont.get_linesize() + lspacing
  203. hx = 40
  204. #Fill screen
  205. scr.fill(self.col['help_bg'])
  206. #Print title
  207. titleTxt = helpTxts.pop(0)
  208. tsurf = tFont.render(titleTxt,1,tCol)
  209. tsc = tsurf.get_rect()
  210. tsc.centerx = self.width/2 #center the title
  211. tsc.y = ty
  212. scr.blit(tsurf,tsc)
  213. #Print help text
  214. for line in helpTxts:
  215. if len(line) > 0:
  216. tsurf = hFont.render(line,1,hCol)
  217. scr.blit(tsurf,(hx, hy))
  218. hy += hFont.get_linesize()
  219. hy += lspacing
  220. #pygame.display.flip();time.sleep(0.05);
  221. pygame.display.flip()
  222. if ttl == None:
  223. wait = True
  224. while wait:
  225. evts = pygame.event.get()
  226. for evt in evts:
  227. if evt.type in (pygame.QUIT, pygame.KEYDOWN, pygame.MOUSEBUTTONUP):
  228. wait = False
  229. break;
  230. else:
  231. time.sleep(ttl)
  232. pass
  233. """
  234. ## Return True
  235. # @c1 unicode1
  236. # @c2 unicode2
  237. def isCtrlUnicode(self, c, cu):
  238. return (self.ctrl and Vtracert.ctrlUnicode(c) == cu)
  239. """
  240. ## Blit a surface on the screen surface
  241. # @param sname The surface name
  242. # @param pos A tuple (x,y) for the blit position
  243. # @return self.surf['screen'].blit(self.surf[sname], pos)
  244. def blitScreen(self, sname, pos = (0,0) ):
  245. return self.surf['screen'].blit(self.surf[sname], pos)
  246. ## Draw the map again and call Vtracert::flip()
  247. # Call Vtracert::fillScreen()
  248. # @see Vtracert::fillScreen(), Vtracert::flip()
  249. def mapRedraw(self):
  250. self.fillScreen()
  251. self.trmap.draw()
  252. self.flip()
  253. ## Fill the screen with the 'bg' color
  254. def fillScreen(self):
  255. self.surf['screen'].fill(self.col['bg'])
  256. pass
  257. ## Blit all the surface is the right order but don't call pygame.display.flip()
  258. # The status bar horizontal ruler is a draw and not a blit
  259. # @see Vtracert::flip()
  260. def blit(self):
  261. self.blitScreen('map')
  262. self.blitScreen('status')
  263. self.drawStatusLine()
  264. self.blitScreen('trace')
  265. self.blitScreen('tracetxt')
  266. pass
  267. ## Blit all surface and flip display
  268. # @see Vtracert::blit()
  269. def flip(self, fill = True):
  270. if fill:
  271. self.fillScreen()
  272. self.blit()
  273. pygame.display.flip()
  274. pass
  275. ## Draw the prompt in the status surface
  276. # @see Vtracert::drawStatus()
  277. def drawPrompt(self):
  278. self.drawStatus(self.promptText%(self.inputText), self.font['prompt'], self.col['prompt'])
  279. ## Draw the status bar on the screen
  280. # @param statusText The text to display in the status bar
  281. # @param font The font used to render the text
  282. # @param col The text color
  283. # @param antialiasing A boolean telling if the text is rendered antialiased
  284. # @param line A boolean telling if the status bar line has to be drawn
  285. # @param lineCol The status bar line color (by default same as col)
  286. def drawStatus(self, statusText, font = None, col = None, antialiasing = True, line = True, lineCol = None):
  287. if font == None:
  288. font = self.font['status']
  289. self.font['cur_status'] = font
  290. if col == None:
  291. col = self.col['status']
  292. self.col['cur_status'] = col
  293. if lineCol == None:
  294. lineCol = col
  295. self.col['cur_statusline'] = lineCol
  296. self.surf['status'] = font.render(statusText, antialiasing, col)
  297. self.blitScreen('status')
  298. if line:
  299. self.drawStatusLine()
  300. pass
  301. ## Draw the status bar horizontal ruler
  302. # @see Vtracert::drawStatus()
  303. def drawStatusLine(self):
  304. ly = self.font['cur_status'].get_linesize() + 2
  305. pygame.gfxdraw.line(self.surf['screen'], 0, ly, self.width - 1, ly, self.col['cur_statusline'])
  306. pass
  307. ## Handle a KEYUP event
  308. # @param evt Event
  309. def keyupEvt(self, evt):
  310. if evt.key == 303 or evt.key == 304: #caps release
  311. self.caps = False
  312. elif evt.key == 305 or evt.key == 306: #ctrl release
  313. self.ctrl = False
  314. pass
  315. ## Handles a KEYDOWN event
  316. # @param evt Event
  317. def keydownEvt(self, evt):
  318. ccode = evt.key
  319. c2u = Vtracert.ctrlUnicode
  320. allowedChar = range(ord('a'),ord('z')+1)
  321. allowedChar += range(ord('A'),ord('Z')+1)
  322. allowedChar += range(ord('0'),ord('9')+1)
  323. allowedChar += [ord('.'),ord('\b'),ord('\r'),ord('\n')]
  324. allowedChar = [ chr(cc) for cc in allowedChar ]
  325. ctrlChar = [ c2u('q'), #^q
  326. c2u('h'), #^h
  327. c2u('n'), #^n
  328. c2u('c'), #^n
  329. '?',
  330. u'\x1b' #escape button
  331. ]
  332. #print evt
  333. #print self.ctrl
  334. if ccode in [273,274,275,276]: #Arrow keys
  335. if ccode == 273:
  336. d = 'up'
  337. elif ccode == 274:
  338. d = 'down'
  339. elif ccode == 275:
  340. d = 'right'
  341. else:
  342. d = 'left'
  343. self.trmap.zoom( (None, None), d)
  344. self.tracer.drawTrace(0)
  345. self.flip()
  346. pygame.event.clear(pygame.MOUSEBUTTONUP)
  347. elif ccode == 303 or ccode == 304: #caps
  348. self.caps = True
  349. elif evt.key == 305 or evt.key == 306: #ctrl release
  350. self.ctrl = True
  351. elif self.ctrl or cc in ctrlChar: # ctrl + key handle
  352. cc = evt.unicode
  353. if cc == u'\x1b' or cc == c2u('q'): #quit (esc or ^q)
  354. pygame.event.post(pygame.event.Event(pygame.QUIT))
  355. elif cc == c2u('h') or cc == '?': #help ^h ? ^?
  356. print "Help !!!!!!!!"
  357. self.help()
  358. self.flip()
  359. elif cc == c2u('c'):
  360. self.inputText = ''
  361. self.drawPrompt()
  362. elif cc == c2u('n'):
  363. for cname in self.col:
  364. c = self.col[cname]
  365. c.r = 255 - c.r
  366. c.g = 255 - c.g
  367. c.b = 255 - c.b
  368. self.col[cname] = c
  369. self.mapRedraw()
  370. elif evt.unicode in allowedChar:
  371. cc = evt.unicode
  372. if cc == '\b': #backspace
  373. if len(self.inputText) > 0:
  374. self.inputText = self.inputText[:-1]
  375. elif cc == '\r' or cc == '\n':
  376. print "OK, tracerouting %s ..."%self.inputText
  377. dest = self.inputText
  378. self.inputText = ""
  379. self.surf['trace'].fill((0,0,0,0))
  380. self.tracer.liveTrace(dest)
  381. #self.tracer.trace(dest)
  382. self.flip()
  383. else:
  384. self.inputText += cc
  385. self.drawPrompt()
  386. pass
  387. ## Handles a MOUSEBUTTONUP event
  388. # @param evt Event
  389. def mouseboutonupEvt(self, evt):
  390. if evt.button == 4 or evt.button == 5: #scrollzoom
  391. if evt.button == 4 or evt.button == 1:
  392. zoom = True
  393. elif evt.button == 5 or evt.button == 3:
  394. zoom = False
  395. self.trmap.zoom(evt.pos,zoom)
  396. self.tracer.drawTrace(0)
  397. self.blit()
  398. pygame.event.clear(pygame.MOUSEBUTTONUP)
  399. #end scrollzoom
  400. elif evt.button == 1:
  401. #Trying to select hops
  402. if self.tracer.select(evt.pos):
  403. print 'Selected : \n',self.tracer.selectedIp()
  404. self.tracer.drawTrace(0)
  405. elif evt.button == 2:
  406. clipTxt = pygame.scrap.get('TEXT')
  407. if clipTxt:
  408. inputTxt += clipTxt
  409. self.drawPrompt()
  410. else:
  411. print "\n\n"
  412. types = pygame.scrap.get_types()
  413. for t in types:
  414. print t,pygame.scrap.get(t)
  415. pass
  416. ## Listen for pygame events
  417. def run(self):
  418. self.drawPrompt();
  419. self.mapRedraw();
  420. self.flip()
  421. #Display small onetime help text
  422. htext = self.font['status'].render("Type ^h or ? for help.",1, self.col['status'])
  423. self.surf['screen'].blit(htext, (5, self.font['status'].get_linesize()+2))
  424. pygame.display.flip()
  425. again = True
  426. while again:
  427. evt = pygame.event.wait()
  428. if evt != pygame.NOEVENT:
  429. if evt.type == pygame.QUIT:
  430. again = False
  431. break
  432. elif evt.type == pygame.KEYUP:
  433. self.keyupEvt(evt)
  434. elif evt.type == pygame.KEYDOWN:
  435. self.keydownEvt(evt)
  436. elif evt.type == pygame.MOUSEBUTTONUP:
  437. self.mouseboutonupEvt(evt)
  438. self.drawPrompt();
  439. self.flip()