mirror of
https://github.com/isc-projects/bind9.git
synced 2026-02-27 12:02:10 -05:00
The reference counting and isc_timer_attach()/isc_timer_detach()
semantic are actually misleading because it cannot be used under normal
conditions. The usual conditions under which is timer used uses the
object where timer is used as argument to the "timer" itself. This
means that when the caller is using `isc_timer_detach()` it needs the
timer to stop and the isc_timer_detach() does that only if this would be
the last reference. Unfortunately, this also means that if the timer is
attached elsewhere and the timer is fired it will most likely be
use-after-free, because the object used in the timer no longer exists.
Remove the reference counting from the isc_timer unit, remove
isc_timer_attach() function and rename isc_timer_detach() to
isc_timer_destroy() to better reflect how the API needs to be used.
The only caveat is that the already executed event must be destroyed
before the isc_timer_destroy() is called because the timer is no longet
attached to .ev_destroy_arg.
(cherry picked from commit ae01ec2823)
394 lines
9.6 KiB
C
394 lines
9.6 KiB
C
/*
|
|
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
|
*
|
|
* SPDX-License-Identifier: MPL-2.0
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
|
*
|
|
* See the COPYRIGHT file distributed with this work for additional
|
|
* information regarding copyright ownership.
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include <isc/managers.h>
|
|
#include <isc/mem.h>
|
|
#include <isc/print.h>
|
|
#include <isc/socket.h>
|
|
#include <isc/string.h>
|
|
#include <isc/task.h>
|
|
#include <isc/timer.h>
|
|
#include <isc/util.h>
|
|
|
|
isc_mem_t *mctx = NULL;
|
|
isc_nm_t *netmgr = NULL;
|
|
isc_taskmgr_t *taskmgr = NULL;
|
|
|
|
static void
|
|
my_shutdown(isc_task_t *task, isc_event_t *event) {
|
|
char *name = event->ev_arg;
|
|
|
|
printf("shutdown %s (%p)\n", name, task);
|
|
fflush(stdout);
|
|
isc_event_free(&event);
|
|
}
|
|
|
|
static void
|
|
my_send(isc_task_t *task, isc_event_t *event) {
|
|
isc_socket_t *sock;
|
|
isc_socketevent_t *dev;
|
|
|
|
sock = event->ev_sender;
|
|
dev = (isc_socketevent_t *)event;
|
|
|
|
printf("my_send: %s task %p\n\t(sock %p, base %p, length %u, n %u, "
|
|
"result %u)\n",
|
|
(char *)(event->ev_arg), task, sock, dev->region.base,
|
|
dev->region.length, dev->n, dev->result);
|
|
|
|
if (dev->result != ISC_R_SUCCESS) {
|
|
isc_socket_detach(&sock);
|
|
isc_task_shutdown(task);
|
|
}
|
|
|
|
if (dev->region.base != NULL) {
|
|
isc_mem_put(mctx, dev->region.base, dev->region.length);
|
|
}
|
|
|
|
isc_event_free(&event);
|
|
}
|
|
|
|
static void
|
|
my_recv(isc_task_t *task, isc_event_t *event) {
|
|
isc_socket_t *sock;
|
|
isc_socketevent_t *dev;
|
|
isc_region_t region;
|
|
char buf[1024];
|
|
char host[256];
|
|
|
|
sock = event->ev_sender;
|
|
dev = (isc_socketevent_t *)event;
|
|
|
|
printf("Socket %s (sock %p, base %p, length %u, n %u, result %u)\n",
|
|
(char *)(event->ev_arg), sock, dev->region.base,
|
|
dev->region.length, dev->n, dev->result);
|
|
if (dev->address.type.sa.sa_family == AF_INET6) {
|
|
inet_ntop(AF_INET6, &dev->address.type.sin6.sin6_addr, host,
|
|
sizeof(host));
|
|
printf("\tFrom: %s port %d\n", host,
|
|
ntohs(dev->address.type.sin6.sin6_port));
|
|
} else {
|
|
inet_ntop(AF_INET, &dev->address.type.sin.sin_addr, host,
|
|
sizeof(host));
|
|
printf("\tFrom: %s port %d\n", host,
|
|
ntohs(dev->address.type.sin.sin_port));
|
|
}
|
|
|
|
if (dev->result != ISC_R_SUCCESS) {
|
|
isc_socket_detach(&sock);
|
|
|
|
if (dev->region.base != NULL) {
|
|
isc_mem_put(mctx, dev->region.base, dev->region.length);
|
|
}
|
|
isc_event_free(&event);
|
|
|
|
isc_task_shutdown(task);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Echo the data back.
|
|
*/
|
|
if (strcmp(event->ev_arg, "so2") != 0) {
|
|
region = dev->region;
|
|
snprintf(buf, sizeof(buf), "\r\nReceived: %.*s\r\n\r\n",
|
|
(int)dev->n, (char *)region.base);
|
|
region.base = isc_mem_get(mctx, strlen(buf) + 1);
|
|
{
|
|
region.length = strlen(buf) + 1;
|
|
strlcpy((char *)region.base, buf, region.length);
|
|
}
|
|
isc_socket_send(sock, ®ion, task, my_send, event->ev_arg);
|
|
} else {
|
|
region = dev->region;
|
|
printf("\r\nReceived: %.*s\r\n\r\n", (int)dev->n,
|
|
(char *)region.base);
|
|
}
|
|
|
|
isc_socket_recv(sock, &dev->region, 1, task, my_recv, event->ev_arg);
|
|
|
|
isc_event_free(&event);
|
|
}
|
|
|
|
static void
|
|
my_http_get(isc_task_t *task, isc_event_t *event) {
|
|
isc_socket_t *sock;
|
|
isc_socketevent_t *dev;
|
|
|
|
sock = event->ev_sender;
|
|
dev = (isc_socketevent_t *)event;
|
|
|
|
printf("my_http_get: %s task %p\n\t(sock %p, base %p, length %u, "
|
|
"n %u, result %u)\n",
|
|
(char *)(event->ev_arg), task, sock, dev->region.base,
|
|
dev->region.length, dev->n, dev->result);
|
|
|
|
if (dev->result != ISC_R_SUCCESS) {
|
|
isc_socket_detach(&sock);
|
|
isc_task_shutdown(task);
|
|
if (dev->region.base != NULL) {
|
|
isc_mem_put(mctx, dev->region.base, dev->region.length);
|
|
}
|
|
isc_event_free(&event);
|
|
return;
|
|
}
|
|
|
|
isc_socket_recv(sock, &dev->region, 1, task, my_recv, event->ev_arg);
|
|
|
|
isc_event_free(&event);
|
|
}
|
|
|
|
static void
|
|
my_connect(isc_task_t *task, isc_event_t *event) {
|
|
isc_socket_t *sock;
|
|
isc_socket_connev_t *dev;
|
|
isc_region_t region;
|
|
char buf[1024];
|
|
|
|
sock = event->ev_sender;
|
|
dev = (isc_socket_connev_t *)event;
|
|
|
|
printf("%s: Connection result: %u\n", (char *)(event->ev_arg),
|
|
dev->result);
|
|
|
|
if (dev->result != ISC_R_SUCCESS) {
|
|
isc_socket_detach(&sock);
|
|
isc_event_free(&event);
|
|
isc_task_shutdown(task);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Send a GET string, and set up to receive (and just display)
|
|
* the result.
|
|
*/
|
|
snprintf(buf, sizeof(buf),
|
|
"GET / HTTP/1.1\r\nHost: www.flame.org\r\n"
|
|
"Connection: Close\r\n\r\n");
|
|
region.base = isc_mem_get(mctx, strlen(buf) + 1);
|
|
{
|
|
region.length = strlen(buf) + 1;
|
|
strlcpy((char *)region.base, buf, region.length);
|
|
}
|
|
|
|
isc_socket_send(sock, ®ion, task, my_http_get, event->ev_arg);
|
|
|
|
isc_event_free(&event);
|
|
}
|
|
|
|
static void
|
|
my_listen(isc_task_t *task, isc_event_t *event) {
|
|
char *name = event->ev_arg;
|
|
isc_socket_newconnev_t *dev = NULL;
|
|
isc_region_t region;
|
|
isc_socket_t *oldsock = NULL;
|
|
isc_task_t *newtask = NULL;
|
|
|
|
dev = (isc_socket_newconnev_t *)event;
|
|
|
|
printf("newcon %s (task %p, oldsock %p, newsock %p, result %u)\n", name,
|
|
task, event->ev_sender, dev->newsocket, dev->result);
|
|
fflush(stdout);
|
|
|
|
if (dev->result == ISC_R_SUCCESS) {
|
|
/*
|
|
* Queue another listen on this socket.
|
|
*/
|
|
RUNTIME_CHECK(isc_socket_accept(event->ev_sender, task,
|
|
my_listen, event->ev_arg) ==
|
|
ISC_R_SUCCESS);
|
|
|
|
region.base = isc_mem_get(mctx, 20);
|
|
region.length = 20;
|
|
|
|
/*
|
|
* Create a new task for this socket, and queue up a
|
|
* recv on it.
|
|
*/
|
|
RUNTIME_CHECK(isc_task_create(taskmgr, 0, &newtask) ==
|
|
ISC_R_SUCCESS);
|
|
isc_socket_recv(dev->newsocket, ®ion, 1, newtask, my_recv,
|
|
event->ev_arg);
|
|
isc_task_detach(&newtask);
|
|
} else {
|
|
printf("detaching from socket %p\n", event->ev_sender);
|
|
oldsock = event->ev_sender;
|
|
|
|
isc_socket_detach(&oldsock);
|
|
|
|
isc_event_free(&event);
|
|
isc_task_shutdown(task);
|
|
return;
|
|
}
|
|
|
|
isc_event_free(&event);
|
|
}
|
|
|
|
static void
|
|
timeout(isc_task_t *task, isc_event_t *event) {
|
|
isc_socket_t *sock = event->ev_arg;
|
|
|
|
printf("Timeout, canceling IO on socket %p (task %p)\n", sock, task);
|
|
|
|
isc_socket_cancel(sock, NULL, ISC_SOCKCANCEL_ALL);
|
|
isc_timer_destroy((isc_timer_t **)&event->ev_sender);
|
|
isc_event_free(&event);
|
|
}
|
|
|
|
static char one[] = "1";
|
|
static char two[] = "2";
|
|
static char xso1[] = "so1";
|
|
static char xso2[] = "so2";
|
|
|
|
int
|
|
main(int argc, char *argv[]) {
|
|
isc_task_t *t1 = NULL, *t2 = NULL;
|
|
isc_timermgr_t *timgr = NULL;
|
|
isc_time_t expires;
|
|
isc_interval_t interval;
|
|
isc_timer_t *ti1 = NULL;
|
|
unsigned int workers;
|
|
isc_socketmgr_t *socketmgr = NULL;
|
|
isc_socket_t *so1 = NULL, *so2 = NULL;
|
|
isc_sockaddr_t sockaddr;
|
|
struct in_addr ina;
|
|
struct in6_addr in6a;
|
|
isc_result_t result;
|
|
int pf;
|
|
|
|
if (argc > 1) {
|
|
workers = atoi(argv[1]);
|
|
if (workers < 1) {
|
|
workers = 1;
|
|
}
|
|
if (workers > 8192) {
|
|
workers = 8192;
|
|
}
|
|
} else {
|
|
workers = 2;
|
|
}
|
|
printf("%u workers\n", workers);
|
|
|
|
if (isc_net_probeipv6() == ISC_R_SUCCESS) {
|
|
pf = PF_INET6;
|
|
} else {
|
|
pf = PF_INET;
|
|
}
|
|
|
|
/*
|
|
* EVERYTHING needs a memory context.
|
|
*/
|
|
isc_mem_create(&mctx);
|
|
|
|
/*
|
|
* The task manager is independent (other than memory context)
|
|
*/
|
|
RUNTIME_CHECK(isc_managers_create(mctx, workers, 0, &netmgr,
|
|
&taskmgr) == ISC_R_SUCCESS);
|
|
|
|
/*
|
|
* Timer manager depends only on the memory context as well.
|
|
*/
|
|
RUNTIME_CHECK(isc_timermgr_create(mctx, &timgr) == ISC_R_SUCCESS);
|
|
|
|
RUNTIME_CHECK(isc_task_create(taskmgr, 0, &t1) == ISC_R_SUCCESS);
|
|
RUNTIME_CHECK(isc_task_create(taskmgr, 0, &t2) == ISC_R_SUCCESS);
|
|
RUNTIME_CHECK(isc_task_onshutdown(t1, my_shutdown, one) ==
|
|
ISC_R_SUCCESS);
|
|
RUNTIME_CHECK(isc_task_onshutdown(t2, my_shutdown, two) ==
|
|
ISC_R_SUCCESS);
|
|
|
|
printf("task 1 = %p\n", t1);
|
|
printf("task 2 = %p\n", t2);
|
|
|
|
RUNTIME_CHECK(isc_socketmgr_create(mctx, &socketmgr) == ISC_R_SUCCESS);
|
|
|
|
/*
|
|
* Open up a listener socket.
|
|
*/
|
|
|
|
if (pf == PF_INET6) {
|
|
in6a = in6addr_any;
|
|
isc_sockaddr_fromin6(&sockaddr, &in6a, 5544);
|
|
} else {
|
|
ina.s_addr = INADDR_ANY;
|
|
isc_sockaddr_fromin(&sockaddr, &ina, 5544);
|
|
}
|
|
RUNTIME_CHECK(isc_socket_create(socketmgr, pf, isc_sockettype_tcp,
|
|
&so1) == ISC_R_SUCCESS);
|
|
result = isc_socket_bind(so1, &sockaddr, ISC_SOCKET_REUSEADDRESS);
|
|
RUNTIME_CHECK(result == ISC_R_SUCCESS);
|
|
RUNTIME_CHECK(isc_socket_listen(so1, 0) == ISC_R_SUCCESS);
|
|
|
|
/*
|
|
* Queue up the first accept event.
|
|
*/
|
|
RUNTIME_CHECK(isc_socket_accept(so1, t1, my_listen, xso1) ==
|
|
ISC_R_SUCCESS);
|
|
isc_time_settoepoch(&expires);
|
|
isc_interval_set(&interval, 10, 0);
|
|
RUNTIME_CHECK(isc_timer_create(timgr, isc_timertype_once, &expires,
|
|
&interval, t1, timeout, so1,
|
|
&ti1) == ISC_R_SUCCESS);
|
|
|
|
/*
|
|
* Open up a socket that will connect to www.flame.org, port 80.
|
|
* Why not. :)
|
|
*/
|
|
ina.s_addr = inet_addr("204.152.184.97");
|
|
if (0 && pf == PF_INET6) {
|
|
isc_sockaddr_v6fromin(&sockaddr, &ina, 80);
|
|
} else {
|
|
isc_sockaddr_fromin(&sockaddr, &ina, 80);
|
|
}
|
|
RUNTIME_CHECK(isc_socket_create(socketmgr, isc_sockaddr_pf(&sockaddr),
|
|
isc_sockettype_tcp,
|
|
&so2) == ISC_R_SUCCESS);
|
|
|
|
RUNTIME_CHECK(isc_socket_connect(so2, &sockaddr, t2, my_connect,
|
|
xso2) == ISC_R_SUCCESS);
|
|
|
|
/*
|
|
* Detaching these is safe, since the socket will attach to the
|
|
* task for any outstanding requests.
|
|
*/
|
|
isc_task_detach(&t1);
|
|
isc_task_detach(&t2);
|
|
|
|
/*
|
|
* Wait a short while.
|
|
*/
|
|
#ifndef WIN32
|
|
sleep(10);
|
|
#else /* ifndef WIN32 */
|
|
Sleep(10000);
|
|
#endif /* ifndef WIN32 */
|
|
|
|
fprintf(stderr, "Destroying socket manager\n");
|
|
isc_socketmgr_destroy(&socketmgr);
|
|
|
|
fprintf(stderr, "Destroying timer manager\n");
|
|
isc_timermgr_destroy(&timgr);
|
|
|
|
fprintf(stderr, "Destroying task manager\n");
|
|
isc_managers_destroy(&netmgr, &taskmgr);
|
|
|
|
isc_mem_stats(mctx, stdout);
|
|
isc_mem_destroy(&mctx);
|
|
|
|
return (0);
|
|
}
|