cURL / Mailing Lists / curl-library / Single Mail

curl-library

[RFC PATCH] Add libproxy support

From: David Woodhouse <dwmw2_at_infradead.org>
Date: Tue, 26 Jul 2016 13:22:02 +0100

This adds optional support for using libproxy to determine which proxy
to use for a given URL.

The original libproxy has evolved into a C++ monstrosity which
potentially loads a JavaScript interpreter into the address space of
anything which uses it... but the API is clean and simple.

The PacRunner dæmon provides a service where you can simply ask it,
over D-Bus, which proxy to use for a given URL. Not only does it have a
plugin for the original libproxy, but it also provides its own
replacement libproxy implementation which does *nothing* but field the
call to PacRunner.

I am mentoring a Google Summer of Code project this year which is
fixing NetworkManager to pick up proxy information from DHCP, VPN
servers and other sources, and to allow it to be an explicit part of
per-network configuration — and to poke it into PacRunner
appropriately.

Thus the expectation will be that asking libproxy should *consistently*
give correct answers. Once that's true, I hope to change Linux
distribution packaging guidelines to suggest that packaged applications
*should* be using libproxy's results (or at least PacRunner's results)
by default.

This implements libproxy support for curl because it seems like the
more generic option, rather than talking to PacRunner directly over
D-Bus. This is just the basic functionality; we need to look at how we
make it accessible from the curl command line tool, and/or enable it by
default.

---
 configure.ac                         | 88 ++++++++++++++++++++++++++++++++++++
 docs/libcurl/opts/CURLOPT_LIBPROXY.3 | 46 +++++++++++++++++++
 docs/libcurl/opts/Makefile.am        |  3 ++
 include/curl/curl.h                  |  3 ++
 lib/Makefile.inc                     |  5 +-
 lib/curl_libproxy.c                  | 65 ++++++++++++++++++++++++++
 lib/curl_libproxy.h                  | 37 +++++++++++++++
 lib/easy.c                           |  8 ++++
 lib/url.c                            | 11 +++++
 lib/urldata.h                        |  5 ++
 10 files changed, 269 insertions(+), 2 deletions(-)
 create mode 100644 docs/libcurl/opts/CURLOPT_LIBPROXY.3
 create mode 100644 lib/curl_libproxy.c
 create mode 100644 lib/curl_libproxy.h
diff --git a/configure.ac b/configure.ac
index 60c99ac..5f70969 100644
--- a/configure.ac
+++ b/configure.ac
@@ -168,6 +168,7 @@ curl_verbose_msg="enabled (--disable-verbose)"
    curl_rtmp_msg="no      (--with-librtmp)"
   curl_mtlnk_msg="no      (--with-libmetalink)"
     curl_psl_msg="no      (--with-libpsl)"
+curl_libproxy_msg="no      (--with-libproxy)"
 
     init_ssl_msg=${curl_ssl_msg}
 
@@ -2608,6 +2609,92 @@ if test X"$OPT_LIBSSH2" != Xno; then
 fi
 
 dnl **********************************************************************
+dnl Check for the presence of LIBPROXY libraries and headers
+dnl **********************************************************************
+
+dnl Default to compiler & linker defaults for LIBPROXY files & libraries.
+OPT_LIBPROXY=off
+AC_ARG_WITH(libproxy,dnl
+AC_HELP_STRING([--with-libproxy=PATH],[Where to look for libproxy, PATH points to the LIBPROXY installation; when possible, set the PKG_CONFIG_PATH environment variable instead of using this option])
+AC_HELP_STRING([--without-libproxy], [disable LIBPROXY]),
+  OPT_LIBPROXY=$withval)
+
+if test X"$OPT_LIBPROXY" != Xno; then
+  dnl backup the pre-libproxy variables
+  CLEANLDFLAGS="$LDFLAGS"
+  CLEANCPPFLAGS="$CPPFLAGS"
+  CLEANLIBS="$LIBS"
+
+  case "$OPT_LIBPROXY" in
+  yes)
+    dnl --with-libproxy (without path) used
+    CURL_CHECK_PKGCONFIG(libproxy)
+
+    if test "$PKGCONFIG" != "no" ; then
+      LIB_PROXY=`$PKGCONFIG --libs-only-l libproxy`
+      LD_PROXY=`$PKGCONFIG --libs-only-L libproxy`
+      CPP_PROXY=`$PKGCONFIG --cflags-only-I libproxy`
+      version=`$PKGCONFIG --modversion libproxy`
+      DIR_PROXY=`echo $LD_PROXY | $SED -e 's/-L//'`
+    fi
+
+    ;;
+  off)
+    dnl no --with-libproxy option given, just check default places
+    ;;
+  *)
+    dnl use the given --with-libproxy spot
+    PREFIX_PROXY=$OPT_LIBPROXY
+    ;;
+  esac
+
+  dnl if given with a prefix, we set -L and -I based on that
+  if test -n "$PREFIX_PROXY"; then
+    LIB_PROXY="-lproxy"
+    LD_PROXY=-L${PREFIX_PROXY}/lib$libsuff
+    CPP_PROXY=-I${PREFIX_PROXY}/include
+    DIR_PROXY=${PREFIX_PROXY}/lib$libsuff
+  fi
+
+  LDFLAGS="$LDFLAGS $LD_PROXY"
+  CPPFLAGS="$CPPFLAGS $CPP_PROXY"
+  LIBS="$LIB_PROXY $LIBS"
+
+  AC_CHECK_LIB(proxy, px_proxy_factory_new)
+
+  AC_CHECK_HEADERS(proxy.h,
+    curl_libproxy_msg="enabled"
+    LIBPROXY_ENABLED=1
+    AC_DEFINE(USE_LIBPROXY, 1, [if libproxy is in use])
+    AC_SUBST(USE_LIBPROXY, [1])
+  )
+
+  if test X"$OPT_LIBPROXY" != Xoff &&
+     test "$LIBPROXY_ENABLED" != "1"; then
+    AC_MSG_ERROR([libproxy libs and/or directories were not found where specified!])
+  fi
+
+  if test "$LIBPROXY_ENABLED" = "1"; then
+    if test -n "$DIR_LIBPROXY"; then
+       dnl when the libproxy shared libs were found in a path that the run-time
+       dnl linker doesn't search through, we need to add it to LD_LIBRARY_PATH
+       dnl to prevent further configure tests to fail due to this
+
+       if test "x$cross_compiling" != "xyes"; then
+         LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$DIR_LIBPROXY"
+         export LD_LIBRARY_PATH
+         AC_MSG_NOTICE([Added $DIR_LIBPROXY to LD_LIBRARY_PATH])
+       fi
+    fi
+  else
+    dnl no libproxy; revert back to clean variables
+    LDFLAGS=$CLEANLDFLAGS
+    CPPFLAGS=$CLEANCPPFLAGS
+    LIBS=$CLEANLIBS
+  fi
+fi
+
+dnl **********************************************************************
 dnl Check for the presence of LIBRTMP libraries and headers
 dnl **********************************************************************
 
@@ -3894,6 +3981,7 @@ AC_MSG_NOTICE([Configured to build curl/libcurl:
   metalink support: ${curl_mtlnk_msg}
   PSL support:      ${curl_psl_msg}
   HTTP2 support:    ${curl_h2_msg}
+  libproxy support: ${curl_libproxy_msg}
   Protocols:        ${SUPPORT_PROTOCOLS}
 ])
 
diff --git a/docs/libcurl/opts/CURLOPT_LIBPROXY.3 b/docs/libcurl/opts/CURLOPT_LIBPROXY.3
new file mode 100644
index 0000000..bf01c68
--- /dev/null
+++ b/docs/libcurl/opts/CURLOPT_LIBPROXY.3
@@ -0,0 +1,46 @@
+.\" **************************************************************************
+.\" *                                  _   _ ____  _
+.\" *  Project                     ___| | | |  _ \| |
+.\" *                             / __| | | | |_) | |
+.\" *                            | (__| |_| |  _ <| |___
+.\" *                             \___|\___/|_| \_\_____|
+.\" *
+.\" * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel_at_haxx.se>, et al.
+.\" *
+.\" * This software is licensed as described in the file COPYING, which
+.\" * you should have received as part of this distribution. The terms
+.\" * are also available at http://curl.haxx.se/docs/copyright.html.
+.\" *
+.\" * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+.\" * copies of the Software, and permit persons to whom the Software is
+.\" * furnished to do so, under the terms of the COPYING file.
+.\" *
+.\" * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+.\" * KIND, either express or implied.
+.\" *
+.\" **************************************************************************
+.\"
+.TH CURLOPT_LIBPROXY 3 "26 Jul 2016" "libcurl 7.50.1" "curl_easy_setopt options"
+.SH NAME
+CURLOPT_LIBPROXY \- enable automatic proxy detection via libproxy
+.SH SYNOPSIS
+#include <curl/curl.h>
+
+CURLcode curl_easy_setopt(CURL *handle, CURLOPT_LIBPROXY, long enable);
+.SH DESCRIPTION
+Set \fIenable\fP to 1L to tell libcurl to use the libproxy API to determine
+which proxy, if any, to use for each request according to the system
+configuration.
+.SH DEFAULT
+0 (off)
+.SH PROTOCOLS
+All except file://. Note that some protocols don't do very well over proxy.
+.SH EXAMPLE
+TODO
+.SH AVAILABILITY
+Added in 7.50.1
+.SH RETURN VALUE
+Returns CURLE_OK if libproxy is supported, CURLE_NOT_BUILT_IN if not.
+.SH "SEE ALSO"
+.BR CURLOPT_PROXYPORT "(3), " CURLOPT_HTTPPROXYTUNNEL "(3), "
+.BR CURLOPT_PROXYTYPE "(3), " CURLOPT_PROXY "(3)" "
diff --git a/docs/libcurl/opts/Makefile.am b/docs/libcurl/opts/Makefile.am
index a3fc064..3c453b5 100644
--- a/docs/libcurl/opts/Makefile.am
+++ b/docs/libcurl/opts/Makefile.am
@@ -172,6 +172,7 @@ man_MANS =                                      \
  CURLOPT_ISSUERCERT.3                           \
  CURLOPT_KEYPASSWD.3                            \
  CURLOPT_KRBLEVEL.3                             \
+ CURLOPT_LIBPROXY.3                             \
  CURLOPT_LOCALPORT.3                            \
  CURLOPT_LOCALPORTRANGE.3                       \
  CURLOPT_LOGIN_OPTIONS.3                        \
@@ -459,6 +460,7 @@ HTMLPAGES =                                     \
  CURLOPT_ISSUERCERT.html                        \
  CURLOPT_KEYPASSWD.html                         \
  CURLOPT_KRBLEVEL.html                          \
+ CURLOPT_LIBPROXY.html                          \
  CURLOPT_LOCALPORT.html                         \
  CURLOPT_LOCALPORTRANGE.html                    \
  CURLOPT_LOGIN_OPTIONS.html                     \
@@ -746,6 +748,7 @@ PDFPAGES =                                      \
  CURLOPT_ISSUERCERT.pdf                         \
  CURLOPT_KEYPASSWD.pdf                          \
  CURLOPT_KRBLEVEL.pdf                           \
+ CURLOPT_LIBPROXY.pdf                           \
  CURLOPT_LOCALPORT.pdf                          \
  CURLOPT_LOCALPORTRANGE.pdf                     \
  CURLOPT_LOGIN_OPTIONS.pdf                      \
diff --git a/include/curl/curl.h b/include/curl/curl.h
index 516ede6..b8822f2 100644
--- a/include/curl/curl.h
+++ b/include/curl/curl.h
@@ -1689,6 +1689,9 @@ typedef enum {
   /* Set TCP Fast Open */
   CINIT(TCP_FASTOPEN, LONG, 244),
 
+  /* Use libproxy to determine which proxy to use */
+  CINIT(LIBPROXY, LONG, 245),
+
   CURLOPT_LASTENTRY /* the last unused */
 } CURLoption;
 
diff --git a/lib/Makefile.inc b/lib/Makefile.inc
index 0ed998c..d34f23d 100644
--- a/lib/Makefile.inc
+++ b/lib/Makefile.inc
@@ -53,7 +53,8 @@ LIB_CFILES = file.c timeval.c base64.c hostip.c progress.c formdata.c   \
   http_proxy.c non-ascii.c asyn-ares.c asyn-thread.c curl_gssapi.c      \
   http_ntlm.c curl_ntlm_wb.c curl_ntlm_core.c curl_sasl.c               \
   curl_multibyte.c hostcheck.c conncache.c pipeline.c dotdot.c          \
-  x509asn1.c http2.c smb.c curl_endian.c curl_des.c system_win32.c
+  x509asn1.c http2.c smb.c curl_endian.c curl_des.c system_win32.c      \
+  curl_libproxy.c
 
 LIB_HFILES = arpa_telnet.h netrc.h file.h timeval.h hostip.h progress.h \
   formdata.h cookie.h http.h sendf.h ftp.h url.h dict.h if2ip.h         \
@@ -72,7 +73,7 @@ LIB_HFILES = arpa_telnet.h netrc.h file.h timeval.h hostip.h progress.h \
   curl_sasl.h curl_multibyte.h hostcheck.h conncache.h                  \
   curl_setup_once.h multihandle.h setup-vms.h pipeline.h dotdot.h       \
   x509asn1.h http2.h sigpipe.h smb.h curl_endian.h curl_des.h           \
-  curl_printf.h system_win32.h
+  curl_printf.h system_win32.h curl_libproxy.h
 
 LIB_RCFILES = libcurl.rc
 
diff --git a/lib/curl_libproxy.c b/lib/curl_libproxy.c
new file mode 100644
index 0000000..b0bf2be
--- /dev/null
+++ b/lib/curl_libproxy.c
@@ -0,0 +1,65 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2011 - 2015, Daniel Stenberg, <daniel_at_haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifdef USE_LIBPROXY
+
+#include "urldata.h"
+
+#include <proxy.h>
+
+static pxProxyFactory *factory;
+
+CURLcode Curl_libproxy_global_init(void)
+{
+  factory = px_proxy_factory_new();
+}
+
+void Curl_libproxy_global_cleanup(void)
+{
+  if (factory)
+    px_proxy_factory_free(factory);
+  factory = NULL;
+}
+
+char *Curl_libproxy_detect_proxy(const char *url)
+{
+  char *result = NULL;
+
+  if (factory) {
+    char **libproxy_results = px_proxy_factory_get_proxies(factory, url);
+    if (libproxy_results) {
+      int i;
+
+      /* We only cope with one; can't fall back on failure */
+      result = libproxy_results[0];
+      for (i=1; libproxy_results[i]; i++)
+        free(libproxy_results[i]);
+      free(libproxy_results);
+    }
+  }
+
+  return result;
+}
+
+#endif /* USE_LIBPROXY */
diff --git a/lib/curl_libproxy.h b/lib/curl_libproxy.h
new file mode 100644
index 0000000..8541c1e
--- /dev/null
+++ b/lib/curl_libproxy.h
@@ -0,0 +1,37 @@
+#ifndef HEADER_CURL_LIBPROXY_H
+#define HEADER_CURL_LIBPROXY_H
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2011 - 2015, Daniel Stenberg, <daniel_at_haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+#include "urldata.h"
+
+#ifdef USE_LIBPROXY
+
+CURLcode Curl_libproxy_global_init(void);
+void Curl_libproxy_global_cleanup(void);
+
+char *Curl_libproxy_detect_proxy(const char *url);
+
+#endif /* USE_LIBPROXY */
+
+#endif /* HEADER_CURL_LIBPROXY_H */
diff --git a/lib/easy.c b/lib/easy.c
index dc7139f..78053f1 100644
--- a/lib/easy.c
+++ b/lib/easy.c
@@ -141,6 +141,10 @@ static CURLcode win32_init(void)
   }
 #endif
 
+#ifdef USE_LIBPROXY
+  Curl_libproxy_global_init();
+#endif
+
   return CURLE_OK;
 }
 
@@ -360,6 +364,10 @@ void curl_global_cleanup(void)
   (void)libssh2_exit();
 #endif
 
+#ifdef USE_LIBPROXY
+  Curl_libproxy_global_cleanup();
+#endif
+
   init_flags  = 0;
 }
 
diff --git a/lib/url.c b/lib/url.c
index 258a286..c9f2fdc 100644
--- a/lib/url.c
+++ b/lib/url.c
@@ -2695,6 +2695,13 @@ CURLcode Curl_setopt(struct Curl_easy *data, CURLoption option,
   case CURLOPT_CONNECT_TO:
     data->set.connect_to = va_arg(param, struct curl_slist *);
     break;
+  case CURLOPT_LIBPROXY:
+#ifdef USE_LIBPROXY
+    data->set.libproxy = (0 != va_arg(param, long))?TRUE:FALSE;
+#else
+    result = CURLE_NOT_BUILT_IN;
+#endif
+    break;
   default:
     /* unknown tag and its companion, just ignore: */
     result = CURLE_UNKNOWN_OPTION;
@@ -5925,6 +5932,10 @@ static CURLcode create_conn(struct Curl_easy *data,
     free(proxy);  /* proxy is in exception list */
     proxy = NULL;
   }
+#ifdef USE_LIBPROXY
+  else if (data->set.libproxy)
+    proxy = Curl_libproxy_detect_proxy(data->change.url);
+#endif
   else if(!proxy)
     proxy = detect_proxy(conn);
 
diff --git a/lib/urldata.h b/lib/urldata.h
index 611c5a7..71002f0 100644
--- a/lib/urldata.h
+++ b/lib/urldata.h
@@ -198,6 +198,10 @@
 #include <libssh2_sftp.h>;
 #endif /* HAVE_LIBSSH2_H */
 
+#ifdef USE_LIBPROXY
+#include "curl_libproxy.h"
+#endif
+
 /* Download buffer size, keep it fairly big for speed reasons */
 #undef BUFSIZE
 #define BUFSIZE CURL_MAX_WRITE_SIZE
@@ -1676,6 +1680,7 @@ struct UserDefined {
   bool path_as_is;      /* allow dotdots? */
   bool pipewait;        /* wait for pipe/multiplex status before starting a
                            new connection */
+  bool libproxy;        /* use libproxy to discover proxies */
   long expect_100_timeout; /* in milliseconds */
 
   struct Curl_easy *stream_depends_on;
-- 
2.7.4
-- 
David Woodhouse                            Open Source Technology Centre
David.Woodhouse_at_intel.com                              Intel Corporation

-------------------------------------------------------------------
List admin: https://cool.haxx.se/list/listinfo/curl-library
Etiquette: https://curl.haxx.se/mail/etiquette.html

  • application/x-pkcs7-signature attachment: smime.p7s
Received on 2016-07-26