mirror of
				https://github.com/containers/podman.git
				synced 2025-11-01 02:42:11 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			211 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			211 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright 2011 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 proxy
 | |
| 
 | |
| import (
 | |
| 	"errors"
 | |
| 	"io"
 | |
| 	"net"
 | |
| 	"strconv"
 | |
| )
 | |
| 
 | |
| // SOCKS5 returns a Dialer that makes SOCKSv5 connections to the given address
 | |
| // with an optional username and password. See RFC 1928.
 | |
| func SOCKS5(network, addr string, auth *Auth, forward Dialer) (Dialer, error) {
 | |
| 	s := &socks5{
 | |
| 		network: network,
 | |
| 		addr:    addr,
 | |
| 		forward: forward,
 | |
| 	}
 | |
| 	if auth != nil {
 | |
| 		s.user = auth.User
 | |
| 		s.password = auth.Password
 | |
| 	}
 | |
| 
 | |
| 	return s, nil
 | |
| }
 | |
| 
 | |
| type socks5 struct {
 | |
| 	user, password string
 | |
| 	network, addr  string
 | |
| 	forward        Dialer
 | |
| }
 | |
| 
 | |
| const socks5Version = 5
 | |
| 
 | |
| const (
 | |
| 	socks5AuthNone     = 0
 | |
| 	socks5AuthPassword = 2
 | |
| )
 | |
| 
 | |
| const socks5Connect = 1
 | |
| 
 | |
| const (
 | |
| 	socks5IP4    = 1
 | |
| 	socks5Domain = 3
 | |
| 	socks5IP6    = 4
 | |
| )
 | |
| 
 | |
| var socks5Errors = []string{
 | |
| 	"",
 | |
| 	"general failure",
 | |
| 	"connection forbidden",
 | |
| 	"network unreachable",
 | |
| 	"host unreachable",
 | |
| 	"connection refused",
 | |
| 	"TTL expired",
 | |
| 	"command not supported",
 | |
| 	"address type not supported",
 | |
| }
 | |
| 
 | |
| // Dial connects to the address addr on the network net via the SOCKS5 proxy.
 | |
| func (s *socks5) Dial(network, addr string) (net.Conn, error) {
 | |
| 	switch network {
 | |
| 	case "tcp", "tcp6", "tcp4":
 | |
| 	default:
 | |
| 		return nil, errors.New("proxy: no support for SOCKS5 proxy connections of type " + network)
 | |
| 	}
 | |
| 
 | |
| 	conn, err := s.forward.Dial(s.network, s.addr)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	closeConn := &conn
 | |
| 	defer func() {
 | |
| 		if closeConn != nil {
 | |
| 			(*closeConn).Close()
 | |
| 		}
 | |
| 	}()
 | |
| 
 | |
| 	host, portStr, err := net.SplitHostPort(addr)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	port, err := strconv.Atoi(portStr)
 | |
| 	if err != nil {
 | |
| 		return nil, errors.New("proxy: failed to parse port number: " + portStr)
 | |
| 	}
 | |
| 	if port < 1 || port > 0xffff {
 | |
| 		return nil, errors.New("proxy: port number out of range: " + portStr)
 | |
| 	}
 | |
| 
 | |
| 	// the size here is just an estimate
 | |
| 	buf := make([]byte, 0, 6+len(host))
 | |
| 
 | |
| 	buf = append(buf, socks5Version)
 | |
| 	if len(s.user) > 0 && len(s.user) < 256 && len(s.password) < 256 {
 | |
| 		buf = append(buf, 2 /* num auth methods */, socks5AuthNone, socks5AuthPassword)
 | |
| 	} else {
 | |
| 		buf = append(buf, 1 /* num auth methods */, socks5AuthNone)
 | |
| 	}
 | |
| 
 | |
| 	if _, err := conn.Write(buf); err != nil {
 | |
| 		return nil, errors.New("proxy: failed to write greeting to SOCKS5 proxy at " + s.addr + ": " + err.Error())
 | |
| 	}
 | |
| 
 | |
| 	if _, err := io.ReadFull(conn, buf[:2]); err != nil {
 | |
| 		return nil, errors.New("proxy: failed to read greeting from SOCKS5 proxy at " + s.addr + ": " + err.Error())
 | |
| 	}
 | |
| 	if buf[0] != 5 {
 | |
| 		return nil, errors.New("proxy: SOCKS5 proxy at " + s.addr + " has unexpected version " + strconv.Itoa(int(buf[0])))
 | |
| 	}
 | |
| 	if buf[1] == 0xff {
 | |
| 		return nil, errors.New("proxy: SOCKS5 proxy at " + s.addr + " requires authentication")
 | |
| 	}
 | |
| 
 | |
| 	if buf[1] == socks5AuthPassword {
 | |
| 		buf = buf[:0]
 | |
| 		buf = append(buf, 1 /* password protocol version */)
 | |
| 		buf = append(buf, uint8(len(s.user)))
 | |
| 		buf = append(buf, s.user...)
 | |
| 		buf = append(buf, uint8(len(s.password)))
 | |
| 		buf = append(buf, s.password...)
 | |
| 
 | |
| 		if _, err := conn.Write(buf); err != nil {
 | |
| 			return nil, errors.New("proxy: failed to write authentication request to SOCKS5 proxy at " + s.addr + ": " + err.Error())
 | |
| 		}
 | |
| 
 | |
| 		if _, err := io.ReadFull(conn, buf[:2]); err != nil {
 | |
| 			return nil, errors.New("proxy: failed to read authentication reply from SOCKS5 proxy at " + s.addr + ": " + err.Error())
 | |
| 		}
 | |
| 
 | |
| 		if buf[1] != 0 {
 | |
| 			return nil, errors.New("proxy: SOCKS5 proxy at " + s.addr + " rejected username/password")
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	buf = buf[:0]
 | |
| 	buf = append(buf, socks5Version, socks5Connect, 0 /* reserved */)
 | |
| 
 | |
| 	if ip := net.ParseIP(host); ip != nil {
 | |
| 		if ip4 := ip.To4(); ip4 != nil {
 | |
| 			buf = append(buf, socks5IP4)
 | |
| 			ip = ip4
 | |
| 		} else {
 | |
| 			buf = append(buf, socks5IP6)
 | |
| 		}
 | |
| 		buf = append(buf, ip...)
 | |
| 	} else {
 | |
| 		if len(host) > 255 {
 | |
| 			return nil, errors.New("proxy: destination hostname too long: " + host)
 | |
| 		}
 | |
| 		buf = append(buf, socks5Domain)
 | |
| 		buf = append(buf, byte(len(host)))
 | |
| 		buf = append(buf, host...)
 | |
| 	}
 | |
| 	buf = append(buf, byte(port>>8), byte(port))
 | |
| 
 | |
| 	if _, err := conn.Write(buf); err != nil {
 | |
| 		return nil, errors.New("proxy: failed to write connect request to SOCKS5 proxy at " + s.addr + ": " + err.Error())
 | |
| 	}
 | |
| 
 | |
| 	if _, err := io.ReadFull(conn, buf[:4]); err != nil {
 | |
| 		return nil, errors.New("proxy: failed to read connect reply from SOCKS5 proxy at " + s.addr + ": " + err.Error())
 | |
| 	}
 | |
| 
 | |
| 	failure := "unknown error"
 | |
| 	if int(buf[1]) < len(socks5Errors) {
 | |
| 		failure = socks5Errors[buf[1]]
 | |
| 	}
 | |
| 
 | |
| 	if len(failure) > 0 {
 | |
| 		return nil, errors.New("proxy: SOCKS5 proxy at " + s.addr + " failed to connect: " + failure)
 | |
| 	}
 | |
| 
 | |
| 	bytesToDiscard := 0
 | |
| 	switch buf[3] {
 | |
| 	case socks5IP4:
 | |
| 		bytesToDiscard = net.IPv4len
 | |
| 	case socks5IP6:
 | |
| 		bytesToDiscard = net.IPv6len
 | |
| 	case socks5Domain:
 | |
| 		_, err := io.ReadFull(conn, buf[:1])
 | |
| 		if err != nil {
 | |
| 			return nil, errors.New("proxy: failed to read domain length from SOCKS5 proxy at " + s.addr + ": " + err.Error())
 | |
| 		}
 | |
| 		bytesToDiscard = int(buf[0])
 | |
| 	default:
 | |
| 		return nil, errors.New("proxy: got unknown address type " + strconv.Itoa(int(buf[3])) + " from SOCKS5 proxy at " + s.addr)
 | |
| 	}
 | |
| 
 | |
| 	if cap(buf) < bytesToDiscard {
 | |
| 		buf = make([]byte, bytesToDiscard)
 | |
| 	} else {
 | |
| 		buf = buf[:bytesToDiscard]
 | |
| 	}
 | |
| 	if _, err := io.ReadFull(conn, buf); err != nil {
 | |
| 		return nil, errors.New("proxy: failed to read address from SOCKS5 proxy at " + s.addr + ": " + err.Error())
 | |
| 	}
 | |
| 
 | |
| 	// Also need to discard the port number
 | |
| 	if _, err := io.ReadFull(conn, buf[:2]); err != nil {
 | |
| 		return nil, errors.New("proxy: failed to read port from SOCKS5 proxy at " + s.addr + ": " + err.Error())
 | |
| 	}
 | |
| 
 | |
| 	closeConn = nil
 | |
| 	return conn, nil
 | |
| }
 | 
