netviewer.cpp

Go to the documentation of this file.
00001 /*
00002 **  This file is part of Vidalia, and is subject to the license terms in the
00003 **  LICENSE file, found in the top level directory of this distribution. If you
00004 **  did not receive the LICENSE file with this file, you may obtain it from the
00005 **  Vidalia source package distributed by the Vidalia Project at
00006 **  http://www.vidalia-project.net/. No part of Vidalia, including this file,
00007 **  may be copied, modified, propagated, or distributed except according to the
00008 **  terms described in the LICENSE file.
00009 */
00010 
00011 /*
00012 ** \file netviewer.cpp
00013 ** \version $Id: netviewer.cpp 2983 2008-08-17 05:59:43Z edmanm $
00014 ** \brief Displays a map of the Tor network and the user's circuits
00015 */
00016 
00017 #include <QMessageBox>
00018 #include <QHeaderView>
00019 #include <vidalia.h>
00020 
00021 #include "netviewer.h"
00022 
00023 #define IMG_MOVE    ":/images/22x22/move-map.png"
00024 #define IMG_ZOOMIN  ":/images/22x22/zoom-in.png"
00025 #define IMG_ZOOMOUT ":/images/22x22/zoom-out.png"
00026 
00027 /** Number of milliseconds to wait after the arrival of the last descriptor whose
00028  * IP needs to be resolved to geographic information, in case more descriptors
00029  * arrive. Then we can simply lump the IPs into a single request. */
00030 #define MIN_RESOLVE_QUEUE_DELAY   (10*1000)
00031 /** Maximum number of milliseconds to wait after the arrival of the first
00032  * IP address into the resolve queue, before we flush the entire queue. */
00033 #define MAX_RESOLVE_QUEUE_DELAY   (30*1000)
00034 
00035 
00036 /** Constructor. Loads settings from VidaliaSettings.
00037  * \param parent The parent widget of this NetViewer object.\
00038  */
00039 NetViewer::NetViewer(QWidget *parent)
00040 : VidaliaWindow("NetViewer", parent)
00041 {
00042   /* Invoke Qt Designer generated QObject setup routine */
00043   ui.setupUi(this);
00044 #if defined(Q_WS_MAC)
00045   ui.actionHelp->setShortcut(QString("Ctrl+?"));
00046 #endif
00047 
00048   /* Pressing 'Esc' or 'Ctrl+W' will close the window */
00049   ui.actionClose->setShortcut(QString("Esc"));
00050   Vidalia::createShortcut("Ctrl+W", this, ui.actionClose, SLOT(trigger()));
00051 
00052   /* Get the TorControl object */
00053   _torControl = Vidalia::torControl();
00054   _torControl->setEvent(TorEvents::NewDescriptor, this, true);
00055   _torControl->setEvent(TorEvents::CircuitStatus, this, true);
00056   _torControl->setEvent(TorEvents::StreamStatus,  this, true);
00057   _torControl->setEvent(TorEvents::AddressMap,    this, true);
00058 
00059   /* Change the column widths of the tree widgets */
00060   ui.treeRouterList->header()->
00061     resizeSection(RouterListWidget::StatusColumn, 25);
00062   ui.treeRouterList->header()->
00063     resizeSection(RouterListWidget::CountryColumn, 25);
00064   ui.treeCircuitList->header()->
00065     resizeSection(CircuitListWidget::ConnectionColumn, 235);
00066 
00067   /* Create the TorMapWidget and add it to the dialog */
00068   _map = new TorMapWidget();
00069   ui.gridLayout->addWidget(_map);
00070 
00071   /* Connect zoom buttons to ZImageView zoom slots */
00072   connect(ui.actionZoomIn, SIGNAL(triggered()), _map, SLOT(zoomIn()));
00073   connect(ui.actionZoomOut, SIGNAL(triggered()), _map, SLOT(zoomOut()));
00074   connect(ui.actionZoomToFit, SIGNAL(triggered()), _map, SLOT(zoomToFit()));
00075 
00076   /* Create the timer that will be used to update the router list once every
00077    * hour. We still receive the NEWDESC event to get new descriptors, but this
00078    * needs to be called to get rid of any descriptors that were removed. */
00079   _refreshTimer.setInterval(60*60*1000);
00080   connect(&_refreshTimer, SIGNAL(timeout()), this, SLOT(refresh()));
00081   
00082   /* Set up the timers used to group together GeoIP requests for new
00083    * descriptors arriving within MIN_RESOLVE_QUEUE_DELAY milliseconds, but no
00084    * more than MAX_RESOLVE_QUEUE_DELAY milliseconds of each other. */
00085   _minResolveQueueTimer.setSingleShot(true);
00086   connect(&_minResolveQueueTimer, SIGNAL(timeout()), this, SLOT(resolve()));
00087   _maxResolveQueueTimer.setSingleShot(true);
00088   connect(&_maxResolveQueueTimer, SIGNAL(timeout()), this, SLOT(resolve()));
00089 
00090   /* Connect the necessary slots and signals */
00091   connect(ui.actionHelp, SIGNAL(triggered()), this, SLOT(help()));
00092   connect(ui.actionRefresh, SIGNAL(triggered()), this, SLOT(refresh()));
00093   connect(ui.treeRouterList, SIGNAL(routerSelected(RouterDescriptor)),
00094                 this, SLOT(routerSelected(RouterDescriptor)));
00095   connect(ui.treeRouterList, SIGNAL(zoomToRouter(QString)),
00096           _map, SLOT(zoomToRouter(QString)));
00097   connect(ui.treeCircuitList, SIGNAL(circuitSelected(Circuit)),
00098           this, SLOT(circuitSelected(Circuit)));
00099   connect(ui.treeCircuitList, SIGNAL(circuitRemoved(CircuitId)),
00100           _map, SLOT(removeCircuit(CircuitId)));
00101   connect(ui.treeCircuitList, SIGNAL(zoomToCircuit(CircuitId)),
00102           _map, SLOT(zoomToCircuit(CircuitId)));
00103   connect(ui.treeCircuitList, SIGNAL(closeCircuit(CircuitId)),
00104           _torControl, SLOT(closeCircuit(CircuitId)));
00105   connect(ui.treeCircuitList, SIGNAL(closeStream(StreamId)),
00106           _torControl, SLOT(closeStream(StreamId)));
00107 
00108   /* Respond to changes in the status of the control connection */
00109   connect(_torControl, SIGNAL(authenticated()), this, SLOT(onAuthenticated()));
00110   connect(_torControl, SIGNAL(disconnected()), this, SLOT(onDisconnected())); 
00111 
00112   /* Connect the slot to find out when geoip information has arrived */
00113   connect(&_geoip, SIGNAL(resolved(int, QList<GeoIp>)), 
00114              this,   SLOT(resolved(int, QList<GeoIp>)));
00115 }
00116 
00117 /** Display the network map window. If there are geoip requests waiting in the
00118  * queue, start the queue timers now. */
00119 void
00120 NetViewer::showWindow()
00121 {
00122   if (!_resolveQueue.isEmpty()) {
00123     _minResolveQueueTimer.start(MIN_RESOLVE_QUEUE_DELAY);
00124     _maxResolveQueueTimer.start(MAX_RESOLVE_QUEUE_DELAY);
00125   }
00126   VidaliaWindow::showWindow();
00127 }
00128 
00129 /** Loads data into map, lists and starts timer when we get connected*/
00130 void
00131 NetViewer::onAuthenticated()
00132 {
00133   _geoip.setSocksHost(_torControl->getSocksAddress(),
00134                       _torControl->getSocksPort());
00135   refresh();
00136   _refreshTimer.start();
00137   ui.actionRefresh->setEnabled(true);
00138 }
00139 
00140 /** Clears map, lists and stops timer when we get disconnected */
00141 void
00142 NetViewer::onDisconnected()
00143 {
00144   clear();
00145   _refreshTimer.stop();
00146   ui.actionRefresh->setEnabled(false);
00147 }
00148 
00149 /** Custom event handler. Catches the new descriptor events. */
00150 void
00151 NetViewer::customEvent(QEvent *event)
00152 {
00153   int type = event->type();
00154   
00155   if (type == CustomEventType::NewDescriptorEvent) {
00156     /* New router descriptor, so load it and add it to the list */
00157     NewDescriptorEvent *nde = (NewDescriptorEvent *)event;
00158     newDescriptors(nde->descriptorIDs());
00159   } else if (type == CustomEventType::CircuitEvent) {
00160     /* New or updated circuit information */
00161     CircuitEvent *ce = (CircuitEvent *)event;
00162     addCircuit(ce->circuit());
00163   } else if (type == CustomEventType::StreamEvent) {
00164     /* New or updated stream information */
00165     StreamEvent *se = (StreamEvent *)event;
00166     addStream(se->stream());
00167   } else if (type == CustomEventType::AddressMapEvent) {
00168     /* New or updated address mapping. We store the reverse of the new
00169      * mapping, so we can go from an IP address back to a hostname. */
00170     AddressMapEvent *ae = (AddressMapEvent *)event;
00171     _addressMap.add(ae->to(), ae->from(), ae->expires());
00172   }
00173 
00174   /* Update the world map */
00175   _map->update();
00176 }
00177 
00178 /** Reloads the lists of routers, circuits that Tor knows about */
00179 void
00180 NetViewer::refresh()
00181 {
00182   /* Don't let the user refresh while we're refreshing. */
00183   ui.actionRefresh->setEnabled(false);
00184 
00185   /* Clear the data */
00186   clear();
00187 
00188   /* Load router information */
00189   loadNetworkStatus();
00190   /* Load existing address mappings */
00191   loadAddressMap();
00192   /* Load Circuits and Streams information */
00193   loadConnections();
00194 
00195   /* Ok, they can refresh again. */
00196   ui.actionRefresh->setEnabled(true);
00197 } 
00198 
00199 /** Clears the lists and the map */
00200 void
00201 NetViewer::clear()
00202 {
00203   /* Clear the resolve queue and map */
00204   _resolveMap.clear();
00205   _resolveQueue.clear();
00206   /* Clear the network map */
00207   _map->clear();
00208   _map->update();
00209   /* Clear the address map */
00210   _addressMap.clear();
00211   /* Clear the lists of routers, circuits, and streams */
00212   ui.treeRouterList->clearRouters();
00213   ui.treeCircuitList->clearCircuits();
00214   ui.textRouterInfo->clear();
00215 }
00216 
00217 /** Loads a list of all current address mappings. */
00218 void
00219 NetViewer::loadAddressMap()
00220 {
00221   /* We store the reverse address mappings, so we can go from a numeric value
00222    * back to a likely more meaningful hostname to display for the user. */
00223   _addressMap = _torControl->getAddressMap().reverse();
00224 }
00225 
00226 /** Loads a list of all current circuits and streams. */
00227 void
00228 NetViewer::loadConnections()
00229 {
00230   /* Load all circuits */
00231   CircuitList circuits = _torControl->getCircuits();
00232   foreach (Circuit circuit, circuits) {
00233     addCircuit(circuit);
00234   }
00235   /* Now load all streams */
00236   StreamList streams = _torControl->getStreams();
00237   foreach (Stream stream, streams) {
00238     addStream(stream);
00239   }
00240 
00241   /* Update the map */
00242   _map->update();
00243 }
00244 
00245 /** Adds <b>circuit</b> to the map and the list */
00246 void
00247 NetViewer::addCircuit(const Circuit &circuit)
00248 {
00249   /* Add the circuit to the list of all current circuits */
00250   ui.treeCircuitList->addCircuit(circuit);
00251   /* Plot the circuit on the map */
00252   _map->addCircuit(circuit.id(), circuit.routerIDs());
00253 }
00254 
00255 /** Adds <b>stream</b> to its associated circuit on the list of all circuits. */
00256 void
00257 NetViewer::addStream(const Stream &stream)
00258 {
00259   QString target = stream.targetAddress();
00260   QHostAddress addr(target);
00261   
00262   /* If the stream's target has an IP address instead of a host name,
00263    * check our cache for an existing reverse address mapping. */
00264   if (!addr.isNull() && _addressMap.isMapped(target)) {
00265     /* Replace the IP address in the stream event with the original 
00266      * hostname */
00267     ui.treeCircuitList->addStream(
00268       Stream(stream.id(), stream.status(), stream.circuitId(),
00269              _addressMap.mappedTo(target), stream.targetPort()));
00270   } else {
00271     ui.treeCircuitList->addStream(stream);
00272   }
00273 }
00274 
00275 /** Called when the user selects the "Help" action from the toolbar. */
00276 void
00277 NetViewer::help()
00278 {
00279   emit helpRequested("netview");
00280 }
00281 
00282 /** Retrieves a list of all running routers from Tor and their descriptors,
00283  * and adds them to the RouterListWidget. */
00284 void
00285 NetViewer::loadNetworkStatus()
00286 {
00287   NetworkStatus networkStatus = _torControl->getNetworkStatus();
00288   foreach (RouterStatus rs, networkStatus) {
00289     if (!rs.isRunning())
00290       continue;
00291 
00292     RouterDescriptor rd = _torControl->getRouterDescriptor(rs.id());
00293     if (!rd.isEmpty())
00294       addRouter(rd);
00295   }
00296 }
00297 
00298 /** Adds a router to our list of servers and retrieves geographic location
00299  * information for the server. */
00300 void
00301 NetViewer::addRouter(const RouterDescriptor &rd)
00302 {
00303   /* Add the descriptor to the list of server */
00304   ui.treeRouterList->addRouter(rd);
00305 
00306   /* Add this IP to a list of addresses whose geographic location we'd like to
00307    * find, but not for special purpose descriptors (e.g., bridges). This
00308    * check is only valid for Tor >= 0.2.0.13-alpha. */
00309   if (_torControl->getTorVersion() >= 0x020013) {
00310     DescriptorAnnotations annotations =
00311       _torControl->getDescriptorAnnotations(rd.id());
00312     if (!annotations.contains("purpose"))
00313       addToResolveQueue(rd.ip(), rd.id());
00314   } else {
00315     addToResolveQueue(rd.ip(), rd.id());
00316   }
00317 }
00318 
00319 /** Called when a NEWDESC event arrives. Retrieves new router descriptors
00320  * for the router identities given in <b>ids</b> and updates the router
00321  * list and network map. */
00322 void
00323 NetViewer::newDescriptors(const QStringList &ids)
00324 {
00325   foreach (QString id, ids) {
00326     RouterDescriptor rd = _torControl->getRouterDescriptor(id);
00327     if (!rd.isEmpty())
00328       addRouter(rd); /* Updates the existing entry */
00329   }
00330 }
00331 
00332 /** Adds an IP address to the resolve queue and updates the queue timers. */
00333 void
00334 NetViewer::addToResolveQueue(const QHostAddress &ip, const QString &id)
00335 {
00336   QString ipstr = ip.toString();
00337   if (!_resolveMap.values(ipstr).contains(id)) {
00338     /* Remember which server ids belong to which IP addresses */
00339     _resolveMap.insertMulti(ipstr, id);
00340   }
00341  
00342   if (!_resolveQueue.contains(ip) && !_geoip.resolveFromCache(ip)) {
00343     /* Add the IP to the queue of IPs waiting for geographic information  */
00344     _resolveQueue << ip;
00345  
00346     /* Wait MIN_RESOLVE_QUEUE_DELAY after the last item inserted into the
00347      * queue, before sending the resolve request. */
00348     _minResolveQueueTimer.start(MIN_RESOLVE_QUEUE_DELAY);
00349     
00350     /* Do not wait longer than MAX_RESOLVE_QUEUE_DELAY from the time the first
00351      * item is inserted into the queue, before flushing and resolving the
00352      * queue. */
00353     if (_resolveQueue.size() == 1) {
00354       _maxResolveQueueTimer.start(MAX_RESOLVE_QUEUE_DELAY);
00355     }
00356   }
00357 }
00358 
00359 /** Called when the user selects a circuit from the circuit and streams
00360  * list. */
00361 void
00362 NetViewer::circuitSelected(const Circuit &circuit)
00363 {
00364   /* Clear any selected items. */
00365   ui.treeRouterList->deselectAll();
00366   ui.textRouterInfo->clear();
00367   _map->deselectAll();
00368 
00369   /* Select the items on the map and in the list */
00370   _map->selectCircuit(circuit.id());
00371 
00372   QList<RouterDescriptor> routers;
00373 
00374   foreach (QString id, circuit.routerIDs()) {
00375     /* Try to find and select each router in the path */
00376     RouterListItem *item = ui.treeRouterList->findRouterById(id);
00377     if (item)
00378       routers.append(item->descriptor());
00379   }
00380 
00381   ui.textRouterInfo->display(routers);
00382 }
00383 
00384 /** Called when the user selects a router from the router list. */
00385 void
00386 NetViewer::routerSelected(const RouterDescriptor &router)
00387 {
00388   _map->deselectAll();
00389   ui.textRouterInfo->clear();
00390   ui.textRouterInfo->display(router);
00391   _map->selectRouter(router.id());
00392 }
00393 
00394 /** If there are any IPs in the resolve queue, do the request now. */
00395 void
00396 NetViewer::resolve()
00397 {
00398   if (!_resolveQueue.isEmpty()) {
00399     /* Send the request now if either the network map is visible, or the
00400      * request is for more than a quarter of the servers in the list. */
00401     if (isVisible() || 
00402         (_resolveQueue.size() >= ui.treeRouterList->topLevelItemCount()/4)) {
00403       vInfo("Sending GeoIP request for %1 IP addresses.")
00404                                .arg(_resolveQueue.size());
00405       /* Flush the resolve queue and stop the timers */
00406       _geoip.resolve(_resolveQueue);
00407       _resolveQueue.clear();
00408     }
00409   }
00410   /* Stop the queue timers. Only one should be active since the other is what
00411    * called this slot, but calling stop() on a stopped timer does not hurt. */
00412   _minResolveQueueTimer.stop();
00413   _maxResolveQueueTimer.stop();
00414 }
00415 
00416 /** Called when a list of GeoIp information has been resolved. */
00417 void
00418 NetViewer::resolved(int id, const QList<GeoIp> &geoips)
00419 {
00420   Q_UNUSED(id);
00421   QString ip;
00422   RouterListItem *router;
00423  
00424   foreach (GeoIp geoip, geoips) {
00425     /* Find all routers that are at this IP address */
00426     ip = geoip.ip().toString();
00427     QList<QString> ids = _resolveMap.values(ip);
00428     _resolveMap.remove(ip);
00429     if (geoip.isUnknown())
00430       continue; /* We don't know where this router is */
00431       
00432     /* Update their geographic location information with the results of this
00433      * GeoIP query. */
00434     foreach (QString id, ids) {
00435       router = ui.treeRouterList->findRouterById(id);
00436       if (router) {
00437         /* Save the location information in the descriptor */
00438         router->setLocation(geoip);
00439         /* Plot the router on the map */
00440         _map->addRouter(router->id(), geoip.latitude(), geoip.longitude());
00441       }
00442     }
00443   }
00444 
00445   /* Update the circuit lines */
00446   foreach (Circuit circuit, ui.treeCircuitList->circuits()) {
00447     _map->addCircuit(circuit.id(), circuit.routerIDs());
00448   }
00449 
00450   /* Repaint the map */
00451   _map->update();
00452 }
00453 

Generated on Tue Jul 7 17:00:56 2009 for Vidalia by  doxygen 1.4.7