Commit ad6c44cc authored by Geoff Simmons's avatar Geoff Simmons

Initial commit

parents
Pipeline #471 failed with stages
See LICENSE for details.
Copyright (c) 2020 UPLEX Nils Goroll Systemoptimierung
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
# Copyright (c) 2020 UPLEX Nils Goroll Systemoptimierung
# All rights reserved
#
# Author: Geoffrey Simmons <geoffrey.simmons@uplex.de>
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
all: build doc
# Requires gogitversion: https://github.com/slimhazard/gogitversion
http-faccess: main.go
go fmt
go generate
go build
build: http-faccess
# Requires goreadme: https://github.com/dmjones/goreadme
README.md: doc.go
goreadme README.md
doc: README.md
check: build
golint
go vet
test: check
clean:
go clean
rm -f main_version.go
rm -f http-faccess
install: all
go install
VERSION=`git describe --always 2>/dev/null || echo unknown`
container:
docker build -t http-faccess:${VERSION} -f Dockerfile .
This diff is collapsed.
This diff is collapsed.
module code.uplex.de/testing/http-faccess
require golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4 h1:sfkvUWPNGwSV+8/fNqctR5lS2AqCSqYwXdrjCxp/dXo=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
/*
* Copyright (c) 2020 UPLEX Nils Goroll Systemoptimierung
* All rights reserved
*
* Author: Geoffrey Simmons <geoffrey.simmons@uplex.de>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
package main
//go:generate gogitversion -p main
import (
"flag"
"fmt"
"log"
"net"
"net/http"
"os"
"strings"
"syscall"
"time"
"golang.org/x/sys/unix"
)
var (
dirfd = unix.AT_FDCWD
logger, errLogger *log.Logger
address = flag.String("address", ":7357",
"listen address, [addr]:port for IP,\n"+
"unix@/path for unix socket")
base = flag.String("base", "", "base directory (default cwd)")
access = flag.String("access", "", "access log file "+
"(default stdout)")
errLog = flag.String("error", "", "error log file "+
"(default stderr, - for stdout)")
gid = flag.Int("gid", -1, "group id of the unix socket listener, -1 to"+
" leave unchanged,\nignored for IP listeners")
mode = flag.Int("mode", -1, "permissions of the unix socket listener, "+
"-1 to leave unchanged,\nignored for IP listeners")
versionF = flag.Bool("version", false, "show version and exit")
allowed = map[string]struct{}{
http.MethodGet: struct{}{},
http.MethodHead: struct{}{},
}
)
// Date format for Common Log Format
const common = "02/Jan/2006:15:04:05 -0700"
func reqLog(req *http.Request, now time.Time, status int) {
clientIP := req.RemoteAddr
if colon := strings.LastIndex(clientIP, ":"); colon != -1 {
clientIP = clientIP[:colon]
}
logger.Printf("%s - - [%s] \"%s %s %s\" %d 0\n", clientIP,
now.Format(common), req.Method, req.RequestURI, req.Proto,
status)
}
func handle(resp http.ResponseWriter, req *http.Request) {
now := time.Now()
resp.Header().Set("Content-Type", "text/plain")
resp.Header().Set("Content-Length", "0")
resp.Header().Set("Cache-Control", "no-store")
status := http.StatusTeapot
if _, ok := allowed[req.Method]; !ok {
status = http.StatusMethodNotAllowed
} else if path := req.URL.Path[1:]; path == "" {
status = http.StatusNoContent
} else {
switch err := unix.Faccessat(dirfd, path, unix.R_OK,
unix.AT_EACCESS); err {
case nil:
status = http.StatusNoContent
case syscall.ENOENT, syscall.ENOTDIR:
status = http.StatusNotFound
case syscall.EACCES:
status = http.StatusForbidden
default:
status = http.StatusInternalServerError
errLogger.Printf("%s \"%s %s %s\": %s (%d)\n",
req.RemoteAddr, req.Method,
req.RequestURI, req.Proto, err,
int(err.(syscall.Errno)))
}
}
defer reqLog(req, now, status)
resp.WriteHeader(status)
}
func main() {
var err error
flag.Parse()
if *versionF {
fmt.Println("http-faccess version", version)
os.Exit(0)
}
out := os.Stdout
if *access != "" && *access != "-" {
if out, err = os.OpenFile(*access,
os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644); err != nil {
log.Fatalf(*access, ": ", err)
}
}
logger = log.New(out, "", log.LstdFlags)
logger.Println("starting http-faccess version", version)
logger.SetPrefix("[" + os.Args[0] + "] ")
out = os.Stderr
if *errLog != "" {
if *errLog != "-" {
out = os.Stdout
} else if out, err = os.OpenFile(*errLog,
os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644); err != nil {
logger.Fatal(*errLog, ": ", err)
}
}
errLogger = log.New(out, "", log.Ldate|log.Lmicroseconds)
if *base != "" {
if info, err := os.Stat(*base); err != nil {
errLogger.Fatal(*base, ": ", err)
} else if !info.IsDir() {
errLogger.Fatal(*base, ": not a directory")
}
if dirfd, err = unix.Open(*base, os.O_RDONLY, 0); err != nil {
errLogger.Fatal(*base, ": ", err)
}
defer unix.Close(dirfd)
}
if *base == "" {
if cwd, err := os.Getwd(); err != nil {
// Not fatal because this is for logging, and
// AT_FDCWD might work.
errLogger.Println("Cannot get cwd:", err)
} else {
logger.Println("base directory:", cwd)
}
} else {
logger.Println("base directory:", *base)
}
mux := http.NewServeMux()
mux.HandleFunc("/", handle)
server := http.Server{Handler: mux}
network := "tcp"
if strings.HasPrefix(*address, "unix@") {
*address = (*address)[5:]
if _, err = os.Stat(*address); err == nil {
logger.Println("deleting", *address)
if err = os.Remove(*address); err != nil {
errLogger.Fatal("Cannot delete ", *address,
": ", err)
}
} else if !os.IsNotExist(err) {
errLogger.Fatal(*address, ": ", err)
}
network = "unix"
}
lsnr, err := net.Listen(network, *address)
if err != nil {
errLogger.Fatal(*address, ": ", err)
}
if network == "unix" {
if *gid != -1 {
if err = os.Chown(*address, -1, *gid); err != nil {
errLogger.Fatalf("Cannot set gid %d for %s: %v",
*gid, *address, err)
}
}
if *mode != -1 {
if err = os.Chmod(
*address, os.FileMode(*mode)); err != nil {
errLogger.Fatalf("Cannot set mode %0o for %s: "+
"%v", *mode, *address, err)
}
}
}
logger.Println("listening at:", *address)
logger.SetFlags(0)
logger.Fatal(server.Serve(lsnr))
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment