TCPConn.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. /*
  2. * Copyright (c) 2015, Psiphon Inc.
  3. * All rights reserved.
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. *
  18. */
  19. package psiphon
  20. import (
  21. "errors"
  22. "net"
  23. "sync"
  24. "time"
  25. )
  26. // TCPConn is a customized TCP connection that:
  27. // - can be interrupted while connecting;
  28. // - implements idle read/write timeouts;
  29. // - can be bound to a specific system device (for Android VpnService
  30. // routing compatibility, for example).
  31. // - implements the psiphon.Conn interface
  32. type TCPConn struct {
  33. net.Conn
  34. mutex sync.Mutex
  35. isClosed bool
  36. closedSignal chan struct{}
  37. interruptible interruptibleTCPSocket
  38. readTimeout time.Duration
  39. writeTimeout time.Duration
  40. }
  41. // NewTCPDialer creates a TCPDialer.
  42. func NewTCPDialer(config *DialConfig) Dialer {
  43. return func(network, addr string) (net.Conn, error) {
  44. if network != "tcp" {
  45. return nil, errors.New("unsupported network type in NewTCPDialer")
  46. }
  47. return DialTCP(addr, config)
  48. }
  49. }
  50. // TCPConn creates a new, connected TCPConn.
  51. func DialTCP(addr string, config *DialConfig) (conn *TCPConn, err error) {
  52. conn, err = interruptibleTCPDial(addr, config)
  53. if err != nil {
  54. return nil, ContextError(err)
  55. }
  56. return conn, nil
  57. }
  58. // SetClosedSignal implements psiphon.Conn.SetClosedSignal.
  59. func (conn *TCPConn) SetClosedSignal(closedSignal chan struct{}) bool {
  60. conn.mutex.Lock()
  61. defer conn.mutex.Unlock()
  62. if conn.isClosed {
  63. return false
  64. }
  65. conn.closedSignal = closedSignal
  66. return true
  67. }
  68. // Close terminates a connected (net.Conn) or connecting (socketFd) TCPConn.
  69. // A mutex is required to support psiphon.Conn.SetClosedSignal concurrency semantics.
  70. func (conn *TCPConn) Close() (err error) {
  71. conn.mutex.Lock()
  72. defer conn.mutex.Unlock()
  73. if !conn.isClosed {
  74. if conn.Conn == nil {
  75. err = interruptibleTCPClose(conn.interruptible)
  76. } else {
  77. err = conn.Conn.Close()
  78. }
  79. conn.isClosed = true
  80. select {
  81. case conn.closedSignal <- *new(struct{}):
  82. default:
  83. }
  84. }
  85. return err
  86. }
  87. // Read wraps standard Read to add an idle timeout. The connection
  88. // is explicitly closed on timeout.
  89. func (conn *TCPConn) Read(buffer []byte) (n int, err error) {
  90. // Note: no mutex on the conn.readTimeout access
  91. if conn.readTimeout != 0 {
  92. err = conn.Conn.SetReadDeadline(time.Now().Add(conn.readTimeout))
  93. if err != nil {
  94. return 0, ContextError(err)
  95. }
  96. }
  97. n, err = conn.Conn.Read(buffer)
  98. if err != nil {
  99. conn.Close()
  100. }
  101. return
  102. }
  103. // Write wraps standard Write to add an idle timeout The connection
  104. // is explicitly closed on timeout.
  105. func (conn *TCPConn) Write(buffer []byte) (n int, err error) {
  106. // Note: no mutex on the conn.writeTimeout access
  107. if conn.writeTimeout != 0 {
  108. err = conn.Conn.SetWriteDeadline(time.Now().Add(conn.writeTimeout))
  109. if err != nil {
  110. return 0, ContextError(err)
  111. }
  112. }
  113. n, err = conn.Conn.Write(buffer)
  114. if err != nil {
  115. conn.Close()
  116. }
  117. return
  118. }
  119. // Override implementation of net.Conn.SetDeadline
  120. func (conn *TCPConn) SetDeadline(t time.Time) error {
  121. return errors.New("net.Conn SetDeadline not supported")
  122. }
  123. // Override implementation of net.Conn.SetReadDeadline
  124. func (conn *TCPConn) SetReadDeadline(t time.Time) error {
  125. return errors.New("net.Conn SetReadDeadline not supported")
  126. }
  127. // Override implementation of net.Conn.SetWriteDeadline
  128. func (conn *TCPConn) SetWriteDeadline(t time.Time) error {
  129. return errors.New("net.Conn SetWriteDeadline not supported")
  130. }