serverless-ircd 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/ci.yml +96 -2
- package/.github/workflows/deploy-aws.yml +129 -0
- package/.github/workflows/deploy-cf.yml +0 -2
- package/.gitmodules +3 -0
- package/AGENTS.md +5 -0
- package/CHANGELOG.md +352 -0
- package/MOTD.txt +3 -0
- package/PLAN-FIXES.md +358 -0
- package/PLAN.md +420 -0
- package/README.md +115 -81
- package/apps/aws-stack/README.md +73 -0
- package/apps/aws-stack/bin/aws.ts +49 -0
- package/apps/aws-stack/cdk.json +10 -0
- package/apps/aws-stack/package.json +41 -0
- package/apps/aws-stack/scripts/smoke-helpers.d.mts +22 -0
- package/apps/aws-stack/scripts/smoke-helpers.mjs +89 -0
- package/apps/aws-stack/scripts/smoke.mjs +142 -0
- package/apps/aws-stack/src/aws-stack.ts +263 -0
- package/apps/aws-stack/src/tables.ts +10 -0
- package/apps/aws-stack/tests/localstack.test.ts +46 -0
- package/apps/aws-stack/tests/smoke-helpers.test.ts +98 -0
- package/apps/aws-stack/tests/stack.test.ts +464 -0
- package/apps/aws-stack/tsconfig.build.json +11 -0
- package/apps/aws-stack/tsconfig.test.json +8 -0
- package/apps/aws-stack/vitest.config.ts +20 -0
- package/apps/cf-worker/package.json +1 -1
- package/apps/cf-worker/wrangler.toml +25 -0
- package/apps/local-cli/package.json +1 -1
- package/apps/local-cli/src/config-loader.ts +56 -0
- package/apps/local-cli/src/server.ts +241 -32
- package/apps/local-cli/tests/config-loader.test.ts +107 -0
- package/apps/local-cli/tests/e2e.test.ts +74 -0
- package/apps/local-cli/tests/security-e2e.test.ts +239 -0
- package/biome.json +3 -1
- package/dashboards/cloudwatch-irc.json +139 -0
- package/docs/AWS-Adapter-Architecture.md +494 -0
- package/docs/AWS-Deployment.md +1107 -0
- package/docs/Cloudflare-Deployment-Guide.md +660 -0
- package/docs/Home.md +1 -0
- package/docs/Observability.md +87 -0
- package/docs/PlanIRCv3Websocket.md +489 -0
- package/docs/PlanWebClient.md +451 -0
- package/docs/Release-Process.md +440 -0
- package/package.json +8 -2
- package/packages/aws-adapter/README.md +35 -0
- package/packages/aws-adapter/package.json +52 -0
- package/packages/aws-adapter/src/aws-runtime.ts +783 -0
- package/packages/aws-adapter/src/cdk-table-defs.ts +69 -0
- package/packages/aws-adapter/src/config-loader.ts +96 -0
- package/packages/aws-adapter/src/dynamo.ts +61 -0
- package/packages/aws-adapter/src/handlers/connect.ts +44 -0
- package/packages/aws-adapter/src/handlers/default.ts +322 -0
- package/packages/aws-adapter/src/handlers/disconnect.ts +29 -0
- package/packages/aws-adapter/src/handlers/index.ts +248 -0
- package/packages/aws-adapter/src/handlers/ping-checker.ts +217 -0
- package/packages/aws-adapter/src/handlers/state.ts +13 -0
- package/packages/aws-adapter/src/handlers/sweeper.ts +84 -0
- package/packages/aws-adapter/src/index.ts +67 -0
- package/packages/aws-adapter/src/message-store.ts +34 -0
- package/packages/aws-adapter/src/serialize.ts +283 -0
- package/packages/aws-adapter/src/tables.ts +53 -0
- package/packages/aws-adapter/tests/aws-harness.ts +408 -0
- package/packages/aws-adapter/tests/aws-integration.test.ts +17 -0
- package/packages/aws-adapter/tests/aws-runtime.test.ts +654 -0
- package/packages/aws-adapter/tests/config-loader.test.ts +158 -0
- package/packages/aws-adapter/tests/dynamo.test.ts +57 -0
- package/packages/aws-adapter/tests/global-setup.ts +301 -0
- package/packages/aws-adapter/tests/gone-exception.test.ts +271 -0
- package/packages/aws-adapter/tests/handlers.test.ts +427 -0
- package/packages/aws-adapter/tests/message-store.test.ts +55 -0
- package/packages/aws-adapter/tests/ping-checker.test.ts +571 -0
- package/packages/aws-adapter/tests/serialize.test.ts +249 -0
- package/packages/aws-adapter/tests/smoke.test.ts +48 -0
- package/packages/aws-adapter/tests/sweeper.test.ts +420 -0
- package/packages/aws-adapter/tests/tables.test.ts +74 -0
- package/packages/aws-adapter/tests/transactions.test.ts +446 -0
- package/packages/aws-adapter/tsconfig.build.json +16 -0
- package/packages/aws-adapter/tsconfig.test.json +14 -0
- package/packages/aws-adapter/vitest.config.ts +23 -0
- package/packages/cf-adapter/package.json +2 -1
- package/packages/cf-adapter/src/config-loader.ts +106 -0
- package/packages/cf-adapter/src/connection-do.ts +34 -0
- package/packages/cf-adapter/tests/cf-harness.ts +2 -2
- package/packages/cf-adapter/tests/cf-integration.test.ts +2 -2
- package/packages/cf-adapter/tests/config-loader.test.ts +149 -0
- package/packages/cf-adapter/tests/connection-do.test.ts +42 -0
- package/packages/in-memory-runtime/package.json +1 -1
- package/packages/in-memory-runtime/src/in-memory-runtime.ts +108 -3
- package/packages/in-memory-runtime/src/index.ts +1 -1
- package/packages/in-memory-runtime/tests/in-memory-runtime.test.ts +115 -0
- package/packages/irc-core/package.json +12 -2
- package/packages/irc-core/src/admission.ts +216 -0
- package/packages/irc-core/src/caps/capabilities.ts +1 -0
- package/packages/irc-core/src/cloak.ts +81 -0
- package/packages/irc-core/src/commands/cap.ts +1 -2
- package/packages/irc-core/src/commands/chathistory.ts +305 -0
- package/packages/irc-core/src/commands/index.ts +7 -0
- package/packages/irc-core/src/commands/join.ts +59 -7
- package/packages/irc-core/src/commands/privmsg.ts +24 -0
- package/packages/irc-core/src/commands/registration.ts +71 -14
- package/packages/irc-core/src/commands/sasl.ts +251 -0
- package/packages/irc-core/src/config.ts +247 -0
- package/packages/irc-core/src/flood-control.ts +175 -0
- package/packages/irc-core/src/index.ts +5 -0
- package/packages/irc-core/src/ports.ts +655 -0
- package/packages/irc-core/src/protocol/base64.ts +16 -0
- package/packages/irc-core/src/protocol/index.ts +2 -1
- package/packages/irc-core/src/protocol/numerics.ts +11 -0
- package/packages/irc-core/src/protocol/parser.ts +27 -2
- package/packages/irc-core/src/state/connection.ts +41 -0
- package/packages/irc-core/src/types.ts +66 -2
- package/packages/irc-core/stryker.commands.conf.json +41 -0
- package/packages/irc-core/stryker.protocol.conf.json +26 -0
- package/packages/irc-core/tests/admission.test.ts +229 -0
- package/packages/irc-core/tests/caps/capabilities.test.ts +22 -0
- package/packages/irc-core/tests/cloak.test.ts +78 -0
- package/packages/irc-core/tests/commands/chathistory.test.ts +996 -0
- package/packages/irc-core/tests/commands/join.test.ts +246 -2
- package/packages/irc-core/tests/commands/privmsg.test.ts +146 -2
- package/packages/irc-core/tests/commands/registration.test.ts +277 -9
- package/packages/irc-core/tests/commands/sasl.test.ts +536 -0
- package/packages/irc-core/tests/config.test.ts +646 -0
- package/packages/irc-core/tests/flood-control.test.ts +394 -0
- package/packages/irc-core/tests/message-store.test.ts +530 -0
- package/packages/irc-core/tests/parser.test.ts +44 -1
- package/packages/irc-core/tests/ports.test.ts +263 -0
- package/packages/irc-server/package.json +1 -1
- package/packages/irc-server/src/actor.ts +162 -44
- package/packages/irc-server/src/dispatch.ts +89 -5
- package/packages/irc-server/src/index.ts +2 -0
- package/packages/irc-server/src/routing.ts +151 -0
- package/packages/irc-server/tests/actor.test.ts +470 -14
- package/packages/irc-server/tests/dispatch.test.ts +84 -0
- package/packages/irc-server/tests/routing.test.ts +201 -0
- package/packages/irc-test-support/README.md +32 -0
- package/packages/irc-test-support/package.json +37 -0
- package/packages/{cf-adapter/tests/integration → irc-test-support/src}/in-memory-harness.ts +1 -1
- package/packages/irc-test-support/src/index.ts +24 -0
- package/packages/{cf-adapter/tests/integration/harness.test.ts → irc-test-support/tests/in-memory-harness.test.ts} +1 -1
- package/packages/{cf-adapter/tests/integration/scenarios.test.ts → irc-test-support/tests/in-memory-scenarios.test.ts} +1 -2
- package/packages/irc-test-support/tests/smoke.test.ts +11 -0
- package/packages/irc-test-support/tsconfig.build.json +16 -0
- package/packages/irc-test-support/tsconfig.test.json +14 -0
- package/packages/irc-test-support/vitest.config.ts +20 -0
- package/progress.md +107 -0
- package/tickets.md +2485 -0
- package/tools/ci-hardening/package.json +30 -0
- package/tools/ci-hardening/src/index.ts +6 -0
- package/tools/ci-hardening/src/validate.ts +103 -0
- package/tools/ci-hardening/tests/__no_thresholds__.txt +11 -0
- package/tools/ci-hardening/tests/__partial_thresholds__.txt +16 -0
- package/tools/ci-hardening/tests/__stryker_bad__.json +4 -0
- package/tools/ci-hardening/tests/validate.test.ts +177 -0
- package/tools/ci-hardening/tsconfig.build.json +12 -0
- package/tools/ci-hardening/tsconfig.test.json +10 -0
- package/tools/ci-hardening/vitest.config.ts +21 -0
- package/tools/tcp-ws-forwarder/package.json +1 -1
- package/tools/tcp-ws-forwarder/src/forwarder.ts +34 -23
- package/tools/tcp-ws-forwarder/src/logger.ts +155 -0
- package/tools/tcp-ws-forwarder/src/main.ts +33 -14
- package/tools/tcp-ws-forwarder/tests/forwarder.test.ts +86 -0
- package/tools/tcp-ws-forwarder/tests/logger.test.ts +136 -0
- package/webircgateway/LICENSE +201 -0
- package/webircgateway/Makefile +44 -0
- package/webircgateway/README.md +134 -0
- package/webircgateway/config.conf.example +135 -0
- package/webircgateway/go.mod +16 -0
- package/webircgateway/go.sum +89 -0
- package/webircgateway/main.go +118 -0
- package/webircgateway/pkg/dnsbl/dnsbl.go +121 -0
- package/webircgateway/pkg/identd/identd.go +86 -0
- package/webircgateway/pkg/identd/rpcclient.go +59 -0
- package/webircgateway/pkg/irc/isupport.go +56 -0
- package/webircgateway/pkg/irc/message.go +217 -0
- package/webircgateway/pkg/irc/state.go +79 -0
- package/webircgateway/pkg/proxy/proxy.go +129 -0
- package/webircgateway/pkg/proxy/server.go +237 -0
- package/webircgateway/pkg/recaptcha/recaptcha.go +59 -0
- package/webircgateway/pkg/webircgateway/client.go +741 -0
- package/webircgateway/pkg/webircgateway/client_command_handlers.go +495 -0
- package/webircgateway/pkg/webircgateway/config.go +385 -0
- package/webircgateway/pkg/webircgateway/gateway.go +278 -0
- package/webircgateway/pkg/webircgateway/gateway_utils.go +133 -0
- package/webircgateway/pkg/webircgateway/hooks.go +152 -0
- package/webircgateway/pkg/webircgateway/letsencrypt.go +41 -0
- package/webircgateway/pkg/webircgateway/messagetags.go +103 -0
- package/webircgateway/pkg/webircgateway/transport_kiwiirc.go +206 -0
- package/webircgateway/pkg/webircgateway/transport_sockjs.go +107 -0
- package/webircgateway/pkg/webircgateway/transport_tcp.go +113 -0
- package/webircgateway/pkg/webircgateway/transport_websocket.go +126 -0
- package/webircgateway/pkg/webircgateway/utils.go +147 -0
- package/webircgateway/plugins/example/plugin.go +11 -0
- package/webircgateway/plugins/stats/plugin.go +52 -0
- package/webircgateway/staticcheck.conf +1 -0
- package/webircgateway/webircgateway.svg +3 -0
- package/packages/cf-adapter/tests/integration/index.ts +0 -17
- /package/packages/{cf-adapter/tests/integration → irc-test-support/src}/harness.ts +0 -0
- /package/packages/{cf-adapter/tests/integration → irc-test-support/src}/scenarios.ts +0 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
package irc
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"strings"
|
|
5
|
+
"sync"
|
|
6
|
+
"time"
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
type State struct {
|
|
10
|
+
LocalPort int
|
|
11
|
+
RemotePort int
|
|
12
|
+
Username string
|
|
13
|
+
Nick string
|
|
14
|
+
RealName string
|
|
15
|
+
Password string
|
|
16
|
+
Account string
|
|
17
|
+
Modes map[string]string
|
|
18
|
+
|
|
19
|
+
channelsMutex sync.Mutex
|
|
20
|
+
Channels map[string]*StateChannel
|
|
21
|
+
ISupport *ISupport
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
type StateChannel struct {
|
|
25
|
+
Name string
|
|
26
|
+
Modes map[string]string
|
|
27
|
+
Joined time.Time
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
func NewState() *State {
|
|
31
|
+
return &State{
|
|
32
|
+
Channels: make(map[string]*StateChannel),
|
|
33
|
+
ISupport: &ISupport{
|
|
34
|
+
tokens: make(map[string]string),
|
|
35
|
+
},
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
func NewStateChannel(name string) *StateChannel {
|
|
40
|
+
return &StateChannel{
|
|
41
|
+
Name: name,
|
|
42
|
+
Modes: make(map[string]string),
|
|
43
|
+
Joined: time.Now(),
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
func (m *State) HasChannel(name string) (ok bool) {
|
|
48
|
+
m.channelsMutex.Lock()
|
|
49
|
+
_, ok = m.Channels[strings.ToLower(name)]
|
|
50
|
+
m.channelsMutex.Unlock()
|
|
51
|
+
return
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
func (m *State) GetChannel(name string) (channel *StateChannel) {
|
|
55
|
+
m.channelsMutex.Lock()
|
|
56
|
+
channel = m.Channels[strings.ToLower(name)]
|
|
57
|
+
m.channelsMutex.Unlock()
|
|
58
|
+
return
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
func (m *State) SetChannel(channel *StateChannel) {
|
|
62
|
+
m.channelsMutex.Lock()
|
|
63
|
+
m.Channels[strings.ToLower(channel.Name)] = channel
|
|
64
|
+
m.channelsMutex.Unlock()
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
func (m *State) RemoveChannel(name string) {
|
|
68
|
+
m.channelsMutex.Lock()
|
|
69
|
+
delete(m.Channels, strings.ToLower(name))
|
|
70
|
+
m.channelsMutex.Unlock()
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
func (m *State) ClearChannels() {
|
|
74
|
+
m.channelsMutex.Lock()
|
|
75
|
+
for i := range m.Channels {
|
|
76
|
+
delete(m.Channels, i)
|
|
77
|
+
}
|
|
78
|
+
m.channelsMutex.Unlock()
|
|
79
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
package proxy
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"encoding/json"
|
|
5
|
+
"errors"
|
|
6
|
+
"io"
|
|
7
|
+
"net"
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
type KiwiProxyState int
|
|
11
|
+
|
|
12
|
+
const KiwiProxyStateClosed KiwiProxyState = 0
|
|
13
|
+
const KiwiProxyStateConnecting KiwiProxyState = 1
|
|
14
|
+
const KiwiProxyStateHandshaking KiwiProxyState = 2
|
|
15
|
+
const KiwiProxyStateConnected KiwiProxyState = 3
|
|
16
|
+
|
|
17
|
+
type ConnError struct {
|
|
18
|
+
Msg string
|
|
19
|
+
Type string
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
func (err *ConnError) Error() string {
|
|
23
|
+
return err.Msg
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
type KiwiProxyConnection struct {
|
|
27
|
+
Username string
|
|
28
|
+
ProxyInterface string
|
|
29
|
+
DestHost string
|
|
30
|
+
DestPort int
|
|
31
|
+
DestTLS bool
|
|
32
|
+
State KiwiProxyState
|
|
33
|
+
Conn *net.Conn
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
func MakeKiwiProxyConnection() *KiwiProxyConnection {
|
|
37
|
+
return &KiwiProxyConnection{
|
|
38
|
+
State: KiwiProxyStateClosed,
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
func (c *KiwiProxyConnection) Close() error {
|
|
43
|
+
if c.State == KiwiProxyStateClosed {
|
|
44
|
+
return errors.New("Connection already closed")
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return (*c.Conn).Close()
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
func (c *KiwiProxyConnection) Dial(proxyServerAddr string) error {
|
|
51
|
+
if c.State != KiwiProxyStateClosed {
|
|
52
|
+
return errors.New("Connection in closed state")
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
c.State = KiwiProxyStateConnecting
|
|
56
|
+
|
|
57
|
+
conn, err := net.Dial("tcp", proxyServerAddr)
|
|
58
|
+
if err != nil {
|
|
59
|
+
return err
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
c.Conn = &conn
|
|
63
|
+
c.State = KiwiProxyStateHandshaking
|
|
64
|
+
|
|
65
|
+
meta, _ := json.Marshal(map[string]interface{}{
|
|
66
|
+
"username": c.Username,
|
|
67
|
+
"interface": c.ProxyInterface,
|
|
68
|
+
"host": c.DestHost,
|
|
69
|
+
"port": c.DestPort,
|
|
70
|
+
"ssl": c.DestTLS,
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
(*c.Conn).Write(append(meta, byte('\n')))
|
|
74
|
+
|
|
75
|
+
buf := make([]byte, 1024)
|
|
76
|
+
bufLen, readErr := (*c.Conn).Read(buf)
|
|
77
|
+
if readErr != nil {
|
|
78
|
+
(*c.Conn).Close()
|
|
79
|
+
c.State = KiwiProxyStateClosed
|
|
80
|
+
return readErr
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
response := string(buf)
|
|
84
|
+
if bufLen > 0 && response[0] == '1' {
|
|
85
|
+
c.State = KiwiProxyStateConnected
|
|
86
|
+
} else {
|
|
87
|
+
(*c.Conn).Close()
|
|
88
|
+
c.State = KiwiProxyStateClosed
|
|
89
|
+
|
|
90
|
+
if bufLen == 0 {
|
|
91
|
+
return errors.New("The proxy could not connect to the destination")
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
switch response[0] {
|
|
95
|
+
case '0':
|
|
96
|
+
return errors.New("The proxy could not connect to the destination")
|
|
97
|
+
case '2':
|
|
98
|
+
return &ConnError{Msg: "Connection reset", Type: "conn_reset"}
|
|
99
|
+
case '3':
|
|
100
|
+
return &ConnError{Msg: "Connection refused", Type: "conn_refused"}
|
|
101
|
+
case '4':
|
|
102
|
+
return &ConnError{Msg: "Host not found", Type: "not_found"}
|
|
103
|
+
case '5':
|
|
104
|
+
return &ConnError{Msg: "Connection timed out", Type: "conn_timeout"}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return nil
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
func (c *KiwiProxyConnection) Read(b []byte) (n int, err error) {
|
|
112
|
+
if c.State == KiwiProxyStateConnecting || c.State == KiwiProxyStateHandshaking {
|
|
113
|
+
return 0, nil
|
|
114
|
+
} else if c.State == KiwiProxyStateClosed {
|
|
115
|
+
return 0, io.EOF
|
|
116
|
+
} else {
|
|
117
|
+
return (*c.Conn).Read(b)
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
func (c *KiwiProxyConnection) Write(b []byte) (n int, err error) {
|
|
122
|
+
if c.State == KiwiProxyStateConnecting || c.State == KiwiProxyStateHandshaking {
|
|
123
|
+
return 0, nil
|
|
124
|
+
} else if c.State == KiwiProxyStateClosed {
|
|
125
|
+
return 0, io.EOF
|
|
126
|
+
} else {
|
|
127
|
+
return (*c.Conn).Write(b)
|
|
128
|
+
}
|
|
129
|
+
}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
package proxy
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"bufio"
|
|
5
|
+
"crypto/tls"
|
|
6
|
+
"encoding/json"
|
|
7
|
+
"fmt"
|
|
8
|
+
"io"
|
|
9
|
+
"log"
|
|
10
|
+
"net"
|
|
11
|
+
"strconv"
|
|
12
|
+
"sync"
|
|
13
|
+
"syscall"
|
|
14
|
+
"time"
|
|
15
|
+
|
|
16
|
+
"github.com/kiwiirc/webircgateway/pkg/identd"
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
const (
|
|
20
|
+
ResponseError = "0"
|
|
21
|
+
ResponseOK = "1"
|
|
22
|
+
ResponseReset = "2"
|
|
23
|
+
ResponseRefused = "3"
|
|
24
|
+
ResponseUnknownHost = "4"
|
|
25
|
+
ResponseTimeout = "5"
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
var identdRpc *identd.RpcClient
|
|
29
|
+
var Server net.Listener
|
|
30
|
+
|
|
31
|
+
type HandshakeMeta struct {
|
|
32
|
+
Host string `json:"host"`
|
|
33
|
+
Port int `json:"port"`
|
|
34
|
+
TLS bool `json:"ssl"`
|
|
35
|
+
Username string `json:"username"`
|
|
36
|
+
Interface string `json:"interface"`
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
func MakeClient(conn net.Conn) *Client {
|
|
40
|
+
return &Client{
|
|
41
|
+
Client: conn,
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
type Client struct {
|
|
46
|
+
Client net.Conn
|
|
47
|
+
Upstream net.Conn
|
|
48
|
+
UpstreamAddr *net.TCPAddr
|
|
49
|
+
Username string
|
|
50
|
+
BindAddr *net.TCPAddr
|
|
51
|
+
TLS bool
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
func (c *Client) Run() {
|
|
55
|
+
var err error
|
|
56
|
+
|
|
57
|
+
err = c.Handshake()
|
|
58
|
+
if err != nil {
|
|
59
|
+
log.Println(err.Error())
|
|
60
|
+
return
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
err = c.ConnectUpstream()
|
|
64
|
+
if err != nil {
|
|
65
|
+
log.Println(err.Error())
|
|
66
|
+
return
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
c.Pipe()
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
func (c *Client) Handshake() error {
|
|
73
|
+
// Read the first line - it should be JSON
|
|
74
|
+
reader := bufio.NewReader(c.Client)
|
|
75
|
+
line, readErr := reader.ReadBytes('\n')
|
|
76
|
+
if readErr != nil {
|
|
77
|
+
return readErr
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
var meta = HandshakeMeta{
|
|
81
|
+
Username: "user",
|
|
82
|
+
Port: 6667,
|
|
83
|
+
Interface: "0.0.0.0",
|
|
84
|
+
}
|
|
85
|
+
unmarshalErr := json.Unmarshal(line, &meta)
|
|
86
|
+
if unmarshalErr != nil {
|
|
87
|
+
c.Client.Write([]byte(ResponseError))
|
|
88
|
+
return unmarshalErr
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if meta.Host == "" || meta.Port == 0 || meta.Username == "" || meta.Interface == "" {
|
|
92
|
+
c.Client.Write([]byte(ResponseError))
|
|
93
|
+
return fmt.Errorf("missing args")
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
c.Username = meta.Username
|
|
97
|
+
c.TLS = meta.TLS
|
|
98
|
+
|
|
99
|
+
bindAddr, bindAddrErr := net.ResolveTCPAddr("tcp", meta.Interface+":")
|
|
100
|
+
if bindAddrErr != nil {
|
|
101
|
+
c.Client.Write([]byte(ResponseError))
|
|
102
|
+
return fmt.Errorf("interface: " + bindAddrErr.Error())
|
|
103
|
+
}
|
|
104
|
+
c.BindAddr = bindAddr
|
|
105
|
+
|
|
106
|
+
hostStr := net.JoinHostPort(meta.Host, strconv.Itoa(meta.Port))
|
|
107
|
+
addr, addrErr := net.ResolveTCPAddr("tcp", hostStr)
|
|
108
|
+
if addrErr != nil {
|
|
109
|
+
c.Client.Write([]byte(ResponseUnknownHost))
|
|
110
|
+
return fmt.Errorf("remote host: " + addrErr.Error())
|
|
111
|
+
}
|
|
112
|
+
c.UpstreamAddr = addr
|
|
113
|
+
|
|
114
|
+
return nil
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
func (c *Client) ConnectUpstream() error {
|
|
118
|
+
dialer := &net.Dialer{}
|
|
119
|
+
dialer.LocalAddr = c.BindAddr
|
|
120
|
+
dialer.Timeout = time.Second * 10
|
|
121
|
+
|
|
122
|
+
conn, err := dialer.Dial("tcp", c.UpstreamAddr.String())
|
|
123
|
+
if err != nil {
|
|
124
|
+
response := ""
|
|
125
|
+
errType := typeOfErr(err)
|
|
126
|
+
switch errType {
|
|
127
|
+
case "timeout":
|
|
128
|
+
response = ResponseTimeout
|
|
129
|
+
case "unknown_host":
|
|
130
|
+
response = ResponseUnknownHost
|
|
131
|
+
case "refused":
|
|
132
|
+
response = ResponseRefused
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
c.Client.Write([]byte(response))
|
|
136
|
+
return err
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if identdRpc != nil {
|
|
140
|
+
lAddr, lPortStr, _ := net.SplitHostPort(conn.LocalAddr().String())
|
|
141
|
+
lPort, _ := strconv.Atoi(lPortStr)
|
|
142
|
+
identdRpc.AddIdent(lPort, c.UpstreamAddr.Port, c.Username, lAddr)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if c.TLS {
|
|
146
|
+
tlsConfig := &tls.Config{InsecureSkipVerify: true}
|
|
147
|
+
tlsConn := tls.Client(conn, tlsConfig)
|
|
148
|
+
err := tlsConn.Handshake()
|
|
149
|
+
if err != nil {
|
|
150
|
+
conn.Close()
|
|
151
|
+
c.Client.Write([]byte(ResponseReset))
|
|
152
|
+
return err
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
conn = net.Conn(tlsConn)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
c.Upstream = conn
|
|
159
|
+
c.Client.Write([]byte(ResponseOK))
|
|
160
|
+
return nil
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
func (c *Client) Pipe() {
|
|
164
|
+
wg := sync.WaitGroup{}
|
|
165
|
+
wg.Add(2)
|
|
166
|
+
|
|
167
|
+
go func() {
|
|
168
|
+
io.Copy(c.Client, c.Upstream)
|
|
169
|
+
c.Client.Close()
|
|
170
|
+
wg.Done()
|
|
171
|
+
}()
|
|
172
|
+
|
|
173
|
+
go func() {
|
|
174
|
+
io.Copy(c.Upstream, c.Client)
|
|
175
|
+
c.Upstream.Close()
|
|
176
|
+
wg.Done()
|
|
177
|
+
}()
|
|
178
|
+
|
|
179
|
+
wg.Wait()
|
|
180
|
+
|
|
181
|
+
if identdRpc != nil {
|
|
182
|
+
lAddr, lPortStr, _ := net.SplitHostPort(c.Upstream.LocalAddr().String())
|
|
183
|
+
lPort, _ := strconv.Atoi(lPortStr)
|
|
184
|
+
identdRpc.RemoveIdent(lPort, c.UpstreamAddr.Port, c.Username, lAddr)
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
func Start(laddr string) {
|
|
189
|
+
srv, err := net.Listen("tcp", laddr)
|
|
190
|
+
if err != nil {
|
|
191
|
+
log.Fatal(err.Error())
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Expose the server
|
|
195
|
+
Server = srv
|
|
196
|
+
log.Printf("Kiwi proxy listening on %s", srv.Addr().String())
|
|
197
|
+
|
|
198
|
+
identdRpc = identd.MakeRpcClient("kiwiproxy" + laddr)
|
|
199
|
+
go identdRpc.ConnectAndReconnect("127.0.0.1:1133")
|
|
200
|
+
|
|
201
|
+
for {
|
|
202
|
+
conn, err := srv.Accept()
|
|
203
|
+
if err != nil {
|
|
204
|
+
log.Print(err.Error())
|
|
205
|
+
break
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
c := MakeClient(conn)
|
|
209
|
+
go c.Run()
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
func typeOfErr(err error) string {
|
|
214
|
+
if err == nil {
|
|
215
|
+
return ""
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if netError, ok := err.(net.Error); ok && netError.Timeout() {
|
|
219
|
+
return "timeout"
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
switch t := err.(type) {
|
|
223
|
+
case *net.OpError:
|
|
224
|
+
if t.Op == "dial" {
|
|
225
|
+
return "unknown_host"
|
|
226
|
+
} else if t.Op == "read" {
|
|
227
|
+
return "refused"
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
case syscall.Errno:
|
|
231
|
+
if t == syscall.ECONNREFUSED {
|
|
232
|
+
return "refused"
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return ""
|
|
237
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// Google re-captcha package tweaked from http://github.com/haisum/recaptcha
|
|
2
|
+
|
|
3
|
+
package recaptcha
|
|
4
|
+
|
|
5
|
+
import (
|
|
6
|
+
"encoding/json"
|
|
7
|
+
"io/ioutil"
|
|
8
|
+
"net/http"
|
|
9
|
+
"net/url"
|
|
10
|
+
"time"
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
// R type represents an object of Recaptcha and has public property Secret,
|
|
14
|
+
// which is secret obtained from google recaptcha tool admin interface
|
|
15
|
+
type R struct {
|
|
16
|
+
URL string
|
|
17
|
+
Secret string
|
|
18
|
+
lastError []string
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Struct for parsing json in google's response
|
|
22
|
+
type googleResponse struct {
|
|
23
|
+
Success bool
|
|
24
|
+
ErrorCodes []string `json:"error-codes"`
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// VerifyResponse is a method similar to `Verify`; but doesn't parse the form for you. Useful if
|
|
28
|
+
// you're receiving the data as a JSON object from a javascript app or similar.
|
|
29
|
+
func (r *R) VerifyResponse(response string) bool {
|
|
30
|
+
r.lastError = make([]string, 1)
|
|
31
|
+
client := &http.Client{Timeout: 20 * time.Second}
|
|
32
|
+
resp, err := client.PostForm(r.URL,
|
|
33
|
+
url.Values{"secret": {r.Secret}, "response": {response}})
|
|
34
|
+
if err != nil {
|
|
35
|
+
r.lastError = append(r.lastError, err.Error())
|
|
36
|
+
return false
|
|
37
|
+
}
|
|
38
|
+
defer resp.Body.Close()
|
|
39
|
+
body, err := ioutil.ReadAll(resp.Body)
|
|
40
|
+
if err != nil {
|
|
41
|
+
r.lastError = append(r.lastError, err.Error())
|
|
42
|
+
return false
|
|
43
|
+
}
|
|
44
|
+
gr := new(googleResponse)
|
|
45
|
+
err = json.Unmarshal(body, gr)
|
|
46
|
+
if err != nil {
|
|
47
|
+
r.lastError = append(r.lastError, err.Error())
|
|
48
|
+
return false
|
|
49
|
+
}
|
|
50
|
+
if !gr.Success {
|
|
51
|
+
r.lastError = append(r.lastError, gr.ErrorCodes...)
|
|
52
|
+
}
|
|
53
|
+
return gr.Success
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// LastError returns errors occurred in last re-captcha validation attempt
|
|
57
|
+
func (r R) LastError() []string {
|
|
58
|
+
return r.lastError
|
|
59
|
+
}
|