def _matchToDialog(msg, origin, dest, dialogs): dialog= dialogs.get( (msg.headers['call-id'][0], parseAddress(msg.headers[origin][0])[2].get('tag',''), parseAddress(msg.headers[dest][0])[2].get('tag','')), None) return dialog
def _findDest(self, msg): rs = msg.headers.get('route', None) if rs: dest = parseAddress(rs[0])[1] else: dest = self.remoteAddress[1] return dest
def forServer(cls, tu, contactURI, msg): """ Create a dialog from a received INVITE. tu: the transaction user handling this dialog. contactURI: the contact for the other party. msg: the initial INVITE that establishes this dialog. """ #RFC 3261 12.1.1 self = cls(tu, contactURI) self.msg = msg toAddress, fromAddress = self._finishInit() self.localAddress = toAddress self.remoteAddress = fromAddress self.localAddress[2]['tag'] = self.genTag() self.direction = "server" self.routeSet = [ parseAddress(route) for route in self.msg.headers.get('record-route', []) ] self.clientState = "confirmed" def gotRTP(rtp): self.rtp = rtp return self.rtp.createRTPSocket(self, contactURI.host) def gotCookie(c): self.cookie = c return self return self.tu.mediaController.getProcess().addCallback( gotRTP).addCallback(gotCookie)
def forServer(cls, tu, contactURI, msg): """ Create a dialog from a received INVITE. tu: the transaction user handling this dialog. contactURI: the contact for the other party. msg: the initial INVITE that establishes this dialog. """ #RFC 3261 12.1.1 self = cls(tu, contactURI) self.msg = msg toAddress, fromAddress = self._finishInit() self.localAddress = toAddress self.remoteAddress = fromAddress self.localAddress[2]['tag'] = self.genTag() self.direction = "server" self.routeSet = [parseAddress(route) for route in self.msg.headers.get('record-route', [])] self.clientState = "confirmed" def gotRTP(rtp): self.rtp = rtp return self.rtp.createRTPSocket(self, contactURI.host) def gotCookie(c): self.cookie = c return self return self.tu.mediaController.getProcess().addCallback(gotRTP).addCallback(gotCookie)
def requestReceived(self, msg, addr): #RFC 3261 12.2.2 if msg.method == "INVITE": st = ServerInviteTransaction(self.transport, self, msg, addr) else: st = ServerTransaction(self.transport, self, msg, addr) #dialog checking dialog = matchRequestToDialog(msg, self.dialogs) #untagged requests must be checked against ongoing transactions # see 8.2.2.2 if not dialog and parseAddress(msg.headers['to'][0])[2].get('tag',None): #uh oh, there was an expectation of a dialog #but we can't remember it (maybe we crashed?) st.messageReceivedFromTU(responseFromRequest(481, msg)) return defer.succeed(st) #authentication #check for Require m = getattr(self, "process_" + msg.method, None) if not m: st.messageReceivedFromTU(responseFromRequest(405, msg)) return defer.succeed(st) else: return defer.maybeDeferred(m, st, msg, addr, dialog).addCallback( lambda x: st)
def lookupElement(dialog): avatar = self.voicesystem.localElementByName( parseAddress(msg.headers['to'][0])[1].username) dialog.callController = avatar.buildCallController(dialog) if msg.body: sdp = SDP(msg.body) else: sdp = None d = defer.maybeDeferred(dialog.rtp.getSDP, dialog, sdp) def gotSDP(mysdp): dialog.sessionDescription = mysdp if not mysdp.hasMediaDescriptions(): st.messageReceivedFromTU(responseFromRequest(406, msg)) return st if sdp: self.maybeStartAudio(dialog, sdp) self.dialogs[dialog.getDialogID()] = dialog response = dialog.responseFromRequest(200, msg, mysdp.show()) st.messageReceivedFromTU(response) dialog.ackTimerRetry(response) d.addCallback(gotSDP) return d
def requestReceived(self, msg, addr): #RFC 3261 12.2.2 if msg.method == "INVITE": st = ServerInviteTransaction(self.transport, self, msg, addr) else: st = ServerTransaction(self.transport, self, msg, addr) #dialog checking dialog = matchRequestToDialog(msg, self.dialogs) #untagged requests must be checked against ongoing transactions # see 8.2.2.2 if not dialog and parseAddress(msg.headers['to'][0])[2].get( 'tag', None): #uh oh, there was an expectation of a dialog #but we can't remember it (maybe we crashed?) st.messageReceivedFromTU(responseFromRequest(481, msg)) return defer.succeed(st) #authentication #check for Require m = getattr(self, "process_" + msg.method, None) if not m: st.messageReceivedFromTU(responseFromRequest(405, msg)) return defer.succeed(st) else: return defer.maybeDeferred(m, st, msg, addr, dialog).addCallback(lambda x: st)
def responseReceived(self, response, ct=None): #OK this function is a bit hairy because I don't want to track #any call state in this class and responses to various things #need to be handled differently. The main event is 2xx #responses to the INVITE -- that changes the early dialog #(created when the INVITE was sent) to an confirmed dialog. #Error responses result in dialog teardown, as do responses to BYEs. #RFC 3261 12.2.1.2 dialog = self.cts.get(ct, None) if dialog is None: dialog = matchResponseToDialog(response, self.dialogs) if 'INVITE' in response.headers['cseq'][0] and 200 <= response.code < 300: #possibly this line doesn't belong here? earlyResponseReceived #does it too but IIRC it can't come before sending the ack dialog.remoteAddress = parseAddress(response.headers['to'][0]) self.acknowledgeInvite(dialog, response) if dialog.clientState == "early": self.earlyResponseReceived(dialog, response, ct) if dialog.clientState == "byeSent": self.byeResponseReceived(dialog, response, ct) elif dialog.clientState == "reinviteSent": self.reinviteResponseReceived(dialog, response, ct)
def lookupProcessor(self, msg, dialogs): if isinstance(msg, sip.Request) and msg.method == "REGISTER": #not our dept return defer.succeed(None) for name, domain in userbase.getAccountNames(self.store, protocol=u'sip'): if name == sip.parseAddress(msg.headers["to"][0])[1].username: contact = sip.IContact(self.store) def regged(_): return defer.succeed(None) def unregged(e): self.uas.dialogs = dialogs return self.uas return defer.maybeDeferred(contact.getRegistrationInfo, sip.parseAddress(msg.headers["from"][0])[1]).addCallbacks(regged, unregged) else: return defer.succeed(None)
def responseReceived(self, response, ct=None): #OK this function is a bit hairy because I don't want to track #any call state in this class and responses to various things #need to be handled differently. The main event is 2xx #responses to the INVITE -- that changes the early dialog #(created when the INVITE was sent) to an confirmed dialog. #Error responses result in dialog teardown, as do responses to BYEs. #RFC 3261 12.2.1.2 dialog = self.cts.get(ct, None) if dialog is None: dialog = matchResponseToDialog(response, self.dialogs) if 'INVITE' in response.headers['cseq'][ 0] and 200 <= response.code < 300: #possibly this line doesn't belong here? earlyResponseReceived #does it too but IIRC it can't come before sending the ack dialog.remoteAddress = parseAddress(response.headers['to'][0]) self.acknowledgeInvite(dialog, response) if dialog.clientState == "early": self.earlyResponseReceived(dialog, response, ct) if dialog.clientState == "byeSent": self.byeResponseReceived(dialog, response, ct) elif dialog.clientState == "reinviteSent": self.reinviteResponseReceived(dialog, response, ct)
def earlyResponseReceived(self, dialog, response, ct): if 200 <= response.code < 300: #RFC 3261 12.1.2 dialog.clientState = "confirmed" dialog.remoteAddress = parseAddress(response.headers['to'][0]) dialog.routeSet = [parseAddress(route) for route in response.headers.get('record-route', [])[::-1]] self.dialogs[dialog.getDialogID()] = dialog sdp = SDP(response.body) self.maybeStartAudio(dialog, sdp) if self.controller: self.controller.callBegan(dialog) elif 300 <= response.code < 400: raise NotImplemented, "Dunno about redirects yet" elif 400 <= response.code < 700: if dialog.getDialogID() in self.dialogs: del self.dialogs[dialog.getDialogID()] del self.cts[ct] self.controller.callFailed(dialog, response)
def earlyResponseReceived(self, dialog, response, ct): if 200 <= response.code < 300: #RFC 3261 12.1.2 dialog.clientState = "confirmed" dialog.remoteAddress = parseAddress(response.headers['to'][0]) dialog.routeSet = [ parseAddress(route) for route in response.headers.get('record-route', [])[::-1] ] self.dialogs[dialog.getDialogID()] = dialog sdp = SDP(response.body) self.maybeStartAudio(dialog, sdp) if self.controller: self.controller.callBegan(dialog) elif 300 <= response.code < 400: raise NotImplemented, "Dunno about redirects yet" elif 400 <= response.code < 700: if dialog.getDialogID() in self.dialogs: del self.dialogs[dialog.getDialogID()] del self.cts[ct] self.controller.callFailed(dialog, response)
def fuzzyMatch(self, first, second): "try to ignore bits randomly generated by our code" self.assertEqual(first.__class__, second.__class__) self.assertEqual(first.version, second.version) if isinstance(first, sip.Request): self.assertEqual(first.method, second.method) self.assertEqual(first.uri, second.uri) else: self.assertEqual(first.code, second.code) for header in first.headers.keys(): if not second.headers.get(header): if not first.headers[header]: #woops, it's empty, never mind continue raise unittest.FailTest("%s not present in %s" % (header, second)) if header in ('from', 'to', 'contact'): #strip tags if isinstance(first.headers[header][0], sip.URL): firsturl = first.headers[header][0] else: firsturl = sip.parseAddress(first.headers[header][0])[1] secondurl = sip.parseAddress(second.headers[header][0])[1] self.assertEqual(firsturl, secondurl) elif header == "via": firstvia = [sip.parseViaHeader(h) for h in first.headers['via']] secondvia = [sip.parseViaHeader(h) for h in second.headers['via']] #convert to strings for easy reading of output self.assertEqual([x.toString() for x in firstvia], [x.toString() for x in firstvia]) elif header == "content-length": continue else: self.assertEqual([str(x) for x in first.headers[header]], [str(x) for x in second.headers[header]])
def reinviteResponseReceived(self, dialog, response, ct): if response.code == 491: if dialog.direction == "client": reactor.callLater(random.randint(210,400)/100.0, dialog._sendReinvite, dialog.reinviteMsg) else: reactor.callLater(random.randint(0, 200)/100.0, dialog._sendReinvite, dialog.reinviteMsg) elif 200 <= response.code < 300: dialog.clientState = "confirmed" dialog.msg = dialog.reinviteMsg dialog.contactURI = parseAddress(dialog.msg.headers['contact'][0])[1] dialog.sessionDescription = dialog.reinviteSDP else: dialog.clientState = "confirmed"
def gotSDP(mysdp): if not mysdp.hasMediaDescriptions(): st.messageReceivedFromTU(responseFromRequest(488, msg)) return st dialog.sessionDescription = mysdp if dialog.clientState == "reinviteSent": st.messageReceivedFromTU(dialog.responseFromRequest(491, msg)) else: if sdp: self.maybeStartAudio(dialog, sdp) dialog.msg = msg dialog.reinviteMsg = True dialog.remoteAddress = parseAddress(msg.headers['from'][0]) response = dialog.responseFromRequest(200, msg, mysdp.show()) st.messageReceivedFromTU(response) dialog.ackTimerRetry(response) return st
def generateRequest(self, method): #RFC 3261 12.2.1.1 r = Request(method, self.remoteAddress[1]) if self.routeSet: r.headers['route'] = [formatAddress(route) for route in self.routeSet] if 'lr' not in self.routeSet[0][1].other: r.headers['route'].append(formatAddress(("", r.uri, {}))) r.uri = parseAddress(r.headers['route'].pop())[1] r.addHeader('to', formatAddress(self.remoteAddress)) r.addHeader('from', formatAddress(self.localAddress)) r.addHeader('cseq', "%s %s" % (self.localCSeq, method)) self.localCSeq += 1 r.addHeader('call-id', self.msg.headers['call-id'][0]) r.addHeader('contact', formatAddress(self.contactURI)) r.addHeader('content-length', 0) return r
def reinviteResponseReceived(self, dialog, response, ct): if response.code == 491: if dialog.direction == "client": reactor.callLater( random.randint(210, 400) / 100.0, dialog._sendReinvite, dialog.reinviteMsg) else: reactor.callLater( random.randint(0, 200) / 100.0, dialog._sendReinvite, dialog.reinviteMsg) elif 200 <= response.code < 300: dialog.clientState = "confirmed" dialog.msg = dialog.reinviteMsg dialog.contactURI = parseAddress( dialog.msg.headers['contact'][0])[1] dialog.sessionDescription = dialog.reinviteSDP else: dialog.clientState = "confirmed"
def generateRequest(self, method): #RFC 3261 12.2.1.1 r = Request(method, self.remoteAddress[1]) if self.routeSet: r.headers['route'] = [ formatAddress(route) for route in self.routeSet ] if 'lr' not in self.routeSet[0][1].other: r.headers['route'].append(formatAddress(("", r.uri, {}))) r.uri = parseAddress(r.headers['route'].pop())[1] r.addHeader('to', formatAddress(self.remoteAddress)) r.addHeader('from', formatAddress(self.localAddress)) r.addHeader('cseq', "%s %s" % (self.localCSeq, method)) self.localCSeq += 1 r.addHeader('call-id', self.msg.headers['call-id'][0]) r.addHeader('contact', formatAddress(self.contactURI)) r.addHeader('content-length', 0) return r
def gotSDP(mysdp): if not mysdp.hasMediaDescriptions(): st.messageReceivedFromTU(responseFromRequest(488, msg)) return st dialog.sessionDescription = mysdp if dialog.clientState == "reinviteSent": st.messageReceivedFromTU( dialog.responseFromRequest(491, msg)) else: if sdp: self.maybeStartAudio(dialog, sdp) dialog.msg = msg dialog.reinviteMsg = True dialog.remoteAddress = parseAddress(msg.headers['from'][0]) response = dialog.responseFromRequest( 200, msg, mysdp.show()) st.messageReceivedFromTU(response) dialog.ackTimerRetry(response) return st
def lookupElement(dialog): avatar = self.voicesystem.localElementByName(parseAddress(msg.headers['to'][0])[1].username) dialog.callController = avatar.buildCallController(dialog) if msg.body: sdp = SDP(msg.body) else: sdp = None d = defer.maybeDeferred(dialog.rtp.getSDP, dialog, sdp) def gotSDP(mysdp): dialog.sessionDescription = mysdp if not mysdp.hasMediaDescriptions(): st.messageReceivedFromTU(responseFromRequest(406, msg)) return st if sdp: self.maybeStartAudio(dialog, sdp) self.dialogs[dialog.getDialogID()] = dialog response = dialog.responseFromRequest(200, msg, mysdp.show()) st.messageReceivedFromTU(response) dialog.ackTimerRetry(response) d.addCallback(gotSDP) return d
def test3PCC(self): self.svc.setupCallBetween(sip.parseAddress("sip:[email protected]"), sip.parseAddress("sip:[email protected]"))
def process_INVITE(self, st, msg, addr, dialog): #RFC 3261 13.3.1 if dialog: #it's a reinvite if msg.body: #new SDP ahoy sdp = SDP(msg.body) else: sdp = None d = defer.maybeDeferred(dialog.rtp.getSDP, dialog, sdp) def gotSDP(mysdp): if not mysdp.hasMediaDescriptions(): st.messageReceivedFromTU(responseFromRequest(488, msg)) return st dialog.sessionDescription = mysdp if dialog.clientState == "reinviteSent": st.messageReceivedFromTU(dialog.responseFromRequest(491, msg)) else: if sdp: self.maybeStartAudio(dialog, sdp) dialog.msg = msg dialog.reinviteMsg = True dialog.remoteAddress = parseAddress(msg.headers['from'][0]) response = dialog.responseFromRequest(200, msg, mysdp.show()) st.messageReceivedFromTU(response) dialog.ackTimerRetry(response) return st return d.addCallback(gotSDP) #otherwise, time to start a new dialog d = Dialog.forServer(self, URL(self.host, parseAddress(msg.headers['to'][0])[1].username), msg) def lookupElement(dialog): avatar = self.voicesystem.localElementByName(parseAddress(msg.headers['to'][0])[1].username) dialog.callController = avatar.buildCallController(dialog) if msg.body: sdp = SDP(msg.body) else: sdp = None d = defer.maybeDeferred(dialog.rtp.getSDP, dialog, sdp) def gotSDP(mysdp): dialog.sessionDescription = mysdp if not mysdp.hasMediaDescriptions(): st.messageReceivedFromTU(responseFromRequest(406, msg)) return st if sdp: self.maybeStartAudio(dialog, sdp) self.dialogs[dialog.getDialogID()] = dialog response = dialog.responseFromRequest(200, msg, mysdp.show()) st.messageReceivedFromTU(response) dialog.ackTimerRetry(response) d.addCallback(gotSDP) return d def failedLookup(err): err.trap(NoSuchUser, UnauthorizedLogin) raise SIPLookupError(604) return d.addCallback(lookupElement).addErrback(failedLookup)
def _finishInit(self): self.callID = self.msg.headers['call-id'][0] toAddress = parseAddress(self.msg.headers['to'][0]) fromAddress = parseAddress(self.msg.headers['from'][0]) return toAddress, fromAddress
def process_INVITE(self, st, msg, addr, dialog): #RFC 3261 13.3.1 if dialog: #it's a reinvite if msg.body: #new SDP ahoy sdp = SDP(msg.body) else: sdp = None d = defer.maybeDeferred(dialog.rtp.getSDP, dialog, sdp) def gotSDP(mysdp): if not mysdp.hasMediaDescriptions(): st.messageReceivedFromTU(responseFromRequest(488, msg)) return st dialog.sessionDescription = mysdp if dialog.clientState == "reinviteSent": st.messageReceivedFromTU( dialog.responseFromRequest(491, msg)) else: if sdp: self.maybeStartAudio(dialog, sdp) dialog.msg = msg dialog.reinviteMsg = True dialog.remoteAddress = parseAddress(msg.headers['from'][0]) response = dialog.responseFromRequest( 200, msg, mysdp.show()) st.messageReceivedFromTU(response) dialog.ackTimerRetry(response) return st return d.addCallback(gotSDP) #otherwise, time to start a new dialog d = Dialog.forServer( self, URL(self.host, parseAddress(msg.headers['to'][0])[1].username), msg) def lookupElement(dialog): avatar = self.voicesystem.localElementByName( parseAddress(msg.headers['to'][0])[1].username) dialog.callController = avatar.buildCallController(dialog) if msg.body: sdp = SDP(msg.body) else: sdp = None d = defer.maybeDeferred(dialog.rtp.getSDP, dialog, sdp) def gotSDP(mysdp): dialog.sessionDescription = mysdp if not mysdp.hasMediaDescriptions(): st.messageReceivedFromTU(responseFromRequest(406, msg)) return st if sdp: self.maybeStartAudio(dialog, sdp) self.dialogs[dialog.getDialogID()] = dialog response = dialog.responseFromRequest(200, msg, mysdp.show()) st.messageReceivedFromTU(response) dialog.ackTimerRetry(response) d.addCallback(gotSDP) return d def failedLookup(err): err.trap(NoSuchUser, UnauthorizedLogin) raise SIPLookupError(604) return d.addCallback(lookupElement).addErrback(failedLookup)
def _matchToDialog(msg, origin, dest, dialogs): dialog = dialogs.get( (msg.headers['call-id'][0], parseAddress( msg.headers[origin][0])[2].get('tag', ''), parseAddress(msg.headers[dest][0])[2].get('tag', '')), None) return dialog