// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package socks

import (
	
	
	
	
	
	
)

var (
	noDeadline   = time.Time{}
	aLongTimeAgo = time.Unix(1, 0)
)

func ( *Dialer) ( context.Context,  net.Conn,  string) ( net.Addr,  error) {
	, ,  := splitHostPort()
	if  != nil {
		return nil, 
	}
	if ,  := .Deadline();  && !.IsZero() {
		.SetDeadline()
		defer .SetDeadline(noDeadline)
	}
	if  != context.Background() {
		 := make(chan error, 1)
		 := make(chan struct{})
		defer func() {
			close()
			if  == nil {
				 = <-
			}
		}()
		go func() {
			select {
			case <-.Done():
				.SetDeadline(aLongTimeAgo)
				 <- .Err()
			case <-:
				 <- nil
			}
		}()
	}

	 := make([]byte, 0, 6+len()) // the size here is just an estimate
	 = append(, Version5)
	if len(.AuthMethods) == 0 || .Authenticate == nil {
		 = append(, 1, byte(AuthMethodNotRequired))
	} else {
		 := .AuthMethods
		if len() > 255 {
			return nil, errors.New("too many authentication methods")
		}
		 = append(, byte(len()))
		for ,  := range  {
			 = append(, byte())
		}
	}
	if _,  = .Write();  != nil {
		return
	}

	if _,  = io.ReadFull(, [:2]);  != nil {
		return
	}
	if [0] != Version5 {
		return nil, errors.New("unexpected protocol version " + strconv.Itoa(int([0])))
	}
	 := AuthMethod([1])
	if  == AuthMethodNoAcceptableMethods {
		return nil, errors.New("no acceptable authentication methods")
	}
	if .Authenticate != nil {
		if  = .Authenticate(, , );  != nil {
			return
		}
	}

	 = [:0]
	 = append(, Version5, byte(.cmd), 0)
	if  := net.ParseIP();  != nil {
		if  := .To4();  != nil {
			 = append(, AddrTypeIPv4)
			 = append(, ...)
		} else if  := .To16();  != nil {
			 = append(, AddrTypeIPv6)
			 = append(, ...)
		} else {
			return nil, errors.New("unknown address type")
		}
	} else {
		if len() > 255 {
			return nil, errors.New("FQDN too long")
		}
		 = append(, AddrTypeFQDN)
		 = append(, byte(len()))
		 = append(, ...)
	}
	 = append(, byte(>>8), byte())
	if _,  = .Write();  != nil {
		return
	}

	if _,  = io.ReadFull(, [:4]);  != nil {
		return
	}
	if [0] != Version5 {
		return nil, errors.New("unexpected protocol version " + strconv.Itoa(int([0])))
	}
	if  := Reply([1]);  != StatusSucceeded {
		return nil, errors.New("unknown error " + .String())
	}
	if [2] != 0 {
		return nil, errors.New("non-zero reserved field")
	}
	 := 2
	var  Addr
	switch [3] {
	case AddrTypeIPv4:
		 += net.IPv4len
		.IP = make(net.IP, net.IPv4len)
	case AddrTypeIPv6:
		 += net.IPv6len
		.IP = make(net.IP, net.IPv6len)
	case AddrTypeFQDN:
		if ,  := io.ReadFull(, [:1]);  != nil {
			return nil, 
		}
		 += int([0])
	default:
		return nil, errors.New("unknown address type " + strconv.Itoa(int([3])))
	}
	if cap() <  {
		 = make([]byte, )
	} else {
		 = [:]
	}
	if _,  = io.ReadFull(, );  != nil {
		return
	}
	if .IP != nil {
		copy(.IP, )
	} else {
		.Name = string([:len()-2])
	}
	.Port = int([len()-2])<<8 | int([len()-1])
	return &, nil
}

func ( string) (string, int, error) {
	, ,  := net.SplitHostPort()
	if  != nil {
		return "", 0, 
	}
	,  := strconv.Atoi()
	if  != nil {
		return "", 0, 
	}
	if 1 >  ||  > 0xffff {
		return "", 0, errors.New("port number out of range " + )
	}
	return , , nil
}