📄 controlconf.c
字号:
/* * Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 2001-2003 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. *//* $Id: controlconf.c,v 1.28.2.9.2.6 2004/03/08 09:04:14 marka Exp $ */#include <config.h>#include <isc/base64.h>#include <isc/buffer.h>#include <isc/event.h>#include <isc/mem.h>#include <isc/net.h>#include <isc/netaddr.h>#include <isc/random.h>#include <isc/result.h>#include <isc/stdtime.h>#include <isc/string.h>#include <isc/timer.h>#include <isc/util.h>#include <isccfg/namedconf.h>#include <bind9/check.h>#include <isccc/alist.h>#include <isccc/cc.h>#include <isccc/ccmsg.h>#include <isccc/events.h>#include <isccc/result.h>#include <isccc/sexpr.h>#include <isccc/symtab.h>#include <isccc/util.h>#include <dns/result.h>#include <named/config.h>#include <named/control.h>#include <named/log.h>#include <named/server.h>/* * Note: Listeners and connections are not locked. All event handlers are * executed by the server task, and all callers of exported routines must * be running under the server task. */typedef struct controlkey controlkey_t;typedef ISC_LIST(controlkey_t) controlkeylist_t;typedef struct controlconnection controlconnection_t;typedef ISC_LIST(controlconnection_t) controlconnectionlist_t;typedef struct controllistener controllistener_t;typedef ISC_LIST(controllistener_t) controllistenerlist_t;struct controlkey { char * keyname; isc_region_t secret; ISC_LINK(controlkey_t) link;};struct controlconnection { isc_socket_t * sock; isccc_ccmsg_t ccmsg; isc_boolean_t ccmsg_valid; isc_boolean_t sending; isc_timer_t * timer; unsigned char buffer[2048]; controllistener_t * listener; isc_uint32_t nonce; ISC_LINK(controlconnection_t) link;};struct controllistener { ns_controls_t * controls; isc_mem_t * mctx; isc_task_t * task; isc_sockaddr_t address; isc_socket_t * sock; dns_acl_t * acl; isc_boolean_t listening; isc_boolean_t exiting; controlkeylist_t keys; controlconnectionlist_t connections; ISC_LINK(controllistener_t) link;};struct ns_controls { ns_server_t *server; controllistenerlist_t listeners; isc_boolean_t shuttingdown; isccc_symtab_t *symtab;};static void control_newconn(isc_task_t *task, isc_event_t *event);static void control_recvmessage(isc_task_t *task, isc_event_t *event);#define CLOCKSKEW 300static voidfree_controlkey(controlkey_t *key, isc_mem_t *mctx) { if (key->keyname != NULL) isc_mem_free(mctx, key->keyname); if (key->secret.base != NULL) isc_mem_put(mctx, key->secret.base, key->secret.length); isc_mem_put(mctx, key, sizeof(*key));}static voidfree_controlkeylist(controlkeylist_t *keylist, isc_mem_t *mctx) { while (!ISC_LIST_EMPTY(*keylist)) { controlkey_t *key = ISC_LIST_HEAD(*keylist); ISC_LIST_UNLINK(*keylist, key, link); free_controlkey(key, mctx); }}static voidfree_listener(controllistener_t *listener) { INSIST(listener->exiting); INSIST(!listener->listening); INSIST(ISC_LIST_EMPTY(listener->connections)); if (listener->sock != NULL) isc_socket_detach(&listener->sock); free_controlkeylist(&listener->keys, listener->mctx); if (listener->acl != NULL) dns_acl_detach(&listener->acl); isc_mem_put(listener->mctx, listener, sizeof(*listener));}static voidmaybe_free_listener(controllistener_t *listener) { if (listener->exiting && !listener->listening && ISC_LIST_EMPTY(listener->connections)) free_listener(listener);}static voidmaybe_free_connection(controlconnection_t *conn) { controllistener_t *listener = conn->listener; if (conn->timer != NULL) isc_timer_detach(&conn->timer); if (conn->ccmsg_valid) { isccc_ccmsg_cancelread(&conn->ccmsg); return; } if (conn->sending) { isc_socket_cancel(conn->sock, listener->task, ISC_SOCKCANCEL_SEND); return; } ISC_LIST_UNLINK(listener->connections, conn, link); isc_mem_put(listener->mctx, conn, sizeof(*conn));}static voidshutdown_listener(controllistener_t *listener) { controlconnection_t *conn; controlconnection_t *next; if (!listener->exiting) { char socktext[ISC_SOCKADDR_FORMATSIZE]; ISC_LIST_UNLINK(listener->controls->listeners, listener, link); isc_sockaddr_format(&listener->address, socktext, sizeof(socktext)); isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_CONTROL, ISC_LOG_NOTICE, "stopping command channel on %s", socktext); listener->exiting = ISC_TRUE; } for (conn = ISC_LIST_HEAD(listener->connections); conn != NULL; conn = next) { next = ISC_LIST_NEXT(conn, link); maybe_free_connection(conn); } if (listener->listening) isc_socket_cancel(listener->sock, listener->task, ISC_SOCKCANCEL_ACCEPT); maybe_free_listener(listener);}static isc_boolean_taddress_ok(isc_sockaddr_t *sockaddr, dns_acl_t *acl) { isc_netaddr_t netaddr; isc_result_t result; int match; isc_netaddr_fromsockaddr(&netaddr, sockaddr); result = dns_acl_match(&netaddr, NULL, acl, &ns_g_server->aclenv, &match, NULL); if (result != ISC_R_SUCCESS || match <= 0) return (ISC_FALSE); else return (ISC_TRUE);}static isc_result_tcontrol_accept(controllistener_t *listener) { isc_result_t result; result = isc_socket_accept(listener->sock, listener->task, control_newconn, listener); if (result != ISC_R_SUCCESS) UNEXPECTED_ERROR(__FILE__, __LINE__, "isc_socket_accept() failed: %s", isc_result_totext(result)); else listener->listening = ISC_TRUE; return (result);}static isc_result_tcontrol_listen(controllistener_t *listener) { isc_result_t result; result = isc_socket_listen(listener->sock, 0); if (result != ISC_R_SUCCESS) UNEXPECTED_ERROR(__FILE__, __LINE__, "isc_socket_listen() failed: %s", isc_result_totext(result)); return (result);}static voidcontrol_next(controllistener_t *listener) { (void)control_accept(listener);}static voidcontrol_senddone(isc_task_t *task, isc_event_t *event) { isc_socketevent_t *sevent = (isc_socketevent_t *) event; controlconnection_t *conn = event->ev_arg; controllistener_t *listener = conn->listener; isc_socket_t *sock = (isc_socket_t *)sevent->ev_sender; isc_result_t result; REQUIRE(conn->sending); UNUSED(task); conn->sending = ISC_FALSE; if (sevent->result != ISC_R_SUCCESS && sevent->result != ISC_R_CANCELED) { char socktext[ISC_SOCKADDR_FORMATSIZE]; isc_sockaddr_t peeraddr; (void)isc_socket_getpeername(sock, &peeraddr); isc_sockaddr_format(&peeraddr, socktext, sizeof(socktext)); isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_CONTROL, ISC_LOG_WARNING, "error sending command response to %s: %s", socktext, isc_result_totext(sevent->result)); } isc_event_free(&event); result = isccc_ccmsg_readmessage(&conn->ccmsg, listener->task, control_recvmessage, conn); if (result != ISC_R_SUCCESS) { isc_socket_detach(&conn->sock); maybe_free_connection(conn); maybe_free_listener(listener); }}static inline voidlog_invalid(isccc_ccmsg_t *ccmsg, isc_result_t result) { char socktext[ISC_SOCKADDR_FORMATSIZE]; isc_sockaddr_t peeraddr; (void)isc_socket_getpeername(ccmsg->sock, &peeraddr); isc_sockaddr_format(&peeraddr, socktext, sizeof(socktext)); isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_CONTROL, ISC_LOG_ERROR, "invalid command from %s: %s", socktext, isc_result_totext(result));}static voidcontrol_recvmessage(isc_task_t *task, isc_event_t *event) { controlconnection_t *conn; controllistener_t *listener; controlkey_t *key; isccc_sexpr_t *request = NULL; isccc_sexpr_t *response = NULL; isccc_region_t ccregion; isccc_region_t secret; isc_stdtime_t now; isc_buffer_t b; isc_region_t r; isc_uint32_t len; isc_buffer_t text; char textarray[1024]; isc_result_t result; isc_result_t eresult; isccc_sexpr_t *_ctrl; isccc_time_t sent; isccc_time_t exp; isc_uint32_t nonce; REQUIRE(event->ev_type == ISCCC_EVENT_CCMSG); conn = event->ev_arg; listener = conn->listener; secret.rstart = NULL; /* Is the server shutting down? */ if (listener->controls->shuttingdown) goto cleanup; if (conn->ccmsg.result != ISC_R_SUCCESS) { if (conn->ccmsg.result != ISC_R_CANCELED && conn->ccmsg.result != ISC_R_EOF) log_invalid(&conn->ccmsg, conn->ccmsg.result); goto cleanup; } request = NULL; for (key = ISC_LIST_HEAD(listener->keys); key != NULL; key = ISC_LIST_NEXT(key, link)) { ccregion.rstart = isc_buffer_base(&conn->ccmsg.buffer); ccregion.rend = isc_buffer_used(&conn->ccmsg.buffer); secret.rstart = isc_mem_get(listener->mctx, key->secret.length); if (secret.rstart == NULL) goto cleanup; memcpy(secret.rstart, key->secret.base, key->secret.length); secret.rend = secret.rstart + key->secret.length; result = isccc_cc_fromwire(&ccregion, &request, &secret); if (result == ISC_R_SUCCESS) break; else if (result == ISCCC_R_BADAUTH) { /* * For some reason, request is non-NULL when * isccc_cc_fromwire returns ISCCC_R_BADAUTH. */ if (request != NULL) isccc_sexpr_free(&request); isc_mem_put(listener->mctx, secret.rstart, REGION_SIZE(secret)); } else { log_invalid(&conn->ccmsg, result); goto cleanup; } } if (key == NULL) { log_invalid(&conn->ccmsg, ISCCC_R_BADAUTH); goto cleanup; } /* We shouldn't be getting a reply. */ if (isccc_cc_isreply(request)) { log_invalid(&conn->ccmsg, ISC_R_FAILURE); goto cleanup; } isc_stdtime_get(&now); /* * Limit exposure to replay attacks. */ _ctrl = isccc_alist_lookup(request, "_ctrl"); if (_ctrl == NULL) { log_invalid(&conn->ccmsg, ISC_R_FAILURE); goto cleanup; } if (isccc_cc_lookupuint32(_ctrl, "_tim", &sent) == ISC_R_SUCCESS) { if ((sent + CLOCKSKEW) < now || (sent - CLOCKSKEW) > now) { log_invalid(&conn->ccmsg, ISCCC_R_CLOCKSKEW); goto cleanup; } } else { log_invalid(&conn->ccmsg, ISC_R_FAILURE); goto cleanup; } /* * Expire messages that are too old. */ if (isccc_cc_lookupuint32(_ctrl, "_exp", &exp) == ISC_R_SUCCESS && now > exp) { log_invalid(&conn->ccmsg, ISCCC_R_EXPIRED); goto cleanup; } /* * Duplicate suppression (required for UDP). */ isccc_cc_cleansymtab(listener->controls->symtab, now); result = isccc_cc_checkdup(listener->controls->symtab, request, now); if (result != ISC_R_SUCCESS) { if (result == ISC_R_EXISTS) result = ISCCC_R_DUPLICATE; log_invalid(&conn->ccmsg, result); goto cleanup; } if (conn->nonce != 0 && (isccc_cc_lookupuint32(_ctrl, "_nonce", &nonce) != ISC_R_SUCCESS || conn->nonce != nonce)) { log_invalid(&conn->ccmsg, ISCCC_R_BADAUTH); goto cleanup; } /*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -