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 /* mux.c
27 *
28 * Limitations:
29 * - LM exclusive mode not supported
30 */
31
32 #include <irda.h>
33 #include <lapmux.h>
34
35 #include <string.h>
36
37 /**********************************************************************
38 * Constants
39 **********************************************************************/
40
41 static const char id_mux_lsap[]="mux lsap";
42 static const char id_mux_connection[]="mux connection";
43 static const char id_mux_sarbuffer[]="mux sar buffer";
44 static const char id_mux_sendbuf[]="mux sendbuf";
45
46 #define LM_CONTROL 0x80
47
48 #define LM_CONNECT 0x01
49 #define LM_CONNECT_CONFIRM 0x81
50 #define LM_DISCONNECT 0x02
51 #define LM_ACCESSMODE 0x03
52 #define LM_ACCESSMODE_CONFIRM 0x83
53
54 #define LM_UNSUPPORTED 0xff
55
56 #define REASON_USER 0x01
57 #define REASON_LINK_MANAGEMENT 0x05
58 #define REASON_DISCONNECTED 0x06
59 #define REASON_NO_PEER 0x08
60 #define REASON_UNSPECIFIED 0xff
61
62 #define LSAP_UNCONNECTED 0x70
63
64 #define TTP_P 0x80
65 #define TTP_M 0x80
66
67 #define PI_MAXSDUSIZE 1
68
69 #define INITIAL_CREDITS 1 /* Is this the level we want? */
70
71 /**********************************************************************
72 * Internal functions
73 **********************************************************************/
74
makeSendBuf(int length)75 static SendBuf* makeSendBuf(int length)
76 {
77 SendBuf* s=allocMem(id_mux_sendbuf,sizeof(SendBuf)+length-1);
78 s->length=length;
79 return s;
80 }
81
sendDisconnect(LAPPrivate * lapp,int dlsap,int slsap,int reason)82 static void sendDisconnect(LAPPrivate* lapp, int dlsap, int slsap, int reason)
83 {
84 SendBuf* sb=makeSendBuf(6);
85
86 sb->buf[2]=dlsap|LM_CONTROL;
87 sb->buf[3]=slsap;
88 sb->buf[4]=LM_DISCONNECT;
89 sb->buf[5]=reason;
90 /* User data is not supported */
91 lapAppendSendBuffer(lapp,sb);
92 }
93
sendConnect(LAPPrivate * lapp,int dlsap,int slsap,bool ttp,int credits,const u_char * buf,int len)94 static void sendConnect(LAPPrivate* lapp, int dlsap, int slsap,
95 bool ttp, int credits, const u_char* buf, int len)
96 {
97 SendBuf* sb;
98
99 sb=makeSendBuf((ttp ? 7 : 6)+len);
100 sb->buf[2]=dlsap|LM_CONTROL;
101 sb->buf[3]=slsap;
102 sb->buf[4]=LM_CONNECT;
103 sb->buf[5]=0;
104 if(ttp) sb->buf[6]=credits;
105 memcpy(sb->buf+(ttp ? 7 : 6),buf,len);
106 lapAppendSendBuffer(lapp,sb);
107 }
108
sendConnectConfirm(LAPPrivate * lapp,int dlsap,int slsap,bool ttp,int credits)109 static void sendConnectConfirm(LAPPrivate* lapp, int dlsap, int slsap,
110 bool ttp, int credits)
111 {
112 SendBuf* sb;
113
114 sb=makeSendBuf(ttp ? 7 : 6);
115 sb->buf[2]=dlsap|LM_CONTROL;
116 sb->buf[3]=slsap;
117 sb->buf[4]=LM_CONNECT_CONFIRM;
118 sb->buf[5]=0;
119 if(ttp) sb->buf[6]=credits;
120 /* User data not supported */
121 lapAppendSendBuffer(lapp,sb);
122 }
123
sendAccessModeDeny(LAPPrivate * lapp,int dlsap,int slsap)124 static void sendAccessModeDeny(LAPPrivate* lapp, int dlsap, int slsap)
125 {
126 SendBuf* sb=makeSendBuf(6);
127
128 sb->buf[2]=dlsap|LM_CONTROL;
129 sb->buf[3]=slsap;
130 sb->buf[4]=LM_ACCESSMODE_CONFIRM;
131 sb->buf[5]=LM_UNSUPPORTED;
132 lapAppendSendBuffer(lapp,sb);
133 }
134
disableConnection(ConnectionPrivate * conp)135 static void disableConnection(ConnectionPrivate* conp)
136 {
137 if((conp->flags&(CST_OPEN|CST_OPENING)) &&
138 !(conp->flags&(CST_NOTIFIED))) {
139 conp->flags|=CST_NOTIFIED;
140 sendDisconnect(conp->lap,conp->remoteSel,conp->localSel,REASON_USER);
141 }
142
143 if(conp->sarBuffer) {
144 freeMem(conp->sarBuffer);
145 conp->sarBuffer=0;
146 }
147
148 while(conp->sendHead) {
149 SendBuf* sb=conp->sendHead;
150 conp->sendHead=sb->next;
151 freeMem(sb);
152 }
153
154 if(conp->flags&(CST_OPENING|CST_OPEN)) {
155 conp->flags&=~(CST_OPENING|CST_OPEN);
156 if(conp->con.status) conp->con.status(&conp->con,CONN_CLOSED,0,0);
157 }
158 }
159
freeConnection(ConnectionPrivate * conp)160 static void freeConnection(ConnectionPrivate* conp)
161 {
162 LAPPrivate* lapp=conp->lap;
163 ConnectionPrivate** ch=&lapp->connections;
164 ConnectionPrivate* c;
165
166 while((c=*ch)) {
167 if(c==conp) {
168 *ch=c->next;
169 break;
170 } else {
171 ch=&c->next;
172 }
173 }
174
175 if(lapp->nextConn==conp) lapp->nextConn=0;
176 freeMem(conp);
177 }
178
parseTTPConnect(ConnectionPrivate * c,const u_char * buf,int len)179 static int parseTTPConnect(ConnectionPrivate* c, const u_char* buf, int len)
180 {
181 int len1;
182
183 if(len<1) return -1;
184 c->sendCredits=buf[0]&~TTP_P;
185 if(buf[0]&TTP_P) {
186 if(len<2) return -1;
187 len1=buf[1]+2;
188 if(len1>len) return -1;
189 c->sendSduSize=0x7fffffff&getBEParameter(PI_MAXSDUSIZE,0,buf+2,len1-2);
190 return len1;
191 } else {
192 return 1;
193 }
194 }
195
lsapControl(LAPPrivate * lapp,int dlsap,int slsap,int op,ConnectionPrivate * con,u_char * buf,int len)196 static void lsapControl(LAPPrivate* lapp, int dlsap, int slsap, int op,
197 ConnectionPrivate* con, u_char* buf, int len)
198 {
199 int i;
200
201 switch(op) {
202 case LM_CONNECT:
203 if(con) {
204 /* Reply to this? */
205 birda_log("Attempted connect to existing connection\n");
206 } else {
207 LSAPPrivate* lsapp=lapp->lsaps;
208 while(lsapp && lsapp->lsapSel!=dlsap) lsapp=lsapp->next;
209 if(lsapp) {
210 con=allocMem(id_mux_connection,sizeof(ConnectionPrivate));
211 con->con.handle=0;
212 con->con.status=0;
213 con->con.data=0;
214 con->lap=lapp;
215 con->localSel=dlsap;
216 con->remoteSel=slsap;
217 con->flags=(lsapp->flags&CST_USER_FLAGS)|CST_OPEN;
218 con->sendSduSize=0;
219 con->recvSduSize=0;
220 con->sarLength=0;
221 con->sarBuffer=0;
222 con->sendHead=0;
223
224 con->sendCredits=0;
225 con->recvCredits=0;
226 con->wantedRecvCredits=0;
227 con->sendDataSize=lapp->sendParams.dataSize-2; /* LM header bytes */
228 con->recvDataSize=lapp->recvDataSize-2; /* LM header bytes */
229
230 if(con->flags&LM_TINY_TP) {
231 con->recvDataSize--;
232 con->sendDataSize--;
233 con->recvCredits=1;
234 con->wantedRecvCredits=INITIAL_CREDITS;
235 }
236
237 i=lsapp->flags&LM_TINY_TP ? parseTTPConnect(con,buf,len) : 0;
238 if(i>=0 &&
239 lsapp->lsap.accept &&
240 lsapp->lsap.accept(&lsapp->lsap,&con->con,buf+i,len-i)) {
241 if(con->recvSduSize) con->sarBuffer=allocMem(id_mux_sarbuffer,con->recvSduSize);
242 con->next=lapp->connections;
243 lapp->connections=con;
244
245 /* SAR not enabled in the return direction */
246 sendConnectConfirm(lapp,slsap,dlsap,con->flags&LM_TINY_TP,con->recvCredits);
247 } else {
248 freeMem(con);
249 sendDisconnect(lapp,slsap,dlsap,REASON_UNSPECIFIED);
250 }
251 } else {
252 birda_log("No such LSAP: %d\n",dlsap);
253 sendDisconnect(lapp,slsap,dlsap,REASON_NO_PEER);
254 }
255 }
256 break;
257 case LM_CONNECT_CONFIRM:
258 if(con->flags&CST_OPENING) {
259 i=(con->flags&LM_TINY_TP) ? parseTTPConnect(con,buf,len) : 0;
260 if(i>=0) {
261 con->flags=(con->flags&~CST_OPENING)|CST_OPEN;
262 if(con->recvSduSize) con->sarBuffer=allocMem(id_mux_sarbuffer,con->recvSduSize);
263 if(con->con.status) con->con.status(&con->con,CONN_OPENED,buf+i,len-i);
264 } else {
265 birda_log("failed to exchanged ttp settings\n");
266 con->flags&=CST_OPENING;
267 /* Do anything more? */
268 }
269 }
270 break;
271 case LM_DISCONNECT:
272 if(con) {
273 con->flags|=CST_NOTIFIED;
274 disableConnection(con);
275 }
276 break;
277 }
278 }
279
unconnData(LAPPrivate * lapp,u_char * buf,int len)280 static void unconnData(LAPPrivate* lapp, u_char* buf, int len)
281 {
282 birda_log("Unconnected LSAP data:\n");
283 showBytes(buf,len);
284 }
285
286 /**********************************************************************
287 * Called by lap.c
288 **********************************************************************/
289
lmDisableConnections(LAPPrivate * lapp)290 void lmDisableConnections(LAPPrivate* lapp)
291 {
292 ConnectionPrivate* c;
293
294 for(c=lapp->connections;c;c=c->next) disableConnection(c);
295 }
296
lmFreeConnections(LAPPrivate * lapp)297 void lmFreeConnections(LAPPrivate* lapp)
298 {
299 while(lapp->connections) freeConnection(lapp->connections);
300 }
301
lmMoveConnectionBuffers(LAPPrivate * lapp,int wanted)302 void lmMoveConnectionBuffers(LAPPrivate* lapp, int wanted)
303 {
304 if(lapp->connections) {
305 int flag;
306 ConnectionPrivate* c;
307
308 if(!lapp->nextConn) lapp->nextConn=lapp->connections;
309 for(flag=0,c=lapp->nextConn;lapp->sendCount<wanted;) {
310 if(c->flags&CST_OPEN) {
311 int delta=c->wantedRecvCredits-c->recvCredits;
312 SendBuf* s=c->sendHead;
313 if(s) {
314 c->sendHead=s->next;
315 } else if(delta>0) {
316 s=makeSendBuf(5);
317 s->buf[2]=c->remoteSel;
318 s->buf[3]=c->localSel;
319 s->buf[4]=0;
320 }
321 if(delta>0) {
322 if(delta>127) delta=127;
323 s->buf[4]=(s->buf[4]&TTP_M)|delta;
324 c->recvCredits+=delta;
325 }
326 if(s) {
327 lapAppendSendBuffer(lapp,s);
328 flag=1;
329 }
330 }
331 c=c->next ? c->next : lapp->connections;
332 if(c==lapp->nextConn) {
333 if(!flag) return;
334 flag=0;
335 }
336 }
337 }
338 }
339
lmData(LAPPrivate * lapp,u_char * buf,int len,int expedited)340 void lmData(LAPPrivate* lapp, u_char* buf, int len, int expedited)
341 {
342 int cmd,dlsap,slsap,opcode;
343 ConnectionPrivate* conp;
344
345 if(len<2) return;
346 cmd=LM_CONTROL&buf[0];
347 dlsap=~LM_CONTROL&buf[0];
348 slsap=~LM_CONTROL&buf[1];
349
350 if(dlsap>=LSAP_UNCONNECTED || slsap>=LSAP_UNCONNECTED) {
351 if(!cmd && dlsap==LSAP_UNCONNECTED && slsap==LSAP_UNCONNECTED)
352 unconnData(lapp,buf+2,len-2);
353 return;
354 }
355
356 for(conp=lapp->connections;conp;conp=conp->next)
357 if((conp->flags&(CST_OPENING|CST_OPEN)) &&
358 conp->localSel==dlsap &&
359 conp->remoteSel==slsap) break;
360
361 /* NB: Should we also discard expedited data? */
362
363 if(cmd) {
364 if(len<3 || expedited) return;
365 opcode=buf[2];
366
367 switch(opcode) {
368 case LM_CONNECT_CONFIRM:
369 case LM_CONNECT:
370 case LM_DISCONNECT:
371 lsapControl(lapp,dlsap,slsap,opcode,conp,buf+4,len-4);
372 break;
373 case LM_ACCESSMODE:
374 sendAccessModeDeny(lapp,slsap,dlsap);
375 break;
376 case LM_ACCESSMODE_CONFIRM:
377 break;
378 }
379 } else if(!conp) {
380 sendDisconnect(lapp,slsap,dlsap,REASON_DISCONNECTED);
381 } else if(len>2) {
382 if(!(conp->flags&LM_TINY_TP)) {
383 if(conp->con.data) conp->con.data(&conp->con,buf+2,len-2);
384 } else {
385 if(conp->flags&CST_OPEN) {
386 conp->sendCredits+=buf[2]&~TTP_M;
387 if(len>3) {
388 if(conp->recvCredits>0) conp->recvCredits--;
389 if(!conp->recvSduSize) {
390 if(conp->con.data) conp->con.data(&conp->con,buf+3,len-3);
391 } else if(conp->sarLength+len-3>conp->recvSduSize) {
392 sendDisconnect(conp->lap,conp->remoteSel,conp->localSel,REASON_LINK_MANAGEMENT);
393 disableConnection(conp);
394 birda_log("SDU is too long, closing the connection\n");
395 } else if(buf[2]&TTP_M) {
396 memcpy(conp->sarBuffer+conp->sarLength,buf+3,len-3);
397 conp->sarLength+=len-3;
398 } else if(conp->sarLength) {
399 memcpy(conp->sarBuffer+conp->sarLength,buf+3,len-3);
400 conp->sarLength+=len-3;
401 if(conp->con.data) conp->con.data(&conp->con,conp->sarBuffer,conp->sarLength);
402 conp->sarLength=0;
403 } else {
404 if(conp->con.data) conp->con.data(&conp->con,buf+3,len-3);
405 }
406 }
407 }
408 }
409 }
410 }
411
lmUnitData(LAPPrivate * lapp,u_char * buf,int len)412 void lmUnitData(LAPPrivate* lapp, u_char* buf, int len)
413 {
414 if(len>=2 && buf[0]==LSAP_UNCONNECTED && (~LM_CONTROL&buf[1])==LSAP_UNCONNECTED)
415 unconnData(lapp,buf+2,len-2);
416 }
417
418 /**********************************************************************
419 * External functions
420 **********************************************************************/
421
422
lapNewLSAP(LAP * lap,int flags)423 LSAP* lapNewLSAP(LAP* lap, int flags)
424 {
425 LAPPrivate* lapp=(LAPPrivate*)lap;
426 LSAPPrivate* lsapp;
427 ConnectionPrivate* c;
428 u_char sel=0;
429
430 for(sel=0;sel<LSAP_UNCONNECTED;sel++) {
431 for(lsapp=lapp->lsaps; lsapp && lsapp->lsapSel!=sel; lsapp=lsapp->next);
432 if(lsapp) continue;
433 for(c=lapp->connections;
434 c && !(c->flags&(CST_OPENING|CST_OPEN)) && c->localSel!=sel;
435 c=c->next);
436 if(!c) break;
437 }
438 if(sel==LSAP_UNCONNECTED) return 0;
439
440 lsapp=allocMem(id_mux_lsap,sizeof(LSAPPrivate));
441 lsapp->lsap.handle=0;
442 lsapp->lsap.accept=0;
443 lsapp->lsapSel=sel;
444 lsapp->flags=flags;
445 lsapp->lap=lapp;
446
447 lsapp->next=lapp->lsaps;
448 lapp->lsaps=lsapp;
449
450 return &lsapp->lsap;
451 }
452
lsapGetSelector(LSAP * lsap)453 u_char lsapGetSelector(LSAP* lsap)
454 {
455 return ((LSAPPrivate*)lsap)->lsapSel;
456 }
457
lsapClose(LSAP * lsap)458 void lsapClose(LSAP* lsap)
459 {
460 LSAPPrivate* lsapp=(LSAPPrivate*)lsap;
461 LSAPPrivate* l;
462 LSAPPrivate** lh=&lsapp->lap->lsaps;
463
464 while((l=*lh)) {
465 if(l==lsapp) {
466 *lh=l->next;
467 freeMem(l);
468 return;
469 } else {
470 lh=&l->next;
471 }
472 }
473 }
474
lapNewConnection(LAP * lap,int rlsap,int flags,const void * buf0,int len)475 Connection* lapNewConnection(LAP* lap, int rlsap, int flags, const void* buf0, int len)
476 {
477 const u_char* buf=(u_char*)buf0;
478 LAPPrivate* lapp=(LAPPrivate*)lap;
479 LSAPPrivate* l;
480 ConnectionPrivate* c;
481 u_char llsap=0;
482
483 if(!(lapp->state&STATE_CONNECTED)) return 0;
484
485 for(llsap=0;llsap<LSAP_UNCONNECTED;llsap++) {
486 for(l=lapp->lsaps; l && l->lsapSel!=llsap; l=l->next);
487 if(l) continue;
488 for(c=lapp->connections;
489 c && (!(c->flags&(CST_OPENING|CST_OPEN)) ||
490 c->localSel!=llsap || c->remoteSel!=rlsap);
491 c=c->next);
492 if(!c) break;
493 }
494 if(llsap==LSAP_UNCONNECTED) return 0;
495
496 c=allocMem(id_mux_connection,sizeof(ConnectionPrivate));
497 c->con.handle=0;
498 c->con.status=0;
499 c->con.data=0;
500 c->lap=lapp;
501 c->localSel=llsap;
502 c->remoteSel=rlsap;
503 c->flags=(flags&CST_USER_FLAGS)|CST_OPENING;
504 c->sendSduSize=0;
505 c->recvSduSize=0;
506 c->sarLength=0;
507 c->sarBuffer=0;
508 c->sendHead=0;
509
510 c->sendCredits=0;
511 c->recvCredits=0;
512 c->wantedRecvCredits=0;
513 c->sendDataSize=lapp->sendParams.dataSize-2; /* LM header bytes */
514 c->recvDataSize=lapp->recvDataSize-2; /* LM header bytes */
515
516 if(c->flags&LM_TINY_TP) {
517 c->sendDataSize--;
518 c->recvDataSize--;
519 c->recvCredits=1;
520 c->wantedRecvCredits=INITIAL_CREDITS;
521 }
522
523 c->next=lapp->connections;
524 lapp->connections=c;
525
526 sendConnect(lapp,rlsap,llsap,c->flags&LM_TINY_TP,c->recvCredits,buf,len);
527
528 return &c->con;
529 }
530
connClose(Connection * con)531 void connClose(Connection* con)
532 {
533 ConnectionPrivate* conp=(ConnectionPrivate*)con;
534
535 conp->con.status=0;
536 disableConnection(conp);
537 freeConnection(conp);
538 }
539
connGetSendDataSize(Connection * con)540 int connGetSendDataSize(Connection* con)
541 {
542 ConnectionPrivate* conp=(ConnectionPrivate*)con;
543 return conp->sendSduSize ? conp->sendSduSize : conp->sendDataSize;
544 }
545
connGetRecvDataSize(Connection * con)546 int connGetRecvDataSize(Connection* con)
547 {
548 ConnectionPrivate* conp=(ConnectionPrivate*)con;
549 return conp->recvSduSize ? conp->recvSduSize : conp->recvDataSize;
550 }
551
connWrite(Connection * con,const void * buf0,int len)552 bool connWrite(Connection* con, const void* buf0, int len)
553 {
554 u_char* buf=(u_char*)buf0;
555 ConnectionPrivate* conp=(ConnectionPrivate*)con;
556 int ttp=conp->flags&LM_TINY_TP ? 1 : 0;
557
558 if(!(conp->flags&(CST_OPENING|CST_OPEN))) return FALSE;
559
560 if(len>(conp->sendSduSize ? conp->sendSduSize : conp->sendDataSize)) {
561 birda_log("Packet is too large, discarding\n");
562 return FALSE;
563 }
564
565 while(len>0) {
566 int len1=len<conp->sendDataSize ? len : conp->sendDataSize;
567 SendBuf* sb=makeSendBuf(len1+4+ttp);
568 sb->buf[2]=conp->remoteSel;
569 sb->buf[3]=conp->localSel;
570 if(ttp) sb->buf[4]=len1<len ? TTP_M : 0;
571 memcpy(sb->buf+4+ttp,buf,len1);
572 if(conp->sendHead) conp->sendTail->next=sb;
573 else conp->sendHead=sb;
574 conp->sendTail=sb;
575 sb->next=0;
576 buf+=len1;
577 len-=len1;
578 }
579
580 return TRUE;
581 }
582
connWrite2(Connection * con,const void * buf10,int len1,const void * buf20,int len2)583 bool connWrite2(Connection* con, const void* buf10, int len1, const void* buf20, int len2)
584 {
585 u_char* buf1=(u_char*)buf10;
586 u_char* buf2=(u_char*)buf20;
587 ConnectionPrivate* conp=(ConnectionPrivate*)con;
588 int ttp=conp->flags&LM_TINY_TP ? 1 : 0;
589
590 if(!(conp->flags&(CST_OPENING|CST_OPEN))) return FALSE;
591
592 if(len1+len2>(conp->sendSduSize ? conp->sendSduSize : conp->sendDataSize)) {
593 birda_log("Packet is too large, discarding\n");
594 return FALSE;
595 }
596
597 while(len1+len2>0) {
598 int len1x=len1<conp->sendDataSize ? len1 : conp->sendDataSize;
599 int len2x=len2<conp->sendDataSize-len1x ? len2 : conp->sendDataSize-len1x;
600 SendBuf* sb=makeSendBuf(len1x+len2x+4+ttp);
601 sb->buf[2]=conp->remoteSel;
602 sb->buf[3]=conp->localSel;
603 if(ttp) sb->buf[4]=len2x<len2 ? TTP_M : 0;
604 memcpy(sb->buf+4+ttp,buf1,len1x);
605 memcpy(sb->buf+4+ttp+len1x,buf2,len2x);
606 if(conp->sendHead) conp->sendTail->next=sb;
607 else conp->sendHead=sb;
608 conp->sendTail=sb;
609 sb->next=0;
610 buf1+=len1x;
611 len1-=len1x;
612 buf2+=len2x;
613 len2-=len2x;
614 }
615
616 return TRUE;
617 }
618