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

Generated on Sat Aug 16 17:31:48 2008 for Vidalia by  doxygen 1.5.6