Esempio n. 1
0
def parseIp(ipPair, version):
    """解析本地网卡中的IP信息

    Args:
        ipPair (str): IP信息文本
        version (int): 4,6

    Returns:
        tuple: (ip, score)
    """
    ipLine, tlft = ipPair
    if version == 4:
        ipMatch = re.findall(r'inet ([0-9\.]+)/(\d+)\s', ipLine)
    elif version == 6:
        ipMatch = re.findall(r'inet6 ([0-9a-f:]+)/(\d+)\s', ipLine)
    APP.debug(ipMatch)
    ip, prefix = ipMatch[0]
    prefix = int(prefix)
    tlftMatch = re.findall(
        r'valid_lft (\d+sec|forever) preferred_lft (\d+sec|forever)', tlft)
    APP.debug(tlftMatch)
    valid, prefer = tlftMatch[0]

    if prefer == 'forever':
        score = sys.maxsize
    else:
        prefer = int(prefer[:-3])  # Remove 'sec'
        valid = int(valid[:-3])  # Remove 'sec'
        score = prefer + valid
        if 'mngtmpaddr' in ipLine:
            score = prefer * 2
    return (ip, score)
Esempio n. 2
0
def getIp(version):
    methodConfig = CONFIG[f'get_ipv{version}']
    ipRegex = None
    ipApi = None

    if 'regex' in methodConfig:
        ipRegex = getIpByRegex(version)

    if 'api' in methodConfig:
        ipData = getIpByApi(version)
        ipApi = ipData['ip']
        ipUrl = ipData['url']

    ip = None
    if ipRegex and ipApi:
        ip = ipApi
        if ipRegex != ipApi:
            APP.warn(f'IPv{version}不一致',
                     f'从网卡获取的IP为{ipRegex},从{ipUrl}获取的IP为{ipApi}')
    elif not ipRegex and not ipApi:
        raise RuntimeError(f'未获取到IPv{version}地址')
    elif ipApi:
        ip = ipApi
    elif ipRegex:
        ip = ipRegex
    return ip
Esempio n. 3
0
def getIpByRegex(version):
    """获取本机IP地址
    """
    if version == 4:
        command = 'ip -4 addr show scope global {} up | ' \
        'grep -v " deprecated" | ' \
        'grep -v " 0sec" | ' \
        'grep -A1 "inet [1-9]"'.format(CONFIG['interface'])
    elif version == 6:
        command = 'ip -6 addr show scope global {} up | ' \
            'grep -v " deprecated" | ' \
            'grep -v " 0sec" | ' \
            'grep -A1 "inet6 [^f:]"'.format(CONFIG['interface'])
    APP.debug(command)
    ipStr = subprocess.check_output(command, shell=True).decode('utf-8')
    APP.debug(ipStr)

    ipParts = [x.strip() for x in ipStr.split('\n')]
    APP.debug(ipParts)
    ipPairList = list(zip(ipParts[::2], ipParts[1::2]))
    APP.debug(f'获取到本地IPv{version} >>> {ipPairList}')

    if len(ipPairList) == 1:
        ips = parseIp(ipPairList[0], version)
        return ips[0]
    elif len(ipParts) > 1:
        ipList = []
        for ipPair in ipPairList:
            ips = parseIp(ipPair, version)
            ipList.append(ips)
        ipList.sort(key=lambda ip: ip[-1], reverse=True)
        APP.debug(ipList)
        return ipList[0][0]
    else:
        raise RuntimeError(f'无法找到IPv{version}地址')
Esempio n. 4
0
def notify_by_server_chan(config, data):
    prefix = 'error_' if 'error' in data else ''
    url = 'https://sc.ftqq.com/{sckey}.send'.format(**config)
    title = config[prefix + 'title'].format(**data)
    message = config[prefix + 'message'].format(**data)
    APP.debug(f'Server酱 数据===> {title} ; {message}')
    res = requests.get(url, params={'text': title, 'desp': message})
    APP.debug(f'Server酱推送结果:{res.json()}')
Esempio n. 5
0
def getIpByApi(version):
    apiUrls = ['ip.sb', 'myip.com', 'dyndns.com']
    for apiUrl in apiUrls:
        iper = Ip.create(apiUrl, version)
        ip = iper.getIp()
        APP.debug(ip)
        if ip['ip']:
            return ip
    return {'ip': None, 'url': None}
Esempio n. 6
0
def notifyByServerChan(config, data):
    prefix = 'error_' if 'error' in data else ''
    url = 'https://sc.ftqq.com/{sckey}.send'.format(**config)
    title = config[prefix + 'title'].format(**data)
    message = config[prefix + 'message'].format(**data)
    APP.debug(f'Server酱 数据===> {title} ; {message}')
    #p('Server酱===>', title, '; ', message)
    if not CONFIG['dry']:
        res = requests.get(url, params={'text': title, 'desp': message})
        #p('Server酱推送结果:', res.json())
        APP.debug(f'Server酱推送结果:{res.json()}')
Esempio n. 7
0
def saveIP(version, ip, domains):
    """保存新IP

    Args:
        ip (str): 新IP
        version (int): 4,6
    """
    ipFilePath = IP_FILE.format(version)
    for domain in domains:
        DOMAIN_IP[domain] = ip
    dump_json(ipFilePath, DOMAIN_IP)
    APP.debug(f'新IP地址已经保存到{ipFilePath}')
Esempio n. 8
0
def notify_by_ding_talk(config, data):
    """发消息给钉钉机器人
    """
    dt_data = data.copy()
    dt_data['comment_li'] = '\n'.join((f'- {c}' for c in data['comments']))
    dt_data['command_li'] = '\n'.join((f'- {c}' for c in data['commands']))
    dt_data['stdout_li'] = '\n'.join((f'- {c}' for c in data['stdout_list']))
    dt_data['stderr_li'] = '\n'.join((f'- {c}' for c in data['stderr_list']))

    dt_msg = {
        "msgtype": 'markdown',
        "markdown": {
            'title': DINGTAIL_SUBJECT.format(**dt_data),
            'text': DINGTAIL_BODY.format(**dt_data)
        }
    }
    res = do_notify_by_ding_talk(config, dt_msg)
    APP.debug(f'钉钉推送结果:{res.json()}')
Esempio n. 9
0
def notify_by_email(data):
    mail_data = data.copy()
    #APP.debug(f'邮件 数据===> {subject} ; {body}')
    subject = MAIL_SUBJECT.format(**data)

    mail_data['comment_li'] = ''.join(
        (f'<li>{c}</li>' for c in data['comments']))
    mail_data['command_li'] = ''.join(
        (f'<li>{c}</li>' for c in data['commands']))
    mail_data['stdout_li'] = ''.join(
        (f'<li>{c}</li>' for c in data['stdout_list']))
    mail_data['stderr_li'] = ''.join(
        (f'<li>{c}</li>' for c in data['stderr_list']))

    body = MAIL_BODY.format(**mail_data)
    res = APP.send_email(subject, html_body=body)
    if res:
        APP.error(f'邮件推送失败:{res}')
    else:
        APP.debug(f'邮件发送成功,数据===> {subject}')
Esempio n. 10
0
def notifyByEmail(config, data):
    prefix = 'error_' if 'error' in data else ''
    subject = config[prefix + 'subject'].format(**data)
    body = config[prefix + 'body'].format(**data)
    APP.debug(f'邮件 数据===> {subject} ; {body}')
    #p('邮件===>', subject, '; ', body)
    if not CONFIG['dry']:
        res = APP.send_email(subject, body)
        if res:
            APP.error(f'邮件推送失败:{res}')
            #p('邮件推送失败:', res, force=True)
        else:
            APP.debug('邮件发送成功。')
Esempio n. 11
0
def notifyByDingTail(config, data):
    """发消息给钉钉机器人
    """
    token = config['token']
    if not token:
        APP.error('没有钉钉token')
        #p('APP.error: 没有钉钉token')
        return
    prefix = 'error_' if 'error' in data else ''
    data = {
        "msgtype": "text",
        "text": {
            "content":
            config['keyword'] + config[prefix + 'message'].format(**data)
        },
        "at": config['at']
    }
    APP.debug(f'钉钉机器人 数据===> {data}')
    #p('钉钉机器人===>', data)
    if not CONFIG['dry']:
        res = requests.post(url="https://oapi.dingtalk.com/robot/send?access_token={}".format(token), \
            headers = {'Content-Type': 'application/json'}, data=json.dumps(data))
        #p('钉钉推送结果:', res.json())
        APP.debug(f'钉钉推送结果:{res.json()}')
Esempio n. 12
0
def set_ip():
    domains = request_get('domain')
    token = request_get('token')
    #print(domains, token)

    if token != TOKEN:
        message = {'error': 'Unauthorized'}
        return jsonify(message), 404

    if not domains or type(domains) not in (str, list):
        message = {
            'error': 'Invalid request data',
            'example1': {
                'domain': 'sub.domain.com'
            },
            'example2': {
                'domain': ['sub1.domain.com', 'sub2.domain.com']
            }
        }
        return jsonify(message), 422

    ipv6 = get_real_ip()
    ipv4 = ipv6to4(ipv6)
    newIP = ipv4 if ipv4 else ipv6
    version = 4 if ipv4 else 6
    dnsType = 'AAAA' if version == 6 else 'A'

    if type(domains) == str:
        domains = [domains]

    results = []
    changedDomains = []
    for domain in domains:
        oldIp = getOldIP(version, domain)
        if newIP != oldIp or CONFIG['force']:
            APP.info(f'域名{domain}的IPv{version}已发生改变,上次地址为{oldIp}')
            result = refreshRecord(domain, newIP, version)
            results.append(result)
            changedDomains.append(domain)
        else:
            APP.debug(f'域名{domain}的{dnsType}纪录未发生改变')
            results.append({
                "status": {
                    "code": "1",
                    "message": "Action completed successful",
                    "created_at": now()
                },
                'record': {
                    "name": domain,
                    "value": newIP,
                    "status": "unchanged"
                }
            })
    if not CONFIG['dry']:
        saveIP(version, newIP, domains)
    if changedDomains:
        notify({
            'version': version,
            'dnsType': 'AAAA' if version == 6 else 'A',
            'ip': newIP,
            'domains': ','.join(changedDomains)
        })
    return jsonify(results)
Esempio n. 13
0
def refreshRecord(subDomainName, newIP, version):
    """更新记录IP,或添加新记录

    Args:
        subDomainName (str): 记录名(包含域名)
        newIP (str): IP
        version (int): 4,6

    Raises:
        RuntimeError: 错误
    """
    getDomains()
    recordType = 'AAAA' if version == 6 else 'A'
    example = {
        "status": {
            "code": "1",
            "message": "Action completed successful",
            "created_at": now()
        },
    }
    # TODO: 统一对dry判断
    if CONFIG['dry']:
        example["NOTICE"] = '目前在dry模式下运行'

    for domainId in DOMAIN_RECORD.keys():
        domain = DOMAIN_RECORD[domainId]
        if domain['name'] in subDomainName:
            getRecords(domainId)
            key = f'{subDomainName}:{recordType}'
            if key in domain['records']:
                record = domain['records'][key]
                if record['value'] == newIP:
                    APP.debug(f'{subDomainName}的IPv{version}地址与线上一致:{newIP}')
                    continue
                data = {
                    'domain_id': domainId,
                    'record_id': record['id'],
                    'sub_domain': record['name'],
                    'value': newIP,
                    'record_type': record['type'],
                    'record_line': '默认'
                }
                action = 'Record.Modify'
                example["record"] = {
                    "id": 16894439,
                    "name": domain['name'],
                    "value": newIP,
                    "status": "enable"
                }
            else:
                data = {
                    'domain_id':
                    domainId,
                    'sub_domain':
                    '@' if subDomainName == domain['name'] else
                    subDomainName.replace('.' + domain['name'], ''),
                    'value':
                    newIP,
                    'record_type':
                    recordType,
                    'record_line':
                    '默认'
                }
                action = 'Record.Create'
                example["record"] = {
                    "id": "16894439",
                    "name": domain['name'],
                    "status": "enable"
                }
            APP.debug(data)
            if not CONFIG['dry']:
                result = requestDnsApi(action, data)
                APP.debug(result)
                return result
            APP.debug(example)
    # Clear cache
    DOMAIN_RECORD.clear()
    return example
Esempio n. 14
0
def run(version):
    assert (version == 4 or version == 6)
    dnsType = 'AAAA' if version == 6 else 'A'
    domains = CONFIG[f'ipv{version}']
    if not domains:
        APP.debug(f'未配置IPv{version},不需要更新。')
        return
    try:
        APP.debug('*' * 40 + f'IPv{version}' + '*' * 40)
        newIP = getIp(version)
        APP.debug(f'解析IPv{version}结果为:{newIP}')
        changedDomains = []
        for subDomain in domains:
            oldIp = getOldIP(version, subDomain)
            if newIP != oldIp or CONFIG['force']:
                APP.info(f'域名{subDomain}的IPv{version}已发生改变,上次地址为{oldIp}')

                result = refreshRecord(subDomain, newIP, version)
                status = result['status']
                if status['code'] != '1':
                    raise RuntimeError('{}-{}'.format(status['code'],
                                                      status['message']))

                APP.info(f'域名{subDomain}的{dnsType}纪录已经更新为{newIP}')
                changedDomains.append(subDomain)
            else:
                APP.debug(f'域名{subDomain}的{dnsType}纪录未发生改变')
        if not CONFIG['dry']:
            saveIP(version, newIP, domains)
        if changedDomains:
            notify({
                'version': version,
                'dnsType': dnsType,
                'ip': newIP,
                'domains': ','.join(changedDomains)
            })
    except Exception as ex:
        APP.error(f'!!!运行失败,原因:{ex}')
        notify({
            'version': version,
            'dnsType': dnsType,
            'domains': ','.join(domains),
            'error': ex
        })

    if CONFIG['dry']:
        print('\n\n' + '!' * 20 + f'-这是在dry模式下运行,实际IPv{version}域名未作更新-' +
              '!' * 20)