You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ircconn.go 2.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. package irc
  2. import (
  3. "bufio"
  4. "bytes"
  5. "net"
  6. "unicode/utf8"
  7. "github.com/gorilla/websocket"
  8. "github.com/goshuirc/irc-go/ircmsg"
  9. "github.com/oragono/oragono/irc/utils"
  10. )
  11. const (
  12. maxReadQBytes = ircmsg.MaxlenTagsFromClient + 512 + 1024
  13. )
  14. var (
  15. crlf = []byte{'\r', '\n'}
  16. )
  17. // IRCConn abstracts away the distinction between a regular
  18. // net.Conn (which includes both raw TCP and TLS) and a websocket.
  19. // it doesn't expose Read and Write because websockets are message-oriented,
  20. // not stream-oriented.
  21. type IRCConn interface {
  22. UnderlyingConn() *utils.ProxiedConnection
  23. Write([]byte) error
  24. WriteBuffers([][]byte) error
  25. ReadLine() (line []byte, err error)
  26. Close() error
  27. }
  28. // IRCStreamConn is an IRCConn over a regular stream connection.
  29. type IRCStreamConn struct {
  30. conn *utils.ProxiedConnection
  31. reader *bufio.Reader
  32. }
  33. func NewIRCStreamConn(conn *utils.ProxiedConnection) *IRCStreamConn {
  34. return &IRCStreamConn{
  35. conn: conn,
  36. }
  37. }
  38. func (cc *IRCStreamConn) UnderlyingConn() *utils.ProxiedConnection {
  39. return cc.conn
  40. }
  41. func (cc *IRCStreamConn) Write(buf []byte) (err error) {
  42. _, err = cc.conn.Write(buf)
  43. return
  44. }
  45. func (cc *IRCStreamConn) WriteBuffers(buffers [][]byte) (err error) {
  46. // on Linux, with a plaintext TCP or Unix domain socket,
  47. // the Go runtime will optimize this into a single writev(2) call:
  48. _, err = (*net.Buffers)(&buffers).WriteTo(cc.conn)
  49. return
  50. }
  51. func (cc *IRCStreamConn) ReadLine() (line []byte, err error) {
  52. // lazy initialize the reader in case the IP is banned
  53. if cc.reader == nil {
  54. cc.reader = bufio.NewReaderSize(cc.conn, maxReadQBytes)
  55. }
  56. var isPrefix bool
  57. line, isPrefix, err = cc.reader.ReadLine()
  58. if isPrefix {
  59. return nil, errReadQ
  60. }
  61. line = bytes.TrimSuffix(line, crlf)
  62. return
  63. }
  64. func (cc *IRCStreamConn) Close() (err error) {
  65. return cc.conn.Close()
  66. }
  67. // IRCWSConn is an IRCConn over a websocket.
  68. type IRCWSConn struct {
  69. conn *websocket.Conn
  70. }
  71. func NewIRCWSConn(conn *websocket.Conn) IRCWSConn {
  72. return IRCWSConn{conn: conn}
  73. }
  74. func (wc IRCWSConn) UnderlyingConn() *utils.ProxiedConnection {
  75. pConn, ok := wc.conn.UnderlyingConn().(*utils.ProxiedConnection)
  76. if ok {
  77. return pConn
  78. } else {
  79. // this can't happen
  80. return nil
  81. }
  82. }
  83. func (wc IRCWSConn) Write(buf []byte) (err error) {
  84. buf = bytes.TrimSuffix(buf, crlf)
  85. // there's not much we can do about this;
  86. // silently drop the message
  87. if !utf8.Valid(buf) {
  88. return nil
  89. }
  90. return wc.conn.WriteMessage(websocket.TextMessage, buf)
  91. }
  92. func (wc IRCWSConn) WriteBuffers(buffers [][]byte) (err error) {
  93. for _, buf := range buffers {
  94. err = wc.Write(buf)
  95. if err != nil {
  96. return
  97. }
  98. }
  99. return
  100. }
  101. func (wc IRCWSConn) ReadLine() (line []byte, err error) {
  102. for {
  103. var messageType int
  104. messageType, line, err = wc.conn.ReadMessage()
  105. // on empty message or non-text message, try again, block if necessary
  106. if err != nil || (messageType == websocket.TextMessage && len(line) != 0) {
  107. return
  108. }
  109. }
  110. }
  111. func (wc IRCWSConn) Close() (err error) {
  112. return wc.conn.Close()
  113. }