[systemd-devel] [RFC 3/6] proxy-discoveryd: Add PAC support through duktape js engine

Tomasz Bursztyka tomasz.bursztyka at linux.intel.com
Fri Apr 10 05:17:40 PDT 2015


The proxy PAC file - basically a javascript file - will be executed through
duktape js engine (duktape.org). For this to work, one will need to embed
duktape.h/c files in src/proxy-discovery/.

The current support is sub-optimal as it might generate context switches
via ioctls or netlink calls. This will be fixed later on.
---
 Makefile.am                                |   7 ++
 src/proxy-discovery/proxy-discoveryd-pac.c | 185 +++++++++++++++++++++++++++++
 src/proxy-discovery/proxy-discoveryd.h     |  13 ++
 3 files changed, 205 insertions(+)
 create mode 100644 src/proxy-discovery/proxy-discoveryd-pac.c

diff --git a/Makefile.am b/Makefile.am
index 6e839b9..25ea0dd 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -5895,12 +5895,19 @@ rootlibexec_PROGRAMS += \
 	systemd-proxy-discoveryd
 
 systemd_proxy_discoveryd_SOURCES = \
+	src/proxy-discovery/duktape.h \
+	src/proxy-discovery/duktape.c \
 	src/proxy-discovery/proxy-discoveryd.c \
 	src/proxy-discovery/proxy-discoveryd.h \
 	src/proxy-discovery/proxy-discoveryd-manager.c \
+	src/proxy-discovery/proxy-discoveryd-pac.c \
 	src/proxy-discovery/proxy-discoveryd-proxy.c \
 	src/proxy-discovery/proxy-discoveryd-proxy-gperf.c
 
+systemd_proxy_discoveryd_CFLAGS = \
+	$(AM_CFLAGS) \
+	-fno-fast-math
+
 systemd_proxy_discoveryd_LDADD = \
 	libsystemd-internal.la \
 	libsystemd-shared.la
diff --git a/src/proxy-discovery/proxy-discoveryd-pac.c b/src/proxy-discovery/proxy-discoveryd-pac.c
new file mode 100644
index 0000000..5c779bb
--- /dev/null
+++ b/src/proxy-discovery/proxy-discoveryd-pac.c
@@ -0,0 +1,185 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright (C) 2015 Intel Corporation. All rights reserved.
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "duktape.h"
+
+#include "proxy-discoveryd.h"
+#include "local-addresses.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <net/if.h>
+
+struct PAC {
+        duk_context *ctx;
+};
+
+static int get_addresses_from_interface(int ifindex, union in_addr_union *address) {
+        struct ifreq ifr = {};
+        int sk;
+
+        sk = socket(AF_INET, SOCK_DGRAM, 0);
+        if (sk < 0)
+                return -1;
+
+        ifr.ifr_ifindex = ifindex;
+
+        if (ioctl(sk, SIOCGIFNAME, &ifr) < 0 || ioctl(sk, SIOCGIFADDR, &ifr) < 0) {
+                close(sk);
+                return -1;
+        }
+
+        address->in = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
+
+        close(sk);
+
+        return 0;
+}
+
+static int pac_dns_resolve(duk_context *ctx) {
+        _cleanup_free_ char *address = NULL;
+        struct addrinfo hints = {};
+        struct addrinfo *res, *addr;
+        const char *hostname;
+        int r;
+
+        hostname = duk_require_string(ctx, 0);
+
+        hints.ai_family = AF_INET;
+
+        r = getaddrinfo(hostname, NULL, &hints, &res);
+        if (r != 0)
+                return 0;
+
+        for (addr = res; addr; addr = addr->ai_next) {
+                union in_addr_union a;
+
+                if (addr->ai_family != AF_INET ||
+                                addr->ai_addrlen != sizeof(struct sockaddr_in))
+                        continue;
+
+                a.in = ((struct sockaddr_in *) addr->ai_addr)->sin_addr;
+                in_addr_to_string(AF_INET, &a, &address);
+
+                break;
+        }
+
+        freeaddrinfo(res);
+
+        duk_push_string(ctx, address);
+
+        return 1;
+}
+
+static int pac_my_ip_address(duk_context *ctx) {
+        _cleanup_free_ struct local_address *gateways = NULL;
+        _cleanup_free_ char *address = NULL;
+        union in_addr_union addr;
+        int r;
+
+        r = local_gateways(NULL, 0, AF_INET, &gateways);
+        if (r <= 0)
+                return 0;
+
+        r = get_addresses_from_interface(gateways->ifindex, &addr);
+        if (r < 0)
+                return 0;
+
+        in_addr_to_string(AF_INET, &addr, &address);
+
+        duk_push_string(ctx, address);
+
+        return 1;
+}
+
+static int create_context(struct PAC *pac, char *pac_file, void *user_data) {
+        duk_context *ctx;
+
+        ctx = duk_create_heap(NULL, NULL, NULL, NULL, NULL);
+        if (!ctx)
+                return -ENOMEM;
+
+        duk_push_global_object(ctx);
+        duk_push_c_function(ctx, pac_dns_resolve, 1);
+        duk_put_prop_string(ctx, -2, "dnsResolve");
+        duk_push_c_function(ctx, pac_my_ip_address, 0);
+        duk_put_prop_string(ctx, -2, "myIpAddress");
+
+        duk_push_pointer(ctx, user_data);
+        duk_put_prop_string(ctx, -2, "_user_data_");
+
+        duk_pop(ctx);
+
+        if (duk_peval_file(ctx, pac_file) != 0) {
+                duk_destroy_heap(ctx);
+                return -EINVAL;
+        }
+
+        pac->ctx = ctx;
+
+        return 0;
+}
+
+int pac_load(Proxy *proxy, struct PAC **ret) {
+        _cleanup_pac_free_ struct PAC *pac = NULL;
+        int r;
+
+        assert(proxy);
+        assert(ret);
+
+        pac = new0(struct PAC, 1);
+        if (!pac)
+                return -ENOMEM;
+
+        r = create_context(pac, proxy->pac_path, proxy);
+        if (r < 0)
+                return r;
+
+        *ret = pac;
+        pac = NULL;
+
+        return 0;
+}
+
+void pac_free(struct PAC *pac) {
+        if (pac->ctx)
+                duk_destroy_heap(pac->ctx);
+
+        free(pac);
+}
+
+int pac_execute(struct PAC *pac, const char *url, const char *host, char **ret) {
+        duk_push_global_object(pac->ctx);
+        duk_get_prop_string(pac->ctx, -1, "FindProxyForURL");
+        duk_push_string(pac->ctx, url);
+        duk_push_string(pac->ctx, host);
+
+        if (duk_pcall(pac->ctx, 2) != DUK_EXEC_SUCCESS)
+                 return -1;
+
+        *ret = strdup(duk_to_string(pac->ctx, -1));
+
+        duk_pop(pac->ctx);
+        duk_pop(pac->ctx);
+
+        return 0;
+}
diff --git a/src/proxy-discovery/proxy-discoveryd.h b/src/proxy-discovery/proxy-discoveryd.h
index f51fca5..17c5378 100644
--- a/src/proxy-discovery/proxy-discoveryd.h
+++ b/src/proxy-discovery/proxy-discoveryd.h
@@ -35,10 +35,13 @@ struct Manager {
         LIST_HEAD(Proxy, default_proxies);
 };
 
+struct PAC;
+
 struct Proxy {
         Manager *manager;
 
         char *pac_path;
+        struct PAC *pac;
 
         LIST_FIELDS(Proxy, proxy_next);
 };
@@ -63,3 +66,13 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(Proxy*, proxy_free);
 #define _cleanup_proxy_free_ _cleanup_(proxy_freep)
 
 const struct ConfigPerfItem* proxy_discoveryd_proxy_gperf_lookup(const char *key, unsigned length);
+
+/* PAC */
+
+int pac_load(Proxy *proxy, struct PAC **ret);
+void pac_free(struct PAC *pac);
+
+int pac_execute(struct PAC *pac, const char *url, const char *host, char **ret);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct PAC*, pac_free);
+#define _cleanup_pac_free_ _cleanup_(pac_freep)
-- 
2.0.5



More information about the systemd-devel mailing list