123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157 |
- // Copyright 2017 The Go Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- package autocert
- import (
- "crypto/tls"
- "log"
- "net"
- "os"
- "path/filepath"
- "runtime"
- "time"
- )
- // NewListener returns a net.Listener that listens on the standard TLS
- // port (443) on all interfaces and returns *tls.Conn connections with
- // LetsEncrypt certificates for the provided domain or domains.
- //
- // It enables one-line HTTPS servers:
- //
- // log.Fatal(http.Serve(autocert.NewListener("example.com"), handler))
- //
- // NewListener is a convenience function for a common configuration.
- // More complex or custom configurations can use the autocert.Manager
- // type instead.
- //
- // Use of this function implies acceptance of the LetsEncrypt Terms of
- // Service. If domains is not empty, the provided domains are passed
- // to HostWhitelist. If domains is empty, the listener will do
- // LetsEncrypt challenges for any requested domain, which is not
- // recommended.
- //
- // Certificates are cached in a "golang-autocert" directory under an
- // operating system-specific cache or temp directory. This may not
- // be suitable for servers spanning multiple machines.
- //
- // The returned listener uses a *tls.Config that enables HTTP/2, and
- // should only be used with servers that support HTTP/2.
- //
- // The returned Listener also enables TCP keep-alives on the accepted
- // connections. The returned *tls.Conn are returned before their TLS
- // handshake has completed.
- func NewListener(domains ...string) net.Listener {
- m := &Manager{
- Prompt: AcceptTOS,
- }
- if len(domains) > 0 {
- m.HostPolicy = HostWhitelist(domains...)
- }
- dir := cacheDir()
- if err := os.MkdirAll(dir, 0700); err != nil {
- log.Printf("warning: autocert.NewListener not using a cache: %v", err)
- } else {
- m.Cache = DirCache(dir)
- }
- return m.Listener()
- }
- // Listener listens on the standard TLS port (443) on all interfaces
- // and returns a net.Listener returning *tls.Conn connections.
- //
- // The returned listener uses a *tls.Config that enables HTTP/2, and
- // should only be used with servers that support HTTP/2.
- //
- // The returned Listener also enables TCP keep-alives on the accepted
- // connections. The returned *tls.Conn are returned before their TLS
- // handshake has completed.
- //
- // Unlike NewListener, it is the caller's responsibility to initialize
- // the Manager m's Prompt, Cache, HostPolicy, and other desired options.
- func (m *Manager) Listener() net.Listener {
- ln := &listener{
- m: m,
- conf: m.TLSConfig(),
- }
- ln.tcpListener, ln.tcpListenErr = net.Listen("tcp", ":443")
- return ln
- }
- type listener struct {
- m *Manager
- conf *tls.Config
- tcpListener net.Listener
- tcpListenErr error
- }
- func (ln *listener) Accept() (net.Conn, error) {
- if ln.tcpListenErr != nil {
- return nil, ln.tcpListenErr
- }
- conn, err := ln.tcpListener.Accept()
- if err != nil {
- return nil, err
- }
- tcpConn := conn.(*net.TCPConn)
- // Because Listener is a convenience function, help out with
- // this too. This is not possible for the caller to set once
- // we return a *tcp.Conn wrapping an inaccessible net.Conn.
- // If callers don't want this, they can do things the manual
- // way and tweak as needed. But this is what net/http does
- // itself, so copy that. If net/http changes, we can change
- // here too.
- tcpConn.SetKeepAlive(true)
- tcpConn.SetKeepAlivePeriod(3 * time.Minute)
- return tls.Server(tcpConn, ln.conf), nil
- }
- func (ln *listener) Addr() net.Addr {
- if ln.tcpListener != nil {
- return ln.tcpListener.Addr()
- }
- // net.Listen failed. Return something non-nil in case callers
- // call Addr before Accept:
- return &net.TCPAddr{IP: net.IP{0, 0, 0, 0}, Port: 443}
- }
- func (ln *listener) Close() error {
- if ln.tcpListenErr != nil {
- return ln.tcpListenErr
- }
- return ln.tcpListener.Close()
- }
- func homeDir() string {
- if runtime.GOOS == "windows" {
- return os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH")
- }
- if h := os.Getenv("HOME"); h != "" {
- return h
- }
- return "/"
- }
- func cacheDir() string {
- const base = "golang-autocert"
- switch runtime.GOOS {
- case "darwin":
- return filepath.Join(homeDir(), "Library", "Caches", base)
- case "windows":
- for _, ev := range []string{"APPDATA", "CSIDL_APPDATA", "TEMP", "TMP"} {
- if v := os.Getenv(ev); v != "" {
- return filepath.Join(v, base)
- }
- }
- // Worst case:
- return filepath.Join(homeDir(), base)
- }
- if xdg := os.Getenv("XDG_CACHE_HOME"); xdg != "" {
- return filepath.Join(xdg, base)
- }
- return filepath.Join(homeDir(), ".cache", base)
- }
|