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 }