private void EstablishSocks4Connection(Socket client) { var userNameBytes = Encoding.ASCII.GetBytes(_userName); var addressBytes = _remoteEndpoint.Address.GetAddressBytes(); var portBytes = BitConverter.GetBytes((ushort)_remoteEndpoint.Port).Reverse().ToArray(); _client.Connect(_endpoint); // send SOCKS version client.Send(new byte[] { 0x04 }, 0, 1, SocketFlags.None); // send command byte client.Send(new byte[] { 0x00 }, 0, 1, SocketFlags.None); // send port client.Send(portBytes, 0, portBytes.Length, SocketFlags.None); // send address client.Send(addressBytes, 0, addressBytes.Length, SocketFlags.None); // send user name client.Send(userNameBytes, 0, userNameBytes.Length, SocketFlags.None); // terminate user name with null client.Send(new byte[] { 0x00 }, 0, 1, SocketFlags.None); var buffer = new byte[8]; var bytesRead = SocketAbstraction.Read(client, buffer, 0, buffer.Length, TimeSpan.FromMilliseconds(500)); Assert.AreEqual(buffer.Length, bytesRead); // wait until SOCKS client is bound to channel Assert.IsTrue(_channelBindStarted.WaitOne(TimeSpan.FromMilliseconds(200))); }
/// <summary> /// Reads a null terminated string from a socket. /// </summary> /// <param name="socket">The <see cref="Socket"/> to read from.</param> /// <param name="timeout">The timeout to apply to individual reads.</param> /// <returns> /// The <see cref="string"/> read, or <c>null</c> when the socket was closed. /// </returns> private static string ReadString(Socket socket, TimeSpan timeout) { var text = new StringBuilder(); var buffer = new byte[1]; while (true) { if (SocketAbstraction.Read(socket, buffer, 0, 1, timeout) == 0) { // SOCKS client closed connection return(null); } var byteRead = buffer[0]; if (byteRead == 0) { // end of the string break; } var c = (char)byteRead; text.Append(c); } return(text.ToString()); }
private bool HandleSocks4(Socket socket, IChannelDirectTcpip channel, TimeSpan timeout) { var commandCode = SocketAbstraction.ReadByte(socket, timeout); if (commandCode == 0) { // SOCKS client closed connection return(false); } // TODO: See what need to be done depends on the code var portBuffer = new byte[2]; if (SocketAbstraction.Read(socket, portBuffer, 0, portBuffer.Length, timeout) == 0) { // SOCKS client closed connection return(false); } var port = (uint)(portBuffer[0] * 256 + portBuffer[1]); var ipBuffer = new byte[4]; if (SocketAbstraction.Read(socket, ipBuffer, 0, ipBuffer.Length, timeout) == 0) { // SOCKS client closed connection return(false); } var ipAddress = new IPAddress(ipBuffer); var username = ReadString(socket, timeout); if (username == null) { // SOCKS client closed connection return(false); } var host = ipAddress.ToString(); RaiseRequestReceived(host, port); channel.Open(host, port, this, socket); SocketAbstraction.SendByte(socket, 0x00); if (channel.IsOpen) { SocketAbstraction.SendByte(socket, 0x5a); SocketAbstraction.Send(socket, portBuffer, 0, portBuffer.Length); SocketAbstraction.Send(socket, ipBuffer, 0, ipBuffer.Length); return(true); } // signal that request was rejected or failed SocketAbstraction.SendByte(socket, 0x5b); return(false); }
/// <summary> /// Performs a blocking read on the socket until a line is read. /// </summary> /// <param name="socket">The <see cref="Socket"/> to read from.</param> /// <param name="readTimeout">A <see cref="TimeSpan"/> that represents the time to wait until a line is read.</param> /// <exception cref="SshOperationTimeoutException">The read has timed-out.</exception> /// <exception cref="SocketException">An error occurred when trying to access the socket.</exception> /// <returns> /// The line read from the socket, or <c>null</c> when the remote server has shutdown and all data has been received. /// </returns> private static string SocketReadLine(Socket socket, TimeSpan readTimeout) { var encoding = SshData.Ascii; var buffer = new List <byte>(); var data = new byte[1]; // read data one byte at a time to find end of line and leave any unhandled information in the buffer // to be processed by subsequent invocations do { var bytesRead = SocketAbstraction.Read(socket, data, 0, data.Length, readTimeout); if (bytesRead == 0) { // the remote server shut down the socket break; } var b = data[0]; buffer.Add(b); if (b == Session.LineFeed && buffer.Count > 1 && buffer[buffer.Count - 2] == Session.CarriageReturn) { // Return line without CRLF return(encoding.GetString(buffer.ToArray(), 0, buffer.Count - 2)); } }while (true); if (buffer.Count == 0) { return(null); } return(encoding.GetString(buffer.ToArray(), 0, buffer.Count)); }
/// <summary> /// Performs a blocking read on the socket until <paramref name="length"/> bytes are received. /// </summary> /// <param name="socket">The <see cref="Socket"/> to read from.</param> /// <param name="buffer">An array of type <see cref="byte"/> that is the storage location for the received data.</param> /// <param name="offset">The position in <paramref name="buffer"/> parameter to store the received data.</param> /// <param name="length">The number of bytes to read.</param> /// <param name="readTimeout">The maximum time to wait until <paramref name="length"/> bytes have been received.</param> /// <returns> /// The number of bytes read. /// </returns> /// <exception cref="SshConnectionException">The socket is closed.</exception> /// <exception cref="SshOperationTimeoutException">The read has timed-out.</exception> /// <exception cref="SocketException">The read failed.</exception> protected static int SocketRead(Socket socket, byte[] buffer, int offset, int length, TimeSpan readTimeout) { var bytesRead = SocketAbstraction.Read(socket, buffer, offset, length, readTimeout); if (bytesRead == 0) { throw new SshConnectionException("An established connection was aborted by the server.", DisconnectReason.ConnectionLost); } return(bytesRead); }
private static string GetSocks5Host(int addressType, Socket socket, TimeSpan timeout) { switch (addressType) { case 0x01: // IPv4 { var addressBuffer = new byte[4]; if (SocketAbstraction.Read(socket, addressBuffer, 0, 4, timeout) == 0) { // SOCKS client closed connection return(null); } var ipv4 = new IPAddress(addressBuffer); return(ipv4.ToString()); } case 0x03: // Domain name { var length = SocketAbstraction.ReadByte(socket, timeout); if (length == -1) { // SOCKS client closed connection return(null); } var addressBuffer = new byte[length]; if (SocketAbstraction.Read(socket, addressBuffer, 0, addressBuffer.Length, timeout) == 0) { // SOCKS client closed connection return(null); } var hostName = SshData.Ascii.GetString(addressBuffer, 0, addressBuffer.Length); return(hostName); } case 0x04: // IPv6 { var addressBuffer = new byte[16]; if (SocketAbstraction.Read(socket, addressBuffer, 0, 16, timeout) == 0) { // SOCKS client closed connection return(null); } var ipv6 = new IPAddress(addressBuffer); return(ipv6.ToString()); } default: throw new ProxyException(string.Format("SOCKS5: Address type '{0}' is not supported.", addressType)); } }
/// <summary> /// Performs a blocking read on the socket until a line is read. /// </summary> /// <param name="socket">The <see cref="Socket"/> to read from.</param> /// <param name="timeout">A <see cref="TimeSpan"/> that represents the time to wait until a line is read.</param> /// <param name="buffer">A <see cref="List{Byte}"/> to which read bytes will be added.</param> /// <exception cref="SshOperationTimeoutException">The read has timed-out.</exception> /// <exception cref="SocketException">An error occurred when trying to access the socket.</exception> /// <returns> /// The line read from the socket, or <c>null</c> when the remote server has shutdown and all data has been received. /// </returns> private static string SocketReadLine(Socket socket, TimeSpan timeout, List <byte> buffer) { var data = new byte[1]; var startPosition = buffer.Count; // Read data one byte at a time to find end of line and leave any unhandled information in the buffer // to be processed by subsequent invocations. while (true) { var bytesRead = SocketAbstraction.Read(socket, data, 0, data.Length, timeout); if (bytesRead == 0) { // The remote server shut down the socket. break; } var byteRead = data[0]; buffer.Add(byteRead); // The null character MUST NOT be sent if (byteRead == Null) { throw new SshConnectionException(string.Format(CultureInfo.InvariantCulture, "The server response contains a null character at position 0x{0:X8}:{1}{1}{2}{1}{1}" + "A server must not send a null character before the Protocol Version Exchange is complete.{1}{1}" + "More information is available here:{1}" + "https://tools.ietf.org/html/rfc4253#section-4.2", buffer.Count, Environment.NewLine, PacketDump.Create(buffer.ToArray(), 2))); } if (byteRead == Session.LineFeed) { if (buffer.Count > startPosition + 1 && buffer[buffer.Count - 2] == Session.CarriageReturn) { // Return current line without CRLF return(Encoding.UTF8.GetString(buffer.ToArray(), startPosition, buffer.Count - (startPosition + 2))); } else { // Even though RFC4253 clearly indicates that the identification string should be terminated // by a CR LF we also support banners and identification strings that are terminated by a LF // Return current line without LF return(Encoding.UTF8.GetString(buffer.ToArray(), startPosition, buffer.Count - (startPosition + 1))); } } } return(null); }
private bool HandleSocks5(Socket socket, IChannelDirectTcpip channel, TimeSpan timeout) { #if DEBUG_GERT Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " | Handling Socks5: " + socket.LocalEndPoint + " | " + socket.RemoteEndPoint + " | " + DateTime.Now.ToString("hh:mm:ss.fff")); #endif // DEBUG_GERT var authenticationMethodsCount = SocketAbstraction.ReadByte(socket, timeout); if (authenticationMethodsCount == -1) { // SOCKS client closed connection return(false); } #if DEBUG_GERT Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " | After ReadByte for authenticationMethodsCount | " + DateTime.Now.ToString("hh:mm:ss.fff")); #endif // DEBUG_GERT var authenticationMethods = new byte[authenticationMethodsCount]; if (SocketAbstraction.Read(socket, authenticationMethods, 0, authenticationMethods.Length, timeout) == 0) { // SOCKS client closed connection return(false); } #if DEBUG_GERT Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " | After Read for authenticationMethods | " + DateTime.Now.ToString("hh:mm:ss.fff")); #endif // DEBUG_GERT if (authenticationMethods.Min() == 0) { // no user authentication is one of the authentication methods supported // by the SOCKS client SocketAbstraction.Send(socket, new byte[] { 0x05, 0x00 }, 0, 2); #if DEBUG_GERT Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " | After Send for authenticationMethods 0 | " + DateTime.Now.ToString("hh:mm:ss.fff")); #endif // DEBUG_GERT } else { // the SOCKS client requires authentication, which we currently do not support SocketAbstraction.Send(socket, new byte[] { 0x05, 0xFF }, 0, 2); // we continue business as usual but expect the client to close the connection // so one of the subsequent reads should return -1 signaling that the client // has effectively closed the connection #if DEBUG_GERT Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " | After Send for authenticationMethods 2 | " + DateTime.Now.ToString("hh:mm:ss.fff")); #endif // DEBUG_GERT } var version = SocketAbstraction.ReadByte(socket, timeout); if (version == -1) { // SOCKS client closed connection return(false); } if (version != 5) { throw new ProxyException("SOCKS5: Version 5 is expected."); } var commandCode = SocketAbstraction.ReadByte(socket, timeout); if (commandCode == -1) { // SOCKS client closed connection return(false); } var reserved = SocketAbstraction.ReadByte(socket, timeout); if (reserved == -1) { // SOCKS client closed connection return(false); } if (reserved != 0) { throw new ProxyException("SOCKS5: 0 is expected for reserved byte."); } var addressType = SocketAbstraction.ReadByte(socket, timeout); if (addressType == -1) { // SOCKS client closed connection return(false); } IPAddress ipAddress; byte[] addressBuffer; switch (addressType) { case 0x01: { addressBuffer = new byte[4]; if (SocketAbstraction.Read(socket, addressBuffer, 0, 4, timeout) == 0) { // SOCKS client closed connection return(false); } ipAddress = new IPAddress(addressBuffer); } break; case 0x03: { var length = SocketAbstraction.ReadByte(socket, timeout); addressBuffer = new byte[length]; if (SocketAbstraction.Read(socket, addressBuffer, 0, addressBuffer.Length, timeout) == 0) { // SOCKS client closed connection return(false); } ipAddress = IPAddress.Parse(SshData.Ascii.GetString(addressBuffer)); //var hostName = new Common.ASCIIEncoding().GetString(addressBuffer); //ipAddress = Dns.GetHostEntry(hostName).AddressList[0]; } break; case 0x04: { addressBuffer = new byte[16]; if (SocketAbstraction.Read(socket, addressBuffer, 0, 16, timeout) == 0) { // SOCKS client closed connection return(false); } ipAddress = new IPAddress(addressBuffer); } break; default: throw new ProxyException(string.Format("SOCKS5: Address type '{0}' is not supported.", addressType)); } var portBuffer = new byte[2]; if (SocketAbstraction.Read(socket, portBuffer, 0, portBuffer.Length, timeout) == 0) { // SOCKS client closed connection return(false); } var port = (uint)(portBuffer[0] * 256 + portBuffer[1]); var host = ipAddress.ToString(); RaiseRequestReceived(host, port); #if DEBUG_GERT Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " | Before channel open | " + DateTime.Now.ToString("hh:mm:ss.fff")); var stopWatch = new Stopwatch(); stopWatch.Start(); #endif // DEBUG_GERT channel.Open(host, port, this, socket); #if DEBUG_GERT stopWatch.Stop(); Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " | After channel open | " + DateTime.Now.ToString("hh:mm:ss.fff") + " => " + stopWatch.ElapsedMilliseconds); #endif // DEBUG_GERT SocketAbstraction.SendByte(socket, 0x05); if (channel.IsOpen) { SocketAbstraction.SendByte(socket, 0x00); } else { SocketAbstraction.SendByte(socket, 0x01); } // reserved SocketAbstraction.SendByte(socket, 0x00); if (ipAddress.AddressFamily == AddressFamily.InterNetwork) { SocketAbstraction.SendByte(socket, 0x01); } else if (ipAddress.AddressFamily == AddressFamily.InterNetworkV6) { SocketAbstraction.SendByte(socket, 0x04); } else { throw new NotSupportedException("Not supported address family."); } var addressBytes = ipAddress.GetAddressBytes(); SocketAbstraction.Send(socket, addressBytes, 0, addressBytes.Length); SocketAbstraction.Send(socket, portBuffer, 0, portBuffer.Length); return(true); }
private bool HandleSocks5(Socket socket, IChannelDirectTcpip channel, TimeSpan timeout) { var authenticationMethodsCount = SocketAbstraction.ReadByte(socket, timeout); if (authenticationMethodsCount == -1) { // SOCKS client closed connection return(false); } var authenticationMethods = new byte[authenticationMethodsCount]; if (SocketAbstraction.Read(socket, authenticationMethods, 0, authenticationMethods.Length, timeout) == 0) { // SOCKS client closed connection return(false); } if (authenticationMethods.Min() == 0) { // no user authentication is one of the authentication methods supported // by the SOCKS client SocketAbstraction.Send(socket, new byte[] { 0x05, 0x00 }, 0, 2); } else { // the SOCKS client requires authentication, which we currently do not support SocketAbstraction.Send(socket, new byte[] { 0x05, 0xFF }, 0, 2); // we continue business as usual but expect the client to close the connection // so one of the subsequent reads should return -1 signaling that the client // has effectively closed the connection } var version = SocketAbstraction.ReadByte(socket, timeout); if (version == -1) { // SOCKS client closed connection return(false); } if (version != 5) { throw new ProxyException("SOCKS5: Version 5 is expected."); } var commandCode = SocketAbstraction.ReadByte(socket, timeout); if (commandCode == -1) { // SOCKS client closed connection return(false); } var reserved = SocketAbstraction.ReadByte(socket, timeout); if (reserved == -1) { // SOCKS client closed connection return(false); } if (reserved != 0) { throw new ProxyException("SOCKS5: 0 is expected for reserved byte."); } var addressType = SocketAbstraction.ReadByte(socket, timeout); if (addressType == -1) { // SOCKS client closed connection return(false); } IPAddress ipAddress; byte[] addressBuffer; switch (addressType) { case 0x01: { addressBuffer = new byte[4]; if (SocketAbstraction.Read(socket, addressBuffer, 0, 4, timeout) == 0) { // SOCKS client closed connection return(false); } ipAddress = new IPAddress(addressBuffer); } break; case 0x03: { var length = SocketAbstraction.ReadByte(socket, timeout); if (length == -1) { // SOCKS client closed connection return(false); } addressBuffer = new byte[length]; if (SocketAbstraction.Read(socket, addressBuffer, 0, addressBuffer.Length, timeout) == 0) { // SOCKS client closed connection return(false); } ipAddress = IPAddress.Parse(SshData.Ascii.GetString(addressBuffer, 0, addressBuffer.Length)); //var hostName = new Common.ASCIIEncoding().GetString(addressBuffer); //ipAddress = Dns.GetHostEntry(hostName).AddressList[0]; } break; case 0x04: { addressBuffer = new byte[16]; if (SocketAbstraction.Read(socket, addressBuffer, 0, 16, timeout) == 0) { // SOCKS client closed connection return(false); } ipAddress = new IPAddress(addressBuffer); } break; default: throw new ProxyException(string.Format("SOCKS5: Address type '{0}' is not supported.", addressType)); } var portBuffer = new byte[2]; if (SocketAbstraction.Read(socket, portBuffer, 0, portBuffer.Length, timeout) == 0) { // SOCKS client closed connection return(false); } var port = (uint)(portBuffer[0] * 256 + portBuffer[1]); var host = ipAddress.ToString(); RaiseRequestReceived(host, port); channel.Open(host, port, this, socket); SocketAbstraction.SendByte(socket, 0x05); if (channel.IsOpen) { SocketAbstraction.SendByte(socket, 0x00); } else { SocketAbstraction.SendByte(socket, 0x01); } // reserved SocketAbstraction.SendByte(socket, 0x00); if (ipAddress.AddressFamily == AddressFamily.InterNetwork) { SocketAbstraction.SendByte(socket, 0x01); } else if (ipAddress.AddressFamily == AddressFamily.InterNetworkV6) { SocketAbstraction.SendByte(socket, 0x04); } else { throw new NotSupportedException("Not supported address family."); } var addressBytes = ipAddress.GetAddressBytes(); SocketAbstraction.Send(socket, addressBytes, 0, addressBytes.Length); SocketAbstraction.Send(socket, portBuffer, 0, portBuffer.Length); return(true); }
private bool HandleSocks5(Socket socket, IChannelDirectTcpip channel, TimeSpan timeout) { var authenticationMethodsCount = SocketAbstraction.ReadByte(socket, timeout); if (authenticationMethodsCount == -1) { // SOCKS client closed connection return(false); } var authenticationMethods = new byte[authenticationMethodsCount]; if (SocketAbstraction.Read(socket, authenticationMethods, 0, authenticationMethods.Length, timeout) == 0) { // SOCKS client closed connection return(false); } if (authenticationMethods.Min() == 0) { // no user authentication is one of the authentication methods supported // by the SOCKS client SocketAbstraction.Send(socket, new byte[] { 0x05, 0x00 }, 0, 2); } else { // the SOCKS client requires authentication, which we currently do not support SocketAbstraction.Send(socket, new byte[] { 0x05, 0xFF }, 0, 2); // we continue business as usual but expect the client to close the connection // so one of the subsequent reads should return -1 signaling that the client // has effectively closed the connection } var version = SocketAbstraction.ReadByte(socket, timeout); if (version == -1) { // SOCKS client closed connection return(false); } if (version != 5) { throw new ProxyException("SOCKS5: Version 5 is expected."); } var commandCode = SocketAbstraction.ReadByte(socket, timeout); if (commandCode == -1) { // SOCKS client closed connection return(false); } var reserved = SocketAbstraction.ReadByte(socket, timeout); if (reserved == -1) { // SOCKS client closed connection return(false); } if (reserved != 0) { throw new ProxyException("SOCKS5: 0 is expected for reserved byte."); } var addressType = SocketAbstraction.ReadByte(socket, timeout); if (addressType == -1) { // SOCKS client closed connection return(false); } var host = GetSocks5Host(addressType, socket, timeout); if (host == null) { // SOCKS client closed connection return(false); } var portBuffer = new byte[2]; if (SocketAbstraction.Read(socket, portBuffer, 0, portBuffer.Length, timeout) == 0) { // SOCKS client closed connection return(false); } var port = Pack.BigEndianToUInt16(portBuffer); RaiseRequestReceived(host, port); channel.Open(host, port, this, socket); var socksReply = CreateSocks5Reply(channel.IsOpen); SocketAbstraction.Send(socket, socksReply, 0, socksReply.Length); return(true); }
/// <summary> /// Establishes a connection to the server via a SOCKS5 proxy. /// </summary> /// <param name="connectionInfo">The connection information.</param> /// <param name="socket">The <see cref="Socket"/>.</param> private void HandleProxyConnect(IConnectionInfo connectionInfo, Socket socket) { var greeting = new byte[] { // SOCKS version number 0x05, // Number of supported authentication methods 0x02, // No authentication 0x00, // Username/Password authentication 0x02 }; SocketAbstraction.Send(socket, greeting); var socksVersion = SocketReadByte(socket); if (socksVersion != 0x05) { throw new ProxyException(string.Format("SOCKS Version '{0}' is not supported.", socksVersion)); } var authenticationMethod = SocketReadByte(socket); switch (authenticationMethod) { case 0x00: break; case 0x02: // Create username/password authentication request var authenticationRequest = CreateSocks5UserNameAndPasswordAuthenticationRequest(connectionInfo.ProxyUsername, connectionInfo.ProxyPassword); // Send authentication request SocketAbstraction.Send(socket, authenticationRequest); // Read authentication result var authenticationResult = SocketAbstraction.Read(socket, 2, connectionInfo.Timeout); if (authenticationResult[0] != 0x01) { throw new ProxyException("SOCKS5: Server authentication version is not valid."); } if (authenticationResult[1] != 0x00) { throw new ProxyException("SOCKS5: Username/Password authentication failed."); } break; case 0xFF: throw new ProxyException("SOCKS5: No acceptable authentication methods were offered."); } var connectionRequest = CreateSocks5ConnectionRequest(connectionInfo.Host, (ushort)connectionInfo.Port); SocketAbstraction.Send(socket, connectionRequest); // Read Server SOCKS5 version if (SocketReadByte(socket) != 5) { throw new ProxyException("SOCKS5: Version 5 is expected."); } // Read response code var status = SocketReadByte(socket); switch (status) { case 0x00: break; case 0x01: throw new ProxyException("SOCKS5: General failure."); case 0x02: throw new ProxyException("SOCKS5: Connection not allowed by ruleset."); case 0x03: throw new ProxyException("SOCKS5: Network unreachable."); case 0x04: throw new ProxyException("SOCKS5: Host unreachable."); case 0x05: throw new ProxyException("SOCKS5: Connection refused by destination host."); case 0x06: throw new ProxyException("SOCKS5: TTL expired."); case 0x07: throw new ProxyException("SOCKS5: Command not supported or protocol error."); case 0x08: throw new ProxyException("SOCKS5: Address type not supported."); default: throw new ProxyException("SOCKS5: Not valid response."); } // Read reserved byte if (SocketReadByte(socket) != 0) { throw new ProxyException("SOCKS5: 0 byte is expected."); } var addressType = SocketReadByte(socket); switch (addressType) { case 0x01: var ipv4 = new byte[4]; SocketRead(socket, ipv4, 0, 4); break; case 0x04: var ipv6 = new byte[16]; SocketRead(socket, ipv6, 0, 16); break; default: throw new ProxyException(string.Format("Address type '{0}' is not supported.", addressType)); } var port = new byte[2]; // Read 2 bytes to be ignored SocketRead(socket, port, 0, 2); }