Added a parameter to semaphore constructor to avoid ambiguity
[pwlib.git] / src / ptclib / socks.cxx
blob801f95082c5bba4be4cd100c45f1bbcec096f306
1 /*
2 * socks.cxx
4 * SOCKS protocol
6 * Portable Windows Library
8 * Copyright (c) 1993-2002 Equivalence Pty. Ltd.
10 * The contents of this file are subject to the Mozilla Public License
11 * Version 1.0 (the "License"); you may not use this file except in
12 * compliance with the License. You may obtain a copy of the License at
13 * http://www.mozilla.org/MPL/
15 * Software distributed under the License is distributed on an "AS IS"
16 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
17 * the License for the specific language governing rights and limitations
18 * under the License.
20 * The Original Code is Portable Windows Library.
22 * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
24 * $Log$
25 * Revision 1.8 2003/09/08 01:42:48 dereksmithies
26 * Add patch from Diego Tartara <dtartara@mens2.hq.novamens.com>. Many Thanks!
28 * Revision 1.7 2002/11/06 22:47:25 robertj
29 * Fixed header comment (copyright etc)
31 * Revision 1.6 2002/08/05 05:40:45 robertj
32 * Fixed missing pragma interface/implementation
34 * Revision 1.5 2001/09/10 02:51:23 robertj
35 * Major change to fix problem with error codes being corrupted in a
36 * PChannel when have simultaneous reads and writes in threads.
38 * Revision 1.4 1999/11/23 08:45:10 robertj
39 * Fixed bug in user/pass authentication version, thanks Dmitry <dipa@linkline.com>
41 * Revision 1.3 1999/02/16 08:08:06 robertj
42 * MSVC 6.0 compatibility changes.
44 * Revision 1.2 1998/12/23 00:35:28 robertj
45 * UDP support.
47 * Revision 1.1 1998/12/22 10:30:24 robertj
48 * Initial revision
52 #include <ptlib.h>
54 #ifdef __GNUC__
55 #pragma implementation "socks.h"
56 #endif
58 #include <ptclib/socks.h>
60 #define new PNEW
62 #define SOCKS_VERSION_4 ((BYTE)4)
63 #define SOCKS_VERSION_5 ((BYTE)5)
65 #define SOCKS_CMD_CONNECT ((BYTE)1)
66 #define SOCKS_CMD_BIND ((BYTE)2)
67 #define SOCKS_CMD_UDP_ASSOCIATE ((BYTE)3)
69 #define SOCKS_AUTH_NONE ((BYTE)0)
70 #define SOCKS_AUTH_USER_PASS ((BYTE)2)
71 #define SOCKS_AUTH_FAILED ((BYTE)0xff)
73 #define SOCKS_ADDR_IPV4 ((BYTE)1)
74 #define SOCKS_ADDR_DOMAINNAME ((BYTE)3)
75 #define SOCKS_ADDR_IPV6 ((BYTE)4)
78 ///////////////////////////////////////////////////////////////////////////////
80 PSocksProtocol::PSocksProtocol(WORD port)
81 : serverHost("proxy")
83 serverPort = DefaultServerPort;
84 remotePort = port;
85 localPort = 0;
87 // get proxy information
88 PConfig config(PConfig::System, "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\");
90 // get the proxy configuration string
91 PString str = config.GetString("Internet Settings", "ProxyServer", "");
92 if (str.Find('=') == P_MAX_INDEX)
93 SetServer("socks");
94 else {
95 PStringArray tokens = str.Tokenise(";");
96 PINDEX i;
97 for (i = 0; i < tokens.GetSize(); i++) {
98 str = tokens[i];
99 PINDEX equalPos = str.Find('=');
100 if (equalPos != P_MAX_INDEX && (str.Left(equalPos) *= "socks")) {
101 SetServer(str.Mid(equalPos+1));
102 break;
109 BOOL PSocksProtocol::SetServer(const PString & hostname, const char * service)
111 return SetServer(hostname, PIPSocket::GetPortByService("tcp", service));
115 BOOL PSocksProtocol::SetServer(const PString & hostname, WORD port)
117 PINDEX colon = hostname.Find(':');
118 if (colon == P_MAX_INDEX)
119 serverHost = hostname;
120 else {
121 unsigned portnum = hostname.Mid(colon+1).AsUnsigned();
122 if (portnum == 0)
123 serverHost = hostname;
124 else {
125 serverHost = hostname.Left(colon);
126 port = (WORD)portnum;
130 if (port == 0)
131 port = DefaultServerPort;
133 serverPort = port;
135 return TRUE;
139 void PSocksProtocol::SetAuthentication(const PString & username, const PString & password)
141 PAssert(authenticationUsername.GetLength() < 256, PInvalidParameter);
142 authenticationUsername = username;
143 PAssert(authenticationPassword.GetLength() < 256, PInvalidParameter);
144 authenticationPassword = password;
148 BOOL PSocksProtocol::ConnectSocksServer(PTCPSocket & socket)
150 PIPSocket::Address ipnum;
151 if (!PIPSocket::GetHostAddress(serverHost, ipnum))
152 return FALSE;
154 remotePort = socket.GetPort();
155 socket.SetPort(serverPort);
156 return socket.PTCPSocket::Connect(0, ipnum);
160 BOOL PSocksProtocol::SendSocksCommand(PTCPSocket & socket,
161 BYTE command,
162 const char * hostname,
163 PIPSocket::Address addr)
165 if (!socket.IsOpen()) {
166 if (!ConnectSocksServer(socket))
167 return FALSE;
169 socket << SOCKS_VERSION_5
170 << (authenticationUsername.IsEmpty() ? '\001' : '\002') // length
171 << SOCKS_AUTH_NONE;
172 if (!authenticationUsername)
173 socket << SOCKS_AUTH_USER_PASS; // Simple cleartext username/password
174 socket.flush();
176 BYTE auth_pdu[2];
177 if (!socket.ReadBlock(auth_pdu, sizeof(auth_pdu))) // Should get 2 byte reply
178 return FALSE;
180 if (auth_pdu[0] != SOCKS_VERSION_5 || auth_pdu[1] == SOCKS_AUTH_FAILED) {
181 socket.Close();
182 SetErrorCodes(PChannel::AccessDenied, EACCES);
183 return FALSE;
186 if (auth_pdu[1] == SOCKS_AUTH_USER_PASS) {
187 // Send username and pasword
188 socket << '\x01'
189 << (BYTE)authenticationUsername.GetLength() // Username length as single byte
190 << authenticationUsername
191 << (BYTE)authenticationPassword.GetLength() // Password length as single byte
192 << authenticationPassword
193 << ::flush;
195 if (!socket.ReadBlock(auth_pdu, sizeof(auth_pdu))) // Should get 2 byte reply
196 return FALSE;
198 if (/*auth_pdu[0] != SOCKS_VERSION_5 ||*/ auth_pdu[1] != 0) {
199 socket.Close();
200 SetErrorCodes(PChannel::AccessDenied, EACCES);
201 return FALSE;
206 socket << SOCKS_VERSION_5
207 << command
208 << '\000'; // Reserved
209 if (hostname != NULL)
210 socket << SOCKS_ADDR_DOMAINNAME << (BYTE)strlen(hostname) << hostname;
211 #if P_HAS_IPV6
212 else if ( addr.GetVersion() == 6 )
214 socket << SOCKS_ADDR_IPV6;
215 /* Should be 16 bytes */
216 for ( PINDEX i = 0; i < addr.GetSize(); i++ )
218 socket << addr[i];
221 #endif
222 else
223 socket << SOCKS_ADDR_IPV4
224 << addr.Byte1() << addr.Byte2() << addr.Byte3() << addr.Byte4();
226 socket << (BYTE)(remotePort >> 8) << (BYTE)remotePort
227 << ::flush;
229 return ReceiveSocksResponse(socket, localAddress, localPort);
233 BOOL PSocksProtocol::ReceiveSocksResponse(PTCPSocket & socket,
234 PIPSocket::Address & addr,
235 WORD & port)
237 int reply;
238 if ((reply = socket.ReadChar()) < 0)
239 return FALSE;
241 if (reply != SOCKS_VERSION_5) {
242 SetErrorCodes(PChannel::Miscellaneous, EINVAL);
243 return FALSE;
246 if ((reply = socket.ReadChar()) < 0)
247 return FALSE;
249 switch (reply) {
250 case 0 : // No error
251 break;
253 case 2 : // Refused permission
254 SetErrorCodes(PChannel::AccessDenied, EACCES);
255 return FALSE;
257 case 3 : // Network unreachable
258 SetErrorCodes(PChannel::NotFound, ENETUNREACH);
259 return FALSE;
261 case 4 : // Host unreachable
262 SetErrorCodes(PChannel::NotFound, EHOSTUNREACH);
263 return FALSE;
265 case 5 : // Connection refused
266 SetErrorCodes(PChannel::NotFound, EHOSTUNREACH);
267 return FALSE;
269 default :
270 SetErrorCodes(PChannel::Miscellaneous, EINVAL);
271 return FALSE;
274 // Ignore next byte (reserved)
275 if ((reply = socket.ReadChar()) < 0)
276 return FALSE;
278 // Get type byte for bound address
279 if ((reply = socket.ReadChar()) < 0)
280 return FALSE;
282 switch (reply) {
283 case SOCKS_ADDR_DOMAINNAME :
284 // Get length
285 if ((reply = socket.ReadChar()) < 0)
286 return FALSE;
288 if (!PIPSocket::GetHostAddress(socket.ReadString(reply), addr))
289 return FALSE;
290 break;
292 case SOCKS_ADDR_IPV4 :
294 in_addr add;
295 if (!socket.ReadBlock(&add, sizeof(add)))
296 return FALSE;
297 addr = add;
299 break;
301 #if P_HAS_IPV6
302 case SOCKS_ADDR_IPV6 :
304 in6_addr add;
305 if (!socket.ReadBlock(&add, sizeof(add)))
306 return FALSE;
307 addr = add;
309 break;
310 #endif
312 default :
313 SetErrorCodes(PChannel::Miscellaneous, EINVAL);
314 return FALSE;
317 WORD rxPort;
318 if (!socket.ReadBlock(&rxPort, sizeof(rxPort)))
319 return FALSE;
321 port = PSocket::Net2Host(rxPort);
322 return TRUE;
326 ///////////////////////////////////////////////////////////////////////////////
328 PSocksSocket::PSocksSocket(WORD port)
329 : PSocksProtocol(port)
334 BOOL PSocksSocket::Connect(const PString & address)
336 if (!SendSocksCommand(*this, SOCKS_CMD_CONNECT, address, 0))
337 return FALSE;
339 port = remotePort;
340 return TRUE;
344 BOOL PSocksSocket::Connect(const Address & addr)
346 if (!SendSocksCommand(*this, SOCKS_CMD_CONNECT, NULL, addr))
347 return FALSE;
349 port = remotePort;
350 return TRUE;
354 BOOL PSocksSocket::Connect(WORD, const Address &)
356 PAssertAlways(PUnsupportedFeature);
357 return FALSE;
361 BOOL PSocksSocket::Listen(unsigned, WORD newPort, Reusability reuse)
363 PAssert(newPort == 0 && port == 0, PUnsupportedFeature);
364 PAssert(reuse, PUnsupportedFeature);
366 if (!SendSocksCommand(*this, SOCKS_CMD_BIND, NULL, 0))
367 return FALSE;
369 port = localPort;
370 return TRUE;
374 BOOL PSocksSocket::Accept()
376 if (!IsOpen())
377 return FALSE;
379 return ReceiveSocksResponse(*this, remoteAddress, remotePort);
383 BOOL PSocksSocket::Accept(PSocket & socket)
385 // If is right class, transfer the SOCKS socket to class to receive the accept
386 // The "listener" socket is implicitly closed as there is really only one
387 // handle in a SOCKS BIND operation.
388 PAssert(socket.IsDescendant(PSocksSocket::Class()), PUnsupportedFeature);
389 os_handle = ((PSocksSocket &)socket).TransferHandle(*this);
390 return Accept();
394 int PSocksSocket::TransferHandle(PSocksSocket & destination)
396 // This "transfers" the socket from one onstance to another.
398 int the_handle = os_handle;
399 destination.SetReadTimeout(readTimeout);
400 destination.SetWriteTimeout(writeTimeout);
402 // Close the instance of the socket but don't actually close handle.
403 os_handle = -1;
405 return the_handle;
409 BOOL PSocksSocket::GetLocalAddress(Address & addr)
411 if (!IsOpen())
412 return FALSE;
414 addr = localAddress;
415 return TRUE;
419 BOOL PSocksSocket::GetLocalAddress(Address & addr, WORD & port)
421 if (!IsOpen())
422 return FALSE;
424 addr = localAddress;
425 port = localPort;
426 return TRUE;
430 BOOL PSocksSocket::GetPeerAddress(Address & addr)
432 if (!IsOpen())
433 return FALSE;
435 addr = remoteAddress;
436 return TRUE;
440 BOOL PSocksSocket::GetPeerAddress(Address & addr, WORD & port)
442 if (!IsOpen())
443 return FALSE;
445 addr = remoteAddress;
446 port = remotePort;
447 return TRUE;
451 void PSocksSocket::SetErrorCodes(PChannel::Errors errCode, int osErr)
453 SetErrorValues(errCode, osErr);
457 ///////////////////////////////////////////////////////////////////////////////
459 PSocks4Socket::PSocks4Socket(WORD port)
460 : PSocksSocket(port)
465 PSocks4Socket::PSocks4Socket(const PString & host, WORD port)
466 : PSocksSocket(port)
468 Connect(host);
472 PObject * PSocks4Socket::Clone() const
474 return new PSocks4Socket(remotePort);
478 BOOL PSocks4Socket::SendSocksCommand(PTCPSocket & socket,
479 BYTE command,
480 const char * hostname,
481 Address addr)
483 if (hostname != NULL) {
484 if (!GetHostAddress(hostname, addr))
485 return FALSE;
488 if (!IsOpen()) {
489 if (!ConnectSocksServer(*this))
490 return FALSE;
493 PString user = PProcess::Current().GetUserName();
494 socket << SOCKS_VERSION_4
495 << command
496 << (BYTE)(remotePort >> 8) << (BYTE)remotePort
497 << addr.Byte1() << addr.Byte2() << addr.Byte3() << addr.Byte4()
498 << user << ((BYTE)0)
499 << ::flush;
501 return ReceiveSocksResponse(socket, localAddress, localPort);
505 BOOL PSocks4Socket::ReceiveSocksResponse(PTCPSocket & socket,
506 Address & addr,
507 WORD & port)
509 int reply;
510 if ((reply = socket.ReadChar()) < 0)
511 return FALSE;
513 if (reply != 0 /*!= SOCKS_VERSION_4*/) {
514 SetErrorCodes(PChannel::Miscellaneous, EINVAL);
515 return FALSE;
518 if ((reply = socket.ReadChar()) < 0)
519 return FALSE;
521 switch (reply) {
522 case 90 : // No error
523 break;
525 case 91 : // Connection refused
526 SetErrorCodes(PChannel::NotFound, EHOSTUNREACH);
527 return FALSE;
529 case 92 : // Refused permission
530 SetErrorCodes(PChannel::AccessDenied, EACCES);
531 return FALSE;
533 default :
534 SetErrorCodes(PChannel::Miscellaneous, EINVAL);
535 return FALSE;
538 WORD rxPort;
539 if (!socket.ReadBlock(&rxPort, sizeof(rxPort)))
540 return FALSE;
542 port = PSocket::Net2Host(rxPort);
544 in_addr add;
545 if ( socket.ReadBlock(&add, sizeof(add)) )
547 addr = add;
548 return TRUE;
551 return FALSE;
555 ///////////////////////////////////////////////////////////////////////////////
557 PSocks5Socket::PSocks5Socket(WORD port)
558 : PSocksSocket(port)
563 PSocks5Socket::PSocks5Socket(const PString & host, WORD port)
564 : PSocksSocket(port)
566 Connect(host);
570 PObject * PSocks5Socket::Clone() const
572 return new PSocks5Socket(remotePort);
576 ///////////////////////////////////////////////////////////////////////////////
578 PSocksUDPSocket::PSocksUDPSocket(WORD port)
579 : PSocksProtocol(port)
584 PSocksUDPSocket::PSocksUDPSocket(const PString & host, WORD port)
585 : PSocksProtocol(port)
587 Connect(host);
591 PObject * PSocksUDPSocket::Clone() const
593 return new PSocksUDPSocket(port);
597 BOOL PSocksUDPSocket::Connect(const PString & address)
599 if (!SendSocksCommand(socksControl, SOCKS_CMD_UDP_ASSOCIATE, address, 0))
600 return FALSE;
602 socksControl.GetPeerAddress(serverAddress);
603 return TRUE;
607 BOOL PSocksUDPSocket::Connect(const Address & addr)
609 if (!SendSocksCommand(socksControl, SOCKS_CMD_UDP_ASSOCIATE, NULL, addr))
610 return FALSE;
612 socksControl.GetPeerAddress(serverAddress);
613 return TRUE;
617 BOOL PSocksUDPSocket::Connect(WORD, const Address &)
619 PAssertAlways(PUnsupportedFeature);
620 return FALSE;
624 BOOL PSocksUDPSocket::Listen(unsigned, WORD newPort, Reusability reuse)
626 PAssert(newPort == 0 && port == 0, PUnsupportedFeature);
627 PAssert(reuse, PUnsupportedFeature);
629 if (!SendSocksCommand(socksControl, SOCKS_CMD_UDP_ASSOCIATE, NULL, 0))
630 return FALSE;
632 socksControl.GetPeerAddress(serverAddress);
633 port = localPort;
634 return TRUE;
638 BOOL PSocksUDPSocket::GetLocalAddress(Address & addr)
640 if (!IsOpen())
641 return FALSE;
643 addr = localAddress;
644 return TRUE;
648 BOOL PSocksUDPSocket::GetLocalAddress(Address & addr, WORD & port)
650 if (!IsOpen())
651 return FALSE;
653 addr = localAddress;
654 port = localPort;
655 return TRUE;
659 BOOL PSocksUDPSocket::GetPeerAddress(Address & addr)
661 if (!IsOpen())
662 return FALSE;
664 addr = remoteAddress;
665 return TRUE;
669 BOOL PSocksUDPSocket::GetPeerAddress(Address & addr, WORD & port)
671 if (!IsOpen())
672 return FALSE;
674 addr = remoteAddress;
675 port = remotePort;
676 return TRUE;
680 BOOL PSocksUDPSocket::ReadFrom(void * buf, PINDEX len, Address & addr, WORD & port)
682 PBYTEArray newbuf(len+262);
683 Address rx_addr;
684 WORD rx_port;
685 if (!PUDPSocket::ReadFrom(newbuf.GetPointer(), newbuf.GetSize(), rx_addr, rx_port))
686 return FALSE;
688 if (rx_addr != serverAddress || rx_port != serverPort)
689 return FALSE;
691 PINDEX port_pos;
692 switch (newbuf[3]) {
693 case SOCKS_ADDR_DOMAINNAME :
694 if (!PIPSocket::GetHostAddress(PString((const char *)&newbuf[5], (PINDEX)newbuf[4]), addr))
695 return FALSE;
697 port_pos = newbuf[4]+5;
698 break;
700 case SOCKS_ADDR_IPV4 :
701 memcpy(&addr, &newbuf[4], 4);
702 port_pos = 4;
703 break;
705 default :
706 SetErrorCodes(PChannel::Miscellaneous, EINVAL);
707 return FALSE;
710 port = (WORD)((newbuf[port_pos] << 8)|newbuf[port_pos+1]);
711 memcpy(buf, &newbuf[port_pos+2], len);
713 return TRUE;
717 BOOL PSocksUDPSocket::WriteTo(const void * buf, PINDEX len, const Address & addr, WORD port)
719 PBYTEArray newbuf(len+10);
720 BYTE * bufptr = newbuf.GetPointer();
722 // Build header, bytes 0, 1 & 2 are zero
724 bufptr[3] = SOCKS_ADDR_IPV4;
725 memcpy(bufptr+4, &addr, 4);
726 bufptr[8] = (BYTE)(port >> 8);
727 bufptr[9] = (BYTE)port;
728 memcpy(bufptr+10, buf, len);
730 return PUDPSocket::WriteTo(newbuf, newbuf.GetSize(), serverAddress, serverPort);
734 void PSocksUDPSocket::SetErrorCodes(PChannel::Errors errCode, int osErr)
736 SetErrorValues(errCode, osErr);
740 // End of File ///////////////////////////////////////////////////////////////