/
visual_traceroute.py
225 lines (177 loc) · 7.2 KB
/
visual_traceroute.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
import sys
from PyQt5 import QtCore
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtGui import QIcon
from PyQt5.QtWebKitWidgets import *
from PyQt5.QtCore import pyqtSlot
from traceroute import TraceRoute
import visual_traceroute_ui
# todo
# - only works with IP4, look at IP6
# - look at fixing hack for multiple markers on google maps
class VisualTraceRoute(QMainWindow, visual_traceroute_ui.Ui_visual_traceroute_main_window):
""" Performs a trace route on a given IP address, and displays
as a raw text output and an overlay on top of Google Maps.
This makes use of http://ip-api.com to determine the latitude and longitude (among other
attributes) of each IP address in the route.
"""
def __init__(self):
"""
Sets up the initial windows
"""
super(VisualTraceRoute, self).__init__()
self.setupUi(self)
self.statusbar.show()
self.setWindowTitle("Visual TraceRoute")
self.setWindowIcon(QIcon('network-icon.png'))
self.routeListObjectWrapper = RouteWrapper()
# set up buttons
self.doLookupPushButton.setToolTip("start Trace Route")
self.doLookupPushButton.clicked.connect(self.onClickDoItButton)
self.doLookupPushButton.setAutoDefault(True)
# set up menu handlers
self.aboutMenuItem.triggered.connect(self.onAboutClicked)
self.exitMenuItem.triggered.connect(self.close)
# set up async worker thread
self.traceRouteThreadedHandler = None
# set up web view
hbx = QHBoxLayout()
self.map.setLayout(hbx)
self.web = QWebView()
self.web.page().mainFrame().javaScriptWindowObjectCleared.connect(self.addJavascriptObjects)
self.web.page().mainFrame().addToJavaScriptWindowObject("route_list", self.routeListObjectWrapper)
hbx.addWidget(self.web)
self.web.show()
@pyqtSlot()
def addJavascriptObjects(self):
""" Needed to repopulate the Javascript with the python objects each time the web page is refreshed
:return: None
"""
self.web.page().mainFrame().addToJavaScriptWindowObject("route_list", self.routeListObjectWrapper)
@pyqtSlot()
def onClickDoItButton(self):
""" Called when the 'DoIt' button is pressed.
Reads the entered URL, validates it and initiates the trace route command
:return: None
"""
try:
self.statusbar.clearMessage()
self.statusbar.showMessage("Working...")
self.doLookupPushButton.setEnabled(False)
self.textOutput.clear()
# read entered URL
url = self.urlLineEdit.text()
if url:
with open("./busy.html", "r") as htmlFile:
html = htmlFile.read()
self.web.setHtml(html)
self.traceRouteThreadedHandler = TraceRoute(url)
# set up callbacks for the trace route output
self.traceRouteThreadedHandler.traceRouteTerminated.connect(self.onTraceRouteComplete)
self.traceRouteThreadedHandler.textOutputReady.connect(self.onTraceRouteRawOutput)
self.traceRouteThreadedHandler.start()
else:
self.statusbar.showMessage("URL is invalid", 5000)
self.doLookupPushButton.setEnabled(True)
QMessageBox.information(self, "Empty Field",
"The entered URL is invalid")
except Exception as e:
QMessageBox.critical(self,
"Critical",
"Problem initiating trace route : " + str(e))
@pyqtSlot(str)
def onTraceRouteRawOutput(self, command_output):
"""
Accepts a single line of text from the traceroute command, and displays on the text view
:param command_output: a single line from the traceroute output
:return: None
"""
try:
self.textOutput.moveCursor(QTextCursor.End)
self.textOutput.insertPlainText(command_output)
except Exception as e:
QMessageBox.critical(self,
"Critical",
"Problem updating UI with traceroute text output : " + str(e))
@pyqtSlot(object)
def onTraceRouteComplete(self, route_list):
"""
Called when the trace route command is complete.
Takes the entire route list, passes it to the Javascript and draws the visual trace route
:param route_list: a list of IP addresses
:return: None
"""
try:
self.doLookupPushButton.setEnabled(True)
self.doLookupPushButton.update()
self.statusbar.clearMessage()
self.statusbar.showMessage("Complete!")
self.routeListObjectWrapper.clear()
self.routeListObjectWrapper.add(route_list)
self.web.page().mainFrame().addToJavaScriptWindowObject("route_list", self.routeListObjectWrapper)
with open("./map_js.html", "r") as htmlFile:
html = htmlFile.read()
self.web.setHtml(html)
except Exception as e:
QMessageBox.critical(self,
"Critical",
"Problem performing TraceRoute command : " + str(e))
@pyqtSlot()
def onAboutClicked(self):
box = QMessageBox.information(self, "About Visual TraceRoute", "Visual trace route")
def closeEvent(self, event):
"""
Intercepts the app exit event
:param event: incoming close event
:return: None
"""
quit_msg = "Are you sure you want to exit the program?"
reply = QMessageBox.question(self, 'Message',
quit_msg, QMessageBox.Yes, QMessageBox.No)
if reply == QMessageBox.Yes:
event.accept()
else:
event.ignore()
class RouteWrapper(QtCore.QObject):
"""
A class used for holding attributes that are passed into the QWebView Javascript container
"""
def __init__(self):
QtCore.QObject.__init__(self)
self.routes = []
def clear(self):
self.routes = []
def add(self, route_list):
self.routes = route_list
@pyqtSlot(result="int")
def num_routes(self):
return len(self.routes)
@pyqtSlot(int, result=str)
def get_ip(self, offset):
row = self.routes[offset]
return str(row["query"])
@pyqtSlot(int, result=str)
def get_longitude(self, offset):
row = self.routes[offset]
return str(row["lon"])
@pyqtSlot(int, result=str)
def get_latitude(self, offset):
row = self.routes[offset]
return str(row["lat"])
@pyqtSlot(int, result=str)
def get_ISP(self, offset):
row = self.routes[offset]
return str(row["isp"])
@pyqtSlot(int, result=str)
def get_country(self, offset):
row = self.routes[offset]
return str(row["country"])
@pyqtSlot(int, result=str)
def get_timezone(self, offset):
row = self.routes[offset]
return str(row["timezone"])
app = QApplication(sys.argv)
nu = VisualTraceRoute()
nu.show()
app.exec_()