github.com/psiphon-labs/psiphon-tunnel-core@v2.0.28+incompatible/psiphon/server/utils.go (about)

     1  /*
     2   * Copyright (c) 2016, 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  
    20  package server
    21  
    22  import (
    23  	"fmt"
    24  	"io"
    25  	"strings"
    26  	"sync/atomic"
    27  )
    28  
    29  // IntentionalPanicError is an error type that is used
    30  // when calling panic() in a situation where recovers
    31  // should propagate the panic.
    32  type IntentionalPanicError struct {
    33  	message string
    34  }
    35  
    36  // NewIntentionalPanicError creates a new IntentionalPanicError.
    37  func NewIntentionalPanicError(errorMessage string) error {
    38  	return IntentionalPanicError{
    39  		message: fmt.Sprintf("intentional panic error: %s", errorMessage)}
    40  }
    41  
    42  // Error implements the error interface.
    43  func (err IntentionalPanicError) Error() string {
    44  	return err.message
    45  }
    46  
    47  // PanickingLogWriter wraps an io.Writer and intentionally
    48  // panics when a Write() fails.
    49  type PanickingLogWriter struct {
    50  	name   string
    51  	writer io.Writer
    52  }
    53  
    54  // NewPanickingLogWriter creates a new PanickingLogWriter.
    55  func NewPanickingLogWriter(
    56  	name string, writer io.Writer) *PanickingLogWriter {
    57  
    58  	return &PanickingLogWriter{
    59  		name:   name,
    60  		writer: writer,
    61  	}
    62  }
    63  
    64  // Write implements the io.Writer interface.
    65  func (w *PanickingLogWriter) Write(p []byte) (n int, err error) {
    66  	n, err = w.writer.Write(p)
    67  	if err != nil {
    68  		panic(
    69  			NewIntentionalPanicError(
    70  				fmt.Sprintf("fatal write to %s failed: %s", w.name, err)))
    71  	}
    72  	return
    73  }
    74  
    75  func min(a, b int) int {
    76  	if a < b {
    77  		return a
    78  	}
    79  	return b
    80  }
    81  
    82  func greaterThanSwapInt64(addr *int64, new int64) bool {
    83  	old := atomic.LoadInt64(addr)
    84  	if new > old {
    85  		return atomic.CompareAndSwapInt64(addr, old, new)
    86  	}
    87  	return false
    88  }
    89  
    90  var expectedTunnelIOErrorSubstrings = []string{
    91  	"EOF",
    92  	"use of closed network connection",
    93  	"connection reset by peer",
    94  	"connection has closed",
    95  	"broken pipe",
    96  	"i/o timeout",
    97  	"deadline exceeded",
    98  	"NetworkIdleTimeout",
    99  	"PeerGoingAway",
   100  	"Application error 0x0",
   101  	"No recent network activity",
   102  }
   103  
   104  // isExpectedTunnelIOError checks if the error indicates failure due to tunnel
   105  // I/O timing out, use of a closed tunnel, etc. This is used to avoid logging
   106  // noise in cases where sending messages through the tunnel fail due regular,
   107  // expected tunnel failure conditions.
   108  //
   109  // Limitations introduced by error type wrapping and lack of common error
   110  // types across all network protcol layers means this function uses
   111  // heuristical error text substring matching and may fall out of sync with new
   112  // protocols/error messages. As such, this function should only be used for
   113  // the intended log noise purpose.
   114  func isExpectedTunnelIOError(err error) bool {
   115  	errString := err.Error()
   116  	for _, substring := range expectedTunnelIOErrorSubstrings {
   117  		if strings.Contains(errString, substring) {
   118  			return true
   119  		}
   120  	}
   121  	return false
   122  }