mirror of
https://github.com/RangeNetworks/openbts.git
synced 2025-10-23 07:42:01 +00:00
396 lines
17 KiB
C++
396 lines
17 KiB
C++
/*
|
|
* Copyright 2008 Free Software Foundation, Inc.
|
|
* Copyright 2010 Kestrel Signal Processing, Inc.
|
|
* Copyright 2011, 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.
|
|
|
|
*/
|
|
#ifndef _SIPBase_H_
|
|
#define _SIPBase_H_ 1
|
|
#include <string>
|
|
#include <sys/time.h>
|
|
#include <sys/types.h>
|
|
#include <semaphore.h>
|
|
|
|
|
|
|
|
#include <Sockets.h>
|
|
#include <ByteVector.h>
|
|
#include <CodecSet.h>
|
|
#include <Timeval.h>
|
|
#include <ControlTransfer.h>
|
|
#include "SIPUtility.h"
|
|
#include "SIPMessage.h"
|
|
|
|
extern int gCountSipDialogs;
|
|
|
|
namespace Control { class L3LogicalChannel; }
|
|
|
|
namespace SIP {
|
|
class SipDialog;
|
|
typedef RefCntPointer<SipDialog> SipDialogRef;
|
|
using namespace Control;
|
|
using namespace std;
|
|
|
|
extern bool gPeerTestedForSmqueue;
|
|
|
|
// These could be global.
|
|
extern string localIP(); // Replaces mSIPIP.
|
|
extern string localIPAndPort(); // Replaces mSIPIP and mSIPPort.
|
|
|
|
// These are the overall dialog states.
|
|
enum SipState {
|
|
SSNullState,
|
|
SSTimeout,
|
|
Starting, // (pat) MOC or MOSMS or inboundHandover sent INVITE; MTC not used.
|
|
Proceeding, // (pat) MOC received Trying, Queued, BeingForwarded; MTC sent Trying
|
|
Ringing, // (pat) MOC received Ringing, notably not used for MTC sent Ringing, which is probably a bug of no import.
|
|
MOCBusy, // (pat) MOC received Busy; MTC not used.
|
|
Connecting, // (pat) MTC sent OK.
|
|
Active, // (pat) MOC received OK; MTC sent ACK
|
|
MODClearing, // (pat) MOD sent BYE
|
|
MODCanceling, // (pat) MOD sent a cancel, see forceSIPClearing.
|
|
MODError, // (pat) MOD sent an error response, see forceSIPClearing.
|
|
MTDClearing, // (pat) MTD received BYE.
|
|
MTDCanceling, // (pat) MTD received CANCEL
|
|
Canceled, // (pat) received OK to CANCEL.
|
|
Cleared, // (pat) MTD sent OK to BYE, or MTD internal error loss of FIFO, or MOSMS received OK, or MTSMS sent OK.
|
|
|
|
//SipRegister, // (pat) This SIPEngine is being used for registration, none of the other stuff applies.
|
|
//SipUnregister, // (pat) This SIPEngine is being used for registration, none of the other stuff applies.
|
|
SSFail,
|
|
|
|
MOSMSSubmit, // (pat) SMS message submitted, "MESSAGE" method. Set but never used. Message success indicated by Cleared.
|
|
// (pat) The handover states are invalid SIPEngine states, set during initial state for handover,
|
|
// used only to check if SIPEngine is dead because this state was never changed to a valid SIP state.
|
|
HandoverInbound,
|
|
//HandoverInboundReferred, // pat removed, totally unused.
|
|
HandoverOutbound // (pat) Another fine state that we dont ever use.
|
|
};
|
|
std::ostream& operator<<(std::ostream& os, SipState s);
|
|
extern const char* SipStateString(SipState s);
|
|
|
|
enum DialogType {
|
|
SIPDTUndefined,
|
|
SIPDTRegister,
|
|
SIPDTUnregister,
|
|
SIPDTMOC, // Mobile Originate Call
|
|
SIPDTMTC, // Mobile Terminate Call
|
|
SIPDTMOSMS,
|
|
SIPDTMTSMS,
|
|
SIPDTMOUssd
|
|
};
|
|
|
|
class DialogMessage;
|
|
class SipTransaction;
|
|
|
|
// This is a simple attachment point for a TranEntry.
|
|
class SipEngine {
|
|
|
|
public:
|
|
Control::TranEntryId mTranId; // So we can find the TransactionEntry that owns this SIPEngine.
|
|
|
|
void dialogQueueMessage(DialogMessage *dmsg);
|
|
|
|
void setTranId(Control::TranEntryId wTranId) { mTranId = wTranId; }
|
|
|
|
// Dont delete these empty constructor/destructors. They are needed because InterthreadQueue calls
|
|
// delete DialogMessage and it causes weird error messages without these.
|
|
SipEngine();
|
|
virtual ~SipEngine();
|
|
};
|
|
|
|
// This class is for variables that not even SipBase is allowed to change directly.
|
|
class SipBaseProtected
|
|
{
|
|
virtual void _define_vtable();
|
|
// You must not change mState without going through setSipState to make sure the state age is updated.
|
|
SipState mState; ///< current SIP call state
|
|
Timeval mStateAge;
|
|
virtual DialogType vgetDialogType() const = 0;
|
|
|
|
public:
|
|
/** Return the current SIP call state. */
|
|
// (pat) This is the dialog state, if we are a dialog.
|
|
SipState getSipState() const { return mState; }
|
|
// (pat) Set the dialog state. The purpose of this protected class is to enforce that the dialog type is set
|
|
// only with this method by all internal and external users.
|
|
void setSipState(SipState wState) { mState=wState; mStateAge.now(); }
|
|
|
|
/** Return TRUE if it looks like the SIP session is stuck indicating some internal error. */
|
|
bool sipIsStuck() const;
|
|
|
|
/** Return true if the call has finished, successfully or not. */
|
|
bool sipIsFinished() const {
|
|
switch (mState) {
|
|
case Cleared: case MODClearing: case MTDClearing:
|
|
case Canceled: case MODCanceling: case MTDCanceling:
|
|
case SSFail: case MODError:
|
|
case SSTimeout:
|
|
return true;
|
|
case SSNullState:
|
|
case Starting:
|
|
case Proceeding:
|
|
case Ringing:
|
|
case MOCBusy:
|
|
case Connecting:
|
|
case Active:
|
|
case MOSMSSubmit:
|
|
case HandoverInbound:
|
|
case HandoverOutbound:
|
|
return false;
|
|
}
|
|
return false; /*NOTREACHED*/
|
|
}
|
|
|
|
SipBaseProtected() : mState(SSNullState) { mStateAge.now(); }
|
|
};
|
|
|
|
|
|
// The 'Dialog State' is a defined term in RFC3261. It is established by the INVITE and 2xx reply,
|
|
// and can only be modified by re-INVITE.
|
|
// Be careful with the name - we also use a DialogState to mean the integral dialog state, so we call this a context.
|
|
class DialogStateVars {
|
|
protected:
|
|
friend class SipTransaction;
|
|
// RFC3261 uses the terms 'remote URI' and 'remote target URI' interchangably.
|
|
// The To and From headers optionally include a Display Name as well as the URI.
|
|
// We are going to save the whole contact here, including the display name,
|
|
// even though the Display Names are technically not part of the DialogStateVars.
|
|
SipPreposition mLocalHeader; // Used in From:
|
|
SipPreposition mRemoteHeader; // Used in To: and sometimes in request-URI. Updated from Contact: header.
|
|
string mRouteSet; // Complicated. See RFC-3261 12.2.1.1 Immutable once set.
|
|
std::string mCallId; // (pat) dialog-id for inbound sip messages but also used for some outbound messages.
|
|
// (pat 5-2104) The proxy address is set from config options depending on the type of message.
|
|
// We currently dont support multiple upside peers. We dont return the message using the via for example.
|
|
IPAddressSpec mProxy; // The remote URL.
|
|
public:
|
|
unsigned mLocalCSeq; // The local CSeq for in-dialog requests. We dont bother tracking the remote CSeq.
|
|
// 14.1: The Cseq for re-INVITE follows same rules for in-dialog requests, namely,
|
|
// CSeq num must be incremented by one.
|
|
string dsPAssociatedUri;
|
|
string dsPAssertedIdentity;
|
|
|
|
DialogStateVars();
|
|
string dsLocalTag() { return mLocalHeader.mTag; }
|
|
string dsRemoteTag() { return mRemoteHeader.mTag; }
|
|
string sipLocalUsername() const { return mLocalHeader.mUri.uriUsername(); } // This is the IMSI or IMEI.
|
|
string sipRemoteUsername() const { return mRemoteHeader.mUri.uriUsername(); }
|
|
string sipRemoteDisplayname() const { return mRemoteHeader.mDisplayName.empty() ? sipRemoteUsername() : dequote(mRemoteHeader.mDisplayName); }
|
|
|
|
// Various ways to set local and remote headers:
|
|
//void dsSetLocalNameAndHost(string username, string host, unsigned port = 0) {
|
|
// mLocalHeader.prepSetUri(makeUri(username,host,port));
|
|
//}
|
|
// This sets the local URI without a tag. It is used for non-dialogs
|
|
private:
|
|
void dsSetLocalUri(string uri) { mLocalHeader.prepSetUri(uri); }
|
|
public:
|
|
void dsSetRemoteUri(string uri) { mRemoteHeader.prepSetUri(uri); }
|
|
//void dsSetRemoteNameAndHost(string username, string host, unsigned port = 0) {
|
|
// mRemoteHeader.prepSetUri(makeUri(username,host,port));
|
|
//}
|
|
void dsSetLocalHeaderMT(SipPreposition *toheader, bool addTag);
|
|
void dsSetLocalMO(const FullMobileId &msid, bool addTag);
|
|
// Set local header directly, used for handover:
|
|
void dsSetLocalHeader(const SipPreposition *header) { LOG(DEBUG); mLocalHeader = *header; }
|
|
void dsSetRemoteHeader(const SipPreposition *header) { LOG(DEBUG); mRemoteHeader = *header; }
|
|
void dsSetCallId(const std::string wCallId) { mCallId = wCallId; }
|
|
|
|
// RFC3261 12.2.1.1: "The URI in the To field of the request MUST be set to the remote URI
|
|
// from the dialog state. The tag in the To header field of the request
|
|
// MUST be set to the remote tag of the dialog ID. The From URI of the
|
|
// request MUST be set to the local URI from the dialog state. The tag
|
|
// in the From header field of the request MUST be set to the local tag
|
|
// of the dialog ID. If the value of the remote or local tags is null,
|
|
// the tag parameter MUST be omitted from the To or From header fields, respectively.
|
|
|
|
// Accessors:
|
|
// We set the remote-tag when we create the URI for a dialog.
|
|
// We set the local-tag the first time we hear from the peer, which for an inbound INVITE is
|
|
// in that invite, or for outbound INVITE is the first response.
|
|
//string dsRequestToHeader() const { return mRemoteHeader.toFromValue(); }
|
|
//string dsRequestFromHeader() const { return mLocalHeader.toFromValue(); }
|
|
// This does alot of copying, but at least they are strings so it is just refcnt incrementing.
|
|
const SipPreposition *dsRequestToHeader() const { return &mRemoteHeader; }
|
|
const SipPreposition *dsRequestFromHeader() const { return &mLocalHeader; }
|
|
//string dsReplyToHeader() const { return mLocalHeader.toFromValue(); }
|
|
//string dsReplyFromHeader() const { return mRemoteHeader.toFromValue(); }
|
|
const SipPreposition *dsReplyToHeader() const { return &mLocalHeader; }
|
|
const SipPreposition *dsReplyFromHeader() const { return &mRemoteHeader; }
|
|
//string dsRemoteURI() const { return mRemoteHeader.mUri.uriValue(); }
|
|
string dsRemoteURI() const { return format("sip:%s@%s",sipRemoteUsername(),mProxy.mipName); }
|
|
// TODO_NOW: THIS IS WRONG! See SipMessageRequestWithinDialog
|
|
//string dsInDialogRequestURI() const { return mRemoteHeader.mUri.uriAddress(); }
|
|
string dsInDialogRequestURI() const { return dsRemoteURI(); }
|
|
unsigned dsNextCSeq() { return ++mLocalCSeq; }
|
|
string dsToString() const;
|
|
const std::string& callId() const { return mCallId; }
|
|
const IPAddressSpec *dsPeer() const { return &mProxy; }
|
|
string localIP() const { return SIP::localIP(); }
|
|
string localIPAndPort() const { return SIP::localIPAndPort(); }
|
|
void updateProxy(const char *sqlOption);
|
|
};
|
|
|
|
|
|
// SipBase is intended to have no dependencies on the rest of OpenBTS so that it can be used as the base class for
|
|
// the message parser classes, allowing them to be used stand-alone.
|
|
// SipDialog is the class with many dependencies elsewhere, including, for example, gSipInterface and SipRtp.
|
|
// SipBase includes much of the state for a SIP dialog, but SipBase can also be used to parse non-dialog SIP messages,
|
|
// in which case all the dialog state stuff is just ignored.
|
|
DEFINE_MEMORY_LEAK_DETECTOR_CLASS(SipBase,MemCheckSipBase)
|
|
class SipBase : public MemCheckSipBase,
|
|
public RefCntBase, public SipEngine, public DialogStateVars, public SipBaseProtected
|
|
{
|
|
friend class SipInterface;
|
|
protected:
|
|
mutable Mutex mDialogLock;
|
|
/**@name General SIP tags and ids. */
|
|
|
|
SipMessage* mInvite; ///< the INVITE or MESSAGE message for this transaction
|
|
public:
|
|
SipMessage *getInvite() const { return mInvite; }
|
|
|
|
/**@name SIP UDP parameters */
|
|
//SipState getSipState() const { return SipBaseProtected::getSipState(); } // stupid language.
|
|
|
|
string localSipUri() {
|
|
return format("sip:%s@%s", sipLocalUsername(), localIPAndPort());
|
|
}
|
|
// Normally the localUserName would just be sipLocalUserName from the SipDialog, but
|
|
// REGISTER is special because the on-going dialog for all REGISTER messages does not contain
|
|
// the user info for any particular REGISTER, so we need to pass the user name in for that.
|
|
string preferredIdentity(string localUsername) {
|
|
return format("<sip:%s@%s>",localUsername,localIP());
|
|
}
|
|
string localContact(string localUsername, int expires=3600) {
|
|
return format("<sip:%s@%s>;expires=%u",localUsername,localIPAndPort(),expires);
|
|
}
|
|
string proxyIP() const { return mProxy.mipIP; }
|
|
string transportName() { return string("UDP"); } // TODO
|
|
bool isReliableTransport() { return transportName() == "TCP"; } // TODO
|
|
|
|
//bool mInstigator; ///< true if this side initiated the call
|
|
DialogType mDialogType;
|
|
unsigned mDialogId; // A numeric id in case we need it. It may be used for the RTP call back.
|
|
|
|
/**@name Saved SIP messages. */
|
|
//@{
|
|
// The SDP we received from the peer, in the MTC invite or the MOC 200 OK. We need it for the IP and port number.
|
|
// Note that in the MTC case the peer info is the offer, while in the MOC case it is the final answer.
|
|
//string mSdpPeerInfo;
|
|
string mSdpOffer, mSdpAnswer;
|
|
string getSdpRemote() const { return mDialogType == SIPDTMTC ? mSdpOffer : mSdpAnswer; }
|
|
SipMessage *mLastResponse; // used only for the 200 OK to INVITE to retrive the sdp stuff.
|
|
// Return the response code, only for MO invites.
|
|
int getLastResponseCode() { return mLastResponse ? mLastResponse->smGetCode() : 0; }
|
|
int getLastResponseCode(string &reason) { // Return is the reason phrase from an error response.
|
|
if (mLastResponse) { reason = mLastResponse->smGetReason(); return mLastResponse->smGetCode(); }
|
|
else return 0;
|
|
}
|
|
string getLastResponseReasonHeader() { // Return is the reason phrase from an error response.
|
|
return mLastResponse ? mLastResponse->msmReasonHeader : string("");
|
|
}
|
|
//string getLastResponseReason() { return mLastResponse ? mLastResponse->smGetReason() : string(""); }
|
|
//@}
|
|
|
|
// These go in the Transaction:
|
|
std::string mInviteViaBranch; // (pat) This is part of the individual Transaction.
|
|
|
|
// Make an initial request: INVITE MESSAGE REGISTER
|
|
// Make a standard initial request with info from the DialogState.
|
|
SipMessage *makeRequest(string method,string requri, string whoami, SipPreposition* toContact, SipPreposition* fromContact, string branch);
|
|
SipMessage *makeInitialRequest(string method); // with default URIs
|
|
public:
|
|
Bool_z mIsHandover; // TRUE if this dialog was established by an inbound handover.
|
|
|
|
// Warning: sipWrite is only for non-invite transactions.
|
|
// Instead use moWriteLowSide or mtWriteLowSide for INVITE or MESSAGE transactions.
|
|
// However, sipWrite is called from L3MobilityManagement.cpp for the register message in the pre-transaction code
|
|
void sipWrite(SipMessage *sipmsg);
|
|
|
|
/**
|
|
Default constructor. Initialize the object.
|
|
// (pat) New way is to call SipDialogMT or SipDialogMO
|
|
@param proxy <host>:<port>
|
|
*/
|
|
void SipBaseInit();
|
|
void SipBaseInit(DialogType wDialogType, string proxy, const char *proxyProvenance); // , TranEntryId wTranId)
|
|
|
|
// This constructor is just to count them. Initialization is via SipBaseInit().
|
|
SipBase() { gCountSipDialogs++; }
|
|
/** Destroy held message copies. */
|
|
virtual ~SipBase();
|
|
|
|
bool dgIsInvite() const; // As opposed to a MESSAGE.
|
|
|
|
// Return if we are the server, ie, return true if communication was started by peer, not us.
|
|
DialogType vgetDialogType() const { return mDialogType; }
|
|
bool dgIsServer() const { return mDialogType == SIPDTMTC || mDialogType == SIPDTMTSMS; }
|
|
|
|
/** Save a copy of an INVITE or MESSAGE message in the engine. */
|
|
void saveInviteOrMessage(const SipMessage *INVITE, bool mine);
|
|
|
|
/** Save a copy of a response message to an MO request in the engine. */
|
|
void saveMOResponse(SipMessage *respsonse);
|
|
|
|
/** Determine if this invite matches the saved one */
|
|
bool sameInviteOrMessage(SipMessage * msg);
|
|
|
|
bool sameDialog(SipBase *other);
|
|
bool matchMessage(SipMessage *msg);
|
|
SipDialog *dgGetDialog();
|
|
|
|
public:
|
|
void sbText(std::ostringstream&os) const;
|
|
string sbText() const;
|
|
virtual int vGetRtpPort() const { return 0; }
|
|
Control::CodecSet vGetCodecs() const { return CodecSet(); }
|
|
|
|
string dsHandoverMessage(string peer) const;
|
|
|
|
// Add this to code where reason needs to be set
|
|
// Example: SipBase::addCallTerminationReasonDlg(CallTerminationCause(CallTerminationCause::eTermSIP/eQ850, 100, "This is an error"));
|
|
void addCallTerminationReasonDlg(CallTerminationCause::termGroup group, int cause, string desc) {
|
|
LOG(INFO) << "SIP term info addCallTerminationReasonDlg cause: " << cause;
|
|
SIPDlgCallTermList.add(group, cause, desc);
|
|
}
|
|
|
|
SipTermList& getTermList() { return SIPDlgCallTermList; }
|
|
private:
|
|
SipTermList SIPDlgCallTermList; // List of call termination reasons
|
|
};
|
|
|
|
struct SipCallbacks {
|
|
// ttAddMessage
|
|
typedef void (*ttAddMessage_functype)(TranEntryId tranid,DialogMessage *dmsg);
|
|
static ttAddMessage_functype ttAddMessage_callback;
|
|
static void ttAddMessage(TranEntryId tranid,SIP::DialogMessage *dmsg);
|
|
static void setcallback_ttAddMessage(ttAddMessage_functype callback) {
|
|
ttAddMessage_callback = callback;
|
|
}
|
|
|
|
// writePrivateHeaders
|
|
typedef void (*writePrivateHeaders_functype)(SipMessage *msg, const L3LogicalChannel *l3chan);
|
|
static writePrivateHeaders_functype writePrivateHeaders_callback;
|
|
static void writePrivateHeaders(SipMessage *msg, const L3LogicalChannel *l3chan);
|
|
static void setcallback_writePrivateHeaders(writePrivateHeaders_functype callback) {
|
|
writePrivateHeaders_callback = callback;
|
|
}
|
|
};
|
|
|
|
extern string OpenBTSUserAgent();
|
|
|
|
};
|
|
#endif
|