1// Copyright 2015 CoreOS, Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15// Package netutil implements network-related utility functions.
16package netutil
17
18import (
19	"net"
20	"net/url"
21	"reflect"
22	"sort"
23
24	"github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/pkg/capnslog"
25	"github.com/coreos/etcd/pkg/types"
26)
27
28var (
29	plog = capnslog.NewPackageLogger("github.com/coreos/etcd/pkg", "netutil")
30
31	// indirection for testing
32	resolveTCPAddr = net.ResolveTCPAddr
33)
34
35// resolveTCPAddrs is a convenience wrapper for net.ResolveTCPAddr.
36// resolveTCPAddrs return a new set of url.URLs, in which all DNS hostnames
37// are resolved.
38func resolveTCPAddrs(urls [][]url.URL) ([][]url.URL, error) {
39	newurls := make([][]url.URL, 0)
40	for _, us := range urls {
41		nus := make([]url.URL, len(us))
42		for i, u := range us {
43			nu, err := url.Parse(u.String())
44			if err != nil {
45				return nil, err
46			}
47			nus[i] = *nu
48		}
49		for i, u := range nus {
50			host, _, err := net.SplitHostPort(u.Host)
51			if err != nil {
52				plog.Errorf("could not parse url %s during tcp resolving", u.Host)
53				return nil, err
54			}
55			if host == "localhost" {
56				continue
57			}
58			if net.ParseIP(host) != nil {
59				continue
60			}
61			tcpAddr, err := resolveTCPAddr("tcp", u.Host)
62			if err != nil {
63				plog.Errorf("could not resolve host %s", u.Host)
64				return nil, err
65			}
66			plog.Infof("resolving %s to %s", u.Host, tcpAddr.String())
67			nus[i].Host = tcpAddr.String()
68		}
69		newurls = append(newurls, nus)
70	}
71	return newurls, nil
72}
73
74// urlsEqual checks equality of url.URLS between two arrays.
75// This check pass even if an URL is in hostname and opposite is in IP address.
76func urlsEqual(a []url.URL, b []url.URL) bool {
77	if len(a) != len(b) {
78		return false
79	}
80	urls, err := resolveTCPAddrs([][]url.URL{a, b})
81	if err != nil {
82		return false
83	}
84	a, b = urls[0], urls[1]
85	sort.Sort(types.URLs(a))
86	sort.Sort(types.URLs(b))
87	for i := range a {
88		if !reflect.DeepEqual(a[i], b[i]) {
89			return false
90		}
91	}
92
93	return true
94}
95
96func URLStringsEqual(a []string, b []string) bool {
97	if len(a) != len(b) {
98		return false
99	}
100	urlsA := make([]url.URL, 0)
101	for _, str := range a {
102		u, err := url.Parse(str)
103		if err != nil {
104			return false
105		}
106		urlsA = append(urlsA, *u)
107	}
108	urlsB := make([]url.URL, 0)
109	for _, str := range b {
110		u, err := url.Parse(str)
111		if err != nil {
112			return false
113		}
114		urlsB = append(urlsB, *u)
115	}
116
117	return urlsEqual(urlsA, urlsB)
118}
119