#include <string.h> /* strerror */
#include <errno.h>
#include <unistd.h> /* close */
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h> /* IPPROTO_TCP */
/* ``tcpConnect(host, port, errFunc, errMsg)'' looks up the IP addresses for
* host `host' (including IPv6 and any other address families if your system
* supports them) and the port that provides service `port' (e.g., "http" or
* "gopher"; a number as a decimal string is also acceptable). It then
* attempts to create a TCP connection to the given port of each IP address in
* turn, and, if successful, returns a socket descriptor for the new
* connection. If any part of the process is unsuccessful, -1 is returned. If
* an error occurs and they are non-NULL, *errFunc and *errMsg will hold
* pointers to static strings giving the name of the failed function and an
* error message, respectively. If tcpConnect() returns successfully, the
* values of *errFunc and *errMsg are indeterminate.
*
* Note that tcpConnect() relies upon the somewhat modern yet incredibly
* useful getaddrinfo(3). If your system does not have this function, feel
* free to cry in the corner.
*/
int tcpConnect(const char* host, const char* port, const char** errFunc,
const char** errMsg) {
static struct addrinfo hints = {.ai_family = AF_UNSPEC, .ai_socktype
= SOCK_STREAM, .ai_protocol = IPPROTO_TCP};
struct addrinfo* addrs;
int retval = getaddrinfo(host, port, &hints, &addrs);
if (retval != 0) {
if (errFunc != NULL) *errFunc = "getaddrinfo";
if (errMsg != NULL) *errMsg = gai_strerror(retval);
return -1;
}
int sock = -1;
for (struct addrinfo* res = addrs; res != NULL; res = res->ai_next) {
sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (sock < 0) {
if (errFunc != NULL) *errFunc = "socket";
if (errMsg != NULL) *errMsg = strerror(errno);
continue;
}
if (connect(sock, res->ai_addr, res->ai_addrlen) < 0) {
close(sock);
sock = -1;
if (errFunc != NULL) *errFunc = "connect";
if (errMsg != NULL) *errMsg = strerror(errno);
continue;
}
break;
}
/* Every specification of getaddrinfo() I've read says that addr points to at
* least one struct addrinfo, so if an implementation assigns it a NULL value,
* causing sock to be -1 with *errMsg == NULL, that's not my fault. */
freeaddrinfo(addrs);
return sock;
}