1 /*
2 * Copyright (c) 2001 Tommy Bohlin <tommy@gatespace.com>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26 /* iassrv.c
27 *
28 * Limitations:
29 * - Only GetValueByClass supported
30 */
31
32 #include <irda.h>
33 #include <ias.h>
34
35 #include <string.h>
36
37 /**********************************************************************
38 * Constants
39 **********************************************************************/
40
41 static const char id_server[]="ias server";
42 static const char id_object[]="ias object";
43 static const char id_attribute[]="ias attribute";
44 static const char id_connection[]="ias server connection";
45 static const char id_inbuf[]="ias server inbuf";
46 static const char id_outbuf[]="ias server outbuf";
47
48 #define IrLMP_VERSION 0x01
49 #define IAS_SUPPORT 0x00
50 #define LM_MUX_SUPPORT 0x00
51
52 static const u_char irLMPSupport[] = { IrLMP_VERSION, IAS_SUPPORT, LM_MUX_SUPPORT };
53
54 /**********************************************************************
55 * Data structures
56 **********************************************************************/
57
58 typedef struct String {
59 int length;
60 char chars[60]; /* Longest class and attribute names according to spec */
61 } String;
62
63 typedef struct AttributePrivate {
64 Attribute attr;
65 struct AttributePrivate* next;
66 struct AttributePrivate* selnext;
67 struct ObjectPrivate* obj;
68 String name;
69 int type;
70 union {
71 int i;
72 struct {
73 u_short length;
74 u_char charset;
75 u_char bytes[1];
76 } s;
77 } value;
78 } AttributePrivate;
79
80 typedef struct ObjectPrivate {
81 Object object;
82 struct ObjectPrivate* next;
83 struct IASServerPrivate* ias;
84 u_short id;
85 String class;
86 AttributePrivate* attrs;
87 } ObjectPrivate;
88
89 typedef struct IASServerPrivate {
90 IASServer ias;
91 LSAP* lsap;
92 ObjectPrivate* objects;
93 struct IASConnection* connections;
94 } IASServerPrivate;
95
96 typedef struct IASConnection {
97 struct IASConnection* next;
98 IASServerPrivate* ias;
99
100 int op;
101
102 int outOfs;
103 int outLength;
104 int outMax;
105 u_char* outBuf;
106
107 int inLength;
108 int inMax;
109 u_char* inBuf;
110 } IASConnection;
111
112 /**********************************************************************
113 * Internal functions
114 **********************************************************************/
115
cmpString(String * s1,String * s2)116 static int cmpString(String* s1, String* s2)
117 {
118 int d=s1->length-s2->length;
119 int len=d<0 ? s1->length : s2->length;
120 return memcmp(s1->chars,s2->chars,len) || d;
121 }
122
getString(String * s,u_char * buf,int * ip,int len)123 static int getString(String* s, u_char* buf, int* ip, int len)
124 {
125 int i=*ip;
126
127 if(i>=len) return 0;
128 s->length=buf[i++];
129 if(i+s->length>len) return 0;
130 if(s->length>sizeof s->chars) return 0;
131 memcpy(s->chars,buf+i,len);
132 i+=s->length;
133 *ip=i;
134 return 1;
135 }
136
sendACK(Connection * con,int op)137 static void sendACK(Connection* con, int op)
138 {
139 u_char hdr[1];
140
141 hdr[0]=op|IAS_ACK|IAS_LAST;
142 connWrite(con,hdr,1);
143 }
144
sendUnsupported(Connection * con,int op)145 static void sendUnsupported(Connection* con, int op)
146 {
147 u_char hdr[2];
148
149 hdr[0]=op|IAS_LAST;
150 hdr[1]=IAS_UNSUPPORTED;
151 connWrite(con,hdr,2);
152 }
153
reserveReply(IASConnection * iasc,int n)154 static u_char* reserveReply(IASConnection* iasc, int n)
155 {
156 u_char* p;
157
158 while(iasc->outLength+n>iasc->outMax) {
159 iasc->outMax*=2;
160 iasc->outBuf=growMem(iasc->outBuf,iasc->outMax);
161 }
162 p=iasc->outBuf+iasc->outLength;
163 iasc->outLength+=n;
164 return p;
165 }
166
getValueByClass(IASConnection * ic,u_char * buf,int len)167 static void getValueByClass(IASConnection* ic, u_char* buf, int len)
168 {
169 String class;
170 String attr;
171 int i;
172 bool classFound=FALSE;
173 int nFound=0;
174 AttributePrivate* selected=0;
175 ObjectPrivate* op;
176 AttributePrivate* ap;
177
178 /* Dissect arguments */
179 i=0;
180 if(!getString(&class,buf,&i,len)) {
181 *reserveReply(ic,1)=IAS_NO_CLASS;
182 return;
183 }
184 if(!getString(&attr,buf,&i,len)) {
185 *reserveReply(ic,1)=IAS_NO_ATTR;
186 return;
187 }
188
189 /* Filter out reply set */
190 if(ic->ias) {
191 for(op=ic->ias->objects;op;op=op->next) {
192 if(cmpString(&class,&op->class)) continue;
193 classFound=TRUE;
194 for(ap=op->attrs;ap;ap=ap->next) {
195 if(cmpString(&attr,&ap->name)) continue;
196 ap->selnext=selected;
197 selected=ap;
198 nFound++;
199 }
200 }
201 }
202
203 if(ic->ias->ias.debug&IAS_DEBUG_INFO)
204 birda_log("GetValueByClass %.*s %.*s\n",class.length,class.chars,attr.length,attr.chars);
205
206 /* Format reply */
207
208 if(nFound) {
209 *reserveReply(ic,1)=IAS_SUCCESS;
210 putBEShort(reserveReply(ic,2),nFound);
211
212 for(ap=selected;ap;ap=ap->selnext) {
213 putBEShort(reserveReply(ic,2),ap->obj->id);
214 *reserveReply(ic,1)=ap->type;
215 if(ic->ias->ias.debug&IAS_DEBUG_INFO) birda_log(" -> %d ",ap->obj->id);
216 usleep(50000); /* XXX LA: this seems to improve ircomm */
217 switch(ap->type) {
218 case IAS_INTEGER:
219 putBELong(reserveReply(ic,4),ap->value.i);
220 if(ic->ias->ias.debug&IAS_DEBUG_INFO) birda_log("%d\n",ap->value.i);
221 break;
222 case IAS_OCTETS:
223 i=ap->value.s.length;
224 putBEShort(reserveReply(ic,2),i);
225 memcpy(reserveReply(ic,i),ap->value.s.bytes,i);
226 showBytes(ap->value.s.bytes,i);
227 if(ic->ias->ias.debug&IAS_DEBUG_INFO) birda_log("\n");
228 break;
229 case IAS_STRING:
230 i=ap->value.s.length;
231 *reserveReply(ic,1)=ap->value.s.charset;
232 *reserveReply(ic,1)=i;
233 memcpy(reserveReply(ic,i),ap->value.s.bytes,i);
234 if(ic->ias->ias.debug&IAS_DEBUG_INFO) birda_log("%.*s\n",i,ap->value.s.bytes);
235 break;
236 default:
237 if(ic->ias->ias.debug&IAS_DEBUG_INFO) birda_log("[missing]\n");
238 break;
239 }
240 }
241 } else {
242 *reserveReply(ic,1)=classFound ? IAS_NO_ATTR : IAS_NO_CLASS;
243 }
244 }
245
sendChunk(Connection * con,IASConnection * iasc)246 static void sendChunk(Connection* con, IASConnection* iasc)
247 {
248 int n=iasc->outLength-iasc->outOfs;
249 int k=connGetSendDataSize(con)-1;
250 u_char hdr[1];
251
252 if(k>n) k=n;
253 hdr[0]=iasc->op;
254 if(k==n) hdr[0]|=IAS_LAST;
255 connWrite2(con,hdr,1,iasc->outBuf,k);
256 iasc->outOfs+=k;
257 }
258
makeAttribute(Object * obj,const char * name,int extra)259 static AttributePrivate* makeAttribute(Object* obj, const char* name, int extra)
260 {
261 ObjectPrivate* objp=(ObjectPrivate*)obj;
262 AttributePrivate* attr;
263 int len=strlen(name);
264
265 if(len>sizeof attr->name.chars) return 0;
266 for(attr=objp->attrs;attr;attr=attr->next) {
267 if(attr->name.length==len &&
268 !memcmp(attr->name.chars,name,len)) return 0;
269 }
270
271 attr=allocMem(id_attribute,sizeof(AttributePrivate)+extra);
272 attr->obj=objp;
273 attr->name.length=len;
274 memcpy(attr->name.chars,name,len);
275
276 attr->next=objp->attrs;
277 objp->attrs=attr;
278
279 return attr;
280 }
281
status(Connection * con,int event,void * buf,int len)282 static void status(Connection* con, int event, void* buf, int len)
283 {
284 int flags;
285 IASConnection* ic=(IASConnection*)con->handle;
286
287 if(event==CONN_CLOSED) {
288 if(ic->ias) {
289 IASConnection** ih=&ic->ias->connections;
290 IASConnection* i;
291
292 while((i=*ih)) {
293 if(i==ic) {
294 *ih=i->next;
295 break;
296 } else {
297 ih=&i->next;
298 }
299 }
300 }
301
302 if(ic->outBuf) freeMem(ic->outBuf);
303 if(ic->inBuf) freeMem(ic->inBuf);
304 flags = ic->ias->ias.debug&IAS_DEBUG_INFO;
305 freeMem(ic);
306 connClose(con);
307 if(flags) birda_log("ias closed\n");
308 }
309 }
310
data(Connection * con,void * buf0,int len)311 static void data(Connection* con, void* buf0, int len)
312 {
313 u_char* buf=(u_char*)buf0;
314 IASConnection* iasc=(IASConnection*)con->handle;
315 u_char op;
316
317 /* Just ignore null input, OK? */
318 if(len<1) return;
319
320 op=buf[0]&~(IAS_LAST|IAS_ACK);
321 if(op!=iasc->op) {
322 iasc->op=op;
323 iasc->outLength=0;
324 iasc->inLength=0;
325 }
326
327 if(buf[0]&IAS_ACK) {
328 if(iasc->outOfs<iasc->outLength) sendChunk(con,iasc);
329 } else {
330 iasc->outOfs=0;
331 iasc->outLength=0;
332
333 while(iasc->inMax<iasc->inLength+len-1) {
334 iasc->inMax*=2;
335 iasc->inBuf=growMem(iasc->inBuf,iasc->inMax);
336 }
337
338 memcpy(iasc->inBuf+iasc->inLength,buf+1,len-1);
339 iasc->inLength+=len-1;
340
341 if(buf[0]&IAS_LAST) {
342 switch(op) {
343 case OP_GetValueByClass:
344 getValueByClass(iasc,buf+1,len-1);
345 sendChunk(con,iasc);
346 break;
347 default:
348 birda_log("opcode %x not supported\n",op);
349 sendUnsupported(con,op);
350 break;
351 }
352 iasc->inLength=0;
353 } else {
354 sendACK(con,op);
355 }
356 }
357 }
358
accept(LSAP * lsap,Connection * con,void * buf0,int len)359 static bool accept(LSAP* lsap, Connection* con, void* buf0, int len)
360 {
361 IASConnection* iasc=allocMem(id_connection,sizeof(IASConnection));
362 IASServerPrivate* iasp=(IASServerPrivate*)lsap->handle;
363
364 iasc->ias=iasp;
365 iasc->inLength=0;
366 iasc->inMax=256;
367 iasc->inBuf=allocMem(id_inbuf,iasc->inMax);
368 iasc->outOfs=0;
369 iasc->outLength=0;
370 iasc->outMax=256;
371 iasc->outBuf=allocMem(id_outbuf,iasc->outMax);
372
373 iasc->next=iasp->connections;
374 iasp->connections=iasc;
375
376 con->handle=iasc;
377 con->status=status;
378 con->data=data;
379
380 if(iasc->ias->ias.debug&IAS_DEBUG_INFO) birda_log("ias accept\n");
381 return TRUE;
382 }
383
384 /**********************************************************************
385 * External functions
386 **********************************************************************/
387
iasAttrDelete(Attribute * attr)388 void iasAttrDelete(Attribute* attr)
389 {
390 AttributePrivate* attrp=(AttributePrivate*)attr;
391 AttributePrivate** ah=&attrp->obj->attrs;
392 AttributePrivate* a;
393
394 while((a=*ah)) {
395 if(a==attrp) {
396 *ah=a->next;
397 break;
398 } else {
399 ah=&a->next;
400 }
401 }
402
403 freeMem(attrp);
404 }
405
iasObjNewInteger(Object * obj,const char * name,int value)406 Attribute* iasObjNewInteger(Object* obj, const char* name, int value)
407 {
408 AttributePrivate* attr=makeAttribute(obj,name,0);
409
410 if(attr) {
411 attr->type=IAS_INTEGER;
412 attr->value.i=value;
413 }
414 return &attr->attr;
415 }
416
iasObjNewString(Object * obj,const char * name,int charset,const char * value,int length)417 Attribute* iasObjNewString(Object* obj, const char* name, int charset, const char* value, int length)
418 {
419 AttributePrivate* attr;
420
421 if(length<0 || length>255) return 0;
422 attr=makeAttribute(obj,name,length);
423 if(attr) {
424 attr->type=IAS_STRING;
425 attr->value.s.charset=charset;
426 attr->value.s.length=length;
427 memcpy(attr->value.s.bytes,value,length);
428 }
429 return &attr->attr;
430 }
431
iasObjNewOctets(Object * obj,const char * name,const void * value0,int length)432 Attribute* iasObjNewOctets(Object* obj, const char* name, const void* value0, int length)
433 {
434 u_char* value=(u_char*)value0;
435 AttributePrivate* attr;
436
437 if(length<0 || length>1024) return 0;
438 attr=makeAttribute(obj,name,length);
439 if(attr) {
440 attr->type=IAS_OCTETS;
441 attr->value.s.length=length;
442 memcpy(attr->value.s.bytes,value,length);
443 }
444 return &attr->attr;
445 }
446
iasObjDelete(Object * obj)447 void iasObjDelete(Object* obj)
448 {
449 ObjectPrivate* objp=(ObjectPrivate*)obj;
450 ObjectPrivate** oh=&objp->ias->objects;
451 ObjectPrivate* o;
452 AttributePrivate* attr;
453
454 while((o=*oh)) {
455 if(o==objp) {
456 *oh=o->next;
457 break;
458 } else {
459 oh=&o->next;
460 }
461 }
462
463 for(attr=objp->attrs;attr;) {
464 AttributePrivate* a1=attr;
465 attr=attr->next;
466 freeMem(a1);
467 }
468
469 freeMem(objp);
470 }
471
iasSrvNewObject(IASServer * ias,const char * class)472 Object* iasSrvNewObject(IASServer* ias, const char* class)
473 {
474 IASServerPrivate* iasp=(IASServerPrivate*)ias;
475 ObjectPrivate* op;
476 int len=strlen(class);
477 u_short id;
478
479 if(len>sizeof op->class.chars) return 0;
480
481 for(id=0;;id++) {
482 for(op=iasp->objects;op;op=op->next) if(op->id==id) break;
483 if(!op) break;
484 }
485
486 op=allocMem(id_object,sizeof(ObjectPrivate));
487 op->attrs=0;
488 op->ias=iasp;
489 op->id=id;
490 op->class.length=len;
491 memcpy(op->class.chars,class,len);
492
493 op->next=iasp->objects;
494 iasp->objects=op;
495
496 return &op->object;
497 }
498
iasSrvClose(IASServer * ias)499 void iasSrvClose(IASServer* ias)
500 {
501 IASServerPrivate* iasp=(IASServerPrivate*)ias;
502 ObjectPrivate* obj;
503 IASConnection* iasc;
504
505 lsapClose(iasp->lsap);
506 for(iasc=iasp->connections;iasc;iasc=iasc->next) iasc->ias=0;
507
508 for(obj=iasp->objects;obj;) {
509 ObjectPrivate* o1=obj;
510 AttributePrivate* attr;
511 for(attr=obj->attrs;attr;) {
512 AttributePrivate* a1=attr;
513 attr=attr->next;
514 freeMem(a1);
515 }
516 obj=obj->next;
517 freeMem(o1);
518 }
519
520 freeMem(iasp);
521 }
522
createIASServer(LAP * lap,int charset,const char * name,int len)523 IASServer* createIASServer(LAP* lap, int charset, const char* name, int len)
524 {
525 IASServerPrivate* iasp=allocMem(id_server,sizeof(IASServerPrivate));
526 IASServer* ias=&iasp->ias;
527 Object* obj;
528
529 iasp->objects=0;
530 iasp->connections=0;
531
532 iasp->lsap=lapNewLSAP(lap,0);
533 iasp->lsap->handle=iasp;
534 iasp->lsap->accept=accept;
535
536 if(lsapGetSelector(iasp->lsap)!=IAS_LSAPSEL) {
537 birda_log("IAS wasn't registered first: wrong LSAP selector\n");
538 }
539
540 obj=iasSrvNewObject(ias,"Device");
541
542 iasObjNewString(obj, "DeviceName", charset, name, len);
543 iasObjNewOctets(obj, "IrLMPSupport", irLMPSupport, sizeof irLMPSupport);
544
545 return ias;
546 }
547