/// <summary> /// Gets fired when a suspected SIP message is extracted from the TCP data stream. /// </summary> protected void SIPTCPMessageReceived(SIPChannel channel, SIPEndPoint remoteEndPoint, byte[] buffer) { if (m_connectionFailures.ContainsKey(remoteEndPoint.GetIPEndPoint().ToString())) { m_connectionFailures.Remove(remoteEndPoint.GetIPEndPoint().ToString()); } if (m_connectionFailureStrikes.ContainsKey(remoteEndPoint.GetIPEndPoint().ToString())) { m_connectionFailureStrikes.Remove(remoteEndPoint.GetIPEndPoint().ToString()); } SIPMessageReceived?.Invoke(channel, remoteEndPoint, buffer); }
/// <summary> /// Get the local SIPEndPoint this channel will use for communicating with the destination SIP end point. /// </summary> /// <param name="dstEndPoint">The destination SIP end point.</param> /// <returns>The local SIP end points this channel selects to use for connecting to the destination.</returns> internal virtual SIPEndPoint GetLocalSIPEndPointForDestination(SIPEndPoint dstEndPoint) { IPAddress dstAddress = dstEndPoint.GetIPEndPoint().Address; IPAddress localAddress = GetLocalIPAddressForDestination(dstAddress); return(new SIPEndPoint(SIPProtocol, localAddress, Port, ID, null)); }
/// <summary> /// This constructor is used to create non-INVITE dialogues for example the dialogues used in SIP event interactions /// where the dialogue is created based on a SUBSCRIBE request. /// </summary> public SIPDialogue( SIPRequest nonInviteRequest, string toTag) { Id = Guid.NewGuid(); CallId = nonInviteRequest.Header.CallId; RouteSet = (nonInviteRequest.Header.RecordRoutes != null) ? nonInviteRequest.Header.RecordRoutes.Reversed() : null; RemoteUserField = nonInviteRequest.Header.From.FromUserField; RemoteTag = nonInviteRequest.Header.From.FromTag; LocalUserField = nonInviteRequest.Header.To.ToUserField; LocalUserField.Parameters.Set("tag", toTag); LocalTag = toTag; CSeq = nonInviteRequest.Header.CSeq; Inserted = DateTime.UtcNow; Direction = SIPCallDirection.Out; // Set the dialogue remote target and take care of mangling if an upstream proxy has indicated it's required. RemoteTarget = nonInviteRequest.Header.Contact[0].ContactURI; ProxySendFrom = nonInviteRequest.Header.ProxyReceivedOn; if (!nonInviteRequest.Header.ProxyReceivedFrom.IsNullOrBlank()) { // Setting the Proxy-ReceivedOn header is how an upstream proxy will let an agent know it should mangle the contact. // Don't mangle private contacts if there is a Record-Route header. If a proxy is putting private IP's in a Record-Route header that's its problem. if (RouteSet == null && IPSocket.IsPrivateAddress(RemoteTarget.Host)) { SIPEndPoint remoteUASIPEndPoint = SIPEndPoint.ParseSIPEndPoint(nonInviteRequest.Header.ProxyReceivedFrom); RemoteTarget.Host = remoteUASIPEndPoint.GetIPEndPoint().ToString(); } } }
/// <summary> /// Ideally sends on the web socket channel should specify the connection ID. But if there's /// a good reason not to we can check if there is an existing client connection with the /// requested remote end point and use it. /// </summary> /// <param name="destinationEndPoint">The remote destination end point to send the data to.</param> /// <param name="buffer">The data to send.</param> /// <param name="connectionIDHint">The ID of the specific web socket connection to try and send the message on.</param> /// <returns>If no errors SocketError.Success otherwise an error value.</returns> public override async Task <SocketError> SendAsync(SIPEndPoint destinationEndPoint, byte[] buffer, string connectionIDHint) { if (destinationEndPoint == null) { throw new ApplicationException("An empty destination was specified to Send in SIPWebSocketChannel."); } else if (buffer == null || buffer.Length == 0) { throw new ArgumentException("buffer", "The buffer must be set and non empty for Send in SIPWebSocketChannel."); } try { var ingressClient = GetIngressConnection(destinationEndPoint.GetIPEndPoint(), connectionIDHint); // And lastly if we now have a valid web socket then send. if (ingressClient != null) { await Task.Run(() => ingressClient.Send(buffer, 0, buffer.Length)).ConfigureAwait(false); return(SocketError.Success); } else { return(SocketError.ConnectionReset); } } catch (SocketException sockExcp) { return(sockExcp.SocketErrorCode); } }
/// <summary> /// Attempts to extract SIP messages from the data that has been received on the SIP stream connection. /// </summary> /// <param name="recvChannel">The receiving SIP channel.</param> /// <param name="buffer">The buffer holding the current data from the stream. Note that the buffer can /// stretch over multiple receives.</param> /// <param name="bytesRead">The bytes that were read by the latest receive operation (the new bytes available).</param> public void ExtractSIPMessages(SIPChannel recvChannel, byte[] buffer, int bytesRead) { RecvEndPosn += bytesRead; int bytesSkipped = 0; byte[] sipMsgBuffer = SIPMessageBuffer.ParseSIPMessageFromStream(buffer, RecvStartPosn, RecvEndPosn, m_sipEncoding, out bytesSkipped); while (sipMsgBuffer != null) { // A SIP message is available. if (SIPMessageReceived != null) { LastTransmission = DateTime.Now; SIPEndPoint localEndPoint = new SIPEndPoint(ConnectionProtocol, StreamSocket.LocalEndPoint as IPEndPoint, recvChannel.ID, ConnectionID); SIPEndPoint remoteEndPoint = new SIPEndPoint(ConnectionProtocol, RemoteSIPEndPoint.GetIPEndPoint(), recvChannel.ID, ConnectionID); SIPMessageReceived(recvChannel, localEndPoint, remoteEndPoint, sipMsgBuffer); } RecvStartPosn += (sipMsgBuffer.Length + bytesSkipped); if (RecvStartPosn == RecvEndPosn) { // All data has been successfully extracted from the receive buffer. RecvStartPosn = RecvEndPosn = 0; break; } else { // Try and extract another SIP message from the receive buffer. sipMsgBuffer = SIPMessageBuffer.ParseSIPMessageFromStream(buffer, RecvStartPosn, RecvEndPosn, m_sipEncoding, out bytesSkipped); } } }
/// <summary> /// Attempts to send data to the remote end point over a reliable connection. If an existing /// connection exists it will be used otherwise an attempt will be made to establish a new connection. /// </summary> /// <param name="dstSIPEndPoint">The remote SIP end point to send the reliable data to.</param> /// <param name="buffer">The data to send.</param> /// <param name="serverCertificateName">Optional. Only relevant for SSL streams. The common name /// that is expected for the remote SSL server.</param> /// <param name="connectionIDHint">Optional. The ID of the specific TCP connection to try and the send the message on.</param> /// <returns>If no errors SocketError.Success otherwise an error value.</returns> public override Task <SocketError> SendSecureAsync(SIPEndPoint dstSIPEndPoint, byte[] buffer, string serverCertificateName, string connectionIDHint) { try { IPEndPoint dstEndPoint = dstSIPEndPoint?.GetIPEndPoint(); if (dstEndPoint == null) { throw new ArgumentException("dstEndPoint", "An empty destination was specified to Send in SIPTCPChannel."); } if (buffer == null || buffer.Length == 0) { throw new ApplicationException("An empty buffer was specified to Send in SIPTCPChannel."); } else if (DisableLocalTCPSocketsCheck == false && m_localTCPSockets.Contains(dstEndPoint.ToString())) { logger.LogWarning($"SIP {ProtDescr} Channel blocked Send to {dstEndPoint} as it was identified as a locally hosted {ProtDescr} socket.\r\n" + Encoding.UTF8.GetString(buffer)); throw new ApplicationException($"A Send call was blocked in SIP {ProtDescr} Channel due to the destination being another local TCP socket."); } else { // Lookup a client socket that is connected to the destination. If it does not exist attempt to connect a new one. SIPStreamConnection sipStreamConn = null; if (connectionIDHint != null) { m_connections.TryGetValue(connectionIDHint, out sipStreamConn); } if (sipStreamConn == null && HasConnection(dstSIPEndPoint)) { sipStreamConn = m_connections.Where(x => x.Value.RemoteEndPoint.Equals(dstEndPoint)).First().Value; } if (sipStreamConn != null) { SendOnConnected(sipStreamConn, buffer); return(Task.FromResult(SocketError.Success)); } else { return(ConnectClientAsync(dstEndPoint, buffer, serverCertificateName)); } } } catch (SocketException sockExcp) { return(Task.FromResult(sockExcp.SocketErrorCode)); } catch (ApplicationException) { throw; } catch (Exception excp) { logger.LogError($"Exception SIPTCPChannel Send (sendto=> {dstSIPEndPoint}. {excp.Message}"); throw; } }
private void SIPTCPMessageReceived(SIPChannel channel, SIPEndPoint remoteEndPoint, byte[] buffer) { if (m_connectionFailures.ContainsKey(remoteEndPoint.GetIPEndPoint().ToString())) { m_connectionFailures.Remove(remoteEndPoint.GetIPEndPoint().ToString()); } if (m_connectionFailureStrikes.ContainsKey(remoteEndPoint.GetIPEndPoint().ToString())) { m_connectionFailureStrikes.Remove(remoteEndPoint.GetIPEndPoint().ToString()); } if (SIPMessageReceived != null) { SIPMessageReceived(channel, remoteEndPoint, buffer); } }
/// <summary> /// Get the local SIPEndPoint this channel will use for communicating with the destination SIP end point. /// </summary> /// <param name="dstEndPoint">The destination SIP end point.</param> /// <returns>The local SIP end points this channel selects to use for connecting to the destination.</returns> internal override SIPEndPoint GetLocalSIPEndPointForDestination(SIPEndPoint dstEndPoint) { IPAddress dstAddress = dstEndPoint.GetIPEndPoint().Address; IPAddress localAddress = GetLocalIPAddressForDestination(dstAddress); // Need to return ws or wss to match the destination. return(new SIPEndPoint(dstEndPoint.Protocol, localAddress, Port, ID, null)); }
/// <summary> /// Checks whether the client web socket SIP channel has a connection to the requested server end point. /// </summary> public override bool HasConnection(SIPEndPoint serverEndPoint) { string uriPrefix = (serverEndPoint.Protocol == SIPProtocolsEnum.wss) ? WEB_SOCKET_SECURE_URI_PREFIX : WEB_SOCKET_URI_PREFIX; var serverUri = new Uri($"{uriPrefix}{serverEndPoint.GetIPEndPoint()}"); string connectionID = GetConnectionID(serverUri); return(m_egressConnections.ContainsKey(connectionID)); }
/// <summary> /// This constructor is used by client user agents or SIP elements acting in a client user agent role. When /// acting as a client user agent the local fields are contained in the From header and the remote fields are /// in the To header. /// </summary> public SIPDialogue( UACInviteTransaction uacInviteTransaction, string owner, string adminMemberId) { Id = Guid.NewGuid(); CallId = uacInviteTransaction.TransactionRequest.Header.CallId; RouteSet = (uacInviteTransaction.TransactionFinalResponse != null && uacInviteTransaction.TransactionFinalResponse.Header.RecordRoutes != null) ? uacInviteTransaction.TransactionFinalResponse.Header.RecordRoutes.Reversed() : null; LocalUserField = uacInviteTransaction.TransactionFinalResponse.Header.From.FromUserField; LocalTag = uacInviteTransaction.TransactionFinalResponse.Header.From.FromTag; RemoteUserField = uacInviteTransaction.TransactionFinalResponse.Header.To.ToUserField; RemoteTag = uacInviteTransaction.TransactionFinalResponse.Header.To.ToTag; CSeq = uacInviteTransaction.TransactionRequest.Header.CSeq; CDRId = (uacInviteTransaction.CDR != null) ? uacInviteTransaction.CDR.CDRId : Guid.Empty; Owner = owner; AdminMemberId = adminMemberId; ContentType = uacInviteTransaction.TransactionRequest.Header.ContentType; SDP = uacInviteTransaction.TransactionRequest.Body; RemoteSDP = uacInviteTransaction.TransactionFinalResponse.Body; Inserted = DateTimeOffset.UtcNow; Direction = SIPCallDirection.Out; // Set the dialogue remote target and take care of mangling if an upstream proxy has indicated it's required. if (uacInviteTransaction.TransactionFinalResponse != null) { RemoteTarget = new SIPURI(uacInviteTransaction.TransactionRequest.URI.Scheme, uacInviteTransaction.TransactionFinalResponse.RemoteSIPEndPoint.CopyOf()); } else { RemoteTarget = new SIPURI(uacInviteTransaction.TransactionRequest.URI.Scheme, uacInviteTransaction.TransactionRequest.RemoteSIPEndPoint.CopyOf()); } ProxySendFrom = uacInviteTransaction.TransactionFinalResponse.Header.ProxyReceivedOn; if (uacInviteTransaction.TransactionFinalResponse.Header.Contact != null && uacInviteTransaction.TransactionFinalResponse.Header.Contact.Count > 0) { RemoteTarget = uacInviteTransaction.TransactionFinalResponse.Header.Contact[0].ContactURI.CopyOf(); if (!uacInviteTransaction.TransactionFinalResponse.Header.ProxyReceivedFrom.IsNullOrBlank()) { // Setting the Proxy-ReceivedOn header is how an upstream proxy will let an agent know it should mangle the contact. // Don't mangle private contacts if there is a Record-Route header. If a proxy is putting private IP's in a Record-Route header that's its problem. if (RouteSet == null && IPSocket.IsPrivateAddress(RemoteTarget.Host)) { SIPEndPoint remoteUASSIPEndPoint = SIPEndPoint.ParseSIPEndPoint(uacInviteTransaction.TransactionFinalResponse.Header .ProxyReceivedFrom); RemoteTarget.Host = remoteUASSIPEndPoint.GetIPEndPoint().ToString(); } } } }
public SIPURI(SIPSchemesEnum scheme, SIPEndPoint sipEndPoint) { Scheme = scheme; Host = sipEndPoint.GetIPEndPoint().ToString(); if (sipEndPoint.Protocol != SIPProtocolsEnum.udp && scheme != SIPSchemesEnum.sips) { Parameters.Set(m_uriParamTransportKey, sipEndPoint.Protocol.ToString()); } }
private void UACInviteTransaction_TransactionFinalResponseReceived(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPTransaction sipTransaction, SIPResponse sipResponse) { try { // BranchId for 2xx responses needs to be a new one, non-2xx final responses use same one as original request. SIPRequest ackRequest = null; if (sipResponse.StatusCode >= 200 && sipResponse.StatusCode < 299) { if (sipResponse.Header.To != null) { m_remoteTag = sipResponse.Header.To.ToTag; } SIPURI ackURI = m_transactionRequest.URI; if (sipResponse.Header.Contact != null && sipResponse.Header.Contact.Count > 0) { ackURI = sipResponse.Header.Contact[0].ContactURI; // Don't mangle private contacts if there is a Record-Route header. If a proxy is putting private IP's in a Record-Route header that's its problem. if ((sipResponse.Header.RecordRoutes == null || sipResponse.Header.RecordRoutes.Length == 0) && IPSocket.IsPrivateAddress(ackURI.Host) && !sipResponse.Header.ProxyReceivedFrom.IsNullOrBlank()) { // Setting the Proxy-ReceivedOn header is how an upstream proxy will let an agent know it should mangle the contact. SIPEndPoint remoteUASSIPEndPoint = SIPEndPoint.ParseSIPEndPoint(sipResponse.Header.ProxyReceivedFrom); ackURI.Host = remoteUASSIPEndPoint.GetIPEndPoint().ToString(); } } // ACK for 2xx response needs to be a new transaction and gets routed based on SIP request fields. ackRequest = GetNewTransactionACKRequest(sipResponse, ackURI, LocalSIPEndPoint); base.SendRequest(ackRequest); } else { // ACK for non 2xx response is part of the INVITE transaction and gets routed to the same endpoint as the INVITE. ackRequest = GetInTransactionACKRequest(sipResponse, m_transactionRequest.URI, LocalSIPEndPoint); base.SendRequest(RemoteEndPoint, ackRequest); } if (UACInviteTransactionFinalResponseReceived != null) { UACInviteTransactionFinalResponseReceived(localSIPEndPoint, remoteEndPoint, sipTransaction, sipResponse); } if (CDR != null) { SIPEndPoint localEP = SIPEndPoint.TryParse(sipResponse.Header.ProxyReceivedOn) ?? localSIPEndPoint; SIPEndPoint remoteEP = SIPEndPoint.TryParse(sipResponse.Header.ProxyReceivedFrom) ?? remoteEndPoint; CDR.Answered(sipResponse.StatusCode, sipResponse.Status, sipResponse.ReasonPhrase, localEP, remoteEP); } } catch (Exception excp) { logger.Error("Exception UACInviteTransaction_TransactionFinalResponseReceived. " + excp.Message); } }
public override Task <SocketError> SendAsync(SIPEndPoint dstEndPoint, byte[] buffer, string connectionIDHint) { if (dstEndPoint == null) { throw new ArgumentException("dstEndPoint", "An empty destination was specified to Send in SIPUDPChannel."); } else if (buffer == null || buffer.Length == 0) { throw new ArgumentException("buffer", "The buffer must be set and non empty for Send in SIPUDPChannel."); } try { IPEndPoint dstIPEndPoint = dstEndPoint.GetIPEndPoint(); if (m_sendFailures.ContainsKey(dstEndPoint.GetIPEndPoint())) { return(Task.FromResult(SocketError.ConnectionRefused)); } else { m_udpSocket.BeginSendTo(buffer, 0, buffer.Length, SocketFlags.None, dstIPEndPoint, EndSendTo, dstEndPoint); return(Task.FromResult(SocketError.Success)); } } catch (ObjectDisposedException) // Thrown when socket is closed. Can be safely ignored. { return(Task.FromResult(SocketError.Disconnecting)); } catch (SocketException sockExcp) { return(Task.FromResult(sockExcp.SocketErrorCode)); } catch (Exception excp) { Logger.Logger.Error($"Exception SIPUDPChannel.SendAsync. ->{excp}"); return(Task.FromResult(SocketError.Fault)); } }
public SIPUDPChannel(IPEndPoint endPoint) { m_localSIPEndPoint = new SIPEndPoint(SIPProtocolsEnum.udp, endPoint); m_sipConn = new UdpClient(m_localSIPEndPoint.GetIPEndPoint()); if (m_localSIPEndPoint.Port == 0) { m_localSIPEndPoint = new SIPEndPoint(SIPProtocolsEnum.udp, (IPEndPoint)m_sipConn.Client.LocalEndPoint); } logger.LogDebug("SIPUDPChannel listener created " + m_localSIPEndPoint.GetIPEndPoint() + "."); m_mainLoop = Task.Run(Listen); }
/// <summary> /// Sends the ACK request as a new transaction. This is required for 2xx responses. /// </summary> /// <param name="content">The optional content body for the ACK request.</param> /// <param name="contentType">The optional content type.</param> private SIPRequest Get2xxAckRequest(string content, string contentType) { try { var sipResponse = m_transactionFinalResponse; if (sipResponse.Header.To != null) { m_remoteTag = sipResponse.Header.To.ToTag; } SIPURI ackURI = m_transactionRequest.URI; if (sipResponse.Header.Contact != null && sipResponse.Header.Contact.Count > 0) { ackURI = sipResponse.Header.Contact[0].ContactURI; // Don't mangle private contacts if there is a Record-Route header. If a proxy is putting private IP's in a Record-Route header that's its problem. if ((sipResponse.Header.RecordRoutes == null || sipResponse.Header.RecordRoutes.Length == 0) && IPSocket.IsPrivateAddress(ackURI.Host) && !sipResponse.Header.ProxyReceivedFrom.IsNullOrBlank()) { // Setting the Proxy-ReceivedOn header is how an upstream proxy will let an agent know it should mangle the contact. SIPEndPoint remoteUASSIPEndPoint = SIPEndPoint.ParseSIPEndPoint(sipResponse.Header.ProxyReceivedFrom); ackURI.Host = remoteUASSIPEndPoint.GetIPEndPoint().ToString(); } } // ACK for 2xx response needs to be a new transaction and gets routed based on SIP request fields. var ackRequest = GetNewTransactionAcknowledgeRequest(SIPMethodsEnum.ACK, sipResponse, ackURI); if (content.NotNullOrBlank()) { ackRequest.Body = content; ackRequest.Header.ContentLength = ackRequest.Body.Length; ackRequest.Header.ContentType = contentType; } return(ackRequest); } catch (Exception excp) { Logger.Logger.Error($"Exception Get2xxAckRequest. ->{excp.Message}"); throw excp; } }
/// <summary> /// This constructor is used by server user agents or SIP elements acting in a server user agent role. When /// acting as a server user agent the local fields are contained in the To header and the remote fields are /// in the From header. /// </summary> public SIPDialogue( UASInviteTransaction uasInviteTransaction) { Id = Guid.NewGuid(); CallId = uasInviteTransaction.TransactionRequest.Header.CallId; //RouteSet = (uasInviteTransaction.TransactionFinalResponse != null && uasInviteTransaction.TransactionFinalResponse.Header.RecordRoutes != null) ? uasInviteTransaction.TransactionFinalResponse.Header.RecordRoutes.Reversed() : null; RouteSet = (uasInviteTransaction.TransactionFinalResponse != null && uasInviteTransaction.TransactionFinalResponse.Header.RecordRoutes != null) ? uasInviteTransaction.TransactionFinalResponse.Header.RecordRoutes : null; LocalUserField = uasInviteTransaction.TransactionFinalResponse.Header.To.ToUserField; LocalTag = uasInviteTransaction.TransactionFinalResponse.Header.To.ToTag; RemoteUserField = uasInviteTransaction.TransactionFinalResponse.Header.From.FromUserField; RemoteTag = uasInviteTransaction.TransactionFinalResponse.Header.From.FromTag; CSeq = uasInviteTransaction.TransactionRequest.Header.CSeq; CDRId = uasInviteTransaction.CDR != null ? uasInviteTransaction.CDR.CDRId : Guid.Empty; ContentType = uasInviteTransaction.TransactionFinalResponse.Header.ContentType; SDP = uasInviteTransaction.TransactionFinalResponse.Body; RemoteSDP = uasInviteTransaction.TransactionRequest.Body ?? uasInviteTransaction.AckRequest.Body; Inserted = DateTime.UtcNow; Direction = SIPCallDirection.In; if (uasInviteTransaction.m_gotPrack) { CSeq++; } RemoteTarget = new SIPURI(uasInviteTransaction.TransactionRequest.URI.Scheme, uasInviteTransaction.TransactionRequest.RemoteSIPEndPoint.CopyOf()); ProxySendFrom = uasInviteTransaction.TransactionRequest.Header.ProxyReceivedOn; if (uasInviteTransaction.TransactionRequest.Header.Contact != null && uasInviteTransaction.TransactionRequest.Header.Contact.Count > 0) { RemoteTarget = uasInviteTransaction.TransactionRequest.Header.Contact[0].ContactURI.CopyOf(); if (!uasInviteTransaction.TransactionRequest.Header.ProxyReceivedFrom.IsNullOrBlank()) { // Setting the Proxy-ReceivedOn header is how an upstream proxy will let an agent know it should mangle the contact. // Don't mangle private contacts if there is a Record-Route header. If a proxy is putting private IP's in a Record-Route header that's its problem. if (RouteSet == null && IPSocket.IsPrivateAddress(RemoteTarget.Host)) { SIPEndPoint remoteUASSIPEndPoint = SIPEndPoint.ParseSIPEndPoint(uasInviteTransaction.TransactionRequest.Header.ProxyReceivedFrom); RemoteTarget.Host = remoteUASSIPEndPoint.GetIPEndPoint().ToString(); } } } }
public void Send2xxAckRequest(string content, string contentType) { var sipResponse = m_transactionFinalResponse; if (sipResponse.Header.To != null) { m_remoteTag = sipResponse.Header.To.ToTag; } SIPURI ackURI = m_transactionRequest.URI; if (sipResponse.Header.Contact != null && sipResponse.Header.Contact.Count > 0) { ackURI = sipResponse.Header.Contact[0].ContactURI; // Don't mangle private contacts if there is a Record-Route header. If a proxy is putting private IP's in a Record-Route header that's its problem. if ((sipResponse.Header.RecordRoutes == null || sipResponse.Header.RecordRoutes.Length == 0) && IPSocket.IsPrivateAddress(ackURI.Host) && !sipResponse.Header.ProxyReceivedFrom.IsNullOrBlank()) { // Setting the Proxy-ReceivedOn header is how an upstream proxy will let an agent know it should mangle the contact. SIPEndPoint remoteUASSIPEndPoint = SIPEndPoint.ParseSIPEndPoint(sipResponse.Header.ProxyReceivedFrom); ackURI.Host = remoteUASSIPEndPoint.GetIPEndPoint().ToString(); } } // ACK for 2xx response needs to be a new transaction and gets routed based on SIP request fields. var ackRequest = GetNewTransactionACKRequest(sipResponse, ackURI, LocalSIPEndPoint); if (content.NotNullOrBlank()) { ackRequest.Body = content; ackRequest.Header.ContentLength = ackRequest.Body.Length; ackRequest.Header.ContentType = contentType; } base.SendRequest(ackRequest); }
/// <summary> /// Generates the ACK or PRACK request to acknowledge a response. This method generates the ACK requests /// for INVITE 2xx and PRACK for 1xx responses. The request needs to be sent as part of a new transaction. /// Note for constructing the ACK for INVITE >= 300 responses is <seealso cref="GetInTransactionACKRequest"/>. /// </summary> /// <param name="ackResponse">The response being acknowledged.</param> /// <param name="ackMethod">The acknowledgement request method, either ACK or PRACK.</param> /// <param name="cseq">The SIP CSeq header value to set on the acknowledge request.</param> /// <param name="content">The optional content body for the ACK request.</param> /// <param name="contentType">The optional content type.</param> private SIPRequest GetAcknowledgeRequest(SIPResponse ackResponse, SIPMethodsEnum ackMethod, int cseq, string content, string contentType) { if (ackResponse.Header.To != null) { m_remoteTag = ackResponse.Header.To.ToTag; } SIPURI requestURI = m_transactionRequest.URI.CopyOf(); if (ackResponse.Header.Contact?.Count > 0) { requestURI = ackResponse.Header.Contact[0].ContactURI; // Don't mangle private contacts if there is a Record-Route header. If a proxy is putting private IP's // in a Record-Route header that's its problem. if ((ackResponse.Header.RecordRoutes == null || ackResponse.Header.RecordRoutes.Length == 0) && IPSocket.IsPrivateAddress(requestURI.Host) && !ackResponse.Header.ProxyReceivedFrom.IsNullOrBlank()) { // Setting the Proxy-ReceivedOn header is how an upstream proxy will let an agent know it should // mangle the contact. SIPEndPoint remoteUASSIPEndPoint = SIPEndPoint.ParseSIPEndPoint(ackResponse.Header.ProxyReceivedFrom); requestURI.Host = remoteUASSIPEndPoint.GetIPEndPoint().ToString(); } } // ACK for 2xx response needs to be a new transaction and gets routed based on SIP request fields. var ackRequest = GetNewTxACKRequest(ackMethod, cseq, ackResponse, requestURI); if (content.NotNullOrBlank()) { ackRequest.Body = content; ackRequest.Header.ContentLength = ackRequest.Body.Length; ackRequest.Header.ContentType = contentType; } return(ackRequest); }
public void AckRecognitionUnitTest() { SIPTransport clientTransport = null; SIPTransport serverTransport = null; try { SIPTransactionEngine clientEngine = new SIPTransactionEngine(); // Client side of the INVITE. SIPEndPoint clientEndPoint = new SIPEndPoint(SIPProtocolsEnum.udp, new IPEndPoint(IPAddress.Loopback, 12013)); clientTransport = new SIPTransport(MockSIPDNSManager.Resolve, clientEngine, new SIPUDPChannel(clientEndPoint.GetIPEndPoint()), false); SetTransportTraceEvents(clientTransport); SIPTransactionEngine serverEngine = new SIPTransactionEngine(); // Server side of the INVITE. UASInviteTransaction serverTransaction = null; SIPEndPoint serverEndPoint = new SIPEndPoint(new IPEndPoint(IPAddress.Loopback, 12014)); serverTransport = new SIPTransport(MockSIPDNSManager.Resolve, serverEngine, new SIPUDPChannel(serverEndPoint.GetIPEndPoint()), false); SetTransportTraceEvents(serverTransport); serverTransport.SIPTransportRequestReceived += (localEndPoint, remoteEndPoint, sipRequest) => { Console.WriteLine("Server Transport Request In: " + sipRequest.Method + "."); serverTransaction = serverTransport.CreateUASTransaction(sipRequest, remoteEndPoint, localEndPoint, null); SetTransactionTraceEvents(serverTransaction); serverTransaction.GotRequest(localEndPoint, remoteEndPoint, sipRequest); }; SIPURI dummyURI = SIPURI.ParseSIPURI("sip:dummy@" + serverEndPoint); SIPRequest inviteRequest = GetDummyINVITERequest(dummyURI); inviteRequest.LocalSIPEndPoint = clientTransport.GetDefaultTransportContact(SIPProtocolsEnum.udp); // Send the invite to the server side. UACInviteTransaction clientTransaction = new UACInviteTransaction(clientTransport, inviteRequest, serverEndPoint, clientEndPoint, null); SetTransactionTraceEvents(clientTransaction); clientEngine.AddTransaction(clientTransaction); clientTransaction.SendInviteRequest(serverEndPoint, inviteRequest); Thread.Sleep(500); Assert.IsTrue(clientTransaction.TransactionState == SIPTransactionStatesEnum.Completed, "Client transaction in incorrect state."); Assert.IsTrue(serverTransaction.TransactionState == SIPTransactionStatesEnum.Confirmed, "Server transaction in incorrect state."); } finally { if (clientTransport != null) { clientTransport.Shutdown(); } if (serverTransport != null) { serverTransport.Shutdown(); } } }
/// <summary> /// Checks whether there is an existing client web socket connection for a remote end point. /// </summary> /// <param name="remoteEndPoint">The remote end point to check for an existing connection.</param> /// <returns>True if there is a connection or false if not.</returns> public override bool HasConnection(SIPEndPoint remoteEndPoint) { return(m_ingressConnections.Any(x => x.Value.Context.UserEndPoint.Equals(remoteEndPoint.GetIPEndPoint()))); }
public SIPURI(SIPSchemesEnum scheme, SIPEndPoint sipEndPoint) { Scheme = scheme; Host = sipEndPoint.GetIPEndPoint().ToString(); if (sipEndPoint.Protocol != SIPProtocolsEnum.udp) { Parameters.Set(m_uriParamTransportKey, sipEndPoint.Protocol.ToString()); } }
/// <summary> /// Used to create a SIP response when it was not possible to parse the incoming SIP request. /// </summary> public SIPResponse GetResponse(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPResponseStatusCodesEnum responseCode, string reasonPhrase) { try { if (localSIPEndPoint == null) { localSIPEndPoint = GetDefaultSIPEndPoint(); } SIPResponse response = new SIPResponse(responseCode, reasonPhrase, localSIPEndPoint); SIPSchemesEnum sipScheme = (localSIPEndPoint.Protocol == SIPProtocolsEnum.tls) ? SIPSchemesEnum.sips : SIPSchemesEnum.sip; SIPFromHeader from = new SIPFromHeader(null, new SIPURI(sipScheme, localSIPEndPoint), null); SIPToHeader to = new SIPToHeader(null, new SIPURI(sipScheme, localSIPEndPoint), null); int cSeq = 1; string callId = CallProperties.CreateNewCallId(); response.Header = new SIPHeader(from, to, cSeq, callId); response.Header.CSeqMethod = SIPMethodsEnum.NONE; response.Header.Vias.PushViaHeader(new SIPViaHeader(new SIPEndPoint(localSIPEndPoint.Protocol, remoteEndPoint.GetIPEndPoint()), CallProperties.CreateBranchId())); response.Header.MaxForwards = Int32.MinValue; response.Header.Allow = ALLOWED_SIP_METHODS; return response; } catch (Exception excp) { logger.Error("Exception SIPTransport GetResponse. " + excp.Message); throw; } }
/// <summary> /// Allows raw bytes to be sent from one of the SIPTransport sockets. This should not be used for SIP payloads and instead is /// provided to allow other types of payloads to be multi-plexed on the SIP socket. Examples are sending NAT keep alives and /// STUN responses where it's useful to use the same socket as the SIP packets. /// </summary> public void SendRaw(SIPEndPoint localSIPEndPoint, SIPEndPoint destinationEndPoint, byte[] buffer) { if (destinationEndPoint != null && destinationEndPoint.Address.Equals(BlackholeAddress)) { // Ignore packet, it's destined for the blackhole. return; } if (m_sipChannels.Count == 0) { throw new ApplicationException("No channels are configured in the SIP transport layer. The data could not be sent."); } SIPChannel sendSIPChannel = FindSIPChannel(localSIPEndPoint); if (sendSIPChannel != null) { sendSIPChannel.Send(destinationEndPoint.GetIPEndPoint(), buffer); } else { logger.Warn("No SIPChannel could be found for " + localSIPEndPoint + " in SIPTransport.SendRaw, sending to " + destinationEndPoint.ToString() + "."); //logger.Warn(Encoding.UTF8.GetString(buffer)); } }
private void SendResponse(SIPChannel sipChannel, SIPEndPoint dstEndPoint, SIPResponse sipResponse) { try { if (dstEndPoint != null && dstEndPoint.Address.Equals(BlackholeAddress)) { // Ignore packet, it's destined for the blackhole. return; } if (m_sipChannels.Count == 0) { throw new ApplicationException("No channels are configured in the SIP transport layer. The response could not be sent."); } sipResponse.Header.ContentLength = (sipResponse.Body.NotNullOrBlank()) ? Encoding.UTF8.GetByteCount(sipResponse.Body) : 0; sipChannel.Send(dstEndPoint.GetIPEndPoint(), Encoding.UTF8.GetBytes(sipResponse.ToString())); if (SIPRequestOutTraceEvent != null) { FireSIPResponseOutTraceEvent(sipChannel.SIPChannelEndPoint, dstEndPoint, sipResponse); } } catch (ApplicationException appExcp) { logger.Warn("ApplicationException SIPTransport SendResponse. " + appExcp.Message); } }
private void SIPMessageReceived(SIPChannel sipChannel, SIPEndPoint remoteEndPoint, byte[] buffer) { string rawSIPMessage = null; try { if (buffer != null && buffer.Length > 0) { if ((buffer[0] == 0x0 || buffer[0] == 0x1) && buffer.Length >= 20) { // Treat any messages that cannot be SIP as STUN requests. if (STUNRequestReceived != null) { STUNRequestReceived(sipChannel.SIPChannelEndPoint.GetIPEndPoint(), remoteEndPoint.GetIPEndPoint(), buffer, buffer.Length); #if !SILVERLIGHT if (PerformanceMonitorPrefix != null) { SIPSorceryPerformanceMonitor.IncrementCounter(PerformanceMonitorPrefix + SIPSorceryPerformanceMonitor.SIP_TRANSPORT_STUN_REQUESTS_PER_SECOND_SUFFIX); } #endif } } else { // Treat all messages that don't match STUN requests as SIP. if (buffer.Length > SIPConstants.SIP_MAXIMUM_RECEIVE_LENGTH) { string rawErrorMessage = Encoding.UTF8.GetString(buffer, 0, 1024) + "\r\n..truncated"; FireSIPBadRequestInTraceEvent(sipChannel.SIPChannelEndPoint, remoteEndPoint, "SIP message too large, " + buffer.Length + " bytes, maximum allowed is " + SIPConstants.SIP_MAXIMUM_RECEIVE_LENGTH + " bytes.", SIPValidationFieldsEnum.Request, rawErrorMessage); SIPResponse tooLargeResponse = GetResponse(sipChannel.SIPChannelEndPoint, remoteEndPoint, SIPResponseStatusCodesEnum.MessageTooLarge, null); SendResponse(tooLargeResponse); #if !SILVERLIGHT if (PerformanceMonitorPrefix != null) { SIPSorceryPerformanceMonitor.IncrementCounter(PerformanceMonitorPrefix + SIPSorceryPerformanceMonitor.SIP_TRANSPORT_SIP_BAD_MESSAGES_PER_SECOND_SUFFIX); } #endif } else { rawSIPMessage = Encoding.UTF8.GetString(buffer, 0, buffer.Length); if (rawSIPMessage.IsNullOrBlank()) { // An emptry transmission has been received. More than likely this is a NAT keep alive and can be disregarded. //FireSIPBadRequestInTraceEvent(sipChannel.SIPChannelEndPoint, remoteEndPoint, "No printable characters, length " + buffer.Length + " bytes.", SIPValidationFieldsEnum.Unknown, null); #if !SILVERLIGHT if (PerformanceMonitorPrefix != null) { // SIPSorceryPerformanceMonitor.IncrementCounter(PerformanceMonitorPrefix + SIPSorceryPerformanceMonitor.SIP_TRANSPORT_SIP_BAD_MESSAGES_PER_SECOND_SUFFIX); } #endif return; } else if (!rawSIPMessage.Contains("SIP")) { FireSIPBadRequestInTraceEvent(sipChannel.SIPChannelEndPoint, remoteEndPoint, "Missing SIP string.", SIPValidationFieldsEnum.NoSIPString, rawSIPMessage); #if !SILVERLIGHT if (PerformanceMonitorPrefix != null) { SIPSorceryPerformanceMonitor.IncrementCounter(PerformanceMonitorPrefix + SIPSorceryPerformanceMonitor.SIP_TRANSPORT_SIP_BAD_MESSAGES_PER_SECOND_SUFFIX); } #endif return; } SIPMessage sipMessage = SIPMessage.ParseSIPMessage(rawSIPMessage, sipChannel.SIPChannelEndPoint, remoteEndPoint); if (sipMessage != null) { if (sipMessage.SIPMessageType == SIPMessageTypesEnum.Response) { #region SIP Response. try { #if !SILVERLIGHT if (PerformanceMonitorPrefix != null) { SIPSorceryPerformanceMonitor.IncrementCounter(PerformanceMonitorPrefix + SIPSorceryPerformanceMonitor.SIP_TRANSPORT_SIP_RESPONSES_PER_SECOND_SUFFIX); } #endif SIPResponse sipResponse = SIPResponse.ParseSIPResponse(sipMessage); if (SIPResponseInTraceEvent != null) { FireSIPResponseInTraceEvent(sipChannel.SIPChannelEndPoint, remoteEndPoint, sipResponse); } if (m_transactionEngine != null && m_transactionEngine.Exists(sipResponse)) { SIPTransaction transaction = m_transactionEngine.GetTransaction(sipResponse); if (transaction.TransactionState != SIPTransactionStatesEnum.Completed) { transaction.DeliveryPending = false; if (m_reliableTransmissions.ContainsKey(transaction.TransactionId)) { lock (m_reliableTransmissions) { m_reliableTransmissions.Remove(transaction.TransactionId); } } } transaction.GotResponse(sipChannel.SIPChannelEndPoint, remoteEndPoint, sipResponse); } else if (SIPTransportResponseReceived != null) { SIPTransportResponseReceived(sipChannel.SIPChannelEndPoint, remoteEndPoint, sipResponse); } } catch (SIPValidationException sipValidationException) { //logger.Warn("Invalid SIP response from " + sipMessage.ReceivedFrom + ", " + sipResponse.ValidationError + " , ignoring."); //logger.Warn(sipMessage.RawMessage); FireSIPBadResponseInTraceEvent(sipChannel.SIPChannelEndPoint, remoteEndPoint, sipMessage.RawMessage, sipValidationException.SIPErrorField, sipMessage.RawMessage); } #endregion } else { #region SIP Request. #if !SILVERLIGHT if (PerformanceMonitorPrefix != null) { SIPSorceryPerformanceMonitor.IncrementCounter(PerformanceMonitorPrefix + SIPSorceryPerformanceMonitor.SIP_TRANSPORT_SIP_REQUESTS_PER_SECOND_SUFFIX); } #endif try { SIPRequest sipRequest = SIPRequest.ParseSIPRequest(sipMessage); SIPValidationFieldsEnum sipRequestErrorField = SIPValidationFieldsEnum.Unknown; string sipRequestValidationError = null; if (!sipRequest.IsValid(out sipRequestErrorField, out sipRequestValidationError)) { throw new SIPValidationException(sipRequestErrorField, sipRequestValidationError); } if (SIPRequestInTraceEvent != null) { FireSIPRequestInTraceEvent(sipChannel.SIPChannelEndPoint, remoteEndPoint, sipRequest); } // Stateful cores will create transactions once they get the request and the transport layer will use those transactions. // Stateless cores will not be affected by this step as the transaction layer will always return false. SIPTransaction requestTransaction = (m_transactionEngine != null) ? m_transactionEngine.GetTransaction(sipRequest) : null; if (requestTransaction != null) { if (requestTransaction.TransactionState == SIPTransactionStatesEnum.Completed && sipRequest.Method != SIPMethodsEnum.ACK) { logger.Warn("Resending final response for " + sipRequest.Method + ", " + sipRequest.URI.ToString() + ", cseq=" + sipRequest.Header.CSeq + "."); requestTransaction.RetransmitFinalResponse(); } else if (sipRequest.Method == SIPMethodsEnum.ACK) { //logger.Debug("ACK received for " + requestTransaction.TransactionRequest.URI.ToString() + "."); if (requestTransaction.TransactionState == SIPTransactionStatesEnum.Completed) { //logger.Debug("ACK received for INVITE, setting state to Confirmed, " + sipRequest.URI.ToString() + " from " + sipRequest.Header.From.FromURI.User + " " + remoteEndPoint + "."); //requestTransaction.UpdateTransactionState(SIPTransactionStatesEnum.Confirmed); requestTransaction.ACKReceived(sipChannel.SIPChannelEndPoint, remoteEndPoint, sipRequest); } else if (requestTransaction.TransactionState == SIPTransactionStatesEnum.Confirmed) { // ACK retransmit, ignore as a previous ACK was received and the transaction has already been confirmed. } else { //logger.Warn("ACK recieved from " + remoteEndPoint.ToString() + " on " + requestTransaction.TransactionState + " transaction, ignoring."); FireSIPBadRequestInTraceEvent(sipChannel.SIPChannelEndPoint, remoteEndPoint, "ACK recieved on " + requestTransaction.TransactionState + " transaction, ignoring.", SIPValidationFieldsEnum.Request, null); } } else { logger.Warn("Transaction already exists, ignoring duplicate request, " + sipRequest.Method + " " + sipRequest.URI.ToString() + "."); //FireSIPBadRequestInTraceEvent(sipChannel.SIPChannelEndPoint, remoteEndPoint, "Transaction already exists, ignoring duplicate request, " + sipRequest.Method + " " + sipRequest.URI.ToString() + " from " + remoteEndPoint + ".", SIPValidationFieldsEnum.Request); } } else if (SIPTransportRequestReceived != null) { // This is a new SIP request and if the validity checks are passed it will be handed off to all subscribed new request listeners. #region Check for invalid SIP requests. if (sipRequest.Header.MaxForwards == 0 && sipRequest.Method != SIPMethodsEnum.OPTIONS) { // Check the MaxForwards value, if equal to 0 the request must be discarded. If MaxForwards is -1 it indicates the // header was not present in the request and that the MaxForwards check should not be undertaken. //logger.Warn("SIPTransport responding with TooManyHops due to 0 MaxForwards."); FireSIPBadRequestInTraceEvent(sipChannel.SIPChannelEndPoint, remoteEndPoint, "Zero MaxForwards on " + sipRequest.Method + " " + sipRequest.URI.ToString() + " from " + sipRequest.Header.From.FromURI.User + " " + remoteEndPoint.ToString(), SIPValidationFieldsEnum.Request, sipRequest.ToString()); SIPResponse tooManyHops = GetResponse(sipRequest, SIPResponseStatusCodesEnum.TooManyHops, null); SendResponse(sipChannel, tooManyHops); return; } /*else if (sipRequest.IsLoop(sipChannel.SIPChannelEndPoint.SocketEndPoint.Address.ToString(), sipChannel.SIPChannelEndPoint.SocketEndPoint.Port, sipRequest.CreateBranchId())) { //logger.Warn("SIPTransport Dropping looped request."); FireSIPBadRequestInTraceEvent(sipChannel.SIPChannelEndPoint, remoteEndPoint, "Dropping looped request, " + sipRequest.Method + " " + sipRequest.URI.ToString() + " from " + sipRequest.Header.From.FromURI.User + " " + IPSocket.GetSocketString(remoteEndPoint), SIPValidationFieldsEnum.Request); SIPResponse loopResponse = GetResponse(sipRequest, SIPResponseStatusCodesEnum.LoopDetected, null); SendResponse(loopResponse); return; }*/ #endregion #region Route pre-processing. if (sipRequest.Header.Routes.Length > 0) { PreProcessRouteInfo(sipRequest); } #endregion // Request has passed validity checks, adjust the client Via header to reflect the socket the request was received on. //SIPViaHeader originalTopViaHeader = sipRequest.Header.Via.TopViaHeader; sipRequest.Header.Vias.UpateTopViaHeader(remoteEndPoint.GetIPEndPoint()); // Stateful cores should create a transaction once they receive this event, stateless cores should not. SIPTransportRequestReceived(sipChannel.SIPChannelEndPoint, remoteEndPoint, sipRequest); } } catch (SIPValidationException sipRequestExcp) { FireSIPBadRequestInTraceEvent(sipChannel.SIPChannelEndPoint, remoteEndPoint, sipRequestExcp.Message, sipRequestExcp.SIPErrorField, sipMessage.RawMessage); SIPResponse errorResponse = GetResponse(sipChannel.SIPChannelEndPoint, remoteEndPoint, sipRequestExcp.SIPResponseErrorCode, sipRequestExcp.Message); SendResponse(sipChannel, errorResponse); } #endregion } } else { FireSIPBadRequestInTraceEvent(sipChannel.SIPChannelEndPoint, remoteEndPoint, "Not parseable as SIP message.", SIPValidationFieldsEnum.Unknown, rawSIPMessage); #if !SILVERLIGHT if (PerformanceMonitorPrefix != null) { SIPSorceryPerformanceMonitor.IncrementCounter(PerformanceMonitorPrefix + SIPSorceryPerformanceMonitor.SIP_TRANSPORT_SIP_BAD_MESSAGES_PER_SECOND_SUFFIX); } #endif } } } } } catch (Exception excp) { FireSIPBadRequestInTraceEvent(sipChannel.SIPChannelEndPoint, remoteEndPoint, "Exception SIPTransport. " + excp.Message, SIPValidationFieldsEnum.Unknown, rawSIPMessage); #if !SILVERLIGHT if (PerformanceMonitorPrefix != null) { SIPSorceryPerformanceMonitor.IncrementCounter(PerformanceMonitorPrefix + SIPSorceryPerformanceMonitor.SIP_TRANSPORT_SIP_BAD_MESSAGES_PER_SECOND_SUFFIX); } #endif } }
private void EndReceiveFrom(IAsyncResult ar) { EndPoint remoteEP = (ListeningIPAddress.AddressFamily == AddressFamily.InterNetwork) ? new IPEndPoint(IPAddress.Any, 0) : new IPEndPoint(IPAddress.IPv6Any, 0); try { if (!Closed) { SocketFlags flags = SocketFlags.None; int bytesRead = m_udpSocket.EndReceiveFrom(ar, ref remoteEP); if (flags == SocketFlags.Truncated) { logger.LogWarning($"The message was too large to fit into the specified buffer and was truncated."); } if (bytesRead > 0) { // In addition to the note in the RTPChannel class about IPPacketInformation some versions of the mono runtime // on Android are unable to use Begin/EndReceiveMessageFrom. // See https://github.com/sipsorcery/sipsorcery/issues/302. // Those specific Begin/End calls were being used to get the packet information and identify which local // IP address a receive occurred on. Upon investigation it does not seem that the local IP address is // required on incoming SIP packets. The outgoing socket was previously chosen based on this address but // subsequent to https://github.com/sipsorcery/sipsorcery/issues/97 the mechanism has changed. // The calls have been changed to Begin/EndReceiveFrom in order to support Android. The consequence is the localEndPoint // parameter for the SIPMessageReceived will have an IP address of 0.0.0.0 or [::0] if wildcard addresses are in use. SIPEndPoint remoteEndPoint = new SIPEndPoint(SIPProtocolsEnum.udp, remoteEP as IPEndPoint, ID, null); //SIPEndPoint localEndPoint = new SIPEndPoint(SIPProtocolsEnum.udp, new IPEndPoint(packetInfo.Address, Port), ID, null); SIPEndPoint localEndPoint = new SIPEndPoint(SIPProtocolsEnum.udp, new IPEndPoint(ListeningIPAddress, Port), ID, null); byte[] sipMsgBuffer = new byte[bytesRead]; Buffer.BlockCopy(m_recvBuffer, 0, sipMsgBuffer, 0, bytesRead); SIPMessageReceived?.Invoke(this, localEndPoint, remoteEndPoint, sipMsgBuffer); } } } catch (SocketException sockExcp) { if (remoteEP != null) { // Note the SIPEndPoint is being used to take care of any IPv4 mapped to IPv6 addresses. SIPEndPoint remSIPEndPoint = new SIPEndPoint(SIPProtocolsEnum.udp, remoteEP as IPEndPoint); // This exception can occur as the result of a Send operation. It's caused by an ICMP packet from a remote host // rejecting an incoming UDP packet. If that happens we want to stop further sends to the socket for a short period. logger.LogWarning(sockExcp, $"SocketException SIPUDPChannel EndReceiveFrom from {remSIPEndPoint} ({sockExcp.ErrorCode}). {sockExcp.Message}"); m_sendFailures.TryAdd(remSIPEndPoint.GetIPEndPoint(), DateTime.Now); } else { logger.LogError($"SocketException SIPUDPChannel EndReceiveFrom. {sockExcp}"); } } catch (ObjectDisposedException) // Thrown when socket is closed. Can be safely ignored. { } catch (Exception excp) { logger.LogError($"Exception SIPUDPChannel EndReceiveFrom. {excp}"); } finally { if (!Closed) { Receive(); } } }
private void SendRequest(SIPChannel sipChannel, SIPEndPoint dstEndPoint, SIPRequest sipRequest) { try { if (dstEndPoint != null && dstEndPoint.Address.Equals(BlackholeAddress)) { // Ignore packet, it's destined for the blackhole. return; } if (m_sipChannels.Count == 0) { throw new ApplicationException("No channels are configured in the SIP transport layer. The request could not be sent."); } sipRequest.Header.ContentLength = (sipRequest.Body.NotNullOrBlank()) ? Encoding.UTF8.GetByteCount(sipRequest.Body) : 0; if (sipChannel.IsTLS) { sipChannel.Send(dstEndPoint.GetIPEndPoint(), Encoding.UTF8.GetBytes(sipRequest.ToString()), sipRequest.URI.Host); } else { sipChannel.Send(dstEndPoint.GetIPEndPoint(), Encoding.UTF8.GetBytes(sipRequest.ToString())); } if (SIPRequestOutTraceEvent != null) { FireSIPRequestOutTraceEvent(sipChannel.SIPChannelEndPoint, dstEndPoint, sipRequest); } } catch (ApplicationException appExcp) { logger.Warn("ApplicationException SIPTransport SendRequest. " + appExcp.Message); SIPResponse errorResponse = GetResponse(sipRequest, SIPResponseStatusCodesEnum.InternalServerError, appExcp.Message); // Remove any Via headers, other than the last one, that are for sockets hosted by this process. while (errorResponse.Header.Vias.Length > 0) { if (IsLocalSIPEndPoint(SIPEndPoint.ParseSIPEndPoint(errorResponse.Header.Vias.TopViaHeader.ReceivedFromAddress))) { errorResponse.Header.Vias.PopTopViaHeader(); } else { break; } } if (errorResponse.Header.Vias.Length == 0) { logger.Warn("Could not send error response for " + appExcp.Message + " as no non-local Via headers were available."); } else { SendResponse(errorResponse); } } }
/// <summary> /// Checks whether there is an existing connection for a remote end point. Existing connections include /// connections that have been accepted by this channel's listener and connections that have been initiated /// due to sends from this channel. /// </summary> /// <param name="remoteEndPoint">The remote end point to check for an existing connection.</param> /// <returns>True if there is a connection or false if not.</returns> public override bool HasConnection(SIPEndPoint remoteEndPoint) { return(m_connections.Any(x => x.Value.RemoteEndPoint.Equals(remoteEndPoint.GetIPEndPoint()))); }
/// <summary> /// Used to create a SIP response for a request when it was not possible to parse the incoming SIP request. /// The response generated by this method may or may not make it back to the requester. Because the SIP /// request could not be parsed there are no Via headers available and without those the return network /// path is missing. Instead a new Via header is generated that may get through if the requester is only /// one SIP hop away. /// </summary> /// <param name="localSIPEndPoint">The local SIP end point the request was received on.</param> /// <param name="remoteSIPEndPoint">The remote SIP end point the request was received on.</param> /// <param name="responseCode">The response code to set on the response.</param> /// <param name="reasonPhrase">Optional reason phrase to set on the response (keep short).</param> public static SIPResponse GetResponse(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteSIPEndPoint, SIPResponseStatusCodesEnum responseCode, string reasonPhrase) { try { SIPResponse response = new SIPResponse(responseCode, reasonPhrase); response.SetSendFromHints(localSIPEndPoint); SIPSchemesEnum sipScheme = (localSIPEndPoint.Protocol == SIPProtocolsEnum.tls) ? SIPSchemesEnum.sips : SIPSchemesEnum.sip; SIPFromHeader from = new SIPFromHeader(null, new SIPURI(sipScheme, localSIPEndPoint), null); SIPToHeader to = new SIPToHeader(null, new SIPURI(sipScheme, localSIPEndPoint), null); int cSeq = 1; string callId = CallProperties.CreateNewCallId(); response.Header = new SIPHeader(from, to, cSeq, callId); response.Header.CSeqMethod = SIPMethodsEnum.NONE; response.Header.Vias.PushViaHeader(new SIPViaHeader(new SIPEndPoint(localSIPEndPoint.Protocol, remoteSIPEndPoint.GetIPEndPoint()), CallProperties.CreateBranchId())); response.Header.MaxForwards = Int32.MinValue; response.Header.Allow = m_allowedSIPMethods; return(response); } catch (Exception excp) { logger.LogError("Exception SIPResponse.GetResponse. " + excp.Message); throw; } }
/// <summary> /// Attempts a send to a remote web socket server. If there is an existing connection it will be used /// otherwise an attempt will made to establish a new one. /// </summary> /// <param name="serverEndPoint">The remote web socket server URI to send to.</param> /// <param name="buffer">The data buffer to send.</param> /// <returns>A success value or an error for failure.</returns> private async Task <SocketError> SendAsync(SIPEndPoint serverEndPoint, byte[] buffer) { try { string uriPrefix = (serverEndPoint.Protocol == SIPProtocolsEnum.wss) ? WEB_SOCKET_SECURE_URI_PREFIX : WEB_SOCKET_URI_PREFIX; var serverUri = new Uri($"{uriPrefix}{serverEndPoint.GetIPEndPoint()}"); string connectionID = GetConnectionID(serverUri); serverEndPoint.ChannelID = this.ID; serverEndPoint.ConnectionID = connectionID; if (m_egressConnections.TryGetValue(connectionID, out var conn)) { Logger.Logger.Debug( $"Sending {buffer.Length} bytes on client web socket connection to {conn.ServerUri}."); ArraySegment <byte> segmentBuffer = new ArraySegment <byte>(buffer); await conn.Client.SendAsync(segmentBuffer, WebSocketMessageType.Text, true, m_cts.Token) .ConfigureAwait(false); return(SocketError.Success); } else { // Attempt a new connection. ClientWebSocket clientWebSocket = new ClientWebSocket(); await clientWebSocket.ConnectAsync(serverUri, m_cts.Token).ConfigureAwait(false); Logger.Logger.Debug($"Successfully connected web socket client to {serverUri}."); ArraySegment <byte> segmentBuffer = new ArraySegment <byte>(buffer); await clientWebSocket.SendAsync(segmentBuffer, WebSocketMessageType.Text, true, m_cts.Token) .ConfigureAwait(false); var recvBuffer = new ArraySegment <byte>(new byte[2 * SIPStreamConnection.MaxSIPTCPMessageSize]); Task <WebSocketReceiveResult> receiveTask = clientWebSocket.ReceiveAsync(recvBuffer, m_cts.Token); // There's currently no way to get the socket IP end point used by the client web socket to establish // the connection. Instead provide a dummy local end point that has as much of the information as we can. IPEndPoint localEndPoint = new IPEndPoint( (serverEndPoint.Address.AddressFamily == AddressFamily.InterNetwork) ? IPAddress.Any : IPAddress.IPv6Any, 0); SIPEndPoint localSIPEndPoint = new SIPEndPoint(serverEndPoint.Protocol, localEndPoint, this.ID, connectionID); ClientWebSocketConnection newConn = new ClientWebSocketConnection { LocalEndPoint = localSIPEndPoint, ServerUri = serverUri, RemoteEndPoint = serverEndPoint, ConnectionID = connectionID, ReceiveBuffer = recvBuffer, ReceiveTask = receiveTask, Client = clientWebSocket }; if (!m_egressConnections.TryAdd(connectionID, newConn)) { Logger.Logger.Error( $"Could not add web socket client connected to {serverUri} to channel collection, closing."); await Close(connectionID, clientWebSocket).ConfigureAwait(false); } else { if (!m_isReceiveTaskRunning) { m_isReceiveTaskRunning = true; _ = Task.Factory.StartNew(MonitorReceiveTasks, TaskCreationOptions.LongRunning); } } return(SocketError.Success); } } catch (SocketException sockExcp) { return(sockExcp.SocketErrorCode); } }
/// <summary> /// Attempts to send data to the remote end point over a reliable connection. If an existing /// connection exists it will be used otherwise an attempt will be made to establish a new connection. /// </summary> /// <param name="dstSIPEndPoint">The remote SIP end point to send the reliable data to.</param> /// <param name="buffer">The data to send.</param> /// <param name="serverCertificateName">Optional. Only relevant for SSL streams. The common name /// that is expected for the remote SSL server.</param> /// <param name="canInitiateConnection">Indicates whether this send should initiate a connection if needed. /// The typical case is SIP requests can initiate new connections but responses should not. Responses should /// only be sent on the same TCP or TLS connection that the original request was received on.</param> /// <param name="connectionIDHint">Optional. The ID of the specific TCP connection to try and the send the message on.</param> /// <returns>If no errors SocketError.Success otherwise an error value.</returns> public override Task <SocketError> SendSecureAsync( SIPEndPoint dstSIPEndPoint, byte[] buffer, string serverCertificateName, bool canInitiateConnection, string connectionIDHint) { try { if (dstSIPEndPoint == null) { throw new ArgumentException(nameof(dstSIPEndPoint), "An empty destination was specified to Send in SIPTCPChannel."); } if (buffer == null || buffer.Length == 0) { throw new ApplicationException("An empty buffer was specified to Send in SIPTCPChannel."); } else if (!DisableLocalTCPSocketsCheck && NetServices.LocalIPAddresses.Contains(dstSIPEndPoint.Address) && Port == dstSIPEndPoint.Port) { logger.LogWarning($"SIP {ProtDescr} Channel blocked Send to {dstSIPEndPoint} as it was identified as a locally hosted {ProtDescr} socket.\r\n" + SIPConstants.DEFAULT_ENCODING.GetString(buffer)); throw new ApplicationException($"A Send call was blocked in SIP {ProtDescr} Channel due to the destination being another local TCP socket."); } else { IPEndPoint dstEndPoint = dstSIPEndPoint.GetIPEndPoint(m_isDualMode); // Lookup a client socket that is connected to the destination. If it does not exist attempt to connect a new one. SIPStreamConnection sipStreamConn = null; if (connectionIDHint != null) { m_connections.TryGetValue(connectionIDHint, out sipStreamConn); } if (sipStreamConn == null && HasConnection(dstSIPEndPoint)) { sipStreamConn = m_connections.Where(x => x.Value.RemoteSIPEndPoint.IsSocketEqual(dstSIPEndPoint)).First().Value; } if (sipStreamConn != null) { SendOnConnected(sipStreamConn, buffer); return(Task.FromResult(SocketError.Success)); } else if (canInitiateConnection) { return(ConnectClientAsync(dstEndPoint, buffer, serverCertificateName)); } else { logger.LogWarning($"SIP {ProtDescr} Channel did not have an existing connection for send to {dstSIPEndPoint} and requested not to initiate a connection."); return(Task.FromResult(SocketError.NotConnected)); } } } catch (SocketException sockExcp) { return(Task.FromResult(sockExcp.SocketErrorCode)); } catch (ApplicationException) { throw; } catch (Exception excp) { logger.LogError($"Exception SIPTCPChannel Send (sendto=>{dstSIPEndPoint}). {excp}"); throw; } }