Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Configurable hrtb_*, Float param support #19

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
176 changes: 79 additions & 97 deletions sippy/MyConfigParser.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,71 +29,73 @@
from ConfigParser import RawConfigParser
from SipConf import SipConf

SUPPORTED_OPTIONS = { \
'acct_enable': ('B', 'enable or disable Radius accounting'), \
'precise_acct': ('B', 'do Radius accounting with millisecond precision'), \
'alive_acct_int': ('I', 'interval for sending alive Radius accounting in ' \
'second (0 to disable alive accounting)'), \
'config': ('S', 'load configuration from file (path to file)'), \
'auth_enable': ('B', 'enable or disable Radius authentication'), \
'b2bua_socket': ('S', 'path to the B2BUA command socket or address to listen ' \
'for commands in the format "udp:host[:port]"'), \
'digest_auth': ('B', 'enable or disable SIP Digest authentication of ' \
'incoming INVITE requests'), \
'foreground': ('B', 'run in foreground'), \
'hide_call_id': ('B', 'do not pass Call-ID header value from ingress call ' \
'leg to egress call leg'), \
'keepalive_ans': ('I', 'send periodic "keep-alive" re-INVITE requests on ' \
'answering (ingress) call leg and disconnect a call ' \
'if the re-INVITE fails (period in seconds, 0 to ' \
'disable)'), \
'keepalive_orig': ('I', 'send periodic "keep-alive" re-INVITE requests on ' \
'originating (egress) call leg and disconnect a call ' \
'if the re-INVITE fails (period in seconds, 0 to ' \
'disable)'), \
'logfile': ('S', 'path to the B2BUA log file'), \
'max_credit_time': ('I', 'upper limit of session time for all calls in ' \
'seconds'), \
'max_radiusclients': ('I', 'maximum number of Radius Client helper ' \
'processes to start'), \
'pidfile': ('S', 'path to the B2BUA PID file'), \
'radiusclient.conf': ('S', 'path to the radiusclient.conf file'), \
'sip_address': ('S', 'local SIP address to listen for incoming SIP requests ' \
'("*", "0.0.0.0" or "::" to listen on all IPv4 ' \
SUPPORTED_OPTIONS = {
'acct_enable': ('B', 'enable or disable Radius accounting'),
'precise_acct': ('B', 'do Radius accounting with millisecond precision'),
'alive_acct_int': ('I', 'interval for sending alive Radius accounting in '
'second (0 to disable alive accounting)'),
'config': ('S', 'load configuration from file (path to file)'),
'auth_enable': ('B', 'enable or disable Radius authentication'),
'b2bua_socket': ('S', 'path to the B2BUA command socket or address to listen '
'for commands in the format "udp:host[:port]"'),
'digest_auth': ('B', 'enable or disable SIP Digest authentication of '
'incoming INVITE requests'),
'foreground': ('B', 'run in foreground'),
'hide_call_id': ('B', 'do not pass Call-ID header value from ingress call '
'leg to egress call leg'),
'keepalive_ans': ('I', 'send periodic "keep-alive" re-INVITE requests on '
'answering (ingress) call leg and disconnect a call '
'if the re-INVITE fails (period in seconds, 0 to '
'disable)'),
'keepalive_orig': ('I', 'send periodic "keep-alive" re-INVITE requests on '
'originating (egress) call leg and disconnect a call '
'if the re-INVITE fails (period in seconds, 0 to '
'disable)'),
'logfile': ('S', 'path to the B2BUA log file'),
'max_credit_time': ('I', 'upper limit of session time for all calls in '
'seconds'),
'max_radiusclients': ('I', 'maximum number of Radius Client helper '
'processes to start'),
'pidfile': ('S', 'path to the B2BUA PID file'),
'radiusclient.conf': ('S', 'path to the radiusclient.conf file'),
'sip_address': ('S', 'local SIP address to listen for incoming SIP requests '
'("*", "0.0.0.0" or "::" to listen on all IPv4 '
'or IPv6 interfaces)'),
'sip_port': ('I', 'local UDP port to listen for incoming SIP requests'), \
'start_acct_enable': ('B', 'enable start Radius accounting'), \
'static_route': ('S', 'static route for all SIP calls'), \
'static_tr_in': ('S', 'translation rule (regexp) to apply to all incoming ' \
'(ingress) destination numbers'), \
'static_tr_out': ('S', 'translation rule (regexp) to apply to all outgoing ' \
'(egress) destination numbers'), \
'allowed_pts': ('S', 'list of allowed media (RTP) IANA-assigned payload ' \
'types that the B2BUA will pass from input to ' \
'output, payload types not in this list will be ' \
'filtered out (comma separated list)'), \
'pass_headers': ('S', 'list of SIP header field names that the B2BUA will ' \
'pass from ingress call leg to egress call leg ' \
'unmodified (comma-separated list)'), \
'accept_ips': ('S', 'IP addresses that we will only be accepting incoming ' \
'calls from (comma-separated list). If the parameter ' \
'is not specified, we will accept from any IP and ' \
'then either try to authenticate if authentication ' \
'sip_port': ('I', 'local UDP port to listen for incoming SIP requests'),
'start_acct_enable': ('B', 'enable start Radius accounting'),
'static_route': ('S', 'static route for all SIP calls'),
'static_tr_in': ('S', 'translation rule (regexp) to apply to all incoming '
'(ingress) destination numbers'),
'static_tr_out': ('S', 'translation rule (regexp) to apply to all outgoing '
'(egress) destination numbers'),
'allowed_pts': ('S', 'list of allowed media (RTP) IANA-assigned payload '
'types that the B2BUA will pass from input to '
'output, payload types not in this list will be '
'filtered out (comma separated list)'),
'pass_headers': ('S', 'list of SIP header field names that the B2BUA will '
'pass from ingress call leg to egress call leg '
'unmodified (comma-separated list)'),
'accept_ips': ('S', 'IP addresses that we will only be accepting incoming '
'calls from (comma-separated list). If the parameter '
'is not specified, we will accept from any IP and '
'then either try to authenticate if authentication '
'is enabled, or just let them to pass through'),
'digest_auth_only': ('B', 'only use SIP Digest method to authenticate ' \
'incoming INVITE requests. If the option is not ' \
'specified or set to "off" then B2BUA will try to ' \
'digest_auth_only': ('B', 'only use SIP Digest method to authenticate '
'incoming INVITE requests. If the option is not '
'specified or set to "off" then B2BUA will try to '
'do remote IP authentication first and if that fails '
'then send a challenge and re-authenticate when ' \
'challenge response comes in'), \
'rtp_proxy_clients': ('S', 'comma-separated list of paths or addresses of the ' \
'RTPproxy control socket. Address in the format ' \
'"udp:host[:port]" (comma-separated list)'), \
'sip_proxy': ('S', 'address of the helper proxy to handle "REGISTER" ' \
'and "SUBSCRIBE" messages. Address in the format ' \
'then send a challenge and re-authenticate when '
'challenge response comes in'),
'rtp_proxy_clients': ('S', 'comma-separated list of paths or addresses of the '
'RTPproxy control socket. Address in the format '
'"udp:host[:port]" (comma-separated list)'),
'sip_proxy': ('S', 'address of the helper proxy to handle "REGISTER" '
'and "SUBSCRIBE" messages. Address in the format '
'"host[:port]"'),
'nat_traversal': ('B', 'enable NAT traversal for signalling'), \
'xmpp_b2bua_id': ('I', 'ID passed to the XMPP socket server')}
'nat_traversal': ('B', 'enable NAT traversal for signalling'),
'xmpp_b2bua_id': ('I', 'ID passed to the XMPP socket server'),
'hrtb_retr_ival': ('F', 'Heartbeat retransmit value for rtpproxy client'),
'hrtb_ival': ('F', 'Heartbeat interval for rpproxy client')}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line.

class MyConfigParser(RawConfigParser):
default_section = None
Expand All @@ -108,11 +110,13 @@ def __init__(self, default_section = 'general'):
def __getitem__(self, key):
if key.startswith('_'):
return self._private_keys[key]
value_type = SUPPORTED_OPTIONS[key][0]
if value_type == 'B':
value_type = SUPPORTED_OPTIONS[key][0]
if value_type == 'B':
return self.getboolean(self.default_section, key)
elif value_type == 'I':
return self.getint(self.default_section, key)
elif value_type == 'F':
return self.getfloat(self.default_section, key)
return self.get(self.default_section, key)

def __setitem__(self, key, value):
Expand Down Expand Up @@ -146,7 +150,7 @@ def get_longopts(self):
def read(self, fname):
RawConfigParser.readfp(self, open(fname))
for key in tuple(self.options(self.default_section)):
self.check_and_set(key, RawConfigParser.get(self, \
self.check_and_set(key, RawConfigParser.get(self,
self.default_section, key), False)

def check_and_set(self, key, value, compat = True):
Expand All @@ -157,7 +161,7 @@ def check_and_set(self, key, value, compat = True):
if self.has_key('_rtp_proxy_clients'):
self['_rtp_proxy_clients'].append(value)
else:
self['_rtp_proxy_clients'] = [value,]
self['_rtp_proxy_clients'] = [value, ]
if self.has_key('rtp_proxy_clients'):
self['rtp_proxy_clients'] += ',' + value
else:
Expand All @@ -168,25 +172,27 @@ def check_and_set(self, key, value, compat = True):
if self.has_key('_pass_headers'):
self['_pass_headers'].append(value)
else:
self['_pass_headers'] = [value,]
self['_pass_headers'] = [value, ]
if self.has_key('pass_headers'):
self['pass_headers'] += ',' + value
else:
self['pass_headers'] = value
return

value_type = SUPPORTED_OPTIONS[key][0]
value_type = SUPPORTED_OPTIONS[key][0]
if value_type == 'B':
if value.lower() not in self._boolean_states:
raise ValueError, 'Not a boolean: %s' % value
raise ValueError('Not a boolean: {}'.format(value))
elif value_type == 'I':
_value = int(value)
elif value_type == 'F':
_value = float(value)
if key in ('keepalive_ans', 'keepalive_orig'):
if _value < 0:
raise ValueError, 'keepalive_ans should be non-negative'
raise ValueError('keepalive_ans should be non-negative')
elif key == 'max_credit_time':
if _value <= 0:
raise ValueError, 'max_credit_time should be more than zero'
raise ValueError('max_credit_time should be more than zero')
elif key == 'allowed_pts':
self['_allowed_pts'] = [int(x) for x in value.split(',')]
elif key in ('accept_ips', 'rtp_proxy_clients'):
Expand All @@ -203,7 +209,7 @@ def check_and_set(self, key, value, compat = True):
self['_sip_address'] = value
elif key == 'sip_port':
if _value <= 0 or _value > 65535:
raise ValueError, 'sip_port should be in the range 1-65535'
raise ValueError('sip_port should be in the range 1-65535')
self['_sip_port'] = _value
self[key] = value

Expand All @@ -215,32 +221,8 @@ def options_help(self):
value = 'on/off'
elif value_type == 'I':
value = 'number'
elif value_type == 'F':
value = 'float'
else:
value = '"string"'
print '--%s=%s\n\t%s\n' % (option, value, helptext)

if __name__ == '__main__':
m = MyConfigParser()
m['_foo'] = 'bar'
m['b2bua_socket'] = 'bar1'
m['acct_enable'] = True
m['auth_enable'] = 'False'
assert m.has_key('_foo')
assert m['_foo'] == 'bar'
assert m['b2bua_socket'] == 'bar1'
assert m.get('_foo') == 'bar'
assert m.get('b2bua_socket') == 'bar1'
assert m.get('general', 'b2bua_socket') == 'bar1'
assert m.get('acct_enable')
assert not m.get('auth_enable')
m.check_and_set('keepalive_ans', '15')
assert m['keepalive_ans'] == 15
m.check_and_set('pass_header', 'a')
m.check_and_set('pass_header', 'b')
assert m['pass_headers'] == 'a,b'
assert m['_pass_headers'][0] == 'a'
assert m['_pass_headers'][1] == 'b'
m.check_and_set('accept_ips', '1.2.3.4, 5.6.7.8')
assert m['accept_ips'] == '1.2.3.4, 5.6.7.8'
assert m['_accept_ips'][0] == '1.2.3.4'
assert m['_accept_ips'][1] == '5.6.7.8'
1 change: 1 addition & 0 deletions sippy/Rtp_proxy_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ def __init__(self, global_config, *address, **kwargs):
if kwargs.has_key('no_version_check'):
no_version_check = kwargs['no_version_check']
del kwargs['no_version_check']

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove the whole patch (Rtp_proxy_client.py).

if len(address) == 0 and kwargs.has_key('spath'):
a = kwargs['spath']
del kwargs['spath']
Expand Down
20 changes: 14 additions & 6 deletions sippy/b2bua_radius.py
Original file line number Diff line number Diff line change
Expand Up @@ -750,14 +750,26 @@ def main_func():
global_config.write(open(writeconf, 'w'))

if not global_config['foreground']:
daemonize(logfile = global_config['logfile'])
try:
daemonize(logfile = global_config['logfile'])
except IOError as e:
print("IOError: {}".format(e))
sys.exit(e.errno)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line.

global_config['_sip_logger'] = SipLogger('b2bua')

if len(rtp_proxy_clients) > 0:
global_config['_rtp_proxy_clients'] = []
for address in rtp_proxy_clients:
global_config['_rtp_proxy_clients'].append(Rtp_proxy_client(global_config, spath = address))
client = Rtp_proxy_client(global_config, spath = address)

if 'hrtb_ival' in global_config:
client.hrtb_ival = global_config['hrtb_ival']

if 'hrtb_retr_ival' in global_config:
client.hrtb_retr_ival = global_config['hrtb_retr_ival']

global_config['_rtp_proxy_clients'].append(client)

if global_config['auth_enable'] or global_config['acct_enable']:
global_config['_radius_client'] = RadiusAuthorisation(global_config)
Expand Down Expand Up @@ -788,7 +800,3 @@ def main_func():

reactor.suggestThreadPoolSize(50)
reactor.run(installSignalHandlers = True)


if __name__ == '__main__':
main_func()
4 changes: 0 additions & 4 deletions sippy/b2bua_simple.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,3 @@ def main_func():
global_config['_sip_tm'] = SipTransactionManager(global_config, cmap.recvRequest)

reactor.run(installSignalHandlers = True)

if __name__ == '__main__':
main_func()

67 changes: 67 additions & 0 deletions tests/MyConfigParser_tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import unittest
from sippy.MyConfigParser import MyConfigParser


class TestMyConfigParser(unittest.TestCase):
def TestParamHandling(self):
m = MyConfigParser()
m['_foo'] = 'bar'
m['b2bua_socket'] = 'bar1'
m['acct_enable'] = True
m['auth_enable'] = 'False'

self.assertTrue(m.has_key('_foo'))
self.assertEqual(m['_foo'], 'bar')
self.assertEqual(m['b2bua_socket'], 'bar1')
self.assertEqual(m.get('_foo'), 'bar')
self.assertEqual(m.get('b2bua_socket'), 'bar1')
self.assertEqual(m.get('general', 'b2bua_socket'), 'bar1')
self.assertTrue(m.get('acct_enable'))
self.assertFalse(m.get('auth_enable'))

m.check_and_set('keepalive_ans', '15')
self.assertEqual(m['keepalive_ans'], 15)
self.assertIsInstance(m.get('keepalive_ans'), int)

m.check_and_set('pass_header', 'a')
m.check_and_set('pass_header', 'b')

self.assertEqual(m['pass_headers'], 'a,b')
self.assertEqual(m['_pass_headers'][0], 'a')
self.assertEqual(m['_pass_headers'][1], 'b')

m.check_and_set('accept_ips', '1.2.3.4, 5.6.7.8')
self.assertEqual(m['_accept_ips'][0], '1.2.3.4')
self.assertEqual(m['_accept_ips'][1], '5.6.7.8')

self.assertEqual(m.check_and_set('hrtb_ival', "1"), None)
self.assertEqual(m['hrtb_ival'], 1)
self.assertEqual(m.get('hrtb_ival'), 1)
# Test that get method returns correct type
self.assertIsInstance(m.get('hrtb_ival'), float)
# Test that access by index returns correct type
self.assertIsInstance(m['hrtb_ival'], float)

with self.assertRaises(KeyError):
m.check_and_set('non_existant_key', "1")

def TestSipPortValidation(self):
m = MyConfigParser()
with self.assertRaises(ValueError):
m.check_and_set('sip_port', "-1")
with self.assertRaises(ValueError):
m.check_and_set('sip_port', "0")
with self.assertRaises(ValueError):
m.check_and_set('sip_port', "65536")
self.assertEquals(m.check_and_set('sip_port', "1"), None)
self.assertEquals(m.check_and_set('sip_port', "65535"), None)

def TestMaxCreditTime(self):
m = MyConfigParser()
with self.assertRaises(ValueError):
m.check_and_set('max_credit_time', "-1")

def TestMaxKeepAlive(self):
m = MyConfigParser()
with self.assertRaises(ValueError):
m.check_and_set('keepalive_ans', "-1")