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,385 +0,0 @@
1
- package webircgateway
2
-
3
- import (
4
- "errors"
5
- "net"
6
- "os"
7
- "os/exec"
8
- "path/filepath"
9
- "strconv"
10
- "strings"
11
-
12
- "github.com/gobwas/glob"
13
- "gopkg.in/ini.v1"
14
- )
15
-
16
- // ConfigUpstream - An upstream config
17
- type ConfigUpstream struct {
18
- // Plugins may assign an arbitary address to an upstream network
19
- NetworkCommonAddress string
20
- Hostname string
21
- Port int
22
- TLS bool
23
- Timeout int
24
- Throttle int
25
- WebircPassword string
26
- ServerPassword string
27
- GatewayName string
28
- Proxy *ConfigProxy
29
- Protocol string
30
- LocalAddr string
31
- }
32
-
33
- // ConfigServer - A web server config
34
- type ConfigServer struct {
35
- LocalAddr string
36
- BindMode os.FileMode
37
- Port int
38
- TLS bool
39
- CertFile string
40
- KeyFile string
41
- LetsEncryptCacheDir string
42
- }
43
-
44
- type ConfigProxy struct {
45
- Type string
46
- Hostname string
47
- Port int
48
- TLS bool
49
- Username string
50
- Interface string
51
- }
52
-
53
- // Config - Config options for the running app
54
- type Config struct {
55
- gateway *Gateway
56
- ConfigFile string
57
- LogLevel int
58
- Gateway bool
59
- GatewayName string
60
- GatewayWhitelist []glob.Glob
61
- GatewayThrottle int
62
- GatewayTimeout int
63
- GatewayWebircPassword map[string]string
64
- GatewayProtocol string
65
- GatewayLocalAddr string
66
- Proxy ConfigServer
67
- Upstreams []ConfigUpstream
68
- Servers []ConfigServer
69
- ServerTransports []string
70
- RemoteOrigins []glob.Glob
71
- ReverseProxies []net.IPNet
72
- Webroot string
73
- ClientRealname string
74
- ClientUsername string
75
- ClientHostname string
76
- Identd bool
77
- RequiresVerification bool
78
- SendQuitOnClientClose string
79
- ReCaptchaURL string
80
- ReCaptchaSecret string
81
- ReCaptchaKey string
82
- Secret string
83
- Plugins []string
84
- DnsblServers []string
85
- // DnsblAction - "deny" = deny the connection. "verify" = require verification
86
- DnsblAction string
87
- }
88
-
89
- func NewConfig(gateway *Gateway) *Config {
90
- return &Config{gateway: gateway}
91
- }
92
-
93
- // ConfigResolvePath - If relative, resolve a path to it's full absolute path relative to the config file
94
- func (c *Config) ResolvePath(path string) string {
95
- // Absolute paths should stay as they are
96
- if path[0:1] == "/" {
97
- return path
98
- }
99
-
100
- resolved := filepath.Dir(c.ConfigFile)
101
- resolved = filepath.Clean(resolved + "/" + path)
102
- return resolved
103
- }
104
-
105
- func (c *Config) SetConfigFile(configFile string) {
106
- // Config paths starting with $ is executed rather than treated as a path
107
- if strings.HasPrefix(configFile, "$ ") {
108
- c.ConfigFile = configFile
109
- } else {
110
- c.ConfigFile, _ = filepath.Abs(configFile)
111
- }
112
- }
113
-
114
- // CurrentConfigFile - Return the full path or command for the config file in use
115
- func (c *Config) CurrentConfigFile() string {
116
- return c.ConfigFile
117
- }
118
-
119
- func (c *Config) Load() error {
120
- var configSrc interface{}
121
-
122
- if strings.HasPrefix(c.ConfigFile, "$ ") {
123
- cmdRawOut, err := exec.Command("sh", "-c", c.ConfigFile[2:]).Output()
124
- if err != nil {
125
- return err
126
- }
127
-
128
- configSrc = cmdRawOut
129
- } else {
130
- configSrc = c.ConfigFile
131
- }
132
-
133
- cfg, err := ini.LoadSources(ini.LoadOptions{AllowBooleanKeys: true, SpaceBeforeInlineComment: true}, configSrc)
134
- if err != nil {
135
- return err
136
- }
137
-
138
- // Clear the existing config
139
- c.Gateway = false
140
- c.GatewayWebircPassword = make(map[string]string)
141
- c.Proxy = ConfigServer{}
142
- c.Upstreams = []ConfigUpstream{}
143
- c.Servers = []ConfigServer{}
144
- c.ServerTransports = []string{}
145
- c.RemoteOrigins = []glob.Glob{}
146
- c.GatewayWhitelist = []glob.Glob{}
147
- c.ReverseProxies = []net.IPNet{}
148
- c.Webroot = ""
149
- c.ReCaptchaURL = ""
150
- c.ReCaptchaSecret = ""
151
- c.ReCaptchaKey = ""
152
- c.RequiresVerification = false
153
- c.Secret = ""
154
- c.SendQuitOnClientClose = ""
155
- c.ClientRealname = ""
156
- c.ClientUsername = ""
157
- c.ClientHostname = ""
158
- c.DnsblServers = []string{}
159
- c.DnsblAction = ""
160
-
161
- for _, section := range cfg.Sections() {
162
- if strings.Index(section.Name(), "DEFAULT") == 0 {
163
- c.LogLevel = section.Key("logLevel").MustInt(3)
164
- if c.LogLevel < 1 || c.LogLevel > 3 {
165
- c.gateway.Log(3, "Config option logLevel must be between 1-3. Setting default value of 3.")
166
- c.LogLevel = 3
167
- }
168
-
169
- c.Identd = section.Key("identd").MustBool(false)
170
-
171
- c.GatewayName = section.Key("gateway_name").MustString("")
172
- if strings.Contains(c.GatewayName, " ") {
173
- c.gateway.Log(3, "Config option gateway_name must not contain spaces")
174
- c.GatewayName = ""
175
- }
176
-
177
- c.Secret = section.Key("secret").MustString("")
178
- c.SendQuitOnClientClose = section.Key("send_quit_on_client_close").MustString("Connection closed")
179
- }
180
-
181
- if section.Name() == "verify" {
182
- captchaSecret := section.Key("recaptcha_secret").MustString("")
183
- captchaKey := section.Key("recaptcha_key").MustString("")
184
- if captchaSecret != "" && captchaKey != "" {
185
- c.RequiresVerification = section.Key("required").MustBool(false)
186
- c.ReCaptchaSecret = captchaSecret
187
- }
188
- c.ReCaptchaURL = section.Key("recaptcha_url").MustString("https://www.google.com/recaptcha/api/siteverify")
189
- }
190
-
191
- if section.Name() == "dnsbl" {
192
- c.DnsblAction = section.Key("action").MustString("")
193
- }
194
-
195
- if section.Name() == "dnsbl.servers" {
196
- c.DnsblServers = append(c.DnsblServers, section.KeyStrings()...)
197
- }
198
-
199
- if section.Name() == "gateway" {
200
- c.Gateway = section.Key("enabled").MustBool(false)
201
- c.GatewayTimeout = section.Key("timeout").MustInt(10)
202
- c.GatewayThrottle = section.Key("throttle").MustInt(2)
203
-
204
- validProtocols := []string{"tcp", "tcp4", "tcp6"}
205
- c.GatewayProtocol = stringInSliceOrDefault(section.Key("protocol").MustString(""), "tcp", validProtocols)
206
- c.GatewayLocalAddr = section.Key("localaddr").MustString("")
207
- }
208
-
209
- if section.Name() == "gateway.webirc" {
210
- for _, serverAddr := range section.KeyStrings() {
211
- c.GatewayWebircPassword[serverAddr] = section.Key(serverAddr).MustString("")
212
- }
213
- }
214
-
215
- if strings.Index(section.Name(), "clients") == 0 {
216
- c.ClientUsername = section.Key("username").MustString("")
217
- c.ClientRealname = section.Key("realname").MustString("")
218
- c.ClientHostname = section.Key("hostname").MustString("")
219
- }
220
-
221
- if strings.Index(section.Name(), "fileserving") == 0 {
222
- if section.Key("enabled").MustBool(false) {
223
- c.Webroot = section.Key("webroot").MustString("")
224
- }
225
- }
226
-
227
- if strings.Index(section.Name(), "server.") == 0 {
228
- server := ConfigServer{}
229
- server.LocalAddr = confKeyAsString(section.Key("bind"), "127.0.0.1")
230
- rawMode := confKeyAsString(section.Key("bind_mode"), "")
231
- mode, err := strconv.ParseInt(rawMode, 8, 32)
232
- if err != nil {
233
- mode = 0755
234
- }
235
- server.BindMode = os.FileMode(mode)
236
- server.Port = confKeyAsInt(section.Key("port"), 80)
237
- server.TLS = confKeyAsBool(section.Key("tls"), false)
238
- server.CertFile = confKeyAsString(section.Key("cert"), "")
239
- server.KeyFile = confKeyAsString(section.Key("key"), "")
240
- server.LetsEncryptCacheDir = confKeyAsString(section.Key("letsencrypt_cache"), "")
241
-
242
- if strings.HasSuffix(server.LetsEncryptCacheDir, ".cache") {
243
- return errors.New("Syntax has changed. Please update letsencrypt_cache to a directory path (eg ./cache)")
244
- }
245
-
246
- c.Servers = append(c.Servers, server)
247
- }
248
-
249
- if section.Name() == "proxy" {
250
- server := ConfigServer{}
251
- server.LocalAddr = confKeyAsString(section.Key("bind"), "0.0.0.0")
252
- server.Port = confKeyAsInt(section.Key("port"), 7999)
253
- c.Proxy = server
254
- }
255
-
256
- if strings.Index(section.Name(), "upstream.") == 0 {
257
- upstream := ConfigUpstream{}
258
-
259
- validProtocols := []string{"tcp", "tcp4", "tcp6", "unix"}
260
- upstream.Protocol = stringInSliceOrDefault(section.Key("protocol").MustString(""), "tcp", validProtocols)
261
-
262
- hostname := section.Key("hostname").MustString("127.0.0.1")
263
- if strings.HasPrefix(strings.ToLower(hostname), "unix:") {
264
- upstream.Protocol = "unix"
265
- upstream.Hostname = hostname[5:]
266
- } else {
267
- upstream.Hostname = hostname
268
- upstream.Port = section.Key("port").MustInt(6667)
269
- upstream.TLS = section.Key("tls").MustBool(false)
270
- }
271
-
272
- upstream.Timeout = section.Key("timeout").MustInt(10)
273
- upstream.Throttle = section.Key("throttle").MustInt(2)
274
- upstream.WebircPassword = section.Key("webirc").MustString("")
275
- upstream.ServerPassword = section.Key("serverpassword").MustString("")
276
- upstream.LocalAddr = section.Key("localaddr").MustString("")
277
-
278
- upstream.GatewayName = section.Key("gateway_name").MustString("")
279
- if strings.Contains(upstream.GatewayName, " ") {
280
- c.gateway.Log(3, "Config option gateway_name must not contain spaces")
281
- upstream.GatewayName = ""
282
- }
283
-
284
- upstream.NetworkCommonAddress = section.Key("network_common_address").MustString("")
285
-
286
- c.Upstreams = append(c.Upstreams, upstream)
287
- }
288
-
289
- // "engines" is now legacy naming
290
- if section.Name() == "engines" || section.Name() == "transports" {
291
- for _, transport := range section.KeyStrings() {
292
- c.ServerTransports = append(c.ServerTransports, strings.Trim(transport, "\n"))
293
- }
294
- }
295
-
296
- if strings.Index(section.Name(), "plugins") == 0 {
297
- for _, plugin := range section.KeyStrings() {
298
- c.Plugins = append(c.Plugins, strings.Trim(plugin, "\n"))
299
- }
300
- }
301
-
302
- if strings.Index(section.Name(), "allowed_origins") == 0 {
303
- for _, origin := range section.KeyStrings() {
304
- match, err := glob.Compile(origin)
305
- if err != nil {
306
- c.gateway.Log(3, "Config section allowed_origins has invalid match, "+origin)
307
- continue
308
- }
309
- c.RemoteOrigins = append(c.RemoteOrigins, match)
310
- }
311
- }
312
-
313
- if strings.Index(section.Name(), "gateway.whitelist") == 0 {
314
- for _, origin := range section.KeyStrings() {
315
- match, err := glob.Compile(origin)
316
- if err != nil {
317
- c.gateway.Log(3, "Config section gateway.whitelist has invalid match, "+origin)
318
- continue
319
- }
320
- c.GatewayWhitelist = append(c.GatewayWhitelist, match)
321
- }
322
- }
323
-
324
- if strings.Index(section.Name(), "reverse_proxies") == 0 {
325
- for _, cidrRange := range section.KeyStrings() {
326
- _, validRange, cidrErr := net.ParseCIDR(cidrRange)
327
- if cidrErr != nil {
328
- c.gateway.Log(3, "Config section reverse_proxies has invalid entry, "+cidrRange)
329
- continue
330
- }
331
- c.ReverseProxies = append(c.ReverseProxies, *validRange)
332
- }
333
- }
334
- }
335
-
336
- return nil
337
- }
338
-
339
- func confKeyAsString(key *ini.Key, def string) string {
340
- val := def
341
-
342
- str := key.String()
343
- if len(str) > 1 && str[:1] == "$" {
344
- val = os.Getenv(str[1:])
345
- } else {
346
- val = key.MustString(def)
347
- }
348
-
349
- return val
350
- }
351
-
352
- func confKeyAsInt(key *ini.Key, def int) int {
353
- val := def
354
-
355
- str := key.String()
356
- if len(str) > 1 && str[:1] == "$" {
357
- envVal := os.Getenv(str[1:])
358
- envValInt, err := strconv.Atoi(envVal)
359
- if err == nil {
360
- val = envValInt
361
- }
362
- } else {
363
- val = key.MustInt(def)
364
- }
365
-
366
- return val
367
- }
368
-
369
- func confKeyAsBool(key *ini.Key, def bool) bool {
370
- val := def
371
-
372
- str := key.String()
373
- if len(str) > 1 && str[:1] == "$" {
374
- envVal := os.Getenv(str[1:])
375
- if envVal == "0" || envVal == "false" || envVal == "no" {
376
- val = false
377
- } else {
378
- val = true
379
- }
380
- } else {
381
- val = key.MustBool(def)
382
- }
383
-
384
- return val
385
- }
@@ -1,278 +0,0 @@
1
- package webircgateway
2
-
3
- import (
4
- "crypto/tls"
5
- "encoding/json"
6
- "fmt"
7
- "net"
8
- "net/http"
9
- "os"
10
- "strconv"
11
- "strings"
12
- "sync"
13
-
14
- "errors"
15
-
16
- "github.com/kiwiirc/webircgateway/pkg/identd"
17
- "github.com/kiwiirc/webircgateway/pkg/proxy"
18
- cmap "github.com/orcaman/concurrent-map"
19
- )
20
-
21
- var (
22
- Version = "-"
23
- )
24
-
25
- type Gateway struct {
26
- Config *Config
27
- HttpRouter *http.ServeMux
28
- LogOutput chan string
29
- messageTags *MessageTagManager
30
- identdServ identd.Server
31
- Clients cmap.ConcurrentMap
32
- Acme *LEManager
33
- Function string
34
- httpSrvs []*http.Server
35
- httpSrvsMu sync.Mutex
36
- closeWg sync.WaitGroup
37
- }
38
-
39
- func NewGateway(function string) *Gateway {
40
- s := &Gateway{}
41
- s.Function = function
42
- s.Config = NewConfig(s)
43
- s.HttpRouter = http.NewServeMux()
44
- s.LogOutput = make(chan string, 5)
45
- s.identdServ = identd.NewIdentdServer()
46
- s.messageTags = NewMessageTagManager()
47
- // Clients hold a map lookup for all the connected clients
48
- s.Clients = cmap.New()
49
- s.Acme = NewLetsEncryptManager(s)
50
-
51
- return s
52
- }
53
-
54
- func (s *Gateway) Log(level int, format string, args ...interface{}) {
55
- if level < s.Config.LogLevel {
56
- return
57
- }
58
-
59
- levels := [...]string{"L_DEBUG", "L_INFO", "L_WARN"}
60
- line := fmt.Sprintf(levels[level-1]+" "+format, args...)
61
- s.LogOutput <- line
62
- }
63
-
64
- func (s *Gateway) Start() {
65
- s.closeWg.Add(1)
66
-
67
- if s.Function == "gateway" {
68
- s.maybeStartStaticFileServer()
69
- s.initHttpRoutes()
70
- s.maybeStartIdentd()
71
-
72
- for _, serverConfig := range s.Config.Servers {
73
- go s.startServer(serverConfig)
74
- }
75
- }
76
-
77
- if s.Function == "proxy" {
78
- proxy.Start(fmt.Sprintf("%s:%d", s.Config.Proxy.LocalAddr, s.Config.Proxy.Port))
79
- }
80
- }
81
-
82
- func (s *Gateway) Close() {
83
- hook := HookGatewayClosing{}
84
- hook.Dispatch("gateway.closing")
85
-
86
- defer s.closeWg.Done()
87
-
88
- s.httpSrvsMu.Lock()
89
- defer s.httpSrvsMu.Unlock()
90
-
91
- for _, httpSrv := range s.httpSrvs {
92
- httpSrv.Close()
93
- }
94
- }
95
-
96
- func (s *Gateway) WaitClose() {
97
- s.closeWg.Wait()
98
- }
99
-
100
- func (s *Gateway) maybeStartStaticFileServer() {
101
- if s.Config.Webroot != "" {
102
- webroot := s.Config.ResolvePath(s.Config.Webroot)
103
- s.Log(2, "Serving files from %s", webroot)
104
- s.HttpRouter.Handle("/", http.FileServer(http.Dir(webroot)))
105
- }
106
- }
107
-
108
- func (s *Gateway) initHttpRoutes() error {
109
- // Add all the transport routes
110
- engineConfigured := false
111
- for _, transport := range s.Config.ServerTransports {
112
- switch transport {
113
- case "kiwiirc":
114
- t := &TransportKiwiirc{}
115
- t.Init(s)
116
- engineConfigured = true
117
- case "websocket":
118
- t := &TransportWebsocket{}
119
- t.Init(s)
120
- engineConfigured = true
121
- case "sockjs":
122
- t := &TransportSockjs{}
123
- t.Init(s)
124
- engineConfigured = true
125
- default:
126
- s.Log(3, "Invalid server engine: '%s'", transport)
127
- }
128
- }
129
-
130
- if !engineConfigured {
131
- s.Log(3, "No server engines configured")
132
- return errors.New("No server engines configured")
133
- }
134
-
135
- // Add some general server info about this webircgateway instance
136
- s.HttpRouter.HandleFunc("/webirc/info", func(w http.ResponseWriter, r *http.Request) {
137
- out, _ := json.Marshal(map[string]interface{}{
138
- "name": "webircgateway",
139
- "version": Version,
140
- })
141
-
142
- w.Write(out)
143
- })
144
-
145
- s.HttpRouter.HandleFunc("/webirc/_status", func(w http.ResponseWriter, r *http.Request) {
146
- if !isPrivateIP(s.GetRemoteAddressFromRequest(r)) {
147
- w.WriteHeader(403)
148
- return
149
- }
150
-
151
- out := ""
152
- for item := range s.Clients.IterBuffered() {
153
- c := item.Val.(*Client)
154
- line := fmt.Sprintf(
155
- "%s:%d %s %s!%s %s %s",
156
- c.UpstreamConfig.Hostname,
157
- c.UpstreamConfig.Port,
158
- c.State,
159
- c.IrcState.Nick,
160
- c.IrcState.Username,
161
- c.RemoteAddr,
162
- c.RemoteHostname,
163
- )
164
-
165
- // Allow plugins to add their own status data
166
- hook := HookStatus{}
167
- hook.Client = c
168
- hook.Line = line
169
- hook.Dispatch("status.client")
170
- if !hook.Halt {
171
- out += hook.Line + "\n"
172
- }
173
-
174
- }
175
-
176
- w.Write([]byte(out))
177
- })
178
-
179
- return nil
180
- }
181
-
182
- func (s *Gateway) maybeStartIdentd() {
183
- if s.Config.Identd {
184
- err := s.identdServ.Run()
185
- if err != nil {
186
- s.Log(3, "Error starting identd server: %s", err.Error())
187
- } else {
188
- s.Log(2, "Identd server started")
189
- }
190
- }
191
- }
192
-
193
- func (s *Gateway) startServer(conf ConfigServer) {
194
- addr := fmt.Sprintf("%s:%d", conf.LocalAddr, conf.Port)
195
-
196
- if strings.HasPrefix(strings.ToLower(conf.LocalAddr), "tcp:") {
197
- t := &TransportTcp{}
198
- t.Init(s)
199
- t.Start(conf.LocalAddr[4:] + ":" + strconv.Itoa(conf.Port))
200
- } else if conf.TLS && conf.LetsEncryptCacheDir == "" {
201
- if conf.CertFile == "" || conf.KeyFile == "" {
202
- s.Log(3, "'cert' and 'key' options must be set for TLS servers")
203
- return
204
- }
205
-
206
- tlsCert := s.Config.ResolvePath(conf.CertFile)
207
- tlsKey := s.Config.ResolvePath(conf.KeyFile)
208
-
209
- s.Log(2, "Listening with TLS on %s", addr)
210
- keyPair, keyPairErr := tls.LoadX509KeyPair(tlsCert, tlsKey)
211
- if keyPairErr != nil {
212
- s.Log(3, "Failed to listen with TLS, certificate error: %s", keyPairErr.Error())
213
- return
214
- }
215
- srv := &http.Server{
216
- Addr: addr,
217
- TLSConfig: &tls.Config{
218
- Certificates: []tls.Certificate{keyPair},
219
- },
220
- Handler: s.HttpRouter,
221
- }
222
- s.httpSrvsMu.Lock()
223
- s.httpSrvs = append(s.httpSrvs, srv)
224
- s.httpSrvsMu.Unlock()
225
-
226
- // Don't use HTTP2 since it doesn't support websockets
227
- srv.TLSNextProto = make(map[string]func(*http.Server, *tls.Conn, http.Handler))
228
-
229
- err := srv.ListenAndServeTLS("", "")
230
- if err != nil && err != http.ErrServerClosed {
231
- s.Log(3, "Failed to listen with TLS: %s", err.Error())
232
- }
233
- } else if conf.TLS && conf.LetsEncryptCacheDir != "" {
234
- s.Log(2, "Listening with letsencrypt TLS on %s", addr)
235
- leManager := s.Acme.Get(conf.LetsEncryptCacheDir)
236
- srv := &http.Server{
237
- Addr: addr,
238
- TLSConfig: &tls.Config{
239
- GetCertificate: leManager.GetCertificate,
240
- },
241
- Handler: s.HttpRouter,
242
- }
243
- s.httpSrvsMu.Lock()
244
- s.httpSrvs = append(s.httpSrvs, srv)
245
- s.httpSrvsMu.Unlock()
246
-
247
- // Don't use HTTP2 since it doesn't support websockets
248
- srv.TLSNextProto = make(map[string]func(*http.Server, *tls.Conn, http.Handler))
249
-
250
- err := srv.ListenAndServeTLS("", "")
251
- if err != nil && err != http.ErrServerClosed {
252
- s.Log(3, "Listening with letsencrypt failed: %s", err.Error())
253
- }
254
- } else if strings.HasPrefix(strings.ToLower(conf.LocalAddr), "unix:") {
255
- socketFile := conf.LocalAddr[5:]
256
- s.Log(2, "Listening on %s", socketFile)
257
- os.Remove(socketFile)
258
- server, serverErr := net.Listen("unix", socketFile)
259
- if serverErr != nil {
260
- s.Log(3, serverErr.Error())
261
- return
262
- }
263
- os.Chmod(socketFile, conf.BindMode)
264
- http.Serve(server, s.HttpRouter)
265
- } else {
266
- s.Log(2, "Listening on %s", addr)
267
- srv := &http.Server{Addr: addr, Handler: s.HttpRouter}
268
-
269
- s.httpSrvsMu.Lock()
270
- s.httpSrvs = append(s.httpSrvs, srv)
271
- s.httpSrvsMu.Unlock()
272
-
273
- err := srv.ListenAndServe()
274
- if err != nil && err != http.ErrServerClosed {
275
- s.Log(3, err.Error())
276
- }
277
- }
278
- }