|
1 // Copyright (c) 2000-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
2 // All rights reserved. |
|
3 // This component and the accompanying materials are made available |
|
4 // under the terms of "Eclipse Public License v1.0" |
|
5 // which accompanies this distribution, and is available |
|
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
7 // |
|
8 // Initial Contributors: |
|
9 // Nokia Corporation - initial contribution. |
|
10 // |
|
11 // Contributors: |
|
12 // |
|
13 // Description: |
|
14 // |
|
15 |
|
16 #include <bluetooth/logger.h> |
|
17 #include "listener.h" |
|
18 #include "reqhandler.h" |
|
19 #include "sdpconsts.h" |
|
20 #include "epocsvr.h" |
|
21 #include <bt_sock.h> |
|
22 #include <es_sock.h> |
|
23 |
|
24 #ifdef __FLOG_ACTIVE |
|
25 _LIT8(KLogComponent, LOG_COMPONENT_SDP_SERVER); |
|
26 #endif |
|
27 |
|
28 #ifdef _DEBUG |
|
29 PANICCATEGORY("sdplist"); |
|
30 #endif |
|
31 |
|
32 //The size of a the buffer that's used to read a fragment of the incoming data |
|
33 //Typical SDP requests are under 100 bytes. 512 is chosen because it is the |
|
34 //closest power of 2 to the spec default MTU (672). We expect it is big enough |
|
35 //to handle most requests while not taking too much unnecessary space |
|
36 const TInt KRecvFragmentSize = 512; |
|
37 //The initial size of the buffer used to assemble the fragmented data from multiple |
|
38 //continuation read. It is initilised to 4 times of the fragment size in the hope |
|
39 //that on demand growing of the buffer will rarely be necessary. |
|
40 const TInt KRecvInitSize = 2048; |
|
41 |
|
42 CSdpConnection* CSdpConnection::NewL(RSocket& aSocket, CSdpDatabase& aDatabase) |
|
43 /** |
|
44 Spawn of a new connection, using this pre-connected socekt. |
|
45 **/ |
|
46 { |
|
47 LOG_STATIC_FUNC |
|
48 CSdpConnection* self = new (ELeave)CSdpConnection(aSocket, aDatabase); |
|
49 CleanupStack::PushL(self); |
|
50 self->ConstructL(); |
|
51 CleanupStack::Pop(); |
|
52 return self; |
|
53 } |
|
54 |
|
55 CSdpConnection::~CSdpConnection() |
|
56 { |
|
57 LOG_FUNC |
|
58 LOG1(_L("CSdpConnection::~CSdpConnection() 0x%08x"), this); |
|
59 Cancel(); |
|
60 |
|
61 // Delete the active objects that callback on the connection before |
|
62 // closing the socket so that they don't try and use a bad handle. |
|
63 delete iSDPClientTimer; |
|
64 delete iReader; |
|
65 |
|
66 iSocket.Close(); |
|
67 |
|
68 // Delete the various buffers used for the connection |
|
69 delete iReadFragmentHBuf; |
|
70 delete iReadTotalHBuf; |
|
71 delete iWriteHBuf; |
|
72 |
|
73 iLink.Deque(); // Take ourselves off the connection Q. |
|
74 } |
|
75 |
|
76 /** |
|
77 Handles the completion of asynchronous write and shutdown operations. |
|
78 (reads are handled by the separate CSdpConnectionReader active object). |
|
79 */ |
|
80 void CSdpConnection::RunL() |
|
81 { |
|
82 LOG_FUNC |
|
83 LOG2(_L("CSdpConnection::RunL() 0x%08x state=%d"), this, iConnState); |
|
84 if (iConnState == EShutting) |
|
85 { |
|
86 delete this; |
|
87 } |
|
88 else |
|
89 { |
|
90 // If not in the shutting state, then we must be in the writing state. |
|
91 __ASSERT_DEBUG(iConnState == EWriting, PanicServer(ESdpBadState)); |
|
92 User::LeaveIfError(iStatus.Int()); // On error go to the error handling routine |
|
93 // If here then a write successfully completed, so return to |
|
94 // the reading state. |
|
95 iConnState = EReading; |
|
96 } |
|
97 } |
|
98 |
|
99 void CSdpConnection::HandleReadL() |
|
100 { |
|
101 LOG_FUNC |
|
102 iSDPClientTimer->Cancel(); // client is alive, cancel timer |
|
103 LOG1(_L("Received %d bytes of request data [contents]"), iReadFragmentBuf.Length()); |
|
104 FTRACE(FHex(iReadFragmentBuf)); |
|
105 |
|
106 // Adjust the fragmented reading buffers as appropriate |
|
107 TInt remainingDataLength = iRemainingDataLength(); |
|
108 TInt totalDataLength = remainingDataLength + iReadFragmentBuf.Length(); |
|
109 if(iReadTotalBuf.Length() + totalDataLength > iReadTotalBuf.MaxLength()) |
|
110 { |
|
111 iReadTotalHBuf = iReadTotalHBuf->ReAllocL(iReadTotalBuf.MaxLength() + totalDataLength); |
|
112 iReadTotalBuf.Set(iReadTotalHBuf->Des()); //in case the buffer has been moved in ReAlloc |
|
113 } |
|
114 iReadTotalBuf.Append(iReadFragmentBuf); |
|
115 |
|
116 // Queue the next read up to always have a read outstanding. |
|
117 // NOTE: This should be called before the ParseNextPacketL to |
|
118 // ensure the connection state is correct. |
|
119 QueRead(); |
|
120 |
|
121 // If we have a complete L2CAP datagram then parse it, otherwise |
|
122 // continue the fragmented read. |
|
123 if(remainingDataLength > 0) |
|
124 { |
|
125 LOG1(_L("Receive Continue...(%d bytes left to read)"), iRemainingDataLength()); |
|
126 } |
|
127 else |
|
128 { |
|
129 ParseNextPacketL(); |
|
130 iReadTotalBuf.Zero(); |
|
131 } |
|
132 } |
|
133 |
|
134 void CSdpConnection::DoCancel() |
|
135 { |
|
136 LOG_FUNC |
|
137 // There isn't async break in handling state, hence something is wrong if we are here |
|
138 __ASSERT_DEBUG(iConnState != EHandling, PanicServer(ESdpBadState)); |
|
139 |
|
140 iReader->Cancel(); |
|
141 iSocket.CancelWrite(); |
|
142 // Can't cancel a shutdown request |
|
143 } |
|
144 |
|
145 TInt CSdpConnection::RunError(TInt __DEBUG_ONLY(aError)) |
|
146 { |
|
147 LOG_FUNC |
|
148 // Not a recoverable error, disconnect |
|
149 LOG1(_L("CSdpConnection::Error(%d). Aborting link!"), aError); |
|
150 ShutdownConnection(); |
|
151 |
|
152 return KErrNone; |
|
153 } |
|
154 |
|
155 void CSdpConnection::ClientTimeout() |
|
156 { |
|
157 LOG_FUNC |
|
158 LOG(_L("CSdpListener::ClientTimeout()")); |
|
159 LOG(_L("Disconnecting Inactive client")); |
|
160 delete this; |
|
161 } |
|
162 |
|
163 CSdpConnection::CSdpConnection(RSocket& aSocket, CSdpDatabase& aDatabase) |
|
164 : CActive(CActive::EPriorityStandard), |
|
165 iSocket(aSocket), |
|
166 iDatabase(aDatabase), |
|
167 iReadFragmentBuf(0,0), |
|
168 iReadTotalBuf(0,0), |
|
169 iWriteBuf(0,0) |
|
170 { |
|
171 LOG_FUNC |
|
172 LOG1(_L("CSdpConnection::CSdpConnection 0x%08x"), this); |
|
173 CActiveScheduler::Add(this); |
|
174 } |
|
175 |
|
176 void CSdpConnection::ConstructL() |
|
177 { |
|
178 LOG_FUNC |
|
179 TInt outMTU; |
|
180 User::LeaveIfError(iSocket.GetOpt(KL2CAPOutboundMTUForBestPerformance, KSolBtL2CAP, outMTU)); |
|
181 TInt mru; |
|
182 User::LeaveIfError(iSocket.GetOpt(KL2CAPInboundMTU, KSolBtL2CAP, mru)); |
|
183 |
|
184 iReadFragmentHBuf = HBufC8::NewL(KRecvFragmentSize); |
|
185 iReadFragmentBuf.Set(iReadFragmentHBuf->Des()); |
|
186 iReadTotalHBuf = HBufC8::NewL(KRecvInitSize); //will grow on demand |
|
187 iReadTotalBuf.Set(iReadTotalHBuf->Des()); |
|
188 |
|
189 iWriteHBuf = HBufC8::NewL(outMTU); |
|
190 iWriteBuf.Set(iWriteHBuf->Des()); |
|
191 iWriteBuf.SetMax(); |
|
192 iResponse.iParams.Set(&iWriteBuf[KSdpPduHeaderSize], 0, outMTU-KSdpPduHeaderSize); |
|
193 |
|
194 iSDPClientTimer = CSdpClientTimer::NewL(*this); |
|
195 iReader = CSdpConnectionReader::NewL(*this); |
|
196 QueRead(); |
|
197 } |
|
198 |
|
199 void CSdpConnection::ParseNextPacketL() |
|
200 /** |
|
201 Parse a single SDP PDU out of the L2CAP packet received. |
|
202 **/ |
|
203 { |
|
204 LOG_FUNC |
|
205 LOG(_L("CSdpConnection::ParseNextPacketL()")); |
|
206 iConnState = EHandling; // So that the RunError will handle leaves correctly |
|
207 TInt rem = iReadTotalBuf.Length(); |
|
208 if (rem < KSdpPduHeaderSize) |
|
209 { |
|
210 // Incomplete header. Let request hadler cope with the error |
|
211 User::Leave(KErrUnderflow); // Will cause "Invalid Pdu Size" error |
|
212 } |
|
213 iRequest.iPduId = iReadTotalBuf[KSdpPduIdOffset]; |
|
214 iRequest.iTransId = BigEndian::Get16(&iReadTotalBuf[KSdpPduTransIdOffset]); |
|
215 iResponse.iTransId = iRequest.iTransId; // Response has same TransID as request. |
|
216 |
|
217 TUint16 paramlen= BigEndian::Get16(&iReadTotalBuf[KSdpPduParamLengthOffset]); |
|
218 if ((rem != KSdpPduHeaderSize + paramlen) || (paramlen == 0)) |
|
219 { |
|
220 // Not enough parameter data. |
|
221 User::Leave(KErrUnderflow); // Will cause "Invalid Pdu Size" error |
|
222 } |
|
223 iRequest.iParams.Set(&iReadTotalBuf[KSdpPduHeaderSize], paramlen, paramlen); |
|
224 iResponse.iParams.Zero(); |
|
225 LOG3(_L("Parsed SDP PDU ID %d, transaction ID %d, paramater length %d, [params]"), iRequest.iPduId, iRequest.iTransId, paramlen); |
|
226 FTRACE(FHex(iRequest.iParams)); |
|
227 |
|
228 SdpReqHandler::HandleL(iDatabase, iRequest, iResponse); |
|
229 // No leave occured, so response was formed okay. |
|
230 // Now write it back to the client |
|
231 WriteResponse(); |
|
232 } |
|
233 |
|
234 void CSdpConnection::QueRead() |
|
235 { |
|
236 LOG_FUNC |
|
237 __ASSERT_DEBUG(!iReader->IsActive(), PanicServer(ESdpReadAlreadyOutstanding)); |
|
238 iConnState = EReading; |
|
239 iSocket.Recv(iReadFragmentBuf, KSockReadContinuation, iReader->iStatus, iRemainingDataLength); |
|
240 iReader->SetActive(); |
|
241 iSDPClientTimer->Start(); // gate the read with a timeout to guard against inactive clients |
|
242 } |
|
243 |
|
244 void CSdpConnection::WriteResponse() |
|
245 { |
|
246 LOG_FUNC |
|
247 LOG1(_L("Writing %d bytes of response data [contents]"), iWriteBuf.Length()); |
|
248 __ASSERT_DEBUG(!IsActive(), PanicServer(ESdpOutstandingOperation)); |
|
249 TInt paramLen = iResponse.iParams.Length(); |
|
250 ASSERT_DEBUG(paramLen <= static_cast<TInt>(KMaxTUint16)); |
|
251 |
|
252 iWriteBuf.SetLength(paramLen + KSdpPduHeaderSize); |
|
253 iWriteBuf[KSdpPduIdOffset] = iResponse.iPduId; |
|
254 BigEndian::Put16(&iWriteBuf[KSdpPduTransIdOffset], iResponse.iTransId); |
|
255 BigEndian::Put16(&iWriteBuf[KSdpPduParamLengthOffset], TUint16(paramLen)); |
|
256 |
|
257 FTRACE(FHex(iWriteBuf)); |
|
258 iConnState = EWriting; |
|
259 iSocket.Write(iWriteBuf, iStatus); |
|
260 SetActive(); |
|
261 } |
|
262 |
|
263 void CSdpConnection::ShutdownConnection() |
|
264 { |
|
265 LOG_FUNC |
|
266 __ASSERT_DEBUG(!IsActive(), PanicServer(ESdpOutstandingOperation)); |
|
267 iConnState = EShutting; |
|
268 iSocket.Shutdown(RSocket::ENormal, iStatus); |
|
269 SetActive(); |
|
270 } |
|
271 |
|
272 /** |
|
273 Handles the completion of a read operation. |
|
274 */ |
|
275 void CSdpConnection::ReadComplete(TInt aStatus) |
|
276 { |
|
277 LOG_FUNC |
|
278 LOG1(_L("CSdpConnection::ReadComplete with %d"), aStatus); |
|
279 if(aStatus == KErrNone) |
|
280 { |
|
281 if(iConnState == EReading) |
|
282 { |
|
283 TRAP(aStatus, HandleReadL()); |
|
284 |
|
285 if ((aStatus != KErrNone) && (iConnState == EHandling)) |
|
286 { |
|
287 // Problem with received pdu, send an error response to the remote |
|
288 aStatus = SdpReqHandler::RunError(aStatus, iRequest, iResponse); |
|
289 if (aStatus == KErrNone) |
|
290 { |
|
291 LOG(_L("Handled bad SDP request. Sending response...")); |
|
292 WriteResponse(); |
|
293 iReadTotalBuf.Zero(); |
|
294 } |
|
295 } |
|
296 } |
|
297 else |
|
298 { |
|
299 // If we complete a read while handling an existing request |
|
300 // the other side is behaving badly. We deal with this by |
|
301 // ignoring them and disconnecting the connection. |
|
302 LOG1(_L("ReadComplete during another operation(%d)!! Disconnecting..."), iConnState); |
|
303 Cancel(); |
|
304 ShutdownConnection(); |
|
305 } |
|
306 } |
|
307 |
|
308 // deal with any unhandled errors by disconnecting |
|
309 if(aStatus != KErrNone) |
|
310 { |
|
311 Cancel(); |
|
312 ShutdownConnection(); |
|
313 } |
|
314 } |
|
315 |
|
316 void CSdpConnection::CancelRead() |
|
317 { |
|
318 LOG_FUNC |
|
319 iSDPClientTimer->Cancel(); |
|
320 iSocket.CancelRecv(); |
|
321 } |
|
322 |
|
323 CSdpConnectionReader* CSdpConnectionReader::NewL(CSdpConnection& aConnection) |
|
324 { |
|
325 LOG_STATIC_FUNC |
|
326 CSdpConnectionReader* self = new(ELeave) CSdpConnectionReader(aConnection); |
|
327 CleanupStack::PushL(self); |
|
328 self->ConstructL(); |
|
329 CleanupStack::Pop(self); |
|
330 return self; |
|
331 } |
|
332 |
|
333 CSdpConnectionReader::CSdpConnectionReader(CSdpConnection& aConnection) |
|
334 : CActive(EPriorityStandard) |
|
335 , iConnection(aConnection) |
|
336 { |
|
337 LOG_FUNC |
|
338 } |
|
339 |
|
340 void CSdpConnectionReader::ConstructL() |
|
341 { |
|
342 LOG_FUNC |
|
343 CActiveScheduler::Add(this); |
|
344 } |
|
345 |
|
346 CSdpConnectionReader::~CSdpConnectionReader() |
|
347 { |
|
348 LOG_FUNC |
|
349 Cancel(); |
|
350 } |
|
351 |
|
352 void CSdpConnectionReader::RunL() |
|
353 { |
|
354 LOG_FUNC |
|
355 iConnection.ReadComplete(iStatus.Int()); |
|
356 } |
|
357 |
|
358 void CSdpConnectionReader::DoCancel() |
|
359 { |
|
360 LOG_FUNC |
|
361 iConnection.CancelRead(); |
|
362 } |
|
363 |
|
364 CSdpClientTimer* CSdpClientTimer::NewL(CSdpConnection& aConnection) |
|
365 { |
|
366 LOG_STATIC_FUNC |
|
367 CSdpClientTimer* t = new (ELeave) CSdpClientTimer(aConnection); |
|
368 CleanupStack::PushL(t); |
|
369 t->ConstructL(); |
|
370 CleanupStack::Pop(t); |
|
371 return t; |
|
372 } |
|
373 |
|
374 |
|
375 CSdpClientTimer::CSdpClientTimer(CSdpConnection& aConnection) |
|
376 : CTimer(EPriorityStandard), iConnection(aConnection) |
|
377 { |
|
378 LOG_FUNC |
|
379 } |
|
380 |
|
381 void CSdpClientTimer::Start() |
|
382 { |
|
383 LOG_FUNC |
|
384 After(KSDPClientTimeout * 1000000); |
|
385 } |
|
386 |
|
387 void CSdpClientTimer::ConstructL() |
|
388 { |
|
389 LOG_FUNC |
|
390 CTimer::ConstructL(); |
|
391 CActiveScheduler::Add(this); |
|
392 } |
|
393 |
|
394 void CSdpClientTimer::RunL() |
|
395 { |
|
396 LOG_FUNC |
|
397 iConnection.ClientTimeout(); |
|
398 } |
|
399 |
|
400 |
|
401 CSdpListener* CSdpListener::NewL(RSocketServ& aSockServ, TInt aQueSize, CSdpDatabase& aDatabase) |
|
402 { |
|
403 LOG_STATIC_FUNC |
|
404 CSdpListener* self = NewLC(aSockServ, aQueSize, aDatabase); |
|
405 CleanupStack::Pop(); |
|
406 return self; |
|
407 } |
|
408 |
|
409 CSdpListener* CSdpListener::NewLC(RSocketServ& aSockServ, TInt aQueSize, CSdpDatabase& aDatabase) |
|
410 { |
|
411 LOG_STATIC_FUNC |
|
412 CSdpListener* self = new (ELeave) CSdpListener(aSockServ, aDatabase); |
|
413 CleanupStack::PushL(self); |
|
414 self->ConstructL(aQueSize); |
|
415 return self; |
|
416 } |
|
417 |
|
418 |
|
419 CSdpListener::~CSdpListener() |
|
420 { |
|
421 LOG_FUNC |
|
422 while (!iConns.IsEmpty()) |
|
423 { |
|
424 CSdpConnection* conn = iConns.First(); |
|
425 delete conn; |
|
426 } |
|
427 |
|
428 Cancel(); //Ensure this active object is not still active. |
|
429 |
|
430 iListener.Close(); |
|
431 iAccepter.Close(); |
|
432 iDelayAcceptTimer.Close(); |
|
433 } |
|
434 |
|
435 CSdpListener::CSdpListener(RSocketServ& aSockServ, CSdpDatabase& aDatabase) |
|
436 : CActive(EPriorityLow), iSockServ(aSockServ), iDatabase(aDatabase), |
|
437 iIsAcceptDelayed(ETrue), iAcceptDelay(KInitialAcceptDelay), |
|
438 iConns(_FOFF(CSdpConnection, iLink)) |
|
439 { |
|
440 LOG_FUNC |
|
441 CActiveScheduler::Add(this); |
|
442 } |
|
443 |
|
444 void CSdpListener::ConstructL(TInt aQueSize) |
|
445 { |
|
446 LOG_FUNC |
|
447 User::LeaveIfError(iDelayAcceptTimer.CreateLocal()); |
|
448 iQueSize = aQueSize; //set before calling OpenListeningSocket |
|
449 QueAcceptL(); |
|
450 } |
|
451 |
|
452 void CSdpListener::OpenListeningSocketL() |
|
453 { |
|
454 LOG_FUNC |
|
455 User::LeaveIfError(iListener.Open(iSockServ, _L("L2CAP"))); |
|
456 |
|
457 TL2CAPSockAddr addr; |
|
458 addr.SetPort(KSDPPSM); |
|
459 TBTServiceSecurity sdpSecurity; |
|
460 sdpSecurity.SetUid(KUidServiceSDP); |
|
461 sdpSecurity.SetAuthentication(EMitmNotRequired); |
|
462 sdpSecurity.SetAuthorisation(EFalse); |
|
463 sdpSecurity.SetEncryption(EFalse); |
|
464 sdpSecurity.SetDenied(EFalse); |
|
465 |
|
466 addr.SetSecurity(sdpSecurity); |
|
467 |
|
468 User::LeaveIfError(iListener.Bind(addr)); |
|
469 User::LeaveIfError(iListener.Listen(iQueSize)); |
|
470 } |
|
471 |
|
472 void CSdpListener::TryRestartL() |
|
473 { |
|
474 LOG_FUNC |
|
475 if (iIsAcceptDelayed) |
|
476 { |
|
477 LOG(_L("SDP Server: New session open when accept delayed. Retrying right now")); |
|
478 Cancel(); |
|
479 QueAcceptL(); |
|
480 } |
|
481 } |
|
482 |
|
483 void CSdpListener::RunL() |
|
484 { |
|
485 LOG_FUNC |
|
486 if(!iIsAcceptDelayed) |
|
487 { |
|
488 User::LeaveIfError(iStatus.Int()); |
|
489 CSdpConnection *conn = CSdpConnection::NewL(iAccepter, iDatabase); |
|
490 iConns.AddFirst(*conn); |
|
491 iAcceptDelay = KInitialAcceptDelay; |
|
492 } |
|
493 QueAcceptL(); |
|
494 } |
|
495 |
|
496 |
|
497 void CSdpListener::DoCancel() |
|
498 { |
|
499 LOG_FUNC |
|
500 if (iListener.SubSessionHandle()) |
|
501 { |
|
502 iListener.CancelAccept(); |
|
503 iListener.CancelRecv(); |
|
504 } |
|
505 if(iIsAcceptDelayed) |
|
506 { |
|
507 iDelayAcceptTimer.Cancel(); |
|
508 } |
|
509 } |
|
510 |
|
511 |
|
512 TInt CSdpListener::RunError(TInt aError) |
|
513 /** |
|
514 Handle leave from RunL. |
|
515 If this is called, the accept socket is assumed to be cleaned up already |
|
516 **/ |
|
517 { |
|
518 LOG_FUNC |
|
519 LOG1(_L("CSdpListener::Error(%d)"), aError); |
|
520 // Just try to get an accpet going. If this fails, we'll keep trying using |
|
521 // an exponential timeout, as OOM could cause this to keep failing. |
|
522 iListener.Close(); |
|
523 iAccepter.Close(); |
|
524 iIsAcceptDelayed = ETrue; |
|
525 |
|
526 if(iAcceptDelay<=(KMaxAcceptDelay/2)) |
|
527 { |
|
528 //alter ready for next attempt |
|
529 iAcceptDelay*=2; |
|
530 } |
|
531 else if (aError == KErrHardwareNotAvailable) |
|
532 { |
|
533 /* the hardware has gone and we've met our retry limit |
|
534 we'll wait until a new session is created |
|
535 before attempting to get a connection |
|
536 this makes sense as the services will have detected hardware off |
|
537 and typically closed their sockets and handles to SDP. |
|
538 At some point services will try to re-register and this will trigger |
|
539 the SDP server to attempt to reaccept connections. |
|
540 */ |
|
541 return KErrNone; |
|
542 } |
|
543 |
|
544 iDelayAcceptTimer.After(iStatus, iAcceptDelay); |
|
545 SetActive(); |
|
546 |
|
547 return KErrNone; |
|
548 } |
|
549 |
|
550 void CSdpListener::QueAcceptL() |
|
551 { |
|
552 LOG_FUNC |
|
553 User::LeaveIfError(iAccepter.Open(iSockServ)); |
|
554 if(iIsAcceptDelayed) |
|
555 { |
|
556 OpenListeningSocketL(); |
|
557 iIsAcceptDelayed = EFalse; |
|
558 } |
|
559 iListener.Accept(iAccepter, iStatus); |
|
560 SetActive(); |
|
561 } |
|
562 |
|
563 |