socks/socks.go

123 lines
3.4 KiB
Go
Raw Permalink Normal View History

2012-08-01 02:34:46 +00:00
// Copyright 2012, Hailiang Wang. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
2012-08-01 02:59:28 +00:00
/*
2012-08-04 06:47:38 +00:00
Package socks implements a SOCKS (SOCKS4, SOCKS4A and SOCKS5) proxy client.
2012-08-01 03:36:32 +00:00
A complete example using this package:
2022-08-28 11:14:27 +00:00
2012-08-01 02:59:28 +00:00
package main
import (
2022-08-28 11:17:53 +00:00
"git.tcp.direct/kayos/socks"
2012-08-01 02:59:28 +00:00
"fmt"
"net/http"
"io/ioutil"
)
func main() {
2019-02-09 11:11:00 +00:00
dialSocksProxy := socks.Dial("socks5://127.0.0.1:1080?timeout=5s")
2012-08-01 02:59:28 +00:00
tr := &http.Transport{Dial: dialSocksProxy}
httpClient := &http.Client{Transport: tr}
bodyText, err := TestHttpsGet(httpClient, "https://h12.io/about")
2012-08-01 02:59:28 +00:00
if err != nil {
fmt.Println(err.Error())
}
fmt.Print(bodyText)
}
func TestHttpsGet(c *http.Client, url string) (bodyText string, err error) {
resp, err := c.Get(url)
if err != nil { return }
defer resp.Body.Close()
2012-08-01 02:59:28 +00:00
body, err := ioutil.ReadAll(resp.Body)
if err != nil { return }
bodyText = string(body)
return
}
*/
2022-08-28 11:17:53 +00:00
package socks // import "git.tcp.direct/kayos/socks"
2012-08-01 02:34:46 +00:00
import (
"fmt"
"net"
2022-08-28 11:14:27 +00:00
"time"
2012-08-01 02:34:46 +00:00
)
2012-08-01 03:36:32 +00:00
// Constants to choose which version of SOCKS protocol to use.
2022-10-16 09:21:24 +00:00
//
//goland:noinspection GoNameStartsWithPackageName
2012-08-01 02:34:46 +00:00
const (
SOCKS4 = iota
SOCKS4A
SOCKS5
2012-08-01 02:34:46 +00:00
)
2022-08-28 11:14:27 +00:00
// DialWithConn returns the dial function to be used in http.Transport object.
// Argument proxyURI should be in the format: "socks5://user:password@127.0.0.1:1080?timeout=5s".
// The protocol could be socks5, socks4 and socks4a. DialWithConn will use the given connection
// to communicate with the proxy server.
func DialWithConn(proxyURI string, conn net.Conn) func(string, string) (net.Conn, error) {
cfg, err := parse(proxyURI)
if err != nil {
return dialError(err)
}
cfg.conn = conn
return cfg.dialFunc()
}
2019-02-09 11:11:00 +00:00
// Dial returns the dial function to be used in http.Transport object.
// Argument proxyURI should be in the format: "socks5://user:password@127.0.0.1:1080?timeout=5s".
// The protocol could be socks5, socks4 and socks4a.
func Dial(proxyURI string) func(string, string) (net.Conn, error) {
cfg, err := parse(proxyURI)
if err != nil {
return dialError(err)
}
return cfg.dialFunc()
}
2012-08-01 03:36:32 +00:00
// DialSocksProxy returns the dial function to be used in http.Transport object.
// Argument socksType should be one of SOCKS4, SOCKS4A and SOCKS5.
// Argument proxy should be in this format "127.0.0.1:1080".
2012-08-01 02:34:46 +00:00
func DialSocksProxy(socksType int, proxy string) func(string, string) (net.Conn, error) {
2022-10-16 09:21:24 +00:00
return (&session{Proto: socksType, Host: proxy}).dialFunc()
2019-02-09 11:11:00 +00:00
}
2022-10-16 09:21:24 +00:00
func (sesh *session) dialFunc() func(string, string) (net.Conn, error) {
switch sesh.Proto {
2019-02-09 11:11:00 +00:00
case SOCKS5:
return func(_, targetAddr string) (conn net.Conn, err error) {
2022-10-16 09:21:24 +00:00
return sesh.dialSocks5(targetAddr)
2019-02-09 11:11:00 +00:00
}
case SOCKS4, SOCKS4A:
return func(_, targetAddr string) (conn net.Conn, err error) {
2022-10-16 09:21:24 +00:00
return sesh.dialSocks4(targetAddr)
2012-08-01 02:34:46 +00:00
}
}
2022-10-16 09:21:24 +00:00
return dialError(fmt.Errorf("unknown SOCKS protocol %v", sesh.Proto))
2012-08-01 02:34:46 +00:00
}
2019-02-09 11:11:00 +00:00
func dialError(err error) func(string, string) (net.Conn, error) {
return func(_, _ string) (net.Conn, error) {
return nil, err
}
}
2022-08-28 11:14:27 +00:00
2022-10-16 09:21:24 +00:00
func (sesh *session) internalDial() (conn net.Conn, err error) {
// fmt.Printf("Dialing %s\n", sesh.Host)
if sesh.conn == nil {
return net.DialTimeout("tcp", sesh.Host, sesh.Timeout)
2022-08-28 11:14:27 +00:00
}
2022-10-16 09:21:24 +00:00
if sesh.Timeout > 0 {
if err = sesh.conn.SetDeadline(time.Now().Add(sesh.Timeout)); err != nil {
return nil, err
}
}
// fmt.Printf("Using existing connection: %v(local)->%v(remote)\n", sesh.conn.LocalAddr(), sesh.conn.RemoteAddr())
return sesh.conn, nil
2022-08-28 11:14:27 +00:00
}