2014-02-10 03:55:36 +00:00
|
|
|
/*
|
Rename import paths from camlistore.org to perkeep.org.
Part of the project renaming, issue #981.
After this, users will need to mv their $GOPATH/src/camlistore.org to
$GOPATH/src/perkeep.org. Sorry.
This doesn't yet rename the tools like camlistored, camput, camget,
camtool, etc.
Also, this only moves the lru package to internal. More will move to
internal later.
Also, this doesn't yet remove the "/pkg/" directory. That'll likely
happen later.
This updates some docs, but not all.
devcam test now passes again, even with Go 1.10 (which requires vet
checks are clean too). So a bunch of vet tests are fixed in this CL
too, and a bunch of other broken tests are now fixed (introduced from
the past week of merging the CL backlog).
Change-Id: If580db1691b5b99f8ed6195070789b1f44877dd4
2018-01-01 22:41:41 +00:00
|
|
|
Copyright 2014 The Perkeep Authors
|
2014-02-10 03:55:36 +00:00
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
package netutil
|
|
|
|
|
|
|
|
import (
|
2014-09-15 13:15:59 +00:00
|
|
|
"errors"
|
2014-07-16 18:05:52 +00:00
|
|
|
"fmt"
|
2014-02-10 03:55:36 +00:00
|
|
|
"net"
|
2014-07-29 18:23:14 +00:00
|
|
|
"net/url"
|
|
|
|
"strings"
|
2014-02-10 03:55:36 +00:00
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
// AwaitReachable tries to make a TCP connection to addr regularly.
|
|
|
|
// It returns an error if it's unable to make a connection before maxWait.
|
|
|
|
func AwaitReachable(addr string, maxWait time.Duration) error {
|
|
|
|
done := time.Now().Add(maxWait)
|
|
|
|
for time.Now().Before(done) {
|
|
|
|
c, err := net.Dial("tcp", addr)
|
|
|
|
if err == nil {
|
|
|
|
c.Close()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
time.Sleep(100 * time.Millisecond)
|
|
|
|
}
|
2014-07-16 18:05:52 +00:00
|
|
|
return fmt.Errorf("%v unreachable for %v", addr, maxWait)
|
2014-02-10 03:55:36 +00:00
|
|
|
}
|
2014-07-29 18:23:14 +00:00
|
|
|
|
|
|
|
// HostPort takes a urlStr string URL, and returns a host:port string suitable
|
|
|
|
// to passing to net.Dial, with the port set as the scheme's default port if
|
|
|
|
// absent.
|
|
|
|
func HostPort(urlStr string) (string, error) {
|
2015-12-26 21:32:49 +00:00
|
|
|
// TODO: rename this function to URLHostPort instead, like
|
|
|
|
// ListenHostPort below.
|
2014-07-29 18:23:14 +00:00
|
|
|
u, err := url.Parse(urlStr)
|
|
|
|
if err != nil {
|
|
|
|
return "", fmt.Errorf("could not parse %q as a url: %v", urlStr, err)
|
|
|
|
}
|
|
|
|
if u.Scheme == "" {
|
|
|
|
return "", fmt.Errorf("url %q has no scheme", urlStr)
|
|
|
|
}
|
|
|
|
hostPort := u.Host
|
|
|
|
if hostPort == "" || strings.HasPrefix(hostPort, ":") {
|
|
|
|
return "", fmt.Errorf("url %q has no host", urlStr)
|
|
|
|
}
|
|
|
|
idx := strings.Index(hostPort, "]")
|
|
|
|
if idx == -1 {
|
|
|
|
idx = 0
|
|
|
|
}
|
|
|
|
if !strings.Contains(hostPort[idx:], ":") {
|
|
|
|
if u.Scheme == "https" {
|
|
|
|
hostPort += ":443"
|
|
|
|
} else {
|
|
|
|
hostPort += ":80"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return hostPort, nil
|
|
|
|
}
|
2014-09-15 13:15:59 +00:00
|
|
|
|
2015-12-26 21:32:49 +00:00
|
|
|
// ListenHostPort maps a listen address into a host:port string.
|
|
|
|
// If the host part in listenAddr is empty or 0.0.0.0, localhost
|
|
|
|
// is used instead.
|
|
|
|
func ListenHostPort(listenAddr string) (string, error) {
|
|
|
|
hp := listenAddr
|
|
|
|
if strings.HasPrefix(hp, ":") {
|
|
|
|
hp = "localhost" + hp
|
|
|
|
} else if strings.HasPrefix(hp, "0.0.0.0:") {
|
|
|
|
hp = "localhost:" + hp[len("0.0.0.0:"):]
|
|
|
|
}
|
|
|
|
if _, _, err := net.SplitHostPort(hp); err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
return hp, nil
|
|
|
|
}
|
|
|
|
|
2014-11-23 15:43:09 +00:00
|
|
|
// ListenOnLocalRandomPort returns a TCP listener on a random
|
|
|
|
// localhost port.
|
2014-09-15 13:15:59 +00:00
|
|
|
func ListenOnLocalRandomPort() (net.Listener, error) {
|
|
|
|
ip, err := Localhost()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2014-11-23 15:43:09 +00:00
|
|
|
return net.ListenTCP("tcp", &net.TCPAddr{IP: ip, Port: 0})
|
2014-09-15 13:15:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Localhost returns the first address found when
|
|
|
|
// doing a lookup of "localhost". If not successful,
|
|
|
|
// it looks for an ip on the loopback interfaces.
|
|
|
|
func Localhost() (net.IP, error) {
|
|
|
|
if ip := localhostLookup(); ip != nil {
|
|
|
|
return ip, nil
|
|
|
|
}
|
|
|
|
if ip := loopbackIP(); ip != nil {
|
|
|
|
return ip, nil
|
|
|
|
}
|
2017-12-10 09:13:00 +00:00
|
|
|
return nil, errors.New("no loopback ip found")
|
2014-09-15 13:15:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// localhostLookup looks for a loopback IP by resolving localhost.
|
|
|
|
func localhostLookup() net.IP {
|
|
|
|
if ips, err := net.LookupIP("localhost"); err == nil && len(ips) > 0 {
|
|
|
|
return ips[0]
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2014-11-23 15:43:09 +00:00
|
|
|
// loopbackIP returns the first loopback IP address sniffing network
|
|
|
|
// interfaces or nil if none is found.
|
2014-09-15 13:15:59 +00:00
|
|
|
func loopbackIP() net.IP {
|
|
|
|
interfaces, err := net.Interfaces()
|
|
|
|
if err != nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
for _, inf := range interfaces {
|
2014-11-23 15:43:09 +00:00
|
|
|
const flagUpLoopback = net.FlagUp | net.FlagLoopback
|
2014-09-15 13:15:59 +00:00
|
|
|
if inf.Flags&flagUpLoopback == flagUpLoopback {
|
2014-11-23 15:43:09 +00:00
|
|
|
addrs, _ := inf.Addrs()
|
2014-09-15 13:15:59 +00:00
|
|
|
for _, addr := range addrs {
|
|
|
|
ip, _, err := net.ParseCIDR(addr.String())
|
|
|
|
if err == nil && ip.IsLoopback() {
|
|
|
|
return ip
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2016-06-28 00:12:47 +00:00
|
|
|
|
|
|
|
// RandPort returns a random port to listen on.
|
|
|
|
func RandPort() (int, error) {
|
|
|
|
var port int
|
|
|
|
addr, err := net.ResolveTCPAddr("tcp", "localhost:0")
|
|
|
|
if err != nil {
|
|
|
|
return port, err
|
|
|
|
}
|
|
|
|
listener, err := net.ListenTCP("tcp", addr)
|
|
|
|
if err != nil {
|
|
|
|
return port, fmt.Errorf("could not listen to find random port: %v", err)
|
|
|
|
}
|
|
|
|
randAddr := listener.Addr().(*net.TCPAddr)
|
|
|
|
if err := listener.Close(); err != nil {
|
|
|
|
return port, fmt.Errorf("could not close random listener: %v", err)
|
|
|
|
}
|
|
|
|
return randAddr.Port, nil
|
|
|
|
}
|
|
|
|
|
2018-01-08 02:52:27 +00:00
|
|
|
// HasPort when given a string of the form "host", "host:port", or
|
2016-06-28 00:12:47 +00:00
|
|
|
// "[ipv6::address]:port", returns true if the string includes a port.
|
|
|
|
func HasPort(s string) bool {
|
|
|
|
return strings.LastIndex(s, ":") > strings.LastIndex(s, "]")
|
|
|
|
}
|
server/camlistored: use Let's Encrypt
Or to be more precise, golang.org/x/crypto/acme/autocert
The default behaviour regarding HTTPS certificates changes as such:
1) If the high-level config does not specify a certificate, the
low-level config used to be generated with a default certificate path.
This is no longer the case.
2) If the low-level config does not specify a certificate, we used to
generate self-signed ones at the default path. This is no longer always
the case. We only do this if our hostname does not look like an FQDN,
otherwise we try Let's Encrypt.
3) As a result, if the high-level config does not specify a certificate,
and the hostname looks like an FQDN, it is no longer the case that we'll
generate a self-signed. Let's Encrypt will be tried instead.
To sum up, the new rules are:
If cert/key files are specified, and found, use them.
If cert/key files are specified, not found, and the default values,
generate them (self-signed CA used as a cert), and use them.
If cert/key files are not specified, use Let's Encrypt if we have an
FQDN, otherwise generate self-signed.
Regarding cert caching:
On non-GCE, store the autocert cache dir in
osutil.CamliConfigDir()/letsencrypt.cache
On GCE, store in /tmp/camli-letsencrypt.cache
Fixes #701
Fixes #859
Change-Id: Id78a9c6f113fa93e38d690033c10a749d1844ea6
2016-12-05 18:39:50 +00:00
|
|
|
|
|
|
|
// IsFQDN reports whether domain looks like a fully qualified domain name.
|
|
|
|
func IsFQDN(domain string) bool {
|
|
|
|
// TODO(mpl): there's probably a regexp for all this...
|
|
|
|
if domain == "localhost" {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
if !strings.Contains(domain, ".") {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
if strings.Contains(domain, "/") {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
if net.ParseIP(domain) != nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|