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.
Files changed (221) hide show
  1. package/.github/workflows/ci.yml +2 -2
  2. package/.github/workflows/deploy-aws.yml +1 -3
  3. package/.github/workflows/deploy-cf-tcp.yml +87 -0
  4. package/.github/workflows/deploy-cf.yml +1 -1
  5. package/.node-version +1 -0
  6. package/.nvmrc +1 -0
  7. package/CHANGELOG.md +258 -18
  8. package/README.md +72 -30
  9. package/apps/aws-stack/README.md +2 -2
  10. package/apps/aws-stack/bin/aws.ts +7 -0
  11. package/apps/aws-stack/package.json +4 -4
  12. package/apps/aws-stack/src/aws-stack.ts +118 -6
  13. package/apps/aws-stack/tests/stack.test.ts +98 -3
  14. package/apps/cf-tcp-container/Dockerfile +69 -0
  15. package/apps/cf-tcp-container/package.json +34 -0
  16. package/apps/cf-tcp-container/src/config-loader.ts +145 -0
  17. package/apps/cf-tcp-container/src/container-do.ts +38 -0
  18. package/apps/cf-tcp-container/src/container-server.ts +363 -0
  19. package/apps/cf-tcp-container/src/main.ts +77 -0
  20. package/apps/cf-tcp-container/src/persistence.ts +144 -0
  21. package/apps/cf-tcp-container/src/worker.ts +41 -0
  22. package/apps/cf-tcp-container/terraform/provider.tf +24 -0
  23. package/apps/cf-tcp-container/terraform/spectrum.tf +81 -0
  24. package/apps/cf-tcp-container/tests/config-loader.test.ts +217 -0
  25. package/apps/cf-tcp-container/tests/container-server.test.ts +465 -0
  26. package/apps/cf-tcp-container/tests/persistence.test.ts +227 -0
  27. package/apps/cf-tcp-container/tests/tls-e2e.test.ts +275 -0
  28. package/apps/cf-tcp-container/tsconfig.build.json +17 -0
  29. package/apps/cf-tcp-container/tsconfig.test.json +15 -0
  30. package/apps/cf-tcp-container/vitest.config.ts +26 -0
  31. package/apps/cf-tcp-container/wrangler.toml +63 -0
  32. package/apps/cf-worker/package.json +1 -1
  33. package/apps/cf-worker/scripts/smoke.mjs +0 -0
  34. package/apps/cf-worker/src/worker.ts +8 -2
  35. package/apps/cf-worker/tests/smoke.test.ts +1 -0
  36. package/apps/cf-worker/wrangler.test.toml +11 -1
  37. package/apps/cf-worker/wrangler.toml +69 -19
  38. package/apps/local-cli/package.json +1 -1
  39. package/apps/local-cli/src/config-loader.ts +11 -0
  40. package/apps/local-cli/src/main.ts +1 -1
  41. package/apps/local-cli/src/server.ts +46 -3
  42. package/apps/local-cli/tests/e2e.test.ts +52 -0
  43. package/package.json +19 -16
  44. package/packages/aws-adapter/package.json +3 -3
  45. package/packages/aws-adapter/src/account-store.ts +121 -0
  46. package/packages/aws-adapter/src/admission.ts +74 -0
  47. package/packages/aws-adapter/src/aws-runtime.ts +124 -34
  48. package/packages/aws-adapter/src/cdk-table-defs.ts +5 -1
  49. package/packages/aws-adapter/src/config-loader.ts +62 -0
  50. package/packages/aws-adapter/src/dynamo-account-store.ts +95 -0
  51. package/packages/aws-adapter/src/handlers/connect.ts +64 -8
  52. package/packages/aws-adapter/src/handlers/default.ts +43 -2
  53. package/packages/aws-adapter/src/handlers/disconnect.ts +27 -8
  54. package/packages/aws-adapter/src/handlers/index.ts +120 -9
  55. package/packages/aws-adapter/src/handlers/nlb-stream.ts +481 -0
  56. package/packages/aws-adapter/src/handlers/ping-checker.ts +1 -1
  57. package/packages/aws-adapter/src/index.ts +13 -0
  58. package/packages/aws-adapter/tests/account-store-dynamo.test.ts +182 -0
  59. package/packages/aws-adapter/tests/account-store.test.ts +279 -0
  60. package/packages/aws-adapter/tests/admission.test.ts +70 -0
  61. package/packages/aws-adapter/tests/aws-harness.ts +13 -1
  62. package/packages/aws-adapter/tests/config-loader.test.ts +75 -0
  63. package/packages/aws-adapter/tests/connect.test.ts +78 -0
  64. package/packages/aws-adapter/tests/disconnect-fanout.test.ts +343 -0
  65. package/packages/aws-adapter/tests/gone-exception.test.ts +31 -26
  66. package/packages/aws-adapter/tests/handlers.test.ts +194 -47
  67. package/packages/aws-adapter/tests/nlb-stream.test.ts +478 -0
  68. package/packages/aws-adapter/tests/ping-checker.test.ts +34 -29
  69. package/packages/aws-adapter/tests/sweeper.test.ts +25 -18
  70. package/packages/aws-adapter/tests/transactions.test.ts +25 -20
  71. package/packages/cf-adapter/package.json +1 -1
  72. package/packages/cf-adapter/src/cf-runtime.ts +40 -22
  73. package/packages/cf-adapter/src/channel-do.ts +40 -1
  74. package/packages/cf-adapter/src/channel-registry-do.ts +58 -0
  75. package/packages/cf-adapter/src/config-loader.ts +62 -0
  76. package/packages/cf-adapter/src/connection-do.ts +181 -31
  77. package/packages/cf-adapter/src/d1-account-store.ts +198 -0
  78. package/packages/cf-adapter/src/env.ts +58 -8
  79. package/packages/cf-adapter/src/index.ts +11 -9
  80. package/packages/cf-adapter/tests/cf-harness.ts +11 -1
  81. package/packages/cf-adapter/tests/cf-integration.test.ts +2 -1
  82. package/packages/cf-adapter/tests/cf-runtime.test.ts +91 -0
  83. package/packages/cf-adapter/tests/config-loader.test.ts +22 -0
  84. package/packages/cf-adapter/tests/connection-do-channel-registration.test.ts +37 -0
  85. package/packages/cf-adapter/tests/connection-do-no-batching-reservation.test.ts +52 -0
  86. package/packages/cf-adapter/tests/connection-do-sasl-d1.test.ts +166 -0
  87. package/packages/cf-adapter/tests/connection-do.test.ts +40 -0
  88. package/packages/cf-adapter/tests/d1-account-store.test.ts +226 -0
  89. package/packages/cf-adapter/tests/raw-modules.d.ts +11 -0
  90. package/packages/cf-adapter/tests/worker/main.ts +9 -1
  91. package/packages/cf-adapter/tests/worker/stubs/channel-stub.ts +7 -1
  92. package/packages/cf-adapter/wrangler.test.toml +18 -1
  93. package/packages/in-memory-runtime/package.json +1 -1
  94. package/packages/in-memory-runtime/src/in-memory-runtime.ts +14 -28
  95. package/packages/in-memory-runtime/tests/in-memory-runtime.test.ts +19 -0
  96. package/packages/irc-core/package.json +8 -2
  97. package/packages/irc-core/scripts/generate-build-info.mjs +31 -0
  98. package/packages/irc-core/src/caps/capabilities.ts +23 -2
  99. package/packages/irc-core/src/case-fold.ts +64 -0
  100. package/packages/irc-core/src/cloak.ts +1 -1
  101. package/packages/irc-core/src/commands/cap.ts +8 -1
  102. package/packages/irc-core/src/commands/index.ts +11 -0
  103. package/packages/irc-core/src/commands/invite.ts +6 -9
  104. package/packages/irc-core/src/commands/ison.ts +61 -0
  105. package/packages/irc-core/src/commands/isupport.ts +1 -1
  106. package/packages/irc-core/src/commands/join.ts +4 -3
  107. package/packages/irc-core/src/commands/kick.ts +4 -11
  108. package/packages/irc-core/src/commands/list.ts +5 -4
  109. package/packages/irc-core/src/commands/mode.ts +5 -9
  110. package/packages/irc-core/src/commands/motd-lines.ts +127 -0
  111. package/packages/irc-core/src/commands/motd.ts +6 -110
  112. package/packages/irc-core/src/commands/oper.ts +151 -0
  113. package/packages/irc-core/src/commands/quit.ts +12 -0
  114. package/packages/irc-core/src/commands/registration.ts +26 -19
  115. package/packages/irc-core/src/commands/sasl.ts +72 -9
  116. package/packages/irc-core/src/commands/server-info.ts +129 -0
  117. package/packages/irc-core/src/commands/tagmsg.ts +205 -0
  118. package/packages/irc-core/src/commands/userhost.ts +84 -0
  119. package/packages/irc-core/src/commands/whowas.ts +113 -0
  120. package/packages/irc-core/src/config.ts +84 -3
  121. package/packages/irc-core/src/credential-hashing.ts +124 -0
  122. package/packages/irc-core/src/effects.ts +6 -29
  123. package/packages/irc-core/src/index.ts +3 -0
  124. package/packages/irc-core/src/ports.ts +240 -7
  125. package/packages/irc-core/src/protocol/numerics.ts +14 -8
  126. package/packages/irc-core/src/types.ts +60 -1
  127. package/packages/irc-core/tests/account-store.test.ts +131 -0
  128. package/packages/irc-core/tests/caps/capabilities.test.ts +4 -3
  129. package/packages/irc-core/tests/case-fold.test.ts +80 -0
  130. package/packages/irc-core/tests/commands/cap.test.ts +33 -1
  131. package/packages/irc-core/tests/commands/ison.test.ts +166 -0
  132. package/packages/irc-core/tests/commands/kick.test.ts +15 -0
  133. package/packages/irc-core/tests/commands/oper.test.ts +257 -0
  134. package/packages/irc-core/tests/commands/quit.test.ts +69 -2
  135. package/packages/irc-core/tests/commands/registration.test.ts +256 -19
  136. package/packages/irc-core/tests/commands/sasl.test.ts +118 -10
  137. package/packages/irc-core/tests/commands/server-info.test.ts +274 -0
  138. package/packages/irc-core/tests/commands/tagmsg.test.ts +662 -0
  139. package/packages/irc-core/tests/commands/userhost.test.ts +264 -0
  140. package/packages/irc-core/tests/commands/who.test.ts +22 -4
  141. package/packages/irc-core/tests/commands/whois.test.ts +8 -1
  142. package/packages/irc-core/tests/commands/whowas.test.ts +312 -0
  143. package/packages/irc-core/tests/config.test.ts +170 -1
  144. package/packages/irc-core/tests/credential-hashing.test.ts +170 -0
  145. package/packages/irc-core/tests/effects.test.ts +0 -27
  146. package/packages/irc-core/tests/nick-history-store.test.ts +162 -0
  147. package/packages/irc-core/tests/numerics.test.ts +12 -0
  148. package/packages/irc-core/tests/types.test.ts +35 -1
  149. package/packages/irc-core/tsconfig.build.json +1 -1
  150. package/packages/irc-core/tsconfig.test.json +1 -1
  151. package/packages/irc-server/package.json +1 -1
  152. package/packages/irc-server/src/actor.ts +182 -14
  153. package/packages/irc-server/src/dispatch.ts +0 -3
  154. package/packages/irc-server/src/index.ts +10 -2
  155. package/packages/irc-server/src/routing.ts +21 -0
  156. package/packages/irc-server/src/transport.ts +101 -0
  157. package/packages/irc-server/tests/actor.test.ts +617 -1
  158. package/packages/irc-server/tests/dispatch.test.ts +0 -17
  159. package/packages/irc-server/tests/routing.test.ts +7 -0
  160. package/packages/irc-server/tests/transport.test.ts +230 -0
  161. package/packages/irc-test-support/package.json +1 -1
  162. package/packages/irc-test-support/src/harness.ts +44 -9
  163. package/packages/irc-test-support/src/in-memory-harness.ts +78 -13
  164. package/packages/irc-test-support/src/index.ts +3 -0
  165. package/packages/irc-test-support/src/scenarios.ts +132 -2
  166. package/packages/irc-test-support/tests/in-memory-harness.test.ts +2 -1
  167. package/packages/irc-test-support/tests/in-memory-scenarios.test.ts +23 -9
  168. package/pnpm-workspace.yaml +9 -0
  169. package/tools/ci-hardening/package.json +1 -1
  170. package/tools/package.json +4 -0
  171. package/tools/seed-aws-accounts.ts +79 -0
  172. package/tools/seed-cf-accounts.ts +104 -0
  173. package/tools/tcp-ws-forwarder/package.json +1 -1
  174. package/AGENTS.md +0 -5
  175. package/MOTD.txt +0 -3
  176. package/PLAN-FIXES.md +0 -358
  177. package/PLAN.md +0 -420
  178. package/dashboards/cloudwatch-irc.json +0 -139
  179. package/docs/AWS-Adapter-Architecture.md +0 -494
  180. package/docs/AWS-Deployment.md +0 -1107
  181. package/docs/Cloudflare-Deployment-Guide.md +0 -660
  182. package/docs/Home.md +0 -1
  183. package/docs/Observability.md +0 -87
  184. package/docs/PlanIRCv3Websocket.md +0 -489
  185. package/docs/PlanWebClient.md +0 -451
  186. package/docs/Release-Process.md +0 -440
  187. package/progress.md +0 -107
  188. package/tickets.md +0 -2485
  189. package/webircgateway/LICENSE +0 -201
  190. package/webircgateway/Makefile +0 -44
  191. package/webircgateway/README.md +0 -134
  192. package/webircgateway/config.conf.example +0 -135
  193. package/webircgateway/go.mod +0 -16
  194. package/webircgateway/go.sum +0 -89
  195. package/webircgateway/main.go +0 -118
  196. package/webircgateway/pkg/dnsbl/dnsbl.go +0 -121
  197. package/webircgateway/pkg/identd/identd.go +0 -86
  198. package/webircgateway/pkg/identd/rpcclient.go +0 -59
  199. package/webircgateway/pkg/irc/isupport.go +0 -56
  200. package/webircgateway/pkg/irc/message.go +0 -217
  201. package/webircgateway/pkg/irc/state.go +0 -79
  202. package/webircgateway/pkg/proxy/proxy.go +0 -129
  203. package/webircgateway/pkg/proxy/server.go +0 -237
  204. package/webircgateway/pkg/recaptcha/recaptcha.go +0 -59
  205. package/webircgateway/pkg/webircgateway/client.go +0 -741
  206. package/webircgateway/pkg/webircgateway/client_command_handlers.go +0 -495
  207. package/webircgateway/pkg/webircgateway/config.go +0 -385
  208. package/webircgateway/pkg/webircgateway/gateway.go +0 -278
  209. package/webircgateway/pkg/webircgateway/gateway_utils.go +0 -133
  210. package/webircgateway/pkg/webircgateway/hooks.go +0 -152
  211. package/webircgateway/pkg/webircgateway/letsencrypt.go +0 -41
  212. package/webircgateway/pkg/webircgateway/messagetags.go +0 -103
  213. package/webircgateway/pkg/webircgateway/transport_kiwiirc.go +0 -206
  214. package/webircgateway/pkg/webircgateway/transport_sockjs.go +0 -107
  215. package/webircgateway/pkg/webircgateway/transport_tcp.go +0 -113
  216. package/webircgateway/pkg/webircgateway/transport_websocket.go +0 -126
  217. package/webircgateway/pkg/webircgateway/utils.go +0 -147
  218. package/webircgateway/plugins/example/plugin.go +0 -11
  219. package/webircgateway/plugins/stats/plugin.go +0 -52
  220. package/webircgateway/staticcheck.conf +0 -1
  221. 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,11 +0,0 @@
1
- package main
2
-
3
- import (
4
- "sync"
5
-
6
- "github.com/kiwiirc/webircgateway/pkg/webircgateway"
7
- )
8
-
9
- func Start(gateway *webircgateway.Gateway, pluginsQuit *sync.WaitGroup) {
10
- gateway.Log(1, "Example gateway plugin %s", webircgateway.Version)
11
- }
@@ -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 &lt;&gt; 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>