@@ -87,6 +87,9 @@ internal static class Socks5Helper
87
87
88
88
private const int BufferSize = 512 ;
89
89
90
+ //TODO Maybe need to change the name of the ReadMethods... It's more like interpret
91
+ //Need to specify all create request methods return the bytes of the message
92
+
90
93
public static void PerformSocks5Handshake ( Stream stream , EndPoint endPoint , Socks5AuthenticationSettings authenticationSettings , CancellationToken cancellationToken )
91
94
{
92
95
var ( targetHost , targetPort ) = endPoint . GetHostAndPort ( ) ;
@@ -98,13 +101,23 @@ public static void PerformSocks5Handshake(Stream stream, EndPoint endPoint, Sock
98
101
var greetingRequestLength = CreateGreetingRequest ( buffer , useAuth ) ;
99
102
stream . Write ( buffer , 0 , greetingRequestLength ) ;
100
103
101
- ReadGreetingResponse ( stream , buffer , useAuth , authenticationSettings , cancellationToken ) ;
104
+ stream . ReadBytes ( buffer , 0 , 2 , cancellationToken ) ;
105
+ ProcessGreetingResponse ( buffer , useAuth , authenticationSettings , cancellationToken ) ;
106
+
107
+ if ( useAuth )
108
+ {
109
+ var authenticationRequestLength = CreateAuthenticationRequest ( buffer , authenticationSettings , cancellationToken ) ;
110
+ stream . Write ( buffer , 0 , authenticationRequestLength ) ;
111
+
112
+ stream . ReadBytes ( buffer , 0 , 2 , cancellationToken ) ;
113
+ ProcessAuthenticationResponse ( buffer ) ;
114
+ }
102
115
103
- var addressLength = CreateConnectRequest ( buffer , targetHost , targetPort ) ;
104
- stream . Write ( buffer , 0 , addressLength + 6 ) ;
116
+ var connectRequestLength = CreateConnectRequest ( buffer , targetHost , targetPort ) ;
117
+ stream . Write ( buffer , 0 , connectRequestLength ) ;
105
118
106
119
stream . ReadBytes ( buffer , 0 , 5 , cancellationToken ) ;
107
- var skip = ReadConnectResponse ( buffer ) ;
120
+ var skip = ProcessConnectResponse ( buffer ) ;
108
121
stream . ReadBytes ( buffer , 0 , skip , cancellationToken ) ;
109
122
}
110
123
finally
@@ -124,13 +137,22 @@ public static async Task PerformSocks5HandshakeAsync(Stream stream, EndPoint end
124
137
var greetingRequestLength = CreateGreetingRequest ( buffer , useAuth ) ;
125
138
await stream . WriteAsync ( buffer , 0 , greetingRequestLength , cancellationToken ) . ConfigureAwait ( false ) ;
126
139
127
- await ReadGreetingResponseAsync ( stream , buffer , useAuth , authenticationSettings , cancellationToken ) . ConfigureAwait ( false ) ;
140
+ ProcessGreetingResponse ( buffer , useAuth , authenticationSettings , cancellationToken ) ;
128
141
129
- var addressLength = CreateConnectRequest ( buffer , targetHost , targetPort ) ;
130
- await stream . WriteAsync ( buffer , 0 , addressLength + 6 , cancellationToken ) . ConfigureAwait ( false ) ;
142
+ if ( useAuth )
143
+ {
144
+ var authenticationRequestLength = CreateAuthenticationRequest ( buffer , authenticationSettings , cancellationToken ) ;
145
+ await stream . WriteAsync ( buffer , 0 , authenticationRequestLength , cancellationToken ) . ConfigureAwait ( false ) ;
146
+
147
+ await stream . ReadBytesAsync ( buffer , 0 , 2 , cancellationToken ) . ConfigureAwait ( false ) ;
148
+ ProcessAuthenticationResponse ( buffer ) ;
149
+ }
150
+
151
+ var connectRequestLength = CreateConnectRequest ( buffer , targetHost , targetPort ) ;
152
+ await stream . WriteAsync ( buffer , 0 , connectRequestLength , cancellationToken ) . ConfigureAwait ( false ) ;
131
153
132
154
await stream . ReadBytesAsync ( buffer , 0 , 5 , cancellationToken ) . ConfigureAwait ( false ) ;
133
- var skip = ReadConnectResponse ( buffer ) ;
155
+ var skip = ProcessConnectResponse ( buffer ) ;
134
156
await stream . ReadBytesAsync ( buffer , 0 , skip , cancellationToken ) . ConfigureAwait ( true ) ;
135
157
}
136
158
finally
@@ -156,9 +178,8 @@ private static int CreateGreetingRequest(byte[] buffer, bool useAuth)
156
178
return 4 ;
157
179
}
158
180
159
- private static void ReadGreetingResponse ( Stream stream , byte [ ] buffer , bool useAuth , Socks5AuthenticationSettings authenticationSettings , CancellationToken cancellationToken )
181
+ private static void ProcessGreetingResponse ( byte [ ] buffer , bool useAuth , Socks5AuthenticationSettings authenticationSettings , CancellationToken cancellationToken )
160
182
{
161
- stream . ReadBytes ( buffer , 0 , 2 , cancellationToken ) ;
162
183
VerifyProtocolVersion ( buffer [ 0 ] ) ;
163
184
var method = buffer [ 1 ] ;
164
185
if ( method == MethodUsernamePassword )
@@ -167,30 +188,33 @@ private static void ReadGreetingResponse(Stream stream, byte[] buffer, bool useA
167
188
{
168
189
throw new IOException ( "SOCKS5 proxy requires authentication, but no credentials were provided." ) ;
169
190
}
170
- PerformUsernamePasswordAuth ( stream , buffer , authenticationSettings , cancellationToken ) ;
171
191
}
172
192
else if ( method != MethodNoAuth )
173
193
{
174
194
throw new IOException ( "SOCKS5 proxy requires unsupported authentication method." ) ;
175
195
}
176
196
}
177
197
178
- private static async Task ReadGreetingResponseAsync ( Stream stream , byte [ ] buffer , bool useAuth , Socks5AuthenticationSettings authenticationSettings , CancellationToken cancellationToken )
198
+ private static int CreateAuthenticationRequest ( byte [ ] buffer , Socks5AuthenticationSettings authenticationSettings , CancellationToken cancellationToken )
179
199
{
180
- await stream . ReadBytesAsync ( buffer , 0 , 2 , cancellationToken ) . ConfigureAwait ( false ) ;
181
- VerifyProtocolVersion ( buffer [ 0 ] ) ;
182
- var method = buffer [ 1 ] ;
183
- if ( method == MethodUsernamePassword )
184
- {
185
- if ( ! useAuth )
186
- {
187
- throw new IOException ( "SOCKS5 proxy requires authentication, but no credentials were provided." ) ;
188
- }
189
- await PerformUsernamePasswordAuthAsync ( stream , buffer , authenticationSettings , cancellationToken ) . ConfigureAwait ( false ) ;
190
- }
191
- else if ( method != MethodNoAuth )
200
+ var usernamePasswordAuthenticationSettings = ( Socks5AuthenticationSettings . UsernamePasswordAuthenticationSettings ) authenticationSettings ;
201
+ var proxyUsername = usernamePasswordAuthenticationSettings . Username ;
202
+ var proxyPassword = usernamePasswordAuthenticationSettings . Password ;
203
+
204
+ buffer [ 0 ] = SubnegotiationVersion ;
205
+ var usernameLength = EncodeString ( proxyUsername , buffer , 2 , nameof ( proxyUsername ) ) ;
206
+ buffer [ 1 ] = usernameLength ;
207
+ var passwordLength = EncodeString ( proxyPassword , buffer , 3 + usernameLength , nameof ( proxyPassword ) ) ;
208
+ buffer [ 2 + usernameLength ] = passwordLength ;
209
+
210
+ return 3 + usernameLength + passwordLength ;
211
+ }
212
+
213
+ private static void ProcessAuthenticationResponse ( byte [ ] buffer )
214
+ {
215
+ if ( buffer [ 0 ] != SubnegotiationVersion || buffer [ 1 ] != Socks5Success )
192
216
{
193
- throw new IOException ( "SOCKS5 proxy requires unsupported authentication method ." ) ;
217
+ throw new IOException ( "SOCKS5 authentication failed ." ) ;
194
218
}
195
219
}
196
220
@@ -231,7 +255,6 @@ private static async Task PerformUsernamePasswordAuthAsync(Stream stream, byte[]
231
255
var authLength = 3 + usernameLength + passwordLength ;
232
256
await stream . WriteAsync ( buffer , 0 , authLength , cancellationToken ) . ConfigureAwait ( false ) ;
233
257
234
-
235
258
await stream . ReadBytesAsync ( buffer , 0 , 2 , cancellationToken ) . ConfigureAwait ( false ) ;
236
259
if ( buffer [ 0 ] != SubnegotiationVersion || buffer [ 1 ] != Socks5Success )
237
260
{
@@ -274,11 +297,11 @@ private static int CreateConnectRequest(byte[] buffer, string targetHost, int ta
274
297
275
298
BinaryPrimitives . WriteUInt16BigEndian ( buffer . AsSpan ( addressLength + 4 ) , ( ushort ) targetPort ) ;
276
299
277
- return addressLength ;
300
+ return addressLength + 6 ;
278
301
}
279
302
280
303
// Reads the SOCKS5 connect response and returns the number of bytes to skip in the buffer.
281
- private static int ReadConnectResponse ( byte [ ] buffer )
304
+ private static int ProcessConnectResponse ( byte [ ] buffer )
282
305
{
283
306
VerifyProtocolVersion ( buffer [ 0 ] ) ;
284
307
0 commit comments