mirror of
https://github.com/isc-projects/bind9.git
synced 2026-02-23 01:40:23 -05:00
377 lines
9.9 KiB
C
377 lines
9.9 KiB
C
/*
|
|
* 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.
|
|
*/
|
|
|
|
/* $Id: connection.c,v 1.3 2000/01/04 20:04:37 tale Exp $ */
|
|
|
|
/* Principal Author: Ted Lemon */
|
|
|
|
/*
|
|
* Subroutines for dealing with connections.
|
|
*/
|
|
#include <errno.h>
|
|
#include <fcntl.h> /* F_SETFL, O_NONBLOCK */
|
|
#include <stddef.h> /* NULL */
|
|
#include <string.h> /* memset */
|
|
#include <unistd.h> /* close */
|
|
|
|
#include <isc/assertions.h>
|
|
#include <isc/netdb.h>
|
|
|
|
#include <omapi/private.h>
|
|
|
|
/*
|
|
* Swiped from bin/tests/sdig.c.
|
|
*/
|
|
static isc_result_t
|
|
get_address(const char *hostname, in_port_t port, isc_sockaddr_t *sockaddr) {
|
|
struct in_addr in4;
|
|
struct in6_addr in6;
|
|
struct hostent *he;
|
|
|
|
/*
|
|
* Is this an IPv6 numeric address?
|
|
*/
|
|
if (omapi_ipv6 && inet_pton(AF_INET6, hostname, &in6) == 1)
|
|
isc_sockaddr_fromin6(sockaddr, &in6, port);
|
|
|
|
/*
|
|
* What about an IPv4 numeric address?
|
|
*/
|
|
else if (inet_pton(AF_INET, hostname, &in4) == 1)
|
|
isc_sockaddr_fromin(sockaddr, &in4, port);
|
|
|
|
else {
|
|
/*
|
|
* Look up the host name.
|
|
*/
|
|
he = gethostbyname(hostname);
|
|
if (he == NULL)
|
|
return (ISC_R_NOTFOUND);
|
|
|
|
INSIST(he->h_addrtype == AF_INET);
|
|
isc_sockaddr_fromin(sockaddr,
|
|
(struct in_addr *)(he->h_addr_list[0]),
|
|
port);
|
|
}
|
|
|
|
return (ISC_R_SUCCESS);
|
|
}
|
|
|
|
/*
|
|
* This is the function that is called when a CONNECT event is posted on
|
|
* the socket as a result of isc_socket_connect.
|
|
*/
|
|
static void
|
|
omapi_connection_connect(isc_task_t *task, isc_event_t *event) {
|
|
isc_result_t result;
|
|
isc_socket_connev_t *connect_event;
|
|
omapi_connection_object_t *connection;
|
|
|
|
ENSURE(event->sender == connection->socket);
|
|
|
|
connect_event = (isc_socket_connev_t *)event;
|
|
if (connect_event->result != ISC_R_SUCCESS) {
|
|
isc_socket_detach(&connection->socket);
|
|
isc_event_free(&event);
|
|
isc_task_shutdown(task);
|
|
return;
|
|
}
|
|
|
|
connection = event->arg;
|
|
|
|
result = isc_socket_getpeername(connection->socket,
|
|
&connection->remote_addr);
|
|
if (result != ISC_R_SUCCESS) {
|
|
OBJECT_DEREF(&connection, "omapi_connection_connect");
|
|
return;
|
|
}
|
|
|
|
result = isc_socket_getsockname(connection->socket,
|
|
&connection->local_addr);
|
|
if (result != ISC_R_SUCCESS) {
|
|
OBJECT_DEREF(&connection, "omapi_connection_connect");
|
|
return;
|
|
}
|
|
|
|
connection->state = omapi_connection_connected;
|
|
|
|
isc_event_free(&event);
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Make an outgoing connection to an OMAPI server.
|
|
*/
|
|
isc_result_t
|
|
omapi_connection_toserver(omapi_object_t *c, const char *server_name, int port)
|
|
{
|
|
isc_result_t result;
|
|
isc_buffer_t *ibuffer, *obuffer;
|
|
isc_task_t *task;
|
|
isc_sockaddr_t sockaddr;
|
|
omapi_connection_object_t *obj;
|
|
#if 0 /*XXXDCL*/
|
|
int flag;
|
|
#endif
|
|
|
|
result = get_address(server_name, port, &sockaddr);
|
|
if (result != ISC_R_SUCCESS)
|
|
return (result);
|
|
|
|
/*
|
|
* Prepare the task that will wait for the connection to be made.
|
|
*/
|
|
task = NULL;
|
|
result = isc_task_create(omapi_taskmgr, NULL, 0, &task);
|
|
if (result != ISC_R_SUCCESS)
|
|
return (result);
|
|
|
|
ibuffer = NULL;
|
|
result = isc_buffer_allocate(omapi_mctx, &ibuffer, OMAPI_BUFFER_SIZE,
|
|
ISC_BUFFERTYPE_BINARY);
|
|
if (result != ISC_R_SUCCESS)
|
|
return (result);
|
|
|
|
obuffer = NULL;
|
|
result = isc_buffer_allocate(omapi_mctx, &obuffer, OMAPI_BUFFER_SIZE,
|
|
ISC_BUFFERTYPE_BINARY);
|
|
if (result != ISC_R_SUCCESS)
|
|
return (result);
|
|
|
|
/*
|
|
* XXXDCL on errors I need to also blast the task and buffers.
|
|
*/
|
|
|
|
/*
|
|
* Create a new connection object.
|
|
*/
|
|
obj = isc_mem_get(omapi_mctx, sizeof(*obj));
|
|
if (obj == NULL)
|
|
return (ISC_R_NOMEMORY);
|
|
memset(obj, 0, sizeof(*obj));
|
|
obj->refcnt = 1;
|
|
obj->task = task;
|
|
obj->type = omapi_type_connection;
|
|
|
|
ISC_LIST_INIT(obj->input_buffers);
|
|
ISC_LIST_APPEND(obj->input_buffers, ibuffer, link);
|
|
ISC_LIST_INIT(obj->output_buffers);
|
|
ISC_LIST_APPEND(obj->output_buffers, obuffer, link);
|
|
|
|
/*
|
|
* Tie the new connection object to the protocol object.
|
|
*/
|
|
OBJECT_REF(&c->outer, obj, "omapi_connection_toserver");
|
|
OBJECT_REF(&obj->inner, c, "omapi_connection_toserver");
|
|
|
|
/*
|
|
* Create a socket on which to communicate.
|
|
*/
|
|
result = isc_socket_create(omapi_socketmgr, isc_sockaddr_pf(&sockaddr),
|
|
isc_sockettype_tcp, &obj->socket);
|
|
if (result != ISC_R_SUCCESS) {
|
|
/* XXXDCL this call and later will not free the connection obj
|
|
* because it has two refcnts, one for existing plus one
|
|
* for the tie to h->outer. This does not seem right to me.
|
|
*/
|
|
OBJECT_DEREF(&obj, "omapi_connection_toserver");
|
|
return (result);
|
|
}
|
|
|
|
#if 0 /*XXXDCL*/
|
|
/*
|
|
* Set the SO_REUSEADDR flag (this should not fail).
|
|
*/
|
|
flag = 1;
|
|
if (setsockopt(obj->socket, SOL_SOCKET, SO_REUSEADDR,
|
|
(char *)&flag, sizeof(flag)) < 0) {
|
|
OBJECT_DEREF(&obj, "omapi_connect");
|
|
return (ISC_R_UNEXPECTED);
|
|
}
|
|
#endif
|
|
|
|
result = isc_socket_connect(obj->socket, &sockaddr, task,
|
|
omapi_connection_connect, obj);
|
|
if (result != ISC_R_SUCCESS)
|
|
OBJECT_DEREF(&obj, "omapi_connection_toserver");
|
|
return (result);
|
|
}
|
|
|
|
/*
|
|
* Disconnect a connection object from the remote end. If force is true,
|
|
* close the connection immediately. Otherwise, shut down the receiving end
|
|
* but allow any unsent data to be sent before actually closing the socket.
|
|
*/
|
|
|
|
void
|
|
omapi_disconnect(omapi_object_t *generic, isc_boolean_t force) {
|
|
omapi_connection_object_t *connection;
|
|
|
|
REQUIRE(generic != NULL);
|
|
|
|
connection = (omapi_connection_object_t *)generic;
|
|
|
|
REQUIRE(connection->type == omapi_type_connection);
|
|
|
|
if (! force) {
|
|
/*
|
|
* If we're already disconnecting, we don't have to do
|
|
* anything.
|
|
*/
|
|
if (connection->state == omapi_connection_disconnecting)
|
|
return;
|
|
|
|
/*
|
|
* Try to shut down the socket - this sends a FIN to the
|
|
* remote end, so that it won't send us any more data. If
|
|
* the shutdown succeeds, and we still have bytes left to
|
|
* write, defer closing the socket until that's done.
|
|
*/
|
|
if (connection->out_bytes > 0) {
|
|
#if 0 /*XXXDCL*/
|
|
isc_socket_shutdown(connection->socket,
|
|
ISC_SOCKSHUT_RECV);
|
|
#else
|
|
isc_socket_cancel(connection->socket, NULL,
|
|
ISC_SOCKCANCEL_RECV);
|
|
#endif
|
|
connection->state = omapi_connection_disconnecting;
|
|
return;
|
|
}
|
|
}
|
|
|
|
isc_task_shutdown(connection->task);
|
|
connection->state = omapi_connection_closed;
|
|
|
|
/*
|
|
* Disconnect from I/O object, if any.
|
|
*/
|
|
if (connection->outer != NULL)
|
|
OBJECT_DEREF(&connection->outer, "omapi_disconnect");
|
|
|
|
/*
|
|
* If whatever created us registered a signal handler, send it
|
|
* a disconnect signal.
|
|
*/
|
|
omapi_signal(generic, "disconnect", generic);
|
|
}
|
|
|
|
isc_result_t
|
|
omapi_connection_require(omapi_object_t *generic, unsigned int bytes) {
|
|
omapi_connection_object_t *connection;
|
|
|
|
REQUIRE(generic != NULL && generic->type == omapi_type_connection);
|
|
|
|
connection = (omapi_connection_object_t *)generic;
|
|
|
|
connection->bytes_needed = bytes;
|
|
if (connection->bytes_needed <= connection->in_bytes)
|
|
return (ISC_R_SUCCESS);
|
|
|
|
return (ISC_R_NOTYET);
|
|
}
|
|
|
|
/*
|
|
* Reaper function for connection - if the connection is completely closed,
|
|
* reap it. If it's in the disconnecting state, there were bytes left
|
|
* to write when the user closed it, so if there are now no bytes left to
|
|
* write, we can close it.
|
|
*/
|
|
isc_result_t
|
|
omapi_connection_reaper(omapi_object_t *h) {
|
|
omapi_connection_object_t *c;
|
|
|
|
REQUIRE(h != NULL && h->type == omapi_type_connection);
|
|
|
|
c = (omapi_connection_object_t *)h;
|
|
if (c->state == omapi_connection_disconnecting && c->out_bytes == 0)
|
|
omapi_disconnect(h, OMAPI_FORCE_DISCONNECT);
|
|
if (c->state == omapi_connection_closed)
|
|
return (ISC_R_NOTCONNECTED);
|
|
return (ISC_R_SUCCESS);
|
|
}
|
|
|
|
isc_result_t
|
|
omapi_connection_set_value(omapi_object_t *h, omapi_object_t *id,
|
|
omapi_data_string_t *name,
|
|
omapi_typed_data_t *value)
|
|
{
|
|
REQUIRE(h != NULL && h->type == omapi_type_connection);
|
|
|
|
if (h->inner != NULL && h->inner->type->set_value)
|
|
return (*(h->inner->type->set_value))(h->inner, id,
|
|
name, value);
|
|
return (ISC_R_NOTFOUND);
|
|
}
|
|
|
|
isc_result_t
|
|
omapi_connection_get_value(omapi_object_t *h, omapi_object_t *id,
|
|
omapi_data_string_t *name,
|
|
omapi_value_t **value)
|
|
{
|
|
REQUIRE(h != NULL && h->type == omapi_type_connection);
|
|
|
|
if (h->inner != NULL && h->inner->type->get_value)
|
|
return (*(h->inner->type->get_value))(h->inner, id,
|
|
name, value);
|
|
return (ISC_R_NOTFOUND);
|
|
}
|
|
|
|
void
|
|
omapi_connection_destroy(omapi_object_t *h, const char *name) {
|
|
omapi_connection_object_t *c;
|
|
|
|
REQUIRE(h != NULL && h->type == omapi_type_connection);
|
|
|
|
c = (omapi_connection_object_t *)h;
|
|
|
|
if (c->state == omapi_connection_connected)
|
|
omapi_disconnect(h, OMAPI_FORCE_DISCONNECT);
|
|
|
|
if (c->listener != NULL)
|
|
OBJECT_DEREF(&c->listener, name);
|
|
}
|
|
|
|
isc_result_t
|
|
omapi_connection_signal_handler(omapi_object_t *h, const char *name,
|
|
va_list ap)
|
|
{
|
|
REQUIRE(h != NULL && h->type == omapi_type_connection);
|
|
|
|
if (h->inner != NULL && h->inner->type->signal_handler)
|
|
return (*(h->inner->type->signal_handler))(h->inner, name, ap);
|
|
|
|
return (ISC_R_NOTFOUND);
|
|
}
|
|
|
|
/*
|
|
* Write all the published values associated with the object through the
|
|
* specified connection.
|
|
*/
|
|
|
|
isc_result_t
|
|
omapi_connection_stuff_values(omapi_object_t *c, omapi_object_t *id,
|
|
omapi_object_t *h)
|
|
{
|
|
REQUIRE(h != NULL && h->type == omapi_type_connection);
|
|
|
|
if (h->inner != NULL && h->inner->type->stuff_values)
|
|
return ((*(h->inner->type->stuff_values))(c, id, h->inner));
|
|
|
|
return (ISC_R_SUCCESS);
|
|
}
|