123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107 |
- // Copyright (C) 2015 The Syncthing Authors.
- //
- // This Source Code Form is subject to the terms of the Mozilla Public
- // License, v. 2.0. If a copy of the MPL was not distributed with this file,
- // You can obtain one at https://mozilla.org/MPL/2.0/.
- package dialer
- import (
- "context"
- "errors"
- "fmt"
- "net"
- "time"
- "golang.org/x/net/ipv4"
- "golang.org/x/net/ipv6"
- "golang.org/x/net/proxy"
- )
- var errUnexpectedInterfaceType = errors.New("unexpected interface type")
- // SetTCPOptions sets our default TCP options on a TCP connection, possibly
- // digging through dialerConn to extract the *net.TCPConn
- func SetTCPOptions(conn net.Conn) error {
- switch conn := conn.(type) {
- case *net.TCPConn:
- var err error
- if err = conn.SetLinger(0); err != nil {
- return err
- }
- if err = conn.SetNoDelay(false); err != nil {
- return err
- }
- if err = conn.SetKeepAlivePeriod(60 * time.Second); err != nil {
- return err
- }
- if err = conn.SetKeepAlive(true); err != nil {
- return err
- }
- return nil
- default:
- return fmt.Errorf("unknown connection type %T", conn)
- }
- }
- func SetTrafficClass(conn net.Conn, class int) error {
- switch conn := conn.(type) {
- case *net.TCPConn:
- e1 := ipv4.NewConn(conn).SetTOS(class)
- e2 := ipv6.NewConn(conn).SetTrafficClass(class)
- if e1 != nil {
- return e1
- }
- return e2
- default:
- return fmt.Errorf("unknown connection type %T", conn)
- }
- }
- func dialContextWithFallback(ctx context.Context, fallback proxy.ContextDialer, network, addr string) (net.Conn, error) {
- dialer, ok := proxy.FromEnvironment().(proxy.ContextDialer)
- if !ok {
- return nil, errUnexpectedInterfaceType
- }
- if dialer == proxy.Direct {
- return fallback.DialContext(ctx, network, addr)
- }
- if noFallback {
- return dialer.DialContext(ctx, network, addr)
- }
- ctx, cancel := context.WithCancel(ctx)
- defer cancel()
- var proxyConn, fallbackConn net.Conn
- var proxyErr, fallbackErr error
- proxyDone := make(chan struct{})
- fallbackDone := make(chan struct{})
- go func() {
- proxyConn, proxyErr = dialer.DialContext(ctx, network, addr)
- close(proxyDone)
- }()
- go func() {
- fallbackConn, fallbackErr = fallback.DialContext(ctx, network, addr)
- close(fallbackDone)
- }()
- <-proxyDone
- if proxyErr == nil {
- go func() {
- <-fallbackDone
- if fallbackErr == nil {
- fallbackConn.Close()
- }
- }()
- return proxyConn, nil
- }
- <-fallbackDone
- return fallbackConn, fallbackErr
- }
- // DialContext dials via context and/or directly, depending on how it is configured.
- // If dialing via proxy and allowing fallback, dialing for both happens simultaneously
- // and the proxy connection is returned if successful.
- func DialContext(ctx context.Context, network, addr string) (net.Conn, error) {
- return dialContextWithFallback(ctx, proxy.Direct, network, addr)
- }
|