mirror of
https://gitlab.isc.org/isc-projects/dhcp
synced 2025-08-30 13:57:50 +00:00
- Make the connect system call asynchronous.
- Add a new call that takes a list of addresses of arbitrary types, rather than just IPv4 addresses. - Allow caller to also specify a local address to which to bind before connecting.
This commit is contained in:
@@ -49,11 +49,55 @@ isc_result_t omapi_connect (omapi_object_t *c,
|
||||
unsigned port)
|
||||
{
|
||||
struct hostent *he;
|
||||
int hix;
|
||||
unsigned i, hix;
|
||||
omapi_addr_list_t *addrs = (omapi_addr_list_t *)0;
|
||||
struct in_addr foo;
|
||||
isc_result_t status;
|
||||
|
||||
if (!inet_aton (server_name, &foo)) {
|
||||
/* If we didn't get a numeric address, try for a domain
|
||||
name. It's okay for this call to block. */
|
||||
he = gethostbyname (server_name);
|
||||
if (!he)
|
||||
return ISC_R_HOSTUNKNOWN;
|
||||
for (i = 0; he -> h_addr_list [i]; i++)
|
||||
;
|
||||
if (i == 0)
|
||||
return ISC_R_HOSTUNKNOWN;
|
||||
hix = i;
|
||||
|
||||
if (!omapi_addr_list_new (&addrs, hix, MDL))
|
||||
return ISC_R_NOMEMORY;
|
||||
for (i = 0; i < hix; i++) {
|
||||
addrs -> addresses [i].addrtype = he -> h_addrtype;
|
||||
addrs -> addresses [i].addrlen = he -> h_length;
|
||||
memcpy (addrs -> addresses [i].address,
|
||||
he -> h_addr_list [i],
|
||||
(unsigned)he -> h_length);
|
||||
addrs -> addresses [i].port = port;
|
||||
}
|
||||
} else {
|
||||
if (!omapi_addr_list_new (&addrs, 1, MDL))
|
||||
return ISC_R_NOMEMORY;
|
||||
addrs -> addresses [0].addrtype = AF_INET;
|
||||
addrs -> addresses [0].addrlen = sizeof foo;
|
||||
memcpy (addrs -> addresses [0].address, &foo, sizeof foo);
|
||||
addrs -> addresses [0].port = port;
|
||||
hix = 1;
|
||||
}
|
||||
status = omapi_connect_list (c, addrs, (omapi_addr_t *)0);
|
||||
omapi_addr_list_dereference (&addrs, MDL);
|
||||
return status;
|
||||
}
|
||||
|
||||
isc_result_t omapi_connect_list (omapi_object_t *c,
|
||||
omapi_addr_list_t *remote_addrs,
|
||||
omapi_addr_t *local_addr)
|
||||
{
|
||||
isc_result_t status;
|
||||
omapi_connection_object_t *obj;
|
||||
int flag;
|
||||
SOCKLEN_T sl;
|
||||
struct sockaddr_in local_sin;
|
||||
|
||||
obj = (omapi_connection_object_t *)dmalloc (sizeof *obj, MDL);
|
||||
if (!obj)
|
||||
@@ -75,34 +119,6 @@ isc_result_t omapi_connect (omapi_object_t *c,
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Set up all the constants in the address... */
|
||||
obj -> remote_addr.sin_port = htons (port);
|
||||
|
||||
/* First try for a numeric address, since that's easier to check. */
|
||||
if (!inet_aton (server_name, &obj -> remote_addr.sin_addr)) {
|
||||
/* If we didn't get a numeric address, try for a domain
|
||||
name. It's okay for this call to block. */
|
||||
he = gethostbyname (server_name);
|
||||
if (!he) {
|
||||
omapi_object_dereference ((omapi_object_t **)&obj,
|
||||
MDL);
|
||||
return ISC_R_HOSTUNKNOWN;
|
||||
}
|
||||
hix = 1;
|
||||
memcpy (&obj -> remote_addr.sin_addr,
|
||||
he -> h_addr_list [0],
|
||||
sizeof obj -> remote_addr.sin_addr);
|
||||
} else
|
||||
he = (struct hostent *)0;
|
||||
|
||||
#if defined (HAVE_SA_LEN)
|
||||
obj -> remote_addr.sin_len =
|
||||
sizeof (struct sockaddr_in);
|
||||
#endif
|
||||
obj -> remote_addr.sin_family = AF_INET;
|
||||
memset (&(obj -> remote_addr.sin_zero), 0,
|
||||
sizeof obj -> remote_addr.sin_zero);
|
||||
|
||||
/* Create a socket on which to communicate. */
|
||||
obj -> socket =
|
||||
socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
@@ -113,6 +129,38 @@ isc_result_t omapi_connect (omapi_object_t *c,
|
||||
return ISC_R_UNEXPECTED;
|
||||
}
|
||||
|
||||
/* Set up the local address, if any. */
|
||||
if (local_addr) {
|
||||
/* Only do TCPv4 so far. */
|
||||
if (local_addr -> addrtype != AF_INET) {
|
||||
omapi_object_dereference ((omapi_object_t **)&obj,
|
||||
MDL);
|
||||
return ISC_R_INVALIDARG;
|
||||
}
|
||||
local_sin.sin_port = htons (local_addr -> port);
|
||||
memcpy (&local_sin.sin_addr,
|
||||
local_addr -> address,
|
||||
local_addr -> addrlen);
|
||||
#if defined (HAVE_SA_LEN)
|
||||
local_sin.sin_len = sizeof local_addr;
|
||||
#endif
|
||||
local_sin.sin_family = AF_INET;
|
||||
memset (&local_sin.sin_zero, 0, sizeof local_sin.sin_zero);
|
||||
|
||||
if (bind (obj -> socket, (struct sockaddr *)&local_sin,
|
||||
sizeof local_sin) < 0) {
|
||||
omapi_object_dereference ((omapi_object_t **)&obj,
|
||||
MDL);
|
||||
if (errno == EADDRINUSE)
|
||||
return ISC_R_ADDRINUSE;
|
||||
if (errno == EADDRNOTAVAIL)
|
||||
return ISC_R_ADDRNOTAVAIL;
|
||||
if (errno == EACCES)
|
||||
return ISC_R_NOPERM;
|
||||
return ISC_R_UNEXPECTED;
|
||||
}
|
||||
}
|
||||
|
||||
#if defined (HAVE_SETFD)
|
||||
if (fcntl (obj -> socket, F_SETFD, 1) < 0) {
|
||||
close (obj -> socket);
|
||||
@@ -129,53 +177,34 @@ isc_result_t omapi_connect (omapi_object_t *c,
|
||||
return ISC_R_UNEXPECTED;
|
||||
}
|
||||
|
||||
/* Try to connect to the one IP address we were given, or any of
|
||||
the IP addresses listed in the host's A RR. */
|
||||
while (connect (obj -> socket,
|
||||
((struct sockaddr *)
|
||||
&obj -> remote_addr),
|
||||
sizeof obj -> remote_addr)) {
|
||||
if (!he || !he -> h_addr_list [hix]) {
|
||||
omapi_object_dereference ((omapi_object_t **)&obj,
|
||||
MDL);
|
||||
if (errno == ECONNREFUSED)
|
||||
return ISC_R_CONNREFUSED;
|
||||
if (errno == ENETUNREACH)
|
||||
return ISC_R_NETUNREACH;
|
||||
/* Set the SO_USELOOPBACK flag (this should not fail). */
|
||||
flag = 1;
|
||||
if (setsockopt (obj -> socket, SOL_SOCKET, SO_USELOOPBACK,
|
||||
(char *)&flag, sizeof flag) < 0) {
|
||||
omapi_object_dereference ((omapi_object_t **)&obj, MDL);
|
||||
return ISC_R_UNEXPECTED;
|
||||
}
|
||||
memcpy (&obj -> remote_addr.sin_addr,
|
||||
he -> h_addr_list [hix++],
|
||||
sizeof obj -> remote_addr.sin_addr);
|
||||
}
|
||||
|
||||
obj -> state = omapi_connection_connected;
|
||||
|
||||
/* I don't know why this would fail, so I'm tempted not to test
|
||||
the return value. */
|
||||
sl = sizeof (obj -> local_addr);
|
||||
if (getsockname (obj -> socket,
|
||||
((struct sockaddr *)
|
||||
&obj -> local_addr), &sl) < 0) {
|
||||
}
|
||||
|
||||
/* Set the file to nonblocking mode. */
|
||||
if (fcntl (obj -> socket, F_SETFL, O_NONBLOCK) < 0) {
|
||||
omapi_object_dereference ((omapi_object_t **)&obj, MDL);
|
||||
return ISC_R_UNEXPECTED;
|
||||
}
|
||||
|
||||
status = omapi_register_io_object ((omapi_object_t *)obj,
|
||||
omapi_connection_readfd,
|
||||
omapi_connection_writefd,
|
||||
omapi_connection_reader,
|
||||
omapi_connection_writer,
|
||||
omapi_connection_reaper);
|
||||
if (status != ISC_R_SUCCESS) {
|
||||
/* Store the address list on the object. */
|
||||
omapi_addr_list_reference (&obj -> connect_list, remote_addrs, MDL);
|
||||
obj -> cptr = 0;
|
||||
|
||||
status = (omapi_register_io_object
|
||||
((omapi_object_t *)obj,
|
||||
0, omapi_connection_writefd,
|
||||
0, omapi_connection_connect,
|
||||
omapi_connection_reaper));
|
||||
|
||||
obj -> state = omapi_connection_unconnected;
|
||||
omapi_connection_connect ((omapi_object_t *)obj);
|
||||
omapi_object_dereference ((omapi_object_t **)&obj, MDL);
|
||||
return status;
|
||||
}
|
||||
|
||||
return ISC_R_SUCCESS;
|
||||
}
|
||||
|
||||
/* Disconnect a connection object from the remote end. If force is nonzero,
|
||||
@@ -263,12 +292,102 @@ int omapi_connection_writefd (omapi_object_t *h)
|
||||
if (h -> type != omapi_type_connection)
|
||||
return -1;
|
||||
c = (omapi_connection_object_t *)h;
|
||||
if (c -> state == omapi_connection_connecting)
|
||||
return c -> socket;
|
||||
if (c -> out_bytes)
|
||||
return c -> socket;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
isc_result_t omapi_connection_connect (omapi_object_t *h)
|
||||
{
|
||||
int error;
|
||||
omapi_connection_object_t *c;
|
||||
SOCKLEN_T sl;
|
||||
isc_result_t status;
|
||||
|
||||
if (h -> type != omapi_type_connection)
|
||||
return ISC_R_INVALIDARG;
|
||||
c = (omapi_connection_object_t *)h;
|
||||
|
||||
if (c -> state == omapi_connection_connecting) {
|
||||
sl = sizeof error;
|
||||
if (getsockopt (c -> socket, SOL_SOCKET, SO_ERROR,
|
||||
&error, &sl) < 0) {
|
||||
omapi_disconnect (h, 1);
|
||||
return ISC_R_SUCCESS;
|
||||
}
|
||||
if (!errno)
|
||||
c -> state = omapi_connection_connected;
|
||||
}
|
||||
if (c -> state == omapi_connection_connecting ||
|
||||
c -> state == omapi_connection_unconnected) {
|
||||
if (c -> cptr >= c -> connect_list -> count) {
|
||||
omapi_disconnect (h, 1);
|
||||
return ISC_R_SUCCESS;
|
||||
}
|
||||
|
||||
if (c -> connect_list -> addresses [c -> cptr].addrtype !=
|
||||
AF_INET) {
|
||||
omapi_disconnect (h, 1);
|
||||
return ISC_R_SUCCESS;
|
||||
}
|
||||
|
||||
memcpy (&c -> remote_addr.sin_addr,
|
||||
&c -> connect_list -> addresses [c -> cptr].address,
|
||||
sizeof c -> remote_addr.sin_addr);
|
||||
c -> remote_addr.sin_family = AF_INET;
|
||||
c -> remote_addr.sin_port =
|
||||
htons (c -> connect_list -> addresses [c -> cptr].port);
|
||||
#if defined (HAVE_SA_LEN)
|
||||
c -> remote_addr.sin_len = sizeof c -> remote_addr;
|
||||
#endif
|
||||
memset (&c -> remote_addr.sin_zero, 0,
|
||||
sizeof c -> remote_addr.sin_zero);
|
||||
++c -> cptr;
|
||||
|
||||
if (connect (c -> socket,
|
||||
(struct sockaddr *)&c -> remote_addr,
|
||||
sizeof c -> remote_addr) < 0) {
|
||||
if (errno != EINPROGRESS) {
|
||||
omapi_disconnect (h, 1);
|
||||
return ISC_R_UNEXPECTED;
|
||||
}
|
||||
c -> state = omapi_connection_connecting;
|
||||
return ISC_R_SUCCESS;
|
||||
}
|
||||
c -> state = omapi_connection_connected;
|
||||
}
|
||||
|
||||
/* I don't know why this would fail, so I'm tempted not to test
|
||||
the return value. */
|
||||
sl = sizeof (c -> local_addr);
|
||||
if (getsockname (c -> socket,
|
||||
(struct sockaddr *)&c -> local_addr, &sl) < 0) {
|
||||
}
|
||||
|
||||
/* Disconnect from I/O object, if any. */
|
||||
if (h -> outer)
|
||||
omapi_object_dereference (&h -> outer, MDL);
|
||||
|
||||
status = omapi_register_io_object (h,
|
||||
omapi_connection_readfd,
|
||||
omapi_connection_writefd,
|
||||
omapi_connection_reader,
|
||||
omapi_connection_writer,
|
||||
omapi_connection_reaper);
|
||||
|
||||
if (status != ISC_R_SUCCESS) {
|
||||
omapi_disconnect (h, 1);
|
||||
return status;
|
||||
}
|
||||
|
||||
omapi_signal_in (h, "connect");
|
||||
omapi_addr_list_dereference (&c -> connect_list, MDL);
|
||||
return ISC_R_SUCCESS;
|
||||
}
|
||||
|
||||
/* 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
|
||||
@@ -329,6 +448,8 @@ isc_result_t omapi_connection_destroy (omapi_object_t *h,
|
||||
omapi_disconnect (h, 1);
|
||||
if (c -> listener)
|
||||
omapi_object_dereference (&c -> listener, file, line);
|
||||
if (c -> connect_list)
|
||||
omapi_addr_list_dereference (&c -> connect_list, file, line);
|
||||
return ISC_R_SUCCESS;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user