serverless-ircd 0.2.0 → 0.3.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/CHANGELOG.md +84 -0
- package/README.md +28 -19
- 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 +5 -1
- package/apps/cf-worker/wrangler.toml +41 -17
- package/apps/local-cli/package.json +1 -1
- package/apps/local-cli/src/config-loader.ts +1 -0
- package/apps/local-cli/src/main.ts +1 -1
- package/apps/local-cli/src/server.ts +28 -2
- package/apps/local-cli/tests/e2e.test.ts +52 -0
- package/docs/ADR-001-pure-reducers-and-effect-system.md +74 -0
- package/docs/ADR-002-location-of-authority.md +82 -0
- package/docs/ADR-003-durable-object-sharding.md +93 -0
- package/docs/ADR-004-dynamodb-schema.md +96 -0
- package/docs/ADR-005-wss-only-transport-v1.md +83 -0
- package/docs/ADR-006-sasl-mechanism-scope.md +86 -0
- package/docs/ADR-007-deterministic-ports.md +82 -0
- package/docs/ADR-008-monorepo-tooling.md +60 -0
- package/docs/AWS-Adapter-Architecture.md +6 -4
- package/docs/AWS-Deployment.md +86 -7
- package/docs/Cloudflare-Deployment-Guide.md +5 -5
- package/docs/Home.md +11 -1
- package/docs/Release-Process.md +9 -6
- package/package.json +14 -15
- package/packages/aws-adapter/package.json +1 -1
- package/packages/aws-adapter/src/account-store.ts +121 -0
- package/packages/aws-adapter/src/aws-runtime.ts +118 -30
- package/packages/aws-adapter/src/cdk-table-defs.ts +5 -1
- package/packages/aws-adapter/src/config-loader.ts +30 -0
- package/packages/aws-adapter/src/dynamo-account-store.ts +156 -0
- package/packages/aws-adapter/src/handlers/default.ts +8 -0
- package/packages/aws-adapter/src/handlers/disconnect.ts +27 -8
- package/packages/aws-adapter/src/handlers/index.ts +51 -6
- package/packages/aws-adapter/src/index.ts +7 -0
- package/packages/aws-adapter/tests/account-store-dynamo.test.ts +171 -0
- package/packages/aws-adapter/tests/account-store.test.ts +280 -0
- package/packages/aws-adapter/tests/config-loader.test.ts +55 -0
- package/packages/aws-adapter/tests/disconnect-fanout.test.ts +336 -0
- package/packages/aws-adapter/tests/handlers.test.ts +46 -0
- package/packages/cf-adapter/package.json +1 -1
- package/packages/cf-adapter/src/cf-runtime.ts +42 -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 +29 -0
- package/packages/cf-adapter/src/connection-do.ts +85 -0
- package/packages/cf-adapter/src/env.ts +27 -1
- package/packages/cf-adapter/src/index.ts +2 -1
- package/packages/cf-adapter/tests/cf-runtime.test.ts +91 -0
- package/packages/cf-adapter/tests/connection-do.test.ts +40 -0
- package/packages/cf-adapter/tests/worker/main.ts +2 -1
- package/packages/cf-adapter/tests/worker/stubs/channel-stub.ts +7 -1
- package/packages/cf-adapter/wrangler.test.toml +10 -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 +3 -2
- package/packages/irc-core/src/case-fold.ts +64 -0
- package/packages/irc-core/src/commands/index.ts +2 -0
- package/packages/irc-core/src/commands/invite.ts +6 -9
- 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/registration.ts +8 -7
- package/packages/irc-core/src/commands/tagmsg.ts +205 -0
- package/packages/irc-core/src/config.ts +26 -3
- package/packages/irc-core/src/index.ts +2 -0
- package/packages/irc-core/src/ports.ts +68 -4
- package/packages/irc-core/src/types.ts +22 -0
- package/packages/irc-core/tests/account-store.test.ts +88 -0
- package/packages/irc-core/tests/case-fold.test.ts +80 -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/registration.test.ts +106 -14
- package/packages/irc-core/tests/commands/tagmsg.test.ts +688 -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/config.test.ts +75 -0
- package/packages/irc-server/package.json +1 -1
- package/packages/irc-server/src/actor.ts +24 -0
- package/packages/irc-server/src/routing.ts +9 -0
- package/packages/irc-server/tests/actor.test.ts +220 -1
- package/packages/irc-server/tests/routing.test.ts +3 -0
- package/packages/irc-test-support/package.json +1 -1
- package/packages/irc-test-support/src/in-memory-harness.ts +5 -4
- package/pnpm-workspace.yaml +1 -0
- package/tools/seed-aws-accounts.ts +80 -0
- 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/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,107 +0,0 @@
|
|
|
1
|
-
package webircgateway
|
|
2
|
-
|
|
3
|
-
import (
|
|
4
|
-
"net"
|
|
5
|
-
"net/http"
|
|
6
|
-
"strings"
|
|
7
|
-
|
|
8
|
-
"github.com/gorilla/websocket"
|
|
9
|
-
"github.com/igm/sockjs-go/v3/sockjs"
|
|
10
|
-
)
|
|
11
|
-
|
|
12
|
-
type TransportSockjs struct {
|
|
13
|
-
gateway *Gateway
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
func (t *TransportSockjs) Init(g *Gateway) {
|
|
17
|
-
t.gateway = g
|
|
18
|
-
sockjsOptions := sockjs.DefaultOptions
|
|
19
|
-
sockjsOptions.WebsocketUpgrader = &websocket.Upgrader{
|
|
20
|
-
// Origin is checked within the session handler
|
|
21
|
-
CheckOrigin: func(_ *http.Request) bool { return true },
|
|
22
|
-
}
|
|
23
|
-
sockjsHandler := sockjs.NewHandler("/webirc/sockjs", sockjsOptions, t.sessionHandler)
|
|
24
|
-
t.gateway.HttpRouter.Handle("/webirc/sockjs/", sockjsHandler)
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
func (t *TransportSockjs) sessionHandler(session sockjs.Session) {
|
|
28
|
-
client := t.gateway.NewClient()
|
|
29
|
-
|
|
30
|
-
originHeader := strings.ToLower(session.Request().Header.Get("Origin"))
|
|
31
|
-
if !t.gateway.IsClientOriginAllowed(originHeader) {
|
|
32
|
-
client.Log(2, "Origin %s not allowed. Closing connection", originHeader)
|
|
33
|
-
session.Close(0, "Origin not allowed")
|
|
34
|
-
return
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
client.RemoteAddr = t.gateway.GetRemoteAddressFromRequest(session.Request()).String()
|
|
38
|
-
|
|
39
|
-
clientHostnames, err := net.LookupAddr(client.RemoteAddr)
|
|
40
|
-
if err != nil {
|
|
41
|
-
client.RemoteHostname = client.RemoteAddr
|
|
42
|
-
} else {
|
|
43
|
-
// FQDNs include a . at the end. Strip it out
|
|
44
|
-
potentialHostname := strings.Trim(clientHostnames[0], ".")
|
|
45
|
-
|
|
46
|
-
// Must check that the resolved hostname also resolves back to the users IP
|
|
47
|
-
addr, err := net.LookupIP(potentialHostname)
|
|
48
|
-
if err == nil && len(addr) == 1 && addr[0].String() == client.RemoteAddr {
|
|
49
|
-
client.RemoteHostname = potentialHostname
|
|
50
|
-
} else {
|
|
51
|
-
client.RemoteHostname = client.RemoteAddr
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
if t.gateway.isRequestSecure(session.Request()) {
|
|
56
|
-
client.Tags["secure"] = ""
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// This doesn't make sense to have since the remote port may change between requests. Only
|
|
60
|
-
// here for testing purposes for now.
|
|
61
|
-
_, remoteAddrPort, _ := net.SplitHostPort(session.Request().RemoteAddr)
|
|
62
|
-
client.Tags["remote-port"] = remoteAddrPort
|
|
63
|
-
|
|
64
|
-
client.Log(2, "New sockjs client on %s from %s %s", session.Request().Host, client.RemoteAddr, client.RemoteHostname)
|
|
65
|
-
client.Ready()
|
|
66
|
-
|
|
67
|
-
// Read from sockjs
|
|
68
|
-
go func() {
|
|
69
|
-
for {
|
|
70
|
-
msg, err := session.Recv()
|
|
71
|
-
if err == nil && len(msg) > 0 {
|
|
72
|
-
client.Log(1, "client->: %s", msg)
|
|
73
|
-
select {
|
|
74
|
-
case client.Recv <- msg:
|
|
75
|
-
default:
|
|
76
|
-
client.Log(3, "Recv queue full. Dropping data")
|
|
77
|
-
// TODO: Should this really just drop the data or close the connection?
|
|
78
|
-
}
|
|
79
|
-
} else if err != nil {
|
|
80
|
-
client.Log(1, "sockjs connection closed (%s)", err.Error())
|
|
81
|
-
break
|
|
82
|
-
} else if len(msg) == 0 {
|
|
83
|
-
client.Log(1, "Got 0 bytes from websocket")
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
close(client.Recv)
|
|
88
|
-
}()
|
|
89
|
-
|
|
90
|
-
// Process signals for the client
|
|
91
|
-
for {
|
|
92
|
-
signal, ok := <-client.Signals
|
|
93
|
-
if !ok {
|
|
94
|
-
break
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
if signal[0] == "data" {
|
|
98
|
-
line := strings.Trim(signal[1], "\r\n")
|
|
99
|
-
client.Log(1, "->ws: %s", line)
|
|
100
|
-
session.Send(line)
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
if signal[0] == "state" && signal[1] == "closed" {
|
|
104
|
-
session.Close(0, "Closed")
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
package webircgateway
|
|
2
|
-
|
|
3
|
-
import (
|
|
4
|
-
"bufio"
|
|
5
|
-
"net"
|
|
6
|
-
"strings"
|
|
7
|
-
"sync"
|
|
8
|
-
)
|
|
9
|
-
|
|
10
|
-
type TransportTcp struct {
|
|
11
|
-
gateway *Gateway
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
func (t *TransportTcp) Init(g *Gateway) {
|
|
15
|
-
t.gateway = g
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
func (t *TransportTcp) Start(lAddr string) {
|
|
19
|
-
l, err := net.Listen("tcp", lAddr)
|
|
20
|
-
if err != nil {
|
|
21
|
-
t.gateway.Log(3, "TCP error listening: "+err.Error())
|
|
22
|
-
return
|
|
23
|
-
}
|
|
24
|
-
// Close the listener when the application closes.
|
|
25
|
-
defer l.Close()
|
|
26
|
-
t.gateway.Log(2, "TCP listening on "+lAddr)
|
|
27
|
-
for {
|
|
28
|
-
// Listen for an incoming connection.
|
|
29
|
-
conn, err := l.Accept()
|
|
30
|
-
if err != nil {
|
|
31
|
-
t.gateway.Log(3, "TCP error accepting: "+err.Error())
|
|
32
|
-
break
|
|
33
|
-
}
|
|
34
|
-
// Handle connections in a new goroutine.
|
|
35
|
-
go t.handleConn(conn)
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
func (t *TransportTcp) handleConn(conn net.Conn) {
|
|
40
|
-
client := t.gateway.NewClient()
|
|
41
|
-
|
|
42
|
-
client.RemoteAddr = conn.RemoteAddr().String()
|
|
43
|
-
|
|
44
|
-
clientHostnames, err := net.LookupAddr(client.RemoteAddr)
|
|
45
|
-
if err != nil {
|
|
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
|
-
_, remoteAddrPort, _ := net.SplitHostPort(conn.RemoteAddr().String())
|
|
61
|
-
client.Tags["remote-port"] = remoteAddrPort
|
|
62
|
-
|
|
63
|
-
client.Log(2, "New tcp client on %s from %s %s", conn.LocalAddr().String(), client.RemoteAddr, client.RemoteHostname)
|
|
64
|
-
client.Ready()
|
|
65
|
-
|
|
66
|
-
// We wait until the client send queue has been drained
|
|
67
|
-
var sendDrained sync.WaitGroup
|
|
68
|
-
sendDrained.Add(1)
|
|
69
|
-
|
|
70
|
-
// Read from TCP
|
|
71
|
-
go func() {
|
|
72
|
-
reader := bufio.NewReader(conn)
|
|
73
|
-
for {
|
|
74
|
-
data, err := reader.ReadString('\n')
|
|
75
|
-
if err == nil {
|
|
76
|
-
message := strings.TrimRight(data, "\r\n")
|
|
77
|
-
client.Log(1, "client->: %s", message)
|
|
78
|
-
select {
|
|
79
|
-
case client.Recv <- message:
|
|
80
|
-
default:
|
|
81
|
-
client.Log(3, "Recv queue full. Dropping data")
|
|
82
|
-
// TODO: Should this really just drop the data or close the connection?
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
} else {
|
|
86
|
-
client.Log(1, "TCP connection closed (%s)", err.Error())
|
|
87
|
-
break
|
|
88
|
-
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
close(client.Recv)
|
|
93
|
-
}()
|
|
94
|
-
|
|
95
|
-
// Process signals for the client
|
|
96
|
-
for {
|
|
97
|
-
signal, ok := <-client.Signals
|
|
98
|
-
if !ok {
|
|
99
|
-
sendDrained.Done()
|
|
100
|
-
break
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
if signal[0] == "data" {
|
|
104
|
-
//line := strings.Trim(signal[1], "\r\n")
|
|
105
|
-
line := signal[1] + "\n"
|
|
106
|
-
client.Log(1, "->tcp: %s", signal[1])
|
|
107
|
-
conn.Write([]byte(line))
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
sendDrained.Wait()
|
|
112
|
-
conn.Close()
|
|
113
|
-
}
|
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
package webircgateway
|
|
2
|
-
|
|
3
|
-
import (
|
|
4
|
-
"fmt"
|
|
5
|
-
"net"
|
|
6
|
-
"net/http"
|
|
7
|
-
"strings"
|
|
8
|
-
"sync"
|
|
9
|
-
|
|
10
|
-
"golang.org/x/net/websocket"
|
|
11
|
-
)
|
|
12
|
-
|
|
13
|
-
type TransportWebsocket struct {
|
|
14
|
-
gateway *Gateway
|
|
15
|
-
wsServer *websocket.Server
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
func (t *TransportWebsocket) Init(g *Gateway) {
|
|
19
|
-
t.gateway = g
|
|
20
|
-
t.wsServer = &websocket.Server{Handler: t.websocketHandler, Handshake: t.checkOrigin}
|
|
21
|
-
t.gateway.HttpRouter.Handle("/webirc/websocket/", t.wsServer)
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
func (t *TransportWebsocket) checkOrigin(config *websocket.Config, req *http.Request) (err error) {
|
|
25
|
-
config.Origin, err = websocket.Origin(config, req)
|
|
26
|
-
|
|
27
|
-
var origin string
|
|
28
|
-
if config.Origin != nil {
|
|
29
|
-
origin = config.Origin.String()
|
|
30
|
-
} else {
|
|
31
|
-
origin = ""
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
if !t.gateway.IsClientOriginAllowed(origin) {
|
|
35
|
-
err = fmt.Errorf("Origin %#v not allowed", origin)
|
|
36
|
-
t.gateway.Log(2, "%s. Closing connection", err)
|
|
37
|
-
return err
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
return err
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
func (t *TransportWebsocket) websocketHandler(ws *websocket.Conn) {
|
|
44
|
-
client := t.gateway.NewClient()
|
|
45
|
-
|
|
46
|
-
client.RemoteAddr = t.gateway.GetRemoteAddressFromRequest(ws.Request()).String()
|
|
47
|
-
|
|
48
|
-
clientHostnames, err := net.LookupAddr(client.RemoteAddr)
|
|
49
|
-
if err != nil {
|
|
50
|
-
client.RemoteHostname = client.RemoteAddr
|
|
51
|
-
} else {
|
|
52
|
-
// FQDNs include a . at the end. Strip it out
|
|
53
|
-
potentialHostname := strings.Trim(clientHostnames[0], ".")
|
|
54
|
-
|
|
55
|
-
// Must check that the resolved hostname also resolves back to the users IP
|
|
56
|
-
addr, err := net.LookupIP(potentialHostname)
|
|
57
|
-
if err == nil && len(addr) == 1 && addr[0].String() == client.RemoteAddr {
|
|
58
|
-
client.RemoteHostname = potentialHostname
|
|
59
|
-
} else {
|
|
60
|
-
client.RemoteHostname = client.RemoteAddr
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
if t.gateway.isRequestSecure(ws.Request()) {
|
|
65
|
-
client.Tags["secure"] = ""
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
_, remoteAddrPort, _ := net.SplitHostPort(ws.Request().RemoteAddr)
|
|
69
|
-
client.Tags["remote-port"] = remoteAddrPort
|
|
70
|
-
|
|
71
|
-
client.Log(2, "New websocket client on %s from %s %s", ws.Request().Host, client.RemoteAddr, client.RemoteHostname)
|
|
72
|
-
client.Ready()
|
|
73
|
-
|
|
74
|
-
// We wait until the client send queue has been drained
|
|
75
|
-
var sendDrained sync.WaitGroup
|
|
76
|
-
sendDrained.Add(1)
|
|
77
|
-
|
|
78
|
-
// Read from websocket
|
|
79
|
-
go func() {
|
|
80
|
-
for {
|
|
81
|
-
r := make([]byte, 1024)
|
|
82
|
-
len, err := ws.Read(r)
|
|
83
|
-
if err == nil && len > 0 {
|
|
84
|
-
message := string(r[:len])
|
|
85
|
-
client.Log(1, "client->: %s", message)
|
|
86
|
-
select {
|
|
87
|
-
case client.Recv <- message:
|
|
88
|
-
default:
|
|
89
|
-
client.Log(3, "Recv queue full. Dropping data")
|
|
90
|
-
// TODO: Should this really just drop the data or close the connection?
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
} else if err != nil {
|
|
94
|
-
client.Log(1, "Websocket connection closed (%s)", err.Error())
|
|
95
|
-
break
|
|
96
|
-
|
|
97
|
-
} else if len == 0 {
|
|
98
|
-
client.Log(1, "Got 0 bytes from websocket")
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
close(client.Recv)
|
|
103
|
-
}()
|
|
104
|
-
|
|
105
|
-
// Process signals for the client
|
|
106
|
-
for {
|
|
107
|
-
signal, ok := <-client.Signals
|
|
108
|
-
if !ok {
|
|
109
|
-
sendDrained.Done()
|
|
110
|
-
break
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
if signal[0] == "data" {
|
|
114
|
-
line := strings.Trim(signal[1], "\r\n")
|
|
115
|
-
client.Log(1, "->ws: %s", line)
|
|
116
|
-
ws.Write([]byte(line))
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
if signal[0] == "state" && signal[1] == "closed" {
|
|
120
|
-
ws.Close()
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
sendDrained.Wait()
|
|
125
|
-
ws.Close()
|
|
126
|
-
}
|
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
package webircgateway
|
|
2
|
-
|
|
3
|
-
import (
|
|
4
|
-
"context"
|
|
5
|
-
"fmt"
|
|
6
|
-
"net"
|
|
7
|
-
"strings"
|
|
8
|
-
"unicode/utf8"
|
|
9
|
-
|
|
10
|
-
"golang.org/x/net/html/charset"
|
|
11
|
-
"golang.org/x/time/rate"
|
|
12
|
-
)
|
|
13
|
-
|
|
14
|
-
var privateIPBlocks []*net.IPNet
|
|
15
|
-
|
|
16
|
-
func init() {
|
|
17
|
-
for _, cidr := range []string{
|
|
18
|
-
"127.0.0.0/8", // IPv4 loopback
|
|
19
|
-
"10.0.0.0/8", // RFC1918
|
|
20
|
-
"172.16.0.0/12", // RFC1918
|
|
21
|
-
"192.168.0.0/16", // RFC1918
|
|
22
|
-
"::1/128", // IPv6 loopback
|
|
23
|
-
"fe80::/10", // IPv6 link-local
|
|
24
|
-
} {
|
|
25
|
-
_, block, _ := net.ParseCIDR(cidr)
|
|
26
|
-
privateIPBlocks = append(privateIPBlocks, block)
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
func isPrivateIP(ip net.IP) bool {
|
|
31
|
-
for _, block := range privateIPBlocks {
|
|
32
|
-
if block.Contains(ip) {
|
|
33
|
-
return true
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
return false
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// Username / realname / webirc hostname can all have configurable replacements
|
|
40
|
-
func makeClientReplacements(format string, client *Client) string {
|
|
41
|
-
ret := format
|
|
42
|
-
ret = strings.Replace(ret, "%a", client.RemoteAddr, -1)
|
|
43
|
-
ret = strings.Replace(ret, "%i", Ipv4ToHex(client.RemoteAddr), -1)
|
|
44
|
-
ret = strings.Replace(ret, "%h", client.RemoteHostname, -1)
|
|
45
|
-
ret = strings.Replace(ret, "%n", client.IrcState.Nick, -1)
|
|
46
|
-
return ret
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
func Ipv4ToHex(ip string) string {
|
|
50
|
-
var ipParts [4]int
|
|
51
|
-
fmt.Sscanf(ip, "%d.%d.%d.%d", &ipParts[0], &ipParts[1], &ipParts[2], &ipParts[3])
|
|
52
|
-
ipHex := fmt.Sprintf("%02x%02x%02x%02x", ipParts[0], ipParts[1], ipParts[2], ipParts[3])
|
|
53
|
-
return ipHex
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
func ensureUtf8(s string, fromEncoding string) string {
|
|
57
|
-
if utf8.ValidString(s) {
|
|
58
|
-
return s
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
encoding, encErr := charset.Lookup(fromEncoding)
|
|
62
|
-
if encoding == nil {
|
|
63
|
-
println("encErr:", encErr)
|
|
64
|
-
return ""
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
d := encoding.NewDecoder()
|
|
68
|
-
s2, _ := d.String(s)
|
|
69
|
-
return s2
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
func utf8ToOther(s string, toEncoding string) string {
|
|
73
|
-
if toEncoding == "UTF-8" && utf8.ValidString(s) {
|
|
74
|
-
return s
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
encoding, _ := charset.Lookup(toEncoding)
|
|
78
|
-
if encoding == nil {
|
|
79
|
-
return ""
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
e := encoding.NewEncoder()
|
|
83
|
-
s2, _ := e.String(s)
|
|
84
|
-
return s2
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
func containsOneOf(s string, substrs []string) bool {
|
|
88
|
-
for _, substr := range substrs {
|
|
89
|
-
if strings.Contains(s, substr) {
|
|
90
|
-
return true
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
return false
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
func stringInSlice(s string, slice []string) bool {
|
|
98
|
-
for _, v := range slice {
|
|
99
|
-
if v == s {
|
|
100
|
-
return true
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
return false
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
func stringInSliceOrDefault(s, def string, validStrings []string) string {
|
|
107
|
-
if stringInSlice(s, validStrings) {
|
|
108
|
-
return s
|
|
109
|
-
}
|
|
110
|
-
return def
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
type ThrottledStringChannel struct {
|
|
114
|
-
in chan string
|
|
115
|
-
Input chan<- string
|
|
116
|
-
out chan string
|
|
117
|
-
Output <-chan string
|
|
118
|
-
*rate.Limiter
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
func NewThrottledStringChannel(wrappedChan chan string, limiter *rate.Limiter) *ThrottledStringChannel {
|
|
122
|
-
out := make(chan string, 50)
|
|
123
|
-
|
|
124
|
-
c := &ThrottledStringChannel{
|
|
125
|
-
in: wrappedChan,
|
|
126
|
-
Input: wrappedChan,
|
|
127
|
-
out: out,
|
|
128
|
-
Output: out,
|
|
129
|
-
Limiter: limiter,
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
go c.run()
|
|
133
|
-
|
|
134
|
-
return c
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
func (c *ThrottledStringChannel) run() {
|
|
138
|
-
for msg := range c.in {
|
|
139
|
-
// start := time.Now()
|
|
140
|
-
c.Wait(context.Background())
|
|
141
|
-
c.out <- msg
|
|
142
|
-
// elapsed := time.Since(start)
|
|
143
|
-
// fmt.Printf("waited %v to send %v\n", elapsed, msg)
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
close(c.out)
|
|
147
|
-
}
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
package main
|
|
2
|
-
|
|
3
|
-
import (
|
|
4
|
-
"fmt"
|
|
5
|
-
"math"
|
|
6
|
-
"os"
|
|
7
|
-
"runtime"
|
|
8
|
-
"sync"
|
|
9
|
-
"time"
|
|
10
|
-
|
|
11
|
-
"github.com/kiwiirc/webircgateway/pkg/webircgateway"
|
|
12
|
-
)
|
|
13
|
-
|
|
14
|
-
func Start(gateway *webircgateway.Gateway, pluginsQuit *sync.WaitGroup) {
|
|
15
|
-
gateway.Log(2, "Stats reporting plugin loading")
|
|
16
|
-
go reportUsage(gateway)
|
|
17
|
-
|
|
18
|
-
pluginsQuit.Done()
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
func reportUsage(gateway *webircgateway.Gateway) {
|
|
22
|
-
started := time.Now()
|
|
23
|
-
|
|
24
|
-
out := func(line string) {
|
|
25
|
-
file, _ := os.OpenFile("stats_"+fmt.Sprintf("%v", started.Unix())+".csv",
|
|
26
|
-
os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
|
27
|
-
file.WriteString(line)
|
|
28
|
-
file.Close()
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
out("time,rss,heapinuse,heapalloc,numroutines,numclients\n")
|
|
32
|
-
|
|
33
|
-
for {
|
|
34
|
-
time.Sleep(time.Second * 5)
|
|
35
|
-
|
|
36
|
-
numClients := gateway.Clients.Count()
|
|
37
|
-
mem := &runtime.MemStats{}
|
|
38
|
-
runtime.ReadMemStats(mem)
|
|
39
|
-
|
|
40
|
-
line := fmt.Sprintf(
|
|
41
|
-
"%v,%v,%v,%v,%v,%v\n",
|
|
42
|
-
math.Round(time.Now().Sub(started).Seconds()),
|
|
43
|
-
mem.Sys/1024,
|
|
44
|
-
mem.HeapInuse/1024,
|
|
45
|
-
mem.HeapAlloc/1024,
|
|
46
|
-
runtime.NumGoroutine(),
|
|
47
|
-
numClients,
|
|
48
|
-
)
|
|
49
|
-
|
|
50
|
-
out(line)
|
|
51
|
-
}
|
|
52
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
checks = ["all", "-ST1005"]
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
-
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
3
|
-
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="583px" height="161px" viewBox="-0.5 -0.5 583 161" style="background-color: rgb(255, 255, 255);"><defs/><g><path d="M 372 80 L 417 80 L 417 40 L 455.63 40" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 460.88 40 L 453.88 43.5 L 455.63 40 L 453.88 36.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><path d="M 372 80 L 455.63 80" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 460.88 80 L 453.88 83.5 L 455.63 80 L 453.88 76.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><path d="M 372 80 L 417 80 L 417 120 L 455.63 120" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 460.88 120 L 453.88 123.5 L 455.63 120 L 453.88 116.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><path d="M 173.43 -0.66 L 372.31 -0.66 L 373.45 160.81 L 170.66 161.76" fill="#ffffff" stroke="none" pointer-events="all"/><path d="M 173.37 -1.8 C 241.48 0.42 311.12 -0.95 370.41 0.06 M 172.82 -0.12 C 219.08 -0.19 266 -0.93 371.48 0.44 M 370.02 -0.51 C 372.54 41.23 371.58 83.32 370.7 161.2 M 371.85 0.91 C 373.44 40.44 372.66 83.06 371.45 160.63 M 372.72 158.75 C 297.02 161.45 222.98 160.05 170.89 158.36 M 372.47 160.27 C 297.32 159.9 223.07 159.91 172.81 160.39 M 171.63 160.81 C 169.73 118.56 171.97 74.1 171.7 -1.35 M 172.26 159.53 C 172.22 100.75 171.74 39.86 172.59 0.3" fill="none" stroke="#696969" stroke-linejoin="round" stroke-linecap="round" stroke-miterlimit="10" pointer-events="all"/><path d="M 80 40 L 165.63 40" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 170.88 40 L 163.88 43.5 L 165.63 40 L 163.88 36.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><rect x="0" y="30" width="80" height="20" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 78px; height: 1px; padding-top: 40px; margin-left: 2px;"><div style="box-sizing: border-box; font-size: 0; text-align: left; "><div style="display: inline-block; font-size: 14px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">TCP socket</div></div></div></foreignObject><text x="2" y="44" fill="#000000" font-family="Helvetica" font-size="14px">TCP socket</text></switch></g><path d="M 80 80 L 165.63 80" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 170.88 80 L 163.88 83.5 L 165.63 80 L 163.88 76.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><rect x="0" y="70" width="80" height="20" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 78px; height: 1px; padding-top: 80px; margin-left: 2px;"><div style="box-sizing: border-box; font-size: 0; text-align: left; "><div style="display: inline-block; font-size: 14px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Websocket</div></div></div></foreignObject><text x="2" y="84" fill="#000000" font-family="Helvetica" font-size="14px">Websocket</text></switch></g><path d="M 80 120 L 165.63 120" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 170.88 120 L 163.88 123.5 L 165.63 120 L 163.88 116.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><rect x="0" y="110" width="80" height="20" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 78px; height: 1px; padding-top: 120px; margin-left: 2px;"><div style="box-sizing: border-box; font-size: 0; text-align: left; "><div style="display: inline-block; font-size: 14px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Sockjs</div></div></div></foreignObject><text x="2" y="124" fill="#000000" font-family="Helvetica" font-size="14px">Sockjs</text></switch></g><rect x="172" y="10" width="200" height="20" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 198px; height: 1px; padding-top: 20px; margin-left: 173px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 16px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; font-weight: bold; white-space: normal; word-wrap: normal; ">webircgateway</div></div></div></foreignObject><text x="272" y="25" fill="#000000" font-family="Helvetica" font-size="16px" text-anchor="middle" font-weight="bold">webircgateway</text></switch></g><rect x="172" y="42" width="200" height="90" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 202px; height: 1px; padding-top: 47px; margin-left: 171px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 14px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Captcha<br style="font-size: 14px" />DNSBL<br style="font-size: 14px" />UTF8 <> re-encoding<br style="font-size: 14px" />WEBIRC<br style="font-size: 14px" />ident/host/nick rewriting<br />Lets Encrypt certificates</div></div></div></foreignObject><text x="272" y="61" fill="#000000" font-family="Helvetica" font-size="14px" text-anchor="middle">Captcha...</text></switch></g><rect x="462" y="30" width="120" height="20" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 118px; height: 1px; padding-top: 40px; margin-left: 464px;"><div style="box-sizing: border-box; font-size: 0; text-align: left; "><div style="display: inline-block; font-size: 14px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">irc1.example.com</div></div></div></foreignObject><text x="464" y="44" fill="#000000" font-family="Helvetica" font-size="14px">irc1.example.com</text></switch></g><rect x="462" y="70" width="120" height="20" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 118px; height: 1px; padding-top: 80px; margin-left: 464px;"><div style="box-sizing: border-box; font-size: 0; text-align: left; "><div style="display: inline-block; font-size: 14px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">irc2.example.com</div></div></div></foreignObject><text x="464" y="84" fill="#000000" font-family="Helvetica" font-size="14px">irc2.example.com</text></switch></g><rect x="462" y="110" width="120" height="20" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 118px; height: 1px; padding-top: 120px; margin-left: 464px;"><div style="box-sizing: border-box; font-size: 0; text-align: left; "><div style="display: inline-block; font-size: 14px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">irc3.example.com</div></div></div></foreignObject><text x="464" y="124" fill="#000000" font-family="Helvetica" font-size="14px">irc3.example.com</text></switch></g></g><switch><g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/><a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank"><text text-anchor="middle" font-size="10px" x="50%" y="100%">Viewer does not support full SVG 1.1</text></a></switch></svg>
|