mirror of
https://github.com/KDE/kdeconnect-android
synced 2025-08-22 09:58:08 +00:00
Refactor shared code out, apply ratelimit also to TCP
This commit is contained in:
parent
801367458e
commit
4ae04ae060
@ -11,6 +11,7 @@ import android.content.SharedPreferences;
|
|||||||
import android.net.Network;
|
import android.net.Network;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import android.util.Pair;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.WorkerThread;
|
import androidx.annotation.WorkerThread;
|
||||||
@ -92,29 +93,70 @@ public class LanLinkProvider extends BaseLinkProvider {
|
|||||||
super.onConnectionLost(link);
|
super.onConnectionLost(link);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Pair<NetworkPacket, Boolean> unserializeReceivedIdentityPacket(String message) {
|
||||||
|
NetworkPacket identityPacket;
|
||||||
|
try {
|
||||||
|
identityPacket = NetworkPacket.unserialize(message);
|
||||||
|
} catch (JSONException e) {
|
||||||
|
Log.w("KDE/LanLinkProvider", "Invalid identity packet received: " + e.getMessage());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!DeviceInfo.isValidIdentityPacket(identityPacket)) {
|
||||||
|
Log.w("KDE/LanLinkProvider", "Invalid identity packet received.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String deviceId = identityPacket.getString("deviceId");
|
||||||
|
String myId = DeviceHelper.getDeviceId(context);
|
||||||
|
if (deviceId.equals(myId)) {
|
||||||
|
//Ignore my own broadcast
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rateLimitByDeviceId(deviceId)) {
|
||||||
|
Log.i("LanLinkProvider", "Discarding second packet from the same device " + deviceId + " received too quickly");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean deviceTrusted = isDeviceTrusted(deviceId);
|
||||||
|
if (!deviceTrusted && !TrustedNetworkHelper.isTrustedNetwork(context)) {
|
||||||
|
Log.i("KDE/LanLinkProvider", "Ignoring identity packet because the device is not trusted and I'm not on a trusted network.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Pair<>(identityPacket, deviceTrusted);
|
||||||
|
}
|
||||||
|
|
||||||
//They received my UDP broadcast and are connecting to me. The first thing they send should be their identity packet.
|
//They received my UDP broadcast and are connecting to me. The first thing they send should be their identity packet.
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
private void tcpPacketReceived(Socket socket) throws IOException {
|
private void tcpPacketReceived(Socket socket) throws IOException {
|
||||||
|
|
||||||
NetworkPacket networkPacket;
|
InetAddress address = socket.getInetAddress();
|
||||||
|
if (rateLimitByIp(address)) {
|
||||||
|
Log.i("LanLinkProvider", "Discarding second TCP packet from the same ip " + address + " received too quickly");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String message;
|
||||||
try {
|
try {
|
||||||
String message = readSingleLine(socket);
|
message = readSingleLine(socket);
|
||||||
networkPacket = NetworkPacket.unserialize(message);
|
//Log.e("TcpListener", "Received TCP packet: " + identityPacket.serialize());
|
||||||
//Log.e("TcpListener", "Received TCP packet: " + networkPacket.serialize());
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.e("KDE/LanLinkProvider", "Exception while receiving TCP packet", e);
|
Log.e("KDE/LanLinkProvider", "Exception while receiving TCP packet", e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.i("KDE/LanLinkProvider", "identity packet received from a TCP connection from " + networkPacket.getString("deviceName"));
|
final Pair<NetworkPacket, Boolean> pair = unserializeReceivedIdentityPacket(message);
|
||||||
|
if (pair == null) {
|
||||||
boolean deviceTrusted = isDeviceTrusted(networkPacket.getString("deviceId"));
|
|
||||||
if (!deviceTrusted && !TrustedNetworkHelper.isTrustedNetwork(context)) {
|
|
||||||
Log.i("KDE/LanLinkProvider", "Ignoring identity packet because the device is not trusted and I'm not on a trusted network.");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
final NetworkPacket identityPacket = pair.first;
|
||||||
|
final boolean deviceTrusted = pair.second;
|
||||||
|
|
||||||
identityPacketReceived(networkPacket, socket, LanLink.ConnectionStarted.Locally, deviceTrusted);
|
Log.i("KDE/LanLinkProvider", "identity packet received from a TCP connection from " + identityPacket.getString("deviceName"));
|
||||||
|
|
||||||
|
identityPacketReceived(identityPacket, socket, LanLink.ConnectionStarted.Locally, deviceTrusted);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -136,53 +178,53 @@ public class LanLinkProvider extends BaseLinkProvider {
|
|||||||
throw new IOException("Couldn't read a line from the socket");
|
throw new IOException("Couldn't read a line from the socket");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean rateLimitByIp(InetAddress address) {
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
Long last = lastConnectionTimeByIp.get(address);
|
||||||
|
if (last != null && (last + MILLIS_DELAY_BETWEEN_CONNECTIONS_TO_SAME_DEVICE > now)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
lastConnectionTimeByIp.put(address, now);
|
||||||
|
if (lastConnectionTimeByIp.size() > MAX_RATE_LIMIT_ENTRIES) {
|
||||||
|
lastConnectionTimeByIp.entrySet().removeIf(e -> e.getValue() + MILLIS_DELAY_BETWEEN_CONNECTIONS_TO_SAME_DEVICE < now);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean rateLimitByDeviceId(String deviceId) {
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
Long last = lastConnectionTimeByDeviceId.get(deviceId);
|
||||||
|
if (last != null && (last + MILLIS_DELAY_BETWEEN_CONNECTIONS_TO_SAME_DEVICE > now)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
lastConnectionTimeByDeviceId.put(deviceId, now);
|
||||||
|
if (lastConnectionTimeByDeviceId.size() > MAX_RATE_LIMIT_ENTRIES) {
|
||||||
|
lastConnectionTimeByDeviceId.entrySet().removeIf(e -> e.getValue() + MILLIS_DELAY_BETWEEN_CONNECTIONS_TO_SAME_DEVICE < now);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
//I've received their broadcast and should connect to their TCP socket and send my identity.
|
//I've received their broadcast and should connect to their TCP socket and send my identity.
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
private void udpPacketReceived(DatagramPacket packet) throws JSONException, IOException {
|
private void udpPacketReceived(DatagramPacket packet) throws JSONException, IOException {
|
||||||
|
|
||||||
final InetAddress address = packet.getAddress();
|
final InetAddress address = packet.getAddress();
|
||||||
|
|
||||||
long now = System.currentTimeMillis();
|
if (rateLimitByIp(address)) {
|
||||||
Long lastByIp = lastConnectionTimeByIp.get(address);
|
|
||||||
if (lastByIp != null && (lastByIp + MILLIS_DELAY_BETWEEN_CONNECTIONS_TO_SAME_DEVICE > now)) {
|
|
||||||
Log.i("LanLinkProvider", "Discarding second UDP packet from the same ip " + address + " received too quickly");
|
Log.i("LanLinkProvider", "Discarding second UDP packet from the same ip " + address + " received too quickly");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
lastConnectionTimeByIp.put(address, now);
|
|
||||||
if (lastConnectionTimeByIp.size() > MAX_RATE_LIMIT_ENTRIES) {
|
|
||||||
lastConnectionTimeByIp.entrySet().removeIf(e -> e.getValue() + MILLIS_DELAY_BETWEEN_CONNECTIONS_TO_SAME_DEVICE < now);
|
|
||||||
}
|
|
||||||
|
|
||||||
String message = new String(packet.getData(), Charsets.UTF_8);
|
String message = new String(packet.getData(), Charsets.UTF_8);
|
||||||
final NetworkPacket identityPacket;
|
|
||||||
try {
|
|
||||||
identityPacket = NetworkPacket.unserialize(message);
|
|
||||||
} catch (JSONException e) {
|
|
||||||
Log.w("KDE/LanLinkProvider", "Invalid identity packet received: " + e.getMessage());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!DeviceInfo.isValidIdentityPacket(identityPacket)) {
|
final Pair<NetworkPacket, Boolean> pair = unserializeReceivedIdentityPacket(message);
|
||||||
Log.w("KDE/LanLinkProvider", "Invalid identity packet received.");
|
if (pair == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
final NetworkPacket identityPacket = pair.first;
|
||||||
|
final boolean deviceTrusted = pair.second;
|
||||||
|
|
||||||
final String deviceId = identityPacket.getString("deviceId");
|
Log.i("KDE/LanLinkProvider", "Broadcast identity packet received from " + identityPacket.getString("deviceName"));
|
||||||
String myId = DeviceHelper.getDeviceId(context);
|
|
||||||
if (deviceId.equals(myId)) {
|
|
||||||
//Ignore my own broadcast
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Long lastByDeviceId = lastConnectionTimeByDeviceId.get(deviceId);
|
|
||||||
if (lastByDeviceId != null && (lastByDeviceId + MILLIS_DELAY_BETWEEN_CONNECTIONS_TO_SAME_DEVICE > now)) {
|
|
||||||
Log.i("LanLinkProvider", "Discarding second UDP packet from the same device " + deviceId + " received too quickly");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
lastConnectionTimeByDeviceId.put(deviceId, now);
|
|
||||||
if (lastConnectionTimeByDeviceId.size() > MAX_RATE_LIMIT_ENTRIES) {
|
|
||||||
lastConnectionTimeByDeviceId.entrySet().removeIf(e -> e.getValue() + MILLIS_DELAY_BETWEEN_CONNECTIONS_TO_SAME_DEVICE < now);
|
|
||||||
}
|
|
||||||
|
|
||||||
int tcpPort = identityPacket.getInt("tcpPort", MIN_PORT);
|
int tcpPort = identityPacket.getInt("tcpPort", MIN_PORT);
|
||||||
if (tcpPort < MIN_PORT || tcpPort > MAX_PORT) {
|
if (tcpPort < MIN_PORT || tcpPort > MAX_PORT) {
|
||||||
@ -190,14 +232,6 @@ public class LanLinkProvider extends BaseLinkProvider {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.i("KDE/LanLinkProvider", "Broadcast identity packet received from " + identityPacket.getString("deviceName"));
|
|
||||||
|
|
||||||
boolean deviceTrusted = isDeviceTrusted(identityPacket.getString("deviceId"));
|
|
||||||
if (!deviceTrusted && !TrustedNetworkHelper.isTrustedNetwork(context)) {
|
|
||||||
Log.i("KDE/LanLinkProvider", "Ignoring identity packet because the device is not trusted and I'm not on a trusted network.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
SocketFactory socketFactory = SocketFactory.getDefault();
|
SocketFactory socketFactory = SocketFactory.getDefault();
|
||||||
Socket socket = socketFactory.createSocket(address, tcpPort);
|
Socket socket = socketFactory.createSocket(address, tcpPort);
|
||||||
configureSocket(socket);
|
configureSocket(socket);
|
||||||
@ -237,18 +271,7 @@ public class LanLinkProvider extends BaseLinkProvider {
|
|||||||
*/
|
*/
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
private void identityPacketReceived(final NetworkPacket identityPacket, final Socket socket, final LanLink.ConnectionStarted connectionStarted, final boolean deviceTrusted) throws IOException {
|
private void identityPacketReceived(final NetworkPacket identityPacket, final Socket socket, final LanLink.ConnectionStarted connectionStarted, final boolean deviceTrusted) throws IOException {
|
||||||
|
|
||||||
if (!DeviceInfo.isValidIdentityPacket(identityPacket)) {
|
|
||||||
Log.w("KDE/LanLinkProvider", "Invalid identity packet received.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String myId = DeviceHelper.getDeviceId(context);
|
|
||||||
final String deviceId = identityPacket.getString("deviceId");
|
final String deviceId = identityPacket.getString("deviceId");
|
||||||
if (deviceId.equals(myId)) {
|
|
||||||
Log.e("KDE/LanLinkProvider", "Somehow I'm connected to myself, ignoring. This should not happen.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int protocolVersion = identityPacket.getInt("protocolVersion");
|
int protocolVersion = identityPacket.getInt("protocolVersion");
|
||||||
if (deviceTrusted && isProtocolDowngrade(deviceId, protocolVersion)) {
|
if (deviceTrusted && isProtocolDowngrade(deviceId, protocolVersion)) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user