aboutsummaryrefslogtreecommitdiff
path: root/cmd/receivepack9418
diff options
context:
space:
mode:
Diffstat (limited to 'cmd/receivepack9418')
-rw-r--r--cmd/receivepack9418/conn.go65
-rw-r--r--cmd/receivepack9418/errpkt.go18
-rw-r--r--cmd/receivepack9418/gitproto.go23
-rw-r--r--cmd/receivepack9418/main.go23
-rw-r--r--cmd/receivepack9418/request.go60
-rw-r--r--cmd/receivepack9418/run.go64
-rw-r--r--cmd/receivepack9418/server.go12
7 files changed, 265 insertions, 0 deletions
diff --git a/cmd/receivepack9418/conn.go b/cmd/receivepack9418/conn.go
new file mode 100644
index 00000000..31062193
--- /dev/null
+++ b/cmd/receivepack9418/conn.go
@@ -0,0 +1,65 @@
+package main
+
+import (
+ "bufio"
+ "context"
+ "fmt"
+ "log"
+ "net"
+ "strings"
+
+ "codeberg.org/lindenii/furgit/receivepack"
+)
+
+func (srv *server) handleConn(conn net.Conn) {
+ defer func() { _ = conn.Close() }()
+
+ reader := bufio.NewReader(conn)
+ writer := bufio.NewWriter(conn)
+
+ req, err := readGitProtoRequest(reader)
+ if err != nil {
+ writeErrPkt(writer, fmt.Sprintf("invalid initial request: %v", err))
+ _ = writer.Flush()
+ log.Printf("receivepack9418: %s: invalid initial request: %v", conn.RemoteAddr(), err)
+ return
+ }
+
+ if req.Command != "git-receive-pack" {
+ writeErrPkt(writer, fmt.Sprintf("unsupported command %q", req.Command))
+ _ = writer.Flush()
+ log.Printf("receivepack9418: %s: unsupported command %q", conn.RemoteAddr(), req.Command)
+ return
+ }
+
+ gitProtocol := strings.Join(req.ExtraParameters, ":")
+
+ opts := receivepack.Options{
+ GitProtocol: gitProtocol,
+ Algorithm: srv.repo.Algorithm(),
+ Refs: srv.repo.Refs(),
+ ExistingObjects: srv.repo.Objects(),
+ ObjectsRoot: srv.objectsRoot,
+ }
+
+ err = receivepack.ReceivePack(context.Background(), writer, reader, opts)
+ if err != nil {
+ _ = writer.Flush()
+ log.Printf(
+ "receivepack9418: %s: receive-pack failed (path=%q host=%q extras=%v): %v",
+ conn.RemoteAddr(),
+ req.Pathname,
+ req.Host,
+ req.ExtraParameters,
+ err,
+ )
+
+ return
+ }
+
+ err = writer.Flush()
+ if err != nil {
+ log.Printf("receivepack9418: %s: flush failed: %v", conn.RemoteAddr(), err)
+ return
+ }
+}
diff --git a/cmd/receivepack9418/errpkt.go b/cmd/receivepack9418/errpkt.go
new file mode 100644
index 00000000..379c3cb2
--- /dev/null
+++ b/cmd/receivepack9418/errpkt.go
@@ -0,0 +1,18 @@
+package main
+
+import (
+ "io"
+
+ "codeberg.org/lindenii/furgit/format/pktline"
+)
+
+func writeErrPkt(w io.Writer, message string) {
+ payload := []byte("ERR " + message + "\n")
+
+ frame, err := pktline.AppendData(nil, payload)
+ if err != nil {
+ return
+ }
+
+ _, _ = w.Write(frame)
+}
diff --git a/cmd/receivepack9418/gitproto.go b/cmd/receivepack9418/gitproto.go
new file mode 100644
index 00000000..609f2c12
--- /dev/null
+++ b/cmd/receivepack9418/gitproto.go
@@ -0,0 +1,23 @@
+package main
+
+import (
+ "fmt"
+ "io"
+
+ "codeberg.org/lindenii/furgit/format/pktline"
+)
+
+func readGitProtoRequest(r io.Reader) (gitProtoRequest, error) {
+ dec := pktline.NewDecoder(r, pktline.ReadOptions{})
+
+ frame, err := dec.ReadFrame()
+ if err != nil {
+ return gitProtoRequest{}, err
+ }
+
+ if frame.Type != pktline.PacketData {
+ return gitProtoRequest{}, fmt.Errorf("expected initial pkt-line data, got %v", frame.Type)
+ }
+
+ return parseGitProtoRequestPayload(frame.Payload)
+}
diff --git a/cmd/receivepack9418/main.go b/cmd/receivepack9418/main.go
new file mode 100644
index 00000000..377bf7d4
--- /dev/null
+++ b/cmd/receivepack9418/main.go
@@ -0,0 +1,23 @@
+// Command receivepack9418 serves one fixed repository over git:// receive-pack on TCP 9418.
+package main
+
+import (
+ "flag"
+ "log"
+)
+
+func main() {
+ listenAddr := flag.String("listen", ":9418", "listen address")
+ repoPath := flag.String("repo", "", "path to git dir (.git or bare repo root)")
+
+ flag.Parse()
+
+ if *repoPath == "" {
+ log.Fatal("must provide -repo <path-to-git-dir>")
+ }
+
+ err := run(*listenAddr, *repoPath)
+ if err != nil {
+ log.Fatalf("run: %v", err)
+ }
+}
diff --git a/cmd/receivepack9418/request.go b/cmd/receivepack9418/request.go
new file mode 100644
index 00000000..5e392926
--- /dev/null
+++ b/cmd/receivepack9418/request.go
@@ -0,0 +1,60 @@
+package main
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "strings"
+)
+
+type gitProtoRequest struct {
+ Command string
+ Pathname string
+ Host string
+ ExtraParameters []string
+}
+
+func parseGitProtoRequestPayload(payload []byte) (gitProtoRequest, error) {
+ parts := bytes.Split(payload, []byte{0})
+ if len(parts) == 0 || len(parts[0]) == 0 {
+ return gitProtoRequest{}, errors.New("missing command/path segment")
+ }
+
+ commandPath := string(parts[0])
+ command, pathname, ok := strings.Cut(commandPath, " ")
+ if !ok || command == "" || pathname == "" {
+ return gitProtoRequest{}, fmt.Errorf("malformed command/path segment %q", commandPath)
+ }
+
+ req := gitProtoRequest{
+ Command: command,
+ Pathname: pathname,
+ }
+
+ i := 1
+ if i < len(parts) && strings.HasPrefix(string(parts[i]), "host=") {
+ req.Host = strings.TrimPrefix(string(parts[i]), "host=")
+ i++
+ }
+
+ // No tail left.
+ if i >= len(parts) {
+ return req, nil
+ }
+
+ // If there is tail, grammar requires one empty field before extras.
+ if len(parts[i]) != 0 {
+ return gitProtoRequest{}, fmt.Errorf("unexpected token %q after host/path", string(parts[i]))
+ }
+
+ i++
+ for ; i < len(parts); i++ {
+ if len(parts[i]) == 0 {
+ continue
+ }
+
+ req.ExtraParameters = append(req.ExtraParameters, string(parts[i]))
+ }
+
+ return req, nil
+}
diff --git a/cmd/receivepack9418/run.go b/cmd/receivepack9418/run.go
new file mode 100644
index 00000000..3c806e80
--- /dev/null
+++ b/cmd/receivepack9418/run.go
@@ -0,0 +1,64 @@
+package main
+
+import (
+ "errors"
+ "fmt"
+ "log"
+ "net"
+ "os"
+
+ "codeberg.org/lindenii/furgit/repository"
+)
+
+func run(listenAddr, repoPath string) error {
+ repoRoot, err := os.OpenRoot(repoPath)
+ if err != nil {
+ return fmt.Errorf("open repo root: %w", err)
+ }
+ defer func() { _ = repoRoot.Close() }()
+
+ repo, err := repository.Open(repoRoot)
+ if err != nil {
+ return fmt.Errorf("open repository: %w", err)
+ }
+ defer func() { _ = repo.Close() }()
+
+ objectsRoot, err := repoRoot.OpenRoot("objects")
+ if err != nil {
+ return fmt.Errorf("open objects root: %w", err)
+ }
+ defer func() { _ = objectsRoot.Close() }()
+
+ srv := &server{
+ repo: repo,
+ objectsRoot: objectsRoot,
+ }
+
+ ln, err := net.Listen("tcp", listenAddr)
+ if err != nil {
+ return fmt.Errorf("listen %q: %w", listenAddr, err)
+ }
+ defer func() { _ = ln.Close() }()
+
+ log.Printf("receivepack9418: listening on %s", listenAddr)
+ log.Printf("receivepack9418: repository=%s algorithm=%s", repoPath, repo.Algorithm())
+
+ for {
+ conn, err := ln.Accept()
+ if err != nil {
+ if errors.Is(err, net.ErrClosed) {
+ return nil
+ }
+
+ var nerr net.Error
+ if errors.As(err, &nerr) && nerr.Temporary() {
+ log.Printf("receivepack9418: temporary accept error: %v", err)
+ continue
+ }
+
+ return fmt.Errorf("accept: %w", err)
+ }
+
+ go srv.handleConn(conn)
+ }
+}
diff --git a/cmd/receivepack9418/server.go b/cmd/receivepack9418/server.go
new file mode 100644
index 00000000..74793712
--- /dev/null
+++ b/cmd/receivepack9418/server.go
@@ -0,0 +1,12 @@
+package main
+
+import (
+ "os"
+
+ "codeberg.org/lindenii/furgit/repository"
+)
+
+type server struct {
+ repo *repository.Repository
+ objectsRoot *os.Root
+}