1999-11-02 04:01:34 +00:00
|
|
|
/*
|
|
|
|
* Copyright (C) 1996, 1997, 1998, 1999 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 INTERNET SOFTWARE CONSORTIUM DISCLAIMS
|
|
|
|
* ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
|
|
|
|
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
|
|
|
|
* CONSORTIUM 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.
|
|
|
|
*/
|
1999-10-27 22:24:32 +00:00
|
|
|
|
|
|
|
/*
|
1999-11-02 04:01:34 +00:00
|
|
|
* Subroutines that support the generic listener object.
|
1999-10-27 22:24:32 +00:00
|
|
|
*/
|
1999-11-02 04:01:34 +00:00
|
|
|
#include <stddef.h> /* NULL */
|
|
|
|
#include <string.h> /* memset */
|
|
|
|
#include <unistd.h> /* close */
|
1999-10-27 22:24:32 +00:00
|
|
|
|
1999-11-02 04:01:34 +00:00
|
|
|
#include <isc/assertions.h>
|
2000-01-04 20:04:42 +00:00
|
|
|
#include <isc/bufferlist.h>
|
2000-01-14 23:10:04 +00:00
|
|
|
#include <isc/error.h>
|
2000-01-04 20:04:42 +00:00
|
|
|
#include <isc/mem.h>
|
1999-10-27 22:24:32 +00:00
|
|
|
|
2000-01-04 20:04:42 +00:00
|
|
|
#include <omapi/private.h>
|
1999-11-02 04:01:34 +00:00
|
|
|
|
|
|
|
typedef struct omapi_listener_object {
|
|
|
|
OMAPI_OBJECT_PREAMBLE;
|
2000-01-04 20:04:42 +00:00
|
|
|
isc_task_t *task;
|
|
|
|
isc_socket_t *socket; /* Connection socket. */
|
|
|
|
isc_sockaddr_t address;
|
1999-11-02 04:01:34 +00:00
|
|
|
} omapi_listener_object_t;
|
|
|
|
|
2000-01-04 20:04:42 +00:00
|
|
|
/*
|
|
|
|
* Reader callback for a listener object. Accept an incoming connection.
|
|
|
|
*/
|
|
|
|
static void
|
2000-01-17 18:02:11 +00:00
|
|
|
listener_accept(isc_task_t *task, isc_event_t *event) {
|
1999-11-02 04:01:34 +00:00
|
|
|
isc_result_t result;
|
2000-01-17 20:06:37 +00:00
|
|
|
isc_buffer_t *ibuffer = NULL;
|
|
|
|
isc_buffer_t *obuffer = NULL;
|
2000-01-06 03:36:32 +00:00
|
|
|
isc_task_t *connection_task = NULL;
|
2000-01-17 20:06:37 +00:00
|
|
|
isc_socket_t *socket;
|
2000-01-06 03:36:32 +00:00
|
|
|
omapi_connection_object_t *connection = NULL;
|
2000-01-17 20:06:37 +00:00
|
|
|
omapi_object_t *protocol = NULL;
|
1999-10-27 22:24:32 +00:00
|
|
|
|
1999-11-02 04:01:34 +00:00
|
|
|
/*
|
2000-01-04 20:04:42 +00:00
|
|
|
* XXXDCL What are the meaningful things the listen/accept function
|
|
|
|
* can do if it fails to process an incoming connection because one
|
|
|
|
* of the functions it calls fails?
|
2000-01-14 23:10:04 +00:00
|
|
|
* The cleanup options are hurting my head.
|
1999-11-02 04:01:34 +00:00
|
|
|
*/
|
1999-10-27 22:24:32 +00:00
|
|
|
|
1999-11-02 04:01:34 +00:00
|
|
|
/*
|
2000-01-17 20:06:37 +00:00
|
|
|
* Immediately set up another listen task for the socket.
|
1999-11-02 04:01:34 +00:00
|
|
|
*/
|
2000-01-17 20:06:37 +00:00
|
|
|
isc_socket_accept(event->sender, task, listener_accept, event->arg);
|
|
|
|
|
|
|
|
result = ((isc_socket_newconnev_t *)event)->result;
|
|
|
|
socket = ((isc_socket_newconnev_t *)event)->newsocket;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* No more need for the event, once all the desired data has been
|
|
|
|
* used from it.
|
|
|
|
*/
|
|
|
|
isc_event_free(&event);
|
1999-10-27 22:24:32 +00:00
|
|
|
|
1999-11-02 04:01:34 +00:00
|
|
|
/*
|
2000-01-04 20:04:42 +00:00
|
|
|
* Check for the validity of new connection event.
|
1999-11-02 04:01:34 +00:00
|
|
|
*/
|
2000-01-17 20:06:37 +00:00
|
|
|
if (result != ISC_R_SUCCESS)
|
2000-01-04 20:04:42 +00:00
|
|
|
/*
|
|
|
|
* The result is probably ISC_R_UNEXPECTED; what can really be
|
|
|
|
* done about this other than just flunking out of here?
|
|
|
|
*/
|
2000-01-17 20:06:37 +00:00
|
|
|
return;
|
1999-11-02 04:01:34 +00:00
|
|
|
|
|
|
|
/*
|
2000-01-04 20:04:42 +00:00
|
|
|
* The new connection is good to go. Allocate the buffers for it and
|
2000-01-06 03:36:32 +00:00
|
|
|
* prepare its own task.
|
1999-11-02 04:01:34 +00:00
|
|
|
*/
|
2000-01-06 03:36:32 +00:00
|
|
|
if (isc_task_create(omapi_taskmgr, NULL, 0, &connection_task) !=
|
2000-01-04 20:04:42 +00:00
|
|
|
ISC_R_SUCCESS)
|
2000-01-14 23:10:04 +00:00
|
|
|
goto free_task;
|
2000-01-04 20:04:42 +00:00
|
|
|
|
|
|
|
ibuffer = NULL;
|
|
|
|
result = isc_buffer_allocate(omapi_mctx, &ibuffer, OMAPI_BUFFER_SIZE,
|
|
|
|
ISC_BUFFERTYPE_BINARY);
|
|
|
|
if (result != ISC_R_SUCCESS)
|
2000-01-14 23:10:04 +00:00
|
|
|
goto free_ibuffer;
|
2000-01-04 20:04:42 +00:00
|
|
|
|
|
|
|
obuffer = NULL;
|
|
|
|
result = isc_buffer_allocate(omapi_mctx, &obuffer, OMAPI_BUFFER_SIZE,
|
|
|
|
ISC_BUFFERTYPE_BINARY);
|
|
|
|
if (result != ISC_R_SUCCESS)
|
2000-01-14 23:10:04 +00:00
|
|
|
goto free_obuffer;
|
1999-10-27 22:24:32 +00:00
|
|
|
|
1999-11-02 04:01:34 +00:00
|
|
|
/*
|
2000-01-04 20:04:42 +00:00
|
|
|
* Create a new connection object.
|
1999-11-02 04:01:34 +00:00
|
|
|
*/
|
2000-01-17 18:02:11 +00:00
|
|
|
result = omapi_object_create((omapi_object_t **)&connection,
|
2000-01-06 03:36:32 +00:00
|
|
|
omapi_type_connection, sizeof(*connection));
|
2000-01-14 23:10:04 +00:00
|
|
|
if (result != ISC_R_SUCCESS)
|
|
|
|
goto free_obuffer;
|
1999-10-27 22:24:32 +00:00
|
|
|
|
2000-01-06 03:36:32 +00:00
|
|
|
connection->task = connection_task;
|
2000-01-04 20:04:42 +00:00
|
|
|
connection->state = omapi_connection_connected;
|
2000-01-17 20:06:37 +00:00
|
|
|
connection->socket = socket;
|
2000-01-13 06:13:26 +00:00
|
|
|
connection->is_client = ISC_FALSE;
|
1999-10-27 22:24:32 +00:00
|
|
|
|
2000-01-04 20:04:42 +00:00
|
|
|
ISC_LIST_INIT(connection->input_buffers);
|
|
|
|
ISC_LIST_APPEND(connection->input_buffers, ibuffer, link);
|
|
|
|
ISC_LIST_INIT(connection->output_buffers);
|
|
|
|
ISC_LIST_APPEND(connection->output_buffers, obuffer, link);
|
1999-10-27 22:24:32 +00:00
|
|
|
|
2000-01-14 23:10:04 +00:00
|
|
|
RUNTIME_CHECK(isc_mutex_init(&connection->mutex) == ISC_R_SUCCESS);
|
2000-01-17 18:02:11 +00:00
|
|
|
RUNTIME_CHECK(isc_mutex_init(&connection->recv_lock) == ISC_R_SUCCESS);
|
2000-01-14 23:10:04 +00:00
|
|
|
|
2000-01-04 20:04:42 +00:00
|
|
|
/*
|
2000-01-17 20:06:37 +00:00
|
|
|
* Create a new protocol object to oversee the handling of this
|
|
|
|
* connection.
|
2000-01-04 20:04:42 +00:00
|
|
|
*/
|
2000-01-17 20:06:37 +00:00
|
|
|
protocol = NULL;
|
|
|
|
result = omapi_object_create(&protocol, omapi_type_protocol,
|
|
|
|
sizeof(omapi_protocol_object_t));
|
2000-01-04 20:04:42 +00:00
|
|
|
if (result != ISC_R_SUCCESS)
|
2000-01-17 20:06:37 +00:00
|
|
|
goto free_connection_object;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Tie the protocol object bidirectionally to the connection
|
|
|
|
* object, with the connection as the outer object.
|
|
|
|
*/
|
|
|
|
OBJECT_REF(&protocol->outer, connection);
|
|
|
|
OBJECT_REF(&connection->inner, protocol);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Send the introductory message.
|
|
|
|
*/
|
|
|
|
result = omapi_protocol_send_intro(protocol, OMAPI_PROTOCOL_VERSION,
|
|
|
|
sizeof(omapi_protocol_header_t));
|
|
|
|
|
|
|
|
if (result != ISC_R_SUCCESS)
|
|
|
|
goto free_protocol_object;
|
2000-01-04 20:04:42 +00:00
|
|
|
|
|
|
|
/*
|
2000-01-14 23:10:04 +00:00
|
|
|
* Lose one reference to the connection, so it'll be gc'd when it's
|
|
|
|
* reaped. The omapi_protocol_listener_signal function added a
|
|
|
|
* reference when it created a protocol object as connection->inner.
|
2000-01-17 20:06:37 +00:00
|
|
|
* XXXDCL that's Ted's comment, but I don't see how it can be true.
|
|
|
|
* I don't see how it will "lose one reference" since
|
|
|
|
* omapi_object_dereference does not decrement refcnt.
|
2000-01-04 20:04:42 +00:00
|
|
|
*/
|
2000-01-17 18:02:11 +00:00
|
|
|
OBJECT_DEREF(&connection);
|
2000-01-14 23:10:04 +00:00
|
|
|
return;
|
2000-01-04 20:04:42 +00:00
|
|
|
|
2000-01-17 20:06:37 +00:00
|
|
|
free_protocol_object:
|
|
|
|
/*
|
|
|
|
* Remove the protocol object's reference to the connection
|
|
|
|
* object, so that the connection object will be destroyed.
|
|
|
|
* XXXDCL aigh, this is so confusing. I don't think the
|
|
|
|
* right thing is being done.
|
|
|
|
*/
|
|
|
|
OBJECT_DEREF(&connection->inner);
|
|
|
|
OBJECT_DEREF(&protocol);
|
|
|
|
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
free_connection_object:
|
2000-01-14 23:10:04 +00:00
|
|
|
/*
|
|
|
|
* Destroy the connection. This will free everything created
|
2000-01-17 20:06:37 +00:00
|
|
|
* in this function but the event, which was already freed.
|
2000-01-14 23:10:04 +00:00
|
|
|
*/
|
2000-01-17 18:02:11 +00:00
|
|
|
OBJECT_DEREF(&connection);
|
2000-01-14 23:10:04 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Free resources that were being created for the connection object.
|
|
|
|
*/
|
|
|
|
free_obuffer:
|
|
|
|
isc_buffer_free(&obuffer);
|
|
|
|
free_ibuffer:
|
|
|
|
isc_buffer_free(&ibuffer);
|
|
|
|
free_task:
|
|
|
|
isc_task_destroy(&connection_task);
|
2000-01-04 20:04:42 +00:00
|
|
|
return;
|
1999-10-27 22:24:32 +00:00
|
|
|
}
|
|
|
|
|
1999-11-02 04:01:34 +00:00
|
|
|
isc_result_t
|
2000-01-04 20:04:42 +00:00
|
|
|
omapi_listener_listen(omapi_object_t *caller, int port, int max) {
|
1999-11-02 04:01:34 +00:00
|
|
|
isc_result_t result;
|
2000-01-04 20:04:42 +00:00
|
|
|
isc_task_t *task;
|
1999-10-27 22:24:32 +00:00
|
|
|
omapi_listener_object_t *listener;
|
2000-01-04 20:04:42 +00:00
|
|
|
struct in_addr inaddr;
|
1999-10-27 22:24:32 +00:00
|
|
|
|
2000-01-04 20:04:42 +00:00
|
|
|
task = NULL;
|
|
|
|
result = isc_task_create(omapi_taskmgr, NULL, 0, &task);
|
|
|
|
if (result != ISC_R_SUCCESS)
|
|
|
|
return (result);
|
|
|
|
|
1999-11-02 04:01:34 +00:00
|
|
|
/*
|
2000-01-17 20:06:37 +00:00
|
|
|
* Create the listener object.
|
1999-11-02 04:01:34 +00:00
|
|
|
*/
|
2000-01-17 20:06:37 +00:00
|
|
|
listener = NULL;
|
|
|
|
result = omapi_object_create((omapi_object_t **)&listener,
|
|
|
|
omapi_type_listener, sizeof(*listener));
|
|
|
|
|
|
|
|
if (result != ISC_R_SUCCESS) {
|
|
|
|
isc_task_destroy(&task);
|
|
|
|
return (result);
|
|
|
|
}
|
|
|
|
|
2000-01-04 20:04:42 +00:00
|
|
|
listener->task = task;
|
1999-11-02 04:01:34 +00:00
|
|
|
|
|
|
|
/*
|
2000-01-04 20:04:42 +00:00
|
|
|
* Tie the listener object to the calling object.
|
1999-11-02 04:01:34 +00:00
|
|
|
*/
|
2000-01-17 18:02:11 +00:00
|
|
|
OBJECT_REF(&caller->outer, listener);
|
|
|
|
OBJECT_REF(&listener->inner, caller);
|
2000-01-04 20:04:42 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Create a socket on which to listen.
|
|
|
|
*/
|
|
|
|
listener->socket = NULL;
|
|
|
|
result = isc_socket_create(omapi_socketmgr, PF_INET,
|
|
|
|
isc_sockettype_tcp, &listener->socket);
|
2000-01-17 20:06:37 +00:00
|
|
|
|
|
|
|
if (result == ISC_R_SUCCESS) {
|
|
|
|
/*
|
|
|
|
* Set up the addressses on which to listen and bind to it.
|
2000-01-04 20:04:42 +00:00
|
|
|
*/
|
2000-01-17 20:06:37 +00:00
|
|
|
inaddr.s_addr = INADDR_ANY;
|
|
|
|
isc_sockaddr_fromin(&listener->address, &inaddr, port);
|
2000-01-04 20:04:42 +00:00
|
|
|
|
2000-01-17 20:06:37 +00:00
|
|
|
result = isc_socket_bind(listener->socket, &listener->address);
|
1999-10-27 22:24:32 +00:00
|
|
|
}
|
|
|
|
|
2000-01-17 20:06:37 +00:00
|
|
|
if (result == ISC_R_SUCCESS)
|
|
|
|
/*
|
|
|
|
* Now tell the kernel to listen for connections.
|
|
|
|
*/
|
|
|
|
result = isc_socket_listen(listener->socket, max);
|
|
|
|
|
|
|
|
if (result == ISC_R_SUCCESS)
|
|
|
|
/*
|
|
|
|
* Queue up the first accept event. The listener object
|
|
|
|
* will be passed to listener_accept() when it is called,
|
|
|
|
* though currently nothing is done with it.
|
|
|
|
*/
|
|
|
|
result = isc_socket_accept(listener->socket, task,
|
|
|
|
listener_accept, listener);
|
1999-10-27 22:24:32 +00:00
|
|
|
|
2000-01-04 20:04:42 +00:00
|
|
|
if (result != ISC_R_SUCCESS)
|
2000-01-17 20:06:37 +00:00
|
|
|
/*
|
|
|
|
* The listener has a refcnt of 2, so this does not really
|
|
|
|
* free it. XXXDCL
|
|
|
|
*/
|
2000-01-17 18:02:11 +00:00
|
|
|
OBJECT_DEREF(&listener);
|
2000-01-04 20:04:42 +00:00
|
|
|
|
1999-11-02 04:01:34 +00:00
|
|
|
return (result);
|
1999-10-27 22:24:32 +00:00
|
|
|
}
|
|
|
|
|
2000-01-17 18:02:11 +00:00
|
|
|
static isc_result_t
|
|
|
|
listener_setvalue(omapi_object_t *listener, omapi_object_t *id,
|
2000-01-04 20:04:42 +00:00
|
|
|
omapi_data_string_t *name, omapi_typed_data_t *value)
|
1999-10-27 22:24:32 +00:00
|
|
|
{
|
2000-01-04 20:04:42 +00:00
|
|
|
/*
|
|
|
|
* Nothing meaningful can be set in a listener object; just
|
|
|
|
* continue the call through the object chain.
|
|
|
|
*/
|
|
|
|
REQUIRE(listener != NULL && listener->type == omapi_type_listener);
|
|
|
|
|
|
|
|
PASS_SETVALUE(listener);
|
1999-10-27 22:24:32 +00:00
|
|
|
}
|
|
|
|
|
2000-01-17 18:02:11 +00:00
|
|
|
static isc_result_t
|
|
|
|
listener_getvalue(omapi_object_t *listener, omapi_object_t *id,
|
2000-01-04 20:04:42 +00:00
|
|
|
omapi_data_string_t *name, omapi_value_t **value)
|
1999-10-27 22:24:32 +00:00
|
|
|
{
|
2000-01-04 20:04:42 +00:00
|
|
|
/*
|
|
|
|
* Nothing meaningful can be fetched from a listener object; just
|
|
|
|
* continue the call through the object chain.
|
|
|
|
*/
|
|
|
|
REQUIRE(listener != NULL && listener->type == omapi_type_listener);
|
1999-10-27 22:24:32 +00:00
|
|
|
|
2000-01-04 20:04:42 +00:00
|
|
|
PASS_GETVALUE(listener);
|
1999-10-27 22:24:32 +00:00
|
|
|
}
|
|
|
|
|
2000-01-17 18:02:11 +00:00
|
|
|
static void
|
|
|
|
listener_destroy(omapi_object_t *object) {
|
2000-01-04 20:04:42 +00:00
|
|
|
omapi_listener_object_t *listener;
|
1999-10-27 22:24:32 +00:00
|
|
|
|
2000-01-04 20:04:42 +00:00
|
|
|
REQUIRE(object != NULL && object->type == omapi_type_listener);
|
1999-11-02 04:01:34 +00:00
|
|
|
|
2000-01-04 20:04:42 +00:00
|
|
|
listener = (omapi_listener_object_t *)object;
|
|
|
|
|
2000-01-17 20:06:37 +00:00
|
|
|
isc_task_destroy(&listener->task);
|
|
|
|
|
2000-01-04 20:04:42 +00:00
|
|
|
if (listener->socket != NULL) {
|
|
|
|
#if 0 /*XXXDCL*/
|
|
|
|
isc_socket_cancel(listener->socket, NULL, ISC_SOCKCANCEL_ALL);
|
|
|
|
isc_socket_shutdown(listener->socket, ISC_SOCKSHUT_ALL);
|
|
|
|
#else
|
|
|
|
isc_task_shutdown(listener->task);
|
|
|
|
#endif
|
|
|
|
listener->socket = NULL;
|
1999-10-27 22:24:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2000-01-17 18:02:11 +00:00
|
|
|
static isc_result_t
|
|
|
|
listener_signalhandler(omapi_object_t *listener, const char *name, va_list ap)
|
1999-10-27 22:24:32 +00:00
|
|
|
{
|
2000-01-04 20:04:42 +00:00
|
|
|
REQUIRE(listener != NULL && listener->type == omapi_type_listener);
|
1999-10-27 22:24:32 +00:00
|
|
|
|
2000-01-04 20:04:42 +00:00
|
|
|
/*
|
2000-01-17 18:02:11 +00:00
|
|
|
* This function is reached when listener_accept does
|
2000-01-04 20:04:42 +00:00
|
|
|
* an omapi_signal of "connect" on the listener object. Nothing
|
|
|
|
* need be done here, but the object that originally requested
|
|
|
|
* the listen needs to signalled that a connection was made.
|
|
|
|
*
|
|
|
|
* In the normal instance, the pass-through is to an object of type
|
|
|
|
* omapi_type_protocol_listener, so the signal_handler that
|
|
|
|
* is getting called is omapi_protocol_listener_signal.
|
|
|
|
*/
|
|
|
|
PASS_SIGNAL(listener);
|
1999-10-27 22:24:32 +00:00
|
|
|
}
|
|
|
|
|
1999-11-02 04:01:34 +00:00
|
|
|
/*
|
|
|
|
* Write all the published values associated with the object through the
|
|
|
|
* specified connection.
|
|
|
|
*/
|
2000-01-17 18:02:11 +00:00
|
|
|
static isc_result_t
|
|
|
|
listener_stuffvalues(omapi_object_t *connection, omapi_object_t *id,
|
|
|
|
omapi_object_t *listener)
|
1999-10-27 22:24:32 +00:00
|
|
|
{
|
2000-01-04 20:04:42 +00:00
|
|
|
REQUIRE(listener != NULL && listener->type == omapi_type_listener);
|
1999-10-27 22:24:32 +00:00
|
|
|
|
2000-01-04 20:04:42 +00:00
|
|
|
PASS_STUFFVALUES(listener);
|
1999-10-27 22:24:32 +00:00
|
|
|
}
|
|
|
|
|
2000-01-17 18:02:11 +00:00
|
|
|
isc_result_t
|
|
|
|
omapi_listener_init(void) {
|
2000-01-17 20:06:37 +00:00
|
|
|
return (omapi_object_register(&omapi_type_listener, "listener",
|
|
|
|
listener_setvalue,
|
|
|
|
listener_getvalue,
|
|
|
|
listener_destroy,
|
|
|
|
listener_signalhandler,
|
|
|
|
listener_stuffvalues,
|
|
|
|
NULL, NULL, NULL));
|
2000-01-17 18:02:11 +00:00
|
|
|
}
|