-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
137 lines (119 loc) · 2.92 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
package main
import (
"bufio"
"errors"
"fmt"
"io"
"log"
"net"
"os"
"path"
"path/filepath"
"strings"
flag "github.com/spf13/pflag"
)
var version = "invalid"
var (
printVersion bool
root string
hostname string
port string
)
var errCantResolve = errors.New("can't resolve selector")
func init() {
flag.BoolVarP(&printVersion, "version", "V", false, "Print version and exit")
flag.StringVarP(&root, "root", "r", "/srv/gopher", "Root directory of server")
flag.StringVarP(&hostname, "hostname", "h", "localhost", "Hostname to present")
flag.StringVarP(&port, "port", "p", "70", "Port to bind to")
flag.Usage = func() {
name := path.Base(os.Args[0])
fmt.Fprintf(os.Stderr, "%s - a server for the Gopher protocol.\n\n", name)
fmt.Fprintf(os.Stderr, ":\n\n")
flag.PrintDefaults()
}
}
func main() {
flag.Parse()
if printVersion {
fmt.Println(version)
return
}
if _, err := os.Stat(root); os.IsNotExist(err) {
log.Fatalf("Root directory '%v' not found", root)
}
ln, err := net.Listen("tcp", net.JoinHostPort(hostname, port))
if err != nil {
log.Fatal(err)
}
defer ln.Close()
for {
if conn, err := ln.Accept(); err != nil {
log.Print(err)
} else {
go handleConnection(conn)
}
}
}
func handleConnection(conn net.Conn) {
defer conn.Close()
scanner := bufio.NewScanner(conn)
if scanner.Scan() {
// Format is <selector>TAB<query>CRLF
parts := strings.SplitN(strings.TrimRight(scanner.Text(), "\r\n"), "\t", 2)
// Make sure the selector is safe
parts[0] = path.Clean(parts[0])
if parts[0] == "." {
parts[0] = ""
}
if strings.HasPrefix(parts[0], "..") {
if err := writeError(conn, "Bad selector"); err != nil {
log.Print(err)
}
return
}
localPath := filepath.Join(root, parts[0])
if err := resolve(conn, localPath, parts[0]); err != nil {
log.Print(err)
if err := writeError(conn, err.Error()); err != nil {
log.Print(err)
}
}
} else if err := scanner.Err(); err != nil {
log.Print(err)
}
}
func resolve(out io.Writer, localPath, selector string) error {
if fi, err := os.Stat(localPath); err != nil {
return fmt.Errorf("%q: %w", selector, errCantResolve)
} else if fi.IsDir() {
gophermap := filepath.Join(localPath, "gophermap")
if _, err := os.Stat(gophermap); err == nil {
if err := loadGopherMap(out, localPath, selector); err != nil {
return err //nolint:wrapcheck
}
if _, err := out.Write([]byte(".\r\n")); err != nil {
return err //nolint:wrapcheck
}
}
catalogue, err := listDirectory(localPath, selector)
if err != nil {
return err
}
if _, err := write(out, catalogue); err != nil {
return err
}
return nil
}
return sendFile(out, localPath)
}
func sendFile(out io.Writer, localPath string) error {
f, err := os.Open(localPath)
if err != nil {
return err //nolint:wrapcheck
}
defer f.Close()
if _, err := io.Copy(out, f); err != nil {
return err //nolint:wrapcheck
}
return nil
}