%% 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 }