1/* 2 * Copyright (c) 2014, Yawning Angel <yawning at schwanenlied dot me> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * * Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * 11 * * Redistributions in binary form must reproduce the above copyright notice, 12 * this list of conditions and the following disclaimer in the documentation 13 * and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 19 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 * POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28package main 29 30import ( 31 "errors" 32 "fmt" 33 "net" 34 "net/url" 35 "os" 36 "strconv" 37 38 "git.torproject.org/pluggable-transports/goptlib.git" 39) 40 41// This file contains things that probably should be in goptlib but are not 42// yet or are not finalized. 43 44func ptEnvError(msg string) error { 45 line := []byte(fmt.Sprintf("ENV-ERROR %s\n", msg)) 46 _, _ = pt.Stdout.Write(line) 47 return errors.New(msg) 48} 49 50func ptProxyError(msg string) error { 51 line := []byte(fmt.Sprintf("PROXY-ERROR %s\n", msg)) 52 _, _ = pt.Stdout.Write(line) 53 return errors.New(msg) 54} 55 56func ptProxyDone() { 57 line := []byte("PROXY DONE\n") 58 _, _ = pt.Stdout.Write(line) 59} 60 61func ptIsClient() (bool, error) { 62 clientEnv := os.Getenv("TOR_PT_CLIENT_TRANSPORTS") 63 serverEnv := os.Getenv("TOR_PT_SERVER_TRANSPORTS") 64 if clientEnv != "" && serverEnv != "" { 65 return false, ptEnvError("TOR_PT_[CLIENT,SERVER]_TRANSPORTS both set") 66 } else if clientEnv != "" { 67 return true, nil 68 } else if serverEnv != "" { 69 return false, nil 70 } 71 return false, errors.New("not launched as a managed transport") 72} 73 74func ptGetProxy() (*url.URL, error) { 75 specString := os.Getenv("TOR_PT_PROXY") 76 if specString == "" { 77 return nil, nil 78 } 79 spec, err := url.Parse(specString) 80 if err != nil { 81 return nil, ptProxyError(fmt.Sprintf("failed to parse proxy config: %s", err)) 82 } 83 84 // Validate the TOR_PT_PROXY uri. 85 if !spec.IsAbs() { 86 return nil, ptProxyError("proxy URI is relative, must be absolute") 87 } 88 if spec.Path != "" { 89 return nil, ptProxyError("proxy URI has a path defined") 90 } 91 if spec.RawQuery != "" { 92 return nil, ptProxyError("proxy URI has a query defined") 93 } 94 if spec.Fragment != "" { 95 return nil, ptProxyError("proxy URI has a fragment defined") 96 } 97 98 switch spec.Scheme { 99 case "http": 100 // The most forgiving of proxies. 101 102 case "socks4a": 103 if spec.User != nil { 104 _, isSet := spec.User.Password() 105 if isSet { 106 return nil, ptProxyError("proxy URI specified SOCKS4a and a password") 107 } 108 } 109 110 case "socks5": 111 if spec.User != nil { 112 // UNAME/PASSWD both must be between 1 and 255 bytes long. (RFC1929) 113 user := spec.User.Username() 114 passwd, isSet := spec.User.Password() 115 if len(user) < 1 || len(user) > 255 { 116 return nil, ptProxyError("proxy URI specified a invalid SOCKS5 username") 117 } 118 if !isSet || len(passwd) < 1 || len(passwd) > 255 { 119 return nil, ptProxyError("proxy URI specified a invalid SOCKS5 password") 120 } 121 } 122 123 default: 124 return nil, ptProxyError(fmt.Sprintf("proxy URI has invalid scheme: %s", spec.Scheme)) 125 } 126 127 _, err = resolveAddrStr(spec.Host) 128 if err != nil { 129 return nil, ptProxyError(fmt.Sprintf("proxy URI has invalid host: %s", err)) 130 } 131 132 return spec, nil 133} 134 135// Sigh, pt.resolveAddr() isn't exported. Include our own getto version that 136// doesn't work around #7011, because we don't work with pre-0.2.5.x tor, and 137// all we care about is validation anyway. 138func resolveAddrStr(addrStr string) (*net.TCPAddr, error) { 139 ipStr, portStr, err := net.SplitHostPort(addrStr) 140 if err != nil { 141 return nil, err 142 } 143 144 if ipStr == "" { 145 return nil, net.InvalidAddrError(fmt.Sprintf("address string %q lacks a host part", addrStr)) 146 } 147 if portStr == "" { 148 return nil, net.InvalidAddrError(fmt.Sprintf("address string %q lacks a port part", addrStr)) 149 } 150 ip := net.ParseIP(ipStr) 151 if ip == nil { 152 return nil, net.InvalidAddrError(fmt.Sprintf("not an IP string: %q", ipStr)) 153 } 154 port, err := strconv.ParseUint(portStr, 10, 16) 155 if err != nil { 156 return nil, net.InvalidAddrError(fmt.Sprintf("not a Port string: %q", portStr)) 157 } 158 159 return &net.TCPAddr{IP: ip, Port: int(port), Zone: ""}, nil 160} 161 162// Feature #15435 adds a new env var for determining if Tor keeps stdin 163// open for use in termination detection. 164func ptShouldExitOnStdinClose() bool { 165 return os.Getenv("TOR_PT_EXIT_ON_STDIN_CLOSE") == "1" 166} 167