serverless-ircd 0.2.0 → 0.4.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 +2 -2
- package/.github/workflows/deploy-aws.yml +1 -3
- package/.github/workflows/deploy-cf-tcp.yml +87 -0
- package/.github/workflows/deploy-cf.yml +1 -1
- package/.node-version +1 -0
- package/.nvmrc +1 -0
- package/CHANGELOG.md +258 -18
- package/README.md +72 -30
- package/apps/aws-stack/README.md +2 -2
- package/apps/aws-stack/bin/aws.ts +7 -0
- package/apps/aws-stack/package.json +4 -4
- package/apps/aws-stack/src/aws-stack.ts +118 -6
- package/apps/aws-stack/tests/stack.test.ts +98 -3
- package/apps/cf-tcp-container/Dockerfile +69 -0
- package/apps/cf-tcp-container/package.json +34 -0
- package/apps/cf-tcp-container/src/config-loader.ts +145 -0
- package/apps/cf-tcp-container/src/container-do.ts +38 -0
- package/apps/cf-tcp-container/src/container-server.ts +363 -0
- package/apps/cf-tcp-container/src/main.ts +77 -0
- package/apps/cf-tcp-container/src/persistence.ts +144 -0
- package/apps/cf-tcp-container/src/worker.ts +41 -0
- package/apps/cf-tcp-container/terraform/provider.tf +24 -0
- package/apps/cf-tcp-container/terraform/spectrum.tf +81 -0
- package/apps/cf-tcp-container/tests/config-loader.test.ts +217 -0
- package/apps/cf-tcp-container/tests/container-server.test.ts +465 -0
- package/apps/cf-tcp-container/tests/persistence.test.ts +227 -0
- package/apps/cf-tcp-container/tests/tls-e2e.test.ts +275 -0
- package/apps/cf-tcp-container/tsconfig.build.json +17 -0
- package/apps/cf-tcp-container/tsconfig.test.json +15 -0
- package/apps/cf-tcp-container/vitest.config.ts +26 -0
- package/apps/cf-tcp-container/wrangler.toml +63 -0
- package/apps/cf-worker/package.json +1 -1
- package/apps/cf-worker/scripts/smoke.mjs +0 -0
- package/apps/cf-worker/src/worker.ts +8 -2
- package/apps/cf-worker/tests/smoke.test.ts +1 -0
- package/apps/cf-worker/wrangler.test.toml +11 -1
- package/apps/cf-worker/wrangler.toml +69 -19
- package/apps/local-cli/package.json +1 -1
- package/apps/local-cli/src/config-loader.ts +11 -0
- package/apps/local-cli/src/main.ts +1 -1
- package/apps/local-cli/src/server.ts +46 -3
- package/apps/local-cli/tests/e2e.test.ts +52 -0
- package/package.json +19 -16
- package/packages/aws-adapter/package.json +3 -3
- package/packages/aws-adapter/src/account-store.ts +121 -0
- package/packages/aws-adapter/src/admission.ts +74 -0
- package/packages/aws-adapter/src/aws-runtime.ts +124 -34
- package/packages/aws-adapter/src/cdk-table-defs.ts +5 -1
- package/packages/aws-adapter/src/config-loader.ts +62 -0
- package/packages/aws-adapter/src/dynamo-account-store.ts +95 -0
- package/packages/aws-adapter/src/handlers/connect.ts +64 -8
- package/packages/aws-adapter/src/handlers/default.ts +43 -2
- package/packages/aws-adapter/src/handlers/disconnect.ts +27 -8
- package/packages/aws-adapter/src/handlers/index.ts +120 -9
- package/packages/aws-adapter/src/handlers/nlb-stream.ts +481 -0
- package/packages/aws-adapter/src/handlers/ping-checker.ts +1 -1
- package/packages/aws-adapter/src/index.ts +13 -0
- package/packages/aws-adapter/tests/account-store-dynamo.test.ts +182 -0
- package/packages/aws-adapter/tests/account-store.test.ts +279 -0
- package/packages/aws-adapter/tests/admission.test.ts +70 -0
- package/packages/aws-adapter/tests/aws-harness.ts +13 -1
- package/packages/aws-adapter/tests/config-loader.test.ts +75 -0
- package/packages/aws-adapter/tests/connect.test.ts +78 -0
- package/packages/aws-adapter/tests/disconnect-fanout.test.ts +343 -0
- package/packages/aws-adapter/tests/gone-exception.test.ts +31 -26
- package/packages/aws-adapter/tests/handlers.test.ts +194 -47
- package/packages/aws-adapter/tests/nlb-stream.test.ts +478 -0
- package/packages/aws-adapter/tests/ping-checker.test.ts +34 -29
- package/packages/aws-adapter/tests/sweeper.test.ts +25 -18
- package/packages/aws-adapter/tests/transactions.test.ts +25 -20
- package/packages/cf-adapter/package.json +1 -1
- package/packages/cf-adapter/src/cf-runtime.ts +40 -22
- package/packages/cf-adapter/src/channel-do.ts +40 -1
- package/packages/cf-adapter/src/channel-registry-do.ts +58 -0
- package/packages/cf-adapter/src/config-loader.ts +62 -0
- package/packages/cf-adapter/src/connection-do.ts +181 -31
- package/packages/cf-adapter/src/d1-account-store.ts +198 -0
- package/packages/cf-adapter/src/env.ts +58 -8
- package/packages/cf-adapter/src/index.ts +11 -9
- package/packages/cf-adapter/tests/cf-harness.ts +11 -1
- package/packages/cf-adapter/tests/cf-integration.test.ts +2 -1
- package/packages/cf-adapter/tests/cf-runtime.test.ts +91 -0
- package/packages/cf-adapter/tests/config-loader.test.ts +22 -0
- package/packages/cf-adapter/tests/connection-do-channel-registration.test.ts +37 -0
- package/packages/cf-adapter/tests/connection-do-no-batching-reservation.test.ts +52 -0
- package/packages/cf-adapter/tests/connection-do-sasl-d1.test.ts +166 -0
- package/packages/cf-adapter/tests/connection-do.test.ts +40 -0
- package/packages/cf-adapter/tests/d1-account-store.test.ts +226 -0
- package/packages/cf-adapter/tests/raw-modules.d.ts +11 -0
- package/packages/cf-adapter/tests/worker/main.ts +9 -1
- package/packages/cf-adapter/tests/worker/stubs/channel-stub.ts +7 -1
- package/packages/cf-adapter/wrangler.test.toml +18 -1
- package/packages/in-memory-runtime/package.json +1 -1
- package/packages/in-memory-runtime/src/in-memory-runtime.ts +14 -28
- package/packages/in-memory-runtime/tests/in-memory-runtime.test.ts +19 -0
- package/packages/irc-core/package.json +8 -2
- package/packages/irc-core/scripts/generate-build-info.mjs +31 -0
- package/packages/irc-core/src/caps/capabilities.ts +23 -2
- package/packages/irc-core/src/case-fold.ts +64 -0
- package/packages/irc-core/src/cloak.ts +1 -1
- package/packages/irc-core/src/commands/cap.ts +8 -1
- package/packages/irc-core/src/commands/index.ts +11 -0
- package/packages/irc-core/src/commands/invite.ts +6 -9
- package/packages/irc-core/src/commands/ison.ts +61 -0
- package/packages/irc-core/src/commands/isupport.ts +1 -1
- package/packages/irc-core/src/commands/join.ts +4 -3
- package/packages/irc-core/src/commands/kick.ts +4 -11
- package/packages/irc-core/src/commands/list.ts +5 -4
- package/packages/irc-core/src/commands/mode.ts +5 -9
- package/packages/irc-core/src/commands/motd-lines.ts +127 -0
- package/packages/irc-core/src/commands/motd.ts +6 -110
- package/packages/irc-core/src/commands/oper.ts +151 -0
- package/packages/irc-core/src/commands/quit.ts +12 -0
- package/packages/irc-core/src/commands/registration.ts +26 -19
- package/packages/irc-core/src/commands/sasl.ts +72 -9
- package/packages/irc-core/src/commands/server-info.ts +129 -0
- package/packages/irc-core/src/commands/tagmsg.ts +205 -0
- package/packages/irc-core/src/commands/userhost.ts +84 -0
- package/packages/irc-core/src/commands/whowas.ts +113 -0
- package/packages/irc-core/src/config.ts +84 -3
- package/packages/irc-core/src/credential-hashing.ts +124 -0
- package/packages/irc-core/src/effects.ts +6 -29
- package/packages/irc-core/src/index.ts +3 -0
- package/packages/irc-core/src/ports.ts +240 -7
- package/packages/irc-core/src/protocol/numerics.ts +14 -8
- package/packages/irc-core/src/types.ts +60 -1
- package/packages/irc-core/tests/account-store.test.ts +131 -0
- package/packages/irc-core/tests/caps/capabilities.test.ts +4 -3
- package/packages/irc-core/tests/case-fold.test.ts +80 -0
- package/packages/irc-core/tests/commands/cap.test.ts +33 -1
- package/packages/irc-core/tests/commands/ison.test.ts +166 -0
- package/packages/irc-core/tests/commands/kick.test.ts +15 -0
- package/packages/irc-core/tests/commands/oper.test.ts +257 -0
- package/packages/irc-core/tests/commands/quit.test.ts +69 -2
- package/packages/irc-core/tests/commands/registration.test.ts +256 -19
- package/packages/irc-core/tests/commands/sasl.test.ts +118 -10
- package/packages/irc-core/tests/commands/server-info.test.ts +274 -0
- package/packages/irc-core/tests/commands/tagmsg.test.ts +662 -0
- package/packages/irc-core/tests/commands/userhost.test.ts +264 -0
- package/packages/irc-core/tests/commands/who.test.ts +22 -4
- package/packages/irc-core/tests/commands/whois.test.ts +8 -1
- package/packages/irc-core/tests/commands/whowas.test.ts +312 -0
- package/packages/irc-core/tests/config.test.ts +170 -1
- package/packages/irc-core/tests/credential-hashing.test.ts +170 -0
- package/packages/irc-core/tests/effects.test.ts +0 -27
- package/packages/irc-core/tests/nick-history-store.test.ts +162 -0
- package/packages/irc-core/tests/numerics.test.ts +12 -0
- package/packages/irc-core/tests/types.test.ts +35 -1
- package/packages/irc-core/tsconfig.build.json +1 -1
- package/packages/irc-core/tsconfig.test.json +1 -1
- package/packages/irc-server/package.json +1 -1
- package/packages/irc-server/src/actor.ts +182 -14
- package/packages/irc-server/src/dispatch.ts +0 -3
- package/packages/irc-server/src/index.ts +10 -2
- package/packages/irc-server/src/routing.ts +21 -0
- package/packages/irc-server/src/transport.ts +101 -0
- package/packages/irc-server/tests/actor.test.ts +617 -1
- package/packages/irc-server/tests/dispatch.test.ts +0 -17
- package/packages/irc-server/tests/routing.test.ts +7 -0
- package/packages/irc-server/tests/transport.test.ts +230 -0
- package/packages/irc-test-support/package.json +1 -1
- package/packages/irc-test-support/src/harness.ts +44 -9
- package/packages/irc-test-support/src/in-memory-harness.ts +78 -13
- package/packages/irc-test-support/src/index.ts +3 -0
- package/packages/irc-test-support/src/scenarios.ts +132 -2
- package/packages/irc-test-support/tests/in-memory-harness.test.ts +2 -1
- package/packages/irc-test-support/tests/in-memory-scenarios.test.ts +23 -9
- package/pnpm-workspace.yaml +9 -0
- package/tools/ci-hardening/package.json +1 -1
- package/tools/package.json +4 -0
- package/tools/seed-aws-accounts.ts +79 -0
- package/tools/seed-cf-accounts.ts +104 -0
- package/tools/tcp-ws-forwarder/package.json +1 -1
- package/AGENTS.md +0 -5
- package/MOTD.txt +0 -3
- package/PLAN-FIXES.md +0 -358
- package/PLAN.md +0 -420
- package/dashboards/cloudwatch-irc.json +0 -139
- package/docs/AWS-Adapter-Architecture.md +0 -494
- package/docs/AWS-Deployment.md +0 -1107
- package/docs/Cloudflare-Deployment-Guide.md +0 -660
- package/docs/Home.md +0 -1
- package/docs/Observability.md +0 -87
- package/docs/PlanIRCv3Websocket.md +0 -489
- package/docs/PlanWebClient.md +0 -451
- package/docs/Release-Process.md +0 -440
- package/progress.md +0 -107
- package/tickets.md +0 -2485
- package/webircgateway/LICENSE +0 -201
- package/webircgateway/Makefile +0 -44
- package/webircgateway/README.md +0 -134
- package/webircgateway/config.conf.example +0 -135
- package/webircgateway/go.mod +0 -16
- package/webircgateway/go.sum +0 -89
- package/webircgateway/main.go +0 -118
- package/webircgateway/pkg/dnsbl/dnsbl.go +0 -121
- package/webircgateway/pkg/identd/identd.go +0 -86
- package/webircgateway/pkg/identd/rpcclient.go +0 -59
- package/webircgateway/pkg/irc/isupport.go +0 -56
- package/webircgateway/pkg/irc/message.go +0 -217
- package/webircgateway/pkg/irc/state.go +0 -79
- package/webircgateway/pkg/proxy/proxy.go +0 -129
- package/webircgateway/pkg/proxy/server.go +0 -237
- package/webircgateway/pkg/recaptcha/recaptcha.go +0 -59
- package/webircgateway/pkg/webircgateway/client.go +0 -741
- package/webircgateway/pkg/webircgateway/client_command_handlers.go +0 -495
- package/webircgateway/pkg/webircgateway/config.go +0 -385
- package/webircgateway/pkg/webircgateway/gateway.go +0 -278
- package/webircgateway/pkg/webircgateway/gateway_utils.go +0 -133
- package/webircgateway/pkg/webircgateway/hooks.go +0 -152
- package/webircgateway/pkg/webircgateway/letsencrypt.go +0 -41
- package/webircgateway/pkg/webircgateway/messagetags.go +0 -103
- package/webircgateway/pkg/webircgateway/transport_kiwiirc.go +0 -206
- package/webircgateway/pkg/webircgateway/transport_sockjs.go +0 -107
- package/webircgateway/pkg/webircgateway/transport_tcp.go +0 -113
- package/webircgateway/pkg/webircgateway/transport_websocket.go +0 -126
- package/webircgateway/pkg/webircgateway/utils.go +0 -147
- package/webircgateway/plugins/example/plugin.go +0 -11
- package/webircgateway/plugins/stats/plugin.go +0 -52
- package/webircgateway/staticcheck.conf +0 -1
- package/webircgateway/webircgateway.svg +0 -3
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
package webircgateway
|
|
2
|
-
|
|
3
|
-
import (
|
|
4
|
-
"errors"
|
|
5
|
-
"math/rand"
|
|
6
|
-
"net"
|
|
7
|
-
"net/http"
|
|
8
|
-
"strings"
|
|
9
|
-
)
|
|
10
|
-
|
|
11
|
-
var v4LoopbackAddr = net.ParseIP("127.0.0.1")
|
|
12
|
-
|
|
13
|
-
func (s *Gateway) NewClient() *Client {
|
|
14
|
-
return NewClient(s)
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
func (s *Gateway) IsClientOriginAllowed(originHeader string) bool {
|
|
18
|
-
// Empty list of origins = all origins allowed
|
|
19
|
-
if len(s.Config.RemoteOrigins) == 0 {
|
|
20
|
-
return true
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// No origin header = running on the same page
|
|
24
|
-
if originHeader == "" {
|
|
25
|
-
return true
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
foundMatch := false
|
|
29
|
-
|
|
30
|
-
for _, originMatch := range s.Config.RemoteOrigins {
|
|
31
|
-
if originMatch.Match(originHeader) {
|
|
32
|
-
foundMatch = true
|
|
33
|
-
break
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
return foundMatch
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
func (s *Gateway) isIrcAddressAllowed(addr string) bool {
|
|
41
|
-
// Empty whitelist = all destinations allowed
|
|
42
|
-
if len(s.Config.GatewayWhitelist) == 0 {
|
|
43
|
-
return true
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
foundMatch := false
|
|
47
|
-
|
|
48
|
-
for _, addrMatch := range s.Config.GatewayWhitelist {
|
|
49
|
-
if addrMatch.Match(addr) {
|
|
50
|
-
foundMatch = true
|
|
51
|
-
break
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
return foundMatch
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
func (s *Gateway) findUpstream() (ConfigUpstream, error) {
|
|
59
|
-
var ret ConfigUpstream
|
|
60
|
-
|
|
61
|
-
if len(s.Config.Upstreams) == 0 {
|
|
62
|
-
return ret, errors.New("No upstreams available")
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
randIdx := rand.Intn(len(s.Config.Upstreams))
|
|
66
|
-
ret = s.Config.Upstreams[randIdx]
|
|
67
|
-
|
|
68
|
-
return ret, nil
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
func (s *Gateway) findWebircPassword(ircHost string) string {
|
|
72
|
-
pass, exists := s.Config.GatewayWebircPassword[strings.ToLower(ircHost)]
|
|
73
|
-
if !exists {
|
|
74
|
-
pass = ""
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
return pass
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
func (s *Gateway) GetRemoteAddressFromRequest(req *http.Request) net.IP {
|
|
81
|
-
remoteIP := remoteIPFromRequest(req)
|
|
82
|
-
|
|
83
|
-
// If the remoteIP is not in a whitelisted reverse proxy range, don't trust
|
|
84
|
-
// the headers and use the remoteIP as the users IP
|
|
85
|
-
if !s.isTrustedProxy(remoteIP) {
|
|
86
|
-
return remoteIP
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
headerVal := req.Header.Get("x-forwarded-for")
|
|
90
|
-
ips := strings.Split(headerVal, ",")
|
|
91
|
-
ipStr := strings.Trim(ips[0], " ")
|
|
92
|
-
if ipStr != "" {
|
|
93
|
-
ip := net.ParseIP(ipStr)
|
|
94
|
-
if ip != nil {
|
|
95
|
-
remoteIP = ip
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
return remoteIP
|
|
100
|
-
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
func (s *Gateway) isRequestSecure(req *http.Request) bool {
|
|
104
|
-
remoteIP := remoteIPFromRequest(req)
|
|
105
|
-
|
|
106
|
-
// If the remoteIP is not in a whitelisted reverse proxy range, don't trust
|
|
107
|
-
// the headers and check the request directly
|
|
108
|
-
if !s.isTrustedProxy(remoteIP) {
|
|
109
|
-
return req.TLS != nil
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
fwdProto := req.Header.Get("x-forwarded-proto")
|
|
113
|
-
return strings.EqualFold(fwdProto, "https")
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
func (s *Gateway) isTrustedProxy(remoteIP net.IP) bool {
|
|
117
|
-
for _, cidrRange := range s.Config.ReverseProxies {
|
|
118
|
-
if cidrRange.Contains(remoteIP) {
|
|
119
|
-
return true
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
return false
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
func remoteIPFromRequest(req *http.Request) net.IP {
|
|
126
|
-
if req.RemoteAddr == "@" {
|
|
127
|
-
// remote address is unix socket, treat it as loopback interface
|
|
128
|
-
return v4LoopbackAddr
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
remoteAddr, _, _ := net.SplitHostPort(req.RemoteAddr)
|
|
132
|
-
return net.ParseIP(remoteAddr)
|
|
133
|
-
}
|
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
package webircgateway
|
|
2
|
-
|
|
3
|
-
import "github.com/kiwiirc/webircgateway/pkg/irc"
|
|
4
|
-
|
|
5
|
-
var hooksRegistered map[string][]interface{}
|
|
6
|
-
|
|
7
|
-
func init() {
|
|
8
|
-
hooksRegistered = make(map[string][]interface{})
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
func HookRegister(hookName string, p interface{}) {
|
|
12
|
-
_, exists := hooksRegistered[hookName]
|
|
13
|
-
if !exists {
|
|
14
|
-
hooksRegistered[hookName] = make([]interface{}, 0)
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
hooksRegistered[hookName] = append(hooksRegistered[hookName], p)
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
type Hook struct {
|
|
21
|
-
ID string
|
|
22
|
-
Halt bool
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
func (h *Hook) getCallbacks(eventType string) []interface{} {
|
|
26
|
-
var f []interface{}
|
|
27
|
-
f = make([]interface{}, 0)
|
|
28
|
-
|
|
29
|
-
callbacks, exists := hooksRegistered[eventType]
|
|
30
|
-
if exists {
|
|
31
|
-
f = callbacks
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
return f
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* HookIrcConnectionPre
|
|
39
|
-
* Dispatched just before an IRC connection is attempted
|
|
40
|
-
* Types: irc.connection.pre
|
|
41
|
-
*/
|
|
42
|
-
type HookIrcConnectionPre struct {
|
|
43
|
-
Hook
|
|
44
|
-
Client *Client
|
|
45
|
-
UpstreamConfig *ConfigUpstream
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
func (h *HookIrcConnectionPre) Dispatch(eventType string) {
|
|
49
|
-
for _, p := range h.getCallbacks(eventType) {
|
|
50
|
-
if f, ok := p.(func(*HookIrcConnectionPre)); ok {
|
|
51
|
-
f(h)
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* HookIrcLine
|
|
58
|
-
* Dispatched when either:
|
|
59
|
-
* * A line arrives from the IRCd, before sending to the client
|
|
60
|
-
* * A line arrives from the client, before sending to the IRCd
|
|
61
|
-
* Types: irc.line
|
|
62
|
-
*/
|
|
63
|
-
type HookIrcLine struct {
|
|
64
|
-
Hook
|
|
65
|
-
Client *Client
|
|
66
|
-
UpstreamConfig *ConfigUpstream
|
|
67
|
-
Line string
|
|
68
|
-
Message *irc.Message
|
|
69
|
-
ToServer bool
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
func (h *HookIrcLine) Dispatch(eventType string) {
|
|
73
|
-
for _, p := range h.getCallbacks(eventType) {
|
|
74
|
-
if f, ok := p.(func(*HookIrcLine)); ok {
|
|
75
|
-
f(h)
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* HookClientState
|
|
82
|
-
* Dispatched after a client connects or disconnects
|
|
83
|
-
* Types: client.state
|
|
84
|
-
*/
|
|
85
|
-
type HookClientState struct {
|
|
86
|
-
Hook
|
|
87
|
-
Client *Client
|
|
88
|
-
Connected bool
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
func (h *HookClientState) Dispatch(eventType string) {
|
|
92
|
-
for _, p := range h.getCallbacks(eventType) {
|
|
93
|
-
if f, ok := p.(func(*HookClientState)); ok {
|
|
94
|
-
f(h)
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* HookClientInit
|
|
101
|
-
* Dispatched directly after a new Client instance has been created
|
|
102
|
-
* Types: client.init
|
|
103
|
-
*/
|
|
104
|
-
type HookClientInit struct {
|
|
105
|
-
Hook
|
|
106
|
-
Client *Client
|
|
107
|
-
Connected bool
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
func (h *HookClientInit) Dispatch(eventType string) {
|
|
111
|
-
for _, p := range h.getCallbacks(eventType) {
|
|
112
|
-
if f, ok := p.(func(*HookClientInit)); ok {
|
|
113
|
-
f(h)
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* HookStatus
|
|
120
|
-
* Dispatched for each line output of the _status HTTP request
|
|
121
|
-
* Types: status.client
|
|
122
|
-
*/
|
|
123
|
-
type HookStatus struct {
|
|
124
|
-
Hook
|
|
125
|
-
Client *Client
|
|
126
|
-
Line string
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
func (h *HookStatus) Dispatch(eventType string) {
|
|
130
|
-
for _, p := range h.getCallbacks(eventType) {
|
|
131
|
-
if f, ok := p.(func(*HookStatus)); ok {
|
|
132
|
-
f(h)
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* HookGatewayClosing
|
|
139
|
-
* Dispatched when the gateway has been told to shutdown
|
|
140
|
-
* Types: gateway.closing
|
|
141
|
-
*/
|
|
142
|
-
type HookGatewayClosing struct {
|
|
143
|
-
Hook
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
func (h *HookGatewayClosing) Dispatch(eventType string) {
|
|
147
|
-
for _, p := range h.getCallbacks(eventType) {
|
|
148
|
-
if f, ok := p.(func(*HookGatewayClosing)); ok {
|
|
149
|
-
f(h)
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
}
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
package webircgateway
|
|
2
|
-
|
|
3
|
-
import (
|
|
4
|
-
"context"
|
|
5
|
-
"strings"
|
|
6
|
-
"sync"
|
|
7
|
-
|
|
8
|
-
"golang.org/x/crypto/acme/autocert"
|
|
9
|
-
)
|
|
10
|
-
|
|
11
|
-
type LEManager struct {
|
|
12
|
-
// ensure only one instance of the manager and handler is running
|
|
13
|
-
// while allowing multiple listeners to use it
|
|
14
|
-
Mutex sync.Mutex
|
|
15
|
-
Manager *autocert.Manager
|
|
16
|
-
gateway *Gateway
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
func NewLetsEncryptManager(gateway *Gateway) *LEManager {
|
|
20
|
-
return &LEManager{gateway: gateway}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
func (le *LEManager) Get(certCacheDir string) *autocert.Manager {
|
|
24
|
-
le.Mutex.Lock()
|
|
25
|
-
defer le.Mutex.Unlock()
|
|
26
|
-
|
|
27
|
-
// Create it if it doesn't already exist
|
|
28
|
-
if le.Manager == nil {
|
|
29
|
-
le.Manager = &autocert.Manager{
|
|
30
|
-
Prompt: autocert.AcceptTOS,
|
|
31
|
-
Cache: autocert.DirCache(strings.TrimRight(certCacheDir, "/")),
|
|
32
|
-
HostPolicy: func(ctx context.Context, host string) error {
|
|
33
|
-
le.gateway.Log(2, "Automatically requesting a HTTPS certificate for %s", host)
|
|
34
|
-
return nil
|
|
35
|
-
},
|
|
36
|
-
}
|
|
37
|
-
le.gateway.HttpRouter.Handle("/.well-known/", le.Manager.HTTPHandler(nil))
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
return le.Manager
|
|
41
|
-
}
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
package webircgateway
|
|
2
|
-
|
|
3
|
-
import (
|
|
4
|
-
"strings"
|
|
5
|
-
"sync"
|
|
6
|
-
"time"
|
|
7
|
-
|
|
8
|
-
"github.com/OneOfOne/xxhash"
|
|
9
|
-
"github.com/kiwiirc/webircgateway/pkg/irc"
|
|
10
|
-
)
|
|
11
|
-
|
|
12
|
-
type MessageTagManager struct {
|
|
13
|
-
Mutex sync.Mutex
|
|
14
|
-
knownTags map[uint64]MessageTags
|
|
15
|
-
gcTimes map[uint64]time.Time
|
|
16
|
-
}
|
|
17
|
-
type MessageTags struct {
|
|
18
|
-
Tags map[string]string
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
func NewMessageTagManager() *MessageTagManager {
|
|
22
|
-
tm := &MessageTagManager{
|
|
23
|
-
knownTags: make(map[uint64]MessageTags),
|
|
24
|
-
gcTimes: make(map[uint64]time.Time),
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
go tm.RunGarbageCollectionLoop()
|
|
28
|
-
return tm
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
func (tags *MessageTagManager) CanMessageContainClientTags(msg *irc.Message) bool {
|
|
32
|
-
return stringInSlice(msg.Command, []string{
|
|
33
|
-
"PRIVMSG",
|
|
34
|
-
"NOTICE",
|
|
35
|
-
"TAGMSG",
|
|
36
|
-
})
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
func (tags *MessageTagManager) RunGarbageCollectionLoop() {
|
|
40
|
-
for {
|
|
41
|
-
tags.Mutex.Lock()
|
|
42
|
-
for messageHash, timeCreated := range tags.gcTimes {
|
|
43
|
-
if timeCreated.Add(time.Second * 30).After(time.Now()) {
|
|
44
|
-
delete(tags.knownTags, messageHash)
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
}
|
|
48
|
-
tags.Mutex.Unlock()
|
|
49
|
-
|
|
50
|
-
time.Sleep(time.Second * 30)
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
func (tags *MessageTagManager) AddTagsFromMessage(client *Client, fromNick string, msg *irc.Message) {
|
|
55
|
-
if !tags.CanMessageContainClientTags(msg) {
|
|
56
|
-
return
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
clientTags := MessageTags{
|
|
60
|
-
Tags: make(map[string]string),
|
|
61
|
-
}
|
|
62
|
-
for tagName, tagVal := range msg.Tags {
|
|
63
|
-
if len(tagName) > 0 && tagName[0] == '+' {
|
|
64
|
-
clientTags.Tags[tagName] = tagVal
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
if len(clientTags.Tags) > 0 {
|
|
69
|
-
tags.Mutex.Lock()
|
|
70
|
-
msgHash := tags.messageHash(client, fromNick, msg)
|
|
71
|
-
tags.knownTags[msgHash] = clientTags
|
|
72
|
-
tags.gcTimes[msgHash] = time.Now()
|
|
73
|
-
tags.Mutex.Unlock()
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
func (tags *MessageTagManager) GetTagsFromMessage(client *Client, fromNick string, msg *irc.Message) (MessageTags, bool) {
|
|
78
|
-
if !tags.CanMessageContainClientTags(msg) {
|
|
79
|
-
return MessageTags{}, false
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
msgHash := tags.messageHash(client, fromNick, msg)
|
|
83
|
-
|
|
84
|
-
tags.Mutex.Lock()
|
|
85
|
-
defer tags.Mutex.Unlock()
|
|
86
|
-
|
|
87
|
-
clientTags, tagsExist := tags.knownTags[msgHash]
|
|
88
|
-
if !tagsExist {
|
|
89
|
-
return clientTags, false
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
return clientTags, true
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
func (tags *MessageTagManager) messageHash(client *Client, fromNick string, msg *irc.Message) uint64 {
|
|
96
|
-
h := xxhash.New64()
|
|
97
|
-
h.WriteString(strings.ToLower(client.UpstreamConfig.Hostname))
|
|
98
|
-
h.WriteString(strings.ToLower(fromNick))
|
|
99
|
-
// make target case insensitive
|
|
100
|
-
h.WriteString(strings.ToLower(msg.GetParam(0, "")))
|
|
101
|
-
h.WriteString(msg.GetParam(1, ""))
|
|
102
|
-
return h.Sum64()
|
|
103
|
-
}
|
|
@@ -1,206 +0,0 @@
|
|
|
1
|
-
package webircgateway
|
|
2
|
-
|
|
3
|
-
import (
|
|
4
|
-
"fmt"
|
|
5
|
-
"log"
|
|
6
|
-
"net"
|
|
7
|
-
"net/http"
|
|
8
|
-
"runtime/debug"
|
|
9
|
-
"strings"
|
|
10
|
-
"sync"
|
|
11
|
-
|
|
12
|
-
"github.com/gorilla/websocket"
|
|
13
|
-
"github.com/igm/sockjs-go/v3/sockjs"
|
|
14
|
-
cmap "github.com/orcaman/concurrent-map"
|
|
15
|
-
)
|
|
16
|
-
|
|
17
|
-
type TransportKiwiirc struct {
|
|
18
|
-
gateway *Gateway
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
func (t *TransportKiwiirc) Init(g *Gateway) {
|
|
22
|
-
t.gateway = g
|
|
23
|
-
sockjsOptions := sockjs.DefaultOptions
|
|
24
|
-
sockjsOptions.WebsocketUpgrader = &websocket.Upgrader{
|
|
25
|
-
// Origin is checked within the session handler
|
|
26
|
-
CheckOrigin: func(_ *http.Request) bool { return true },
|
|
27
|
-
}
|
|
28
|
-
handler := sockjs.NewHandler("/webirc/kiwiirc", sockjsOptions, t.sessionHandler)
|
|
29
|
-
t.gateway.HttpRouter.Handle("/webirc/kiwiirc/", handler)
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
func (t *TransportKiwiirc) makeChannel(chanID string, ws sockjs.Session) *TransportKiwiircChannel {
|
|
33
|
-
client := t.gateway.NewClient()
|
|
34
|
-
|
|
35
|
-
originHeader := strings.ToLower(ws.Request().Header.Get("Origin"))
|
|
36
|
-
if !t.gateway.IsClientOriginAllowed(originHeader) {
|
|
37
|
-
client.Log(2, "Origin %s not allowed. Closing connection", originHeader)
|
|
38
|
-
ws.Close(0, "Origin not allowed")
|
|
39
|
-
return nil
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
client.RemoteAddr = t.gateway.GetRemoteAddressFromRequest(ws.Request()).String()
|
|
43
|
-
|
|
44
|
-
clientHostnames, err := net.LookupAddr(client.RemoteAddr)
|
|
45
|
-
if err != nil || len(clientHostnames) == 0 {
|
|
46
|
-
client.RemoteHostname = client.RemoteAddr
|
|
47
|
-
} else {
|
|
48
|
-
// FQDNs include a . at the end. Strip it out
|
|
49
|
-
potentialHostname := strings.Trim(clientHostnames[0], ".")
|
|
50
|
-
|
|
51
|
-
// Must check that the resolved hostname also resolves back to the users IP
|
|
52
|
-
addr, err := net.LookupIP(potentialHostname)
|
|
53
|
-
if err == nil && len(addr) == 1 && addr[0].String() == client.RemoteAddr {
|
|
54
|
-
client.RemoteHostname = potentialHostname
|
|
55
|
-
} else {
|
|
56
|
-
client.RemoteHostname = client.RemoteAddr
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
if t.gateway.isRequestSecure(ws.Request()) {
|
|
61
|
-
client.Tags["secure"] = ""
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// This doesn't make sense to have since the remote port may change between requests. Only
|
|
65
|
-
// here for testing purposes for now.
|
|
66
|
-
_, remoteAddrPort, _ := net.SplitHostPort(ws.Request().RemoteAddr)
|
|
67
|
-
client.Tags["remote-port"] = remoteAddrPort
|
|
68
|
-
|
|
69
|
-
client.Log(2, "New kiwiirc channel on %s from %s %s", ws.Request().Host, client.RemoteAddr, client.RemoteHostname)
|
|
70
|
-
client.Ready()
|
|
71
|
-
|
|
72
|
-
channel := &TransportKiwiircChannel{
|
|
73
|
-
Id: chanID,
|
|
74
|
-
Client: client,
|
|
75
|
-
Conn: ws,
|
|
76
|
-
waitForClose: make(chan bool),
|
|
77
|
-
Closed: false,
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
go channel.listenForSignals()
|
|
81
|
-
|
|
82
|
-
return channel
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
func (t *TransportKiwiirc) sessionHandler(session sockjs.Session) {
|
|
86
|
-
// Don't let a single users error kill the entire service for everyone
|
|
87
|
-
defer func() {
|
|
88
|
-
if r := recover(); r != nil {
|
|
89
|
-
log.Printf("[ERROR] Recovered from %s\n%s", r, debug.Stack())
|
|
90
|
-
}
|
|
91
|
-
}()
|
|
92
|
-
|
|
93
|
-
channels := cmap.New()
|
|
94
|
-
|
|
95
|
-
// Read from sockjs
|
|
96
|
-
go func() {
|
|
97
|
-
for {
|
|
98
|
-
msg, err := session.Recv()
|
|
99
|
-
if err == nil && len(msg) > 0 {
|
|
100
|
-
idEnd := strings.Index(msg, " ")
|
|
101
|
-
if idEnd == -1 {
|
|
102
|
-
// msg is in the form of ":chanId"
|
|
103
|
-
chanID := msg[1:]
|
|
104
|
-
|
|
105
|
-
c, channelExists := channels.Get(chanID)
|
|
106
|
-
if channelExists {
|
|
107
|
-
channel := c.(*TransportKiwiircChannel)
|
|
108
|
-
channel.close()
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
if !channelExists {
|
|
112
|
-
channel := t.makeChannel(chanID, session)
|
|
113
|
-
if channel == nil {
|
|
114
|
-
continue
|
|
115
|
-
}
|
|
116
|
-
channels.Set(chanID, channel)
|
|
117
|
-
|
|
118
|
-
// When the channel closes, remove it from the map again
|
|
119
|
-
go func() {
|
|
120
|
-
<-channel.waitForClose
|
|
121
|
-
channel.Client.Log(2, "Removing channel from connection")
|
|
122
|
-
channels.Remove(chanID)
|
|
123
|
-
}()
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
session.Send(":" + chanID)
|
|
127
|
-
|
|
128
|
-
} else {
|
|
129
|
-
// msg is in the form of ":chanId data"
|
|
130
|
-
chanID := msg[1:idEnd]
|
|
131
|
-
data := msg[idEnd+1:]
|
|
132
|
-
|
|
133
|
-
channel, channelExists := channels.Get(chanID)
|
|
134
|
-
if channelExists {
|
|
135
|
-
c := channel.(*TransportKiwiircChannel)
|
|
136
|
-
c.handleIncomingLine(data)
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
} else if err != nil {
|
|
140
|
-
t.gateway.Log(1, "kiwi connection closed (%s)", err.Error())
|
|
141
|
-
break
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
for channel := range channels.IterBuffered() {
|
|
146
|
-
c := channel.Val.(*TransportKiwiircChannel)
|
|
147
|
-
c.Closed = true
|
|
148
|
-
c.Client.StartShutdown("client_closed")
|
|
149
|
-
}
|
|
150
|
-
}()
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
type TransportKiwiircChannel struct {
|
|
154
|
-
Conn sockjs.Session
|
|
155
|
-
Client *Client
|
|
156
|
-
Id string
|
|
157
|
-
waitForClose chan bool
|
|
158
|
-
ClosedLock sync.Mutex
|
|
159
|
-
Closed bool
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
func (c *TransportKiwiircChannel) listenForSignals() {
|
|
163
|
-
for {
|
|
164
|
-
signal, ok := <-c.Client.Signals
|
|
165
|
-
if !ok {
|
|
166
|
-
break
|
|
167
|
-
}
|
|
168
|
-
c.Client.Log(1, "signal:%s %s", signal[0], signal[1])
|
|
169
|
-
if signal[0] == "state" {
|
|
170
|
-
if signal[1] == "connected" {
|
|
171
|
-
c.Conn.Send(fmt.Sprintf(":%s control connected", c.Id))
|
|
172
|
-
} else if signal[1] == "closed" {
|
|
173
|
-
c.Conn.Send(fmt.Sprintf(":%s control closed %s", c.Id, signal[2]))
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
if signal[0] == "data" {
|
|
178
|
-
toSend := strings.Trim(signal[1], "\r\n")
|
|
179
|
-
c.Conn.Send(fmt.Sprintf(":%s %s", c.Id, toSend))
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
c.ClosedLock.Lock()
|
|
184
|
-
|
|
185
|
-
c.Closed = true
|
|
186
|
-
close(c.Client.Recv)
|
|
187
|
-
close(c.waitForClose)
|
|
188
|
-
|
|
189
|
-
c.ClosedLock.Unlock()
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
func (c *TransportKiwiircChannel) handleIncomingLine(line string) {
|
|
193
|
-
c.ClosedLock.Lock()
|
|
194
|
-
|
|
195
|
-
if !c.Closed {
|
|
196
|
-
c.Client.Recv <- line
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
c.ClosedLock.Unlock()
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
func (c *TransportKiwiircChannel) close() {
|
|
203
|
-
if c.Client.upstream != nil {
|
|
204
|
-
c.Client.upstream.Close()
|
|
205
|
-
}
|
|
206
|
-
}
|