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 699 2006-04-15 03:12:22Z hipplej $
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 /** Maximum time to proceess other events while loading the long list of
00039  * router descriptors. */
00040 #define MAX_EVENTS_TIMEOUT  25
00041 /** Number of milliseconds to wait after the arrival of the last descriptor whose
00042  * IP needs to be resolved to geographic information, in case more descriptors
00043  * arrive. Then we can simply lump the IPs into a single request. */
00044 #define RESOLVE_QUEUE_DELAY   3000
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   ui.actionClose->setShortcut(QString("Ctrl+W"));
00058 #endif
00059 
00060   /* Get the TorControl object */
00061   _torControl = Vidalia::torControl();
00062   _torControl->setEvent(TorEvents::NewDescriptor, this, true);
00063   _torControl->setEvent(TorEvents::CircuitStatus, this, true);
00064   _torControl->setEvent(TorEvents::StreamStatus,  this, true);
00065   
00066   /* Change the column widths of the tree widgets */
00067   ui.treeRouterList->header()->
00068     resizeSection(RouterListWidget::StatusColumn,
00069                   RouterListWidget::StatusColumnWidth);
00070   ui.treeCircuitList->header()->
00071     resizeSection(CircuitListWidget::ConnectionColumn,
00072                   CircuitListWidget::ConnectionColumnWidth);
00073 
00074   /* Create the TorMapWidget and add it to the dialog */
00075   _map = new TorMapWidget();
00076   ui.gridLayout->addWidget(_map);
00077 
00078   /* Connect zoom buttons to ZImageView zoom slots */
00079   connect(ui.actionZoomIn, SIGNAL(triggered()), _map, SLOT(zoomIn()));
00080   connect(ui.actionZoomOut, SIGNAL(triggered()), _map, SLOT(zoomOut()));
00081   connect(ui.actionZoomToFit, SIGNAL(triggered()), _map, SLOT(zoomToFit()));
00082 
00083   /* Create the timer that will be used to update the router list once every
00084    * hour. We still receive the NEWDESC event to get new descriptors, but this
00085    * needs to be called to get rid of any descriptors that were removed. */
00086   _refreshTimer.setInterval(60*60*1000);
00087   connect(&_refreshTimer, SIGNAL(timeout()), this, SLOT(refresh()));
00088   
00089   /* Set up the timer used to group together GeoIP requests for new
00090    * descriptors arriving within RESOLVE_QUEUE_DELAY milliseconds of each
00091    * other. */
00092   _resolveQueueTimer.setSingleShot(true);
00093   connect(&_resolveQueueTimer, SIGNAL(timeout()), this, SLOT(resolve()));
00094 
00095   /* Connect the necessary slots and signals */
00096   connect(ui.actionHelp, SIGNAL(triggered()), this, SLOT(help()));
00097   connect(ui.actionRefresh, SIGNAL(triggered()), this, SLOT(refresh()));
00098   connect(ui.treeRouterList, SIGNAL(routerSelected(RouterDescriptor)),
00099           this, SLOT(routerSelected(RouterDescriptor)));
00100   connect(ui.treeCircuitList, SIGNAL(circuitSelected(Circuit)),
00101           this, SLOT(circuitSelected(Circuit)));
00102   connect(ui.treeCircuitList, SIGNAL(circuitRemoved(quint64)),
00103           _map, SLOT(removeCircuit(quint64)));
00104 
00105   /* Respond to changes in the status of the control connection */
00106   connect(_torControl, SIGNAL(connected(bool)), ui.actionRefresh, SLOT(setEnabled(bool)));
00107   connect(_torControl, SIGNAL(connected()), this, SLOT(gotConnected()));
00108   connect(_torControl, SIGNAL(disconnected()), this, SLOT(gotDisconnected())); 
00109 
00110   /* Connect the slot to find out when geoip information has arrived */
00111   connect(&_geoip, SIGNAL(resolved(int, QList<GeoIp>)), 
00112              this,   SLOT(resolved(int, QList<GeoIp>)));
00113 }
00114 
00115 /** Clears map, lists and stops timer when we get disconnected */
00116 void
00117 NetViewer::gotDisconnected()
00118 {
00119   clear();
00120   _refreshTimer.stop();
00121 }
00122 
00123 /** Loads data into map, lists and starts timer when we get connected*/
00124 void
00125 NetViewer::gotConnected()
00126 {
00127   refresh();
00128   _refreshTimer.start();
00129 }
00130 
00131 /** Custom event handler. Catches the new descriptor events. */
00132 void
00133 NetViewer::customEvent(QEvent *event)
00134 {
00135   int type = event->type();
00136   
00137   if (type == CustomEventType::NewDescriptorEvent) {
00138     /* New router descriptor, so load it and add it to the list */
00139     NewDescriptorEvent *nde = (NewDescriptorEvent *)event;
00140     loadDescriptors(nde->descriptorIDs());
00141   
00142   } else if (type == CustomEventType::CircuitEvent) {
00143     /* New or updated circuit information */
00144     CircuitEvent *ce = (CircuitEvent *)event;
00145     addCircuit(ce->circuit());
00146   
00147   } else if (type == CustomEventType::StreamEvent) {
00148     /* New or updated stream information */
00149     StreamEvent *se = (StreamEvent *)event;
00150     ui.treeCircuitList->addStream(se->stream());
00151   }
00152 
00153   /** Update the map */
00154   _map->update();
00155 }
00156 
00157 /** Reloads the lists of routers, circuits that Tor knows about */
00158 void
00159 NetViewer::refresh()
00160 {
00161   /* Don't let the user refresh while we're refreshing. */
00162   ui.actionRefresh->setEnabled(false);
00163 
00164   /* Clear the data */
00165   clear();
00166 
00167   /* Load router information */
00168   loadDescriptors(_torControl->getRouterIDList());
00169 
00170   /* Load Circuits and Streams information */
00171   loadConnections();
00172 
00173   /* Ok, they can refresh again. */
00174   ui.actionRefresh->setEnabled(true);
00175 } 
00176 
00177 /** Clears the lists and the map */
00178 void
00179 NetViewer::clear()
00180 {
00181   /* Clear the resolve queue and map */
00182   _resolveMap.clear();
00183   _resolveQueue.clear();
00184   /* Clear the network map */
00185   _map->clear();
00186   _map->update();
00187   /* Clear the lists of routers, circuits, and streams */
00188   ui.treeRouterList->clearRouters();
00189   ui.treeCircuitList->clearCircuits();
00190   ui.textRouterInfo->clear();
00191 }
00192 
00193 /** Loads a list of all current circuits and streams. */
00194 void
00195 NetViewer::loadConnections()
00196 {
00197   /* Load all circuits */
00198   QList<Circuit> circuits = _torControl->getCircuits();
00199   foreach (Circuit circuit, circuits) {
00200     addCircuit(circuit);
00201   }
00202   /* Now load all streams */
00203   QList<Stream> streams = _torControl->getStreams();
00204   foreach (Stream stream, streams) {
00205     ui.treeCircuitList->addStream(stream);
00206   }
00207 
00208   /** Update the map */
00209   _map->update();
00210 }
00211 
00212 /** Adds a circuit to the map and the list */
00213 void
00214 NetViewer::addCircuit(Circuit circuit)
00215 {
00216   /* Circuits displayed to the user always use nicknames */
00217   Circuit circNames = circuitPathNames(circuit);
00218   /* Circuits on the map always use keyids */
00219   Circuit circIds   = circuitPathIDs(circuit);
00220 
00221   ui.treeCircuitList->addCircuit(circNames);
00222   _map->addCircuit(circuit.id(), circIds.hops());
00223 }
00224 
00225 /** Called when the user selects the "Help" action from the toolbar. */
00226 void
00227 NetViewer::help()
00228 {
00229   Vidalia::help("netview");
00230 }
00231 
00232 /** Loads a list of new descriptors from the given IDs. */
00233 void
00234 NetViewer::loadDescriptors(QStringList ids)
00235 {
00236   /* Get descriptors for all the given IDs */
00237   QList<RouterDescriptor> rds = _torControl->getDescriptorListById(ids);
00238    
00239   foreach (RouterDescriptor rd, rds) {
00240     /* Load the router descriptor and add it to the router list. */
00241     if (!rd.isEmpty()) {
00242       addRouter(rd);
00243     }
00244   }
00245 }
00246 
00247 /** Adds a router to our list of servers and retrieves geographic location
00248  * information for the server. */
00249 void
00250 NetViewer::addRouter(RouterDescriptor rd)
00251 {
00252   /* Add the descriptor to the list of server */
00253   ui.treeRouterList->addRouter(rd);
00254 
00255   /* Add this IP to a list whose geographic location we'd like to find. */
00256   QHostAddress ip(rd.ip());
00257   if (!_resolveQueue.contains(ip)) {
00258     _resolveQueue << ip;
00259   }
00260   if (!_resolveMap.values(rd.ip()).contains(rd.id())) {
00261     _resolveMap.insertMulti(rd.ip(), rd.id());
00262   }
00263 
00264   /* If there are any IPs in the list, then start a timer. We delay resolution
00265    * because it's likely that we'll receive new descriptors shortly, so we
00266    * might as well lump them into a single request. */
00267   if (!_resolveQueue.isEmpty()) {
00268     /* Wait RESOLVE_QUEUE_DELAY after the <i>last</i> item inserted into the
00269      * queue, before sending the resolve request. */
00270     _resolveQueueTimer.start(RESOLVE_QUEUE_DELAY);
00271   }
00272 }
00273 
00274 /** Called when the user selects a circuit from the circuit and streams
00275  * list. */
00276 void
00277 NetViewer::circuitSelected(Circuit circuit)
00278 {
00279   /* Clear any selected items. */
00280   ui.treeRouterList->deselectAll();
00281   ui.textRouterInfo->clear();
00282   _map->deselectAll();
00283 
00284   /* Select the items on the map and in the list */
00285   _map->selectCircuit(circuit.id());
00286 
00287   QList<RouterDescriptor> routers;
00288 
00289   foreach (QString router, circuit.hops()) {
00290     /* Try to find and select each router in the path */
00291     RouterListItem *item = ui.treeRouterList->findRouterByName(router);
00292     if (item) {
00293       routers.append(item->descriptor());
00294     }
00295   }
00296 
00297   ui.textRouterInfo->display(routers);
00298 }
00299 
00300 /** Called when the user selects a router from the router list. */
00301 void
00302 NetViewer::routerSelected(RouterDescriptor router)
00303 {
00304   _map->deselectAll();
00305   ui.textRouterInfo->clear();
00306   ui.textRouterInfo->display(router);
00307   _map->selectRouter(router.name());
00308 }
00309 
00310 /** If there are any IPs in the resolve queue, do the request now. */
00311 void
00312 NetViewer::resolve()
00313 {
00314   if (!_resolveQueue.isEmpty()) {
00315     _geoip.resolve(_resolveQueue);
00316     _resolveQueue.clear();
00317   }
00318 }
00319 
00320 /** Called when a list of GeoIp information has been resolved. */
00321 void
00322 NetViewer::resolved(int id, QList<GeoIp> geoips)
00323 {
00324   Q_UNUSED(id);
00325   QString ip;
00326   RouterListItem *router;
00327   
00328   foreach (GeoIp geoip, geoips) {
00329     /* Find all routers that are at this IP address */
00330     ip = geoip.ip().toString();
00331     QList<QString> ids = _resolveMap.values(ip);
00332 
00333     /* Update their geographic location information with the results of this
00334      * GeoIP query. */
00335     foreach (QString id, ids) {
00336       router = ui.treeRouterList->findRouterById(id);
00337       if (router) {
00338         /* Save the location information in the descriptor */
00339         router->setLocation(geoip.toLocation());
00340         /* Plot the router on the map */
00341         _map->addRouter(router->id(), geoip.latitude(), geoip.longitude());
00342       }
00343     }
00344     _resolveMap.remove(ip);
00345   }
00346 
00347   /* Update the circuit lines */
00348   foreach (Circuit circuit, ui.treeCircuitList->circuits()) {
00349     _map->addCircuit(circuit.id(), circuitPathIDs(circuit).hops());
00350   }
00351   
00352   /* Repaint the map */
00353   _map->update();
00354 }
00355 
00356 /** Convert all hops in <b>circ</b>'s path to server identities. <b>circ</b>'s
00357  * status and circuit ID will be preserved. If no ID can be found for a
00358  * router name, the name will be left in the circuit's path. */
00359 Circuit
00360 NetViewer::circuitPathIDs(Circuit circ)
00361 {
00362   QStringList path = circ.hops();
00363   QStringList ids;
00364   RouterDescriptor rd;
00365   quint32 torVersion;
00366 
00367   torVersion = _torControl->getTorVersion();
00368   foreach (QString hop, path) {
00369     if (!hop.startsWith("$")) {
00370       if (torVersion < 0x00010202) {
00371         /* Tor versions earlier than 0.1.2.2 have a bug where they will tell
00372          * you a server's nickname in a circuit, even if that server is
00373          * non-Named. If we requested the descriptor by name for a non-Named server
00374          * from Tor, the user's log could be filled with warnings. So, just
00375          * try to look it up from our own router list first. */
00376         RouterListItem *item = ui.treeRouterList->findRouterByName(hop);
00377         if (item) {
00378           rd = item->descriptor();
00379         } else {
00380           /* We don't know about the router, so try to get it by name even
00381            * though Tor will probably yell at us. We can't help it. */
00382           rd = _torControl->getDescriptorByName(hop);
00383           if (!rd.isEmpty()) {
00384             /* Add this router to the list of those we know about */
00385             addRouter(rd);
00386           }
00387         }
00388       } else {
00389         /* Ask Tor for the descriptor for the given router name */
00390         rd = _torControl->getDescriptorByName(hop);
00391       }
00392       hop = (rd.isEmpty() ? hop : rd.id());
00393     }
00394     ids << hop;
00395   }
00396   return Circuit(circ.id(), circ.status(), ids);
00397 }
00398 
00399 /** Convert all hops in <b>circ</b>'s path to server names. <b>circ</b>'s
00400  * status and circuit ID will be preserved. If no name can be found for a
00401  * server identity, the ID will be left in the circuit's path. */
00402 Circuit
00403 NetViewer::circuitPathNames(Circuit circ)
00404 {
00405   QStringList path = circ.hops();
00406   QStringList names;
00407   RouterDescriptor rd;
00408   
00409   foreach (QString hop, path) {
00410     if (hop.startsWith("$")) {
00411       /* Check if we already have a descriptor for this server identity. */
00412       RouterListItem *item =  ui.treeRouterList->findRouterById(hop.mid(1));
00413       if (item) {
00414         rd = item->descriptor();
00415       } else {
00416         /* A router with the current hop ID does not exist in our router list,
00417          * so ask Tor for its descriptor */
00418         rd = _torControl->getDescriptorById(hop);
00419         if (!rd.isEmpty()) {
00420           /* This is a fine time to add this new router to our list */
00421           addRouter(rd);
00422         }
00423       }
00424       hop = (rd.isEmpty() ? hop : rd.name());
00425     }
00426     names << hop;
00427   }
00428   return Circuit(circ.id(), circ.status(), names);
00429 }
00430 

Generated on Mon Oct 23 20:08:16 2006 for Vidalia by  doxygen 1.5.0