def __init__(self, ua): UaStateGeneric.__init__(self, ua) ua.on_local_sdp_change = None ua.on_remote_sdp_change = None # 300 provides good estimate on the amount of time during which # we can wait for receiving non-negative response to CANCELled # INVITE transaction. self.te = Timeout(self.goIdle, 300.0)
def on_reinvite_connected(self, ua): if len(self.sched) > 0: if self.onhold_count > self.offhold_count: # Take call off-hold little bit later Timeout(self.off_hold, self.sched.pop(), 1, ua) else: Timeout(self.reinvite, self.sched.pop(), 1, ua) a_test_reinvite.on_reinvite_connected(self, ua)
def sendResponse(self, resp, t=None, retrans=False, ack_cb=None, lossemul=0): #print self.tserver if t == None: tid = resp.getTId(wBRN=True) t = self.tserver[tid] if t.state not in (TRYING, RINGING) and not retrans: raise ValueError( 'BUG: attempt to send reply on already finished transaction!!!' ) scode = resp.getSCode()[0] toHF = resp.getHFBody('to') if scode > 100 and toHF.getTag() == None: toHF.genTag() t.data = resp.localStr(*t.userv.uopts.getSIPaddr(), compact=t.compact) t.address = resp.getHFBody('via').getTAddr() self.transmitData(t.userv, t.data, t.address, t.checksum, lossemul) if t.res_out_cb != None: t.res_out_cb(resp) if scode < 200: t.state = RINGING if self.provisional_retr > 0 and scode > 100: if t.teF != None: t.teF.cancel() t.teF = Timeout(self.timerF, self.provisional_retr, 1, t) else: t.state = COMPLETED if t.teE != None: t.teE.cancel() t.teE = None if t.teF != None: t.teF.cancel() t.teF = None if t.needack: # Schedule removal of the transaction t.ack_cb = ack_cb t.teD = Timeout(self.timerD, 32.0, 1, t) if scode >= 200: # Black magick to allow proxy send us another INVITE # same branch and From tag. Use To tag to match # ACK transaction after this point. Branch tag in ACK # could differ as well. del self.tserver[t.tid] t.tid = list(t.tid[:-1]) t.tid.append(resp.getHFBody('to').getTag()) t.tid = tuple(t.tid) self.tserver[t.tid] = t # Install retransmit timer if necessary t.tout = 0.5 t.teA = Timeout(self.timerA, t.tout, 1, t) else: # We have done with the transaction del self.tserver[t.tid] t.cleanup()
def gotreply(self, resp): if self.dead: return if resp.scode < 200: return if resp.scode >= 200 and resp.scode < 300 and resp.reason != 'Auth Failed': contact = None if resp.countHFs('contact') > 0: contact = resp.getHFBody('contact') if contact != None and 'expires' in contact.address.params: tout = int(contact.address.params['expires']) elif resp.countHFs('expires') > 0: tout = resp.getHFBody('expires').getNum() else: tout = 180 timer = Timeout(self.doregister, tout) if self.rok_cb != None: self.rok_cb(timer.etime.realt, contact, self.cb_arg) self.atries = 0 return if resp.scode == 401 and resp.countHFs('www-authenticate') != 0 and \ self.user != None and self.passw != None and self.atries < 3: challenge = resp.getHFBody('www-authenticate') auth = SipAuthorization(realm=challenge.getRealm(), nonce=challenge.getNonce(), method='REGISTER', uri=str(self.rmsg.ruri), username=self.user, password=self.passw) for authorization in self.rmsg.getHFs('authorization'): self.rmsg.removeHeader(authorization) self.rmsg.appendHeader(SipHeader(name='authorization', body=auth)) self.atries += 1 self.doregister() return if resp.scode == 407 and resp.countHFs('proxy-authenticate') != 0 and \ self.user != None and self.passw != None and self.atries < 3: challenge = resp.getHFBody('proxy-authenticate') auth = SipProxyAuthorization(realm=challenge.getRealm(), nonce=challenge.getNonce(), method='REGISTER', uri=str(self.rmsg.ruri), username=self.user, password=self.passw) for authorization in self.rmsg.getHFs('proxy-authorization'): self.rmsg.removeHeader(authorization) self.rmsg.appendHeader( SipHeader(name='proxy-authorization', body=auth)) self.atries += 1 self.doregister() return if self.rfail_cb != None: self.rfail_cb(resp.getSL(), self.cb_arg) Timeout(self.doregister, 60) self.atries = 0
def newTransaction(self, msg, resp_cb = None, laddress = None, userv = None, \ cb_ifver = 1, compact = False, t = None): if t == None: t = SipTransaction() t.rtime = MonoTime() t.compact = compact t.method = msg.getMethod() t.cb_ifver = cb_ifver t.tid = msg.getTId(True, True) if t.tid in self.tclient: raise ValueError( 'BUG: Attempt to initiate transaction with the same TID as existing one!!!' ) t.tout = 0.5 t.fcode = None t.address = msg.getTarget() if userv == None: if laddress == None: t.userv = self.l4r.getServer(t.address) else: t.userv = self.l4r.getServer(laddress, is_local=True) else: t.userv = userv t.data = msg.localStr(*t.userv.uopts.getSIPaddr(), compact=t.compact) if t.method == 'INVITE': try: t.expires = msg.getHFBody('expires').getNum() if t.expires <= 0: t.expires = 300 except IndexError: t.expires = 300 t.needack = True t.ack = msg.genACK() t.cancel = msg.genCANCEL() else: t.expires = 32 t.needack = False t.ack = None t.cancel = None t.cancelPending = False t.resp_cb = resp_cb t.teA = Timeout(self.timerA, t.tout, 1, t) if resp_cb != None: t.r408 = msg.genResponse(408, 'Request Timeout') t.teB = Timeout(self.timerB, 32.0, 1, t) t.teC = None t.state = TRYING self.tclient[t.tid] = t self.transmitData(t.userv, t.data, t.address) if t.req_out_cb != None: t.req_out_cb(msg) return t
def send_command(self, command, result_callback=None, *callback_parameters): entropy = str(random()) + str(time()) cookie = md5(entropy.encode()).hexdigest() next_retr = self.delay_flt.lastval * 4.0 exp_time = 3.0 if isinstance(command, Rtp_proxy_cmd): if command.type == 'I': exp_time = 10.0 if command.type == 'G': exp_time = 1.0 nretr = command.nretr command = str(command) else: if command.startswith('I'): exp_time = 10.0 elif command.startswith('G'): exp_time = 1.0 nretr = None if nretr == None: nretr = getnretrans(next_retr, exp_time) command = '%s %s' % (cookie, command) timer = Timeout(self.retransmit, next_retr, 1, cookie) preq = Rtp_proxy_pending_req(next_retr, nretr - 1, timer, command, \ result_callback, callback_parameters) self.worker.send_to(command, self.address) self.pending_requests[cookie] = preq
def __init__(self, tcfg): tcfg.global_config['_sip_tm'] = SipTransactionManager( tcfg.global_config, self.recvRequest) i = 0 ttype = tcfg.ttype if len(ttype) == 1: ttype += ttype atests = [a_cfg(x) for x in ALL_TESTS * len(ttype)] for subtest_cfg in atests: subtest_class = subtest_cfg.test_class if tcfg.tests != None and subtest_class.cli not in tcfg.tests: subtest_cfg.disabled = True i += 1 continue subtest_cfg.disabled = False cli = subtest_class.cli if i >= len(ALL_TESTS): atype = ttype[1] else: atype = ttype[0] cli += '_ipv%s' % atype[-1] subtest_cfg.tcfg = tcfg.gen_tccfg(atype, self.subtest_done, cli) print('tcfg.gen_tccfg(%s, self.subtest_done, %s)' % (atype, cli)) i += 1 shuffle(atests) for subtest_cfg in atests: if subtest_cfg.disabled: continue subtest = subtest_cfg.test_class(subtest_cfg.tcfg) self.nsubtests_running += 1 self.rval = self.nsubtests_running Timeout(self.timeout, tcfg.test_timeout, 1)
def placeOriginate(self, oroute): cId, cGUID, cli, cld, body, auth, caller_name = self.eTry.getData() cld = oroute.cld self.huntstop_scodes = oroute.params.get('huntstop_scodes', ()) if 'static_tr_out' in self.global_config: cld = re_replace(self.global_config['static_tr_out'], cld) if oroute.hostport == 'sip-ua': host = self.source[0] nh_address, same_af = self.source, True else: host = oroute.hostonly nh_address, same_af = oroute.getNHAddr(self.source) if not oroute.forward_on_fail and self.global_config['acct_enable']: self.acctO = RadiusAccounting(self.global_config, 'originate', \ send_start = self.global_config['start_acct_enable'], lperiod = \ self.global_config.getdefault('alive_acct_int', None)) self.acctO.ms_precision = self.global_config.getdefault( 'precise_acct', False) self.acctO.setParams(oroute.params.get('bill-to', self.username), oroute.params.get('bill-cli', oroute.cli), \ oroute.params.get('bill-cld', cld), self.cGUID, self.cId, host) else: self.acctO = None self.acctA.credit_time = oroute.credit_time conn_handlers = [self.oConn] disc_handlers = [] if not oroute.forward_on_fail and self.global_config['acct_enable']: disc_handlers.append(self.acctO.disc) self.uaO = UA(self.global_config, self.recvEvent, oroute.user, oroute.passw, nh_address, oroute.credit_time, tuple(conn_handlers), \ tuple(disc_handlers), tuple(disc_handlers), dead_cbs = (self.oDead,), expire_time = oroute.expires, \ no_progress_time = oroute.no_progress_expires, extra_headers = oroute.extra_headers) self.uaO.local_ua = self.global_config['_uaname'] self.uaO.no_reply_time = oroute.no_reply_expires if self.source != oroute.params.get('outbound_proxy', None): self.uaO.outbound_proxy = oroute.params.get('outbound_proxy', None) if self.rtp_proxy_session != None and oroute.params.get('rtpp', True): self.uaO.on_local_sdp_change = self.rtp_proxy_session.on_caller_sdp_change self.uaO.on_remote_sdp_change = self.rtp_proxy_session.on_callee_sdp_change self.rtp_proxy_session.caller.raddress = nh_address if body != None: body = body.getCopy() self.proxied = True self.uaO.kaInterval = self.global_config['keepalive_orig'] if 'group_timeout' in oroute.params: timeout, skipto = oroute.params['group_timeout'] Timeout(self.group_expires, timeout, 1, skipto) if self.global_config.getdefault('hide_call_id', False): cId = SipCallId( md5(str(cId).encode()).hexdigest() + ('-b2b_%d' % oroute.rnum)) else: cId += '-b2b_%d' % oroute.rnum event = CCEventTry((cId, cGUID, oroute.cli, cld, body, auth, \ oroute.params.get('caller_name', self.caller_name))) if self.eTry.max_forwards != None: event.max_forwards = self.eTry.max_forwards - 1 if event.max_forwards <= 0: self.uaA.recvEvent(CCEventFail((483, 'Too Many Hops'))) self.state = CCStateDead return event.reason = self.eTry.reason self.uaO.recvEvent(event)
def __init__(self, ua): self.keepalives = 0 self.ka_tr = None UaStateGeneric.__init__(self, ua) self.ua.branch = None if self.ua.kaInterval > 0: Timeout(self.keepAlive, self.ua.kaInterval)
def disconnect(self, ua): if self.disconnect_done: return if self.onhold_count != 3 or self.offhold_count != 2: Timeout(self.disconnect, 1.0, 1, ua) return a_test_reinvite.disconnect(self, ua)
def conn(self, ua, rtime, origin): if self.crec: return self.crec = True self.iTime = ua.setup_ts self.cTime = ua.connect_ts if ua.remote_ua != None and self.user_agent == None: self.user_agent = ua.remote_ua if ua.p1xx_ts != None: self.p1xx_ts = ua.p1xx_ts if ua.p100_ts != None: self.p100_ts = ua.p100_ts if self.send_start: self.asend('Start', rtime, origin, ua) self._attributes.extend((('h323-voice-quality', 0), ('Acct-Terminate-Cause', 'User-Request'))) if self.lperiod != None and self.lperiod > 0: self.el = Timeout(self.asend, self.lperiod, -1, 'Alive')
def __init__(self, global_config): self.global_config = global_config self.ccmap = [] self.el = Timeout(self.GClector, 60, -1) Signal(SIGHUP, self.discAll, SIGHUP) Signal(SIGUSR2, self.toggleDebug, SIGUSR2) Signal(SIGPROF, self.safeRestart, SIGPROF) Signal(SIGTERM, self.safeStop, SIGTERM)
def ring(self, ua): #print('%s: ring: %s %s' % (self.my_name(), self.cli, self.ring_done, self.disconnect_done)) if self.connect_done or self.disconnect_done: return event = CCEventRing((180, 'Ringing', None), origin='switch') Timeout(self.connect, self.answer_ival, 1, ua) ua.recvEvent(event) self.ring_done = True
def ring(self, ua): if self.connect_done or self.disconnect_done: return event = CCEventRing((183, 'Session Progress', self.body), \ origin = 'switch') Timeout(self.connect, self.answer_ival, 1, ua) ua.recvEvent(event) self.ring_done = True
def keepAliveResp(self, resp): if self.ua.state != self: return code, reason = resp.getSCode() if code == 401 and resp.countHFs('www-authenticate') != 0 and \ self.ua.username != None and self.ua.password != None and not self.triedauth: challenge = resp.getHFBody('www-authenticate') req = self.ua.genRequest('INVITE', self.ua.lSDP, challenge.getNonce(), challenge.getRealm()) self.ua.lCSeq += 1 self.ka_tr = self.ua.global_config['_sip_tm'].newTransaction(req, self.keepAliveResp, \ laddress = self.ua.source_address, compact = self.ua.compact_sip) self.triedauth = True return if code == 407 and resp.countHFs('proxy-authenticate') != 0 and \ self.ua.username != None and self.ua.password != None and not self.triedauth: challenge = resp.getHFBody('proxy-authenticate') req = self.ua.genRequest('INVITE', self.ua.lSDP, challenge.getNonce(), challenge.getRealm(), SipProxyAuthorization) self.ua.lCSeq += 1 self.ka_tr = self.ua.global_config['_sip_tm'].newTransaction(req, self.keepAliveResp, \ laddress = self.ua.source_address, compact = self.ua.compact_sip) self.triedauth = True return if code < 200: return self.ka_tr = None self.keepalives += 1 if code in (408, 481, 486): if self.keepalives == 1: print( '%s: Remote UAS at %s:%d does not support re-INVITES, disabling keep alives' % (self.ua.cId, self.ua.rAddr[0], self.ua.rAddr[1])) Timeout(self.ua.disconnect, 600) return print( '%s: Received %d response to keep alive from %s:%d, disconnecting the call' % (self.ua.cId, code, self.ua.rAddr[0], self.ua.rAddr[1])) self.ua.disconnect() return Timeout(self.keepAlive, self.ua.kaInterval)
def connect(self, ua): #if random() > 0.3: # ua.recvEvent(CCEventFail((666, 'Random Failure'))) # return if self.connect_done or self.disconnect_done: return event = CCEventConnect((200, 'OK', self.body), origin='switch') Timeout(self.disconnect, self.disconnect_ival, 1, ua) ua.recvEvent(event) self.connect_done = True
def __init__(self, global_config, req_cb=None): self.global_config = global_config self.l4r = local4remote(global_config, self.handleIncoming, self.nworkers_udp) self.l4r.ploss_out_rate = self.ploss_out_rate self.l4r.pdelay_out_max = self.pdelay_out_max self.tclient = {} self.tserver = {} self.req_cb = req_cb self.l1rcache = {} self.l2rcache = {} self.req_consumers = {} Timeout(self.rCachePurge, 32, -1)
def retransmit(self, cookie): preq = self.pending_requests[cookie] #print('command to %s timeout %s cookie %s triesleft %d' % (str(self.address), preq.command, cookie, preq.triesleft)) if preq.triesleft <= 0 or self.worker == None: del self.pending_requests[cookie] self.go_offline() if preq.result_callback != None: preq.result_callback(None, *preq.callback_parameters) return preq.retransmits += 1 preq.next_retr *= 2 preq.timer = Timeout(self.retransmit, preq.next_retr, 1, cookie) self.worker.send_to(preq.command, self.address) preq.triesleft -= 1
def timerB(self, t): #print 'timerB', t t.teB = None if t.teA != None: t.teA.cancel() t.teA = None t.state = TERMINATED #print '2: Timeout(self.timerC, 32.0, 1, t)', t t.teC = Timeout(self.timerC, 32.0, 1, t) if t.resp_cb == None: return t.r408.rtime = MonoTime() if t.cb_ifver == 1: t.resp_cb(t.r408) else: t.resp_cb(t.r408, t)
def reinvite(self, ua): if not self.connect_done or self.disconnect_done: return sdp_body_bak = ua.lSDP ua.lSDP = sdp_body_bak.getCopy() for sect in ua.lSDP.content.sections: if self.tccfg.atype == 'IP4': sect.c_header.addr = '0.0.0.0' else: sect.c_header.addr = '::' while 'sendrecv' in sect.a_headers: sect.a_headers.remove('sendrecv') rval = a_test_reinvite.reinvite(self, ua, alter_port=False) self.onhold_count += 1 ua.lSDP = sdp_body_bak if len(self.sched) > 0: # Take call off-hold little bit later Timeout(self.off_hold, self.sched.pop(), 1, ua) return rval
def answer(self, global_config, body, req, sip_t): if self.connect_done or self.disconnect_done: return in_body = req.getBody() in_body.parse() cres, why = self.tccfg.checkhostport(in_body) if not cres: self.nerrs += 1 raise ValueError('%s: class %s: hostport validation has failed (%s): %s:\n%s' % \ (self.my_name(), str(self.__class__), self.atype, why, in_body)) # New dialog uaA = UA(global_config, self.recvEvent, disc_cbs = (self.disconnected,), \ fail_cbs = (self.disconnected,), dead_cbs = (self.alldone,)) uaA.godead_timeout = self.godead_timeout uaA.compact_sip = self.compact_sip Timeout(self.ring, self.ring_ival, 1, uaA) self.body = body self.call_id = req.getHFBody('call-id') return self.complete_answer(uaA, req, sip_t)
def handle_read(self, data, address, rtime, delayed = False): if len(data) > 0 and self.uopts.data_callback != None: self.stats[2] += 1 if self.uopts.ploss_in_rate > 0.0 and not delayed: if random() < self.uopts.ploss_in_rate: return if self.uopts.pdelay_in_max > 0.0 and not delayed: pdelay = self.uopts.pdelay_in_max * random() Timeout(self.handle_read, pdelay, 1, data, address, rtime.getOffsetCopy(pdelay), True) return try: self.uopts.data_callback(data, address, self, rtime) except Exception as ex: if isinstance(ex, SystemExit): raise print(datetime.now(), 'Udp_server: unhandled exception when processing incoming data') print('-' * 70) traceback.print_exc(file = sys.stdout) print('-' * 70) sys.stdout.flush()
def send_to(self, data, address, delayed = False): if not isinstance(address, tuple): raise Exception('Invalid address, not a tuple: %s' % str(address)) if not isinstance(data, bytes): data = data.encode('utf-8') if self.uopts.ploss_out_rate > 0.0 and not delayed: if random() < self.uopts.ploss_out_rate: return if self.uopts.pdelay_out_max > 0.0 and not delayed: pdelay = self.uopts.pdelay_out_max * random() Timeout(self.send_to, pdelay, 1, data, address, True) return addr, port = address if self.uopts.family == socket.AF_INET6: if not addr.startswith('['): raise Exception('Invalid IPv6 address: %s' % addr) address = (addr[1:-1], port) self.wi_available.acquire() self.wi.append((data, address)) self.wi_available.notify() self.wi_available.release()
class CallMap(object): ccmap = None el = None debug_mode = False safe_restart = False global_config = None proxy = None #rc1 = None #rc2 = None def __init__(self, global_config): self.global_config = global_config self.ccmap = [] self.el = Timeout(self.GClector, 60, -1) Signal(SIGHUP, self.discAll, SIGHUP) Signal(SIGUSR2, self.toggleDebug, SIGUSR2) Signal(SIGPROF, self.safeRestart, SIGPROF) Signal(SIGTERM, self.safeStop, SIGTERM) #gc.disable() #gc.set_debug(gc.DEBUG_STATS) #gc.set_threshold(0) #print gc.collect() def recvRequest(self, req, sip_t): try: to_tag = req.getHFBody('to').getTag() except Exception as exception: print(datetime.now(), 'can\'t parse SIP request: %s:\n' % str(exception)) print('-' * 70) print_exc(file=sys.stdout) print('-' * 70) print(req) print('-' * 70) sys.stdout.flush() return (None, None, None) if to_tag != None: # Request within dialog, but no such dialog return (req.genResponse(481, 'Call Leg/Transaction Does Not Exist'), None, None) if req.getMethod() == 'INVITE': # New dialog if req.countHFs('via') > 1: via = req.getHFBody('via', 1) else: via = req.getHFBody('via', 0) remote_ip = via.getTAddr()[0] source = req.getSource() # First check if request comes from IP that # we want to accept our traffic from if '_accept_ips' in self.global_config and \ not source[0] in self.global_config['_accept_ips']: resp = req.genResponse(403, 'Forbidden') return (resp, None, None) challenge = None if self.global_config['auth_enable']: # Prepare challenge if no authorization header is present. # Depending on configuration, we might try remote ip auth # first and then challenge it or challenge immediately. if self.global_config['digest_auth'] and \ req.countHFs('authorization') == 0: challenge = SipHeader(name='www-authenticate') challenge.getBody().realm = req.getRURI().host # Send challenge immediately if digest is the # only method of authenticating if challenge != None and self.global_config.getdefault( 'digest_auth_only', False): resp = req.genResponse(401, 'Unauthorized') resp.appendHeader(challenge) return (resp, None, None) pass_headers = [] for header in self.global_config['_pass_headers']: hfs = req.getHFs(header) if len(hfs) > 0: pass_headers.extend(hfs) cc = CallController(remote_ip, source, self.global_config, pass_headers) cc.challenge = challenge rval = cc.uaA.recvRequest(req, sip_t) self.ccmap.append(cc) return rval if self.proxy != None and req.getMethod() in ('REGISTER', 'SUBSCRIBE'): return self.proxy.recvRequest(req) if req.getMethod() in ('NOTIFY', 'PING'): # Whynot? return (req.genResponse(200, 'OK'), None, None) return (req.genResponse(501, 'Not Implemented'), None, None) def discAll(self, signum=None): if signum != None: print('Signal %d received, disconnecting all calls' % signum) for cc in tuple(self.ccmap): cc.disconnect() def toggleDebug(self, signum): if self.debug_mode: print('Signal %d received, toggling extra debug output off' % signum) else: print('Signal %d received, toggling extra debug output on' % signum) self.debug_mode = not self.debug_mode def safeRestart(self, signum): print('Signal %d received, scheduling safe restart' % signum) self.safe_restart = True def safeStop(self, signum): print('Signal %d received, scheduling safe stop' % signum) self.discAll(signum) self.safe_stop = True self.global_config['_executeStop_count'] = 0 self.er_timer = Timeout(self.executeStop, 0.5, -1) def executeStop(self): if not self.safe_stop: return self.global_config['_executeStop_count'] += 1 acalls = [ x for x in self.ccmap if x.state in (CCStateConnected, CCStateARComplete) ] nactive = len(acalls) print('[%d]: executeStop is invoked, %d calls in map, %d active' % \ (self.global_config['_my_pid'], len(self.ccmap), nactive)) if self.global_config['_executeStop_count'] >= 5 and nactive > 0: print('executeStop: some sessions would not die, forcing exit:') for cc in acalls: print('\t' + str(cc)) nactive = 0 if nactive > 0: return self.er_timer.cancel() del self.er_timer sys.stdout.flush() sys.stderr.flush() ED2.breakLoop() return def GClector(self): print('GC is invoked, %d calls in map' % len(self.ccmap)) if self.debug_mode: print(self.global_config['_sip_tm'].tclient, self.global_config['_sip_tm'].tserver) for cc in tuple(self.ccmap): try: print(cc.uaA.state, cc.uaO.state) except AttributeError: print(None) else: print('[%d]: %d client, %d server transactions in memory' % \ (os.getpid(), len(self.global_config['_sip_tm'].tclient), len(self.global_config['_sip_tm'].tserver))) if self.safe_restart: if len(self.ccmap) == 0: self.global_config['_sip_tm'].userv.close() os.chdir(self.global_config['_orig_cwd']) argv = [ sys.executable, ] argv.extend(self.global_config['_orig_argv']) os.execv(sys.executable, argv) # Should not reach this point! self.el.ival = 1 #print gc.collect() if len(gc.garbage) > 0: print(gc.garbage) def recvCommand(self, clim, cmd): args = cmd.split() cmd = args.pop(0).lower() if cmd == 'q': clim.close() return False if cmd == 'l': res = 'In-memory calls:\n' total = 0 for cc in self.ccmap: res += '%s: %s (' % (cc.cId, cc.state.sname) if cc.uaA != None: res += '%s %s:%d %s %s -> ' % (cc.uaA.state, cc.uaA.getRAddr0()[0], \ cc.uaA.getRAddr0()[1], cc.uaA.getCLD(), cc.uaA.getCLI()) else: res += 'N/A -> ' if cc.uaO != None: res += '%s %s:%d %s %s)\n' % (cc.uaO.state, cc.uaO.getRAddr0()[0], \ cc.uaO.getRAddr0()[1], cc.uaO.getCLI(), cc.uaO.getCLD()) else: res += 'N/A)\n' total += 1 res += 'Total: %d\n' % total clim.send(res) return False if cmd == 'lt': res = 'In-memory server transactions:\n' for tid, t in self.global_config['_sip_tm'].tserver.iteritems(): res += '%s %s %s\n' % (tid, t.method, t.state) res += 'In-memory client transactions:\n' for tid, t in self.global_config['_sip_tm'].tclient.iteritems(): res += '%s %s %s\n' % (tid, t.method, t.state) clim.send(res) return False if cmd in ('lt', 'llt'): if cmd == 'llt': mindur = 60.0 else: mindur = 0.0 ctime = MonoTime() res = 'In-memory server transactions:\n' for tid, t in self.global_config['_sip_tm'].tserver.iteritems(): duration = ctime - t.rtime if duration < mindur: continue res += '%s %s %s %s\n' % (tid, t.method, t.state, duration) res += 'In-memory client transactions:\n' for tid, t in self.global_config['_sip_tm'].tclient.iteritems(): duration = ctime - t.rtime if duration < mindur: continue res += '%s %s %s %s\n' % (tid, t.method, t.state, duration) clim.send(res) return False if cmd == 'd': if len(args) != 1: clim.send('ERROR: syntax error: d <call-id>\n') return False if args[0] == '*': self.discAll() clim.send('OK\n') return False dlist = [x for x in self.ccmap if str(x.cId) == args[0]] if len(dlist) == 0: clim.send('ERROR: no call with id of %s has been found\n' % args[0]) return False for cc in dlist: cc.disconnect() clim.send('OK\n') return False if cmd == 'r': if len(args) != 1: clim.send('ERROR: syntax error: r [<id>]\n') return False idx = int(args[0]) dlist = [x for x in self.ccmap if x.id == idx] if len(dlist) == 0: clim.send('ERROR: no call with id of %d has been found\n' % idx) return False for cc in dlist: if not cc.proxied: continue if cc.state == CCStateConnected: cc.disconnect(MonoTime().getOffsetCopy(-60)) continue if cc.state == CCStateARComplete: cc.uaO.disconnect(MonoTime().getOffsetCopy(-60)) continue clim.send('OK\n') return False clim.send('ERROR: unknown command\n') return False
def process_reinvite(self, ua): if self.reinv_answ_delay > 0: Timeout(self._process_reinvite, self.reinv_answ_delay, 1, \ ua) return self._process_reinvite(ua)
def complete_answer(self, ua, *args): rval = super(test_reinvite, self).complete_answer(ua, *args) if self.reinvite_ival != None: Timeout(self.reinvite, self.reinvite_ival, 1, ua) return rval
def connected(self, ua, *args): #print('test_reinvite.connected') rval = super(test_reinvite, self).connected(ua, *args) if self.reinvite_ival != None: Timeout(self.reinvite, self.reinvite_ival, 1, ua) return rval
def safeStop(self, signum): print('Signal %d received, scheduling safe stop' % signum) self.discAll(signum) self.safe_stop = True self.global_config['_executeStop_count'] = 0 self.er_timer = Timeout(self.executeStop, 0.5, -1)
class CallMap(object): ccmap = None el = None debug_mode = False safe_restart = False global_config = None proxy = None #rc1 = None #rc2 = None def __init__(self, global_config): self.global_config = global_config self.ccmap = [] self.el = Timeout(self.GClector, 60, -1) Signal(SIGHUP, self.discAll, SIGHUP) Signal(SIGUSR2, self.toggleDebug, SIGUSR2) Signal(SIGPROF, self.safeRestart, SIGPROF) Signal(SIGTERM, self.safeStop, SIGTERM) #gc.disable() #gc.set_debug(gc.DEBUG_STATS) #gc.set_threshold(0) #print gc.collect() def recvRequest(self, req, sip_t): try: to_tag = req.getHFBody('to').getTag() except Exception as exception: print(datetime.now(), 'can\'t parse SIP request: %s:\n' % str(exception)) print( '-' * 70) print_exc(file = sys.stdout) print( '-' * 70) print(req) print('-' * 70) sys.stdout.flush() return (None, None, None) if to_tag != None: # Request within dialog, but no such dialog return (req.genResponse(481, 'Call Leg/Transaction Does Not Exist'), None, None) if req.getMethod() == 'INVITE': # New dialog if req.countHFs('via') > 1: via = req.getHFBody('via', 1) else: via = req.getHFBody('via', 0) remote_ip = via.getTAddr()[0] source = req.getSource() # First check if request comes from IP that # we want to accept our traffic from if '_accept_ips' in self.global_config and \ not source[0] in self.global_config['_accept_ips']: resp = req.genResponse(403, 'Forbidden') return (resp, None, None) challenge = None if self.global_config['auth_enable']: # Prepare challenge if no authorization header is present. # Depending on configuration, we might try remote ip auth # first and then challenge it or challenge immediately. if self.global_config['digest_auth'] and \ req.countHFs('authorization') == 0: challenge = SipHeader(name = 'www-authenticate') challenge.getBody().realm = req.getRURI().host # Send challenge immediately if digest is the # only method of authenticating if challenge != None and self.global_config.getdefault('digest_auth_only', False): resp = req.genResponse(401, 'Unauthorized') resp.appendHeader(challenge) return (resp, None, None) pass_headers = [] for header in self.global_config['_pass_headers']: hfs = req.getHFs(header) if len(hfs) > 0: pass_headers.extend(hfs) cc = CallController(remote_ip, source, self.global_config, pass_headers) cc.challenge = challenge rval = cc.uaA.recvRequest(req, sip_t) self.ccmap.append(cc) return rval if self.proxy != None and req.getMethod() in ('REGISTER', 'SUBSCRIBE'): return self.proxy.recvRequest(req) if req.getMethod() in ('NOTIFY', 'PING'): # Whynot? return (req.genResponse(200, 'OK'), None, None) return (req.genResponse(501, 'Not Implemented'), None, None) def discAll(self, signum = None): if signum != None: print('Signal %d received, disconnecting all calls' % signum) for cc in tuple(self.ccmap): cc.disconnect() def toggleDebug(self, signum): if self.debug_mode: print('Signal %d received, toggling extra debug output off' % signum) else: print('Signal %d received, toggling extra debug output on' % signum) self.debug_mode = not self.debug_mode def safeRestart(self, signum): print('Signal %d received, scheduling safe restart' % signum) self.safe_restart = True def safeStop(self, signum): print('Signal %d received, scheduling safe stop' % signum) self.discAll(signum) self.safe_stop = True self.global_config['_executeStop_count'] = 0 self.er_timer = Timeout(self.executeStop, 0.5, -1) def executeStop(self): if not self.safe_stop: return self.global_config['_executeStop_count'] += 1 acalls = [x for x in self.ccmap if x.state in (CCStateConnected, CCStateARComplete)] nactive = len(acalls) print('[%d]: executeStop is invoked, %d calls in map, %d active' % \ (self.global_config['_my_pid'], len(self.ccmap), nactive)) if self.global_config['_executeStop_count'] >= 5 and nactive > 0: print('executeStop: some sessions would not die, forcing exit:') for cc in acalls: print('\t' + str(cc)) nactive = 0 if nactive > 0: return self.er_timer.cancel() del self.er_timer sys.stdout.flush() sys.stderr.flush() ED2.breakLoop() return def GClector(self): print('GC is invoked, %d calls in map' % len(self.ccmap)) if self.debug_mode: print(self.global_config['_sip_tm'].tclient, self.global_config['_sip_tm'].tserver) for cc in tuple(self.ccmap): try: print(cc.uaA.state, cc.uaO.state) except AttributeError: print(None) else: print('[%d]: %d client, %d server transactions in memory' % \ (os.getpid(), len(self.global_config['_sip_tm'].tclient), len(self.global_config['_sip_tm'].tserver))) if self.safe_restart: if len(self.ccmap) == 0: self.global_config['_sip_tm'].userv.close() os.chdir(self.global_config['_orig_cwd']) argv = [sys.executable,] argv.extend(self.global_config['_orig_argv']) os.execv(sys.executable, argv) # Should not reach this point! self.el.ival = 1 #print gc.collect() if len(gc.garbage) > 0: print(gc.garbage) def recvCommand(self, clim, cmd): args = cmd.split() cmd = args.pop(0).lower() if cmd == 'q': clim.close() return False if cmd == 'l': res = 'In-memory calls:\n' total = 0 for cc in self.ccmap: res += '%s: %s (' % (cc.cId, cc.state.sname) if cc.uaA != None: res += '%s %s:%d %s %s -> ' % (cc.uaA.state, cc.uaA.getRAddr0()[0], \ cc.uaA.getRAddr0()[1], cc.uaA.getCLD(), cc.uaA.getCLI()) else: res += 'N/A -> ' if cc.uaO != None: res += '%s %s:%d %s %s)\n' % (cc.uaO.state, cc.uaO.getRAddr0()[0], \ cc.uaO.getRAddr0()[1], cc.uaO.getCLI(), cc.uaO.getCLD()) else: res += 'N/A)\n' total += 1 res += 'Total: %d\n' % total clim.send(res) return False if cmd == 'lt': res = 'In-memory server transactions:\n' for tid, t in self.global_config['_sip_tm'].tserver.iteritems(): res += '%s %s %s\n' % (tid, t.method, t.state) res += 'In-memory client transactions:\n' for tid, t in self.global_config['_sip_tm'].tclient.iteritems(): res += '%s %s %s\n' % (tid, t.method, t.state) clim.send(res) return False if cmd in ('lt', 'llt'): if cmd == 'llt': mindur = 60.0 else: mindur = 0.0 ctime = MonoTime() res = 'In-memory server transactions:\n' for tid, t in self.global_config['_sip_tm'].tserver.iteritems(): duration = ctime - t.rtime if duration < mindur: continue res += '%s %s %s %s\n' % (tid, t.method, t.state, duration) res += 'In-memory client transactions:\n' for tid, t in self.global_config['_sip_tm'].tclient.iteritems(): duration = ctime - t.rtime if duration < mindur: continue res += '%s %s %s %s\n' % (tid, t.method, t.state, duration) clim.send(res) return False if cmd == 'd': if len(args) != 1: clim.send('ERROR: syntax error: d <call-id>\n') return False if args[0] == '*': self.discAll() clim.send('OK\n') return False dlist = [x for x in self.ccmap if str(x.cId) == args[0]] if len(dlist) == 0: clim.send('ERROR: no call with id of %s has been found\n' % args[0]) return False for cc in dlist: cc.disconnect() clim.send('OK\n') return False if cmd == 'r': if len(args) != 1: clim.send('ERROR: syntax error: r [<id>]\n') return False idx = int(args[0]) dlist = [x for x in self.ccmap if x.id == idx] if len(dlist) == 0: clim.send('ERROR: no call with id of %d has been found\n' % idx) return False for cc in dlist: if not cc.proxied: continue if cc.state == CCStateConnected: cc.disconnect(MonoTime().getOffsetCopy(-60)) continue if cc.state == CCStateARComplete: cc.uaO.disconnect(MonoTime().getOffsetCopy(-60)) continue clim.send('OK\n') return False clim.send('ERROR: unknown command\n') return False
class RadiusAccounting(object): global_config = None drec = None crec = None iTime = None cTime = None sip_cid = None origin = None lperiod = None el = None send_start = None complete = False ms_precision = False user_agent = None p1xx_ts = None p100_ts = None def __init__(self, global_config, origin, lperiod = None, send_start = False): self.global_config = global_config self._attributes = [('h323-call-origin', origin), ('h323-call-type', 'VoIP'), \ ('h323-session-protocol', 'sipv2')] self.drec = False self.crec = False self.origin = origin self.lperiod = lperiod self.send_start = send_start def setParams(self, username, caller, callee, h323_cid, sip_cid, remote_ip, \ h323_in_cid = None): if caller == None: caller = '' self._attributes.extend((('User-Name', username), ('Calling-Station-Id', caller), \ ('Called-Station-Id', callee), ('h323-conf-id', h323_cid), ('call-id', sip_cid), \ ('Acct-Session-Id', sip_cid), ('h323-remote-address', remote_ip))) if h323_in_cid != None and h323_in_cid != h323_cid: self._attributes.append(('h323-incoming-conf-id', h323_in_cid)) self.sip_cid = str(sip_cid) self.complete = True def conn(self, ua, rtime, origin): if self.crec: return self.crec = True self.iTime = ua.setup_ts self.cTime = ua.connect_ts if ua.remote_ua != None and self.user_agent == None: self.user_agent = ua.remote_ua if ua.p1xx_ts != None: self.p1xx_ts = ua.p1xx_ts if ua.p100_ts != None: self.p100_ts = ua.p100_ts if self.send_start: self.asend('Start', rtime, origin, ua) self._attributes.extend((('h323-voice-quality', 0), ('Acct-Terminate-Cause', 'User-Request'))) if self.lperiod != None and self.lperiod > 0: self.el = Timeout(self.asend, self.lperiod, -1, 'Alive') def disc(self, ua, rtime, origin, result = 0): if self.drec: return self.drec = True if self.el != None: self.el.cancel() self.el = None if self.iTime == None: self.iTime = ua.setup_ts if self.cTime == None: self.cTime = rtime if ua.remote_ua != None and self.user_agent == None: self.user_agent = ua.remote_ua if ua.p1xx_ts != None: self.p1xx_ts = ua.p1xx_ts if ua.p100_ts != None: self.p100_ts = ua.p100_ts self.asend('Stop', rtime, origin, result, ua) def asend(self, type, rtime = None, origin = None, result = 0, ua = None): if not self.complete: return if rtime == None: rtime = time() if ua != None: duration, delay, connected = ua.getAcct()[:3] else: # Alive accounting duration = rtime - self.cTime delay = self.cTime - self.iTime connected = True if not(self.ms_precision): duration = round(duration) delay = round(delay) attributes = self._attributes[:] if type != 'Start': if result >= 400: try: dc = sipErrToH323Err[result][0] except: dc = '7f' elif result < 200: dc = '10' else: dc = '0' attributes.extend((('h323-disconnect-time', self.ftime(self.iTime + delay + duration)), \ ('Acct-Session-Time', '%d' % round(duration)), ('h323-disconnect-cause', dc))) if type == 'Stop': if origin == 'caller': release_source = '2' elif origin == 'callee': release_source = '4' else: release_source = '8' attributes.append(('release-source', release_source)) attributes.extend((('h323-connect-time', self.ftime(self.iTime + delay)), ('h323-setup-time', self.ftime(self.iTime)), \ ('Acct-Status-Type', type))) if self.user_agent != None: attributes.append(('h323-ivr-out', 'sip_ua:' + self.user_agent)) if self.p1xx_ts != None: attributes.append(('Acct-Delay-Time', round(self.p1xx_ts))) if self.p100_ts != None: attributes.append(('provisional-timepoint', self.ftime(self.p100_ts))) pattributes = ['%-32s = \'%s\'\n' % (x[0], str(x[1])) for x in attributes] pattributes.insert(0, 'sending Acct %s (%s):\n' % (type, self.origin.capitalize())) self.global_config['_sip_logger'].write(call_id = self.sip_cid, *pattributes) self.global_config['_radius_client'].do_acct(attributes, self._process_result, self.sip_cid, time()) def ftime(self, t): gt = gmtime(t) day = strftime('%d', gt) if day[0] == '0': day = day[1] if self.ms_precision: msec = (t % 1) * 1000 else: msec = 0 return strftime('%%H:%%M:%%S.%.3d GMT %%a %%b %s %%Y' % (msec, day), gt) def _process_result(self, results, sip_cid, btime): delay = time() - btime rcode = results[1] if rcode in (0, 1): if rcode == 0: message = 'Acct/%s request accepted (delay is %.3f)\n' % (self.origin, delay) else: message = 'Acct/%s request rejected (delay is %.3f)\n' % (self.origin, delay) else: message = 'Error sending Acct/%s request (delay is %.3f)\n' % (self.origin, delay) self.global_config['_sip_logger'].write(message, call_id = sip_cid)
def __init__(self, tcfg): tcfg.global_config['_sip_tm'] = SipTransactionManager( tcfg.global_config, self.recvRequest) Timeout(self.timeout, tcfg.test_timeout, 1) self.tcfg = tcfg
self.ptransmitted = ptransmitted def shutdown(self): if self.shut_down: # do not crash when shutdown() called twice return self.shut_down = True self.rtpp_class.shutdown(self) self.rtpp_class = None def get_rtpc_delay(self): return self.rtpp_class.get_rtpc_delay(self) if __name__ == '__main__': from sippy.Core.EventDispatcher import ED2 from sippy.Time.Timeout import Timeout def display(*args): print(args) ED2.breakLoop() def waitonline(rpc): if rpc.online: ED2.breakLoop() r = Rtp_proxy_client({'_sip_address':'1.2.3.4'}) t = Timeout(waitonline, 0.1, 10, r) ED2.loop(2.0) assert(r.online) t.cancel() r.send_command('VF 123456', display, 'abcd') ED2.loop() r.shutdown() print('passed')
if __name__ == '__main__': from sippy.Time.Timeout import Timeout from sippy.Rtp_proxy_client import Rtp_proxy_client def display(*args): print('got:', args) ED2.breakLoop() def waitonline(rpc): if rpc.online: ED2.breakLoop() gc = {'_sip_address': '1.2.3.4'} r = Rtp_proxy_client(gc) t = Timeout(waitonline, 0.1, 10, r) ED2.loop(2.0) assert (r.online) t.cancel() gc['rtp_proxy_client'] = r rs = Rtp_proxy_session(gc, 'call_id1', 'from_tag1', 'to_tag1') rs.version(display) ED2.loop() rs.start_recording('bogus', result_callback=display) print(1) ED2.loop() print(2) rs.play_callee('bogus', result_callback=display) ED2.loop()
class UacStateCancelling(UaStateGeneric): sname = 'Cancelling(UAC)' def __init__(self, ua): UaStateGeneric.__init__(self, ua) ua.on_local_sdp_change = None ua.on_remote_sdp_change = None # 300 provides good estimate on the amount of time during which # we can wait for receiving non-negative response to CANCELled # INVITE transaction. self.te = Timeout(self.goIdle, 300.0) def goIdle(self): #print 'Time in Cancelling state expired, going to the Dead state' self.te = None self.ua.changeState((UaStateDead,)) def recvResponse(self, resp, tr): code, reason = resp.getSCode() if code < 200: return None if self.te != None: self.te.cancel() self.te = None # When the final response arrives make sure to send BYE # if response is positive 200 OK and move into # UaStateDisconnected to catch any in-flight BYE from the # called party. # # If the response is negative or redirect go to the UaStateDead # immediately, since this means that we won't receive any more # requests from the calling party. XXX: redirects should probably # somehow reported to the upper level, but it will create # significant additional complexity there, since after signalling # Failure/Disconnect calling party don't expect any more # events to be delivered from the called one. In any case, # this should be fine, since we are in this state only when # caller already has declared his wilingless to end the session, # so that he is probably isn't interested in redirects anymore. if code >= 200 and code < 300: if resp.countHFs('contact') > 0: self.ua.rTarget = resp.getHFBody('contact').getUrl().getCopy() self.ua.routes = [x.getCopy() for x in resp.getHFBodys('record-route')] self.ua.routes.reverse() if len(self.ua.routes) > 0: if not self.ua.routes[0].getUrl().lr: self.ua.routes.append(SipRoute(address = SipAddress(url = self.ua.rTarget))) self.ua.rTarget = self.ua.routes.pop(0).getUrl() self.ua.rAddr = self.ua.rTarget.getAddr() elif self.ua.outbound_proxy != None: self.ua.routes.append(SipRoute(address = SipAddress(url = self.ua.rTarget))) self.ua.rTarget = self.ua.routes[0].getUrl().getCopy() self.ua.rTarget.lr = False self.ua.rTarget.other = tuple() self.ua.rTarget.headers = tuple() else: self.ua.rAddr = self.ua.routes[0].getAddr() else: self.ua.rAddr = self.ua.rTarget.getAddr() self.ua.rUri.setTag(resp.getHFBody('to').getTag()) req = self.ua.genRequest('BYE') self.ua.lCSeq += 1 self.ua.global_config['_sip_tm'].newTransaction(req, \ laddress = self.ua.source_address, compact = self.ua.compact_sip) return (UaStateDisconnected,) return (UaStateDead,) def recvEvent(self, event): #print 'wrong event %s in the Cancelling state' % event return None