ObjFW  Diff

Differences From Artifact [8b7939a5bd]:

To Artifact [f0cdd75885]:


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
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







+


+
+
+

+
+


+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+





-
+

+
+
-
+
+
+


-
+
-

-
-
+








-
-
+
+
+

-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+

-
-
+
+
+
+

-
-
+











-
-
+


-
-
+
+


-
-
+








-
+


 * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
 * file.
 */

#include "config.h"

#import "OFTCPSocket+SOCKS5.h"
#import "OFDataArray.h"

#import "OFConnectionFailedException.h"
#import "OFOutOfRangeException.h"
#import "OFReadFailedException.h"
#import "OFWriteFailedException.h"

#import "socket_helpers.h"

/* Reference for static linking */
int _OFTCPSocket_SOCKS5_reference;

static void
send_or_exception(OFTCPSocket *self, int socket, char *buffer, size_t length)
{
	if (send(socket, buffer, length, 0) != length)
		@throw [OFWriteFailedException
		    exceptionWithObject: self
			requestedLength: length
				  errNo: of_socket_errno()];
}

static void
recv_exact(OFTCPSocket *self, int socket, char *buffer, size_t length)
{
	while (length > 0) {
		ssize_t ret = recv(socket, buffer, length, 0);

		if (ret < 0)
			@throw [OFReadFailedException
			    exceptionWithObject: self
				requestedLength: length
					  errNo: of_socket_errno()];

		buffer += ret;
		length -= ret;
	}
}

@implementation OFTCPSocket (SOCKS5)
- (void)OF_SOCKS5ConnectToHost: (OFString*)host
			  port: (uint16_t)port
{
	const char request[] = { 5, 1, 0, 3 };
	char request[] = { 5, 1, 0, 3 };
	char reply[256];
	void *pool;
	OFDataArray *connectRequest;
	bool wasWriteBuffered;

	if ([host UTF8StringLength] > 255)
		@throw [OFOutOfRangeException exception];

	/* 5 1 0 -> no authentication */
	[self writeBuffer: request
	send_or_exception(self, _socket, request, 3);
		   length: 3];

	[self readIntoBuffer: reply
		 exactLength: 2];
	recv_exact(self, _socket, reply, 2);

	if (reply[0] != 5 || reply[1] != 0) {
		[self close];
		@throw [OFConnectionFailedException exceptionWithHost: host
								 port: port
							       socket: self];
	}

	wasWriteBuffered = [self isWriteBuffered];
	[self setWriteBuffered: true];
	/* CONNECT request */
	pool = objc_autoreleasePoolPush();
	connectRequest = [OFDataArray dataArray];

	/* CONNECT request */
	[self writeBuffer: request
		   length: 4];
	[self writeInt8: [host UTF8StringLength]];
	[self writeBuffer: [host UTF8String]
		   length: [host UTF8StringLength]];
	[self writeBigEndianInt16: port];
	[connectRequest addItems: request
			   count: 4];

	request[0] = [host UTF8StringLength];
	[connectRequest addItem: request];
	[connectRequest addItems: [host UTF8String]
			   count: request[0]];

	request[0] = port >> 8;
	request[1] = port & 0xFF;
	[connectRequest addItems: request
			   count: 2];

	[self flushWriteBuffer];
	[self setWriteBuffered: wasWriteBuffered];
	send_or_exception(self, _socket,
	    [connectRequest items], [connectRequest count]);

	objc_autoreleasePoolPop(pool);

	[self readIntoBuffer: reply
		 exactLength: 4];
	recv_exact(self, _socket, reply, 4);

	if (reply[0] != 5 || reply[1] != 0 || reply[2] != 0) {
		[self close];
		@throw [OFConnectionFailedException exceptionWithHost: host
								 port: port
							       socket: self];
	}

	/* Skip the rest of the reply */
	switch (reply[3]) {
	case 1: /* IPv4 */
		[self readIntoBuffer: reply
			 exactLength: 4];
		recv_exact(self, _socket, reply, 4);
		break;
	case 3: /* Domainname */
		[self readIntoBuffer: reply
			 exactLength: [self readInt8]];
		recv_exact(self, _socket, reply, 1);
		recv_exact(self, _socket, reply, reply[0]);
		break;
	case 4: /* IPv6 */
		[self readIntoBuffer: reply
			 exactLength: 16];
		recv_exact(self, _socket, reply, 16);
		break;
	default:
		[self close];
		@throw [OFConnectionFailedException exceptionWithHost: host
								 port: port
							       socket: self];
	}

	[self readBigEndianInt16];
	recv_exact(self, _socket, reply, 2);
}
@end