Files
mars-tinyldap/tinyldap.c
leitner 43f8f40837 add md5password (convert argv to md5 password string with base64 for ldif)
fix ldapclient to use all the arguments and cope with fused packets (one
read() returning the search result entry and the end marker).
Make ldif_parse() use strallocs (remove 8k limit on payloads)
2002-07-14 23:34:12 +00:00

873 lines
23 KiB
C

#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include "byte.h"
#include "buffer.h"
#include "ldap.h"
#include "ldif.h"
#include "open.h"
#include "mmap.h"
#include "uint32.h"
#include "auth.h"
#include "bstr.h"
#ifdef STANDALONE
#include "socket.h"
#include "ip6.h"
#include <wait.h>
#endif
#include <signal.h>
#define verbose 1
#define debug 1
char* map;
long filelen;
uint32 magic,attribute_count,record_count,indices_offset,size_of_string_table;
/* how many longs are needed to have one bit for each record? */
uint32 record_set_length;
/* some pre-looked-up attribute offsets to speed up ldap_match_mapped */
uint32 dn_ofs,objectClass_ofs,userPassword_ofs;
#define BUFSIZE 8192
#if (debug != 0)
/* debugging support functions, adapted from t2.c */
static void printava(struct AttributeValueAssertion* a,const char* rel) {
buffer_puts(buffer_2,"[");
buffer_put(buffer_2,a->desc.s,a->desc.l);
buffer_puts(buffer_2," ");
buffer_puts(buffer_2,rel);
buffer_puts(buffer_2," ");
buffer_put(buffer_2,a->value.s,a->value.l);
buffer_puts(buffer_2,"]");
}
static void printal(struct AttributeDescriptionList* a) {
while (a) {
buffer_put(buffer_2,a->a.s,a->a.l);
a=a->next;
if (a) buffer_puts(buffer_2,",");
}
if (a) buffer_puts(buffer_2,"\n");
}
static void printfilter(struct Filter* f) {
switch (f->type) {
case AND:
buffer_puts(buffer_2,"&(");
mergesub:
printfilter(f->x);
buffer_puts(buffer_2,")\n");
break;
case OR:
buffer_puts(buffer_2,"|(");
goto mergesub;
break;
case NOT:
buffer_puts(buffer_2,"!(");
goto mergesub;
case EQUAL:
printava(&f->ava,"==");
break;
case SUBSTRING:
{
struct Substring* s=f->substrings;
int first=1;
buffer_put(buffer_2,f->ava.desc.s,f->ava.desc.l);
buffer_puts(buffer_2," has ");
while (s) {
if (!first) buffer_puts(buffer_2," and "); first=0;
switch(s->substrtype) {
case prefix: buffer_puts(buffer_2,"prefix \""); break;
case any: buffer_puts(buffer_2,"substr \""); break;
case suffix: buffer_puts(buffer_2,"suffix \""); break;
}
buffer_put(buffer_2,s->s.s,s->s.l);
buffer_puts(buffer_2,"\"");
s=s->next;
}
}
break;
case GREATEQUAL:
printava(&f->ava,">=");
break;
case LESSEQUAL:
printava(&f->ava,"<=");
break;
case PRESENT:
printava(&f->ava,"\\exist");
break;
case APPROX:
printava(&f->ava,"\\approx");
break;
case EXTENSIBLE:
buffer_puts(buffer_2,"[extensible]");
break;
}
if (f->next) {
buffer_puts(buffer_2,",");
printfilter(f->next);
}
buffer_flush(buffer_2);
}
#endif
/* recursively fill in attrofs and attrflag */
static void fixup(struct Filter* f) {
if (!f) return;
switch (f->type) {
case EQUAL:
case SUBSTRING:
case GREATEQUAL:
case LESSEQUAL:
case PRESENT:
case APPROX:
{
char* x=map+5*4+size_of_string_table;
unsigned int i;
f->attrofs=f->attrflag=0;
for (i=0; i<attribute_count; ++i) {
uint32 j=uint32_read(x);
if (!matchcasestring(&f->ava.desc,map+j)) {
f->attrofs=j;
uint32_unpack(x+attribute_count*4,&f->attrflag);
break;
}
x+=4;
}
if (!f->attrofs) {
buffer_puts(buffer_2,"cannot find attribute \"");
buffer_put(buffer_2,f->ava.desc.s,f->ava.desc.l);
buffer_putsflush(buffer_2,"\"!\n");
}
}
case AND:
case OR:
case NOT:
if (f->x) fixup(f->x);
default:
break;
}
if (f->next) fixup(f->next);
}
/* find out whether this filter can be accelerated with the indices */
static int indexable(struct Filter* f) {
struct Filter* y=f->x;
if (!f) return 0;
switch (f->type) {
case AND:
while (y) {
if (indexable(y)) return 1;
y=y->next;
}
return 0;
case OR:
while (y) {
if (!indexable(y)) return 0;
y=y->next;
}
/* fall through */
case PRESENT:
return 1;
#if 0
/* doesn't make much sense to try to speed up negated queries */
case NOT:
return indexable(y);
#endif
case SUBSTRING:
if (f->substrings->substrtype!=prefix) return 0;
/* fall through */
case EQUAL:
{
uint32 ofs;
for (ofs=indices_offset+record_count*4; ofs<(unsigned long)filelen;) {
uint32 index_type,next,indexed_attribute;
index_type=uint32_read(map+ofs);
next=uint32_read(map+ofs+4);
indexed_attribute=uint32_read(map+ofs+8);
if (index_type==0)
if (!matchstring(&f->ava.desc,map+indexed_attribute))
return 1;
ofs=next;
}
}
/* fall through */
default:
return 0;
}
}
/* each record can have more than one attribute with the same name, i.e.
* two email addresses. Thus, the index can't just be a sorted list of
* pointers the records (because a record with two email addresses needs
* to be in the index twice, once for each email address). So our index
* is a sorted list of pointers to the attributes. Thus, a look-up in
* the index does not yield the record but the attribute. We need to be
* able to find the record for a given attribute. To do that, we
* exploit the fact that the strings in the string table are in the same
* order as the records, so we can do a binary search over the record
* table to find the record with the attribute. This does not work for
* objectClass, because the classes are stored in a different string
* table to remove duplicates. */
/* find record given a data pointer */
static uint32 findrec(uint32 dat) {
uint32* records=(uint32*)(map+indices_offset);
uint32 bottom=0;
uint32 top=record_count-1;
while ((top>=bottom)) {
uint32 mid=(top+bottom)/2;
uint32 l;
l=uint32_read(map+uint32_read(&records[mid])+8);
if (l<=dat) {
if (mid>=record_count-1)
l=uint32_read(map+uint32_read(&records[0])+12);
else
l=uint32_read(map+uint32_read(&records[mid+1])+8);
if (l>dat) return mid; /* found! */
bottom=mid+1;
} else
if (mid)
top=mid-1;
else
break;
}
buffer_putsflush(buffer_2,"findrec failed!\n");
return 0;
}
/* basic bit-set support: set all bits to zero */
static inline void emptyset(unsigned long* r) {
unsigned long i;
for (i=0; i<record_set_length; ++i) r[i]=0;
}
/* basic bit-set support: set all bits to zero */
static inline void fillset(unsigned long* r) {
unsigned long i;
for (i=0; i<record_set_length; ++i) r[i]=(unsigned long)-1;
}
/* basic bit-set support: set one bit to 1 */
static inline void setbit(unsigned long* r,unsigned long bit) {
r[bit/(8*sizeof(long))] |= (1<<(bit&(8*sizeof(long)-1)));
}
/* basic bit-set support: see if given bit is set */
static inline int isset(unsigned long* r,unsigned long bit) {
return r[bit/(8*sizeof(long))] & (1<<(bit&(8*sizeof(long)-1)));
}
/* use index (sorted table of offsets to records) to do a binary search
* for all records that match the value in s. Set the corresponding
* bits to 1 in bitfield. */
static void tagmatches(uint32* index,unsigned int elements,struct string* s,
unsigned long* bitfield,int (*match)(struct string* s,const char* c)) {
uint32 bottom=0;
uint32 top=elements;
emptyset(bitfield);
while ((top>=bottom)) {
uint32 mid=(top+bottom)/2;
uint32 k;
int l;
k=uint32_read(&index[mid]);
if ((l=match(s,map+k))==0) {
/* match! */
uint32 rec;
uint32 m;
if ((rec=findrec(k)))
setbit(bitfield,rec);
/* there may be multiple matches.
* Look before and after mid, too */
for (k=mid-1; k>0; --k) {
m=uint32_read(&index[k]);
if ((l=match(s,map+m))==0) {
if ((rec=findrec(m)))
setbit(bitfield,rec);
} else break;
}
for (k=mid+1; k<elements; ++k) {
m=uint32_read(&index[k]);
if ((l=match(s,map+m))==0) {
if ((rec=findrec(m)))
setbit(bitfield,rec);
} else break;
}
return;
}
if (l<0) {
if (mid)
top=mid-1;
else
break; /* since our offsets are unsigned, we need to avoid the -1 case */
} else
bottom=mid+1;
}
}
/* Use the indices to answer a query with the given filter.
* For all matching records, set the corresponding bit to 1 in bitfield.
* Note that this match can be approximate. Before answering, the
* matches are verified with ldap_match_mapped, so the index can also
* be used if it only helps eliminate some of the possible matches (for
* example an AND query where only one of the involved attributes has an
* index). */
static int useindex(struct Filter* f,unsigned long* bitfield) {
struct Filter* y=f->x;
if (!f) return 1;
switch (f->type) {
case AND:
{
unsigned long* tmp=alloca(record_set_length*sizeof(unsigned long));
int ok=0;
fillset(bitfield);
while (y) {
if (useindex(y,tmp)) {
unsigned int i;
for (i=0; i<record_set_length; ++i)
bitfield[i] &= tmp[i];
ok=1;
}
y=y->next;
}
return ok;
}
case OR:
{
unsigned long* tmp=alloca(record_set_length*sizeof(unsigned long));
int ok=1;
emptyset(bitfield);
while (y) {
if (useindex(y,tmp)) {
unsigned int i;
for (i=0; i<record_set_length; ++i)
bitfield[i] |= tmp[i];
} else
ok=0;
y=y->next;
}
return ok;
}
#if 0
/* doesn't make much sense to try to speed up negated queries */
case NOT:
return indexable(y);
#endif
case SUBSTRING:
if (f->substrings->substrtype!=prefix) return 0;
{
uint32 ofs;
for (ofs=indices_offset+record_count*4; ofs<(unsigned long)filelen;) {
uint32 index_type,next,indexed_attribute;
index_type=uint32_read(map+ofs);
next=uint32_read(map+ofs+4);
indexed_attribute=uint32_read(map+ofs+8);
if (index_type==0)
if (!matchstring(&f->ava.desc,map+indexed_attribute)) {
tagmatches((uint32*)(map+ofs+12),(next-ofs-12)/4,&f->substrings->s,bitfield,
f->attrflag&1?matchcaseprefix:matchprefix);
return 1;
}
ofs=next;
}
}
return 0;
case PRESENT:
{
/* now this is not exactly using an index, but a linear search
* through the record table, but since each check is very cheap,
* we pretend it's indexed */
char* x=map+5*4+size_of_string_table+attribute_count*8;
unsigned long i;
emptyset(bitfield);
for (i=0; i<record_count; ++i) {
if (ldap_match_present(x-map,f->attrofs))
setbit(bitfield,i);
x+=uint32_read(x)*8;
}
return 1;
}
case EQUAL:
{
uint32 ofs;
for (ofs=indices_offset+record_count*4; ofs<(unsigned long)filelen;) {
uint32 index_type,next,indexed_attribute;
index_type=uint32_read(map+ofs);
next=uint32_read(map+ofs+4);
indexed_attribute=uint32_read(map+ofs+8);
if (index_type==0)
if (!matchstring(&f->ava.desc,map+indexed_attribute)) {
tagmatches((uint32*)(map+ofs+12),(next-ofs-12)/4,&f->ava.value,bitfield,
f->attrflag&1?matchcasestring:matchstring);
return 1;
}
ofs=next;
}
}
/* fall through */
default:
return 0;
}
}
static void answerwith(uint32 ofs,struct SearchRequest* sr,long messageid,int out) {
struct SearchResultEntry sre;
struct PartialAttributeList** pal=&sre.attributes;
#if (debug != 0)
if (debug) {
char* x=map+ofs;
uint32 j;
buffer_putulong(buffer_2,j=uint32_read(x));
buffer_puts(buffer_2," attributes:\n");
x+=8;
buffer_puts(buffer_2," dn: ");
buffer_puts(buffer_2,map+uint32_read(x));
buffer_puts(buffer_2,"\n objectClass: ");
x+=4;
buffer_puts(buffer_2,map+uint32_read(x));
buffer_puts(buffer_2,"\n");
x+=4;
for (; j>2; --j) {
buffer_puts(buffer_2," ");
buffer_puts(buffer_2,map+uint32_read(x));
buffer_puts(buffer_2,": ");
buffer_puts(buffer_2,map+uint32_read(x+4));
buffer_puts(buffer_2,"\n");
x+=8;
}
buffer_flush(buffer_2);
}
#endif
sre.objectName.l=bstrlen(sre.objectName.s=map+uint32_read(map+ofs+8));
sre.attributes=0;
/* now go through list of requested attributes */
{
struct AttributeDescriptionList* adl=sr->attributes;
while (adl) {
const char* val=0;
uint32 i=2,j;
uint32_unpack(map+ofs,&j);
#if 0
buffer_puts(buffer_2,"looking for attribute \"");
buffer_put(buffer_2,adl->a.s,adl->a.l);
buffer_putsflush(buffer_2,"\"\n");
#endif
if (!matchstring(&adl->a,"dn")) val=sre.objectName.s; else
if (!matchstring(&adl->a,"objectClass"))
val=map+uint32_read(map+ofs+12);
else {
for (; i<j; ++i)
if (!matchstring(&adl->a,map+uint32_read(map+ofs+i*8))) {
val=map+uint32_read(map+ofs+i*8+4);
++i;
break;
}
}
if (val) {
*pal=malloc(sizeof(struct PartialAttributeList));
if (!*pal) {
nomem:
buffer_putsflush(buffer_2,"out of virtual memory!\n");
exit(1);
}
(*pal)->type=adl->a;
{
struct AttributeDescriptionList** a=&(*pal)->values;
add_attribute:
*a=malloc(sizeof(struct AttributeDescriptionList));
if (!*a) goto nomem;
(*a)->a.s=bstrfirst(val);
(*a)->a.l=bstrlen(val);
for (;i<j; ++i)
if (!matchstring(&adl->a,map+uint32_read(map+ofs+i*8))) {
val=map+uint32_read(map+ofs+i*8+4);
++i;
a=&(*a)->next;
goto add_attribute;
}
(*a)->next=0;
}
(*pal)->next=0;
pal=&(*pal)->next;
}
adl=adl->next;
}
}
{
long l=fmt_ldapsearchresultentry(0,&sre);
char *buf=alloca(l+300); /* you never know ;) */
long tmp;
if (verbose) {
buffer_puts(buffer_2,"sre len ");
buffer_putulong(buffer_2,l);
buffer_putsflush(buffer_2,".\n");
}
tmp=fmt_ldapmessage(buf,messageid,SearchResultEntry,l);
fmt_ldapsearchresultentry(buf+tmp,&sre);
write(out,buf,l+tmp);
}
}
int handle(int in,int out) {
int len;
char buf[BUFSIZE];
for (len=0;;) {
int tmp=read(in,buf+len,BUFSIZE-len);
int res;
long messageid,op,Len;
if (tmp==0)
if (BUFSIZE-len) { return 0; }
if (tmp<0) { write(2,"error!\n",7); return 1; }
len+=tmp;
res=scan_ldapmessage(buf,buf+len,&messageid,&op,&Len);
if (res>0) {
if (verbose) {
buffer_puts(buffer_2,"got message of length ");
buffer_putulong(buffer_2,Len);
buffer_puts(buffer_2," with id ");
buffer_putulong(buffer_2,messageid);
buffer_puts(buffer_2,": op ");
buffer_putulong(buffer_2,op);
buffer_putsflush(buffer_2,".\n");
}
switch (op) {
case BindRequest:
{
long version,method;
struct string name;
int tmp;
tmp=scan_ldapbindrequest(buf+res,buf+res+len,&version,&name,&method);
if (tmp>=0) {
if (verbose) {
buffer_puts(buffer_2,"bind request: version ");
buffer_putulong(buffer_2,version);
buffer_puts(buffer_2," for name \"");
buffer_put(buffer_2,name.s,name.l);
buffer_puts(buffer_2,"\" with method ");
buffer_putulong(buffer_2,method);
buffer_putsflush(buffer_2,".\n");
}
if (name.l) {
struct Filter f;
struct string password;
f.type=EQUAL; f.attrofs=dn_ofs;
scan_ldapstring(buf+res+tmp,buf+res+len,&password);
f.ava.desc.l=2; f.ava.desc.s="dn";
f.ava.value=name;
f.next=0;
if (!indexable(&f)) {
authfailure:
{
char outbuf[1024];
int s=100;
int len=fmt_ldapbindresponse(outbuf+s,48,"","authentication failure","");
int hlen=fmt_ldapmessage(0,messageid,BindResponse,len);
fmt_ldapmessage(outbuf+s-hlen,messageid,BindResponse,len);
write(out,outbuf+s-hlen,len+hlen);
continue;
}
} else {
unsigned long* result;
unsigned long i,done;
result=alloca(record_set_length*sizeof(unsigned long));
useindex(&f,result);
done=0;
for (i=0; i<record_set_length; ++i)
if (result[i])
done=1;
done=0;
for (i=0; i<record_count; ) {
if (!result[i/(8*sizeof(long))]) {
i+=8*sizeof(long);
continue;
}
for (; i<record_count; ++i) {
if (isset(result,i)) {
uint32 j;
const char* c;
uint32_unpack(map+indices_offset+4*i,&j);
if (!(j=ldap_find_attr_value(j,userPassword_ofs)))
goto authfailure;
c=map+j;
#if 0
buffer_puts(buffer_2,"compare ");
buffer_puts(buffer_2,c);
buffer_puts(buffer_2," with ");
buffer_put(buffer_2,f.ava.value.s,f.ava.value.l);
buffer_putsflush(buffer_2,".\n");
#endif
if (check_password(c,&password)) {
done=1;
goto found;
}
}
}
}
if (!done) goto authfailure;
found:
}
}
{
char outbuf[1024];
int s=100;
int len=fmt_ldapbindresponse(outbuf+s,0,"","go ahead","");
int hlen=fmt_ldapmessage(0,messageid,BindResponse,len);
fmt_ldapmessage(outbuf+s-hlen,messageid,BindResponse,len);
write(out,outbuf+s-hlen,len+hlen);
}
}
}
break;
case SearchRequest:
{
struct SearchRequest sr;
int tmp;
#if 0
{
int fd=open_write("request");
write(fd,buf,res+len);
close(fd);
}
#endif
if ((tmp=scan_ldapsearchrequest(buf+res,buf+res+len,&sr))) {
#if (debug != 0)
if (debug) {
const char* scopes[]={"baseObject","singleLevel","wholeSubtree"};
const char* alias[]={"neverDerefAliases","derefInSearching","derefFindingBaseObj","derefAlways"};
buffer_puts(buffer_2,"search request: baseObject \"");
buffer_put(buffer_2,sr.baseObject.s,sr.baseObject.l);
buffer_puts(buffer_2,"\", scope ");
buffer_puts(buffer_2,scopes[sr.scope]);
buffer_puts(buffer_2,", ");
buffer_puts(buffer_2,alias[sr.derefAliases]);
buffer_puts(buffer_2,"\nsize limit ");
buffer_putulong(buffer_2,sr.sizeLimit);
buffer_puts(buffer_2,", time limit ");
buffer_putulong(buffer_2,sr.timeLimit);
buffer_puts(buffer_2,"\n");
printfilter(sr.filter);
buffer_puts(buffer_2,"attributes: ");
printal(sr.attributes);
buffer_putsflush(buffer_2,"\n\n");
}
#endif
fixup(sr.filter);
if (indexable(sr.filter)) {
unsigned long* result;
unsigned long i;
#if (debug != 0)
if (debug) buffer_putsflush(buffer_2,"query can be answered with index!\n");
#endif
result=alloca(record_set_length*sizeof(unsigned long));
/* Use the index to find matching data. Put the offsets
* of the matches in a table. Use findrec to locate
* the records that point to the data. */
useindex(sr.filter,result);
for (i=0; i<record_count; ) {
if (!result[i/(8*sizeof(long))]) {
i+=8*sizeof(long);
continue;
}
for (; i<record_count; ++i) {
if (isset(result,i)) {
uint32 j;
uint32_unpack(map+indices_offset+4*i,&j);
if (ldap_match_mapped(j,&sr))
answerwith(j,&sr,messageid,out);
}
}
}
} else {
char* x=map+5*4+size_of_string_table+attribute_count*8;
unsigned long i;
for (i=0; i<record_count; ++i) {
uint32 j;
uint32_unpack(x,&j);
if (ldap_match_mapped(x-map,&sr))
answerwith(x-map,&sr,messageid,out);
x+=j*8;
}
}
} else {
buffer_putsflush(buffer_2,"couldn't parse search request!\n");
exit(1);
}
{
char buf[1000];
long l=fmt_ldapsearchresultdone(buf+100,0,"","","");
int hlen=fmt_ldapmessage(0,messageid,SearchResultDone,l);
fmt_ldapmessage(buf+100-hlen,messageid,SearchResultDone,l);
write(out,buf+100-hlen,l+hlen);
}
}
break;
case UnbindRequest:
close(out); if (in!=out) close(in);
return 0;
case ModifyRequest:
{
struct ModifyRequest mr;
int tmp;
buffer_putsflush(buffer_2,"modifyrequest!\n");
if ((tmp=scan_ldapmodifyrequest(buf+res,buf+res+len,&mr))) {
buffer_puts(buffer_1,"modify request: dn \"");
buffer_put(buffer_1,mr.object.s,mr.object.l);
buffer_putsflush(buffer_1,"\"\n");
switch (mr.m.operation) {
case 0: buffer_puts(buffer_1,"Add\n"); break;
case 1: buffer_puts(buffer_1,"Delete\n"); break;
case 2: buffer_puts(buffer_1,"Replace\n"); break;
}
buffer_put(buffer_1,mr.m.AttributeDescription.s,mr.m.AttributeDescription.l);
buffer_puts(buffer_1,"\n");
{
struct AttributeDescriptionList* x=&mr.m.vals;
do {
buffer_puts(buffer_1," -> \"");
buffer_put(buffer_1,x->a.s,x->a.l);
buffer_putsflush(buffer_1,"\"\n");
x=x->next;
} while (x);
}
} else {
buffer_putsflush(buffer_2,"couldn't parse modify request!\n");
exit(1);
}
}
case AbandonRequest:
buffer_putsflush(buffer_2,"AbandonRequest!\n");
/* do nothing */
break;
default:
buffer_puts(buffer_2,"unknown request type ");
buffer_putulong(buffer_2,op);
buffer_putsflush(buffer_2,"\n");
return 0;
// exit(1);
}
Len+=res;
#if 0
buffer_puts(buffer_2,"byte_copy(buf,");
buffer_putulong(buffer_2,len-Len);
buffer_puts(buffer_2,",buf+");
buffer_putulong(buffer_2,Len);
buffer_putsflush(buffer_2,");\n");
#endif
if (Len<len) {
byte_copy(buf,len-Len,buf+Len);
len-=Len;
} else len=0;
} else
exit(2);
}
}
int main() {
#ifdef STANDALONE
int sock;
#endif
signal(SIGPIPE,SIG_IGN);
map=mmap_read("data",&filelen);
if (!map) {
buffer_putsflush(buffer_2,"could not open data!\n");
return 1;
}
uint32_unpack(map,&magic);
uint32_unpack(map+4,&attribute_count);
uint32_unpack(map+2*4,&record_count);
uint32_unpack(map+3*4,&indices_offset);
uint32_unpack(map+4*4,&size_of_string_table);
record_set_length=(record_count+sizeof(unsigned long)*8-1) / (sizeof(long)*8);
/* look up "dn" and "objectClass" */
{
char* x=map+5*4+size_of_string_table;
unsigned int i;
dn_ofs=objectClass_ofs=userPassword_ofs=0;
for (i=0; i<attribute_count; ++i) {
uint32 j;
j=uint32_read(x);
if (!strcmp("dn",map+j))
dn_ofs=j;
else if (!strcasecmp("objectClass",map+j))
objectClass_ofs=j;
else if (!strcasecmp("userPassword",map+j))
userPassword_ofs=j;
x+=4;
}
if (!dn_ofs || !objectClass_ofs) {
buffer_putsflush(buffer_2,"can't happen error: dn or objectClass not there?!\n");
return 0;
}
}
#if 0
ldif_parse("exp.ldif");
if (!first) {
buffer_putsflush(buffer_2,"no data?!");
}
#endif
#ifdef STANDALONE
if ((sock=socket_tcp6())==-1) {
buffer_putsflush(buffer_2,"socket failed!\n");
exit(1);
}
if (socket_bind6_reuse(sock,V6any,389,0)) {
buffer_putsflush(buffer_2,"bind failed!\n");
exit(1);
}
if (socket_listen(sock,32)) {
buffer_putsflush(buffer_2,"listen failed!\n");
exit(1);
}
for (;;) {
char ip[16];
uint16 port;
uint32 scope_id;
int asock;
{
int status;
while ((status=waitpid(-1,0,WNOHANG))!=0 && status!=(pid_t)-1); /* reap zombies */
}
#ifdef DEBUG
again:
#endif
asock=socket_accept6(sock,ip,&port,&scope_id);
if (asock==-1) {
buffer_putsflush(buffer_2,"accept failed!\n");
exit(1);
}
#ifdef DEBUG
handle(asock,asock);
goto again;
// exit(0);
#else
#endif
switch (fork()) {
case -1: buffer_putsflush(buffer_2,"fork failed!\n"); exit(1);
case 0: /* child */
handle(asock,asock);
exit(0); /* not reached */
default:
close(asock);
}
}
#else
handle(0,1);
#endif
return 0;
}