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  }