KDE Connect iOS Develop Dairy(2) Identity Protocol

In the previous post, KDE Connect iOS Develop Dairy(1) Build, the build of KDE Connect iOS has been fixed, the application can be installed into a device or a simulator.

To connect with the other devices, we need to pair with them. But, before that, the devices need discover each other with the KDE Connect identity mechanism.

Identity Process

The initial identity process is really simple, as shown below:

  1. First, a device A will send a UDP broadcast packet, which carries its identity packet;
  2. Each device B, which receives the UDP broadcast, will try to extract the TCP port information in the packet, and try to connect with the device via the port;
  3. With a TCP connection, each device B will send its own identity packet to the device A;
  4. Then the devices will add a link item to the discovered device list, and wait the users’ actions.

Update Identity Packet

With a first attemp, all my devices can find each other, except the iOS one.

In the debug mode, I saw discover message and an output associated:

1
"Inoki" uses an old protocol version, this won't work

So, I captured the packets using Wireshark, to see why the old implementation in KDE Connect iOS doesn’t work. The differences are in the packet content and the tailor data.

Identity Packet content

All network packets are generated by serilizing NetWorkPackage class, which is defined in lib/NetworkPackage.h and lib/NetworkPackage.m.

The properties of that class are:

1
2
3
4
5
6
@property(nonatomic) NSString* _Id;
@property(nonatomic) NSString *_Type;
@property(nonatomic) NSMutableDictionary *_Body;
@property(nonatomic) NSData *_Payload;
@property(nonatomic) NSDictionary *_PayloadTransferInfo;
@property(nonatomic)long _PayloadSize;

After the serilization, the content will be a JSON format string. For example, the packet content from KDE Connect iOS is:

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"id":"1587284674",
"type":"kdeconnect.identity",
"body":{
"deviceId":"test-kdeconnect-ios",
"SupportedOutgoingInterfaces":"kdeconnect.ping,kdeconnect.mpris,kdeconnect.share,kdeconnect.clipboard,kdeconnect.mousepad,kdeconnect.battery,kdeconnect.calendar,kdeconnect.reminder,kdeconnect.contact",
"protocolVersion":5,
"tcpPort":1714,
"deviceType":"Phone",
"deviceName":"Inoki",
"SupportedIncomingInterfaces":"kdeconnect.calendar,kdeconnect.clipboard,kdeconnect.ping,kdeconnect.reminder,kdeconnect.share,kdeconnect.contact"
}
}

And the one from KDE Connect on the other platforme is:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
"id":1587284383,
"type":"kdeconnect.identity",
"body":{
"deviceId":"9985DA4FDD3449C78ACC8597D2C5A782",
"protocolVersion":7,
"tcpPort":1716,
"deviceType":"phone",
"deviceName":"Inoki",
"incomingCapabilities":[
"kdeconnect.calendar","kdeconnect.clipboard","kdeconnect.ping","kdeconnect.reminder","kdeconnect.share","kdeconnect.contact"
],
"outgoingCapabilities":[
"kdeconnect.ping","kdeconnect.mpris","kdeconnect.share","kdeconnect.clipboard","kdeconnect.mousepad","kdeconnect.battery","kdeconnect.calendar","kdeconnect.reminder","kdeconnect.contact"
]
}
}

Fix id field type

We can see the first difference is about the id field. In the KDE Connect iOS packet, it’s a string. But in the newer version protocol, it’s an integer.

So, I change its property type from NSString to NSNumber:

1
2
3
4
5
6
@property(nonatomic) NSNumber *_Id;
@property(nonatomic) NSString *_Type;
@property(nonatomic) NSMutableDictionary *_Body;
@property(nonatomic) NSData *_Payload;
@property(nonatomic) NSDictionary *_PayloadTransferInfo;
@property(nonatomic)long _PayloadSize;

Update capabilities type

Another big change is the type and the name of description of capacibilities:

  • They were SupportedOutgoingInterfaces and SupportedIncomingInterfaces, with string type;
  • In the latest version, they are incomingCapabilities and outgoingCapabilities, with array type.

In KDE Connect iOS, it’s generated by the following lines in lib/NetworkPackage.m:

1
2
[np setObject:[[[PluginFactory sharedInstance] getSupportedIncomingInterfaces] componentsJoinedByString:@","] forKey:@"SupportedIncomingInterfaces"];
[np setObject:[[[PluginFactory sharedInstance] getSupportedOutgoingInterfaces] componentsJoinedByString:@"," ] forKey:@"SupportedOutgoingInterfaces"];

Obviously, the returned values are arrays, but they are joined by a comma string.

So, I just change the key string, and remove the componentsJoinedByString method:

1
2
[np setObject:[[PluginFactory sharedInstance] getSupportedIncomingInterfaces] forKey:@"incomingCapabilities"];
[np setObject:[[PluginFactory sharedInstance] getSupportedOutgoingInterfaces] forKey:@"outgoingCapabilities"];

Update the protocol version

The protocol version field is defined in the header file:

1
#define ProtocolVersion         5

I update it to 7 to match the current version.

Identity Packet tailor data

The tailor data of identity packet from KDE Connect iOS in a Wireshark traffic is \x0D\x0A.

And the one from other KDE Connect versions are \x0A.

So, I change the line in lib/NetworkPackage.m:

1
2
- #define LFDATA [NSData dataWithBytes:"\x0D\x0A" length:2]
+ #define LFDATA [NSData dataWithBytes:"\x0A" length:1]

Conclusion

Finally, the KDE Connect iOS can find the other device and establish the connections to them:

On the contrary, the others cannot find KDE Connect iOS client out yet. That’s because the new version requires TLS/SSL after a TCP connnection. This will get fixed in the next post.

Good luck to myself!