github.com/Psiphon-Labs/psiphon-tunnel-core@v2.0.28+incompatible/psiphon/transferstats/hostname.go (about) 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 20 package transferstats 21 22 import ( 23 "bufio" 24 "bytes" 25 "net/http" 26 ) 27 28 // getHostname attempts to determine the hostname of the server from the request data. 29 func getHostname(buffer []byte) (hostname string, ok bool) { 30 // Check if this is a HTTP request 31 bufferReader := bufio.NewReader(bytes.NewReader(buffer)) 32 httpReq, httpErr := http.ReadRequest(bufferReader) 33 if httpErr == nil { 34 return httpReq.Host, true 35 } 36 37 // Check if it's a TLS request 38 hostname, ok = getTLSHostname(buffer) 39 40 return 41 } 42 43 /* 44 TLS Record Protocol: 45 Record layer content type (1B): handshake is 22: 22 46 SSL version (2B): SSL3 is {3,0}, TLS 1.0 is {3,1}, TLS 1.2 is {3,2} TLS 1.2 is {3,3}; seems to typically be {3,1}: 3 1 47 Plaintext envelope length (2B): maximum of 2^14, but usually much smaller: 2 0 48 TLS Handshake Protocol: 49 Handshake type (1B): client hello is 1: 1 50 Handshake length (3B): will always be 4 bytes smaller than the envelope length (because the envelope length counts the handshake type and the bytes of this length, but this length does not): 0 1 252 51 Protocol version (2B): see "SSL version" above; seems to typically be {3,3}: 3 3 52 Random data (32B): 131 89 82 204 123 41 188 215 100 17 206 199 21 202 81 139 145 138 26 95 144 92 183 186 186 36 234 203 207 196 238 115 53 Session ID length (1B): 32 54 Session ID: 80 149 254 248 148 156 249 42 173 29 7 58 44 0 92 173 203 11 94 252 117 212 24 20 47 131 135 204 150 37 247 229 55 Cipher suites length (2B): 0 24 56 Cipher suites: e.g., TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 is 0xc02b: 192 43 192 47 192 10 192 9 192 19 192 20 0 51 0 50 0 57 0 47 0 53 0 10 57 Compression methods length (1B): 1 58 Compression methods: 0 59 Extensions length (2B): 1 155 60 ...some number of extension defs here... 61 Extension type (2B): server_name is 0x0000: 0 0 62 Extension length (2B): 0 25 63 SNI list length (2B): 0 23 64 SNI type (1B): host_name is 0x00: 0 65 SNI hostname length (2B): 0 20 66 SNI hostname: e.g., upload.wikimedia.org: 117 112 108 111 97 100 46 119 105 107 105 109 101 100 105 97 46 111 114 103 67 ...more extensions... 68 */ 69 // getTLSHostname attempts to interpret the buffer as a TLS client hello and 70 // extract the SNI hostname from it. 71 func getTLSHostname(buffer []byte) (hostname string, ok bool) { 72 bufLen := uint32(len(buffer)) 73 74 // If the buffer is smaller than this, it can't possibly be a TLS client hello. 75 if bufLen < 60 { 76 return 77 } 78 79 pos := uint32(0) 80 81 // Make sure this is a handshake 82 if buffer[pos] != 22 { 83 return 84 } 85 pos += 1 86 87 // We'll check the first byte of the SSL version 88 // NOTE: Not future proof. 89 if buffer[pos] != 3 { 90 return 91 } 92 pos += 2 93 94 plaintextLen := uint32(buffer[pos])<<8 | uint32(buffer[pos+1]) 95 if plaintextLen < (60-3) || plaintextLen > bufLen { 96 return 97 } 98 pos += 2 99 100 // Make sure handshake type is client hello 101 if buffer[pos] != 1 { 102 return 103 } 104 pos += 1 105 106 // Make sure handshake length is expected size. 107 handshakeLen := uint32(buffer[pos])<<16 | uint32(buffer[pos+1])<<8 | uint32(buffer[pos+2]) 108 if handshakeLen+4 != plaintextLen { 109 return 110 } 111 pos += 3 112 113 // Check the first byte of protocol version 114 // NOTE: Not future proof. 115 if buffer[pos] != 3 { 116 return 117 } 118 pos += 2 119 120 // Skip 32 bytes of random data 121 pos += 32 122 123 sessionIDLen := uint32(buffer[pos]) 124 pos += 1 125 if sessionIDLen > bufLen-pos { 126 return 127 } 128 pos += sessionIDLen 129 130 // At this point we can't trust that our initial minimum length check will 131 // save us from going out-of-bounds on the buffer slice, so we'll have to 132 // do buffer length checks as we go. 133 134 // Skip over the cipher suites. In theory we could check them, but we're not going to. 135 if pos+2 > bufLen { 136 return 137 } 138 cipherSuitesLen := uint32(buffer[pos])<<8 | uint32(buffer[pos+1]) 139 pos += 2 140 if cipherSuitesLen > bufLen-pos { 141 return 142 } 143 pos += cipherSuitesLen 144 145 // Skip compression methods 146 if pos+1 > bufLen { 147 return 148 } 149 compressionMethodsLen := uint32(buffer[pos]) 150 pos += 1 151 if compressionMethodsLen > bufLen-pos { 152 return 153 } 154 pos += compressionMethodsLen 155 156 // Extensions 157 if pos+2 > bufLen { 158 return 159 } 160 extensionsLen := uint32(buffer[pos])<<8 | uint32(buffer[pos+1]) 161 pos += 2 162 if extensionsLen > bufLen-pos { 163 return 164 } 165 166 // Go through each extension entry, looking for the SNI 167 for { 168 if pos+2 > bufLen { 169 return 170 } 171 extensionType := uint32(buffer[pos])<<8 | uint32(buffer[pos+1]) 172 pos += 2 173 174 if pos+2 > bufLen { 175 return 176 } 177 extensionLen := uint32(buffer[pos])<<8 | uint32(buffer[pos+1]) 178 pos += 2 179 180 // server_name extension type is 0x0000 181 if extensionType != 0 { 182 pos += extensionLen 183 continue 184 } 185 186 // Basic santiy check on the SNI list length 187 if pos+2 > bufLen { 188 return 189 } 190 sniListLen := uint32(buffer[pos])<<8 | uint32(buffer[pos+1]) 191 pos += 2 192 if sniListLen > extensionLen { 193 return 194 } 195 196 // Check the SNI type. There's only one allowed value in the spec: hostname (0x00) 197 if pos+1 > bufLen { 198 return 199 } 200 sniType := uint32(buffer[pos]) 201 pos += 1 202 if sniType != 0 { 203 return 204 } 205 206 // Finally, the goal: the hostname 207 if pos+2 > bufLen { 208 return 209 } 210 hostnameLen := uint32(buffer[pos])<<8 | uint32(buffer[pos+1]) 211 pos += 2 212 if hostnameLen > bufLen-pos { 213 return 214 } 215 hostname = string(buffer[pos : pos+hostnameLen]) 216 return hostname, true 217 } 218 }