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 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 __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 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 __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 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 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 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 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 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 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 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()
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 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()
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)
def go_offline(self): #print 'go_offline', self.address, self.online if self.online: self.online = False Timeout(self.version_check, randomize(60, 0.1))
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)
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)