add unit tests for scan_ldap*

add return 0 to unit tests for scan_asn1*
This commit is contained in:
leitner
2023-09-16 09:19:19 +00:00
parent b75c256e32
commit 41df4dbaeb
22 changed files with 305 additions and 31 deletions

View File

@@ -59,5 +59,6 @@ int main() {
strcpy(buf,"\x03\x02\x07\x81"); // 0x03 = UNIVERSAL PRIMITIVE BIT_STRING, 0x02 = length 2, 0x07 = unused bits in last octet, 0x81 = invalid
assert(scan_asn1BITSTRING(buf,buf+4,&s,&l)==0); // unused bits not 0, return 0 in line 21
// we only care for 100% coverage of this file, the others have their own unit tests */
return 0;
}
#endif

View File

@@ -40,5 +40,6 @@ int main() {
buf[0]=0x30; buf[2]=1;
assert(scan_asn1BOOLEAN(buf,buf+3,&l)==0); // 0x30 = SEQUENCE_OF, fails line 10
// we only care for 100% coverage of this file, the others have their own unit tests */
return 0;
}
#endif

View File

@@ -34,5 +34,6 @@ int main() {
buf[0]=0x30;
assert(scan_asn1ENUMERATED(buf,buf+3,&l)==0); // 0x30 = SEQUENCE_OF, fails line 10
// we only care for 100% coverage of this file, the others have their own unit tests */
return 0;
}
#endif

View File

@@ -49,5 +49,6 @@ int main() {
strcpy(buf,"\x02\x08\xff\xff\xff\xff\xff\xff\xff\xff");
assert(scan_asn1INTEGER(buf,buf+10,&l)==0); // non-minimal encoding of -1
}
return 0;
}
#endif

View File

@@ -35,5 +35,6 @@ int main() {
buf[0]=0x31;
assert(scan_asn1SEQUENCE(buf,buf+3,&l)==0); // 0x31 = SET_OF, third return 0
// we only care for 100% coverage of this file, the others have their own unit tests */
return 0;
}
#endif

View File

@@ -13,4 +13,28 @@ size_t scan_asn1SEQUENCE_nolengthcheck(const char* src,const char* max,size_t* l
return 0;
}
#ifdef UNITTEST
#include <string.h>
#include <assert.h>
#undef UNITTEST
#include "scan_asn1tag.c"
#include "scan_asn1tagint.c"
#include "scan_asn1length.c"
int main() {
char buf[100];
size_t l;
strcpy(buf,"\x30\x01\x01"); // 0x30 = UNIVERSAL + CONSTRUCTED + SEQUENCE_OF, 0x01 = length 1, 0x01 = dummy filler
// this function only parses the header so our test data doesn't need
// to have an actual sequence, only the header for one. \x01 is not a
// valid sequence.
assert(scan_asn1SEQUENCE_nolengthcheck(buf,buf+3,&l)==2 && l==1);
assert(scan_asn1SEQUENCE_nolengthcheck(buf,buf,&l)==0); // not enough input, first return 0
assert(scan_asn1SEQUENCE_nolengthcheck(buf,buf+2,&l)==2 && l==1); // not enough input, but we used the _nolengthcheck version so expect success
buf[0]=0x31;
assert(scan_asn1SEQUENCE_nolengthcheck(buf,buf+3,&l)==0); // 0x31 = SET_OF, third return 0
// we only care for 100% coverage of this file, the others have their own unit tests */
return 0;
}
#endif

View File

@@ -37,5 +37,6 @@ int main() {
buf[0]=0x30;
assert(scan_asn1SET(buf,buf+3,&l)==0); // 0x30 = SEQUENCE_OF, third return 0
// we only care for 100% coverage of this file, the others have their own unit tests */
return 0;
}
#endif

View File

@@ -31,5 +31,6 @@ int main() {
buf[0]=0x13; // 0x13 = UNIVERSAL PRIMITIVE PrintableString
assert(scan_asn1STRING(buf,buf+7,&s,&l)==0); // scan_asn1string succeeds but line 9 fails
// we only care for 100% coverage of this file, the others have their own unit tests */
return 0;
}
#endif

View File

@@ -36,5 +36,6 @@ int main() {
assert(scan_asn1int(buf,buf+3,&tc,&tt,&tag,&l)==0); // not enough input, second return 0
assert(scan_asn1int(buf,buf+4,&tc,&tt,&tag,&l)==0); // non-minimally encoded raw int, third return
// we only care for 100% coverage of this file, the others have their own unit tests */
return 0;
}
#endif

View File

@@ -114,5 +114,6 @@ int main() {
assert(scan_asn1length(buf,buf+10,&l)==0); // length fits, value doesn't
assert(scan_asn1length(buf,buf+0x8010,&l)==3 && l==0x8000); // OK
assert(scan_asn1length(buf,buf+2,&l)==0); // length doesn't fit
return 0;
}
#endif

View File

@@ -52,5 +52,6 @@ int main() {
buf[1]=3;
assert(scan_asn1oid(buf,buf+6,retval,&retvals)==0); // trigger line 21
// we only care for 100% coverage of this file, the others have their own unit tests */
return 0;
}
#endif

View File

@@ -93,5 +93,6 @@ int main() {
assert(scan_asn1rawint(buf, buf+sizeof(long)+1, sizeof(long)+1, &l) == 0);
memcpy(buf,"\x00\xff\xff\xff\xff\xff\xff\xff\xff",9);
assert(scan_asn1rawint(buf, buf+sizeof(long)+1, sizeof(long)+1, &l) == 0);
return 0;
}
#endif

View File

@@ -70,5 +70,6 @@ int main() {
retvals=10;
assert(scan_asn1rawoid(buf,buf+3,retval,&retvals)==3 && retvals==4 && retval[0]==2 && retval[1]==100 && retval[2]==4 && retval[3]==3);
// we only care for 100% coverage of this file, the others have their own unit tests */
return 0;
}
#endif

View File

@@ -34,5 +34,6 @@ int main() {
assert(scan_asn1string(buf,buf,&tc,&tt,&tag,&s,&l)==0); // not enough input, first return 0
assert(scan_asn1string(buf,buf+6,&tc,&tt,&tag,&s,&l)==0); // not enough input, second return 0
// we only care for 100% coverage of this file, the others have their own unit tests */
return 0;
}
#endif

View File

@@ -86,5 +86,6 @@ int main() {
assert(scan_asn1tag(buf,buf+11,&tc,&tt,&tag)==0);
memcpy(buf,"\x1f\x8f\xff\xff\xff\x7f",7);
assert(scan_asn1tag(buf,buf+7,&tc,&tt,&tag)==6 && tc==UNIVERSAL && tt==PRIMITIVE && tag==0xffffffff);
return 0;
}
#endif

View File

@@ -77,5 +77,6 @@ int main() {
memcpy(buf,"\x90\x00\x00\x00\x00",9);
assert(scan_asn1tagint(buf,buf+10,&l)==0); // too large
}
return 0;
}
#endif

View File

@@ -2,8 +2,10 @@
size_t scan_ldapava(const char* src,const char* max,struct AttributeValueAssertion* ava) {
size_t res,tmp;
if (!(res=scan_ldapstring(src,max,&ava->desc))) goto error;
if (!(tmp=scan_ldapstring(src+res,max,&ava->value))) goto error;
if (!(res=scan_ldapstring(src,max,&ava->desc)))
goto error;
if (!(tmp=scan_ldapstring(src+res,max,&ava->value)))
goto error;
return res+tmp;
error:
return 0;

View File

@@ -4,14 +4,48 @@ size_t scan_ldapbindrequest(const char* src,const char* max,
unsigned long* version,struct string* name,
unsigned long* method) {
size_t res,tmp;
if (!(res=scan_asn1INTEGER(src,max,(signed long*)version))) return 0;
if (!(tmp=scan_ldapstring(src+res,max,name))) return 0;
if (!(res=scan_asn1INTEGER(src,max,(signed long*)version)))
return 0;
if (!(tmp=scan_ldapstring(src+res,max,name)))
return 0;
res+=tmp;
{
enum asn1_tagclass tc;
enum asn1_tagtype tt;
if (!(tmp=scan_asn1tag(src+res,max,&tc,&tt,method))) return 0;
if (tc!=PRIVATE || tt!=PRIMITIVE) return 0;
if (!(tmp=scan_asn1tag(src+res,max,&tc,&tt,method)))
return 0;
if (tc!=PRIVATE || tt!=PRIMITIVE)
return 0;
res+=tmp;
}
return res;
}
#ifdef UNITTEST
#undef UNITTEST
#include <assert.h>
#include "scan_asn1STRING.c"
#include "scan_asn1tag.c"
#include "scan_asn1tagint.c"
#include "scan_asn1length.c"
#include "scan_asn1string.c"
#include "scan_ldapstring.c"
#include "scan_asn1rawint.c"
#include "scan_asn1int.c"
#include "scan_asn1INTEGER.c"
int main() {
static char buf[] = "\x02\x01\x03\x04\x00\x80"; // bind request without message header
unsigned long version=0, method=0;
struct string name = { 0 };
assert(scan_ldapbindrequest(buf, buf+6, &version, &name, &method)==6 && version==3 && name.l==0 && method==0);
assert(scan_ldapbindrequest(buf, buf+5, &version, &name, &method)==0); // too short
buf[5]=0; assert(scan_ldapbindrequest(buf, buf+6, &version, &name, &method)==0); // tc!=PRIVATE
buf[5]=0xa0; assert(scan_ldapbindrequest(buf, buf+6, &version, &name, &method)==0); // tt!=PRIMITIVE
buf[5]=0x1f; assert(scan_ldapbindrequest(buf, buf+6, &version, &name, &method)==0); // fail scan_asn1tag
buf[4]=5; assert(scan_ldapbindrequest(buf, buf+6, &version, &name, &method)==0); // fail scan_ldapstring
buf[0]=0; assert(scan_ldapbindrequest(buf, buf+6, &version, &name, &method)==0); // fail scan_asn1INTEGER
// we only care for 100% coverage of this file, the others have their own unit tests */
return 0;
}
#endif

View File

@@ -3,19 +3,55 @@
size_t scan_ldapmessage(const char* src,const char* max,
unsigned long* messageid,unsigned long* op,size_t* len) {
size_t res,tmp;
if (!(res=scan_asn1SEQUENCE(src,max,len))) goto error;
if (!(tmp=scan_asn1INTEGER(src+res,max,(long*)messageid))) goto error;
if (!(res=scan_asn1SEQUENCE(src,max,len)))
goto error;
if (!(tmp=scan_asn1INTEGER(src+res,max,(long*)messageid)))
goto error;
res+=tmp;
{
enum asn1_tagclass tc;
enum asn1_tagtype tt;
if (!(tmp=scan_asn1tag(src+res,max,&tc,&tt,op))) goto error;
if (tc!=APPLICATION) goto error;
if (!(tmp=scan_asn1tag(src+res,max,&tc,&tt,op)))
goto error;
if (tc!=APPLICATION)
goto error;
res+=tmp;
if (!(tmp=scan_asn1length(src+res,max,len))) goto error;
if (!(tmp=scan_asn1length(src+res,max,len)))
goto error;
res+=tmp;
}
return res;
error:
return 0;
}
#ifdef UNITTEST
#undef UNITTEST
#include <assert.h>
#include <string.h>
#include "scan_asn1SEQUENCE.c"
#include "scan_asn1INTEGER.c"
#include "scan_asn1tag.c"
#include "scan_asn1tagint.c"
#include "scan_asn1length.c"
#include "scan_asn1int.c"
#include "scan_asn1rawint.c"
int main() {
static char buf[] = "\x30\x0c\x02\x01\x01\x60\x07\x02\x01\x03\x04\x00\x80\x00"; // an LDAP bind request
unsigned long msgid=0, op=0;
size_t len=0;
assert(scan_ldapmessage(buf,buf+14,&msgid,&op,&len)==7 && msgid==1 && op==BindRequest && len == 7);
// trigger scan_asn1SEQUENCE error (too short)
assert(scan_ldapmessage(buf,buf+13,&msgid,&op,&len)==0);
buf[6]=8; assert(scan_ldapmessage(buf,buf+14,&msgid,&op,&len)==0); // fail scan_asn1length
buf[5]=0; assert(scan_ldapmessage(buf,buf+14,&msgid,&op,&len)==0); // fail tc==APPLICATION
buf[5]=0x80; assert(scan_ldapmessage(buf,buf+14,&msgid,&op,&len)==0); // fail scan_asn1tag
buf[2]=0; assert(scan_ldapmessage(buf,buf+14,&msgid,&op,&len)==0); // fail scan_asn1INTEGER
buf[0]=0; assert(scan_ldapmessage(buf,buf+14,&msgid,&op,&len)==0); // fail scan_asn1SEQUENCE
// we only care for 100% coverage of this file, the others have their own unit tests */
return 0;
}
#endif

View File

@@ -3,3 +3,21 @@
size_t scan_ldapmessage_nolengthcheck(const char* src,const char* max,size_t* len) {
return scan_asn1SEQUENCE_nolengthcheck(src,max,len);
}
#ifdef UNITTEST
#include <assert.h>
#undef UNITTEST
#include "scan_asn1tag.c"
#include "scan_asn1tagint.c"
#include "scan_asn1length.c"
#include "scan_asn1SEQUENCE_nolengthcheck.c"
int main() {
static char buf[] = "\x30\x01\x01"; // 0x30 = UNIVERSAL + CONSTRUCTED + SEQUENCE_OF, 0x01 = length 1, 0x01 = dummy filler
size_t l;
assert(scan_ldapmessage_nolengthcheck(buf,buf+3,&l)==2 && l==1);
// we only care for 100% coverage of this file, the others have their own unit tests */
return 0;
}
#endif

View File

@@ -36,24 +36,27 @@ size_t scan_ldapsearchfilter(const char* src,const char* max,struct Filter** f)
unsigned long tag;
const char* nmax;
*f=0;
if (!(res=scan_asn1tag(src,max,&tc,&tt,&tag))) goto error;
if (tc!=PRIVATE || (tt!=CONSTRUCTED && tag!=7) || tag>9) goto error;
if (!(tmp=scan_asn1length(src+res,max,&len))) goto error;
if (!(res=scan_asn1tag(src,max,&tc,&tt,&tag))) // fail01
goto error;
if (tc!=PRIVATE || (tt!=CONSTRUCTED && tag!=7) || tag>9) // fail02
goto error;
if (!(tmp=scan_asn1length(src+res,max,&len))) // fail03
goto error;
res+=tmp;
nmax=src+res+len;
if (nmax>max) goto error;
if (!(*f=calloc(1,sizeof(struct Filter)))) goto error;
#if 0
if (nmax>max) // already caught by scan_asn1length
goto error;
#endif
if (!(*f=calloc(1,sizeof(struct Filter)))) // fail04
goto error;
switch ((*f)->type=tag) {
case 0: /* and [0] SET OF Filter, */
case 1: /* or [1] SET OF Filter, */
(*f)->x=0;
while (src+res<nmax) {
struct Filter* F=(*f)->x;
if (!(tmp=scan_ldapsearchfilter(src+res,nmax,&(*f)->x))) {
if (F) { /* OK, end of sequence */
(*f)->x=F;
break;
}
if (!(tmp=scan_ldapsearchfilter(src+res,nmax,&(*f)->x))) { // fail05
(*f)->x=F;
goto error;
}
@@ -62,32 +65,40 @@ size_t scan_ldapsearchfilter(const char* src,const char* max,struct Filter** f)
}
break;
case 2: /* not [2] Filter, */
if (!(tmp=scan_ldapsearchfilter(src+res,nmax,&(*f)->x))) goto error;
if (tmp!=len) goto error;
if (!(tmp=scan_ldapsearchfilter(src+res,nmax,&(*f)->x))) // fail06
goto error;
if (tmp!=len) // fail07
goto error;
res+=tmp;
break;
case 3: /* equalityMatch [3] AttributeValueAssertion, */
case 5: /* greaterOrEqual [5] AttributeValueAssertion, */
case 6: /* lessOrEqual [6] AttributeValueAssertion, */
case 8: /* approxMatch [8] AttributeValueAssertion, */
if (!(tmp=scan_ldapava(src+res,nmax,&(*f)->ava))) goto error;
if (!(tmp=scan_ldapava(src+res,nmax,&(*f)->ava))) // fail08
goto error;
res+=tmp;
break;
case 4: /* substrings [4] SubstringFilter, */
{
size_t len2;
if (!(tmp=scan_ldapstring(src+res,nmax,&(*f)->ava.desc))) goto error;
if (!(tmp=scan_ldapstring(src+res,nmax,&(*f)->ava.desc))) // fail09
goto error;
res+=tmp;
if (!(tmp=scan_asn1SEQUENCE(src+res,nmax,&len2))) goto error;
if (!(tmp=scan_asn1SEQUENCE(src+res,nmax,&len2))) // fail10
goto error;
res+=tmp;
if (src+res+len2!=nmax) goto error;
while (src+res<nmax) {
struct Substring* s=calloc(1,sizeof(struct Substring));
unsigned long x;
enum asn1_tagtype tt;
enum asn1_tagclass tc;
if (!s) goto error;
if (!(tmp=scan_asn1string(src+res,nmax,&tc,&tt,&x,&s->s.s,&s->s.l)) || x>2) { free(s); goto error; }
if (!s) // fail11
goto error;
if (!(tmp=scan_asn1string(src+res,nmax,&tc,&tt,&x,&s->s.s,&s->s.l)) || x>2) { // fail12
free(s);
goto error;
}
s->substrtype=x;
res+=tmp;
s->next=(*f)->substrings;
@@ -100,9 +111,9 @@ size_t scan_ldapsearchfilter(const char* src,const char* max,struct Filter** f)
(*f)->ava.desc.l=len;
res+=len;
break;
case 9: /* extensibleMatch [9] MatchingRuleAssertion } */
case 9: /* extensibleMatch [9] MatchingRuleAssertion */
default:
goto error;
goto error; // fail14
}
return res;
error:
@@ -110,3 +121,116 @@ error:
*f=0;
return 0;
}
#ifdef UNITTEST
#undef UNITTEST
#include <string.h>
#include <assert.h>
#include "free_ldapsearchfilter.c"
#include "scan_asn1tag.c"
#include "scan_asn1length.c"
#include "scan_ldapava.c"
#include "scan_ldapstring.c"
#include "scan_asn1STRING.c"
#include "scan_asn1tagint.c"
#include "scan_asn1string.c"
#include "scan_asn1SEQUENCE.c"
#include <stdio.h>
size_t callocfail=(size_t)-1;
void* calloc(size_t a,size_t b) {
if (--callocfail==0) return 0;
size_t l;
assert(__builtin_mul_overflow(a,b,&l)==0);
void* x = malloc(l);
if (x) memset(x,0,l);
return x;
}
int main() {
struct Filter *f = 0;
char buf[100];
strcpy(buf,"\x87\x03""foo");
// type 7, PRESENT
assert(scan_ldapsearchfilter(buf, buf+100, &f) == 5); // "(foo=*)" (PRESENT)
assert(f && f->type==PRESENT && f->ava.desc.l==3 && !memcmp(f->ava.desc.s,"foo",3));
free_ldapsearchfilter(f); f=0;
callocfail=1; assert(scan_ldapsearchfilter(buf, buf+100, &f) == 0); // fail04
callocfail=(size_t)-1;
buf[1]=0x7e; assert(scan_ldapsearchfilter(buf, buf+100, &f)==0); // fail03
buf[0]=0x27; assert(scan_ldapsearchfilter(buf, buf+100, &f)==0); // middle fail02
buf[0]=0x8a; assert(scan_ldapsearchfilter(buf, buf+100, &f)==0); // right fail02
buf[0]=0x00; assert(scan_ldapsearchfilter(buf, buf+100, &f)==0); // left fail02
buf[0]=0x1f; assert(scan_ldapsearchfilter(buf, buf+1, &f)==0); // fail01
// type 2, NOT
strcpy(buf,"\xa2\x05\x87\x03""foo");
assert(scan_ldapsearchfilter(buf, buf+100, &f) == 7); // "(!(foo=*))" (NOT + PRESENT)
assert(f && f->type==NOT && f->x->type==PRESENT);
free_ldapsearchfilter(f); f=0;
buf[1]=6; assert(scan_ldapsearchfilter(buf, buf+100, &f) == 0); // fail07
buf[1]=5;
buf[2]=0x00; assert(scan_ldapsearchfilter(buf, buf+100, &f) == 0); // fail06
buf[0]=0xa9; assert(scan_ldapsearchfilter(buf, buf+100, &f) == 0); // type 9 (extensibleMatch) not implemented, should be rejected, fail14
// type 1, OR
strcpy(buf,"\xa1\x0d\x87\5danke\x87\4text");
assert(scan_ldapsearchfilter(buf, buf+100, &f) == 15);
assert(f && f->type==OR && f->x->type==PRESENT && f->x->next->type==PRESENT);
free_ldapsearchfilter(f); f=0;
buf[1]=0xc; assert(scan_ldapsearchfilter(buf, buf+100, &f) == 0); // fail05
buf[1]=0xd; buf[0]=0xa0; assert(scan_ldapsearchfilter(buf, buf+100, &f) == 15); // type 0, AND
assert(f && f->type==AND && f->x->type==PRESENT && f->x->next->type==PRESENT);
free_ldapsearchfilter(f); f=0;
// type 3, equalityMatch
strcpy(buf,"\xa3\x06\x04\x01x\x04\x01y");
assert(scan_ldapsearchfilter(buf, buf+100, &f) == 8);
assert(f && f->type==EQUAL && f->ava.desc.l==1 && f->ava.desc.s[0]=='x' &&
f->ava.value.l==1 && f->ava.value.s[0]=='y');
free_ldapsearchfilter(f); f=0;
// this also achieves 100% coverge of scan_ldapava:
buf[1]=5; assert(scan_ldapsearchfilter(buf, buf+100, &f) == 0); // fail08, fail 2 in scan_ldapava
buf[1]=2; assert(scan_ldapsearchfilter(buf, buf+100, &f) == 0); // fail08, fail 1 in scan_ldapava
// type 5, greaterOrEqual
strcpy(buf,"\xa5\x06\x04\x01x\x04\x01y");
assert(scan_ldapsearchfilter(buf, buf+100, &f) == 8);
assert(f && f->type==GREATEQUAL && f->ava.desc.l==1 && f->ava.desc.s[0]=='x' &&
f->ava.value.l==1 && f->ava.value.s[0]=='y');
free_ldapsearchfilter(f); f=0;
// type 6, lessOrEqual
strcpy(buf,"\xa6\x06\x04\x01x\x04\x01y");
assert(scan_ldapsearchfilter(buf, buf+100, &f) == 8);
assert(f && f->type==LESSEQUAL && f->ava.desc.l==1 && f->ava.desc.s[0]=='x' &&
f->ava.value.l==1 && f->ava.value.s[0]=='y');
free_ldapsearchfilter(f); f=0;
// type 8, approxMatch
strcpy(buf,"\xa8\x06\x04\x01x\x04\x01y");
assert(scan_ldapsearchfilter(buf, buf+100, &f) == 8);
assert(f && f->type==APPROX && f->ava.desc.l==1 && f->ava.desc.s[0]=='x' &&
f->ava.value.l==1 && f->ava.value.s[0]=='y');
free_ldapsearchfilter(f); f=0;
// type 4, substrings
strcpy(buf,"\xa4\x0e\x04\5danke\x30\x05\x82\3red"); // "(danke=*red)"
assert(scan_ldapsearchfilter(buf, buf+100, &f) == 16);
assert(f && f->type==SUBSTRING && f->ava.desc.l==5 && !memcmp(f->ava.desc.s,"danke",5) &&
f->substrings->substrtype==suffix && f->substrings->s.l==3 && !memcmp(f->substrings->s.s,"red",3) &&
f->substrings->next==0);
free_ldapsearchfilter(f); f=0;
callocfail=2;
assert(scan_ldapsearchfilter(buf, buf+100, &f) == 0); // fail11
callocfail=(size_t)-1;
buf[12]=4; assert(scan_ldapsearchfilter(buf, buf+100, &f) == 0); // fail12
buf[10]++; assert(scan_ldapsearchfilter(buf, buf+100, &f) == 0); // fail10
buf[3]=100; assert(scan_ldapsearchfilter(buf, buf+100, &f) == 0); // fail09
return 0;
}
#endif

View File

@@ -3,3 +3,24 @@
size_t scan_ldapstring(const char* src,const char* max,struct string* s) {
return scan_asn1STRING(src,max,&s->s,&s->l);
}
#ifdef UNITTEST
#include <assert.h>
#undef UNITTEST
#include "scan_asn1STRING.c"
#include "scan_asn1tag.c"
#include "scan_asn1tagint.c"
#include "scan_asn1length.c"
#include "scan_asn1string.c"
/* scan_asn1string already has 100% coverage */
int main() {
static char buf[]="\x04\x05""fnord";
struct string s = { 0 };
assert(scan_ldapstring(buf,buf+7,&s)==7 && s.l==5 && s.s==buf+2);
// we only care for 100% coverage of this file, the others have their own unit tests */
return 0;
}
#endif