This chapter describes how to use the Connection Manager to manage network connections in an application.
Managing Profiles
The Connection Manager handles network interfaces on request from an application by manipulating connection profiles. A connection profile can be described as a configuration setup of a network interface. It is represented by a string. For example, the following profile sets up the interface eth0 and assign it the IPv4 address 10.0.0.3. :
"NetOut/IPIF:LoIP='10.0.0.3'/Ethernet:DDev='eth0'"
Slashes ("/") separate the various plug-ins of a profile. A plug-in handles the configuration of a physical interface (e.g. Ethernet) or a protocol (e.g. IPIF for IPv4). Each plug-in imports and exports parameters (such as LoIP='10.0.0.3') that can be hard-coded in the profile or filled at connection time. (For more information on profiles see "Connecting Profiles".)
Profiles may also be tagged for specific use (web, IM, etc...)
Profiles fall into three main categories:
- NetOut/* for profiles supporting connection to the outside world
- VPN/* for VPN profiles
- NetIn/* for connection sharing profiles (see "Connection Sharing")
Data Channels
Before using standard socket functions to communicate, an application must connect a data channel. This brings up the right interface for the application's needs, and ensures that all of the application's network traffic goes through that interface.
A data channel contains a list of suitable profiles sorted by priority. Trying to connect a data channel involves attempting to connect profiles of the list until one succeeds. If a profile is already connected, it is chosen.
Binding Sockets
When a data channel is connected, sockets created in the same thread are bound to the channel interface. It is possible to change the active data channel with alp_cnc_channel_select(). Note that this does not change the binding of already created sockets.
The Connection Manager provides an API to create bound sockets: alp_cnc_socket(). An application can also include the file cnc_inet.h, which overrides socket() with alp_cnc_socket(). This is automatically done for ACCESS Linux Platform applications. Such sockets are bound to the network interface of the selected channel.
If an application wants to create an unbound socket, for example to be able to bind it later to a well-known port, it can use alp_cnc_socket_unbound(). This function simply uses the standard socket() call, and does not bind it to the network interface.
Disconnecting Channels
Disconnecting a channel may not immediately disconnect the profile, since it may be used by other channels in the system. Moreover, the disconnection of a profile is postponed in time. This mechanism avoids losing time when an application connects and disconnects channels quickly.
Channels do not survive their creating application.
Programming Examples
Opening and Closing a Data Channel
First, create a data channel matching the application's needs.
tagStr is a string containing the search criteria. Interfaces should to communicate using IPv4, thus "NetOut/IPIF/*".
Some carriers force developers to use well-defined connections for some services (for example, MMS, or WAP-only APN). The example in this section uses a web profile or a generic one, which is reflected by the "srv0" parameter.
For the data channel, you must specify flags. The standard progress user interface should be displayed during the connection process. The CHOOSER flag is provided for the user to validate the next profile to try.
// build the channel search string: only consider // web or generic profiles using IPv4 snprintf(tagStr,       TAG_STRING_SIZE - 1,       "NetOut:srv0=%d/IPIF/*",       ALP_CNC_CHANNEL_SRV_WEB | ALP_CNC_CHANNEL_SRV_GENERIC); // set the channel flags cncFlags =    ALP_CNC_CONNECT_PROGRESS_UI|ALP_CNC_CONNECT_CHOOSER_UI; // create a CNC channel    err = alp_cnc_channel_create(tagStr, cncFlags, &chanId);
Next, connect the data channel:
err = alp_cnc_channel_connect(chanId);
When the channel is connected, all data traffic goes through the network interface associated with the channel.
You can also use an existing channel that is connected in another thread - and may be in another process - by selecting it:
err = alp_cnc_channel_select(chanId);
The calling thread becomes an owner of the channel. Channel IDs are integers, so they can be exchanged between processes. Note that selecting a channel is the same as reopening it; a channel used by several threads - the thread that created it and the threads that selected it - must be closed in all threads.
The third step in the process of opening and closing a data channel involves sending and receiving data using standard socket functions.
If a thread is the owner of a channel, the alp_cnc_socket() function returns a socket bound to the interface associated with the channel. If a thread is not the owner of a channel, alp_cnc_socket() returns a normal socket, not bound to any interface.
In ACCESS Linux Platform applications, the socket function is automatically redirected to alp_cnc_socket(), which automatically binds the socket to the interface associated with the active channel.
Send and receive data using standard socket functions:
host = gethostbyname(hostName); memcpy(&to_addr.sin_addr, host->h_addr, Â Â Â Â Â Â Â Â sizeof(to_addr.sin_addr)); to_addr.sin_family = AF_INET; to_addr.sin_port = htons(HTTP_PORT); sockfd = socket(AF_INET, SOCK_STREAM, 0); connect(sockfd, (struct sockaddr *)&to_addr, Â Â Â Â Â Â Â sizeof(struct sockaddr_in)); send(sockfd, buffer, strlen(buffer), 0); recv(sockfd, buffer, MAX_BUFFER_SIZE, 0); close(sockfd);
In an application where the socket is redirected to alp_cnc_socket(), all sockets are bound to the interface associated with the active channel. To create an unbound socket, (for example, to manually bind it to a port and listen on it) an application should use alp_cnc_unbound_socket(). This will not bind the socket to the interface.
Outside of an ACCESS Linux Platform application, you can bind the sockets to the active channel using one of the following methods:
- Redirect
socket()calls toalp_cnc_socket(): Includecnc_inet.hin one file of your main code module (for example, where you declare themain()function). - Bind the sockets to the right interface by hand: Get the LoIP parameter of the active channel (see "Getting Connection Information"). Store the information and use the
libc bind()function to bind sockets to the interface.
Next, disconnect the data channel. Here we can specify the reason of the disconnection. ALP_CNC_DISCONNECTED_USER is the most standard reason for application.
err = alp_cnc_channel_disconnect(chanId, Â Â Â Â Â Â Â ALP_CNC_DISCONNECTED_USER);
Finally, destroy the data channel:
err = alp_cnc_channel_destroy(chanId);
Note that calls to alp_cnc_channel_create() and alp_cnc_channel_connect() can be replaced by a call to alp_cnc_channel_open(). For example:
err = alp_cnc_channel_open(tagStr, cncFlags, &chanId);
In the same way, calls to alp_cnc_channel_disconnect() and alp_cnc_channel_destroy() can be replaced by a call to alp_cnc_channel_close():
err = alp_cnc_channel_close(chanId, Â Â Â Â Â ALP_CNC_DISCONNECTED_USER);
Getting Connection Information
An application can retrieve a description of a channel calling alp_cnc_channel_get(). This returns an AlpCncChannel structure:
struct _AlpCncChannel {
   AlpCncChannelProfile *profiles; // information about all profiles
                  // compatible with this channel (computed by
                  // alp_cnc_channel_create, including unavailable)
   uint32_t flags;    // connection flags (ui, etc)
   uint16_t timeout;  // 0 = disabled, else seconds of inactivity
   uint16_t global_state;   // incremented each time a change is made on the
             //channel (not useful for an app)
   alp_status_t error;     // error (when state is disconnected)
   int fd;             // fd (when state is connected)
   uint32_t plugin_id;  // the current plug-in id (progress)
   uint32_t profile_id;  // the current profile id
   int8_t plugin_index;  // the current plugin index in the profile
   int8_t plugins_count;  // the items total count in the current profile
   uint8_t channel_id;    // unique channel id allocated by the server
   int8_t profiles_count;  // number of items in the "profiles" list
   int8_t profile_index;    // index of the current profile in "profiles" list
   uint8_t state;          // connection state
   uint8_t cancelled;     // the cancel reason if != 0
};
If the channel is connected, the ID of the connected profile is stored in the profile_id field.
An application can also retrieve a description of a profile by calling alp_cnc_object_get_info(). This returns an AlpCncObjectInfo structure:
struct _AlpCncObjectInfo {
char name[ALP_CNC_MAX_NAME_LENGTH + 1];Â Â Â // object name
uint32_t object_id;Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â // object id
uint8_t version;Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â // version
uint8_t type;Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â // object type
uint16_t count;Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â // item count in the object
uint8_t priority;Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â // object priority
uint8_t availability;Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â // profile availability
uint16_t options;Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â // options
uint32_t manual_id;Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â // reserved
char avail_icon_name[ALP_CNC_ICON_NAME_MAX_SIZE];Â // profile avail. icon id
};
A profile's parameters are retrieved by calling alp_cnc_object_get_parameters(), which takes an array of AlpCncParameter as an In/Out parameter:
struct _AlpCncParameter {
     uint32_t name;        // parameter name
     uint16_t size;        // size of the value in bytes
     uint8_t type;         // parameter type (see kCncXxxxParameterType)
     uint8_t flags;        // flags for parameter checking
     union {
     uint32_t asInteger;
     char *asString;
     uint8_t *asBinary;
     } value;              // parameter value
};
If size and flags are set, alp_cnc_object_get_parameters() performs checks on the parameters and returns an error if they do not match. Otherwise, it is the application's responsibility to do these checks.
If the parameter is not present in the profile, its name in the array is set to ALP_CNC_PARAMETER_UNDEFINED.
void prv_get_info(uint8_t chanId) {    AlpCncChannel        *chan;    alp_status_t          err;    AlpCncObjectInfo      info;    // list of parameters we want    AlpCncParameter params[] = {        {          .name = ALP_CNC_PARAMETER_INTERFACE_NAME,          .size = ALP_CNC_PARAMETER_NO_SIZE,          .type = ALP_CNC_PARAMETER_TYPE_INTEGER,          .flags = ALP_CNC_PARAMETER_CHECK_TYPE_AND_SIZE,          .value = {0}          },          {          .name = ALP_CNC_PARAMETER_LOCAL_IP,          .size = ALP_CNC_PARAMETER_NO_SIZE,          .type = ALP_CNC_PARAMETER_TYPE_STRING,          .flags = ALP_CNC_PARAMETER_CHECK_TYPE_AND_SIZE |                       ALP_CNC_PARAMETER_CHECK_MANDATORY,          .value = {0}          },          {          .name = ALP_CNC_PARAMETER_GATEWAYS,          .size = ALP_CNC_PARAMETER_NO_SIZE,          .type = ALP_CNC_PARAMETER_TYPE_INTEGER,          .flags = ALP_CNC_PARAMETER_CHECK_TYPE_AND_SIZE,          .value = {0}          },          {          .name = ALP_CNC_PARAMETER_DNS_SERVERS,          .size = ALP_CNC_PARAMETER_NO_SIZE,          .type = ALP_CNC_PARAMETER_TYPE_INTEGER,          .flags = ALP_CNC_PARAMETER_CHECK_TYPE_AND_SIZE,          .value = {0}          },          {          .name = ALP_CNC_PARAMETER_TABLE_END,          }          };    // get the channel description chan = alp_cnc_channel_get(chanId, &err); if (chan == NULL) {     printf("Channel get failed (0X%08X)\r\n", (uint32_t)err);     return; } // chan->profile_id is the ID of the connected profile // get info on the connected profile err = alp_cnc_object_get_info(chan->profile_id, &info); if (err != ALP_STATUS_OK) {     printf("Get info failed (0X%08X)\r\n", (uint32_t)err);     return; } // retrieve profile's parameter err = alp_cnc_object_get_parameters(chan->profile_id,            0,            ALP_CNC_GET_PARAMETERS_WHOLE_PROFILE|ALP_CNC_GET_PARAMETERS_ACTIVE,            params); // display info printf("Connected through profile \"%s\" ID: %d\r\n", info.name, chan->profile_id); // display parameters if (params[0].name != ALP_CNC_PARAMETER_UNDEFINED) { printf("\tInterface is %s\r\n", params[0].value.asString); } if (params[1].name != ALP_CNC_PARAMETER_UNDEFINED) { printf("\tIP address is %s\r\n", params[1].value.asString); } if (params[2].name != ALP_CNC_PARAMETER_UNDEFINED) { printf("\tGateway is %s\r\n", params[2].value.asString); } if (params[3].name != ALP_CNC_PARAMETER_UNDEFINED) { printf("\tDNS servers are %s\r\n", params[3].value.asString); } }
Advanced: Using Provisioned Profiles
Connection manager profiles can be provisioned using OMA DM and/or OMA CP. The Connection Manager library provides functions to facilitate the enumeration and connection of provisioned applications to provisioned profiles, as well as to related proxy information.
An application that creates a data channel uses either the alp_cnc_channel_create(), or directly calls the alp_cnc_channel_open() function. The first parameter of these functions is a search string that gives criteria to the Connection Manager to make the list of connection profiles compatible with the application. To use provisioned profiles, the application uses the same functions, passing a special search string that is given by OMA DM provisioning API.
NOTE: The alp_cnc_channel_create() function accepts several search strings separated by the ";" (semicolon) character.
The following example illustrates how a browser application generates a search string:
alp_status_t status; OMADM_AccountListType accounts; OMADM_BrowserSettingsType browser_settings; OMADM_PXLogicalType proxy; char* omadm_search_string = ""; char* legacy_search_string = "NetOut:srv0=0x100000004/ IPIF/*"; char* search_string; uint8_t channel_id; size_t len; status = alp_omadm_browser_account_get_list(&accounts); if (status == ALP_STATUS_OK) { if (accounts.itsCount >= 1) { status = alp_omadm_browser_account_get_settings(accounts.itsKey[0], &browser_settings); if (status == ALP_STATUS_OK && browser_settings.itsConnInfo != NULL) omadm_search_string = browser_settings.itsConnInfo; } } len = strlen(omadm_search_string); if (len != 0) // Add space for ';' len++; search_string = malloc(len + strlen(legacy_search_string) + 1); if (!search_string) goto finalize; strcpy(search_string, omadm_search_string); if (*omadm_search_string) strcat(search_string, ";"); strcat(search_string, legacy_search_string);
After creating the search string, an application can usually connect:
status = alp_cnc_channel_open(searchString, ALP_CNC_CONNECT_PROGRESS_UI, &channel_id); if(status != 0) { channel_id = 0; return status; }
When the channel is connected, the application can retrieve proxy information associated with the connection. The application (and the application developers) must decide how to handle the following potential cases:
Table 3.1 Handling Proxy Information
After a channel is connected, you can get proxy information associated with the connected profile as shown in the following example.
// The 2nd parameter (0) will return the first proxy   // associated with the connected profile // An application that wants information about other // proxies must call the same function with the value // 0, 1, 2, etc. The function returns an error when there // is no proxy. status = alp_cnc_channel_proxy_get_settings(channel_id, 0, &proxy); if (status != ALP_STATUS_OK) { // No proxy information } else { // Proxy defines the logical proxy associated with the // connected profile. // proxy.itsPxPhysical points to the physical proxy // associated with the connected profile. // Use proxy settings. }
The application should free the memory allocated by the provisioning API as soon as the application no longer needs the information. The provisioning API provides special functions to handle freeing of memory, as shown in the following example:
alp_omadm_browser_account_free_settings(&browser_settings); alp_omadm_account_free_list(&accounts); alp_omadm_pxlogical_free_settings(&proxy);
Setting the Default Profile ¶
An application can define a default profile that will be chosen when connecting a channel. The application must not use a custom setting to store a profile ID because the OMA DM provisioned profile ID may change from one session to another. Instead, applications must use alp_cnc_channel_set_default() to store a channel default profile.
To set or unset a default profile, an application must perform the following operations:
- Call
alp_cnc_channel_create()with the tags it normally uses to open a channel. - Use
alp_cnc_channel_get()to get the list of profiles that are compatible with the application - Display the profile list to the user, from which the user will select one profile (or "none" for an automatic connection).
- Use
alp_cnc_channel_set_default()to store the user's choice of profiles.
An application can also directly call alp_cnc_channel_settings(), which provides a user interface that presents a choice of default profiles.
Applications that support provisioning and that use the alp_cnc_channel_set_default() to allow users to change the default profile must call alp_cnc_channel_set_default() to set the default profile back to "Auto" when the provisioned settings change. When a user accepts a new set of provisioned settings, the provisioned settings should become the default settings.
To determine whether provisioning settings have changed, an application can register for the appropriate notification, defined in omadm_provisiong.h, or by checking the application version number on start up, using one of the alp_omadm_*_settings_version methods.
Obtaining the Default Profile from a User
The Connection Manager defines the alp_cnc_channel_settings(char* tags) function, which displays a list of compatible profiles to the user, given a search string. The user can choose a default profile. Whenever an application tries to connect a channel using this search string in alp_cnc_channel_create() or alp_cnc_channel_open(), it attempts to use the default profile first.
Networked applications should add a menu option to allow the user to choose a default profile. They must use the same search strings used in alp_cnc_channel_create() and/or alp_cnc_channel_open().
Working with the Disconnection Timeout
The disconnection timeout feature avoids multiple connections or disconnections of the same profile when an application tries to frequently open or close a data channel. The feature also allows an application to avoid disconnecting a profile when switching from one networked application to another.
The disconnection timeout feature is defined on a per-profile basis: the special DisT parameter can be set to values from 0 (no disconnection timeout, i.e. disconnection is done when the channel is closed) up to 3600 (no disconnection at all). If a profile does not define the DisT parameter, the default disconnection timeout is applied.
In ACCESS Linux Platform, all profiles use the default disconnection timeout of 10 minutes.
In some situations, carrier or licencees may want to disable this feature for some profiles or some applications. For example, a MMS application that works in the background may want to disconnect the profile as soon as the communication is terminated. The configuration of the disconnection timeout is performed in the Connection Manager's configuration files (located in /etc/cnc/<target>.cnc). This means that the configuration is target- specific and can be easily performed by carriers, licencees, or integrators by editing these configuration files.
In ACCESS Linux Platform, there are two ways to alter the disconnection timeout:
- Built-in profiles: Disconnection timeout can have different values given the link used for the connection. For example the Wi-Fi profile can have a disconnection timeout of 30 seconds. Just add a
DisT=time-in-secondsparameter to the NetOut plug-in of the network profile. - Built-in templates: If user or OMA-provisioned profiles must have a disconnection timeout, you can add a DisT parameter to a template. Templates are used when creating a new network profile. For example, add
DisT=30to a GSM profile andDisT=120to a GPRS profile.
The Connection Manager also allows carriers and licencees to have better control of the disconnection timeout using the following two methods:
- Auto-profiles: New profiles that associate an application class with automatic parameters. When an application from a specific class creates a channel, and if there are automatic parameters associated with this application class, these parameters will be automatically set in the connected profile.
- Background flag: New ALP_CNC_CONNECT_BACKGROUND flag that can be used by applications when creating a new channel:
status = alp_cnc_channel_open("NetOut?:srv0=0x2", ALP_CNC_CONNECT_BACKGROUND, &chan_id)
Channels connected with this flag are marked as "background" channels, and the flag is internally handled as a class. Therefore, the carrier, licensee, or integrator can create an auto-profile for such channels.
Advanced: Using Several Channels (Multi-homing)
Use case: a mail client receiving an HTML email with link to web images. Carriers may ask mail client to use a specific 3GPP data APN to connect the carrier email server.
The application first uses a data channel to connect its mail server:
uint8_t mailChanId; snprintf(tagStr, TAG_STRING_SIZE - 1, "NetOut:srv0=%d/IPIF/*", ALP_CNC_CHANNEL_SRV_MAIL); cncFlags = ALP_CNC_CONNECT_PROGRESS_UI; err = alp_cnc_channel_create(tagStr, cncFlags, &mailChanId); err = alp_cnc_channel_connect(mailChanId);
Then it creates a socket to retrieve new emails:
int mailSock; mailSock = socket(AF_INET, SOCK_STREAM, 0); connect(mailSock, (struct sockaddr *)&srv_addr, sizeof(struct sockaddr_in)); send(mailSock, buffer, strlen(buffer), 0); recv(mailSock, buffer, MAX_BUFFER_SIZE, 0);
When needed, it creates a channel to retrieve web images:
uint8_t  webChanId; snprintf(tagStr, TAG_STRING_SIZE - 1, "NetOut:srv0=%d/IPIF/*", ALP_CNC_CHANNEL_SRV_WEB); cncFlags = ALP_CNC_CONNECT_PROGRESS_UI; err = alp_cnc_channel_create(tagStr, cncFlags, &mailChanId); err = alp_cnc_channel_connect(mailChanId);
Once the channel is connected, it is the active channel. A newly created socket uses it to send and retrieve data.
int webSock; webSock = socket(AF_INET, SOCK_STREAM, 0); connect(webSock, (struct sockaddr *)&web_addr, sizeof(struct sockaddr_in)); send(webSock, buffer, strlen(buffer), 0); recv(webSock, buffer, MAX_BUFFER_SIZE, 0); close(webSock);
To create another socket to the mail server, first return to the context of the mail data channel, and then create the socket:
int anotherMailSock; err = alp_cnc_channel_select(mailChanId); anotherMailSock = socket(AF_INET, SOCK_STREAM, 0); connect(anotherMailSock, (struct sockaddr *)&srv_addr, sizeof(struct sockaddr_in));
Note that active channels are thread-wide.
Using a Custom Progress UI
cncwget -t http://www.google.com
An application can override the default progress UI used during the channel connection. It has to implement a callback:
void *prv_progress(int request, void *data, void *uData)
It can then set the global variable gcnc_progress exported by the Connection Manager library.
ALP_CNC_CONNECT_PROGRESS_UI set in the data channel's flags activates the default UI. The callback is called whenever the flags are called.
The callback returns NULL in case of error. In other cases the pointer returned is passed as uData at next callback call.
- data
- A pointer to an
AlpCncChannelstructure describing the channel being connected. - uData
- A pointer to callback's own data. Contains what was returned by the callback at previous call.
- request
- One the following values:
-
ALP_CNC_PROGRESS_INIT: Initialization. Called before actually connecting the data channel. -
ALP_CNC_PROGRESS_UPDATE: Called between connection of individual plug-ins. -
ALP_CNC_PROGRESS_CHOOSER: Called only ifALP_CNC_CONNECT_CHOOSER_UIis set in the data channel's flags. This is called if the connection of a profile fails. Here the application can modify which profile will be connected next by changing 'profile_index' in theAlpCncChannelstructure. It can also cancel the connection by setting the 'cancelled' field. -
ALP_CNC_PROGRESS_FINI: Finalize. Called at the end of the connection (successful or not).
void *prv_progress(int request, void *data, void *uData) { AlpCncChannel *chan = (AlpCncChannel *) data; switch (request) { case ALP_CNC_PROGRESS_INIT: uData = (void *) (1); // must not be NULL (NULL == cancel) break; case ALP_CNC_PROGRESS_CHOOSER: { char line[16]; int8_t index; printf("*** Chooser: Automatic choosen profile is: %u (index %d in [0-%d])\n", chan->profile_index >= 0 ? chan->profiles[chan->profile_index].id : 0, chan->profile_index, chan->profiles_count - 1); for (index = 0; index < chan->profiles_count; index++) { AlpCncObjectInfo info; alp_cnc_object_get_info(chan->profiles[index].id, &info); printf("%s%d. %s%s\n", index == chan->profile_index ? "->" : " ", index, info.name, index == chan->profile_index ? "<-" : " "); } printf("*** Chooser: Enter to accept, a profile index to change, or c to cancel: "); fgets(line, 16, stdin); if (*line == 0 || *line == '\n') break; if (*line == 'c') { chan->cancelled = ALP_CNC_CANCELLED_USER; break; } index = atoi(line); if (index < 0 || index >= chan->profiles_count) break; chan->profile_index = index; break; } case ALP_CNC_PROGRESS_UPDATE: { char *stateStr; AlpCncObjectInfo info; if ((chan->flags & ALP_CNC_CONNECT_END) != 0) break; switch (chan->state) { case ALP_CNC_STATE_CONNECTED: stateStr = "Connected"; break; case ALP_CNC_STATE_CONNECTING: stateStr = "Connecting"; break; case ALP_CNC_STATE_DISCONNECTED: stateStr = "Disconnected"; break; case ALP_CNC_STATE_DISCONNECTING: stateStr = "Disconnecting"; break; case ALP_CNC_STATE_SUSPENDED: stateStr = "Suspended"; break; case ALP_CNC_STATE_NOT_CONNECTED: stateStr = "Not connected"; break; default: stateStr = "unknown"; break; } if (chan->plugin_id) { alp_cnc_object_get_info(chan->plugin_id, &info); } else { *info.name = 0; } if (chan->error) printf("*** error = 0x%x ***\n", chan->error); printf("%s %d%s%s\n", stateStr, chan->profile_id, *info.name ? ": " : "", info.name); } break; case ALP_CNC_PROGRESS_FINI: break; } return uData; }
Using VPN
VPN profiles are described as other profiles for the Connection Manager. For example:
"VPN:ReHt='vpn.company.com',User='me',Pass=0x00000001/IPIF/ IPSEC:GrpN='company'/NetAccess:CPro='Wifi Connection')
A VPN profile needs a NetOut profile. When connected, the VPN profile patches the underlying NetOut profile so data traffic on this profile can reach the private network.
NOTE: The effect of the VPN profile is system wide. Every application using the patched NetOut profile gains access to the private network.










