[PATCH 6/9] hs20: Bind curl, including dns, to wlan interface.

[email protected]
Newsgroups gmane.linux.drivers.hostap
Message-ID <[email protected]>
From: Ben Greear <[email protected]>

This lets things work better on multi-homed networks.

Signed-off-by: Ben Greear <[email protected]>
---
 hs20/client/est.c           |  15 ++++--
 hs20/client/oma_dm_client.c |   7 ++-
 hs20/client/osu_client.c    |  43 +++++++++++++--
 hs20/client/osu_client.h    |   4 ++
 hs20/client/spp_client.c    |  18 +++++--
 src/utils/http-utils.h      |  12 +++--
 src/utils/http_curl.c       | 103 ++++++++++++++++++++++++++++++++----
 7 files changed, 179 insertions(+), 23 deletions(-)

diff --git a/hs20/client/est.c b/hs20/client/est.c
index 97f913210..f93a15696 100644
--- a/hs20/client/est.c
+++ b/hs20/client/est.c
@@ -137,7 +137,10 @@ int est_load_cacerts(struct hs20_osu_client *ctx, const char *url)
 	ctx->no_osu_cert_validation = 1;
 	http_ocsp_set(ctx->http, 1);
 	res = http_download_file(ctx->http, buf, "Cert/est-cacerts.txt",
-				 ctx->ca_fname);
+				 ctx->ca_fname,
+				 ctx->do_bind_iface ? ctx->ifname : NULL,
+				 ctx->dns);
+
 	http_ocsp_set(ctx->http,
 		      (ctx->workarounds & WORKAROUND_OCSP_OPTIONAL) ? 1 : 2);
 	ctx->no_osu_cert_validation = 0;
@@ -619,7 +622,10 @@ int est_build_csr(struct hs20_osu_client *ctx, const char *url)
 	ctx->no_osu_cert_validation = 1;
 	http_ocsp_set(ctx->http, 1);
 	res = http_download_file(ctx->http, buf, "Cert/est-csrattrs.txt",
-				 ctx->ca_fname);
+				 ctx->ca_fname,
+				 ctx->do_bind_iface ? ctx->ifname : NULL,
+				 ctx->dns);
+
 	http_ocsp_set(ctx->http,
 		      (ctx->workarounds & WORKAROUND_OCSP_OPTIONAL) ? 1 : 2);
 	ctx->no_osu_cert_validation = 0;
@@ -721,7 +727,10 @@ int est_simple_enroll(struct hs20_osu_client *ctx, const char *url,
 	resp = http_post(ctx->http, buf, req, "application/pkcs10",
 			 "Content-Transfer-Encoding: base64",
 			 ctx->ca_fname, user, pw, client_cert, client_key,
-			 &resp_len);
+			 &resp_len,
+			 ctx->do_bind_iface ? ctx->ifname : NULL,
+			 ctx->dns);
+
 	http_ocsp_set(ctx->http,
 		      (ctx->workarounds & WORKAROUND_OCSP_OPTIONAL) ? 1 : 2);
 	ctx->no_osu_cert_validation = 0;
diff --git a/hs20/client/oma_dm_client.c b/hs20/client/oma_dm_client.c
index d75c84562..78b46341a 100644
--- a/hs20/client/oma_dm_client.c
+++ b/hs20/client/oma_dm_client.c
@@ -957,7 +957,10 @@ static xml_node_t * oma_dm_send_recv(struct hs20_osu_client *ctx,
 	ctx->server_url = os_strdup(url);
 	res = http_post(ctx->http, url, str, "application/vnd.syncml.dm+xml",
 			ext_hdr, ctx->ca_fname, username, password,
-			client_cert, client_key, NULL);
+			client_cert, client_key, NULL,
+			ctx->do_bind_iface ? ctx->ifname : NULL,
+			ctx->dns);
+
 	os_free(str);
 	os_free(resp_uri);
 	resp_uri = NULL;
@@ -1210,6 +1213,8 @@ int cmd_oma_dm_sim_prov(struct hs20_osu_client *ctx, const char *url)
 	}
 	write_summary(ctx, "OMA-DM SIM provisioning");
 
+	check_dns_file(ctx);
+
 	msgid++;
 	syncml = build_oma_dm_1_sub_prov(ctx, url, msgid);
 	if (syncml == NULL)
diff --git a/hs20/client/osu_client.c b/hs20/client/osu_client.c
index 9f9c307b6..ff6e5b1c0 100644
--- a/hs20/client/osu_client.c
+++ b/hs20/client/osu_client.c
@@ -339,7 +339,10 @@ static int download_cert(struct hs20_osu_client *ctx, xml_node_t *params,
 	write_summary(ctx, "Download certificate from %s", url);
 	ctx->no_osu_cert_validation = 1;
 	http_ocsp_set(ctx->http, 1);
-	res = http_download_file(ctx->http, url, TMP_CERT_DL_FILE, NULL);
+	res = http_download_file(ctx->http, url, TMP_CERT_DL_FILE, NULL,
+				 ctx->do_bind_iface ? ctx->ifname : NULL,
+				 ctx->dns);
+
 	http_ocsp_set(ctx->http,
 		      (ctx->workarounds & WORKAROUND_OCSP_OPTIONAL) ? 1 : 2);
 	ctx->no_osu_cert_validation = 0;
@@ -2151,6 +2154,33 @@ static struct osu_data * parse_osu_providers(const char *fname, size_t *count)
 	return osu;
 }
 
+void check_dns_file(struct hs20_osu_client* ctx)
+{
+	/* Look for DNS servers in case user specified a place to look. */
+	if (ctx->dns_file) {
+		FILE *f;
+		char buf[100];
+
+		f = fopen(ctx->dns_file, "r");
+		if (f) {
+			if (fgets(buf, sizeof(buf), f)) {
+				wpa_printf(MSG_DEBUG, "Checking DNS file: %s contents: %s",
+					   ctx->dns_file, buf);
+				if (strncmp(buf, "DNS:", 4) == 0) {
+					/* remove ending whitespace */
+					int len = strlen(buf);
+					if ((buf[len - 2] == '\n') || (buf[len - 2] == '\r'))
+						buf[len - 2] = 0;
+					else if ((buf[len - 1] == '\n') || (buf[len - 1] == '\r'))
+						buf[len - 1] = 0;
+					http_bind_dns(ctx->http, NULL, buf + 4);
+					ctx->dns = strdup(buf + 4);
+				}
+			}
+			fclose(f);
+		}
+	}
+}
 
 static int osu_connect(struct hs20_osu_client *ctx, const char *bssid,
 		       const char *ssid, const char *ssid2, const char *url,
@@ -2255,6 +2285,8 @@ static int osu_connect(struct hs20_osu_client *ctx, const char *bssid,
 		wpa_printf(MSG_INFO, "Could not get IP address for WLAN - try connection anyway");
 	}
 
+	check_dns_file(ctx);
+
 	if (no_prod_assoc) {
 		if (res < 0)
 			return -1;
@@ -2698,6 +2730,7 @@ static int cmd_sub_rem(struct hs20_osu_client *ctx, const char *address,
 	if (wait_ip_addr(ctx->ifname, 15) < 0) {
 		wpa_printf(MSG_INFO, "Could not get IP address for WLAN - try connection anyway");
 	}
+	check_dns_file(ctx);
 
 	if (spp)
 		spp_sub_rem(ctx, address, pps_fname,
@@ -3162,7 +3195,7 @@ static void check_workarounds(struct hs20_osu_client *ctx)
 static void usage(void)
 {
 	printf("usage: hs20-osu-client [-dddqqKt] [-S<station ifname>] \\\n"
-	       "    [-w<wpa_supplicant ctrl_iface dir>] "
+	       "    [-w<wpa_supplicant ctrl_iface dir>] [-D<dns-file-name>] "
 	       "[-r<result file>] [-f<debug file>] \\\n"
 	       "    [-s<summary file>] \\\n"
 	       "    [-x<spp.xsd file name>] \\\n"
@@ -3207,7 +3240,7 @@ int main(int argc, char *argv[])
 		return -1;
 
 	for (;;) {
-		c = getopt(argc, argv, "df:hKNo:O:qr:s:S:tw:x:");
+		c = getopt(argc, argv, "dD:f:hKNo:O:qr:s:S:tw:x:");
 		if (c < 0)
 			break;
 		switch (c) {
@@ -3215,6 +3248,9 @@ int main(int argc, char *argv[])
 			if (wpa_debug_level > 0)
 				wpa_debug_level--;
 			break;
+		case 'D':
+			ctx.dns_file = optarg;
+			break;
 		case 'f':
 			wpa_debug_file_path = optarg;
 			break;
@@ -3241,6 +3277,7 @@ int main(int argc, char *argv[])
 			break;
 		case 'S':
 			ctx.ifname = optarg;
+			ctx.do_bind_iface = 1;
 			break;
 		case 't':
 			wpa_debug_timestamp++;
diff --git a/hs20/client/osu_client.h b/hs20/client/osu_client.h
index 5c8e6d00b..9b1082278 100644
--- a/hs20/client/osu_client.h
+++ b/hs20/client/osu_client.h
@@ -34,6 +34,9 @@ struct hs20_osu_client {
 	const char *summary_file;
 	const char *ifname;
 	const char *ca_fname;
+	const char *dns_file;
+	const char* dns;
+	int do_bind_iface;
 	int no_osu_cert_validation; /* for EST operations */
 	char *fqdn;
 	char *server_url;
@@ -71,6 +74,7 @@ int update_pps_file(struct hs20_osu_client *ctx, const char *pps_fname,
 		    xml_node_t *pps);
 void cmd_set_pps(struct hs20_osu_client *ctx, const char *pps_fname);
 
+void check_dns_file(struct hs20_osu_client* ctx);
 
 /* spp_client.c */
 
diff --git a/hs20/client/spp_client.c b/hs20/client/spp_client.c
index c619541ae..4144189b0 100644
--- a/hs20/client/spp_client.c
+++ b/hs20/client/spp_client.c
@@ -796,7 +796,9 @@ void spp_sub_rem(struct hs20_osu_client *ctx, const char *address,
 
 	if (soap_init_client(ctx->http, address, ctx->ca_fname,
 			     cred_username, cred_password, client_cert,
-			     client_key) == 0) {
+			     client_key,
+			     ctx->do_bind_iface ? ctx->ifname : NULL,
+			     ctx->dns) == 0) {
 		spp_post_dev_data(ctx, SPP_SUBSCRIPTION_REMEDIATION,
 				  "Subscription remediation", pps_fname, pps);
 	}
@@ -942,7 +944,9 @@ void spp_pol_upd(struct hs20_osu_client *ctx, const char *address,
 	ctx->server_url = os_strdup(address);
 
 	if (soap_init_client(ctx->http, address, ctx->ca_fname, cred_username,
-			     cred_password, client_cert, client_key) == 0) {
+			     cred_password, client_cert, client_key,
+			     ctx->do_bind_iface ? ctx->ifname : NULL,
+			     ctx->dns) == 0) {
 		spp_post_dev_data(ctx, SPP_POLICY_UPDATE, "Policy update",
 				  pps_fname, pps);
 	}
@@ -967,7 +971,9 @@ int cmd_prov(struct hs20_osu_client *ctx, const char *url)
 	ctx->server_url = os_strdup(url);
 
 	if (soap_init_client(ctx->http, url, ctx->ca_fname, NULL, NULL, NULL,
-			     NULL) < 0)
+			     NULL,
+			     ctx->do_bind_iface ? ctx->ifname : NULL,
+			     ctx->dns) < 0)
 		return -1;
 	spp_post_dev_data(ctx, SPP_SUBSCRIPTION_REGISTRATION,
 			  "Subscription registration", NULL, NULL);
@@ -994,8 +1000,12 @@ int cmd_sim_prov(struct hs20_osu_client *ctx, const char *url)
 		wpa_printf(MSG_INFO, "Could not get IP address for WLAN - try connection anyway");
 	}
 
+	check_dns_file(ctx);
+
 	if (soap_init_client(ctx->http, url, ctx->ca_fname, NULL, NULL, NULL,
-			     NULL) < 0)
+			     NULL,
+			     ctx->do_bind_iface ? ctx->ifname : NULL,
+			     ctx->dns) < 0)
 		return -1;
 	spp_post_dev_data(ctx, SPP_SUBSCRIPTION_REGISTRATION,
 			  "Subscription provisioning", NULL, NULL);
diff --git a/src/utils/http-utils.h b/src/utils/http-utils.h
index 8d4399a37..277ac3485 100644
--- a/src/utils/http-utils.h
+++ b/src/utils/http-utils.h
@@ -38,22 +38,28 @@ struct http_cert {
 int soap_init_client(struct http_ctx *ctx, const char *address,
 		     const char *ca_fname, const char *username,
 		     const char *password, const char *client_cert,
-		     const char *client_key);
+		     const char *client_key, const char* ifname, const char* dns);
 int soap_reinit_client(struct http_ctx *ctx);
 xml_node_t * soap_send_receive(struct http_ctx *ctx, xml_node_t *node);
 
+/* Bind curl to an interface. */
+int http_bind_iface(struct http_ctx *ctx, void* curl, const char* ifname);
+/* Tell curl's resolver (in case it is using one) to use a specific DNS server. */
+int http_bind_dns(struct http_ctx *ctx, void* curl, const char* dns);
+
 struct http_ctx * http_init_ctx(void *upper_ctx, struct xml_node_ctx *xml_ctx);
 void http_ocsp_set(struct http_ctx *ctx, int val);
 void http_deinit_ctx(struct http_ctx *ctx);
 
 int http_download_file(struct http_ctx *ctx, const char *url,
-		       const char *fname, const char *ca_fname);
+		       const char *fname, const char *ca_fname,
+		       const char* ifname, const char* dns);
 char * http_post(struct http_ctx *ctx, const char *url, const char *data,
 		 const char *content_type, const char *ext_hdr,
 		 const char *ca_fname,
 		 const char *username, const char *password,
 		 const char *client_cert, const char *client_key,
-		 size_t *resp_len);
+		 size_t *resp_len, const char* ifname, const char* dns);
 void http_set_cert_cb(struct http_ctx *ctx,
 		      int (*cb)(void *ctx, struct http_cert *cert),
 		      void *cb_ctx);
diff --git a/src/utils/http_curl.c b/src/utils/http_curl.c
index e62fbf96b..967eace09 100644
--- a/src/utils/http_curl.c
+++ b/src/utils/http_curl.c
@@ -50,8 +50,11 @@ struct http_ctx {
 	char *svc_password;
 	char *svc_client_cert;
 	char *svc_client_key;
+	char *ifname;
+	char *dns;
 	char *curl_buf;
 	size_t curl_buf_len;
+	char curl_err_buffer[CURL_ERROR_SIZE + 1];
 
 	int (*cert_cb)(void *ctx, struct http_cert *cert);
 	void *cert_cb_ctx;
@@ -1348,7 +1351,7 @@ static CURLcode curl_cb_ssl(CURL *curl, void *sslctx, void *parm)
 static CURL * setup_curl_post(struct http_ctx *ctx, const char *address,
 			      const char *ca_fname, const char *username,
 			      const char *password, const char *client_cert,
-			      const char *client_key)
+			      const char *client_key, const char* ifname, const char* dns)
 {
 	CURL *curl;
 #ifdef EAP_TLS_OPENSSL
@@ -1364,6 +1367,15 @@ static CURL * setup_curl_post(struct http_ctx *ctx, const char *address,
 	if (curl == NULL)
 		return NULL;
 
+	ctx->curl_err_buffer[0] = 0;
+	curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, ctx->curl_err_buffer);
+
+	if (ifname)
+		http_bind_iface(ctx, curl, ifname);
+
+	if (dns)
+		http_bind_dns(ctx, curl, dns);
+
 	curl_easy_setopt(curl, CURLOPT_URL, address);
 	curl_easy_setopt(curl, CURLOPT_POST, 1L);
 	if (ca_fname) {
@@ -1398,6 +1410,7 @@ static CURL * setup_curl_post(struct http_ctx *ctx, const char *address,
 	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_cb_write);
 	curl_easy_setopt(curl, CURLOPT_WRITEDATA, ctx);
 	curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
+
 	if (username) {
 		curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_ANYSAFE);
 		curl_easy_setopt(curl, CURLOPT_USERNAME, username);
@@ -1407,11 +1420,67 @@ static CURL * setup_curl_post(struct http_ctx *ctx, const char *address,
 	return curl;
 }
 
+int http_bind_iface(struct http_ctx *ctx, void* _curl, const char* ifname)
+{
+	int rv;
+	CURL* curl = _curl;
+
+	if (!curl)
+		curl = ctx->curl;
+
+	if (ctx->ifname)
+		os_free(ctx->ifname);
+	ctx->ifname = NULL;
+	clone_str(&ctx->ifname, ifname);
+
+	/* Bind DNS resolver to the local interface, if curl is using such. */
+	rv = curl_easy_setopt(curl, CURLOPT_DNS_INTERFACE, ifname);
+	if (rv != CURLE_OK) {
+		wpa_printf(MSG_ERROR, "Failed CURLOPT_DNS_INTERFACE, curl: %p ifname: %s  error: %s rv: %d (%s)\n",
+			   curl, ifname, ctx->curl_err_buffer, rv, curl_easy_strerror(rv));
+	}
+	else {
+		wpa_printf(MSG_DEBUG, "Bound curl DNS to interface: %s\n", ifname);
+	}
+	rv = curl_easy_setopt(curl, CURLOPT_INTERFACE, ifname);
+	if (rv != CURLE_OK) {
+		wpa_printf(MSG_ERROR, "Failed CURLOPT_INTERFACE, curl: %p ifname: %s  error: %s, rv: %d(%s)\n",
+			   curl, ifname, ctx->curl_err_buffer, rv, curl_easy_strerror(rv));
+	}
+	else {
+		wpa_printf(MSG_DEBUG, "Bound curl to interface: %s\n", ifname);
+	}
+	return rv;
+}
+
+int http_bind_dns(struct http_ctx *ctx, void* _curl, const char* dns)
+{
+	CURL* curl = _curl;
+	int rv;
+
+	if (!curl)
+		curl = ctx->curl;
+
+	if (ctx->dns)
+		os_free(ctx->dns);
+	ctx->dns = NULL;
+	clone_str(&ctx->dns, dns);
+
+	rv = curl_easy_setopt(curl, CURLOPT_DNS_SERVERS, dns);
+	if (rv != CURLE_OK) {
+		wpa_printf(MSG_ERROR, "Failed CURLOPT_DNS_SERVERS, curl: %p dns: %s  error: %s rv: %d(%s)\n",
+			   curl, dns, ctx->curl_err_buffer, rv, curl_easy_strerror(rv));
+	}
+	else {
+		wpa_printf(MSG_DEBUG, "Bound curl DNS servers: %s\n", dns);
+	}
+	return rv;
+}
 
 static int post_init_client(struct http_ctx *ctx, const char *address,
 			    const char *ca_fname, const char *username,
 			    const char *password, const char *client_cert,
-			    const char *client_key)
+			    const char *client_key, const char* ifname, const char* dns)
 {
 	char *pos;
 	int count;
@@ -1422,6 +1491,8 @@ static int post_init_client(struct http_ctx *ctx, const char *address,
 	clone_str(&ctx->svc_password, password);
 	clone_str(&ctx->svc_client_cert, client_cert);
 	clone_str(&ctx->svc_client_key, client_key);
+	clone_str(&ctx->ifname, ifname);
+	clone_str(&ctx->dns, dns);
 
 	/*
 	 * Workaround for Apache "Hostname 'FOO' provided via SNI and hostname
@@ -1435,7 +1506,7 @@ static int post_init_client(struct http_ctx *ctx, const char *address,
 	}
 
 	ctx->curl = setup_curl_post(ctx, ctx->svc_address, ca_fname, username,
-				    password, client_cert, client_key);
+				    password, client_cert, client_key, ifname, dns);
 	if (ctx->curl == NULL)
 		return -1;
 
@@ -1446,10 +1517,10 @@ static int post_init_client(struct http_ctx *ctx, const char *address,
 int soap_init_client(struct http_ctx *ctx, const char *address,
 		     const char *ca_fname, const char *username,
 		     const char *password, const char *client_cert,
-		     const char *client_key)
+		     const char *client_key, const char* ifname, const char* dns)
 {
 	if (post_init_client(ctx, address, ca_fname, username, password,
-			     client_cert, client_key) < 0)
+			     client_cert, client_key, ifname, dns) < 0)
 		return -1;
 
 	ctx->curl_hdr = curl_slist_append(ctx->curl_hdr,
@@ -1470,6 +1541,8 @@ int soap_reinit_client(struct http_ctx *ctx)
 	char *password = NULL;
 	char *client_cert = NULL;
 	char *client_key = NULL;
+	char *ifname = NULL;
+	char *dns = NULL;
 	int ret;
 
 	clear_curl(ctx);
@@ -1480,9 +1553,11 @@ int soap_reinit_client(struct http_ctx *ctx)
 	clone_str(&password, ctx->svc_password);
 	clone_str(&client_cert, ctx->svc_client_cert);
 	clone_str(&client_key, ctx->svc_client_key);
+	clone_str(&ifname, ctx->ifname);
+	clone_str(&dns, ctx->dns);
 
 	ret = soap_init_client(ctx, address, ca_fname, username, password,
-			       client_cert, client_key);
+			       client_cert, client_key, ifname, dns);
 	os_free(address);
 	os_free(ca_fname);
 	str_clear_free(username);
@@ -1614,7 +1689,8 @@ void http_deinit_ctx(struct http_ctx *ctx)
 
 
 int http_download_file(struct http_ctx *ctx, const char *url,
-		       const char *fname, const char *ca_fname)
+		       const char *fname, const char *ca_fname,
+		       const char* ifname, const char* dns)
 {
 	CURL *curl;
 	FILE *f;
@@ -1629,6 +1705,15 @@ int http_download_file(struct http_ctx *ctx, const char *url,
 	if (curl == NULL)
 		return -1;
 
+	ctx->curl_err_buffer[0] = 0;
+	curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, ctx->curl_err_buffer);
+
+	if (ifname)
+		http_bind_iface(ctx, curl, ifname);
+
+	if (dns)
+		http_bind_dns(ctx, curl, dns);
+
 	f = fopen(fname, "wb");
 	if (f == NULL) {
 		curl_easy_cleanup(curl);
@@ -1682,7 +1767,7 @@ char * http_post(struct http_ctx *ctx, const char *url, const char *data,
 		 const char *ca_fname,
 		 const char *username, const char *password,
 		 const char *client_cert, const char *client_key,
-		 size_t *resp_len)
+		 size_t *resp_len, const char* ifname, const char* dns)
 {
 	long http = 0;
 	CURLcode res;
@@ -1693,7 +1778,7 @@ char * http_post(struct http_ctx *ctx, const char *url, const char *data,
 	ctx->last_err = NULL;
 	wpa_printf(MSG_DEBUG, "curl: HTTP POST to %s", url);
 	curl = setup_curl_post(ctx, url, ca_fname, username, password,
-			       client_cert, client_key);
+			       client_cert, client_key, ifname, dns);
 	if (curl == NULL)
 		return NULL;
 
-- 
2.20.1
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.