go.dedis.ch/onet/v4@v4.0.0-pre1/network/address.go (about)

     1  package network
     2  
     3  import (
     4  	"net"
     5  	"regexp"
     6  	"strconv"
     7  	"strings"
     8  )
     9  
    10  // ConnType represents the type of a Connection.
    11  // The supported types are defined as constants of type ConnType.
    12  type ConnType string
    13  
    14  // Address contains the ConnType and the actual network address. It is used to connect
    15  // to a remote host with a Conn and to listen by a Listener.
    16  // A network address holds an IP address and the port number joined
    17  // by a colon.
    18  // It doesn't support IPv6 yet.
    19  type Address string
    20  
    21  var lookupHost = net.LookupHost
    22  
    23  const (
    24  	// PlainTCP is an unencrypted TCP connection.
    25  	PlainTCP ConnType = "tcp"
    26  	// TLS is a TLS encrypted connection over TCP.
    27  	TLS = "tls"
    28  	// Local is a channel based connection type.
    29  	Local = "local"
    30  	// InvalidConnType is an invalid connection type.
    31  	InvalidConnType = "wrong"
    32  )
    33  
    34  // typeAddressSep is the separator between the type of the connection and the actual
    35  // IP address.
    36  const typeAddressSep = "://"
    37  
    38  // connType converts a string to a ConnType. In case of failure,
    39  // it returns InvalidConnType.
    40  func connType(t string) ConnType {
    41  	ct := ConnType(t)
    42  	types := []ConnType{PlainTCP, TLS, Local}
    43  	for _, t := range types {
    44  		if t == ct {
    45  			return ct
    46  		}
    47  	}
    48  	return InvalidConnType
    49  }
    50  
    51  // ConnType returns the connection type from the address.
    52  // It returns InvalidConnType if the address is not valid or if the
    53  // connection type is not known.
    54  func (a Address) ConnType() ConnType {
    55  	if !a.Valid() {
    56  		return InvalidConnType
    57  	}
    58  	vals := strings.Split(string(a), typeAddressSep)
    59  	return connType(vals[0])
    60  }
    61  
    62  // IsHostname returns true if the address is defined by a VALID DNS name
    63  func (a Address) IsHostname() bool {
    64  	host := a.Host()
    65  
    66  	// validHostname(host) would be enough with the current implementation.
    67  	// However, if we will include IDNs as valid hostnames, an IP address in the form
    68  	// *.*.*.* would be a valid hostname too. This is why ParseIP is used as well.
    69  	return validHostname(host) && net.ParseIP(host) == nil
    70  }
    71  
    72  // NetworkAddress returns the network address part of the address, which is
    73  // the host and the port joined by a colon.
    74  // It returns an empty string the address is not valid
    75  func (a Address) NetworkAddress() string {
    76  	if !a.Valid() {
    77  		return ""
    78  	}
    79  	vals := strings.Split(string(a), typeAddressSep)
    80  	return vals[1]
    81  }
    82  
    83  // NetworkAddressResolved returns the network address of the address, but resolved.
    84  // That is: the hostname resolved and the port joined by a colon.
    85  // It returns an empty string if the address is not valid.
    86  func (a Address) NetworkAddressResolved() string {
    87  	if !a.Valid() {
    88  		return ""
    89  	}
    90  	ipAddress := a.Resolve()
    91  	port := a.Port()
    92  	return net.JoinHostPort(ipAddress, port)
    93  }
    94  
    95  // Resolve returns the IP address associated to the hostname that represents the address a.
    96  // If a is defined by an IP address (*.*.*.*) or if the hostname is not valid, the empty string
    97  // is returned
    98  func (a Address) Resolve() string {
    99  	if !a.Valid() {
   100  		return ""
   101  	}
   102  	host := a.Host()
   103  	// Ipv6 not handled properly yet
   104  	if host == "[::]" {
   105  		return "::"
   106  	}
   107  	// If the address is defined by an IP address, return it
   108  	if net.ParseIP(host) != nil {
   109  		return host
   110  	}
   111  
   112  	if !a.IsHostname() {
   113  		return ""
   114  	}
   115  
   116  	ipAddress, err := lookupHost(host)
   117  	if err != nil {
   118  		return ""
   119  	}
   120  
   121  	return ipAddress[0]
   122  }
   123  
   124  // validHostname returns true if the hostname is well formed or false otherwise.
   125  // A hostname is well formed if the following conditions are met:
   126  //	- each label contains from 1 to 63 characters
   127  //	- the entire hostname (including the delimiting dots, but not a trailing dot)
   128  // 		has a maximum of 253 ASCII characters
   129  //	- labels have only ASCII letters 'a' through 'z' (case-insensitive), the digits
   130  //		'0' through '9', and the hyphen (-). No other symbol is permitted
   131  //	- labels cannot start with a hyphen
   132  //	- labels cannot end with a hyphen
   133  //	- the last label is alphabetic
   134  //	More information about the definition of the TLD (the last label of a hostname) on:
   135  //		https://github.com/dedis/cothority/issues/620
   136  // This method assumes that only the host part is passed as parameter
   137  // This function is integrated in the Valid() function
   138  //
   139  // For easier integration and testing, this function also returns true if the
   140  // string doesn't have any '.' in it.
   141  func validHostname(s string) bool {
   142  	if len(s) == 0 {
   143  		return false
   144  	}
   145  
   146  	s = strings.ToLower(s)
   147  
   148  	maxLength := 253
   149  	if s[len(s)-1] == '.' {
   150  		maxLength = 254
   151  		s = s[:len(s)-1] // remove the last dot --> easier computations
   152  	}
   153  
   154  	if len(s) > maxLength {
   155  		return false
   156  	}
   157  
   158  	labels := strings.Split(s, ".")
   159  
   160  	for _, element := range labels {
   161  		if len(element) < 1 || len(element) > 63 {
   162  			return false
   163  		}
   164  	}
   165  
   166  	valid, _ := regexp.MatchString("^(([a-z0-9]|[a-z0-9][a-z0-9\\-]*[a-z0-9])\\.)*([a-z]+)$", s)
   167  	if !valid {
   168  		if strings.Count(s, ".") == 0 {
   169  			return true
   170  		}
   171  	}
   172  	return valid
   173  }
   174  
   175  // Valid returns true if the address is well formed or false otherwise.
   176  // An address is well formed if it is of the form: ConnType://NetworkAddress.
   177  // ConnType must be one of the constants defined in this file,
   178  // NetworkAddress must contain the IP address + Port number.
   179  // The IP address is validated by net.ParseIP & the port must be included in the
   180  // range [0;65536]. For example, "tls://192.168.1.10:5678".
   181  func (a Address) Valid() bool {
   182  	vals := strings.Split(string(a), typeAddressSep)
   183  	if len(vals) != 2 {
   184  		return false
   185  	}
   186  	if connType(vals[0]) == InvalidConnType {
   187  		return false
   188  	}
   189  
   190  	ip, port, e := net.SplitHostPort(vals[1])
   191  	if e != nil {
   192  		return false
   193  	}
   194  
   195  	p, err := strconv.Atoi(port)
   196  	if err != nil || p < 0 || p > 65535 {
   197  		return false
   198  	}
   199  
   200  	// If hostname is missing then they mean "all IPs", and that's valid.
   201  	if len(ip) == 0 {
   202  		return true
   203  	}
   204  	if net.ParseIP(ip) == nil {
   205  		// if the Host is NOT in the form of *.*.*.* , check whether it has a valid DNS name
   206  		// This includes "localhost", which is NOT recognized by net.ParseIP
   207  		return validHostname(ip)
   208  	}
   209  	return true
   210  }
   211  
   212  // String returns the address as a string.
   213  func (a Address) String() string {
   214  	return string(a)
   215  }
   216  
   217  // Host returns the host part of the address.
   218  // ex: "tcp://127.0.0.1:2000" => "127.0.0.1"
   219  // In case of an error, it returns an empty string.
   220  func (a Address) Host() string {
   221  	na := a.NetworkAddress()
   222  	if na == "" {
   223  		return ""
   224  	}
   225  	h, _, e := net.SplitHostPort(a.NetworkAddress())
   226  	if e != nil {
   227  		return ""
   228  	}
   229  	return h
   230  }
   231  
   232  // Port will return the port part of the Address. In the
   233  // case of an invalid address or an invalid port, it
   234  // will return "".
   235  func (a Address) Port() string {
   236  	na := a.NetworkAddress()
   237  	if na == "" {
   238  		return ""
   239  	}
   240  	_, p, e := net.SplitHostPort(na)
   241  	if e != nil {
   242  		return ""
   243  	}
   244  	return p
   245  
   246  }
   247  
   248  // Public returns true if the address is a public and valid one
   249  // or false otherwise.
   250  // Specifically it checks if it is a private address by checking
   251  // 192.168.**,10.***,127.***,172.16-31.**,169.254.**,^::1,^fd.{0,2}:
   252  func (a Address) Public() bool {
   253  	private, err := regexp.MatchString("(^127\\.)|(^10\\.)|"+
   254  		"(^172\\.1[6-9]\\.)|(^172\\.2[0-9]\\.)|"+
   255  		"(^172\\.3[0-1]\\.)|(^192\\.168\\.)|(^169\\.254)|"+
   256  		"(^\\[::1\\])|(^\\[fd.{0,2}:)", a.NetworkAddressResolved())
   257  	if err != nil {
   258  		return false
   259  	}
   260  	return !private && a.Valid()
   261  }
   262  
   263  // NewAddress takes a connection type and the raw address. It returns a
   264  // correctly formatted address, which will be of type t.
   265  // It doesn't do any checking of ConnType or network.
   266  func NewAddress(t ConnType, network string) Address {
   267  	return Address(string(t) + typeAddressSep + network)
   268  }