Skip to content

Commit

Permalink
Merge pull request Tribler#13 from Tribler/nsd
Browse files Browse the repository at this point in the history
Network Service Discovery
  • Loading branch information
MattSkala authored Feb 25, 2020
2 parents b81e4de + 8d15b5a commit 1ed4760
Show file tree
Hide file tree
Showing 17 changed files with 297 additions and 31 deletions.
28 changes: 14 additions & 14 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,27 @@
This document describes long-term plans for the kotlin-ipv8 project.

## General
- tutorial: How to create an overlay
- tutorial: How to create a TrustChain-based overlay
- simplify API for IPv8 initialization
* [x] tutorial: How to create an overlay
* [x] tutorial: How to create a TrustChain-based overlay
* [x] simplify API for IPv8 initialization

## TrustChain
- block integrity validation
- py-ipv8 compatible transaction serialization
* [x] block integrity validation
* [x] py-ipv8 compatible transaction serialization

## Peer Discovery
- LAN discovery
- Bluetooth LE discovery
* [x] LAN discovery
* [ ] Bluetooth LE discovery

## Connectivity
- Bluetooth LE for nearby infrastructure-less connectivity
- relay/proxy community
- IPv6 support?
* [ ] Bluetooth LE for nearby infrastructure-less connectivity
* [ ] relay/proxy community
* [ ] IPv6 support?

## DHT
- DHTCommunity
- DHTDiscoveryCommunity
* [ ] DHTCommunity
* [ ] DHTDiscoveryCommunity

## Demos
- TrustChain Explorer
- PeerChat
* [x] TrustChain Explorer
* [ ] PeerChat
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ package nl.tudelft.ipv8.android.demo

import android.app.Application
import android.util.Log
import androidx.core.content.getSystemService
import androidx.preference.PreferenceManager
import com.squareup.sqldelight.android.AndroidSqliteDriver
import nl.tudelft.ipv8.*
import nl.tudelft.ipv8.android.IPv8Android
import nl.tudelft.ipv8.android.demo.service.DemoService
import nl.tudelft.ipv8.android.keyvault.AndroidCryptoProvider
import nl.tudelft.ipv8.android.peerdiscovery.NetworkServiceDiscovery
import nl.tudelft.ipv8.attestation.trustchain.*
import nl.tudelft.ipv8.attestation.trustchain.store.TrustChainSQLiteStore
import nl.tudelft.ipv8.attestation.trustchain.store.TrustChainStore
Expand Down Expand Up @@ -93,9 +95,10 @@ class DemoApplication : Application() {

private fun createDemoCommunity(): OverlayConfiguration<DemoCommunity> {
val randomWalk = RandomWalk.Factory()
val nsd = NetworkServiceDiscovery.Factory(getSystemService()!!)
return OverlayConfiguration(
Overlay.Factory(DemoCommunity::class.java),
listOf(randomWalk)
listOf(randomWalk, nsd)
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,12 @@ class DemoCommunity : Community() {

discoveredAddressesContacted[address] = Date()
}

override fun load() {
super.load()
}

override fun unload() {
super.unload()
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package nl.tudelft.ipv8.android.demo.ui.users

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
Expand Down
9 changes: 9 additions & 0 deletions ipv8-android/build.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'org.jlleitschuh.gradle.ktlint'

ktlint {
version = "$ktlint_version"
android = true
outputToConsole = true
ignoreFailures = true
}

android {
compileSdkVersion 29
buildToolsVersion "29.0.2"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,11 @@ import android.content.Intent
import android.net.ConnectivityManager
import android.os.Build
import androidx.core.content.getSystemService
import androidx.lifecycle.ProcessLifecycleOwner
import nl.tudelft.ipv8.IPv8
import nl.tudelft.ipv8.IPv8Configuration
import nl.tudelft.ipv8.android.keyvault.AndroidCryptoProvider
import nl.tudelft.ipv8.android.messaging.udp.AndroidUdpEndpoint
import nl.tudelft.ipv8.android.service.IPv8Service
import nl.tudelft.ipv8.keyvault.CryptoProvider
import nl.tudelft.ipv8.keyvault.PrivateKey
import nl.tudelft.ipv8.keyvault.defaultCryptoProvider
import java.net.InetAddress
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
package nl.tudelft.ipv8.android.peerdiscovery

import android.net.nsd.NsdManager
import android.net.nsd.NsdServiceInfo
import mu.KotlinLogging
import nl.tudelft.ipv8.Address
import nl.tudelft.ipv8.Overlay
import nl.tudelft.ipv8.messaging.Packet
import nl.tudelft.ipv8.messaging.udp.UdpEndpoint
import nl.tudelft.ipv8.peerdiscovery.strategy.DiscoveryStrategy
import kotlin.random.Random

private val logger = KotlinLogging.logger {}

/**
* A strategy to perform network service discovery (NSD) on LAN without using a bootstrap server.
*/
class NetworkServiceDiscovery(
private val nsdManager: NsdManager,
private val overlay: Overlay
) : DiscoveryStrategy {
private var serviceName: String? = null

private val registrationListener = object : NsdManager.RegistrationListener {
override fun onServiceRegistered(serviceInfo: NsdServiceInfo) {
// Save the service name. Android may have changed it in order to
// resolve a conflict, so update the name you initially requested
// with the name Android actually used.
logger.info { "Service registered: $serviceInfo" }
serviceName = serviceInfo.serviceName
}

override fun onRegistrationFailed(serviceInfo: NsdServiceInfo, errorCode: Int) {
// Registration failed! Put debugging code here to determine why.
logger.error { "Service registration failed: $errorCode" }
}

override fun onServiceUnregistered(serviceInfo: NsdServiceInfo) {
// Service has been unregistered. This only happens when you call
// NsdManager.unregisterService() and pass in this listener.
logger.info { "Service unregistered: $serviceInfo" }
}

override fun onUnregistrationFailed(serviceInfo: NsdServiceInfo, errorCode: Int) {
// Unregistration failed. Put debugging code here to determine why.
logger.error { "Service unregistration failed: $errorCode" }
}
}

// Instantiate a new DiscoveryListener
private val discoveryListener = object : NsdManager.DiscoveryListener {

// Called as soon as service discovery begins.
override fun onDiscoveryStarted(regType: String) {
logger.debug { "Service discovery started" }
}

override fun onServiceFound(serviceInfo: NsdServiceInfo) {
// A service was found! Do something with it.
logger.debug { "Service found: $serviceInfo" }

if (serviceInfo.serviceType == SERVICE_TYPE) {
// This is IPv8 service

if (serviceInfo.serviceName == serviceName) {
logger.debug { "Found its own service" }
}

val serviceId = getServiceId(serviceInfo.serviceName)

logger.debug { "Service ID: $serviceId" }

if (serviceId == overlay.serviceId) {
nsdManager.resolveService(serviceInfo, createResolveListener())
}
}
}

override fun onServiceLost(service: NsdServiceInfo) {
// When the network service is no longer available.
// Internal bookkeeping code goes here.
logger.debug { "Service lost: $service" }
}

override fun onDiscoveryStopped(serviceType: String) {
logger.debug("Discovery stopped: $serviceType")
}

override fun onStartDiscoveryFailed(serviceType: String, errorCode: Int) {
logger.error("Discovery failed: $errorCode")
nsdManager.stopServiceDiscovery(this)
}

override fun onStopDiscoveryFailed(serviceType: String, errorCode: Int) {
logger.error { "Discovery failed: $errorCode" }
nsdManager.stopServiceDiscovery(this)
}
}

private fun createResolveListener(): NsdManager.ResolveListener {
return object : NsdManager.ResolveListener {

override fun onResolveFailed(serviceInfo: NsdServiceInfo, errorCode: Int) {
// Called when the resolve fails. Use the error code to debug.
logger.error("Resolve failed: $errorCode")
}

override fun onServiceResolved(serviceInfo: NsdServiceInfo) {
logger.info("Service resolved: $serviceInfo")

val peer = overlay.myPeer
val address = Address(serviceInfo.host.hostAddress, serviceInfo.port)

if (overlay.myEstimatedLan != address) {
logger.debug { "Discovered address: $address" }
overlay.network.discoverAddress(peer, address, overlay.serviceId)
} else {
logger.debug { "Resolved its own IP address" }
}
}
}
}

private fun registerService(port: Int, serviceName: String) {
val serviceInfo = NsdServiceInfo()
// The name is subject to change based on conflicts
// with other services advertised on the same network.
serviceInfo.serviceName = serviceName
serviceInfo.serviceType = SERVICE_TYPE
serviceInfo.port = port

logger.debug { "Registering service info $serviceInfo" }

nsdManager.registerService(serviceInfo, NsdManager.PROTOCOL_DNS_SD, registrationListener)
}

private fun discoverServices() {
nsdManager.discoverServices(SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, discoveryListener)
}

private fun unregisterService() {
nsdManager.unregisterService(registrationListener)
}

private fun stopServiceDiscovery() {
nsdManager.stopServiceDiscovery(discoveryListener)
}

override fun load() {
logger.debug { "NetworkServiceDiscovery load" }

val endpoint = overlay.endpoint
if (endpoint is UdpEndpoint) {
val socketPort = endpoint.getSocketPort()
val serviceName = overlay.serviceId + "_" + Random.nextInt(10000)
logger.debug { "Registering service $serviceName on port $socketPort" }
registerService(socketPort, serviceName)
} else {
logger.error { "Overlay endpoint is not UdpEndpoint" }
}
discoverServices()
}

override fun takeStep() {
val addresses = overlay.getWalkableAddresses()
if (addresses.isNotEmpty()) {
val address = addresses.random()
overlay.walkTo(address)
}
}

override fun unload() {
logger.debug { "NetworkServiceDiscovery unload" }
unregisterService()
stopServiceDiscovery()
}

/**
* The service ID are the first 40 characters of the service name. Returns null if the service
* name name is invalid.
*/
private fun getServiceId(serviceName: String): String? {
// Service ID string length is two times its size in bytes as it is hexadecimal encoded
val serviceIdLength = Packet.SERVICE_ID_SIZE * 2
if (serviceName.length < serviceIdLength) return null

val serviceId = serviceName.substring(0, serviceIdLength)
for (char in serviceId) {
if (!char.isDigit() && char !in 'a'..'f') {
return null
}
}

return serviceId
}

companion object {
private const val SERVICE_TYPE = "_ipv8._udp."
}

class Factory(
private val nsdManager: NsdManager
) : DiscoveryStrategy.Factory<NetworkServiceDiscovery>() {
override fun create(): NetworkServiceDiscovery {
return NetworkServiceDiscovery(nsdManager, getOverlay())
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,8 @@ import android.app.NotificationManager
import android.app.PendingIntent
import android.app.Service
import android.content.Intent
import android.os.Binder
import android.os.Build
import android.os.IBinder
import android.util.Log
import androidx.core.app.NotificationCompat
import androidx.core.content.getSystemService
import androidx.lifecycle.Lifecycle
Expand All @@ -19,6 +17,7 @@ import kotlinx.coroutines.*
import nl.tudelft.ipv8.*
import nl.tudelft.ipv8.android.IPv8Android
import nl.tudelft.ipv8.android.R
import kotlin.system.exitProcess

open class IPv8Service : Service(), LifecycleObserver {
private val scope = CoroutineScope(Dispatchers.Default)
Expand Down Expand Up @@ -49,6 +48,9 @@ open class IPv8Service : Service(), LifecycleObserver {
.removeObserver(this)

super.onDestroy()

// We need to kill the app as IPv8 is started in Application.onCreate
exitProcess(0)
}

@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,10 @@ import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import nl.tudelft.ipv8.android.IPv8Android
import kotlin.system.exitProcess

class StopIPv8Receiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val serviceIntent = Intent(context, IPv8Android.serviceClass)
context.stopService(serviceIntent)
exitProcess(0)
}
}
6 changes: 3 additions & 3 deletions ipv8/src/main/java/nl/tudelft/ipv8/Community.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ abstract class Community : Overlay {
protected val prefix: ByteArray
get() = ByteArray(0) + 0.toByte() + VERSION + serviceId.hexToBytes()

var myEstimatedWan: Address = Address.EMPTY
var myEstimatedLan: Address = Address.EMPTY
override var myEstimatedWan: Address = Address.EMPTY
override var myEstimatedLan: Address = Address.EMPTY

private var lastBootstrap: Date? = null

Expand Down Expand Up @@ -432,7 +432,7 @@ abstract class Community : Overlay {
}

companion object {
val DEFAULT_ADDRESSES = listOf(
val DEFAULT_ADDRESSES = listOf<Address>(
// Dispersy
// Address("130.161.119.206", 6421),
// Address("130.161.119.206", 6422),
Expand Down
Loading

0 comments on commit 1ed4760

Please sign in to comment.