Ejemplo n.º 1
0
Archivo: dns.py Proyecto: z0day/GotoX
def dns_system_resolve(host, qtypes=qtypes):
    now = time()
    try:
        if local_dnsservers:
            iplist = _dns_remote_resolve(host,
                                         local_dnsservers,
                                         timeout=2,
                                         qtypes=qtypes)
        else:
            if AAAA in qtypes:
                # getaddrinfo 在Windows 下无法并发,其它系统未知
                if A in qtypes:
                    iplist = list(
                        set(ipaddr[4][0]
                            for ipaddr in socket.getaddrinfo(host, None)) -
                        GC.DNS_BLACKLIST)
                else:
                    iplist = list(
                        set(ipaddr[4][0] for ipaddr in socket.getaddrinfo(
                            host, None, socket.AF_INET6)) - GC.DNS_BLACKLIST)
            else:
                iplist = list(
                    set(socket.gethostbyname_ex(host)[-1]) - GC.DNS_BLACKLIST)
    except:
        iplist = None
    cost = int((time() - now) * 1000)
    logging.test('dns_system_resolve 已缓存:%s/%s,耗时:%s 毫秒,%s = %s', len(dns),
                 dns.max_items, cost, host, iplist or '查询失败')
    return iplist
Ejemplo n.º 2
0
def dns_system_resolve(host, qtypes=qtypes):
    start = mtime()
    try:
        if dns_system_servers:
            iplist = _dns_udp_resolve(host,
                                      dns_system_servers,
                                      timeout=2,
                                      qtypes=qtypes)
        # getaddrinfo 在 Windows 下无法并发,其它系统未知
        else:
            if AAAA not in qtypes:
                iplist = socket.gethostbyname_ex(host)[-1]
            elif A in qtypes:
                iplist = [
                    ipaddr[4][0] for ipaddr in socket.getaddrinfo(host, None)
                ]
            else:
                iplist = [
                    ipaddr[4][0] for ipaddr in socket.getaddrinfo(
                        host, None, socket.AF_INET6)
                ]
    except:
        iplist = None
    cost = int((mtime() - start) * 1000)
    logging.test('%sdns_system_resolve 已缓存:%s/%s,耗时:%s 毫秒,%s = %s',
                 address_string(iplist), len(dns), dns.max_items, cost, host,
                 iplist or '查询失败')
    return iplist
Ejemplo n.º 3
0
def refreship(needgws=None, needcom=None):
    threading.current_thread().setName('Find GAE')
    #检测当前 IP 并搜索新的 IP
    network_test()
    if needgws is None:
        needgws = countneedgws()
    if needcom is None:
        needcom = countneedcom()
    gaeip = getgaeip(GC.IPLIST_MAP['google_gws'], needgws, needcom)
    #更新 IP
    if gaeip and gaeip['google_gws']:
        _refreship(gaeip)
        #更新 ip.use
        with open(GC.CONFIG_IPDB, 'wb') as f:
            write = writebytes(f.write)
            f.write(ipuse_h)
            for name in gaeip:
                write(name)
                f.write(b' = ')
                write('|'.join(GC.IPLIST_MAP[name]))
                f.write(b'\n')
        logging.test('GAE IP 更新完毕')
    if len(GC.IPLIST_MAP['google_gws']) < GC.FINDER_MINIPCNT:
        logging.warning('没有检测到足够数量符合要求的 GAE IP,请重新设定参数!')
    #更新完毕
    updateip.running = False
Ejemplo n.º 4
0
def check_response(response, host):
    if response:
        if response.headers.get('Server') == 'cloudflare':
            if response.headers.get('X-Fetch-Status'):  # ok / fail
                return 'ok'
            elif response.status == 429:  # a burst rate limit of 1000 requests per minute.
                if lock.acquire(timeout=1):
                    try:
                        logging.warning('CFW %r 超限,暂停使用 30 秒', cfw_params.host)
                        sleep(30)
                    finally:
                        lock.release()
            elif response.status == 302 or 400 <= response.status < 500 or \
                     response.status == 530 and dns_resolve(host):
                with lock:
                    try:
                        cfw_iplist.remove(response.xip[0])
                        logging.test('CFW 移除 %s', response.xip[0])
                    except:
                        pass
            elif response.status in (500, 530):
                return 'ok'
            else:
                #打印收集未知异常状态
                logging.warning('CFW %r 工作异常:%d %s', cfw_params.host,
                                response.status, response.reason)
                return 'ok'
        else:
            logging.error('CFW %r 工作异常:%r 可能不是可用的 CloudFlare 节点',
                          cfw_params.host, response.xip[0])
        return 'retry'
    else:
        logging.test('CFW %r 连接失败', cfw_params.host)
        return 'fail'
Ejemplo n.º 5
0
 def clear_node(node, pname):
     for k, v in node.items():
         lname = '%s.%s' % (k, pname)
         if v is self.leaf:
             logging.test('移除直连域名:%s', lname)
             self.count_dm -= 1
         else:
             clear_node(v, lname)
Ejemplo n.º 6
0
def dns_over_https_resolve(host, qtypes=qtypes):
    start = mtime()
    iplist = _dns_over_https_resolve(host, qtypes=qtypes)
    cost = int((mtime() - start) * 1000)
    logging.test('%sdns_over_https 已缓存:%s/%s,耗时:%s 毫秒,%s = %s',
                 address_string(iplist), len(dns), dns.max_items, cost, host,
                 iplist or '查询失败')
    return iplist
Ejemplo n.º 7
0
Archivo: dns.py Proyecto: z0day/GotoX
def dns_over_https_resolve(host, qtypes=qtypes):
    if not GC.DNS_OVER_HTTPS:
        return
    now = time()
    iplist = _dns_over_https_resolve(host, qtypes=qtypes)
    cost = int((time() - now) * 1000)
    logging.test('%sdns_over_https 已缓存:%s/%s,耗时:%s 毫秒,%s = %s',
                 address_string(iplist), len(dns), dns.max_items, cost, host,
                 iplist or '查询失败')
    return iplist
Ejemplo n.º 8
0
def dns_local_resolve(host, qtypes=qtypes):
    start = time()
    iplist = _dns_remote_resolve(host,
                                 GC.DNS_LOCAL_SERVERS,
                                 timeout=2,
                                 qtypes=qtypes)
    cost = int((time() - start) * 1000)
    logging.test('%sdns_local_resolve 已缓存:%s/%s,耗时:%s 毫秒,%s = %s',
                 address_string(iplist), len(dns), dns.max_items, cost, host,
                 iplist or '查询失败')
    return iplist
Ejemplo n.º 9
0
def dns_local_resolve(host, qtypes=qtypes):
    start = mtime()
    iplist = _dns_udp_resolve(host,
                              dns_local_servers,
                              timeout=2,
                              qtypes=qtypes)
    cost = int((mtime() - start) * 1000)
    logging.test('%sdns_local_resolve 已缓存:%s/%s,耗时:%s 毫秒,%s = %s',
                 address_string(iplist), len(dns), dns.max_items, cost, host,
                 iplist or '查询失败')
    return iplist
Ejemplo n.º 10
0
Archivo: dns.py Proyecto: z0day/GotoX
def dns_remote_resolve(host, qtypes=qtypes):
    now = time()
    iplist = _dns_remote_resolve(host,
                                 GC.DNS_SERVERS,
                                 GC.DNS_BLACKLIST,
                                 timeout=2,
                                 qtypes=qtypes)
    cost = int((time() - now) * 1000)
    logging.test('%sdns_remote_resolve 已缓存:%s/%s,耗时:%s 毫秒,%s = %s',
                 address_string(iplist), len(dns), dns.max_items, cost, host,
                 iplist or '查询失败')
    return iplist
Ejemplo n.º 11
0
 def _verify_callback(self, sock, cert, error_number, depth, ok):
     if ok and depth == 0 and not self.gws:
         self.match_hostname(sock, cert)
     elif error_number:
         if error_number in CertificateErrorTab:
             raise CertificateError(-1, (CertificateErrorTab[error_number](cert), depth))
         else:
             logging.test('%s:%d-%d,%s', sock.get_servername(), depth, error_number, cert.get_subject())
     elif depth and ok:
         #添加可信的中间证书,一定程度上有助于验证配置缺失的服务器
         #下一步计划使用直接下载
         OpenSSL._util.lib.X509_STORE_add_cert(self._cert_store, cert._x509)
     return ok
Ejemplo n.º 12
0
def get_wan_ipv4():
    for url in GC.DNS_IP_API:
        response = None
        try:
            response = direct_opener.open(url, timeout=10)
            content = response.read().decode().strip()
            if isip(content):
                logging.test('当前 IPv4 公网出口 IP 是:%s', content)
                return content
        except:
            pass
        finally:
            if response:
                response.close()
    logging.warning('获取 IPv4 公网出口 IP 失败,请增加更多的 IP-API')
Ejemplo n.º 13
0
    def add(self, domain):
        if not domain or not isinstance(domain, str) or \
                len(domain) > 253 or \
                self.add_ip(domain) or \
                self.check_domain(domain) is None:
            return

        def clear_node(node, pname):
            for k, v in node.items():
                lname = '%s.%s' % (k, pname)
                if v is self.leaf:
                    logging.debug('移除直连域名:%s', lname)
                    self.count_dm -= 1
                else:
                    clear_node(v, lname)

        if domain[0] == '.':
            domain = domain[1:]
        domain = domain.lower()
        names = domain.split('.')
        node = self.root
        while names:
            name = names.pop()
            try:
                child = node[name]
            except KeyError:
                if names:
                    node[name] = child = {}
                else:
                    node[name] = self.leaf
                    break
            else:
                if child is self.leaf:
                    lname = domain[domain.find(name):]
                    logging.test('发现重复直连域名:%s < %s', domain, lname)
                    return
                elif not names:
                    node[name] = self.leaf
                    lname = domain[domain.find(name):]
                    logging.test('发现重复直连域名:%s > *.%s', domain, lname)
                    clear_node(child, lname)
            node = child
        self.count_dm += 1
Ejemplo n.º 14
0
def load_domains():
    global direct_domains_tree, DDTVer
    domains_tree = DomainsTree('直连/白名单')
    if os.path.exists(direct_domains):
        domains_tree.add_file(direct_domains)
        DDTVer = '%s, domains count: %d, IPs count: %d' % (
            domains_tree.update, domains_tree.count_dm, domains_tree.count_ip)
    else:
        DDTVer = '列表文件未安装'
        buildscript = os.path.join(launcher_dir, 'builddomains.py')
        logging.warning('无法找到直连域名列表文件,Win 用户可用托盘工具下载更新,'
                        '其它系统请运行脚本 %r 下载更新。', buildscript)
    logging.test('开始添加内置直连域名列表')
    for domain in direct_tlds:
        domains_tree.add(domain)
    logging.test('开始添加用户本地域名列表')
    for domain in GC.DNS_LOCAL_WHITELIST:
        domains_tree.add(domain)
    direct_domains_tree = domains_tree
Ejemplo n.º 15
0
def get_wan_ipv4():
    if direct_opener is None:
        init_direct_opener()
    if dns_ip_api:
        apis = list(dns_ip_api)
        random.shuffle(apis)
        for url in apis:
            response = None
            try:
                response = direct_opener.open(url, timeout=10)
                content = response.read().decode().strip()
                if isipv4(content):
                    logging.test('当前 IPv4 公网出口 IP 是:%s', content)
                    return content
            except:
                pass
            finally:
                if response:
                    response.close()
    logging.warning('获取 IPv4 公网出口 IP 失败,请增加更多的 IP-API')
Ejemplo n.º 16
0
def check_response(response, host):
    if response:
        if response.headers.get('Server') == 'cloudflare':
            # https://support.cloudflare.com/hc/zh-cn/articles/115003014512-4xx-客户端错误
            # https://support.cloudflare.com/hc/zh-cn/articles/115003011431-Cloudflare-5XX-错误故障排除
            # https://support.cloudflare.com/hc/zh-cn/articles/360029779472-Cloudflare-1XXX-错误故障排除
            if response.headers.get('X-Fetch-Status'):  # ok / fail
                return 'ok'
            content = None
            if response.status == 530:
                ce = response.headers.get('Content-Encoding')
                if ce and ce in decompress_readers:
                    del response.headers['Content-Encoding']
                    response = decompress_readers[ce](response)
                content = response.read()
                response.fp = BytesIO(content)
                response.chunked = False
                response.length = len(content)
            if content and (cfw_530_ignore not in content
                            or not dns_resolve(GC.CFW_WORKER)):
                return 'ok'
            if response.status == 429:
                # https://developers.cloudflare.com/workers/platform/limits#request
                # a burst rate limit of 1000 requests per minute.
                if lock.acquire(timeout=1):
                    try:
                        logging.warning('CFW %r 超限,暂停使用 30 秒', cfw_params.host)
                        sleep(30)
                    finally:
                        lock.release()
            elif response.status in (502, 503, 504):
                sleep(5)
            elif remove_badip(response.xip[0]):
                logging.test('CFW %d 移除 %s', response.status, response.xip[0])
        elif remove_badip(response.xip[0]):
            logging.error('CFW %r 工作异常:%r 可能不是可用的 CloudFlare 节点',
                          cfw_params.host, response.xip[0])
        return 'retry'
    else:
        logging.test('CFW %r 连接失败', cfw_params.host)
        return 'fail'
Ejemplo n.º 17
0
def check_response(response, host):
    if response:
        if response.headers.get('Server') == 'cloudflare':
            if response.headers.get('X-Fetch-Status'):  # ok / fail
                return 'ok'
            elif response.status == 429:
                # https://developers.cloudflare.com/workers/about/limits/
                # a burst rate limit of 1000 requests per minute.
                if lock.acquire(timeout=1):
                    try:
                        logging.warning('CFW %r 超限,暂停使用 30 秒', cfw_params.host)
                        sleep(30)
                    finally:
                        lock.release()
            elif response.status == 302 or 400 <= response.status < 500 or \
                     response.status == 530 and dns_resolve(host):
                # https://support.cloudflare.com/hc/zh-cn/articles/360029779472-Cloudflare-1XXX-错误故障排除
                # https://support.cloudflare.com/hc/zh-cn/articles/115003011431-Cloudflare-5XX-错误故障排除
                with lock:
                    try:
                        cfw_iplist.remove(response.xip[0])
                        logging.test('CFW 移除 %s', response.xip[0])
                    except:
                        pass
            elif response.status in (500, 530):
                return 'ok'
            else:
                #打印收集未知异常状态
                logging.warning('CFW %r 工作异常:%d %s', cfw_params.host,
                                response.status, response.reason)
                return 'ok'
        else:
            logging.error('CFW %r 工作异常:%r 可能不是可用的 CloudFlare 节点',
                          cfw_params.host, response.xip[0])
        return 'retry'
    else:
        logging.test('CFW %r 连接失败', cfw_params.host)
        return 'fail'
Ejemplo n.º 18
0
def _testallgaeip():
    iplist = GC.IPLIST_MAP['google_gws']
    if not iplist:
        testip.running = False
        return updateip()
    badip = set()
    timeout = gettimeout()
    timeoutl = timeout + 1000
    logging.test('连接测试开始,超时:%d 毫秒', timeout)
    network_test()
    testip.queobj.queue.clear()
    for ip in iplist:
        if ip in GC.IPLIST_MAP['google_com']:
            _timeout = timeoutl
        else:
            _timeout = timeout
        thread.start_new_thread(
            http_gws._create_ssl_connection,
            ((ip, 443), getcachekey(), None, testip.queobj, _timeout / 1000))
    for _ in iplist:
        result = testip.queobj.get()
        if isinstance(result, Exception):
            ip = result.xip[0]
            logging.warning('测试失败 %s:%s' %
                            ('.'.join(x.rjust(3)
                                      for x in ip.split('.')), result.args[0]))
            badip.add(ip)
        else:
            logging.test('测试连接 %s: %d' % ('.'.join(
                x.rjust(3)
                for x in result[0].split('.')), int(result[1] * 1000)))
    #删除 bad IP
    nbadip = len(badip)
    if nbadip > 0:
        for ip in badip:
            removeip(ip)
    logging.test('连接测试完毕%s', ',Bad IP 已删除' if nbadip > 0 else '')
    testip.lastactive = testip.lasttest = time()
    testip.running = False
    #刷新开始
    needgws = countneedgws()
    needcom = countneedcom()
    if needgws > 0 or needcom > 0:
        updateip(needgws, needcom)
Ejemplo n.º 19
0
        with open('/etc/resolv.conf', 'r') as fp:
            return re.findall(r'(?m)^nameserver\s+(\S+)', fp.read())
    else:
        import sys
        logging.warning('get_dnsserver_list 失败:不支持 "%s-%s" 平台', sys.platform,
                        os.name)
        return []


local_dnsservers = set(ip for ip in get_dnsserver_list() if isip(ip))
if '127.0.0.1' in local_dnsservers and '::1' in local_dnsservers:
    #视为同一个本地服务器,大多数情况下这是正确地
    local_dnsservers.remove('::1')
local_dnsservers = tuple((server, 53) for server in local_dnsservers)
if local_dnsservers:
    logging.test('已读取系统当前 DNS 设置:%r', local_dnsservers)
else:
    logging.warning('读取系统当前 DNS 设置失败')


def dns_system_resolve(host, qtypes=qtypes):
    start = time()
    try:
        if local_dnsservers:
            iplist = _dns_remote_resolve(host,
                                         local_dnsservers,
                                         timeout=2,
                                         qtypes=qtypes)
        # getaddrinfo 在 Windows 下无法并发,其它系统未知
        else:
            if AAAA not in qtypes:
Ejemplo n.º 20
0
def PRINT(fmt, *args, **kwargs):
    #logging.info(strlog)
    logging.test('[%s] %s' % (threading.current_thread().name, fmt), *args,
                 **kwargs)
Ejemplo n.º 21
0
        with open('/etc/resolv.conf', 'r') as fp:
            return re.findall(r'(?m)^nameserver\s+(\S+)', fp.read())
    else:
        import sys
        logging.warning('get_dnsserver_list 失败:不支持 "%s-%s" 平台', sys.platform,
                        os.name)
        return []


dns_system_servers = set(ip for ip in get_dnsserver_list() if isip(ip))
if '127.0.0.1' in dns_system_servers and '::1' in dns_system_servers:
    #视为同一个本地服务器,大多数情况下这是正确地
    dns_system_servers.remove('::1')
dns_system_servers = tuple((server, 53) for server in dns_system_servers)
if dns_system_servers:
    logging.test('已读取系统当前 DNS 设置:%r', dns_system_servers)
else:
    logging.warning('读取系统当前 DNS 设置失败')


def dns_system_resolve(host, qtypes=qtypes):
    start = mtime()
    try:
        if dns_system_servers:
            iplist = _dns_udp_resolve(host,
                                      dns_system_servers,
                                      timeout=2,
                                      qtypes=qtypes)
        # getaddrinfo 在 Windows 下无法并发,其它系统未知
        else:
            if AAAA not in qtypes:
Ejemplo n.º 22
0
 def __fetchlet(self, range_queue, data_queue, threadorder):
     headers = {k.title(): v for k, v in self.headers.items()}
     #headers['Connection'] = 'close'
     while True:
         try:
             with self.tLock:
                 if self.lastupdate != ip_manager_gae.last_update:
                     self.lastupdate = ip_manager_gae.last_update
                     self.iplist = GC.IPLIST_MAP['google_gae'].copy()
             noerror = True
             response = None
             starttime = None
             appid = None
             if self._stopped: return
             try:
                 if self.response:
                     response = self.response
                     self.response = None
                     start, end = self.firstrange
                 else:
                     appid = get_appid()
                     if self._last_app_status.get(appid, 200) >= 500:
                         sleep(2)
                     start, end = range_queue.get(timeout=1)
                 headers['Range'] = 'bytes=%d-%d' % (start, end)
                 while start - self.expect_begin > self.threads * self.delaysize and \
                         data_queue.qsize() * self.bufsize > 3 * self.threads * self.delaysize:
                     if self._stopped: return
                     sleep(0.1)
                 if appid:
                     response = gae_urlfetch(self.command,
                                             self.url,
                                             headers,
                                             self.payload,
                                             appid,
                                             getfast=self.timeout)
                 if response:
                     if appid:
                         self._last_app_status[appid] = response.app_status
                     xip = response.xip[0]
                     if xip in self.iplist:
                         realstart = start
                         starttime = time()
                     else:
                         range_queue.put((start, end))
                         noerror = False
                         continue
             except queue.Empty:
                 appid = None
                 return
             except LimiterFull:
                 range_queue.put((start, end))
                 sleep(2)
                 continue
             except Exception as e:
                 logging.warning('%s Response %r in __fetchlet',
                                 self.address_string(response), e)
                 range_queue.put((start, end))
                 continue
             if self._stopped: return
             if not response:
                 logging.warning('%s RangeFetch %s 没有响应,重试',
                                 self.address_string(response),
                                 headers['Range'])
                 range_queue.put((start, end))
             elif response.app_status == 503:
                 if appid:
                     mark_badappid(appid)
                 range_queue.put((start, end))
                 noerror = False
             elif response.app_status != 200:
                 logging.warning('%s Range Fetch "%s %s" %s 返回 %s',
                                 self.address_string(response),
                                 self.command, self.url, headers['Range'],
                                 response.app_status)
                 range_queue.put((start, end))
                 noerror = False
             elif response.getheader('Location'):
                 self.url = urljoin(self.url,
                                    response.getheader('Location'))
                 logging.info('%s RangeFetch Redirect(%r)',
                              self.address_string(response), self.url)
                 range_queue.put((start, end))
             elif 200 <= response.status < 300:
                 content_range = response.getheader('Content-Range')
                 if not content_range:
                     logging.warning(
                         '%s RangeFetch "%s %s" 返回 Content-Range=%r: response headers=%r',
                         self.address_string(response), self.command,
                         self.url, content_range, response.getheaders())
                     range_queue.put((start, end))
                     continue
                 content_length = int(
                     response.getheader('Content-Length', 0))
                 logging.test('%s >>>> %s: 线程 %s %s %s',
                              self.address_string(response), self.host,
                              threadorder, content_length, content_range)
                 try:
                     data = response.read(self.bufsize)
                     while data:
                         data_queue.put((start, data))
                         start += len(data)
                         if self._stopped: return
                         if (start - realstart) / (
                                 time() - starttime) < self.lowspeed:
                             #移除慢速 ip
                             if self.delable:
                                 with self.tLock:
                                     if xip in self.iplist and len(
                                             self.iplist) > self.minip:
                                         self.iplist.remove(xip)
                                         logging.warning(
                                             '%s RangeFetch 移除慢速 ip %s',
                                             self.address_string(), xip)
                             noerror = False
                             break
                         else:
                             data = response.read(self.bufsize)
                 except Exception as e:
                     noerror = False
                     logging.warning('%s RangeFetch "%s %s" %s 失败:%r',
                                     self.address_string(response),
                                     self.command, self.url,
                                     headers['Range'], e)
                 if self._stopped: return
                 if start < end + 1:
                     logging.warning('%s RangeFetch "%s %s" 重试 %s-%s',
                                     self.address_string(response),
                                     self.command, self.url, start, end)
                     range_queue.put((start, end))
                     continue
                 logging.test('%s >>>> %s: 线程 %s 成功接收到 %d 字节',
                              self.address_string(response), self.host,
                              threadorder, start)
             else:
                 logging.error('%s RangeFetch %r 返回 %s',
                               self.address_string(response), self.url,
                               response.status)
                 range_queue.put((start, end))
                 noerror = False
         except Exception as e:
             logging.exception('%s RangeFetch._fetchlet 错误:%r',
                               self.address_string(), e)
             noerror = False
             raise
         finally:
             if appid:
                 qGAE.put(True)
             if response:
                 response.close()
                 if noerror:
                     #放入套接字缓存
                     ssl_connection_cache['google_gae|:443'].append(
                         (time(), response.sock))
                 elif self.delable:
                     with self.tLock:
                         if xip in self.iplist and len(
                                 self.iplist) > self.minip:
                             self.iplist.remove(xip)
                             logging.warning('%s RangeFetch 移除故障 ip %s',
                                             self.address_string(response),
                                             xip)
             if noerror:
                 sleep(0.1)
Ejemplo n.º 23
0
def import_ca(certfile=None):
    if certfile is None:
        certfile = ca_certfile
    with open(certfile, 'rb') as fp:
        certdata = fp.read().strip()
    try:
        begin = b'-----BEGIN CERTIFICATE-----'
        end = b'-----END CERTIFICATE-----'
        if certdata.startswith(begin) and certdata.endswith(end):
            certdata = binascii.a2b_base64(certdata[len(begin):-len(end)])
        commonname = crypto.load_certificate(crypto.FILETYPE_ASN1,
                                             certdata).get_subject().CN
    except Exception as e:
        logging.error('load_certificate(certfile=%r) 失败:%s', certfile, e)
        return -1

    if sys.platform.startswith('win'):
        import ctypes
        import ctypes.wintypes

        class CERT_CONTEXT(ctypes.Structure):
            _fields_ = [
                ('dwCertEncodingType', ctypes.wintypes.DWORD),
                ('pbCertEncoded', ctypes.POINTER(ctypes.wintypes.BYTE)),
                ('cbCertEncoded', ctypes.wintypes.DWORD),
                ('pCertInfo', ctypes.c_void_p),
                ('hCertStore', ctypes.c_void_p),
            ]

        X509_ASN_ENCODING = 0x1
        CERT_STORE_ADD_ALWAYS = 4
        CERT_STORE_PROV_SYSTEM = 10
        CERT_STORE_OPEN_EXISTING_FLAG = 0x4000
        CERT_SYSTEM_STORE_CURRENT_USER = 1 << 16
        CERT_SYSTEM_STORE_LOCAL_MACHINE = 2 << 16
        CERT_FIND_SUBJECT_STR = 8 << 16 | 7
        crypt32 = ctypes.windll.crypt32
        ca_exists = False
        store_handle = None
        pCertCtx = None
        for store in (CERT_SYSTEM_STORE_LOCAL_MACHINE,
                      CERT_SYSTEM_STORE_CURRENT_USER):
            try:
                store_handle = crypt32.CertOpenStore(
                    CERT_STORE_PROV_SYSTEM, 0, None,
                    CERT_STORE_OPEN_EXISTING_FLAG | store, 'root')
                if not store_handle:
                    if store == CERT_SYSTEM_STORE_CURRENT_USER and not ca_exists:
                        logging.warning('导入证书时发生错误:无法打开 Windows 系统证书仓库')
                        return -1
                    else:
                        continue

                pCertCtx = crypt32.CertFindCertificateInStore(
                    store_handle, X509_ASN_ENCODING, 0, CERT_FIND_SUBJECT_STR,
                    commonname, None)
                while pCertCtx:
                    certCtx = CERT_CONTEXT.from_address(pCertCtx)
                    _certdata = ctypes.string_at(certCtx.pbCertEncoded,
                                                 certCtx.cbCertEncoded)
                    if _certdata == certdata:
                        ca_exists = True
                        logging.test("证书 %r 已经存在于 Windows 系统证书仓库", commonname)
                    else:
                        cert = crypto.load_certificate(crypto.FILETYPE_ASN1,
                                                       _certdata)
                        if cert.get_subject().CN == commonname:
                            ret = crypt32.CertDeleteCertificateFromStore(
                                crypt32.CertDuplicateCertificateContext(
                                    pCertCtx))
                            if ret == 1:
                                logging.test("已经移除无效的 Windows 证书 %r",
                                             commonname)
                            elif ret == 0 and store == CERT_SYSTEM_STORE_LOCAL_MACHINE:
                                logging.warning(
                                    '无法从 Windows 计算机账户删除无效证书 %r,请用管理员权限重新运行 GotoX,或者手动删除',
                                    commonname)
                    pCertCtx = crypt32.CertFindCertificateInStore(
                        store_handle, X509_ASN_ENCODING, 0,
                        CERT_FIND_SUBJECT_STR, commonname, pCertCtx)

                #只导入到当前用户账户,无需管理员权限
                if store == CERT_SYSTEM_STORE_CURRENT_USER and \
                        not ca_exists and \
                        crypt32.CertAddEncodedCertificateToStore(store_handle, X509_ASN_ENCODING, certdata, len(certdata), CERT_STORE_ADD_ALWAYS, None) == 1:
                    ca_exists = True
                    msg = ('已经将 GotoX CA 证书导入到系统证书仓库,请重启浏览器。\n\n'
                           '如果你使用的是 Firefox,且导入过老旧证书,请在高级设置中手动删除,'
                           '再重启浏览器,设置好代理后访问以下网址即可导入新证书:\n\n'
                           '\thttp://gotox.go')
                    title = 'GotoX 提示'
                    ctypes.windll.user32.MessageBoxW(None, msg, title, 48)
            except Exception as e:
                logging.warning('导入证书时发生错误:%r', e)
                if isinstance(e, OSError):
                    store_handle = None
            finally:
                if pCertCtx:
                    crypt32.CertFreeCertificateContext(pCertCtx)
                    pCertCtx = None
                if store_handle:
                    crypt32.CertCloseStore(store_handle, 0)
                    store_handle = None
        return 0 if ca_exists else -1

    #放弃其它系统
    return 0

    if sys.platform == 'darwin':
        return os.system((
            'security find-certificate -a -c "%s" | grep "%s" >/dev/null || security add-trusted-cert -d -r trustRoot -k "/Library/Keychains/System.keychain" "%s"'
            % (commonname, commonname,
               certfile.decode('utf-8'))).encode('utf-8'))

    if sys.platform.startswith('linux'):
        import platform
        platform_distname = platform.dist()[0]
        if platform_distname == 'Ubuntu':
            pemfile = "/etc/ssl/certs/%s.pem" % commonname
            new_certfile = "/usr/local/share/ca-certificates/%s.crt" % commonname
            if not os.path.exists(pemfile):
                return os.system('cp "%s" "%s" && update-ca-certificates' %
                                 (certfile, new_certfile))
        elif any(
                os.path.isfile('%s/certutil' % x)
                for x in os.environ['PATH'].split(os.pathsep)):
            return os.system(
                'certutil -L -d sql:$HOME/.pki/nssdb | grep "%s" || certutil -d sql:$HOME/.pki/nssdb -A -t "C,," -n "%s" -i "%s"'
                % (commonname, commonname, certfile))
        else:
            logging.warning(
                'please install *libnss3-tools* package to import GotoX root ca'
            )
    return 0
Ejemplo n.º 24
0
def testonegaeip():
    with tLock:
        if updateip.running or testip.running:
            return
        testip.running = 1
    iplist = GC.IPLIST_MAP['google_gws']
    if not iplist:
        testip.running = False
        return updateip()
    ip = iplist[-1]
    timeout = gettimeout()
    if ip in GC.IPLIST_MAP['google_com'] and len(
            GC.IPLIST_MAP['google_com']) < len(iplist):
        iplist.insert(0, iplist.pop())
        testip.running = False
        return
    badip = False
    statistics = finder.statistics
    network_test()
    testip.queobj.queue.clear()
    http_gws._create_ssl_connection((ip, 443), getcachekey(), None,
                                    testip.queobj, timeout / 1000)
    result = testip.queobj.get()
    if isinstance(result, Exception):
        logging.warning(
            '测试失败(超时:%d 毫秒)%s:%s,Bad IP 已删除' %
            (timeout, '.'.join(x.rjust(3)
                               for x in ip.split('.')), result.args[0]))
        removeip(ip)
        badip = True
        ipdict, ipdicttoday = statistics
        if ip in ipdict:
            good, bad = ipdict[ip]
            #失败次数超出预期,设置 -1 表示删除
            s = bad / max(good, 1)
            if s > 2 or (s > 0.4 and bad > 10):
                ipdict[ip] = ipdicttoday[ip] = -1, 0
            else:
                ipdict[ip] = good, bad + 1
                if ip in ipdicttoday:
                    good, bad = ipdicttoday[ip]
                else:
                    good = bad = 0
                ipdicttoday[ip] = good, bad + 1
        #加入统计
        else:
            ipdict[ip] = ipdicttoday[ip] = 0, 1
    else:
        logging.test('测试连接(超时:%d 毫秒)%s: %d' % (timeout, '.'.join(
            x.rjust(3) for x in result[0].split('.')), int(result[1] * 1000)))
        iplist.insert(0, iplist.pop())
        #调高 com 权重
        addn = 2 if ip in GC.IPLIST_MAP['google_com'] else 1
        baddict = finder.baddict
        for ipdict in statistics:
            if ip in ipdict:
                good, bad = ipdict[ip]
                good += addn
                ipdict[ip] = good, bad
                #当天通过测试次数达到条件后重置容忍次数
                if ipdict is statistics[1] and ip in baddict:
                    s = bad / max(good, 1)
                    if s < 0.1:
                        del baddict[ip]
            #加入统计
            else:
                ipdict[ip] = addn, 0
    savestatistics()
    testip.lasttest = time()
    testip.running = False
    #刷新开始
    needgws = countneedgws()
    needcom = countneedcom()
    if needgws > 0 or needcom > 0:
        updateip(needgws, needcom)
    elif badip:
        testonegaeip()