Repository: meejah/txtorcon
Issue: txtorcon 0.19.0 and issue with the tor process

I have experienced some weird behavior with ooniprobe version `2.2.0` and txtorcon version `0.19.0`.

Running any ooniprobe test hangs after the circuit build:
```
Starting Tor
Setting control port as 14733
Setting SOCKS port as 16615
10%: Finishing handshake with directory server
15%: Establishing an encrypted directory connection
20%: Asking for networkstatus consensus
25%: Loading networkstatus consensus
40%: Loading authority key certs
45%: Asking for relay descriptors
50%: Loading relay descriptors
51%: Loading relay descriptors
52%: Loading relay descriptors
54%: Loading relay descriptors
57%: Loading relay descriptors
61%: Loading relay descriptors
64%: Loading relay descriptors
65%: Loading relay descriptors
69%: Loading relay descriptors
74%: Loading relay descriptors
80%: Connecting to the Tor network
90%: Establishing a Tor circuit
90%: Establishing a Tor circuit
90%: Establishing a Tor circuit
100%: Done
Building a TorState
We now have the following circuits:
 * <Circuit 8 BUILT [xxx.xxx.xxx.xxx] for GENERAL>
 * <Circuit 9 BUILT [xxx.xxx.xxx.xxx] for GENERAL>
 * <Circuit 10 BUILT [xxx.xxx.xxx.xxx xxx.xxx.xxx.xxx xxx.xxx.xxx.xxx] for GENERAL>
 * <Circuit 11 BUILT [xxx.xxx.xxx.xxx xxx.xxx.xxx.xxx xxx.xxx.xxx.xxx] for GENERAL>
 * <Circuit 12 BUILT [xxx.xxx.xxx.xxx xxx.xxx.xxx.xxx xxx.xxx.xxx.xxx] for GENERAL>
 * <Circuit 13 BUILT [xxx.xxx.xxx.xxx xxx.xxx.xxx.xxx xxx.xxx.xxx.xxx] for GENERAL>
 * <Circuit 14 BUILT [xxx.xxx.xxx.xxx xxx.xxx.xxx.xxx xxx.xxx.xxx.xxx] for GENERAL>
 * <Circuit 15 EXTENDED [] for GENERAL>
```
`# pip freeze`
```
appdirs==1.4.3
asn1crypto==0.22.0
attrs==16.3.0
Automat==0.5.0
certifi==2017.4.17
cffi==1.10.0
constantly==15.1.0
cryptography==1.8.1
enum34==1.1.6
fte==0.1.3
fteproxy==0.2.19
GeoIP==1.3.2
idna==2.5
incremental==16.10.1
ipaddr==2.1.11
ipaddress==1.0.18
keyrings.alt==1.3
klein==17.2.0
obfsproxy==0.2.13
ooniprobe==2.2.0
packaging==16.8
Parsley==1.3
pyasn1==0.2.3
pyasn1-modules==0.0.8
pycparser==2.17
pycrypto==2.6.1
pydumbnet==1.12.1
pygobject==3.22.0
pyOpenSSL==17.0.0
pyparsing==2.2.0
pypcap==1.1.5
pyptlib==0.0.6
pyxdg==0.25
PyYAML==3.12
scapy==2.3.3
service-identity==16.0.0
six==1.10.0
Twisted==17.1.0
txsocksx==1.15.0.2
txtorcon==0.19.0
Werkzeug==0.12.1
zope.interface==4.4.0
```

You can reproduce this by running:
```
# virtualenv venv
# source venv/bin/activate
# pip install ooniprobe
# ooniprobe -nv web_connectivity --url https://ooni.torproject.org
```
ooniprobe is happy when txtorcon version is downgraded to `0.18.0` (`pip install txtorcon==0.18.0`).

Possible related to: https://github.com/TheTorProject/ooni-backend/issues/107

Changed files:
txtorcon/torconfig.py, txtorcon/controller.py, test/test_endpoints.py

diff --git a/test/test_endpoints.py b/test/test_endpoints.py
index 5cc1899..d912638 100644
--- a/test/test_endpoints.py
+++ b/test/test_endpoints.py
@@ -68,7 +68,7 @@ class EndpointTests(unittest.TestCase):
         )
         self.config = TorConfig(self.protocol)
         self.protocol.answers.append(
-            'config/names=\\nHiddenServiceOptions Virtual\\nControlPort Integer'
+            'config/names=\\nHiddenServiceOptions Virtual\\nControlPort LineList'
         )
         self.protocol.answers.append('HiddenServiceOptions')
         # why do i have to pass a dict for this V but not this ^
diff --git a/txtorcon/controller.py b/txtorcon/controller.py
index 4052529..61b6ac2 100644
--- a/txtorcon/controller.py
+++ b/txtorcon/controller.py
@@ -197,6 +197,20 @@ def launch(reactor,
             functools.partial(delete_file_or_tree, data_directory)
         )
 
+    # things that used launch_tor() had to set ControlPort and/or
+    # SocksPort on the config to pass them, so we honour that here.
+    if control_port is None and _tor_config is not None:
+        try:
+            control_port = config.ControlPort
+        except KeyError:
+            control_port = None
+
+    if socks_port is None and _tor_config is not None:
+        try:
+            socks_port = config.SocksPort
+        except KeyError:
+            socks_port = None
+
     if socks_port is None:
         socks_port = yield available_tcp_port(reactor)
     config.SOCKSPort = socks_port
diff --git a/txtorcon/torconfig.py b/txtorcon/torconfig.py
index f9b434e..0056b91 100644
--- a/txtorcon/torconfig.py
+++ b/txtorcon/torconfig.py
@@ -1214,7 +1214,11 @@ class TorConfig(object):
         string which is also a valid ``torrc`` file
         '''
 
-        for (k, v) in list(self.config.items()) + list(self.unsaved.items()):
+        everything = dict()
+        everything.update(self.config)
+        everything.update(self.unsaved)
+
+        for (k, v) in list(everything.items()):
             if type(v) is _ListWrapper:
                 if k.lower() == 'hiddenservices':
                     for x in v: