用linux c实现高效dns解析 (linux c dns)

用Linux C实现高效DNS解析

创新互联建站是一家专注于成都网站设计、成都网站建设、外贸网站建设与策划设计,博山网站建设哪家好?创新互联建站做网站,专注于网站建设十年,网设计领域的专业建站公司;建站业务涵盖:博山等地区。博山做网站价格咨询:18980820575

DNS(Domn Name System)是指将域名转换成IP地址,以便让计算机之间相互访问的技术。查找域名对应的IP地址是网络通信的基础,因此DNS解析的速度和效率对于计算机的运行和网络的流畅度有着至关重要的影响。本文将介绍如何使用Linux C实现高效的DNS解析。

一、DNS解析的基本原理

DNS解析的基本原理是将域名解析成一个可用的IP地址,使计算机之间可以通过IP地址访问对方。例如,当我们在浏览器中输入www.google.com时,浏览器会向DNS服务器发出请求,询问域名www.google.com对应的IP地址是多少。DNS服务器会返回一个IP地址(例如172.217.25.68),浏览器通过该IP地址就可以连接到Google网站。

DNS服务器通常由ISP或企业或组织自己维护,可以是一台或多台。DNS服务器通常存储了所有域名和对应的IP地址,因此DNS解析是一个非常重要和繁琐的工作。

二、Linux系统中常用的DNS解析方法

在Linux系统中,常用的DNS解析方法有以下几种:

1. gethostbyname()函数

gethostbyname()函数是C语言中用于DNS解析的函数,使用较为简单,但效率不高。该函数的使用方式如下:

“`c

#include

struct hostent *gethostbyname(const char *name);

“`

在使用该函数时,需要注意该函数返回的结构体中并不包含TTL信息,因此无法判断该解析结果是否过期。此外,由于gethostbyname()函数会阻塞进程,因此需要单独开辟线程进行DNS解析。

2. 解析配置文件/etc/hosts

在Linux系统中,/etc/hosts文件是用于本地DNS解析的文件,存储了一些常用的域名和对应的IP地址,例如:

“`

127.0.0.1 localhost

127.0.0.1 mydomn

“`

在访问mydomn时,系统会首先去/etc/hosts文件中查找mydomn对应的IP地址,如果找到则直接使用该IP地址进行访问。由于/etc/hosts文件是本地存储的,因此不涉及网络通信,可以提升一定的解析速度。

3. 使用DNS缓存

由于DNS解析过程存在重复性,因此在Linux系统中可以使用DNS缓存提升解析速度。DNS缓存通常是指本地主机上对查询到的IP地址进行缓存,以便下次查询时可以直接读取缓存中的IP地址,从而避免了网络通信的开销。Linux系统中可以使用dnasq等DNS软件来实现DNS缓存。

三、使用Linux C实现高效DNS解析

由于gethostbyname()函数的效率较低,因此本文将介绍如何使用Linux C实现一种高效的DNS解析方法。该方法的主要思路是通过获取DNS服务器的IP地址,手动向DNS服务器发起查询,从而提升解析效率。下面是具体的实现步骤:

1. 获取DNS服务器的IP地址

在Linux系统中,DNS服务器的IP地址通常存储在/etc/resolv.conf文件中,该文件中包含了域名解析的配置信息。可以使用以下代码来获取DNS服务器的IP地址:

“`c

#include

#include

int get_dns_server(char *buf, int len) {

FILE *f = fopen(“/etc/resolv.conf”, “r”);

if (!f) {

return -1;

}

char line[1024];

while (fgets(line, sizeof(line), f)) {

if (line[0] == ‘#’ || line[0] == ‘\n’) {

continue;

}

if (!strncmp(line, “nameserver”, 10)) {

char *p = strtok(line + 10, ” \t\n”);

if (!p) {

continue;

}

struct in_addr addr;

addr.s_addr = inet_addr(p);

if (addr.s_addr == INADDR_NONE) {

continue;

}

strncpy(buf, p, len – 1);

buf[len – 1] = ‘\0’;

break;

}

}

fclose(f);

if (buf[0] == ‘\0’) {

return -1;

}

return 0;

}

“`

上述代码中,get_dns_server()函数通过打开/etc/resolv.conf文件来查找DNS服务器的IP地址。由于/etc/resolv.conf文件中可能存在多个DNS服务器,因此该函数仅返回查找到的之一个DNS服务器的IP地址。

2. 向DNS服务器发起查询

获取DNS服务器的IP地址之后,需要向该DNS服务器发起DNS查询请求。DNS查询请求是一种不同于TCP和UDP的协议,需要使用RAW Socket进行开发。

RAW Socket是指可以自定义协议头的Socket,在Linux系统中可以创建自定义协议的Socket,从而实现DNS查询请求。以下是创建RAW Socket的代码示例:

“`c

#include

#include

#include

#include

int create_raw_socket(void) {

int fd = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);

if (fd == -1) {

return -1;

}

int opt = 1;

if (setsockopt(fd, IPPROTO_IP, IP_HDRINCL, &opt, sizeof(opt)) == -1) {

close(fd);

return -1;

}

return fd;

}

“`

上述代码中,create_raw_socket()函数使用Socket协议创建了一个UDP Socket,并通过setsockopt()函数设置了IP_HDRINCL选项,该选项指示内核使用应用程序提供的IP包头,从而实现自定义的协议头。

调用create_raw_socket()函数之后,就可以使用sendto()函数向DNS服务器发起查询请求,然后使用recvfrom()函数接收DNS服务器返回的IP地址信息,从而完成DNS解析过程了。完整的高效DNS解析代码如下:

“`c

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#define DNS_SERVER_PORT 53

#define DNS_SERVER_ADDR “8.8.8.8”

#define DNS_CLIENT_PORT 5353

#define DNS_CLIENT_ADDR “0.0.0.0”

struct dns_header {

unsigned short id;

unsigned short flags;

unsigned short qdcount; // Question Count

unsigned short ancount; // Answer Count

unsigned short nscount; // Authority Count

unsigned short arcount; // Additional Count

};

struct dns_query {

unsigned short qtype;

unsigned short qclass;

};

int create_raw_socket(void);

int get_dns_server(char *buf, int len);

int send_dns_query(int fd, const char *domn, const char *server);

int resolve_dns_response(const char *buf, size_t buflen);

int mn(int argc, char **argv) {

if (argc

printf(“Usage: %s domn_name\n”, argv[0]);

exit(0);

}

int sock = create_raw_socket();

if (sock == -1) {

perror(“create_raw_socket”);

exit(1);

}

char dns_server[16];

if (get_dns_server(dns_server, sizeof(dns_server)) == -1) {

strncpy(dns_server, DNS_SERVER_ADDR, sizeof(dns_server) – 1);

}

if (send_dns_query(sock, argv[1], dns_server) == -1) {

perror(“send_dns_query”);

close(sock);

exit(1);

}

char buf[1024];

ssize_t n = recvfrom(sock, buf, sizeof(buf), 0, NULL, NULL);

if (n == -1) {

perror(“recvfrom”);

close(sock);

exit(1);

}

int ret = resolve_dns_response(buf, n);

close(sock);

return ret;

}

int create_raw_socket(void) {

int fd = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);

if (fd == -1) {

return -1;

}

int opt = 1;

if (setsockopt(fd, IPPROTO_IP, IP_HDRINCL, &opt, sizeof(opt)) == -1) {

close(fd);

return -1;

}

struct sockaddr_in addr;

addr.sin_family = AF_INET;

addr.sin_port = htons(DNS_CLIENT_PORT);

addr.sin_addr.s_addr = inet_addr(DNS_CLIENT_ADDR);

if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {

close(fd);

return -1;

}

return fd;

}

int get_dns_server(char *buf, int len) {

FILE *f = fopen(“/etc/resolv.conf”, “r”);

if (!f) {

return -1;

}

char line[1024];

while (fgets(line, sizeof(line), f)) {

if (line[0] == ‘#’ || line[0] == ‘\n’) {

continue;

}

if (!strncmp(line, “nameserver”, 10)) {

char *p = strtok(line + 10, ” \t\n”);

if (!p) {

continue;

}

struct in_addr addr;

addr.s_addr = inet_addr(p);

if (addr.s_addr == INADDR_NONE) {

continue;

}

strncpy(buf, p, len – 1);

buf[len – 1] = ‘\0’;

break;

}

}

fclose(f);

if (buf[0] == ‘\0’) {

return -1;

}

return 0;

}

int send_dns_query(int fd, const char *domn, const char *server) {

size_t len = strlen(domn);

char *buf = (char *)malloc(sizeof(struct dns_header) + len + 2 + sizeof(struct dns_query));

memset(buf, 0, sizeof(struct dns_header) + len + 2 + sizeof(struct dns_query));

struct dns_header *hdr = (struct dns_header *)buf;

hdr->id = htons(rand() % 65536);

hdr->flags = htons(0x0100); // Standard query

hdr->qdcount = htons(1);

char *pos = buf + sizeof(struct dns_header);

memcpy(pos, domn, len + 1);

for (char *p = pos; *p; ++p) {

if (*p == ‘.’) {

*p = p – pos;

} else {

++*p;

}

}

struct dns_query *qry = (struct dns_query *)(pos + len + 1);

qry->qtype = htons(1); // A record

qry->qclass = htons(1); // IN (Internet)

struct sockaddr_in addr;

addr.sin_family = AF_INET;

addr.sin_port = htons(DNS_SERVER_PORT);

addr.sin_addr.s_addr = inet_addr(server);

ssize_t n = sendto(fd, buf, sizeof(struct dns_header) + len + 2 + sizeof(struct dns_query), 0, (struct sockaddr *)&addr, sizeof(addr));

free(buf);

return (n == -1) ? -1 : 0;

}

int resolve_dns_response(const char *buf, size_t buflen) {

struct dns_header *hdr = (struct dns_header *)buf;

size_t len = ntohs(hdr->qdcount);

if (len == 0) {

printf(“Invalid DNS response: %d\n”, len);

return -1;

}

char *pos = (char *)(buf + sizeof(struct dns_header));

for (size_t i = 0; i

pos += strlen(pos) + 1 + sizeof(struct dns_query);

}

if (pos >= buf + buflen) {

printf(“Invalid DNS response: %p – %p\n”, pos, buf + buflen);

return -1;

}

printf(“%d.%d.%d.%d\n”, pos[0], pos[1], pos[2], pos[3]);

return 0;

}

“`

四、注意事项

使用该方法进行DNS解析时,需要注意以下几点:

1. DNS服务器的IP地址可能会变动,因此应该定期更新/etc/resolv.conf文件来及时更新DNS服务器的IP地址。

2. DNS查询请求需要使用RAW Socket,因此需要通过root权限运行程序。

3. DNS服务器返回的IP地址可能会存在多种情况,例如多个IP地址一起返回(例如cname记录),因此需要对DNS服务器返回的信息进行解析。

相关问题拓展阅读:

  • linux下如何配置DNS服务器,
  • 写一个C语言程序: 能修改Linux 主机的IP、DHCP、DNS 等设置

linux下如何配置DNS服务器,

答案给出的很详细,可是那是老版本redhat9的配置了。 现在通用的bind9.0使用的是chroot环境,就是说你安装RHEL5.4的时候,在/etc/下面是找不到named.conf文件的.简要说下过程 如果你用的RHEL5的yum库1.安装#yum install bind* -y#yum install caching-nameserver2.配置主配文件#cd /var/named/chroot/etc/ #cp -a named.caching-nameserver.conf named.conf# vim named.conf主游雀要参饥磨滚数和以前的bind差不多,都烂余是写明正向和方向域文件。allow-query 这行要匹配any 否则服务器就只能你本地使用

linux DNS服务器配置

基本理论:

DNS系统的作用是把域名和IP对应起来。

正向解析:根据域名(主机名)查找对应的IP地址。

反向解析:根据IP地址查询对应的域名(主机名)。

查询

递归查询:大多数客户机向芦前DNS服务器解析域名的方式。

迭代查询:大多数DNS服务器向其它DNS服务器解析域名的方式。

DNS服务器的类型

缓存域名服务器:也称唯高速缓存服务器。通过向其它域名服务器查询获得域名与IP地址的对应孙伏记录,将域名查询结果缓存到本地,提高重复查询时的速度。

主域名服务器:特定DNS区域的官方服务器陪凯清,具有唯一性。负责维护该区域内的所有域名与IP的映射记录。

从域名服务器:也称辅助域名服务器。其维护的域名与IP地址的映射记录来源于主域名服务器。

环境准备:

临时关闭selinux和iptables

#setenforce 0

#service iptables stop

查询相关软件包:

# yum search bind

Loaded plugins: product-id, refresh-packagekit, subscription-manager

Updating Red Hat repositories.

====================================================================================== N/S Matched: bind ======================================================================================

PackageKit-device-rebind.i686 : Device rebind functionality for PackageKit

bind.i686 : The Berkeley Internet Name Domain (BIND) DNS (Domain Name System) server

bind-chroot.i686 : A chroot runtime environment for the ISC BIND DNS server, named(8)

bind-utils.i686 : Utilities for querying DNS name servers

其中各软件包的作用如下:

bind: 提供域名服务的主要程序及相关文件。

bind-chroot:为bind提供一个伪装的根目录以增强安全性。

bind-utils:提供对DNS服务器测试的工具程序(如nslookup、dig等)。

安装BIND软件包#yum install *bind*

配置DNS服务器:

bind服务器端程序

主要执行程序:/usr/in/named

服务脚本:、etc/init.d/named

默认监听端口:53

主配置文件: /etc/named.conf

保存DNS解析记录的数据文件: /var/named/chroot/var/named

查询bind程序的配置文件列表

# rpm -qc bind

/etc/logrotate.d/named

/etc/named.conf

/etc/named.iscdlv.key

/etc/named.rfc1912.zones

/etc/named.root.key

/etc/rndc.conf

/etc/rndc.key

/etc/sysconfig/named

/var/named/named.ca

/var/named/named.empty

/var/named/named.localhost

/var/named/named.loopback

查看主配置文件named.conf

#vim /etc/named.conf

主配置文件解析:

全局配置部分:

默认的全局配置项如下:

10 options {

listen-on port 53 { 127.0.0.1; }; //监听的端口和接口IP地址

listen-on-v6 port 53 { ::1; };

directory”/var/named”;//dns区域的数据文件默认存放位置

dump-file”/var/named/data/cache_dump.db”;

statistics-file “/var/named/data/named_stats.txt”;

memstatistics-file “/var/named/data/named_mem_stats.txt”;

allow-query { localhost; }; //允许dns查询的客户机列表,any表示所有

recursion yes;//是否允许客户机进行递归查询

19

dnssec-enable yes;

dnssec-validation yes;

dnssec-lookaside auto;

23

/* Path to ISC DLV key */

bindkeys-file “/etc/named.iscdlv.key”;

26 };

全局配置中还有如下选项:

forwarders {202.102.24.68;12.3.3.3;};//将本域名服务器不能解析的条目转发给其它DNS服务器的IP地址

默认的区域配置项如下:

35 zone “.” IN {

type hint; //区域类型。hint为根区域;master为主区域; slave为辅助区域

file “named.ca”;//该区域对应的区域数据配置文件名

38 };

区域配置中还有如下选项:

allow-transfer {189.98.90.23;};//允许下载区域数据库的从域名服务器IP地址

allow-update {none;};//允许动态更新的客户端IP地址(none表示全部禁止)

添加如下区域配置:

zone “my.com” IN {

type master;//主区域

file “my.com”; //该区域对应的区域数据配置文件名

allow-transfer {192.168.153.1;};//允许下载区域数据库的从域名服务器IP地址

allow-update {none;};

};

zone “153.168.192.in-addr.arpa” IN { //表示针对IP192.168.153.130反向解析

type master;//主区域

file “192.168.153.my.arpa”;//该区域对应的区域数据配置文件名

};

配置完了,可以执行如下命令对named.conf文件进行语法检查。

#named-checkconf

注意:倒序网络地址.in-addr.arpa 表示反向区域

主配置文件最后还有一行是:

include “/etc/named.rfc1912.zones”//该文件包含/etc/named.rfc1912.zones文件

区域数据配置文件:

先看一下named.localhost的内容:

$TTL 1D//time to live 生存时间

@IN SOA @ rname.invalid. ( //”rname.invalid”DNS区域地址

; serial //更新序列号

D ; refresh //更新时间

H ; retry //重试延时

W ; expire //失效时间

H ) ; minimum//无效地址解析记录的默认缓存时间

NS @//name server 域名服务记录

A.0.0. //address 只用在正向解析的区域数据文件中

AAAA ::1

新建2个对应的区域数据配置文件:

#touch my.com

#touch 192.168.153.my.arpa

#vim my.com

$TTL 86400

@ IN SOA my.com. admin.my.com (//admin.myNaN为该区域管理员的邮箱地址

201

H

M

W

D

)

@IN NS ns1.my.com.//当前域的DNS服务器地址

IN MXmail.my.com.//用于设置当前域的邮件服务器域名地址,数字10表示优先级别,数字越大优先级越低

ns1 IN A.168.153.130

mail IN A.168.153.130

www IN A.168.153.130

ftp IN CNAME www //CNAME别名(canonical name)记录,表示ftp.my.com和

www.my.com

对应同一个IP.

# vim 192.168.153.my.arpa

$TTL 86400

@ IN SOA my.com. admin.my.com (

201

H

M

W

D

)

@IN NS ns1.my.com.

INPTR ftp.my.com

启动DNS服务

# service named start

测试:

配置一台ftp服务器用于测试:

#service vsftpd start//启动vsftpd服务

当前网卡的配置:

eth0: 192.168.0.1/24

eth1: 192.168.153.130/24

# nslookup 192.168.153.130

Server: 127.0.0.1

Address: 127.0.0.1#53

130.153.168.192.in-addr.arpaname =

www.my.com

.

# nslookup ftp.my.com

Server: 127.0.0.1

Address: 127.0.0.1#53

ftp.my.comcanonical name =

www.my.com

.

Name:

www.my.com

Address: 192.168.153.130

测试成功

在linux下配置DNS服务器,下面是配置过程中设置过的一些文件,

/etc/hosts 文件的具体内容如下:

# Do not remove the following line, or various programs

# that require network functionality will fail.

127.0.0.1 localhost.localdomain localhost fc4

192.168.1.3 a.test.com a

192.168.1.1 b.test.cn b

/etc/host.conf 文件:

order hosts,bind

表示先用hosts文件做解析,春改在用DNS解析

/etc/resolv.conf 文件:

; generated by NetworkManager, do not edit!

search test.com

nameserver 127.0.0.1

search test.cn

扒谈判 nameserver 192.168.1.1

nameserver 61.144.56.100

/etc/named.conf 文侍氏件:

//

// named.conf for Red Hat caching-nameserver

//

options {

directory “/var/named”;

dump-file “/var/named/data/cache_dump.db”;

statistics-file “/var/named/data/named_stats.txt”;

/*

* If there is a firewall between you and nameservers you want

* to talk to, you might need to uncomment the query-source

* directive below. Previous versions of BIND always asked

* questions using port 53, but BIND 8.1 uses an unprivileged

* port by default.

*/

// query-source address * port 53;

};

//

// a caching only nameserver config

//

controls {

inet 127.0.0.1 allow { localhost; } keys { rndckey; };

};

zone “.” IN {

type hint;

file “named.ca”;

};

zone “test.com”IN {

type master;

file “test.com”;

allow-update { none; };

};

zone “1.168.192.in-addr.arpa”IN {

type master;

file “192.168.1.rev”;

allow-update { none; };

};

zone “test.cn”IN {

type master;

file “test.cn”;

allow-update { none; };

};

zone “0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa” IN {

type master;

file “named.ip6.local”;

allow-update { none; };

};

zone “255.in-addr.arpa” IN {

type master;

file “named.broadcast”;

allow-update { none; };

};

zone “0.in-addr.arpa” IN {

type master;

file “named.zero”;

allow-update { none; };

};

include “/etc/rndc.key”;

在/var/name/test.com 文件下:

$TTL

@ IN SOA a.test.com. root.a.test.com (

42 ; serial (d. adams)

3H ; refresh

15M ; retry

1W ; expiry

1D ) ; minimum

IN NS a.test.com.

IN MX 10 mail.test.com.

a IN A 192.168.1.3

mail IN A 192.168.1.3

//其中root.a.test.com的含义是管理员的邮箱

/var/name/test.cn 文件下:

$TTL

@ IN SOA b.test.cn. root.a.test.com (

42 ; serial (d. adams)

3H ; refresh

15M ; retry

1W ; expiry

1D ) ; minimum

IN NS b.test.cn.

IN MX 10 mail.test.cn.

b IN A 192.168.1.1

mail IN A 192.168.1.1

/var/name/192.168.1.rev 文件下:

$TTL

@ IN SOA 1.168.192.in-addr.arpa. root.test.com. (

; Serial

; Refresh

; Retry

; Expire

) ; Minimum

IN NS a.test.com.

IN NS b.test.cn.

IN MX 10 mail.test.com.

IN MX 10 mail.test.cn.

3 IN PTR a.test.com.

3 IN PTR mail.test.com.

1 IN PTR b.test.cn.

1 IN PTR mail.test.cn.

然后用/etc/init.d/named restart重启DNS服务,在重启过程中,我曾经出现过好几次的错误,按照出错的提示,会提示是named.conf文件第几行出错的。或者提示在那些包含文件例如test.cn这些文件里面的问题,然后一个一个排除。

最后还有一些nslookup的命令比较有用:

set all用于显示使用nslookup工具这台机器上的DNS服务器的一些信息

set type=any会显示完整信息包括域中邮件服务器和主从DNS服务器的名字和IP地址

server 192.168.0.1更换查询的DNS服务器地址

写一个C语言程序: 能修改Linux 主机的IP、DHCP、DNS 等设置

5月8日

21:10

dns所要的包:

bind-*

bind-devel-*

bind-utils-*

caching-nameserver-*

redhat-config-bind-*

具体在那张盘我就记不太清楚了,你可枝侍侍以放进去试,反正总共才3张盘。

关于linux安装程序,比windows要猛吵复杂一点,一般的就是执行一个configure脚本,但是不是每个软件都是这么安装,你可以看一下包内的readme.txt,或

readme.txt文件,你们一定有详细的添谈毕加删除应用软件的步骤。

可以调用system()函圆中数。把linux系统命令拼成字符串,然后调用system去执行这个字符串就可以了。修改系统配置,需要root身份。

1、system函数:

原型:int

system(const

char

*

command);

功能:执行

dos(windows系统)

shell(Linux/Unix系统)

命令,参数字符串command为命令名;

说明:在windows系统中,system函数直接在控制台调用一个command命令。在Linux/Unix系统中,system函孙扮数会调用fork函数产生子进程,由子进程来执行command命令,命令橘凯山执行完后随即返回原调用的进程;

头文件:stdlib.h;

返回值:命令执行成功返回0,执行失败返回-1。

2、例程:

#include

#include

int main(){

system(“del C:\\123.txt”);//在控制台中,执行命令del C:\\123.txt,删除C盘目录下的123.txt文件

return 0;

}

如果是在LINUX下面用的话,写一个交互友好一点的SHELL脚本是罩念代价最小的,要想用C语言实现代价实在太高,光ifconfig/route的源码分析就很多了,还不说DHCP、DNS和用户管理什么的了。

其实以上这些需求直接都孙好有物凯困现成的指令或系统调用,用Shell加工一下就OK了。

关于linux c dns的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站。

成都网站设计制作选创新互联,专业网站建设公司。
成都创新互联10余年专注成都高端网站建设定制开发服务,为客户提供专业的成都网站制作,成都网页设计,成都网站设计服务;成都创新互联服务内容包含成都网站建设,小程序开发,营销网站建设,网站改版,服务器托管租用等互联网服务。

新闻标题:用linux c实现高效dns解析 (linux c dns)
新闻来源:http://www.shufengxianlan.com/qtweb/news34/529984.html

网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联