The provided example is a C program that demonstrates the use of the libdomain library to perform an LDAP search operation. The program establishes a connection to the LDAP server and executes a search in the specified directory server.

    The program takes command-line arguments to specify LDAP server connection parameters and search parameters:

 

$ ./libdomain-c-sample --host ldap://dc.example.org:389 --user administrator --pass password --bind "dc=example,dc=org" --sasl

    Command Line Options  :

       * --host (-h) — specifies the LDAP server in the format "protocol://address:port," for example, "ldap://example.org:389";

       * --user (-u) — specifies the LDAP username;

       * --pass (-w) — specifies the LDAP password;

       * --bind (-b) — specifies the Distinguished Name (DN) for LDAP binding, for example, "dc=example,dc=org";

       * --sasl (-s) — enables the use of SASL binding.

#include <argp.h>
#include <libdomain/domain.h>
#include <libdomain/directory.h>
#include <libdomain/entry.h>
#include <libdomain/connection_state_machine.h>
#include <stdio.h>
#include <stdbool.h>
#include <talloc.h>
 
static char* LDAP_DIRECTORY_ATTRS[] = { "objectClass", NULL };
 
static void exit_callback(verto_ctx *ctx, verto_ev *ev)
{
    (void) ctx;
    (void) ev;
 
    verto_break(ctx);
}
 
static enum OperationReturnCode connection_on_error(int rc, void* unused_a, void* connection)
{
    (void)(unused_a);
 
    verto_break(((ldap_connection_ctx_t*)connection)->base);
 
    error("Unable to perform operation!\n");
 
    exit(EXIT_FAILURE);
 
    return RETURN_CODE_SUCCESS;
}
 
static void connection_on_update(verto_ctx *ctx, verto_ev *ev)
{
    (void)(ctx);
 
    struct ldap_connection_ctx_t* connection = verto_get_private(ev);
 
    if (connection->state_machine->state == LDAP_CONNECTION_STATE_RUN)
    {
        verto_del(ev);
 
        search(connection, "dc=domain,dc=alt", LDAP_SCOPE_SUBTREE,
               "(objectClass=*)", LDAP_DIRECTORY_ATTRS, 0, NULL);
    }
 
    if (connection->state_machine->state == LDAP_CONNECTION_STATE_ERROR)
    {
        verto_break(ctx);
 
        error("Error encountered during bind!\n");
    }
}
 
const char *argp_program_version = "1.0.0";
const char *argp_program_bug_address = "https://bugzilla.altlinux.org";
 
static char doc[] = "Libdomain get RootDSE sample.";
static char args_doc[] = "[HOSTNAME] [BINDDN] <USERNAME> <PASSWORD> <USE_SASL>";
 
static struct argp_option options[] =
{
    { "host", 'h', "HOST", 0, "Host. Use protocol://adress:port format e.g. ldap://dc.example.org:389."},
    { "user", 'u', "USER", 0, "User name."},
    { "pass", 'w', "PASS", 0, "Password."},
    { "sasl", 's', "SASL", 0, "Whether or not use SASL bind."},
    { "bind", 'b', "BIND", 0, "Bind dn. For example: \"dc=example,dc=org\"."},
    { 0 }
};
 
struct arguments
{
    char* hostname;
    char* password;
    char* username;
    char* bind_dn;
    bool useSasl;
};
 
static error_t parse_opt(int key, char *arg, struct argp_state *state)
{
    struct arguments *arguments = state->input;
 
    switch (key)
    {
    case 'h':
        arguments->hostname = arg;
        break;
    case 'u':
        arguments->username = arg;
        break;
    case 'w':
        arguments->password = arg;
        break;
    case 's':
        arguments->useSasl = true;
        break;
    case 'b':
        arguments->bind_dn = arg;
        break;
    case ARGP_KEY_ARG:
        return 0;
    default:
        return ARGP_ERR_UNKNOWN;
    }
 
    return 0;
}
 
static struct argp argp = { options, parse_opt, args_doc, doc, NULL, NULL, NULL };
 
int main(int argc, char **argv)
{
    TALLOC_CTX* talloc_ctx = talloc_new(NULL);
 
    struct arguments arguments;
 
    arguments.hostname = NULL;
    arguments.username = NULL;
    arguments.password = NULL;
    arguments.bind_dn = NULL;
    arguments.useSasl = false;
 
    argp_parse(&argp, argc, argv, 0, 0, &arguments);
 
    if (arguments.hostname == NULL)
    {
        printf("Error: Missing required argument --host\n");
        exit(EXIT_FAILURE);
    }
 
    if (arguments.bind_dn == NULL)
    {
        printf("Error: Missing required argument --bind\n");
        exit(EXIT_FAILURE);
    }
 
    const int update_interval = 1000;
 
    ld_config_t *config = NULL;
    config = ld_create_config(talloc_ctx, arguments.hostname, 0, LDAP_VERSION3, "dc=domain,dc=alt",
                              arguments.username, arguments.password, !arguments.useSasl, false, arguments.useSasl, false,
                              update_interval, "", "", "");
 
    const int exit_time = 10000;
 
    LDHandle *handle = NULL;
    ld_init(&handle, config);
 
    ld_install_default_handlers(handle);
    ld_install_handler(handle, connection_on_update, update_interval);
    ld_install_handler(handle, exit_callback, exit_time);
    ld_install_error_handler(handle, connection_on_error);
 
    ld_exec(handle);
 
    ld_free(handle);
 
    talloc_free(talloc_ctx);
 
    return 0;
}

Program Structure

Event Handling Functions:

   * exit_callback — triggers the program exit event;

   * connection_on_error — handles errors during LDAP operations;

   * connection_on_update — handles connection state updates and errors during connection establishment.

Option Parsing:

    * parse_opt — parses command-line options using the argp library.

Main Function:

1. First, a new talloc context is created using the talloc_new function.

2. The arguments structure is initialized to store command-line arguments.

3. Command-line arguments are processed using the argp_parse function.

4. Mandatory arguments, such as host and bind_dn, are checked, and the program exits if these arguments are not found.

5. A configuration structure for connecting to the LDAP server is created using the ld_config function.

6. The main library handle is initialized using the ld_init function.

7. Default event handlers are installed using ld_install_default_handlers(handle).

8. An event handler with the main program logic is set using ld_install_handler(handle, connection_on_update, update_interval).

9. An exit handler that shuts down the program after 10 seconds is installed using ld_install_handler(handle, exit_callback, exit_time). 

10. Error handlers are set.

11. Starting the main event cycle with ld_exec. 

12. Error handlers are set.

13. Resources are cleaned up using ld_free(handle) and talloc_free(talloc_ctx) functions.

 

Error Handling

    * Errors, such as the inability to perform LDAP operations, lead to program termination with the appropriate error message. 

Error handling is implemented in the connection_on_error function. However, connection error handling occurs in the connection_on_update function.

 

Notes

    * The program performs an LDAP search when the connection state changes to LDAP_CONNECTION_STATE_RUN. Objects for search are specified in the LDAP_DIRECTORY_ATTRS variable. 

 

Compilation

Link

To compile the program, you need to install the libdomain library:

# apt-get install libdomain-devel libconfig-devel

Clone the example:

$ git clone https://github.com/libdomain/libdomain-c-sample

Use the following compilation command:

$ mkdir build && cd build && cmake .. && make -j `nproc`