Proxy Settings in Meeting SDK for Linux

How is one supposed to configure proxy settings when developing an application using the Meeting SDK for Linux?

I’ve been using the Linux SDK for a couple of weeks to try and setup a demonstration app for a client. In it, the application will perform SDK authentication and join a meeting. This works with no problem the way it is written. The program is similar to the following:

//Error handling and event classes omitted for brevity.
using namespace ZOOMSDK;
int main(int argc, char**argv) {
    InitParam initParam;
    initParam.strWebDomain = "https://zoom.us";
    initParam.strSupportUrl = "https://zoom.us";
    initParam.emLanguageID = LANGUAGE_English;
    initParam.enableLogByDefault = true;
    SDKError result = InitSDK(initParam);
    IMeetingService *mtgSvc; // Assume initialized with CreateMeetingService(...)
    IAuthService *authSvc;   // Assume initialized with CreateAuthService(...)
    authSvc->SetEvent(&myAuthServiceEvent); // Responds to SDKAuth by joining a meeting
    AuthContext authContext;
    authContext.jwt_token = jwtToken; //Assume set with valid JWT
    authSvc->SDKAuth(authContext);

    /* Create a new GMainLoop with default context (NULL) and initial
      state of "not running" (FALSE). */
    GMainLoop *mainloop = g_main_loop_new(NULL, FALSE);
    /* Failure to create the main loop is fatal (for us). */
    if(mainloop == NULL)
    {
      g_printerr("Failed to create the mainloop Unknown (OOM?)\n");
      return EXIT_FAILURE;
    }

    g_print("Entering run loop\n");
    /* Run the program. */
    g_main_loop_run(mainloop);
}

The problem occurs when I try to get the SDK to communicate over a proxy. To support this I assume that I have to register a network connection handler, like so:

/*
MyNetworkConnectionHandler is a subclass if INetworkConnectionHandler that will authenticate the SDK once the `onProxyDetectComplete` is called on it.
*/
MyNetConnectionHandler *myNetConnHandler; //Assume initialized and valid. 
INetworkConnectionHelper *netConnHelper;  //Assume initialized with CreateNetworkConnectionHelper(...)
netConnHelper->RegisterNetworkConnectionHandler(&myNetConnHandler);

The problem I’m having is that it doesn’t seem to do anything. I figured it might respond to environment variable settings. I’ve tried setting the following envvars to http://<ip address>:<port>:

  • http_proxy
  • https_proxy
  • HTTPS_PROXY
  • HTTP_PROXY

I have also tried configuring the proxy settings directly through the INeworkConnectionHelper like this.

ProxySettings settings;
settings.proxy = "<ip address>:<port>";
settings.auto_detect = true; # or false, doesn't seem to change matters
netConnHelper->ConfigureProxy(settings);

In every case, the application doesn’t seem to do anything once I enter the run loop and I’m not sure what’s going on. I do have some questions:

  1. What is the preferred way of parking the main thread to allow for background (or asynchronous) tasks to complete? Up until now I’ve been using GMainLoop (from glib-2.0) to enter a run loop.
  2. What steps are required before using the INetworkConnectionHelper interface? Is it just InitSDK(...) or are there other SDK services that need to be created/configured beforehand?
  3. Logs would be a huge help when debugging. Is there a way to decrypt these on our own?

Thank you for your assistance and time.

@ncage ,

I know that for the Meeting SDK, initialization and all API calls must run in the Main Thread. Let me check if there is a way to pause the thread to allow background tasks to be completed. In the meantime, could you provide more information about the background tasks and the type of user experience you are aiming for?

I will follow up with more details on this once the requested information above is provided.

Currently, there is no method available to decrypt SDK logs. To proceed, you will need to enable the logs and share them with us. Please refer to our documentation on how to enable the logs.

@ncage ,

I’m doing something like this.

In the main method, I’m calling InitMeetingSDK(), and then AuthMeetingSDK().

void AuthMeetingSDK()
{
	SDKError err(SDKError::SDKERR_SUCCESS);

	//create auth service
	if ((err = CreateAuthService(&m_pAuthService)) != SDKError::SDKERR_SUCCESS) {};
	std::cerr << "AuthService created." << std::endl;

	//Create a param to insert jwt token
	ZOOM_SDK_NAMESPACE::AuthContext param;

	//set the event listener for onauthenticationcompleted
	if ((err = m_pAuthService->SetEvent(new AuthServiceEventListener(&OnAuthenticationComplete))) != SDKError::SDKERR_SUCCESS) {};
	std::cout << "AuthServiceEventListener added." << std::endl;

	//attempt to authenticate
	ZOOM_SDK_NAMESPACE::SDKError sdkErrorResult = m_pAuthService->SDKAuth(param);

	if (ZOOM_SDK_NAMESPACE::SDKERR_SUCCESS != sdkErrorResult){
		std::cerr << "AuthSDK:error " << std::endl;
	}
	else{
		std::cerr << "AuthSDK:success " << std::endl;
	}
}

void InitMeetingSDK()
{
	ZOOM_SDK_NAMESPACE::SDKError err(ZOOM_SDK_NAMESPACE::SDKERR_SUCCESS);
	ZOOM_SDK_NAMESPACE::InitParam initParam;

	// set domain
	initParam.strWebDomain = "https://zoom.us";
	initParam.strSupportUrl = "https://zoom.us";

	// set language id
	initParam.emLanguageID = ZOOM_SDK_NAMESPACE::LANGUAGE_English;

	//set logging perferences
	initParam.enableLogByDefault = true;
	initParam.enableGenerateDump = true;

	// attempt to initialize
	err = ZOOM_SDK_NAMESPACE::InitSDK(initParam);
	if (err != ZOOM_SDK_NAMESPACE::SDKERR_SUCCESS){
		std::cerr << "Init meetingSdk:error " << std::endl;
	}
	else{
		std::cerr << "Init meetingSdk:success" << std::endl;
	}

	//use connection helper
	if ((err = CreateNetworkConnectionHelper(&network_connection_helper)) == SDKError::SDKERR_SUCCESS) {
		std::cout << "CreateNetworkConnectionHelper created." << std::endl;
	}
	if ((err = network_connection_helper->RegisterNetworkConnectionHandler(new NetworkConnectionHandler(&AuthMeetingSDK))) == SDKError::SDKERR_SUCCESS) {
		std::cout << "NetworkConnectionHandler registered. Detecting proxy." << std::endl;
	}
}


int main(int argc, char* argv[])
{
	InitMeetingSDK();
	AuthMeetingSDK();

	loop = g_main_loop_new(NULL, FALSE);
	// add source to default context
	g_timeout_add(100, timeout_callback, loop);
	g_main_loop_run(loop);
	return 0;
}


One key thing to do, if you want a call back, is to do something like postToDo_() below.

#include <iostream>
#include "NetworkConnectionHandler.h"

using namespace std;

NetworkConnectionHandler::NetworkConnectionHandler(void (*postToDo)())
{
postToDo_ = postToDo;
}

void NetworkConnectionHandler::onProxyDetectComplete()
{
std::cout << "onProxyDetectComplete" << endl;
if (postToDo_) postToDo_();
}

void NetworkConnectionHandler::onProxySettingNotification(IProxySettingHandler* handler)
{
std::cout << "onProxySettingNotification" << endl;
}

void NetworkConnectionHandler::onSSLCertVerifyNotification(ISSLCertVerificationHandler* handler)
{
std::cout << "onSSLCertVerifyNotification" << endl;
}
1 Like

@donte.zoom ,

The background tasks I was referring to would be any tasks that the Zoom SDK might be completing on behalf of my code. I should have said asynchronous and not background. I assumed that the SDK must be performing certain tasks off of the main thread. At this time, my example code doesn’t perform any of it’s own background tasks. User experience is not a concern at this time. I’m only trying to work out how to utilize the SDK to join a meeting. Currently I’m stuck getting the SDK to account for proxy settings.

I will not be sending any file generated internally to an outside party if I cannot inspect its contents. I’m sure you understand the security implications from the outside and will reconsider why these logs are being encrypted in the first place.

I see that you’re calling the SDKAuth method without waiting for the onProxyDetectComplete callback to be invoked. Does this matter? I’d assumed that one would want to wait for proxy detection to complete and perform SDK authentication in response to it.

I also see that you’re creating the INetworkConntectionHelper before creating the IAuthService. Does the order in which these objects are created matter?

In my example, if I call SDKAuth like this, onAuthenticationReturn(AuthResult ret) is called, but the value of ret is AUTHRET_OVERTIME. Which I’ve taken to mean that the sdk authentication request to the server has timed out. It suggests that the proxy settings were not being utilized.

So far, I’ve only tried setting the http proxy through the environment variables HTTP_PROXY and HTTPS_PROXY (and the lowercase variants of these) or by calling network_connection_helper->ConfigureProxy(). Are you setting your proxy settings in a similar manner? If not, how are you setting them?

@ncage

I see that you’re calling the SDKAuth method without waiting for the onProxyDetectComplete callback to be invoked. Does this matter? I’d assumed that one would want to wait for proxy detection to complete and perform SDK authentication in response to it.

This is a logic error, you might want to ignore that in the sample code.

I also see that you’re creating the INetworkConntectionHelper before creating the IAuthService . Does the order in which these objects are created matter?

No

So far, I’ve only tried setting the http proxy through the environment variables HTTP_PROXY and HTTPS_PROXY (and the lowercase variants of these) or by calling network_connection_helper->ConfigureProxy() . Are you setting your proxy settings in a similar manner? If not, how are you setting them?

From the inline docs, it is ip:port only

Hi,

I’ve been encountering the same problem that our application using zoom SDK is able to authenticate and join a zoom meeting in local dev environment but bot able to achieve the same in the integration environment where it needs to communicate over a proxy.

The way we configured the proxy is the somewhat the same as what @chunsiong.zoom shared earlier, here’s the related code snippet of our application:

SDKError Zoom::init() {
    InitParam initParam;

    auto host = m_config.zoomHost().c_str();

    initParam.strWebDomain = host;
    initParam.strSupportUrl = host;

    initParam.emLanguageID = LANGUAGE_English;

    initParam.enableLogByDefault = true;
    initParam.enableGenerateDump = true;

    auto err = InitSDK(initParam);
    if (hasError(err)) {
        Log::error("InitSDK failed");
        return err;
    }

    // Creating NetworkConnectionHelper
    if ((err = CreateNetworkConnectionHelper(&m_networkConnectionHelper)) == SDKError::SDKERR_SUCCESS) {
        std::cout << "CreateNetworkConnectionHelper created." << std::endl;
    } else {
        return err;
    }

    // Setting Proxy
    ProxySettings proxySettings;
    proxySettings.proxy = "proxy.server:8443";  // Proxy address and port
    proxySettings.auto_detect = false;  // Disable auto-detection, use the specified proxy

    if ((err = m_networkConnectionHelper->ConfigureProxy(proxySettings)) == SDKError::SDKERR_SUCCESS) {
        std::cout << "Proxy configured." << std::endl;
    } else {
        return err;
    }

    // Registering NetworkConnectionHandler
    if ((err = m_networkConnectionHelper->RegisterNetworkConnectionHandler(m_networkConnectionHandler)) == SDKError::SDKERR_SUCCESS) {
        std::cout << "NetworkConnectionHandler registered. Detecting proxy." << std::endl;
    } else {
        return err;
    }

    return createServices();
}


SDKError Zoom::createServices() {
    auto err = CreateMeetingService(&m_meetingService);
    if (hasError(err)) return err;

    err = CreateSettingService(&m_settingService);
    if (hasError(err)) return err;

    auto meetingServiceEvent = new MeetingServiceEvent();
    meetingServiceEvent->setOnMeetingJoin(onJoin);
    //When bot leaves the meeting, exit the app.
    meetingServiceEvent->setOnMeetingEnd(onLeave);

    err = m_meetingService->SetEvent(meetingServiceEvent);
    if (hasError(err)) return err;

    return CreateAuthService(&m_authService);
}

SDKError Zoom::auth() {
    SDKError err{SDKERR_UNINITIALIZE};

    auto id = m_config.clientId();
    auto secret = m_config.clientSecret();

    if (id.empty()) {
        Log::error("Client ID cannot be blank");
        return err;
    }

    if (secret.empty()) {
        Log::error("Client Secret cannot be blank");
        return err;
    }

    err = m_authService->SetEvent(new AuthServiceEvent(onAuth));
    if (hasError(err)) return err;

    generateJWT(m_config.clientId(), m_config.clientSecret());

    AuthContext ctx;
    ctx.jwt_token = m_jwt.c_str();

    return m_authService->SDKAuth(ctx);
}

void Zoom::generateJWT(const string& key, const string& secret) {

    m_iat = std::chrono::system_clock::now();
    m_exp = m_iat + std::chrono::hours{24};

    m_jwt = jwt::create()
            .set_type("JWT")
            .set_issued_at(m_iat)
            .set_expires_at(m_exp)
            .set_payload_claim("appKey", claim(key))
            .set_payload_claim("tokenExp", claim(m_exp))
            .sign(algorithm::hs256{secret});
}

In main.cpp it calls the init() and auth() methods sequentially

    // initialize the Zoom SDK
    err = zoom->init();
    if(Zoom::hasError(err, "initialize"))
        return err;

    // authorize with the Zoom SDK
    err = zoom->auth();
    if (Zoom::hasError(err, "authorize"))
        return err;

In our integration environment the call back AuthServiceEvent::onAuthenticationReturn(AuthResult result) returns AUTHRET_UNKNOWN

I’d like to request help from you to understand the root cause of this authentication failure. I have the encrypted debug log available, please kindly let me know how you want me to share the log file with you to debug this further.

Thank you for your assistance and time.

@tzhou in this case, is the proxy server a firewall service?

Some of the cases which I’ve seen previously, the firewall / proxy service blocks off zoom.us domain name, hence the integration environment didn’t work while development environment worked.

1 Like

Thank you @chunsiong.zoom for getting back to me, as to the network configuration, we have allowed outbound access to zoom.us from the integration environment, where I’m able to curl https://api.zoom.us/ and got 200 back.

$ curl -v --proxy "http://proxy.server:8443" https://api.zoom.us/

* Trying 10.80.23.166:8443...

* Connected to (nil) (10.80.23.166) port 8443 (#0)

* allocate connect buffer!

* Establish HTTP proxy tunnel to api.zoom.us:443

> CONNECT api.zoom.us:443 HTTP/1.1

> Host: api.zoom.us:443

> User-Agent: curl/7.81.0

> Proxy-Connection: Keep-Alive

>

< HTTP/1.1 200 Connection established

@chunsiong.zoom do you mind checking the encrypted SDK logs for us? I don’t see any option to upload here but I can send you through email if you don’t mind.

@tzhou I’ll PM you for the logs. Meanwhile could you try the domain for zoom.us instead of api.zoom.us?

It also returns 200 back.

$ curl -v --proxy "http://proxy.server:8443" https://zoom.us/        
*   Trying 10.80.23.166:8443...
* Connected to (nil) (10.80.23.166) port 8443 (#0)
* allocate connect buffer!
* Establish HTTP proxy tunnel to zoom.us:443
> CONNECT zoom.us:443 HTTP/1.1
> Host: zoom.us:443
> User-Agent: curl/7.81.0
> Proxy-Connection: Keep-Alive
> 
< HTTP/1.1 200 Connection established

@chunsiong.zoom In the meantime I observed that the proxy side car within the application pod didn’t receive any zoom authentication related requests but only the curl request I sent manually for testing purpose, so I suspect in the application code the zoom SDK is not honoring the proxy setup