protected override async Task <Socket> ClientCommandReceived(NetworkStream client, byte[] buffer, int length, CancellationToken cancellationToken) { Socket server = null; Socks4Command cmd; byte[] response; IPAddress ip; string user; int port; if (TryParse(buffer, length, out cmd, out port, out ip, out user)) { var host = ip.ToString(); try { server = await SocketUtils.ConnectAsync(host, port, null, true, cancellationToken).ConfigureAwait(false); var remote = (IPEndPoint)server.RemoteEndPoint; response = GetResponse(Socks4Reply.RequestGranted, remote); } catch { response = GetResponse(Socks4Reply.RequestRejected, null); } } else { response = GetResponse(Socks4Reply.RequestRejected, null); } await client.WriteAsync(response, 0, response.Length, cancellationToken).ConfigureAwait(false); return(server); }
async Task <Socket> ConnectAsync(string host, int port, bool doAsync, CancellationToken cancellationToken) { ValidateArguments(host, port); cancellationToken.ThrowIfCancellationRequested(); var socket = await SocketUtils.ConnectAsync(ProxyHost, ProxyPort, LocalEndPoint, doAsync, cancellationToken).ConfigureAwait(false); var builder = new StringBuilder(); builder.AppendFormat("CONNECT {0}:{1} HTTP/1.1\r\n", host, port); builder.AppendFormat("Host: {0}:{1}\r\n", host, port); if (ProxyCredentials != null) { var token = Encoding.UTF8.GetBytes(string.Format("{0}:{1}", ProxyCredentials.UserName, ProxyCredentials.Password)); var base64 = Convert.ToBase64String(token); builder.AppendFormat("Proxy-Authorization: Basic {0}\r\n", base64); } builder.Append("\r\n"); var command = Encoding.UTF8.GetBytes(builder.ToString()); try { await SendAsync(socket, command, 0, command.Length, doAsync, cancellationToken).ConfigureAwait(false); var buffer = new byte[1024]; var endOfHeaders = false; var newline = false; builder.Clear(); // read until we consume the end of the headers (it's ok if we read some of the content) do { int nread = await ReceiveAsync(socket, buffer, 0, buffer.Length, doAsync, cancellationToken).ConfigureAwait(false); if (nread > 0) { int n = nread; for (int i = 0; i < nread && !endOfHeaders; i++) { switch ((char)buffer[i]) { case '\r': break; case '\n': endOfHeaders = newline; newline = true; if (endOfHeaders) { n = i + 1; } break; default: newline = false; break; } } builder.Append(Encoding.UTF8.GetString(buffer, 0, n)); } } while (!endOfHeaders); int index = 0; while (builder[index] != '\n') { index++; } if (index > 0 && builder[index - 1] == '\r') { index--; } // trim everything beyond the "HTTP/1.1 200 ..." part of the response builder.Length = index; var response = builder.ToString(); if (response.Length >= 16 && response.StartsWith("HTTP/1.", StringComparison.OrdinalIgnoreCase) && (response[7] == '1' || response[7] == '0') && response[8] == ' ' && response[9] == '2' && response[10] == '0' && response[11] == '0' && response[12] == ' ') { return(socket); } throw new ProxyProtocolException(string.Format("Failed to connect to {0}:{1}: {2}", host, port, response)); } catch { #if NETSTANDARD_2_0 || NET_4_5 || __MOBILE__ if (socket.Connected) { socket.Disconnect(false); } #endif socket.Dispose(); throw; } }
async Task <Socket> ConnectAsync(string host, int port, bool doAsync, CancellationToken cancellationToken) { Socks5AddressType addrType; IPAddress ip; ValidateArguments(host, port); addrType = GetAddressType(host, out ip); cancellationToken.ThrowIfCancellationRequested(); var socket = await SocketUtils.ConnectAsync(ProxyHost, ProxyPort, LocalEndPoint, doAsync, cancellationToken).ConfigureAwait(false); byte[] domain = null, addr = null; const int bufferSize = 6 + 257; if (addrType == Socks5AddressType.Domain) { domain = Encoding.UTF8.GetBytes(host); } try { Socks5AuthMethod method; if (ProxyCredentials != null) { method = await NegotiateAuthMethodAsync(socket, doAsync, cancellationToken, Socks5AuthMethod.UserPassword, Socks5AuthMethod.Anonymous).ConfigureAwait(false); } else { method = await NegotiateAuthMethodAsync(socket, doAsync, cancellationToken, Socks5AuthMethod.Anonymous).ConfigureAwait(false); } switch (method) { case Socks5AuthMethod.UserPassword: await AuthenticateAsync(socket, doAsync, cancellationToken).ConfigureAwait(false); break; case Socks5AuthMethod.Anonymous: break; default: throw new ProxyProtocolException("Failed to negotiate authentication method with the proxy server."); } // +----+-----+-------+------+----------+----------+ // |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT | // +----+-----+-------+------+----------+----------+ // | 1 | 1 | X'00' | 1 | Variable | 2 | // +----+-----+-------+------+----------+----------+ var buffer = new byte[bufferSize]; int nread, n = 0; buffer[n++] = (byte)SocksVersion; buffer[n++] = (byte)Socks5Command.Connect; buffer[n++] = 0x00; buffer[n++] = (byte)addrType; switch (addrType) { case Socks5AddressType.Domain: buffer[n++] = (byte)domain.Length; Buffer.BlockCopy(domain, 0, buffer, n, domain.Length); n += domain.Length; break; case Socks5AddressType.IPv6: addr = ip.GetAddressBytes(); Buffer.BlockCopy(addr, 0, buffer, n, addr.Length); n += 16; break; case Socks5AddressType.IPv4: addr = ip.GetAddressBytes(); Buffer.BlockCopy(addr, 0, buffer, n, addr.Length); n += 4; break; } buffer[n++] = (byte)(port >> 8); buffer[n++] = (byte)port; await SendAsync(socket, buffer, 0, n, doAsync, cancellationToken).ConfigureAwait(false); // +-----+-----+-------+------+----------+----------+ // | VER | REP | RSV | ATYP | BND.ADDR | BND.PORT | // +-----+-----+-------+------+----------+----------+ // | 1 | 1 | X'00' | 1 | Variable | 2 | // +-----+-----+-------+------+----------+----------+ // Note: We know we'll need at least 4 bytes of header + a minimum of 1 byte // to determine the length of the BND.ADDR field if ATYP is a domain. int need = 5; n = 0; do { if ((nread = await ReceiveAsync(socket, buffer, 0 + n, need - n, doAsync, cancellationToken).ConfigureAwait(false)) > 0) { n += nread; } } while (n < need); VerifySocksVersion(buffer[0]); if (buffer[1] != (byte)Socks5Reply.Success) { throw new ProxyProtocolException(string.Format("Failed to connect to {0}:{1}: {2}", host, port, GetFailureReason(buffer[1]))); } addrType = (Socks5AddressType)buffer[3]; switch (addrType) { case Socks5AddressType.Domain: need += buffer[4] + 2; break; case Socks5AddressType.IPv6: need += (16 - 1) + 2; break; case Socks5AddressType.IPv4: need += (4 - 1) + 2; break; default: throw new ProxyProtocolException("Proxy server returned unknown address type."); } do { if ((nread = await ReceiveAsync(socket, buffer, 0 + n, need - n, doAsync, cancellationToken).ConfigureAwait(false)) > 0) { n += nread; } } while (n < need); // TODO: do we care about BND.ADDR and BND.PORT? return(socket); } catch { #if !NETSTANDARD1_3 && !NETSTANDARD1_6 if (socket.Connected) { socket.Disconnect(false); } #endif socket.Dispose(); throw; } }
async Task <Stream> ConnectAsync(string host, int port, bool doAsync, CancellationToken cancellationToken) { ValidateArguments(host, port); cancellationToken.ThrowIfCancellationRequested(); var socket = await SocketUtils.ConnectAsync(ProxyHost, ProxyPort, LocalEndPoint, doAsync, cancellationToken).ConfigureAwait(false); var ssl = new SslStream(new NetworkStream(socket, true), false, ValidateRemoteCertificate); try { if (doAsync) { #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER await ssl.AuthenticateAsClientAsync(GetSslClientAuthenticationOptions (host, ValidateRemoteCertificate), cancellationToken).ConfigureAwait(false); #else await ssl.AuthenticateAsClientAsync(host, ClientCertificates, SslProtocols, CheckCertificateRevocation).ConfigureAwait(false); #endif } else { #if NETSTANDARD1_3 || NETSTANDARD1_6 ssl.AuthenticateAsClientAsync(host, ClientCertificates, SslProtocols, CheckCertificateRevocation).GetAwaiter().GetResult(); #elif NET5_0_OR_GREATER ssl.AuthenticateAsClient(GetSslClientAuthenticationOptions(host, ValidateRemoteCertificate)); #else ssl.AuthenticateAsClient(host, ClientCertificates, SslProtocols, CheckCertificateRevocation); #endif } } catch (Exception ex) { ssl.Dispose(); throw SslHandshakeException.Create(ref sslValidationInfo, ex, false, "HTTP", host, port, 443, 80); } var command = HttpProxyClient.GetConnectCommand(host, port, ProxyCredentials); try { if (doAsync) { await ssl.WriteAsync(command, 0, command.Length, cancellationToken).ConfigureAwait(false); } else { ssl.Write(command, 0, command.Length); } var buffer = ArrayPool <byte> .Shared.Rent(BufferSize); var builder = new StringBuilder(); try { var endOfHeaders = false; var newline = false; // read until we consume the end of the headers (it's ok if we read some of the content) do { int nread; if (doAsync) { nread = await ssl.ReadAsync(buffer, 0, BufferSize, cancellationToken).ConfigureAwait(false); } else { nread = ssl.Read(buffer, 0, BufferSize); } if (nread > 0) { int n = nread; for (int i = 0; i < nread && !endOfHeaders; i++) { switch ((char)buffer[i]) { case '\r': break; case '\n': endOfHeaders = newline; newline = true; if (endOfHeaders) { n = i + 1; } break; default: newline = false; break; } } builder.Append(Encoding.UTF8.GetString(buffer, 0, n)); } } while (!endOfHeaders); } finally { ArrayPool <byte> .Shared.Return(buffer); } int index = 0; while (builder[index] != '\n') { index++; } if (index > 0 && builder[index - 1] == '\r') { index--; } // trim everything beyond the "HTTP/1.1 200 ..." part of the response builder.Length = index; var response = builder.ToString(); if (response.Length >= 15 && response.StartsWith("HTTP/1.", StringComparison.OrdinalIgnoreCase) && (response[7] == '1' || response[7] == '0') && response[8] == ' ' && response[9] == '2' && response[10] == '0' && response[11] == '0' && response[12] == ' ') { return(ssl); } throw new ProxyProtocolException(string.Format(CultureInfo.InvariantCulture, "Failed to connect to {0}:{1}: {2}", host, port, response)); } catch { ssl.Dispose(); throw; } }
async Task <Socket> ConnectAsync(string host, int port, bool doAsync, CancellationToken cancellationToken) { byte[] addr, domain = null; IPAddress ip; ValidateArguments(host, port); if (!IPAddress.TryParse(host, out ip)) { if (IsSocks4a) { domain = Encoding.UTF8.GetBytes(host); addr = InvalidIPAddress; } else { ip = await ResolveAsync(host, doAsync, cancellationToken).ConfigureAwait(false); addr = ip.GetAddressBytes(); } } else { if (ip.AddressFamily != AddressFamily.InterNetwork) { throw new ArgumentException("The specified host address must be IPv4.", nameof(host)); } addr = ip.GetAddressBytes(); } cancellationToken.ThrowIfCancellationRequested(); var socket = await SocketUtils.ConnectAsync(ProxyHost, ProxyPort, LocalEndPoint, doAsync, cancellationToken).ConfigureAwait(false); var user = ProxyCredentials != null?Encoding.UTF8.GetBytes(ProxyCredentials.UserName) : new byte[0]; try { // +----+-----+----------+----------+----------+-------+--------------+-------+ // |VER | CMD | DST.PORT | DST.ADDR | USERID | NULL | DST.DOMAIN | NULL | // +----+-----+----------+----------+----------+-------+--------------+-------+ // | 1 | 1 | 2 | 4 | VARIABLE | X'00' | VARIABLE | X'00' | // +----+-----+----------+----------+----------+-------+--------------+-------+ int bufferSize = 9 + user.Length + (domain != null ? domain.Length + 1 : 0); var buffer = new byte[bufferSize]; int nread, n = 0; buffer[n++] = (byte)SocksVersion; buffer[n++] = (byte)Socks4Command.Connect; buffer[n++] = (byte)(port >> 8); buffer[n++] = (byte)port; Buffer.BlockCopy(addr, 0, buffer, n, 4); n += 4; Buffer.BlockCopy(user, 0, buffer, n, user.Length); n += user.Length; buffer[n++] = 0x00; if (domain != null) { Buffer.BlockCopy(domain, 0, buffer, n, domain.Length); n += domain.Length; buffer[n++] = 0x00; } await SendAsync(socket, buffer, 0, n, doAsync, cancellationToken).ConfigureAwait(false); // +-----+-----+----------+----------+ // | VER | REP | BND.PORT | BND.ADDR | // +-----+-----+----------+----------+ // | 1 | 1 | 2 | 4 | // +-----+-----+----------+----------+ n = 0; do { if ((nread = await ReceiveAsync(socket, buffer, 0 + n, 8 - n, doAsync, cancellationToken).ConfigureAwait(false)) > 0) { n += nread; } } while (n < 8); if (buffer[1] != (byte)Socks4Reply.RequestGranted) { throw new ProxyProtocolException(string.Format("Failed to connect to {0}:{1}: {2}", host, port, GetFailureReason(buffer[1]))); } // TODO: do we care about BND.ADDR and BND.PORT? return(socket); } catch { #if !NETSTANDARD1_3 && !NETSTANDARD1_6 if (socket.Connected) { socket.Disconnect(false); } #endif socket.Dispose(); throw; } }
protected override async Task <Socket> ClientCommandReceived(NetworkStream client, byte[] buffer, int length, CancellationToken cancellationToken) { byte[] response = null; Socket server = null; Buffer.BlockCopy(buffer, 0, request, requestLength, length); requestLength += length; switch (state) { case Socks5ListenerState.NegotiateAuthMethod: response = NegotiateAuthMethod(); break; case Socks5ListenerState.Authenticate: response = Authenticate(); break; case Socks5ListenerState.Command: var result = Parse(request, requestLength, out Socks5Command cmd, out string host, out int port); switch (result) { case Socks5ParseResult.Success: try { server = await SocketUtils.ConnectAsync(host, port, null, true, cancellationToken).ConfigureAwait(false); var remote = (IPEndPoint)server.RemoteEndPoint; response = GetCommandResponse(Socks5Reply.Success, remote); } catch (OperationCanceledException) { throw; } catch (SocketException ex) { response = GetCommandResponse(ex.SocketErrorCode); } catch { response = GetCommandResponse(Socks5Reply.GeneralServerFailure, null); } break; case Socks5ParseResult.InvalidAddrType: response = GetCommandResponse(Socks5Reply.AddressTypeNotSupported, null); break; case Socks5ParseResult.InvalidCommand: response = GetCommandResponse(Socks5Reply.CommandNotSupported, null); break; case Socks5ParseResult.NotEnoughData: response = null; break; case Socks5ParseResult.InvalidRequest: default: response = GetCommandResponse(Socks5Reply.GeneralServerFailure, null); break; } break; } if (response == null) { return(null); } await client.WriteAsync(response, 0, response.Length, cancellationToken).ConfigureAwait(false); requestLength = 0; return(server); }
async Task <Stream> ConnectAsync(string host, int port, bool doAsync, CancellationToken cancellationToken) { ValidateArguments(host, port); cancellationToken.ThrowIfCancellationRequested(); var socket = await SocketUtils.ConnectAsync(ProxyHost, ProxyPort, LocalEndPoint, doAsync, cancellationToken).ConfigureAwait(false); var command = GetConnectCommand(host, port, ProxyCredentials); try { await SendAsync(socket, command, 0, command.Length, doAsync, cancellationToken).ConfigureAwait(false); var buffer = ArrayPool <byte> .Shared.Rent(BufferSize); var builder = new StringBuilder(); try { var endOfHeaders = false; var newline = false; // read until we consume the end of the headers (it's ok if we read some of the content) do { int nread = await ReceiveAsync(socket, buffer, 0, BufferSize, doAsync, cancellationToken).ConfigureAwait(false); if (nread > 0) { int n = nread; for (int i = 0; i < nread && !endOfHeaders; i++) { switch ((char)buffer[i]) { case '\r': break; case '\n': endOfHeaders = newline; newline = true; if (endOfHeaders) { n = i + 1; } break; default: newline = false; break; } } builder.Append(Encoding.UTF8.GetString(buffer, 0, n)); } } while (!endOfHeaders); } finally { ArrayPool <byte> .Shared.Return(buffer); } int index = 0; while (builder[index] != '\n') { index++; } if (index > 0 && builder[index - 1] == '\r') { index--; } // trim everything beyond the "HTTP/1.1 200 ..." part of the response builder.Length = index; var response = builder.ToString(); if (response.Length >= 15 && response.StartsWith("HTTP/1.", StringComparison.OrdinalIgnoreCase) && (response[7] == '1' || response[7] == '0') && response[8] == ' ' && response[9] == '2' && response[10] == '0' && response[11] == '0' && response[12] == ' ') { return(new NetworkStream(socket, true)); } throw new ProxyProtocolException(string.Format(CultureInfo.InvariantCulture, "Failed to connect to {0}:{1}: {2}", host, port, response)); } catch { #if !NETSTANDARD1_3 && !NETSTANDARD1_6 if (socket.Connected) { socket.Disconnect(false); } #endif socket.Dispose(); throw; } }
protected override async Task <Socket> ClientCommandReceived(Stream client, byte[] buffer, int length, CancellationToken cancellationToken) { byte[] response = null; Socket server = null; using (var stream = new MemoryStream(buffer, 0, length, false)) { using (var reader = new StreamReader(stream)) { string line; while ((line = reader.ReadLine()) != null) { if (string.IsNullOrEmpty(line)) { break; } if (line.StartsWith("CONNECT ", StringComparison.OrdinalIgnoreCase)) { int startIndex = "CONNECT ".Length; int index = startIndex; while (index < line.Length && line[index] != ':') { index++; } var host = line.Substring(startIndex, index - startIndex); startIndex = ++index; while (index < line.Length && line[index] != ' ') { index++; } var portStr = line.Substring(startIndex, index - startIndex); int port = int.Parse(portStr, CultureInfo.InvariantCulture); index++; // HTTP/X.Y var httpVersion = line.Substring(index); try { server = await SocketUtils.ConnectAsync(host, port, null, true, cancellationToken).ConfigureAwait(false); var remote = (IPEndPoint)server.RemoteEndPoint; response = Encoding.ASCII.GetBytes($"HTTP/1.1 200 Connected to {remote}\r\n\r\n"); } catch { response = Encoding.ASCII.GetBytes("HTTP/1.1 404 Not Found\r\n\r\n"); } break; } } } } if (response == null) { return(null); } await client.WriteAsync(response, 0, response.Length, cancellationToken).ConfigureAwait(false); return(server); }