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.

socket.go 5.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. // Copyright (c) 2012-2014 Jeremy Latt
  2. // Copyright (c) 2016-2017 Daniel Oaks <daniel@danieloaks.net>
  3. // released under the MIT license
  4. package irc
  5. import (
  6. "bufio"
  7. "crypto/sha256"
  8. "crypto/tls"
  9. "encoding/hex"
  10. "io"
  11. "net"
  12. "strings"
  13. "sync"
  14. "time"
  15. )
  16. var (
  17. handshakeTimeout, _ = time.ParseDuration("5s")
  18. )
  19. // Socket represents an IRC socket.
  20. type Socket struct {
  21. conn net.Conn
  22. reader *bufio.Reader
  23. MaxSendQBytes uint64
  24. closed bool
  25. closedMutex sync.Mutex
  26. finalData string // what to send when we die
  27. finalDataMutex sync.Mutex
  28. lineToSendExists chan bool
  29. linesToSend []string
  30. linesToSendMutex sync.Mutex
  31. }
  32. // NewSocket returns a new Socket.
  33. func NewSocket(conn net.Conn, maxSendQBytes uint64) Socket {
  34. return Socket{
  35. conn: conn,
  36. reader: bufio.NewReader(conn),
  37. MaxSendQBytes: maxSendQBytes,
  38. lineToSendExists: make(chan bool),
  39. }
  40. }
  41. // Close stops a Socket from being able to send/receive any more data.
  42. func (socket *Socket) Close() {
  43. socket.closedMutex.Lock()
  44. defer socket.closedMutex.Unlock()
  45. if socket.closed {
  46. return
  47. }
  48. socket.closed = true
  49. // force close loop to happen if it hasn't already
  50. go socket.timedFillLineToSendExists(200 * time.Millisecond)
  51. }
  52. // CertFP returns the fingerprint of the certificate provided by the client.
  53. func (socket *Socket) CertFP() (string, error) {
  54. var tlsConn, isTLS = socket.conn.(*tls.Conn)
  55. if !isTLS {
  56. return "", errNotTLS
  57. }
  58. // ensure handehake is performed, and timeout after a few seconds
  59. tlsConn.SetDeadline(time.Now().Add(handshakeTimeout))
  60. err := tlsConn.Handshake()
  61. tlsConn.SetDeadline(time.Time{})
  62. if err != nil {
  63. return "", err
  64. }
  65. peerCerts := tlsConn.ConnectionState().PeerCertificates
  66. if len(peerCerts) < 1 {
  67. return "", errNoPeerCerts
  68. }
  69. rawCert := sha256.Sum256(peerCerts[0].Raw)
  70. fingerprint := hex.EncodeToString(rawCert[:])
  71. return fingerprint, nil
  72. }
  73. // Read returns a single IRC line from a Socket.
  74. func (socket *Socket) Read() (string, error) {
  75. if socket.IsClosed() {
  76. return "", io.EOF
  77. }
  78. lineBytes, err := socket.reader.ReadBytes('\n')
  79. // convert bytes to string
  80. line := string(lineBytes[:])
  81. // read last message properly (such as ERROR/QUIT/etc), just fail next reads/writes
  82. if err == io.EOF {
  83. socket.Close()
  84. }
  85. if err == io.EOF && strings.TrimSpace(line) != "" {
  86. // don't do anything
  87. } else if err != nil {
  88. return "", err
  89. }
  90. return strings.TrimRight(line, "\r\n"), nil
  91. }
  92. // Write sends the given string out of Socket.
  93. func (socket *Socket) Write(data string) error {
  94. if socket.IsClosed() {
  95. return io.EOF
  96. }
  97. socket.linesToSendMutex.Lock()
  98. socket.linesToSend = append(socket.linesToSend, data)
  99. socket.linesToSendMutex.Unlock()
  100. go socket.timedFillLineToSendExists(15 * time.Second)
  101. return nil
  102. }
  103. // timedFillLineToSendExists either sends the note or times out.
  104. func (socket *Socket) timedFillLineToSendExists(duration time.Duration) {
  105. lineToSendTimeout := time.NewTimer(duration)
  106. defer lineToSendTimeout.Stop()
  107. select {
  108. case socket.lineToSendExists <- true:
  109. // passed data successfully
  110. case <-lineToSendTimeout.C:
  111. // timed out send
  112. }
  113. }
  114. // SetFinalData sets the final data to send when the SocketWriter closes.
  115. func (socket *Socket) SetFinalData(data string) {
  116. socket.finalDataMutex.Lock()
  117. socket.finalData = data
  118. socket.finalDataMutex.Unlock()
  119. }
  120. // IsClosed returns whether the socket is closed.
  121. func (socket *Socket) IsClosed() bool {
  122. socket.closedMutex.Lock()
  123. defer socket.closedMutex.Unlock()
  124. return socket.closed
  125. }
  126. // RunSocketWriter starts writing messages to the outgoing socket.
  127. func (socket *Socket) RunSocketWriter() {
  128. for {
  129. // wait for new lines
  130. select {
  131. case <-socket.lineToSendExists:
  132. socket.linesToSendMutex.Lock()
  133. // check if we're closed
  134. if socket.IsClosed() {
  135. socket.linesToSendMutex.Unlock()
  136. break
  137. }
  138. // check whether new lines actually exist or not
  139. if len(socket.linesToSend) < 1 {
  140. socket.linesToSendMutex.Unlock()
  141. continue
  142. }
  143. // check sendq
  144. var sendQBytes uint64
  145. for _, line := range socket.linesToSend {
  146. sendQBytes += uint64(len(line))
  147. if socket.MaxSendQBytes < sendQBytes {
  148. // don't unlock mutex because this break is just to escape this for loop
  149. break
  150. }
  151. }
  152. if socket.MaxSendQBytes < sendQBytes {
  153. socket.SetFinalData("\r\nERROR :SendQ Exceeded\r\n")
  154. socket.linesToSendMutex.Unlock()
  155. break
  156. }
  157. // get all existing data
  158. data := strings.Join(socket.linesToSend, "")
  159. socket.linesToSend = []string{}
  160. socket.linesToSendMutex.Unlock()
  161. // write data
  162. if 0 < len(data) {
  163. _, err := socket.conn.Write([]byte(data))
  164. if err != nil {
  165. break
  166. }
  167. }
  168. }
  169. if socket.IsClosed() {
  170. // error out or we've been closed
  171. break
  172. }
  173. }
  174. // force closure of socket
  175. socket.closedMutex.Lock()
  176. if !socket.closed {
  177. socket.closed = true
  178. }
  179. socket.closedMutex.Unlock()
  180. // write error lines
  181. socket.finalDataMutex.Lock()
  182. if 0 < len(socket.finalData) {
  183. socket.conn.Write([]byte(socket.finalData))
  184. }
  185. socket.finalDataMutex.Unlock()
  186. // close the connection
  187. socket.conn.Close()
  188. // empty the lineToSendExists channel
  189. for 0 < len(socket.lineToSendExists) {
  190. <-socket.lineToSendExists
  191. }
  192. }
  193. // WriteLine writes the given line out of Socket.
  194. func (socket *Socket) WriteLine(line string) error {
  195. return socket.Write(line + "\r\n")
  196. }