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
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`