-
Notifications
You must be signed in to change notification settings - Fork 0
/
listener.py
152 lines (126 loc) · 4.53 KB
/
listener.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
#
# Proximate - Peer-to-peer social networking
#
# Copyright (c) 2008-2011 Nokia Corporation
#
# All rights reserved.
#
# This software is licensed under The Clear BSD license.
# See the LICENSE file for more details.
#
"""
Listen for incoming TCP connections
"""
from errno import EAGAIN, EINTR
from gobject import io_add_watch, source_remove, timeout_add, IO_IN
import socket
from ioutils import create_tcp_listener
from plugins import rpc_commands, get_plugin_by_type
from support import debug, warning, info
from proximateprotocol import TP_PROTOCOL_TIMEOUT, TP_MAX_CMD_NAME_LEN, \
RPC_MORE_DATA, RPC_CLOSE, RPC_RELEASE, PORT_RETRIES, DEFAULT_PROXIMATE_PORT, \
PLUGIN_TYPE_COMMUNITY
community = None
listener = None
class Connection:
def __init__(self, sock, address):
# Act like the IP network does not exist
if not community.get_network_state(community.IP_NETWORK):
return
self.sock = sock
self.address = address
self.data = []
self.nbytes = 0
# Put self reference to avoid garbage collection
self.iotag = io_add_watch(sock, IO_IN, self.read, self)
self.timeouttag = timeout_add(TP_PROTOCOL_TIMEOUT * 1000, self.close)
def remove_io_notifications(self):
if self.iotag != None:
source_remove(self.iotag)
self.iotag = None
if self.timeouttag != None:
source_remove(self.timeouttag)
self.timeouttag = None
def close(self):
self.remove_io_notifications()
self.sock.close()
self.sock = None
return False
def handle_rpc_message(self, data, eof):
if len(data) == 0:
self.close()
return False
cmd = data[0:TP_MAX_CMD_NAME_LEN].split('\n')[0]
rpchandler = rpc_commands.get(cmd)
if rpchandler == None:
self.close()
return False
payload = data[(len(cmd) + 1):]
status = rpchandler(cmd, payload, eof, self.sock, self.address)
ret = False
if status == RPC_MORE_DATA:
ret = True
elif status == RPC_CLOSE:
self.close()
elif status == RPC_RELEASE:
# We are not interested to gobject events anymore
self.remove_io_notifications()
else:
self.close()
warning('Unknown RPC value: %s\n' %(str(status)))
return ret
def read(self, fd, condition, this):
try:
chunk = self.sock.recv(4096)
except socket.error, (errno, strerror):
ret = (errno == EAGAIN or errno == EINTR)
if not ret:
warning('Listener: Read error (%s): %s\n' %(errno, strerror))
self.close()
return ret
self.nbytes += len(chunk)
self.data.append(chunk)
gotfirstline = (chunk.find('\n') >= 0)
eof = (len(chunk) == 0)
ret = True
if eof or self.nbytes > TP_MAX_CMD_NAME_LEN or gotfirstline:
# Data compaction
data = ''.join(self.data)
self.data = [data]
# The handler may take control over the socket (return False)
ret = self.handle_rpc_message(data, eof)
return ret
def tcp_listener_accept(rfd, conditions):
try:
(sock, address) = rfd.accept()
except socket.error, (errno, strerror):
ret = (errno == EAGAIN or errno == EINTR)
if not ret:
warning('Listener: Socket error (%s): %s\n' % (errno, strerror))
return ret
sock.setblocking(False)
Connection(sock, address)
return True
def init():
""" Bind a default and a random port.
The random port is used for local network communication.
The default port is used to establish remote connections. """
global community
community = get_plugin_by_type(PLUGIN_TYPE_COMMUNITY)
create_tcp_listener(DEFAULT_PROXIMATE_PORT, tcp_listener_accept, reuse=True)
success = False
for i in xrange(PORT_RETRIES):
port = community.get_rpc_port()
if port == DEFAULT_PROXIMATE_PORT:
continue
(rfd, tag) = create_tcp_listener(port, tcp_listener_accept, reuse=True)
if rfd != None:
info('Listening to TCP connections on port %d\n' %(port))
success = True
break
warning('Can not bind to TCP port %d\n' %(port))
# Generate a new port number so that next iteration will not fail
if not community.gen_port():
break
if not success:
warning('Can not listen to TCP connections\n')