]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_utilities/lib/socks++5.cpp
Security-57337.20.44.tar.gz
[apple/security.git] / OSX / libsecurity_utilities / lib / socks++5.cpp
1 /*
2 * Copyright (c) 2000-2004,2011,2014 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24
25 //
26 // socks++int - internal Socks implementation
27 //
28 #include "socks++5.h"
29 #include "hosts.h"
30
31
32 namespace Security {
33 namespace IPPlusPlus {
34 namespace Socks5 {
35
36
37 //
38 // Socks5 Protocol implementation
39 //
40 void Server::open(Socket &s, Support &my)
41 {
42 s.open(SOCK_STREAM);
43 s.connect(my.mServer->address());
44 secdebug("socks", "%d connected to server %s", s.fd(), string(my.mServer->address()).c_str());
45 Byte request[] = { 5, 1, socksAuthPublic };
46 s.write(request, sizeof(request));
47 Byte reply[2];
48 s.read(reply, sizeof(reply));
49 if (reply[0] != 5 || reply[1] != socksAuthPublic) {
50 secdebug("socks", "%d server failed (v%d auth=%d)", s.fd(), reply[0], reply[1]);
51 s.close();
52 UnixError::throwMe(EPROTONOSUPPORT);
53 }
54 }
55
56 void Server::connect(SocksClientSocket &me, const IPSockAddress &peer)
57 {
58 open(me, me);
59 Message request(socksConnect, peer.address(), peer.port());
60 request.send(me);
61 Message reply(me);
62 me.mLocalAddress = reply.address();
63 me.mPeerAddress = peer;
64 secdebug("socks", "%d socks connected to %s", me.fd(), string(peer).c_str());
65 }
66
67 void Server::connect(SocksClientSocket &me, const Host &host, IPPort port)
68 {
69 #if 1
70 //@@@ should be using Hostname (server resolution) mode, but this won't get us
71 //@@@ any useful peer address to use for bind relaying. Need to rethink this scenario.
72 set<IPAddress> addrs = host.addresses();
73 for (set<IPAddress>::const_iterator it = addrs.begin(); it != addrs.end(); it++) {
74 try {
75 IPSockAddress addr(*it, port);
76 connect(me, addr);
77 return;
78 } catch (const UnixError &err) {
79 errno = err.error;
80 }
81 }
82 // exhausted
83 UnixError::throwMe();
84 #else
85 open(me, me);
86 Message request(socksConnect, host.name().c_str(), port);
87 request.send(me);
88 Message reply(me);
89 me.mLocalAddress = reply.address();
90 //me.mPeerAddress = not provided by Socks5 protocol;
91 secdebug("socks", "%d socks connected to %s", me.fd(), host.name().c_str());
92 #endif
93 }
94
95
96 void Server::bind(SocksServerSocket &me, const IPAddress &peer, IPPort port)
97 {
98 open(me, me);
99 Message request(socksBind, peer, port);
100 request.send(me);
101 Message reply(me);
102 me.mLocalAddress = reply.address();
103 //me.mPeerAddress not available yet;
104 secdebug("socks", "%d socks bound to %s", me.fd(), string(me.mLocalAddress).c_str());
105 }
106
107 void Server::receive(SocksServerSocket &me, SocksClientSocket &receiver)
108 {
109 Message reply(me);
110 receiver.setFd(me.fd(), me.mLocalAddress, reply.address());
111 me.clear(); // clear our own (don't close on destruction)
112 secdebug("socks", "%d socks received from %s", receiver.fd(), string(reply.address()).c_str());
113 }
114
115
116 //
117 // Construct a request from an IPv4 address and port
118 //
119 Message::Message(Command cmd, IPAddress addr, IPPort port)
120 {
121 version = 5;
122 message = cmd;
123 reserved = 0;
124 addressType = socksIPv4;
125 this->addr = addr;
126 this->port = htons(port);
127 length = 4 + sizeof(this->addr) + sizeof(this->port);
128 }
129
130
131 //
132 // Construct a request from a hostname and port (server resolves name)
133 //
134 Message::Message(Command cmd, const char *hostname, IPPort port)
135 {
136 version = 5;
137 message = cmd;
138 reserved = 0;
139 addressType = socksName;
140
141 size_t nameLength = strlen(hostname);
142 if (nameLength > 255)
143 UnixError::throwMe(ENAMETOOLONG);
144 char *addrp = reinterpret_cast<char *>(&addr);
145 addrp[0] = nameLength;
146 memcpy(addrp + 1, hostname, nameLength);
147 IPPort nboPort = htons(port);
148 memcpy(addrp + 1 + nameLength, &nboPort, sizeof(nboPort));
149 length = 4 + 1 + nameLength + sizeof(nboPort);
150 }
151
152
153 //
154 // Send a completed request message
155 //
156 void Message::send(Socket &s)
157 {
158 if (s.write(this, length) != length) {
159 s.close();
160 UnixError::throwMe(EIO);
161 }
162 }
163
164
165 //
166 // Construct a reply object from a socket source.
167 // Throws exceptions if the reply is not successful and supported.
168 //
169 Message::Message(Socket &socket)
170 {
171 length = 4 + sizeof(addr) + sizeof(port); //@@@ calculate if addrType != 1 supported
172
173 if (socket.read(this, length) != length) {
174 socket.close();
175 UnixError::throwMe(EIO);
176 }
177
178 // check error code
179 switch (message) {
180 case socksSuccess:
181 break;
182 case socksDenied:
183 UnixError::throwMe(EPERM);
184 case socksNetUnreach:
185 UnixError::throwMe(ENETUNREACH);
186 case socksHostUnreach:
187 UnixError::throwMe(EHOSTUNREACH);
188 case socksConRefused:
189 UnixError::throwMe(ECONNREFUSED);
190 case socksTTLExpired:
191 UnixError::throwMe(ETIMEDOUT); // not really, but what's better?
192 case socksUnsupported:
193 UnixError::throwMe(EOPNOTSUPP);
194 case socksAddressNotSupported:
195 UnixError::throwMe(EADDRNOTAVAIL);
196 default:
197 UnixError::throwMe(EIO); // what else? :-)
198 }
199
200 // can't deal with non-IPv4 address replies
201 if (addressType != socksIPv4 || reserved != 0)
202 UnixError::throwMe(ENOTSUP);
203 }
204
205
206 } // end namespace Socks
207 } // end namespace IPPlusPlus
208 } // end namespace Security