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,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
|
-
}
|