mirror of
https://gitlab.isc.org/isc-projects/dhcp
synced 2025-08-30 05:47:45 +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:
parent
df9ff0a119
commit
fc24e951a2
@ -49,11 +49,55 @@ isc_result_t omapi_connect (omapi_object_t *c,
|
|||||||
unsigned port)
|
unsigned port)
|
||||||
{
|
{
|
||||||
struct hostent *he;
|
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;
|
isc_result_t status;
|
||||||
omapi_connection_object_t *obj;
|
omapi_connection_object_t *obj;
|
||||||
int flag;
|
int flag;
|
||||||
SOCKLEN_T sl;
|
struct sockaddr_in local_sin;
|
||||||
|
|
||||||
obj = (omapi_connection_object_t *)dmalloc (sizeof *obj, MDL);
|
obj = (omapi_connection_object_t *)dmalloc (sizeof *obj, MDL);
|
||||||
if (!obj)
|
if (!obj)
|
||||||
@ -75,34 +119,6 @@ isc_result_t omapi_connect (omapi_object_t *c,
|
|||||||
return status;
|
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. */
|
/* Create a socket on which to communicate. */
|
||||||
obj -> socket =
|
obj -> socket =
|
||||||
socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
|
socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||||
@ -113,6 +129,38 @@ isc_result_t omapi_connect (omapi_object_t *c,
|
|||||||
return ISC_R_UNEXPECTED;
|
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 defined (HAVE_SETFD)
|
||||||
if (fcntl (obj -> socket, F_SETFD, 1) < 0) {
|
if (fcntl (obj -> socket, F_SETFD, 1) < 0) {
|
||||||
close (obj -> socket);
|
close (obj -> socket);
|
||||||
@ -129,53 +177,34 @@ isc_result_t omapi_connect (omapi_object_t *c,
|
|||||||
return ISC_R_UNEXPECTED;
|
return ISC_R_UNEXPECTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Try to connect to the one IP address we were given, or any of
|
/* Set the SO_USELOOPBACK flag (this should not fail). */
|
||||||
the IP addresses listed in the host's A RR. */
|
flag = 1;
|
||||||
while (connect (obj -> socket,
|
if (setsockopt (obj -> socket, SOL_SOCKET, SO_USELOOPBACK,
|
||||||
((struct sockaddr *)
|
(char *)&flag, sizeof flag) < 0) {
|
||||||
&obj -> remote_addr),
|
omapi_object_dereference ((omapi_object_t **)&obj, MDL);
|
||||||
sizeof obj -> remote_addr)) {
|
return ISC_R_UNEXPECTED;
|
||||||
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;
|
|
||||||
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) {
|
if (fcntl (obj -> socket, F_SETFL, O_NONBLOCK) < 0) {
|
||||||
omapi_object_dereference ((omapi_object_t **)&obj, MDL);
|
omapi_object_dereference ((omapi_object_t **)&obj, MDL);
|
||||||
return ISC_R_UNEXPECTED;
|
return ISC_R_UNEXPECTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
status = omapi_register_io_object ((omapi_object_t *)obj,
|
/* Store the address list on the object. */
|
||||||
omapi_connection_readfd,
|
omapi_addr_list_reference (&obj -> connect_list, remote_addrs, MDL);
|
||||||
omapi_connection_writefd,
|
obj -> cptr = 0;
|
||||||
omapi_connection_reader,
|
|
||||||
omapi_connection_writer,
|
|
||||||
omapi_connection_reaper);
|
|
||||||
if (status != ISC_R_SUCCESS) {
|
|
||||||
omapi_object_dereference ((omapi_object_t **)&obj, MDL);
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ISC_R_SUCCESS;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Disconnect a connection object from the remote end. If force is nonzero,
|
/* 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)
|
if (h -> type != omapi_type_connection)
|
||||||
return -1;
|
return -1;
|
||||||
c = (omapi_connection_object_t *)h;
|
c = (omapi_connection_object_t *)h;
|
||||||
|
if (c -> state == omapi_connection_connecting)
|
||||||
|
return c -> socket;
|
||||||
if (c -> out_bytes)
|
if (c -> out_bytes)
|
||||||
return c -> socket;
|
return c -> socket;
|
||||||
else
|
else
|
||||||
return -1;
|
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,
|
/* Reaper function for connection - if the connection is completely closed,
|
||||||
reap it. If it's in the disconnecting state, there were bytes left
|
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
|
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);
|
omapi_disconnect (h, 1);
|
||||||
if (c -> listener)
|
if (c -> listener)
|
||||||
omapi_object_dereference (&c -> listener, file, line);
|
omapi_object_dereference (&c -> listener, file, line);
|
||||||
|
if (c -> connect_list)
|
||||||
|
omapi_addr_list_dereference (&c -> connect_list, file, line);
|
||||||
return ISC_R_SUCCESS;
|
return ISC_R_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user