1 | package soju
|
---|
2 |
|
---|
3 | import (
|
---|
4 | "bufio"
|
---|
5 | "fmt"
|
---|
6 | "net"
|
---|
7 | "strconv"
|
---|
8 | "strings"
|
---|
9 | "sync"
|
---|
10 | "time"
|
---|
11 | )
|
---|
12 |
|
---|
13 | var identdTimeout = 10 * time.Second
|
---|
14 |
|
---|
15 | type identKey struct {
|
---|
16 | remoteHost string
|
---|
17 | remotePort int
|
---|
18 | localPort int
|
---|
19 | }
|
---|
20 |
|
---|
21 | func newIdentKey(remoteAddr, localAddr string) (*identKey, error) {
|
---|
22 | remoteHost, remotePort, err := splitHostPort(remoteAddr)
|
---|
23 | if err != nil {
|
---|
24 | return nil, err
|
---|
25 | }
|
---|
26 | _, localPort, err := splitHostPort(localAddr)
|
---|
27 | if err != nil {
|
---|
28 | return nil, err
|
---|
29 | }
|
---|
30 | return &identKey{
|
---|
31 | remoteHost: remoteHost,
|
---|
32 | remotePort: remotePort,
|
---|
33 | localPort: localPort,
|
---|
34 | }, nil
|
---|
35 | }
|
---|
36 |
|
---|
37 | func splitHostPort(addr string) (host string, port int, err error) {
|
---|
38 | host, portStr, err := net.SplitHostPort(addr)
|
---|
39 | if err != nil {
|
---|
40 | return "", 0, err
|
---|
41 | }
|
---|
42 | port, err = strconv.Atoi(portStr)
|
---|
43 | return host, port, err
|
---|
44 | }
|
---|
45 |
|
---|
46 | // Identd implements an ident server, as described in RFC 1413.
|
---|
47 | type Identd struct {
|
---|
48 | entries map[identKey]string
|
---|
49 | lock sync.RWMutex
|
---|
50 | }
|
---|
51 |
|
---|
52 | func NewIdentd() *Identd {
|
---|
53 | return &Identd{entries: make(map[identKey]string)}
|
---|
54 | }
|
---|
55 |
|
---|
56 | func (s *Identd) Store(remoteAddr, localAddr, ident string) {
|
---|
57 | k, err := newIdentKey(remoteAddr, localAddr)
|
---|
58 | if err != nil {
|
---|
59 | return
|
---|
60 | }
|
---|
61 | fmt.Println(remoteAddr, localAddr, ident)
|
---|
62 | s.lock.Lock()
|
---|
63 | s.entries[*k] = ident
|
---|
64 | s.lock.Unlock()
|
---|
65 | }
|
---|
66 |
|
---|
67 | func (s *Identd) Delete(remoteAddr, localAddr string) {
|
---|
68 | k, err := newIdentKey(remoteAddr, localAddr)
|
---|
69 | if err != nil {
|
---|
70 | return
|
---|
71 | }
|
---|
72 | s.lock.Lock()
|
---|
73 | delete(s.entries, *k)
|
---|
74 | s.lock.Unlock()
|
---|
75 | }
|
---|
76 |
|
---|
77 | func (s *Identd) Serve(ln net.Listener) error {
|
---|
78 | for {
|
---|
79 | conn, err := ln.Accept()
|
---|
80 | if err != nil {
|
---|
81 | return fmt.Errorf("failed to accept connection: %v", err)
|
---|
82 | }
|
---|
83 |
|
---|
84 | go s.handle(conn)
|
---|
85 | }
|
---|
86 | }
|
---|
87 |
|
---|
88 | func (s *Identd) handle(c net.Conn) {
|
---|
89 | defer c.Close()
|
---|
90 |
|
---|
91 | remoteHost, _, err := net.SplitHostPort(c.RemoteAddr().String())
|
---|
92 | if err != nil {
|
---|
93 | return
|
---|
94 | }
|
---|
95 |
|
---|
96 | scanner := bufio.NewScanner(c)
|
---|
97 |
|
---|
98 | // We only read to read lines with two port numbers
|
---|
99 | var buf [512]byte
|
---|
100 | scanner.Buffer(buf[:], len(buf))
|
---|
101 |
|
---|
102 | for {
|
---|
103 | c.SetDeadline(time.Now().Add(identdTimeout))
|
---|
104 | if !scanner.Scan() {
|
---|
105 | break
|
---|
106 | }
|
---|
107 | l := scanner.Text()
|
---|
108 |
|
---|
109 | localPort, remotePort, err := parseIdentQuery(l)
|
---|
110 | if err != nil {
|
---|
111 | fmt.Fprintf(c, "%s : ERROR : INVALID-PORT\r\n", l)
|
---|
112 | break
|
---|
113 | }
|
---|
114 |
|
---|
115 | k := identKey{
|
---|
116 | remoteHost: remoteHost,
|
---|
117 | remotePort: remotePort,
|
---|
118 | localPort: localPort,
|
---|
119 | }
|
---|
120 |
|
---|
121 | s.lock.RLock()
|
---|
122 | ident := s.entries[k]
|
---|
123 | s.lock.RUnlock()
|
---|
124 |
|
---|
125 | if ident == "" {
|
---|
126 | fmt.Fprintf(c, "%s : ERROR : NO-USER\r\n", l)
|
---|
127 | break
|
---|
128 | }
|
---|
129 |
|
---|
130 | fmt.Fprintf(c, "%s : USERID : OTHER : %s\r\n", l, ident)
|
---|
131 | }
|
---|
132 | }
|
---|
133 |
|
---|
134 | func parseIdentQuery(l string) (localPort, remotePort int, err error) {
|
---|
135 | parts := strings.SplitN(l, ",", 2)
|
---|
136 | if len(parts) != 2 {
|
---|
137 | return 0, 0, fmt.Errorf("expected two ports")
|
---|
138 | }
|
---|
139 | localStr, remoteStr := strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1])
|
---|
140 | if localPort, err = strconv.Atoi(localStr); err != nil {
|
---|
141 | return 0, 0, err
|
---|
142 | }
|
---|
143 | if remotePort, err = strconv.Atoi(remoteStr); err != nil {
|
---|
144 | return 0, 0, err
|
---|
145 | }
|
---|
146 | if localPort <= 0 || remotePort <= 0 {
|
---|
147 | return 0, 0, fmt.Errorf("invalid port")
|
---|
148 | }
|
---|
149 | return localPort, remotePort, nil
|
---|
150 | }
|
---|