%% options
copyright owner = Dirk Krause
copyright year = 2015-xxxx
license = bsd
%% module
#include "dk4sock.h"
#include "dk4mem.h"
#include "dk4mao8d.h"
#if !DK4_HAVE_GETADDRINFO
#include "dk4mai8dus.h"
#endif
$!trace-include
#if DK4_HAVE_GETADDRINFO
/* +++++ +getaddrinfo +++++ */
/** Create client socket for given address lists.
@param hostname Remote host name to connect to.
@param raddrst List of remote addresses.
@param laddrst List of local addresses to use, optional.
@param secs Seconds for timeout.
@param usecs Microseconds for timeout.
@param erp Error report, may be NULL.
@return Valid socket on success, INVALID_SOCKET on error.
Error codes:
- DK4_E_INVALID_ARGUMENTS
if any argument is bad,
- DK4_E_SOCKET_SOCKET
if the socket() function failed to create a new socket,
- DK4_E_SOCKET_BIND
if the bind() function failed to bind the requested local address,
- DK4_E_SOCKET_FCNTL
if the fcntl() function failed,
- DK4_E_SOCKET_IOCTLSOCKET
if the ioctlsocket() function failed.
- DK4_E_SOCKET_CONNECT
if the connect() function failed,
- DK4_E_SOCKET_TIMEOUT
if a timeout occured in the connect attempt,
- DK4_E_SOCKET_SELECT
if the select() function failed,
- DK4_E_SOCKET_GETSOCKOPT
if the getsockopt() function failed.
For all error codes - except DK4_E_SOCKET_TIMEOUT - the iDetails1
field contains the errno or WSAGetLastError() value.
*/
static
dk4_socket_t
dk4socket_c8_tcp_client_addresses(
const char *hostname,
struct addrinfo *raddrst,
struct addrinfo *laddrst,
long secs,
long usecs,
dk4_er_t *erp
)
{
struct addrinfo *ra;
struct addrinfo *la;
dk4_socket_t back = INVALID_SOCKET;
$? "+ dk4socket_c8_tcp_client_addresses"
ra = raddrst;
while ((INVALID_SOCKET == back) && (NULL != ra)) {
if (NULL != laddrst) {
la = laddrst;
while ((INVALID_SOCKET == back) && (NULL != la)) {
if (la->ai_family == ra->ai_family) {
$? ". remote = %d PTR=%d %lu", (int)(ra->ai_family), TR_IPTR(ra->ai_addr), (unsigned long)(ra->ai_addrlen)
$? ". local = %d PTR=%d %lu", (int)(la->ai_family), TR_IPTR(la->ai_addr), (unsigned long)(la->ai_addrlen)
$!trace-code dk4socket_debug_address("remote", ra->ai_addr, ra->ai_addrlen);
$!trace-code dk4socket_debug_address("local ", la->ai_addr, la->ai_addrlen);
back = dk4socket_tcp_client_for_addr(
ra->ai_family, ra->ai_addr, ra->ai_addrlen,
la->ai_addr, la->ai_addrlen, secs, usecs, erp
);
if (INVALID_SOCKET != back) {
la = NULL; ra = NULL;
}
}
if (NULL != la) { la = la->ai_next; }
}
} else {
back = dk4socket_tcp_client_for_addr(
ra->ai_family, ra->ai_addr, ra->ai_addrlen, NULL, 0,
secs, usecs, erp
);
if (INVALID_SOCKET != back) {
ra = NULL;
}
}
if (NULL != ra) { ra = ra->ai_next; }
}
$? "- dk4socket_c8_tcp_client_addresses %d", (int)back
return back;
}
/* ----- +getaddrinfo ----- */
#else
/* +++++ -getaddrinfo +++++ */
#if DK4_HAVE_STRUCT_SOCKADDR_IN6
/** used to initialize IPv6 address for binding local ports.
*/
static const IN6_ADDR dk4socket_any6 = IN6ADDR_ANY_INIT;
#endif
/* ----- -getaddrinfo ----- */
#endif
dk4_socket_t
dk4socket_c8_tcp_client_host_num_srv(
const char *hostname,
const char *svcname,
const char *lsvcname,
long secs,
long usecs,
int nsrv,
dk4_er_t *erp
)
{
#if DK4_HAVE_GETADDRINFO
/* +++++ +getaddrinfo */
struct addrinfo rhints; /* Hints for remote address */
struct addrinfo lhints; /* Hints for local address */
struct addrinfo *raddrst = NULL; /* Storage for remote addresses */
struct addrinfo *laddrst = NULL; /* Storage for local addresses */
int res; /* Operation result */
dk4_socket_t back = INVALID_SOCKET;
$? "+ dk4socket_c8_tcp_client_host_num_srv (+getaddrinfo)"
if ((NULL != hostname) && (NULL != svcname)) { $? ". args ok"
$? ". have getaddrinfo"
DK4_MEMRES(&rhints, sizeof(rhints));
rhints.ai_family = AF_UNSPEC;
rhints.ai_socktype = SOCK_STREAM;
if (0 != nsrv) { rhints.ai_flags = AI_NUMERICSERV; }
rhints.ai_protocol = 0;
res = getaddrinfo(hostname, svcname, &rhints, &raddrst);
if ((0 == res) && (NULL != raddrst)) {
if (NULL != lsvcname) {
DK4_MEMRES(&lhints, sizeof(lhints));
lhints.ai_family = AF_UNSPEC;
lhints.ai_socktype = SOCK_STREAM;
lhints.ai_flags = AI_PASSIVE;
if (0 != nsrv) { lhints.ai_flags |= AI_NUMERICSERV; }
lhints.ai_protocol = 0;
res = getaddrinfo(NULL, lsvcname, &lhints, &laddrst);
if ((0 == res) && (NULL != laddrst)) {
back = dk4socket_c8_tcp_client_addresses(
hostname, raddrst, laddrst, secs, usecs, erp
);
if (NULL != laddrst) { freeaddrinfo(laddrst); }
} else { $? "! bug"
/* ERROR: getaddrinfo failed */
dk4error_set_idetails(erp, DK4_E_SOCKET_GETADDRLOC, res);
}
} else {
back = dk4socket_c8_tcp_client_addresses(
hostname, raddrst, NULL, secs, usecs, erp
);
}
if (NULL != raddrst) { freeaddrinfo(raddrst); }
} else { $? "! host not found"
/* ERROR: getaddrinfo failed */
dk4error_set_idetails(erp, DK4_E_SOCKET_GETADDRINFO, res);
}
} else {
dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS);
}
$? "- dk4socket_c8_tcp_client_host_num_srv %d", (int)back
return back;
/* ----- +getaddrinfo */
#else
/* +++++ -getaddrinfo */
struct servent *serv = NULL; /* Service entry */
const char *endptr = NULL; /* End of used text */
int iport = 0; /* Port number from servent */
unsigned short rport = 0; /* Remote port number */
unsigned short lport = 0; /* Local port number */
int res; /* Operation result */
dk4_socket_t back = INVALID_SOCKET;
$? "+ dk4socket_c8_tcp_client_host_num_srv (-getaddrinfo)"
if ((NULL != hostname) && (NULL != svcname)) { $? ". args ok"
res = dk4ma_input_c8_dec_ushort(&rport, svcname, &endptr, 1, NULL);
if (0 == res) {
dk4socket_error_reset();
serv = getservbyname(svcname, NULL);
if (NULL != serv) {
iport = serv->s_port;
dk4socket_swap_bytes_if_not_bigendian((void *)(&iport), sizeof(iport));
rport = (unsigned short)iport;
} else {
dk4socket_error_report(erp, DK4_E_SOCKET_GETSERVBYNAME);
}
}
if (0 != rport) {
if (NULL != lsvcname) {
endptr = NULL;
res = dk4ma_input_c8_dec_ushort(&lport, lsvcname, &endptr, 1, NULL);
if (0 == res) {
dk4socket_error_reset();
serv = getservbyname(lsvcname, NULL);
if (NULL != serv) {
iport = serv->s_port;
dk4socket_swap_bytes_if_not_bigendian(
(void *)(&iport), sizeof(iport)
);
lport = (unsigned short)iport;
} else {
dk4socket_error_report(erp, DK4_E_SOCKET_GETSERVBYNAME);
}
}
if (0 != lport) {
back = dk4socket_c8_tcp_client_host_port(
hostname, rport, lport, secs, usecs, erp
);
}
} else {
back = dk4socket_c8_tcp_client_host_port(
hostname, rport, (unsigned short)0, secs, usecs, erp
);
}
}
} else {
dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS);
}
$? "- dk4socket_c8_tcp_client_host_num_srv %d", (int)back
return back;
/* ----- -getaddrinfo */
#endif
}
dk4_socket_t
dk4socket_c8_tcp_client_host_srv(
const char *hostname,
const char *svcname,
const char *lsvcname,
long secs,
long usecs,
dk4_er_t *erp
)
{
return (
dk4socket_c8_tcp_client_host_num_srv(
hostname, svcname, lsvcname, secs, usecs, 0, erp
)
);
}
dk4_socket_t
dk4socket_c8_tcp_client_host_port(
const char *hostname,
unsigned short portno,
unsigned short lportno,
long secs,
long usecs,
dk4_er_t *erp
)
{
#if DK4_HAVE_GETADDRINFO
/* +++++ +getaddrinfo */
char rpnb[16*sizeof(dk4_um_t)]; /* Remote port no */
char lpnb[16*sizeof(dk4_um_t)]; /* Local port no */
int res;
dk4_socket_t back = INVALID_SOCKET;
$? "+ dk4socket_c8_tcp_client_host_port"
if ((NULL != hostname) && (0 < portno)) {
res = dk4ma_write_c8_decimal_unsigned(
rpnb, sizeof(rpnb), (dk4_um_t)portno, 0, erp
);
if (0 != res) {
if (0 < lportno) {
res = dk4ma_write_c8_decimal_unsigned(
lpnb, sizeof(lpnb), (dk4_um_t)lportno, 0, erp
);
if (0 != res) {
back = dk4socket_c8_tcp_client_host_num_srv(
hostname, rpnb, lpnb, secs, usecs, 1, erp
);
} else { $? "! bug"
}
} else {
back = dk4socket_c8_tcp_client_host_num_srv(
hostname, rpnb, NULL, secs, usecs, 1, erp
);
}
} else { $? "! bug"
}
} else {
dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS);
}
return back;
/* ----- -getaddrinfo */
/* ----- +getaddrinfo */
#else
/* +++++ -getaddrinfo */
#if DK4_HAVE_STRUCT_SOCKADDR_IN6
struct sockaddr_in6 sr6;
struct sockaddr_in6 sl6;
#endif
struct sockaddr_in sr4;
struct sockaddr_in sl4;
char **addrptr;
struct hostent * hep = NULL;
dk4_socket_t back = INVALID_SOCKET;
$? "+ dk4socket_c8_tcp_client_host_port"
if ((NULL != hostname) && (0 < portno)) {
hep = gethostbyname(hostname);
if (NULL != hep) { $? ". hep"
if (NULL != hep->h_addr_list) { $? ". addrlist"
switch (hep->h_addrtype) {
case AF_INET : { $? ". AF_INET"
if (4 == hep->h_length) { $? ". h_length"
addrptr = hep->h_addr_list;
while ((INVALID_SOCKET == back) && (NULL != *addrptr)) {
DK4_MEMRES(&sr4, sizeof(sr4));
sr4.sin_family = AF_INET;
sr4.sin_port = dk4socket_htons(portno);
DK4_MEMCPY(&(sr4.sin_addr), *addrptr, sizeof(IN_ADDR));
$!trace-code dk4socket_debug_address("remote", (struct sockaddr *)(&sr4), sizeof(sr4));
if (0 != lportno) {
DK4_MEMRES(&sl4, sizeof(sl4));
sl4.sin_family = AF_INET;
sl4.sin_port = dk4socket_htons(lportno);
sl4.sin_addr.s_addr = dk4socket_htonl(INADDR_ANY);
$!trace-code dk4socket_debug_address("local ", (struct sockaddr *)(&sl4), sizeof(sl4));
back = dk4socket_tcp_client_for_addr(
AF_INET, (struct sockaddr *)(&sr4), sizeof(sr4),
(struct sockaddr *)(&sl4), sizeof(sl4),
secs, usecs, erp
);
} else {
back = dk4socket_tcp_client_for_addr(
AF_INET, (struct sockaddr *)(&sr4), sizeof(sr4),
NULL, 0, secs, usecs, erp
);
}
addrptr++;
}
} else { $? "!. h_length"
/* ERROR: Address length mismatch */
dk4error_set_simple_error_code(erp, DK4_E_BUG);
}
} break;
#if DK4_HAVE_STRUCT_SOCKADDR_IN6
case AF_INET6 : { $? ". AF_INET6"
if (16 == hep->h_length) {
addrptr = hep->h_addr_list;
while ((INVALID_SOCKET == back) && (NULL != *addrptr)) {
DK4_MEMRES(&sr6, sizeof(sr6));
sr6.sin6_family = AF_INET6;
sr6.sin6_port = dk4socket_htons(portno);
DK4_MEMCPY(&(sr6.sin6_addr), *addrptr, sizeof(IN6_ADDR));
if (0 != lportno) {
DK4_MEMRES(&sl6, sizeof(sl6));
sl6.sin6_family = AF_INET6;
sl6.sin6_port = dk4socket_htons(lportno);
DK4_MEMCPY(&(sl6.sin6_addr), &dk4socket_any6, sizeof(IN6_ADDR));
back = dk4socket_tcp_client_for_addr(
AF_INET6, (struct sockaddr *)(&sr6), sizeof(sr6),
(struct sockaddr *)(&sl6), sizeof(sl6),
secs, usecs, erp
);
} else {
back = dk4socket_tcp_client_for_addr(
AF_INET6, (struct sockaddr *)(&sr6), sizeof(sr6),
NULL, 0, secs, usecs, erp
);
}
addrptr++;
}
} else {
/* ERROR: Address length mismatch */
dk4error_set_simple_error_code(erp, DK4_E_BUG);
}
} break;
#endif
default : {
/* ERROR: Unknown protocol */
dk4error_set_simple_error_code(erp, DK4_E_BUG);
} break;
}
} else { $? "! addrlist"
/* ERROR: Host not found (empty list) */
dk4error_set_simple_error_code(erp, DK4_E_BUG);
}
} else { $? "! hep"
/* ERROR: Host not found */
dk4socket_error_report(erp, DK4_E_SOCKET_GETHOSTBYNAME);
}
} else {
dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS);
}
return back;
/* ----- -getaddrinfo */
#endif
}