mirror of
https://github.com/RangeNetworks/openbts.git
synced 2025-10-24 16:43:58 +00:00
284 lines
11 KiB
C++
284 lines
11 KiB
C++
/*
|
|
* Copyright 2013, 2014 Range Networks, Inc.
|
|
*
|
|
* This software is distributed under multiple licenses;
|
|
* see the COPYING file in the main directory for licensing
|
|
* information for this specific distribution.
|
|
*
|
|
* This use of this software may be subject to additional restrictions.
|
|
* See the LEGAL file in the main directory for details.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
*/
|
|
// Written by Pat Thompson.
|
|
|
|
#ifndef SIPDIALOG_H
|
|
#define SIPDIALOG_H
|
|
#include <GSML3CCElements.h>
|
|
#include <ControlTransfer.h>
|
|
#include <L3TermCause.h>
|
|
#include "SIPExport.h"
|
|
#include "SIPMessage.h"
|
|
#include "SIPBase.h"
|
|
#include "SIPRtp.h"
|
|
#include "SIPTransaction.h"
|
|
|
|
|
|
namespace SIP {
|
|
|
|
class SipDialogBase: public virtual SipBase, public SipRtp
|
|
{
|
|
public:
|
|
void sipWrite(SipMessage *sipmsg);
|
|
SipDialog *dgGetDialog();
|
|
SipState MODSendCANCEL(Control::TermCause l3Cause);
|
|
void initRTP();
|
|
void MOCInitRTP();
|
|
void MTCInitRTP();
|
|
string sdbText() const;
|
|
void sdbText(std::ostringstream&os, bool verbose=false) const;
|
|
string makeSDPOffer();
|
|
string makeSDPAnswer();
|
|
int vGetRtpPort() const { return this->SipRtp::mRTPPort; }
|
|
Control::CodecSet vGetCodecs() const { return this->SipRtp::mCodec; }
|
|
};
|
|
|
|
|
|
// Typical OpenBTS message stream for MOC to MTC on same BTS:
|
|
// MOC invite->
|
|
// MOC <-100
|
|
// MTC <-invite
|
|
// MTC 100->
|
|
// MTC 180->
|
|
// MOC <-180
|
|
// MTC 200->
|
|
// MTC <-ACK
|
|
// MOC <-200
|
|
// CC Connect
|
|
|
|
class SipInviteServerTransactionLayerBase: public SipTimers, public virtual SipDialogBase
|
|
{
|
|
public:
|
|
virtual void dialogPushState(SipState newState, int code, char letter=0) = 0;
|
|
// This is used by inbound BYE or CANCEL. We dont care which because both kill off the dialog.
|
|
SipTimer mTimerJ;
|
|
void setTimerJ() { if (!dsPeer()->ipIsReliableTransport()) { mTimerJ.setOnce(64 * T1); } }
|
|
void SipMTBye(SipMessage *sipmsg);
|
|
void SipMTCancel(SipMessage *sipmsg);
|
|
};
|
|
|
|
// The SipStates used here and their correspondence to RFC3261 server transaction states are:
|
|
// Our SipState | RFC3261 INVITE server | RFC3261 non-INVITE server
|
|
// Starting | N/A | Trying
|
|
// Proceeding | Proceeding | Proceeding
|
|
// Connecting | TU sent OK, TL goes to state Terminated, but we go to Completed | N/A
|
|
// Active | Confirmed | Completed
|
|
// SSFail, various cancel/bye states
|
|
class SipMTInviteServerTransactionLayer : public virtual SipInviteServerTransactionLayerBase
|
|
{
|
|
SipTimer mTimerG; // Resend response for unreliable transport.
|
|
|
|
// TimerHJ is how long we wait for a response from the peer before declaring failure.
|
|
// For non-INVITE it is TimerJ, and for INVITE it is timerH.
|
|
// For INVITE it is the wait for additional requests to be answered with the previous response.
|
|
// In RFC3261 the 200 OK reply is passed to the TU which needs a similar delay; but we are going to use the same
|
|
// state machine to send the 200 OK response, which makes it look more similar to the non-INVITE server transaction (figure 8.)
|
|
// After an ACK is received, TimerI is how long we will soak up additional ACKs, but who cares? We will soak
|
|
// them up as long as the dialog is extant.
|
|
SipTimer mTimerH;
|
|
|
|
// There is no SIP specified timer for the 2xx response to an INVITE.
|
|
// Eventually the MS will stop waiting and we will be canceled from that side.
|
|
|
|
SipMessage mtLastResponse;
|
|
|
|
void stopTimers() { mTimerG.stop(); mTimerH.stop(); /*mTimerI.stop();*/ }
|
|
void setTimerG() { if (!dsPeer()->ipIsReliableTransport()) { mTimerG.setOnce(T1); } }
|
|
void setTimerH() { if (!dsPeer()->ipIsReliableTransport()) { mTimerH.setOnce(64 * T1); } }
|
|
|
|
protected:
|
|
void mtWriteLowSide(SipMessage *sipmsg) { // Outgoing message.
|
|
mtLastResponse = *sipmsg;
|
|
sipWrite(sipmsg);
|
|
}
|
|
|
|
public:
|
|
|
|
void MTCSendTrying();
|
|
void MTCSendRinging();
|
|
void MTCSendOK(CodecSet wCodec, const L3LogicalChannel *chan);
|
|
|
|
// Doesnt seem like messages need the private headers.
|
|
void MTSMSReply(int code, const char *explanation); // , const L3LogicalChannel *chan)
|
|
void MTSMSSendTrying();
|
|
|
|
// This can only be used for early errors before we get the ACK.
|
|
void MTCEarlyError(Control::TermCause cause); // The message must be 300-699.
|
|
|
|
// This is called for the second and subsequent received INVITEs as well as the ACK.
|
|
// We send the current response, whatever it is.
|
|
void MTWriteHighSide(SipMessage *sipmsg);
|
|
|
|
// Return TRUE to remove the dialog.
|
|
bool mtPeriodicService();
|
|
string mttlText() const; // MT Transaction Layer Text
|
|
};
|
|
|
|
|
|
class SipMOInviteClientTransactionLayer : public virtual SipInviteServerTransactionLayerBase
|
|
{
|
|
SipTimer mTimerAE; // Time to resend initial INVITE for non-reliable transport.
|
|
SipTimer mTimerBF; // Timeout during INVITE phase.
|
|
virtual void handleInviteResponse(int status, bool sendAck) = 0;
|
|
void stopTimers() { mTimerAE.stop(); mTimerBF.stop(); mTimerK.stop(); mTimerD.stop(); }
|
|
|
|
protected:
|
|
// Timers K and D are for non-invite client transactions, MO BYE and MO CANCEL.
|
|
SipTimer mTimerK; // Timeout destroys dialog.
|
|
SipTimer mTimerD; // Timeout destroys dialog.
|
|
void MOCSendINVITE(const L3LogicalChannel *chan = NULL);
|
|
void MOUssdSendINVITE(string ussd, const L3LogicalChannel *chan = NULL);
|
|
void handleSMSResponse(SipMessage *sipmsg);
|
|
void MOWriteHighSide(SipMessage *sipmsg); // Incoming message from outside, called from SIPInterface.
|
|
void moWriteLowSide(SipMessage *sipmsg); // Outgoing message from us, called only by SIPBase descendents.
|
|
|
|
public:
|
|
void MOCSendACK();
|
|
void MOSMSSendMESSAGE(const string &messageText, const string &contentType);
|
|
|
|
bool moPeriodicService(); // Return TRUE to remove the dialog.
|
|
string motlText() const; // MO Transaction Layer Text
|
|
};
|
|
|
|
// MO, uses SIP Client transaction:
|
|
// us -> INVITE -> them
|
|
// us <- 1xx whatever <- them
|
|
// us <- 200 OK <- them
|
|
// us -> ACK -> them
|
|
// We dont use server transactions for requests within an INVITE (RFC3261 section 17.2.2 Figure 8)
|
|
// - the only thing the server transaction has to do is resend the reply if a new request arrives,
|
|
// so that is just handled by the dialog.
|
|
// For the INVITE server transaction (figure 7), the transaction layer is required to resend the 2xx
|
|
// MT, uses SIP Server transaction:
|
|
// us <- INVITE <- them duplicate INVITE handled by SIP2Interface, not SipServerTransaction
|
|
// us -> 1xx whatever -> them
|
|
// us -> 200 OK -> them
|
|
// us <- ACK <- them
|
|
|
|
// The antecedent classes are virtual because they all are descendents of a single SipBase.
|
|
DEFINE_MEMORY_LEAK_DETECTOR_CLASS(SipDialog,MemCheckSipDialog)
|
|
class SipDialog : public MemCheckSipDialog, public virtual SipDialogBase,
|
|
public virtual SipMOInviteClientTransactionLayer, public virtual SipMTInviteServerTransactionLayer
|
|
{
|
|
|
|
private:
|
|
|
|
// We only send one state change message to L3 for each change of DialogState. This is the previous one we sent.
|
|
// There is no current dialog state - it is derived from SIPBase::mState
|
|
DialogState::msgState mPrevDialogState;
|
|
|
|
bool permittedTransition(DialogState::msgState oldState, DialogState::msgState newState);
|
|
// Change the sip state and possibly push a message to L3.
|
|
public: void dialogPushState(SipState newState, int code, char timer = 0);
|
|
// Possibly send a message to L3 if the SIP state has changed.
|
|
private: void dialogChangeState(SipMessage *dmsg);
|
|
void registerHandleSipCode(SipMessage *msg);
|
|
|
|
public:
|
|
Bool_z mReceived180; // The 1xx response, with the caveat that 180 (ringing) is saved over others.
|
|
|
|
// To work around the buggy smqueue we need to resend the SMS message so we need to save it.
|
|
// There is another copy saved down in the transaction layer, but it is cleaner to just save it up here
|
|
// than try to dig it out of the lower layer resend machinery.
|
|
string smsBody, smsContentType; // Temporary, until smqueue is fixed.
|
|
|
|
void dgReset();
|
|
DialogState::msgState getDialogState() const;
|
|
bool isActive() const { return getDialogState() == DialogState::dialogActive; }
|
|
bool isFinished() const { DialogState::msgState st = getDialogState(); return st == DialogState::dialogFail || st == DialogState::dialogBye; }
|
|
bool dgIsDeletable() const;
|
|
const char *dialogStateString() const { return DialogState::msgStateString(getDialogState()); }
|
|
//void dialogOpen(const char *userid); // The userid is the IMSI.
|
|
//void dialogClose();
|
|
void MODSendBYE(Control::TermCause l3Cause);
|
|
void sendInfoDtmf(unsigned bcdkey);
|
|
|
|
// Send an error code that terminates the dialog.
|
|
void sendError(int code, const char *errorString) {LOG(ALERT) << "unimplemented"<<code<<errorString;} // TODO;
|
|
|
|
// Dont call this directly. Use one of the static newSipDialog.... methods.
|
|
SipDialog(DialogType wDialogType,
|
|
string wProxy, // The proxy IP address or DNS name.
|
|
const char * wProxyProvenance): // A helpful message in case the proxy address cannot be resolved.
|
|
mPrevDialogState(DialogState::dialogUndefined)
|
|
{
|
|
SipBaseInit(wDialogType, wProxy, wProxyProvenance);
|
|
}
|
|
|
|
// This is the new way:
|
|
static SipDialog *newSipDialogMT(DialogType dtype,
|
|
SipMessage *request); // INVITE or MESSAGE.
|
|
|
|
static SipDialog *newSipDialogMOC(TranEntryId tranid, const FullMobileId &msid,
|
|
const string&wCalledDigits, Control::CodecSet wCodecs,
|
|
L3LogicalChannel *chan);
|
|
static SipDialog *newSipDialogMOUssd(TranEntryId tranid, const FullMobileId &msid, const string&wUssd, L3LogicalChannel *chan);
|
|
static SipDialog *newSipDialogMOSMS(TranEntryId tranid, const FullMobileId &msid, const string &calledDigits, const string &body, const string &contentType);
|
|
static SipDialog *newSipDialogRegister1();
|
|
static SipDialog *newSipDialogHandover(TranEntry *tran, string sipReferStr);
|
|
|
|
virtual ~SipDialog();
|
|
void sipStartTimers() { // We dont do it this way any more.
|
|
}
|
|
void sipStopTimers() { // We dont do it this way any more.
|
|
}
|
|
bool dgWriteHighSide(SipMessage *msg) {
|
|
switch (mDialogType) {
|
|
case SIPDTMTC: case SIPDTMTSMS:
|
|
MTWriteHighSide(msg); return true;
|
|
case SIPDTMOC: case SIPDTMOSMS: case SIPDTMOUssd:
|
|
MOWriteHighSide(msg); return true;
|
|
default: return false; // This indicates a serious error on the part of the SIP peer.
|
|
}
|
|
}
|
|
|
|
void dialogCancel(TermCause cause/*, GSM::CCCause l3Cause*/);
|
|
//void dialogMOCSendInvite(const char *bcddigits,Control::CodecSet codecs);
|
|
void dialogWriteDownlink(SipMessage *msg);
|
|
void handleInviteResponse(int status, bool sendAck);
|
|
bool dialogPeriodicService();
|
|
string dialogText(bool verbose=true) const;
|
|
Control::TranEntry *createMTTransaction(SipMessage *invite);
|
|
Control::TranEntry *findTranEntry();
|
|
void handleUssdBye(SipMessage *msg);
|
|
/**@name Messages for SIP registration. */
|
|
/**
|
|
Send sip register and look at return msg.
|
|
Can throw SIPTimeout().
|
|
@return True on success.
|
|
*/
|
|
SipMessage *makeRegisterMsg(DialogType wMethod, const L3LogicalChannel* chan, string RAND, const FullMobileId &msid, const char *SRES = NULL);
|
|
|
|
/**
|
|
Send sip unregister and look at return msg.
|
|
Can throw SIPTimeout().
|
|
@return True on success.
|
|
*/
|
|
//bool unregister() { return (Register(SIPDTUnregister)); };
|
|
string vsdbText() const { return sdbText(); }
|
|
SipState vgetSipState() const { return getSipState(); }
|
|
};
|
|
|
|
std::ostream& operator<<(std::ostream& os, const DialogState);
|
|
std::ostream& operator<<(std::ostream& os, const SipDialog&);
|
|
std::ostream& operator<<(std::ostream& os, const SipDialogRef&);
|
|
std::ostream& operator<<(std::ostream& os, const SipDialog*);
|
|
|
|
extern SipDialog *gRegisterDialog;
|
|
|
|
}; // namespace
|
|
#endif
|