Compare commits
3 Commits
v0.9.0
...
6845060d00
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6845060d00 | ||
|
|
762cb11389 | ||
|
|
a7e6b5420e |
@@ -25,7 +25,7 @@ jobs:
|
|||||||
go-version: "1.22.x"
|
go-version: "1.22.x"
|
||||||
cache: true
|
cache: true
|
||||||
|
|
||||||
- name: Build (linux/musl)
|
- name: Build
|
||||||
env:
|
env:
|
||||||
GOOS: ${{ matrix.goos }}
|
GOOS: ${{ matrix.goos }}
|
||||||
GOARCH: ${{ matrix.arch }}
|
GOARCH: ${{ matrix.arch }}
|
||||||
@@ -40,14 +40,17 @@ jobs:
|
|||||||
-ldflags="-s -w -X main.version=${VERSION}" \
|
-ldflags="-s -w -X main.version=${VERSION}" \
|
||||||
-o "dist/${BIN}" ./cmd/kc-policy
|
-o "dist/${BIN}" ./cmd/kc-policy
|
||||||
tar -C dist -czf "dist/${BIN}.tar.gz" "${BIN}"
|
tar -C dist -czf "dist/${BIN}.tar.gz" "${BIN}"
|
||||||
|
cp aliasctl dist/
|
||||||
sha256sum "dist/${BIN}.tar.gz" >> dist/checksums.txt
|
sha256sum "dist/${BIN}.tar.gz" >> dist/checksums.txt
|
||||||
|
sha256sum "dist/aliasctl" >> dist/checksums.txt
|
||||||
|
|
||||||
- name: Upload assets to Gitea release
|
- name: Upload assets to Gitea release
|
||||||
uses: https://gitea.com/actions/gitea-release-action@v1
|
uses: https://gitea.com/actions/gitea-release-action@v1
|
||||||
with:
|
with:
|
||||||
files: |
|
files: |
|
||||||
dist/*.tar.gz
|
dist/*.tar.gz
|
||||||
dist/*.sha256
|
dist/aliasctl
|
||||||
|
dist/checksums.txt
|
||||||
generate_release_notes: true
|
generate_release_notes: true
|
||||||
env:
|
env:
|
||||||
GITEA_TOKEN: ${{ github.token }}
|
GITEA_TOKEN: ${{ github.token }}
|
||||||
|
|||||||
@@ -4,8 +4,10 @@ import (
|
|||||||
"bufio"
|
"bufio"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -37,49 +39,104 @@ func RunSocketmap(ctx context.Context, cfg *Config, db *AliasDB) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Socketmap protocol: "mapname key\n" -> "OK value\n" or "NOTFOUND\n" or "TEMP\n"
|
// Postfix socketmap framing: "<len>:<payload>,"
|
||||||
func handleSocketmapConn(conn net.Conn, cfg *Config, db *AliasDB) {
|
func handleSocketmapConn(conn net.Conn, cfg *Config, db *AliasDB) {
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
r := bufio.NewReader(conn)
|
r := bufio.NewReader(conn)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
line, err := r.ReadString('\n')
|
payload, err := readSocketmapFrame(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// normal close
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
line = strings.TrimSpace(line)
|
|
||||||
if line == "" {
|
payload = strings.TrimSpace(payload)
|
||||||
|
if payload == "" {
|
||||||
|
_ = writeSocketmapFrame(conn, "NOTFOUND")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
parts := strings.SplitN(line, " ", 2)
|
|
||||||
|
parts := strings.SplitN(payload, " ", 2)
|
||||||
if len(parts) != 2 {
|
if len(parts) != 2 {
|
||||||
fmt.Fprint(conn, "TEMP\n")
|
_ = writeSocketmapFrame(conn, "TEMP")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
mapName := parts[0]
|
mapName := parts[0]
|
||||||
key := strings.ToLower(strings.TrimSpace(parts[1]))
|
key := strings.ToLower(strings.TrimSpace(parts[1]))
|
||||||
|
|
||||||
if mapName != "alias" {
|
if mapName != "alias" {
|
||||||
fmt.Fprint(conn, "NOTFOUND\n")
|
_ = writeSocketmapFrame(conn, "NOTFOUND")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only handle our domain
|
// Only handle our domain
|
||||||
if !strings.HasSuffix(key, "@"+strings.ToLower(cfg.Policy.Domain)) {
|
domain := strings.ToLower(cfg.Policy.Domain)
|
||||||
fmt.Fprint(conn, "NOTFOUND\n")
|
if !strings.HasSuffix(key, "@"+domain) {
|
||||||
|
_ = writeSocketmapFrame(conn, "NOTFOUND")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
username, ok, err := db.AliasOwner(key)
|
username, ok, err := db.AliasOwner(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprint(conn, "TEMP\n")
|
_ = writeSocketmapFrame(conn, "TEMP")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if !ok {
|
if !ok {
|
||||||
fmt.Fprint(conn, "NOTFOUND\n")
|
_ = writeSocketmapFrame(conn, "NOTFOUND")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// rewrite alias -> primary rcpt (username@domain)
|
|
||||||
fmt.Fprintf(conn, "OK %s@%s\n", username, strings.ToLower(cfg.Policy.Domain))
|
// rewrite alias -> username@domain
|
||||||
|
_ = writeSocketmapFrame(conn, fmt.Sprintf("OK %s@%s", username, domain))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func readSocketmapFrame(r *bufio.Reader) (string, error) {
|
||||||
|
// read decimal length until ':'
|
||||||
|
var lenBuf strings.Builder
|
||||||
|
for {
|
||||||
|
b, err := r.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if b == ':' {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if b < '0' || b > '9' {
|
||||||
|
return "", io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
lenBuf.WriteByte(b)
|
||||||
|
if lenBuf.Len() > 10 {
|
||||||
|
return "", io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
n, err := strconv.Atoi(lenBuf.String())
|
||||||
|
if err != nil || n < 0 || n > 1024*1024 {
|
||||||
|
return "", io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := make([]byte, n)
|
||||||
|
if _, err := io.ReadFull(r, buf); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// expect trailing comma
|
||||||
|
b, err := r.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if b != ',' {
|
||||||
|
return "", io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(buf), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeSocketmapFrame(w io.Writer, payload string) error {
|
||||||
|
// "<len>:<payload>,"
|
||||||
|
_, err := fmt.Fprintf(w, "%d:%s,", len(payload), payload)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user