From: Evan Hunt Date: Fri, 14 Nov 2014 23:58:54 +0000 (-0800) Subject: [master] allow arbitrary-size rndc output X-Git-Tag: v9.11.0a1~1272 X-Git-Url: http://git.ipfire.org/gitweb/?a=commitdiff_plain;h=e32d354f754a5d7847a0862bcd6302827ea225bf;p=thirdparty%2Fbind9.git [master] allow arbitrary-size rndc output 4005. [func] The buffer used for returning text from rndc commands is now dynamically resizable, allowing arbitrarily large amounts of text to be sent back to the client. (Prior to this change, it was possible for the output of "rndc tsig-list" to be truncated.) [RT #37731] --- diff --git a/CHANGES b/CHANGES index 1ed92b40a93..8c72283732e 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,10 @@ +4005. [func] The buffer used for returning text from rndc + commands is now dynamically resizable, allowing + arbitrarily large amounts of text to be sent back + to the client. (Prior to this change, it was + possible for the output of "rndc tsig-list" to be + truncated.) [RT #37731] + 4004. [bug] When delegations had AAAA glue but not A, a reference could be leaked causing an assertion failure on shutdown. [RT #37796] diff --git a/bin/named/control.c b/bin/named/control.c index 5def85fb5bd..41be1f36537 100644 --- a/bin/named/control.c +++ b/bin/named/control.c @@ -59,7 +59,7 @@ command_compare(const char *text, const char *command) { * when a control channel message is received. */ isc_result_t -ns_control_docommand(isccc_sexpr_t *message, isc_buffer_t *text) { +ns_control_docommand(isccc_sexpr_t *message, isc_buffer_t **text) { isccc_sexpr_t *data; char *command = NULL; isc_result_t result; @@ -132,7 +132,7 @@ ns_control_docommand(isccc_sexpr_t *message, isc_buffer_t *text) { #endif /* Do not flush master files */ ns_server_flushonshutdown(ns_g_server, ISC_FALSE); - ns_os_shutdownmsg(command, text); + ns_os_shutdownmsg(command, *text); isc_app_shutdown(); result = ISC_R_SUCCESS; } else if (command_compare(command, NS_COMMAND_STOP)) { @@ -149,7 +149,7 @@ ns_control_docommand(isccc_sexpr_t *message, isc_buffer_t *text) { ns_smf_want_disable = 1; #endif ns_server_flushonshutdown(ns_g_server, ISC_TRUE); - ns_os_shutdownmsg(command, text); + ns_os_shutdownmsg(command, *text); isc_app_shutdown(); result = ISC_R_SUCCESS; } else if (command_compare(command, NS_COMMAND_DUMPSTATS)) { @@ -215,6 +215,8 @@ ns_control_docommand(isccc_sexpr_t *message, isc_buffer_t *text) { result = ns_server_zonestatus(ns_g_server, command, text); } else if (command_compare(command, NS_COMMAND_NTA)) { result = ns_server_nta(ns_g_server, command, text); + } else if (command_compare(command, NS_COMMAND_TESTGEN)) { + result = ns_server_testgen(command, text); } else { isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_CONTROL, ISC_LOG_WARNING, diff --git a/bin/named/controlconf.c b/bin/named/controlconf.c index 765afdd9662..9492aae8c65 100644 --- a/bin/named/controlconf.c +++ b/bin/named/controlconf.c @@ -83,7 +83,7 @@ struct controlconnection { isc_boolean_t ccmsg_valid; isc_boolean_t sending; isc_timer_t * timer; - unsigned char buffer[2048]; + isc_buffer_t * buffer; controllistener_t * listener; isc_uint32_t nonce; ISC_LINK(controlconnection_t) link; @@ -166,6 +166,9 @@ static void maybe_free_connection(controlconnection_t *conn) { controllistener_t *listener = conn->listener; + if (conn->buffer != NULL) + isc_buffer_free(&conn->buffer); + if (conn->timer != NULL) isc_timer_detach(&conn->timer); @@ -326,15 +329,12 @@ control_recvmessage(isc_task_t *task, isc_event_t *event) { controlkey_t *key; isccc_sexpr_t *request = NULL; isccc_sexpr_t *response = NULL; - isccc_region_t ccregion; isc_uint32_t algorithm; 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[2*1024]; + isc_buffer_t *text; isc_result_t result; isc_result_t eresult; isccc_sexpr_t *_ctrl; @@ -348,6 +348,7 @@ control_recvmessage(isc_task_t *task, isc_event_t *event) { listener = conn->listener; algorithm = DST_ALG_UNKNOWN; secret.rstart = NULL; + text = NULL; /* Is the server shutting down? */ if (listener->controls->shuttingdown) @@ -366,6 +367,8 @@ control_recvmessage(isc_task_t *task, isc_event_t *event) { key != NULL; key = ISC_LIST_NEXT(key, link)) { + isccc_region_t ccregion; + 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); @@ -445,7 +448,9 @@ control_recvmessage(isc_task_t *task, isc_event_t *event) { goto cleanup_request; } - isc_buffer_init(&text, textarray, sizeof(textarray)); + result = isc_buffer_allocate(listener->mctx, &text, 2 * 2048); + if (result != ISC_R_SUCCESS) + goto cleanup_request; /* * Establish nonce. @@ -471,12 +476,12 @@ control_recvmessage(isc_task_t *task, isc_event_t *event) { } } - if (isc_buffer_usedlength(&text) > 0) { + if (isc_buffer_usedlength(text) > 0) { isccc_sexpr_t *data; data = isccc_alist_lookup(response, "_data"); if (data != NULL) { - char *str = (char *)isc_buffer_base(&text); + char *str = (char *)isc_buffer_base(text); if (isccc_cc_definestring(data, "text", str) == NULL) goto cleanup_response; } @@ -487,16 +492,26 @@ control_recvmessage(isc_task_t *task, isc_event_t *event) { isccc_cc_defineuint32(_ctrl, "_nonce", conn->nonce) == NULL) goto cleanup_response; - ccregion.rstart = conn->buffer + 4; - ccregion.rend = conn->buffer + sizeof(conn->buffer); - result = isccc_cc_towire(response, &ccregion, algorithm, &secret); + if (conn->buffer == NULL) { + result = isc_buffer_allocate(listener->mctx, + &conn->buffer, 2 * 2048); + if (result != ISC_R_SUCCESS) + goto cleanup_response; + } + + isc_buffer_clear(conn->buffer); + /* Skip the length field (4 bytes) */ + isc_buffer_add(conn->buffer, 4); + + result = isccc_cc_towire(response, &conn->buffer, algorithm, &secret); if (result != ISC_R_SUCCESS) goto cleanup_response; - isc_buffer_init(&b, conn->buffer, 4); - len = sizeof(conn->buffer) - REGION_SIZE(ccregion); - isc_buffer_putuint32(&b, len - 4); - r.base = conn->buffer; - r.length = len; + + isc_buffer_init(&b, conn->buffer->base, 4); + isc_buffer_putuint32(&b, conn->buffer->used - 4); + + r.base = conn->buffer->base; + r.length = conn->buffer->used; result = isc_socket_send(conn->sock, &r, task, control_senddone, conn); if (result != ISC_R_SUCCESS) @@ -506,6 +521,7 @@ control_recvmessage(isc_task_t *task, isc_event_t *event) { isc_mem_put(listener->mctx, secret.rstart, REGION_SIZE(secret)); isccc_sexpr_free(&request); isccc_sexpr_free(&response); + isc_buffer_free(&text); return; cleanup_response: @@ -514,6 +530,8 @@ control_recvmessage(isc_task_t *task, isc_event_t *event) { cleanup_request: isccc_sexpr_free(&request); isc_mem_put(listener->mctx, secret.rstart, REGION_SIZE(secret)); + if (text != NULL) + isc_buffer_free(&text); cleanup: isc_socket_detach(&conn->sock); @@ -549,6 +567,7 @@ newconnection(controllistener_t *listener, isc_socket_t *sock) { isccc_ccmsg_init(listener->mctx, sock, &conn->ccmsg); conn->ccmsg_valid = ISC_TRUE; conn->sending = ISC_FALSE; + conn->buffer = NULL; conn->timer = NULL; isc_interval_set(&interval, 60, 0); result = isc_timer_create(ns_g_timermgr, isc_timertype_once, @@ -565,12 +584,13 @@ newconnection(controllistener_t *listener, isc_socket_t *sock) { control_recvmessage, conn); if (result != ISC_R_SUCCESS) goto cleanup; - isccc_ccmsg_setmaxsize(&conn->ccmsg, 2048); ISC_LIST_APPEND(listener->connections, conn, link); return (ISC_R_SUCCESS); cleanup: + if (conn->buffer != NULL) + isc_buffer_free(&conn->buffer); isccc_ccmsg_invalidate(&conn->ccmsg); if (conn->timer != NULL) isc_timer_detach(&conn->timer); diff --git a/bin/named/include/named/control.h b/bin/named/include/named/control.h index 33a16193d94..ee1d6969453 100644 --- a/bin/named/include/named/control.h +++ b/bin/named/include/named/control.h @@ -68,6 +68,7 @@ #define NS_COMMAND_SIGNING "signing" #define NS_COMMAND_ZONESTATUS "zonestatus" #define NS_COMMAND_NTA "nta" +#define NS_COMMAND_TESTGEN "testgen" isc_result_t ns_controls_create(ns_server_t *server, ns_controls_t **ctrlsp); @@ -101,6 +102,6 @@ ns_controls_shutdown(ns_controls_t *controls); */ isc_result_t -ns_control_docommand(isccc_sexpr_t *message, isc_buffer_t *text); +ns_control_docommand(isccc_sexpr_t *message, isc_buffer_t **text); #endif /* NAMED_CONTROL_H */ diff --git a/bin/named/include/named/server.h b/bin/named/include/named/server.h index 0b241b7471b..953eae77d36 100644 --- a/bin/named/include/named/server.h +++ b/bin/named/include/named/server.h @@ -235,7 +235,7 @@ ns_server_flushonshutdown(ns_server_t *server, isc_boolean_t flush); */ isc_result_t -ns_server_reloadcommand(ns_server_t *server, char *args, isc_buffer_t *text); +ns_server_reloadcommand(ns_server_t *server, char *args, isc_buffer_t **text); /*%< * Act on a "reload" command from the command channel. */ @@ -247,20 +247,20 @@ ns_server_reconfigcommand(ns_server_t *server, char *args); */ isc_result_t -ns_server_notifycommand(ns_server_t *server, char *args, isc_buffer_t *text); +ns_server_notifycommand(ns_server_t *server, char *args, isc_buffer_t **text); /*%< * Act on a "notify" command from the command channel. */ isc_result_t -ns_server_refreshcommand(ns_server_t *server, char *args, isc_buffer_t *text); +ns_server_refreshcommand(ns_server_t *server, char *args, isc_buffer_t **text); /*%< * Act on a "refresh" command from the command channel. */ isc_result_t ns_server_retransfercommand(ns_server_t *server, char *args, - isc_buffer_t *text); + isc_buffer_t **text); /*%< * Act on a "retransfer" command from the command channel. */ @@ -314,32 +314,32 @@ ns_server_flushnode(ns_server_t *server, char *args, isc_boolean_t tree); * Report the server's status. */ isc_result_t -ns_server_status(ns_server_t *server, isc_buffer_t *text); +ns_server_status(ns_server_t *server, isc_buffer_t **text); /*% * Report a list of dynamic and static tsig keys, per view. */ isc_result_t -ns_server_tsiglist(ns_server_t *server, isc_buffer_t *text); +ns_server_tsiglist(ns_server_t *server, isc_buffer_t **text); /*% * Delete a specific key (with optional view). */ isc_result_t -ns_server_tsigdelete(ns_server_t *server, char *command, isc_buffer_t *text); +ns_server_tsigdelete(ns_server_t *server, char *command, isc_buffer_t **text); /*% * Enable or disable updates for a zone. */ isc_result_t ns_server_freeze(ns_server_t *server, isc_boolean_t freeze, char *args, - isc_buffer_t *text); + isc_buffer_t **text); /*% * Dump zone updates to disk, optionally removing the journal file */ isc_result_t -ns_server_sync(ns_server_t *server, char *args, isc_buffer_t *text); +ns_server_sync(ns_server_t *server, char *args, isc_buffer_t **text); /*% * Update a zone's DNSKEY set from the key repository. If @@ -349,7 +349,7 @@ ns_server_sync(ns_server_t *server, char *args, isc_buffer_t *text); * take place incrementally. */ isc_result_t -ns_server_rekey(ns_server_t *server, char *args, isc_buffer_t *text); +ns_server_rekey(ns_server_t *server, char *args, isc_buffer_t **text); /*% * Dump the current recursive queries. @@ -367,37 +367,44 @@ ns_add_reserved_dispatch(ns_server_t *server, const isc_sockaddr_t *addr); * Enable or disable dnssec validation. */ isc_result_t -ns_server_validation(ns_server_t *server, char *args, isc_buffer_t *text); +ns_server_validation(ns_server_t *server, char *args, isc_buffer_t **text); /*% * Add a zone to a running process */ isc_result_t -ns_server_add_zone(ns_server_t *server, char *args, isc_buffer_t *text); +ns_server_add_zone(ns_server_t *server, char *args, isc_buffer_t **text); /*% * Deletes a zone from a running process */ isc_result_t -ns_server_del_zone(ns_server_t *server, char *args, isc_buffer_t *text); +ns_server_del_zone(ns_server_t *server, char *args, isc_buffer_t **text); /*% * Lists the status of the signing records for a given zone. */ isc_result_t -ns_server_signing(ns_server_t *server, char *args, isc_buffer_t *text); +ns_server_signing(ns_server_t *server, char *args, isc_buffer_t **text); /*% * Lists status information for a given zone (e.g., name, type, files, * load time, expiry, etc). */ isc_result_t -ns_server_zonestatus(ns_server_t *server, char *args, isc_buffer_t *text); +ns_server_zonestatus(ns_server_t *server, char *args, isc_buffer_t **text); /*% * Adds a Negative Trust Anchor (NTA) for a specified name and * duration, in a particular view if specified, or in all views. */ isc_result_t -ns_server_nta(ns_server_t *server, char *args, isc_buffer_t *text); +ns_server_nta(ns_server_t *server, char *args, isc_buffer_t **text); + +/*% + * Generates a test sequence that is only for use in system tests. The + * argument is the size of required output in bytes. + */ +isc_result_t +ns_server_testgen(char *args, isc_buffer_t **text); #endif /* NAMED_SERVER_H */ diff --git a/bin/named/server.c b/bin/named/server.c index c650b3d79a5..5c45fa8cdb1 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -155,7 +155,7 @@ #define TCHECK(op) \ do { tresult = (op); \ if (tresult != ISC_R_SUCCESS) { \ - isc_buffer_clear(text); \ + isc_buffer_clear(*text); \ goto cleanup; \ } \ } while (0) @@ -418,10 +418,16 @@ static void newzone_cfgctx_destroy(void **cfgp); static isc_result_t -putstr(isc_buffer_t *b, const char *str); +putstr(isc_buffer_t **b, const char *str); static isc_result_t -putnull(isc_buffer_t *b); +putmem(isc_buffer_t **b, const char *str, size_t len); + +static isc_result_t +putuint8(isc_buffer_t **b, isc_uint8_t val); + +static isc_result_t +putnull(isc_buffer_t **b); isc_result_t add_comment(FILE *fp, const char *viewname); @@ -7138,7 +7144,7 @@ next_token(char **stringp, const char *delim) { static isc_result_t zone_from_args(ns_server_t *server, char *args, const char *zonetxt, dns_zone_t **zonep, const char **zonename, - isc_buffer_t *text, isc_boolean_t skip) + isc_buffer_t **text, isc_boolean_t skip) { char *input, *ptr; char *classtxt; @@ -7243,7 +7249,7 @@ zone_from_args(ns_server_t *server, char *args, const char *zonetxt, */ isc_result_t ns_server_retransfercommand(ns_server_t *server, char *args, - isc_buffer_t *text) + isc_buffer_t **text) { isc_result_t result; dns_zone_t *zone = NULL; @@ -7275,7 +7281,7 @@ ns_server_retransfercommand(ns_server_t *server, char *args, * Act on a "reload" command from the command channel. */ isc_result_t -ns_server_reloadcommand(ns_server_t *server, char *args, isc_buffer_t *text) { +ns_server_reloadcommand(ns_server_t *server, char *args, isc_buffer_t **text) { isc_result_t result; dns_zone_t *zone = NULL; dns_zonetype_t type; @@ -7316,9 +7322,10 @@ ns_server_reloadcommand(ns_server_t *server, char *args, isc_buffer_t *text) { } } } - if (msg != NULL && strlen(msg) < isc_buffer_availablelength(text)) - isc_buffer_putmem(text, (const unsigned char *)msg, - strlen(msg) + 1); + if (msg != NULL) { + putstr(text, msg); + putnull(text); + } return (result); } @@ -7337,10 +7344,10 @@ ns_server_reconfigcommand(ns_server_t *server, char *args) { * Act on a "notify" command from the command channel. */ isc_result_t -ns_server_notifycommand(ns_server_t *server, char *args, isc_buffer_t *text) { +ns_server_notifycommand(ns_server_t *server, char *args, isc_buffer_t **text) { isc_result_t result; dns_zone_t *zone = NULL; - const unsigned char msg[] = "zone notify queued"; + const char msg[] = "zone notify queued"; result = zone_from_args(server, args, NULL, &zone, NULL, text, ISC_TRUE); @@ -7351,8 +7358,8 @@ ns_server_notifycommand(ns_server_t *server, char *args, isc_buffer_t *text) { dns_zone_notify(zone); dns_zone_detach(&zone); - if (sizeof(msg) <= isc_buffer_availablelength(text)) - isc_buffer_putmem(text, msg, sizeof(msg)); + putstr(text, msg); + putnull(text); return (ISC_R_SUCCESS); } @@ -7361,11 +7368,12 @@ ns_server_notifycommand(ns_server_t *server, char *args, isc_buffer_t *text) { * Act on a "refresh" command from the command channel. */ isc_result_t -ns_server_refreshcommand(ns_server_t *server, char *args, isc_buffer_t *text) { +ns_server_refreshcommand(ns_server_t *server, char *args, isc_buffer_t **text) +{ isc_result_t result; dns_zone_t *zone = NULL, *raw = NULL; - const unsigned char msg1[] = "zone refresh queued"; - const unsigned char msg2[] = "not a slave or stub zone"; + const char msg1[] = "zone refresh queued"; + const char msg2[] = "not a slave or stub zone"; dns_zonetype_t type; result = zone_from_args(server, args, NULL, &zone, NULL, @@ -7386,14 +7394,14 @@ ns_server_refreshcommand(ns_server_t *server, char *args, isc_buffer_t *text) { if (type == dns_zone_slave || type == dns_zone_stub) { dns_zone_refresh(zone); dns_zone_detach(&zone); - if (sizeof(msg1) <= isc_buffer_availablelength(text)) - isc_buffer_putmem(text, msg1, sizeof(msg1)); + putstr(text, msg1); + putnull(text); return (ISC_R_SUCCESS); } dns_zone_detach(&zone); - if (sizeof(msg2) <= isc_buffer_availablelength(text)) - isc_buffer_putmem(text, msg2, sizeof(msg2)); + putstr(text, msg2); + putnull(text); return (ISC_R_FAILURE); } @@ -7969,7 +7977,7 @@ ns_server_setdebuglevel(ns_server_t *server, char *args) { } isc_result_t -ns_server_validation(ns_server_t *server, char *args, isc_buffer_t *text) { +ns_server_validation(ns_server_t *server, char *args, isc_buffer_t **text) { char *ptr, *viewname; dns_view_t *view; isc_boolean_t changed = ISC_FALSE; @@ -8016,29 +8024,16 @@ ns_server_validation(ns_server_t *server, char *args, isc_buffer_t *text) { view->enablevalidation = enable; changed = ISC_TRUE; } else { - unsigned int n; - if (!first) { - n = snprintf((char *)isc_buffer_used(text), - isc_buffer_availablelength(text), - "\n"); - if (n >= isc_buffer_availablelength(text)) { - result = ISC_R_NOSPACE; - goto out; - } - isc_buffer_add(text, n); - } + if (!first) + putstr(text, "\n"); + putstr(text, "DNSSEC validation is "); + putstr(text, view->enablevalidation + ? "enabled" : "disabled"); + putstr(text, " (view "); + putstr(text, view->name); + putstr(text, ")"); + putnull(text); first = ISC_FALSE; - n = snprintf((char *)isc_buffer_used(text), - isc_buffer_availablelength(text), - "DNSSEC validation is %s (view %s)", - view->enablevalidation ? - "enabled" : "disabled", - view->name); - if (n >= isc_buffer_availablelength(text)) { - result = ISC_R_NOSPACE; - goto out; - } - isc_buffer_add(text, n); } } @@ -8280,12 +8275,12 @@ ns_server_flushnode(ns_server_t *server, char *args, isc_boolean_t tree) { } isc_result_t -ns_server_status(ns_server_t *server, isc_buffer_t *text) { +ns_server_status(ns_server_t *server, isc_buffer_t **text) { + isc_result_t result; unsigned int zonecount, xferrunning, xferdeferred, soaqueries; unsigned int automatic; - unsigned int n; const char *ob = "", *cb = "", *alt = ""; - char boottime[80], configtime[80]; + char boottime[80], configtime[80], line[1024]; if (ns_g_server->version_set) { ob = " ("; @@ -8310,40 +8305,93 @@ ns_server_status(ns_server_t *server, isc_buffer_t *text) { isc_time_formathttptimestamp(&ns_g_configtime, configtime, sizeof(configtime)); - n = snprintf((char *)isc_buffer_used(text), - isc_buffer_availablelength(text), - "version: %s%s%s%s \n" - "boot time: %s\n" - "last configured: %s\n" -#ifdef ISC_PLATFORM_USETHREADS - "CPUs found: %u\n" - "worker threads: %u\n" - "UDP listeners per interface: %u\n" -#endif - "number of zones: %u (%u automatic)\n" - "debug level: %d\n" - "xfers running: %u\n" - "xfers deferred: %u\n" - "soa queries in progress: %u\n" - "query logging is %s\n" - "recursive clients: %d/%d/%d\n" - "tcp clients: %d/%d\n" - "server is up and running", - ns_g_version, ob, alt, cb, ns_g_srcid, - boottime, configtime, + snprintf(line, sizeof(line), "version: %s%s%s%s \n", + ns_g_version, ob, alt, cb, ns_g_srcid); + CHECK(putstr(text, line)); + + snprintf(line, sizeof(line), "boot time: %s\n", boottime); + CHECK(putstr(text, line)); + + snprintf(line, sizeof(line), "last configured: %s\n", configtime); + CHECK(putstr(text, line)); + #ifdef ISC_PLATFORM_USETHREADS - ns_g_cpus_detected, ns_g_cpus, ns_g_udpdisp, + snprintf(line, sizeof(line), "CPUs found: %u\n", ns_g_cpus_detected); + CHECK(putstr(text, line)); + + snprintf(line, sizeof(line), "worker threads: %u\n", ns_g_cpus); + CHECK(putstr(text, line)); + + snprintf(line, sizeof(line), "UDP listeners per interface: %u\n", + ns_g_udpdisp); + CHECK(putstr(text, line)); + #endif - zonecount, automatic, ns_g_debuglevel, xferrunning, - xferdeferred, soaqueries, - server->log_queries ? "ON" : "OFF", + snprintf(line, sizeof(line), "number of zones: %u (%u automatic)\n", + zonecount, automatic); + CHECK(putstr(text, line)); + + snprintf(line, sizeof(line), "debug level: %d\n", ns_g_debuglevel); + CHECK(putstr(text, line)); + + snprintf(line, sizeof(line), "xfers running: %u\n", xferrunning); + CHECK(putstr(text, line)); + + snprintf(line, sizeof(line), "xfers deferred: %u\n", xferdeferred); + CHECK(putstr(text, line)); + + snprintf(line, sizeof(line), "soa queries in progress: %u\n", + soaqueries); + CHECK(putstr(text, line)); + + snprintf(line, sizeof(line), "query logging is %s\n", + server->log_queries ? "ON" : "OFF"); + CHECK(putstr(text, line)); + + snprintf(line, sizeof(line), "recursive clients: %d/%d/%d\n", server->recursionquota.used, server->recursionquota.soft, - server->recursionquota.max, + server->recursionquota.max); + CHECK(putstr(text, line)); + + snprintf(line, sizeof(line), "tcp clients: %d/%d\n", server->tcpquota.used, server->tcpquota.max); - if (n >= isc_buffer_availablelength(text)) - return (ISC_R_NOSPACE); - isc_buffer_add(text, n); + CHECK(putstr(text, line)); + + CHECK(putstr(text, "server is up and running")); + CHECK(putnull(text)); + return (ISC_R_SUCCESS); + cleanup: + return (result); +} + +isc_result_t +ns_server_testgen(char *args, isc_buffer_t **text) { + isc_result_t result; + char *ptr; + unsigned long count; + unsigned long i; + const unsigned char chars[] = "abcdefghijklmnopqrstuvwxyz0123456789"; + + /* Skip the command name. */ + ptr = next_token(&args, " \t"); + if (ptr == NULL) + return (ISC_R_UNEXPECTEDEND); + + ptr = next_token(&args, " \t"); + if (ptr == NULL) + count = 26; + else + count = strtoul(ptr, NULL, 10); + + CHECK(isc_buffer_reserve(text, count)); + for (i = 0; i < count; i++) + CHECK(putuint8(text, chars[i % (sizeof(chars) - 1)])); + + CHECK(putnull(text)); + + cleanup: + return (result); } static isc_result_t @@ -8410,13 +8458,12 @@ delete_keynames(dns_tsig_keyring_t *ring, char *target, } isc_result_t -ns_server_tsigdelete(ns_server_t *server, char *command, isc_buffer_t *text) { +ns_server_tsigdelete(ns_server_t *server, char *command, isc_buffer_t **text) { isc_result_t result; - unsigned int n; dns_view_t *view; unsigned int foundkeys = 0; - char *target; - char *viewname; + char *target, *viewname; + char fbuf[16]; (void)next_token(&command, " \t"); /* skip command name */ target = next_token(&command, " \t"); @@ -8443,19 +8490,19 @@ ns_server_tsigdelete(ns_server_t *server, char *command, isc_buffer_t *text) { } isc_task_endexclusive(server->task); - n = snprintf((char *)isc_buffer_used(text), - isc_buffer_availablelength(text), - "%d tsig keys deleted.\n", foundkeys); - if (n >= isc_buffer_availablelength(text)) - return (ISC_R_NOSPACE); - isc_buffer_add(text, n); + snprintf(fbuf, sizeof(fbuf), "%d", foundkeys); - return (ISC_R_SUCCESS); + CHECK(putstr(text, fbuf)); + CHECK(putstr(text, " tsig keys deleted.")); + CHECK(putnull(text)); + + cleanup: + return (result); } static isc_result_t -list_keynames(dns_view_t *view, dns_tsig_keyring_t *ring, isc_buffer_t *text, - unsigned int *foundkeys) +list_keynames(dns_view_t *view, dns_tsig_keyring_t *ring, isc_buffer_t **text, + unsigned int *foundkeys) { char namestr[DNS_NAME_FORMATSIZE]; char creatorstr[DNS_NAME_FORMATSIZE]; @@ -8466,7 +8513,6 @@ list_keynames(dns_view_t *view, dns_tsig_keyring_t *ring, isc_buffer_t *text, dns_name_t *origin; dns_rbtnode_t *node; dns_tsigkey_t *tkey; - unsigned int n; const char *viewname; if (view != NULL) @@ -8495,43 +8541,46 @@ list_keynames(dns_view_t *view, dns_tsig_keyring_t *ring, isc_buffer_t *text, tkey = node->data; if (tkey != NULL) { - (*foundkeys)++; dns_name_format(&tkey->name, namestr, sizeof(namestr)); if (tkey->generated) { dns_name_format(tkey->creator, creatorstr, sizeof(creatorstr)); - n = snprintf((char *)isc_buffer_used(text), - isc_buffer_availablelength(text), - "view \"%s\"; type \"dynamic\"; key \"%s\"; creator \"%s\";\n", - viewname, namestr, creatorstr); + if (*foundkeys != 0) + putstr(text, "\n"); + CHECK(putstr(text, "view \"")); + CHECK(putstr(text, viewname)); + CHECK(putstr(text, + "\"; type \"dynamic\"; key \"")); + CHECK(putstr(text, namestr)); + CHECK(putstr(text, "\"; creator \"")); + CHECK(putstr(text, creatorstr)); + CHECK(putstr(text, "\";")); } else { - n = snprintf((char *)isc_buffer_used(text), - isc_buffer_availablelength(text), - "view \"%s\"; type \"static\"; key \"%s\";\n", - viewname, namestr); - } - if (n >= isc_buffer_availablelength(text)) { - dns_rbtnodechain_invalidate(&chain); - return (ISC_R_NOSPACE); + if (*foundkeys != 0) + putstr(text, "\n"); + CHECK(putstr(text, "view \"")); + CHECK(putstr(text, viewname)); + CHECK(putstr(text, + "\"; type \"static\"; key \"")); + CHECK(putstr(text, namestr)); + CHECK(putstr(text, "\";")); } - isc_buffer_add(text, n); + (*foundkeys)++; } result = dns_rbtnodechain_next(&chain, &foundname, origin); - if (result == ISC_R_NOMORE) + if (result == ISC_R_NOMORE || result == DNS_R_NEWORIGIN) break; - if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) { - dns_rbtnodechain_invalidate(&chain); - return (result); - } } return (ISC_R_SUCCESS); + cleanup: + dns_rbtnodechain_invalidate(&chain); + return (result); } isc_result_t -ns_server_tsiglist(ns_server_t *server, isc_buffer_t *text) { +ns_server_tsiglist(ns_server_t *server, isc_buffer_t **text) { isc_result_t result; - unsigned int n; dns_view_t *view; unsigned int foundkeys = 0; @@ -8559,14 +8608,11 @@ ns_server_tsiglist(ns_server_t *server, isc_buffer_t *text) { } isc_task_endexclusive(server->task); - if (foundkeys == 0) { - n = snprintf((char *)isc_buffer_used(text), - isc_buffer_availablelength(text), - "no tsig keys found.\n"); - if (n >= isc_buffer_availablelength(text)) - return (ISC_R_NOSPACE); - isc_buffer_add(text, n); - } + if (foundkeys == 0) + putstr(text, "no tsig keys found."); + + if (isc_buffer_usedlength(*text) > 0) + putnull(text); return (ISC_R_SUCCESS); } @@ -8575,7 +8621,7 @@ ns_server_tsiglist(ns_server_t *server, isc_buffer_t *text) { * Act on a "sign" or "loadkeys" command from the command channel. */ isc_result_t -ns_server_rekey(ns_server_t *server, char *args, isc_buffer_t *text) { +ns_server_rekey(ns_server_t *server, char *args, isc_buffer_t **text) { isc_result_t result; dns_zone_t *zone = NULL; dns_zonetype_t type; @@ -8641,7 +8687,7 @@ synczone(dns_zone_t *zone, void *uap) { } isc_result_t -ns_server_sync(ns_server_t *server, char *args, isc_buffer_t *text) { +ns_server_sync(ns_server_t *server, char *args, isc_buffer_t **text) { isc_result_t result, tresult; dns_view_t *view; dns_zone_t *zone = NULL; @@ -8691,9 +8737,10 @@ ns_server_sync(ns_server_t *server, char *args, isc_buffer_t *text) { result = synczone(zone, &cleanup); isc_task_endexclusive(server->task); - if (msg != NULL && strlen(msg) < isc_buffer_availablelength(text)) - isc_buffer_putmem(text, (const unsigned char *)msg, - strlen(msg) + 1); + if (msg != NULL) { + putstr(text, msg); + putnull(text); + } view = dns_zone_getview(zone); if (strcmp(view->name, "_default") == 0 || @@ -8724,7 +8771,7 @@ ns_server_sync(ns_server_t *server, char *args, isc_buffer_t *text) { */ isc_result_t ns_server_freeze(ns_server_t *server, isc_boolean_t freeze, char *args, - isc_buffer_t *text) + isc_buffer_t **text) { isc_result_t result, tresult; dns_zone_t *zone = NULL, *raw = NULL; @@ -8815,9 +8862,10 @@ ns_server_freeze(ns_server_t *server, isc_boolean_t freeze, char *args, } isc_task_endexclusive(server->task); - if (msg != NULL && strlen(msg) < isc_buffer_availablelength(text)) - isc_buffer_putmem(text, (const unsigned char *)msg, - strlen(msg) + 1); + if (msg != NULL) { + putstr(text, msg); + putnull(text); + } view = dns_zone_getview(zone); if (strcmp(view->name, "_default") == 0 || @@ -8849,16 +8897,8 @@ ns_server_freeze(ns_server_t *server, isc_boolean_t freeze, char *args, * is managed by smf and is also running chroot. */ isc_result_t -ns_smf_add_message(isc_buffer_t *text) { - unsigned int n; - - n = snprintf((char *)isc_buffer_used(text), - isc_buffer_availablelength(text), - "use svcadm(1M) to manage named"); - if (n >= isc_buffer_availablelength(text)) - return (ISC_R_NOSPACE); - isc_buffer_add(text, n); - return (ISC_R_SUCCESS); +ns_smf_add_message(isc_buffer_t **text) { + return (putstr(text, "use svcadm(1M) to manage named")); } #endif /* HAVE_LIBSCF */ @@ -8883,7 +8923,7 @@ add_comment(FILE *fp, const char *viewname) { * Act on an "addzone" command from the command channel. */ isc_result_t -ns_server_add_zone(ns_server_t *server, char *args, isc_buffer_t *text) { +ns_server_add_zone(ns_server_t *server, char *args, isc_buffer_t **text) { isc_result_t result, tresult; isc_buffer_t argbuf; size_t arglen; @@ -9042,7 +9082,7 @@ ns_server_add_zone(ns_server_t *server, char *args, isc_buffer_t *text) { /* Emit the zone name, quoted and escaped */ isc_buffer_init(&buf, namebuf, sizeof(namebuf)); CHECK(dns_name_totext(dnsname, ISC_TRUE, &buf)); - putnull(&buf); + isc_buffer_putuint8(&buf, 0); CHECK(isc_stdio_write("zone \"", 6, 1, fp, NULL)); CHECK(isc_stdio_write(namebuf, strlen(namebuf), 1, fp, NULL)); CHECK(isc_stdio_write("\" ", 2, 1, fp, NULL)); @@ -9081,7 +9121,7 @@ ns_server_add_zone(ns_server_t *server, char *args, isc_buffer_t *text) { result = ISC_R_SUCCESS; cleanup: - if (isc_buffer_usedlength(text) > 0) + if (isc_buffer_usedlength(*text) > 0) putnull(text); if (fp != NULL) isc_stdio_close(fp); @@ -9099,18 +9139,16 @@ ns_server_add_zone(ns_server_t *server, char *args, isc_buffer_t *text) { } static isc_boolean_t -inuse(const char* file, isc_boolean_t first, isc_buffer_t *text) { +inuse(const char* file, isc_boolean_t first, isc_buffer_t **text) { #define INUSEMSG "The following files were in use and may now be removed:\n" - if (file != NULL && isc_file_exists(file) && - isc_buffer_availablelength(text) > - strlen(file) + (first ? sizeof(INUSEMSG) : sizeof("\n"))) - { + if (file != NULL && isc_file_exists(file)) { if (first) (void) putstr(text, INUSEMSG); else (void) putstr(text, "\n"); (void) putstr(text, file); + putnull(text); return (ISC_FALSE); } return (first); @@ -9120,7 +9158,7 @@ inuse(const char* file, isc_boolean_t first, isc_buffer_t *text) { * Act on a "delzone" command from the command channel. */ isc_result_t -ns_server_del_zone(ns_server_t *server, char *args, isc_buffer_t *text) { +ns_server_del_zone(ns_server_t *server, char *args, isc_buffer_t **text) { isc_result_t result; dns_zone_t *zone = NULL; dns_zone_t *raw = NULL; @@ -9346,7 +9384,7 @@ ns_server_del_zone(ns_server_t *server, char *args, isc_buffer_t *text) { result = ISC_R_SUCCESS; cleanup: - if (isc_buffer_usedlength(text) > 0) + if (isc_buffer_usedlength(*text) > 0) putnull(text); if (exclusive) isc_task_endexclusive(server->task); @@ -9429,7 +9467,7 @@ generate_salt(unsigned char *salt, size_t saltlen) { } isc_result_t -ns_server_signing(ns_server_t *server, char *args, isc_buffer_t *text) { +ns_server_signing(ns_server_t *server, char *args, isc_buffer_t **text) { isc_result_t result = ISC_R_SUCCESS; dns_zone_t *zone = NULL; dns_name_t *origin; @@ -9576,21 +9614,13 @@ ns_server_signing(ns_server_t *server, char *args, isc_buffer_t *text) { isc_buffer_init(&buf, output, sizeof(output)); CHECK(dns_private_totext(&priv, &buf)); - if (!first) - putstr(text, "\n"); + CHECK(putstr(text, "\n")); + CHECK(putstr(text, output)); first = ISC_FALSE; - - n = snprintf((char *)isc_buffer_used(text), - isc_buffer_availablelength(text), - "%s", output); - if (n >= isc_buffer_availablelength(text)) - CHECK(ISC_R_NOSPACE); - - isc_buffer_add(text, (unsigned int)n); } if (!first) - putnull(text); + CHECK(putnull(text)); if (result == ISC_R_NOMORE) result = ISC_R_SUCCESS; @@ -9612,30 +9642,41 @@ ns_server_signing(ns_server_t *server, char *args, isc_buffer_t *text) { } static isc_result_t -putstr(isc_buffer_t *b, const char *str) { - unsigned int l = strlen(str); +putmem(isc_buffer_t **b, const char *str, size_t len) { + isc_result_t result; - /* - * Use >= to leave space for NUL termination. - */ - if (l >= isc_buffer_availablelength(b)) + result = isc_buffer_reserve(b, len); + if (result != ISC_R_SUCCESS) return (ISC_R_NOSPACE); - isc_buffer_putmem(b, (const unsigned char *)str, l); + isc_buffer_putmem(*b, (const unsigned char *)str, len); return (ISC_R_SUCCESS); } +static inline isc_result_t +putstr(isc_buffer_t **b, const char *str) { + return (putmem(b, str, strlen(str))); +} + static isc_result_t -putnull(isc_buffer_t *b) { - if (isc_buffer_availablelength(b) == 0) +putuint8(isc_buffer_t **b, isc_uint8_t val) { + isc_result_t result; + + result = isc_buffer_reserve(b, 1); + if (result != ISC_R_SUCCESS) return (ISC_R_NOSPACE); - isc_buffer_putuint8(b, 0); + isc_buffer_putuint8(*b, val); return (ISC_R_SUCCESS); } +static inline isc_result_t +putnull(isc_buffer_t **b) { + return (putuint8(b, 0)); +} + isc_result_t -ns_server_zonestatus(ns_server_t *server, char *args, isc_buffer_t *text) { +ns_server_zonestatus(ns_server_t *server, char *args, isc_buffer_t **text) { isc_result_t result = ISC_R_SUCCESS; dns_zone_t *zone = NULL, *raw = NULL; const char *type, *file, *zonename = NULL; @@ -9907,7 +9948,7 @@ argcheck(char *cmd, const char *full) { } isc_result_t -ns_server_nta(ns_server_t *server, char *args, isc_buffer_t *text) { +ns_server_nta(ns_server_t *server, char *args, isc_buffer_t **text) { dns_view_t *view; dns_ntatable_t *ntatable = NULL; isc_result_t result = ISC_R_SUCCESS; @@ -10083,10 +10124,7 @@ ns_server_nta(ns_server_t *server, char *args, isc_buffer_t *text) { nametext, view->name); } - if (isc_buffer_availablelength(text) == 0) - return (ISC_R_NOSPACE); - - isc_buffer_putuint8(text, 0); + CHECK(putnull(text)); } cleanup: diff --git a/bin/rndc/rndc.c b/bin/rndc/rndc.c index 6e8367cee43..ab2973df572 100644 --- a/bin/rndc/rndc.c +++ b/bin/rndc/rndc.c @@ -73,7 +73,7 @@ static int nserveraddrs; static int currentaddr = 0; static unsigned int remoteport = 0; static isc_socketmgr_t *socketmgr = NULL; -static unsigned char databuf[2048]; +static isc_buffer_t *databuf; static isccc_ccmsg_t ccmsg; static isc_uint32_t algorithm; static isccc_region_t secret; @@ -306,8 +306,6 @@ rndc_recvnonce(isc_task_t *task, isc_event_t *event) { isccc_time_t now; isc_region_t r; isccc_sexpr_t *data; - isccc_region_t message; - isc_uint32_t len; isc_buffer_t b; recvs--; @@ -354,15 +352,19 @@ rndc_recvnonce(isc_task_t *task, isc_event_t *event) { if (isccc_cc_defineuint32(_ctrl, "_nonce", nonce) == NULL) fatal("out of memory"); } - message.rstart = databuf + 4; - message.rend = databuf + sizeof(databuf); + + isc_buffer_clear(databuf); + /* Skip the length field (4 bytes) */ + isc_buffer_add(databuf, 4); + DO("render message", - isccc_cc_towire(request, &message, algorithm, &secret)); - len = sizeof(databuf) - REGION_SIZE(message); - isc_buffer_init(&b, databuf, 4); - isc_buffer_putuint32(&b, len - 4); - r.length = len; - r.base = databuf; + isccc_cc_towire(request, &databuf, algorithm, &secret)); + + isc_buffer_init(&b, databuf->base, 4); + isc_buffer_putuint32(&b, databuf->used - 4); + + r.base = databuf->base; + r.length = databuf->used; isccc_ccmsg_cancelread(&ccmsg); DO("schedule recv", isccc_ccmsg_readmessage(&ccmsg, task, @@ -384,9 +386,7 @@ rndc_connected(isc_task_t *task, isc_event_t *event) { isccc_sexpr_t *request = NULL; isccc_sexpr_t *data; isccc_time_t now; - isccc_region_t message; isc_region_t r; - isc_uint32_t len; isc_buffer_t b; isc_result_t result; @@ -417,15 +417,19 @@ rndc_connected(isc_task_t *task, isc_event_t *event) { fatal("_data section missing"); if (isccc_cc_definestring(data, "type", "null") == NULL) fatal("out of memory"); - message.rstart = databuf + 4; - message.rend = databuf + sizeof(databuf); + + isc_buffer_clear(databuf); + /* Skip the length field (4 bytes) */ + isc_buffer_add(databuf, 4); + DO("render message", - isccc_cc_towire(request, &message, algorithm, &secret)); - len = sizeof(databuf) - REGION_SIZE(message); - isc_buffer_init(&b, databuf, 4); - isc_buffer_putuint32(&b, len - 4); - r.length = len; - r.base = databuf; + isccc_cc_towire(request, &databuf, algorithm, &secret)); + + isc_buffer_init(&b, databuf->base, 4); + isc_buffer_putuint32(&b, databuf->used - 4); + + r.base = databuf->base; + r.length = databuf->used; isccc_ccmsg_init(mctx, sock, &ccmsg); isccc_ccmsg_setmaxsize(&ccmsg, 1024 * 1024); @@ -875,6 +879,9 @@ main(int argc, char **argv) { command = *argv; + DO("allocate data buffer", + isc_buffer_allocate(mctx, &databuf, 2048)); + /* * Convert argc/argv into a space-delimited command string * similar to what the user might enter in interactive mode @@ -931,6 +938,8 @@ main(int argc, char **argv) { dns_name_destroy(); + isc_buffer_free(&databuf); + if (show_final_mem) isc_mem_stats(mctx, stderr); diff --git a/bin/tests/system/Makefile.in b/bin/tests/system/Makefile.in index a4ce2fd1e9c..0bf725ec521 100644 --- a/bin/tests/system/Makefile.in +++ b/bin/tests/system/Makefile.in @@ -21,7 +21,7 @@ top_srcdir = @top_srcdir@ @BIND9_MAKE_INCLUDES@ -SUBDIRS = builtin dlzexternal filter-aaaa geoip lwresd resolver rpz \ +SUBDIRS = builtin dlzexternal filter-aaaa geoip lwresd resolver rndc rpz \ rsabigexponent tkey tsiggss TARGETS = diff --git a/bin/tests/system/rndc/Makefile.in b/bin/tests/system/rndc/Makefile.in new file mode 100644 index 00000000000..787d35a23cc --- /dev/null +++ b/bin/tests/system/rndc/Makefile.in @@ -0,0 +1,55 @@ +# Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC") +# +# Permission to use, copy, modify, and/or 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$ + + +srcdir = @srcdir@ +VPATH = @srcdir@ +top_srcdir = @top_srcdir@ + +@BIND9_VERSION@ + +@BIND9_MAKE_INCLUDES@ + +CINCLUDES = + +CDEFINES = +CWARNINGS = + +DNSLIBS = +ISCLIBS = . + +DNSDEPLIBS = +ISCDEPLIBS = + +DEPLIBS = + +LIBS = @LIBS@ + +TARGETS = gencheck@EXEEXT@ + +GENCHECKOBJS = gencheck.@O@ + +SRCS = gencheck.c + +@BIND9_MAKE_RULES@ + +all: gencheck@EXEEXT@ + +gencheck@EXEEXT@: ${GENCHECKOBJS} + ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ ${GENCHECKOBJS} ${LIBS} + +clean distclean:: + rm -f ${TARGETS} diff --git a/bin/tests/system/rndc/clean.sh b/bin/tests/system/rndc/clean.sh index a7b321637e3..ca307a63938 100644 --- a/bin/tests/system/rndc/clean.sh +++ b/bin/tests/system/rndc/clean.sh @@ -22,3 +22,4 @@ rm -f ns*/named.memstats rm -f ns*/named.run rm -f ns4/*.conf rm -f rndc.status +rm -f rndc.output diff --git a/bin/tests/system/rndc/gencheck.c b/bin/tests/system/rndc/gencheck.c new file mode 100644 index 00000000000..d72078782a4 --- /dev/null +++ b/bin/tests/system/rndc/gencheck.c @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC") + * + * Permission to use, copy, modify, and/or 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. + */ + +#include + +#include +#include +#include + +#include +#include +#include + +#define USAGE "usage: gencheck \n" + +static int +check(const char *buf, ssize_t count, size_t *start) { + const char chars[] = "abcdefghijklmnopqrstuvwxyz0123456789"; + ssize_t i; + + for (i = 0; i < count; i++, *start = (*start + 1) % (sizeof(chars) - 1)) { + /* Just ignore the trailing newline */ + if (buf[i] == '\n') + continue; + if (buf[i] != chars[*start]) + return 0; + } + + return 1; +} + +int +main(int argc, char **argv) +{ + int ret; + int fd; + ssize_t count; + char buf[1024]; + size_t start; + size_t length; + + ret = EXIT_FAILURE; + fd = -1; + length = 0; + + if (argc != 2) { + fputs(USAGE, stderr); + goto out; + } + + fd = open(argv[1], O_RDONLY); + if (fd == -1) + goto out; + + start = 0; + while ((count = read(fd, buf, sizeof(buf))) != 0) { + if (count < 0) + goto out; + + if (!check(buf, count, &start)) + goto out; + + length += count; + } + + ret = EXIT_SUCCESS; + + out: + printf("%zu\n", length); + + if (fd != -1) + close(fd); + + return (ret); +} diff --git a/bin/tests/system/rndc/tests.sh b/bin/tests/system/rndc/tests.sh index 8cc8992c149..e0163b81ad4 100644 --- a/bin/tests/system/rndc/tests.sh +++ b/bin/tests/system/rndc/tests.sh @@ -384,5 +384,24 @@ $RNDC -s 10.53.0.4 -p 9956 -c ns4/key6.conf nta -l 2h nta1.example 2>&1 | grep " $RNDC -s 10.53.0.4 -p 9956 -c ns4/key6.conf nta -l 1d nta2.example 2>&1 | grep "Negative trust anchor added" > /dev/null || ret=1 $RNDC -s 10.53.0.4 -p 9956 -c ns4/key6.conf nta -l 1w nta3.example 2>&1 | grep "Negative trust anchor added" > /dev/null || ret=1 $RNDC -s 10.53.0.4 -p 9956 -c ns4/key6.conf nta -l 8d nta4.example 2>&1 | grep "NTA lifetime cannot exceed one week" > /dev/null || ret=1 -echo "I:exit status: $status" +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +for i in 512 1024 2048 4096 8192 16384 32768 65536 131072 262144 524288 +do + echo "I:testing rndc buffer size limits (size=${i})" + ret=0 + $RNDC -s 10.53.0.4 -p 9956 -c ns4/key6.conf testgen ${i} 2>&1 > rndc.output || ret=1 + actual_size=`./gencheck rndc.output` + if [ "$?" = "0" ]; then + expected_size=`expr $i + 1` + if [ $actual_size != $expected_size ]; then ret=1; fi + else + ret=1 + fi + + if [ $ret != 0 ]; then echo "I:failed"; fi + status=`expr $status + $ret` +done + exit $status diff --git a/configure b/configure index e4f4987819f..d8c45204b5d 100755 --- a/configure +++ b/configure @@ -21724,7 +21724,7 @@ ac_config_commands="$ac_config_commands chmod" # elsewhere if there's a good reason for doing so. # -ac_config_files="$ac_config_files make/Makefile make/mkdep Makefile bin/Makefile bin/check/Makefile bin/confgen/Makefile bin/confgen/unix/Makefile bin/delv/Makefile bin/dig/Makefile bin/dnssec/Makefile bin/named/Makefile bin/named/unix/Makefile bin/nsupdate/Makefile bin/pkcs11/Makefile bin/python/Makefile bin/python/dnssec-checkds.py bin/python/dnssec-coverage.py bin/rndc/Makefile bin/tests/Makefile bin/tests/atomic/Makefile bin/tests/db/Makefile bin/tests/dst/Makefile bin/tests/dst/Kdh.+002+18602.key bin/tests/dst/Kdh.+002+18602.private bin/tests/dst/Kdh.+002+48957.key bin/tests/dst/Kdh.+002+48957.private bin/tests/dst/Ktest.+001+00002.key bin/tests/dst/Ktest.+001+54622.key bin/tests/dst/Ktest.+001+54622.private bin/tests/dst/Ktest.+003+23616.key bin/tests/dst/Ktest.+003+23616.private bin/tests/dst/Ktest.+003+49667.key bin/tests/dst/dst_2_data bin/tests/dst/t2_data_1 bin/tests/dst/t2_data_2 bin/tests/dst/t2_dsasig bin/tests/dst/t2_rsasig bin/tests/hashes/Makefile bin/tests/headerdep_test.sh bin/tests/master/Makefile bin/tests/mem/Makefile bin/tests/names/Makefile bin/tests/net/Makefile bin/tests/pkcs11/Makefile bin/tests/pkcs11/benchmarks/Makefile bin/tests/rbt/Makefile bin/tests/resolver/Makefile bin/tests/sockaddr/Makefile bin/tests/system/Makefile bin/tests/system/builtin/Makefile bin/tests/system/conf.sh bin/tests/system/dlz/prereq.sh bin/tests/system/dlzexternal/Makefile bin/tests/system/dlzexternal/ns1/named.conf bin/tests/system/filter-aaaa/Makefile bin/tests/system/geoip/Makefile bin/tests/system/inline/checkdsa.sh bin/tests/system/lwresd/Makefile bin/tests/system/resolver/Makefile bin/tests/system/rpz/Makefile bin/tests/system/rsabigexponent/Makefile bin/tests/system/sit/prereq.sh bin/tests/system/tkey/Makefile bin/tests/system/tsiggss/Makefile bin/tests/tasks/Makefile bin/tests/timers/Makefile bin/tests/virtual-time/Makefile bin/tests/virtual-time/conf.sh bin/tools/Makefile contrib/scripts/check-secure-delegation.pl contrib/scripts/zone-edit.sh doc/Makefile doc/arm/Makefile doc/doxygen/Doxyfile doc/doxygen/Makefile doc/doxygen/doxygen-input-filter doc/misc/Makefile doc/xsl/Makefile doc/xsl/isc-docbook-chunk.xsl doc/xsl/isc-docbook-html.xsl doc/xsl/isc-docbook-latex.xsl doc/xsl/isc-manpage.xsl doc/xsl/isc-notes-latex.xsl isc-config.sh lib/Makefile lib/bind9/Makefile lib/bind9/include/Makefile lib/bind9/include/bind9/Makefile lib/dns/Makefile lib/dns/include/Makefile lib/dns/include/dns/Makefile lib/dns/include/dst/Makefile lib/dns/tests/Makefile lib/irs/Makefile lib/irs/include/Makefile lib/irs/include/irs/Makefile lib/irs/include/irs/netdb.h lib/irs/include/irs/platform.h lib/isc/$arch/Makefile lib/isc/$arch/include/Makefile lib/isc/$arch/include/isc/Makefile lib/isc/$thread_dir/Makefile lib/isc/$thread_dir/include/Makefile lib/isc/$thread_dir/include/isc/Makefile lib/isc/Makefile lib/isc/include/Makefile lib/isc/include/isc/Makefile lib/isc/include/isc/platform.h lib/isc/include/pk11/Makefile lib/isc/include/pkcs11/Makefile lib/isc/tests/Makefile lib/isc/nls/Makefile lib/isc/unix/Makefile lib/isc/unix/include/Makefile lib/isc/unix/include/isc/Makefile lib/isc/unix/include/pkcs11/Makefile lib/isccc/Makefile lib/isccc/include/Makefile lib/isccc/include/isccc/Makefile lib/isccfg/Makefile lib/isccfg/include/Makefile lib/isccfg/include/isccfg/Makefile lib/lwres/Makefile lib/lwres/include/Makefile lib/lwres/include/lwres/Makefile lib/lwres/include/lwres/netdb.h lib/lwres/include/lwres/platform.h lib/lwres/man/Makefile lib/lwres/tests/Makefile lib/lwres/unix/Makefile lib/lwres/unix/include/Makefile lib/lwres/unix/include/lwres/Makefile lib/tests/Makefile lib/tests/include/Makefile lib/tests/include/tests/Makefile lib/samples/Makefile lib/samples/Makefile-postinstall unit/Makefile unit/unittest.sh" +ac_config_files="$ac_config_files make/Makefile make/mkdep Makefile bin/Makefile bin/check/Makefile bin/confgen/Makefile bin/confgen/unix/Makefile bin/delv/Makefile bin/dig/Makefile bin/dnssec/Makefile bin/named/Makefile bin/named/unix/Makefile bin/nsupdate/Makefile bin/pkcs11/Makefile bin/python/Makefile bin/python/dnssec-checkds.py bin/python/dnssec-coverage.py bin/rndc/Makefile bin/tests/Makefile bin/tests/atomic/Makefile bin/tests/db/Makefile bin/tests/dst/Makefile bin/tests/dst/Kdh.+002+18602.key bin/tests/dst/Kdh.+002+18602.private bin/tests/dst/Kdh.+002+48957.key bin/tests/dst/Kdh.+002+48957.private bin/tests/dst/Ktest.+001+00002.key bin/tests/dst/Ktest.+001+54622.key bin/tests/dst/Ktest.+001+54622.private bin/tests/dst/Ktest.+003+23616.key bin/tests/dst/Ktest.+003+23616.private bin/tests/dst/Ktest.+003+49667.key bin/tests/dst/dst_2_data bin/tests/dst/t2_data_1 bin/tests/dst/t2_data_2 bin/tests/dst/t2_dsasig bin/tests/dst/t2_rsasig bin/tests/hashes/Makefile bin/tests/headerdep_test.sh bin/tests/master/Makefile bin/tests/mem/Makefile bin/tests/names/Makefile bin/tests/net/Makefile bin/tests/pkcs11/Makefile bin/tests/pkcs11/benchmarks/Makefile bin/tests/rbt/Makefile bin/tests/resolver/Makefile bin/tests/sockaddr/Makefile bin/tests/system/Makefile bin/tests/system/builtin/Makefile bin/tests/system/conf.sh bin/tests/system/dlz/prereq.sh bin/tests/system/dlzexternal/Makefile bin/tests/system/dlzexternal/ns1/named.conf bin/tests/system/filter-aaaa/Makefile bin/tests/system/geoip/Makefile bin/tests/system/inline/checkdsa.sh bin/tests/system/lwresd/Makefile bin/tests/system/resolver/Makefile bin/tests/system/rndc/Makefile bin/tests/system/rpz/Makefile bin/tests/system/rsabigexponent/Makefile bin/tests/system/sit/prereq.sh bin/tests/system/tkey/Makefile bin/tests/system/tsiggss/Makefile bin/tests/tasks/Makefile bin/tests/timers/Makefile bin/tests/virtual-time/Makefile bin/tests/virtual-time/conf.sh bin/tools/Makefile contrib/scripts/check-secure-delegation.pl contrib/scripts/zone-edit.sh doc/Makefile doc/arm/Makefile doc/doxygen/Doxyfile doc/doxygen/Makefile doc/doxygen/doxygen-input-filter doc/misc/Makefile doc/xsl/Makefile doc/xsl/isc-docbook-chunk.xsl doc/xsl/isc-docbook-html.xsl doc/xsl/isc-docbook-latex.xsl doc/xsl/isc-manpage.xsl doc/xsl/isc-notes-latex.xsl isc-config.sh lib/Makefile lib/bind9/Makefile lib/bind9/include/Makefile lib/bind9/include/bind9/Makefile lib/dns/Makefile lib/dns/include/Makefile lib/dns/include/dns/Makefile lib/dns/include/dst/Makefile lib/dns/tests/Makefile lib/irs/Makefile lib/irs/include/Makefile lib/irs/include/irs/Makefile lib/irs/include/irs/netdb.h lib/irs/include/irs/platform.h lib/isc/$arch/Makefile lib/isc/$arch/include/Makefile lib/isc/$arch/include/isc/Makefile lib/isc/$thread_dir/Makefile lib/isc/$thread_dir/include/Makefile lib/isc/$thread_dir/include/isc/Makefile lib/isc/Makefile lib/isc/include/Makefile lib/isc/include/isc/Makefile lib/isc/include/isc/platform.h lib/isc/include/pk11/Makefile lib/isc/include/pkcs11/Makefile lib/isc/tests/Makefile lib/isc/nls/Makefile lib/isc/unix/Makefile lib/isc/unix/include/Makefile lib/isc/unix/include/isc/Makefile lib/isc/unix/include/pkcs11/Makefile lib/isccc/Makefile lib/isccc/include/Makefile lib/isccc/include/isccc/Makefile lib/isccfg/Makefile lib/isccfg/include/Makefile lib/isccfg/include/isccfg/Makefile lib/lwres/Makefile lib/lwres/include/Makefile lib/lwres/include/lwres/Makefile lib/lwres/include/lwres/netdb.h lib/lwres/include/lwres/platform.h lib/lwres/man/Makefile lib/lwres/tests/Makefile lib/lwres/unix/Makefile lib/lwres/unix/include/Makefile lib/lwres/unix/include/lwres/Makefile lib/tests/Makefile lib/tests/include/Makefile lib/tests/include/tests/Makefile lib/samples/Makefile lib/samples/Makefile-postinstall unit/Makefile unit/unittest.sh" # @@ -22777,6 +22777,7 @@ do "bin/tests/system/inline/checkdsa.sh") CONFIG_FILES="$CONFIG_FILES bin/tests/system/inline/checkdsa.sh" ;; "bin/tests/system/lwresd/Makefile") CONFIG_FILES="$CONFIG_FILES bin/tests/system/lwresd/Makefile" ;; "bin/tests/system/resolver/Makefile") CONFIG_FILES="$CONFIG_FILES bin/tests/system/resolver/Makefile" ;; + "bin/tests/system/rndc/Makefile") CONFIG_FILES="$CONFIG_FILES bin/tests/system/rndc/Makefile" ;; "bin/tests/system/rpz/Makefile") CONFIG_FILES="$CONFIG_FILES bin/tests/system/rpz/Makefile" ;; "bin/tests/system/rsabigexponent/Makefile") CONFIG_FILES="$CONFIG_FILES bin/tests/system/rsabigexponent/Makefile" ;; "bin/tests/system/sit/prereq.sh") CONFIG_FILES="$CONFIG_FILES bin/tests/system/sit/prereq.sh" ;; diff --git a/configure.in b/configure.in index 59c78162222..271a3b4db8a 100644 --- a/configure.in +++ b/configure.in @@ -4535,6 +4535,7 @@ AC_CONFIG_FILES([ bin/tests/system/inline/checkdsa.sh bin/tests/system/lwresd/Makefile bin/tests/system/resolver/Makefile + bin/tests/system/rndc/Makefile bin/tests/system/rpz/Makefile bin/tests/system/rsabigexponent/Makefile bin/tests/system/sit/prereq.sh diff --git a/doc/arm/notes.xml b/doc/arm/notes.xml index db721d7f9a9..bb06f1e07e5 100644 --- a/doc/arm/notes.xml +++ b/doc/arm/notes.xml @@ -275,6 +275,14 @@ name format is found to exist, it will continue to be used. + + + "rndc" can now return text output of arbitrary size to + the caller. (Prior to this, certain commands such as + "rndc tsig-list" and "rndc zonestatus" could return + truncated output.) + + diff --git a/lib/dns/include/dns/nta.h b/lib/dns/include/dns/nta.h index f6826485a96..1e24f4de326 100644 --- a/lib/dns/include/dns/nta.h +++ b/lib/dns/include/dns/nta.h @@ -199,9 +199,9 @@ dns_ntatable_covered(dns_ntatable_t *ntatable, isc_stdtime_t now, */ isc_result_t -dns_ntatable_totext(dns_ntatable_t *ntatable, isc_buffer_t *buf); +dns_ntatable_totext(dns_ntatable_t *ntatable, isc_buffer_t **buf); /*%< - * Dump the NTA table to buffer 'buf' + * Dump the NTA table to buffer at 'buf' */ isc_result_t diff --git a/lib/dns/nta.c b/lib/dns/nta.c index 414fac1318c..3d78acce2f0 100644 --- a/lib/dns/nta.c +++ b/lib/dns/nta.c @@ -501,7 +501,7 @@ dns_ntatable_covered(dns_ntatable_t *ntatable, isc_stdtime_t now, } isc_result_t -dns_ntatable_totext(dns_ntatable_t *ntatable, isc_buffer_t *buf) { +dns_ntatable_totext(dns_ntatable_t *ntatable, isc_buffer_t **buf) { isc_result_t result; dns_rbtnode_t *node; dns_rbtnodechain_t chain; @@ -539,11 +539,12 @@ dns_ntatable_totext(dns_ntatable_t *ntatable, isc_buffer_t *buf) { n->expiry < now ? "expired" : "expiry", tbuf); first = ISC_FALSE; - if (strlen(obuf) >= isc_buffer_availablelength(buf)) { + result = isc_buffer_reserve(buf, strlen(obuf)); + if (result != ISC_R_SUCCESS) { result = ISC_R_NOSPACE; goto cleanup; - } else - isc_buffer_putstr(buf, obuf); + } + isc_buffer_putstr(*buf, obuf); } result = dns_rbtnodechain_next(&chain, NULL, NULL); if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) { @@ -553,8 +554,9 @@ dns_ntatable_totext(dns_ntatable_t *ntatable, isc_buffer_t *buf) { } } - if (isc_buffer_availablelength(buf) != 0) - isc_buffer_putuint8(buf, 0); + isc_buffer_reserve(buf, 1); + if (isc_buffer_availablelength(*buf) != 0) + isc_buffer_putuint8(*buf, 0); cleanup: dns_rbtnodechain_invalidate(&chain); diff --git a/lib/isc/buffer.c b/lib/isc/buffer.c index 2d15e248575..848888ea631 100644 --- a/lib/isc/buffer.c +++ b/lib/isc/buffer.c @@ -15,8 +15,6 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: buffer.c,v 1.49 2008/09/25 04:02:39 tbox Exp $ */ - /*! \file */ #include @@ -467,6 +465,63 @@ isc_buffer_allocate(isc_mem_t *mctx, isc_buffer_t **dynbuffer, return (ISC_R_SUCCESS); } +isc_result_t +isc_buffer_reallocate(isc_buffer_t **dynbuffer, unsigned int length) { + isc_buffer_t *dbuf; + + REQUIRE(dynbuffer != NULL); + REQUIRE(ISC_BUFFER_VALID(*dynbuffer)); + + if ((*dynbuffer)->length > length) + return (ISC_R_NOSPACE); + + /* + * XXXMUKS: This is far more expensive than plain realloc() as + * it doesn't remap pages, but does ordinary copy. So is + * isc_mem_reallocate(), which has additional issues. + */ + dbuf = isc_mem_get((*dynbuffer)->mctx, length + sizeof(isc_buffer_t)); + if (dbuf == NULL) + return (ISC_R_NOMEMORY); + + memmove(dbuf, *dynbuffer, (*dynbuffer)->length + sizeof(isc_buffer_t)); + isc_mem_put(dbuf->mctx, *dynbuffer, + (*dynbuffer)->length + sizeof(isc_buffer_t)); + + dbuf->base = ((unsigned char *)dbuf) + sizeof(isc_buffer_t); + dbuf->length = length; + + INSIST(ISC_BUFFER_VALID(dbuf)); + + *dynbuffer = dbuf; + + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_buffer_reserve(isc_buffer_t **dynbuffer, unsigned int size) { + isc_uint64_t len; + + REQUIRE(ISC_BUFFER_VALID(*dynbuffer)); + + len = (*dynbuffer)->length; + if ((len - (*dynbuffer)->used) > size) + return (ISC_R_SUCCESS); + + /* Round to nearest buffer size increment */ + len += size; + len = (len + ISC_BUFFER_INCR - 1 - ((len - 1) % ISC_BUFFER_INCR)); + + /* Cap at UINT_MAX */ + if (len > UINT_MAX) { + len = UINT_MAX; + if ((len - (*dynbuffer)->used) < size) + return (ISC_R_NOMEMORY); + } + + return (isc_buffer_reallocate(dynbuffer, (unsigned int) len)); +} + void isc_buffer_free(isc_buffer_t **dynbuffer) { unsigned int real_length; diff --git a/lib/isc/include/isc/buffer.h b/lib/isc/include/isc/buffer.h index f9c0775790e..a6193a53c24 100644 --- a/lib/isc/include/isc/buffer.h +++ b/lib/isc/include/isc/buffer.h @@ -15,8 +15,6 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: buffer.h,v 1.55 2010/12/20 23:47:21 tbox Exp $ */ - #ifndef ISC_BUFFER_H #define ISC_BUFFER_H 1 @@ -127,6 +125,13 @@ ISC_LANG_BEGINDECLS #define ISC_BUFFER_VALID(b) ISC_MAGIC_VALID(b, ISC_BUFFER_MAGIC) /*@}*/ +/*! + * Size granularity for dynamically resizeable buffers; when reserving + * space in a buffer, we round the allocated buffer length up to the + * nearest * multiple of this value. + */ +#define ISC_BUFFER_INCR 2048 + /* * The following macros MUST be used only on valid buffers. It is the * caller's responsibility to ensure this by using the ISC_BUFFER_VALID @@ -205,6 +210,50 @@ isc_buffer_allocate(isc_mem_t *mctx, isc_buffer_t **dynbuffer, *\li Changing the buffer's length field is not permitted. */ +isc_result_t +isc_buffer_reallocate(isc_buffer_t **dynbuffer, unsigned int length); +/*!< + * \brief Reallocate the buffer to be "length" bytes long. The buffer + * pointer may move when you call this function. + * + * Requires: + *\li "dynbuffer" is not NULL. + * + *\li "*dynbuffer" is a valid dynamic buffer. + * + *\li 'length' > current length of buffer. + * + * Returns: + *\li ISC_R_SUCCESS - success + *\li ISC_R_NOMEMORY - no memory available + * + * Ensures: + *\li "*dynbuffer" will be valid on return and will contain all the + * original data. However, the buffer pointer may be moved during + * reallocation. + */ + +isc_result_t +isc_buffer_reserve(isc_buffer_t **dynbuffer, unsigned int size); +/*!< + * \brief Make "size" bytes of space available in the buffer. The buffer + * pointer may move when you call this function. + * + * Requires: + *\li "dynbuffer" is not NULL. + * + *\li "*dynbuffer" is a valid dynamic buffer. + * + * Returns: + *\li ISC_R_SUCCESS - success + *\li ISC_R_NOMEMORY - no memory available + * + * Ensures: + *\li "*dynbuffer" will be valid on return and will contain all the + * original data. However, the buffer pointer may be moved during + * reallocation. + */ + void isc_buffer_free(isc_buffer_t **dynbuffer); /*!< diff --git a/lib/isc/tests/Makefile.in b/lib/isc/tests/Makefile.in index afc672de2fb..8160f8a9a04 100644 --- a/lib/isc/tests/Makefile.in +++ b/lib/isc/tests/Makefile.in @@ -40,7 +40,7 @@ SRCS = isctest.c taskpool_test.c socket_test.c hash_test.c \ sockaddr_test.c symtab_test.c task_test.c queue_test.c \ parse_test.c pool_test.c print_test.c regex_test.c \ socket_test.c safe_test.c time_test.c aes_test.c \ - file_test.c + file_test.c buffer_test.c SUBDIRS = TARGETS = taskpool_test@EXEEXT@ socket_test@EXEEXT@ hash_test@EXEEXT@ \ @@ -49,7 +49,7 @@ TARGETS = taskpool_test@EXEEXT@ socket_test@EXEEXT@ hash_test@EXEEXT@ \ queue_test@EXEEXT@ parse_test@EXEEXT@ pool_test@EXEEXT@ \ print_test@EXEEXT@ regex_test@EXEEXT@ socket_test@EXEEXT@ \ safe_test@EXEEXT@ time_test@EXEEXT@ aes_test@EXEEXT@ \ - file_test@EXEEXT@ + file_test@EXEEXT@ buffer_test@EXEEXT@ @BIND9_MAKE_RULES@ @@ -125,6 +125,10 @@ file_test@EXEEXT@: file_test.@O@ ${ISCDEPLIBS} ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \ file_test.@O@ ${ISCLIBS} ${LIBS} +buffer_test@EXEEXT@: buffer_test.@O@ ${ISCDEPLIBS} + ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \ + buffer_test.@O@ isctest.@O@ ${ISCLIBS} ${LIBS} + unit:: sh ${top_srcdir}/unit/unittest.sh diff --git a/lib/isc/tests/buffer_test.c b/lib/isc/tests/buffer_test.c new file mode 100644 index 00000000000..d1b27834d94 --- /dev/null +++ b/lib/isc/tests/buffer_test.c @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC") + * + * Permission to use, copy, modify, and/or 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. + */ + +#include +#include +#include +#include + +#include + +#include "isctest.h" + +#include +#include + +ATF_TC(isc_buffer_reserve); +ATF_TC_HEAD(isc_buffer_reserve, tc) { + atf_tc_set_md_var(tc, "descr", "reserve space in dynamic buffers"); +} + +ATF_TC_BODY(isc_buffer_reserve, tc) { + isc_result_t result; + isc_buffer_t *b; + + result = isc_test_begin(NULL, ISC_TRUE); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + b = NULL; + result = isc_buffer_allocate(mctx, &b, 1024); + ATF_CHECK_EQ(result, ISC_R_SUCCESS); + ATF_CHECK_EQ(b->length, 1024); + + /* + * 1024 bytes should already be available, so this call does + * nothing. + */ + result = isc_buffer_reserve(&b, 1024); + ATF_CHECK_EQ(result, ISC_R_SUCCESS); + + ATF_CHECK(ISC_BUFFER_VALID(b)); + ATF_CHECK_EQ(b->length, 1024); + + /* + * This call should grow it to 2048 bytes as only 1024 bytes are + * available in the buffer. + */ + result = isc_buffer_reserve(&b, 1025); + ATF_CHECK_EQ(result, ISC_R_SUCCESS); + + ATF_CHECK(ISC_BUFFER_VALID(b)); + ATF_CHECK_EQ(b->length, 2048); + + /* + * 2048 bytes should already be available, so this call does + * nothing. + */ + result = isc_buffer_reserve(&b, 2048); + ATF_CHECK_EQ(result, ISC_R_SUCCESS); + + ATF_CHECK(ISC_BUFFER_VALID(b)); + ATF_CHECK_EQ(b->length, 2048); + + /* + * This call should grow it to 4096 bytes as only 2048 bytes are + * available in the buffer. + */ + result = isc_buffer_reserve(&b, 2049); + ATF_CHECK_EQ(result, ISC_R_SUCCESS); + + ATF_CHECK(ISC_BUFFER_VALID(b)); + ATF_CHECK_EQ(b->length, 4096); + + /* Consume some of the buffer so we can run the next test. */ + isc_buffer_add(b, 4096); + + /* + * This call should fail and leave buffer untouched. + */ + result = isc_buffer_reserve(&b, UINT_MAX); + ATF_CHECK_EQ(result, ISC_R_NOMEMORY); + + ATF_CHECK(ISC_BUFFER_VALID(b)); + ATF_CHECK_EQ(b->length, 4096); + + isc_buffer_free(&b); + + isc_test_end(); +} + +ATF_TC(isc_buffer_reallocate); +ATF_TC_HEAD(isc_buffer_reallocate, tc) { + atf_tc_set_md_var(tc, "descr", "reallocate dynamic buffers"); +} + +ATF_TC_BODY(isc_buffer_reallocate, tc) { + isc_result_t result; + isc_buffer_t *b; + + result = isc_test_begin(NULL, ISC_TRUE); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + b = NULL; + result = isc_buffer_allocate(mctx, &b, 1024); + ATF_CHECK_EQ(result, ISC_R_SUCCESS); + ATF_CHECK_EQ(b->length, 1024); + + result = isc_buffer_reallocate(&b, 512); + ATF_CHECK_EQ(result, ISC_R_NOSPACE); + + ATF_CHECK(ISC_BUFFER_VALID(b)); + ATF_CHECK_EQ(b->length, 1024); + + result = isc_buffer_reallocate(&b, 1536); + ATF_CHECK_EQ(result, ISC_R_SUCCESS); + + ATF_CHECK(ISC_BUFFER_VALID(b)); + ATF_CHECK_EQ(b->length, 1536); + + isc_buffer_free(&b); + + isc_test_end(); +} + +/* + * Main + */ +ATF_TP_ADD_TCS(tp) { + ATF_TP_ADD_TC(tp, isc_buffer_reserve); + ATF_TP_ADD_TC(tp, isc_buffer_reallocate); + return (atf_no_error()); +} diff --git a/lib/isc/win32/libisc.def.in b/lib/isc/win32/libisc.def.in index 1164d9e4758..2dbbfaf0267 100644 --- a/lib/isc/win32/libisc.def.in +++ b/lib/isc/win32/libisc.def.in @@ -159,6 +159,8 @@ isc_buffer_getuint16 isc_buffer_getuint32 isc_buffer_getuint8 isc_buffer_reinit +isc_buffer_reallocate +isc_buffer_reserve isc_bufferlist_availablecount isc_bufferlist_usedcount isc_commandline_parse diff --git a/lib/isccc/cc.c b/lib/isccc/cc.c index 43e309436ef..36d4dffaee6 100644 --- a/lib/isccc/cc.c +++ b/lib/isccc/cc.c @@ -108,84 +108,102 @@ static unsigned char auth_hsha[] = { #define HSHA_LENGTH 88 static isc_result_t -table_towire(isccc_sexpr_t *alist, isccc_region_t *target); +table_towire(isccc_sexpr_t *alist, isc_buffer_t **buffer); static isc_result_t -list_towire(isccc_sexpr_t *alist, isccc_region_t *target); +list_towire(isccc_sexpr_t *alist, isc_buffer_t **buffer); static isc_result_t -value_towire(isccc_sexpr_t *elt, isccc_region_t *target) +value_towire(isccc_sexpr_t *elt, isc_buffer_t **buffer) { unsigned int len; - unsigned char *lenp; isccc_region_t *vr; isc_result_t result; if (isccc_sexpr_binaryp(elt)) { vr = isccc_sexpr_tobinary(elt); len = REGION_SIZE(*vr); - if (REGION_SIZE(*target) < 1 + 4 + len) + result = isc_buffer_reserve(buffer, 1 + 4); + if (result != ISC_R_SUCCESS) return (ISC_R_NOSPACE); - PUT8(ISCCC_CCMSGTYPE_BINARYDATA, target->rstart); - PUT32(len, target->rstart); - if (REGION_SIZE(*target) < len) + isc_buffer_putuint8(*buffer, ISCCC_CCMSGTYPE_BINARYDATA); + isc_buffer_putuint32(*buffer, len); + + result = isc_buffer_reserve(buffer, len); + if (result != ISC_R_SUCCESS) return (ISC_R_NOSPACE); - PUT_MEM(vr->rstart, len, target->rstart); + isc_buffer_putmem(*buffer, vr->rstart, len); } else if (isccc_alist_alistp(elt)) { - if (REGION_SIZE(*target) < 1 + 4) + unsigned int used; + isc_buffer_t b; + + result = isc_buffer_reserve(buffer, 1 + 4); + if (result != ISC_R_SUCCESS) return (ISC_R_NOSPACE); - PUT8(ISCCC_CCMSGTYPE_TABLE, target->rstart); + isc_buffer_putuint8(*buffer, ISCCC_CCMSGTYPE_TABLE); /* * Emit a placeholder length. */ - lenp = target->rstart; - PUT32(0, target->rstart); + used = (*buffer)->used; + isc_buffer_putuint32(*buffer, 0); + /* * Emit the table. */ - result = table_towire(elt, target); + result = table_towire(elt, buffer); if (result != ISC_R_SUCCESS) return (result); - len = (unsigned int)(target->rstart - lenp); + + len = (*buffer)->used - used; /* * 'len' is 4 bytes too big, since it counts - * the placeholder length too. Adjust and + * the placeholder length too. Adjust and * emit. */ INSIST(len >= 4U); len -= 4; - PUT32(len, lenp); + + isc_buffer_init(&b, (unsigned char *) (*buffer)->base + used, 4); + isc_buffer_putuint32(&b, len); } else if (isccc_sexpr_listp(elt)) { - if (REGION_SIZE(*target) < 1 + 4) + unsigned int used; + isc_buffer_t b; + + result = isc_buffer_reserve(buffer, 1 + 4); + if (result != ISC_R_SUCCESS) return (ISC_R_NOSPACE); - PUT8(ISCCC_CCMSGTYPE_LIST, target->rstart); + isc_buffer_putuint8(*buffer, ISCCC_CCMSGTYPE_LIST); /* - * Emit a placeholder length and count. + * Emit a placeholder length. */ - lenp = target->rstart; - PUT32(0, target->rstart); + used = (*buffer)->used; + isc_buffer_putuint32(*buffer, 0); + /* * Emit the list. */ - result = list_towire(elt, target); + result = list_towire(elt, buffer); if (result != ISC_R_SUCCESS) return (result); - len = (unsigned int)(target->rstart - lenp); + + len = (*buffer)->used - used; /* * 'len' is 4 bytes too big, since it counts - * the placeholder length. Adjust and emit. + * the placeholder length too. Adjust and + * emit. */ INSIST(len >= 4U); len -= 4; - PUT32(len, lenp); + + isc_buffer_init(&b, (unsigned char *) (*buffer)->base + used, 4); + isc_buffer_putuint32(&b, len); } return (ISC_R_SUCCESS); } static isc_result_t -table_towire(isccc_sexpr_t *alist, isccc_region_t *target) -{ +table_towire(isccc_sexpr_t *alist, isc_buffer_t **buffer) { isccc_sexpr_t *kv, *elt, *k, *v; char *ks; isc_result_t result; @@ -203,14 +221,15 @@ table_towire(isccc_sexpr_t *alist, isccc_region_t *target) /* * Emit the key name. */ - if (REGION_SIZE(*target) < 1 + len) + result = isc_buffer_reserve(buffer, 1 + len); + if (result != ISC_R_SUCCESS) return (ISC_R_NOSPACE); - PUT8(len, target->rstart); - PUT_MEM(ks, len, target->rstart); + isc_buffer_putuint8(*buffer, len); + isc_buffer_putmem(*buffer, (const unsigned char *) ks, len); /* * Emit the value. */ - result = value_towire(v, target); + result = value_towire(v, buffer); if (result != ISC_R_SUCCESS) return (result); } @@ -219,12 +238,11 @@ table_towire(isccc_sexpr_t *alist, isccc_region_t *target) } static isc_result_t -list_towire(isccc_sexpr_t *list, isccc_region_t *target) -{ +list_towire(isccc_sexpr_t *list, isc_buffer_t **buffer) { isc_result_t result; while (list != NULL) { - result = value_towire(ISCCC_SEXPR_CAR(list), target); + result = value_towire(ISCCC_SEXPR_CAR(list), buffer); if (result != ISC_R_SUCCESS) return (result); list = ISCCC_SEXPR_CDR(list); @@ -324,24 +342,24 @@ sign(unsigned char *data, unsigned int length, unsigned char *hmac, } isc_result_t -isccc_cc_towire(isccc_sexpr_t *alist, isccc_region_t *target, +isccc_cc_towire(isccc_sexpr_t *alist, isc_buffer_t **buffer, isc_uint32_t algorithm, isccc_region_t *secret) { - unsigned char *hmac_rstart, *signed_rstart; + unsigned int hmac_size, signed_size; isc_result_t result; - if (algorithm == ISCCC_ALG_HMACMD5) { - if (REGION_SIZE(*target) < 4 + sizeof(auth_hmd5)) - return (ISC_R_NOSPACE); - } else { - if (REGION_SIZE(*target) < 4 + sizeof(auth_hsha)) - return (ISC_R_NOSPACE); - } + result = isc_buffer_reserve(buffer, + 4 + ((algorithm == ISCCC_ALG_HMACMD5) ? + sizeof(auth_hmd5) : + sizeof(auth_hsha))); + if (result != ISC_R_SUCCESS) + return (ISC_R_NOSPACE); /* * Emit protocol version. */ - PUT32(1, target->rstart); + isc_buffer_putuint32(*buffer, 1); + if (secret != NULL) { /* * Emit _auth section with zeroed HMAC signature. @@ -349,19 +367,21 @@ isccc_cc_towire(isccc_sexpr_t *alist, isccc_region_t *target, * we know what it is. */ if (algorithm == ISCCC_ALG_HMACMD5) { - hmac_rstart = target->rstart + HMD5_OFFSET; - PUT_MEM(auth_hmd5, sizeof(auth_hmd5), target->rstart); + hmac_size = (*buffer)->used + HMD5_OFFSET; + isc_buffer_putmem(*buffer, + auth_hmd5, sizeof(auth_hmd5)); } else { unsigned char *hmac_alg; - hmac_rstart = target->rstart + HSHA_OFFSET; - hmac_alg = hmac_rstart - 1; - PUT_MEM(auth_hsha, sizeof(auth_hsha), target->rstart); - PUT8(algorithm, hmac_alg); + hmac_size = (*buffer)->used + HSHA_OFFSET; + hmac_alg = (unsigned char *) isc_buffer_used(*buffer) + HSHA_OFFSET - 1; + isc_buffer_putmem(*buffer, + auth_hsha, sizeof(auth_hsha)); + *hmac_alg = algorithm; } } else - hmac_rstart = NULL; - signed_rstart = target->rstart; + hmac_size = 0; + signed_size = (*buffer)->used; /* * Delete any existing _auth section so that we don't try * to encode it. @@ -370,13 +390,15 @@ isccc_cc_towire(isccc_sexpr_t *alist, isccc_region_t *target, /* * Emit the message. */ - result = table_towire(alist, target); + result = table_towire(alist, buffer); if (result != ISC_R_SUCCESS) return (result); if (secret != NULL) - return (sign(signed_rstart, - (unsigned int)(target->rstart - signed_rstart), - hmac_rstart, algorithm, secret)); + return (sign((unsigned char *) (*buffer)->base + signed_size, + (*buffer)->used - signed_size, + (hmac_size == 0 ? NULL : + (unsigned char *) (*buffer)->base + hmac_size), + algorithm, secret)); return (ISC_R_SUCCESS); } diff --git a/lib/isccc/include/isccc/cc.h b/lib/isccc/include/isccc/cc.h index 55dc7a0a365..5f7f88e1e53 100644 --- a/lib/isccc/include/isccc/cc.h +++ b/lib/isccc/include/isccc/cc.h @@ -37,6 +37,7 @@ /*! \file isccc/cc.h */ #include +#include #include ISC_LANG_BEGINDECLS @@ -65,7 +66,7 @@ ISC_LANG_BEGINDECLS /*% Send to Wire */ isc_result_t -isccc_cc_towire(isccc_sexpr_t *alist, isccc_region_t *target, +isccc_cc_towire(isccc_sexpr_t *alist, isc_buffer_t **buffer, isc_uint32_t algorithm, isccc_region_t *secret); /*% Get From Wire */