|
@@ -0,0 +1,139 @@
|
|
|
+
|
|
|
+using Invercargill;
|
|
|
+
|
|
|
+namespace Astrogate {
|
|
|
+
|
|
|
+ public enum ConnectionType {
|
|
|
+ INVALID,
|
|
|
+ TLS,
|
|
|
+ HTTP,
|
|
|
+ ASTORGATE
|
|
|
+ }
|
|
|
+
|
|
|
+ public class HeaderReader {
|
|
|
+
|
|
|
+ private static int MIN_TLS_SNI_DATA_SIZE = 9;
|
|
|
+
|
|
|
+ public static async BinaryData read_sample(InputStream stream) throws Error{
|
|
|
+ var buffer = new uint8[8192];
|
|
|
+ size_t bytes_read = yield stream.read_async(buffer);
|
|
|
+ buffer.length = (int)bytes_read;
|
|
|
+ return new BinaryData.from_byte_array(buffer);
|
|
|
+ }
|
|
|
+
|
|
|
+ public static ConnectionType determine_type(BinaryData data) {
|
|
|
+ // TLS
|
|
|
+ if(data.first_or_default() == 22) {
|
|
|
+ return ConnectionType.TLS;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Astrogate
|
|
|
+ var ast_header = new BinaryData();
|
|
|
+ ast_header.append_string("astrogate-dial: ", true);
|
|
|
+ if(ast_header.equals(data.take(16))) {
|
|
|
+ return ConnectionType.ASTORGATE;
|
|
|
+ }
|
|
|
+
|
|
|
+ // HTTP
|
|
|
+ var str_data = data.to_raw_string();
|
|
|
+ var id_start = str_data.index_of("HTTP/");
|
|
|
+ if(id_start != -1) {
|
|
|
+ var headers = str_data.split("\r\n");
|
|
|
+ foreach (var header in headers) {
|
|
|
+ if(header.has_prefix("Host:")) {
|
|
|
+ return ConnectionType.HTTP;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return ConnectionType.INVALID;
|
|
|
+ }
|
|
|
+
|
|
|
+ public static string? read_name(BinaryData data, ConnectionType type) {
|
|
|
+ switch(type) {
|
|
|
+ case ConnectionType.HTTP:
|
|
|
+ return read_http_host(data);
|
|
|
+ case ConnectionType.ASTORGATE:
|
|
|
+ return read_astrogate_name(data);
|
|
|
+ case ConnectionType.TLS:
|
|
|
+ return read_tls_sni(data);
|
|
|
+ default:
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public static string? read_http_host(BinaryData data) {
|
|
|
+ var str_data = data.to_raw_string();
|
|
|
+ var id_start = str_data.index_of("HTTP/", 0);
|
|
|
+ if(id_start != -1) {
|
|
|
+ var headers = str_data.split("\r\n");
|
|
|
+ foreach (var header in headers) {
|
|
|
+ if(header.has_prefix("Host:")) {
|
|
|
+ var host = header[5:header.length].chomp().chug();
|
|
|
+ return host.split(":", 2)[0];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ public static string? read_astrogate_name(BinaryData data) {
|
|
|
+ var ast_header = new BinaryData();
|
|
|
+ ast_header.append_string("astrogate-dial: ", true);
|
|
|
+ if(!ast_header.equals(data.take(16))) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ var str_data = data.to_escaped_string();
|
|
|
+ var header = str_data.split("\n", 2);
|
|
|
+ if(header.length != 2) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ return header[0][16:header.length];
|
|
|
+ }
|
|
|
+
|
|
|
+ public static string? read_tls_sni(BinaryData data) {
|
|
|
+ var buffer = data.to_array();
|
|
|
+ while(buffer.length >= MIN_TLS_SNI_DATA_SIZE) {
|
|
|
+ var result = try_read_sni(buffer);
|
|
|
+ if(result != null) {
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+ buffer = buffer[1:buffer.length];
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static string? try_read_sni(uint8[] data) {
|
|
|
+
|
|
|
+ if(data[0] != 0 || data[1] != 0 || data[6] != 0) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ var size_1 = read_uint16(data[2:4]);
|
|
|
+ var size_2 = read_uint16(data[4:6]);
|
|
|
+ if(size_1 != size_2 + 2) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ var name_size = read_uint16(data[7:9]);
|
|
|
+ if(size_2 != name_size + 3) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ var name = new uint8[name_size + 1];
|
|
|
+ Memory.copy(name, data[9:name_size], name_size);
|
|
|
+ name[name_size] = 0;
|
|
|
+ return (string)name;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static uint16 read_uint16(uint8[] data) {
|
|
|
+ uint16 val = 0;
|
|
|
+ Memory.copy(&val, data, sizeof(int16));
|
|
|
+ return val.to_big_endian();
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+}
|