netviewer.cpp

Go to the documentation of this file.
00001 /****************************************************************
00002  *  Vidalia is distributed under the following license:
00003  *
00004  *  Copyright (C) 2006,  Matt Edman, Justin Hipple
00005  *
00006  *  This program is free software; you can redistribute it and/or
00007  *  modify it under the terms of the GNU General Public License
00008  *  as published by the Free Software Foundation; either version 2
00009  *  of the License, or (at your option) any later version.
00010  *
00011  *  This program is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  *  GNU General Public License for more details.
00015  *
00016  *  You should have received a copy of the GNU General Public License
00017  *  along with this program; if not, write to the Free Software
00018  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, 
00019  *  Boston, MA  02110-1301, USA.
00020  ****************************************************************/
00021 
00022 /** 
00023  * \file netviewer.cpp
00024  * \version $Id: netviewer.cpp 1845 2007-08-21 04:34:06Z edmanm $
00025  * \brief Displays a map of the Tor network and the user's circuits
00026  */
00027 
00028 #include <QMessageBox>
00029 #include <QHeaderView>
00030 #include <vidalia.h>
00031 
00032 #include "netviewer.h"
00033 
00034 #define IMG_MOVE    ":/images/22x22/move-map.png"
00035 #define IMG_ZOOMIN  ":/images/22x22/zoom-in.png"
00036 #define IMG_ZOOMOUT ":/images/22x22/zoom-out.png"
00037 
00038 /** Number of milliseconds to wait after the arrival of the last descriptor whose
00039  * IP needs to be resolved to geographic information, in case more descriptors
00040  * arrive. Then we can simply lump the IPs into a single request. */
00041 #define MIN_RESOLVE_QUEUE_DELAY   (10*1000)
00042 /** Maximum number of milliseconds to wait after the arrival of the first
00043  * IP address into the resolve queue, before we flush the entire queue. */
00044 #define MAX_RESOLVE_QUEUE_DELAY   (30*1000)
00045 
00046 
00047 /** Constructor. Loads settings from VidaliaSettings.
00048  * \param parent The parent widget of this NetViewer object.\
00049  */
00050 NetViewer::NetViewer(QWidget *parent)
00051 : VidaliaWindow("NetViewer", parent)
00052 {
00053   /* Invoke Qt Designer generated QObject setup routine */
00054   ui.setupUi(this);
00055 #if defined(Q_WS_MAC)
00056   ui.actionHelp->setShortcut(QString("Ctrl+?"));
00057 #endif
00058 #if !defined(Q_WS_WIN)
00059   ui.actionClose->setShortcut(QString("Ctrl+W"));
00060 #endif
00061 
00062   /* Get the TorControl object */
00063   _torControl = Vidalia::torControl();
00064   _torControl->setEvent(TorEvents::NewDescriptor, this, true);
00065   _torControl->setEvent(TorEvents::CircuitStatus, this, true);
00066   _torControl->setEvent(TorEvents::StreamStatus,  this, true);
00067   _torControl->setEvent(TorEvents::AddressMap,    this, true);
00068 
00069   /* Change the column widths of the tree widgets */
00070   ui.treeRouterList->header()->
00071     resizeSection(RouterListWidget::StatusColumn, 25);
00072   ui.treeRouterList->header()->
00073     resizeSection(RouterListWidget::CountryColumn, 25);
00074   ui.treeCircuitList->header()->
00075     resizeSection(CircuitListWidget::ConnectionColumn, 235);
00076 
00077   /* Create the TorMapWidget and add it to the dialog */
00078   _map = new TorMapWidget();
00079   ui.gridLayout->addWidget(_map);
00080 
00081   /* Connect zoom buttons to ZImageView zoom slots */
00082   connect(ui.actionZoomIn, SIGNAL(triggered()), _map, SLOT(zoomIn()));
00083   connect(ui.actionZoomOut, SIGNAL(triggered()), _map, SLOT(zoomOut()));
00084   connect(ui.actionZoomToFit, SIGNAL(triggered()), _map, SLOT(zoomToFit()));
00085 
00086   /* Create the timer that will be used to update the router list once every
00087    * hour. We still receive the NEWDESC event to get new descriptors, but this
00088    * needs to be called to get rid of any descriptors that were removed. */
00089   _refreshTimer.setInterval(60*60*1000);
00090   connect(&_refreshTimer, SIGNAL(timeout()), this, SLOT(refresh()));
00091   
00092   /* Set up the timers used to group together GeoIP requests for new
00093    * descriptors arriving within MIN_RESOLVE_QUEUE_DELAY milliseconds, but no
00094    * more than MAX_RESOLVE_QUEUE_DELAY milliseconds of each other. */
00095   _minResolveQueueTimer.setSingleShot(true);
00096   connect(&_minResolveQueueTimer, SIGNAL(timeout()), this, SLOT(resolve()));
00097   _maxResolveQueueTimer.setSingleShot(true);
00098   connect(&_maxResolveQueueTimer, SIGNAL(timeout()), this, SLOT(resolve()));
00099 
00100   /* Connect the necessary slots and signals */
00101   connect(ui.actionHelp, SIGNAL(triggered()), this, SLOT(help()));
00102   connect(ui.actionRefresh, SIGNAL(triggered()), this, SLOT(refresh()));
00103   connect(ui.treeRouterList, SIGNAL(routerSelected(RouterDescriptor)),
00104                 this, SLOT(routerSelected(RouterDescriptor)));
00105   connect(ui.treeRouterList, SIGNAL(zoomToRouter(QString)),
00106           _map, SLOT(zoomToRouter(QString)));
00107   connect(ui.treeCircuitList, SIGNAL(circuitSelected(Circuit)),
00108           this, SLOT(circuitSelected(Circuit)));
00109   connect(ui.treeCircuitList, SIGNAL(circuitRemoved(quint64)),
00110           _map, SLOT(removeCircuit(quint64)));
00111   connect(ui.treeCircuitList, SIGNAL(zoomToCircuit(quint64)),
00112           _map, SLOT(zoomToCircuit(quint64)));
00113   connect(ui.treeCircuitList, SIGNAL(closeCircuit(quint64)),
00114           _torControl, SLOT(closeCircuit(quint64)));
00115   connect(ui.treeCircuitList, SIGNAL(closeStream(quint64)),
00116           _torControl, SLOT(closeStream(quint64)));
00117 
00118   /* Respond to changes in the status of the control connection */
00119   connect(_torControl, SIGNAL(authenticated()), this, SLOT(onAuthenticated()));
00120   connect(_torControl, SIGNAL(disconnected()), this, SLOT(onDisconnected())); 
00121 
00122   /* Connect the slot to find out when geoip information has arrived */
00123   connect(&_geoip, SIGNAL(resolved(int, QList<GeoIp>)), 
00124              this,   SLOT(resolved(int, QList<GeoIp>)));
00125 }
00126 
00127 /** Display the network map window. If there are geoip requests waiting in the
00128  * queue, start the queue timers now. */
00129 void
00130 NetViewer::showWindow()
00131 {
00132   if (!_resolveQueue.isEmpty()) {
00133     _minResolveQueueTimer.start(MIN_RESOLVE_QUEUE_DELAY);
00134     _maxResolveQueueTimer.start(MAX_RESOLVE_QUEUE_DELAY);
00135   }
00136   VidaliaWindow::showWindow();
00137 }
00138 
00139 /** Loads data into map, lists and starts timer when we get connected*/
00140 void
00141 NetViewer::onAuthenticated()
00142 {
00143   _geoip.setSocksHost(_torControl->getSocksAddress(),
00144                       _torControl->getSocksPort());
00145   refresh();
00146   _refreshTimer.start();
00147   ui.actionRefresh->setEnabled(true);
00148 }
00149 
00150 /** Clears map, lists and stops timer when we get disconnected */
00151 void
00152 NetViewer::onDisconnected()
00153 {
00154   clear();
00155   _refreshTimer.stop();
00156   ui.actionRefresh->setEnabled(false);
00157 }
00158 
00159 /** Custom event handler. Catches the new descriptor events. */
00160 void
00161 NetViewer::customEvent(QEvent *event)
00162 {
00163   int type = event->type();
00164   
00165   if (type == CustomEventType::NewDescriptorEvent) {
00166     /* New router descriptor, so load it and add it to the list */
00167     NewDescriptorEvent *nde = (NewDescriptorEvent *)event;
00168     loadDescriptors(nde->descriptorIDs());
00169   } else if (type == CustomEventType::CircuitEvent) {
00170     /* New or updated circuit information */
00171     CircuitEvent *ce = (CircuitEvent *)event;
00172     addCircuit(ce->circuit());
00173   } else if (type == CustomEventType::StreamEvent) {
00174     /* New or updated stream information */
00175     StreamEvent *se = (StreamEvent *)event;
00176     addStream(se->stream());
00177   } else if (type == CustomEventType::AddressMapEvent) {
00178     /* New or updated address mapping. We store the reverse of the new
00179      * mapping, so we can go from an IP address back to a hostname. */
00180     AddressMapEvent *ae = (AddressMapEvent *)event;
00181     _addressMap.add(ae->to(), ae->from(), ae->expires());
00182   }
00183 
00184   /* Update the world map */
00185   _map->update();
00186 }
00187 
00188 /** Reloads the lists of routers, circuits that Tor knows about */
00189 void
00190 NetViewer::refresh()
00191 {
00192   /* Don't let the user refresh while we're refreshing. */
00193   ui.actionRefresh->setEnabled(false);
00194 
00195   /* Clear the data */
00196   clear();
00197 
00198   /* Load router information */
00199   loadDescriptors(_torControl->getRouterIDList());
00200   /* Load existing address mappings */
00201   loadAddressMap();
00202   /* Load Circuits and Streams information */
00203   loadConnections();
00204 
00205   /* Ok, they can refresh again. */
00206   ui.actionRefresh->setEnabled(true);
00207 } 
00208 
00209 /** Clears the lists and the map */
00210 void
00211 NetViewer::clear()
00212 {
00213   /* Clear the resolve queue and map */
00214   _resolveMap.clear();
00215   _resolveQueue.clear();
00216   /* Clear the network map */
00217   _map->clear();
00218   _map->update();
00219   /* Clear the address map */
00220   _addressMap.clear();
00221   /* Clear the lists of routers, circuits, and streams */
00222   ui.treeRouterList->clearRouters();
00223   ui.treeCircuitList->clearCircuits();
00224   ui.textRouterInfo->clear();
00225 }
00226 
00227 /** Loads a list of all current address mappings. */
00228 void
00229 NetViewer::loadAddressMap()
00230 {
00231   /* We store the reverse address mappings, so we can go from a numeric value
00232    * back to a likely more meaningful hostname to display for the user. */
00233   _addressMap = _torControl->getAddressMap().reverse();
00234 }
00235 
00236 /** Loads a list of all current circuits and streams. */
00237 void
00238 NetViewer::loadConnections()
00239 {
00240   /* Load all circuits */
00241   QList<Circuit> circuits = _torControl->getCircuits();
00242   foreach (Circuit circuit, circuits) {
00243     addCircuit(circuit);
00244   }
00245   /* Now load all streams */
00246   QList<Stream> streams = _torControl->getStreams();
00247   foreach (Stream stream, streams) {
00248     addStream(stream);
00249   }
00250 
00251   /* Update the map */
00252   _map->update();
00253 }
00254 
00255 /** Adds <b>circuit</b> to the map and the list */
00256 void
00257 NetViewer::addCircuit(Circuit circuit)
00258 {
00259   /* Circuits displayed to the user always use nicknames */
00260   Circuit circNames = circuitPathNames(circuit);
00261   /* Circuits on the map always use keyids */
00262   Circuit circIds   = circuitPathIDs(circuit);
00263 
00264   ui.treeCircuitList->addCircuit(circuit, circNames.path());
00265   _map->addCircuit(circuit.id(), circIds.hops());
00266 }
00267 
00268 /** Adds <b>stream</b> to its associated circuit on the list of all circuits. */
00269 void
00270 NetViewer::addStream(Stream stream)
00271 {
00272   QString target = stream.targetAddress();
00273   QHostAddress addr(target);
00274   
00275   /* If the stream's target has an IP address instead of a host name,
00276    * check our cache for an existing reverse address mapping. */
00277   if (!addr.isNull() && _addressMap.isMapped(target)) {
00278     /* Replace the IP address in the stream event with the original 
00279      * hostname */
00280     stream = Stream(stream.id(), stream.status(), stream.circuitId(),
00281                     _addressMap.mappedTo(target), stream.targetPort());
00282   }
00283   ui.treeCircuitList->addStream(stream);
00284 }
00285 
00286 /** Called when the user selects the "Help" action from the toolbar. */
00287 void
00288 NetViewer::help()
00289 {
00290   Vidalia::help("netview");
00291 }
00292 
00293 /** Loads a list of new descriptors from the given IDs. */
00294 void
00295 NetViewer::loadDescriptors(QStringList ids)
00296 {
00297   /* Get descriptors for all the given IDs */
00298   QList<RouterDescriptor> rds = _torControl->getDescriptorListById(ids);
00299   
00300   vInfo("Loading %1 server descriptors for the network map.")
00301                                              .arg(rds.size());
00302   foreach (RouterDescriptor rd, rds) {
00303     /* Load the router descriptor and add it to the router list. */
00304     if (!rd.isEmpty()) {
00305       addRouter(rd);
00306     }
00307   }
00308 }
00309 
00310 /** Adds a router to our list of servers and retrieves geographic location
00311  * information for the server. */
00312 void
00313 NetViewer::addRouter(RouterDescriptor rd)
00314 {
00315   /* Add the descriptor to the list of server */
00316   ui.treeRouterList->addRouter(rd);
00317 
00318   /* Add this IP to a list whose geographic location we'd like to find. */
00319   addToResolveQueue(rd.ip(), rd.id());
00320 }
00321 
00322 /** Adds an IP address to the resolve queue and updates the queue timers. */
00323 void
00324 NetViewer::addToResolveQueue(QHostAddress ip, QString id)
00325 {
00326   QString ipstr = ip.toString();
00327   if (!_resolveMap.values(ipstr).contains(id)) {
00328     /* Remember which server ids belong to which IP addresses */
00329     _resolveMap.insertMulti(ipstr, id);
00330   }
00331  
00332   if (!_resolveQueue.contains(ip) && !_geoip.resolveFromCache(ip)) {
00333     /* Add the IP to the queue of IPs waiting for geographic information  */
00334     _resolveQueue << ip;
00335  
00336     /* Wait MIN_RESOLVE_QUEUE_DELAY after the last item inserted into the
00337      * queue, before sending the resolve request. */
00338     _minResolveQueueTimer.start(MIN_RESOLVE_QUEUE_DELAY);
00339     
00340     /* Do not wait longer than MAX_RESOLVE_QUEUE_DELAY from the time the first
00341      * item is inserted into the queue, before flushing and resolving the
00342      * queue. */
00343     if (_resolveQueue.size() == 1) {
00344       _maxResolveQueueTimer.start(MAX_RESOLVE_QUEUE_DELAY);
00345     }
00346   }
00347 }
00348 
00349 /** Called when the user selects a circuit from the circuit and streams
00350  * list. */
00351 void
00352 NetViewer::circuitSelected(Circuit circuit)
00353 {
00354   /* Clear any selected items. */
00355   ui.treeRouterList->deselectAll();
00356   ui.textRouterInfo->clear();
00357   _map->deselectAll();
00358 
00359   /* Select the items on the map and in the list */
00360   _map->selectCircuit(circuit.id());
00361 
00362   QList<RouterDescriptor> routers;
00363 
00364   foreach (QString router, circuit.hops()) {
00365     /* Try to find and select each router in the path */
00366     RouterListItem *item = (router.startsWith("$") ? 
00367                 ui.treeRouterList->findRouterById(router.mid(1))
00368               : ui.treeRouterList->findRouterByName(router));
00369     if (item) {
00370       routers.append(item->descriptor());
00371     }
00372   }
00373 
00374   ui.textRouterInfo->display(routers);
00375 }
00376 
00377 /** Called when the user selects a router from the router list. */
00378 void
00379 NetViewer::routerSelected(RouterDescriptor router)
00380 {
00381   _map->deselectAll();
00382   ui.textRouterInfo->clear();
00383   ui.textRouterInfo->display(router);
00384   _map->selectRouter(router.id());
00385 }
00386 
00387 /** If there are any IPs in the resolve queue, do the request now. */
00388 void
00389 NetViewer::resolve()
00390 {
00391   if (!_resolveQueue.isEmpty()) {
00392     /* Send the request now if either the network map is visible, or the
00393      * request is for more than a quarter of the servers in the list. */
00394     if (isVisible() || 
00395         (_resolveQueue.size() >= ui.treeRouterList->topLevelItemCount()/4)) {
00396       vInfo("Sending GeoIP request for %1 IP addresses.")
00397                                .arg(_resolveQueue.size());
00398       /* Flush the resolve queue and stop the timers */
00399       _geoip.resolve(_resolveQueue);
00400       _resolveQueue.clear();
00401     }
00402   }
00403   /* Stop the queue timers. Only one should be active since the other is what
00404    * called this slot, but calling stop() on a stopped timer does not hurt. */
00405   _minResolveQueueTimer.stop();
00406   _maxResolveQueueTimer.stop();
00407 }
00408 
00409 /** Called when a list of GeoIp information has been resolved. */
00410 void
00411 NetViewer::resolved(int id, QList<GeoIp> geoips)
00412 {
00413   Q_UNUSED(id);
00414   QString ip;
00415   RouterListItem *router;
00416  
00417   foreach (GeoIp geoip, geoips) {
00418     /* Find all routers that are at this IP address */
00419     ip = geoip.ip().toString();
00420     QList<QString> ids = _resolveMap.values(ip);
00421     _resolveMap.remove(ip);
00422     if (geoip.isUnknown())
00423       continue; /* We don't know where this router is */
00424       
00425     /* Update their geographic location information with the results of this
00426      * GeoIP query. */
00427     foreach (QString id, ids) {
00428       router = ui.treeRouterList->findRouterById(id);
00429       if (router) {
00430         /* Save the location information in the descriptor */
00431         router->setLocation(geoip);
00432         /* Plot the router on the map */
00433         _map->addRouter(router->id(), geoip.latitude(), geoip.longitude());
00434       }
00435     }
00436   }
00437 
00438   /* Update the circuit lines */
00439   foreach (Circuit circuit, ui.treeCircuitList->circuits()) {
00440     _map->addCircuit(circuit.id(), circuitPathIDs(circuit).hops());
00441   }
00442   
00443   /* Repaint the map */
00444   _map->update();
00445 }
00446 
00447 /** Convert all hops in <b>circ</b>'s path to server identities. <b>circ</b>'s
00448  * status and circuit ID will be preserved. If no ID can be found for a
00449  * router name, the name will be left in the circuit's path. */
00450 Circuit
00451 NetViewer::circuitPathIDs(Circuit circ)
00452 {
00453   QStringList path = circ.hops();
00454   QStringList ids;
00455   RouterDescriptor rd;
00456   quint32 torVersion;
00457 
00458   torVersion = _torControl->getTorVersion();
00459   foreach (QString hop, path) {
00460     if (hop.startsWith("$")) {
00461       /* This hop is already specified as a keyid, so just strip the "$" */
00462       hop.remove(0,1);
00463     } else {
00464       if (torVersion < 0x00010202) {
00465         /* Tor versions earlier than 0.1.2.2 have a bug where they will tell
00466          * you a server's nickname in a circuit, even if that server is
00467          * non-Named. If we requested the descriptor by name for a non-Named server
00468          * from Tor, the user's log could be filled with warnings. So, just
00469          * try to look it up from our own router list first. */
00470         RouterListItem *item = ui.treeRouterList->findRouterByName(hop);
00471         if (item) {
00472           rd = item->descriptor();
00473         } else {
00474           /* We don't know about the router, so try to get it by name even
00475            * though Tor will probably yell at us. We can't help it. */
00476           rd = _torControl->getDescriptorByName(hop);
00477           if (!rd.isEmpty()) {
00478             /* Add this router to the list of those we know about */
00479             addRouter(rd);
00480           }
00481         }
00482       } else {
00483         /* Ask Tor for the descriptor for the given router name */
00484         rd = _torControl->getDescriptorByName(hop);
00485       }
00486       hop = (rd.isEmpty() ? hop : rd.id());
00487     }
00488     ids << hop;
00489   }
00490   return Circuit(circ.id(), circ.status(), ids);
00491 }
00492 
00493 /** Convert all hops in <b>circ</b>'s path to server names. <b>circ</b>'s
00494  * status and circuit ID will be preserved. If no name can be found for a
00495  * server identity, the ID will be left in the circuit's path. */
00496 Circuit
00497 NetViewer::circuitPathNames(Circuit circ)
00498 {
00499   QStringList path = circ.hops();
00500   QStringList names;
00501   RouterDescriptor rd;
00502   
00503   foreach (QString hop, path) {
00504     if (hop.startsWith("$")) {
00505       /* Check if we already have a descriptor for this server identity. */
00506       RouterListItem *item =  ui.treeRouterList->findRouterById(hop.mid(1));
00507       if (item) {
00508         rd = item->descriptor();
00509       } else {
00510         /* A router with the current hop ID does not exist in our router list,
00511          * so ask Tor for its descriptor */
00512         rd = _torControl->getDescriptorById(hop);
00513         if (!rd.isEmpty()) {
00514           /* This is a fine time to add this new router to our list */
00515           addRouter(rd);
00516         }
00517       }
00518       hop = (rd.isEmpty() ? hop : rd.name());
00519     }
00520     names << hop;
00521   }
00522   return Circuit(circ.id(), circ.status(), names);
00523 }
00524 

Generated on Wed Sep 5 15:49:27 2007 for Vidalia by  doxygen 1.5.3