grim/gobnc

a bunch more work
draft
2018-08-09, Gary Kramlich
e1cc64a24bfe
Parents d166328a1bc8
Children 289b2d7f32b6
a bunch more work
--- a/irc/irc.go Wed Aug 08 23:02:21 2018 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,13 +0,0 @@
-package irc
-
-import (
- "regexp"
-)
-
-var (
- regexNick = regexp.MustCompile(`^[A-Za-z][A-Za-z0-9-[\]\^{}]*`)
-)
-
-func NickValid(nick string) bool {
- return regexNick.MatchString(nick)
-}
--- a/irc/irc_test.go Wed Aug 08 23:02:21 2018 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,29 +0,0 @@
-package irc
-
-import (
- "testing"
-
- . "github.com/onsi/gomega"
-)
-
-func TestNickValid(t *testing.T) {
- g := NewGomegaWithT(t)
- tests := map[string]bool{
- "": false,
- "a": true,
- "A": true,
- "*": false,
- "aa": true,
- "Aa": true,
- "aA": true,
- "1a": false,
- "1A": false,
- "a1": true,
- "A1": true,
- "A^": true,
- }
-
- for nick, exp := range tests {
- g.Expect(NickValid(nick)).To(Equal(exp))
- }
-}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/irc/types.go Thu Aug 09 01:52:23 2018 -0500
@@ -0,0 +1,65 @@
+package irc
+
+import (
+ "errors"
+ "regexp"
+ "strings"
+)
+
+var (
+ ErrMalformedMessage = errors.New("malformed message")
+
+ ErrInvalidNick = errors.New("invalid nick")
+ regexNick = regexp.MustCompile(`^[A-Za-z][A-Za-z0-9-[\]\^{}]*`)
+)
+
+type Unmarshaler interface {
+ UnmarshalIRC([]byte) error
+}
+
+type Marshaller interface {
+ MarshalIRC() ([]byte, error)
+}
+
+// Message represents a message in the IRC protocol.
+type Message string
+
+func (m *Message) UnmarshalIRC(b []byte) error {
+ s := string(b)
+ if !strings.HasPrefix(s, ":") {
+ return ErrMalformedMessage
+ }
+
+ *m = Message(s[1:])
+
+ return nil
+}
+
+func (m *Message) MarshalIRC() ([]byte, error) {
+ return []byte(":" + *m), nil
+}
+
+// Nick represents a nick in the IRC protocol.
+type Nick string
+
+func (n *Nick) UnmarshalIRC(b []byte) error {
+ if !regexNick.Match(b) {
+ return ErrInvalidNick
+ }
+
+ *n = Nick(string(b))
+
+ return nil
+}
+
+func (n *Nick) MarshalNick() ([]byte, error) {
+ if !n.Valid() {
+ return nil, ErrInvalidNick
+ }
+
+ return []byte(*n), nil
+}
+
+func (n *Nick) Valid() bool {
+ return regexNick.MatchString(string(*n))
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/irc/types_test.go Thu Aug 09 01:52:23 2018 -0500
@@ -0,0 +1,82 @@
+package irc
+
+import (
+ "testing"
+
+ . "github.com/onsi/gomega"
+)
+
+func TestMessageMarshal(t *testing.T) {
+ g := NewGomegaWithT(t)
+
+ var m Message = "foo"
+
+ d, e := m.MarshalIRC()
+ g.Expect(e).To(BeNil())
+ g.Expect(d).To(Equal([]byte(":foo")))
+}
+
+func TestMessageUnmarshal(t *testing.T) {
+ g := NewGomegaWithT(t)
+
+ var m Message
+ var e error
+
+ e = m.UnmarshalIRC([]byte(":foo"))
+ g.Expect(e).To(BeNil())
+ g.Expect(m).To(Equal(Message("foo")))
+
+ e = m.UnmarshalIRC([]byte("foo"))
+ g.Expect(e).To(MatchError(ErrMalformedMessage))
+}
+
+// Nick tests
+var (
+ nickTests = map[string]bool{
+ "": false,
+ "a": true,
+ "A": true,
+ "*": false,
+ "aa": true,
+ "Aa": true,
+ "aA": true,
+ "1a": false,
+ "1A": false,
+ "a1": true,
+ "A1": true,
+ "A^": true,
+ }
+)
+
+func TestNickMarshal(t *testing.T) {
+ g := NewGomegaWithT(t)
+
+ for nick, exp := range nickTests {
+ var n Nick = Nick(nick)
+
+ mn, e := n.MarshalNick()
+
+ if exp {
+ g.Expect(e).To(BeNil())
+ g.Expect(mn).To(Equal([]byte(nick)))
+ } else {
+ g.Expect(e).To(MatchError(ErrInvalidNick))
+ }
+ }
+}
+
+func TestNickUnmarshal(t *testing.T) {
+ g := NewGomegaWithT(t)
+
+ var n Nick
+
+ for nick, exp := range nickTests {
+ err := n.UnmarshalIRC([]byte(nick))
+ if exp {
+ g.Expect(err).To(BeNil())
+ } else {
+ g.Expect(err).To(Not(BeNil()))
+ }
+ }
+
+}
--- a/main.go Wed Aug 08 23:02:21 2018 -0500
+++ b/main.go Thu Aug 09 01:52:23 2018 -0500
@@ -1,5 +1,21 @@
package main
+import (
+ "fmt"
+
+ "bitbucket.org/rw_grim/gobnc/server"
+)
+
func main() {
+ cfg := &server.Config{
+ Port: 6667,
+ Nick: "rw_grim",
+ Password: "hunter2",
+ }
+ s := server.NewServer(cfg)
+
+ if err := s.Listen(); err != nil {
+ fmt.Printf("error: %s\n", err)
+ }
}
--- a/server/config.go Wed Aug 08 23:02:21 2018 -0500
+++ b/server/config.go Thu Aug 09 01:52:23 2018 -0500
@@ -9,24 +9,28 @@
// Config handles the configuration data for a server. It may be marshalled to
// yaml.
type Config struct {
- ListenAddr string `yaml:"listen-addr"`
- Port int `yaml:"port"`
- Nick string `yaml:"nick"`
- Password string `yaml:"password"`
+ Addr string `yaml:"addr"`
+ Port int `yaml:"port"`
+ Nick irc.Nick `yaml:"nick"`
+ Password string `yaml:"password"`
}
func (c *Config) Valid() error {
- if c.ListenAddr == "" {
- c.ListenAddr = "127.0.0.1"
+ if c.Addr == "" {
+ c.Addr = "127.0.0.1"
}
if c.Port <= 0 || c.Port > 65535 {
return fmt.Errorf("Invalid port %d: must be between 1 and 65535", c.Port)
}
- if !irc.NickValid(c.Nick) {
+ if !c.Nick.Valid() {
return fmt.Errorf("Invalid nick '%s'", c.Nick)
}
return nil
}
+
+func (c *Config) ListenAddress() string {
+ return fmt.Sprintf("%s:%d", c.Addr, c.Port)
+}
--- a/server/config_test.go Wed Aug 08 23:02:21 2018 -0500
+++ b/server/config_test.go Thu Aug 09 01:52:23 2018 -0500
@@ -14,7 +14,7 @@
Nick: "foo",
}
g.Expect(cfg.Valid()).To(BeNil())
- g.Expect(cfg.ListenAddr).To(Equal("127.0.0.1"))
+ g.Expect(cfg.Addr).To(Equal("127.0.0.1"))
}
func TestConfigValidPort(t *testing.T) {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/server/proxy.go Thu Aug 09 01:52:23 2018 -0500
@@ -0,0 +1,32 @@
+package server
+
+import (
+ "bufio"
+ "fmt"
+ "net"
+)
+
+type Proxy struct {
+ server *Server
+ conn net.Conn
+}
+
+func NewProxy(server *Server, conn net.Conn) *Proxy {
+ return &Proxy{
+ server: server,
+ conn: conn,
+ }
+}
+
+func (p *Proxy) Run() {
+ for {
+ msg, err := bufio.NewReader(p.conn).ReadString('\n')
+ if err != nil {
+ fmt.Printf("error: %s\n", err)
+
+ return
+ }
+
+ fmt.Printf("msg: %s\n", msg)
+ }
+}
--- a/server/server.go Wed Aug 08 23:02:21 2018 -0500
+++ b/server/server.go Thu Aug 09 01:52:23 2018 -0500
@@ -1,7 +1,48 @@
package server
-type Server struct{}
+import (
+ "fmt"
+ "net"
+ "sync"
+)
+
+type Server struct {
+ cfg *Config
+ sock net.Listener
+
+ proxies map[string]*Proxy
+ proxyLock sync.Mutex
+}
+
+func NewServer(cfg *Config) *Server {
+ return &Server{
+ cfg: cfg,
+ proxies: map[string]*Proxy{},
+ }
+}
-func (s *Server) Listen() {
+func (s *Server) Listen() error {
+ if s.sock != nil {
+ return fmt.Errorf("server already listening")
+ }
+
+ sock, err := net.Listen("tcp", s.cfg.ListenAddress())
+ if err != nil {
+ return err
+ }
+
+ s.sock = sock
+ defer s.sock.Close()
+ for {
+ conn, err := s.sock.Accept()
+ if err != nil {
+ return err
+ }
+
+ proxy := NewProxy(s, conn)
+ go proxy.Run()
+ }
+
+ return nil
}