mirror of
				https://github.com/RangeNetworks/openbts.git
				synced 2025-11-03 21:33:15 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			200 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			200 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*
 | 
						|
* Copyright 2008 Free Software Foundation, Inc.
 | 
						|
* Copyright 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 SIP2INTERFACE_H
 | 
						|
#define SIP2INTERFACE_H
 | 
						|
 | 
						|
#include <Interthread.h>
 | 
						|
#include <Sockets.h>
 | 
						|
 | 
						|
#include <string>
 | 
						|
#include <GSML3CCElements.h>
 | 
						|
#include "SIPMessage.h"
 | 
						|
#include "SIPDialog.h"
 | 
						|
#include "SIPTransaction.h"
 | 
						|
 | 
						|
#define PAT_TEST_SIP_DIRECT 0
 | 
						|
 | 
						|
 | 
						|
 | 
						|
namespace SIP {
 | 
						|
using namespace std;
 | 
						|
 | 
						|
static const string cInviteStr("INVITE");
 | 
						|
 | 
						|
extern void SIPInterfaceStart();
 | 
						|
extern void printDialogs(std::ostream&os);
 | 
						|
 | 
						|
typedef std::map<std::string,SipTransaction*> TUMap_t;
 | 
						|
 | 
						|
// We need two separate maps: one for requests and one for replies.
 | 
						|
// For request matching we use SipDialogMap.  We do not need to recognize in-dialog request repeats because
 | 
						|
// we always just generate an immediate response for those anyway.  So it only needs to map INVITEs.
 | 
						|
// For reply matching we use SipTUMap, cf.
 | 
						|
class SipDialogMap {
 | 
						|
	//Mutex mDialogMapLock;
 | 
						|
	typedef InterthreadMap1<string,SipDialogRef> DialogMap_t;
 | 
						|
	DialogMap_t mDialogMap;
 | 
						|
	string makeTagKey(string callid, string localTag);
 | 
						|
	public:
 | 
						|
	SipDialogRef findDialogByMsg(SipMessage *msg);
 | 
						|
	void dmAddCallDialog(SipDialog*dialog);
 | 
						|
	void dmAddLocalTag(SipDialog*dialog);
 | 
						|
	void printDialogs(ostream&os);
 | 
						|
	void dmPeriodicService();
 | 
						|
	bool dmRemoveDialog(SipBase *dialog);
 | 
						|
	//SipBase *dmFindDialogById(unsigned id);
 | 
						|
	SipDialogRef dmFindDialogByRtp(RtpSession *session);
 | 
						|
};
 | 
						|
 | 
						|
// Match incoming replies to the TU that made the outgoing request.
 | 
						|
// The things saved here are SIP 'Transaction Users' as defined in RFC3261.
 | 
						|
// The TransactionUsers are the layer that sends/receives original messages and has some behavior related to our application.
 | 
						|
// RFC3261 17.1.3: When a response is received we are supposed find the client transaction using the branch in the top via.
 | 
						|
// Unfortunately, as of 6/2013 our SR is non-compliant.  It adds an extra via with a branch of "1".  Gotta love that.
 | 
						|
// An alternate way to find the transaction is by the CSeq.  So why is there a branch at all?  For reasons
 | 
						|
// that do not apply to us, namely 1.  The branch each proxy to have its own private ids
 | 
						|
// 2.  On the SIP Transaction Server side, the same request may arrive multiple times by multiple
 | 
						|
// paths through the intermediate SIP proxies, and those requests can be distinguished by branch, but in our case we
 | 
						|
// would want to view those as multiple identical requests and respond to all but the first with a repeated request error.
 | 
						|
class SipTUMap {
 | 
						|
	InterthreadMap<string,SipTransaction> mTUMap;
 | 
						|
	// (pat 7-23-2013) We are supposed to use the via-branch to identify the transaction, but unfortunately
 | 
						|
	// sipauthserve is non-compliant and does not return it.
 | 
						|
	string tuMakeKey(string callid, string method, int seqnum) {
 | 
						|
		if (method == "ACK") { method = cInviteStr; }	// Irrelevant, since we dont use TUs for ACK.
 | 
						|
		return format("%s %s %d",callid,method,seqnum);
 | 
						|
	}
 | 
						|
	string tuMakeKey(SipMessage *msg) { return tuMakeKey(msg->msmCallId, msg->msmCSeqMethod, msg->msmCSeqNum); }
 | 
						|
	string tuMakeKey(SipTransaction*tup) { return tuMakeKey(tup->mstCallId,tup->mstMethod,tup->mstSeqNum); }
 | 
						|
 | 
						|
	public:
 | 
						|
	void tuMapAdd(SipTransaction*tup);
 | 
						|
	void tuMapRemove(SipTransaction*tup, bool /*whine*/=true);
 | 
						|
	void tuMapPeriodicService();
 | 
						|
	// Attempt to dispatch an incoming SIP message to a TU; return true if a transaction wanted this message.
 | 
						|
	// This has to be locked so someone doesnt delete the TU between the time we get its pointer
 | 
						|
	// and send it the message.
 | 
						|
	bool tuMapDispatch(SipMessage*msg);
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
class SipInterface
 | 
						|
{
 | 
						|
	char mReadBuffer[MAX_UDP_LENGTH+500];		///< buffer for UDP reads.  The +500 is way overkill.
 | 
						|
 | 
						|
	UDPSocket *mSIPSocket;
 | 
						|
	Mutex mSocketLock;
 | 
						|
 | 
						|
	// SIP Message CSeq numbers for initial out-of-dialog transcations, which includes the initial INVITE:
 | 
						|
	// We want these to be unique, and the easiest way is to increment a counter.
 | 
						|
	unsigned mMessageCSeqNum;
 | 
						|
	unsigned mInfoCSeqNum;	// This is for INFO messages outside a dialog.  Inside a dialog they must use dsNextCSeq()
 | 
						|
	unsigned mInviteCSeqNum;	// For the initial invite, then in-invite numbers advance from there.
 | 
						|
 | 
						|
	public:
 | 
						|
	SipInterface() {
 | 
						|
		// sipauthserve appears to have a bug that it does not properly differentiate messages
 | 
						|
		// from different BTS, possibly only if they have the same IP address, but in any case,
 | 
						|
		// using random numbers to init makes encountering that bug quite rare.
 | 
						|
		mMessageCSeqNum = random() & 0xfffff;
 | 
						|
		mInfoCSeqNum = random() & 0xfffff;	// Any old number will do.
 | 
						|
		mInviteCSeqNum = random() & 0xfffff;	// Any old number will do.
 | 
						|
	}
 | 
						|
 | 
						|
	void siWrite(const struct sockaddr_in*, SipMessage *);	// new
 | 
						|
	/** Start the SIP drive loop. */
 | 
						|
	void siDrive2();
 | 
						|
	void siInit();
 | 
						|
	virtual void newDriveIncoming(char *content) = 0;
 | 
						|
 | 
						|
	unsigned nextMessageCSeqNum() { return ++mMessageCSeqNum; }
 | 
						|
	unsigned nextInfoCSeqNum() { return ++mInfoCSeqNum; }
 | 
						|
	unsigned nextInviteCSeqNum() { return ++mInviteCSeqNum; }
 | 
						|
};
 | 
						|
 | 
						|
class MySipInterface  : public SipDialogMap, public SipTUMap, public SipInterface
 | 
						|
{
 | 
						|
 | 
						|
public:
 | 
						|
	Thread mDriveThread;	
 | 
						|
	Thread mPeriodicServiceThread;	
 | 
						|
 | 
						|
	// (pat) Formerly all downlink SIP messages were added to the FIFO from which they were read via polling.
 | 
						|
	// Now messages are delivered to the SipDialog state machine, which sends SIP responses immediately, and
 | 
						|
	// queues any L3 bound messages as DialogMessages into a queue destined for L3 processing,
 | 
						|
	// whence the messages will be sent to the TransactionEntries.
 | 
						|
	// How many SIPEngine/SipDialog objects are there?
 | 
						|
	// Formerly (GSM only, prior to UMTS and L3 rewrite) there could be only one SIPEngine per LogicalChannel;
 | 
						|
	// each TCH might have a SIPEngine in the TransactionEntry used for voice traffic, and each SDDCH/FACCH might have a SIPEngine
 | 
						|
	// used for registration.  Each LogicalChannel was driven by a separate thread to make the polling scheme work.
 | 
						|
	// Now for GSM there can be at least two TransactionEntries per MS or UE (for call-hold/waiting),
 | 
						|
	// and in UMTS the maximum number of UEs is dependent on the spreading factor used for voice calls, maybe 128.
 | 
						|
	// Update: There is really no limit on the number of simultaneous SIPEngines and TransactionEntries, because
 | 
						|
	// we start a new one for each inbound SMS or voice call; if there are too many they will get destroyed
 | 
						|
	// in the upcoming connection-management layer.
 | 
						|
 | 
						|
	// DialogMap maps the SIP callid to the SipDialog state machine that runs the state machine.
 | 
						|
	// The SIPEngine class doesnt contain a state machine, but SipDialog does.
 | 
						|
	typedef ThreadSafeList<SipDialogRef> DeadDialogListType;
 | 
						|
	DeadDialogListType mDeadDialogs;
 | 
						|
 | 
						|
	/**
 | 
						|
		Create the SIP interface to watch for incoming SIP messages.
 | 
						|
	*/
 | 
						|
	MySipInterface()
 | 
						|
		// (pat) Dont do this! There is a constructor race between SIPInterface and ConfigurationTable needed by gConfig.
 | 
						|
		// :mSIPSocket(gConfig.getNum("SIP.Local.Port"))
 | 
						|
	{
 | 
						|
	}
 | 
						|
 | 
						|
	
 | 
						|
	void msiInit();
 | 
						|
 | 
						|
	/** Receive, parse and dispatch a single SIP message. */
 | 
						|
	void newDriveIncoming(char *content);
 | 
						|
	void purgeDeadDialogs();
 | 
						|
 | 
						|
	/**
 | 
						|
		Look for incoming INVITE messages to start MTC.
 | 
						|
		@param msg The SIP message to check.
 | 
						|
		@return true if the message is a new INVITE
 | 
						|
	*/
 | 
						|
	void handleInvite(SipMessage *msg,bool isINVITE);
 | 
						|
	bool newCheckInvite(SipMessage *msg);
 | 
						|
	bool checkTU(SipMessage *msg);
 | 
						|
 | 
						|
	/**
 | 
						|
		Send an error response before a transaction is even created.
 | 
						|
	*/
 | 
						|
	void newSendEarlyError(SipMessage *cause, int code, const char * reason);
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
/*@addtogroup Globals */
 | 
						|
//@{
 | 
						|
/** A single global SIPInterface in the global namespace. */
 | 
						|
extern MySipInterface gSipInterface;
 | 
						|
//@}
 | 
						|
 | 
						|
}; // namespace SIP.
 | 
						|
 | 
						|
#endif // SIP2INTERFACE_H
 | 
						|
// vim: ts=4 sw=4
 |