IPv6 support

inspired by a patch by "Todd T. Fries" <todd@fries.net>.
This commit is contained in:
Oswald Buddenhagen 2013-09-01 17:35:31 +02:00
parent 4a39cae8c4
commit 3ceb553102
3 changed files with 69 additions and 0 deletions

View File

@ -35,6 +35,15 @@ AC_CHECK_LIB(socket, socket, [SOCK_LIBS="-lsocket"])
AC_CHECK_LIB(nsl, inet_ntoa, [SOCK_LIBS="$SOCK_LIBS -lnsl"]) AC_CHECK_LIB(nsl, inet_ntoa, [SOCK_LIBS="$SOCK_LIBS -lnsl"])
AC_SUBST(SOCK_LIBS) AC_SUBST(SOCK_LIBS)
have_ipv6=true
sav_LDFLAGS=$LDFLAGS
LDFLAGS="$LDFLAGS $SOCK_LIBS"
AC_CHECK_FUNCS(getaddrinfo inet_ntop, , [have_ipv6=false])
LDFLAGS=$sav_LDFLAGS
if $have_ipv6; then
AC_DEFINE(HAVE_IPV6, 1, [if your libc has IPv6 support])
fi
have_ssl_paths= have_ssl_paths=
AC_ARG_WITH(ssl, AC_ARG_WITH(ssl,
AC_HELP_STRING([--with-ssl[=PATH]], [where to look for SSL [detect]]), AC_HELP_STRING([--with-ssl[=PATH]], [where to look for SSL [detect]]),

View File

@ -85,7 +85,11 @@ typedef struct {
int fd; int fd;
int state; int state;
const server_conf_t *conf; /* needed during connect */ const server_conf_t *conf; /* needed during connect */
#ifdef HAVE_IPV6
struct addrinfo *addrs, *curr_addr; /* needed during connect */
#else
char **curr_addr; /* needed during connect */ char **curr_addr; /* needed during connect */
#endif
char *name; char *name;
#ifdef HAVE_LIBSSL #ifdef HAVE_LIBSSL
SSL *ssl; SSL *ssl;

View File

@ -346,6 +346,24 @@ socket_connect( conn_t *sock, void (*cb)( int ok, void *aux ) )
info( "\vok\n" ); info( "\vok\n" );
socket_connected( sock ); socket_connected( sock );
} else { } else {
#ifdef HAVE_IPV6
int gaierr;
struct addrinfo hints;
memset( &hints, 0, sizeof(hints) );
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_V4MAPPED | AI_ADDRCONFIG;
infon( "Resolving %s... ", conf->host );
if ((gaierr = getaddrinfo( conf->host, NULL, &hints, &sock->addrs ))) {
error( "IMAP error: Cannot resolve server '%s': %s\n", conf->host, gai_strerror( gaierr ) );
socket_connect_bail( sock );
return;
}
info( "\vok\n" );
sock->curr_addr = sock->addrs;
#else
struct hostent *he; struct hostent *he;
infon( "Resolving %s... ", conf->host ); infon( "Resolving %s... ", conf->host );
@ -358,6 +376,7 @@ socket_connect( conn_t *sock, void (*cb)( int ok, void *aux ) )
info( "\vok\n" ); info( "\vok\n" );
sock->curr_addr = he->h_addr_list; sock->curr_addr = he->h_addr_list;
#endif
socket_connect_one( sock ); socket_connect_one( sock );
} }
} }
@ -367,11 +386,19 @@ socket_connect_one( conn_t *sock )
{ {
int s; int s;
ushort port; ushort port;
#ifdef HAVE_IPV6
struct addrinfo *ai;
#else
struct { struct {
struct sockaddr_in ai_addr[1]; struct sockaddr_in ai_addr[1];
} ai[1]; } ai[1];
#endif
#ifdef HAVE_IPV6
if (!(ai = sock->curr_addr)) {
#else
if (!*sock->curr_addr) { if (!*sock->curr_addr) {
#endif
error( "No working address found for %s\n", sock->conf->host ); error( "No working address found for %s\n", sock->conf->host );
socket_connect_bail( sock ); socket_connect_bail( sock );
return; return;
@ -382,17 +409,32 @@ socket_connect_one( conn_t *sock )
sock->conf->use_imaps ? 993 : sock->conf->use_imaps ? 993 :
#endif #endif
143; 143;
#ifdef HAVE_IPV6
if (ai->ai_family == AF_INET6) {
struct sockaddr_in6 *in6 = ((struct sockaddr_in6 *)ai->ai_addr);
char sockname[64];
in6->sin6_port = htons( port );
nfasprintf( &sock->name, "%s ([%s]:%hu)",
sock->conf->host, inet_ntop( AF_INET6, &in6->sin6_addr, sockname, sizeof(sockname) ), port );
} else
#endif
{ {
struct sockaddr_in *in = ((struct sockaddr_in *)ai->ai_addr); struct sockaddr_in *in = ((struct sockaddr_in *)ai->ai_addr);
#ifndef HAVE_IPV6
memset( in, 0, sizeof(*in) ); memset( in, 0, sizeof(*in) );
in->sin_family = AF_INET; in->sin_family = AF_INET;
in->sin_addr.s_addr = *((int *)*sock->curr_addr); in->sin_addr.s_addr = *((int *)*sock->curr_addr);
#endif
in->sin_port = htons( port ); in->sin_port = htons( port );
nfasprintf( &sock->name, "%s (%s:%hu)", nfasprintf( &sock->name, "%s (%s:%hu)",
sock->conf->host, inet_ntoa( in->sin_addr ), port ); sock->conf->host, inet_ntoa( in->sin_addr ), port );
} }
#ifdef HAVE_IPV6
s = socket( ai->ai_family, SOCK_STREAM, 0 );
#else
s = socket( PF_INET, SOCK_STREAM, 0 ); s = socket( PF_INET, SOCK_STREAM, 0 );
#endif
if (s < 0) { if (s < 0) {
perror( "socket" ); perror( "socket" );
exit( 1 ); exit( 1 );
@ -402,7 +444,11 @@ socket_connect_one( conn_t *sock )
add_fd( s, socket_fd_cb, sock ); add_fd( s, socket_fd_cb, sock );
infon( "Connecting to %s... ", sock->name ); infon( "Connecting to %s... ", sock->name );
#ifdef HAVE_IPV6
if (connect( s, ai->ai_addr, ai->ai_addrlen )) {
#else
if (connect( s, ai->ai_addr, sizeof(*ai->ai_addr) )) { if (connect( s, ai->ai_addr, sizeof(*ai->ai_addr) )) {
#endif
if (errno != EINPROGRESS) { if (errno != EINPROGRESS) {
socket_connect_failed( sock ); socket_connect_failed( sock );
return; return;
@ -423,13 +469,20 @@ socket_connect_failed( conn_t *conn )
socket_close_internal( conn ); socket_close_internal( conn );
free( conn->name ); free( conn->name );
conn->name = 0; conn->name = 0;
#ifdef HAVE_IPV6
conn->curr_addr = conn->curr_addr->ai_next;
#else
conn->curr_addr++; conn->curr_addr++;
#endif
socket_connect_one( conn ); socket_connect_one( conn );
} }
static void static void
socket_connected( conn_t *conn ) socket_connected( conn_t *conn )
{ {
#ifdef HAVE_IPV6
freeaddrinfo( conn->addrs );
#endif
conf_fd( conn->fd, 0, POLLIN ); conf_fd( conn->fd, 0, POLLIN );
conn->state = SCK_READY; conn->state = SCK_READY;
conn->callbacks.connect( 1, conn->callback_aux ); conn->callbacks.connect( 1, conn->callback_aux );
@ -438,6 +491,9 @@ socket_connected( conn_t *conn )
static void static void
socket_connect_bail( conn_t *conn ) socket_connect_bail( conn_t *conn )
{ {
#ifdef HAVE_IPV6
freeaddrinfo( conn->addrs );
#endif
free( conn->name ); free( conn->name );
conn->name = 0; conn->name = 0;
conn->callbacks.connect( 0, conn->callback_aux ); conn->callbacks.connect( 0, conn->callback_aux );