source: code/trunk/ident.go@ 392

Last change on this file since 392 was 388, checked in by contact, 5 years ago

Add missing ident.go

Forgot to check in this file.

Fixes: 65302d3c1e8e ("Add an ident server")

File size: 2.9 KB
RevLine 
[388]1package soju
2
3import (
4 "bufio"
5 "fmt"
6 "net"
7 "strconv"
8 "strings"
9 "sync"
10 "time"
11)
12
13var identdTimeout = 10 * time.Second
14
15type identKey struct {
16 remoteHost string
17 remotePort int
18 localPort int
19}
20
21func 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
37func 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.
47type Identd struct {
48 entries map[identKey]string
49 lock sync.RWMutex
50}
51
52func NewIdentd() *Identd {
53 return &Identd{entries: make(map[identKey]string)}
54}
55
56func (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
67func (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
77func (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
88func (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
134func 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}
Note: See TracBrowser for help on using the repository browser.