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,741 +0,0 @@
|
|
|
1
|
-
package webircgateway
|
|
2
|
-
|
|
3
|
-
import (
|
|
4
|
-
"bufio"
|
|
5
|
-
"crypto/tls"
|
|
6
|
-
"errors"
|
|
7
|
-
"fmt"
|
|
8
|
-
"io"
|
|
9
|
-
"net"
|
|
10
|
-
"runtime/debug"
|
|
11
|
-
"strconv"
|
|
12
|
-
"strings"
|
|
13
|
-
"sync"
|
|
14
|
-
"sync/atomic"
|
|
15
|
-
"syscall"
|
|
16
|
-
"time"
|
|
17
|
-
|
|
18
|
-
"golang.org/x/time/rate"
|
|
19
|
-
|
|
20
|
-
"github.com/kiwiirc/webircgateway/pkg/dnsbl"
|
|
21
|
-
"github.com/kiwiirc/webircgateway/pkg/irc"
|
|
22
|
-
"github.com/kiwiirc/webircgateway/pkg/proxy"
|
|
23
|
-
)
|
|
24
|
-
|
|
25
|
-
const (
|
|
26
|
-
// ClientStateIdle - Client connected and just sat there
|
|
27
|
-
ClientStateIdle = "idle"
|
|
28
|
-
// ClientStateConnecting - Connecting upstream
|
|
29
|
-
ClientStateConnecting = "connecting"
|
|
30
|
-
// ClientStateRegistering - Registering to the IRC network
|
|
31
|
-
ClientStateRegistering = "registering"
|
|
32
|
-
// ClientStateConnected - Connected upstream
|
|
33
|
-
ClientStateConnected = "connected"
|
|
34
|
-
// ClientStateEnding - Client is ending its connection
|
|
35
|
-
ClientStateEnding = "ending"
|
|
36
|
-
)
|
|
37
|
-
|
|
38
|
-
type ClientSignal [3]string
|
|
39
|
-
|
|
40
|
-
// Client - Connecting client struct
|
|
41
|
-
type Client struct {
|
|
42
|
-
Gateway *Gateway
|
|
43
|
-
Id uint64
|
|
44
|
-
State string
|
|
45
|
-
EndWG sync.WaitGroup
|
|
46
|
-
shuttingDownLock sync.Mutex
|
|
47
|
-
shuttingDown bool
|
|
48
|
-
SeenQuit bool
|
|
49
|
-
Recv chan string
|
|
50
|
-
ThrottledRecv *ThrottledStringChannel
|
|
51
|
-
upstream io.ReadWriteCloser
|
|
52
|
-
UpstreamRecv chan string
|
|
53
|
-
UpstreamSend chan string
|
|
54
|
-
UpstreamStarted bool
|
|
55
|
-
UpstreamConfig *ConfigUpstream
|
|
56
|
-
RemoteAddr string
|
|
57
|
-
RemoteHostname string
|
|
58
|
-
RemotePort int
|
|
59
|
-
DestHost string
|
|
60
|
-
DestPort int
|
|
61
|
-
DestTLS bool
|
|
62
|
-
IrcState *irc.State
|
|
63
|
-
Encoding string
|
|
64
|
-
// Tags get passed upstream via the WEBIRC command
|
|
65
|
-
Tags map[string]string
|
|
66
|
-
// Captchas may be needed to verify a client
|
|
67
|
-
RequiresVerification bool
|
|
68
|
-
Verified bool
|
|
69
|
-
SentPass bool
|
|
70
|
-
// Signals for the transport to make use of (data, connection state, etc)
|
|
71
|
-
Signals chan ClientSignal
|
|
72
|
-
Features struct {
|
|
73
|
-
Messagetags bool
|
|
74
|
-
Metadata bool
|
|
75
|
-
ExtJwt bool
|
|
76
|
-
}
|
|
77
|
-
// The specific message-tags CAP that the client has requested if we are wrapping it
|
|
78
|
-
RequestedMessageTagsCap string
|
|
79
|
-
// Prefix used by the server when sending its own messages
|
|
80
|
-
ServerMessagePrefix irc.Mask
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
var nextClientID uint64 = 1
|
|
84
|
-
|
|
85
|
-
// NewClient - Makes a new client
|
|
86
|
-
func NewClient(gateway *Gateway) *Client {
|
|
87
|
-
thisID := atomic.AddUint64(&nextClientID, 1)
|
|
88
|
-
|
|
89
|
-
recv := make(chan string, 50)
|
|
90
|
-
c := &Client{
|
|
91
|
-
Gateway: gateway,
|
|
92
|
-
Id: thisID,
|
|
93
|
-
State: ClientStateIdle,
|
|
94
|
-
Recv: recv,
|
|
95
|
-
ThrottledRecv: NewThrottledStringChannel(recv, rate.NewLimiter(rate.Inf, 1)),
|
|
96
|
-
UpstreamSend: make(chan string, 50),
|
|
97
|
-
UpstreamRecv: make(chan string, 50),
|
|
98
|
-
Encoding: "UTF-8",
|
|
99
|
-
Signals: make(chan ClientSignal, 50),
|
|
100
|
-
Tags: make(map[string]string),
|
|
101
|
-
IrcState: irc.NewState(),
|
|
102
|
-
UpstreamConfig: &ConfigUpstream{},
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// Auto enable some features by default. They may be disabled later on
|
|
106
|
-
c.Features.ExtJwt = true
|
|
107
|
-
|
|
108
|
-
c.RequiresVerification = gateway.Config.RequiresVerification
|
|
109
|
-
|
|
110
|
-
// Handles data to/from the client and upstreams
|
|
111
|
-
go c.clientLineWorker()
|
|
112
|
-
|
|
113
|
-
// This Add(1) will be ended once the client starts shutting down in StartShutdown()
|
|
114
|
-
c.EndWG.Add(1)
|
|
115
|
-
|
|
116
|
-
// Add to the clients maps and wait until everything has been marked
|
|
117
|
-
// as completed (several routines add themselves to EndWG so that we can catch
|
|
118
|
-
// when they are all completed)
|
|
119
|
-
gateway.Clients.Set(strconv.FormatUint(c.Id, 10), c)
|
|
120
|
-
go func() {
|
|
121
|
-
c.EndWG.Wait()
|
|
122
|
-
gateway.Clients.Remove(strconv.FormatUint(c.Id, 10))
|
|
123
|
-
|
|
124
|
-
hook := &HookClientState{
|
|
125
|
-
Client: c,
|
|
126
|
-
Connected: false,
|
|
127
|
-
}
|
|
128
|
-
hook.Dispatch("client.state")
|
|
129
|
-
}()
|
|
130
|
-
|
|
131
|
-
hook := &HookClientState{
|
|
132
|
-
Client: c,
|
|
133
|
-
Connected: true,
|
|
134
|
-
}
|
|
135
|
-
hook.Dispatch("client.state")
|
|
136
|
-
|
|
137
|
-
return c
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
// Log - Log a line of text with context of this client
|
|
141
|
-
func (c *Client) Log(level int, format string, args ...interface{}) {
|
|
142
|
-
prefix := fmt.Sprintf("client:%d ", c.Id)
|
|
143
|
-
c.Gateway.Log(level, prefix+format, args...)
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// TrafficLog - Log out raw IRC traffic
|
|
147
|
-
func (c *Client) TrafficLog(isUpstream bool, toGateway bool, traffic string) {
|
|
148
|
-
label := ""
|
|
149
|
-
if isUpstream && toGateway {
|
|
150
|
-
label = "Upstream->"
|
|
151
|
-
} else if isUpstream && !toGateway {
|
|
152
|
-
label = "->Upstream"
|
|
153
|
-
} else if !isUpstream && toGateway {
|
|
154
|
-
label = "Client->"
|
|
155
|
-
} else if !isUpstream && !toGateway {
|
|
156
|
-
label = "->Client"
|
|
157
|
-
}
|
|
158
|
-
c.Log(1, "Traffic (%s) %s", label, traffic)
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
func (c *Client) Ready() {
|
|
162
|
-
dnsblAction := c.Gateway.Config.DnsblAction
|
|
163
|
-
validAction := dnsblAction == "verify" || dnsblAction == "deny"
|
|
164
|
-
dnsblTookAction := ""
|
|
165
|
-
|
|
166
|
-
if len(c.Gateway.Config.DnsblServers) > 0 && c.RemoteAddr != "" && !c.Verified && validAction {
|
|
167
|
-
dnsblTookAction = c.checkDnsBl()
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
if dnsblTookAction == "" && c.Gateway.Config.RequiresVerification && !c.Verified {
|
|
171
|
-
c.SendClientSignal("data", "CAPTCHA NEEDED")
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
func (c *Client) checkDnsBl() (tookAction string) {
|
|
176
|
-
dnsResult := dnsbl.Lookup(c.Gateway.Config.DnsblServers, c.RemoteAddr)
|
|
177
|
-
if dnsResult.Listed && c.Gateway.Config.DnsblAction == "deny" {
|
|
178
|
-
c.SendIrcError("Blocked by DNSBL")
|
|
179
|
-
c.SendClientSignal("state", "closed", "dnsbl_listed")
|
|
180
|
-
c.StartShutdown("dnsbl")
|
|
181
|
-
tookAction = "deny"
|
|
182
|
-
} else if dnsResult.Listed && c.Gateway.Config.DnsblAction == "verify" {
|
|
183
|
-
c.RequiresVerification = true
|
|
184
|
-
c.SendClientSignal("data", "CAPTCHA NEEDED")
|
|
185
|
-
tookAction = "verify"
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
return
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
func (c *Client) IsShuttingDown() bool {
|
|
192
|
-
c.shuttingDownLock.Lock()
|
|
193
|
-
defer c.shuttingDownLock.Unlock()
|
|
194
|
-
return c.shuttingDown
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
func (c *Client) StartShutdown(reason string) {
|
|
198
|
-
c.shuttingDownLock.Lock()
|
|
199
|
-
defer c.shuttingDownLock.Unlock()
|
|
200
|
-
|
|
201
|
-
c.Log(1, "StartShutdown(%s) ShuttingDown=%t", reason, c.shuttingDown)
|
|
202
|
-
if !c.shuttingDown {
|
|
203
|
-
c.shuttingDown = true
|
|
204
|
-
c.State = ClientStateEnding
|
|
205
|
-
|
|
206
|
-
switch reason {
|
|
207
|
-
case "upstream_closed":
|
|
208
|
-
c.Log(2, "Upstream closed the connection")
|
|
209
|
-
case "err_connecting_upstream":
|
|
210
|
-
case "err_no_upstream":
|
|
211
|
-
// Error has been logged already
|
|
212
|
-
case "client_closed":
|
|
213
|
-
c.Log(2, "Client disconnected")
|
|
214
|
-
default:
|
|
215
|
-
c.Log(2, "Closed: %s", reason)
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
close(c.Signals)
|
|
219
|
-
c.EndWG.Done()
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
func (c *Client) SendClientSignal(signal string, args ...string) {
|
|
224
|
-
c.shuttingDownLock.Lock()
|
|
225
|
-
defer c.shuttingDownLock.Unlock()
|
|
226
|
-
|
|
227
|
-
if !c.shuttingDown {
|
|
228
|
-
switch len(args) {
|
|
229
|
-
case 0:
|
|
230
|
-
c.Signals <- ClientSignal{signal}
|
|
231
|
-
case 1:
|
|
232
|
-
c.Signals <- ClientSignal{signal, args[0]}
|
|
233
|
-
case 2:
|
|
234
|
-
c.Signals <- ClientSignal{signal, args[0], args[1]}
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
func (c *Client) SendIrcError(message string) {
|
|
240
|
-
c.SendClientSignal("data", "ERROR :"+message)
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
func (c *Client) SendIrcFail(params ...string) {
|
|
244
|
-
failMessage := irc.Message{
|
|
245
|
-
Command: "FAIL",
|
|
246
|
-
Params: params,
|
|
247
|
-
}
|
|
248
|
-
c.SendClientSignal("data", failMessage.ToLine())
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
func (c *Client) connectUpstream() {
|
|
252
|
-
client := c
|
|
253
|
-
|
|
254
|
-
c.UpstreamStarted = true
|
|
255
|
-
|
|
256
|
-
var upstreamConfig ConfigUpstream
|
|
257
|
-
|
|
258
|
-
if client.DestHost == "" {
|
|
259
|
-
client.Log(2, "Using configured upstream")
|
|
260
|
-
var err error
|
|
261
|
-
upstreamConfig, err = c.Gateway.findUpstream()
|
|
262
|
-
if err != nil {
|
|
263
|
-
client.Log(3, "No upstreams available")
|
|
264
|
-
client.SendIrcError("The server has not been configured")
|
|
265
|
-
client.StartShutdown("err_no_upstream")
|
|
266
|
-
return
|
|
267
|
-
}
|
|
268
|
-
} else {
|
|
269
|
-
if !c.Gateway.isIrcAddressAllowed(client.DestHost) {
|
|
270
|
-
client.Log(2, "Server %s is not allowed. Closing connection", client.DestHost)
|
|
271
|
-
client.SendIrcError("Not allowed to connect to " + client.DestHost)
|
|
272
|
-
client.SendClientSignal("state", "closed", "err_forbidden")
|
|
273
|
-
client.StartShutdown("err_no_upstream")
|
|
274
|
-
return
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
client.Log(2, "Using client given upstream")
|
|
278
|
-
upstreamConfig = c.configureUpstream()
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
c.UpstreamConfig = &upstreamConfig
|
|
282
|
-
|
|
283
|
-
hook := &HookIrcConnectionPre{
|
|
284
|
-
Client: client,
|
|
285
|
-
UpstreamConfig: &upstreamConfig,
|
|
286
|
-
}
|
|
287
|
-
hook.Dispatch("irc.connection.pre")
|
|
288
|
-
if hook.Halt {
|
|
289
|
-
client.SendClientSignal("state", "closed", "err_forbidden")
|
|
290
|
-
client.StartShutdown("err_connecting_upstream")
|
|
291
|
-
return
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
client.State = ClientStateConnecting
|
|
295
|
-
|
|
296
|
-
upstream, upstreamErr := client.makeUpstreamConnection()
|
|
297
|
-
if upstreamErr != nil {
|
|
298
|
-
// Error handling was already managed in makeUpstreamConnection()
|
|
299
|
-
return
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
client.State = ClientStateRegistering
|
|
303
|
-
|
|
304
|
-
client.upstream = upstream
|
|
305
|
-
client.readUpstream()
|
|
306
|
-
client.writeWebircLines(upstream)
|
|
307
|
-
client.maybeSendPass(upstream)
|
|
308
|
-
client.SendClientSignal("state", "connected")
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
func (c *Client) makeUpstreamConnection() (io.ReadWriteCloser, error) {
|
|
312
|
-
client := c
|
|
313
|
-
upstreamConfig := c.UpstreamConfig
|
|
314
|
-
|
|
315
|
-
var connection io.ReadWriteCloser
|
|
316
|
-
|
|
317
|
-
if upstreamConfig.Proxy == nil {
|
|
318
|
-
// Connect directly to the IRCd
|
|
319
|
-
dialer := net.Dialer{}
|
|
320
|
-
dialer.Timeout = time.Second * time.Duration(upstreamConfig.Timeout)
|
|
321
|
-
|
|
322
|
-
if upstreamConfig.LocalAddr != "" {
|
|
323
|
-
parsedIP := net.ParseIP(upstreamConfig.LocalAddr)
|
|
324
|
-
if parsedIP != nil {
|
|
325
|
-
dialer.LocalAddr = &net.TCPAddr{
|
|
326
|
-
IP: parsedIP,
|
|
327
|
-
Port: 0,
|
|
328
|
-
}
|
|
329
|
-
} else {
|
|
330
|
-
client.Log(3, "Failed to parse localaddr for upstream connection \"%s\"", upstreamConfig.LocalAddr)
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
var conn net.Conn
|
|
335
|
-
var connErr error
|
|
336
|
-
if upstreamConfig.Protocol == "unix" {
|
|
337
|
-
conn, connErr = dialer.Dial("unix", upstreamConfig.Hostname)
|
|
338
|
-
} else {
|
|
339
|
-
upstreamStr := fmt.Sprintf("%s:%d", upstreamConfig.Hostname, upstreamConfig.Port)
|
|
340
|
-
conn, connErr = dialer.Dial(upstreamConfig.Protocol, upstreamStr)
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
if connErr != nil {
|
|
344
|
-
client.Log(3, "Error connecting to the upstream IRCd. %s", connErr.Error())
|
|
345
|
-
errString := ""
|
|
346
|
-
if errString = typeOfErr(connErr); errString != "" {
|
|
347
|
-
errString = "err_" + errString
|
|
348
|
-
}
|
|
349
|
-
client.SendClientSignal("state", "closed", errString)
|
|
350
|
-
client.StartShutdown("err_connecting_upstream")
|
|
351
|
-
return nil, errors.New("error connecting upstream")
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
// Add the ports into the identd before possible TLS handshaking. If we do it after then
|
|
355
|
-
// there's a good chance the identd lookup will occur before the handshake has finished
|
|
356
|
-
if c.Gateway.Config.Identd {
|
|
357
|
-
// Keep track of the upstreams local and remote port numbers
|
|
358
|
-
_, lPortStr, _ := net.SplitHostPort(conn.LocalAddr().String())
|
|
359
|
-
client.IrcState.LocalPort, _ = strconv.Atoi(lPortStr)
|
|
360
|
-
_, rPortStr, _ := net.SplitHostPort(conn.RemoteAddr().String())
|
|
361
|
-
client.IrcState.RemotePort, _ = strconv.Atoi(rPortStr)
|
|
362
|
-
|
|
363
|
-
c.Gateway.identdServ.AddIdent(client.IrcState.LocalPort, client.IrcState.RemotePort, client.IrcState.Username, "")
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
if upstreamConfig.TLS {
|
|
367
|
-
tlsConfig := &tls.Config{InsecureSkipVerify: true}
|
|
368
|
-
tlsConn := tls.Client(conn, tlsConfig)
|
|
369
|
-
err := tlsConn.Handshake()
|
|
370
|
-
if err != nil {
|
|
371
|
-
client.Log(3, "Error connecting to the upstream IRCd. %s", err.Error())
|
|
372
|
-
client.SendClientSignal("state", "closed", "err_tls")
|
|
373
|
-
client.StartShutdown("err_connecting_upstream")
|
|
374
|
-
return nil, errors.New("error connecting upstream")
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
conn = net.Conn(tlsConn)
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
connection = conn
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
if upstreamConfig.Proxy != nil {
|
|
384
|
-
// Connect to the IRCd via a proxy
|
|
385
|
-
conn := proxy.MakeKiwiProxyConnection()
|
|
386
|
-
conn.DestHost = upstreamConfig.Hostname
|
|
387
|
-
conn.DestPort = upstreamConfig.Port
|
|
388
|
-
conn.DestTLS = upstreamConfig.TLS
|
|
389
|
-
conn.Username = upstreamConfig.Proxy.Username
|
|
390
|
-
conn.ProxyInterface = upstreamConfig.Proxy.Interface
|
|
391
|
-
|
|
392
|
-
dialErr := conn.Dial(fmt.Sprintf(
|
|
393
|
-
"%s:%d",
|
|
394
|
-
upstreamConfig.Proxy.Hostname,
|
|
395
|
-
upstreamConfig.Proxy.Port,
|
|
396
|
-
))
|
|
397
|
-
|
|
398
|
-
if dialErr != nil {
|
|
399
|
-
errString := ""
|
|
400
|
-
if errString = typeOfErr(dialErr); errString != "" {
|
|
401
|
-
errString = "err_" + errString
|
|
402
|
-
} else {
|
|
403
|
-
errString = "err_proxy"
|
|
404
|
-
}
|
|
405
|
-
client.Log(3,
|
|
406
|
-
"Error connecting to the kiwi proxy, %s:%d. %s",
|
|
407
|
-
upstreamConfig.Proxy.Hostname,
|
|
408
|
-
upstreamConfig.Proxy.Port,
|
|
409
|
-
dialErr.Error(),
|
|
410
|
-
)
|
|
411
|
-
|
|
412
|
-
client.SendClientSignal("state", "closed", errString)
|
|
413
|
-
client.StartShutdown("err_connecting_upstream")
|
|
414
|
-
return nil, errors.New("error connecting upstream")
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
connection = conn
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
return connection, nil
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
func (c *Client) writeWebircLines(upstream io.ReadWriteCloser) {
|
|
424
|
-
// Send any WEBIRC lines
|
|
425
|
-
if c.UpstreamConfig.WebircPassword == "" {
|
|
426
|
-
c.Log(1, "No webirc to send")
|
|
427
|
-
return
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
gatewayName := "webircgateway"
|
|
431
|
-
if c.Gateway.Config.GatewayName != "" {
|
|
432
|
-
gatewayName = c.Gateway.Config.GatewayName
|
|
433
|
-
}
|
|
434
|
-
if c.UpstreamConfig.GatewayName != "" {
|
|
435
|
-
gatewayName = c.UpstreamConfig.GatewayName
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
webircTags := c.buildWebircTags()
|
|
439
|
-
if strings.Contains(webircTags, " ") {
|
|
440
|
-
webircTags = ":" + webircTags
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
clientHostname := c.RemoteHostname
|
|
444
|
-
if c.Gateway.Config.ClientHostname != "" {
|
|
445
|
-
clientHostname = makeClientReplacements(c.Gateway.Config.ClientHostname, c)
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
remoteAddr := c.RemoteAddr
|
|
449
|
-
// Prefix IPv6 addresses that start with a : so they can be sent as an individual IRC
|
|
450
|
-
// parameter. eg. ::1 would not parse correctly as a parameter, while 0::1 will
|
|
451
|
-
if strings.HasPrefix(remoteAddr, ":") {
|
|
452
|
-
remoteAddr = "0" + remoteAddr
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
webircLine := fmt.Sprintf(
|
|
456
|
-
"WEBIRC %s %s %s %s %s\n",
|
|
457
|
-
c.UpstreamConfig.WebircPassword,
|
|
458
|
-
gatewayName,
|
|
459
|
-
clientHostname,
|
|
460
|
-
remoteAddr,
|
|
461
|
-
webircTags,
|
|
462
|
-
)
|
|
463
|
-
c.Log(1, "->upstream: %s", webircLine)
|
|
464
|
-
upstream.Write([]byte(webircLine))
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
func (c *Client) maybeSendPass(upstream io.ReadWriteCloser) {
|
|
468
|
-
if c.UpstreamConfig.ServerPassword == "" {
|
|
469
|
-
return
|
|
470
|
-
}
|
|
471
|
-
c.SentPass = true
|
|
472
|
-
passLine := fmt.Sprintf(
|
|
473
|
-
"PASS %s\n",
|
|
474
|
-
c.UpstreamConfig.ServerPassword,
|
|
475
|
-
)
|
|
476
|
-
c.Log(1, "->upstream: %s", passLine)
|
|
477
|
-
upstream.Write([]byte(passLine))
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
func (c *Client) processLineToUpstream(data string) {
|
|
481
|
-
client := c
|
|
482
|
-
upstreamConfig := c.UpstreamConfig
|
|
483
|
-
|
|
484
|
-
if strings.HasPrefix(data, "PASS ") && c.SentPass {
|
|
485
|
-
// Hijack the PASS command if we already sent a pass command
|
|
486
|
-
return
|
|
487
|
-
} else if strings.HasPrefix(data, "USER ") {
|
|
488
|
-
// Hijack the USER command as we may have some overrides
|
|
489
|
-
data = fmt.Sprintf(
|
|
490
|
-
"USER %s 0 * :%s",
|
|
491
|
-
client.IrcState.Username,
|
|
492
|
-
client.IrcState.RealName,
|
|
493
|
-
)
|
|
494
|
-
} else if strings.HasPrefix(strings.ToUpper(data), "QUIT ") {
|
|
495
|
-
client.SeenQuit = true
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
message, _ := irc.ParseLine(data)
|
|
499
|
-
|
|
500
|
-
hook := &HookIrcLine{
|
|
501
|
-
Client: client,
|
|
502
|
-
UpstreamConfig: upstreamConfig,
|
|
503
|
-
Line: data,
|
|
504
|
-
Message: message,
|
|
505
|
-
ToServer: true,
|
|
506
|
-
}
|
|
507
|
-
hook.Dispatch("irc.line")
|
|
508
|
-
if hook.Halt {
|
|
509
|
-
return
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
// Plugins may have modified the data
|
|
513
|
-
data = hook.Line
|
|
514
|
-
|
|
515
|
-
c.TrafficLog(true, false, data)
|
|
516
|
-
data = utf8ToOther(data, client.Encoding)
|
|
517
|
-
if data == "" {
|
|
518
|
-
client.Log(1, "Failed to encode into '%s'. Dropping data", c.Encoding)
|
|
519
|
-
return
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
if client.upstream != nil {
|
|
523
|
-
client.upstream.Write([]byte(data + "\r\n"))
|
|
524
|
-
} else {
|
|
525
|
-
client.Log(2, "Tried sending data upstream before connected")
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
func (c *Client) handleLineFromUpstream(data string) {
|
|
530
|
-
client := c
|
|
531
|
-
upstreamConfig := c.UpstreamConfig
|
|
532
|
-
|
|
533
|
-
message, _ := irc.ParseLine(data)
|
|
534
|
-
|
|
535
|
-
hook := &HookIrcLine{
|
|
536
|
-
Client: client,
|
|
537
|
-
UpstreamConfig: upstreamConfig,
|
|
538
|
-
Line: data,
|
|
539
|
-
Message: message,
|
|
540
|
-
ToServer: false,
|
|
541
|
-
}
|
|
542
|
-
hook.Dispatch("irc.line")
|
|
543
|
-
if hook.Halt {
|
|
544
|
-
return
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
// Plugins may have modified the data
|
|
548
|
-
data = hook.Line
|
|
549
|
-
|
|
550
|
-
if data == "" {
|
|
551
|
-
return
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
data = ensureUtf8(data, client.Encoding)
|
|
555
|
-
if data == "" {
|
|
556
|
-
client.Log(1, "Failed to decode as 'UTF-8'. Dropping data")
|
|
557
|
-
return
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
data = client.ProcessLineFromUpstream(data)
|
|
561
|
-
if data == "" {
|
|
562
|
-
return
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
client.SendClientSignal("data", data)
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
func typeOfErr(err error) string {
|
|
569
|
-
if err == nil {
|
|
570
|
-
return ""
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
if netError, ok := err.(net.Error); ok && netError.Timeout() {
|
|
574
|
-
return "timeout"
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
switch t := err.(type) {
|
|
578
|
-
case *proxy.ConnError:
|
|
579
|
-
switch t.Type {
|
|
580
|
-
case "conn_reset":
|
|
581
|
-
return ""
|
|
582
|
-
case "conn_refused":
|
|
583
|
-
return "refused"
|
|
584
|
-
case "not_found":
|
|
585
|
-
return "unknown_host"
|
|
586
|
-
case "conn_timeout":
|
|
587
|
-
return "timeout"
|
|
588
|
-
default:
|
|
589
|
-
return ""
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
case *net.OpError:
|
|
593
|
-
if t.Op == "dial" {
|
|
594
|
-
return "unknown_host"
|
|
595
|
-
} else if t.Op == "read" {
|
|
596
|
-
return "refused"
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
case syscall.Errno:
|
|
600
|
-
if t == syscall.ECONNREFUSED {
|
|
601
|
-
return "refused"
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
return ""
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
func (c *Client) readUpstream() {
|
|
609
|
-
client := c
|
|
610
|
-
|
|
611
|
-
// Data from upstream to client
|
|
612
|
-
go func() {
|
|
613
|
-
reader := bufio.NewReader(client.upstream)
|
|
614
|
-
for {
|
|
615
|
-
data, err := reader.ReadString('\n')
|
|
616
|
-
if err != nil {
|
|
617
|
-
break
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
data = strings.Trim(data, "\n\r")
|
|
621
|
-
client.UpstreamRecv <- data
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
close(client.UpstreamRecv)
|
|
625
|
-
client.upstream.Close()
|
|
626
|
-
client.upstream = nil
|
|
627
|
-
|
|
628
|
-
if client.IrcState.RemotePort > 0 {
|
|
629
|
-
c.Gateway.identdServ.RemoveIdent(client.IrcState.LocalPort, client.IrcState.RemotePort, "")
|
|
630
|
-
}
|
|
631
|
-
}()
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
// Handle lines sent from the client
|
|
635
|
-
func (c *Client) clientLineWorker() {
|
|
636
|
-
for {
|
|
637
|
-
shouldQuit, _ := c.handleDataLine()
|
|
638
|
-
if shouldQuit {
|
|
639
|
-
break
|
|
640
|
-
}
|
|
641
|
-
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
c.Log(1, "leaving clientLineWorker")
|
|
645
|
-
}
|
|
646
|
-
|
|
647
|
-
func (c *Client) handleDataLine() (shouldQuit bool, hadErr bool) {
|
|
648
|
-
defer func() {
|
|
649
|
-
if err := recover(); err != nil {
|
|
650
|
-
c.Log(3, fmt.Sprint("Error handling data ", err))
|
|
651
|
-
fmt.Println("Error handling data", err)
|
|
652
|
-
debug.PrintStack()
|
|
653
|
-
shouldQuit = false
|
|
654
|
-
hadErr = true
|
|
655
|
-
}
|
|
656
|
-
}()
|
|
657
|
-
|
|
658
|
-
// We only want to send data upstream if we have an upstream connection
|
|
659
|
-
upstreamSend := c.UpstreamSend
|
|
660
|
-
if c.upstream == nil {
|
|
661
|
-
upstreamSend = nil
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
select {
|
|
665
|
-
case clientData, ok := <-c.ThrottledRecv.Output:
|
|
666
|
-
if !ok {
|
|
667
|
-
c.Log(1, "client.Recv closed")
|
|
668
|
-
if !c.SeenQuit && c.Gateway.Config.SendQuitOnClientClose != "" && c.State == ClientStateEnding {
|
|
669
|
-
c.processLineToUpstream("QUIT :" + c.Gateway.Config.SendQuitOnClientClose)
|
|
670
|
-
}
|
|
671
|
-
|
|
672
|
-
c.StartShutdown("client_closed")
|
|
673
|
-
|
|
674
|
-
if c.upstream != nil {
|
|
675
|
-
c.upstream.Close()
|
|
676
|
-
}
|
|
677
|
-
return true, false
|
|
678
|
-
}
|
|
679
|
-
c.Log(1, "in c.ThrottledRecv.Output")
|
|
680
|
-
c.TrafficLog(false, true, clientData)
|
|
681
|
-
|
|
682
|
-
clientLine, err := c.ProcessLineFromClient(clientData)
|
|
683
|
-
if err == nil && clientLine != "" {
|
|
684
|
-
c.UpstreamSend <- clientLine
|
|
685
|
-
}
|
|
686
|
-
|
|
687
|
-
case line, ok := <-upstreamSend:
|
|
688
|
-
if !ok {
|
|
689
|
-
c.Log(1, "client.UpstreamSend closed")
|
|
690
|
-
return true, false
|
|
691
|
-
}
|
|
692
|
-
c.Log(1, "in .UpstreamSend")
|
|
693
|
-
c.processLineToUpstream(line)
|
|
694
|
-
|
|
695
|
-
case upstreamData, ok := <-c.UpstreamRecv:
|
|
696
|
-
if !ok {
|
|
697
|
-
c.Log(1, "client.UpstreamRecv closed")
|
|
698
|
-
c.SendClientSignal("state", "closed")
|
|
699
|
-
c.StartShutdown("upstream_closed")
|
|
700
|
-
return true, false
|
|
701
|
-
}
|
|
702
|
-
c.Log(1, "in .UpstreamRecv")
|
|
703
|
-
c.TrafficLog(true, true, upstreamData)
|
|
704
|
-
|
|
705
|
-
c.handleLineFromUpstream(upstreamData)
|
|
706
|
-
}
|
|
707
|
-
|
|
708
|
-
return false, false
|
|
709
|
-
}
|
|
710
|
-
|
|
711
|
-
// configureUpstream - Generate an upstream configuration from the information set on the client instance
|
|
712
|
-
func (c *Client) configureUpstream() ConfigUpstream {
|
|
713
|
-
upstreamConfig := ConfigUpstream{}
|
|
714
|
-
upstreamConfig.Hostname = c.DestHost
|
|
715
|
-
upstreamConfig.Port = c.DestPort
|
|
716
|
-
upstreamConfig.TLS = c.DestTLS
|
|
717
|
-
upstreamConfig.Timeout = c.Gateway.Config.GatewayTimeout
|
|
718
|
-
upstreamConfig.Throttle = c.Gateway.Config.GatewayThrottle
|
|
719
|
-
upstreamConfig.WebircPassword = c.Gateway.findWebircPassword(c.DestHost)
|
|
720
|
-
upstreamConfig.Protocol = c.Gateway.Config.GatewayProtocol
|
|
721
|
-
upstreamConfig.LocalAddr = c.Gateway.Config.GatewayLocalAddr
|
|
722
|
-
|
|
723
|
-
return upstreamConfig
|
|
724
|
-
}
|
|
725
|
-
|
|
726
|
-
func (c *Client) buildWebircTags() string {
|
|
727
|
-
str := ""
|
|
728
|
-
for key, val := range c.Tags {
|
|
729
|
-
if str != "" {
|
|
730
|
-
str += " "
|
|
731
|
-
}
|
|
732
|
-
|
|
733
|
-
if val == "" {
|
|
734
|
-
str += key
|
|
735
|
-
} else {
|
|
736
|
-
str += key + "=" + val
|
|
737
|
-
}
|
|
738
|
-
}
|
|
739
|
-
|
|
740
|
-
return str
|
|
741
|
-
}
|