mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-28 06:13:31 +01:00
Merge remote-tracking branch 'public/prop171_v2'
This commit is contained in:
commit
195bcb6150
22
changes/prop171
Normal file
22
changes/prop171
Normal file
@ -0,0 +1,22 @@
|
||||
o Major features:
|
||||
- You can now configure Tor so that streams from different
|
||||
applications are isolated on different circuits, to prevent an
|
||||
attacker who sees your streams leaving an exit node from linking
|
||||
your sessions to one another. To do this, choose some way to
|
||||
distinguish the applications -- have them connect to different
|
||||
SocksPorts, or have one of them use SOCKS4 while the other uses
|
||||
SOCKS5, or have them pass different authentication strings to
|
||||
the SOCKS proxy. Then use the new SocksPort syntax to configure
|
||||
the degree of isolation you need. This implements Proposal 171.
|
||||
|
||||
o Minor features:
|
||||
- There's a new syntax for specifying multiple client ports (such as
|
||||
SOCKSPort, TransPort, DNSPort, NATDPort): you can now just declare
|
||||
multiple ...Port entries with full addr:port syntax on each.
|
||||
The old ...ListenAddress format is still supported, but you can't
|
||||
mix it with the new SOCKSPort syntax.
|
||||
|
||||
o Code simplifications and refactoring:
|
||||
- Rewrote the listener-selection logic so that parsing which ports
|
||||
we want to listen on is now separate form binding to the ports
|
||||
we want.
|
100
doc/tor.1.txt
100
doc/tor.1.txt
@ -464,7 +464,7 @@ CLIENT OPTIONS
|
||||
--------------
|
||||
|
||||
The following options are useful only for clients (that is, if
|
||||
**SocksPort** is non-zero):
|
||||
**SocksPort**, **TransPort**, **DNSPort**, or **NATDPort** is non-zero):
|
||||
|
||||
**AllowInvalidNodes** **entry**|**exit**|**middle**|**introduction**|**rendezvous**|**...**::
|
||||
If some Tor servers are obviously not working right, the directory
|
||||
@ -682,17 +682,49 @@ The following options are useful only for clients (that is, if
|
||||
the same circuit. Currently, two addresses are "too close" if they lie in
|
||||
the same /16 range. (Default: 1)
|
||||
|
||||
**SocksPort** __PORT__|**auto**::
|
||||
Advertise this port to listen for connections from Socks-speaking
|
||||
**SOCKSPort** \['address':]__port__|**auto** [_isolation flags_]::
|
||||
Open this port to listen for connections from SOCKS-speaking
|
||||
applications. Set this to 0 if you don't want to allow application
|
||||
connections via SOCKS. Set it to "auto" to have Tor pick a port for
|
||||
you. (Default: 9050)
|
||||
you. This directive can be specified multiple times to bind
|
||||
to multiple addresses/ports. (Default: 9050) +
|
||||
+
|
||||
The _isolation flags_ arguments give Tor rules for which streams
|
||||
received on this SOCKSPort are allowed to share circuits with one
|
||||
another. Recognized isolation flags are:
|
||||
**IsolateClientAddr**;;
|
||||
Don't share a circuits with streams from a different
|
||||
client address. (On by default and strongly recommended;
|
||||
you can disable it with **NoIsolateClientAddr**.)
|
||||
**IsolateSOCKSAuth**;;
|
||||
Don't share a circuits with streams for which different
|
||||
SOCKS authentication was provided. (On by default;
|
||||
you can disable it with **NoIsolateSOCKSAuth**.)
|
||||
**IsolateClientProtocol**;;
|
||||
Don't share circuits with streams using a different protocol.
|
||||
(SOCKS 4, SOCKS 5, TransPort connections, NATDPort connections,
|
||||
and DNSPort requests are all considered to be different protocols.)
|
||||
**IsolateDestPort**;;
|
||||
Don't share a circuits with streams targetting a different
|
||||
destination port.
|
||||
**IsolateDestAddr**;;
|
||||
Don't share a circuits with streams targetting a different
|
||||
destination address.
|
||||
**SessionGroup=**__INT__;;
|
||||
If no other isolation rules would prevent it, allow streams
|
||||
on this port to share circuits with streams from every other
|
||||
port with the same session group. (By default, streams received
|
||||
on different ports are always isolated from one another.)
|
||||
|
||||
**SocksListenAddress** __IP__[:__PORT__]::
|
||||
**SOCKSListenAddress** __IP__[:__PORT__]::
|
||||
Bind to this address to listen for connections from Socks-speaking
|
||||
applications. (Default: 127.0.0.1) You can also specify a port (e.g.
|
||||
192.168.0.1:9100). This directive can be specified multiple times to bind
|
||||
to multiple addresses/ports.
|
||||
to multiple addresses/ports. (DEPRECATED: As of 0.2.3.x-alpha, you can
|
||||
now use multiple SOCKSPort entries, and provide addresses for SOCKSPort
|
||||
entries, so SOCKSListenAddress no longer has a purpose. For backward
|
||||
compatibility, SOCKSListenAddress is only allowed when SOCKSPort is just
|
||||
a port number.)
|
||||
|
||||
**SocksPolicy** __policy__,__policy__,__...__::
|
||||
Set an entrance policy for this server, to limit who can connect to the
|
||||
@ -795,28 +827,44 @@ The following options are useful only for clients (that is, if
|
||||
operating as a relay, and it will never use the public key step if it
|
||||
doesn't yet know the onion key of the first hop. (Default: 1)
|
||||
|
||||
**TransPort** __PORT__|**auto**::
|
||||
If non-zero, enables transparent proxy support on __PORT__ (by convention,
|
||||
9040). Requires OS support for transparent proxies, such as BSDs' pf or
|
||||
**TransPort** \['address':]__port__|**auto** [_isolation flags_]::
|
||||
Open this port to listen for transparent proxy connections. Set this to
|
||||
0 if you don't want to allow transparent proxy connections. Set the port
|
||||
to "auto" to have Tor pick a port for you. This directive can be
|
||||
specified multiple times to bind to multiple addresses/ports. See
|
||||
SOCKSPort for an explanation of isolation flags. +
|
||||
+
|
||||
TransPort requires OS support for transparent proxies, such as BSDs' pf or
|
||||
Linux's IPTables. If you're planning to use Tor as a transparent proxy for
|
||||
a network, you'll want to examine and change VirtualAddrNetwork from the
|
||||
default setting. You'll also want to set the TransListenAddress option for
|
||||
the network you'd like to proxy. Set it to "auto" to have Tor pick a
|
||||
port for you. (Default: 0).
|
||||
the network you'd like to proxy. (Default: 0).
|
||||
|
||||
**TransListenAddress** __IP__[:__PORT__]::
|
||||
Bind to this address to listen for transparent proxy connections. (Default:
|
||||
127.0.0.1). This is useful for exporting a transparent proxy server to an
|
||||
entire network.
|
||||
entire network. (DEPRECATED: As of 0.2.3.x-alpha, you can
|
||||
now use multiple TransPort entries, and provide addresses for TransPort
|
||||
entries, so TransListenAddress no longer has a purpose. For backward
|
||||
compatibility, TransListenAddress is only allowed when TransPort is just
|
||||
a port number.)
|
||||
|
||||
**NATDPort** __PORT__|**auto**::
|
||||
Allow old versions of ipfw (as included in old versions of FreeBSD, etc.)
|
||||
to send connections through Tor using the NATD protocol. This option is
|
||||
only for people who cannot use TransPort. Set it to "auto" to have Tor
|
||||
pick a port for you. (Default: 0)
|
||||
**NATDPort** \['address':]__port__|**auto** [_isolation flags_]::
|
||||
Open this port to listen for connections from old versions of ipfw (as
|
||||
included in old versions of FreeBSD, etc) using the NATD protocol.
|
||||
Use 0 if you don't want to allow NATD connections. Set the port
|
||||
to "auto" to have Tor pick a port for you. This directive can be
|
||||
specified multiple times to bind to multiple addresses/ports. See
|
||||
SOCKSPort for an explanation of isolation flags. +
|
||||
+
|
||||
This option is only for people who cannot use TransPort. (Default: 0)
|
||||
|
||||
**NATDListenAddress** __IP__[:__PORT__]::
|
||||
Bind to this address to listen for NATD connections. (Default: 127.0.0.1).
|
||||
Bind to this address to listen for NATD connections. (DEPRECATED: As of
|
||||
0.2.3.x-alpha, you can now use multiple NATDPort entries, and provide
|
||||
addresses for NATDPort entries, so NATDListenAddress no longer has a
|
||||
purpose. For backward compatibility, NATDListenAddress is only allowed
|
||||
when NATDPort is just a port number.)
|
||||
|
||||
**AutomapHostsOnResolve** **0**|**1**::
|
||||
When this option is enabled, and we get a request to resolve an address
|
||||
@ -829,13 +877,19 @@ The following options are useful only for clients (that is, if
|
||||
A comma-separated list of suffixes to use with **AutomapHostsOnResolve**.
|
||||
The "." suffix is equivalent to "all addresses." (Default: .exit,.onion).
|
||||
|
||||
**DNSPort** __PORT__|**auto**::
|
||||
If non-zero, Tor listens for UDP DNS requests on this port and resolves
|
||||
them anonymously. Set it to "auto" to have Tor pick a port for
|
||||
you. (Default: 0).
|
||||
**DNSPort** \['address':]__port__|**auto** [_isolation flags_]::
|
||||
If non-zero, open this port to listen for UDP DNS requests, and resolve
|
||||
them anonymously. Set the port to "auto" to have Tor pick a port for
|
||||
you. This directive can be specified multiple times to bind to multiple
|
||||
addresses/ports. See SOCKSPort for an explanation of isolation
|
||||
flags. (Default: 0).
|
||||
|
||||
**DNSListenAddress** __IP__[:__PORT__]::
|
||||
Bind to this address to listen for DNS connections. (Default: 127.0.0.1).
|
||||
Bind to this address to listen for DNS connections. (DEPRECATED: As of
|
||||
0.2.3.x-alpha, you can now use multiple DNSPort entries, and provide
|
||||
addresses for DNSPort entries, so DNSListenAddress no longer has a
|
||||
purpose. For backward compatibility, DNSListenAddress is only allowed
|
||||
when DNSPort is just a port number.)
|
||||
|
||||
**ClientDNSRejectInternalAddresses** **0**|**1**::
|
||||
If true, Tor does not believe any anonymously retrieved DNS answer that
|
||||
|
@ -412,6 +412,32 @@ round_uint64_to_next_multiple_of(uint64_t number, uint64_t divisor)
|
||||
return number;
|
||||
}
|
||||
|
||||
/** Return the number of bits set in <b>v</b>. */
|
||||
int
|
||||
n_bits_set_u8(uint8_t v)
|
||||
{
|
||||
static const int nybble_table[] = {
|
||||
0, /* 0000 */
|
||||
1, /* 0001 */
|
||||
1, /* 0010 */
|
||||
2, /* 0011 */
|
||||
1, /* 0100 */
|
||||
2, /* 0101 */
|
||||
2, /* 0110 */
|
||||
3, /* 0111 */
|
||||
1, /* 1000 */
|
||||
2, /* 1001 */
|
||||
2, /* 1010 */
|
||||
3, /* 1011 */
|
||||
2, /* 1100 */
|
||||
3, /* 1101 */
|
||||
3, /* 1110 */
|
||||
4, /* 1111 */
|
||||
};
|
||||
|
||||
return nybble_table[v & 15] + nybble_table[v>>4];
|
||||
}
|
||||
|
||||
/* =====
|
||||
* String manipulation
|
||||
* ===== */
|
||||
@ -495,6 +521,23 @@ tor_strisnonupper(const char *s)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** As strcmp, except that either string may be NULL. The NULL string is
|
||||
* considered to be before any non-NULL string. */
|
||||
int
|
||||
strcmp_opt(const char *s1, const char *s2)
|
||||
{
|
||||
if (!s1) {
|
||||
if (!s2)
|
||||
return 0;
|
||||
else
|
||||
return -1;
|
||||
} else if (!s2) {
|
||||
return 1;
|
||||
} else {
|
||||
return strcmp(s1, s2);
|
||||
}
|
||||
}
|
||||
|
||||
/** Compares the first strlen(s2) characters of s1 with s2. Returns as for
|
||||
* strcmp.
|
||||
*/
|
||||
|
@ -160,6 +160,7 @@ uint64_t round_to_power_of_2(uint64_t u64);
|
||||
unsigned round_to_next_multiple_of(unsigned number, unsigned divisor);
|
||||
uint32_t round_uint32_to_next_multiple_of(uint32_t number, uint32_t divisor);
|
||||
uint64_t round_uint64_to_next_multiple_of(uint64_t number, uint64_t divisor);
|
||||
int n_bits_set_u8(uint8_t v);
|
||||
|
||||
/* Compute the CEIL of <b>a</b> divided by <b>b</b>, for nonnegative <b>a</b>
|
||||
* and positive <b>b</b>. Works on integer types only. Not defined if a+b can
|
||||
@ -174,6 +175,7 @@ void tor_strlower(char *s) ATTR_NONNULL((1));
|
||||
void tor_strupper(char *s) ATTR_NONNULL((1));
|
||||
int tor_strisprint(const char *s) ATTR_PURE ATTR_NONNULL((1));
|
||||
int tor_strisnonupper(const char *s) ATTR_PURE ATTR_NONNULL((1));
|
||||
int strcmp_opt(const char *s1, const char *s2) ATTR_PURE;
|
||||
int strcmpstart(const char *s1, const char *s2) ATTR_PURE ATTR_NONNULL((1,2));
|
||||
int strcmp_len(const char *s1, const char *s2, size_t len)
|
||||
ATTR_PURE ATTR_NONNULL((1,2));
|
||||
|
@ -550,6 +550,10 @@ circuit_free(circuit_t *circ)
|
||||
|
||||
crypto_free_pk_env(ocirc->intro_key);
|
||||
rend_data_free(ocirc->rend_data);
|
||||
|
||||
tor_free(ocirc->dest_address);
|
||||
tor_free(ocirc->socks_username);
|
||||
tor_free(ocirc->socks_password);
|
||||
} else {
|
||||
or_circuit_t *ocirc = TO_OR_CIRCUIT(circ);
|
||||
/* Remember cell statistics for this circuit before deallocating. */
|
||||
|
@ -39,19 +39,19 @@ static void circuit_increment_failure_count(void);
|
||||
* Else return 0.
|
||||
*/
|
||||
static int
|
||||
circuit_is_acceptable(circuit_t *circ, edge_connection_t *conn,
|
||||
circuit_is_acceptable(const origin_circuit_t *origin_circ,
|
||||
const edge_connection_t *conn,
|
||||
int must_be_open, uint8_t purpose,
|
||||
int need_uptime, int need_internal,
|
||||
time_t now)
|
||||
{
|
||||
const circuit_t *circ = TO_CIRCUIT(origin_circ);
|
||||
const node_t *exitnode;
|
||||
cpath_build_state_t *build_state;
|
||||
tor_assert(circ);
|
||||
tor_assert(conn);
|
||||
tor_assert(conn->socks_request);
|
||||
|
||||
if (!CIRCUIT_IS_ORIGIN(circ))
|
||||
return 0; /* this circ doesn't start at us */
|
||||
if (must_be_open && (circ->state != CIRCUIT_STATE_OPEN || !circ->n_conn))
|
||||
return 0; /* ignore non-open circs */
|
||||
if (circ->marked_for_close)
|
||||
@ -86,7 +86,7 @@ circuit_is_acceptable(circuit_t *circ, edge_connection_t *conn,
|
||||
* circuit, it's the magical extra bob hop. so just check the nickname
|
||||
* of the one we meant to finish at.
|
||||
*/
|
||||
build_state = TO_ORIGIN_CIRCUIT(circ)->build_state;
|
||||
build_state = origin_circ->build_state;
|
||||
exitnode = build_state_get_exit_node(build_state);
|
||||
|
||||
if (need_uptime && !build_state->need_uptime)
|
||||
@ -134,25 +134,37 @@ circuit_is_acceptable(circuit_t *circ, edge_connection_t *conn,
|
||||
return 0;
|
||||
}
|
||||
} else { /* not general */
|
||||
origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ);
|
||||
if ((conn->rend_data && !ocirc->rend_data) ||
|
||||
(!conn->rend_data && ocirc->rend_data) ||
|
||||
(conn->rend_data && ocirc->rend_data &&
|
||||
if ((conn->rend_data && !origin_circ->rend_data) ||
|
||||
(!conn->rend_data && origin_circ->rend_data) ||
|
||||
(conn->rend_data && origin_circ->rend_data &&
|
||||
rend_cmp_service_ids(conn->rend_data->onion_address,
|
||||
ocirc->rend_data->onion_address))) {
|
||||
origin_circ->rend_data->onion_address))) {
|
||||
/* this circ is not for this conn */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!connection_edge_compatible_with_circuit(conn, origin_circ)) {
|
||||
/* conn needs to be isolated from other conns that have already used
|
||||
* origin_circ */
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** Return 1 if circuit <b>a</b> is better than circuit <b>b</b> for
|
||||
* <b>purpose</b>, and return 0 otherwise. Used by circuit_get_best.
|
||||
* <b>conn</b>, and return 0 otherwise. Used by circuit_get_best.
|
||||
*/
|
||||
static int
|
||||
circuit_is_better(circuit_t *a, circuit_t *b, uint8_t purpose)
|
||||
circuit_is_better(const origin_circuit_t *oa, const origin_circuit_t *ob,
|
||||
const edge_connection_t *conn)
|
||||
{
|
||||
const circuit_t *a = TO_CIRCUIT(oa);
|
||||
const circuit_t *b = TO_CIRCUIT(ob);
|
||||
const uint8_t purpose = conn->_base.purpose;
|
||||
int a_bits, b_bits;
|
||||
|
||||
switch (purpose) {
|
||||
case CIRCUIT_PURPOSE_C_GENERAL:
|
||||
/* if it's used but less dirty it's best;
|
||||
@ -166,8 +178,7 @@ circuit_is_better(circuit_t *a, circuit_t *b, uint8_t purpose)
|
||||
if (a->timestamp_dirty ||
|
||||
timercmp(&a->timestamp_created, &b->timestamp_created, >))
|
||||
return 1;
|
||||
if (CIRCUIT_IS_ORIGIN(b) &&
|
||||
TO_ORIGIN_CIRCUIT(b)->build_state->is_internal)
|
||||
if (ob->build_state->is_internal)
|
||||
/* XXX023 what the heck is this internal thing doing here. I
|
||||
* think we can get rid of it. circuit_is_acceptable() already
|
||||
* makes sure that is_internal is exactly what we need it to
|
||||
@ -186,6 +197,29 @@ circuit_is_better(circuit_t *a, circuit_t *b, uint8_t purpose)
|
||||
return 1;
|
||||
break;
|
||||
}
|
||||
|
||||
/* XXXX023 Maybe this check should get a higher priority to avoid
|
||||
* using up circuits too rapidly. */
|
||||
|
||||
a_bits = connection_edge_update_circuit_isolation(conn,
|
||||
(origin_circuit_t*)oa, 1);
|
||||
b_bits = connection_edge_update_circuit_isolation(conn,
|
||||
(origin_circuit_t*)ob, 1);
|
||||
/* if x_bits < 0, then we have not used x for anything; better not to dirty
|
||||
* a connection if we can help it. */
|
||||
if (a_bits < 0) {
|
||||
return 0;
|
||||
} else if (b_bits < 0) {
|
||||
return 1;
|
||||
}
|
||||
a_bits &= ~ oa->isolation_flags_mixed;
|
||||
a_bits &= ~ ob->isolation_flags_mixed;
|
||||
if (n_bits_set_u8(a_bits) < n_bits_set_u8(b_bits)) {
|
||||
/* The fewer new restrictions we need to make on a circuit for stream
|
||||
* isolation, the better. */
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -206,10 +240,12 @@ circuit_is_better(circuit_t *a, circuit_t *b, uint8_t purpose)
|
||||
* closest introduce-purposed circuit that you can find.
|
||||
*/
|
||||
static origin_circuit_t *
|
||||
circuit_get_best(edge_connection_t *conn, int must_be_open, uint8_t purpose,
|
||||
circuit_get_best(const edge_connection_t *conn,
|
||||
int must_be_open, uint8_t purpose,
|
||||
int need_uptime, int need_internal)
|
||||
{
|
||||
circuit_t *circ, *best=NULL;
|
||||
circuit_t *circ;
|
||||
origin_circuit_t *best=NULL;
|
||||
struct timeval now;
|
||||
int intro_going_on_but_too_old = 0;
|
||||
|
||||
@ -222,7 +258,11 @@ circuit_get_best(edge_connection_t *conn, int must_be_open, uint8_t purpose,
|
||||
tor_gettimeofday(&now);
|
||||
|
||||
for (circ=global_circuitlist;circ;circ = circ->next) {
|
||||
if (!circuit_is_acceptable(circ,conn,must_be_open,purpose,
|
||||
origin_circuit_t *origin_circ;
|
||||
if (!CIRCUIT_IS_ORIGIN(circ))
|
||||
continue;
|
||||
origin_circ = TO_ORIGIN_CIRCUIT(circ);
|
||||
if (!circuit_is_acceptable(origin_circ,conn,must_be_open,purpose,
|
||||
need_uptime,need_internal,now.tv_sec))
|
||||
continue;
|
||||
|
||||
@ -236,8 +276,8 @@ circuit_get_best(edge_connection_t *conn, int must_be_open, uint8_t purpose,
|
||||
/* now this is an acceptable circ to hand back. but that doesn't
|
||||
* mean it's the *best* circ to hand back. try to decide.
|
||||
*/
|
||||
if (!best || circuit_is_better(circ,best,purpose))
|
||||
best = circ;
|
||||
if (!best || circuit_is_better(origin_circ,best,conn))
|
||||
best = origin_circ;
|
||||
}
|
||||
|
||||
if (!best && intro_going_on_but_too_old)
|
||||
@ -245,7 +285,28 @@ circuit_get_best(edge_connection_t *conn, int must_be_open, uint8_t purpose,
|
||||
"right now, but it has already taken quite a while. Starting "
|
||||
"one in parallel.");
|
||||
|
||||
return best ? TO_ORIGIN_CIRCUIT(best) : NULL;
|
||||
return best;
|
||||
}
|
||||
|
||||
/** Return the number of not-yet-open general-purpose origin circuits. */
|
||||
static int
|
||||
count_pending_general_client_circuits(void)
|
||||
{
|
||||
const circuit_t *circ;
|
||||
|
||||
int count = 0;
|
||||
|
||||
for (circ = global_circuitlist; circ; circ = circ->next) {
|
||||
if (circ->marked_for_close ||
|
||||
circ->state == CIRCUIT_STATE_OPEN ||
|
||||
circ->purpose != CIRCUIT_PURPOSE_C_GENERAL ||
|
||||
!CIRCUIT_IS_ORIGIN(circ))
|
||||
continue;
|
||||
|
||||
++count;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
#if 0
|
||||
@ -937,6 +998,7 @@ circuit_testing_failed(origin_circuit_t *circ, int at_last_hop)
|
||||
void
|
||||
circuit_has_opened(origin_circuit_t *circ)
|
||||
{
|
||||
int can_try_clearing_isolation = 0, tried_clearing_isolation = 0;
|
||||
control_event_circuit_status(circ, CIRC_EVENT_BUILT, 0);
|
||||
|
||||
/* Remember that this circuit has finished building. Now if we start
|
||||
@ -944,9 +1006,12 @@ circuit_has_opened(origin_circuit_t *circ)
|
||||
* to consider its build time. */
|
||||
circ->has_opened = 1;
|
||||
|
||||
again:
|
||||
|
||||
switch (TO_CIRCUIT(circ)->purpose) {
|
||||
case CIRCUIT_PURPOSE_C_ESTABLISH_REND:
|
||||
rend_client_rendcirc_has_opened(circ);
|
||||
can_try_clearing_isolation = 1;
|
||||
connection_ap_attach_pending();
|
||||
break;
|
||||
case CIRCUIT_PURPOSE_C_INTRODUCING:
|
||||
@ -955,6 +1020,7 @@ circuit_has_opened(origin_circuit_t *circ)
|
||||
case CIRCUIT_PURPOSE_C_GENERAL:
|
||||
/* Tell any AP connections that have been waiting for a new
|
||||
* circuit that one is ready. */
|
||||
can_try_clearing_isolation = 1;
|
||||
connection_ap_attach_pending();
|
||||
break;
|
||||
case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO:
|
||||
@ -972,6 +1038,17 @@ circuit_has_opened(origin_circuit_t *circ)
|
||||
* This won't happen in normal operation, but might happen if the
|
||||
* controller did it. Just let it slide. */
|
||||
}
|
||||
|
||||
if (can_try_clearing_isolation && !tried_clearing_isolation &&
|
||||
circ->isolation_values_set &&
|
||||
!circ->isolation_any_streams_attached) {
|
||||
/* If we have any isolation information set on this circuit, and
|
||||
* we didn't manage to attach any streams to it, then we can
|
||||
* and should clear it and try again. */
|
||||
circuit_clear_isolation(circ);
|
||||
tried_clearing_isolation = 1;
|
||||
goto again;
|
||||
}
|
||||
}
|
||||
|
||||
/** Called whenever a circuit could not be successfully built.
|
||||
@ -1307,6 +1384,20 @@ circuit_get_open_circ_or_launch(edge_connection_t *conn,
|
||||
if (!circ) {
|
||||
extend_info_t *extend_info=NULL;
|
||||
uint8_t new_circ_purpose;
|
||||
const int n_pending = count_pending_general_client_circuits();
|
||||
|
||||
if (n_pending >= options->MaxClientCircuitsPending) {
|
||||
static ratelim_t delay_limit = RATELIM_INIT(10*60);
|
||||
char *m;
|
||||
if ((m = rate_limit_log(&delay_limit, approx_time()))) {
|
||||
log_notice(LD_APP, "We'd like to launch a circuit to handle a "
|
||||
"connection, but we already have %d general-purpose client "
|
||||
"circuits pending. Waiting until some finish.",
|
||||
n_pending);
|
||||
tor_free(m);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (desired_circuit_purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) {
|
||||
/* need to pick an intro point */
|
||||
@ -1417,12 +1508,20 @@ circuit_get_open_circ_or_launch(edge_connection_t *conn,
|
||||
rend_client_rendcirc_has_opened(circ);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!circ)
|
||||
} /* endif (!circ) */
|
||||
if (circ) {
|
||||
/* Mark the circuit with the isolation fields for this connection.
|
||||
* When the circuit arrives, we'll clear these flags: this is
|
||||
* just some internal bookkeeping to make sure that we have
|
||||
* launched enough circuits.
|
||||
*/
|
||||
connection_edge_update_circuit_isolation(conn, circ, 0);
|
||||
} else {
|
||||
log_info(LD_APP,
|
||||
"No safe circuit (purpose %d) ready for edge "
|
||||
"connection; delaying.",
|
||||
desired_circuit_purpose);
|
||||
}
|
||||
*circp = circ;
|
||||
return 0;
|
||||
}
|
||||
@ -1468,6 +1567,9 @@ link_apconn_to_circ(edge_connection_t *apconn, origin_circuit_t *circ,
|
||||
tor_assert(circ->cpath->prev->state == CPATH_STATE_OPEN);
|
||||
apconn->cpath_layer = circ->cpath->prev;
|
||||
}
|
||||
|
||||
circ->isolation_any_streams_attached = 1;
|
||||
connection_edge_update_circuit_isolation(apconn, circ, 0);
|
||||
}
|
||||
|
||||
/** Return true iff <b>address</b> is matched by one of the entries in
|
||||
@ -1495,7 +1597,8 @@ hostname_in_track_host_exits(const or_options_t *options, const char *address)
|
||||
* <b>conn</b>'s destination.
|
||||
*/
|
||||
static void
|
||||
consider_recording_trackhost(edge_connection_t *conn, origin_circuit_t *circ)
|
||||
consider_recording_trackhost(const edge_connection_t *conn,
|
||||
const origin_circuit_t *circ)
|
||||
{
|
||||
const or_options_t *options = get_options();
|
||||
char *new_address = NULL;
|
||||
|
451
src/or/config.c
451
src/or/config.c
@ -240,7 +240,7 @@ static config_var_t _option_vars[] = {
|
||||
VAR("DirServer", LINELIST, DirServers, NULL),
|
||||
V(DisableAllSwap, BOOL, "0"),
|
||||
V(DisableIOCP, BOOL, "1"),
|
||||
V(DNSPort, PORT, "0"),
|
||||
V(DNSPort, LINELIST, NULL),
|
||||
V(DNSListenAddress, LINELIST, NULL),
|
||||
V(DownloadExtraInfo, BOOL, "0"),
|
||||
V(EnforceDistinctSubnets, BOOL, "1"),
|
||||
@ -315,13 +315,14 @@ static config_var_t _option_vars[] = {
|
||||
VAR("MapAddress", LINELIST, AddressMap, NULL),
|
||||
V(MaxAdvertisedBandwidth, MEMUNIT, "1 GB"),
|
||||
V(MaxCircuitDirtiness, INTERVAL, "10 minutes"),
|
||||
V(MaxClientCircuitsPending, UINT, "32"),
|
||||
V(MaxOnionsPending, UINT, "100"),
|
||||
OBSOLETE("MonthlyAccountingStart"),
|
||||
V(MyFamily, STRING, NULL),
|
||||
V(NewCircuitPeriod, INTERVAL, "30 seconds"),
|
||||
VAR("NamingAuthoritativeDirectory",BOOL, NamingAuthoritativeDir, "0"),
|
||||
V(NATDListenAddress, LINELIST, NULL),
|
||||
V(NATDPort, PORT, "0"),
|
||||
V(NATDPort, LINELIST, NULL),
|
||||
V(Nickname, STRING, NULL),
|
||||
V(WarnUnsafeSocks, BOOL, "1"),
|
||||
OBSOLETE("NoPublish"),
|
||||
@ -374,7 +375,7 @@ static config_var_t _option_vars[] = {
|
||||
V(ShutdownWaitLength, INTERVAL, "30 seconds"),
|
||||
V(SocksListenAddress, LINELIST, NULL),
|
||||
V(SocksPolicy, LINELIST, NULL),
|
||||
V(SocksPort, PORT, "9050"),
|
||||
V(SocksPort, LINELIST, NULL),
|
||||
V(SocksTimeout, INTERVAL, "2 minutes"),
|
||||
OBSOLETE("StatusFetchPeriod"),
|
||||
V(StrictNodes, BOOL, "0"),
|
||||
@ -385,7 +386,7 @@ static config_var_t _option_vars[] = {
|
||||
V(TrackHostExitsExpire, INTERVAL, "30 minutes"),
|
||||
OBSOLETE("TrafficShaping"),
|
||||
V(TransListenAddress, LINELIST, NULL),
|
||||
V(TransPort, PORT, "0"),
|
||||
V(TransPort, LINELIST, NULL),
|
||||
V(TunnelDirConns, BOOL, "1"),
|
||||
V(UpdateBridgesFromAuthority, BOOL, "0"),
|
||||
V(UseBridges, BOOL, "0"),
|
||||
@ -577,6 +578,9 @@ static int parse_client_transport_line(const char *line, int validate_only);
|
||||
static int parse_dir_server_line(const char *line,
|
||||
dirinfo_type_t required_type,
|
||||
int validate_only);
|
||||
static void port_cfg_free(port_cfg_t *port);
|
||||
static int parse_client_ports(const or_options_t *options, int validate_only,
|
||||
char **msg_out, int *n_ports_out);
|
||||
static int validate_data_directory(or_options_t *options);
|
||||
static int write_configuration_file(const char *fname,
|
||||
const or_options_t *options);
|
||||
@ -646,6 +650,8 @@ static or_state_t *global_state = NULL;
|
||||
static config_line_t *global_cmdline_options = NULL;
|
||||
/** Contents of most recently read DirPortFrontPage file. */
|
||||
static char *global_dirfrontpagecontents = NULL;
|
||||
/** List of port_cfg_t for client-level (SOCKS, DNS, Trans, NATD) ports. */
|
||||
static smartlist_t *configured_client_ports = NULL;
|
||||
|
||||
/** Return the contents of our frontpage string, or NULL if not configured. */
|
||||
const char *
|
||||
@ -758,6 +764,13 @@ config_free_all(void)
|
||||
config_free_lines(global_cmdline_options);
|
||||
global_cmdline_options = NULL;
|
||||
|
||||
if (configured_client_ports) {
|
||||
SMARTLIST_FOREACH(configured_client_ports,
|
||||
port_cfg_t *, p, tor_free(p));
|
||||
smartlist_free(configured_client_ports);
|
||||
configured_client_ports = NULL;
|
||||
}
|
||||
|
||||
tor_free(torrc_fname);
|
||||
tor_free(_version);
|
||||
tor_free(global_dirfrontpagecontents);
|
||||
@ -3027,6 +3040,7 @@ options_validate(or_options_t *old_options, or_options_t *options,
|
||||
int i;
|
||||
config_line_t *cl;
|
||||
const char *uname = get_uname();
|
||||
int n_client_ports=0;
|
||||
#define REJECT(arg) \
|
||||
STMT_BEGIN *msg = tor_strdup(arg); return -1; STMT_END
|
||||
#define COMPLAIN(arg) STMT_BEGIN log(LOG_WARN, LD_CONFIG, arg); STMT_END
|
||||
@ -3050,57 +3064,8 @@ options_validate(or_options_t *old_options, or_options_t *options,
|
||||
if (options->DirPort == 0 && options->DirListenAddress != NULL)
|
||||
REJECT("DirPort must be defined if DirListenAddress is defined.");
|
||||
|
||||
if (options->DNSPort == 0 && options->DNSListenAddress != NULL)
|
||||
REJECT("DNSPort must be defined if DNSListenAddress is defined.");
|
||||
|
||||
if (options->ControlPort == 0 && options->ControlListenAddress != NULL)
|
||||
REJECT("ControlPort must be defined if ControlListenAddress is defined.");
|
||||
|
||||
if (options->TransPort == 0 && options->TransListenAddress != NULL)
|
||||
REJECT("TransPort must be defined if TransListenAddress is defined.");
|
||||
|
||||
if (options->NATDPort == 0 && options->NATDListenAddress != NULL)
|
||||
REJECT("NATDPort must be defined if NATDListenAddress is defined.");
|
||||
|
||||
/* Don't gripe about SocksPort 0 with SocksListenAddress set; a standard
|
||||
* configuration does this. */
|
||||
|
||||
for (i = 0; i < 3; ++i) {
|
||||
int is_socks = i==0;
|
||||
int is_trans = i==1;
|
||||
config_line_t *line, *opt, *old;
|
||||
const char *tp;
|
||||
if (is_socks) {
|
||||
opt = options->SocksListenAddress;
|
||||
old = old_options ? old_options->SocksListenAddress : NULL;
|
||||
tp = "SOCKS proxy";
|
||||
} else if (is_trans) {
|
||||
opt = options->TransListenAddress;
|
||||
old = old_options ? old_options->TransListenAddress : NULL;
|
||||
tp = "transparent proxy";
|
||||
} else {
|
||||
opt = options->NATDListenAddress;
|
||||
old = old_options ? old_options->NATDListenAddress : NULL;
|
||||
tp = "natd proxy";
|
||||
}
|
||||
|
||||
for (line = opt; line; line = line->next) {
|
||||
char *address = NULL;
|
||||
uint16_t port;
|
||||
uint32_t addr;
|
||||
if (parse_addr_port(LOG_WARN, line->value, &address, &addr, &port)<0)
|
||||
continue; /* We'll warn about this later. */
|
||||
if (!is_internal_IP(addr, 1) &&
|
||||
(!old_options || !config_lines_eq(old, opt))) {
|
||||
log_warn(LD_CONFIG,
|
||||
"You specified a public address '%s' for a %s. Other "
|
||||
"people on the Internet might find your computer and use it as "
|
||||
"an open %s. Please don't allow this unless you have "
|
||||
"a good reason.", address, tp, tp);
|
||||
}
|
||||
tor_free(address);
|
||||
}
|
||||
}
|
||||
if (parse_client_ports(options, 1, msg, &n_client_ports) < 0)
|
||||
return -1;
|
||||
|
||||
if (validate_data_directory(options)<0)
|
||||
REJECT("Invalid DataDirectory");
|
||||
@ -3142,9 +3107,7 @@ options_validate(or_options_t *old_options, or_options_t *options,
|
||||
REJECT("Can't use a relative path to torrc when RunAsDaemon is set.");
|
||||
#endif
|
||||
|
||||
if (options->SocksPort == 0 && options->TransPort == 0 &&
|
||||
options->NATDPort == 0 && options->ORPort == 0 &&
|
||||
options->DNSPort == 0 && !options->RendConfigLines)
|
||||
if (n_client_ports == 0 && options->ORPort == 0 && !options->RendConfigLines)
|
||||
log(LOG_WARN, LD_CONFIG,
|
||||
"SocksPort, TransPort, NATDPort, DNSPort, and ORPort are all "
|
||||
"undefined, and there aren't any hidden services configured. "
|
||||
@ -3253,6 +3216,15 @@ options_validate(or_options_t *old_options, or_options_t *options,
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (options->MaxClientCircuitsPending <= 0 ||
|
||||
options->MaxClientCircuitsPending > MAX_MAX_CLIENT_CIRCUITS_PENDING) {
|
||||
tor_asprintf(msg,
|
||||
"MaxClientCircuitsPending must be between 1 and %d, but "
|
||||
"was set to %d", MAX_MAX_CLIENT_CIRCUITS_PENDING,
|
||||
options->MaxClientCircuitsPending);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (validate_ports_csv(options->FirewallPorts, "FirewallPorts", msg) < 0)
|
||||
return -1;
|
||||
|
||||
@ -3892,12 +3864,7 @@ options_validate(or_options_t *old_options, or_options_t *options,
|
||||
static int
|
||||
opt_streq(const char *s1, const char *s2)
|
||||
{
|
||||
if (!s1 && !s2)
|
||||
return 1;
|
||||
else if (s1 && s2 && !strcmp(s1,s2))
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
return 0 == strcmp_opt(s1, s2);
|
||||
}
|
||||
|
||||
/** Check if any of the previous options have changed but aren't allowed to. */
|
||||
@ -4885,6 +4852,362 @@ parse_dir_server_line(const char *line, dirinfo_type_t required_type,
|
||||
return r;
|
||||
}
|
||||
|
||||
/** Free all storage held in <b>port</b> */
|
||||
static void
|
||||
port_cfg_free(port_cfg_t *port)
|
||||
{
|
||||
tor_free(port);
|
||||
}
|
||||
|
||||
/** Warn for every port in <b>ports</b> that is not on a loopback address. */
|
||||
static void
|
||||
warn_nonlocal_client_ports(const smartlist_t *ports, const char *portname)
|
||||
{
|
||||
SMARTLIST_FOREACH_BEGIN(ports, const port_cfg_t *, port) {
|
||||
if (!tor_addr_is_loopback(&port->addr)) {
|
||||
log_warn(LD_CONFIG, "You specified a public address for %sPort. "
|
||||
"Other people on the Internet might find your computer and "
|
||||
"use it as an open proxy. Please don't allow this unless you "
|
||||
"have a good reason.", portname);
|
||||
}
|
||||
} SMARTLIST_FOREACH_END(port);
|
||||
}
|
||||
|
||||
#define CL_PORT_NO_OPTIONS (1u<<0)
|
||||
#define CL_PORT_WARN_NONLOCAL (1u<<1)
|
||||
#define CL_PORT_ALLOW_EXTRA_LISTENADDR (1u<<2)
|
||||
|
||||
/**
|
||||
* Parse port configuration for a single client port type.
|
||||
*
|
||||
* Read entries of the "FooPort" type from the list <b>ports</b>, and
|
||||
* entries of the "FooListenAddress" type from the list
|
||||
* <b>listenaddrs</b>. Two syntaxes are supported: a legacy syntax
|
||||
* where FooPort is at most a single entry containing a port number and
|
||||
* where FooListenAddress has any number of address:port combinations;
|
||||
* and a new syntax where there are no FooListenAddress entries and
|
||||
* where FooPort can have any number of entries of the format
|
||||
* "[Address:][Port] IsolationOptions".
|
||||
*
|
||||
* In log messages, describe the port type as <b>portname</b>.
|
||||
*
|
||||
* If no address is specified, default to <b>defaultaddr</b>. If no
|
||||
* FooPort is given, default to defaultport (if 0, there is no default).
|
||||
*
|
||||
* If CL_PORT_NO_OPTIONS is set in <b>flags</b>, do not allow stream
|
||||
* isolation options in the FooPort entries.
|
||||
*
|
||||
* If CL_PORT_WARN_NONLOCAL is set in <b>flags</b>, warn if any of the
|
||||
* ports are not on a local address.
|
||||
*
|
||||
* Unless CL_PORT_ALLOW_EXTRA_LISTENADDR is set in <b>flags</b>, warn
|
||||
* if FooListenAddress is set but FooPort is 0.
|
||||
*
|
||||
* On success, if <b>out</b> is given, add a new port_cfg_t entry to
|
||||
* <b>out</b> for every port that the client should listen on. Return 0
|
||||
* on success, -1 on failure.
|
||||
*/
|
||||
static int
|
||||
parse_client_port_config(smartlist_t *out,
|
||||
const config_line_t *ports,
|
||||
const config_line_t *listenaddrs,
|
||||
const char *portname,
|
||||
int listener_type,
|
||||
const char *defaultaddr,
|
||||
int defaultport,
|
||||
unsigned flags)
|
||||
{
|
||||
smartlist_t *elts;
|
||||
int retval = -1;
|
||||
const unsigned allow_client_options = !(flags & CL_PORT_NO_OPTIONS);
|
||||
const unsigned warn_nonlocal = flags & CL_PORT_WARN_NONLOCAL;
|
||||
const unsigned allow_spurious_listenaddr =
|
||||
flags & CL_PORT_ALLOW_EXTRA_LISTENADDR;
|
||||
|
||||
/* FooListenAddress is deprecated; let's make it work like it used to work,
|
||||
* though. */
|
||||
if (listenaddrs) {
|
||||
int mainport = defaultport;
|
||||
|
||||
if (ports && ports->next) {
|
||||
log_warn(LD_CONFIG, "%sListenAddress can't be used when there are "
|
||||
"multiple %sPort lines", portname, portname);
|
||||
return -1;
|
||||
} else if (ports) {
|
||||
if (!strcmp(ports->value, "auto")) {
|
||||
mainport = CFG_AUTO_PORT;
|
||||
} else {
|
||||
int ok;
|
||||
mainport = (int)tor_parse_long(ports->value, 10, 0, 65535, &ok, NULL);
|
||||
if (!ok) {
|
||||
log_warn(LD_CONFIG, "%sListenAddress can only be used with a single "
|
||||
"%sPort with value \"auto\" or 65535.", portname, portname);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mainport == 0) {
|
||||
if (allow_spurious_listenaddr)
|
||||
return 1;
|
||||
log_warn(LD_CONFIG, "%sPort must be defined if %sListenAddress is used",
|
||||
portname, portname);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (; listenaddrs; listenaddrs = listenaddrs->next) {
|
||||
tor_addr_t addr;
|
||||
uint16_t port = 0;
|
||||
if (tor_addr_port_parse(listenaddrs->value, &addr, &port) < 0) {
|
||||
log_warn(LD_CONFIG, "Unable to parse %sListenAddress '%s'",
|
||||
portname, listenaddrs->value);
|
||||
return -1;
|
||||
}
|
||||
if (out) {
|
||||
port_cfg_t *cfg = tor_malloc_zero(sizeof(port_cfg_t));
|
||||
cfg->type = listener_type;
|
||||
cfg->port = port ? port : defaultport;
|
||||
tor_addr_copy(&cfg->addr, &addr);
|
||||
cfg->session_group = SESSION_GROUP_UNSET;
|
||||
cfg->isolation_flags = ISO_DEFAULT;
|
||||
smartlist_add(out, cfg);
|
||||
}
|
||||
}
|
||||
|
||||
if (warn_nonlocal && out)
|
||||
warn_nonlocal_client_ports(out, portname);
|
||||
return 0;
|
||||
} /* end if (listenaddrs) */
|
||||
|
||||
/* No ListenAddress lines. If there's no FooPort, then maybe make a default
|
||||
* one. */
|
||||
if (! ports) {
|
||||
if (defaultport && out) {
|
||||
port_cfg_t *cfg = tor_malloc_zero(sizeof(port_cfg_t));
|
||||
cfg->type = listener_type;
|
||||
cfg->port = defaultport;
|
||||
tor_addr_from_str(&cfg->addr, defaultaddr);
|
||||
cfg->session_group = SESSION_GROUP_UNSET;
|
||||
cfg->isolation_flags = ISO_DEFAULT;
|
||||
smartlist_add(out, cfg);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* At last we can actually parse the FooPort lines. The syntax is:
|
||||
* [Addr:](Port|auto) [Options].*/
|
||||
elts = smartlist_create();
|
||||
|
||||
for (; ports; ports = ports->next) {
|
||||
tor_addr_t addr;
|
||||
int port;
|
||||
int sessiongroup = SESSION_GROUP_UNSET;
|
||||
unsigned isolation = ISO_DEFAULT;
|
||||
|
||||
char *addrport;
|
||||
uint16_t ptmp=0;
|
||||
int ok;
|
||||
|
||||
smartlist_split_string(elts, ports->value, NULL,
|
||||
SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
|
||||
if (smartlist_len(elts) == 0) {
|
||||
log_warn(LD_CONFIG, "Invalid %sPort line with no value", portname);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (!allow_client_options && smartlist_len(elts) > 1) {
|
||||
log_warn(LD_CONFIG, "Too many options on %sPort line", portname);
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Now parse the addr/port value */
|
||||
addrport = smartlist_get(elts, 0);
|
||||
if (!strcmp(addrport, "auto")) {
|
||||
port = CFG_AUTO_PORT;
|
||||
tor_addr_from_str(&addr, defaultaddr);
|
||||
} else if (!strcasecmpend(addrport, ":auto")) {
|
||||
char *addrtmp = tor_strndup(addrport, strlen(addrport)-5);
|
||||
port = CFG_AUTO_PORT;
|
||||
if (tor_addr_port_parse(addrtmp, &addr, &ptmp)<0 || ptmp) {
|
||||
log_warn(LD_CONFIG, "Invalid address '%s' for %sPort",
|
||||
escaped(addrport), portname);
|
||||
tor_free(addrtmp);
|
||||
goto err;
|
||||
}
|
||||
} else if (tor_addr_port_parse(addrport, &addr, &ptmp) == 0) {
|
||||
if (ptmp == 0) {
|
||||
log_warn(LD_CONFIG, "%sPort line has address but no port", portname);
|
||||
goto err;
|
||||
}
|
||||
port = ptmp;
|
||||
} else {
|
||||
port = (int) tor_parse_long(addrport, 10, 0, 65535, &ok, NULL);
|
||||
if (!ok) {
|
||||
log_warn(LD_CONFIG, "Couldn't parse address '%s' for %sPort",
|
||||
escaped(addrport), portname);
|
||||
goto err;
|
||||
}
|
||||
tor_addr_from_str(&addr, defaultaddr);
|
||||
}
|
||||
|
||||
/* Now parse the rest of the options, if any. */
|
||||
SMARTLIST_FOREACH_BEGIN(elts, char *, elt) {
|
||||
int no = 0, isoflag = 0;
|
||||
const char *elt_orig = elt;
|
||||
if (elt_sl_idx == 0)
|
||||
continue; /* Skip addr:port */
|
||||
if (!strcasecmpstart(elt, "SessionGroup=")) {
|
||||
int group = (int)tor_parse_long(elt+strlen("SessionGroup="),
|
||||
10, 0, INT_MAX, &ok, NULL);
|
||||
if (!ok) {
|
||||
log_warn(LD_CONFIG, "Invalid %sPort option '%s'",
|
||||
portname, escaped(elt));
|
||||
goto err;
|
||||
}
|
||||
if (sessiongroup >= 0) {
|
||||
log_warn(LD_CONFIG, "Multiple SessionGroup options on %sPort",
|
||||
portname);
|
||||
goto err;
|
||||
}
|
||||
sessiongroup = group;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strcasecmpstart(elt, "No")) {
|
||||
no = 1;
|
||||
elt += 2;
|
||||
}
|
||||
if (!strcasecmpend(elt, "s"))
|
||||
elt[strlen(elt)-1] = '\0'; /* kill plurals. */
|
||||
|
||||
if (!strcasecmp(elt, "IsolateDestPort")) {
|
||||
isoflag = ISO_DESTPORT;
|
||||
} else if (!strcasecmp(elt, "IsolateDestAddr")) {
|
||||
isoflag = ISO_DESTADDR;
|
||||
} else if (!strcasecmp(elt, "IsolateSOCKSAuth")) {
|
||||
isoflag = ISO_SOCKSAUTH;
|
||||
} else if (!strcasecmp(elt, "IsolateClientProtocol")) {
|
||||
isoflag = ISO_CLIENTPROTO;
|
||||
} else if (!strcasecmp(elt, "IsolateClientAddr")) {
|
||||
isoflag = ISO_CLIENTADDR;
|
||||
} else {
|
||||
log_warn(LD_CONFIG, "Unrecognized %sPort option '%s'",
|
||||
portname, escaped(elt_orig));
|
||||
}
|
||||
|
||||
if (no) {
|
||||
isolation &= ~isoflag;
|
||||
} else {
|
||||
isolation |= isoflag;
|
||||
}
|
||||
} SMARTLIST_FOREACH_END(elt);
|
||||
|
||||
if (out) {
|
||||
port_cfg_t *cfg = tor_malloc_zero(sizeof(port_cfg_t));
|
||||
cfg->type = listener_type;
|
||||
cfg->port = port;
|
||||
tor_addr_copy(&cfg->addr, &addr);
|
||||
cfg->session_group = sessiongroup;
|
||||
cfg->isolation_flags = isolation;
|
||||
smartlist_add(out, cfg);
|
||||
}
|
||||
SMARTLIST_FOREACH(elts, char *, cp, tor_free(cp));
|
||||
smartlist_clear(elts);
|
||||
}
|
||||
|
||||
if (warn_nonlocal && out)
|
||||
warn_nonlocal_client_ports(out, portname);
|
||||
|
||||
retval = 0;
|
||||
err:
|
||||
SMARTLIST_FOREACH(elts, char *, cp, tor_free(cp));
|
||||
smartlist_free(elts);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/** Parse all client port types (Socks, DNS, Trans, NATD) from
|
||||
* <b>options</b>. On success, set *<b>n_ports_out</b> to the number of
|
||||
* ports that are listed and return 0. On failure, set *<b>msg</b> to a
|
||||
* description of the problem and return -1.
|
||||
*
|
||||
* If <b>validate_only</b> is false, set configured_client_ports to the
|
||||
* new list of ports parsed from <b>options</b>.
|
||||
**/
|
||||
static int
|
||||
parse_client_ports(const or_options_t *options, int validate_only,
|
||||
char **msg, int *n_ports_out)
|
||||
{
|
||||
smartlist_t *ports;
|
||||
int retval = -1;
|
||||
|
||||
ports = smartlist_create();
|
||||
|
||||
*n_ports_out = 0;
|
||||
|
||||
if (parse_client_port_config(ports,
|
||||
options->SocksPort, options->SocksListenAddress,
|
||||
"Socks", CONN_TYPE_AP_LISTENER,
|
||||
"127.0.0.1", 9050,
|
||||
CL_PORT_WARN_NONLOCAL|CL_PORT_ALLOW_EXTRA_LISTENADDR) < 0) {
|
||||
*msg = tor_strdup("Invalid SocksPort/SocksListenAddress configuration");
|
||||
goto err;
|
||||
}
|
||||
if (parse_client_port_config(ports,
|
||||
options->DNSPort, options->DNSListenAddress,
|
||||
"DNS", CONN_TYPE_AP_DNS_LISTENER,
|
||||
"127.0.0.1", 0,
|
||||
CL_PORT_WARN_NONLOCAL) < 0) {
|
||||
*msg = tor_strdup("Invalid DNSPort/DNSListenAddress configuration");
|
||||
goto err;
|
||||
}
|
||||
if (parse_client_port_config(ports,
|
||||
options->TransPort, options->TransListenAddress,
|
||||
"Trans", CONN_TYPE_AP_TRANS_LISTENER,
|
||||
"127.0.0.1", 0,
|
||||
CL_PORT_WARN_NONLOCAL) < 0) {
|
||||
*msg = tor_strdup("Invalid TransPort/TransListenAddress configuration");
|
||||
goto err;
|
||||
}
|
||||
if (parse_client_port_config(ports,
|
||||
options->NATDPort, options->NATDListenAddress,
|
||||
"NATD", CONN_TYPE_AP_NATD_LISTENER,
|
||||
"127.0.0.1", 0,
|
||||
CL_PORT_WARN_NONLOCAL) < 0) {
|
||||
*msg = tor_strdup("Invalid NatdPort/NatdListenAddress configuration");
|
||||
goto err;
|
||||
}
|
||||
|
||||
*n_ports_out = smartlist_len(ports);
|
||||
|
||||
if (!validate_only) {
|
||||
if (configured_client_ports) {
|
||||
SMARTLIST_FOREACH(configured_client_ports,
|
||||
port_cfg_t *, p, port_cfg_free(p));
|
||||
smartlist_free(configured_client_ports);
|
||||
}
|
||||
configured_client_ports = ports;
|
||||
ports = NULL; /* prevent free below. */
|
||||
}
|
||||
|
||||
retval = 0;
|
||||
err:
|
||||
if (ports) {
|
||||
SMARTLIST_FOREACH(ports, port_cfg_t *, p, port_cfg_free(p));
|
||||
smartlist_free(ports);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
/** Return a list of port_cfg_t for client ports parsed from the
|
||||
* options. */
|
||||
const smartlist_t *
|
||||
get_configured_client_ports(void)
|
||||
{
|
||||
if (!configured_client_ports)
|
||||
configured_client_ports = smartlist_create();
|
||||
return configured_client_ports;
|
||||
}
|
||||
|
||||
/** Adjust the value of options->DataDirectory, or fill it in if it's
|
||||
* absent. Return 0 on success, -1 on failure. */
|
||||
static int
|
||||
|
@ -64,6 +64,8 @@ or_state_t *get_or_state(void);
|
||||
int did_last_state_file_write_fail(void);
|
||||
int or_state_save(time_t now);
|
||||
|
||||
const smartlist_t *get_configured_client_ports(void);
|
||||
|
||||
int options_need_geoip_info(const or_options_t *options,
|
||||
const char **reason_out);
|
||||
int getinfo_helper_config(control_connection_t *conn,
|
||||
|
@ -43,11 +43,12 @@
|
||||
static connection_t *connection_create_listener(
|
||||
const struct sockaddr *listensockaddr,
|
||||
socklen_t listensocklen, int type,
|
||||
char* address);
|
||||
const char *address,
|
||||
const port_cfg_t *portcfg);
|
||||
static void connection_init(time_t now, connection_t *conn, int type,
|
||||
int socket_family);
|
||||
static int connection_init_accepted_conn(connection_t *conn,
|
||||
uint8_t listener_type);
|
||||
const listener_connection_t *listener);
|
||||
static int connection_handle_listener_read(connection_t *conn, int new_type);
|
||||
#ifndef USE_BUFFEREVENTS
|
||||
static int connection_bucket_should_increase(int bucket,
|
||||
@ -76,6 +77,15 @@ static uint32_t last_interface_ip = 0;
|
||||
* Used to detect IP address changes. */
|
||||
static smartlist_t *outgoing_addrs = NULL;
|
||||
|
||||
#define CASE_ANY_LISTENER_TYPE \
|
||||
case CONN_TYPE_OR_LISTENER: \
|
||||
case CONN_TYPE_AP_LISTENER: \
|
||||
case CONN_TYPE_DIR_LISTENER: \
|
||||
case CONN_TYPE_CONTROL_LISTENER: \
|
||||
case CONN_TYPE_AP_TRANS_LISTENER: \
|
||||
case CONN_TYPE_AP_NATD_LISTENER: \
|
||||
case CONN_TYPE_AP_DNS_LISTENER
|
||||
|
||||
/**************************************************************/
|
||||
|
||||
/**
|
||||
@ -116,13 +126,7 @@ conn_state_to_string(int type, int state)
|
||||
{
|
||||
static char buf[96];
|
||||
switch (type) {
|
||||
case CONN_TYPE_OR_LISTENER:
|
||||
case CONN_TYPE_AP_LISTENER:
|
||||
case CONN_TYPE_AP_TRANS_LISTENER:
|
||||
case CONN_TYPE_AP_NATD_LISTENER:
|
||||
case CONN_TYPE_AP_DNS_LISTENER:
|
||||
case CONN_TYPE_DIR_LISTENER:
|
||||
case CONN_TYPE_CONTROL_LISTENER:
|
||||
CASE_ANY_LISTENER_TYPE:
|
||||
if (state == LISTENER_STATE_READY)
|
||||
return "ready";
|
||||
break;
|
||||
@ -265,6 +269,17 @@ control_connection_new(int socket_family)
|
||||
return control_conn;
|
||||
}
|
||||
|
||||
/** Allocate and return a new listener_connection_t, initialized as by
|
||||
* connection_init(). */
|
||||
listener_connection_t *
|
||||
listener_connection_new(int type, int socket_family)
|
||||
{
|
||||
listener_connection_t *listener_conn =
|
||||
tor_malloc_zero(sizeof(listener_connection_t));
|
||||
connection_init(time(NULL), TO_CONN(listener_conn), type, socket_family);
|
||||
return listener_conn;
|
||||
}
|
||||
|
||||
/** Allocate, initialize, and return a new connection_t subtype of <b>type</b>
|
||||
* to make or receive connections of address family <b>socket_family</b>. The
|
||||
* type should be one of the CONN_TYPE_* constants. */
|
||||
@ -285,6 +300,9 @@ connection_new(int type, int socket_family)
|
||||
case CONN_TYPE_CONTROL:
|
||||
return TO_CONN(control_connection_new(socket_family));
|
||||
|
||||
CASE_ANY_LISTENER_TYPE:
|
||||
return TO_CONN(listener_connection_new(type, socket_family));
|
||||
|
||||
default: {
|
||||
connection_t *conn = tor_malloc_zero(sizeof(connection_t));
|
||||
connection_init(time(NULL), conn, type, socket_family);
|
||||
@ -325,6 +343,8 @@ connection_init(time_t now, connection_t *conn, int type, int socket_family)
|
||||
case CONN_TYPE_CONTROL:
|
||||
conn->magic = CONTROL_CONNECTION_MAGIC;
|
||||
break;
|
||||
CASE_ANY_LISTENER_TYPE:
|
||||
conn->magic = LISTENER_CONNECTION_MAGIC;
|
||||
default:
|
||||
conn->magic = BASE_CONNECTION_MAGIC;
|
||||
break;
|
||||
@ -396,6 +416,11 @@ _connection_free(connection_t *conn)
|
||||
mem = TO_CONTROL_CONN(conn);
|
||||
memlen = sizeof(control_connection_t);
|
||||
break;
|
||||
CASE_ANY_LISTENER_TYPE:
|
||||
tor_assert(conn->magic == LISTENER_CONNECTION_MAGIC);
|
||||
mem = TO_LISTENER_CONN(conn);
|
||||
memlen = sizeof(listener_connection_t);
|
||||
break;
|
||||
default:
|
||||
tor_assert(conn->magic == BASE_CONNECTION_MAGIC);
|
||||
mem = conn;
|
||||
@ -442,9 +467,9 @@ _connection_free(connection_t *conn)
|
||||
if (CONN_IS_EDGE(conn)) {
|
||||
edge_connection_t *edge_conn = TO_EDGE_CONN(conn);
|
||||
tor_free(edge_conn->chosen_exit_name);
|
||||
tor_free(edge_conn->original_dest_address);
|
||||
if (edge_conn->socks_request)
|
||||
socks_request_free(edge_conn->socks_request);
|
||||
|
||||
rend_data_free(edge_conn->rend_data);
|
||||
}
|
||||
if (conn->type == CONN_TYPE_CONTROL) {
|
||||
@ -701,48 +726,6 @@ connection_expire_held_open(void)
|
||||
});
|
||||
}
|
||||
|
||||
/** Create an AF_INET listenaddr struct.
|
||||
* <b>listenaddress</b> provides the host and optionally the port information
|
||||
* for the new structure. If no port is provided in <b>listenaddress</b> then
|
||||
* <b>listenport</b> is used.
|
||||
*
|
||||
* If not NULL <b>readable_address</b> will contain a copy of the host part of
|
||||
* <b>listenaddress</b>.
|
||||
*
|
||||
* The listenaddr struct has to be freed by the caller.
|
||||
*/
|
||||
static struct sockaddr_in *
|
||||
create_inet_sockaddr(const char *listenaddress, int listenport,
|
||||
char **readable_address, socklen_t *socklen_out) {
|
||||
struct sockaddr_in *listenaddr = NULL;
|
||||
uint32_t addr;
|
||||
uint16_t usePort = 0;
|
||||
|
||||
if (parse_addr_port(LOG_WARN,
|
||||
listenaddress, readable_address, &addr, &usePort)<0) {
|
||||
log_warn(LD_CONFIG,
|
||||
"Error parsing/resolving ListenAddress %s", listenaddress);
|
||||
goto err;
|
||||
}
|
||||
if (usePort==0) {
|
||||
if (listenport != CFG_AUTO_PORT)
|
||||
usePort = listenport;
|
||||
}
|
||||
|
||||
listenaddr = tor_malloc_zero(sizeof(struct sockaddr_in));
|
||||
listenaddr->sin_addr.s_addr = htonl(addr);
|
||||
listenaddr->sin_family = AF_INET;
|
||||
listenaddr->sin_port = htons((uint16_t) usePort);
|
||||
|
||||
*socklen_out = sizeof(struct sockaddr_in);
|
||||
|
||||
return listenaddr;
|
||||
|
||||
err:
|
||||
tor_free(listenaddr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef HAVE_SYS_UN_H
|
||||
/** Create an AF_UNIX listenaddr struct.
|
||||
* <b>listenaddress</b> provides the path to the Unix socket.
|
||||
@ -877,12 +860,15 @@ make_socket_reuseable(tor_socket_t sock)
|
||||
static connection_t *
|
||||
connection_create_listener(const struct sockaddr *listensockaddr,
|
||||
socklen_t socklen,
|
||||
int type, char* address)
|
||||
int type, const char *address,
|
||||
const port_cfg_t *port_cfg)
|
||||
{
|
||||
listener_connection_t *lis_conn;
|
||||
connection_t *conn;
|
||||
tor_socket_t s; /* the socket we're going to make */
|
||||
uint16_t usePort = 0, gotPort = 0;
|
||||
int start_reading = 0;
|
||||
static int global_next_session_group = SESSION_GROUP_FIRST_AUTO;
|
||||
|
||||
if (get_n_open_sockets() >= get_options()->_ConnLimit-1) {
|
||||
warn_too_many_conns();
|
||||
@ -999,12 +985,23 @@ connection_create_listener(const struct sockaddr *listensockaddr,
|
||||
|
||||
set_socket_nonblocking(s);
|
||||
|
||||
conn = connection_new(type, listensockaddr->sa_family);
|
||||
lis_conn = listener_connection_new(type, listensockaddr->sa_family);
|
||||
conn = TO_CONN(lis_conn);
|
||||
conn->socket_family = listensockaddr->sa_family;
|
||||
conn->s = s;
|
||||
conn->address = tor_strdup(address);
|
||||
conn->port = gotPort;
|
||||
|
||||
if (port_cfg->isolation_flags) {
|
||||
lis_conn->isolation_flags = port_cfg->isolation_flags;
|
||||
if (port_cfg->session_group >= 0) {
|
||||
lis_conn->session_group = port_cfg->session_group;
|
||||
} else {
|
||||
/* XXXX023 This can wrap after ~INT_MAX ports are opened. */
|
||||
lis_conn->session_group = global_next_session_group--;
|
||||
}
|
||||
}
|
||||
|
||||
if (connection_add(conn) < 0) { /* no space, forget it */
|
||||
log_warn(LD_NET,"connection_add for listener failed. Giving up.");
|
||||
connection_free(conn);
|
||||
@ -1217,7 +1214,7 @@ connection_handle_listener_read(connection_t *conn, int new_type)
|
||||
return 0; /* no need to tear down the parent */
|
||||
}
|
||||
|
||||
if (connection_init_accepted_conn(newconn, conn->type) < 0) {
|
||||
if (connection_init_accepted_conn(newconn, TO_LISTENER_CONN(conn)) < 0) {
|
||||
if (! newconn->marked_for_close)
|
||||
connection_mark_for_close(newconn);
|
||||
return 0;
|
||||
@ -1231,7 +1228,8 @@ connection_handle_listener_read(connection_t *conn, int new_type)
|
||||
* and place it in circuit_wait.
|
||||
*/
|
||||
static int
|
||||
connection_init_accepted_conn(connection_t *conn, uint8_t listener_type)
|
||||
connection_init_accepted_conn(connection_t *conn,
|
||||
const listener_connection_t *listener)
|
||||
{
|
||||
connection_start_reading(conn);
|
||||
|
||||
@ -1240,7 +1238,10 @@ connection_init_accepted_conn(connection_t *conn, uint8_t listener_type)
|
||||
control_event_or_conn_status(TO_OR_CONN(conn), OR_CONN_EVENT_NEW, 0);
|
||||
return connection_tls_start_handshake(TO_OR_CONN(conn), 1);
|
||||
case CONN_TYPE_AP:
|
||||
switch (listener_type) {
|
||||
TO_EDGE_CONN(conn)->isolation_flags = listener->isolation_flags;
|
||||
TO_EDGE_CONN(conn)->session_group = listener->session_group;
|
||||
TO_EDGE_CONN(conn)->nym_epoch = get_signewnym_epoch();
|
||||
switch (TO_CONN(listener)->type) {
|
||||
case CONN_TYPE_AP_LISTENER:
|
||||
conn->state = AP_CONN_STATE_SOCKS_WAIT;
|
||||
break;
|
||||
@ -1741,6 +1742,111 @@ connection_read_proxy_handshake(connection_t *conn)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Given a list of listener connections in <b>old_conns</b>, and list of
|
||||
* port_cfg_t entries in <b>ports</b>, open a new listener for every port in
|
||||
* <b>ports</b> that does not already have a listener in <b>old_conns</b>.
|
||||
*
|
||||
* Remove from <b>old_conns</b> every connection that has a corresponding
|
||||
* entry in <b>ports</b>. Add to <b>new_conns</b> new every connection we
|
||||
* launch.
|
||||
*
|
||||
* Return 0 on success, -1 on failure.
|
||||
**/
|
||||
static int
|
||||
retry_listener_ports(smartlist_t *old_conns,
|
||||
const smartlist_t *ports,
|
||||
smartlist_t *new_conns)
|
||||
{
|
||||
smartlist_t *launch = smartlist_create();
|
||||
int r = 0;
|
||||
|
||||
smartlist_add_all(launch, ports);
|
||||
|
||||
/* Iterate through old_conns, comparing it to launch: remove from both lists
|
||||
* each pair of elements that corresponds to the same port. */
|
||||
SMARTLIST_FOREACH_BEGIN(old_conns, connection_t *, conn) {
|
||||
const port_cfg_t *found_port = NULL;
|
||||
|
||||
/* Okay, so this is a listener. Is it configured? */
|
||||
SMARTLIST_FOREACH_BEGIN(launch, const port_cfg_t *, wanted) {
|
||||
if (conn->type != wanted->type)
|
||||
continue;
|
||||
if ((conn->socket_family != AF_UNIX && wanted->is_unix_addr) ||
|
||||
(conn->socket_family == AF_UNIX && ! wanted->is_unix_addr))
|
||||
continue;
|
||||
|
||||
if (wanted->is_unix_addr) {
|
||||
if (conn->socket_family == AF_UNIX &&
|
||||
!strcmp(wanted->unix_addr, conn->address)) {
|
||||
found_port = wanted;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
int port_matches;
|
||||
if (wanted->port == CFG_AUTO_PORT) {
|
||||
port_matches = 1;
|
||||
} else {
|
||||
port_matches = (wanted->port == conn->port);
|
||||
}
|
||||
if (port_matches && tor_addr_eq(&wanted->addr, &conn->addr)) {
|
||||
found_port = wanted;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} SMARTLIST_FOREACH_END(wanted);
|
||||
|
||||
if (found_port) {
|
||||
/* This listener is already running; we don't need to launch it. */
|
||||
// log_debug(LD_NET, "Already have %s on %s:%d",
|
||||
// conn_type_to_string(type), conn->address, conn->port);
|
||||
smartlist_remove(launch, found_port);
|
||||
/* And we can remove the connection from old_conns too. */
|
||||
SMARTLIST_DEL_CURRENT(old_conns, conn);
|
||||
}
|
||||
} SMARTLIST_FOREACH_END(conn);
|
||||
|
||||
/* Now open all the listeners that are configured but not opened. */
|
||||
SMARTLIST_FOREACH_BEGIN(launch, const port_cfg_t *, port) {
|
||||
struct sockaddr *listensockaddr;
|
||||
socklen_t listensocklen = 0;
|
||||
char *address=NULL;
|
||||
connection_t *conn;
|
||||
|
||||
if (port->is_unix_addr) {
|
||||
listensockaddr = (struct sockaddr *)
|
||||
create_unix_sockaddr(port->unix_addr,
|
||||
&address, &listensocklen);
|
||||
} else {
|
||||
listensockaddr = tor_malloc(sizeof(struct sockaddr_storage));
|
||||
listensocklen = tor_addr_to_sockaddr(&port->addr,
|
||||
port->port,
|
||||
listensockaddr,
|
||||
sizeof(struct sockaddr_storage));
|
||||
address = tor_dup_addr(&port->addr);
|
||||
}
|
||||
|
||||
if (listensockaddr) {
|
||||
conn = connection_create_listener(listensockaddr, listensocklen,
|
||||
port->type, address, port);
|
||||
tor_free(listensockaddr);
|
||||
tor_free(address);
|
||||
} else {
|
||||
conn = NULL;
|
||||
}
|
||||
|
||||
if (!conn) {
|
||||
r = -1;
|
||||
} else {
|
||||
if (new_conns)
|
||||
smartlist_add(new_conns, conn);
|
||||
}
|
||||
} SMARTLIST_FOREACH_END(port);
|
||||
|
||||
smartlist_free(launch);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Launch any configured listener connections of type <b>type</b>. (A
|
||||
* listener is configured if <b>port_option</b> is non-zero. If any
|
||||
@ -1748,168 +1854,73 @@ connection_read_proxy_handshake(connection_t *conn)
|
||||
* connection binding to each one. Otherwise, create a single
|
||||
* connection binding to the address <b>default_addr</b>.)
|
||||
*
|
||||
* Only launch the listeners of this type that are not already open, and
|
||||
* only close listeners that are no longer wanted. Existing listeners
|
||||
* that are still configured are not touched.
|
||||
* We assume that we're starting with a list of existing listener connection_t
|
||||
* pointers in <b>old_conns</b>: we do not launch listeners that are already
|
||||
* in that list. Instead, we just remove them from the list.
|
||||
*
|
||||
* If <b>disable_all_conns</b> is set, then never open new conns, and
|
||||
* close the existing ones.
|
||||
*
|
||||
* Add all old conns that should be closed to <b>replaced_conns</b>.
|
||||
* Add all new connections to <b>new_conns</b>.
|
||||
* All new connections we launch are added to <b>new_conns</b>.
|
||||
*/
|
||||
static int
|
||||
retry_listeners(int type, config_line_t *cfg,
|
||||
retry_listeners(smartlist_t *old_conns,
|
||||
int type, const config_line_t *cfg,
|
||||
int port_option, const char *default_addr,
|
||||
smartlist_t *replaced_conns,
|
||||
smartlist_t *new_conns,
|
||||
int disable_all_conns,
|
||||
int socket_family)
|
||||
int is_sockaddr_un)
|
||||
{
|
||||
smartlist_t *launch = smartlist_create(), *conns;
|
||||
int free_launch_elts = 1;
|
||||
int r;
|
||||
config_line_t *c;
|
||||
connection_t *conn;
|
||||
config_line_t *line;
|
||||
smartlist_t *ports = smartlist_create();
|
||||
tor_addr_t dflt_addr;
|
||||
int retval = 0;
|
||||
|
||||
tor_assert(socket_family == AF_INET || socket_family == AF_UNIX);
|
||||
|
||||
if (cfg && port_option) {
|
||||
for (c = cfg; c; c = c->next) {
|
||||
smartlist_add(launch, c);
|
||||
}
|
||||
free_launch_elts = 0;
|
||||
} else if (port_option) {
|
||||
line = tor_malloc_zero(sizeof(config_line_t));
|
||||
line->key = tor_strdup("");
|
||||
line->value = tor_strdup(default_addr);
|
||||
smartlist_add(launch, line);
|
||||
if (default_addr) {
|
||||
tor_addr_from_str(&dflt_addr, default_addr);
|
||||
} else {
|
||||
tor_addr_make_unspec(&dflt_addr);
|
||||
}
|
||||
|
||||
/*
|
||||
SMARTLIST_FOREACH(launch, config_line_t *, l,
|
||||
log_fn(LOG_NOTICE, "#%s#%s", l->key, l->value));
|
||||
*/
|
||||
|
||||
conns = get_connection_array();
|
||||
SMARTLIST_FOREACH(conns, connection_t *, conn,
|
||||
{
|
||||
if (conn->type != type ||
|
||||
conn->socket_family != socket_family ||
|
||||
conn->marked_for_close)
|
||||
continue;
|
||||
/* Okay, so this is a listener. Is it configured? */
|
||||
line = NULL;
|
||||
SMARTLIST_FOREACH(launch, config_line_t *, wanted,
|
||||
{
|
||||
char *address=NULL;
|
||||
uint16_t port;
|
||||
switch (socket_family) {
|
||||
case AF_INET:
|
||||
if (!parse_addr_port(LOG_WARN,
|
||||
wanted->value, &address, NULL, &port)) {
|
||||
int addr_matches = !strcasecmp(address, conn->address);
|
||||
int port_matches;
|
||||
tor_free(address);
|
||||
if (port) {
|
||||
/* The Listener line has a port */
|
||||
port_matches = (port == conn->port);
|
||||
} else if (port_option == CFG_AUTO_PORT) {
|
||||
/* The Listener line has no port, and the Port line is "auto".
|
||||
* "auto" matches anything; transitions from any port to
|
||||
* "auto" succeed. */
|
||||
port_matches = 1;
|
||||
} else {
|
||||
/* The Listener line has no port, and the Port line is "auto".
|
||||
* "auto" matches anything; transitions from any port to
|
||||
* "auto" succeed. */
|
||||
port_matches = (port_option == conn->port);
|
||||
}
|
||||
if (port_matches && addr_matches) {
|
||||
line = wanted;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case AF_UNIX:
|
||||
if (!strcasecmp(wanted->value, conn->address)) {
|
||||
line = wanted;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
tor_assert(0);
|
||||
}
|
||||
});
|
||||
if (!line || disable_all_conns) {
|
||||
/* This one isn't configured. Close it. */
|
||||
log_notice(LD_NET, "Closing no-longer-configured %s on %s:%d",
|
||||
conn_type_to_string(type), conn->address, conn->port);
|
||||
if (replaced_conns) {
|
||||
smartlist_add(replaced_conns, conn);
|
||||
} else {
|
||||
connection_close_immediate(conn);
|
||||
connection_mark_for_close(conn);
|
||||
}
|
||||
if (port_option) {
|
||||
if (!cfg) {
|
||||
port_cfg_t *port = tor_malloc_zero(sizeof(port_cfg_t));
|
||||
tor_addr_copy(&port->addr, &dflt_addr);
|
||||
port->port = port_option;
|
||||
port->type = type;
|
||||
smartlist_add(ports, port);
|
||||
} else {
|
||||
/* It's configured; we don't need to launch it. */
|
||||
// log_debug(LD_NET, "Already have %s on %s:%d",
|
||||
// conn_type_to_string(type), conn->address, conn->port);
|
||||
smartlist_remove(launch, line);
|
||||
if (free_launch_elts)
|
||||
config_free_lines(line);
|
||||
}
|
||||
});
|
||||
|
||||
/* Now open all the listeners that are configured but not opened. */
|
||||
r = 0;
|
||||
if (!disable_all_conns) {
|
||||
SMARTLIST_FOREACH_BEGIN(launch, config_line_t *, cfg_line) {
|
||||
char *address = NULL;
|
||||
struct sockaddr *listensockaddr;
|
||||
socklen_t listensocklen = 0;
|
||||
|
||||
switch (socket_family) {
|
||||
case AF_INET:
|
||||
listensockaddr = (struct sockaddr *)
|
||||
create_inet_sockaddr(cfg_line->value,
|
||||
port_option,
|
||||
&address, &listensocklen);
|
||||
break;
|
||||
case AF_UNIX:
|
||||
listensockaddr = (struct sockaddr *)
|
||||
create_unix_sockaddr(cfg_line->value,
|
||||
&address, &listensocklen);
|
||||
break;
|
||||
default:
|
||||
tor_assert(0);
|
||||
}
|
||||
|
||||
if (listensockaddr) {
|
||||
conn = connection_create_listener(listensockaddr, listensocklen,
|
||||
type, address);
|
||||
tor_free(listensockaddr);
|
||||
tor_free(address);
|
||||
} else
|
||||
conn = NULL;
|
||||
|
||||
if (!conn) {
|
||||
r = -1;
|
||||
const config_line_t *c;
|
||||
for (c = cfg; c; c = c->next) {
|
||||
port_cfg_t *port;
|
||||
tor_addr_t addr;
|
||||
uint16_t portval = 0;
|
||||
if (is_sockaddr_un) {
|
||||
size_t len = strlen(c->value);
|
||||
port = tor_malloc_zero(sizeof(port_cfg_t) + len + 1);
|
||||
port->is_unix_addr = 1;
|
||||
memcpy(port->unix_addr, c->value, len+1);
|
||||
} else {
|
||||
if (new_conns)
|
||||
smartlist_add(new_conns, conn);
|
||||
if (tor_addr_port_parse(c->value, &addr, &portval) < 0) {
|
||||
log_warn(LD_CONFIG, "Can't parse/resolve %s %s",
|
||||
c->key, c->value);
|
||||
retval = -1;
|
||||
continue;
|
||||
}
|
||||
port = tor_malloc_zero(sizeof(port_cfg_t));
|
||||
tor_addr_copy(&port->addr, &addr);
|
||||
}
|
||||
} SMARTLIST_FOREACH_END(cfg_line);
|
||||
port->type = type;
|
||||
port->port = portval ? portval : port_option;
|
||||
smartlist_add(ports, port);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (free_launch_elts) {
|
||||
SMARTLIST_FOREACH(launch, config_line_t *, cfg_line,
|
||||
config_free_lines(cfg_line));
|
||||
}
|
||||
smartlist_free(launch);
|
||||
if (retval == -1)
|
||||
goto cleanup;
|
||||
|
||||
return r;
|
||||
retval = retry_listener_ports(old_conns, ports, new_conns);
|
||||
|
||||
cleanup:
|
||||
SMARTLIST_FOREACH(ports, port_cfg_t *, p, tor_free(p));
|
||||
smartlist_free(ports);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/** Launch listeners for each port you should have open. Only launch
|
||||
@ -1923,54 +1934,62 @@ int
|
||||
retry_all_listeners(smartlist_t *replaced_conns,
|
||||
smartlist_t *new_conns)
|
||||
{
|
||||
smartlist_t *listeners = smartlist_create();
|
||||
const or_options_t *options = get_options();
|
||||
int retval = 0;
|
||||
const uint16_t old_or_port = router_get_advertised_or_port(options);
|
||||
const uint16_t old_dir_port = router_get_advertised_dir_port(options, 0);
|
||||
|
||||
if (retry_listeners(CONN_TYPE_OR_LISTENER, options->ORListenAddress,
|
||||
options->ORPort, "0.0.0.0",
|
||||
replaced_conns, new_conns, options->ClientOnly,
|
||||
AF_INET)<0)
|
||||
SMARTLIST_FOREACH_BEGIN(get_connection_array(), connection_t *, conn) {
|
||||
if (connection_is_listener(conn) && !conn->marked_for_close)
|
||||
smartlist_add(listeners, conn);
|
||||
} SMARTLIST_FOREACH_END(conn);
|
||||
|
||||
if (! options->ClientOnly) {
|
||||
if (retry_listeners(listeners,
|
||||
CONN_TYPE_OR_LISTENER, options->ORListenAddress,
|
||||
options->ORPort, "0.0.0.0",
|
||||
new_conns, 0) < 0)
|
||||
retval = -1;
|
||||
if (retry_listeners(listeners,
|
||||
CONN_TYPE_DIR_LISTENER, options->DirListenAddress,
|
||||
options->DirPort, "0.0.0.0",
|
||||
new_conns, 0) < 0)
|
||||
retval = -1;
|
||||
}
|
||||
|
||||
if (retry_listener_ports(listeners,
|
||||
get_configured_client_ports(),
|
||||
new_conns) < 0)
|
||||
retval = -1;
|
||||
if (retry_listeners(CONN_TYPE_DIR_LISTENER, options->DirListenAddress,
|
||||
options->DirPort, "0.0.0.0",
|
||||
replaced_conns, new_conns, options->ClientOnly,
|
||||
AF_INET)<0)
|
||||
retval = -1;
|
||||
if (retry_listeners(CONN_TYPE_AP_LISTENER, options->SocksListenAddress,
|
||||
options->SocksPort, "127.0.0.1",
|
||||
replaced_conns, new_conns, 0,
|
||||
AF_INET)<0)
|
||||
retval = -1;
|
||||
if (retry_listeners(CONN_TYPE_AP_TRANS_LISTENER, options->TransListenAddress,
|
||||
options->TransPort, "127.0.0.1",
|
||||
replaced_conns, new_conns, 0,
|
||||
AF_INET)<0)
|
||||
retval = -1;
|
||||
if (retry_listeners(CONN_TYPE_AP_NATD_LISTENER, options->NATDListenAddress,
|
||||
options->NATDPort, "127.0.0.1",
|
||||
replaced_conns, new_conns, 0,
|
||||
AF_INET)<0)
|
||||
retval = -1;
|
||||
if (retry_listeners(CONN_TYPE_AP_DNS_LISTENER, options->DNSListenAddress,
|
||||
options->DNSPort, "127.0.0.1",
|
||||
replaced_conns, new_conns, 0,
|
||||
AF_INET)<0)
|
||||
retval = -1;
|
||||
if (retry_listeners(CONN_TYPE_CONTROL_LISTENER,
|
||||
if (retry_listeners(listeners,
|
||||
CONN_TYPE_CONTROL_LISTENER,
|
||||
options->ControlListenAddress,
|
||||
options->ControlPort, "127.0.0.1",
|
||||
replaced_conns, new_conns, 0,
|
||||
AF_INET)<0)
|
||||
new_conns, 0) < 0)
|
||||
return -1;
|
||||
if (retry_listeners(CONN_TYPE_CONTROL_LISTENER,
|
||||
if (retry_listeners(listeners,
|
||||
CONN_TYPE_CONTROL_LISTENER,
|
||||
options->ControlSocket,
|
||||
options->ControlSocket ? 1 : 0, NULL,
|
||||
replaced_conns, new_conns, 0,
|
||||
AF_UNIX)<0)
|
||||
new_conns, 1) < 0)
|
||||
return -1;
|
||||
|
||||
/* Any members that were still in 'listeners' don't correspond to
|
||||
* any configured port. Kill 'em. */
|
||||
SMARTLIST_FOREACH_BEGIN(listeners, connection_t *, conn) {
|
||||
log_notice(LD_NET, "Closing no-longer-configured %s on %s:%d",
|
||||
conn_type_to_string(conn->type), conn->address, conn->port);
|
||||
if (replaced_conns) {
|
||||
smartlist_add(replaced_conns, conn);
|
||||
} else {
|
||||
connection_close_immediate(conn);
|
||||
connection_mark_for_close(conn);
|
||||
}
|
||||
} SMARTLIST_FOREACH_END(conn);
|
||||
|
||||
smartlist_free(listeners);
|
||||
|
||||
if (old_or_port != router_get_advertised_or_port(options) ||
|
||||
old_dir_port != router_get_advertised_dir_port(options, 0)) {
|
||||
/* Our chosen ORPort or DirPort is not what it used to be: the
|
||||
@ -3994,13 +4013,7 @@ assert_connection_ok(connection_t *conn, time_t now)
|
||||
|
||||
switch (conn->type)
|
||||
{
|
||||
case CONN_TYPE_OR_LISTENER:
|
||||
case CONN_TYPE_AP_LISTENER:
|
||||
case CONN_TYPE_AP_TRANS_LISTENER:
|
||||
case CONN_TYPE_AP_NATD_LISTENER:
|
||||
case CONN_TYPE_DIR_LISTENER:
|
||||
case CONN_TYPE_CONTROL_LISTENER:
|
||||
case CONN_TYPE_AP_DNS_LISTENER:
|
||||
CASE_ANY_LISTENER_TYPE:
|
||||
tor_assert(conn->state == LISTENER_STATE_READY);
|
||||
break;
|
||||
case CONN_TYPE_OR:
|
||||
|
@ -22,6 +22,7 @@ dir_connection_t *dir_connection_new(int socket_family);
|
||||
or_connection_t *or_connection_new(int socket_family);
|
||||
edge_connection_t *edge_connection_new(int type, int socket_family);
|
||||
control_connection_t *control_connection_new(int socket_family);
|
||||
listener_connection_t *listener_connection_new(int type, int socket_family);
|
||||
connection_t *connection_new(int type, int socket_family);
|
||||
|
||||
void connection_link_connections(connection_t *conn_a, connection_t *conn_b);
|
||||
|
@ -1671,6 +1671,9 @@ connection_ap_handshake_rewrite_and_attach(edge_connection_t *conn,
|
||||
safe_str_client(socks->address),
|
||||
socks->port);
|
||||
|
||||
if (! conn->original_dest_address)
|
||||
conn->original_dest_address = tor_strdup(conn->socks_request->address);
|
||||
|
||||
if (socks->command == SOCKS_COMMAND_RESOLVE &&
|
||||
!tor_inet_aton(socks->address, &addr_tmp) &&
|
||||
options->AutomapHostsOnResolve && options->AutomapHostsSuffixes) {
|
||||
@ -2494,7 +2497,9 @@ connection_ap_handshake_send_resolve(edge_connection_t *ap_conn)
|
||||
edge_connection_t *
|
||||
connection_ap_make_link(connection_t *partner,
|
||||
char *address, uint16_t port,
|
||||
const char *digest, int use_begindir, int want_onehop)
|
||||
const char *digest,
|
||||
int session_group, int isolation_flags,
|
||||
int use_begindir, int want_onehop)
|
||||
{
|
||||
edge_connection_t *conn;
|
||||
|
||||
@ -2524,6 +2529,11 @@ connection_ap_make_link(connection_t *partner,
|
||||
digest, DIGEST_LEN);
|
||||
}
|
||||
|
||||
/* Populate isolation fields. */
|
||||
conn->original_dest_address = tor_strdup(address);
|
||||
conn->session_group = session_group;
|
||||
conn->isolation_flags = isolation_flags;
|
||||
|
||||
conn->_base.address = tor_strdup("(Tor_internal)");
|
||||
tor_addr_make_unspec(&conn->_base.addr);
|
||||
conn->_base.port = 0;
|
||||
@ -3166,7 +3176,7 @@ connection_edge_is_rendezvous_stream(edge_connection_t *conn)
|
||||
* resolved.)
|
||||
*/
|
||||
int
|
||||
connection_ap_can_use_exit(edge_connection_t *conn, const node_t *exit)
|
||||
connection_ap_can_use_exit(const edge_connection_t *conn, const node_t *exit)
|
||||
{
|
||||
const or_options_t *options = get_options();
|
||||
|
||||
@ -3266,3 +3276,218 @@ parse_extended_hostname(char *address, int allowdotexit)
|
||||
return BAD_HOSTNAME;
|
||||
}
|
||||
|
||||
/** Return true iff <b>a</b> and <b>b</b> have isolation rules and fields that
|
||||
* make it permissible to put them on the same circuit.*/
|
||||
int
|
||||
connection_edge_streams_are_compatible(const edge_connection_t *a,
|
||||
const edge_connection_t *b)
|
||||
{
|
||||
const uint8_t iso = a->isolation_flags | b->isolation_flags;
|
||||
|
||||
if (! a->original_dest_address) {
|
||||
log_warn(LD_BUG, "Reached connection_edge_streams_are_compatible without "
|
||||
"having set a->original_dest_address");
|
||||
((edge_connection_t*)a)->original_dest_address =
|
||||
tor_strdup(a->socks_request->address);
|
||||
}
|
||||
if (! b->original_dest_address) {
|
||||
log_warn(LD_BUG, "Reached connection_edge_streams_are_compatible without "
|
||||
"having set b->original_dest_address");
|
||||
((edge_connection_t*)b)->original_dest_address =
|
||||
tor_strdup(a->socks_request->address);
|
||||
}
|
||||
|
||||
if (iso & ISO_STREAM)
|
||||
return 0;
|
||||
|
||||
if ((iso & ISO_DESTPORT) && a->socks_request->port != b->socks_request->port)
|
||||
return 0;
|
||||
if ((iso & ISO_DESTADDR) &&
|
||||
strcasecmp(a->original_dest_address, b->original_dest_address))
|
||||
return 0;
|
||||
if ((iso & ISO_SOCKSAUTH) &&
|
||||
(strcmp_opt(a->socks_request->username, b->socks_request->username) ||
|
||||
strcmp_opt(a->socks_request->password, b->socks_request->password)))
|
||||
return 0;
|
||||
if ((iso & ISO_CLIENTPROTO) &&
|
||||
(TO_CONN(a)->type != TO_CONN(b)->type ||
|
||||
a->socks_request->socks_version != b->socks_request->socks_version))
|
||||
return 0;
|
||||
if ((iso & ISO_CLIENTADDR) &&
|
||||
!tor_addr_eq(&TO_CONN(a)->addr, &TO_CONN(b)->addr))
|
||||
return 0;
|
||||
if ((iso & ISO_SESSIONGRP) && a->session_group != b->session_group)
|
||||
return 0;
|
||||
if ((iso & ISO_NYM_EPOCH) && a->nym_epoch != b->nym_epoch)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true iff none of the isolation flags and fields in <b>conn</b>
|
||||
* should prevent it from being attached to <b>circ</b>.
|
||||
*/
|
||||
int
|
||||
connection_edge_compatible_with_circuit(const edge_connection_t *conn,
|
||||
const origin_circuit_t *circ)
|
||||
{
|
||||
const uint8_t iso = conn->isolation_flags;
|
||||
|
||||
/* If circ has never been used for an isolated connection, we can
|
||||
* totally use it for this one. */
|
||||
if (!circ->isolation_values_set)
|
||||
return 1;
|
||||
|
||||
/* If circ has been used for connections having more than one value
|
||||
* for some field f, it will have the corresponding bit set in
|
||||
* isolation_flags_mixed. If isolation_flags_mixed has any bits
|
||||
* in common with iso, then conn must be isolated from at least
|
||||
* one stream that has been attached to circ. */
|
||||
if ((iso & circ->isolation_flags_mixed) != 0) {
|
||||
/* For at least one field where conn is isolated, the circuit
|
||||
* already has mixed streams. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (! conn->original_dest_address) {
|
||||
log_warn(LD_BUG, "Reached connection_edge_compatible_with_circuit without "
|
||||
"having set conn->original_dest_address");
|
||||
((edge_connection_t*)conn)->original_dest_address =
|
||||
tor_strdup(conn->socks_request->address);
|
||||
}
|
||||
|
||||
/* If isolation_values_set, then the circuit is not compatible with
|
||||
* any new ISO_STREAM stream. */
|
||||
if (iso & ISO_STREAM)
|
||||
return 0;
|
||||
|
||||
if ((iso & ISO_DESTPORT) && conn->socks_request->port != circ->dest_port)
|
||||
return 0;
|
||||
if ((iso & ISO_DESTADDR) &&
|
||||
strcasecmp(conn->original_dest_address, circ->dest_address))
|
||||
return 0;
|
||||
if ((iso & ISO_SOCKSAUTH) &&
|
||||
(strcmp_opt(conn->socks_request->username, circ->socks_username) ||
|
||||
strcmp_opt(conn->socks_request->password, circ->socks_password)))
|
||||
return 0;
|
||||
if ((iso & ISO_CLIENTPROTO) &&
|
||||
(TO_CONN(conn)->type != circ->client_proto_type ||
|
||||
conn->socks_request->socks_version != circ->client_proto_socksver))
|
||||
return 0;
|
||||
if ((iso & ISO_CLIENTADDR) &&
|
||||
!tor_addr_eq(&TO_CONN(conn)->addr, &circ->client_addr))
|
||||
return 0;
|
||||
if ((iso & ISO_SESSIONGRP) && conn->session_group != circ->session_group)
|
||||
return 0;
|
||||
if ((iso & ISO_NYM_EPOCH) && conn->nym_epoch != circ->nym_epoch)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* If <b>dry_run</b> is false, update <b>circ</b>'s isolation flags and fields
|
||||
* to reflect having had <b>conn</b> attached to it, and return 0. Otherwise,
|
||||
* if <b>dry_run</b> is true, then make no changes to <b>circ</b>, and return
|
||||
* a bitfield of isolation flags that we would have to set in
|
||||
* isolation_flags_mixed to add <b>conn</b> to <b>circ</b>, or -1 if
|
||||
* <b>circ</b> has had no streams attached to it.
|
||||
*/
|
||||
int
|
||||
connection_edge_update_circuit_isolation(const edge_connection_t *conn,
|
||||
origin_circuit_t *circ,
|
||||
int dry_run)
|
||||
{
|
||||
if (! conn->original_dest_address) {
|
||||
log_warn(LD_BUG, "Reached connection_update_circuit_isolation without "
|
||||
"having set conn->original_dest_address");
|
||||
((edge_connection_t*)conn)->original_dest_address =
|
||||
tor_strdup(conn->socks_request->address);
|
||||
}
|
||||
|
||||
if (!circ->isolation_values_set) {
|
||||
if (dry_run)
|
||||
return -1;
|
||||
circ->dest_port = conn->socks_request->port;
|
||||
circ->dest_address = tor_strdup(conn->original_dest_address);
|
||||
circ->client_proto_type = TO_CONN(conn)->type;
|
||||
circ->client_proto_socksver = conn->socks_request->socks_version;
|
||||
tor_addr_copy(&circ->client_addr, &TO_CONN(conn)->addr);
|
||||
circ->session_group = conn->session_group;
|
||||
circ->nym_epoch = conn->nym_epoch;
|
||||
circ->socks_username = conn->socks_request->username ?
|
||||
tor_strdup(conn->socks_request->username) : NULL;
|
||||
circ->socks_password = conn->socks_request->password ?
|
||||
tor_strdup(conn->socks_request->password) : NULL;
|
||||
|
||||
circ->isolation_values_set = 1;
|
||||
return 0;
|
||||
} else {
|
||||
uint8_t mixed = 0;
|
||||
if (conn->socks_request->port != circ->dest_port)
|
||||
mixed |= ISO_DESTPORT;
|
||||
if (strcasecmp(conn->original_dest_address, circ->dest_address))
|
||||
mixed |= ISO_DESTADDR;
|
||||
if (strcmp_opt(conn->socks_request->username, circ->socks_username) ||
|
||||
strcmp_opt(conn->socks_request->password, circ->socks_password))
|
||||
mixed |= ISO_SOCKSAUTH;
|
||||
if ((TO_CONN(conn)->type != circ->client_proto_type ||
|
||||
conn->socks_request->socks_version != circ->client_proto_socksver))
|
||||
mixed |= ISO_CLIENTPROTO;
|
||||
if (!tor_addr_eq(&TO_CONN(conn)->addr, &circ->client_addr))
|
||||
mixed |= ISO_CLIENTADDR;
|
||||
if (conn->session_group != circ->session_group)
|
||||
mixed |= ISO_SESSIONGRP;
|
||||
if (conn->nym_epoch != circ->nym_epoch)
|
||||
mixed |= ISO_NYM_EPOCH;
|
||||
|
||||
if (dry_run)
|
||||
return mixed;
|
||||
|
||||
if ((mixed & conn->isolation_flags) != 0) {
|
||||
log_warn(LD_BUG, "Updating a circuit with seemingly incomaptible "
|
||||
"isolation flags.");
|
||||
}
|
||||
circ->isolation_flags_mixed |= mixed;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the isolation settings on <b>circ</b>.
|
||||
*
|
||||
* This only works on an open circuit that has never had a stream attached to
|
||||
* it, and whose isolation settings are hypothetical. (We set hypothetical
|
||||
* isolation settings on circuits as we're launching them, so that we
|
||||
* know whether they can handle more streams or whether we need to launch
|
||||
* even more circuits. Once the circuit is open, if it turns out that
|
||||
* we no longer have any streams to attach to it, we clear the isolation flags
|
||||
* and data so that other streams can have a chance.)
|
||||
*/
|
||||
void
|
||||
circuit_clear_isolation(origin_circuit_t *circ)
|
||||
{
|
||||
if (circ->isolation_any_streams_attached) {
|
||||
log_warn(LD_BUG, "Tried to clear the isolation status of a dirty circuit");
|
||||
return;
|
||||
}
|
||||
if (TO_CIRCUIT(circ)->state != CIRCUIT_STATE_OPEN) {
|
||||
log_warn(LD_BUG, "Tried to clear the isolation status of a non-open "
|
||||
"circuit");
|
||||
return;
|
||||
}
|
||||
|
||||
circ->isolation_values_set = 0;
|
||||
circ->isolation_flags_mixed = 0;
|
||||
circ->client_proto_type = 0;
|
||||
circ->client_proto_socksver = 0;
|
||||
circ->dest_port = 0;
|
||||
tor_addr_make_unspec(&circ->client_addr);
|
||||
tor_free(circ->dest_address);
|
||||
circ->session_group = -1;
|
||||
circ->nym_epoch = 0;
|
||||
tor_free(circ->socks_username);
|
||||
tor_free(circ->socks_password);
|
||||
}
|
||||
|
||||
|
@ -36,6 +36,8 @@ int connection_ap_handshake_send_resolve(edge_connection_t *ap_conn);
|
||||
edge_connection_t *connection_ap_make_link(connection_t *partner,
|
||||
char *address, uint16_t port,
|
||||
const char *digest,
|
||||
int session_group,
|
||||
int isolation_flags,
|
||||
int use_begindir, int want_onehop);
|
||||
void connection_ap_handshake_socks_reply(edge_connection_t *conn, char *reply,
|
||||
size_t replylen,
|
||||
@ -51,7 +53,7 @@ int connection_exit_begin_conn(cell_t *cell, circuit_t *circ);
|
||||
int connection_exit_begin_resolve(cell_t *cell, or_circuit_t *circ);
|
||||
void connection_exit_connect(edge_connection_t *conn);
|
||||
int connection_edge_is_rendezvous_stream(edge_connection_t *conn);
|
||||
int connection_ap_can_use_exit(edge_connection_t *conn,
|
||||
int connection_ap_can_use_exit(const edge_connection_t *conn,
|
||||
const node_t *exit);
|
||||
void connection_ap_expire_beginning(void);
|
||||
void connection_ap_attach_pending(void);
|
||||
@ -103,5 +105,14 @@ hostname_type_t parse_extended_hostname(char *address, int allowdotexit);
|
||||
int get_pf_socket(void);
|
||||
#endif
|
||||
|
||||
int connection_edge_streams_are_compatible(const edge_connection_t *a,
|
||||
const edge_connection_t *b);
|
||||
int connection_edge_compatible_with_circuit(const edge_connection_t *conn,
|
||||
const origin_circuit_t *circ);
|
||||
int connection_edge_update_circuit_isolation(const edge_connection_t *conn,
|
||||
origin_circuit_t *circ,
|
||||
int dry_run);
|
||||
void circuit_clear_isolation(origin_circuit_t *circ);
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -973,6 +973,10 @@ directory_initiate_command_rend(const char *address, const tor_addr_t *_addr,
|
||||
}
|
||||
} else { /* we want to connect via a tor connection */
|
||||
edge_connection_t *linked_conn;
|
||||
/* Anonymized tunneled connections can never share a circuit.
|
||||
* One-hop directory connections can share circuits with each other
|
||||
* but nothing else. */
|
||||
int iso_flags = anonymized_connection ? ISO_STREAM : ISO_SESSIONGRP;
|
||||
|
||||
/* If it's an anonymized connection, remember the fact that we
|
||||
* wanted it for later: maybe we'll want it again soon. */
|
||||
@ -988,7 +992,9 @@ directory_initiate_command_rend(const char *address, const tor_addr_t *_addr,
|
||||
linked_conn =
|
||||
connection_ap_make_link(TO_CONN(conn),
|
||||
conn->_base.address, conn->_base.port,
|
||||
digest, use_begindir, conn->dirconn_direct);
|
||||
digest,
|
||||
SESSION_GROUP_DIRCONN, iso_flags,
|
||||
use_begindir, conn->dirconn_direct);
|
||||
if (!linked_conn) {
|
||||
log_warn(LD_NET,"Making tunnel to dirserver failed.");
|
||||
connection_mark_for_close(TO_CONN(conn));
|
||||
|
@ -29,8 +29,9 @@
|
||||
* DNSPort. We need to eventually answer the request <b>req</b>.
|
||||
*/
|
||||
static void
|
||||
evdns_server_callback(struct evdns_server_request *req, void *_data)
|
||||
evdns_server_callback(struct evdns_server_request *req, void *data_)
|
||||
{
|
||||
const listener_connection_t *listener = data_;
|
||||
edge_connection_t *conn;
|
||||
int i = 0;
|
||||
struct evdns_server_question *q = NULL;
|
||||
@ -43,7 +44,7 @@ evdns_server_callback(struct evdns_server_request *req, void *_data)
|
||||
char *q_name;
|
||||
|
||||
tor_assert(req);
|
||||
tor_assert(_data == NULL);
|
||||
|
||||
log_info(LD_APP, "Got a new DNS request!");
|
||||
|
||||
req->flags |= 0x80; /* set RA */
|
||||
@ -131,6 +132,9 @@ evdns_server_callback(struct evdns_server_request *req, void *_data)
|
||||
sizeof(conn->socks_request->address));
|
||||
|
||||
conn->dns_server_request = req;
|
||||
conn->isolation_flags = listener->isolation_flags;
|
||||
conn->session_group = listener->session_group;
|
||||
conn->nym_epoch = get_signewnym_epoch();
|
||||
|
||||
if (connection_add(TO_CONN(conn)) < 0) {
|
||||
log_warn(LD_APP, "Couldn't register dummy connection for DNS request");
|
||||
@ -181,6 +185,11 @@ dnsserv_launch_request(const char *name, int reverse)
|
||||
strlcpy(conn->socks_request->address, name,
|
||||
sizeof(conn->socks_request->address));
|
||||
|
||||
conn->original_dest_address = tor_strdup(name);
|
||||
conn->session_group = SESSION_GROUP_CONTROL_RESOLVE;
|
||||
conn->nym_epoch = get_signewnym_epoch();
|
||||
conn->isolation_flags = ISO_DEFAULT;
|
||||
|
||||
if (connection_add(TO_CONN(conn))<0) {
|
||||
log_warn(LD_APP, "Couldn't register dummy connection for RESOLVE request");
|
||||
connection_free(TO_CONN(conn));
|
||||
@ -305,12 +314,15 @@ dnsserv_resolved(edge_connection_t *conn,
|
||||
void
|
||||
dnsserv_configure_listener(connection_t *conn)
|
||||
{
|
||||
listener_connection_t *listener_conn;
|
||||
tor_assert(conn);
|
||||
tor_assert(SOCKET_OK(conn->s));
|
||||
tor_assert(conn->type == CONN_TYPE_AP_DNS_LISTENER);
|
||||
|
||||
conn->dns_server_port =
|
||||
tor_evdns_add_server_port(conn->s, 0, evdns_server_callback, NULL);
|
||||
listener_conn = TO_LISTENER_CONN(conn);
|
||||
listener_conn->dns_server_port =
|
||||
tor_evdns_add_server_port(conn->s, 0, evdns_server_callback,
|
||||
listener_conn);
|
||||
}
|
||||
|
||||
/** Free the evdns server port for <b>conn</b>, which must be an
|
||||
@ -318,12 +330,15 @@ dnsserv_configure_listener(connection_t *conn)
|
||||
void
|
||||
dnsserv_close_listener(connection_t *conn)
|
||||
{
|
||||
listener_connection_t *listener_conn;
|
||||
tor_assert(conn);
|
||||
tor_assert(conn->type == CONN_TYPE_AP_DNS_LISTENER);
|
||||
|
||||
if (conn->dns_server_port) {
|
||||
evdns_close_server_port(conn->dns_server_port);
|
||||
conn->dns_server_port = NULL;
|
||||
listener_conn = TO_LISTENER_CONN(conn);
|
||||
|
||||
if (listener_conn->dns_server_port) {
|
||||
evdns_close_server_port(listener_conn->dns_server_port);
|
||||
listener_conn->dns_server_port = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -114,6 +114,8 @@ static time_t time_to_check_for_correct_dns = 0;
|
||||
static time_t time_of_last_signewnym = 0;
|
||||
/** Is there a signewnym request we're currently waiting to handle? */
|
||||
static int signewnym_is_pending = 0;
|
||||
/** How many times have we called newnym? */
|
||||
static unsigned newnym_epoch = 0;
|
||||
|
||||
/** Smartlist of all open connections. */
|
||||
static smartlist_t *connection_array = NULL;
|
||||
@ -290,7 +292,7 @@ connection_unregister_events(connection_t *conn)
|
||||
conn->bufev = NULL;
|
||||
}
|
||||
#endif
|
||||
if (conn->dns_server_port) {
|
||||
if (conn->type == CONN_TYPE_AP_DNS_LISTENER) {
|
||||
dnsserv_close_listener(conn);
|
||||
}
|
||||
}
|
||||
@ -1038,9 +1040,18 @@ signewnym_impl(time_t now)
|
||||
time_of_last_signewnym = now;
|
||||
signewnym_is_pending = 0;
|
||||
|
||||
++newnym_epoch;
|
||||
|
||||
control_event_signal(SIGNEWNYM);
|
||||
}
|
||||
|
||||
/** Return the number of times that signewnym has been called. */
|
||||
unsigned
|
||||
get_signewnym_epoch(void)
|
||||
{
|
||||
return newnym_epoch;
|
||||
}
|
||||
|
||||
/** Perform regular maintenance tasks. This function gets run once per
|
||||
* second by second_elapsed_callback().
|
||||
*/
|
||||
|
@ -52,6 +52,7 @@ void ip_address_changed(int at_interface);
|
||||
void dns_servers_relaunch_checks(void);
|
||||
|
||||
long get_uptime(void);
|
||||
unsigned get_signewnym_epoch(void);
|
||||
|
||||
void handle_signals(int is_parent);
|
||||
void process_signal(uintptr_t sig);
|
||||
|
161
src/or/or.h
161
src/or/or.h
@ -938,6 +938,7 @@ typedef struct socks_request_t socks_request_t;
|
||||
#define EDGE_CONNECTION_MAGIC 0xF0374013u
|
||||
#define DIR_CONNECTION_MAGIC 0x9988ffeeu
|
||||
#define CONTROL_CONNECTION_MAGIC 0x8abc765du
|
||||
#define LISTENER_CONNECTION_MAGIC 0x1a1ac741u
|
||||
|
||||
/** Description of a connection to another host or process, and associated
|
||||
* data.
|
||||
@ -1043,15 +1044,31 @@ typedef struct connection_t {
|
||||
/** Unique identifier for this connection on this Tor instance. */
|
||||
uint64_t global_identifier;
|
||||
|
||||
/* XXXX023 move this field, and all the listener-only fields (just
|
||||
socket_family, I think), into a new listener_connection_t subtype. */
|
||||
/** Unique ID for measuring tunneled network status requests. */
|
||||
uint64_t dirreq_id;
|
||||
} connection_t;
|
||||
|
||||
typedef struct listener_connection_t {
|
||||
connection_t _base;
|
||||
|
||||
/** If the connection is a CONN_TYPE_AP_DNS_LISTENER, this field points
|
||||
* to the evdns_server_port it uses to listen to and answer connections. */
|
||||
struct evdns_server_port *dns_server_port;
|
||||
|
||||
/** Unique ID for measuring tunneled network status requests. */
|
||||
uint64_t dirreq_id;
|
||||
} connection_t;
|
||||
/** @name Isolation parameters
|
||||
*
|
||||
* For an AP listener, these fields describe how to isolate streams that
|
||||
* arrive on the listener.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
/** The session group for this listener. */
|
||||
int session_group;
|
||||
/** One or more ISO_ flags to describe how to isolate streams. */
|
||||
uint8_t isolation_flags;
|
||||
/**@}*/
|
||||
|
||||
} listener_connection_t;
|
||||
|
||||
/** Stores flags and information related to the portion of a v2 Tor OR
|
||||
* connection handshake that happens after the TLS handshake is finished.
|
||||
@ -1190,6 +1207,20 @@ typedef struct edge_connection_t {
|
||||
/** What rendezvous service are we querying for? (AP only) */
|
||||
rend_data_t *rend_data;
|
||||
|
||||
/* === Isolation related, AP only. === */
|
||||
/** AP only: based on which factors do we isolate this stream? */
|
||||
uint8_t isolation_flags;
|
||||
/** AP only: what session group is this stream in? */
|
||||
int session_group;
|
||||
/** AP only: The newnym epoch in which we created this connection. */
|
||||
unsigned nym_epoch;
|
||||
/** AP only: The original requested address before we rewrote it. */
|
||||
char *original_dest_address;
|
||||
/* Other fields to isolate on already exist. The ClientAddr is addr. The
|
||||
ClientProtocol is a combination of type and socks_request->
|
||||
socks_version. SocksAuth is socks_request->username/password.
|
||||
DestAddr is in socks_request->address. */
|
||||
|
||||
/** Number of times we've reassigned this application connection to
|
||||
* a new circuit. We keep track because the timeout is longer if we've
|
||||
* already retried several times. */
|
||||
@ -1321,6 +1352,9 @@ static edge_connection_t *TO_EDGE_CONN(connection_t *);
|
||||
/** Convert a connection_t* to an control_connection_t*; assert if the cast is
|
||||
* invalid. */
|
||||
static control_connection_t *TO_CONTROL_CONN(connection_t *);
|
||||
/** Convert a connection_t* to an listener_connection_t*; assert if the cast is
|
||||
* invalid. */
|
||||
static listener_connection_t *TO_LISTENER_CONN(connection_t *);
|
||||
|
||||
static INLINE or_connection_t *TO_OR_CONN(connection_t *c)
|
||||
{
|
||||
@ -1342,6 +1376,11 @@ static INLINE control_connection_t *TO_CONTROL_CONN(connection_t *c)
|
||||
tor_assert(c->magic == CONTROL_CONNECTION_MAGIC);
|
||||
return DOWNCAST(control_connection_t, c);
|
||||
}
|
||||
static INLINE listener_connection_t *TO_LISTENER_CONN(connection_t *c)
|
||||
{
|
||||
tor_assert(c->magic == LISTENER_CONNECTION_MAGIC);
|
||||
return DOWNCAST(listener_connection_t, c);
|
||||
}
|
||||
|
||||
/* Conditional macros to help write code that works whether bufferevents are
|
||||
disabled or not.
|
||||
@ -2424,6 +2463,48 @@ typedef struct origin_circuit_t {
|
||||
/* XXXX NM This can get re-used after 2**32 circuits. */
|
||||
uint32_t global_identifier;
|
||||
|
||||
/** True if we have associated one stream to this circuit, thereby setting
|
||||
* the isolation paramaters for this circuit. Note that this doesn't
|
||||
* necessarily mean that we've <em>attached</em> any streams to the circuit:
|
||||
* we may only have marked up this circuit during the launch process.
|
||||
*/
|
||||
unsigned int isolation_values_set : 1;
|
||||
/** True iff any stream has <em>ever</em> been attached to this circuit.
|
||||
*
|
||||
* In a better world we could use timestamp_dirty for this, but
|
||||
* timestamp_dirty is far too overloaded at the moment.
|
||||
*/
|
||||
unsigned int isolation_any_streams_attached : 1;
|
||||
|
||||
/** A bitfield of ISO_* flags for every isolation field such that this
|
||||
* circuit has had streams with more than one value for that field
|
||||
* attached to it. */
|
||||
uint8_t isolation_flags_mixed;
|
||||
|
||||
/** @name Isolation parameters
|
||||
*
|
||||
* If any streams have been associated with this circ (isolation_values_set
|
||||
* == 1), and all streams associated with the circuit have had the same
|
||||
* value for some field ((isolation_flags_mixed & ISO_FOO) == 0), then these
|
||||
* elements hold the value for that field.
|
||||
*
|
||||
* Note again that "associated" is not the same as "attached": we
|
||||
* preliminarily associate streams with a circuit while the circuit is being
|
||||
* launched, so that we can tell whether we need to launch more circuits.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
uint8_t client_proto_type;
|
||||
uint8_t client_proto_socksver;
|
||||
uint16_t dest_port;
|
||||
tor_addr_t client_addr;
|
||||
char *dest_address;
|
||||
int session_group;
|
||||
unsigned nym_epoch;
|
||||
char *socks_username;
|
||||
char *socks_password;
|
||||
/**@}*/
|
||||
|
||||
} origin_circuit_t;
|
||||
|
||||
/** An or_circuit_t holds information needed to implement a circuit at an
|
||||
@ -2542,6 +2623,60 @@ typedef enum invalid_router_usage_t {
|
||||
#define MIN_CONSTRAINED_TCP_BUFFER 2048
|
||||
#define MAX_CONSTRAINED_TCP_BUFFER 262144 /* 256k */
|
||||
|
||||
/** @name Isolation flags
|
||||
|
||||
Ways to isolate client streams
|
||||
|
||||
@{
|
||||
*/
|
||||
/** Isolate based on destination port */
|
||||
#define ISO_DESTPORT (1u<<0)
|
||||
/** Isolate based on destination address */
|
||||
#define ISO_DESTADDR (1u<<1)
|
||||
/** Isolate based on SOCKS authentication */
|
||||
#define ISO_SOCKSAUTH (1u<<2)
|
||||
/** Isolate based on client protocol choice */
|
||||
#define ISO_CLIENTPROTO (1u<<3)
|
||||
/** Isolate based on client address */
|
||||
#define ISO_CLIENTADDR (1u<<4)
|
||||
/** Isolate based on session group (always on). */
|
||||
#define ISO_SESSIONGRP (1u<<5)
|
||||
/** Isolate based on newnym epoch (always on). */
|
||||
#define ISO_NYM_EPOCH (1u<<6)
|
||||
/** Isolate all streams (Internal only). */
|
||||
#define ISO_STREAM (1u<<7)
|
||||
/**@}*/
|
||||
|
||||
/** Default isolation level for ports. */
|
||||
#define ISO_DEFAULT (ISO_CLIENTADDR|ISO_SOCKSAUTH|ISO_SESSIONGRP|ISO_NYM_EPOCH)
|
||||
|
||||
/** Indicates that we haven't yet set a session group on a port_cfg_t. */
|
||||
#define SESSION_GROUP_UNSET -1
|
||||
/** Session group reserved for directory connections */
|
||||
#define SESSION_GROUP_DIRCONN -2
|
||||
/** Session group reserved for resolve requests launched by a controller */
|
||||
#define SESSION_GROUP_CONTROL_RESOLVE -3
|
||||
/** First automatically allocated session group number */
|
||||
#define SESSION_GROUP_FIRST_AUTO -4
|
||||
|
||||
/** Configuration for a single port that we're listening on. */
|
||||
typedef struct port_cfg_t {
|
||||
tor_addr_t addr; /**< The actual IP to listen on, if !is_unix_addr. */
|
||||
int port; /**< The configured port, or CFG_AUTO_PORT to tell Tor to pick its
|
||||
* own port. */
|
||||
uint8_t type; /**< One of CONN_TYPE_*_LISTENER */
|
||||
unsigned is_unix_addr : 1; /**< True iff this is an AF_UNIX address. */
|
||||
|
||||
/* Client port types (socks, dns, trans, natd) only: */
|
||||
uint8_t isolation_flags; /**< Zero or more isolation flags */
|
||||
int session_group; /**< A session group, or -1 if this port is not in a
|
||||
* session group. */
|
||||
|
||||
/* Unix sockets only: */
|
||||
/** Path for an AF_UNIX address */
|
||||
char unix_addr[FLEXIBLE_ARRAY_MEMBER];
|
||||
} port_cfg_t;
|
||||
|
||||
/** A linked list of lines in a config file. */
|
||||
typedef struct config_line_t {
|
||||
char *key;
|
||||
@ -2637,16 +2772,17 @@ typedef struct {
|
||||
char *User; /**< Name of user to run Tor as. */
|
||||
char *Group; /**< Name of group to run Tor as. */
|
||||
int ORPort; /**< Port to listen on for OR connections. */
|
||||
int SocksPort; /**< Port to listen on for SOCKS connections. */
|
||||
/** Port to listen on for transparent pf/netfilter connections. */
|
||||
int TransPort;
|
||||
int NATDPort; /**< Port to listen on for transparent natd connections. */
|
||||
config_line_t *SocksPort; /**< Ports to listen on for SOCKS connections. */
|
||||
/** Ports to listen on for transparent pf/netfilter connections. */
|
||||
config_line_t *TransPort;
|
||||
config_line_t *NATDPort; /**< Ports to listen on for transparent natd
|
||||
* connections. */
|
||||
int ControlPort; /**< Port to listen on for control connections. */
|
||||
config_line_t *ControlSocket; /**< List of Unix Domain Sockets to listen on
|
||||
* for control connections. */
|
||||
int ControlSocketsGroupWritable; /**< Boolean: Are control sockets g+rw? */
|
||||
int DirPort; /**< Port to listen on for directory connections. */
|
||||
int DNSPort; /**< Port to listen on for DNS requests. */
|
||||
config_line_t *DNSPort; /**< Port to listen on for DNS requests. */
|
||||
int AssumeReachable; /**< Whether to publish our descriptor regardless. */
|
||||
int AuthoritativeDir; /**< Boolean: is this an authoritative directory? */
|
||||
int V1AuthoritativeDir; /**< Boolean: is this an authoritative directory
|
||||
@ -3108,6 +3244,11 @@ typedef struct {
|
||||
/** Should that file be group-readable? */
|
||||
int ControlPortFileGroupReadable;
|
||||
|
||||
#define MAX_MAX_CLIENT_CIRCUITS_PENDING 1024
|
||||
/** Maximum number of non-open general-purpose origin circuits to allow at
|
||||
* once. */
|
||||
int MaxClientCircuitsPending;
|
||||
|
||||
} or_options_t;
|
||||
|
||||
/** Persistent state for an onion router, as saved to disk. */
|
||||
|
@ -1116,14 +1116,12 @@ set_server_advertised(int s)
|
||||
server_is_advertised = s;
|
||||
}
|
||||
|
||||
/** Return true iff we are trying to be a socks proxy. */
|
||||
/** Return true iff we are trying to proxy client connections. */
|
||||
int
|
||||
proxy_mode(const or_options_t *options)
|
||||
{
|
||||
return (options->SocksPort != 0 ||
|
||||
options->TransPort != 0 ||
|
||||
options->NATDPort != 0 ||
|
||||
options->DNSPort != 0);
|
||||
(void)options;
|
||||
return smartlist_len(get_configured_client_ports()) > 0;
|
||||
}
|
||||
|
||||
/** Decide if we're a publishable server. We are a publishable server if:
|
||||
|
Loading…
Reference in New Issue
Block a user