Custom Service Extensions
A version of this page is also available for
4/8/2010
Custom server extensions enable you to extend the types of services your OBEX server offers. To create a service extension, create a DLL that exports the ServiceCallback function. The Obexserver.h header file provides all of the necessary definitions and data structures.
The following code sample shows the prototype for ServiceCallback.
typedef int (*ServiceCallback) (
struct _obex_transaction *pTrans
);
To create a service extension, implement the ServerCallback function. The following code sample shows the prototype for ServerCallback, which is defined in Obexserver.h.
typedef int (*ServerCallback)(
unsigned int uiOp,
unsigned int uiId,
struct _obex_command * pCommand
);
It takes a single ObexTransaction structure argument, defined in the following manner.
typedef struct _obex_transaction {
unsigned int uiConnectionId;
unsigned int uiTransactionId;
unsigned int uiOp;
ObexCommand *pObex;
MemAlloc ObexAlloc;
MemFree ObexFree;
ServerCallback ObexExecute;
AuthCallback ObexAuthRequest;
} ObexTransaction;
It contains a connection identifier that is allocated by server. The transaction identifier is unique for standard OBEX Request-Response exchange. The operation selector may be one of the following.
#define OBEX_REQ_REQUEST 0x00000001
#define OBEX_REQ_CLOSE 0x00000002
#define OBEX_REQ_INIT 0x00000003
#define OBEX_REQ_UNLOAD 0x00000004
If the transaction is invoked as a result of an OBEX command, received as uiOp == OBEX_REQ_REQUEST
, the transaction also contains the command. All OBEX transaction structures contain a callback pointer ObexExecute that communicates a response for the command to the OBEX server. Memory allocation APIs are also included to give service access to the OBEX heap.
Note
Memory allocation function pointers must be the same so that ObexAlloc can be used to allocate memory and ObexFree can be used to free the memory. Memory allocations must be persistent across transactions.
ObexAuthRequest and ObexEncryptionRequest functions are used to request the underlying transport authentication and encryption, respectively. These functions are used mainly for Bluetooth, IrDA does not support it. The FileBrowser sample also uses these functions.
The following code example shows how to use ObexAuthRequest.
static int ServiceRequest (ObexTransaction *pOT) {
UINT retCode;
HRESULT hr = pOT->ObexAuthRequest(uiTransaction, &retCode);
// If this succeeds, there were no errors during the execution. However, this does not mean that you are authenticated.
if(SUCCEEDED(hr)){
//Authentication is supported, but failed
if(retCode == 0){
}
//Authentication is supported, and succeeded
else if(retCode == 1){
}
//Authentication is not supported, for example IrDA. It is up to the service to decide what to do
else if(retCode == 2){
}
}
else{
//Internal error
}
}
The following code example shows how to use ObexEncryptionRequest.
static int ServiceRequest (ObexTransaction *pOT)
{
UINT retCode;
HRESULT hr = pOT->ObexEncryptionRequest(uiTransaction, &retCode);
//If this succeeds, there were no errors during the execution.
if(SUCCEEDED(hr))
{
//Encryption is supported, but failed.
if(retCode == 0){
}
//Encryption is supported, and succeeded.
else if(retCode == 1){
}
//Encryption is not supported.
else if(retCode == 2){
}
}
else{
//Internal error.
}
}
The following table shows the return values and their corresponding descriptions.
Return values (for return code) | Description |
---|---|
0 == FAILED |
The transport supports authentication but failed. |
1 == SUCCESS |
The transport supports authentication and succeeded. |
2 == NOT_SUPPORTED |
The transport does not support authentication. |
The following code example shows signatures for ObexAuthRequest and ObexEncryptionRequest.
typedef HRESULT (*AuthCallback)(unsigned int uiTransactionId, UINT *uiRetCode);
typedef HRESULT (*EncryptionCallback) (unsigned int uiTransactionId, UINT *uiRetCode);
When the first OBEX request comes in, it has a TARGET field that specifies a universally unique identifier (UUID) of the OBEX service for which the packet is intended. The OBEX server searches the registry for this OBEX service and loads the server extension DLL. If the TARGET is missing, the default inbox service is assumed.
The first call the OBEX server makes to the service extension is OBEX_REQ_INIT. This call is serialized and used to create synchronization primitives and initialize the service.
The service is called when a package for it is received. These calls are not guaranteed to be serialized within a single connection or multiple simultaneous connections.
Calls that carry OBEX requests have uiOp == OBEX_REQ_REQUEST
and pObex != NULL
, that point to an ObexCommand data structure, defined in the following manner.
typedef union {
unsigned char uc;
unsigned int ui;
WCHAR *pwsz;
struct {
unsigned int cuc;
unsigned char *puc;
} caub;
} ObexVariant;
typedef struct _obex_command {
unsigned int uiOp;
unsigned int uiResp;
unsigned int fFinal;
ObexPacketData sPktData;
unsigned int cProp;
unsigned int *aPropID;
ObexVariant *aPropVar;
} ObexCommand;
ObexCommand is a preparsed OBEX packet that arrives through a supported transport.
In this packet, uiOp equals one of the following minus the OBEX_OP_FINAL flag, indicated by the fFinal field in the structure.
#define OBEX_OP_CONNECT (0x00 | OBEX_OP_ISFINAL)
#define OBEX_OP_DISCONNECT 0x01 | OBEX_OP_ISFINAL)
#define OBEX_OP_PUT 0x02
#define OBEX_OP_GET 0x03
#define OBEX_OP_SETPATH (0x05 | OBEX_OP_ISFINAL)
#define OBEX_OP_ABORT (0x7f | OBEX_OP_ISFINAL)
Note
The Windows Mobile OBEX server does not support extensions to op-codes because it does not know how to parse them.
The array of OBEXVARIANT structures contains properties of the OBEX packet. OBEXPACKETDATA contains data that is intrinsic to the current OBEX command.
When the command executes, the service extension MUST indicate a response to the OBEX server before the function returns. OBEX is a client-server protocol; the server must send a data response to every client command.
To facilitate the response, the OBEX server provides the following callback.
typedef int (*ServerCallback) (
unsigned int uiOp,
unsigned int uiId,
struct _obex_command *pCommand
);
The uiId is the transaction identifier for this response.
uiOp is one of the following.
#define OBEX_RESP_RESPOND 0x00000001
#define OBEX_RESP_OK 0x00000002
#define OBEX_RESP_ACCEPT 0x00000003
#define OBEX_RESP_REJECT 0x00000004
#define OBEX_RESP_DENY 0x00000005
#define OBEX_RESP_CONTINUE 0x00000006
#define OBEX_RESP_ABORT 0x00000007
#define OBEX_RESP_DISCONNECT 0x00000008
#define OBEX_RESP_HANGUP 0x00000009
The OBEX server allows simple canned responses that do not require a server extension to generate a command. The following codes cause the OBEX server to format and send respective OBEX responses back to the client:
- OBEX_RESP_OK
- OBEX_RESP_ACCEPT
- OBEX_RESP_REJECT
- OBEX_RESP_DENY
- OBEX_RESP_CONTINUE
- OBEX_RESP_ABORT
OBEX_RESP_RESPOND requires an explicit command to be present in the function call parameters. This command has the same data structure as request, except the uiResp field is used for the response code. The uiOp field MUST contain the same operation code as the original command. The remaining data must be consistent with the OBEX protocol. The OBEX server, however, cannot verify the data's semantics.
OBEX_RESP_DISCONNECT and OBEX_RESP_HANGUP are special commands that can be called with or without a real command response. If used with a command body, the response is sent like OBEX_RESP_RESPOND.
Note
OBEX_RESP_DISCONNECT and OBEX_RESP_HANGUP tend to close the current connection. OBEX_RESP_HANGUP actually closes the physical connection to the client and should only be used if authentication fails or a security attack is detected by the service extension.
To avoid leaking logical connection identifiers, the service extension MUST indicate OBEX_RESP_DISCONNECT when a disconnect request is received.
The service extension is notified of closed physical connections by an OBEX_REQ_CLOSE transaction containing a NULL OBEX command body.
The service is held in a loaded cache of services until the logical connections to the service are terminated. Before the cache unloads, the OBEX_REQ_UNLOAD command is called to release all resources and shutdown active threads. This request is serialized. Immediately following this transaction, the UNLOADLIBRARY command is called on the service DLL.