diff options
author | Dave Reisner <dreisner@archlinux.org> | 2012-01-22 03:31:39 +0100 |
---|---|---|
committer | Dan McGee <dan@archlinux.org> | 2012-01-23 19:14:58 +0100 |
commit | 44f146f232be5203fb01ad35fdf73122838df97c (patch) | |
tree | ebf99d8f46d91fe3f9f2c383e97c8b2dbb7b0bc2 | |
parent | edd4276bbf3d21a7353e3d67ce6639246ef8032d (diff) | |
download | pacman-44f146f232be5203fb01ad35fdf73122838df97c.tar.gz pacman-44f146f232be5203fb01ad35fdf73122838df97c.tar.xz |
lib/dload: enforce usage of TCP keepalives
This is particularly important in the case of FTP control connections,
which may be closed by rogue NAT/firewall devices detecting idle
connections on larger transfers which may take 5-10+ minutes.
Signed-off-by: Dave Reisner <dreisner@archlinux.org>
Signed-off-by: Dan McGee <dan@archlinux.org>
-rw-r--r-- | configure.ac | 3 | ||||
-rw-r--r-- | lib/libalpm/dload.c | 53 |
2 files changed, 55 insertions, 1 deletions
diff --git a/configure.ac b/configure.ac index a8b142ea..0bf11e99 100644 --- a/configure.ac +++ b/configure.ac @@ -173,7 +173,8 @@ AM_CONDITIONAL([HAVE_LIBGPGME], [test "x$with_gpgme" = "xyes"]) # Checks for header files. AC_CHECK_HEADERS([fcntl.h float.h glob.h libintl.h limits.h locale.h \ - mntent.h stddef.h string.h sys/ioctl.h \ + mntent.h netinet/in.h netinet/tcp.h \ + stddef.h string.h sys/ioctl.h \ sys/mnttab.h sys/mount.h \ sys/param.h sys/statvfs.h sys/time.h sys/types.h \ sys/ucred.h syslog.h termios.h wchar.h]) diff --git a/lib/libalpm/dload.c b/lib/libalpm/dload.c index 6fe4a323..f5af7c64 100644 --- a/lib/libalpm/dload.c +++ b/lib/libalpm/dload.c @@ -25,11 +25,19 @@ #include <errno.h> #include <string.h> #include <unistd.h> +#include <sys/socket.h> /* setsockopt, SO_KEEPALIVE */ #include <sys/time.h> #include <sys/types.h> #include <sys/stat.h> #include <signal.h> +#ifdef HAVE_NETINET_IN_H +#include <netinet/in.h> /* IPPROTO_TCP */ +#endif +#ifdef HAVE_NETINET_TCP_H +#include <netinet/tcp.h> /* TCP_KEEPINTVL, TCP_KEEPIDLE */ +#endif + #ifdef HAVE_LIBCURL #include <curl/curl.h> #endif @@ -217,6 +225,47 @@ static size_t parse_headers(void *ptr, size_t size, size_t nmemb, void *user) return realsize; } +static int dload_sockopt_cb(void *userdata, curl_socket_t curlfd, + curlsocktype purpose) +{ + alpm_handle_t *handle = userdata; + int optval = 1; + + /* this whole method is to prevent FTP control connections from going sour + * during a long data transfer; crappy firewalls love to drop otherwise idle + * connections if there is no traffic. */ + if(purpose != CURLSOCKTYPE_IPCXN) { + return 0; + } + + /* don't abort operation if any setsockopt fails, just log to debug */ + if(setsockopt(curlfd, SOL_SOCKET, SO_KEEPALIVE, (void *)&optval, + sizeof(optval)) < 0) { + _alpm_log(handle, ALPM_LOG_DEBUG, + "Failed to set SO_KEEPALIVE on fd %d\n", curlfd); + } + else { +#ifdef TCP_KEEPIDLE + optval = 60; + if(setsockopt(curlfd, IPPROTO_TCP, TCP_KEEPIDLE, (void *)&optval, + sizeof(optval)) < 0) { + _alpm_log(handle, ALPM_LOG_DEBUG, + "Failed to set TCP_KEEPIDLE on fd %d\n", curlfd); + } +#endif +#ifdef TCP_KEEPINTVL + optval = 60; + if(setsockopt(curlfd, IPPROTO_TCP, TCP_KEEPINTVL, (void *)&optval, + sizeof(optval)) < 0) { + _alpm_log(handle, ALPM_LOG_DEBUG, + "Failed to set TCP_KEEPINTVL on fd %d\n", curlfd); + } +#endif + } + + return 0; +} + static void curl_set_handle_opts(struct dload_payload *payload, CURL *curl, char *error_buffer) { @@ -241,6 +290,8 @@ static void curl_set_handle_opts(struct dload_payload *payload, curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, parse_headers); curl_easy_setopt(curl, CURLOPT_WRITEHEADER, (void *)payload); curl_easy_setopt(curl, CURLOPT_NETRC, CURL_NETRC_OPTIONAL); + curl_easy_setopt(curl, CURLOPT_SOCKOPTFUNCTION, dload_sockopt_cb); + curl_easy_setopt(curl, CURLOPT_SOCKOPTDATA, (void *)handle); _alpm_log(handle, ALPM_LOG_DEBUG, "url: %s\n", payload->fileurl); @@ -392,6 +443,8 @@ static int curl_download_internal(struct dload_payload *payload, /* perform transfer */ payload->curlerr = curl_easy_perform(curl); + _alpm_log(handle, ALPM_LOG_DEBUG, "curl returned error %d from transfer\n", + payload->curlerr); /* disconnect relationships from the curl handle for things that might go out * of scope, but could still be touched on connection teardown. This really |