//TODO PairRequest XmlDocs for XP and CE pre 5.0. /// <summary> /// Intiates pairing for a remote device. /// </summary> /// <param name="device">Remote device with which to pair.</param> /// <param name="pin">Chosen PIN code, must be between 1 and 16 ASCII characters.</param> /// <remarks><para>On Windows CE platforms this calls <c>BthPairRequest</c>, /// its MSDN remarks say: /// </para> /// <para>“BthPairRequest passes the parameters to the <c>BthSetPIN</c> /// function and creates an ACL connection. Once the connection is established, /// it calls the <c>BthAuthenticate</c> function to authenticate the device.” /// </para> /// <para>On Windows XP/Vista platforms this calls <c>BluetoothAuthenticateDevice</c>, /// if the pin argument is set to null a Wizard is displayed to accept a PIN from the user, /// otherwise the function executes in transparent mode. /// </para> /// <para>See also /// <see cref="M:InTheHand.Net.Bluetooth.BluetoothSecurity.SetPin(InTheHand.Net.BluetoothAddress,System.String)"/> /// </para> /// </remarks> /// <returns>Whether the operation was successful.</returns> public static bool PairRequest(BluetoothAddress device, string pin) { if (device == null) { throw new ArgumentNullException("device"); } if (device.ToInt64() == 0) { throw new ArgumentNullException("device", "A non-blank address must be specified."); } #if WinCE if (pin == null) { throw new ArgumentNullException("pin"); } bool success = false; InTheHand.Net.Sockets.BluetoothDeviceInfo bdi = new InTheHand.Net.Sockets.BluetoothDeviceInfo(device); if (System.Environment.OSVersion.Version.Major >= 10) { byte[] pinbytes = System.Text.Encoding.ASCII.GetBytes(pin); int len = pin.Length; int result = NativeMethods.BthPairRequest(device.ToByteArray(), len, pinbytes); if (result == 0) { success = true; } } else { //BthPairRequest is CE 5.0 onwards so we will do it with individual steps //preset outgoing pin success = SetPin(device, pin); if (success) { int hresult; ushort handle = 0; //connect to device try { hresult = NativeMethods.BthCreateACLConnection(device.ToByteArray(), out handle); if (hresult != 0) { success = false; } else { // ushort default_sniff_max = 0x640; // 1 sec // ushort default_sniff_min = 0x4B0; // .75 sec m // ushort default_sniff_attempt = 0x20; // 0x1 // ushort default_sniff_to = 0x20; // 0x1 //force authentication hresult = NativeMethods.BthAuthenticate(device.ToByteArray()); if (hresult != 0) { success = false; } } } finally { if (handle != 0) { //close connection hresult = NativeMethods.BthCloseConnection(handle); } } } } if (success) { //setup UI pairing (registry) RegistryKey rkDevices = Registry.LocalMachine.CreateSubKey(NativeMethods.ceRegistryRoot + "\\Device"); RegistryKey rkNewDevice = rkDevices.CreateSubKey(device.ToString()); rkNewDevice.SetValue("name", bdi.DeviceName); rkNewDevice.SetValue("trusted", 1); rkNewDevice.SetValue("class", bdi.ClassOfDevice.GetHashCode()); #if V2 RegistryKey rkServices = rkNewDevice.CreateSubKey("Services"); ServiceRecord[] recs = bdi.GetServiceRecords(BluetoothService.SerialPort); //byte[][] recs = bdi.GetServiceRecordsUnparsedWindowsRaw(BluetoothService.SerialPort); if (recs.Length > 0) { byte[] servRecord = recs[0].SourceBytes; RegistryKey rkSerial = rkServices.CreateSubKey(BluetoothService.SerialPort.ToString()); rkSerial.SetValue("sdprecord", servRecord); rkSerial.SetValue("Name", "Serial Port"); rkSerial.SetValue("enabled", 1); int channel = ServiceRecordHelper.GetRfcommChannelNumber(recs[0]); if (channel != -1) { rkSerial.SetValue("channel", 0x14b0000 + channel); } else { System.Diagnostics.Debug.Fail("PairRequest CE, peer SPP record missing channel."); } rkSerial.Close(); } rkServices.Close(); #endif rkNewDevice.Close(); rkDevices.Close(); } return success; #else //use other constructor to ensure struct size is set BLUETOOTH_DEVICE_INFO bdi = new BLUETOOTH_DEVICE_INFO(device.ToInt64()); //string length, but allow for null pins for UI int length = 0; if (pin != null) { length = pin.Length; } int result = NativeMethods.BluetoothAuthenticateDevice(IntPtr.Zero, IntPtr.Zero, ref bdi, pin, length); if(result!=0) { //determine error cause from result... return false; } return true; #endif }
//-------------------------------------------------------------- private bool NativeCallback(IntPtr param, ref BLUETOOTH_DEVICE_INFO bdi) { System.Diagnostics.Debug.Assert(m_pin == null ^ m_userCallback == null); // System.Diagnostics.Debug.WriteLine(String.Format( System.Globalization.CultureInfo.InvariantCulture, "AuthenticateResponder callback (for {0}): 0x{1:X} 0x{2:X}", m_remoteAddress, param, bdi.Address)); // String pin; Int32 ret = NativeErrorSuccess; if (m_pin != null) { // Pre-specified case. System.Diagnostics.Debug.Assert(bdi.Address == m_remoteAddress.ToInt64(), "Should only get callback for the single device."); //TODO if (bdi.Address != m_remoteAddress.ToInt64()) { // return false; //} pin = m_pin; ret = NativeMethods.BluetoothSendAuthenticationResponse( m_radioHandle, ref bdi, pin); } else { // Callback case. System.Diagnostics.Debug.Assert(m_userCallback != null); BluetoothWin32AuthenticationEventArgs e = new BluetoothWin32AuthenticationEventArgs(bdi); while (true) { // Callback the user code OnAuthentication(e); // Don't proceed if no (null) passcode given, or // if the last attempt was successful, or // the decvice has disppeared. if (e.Pin == null) { break; } if (e.PreviousNativeErrorCode == NativeErrorSuccess && e.AttemptNumber != 0) { break; } if (e.PreviousNativeErrorCode == NativeErrorDeviceNotConnected) { // When I try this (against Win2k+Belkin and iPaq hx2190, // both apparently with Broadcom) I see: //[[ //Authenticate one device -- with wrong passcode here the first two times. //Passcode respectively: 'BAD-x', 'BAD-y', '9876' //Making PC discoverable //Hit Return to complete //Authenticating 0017E464CF1E wm_alan1 // Attempt# 0, Last error code 0 //Using '0.23672947484847' //Authenticating 0017E464CF1E wm_alan1 // Attempt# 1, Last error code 1244 //Using '0.54782851764365' //Authenticating 0017E464CF1E wm_alan1 // Attempt# 2, Last error code 1167 //Using '9876' //Authenticating 0017E464CF1E wm_alan1 // Attempt# 3, Last error code 1167 //etc //]] // That is we see the error code of 1244=ErrorNotAuthenticated // once, and then the peer device disappears (1167=ErrorDeviceNotConnected). // I suppose that's a security feature -- its stops an attacker // from trying again and again with different passcodes. // // Anyway the result of that is that is it NOT worth repeating // the callback after the device disappears. break; } pin = e.Pin; System.Diagnostics.Debug.WriteLine(String.Format(System.Globalization.CultureInfo.InvariantCulture, "BW32Auth SendAuthRsp pin {0}", pin)); ret = NativeMethods.BluetoothSendAuthenticationResponse( m_radioHandle, ref bdi, pin); if (ret != NativeErrorSuccess) { System.Diagnostics.Trace.WriteLine(String.Format( System.Globalization.CultureInfo.InvariantCulture, " BluetoothSendAuthenticationResponse failed: {0}=0x{0:X}", ret)); } // Have to callback the user code after the attempt? BluetoothWin32AuthenticationEventArgs lastEa = e; if (!lastEa.CallbackWithResult) { break; } e = new BluetoothWin32AuthenticationEventArgs(ret, lastEa); } } // if (ret != NativeErrorSuccess) { System.Diagnostics.Trace.WriteLine(String.Format( System.Globalization.CultureInfo.InvariantCulture, "BluetoothSendAuthenticationResponse failed: {0}=0x{0:X}", ret)); } return true; // "The return value from this function is ignored by the system." }
internal BluetoothWin32AuthenticationEventArgs(BLUETOOTH_DEVICE_INFO device) { m_device = new BluetoothDeviceInfo(device); }
private void Register(BluetoothAddress remoteAddress) { System.Diagnostics.Debug.Assert(m_pin == null ^ m_userCallback == null); // m_callback = new NativeMethods.BluetoothAuthenticationCallback(NativeCallback); BLUETOOTH_DEVICE_INFO bdi = new BLUETOOTH_DEVICE_INFO(remoteAddress); UInt32 ret = NativeMethods.BluetoothRegisterForAuthentication( ref bdi, out m_regHandle, m_callback, IntPtr.Zero); int gle = Marshal.GetLastWin32Error(); System.Diagnostics.Debug.Assert(ret == NativeErrorSuccess, "BluetoothRegisterForAuthentication failed, GLE=" + gle.ToString() + "=0x)" + gle.ToString("X")); if (ret != NativeErrorSuccess) { throw new System.ComponentModel.Win32Exception(gle); } m_regHandle.SetObjectToKeepAlive(m_callback); }