您的位置:首页 > 运维架构 > 网站架构

Bind-9.6.0-P1源代码分析之一:整体架构(初稿)

2016-04-08 16:57 956 查看
转载至:不知转载源

一、 说明

参考http://202.120.16.5/lxr/http/source/bin/named/main.c

这是bind解析程序的入口

事件bind程序也事件驱动型,以任务作为主要的执行。

当一个解析请求到来时,就会通过事件的产生来触发任务dispatch处理。这样的处理有相应

if (event->ev_action != NULL) {

861 UNLOCK(&task->lock);

862 (event->ev_action)(task,event);

863 LOCK(&task->lock);

这里 action就是执行函数

本文主要关注整体的运行结构,主要参考文件是main.c

二、 启动入口

870 int

871 main(int
argc, char *argv[])
{ 命令行参数传入

以上为设置错误消息

895 isc_assertion_setcallback(assertion_failed);

896 isc_error_setfatal(library_fatal_error);

897 isc_error_setunexpected(library_unexpected_error);

898初始化系统日志,权限等

899 ns_os_init(program_name);

900初始化工作

901 dns_result_register();

902 dst_result_register();

903 isccc_result_register();

904 命令行参数分析,如-g将日志输出到front-end

905 parse_command_line(argc,
argv);

{注意以下的内存机制,isc_mem可见http://202.120.16.5/lxr/http/source/lib/isc/mem.c#L113定义

注意以

http://202.120.16.5/lxr/http/source/lib/isc/mem.c#L725

用到了锁机制

830 LOCK(&lock);

831 ISC_LIST_INITANDAPPEND(contexts,
ctx,
link);

832 UNLOCK(&lock);

833

内存生成采用标准的系统调用 malloc和free,但考虑到多线程下的竞争情况,对内存块访问需要锁机制。Ns_g_mctx是信号量,要求ns_g_mctx!=NULL&&*ns_g_mctx=NULL

}

919 result =
isc_mem_create(0, 0, &ns_g_mctx);

920 if (result !=
ISC_R_SUCCESS)

921 ns_main_earlyfatal("isc_mem_create()
failed: %s",

922 isc_result_totext(result));

对设置

923 isc_mem_setname(ns_g_mctx,
"main", NULL);

924 //setup是重要的一部分,见分析三

925 setup();

接下来就是解析主体程序了,是通过循环来做的,直到错误或者接收到退出信号。递

/*

928 * Start things running and then wait for a shutdown request

929 * or reload.

930 */

931 do {

932 result =
isc_app_run();是重要部分,见分析四

933

934 if (result ==
ISC_R_RELOAD) { //通过我们会做这种操作,当配置了一个新的zone文件时

935 ns_server_reloadwanted(ns_g_server);

936 } else if (result !=
ISC_R_SUCCESS) {

937 UNEXPECTED_ERROR(__FILE__,
__LINE__,

938 "isc_app_run(): %s",

939 isc_result_totext(result));

940 /*

941 * Force exit.

942 */

943 result =
ISC_R_SUCCESS;

944 }

945 } while (result !=
ISC_R_SUCCESS);

946

947 #ifdef HAVE_LIBSCF

948 if (ns_smf_want_disable == 1) {

949 result =

ns_smf_get_instance(&instance, 1, ns_g_mctx);

950 if (result ==
ISC_R_SUCCESS && instance != NULL) {

951 if (smf_disable_instance(instance, 0) != 0)

952 UNEXPECTED_ERROR(__FILE__,
__LINE__,

953 "smf_disable_instance() "

954 "failed for %s : %s",

955 instance,

956 scf_strerror(scf_error()));

957 }

958 if (instance != NULL)

959 isc_mem_free(ns_g_mctx,
instance);

960 }

961 #endif
/* HAVE_LIBSCF */

962

963 cleanup();

964

965 if (want_stats)
{

966 isc_mem_stats(ns_g_mctx,
stdout);

967 isc_mutex_stats(stdout);

968 }

969

970 if (ns_g_memstatistics &&
memstats != NULL) {

971 FILE *fp =
NULL;

972 result =
isc_stdio_open(memstats,
"w", &fp);

973 if (result ==
ISC_R_SUCCESS) {

974 isc_mem_stats(ns_g_mctx,
fp);

975 isc_mutex_stats(fp);

976 isc_stdio_close(fp);

977 }

978 }

979 isc_mem_destroy(&ns_g_mctx);

980 isc_mem_checkdestroyed(stderr);

981

982 ns_main_setmemstats(NULL);

983

984 isc_app_finish();

985

986 ns_os_closedevnull();

987

988 ns_os_shutdown();

989

990 return (0);

991 }

三、 初始设置setup

595 ns_os_tzset(); 初始化时区

596

597 ns_os_opendevnull(); 打开空设备以便消息转向

599 #ifdef HAVE_LIBSCF 是否solaris的smf管理机制

600 /* Check if named is under smf control, before chroot. */

601 result =

ns_smf_get_instance(&instance, 0, ns_g_mctx);

602 /* We don't care about instance, just check if we got one. */

603 if (result ==
ISC_R_SUCCESS)

604 ns_smf_got_instance = 1;

605 else

606 ns_smf_got_instance = 0;

607 if (instance != NULL)

608 isc_mem_free(ns_g_mctx,
instance);

609 #endif
/* HAVE_LIBSCF */

如果是chroot运行方式化,初始化可用资源。Chroot是以普通用户运行超级资源,是unix系统的一种机制

611 #ifdef
PATH_RANDOMDEV

612 /*

613 * Initialize system's random device as fallback entropy source

614 * if running chroot'ed.

615 */

616 if (ns_g_chrootdir != NULL) {

617 result =

isc_entropy_create(ns_g_mctx, &ns_g_fallbackentropy);

618 if (result !=
ISC_R_SUCCESS)

619 ns_main_earlyfatal("isc_entropy_create()
failed: %s",

620 isc_result_totext(result));

621

622 result =

isc_entropy_createfilesource(ns_g_fallbackentropy,

623 PATH_RANDOMDEV);

624 if (result !=
ISC_R_SUCCESS) {

625 ns_main_earlywarning("could
not open pre-chroot "

626 "entropy source %s: %s",

627 PATH_RANDOMDEV,

628 isc_result_totext(result));

629 isc_entropy_detach(&ns_g_fallbackentropy);

630 }

631 }

632 #endif

看cpu数目

634 #ifdef

ISC_PLATFORM_USETHREADS

635 /*

636 * Check for the number of cpu's before ns_os_chroot().

637 */

638 ns_g_cpus_detected =
isc_os_ncpus();

639 #endif

以下说明了最小权限运行机制,读config文件需要有root机制,通过unix的sid置位实现,即setsid()

/*

644 * For operating systems which have a capability mechanism, now

645 * is the time to switch to minimal privs and change our user id.

646 * On traditional UNIX systems, this call will be a no-op, and we

647 * will change the user ID after reading the config file the first

648 * time. (We need to read the config file to know which possibly

649 * privileged ports to bind() to.)

650 */

651 ns_os_minprivs();

652 日志初始化

653 result =
ns_log_init(ISC_TF(ns_g_username
!= NULL));

654 if (result !=
ISC_R_SUCCESS)

655 ns_main_earlyfatal("ns_log_init()
failed: %s",

656 isc_result_totext(result));

pid = fork();产生新的子进程,如果是直接named而不是named –g的话,因此,需要派生子进程,并关闭父的一些输入输出句柄,参见fork例程

if (!ns_g_foreground)

666 ns_os_daemonize();

/*

669 * We call isc_app_start() here as some versions of FreeBSD's fork()

670 * destroys all the signal handling it sets up.

671 */

Isc_app_start主要是处理fork的信号机制的恢复

672 result =
isc_app_start();

673 if (result !=
ISC_R_SUCCESS)

674 ns_main_earlyfatal("isc_app_start()
failed: %s",

675 isc_result_totext(result));

676 写日志文件,开始启动

677 isc_log_write(ns_g_lctx,

NS_LOGCATEGORY_GENERAL,
NS_LOGMODULE_MAIN,

678 ISC_LOG_NOTICE,
"starting BIND %s%s", ns_g_version,

679 saved_command_line);

680

681 isc_log_write(ns_g_lctx,

NS_LOGCATEGORY_GENERAL,
NS_LOGMODULE_MAIN,

682 ISC_LOG_NOTICE,
"built with %s", ns_g_configargs);

683

接下来是资源限制部分,可用#ulimit –a来理解

/*

729 * Record the server's startup time.

730 */

731 result =
isc_time_now(&ns_g_boottime);

732 if (result !=
ISC_R_SUCCESS)

733 ns_main_earlyfatal("isc_time_now()
failed: %s",

734 isc_result_totext(result));

735

生成管理器,如任务、超时、entropy、hash,socket等管理器

736 result =
create_managers();

737 if (result !=
ISC_R_SUCCESS)

738 ns_main_earlyfatal("create_managers()
failed: %s",

739 isc_result_totext(result));

740

741 ns_builtin_init();

742

743 /*

744 * Add calls to register sdb drivers here.

745 */

746 /* xxdb_init(); */

747

748 #ifdef DLZ

749 /*

750 * Registyer any DLZ drivers.

751 */

752 result =
dlz_drivers_init();

753 if (result !=
ISC_R_SUCCESS)

754 ns_main_earlyfatal("dlz_drivers_init()
failed: %s",

755 isc_result_totext(result));

756 #endif

757

生成服务,重要部分,见分析六

758 ns_server_create(ns_g_mctx,
&ns_g_server);

759 }

760

四、 运行主体程序

文件http://202.120.16.5/lxr/http/source/lib/isc/unix/app.c#L431

430 isc_result_t

431 isc_app_run(void)
{

432 int
result;

433 isc_event_t *event,
*next_event;

434 isc_task_t *task;

435 #ifdef

ISC_PLATFORM_USETHREADS

436 sigset_t sset;

437 char strbuf[ISC_STRERRORSIZE];

438 #ifdef
HAVE_SIGWAIT

439 int sig;

440 #endif

441 #endif
/* ISC_PLATFORM_USETHREADS */

442

443 #ifdef

HAVE_LINUXTHREADS 如果启用了多线程编译,目前我们编译的是单线程,可用ps –em|grep named查看线程数

444 REQUIRE(main_thread ==
pthread_self());

445 #endif

446 加锁,事件队列是关键区,race condition

447 LOCK(&lock);

448

449 if (!running)
{

450 running =
ISC_TRUE;

451 事件结构有sender,type,action,arguments,而采用队列ev_link实现,事件FIFO依序处理。

452 /*

453 * Post any on-run events (in FIFO order).

454 */主循环

455 for (event =
ISC_LIST_HEAD(on_run);

456 event != NULL;

457 event = next_event) {

458 next_event =
ISC_LIST_NEXT(event, ev_link);

459 ISC_LIST_UNLINK(on_run,
event, ev_link);

460 task =
event->ev_sender;

461 event->ev_sender = NULL;

事件任务机制,见分析五

462 isc_task_sendanddetach(&task,
&event);

463 }

464

465 }

466

467 UNLOCK(&lock);

468 以下主要为信号捕获处理机制,特别注意shutdown退出时的处理。信号catch处理机制能够使应用更安全退出。

469 #ifndef
HAVE_SIGWAIT

470 /*

471 * Catch SIGHUP.

472 *

473 * We do this here to ensure that the signal handler is installed

474 * (i.e. that it wasn't a "one-shot" handler).

475 */

476 result =
handle_signal(SIGHUP,
reload_action);

477 if (result !=
ISC_R_SUCCESS)

478 return (ISC_R_SUCCESS);

479 #endif

480

481 #ifdef

ISC_PLATFORM_USETHREADS

482 /*

483 * There is no danger if isc_app_shutdown() is called before we wait

484 * for signals. Signals are blocked, so any such signal will simply

485 * be made pending and we will get it when we call sigwait().

486 */

487

488 while (!want_shutdown)
{

489 #ifdef
HAVE_SIGWAIT

490 /*

491 * Wait for SIGHUP, SIGINT, or SIGTERM.

492 */

493 if (sigemptyset(&sset) != 0 ||

494 sigaddset(&sset, SIGHUP) != 0 ||

495 sigaddset(&sset, SIGINT) != 0 ||

496 sigaddset(&sset, SIGTERM) != 0) {

497 isc__strerror(errno,
strbuf, sizeof(strbuf));

498 UNEXPECTED_ERROR(__FILE__,
__LINE__,

499 "isc_app_run() sigsetops: %s", strbuf);

500 return (ISC_R_UNEXPECTED);

501 }

502

503 #ifndef

HAVE_UNIXWARE_SIGWAIT

504 result =
sigwait(&sset, &sig);

505 if (result ==
0) {

506 if (sig == SIGINT ||

507 sig == SIGTERM)

508 want_shutdown =
ISC_TRUE;

509 else if (sig == SIGHUP)

510 want_reload =
ISC_TRUE;

511 }

512

513 #else
/* Using UnixWare sigwait semantics. */

514 sig =
sigwait(&sset);

515 if (sig >= 0) {

516 if (sig == SIGINT ||

517 sig == SIGTERM)

518 want_shutdown =
ISC_TRUE;

519 else if (sig == SIGHUP)

520 want_reload =
ISC_TRUE;

521 }

522

523 #endif
/* HAVE_UNIXWARE_SIGWAIT */

524 #else /* Don't have sigwait(). */

525 /*

526 * Listen for all signals.

527 */

528 if (sigemptyset(&sset) != 0) {

529 isc__strerror(errno,
strbuf, sizeof(strbuf));

530 UNEXPECTED_ERROR(__FILE__,
__LINE__,

531 "isc_app_run() sigsetops: %s", strbuf);

532 return (ISC_R_UNEXPECTED);

533 }

534 result =
sigsuspend(&sset);

535 #endif
/* HAVE_SIGWAIT */

536

537 if (want_reload)
{

538 want_reload =
ISC_FALSE;

539 return (ISC_R_RELOAD);

540 }

541

542 if (want_shutdown &&
blocked)

543 exit(1);

544 }

545

546 #else
/* ISC_PLATFORM_USETHREADS */

547

548 (void)isc__taskmgr_dispatch();

549

550 result =
evloop();

551 if (result !=
ISC_R_SUCCESS)

552 return (result);

553

554 #endif
/* ISC_PLATFORM_USETHREADS */

555

556 return (ISC_R_SUCCESS);

557 }

正常关闭时,需要将相关线程也中止

558

559 isc_result_t

560 isc_app_shutdown(void)
{

561 isc_boolean_t want_kill
= ISC_TRUE;

562 char strbuf[ISC_STRERRORSIZE];

563

564 LOCK(&lock);

565

566 REQUIRE(running);

567

568 if (shutdown_requested)

569 want_kill =
ISC_FALSE;

570 else

571 shutdown_requested =
ISC_TRUE;

572

573 UNLOCK(&lock);

574

575 if (want_kill) {

576 #ifdef

HAVE_LINUXTHREADS

577 int
result;

578

579 result =
pthread_kill(main_thread, SIGTERM);

580 if (result !=
0) {

581 isc__strerror(result,
strbuf, sizeof(strbuf));

582 UNEXPECTED_ERROR(__FILE__,
__LINE__,

583 "isc_app_shutdown() pthread_kill: %s",

584 strbuf);

585 return (ISC_R_UNEXPECTED);

586 }

587 #else

588 if (kill(getpid(), SIGTERM) < 0) {

589 isc__strerror(errno,
strbuf, sizeof(strbuf));

590 UNEXPECTED_ERROR(__FILE__,
__LINE__,

591 "isc_app_shutdown() kill: %s", strbuf);

592 return (ISC_R_UNEXPECTED);

593 }

594 #endif

595 }

596

597 return (ISC_R_SUCCESS);

598 }

599

五、 事件与任务处理

还没有充分看代码,需要进一步看代码,主要参考文件去理解app.c和Task.c去理解http://202.120.16.5/lxr/http/source/lib/isc/unix/app.c#L303

task = event->ev_sender;

event->ev_sender = NULL;

isc_task_sendanddetach(&task,
&event);

void

430 isc_task_sendanddetach(isc_task_t **taskp,
isc_event_t **eventp) {

431 isc_boolean_t idle1, idle2;

432 isc_task_t *task;

433

434 /*

435 * Send '*event' to '*taskp' and then detach '*taskp' from its

436 * task.

437 */

438

439 REQUIRE(taskp != NULL);

440 task = *taskp;

441 REQUIRE(VALID_TASK(task));

442

443 XTRACE("isc_task_sendanddetach");

444

445 LOCK(&task->lock);

446 idle1 =
task_send(task,
eventp);

447 idle2 =
task_detach(task);

448 UNLOCK(&task->lock);

449

450 /*

451 * If idle1, then idle2 shouldn't be true as well since we're holding

452 * the task lock, and thus the task cannot switch from ready back to

453 * idle.

454 */

455 INSIST(!(idle1 && idle2));

456

457 if (idle1 || idle2)

458 task_ready(task);

459

460 *taskp = NULL;

461 }

462

463 #define
PURGE_OK(event) (((event)->ev_attributes &

ISC_EVENTATTR_NOPURGE) == 0)

464

六 服务任务生成(主要与rndc的channel通信)

3826 void

3827 ns_server_create(isc_mem_t *mctx,
ns_server_t **serverp) {

3828 isc_result_t result;

3829

3830 ns_server_t *server
= isc_mem_get(mctx,
sizeof(*server));

3831 if (server == NULL)

3832 fatal("allocating
server object",
ISC_R_NOMEMORY);

3833

3834 server->mctx =
mctx;

3835 server->task =
NULL;

3836

3837 /* Initialize configuration data with default values. */

3838

3839 result =
isc_quota_init(&server->xfroutquota, 10);

3840 RUNTIME_CHECK(result ==
ISC_R_SUCCESS);

3841 result =
isc_quota_init(&server->tcpquota, 10);

3842 RUNTIME_CHECK(result ==
ISC_R_SUCCESS);

3843 result =
isc_quota_init(&server->recursionquota, 100);

3844 RUNTIME_CHECK(result ==
ISC_R_SUCCESS);

3845

3846 result =
dns_aclenv_init(mctx,
&server->aclenv);

3847 RUNTIME_CHECK(result ==
ISC_R_SUCCESS);

3848

3849 /* Initialize server data structures. */

3850 server->zonemgr =
NULL; 区管理器

3851 server->interfacemgr = NULL;接口管理器

3852 ISC_LIST_INIT(server->viewlist);

3853 server->in_roothints = NULL;

3854 server->blackholeacl = NULL; 服务的ACL机制

3855

3856 CHECKFATAL(dns_rootns_create(mctx,
dns_rdataclass_in, NULL,

3857 &server->in_roothints),

3858 "setting up root hints");

3859

3860 CHECKFATAL(isc_mutex_init(&server->reload_event_lock),

3861 "initializing reload event lock");

绑定reload事件处理

3862 server->reload_event =

3863 isc_event_allocate(ns_g_mctx,
server,

3864 NS_EVENT_RELOAD,

3865 ns_server_reload,

3866 server,

3867 sizeof(isc_event_t));

3868 CHECKFATAL(server->reload_event == NULL ?

3869 ISC_R_NOMEMORY :
ISC_R_SUCCESS,

3870 "allocating reload event");

3871

3872 CHECKFATAL(dst_lib_init(ns_g_mctx,
ns_g_entropy,
ISC_ENTROPY_GOODONLY), 加密机制dst hmac

3873 "initializing DST");

3874 TKEY加密管理

3875 server->tkeyctx = NULL;

3876 CHECKFATAL(dns_tkeyctx_create(ns_g_mctx,
ns_g_entropy,

3877 &server->tkeyctx),

3878 "creating TKEY context");

3879

3880 /*

3881 * Setup the server task, which is responsible for coordinating

3882 * startup and shutdown of the server.

3883 */

生成服务器的任务机制

3884 CHECKFATAL(isc_task_create(ns_g_taskmgr,
0, &server->task),

3885 "creating server task");

3886 isc_task_setname(server->task,
"server", server);

3887 CHECKFATAL(isc_task_onshutdown(server->task,
shutdown_server, server),

3888 "isc_task_onshutdown");

3889 CHECKFATAL(isc_app_onrun(ns_g_mctx,
server->task,
run_server, server),

3890 "isc_app_onrun");

3891

3892 server->interface_timer = NULL; 接口保活

3893 server->heartbeat_timer = NULL; 心跳定时器

3894 server->pps_timer = NULL;

3895

3896 server->interface_interval = 0;

3897 server->heartbeat_interval = 0;

3898 生成区管理器

3899 CHECKFATAL(dns_zonemgr_create(ns_g_mctx,
ns_g_taskmgr, ns_g_timermgr,

3900 ns_g_socketmgr, &server->zonemgr),

3901 "dns_zonemgr_create");

3902 统计文件,注意有以下统计信息:opcode,zone,resolver,receive query等

3903 server->statsfile =
isc_mem_strdup(server->mctx,
"named.stats");

3904 CHECKFATAL(server->statsfile == NULL ?
ISC_R_NOMEMORY :
ISC_R_SUCCESS,

3905 "isc_mem_strdup");

3906 server->nsstats = NULL;

3907 server->rcvquerystats = NULL;

3908 server->opcodestats = NULL;

3909 server->zonestats = NULL;

3910 server->resolverstats = NULL;

3911 转储文件

3912 server->dumpfile =
isc_mem_strdup(server->mctx,
"named_dump.db");

3913 CHECKFATAL(server->dumpfile == NULL ?
ISC_R_NOMEMORY :
ISC_R_SUCCESS,

3914 "isc_mem_strdup");

3915 递归文件

3916 server->recfile =
isc_mem_strdup(server->mctx,
"named.recursing");

3917 CHECKFATAL(server->recfile == NULL ?
ISC_R_NOMEMORY :
ISC_R_SUCCESS,

3918 "isc_mem_strdup");

3919

3920 server->hostname_set =
ISC_FALSE;

3921 server->hostname =
NULL;

3922 server->version_set =
ISC_FALSE;

3923 server->version =
NULL;

3924 server->server_usehostname =
ISC_FALSE;

3925 server->server_id = NULL;

3926

以下处理统计信息收集

3927 CHECKFATAL(dns_generalstats_create(ns_g_mctx,
&server->nsstats,

3928 dns_nsstatscounter_max),

3929 "dns_stats_create (server)");

3930

3931 CHECKFATAL(dns_rdatatypestats_create(ns_g_mctx,

3932 &server->rcvquerystats),

3933 "dns_stats_create (rcvquery)");

3934

3935 CHECKFATAL(dns_opcodestats_create(ns_g_mctx,
&server->opcodestats),

3936 "dns_stats_create (opcode)");

3937

3938 CHECKFATAL(dns_generalstats_create(ns_g_mctx,
&server->zonestats,

3939 dns_zonestatscounter_max),

3940 "dns_stats_create (zone)");

3941

3942 CHECKFATAL(dns_generalstats_create(ns_g_mctx,
&server->resolverstats,

3943 dns_resstatscounter_max),

3944 "dns_stats_create (resolver)");

3945

3946 server->flushonshutdown =
ISC_FALSE;

默认查询日志是关闭的,打开查询日志较大地影响解析性能

3947 server->log_queries =
ISC_FALSE;

3948

3949 server->controls = NULL;

3950 CHECKFATAL(ns_controls_create(server,
&server->controls),

3951 "ns_controls_create");

3952 server->dispatchgen =
0;

3953 ISC_LIST_INIT(server->dispatches);

3954

3955 ISC_LIST_INIT(server->statschannels);

3956

3957 server->magic =
NS_SERVER_MAGIC;

3958 *serverp = server;

3959 }

3960

附录:

事件结构:

80 struct
isc_event {

81 ISC_EVENT_COMMON(struct
isc_event);



34 #define
ISC_EVENT_COMMON(ltype) \

35 size_t ev_size; \

36 unsigned int ev_attributes; \

37 void * ev_tag; \

38 isc_eventtype_t ev_type;
\

39 isc_taskaction_t ev_action;
\

40 void * ev_arg; \

41 void * ev_sender; \

42 isc_eventdestructor_t ev_destroy;
\

43 void * ev_destroy_arg; \

44 ISC_LINK(ltype)
ev_link

任务结构:

http://202.120.16.5/lxr/http/source/lib/isc/task.c#L78

78 struct
isc_task {

79 /* Not locked. */

80 unsigned int magic;

81 isc_taskmgr_t *
manager;

82 isc_mutex_t lock;

83 /* Locked by task lock. */

84 task_state_t state;

85 unsigned int references;

86 isc_eventlist_t events;

87 isc_eventlist_t on_shutdown;

88 unsigned int quantum;

89 unsigned int flags;

90 isc_stdtime_t now;

91 char name[16];

92 void * tag;

93 /* Locked by task manager lock. */

94 LINK(isc_task_t)
link;

95 LINK(isc_task_t)
ready_link;

96 };

97
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: