/* this file contains information for navigating the various LDAP schema */ #include "ldapdns.h" #include "config.h" #include "dns.h" #include "ip.h" #include void inline ldapdns_list_unique(list_t *p) { list_t x = 0; list_t seen = 0; list_t lp, sp; for (lp = (*p); lp; lp = lp->next) { for (sp = seen; sp; sp = sp->next) { if (str_equal(sp->str, lp->str)) { goto HIT; } } list_push(&x, lp->str); list_push(&seen, lp->str); HIT: continue; } lp = *p; while (list_pop(&lp)); while (list_pop(&seen)); *p = x; } static void inline ldap_escape(str_t retbuf, char *s) { while (*s) { if (*s == ',') { str_addch(retbuf, '\\'); } str_addch(retbuf, *s); s++; } } void name_to_ldap(str_t retbuf, char *name) { list_t p = 0; char *x; int i; str_init(retbuf); switch (ldapdns.dn_mode) { case DN_MODE_COSINE: case DN_MODE_LDAPDNS: /* dc=www, dc=example, dc=com */ case DN_MODE_RFC1279: /* dc=www, dc=example, dc=com */ p = split_name_parts(name); i = 0; while ((x = list_pop(&p))) { str_cat(retbuf, "dc="); ldap_escape(retbuf, x); str_cat(retbuf, ", "); i++; } if (i) str_chopn(retbuf, 2); break; case DN_MODE_MSDNS: /* dc=www.example.com */ str_cat(retbuf, "dc="); ldap_escape(retbuf, x); break; }; } list_t ldap_into_parts(char *dn) { /* safe if dn is infact an asciiZ string */ list_t p = 0; register char *x = dn, *y; str_t s; /* split into parts */ str_init(s); while (*x == ' ' || *x == ',') x++; /* mostly safe :) */ while (*x) { /* looking for: \s*,\s* but !~ \\. */ if (*x == '\\') { x++; str_addch(s, *x); x++; } else if (*x == ',') { /* pull off excess whitespace from previous */ y = strchr(str(s), '\0'); if (y) { y--; while (*y == ' ') y--; if (*y) y++; if (*y) *y = 0; } list_push(&p, str(s)); str_init(s); while (*x == ',' || *x == ' ') x++; } else { str_addch(s, *x); x++; } } return p; } void ldap_to_name(str_t retbuf, char *dn) { /* safe if dn is asciiz */ list_t z, b, p = ldap_into_parts(dn); char *x, *y, *q; /* we need the suffix here in order to pull it off */ if (ldapdns.ldap_suffix && *ldapdns.ldap_suffix) { b = ldap_into_parts(ldapdns.ldap_suffix); for (z = b; z; z = z->next) { if (str_diffi(z->str, p->str)) { break; } else { q = list_pop(&p); mem_free(q); } } while ((x = list_pop(&b))) { if (str_diffi(x, p->str)) { mem_free(x); while ((x = list_pop(&b))) mem_free(x); break; } q = list_pop(&p); mem_free(q); mem_free(x); } } /* flip p around; we have to have this in it's own list */ list_reverse(&p); str_init(retbuf); while ((x = list_pop(&p))) { if (str_start(x, "dc=")) { y = x + 3; while (*y == ' ') y++; if (*y != ',') { str_cat(retbuf, y); str_addch(retbuf, '.'); } } mem_free(x); } str_chop(retbuf); } static int inline __parse_soa(dns_ctx *c, bin_t rrdata, int flag) { register unsigned int i, j; unsigned long ar[5]; register char *q; str_t tmp, tmp2; q = caddr(rrdata); if (flag) { if (clen(rrdata) >= 1 && *q == '*') { c->wantdie=1; return 0; } /* don't want anything else */ if (clen(rrdata) == 1) return 0; } if (c->soahack) return 1; /* skip */ /* remainder is of the format is serial [ttl retry expire minimum] */ for (j = i = 0; j < 5; j++) { /* safe: bounded */ if (!(i < clen(rrdata))) break; while (isspace(((unsigned int)q[i])) && i < clen(rrdata)) i++; if (!(i < clen(rrdata))) break; ar[j] = 0; while (isdigit(((unsigned int)q[i])) && i < clen(rrdata)) { ar[j] *= 10; switch (q[i]) { case '0': ar[j] += 0; break; case '1': ar[j] += 1; break; case '2': ar[j] += 2; break; case '3': ar[j] += 3; break; case '4': ar[j] += 4; break; case '5': ar[j] += 5; break; case '6': ar[j] += 6; break; case '7': ar[j] += 7; break; case '8': ar[j] += 8; break; case '9': ar[j] += 9; break; }; i++; } } if (j < 4 || ar[4] == 0) { /* if j == 1 then... */ switch (j) { case 2: c->refresh = ar[1]; case 1: if (ar[0]) c->serial = ar[0]; }; return 0; } /* these are kept in host-endian form (for now) */ if (ar[0]) c->serial = ar[0]; c->refresh = ar[1]; c->retry = ar[2]; c->expire = ar[3]; c->minimum = ar[4]; c->ttl = c->minimum; if (i < clen(rrdata)) { /* grap ADM as well */ str_init(tmp); str_catb(tmp, caddr(rrdata)+i, clen(rrdata)-i); name_to_dns_fix(tmp2, str(tmp), 2); /* email address? */ list_push(&c->ADM, str(tmp2)); mem_free(str(tmp)); } c->soahack = 1; return 1; } static char inline *__parse_name(dns_ctx *c, bin_t rrdata) { str_t tmp; bin_t retval; int addzone; /* name is in ascii form.... convert to DNS */ bin_init(retval); bin_copy(retval, caddr(rrdata), clen(rrdata)); addzone = 0; if (ldapdns.relative_names) { /* if . is the last character... */ if (clen(retval) && caddr(retval)[clen(retval)-1] != '.') { addzone = 1; } } bin_0(retval); name_to_dns(tmp, (char *)caddr(retval)); if (addzone && c->request_name_zone) { /* add the zone to the end */ str_cat(tmp, c->request_name_zone); } bin_copy(retval, str(tmp), dns_domain_length(str(tmp))); return (char *)caddr(retval); } static char inline *__parse_mx(dns_ctx *c, bin_t rrdata) { str_t tmp; bin_t retval; unsigned short pref; char *q; register int b, len; int addzone; /* name is in ascii form.... convert to DNS */ bin_init(retval); q = (char *)caddr(rrdata); len = clen(rrdata); pref = 0; while (len > 0 && *q) { switch (*q) { case '0': b = 0; break; case '1': b = 1; break; case '2': b = 2; break; case '3': b = 3; break; case '4': b = 4; break; case '5': b = 5; break; case '6': b = 6; break; case '7': b = 7; break; case '8': b = 8; break; case '9': b = 9; break; default: b = -1; break; }; if (b == -1) break; pref *= 10; pref += b; q++; len--; } while (len > 0 && *q == ' ') { q++; len--; } bin_copy(retval, q, len); addzone = 0; if (ldapdns.relative_names) { /* if . is the last character... */ if (caddr(retval)[clen(retval)-1] != '.') { addzone = 1; } } bin_0(retval); name_to_dns(tmp, (char *)caddr(retval)); if (addzone && c->request_name_zone) { /* add the zone to the end */ str_cat(tmp, c->request_name_zone); } /* Clib */ pref = htons(pref); bin_copy(retval, (char *)&pref, 2); bin_cat(retval, str(tmp), dns_domain_length(str(tmp))); mem_free(str(tmp)); return (char *)caddr(retval); } static char inline *__parse_ipv4(dns_ctx *c, bin_t rrdata) { /* rrdata is in the form of A.B.C.D; so length bind it... */ unsigned char ip[8]; register int i, j, a, b, r; bin_t retval; int cflag; bin_init(retval); ip[4] = ip[5] = ip[6] = ip[7] = 0; for (i = j = a = cflag = 0; (cflag || j < 4) && i < clen(rrdata); i++) { switch (rrdata->buf[i]) { case '0': b = 0; break; case '1': b = 1; break; case '2': b = 2; break; case '3': b = 3; break; case '4': b = 4; break; case '5': b = 5; break; case '6': b = 6; break; case '7': b = 7; break; case '8': b = 8; break; case '9': b = 9; break; case '.': ip[j] = a; a = 0; j++; continue; case '/': /* i think we're making a subnet-switch */ if (!c) /* fail early */ return 0; ip[j] = a; j = 4; a = 0; cflag = 1; continue; case '=': if (!c) /* fail early */ return 0; ip[j] = a; if (j == 0) { ip[4] = 0xff; ip[5] = ip[6] = ip[7] = 0; } else if (j == 1) { ip[4] = ip[5] = 0xff; ip[6] = ip[7] = 0; } else if (j == 2) { ip[4] = ip[5] = ip[6] = 0xff; ip[7] = 0; } else if (j == 3) { ip[4] = ip[5] = ip[6] = ip[7] = 0xff; } else if (j == 4) { if (ip[4] == 0) { /* exact match */ ip[4] = ip[5] = ip[6] = ip[7] = 255; } else if (ip[4] == 255) { /* 255.0.0.0 */ ip[4] = 255; ip[5] = ip[6] = ip[7] = 0; } else { /* okay /CIDR notation */ r = ip[4]; ip[4] = ip[5] = ip[6] = ip[7] = 255; if (r < 8) { ip[4] = (255 << (8 - r)); ip[5] = ip[6] = ip[7] = 0; } else if (r < 16) { ip[5] = (255 << (16 - r)); ip[6] = ip[7] = 0; } else if (r < 24) { ip[6] = (255 << (24 - r)); ip[7] = 0; } else { ip[7] = (255 << (32 - r)); } } } if (ipv4_in_subnet(ip, c->ip)) { /* reset and start over */ j = b = a = 0; cflag = 0; /* note, we also blop * 'c' so we can't do this again */ c = (dns_ctx *)0; continue; } return 0; /* don't return anything */ case '%': if (c && j == 3) { if (!c->swm) return 0; /* next chars are "switch" */ b = 0; i++; while (i < clen(rrdata)) { /* make sure it matches switch */ if (c->swm[b] != rrdata->buf[i]) return 0; b++; i++; } goto done_parsing_l; } /* fall through */ default: /* invalid */ warning("invalid IPV4 address (%c, %d)", rrdata->buf[i], i); return 0; }; /* digit */ a *= 10; a += b; } done_parsing_l: ip[j] = a; bin_copy(retval, ip, 4); return (char *)caddr(retval); } static char inline *__parse_email(bin_t rrdata) { str_t tmp; bin_t retval; /* name is in ascii form.... convert to DNS */ bin_init(retval); bin_copy(retval, caddr(rrdata), clen(rrdata)); bin_0(retval); name_to_dns_fix(tmp, (char *)caddr(retval), 2); bin_copy(retval, str(tmp), dns_domain_length(str(tmp))); return (char *)caddr(retval); } static char inline *__parse_txt(bin_t rrdata) { str_t tmp; bin_t retval; /* don't dns-encode the TXT segment */ bin_init(retval); bin_addch(retval, clen(rrdata)); bin_cat(retval, caddr(rrdata), clen(rrdata)); bin_0(retval); return (char *)caddr(retval); } static char inline *__parse_generic(bin_t rrdata) { /* return MUST contain length: * order: rr len data.... */ bin_t retval; unsigned short n; bin_init(retval); bin_copy(retval, caddr(rrdata), 2); /* n is in host-byte order */ n = clen(rrdata)-2; bin_copy(retval, (char *)&n, 2); bin_copy(retval, caddr(rrdata)+2, n); return (char *)caddr(retval); } /* bind-style */ static int inline ldap_dnsrecord_rfc1279(dns_ctx *c, bin_t rrdata) { register char *q; register int len; char rr[2]; bin_t tmp; int r; /* IN rr [str] */ if (clen(rrdata) < 4) return 0; if (!(tolower((unsigned int)(caddr(rrdata)[0])) == 'i' && tolower((unsigned int)(caddr(rrdata)[1])) == 'n')) { return 0; } /* step over whitespace[s] */ q = caddr(rrdata) + 2; len = clen(rrdata) - 2; while (len > 0 && (*q == ' ' || *q == '\t')) { q++; len--; } if (len < 2) return 0; /* parse word */ #define _setrr(x) do { rr[0] = x[0]; rr[1] = x[1]; } while (0) q[0] = toupper(((unsigned int)q[0])); /* modifying rrdata! */ if (q[0] == 'A') { /* modifying rrdata */ if (toupper(((unsigned int)q[1])) == 'A') { /* AAAA: not supported (yet) */ return 0; } /* A: address record */ _setrr(DNS_T_A); } else if (q[0] == 'C') { /* CNAME: alias */ _setrr(DNS_T_CNAME); } else if (q[0] == 'T') { /* TXT: text record */ _setrr(DNS_T_TXT); } else if (q[0] == 'P') { /* PTR: pointer-name */ _setrr(DNS_T_PTR); } else if (q[0] == 'M') { /* MX: mail exchanger */ _setrr(DNS_T_MX); } else if (q[0] == 'N') { /* NS: name server */ _setrr(DNS_T_NS); } else if (q[0] == 'S') { /* SOA: start of authority */ _setrr(DNS_T_SOA); } #undef _setrr /* parse RR -- pass everything but SOA to their ldapdns parser */ while (len > 0 && (*q != ' ' && *q != '\t')) { q++; len--; } if (len < 2) return 0; while (len > 0 && (*q == ' ' || *q == '\t')) { q++; len--; } if (len < 2) return 0; #define _eq4(x) (rr[0] == x[0] && rr[1] == x[1]) if (_eq4(DNS_T_SOA)) { /* start of authority */ if (*q == '(') { q++; len--; if (len < 2) return 0; while (len > 0 && (*q == ' ' || *q == '\t')) { q++; len--; } if (len < 2) return 0; } for (r = 0; r < len; r++) { if (q[r] == ')') { r--; while ((q[r] == ' ' || q[r] == '\t') && r > 0) r--; len = r; break; } } /* fall through */ } bin_init(tmp); bin_copy(tmp, q, len); r = 1; if (_eq4(DNS_T_A)) { /* disables client differentiation */ list_push(&c->A, __parse_ipv4((dns_ctx *)0, tmp)); } else if (_eq4(DNS_T_PTR)) { list_push(&c->PTR, __parse_name(c, tmp)); } else if (!c->subreq) { if (_eq4(DNS_T_MX)) { list_push(&c->MX, __parse_mx(c, tmp)); } else if (_eq4(DNS_T_CNAME)) { list_push(&c->CNAME, __parse_name(c, tmp)); } else if (_eq4(DNS_T_NS)) { list_push(&c->NS, __parse_name(c, tmp)); } else if (_eq4(DNS_T_TXT)) { list_push(&c->TXT, __parse_txt(tmp)); } else if (_eq4(DNS_T_SOA)) { r = __parse_soa(c, tmp, 0); } else r = 0; } else r = 0; #undef _eq4 mem_free(caddr(tmp)); return r; } /* microsoft-style */ static int inline ldap_dnsrecord_msdns(dns_ctx *c, bin_t rrdata) { register char *q; bin_t retval; if (clen(rrdata) < 24) return 0; /* data is in a binary-packed format which i'm not 100% on: * each two-character blob is a single octet. some things are * in network byte-order, other things are NOT. this can be * confusing quick... * * note that places where I've marked the field as 'xx' means i don't * know what it is. 0x means that it's always 00 (in my tests) * but that i still don't know what it is... * * HEADER: * * xx xx [0-1] * RR RR 16-bit: resource record [2-3] * xx xx 0x 0x xx xx 0x 0x [4-11] * TT TT TT TT 32-bit: time to live [12-15] * 0x 0x 0x 0x xx xx xx xx [16-23] * -- last dword has significance for A-records * * what follows is RR-data [24]: * * SOA-RR: * SS SS SS SS serial * AA AA AA AA refresh * BB BB BB BB retry * CC CC CC CC expire * DD DD DD DD minimum * * (44) ll el d.d.d length (ll), elements (el), dns-encoded NS1 * ll el d.d.d length (ll), elements (el), hostmaster * * NS-RR * ll el d.d.d length (ll), elements (el), dns-encoded name * * A-RR * a. b. c. d. ip address * * MX-RR * pp pp preference * ll el d.d.d length (ll), elements (el), mail server * * CNAME-RR * (same as ns) * PTR-RR * (same as ns) * TXT-RR * (same as ns; BUT there's ALWAYS only 1 segment) * */ #define _eq3(a) (a[0] == (caddr(rrdata)[2]) && a[1] == (caddr(rrdata)[3])) bin_init(retval); if (_eq3(DNS_T_SOA)) { /* Clib */ c->refresh = ntohl(*((unsigned long *)(caddr(rrdata)+12))); c->serial = ntohl(*((unsigned long *)(caddr(rrdata)+24))); c->retry = ntohl(*((unsigned long *)(caddr(rrdata)+32))); c->expire = ntohl(*((unsigned long *)(caddr(rrdata)+36))); c->minimum = ntohl(*((unsigned long *)(caddr(rrdata)+40))); c->ttl = c->minimum; q = caddr(rrdata) + 44; q++; q++; q += dns_domain_length(q)+1; q++; q++; bin_copy(retval, q, dns_domain_length(q)); list_push(&c->ADM, caddr(retval)); } else if (_eq3(DNS_T_NS)) { q = caddr(rrdata) + 26; bin_copy(retval, q, dns_domain_length(q)); list_push(&c->NS, caddr(retval)); } else if (_eq3(DNS_T_A)) { q = caddr(rrdata) + 24; bin_copy(retval, q, 4); list_push(&c->MX, caddr(retval)); } else if (_eq3(DNS_T_MX)) { q = caddr(rrdata) + 24; bin_copy(retval, q, 2); q += 4; /* pref and ll,el */ bin_cat(retval, q, dns_domain_length(q)); list_push(&c->MX, caddr(retval)); } else if (_eq3(DNS_T_CNAME)) { q = caddr(rrdata) + 26; bin_copy(retval, q, dns_domain_length(q)); list_push(&c->CNAME, caddr(retval)); } else if (_eq3(DNS_T_PTR)) { q = caddr(rrdata) + 26; bin_copy(retval, q, dns_domain_length(q)); list_push(&c->PTR, caddr(retval)); } else if (_eq3(DNS_T_TXT)) { q = caddr(rrdata) + 26; bin_copy(retval, q, dns_domain_length(q)); list_push(&c->TXT, caddr(retval)); } else { /* didn't really need it */ mem_free(caddr(retval)); return 0; } return 1; } /* me-style */ static int inline ldap_arecord(dns_ctx *c, bin_t rrdata) { list_push(&c->A, __parse_ipv4(c, rrdata)); return 1; } static int inline ldap_serial(dns_ctx *c, bin_t rrdata) { register int i, ylen; unsigned int Y,M,D, h,m,s; struct tm tp; time_t trymk; if (c->soahack) return 1; /* skip */ /* this is NOT a linear number: * it is a timestamp... and should be treated as such */ if (clen(rrdata) < 15) return 0; ylen = clen(rrdata) - 11; for (i = Y = 0; i < ylen; i++) { Y *= 10; Y += str_chtoi(caddr(rrdata)[i]); } M = str_chtoi(caddr(rrdata)[i]) * 10; i++; M += str_chtoi(caddr(rrdata)[i]); i++; D = str_chtoi(caddr(rrdata)[i]) * 10; i++; D += str_chtoi(caddr(rrdata)[i]); i++; h = str_chtoi(caddr(rrdata)[i]) * 10; i++; h += str_chtoi(caddr(rrdata)[i]); i++; m = str_chtoi(caddr(rrdata)[i]) * 10; i++; m += str_chtoi(caddr(rrdata)[i]); i++; s = str_chtoi(caddr(rrdata)[i]) * 10; i++; s += str_chtoi(caddr(rrdata)[i]); i++; tp.tm_sec = s; tp.tm_min = m; tp.tm_hour = h; tp.tm_mday = D; tp.tm_mon = M-1; tp.tm_year = Y-1900; tp.tm_wday = tp.tm_yday = 0; if (caddr(rrdata)[i] == 'z') { tp.tm_isdst = 0; c->serial = mktime(&tp); } else { tp.tm_isdst = 1; trymk = mktime(&tp); if (trymk == -1 || trymk == 11 || trymk == 1) { /* * it would not happen if the ldapserver was always * using GMT.... */ tp.tm_isdst = 0; trymk = mktime(&tp); } c->serial = trymk; } return 1; } static int inline ldap_mxrecord(dns_ctx *c, bin_t rrdata) { if (!c->subreq) { list_push(&c->MX, __parse_mx(c, rrdata)); } return 1; } static int inline ldap_mail(dns_ctx *c, bin_t rrdata) { if (!c->subreq) { list_push(&c->ADM, __parse_email(rrdata)); } return 1; } static int inline ldap_cnamerecord(dns_ctx *c, bin_t rrdata) { static char *inaddr_str = "\007in-addr\004arpa"; /* check to see if we're in in_addr space */ register char *q; if (c->request_name) { q = c->request_name + dns_domain_length(c->request_name); q -= 14; /* in-addr.arpa. */ if (str_equal(q, inaddr_str)) { /* yeehaw */ list_push(&c->PTR, __parse_name(c, rrdata)); return 1; } } if (!c->subreq) { /* ignore CNAME on subrequest */ list_push(&c->CNAME, __parse_name(c, rrdata)); } return 1; } static int inline ldap_description(dns_ctx *c, bin_t rrdata) { if (!c->subreq) { list_push(&c->TXT, __parse_txt(rrdata)); } return 1; } static int inline ldap_photo(dns_ctx *c, bin_t rrdata) { list_push(&c->Generic, __parse_generic(rrdata)); return 1; } static int inline ldap_nsrecord(dns_ctx *c, bin_t rrdata) { if (!c->subreq) { list_push(&c->NS, __parse_name(c, rrdata)); } else { c->subreq_valid++; } return 1; } static int inline ldap_seealso(dns_ctx *c, bin_t rrdata) { list_push(&c->PTR, __parse_name(c, rrdata)); return 1; } static int inline ldap_soarecord(dns_ctx *c, bin_t rrdata) { if (!c->subreq) { return __parse_soa(c, rrdata, 1); } return 0; } static void inline handle_ldap_rrdata(dns_ctx *c, char *attr, bin_t rrdata) { switch (attr[0]) { case 'a': /* aRecord */ ldap_arecord(c, rrdata); break; case 'c': /* cNAMERecord */ ldap_cnamerecord(c, rrdata); break; case 'd': if (attr[1] == 'n') { if (attr[2] == 's') { /* dnsRecord */ switch (ldapdns.dn_mode) { case DN_MODE_RFC1279: ldap_dnsrecord_rfc1279(c, rrdata); break; case DN_MODE_MSDNS: ldap_dnsrecord_msdns(c, rrdata); break; }; } } else { /* description */ ldap_description(c, rrdata); } break; case 'm': if (attr[1] == 'o') { break; } else if (attr[1] == 'x') { /* mXRecord */ ldap_mxrecord(c, rrdata); } else { /* mail */ ldap_mail(c, rrdata); } break; case 'n': /* nSRecord */ ldap_nsrecord(c, rrdata); break; case 'p': /* generic record (photo) */ ldap_photo(c, rrdata); break; case 's': /* sOARecord */ if (attr[1] == 'e') { /* seeAlso */ ldap_seealso(c, rrdata); } else { /* sOARecord */ ldap_soarecord(c, rrdata); } break; }; } int ldap_load_dns_attributes(dns_ctx *c, char **dn, int zonef) { /* should be safe; barring any strangeness from ldap client lib */ BerElement *ber; char *attr, *val; struct berval **bvals; bin_t rrdata; int i, len; LDAPMessage *m; m = ldap_first_entry(c->c->ldap_con, c->message); if (!m) { return 0; /* out of entries */ } if (dn) { *dn = ldap_get_dn(c->c->ldap_con, m); } bin_init(rrdata); attr = ldap_first_attribute(c->c->ldap_con, m, &ber); while (attr) { if (!m) break; /* weird */ bvals = ldap_get_values_len(c->c->ldap_con, m, attr); if (!bvals) /* server problem could halt here */ break; str_lc(attr); for (i = 0; bvals[i]; i++) { len = bvals[i]->bv_len; if (len < 1) continue; val = bvals[i]->bv_val; if (!val) /* should never happen */ continue; if (attr[0] == 'a' && attr[1] == 's') { /* associated Domain */ c->adlen = bvals[i]->bv_len; continue; } /* convert to bin */ bin_copy(rrdata, val, bvals[i]->bv_len); if (attr[0] == 'm' && attr[1] == 'o') { /* modify timestamp */ if (zonef) ldap_serial(c, rrdata); continue; } handle_ldap_rrdata(c, attr, rrdata); } ldap_value_free_len(bvals); ldap_memfree(attr); attr = ldap_next_attribute(c->c->ldap_con, m, ber); } ber_free(ber, 0); mem_free(caddr(rrdata)); while (m) m = ldap_next_entry(c->c->ldap_con, m); return 1; }