/// <summary> /// Default constructor. /// </summary> /// <param name="stack">Owner SIP stack.</param> /// <exception cref="ArgumentNullException">Is raised when <b>stack</b> is null reference.</exception> internal SIP_TransportLayer(SIP_Stack stack) { if (stack == null) { throw new ArgumentNullException("stack"); } m_pStack = stack; m_pUdpServer = new UDP_Server(); m_pUdpServer.ProcessMode = UDP_ProcessMode.Parallel; m_pUdpServer.PacketReceived += m_pUdpServer_PacketReceived; m_pUdpServer.Error += m_pUdpServer_Error; m_pTcpServer = new TCP_Server<TCP_ServerSession>(); m_pTcpServer.SessionCreated += m_pTcpServer_SessionCreated; m_pFlowManager = new SIP_FlowManager(this); m_pBinds = new IPBindInfo[] {}; m_pRandom = new Random(); m_pLocalIPv4 = new CircleCollection<IPAddress>(); m_pLocalIPv6 = new CircleCollection<IPAddress>(); }
/// <summary> /// Default constructor. /// </summary> /// <param name="stack">Reference to SIP stack.</param> /// <param name="transaction">Client transaction what response it is. This value can be null if no matching client response.</param> /// <param name="response">Received response.</param> internal SIP_ResponseReceivedEventArgs(SIP_Stack stack, SIP_ClientTransaction transaction, SIP_Response response) { m_pStack = stack; m_pResponse = response; m_pTransaction = transaction; }
/// <summary> /// Default constructor. /// </summary> /// <param name="stack">Owner SIP stack.</param> /// <param name="flow">Transaction data flow.</param> /// <param name="request">SIP request that transaction will handle.</param> /// <exception cref="ArgumentNullException">Is raised when <b>stack</b>,<b>flow</b> or <b>request</b> is null reference.</exception> public SIP_Transaction(SIP_Stack stack, SIP_Flow flow, SIP_Request request) { if (stack == null) { throw new ArgumentNullException("stack"); } if (flow == null) { throw new ArgumentNullException("flow"); } if (request == null) { throw new ArgumentNullException("request"); } m_pStack = stack; m_pFlow = flow; m_pRequest = request; m_Method = request.RequestLine.Method; m_CreateTime = DateTime.Now; m_pResponses = new List<SIP_Response>(); // Validate Via: SIP_t_ViaParm via = request.Via.GetTopMostValue(); if (via == null) { throw new ArgumentException("Via: header is missing !"); } if (via.Branch == null) { throw new ArgumentException("Via: header 'branch' parameter is missing !"); } m_ID = via.Branch; if (this is SIP_ServerTransaction) { /* We use branch and sent-by as indexing key for transaction, the only special what we need to do is to handle CANCEL, because it has same branch as transaction to be canceled. For avoiding key collision, we add branch + '-' + 'sent-by' + CANCEL for cancel index key. ACK has also same branch, but we won't do transaction for ACK, so it isn't problem. */ string key = request.Via.GetTopMostValue().Branch + '-' + request.Via.GetTopMostValue().SentBy; if (request.RequestLine.Method == SIP_Methods.CANCEL) { key += "-CANCEL"; } m_Key = key; } else { m_Key = m_ID + "-" + request.RequestLine.Method; } }
/// <summary> /// Default constructor. /// </summary> /// <param name="stack">Reference to SIP stack.</param> /// <param name="flow">SIP data flow.</param> /// <param name="request">Recieved request.</param> /// <param name="dialog">SIP dialog which received request.</param> /// <param name="transaction">SIP server transaction which must be used to send response back to request maker.</param> internal SIP_RequestReceivedEventArgs(SIP_Stack stack, SIP_Flow flow, SIP_Request request, SIP_Dialog dialog, SIP_ServerTransaction transaction) { m_pStack = stack; m_pFlow = flow; m_pRequest = request; m_pDialog = dialog; m_pTransaction = transaction; }
/// <summary> /// Default constructor. /// </summary> /// <param name="stack">Owner SIP stack.</param> /// <param name="flow">SIP data flow which received request.</param> /// <param name="request">SIP request that transaction will handle.</param> /// <exception cref="ArgumentNullException">Is raised when <b>stack</b>,<b>flow</b> or <b>request</b> is null reference.</exception> /// <exception cref="ArgumentException">Is raised when any of the arguments has invalid value.</exception> public SIP_ServerTransaction(SIP_Stack stack, SIP_Flow flow, SIP_Request request) : base(stack, flow, request) { // Log if (Stack.Logger != null) { Stack.Logger.AddText(ID, "Transaction [branch='" + ID + "';method='" + Method + "';IsServer=true] created."); } Start(); }
/// <summary> /// Default constructor. /// </summary> /// <param name="stack">Owner SIP stack.</param> /// <param name="flow">SIP data flow which is used to send request.</param> /// <param name="request">SIP request that transaction will handle.</param> /// <exception cref="ArgumentNullException">Is raised when <b>stack</b>,<b>flow</b> or <b>request</b> is null reference.</exception> /// <exception cref="ArgumentException">Is raised when any of the arguments has invalid value.</exception> internal SIP_ClientTransaction(SIP_Stack stack, SIP_Flow flow, SIP_Request request) : base(stack, flow, request) { // Log if (Stack.Logger != null) { Stack.Logger.AddText(ID, "Transaction [branch='" + ID + "';method='" + Method + "';IsServer=false] created."); } SetState(SIP_TransactionState.WaitingToStart); }
/// <summary> /// Default constructor. /// </summary> /// <param name="stack">Reference to SIP stack.</param> /// <exception cref="ArgumentNullException">Is raised when <b>stack</b> is null reference.</exception> internal SIP_TransactionLayer(SIP_Stack stack) { if (stack == null) { throw new ArgumentNullException("stack"); } m_pStack = stack; m_pClientTransactions = new Dictionary<string, SIP_ClientTransaction>(); m_pServerTransactions = new Dictionary<string, SIP_ServerTransaction>(); m_pDialogs = new Dictionary<string, SIP_Dialog>(); m_pTimer = new Timer(20000); m_pTimer.AutoReset = true; m_pTimer.Elapsed += m_pTimer_Elapsed; }
/// <summary> /// Default constructor. /// </summary> /// <param name="stack">Owner stack.</param> /// <param name="request">SIP request.</param> /// <param name="flow">Active data flow what to try before RFC 3261 [4](RFC 3263) methods to use to send request. /// This value can be null.</param> /// <exception cref="ArgumentNullException">Is raised when <b>stack</b> or <b>request</b> is null.</exception> internal SIP_RequestSender(SIP_Stack stack, SIP_Request request, SIP_Flow flow) { if (stack == null) { throw new ArgumentNullException("stack"); } if (request == null) { throw new ArgumentNullException("request"); } m_pStack = stack; m_pRequest = request; m_pFlow = flow; m_pCredentials = new List<NetworkCredential>(); m_pHops = new Queue<SIP_Hop>(); }
/// <summary> /// Initializes dialog. /// </summary> /// <param name="stack">Owner stack.</param> /// <param name="transaction">Owner transaction.</param> /// <param name="response">SIP response what caused dialog creation.</param> /// <exception cref="ArgumentNullException">Is raised when <b>stack</b>,<b>transaction</b> or <b>response</b>.</exception> protected internal override void Init(SIP_Stack stack, SIP_Transaction transaction, SIP_Response response) { if (stack == null) { throw new ArgumentNullException("stack"); } if (transaction == null) { throw new ArgumentNullException("transaction"); } if (response == null) { throw new ArgumentNullException("response"); } base.Init(stack, transaction, response); if (transaction is SIP_ServerTransaction) { if (response.StatusCodeType == SIP_StatusCodeType.Success) { SetState(SIP_DialogState.Early, false); // We need to retransmit 2xx response while we get ACK or timeout. (RFC 3261 13.3.1.4.) m_pUasInvite2xxRetransmits.Add(new UasInvite2xxRetransmit(this, response)); } else if (response.StatusCodeType == SIP_StatusCodeType.Provisional) { SetState(SIP_DialogState.Early, false); m_pActiveInvite = transaction; m_pActiveInvite.StateChanged += delegate { if (m_pActiveInvite != null && m_pActiveInvite.State == SIP_TransactionState.Terminated) { m_pActiveInvite = null; } }; // Once we send 2xx response, we need to retransmit it while get ACK or timeout. (RFC 3261 13.3.1.4.) ((SIP_ServerTransaction) m_pActiveInvite).ResponseSent += delegate(object s, SIP_ResponseSentEventArgs a) { if (a.Response.StatusCodeType == SIP_StatusCodeType.Success) { m_pUasInvite2xxRetransmits.Add(new UasInvite2xxRetransmit(this, a.Response)); } }; } else { throw new ArgumentException( "Argument 'response' has invalid status code, 1xx - 2xx is only allowed."); } } else { if (response.StatusCodeType == SIP_StatusCodeType.Success) { SetState(SIP_DialogState.Confirmed, false); // Wait for retransmited 2xx responses. (RFC 3261 13.2.2.4.) m_pUacInvite2xxRetransmitWaits.Add(new UacInvite2xxRetransmissionWaiter(this, transaction. Request)); } else if (response.StatusCodeType == SIP_StatusCodeType.Provisional) { SetState(SIP_DialogState.Early, false); m_pActiveInvite = transaction; m_pActiveInvite.StateChanged += delegate { if (m_pActiveInvite != null && m_pActiveInvite.State == SIP_TransactionState.Terminated) { m_pActiveInvite = null; } }; // Once we receive 2xx response, we need to wait for retransmitted 2xx responses. (RFC 3261 13.2.2.4) ((SIP_ClientTransaction) m_pActiveInvite).ResponseReceived += delegate(object s, SIP_ResponseReceivedEventArgs a) { if (a.Response.StatusCodeType == SIP_StatusCodeType.Success) { UacInvite2xxRetransmissionWaiter waiter = new UacInvite2xxRetransmissionWaiter(this, m_pActiveInvite.Request); m_pUacInvite2xxRetransmitWaits.Add(waiter); // Force to send initial ACK to 2xx response. waiter.Process(a.Response); SetState(SIP_DialogState.Confirmed, true); } }; } else { throw new ArgumentException( "Argument 'response' has invalid status code, 1xx - 2xx is only allowed."); } } }
/// <summary> /// Default constructor. /// </summary> /// <param name="stack">Owner SIP stack.</param> /// <param name="server">Registrar server URI. For example: sip:domain.com.</param> /// <param name="aor">Address of record. For example: [email protected].</param> /// <param name="contact">Contact URI.</param> /// <param name="expires">Gets after how many seconds reigisration expires.</param> /// <exception cref="ArgumentNullException">Is raised when <b>ua</b>,<b>server</b>,<b>transport</b>,<b>aor</b> or <b>contact</b> is null reference.</exception> /// <exception cref="ArgumentException">Is raised when any of the arguments contains invalid value.</exception> internal SIP_UA_Registration(SIP_Stack stack, SIP_Uri server, string aor, AbsoluteUri contact, int expires) { if (stack == null) { throw new ArgumentNullException("stack"); } if (server == null) { throw new ArgumentNullException("server"); } if (aor == null) { throw new ArgumentNullException("aor"); } if (aor == string.Empty) { throw new ArgumentException("Argument 'aor' value must be specified."); } if (contact == null) { throw new ArgumentNullException("contact"); } m_pStack = stack; m_pServer = server; m_AOR = aor; m_pContact = contact; m_RefreshInterval = expires; m_pTimer = new TimerEx((m_RefreshInterval - 15)*1000); m_pTimer.AutoReset = false; m_pTimer.Elapsed += m_pTimer_Elapsed; m_pTimer.Enabled = false; }
/// <summary> /// Cleans up any resources being used. /// </summary> public void Dispose() { if (m_IsDisposed) { return; } m_IsDisposed = true; m_pStack = null; m_pTimer.Dispose(); m_pTimer = null; SetState(SIP_UA_RegistrationState.Disposed); OnDisposed(); Registered = null; Unregistered = null; Error = null; Disposed = null; }
/// <summary> /// Cleans up any resources being used. /// </summary> public virtual void Dispose() { lock (m_pLock) { if (State == SIP_DialogState.Disposed) { return; } SetState(SIP_DialogState.Disposed, true); m_pStack = null; m_CallID = null; m_LocalTag = null; m_RemoteTag = null; m_pLocalUri = null; m_pRemoteUri = null; m_pLocalContact = null; m_pRemoteTarget = null; m_pRouteSet = null; m_pFlow = null; m_pLock = null; } }
/// <summary> /// Default constructor. /// </summary> /// <param name="stack">Reference to SIP stack.</param> /// <param name="flow">SIP data flow.</param> /// <param name="request">Recieved request.</param> internal SIP_RequestReceivedEventArgs(SIP_Stack stack, SIP_Flow flow, SIP_Request request) : this(stack, flow, request, null, null) {}
/// <summary> /// Server TCP,TLS constructor. /// </summary> /// <param name="stack">Owner stack.</param> /// <param name="session">TCP session.</param> /// <exception cref="ArgumentNullException">Is raised when <b>stack</b> or <b>session</b> is null reference.</exception> internal SIP_Flow(SIP_Stack stack, TCP_Session session) { if (stack == null) { throw new ArgumentNullException("stack"); } if (session == null) { throw new ArgumentNullException("session"); } m_pStack = stack; m_pTcpSession = session; m_IsServer = true; m_pLocalEP = session.LocalEndPoint; m_pRemoteEP = session.RemoteEndPoint; m_Transport = session.IsSecureConnection ? SIP_Transport.TLS : SIP_Transport.TCP; m_CreateTime = DateTime.Now; m_LastActivity = DateTime.Now; m_ID = m_pLocalEP + "-" + m_pRemoteEP + "-" + m_Transport; m_pMessage = new MemoryStream(); BeginReadHeader(); }
/// <summary> /// Default constructor. /// </summary> /// <param name="stack">Owner stack.</param> /// <param name="isServer">Specifies if flow is server or client flow.</param> /// <param name="localEP">Local IP end point.</param> /// <param name="remoteEP">Remote IP end point.</param> /// <param name="transport">SIP transport.</param> /// <exception cref="ArgumentNullException">Is raised when <b>stack</b>,<b>localEP</b>,<b>remoteEP</b> or <b>transport</b> is null reference.</exception> /// <exception cref="ArgumentException">Is raised whena any of the arguments has invalid value.</exception> internal SIP_Flow(SIP_Stack stack, bool isServer, IPEndPoint localEP, IPEndPoint remoteEP, string transport) { if (stack == null) { throw new ArgumentNullException("stack"); } if (localEP == null) { throw new ArgumentNullException("localEP"); } if (remoteEP == null) { throw new ArgumentNullException("remoteEP"); } if (transport == null) { throw new ArgumentNullException("transport"); } m_pStack = stack; m_IsServer = isServer; m_pLocalEP = localEP; m_pRemoteEP = remoteEP; m_Transport = transport.ToUpper(); m_CreateTime = DateTime.Now; m_LastActivity = DateTime.Now; m_ID = m_pLocalEP + "-" + m_pRemoteEP + "-" + m_Transport; m_pMessage = new MemoryStream(); }
/// <summary> /// Initializes dialog. /// </summary> /// <param name="stack">Owner stack.</param> /// <param name="transaction">Owner transaction.</param> /// <param name="response">SIP response what caused dialog creation.</param> /// <exception cref="ArgumentNullException">Is raised when <b>stack</b>,<b>transaction</b> or <b>response</b>.</exception> protected internal virtual void Init(SIP_Stack stack, SIP_Transaction transaction, SIP_Response response) { if (stack == null) { throw new ArgumentNullException("stack"); } if (transaction == null) { throw new ArgumentNullException("transaction"); } if (response == null) { throw new ArgumentNullException("response"); } m_pStack = stack; #region UAS /* RFC 3261 12.1.1. The UAS then constructs the state of the dialog. This state MUST be maintained for the duration of the dialog. If the request arrived over TLS, and the Request-URI contained a SIPS URI, the "secure" flag is set to TRUE. The route set MUST be set to the list of URIs in the Record-Route header field from the request, taken in order and preserving all URI parameters. If no Record-Route header field is present in the request, the route set MUST be set to the empty set. This route set, even if empty, overrides any pre-existing route set for future requests in this dialog. The remote target MUST be set to the URI from the Contact header field of the request. The remote sequence number MUST be set to the value of the sequence number in the CSeq header field of the request. The local sequence number MUST be empty. The call identifier component of the dialog ID MUST be set to the value of the Call-ID in the request. The local tag component of the dialog ID MUST be set to the tag in the To field in the response to the request (which always includes a tag), and the remote tag component of the dialog ID MUST be set to the tag from the From field in the request. A UAS MUST be prepared to receive a request without a tag in the From field, in which case the tag is considered to have a value of null. This is to maintain backwards compatibility with RFC 2543, which did not mandate From tags. The remote URI MUST be set to the URI in the From field, and the local URI MUST be set to the URI in the To field. */ if (transaction is SIP_ServerTransaction) { // TODO: Validate request or client transaction must do it ? m_IsSecure = ((SIP_Uri) transaction.Request.RequestLine.Uri).IsSecure; m_pRouteSet = (SIP_t_AddressParam[]) Core.ReverseArray(transaction.Request.RecordRoute.GetAllValues()); m_pRemoteTarget = (SIP_Uri) transaction.Request.Contact.GetTopMostValue().Address.Uri; m_RemoteSeqNo = transaction.Request.CSeq.SequenceNumber; m_LocalSeqNo = 0; m_CallID = transaction.Request.CallID; m_LocalTag = response.To.Tag; m_RemoteTag = transaction.Request.From.Tag; m_pRemoteUri = transaction.Request.From.Address.Uri; m_pLocalUri = transaction.Request.To.Address.Uri; m_pLocalContact = (SIP_Uri) response.Contact.GetTopMostValue().Address.Uri; } #endregion #region UAC /* RFC 3261 12.1.2. When a UAC receives a response that establishes a dialog, it constructs the state of the dialog. This state MUST be maintained for the duration of the dialog. If the request was sent over TLS, and the Request-URI contained a SIPS URI, the "secure" flag is set to TRUE. The route set MUST be set to the list of URIs in the Record-Route header field from the response, taken in reverse order and preserving all URI parameters. If no Record-Route header field is present in the response, the route set MUST be set to the empty set. This route set, even if empty, overrides any pre-existing route set for future requests in this dialog. The remote target MUST be set to the URI from the Contact header field of the response. The local sequence number MUST be set to the value of the sequence number in the CSeq header field of the request. The remote sequence number MUST be empty (it is established when the remote UA sends a request within the dialog). The call identifier component of the dialog ID MUST be set to the value of the Call-ID in the request. The local tag component of the dialog ID MUST be set to the tag in the From field in the request, and the remote tag component of the dialog ID MUST be set to the tag in the To field of the response. A UAC MUST be prepared to receive a response without a tag in the To field, in which case the tag is considered to have a value of null. This is to maintain backwards compatibility with RFC 2543, which did not mandate To tags. The remote URI MUST be set to the URI in the To field, and the local URI MUST be set to the URI in the From field. */ else { // TODO: Validate request or client transaction must do it ? m_IsSecure = ((SIP_Uri) transaction.Request.RequestLine.Uri).IsSecure; m_pRouteSet = (SIP_t_AddressParam[]) Core.ReverseArray(response.RecordRoute.GetAllValues()); m_pRemoteTarget = (SIP_Uri) response.Contact.GetTopMostValue().Address.Uri; m_LocalSeqNo = transaction.Request.CSeq.SequenceNumber; m_RemoteSeqNo = 0; m_CallID = transaction.Request.CallID; m_LocalTag = transaction.Request.From.Tag; m_RemoteTag = response.To.Tag; m_pRemoteUri = transaction.Request.To.Address.Uri; m_pLocalUri = transaction.Request.From.Address.Uri; m_pLocalContact = (SIP_Uri) transaction.Request.Contact.GetTopMostValue().Address.Uri; } #endregion m_pFlow = transaction.Flow; }
/// <summary> /// Cleans up any resources being used. /// </summary> public void Dispose() { lock (m_pLock) { if (m_State == SIP_RequestSenderState.Disposed) { return; } m_State = SIP_RequestSenderState.Disposed; OnDisposed(); ResponseReceived = null; Completed = null; Disposed = null; m_pStack = null; m_pRequest = null; m_pCredentials = null; m_pHops = null; m_pTransaction = null; m_pLock = null; } }
/// <summary> /// Cleans up any resources being used. /// </summary> public virtual void Dispose() { SetState(SIP_TransactionState.Disposed); OnDisposed(); m_pStack = null; m_pFlow = null; m_pRequest = null; StateChanged = null; Disposed = null; TimedOut = null; TransportError = null; }