@marcel91200 / caddy Mirror · read-only
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 10, 22:30 UTC

master
go 338 lines 9.18 KB
Raw
1 // Copyright 2015 Matthew Holt and The Caddy Authors
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 // Even though the filename ends in _unix.go, we still have to specify the
16 // build constraint here, because the filename convention only works for
17 // literal GOOS values, and "unix" is a shortcut unique to build tags.
18 //go:build unix && !solaris
19
20 package caddy
21
22 import (
23 "context"
24 "errors"
25 "fmt"
26 "io"
27 "io/fs"
28 "net"
29 "os"
30 "runtime"
31 "slices"
32 "strconv"
33 "strings"
34 "sync"
35 "sync/atomic"
36 "syscall"
37
38 "go.uber.org/zap"
39 "golang.org/x/sys/unix"
40 )
41
42 // reuseUnixSocket copies and reuses the unix domain socket (UDS) if we already
43 // have it open; if not, unlink it so we can have it.
44 // No-op if not a unix network.
45 func reuseUnixSocket(network, addr string) (any, error) {
46 socketKey := listenerKey(network, addr)
47
48 socket, exists := unixSockets[socketKey]
49 if exists {
50 // make copy of file descriptor
51 socketFile, err := socket.File() // does dup() deep down
52 if err != nil {
53 return nil, err
54 }
55 defer socketFile.Close()
56
57 // use copied fd to make new Listener or PacketConn, then replace
58 // it in the map so that future copies always come from the most
59 // recent fd (as the previous ones will be closed, and we'd get
60 // "use of closed network connection" errors) -- note that we
61 // preserve the *pointer* to the counter (not just the value) so
62 // that all socket wrappers will refer to the same value
63 switch unixSocket := socket.(type) {
64 case *unixListener:
65 ln, err := net.FileListener(socketFile)
66 if err != nil {
67 return nil, err
68 }
69 unixSocket.count.Add(1)
70 unixSockets[socketKey] = &unixListener{
71 UnixListener: ln.(*net.UnixListener),
72 mapKey: socketKey,
73 count: unixSocket.count,
74 }
75
76 case *unixConn:
77 pc, err := net.FilePacketConn(socketFile)
78 if err != nil {
79 return nil, err
80 }
81 unixSocket.count.Add(1)
82 unixSockets[socketKey] = &unixConn{
83 UnixConn: pc.(*net.UnixConn),
84 mapKey: socketKey,
85 count: unixSocket.count,
86 }
87 }
88
89 return unixSockets[socketKey], nil
90 }
91 if isAbstractUnixSocket(addr) {
92 return nil, nil
93 }
94
95 // from what I can tell after some quick research, it's quite common for programs to
96 // leave their socket file behind after they close, so the typical pattern is to
97 // unlink it before you bind to it -- this is often crucial if the last program using
98 // it was killed forcefully without a chance to clean up the socket, but there is a
99 // race, as the comment in net.UnixListener.close() explains... oh well, I guess?
100 if err := syscall.Unlink(addr); err != nil && !errors.Is(err, fs.ErrNotExist) {
101 return nil, err
102 }
103
104 return nil, nil
105 }
106
107 // listenReusable creates a new listener for the given network and address, and adds it to listenerPool.
108 func listenReusable(ctx context.Context, lnKey string, network, address string, config net.ListenConfig) (any, error) {
109 // even though SO_REUSEPORT lets us bind the socket multiple times,
110 // we still put it in the listenerPool so we can count how many
111 // configs are using this socket; necessary to ensure we can know
112 // whether to enforce shutdown delays, for example (see #5393).
113 var (
114 ln io.Closer
115 err error
116 socketFile *os.File
117 )
118
119 fd := slices.Contains([]string{"fd", "fdgram"}, network)
120 if fd {
121 socketFd, err := strconv.ParseUint(address, 0, strconv.IntSize)
122 if err != nil {
123 return nil, fmt.Errorf("invalid file descriptor: %v", err)
124 }
125
126 func() {
127 socketFilesMu.Lock()
128 defer socketFilesMu.Unlock()
129
130 socketFdWide := uintptr(socketFd)
131 var ok bool
132
133 socketFile, ok = socketFiles[socketFdWide]
134
135 if !ok {
136 socketFile = os.NewFile(socketFdWide, lnKey)
137 if socketFile != nil {
138 socketFiles[socketFdWide] = socketFile
139 }
140 }
141 }()
142
143 if socketFile == nil {
144 return nil, fmt.Errorf("invalid socket file descriptor: %d", socketFd)
145 }
146 } else {
147 // wrap any Control function set by the user so we can also add our reusePort control without clobbering theirs
148 oldControl := config.Control
149 config.Control = func(network, address string, c syscall.RawConn) error {
150 if oldControl != nil {
151 if err := oldControl(network, address, c); err != nil {
152 return err
153 }
154 }
155 return reusePort(network, address, c)
156 }
157 }
158
159 datagram := slices.Contains([]string{"udp", "udp4", "udp6", "unixgram", "fdgram"}, network)
160 if datagram {
161 if fd {
162 ln, err = net.FilePacketConn(socketFile)
163 } else {
164 ln, err = config.ListenPacket(ctx, network, address)
165 }
166 } else {
167 if fd {
168 ln, err = net.FileListener(socketFile)
169 } else {
170 ln, err = config.Listen(ctx, network, address)
171 }
172 }
173
174 if err == nil {
175 listenerPool.LoadOrStore(lnKey, nil)
176 }
177
178 if datagram {
179 if !fd {
180 // TODO: Not 100% sure this is necessary, but we do this for net.UnixListener, so...
181 if unix, ok := ln.(*net.UnixConn); ok {
182 cnt := new(atomic.Int32)
183 cnt.Store(1)
184 ln = &unixConn{UnixConn: unix, mapKey: lnKey, count: cnt}
185 unixSockets[lnKey] = ln.(*unixConn)
186 }
187 }
188 // lightly wrap the connection so that when it is closed,
189 // we can decrement the usage pool counter
190 if specificLn, ok := ln.(net.PacketConn); ok {
191 ln = deletePacketConn{specificLn, lnKey}
192 }
193 } else {
194 if !fd {
195 // if new listener is a unix socket, make sure we can reuse it later
196 // (we do our own "unlink on close" -- not required, but more tidy)
197 if unix, ok := ln.(*net.UnixListener); ok {
198 unix.SetUnlinkOnClose(false)
199 cnt := new(atomic.Int32)
200 cnt.Store(1)
201 ln = &unixListener{UnixListener: unix, mapKey: lnKey, count: cnt}
202 unixSockets[lnKey] = ln.(*unixListener)
203 }
204 }
205 // lightly wrap the listener so that when it is closed,
206 // we can decrement the usage pool counter
207 if specificLn, ok := ln.(net.Listener); ok {
208 ln = deleteListener{specificLn, lnKey}
209 }
210 }
211
212 // other types, I guess we just return them directly
213 return ln, err
214 }
215
216 // reusePort sets SO_REUSEPORT. Ineffective for unix sockets.
217 func reusePort(network, address string, conn syscall.RawConn) error {
218 if IsUnixNetwork(network) {
219 return nil
220 }
221 return conn.Control(func(descriptor uintptr) {
222 if err := unix.SetsockoptInt(int(descriptor), unix.SOL_SOCKET, unixSOREUSEPORT, 1); err != nil {
223 Log().Error("setting SO_REUSEPORT",
224 zap.String("network", network),
225 zap.String("address", address),
226 zap.Uintptr("descriptor", descriptor),
227 zap.Error(err))
228 }
229 })
230 }
231
232 type unixListener struct {
233 *net.UnixListener
234 mapKey string
235 count *atomic.Int32
236 closed atomic.Bool
237 }
238
239 func (uln *unixListener) Close() error {
240 if !uln.closed.CompareAndSwap(false, true) {
241 return net.ErrClosed
242 }
243 unixSocketsMu.Lock()
244 defer unixSocketsMu.Unlock()
245
246 var name string
247 if addr := uln.Addr(); addr != nil {
248 name = addr.String()
249 }
250 newCount := uln.count.Add(-1)
251 err := uln.UnixListener.Close()
252 if newCount == 0 {
253 delete(unixSockets, uln.mapKey)
254 if name != "" && !isAbstractUnixSocket(name) {
255 _ = syscall.Unlink(name)
256 }
257 }
258 return err
259 }
260
261 type unixConn struct {
262 *net.UnixConn
263 mapKey string
264 count *atomic.Int32
265 closed atomic.Bool
266 }
267
268 func (uc *unixConn) Close() error {
269 if !uc.closed.CompareAndSwap(false, true) {
270 return net.ErrClosed
271 }
272 unixSocketsMu.Lock()
273 defer unixSocketsMu.Unlock()
274
275 var name string
276 if addr := uc.LocalAddr(); addr != nil {
277 name = addr.String()
278 }
279 newCount := uc.count.Add(-1)
280 err := uc.UnixConn.Close()
281 if newCount == 0 {
282 delete(unixSockets, uc.mapKey)
283 if name != "" && !isAbstractUnixSocket(name) {
284 _ = syscall.Unlink(name)
285 }
286 }
287 return err
288 }
289
290 func isAbstractUnixSocket(name string) bool {
291 return (runtime.GOOS == "linux" || runtime.GOOS == "android") && strings.HasPrefix(name, "@")
292 }
293
294 func (uc *unixConn) Unwrap() net.PacketConn {
295 return uc.UnixConn
296 }
297
298 // unixSockets keeps track of the currently-active unix sockets
299 // so we can transfer their FDs gracefully during reloads.
300 var unixSockets = make(map[string]interface {
301 File() (*os.File, error)
302 })
303
304 // socketFiles is a fd -> *os.File map used to make a FileListener/FilePacketConn from a socket file descriptor.
305 var socketFiles = map[uintptr]*os.File{}
306
307 // socketFilesMu synchronizes socketFiles insertions
308 var socketFilesMu sync.Mutex
309
310 // deleteListener is a type that simply deletes itself
311 // from the listenerPool when it closes. It is used
312 // solely for the purpose of reference counting (i.e.
313 // counting how many configs are using a given socket).
314 type deleteListener struct {
315 net.Listener
316 lnKey string
317 }
318
319 func (dl deleteListener) Close() error {
320 _, _ = listenerPool.Delete(dl.lnKey)
321 return dl.Listener.Close()
322 }
323
324 // deletePacketConn is like deleteListener, but
325 // for net.PacketConns.
326 type deletePacketConn struct {
327 net.PacketConn
328 lnKey string
329 }
330
331 func (dl deletePacketConn) Close() error {
332 _, _ = listenerPool.Delete(dl.lnKey)
333 return dl.PacketConn.Close()
334 }
335
336 func (dl deletePacketConn) Unwrap() net.PacketConn {
337 return dl.PacketConn
338 }