From 56d47dfffc4025de4c8de6e2e22ae896d2eb46f8 Mon Sep 17 00:00:00 2001 From: paulc Date: Fri, 28 Oct 2005 03:10:32 +0000 Subject: [PATCH] Added auto authorization of transactions and some client NAT support. git-svn-id: http://voip.null.ro/svn/yate@551 acf43c95-373e-0410-b603-e72c3f656dc1 --- contrib/ysip/engine.cpp | 4 +- contrib/ysip/message.cpp | 12 +- contrib/ysip/transaction.cpp | 51 ++++- contrib/ysip/yatesip.h | 45 ++++ modules/ysipchan.cpp | 391 ++++++++++++++++++++++------------- 5 files changed, 355 insertions(+), 148 deletions(-) diff --git a/contrib/ysip/engine.cpp b/contrib/ysip/engine.cpp index 2c70bd58..42e1d6d6 100644 --- a/contrib/ysip/engine.cpp +++ b/contrib/ysip/engine.cpp @@ -269,7 +269,7 @@ SIPTransaction* SIPEngine::addMessage(SIPMessage* message) return 0; } if (message->isACK()) { - Debug("SIPEngine",DebugAll,"Message %p was an unhandled ACK [%p]",message,this); + DDebug("SIPEngine",DebugAll,"Message %p was an unhandled ACK [%p]",message,this); return 0; } message->complete(this); @@ -499,7 +499,7 @@ int SIPEngine::authUser(const SIPMessage* message, String& user, bool proxy) long age = nonceAge(nonce); if (age < 0) continue; - XDebug("SIPEngine",DebugAll,"authUser nonce age is %d",age); + XDebug("SIPEngine",DebugAll,"authUser nonce age is %ld",age); String res(t->getParam("response")); delQuotes(res); if (res.null()) diff --git a/contrib/ysip/message.cpp b/contrib/ysip/message.cpp index d4d4b227..477a9512 100644 --- a/contrib/ysip/message.cpp +++ b/contrib/ysip/message.cpp @@ -253,9 +253,7 @@ SIPMessage::SIPMessage(const SIPMessage& original) if (hl->name() &= "CSeq") continue; SIPHeaderLine* nl = hl->clone(); - // this is a new transaction/dialog so let complete() add randomness - if ((nl->name() &= "From") || (nl->name() &= "To")) - nl->delParam("tag"); + // this is a new transaction so let complete() add randomness if (via1 && (nl->name() &= "Via")) { via1 = false; nl->delParam("branch"); @@ -802,6 +800,14 @@ SIPAuthLine* SIPMessage::buildAuth(const String& username, const String& passwor return 0; } +SIPAuthLine* SIPMessage::buildAuth(const SIPMessage& original) const +{ + if (original.getAuthUsername().null()) + return 0; + return buildAuth(original.getAuthUsername(),original.getAuthPassword(), + original.method,original.uri,(code == 407)); +} + ObjList* SIPMessage::getRoutes() const { ObjList* list = 0; diff --git a/contrib/ysip/transaction.cpp b/contrib/ysip/transaction.cpp index 68d30340..d4e29957 100644 --- a/contrib/ysip/transaction.cpp +++ b/contrib/ysip/transaction.cpp @@ -65,8 +65,40 @@ SIPTransaction::SIPTransaction(SIPMessage* message, SIPEngine* engine, bool outg } } m_invite = (getMethod() == "INVITE"); - m_engine->TransList.append(this); m_state = Initial; + m_engine->TransList.append(this); +} + +SIPTransaction::SIPTransaction(SIPTransaction& original, SIPMessage* answer) + : m_outgoing(true), m_invite(original.m_invite), m_transmit(false), + m_state(Process), m_response(original.m_response), m_timeout(0), + m_firstMessage(original.m_firstMessage), m_lastMessage(original.m_lastMessage), + m_pending(0), m_engine(original.m_engine), + m_branch(original.m_branch), m_callid(original.m_callid), m_tag(original.m_tag), + m_private(0) +{ + DDebug(DebugAll,"SIPTransaction::SIPTransaction(&%p,%p) [%p]", + &original,answer,this); + + SIPMessage* msg = new SIPMessage(*original.m_firstMessage); + SIPAuthLine* auth = answer->buildAuth(*original.m_firstMessage); + m_firstMessage->setAutoAuth(); + msg->complete(m_engine); + msg->addHeader(auth); + const NamedString* ns = msg->getParam("Via","branch"); + if (ns) + original.m_branch = *ns; + else + original.m_branch.clear(); + ns = msg->getParam("To","tag"); + if (ns) + original.m_tag = *ns; + else + original.m_tag.clear(); + original.m_firstMessage = msg; + original.m_lastMessage = 0; + + m_engine->TransList.append(this); } SIPTransaction::~SIPTransaction() @@ -150,7 +182,7 @@ void SIPTransaction::setLatestMessage(SIPMessage* message) m_lastMessage->ref(); if (message->isAnswer()) { m_response = message->code; - if (m_response > 100) + if ((m_response > 100) && (m_response < 300)) setDialogTag(); } message->complete(m_engine,0,0,m_tag); @@ -415,6 +447,8 @@ void SIPTransaction::processClientMessage(SIPMessage* message, int state) case Process: if (message->code <= 100) break; + if (tryAutoAuth(message)) + break; if (m_invite && (m_response <= 100)) // use the human interaction timeout in INVITEs setTimeout(m_engine->getUserTimeout()); @@ -536,4 +570,17 @@ SIPEvent* SIPTransaction::getServerEvent(int state, int timeout) return e; } +bool SIPTransaction::tryAutoAuth(SIPMessage* answer) +{ + if ((answer->code != 401) && (answer->code != 407)) + return false; + if (m_firstMessage->getAuthUsername().null()) + return false; + setTimeout(); + SIPTransaction* tr = new SIPTransaction(*this,answer); + changeState(Initial); + tr->processClientMessage(answer,Process); + return true; +} + /* vi: set ts=8 sw=4 sts=4 noet: */ diff --git a/contrib/ysip/yatesip.h b/contrib/ysip/yatesip.h index 34cfcea1..80474304 100644 --- a/contrib/ysip/yatesip.h +++ b/contrib/ysip/yatesip.h @@ -409,6 +409,35 @@ public: SIPAuthLine* buildAuth(const String& username, const String& password, const String& meth, const String& uri, bool proxy = false) const; + /** + * Construct a new authorization line based on this answer and original message + * @param original Origianl outgoing message + * @return A new authorization line to be used in a new transaction + */ + SIPAuthLine* buildAuth(const SIPMessage& original) const; + + /** + * Prepare the message for automatic client transaction authentication. + * @param username Username for auto authentication + * @param password Password for auto authentication + */ + inline void setAutoAuth(const char* username = 0, const char* password = 0) + { m_authUser = username; m_authPass = password; } + + /** + * Retrive the username to be used for auto authentication + * @return Username for auto authentication + */ + inline const String& getAuthUsername() const + { return m_authUser; } + + /** + * Retrive the password to be used for auto authentication + * @return Password for auto authentication + */ + inline const String& getAuthPassword() const + { return m_authPass; } + /** * Extract routes from Record-Route: headers * @return A list of SIPHeaderLine representing SIP routes @@ -483,6 +512,8 @@ protected: int m_cseq; mutable String m_string; mutable DataBlock m_data; + String m_authUser; + String m_authPass; private: SIPMessage(); // no, thanks }; @@ -549,6 +580,7 @@ public: */ Cleared, }; + /** * Constructor from first message * @param message A pointer to the initial message, should not be used @@ -749,6 +781,19 @@ public: { return m_private; } protected: + /** + * Constructor from previous auto authenticated transaction. This is used only internally + * @param original Original transaction that failed authentication + */ + SIPTransaction(SIPTransaction& original, SIPMessage* answer); + + /** + * Attempt to perform automatic client transaction authentication + * @param answer SIP answer that creates the new transaction + * @return True if current client processing must be abandoned + */ + bool tryAutoAuth(SIPMessage* answer); + /** * Get an event only for client transactions * @param state The current state of the transaction diff --git a/modules/ysipchan.cpp b/modules/ysipchan.cpp index f615b635..cd2235e5 100644 --- a/modules/ysipchan.cpp +++ b/modules/ysipchan.cpp @@ -128,7 +128,7 @@ private: class YateUDPParty : public SIPParty { public: - YateUDPParty(Socket* sock, const SocketAddr& addr, int local); + YateUDPParty(Socket* sock, const SocketAddr& addr, int localPort, const char* localAddr = 0); ~YateUDPParty(); virtual void transmit(SIPEvent* event); virtual const char* getProtoName() const; @@ -154,46 +154,31 @@ private: bool m_prack; }; -class YateSIPEndPoint : public Thread -{ -public: - YateSIPEndPoint(); - ~YateSIPEndPoint(); - bool Init(void); - void run(void); - bool incoming(SIPEvent* e, SIPTransaction* t); - void invite(SIPEvent* e, SIPTransaction* t); - void regreq(SIPEvent* e, SIPTransaction* t); - bool generic(SIPEvent* e, SIPTransaction* t); - bool buildParty(SIPMessage* message, const char* host = 0, int port = 0); - inline YateSIPEngine* engine() const - { return m_engine; } - inline int port() const - { return m_port; } - inline Socket* socket() const - { return m_sock; } -private: - int m_port; - Socket* m_sock; - SocketAddr m_addr; - YateSIPEngine *m_engine; - -}; - class YateSIPLine : public String { YCLASS(YateSIPLine,String) public: YateSIPLine(const String& name); virtual ~YateSIPLine(); - SIPAuthLine* buildAuth(const SIPMessage* answer, const String& method, - const String& uri, bool proxy = false) const; - SIPMessage* buildRegister(int expires, const SIPMessage* msg) const; - void login(const SIPMessage* msg = 0); + void setupAuth(SIPMessage* msg) const; + SIPMessage* buildRegister(int expires) const; + void login(); void logout(); bool process(SIPEvent* ev); void timer(const Time& when); bool update(const Message& msg); + inline const String& getLocalAddr() const + { return m_localAddr; } + inline const String& getPartyAddr() const + { return m_partyAddr; } + inline int getLocalPort() const + { return m_localPort; } + inline int getPartyPort() const + { return m_partyPort; } + inline const String& getUserName() const + { return m_username; } + inline const String& getAuthName() const + { return m_authname ? m_authname : m_username; } inline const String& domain() const { return m_domain ? m_domain : m_registrar; } inline bool valid() const @@ -205,18 +190,50 @@ public: private: void clearTransaction(); bool change(String& dest, const String& src); + bool change(int& dest, int src); String m_registrar; String m_username; + String m_authname; String m_password; String m_outbound; String m_domain; String m_display; Time m_resend; int m_interval; - bool m_retry; SIPTransaction* m_tr; bool m_marked; bool m_valid; + String m_localAddr; + String m_partyAddr; + int m_localPort; + int m_partyPort; + bool m_localDetect; +}; + +class YateSIPEndPoint : public Thread +{ +public: + YateSIPEndPoint(); + ~YateSIPEndPoint(); + bool Init(void); + void run(void); + bool incoming(SIPEvent* e, SIPTransaction* t); + void invite(SIPEvent* e, SIPTransaction* t); + void regreq(SIPEvent* e, SIPTransaction* t); + bool generic(SIPEvent* e, SIPTransaction* t); + bool buildParty(SIPMessage* message, const char* host = 0, int port = 0, const YateSIPLine* line = 0); + inline YateSIPEngine* engine() const + { return m_engine; } + inline int port() const + { return m_port; } + inline Socket* socket() const + { return m_sock; } +private: + void addMessage(const char* buf, int len, const SocketAddr& addr, int port); + int m_port; + Socket* m_sock; + SocketAddr m_addr; + YateSIPEngine *m_engine; }; class YateSIPConnection : public Channel @@ -270,6 +287,8 @@ public: { return m_host; } inline int getPort() const { return m_port; } + inline const String& getRtpAddr() const + { return m_externalAddr ? m_externalAddr : m_rtpLocalAddr; } private: void setMedia(ObjList* media); void clearTransaction(); @@ -287,7 +306,6 @@ private: SIPTransaction* m_tr; bool m_hungup; bool m_byebye; - bool m_retry; int m_state; String m_reason; int m_reasonCode; @@ -295,6 +313,8 @@ private: // SIP dialog of this call, used for re-INVITE or BYE SIPDialog m_dialog; URI m_uri; + // our external IP address, possibly outside of a NAT + String m_externalAddr; // if we do RTP forwarding or not bool m_rtpForward; // remote RTP address @@ -365,6 +385,7 @@ public: YateSIPConnection* findCall(const String& callid); YateSIPConnection* findDialog(const SIPDialog& dialog); YateSIPLine* findLine(const String& line); + YateSIPLine* findLine(const String& addr, int port, const String& user = String::empty()); bool validLine(const String& line); private: YateSIPEndPoint *m_endpoint; @@ -375,6 +396,7 @@ static ObjList s_lines; static Configuration s_cfg; static int s_maxForwards = 20; static bool s_privacy = false; +static bool s_auto_nat = true; // Parse a SDP and return a possibly filtered list of SDP media static ObjList* parseSDP(const SDPBody* sdp, String& addr, ObjList* oldMedia = 0, const char* media = 0) @@ -453,7 +475,7 @@ static ObjList* parseSDP(const SDPBody* sdp, String& addr, ObjList* oldMedia = 0 payload = "ilbc30"; } - XDebug(&plugin,DebugAll,"Payload %d format '%s'",var,payload); + XDebug(&plugin,DebugAll,"Payload %d format '%s'",var,payload.c_str()); if (payload && s_cfg.getBoolValue("codecs",payload,defcodecs && DataTranslator::canConvert(payload))) { if (fmt) fmt << ","; @@ -682,16 +704,19 @@ void RtpMedia::update(const Message& msg, bool pickFormat) m_format = msg.getValue("format"); } -YateUDPParty::YateUDPParty(Socket* sock, const SocketAddr& addr, int local) +YateUDPParty::YateUDPParty(Socket* sock, const SocketAddr& addr, int localPort, const char* localAddr) : m_sock(sock), m_addr(addr) { - m_local = "localhost"; - m_localPort = local; + DDebug(&plugin,DebugAll,"YateUDPParty::YateUDPParty() %s:%d [%p]",localAddr,localPort,this); + m_localPort = localPort; m_party = m_addr.host(); m_partyPort = m_addr.port(); - Socket s(PF_INET,SOCK_DGRAM,IPPROTO_UDP); - if (s.valid()) { - if (s.connect(m_addr)) { + if (localAddr) + m_local = localAddr; + else { + m_local = "localhost"; + Socket s(PF_INET,SOCK_DGRAM,IPPROTO_UDP); + if (s.valid() && s.connect(m_addr)) { SocketAddr laddr; if (s.getSockName(laddr)) m_local = laddr.host(); @@ -704,6 +729,7 @@ YateUDPParty::YateUDPParty(Socket* sock, const SocketAddr& addr, int local) YateUDPParty::~YateUDPParty() { + DDebug(&plugin,DebugAll,"YateUDPParty::~YateUDPParty() [%p]",this); m_sock = 0; } @@ -736,7 +762,7 @@ const char* YateUDPParty::getProtoName() const bool YateUDPParty::setParty(const URI& uri) { - if (m_partyPort && m_party && s_cfg.getBoolValue("general","ignorevia")) + if (m_partyPort && m_party && s_cfg.getBoolValue("general","ignorevia",true)) return true; if (uri.getHost().null()) return false; @@ -845,11 +871,20 @@ YateSIPEndPoint::~YateSIPEndPoint() } } -bool YateSIPEndPoint::buildParty(SIPMessage* message, const char* host, int port) +bool YateSIPEndPoint::buildParty(SIPMessage* message, const char* host, int port, const YateSIPLine* line) { if (message->isAnswer()) return false; + DDebug(&plugin,DebugAll,"YateSIPEndPoint::buildParty(%p,'%s',%d,%p)", + message,host,port,line); URI uri(message->uri); + if (line) { + if (!host) + host = line->getPartyAddr(); + if (port <= 0) + port = line->getPartyPort(); + line->setupAuth(message); + } if (!host) { host = uri.getHost().safe(); if (port <= 0) @@ -865,7 +900,12 @@ bool YateSIPEndPoint::buildParty(SIPMessage* message, const char* host, int port addr.port(port); DDebug(&plugin,DebugAll,"built addr: %s:%d", addr.host().c_str(),addr.port()); - YateUDPParty* party = new YateUDPParty(m_sock,addr,m_port); + // reuse the variables now we finished with them + host = line ? line->getLocalAddr().c_str() : 0; + port = line ? line->getLocalPort() : 0; + if (port <= 0) + port = m_port; + YateUDPParty* party = new YateUDPParty(m_sock,addr,port,host); message->setParty(party); party->deref(); return true; @@ -911,6 +951,28 @@ bool YateSIPEndPoint::Init() return true; } +void YateSIPEndPoint::addMessage(const char* buf, int len, const SocketAddr& addr, int port) +{ + SIPMessage* msg = SIPMessage::fromParsing(0,buf,len); + if (!msg) + return; + + if (!msg->isAnswer()) { + URI uri(msg->uri); + YateSIPLine* line = plugin.findLine(addr.host(),addr.port(),uri.getUser()); + const char* host = 0; + if (line && line->getLocalPort()) { + host = line->getLocalAddr(); + port = line->getLocalPort(); + } + YateUDPParty* party = new YateUDPParty(m_sock,addr,port,host); + msg->setParty(party); + party->deref(); + } + m_engine->addMessage(msg); + msg->deref(); +} + void YateSIPEndPoint::run() { struct timeval tv; @@ -936,7 +998,8 @@ void YateSIPEndPoint::run() Debug(&plugin,DebugInfo,"Received %d bytes SIP message from %s:%d\n------\n%s------", res,m_addr.host().c_str(),m_addr.port(),buf); // we got already the buffer and here we start to do "good" stuff - m_engine->addMessage(new YateUDPParty(m_sock,m_addr,m_port),buf,res); + addMessage(buf,res,m_addr,m_port); + //m_engine->addMessage(new YateUDPParty(m_sock,m_addr,m_port),buf,res); } #ifdef DEBUG else @@ -1063,7 +1126,7 @@ void YateSIPEndPoint::regreq(SIPEvent* e, SIPTransaction* t) int age = t->authUser(user); DDebug(&plugin,DebugAll,"User '%s' age %d",user.c_str(),age); if ((age < 0) || (age > 10)) { - t->requestAuth("realm","",age > 0); + t->requestAuth(s_cfg.getValue("general","realm","Yate"),"",age >= 0); return; } @@ -1101,7 +1164,7 @@ bool YateSIPEndPoint::generic(SIPEvent* e, SIPTransaction* t) int age = t->authUser(user); DDebug(&plugin,DebugAll,"User '%s' age %d",user.c_str(),age); if ((age < 0) || (age > 10)) { - t->requestAuth("realm","",age > 0); + t->requestAuth("realm","",age >= 0); return true; } } @@ -1136,7 +1199,7 @@ bool YateSIPEndPoint::generic(SIPEvent* e, SIPTransaction* t) // Incoming call constructor - just before starting the routing thread YateSIPConnection::YateSIPConnection(SIPEvent* ev, SIPTransaction* tr) : Channel(plugin,0,false), - m_tr(tr), m_hungup(false), m_byebye(true), m_retry(false), + m_tr(tr), m_hungup(false), m_byebye(true), m_state(Incoming), m_rtpForward(false), m_rtpMedia(0), m_sdpSession(0), m_sdpVersion(0), m_port(0), m_route(0), m_routes(0), m_authBye(true), m_mediaStatus(MediaMissing) @@ -1155,19 +1218,30 @@ YateSIPConnection::YateSIPConnection(SIPEvent* ev, SIPTransaction* tr) m_tr->setUserData(this); URI uri(m_tr->getURI()); + YateSIPLine* line = plugin.findLine(m_host,m_port,m_uri.getUser()); Message *m = message("call.route"); - String user; - int age = tr->authUser(user); - DDebug(this,DebugAll,"User '%s' age %d",user.c_str(),age); - if (age >= 0) { - if (age < 10) { - m_user = user; - m->addParam("username",m_user); + if (line) { + // call comes from line we have registered to - trust it... + m_user = line->getUserName(); + m_externalAddr = line->getLocalAddr(); + m_line = *line; + m->addParam("username",m_user); + m->addParam("in_line",m_line); + } + else { + String user; + int age = tr->authUser(user); + DDebug(this,DebugAll,"User '%s' age %d",user.c_str(),age); + if (age >= 0) { + if (age < 10) { + m_user = user; + m->addParam("username",m_user); + } + else + m->addParam("expired_user",user); + m->addParam("xsip_nonce_age",String(age)); } - else - m->addParam("expired_user",user); - m->addParam("xsip_nonce_age",String(age)); } if (s_privacy) copyPrivacy(*m,*ev->getMessage()); @@ -1192,7 +1266,7 @@ YateSIPConnection::YateSIPConnection(SIPEvent* ev, SIPTransaction* tr) if (m_rtpMedia) { m_rtpForward = true; // guess if the call comes from behind a NAT - if (s_cfg.getBoolValue("general","nat",true) && isPrivateAddr(m_rtpAddr) && !isPrivateAddr(m_host)) { + if (s_auto_nat && isPrivateAddr(m_rtpAddr) && !isPrivateAddr(m_host)) { Debug(this,DebugInfo,"NAT detected: private '%s' public '%s'", m_rtpAddr.c_str(),m_host.c_str()); m->addParam("rtp_nat_addr",m_rtpAddr); @@ -1217,7 +1291,7 @@ YateSIPConnection::YateSIPConnection(SIPEvent* ev, SIPTransaction* tr) // Outgoing call constructor - in call.execute handler YateSIPConnection::YateSIPConnection(Message& msg, const String& uri, const char* target) : Channel(plugin,0,true), - m_tr(0), m_hungup(false), m_byebye(true), m_retry(true), + m_tr(0), m_hungup(false), m_byebye(true), m_state(Outgoing), m_rtpForward(false), m_rtpMedia(0), m_sdpSession(0), m_sdpVersion(0), m_port(0), m_route(0), m_routes(0), m_authBye(false), m_mediaStatus(MediaMissing) @@ -1229,20 +1303,23 @@ YateSIPConnection::YateSIPConnection(Message& msg, const String& uri, const char m_rtpForward = msg.getBoolValue("rtp_forward"); m_line = msg.getValue("line"); String tmp; - if (m_line && (uri.find('@') < 0)) { - YateSIPLine* line = plugin.findLine(m_line); - if (line) { + YateSIPLine* line = 0; + if (m_line) { + line = plugin.findLine(m_line); + if (line && (uri.find('@') < 0)) { if (!uri.startsWith("sip:")) tmp = "sip:"; tmp << uri << "@" << line->domain(); } + if (line) + m_externalAddr = line->getLocalAddr(); } if (tmp.null()) tmp = uri; m_uri = tmp; m_uri.parse(); SIPMessage* m = new SIPMessage("INVITE",m_uri); - plugin.ep()->buildParty(m,msg.getValue("host"),msg.getIntValue("port")); + plugin.ep()->buildParty(m,msg.getValue("host"),msg.getIntValue("port"),line); if (!m->getParty()) { Debug(this,DebugWarn,"Could not create party for '%s' [%p]",m_uri.c_str(),this); m->destruct(); @@ -1254,7 +1331,9 @@ YateSIPConnection::YateSIPConnection(Message& msg, const String& uri, const char } int maxf = msg.getIntValue("antiloop",s_maxForwards); m->addHeader("Max-Forwards",String(maxf)); - m->complete(plugin.ep()->engine(),msg.getValue("caller"),msg.getValue("domain")); + m->complete(plugin.ep()->engine(), + msg.getValue("caller"), + msg.getValue("domain",(line ? line->domain().c_str() : 0))); if (plugin.ep()->engine()->prack()) m->addHeader("Supported","100rel"); m_host = m->getParty()->getPartyAddr(); @@ -1363,7 +1442,7 @@ void YateSIPConnection::hangup() case Ringing: if (m_tr) { SIPMessage* m = new SIPMessage("CANCEL",m_uri); - plugin.ep()->buildParty(m,m_host,m_port); + plugin.ep()->buildParty(m,m_host,m_port,plugin.findLine(m_line)); if (!m->getParty()) Debug(this,DebugWarn,"Could not create party for '%s:%d' [%p]", m_host.c_str(),m_port,this); @@ -1411,7 +1490,7 @@ SIPMessage* YateSIPConnection::createDlgMsg(const char* method, const char* uri) uri = m_uri; SIPMessage* m = new SIPMessage(method,uri); m->addRoutes(m_routes); - plugin.ep()->buildParty(m,m_host,m_port); + plugin.ep()->buildParty(m,m_host,m_port,plugin.findLine(m_line)); if (!m->getParty()) { Debug(this,DebugWarn,"Could not create party for '%s:%d' [%p]", m_host.c_str(),m_port,this); @@ -1639,7 +1718,7 @@ SDPBody* YateSIPConnection::createRtpSDP(const char* addr, const Message& msg) if (!dispatchRtp(m,addr,false,true)) return 0; } - return createSDP(m_rtpLocalAddr); + return createSDP(getRtpAddr()); } // Creates a set of started external RTP channels from remote addr and builds SDP from them @@ -1656,7 +1735,7 @@ SDPBody* YateSIPConnection::createRtpSDP(bool start) if (!dispatchRtp(m,m_rtpAddr,start,true)) return 0; } - return createSDP(m_rtpLocalAddr); + return createSDP(getRtpAddr()); } // Starts an already created set of external RTP channels @@ -1799,33 +1878,6 @@ bool YateSIPConnection::process(SIPEvent* ev) const SIPMessage* msg = ev->getMessage(); int code = ev->getTransaction()->getResponseCode(); if (msg && !msg->isOutgoing() && msg->isAnswer() && (code >= 300)) { - if (m_retry && m_line - && ((code == 401) || (code == 407)) - && plugin.validLine(m_line)) { - // try only once to add credentials - m_retry = false; - YateSIPLine* line = plugin.findLine(m_line); - if (line) { - SIPMessage* m = new SIPMessage(*m_tr->initialMessage()); - SIPAuthLine* auth = line->buildAuth(msg,m->method,m->uri,(code == 407)); - m->addHeader(auth); - - m_tr->setUserData(0); - m_tr->deref(); - m_tr = 0; - - m_tr = plugin.ep()->engine()->addMessage(m); - m->deref(); - if (m_tr) { - m_tr->ref(); - m_callid = m_tr->getCallID(); - m_tr->setUserData(this); - } - else - setReason("Internal server failure",500); - return false; - } - } setReason(msg->reason,code); hangup(); } @@ -1848,7 +1900,7 @@ bool YateSIPConnection::process(SIPEvent* ev) DDebug(this,DebugInfo,"YateSIPConnection got SDP [%p]",this); setMedia(parseSDP(static_cast(msg->body),m_rtpAddr,m_rtpMedia)); // guess if the call comes from behind a NAT - if (s_cfg.getBoolValue("general","nat",true) && isPrivateAddr(m_rtpAddr) && !isPrivateAddr(m_host)) { + if (s_auto_nat && isPrivateAddr(m_rtpAddr) && !isPrivateAddr(m_host)) { Debug(this,DebugInfo,"NAT detected: private '%s' public '%s'", m_rtpAddr.c_str(),m_host.c_str()); natAddr = m_rtpAddr; @@ -1908,7 +1960,7 @@ void YateSIPConnection::reInvite(SIPTransaction* t) if (!lst) break; // guess if the call comes from behind a NAT - if (s_cfg.getBoolValue("general","nat",true) && isPrivateAddr(addr) && !isPrivateAddr(m_host)) { + if (s_auto_nat && isPrivateAddr(addr) && !isPrivateAddr(m_host)) { Debug(this,DebugInfo,"NAT detected: private '%s' public '%s'", addr.c_str(),m_host.c_str()); addr = m_host; @@ -1938,7 +1990,7 @@ bool YateSIPConnection::checkUser(SIPTransaction* t, bool refuse) if (m_user.null()) return true; int age = t->authUser(m_user); - if ((age > 0) && (age <= 10)) + if ((age >= 0) && (age <= 10)) return true; DDebug(this,DebugAll,"YateSIPConnection::checkUser(%p) failed, age %d [%p]",t,age,this); if (refuse) @@ -2116,7 +2168,8 @@ void YateSIPConnection::callRejected(const char* error, const char* reason, cons YateSIPLine::YateSIPLine(const String& name) : String(name), m_resend((u_int64_t)0), m_interval(0), - m_retry(false), m_tr(0), m_marked(false), m_valid(false) + m_tr(0), m_marked(false), m_valid(false), + m_localPort(0), m_partyPort(0), m_localDetect(false) { DDebug(&plugin,DebugInfo,"YateSIPLine::YateSIPLine('%s') [%p]",c_str(),this); s_lines.append(this); @@ -2129,22 +2182,19 @@ YateSIPLine::~YateSIPLine() logout(); } -SIPAuthLine* YateSIPLine::buildAuth(const SIPMessage* answer, const String& method, - const String& uri, bool proxy) const +void YateSIPLine::setupAuth(SIPMessage* msg) const { - return answer ? answer->buildAuth(m_username,m_password,method,uri,proxy) : 0; + if (msg) + msg->setAutoAuth(getAuthName(),m_password); } -SIPMessage* YateSIPLine::buildRegister(int expires, const SIPMessage* msg) const +SIPMessage* YateSIPLine::buildRegister(int expires) const { String exp(expires); String tmp; tmp << "sip:" << m_registrar; SIPMessage* m = new SIPMessage("REGISTER",tmp); - if (msg) - m->setParty(msg->getParty()); - else - plugin.ep()->buildParty(m); + plugin.ep()->buildParty(m,0,0,this); if (!m->getParty()) { Debug(&plugin,DebugWarn,"Could not create party for '%s' [%p]", m_registrar.c_str(),this); @@ -2166,30 +2216,23 @@ SIPMessage* YateSIPLine::buildRegister(int expires, const SIPMessage* msg) const return m; } -void YateSIPLine::login(const SIPMessage* msg) +void YateSIPLine::login() { if (m_registrar.null() || m_username.null()) { logout(); - m_retry = false; m_valid = true; return; } + DDebug(&plugin,DebugInfo,"YateSIPLine '%s' logging in [%p]",c_str(),this); clearTransaction(); - m_retry = true; - SIPMessage* m = buildRegister(m_interval,msg); + SIPMessage* m = buildRegister(m_interval); if (!m) { - m_retry = false; m_valid = false; return; } - if (msg) { - SIPAuthLine* auth = buildAuth(msg,m->method,m->uri,(msg->code == 407)); - m->addHeader(auth); - m_retry = false; - } - DDebug(&plugin,DebugInfo,"YateSIPLine '%s' emiting %p for answer %p [%p]", - c_str(),m,msg,this); + DDebug(&plugin,DebugInfo,"YateSIPLine '%s' emiting %p [%p]", + c_str(),m,this); m_tr = plugin.ep()->engine()->addMessage(m); if (m_tr) { m_tr->ref(); @@ -2203,10 +2246,12 @@ void YateSIPLine::logout() m_resend = 0; bool sendLogout = m_valid && m_registrar && m_username; clearTransaction(); - m_retry = false; m_valid = false; if (sendLogout) { - SIPMessage* m = buildRegister(0,0); + DDebug(&plugin,DebugInfo,"YateSIPLine '%s' logging out [%p]",c_str(),this); + SIPMessage* m = buildRegister(0); + m_partyAddr.clear(); + m_partyPort = 0; if (!m) return; plugin.ep()->engine()->addMessage(m); @@ -2222,9 +2267,8 @@ bool YateSIPLine::process(SIPEvent* ev) return false; if (ev->getState() == SIPTransaction::Cleared) { clearTransaction(); - m_retry = false; m_valid = false; - m_resend = m_interval*1000000 + Time::now(); + m_resend = m_interval*(int64_t)1000000 + Time::now(); return false; } const SIPMessage* msg = ev->getMessage(); @@ -2233,26 +2277,29 @@ bool YateSIPLine::process(SIPEvent* ev) if (ev->getState() != SIPTransaction::Process) return false; clearTransaction(); - DDebug(&plugin,DebugAll,"YateSIPLine '%s' got answer %d%s [%p]", - c_str(),msg->code,m_retry ? " (may retry)" : "",this); + DDebug(&plugin,DebugAll,"YateSIPLine '%s' got answer %d [%p]", + c_str(),msg->code,this); switch (msg->code) { case 200: - m_retry = false; - m_valid = true; - m_resend = m_interval*1000000 + Time::now(); - Debug(&plugin,DebugInfo,"SIP line '%s' logon success",c_str()); - break; - case 401: - case 407: - if (m_retry) { - m_retry = false; - login(msg); - break; + if (msg->getParty()) { + if (m_localDetect) { + m_localAddr = msg->getParty()->getLocalAddr(); + m_localPort = msg->getParty()->getLocalPort(); + DDebug(&plugin,DebugInfo,"SIP line '%s' on local address %s:%d", + c_str(),m_localAddr.c_str(),m_localPort); + } + m_partyAddr = msg->getParty()->getPartyAddr(); + m_partyPort = msg->getParty()->getPartyPort(); } + m_valid = true; + // re-register at 3/4 of the expire interval + m_resend = m_interval*(int64_t)750000 + Time::now(); + Debug(&plugin,DebugInfo,"SIP line '%s' logon success to %s:%d", + c_str(),m_partyAddr.c_str(),m_partyPort); + break; default: - m_retry = false; m_valid = false; - Debug(&plugin,DebugInfo,"SIP line '%s' logon failure %d",c_str(),msg->code); + Debug(&plugin,DebugWarn,"SIP line '%s' logon failure %d",c_str(),msg->code); } return false; } @@ -2261,7 +2308,7 @@ void YateSIPLine::timer(const Time& when) { if (!m_resend || (m_resend > when)) return; - m_resend = m_interval*1000000 + when; + m_resend = m_interval*(int64_t)1000000 + when; login(); } @@ -2286,17 +2333,50 @@ bool YateSIPLine::change(String& dest, const String& src) return true; } +bool YateSIPLine::change(int& dest, int src) +{ + if (dest == src) + return false; + // we need to log out before any parameter changes + logout(); + dest = src; + return true; +} + bool YateSIPLine::update(const Message& msg) { DDebug(&plugin,DebugInfo,"YateSIPLine::update() '%s' [%p]",c_str(),this); + String oper(msg.getValue("operation")); + if (oper == "logout") { + logout(); + return true; + } bool chg = false; chg = change(m_registrar,msg.getValue("registrar")) || chg; chg = change(m_outbound,msg.getValue("outbound")) || chg; chg = change(m_username,msg.getValue("username")) || chg; + chg = change(m_authname,msg.getValue("authname")) || chg; chg = change(m_password,msg.getValue("password")) || chg; chg = change(m_domain,msg.getValue("domain")) || chg; m_display = msg.getValue("description"); m_interval = msg.getIntValue("interval",600); + String tmp(msg.getValue("localaddress")); + m_localDetect = (tmp == "auto"); + if (!m_localDetect) { + int port = 0; + if (tmp) { + int sep = tmp.find(':'); + if (sep > 0) { + port = tmp.substr(sep+1).toInteger(5060); + tmp = tmp.substr(0,sep); + } + else if (sep < 0) + port = 5060; + } + chg = change(m_localAddr,tmp) || chg; + chg = change(m_localPort,port) || chg; + } + tmp = msg.getValue("operation"); // if something changed we logged out so try to climb back if (chg) login(); @@ -2377,8 +2457,13 @@ bool SipHandler::received(Message &msg) uri = uri.matchString(1); if (!(method && uri)) return false; + YateSIPLine* line = plugin.findLine(msg.getValue("line")); + if (line && !line->valid()) { + msg.setParam("error","offline"); + return false; + } SIPMessage* sip = new SIPMessage(method,uri); - plugin.ep()->buildParty(sip,msg.getValue("host"),msg.getIntValue("port")); + plugin.ep()->buildParty(sip,msg.getValue("host"),msg.getIntValue("port"),line); copySipHeaders(*sip,msg); const char* type = msg.getValue("xsip_type"); const char* body = msg.getValue("xsip_body"); @@ -2426,6 +2511,7 @@ YateSIPConnection* SIPDriver::findDialog(const SIPDialog& dialog) return 0; } +// find line by name YateSIPLine* SIPDriver::findLine(const String& line) { if (line.null()) @@ -2434,6 +2520,25 @@ YateSIPLine* SIPDriver::findLine(const String& line) return l ? static_cast(l->get()) : 0; } +// find line by party address and port +YateSIPLine* SIPDriver::findLine(const String& addr, int port, const String& user) +{ + if (!(port && addr)) + return 0; + Lock mylock(this); + ObjList* l = s_lines.skipNull(); + for (; l; l = l->skipNext()) { + YateSIPLine* sl = static_cast(l->get()); + if (sl->getPartyPort() && (sl->getPartyPort() == port) && (sl->getPartyAddr() == addr)) { + if (user && (sl->getUserName() != user)) + continue; + return sl; + } + } + return 0; +} + +// check if a line is either empty or valid (logged in or no registrar) bool SIPDriver::validLine(const String& line) { if (line.null()) @@ -2460,8 +2565,11 @@ bool SIPDriver::msgExecute(Message& msg, String& dest) Debug(this,DebugWarn,"SIP call found but no data channel!"); return false; } - if (!validLine(msg.getValue("line"))) + if (!validLine(msg.getValue("line"))) { + // asked to use a line but it's not registered + msg.setParam("error","offline"); return false; + } YateSIPConnection* conn = new YateSIPConnection(msg,dest,msg.getValue("id")); if (conn->getTransaction()) { CallEndpoint* ch = static_cast(msg.userData()); @@ -2494,6 +2602,7 @@ void SIPDriver::initialize() s_cfg.load(); s_maxForwards = s_cfg.getIntValue("general","maxforwards",20); s_privacy = s_cfg.getBoolValue("general","privacy"); + s_auto_nat = s_cfg.getBoolValue("general","nat",true); if (!m_endpoint) { m_endpoint = new YateSIPEndPoint(); if (!(m_endpoint->Init())) {