/// <summary> /// Processes SIP data (request or response). /// </summary> /// <param name="socket">Socket what accepted specified SIP message.</param> /// <param name="data">SIP message or may be junk data too.</param> /// <param name="remoteEndPoint">Remote IP end point what sent SIP message.</param> /// <param name="transport">SIP transport what received message.</param> private void Process(string transport,SocketEx socket,byte[] data,IPEndPoint remoteEndPoint) { try{ // Log m_pSipStack.Logger.AddRead(data.Length,"Received (" + data.Length + " bytes): " + socket.LocalEndPoint.ToString() + " <- " + remoteEndPoint.ToString() + "\r\n" + System.Text.Encoding.UTF8.GetString(data) + "\r\n"); // Bad request or response. if(data.Length < 10){ return; } // TODO: Check that advertised transport matches actual received transport. // Dedect if sip request or reponse. // Response if(System.Text.Encoding.UTF8.GetString(data,0,10).ToUpper().StartsWith("SIP")){ try{ SIP_Response response = SIP_Response.Parse(data); response.Validate(); ProcessResponse(response); } catch(Exception x){ // We have bad response, just skip it. // Log if(m_pSipStack.Logger != null){ m_pSipStack.Logger.AddDebug("Skipping message, parse error: " + x.Message); } } } // Request else{ SIP_Request request = null; try{ request = SIP_Request.Parse(data); try{ request.Validate(); } catch(Exception x){ // Log if(m_pSipStack.Logger != null){ m_pSipStack.Logger.AddDebug("Invalid request: " + x.Message); } // Bad request, send error to request maker. SendResponse(socket,request.CreateResponse(SIP_ResponseCodes.x400_Bad_Request + ". " + x.Message)); return; } } catch(Exception x){ // Log if(m_pSipStack.Logger != null){ m_pSipStack.Logger.AddDebug("Invalid request: " + x.Message); } // We have bad request, try to send bad request error to request maker. SIP_Response badRequestResponse = new SIP_Response(); badRequestResponse.StatusCode_ReasonPhrase = SIP_ResponseCodes.x400_Bad_Request; socket.SendTo(badRequestResponse.ToByteData(),remoteEndPoint); return; } SIP_ValidateRequestEventArgs eArgs = m_pSipStack.OnValidateRequest(request,remoteEndPoint); // Request validated, allow transport layer to handle it. if(eArgs.ResponseCode == null){ ProcessRequest(request,socket,remoteEndPoint); } // Request rejected, return response. else{ SendResponse(socket,request.CreateResponse(eArgs.ResponseCode)); } } } catch(SocketException s){ // Skip all socket errors here string dummy = s.Message; } catch(Exception x){ m_pSipStack.OnError(x); } }
/// <exception cref="System.IO.IOException"></exception> internal virtual void Send(NameServicePacket request, NameServicePacket response, int timeout) { //Log.Out("NameSerciceClient.Send - Start"); int nid = 0; int max = NbtAddress.Nbns.Length; if (max == 0) { max = 1; } lock (response) { this._isActive = true; while (max-- > 0) { try { lock (_lock) { request.NameTrnId = GetNextNameTrnId(); nid = request.NameTrnId; response.Received = false; _responseTable.Put(nid, response); //Log.Out($"NameSerciceClient.Send - timeout = {timeout}"); EnsureOpen(timeout + 1000); int requestLenght = request.WriteWireFormat(_sndBuf, 0); byte[] msg = new byte[requestLenght]; Array.Copy(_sndBuf, msg, requestLenght); _socketSender.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, request.IsBroadcast ? 1 : 0); _socketSender.SendTo(msg, new IPEndPoint(request.Addr, _lport)); //Log.Out("NameSerciceClient.Send - Sended"); if (_log.Level > 3) { _log.WriteLine(request); Hexdump.ToHexdump(_log, _sndBuf, 0, requestLenght); } } if (_waitResponse) { long start = Runtime.CurrentTimeMillis(); var isRecieved = false; var startTime = DateTime.Now; while (timeout > 0) { Runtime.Wait(response, timeout); if (response.Received && request.QuestionType == response.RecordType) { //return; isRecieved = true; break; } response.Received = false; timeout -= (int)(Runtime.CurrentTimeMillis() - start); //if (timeout <= 0) //{ // Log.Out($"NameSerciceClient.Send Timeout! - {(DateTime.Now - startTime).TotalMilliseconds} msec"); //} } if (isRecieved) { break; } } } catch (Exception ie) { if (_waitResponse) { _responseTable.Remove(nid); } //Log.Out("NameSerciceClient.Send - IOException"); throw new IOException(ie.Message); } finally { if (_waitResponse) { _responseTable.Remove(nid); } } if (_waitResponse) { lock (_lock) { if (NbtAddress.IsWins(request.Addr) == false) { break; } if (request.Addr == NbtAddress.GetWinsAddress()) { NbtAddress.SwitchWins(); } request.Addr = NbtAddress.GetWinsAddress(); } } } this._isActive = false; //Log.Out("NameSerciceClient.Send - Normaly Ended."); } }
/// <summary> /// Sends response to request maker. /// </summary> /// <param name="socket">Socket which to use to send response.</param> /// <param name="remoteEndPoint">Remote end point where to send response. /// If this value is null, Via header is used to get remote end point. /// </param> /// <param name="response">Response to send.</param> public void SendResponse(SocketEx socket,IPEndPoint remoteEndPoint,SIP_Response response) { /* RFC 3581 4. Server Behavior. When a server attempts to send a response, it examines the topmost Via header field value of that response. If the "sent-protocol" component indicates an unreliable unicast transport protocol, such as UDP, and there is no "maddr" parameter, but there is both a "received" parameter and an "rport" parameter, the response MUST be sent to the IP address listed in the "received" parameter, and the port in the "rport" parameter. The response MUST be sent from the same address and port that the corresponding request was received on in order to traverse symmetric NATs. */ /* RFC 3261 18.2.2 Sending Responses. The server transport uses the value of the top Via header field in order to determine where to send a response. It MUST follow the following process: o If the "sent-protocol" is a reliable transport protocol such as TCP or SCTP, or TLS over those, the response MUST be sent using the existing connection to the source of the original request that created the transaction, if that connection is still open. This requires the server transport to maintain an association between server transactions and transport connections. If that connection is no longer open, the server SHOULD open a connection to the IP address in the "received" parameter, if present, using the port in the "sent-by" value, or the default port for that transport, if no port is specified. If that connection attempt fails, the server SHOULD use the procedures in [4] for servers in order to determine the IP address and port to open the connection and send the response to. o Otherwise, if the Via header field value contains a "maddr" parameter, the response MUST be forwarded to the address listed there, using the port indicated in "sent-by", or port 5060 if none is present. If the address is a multicast address, the response SHOULD be sent using the TTL indicated in the "ttl" parameter, or with a TTL of 1 if that parameter is not present. o Otherwise (for unreliable unicast transports), if the top Via has a "received" parameter, the response MUST be sent to the address in the "received" parameter, using the port indicated in the "sent-by" value, or using port 5060 if none is specified explicitly. If this fails, for example, elicits an ICMP "port unreachable" response, the procedures of Section 5 of [4] SHOULD be used to determine where to send the response. o Otherwise, if it is not receiver-tagged, the response MUST be sent to the address indicated by the "sent-by" value, using the procedures in Section 5 of [4]. */ // TODO: Probably we can use local endpoint instead of socket. Because then we can // Search right UDP connection or TCP/TLS connection. try{ SIP_t_ViaParm via = response.Via.GetTopMostValue(); // End point not specified, get it from Via. if(remoteEndPoint == null){ string host = null; int port = 5060; // Use received host. if(via.Received != null){ host = via.Received; } // Get sent-by host else{ host = via.SentBy.Split(':')[0]; } // Use rport if recevived is specified too if(via.Received != null && via.RPort > 0){ port = via.RPort; } // Get port from sent-by else{ string[] host_port = via.SentBy.Split(':'); if(host_port.Length == 2){ port = Convert.ToInt32(host_port[1]); } } remoteEndPoint = new IPEndPoint(System.Net.Dns.GetHostAddresses(host)[0],port); } byte[] data = response.ToByteData(); // Log m_pSipStack.Logger.AddWrite(data.Length,"Sending (" + data.Length + " bytes): " + socket.LocalEndPoint.ToString() + " -> " + remoteEndPoint.ToString() + "\r\n<begin>\r\n" + System.Text.Encoding.UTF8.GetString(data) + "<end>\r\n"); // We don't have any more that socket what accepted request which response it is. // There are 2 known cases when no socket: // 1) Stateless proxy. // 2) Statefull proxy, but response didn't match any transaction. if(socket == null){ // UDP Multicast if(via.ProtocolTransport.ToUpper() == SIP_Transport.UDP && via.Maddr != null){ throw new SIP_TransportException("UDP Multicast not implemented !"); } // UDP else if(via.ProtocolTransport.ToUpper() == SIP_Transport.UDP){ foreach(SipListeningPoint listeningPoint in m_pListeningPoints){ if(listeningPoint.Protocol == BindInfoProtocol.UDP){ listeningPoint.Socket.SendTo(data,remoteEndPoint); return; } } throw new SIP_TransportException("No UDP transport available, this never should happen !"); } // TCP else if(via.ProtocolTransport.ToUpper() == SIP_Transport.TCP){ SipTcpPipe pipe = GetTcpPipe(SIP_Transport.TCP,remoteEndPoint); // Not existing connection, create it. if(pipe == null){ pipe = new SipTcpPipe(this,remoteEndPoint,true); } pipe.SendMessage(data); } // TCP TLS else if(via.ProtocolTransport.ToUpper() == SIP_Transport.TLS){ SipTcpPipe pipe = GetTcpPipe(SIP_Transport.TLS,remoteEndPoint); // Not existing connection, create it. if(pipe == null){ pipe = new SipTcpPipe(this,remoteEndPoint,true); } pipe.SendMessage(data); } } // We have existing socket, use it. else{ if(via.ProtocolTransport.ToUpper() == SIP_Transport.UDP){ socket.SendTo(data,remoteEndPoint); } else{ socket.Write(data); } } } catch(Exception x){ throw new SIP_TransportException(x.Message); } }