1import time
2
3from exabgp.vendoring import six
4
5from exabgp.protocol.family import AFI
6from .connection import Connection
7from .tcp import create, bind
8from .tcp import connect
9from .tcp import MD5
10from .tcp import nagle
11from .tcp import TTL
12from .tcp import TTLv6
13from .tcp import asynchronous
14from .tcp import ready
15from .error import NetworkError
16
17
18class Outgoing(Connection):
19    direction = 'outgoing'
20
21    def __init__(self, afi, peer, local, port=179, md5='', md5_base64=False, ttl=None):
22        Connection.__init__(self, afi, peer, local)
23
24        self.ttl = ttl
25        self.afi = afi
26        self.md5 = md5
27        self.md5_base64 = md5_base64
28        self.port = port
29
30    def _setup(self):
31        try:
32            self.io = create(self.afi)
33            MD5(self.io, self.peer, self.port, self.md5, self.md5_base64)
34            if self.afi == AFI.ipv4:
35                TTL(self.io, self.peer, self.ttl)
36            elif self.afi == AFI.ipv6:
37                TTLv6(self.io, self.peer, self.ttl)
38            if self.local:
39                bind(self.io, self.local, self.afi)
40            asynchronous(self.io, self.peer)
41            return None
42        except Exception as exc:
43            self.io.close()
44            self.io = None
45            return exc
46
47    def _connect(self):
48        if not self.io:
49            setup_issue = self._setup()
50            if setup_issue:
51                return setup_issue
52        try:
53            connect(self.io, self.peer, self.port, self.afi, self.md5)
54            return None
55        except Exception as exc:
56            self.io.close()
57            self.io = None
58            return exc
59
60    def establish(self):
61        last = time.time() - 2.0
62
63        while True:
64            notify = time.time() - last > 1.0
65            if notify:
66                last = time.time()
67
68            if notify:
69                self.logger.debug('attempting connection to %s:%d' % (self.peer, self.port), self.session())
70
71            connect_issue = self._connect()
72            if connect_issue:
73                if notify:
74                    self.logger.debug('connection to %s:%d failed' % (self.peer, self.port), self.session())
75                    self.logger.debug(str(connect_issue), self.session())
76                yield False
77                continue
78
79            connected = False
80            for r, message in ready(self.io):
81                if not r:
82                    yield False
83                    continue
84                connected = True
85
86            if connected:
87                self.success()
88                if not self.local:
89                    self.local = self.io.getsockname()[0]
90                yield True
91                return
92
93            self._setup()
94
95        # nagle(self.io,self.peer)
96        # # Not working after connect() at least on FreeBSD TTL(self.io,self.peer,self.ttl)
97        # yield True
98