-
-
Notifications
You must be signed in to change notification settings - Fork 980
Expand file tree
/
Copy pathSocks4Connector.cs
More file actions
139 lines (111 loc) · 4.86 KB
/
Socks4Connector.cs
File metadata and controls
139 lines (111 loc) · 4.86 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
using System;
using System.Buffers.Binary;
using System.Net;
using System.Net.Sockets;
using System.Text;
using Microsoft.Extensions.Logging;
using Renci.SshNet.Abstractions;
using Renci.SshNet.Common;
namespace Renci.SshNet.Connection
{
/// <summary>
/// Establishes a tunnel via a SOCKS4 proxy server.
/// </summary>
/// <remarks>
/// https://www.openssh.com/txt/socks4.protocol.
/// </remarks>
internal sealed class Socks4Connector : ProxyConnector
{
public Socks4Connector(ISocketFactory socketFactory, ILoggerFactory loggerFactory)
: base(socketFactory, loggerFactory)
{
}
/// <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>
protected override void HandleProxyConnect(IConnectionInfo connectionInfo, Socket socket)
{
var connectionRequest = CreateSocks4ConnectionRequest(connectionInfo.Host, (ushort)connectionInfo.Port, connectionInfo.ProxyUsername);
SocketAbstraction.Send(socket, connectionRequest);
// Read reply version
if (SocketReadByte(socket, connectionInfo.Timeout) != 0x00)
{
throw new ProxyException("SOCKS4: Null is expected.");
}
// Read response code
var code = SocketReadByte(socket, connectionInfo.Timeout);
switch (code)
{
case 0x5a:
break;
case 0x5b:
throw new ProxyException("SOCKS4: Connection rejected.");
case 0x5c:
throw new ProxyException("SOCKS4: Client is not running identd or not reachable from the server.");
case 0x5d:
throw new ProxyException("SOCKS4: Client's identd could not confirm the user ID string in the request.");
default:
throw new ProxyException("SOCKS4: Not valid response.");
}
var destBuffer = new byte[6]; // destination port and IP address should be ignored
_ = SocketRead(socket, destBuffer, 0, destBuffer.Length, connectionInfo.Timeout);
}
private static byte[] CreateSocks4ConnectionRequest(string hostname, ushort port, string username)
{
var addressBytes = GetSocks4DestinationAddress(hostname);
var proxyUserBytes = GetProxyUserBytes(username);
var connectionRequest = new byte[// SOCKS version number
1 +
// Command code
1 +
// Port number
2 +
// IP address
addressBytes.Length +
// Username
proxyUserBytes.Length +
// Null terminator
1];
var index = 0;
// SOCKS version number
connectionRequest[index++] = 0x04;
// Command code
connectionRequest[index++] = 0x01; // establish a TCP/IP stream connection
// Port number
BinaryPrimitives.WriteUInt16BigEndian(connectionRequest.AsSpan(index), port);
index += 2;
// Address
Buffer.BlockCopy(addressBytes, 0, connectionRequest, index, addressBytes.Length);
index += addressBytes.Length;
// User name
Buffer.BlockCopy(proxyUserBytes, 0, connectionRequest, index, proxyUserBytes.Length);
index += proxyUserBytes.Length;
// Null terminator
connectionRequest[index] = 0x00;
return connectionRequest;
}
private static byte[] GetSocks4DestinationAddress(string hostname)
{
var addresses = Dns.GetHostAddresses(hostname);
for (var i = 0; i < addresses.Length; i++)
{
var address = addresses[i];
if (address.AddressFamily == AddressFamily.InterNetwork)
{
return address.GetAddressBytes();
}
}
throw new ProxyException(string.Format("SOCKS4 only supports IPv4. No such address found for '{0}'.", hostname));
}
private static byte[] GetProxyUserBytes(string proxyUser)
{
if (proxyUser == null)
{
return Array.Empty<byte>();
}
return Encoding.ASCII.GetBytes(proxyUser);
}
}
}