Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
2498 | - | 1 | /* |
2 | Copyright © 2002, The KPD-Team |
||
3 | All rights reserved. |
||
4 | http://www.mentalis.org/ |
||
5 | |||
6 | Redistribution and use in source and binary forms, with or without |
||
7 | modification, are permitted provided that the following conditions |
||
8 | are met: |
||
9 | |||
10 | - Redistributions of source code must retain the above copyright |
||
11 | notice, this list of conditions and the following disclaimer. |
||
12 | |||
13 | - Neither the name of the KPD-Team, nor the names of its contributors |
||
14 | may be used to endorse or promote products derived from this |
||
15 | software without specific prior written permission. |
||
16 | |||
17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||
18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||
19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
||
20 | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL |
||
21 | THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, |
||
22 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||
23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
||
24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
||
25 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
||
26 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
||
27 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED |
||
28 | OF THE POSSIBILITY OF SUCH DAMAGE. |
||
29 | */ |
||
30 | |||
31 | using System; |
||
32 | using System.Net; |
||
33 | using System.Net.Sockets; |
||
34 | |||
35 | // Implements a number of classes to allow Sockets to connect trough a firewall. |
||
36 | namespace Org.Mentalis.Network.ProxySocket |
||
37 | { |
||
38 | /// <summary> |
||
39 | /// Specifies the type of proxy servers that an instance of the ProxySocket class can use. |
||
40 | /// </summary> |
||
41 | internal enum ProxyTypes |
||
42 | { |
||
43 | /// <summary>No proxy server; the ProxySocket object behaves exactly like an ordinary Socket object.</summary> |
||
44 | None, |
||
45 | /// <summary>A SOCKS4[A] proxy server.</summary> |
||
46 | Socks4, |
||
47 | /// <summary>A SOCKS5 proxy server.</summary> |
||
48 | Socks5 |
||
49 | } |
||
50 | |||
51 | /// <summary> |
||
52 | /// Implements a Socket class that can connect trough a SOCKS proxy server. |
||
53 | /// </summary> |
||
54 | /// <remarks>This class implements SOCKS4[A] and SOCKS5.<br>It does not, however, implement the BIND commands, so you cannot .</br></remarks> |
||
55 | internal class ProxySocket : Socket |
||
56 | { |
||
57 | /// <summary> |
||
58 | /// Initializes a new instance of the ProxySocket class. |
||
59 | /// </summary> |
||
60 | /// <param name="addressFamily">One of the AddressFamily values.</param> |
||
61 | /// <param name="socketType">One of the SocketType values.</param> |
||
62 | /// <param name="protocolType">One of the ProtocolType values.</param> |
||
63 | /// <exception cref="SocketException">The combination of addressFamily, socketType, and protocolType results in an invalid socket.</exception> |
||
64 | public ProxySocket(AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType) : this(addressFamily, socketType, protocolType, "") { } |
||
65 | |||
66 | /// <summary> |
||
67 | /// Initializes a new instance of the ProxySocket class. |
||
68 | /// </summary> |
||
69 | /// <param name="addressFamily">One of the AddressFamily values.</param> |
||
70 | /// <param name="socketType">One of the SocketType values.</param> |
||
71 | /// <param name="protocolType">One of the ProtocolType values.</param> |
||
72 | /// <param name="proxyUsername">The username to use when authenticating with the proxy server.</param> |
||
73 | /// <exception cref="SocketException">The combination of addressFamily, socketType, and protocolType results in an invalid socket.</exception> |
||
74 | /// <exception cref="ArgumentNullException"><c>proxyUsername</c> is null.</exception> |
||
75 | public ProxySocket(AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType, string proxyUsername) : this(addressFamily, socketType, protocolType, proxyUsername, "") { } |
||
76 | |||
77 | /// <summary> |
||
78 | /// Initializes a new instance of the ProxySocket class. |
||
79 | /// </summary> |
||
80 | /// <param name="addressFamily">One of the AddressFamily values.</param> |
||
81 | /// <param name="socketType">One of the SocketType values.</param> |
||
82 | /// <param name="protocolType">One of the ProtocolType values.</param> |
||
83 | /// <param name="proxyUsername">The username to use when authenticating with the proxy server.</param> |
||
84 | /// <param name="proxyPassword">The password to use when authenticating with the proxy server.</param> |
||
85 | /// <exception cref="SocketException">The combination of addressFamily, socketType, and protocolType results in an invalid socket.</exception> |
||
86 | /// <exception cref="ArgumentNullException"><c>proxyUsername</c> -or- <c>proxyPassword</c> is null.</exception> |
||
87 | public ProxySocket(AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType, string proxyUsername, string proxyPassword) |
||
88 | : base(addressFamily, socketType, protocolType) |
||
89 | { |
||
90 | ProxyUser = proxyUsername; |
||
91 | ProxyPass = proxyPassword; |
||
92 | ToThrow = new InvalidOperationException(); |
||
93 | } |
||
94 | |||
95 | /// <summary> |
||
96 | /// Establishes a connection to a remote device. |
||
97 | /// </summary> |
||
98 | /// <param name="remoteEP">An EndPoint that represents the remote device.</param> |
||
99 | /// <exception cref="ArgumentNullException">The remoteEP parameter is a null reference (Nothing in Visual Basic).</exception> |
||
100 | /// <exception cref="SocketException">An operating system error occurs while accessing the Socket.</exception> |
||
101 | /// <exception cref="ObjectDisposedException">The Socket has been closed.</exception> |
||
102 | /// <exception cref="ProxyException">An error occured while talking to the proxy server.</exception> |
||
103 | public new void Connect(EndPoint remoteEP) |
||
104 | { |
||
105 | if (remoteEP == null) |
||
106 | throw new ArgumentNullException("<remoteEP> cannot be null."); |
||
107 | if (this.ProtocolType != ProtocolType.Tcp || ProxyType == ProxyTypes.None || ProxyEndPoint == null) |
||
108 | base.Connect(remoteEP); |
||
109 | else |
||
110 | { |
||
111 | base.Connect(ProxyEndPoint); |
||
112 | if (ProxyType == ProxyTypes.Socks4) |
||
113 | (new Socks4Handler(this, ProxyUser)).Negotiate((IPEndPoint)remoteEP); |
||
114 | else if (ProxyType == ProxyTypes.Socks5) |
||
115 | (new Socks5Handler(this, ProxyUser, ProxyPass)).Negotiate((IPEndPoint)remoteEP); |
||
116 | } |
||
117 | } |
||
118 | |||
119 | /// <summary> |
||
120 | /// Establishes a connection to a remote device. |
||
121 | /// </summary> |
||
122 | /// <param name="host">The remote host to connect to.</param> |
||
123 | /// <param name="port">The remote port to connect to.</param> |
||
124 | /// <exception cref="ArgumentNullException">The host parameter is a null reference (Nothing in Visual Basic).</exception> |
||
125 | /// <exception cref="ArgumentException">The port parameter is invalid.</exception> |
||
126 | /// <exception cref="SocketException">An operating system error occurs while accessing the Socket.</exception> |
||
127 | /// <exception cref="ObjectDisposedException">The Socket has been closed.</exception> |
||
128 | /// <exception cref="ProxyException">An error occured while talking to the proxy server.</exception> |
||
129 | /// <remarks>If you use this method with a SOCKS4 server, it will let the server resolve the hostname. Not all SOCKS4 servers support this 'remote DNS' though.</remarks> |
||
130 | public void Connect(string host, int port) |
||
131 | { |
||
132 | if (host == null) |
||
133 | throw new ArgumentNullException("<host> cannot be null."); |
||
134 | if (port <= 0 || port > 65535) |
||
135 | throw new ArgumentException("Invalid port."); |
||
136 | if (this.ProtocolType != ProtocolType.Tcp || ProxyType == ProxyTypes.None || ProxyEndPoint == null) |
||
137 | base.Connect(new IPEndPoint(Dns.GetHostEntry(host).AddressList[0], port)); |
||
138 | else |
||
139 | { |
||
140 | base.Connect(ProxyEndPoint); |
||
141 | if (ProxyType == ProxyTypes.Socks4) |
||
142 | (new Socks4Handler(this, ProxyUser)).Negotiate(host, port); |
||
143 | else if (ProxyType == ProxyTypes.Socks5) |
||
144 | (new Socks5Handler(this, ProxyUser, ProxyPass)).Negotiate(host, port); |
||
145 | } |
||
146 | } |
||
147 | |||
148 | /// <summary> |
||
149 | /// Begins an asynchronous request for a connection to a network device. |
||
150 | /// </summary> |
||
151 | /// <param name="remoteEP">An EndPoint that represents the remote device.</param> |
||
152 | /// <param name="callback">The AsyncCallback delegate.</param> |
||
153 | /// <param name="state">An object that contains state information for this request.</param> |
||
154 | /// <returns>An IAsyncResult that references the asynchronous connection.</returns> |
||
155 | /// <exception cref="ArgumentNullException">The remoteEP parameter is a null reference (Nothing in Visual Basic).</exception> |
||
156 | /// <exception cref="SocketException">An operating system error occurs while creating the Socket.</exception> |
||
157 | /// <exception cref="ObjectDisposedException">The Socket has been closed.</exception> |
||
158 | public new IAsyncResult BeginConnect(EndPoint remoteEP, AsyncCallback callback, object state) |
||
159 | { |
||
160 | if (remoteEP == null || callback == null) |
||
161 | throw new ArgumentNullException(); |
||
162 | if (this.ProtocolType != ProtocolType.Tcp || ProxyType == ProxyTypes.None || ProxyEndPoint == null) |
||
163 | { |
||
164 | return base.BeginConnect(remoteEP, callback, state); |
||
165 | } |
||
166 | else |
||
167 | { |
||
168 | CallBack = callback; |
||
169 | if (ProxyType == ProxyTypes.Socks4) |
||
170 | { |
||
171 | AsyncResult = (new Socks4Handler(this, ProxyUser)).BeginNegotiate((IPEndPoint)remoteEP, new HandShakeComplete(this.OnHandShakeComplete), ProxyEndPoint); |
||
172 | return AsyncResult; |
||
173 | } |
||
174 | else if (ProxyType == ProxyTypes.Socks5) |
||
175 | { |
||
176 | AsyncResult = (new Socks5Handler(this, ProxyUser, ProxyPass)).BeginNegotiate((IPEndPoint)remoteEP, new HandShakeComplete(this.OnHandShakeComplete), ProxyEndPoint); |
||
177 | return AsyncResult; |
||
178 | } |
||
179 | return null; |
||
180 | } |
||
181 | } |
||
182 | |||
183 | /// <summary> |
||
184 | /// Begins an asynchronous request for a connection to a network device. |
||
185 | /// </summary> |
||
186 | /// <param name="host">The host to connect to.</param> |
||
187 | /// <param name="port">The port on the remote host to connect to.</param> |
||
188 | /// <param name="callback">The AsyncCallback delegate.</param> |
||
189 | /// <param name="state">An object that contains state information for this request.</param> |
||
190 | /// <returns>An IAsyncResult that references the asynchronous connection.</returns> |
||
191 | /// <exception cref="ArgumentNullException">The host parameter is a null reference (Nothing in Visual Basic).</exception> |
||
192 | /// <exception cref="ArgumentException">The port parameter is invalid.</exception> |
||
193 | /// <exception cref="SocketException">An operating system error occurs while creating the Socket.</exception> |
||
194 | /// <exception cref="ObjectDisposedException">The Socket has been closed.</exception> |
||
195 | public IAsyncResult BeginConnect(string host, int port, AsyncCallback callback, object state) |
||
196 | { |
||
197 | if (host == null || callback == null) |
||
198 | throw new ArgumentNullException(); |
||
199 | if (port <= 0 || port > 65535) |
||
200 | throw new ArgumentException(); |
||
201 | CallBack = callback; |
||
202 | if (this.ProtocolType != ProtocolType.Tcp || ProxyType == ProxyTypes.None || ProxyEndPoint == null) |
||
203 | { |
||
204 | RemotePort = port; |
||
205 | AsyncResult = BeginDns(host, new HandShakeComplete(this.OnHandShakeComplete)); |
||
206 | return AsyncResult; |
||
207 | } |
||
208 | else |
||
209 | { |
||
210 | if (ProxyType == ProxyTypes.Socks4) |
||
211 | { |
||
212 | AsyncResult = (new Socks4Handler(this, ProxyUser)).BeginNegotiate(host, port, new HandShakeComplete(this.OnHandShakeComplete), ProxyEndPoint); |
||
213 | return AsyncResult; |
||
214 | } |
||
215 | else if (ProxyType == ProxyTypes.Socks5) |
||
216 | { |
||
217 | AsyncResult = (new Socks5Handler(this, ProxyUser, ProxyPass)).BeginNegotiate(host, port, new HandShakeComplete(this.OnHandShakeComplete), ProxyEndPoint); |
||
218 | return AsyncResult; |
||
219 | } |
||
220 | return null; |
||
221 | } |
||
222 | } |
||
223 | |||
224 | /// <summary> |
||
225 | /// Ends a pending asynchronous connection request. |
||
226 | /// </summary> |
||
227 | /// <param name="asyncResult">Stores state information for this asynchronous operation as well as any user-defined data.</param> |
||
228 | /// <exception cref="ArgumentNullException">The asyncResult parameter is a null reference (Nothing in Visual Basic).</exception> |
||
229 | /// <exception cref="ArgumentException">The asyncResult parameter was not returned by a call to the BeginConnect method.</exception> |
||
230 | /// <exception cref="SocketException">An operating system error occurs while accessing the Socket.</exception> |
||
231 | /// <exception cref="ObjectDisposedException">The Socket has been closed.</exception> |
||
232 | /// <exception cref="InvalidOperationException">EndConnect was previously called for the asynchronous connection.</exception> |
||
233 | /// <exception cref="ProxyException">The proxy server refused the connection.</exception> |
||
234 | public new void EndConnect(IAsyncResult asyncResult) |
||
235 | { |
||
236 | if (asyncResult == null) |
||
237 | throw new ArgumentNullException(); |
||
238 | if (!asyncResult.IsCompleted) |
||
239 | throw new ArgumentException(); |
||
240 | if (ToThrow != null) |
||
241 | throw ToThrow; |
||
242 | return; |
||
243 | } |
||
244 | |||
245 | /// <summary> |
||
246 | /// Begins an asynchronous request to resolve a DNS host name or IP address in dotted-quad notation to an IPAddress instance. |
||
247 | /// </summary> |
||
248 | /// <param name="host">The host to resolve.</param> |
||
249 | /// <param name="callback">The method to call when the hostname has been resolved.</param> |
||
250 | /// <returns>An IAsyncResult instance that references the asynchronous request.</returns> |
||
251 | /// <exception cref="SocketException">There was an error while trying to resolve the host.</exception> |
||
252 | internal IAsyncProxyResult BeginDns(string host, HandShakeComplete callback) |
||
253 | { |
||
254 | try |
||
255 | { |
||
256 | Dns.BeginGetHostEntry(host, new AsyncCallback(this.OnResolved), this); |
||
257 | return new IAsyncProxyResult(); |
||
258 | } |
||
259 | catch |
||
260 | { |
||
261 | throw new SocketException(); |
||
262 | } |
||
263 | } |
||
264 | |||
265 | /// <summary> |
||
266 | /// Called when the specified hostname has been resolved. |
||
267 | /// </summary> |
||
268 | /// <param name="asyncResult">The result of the asynchronous operation.</param> |
||
269 | private void OnResolved(IAsyncResult asyncResult) |
||
270 | { |
||
271 | try |
||
272 | { |
||
273 | IPHostEntry dns = Dns.EndGetHostEntry(asyncResult); |
||
274 | base.BeginConnect(new IPEndPoint(dns.AddressList[0], RemotePort), new AsyncCallback(this.OnConnect), State); |
||
275 | } |
||
276 | catch (Exception e) |
||
277 | { |
||
278 | OnHandShakeComplete(e); |
||
279 | } |
||
280 | } |
||
281 | |||
282 | /// <summary> |
||
283 | /// Called when the Socket is connected to the remote host. |
||
284 | /// </summary> |
||
285 | /// <param name="asyncResult">The result of the asynchronous operation.</param> |
||
286 | private void OnConnect(IAsyncResult asyncResult) |
||
287 | { |
||
288 | try |
||
289 | { |
||
290 | base.EndConnect(asyncResult); |
||
291 | OnHandShakeComplete(null); |
||
292 | } |
||
293 | catch (Exception e) |
||
294 | { |
||
295 | OnHandShakeComplete(e); |
||
296 | } |
||
297 | } |
||
298 | |||
299 | /// <summary> |
||
300 | /// Called when the Socket has finished talking to the proxy server and is ready to relay data. |
||
301 | /// </summary> |
||
302 | /// <param name="error">The error to throw when the EndConnect method is called.</param> |
||
303 | private void OnHandShakeComplete(Exception error) |
||
304 | { |
||
305 | if (error != null) |
||
306 | this.Close(); |
||
307 | ToThrow = error; |
||
308 | AsyncResult.Reset(); |
||
309 | if (CallBack != null) |
||
310 | CallBack(AsyncResult); |
||
311 | } |
||
312 | |||
313 | /// <summary> |
||
314 | /// Gets or sets the EndPoint of the proxy server. |
||
315 | /// </summary> |
||
316 | /// <value>An IPEndPoint object that holds the IP address and the port of the proxy server.</value> |
||
317 | public IPEndPoint ProxyEndPoint |
||
318 | { |
||
319 | get |
||
320 | { |
||
321 | return m_ProxyEndPoint; |
||
322 | } |
||
323 | set |
||
324 | { |
||
325 | m_ProxyEndPoint = value; |
||
326 | } |
||
327 | } |
||
328 | |||
329 | /// <summary> |
||
330 | /// Gets or sets the type of proxy server to use. |
||
331 | /// </summary> |
||
332 | /// <value>One of the ProxyTypes values.</value> |
||
333 | public ProxyTypes ProxyType |
||
334 | { |
||
335 | get |
||
336 | { |
||
337 | return m_ProxyType; |
||
338 | } |
||
339 | set |
||
340 | { |
||
341 | m_ProxyType = value; |
||
342 | } |
||
343 | } |
||
344 | |||
345 | /// <summary> |
||
346 | /// Gets or sets a user-defined object. |
||
347 | /// </summary> |
||
348 | /// <value>The user-defined object.</value> |
||
349 | private object State |
||
350 | { |
||
351 | get |
||
352 | { |
||
353 | return m_State; |
||
354 | } |
||
355 | set |
||
356 | { |
||
357 | m_State = value; |
||
358 | } |
||
359 | } |
||
360 | |||
361 | /// <summary> |
||
362 | /// Gets or sets the username to use when authenticating with the proxy. |
||
363 | /// </summary> |
||
364 | /// <value>A string that holds the username that's used when authenticating with the proxy.</value> |
||
365 | /// <exception cref="ArgumentNullException">The specified value is null.</exception> |
||
366 | public string ProxyUser |
||
367 | { |
||
368 | get |
||
369 | { |
||
370 | return m_ProxyUser; |
||
371 | } |
||
372 | set |
||
373 | { |
||
374 | if (value == null) |
||
375 | throw new ArgumentNullException(); |
||
376 | m_ProxyUser = value; |
||
377 | } |
||
378 | } |
||
379 | |||
380 | /// <summary> |
||
381 | /// Gets or sets the password to use when authenticating with the proxy. |
||
382 | /// </summary> |
||
383 | /// <value>A string that holds the password that's used when authenticating with the proxy.</value> |
||
384 | /// <exception cref="ArgumentNullException">The specified value is null.</exception> |
||
385 | public string ProxyPass |
||
386 | { |
||
387 | get |
||
388 | { |
||
389 | return m_ProxyPass; |
||
390 | } |
||
391 | set |
||
392 | { |
||
393 | if (value == null) |
||
394 | throw new ArgumentNullException(); |
||
395 | m_ProxyPass = value; |
||
396 | } |
||
397 | } |
||
398 | |||
399 | /// <summary> |
||
400 | /// Gets or sets the asynchronous result object. |
||
401 | /// </summary> |
||
402 | /// <value>An instance of the IAsyncProxyResult class.</value> |
||
403 | private IAsyncProxyResult AsyncResult |
||
404 | { |
||
405 | get |
||
406 | { |
||
407 | return m_AsyncResult; |
||
408 | } |
||
409 | set |
||
410 | { |
||
411 | m_AsyncResult = value; |
||
412 | } |
||
413 | } |
||
414 | |||
415 | /// <summary> |
||
416 | /// Gets or sets the exception to throw when the EndConnect method is called. |
||
417 | /// </summary> |
||
418 | /// <value>An instance of the Exception class (or subclasses of Exception).</value> |
||
419 | private Exception ToThrow |
||
420 | { |
||
421 | get |
||
422 | { |
||
423 | return m_ToThrow; |
||
424 | } |
||
425 | set |
||
426 | { |
||
427 | m_ToThrow = value; |
||
428 | } |
||
429 | } |
||
430 | |||
431 | /// <summary> |
||
432 | /// Gets or sets the remote port the user wants to connect to. |
||
433 | /// </summary> |
||
434 | /// <value>An integer that specifies the port the user wants to connect to.</value> |
||
435 | private int RemotePort |
||
436 | { |
||
437 | get |
||
438 | { |
||
439 | return m_RemotePort; |
||
440 | } |
||
441 | set |
||
442 | { |
||
443 | m_RemotePort = value; |
||
444 | } |
||
445 | } |
||
446 | |||
447 | // private variables |
||
448 | /// <summary>Holds the value of the State property.</summary> |
||
449 | private object m_State; |
||
450 | /// <summary>Holds the value of the ProxyEndPoint property.</summary> |
||
451 | private IPEndPoint m_ProxyEndPoint = null; |
||
452 | /// <summary>Holds the value of the ProxyType property.</summary> |
||
453 | private ProxyTypes m_ProxyType = ProxyTypes.None; |
||
454 | /// <summary>Holds the value of the ProxyUser property.</summary> |
||
455 | private string m_ProxyUser = null; |
||
456 | /// <summary>Holds the value of the ProxyPass property.</summary> |
||
457 | private string m_ProxyPass = null; |
||
458 | /// <summary>Holds a pointer to the method that should be called when the Socket is connected to the remote device.</summary> |
||
459 | private AsyncCallback CallBack = null; |
||
460 | /// <summary>Holds the value of the AsyncResult property.</summary> |
||
461 | private IAsyncProxyResult m_AsyncResult; |
||
462 | /// <summary>Holds the value of the ToThrow property.</summary> |
||
463 | private Exception m_ToThrow = null; |
||
464 | /// <summary>Holds the value of the RemotePort property.</summary> |
||
465 | private int m_RemotePort; |
||
466 | } |
||
467 | } |