From: Ben Greear <greearb@xxxxxxxxxxxxxxx> This lets things work better on multi-homed networks. Signed-off-by: Ben Greear <greearb@xxxxxxxxxxxxxxx> --- 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 _______________________________________________ Hostap mailing list Hostap@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/hostap