2
0
陈国伟 4 жил өмнө
parent
commit
11ed2d0c27
41 өөрчлөгдсөн 938 нэмэгдсэн , 2173 устгасан
  1. 0 8
      FastGithub.Configuration/Class1.cs
  2. 0 7
      FastGithub.Configuration/FastGithub.Configuration.csproj
  3. 0 8
      FastGithub.Dns.Configuration/Class1.cs
  4. 0 7
      FastGithub.Dns.Configuration/FastGithub.Dns.Configuration.csproj
  5. 76 0
      FastGithub.ReverseProxy/DnscryptProxyHostedService.cs
  6. 20 0
      FastGithub.ReverseProxy/FastGithub.ReverseProxy.csproj
  7. 2 1
      FastGithub.ReverseProxy/ReverseProxyServiceCollectionExtensions.cs
  8. BIN
      FastGithub.ReverseProxy/dnscrypt-proxy
  9. 826 0
      FastGithub.ReverseProxy/dnscrypt-proxy.toml
  10. 0 52
      FastGithub.Scanner/DomainAddress.cs
  11. 0 19
      FastGithub.Scanner/FastGithub.Scanner.csproj
  12. 0 146
      FastGithub.Scanner/GithubContext.cs
  13. 0 47
      FastGithub.Scanner/GithubDnsFlushService.cs
  14. 0 45
      FastGithub.Scanner/GithubFullScanHostedService.cs
  15. 0 133
      FastGithub.Scanner/GithubHttpClientHanlder.cs
  16. 0 59
      FastGithub.Scanner/GithubLookupFacotry.cs
  17. 0 16
      FastGithub.Scanner/GithubLookupFactoryOptions.cs
  18. 0 49
      FastGithub.Scanner/GithubResolver.cs
  19. 0 44
      FastGithub.Scanner/GithubResultScanHostedService.cs
  20. 0 22
      FastGithub.Scanner/GithubScanOptions.cs
  21. 0 166
      FastGithub.Scanner/GithubScanResults.cs
  22. 0 168
      FastGithub.Scanner/GithubScanService.cs
  23. 0 25
      FastGithub.Scanner/IGithubLookupProvider.cs
  24. 0 24
      FastGithub.Scanner/IGithubResolver.cs
  25. 0 183
      FastGithub.Scanner/IPAddressRange.cs
  26. 0 150
      FastGithub.Scanner/LookupProviders/GithubMetaProvider.cs
  27. 0 21
      FastGithub.Scanner/LookupProviders/GithubMetaProviderOptions.cs
  28. 0 119
      FastGithub.Scanner/LookupProviders/IPAddressComProvider.cs
  29. 0 14
      FastGithub.Scanner/LookupProviders/IPAddressComProviderOptions.cs
  30. 0 105
      FastGithub.Scanner/LookupProviders/PublicDnsProvider.cs
  31. 0 26
      FastGithub.Scanner/LookupProviders/PublicDnsProviderOptions.cs
  32. 0 110
      FastGithub.Scanner/RawSocketPing.cs
  33. 0 44
      FastGithub.Scanner/ScanMiddlewares/ConcurrentMiddleware.cs
  34. 0 117
      FastGithub.Scanner/ScanMiddlewares/HttpsScanMiddleware.cs
  35. 0 43
      FastGithub.Scanner/ScanMiddlewares/HttpsScanOptions.cs
  36. 0 37
      FastGithub.Scanner/ScanMiddlewares/StatisticsMiddleware.cs
  37. 0 88
      FastGithub.Scanner/ScanMiddlewares/TcpScanMiddleware.cs
  38. 0 21
      FastGithub.Scanner/ScanMiddlewares/TcpScanOptions.cs
  39. 0 47
      FastGithub.Scanner/ScannerServiceCollectionExtensions.cs
  40. 12 0
      FastGithub/.config/dotnet-tools.json
  41. 2 2
      FastGithub/publish.cmd

+ 0 - 8
FastGithub.Configuration/Class1.cs

@@ -1,8 +0,0 @@
-using System;
-
-namespace FastGithub.Configuration
-{
-    public class Class1
-    {
-    }
-}

+ 0 - 7
FastGithub.Configuration/FastGithub.Configuration.csproj

@@ -1,7 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk">
-
-  <PropertyGroup>
-    <TargetFramework>net5.0</TargetFramework>
-  </PropertyGroup>
-
-</Project>

+ 0 - 8
FastGithub.Dns.Configuration/Class1.cs

@@ -1,8 +0,0 @@
-using System;
-
-namespace FastGithub.Dns.Configuration
-{
-    public class Class1
-    {
-    }
-}

+ 0 - 7
FastGithub.Dns.Configuration/FastGithub.Dns.Configuration.csproj

@@ -1,7 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk">
-
-  <PropertyGroup>
-    <TargetFramework>net5.0</TargetFramework>
-  </PropertyGroup>
-
-</Project>

+ 76 - 0
FastGithub.ReverseProxy/DnscryptProxyHostedService.cs

@@ -0,0 +1,76 @@
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace FastGithub.ReverseProxy
+{
+    /// <summary>
+    /// DnscryptProxy后台服务
+    /// </summary>
+    sealed class DnscryptProxyHostedService : IHostedService
+    {
+        private const string dnscryptFile = "dnscrypt-proxy";
+        private readonly ILogger<DnscryptProxyHostedService> logger;
+        private Process? dnscryptProcess;
+
+        /// <summary>
+        /// DnscryptProxy后台服务
+        /// </summary>
+        /// <param name="logger"></param>
+        public DnscryptProxyHostedService(ILogger<DnscryptProxyHostedService> logger)
+        {
+            this.logger = logger;
+        }
+
+        /// <summary>
+        /// 启动dnscrypt-proxy
+        /// </summary>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
+        public Task StartAsync(CancellationToken cancellationToken)
+        {
+            try
+            {
+                var fileName = dnscryptFile;
+                if (OperatingSystem.IsWindows())
+                {
+                    fileName = $"{dnscryptFile}.exe";
+                }
+
+                if (File.Exists(fileName) == true)
+                {
+                    this.dnscryptProcess = Process.Start(new ProcessStartInfo
+                    {
+                        FileName = fileName,
+                        UseShellExecute = true,
+                        CreateNoWindow = true,
+                        WindowStyle = ProcessWindowStyle.Hidden
+                    });
+                };
+            }
+            catch (Exception ex)
+            {
+                this.logger.LogWarning(ex.Message);
+            }
+            return Task.CompletedTask;
+        }
+
+        /// <summary>
+        /// 停止dnscrypt-proxy
+        /// </summary>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
+        public Task StopAsync(CancellationToken cancellationToken)
+        {
+            if (this.dnscryptProcess != null)
+            {
+                this.dnscryptProcess.Kill();
+            }
+            return Task.CompletedTask;
+        }
+    }
+}

+ 20 - 0
FastGithub.ReverseProxy/FastGithub.ReverseProxy.csproj

@@ -15,4 +15,24 @@
     <ProjectReference Include="..\FastGithub.Core\FastGithub.Core.csproj" />
   </ItemGroup>
 
+  <ItemGroup>
+    <None Update="dnscrypt-proxy.toml">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </None>
+    <None Update="dnscrypt-proxy.exe">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </None>
+    <None Update="dnscrypt-proxy">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </None>
+  </ItemGroup>
+
+  <ItemGroup Condition="'$(RuntimeIdentifier)' == 'win-x64'">
+    <None Remove="dnscrypt-proxy" />
+  </ItemGroup>
+
+  <ItemGroup Condition="'$(RuntimeIdentifier)' == 'linux-x64'">
+    <None Remove="dnscrypt-proxy.exe" />
+  </ItemGroup>
+
 </Project>

+ 2 - 1
FastGithub.ReverseProxy/ReverseProxyServiceCollectionExtensions.cs

@@ -19,7 +19,8 @@ namespace FastGithub
                 .AddMemoryCache()
                 .AddHttpForwarder()
                 .AddSingleton<TrustedResolver>()
-                .AddTransient<NoSniHttpClientHanlder>();
+                .AddTransient<NoSniHttpClientHanlder>()
+                .AddHostedService<DnscryptProxyHostedService>();
         }
     }
 }

BIN
FastGithub.ReverseProxy/dnscrypt-proxy


+ 826 - 0
FastGithub.ReverseProxy/dnscrypt-proxy.toml

@@ -0,0 +1,826 @@
+
+##############################################
+#                                            #
+#        dnscrypt-proxy configuration        #
+#                                            #
+##############################################
+
+## This is an example configuration file.
+## You should adjust it to your needs, and save it as "dnscrypt-proxy.toml"
+##
+## Online documentation is available here: https://dnscrypt.info/doc
+
+
+
+##################################
+#         Global settings        #
+##################################
+
+## List of servers to use
+##
+## Servers from the "public-resolvers" source (see down below) can
+## be viewed here: https://dnscrypt.info/public-servers
+##
+## The proxy will automatically pick working servers from this list.
+## Note that the require_* filters do NOT apply when using this setting.
+##
+## By default, this list is empty and all registered servers matching the
+## require_* filters will be used instead.
+##
+## Remove the leading # first to enable this; lines starting with # are ignored.
+
+# server_names = ['scaleway-fr', 'google', 'yandex', 'cloudflare']
+
+
+## List of local addresses and ports to listen to. Can be IPv4 and/or IPv6.
+## Example with both IPv4 and IPv6:
+## listen_addresses = ['127.0.0.1:53', '[::1]:53']
+
+listen_addresses = ['127.0.0.1:5533']
+
+
+## Maximum number of simultaneous client connections to accept
+
+max_clients = 250
+
+
+## Switch to a different system user after listening sockets have been created.
+## Note (1): this feature is currently unsupported on Windows.
+## Note (2): this feature is not compatible with systemd socket activation.
+## Note (3): when using -pidfile, the PID file directory must be writable by the new user
+
+# user_name = 'nobody'
+
+
+## Require servers (from static + remote sources) to satisfy specific properties
+
+# Use servers reachable over IPv4
+ipv4_servers = true
+
+# Use servers reachable over IPv6 -- Do not enable if you don't have IPv6 connectivity
+ipv6_servers = false
+
+# Use servers implementing the DNSCrypt protocol
+dnscrypt_servers = true
+
+# Use servers implementing the DNS-over-HTTPS protocol
+doh_servers = true
+
+
+## Require servers defined by remote sources to satisfy specific properties
+
+# Server must support DNS security extensions (DNSSEC)
+require_dnssec = false
+
+# Server must not log user queries (declarative)
+require_nolog = true
+
+# Server must not enforce its own blocklist (for parental control, ads blocking...)
+require_nofilter = true
+
+# Server names to avoid even if they match all criteria
+disabled_server_names = []
+
+
+## Always use TCP to connect to upstream servers.
+## This can be useful if you need to route everything through Tor.
+## Otherwise, leave this to `false`, as it doesn't improve security
+## (dnscrypt-proxy will always encrypt everything even using UDP), and can
+## only increase latency.
+
+force_tcp = false
+
+
+## SOCKS proxy
+## Uncomment the following line to route all TCP connections to a local Tor node
+## Tor doesn't support UDP, so set `force_tcp` to `true` as well.
+
+# proxy = 'socks5://127.0.0.1:9050'
+
+
+## HTTP/HTTPS proxy
+## Only for DoH servers
+
+# http_proxy = 'http://127.0.0.1:8888'
+
+
+## How long a DNS query will wait for a response, in milliseconds.
+## If you have a network with *a lot* of latency, you may need to
+## increase this. Startup may be slower if you do so.
+## Don't increase it too much. 10000 is the highest reasonable value.
+
+timeout = 5000
+
+
+## Keepalive for HTTP (HTTPS, HTTP/2) queries, in seconds
+
+keepalive = 30
+
+
+## Add EDNS-client-subnet information to outgoing queries
+##
+## Multiple networks can be listed; they will be randomly chosen.
+## These networks don't have to match your actual networks.
+
+# edns_client_subnet = ["0.0.0.0/0", "2001:db8::/32"]
+
+
+## Response for blocked queries. Options are `refused`, `hinfo` (default) or
+## an IP response. To give an IP response, use the format `a:<IPv4>,aaaa:<IPv6>`.
+## Using the `hinfo` option means that some responses will be lies.
+## Unfortunately, the `hinfo` option appears to be required for Android 8+
+
+# blocked_query_response = 'refused'
+
+
+## Load-balancing strategy: 'p2' (default), 'ph', 'p<n>', 'first' or 'random'
+## Randomly choose 1 of the fastest 2, half, n, 1 or all live servers by latency.
+## The response quality still depends on the server itself.
+
+# lb_strategy = 'p2'
+
+## Set to `true` to constantly try to estimate the latency of all the resolvers
+## and adjust the load-balancing parameters accordingly, or to `false` to disable.
+## Default is `true` that makes 'p2' `lb_strategy` work well.
+
+# lb_estimator = true
+
+
+## Log level (0-6, default: 2 - 0 is very verbose, 6 only contains fatal errors)
+
+# log_level = 2
+
+
+## Log file for the application, as an alternative to sending logs to
+## the standard system logging service (syslog/Windows event log).
+##
+## This file is different from other log files, and will not be
+## automatically rotated by the application.
+
+# log_file = 'dnscrypt-proxy.log'
+
+
+## When using a log file, only keep logs from the most recent launch.
+
+# log_file_latest = true
+
+
+## Use the system logger (syslog on Unix, Event Log on Windows)
+
+# use_syslog = true
+
+
+## Delay, in minutes, after which certificates are reloaded
+
+cert_refresh_delay = 240
+
+
+## DNSCrypt: Create a new, unique key for every single DNS query
+## This may improve privacy but can also have a significant impact on CPU usage
+## Only enable if you don't have a lot of network load
+
+# dnscrypt_ephemeral_keys = false
+
+
+## DoH: Disable TLS session tickets - increases privacy but also latency
+
+# tls_disable_session_tickets = false
+
+
+## DoH: Use a specific cipher suite instead of the server preference
+## 49199 = TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
+## 49195 = TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
+## 52392 = TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305
+## 52393 = TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305
+##  4865 = TLS_AES_128_GCM_SHA256
+##  4867 = TLS_CHACHA20_POLY1305_SHA256
+##
+## On non-Intel CPUs such as MIPS routers and ARM systems (Android, Raspberry Pi...),
+## the following suite improves performance.
+## This may also help on Intel CPUs running 32-bit operating systems.
+##
+## Keep tls_cipher_suite empty if you have issues fetching sources or
+## connecting to some DoH servers. Google and Cloudflare are fine with it.
+
+# tls_cipher_suite = [52392, 49199]
+
+
+## Fallback resolvers
+## These are normal, non-encrypted DNS resolvers, that will be only used
+## for one-shot queries when retrieving the initial resolvers list, and
+## only if the system DNS configuration doesn't work.
+##
+## No user application queries will ever be leaked through these resolvers,
+## and they will not be used after IP addresses of resolvers URLs have been found.
+## They will never be used if lists have already been cached, and if stamps
+## don't include host names without IP addresses.
+##
+## They will not be used if the configured system DNS works.
+## Resolvers supporting DNSSEC are recommended, and, if you are using
+## DoH, fallback resolvers should ideally be operated by a different entity than
+## the DoH servers you will be using, especially if you have IPv6 enabled.
+##
+## People in China may need to use 114.114.114.114:53 here.
+## Other popular options include 8.8.8.8 and 1.1.1.1.
+##
+## If more than one resolver is specified, they will be tried in sequence.
+
+fallback_resolvers = ['9.9.9.9:53', '8.8.8.8:53']
+
+
+## Always use the fallback resolver before the system DNS settings.
+
+ignore_system_dns = true
+
+
+## Maximum time (in seconds) to wait for network connectivity before
+## initializing the proxy.
+## Useful if the proxy is automatically started at boot, and network
+## connectivity is not guaranteed to be immediately available.
+## Use 0 to not test for connectivity at all (not recommended),
+## and -1 to wait as much as possible.
+
+netprobe_timeout = 60
+
+## Address and port to try initializing a connection to, just to check
+## if the network is up. It can be any address and any port, even if
+## there is nothing answering these on the other side. Just don't use
+## a local address, as the goal is to check for Internet connectivity.
+## On Windows, a datagram with a single, nul byte will be sent, only
+## when the system starts.
+## On other operating systems, the connection will be initialized
+## but nothing will be sent at all.
+
+netprobe_address = '9.9.9.9:53'
+
+
+## Offline mode - Do not use any remote encrypted servers.
+## The proxy will remain fully functional to respond to queries that
+## plugins can handle directly (forwarding, cloaking, ...)
+
+# offline_mode = false
+
+
+## Additional data to attach to outgoing queries.
+## These strings will be added as TXT records to queries.
+## Do not use, except on servers explicitly asking for extra data
+## to be present.
+## encrypted-dns-server can be configured to use this for access control
+## in the [access_control] section
+
+# query_meta = ['key1:value1', 'key2:value2', 'token:MySecretToken']
+
+
+## Automatic log files rotation
+
+# Maximum log files size in MB - Set to 0 for unlimited.
+log_files_max_size = 10
+
+# How long to keep backup files, in days
+log_files_max_age = 7
+
+# Maximum log files backups to keep (or 0 to keep all backups)
+log_files_max_backups = 1
+
+
+
+#########################
+#        Filters        #
+#########################
+
+## Note: if you are using dnsmasq, disable the `dnssec` option in dnsmasq if you
+## configure dnscrypt-proxy to do any kind of filtering (including the filters
+## below and blocklists).
+## You can still choose resolvers that do DNSSEC validation.
+
+
+## Immediately respond to IPv6-related queries with an empty response
+## This makes things faster when there is no IPv6 connectivity, but can
+## also cause reliability issues with some stub resolvers.
+
+block_ipv6 = false
+
+
+## Immediately respond to A and AAAA queries for host names without a domain name
+
+block_unqualified = true
+
+
+## Immediately respond to queries for local zones instead of leaking them to
+## upstream resolvers (always causing errors or timeouts).
+
+block_undelegated = true
+
+
+## TTL for synthetic responses sent when a request has been blocked (due to
+## IPv6 or blocklists).
+
+reject_ttl = 600
+
+
+
+##################################################################################
+#        Route queries for specific domains to a dedicated set of servers        #
+##################################################################################
+
+## See the `example-forwarding-rules.txt` file for an example
+
+# forwarding_rules = 'forwarding-rules.txt'
+
+
+
+###############################
+#        Cloaking rules       #
+###############################
+
+## Cloaking returns a predefined address for a specific name.
+## In addition to acting as a HOSTS file, it can also return the IP address
+## of a different name. It will also do CNAME flattening.
+##
+## See the `example-cloaking-rules.txt` file for an example
+
+# cloaking_rules = 'cloaking-rules.txt'
+
+## TTL used when serving entries in cloaking-rules.txt
+
+# cloak_ttl = 600
+
+
+
+###########################
+#        DNS cache        #
+###########################
+
+## Enable a DNS cache to reduce latency and outgoing traffic
+
+cache = true
+
+
+## Cache size
+
+cache_size = 4096
+
+
+## Minimum TTL for cached entries
+
+cache_min_ttl = 2400
+
+
+## Maximum TTL for cached entries
+
+cache_max_ttl = 86400
+
+
+## Minimum TTL for negatively cached entries
+
+cache_neg_min_ttl = 60
+
+
+## Maximum TTL for negatively cached entries
+
+cache_neg_max_ttl = 600
+
+
+
+########################################
+#        Captive portal handling       #
+########################################
+
+[captive_portals]
+
+## A file that contains a set of names used by operating systems to
+## check for connectivity and captive portals, along with hard-coded
+## IP addresses to return.
+
+# map_file = 'example-captive-portals.txt'
+
+
+
+##################################
+#        Local DoH server        #
+##################################
+
+[local_doh]
+
+## dnscrypt-proxy can act as a local DoH server. By doing so, web browsers
+## requiring a direct connection to a DoH server in order to enable some
+## features will enable these, without bypassing your DNS proxy.
+
+## Addresses that the local DoH server should listen to
+
+# listen_addresses = ['127.0.0.1:3000']
+
+
+## Path of the DoH URL. This is not a file, but the part after the hostname
+## in the URL. By convention, `/dns-query` is frequently chosen.
+## For each `listen_address` the complete URL to access the server will be:
+## `https://<listen_address><path>` (ex: `https://127.0.0.1/dns-query`)
+
+# path = '/dns-query'
+
+
+## Certificate file and key - Note that the certificate has to be trusted.
+## See the documentation (wiki) for more information.
+
+# cert_file = 'localhost.pem'
+# cert_key_file = 'localhost.pem'
+
+
+
+###############################
+#        Query logging        #
+###############################
+
+## Log client queries to a file
+
+[query_log]
+
+  ## Path to the query log file (absolute, or relative to the same directory as the config file)
+  ## Can be set to /dev/stdout in order to log to the standard output.
+
+  # file = 'query.log'
+
+
+  ## Query log format (currently supported: tsv and ltsv)
+
+  format = 'tsv'
+
+
+  ## Do not log these query types, to reduce verbosity. Keep empty to log everything.
+
+  # ignored_qtypes = ['DNSKEY', 'NS']
+
+
+
+############################################
+#        Suspicious queries logging        #
+############################################
+
+## Log queries for nonexistent zones
+## These queries can reveal the presence of malware, broken/obsolete applications,
+## and devices signaling their presence to 3rd parties.
+
+[nx_log]
+
+  ## Path to the query log file (absolute, or relative to the same directory as the config file)
+
+  # file = 'nx.log'
+
+
+  ## Query log format (currently supported: tsv and ltsv)
+
+  format = 'tsv'
+
+
+
+######################################################
+#        Pattern-based blocking (blocklists)        #
+######################################################
+
+## Blocklists are made of one pattern per line. Example of valid patterns:
+##
+##   example.com
+##   =example.com
+##   *sex*
+##   ads.*
+##   ads*.example.*
+##   ads*.example[0-9]*.com
+##
+## Example blocklist files can be found at https://download.dnscrypt.info/blocklists/
+## A script to build blocklists from public feeds can be found in the
+## `utils/generate-domains-blocklists` directory of the dnscrypt-proxy source code.
+
+[blocked_names]
+
+  ## Path to the file of blocking rules (absolute, or relative to the same directory as the config file)
+
+  # blocked_names_file = 'blocked-names.txt'
+
+
+  ## Optional path to a file logging blocked queries
+
+  # log_file = 'blocked-names.log'
+
+
+  ## Optional log format: tsv or ltsv (default: tsv)
+
+  # log_format = 'tsv'
+
+
+
+###########################################################
+#        Pattern-based IP blocking (IP blocklists)        #
+###########################################################
+
+## IP blocklists are made of one pattern per line. Example of valid patterns:
+##
+##   127.*
+##   fe80:abcd:*
+##   192.168.1.4
+
+[blocked_ips]
+
+  ## Path to the file of blocking rules (absolute, or relative to the same directory as the config file)
+
+  # blocked_ips_file = 'blocked-ips.txt'
+
+
+  ## Optional path to a file logging blocked queries
+
+  # log_file = 'blocked-ips.log'
+
+
+  ## Optional log format: tsv or ltsv (default: tsv)
+
+  # log_format = 'tsv'
+
+
+
+######################################################
+#   Pattern-based allow lists (blocklists bypass)   #
+######################################################
+
+## Allowlists support the same patterns as blocklists
+## If a name matches an allowlist entry, the corresponding session
+## will bypass names and IP filters.
+##
+## Time-based rules are also supported to make some websites only accessible at specific times of the day.
+
+[allowed_names]
+
+  ## Path to the file of allow list rules (absolute, or relative to the same directory as the config file)
+
+  # allowed_names_file = 'allowed-names.txt'
+
+
+  ## Optional path to a file logging allowed queries
+
+  # log_file = 'allowed-names.log'
+
+
+  ## Optional log format: tsv or ltsv (default: tsv)
+
+  # log_format = 'tsv'
+
+
+
+#########################################################
+#   Pattern-based allowed IPs lists (blocklists bypass) #
+#########################################################
+
+## Allowed IP lists support the same patterns as IP blocklists
+## If an IP response matches an allow ip entry, the corresponding session
+## will bypass IP filters.
+##
+## Time-based rules are also supported to make some websites only accessible at specific times of the day.
+
+[allowed_ips]
+
+  ## Path to the file of allowed ip rules (absolute, or relative to the same directory as the config file)
+
+  # allowed_ips_file = 'allowed-ips.txt'
+
+
+  ## Optional path to a file logging allowed queries
+
+  # log_file = 'allowed-ips.log'
+
+  ## Optional log format: tsv or ltsv (default: tsv)
+
+  # log_format = 'tsv'
+
+
+
+##########################################
+#        Time access restrictions        #
+##########################################
+
+## One or more weekly schedules can be defined here.
+## Patterns in the name-based blocked_names file can optionally be followed with @schedule_name
+## to apply the pattern 'schedule_name' only when it matches a time range of that schedule.
+##
+## For example, the following rule in a blocklist file:
+## *.youtube.* @time-to-sleep
+## would block access to YouTube during the times defined by the 'time-to-sleep' schedule.
+##
+## {after='21:00', before= '7:00'} matches 0:00-7:00 and 21:00-0:00
+## {after= '9:00', before='18:00'} matches 9:00-18:00
+
+[schedules]
+
+  # [schedules.'time-to-sleep']
+  # mon = [{after='21:00', before='7:00'}]
+  # tue = [{after='21:00', before='7:00'}]
+  # wed = [{after='21:00', before='7:00'}]
+  # thu = [{after='21:00', before='7:00'}]
+  # fri = [{after='23:00', before='7:00'}]
+  # sat = [{after='23:00', before='7:00'}]
+  # sun = [{after='21:00', before='7:00'}]
+
+  # [schedules.'work']
+  # mon = [{after='9:00', before='18:00'}]
+  # tue = [{after='9:00', before='18:00'}]
+  # wed = [{after='9:00', before='18:00'}]
+  # thu = [{after='9:00', before='18:00'}]
+  # fri = [{after='9:00', before='17:00'}]
+
+
+
+#########################
+#        Servers        #
+#########################
+
+## Remote lists of available servers
+## Multiple sources can be used simultaneously, but every source
+## requires a dedicated cache file.
+##
+## Refer to the documentation for URLs of public sources.
+##
+## A prefix can be prepended to server names in order to
+## avoid collisions if different sources share the same for
+## different servers. In that case, names listed in `server_names`
+## must include the prefixes.
+##
+## If the `urls` property is missing, cache files and valid signatures
+## must already be present. This doesn't prevent these cache files from
+## expiring after `refresh_delay` hours.
+## Cache freshness is checked every 24 hours, so values for 'refresh_delay'
+## of less than 24 hours will have no effect.
+## A maximum delay of 168 hours (1 week) is imposed to ensure cache freshness.
+
+[sources]
+
+  ## An example of a remote source from https://github.com/DNSCrypt/dnscrypt-resolvers
+
+  [sources.'public-resolvers']
+  urls = ['https://raw.githubusercontent.com/DNSCrypt/dnscrypt-resolvers/master/v3/public-resolvers.md', 'https://download.dnscrypt.info/resolvers-list/v3/public-resolvers.md', 'https://ipv6.download.dnscrypt.info/resolvers-list/v3/public-resolvers.md', 'https://download.dnscrypt.net/resolvers-list/v3/public-resolvers.md']
+  cache_file = 'public-resolvers.md'
+  minisign_key = 'RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3'
+  refresh_delay = 72
+  prefix = ''
+
+  ## Anonymized DNS relays
+
+  [sources.'relays']
+  urls = ['https://raw.githubusercontent.com/DNSCrypt/dnscrypt-resolvers/master/v3/relays.md', 'https://download.dnscrypt.info/resolvers-list/v3/relays.md', 'https://ipv6.download.dnscrypt.info/resolvers-list/v3/relays.md', 'https://download.dnscrypt.net/resolvers-list/v3/relays.md']
+  cache_file = 'relays.md'
+  minisign_key = 'RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3'
+  refresh_delay = 72
+  prefix = ''
+
+  ## Quad9 over DNSCrypt - https://quad9.net/
+
+  # [sources.quad9-resolvers]
+  # urls = ['https://www.quad9.net/quad9-resolvers.md']
+  # minisign_key = 'RWQBphd2+f6eiAqBsvDZEBXBGHQBJfeG6G+wJPPKxCZMoEQYpmoysKUN'
+  # cache_file = 'quad9-resolvers.md'
+  # prefix = 'quad9-'
+
+  ## Another example source, with resolvers censoring some websites not appropriate for children
+  ## This is a subset of the `public-resolvers` list, so enabling both is useless
+
+  #  [sources.'parental-control']
+  #  urls = ['https://raw.githubusercontent.com/DNSCrypt/dnscrypt-resolvers/master/v3/parental-control.md', 'https://download.dnscrypt.info/resolvers-list/v3/parental-control.md', 'https://ipv6.download.dnscrypt.info/resolvers-list/v3/parental-control.md', 'https://download.dnscrypt.net/resolvers-list/v3/parental-control.md']
+  #  cache_file = 'parental-control.md'
+  #  minisign_key = 'RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3'
+
+
+
+#########################################
+#        Servers with known bugs        #
+#########################################
+
+[broken_implementations]
+
+# Cisco servers currently cannot handle queries larger than 1472 bytes, and don't
+# truncate reponses larger than questions as expected by the DNSCrypt protocol.
+# This prevents large responses from being received over UDP and over relays.
+#
+# Older versions of the `dnsdist` server software had a bug with queries larger
+# than 1500 bytes. This is fixed since `dnsdist` version 1.5.0, but
+# some server may still run an outdated version.
+#
+# The list below enables workarounds to make non-relayed usage more reliable
+# until the servers are fixed.
+
+fragments_blocked = ['cisco', 'cisco-ipv6', 'cisco-familyshield', 'cisco-familyshield-ipv6', 'cleanbrowsing-adult', 'cleanbrowsing-adult-ipv6', 'cleanbrowsing-family', 'cleanbrowsing-family-ipv6', 'cleanbrowsing-security', 'cleanbrowsing-security-ipv6']
+
+
+
+#################################################################
+#        Certificate-based client authentication for DoH        #
+#################################################################
+
+# Use a X509 certificate to authenticate yourself when connecting to DoH servers.
+# This is only useful if you are operating your own, private DoH server(s).
+# 'creds' maps servers to certificates, and supports multiple entries.
+# If you are not using the standard root CA, an optional "root_ca"
+# property set to the path to a root CRT file can be added to a server entry.
+
+[doh_client_x509_auth]
+
+#
+# creds = [
+#    { server_name='myserver', client_cert='client.crt', client_key='client.key' }
+# ]
+
+
+
+################################
+#        Anonymized DNS        #
+################################
+
+[anonymized_dns]
+
+## Routes are indirect ways to reach DNSCrypt servers.
+##
+## A route maps a server name ("server_name") to one or more relays that will be
+## used to connect to that server.
+##
+## A relay can be specified as a DNS Stamp (either a relay stamp, or a
+## DNSCrypt stamp) or a server name.
+##
+## The following example routes "example-server-1" via `anon-example-1` or `anon-example-2`,
+## and "example-server-2" via the relay whose relay DNS stamp is
+## "sdns://gRIxMzcuNzQuMjIzLjIzNDo0NDM".
+##
+## !!! THESE ARE JUST EXAMPLES !!!
+##
+## Review the list of available relays from the "relays.md" file, and, for each
+## server you want to use, define the relays you want connections to go through.
+##
+## Carefully choose relays and servers so that they are run by different entities.
+##
+## "server_name" can also be set to "*" to define a default route, for all servers:
+## { server_name='*', via=['anon-example-1', 'anon-example-2'] }
+##
+## If a route is ["*"], the proxy automatically picks a relay on a distinct network.
+## { server_name='*', via=['*'] } is also an option, but is likely to be suboptimal.
+##
+## Manual selection is always recommended over automatic selection, so that you can
+## select (relay,server) pairs that work well and fit your own criteria (close by or
+## in different countries, operated by different entities, on distinct ISPs...)
+
+# routes = [
+#    { server_name='example-server-1', via=['anon-example-1', 'anon-example-2'] },
+#    { server_name='example-server-2', via=['sdns://gRIxMzcuNzQuMjIzLjIzNDo0NDM'] }
+# ]
+
+
+# Skip resolvers incompatible with anonymization instead of using them directly
+
+skip_incompatible = false
+
+
+# If public server certificates for a non-conformant server cannot be
+# retrieved via a relay, try getting them directly. Actual queries
+# will then always go through relays.
+
+# direct_cert_fallback = false
+
+
+
+###############################
+#            DNS64            #
+###############################
+
+## DNS64 is a mechanism for synthesizing AAAA records from A records.
+## It is used with an IPv6/IPv4 translator to enable client-server
+## communication between an IPv6-only client and an IPv4-only server,
+## without requiring any changes to either the IPv6 or the IPv4 node,
+## for the class of applications that work through NATs.
+##
+## There are two options to synthesize such records:
+## Option 1: Using a set of static IPv6 prefixes;
+## Option 2: By discovering the IPv6 prefix from DNS64-enabled resolver.
+##
+## If both options are configured - only static prefixes are used.
+## (Ref. RFC6147, RFC6052, RFC7050)
+##
+## Do not enable unless you know what DNS64 is and why you need it, or else
+## you won't be able to connect to anything at all.
+
+[dns64]
+
+## (Option 1) Static prefix(es) as Pref64::/n CIDRs.
+# prefix = ['64:ff9b::/96']
+
+## (Option 2) DNS64-enabled resolver(s) to discover Pref64::/n CIDRs.
+## These resolvers are used to query for Well-Known IPv4-only Name (WKN) "ipv4only.arpa." to discover only.
+## Set with your ISP's resolvers in case of custom prefixes (other than Well-Known Prefix 64:ff9b::/96).
+## IMPORTANT: Default resolvers listed below support Well-Known Prefix 64:ff9b::/96 only.
+# resolver = ['[2606:4700:4700::64]:53', '[2001:4860:4860::64]:53']
+
+
+
+########################################
+#            Static entries            #
+########################################
+
+## Optional, local, static list of additional servers
+## Mostly useful for testing your own servers.
+
+[static]
+
+  # [static.'myserver']
+  # stamp = 'sdns://AQcAAAAAAAAAAAAQMi5kbnNjcnlwdC1jZXJ0Lg'

+ 0 - 52
FastGithub.Scanner/DomainAddress.cs

@@ -1,52 +0,0 @@
-using System;
-using System.Net;
-
-namespace FastGithub.Scanner
-{
-    /// <summary>
-    /// 域名与ip关系
-    /// </summary>
-    class DomainAddress : IEquatable<DomainAddress>
-    {
-        /// <summary>
-        /// 获取域名
-        /// </summary>
-        public string Domain { get; }
-
-        /// <summary>
-        /// 获取ip
-        /// </summary>
-        public IPAddress Address { get; }
-
-        /// <summary>
-        /// 域名与ip关系
-        /// </summary>
-        /// <param name="domain"></param>
-        /// <param name="address"></param>
-        public DomainAddress(string domain, IPAddress address)
-        {
-            this.Domain = domain;
-            this.Address = address;
-        }
-
-        public override bool Equals(object? obj)
-        {
-            return obj is DomainAddress other && this.Equals(other);
-        }
-
-        public bool Equals(DomainAddress? other)
-        {
-            return other != null && other.Address.Equals(this.Address) && other.Domain == this.Domain;
-        }
-
-        public override int GetHashCode()
-        {
-            return HashCode.Combine(this.Domain, this.Address);
-        }
-
-        public override string ToString()
-        {
-            return $"{this.Domain}=>{this.Address}";
-        }
-    }
-}

+ 0 - 19
FastGithub.Scanner/FastGithub.Scanner.csproj

@@ -1,19 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk">
-
-	<PropertyGroup>
-		<TargetFramework>net5.0</TargetFramework>
-	</PropertyGroup>
-
-	<ItemGroup>
-		<PackageReference Include="DNS" Version="6.1.0" />
-		<PackageReference Include="IPNetwork2" Version="2.5.320" />
-		<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="5.0.0" />
-		<PackageReference Include="Microsoft.Extensions.Hosting" Version="5.0.0" />
-		<PackageReference Include="Microsoft.Extensions.Http" Version="5.0.0" />
-	</ItemGroup>
-
-	<ItemGroup>
-		<ProjectReference Include="..\FastGithub.Core\FastGithub.Core.csproj" />
-	</ItemGroup>
-
-</Project>

+ 0 - 146
FastGithub.Scanner/GithubContext.cs

@@ -1,146 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Net;
-using System.Threading;
-
-namespace FastGithub.Scanner
-{
-    /// <summary>
-    /// Github扫描上下文
-    /// </summary>
-    sealed class GithubContext : DomainAddress, IEquatable<GithubContext>
-    {
-        /// <summary>
-        /// 最多保存最的近的20条记录
-        /// </summary>
-        private const int MAX_LOG_COUNT = 20;
-
-        /// <summary>
-        /// 扫描记录
-        /// </summary>
-        private record ScanLog(bool Available, TimeSpan Elapsed);
-
-        /// <summary>
-        /// 扫描历史记录
-        /// </summary>
-        private readonly Queue<ScanLog> history = new();
-
-
-
-        /// <summary>
-        /// 设置取消令牌
-        /// </summary>
-        public CancellationToken CancellationToken { get; }
-
-        /// <summary>
-        /// 获取可用率
-        /// </summary>
-        /// <returns></returns>
-        public double AvailableRate => this.GetAvailableRate();
-
-        /// <summary>
-        /// 获取平均耗时
-        /// </summary>
-        /// <returns></returns>
-        public TimeSpan AvgElapsed => this.GetAvgElapsed();
-
-        /// <summary>
-        /// 获取或设置是否可用
-        /// </summary>
-        public bool Available { get; set; }
-
-
-        /// <summary>
-        /// Github扫描上下文
-        /// </summary>
-        /// <param name="domain"></param>
-        /// <param name="address"></param>
-        public GithubContext(string domain, IPAddress address)
-            : this(domain, address, CancellationToken.None)
-        {
-        }
-
-        /// <summary>
-        /// Github扫描上下文
-        /// </summary>
-        /// <param name="domain"></param>
-        /// <param name="address"></param>
-        /// <param name="cancellationToken"></param>
-        public GithubContext(string domain, IPAddress address, CancellationToken cancellationToken)
-            : base(domain, address)
-        {
-            this.CancellationToken = cancellationToken;
-        }
-
-        /// <summary>
-        /// 获取可用率
-        /// </summary>
-        /// <returns></returns>
-        private double GetAvailableRate()
-        {
-            if (this.history.Count == 0)
-            {
-                return 0d;
-            }
-
-            var availableCount = this.history.Count(item => item.Available);
-            return (double)availableCount / this.history.Count;
-        }
-
-        /// <summary>
-        /// 获取平均耗时
-        /// </summary>
-        /// <returns></returns>
-        private TimeSpan GetAvgElapsed()
-        {
-            var availableCount = 0;
-            var availableElapsed = TimeSpan.Zero;
-
-            foreach (var item in this.history)
-            {
-                if (item.Available == true)
-                {
-                    availableCount += 1;
-                    availableElapsed = availableElapsed.Add(item.Elapsed);
-                }
-            }
-            return availableCount == 0 ? TimeSpan.MaxValue : availableElapsed / availableCount;
-        }
-
-
-        /// <summary>
-        /// 添加扫描记录
-        /// </summary>
-        /// <param name="elapsed">扫描耗时</param>
-        public void AddScanLog(TimeSpan elapsed)
-        {
-            var log = new ScanLog(this.Available, elapsed);
-            this.history.Enqueue(log);
-            while (this.history.Count > MAX_LOG_COUNT)
-            {
-                this.history.Dequeue();
-            }
-        }
-
-        /// <summary>
-        /// 是否相等
-        /// </summary>
-        /// <param name="other"></param>
-        /// <returns></returns>
-        public bool Equals(GithubContext? other)
-        {
-            return base.Equals(other);
-        }
-
-        /// <summary>
-        /// 转换为统计信息
-        /// </summary>
-        /// <returns></returns>
-        public string ToStatisticsString()
-        {
-            var availableRate = Math.Round(this.AvailableRate * 100, 2);
-            return $"{{{nameof(Address)}={this.Address}, {nameof(AvailableRate)}={availableRate}%, {nameof(AvgElapsed)}={this.AvgElapsed.TotalSeconds}s}}";
-        }
-    }
-}

+ 0 - 47
FastGithub.Scanner/GithubDnsFlushService.cs

@@ -1,47 +0,0 @@
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Options;
-using System;
-using System.Runtime.InteropServices;
-using System.Runtime.Versioning;
-
-namespace FastGithub.Scanner
-{
-    [Service(ServiceLifetime.Singleton)]
-    sealed class GithubDnsFlushService
-    {
-        private readonly ILogger<GithubDnsFlushService> logger;
-        private readonly IOptionsMonitor<GithubLookupFactoryOptions> options;
-
-        [SupportedOSPlatform("windows")]
-        [DllImport("dnsapi.dll", EntryPoint = "DnsFlushResolverCacheEntry_A", CharSet = CharSet.Ansi)]
-        private static extern int DnsFlushResolverCacheEntry(string hostName);
-
-        public GithubDnsFlushService(
-            ILogger<GithubDnsFlushService> logger,
-            IOptionsMonitor<GithubLookupFactoryOptions> options)
-        {
-            this.logger = logger;
-            this.options = options;
-        }
-
-        public void FlushGithubResolverCache()
-        {
-            if (OperatingSystem.IsWindows())
-            {
-                try
-                {
-                    foreach (var domain in this.options.CurrentValue.Domains)
-                    {
-                        DnsFlushResolverCacheEntry(domain);
-                    }
-                    this.logger.LogInformation($"刷新本机相关域名的dns缓存成功");
-                }
-                catch (Exception ex)
-                {
-                    this.logger.LogWarning($"刷新本机相关域名的dns缓存失败:{ex.Message}");
-                }
-            }
-        }
-    }
-}

+ 0 - 45
FastGithub.Scanner/GithubFullScanHostedService.cs

@@ -1,45 +0,0 @@
-using FastGithub.Scanner;
-using Microsoft.Extensions.Hosting;
-using Microsoft.Extensions.Options;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace FastGithub
-{
-    /// <summary>
-    /// 完整扫描后台服务
-    /// </summary>
-    sealed class GithubFullScanHostedService : BackgroundService
-    {
-        private readonly GithubScanService githubScanService;
-        private readonly IOptionsMonitor<GithubScanOptions> options;
-
-        /// <summary>
-        /// 完整扫描后台服务
-        /// </summary>
-        /// <param name="githubScanService"></param>
-        /// <param name="options"></param>
-        public GithubFullScanHostedService(
-            GithubScanService githubScanService,
-            IOptionsMonitor<GithubScanOptions> options)
-        {
-            this.githubScanService = githubScanService;
-            this.options = options;
-        }
-
-        /// <summary>
-        /// 后台轮询扫描
-        /// </summary>
-        /// <param name="stoppingToken"></param>
-        /// <returns></returns>
-        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
-        {
-            await githubScanService.ScanFastAsync(stoppingToken);
-            while (stoppingToken.IsCancellationRequested == false)
-            {
-                await githubScanService.ScanAllAsync(stoppingToken);
-                await Task.Delay(this.options.CurrentValue.FullScanInterval, stoppingToken);
-            }
-        }
-    }
-}

+ 0 - 133
FastGithub.Scanner/GithubHttpClientHanlder.cs

@@ -1,133 +0,0 @@
-using Microsoft.Extensions.Caching.Memory;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
-using System;
-using System.Net;
-using System.Net.Http;
-using System.Net.Security;
-using System.Net.Sockets;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace FastGithub.Scanner
-{
-    /// <summary>
-    /// 适用于请求github的HttpClientHandler
-    /// </summary>
-    [Service(ServiceLifetime.Transient)]
-    public class GithubHttpClientHanlder : DelegatingHandler
-    {
-        private readonly IGithubResolver githubResolver;
-        private readonly ILogger<GithubHttpClientHanlder> logger;
-        private readonly IMemoryCache memoryCache;
-
-        /// <summary>
-        /// 请求github的HttpClientHandler
-        /// </summary>
-        /// <param name="githubResolver"></param>
-        /// <param name="logger"></param>
-        /// <param name="memoryCache"></param>
-        public GithubHttpClientHanlder(
-            IGithubResolver githubResolver,
-            ILogger<GithubHttpClientHanlder> logger,
-            IMemoryCache memoryCache)
-        {
-            this.githubResolver = githubResolver;
-            this.logger = logger;
-            this.memoryCache = memoryCache;
-            this.InnerHandler = CreateNoneSniHttpHandler();
-        }
-
-        /// <summary>
-        /// 创建无Sni发送的httpHandler
-        /// </summary>
-        /// <returns></returns>
-        private static HttpMessageHandler CreateNoneSniHttpHandler()
-        {
-            return new SocketsHttpHandler
-            {
-                Proxy = null,
-                UseProxy = false,
-                AllowAutoRedirect = false,
-                ConnectCallback = async (ctx, ct) =>
-                {
-                    var socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
-                    await socket.ConnectAsync(ctx.DnsEndPoint, ct);
-                    var stream = new NetworkStream(socket, ownsSocket: true);
-                    if (ctx.InitialRequestMessage.Headers.Host == null)
-                    {
-                        return stream;
-                    }
-
-                    var sslStream = new SslStream(stream, leaveInnerStreamOpen: false);
-                    await sslStream.AuthenticateAsClientAsync(new SslClientAuthenticationOptions
-                    {
-                        TargetHost = string.Empty,
-                        RemoteCertificateValidationCallback = delegate { return true; }
-                    }, ct);
-                    return sslStream;
-                }
-            };
-        }
-
-
-        /// <summary>
-        /// 替换github域名为ip
-        /// </summary>
-        /// <param name="request"></param>
-        /// <param name="cancellationToken"></param>
-        /// <returns></returns>
-        protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
-        {
-            var uri = request.RequestUri;
-            if (uri != null && uri.HostNameType == UriHostNameType.Dns)
-            {
-                var githubAddress = this.ResolveGithub(uri.Host);
-                if (githubAddress != null)
-                {
-                    var builder = new UriBuilder(uri)
-                    {
-                        Scheme = Uri.UriSchemeHttp,
-                        Host = githubAddress.ToString(),
-                        Port = 443
-                    };
-                    request.RequestUri = builder.Uri;
-                    request.Headers.Host = uri.Host;
-                }
-            }
-            return await base.SendAsync(request, cancellationToken);
-        }
-
-
-        /// <summary>
-        /// 解析域名
-        /// 非github域名返回null
-        /// </summary>
-        /// <param name="domain"></param>
-        /// <returns></returns>
-        private IPAddress? ResolveGithub(string domain)
-        {
-            // 非github的域名,返回null走上游dns
-            if (this.githubResolver.IsSupported(domain) == false)
-            {
-                return default;
-            }
-
-            // 缓存1s,避免做为公共服务后不必要的并发查询
-            var key = $"domain:{domain}";
-            var address = this.memoryCache.GetOrCreate(key, e =>
-            {
-                e.SetAbsoluteExpiration(TimeSpan.FromSeconds(1d));
-                return this.githubResolver.Resolve(domain);
-            });
-
-            if (address == null)
-            {
-                throw new HttpRequestException($"无法解析{domain}的ip");
-            }
-
-            this.logger.LogInformation($"使用{address} No SNI请求{domain}");
-            return address;
-        }
-    }
-}

+ 0 - 59
FastGithub.Scanner/GithubLookupFacotry.cs

@@ -1,59 +0,0 @@
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Options;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace FastGithub.Scanner
-{
-    /// <summary>
-    /// 域名与ip关系工厂
-    /// </summary>
-    [Service(ServiceLifetime.Singleton)]
-    sealed class GithubLookupFacotry
-    {
-        private readonly IEnumerable<IGithubLookupProvider> providers;
-        private readonly IOptionsMonitor<GithubLookupFactoryOptions> options;
-        private readonly ILogger<GithubLookupFacotry> logger;
-
-        /// <summary>
-        /// 域名与ip关系工厂
-        /// </summary>
-        /// <param name="providers"></param>
-        /// <param name="options"></param>
-        public GithubLookupFacotry(
-            IEnumerable<IGithubLookupProvider> providers,
-            IOptionsMonitor<GithubLookupFactoryOptions> options,
-            ILogger<GithubLookupFacotry> logger)
-        {
-            this.providers = providers.OrderBy(item => item.Order);
-            this.options = options;
-            this.logger = logger;
-        }
-
-        /// <summary>
-        /// 查找域名与ip关系
-        /// </summary>
-        /// <returns></returns>
-        public async Task<IEnumerable<DomainAddress>> LookupAsync(CancellationToken cancellationToken)
-        {
-            this.logger.LogInformation($"开始查找各域名的ip..");
-            var hashSet = new HashSet<DomainAddress>();
-            var domains = this.options.CurrentValue.Domains;
-
-            foreach (var provider in this.providers)
-            {
-                var domainAddresses = await provider.LookupAsync(domains, cancellationToken);
-                foreach (var item in domainAddresses)
-                {
-                    hashSet.Add(item);
-                }
-            }
-
-            this.logger.LogInformation($"查找到{hashSet.Count}条域名ip记录");
-            return hashSet;
-        }
-    }
-}

+ 0 - 16
FastGithub.Scanner/GithubLookupFactoryOptions.cs

@@ -1,16 +0,0 @@
-using System.Collections.Generic;
-
-namespace FastGithub.Scanner
-{
-    /// <summary>
-    /// 域名
-    /// </summary>
-    [Options("Lookup")]
-    sealed class GithubLookupFactoryOptions
-    {
-        /// <summary>
-        /// 反查的域名
-        /// </summary>
-        public HashSet<string> Domains { get; set; } = new();
-    }
-}

+ 0 - 49
FastGithub.Scanner/GithubResolver.cs

@@ -1,49 +0,0 @@
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Options;
-using System.Net;
-
-namespace FastGithub.Scanner
-{
-    /// <summary>
-    /// github解析器
-    /// </summary>
-    [Service(ServiceLifetime.Singleton, ServiceType = typeof(IGithubResolver))]
-    sealed class GithubResolver : IGithubResolver
-    {
-        private readonly GithubScanResults githubScanResults;
-        private readonly IOptionsMonitor<GithubLookupFactoryOptions> options;
-
-        /// <summary>
-        /// github解析器
-        /// </summary>
-        /// <param name="githubScanResults"></param>
-        /// <param name="options"></param>
-        public GithubResolver(
-            GithubScanResults githubScanResults,
-            IOptionsMonitor<GithubLookupFactoryOptions> options)
-        {
-            this.githubScanResults = githubScanResults;
-            this.options = options;
-        }
-
-        /// <summary>
-        /// 是否支持指定的域名
-        /// </summary>
-        /// <param name="domain"></param>
-        /// <returns></returns>
-        public bool IsSupported(string domain)
-        {
-            return this.options.CurrentValue.Domains.Contains(domain);
-        }
-
-        /// <summary>
-        /// 解析指定的域名
-        /// </summary>
-        /// <param name="domain"></param>
-        /// <returns></returns>
-        public IPAddress? Resolve(string domain)
-        {
-            return this.IsSupported(domain) ? this.githubScanResults.FindBestAddress(domain) : default;
-        }
-    }
-}

+ 0 - 44
FastGithub.Scanner/GithubResultScanHostedService.cs

@@ -1,44 +0,0 @@
-using FastGithub.Scanner;
-using Microsoft.Extensions.Hosting;
-using Microsoft.Extensions.Options;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace FastGithub
-{
-    /// <summary>
-    /// 扫描结果轮询扫描后台服务
-    /// </summary>
-    sealed class GithubResultScanHostedService : BackgroundService
-    {
-        private readonly GithubScanService githubScanService;
-        private readonly IOptionsMonitor<GithubScanOptions> options;
-
-        /// <summary>
-        /// 扫描结果轮询扫描后台服务
-        /// </summary>
-        /// <param name="githubScanService"></param>
-        /// <param name="options"></param>
-        public GithubResultScanHostedService(
-            GithubScanService githubScanService,
-            IOptionsMonitor<GithubScanOptions> options)
-        {
-            this.githubScanService = githubScanService;
-            this.options = options;
-        }
-
-        /// <summary>
-        /// 后台轮询扫描
-        /// </summary>
-        /// <param name="stoppingToken"></param>
-        /// <returns></returns>
-        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
-        {
-            while (stoppingToken.IsCancellationRequested == false)
-            {
-                await Task.Delay(this.options.CurrentValue.ResultScanInterval, stoppingToken);
-                await githubScanService.ScanResultAsync(stoppingToken);
-            }
-        }
-    }
-}

+ 0 - 22
FastGithub.Scanner/GithubScanOptions.cs

@@ -1,22 +0,0 @@
-using System;
-
-namespace FastGithub.Scanner
-{
-    /// <summary>
-    /// 扫描选项
-    /// </summary>
-    [Options("Scan")]
-    sealed class GithubScanOptions
-    {
-        /// <summary>
-        /// 完整扫描轮询时间间隔
-        /// </summary>
-
-        public TimeSpan FullScanInterval = TimeSpan.FromHours(2d);
-
-        /// <summary>
-        /// 结果扫描轮询时间间隔
-        /// </summary>
-        public TimeSpan ResultScanInterval = TimeSpan.FromMinutes(1d);
-    }
-}

+ 0 - 166
FastGithub.Scanner/GithubScanResults.cs

@@ -1,166 +0,0 @@
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
-using System;
-using System.Collections.Generic;
-using System.Diagnostics.CodeAnalysis;
-using System.IO;
-using System.Linq;
-using System.Net;
-using System.Text.Json;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace FastGithub.Scanner
-{
-    /// <summary>
-    /// github扫描结果
-    /// </summary>
-    [Service(ServiceLifetime.Singleton)]
-    sealed class GithubScanResults
-    {
-        private const string dataFile = "FastGithub.dat";
-        private readonly object syncRoot = new();
-        private readonly List<GithubContext> contexts = new();
-        private readonly ILogger<GithubScanResults> logger;
-
-        /// <summary>
-        /// github扫描结果
-        /// </summary>
-        /// <param name="logger"></param>
-        public GithubScanResults(ILogger<GithubScanResults> logger)
-        {
-            this.logger = logger;
-            var datas = LoadDatas(logger);
-            foreach (var context in datas)
-            {
-                this.Add(context);
-            }
-        }
-
-        /// <summary>
-        /// 从磁盘加载数据
-        /// </summary>
-        /// <param name="logger"></param>
-        /// <returns></returns>
-        private static GithubContext[] LoadDatas(ILogger logger)
-        {
-            try
-            {
-                if (File.Exists(dataFile) == true)
-                {
-                    var json = File.ReadAllBytes(dataFile);
-                    var datas = JsonSerializer.Deserialize<GithubDomainAddress[]>(json);
-                    if (datas != null)
-                    {
-                        return datas.Select(item => item.ToGithubContext()).ToArray();
-                    }
-                }
-            }
-            catch (Exception ex)
-            {
-                logger.LogWarning($"从{dataFile}加载数据失败:{ex.Message}");
-            }
-
-            return Array.Empty<GithubContext>();
-        }
-
-        /// <summary>
-        /// 添加GithubContext
-        /// </summary>
-        /// <param name="context"></param>
-        /// <returns></returns>
-        public bool Add(GithubContext context)
-        {
-            lock (this.syncRoot)
-            {
-                if (this.contexts.Contains(context))
-                {
-                    return false;
-                }
-                this.contexts.Add(context);
-                return true;
-            }
-        }
-
-        /// <summary>
-        /// 转换为数组
-        /// </summary>
-        /// <returns></returns>
-        public GithubContext[] ToArray()
-        {
-            lock (this.syncRoot)
-            {
-                return this.contexts.ToArray();
-            }
-        }
-
-        /// <summary>
-        /// 查找最优的ip
-        /// </summary>
-        /// <param name="domain"></param>
-        /// <returns></returns>
-        public IPAddress? FindBestAddress(string domain)
-        {
-            lock (this.syncRoot)
-            {
-                return this.contexts
-                    .Where(item => item.Domain == domain)
-                    .OrderByDescending(item => item.AvailableRate)
-                    .ThenByDescending(item => item.Available)
-                    .ThenBy(item => item.AvgElapsed)
-                    .Select(item => item.Address)
-                    .FirstOrDefault();
-            }
-        }
-
-        /// <summary>
-        /// 保存数据到磁盘
-        /// </summary>
-        /// <param name="cancellationToken"></param>
-        /// <returns></returns>
-        public async Task SaveDatasAsync(CancellationToken cancellationToken = default)
-        {
-            try
-            {
-                using var stream = File.OpenWrite(dataFile);
-                var datas = this.ToArray()
-                    .OrderBy(item => item.Domain)
-                    .ThenByDescending(item => item.AvailableRate)
-                    .Select(item => GithubDomainAddress.From(item));
-
-                await JsonSerializer.SerializeAsync(stream, datas, cancellationToken: cancellationToken);
-            }
-            catch (Exception ex)
-            {
-                this.logger.LogWarning($"保存数据到{dataFile}失败:{ex.Message}");
-            }
-        }
-
-
-        /// <summary>
-        /// github的域名与ip关系数据
-        /// </summary>
-        private class GithubDomainAddress
-        {
-            [AllowNull]
-            public string Domain { get; set; }
-
-            [AllowNull]
-            public string Address { get; set; }
-
-            public GithubContext ToGithubContext()
-            {
-                return new GithubContext(this.Domain, IPAddress.Parse(this.Address)) { Available = true };
-            }
-
-            public static GithubDomainAddress From(GithubContext context)
-            {
-                return new GithubDomainAddress
-                {
-                    Domain = context.Domain,
-                    Address = context.Address.ToString()
-                };
-            }
-        }
-    }
-}

+ 0 - 168
FastGithub.Scanner/GithubScanService.cs

@@ -1,168 +0,0 @@
-using FastGithub.Scanner.ScanMiddlewares;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace FastGithub.Scanner
-{
-    /// <summary>
-    /// github扫描服务
-    /// </summary>
-    [Service(ServiceLifetime.Singleton)]
-    sealed class GithubScanService
-    {
-        private readonly GithubLookupFacotry lookupFactory;
-        private readonly GithubScanResults scanResults;
-        private readonly GithubDnsFlushService dnsFlushService;
-        private readonly ILoggerFactory loggerFactory;
-        private readonly ILogger<GithubScanService> logger;
-
-        private readonly InvokeDelegate<GithubContext> fullScanDelegate;
-        private readonly InvokeDelegate<GithubContext> resultScanDelegate;
-
-        /// <summary>
-        /// github扫描服务
-        /// </summary>
-        /// <param name="lookupFactory"></param>
-        /// <param name="scanResults"></param>
-        /// <param name="appService"></param>
-        /// <param name="logger"></param>
-        public GithubScanService(
-            GithubLookupFacotry lookupFactory,
-            GithubScanResults scanResults,
-            GithubDnsFlushService dnsFlushService,
-            IServiceProvider appService,
-            ILoggerFactory loggerFactory,
-            ILogger<GithubScanService> logger)
-        {
-            this.lookupFactory = lookupFactory;
-            this.scanResults = scanResults;
-            this.dnsFlushService = dnsFlushService;
-            this.loggerFactory = loggerFactory;
-            this.logger = logger;
-
-            this.fullScanDelegate = new PipelineBuilder<GithubContext>(appService, ctx => Task.CompletedTask)
-                .Use<ConcurrentMiddleware>()
-                .Use<StatisticsMiddleware>()
-                .Use<TcpScanMiddleware>()
-                .Use<HttpsScanMiddleware>()
-                .Build();
-
-            this.resultScanDelegate = new PipelineBuilder<GithubContext>(appService, ctx => Task.CompletedTask)
-                .Use<StatisticsMiddleware>()
-                .Use<HttpsScanMiddleware>()
-                .Build();
-        }
-
-        /// <summary>
-        /// 快速扫描所有的ip
-        /// </summary> 
-        /// <param name="cancellationToken"></param>
-        /// <returns></returns>
-        public async Task<bool> ScanFastAsync(CancellationToken cancellationToken)
-        {
-            if (RawSocketPing.IsSupported == false)
-            {
-                this.logger.LogWarning($"{Environment.OSVersion.Platform}不支持快速扫描功能");
-                return false;
-            }
-
-            try
-            {
-                this.logger.LogInformation("快速扫描开始..");
-                var domainAddresses = await this.lookupFactory.LookupAsync(cancellationToken);
-
-                // ping快速过滤可用的ip
-                var destAddresses = domainAddresses.Select(item => item.Address);
-                var hashSet = await RawSocketPing.PingAsync(destAddresses, TimeSpan.FromSeconds(3d), cancellationToken);
-                var results = domainAddresses.Where(item => hashSet.Contains(item.Address)).ToArray();
-                this.logger.LogInformation($"快速扫描到{hashSet.Count}条ip,{results.Length}条域名ip记录");
-
-                var successCount = await this.ScanAsync(results, cancellationToken);
-                this.logger.LogInformation($"快速扫描结束,成功{successCount}条共{domainAddresses.Count()}条");
-                return true;
-            }
-            catch (Exception ex)
-            {
-                this.logger.LogWarning($"快速扫描失败:{ex.Message}");
-                return false;
-            }
-        }
-
-        /// <summary>
-        /// 扫描所有的ip
-        /// </summary> 
-        /// <param name="cancellationToken"></param>
-        /// <returns></returns>
-        public async Task ScanAllAsync(CancellationToken cancellationToken)
-        {
-            this.logger.LogInformation("完整扫描开始..");
-            var domainAddresses = await this.lookupFactory.LookupAsync(cancellationToken);
-            var successCount = await this.ScanAsync(domainAddresses, cancellationToken);
-            this.logger.LogInformation($"完整扫描结束,成功{successCount}条共{domainAddresses.Count()}条");
-        }
-
-        /// <summary>
-        /// 扫描记录
-        /// </summary>
-        /// <param name="domainAddresses"></param>
-        /// <param name="cancellationToken"></param>
-        /// <returns></returns>
-        private async Task<int> ScanAsync(IEnumerable<DomainAddress> domainAddresses, CancellationToken cancellationToken)
-        {
-            var scanTasks = domainAddresses
-                .Select(item => new GithubContext(item.Domain, item.Address, cancellationToken))
-                .Select(ctx => ScanAsync(ctx));
-
-            var results = await Task.WhenAll(scanTasks);
-            return results.Count(item => item);
-
-            async Task<bool> ScanAsync(GithubContext context)
-            {
-                await this.fullScanDelegate(context);
-                if (context.Available && this.scanResults.Add(context))
-                {
-                    this.logger.LogInformation($"扫描到{context}");
-                }
-                return context.Available;
-            }
-        }
-
-        /// <summary>
-        /// 扫描历史结果
-        /// </summary>
-        /// <param name="cancellationToken"></param>
-        /// <returns></returns>
-        public async Task ScanResultAsync(CancellationToken cancellationToken)
-        {
-            this.logger.LogInformation("结果扫描开始..");
-
-            var results = this.scanResults.ToArray();
-            var contexts = results
-                .OrderBy(item => item.Domain)
-                .ThenByDescending(item => item.AvailableRate)
-                .ThenBy(item => item.AvgElapsed);
-
-            foreach (var context in contexts)
-            {
-                await this.resultScanDelegate(context);
-                var domainLogger = this.loggerFactory.CreateLogger(context.Domain);
-                if (context.Available == true)
-                {
-                    domainLogger.LogInformation(context.ToStatisticsString());
-                }
-                else
-                {
-                    domainLogger.LogWarning(context.ToStatisticsString());
-                }
-            }
-            this.dnsFlushService.FlushGithubResolverCache();
-            this.logger.LogInformation($"结果扫描结束,共扫描{results.Length}条记录");
-            await this.scanResults.SaveDatasAsync(cancellationToken);
-        }
-    }
-}

+ 0 - 25
FastGithub.Scanner/IGithubLookupProvider.cs

@@ -1,25 +0,0 @@
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace FastGithub.Scanner
-{
-    /// <summary>
-    /// 定义域名的ip提值者
-    /// </summary>
-    interface IGithubLookupProvider
-    {
-        /// <summary>
-        /// 获取排序
-        /// </summary>
-        int Order { get; }
-
-        /// <summary>
-        /// 查找域名与ip关系
-        /// </summary>
-        /// <param name="domains"></param>
-        /// <param name="cancellationToken"></param>
-        /// <returns></returns>
-        Task<IEnumerable<DomainAddress>> LookupAsync(IEnumerable<string> domains, CancellationToken cancellationToken);
-    }
-}

+ 0 - 24
FastGithub.Scanner/IGithubResolver.cs

@@ -1,24 +0,0 @@
-using System.Net;
-
-namespace FastGithub.Scanner
-{
-    /// <summary>
-    /// github解析器
-    /// </summary>
-    public interface IGithubResolver
-    {
-        /// <summary>
-        /// 是否支持指定的域名
-        /// </summary>
-        /// <param name="domain"></param>
-        /// <returns></returns>
-        bool IsSupported(string domain);
-
-        /// <summary>
-        /// 解析指定的域名
-        /// </summary>
-        /// <param name="domain"></param>
-        /// <returns></returns>
-        IPAddress? Resolve(string domain);
-    }
-}

+ 0 - 183
FastGithub.Scanner/IPAddressRange.cs

@@ -1,183 +0,0 @@
-using System;
-using System.Buffers.Binary;
-using System.Collections;
-using System.Collections.Generic;
-using System.Diagnostics.CodeAnalysis;
-using System.Net;
-using System.Net.Sockets;
-
-namespace FastGithub.Scanner
-{
-    /// <summary>
-    /// 表示IP范围
-    /// </summary>
-    /// <remarks>
-    /// <para>• 192.168.1.0/24</para>
-    /// <para>• 192.168.1.1-192.168.1.254</para>
-    /// </remarks>
-    abstract class IPAddressRange : IEnumerable<IPAddress>
-    {
-        /// <summary>
-        /// 获取ip数量
-        /// </summary>
-        public abstract int Size { get; }
-
-        /// <summary>
-        /// 获取地址族
-        /// </summary>
-        public abstract AddressFamily AddressFamily { get; }
-
-        /// <summary>
-        /// 获取迭代器
-        /// </summary>
-        /// <returns></returns>
-        public abstract IEnumerator<IPAddress> GetEnumerator();
-
-
-        IEnumerator IEnumerable.GetEnumerator()
-        {
-            return this.GetEnumerator();
-        }
-
-        /// <summary>
-        /// 从多个ip范围文本解析
-        /// </summary>
-        /// <param name="ranges"></param>
-        /// <returns></returns>
-        public static IEnumerable<IPAddressRange> From(IEnumerable<string> ranges)
-        {
-            foreach (var item in ranges)
-            {
-                if (TryParse(item, out var range))
-                {
-                    yield return range;
-                }
-            }
-        }
-
-        /// <summary>
-        /// 尝试解析
-        /// </summary>
-        /// <param name="range"></param>
-        /// <param name="value"></param>
-        /// <returns></returns>
-        public static bool TryParse(ReadOnlySpan<char> range, [MaybeNullWhen(false)] out IPAddressRange value)
-        {
-            if (range.IsEmpty == false && IPNetwork.TryParse(range.ToString(), out var ipNetwork))
-            {
-                value = new CidrIPAddressRange(ipNetwork);
-                return true;
-            }
-
-            var index = range.IndexOf('-');
-            if (index >= 0)
-            {
-                var start = range.Slice(0, index);
-                var end = range[(index + 1)..];
-
-                if (IPAddress.TryParse(start, out var startIp) &&
-                   IPAddress.TryParse(end, out var endIp) &&
-                   startIp.AddressFamily == endIp.AddressFamily)
-                {
-                    value = new SplitIPAddressRange(startIp, endIp);
-                    return true;
-                }
-            }
-
-            value = null;
-            return false;
-        }
-
-        /// <summary>
-        /// 192.168.1.0/24
-        /// </summary>
-        private class CidrIPAddressRange : IPAddressRange
-        {
-            private readonly IPAddressCollection addressCollection;
-
-            private readonly AddressFamily addressFamily;
-
-            public override int Size => (int)this.addressCollection.Count;
-
-            public override AddressFamily AddressFamily => this.addressFamily;
-
-            public CidrIPAddressRange(IPNetwork network)
-            {
-                this.addressCollection = network.ListIPAddress(FilterEnum.All);
-                this.addressFamily = network.AddressFamily;
-            }
-
-            public override IEnumerator<IPAddress> GetEnumerator()
-            {
-                return ((IEnumerable<IPAddress>)this.addressCollection).GetEnumerator();
-            }
-        }
-
-        /// <summary>
-        /// 192.168.1.1-192.168.1.254
-        /// </summary>
-        private class SplitIPAddressRange : IPAddressRange
-        {
-            private readonly IPAddress start;
-            private readonly IPAddress end;
-
-            private readonly AddressFamily addressFamily;
-
-            public override AddressFamily AddressFamily => this.addressFamily;
-
-            public SplitIPAddressRange(IPAddress start, IPAddress end)
-            {
-                this.start = start;
-                this.end = end;
-                this.addressFamily = start.AddressFamily;
-            }
-
-            public override int Size
-            {
-                get
-                {
-                    if (this.start.AddressFamily == AddressFamily.InterNetworkV6)
-                    {
-                        var startValue = BinaryPrimitives.ReadInt64BigEndian(this.start.GetAddressBytes());
-                        var endValue = BinaryPrimitives.ReadInt64BigEndian(this.end.GetAddressBytes());
-                        return (int)(endValue - startValue) + 1;
-                    }
-                    else
-                    {
-                        var startValue = BinaryPrimitives.ReadInt32BigEndian(this.start.GetAddressBytes());
-                        var endValue = BinaryPrimitives.ReadInt32BigEndian(this.end.GetAddressBytes());
-                        return endValue - startValue + 1;
-                    }
-                }
-            }
-
-            public override IEnumerator<IPAddress> GetEnumerator()
-            {
-                return this.GetIPAddresses().GetEnumerator();
-            }
-
-            private IEnumerable<IPAddress> GetIPAddresses()
-            {
-                for (var i = 0; i < this.Size; i++)
-                {
-                    var value = i;
-                    yield return Add(this.start, value);
-                }
-            }
-
-            /// <summary>
-            /// 添加值
-            /// </summary>
-            /// <param name="address"></param>
-            /// <param name="value"></param>
-            /// <returns></returns>
-            private static IPAddress Add(IPAddress address, int value)
-            {
-                var span = address.GetAddressBytes().AsSpan();
-                var hostValue = BinaryPrimitives.ReadInt32BigEndian(span);
-                BinaryPrimitives.WriteInt32BigEndian(span, hostValue + value);
-                return new IPAddress(span);
-            }
-        }
-    }
-}

+ 0 - 150
FastGithub.Scanner/LookupProviders/GithubMetaProvider.cs

@@ -1,150 +0,0 @@
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Options;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Net.Http;
-using System.Net.Http.Json;
-using System.Net.Sockets;
-using System.Text.Json.Serialization;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace FastGithub.Scanner.LookupProviders
-{
-    /// <summary>
-    /// Github公开的域名与ip关系提供者
-    /// </summary>
-    [Service(ServiceLifetime.Singleton, ServiceType = typeof(IGithubLookupProvider))]
-    sealed class GithubMetaProvider : IGithubLookupProvider
-    {
-        private readonly IOptionsMonitor<GithubMetaProviderOptions> options;
-        private readonly IHttpClientFactory httpClientFactory;
-        private readonly ILogger<GithubMetaProvider> logger;
-        private const string META_URI = "https://api.github.com/meta";
-
-        /// <summary>
-        /// 获取排序
-        /// </summary>
-        public int Order => int.MaxValue;
-
-        /// <summary>
-        /// Github公开的域名与ip关系提供者
-        /// </summary>
-        /// <param name="options"></param>
-        /// <param name="logger"></param>
-        public GithubMetaProvider(
-            IOptionsMonitor<GithubMetaProviderOptions> options,
-            IHttpClientFactory httpClientFactory,
-            ILogger<GithubMetaProvider> logger)
-        {
-            this.options = options;
-            this.httpClientFactory = httpClientFactory;
-            this.logger = logger;
-        }
-
-        /// <summary>
-        /// 查找域名与ip关系
-        /// </summary>
-        /// <param name="domains"></param>
-        /// <param name="cancellationToken"></param>
-        /// <returns></returns>
-        public async Task<IEnumerable<DomainAddress>> LookupAsync(IEnumerable<string> domains, CancellationToken cancellationToken)
-        {
-            var setting = this.options.CurrentValue;
-            if (setting.Enable == false)
-            {
-                return Enumerable.Empty<DomainAddress>();
-            }
-
-            try
-            {
-                var httpClient = this.httpClientFactory.CreateClient(nameof(Scanner));
-                var meta = await GetMetaAsync(httpClient, setting.MetaUri, cancellationToken);
-                if (meta != null)
-                {
-                    return meta.ToDomainAddresses(domains);
-                }
-            }
-            catch (Exception ex)
-            {
-                cancellationToken.ThrowIfCancellationRequested();
-                this.logger.LogWarning($"加载远程的ip列表异常:{ex.Message}");
-            }
-
-            return Enumerable.Empty<DomainAddress>();
-        }
-
-
-        /// <summary>
-        /// 尝试获取meta
-        /// </summary>
-        /// <param name="httpClient"></param>
-        /// <param name="metaUri"></param>
-        /// <returns></returns>
-        private async Task<Meta?> GetMetaAsync(HttpClient httpClient, Uri metaUri, CancellationToken cancellationToken)
-        {
-            try
-            {
-                return await httpClient.GetFromJsonAsync<Meta>(META_URI, cancellationToken);
-            }
-            catch (Exception)
-            {
-                cancellationToken.ThrowIfCancellationRequested();
-                this.logger.LogWarning($"使用副本数据{metaUri}");
-                return await httpClient.GetFromJsonAsync<Meta>(metaUri, cancellationToken);
-            }
-        }
-
-        /// <summary>
-        /// github的meta结构
-        /// </summary>
-        private class Meta
-        {
-            [JsonPropertyName("web")]
-            public string[] Web { get; set; } = Array.Empty<string>();
-
-            [JsonPropertyName("api")]
-            public string[] Api { get; set; } = Array.Empty<string>();
-
-            /// <summary>
-            /// 转换为域名与ip关系
-            /// </summary>
-            /// <returns></returns>
-            public IEnumerable<DomainAddress> ToDomainAddresses(IEnumerable<string> domains)
-            {
-                const string github = "github.com";
-                const string apiGithub = "api.github.com";
-
-                if (domains.Contains(github) == true)
-                {
-                    foreach (var range in IPAddressRange.From(this.Web).OrderBy(item => item.Size))
-                    {
-                        if (range.AddressFamily == AddressFamily.InterNetwork)
-                        {
-                            foreach (var address in range)
-                            {
-                                yield return new DomainAddress(github, address);
-                            }
-                        }
-                    }
-                }
-
-                if (domains.Contains(apiGithub) == true)
-                {
-                    foreach (var range in IPAddressRange.From(this.Api).OrderBy(item => item.Size))
-                    {
-                        if (range.AddressFamily == AddressFamily.InterNetwork)
-                        {
-                            foreach (var address in range)
-                            {
-                                yield return new DomainAddress(apiGithub, address);
-                            }
-                        }
-                    }
-                }
-            }
-        }
-    }
-}

+ 0 - 21
FastGithub.Scanner/LookupProviders/GithubMetaProviderOptions.cs

@@ -1,21 +0,0 @@
-using System;
-
-namespace FastGithub.Scanner.LookupProviders
-{
-    /// <summary>
-    /// Github公开的域名与ip关系提供者选项
-    /// </summary>
-    [Options("Lookup:GithubMetaProvider")]
-    sealed class GithubMetaProviderOptions
-    {
-        /// <summary>
-        /// 是否启用
-        /// </summary>
-        public bool Enable { get; set; }
-
-        /// <summary>
-        /// meta请求uri
-        /// </summary>
-        public Uri MetaUri { get; set; } = new Uri("https://gitee.com/jiulang/fast-github/raw/master/FastGithub/meta.json");
-    }
-}

+ 0 - 119
FastGithub.Scanner/LookupProviders/IPAddressComProvider.cs

@@ -1,119 +0,0 @@
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Options;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Net;
-using System.Net.Http;
-using System.Text;
-using System.Text.RegularExpressions;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace FastGithub.Scanner.LookupProviders
-{
-    /// <summary>
-    /// ipaddress.com的域名与ip关系提供者
-    /// </summary>
-    [Service(ServiceLifetime.Singleton, ServiceType = typeof(IGithubLookupProvider))]
-    sealed class IPAddressComProvider : IGithubLookupProvider
-    {
-        private readonly IOptionsMonitor<IPAddressComProviderOptions> options;
-        private readonly IHttpClientFactory httpClientFactory;
-        private readonly ILogger<IPAddressComProvider> logger;
-        private readonly Uri lookupUri = new("https://www.ipaddress.com/ip-lookup");
-
-        /// <summary>
-        /// 获取排序
-        /// </summary>
-        public int Order => default;
-
-        /// <summary>
-        /// ipaddress.com的域名与ip关系提供者
-        /// </summary>
-        /// <param name="options"></param>
-        /// <param name="logger"></param>
-        public IPAddressComProvider(
-            IOptionsMonitor<IPAddressComProviderOptions> options,
-            IHttpClientFactory httpClientFactory,
-            ILogger<IPAddressComProvider> logger)
-        {
-            this.options = options;
-            this.httpClientFactory = httpClientFactory;
-            this.logger = logger;
-        }
-
-        /// <summary>
-        /// 查找域名与ip关系
-        /// </summary>
-        /// <param name="domains"></param>
-        /// <param name="cancellationToken"></param>
-        /// <returns></returns>
-        public async Task<IEnumerable<DomainAddress>> LookupAsync(IEnumerable<string> domains, CancellationToken cancellationToken)
-        {
-            var setting = this.options.CurrentValue;
-            if (setting.Enable == false)
-            {
-                return Enumerable.Empty<DomainAddress>();
-            }
-
-            var httpClient = this.httpClientFactory.CreateClient(nameof(Scanner));
-            var result = new HashSet<DomainAddress>();
-            foreach (var domain in domains)
-            {
-                try
-                {
-                    var addresses = await this.LookupAsync(httpClient, domain, cancellationToken);
-                    foreach (var address in addresses)
-                    {
-                        result.Add(new DomainAddress(domain, address));
-                    }
-                }
-                catch (Exception)
-                {
-                    cancellationToken.ThrowIfCancellationRequested();
-                    this.logger.LogWarning($"ipaddress.com无法解析{domain}");
-                }
-            }
-            return result;
-        }
-
-        /// <summary>
-        /// 反查ip
-        /// </summary>
-        /// <param name="httpClient"></param>
-        /// <param name="domain"></param>
-        /// <returns></returns>
-        private async Task<List<IPAddress>> LookupAsync(HttpClient httpClient, string domain, CancellationToken cancellationToken)
-        {
-            using var request = new HttpRequestMessage
-            {
-                Method = HttpMethod.Post,
-                RequestUri = lookupUri,
-                Content = new StringContent($"host={domain}", Encoding.UTF8, "application/x-www-form-urlencoded")
-            };
-
-            using var response = await httpClient.SendAsync(request, cancellationToken);
-            var html = await response.Content.ReadAsStringAsync(cancellationToken);
-            var match = Regex.Match(html, @"(?<=<h1>IP Lookup : )\d+\.\d+\.\d+\.\d+", RegexOptions.IgnoreCase);
-
-            if (match.Success && IPAddress.TryParse(match.Value, out var address))
-            {
-                return new List<IPAddress> { address };
-            }
-
-            var prefix = Regex.Escape("type=\"radio\" value=\"");
-            var matches = Regex.Matches(html, @$"(?<={prefix})\d+\.\d+\.\d+\.\d+", RegexOptions.IgnoreCase);
-            var addressList = new List<IPAddress>();
-            foreach (Match item in matches)
-            {
-                if (IPAddress.TryParse(item.Value, out address))
-                {
-                    addressList.Add(address);
-                }
-            }
-            return addressList;
-        }
-    }
-}

+ 0 - 14
FastGithub.Scanner/LookupProviders/IPAddressComProviderOptions.cs

@@ -1,14 +0,0 @@
-namespace FastGithub.Scanner.LookupProviders
-{
-    /// <summary>
-    /// ipaddress.com的域名与ip关系提供者选项
-    /// </summary>
-    [Options("Lookup:IPAddressComProvider")]
-    sealed class IPAddressComProviderOptions
-    {
-        /// <summary>
-        /// 是否启用
-        /// </summary>
-        public bool Enable { get; set; }
-    }
-}

+ 0 - 105
FastGithub.Scanner/LookupProviders/PublicDnsProvider.cs

@@ -1,105 +0,0 @@
-using DNS.Client;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Options;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Net.Sockets;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace FastGithub.Scanner.LookupProviders
-{
-    /// <summary>
-    /// 公共dns的域名与ip关系提供者
-    /// </summary>
-    [Service(ServiceLifetime.Singleton, ServiceType = typeof(IGithubLookupProvider))]
-    sealed class PublicDnsProvider : IGithubLookupProvider
-    {
-        private readonly IOptionsMonitor<PublicDnsProviderOptions> options;
-        private readonly ILogger<PublicDnsProvider> logger;
-
-        /// <summary>
-        /// 获取排序
-        /// </summary>
-        public int Order => default;
-
-        /// <summary>
-        /// 公共dns的域名与ip关系提供者
-        /// </summary>
-        /// <param name="options"></param>
-        /// <param name="logger"></param>
-        public PublicDnsProvider(
-            IOptionsMonitor<PublicDnsProviderOptions> options,
-            ILogger<PublicDnsProvider> logger)
-        {
-            this.options = options;
-            this.logger = logger;
-        }
-
-        /// <summary>
-        /// 查找域名与ip关系
-        /// </summary>
-        /// <param name="domains"></param>
-        /// <param name="cancellationToken"></param>
-        /// <returns></returns>
-        public async Task<IEnumerable<DomainAddress>> LookupAsync(IEnumerable<string> domains, CancellationToken cancellationToken)
-        {
-            var setting = this.options.CurrentValue;
-            if (setting.Enable == false)
-            {
-                return Enumerable.Empty<DomainAddress>();
-            }
-
-            var result = new HashSet<DomainAddress>();
-            foreach (var dns in setting.Dnss)
-            {
-                var domainAddresses = await this.LookupAsync(dns, domains, cancellationToken);
-                foreach (var item in domainAddresses)
-                {
-                    result.Add(item);
-                }
-            }
-
-            return result;
-        }
-
-        /// <summary>
-        /// 反查ip
-        /// </summary>
-        /// <param name="dns">dns服务器</param>
-        /// <param name="domains">域名</param>
-        /// <returns></returns>
-        private async Task<List<DomainAddress>> LookupAsync(string dns, IEnumerable<string> domains, CancellationToken cancellationToken)
-        {
-            var client = new DnsClient(dns);
-            var result = new List<DomainAddress>();
-
-            foreach (var domain in domains)
-            {
-                try
-                {
-                    using var timeoutTokenSource = new CancellationTokenSource(this.options.CurrentValue.Timeout);
-                    using var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(timeoutTokenSource.Token, cancellationToken);
-
-                    var addresses = await client.Lookup(domain, cancellationToken: linkedTokenSource.Token);
-                    foreach (var address in addresses)
-                    {
-                        if (address.AddressFamily == AddressFamily.InterNetwork)
-                        {
-                            result.Add(new DomainAddress(domain, address));
-                        }
-                    }
-                }
-                catch (Exception)
-                {
-                    cancellationToken.ThrowIfCancellationRequested();
-                    this.logger.LogWarning($"dns({dns})无法解析{domain}");
-                }
-            }
-
-            return result;
-        }
-    }
-}

+ 0 - 26
FastGithub.Scanner/LookupProviders/PublicDnsProviderOptions.cs

@@ -1,26 +0,0 @@
-using System;
-
-namespace FastGithub.Scanner.LookupProviders
-{
-    /// <summary>
-    /// 公共dns的域名与ip关系提供者选项
-    /// </summary>
-    [Options("Lookup:PublicDnsProvider")]
-    sealed class PublicDnsProviderOptions
-    {
-        /// <summary>
-        /// 是否启用
-        /// </summary>
-        public bool Enable { get; set; } = true;
-
-        /// <summary>
-        /// dns查询超时时长
-        /// </summary>
-        public TimeSpan Timeout { get; set; } = TimeSpan.FromMilliseconds(100d);
-
-        /// <summary>
-        /// dns列表
-        /// </summary>
-        public string[] Dnss { get; set; } = Array.Empty<string>();
-    }
-}

+ 0 - 110
FastGithub.Scanner/RawSocketPing.cs

@@ -1,110 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Net;
-using System.Net.Sockets;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace FastGithub.Scanner
-{
-    /// <summary>
-    /// RawSocket的ping功能
-    /// </summary>
-    static class RawSocketPing
-    {
-        private static readonly byte[] echoRequestPacket = Convert.FromHexString("0800F6FF0100000000000000");
-        private static readonly byte[] icmpReceiveBuffer = new byte[72];
-
-        /// <summary>
-        /// 获取是否支持
-        /// </summary>
-        public static bool IsSupported { get; private set; }
-
-        /// <summary>
-        /// RawSocket的ping功能
-        /// </summary>
-        static RawSocketPing()
-        {
-            try
-            {
-                new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.Icmp).Dispose();
-                IsSupported = true;
-            }
-            catch (Exception)
-            {
-                IsSupported = false;
-            }
-        }
-
-        /// <summary>
-        /// ping目标ip
-        /// </summary>
-        /// <param name="destAddresses"></param>
-        /// <param name="timeWait">等待时间</param>
-        /// <param name="cancellationToken">取消令牌</param>
-        /// <returns>ping通的ip</returns>
-        public static async Task<HashSet<IPAddress>> PingAsync(IEnumerable<IPAddress> destAddresses, TimeSpan timeWait, CancellationToken cancellationToken = default)
-        {
-            using var socket = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.Icmp)
-            {
-                Ttl = 128,
-                DontFragment = false
-            };
-            socket.Bind(new IPEndPoint(IPAddress.Any, 0));
-
-            using var cancellationTokenSource = new CancellationTokenSource();
-            using var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, cancellationTokenSource.Token);
-            var receiveTask = ReceiveAsync(socket, linkedTokenSource.Token);
-
-            var distinctDestAddresses = destAddresses.Distinct();
-            foreach (var address in distinctDestAddresses)
-            {
-                var remoteEndPoint = new IPEndPoint(address, 0);
-                await socket.SendToAsync(echoRequestPacket, SocketFlags.None, remoteEndPoint);
-            }
-
-            await Task.Delay(timeWait, cancellationToken);
-            cancellationTokenSource.Cancel();
-            socket.Close();
-
-            var hashSet = await receiveTask;
-            hashSet.IntersectWith(distinctDestAddresses);
-            return hashSet;
-        }
-
-        /// <summary>
-        /// 循环接收任务
-        /// </summary>
-        /// <param name="socket"></param>
-        /// <param name="cancellationToken"></param>
-        /// <returns></returns>
-        private static async Task<HashSet<IPAddress>> ReceiveAsync(Socket socket, CancellationToken cancellationToken)
-        {
-            await Task.Yield();
-
-            var hashSet = new HashSet<IPAddress>();
-            var remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
-
-            while (cancellationToken.IsCancellationRequested == false)
-            {
-                try
-                {
-                    var result = await socket.ReceiveFromAsync(icmpReceiveBuffer, SocketFlags.None, remoteEndPoint);
-                    if (result.RemoteEndPoint is IPEndPoint ipEndPoint)
-                    {
-                        hashSet.Add(ipEndPoint.Address);
-                    }
-                }
-                catch (SocketException ex) when (ex.SocketErrorCode == SocketError.OperationAborted)
-                {
-                    break;
-                }
-                catch (Exception)
-                {
-                }
-            }
-            return hashSet;
-        }
-    }
-}

+ 0 - 44
FastGithub.Scanner/ScanMiddlewares/ConcurrentMiddleware.cs

@@ -1,44 +0,0 @@
-using Microsoft.Extensions.DependencyInjection;
-using System;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace FastGithub.Scanner.ScanMiddlewares
-{
-    /// <summary>
-    /// 扫描并发限制中间件
-    /// </summary>
-    [Service(ServiceLifetime.Singleton)]
-    sealed class ConcurrentMiddleware : IMiddleware<GithubContext>
-    {
-        private readonly SemaphoreSlim semaphoreSlim;
-
-        /// <summary>
-        /// 扫描并发限制中间件
-        /// </summary>
-        public ConcurrentMiddleware()
-        {
-            var currentCount = Environment.ProcessorCount * 2;
-            this.semaphoreSlim = new SemaphoreSlim(currentCount, currentCount);
-        }
-
-        /// <summary>
-        /// 限制描并发扫
-        /// </summary>
-        /// <param name="context"></param>
-        /// <param name="next"></param>
-        /// <returns></returns>
-        public async Task InvokeAsync(GithubContext context, Func<Task> next)
-        {
-            try
-            {
-                await this.semaphoreSlim.WaitAsync(context.CancellationToken);
-                await next();
-            }
-            finally
-            {
-                this.semaphoreSlim.Release();
-            }
-        }
-    }
-}

+ 0 - 117
FastGithub.Scanner/ScanMiddlewares/HttpsScanMiddleware.cs

@@ -1,117 +0,0 @@
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Options;
-using System;
-using System.ComponentModel.DataAnnotations;
-using System.Linq;
-using System.Net.Http;
-using System.Net.Http.Headers;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace FastGithub.Scanner.ScanMiddlewares
-{
-    /// <summary>
-    /// https扫描中间件
-    /// </summary>
-    [Service(ServiceLifetime.Singleton)]
-    sealed class HttpsScanMiddleware : IMiddleware<GithubContext>
-    {
-        private readonly IOptionsMonitor<HttpsScanOptions> options;
-        private readonly IHttpClientFactory httpClientFactory;
-        private readonly ILogger<HttpsScanMiddleware> logger;
-
-        /// <summary>
-        /// https扫描中间件
-        /// </summary>
-        /// <param name="options"></param>
-        /// <param name="logger"></param>
-        public HttpsScanMiddleware(
-            IOptionsMonitor<HttpsScanOptions> options,
-            IHttpClientFactory httpClientFactory,
-            ILogger<HttpsScanMiddleware> logger)
-        {
-            this.options = options;
-            this.httpClientFactory = httpClientFactory;
-            this.logger = logger;
-        }
-
-        /// <summary>
-        /// https扫描
-        /// </summary>
-        /// <param name="context"></param>
-        /// <param name="next"></param>
-        /// <returns></returns>
-        public async Task InvokeAsync(GithubContext context, Func<Task> next)
-        {
-            try
-            {
-                context.Available = false;
-
-                var setting = this.options.CurrentValue;
-                if (setting.Rules.TryGetValue(context.Domain, out var rule) == false)
-                {
-                    rule = new HttpsScanOptions.ScanRule();
-                }
-
-                using var request = new HttpRequestMessage();
-                request.Method = new HttpMethod(rule.Method);
-                request.RequestUri = new Uri(new Uri($"http://{context.Address}:443/"), rule.Path);
-                request.Headers.Host = context.Domain;
-                request.Headers.ConnectionClose = setting.ConnectionClose;
-
-                var timeout = this.options.CurrentValue.Timeout;
-                using var timeoutTokenSource = new CancellationTokenSource(timeout);
-                using var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(timeoutTokenSource.Token, context.CancellationToken);
-
-                var httpClient = this.httpClientFactory.CreateClient(nameof(Scanner));
-                using var response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, linkedTokenSource.Token);
-
-                VerifyHttpsResponse(context.Domain, response);
-                context.Available = true;
-
-                await next();
-            }
-            catch (Exception ex)
-            {
-                context.CancellationToken.ThrowIfCancellationRequested();
-                this.logger.LogTrace($"{context.Domain} {context.Address} { GetInnerMessage(ex)}");
-            }
-        }
-
-        /// <summary>
-        /// 验证响应内容
-        /// </summary>
-        /// <param name="domain"></param>
-        /// <param name="response"></param>
-        /// <exception cref="HttpRequestException"></exception>
-        /// <exception cref="ValidationException"></exception>
-        private static void VerifyHttpsResponse(string domain, HttpResponseMessage response)
-        {
-            response.EnsureSuccessStatusCode();
-
-            if (domain == "github.com" || domain.EndsWith(".github.com"))
-            {
-                if (response.Headers.Server.Any(item => IsGithubServer(item)) == false)
-                {
-                    throw new ValidationException("伪造的github服务");
-                }
-            }
-
-            static bool IsGithubServer(ProductInfoHeaderValue headerValue)
-            {
-                var value = headerValue.Product?.Name;
-                return string.Equals("github.com", value, StringComparison.OrdinalIgnoreCase);
-            }
-        }
-
-        private static string GetInnerMessage(Exception ex)
-        {
-            while (ex.InnerException != null)
-            {
-                return GetInnerMessage(ex.InnerException);
-            }
-            return ex.Message;
-        }
-    }
-}

+ 0 - 43
FastGithub.Scanner/ScanMiddlewares/HttpsScanOptions.cs

@@ -1,43 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-namespace FastGithub.Scanner.ScanMiddlewares
-{
-    /// <summary>
-    /// https扫描选项
-    /// </summary>
-    [Options("Scan:HttpsScan")]
-    sealed class HttpsScanOptions
-    {
-        /// <summary>
-        /// 扫描超时时长
-        /// </summary>
-        public TimeSpan Timeout { get; set; } = TimeSpan.FromSeconds(5d);
-
-        /// <summary>
-        /// 是否使用短连接
-        /// </summary>
-        public bool ConnectionClose { get; set; } = false;
-
-        /// <summary>
-        /// 各域名扫描规则
-        /// </summary>
-        public Dictionary<string, ScanRule> Rules { get; set; } = new Dictionary<string, ScanRule>();
-
-        /// <summary>
-        /// 扫描规则
-        /// </summary>
-        public class ScanRule
-        {
-            /// <summary>
-            /// 请求方式
-            /// </summary>
-            public string Method { get; set; } = "HEAD";
-
-            /// <summary>
-            /// 请求路径
-            /// </summary>
-            public string Path { get; set; } = "/";
-        }
-    }
-}

+ 0 - 37
FastGithub.Scanner/ScanMiddlewares/StatisticsMiddleware.cs

@@ -1,37 +0,0 @@
-using Microsoft.Extensions.DependencyInjection;
-using System;
-using System.Diagnostics;
-using System.Threading.Tasks;
-
-namespace FastGithub.Scanner.ScanMiddlewares
-{
-    /// <summary>
-    /// 扫描统计中间件
-    /// </summary>
-    [Service(ServiceLifetime.Singleton)]
-    sealed class StatisticsMiddleware : IMiddleware<GithubContext>
-    {
-        /// <summary>
-        /// 记录扫描结果
-        /// </summary>
-        /// <param name="context"></param>
-        /// <param name="next"></param>
-        /// <returns></returns>
-        public async Task InvokeAsync(GithubContext context, Func<Task> next)
-        {
-            var stopwatch = new Stopwatch();
-
-            try
-            {
-                stopwatch.Start();
-                await next();
-            }
-            finally
-            {
-                stopwatch.Stop();
-            }
-
-            context.AddScanLog(stopwatch.Elapsed);
-        }
-    }
-}

+ 0 - 88
FastGithub.Scanner/ScanMiddlewares/TcpScanMiddleware.cs

@@ -1,88 +0,0 @@
-using Microsoft.Extensions.Caching.Memory;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Options;
-using System;
-using System.Net.Sockets;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace FastGithub.Scanner.ScanMiddlewares
-{
-    /// <summary>
-    /// tcp扫描中间件
-    /// </summary>
-    [Service(ServiceLifetime.Singleton)]
-    sealed class TcpScanMiddleware : IMiddleware<GithubContext>
-    {
-        private const int PORT = 443;
-        private readonly IOptionsMonitor<TcpScanOptions> options;
-        private readonly IMemoryCache memoryCache;
-        private readonly ILogger<TcpScanMiddleware> logger;
-
-        /// <summary>
-        /// tcp扫描中间件
-        /// </summary>
-        /// <param name="options"></param>
-        /// <param name="logger"></param>
-        public TcpScanMiddleware(
-            IOptionsMonitor<TcpScanOptions> options,
-            IMemoryCache memoryCache,
-            ILogger<TcpScanMiddleware> logger)
-        {
-            this.options = options;
-            this.memoryCache = memoryCache;
-            this.logger = logger;
-        }
-
-        /// <summary>
-        /// tcp扫描
-        /// </summary>
-        /// <param name="context"></param>
-        /// <param name="next"></param>
-        /// <returns></returns>
-        public async Task InvokeAsync(GithubContext context, Func<Task> next)
-        {
-            var key = $"tcp://{context.Address}";
-            if (this.memoryCache.TryGetValue<bool>(key, out var available) == false)
-            {
-                available = await this.TcpScanAsync(context);
-                this.memoryCache.Set(key, available, this.options.CurrentValue.CacheExpiration);
-            }
-
-            if (available == true)
-            {
-                await next();
-            }
-            else
-            {
-                this.logger.LogTrace($"{context.Domain} {context.Address}的{PORT}端口未开放");
-            }
-        }
-
-
-        /// <summary>
-        /// tcp扫描
-        /// </summary>
-        /// <param name="context"></param>
-        /// <returns></returns>
-        private async Task<bool> TcpScanAsync(GithubContext context)
-        {
-            try
-            {
-                var timeout = this.options.CurrentValue.Timeout;
-                using var timeoutTokenSource = new CancellationTokenSource(timeout);
-                using var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(timeoutTokenSource.Token, context.CancellationToken);
-
-                using var socket = new Socket(context.Address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
-                await socket.ConnectAsync(context.Address, PORT, linkedTokenSource.Token);
-                return true;
-            }
-            catch (Exception)
-            {
-                context.CancellationToken.ThrowIfCancellationRequested();
-                return false;
-            }
-        }
-    }
-}

+ 0 - 21
FastGithub.Scanner/ScanMiddlewares/TcpScanOptions.cs

@@ -1,21 +0,0 @@
-using System;
-
-namespace FastGithub.Scanner.ScanMiddlewares
-{
-    /// <summary>
-    /// tcp扫描选项
-    /// </summary>
-    [Options("Scan:TcpScan")]
-    sealed class TcpScanOptions
-    {
-        /// <summary>
-        /// 扫描超时时长
-        /// </summary>
-        public TimeSpan Timeout { get; set; } = TimeSpan.FromSeconds(1d);
-
-        /// <summary>
-        /// 扫描结果缓存时长
-        /// </summary>
-        public TimeSpan CacheExpiration { get; set; } = TimeSpan.FromMinutes(30d);
-    }
-}

+ 0 - 47
FastGithub.Scanner/ScannerServiceCollectionExtensions.cs

@@ -1,47 +0,0 @@
-using FastGithub.Scanner;
-using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.DependencyInjection;
-using System;
-using System.Net.Http.Headers;
-
-namespace FastGithub
-{
-    /// <summary>
-    /// 服务注册扩展
-    /// </summary>
-    public static class ScannerServiceCollectionExtensions
-    {
-        /// <summary>
-        /// 注册程序集下所有服务下选项
-        /// </summary>
-        /// <param name="services"></param>
-        /// <param name="configuration">配置</param>  
-        /// <returns></returns>
-        public static IServiceCollection AddGithubScanner(this IServiceCollection services, IConfiguration configuration)
-        {
-            var assembly = typeof(ScannerServiceCollectionExtensions).Assembly;
-            var defaultUserAgent = new ProductInfoHeaderValue(assembly.GetName().Name ?? nameof(FastGithub), assembly.GetName().Version?.ToString());
-
-            services
-                .AddHttpClient(nameof(Scanner))
-                .SetHandlerLifetime(TimeSpan.FromMinutes(5d))
-                .ConfigureHttpClient(httpClient =>
-                {
-                    httpClient.Timeout = TimeSpan.FromSeconds(10d);
-                    httpClient.DefaultRequestHeaders.Accept.TryParseAdd("*/*");
-                    httpClient.DefaultRequestHeaders.UserAgent.Add(defaultUserAgent);
-                })
-                .ConfigurePrimaryHttpMessageHandler((serviceProvider) =>
-                {
-                    return serviceProvider.GetRequiredService<GithubHttpClientHanlder>();
-                });
-
-            return services
-                .AddMemoryCache()
-                .AddServiceAndOptions(assembly, configuration)
-                .AddHostedService<GithubFullScanHostedService>()
-                .AddHostedService<GithubResultScanHostedService>();
-            ;
-        }
-    }
-}

+ 12 - 0
FastGithub/.config/dotnet-tools.json

@@ -0,0 +1,12 @@
+{
+  "version": 1,
+  "isRoot": true,
+  "tools": {
+    "dotnet-ef": {
+      "version": "5.0.8",
+      "commands": [
+        "dotnet-ef"
+      ]
+    }
+  }
+}

+ 2 - 2
FastGithub/publish.cmd

@@ -1,2 +1,2 @@
-dotnet publish -c Release -f net6.0 /p:PublishSingleFile=true /p:PublishTrimmed=true -r linux-x64 -o ./bin/publish/linux-x64
-dotnet publish -c Release -f net6.0 /p:PublishSingleFile=true /p:PublishTrimmed=true -r win-x64 -o ./bin/publish/win-x64
+dotnet publish -c Release -f net6.0 /p:PublishSingleFile=true /p:PublishTrimmed=true -r win-x64 -o ./bin/publish/win-x64
+dotnet publish -c Release -f net6.0 /p:PublishSingleFile=true /p:PublishTrimmed=true -r linux-x64 -o ./bin/publish/linux-x64