/* * Copyright (c) 2001 Tommy Bohlin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* iassrv.c * * Limitations: * - Only GetValueByClass supported */ #include #include #include /********************************************************************** * Constants **********************************************************************/ static const char id_server[]="ias server"; static const char id_object[]="ias object"; static const char id_attribute[]="ias attribute"; static const char id_connection[]="ias server connection"; static const char id_inbuf[]="ias server inbuf"; static const char id_outbuf[]="ias server outbuf"; #define IrLMP_VERSION 0x01 #define IAS_SUPPORT 0x00 #define LM_MUX_SUPPORT 0x00 static const u_char irLMPSupport[] = { IrLMP_VERSION, IAS_SUPPORT, LM_MUX_SUPPORT }; /********************************************************************** * Data structures **********************************************************************/ typedef struct String { int length; char chars[60]; /* Longest class and attribute names according to spec */ } String; typedef struct AttributePrivate { Attribute attr; struct AttributePrivate* next; struct AttributePrivate* selnext; struct ObjectPrivate* obj; String name; int type; union { int i; struct { u_short length; u_char charset; u_char bytes[1]; } s; } value; } AttributePrivate; typedef struct ObjectPrivate { Object object; struct ObjectPrivate* next; struct IASServerPrivate* ias; u_short id; String class; AttributePrivate* attrs; } ObjectPrivate; typedef struct IASServerPrivate { IASServer ias; LSAP* lsap; ObjectPrivate* objects; struct IASConnection* connections; } IASServerPrivate; typedef struct IASConnection { struct IASConnection* next; IASServerPrivate* ias; int op; int outOfs; int outLength; int outMax; u_char* outBuf; int inLength; int inMax; u_char* inBuf; } IASConnection; /********************************************************************** * Internal functions **********************************************************************/ static int cmpString(String* s1, String* s2) { int d=s1->length-s2->length; int len=d<0 ? s1->length : s2->length; return memcmp(s1->chars,s2->chars,len) || d; } static int getString(String* s, u_char* buf, int* ip, int len) { int i=*ip; if(i>=len) return 0; s->length=buf[i++]; if(i+s->length>len) return 0; if(s->length>sizeof s->chars) return 0; memcpy(s->chars,buf+i,len); i+=s->length; *ip=i; return 1; } static void sendACK(Connection* con, int op) { u_char hdr[1]; hdr[0]=op|IAS_ACK|IAS_LAST; connWrite(con,hdr,1); } static void sendUnsupported(Connection* con, int op) { u_char hdr[2]; hdr[0]=op|IAS_LAST; hdr[1]=IAS_UNSUPPORTED; connWrite(con,hdr,2); } static u_char* reserveReply(IASConnection* iasc, int n) { u_char* p; while(iasc->outLength+n>iasc->outMax) { iasc->outMax*=2; iasc->outBuf=growMem(iasc->outBuf,iasc->outMax); } p=iasc->outBuf+iasc->outLength; iasc->outLength+=n; return p; } static void getValueByClass(IASConnection* ic, u_char* buf, int len) { String class; String attr; int i; bool classFound=FALSE; int nFound=0; AttributePrivate* selected=0; ObjectPrivate* op; AttributePrivate* ap; /* Dissect arguments */ i=0; if(!getString(&class,buf,&i,len)) { *reserveReply(ic,1)=IAS_NO_CLASS; return; } if(!getString(&attr,buf,&i,len)) { *reserveReply(ic,1)=IAS_NO_ATTR; return; } /* Filter out reply set */ if(ic->ias) { for(op=ic->ias->objects;op;op=op->next) { if(cmpString(&class,&op->class)) continue; classFound=TRUE; for(ap=op->attrs;ap;ap=ap->next) { if(cmpString(&attr,&ap->name)) continue; ap->selnext=selected; selected=ap; nFound++; } } } if(ic->ias->ias.debug&IAS_DEBUG_INFO) birda_log("GetValueByClass %.*s %.*s\n",class.length,class.chars,attr.length,attr.chars); /* Format reply */ if(nFound) { *reserveReply(ic,1)=IAS_SUCCESS; putBEShort(reserveReply(ic,2),nFound); for(ap=selected;ap;ap=ap->selnext) { putBEShort(reserveReply(ic,2),ap->obj->id); *reserveReply(ic,1)=ap->type; if(ic->ias->ias.debug&IAS_DEBUG_INFO) birda_log(" -> %d ",ap->obj->id); usleep(50000); /* XXX LA: this seems to improve ircomm */ switch(ap->type) { case IAS_INTEGER: putBELong(reserveReply(ic,4),ap->value.i); if(ic->ias->ias.debug&IAS_DEBUG_INFO) birda_log("%d\n",ap->value.i); break; case IAS_OCTETS: i=ap->value.s.length; putBEShort(reserveReply(ic,2),i); memcpy(reserveReply(ic,i),ap->value.s.bytes,i); showBytes(ap->value.s.bytes,i); if(ic->ias->ias.debug&IAS_DEBUG_INFO) birda_log("\n"); break; case IAS_STRING: i=ap->value.s.length; *reserveReply(ic,1)=ap->value.s.charset; *reserveReply(ic,1)=i; memcpy(reserveReply(ic,i),ap->value.s.bytes,i); if(ic->ias->ias.debug&IAS_DEBUG_INFO) birda_log("%.*s\n",i,ap->value.s.bytes); break; default: if(ic->ias->ias.debug&IAS_DEBUG_INFO) birda_log("[missing]\n"); break; } } } else { *reserveReply(ic,1)=classFound ? IAS_NO_ATTR : IAS_NO_CLASS; } } static void sendChunk(Connection* con, IASConnection* iasc) { int n=iasc->outLength-iasc->outOfs; int k=connGetSendDataSize(con)-1; u_char hdr[1]; if(k>n) k=n; hdr[0]=iasc->op; if(k==n) hdr[0]|=IAS_LAST; connWrite2(con,hdr,1,iasc->outBuf,k); iasc->outOfs+=k; } static AttributePrivate* makeAttribute(Object* obj, const char* name, int extra) { ObjectPrivate* objp=(ObjectPrivate*)obj; AttributePrivate* attr; int len=strlen(name); if(len>sizeof attr->name.chars) return 0; for(attr=objp->attrs;attr;attr=attr->next) { if(attr->name.length==len && !memcmp(attr->name.chars,name,len)) return 0; } attr=allocMem(id_attribute,sizeof(AttributePrivate)+extra); attr->obj=objp; attr->name.length=len; memcpy(attr->name.chars,name,len); attr->next=objp->attrs; objp->attrs=attr; return attr; } static void status(Connection* con, int event, void* buf, int len) { int flags; IASConnection* ic=(IASConnection*)con->handle; if(event==CONN_CLOSED) { if(ic->ias) { IASConnection** ih=&ic->ias->connections; IASConnection* i; while((i=*ih)) { if(i==ic) { *ih=i->next; break; } else { ih=&i->next; } } } if(ic->outBuf) freeMem(ic->outBuf); if(ic->inBuf) freeMem(ic->inBuf); flags = ic->ias->ias.debug&IAS_DEBUG_INFO; freeMem(ic); connClose(con); if(flags) birda_log("ias closed\n"); } } static void data(Connection* con, void* buf0, int len) { u_char* buf=(u_char*)buf0; IASConnection* iasc=(IASConnection*)con->handle; u_char op; /* Just ignore null input, OK? */ if(len<1) return; op=buf[0]&~(IAS_LAST|IAS_ACK); if(op!=iasc->op) { iasc->op=op; iasc->outLength=0; iasc->inLength=0; } if(buf[0]&IAS_ACK) { if(iasc->outOfsoutLength) sendChunk(con,iasc); } else { iasc->outOfs=0; iasc->outLength=0; while(iasc->inMaxinLength+len-1) { iasc->inMax*=2; iasc->inBuf=growMem(iasc->inBuf,iasc->inMax); } memcpy(iasc->inBuf+iasc->inLength,buf+1,len-1); iasc->inLength+=len-1; if(buf[0]&IAS_LAST) { switch(op) { case OP_GetValueByClass: getValueByClass(iasc,buf+1,len-1); sendChunk(con,iasc); break; default: birda_log("opcode %x not supported\n",op); sendUnsupported(con,op); break; } iasc->inLength=0; } else { sendACK(con,op); } } } static bool accept(LSAP* lsap, Connection* con, void* buf0, int len) { IASConnection* iasc=allocMem(id_connection,sizeof(IASConnection)); IASServerPrivate* iasp=(IASServerPrivate*)lsap->handle; iasc->ias=iasp; iasc->inLength=0; iasc->inMax=256; iasc->inBuf=allocMem(id_inbuf,iasc->inMax); iasc->outOfs=0; iasc->outLength=0; iasc->outMax=256; iasc->outBuf=allocMem(id_outbuf,iasc->outMax); iasc->next=iasp->connections; iasp->connections=iasc; con->handle=iasc; con->status=status; con->data=data; if(iasc->ias->ias.debug&IAS_DEBUG_INFO) birda_log("ias accept\n"); return TRUE; } /********************************************************************** * External functions **********************************************************************/ void iasAttrDelete(Attribute* attr) { AttributePrivate* attrp=(AttributePrivate*)attr; AttributePrivate** ah=&attrp->obj->attrs; AttributePrivate* a; while((a=*ah)) { if(a==attrp) { *ah=a->next; break; } else { ah=&a->next; } } freeMem(attrp); } Attribute* iasObjNewInteger(Object* obj, const char* name, int value) { AttributePrivate* attr=makeAttribute(obj,name,0); if(attr) { attr->type=IAS_INTEGER; attr->value.i=value; } return &attr->attr; } Attribute* iasObjNewString(Object* obj, const char* name, int charset, const char* value, int length) { AttributePrivate* attr; if(length<0 || length>255) return 0; attr=makeAttribute(obj,name,length); if(attr) { attr->type=IAS_STRING; attr->value.s.charset=charset; attr->value.s.length=length; memcpy(attr->value.s.bytes,value,length); } return &attr->attr; } Attribute* iasObjNewOctets(Object* obj, const char* name, const void* value0, int length) { u_char* value=(u_char*)value0; AttributePrivate* attr; if(length<0 || length>1024) return 0; attr=makeAttribute(obj,name,length); if(attr) { attr->type=IAS_OCTETS; attr->value.s.length=length; memcpy(attr->value.s.bytes,value,length); } return &attr->attr; } void iasObjDelete(Object* obj) { ObjectPrivate* objp=(ObjectPrivate*)obj; ObjectPrivate** oh=&objp->ias->objects; ObjectPrivate* o; AttributePrivate* attr; while((o=*oh)) { if(o==objp) { *oh=o->next; break; } else { oh=&o->next; } } for(attr=objp->attrs;attr;) { AttributePrivate* a1=attr; attr=attr->next; freeMem(a1); } freeMem(objp); } Object* iasSrvNewObject(IASServer* ias, const char* class) { IASServerPrivate* iasp=(IASServerPrivate*)ias; ObjectPrivate* op; int len=strlen(class); u_short id; if(len>sizeof op->class.chars) return 0; for(id=0;;id++) { for(op=iasp->objects;op;op=op->next) if(op->id==id) break; if(!op) break; } op=allocMem(id_object,sizeof(ObjectPrivate)); op->attrs=0; op->ias=iasp; op->id=id; op->class.length=len; memcpy(op->class.chars,class,len); op->next=iasp->objects; iasp->objects=op; return &op->object; } void iasSrvClose(IASServer* ias) { IASServerPrivate* iasp=(IASServerPrivate*)ias; ObjectPrivate* obj; IASConnection* iasc; lsapClose(iasp->lsap); for(iasc=iasp->connections;iasc;iasc=iasc->next) iasc->ias=0; for(obj=iasp->objects;obj;) { ObjectPrivate* o1=obj; AttributePrivate* attr; for(attr=obj->attrs;attr;) { AttributePrivate* a1=attr; attr=attr->next; freeMem(a1); } obj=obj->next; freeMem(o1); } freeMem(iasp); } IASServer* createIASServer(LAP* lap, int charset, const char* name, int len) { IASServerPrivate* iasp=allocMem(id_server,sizeof(IASServerPrivate)); IASServer* ias=&iasp->ias; Object* obj; iasp->objects=0; iasp->connections=0; iasp->lsap=lapNewLSAP(lap,0); iasp->lsap->handle=iasp; iasp->lsap->accept=accept; if(lsapGetSelector(iasp->lsap)!=IAS_LSAPSEL) { birda_log("IAS wasn't registered first: wrong LSAP selector\n"); } obj=iasSrvNewObject(ias,"Device"); iasObjNewString(obj, "DeviceName", charset, name, len); iasObjNewOctets(obj, "IrLMPSupport", irLMPSupport, sizeof irLMPSupport); return ias; }