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) 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) 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 __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 __init__(self, msg, timeout, next_screen, fine_print = False): Screen.__init__(self) self.msg = msg timer = Timeout(timeout, self) self.next_screen = next_screen self.fine_print = fine_print #stop input Foodputer.Foodputer.accept_input = False timer.start()
def sendResponse(self, resp, t=None, retrans=False, ack_cb=None): #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.laddress, compact=t.compact) t.address = resp.getHFBody('via').getTAddr() self.transmitData(t.userv, t.data, t.address, t.checksum) 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 newTransaction(self, msg, resp_cb = None, laddress = None, userv = None, \ cb_ifver = 1, compact = False): t = SipTransaction() t.rtime = time() t.compact = compact t.method = msg.getMethod() t.cb_ifver = cb_ifver t.tid = msg.getTId(True, True) if self.tclient.has_key(t.tid): 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.laddress, 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) return t
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 heartbeat_reply(self, stats): #print 'heartbeat_reply', self.address, stats, self.online if self.shut_down: return if not self.online: return if stats == None: self.active_sessions = None self.go_offline() else: sessions_created = active_sessions = active_streams = preceived = ptransmitted = 0 for line in stats.splitlines(): line_parts = line.split(':', 1) if line_parts[0] == 'sessions created': sessions_created = int(line_parts[1]) elif line_parts[0] == 'active sessions': active_sessions = int(line_parts[1]) elif line_parts[0] == 'active streams': active_streams = int(line_parts[1]) elif line_parts[0] == 'packets received': preceived = int(line_parts[1]) elif line_parts[0] == 'packets transmitted': ptransmitted = int(line_parts[1]) self.update_active(active_sessions, sessions_created, active_streams, preceived, ptransmitted) Timeout(self.heartbeat, randomize(self.hrtb_ival, 0.1))
def send_command(self, command, result_callback=None, *callback_parameters): cookie = md5(str(random()) + str(time())).hexdigest() next_retr = self.delay_flt.lastval * 4.0 rtime = 3.0 if isinstance(command, Rtp_proxy_cmd): if command.type == 'I': rtime = 10.0 if command.type == 'G': rtime = 1.0 nretr = command.nretr command = str(command) else: if command.startswith('I'): rtime = 10.0 elif command.startswith('G'): rtime = 1.0 nretr = None if nretr == None: nretr = getnretrans(next_retr, rtime) command = '%s %s' % (cookie, command) timer = Timeout(self.retransmit, next_retr, 1, cookie) stime = MonoTime() self.worker.send_to(command, self.address) nretr -= 1 self.pending_requests[cookie] = (next_retr, nretr, timer, command, result_callback, stime, callback_parameters)
def go_offline(self): if self.shut_down: return #print 'go_offline', self.address, self.online if self.online: self.online = False Timeout(self.version_check, randomize(self.hrtb_retr_ival, 0.1))
def sendResponse(self, resp, t=None, retrans=False): #print self.tserver if t == None: tid = resp.getTId() 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.laddress[0], t.userv.laddress[1]) t.address = resp.getHFBody('via').getTAddr() self.transmitData(t.userv, t.data, t.address, t.checksum) 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.teD = Timeout(self.timerD, 32.0, 1, t) if scode >= 300: # Black magick to allow proxy send us another INVITE with diffetent branch del self.tserver[t.tid] t.tid = list(t.tid) t.tid.append(t.branch) 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 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 version_check_reply(self, version): if self.shut_down: return if version == '20040107': self.go_online() elif self.online: self.go_offline() else: Timeout(self.version_check, randomize(self.hrtb_retr_ival, 0.1))
def version_check_reply(self, version): if self.shutdown: self.worker.shutdown() return if version == '20040107': self.go_online() elif self.online: self.go_offline() else: Timeout(self.version_check, 60)
def __init__(self, global_config, req_cb=None): self.global_config = global_config self.l4r = local4remote(global_config, self.handleIncoming) self.tclient = {} self.tserver = {} self.req_cb = req_cb self.l1rcache = {} self.l2rcache = {} self.req_consumers = {} Timeout(self.rCachePurge, 32, -1)
def conn(self, ua, rtime, origin): if self.crec: return self.crec = True self.cTime = rtime if self.send_start: self.asend('Start', rtime, origin) self._attributes.extend((('h323-voice-quality', 0), ('Acct-Terminate-Cause', 'User-Request'))) if self.lperiod != None: self.el = Timeout(self.asend, self.lperiod, -1, 'Alive')
def send_command(self, command, result_callback=None, *callback_parameters): cookie = md5(str(random()) + str(time())).hexdigest() command = '%s %s' % (cookie, command) timer = Timeout(self.retransmit, 1, -1, cookie) self.pending_requests[cookie] = [ 3, timer, command, result_callback, callback_parameters ] self.udp_server.send_to(command, self.address)
def version_check_reply(self, version): if self.shutdown: if self.worker != None: self.worker.shutdown() self.worker = None return if version == '20040107': self.go_online() elif self.online: self.go_offline() else: Timeout(self.version_check, randomize(60, 0.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 = time() t.resp_cb(t.r408)
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 fake_resp = SipRequest(t.data).genResponse(408, 'Request Timeout') fake_resp.rtime = time() t.resp_cb(fake_resp)
def execute(self, timeout=60): """ execute() Connect to the URL and send POST-data. The returned data will be passed to ConfusaParser, and either an array of dictionary-entries or None will be returned. """ # Get the result and parse it opener = urllib2.build_opener(self.https_client) t = Timeout(timeout) try: res = opener.open(self.url, self.data) except xml.sax._exceptions.SAXParseException: t.cancel_timeout() return None except TimeoutException: print "Did not receive a timely answer (%s seconds), aborting" % (timeout) return None t.cancel_timeout() return Parser.Parser(res)
def heartbeat_reply(self, stats): #print 'heartbeat_reply', self.address, stats, self.online if self.shutdown: self.udp_server.shutdown() return if not self.online: return if stats == None: self.active_sessions = None self.go_offline() else: for line in stats.splitlines(): if not line.startswith('active sessions'): continue self.update_active(int(line.split(':', 1)[1])) Timeout(self.heartbeat, 10)
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 + pdelay, True) return try: self.uopts.data_callback(data, address, self, rtime) except: 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 retransmit(self, cookie): next_retr, triesleft, timer, command, result_callback, stime, callback_parameters = self.pending_requests[ cookie] #print 'command to %s timeout %s cookie %s triesleft %d' % (str(self.address), command, cookie, triesleft) if triesleft <= 0 or self.worker == None: del self.pending_requests[cookie] self.go_offline() if result_callback != None: result_callback(None, *callback_parameters) return #next_retr *= 2 timer = Timeout(self.retransmit, next_retr, 1, cookie) stime = MonoTime() self.worker.send_to(command, self.address) triesleft -= 1 self.pending_requests[cookie] = (next_retr, triesleft, timer, command, result_callback, stime, callback_parameters)
def send_to(self, data, address, delayed=False): if not isinstance(address, tuple): raise Exception('Invalid address, not a tuple: %s' % str(address)) 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 RadiusAccounting(object): global_config = None drec = None crec = None iTime = None cTime = None credit_time = None sip_cid = None origin = None lperiod = None el = None send_start = None complete = False def __init__(self, global_config, origin, lperiod = None, send_start = False, itime = None): if not itime: self.iTime = time() else: self.iTime = itime self.global_config = global_config self._attributes = [('h323-call-origin', origin), ('h323-call-type', 'VoIP'), \ ('h323-session-protocol', 'sipv2'), ('h323-setup-time', ftime(self.iTime))] 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, \ credit_time = None, 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.credit_time = credit_time self.sip_cid = str(sip_cid) self.complete = True def conn(self, ua, rtime, origin): if self.crec: return self.crec = True self.cTime = rtime if self.send_start: self.asend('Start', rtime, origin) self._attributes.extend((('h323-voice-quality', 0), ('Acct-Terminate-Cause', 'User-Request'))) if self.lperiod != None: 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: self.el.cancel() self.el = None self.asend('Stop', rtime, origin, result) def asend(self, type, rtime = None, origin = None, result = 0): if not self.complete: return if not rtime: rtime = time() attributes = self._attributes[:] if type != 'Start': if self.cTime: duration = rtime - self.cTime delay = self.cTime - self.iTime else: duration = 0 delay = rtime - self.iTime if self.credit_time != None and duration > self.credit_time and duration < self.credit_time + 10: duration = self.credit_time if result >= 400: try: dc = sipErrToH323Err[result][0] except: dc = '7f' elif result < 200: dc = '10' else: dc = '0' attributes.extend((('h323-disconnect-time', ftime(self.iTime + delay + duration)), \ ('h323-connect-time', ftime(self.iTime + delay)), ('Acct-Session-Time', int(duration)), \ ('h323-disconnect-cause', dc))) else: attributes.append(('h323-connect-time', ftime(self.cTime))) 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.append(('Acct-Status-Type', type)) 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 _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 incomingResponse(self, msg, t, checksum): # In those two states upper level already notified, only do ACK retransmit # if needed if t.state == TERMINATED: return if t.state == TRYING: # Stop timers if t.teA != None: t.teA.cancel() t.teA = None if t.state in (TRYING, RINGING): if t.teB != None: t.teB.cancel() t.teB = None if msg.getSCode()[0] < 200: # Privisional response - leave everything as is, except that # change state and reload timeout timer if t.state == TRYING: t.state = RINGING if t.cancelPending: self.newTransaction(t.cancel, userv=t.userv) t.cancelPending = False t.teB = Timeout(self.timerB, t.expires, 1, t) self.l1rcache[checksum] = (None, None, None) if t.resp_cb != None: if t.cb_ifver == 1: t.resp_cb(msg) else: t.resp_cb(msg, t) else: # Final response - notify upper layer and remove transaction if t.resp_cb != None: if t.cb_ifver == 1: t.resp_cb(msg) else: t.resp_cb(msg, t) if t.needack: # Prepare and send ACK if necessary fcode = msg.getSCode()[0] tag = msg.getHFBody('to').getTag() if tag != None: t.ack.getHFBody('to').setTag(tag) rAddr = None if msg.getSCode()[0] >= 200 and msg.getSCode()[0] < 300: # Some hairy code ahead if msg.countHFs('contact') > 0: rTarget = msg.getHFBody( 'contact').getUrl().getCopy() else: rTarget = None routes = [ x.getCopy() for x in msg.getHFBodys('record-route') ] routes.reverse() if len(routes) > 0: if not routes[0].getUrl().lr: if rTarget != None: routes.append( SipRoute(address=SipAddress( url=rTarget))) rTarget = routes.pop(0).getUrl() rAddr = rTarget.getAddr() else: rAddr = routes[0].getAddr() elif rTarget != None: rAddr = rTarget.getAddr() if rTarget != None: t.ack.setRURI(rTarget) if rAddr != None: t.ack.setTarget(rAddr) t.ack.delHFs('route') t.ack.appendHeaders( [SipHeader(name='route', body=x) for x in routes]) if fcode >= 200 and fcode < 300: t.ack.getHFBody('via').genBranch() if rAddr == None: rAddr = t.address if not t.uack: self.transmitMsg(t.userv, t.ack, rAddr, checksum, t.compact) else: t.state = UACK t.ack_rAddr = rAddr t.ack_checksum = checksum self.l1rcache[checksum] = (None, None, None) t.teG = Timeout(self.timerG, 64, 1, t) return else: self.l1rcache[checksum] = (None, None, None) del self.tclient[t.tid] t.cleanup()
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
def incomingRequest(self, msg, checksum, tids, server): for tid in tids: if self.tclient.has_key(tid): t = self.tclient[tid] resp = msg.genResponse(482, 'Loop Detected') self.transmitMsg(server, resp, resp.getHFBody('via').getTAddr(), checksum, \ t.compact) return if msg.getMethod() != 'ACK': tid = msg.getTId(wBRN=True) else: tid = msg.getTId(wTTG=True) t = self.tserver.get(tid, None) if t != None: #print 'existing transaction' if msg.getMethod() == t.method: # Duplicate received, check that we have sent any response on this # request already if t.data != None: self.transmitData(t.userv, t.data, t.address, checksum) return elif msg.getMethod() == 'CANCEL': # RFC3261 says that we have to reply 200 OK in all cases if # there is such transaction resp = msg.genResponse(200, 'OK') self.transmitMsg(t.userv, resp, resp.getHFBody('via').getTAddr(), checksum, \ t.compact) if t.state in (TRYING, RINGING): self.doCancel(t, msg.rtime, msg) elif msg.getMethod() == 'ACK' and t.state == COMPLETED: t.state = CONFIRMED if t.teA != None: t.teA.cancel() t.teA = None t.teD.cancel() # We have done with the transaction, no need to wait for timeout del self.tserver[t.tid] if t.ack_cb != None: t.ack_cb(msg) t.cleanup() self.l1rcache[checksum] = (None, None, None) elif msg.getMethod() == 'ACK': # Some ACK that doesn't match any existing transaction. # Drop and forget it - upper layer is unlikely to be interested # to seeing this anyway. print datetime.now(), 'unmatched ACK transaction - ignoring' sys.stdout.flush() self.l1rcache[checksum] = (None, None, None) elif msg.getMethod() == 'CANCEL': resp = msg.genResponse(481, 'Call Leg/Transaction Does Not Exist') self.transmitMsg(server, resp, resp.getHFBody('via').getTAddr(), checksum) else: #print 'new transaction', msg.getMethod() t = SipTransaction() t.tid = tid t.state = TRYING t.teA = None t.teD = None t.teE = None t.teF = None t.teG = None t.method = msg.getMethod() t.rtime = msg.rtime t.data = None t.address = None t.noack_cb = None t.ack_cb = None t.cancel_cb = None t.checksum = checksum if server.uopts.laddress[0] not in ('0.0.0.0', '[::]'): t.userv = server else: # For messages received on the wildcard interface find # or create more specific server. t.userv = self.l4r.getServer(msg.getSource()) if msg.getMethod() == 'INVITE': t.r487 = msg.genResponse(487, 'Request Terminated') t.needack = True t.branch = msg.getHFBody('via').getBranch() try: e = msg.getHFBody('expires').getNum() if e <= 0: e = 300 except IndexError: e = 300 t.teE = Timeout(self.timerE, e, 1, t) else: t.r487 = None t.needack = False t.branch = None self.tserver[t.tid] = t for consumer in self.req_consumers.get(t.tid[0], ()): cobj = consumer.cobj.isYours(msg) if cobj != None: t.compact = consumer.compact rval = cobj.recvRequest(msg, t) break else: rval = self.req_cb(msg, t) if rval == None: if t.teA != None or t.teD != None or t.teE != None or t.teF != None: return if self.tserver.has_key(t.tid): del self.tserver[t.tid] t.cleanup() return resp, t.cancel_cb, t.noack_cb = rval if resp != None: self.sendResponse(resp, t)
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): 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.getCopy()))) self.ua.rTarget = self.ua.routes.pop(0).getUrl() self.ua.rAddr = self.ua.rTarget.getAddr() 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) return (UaStateDisconnected,) return (UaStateDead,) def recvEvent(self, event): #print 'wrong event %s in the Cancelling state' % event return None
def go_offline(self): #print 'go_offline', self.address, self.online if self.online: self.online = False Timeout(self.version_check, 60)
def __init__(self, ua): UaStateGeneric.__init__(self, ua) ua.on_local_sdp_change = None ua.on_remote_sdp_change = None Timeout(self.goDead, ua.godead_timeout)
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 timerA(self, t): #print 'timerA', t self.transmitData(t.userv, t.data, t.address) t.tout *= 2 t.teA = Timeout(self.timerA, t.tout, 1, t)
def timerF(self, t): #print 'timerF', t.state t.teF = None if t.state == RINGING and self.provisional_retr > 0: self.transmitData(t.userv, t.data, t.address) t.teF = Timeout(self.timerF, self.provisional_retr, 1, t)