// File: /workspace/github_c_makefile_star2_O2/enzo1982[P]smooth/classes/io/drivers/driver_socks5.cpp
// Function: smooth::IO::DriverSOCKS5::DriverSOCKS5(smooth::String const&, int, smooth::String const&, int, smooth::String const&, smooth::String const&)
S::IO::DriverSOCKS5::DriverSOCKS5(const String &proxy, Int socksPort, const String &hostName, Int port, const String &uname, const String &passwd) : Driver()
{
	closeStream = False;

	stream	    = -1;
	size	    = -1;

	if (hostName == NIL || hostName.Length() > 255) { lastError = IO_ERROR_BADPARAM; return; }

	/* Open TCP/IP socket.
	 */
	stream = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

	if (stream == (unsigned) (~0)) { lastError = IO_ERROR_UNEXPECTED; return; }

	closeStream = True;

	/* Get proxy hostname.
	 */
	hostent		*host = gethostbyname(proxy);

	if (host == NIL) { Close(); lastError = IO_ERROR_UNEXPECTED; return; }

	/* Connect to proxy.
	 */
	sockaddr_in	 saddr;

	saddr.sin_family = AF_INET;
	saddr.sin_addr	 = *((in_addr *) *host->h_addr_list);
	saddr.sin_port	 = htons((short) socksPort);

	memset(&saddr.sin_zero, 0, sizeof(saddr.sin_zero));

	if (connect(stream, (sockaddr *) &saddr, sizeof(struct sockaddr)) == -1) { Close(); lastError = IO_ERROR_UNEXPECTED; return; }

	/* Send connect request.
	 */
	if (uname == NIL)
	{
		unsigned char	*socksdata = new unsigned char [3];

		socksdata[0] = 5;	// SOCKS version 5
		socksdata[1] = 1;	// One authentication method
		socksdata[2] = 0x00;	// Method 1: No authentication

		if (send(stream, (char *) socksdata, 3, 0) < 3) { delete [] socksdata; Close(); lastError = IO_ERROR_UNEXPECTED; return; }

		/* Read and evaluate response.
		 */
		int	 recbytes = 0;

		while (recbytes != 2)
		{
			int	 bytes = recv(stream, (char *) socksdata + recbytes, 2 - recbytes, 0);

			if (bytes <= 0) { delete [] socksdata; Close(); lastError = IO_ERROR_UNEXPECTED; return; }

			recbytes += bytes;
		}

		/* Check if proxy requires authentication.
		 */
		if (socksdata[1] == 0xFF) { delete [] socksdata; Close(); lastError = IO_ERROR_UNEXPECTED; return; }

		delete [] socksdata;
	}
	else
	{
		unsigned char	*socksdata = new unsigned char [4];

		socksdata[0] = 5;	// SOCKS version 5
		socksdata[1] = 2;	// Two authentication methods
		socksdata[2] = 0x00;	// Method 0x00: No authentication
		socksdata[3] = 0x02;	// Method 0x02: Username / password

		if (send(stream, (char *) socksdata, 4, 0) < 4) { delete [] socksdata; Close(); lastError = IO_ERROR_UNEXPECTED; return; }

		/* Read and evaluate response.
		 */
		int	 recbytes = 0;

		while (recbytes != 2)
		{
			int	 bytes = recv(stream, (char *) socksdata + recbytes, 2 - recbytes, 0);

			if (bytes <= 0) { delete [] socksdata; Close(); lastError = IO_ERROR_UNEXPECTED; return; }

			recbytes += bytes;
		}

		/* Check if proxy accepted authentication method.
		 */
		if (socksdata[1] == 0xFF) { delete [] socksdata; Close(); lastError = IO_ERROR_UNEXPECTED; return; }

		if (socksdata[1] == 0x02)
		{
			/* Method 0x02: Username / password
			 */
			delete [] socksdata;

			Int	 unameLen  = strlen(uname);
			Int	 passwdLen = passwd != NIL ? strlen(passwd) : 0;

			socksdata = new unsigned char [3 + unameLen + passwdLen];

			socksdata[0] = 1;
			socksdata[1] = unameLen;

			for (Int i = 0; i < unameLen; i++) socksdata[2 + i] = uname[i];

			socksdata[2 + unameLen] = passwdLen;

			for (Int i = 0; i < passwdLen; i++) socksdata[3 + unameLen + i] = passwd[i];

			if (send(stream, (char *) socksdata, 3 + unameLen + passwdLen, 0) < 3 + unameLen + passwdLen) { delete [] socksdata; Close(); lastError = IO_ERROR_UNEXPECTED; return; }

			/* Read and evaluate response.
			 */
			int	 recbytes = 0;

			while (recbytes != 2)
			{
				int	 bytes = recv(stream, (char *) socksdata + recbytes, 2 - recbytes, 0);

				if (bytes <= 0) { delete [] socksdata; Close(); lastError = IO_ERROR_UNEXPECTED; return; }

				recbytes += bytes;
			}

			/* Check if proxy accepted username/password.
			 */
			if (socksdata[1] != 0x00) { delete [] socksdata; Close(); lastError = IO_ERROR_UNEXPECTED; return; }
		}

		delete [] socksdata;
	}

	/* Send connect request.
	 */
	unsigned char	*socksdata = new unsigned char [5 + 255 + 2];

	Int	 hostNameLen = strlen(hostName);

	socksdata[0] = 5;	// SOCKS version 5
	socksdata[1] = 0x01;	// Command: Connect
	socksdata[2] = 0x00;	// Reserved field
	socksdata[3] = 0x03;	// Connect type: Domain name
	socksdata[4] = hostNameLen;

	for (Int i = 0; i < hostNameLen; i++) socksdata[5 + i] = hostName[i];

	socksdata[5 + hostNameLen] = htons((short) port) % 256;
	socksdata[6 + hostNameLen] = htons((short) port) / 256;

	if (send(stream, (char *) socksdata, 7 + hostNameLen, 0) < 7 + hostNameLen) { delete [] socksdata; Close(); lastError = IO_ERROR_UNEXPECTED; return; }

	/* Receive and evaluate first 4 bytes of answer.
	 */
	int	 recbytes = 0;

	while (recbytes != 4)
	{
		int	 bytes = recv(stream, (char *) socksdata + recbytes, 4 - recbytes, 0);

		if (bytes <= 0) { delete [] socksdata; Close(); lastError = IO_ERROR_UNEXPECTED; return; }

		recbytes += bytes;
	}

	/* Check if connect attempt was successful.
	 */
	if (socksdata[1] != 0x00) { delete [] socksdata; Close(); lastError = IO_ERROR_UNEXPECTED; return; }

	/* Receive rest of answer.
	 */
	if (socksdata[3] == 0x01)
	{
		/* Read IP address and port.
		 */
		int	 recbytes = 0;

		while (recbytes != 6)
		{
			int	 bytes = recv(stream, (char *) socksdata + 4 + recbytes, 6 - recbytes, 0);

			if (bytes <= 0) { delete [] socksdata; Close(); lastError = IO_ERROR_UNEXPECTED; return; }

			recbytes += bytes;
		}
	}
	else if (socksdata[3] == 0x03)
	{
		/* Read length of hostname.
		 */
		if (recv(stream, (char *) socksdata + 4, 1, 0) <= 0) { delete [] socksdata; Close(); lastError = IO_ERROR_UNEXPECTED; return; }

		/* Read hostname and port.
		 */
		int	 neededbytes = Math::Min(255, socksdata[4]) + 2;
		int	 recbytes    = 0;

		while (recbytes != neededbytes)
		{
			int	 bytes = recv(stream, (char *) socksdata + 5 + recbytes, neededbytes - recbytes, 0);

			if (bytes <= 0) { delete [] socksdata; Close(); lastError = IO_ERROR_UNEXPECTED; return; }

			recbytes += bytes;
		}
	}

	delete [] socksdata;
}

// Assembly:
pushq %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0x38, %rsp
movq %r9, %r12
movl %r8d, 0x1c(%rsp)
movq %rcx, %r14
movl %edx, %ebp
movq %rsi, %r13
movq %rdi, %rbx
callq 0x59850
movq 0xc45db(%rip), %rax     # 0x161c38
addq $0x10, %rax
movq %rax, (%rbx)
movb $0x0, 0x3c(%rbx)
orl $-0x1, 0x38(%rbx)
orq $-0x1, 0x8(%rbx)
movq %r14, %rdi
xorl %esi, %esi
callq 0x56280
testb %al, %al
jne 0x9d68e
movq %r14, %rdi
callq 0x58370
cmpl $0x100, %eax            # imm = 0x100
jl 0x9d69a
movl $0x2, 0x18(%rbx)
jmp 0x9db21
pushq $0x2
popq %rdi
pushq $0x1
popq %rsi
pushq $0x6
popq %rdx
callq 0x57780
movl %eax, 0x38(%rbx)
cmpl $-0x1, %eax
je 0x9db1a
movb $0x1, 0x3c(%rbx)
movq %r13, %rdi
callq 0x598d0
movq %rax, %rdi
callq 0x57640
testq %rax, %rax
je 0x9d752
leaq 0x28(%rsp), %rsi
movw $0x2, (%rsi)
movq 0x18(%rax), %rax
movq (%rax), %rax
movl (%rax), %eax
movl %eax, 0x4(%rsi)
rolw $0x8, %bp
movw %bp, 0x2(%rsi)
andq $0x0, 0x8(%rsi)
movl 0x38(%rbx), %edi
pushq $0x10
popq %rdx
callq 0x57320
cmpl $-0x1, %eax
je 0x9d760
movq %r12, %rdi
xorl %esi, %esi
callq 0x56280
testb %al, %al
je 0x9d76e
pushq $0x3
popq %rdi
callq 0x577a0
movq %rax, %r12
movw $0x105, (%rax)          # imm = 0x105
movb $0x0, 0x2(%rax)
movl 0x38(%rbx), %edi
pushq $0x3
popq %rdx
movq %rax, %rsi
xorl %ecx, %ecx
callq 0x57220
cmpq $0x3, %rax
jge 0x9d7ab
movq %r12, %rdi
callq 0x572d0
movq (%rbx), %rax
movq %rbx, %rdi
callq *0x38(%rax)
jmp 0x9db1a
movq (%rbx), %rax
movq %rbx, %rdi
callq *0x38(%rax)
jmp 0x9db1a
movq (%rbx), %rax
movq %rbx, %rdi
callq *0x38(%rax)
jmp 0x9db1a
pushq $0x4
popq %rdi
callq 0x577a0
movq %rax, %rbp
movl $0x2000205, (%rax)      # imm = 0x2000205
movl 0x38(%rbx), %edi
pushq $0x4
popq %rdx
movq %rax, %rsi
xorl %ecx, %ecx
callq 0x57220
cmpq $0x4, %rax
jge 0x9d7e5
movq %rbp, %rdi
callq 0x572d0
movq (%rbx), %rax
movq %rbx, %rdi
callq *0x38(%rax)
jmp 0x9db1a
xorl %ebp, %ebp
cmpl $0x2, %ebp
je 0x9d824
movl 0x38(%rbx), %edi
movl %ebp, %esi
addq %r12, %rsi
pushq $0x2
popq %rax
subl %ebp, %eax
movslq %eax, %rdx
xorl %ecx, %ecx
callq 0x59580
addl %eax, %ebp
testl %eax, %eax
jg 0x9d7ad
movq %r12, %rdi
callq 0x572d0
movq (%rbx), %rax
movq %rbx, %rdi
callq *0x38(%rax)
jmp 0x9db1a
xorl %r13d, %r13d
cmpl $0x2, %r13d
je 0x9d849
movl 0x38(%rbx), %edi
movl %r13d, %esi
addq %rbp, %rsi
pushq $0x2
popq %rax
subl %r13d, %eax
movslq %eax, %rdx
xorl %ecx, %ecx
callq 0x59580
addl %eax, %r13d
testl %eax, %eax
jg 0x9d7e8
movq %rbp, %rdi
callq 0x572d0
movq (%rbx), %rax
movq %rbx, %rdi
callq *0x38(%rax)
jmp 0x9db1a
movb 0x1(%r12), %bpl
movq %r12, %rdi
callq 0x572d0
cmpb $-0x1, %bpl
jne 0x9d9ee
movq (%rbx), %rax
movq %rbx, %rdi
callq *0x38(%rax)
jmp 0x9db1a
movzbl 0x1(%rbp), %eax
cmpl $0x2, %eax
je 0x9d873
cmpl $0xff, %eax
jne 0x9d9e6
movq %rbp, %rdi
callq 0x572d0
movq (%rbx), %rax
movq %rbx, %rdi
callq *0x38(%rax)
jmp 0x9db1a
movq %rbp, %rdi
callq 0x572d0
movq %r12, %rdi
callq 0x598d0
movq %rax, %rdi
callq 0x57460
movq %rax, (%rsp)
movq $0x0, 0x10(%rsp)
movq 0x70(%rsp), %rdi
xorl %esi, %esi
callq 0x57d40
testb %al, %al
je 0x9d8bf
movq 0x70(%rsp), %rdi
callq 0x598d0
movq %rax, %rdi
callq 0x57460
movq %rax, 0x10(%rsp)
movslq (%rsp), %rcx
addq $0x3, %rcx
movq 0x10(%rsp), %rax
movq %rcx, 0x8(%rsp)
addl %ecx, %eax
movslq %eax, %r15
movq %r15, %rdi
callq 0x577a0
movq %rax, %rbp
movq %r15, 0x20(%rsp)
movb $0x1, (%rax)
movq (%rsp), %rax
movb %al, 0x1(%rbp)
xorl %r13d, %r13d
testl %eax, %eax
movl $0x0, %r15d
cmovgl %eax, %r15d
cmpq %r13, %r15
je 0x9d919
movq %r12, %rdi
movl %r13d, %esi
callq 0x59270
movb %al, 0x2(%rbp,%r13)
incq %r13
jmp 0x9d8ff
movq (%rsp), %rax
addl $0x2, %eax
cltq
movq 0x10(%rsp), %r15
movb %r15b, (%rbp,%rax)
xorl %r12d, %r12d
testl %r15d, %r15d
cmovlel %r12d, %r15d
addq %rbp, 0x8(%rsp)
cmpq %r12, %r15
je 0x9d95b
movq 0x70(%rsp), %rdi
movl %r12d, %esi
callq 0x59270
movq 0x8(%rsp), %rcx
movb %al, (%rcx,%r12)
incq %r12
jmp 0x9d93b
movl 0x38(%rbx), %edi
movq %rbp, %rsi
movq 0x20(%rsp), %r15
movq %r15, %rdx
xorl %ecx, %ecx
callq 0x57220
cmpq %r15, %rax
jge 0x9d98b
movq %rbp, %rdi
callq 0x572d0
movq (%rbx), %rax
movq %rbx, %rdi
callq *0x38(%rax)
jmp 0x9db1a
xorl %r12d, %r12d
cmpl $0x2, %r12d
je 0x9d9ca
movl 0x38(%rbx), %edi
movl %r12d, %esi
addq %rbp, %rsi
pushq $0x2
popq %rax
subl %r12d, %eax
movslq %eax, %rdx
xorl %ecx, %ecx
callq 0x59580
addl %eax, %r12d
testl %eax, %eax
jg 0x9d98e
movq %rbp, %rdi
callq 0x572d0
movq (%rbx), %rax
movq %rbx, %rdi
callq *0x38(%rax)
jmp 0x9db1a
cmpb $0x0, 0x1(%rbp)
je 0x9d9e6
movq %rbp, %rdi
callq 0x572d0
movq (%rbx), %rax
movq %rbx, %rdi
callq *0x38(%rax)
jmp 0x9db1a
movq %rbp, %rdi
callq 0x572d0
movl $0x106, %edi            # imm = 0x106
callq 0x577a0
movq %rax, %rbp
movq %r14, %rdi
callq 0x598d0
movq %rax, %rdi
callq 0x57460
movq %rax, %r12
movl $0x3000105, (%rbp)      # imm = 0x3000105
leaq 0x4(%rbp), %rax
movq %rax, 0x8(%rsp)
movb %r12b, 0x4(%rbp)
leaq 0x5(%rbp), %rax
movq %rax, (%rsp)
xorl %r13d, %r13d
testl %r12d, %r12d
movl $0x0, %r15d
cmovgl %r12d, %r15d
cmpq %r13, %r15
je 0x9da54
movq %r14, %rdi
movl %r13d, %esi
callq 0x59270
movb %al, 0x5(%rbp,%r13)
incq %r13
jmp 0x9da3a
movl 0x1c(%rsp), %ecx
rolw $0x8, %cx
shlq $0x20, %r12
movabsq $0x500000000, %rax      # imm = 0x500000000
addq %r12, %rax
sarq $0x20, %rax
movb %cl, (%rbp,%rax)
movabsq $0x600000000, %rax      # imm = 0x600000000
addq %r12, %rax
sarq $0x20, %rax
movb %ch, (%rbp,%rax)
movl 0x38(%rbx), %edi
movabsq $0x700000000, %r14      # imm = 0x700000000
addq %r12, %r14
sarq $0x20, %r14
movq %rbp, %rsi
movq %r14, %rdx
xorl %ecx, %ecx
callq 0x57220
cmpq %r14, %rax
jge 0x9dac3
movq %rbp, %rdi
callq 0x572d0
movq (%rbx), %rax
movq %rbx, %rdi
callq *0x38(%rax)
jmp 0x9db1a
xorl %r14d, %r14d
pushq $0x4
popq %r12
cmpl $0x4, %r14d
je 0x9db03
movl 0x38(%rbx), %edi
movl %r14d, %esi
addq %rbp, %rsi
movl %r12d, %eax
subl %r14d, %eax
movslq %eax, %rdx
xorl %ecx, %ecx
callq 0x59580
addl %eax, %r14d
testl %eax, %eax
jg 0x9daca
movq %rbp, %rdi
callq 0x572d0
movq (%rbx), %rax
movq %rbx, %rdi
callq *0x38(%rax)
jmp 0x9db1a
cmpb $0x0, 0x1(%rbp)
je 0x9db30
movq %rbp, %rdi
callq 0x572d0
movq (%rbx), %rax
movq %rbx, %rdi
callq *0x38(%rax)
movl $0x3, 0x18(%rbx)
addq $0x38, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
movzbl 0x3(%rbp), %eax
cmpl $0x3, %eax
je 0x9db8b
cmpl $0x1, %eax
pushq $0x6
popq %r15
movq 0x8(%rsp), %r12
jne 0x9dbef
xorl %r14d, %r14d
cmpl $0x6, %r14d
je 0x9dbef
movl 0x38(%rbx), %edi
movl %r14d, %esi
addq %r12, %rsi
movl %r15d, %eax
subl %r14d, %eax
movslq %eax, %rdx
xorl %ecx, %ecx
callq 0x59580
addl %eax, %r14d
testl %eax, %eax
jg 0x9db4e
movq %rbp, %rdi
callq 0x572d0
movq (%rbx), %rax
movq %rbx, %rdi
callq *0x38(%rax)
jmp 0x9db1a
movl 0x38(%rbx), %edi
pushq $0x1
popq %rdx
movq 0x8(%rsp), %r14
movq %r14, %rsi
xorl %ecx, %ecx
callq 0x59580
testq %rax, %rax
movq (%rsp), %r12
jle 0x9dbfc
movzbl (%r14), %r14d
addq $0x2, %r14
xorl %r15d, %r15d
cmpl %r15d, %r14d
je 0x9dbef
movl 0x38(%rbx), %edi
movl %r15d, %esi
addq %r12, %rsi
movslq %r15d, %rax
movq %r14, %rdx
subq %rax, %rdx
xorl %ecx, %ecx
callq 0x59580
addl %eax, %r15d
testl %eax, %eax
jg 0x9dbb4
movq %rbp, %rdi
callq 0x572d0
movq (%rbx), %rax
movq %rbx, %rdi
callq *0x38(%rax)
jmp 0x9db1a
movq %rbp, %rdi
callq 0x572d0
jmp 0x9db21
movq %rbp, %rdi
callq 0x572d0
movq (%rbx), %rax
movq %rbx, %rdi
callq *0x38(%rax)
jmp 0x9db1a
jmp 0x9dc48
jmp 0x9dc48
jmp 0x9dc48
jmp 0x9dc48
jmp 0x9dc48
jmp 0x9dc48
jmp 0x9dc48
jmp 0x9dc48
jmp 0x9dc48
jmp 0x9dc48
jmp 0x9dc48
jmp 0x9dc48
jmp 0x9dc48
jmp 0x9dc48
jmp 0x9dc48
jmp 0x9dc48
jmp 0x9dc48
jmp 0x9dc48
jmp 0x9dc48
jmp 0x9dc48
jmp 0x9dc48
jmp 0x9dc48
jmp 0x9dc48
jmp 0x9dc48
jmp 0x9dc48
jmp 0x9dc48
jmp 0x9dc48
movq %rax, %r14
movq %rbx, %rdi
callq 0x57db0
movq %r14, %rdi
callq 0x58260
nop