Azure / azure-iot-sdk-c

A C99 SDK for connecting devices to Microsoft Azure IoT services
https://azure.github.io/azure-iot-sdk-c
Other
583 stars 740 forks source link

Question: Use proxy settings for create connection to IoT Hub #346

Closed dehng closed 6 years ago

dehng commented 6 years ago

Description of the issue:

Hello, is there a way to specify a proxy server (name, user, password, port)? My IoT client is behind a firewall and must first log on to the proxy in order to establish a connection.

Currently I'm using the example 'iothub_client_sample_http' and 'iothub_client_sample_mqtt'.

Thanks.

ewertons commented 6 years ago

Hi @dehng , please take a look at this sample: https://github.com/Azure/azure-iot-sdk-c/blob/master/iothub_client/samples/iothub_client_sample_upload_to_blob/iothub_client_sample_upload_to_blob.c#L63

You can set the user and password as well on that structure. https://github.com/Azure/azure-c-shared-utility/blob/41861a7f3b6c023eda22a7ad07cc9b8489999b14/inc/azure_c_shared_utility/shared_util_options.h#L12

As the name suggests, this option is for HTTP proxy. That can be leveraged if you use our HTTP transport or any of the WebSockets-based transports (AMQP-WS or MQTT-WS).

I would suggest you try using either AMQP over Websockets or MQTT over websockets if you have a client behind a firewall.

dehng commented 6 years ago

Hi @ewertons, thank you for your answer. I've added the code below to allow proxy settings to be set. For accessing the cloud, I'm using the example 'iothub_client_sample_http' My operating system is 'Windows Compact 2013'

HTTP_PROXY_OPTIONS http_proxy_options = { 0 };
 http_proxy_options.host_address = "1.2.3.4"
 http_proxy_options.port = 8080;
 http_proxy_options.username = "domain\\user";
 http_proxy_options.password = "pwd";

 if (IoTHubClient_LL_SetOption(iotHubClientHandle, OPTION_HTTP_PROXY, &http_proxy_options) != IOTHUB_CLIENT_OK)
 {
     (void)printf("failure to set proxy\n");
 }

With this extension: I now get this error. What am I doing wrong?

 Starting the IoTHub client sample HTTP...
 Info: IoT Hub SDK for C, version 1.1.29
 Error: Time:Tue Jan 09 23:04:01 2018 File:..\..\azure-iot-sdk-c\c-utility\adapters\httpapi_wince.c Func:HTTPAPI_CloneOption Line:636 unknown option proxy_data
 Error: Time:Tue Jan 09 23:04:01 2018 File:..\..\azure-iot-sdk-c\c-utility\src\httpapiex.c Func:HTTPAPIEX_SetOption Line:609 error code = HTTPAPIEX_INVALID_ARG
 Error: Time:Tue Jan 09 23:04:01 2018 File:..\..\azure-iot-sdk-c\iothub_client\src\iothubtransporthttp.c Func:?IoTHubTransportHttp_SetOption@@YA?AW4IOTHUB_CLIENT
 _RESULT_TAG@@PAXPBDPBX@Z Line:2426 HTTPAPIEX_SetOption failed
 IoTHubClient_LL_SetMessageCallback...successful.
 IoTHubClient_LL_SendEventAsync accepted message [zu] for transmission to IoT Hub.
 Info: HTTPAPI_Init::Start
 Info: HTTAPI_Init::Time is now (UTC) Tue Jan 09 23:04:01 2018

 Info: HTTPAPI_Init::End
 Info: HTTPAPI_CreateConnection::Start
 Info: HTTPAPI_CreateConnection::Connecting to XXXXXXXX.azure-devices.net
 Info: HTTPAPI_CreateConnection::End
 Info: HTTPAPI_ExecuteRequest::Start
 Error: Time:Tue Jan 09 23:04:23 2018 File:..\..\azure-iot-sdk-c\c-utility\adapters\httpapi_wince.c Func:HTTPAPI_ExecuteRequest Line:324 connect failed
 Info: HTTPAPI_ExecuteRequest::End=0

The examples of AMQP-WS and MQTT-WS both do not work. I am not connected to the cloud. The example 'iothub_client_sample_amqp_websockets' contains an error that TLS for Windows Compact 2013 is not supported. Output from the sample program:

Info: IoT Hub SDK for C, version 1.1.29
Info: Retry policy set (5, timeout = 0)
Error: Time:Tue Jan 09 17:56:00 2018 File:..\..\azure-iot-sdk-c\c-utility\adapters\platform_win32.c Func:platform_get_default_tlsio Line:56 TLS IO interface currently not supported on WEC 2013
Error: Time:Tue Jan 09 17:56:07 2018 File:..\..\azure-iot-sdk-c\c-utility\src\uws_client.c Func:uws_client_create_with_io Line:350 Invalid arguments: io_interface = 00000000, resource_name = 003336AC, protocols = 0099F3B4, protocol_count =
zu
Error: Time:Tue Jan 09 17:56:08 2018 File:..\..\azure-iot-sdk-c\c-utility\src\wsio.c Func:wsio_create Line:257 Cannot create uws instance.
Error: Time:Tue Jan 09 17:56:09 2018 File:..\..\azure-iot-sdk-c\iothub_client\src\iothubtransport_amqp_common.c Func:get_new_underlying_io_transport Line:698 Failed to obtain a TLS I/O transport layer (underlying_io_transport_provider() failed)
Error: Time:Tue Jan 09 17:56:10 2018 File:..\..\azure-iot-sdk-c\iothub_client\src\iothubtransport_amqp_common.c Func:IoTHubTransport_AMQP_Common_SetOption Line:2072 transport failed setting option 'TrustedCerts' (failed to obtain a TLS I/Otransport).
Error: Time:Tue Jan 09 17:56:11 2018 File:..\..\azure-iot-sdk-c\iothub_client\src\iothub_client_ll.c Func:IoTHubClient_LL_SetOption Line:1791 underlying transport failed, returned = IOTHUB_CLIENT_ERROR
Error: Time:Tue Jan 09 17:56:14 2018 File:..\..\azure-iot-sdk-c\iothub_client\src\iothub_client.c Func:IoTHubClient_SetOption Line:1497 IoTHubClient_LL_SetOption failed
Error: Time:Tue Jan 09 17:56:15 2018 File:..\..\azure-iot-sdk-c\c-utility\adapters\platform_win32.c Func:platform_get_default_tlsio Line:56 TLS IO interface currently not supported on WEC 2013
Error: Time:Tue Jan 09 17:56:17 2018 File:..\..\azure-iot-sdk-c\c-utility\src\uws_client.c Func:uws_client_create_with_io Line:350 Invalid arguments: io_interface = 00000000, resource_name = 003336AC, protocols = 00C9FBC4, protocol_count =zu
Error: Time:Tue Jan 09 17:56:21 2018 File:..\..\azure-iot-sdk-c\c-utility\src\wsio.c Func:wsio_create Line:257 Cannot create uws instance.
Error: Time:Tue Jan 09 17:56:22 2018 File:..\..\azure-iot-sdk-c\iothub_client\src\iothubtransport_amqp_common.c Func:get_new_underlying_io_transport Line:698 Failed to obtain a TLS I/O transport layer (underlying_io_transport_provider() failed)
Error: Time:Tue Jan 09 17:56:25 2018 File:..\..\azure-iot-sdk-c\iothub_client\src\iothubtransport_amqp_common.c Func:establish_amqp_connection Line:783 Failed establishing connection (failed to obtain a TLS I/O transport layer).
Error: Time:Tue Jan 09 17:56:28 2018 File:..\..\azure-iot-sdk-c\iothub_client\src\iothubtransport_amqp_common.c Func:IoTHubTransport_AMQP_Common_DoWork Line:1572 AMQP transport failed to establish connection with service.
Info: Preparing transport for re-connection
Error: Time:Tue Jan 09 17:56:28 2018 File:..\..\azure-iot-sdk-c\iothub_client\src\iothubtransport_amqp_common.c Func:save_underlying_io_transport_options Line:605 failed saving underlying I/O transport options (tls_io instance is NULL)
Error: Time:Tue Jan 09 17:56:30 2018 File:..\..\azure-iot-sdk-c\iothub_client\src\iothubtransport_amqp_common.c Func:prepare_for_connection_retry Line:867 Failed saving TLS I/O options while preparing for connection retry; failure will be ignored
ewertons commented 6 years ago

Oh, you are using Windows Compact 2013. I overlooked that. I need to check our support for that version.

dehng commented 6 years ago

Hi @ewertons, do you have information for me?

ewertons commented 6 years ago

Hi @dehng , I confirmed, and we do support the version of Windows Compact you are using. However, proxy configuration is not implemented for that version of OS (httpapi_wince.c). That would be an improvement needed to the SDK.

dehng commented 6 years ago

Hi @everton Is there an appointment, when the SDK supports the proxy function for http protocol? What must be done for the AMQP-WS or MQTT-WS protocols to work under WinCE? I need a connection to the azure over a firewall.

Thanks

ewertons commented 6 years ago

Traditionally the alternative way to getting the websockets-based protocols to work on a system where we do not implement HTTP proxy setting in code is to configure the HTTP proxy directly on the OS. Would you be able to try that option?

dehng commented 6 years ago

@ewertons. I asked the OS Image manufacturer if this is possible. He says that this is not a problem of the OS, but should be solved in azure sdk. Link: https://www.toradex.com/community/questions/18696/set.proxy-settings-in-windows-compact-2013-for-use.html?childToView=18967#comment-18967

Can you help me?

jspaith commented 6 years ago

Hi @dehng - Because our CE implementation uses sockets directly and not a wrapper like Wininet, adding proxy support isn’t practical. The only tested configuration is HTTPS for WEC 2013. Currently there are no plans to support protocols like MQTT and AMQP, either.

One alternative can be to go with Windows IoT Core which has broader support in Azure IoT SDK.

Sorry we don’t have a better story for you here. -- John

dehng commented 6 years ago

Hi @jspaith , I looked at the file 'httpapi_wininet.c' and rebuilt it for testing so I can connect through a proxy server. Now my connection to the cloud works.

How can I install proxy support to be 'correct'?

Call in the client: `HTTP_PROXY_OPTIONS m_httpProxyOptions; m_httpProxyOptions.host_address = "proxy.myAddress.com"; m_httpProxyOptions.port = 8080; m_httpProxyOptions.username = "MyUserName"; m_httpProxyOptions.password = "MyPasswort";

resultIoClient = IoTHubClient_SetOption(m_iothubClientMainHandle, OPTION_HTTP_PROXY, &m_httpProxyOptions);`

Extension in the file httpapi_wininet.c: `///////////////////////////////////////// // File httpapi_wininet.c ///////////////////////////////////////// typedef struct HTTP_HANDLE_DATA_TAG { HINTERNET SessionHandle; HINTERNET ConnectionHandle; HTTP_PROXY_OPTIONS MyProxyOptions; // new setting } HTTP_HANDLE_DATA;

///////////////////////////////////////// // File httpapi_wininet.c, new functions ///////////////////////////////////////// HTTPAPI_RESULT HTTPAPI_SetOption(HTTP_HANDLE handle, const char optionName, const void value) { HTTPAPI_RESULT result; if (strcmp(optionName, OPTION_HTTP_PROXY) == 0) { HTTP_PROXY_OPTIONS proxy_options = (HTTP_PROXY_OPTIONS )value; ... result = HTTPAPI_OK; } ... }

HTTPAPI_RESULT HTTPAPI_CloneOption(const char optionName, const void value, const void* savedValue) { HTTPAPI_RESULT result; if (strcmp(optionName, OPTION_HTTP_PROXY) == 0) { HTTP_PROXY_OPTIONS proxy_options = (HTTP_PROXY_OPTIONS *)value; ... result = HTTPAPI_OK; } ... }`

Is this the right way or does the option have to be set in another file? Thanks

jspaith commented 6 years ago

You're correct - the SetOption & CloneOption are the places you need to wire up to make this work.

Were you having issues with this (it sounds like no?) or were you just double checking, in which case all should be well.

jspaith commented 6 years ago

@dehng - since this thread has been quiet for a bit, I'm going to close it but as always if there's more to discuss by all means re-open it or create a new one.

mohamedibrahimabdelrahman33 commented 6 years ago

hey guys, i want to ask you a question, how to use "http_proxy_io" with "httpapi_compact"? currently i'm using HTTP protocol to send and receive, and i want to use proxy configuration.

dehng commented 6 years ago

Hi @mohamedibrahimabdelrahman33, I replaced the file httpapi_wince with the file httpapi_wininet. In this file I have added the extensions for access with a proxy server.

New code in the file:

#include "azure_c_shared_utility/shared_util_options.h"
#include "azure_c_shared_utility/crt_abstractions.h"
#pragma comment (lib, "Wininet.lib")

#define TEMP_BUFFER_SIZE 1024

static char g_proxyServername[1024] = "";
static char g_proxyServernameAndPort[1024] = "";

typedef struct HTTP_HANDLE_DATA_TAG
{
    HINTERNET SessionHandle;
    HINTERNET ConnectionHandle;
    HTTP_PROXY_OPTIONS proxy_options;            // added
} HTTP_HANDLE_DATA;

new function

HTTPAPI_RESULT HTTPAPI_SetOption(HTTP_HANDLE handle, const char* optionName, const void* value)
{
    HTTPAPI_RESULT result;

    if (
        (handle == NULL) ||
        (optionName == NULL) ||
        (value == NULL)
        )
    {
        result = HTTPAPI_INVALID_ARG;
        LogError("invalid parameter (NULL) passed to HTTPAPI_SetOption");
    }
    else if (strcmp(optionName, OPTION_HTTP_PROXY) == 0)
    {
        HTTP_PROXY_OPTIONS* proxy_options = (HTTP_PROXY_OPTIONS *)value;
        HTTP_HANDLE_DATA* handleData = (HTTP_HANDLE_DATA*)handle;

        if (handleData->ConnectionHandle != NULL)
        {
            BOOL state = FALSE;

            // user and password for proxy server
            if ((proxy_options->username != NULL) && (proxy_options->password != NULL))
            {
                if ((strlen(proxy_options->username) != 0) && ((strlen(proxy_options->password) != 0)))
                {
                    LogInfo("Connection with proxy server. Set user and password ...");
                    state = InternetSetOptionA(handleData->ConnectionHandle, INTERNET_OPTION_PROXY_USERNAME, (void*)proxy_options->username, strlen(proxy_options->username));
                    if (state == TRUE)
                    {
                        state = InternetSetOptionA(handleData->ConnectionHandle, INTERNET_OPTION_PROXY_PASSWORD, (void*)proxy_options->password, strlen(proxy_options->password));
                        if (state == FALSE)
                        {
                            LogError("Connection with proxy server. Set password for connection with proxy server failed. WinError=%d", GetLastError());
                        }
                    }
                    else
                    {
                        LogError("Connection with proxy server. Set user for connection with proxy server failed. WinError=%d", GetLastError());
                    }

                    if (state == TRUE)
                        LogInfo("Connection with proxy server. Set user and password done.");
                    else
                        LogError("Connection with proxy server. Set user and password failed.");
                }
            }
        }

        result = HTTPAPI_OK;
    }
    else
    {
        result = HTTPAPI_INVALID_ARG;
        LogError("unknown option %s", optionName);
    }
    return result;
}

HTTPAPI_RESULT HTTPAPI_CloneOption(const char* optionName, const void* value, const void** savedValue)
{
    HTTPAPI_RESULT result;

    if (
        (optionName == NULL) ||
        (value == NULL) ||
        (savedValue == NULL)
        )
    {
        result = HTTPAPI_INVALID_ARG;
        LogError("invalid argument(NULL) passed to HTTPAPI_CloneOption");
    }
    else if (strcmp(optionName, OPTION_HTTP_PROXY) == 0)
    {
        HTTP_PROXY_OPTIONS* proxy_options = (HTTP_PROXY_OPTIONS *)value;

        HTTP_PROXY_OPTIONS* temp = (HTTP_PROXY_OPTIONS*)malloc(sizeof(HTTP_PROXY_OPTIONS));
        memset(temp, 0, sizeof(HTTP_PROXY_OPTIONS));

        temp->port = proxy_options->port;

        if (proxy_options->host_address != NULL)
            mallocAndStrcpy_s((char**)&temp->host_address, (const char*)proxy_options->host_address);
        if (proxy_options->username != NULL)
            mallocAndStrcpy_s((char**)&temp->username, (const char*)proxy_options->username);
        if (proxy_options->password != NULL)
            mallocAndStrcpy_s((char**)&temp->password, (const char*)proxy_options->password);
        *savedValue = temp;

        // proxy server - address
        if (proxy_options->host_address != NULL)
        {
            strncpy_s(g_proxyServername, _countof(g_proxyServername), proxy_options->host_address, min(_countof(g_proxyServername), strlen(proxy_options->host_address)));
            if (proxy_options->port != 0)
                sprintf_s(g_proxyServernameAndPort, _countof(g_proxyServernameAndPort), "%s:%d", proxy_options->host_address, proxy_options->port);
            else
                sprintf_s(g_proxyServernameAndPort, _countof(g_proxyServernameAndPort), "%s", proxy_options->host_address);
        }
        else
        {
            g_proxyServername[0] = 0;
            g_proxyServernameAndPort[0] = 0;
        }

        result = HTTPAPI_OK;
    }
    else
    {
        *savedValue = NULL;
        result = HTTPAPI_INVALID_ARG;
        LogError("unknown option %s", optionName);
    }
    return result;
}

Change the function "HTTPAPI_CreateConnection"

            if (strlen(g_proxyServernameAndPort) == 0)
            {
                LogInfo("Create connection with direct access to internet...", );
                result->SessionHandle = InternetOpen(
                    NULL,
                    INTERNET_OPEN_TYPE_DIRECT,
                    NULL,
                    NULL,
                    0);
            }
            else
            {
                LogInfo("Create connection with proxy settings ('%s')...", g_proxyServernameAndPort);
                result->SessionHandle = InternetOpen(
                    NULL,
                    INTERNET_OPEN_TYPE_PROXY,
                    g_proxyServernameAndPort,
                    NULL,
                    0);
            }
rkathire commented 6 years ago

We are trying to do the same. Is there a sample for windows?

dehng commented 6 years ago

@rkathire, i used the the standard api for using wininet and use it on windows compact.

mohamedibrahimabdelrahman33 commented 6 years ago

Thanks @dehng for your effort, but actually i'm using "httpapi_compact" not "httpapi_wince". currently i'm working on STM32F7 platform.