src/pkg/net/dial.go - The Go Programming Language

Golang

Source file src/pkg/net/dial.go

     1	// Copyright 2010 The Go Authors.  All rights reserved.
     2	// Use of this source code is governed by a BSD-style
     3	// license that can be found in the LICENSE file.
     4	
     5	package net
     6	
     7	import (
     8		"time"
     9	)
    10	
    11	func parseDialNetwork(net string) (afnet string, proto int, err error) {
    12		i := last(net, ':')
    13		if i < 0 { // no colon
    14			switch net {
    15			case "tcp", "tcp4", "tcp6":
    16			case "udp", "udp4", "udp6":
    17			case "unix", "unixgram", "unixpacket":
    18			default:
    19				return "", 0, UnknownNetworkError(net)
    20			}
    21			return net, 0, nil
    22		}
    23		afnet = net[:i]
    24		switch afnet {
    25		case "ip", "ip4", "ip6":
    26			protostr := net[i+1:]
    27			proto, i, ok := dtoi(protostr, 0)
    28			if !ok || i != len(protostr) {
    29				proto, err = lookupProtocol(protostr)
    30				if err != nil {
    31					return "", 0, err
    32				}
    33			}
    34			return afnet, proto, nil
    35		}
    36		return "", 0, UnknownNetworkError(net)
    37	}
    38	
    39	func resolveNetAddr(op, net, addr string) (afnet string, a Addr, err error) {
    40		afnet, _, err = parseDialNetwork(net)
    41		if err != nil {
    42			return "", nil, &OpError{op, net, nil, err}
    43		}
    44		if op == "dial" && addr == "" {
    45			return "", nil, &OpError{op, net, nil, errMissingAddress}
    46		}
    47		switch afnet {
    48		case "tcp", "tcp4", "tcp6":
    49			if addr != "" {
    50				a, err = ResolveTCPAddr(afnet, addr)
    51			}
    52		case "udp", "udp4", "udp6":
    53			if addr != "" {
    54				a, err = ResolveUDPAddr(afnet, addr)
    55			}
    56		case "ip", "ip4", "ip6":
    57			if addr != "" {
    58				a, err = ResolveIPAddr(afnet, addr)
    59			}
    60		case "unix", "unixgram", "unixpacket":
    61			if addr != "" {
    62				a, err = ResolveUnixAddr(afnet, addr)
    63			}
    64		}
    65		return
    66	}
    67	
    68	// Dial connects to the address addr on the network net.
    69	//
    70	// Known networks are "tcp", "tcp4" (IPv4-only), "tcp6" (IPv6-only),
    71	// "udp", "udp4" (IPv4-only), "udp6" (IPv6-only), "ip", "ip4"
    72	// (IPv4-only), "ip6" (IPv6-only), "unix" and "unixpacket".
    73	//
    74	// For TCP and UDP networks, addresses have the form host:port.
    75	// If host is a literal IPv6 address, it must be enclosed
    76	// in square brackets.  The functions JoinHostPort and SplitHostPort
    77	// manipulate addresses in this form.
    78	//
    79	// Examples:
    80	//	Dial("tcp", "12.34.56.78:80")
    81	//	Dial("tcp", "google.com:80")
    82	//	Dial("tcp", "[de:ad:be:ef::ca:fe]:80")
    83	//
    84	// For IP networks, addr must be "ip", "ip4" or "ip6" followed
    85	// by a colon and a protocol number or name.
    86	//
    87	// Examples:
    88	//	Dial("ip4:1", "127.0.0.1")
    89	//	Dial("ip6:ospf", "::1")
    90	//
    91	func Dial(net, addr string) (Conn, error) {
    92		_, addri, err := resolveNetAddr("dial", net, addr)
    93		if err != nil {
    94			return nil, err
    95		}
    96		return dialAddr(net, addr, addri)
    97	}
    98	
    99	func dialAddr(net, addr string, addri Addr) (c Conn, err error) {
   100		switch ra := addri.(type) {
   101		case *TCPAddr:
   102			c, err = DialTCP(net, nil, ra)
   103		case *UDPAddr:
   104			c, err = DialUDP(net, nil, ra)
   105		case *IPAddr:
   106			c, err = DialIP(net, nil, ra)
   107		case *UnixAddr:
   108			c, err = DialUnix(net, nil, ra)
   109		default:
   110			err = &OpError{"dial", net + " " + addr, nil, UnknownNetworkError(net)}
   111		}
   112		if err != nil {
   113			return nil, err
   114		}
   115		return
   116	}
   117	
   118	// DialTimeout acts like Dial but takes a timeout.
   119	// The timeout includes name resolution, if required.
   120	func DialTimeout(net, addr string, timeout time.Duration) (Conn, error) {
   121		// TODO(bradfitz): the timeout should be pushed down into the
   122		// net package's event loop, so on timeout to dead hosts we
   123		// don't have a goroutine sticking around for the default of
   124		// ~3 minutes.
   125		t := time.NewTimer(timeout)
   126		defer t.Stop()
   127		type pair struct {
   128			Conn
   129			error
   130		}
   131		ch := make(chan pair, 1)
   132		resolvedAddr := make(chan Addr, 1)
   133		go func() {
   134			_, addri, err := resolveNetAddr("dial", net, addr)
   135			if err != nil {
   136				ch <- pair{nil, err}
   137				return
   138			}
   139			resolvedAddr <- addri // in case we need it for OpError
   140			c, err := dialAddr(net, addr, addri)
   141			ch <- pair{c, err}
   142		}()
   143		select {
   144		case <-t.C:
   145			// Try to use the real Addr in our OpError, if we resolved it
   146			// before the timeout. Otherwise we just use stringAddr.
   147			var addri Addr
   148			select {
   149			case a := <-resolvedAddr:
   150				addri = a
   151			default:
   152				addri = &stringAddr{net, addr}
   153			}
   154			err := &OpError{
   155				Op:   "dial",
   156				Net:  net,
   157				Addr: addri,
   158				Err:  &timeoutError{},
   159			}
   160			return nil, err
   161		case p := <-ch:
   162			return p.Conn, p.error
   163		}
   164		panic("unreachable")
   165	}
   166	
   167	type stringAddr struct {
   168		net, addr string
   169	}
   170	
   171	func (a stringAddr) Network() string { return a.net }
   172	func (a stringAddr) String() string  { return a.addr }
   173	
   174	// Listen announces on the local network address laddr.
   175	// The network string net must be a stream-oriented network:
   176	// "tcp", "tcp4", "tcp6", or "unix", or "unixpacket".
   177	func Listen(net, laddr string) (Listener, error) {
   178		afnet, a, err := resolveNetAddr("listen", net, laddr)
   179		if err != nil {
   180			return nil, err
   181		}
   182		switch afnet {
   183		case "tcp", "tcp4", "tcp6":
   184			var la *TCPAddr
   185			if a != nil {
   186				la = a.(*TCPAddr)
   187			}
   188			return ListenTCP(net, la)
   189		case "unix", "unixpacket":
   190			var la *UnixAddr
   191			if a != nil {
   192				la = a.(*UnixAddr)
   193			}
   194			return ListenUnix(net, la)
   195		}
   196		return nil, UnknownNetworkError(net)
   197	}
   198	
   199	// ListenPacket announces on the local network address laddr.
   200	// The network string net must be a packet-oriented network:
   201	// "udp", "udp4", "udp6", "ip", "ip4", "ip6" or "unixgram".
   202	func ListenPacket(net, addr string) (PacketConn, error) {
   203		afnet, a, err := resolveNetAddr("listen", net, addr)
   204		if err != nil {
   205			return nil, err
   206		}
   207		switch afnet {
   208		case "udp", "udp4", "udp6":
   209			var la *UDPAddr
   210			if a != nil {
   211				la = a.(*UDPAddr)
   212			}
   213			return ListenUDP(net, la)
   214		case "ip", "ip4", "ip6":
   215			var la *IPAddr
   216			if a != nil {
   217				la = a.(*IPAddr)
   218			}
   219			return ListenIP(net, la)
   220		case "unixgram":
   221			var la *UnixAddr
   222			if a != nil {
   223				la = a.(*UnixAddr)
   224			}
   225			return DialUnix(net, la, nil)
   226		}
   227		return nil, UnknownNetworkError(net)
   228	}