Refactor fetch_connect() and fetch_bind() to improve readability and avoid
repeating the same DNS lookups. MFC after: 3 weeks
This commit is contained in:
parent
1fa81dab7d
commit
792ef1ae7b
@ -1,5 +1,5 @@
|
|||||||
/*-
|
/*-
|
||||||
* Copyright (c) 1998-2014 Dag-Erling Smørgrav
|
* Copyright (c) 1998-2016 Dag-Erling Smørgrav
|
||||||
* Copyright (c) 2013 Michael Gmelin <freebsd@grem.de>
|
* Copyright (c) 2013 Michael Gmelin <freebsd@grem.de>
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
@ -240,28 +240,84 @@ fetch_ref(conn_t *conn)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Resolve an address
|
||||||
|
*/
|
||||||
|
struct addrinfo *
|
||||||
|
fetch_resolve(const char *addr, int port, int af)
|
||||||
|
{
|
||||||
|
char hbuf[256], sbuf[8];
|
||||||
|
struct addrinfo hints, *res;
|
||||||
|
const char *sep, *host, *service;
|
||||||
|
int err, len;
|
||||||
|
|
||||||
|
/* split address if necessary */
|
||||||
|
err = EAI_SYSTEM;
|
||||||
|
if ((sep = strchr(addr, ':')) != NULL) {
|
||||||
|
len = snprintf(hbuf, sizeof(hbuf),
|
||||||
|
"%.*s", (int)(sep - addr), addr);
|
||||||
|
if (len < 0)
|
||||||
|
return (NULL);
|
||||||
|
if (len >= (int)sizeof(hbuf)) {
|
||||||
|
errno = ENAMETOOLONG;
|
||||||
|
fetch_syserr();
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
host = hbuf;
|
||||||
|
service = sep + 1;
|
||||||
|
} else if (port != 0) {
|
||||||
|
if (port < 1 || port > 65535) {
|
||||||
|
errno = EINVAL;
|
||||||
|
fetch_syserr();
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
if (snprintf(sbuf, sizeof(sbuf), "%d", port) < 0) {
|
||||||
|
fetch_syserr();
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
host = addr;
|
||||||
|
service = sbuf;
|
||||||
|
} else {
|
||||||
|
host = addr;
|
||||||
|
service = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* resolve */
|
||||||
|
fetch_info("resolving host = %s service = %s af = %d",
|
||||||
|
host, service, af);
|
||||||
|
memset(&hints, 0, sizeof(hints));
|
||||||
|
hints.ai_family = af;
|
||||||
|
hints.ai_socktype = SOCK_STREAM;
|
||||||
|
hints.ai_flags = AI_ADDRCONFIG;
|
||||||
|
if ((err = getaddrinfo(host, service, &hints, &res)) != 0) {
|
||||||
|
netdb_seterr(err);
|
||||||
|
fetch_info("getaddrinfo() failed: %s", gai_strerror(err));
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
fetch_info("getaddrinfo() succeeded %p", res);
|
||||||
|
return (res);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Bind a socket to a specific local address
|
* Bind a socket to a specific local address
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
fetch_bind(int sd, int af, const char *addr)
|
fetch_bind(int sd, int af, const char *addr)
|
||||||
{
|
{
|
||||||
struct addrinfo hints, *res, *res0;
|
struct addrinfo *cliai, *ai;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
memset(&hints, 0, sizeof(hints));
|
if ((cliai = fetch_resolve(addr, 0, af)) == NULL)
|
||||||
hints.ai_family = af;
|
|
||||||
hints.ai_socktype = SOCK_STREAM;
|
|
||||||
hints.ai_protocol = 0;
|
|
||||||
if ((err = getaddrinfo(addr, NULL, &hints, &res0)) != 0)
|
|
||||||
return (-1);
|
return (-1);
|
||||||
for (res = res0; res; res = res->ai_next)
|
for (ai = cliai; ai != NULL; ai = ai->ai_next)
|
||||||
if (bind(sd, res->ai_addr, res->ai_addrlen) == 0) {
|
if ((err = bind(sd, ai->ai_addr, ai->ai_addrlen)) == 0)
|
||||||
freeaddrinfo(res0);
|
break;
|
||||||
return (0);
|
if (err != 0)
|
||||||
}
|
fetch_syserr();
|
||||||
freeaddrinfo(res0);
|
freeaddrinfo(cliai);
|
||||||
return (-1);
|
return (err == 0 ? 0 : -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -271,59 +327,77 @@ fetch_bind(int sd, int af, const char *addr)
|
|||||||
conn_t *
|
conn_t *
|
||||||
fetch_connect(const char *host, int port, int af, int verbose)
|
fetch_connect(const char *host, int port, int af, int verbose)
|
||||||
{
|
{
|
||||||
conn_t *conn;
|
struct addrinfo *cais = NULL, *sais = NULL, *cai, *sai;
|
||||||
char pbuf[10];
|
|
||||||
const char *bindaddr;
|
const char *bindaddr;
|
||||||
struct addrinfo hints, *res, *res0;
|
conn_t *conn = NULL;
|
||||||
int sd, err;
|
int err = 0, sd = -1;
|
||||||
|
|
||||||
DEBUG(fprintf(stderr, "---> %s:%d\n", host, port));
|
DEBUG(fprintf(stderr, "---> %s:%d\n", host, port));
|
||||||
|
|
||||||
|
/* resolve server address */
|
||||||
if (verbose)
|
if (verbose)
|
||||||
fetch_info("looking up %s", host);
|
fetch_info("resolving server address: %s:%d", host, port);
|
||||||
|
if ((sais = fetch_resolve(host, port, af)) == NULL)
|
||||||
|
goto fail;
|
||||||
|
fetch_info("resolved");
|
||||||
|
|
||||||
/* look up host name and set up socket address structure */
|
/* resolve client address */
|
||||||
snprintf(pbuf, sizeof(pbuf), "%d", port);
|
|
||||||
memset(&hints, 0, sizeof(hints));
|
|
||||||
hints.ai_family = af;
|
|
||||||
hints.ai_socktype = SOCK_STREAM;
|
|
||||||
hints.ai_protocol = 0;
|
|
||||||
if ((err = getaddrinfo(host, pbuf, &hints, &res0)) != 0) {
|
|
||||||
netdb_seterr(err);
|
|
||||||
return (NULL);
|
|
||||||
}
|
|
||||||
bindaddr = getenv("FETCH_BIND_ADDRESS");
|
bindaddr = getenv("FETCH_BIND_ADDRESS");
|
||||||
|
if (bindaddr != NULL && *bindaddr != '\0') {
|
||||||
|
if (verbose)
|
||||||
|
fetch_info("resolving client address: %s", bindaddr);
|
||||||
|
if ((cais = fetch_resolve(bindaddr, 0, af)) == NULL)
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
if (verbose)
|
/* try each server address in turn */
|
||||||
fetch_info("connecting to %s:%d", host, port);
|
for (err = 0, sai = sais; sai != NULL; sai = sai->ai_next) {
|
||||||
|
/* open socket */
|
||||||
/* try to connect */
|
if ((sd = socket(sai->ai_family, SOCK_STREAM, 0)) < 0)
|
||||||
for (sd = -1, res = res0; res; sd = -1, res = res->ai_next) {
|
goto syserr;
|
||||||
if ((sd = socket(res->ai_family, res->ai_socktype,
|
/* attempt to bind to client address */
|
||||||
res->ai_protocol)) == -1)
|
for (err = 0, cai = cais; cai != NULL; cai = cai->ai_next) {
|
||||||
continue;
|
if (cai->ai_family != sai->ai_family)
|
||||||
if (bindaddr != NULL && *bindaddr != '\0' &&
|
continue;
|
||||||
fetch_bind(sd, res->ai_family, bindaddr) != 0) {
|
if ((err = bind(sd, cai->ai_addr, cai->ai_addrlen)) == 0)
|
||||||
fetch_info("failed to bind to '%s'", bindaddr);
|
break;
|
||||||
close(sd);
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
if (connect(sd, res->ai_addr, res->ai_addrlen) == 0 &&
|
if (err != 0) {
|
||||||
fcntl(sd, F_SETFL, O_NONBLOCK) == 0)
|
if (verbose)
|
||||||
|
fetch_info("failed to bind to %s", bindaddr);
|
||||||
|
goto syserr;
|
||||||
|
}
|
||||||
|
/* attempt to connect to server address */
|
||||||
|
if ((err = connect(sd, sai->ai_addr, sai->ai_addrlen)) == 0)
|
||||||
break;
|
break;
|
||||||
|
/* clean up before next attempt */
|
||||||
close(sd);
|
close(sd);
|
||||||
|
sd = -1;
|
||||||
}
|
}
|
||||||
freeaddrinfo(res0);
|
if (err != 0) {
|
||||||
if (sd == -1) {
|
if (verbose)
|
||||||
fetch_syserr();
|
fetch_info("failed to connect to %s:%s", host, port);
|
||||||
return (NULL);
|
goto syserr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((conn = fetch_reopen(sd)) == NULL) {
|
if ((conn = fetch_reopen(sd)) == NULL)
|
||||||
fetch_syserr();
|
goto syserr;
|
||||||
close(sd);
|
if (cais != NULL)
|
||||||
}
|
freeaddrinfo(cais);
|
||||||
|
if (sais != NULL)
|
||||||
|
freeaddrinfo(sais);
|
||||||
return (conn);
|
return (conn);
|
||||||
|
syserr:
|
||||||
|
fetch_syserr();
|
||||||
|
goto fail;
|
||||||
|
fail:
|
||||||
|
if (sd >= 0)
|
||||||
|
close(sd);
|
||||||
|
if (cais != NULL)
|
||||||
|
freeaddrinfo(cais);
|
||||||
|
if (sais != NULL)
|
||||||
|
freeaddrinfo(sais);
|
||||||
|
return (NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef WITH_SSL
|
#ifdef WITH_SSL
|
||||||
|
@ -76,6 +76,7 @@ void fetch_syserr(void);
|
|||||||
void fetch_info(const char *, ...);
|
void fetch_info(const char *, ...);
|
||||||
int fetch_default_port(const char *);
|
int fetch_default_port(const char *);
|
||||||
int fetch_default_proxy_port(const char *);
|
int fetch_default_proxy_port(const char *);
|
||||||
|
struct addrinfo *fetch_resolve(const char *, int, int);
|
||||||
int fetch_bind(int, int, const char *);
|
int fetch_bind(int, int, const char *);
|
||||||
conn_t *fetch_connect(const char *, int, int, int);
|
conn_t *fetch_connect(const char *, int, int, int);
|
||||||
conn_t *fetch_reopen(int);
|
conn_t *fetch_reopen(int);
|
||||||
|
@ -768,8 +768,8 @@ ftp_transfer(conn_t *conn, const char *oper, const char *file,
|
|||||||
fetch_info("opening data connection");
|
fetch_info("opening data connection");
|
||||||
bindaddr = getenv("FETCH_BIND_ADDRESS");
|
bindaddr = getenv("FETCH_BIND_ADDRESS");
|
||||||
if (bindaddr != NULL && *bindaddr != '\0' &&
|
if (bindaddr != NULL && *bindaddr != '\0' &&
|
||||||
fetch_bind(sd, sa.ss_family, bindaddr) != 0)
|
(e = fetch_bind(sd, sa.ss_family, bindaddr)) != 0)
|
||||||
goto sysouch;
|
goto ouch;
|
||||||
if (connect(sd, (struct sockaddr *)&sa, sa.ss_len) == -1)
|
if (connect(sd, (struct sockaddr *)&sa, sa.ss_len) == -1)
|
||||||
goto sysouch;
|
goto sysouch;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user