mirror of
https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake.git
synced 2025-10-13 20:11:19 -04:00
Use ListenAndServe{TLS} rather than separate Listen and Serve.
This is a port of commit cea86c937dc278ba6b2100c238b1d5206bbae2f0 from meek. Its purpose is to remove the need to copy-paste parts of net/http.Server.ListenAndServeTLS. Here is a copy of the commit message from meek: The net/http package provides ListenAndServe and ListenAndServeTLS functions, but it doesn't provide a way to set up a listener without also entering an infinite serve loop. This matters for ListenAndServeTLS, which sets up a lot of magic behind the scenes for TLS and HTTP/2 support. Formerly, we had copy-pasted code from ListenAndServeTLS, but that code has only gotten more complicated in upstream net/http. The price we pay for this is that it's no longer possible for a server bindaddr to ask to listen on port 0 (i.e., a random ephemeral port). That's because we never get a change to find out what the listening address is, before entering the serve loop. What we gain is HTTP/2 support; formerly our copy-pasted code had the side effect of disabling HTTP/2, because it was copied from an older version and did things like config.NextProtos = []string{"http/1.1"} The new code calls http2.ConfigureServer first, but that's not what's providing HTTP/2 support. HTTP/2 support happens by default. The reason we call http2.ConfigureServer is because we need to set TLSConfig.GetCertificate, and http2.ConfigureServer is a convenient way to initialize TLSConfig in a way that is guaranteed to work with HTTP/2.
This commit is contained in:
parent
c61336c897
commit
19b317e781
1 changed files with 62 additions and 58 deletions
120
server/server.go
120
server/server.go
|
@ -22,6 +22,7 @@ import (
|
||||||
"git.torproject.org/pluggable-transports/goptlib.git"
|
"git.torproject.org/pluggable-transports/goptlib.git"
|
||||||
"git.torproject.org/pluggable-transports/websocket.git/websocket"
|
"git.torproject.org/pluggable-transports/websocket.git/websocket"
|
||||||
"golang.org/x/crypto/acme/autocert"
|
"golang.org/x/crypto/acme/autocert"
|
||||||
|
"golang.org/x/net/http2"
|
||||||
)
|
)
|
||||||
|
|
||||||
const ptMethodName = "snowflake"
|
const ptMethodName = "snowflake"
|
||||||
|
@ -171,64 +172,63 @@ func webSocketHandler(ws *websocket.WebSocket) {
|
||||||
proxy(or, &conn)
|
proxy(or, &conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
func listenTLS(network string, addr *net.TCPAddr, m *autocert.Manager) (net.Listener, error) {
|
func initServer(addr *net.TCPAddr,
|
||||||
// This is cribbed from the source of net/http.Server.ListenAndServeTLS.
|
getCertificate func(*tls.ClientHelloInfo) (*tls.Certificate, error),
|
||||||
// We have to separate the Listen and Serve parts because we need to
|
listenAndServe func(*http.Server)) (*http.Server, error) {
|
||||||
// report the listening address before entering Serve (which is an
|
// We're not capable of listening on port 0 (i.e., an ephemeral port
|
||||||
// infinite loop).
|
// unknown in advance). The reason is that while the net/http package
|
||||||
|
// exposes ListenAndServe and ListenAndServeTLS, those functions never
|
||||||
|
// return, so there's no opportunity to find out what the port number
|
||||||
|
// is, in between the Listen and Serve steps.
|
||||||
// https://groups.google.com/d/msg/Golang-nuts/3F1VRCCENp8/3hcayZiwYM8J
|
// https://groups.google.com/d/msg/Golang-nuts/3F1VRCCENp8/3hcayZiwYM8J
|
||||||
config := &tls.Config{}
|
if addr.Port == 0 {
|
||||||
config.NextProtos = []string{"http/1.1"}
|
return nil, fmt.Errorf("cannot listen on port %d; configure a port using ServerTransportListenAddr", addr.Port)
|
||||||
config.GetCertificate = m.GetCertificate
|
|
||||||
|
|
||||||
conn, err := net.ListenTCP(network, addr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Additionally disable SSLv3 because of the POODLE attack.
|
var config websocket.Config
|
||||||
// http://googleonlinesecurity.blogspot.com/2014/10/this-poodle-bites-exploiting-ssl-30.html
|
config.MaxMessageSize = maxMessageSize
|
||||||
// https://code.google.com/p/go/source/detail?r=ad9e191a51946e43f1abac8b6a2fefbf2291eea7
|
server := &http.Server{
|
||||||
config.MinVersion = tls.VersionTLS10
|
Addr: addr.String(),
|
||||||
|
Handler: config.Handler(webSocketHandler),
|
||||||
tlsListener := tls.NewListener(conn, config)
|
ReadTimeout: requestTimeout,
|
||||||
|
|
||||||
return tlsListener, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func startListener(network string, addr *net.TCPAddr) (net.Listener, error) {
|
|
||||||
ln, err := net.ListenTCP(network, addr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
log.Printf("listening with plain HTTP on %s", ln.Addr())
|
// We need to override server.TLSConfig.GetCertificate--but first
|
||||||
return startServer(ln)
|
// server.TLSConfig needs to be non-nil. If we just create our own new
|
||||||
}
|
// &tls.Config, it will lack the default settings that the net/http
|
||||||
|
// package sets up for things like HTTP/2. Therefore we first call
|
||||||
func startListenerTLS(network string, addr *net.TCPAddr, m *autocert.Manager) (net.Listener, error) {
|
// http2.ConfigureServer for its side effect of initializing
|
||||||
ln, err := listenTLS(network, addr, m)
|
// server.TLSConfig properly. An alternative would be to make a dummy
|
||||||
|
// net.Listener, call Serve on it, and let it return.
|
||||||
|
// https://github.com/golang/go/issues/16588#issuecomment-237386446
|
||||||
|
err := http2.ConfigureServer(server, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return server, err
|
||||||
}
|
}
|
||||||
log.Printf("listening with HTTPS on %s", ln.Addr())
|
server.TLSConfig.GetCertificate = getCertificate
|
||||||
return startServer(ln)
|
|
||||||
|
go listenAndServe(server)
|
||||||
|
|
||||||
|
return server, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func startServer(ln net.Listener) (net.Listener, error) {
|
func startServer(addr *net.TCPAddr) (*http.Server, error) {
|
||||||
go func() {
|
return initServer(addr, nil, func(server *http.Server) {
|
||||||
defer ln.Close()
|
log.Printf("listening with plain HTTP on %s", addr)
|
||||||
var config websocket.Config
|
err := server.ListenAndServe()
|
||||||
config.MaxMessageSize = maxMessageSize
|
|
||||||
s := &http.Server{
|
|
||||||
Handler: config.Handler(webSocketHandler),
|
|
||||||
ReadTimeout: requestTimeout,
|
|
||||||
}
|
|
||||||
err := s.Serve(ln)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("http.Serve: %s", err)
|
log.Printf("error in ListenAndServe: %s", err)
|
||||||
}
|
}
|
||||||
}()
|
})
|
||||||
return ln, nil
|
}
|
||||||
|
|
||||||
|
func startServerTLS(addr *net.TCPAddr, getCertificate func(*tls.ClientHelloInfo) (*tls.Certificate, error)) (*http.Server, error) {
|
||||||
|
return initServer(addr, getCertificate, func(server *http.Server) {
|
||||||
|
log.Printf("listening with HTTPS on %s", addr)
|
||||||
|
err := server.ListenAndServeTLS("", "")
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("error in ListenAndServeTLS: %s", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func getCertificateCacheDir() (string, error) {
|
func getCertificateCacheDir() (string, error) {
|
||||||
|
@ -303,7 +303,7 @@ func main() {
|
||||||
// https://github.com/ietf-wg-acme/acme/blob/master/draft-ietf-acme-acme.md#http-challenge
|
// https://github.com/ietf-wg-acme/acme/blob/master/draft-ietf-acme-acme.md#http-challenge
|
||||||
needHTTP01Listener := !disableTLS
|
needHTTP01Listener := !disableTLS
|
||||||
|
|
||||||
listeners := make([]net.Listener, 0)
|
servers := make([]*http.Server, 0)
|
||||||
for _, bindaddr := range ptInfo.Bindaddrs {
|
for _, bindaddr := range ptInfo.Bindaddrs {
|
||||||
if bindaddr.MethodName != ptMethodName {
|
if bindaddr.MethodName != ptMethodName {
|
||||||
pt.SmethodError(bindaddr.MethodName, "no such method")
|
pt.SmethodError(bindaddr.MethodName, "no such method")
|
||||||
|
@ -320,32 +320,36 @@ func main() {
|
||||||
pt.SmethodError(bindaddr.MethodName, "HTTP-01 ACME listener: "+err.Error())
|
pt.SmethodError(bindaddr.MethodName, "HTTP-01 ACME listener: "+err.Error())
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
server := &http.Server{
|
||||||
|
Addr: addr.String(),
|
||||||
|
Handler: certManager.HTTPHandler(nil),
|
||||||
|
}
|
||||||
go func() {
|
go func() {
|
||||||
log.Fatal(http.Serve(lnHTTP01, certManager.HTTPHandler(nil)))
|
log.Fatal(server.Serve(lnHTTP01))
|
||||||
}()
|
}()
|
||||||
listeners = append(listeners, lnHTTP01)
|
servers = append(servers, server)
|
||||||
needHTTP01Listener = false
|
needHTTP01Listener = false
|
||||||
}
|
}
|
||||||
|
|
||||||
var ln net.Listener
|
var server *http.Server
|
||||||
args := pt.Args{}
|
args := pt.Args{}
|
||||||
if disableTLS {
|
if disableTLS {
|
||||||
args.Add("tls", "no")
|
args.Add("tls", "no")
|
||||||
ln, err = startListener("tcp", bindaddr.Addr)
|
server, err = startServer(bindaddr.Addr)
|
||||||
} else {
|
} else {
|
||||||
args.Add("tls", "yes")
|
args.Add("tls", "yes")
|
||||||
for _, hostname := range acmeHostnames {
|
for _, hostname := range acmeHostnames {
|
||||||
args.Add("hostname", hostname)
|
args.Add("hostname", hostname)
|
||||||
}
|
}
|
||||||
ln, err = startListenerTLS("tcp", bindaddr.Addr, certManager)
|
server, err = startServerTLS(bindaddr.Addr, certManager.GetCertificate)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("error opening listener: %s", err)
|
log.Printf("error opening listener: %s", err)
|
||||||
pt.SmethodError(bindaddr.MethodName, err.Error())
|
pt.SmethodError(bindaddr.MethodName, err.Error())
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
pt.SmethodArgs(bindaddr.MethodName, ln.Addr(), args)
|
pt.SmethodArgs(bindaddr.MethodName, bindaddr.Addr, args)
|
||||||
listeners = append(listeners, ln)
|
servers = append(servers, server)
|
||||||
}
|
}
|
||||||
pt.SmethodsDone()
|
pt.SmethodsDone()
|
||||||
|
|
||||||
|
@ -366,8 +370,8 @@ func main() {
|
||||||
|
|
||||||
// signal received, shut down
|
// signal received, shut down
|
||||||
log.Printf("caught signal %q, exiting", sig)
|
log.Printf("caught signal %q, exiting", sig)
|
||||||
for _, ln := range listeners {
|
for _, server := range servers {
|
||||||
ln.Close()
|
server.Close()
|
||||||
}
|
}
|
||||||
for n := range handlerChan {
|
for n := range handlerChan {
|
||||||
numHandlers += n
|
numHandlers += n
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue