We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Duplicate connections to some onion peers
The description is based on messages from `[chan] bitmessage` but I've seen it myself a couple of weeks ago (I thought it's related to my #1394 - wrongly).

![image](https://user-images.githubusercontent.com/4012700/49939372-56add980-fee5-11e8-8f6c-de2f83123ebd.png)

Changes proposed today:
```patch
diff --git a/src/network/connectionpool.py b/src/network/connectionpool.py
index e599cdf..c5ba701 100644
--- a/src/network/connectionpool.py
+++ b/src/network/connectionpool.py
@@ -93,7 +93,7 @@ class BMConnectionPool(object):
                     del self.inboundConnections[connection.destination.host]
                 except KeyError:
                     pass
-        connection.close()
+        connection.handle_close()
 
     def getListeningIP(self):
         if BMConfigParser().safeGet("bitmessagesettings", "onionhostname").endswith(".onion"):

```

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `src/network/connectionpool.py`
Content:
```
1 from ConfigParser import NoOptionError, NoSectionError
2 import errno
3 import socket
4 import time
5 import random
6 import re
7 
8 from bmconfigparser import BMConfigParser
9 from debug import logger
10 import helper_bootstrap
11 import knownnodes
12 from network.proxy import Proxy
13 from network.tcp import TCPServer, Socks5BMConnection, Socks4aBMConnection, TCPConnection
14 from network.udp import UDPSocket
15 from network.connectionchooser import chooseConnection
16 import network.asyncore_pollchoose as asyncore
17 import protocol
18 from singleton import Singleton
19 import state
20 import helper_random
21 
22 
23 @Singleton
24 class BMConnectionPool(object):
25     def __init__(self):
26         asyncore.set_rates(
27                 BMConfigParser().safeGetInt("bitmessagesettings", "maxdownloadrate"),
28                 BMConfigParser().safeGetInt("bitmessagesettings", "maxuploadrate"))
29         self.outboundConnections = {}
30         self.inboundConnections = {}
31         self.listeningSockets = {}
32         self.udpSockets = {}
33         self.streams = []
34         self.lastSpawned = 0
35         self.spawnWait = 2 
36         self.bootstrapped = False
37 
38     def connectToStream(self, streamNumber):
39         self.streams.append(streamNumber)
40 
41     def getConnectionByAddr(self, addr):
42         if addr in self.inboundConnections:
43             return self.inboundConnections[addr]
44         try:
45             if addr.host in self.inboundConnections:
46                 return self.inboundConnections[addr.host]
47         except AttributeError:
48             pass
49         if addr in self.outboundConnections:
50             return self.outboundConnections[addr]
51         try:
52             if addr.host in self.udpSockets:
53                 return self.udpSockets[addr.host]
54         except AttributeError:
55             pass
56         raise KeyError
57 
58     def isAlreadyConnected(self, nodeid):
59         for i in self.inboundConnections.values() + self.outboundConnections.values():
60             try:
61                 if nodeid == i.nodeid:
62                     return True
63             except AttributeError:
64                 pass
65         return False
66 
67     def addConnection(self, connection):
68         if isinstance(connection, UDPSocket):
69             return
70         if connection.isOutbound:
71             self.outboundConnections[connection.destination] = connection
72         else:
73             if connection.destination.host in self.inboundConnections:
74                 self.inboundConnections[connection.destination] = connection
75             else:
76                 self.inboundConnections[connection.destination.host] = connection
77 
78     def removeConnection(self, connection):
79         if isinstance(connection, UDPSocket):
80             del self.udpSockets[connection.listening.host]
81         elif isinstance(connection, TCPServer):
82             del self.listeningSockets[state.Peer(connection.destination.host, connection.destination.port)]
83         elif connection.isOutbound:
84             try:
85                 del self.outboundConnections[connection.destination]
86             except KeyError:
87                 pass
88         else:
89             try:
90                 del self.inboundConnections[connection.destination]
91             except KeyError:
92                 try:
93                     del self.inboundConnections[connection.destination.host]
94                 except KeyError:
95                     pass
96         connection.close()
97 
98     def getListeningIP(self):
99         if BMConfigParser().safeGet("bitmessagesettings", "onionhostname").endswith(".onion"):
100             host = BMConfigParser().safeGet("bitmessagesettings", "onionbindip")
101         else:
102             host = '127.0.0.1'
103         if BMConfigParser().safeGetBoolean("bitmessagesettings", "sockslisten") or \
104                 BMConfigParser().get("bitmessagesettings", "socksproxytype") == "none":
105             # python doesn't like bind + INADDR_ANY?
106             #host = socket.INADDR_ANY
107             host = BMConfigParser().get("network", "bind")
108         return host
109 
110     def startListening(self, bind=None):
111         if bind is None:
112             bind = self.getListeningIP()
113         port = BMConfigParser().safeGetInt("bitmessagesettings", "port")
114         # correct port even if it changed
115         ls = TCPServer(host=bind, port=port)
116         self.listeningSockets[ls.destination] = ls
117 
118     def startUDPSocket(self, bind=None):
119         if bind is None:
120             host = self.getListeningIP()
121             udpSocket = UDPSocket(host=host, announcing=True)
122         else:
123             if bind is False:
124                 udpSocket = UDPSocket(announcing=False)
125             else:
126                 udpSocket = UDPSocket(host=bind, announcing=True)
127         self.udpSockets[udpSocket.listening.host] = udpSocket
128 
129     def loop(self):
130         # defaults to empty loop if outbound connections are maxed
131         spawnConnections = False
132         acceptConnections = True
133         if BMConfigParser().safeGetBoolean('bitmessagesettings', 'dontconnect'):
134             acceptConnections = False
135         elif BMConfigParser().safeGetBoolean('bitmessagesettings', 'sendoutgoingconnections'):
136             spawnConnections = True
137         if BMConfigParser().get('bitmessagesettings', 'socksproxytype')[0:5] == 'SOCKS' and \
138             (not BMConfigParser().getboolean('bitmessagesettings', 'sockslisten') and \
139             ".onion" not in BMConfigParser().get('bitmessagesettings', 'onionhostname')):
140             acceptConnections = False
141 
142         if spawnConnections:
143             if not knownnodes.knownNodesActual:
144                 helper_bootstrap.dns()
145             if not self.bootstrapped:
146                 self.bootstrapped = True
147                 Proxy.proxy = (BMConfigParser().safeGet("bitmessagesettings", "sockshostname"),
148                         BMConfigParser().safeGetInt("bitmessagesettings", "socksport"))
149                 # TODO AUTH
150                 # TODO reset based on GUI settings changes
151                 try:
152                     if not BMConfigParser().get("network", "onionsocksproxytype").startswith("SOCKS"):
153                         raise NoOptionError
154                     Proxy.onionproxy = (BMConfigParser().get("network", "onionsockshostname"),
155                         BMConfigParser().getint("network", "onionsocksport"))
156                 except (NoOptionError, NoSectionError):
157                     Proxy.onionproxy = None
158             established = sum(1 for c in self.outboundConnections.values() if (c.connected and c.fullyEstablished))
159             pending = len(self.outboundConnections) - established
160             if established < BMConfigParser().safeGetInt("bitmessagesettings", "maxoutboundconnections"):
161                 for i in range(state.maximumNumberOfHalfOpenConnections - pending):
162                     try:
163                         chosen = chooseConnection(helper_random.randomchoice(self.streams))
164                     except ValueError:
165                         continue
166                     if chosen in self.outboundConnections:
167                         continue
168                     if chosen.host in self.inboundConnections:
169                         continue
170                     # don't connect to self
171                     if chosen in state.ownAddresses:
172                         continue
173     
174                     #for c in self.outboundConnections:
175                     #    if chosen == c.destination:
176                     #        continue
177                     #for c in self.inboundConnections:
178                     #    if chosen.host == c.destination.host:
179                     #        continue
180                     try:
181                         if chosen.host.endswith(".onion") and Proxy.onionproxy is not None:
182                             if BMConfigParser().get("network", "onionsocksproxytype") == "SOCKS5":
183                                 self.addConnection(Socks5BMConnection(chosen))
184                             elif BMConfigParser().get("network", "onionsocksproxytype") == "SOCKS4a":
185                                 self.addConnection(Socks4aBMConnection(chosen))
186                         elif BMConfigParser().safeGet("bitmessagesettings", "socksproxytype") == "SOCKS5":
187                             self.addConnection(Socks5BMConnection(chosen))
188                         elif BMConfigParser().safeGet("bitmessagesettings", "socksproxytype") == "SOCKS4a":
189                             self.addConnection(Socks4aBMConnection(chosen))
190                         else:
191                             self.addConnection(TCPConnection(chosen))
192                     except socket.error as e:
193                         if e.errno == errno.ENETUNREACH:
194                             continue
195                     except (NoSectionError, NoOptionError):
196                         # shouldn't happen
197                         pass
198 
199                     self.lastSpawned = time.time()
200         else:
201             for i in (
202                     self.inboundConnections.values() +
203                     self.outboundConnections.values()
204             ):
205                 i.set_state("close")
206                 # FIXME: rating will be increased after next connection
207                 i.handle_close()
208 
209         if acceptConnections:
210             if not self.listeningSockets:
211                 if BMConfigParser().safeGet("network", "bind") == '':
212                     self.startListening()
213                 else:
214                     for bind in re.sub("[^\w.]+", " ", BMConfigParser().safeGet("network", "bind")).split():
215                         self.startListening(bind)
216                 logger.info('Listening for incoming connections.')
217             if not self.udpSockets:
218                 if BMConfigParser().safeGet("network", "bind") == '':
219                     self.startUDPSocket()
220                 else:
221                     for bind in re.sub("[^\w.]+", " ", BMConfigParser().safeGet("network", "bind")).split():
222                         self.startUDPSocket(bind)
223                     self.startUDPSocket(False)
224                 logger.info('Starting UDP socket(s).')
225         else:
226             if self.listeningSockets:
227                 for i in self.listeningSockets.values():
228                     i.close_reason = "Stopping listening"
229                     i.accepting = i.connecting = i.connected = False
230                 logger.info('Stopped listening for incoming connections.')
231             if self.udpSockets:
232                 for i in self.udpSockets.values():
233                     i.close_reason = "Stopping UDP socket"
234                     i.accepting = i.connecting = i.connected = False
235                 logger.info('Stopped udp sockets.')
236 
237         loopTime = float(self.spawnWait)
238         if self.lastSpawned < time.time() - self.spawnWait:
239             loopTime = 2.0
240         asyncore.loop(timeout=loopTime, count=1000)
241 
242         reaper = []
243         for i in self.inboundConnections.values() + self.outboundConnections.values():
244             minTx = time.time() - 20
245             if i.fullyEstablished:
246                 minTx -= 300 - 20
247             if i.lastTx < minTx:
248                 if i.fullyEstablished:
249                     i.append_write_buf(protocol.CreatePacket('ping'))
250                 else:
251                     i.close_reason = "Timeout (%is)" % (time.time() - i.lastTx) 
252                     i.set_state("close")
253         for i in self.inboundConnections.values() + self.outboundConnections.values() + self.listeningSockets.values() + self.udpSockets.values():
254             if not (i.accepting or i.connecting or i.connected):
255                 reaper.append(i)
256             else:
257                 try:
258                     if i.state == "close":
259                         reaper.append(i)
260                 except AttributeError:
261                     pass
262         for i in reaper:
263             self.removeConnection(i)
264
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/src/network/connectionpool.py b/src/network/connectionpool.py
--- a/src/network/connectionpool.py
+++ b/src/network/connectionpool.py
@@ -93,7 +93,7 @@
                     del self.inboundConnections[connection.destination.host]
                 except KeyError:
                     pass
-        connection.close()
+        connection.handle_close()
 
     def getListeningIP(self):
         if BMConfigParser().safeGet("bitmessagesettings", "onionhostname").endswith(".onion"):
@@ -202,7 +202,6 @@
                     self.inboundConnections.values() +
                     self.outboundConnections.values()
             ):
-                i.set_state("close")
                 # FIXME: rating will be increased after next connection
                 i.handle_close()

{"golden_diff": "diff --git a/src/network/connectionpool.py b/src/network/connectionpool.py\n--- a/src/network/connectionpool.py\n+++ b/src/network/connectionpool.py\n@@ -93,7 +93,7 @@\n                     del self.inboundConnections[connection.destination.host]\n                 except KeyError:\n                     pass\n-        connection.close()\n+        connection.handle_close()\n \n     def getListeningIP(self):\n         if BMConfigParser().safeGet(\"bitmessagesettings\", \"onionhostname\").endswith(\".onion\"):\n@@ -202,7 +202,6 @@\n                     self.inboundConnections.values() +\n                     self.outboundConnections.values()\n             ):\n-                i.set_state(\"close\")\n                 # FIXME: rating will be increased after next connection\n                 i.handle_close()\n", "issue": "Duplicate connections to some onion peers\nThe description is based on messages from `[chan] bitmessage` but I've seen it myself a couple of weeks ago (I thought it's related to my #1394 - wrongly).\r\n\r\n![image](https://user-images.githubusercontent.com/4012700/49939372-56add980-fee5-11e8-8f6c-de2f83123ebd.png)\r\n\r\nChanges proposed today:\r\n```patch\r\ndiff --git a/src/network/connectionpool.py b/src/network/connectionpool.py\r\nindex e599cdf..c5ba701 100644\r\n--- a/src/network/connectionpool.py\r\n+++ b/src/network/connectionpool.py\r\n@@ -93,7 +93,7 @@ class BMConnectionPool(object):\r\n                     del self.inboundConnections[connection.destination.host]\r\n                 except KeyError:\r\n                     pass\r\n-        connection.close()\r\n+        connection.handle_close()\r\n \r\n     def getListeningIP(self):\r\n         if BMConfigParser().safeGet(\"bitmessagesettings\", \"onionhostname\").endswith(\".onion\"):\r\n\r\n```\n", "before_files": [{"content": "from ConfigParser import NoOptionError, NoSectionError\nimport errno\nimport socket\nimport time\nimport random\nimport re\n\nfrom bmconfigparser import BMConfigParser\nfrom debug import logger\nimport helper_bootstrap\nimport knownnodes\nfrom network.proxy import Proxy\nfrom network.tcp import TCPServer, Socks5BMConnection, Socks4aBMConnection, TCPConnection\nfrom network.udp import UDPSocket\nfrom network.connectionchooser import chooseConnection\nimport network.asyncore_pollchoose as asyncore\nimport protocol\nfrom singleton import Singleton\nimport state\nimport helper_random\n\n\n@Singleton\nclass BMConnectionPool(object):\n    def __init__(self):\n        asyncore.set_rates(\n                BMConfigParser().safeGetInt(\"bitmessagesettings\", \"maxdownloadrate\"),\n                BMConfigParser().safeGetInt(\"bitmessagesettings\", \"maxuploadrate\"))\n        self.outboundConnections = {}\n        self.inboundConnections = {}\n        self.listeningSockets = {}\n        self.udpSockets = {}\n        self.streams = []\n        self.lastSpawned = 0\n        self.spawnWait = 2 \n        self.bootstrapped = False\n\n    def connectToStream(self, streamNumber):\n        self.streams.append(streamNumber)\n\n    def getConnectionByAddr(self, addr):\n        if addr in self.inboundConnections:\n            return self.inboundConnections[addr]\n        try:\n            if addr.host in self.inboundConnections:\n                return self.inboundConnections[addr.host]\n        except AttributeError:\n            pass\n        if addr in self.outboundConnections:\n            return self.outboundConnections[addr]\n        try:\n            if addr.host in self.udpSockets:\n                return self.udpSockets[addr.host]\n        except AttributeError:\n            pass\n        raise KeyError\n\n    def isAlreadyConnected(self, nodeid):\n        for i in self.inboundConnections.values() + self.outboundConnections.values():\n            try:\n                if nodeid == i.nodeid:\n                    return True\n            except AttributeError:\n                pass\n        return False\n\n    def addConnection(self, connection):\n        if isinstance(connection, UDPSocket):\n            return\n        if connection.isOutbound:\n            self.outboundConnections[connection.destination] = connection\n        else:\n            if connection.destination.host in self.inboundConnections:\n                self.inboundConnections[connection.destination] = connection\n            else:\n                self.inboundConnections[connection.destination.host] = connection\n\n    def removeConnection(self, connection):\n        if isinstance(connection, UDPSocket):\n            del self.udpSockets[connection.listening.host]\n        elif isinstance(connection, TCPServer):\n            del self.listeningSockets[state.Peer(connection.destination.host, connection.destination.port)]\n        elif connection.isOutbound:\n            try:\n                del self.outboundConnections[connection.destination]\n            except KeyError:\n                pass\n        else:\n            try:\n                del self.inboundConnections[connection.destination]\n            except KeyError:\n                try:\n                    del self.inboundConnections[connection.destination.host]\n                except KeyError:\n                    pass\n        connection.close()\n\n    def getListeningIP(self):\n        if BMConfigParser().safeGet(\"bitmessagesettings\", \"onionhostname\").endswith(\".onion\"):\n            host = BMConfigParser().safeGet(\"bitmessagesettings\", \"onionbindip\")\n        else:\n            host = '127.0.0.1'\n        if BMConfigParser().safeGetBoolean(\"bitmessagesettings\", \"sockslisten\") or \\\n                BMConfigParser().get(\"bitmessagesettings\", \"socksproxytype\") == \"none\":\n            # python doesn't like bind + INADDR_ANY?\n            #host = socket.INADDR_ANY\n            host = BMConfigParser().get(\"network\", \"bind\")\n        return host\n\n    def startListening(self, bind=None):\n        if bind is None:\n            bind = self.getListeningIP()\n        port = BMConfigParser().safeGetInt(\"bitmessagesettings\", \"port\")\n        # correct port even if it changed\n        ls = TCPServer(host=bind, port=port)\n        self.listeningSockets[ls.destination] = ls\n\n    def startUDPSocket(self, bind=None):\n        if bind is None:\n            host = self.getListeningIP()\n            udpSocket = UDPSocket(host=host, announcing=True)\n        else:\n            if bind is False:\n                udpSocket = UDPSocket(announcing=False)\n            else:\n                udpSocket = UDPSocket(host=bind, announcing=True)\n        self.udpSockets[udpSocket.listening.host] = udpSocket\n\n    def loop(self):\n        # defaults to empty loop if outbound connections are maxed\n        spawnConnections = False\n        acceptConnections = True\n        if BMConfigParser().safeGetBoolean('bitmessagesettings', 'dontconnect'):\n            acceptConnections = False\n        elif BMConfigParser().safeGetBoolean('bitmessagesettings', 'sendoutgoingconnections'):\n            spawnConnections = True\n        if BMConfigParser().get('bitmessagesettings', 'socksproxytype')[0:5] == 'SOCKS' and \\\n            (not BMConfigParser().getboolean('bitmessagesettings', 'sockslisten') and \\\n            \".onion\" not in BMConfigParser().get('bitmessagesettings', 'onionhostname')):\n            acceptConnections = False\n\n        if spawnConnections:\n            if not knownnodes.knownNodesActual:\n                helper_bootstrap.dns()\n            if not self.bootstrapped:\n                self.bootstrapped = True\n                Proxy.proxy = (BMConfigParser().safeGet(\"bitmessagesettings\", \"sockshostname\"),\n                        BMConfigParser().safeGetInt(\"bitmessagesettings\", \"socksport\"))\n                # TODO AUTH\n                # TODO reset based on GUI settings changes\n                try:\n                    if not BMConfigParser().get(\"network\", \"onionsocksproxytype\").startswith(\"SOCKS\"):\n                        raise NoOptionError\n                    Proxy.onionproxy = (BMConfigParser().get(\"network\", \"onionsockshostname\"),\n                        BMConfigParser().getint(\"network\", \"onionsocksport\"))\n                except (NoOptionError, NoSectionError):\n                    Proxy.onionproxy = None\n            established = sum(1 for c in self.outboundConnections.values() if (c.connected and c.fullyEstablished))\n            pending = len(self.outboundConnections) - established\n            if established < BMConfigParser().safeGetInt(\"bitmessagesettings\", \"maxoutboundconnections\"):\n                for i in range(state.maximumNumberOfHalfOpenConnections - pending):\n                    try:\n                        chosen = chooseConnection(helper_random.randomchoice(self.streams))\n                    except ValueError:\n                        continue\n                    if chosen in self.outboundConnections:\n                        continue\n                    if chosen.host in self.inboundConnections:\n                        continue\n                    # don't connect to self\n                    if chosen in state.ownAddresses:\n                        continue\n    \n                    #for c in self.outboundConnections:\n                    #    if chosen == c.destination:\n                    #        continue\n                    #for c in self.inboundConnections:\n                    #    if chosen.host == c.destination.host:\n                    #        continue\n                    try:\n                        if chosen.host.endswith(\".onion\") and Proxy.onionproxy is not None:\n                            if BMConfigParser().get(\"network\", \"onionsocksproxytype\") == \"SOCKS5\":\n                                self.addConnection(Socks5BMConnection(chosen))\n                            elif BMConfigParser().get(\"network\", \"onionsocksproxytype\") == \"SOCKS4a\":\n                                self.addConnection(Socks4aBMConnection(chosen))\n                        elif BMConfigParser().safeGet(\"bitmessagesettings\", \"socksproxytype\") == \"SOCKS5\":\n                            self.addConnection(Socks5BMConnection(chosen))\n                        elif BMConfigParser().safeGet(\"bitmessagesettings\", \"socksproxytype\") == \"SOCKS4a\":\n                            self.addConnection(Socks4aBMConnection(chosen))\n                        else:\n                            self.addConnection(TCPConnection(chosen))\n                    except socket.error as e:\n                        if e.errno == errno.ENETUNREACH:\n                            continue\n                    except (NoSectionError, NoOptionError):\n                        # shouldn't happen\n                        pass\n\n                    self.lastSpawned = time.time()\n        else:\n            for i in (\n                    self.inboundConnections.values() +\n                    self.outboundConnections.values()\n            ):\n                i.set_state(\"close\")\n                # FIXME: rating will be increased after next connection\n                i.handle_close()\n\n        if acceptConnections:\n            if not self.listeningSockets:\n                if BMConfigParser().safeGet(\"network\", \"bind\") == '':\n                    self.startListening()\n                else:\n                    for bind in re.sub(\"[^\\w.]+\", \" \", BMConfigParser().safeGet(\"network\", \"bind\")).split():\n                        self.startListening(bind)\n                logger.info('Listening for incoming connections.')\n            if not self.udpSockets:\n                if BMConfigParser().safeGet(\"network\", \"bind\") == '':\n                    self.startUDPSocket()\n                else:\n                    for bind in re.sub(\"[^\\w.]+\", \" \", BMConfigParser().safeGet(\"network\", \"bind\")).split():\n                        self.startUDPSocket(bind)\n                    self.startUDPSocket(False)\n                logger.info('Starting UDP socket(s).')\n        else:\n            if self.listeningSockets:\n                for i in self.listeningSockets.values():\n                    i.close_reason = \"Stopping listening\"\n                    i.accepting = i.connecting = i.connected = False\n                logger.info('Stopped listening for incoming connections.')\n            if self.udpSockets:\n                for i in self.udpSockets.values():\n                    i.close_reason = \"Stopping UDP socket\"\n                    i.accepting = i.connecting = i.connected = False\n                logger.info('Stopped udp sockets.')\n\n        loopTime = float(self.spawnWait)\n        if self.lastSpawned < time.time() - self.spawnWait:\n            loopTime = 2.0\n        asyncore.loop(timeout=loopTime, count=1000)\n\n        reaper = []\n        for i in self.inboundConnections.values() + self.outboundConnections.values():\n            minTx = time.time() - 20\n            if i.fullyEstablished:\n                minTx -= 300 - 20\n            if i.lastTx < minTx:\n                if i.fullyEstablished:\n                    i.append_write_buf(protocol.CreatePacket('ping'))\n                else:\n                    i.close_reason = \"Timeout (%is)\" % (time.time() - i.lastTx) \n                    i.set_state(\"close\")\n        for i in self.inboundConnections.values() + self.outboundConnections.values() + self.listeningSockets.values() + self.udpSockets.values():\n            if not (i.accepting or i.connecting or i.connected):\n                reaper.append(i)\n            else:\n                try:\n                    if i.state == \"close\":\n                        reaper.append(i)\n                except AttributeError:\n                    pass\n        for i in reaper:\n            self.removeConnection(i)\n", "path": "src/network/connectionpool.py"}], "after_files": [{"content": "from ConfigParser import NoOptionError, NoSectionError\nimport errno\nimport socket\nimport time\nimport random\nimport re\n\nfrom bmconfigparser import BMConfigParser\nfrom debug import logger\nimport helper_bootstrap\nimport knownnodes\nfrom network.proxy import Proxy\nfrom network.tcp import TCPServer, Socks5BMConnection, Socks4aBMConnection, TCPConnection\nfrom network.udp import UDPSocket\nfrom network.connectionchooser import chooseConnection\nimport network.asyncore_pollchoose as asyncore\nimport protocol\nfrom singleton import Singleton\nimport state\nimport helper_random\n\n\n@Singleton\nclass BMConnectionPool(object):\n    def __init__(self):\n        asyncore.set_rates(\n                BMConfigParser().safeGetInt(\"bitmessagesettings\", \"maxdownloadrate\"),\n                BMConfigParser().safeGetInt(\"bitmessagesettings\", \"maxuploadrate\"))\n        self.outboundConnections = {}\n        self.inboundConnections = {}\n        self.listeningSockets = {}\n        self.udpSockets = {}\n        self.streams = []\n        self.lastSpawned = 0\n        self.spawnWait = 2 \n        self.bootstrapped = False\n\n    def connectToStream(self, streamNumber):\n        self.streams.append(streamNumber)\n\n    def getConnectionByAddr(self, addr):\n        if addr in self.inboundConnections:\n            return self.inboundConnections[addr]\n        try:\n            if addr.host in self.inboundConnections:\n                return self.inboundConnections[addr.host]\n        except AttributeError:\n            pass\n        if addr in self.outboundConnections:\n            return self.outboundConnections[addr]\n        try:\n            if addr.host in self.udpSockets:\n                return self.udpSockets[addr.host]\n        except AttributeError:\n            pass\n        raise KeyError\n\n    def isAlreadyConnected(self, nodeid):\n        for i in self.inboundConnections.values() + self.outboundConnections.values():\n            try:\n                if nodeid == i.nodeid:\n                    return True\n            except AttributeError:\n                pass\n        return False\n\n    def addConnection(self, connection):\n        if isinstance(connection, UDPSocket):\n            return\n        if connection.isOutbound:\n            self.outboundConnections[connection.destination] = connection\n        else:\n            if connection.destination.host in self.inboundConnections:\n                self.inboundConnections[connection.destination] = connection\n            else:\n                self.inboundConnections[connection.destination.host] = connection\n\n    def removeConnection(self, connection):\n        if isinstance(connection, UDPSocket):\n            del self.udpSockets[connection.listening.host]\n        elif isinstance(connection, TCPServer):\n            del self.listeningSockets[state.Peer(connection.destination.host, connection.destination.port)]\n        elif connection.isOutbound:\n            try:\n                del self.outboundConnections[connection.destination]\n            except KeyError:\n                pass\n        else:\n            try:\n                del self.inboundConnections[connection.destination]\n            except KeyError:\n                try:\n                    del self.inboundConnections[connection.destination.host]\n                except KeyError:\n                    pass\n        connection.handle_close()\n\n    def getListeningIP(self):\n        if BMConfigParser().safeGet(\"bitmessagesettings\", \"onionhostname\").endswith(\".onion\"):\n            host = BMConfigParser().safeGet(\"bitmessagesettings\", \"onionbindip\")\n        else:\n            host = '127.0.0.1'\n        if BMConfigParser().safeGetBoolean(\"bitmessagesettings\", \"sockslisten\") or \\\n                BMConfigParser().get(\"bitmessagesettings\", \"socksproxytype\") == \"none\":\n            # python doesn't like bind + INADDR_ANY?\n            #host = socket.INADDR_ANY\n            host = BMConfigParser().get(\"network\", \"bind\")\n        return host\n\n    def startListening(self, bind=None):\n        if bind is None:\n            bind = self.getListeningIP()\n        port = BMConfigParser().safeGetInt(\"bitmessagesettings\", \"port\")\n        # correct port even if it changed\n        ls = TCPServer(host=bind, port=port)\n        self.listeningSockets[ls.destination] = ls\n\n    def startUDPSocket(self, bind=None):\n        if bind is None:\n            host = self.getListeningIP()\n            udpSocket = UDPSocket(host=host, announcing=True)\n        else:\n            if bind is False:\n                udpSocket = UDPSocket(announcing=False)\n            else:\n                udpSocket = UDPSocket(host=bind, announcing=True)\n        self.udpSockets[udpSocket.listening.host] = udpSocket\n\n    def loop(self):\n        # defaults to empty loop if outbound connections are maxed\n        spawnConnections = False\n        acceptConnections = True\n        if BMConfigParser().safeGetBoolean('bitmessagesettings', 'dontconnect'):\n            acceptConnections = False\n        elif BMConfigParser().safeGetBoolean('bitmessagesettings', 'sendoutgoingconnections'):\n            spawnConnections = True\n        if BMConfigParser().get('bitmessagesettings', 'socksproxytype')[0:5] == 'SOCKS' and \\\n            (not BMConfigParser().getboolean('bitmessagesettings', 'sockslisten') and \\\n            \".onion\" not in BMConfigParser().get('bitmessagesettings', 'onionhostname')):\n            acceptConnections = False\n\n        if spawnConnections:\n            if not knownnodes.knownNodesActual:\n                helper_bootstrap.dns()\n            if not self.bootstrapped:\n                self.bootstrapped = True\n                Proxy.proxy = (BMConfigParser().safeGet(\"bitmessagesettings\", \"sockshostname\"),\n                        BMConfigParser().safeGetInt(\"bitmessagesettings\", \"socksport\"))\n                # TODO AUTH\n                # TODO reset based on GUI settings changes\n                try:\n                    if not BMConfigParser().get(\"network\", \"onionsocksproxytype\").startswith(\"SOCKS\"):\n                        raise NoOptionError\n                    Proxy.onionproxy = (BMConfigParser().get(\"network\", \"onionsockshostname\"),\n                        BMConfigParser().getint(\"network\", \"onionsocksport\"))\n                except (NoOptionError, NoSectionError):\n                    Proxy.onionproxy = None\n            established = sum(1 for c in self.outboundConnections.values() if (c.connected and c.fullyEstablished))\n            pending = len(self.outboundConnections) - established\n            if established < BMConfigParser().safeGetInt(\"bitmessagesettings\", \"maxoutboundconnections\"):\n                for i in range(state.maximumNumberOfHalfOpenConnections - pending):\n                    try:\n                        chosen = chooseConnection(helper_random.randomchoice(self.streams))\n                    except ValueError:\n                        continue\n                    if chosen in self.outboundConnections:\n                        continue\n                    if chosen.host in self.inboundConnections:\n                        continue\n                    # don't connect to self\n                    if chosen in state.ownAddresses:\n                        continue\n    \n                    #for c in self.outboundConnections:\n                    #    if chosen == c.destination:\n                    #        continue\n                    #for c in self.inboundConnections:\n                    #    if chosen.host == c.destination.host:\n                    #        continue\n                    try:\n                        if chosen.host.endswith(\".onion\") and Proxy.onionproxy is not None:\n                            if BMConfigParser().get(\"network\", \"onionsocksproxytype\") == \"SOCKS5\":\n                                self.addConnection(Socks5BMConnection(chosen))\n                            elif BMConfigParser().get(\"network\", \"onionsocksproxytype\") == \"SOCKS4a\":\n                                self.addConnection(Socks4aBMConnection(chosen))\n                        elif BMConfigParser().safeGet(\"bitmessagesettings\", \"socksproxytype\") == \"SOCKS5\":\n                            self.addConnection(Socks5BMConnection(chosen))\n                        elif BMConfigParser().safeGet(\"bitmessagesettings\", \"socksproxytype\") == \"SOCKS4a\":\n                            self.addConnection(Socks4aBMConnection(chosen))\n                        else:\n                            self.addConnection(TCPConnection(chosen))\n                    except socket.error as e:\n                        if e.errno == errno.ENETUNREACH:\n                            continue\n                    except (NoSectionError, NoOptionError):\n                        # shouldn't happen\n                        pass\n\n                    self.lastSpawned = time.time()\n        else:\n            for i in (\n                    self.inboundConnections.values() +\n                    self.outboundConnections.values()\n            ):\n                # FIXME: rating will be increased after next connection\n                i.handle_close()\n\n        if acceptConnections:\n            if not self.listeningSockets:\n                if BMConfigParser().safeGet(\"network\", \"bind\") == '':\n                    self.startListening()\n                else:\n                    for bind in re.sub(\"[^\\w.]+\", \" \", BMConfigParser().safeGet(\"network\", \"bind\")).split():\n                        self.startListening(bind)\n                logger.info('Listening for incoming connections.')\n            if not self.udpSockets:\n                if BMConfigParser().safeGet(\"network\", \"bind\") == '':\n                    self.startUDPSocket()\n                else:\n                    for bind in re.sub(\"[^\\w.]+\", \" \", BMConfigParser().safeGet(\"network\", \"bind\")).split():\n                        self.startUDPSocket(bind)\n                    self.startUDPSocket(False)\n                logger.info('Starting UDP socket(s).')\n        else:\n            if self.listeningSockets:\n                for i in self.listeningSockets.values():\n                    i.close_reason = \"Stopping listening\"\n                    i.accepting = i.connecting = i.connected = False\n                logger.info('Stopped listening for incoming connections.')\n            if self.udpSockets:\n                for i in self.udpSockets.values():\n                    i.close_reason = \"Stopping UDP socket\"\n                    i.accepting = i.connecting = i.connected = False\n                logger.info('Stopped udp sockets.')\n\n        loopTime = float(self.spawnWait)\n        if self.lastSpawned < time.time() - self.spawnWait:\n            loopTime = 2.0\n        asyncore.loop(timeout=loopTime, count=1000)\n\n        reaper = []\n        for i in self.inboundConnections.values() + self.outboundConnections.values():\n            minTx = time.time() - 20\n            if i.fullyEstablished:\n                minTx -= 300 - 20\n            if i.lastTx < minTx:\n                if i.fullyEstablished:\n                    i.append_write_buf(protocol.CreatePacket('ping'))\n                else:\n                    i.close_reason = \"Timeout (%is)\" % (time.time() - i.lastTx) \n                    i.set_state(\"close\")\n        for i in self.inboundConnections.values() + self.outboundConnections.values() + self.listeningSockets.values() + self.udpSockets.values():\n            if not (i.accepting or i.connecting or i.connected):\n                reaper.append(i)\n            else:\n                try:\n                    if i.state == \"close\":\n                        reaper.append(i)\n                except AttributeError:\n                    pass\n        for i in reaper:\n            self.removeConnection(i)\n", "path": "src/network/connectionpool.py"}]}