netboot/tftp/handlers.go
David Anderson 551e4aee91 Support the tsize extension for TFTP.
It turns out many PXE firmwares request tsize to size the download
buffer, and will refuse to download at all if the server cannot
provide a tsize.

This unfortunately breaks API compatibility to add a size parameter,
but I think it's an unfortunate necessity to make this work.
2016-08-08 20:41:12 -07:00

60 lines
1.7 KiB
Go

// Copyright 2016 Google Inc.
//
// 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 tftp
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"net"
"os"
"path/filepath"
)
// FilesystemHandler returns a Handler that serves files in root.
func FilesystemHandler(root string) (Handler, error) {
root, err := filepath.Abs(root)
if err != nil {
return nil, err
}
root = filepath.ToSlash(root)
return func(path string, addr net.Addr) (io.ReadCloser, int64, error) {
// Join with a root, which gets rid of directory traversal
// attempts. Then we join that canonicalized path with the
// actual root, which resolves to the actual on-disk file to
// serve.
path = filepath.Join("/", path)
path = filepath.FromSlash(filepath.Join(root, path))
st, err := os.Stat(path)
if err != nil {
return nil, 0, err
}
if !st.Mode().IsRegular() {
return nil, 0, fmt.Errorf("requested path %q is not a file", path)
}
f, err := os.Open(path)
return f, st.Size(), err
}, nil
}
// ConstantHandler returns a Handler that serves bs for all requested paths.
func ConstantHandler(bs []byte) Handler {
return func(path string, addr net.Addr) (io.ReadCloser, int64, error) {
return ioutil.NopCloser(bytes.NewBuffer(bs)), int64(len(bs)), nil
}
}