From 0adb857b2d0d5886c9cb65425df0787c6752cc69 Mon Sep 17 00:00:00 2001 From: Mygod Date: Wed, 5 May 2021 11:49:15 -0400 Subject: [PATCH] Migrate to better MAC lookup --- .../be/mygod/vpnhotspot/client/MacLookup.kt | 40 ++++++++----------- .../mygod/vpnhotspot/net/MacAddressCompat.kt | 2 + .../java/be/mygod/vpnhotspot/util/Utils.kt | 3 +- 3 files changed, 21 insertions(+), 24 deletions(-) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/client/MacLookup.kt b/mobile/src/main/java/be/mygod/vpnhotspot/client/MacLookup.kt index 4e89f9e4..e3802164 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/client/MacLookup.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/client/MacLookup.kt @@ -15,6 +15,7 @@ import kotlinx.coroutines.launch import org.json.JSONException import org.json.JSONObject import timber.log.Timber +import java.io.IOException import java.net.HttpURLConnection import java.net.URL @@ -24,13 +25,12 @@ import java.net.URL object MacLookup { class UnexpectedError(val mac: MacAddressCompat, val error: String) : JSONException("") { private fun formatMessage(context: Context) = - context.getString(R.string.clients_mac_lookup_unexpected_error, mac.toString(), error) + context.getString(R.string.clients_mac_lookup_unexpected_error, mac.toOui(), error) override val message get() = formatMessage(app.english) override fun getLocalizedMessage() = formatMessage(app) } private val macLookupBusy = mutableMapOf>() - private val countryCodeRegex = "([A-Z]{2})\\s*\$".toRegex() // http://en.wikipedia.org/wiki/ISO_3166-1 @MainThread fun abort(mac: MacAddressCompat) = macLookupBusy.remove(mac)?.let { (conn, job) -> @@ -41,40 +41,34 @@ object MacLookup { @MainThread fun perform(mac: MacAddressCompat, explicit: Boolean = false) { abort(mac) - val conn = URL("https://macvendors.co/api/$mac").openConnection() as HttpURLConnection + val conn = URL("https://api.maclookup.app/v2/macs/${mac.toOui()}").openConnection() as HttpURLConnection macLookupBusy[mac] = conn to GlobalScope.launch(Dispatchers.IO) { try { val response = conn.inputStream.bufferedReader().readText() - val obj = JSONObject(response).getJSONObject("result") - obj.opt("error")?.also { throw UnexpectedError(mac, it.toString()) } + val obj = JSONObject(response) + if (!obj.getBoolean("success")) throw UnexpectedError(mac, response) + if (!obj.getBoolean("found")) { + // no vendor found, we should not retry in the future + AppDatabase.instance.clientRecordDao.upsert(mac) { macLookupPending = false } + return@launch + } + val country = obj.getString("country") val company = obj.getString("company") - val match = extractCountry(mac, response, obj) - val result = if (match != null) { - String(match.groupValues[1].flatMap { listOf('\uD83C', it + 0xDDA5) }.toCharArray()) + ' ' + company - } else company + val result = if (country.length != 2) { + Timber.w(UnexpectedError(mac, response)) + company + } else String(country.flatMap { listOf('\uD83C', it + 0xDDA5) }.toCharArray()) + ' ' + company AppDatabase.instance.clientRecordDao.upsert(mac) { nickname = result macLookupPending = false } } catch (e: JSONException) { - if ((e as? UnexpectedError)?.error == "no result") { - // no vendor found, we should not retry in the future - AppDatabase.instance.clientRecordDao.upsert(mac) { macLookupPending = false } - } else Timber.w(e) + Timber.w(e) if (explicit) SmartSnackbar.make(e).show() - } catch (e: Throwable) { + } catch (e: IOException) { Timber.d(e) if (explicit) SmartSnackbar.make(e).show() } } } - - private fun extractCountry(mac: MacAddressCompat, response: String, obj: JSONObject): MatchResult? { - countryCodeRegex.matchEntire(obj.optString("country"))?.also { return it } - val address = obj.optString("address") - if (address.isBlank()) return null - countryCodeRegex.find(address)?.also { return it } - Timber.w(UnexpectedError(mac, response)) - return null - } } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/net/MacAddressCompat.kt b/mobile/src/main/java/be/mygod/vpnhotspot/net/MacAddressCompat.kt index 0b190c1c..97b12d83 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/net/MacAddressCompat.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/net/MacAddressCompat.kt @@ -96,4 +96,6 @@ value class MacAddressCompat(val addr: Long) { fun toPlatform() = MacAddress.fromBytes(toList().toByteArray()) override fun toString() = toList().joinToString(":") { "%02x".format(it) } + + fun toOui() = toList().joinToString("") { "%02x".format(it) }.substring(0, 9) } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/util/Utils.kt b/mobile/src/main/java/be/mygod/vpnhotspot/util/Utils.kt index c0a3a20b..7c11d717 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/util/Utils.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/util/Utils.kt @@ -86,7 +86,8 @@ fun makeIpSpan(ip: InetAddress) = ip.hostAddress.let { } } fun makeMacSpan(mac: String) = if (app.hasTouch) SpannableString(mac).apply { - setSpan(CustomTabsUrlSpan("https://macvendors.co/results/$mac"), 0, length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) + setSpan(CustomTabsUrlSpan("https://maclookup.app/search/result?mac=" + mac.substring(0, 13)), + 0, length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) } else mac fun NetworkInterface.formatAddresses(macOnly: Boolean = false) = SpannableStringBuilder().apply {