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, null, doAsync, cancellationToken).ConfigureAwait(false); var method = ProxyCredentials != null ? Socks5AuthMethod.UserPassword : Socks5AuthMethod.Anonymous; byte[] domain = null, addr = null; int bufferSize = 6; switch (addrType) { case Socks5AddressType.Domain: domain = Encoding.UTF8.GetBytes(host); bufferSize += 1 + domain.Length; break; case Socks5AddressType.IPv6: bufferSize += 16; break; case Socks5AddressType.IPv4: bufferSize += 4; break; } try { method = NegotiateAuthMethod(socket, cancellationToken, method); switch (method) { case Socks5AuthMethod.UserPassword: Authenticate(socket, cancellationToken); break; case Socks5AuthMethod.Anonymous: break; default: //throw new ProxyProtocolException ("Failed to negotiate authentication method with the proxy server."); break; } // +----+-----+-------+------+----------+----------+ // |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; SocketUtils.Poll(socket, SelectMode.SelectWrite, cancellationToken); socket.Send(buffer, 0, n, SocketFlags.None); // +-----+-----+-------+------+----------+----------+ // | 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 { SocketUtils.Poll(socket, SelectMode.SelectRead, cancellationToken); if ((nread = socket.Receive(buffer, 0 + n, need - n, SocketFlags.None)) > 0) { n += nread; } } while (n < need); 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 { SocketUtils.Poll(socket, SelectMode.SelectRead, cancellationToken); if ((nread = socket.Receive(buffer, 0 + n, need - n, SocketFlags.None)) > 0) { n += nread; } } while (n < need); // TODO: do we care about BND.ADDR and BND.PORT? return(socket); } catch (OperationCanceledException) { socket.Disconnect(false); socket.Dispose(); throw; } catch { socket.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)) { domain = Encoding.UTF8.GetBytes(host); addr = InvalidIPAddress; } else { if (ip.AddressFamily != AddressFamily.InterNetwork) { throw new ArgumentException(nameof(host)); } addr = ip.GetAddressBytes(); } cancellationToken.ThrowIfCancellationRequested(); var socket = await SocketUtils.ConnectAsync(ProxyHost, ProxyPort, null, 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; } SocketUtils.Poll(socket, SelectMode.SelectWrite, cancellationToken); socket.Send(buffer, 0, n, SocketFlags.None); // +-----+-----+----------+----------+ // | VER | REP | BND.PORT | BND.ADDR | // +-----+-----+----------+----------+ // | 1 | 1 | 2 | 4 | // +-----+-----+----------+----------+ n = 0; do { SocketUtils.Poll(socket, SelectMode.SelectRead, cancellationToken); if ((nread = socket.Receive(buffer, 0 + n, 8 - n, SocketFlags.None)) > 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 (OperationCanceledException) { socket.Disconnect(false); socket.Dispose(); throw; } catch { socket.Dispose(); throw; } }