/** * internal function to use the tor-resolve-functionality * * @param query a hostname to be resolved, or for a reverse lookup: A.B.C.D.in-addr.arpa * @return either an InetAddress (normal query), or a String (reverse-DNS-lookup) */ private Object resolve_internal(String query) throws IOException { try { // check, if tor is still in startup-phase checkStartup(); // try to resolv query over all existing circuits // so iterate over all TLS-Connections Iterator<String> i = fnh.tls.keySet().iterator(); while (i.hasNext()) { TLSConnection tls = fnh.tls.get(i.next()); // and over all circuits in each TLS-Connection Iterator<Integer> i2 = tls.circuits.keySet().iterator(); while (i2.hasNext()) { Circuit circuit = (Circuit) tls.circuits.get(i2.next()); try { if (circuit.established) { // if an answer is given, we're satisfied ResolveStream rs = new ResolveStream(circuit); Object o = rs.resolve(query); rs.close(); return o; } ; } catch (Exception e) { // in case of error, do nothing, but retry with the next // circuit } } } // if no circuit could give an answer (possibly there was no // established circuit?) // build a new circuit and ask this one to resolve the query ResolveStream rs = new ResolveStream(new Circuit(this, fnh, dir, new TCPStreamProperties())); Object o = rs.resolve(query); rs.close(); return o; } catch (TorException e) { throw new IOException("Error in Tor: " + e.getMessage()); } catch (InterruptedException e) { throw new IOException("Error in Tor: " + e.getMessage()); } }
/** * makes a connection to a hidden service * * @param sp hostname, port to connect to and other stuff * @return some socket-thing */ private TCPStream connectToHidden(TCPStreamProperties spo) throws IOException { // check, if tor is still in startup-phase checkStartup(); Circuit myRendezvousCirc = null; Server hiddenServer; Node hiddenNode = null; // String address, x, y; String z; // Iterator it, i, i2; byte[] cookie = new byte[20]; // boolean notFound; int j; z = (String) Encoding.parseHiddenAddress(spo.hostname).get("z"); // Do we already have a connection to this address? if (hiddenServiceCircuits.containsKey(z)) { // TODO assess suitability of this circuit System.out.println("Reusing existing circuit"); TCPStreamProperties tcpProps = new TCPStreamProperties("", spo.port); try { return new TCPStream(hiddenServiceCircuits.get(z), tcpProps); } catch (TorNoAnswerException e) { // Create a new circuit instead e.printStackTrace(); } catch (TorException e) { // Create a new circuit instead e.printStackTrace(); } } // get a copy from the service descriptor (either local cache or // retrieve form network) ServiceDescriptor sd = (ServiceDescriptor) cachedServiceDescriptors.get(z); if (sd == null || (!sd.checkTimeStampValidity())) { sd = ServiceDescriptor.loadFromDirectory(z, this); cachedServiceDescriptors.put(z, sd); // cache it } boolean establishedRendezvous = false; j = 0; // attempts counted // spo.connect_retries try to establish rendezvous while ((j < spo.connect_retries) && (!establishedRendezvous)) { j++; try { myRendezvousCirc = fnh.provideSuitableNewCircuit(new TCPStreamProperties()); String rendezvousName = myRendezvousCirc.route[myRendezvousCirc.route_established - 1].server.nickname; Logger.logGeneral( Logger.INFO, "Tor.connectToHidden: establishing rendezvous point for " + z + " at " + rendezvousName); Random rnd = new Random(); rnd.nextBytes(cookie); myRendezvousCirc.send_cell(new CellRelayEstablishRendezvous(myRendezvousCirc, cookie)); myRendezvousCirc.streamHistory.add(spo.hostname); // wait for answer CellRelay rendezvousACK = myRendezvousCirc.queue.receiveRelayCell(CellRelay.CELL_RELAY_RENDEZVOUS_ESTABLISHED); if (rendezvousACK.length > 0) { throw new TorException("Tor.connectToHidden: Got NACK from RENDEZVOUS Point"); } myRendezvousCirc.sd = sd; hiddenServer = new Server(this, sd.publicKey); hiddenNode = new Node(hiddenServer); // between HS and // Rendezvous point establishedRendezvous = true; } catch (IOException e) { e.printStackTrace(); } catch (TorException e) { e.printStackTrace(); } } if (!establishedRendezvous) { Logger.logGeneral( Logger.WARNING, "Tor.connectToHidden: coudn't establishing rendezvous point for " + z); throw new IOException("Tor.connectToHidden: coudn't establishing rendezvous point for " + z); } Iterator<IntroductionPoint> it3 = sd.introPoints.iterator(); String rendezvousName = myRendezvousCirc.route[myRendezvousCirc.route.length - 1].server.nickname; while (it3.hasNext()) { IntroductionPoint iPoint = (IntroductionPoint) it3.next(); Logger.logGeneral( Logger.INFO, "Tor.connectToHidden: contacting introduction point " + iPoint.getNickname() + " for " + z); // introduce rendezvous to the node TCPStreamProperties spIntro = new TCPStreamProperties(); spIntro.exitPolicyRequired = false; // Server sr = iPoint.getSrv(); spIntro.setCustomExitpoint(iPoint.getNickname()); try { // make new circ where the last node is intro point Circuit myIntroCirc = new Circuit(this, fnh, dir, spIntro); // System.out.println(" LAST NODE IS... " + // myIntroCirc.route[myIntroCirc.route.length-1].server.name); // and CellIntro1 data encrypted with PK of Hidden Service, and _not_ of the introPoint myIntroCirc.send_cell( new CellRelayIntroduce1(myIntroCirc, cookie, sd, rendezvousName, hiddenNode)); // wait for ack CellRelay introACK = myIntroCirc.queue.receiveRelayCell(CellRelay.CELL_RELAY_COMMAND_INTRODUCE_ACK); if (introACK.length > 0) throw new TorException("Tor.connectToHidden: Got NACK from Introduction Point"); // introduce ACK is received Logger.logGeneral(Logger.RAW_DATA, "Tor.connectToHidden: Got ACK from Intro Point"); myIntroCirc.close(true); // wait for answer from the hidden service (RENDEZVOUS2) int oldTimeout = myRendezvousCirc.queue.timeout; if (oldTimeout < 120 * 1000) myRendezvousCirc.queue.timeout = 120 * 1000; CellRelay r2Relay = myRendezvousCirc.queue.receiveRelayCell(CellRelay.CELL_RELAY_RENDEZVOUS2); myRendezvousCirc.queue.timeout = oldTimeout; // finish diffie-hellman byte[] dh_gy = new byte[148]; System.arraycopy(r2Relay.data, 0, dh_gy, 0, 148); hiddenNode.finish_dh(dh_gy); myRendezvousCirc.addNode(hiddenNode); Logger.logGeneral( Logger.INFO, "Tor.connectToHidden: succesfully established rendezvous with " + z); // address in begin cell set to ""; TCPStreamProperties tcpProps = new TCPStreamProperties("", spo.port); if (hiddenServiceCircuits.containsKey(z)) hiddenServiceCircuits.remove(z); hiddenServiceCircuits.put(z, myRendezvousCirc); // connect return new TCPStream(myRendezvousCirc, tcpProps); } catch (TorException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } // all intros failed... // perhaps there is something wrong with out cached service descriptor cachedServiceDescriptors.remove(z); throw new IOException("Tor.connectToHidden: couldn't connect to an introduction point of " + z); }
/** * makes a connection to a remote service * * @param sp hostname, port to connect to and other stuff * @return some socket-thing */ public TCPStream connect(TCPStreamProperties sp) throws IOException, TorResolveFailedException { // Tor.log.logGeneral(Logger.VERBOSE, "Tor: Trying to connect to " + sp.hostname); if (sp.hostname == null) throw new IOException("Tor: no hostname is provided"); // check, if tor is still in startup-phase checkStartup(); // check whether the address is hidden if (sp.hostname.endsWith(".onion")) return connectToHidden(sp); boolean resolveException = false; Circuit[] cs = fnh.provideSuitableCircuits(sp, false); if (TorConfig.veryAggressiveStreamBuilding) { for (int j = 0; j < cs.length; ++j) { // start N asynchronous stream building threads try { StreamThread[] streamThreads = new StreamThread[cs.length]; for (int i = 0; i < cs.length; ++i) streamThreads[i] = new StreamThread(cs[i], sp); // wait for the first stream to return int chosenStream = -1; int waitingCounter = TorConfig.queueTimeoutStreamBuildup * 1000 / 10; while ((chosenStream < 0) && (waitingCounter >= 0)) { boolean atLeastOneAlive = false; for (int i = 0; (i < cs.length) && (chosenStream < 0); ++i) if (!streamThreads[i].isAlive()) { if ((streamThreads[i].stream != null) && (streamThreads[i].stream.established)) { chosenStream = i; } } else { atLeastOneAlive = true; } if (!atLeastOneAlive) break; Common.sleep(10); --waitingCounter; } // return one and close others if (chosenStream >= 0) { TCPStream returnValue = streamThreads[chosenStream].stream; new ClosingThread(streamThreads, chosenStream); return returnValue; } } catch (Exception e) { Logger.logStream(Logger.WARNING, "Tor.connect(): " + e.getMessage()); return null; } } } else { // build serial N streams, stop if successful for (int i = 0; i < cs.length; ++i) { try { return new TCPStream(cs[i], sp); } catch (TorResolveFailedException e) { Logger.logStream(Logger.WARNING, "Tor.connect: Resolve failed:" + e.getMessage()); resolveException = true; } catch (TorNoAnswerException e) { Logger.logStream(Logger.WARNING, "Tor.connect: Timeout on circuit:" + e.getMessage()); } catch (TorException e) { Logger.logStream( Logger.WARNING, "Tor.connect: TorException trying to reuse existing circuit:" + e.getMessage()); } catch (IOException e) { Logger.logStream(Logger.WARNING, "Tor.connect: IOException " + e.getMessage()); } } } if (resolveException) throw new TorResolveFailedException(); throw new IOException( "Tor.connect: unable to connect to " + sp.hostname + ":" + sp.port + " after " + sp.connect_retries + " retries"); }