Installation:
composer require dunglas/torcontrol-bundle
Register the bundle in config/bundles.php:
return [
// ...
Dunglas\TorControlBundle\DunglasTorControlBundle::class => ['all' => true],
];
Basic Configuration (config/packages/dunglas_tor_control.yaml):
dunglas_tor_control:
hostname: "127.0.0.1" # Default: localhost
port: 9051 # Default: 9051
authmethod: "null" # Default: autodetect
First Use Case:
Inject the TorControl service into a controller or command to manage Tor:
use Dunglas\TorControlBundle\Service\TorControl;
class MyController extends AbstractController
{
public function __construct(private TorControl $torControl)
{}
public function index()
{
$this->torControl->getVersion(); // Example: Fetch Tor version
// ...
}
}
Dynamic Tor Configuration: Use the service to programmatically adjust Tor settings (e.g., reload config after changes):
$this->torControl->setConf("SocksPort", "9150");
$this->torControl->signal("HUP"); // Reload Tor config
Circuit Management: Create/extend circuits for hidden services or testing:
$circuit = $this->torControl->newCircuit();
$circuit->build(3); // Build with 3 hops
$circuit->extend(); // Extend the circuit
Hidden Service Management:
Generate/manage .onion addresses:
$hsdir = sys_get_temp_dir() . '/tor_hs';
$this->torControl->setConf("HiddenServiceDir", $hsdir);
$this->torControl->setConf("HiddenServicePort", "80 127.0.0.1:8080");
$this->torControl->signal("HUP");
$address = file_get_contents($hsdir . "/hostname"); // Fetch .onion address
Event-Driven Actions: Listen for Tor events (e.g., circuit status changes) via Symfony events:
# config/services.yaml
services:
App\EventListener\TorListener:
tags:
- { name: kernel.event_listener, event: tor.control.event, method: onTorEvent }
Command-Line Integration: Use the service in console commands for admin tasks:
namespace App\Command;
use Dunglas\TorControlBundle\Service\TorControl;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class TorStatusCommand extends Command
{
protected static $defaultName = 'app:tor:status';
public function __construct(private TorControl $torControl)
{}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$output->writeln('Tor Version: ' . $this->torControl->getVersion());
return Command::SUCCESS;
}
}
Dependency Injection:
Prefer constructor injection for TorControl to ensure testability:
public function __construct(private TorControl $torControl) {}
Error Handling:
Wrap Tor operations in try-catch blocks (e.g., TorControlException):
try {
$this->torControl->getInfo("traffic/read");
} catch (\Exception $e) {
$this->logger->error("Tor error: {$e->getMessage()}");
}
Configuration Validation: Use Symfony’s validator to validate Tor config before applying:
# config/validator/constraints/TorConfig.yaml
Dunglas\TorControlBundle\Validator\Constraints\TorConfig:
hostname: "127.0.0.1"
port: 9051
Environment-Specific Configs:
Override config per environment (e.g., config/packages/dev/dunglas_tor_control.yaml):
dunglas_tor_control:
authmethod: "password"
password: "%env(TOR_PASSWORD)%"
Logging:
Enable debug logging for Tor events in config/packages/monolog.yaml:
handlers:
tor:
type: stream
path: "%kernel.logs_dir%/tor.log"
level: debug
channels: ["tor"]
Authentication Issues:
authmethod is misconfigured, TorControl will fail silently. Use null for no auth or explicitly set password/cookie.control.authenticate setting in torrc matches the bundle config.Port Conflicts:
9051 may be blocked or in use. Ensure Tor’s ControlPort in torrc matches the bundle’s port./var/log/tor/log) for connection errors.Deprecated Methods:
getConf()) may return null if Tor’s config is not fully loaded. Always validate responses.getInfo() for runtime values instead of getConf() for static config.Race Conditions:
signal("HUP") may take time to propagate. Add retries or delays:$this->torControl->signal("HUP");
sleep(2); // Wait for reload
Hidden Service Delays:
.onion addresses may take minutes to generate. Poll the hostname file with a timeout:$timeout = time() + 60;
while (time() < $timeout) {
if (file_exists($hsdir . "/hostname")) break;
sleep(5);
}
Tor Logs:
Enable verbose logging in torrc:
Log notice stdout
ControlLog stdout
Bundle Debugging:
Enable Symfony’s profiler to inspect TorControl service calls:
// config/packages/dev/debug.yaml
framework:
profiler: { only_exceptions: false }
Connection Testing: Manually test TorControl connection:
telnet 127.0.0.1 9051
AUTHENTICATE "password"
GETINFO version
Custom Commands: Extend the bundle by adding new TorControl commands:
namespace App\Command;
use Dunglas\TorControlBundle\Service\TorControl;
use Symfony\Component\Console\Command\Command;
class TorCircuitCommand extends Command
{
protected static $defaultName = 'app:tor:circuit';
public function __construct(private TorControl $torControl)
{}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$circuit = $this->torControl->newCircuit();
$circuit->build(4);
$output->writeln("Circuit ID: {$circuit->getId()}");
return Command::SUCCESS;
}
}
Event Listeners: Subscribe to Tor events (e.g., circuit status changes):
use Dunglas\TorControlBundle\Event\TorEvent;
class TorListener
{
public function onTorEvent(TorEvent $event)
{
if ($event->getType() === 'CIRC') {
$this->logger->info("Circuit event: {$event->getData()}");
}
}
}
Configuration Overrides: Dynamically override config at runtime:
$container->getParameterBag()->set('dunglas_tor_control.hostname', 'new-host');
Testing:
Mock TorControl in tests using Symfony’s TestContainer:
use Dunglas\TorControlBundle\Service\TorControl;
$container->register('tor_control.test', TorControl::class)
->addArgument($this->createMock(TorControl::class));
$this->torControl->getConnection()->send("GETINFO version");
$this->torControl->getConnection()->send("GETINFO traffic");
$responses = $this->torControl->getConnection()->
How can I help you explore Laravel packages today?