Index: trunk/extensions/TorBlock/TorBlock.php |
— | — | @@ -36,7 +36,7 @@ |
37 | 37 | * Permission keys that bypass Tor blocks. |
38 | 38 | * Array of permission keys. |
39 | 39 | */ |
40 | | -$wgTorBypassPermissions = array( /*'autoconfirmed', 'proxyunbannable'*/ ); |
| 40 | +$wgTorBypassPermissions = array( 'user', /*'autoconfirmed', 'proxyunbannable'*/ ); |
41 | 41 | |
42 | 42 | /** |
43 | 43 | * Whether to load Tor blocks if they aren't stored in memcached. |
Index: trunk/extensions/TorBlock/loadExitNodes.php |
— | — | @@ -0,0 +1,10 @@ |
| 2 | +<?php |
| 3 | +/* |
| 4 | + * Updates the tor exit node list in |
| 5 | + */ |
| 6 | + |
| 7 | +require_once ( getenv('MW_INSTALL_PATH') !== false |
| 8 | + ? getenv('MW_INSTALL_PATH')."/maintenance/commandLine.inc" |
| 9 | + : dirname( __FILE__ ) . '/../../maintenance/commandLine.inc' ); |
| 10 | + |
| 11 | +TorBlock::loadExitNodes(); |
\ No newline at end of file |
Property changes on: trunk/extensions/TorBlock/loadExitNodes.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 12 | + native |
Index: trunk/extensions/TorBlock/TorBlock.class.php |
— | — | @@ -4,18 +4,18 @@ |
5 | 5 | |
6 | 6 | class TorBlock { |
7 | 7 | public static $mExitNodes; |
8 | | - |
| 8 | + |
9 | 9 | public static function onGetUserPermissionsErrorsExpensive( &$title, &$user, &$action, &$result ) { |
10 | 10 | global $wgTorAllowedActions; |
11 | 11 | if (in_array( $action, $wgTorAllowedActions)) { |
12 | 12 | return true; |
13 | 13 | } |
14 | | - |
| 14 | + |
15 | 15 | wfDebug( "Checking Tor status\n" ); |
16 | | - |
| 16 | + |
17 | 17 | if (self::isExitNode()) { |
18 | 18 | wfDebug( "-User detected as editing through tor.\n" ); |
19 | | - |
| 19 | + |
20 | 20 | global $wgTorBypassPermissions; |
21 | 21 | foreach( $wgTorBypassPermissions as $perm) { |
22 | 22 | if ($user->isAllowed( $perm )) { |
— | — | @@ -23,21 +23,20 @@ |
24 | 24 | return true; |
25 | 25 | } |
26 | 26 | } |
27 | | - |
| 27 | + |
28 | 28 | $ip = wfGetIp(); |
29 | 29 | wfDebug( "-User detected as editing from Tor node. Adding Tor block to permissions errors\n" ); |
30 | 30 | wfLoadExtensionMessages( 'TorBlock' ); |
31 | | - |
| 31 | + |
32 | 32 | $result[] = array('torblock-blocked', $ip); |
33 | | - |
| 33 | + |
34 | 34 | return false; |
35 | 35 | } |
36 | | - |
| 36 | + |
37 | 37 | return true; |
38 | 38 | } |
39 | 39 | |
40 | 40 | public static function getExitNodes() { |
41 | | - |
42 | 41 | if (is_array(self::$mExitNodes)) { |
43 | 42 | wfDebug( "Loading Tor exit node list from memory.\n" ); |
44 | 43 | return self::$mExitNodes; |
— | — | @@ -51,9 +50,12 @@ |
52 | 51 | wfDebug( "Loading Tor exit node list from memcached.\n" ); |
53 | 52 | // Lucky. |
54 | 53 | return self::$mExitNodes = $nodes; |
| 54 | + } elseif ($nodes == 'loading') { |
| 55 | + // Somebody else is loading it. |
| 56 | + return array(); |
55 | 57 | } |
56 | 58 | |
57 | | - // We have to actually load them. |
| 59 | + // We have to actually load from the server. |
58 | 60 | |
59 | 61 | if (!$wgTorLoadNodes) { |
60 | 62 | // Disabled. |
— | — | @@ -62,82 +64,93 @@ |
63 | 65 | } |
64 | 66 | |
65 | 67 | wfDebug( "Loading Tor exit node list cold.\n" ); |
66 | | - |
67 | | - return self::$mExitNodes = self::loadExitNodes(); |
| 68 | + |
| 69 | + return self::loadExitNodes(); |
68 | 70 | } |
69 | | - |
| 71 | + |
70 | 72 | public static function loadExitNodes() { |
71 | | - $nodes = array(); |
| 73 | + wfProfileIn( __METHOD__ ); |
| 74 | + |
| 75 | + global $wgTorIPs, $wgMemc; |
| 76 | + |
| 77 | + // Set loading key, to prevent DoS of server. |
72 | 78 | |
73 | | - global $wgTorIPs; |
| 79 | + $wgMemc->set( 'mw-tor-exit-nodes', 'loading', 300 ); |
74 | 80 | |
| 81 | + $nodes = array(); |
75 | 82 | foreach( $wgTorIPs as $ip ) { |
76 | | - $nodes = array_unique( array_merge( $nodes, self::getExitList( $ip ) ) ); |
| 83 | + $nodes = array_unique( array_merge( $nodes, self::loadNodesForIP( $ip ) ) ); |
77 | 84 | } |
78 | | - |
79 | | - global $wgMemc; |
80 | | - |
| 85 | + |
| 86 | + // Save to cache. |
81 | 87 | $wgMemc->set( 'mw-tor-exit-nodes', $nodes, 1800 ); // Store for half an hour. |
82 | | - |
83 | | - return $nodes; |
| 88 | + |
| 89 | + wfProfileOut( __METHOD__ ); |
| 90 | + |
| 91 | + return self::$mExitNodes = $nodes; |
84 | 92 | } |
85 | | - |
86 | | - public static function getExitList( $ip ) { |
| 93 | + |
| 94 | + public static function loadNodesForIP( $ip ) { |
87 | 95 | $url = 'https://check.torproject.org/cgi-bin/TorBulkExitList.py?ip='.$ip; |
88 | | - |
89 | 96 | $data = Http::get( $url ); |
90 | | - |
91 | | - $lines = split("\n", $data); |
92 | | - |
| 97 | + $lines = explode("\n", $data); |
| 98 | + |
93 | 99 | $nodes = array(); |
94 | | - |
95 | 100 | foreach( $lines as $line ) { |
96 | 101 | if (strpos( $line, '#' )===false) { |
97 | 102 | $nodes[] = trim($line); |
98 | 103 | } |
99 | 104 | } |
100 | | - |
| 105 | + |
101 | 106 | return $nodes; |
102 | 107 | } |
103 | | - |
| 108 | + |
104 | 109 | public static function isExitNode($ip = null) { |
105 | 110 | #return true; ## FOR DEBUGGING |
106 | 111 | if ($ip == null) { |
107 | 112 | $ip = wfGetIp(); |
108 | 113 | } |
109 | | - |
| 114 | + |
110 | 115 | $nodes = self::getExitNodes(); |
111 | | - |
| 116 | + |
112 | 117 | return in_array( $ip, $nodes ); |
113 | 118 | } |
114 | | - |
| 119 | + |
115 | 120 | public static function onGetBlockedStatus( &$user ) { |
116 | 121 | if (self::isExitNode() && $user->mBlock && !$user->mBlock->mUser) { |
117 | 122 | wfDebug( "User using Tor node. Disabling IP block as it was probably targetted at the tor node." ); |
118 | 123 | // Node is probably blocked for being a Tor node. Remove block. |
119 | 124 | $user->mBlockedBy = 0; |
120 | 125 | } |
121 | | - |
| 126 | + |
122 | 127 | return true; |
123 | 128 | } |
124 | | - |
| 129 | + |
125 | 130 | public static function onAbortAutoblock( $autoblockip, &$block ) { |
126 | 131 | return !self::isExitNode( $autoblockip ); |
127 | 132 | } |
128 | | - |
| 133 | + |
129 | 134 | public static function onGetAutoPromoteGroups( $user, &$promote ) { |
130 | | - if (self::isExitNode()) { |
131 | | - // Check against stricter requirements. |
132 | | - $age = time() - wfTimestampOrNull( TS_UNIX, $user->getRegistration() ); |
133 | | - |
134 | | - global $wgTorAutoConfirmAge, $wgTorAutoConfirmCount; |
135 | | - |
136 | | - if ($age < $wgTorAutoConfirmAge || $user->getEditCount() < $wgTorAutoConfirmCount) { |
137 | | - // No! |
138 | | - $promote = array(); |
139 | | - } |
| 135 | + // Check against stricter requirements for tor nodes. |
| 136 | + // Counterintuitively, we do the requirement checks first. |
| 137 | + // This is so that we don't have to hit memcached to get the |
| 138 | + // exit list, unnecessarily. |
| 139 | + |
| 140 | + if (!count($promote)) { |
| 141 | + return true; // No groups to promote to anyway |
140 | 142 | } |
141 | | - |
| 143 | + |
| 144 | + $age = time() - wfTimestampOrNull( TS_UNIX, $user->getRegistration() ); |
| 145 | + global $wgTorAutoConfirmAge, $wgTorAutoConfirmCount; |
| 146 | + |
| 147 | + if ($age >= $wgTorAutoConfirmAge && $user->getEditCount() >= $wgTorAutoConfirmCount) { |
| 148 | + return true; // Does match requirements. Don't bother checking if we're an exit node. |
| 149 | + } |
| 150 | + |
| 151 | + if (self::isExitNode()) { // Tor user, doesn't match the expanded requirements. |
| 152 | + $promote = array(); |
| 153 | + } |
| 154 | + |
142 | 155 | return true; |
143 | 156 | } |
144 | | -} |
| 157 | +} |
\ No newline at end of file |