func NewHTTPClient(conf *ProxyChannelConfig) (*http.Client, error) { localDial := func(network, addr string) (net.Conn, error) { host, port, _ := net.SplitHostPort(addr) if port == "443" && len(conf.SNIProxy) > 0 && hosts.InHosts(conf.SNIProxy) { addr = hosts.GetAddr(conf.SNIProxy, "443") host, _, _ = net.SplitHostPort(addr) } if net.ParseIP(host) == nil { iphost, err := DnsGetDoaminIP(host) if nil != err { return nil, err } addr = net.JoinHostPort(iphost, port) } dailTimeout := conf.DialTimeout if 0 == dailTimeout { dailTimeout = 5 } log.Printf("[Proxy]Connect %s", addr) return netx.DialTimeout(network, addr, time.Duration(dailTimeout)*time.Second) } readTimeout := conf.ReadTimeout if 0 == readTimeout { readTimeout = 30 } tr := &http.Transport{ Dial: localDial, DisableCompression: true, MaxIdleConnsPerHost: 2 * int(conf.ConnsPerServer), ResponseHeaderTimeout: time.Duration(readTimeout) * time.Second, } if len(conf.SNI) > 0 { tlscfg := &tls.Config{} tlscfg.InsecureSkipVerify = true tlscfg.ServerName = conf.SNI[0] tr.TLSClientConfig = tlscfg } if len(conf.Proxy) > 0 { proxyUrl, err := url.Parse(conf.Proxy) if nil != err { log.Printf("[ERROR]Invalid proxy url:%s to create http client.", conf.Proxy) return nil, err } tr.Proxy = http.ProxyURL(proxyUrl) } hc := &http.Client{} hc.Timeout = tr.ResponseHeaderTimeout hc.Transport = tr return hc, nil }
func (tc *tcpChannel) Open() error { dailTimeout := tc.conf.DialTimeout if 0 == dailTimeout { dailTimeout = 5 } var tlscfg *tls.Config hostport := tc.rurl.Host vpsHost, vpsPort, _ := net.SplitHostPort(hostport) if strings.EqualFold(tc.rurl.Scheme, "tls") { tlscfg = &tls.Config{} tlscfg.ServerName = vpsHost if len(tc.conf.SNIProxy) > 0 && vpsPort == "443" && hosts.InHosts(tc.conf.SNIProxy) { hostport = hosts.GetAddr(tc.conf.SNIProxy, "443") vpsHost, _, _ = net.SplitHostPort(hostport) tc.useSNIProxy = true log.Printf("VPS channel select SNIProxy %s to connect", hostport) } } if net.ParseIP(vpsHost) == nil { iphost, err := proxy.DnsGetDoaminIP(vpsHost) if nil != err { return err } hostport = net.JoinHostPort(iphost, vpsPort) } //log.Printf("######%s %s", vpsHost, tc.hostport) timeout := time.Duration(dailTimeout) * time.Second var c net.Conn var err error if len(tc.conf.Proxy) > 0 { c, err = helper.HTTPProxyDial(tc.conf.Proxy, hostport, timeout) } else { c, err = netx.DialTimeout("tcp", hostport, timeout) } if nil != tlscfg && nil == err { c = tls.Client(c, tlscfg) } if err != nil { if tc.rurl.String() != tc.originAddr { tc.rurl, _ = url.Parse(tc.originAddr) tc.proxyChannel.Addr = tc.rurl.String() } log.Printf("###Failed to connect %s with err:%v", hostport, err) return err } tc.conn = c return nil }
func Socks5ProxyDial(proxyURL string, addr string, timeout time.Duration) (net.Conn, error) { u, err := url.Parse(proxyURL) if nil != err { return nil, err } c, err := netx.DialTimeout("tcp", u.Host, timeout) if err != nil { return nil, err } err = Socks5ProxyConnect(u, c, addr) if nil != err { c.Close() return nil, err } return c, nil }
func dnsQuery(r *dns.Msg) (*dns.Msg, error) { dnsServers := GConf.LocalDNS.TrustedDNS var record *dnsCacheRecord var domain string useTrustedDNS := true if len(r.Question) == 1 && dns.IsFqdn(r.Question[0].Name) { domain = r.Question[0].Name domain = domain[0 : len(domain)-1] if nil != dnsCache { item, exist := dnsCache.Get(domain) if exist { record = item.(*dnsCacheRecord) if time.Now().After(record.expireAt) { record = nil dnsCache.Remove(domain) } else { if r.Question[0].Qtype == dns.TypeA && nil != record.ipv4Res { return record.ipv4Res, nil } else if r.Question[0].Qtype == dns.TypeAAAA && nil != record.ipv6Res { return record.ipv6Res, nil } } } } if nil != mygfwlist { connReq, _ := http.NewRequest("CONNECT", "https://"+domain, nil) isBlocked, _ := mygfwlist.FastMatchDoamin(connReq) if !isBlocked { dnsServers = GConf.LocalDNS.FastDNS useTrustedDNS = false } } } else { log.Printf("###DNS with %v", r.Question) } if len(dnsServers) == 0 { dnsServers = GConf.LocalDNS.TrustedDNS useTrustedDNS = true } if len(dnsServers) == 0 { log.Printf("At least one DNS server need to be configured in 'FastDNS/TrustedDNS'") return nil, errNoDNServer } server := selectDNSServer(dnsServers) network := "udp" if GConf.LocalDNS.TCPConnect && useTrustedDNS { network = "tcp" } log.Printf("DNS query %s to %s", domain, server) for retry := 0; retry < 3; retry++ { c, err := netx.DialTimeout(network, server, 1*time.Second) if nil != err { return nil, err } dnsConn := new(dns.Conn) if pc, ok := c.(getConnIntf); ok { c = pc.GetConn() } dnsConn.Conn = c dnsConn.WriteMsg(r) dnsConn.SetReadDeadline(time.Now().Add(1 * time.Second)) res, err1 := dnsConn.ReadMsg() if nil == err1 && nil != dnsCache && len(domain) > 0 { record = newDNSCacheRecord(record, res) dnsCache.Add(domain, record) } c.Close() if nil == err1 { return res, nil } } return nil, errDNSQuryFail }
func newDirectChannel(ev event.Event, conf *proxy.ProxyChannelConfig) (*directChannel, error) { host := "" port := "" network := "tcp" needHttpsConnect := false switch ev.(type) { case *event.UDPEvent: network = "udp" host = ev.(*event.UDPEvent).Addr case *event.TCPOpenEvent: host = ev.(*event.TCPOpenEvent).Addr needHttpsConnect = true case *event.HTTPRequestEvent: req := ev.(*event.HTTPRequestEvent) host = req.Headers.Get("Host") needHttpsConnect = strings.EqualFold(req.Method, "Connect") default: return nil, fmt.Errorf("Can NOT create direct channel by event:%T", ev) } //log.Printf("Session:%d enter direct with host %s & event:%T", ev.GetId(), host, ev) if len(host) == 0 { return nil, fmt.Errorf("Empty remote addr in event") } if strings.Contains(host, ":") { host, port, _ = net.SplitHostPort(host) } else { if needHttpsConnect { port = "443" } else { port = "80" } } if len(conf.SNIProxy) > 0 && port == "443" && network == "tcp" && hosts.InHosts(conf.SNIProxy) { host = conf.SNIProxy } isIP := net.ParseIP(host) != nil useTLS := false if conf.ForceTLS && port == "80" && hosts.InHosts(host) { useTLS = true } else { useTLS = false } if !isIP { host = hosts.GetHost(host) } //log.Printf("Session:%d get host:%s", ev.GetId(), host) addr := "" if nil == conf.ProxyURL() { if useTLS { addr = host + ":443" } else { if len(port) > 0 { addr = host + ":" + port } else { if needHttpsConnect { addr = host + ":443" } else { addr = host + ":80" } } } } else { addr = conf.ProxyURL().Host } connectHost, connectPort, _ := net.SplitHostPort(addr) if net.ParseIP(connectHost) == nil { iphost, err := proxy.DnsGetDoaminIP(connectHost) if nil != err { return nil, err } addr = net.JoinHostPort(iphost, connectPort) } dailTimeout := conf.DialTimeout if 0 == dailTimeout { dailTimeout = 5 } //log.Printf("Session:%d connect %s:%s for %s %T %v %v %s", ev.GetId(), network, addr, host, ev, needHttpsConnect, conf.ProxyURL(), net.JoinHostPort(host, port)) c, err := netx.DialTimeout(network, addr, time.Duration(dailTimeout)*time.Second) if nil != conf.ProxyURL() && nil == err { if strings.HasPrefix(conf.ProxyURL().Scheme, "socks") { err = helper.Socks5ProxyConnect(conf.ProxyURL(), c, net.JoinHostPort(host, port)) } else { if needHttpsConnect { err = helper.HTTPProxyConnect(conf.ProxyURL(), c, "https://"+net.JoinHostPort(host, port)) } } } if nil != err { log.Printf("Failed to connect %s for %s with error:%v", addr, host, err) return nil, err } d := &directChannel{ev.GetId(), c, needHttpsConnect, network == "udp", conf, addr} //d := &directChannel{ev.GetId(), c, fromHttpsConnect, network == "udp", toReplaceSNI, false, nil} if useTLS { tlcfg := &tls.Config{} tlcfg.InsecureSkipVerify = true sniLen := len(conf.SNI) if sniLen > 0 { tlcfg.ServerName = conf.SNI[rand.Intn(sniLen)] } tlsconn := tls.Client(c, tlcfg) err = tlsconn.Handshake() if nil != err { log.Printf("Failed to handshake with %s", addr) } d.conn = tlsconn } go d.read() return d, nil }