The FreeRADIUS server $Id: f3670dba8951ca10eb4948feb3dc3db9423a334f $
Loading...
Searching...
No Matches
directory.c
Go to the documentation of this file.
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or (at
5 * your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/**
18 * $Id: 8cf014313e7680ae4f6b9b1a2721b899bda20b9a $
19 * @file lib/ldap/directory.c
20 * @brief Determine remote server implementation and capabilities.
21 *
22 * As described by http://ldapwiki.willeke.com/wiki/Determine%20LDAP%20Server%20Vendor
23 *
24 * @copyright 2016 The FreeRADIUS Server Project.
25 * @copyright 2016 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
26 */
27RCSID("$Id: 8cf014313e7680ae4f6b9b1a2721b899bda20b9a $")
28
30
31#define LOG_PREFIX name
32
33#include <freeradius-devel/ldap/base.h>
34
36 { L("Active Directory"), FR_LDAP_DIRECTORY_ACTIVE_DIRECTORY },
37 { L("IBM"), FR_LDAP_DIRECTORY_IBM },
38 { L("NetScape"), FR_LDAP_DIRECTORY_NETSCAPE },
39 { L("OpenLDAP"), FR_LDAP_DIRECTORY_OPENLDAP },
40 { L("Oracle Internet Directory"), FR_LDAP_DIRECTORY_ORACLE_INTERNET_DIRECTORY },
41 { L("Oracle Unified Directory"), FR_LDAP_DIRECTORY_ORACLE_UNIFIED_DIRECTORY },
42 { L("Oracle Virtual Directory"), FR_LDAP_DIRECTORY_ORACLE_VIRTUAL_DIRECTORY },
43 { L("Samba"), FR_LDAP_DIRECTORY_SAMBA },
44 { L("Siemens AG"), FR_LDAP_DIRECTORY_SIEMENS_AG },
45 { L("Sun One Directory"), FR_LDAP_DIRECTORY_SUN_ONE_DIRECTORY },
46 { L("Unbound ID"), FR_LDAP_DIRECTORY_UNBOUND_ID },
47 { L("Unknown"), FR_LDAP_DIRECTORY_UNKNOWN },
48 { L("eDirectory"), FR_LDAP_DIRECTORY_EDIRECTORY }
49};
51
52/** Hash a naming context, case insensitively
53 *
54 */
56{
58}
59
60/** Compare two naming contexts, case insensitively
61 *
62 */
63static int8_t _naming_context_cmp(void const *one, void const *two)
64{
65 return CMP(strcasecmp(one, two), 0);
66}
67
69 LDAPMessage *result, char const *name)
70{
71 int entry_cnt, i, num, ldap_errno;
72 LDAPMessage *entry;
73 struct berval **values = NULL;
74 struct berval value;
76 char const * const *context_p;
77
78 /*
79 * Connections spawned concurrently may each run discovery
80 * against a shared directory, only parse the first response.
81 */
82 if (directory->discovered) return 0;
83
84 entry_cnt = ldap_count_entries(handle, result);
85 if (entry_cnt != 1) {
86 WARN("Capability check failed: Ambiguous result for rootDSE, expected 1 entry, got %i", entry_cnt);
87 return 1;
88 }
89
90 entry = ldap_first_entry(handle, result);
91 if (!entry) {
92 ldap_get_option(handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
93
94 WARN("Capability check failed: Failed retrieving entry: %s", ldap_err2string(ldap_errno));
95 return 1;
96 }
97 directory->discovered = true;
98
99 if (fr_ldap_entry_value_find(&value, handle, entry, "vendorname") > 0) {
100 directory->vendor_str = fr_ldap_berval_to_string(directory, &value);
101 INFO("Directory vendor: %s", directory->vendor_str);
102 }
103
104 if (fr_ldap_entry_value_find(&value, handle, entry, "vendorversion") > 0) {
105 directory->version_str = fr_ldap_berval_to_string(directory, &value);
106 INFO("Directory version: %s", directory->version_str);
107 }
108
109 if (directory->vendor_str) {
110 if (strcasestr(directory->vendor_str, "International Business Machines")) {
111 directory->type = FR_LDAP_DIRECTORY_IBM;
112 } else if (strcasestr(directory->vendor_str, "Samba Team")) {
113 directory->type = FR_LDAP_DIRECTORY_SAMBA;
114 }
115
116 goto found;
117 }
118
119 if (directory->version_str) {
120 /*
121 * Novell eDirectory vendorversion contains eDirectory
122 */
123 if (strcasestr(directory->version_str, "eDirectory")) {
125 /*
126 * Oracle unified directory vendorversion contains Oracle Unified Directory
127 */
128 } else if (strcasestr(directory->version_str, "Oracle Unified Directory")) {
130 /*
131 * Unbound directory vendorversion contains UnboundID
132 */
133 } else if (strcasestr(directory->version_str, "UnboundID")) {
135 /*
136 * NetScape directory venderversion contains Netscape-Directory
137 */
138 } else if (strcasestr(directory->version_str, "Netscape-Directory")) {
139 directory->type = FR_LDAP_DIRECTORY_NETSCAPE;
140 /*
141 * Siemens AG directory vendorversion contains DirX Directory
142 */
143 } else if (strcasestr(directory->version_str, "DirX Directory")) {
145 /*
146 * Sun One Directory vendorversion contains Sun Java
147 */
148 } else if (strcasestr(directory->version_str, "Sun Java")) {
150 }
151 goto found;
152 }
153
154 /*
155 * isGlobalCatalogReady is only present on ActiveDirectory
156 * instances. AD doesn't provide vendorname or vendorversion
157 */
158 if (fr_ldap_entry_value_find(&value, handle, entry, "isGlobalCatalogReady") > 0) {
160 goto found;
161 }
162
163 /*
164 * OpenLDAP has a special objectClass for its RootDSE
165 */
166 values = ldap_get_values_len(handle, entry, "objectClass");
167 if (values) {
168 num = ldap_count_values_len(values);
169 for (i = 0; i < num; i++) {
170 if ((values[i]->bv_len == sizeof("OpenLDAProotDSE") - 1) &&
171 (memcmp("OpenLDAProotDSE", values[i]->bv_val, values[i]->bv_len) == 0)) {
172 directory->type = FR_LDAP_DIRECTORY_OPENLDAP;
173 }
174 }
175 ldap_value_free_len(values);
176 goto found;
177 }
178
179 /*
180 * Oracle Virtual Directory and Oracle Internet Directory
181 */
182 if (fr_ldap_entry_value_find(&value, handle, entry, "orcldirectoryversion") > 0) {
183 if (memmem(value.bv_val, value.bv_len, "OID", 3)) {
185 } else if (memmem(value.bv_val, value.bv_len, "OVD", 3)) {
187 }
188 }
189
190found:
191 INFO("Directory type: %s", fr_table_str_by_value(fr_ldap_directory_type_table, directory->type, "<INVALID>"));
192
193 switch (directory->type) {
194 /*
195 * Active Directory and Samba don't implement RFC 5020 entryDN,
196 * but allow equality matches on distinguishedName instead.
197 */
200 directory->dn_attr = "distinguishedName";
201 directory->cleartext_password = false;
202 break;
203
205 directory->cleartext_password = false;
206 break;
207
208 default:
209 directory->cleartext_password = true;
210 break;
211 }
212
213 /*
214 * Evaluate what type of sync the directory supports
215 */
216 values = ldap_get_values_len(handle, entry, "supportedControl");
217 if (values) {
218 num = ldap_count_values_len(values);
219 for (i = 0; i < num; i++) {
220 if ((values[i]->bv_len == strlen(LDAP_CONTROL_SYNC)) &&
221 (memcmp(LDAP_CONTROL_SYNC, values[i]->bv_val, values[i]->bv_len) == 0)) {
222 INFO("Directory supports RFC 4533");
223 directory->sync_type = FR_LDAP_SYNC_RFC4533;
224 break;
225 }
226 if ((values[i]->bv_len == strlen(LDAP_SERVER_NOTIFICATION_OID)) &&
227 (memcmp(LDAP_SERVER_NOTIFICATION_OID, values[i]->bv_val, values[i]->bv_len) == 0)) {
228 INFO("Directory supports LDAP_SERVER_NOTIFICATION_OID");
230 break;
231 }
232 if ((values[i]->bv_len == strlen(LDAP_CONTROL_PERSIST_REQUEST)) &&
233 (memcmp(LDAP_CONTROL_PERSIST_REQUEST, values[i]->bv_val, values[i]->bv_len) == 0)) {
234 INFO("Directory supports persistent search");
236 break;
237 }
238 }
239 ldap_value_free_len(values);
240 } else {
241 WARN("No supportedControl returned by LDAP server");
242 }
243
244 /*
245 * Extract naming contexts
246 */
247 list = fr_ldap_str_list_afrom_result(directory, handle, result, "namingContexts", 0);
248 if (unlikely(!list)) {
249 WARN("Capability check failed: %s", fr_strerror());
250 return 1;
251 }
252 if (talloc_str_list_num(list) == 0) {
253 talloc_free(list);
254 return 0;
255 }
256
257 directory->naming_contexts = list->strings;
259 _naming_context_cmp, NULL));
260 for (context_p = list->strings; context_p < list->p; context_p++) {
261 fr_hash_table_insert(directory->naming_contexts_ht, *context_p);
262 }
263
264 return 0;
265}
266
267/** Allocate a directory structure with defaults
268 *
269 * dn_attr defaults to the RFC 5020 entryDN attribute, overridden when
270 * parsing the rootDSE detects a directory which doesn't implement entryDN.
271 */
273{
274 fr_ldap_directory_t *directory;
275
276 MEM(directory = talloc_zero(ctx, fr_ldap_directory_t));
277 directory->dn_attr = "entryDN";
278
279 return directory;
280}
281
282/** Find the naming context which contains a set of DNs
283 *
284 * Looks up successively shorter suffixes of each DN in the hash table of
285 * naming contexts (database suffixes) built when the rootDSE was parsed,
286 * and returns the naming context containing every DN. A search with the
287 * returned base covers all the DNs.
288 *
289 * @param[in] directory Directory discovery results, providing the naming contexts.
290 * @param[in] dn_list NULL terminated list of DNs to cover, no empty strings.
291 * @return
292 * - The matching naming context.
293 * - NULL if the directory hasn't been discovered yet, or no single
294 * naming context contains every DN.
295 */
296char const *fr_ldap_directory_common_base_find(fr_ldap_directory_t const *directory, char const * const *dn_list)
297{
298 char const *common = NULL;
299 char const * const *dn_p;
300
301 if (!directory->naming_contexts_ht) return NULL;
302
303 for (dn_p = dn_list; *dn_p; dn_p++) {
304 char const *context = NULL;
305 char const *p = *dn_p;
306
307 /*
308 * Check successively shorter suffixes of the DN,
309 * starting after each RDN separator.
310 */
311 while (p) {
313 if (context) break;
314
315 p = strchr(p, ',');
316 if (p) p++;
317 }
318 if (!context) return NULL;
319
320 /*
321 * Lookups return the stored string, so pointer
322 * comparison is enough to check every DN resolved
323 * to the same naming context.
324 */
325 if (!common) {
326 common = context;
327 continue;
328 }
329 if (common != context) return NULL;
330 }
331
332 return common;
333}
334
335/** State of an in progress rootDSE search on a connection being established
336 *
337 */
338typedef struct {
339 fr_ldap_connection_t *c; //!< Connection the rootDSE search was sent on.
340 int msgid; //!< Of the outstanding rootDSE search.
342
343/** Error reading from or writing to the file descriptor
344 *
345 * @param[in] el the event occurred in.
346 * @param[in] fd the event occurred on.
347 * @param[in] flags from kevent.
348 * @param[in] fd_errno The error that occurred.
349 * @param[in] uctx discover_ctx containing the connection and message ID.
350 */
352 UNUSED int flags, UNUSED int fd_errno, void *uctx)
353{
354 ldap_directory_discover_ctx_t *discover_ctx = talloc_get_type_abort(uctx, ldap_directory_discover_ctx_t);
355 fr_ldap_connection_t *c = discover_ctx->c;
356
357 talloc_free(discover_ctx);
358 fr_ldap_state_error(c); /* Restart the connection state machine */
359}
360
361/** Parse a rootDSE response from a server
362 *
363 * A failure parsing the rootDSE leaves the directory with its defaults, the
364 * connection is still usable, so the state machine advances either way.
365 *
366 * @param[in] el the event occurred in.
367 * @param[in] fd the event occurred on.
368 * @param[in] flags from kevent.
369 * @param[in] uctx discover_ctx containing the connection and message ID.
370 */
371static void _ldap_directory_discover_io_read(UNUSED fr_event_list_t *el, UNUSED int fd, UNUSED int flags, void *uctx)
372{
373 ldap_directory_discover_ctx_t *discover_ctx = talloc_get_type_abort(uctx, ldap_directory_discover_ctx_t);
374 fr_ldap_connection_t *c = discover_ctx->c;
375 char const *name = c->config->name;
376 LDAPMessage *result = NULL;
377
378 fr_ldap_rcode_t status;
379
380 status = fr_ldap_result(&result, NULL, c, discover_ctx->msgid, LDAP_MSG_ALL, "", fr_time_delta_wrap(0));
381 if (status == LDAP_PROC_SUCCESS) {
383 } else {
384 PWARN("Directory discovery failed, proceeding without directory capability data");
385 }
386 if (result) ldap_msgfree(result);
387
388 fr_ldap_state_next(c); /* onto the next operation */
389
390 talloc_free(discover_ctx); /* Also removes fd events */
391}
392
393/** Send a rootDSE search on a connection being established
394 *
395 * Called by the connection state machine after binding, so the directory
396 * capabilities (vendor, naming contexts) are known before the connection
397 * starts serving requests. Results are parsed into c->directory.
398 *
399 * @param[in] c LDAP connection to be queried.
400 * @return
401 * - 0 on success.
402 * - -1 on failure.
403 */
405{
406 static char const *attrs[] = LDAP_DIRECTORY_ATTRS;
407 ldap_directory_discover_ctx_t *discover_ctx;
408 int fd = -1;
409
410 MEM(discover_ctx = talloc_zero(c, ldap_directory_discover_ctx_t));
411 discover_ctx->c = c;
412
413 if (fr_ldap_search_async(&discover_ctx->msgid, NULL, c, "", LDAP_SCOPE_BASE, "(objectclass=*)",
414 attrs, NULL, NULL) != LDAP_PROC_SUCCESS) {
415 error:
416 talloc_free(discover_ctx);
417 return -1;
418 }
419
420 if ((ldap_get_option(c->handle, LDAP_OPT_DESC, &fd) != LDAP_OPT_SUCCESS) || (fd < 0)) goto error;
421
422 if (fr_event_fd_insert(discover_ctx, NULL, c->conn->el, fd,
424 NULL,
426 discover_ctx) < 0) goto error;
427
429
430 return 0;
431}
432
433/** Asynchronously extract useful information from the rootDSE of the LDAP server
434 *
435 * This version is for a single connection rather than a connection trunk
436 *
437 * @param[in] ldap_conn connection to be queried
438 * @return
439 * - message ID on success
440 * < 0 on failure
441 */
443{
444 int msgid;
445 static char const *attrs[] = LDAP_DIRECTORY_ATTRS;
446
447 ldap_conn->directory = fr_ldap_directory_alloc(ldap_conn);
448
449 if (fr_ldap_search_async(&msgid, NULL, ldap_conn, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs,
450 NULL, NULL) != LDAP_PROC_SUCCESS) return -1;
451
452 return msgid;
453}
static int context
Definition radmin.c:69
#define USES_APPLE_DEPRECATED_API
Definition build.h:499
#define RCSID(id)
Definition build.h:512
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:228
#define CMP(_a, _b)
Same as CMP_PREFER_SMALLER use when you don't really care about ordering, you just want an ordering.
Definition build.h:113
#define unlikely(_x)
Definition build.h:407
#define UNUSED
Definition build.h:336
#define NUM_ELEMENTS(_t)
Definition build.h:358
#define MEM(x)
Definition debug.h:36
Test enumeration values.
Definition dict_test.h:92
char const * fr_ldap_directory_common_base_find(fr_ldap_directory_t const *directory, char const *const *dn_list)
Find the naming context which contains a set of DNs.
Definition directory.c:296
int fr_ldap_conn_directory_alloc_async(fr_ldap_connection_t *ldap_conn)
Asynchronously extract useful information from the rootDSE of the LDAP server.
Definition directory.c:442
int msgid
Of the outstanding rootDSE search.
Definition directory.c:340
fr_ldap_connection_t * c
Connection the rootDSE search was sent on.
Definition directory.c:339
static void _ldap_directory_discover_io_read(UNUSED fr_event_list_t *el, UNUSED int fd, UNUSED int flags, void *uctx)
Parse a rootDSE response from a server.
Definition directory.c:371
fr_ldap_directory_t * fr_ldap_directory_alloc(TALLOC_CTX *ctx)
Allocate a directory structure with defaults.
Definition directory.c:272
static void _ldap_directory_discover_io_error(UNUSED fr_event_list_t *el, UNUSED int fd, UNUSED int flags, UNUSED int fd_errno, void *uctx)
Error reading from or writing to the file descriptor.
Definition directory.c:351
static int8_t _naming_context_cmp(void const *one, void const *two)
Compare two naming contexts, case insensitively.
Definition directory.c:63
static fr_table_num_sorted_t const fr_ldap_directory_type_table[]
Definition directory.c:35
int fr_ldap_directory_discover_async(fr_ldap_connection_t *c)
Send a rootDSE search on a connection being established.
Definition directory.c:404
int fr_ldap_directory_result_parse(fr_ldap_directory_t *directory, LDAP *handle, LDAPMessage *result, char const *name)
Definition directory.c:68
static size_t fr_ldap_directory_type_table_len
Definition directory.c:50
static uint32_t _naming_context_hash(void const *data)
Hash a naming context, case insensitively.
Definition directory.c:55
State of an in progress rootDSE search on a connection being established.
Definition directory.c:338
#define fr_event_fd_insert(...)
Definition event.h:247
void * fr_hash_table_find(fr_hash_table_t *ht, void const *data)
Find data in a hash table.
Definition hash.c:450
uint32_t fr_hash_case_string(char const *p)
Hash a C string, converting all chars to lowercase.
Definition hash.c:916
bool fr_hash_table_insert(fr_hash_table_t *ht, void const *data)
Insert data into a hash table.
Definition hash.c:489
#define fr_hash_table_alloc(_ctx, _hash_node, _cmp_node, _free_node)
Definition hash.h:61
talloc_free(hp)
#define LDAP_DIRECTORY_ATTRS
Definition base.h:830
#define LDAP_SERVER_NOTIFICATION_OID
OID of Active Directory control for persistent search.
Definition base.h:115
int fr_ldap_entry_value_find(struct berval *out, LDAP *handle, LDAPMessage *entry, char const *attr)
Find an attribute in an entry, returning its first value referenced in place.
Definition util.c:748
@ FR_LDAP_DIRECTORY_ORACLE_UNIFIED_DIRECTORY
Directory server is Oracle Unified Directory.
Definition base.h:149
@ FR_LDAP_DIRECTORY_UNKNOWN
We can't determine the directory server.
Definition base.h:141
@ FR_LDAP_DIRECTORY_NETSCAPE
Directory server is Netscape.
Definition base.h:146
@ FR_LDAP_DIRECTORY_EDIRECTORY
Directory server is eDir.
Definition base.h:144
@ FR_LDAP_DIRECTORY_ORACLE_INTERNET_DIRECTORY
Directory server is Oracle Internet Directory.
Definition base.h:148
@ FR_LDAP_DIRECTORY_UNBOUND_ID
Directory server is Unbound ID.
Definition base.h:153
@ FR_LDAP_DIRECTORY_SIEMENS_AG
Directory server is Siemens AG.
Definition base.h:152
@ FR_LDAP_DIRECTORY_ORACLE_VIRTUAL_DIRECTORY
Directory server is Oracle Virtual Directory.
Definition base.h:150
@ FR_LDAP_DIRECTORY_ACTIVE_DIRECTORY
Directory server is Active Directory.
Definition base.h:143
@ FR_LDAP_DIRECTORY_OPENLDAP
Directory server is OpenLDAP.
Definition base.h:147
@ FR_LDAP_DIRECTORY_SUN_ONE_DIRECTORY
Directory server is Sun One Directory.
Definition base.h:151
@ FR_LDAP_DIRECTORY_IBM
Directory server is IBM.
Definition base.h:145
@ FR_LDAP_DIRECTORY_SAMBA
Directory server is Samba.
Definition base.h:154
fr_ldap_sync_type_t sync_type
What kind of LDAP sync this directory supports.
Definition base.h:212
LDAP * handle
libldap handle.
Definition base.h:343
fr_ldap_directory_t * directory
The type of directory we're connected to.
Definition base.h:352
void fr_ldap_state_error(fr_ldap_connection_t *c)
Signal that there's been an error on the connection.
Definition state.c:153
char * fr_ldap_berval_to_string(TALLOC_CTX *ctx, struct berval const *in)
Convert a berval to a talloced string.
Definition util.c:778
fr_ldap_config_t const * config
rlm_ldap connection configuration.
Definition base.h:354
talloc_str_list_t * fr_ldap_str_list_afrom_result(TALLOC_CTX *ctx, LDAP *handle, LDAPMessage *result, char const *attr, size_t extra)
Copy an attribute's values from every entry of a result into a string list.
Definition util.c:696
fr_ldap_state_t fr_ldap_state_next(fr_ldap_connection_t *c)
Move between LDAP connection states.
Definition state.c:50
char const * vendor_str
As returned from the vendorName attribute in the rootDSE.
Definition base.h:203
fr_hash_table_t * naming_contexts_ht
For resolving DNs to the naming context containing them.
Definition base.h:220
bool cleartext_password
Whether the server will return the user's plaintext password.
Definition base.h:209
char const * name
Name of the module that created this connection.
Definition base.h:232
@ FR_LDAP_SYNC_ACTIVE_DIRECTORY
Directory supports AD style persistent search.
Definition base.h:160
@ FR_LDAP_SYNC_PERSISTENT_SEARCH
Directory supports persistent search.
Definition base.h:161
@ FR_LDAP_SYNC_RFC4533
Directory supports RFC 4533.
Definition base.h:159
bool discovered
A rootDSE response has been parsed into these fields.
Definition base.h:223
int fr_ldap_connection_timeout_reset(fr_ldap_connection_t const *conn)
Definition connection.c:437
char const * version_str
As returned from the vendorVersion attribute in the rootDSE.
Definition base.h:205
connection_t * conn
Connection state handle.
Definition base.h:355
char const * dn_attr
Attribute to match an entry's DN in a search filter.
Definition base.h:214
char const ** naming_contexts
NULL terminated array of databases served by this directory.
Definition base.h:218
fr_ldap_directory_type_t type
Canonical server implementation.
Definition base.h:207
fr_ldap_rcode_t
Codes returned by fr_ldap internal functions.
Definition base.h:585
@ LDAP_PROC_SUCCESS
Operation was successful.
Definition base.h:588
Tracks the state of a libldap connection handle.
Definition base.h:342
fr_ldap_rcode_t fr_ldap_search_async(int *msgid, request_t *request, fr_ldap_connection_t *pconn, char const *dn, int scope, char const *filter, char const *const *attrs, LDAPControl **serverctrls, LDAPControl **clientctrls)
Search for something in the LDAP directory.
Definition base.c:529
fr_ldap_rcode_t fr_ldap_result(LDAPMessage **result, LDAPControl ***ctrls, fr_ldap_connection_t const *conn, int msgid, int all, char const *dn, fr_time_delta_t timeout)
Parse response from LDAP server dealing with any errors.
Definition base.c:450
#define PWARN(_fmt,...)
Definition log.h:227
Stores all information relating to an event list.
Definition event.c:377
unsigned int uint32_t
int strcasecmp(char *s1, char *s2)
Definition missing.c:65
#define WARN(fmt,...)
#define INFO(fmt,...)
Definition radict.c:63
static char const * name
#define fr_table_str_by_value(_table, _number, _def)
Convert an integer to a string.
Definition table.h:804
An element in a lexicographically sorted array of name to num mappings.
Definition table.h:49
static size_t talloc_str_list_num(talloc_str_list_t const *list)
Return the number of strings in a string list.
Definition talloc.h:268
A NULL terminated array of strings with an append cursor.
Definition talloc.h:253
#define fr_time_delta_wrap(_time)
Definition time.h:152
static fr_event_list_t * el
char const * fr_strerror(void)
Get the last library error.
Definition strerror.c:558
static fr_slen_t data
Definition value.h:1340