Coder Social home page Coder Social logo

netservice's Introduction

Swift NetService (Bonjour / Zeroconf / mDNS) implementation for Linux

Build Status

This module allows you to publish your own Bonjour service on the local network. On macOS NetService is included with Cocoa, however on Linux there's no such thing in the standard library.

Since version 0.5 this library uses dns_sd as the responder, instead of implementing mDNS itself. On macOS this means that it uses the system-wide daemon, and on Linux one should install libavahi-compat-libdnssd-dev to run the system-wide daemon.

Usage

See also NetService-Example. Note that like Apple's NetService, you need to run a RunLoop in order for the callbacks to happen.

There's also a command line tool included called dns-sd, with a subset of the functionality provided by Apple's tool with the same name. You can use this tool to verify the implementation and debug your network when advertisements are not working. Run swift run dns-sd --help for usage instructions.

Publish a NetService

This code will publish a new NetService advertising port 8000. Note that you need to setup a listening socket on port 8000 yourself.

import Foundation
import NetService

let service = NetService(domain: "local.", type: "_hap._tcp.", name: "Zithoek", port: 8000)
service.delegate = ...
service.publish()
withExtendedLifetime((service, delegate)) {
    RunLoop.main.run()
}

Browsing for NetServices

This code will start a search for the given service type.

let browser = NetServiceBrowser()
browser.delegate = ...
browser.searchForServices(ofType: "_airplay._tcp.", inDomain: "local.")
withExtendedLifetime((browser, delegate)) {
    RunLoop.main.run()
}

Credits

This library was written by Bouke Haarsma.

netservice's People

Contributors

bouke avatar voynovia avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

netservice's Issues

NetService publishing error -65540

I'm having a bit trouble running my service on Ubuntu 16.05 in a VM and Ubuntu Mate on Raspberry. It was working a couple of weeks ago. It runs okay on MacOS and iOS. No changes on the Linux side of the equation, and minimal changes (but some) to the subsystem in my framework, so that's probably the cause. These Apple NetService error codes are very opaque.

The didNotPublish delegate call is called and gives the following error code:

["NSNetServicesErrorCode": -65540, "NSNetServicesErrorDomain": 10]

Are you familiar with that one? As a reminder, I'm using the dns_sd branch.

Drop-in replacement for Apple's NetService?

Super-excited to find your project - it is exactly what I need. I'm adding support for Linux to an existing iOS project that already utilizes Apple's NetService framework. I was wondering if your framework utilizes the same interface as Apple's so that I could just drop it into my project. It looks very similar but it would be really helpful to know any divergence in advance.

Compilation error

Ran into the following after swift run. First, here's my Package.swift:


let package = Package(
    name: "Linux",
    products: [
        .library(
            name: "Linux",
            targets: ["Linux"]),
        ],
    dependencies: [
        .package(url: "https://github.com/IBM-Swift/BlueSocket.git",.upToNextMajor(from: "1.0.0")),
        .package(url: "https://github.com/Bouke/NetService.git",.upToNextMajor(from: "0.4.1"))
    ],
    targets: [
        .target(
            name: "Linux",
            dependencies: ["Socket", "NetService"]),
        .testTarget(
            name: "LinuxTests",
            dependencies: ["Linux"]),
    ]
)

Here's the error:

/home/robreuss/Development/VirtualMobileController/VirtualMobileController_Linux/.build/checkouts/NetService.git-469506449312911919/Sources/NetService/NetServiceBrowser.swift:100:35: error: value of type '[ResourceRecord]' has no member 'compactMap'
        let newPointers = message.answers
                          ~~~~~~~~^~~~~~~
/home/robreuss/Development/VirtualMobileController/VirtualMobileController_Linux/.build/checkouts/NetService.git-469506449312911919/Sources/NetService/Responder.swift:66:21: error: value of type '[Socket.Address]' has no member 'compactMap'
      hostRecords = addresses.compactMap { (address) -> HostRecord<IPv4>? in
                    ^~~~~~~~~ ~~~~~~~~~~
/home/robreuss/Development/VirtualMobileController/VirtualMobileController_Linux/.build/checkouts/NetService.git-469506449312911919/Sources/NetService/Responder.swift:74:22: error: value of type '[Socket.Address]' has no member 'compactMap'
      host6Records = addresses.compactMap { (address) -> HostRecord<IPv6>? in
                     ^~~~~~~~~ ~~~~~~~~~~
/home/robreuss/Development/VirtualMobileController/VirtualMobileController_Linux/.build/checkouts/NetService.git-469506449312911919/Sources/NetService/NetService.swift:291:18: error: value of type '[ResourceRecord]' has no member 'compactMap'
      if message.answers.compactMap({ $0 as? ServiceRecord }).contains(where: { $0.name == fqdn }) {
         ~~~~~~~~^~~~~~~ ~~~~~~~~~~
/home/robreuss/Development/VirtualMobileController/VirtualMobileController_Linux/.build/checkouts/NetService.git-469506449312911919/Sources/NetService/Utils.swift:185:10: error: value of type '[UnsafeMutablePointer<ifaddrs>]' has no member 'compactMap'
        .filter { Int($0.pointee.ifa_flags) & Int(IFF_LOOPBACK) == 0 }
~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: terminated(1): /home/robreuss/swift-4.0-RELEASE-ubuntu16.04/usr/bin/swift-build-tool -f /home/robreuss/Development/VirtualMobileController/VirtualMobileController_Linux/.build/debug.yaml Linux.exe

New info.plist requirement for browsing services: NSBonjourServices

After leaving my project alone for a year, upgraded to Xcode 12 and suddenly couldn't find my available service when browsing, which I confirmed was published and available. Received error -72800 missingRequiredConfigurationError and tracked down that there is a new requirement where you need to list all services you want to provide your user with access to under the key NSBonjourServices in info.plist. What a pain. The format for defining the services for that key are _myservice._tcp and _myservice._udp, depending on transport respectively.

Documention for NSBonjourServices key

If you know a way around this (I doubt that's possible) that would be great. I haven't tested this issue from the Linux side of the equation yet, and will update this issue when I do.

Linux-based client will not resolve service

I'm attempting to browse for a Bonjour service from a Linux client running on Raspbian Buster on a Raspberry Pi Zero W that is published by an iOS server . Although it finds the service, it does not resolve.

This delegate method is called:
public func netService(_ sender: NetService, didNotResolve errorDict: [String: NSNumber])

The error provided is:
["NSNetServicesErrorCode": -1, "NSNetServicesErrorDomain": 10]

I thought I had this Linux-based client browsing/resolution working under Linux about a year or two ago, although that was with Ubuntu Mate, and perhaps I made a configuration change to that install.

I used avahi-browse to look for the service and it is published:

`Server version: avahi 0.7; Host name: goose.local
E Ifce Prot Name Type Domain

  • wlan0 IPv6 RoverController _RemoteLogging._tcp local
  • wlan0 IPv4 RoverController _RemoteLogging._tcp local
    = wlan0 IPv6 RoverController _RemoteLogging._tcp local
    hostname = [Robs-iPad-Pro.local]
    address = [192.168.86.35]
    port = [63252]
    txt = []
    = wlan0 IPv4 RoverController _RemoteLogging._tcp local
    hostname = [Robs-iPad-Pro.local]
    address = [192.168.86.35]
    port = [63252]
    txt = []
    : Cache exhausted
    : All for now
    `

That same Linux host is itself running a service and iOS devices are able to connect to that no problem.

Thanks.

Browsing readiness

Is browsing functionality ready for use yet? I'm happy to test when you're ready!

Build failure when packaged with BlueSocket

I've run into a strange problem where if NetService and BlueSocket are both included as dependencies the build process hangs on the following line and brings down the whole OS:

'Cdns_sd' avahi-compat-libdns_sd.pc: warning: prohibited flag(s): -D_REENTRANT

If I modify the package to build with either of the two dependencies on their own, it builds just fine for each. It only crashes when building with both dependencies.

I've created a simple project to demonstrate the failur. You can comment/uncomment the individual dependencies to build with each dependency solo, and combine them to get the crash:

https://github.com/robreuss/CopyPackage.git

I'm running this on a Raspberry Pi 3b, Ubuntu 20.04.3, and Swift 5.5.

It builds just fine when running in a VM using VMWare under macOS ARM.

This problem is a major blocker for me so any help would be most appreciated! Thanks Bouke.

dns_sd branch does not build

I'm unable to get the "dns_sd" branch to build. Here's the error message. Thanks!

#include "shim.h"
         ^
/home/robreuss/Development/VirtualMobileController/VirtualMobileController_Linux/.build/checkouts/Cdns_sd.git--4743522453419668987/shim.h:1:10: error: 'dns_sd.h' file not found
#include <dns_sd.h>
         ^
/home/robreuss/Development/VirtualMobileController/VirtualMobileController_Linux/.build/checkouts/NetService.git--7099908149735996145/Sources/NetService/NetService.swift:13:8: error: could not build C module 'Cdns_sd'
import Cdns_sd
       ^
<module-includes>:1:10: note: in file included from <module-includes>:1:
#include "shim.h"
         ^
/home/robreuss/Development/VirtualMobileController/VirtualMobileController_Linux/.build/checkouts/Cdns_sd.git--4743522453419668987/shim.h:1:10: error: 'dns_sd.h' file not found
#include <dns_sd.h>
         ^
/home/robreuss/Development/VirtualMobileController/VirtualMobileController_Linux/.build/checkouts/NetService.git--7099908149735996145/Sources/NetService/NetService.swift:13:8: error: could not build C module 'Cdns_sd'
import Cdns_sd

SegFault on resolution of service address.

Hello,

I'm attempting to use the package but am finding that it crashes when one tries to resolve the address of a discovered service.

This is with libavahi-compat-libdnssd-dev. Version 0.8-5+deb11u2 on Raspberry Pi OS (Bullseye) and Swift v5.8 or v.5.9

See sample project here:
https://github.com/Diggory/NetServiceLinuxTest

Thread 0 “NetServiceLinux” crashed:
 0 NetService.didResolveAddress(host:port:textRecord:) + 252 in NetServiceLinuxTest at /home/pi/Swift/NetServiceLinuxTest/.build/checkouts/NetService/Sources/NetService/NetService.swift:489:23
   487│
   488│   fileprivate func didResolveAddress(host: String, port: UInt16, textRecord: Data?) {
   489│     self.hostName = host
   │            ▲
   490│     self.port = Int(port)
   491│     self.textRecord = textRecord
 1 closure #1 in variable initialization expression of _resolveReply + 983 in NetServiceLinuxTest at /home/pi/Swift/NetServiceLinuxTest/.build/checkouts/NetService/Sources/NetService/NetService.swift:45:13
  43│   let port = UInt16(bigEndian: port)
  44│   let textRecord = txtRecord.map { Data(bytes: $0, count: Int(txtLen)) }
  45│   service.didResolveAddress(
   │       ▲
  46│     host: hosttarget,
  47│     port: port,
 2 0x0000007fb4825af8 <unknown> in libdns_sd.so.1.0.0
 3 0x0000007fb3375a78 <unknown> in libavahi-client.so.3.2.9
 4 0x0000007fb33713d4 <unknown> in libavahi-client.so.3.2.9
 5 0x0000007fb2bc9e74 <unknown> in libdbus-1.so.3.19.17
 6 0x0000007fb3377c50 <unknown> in libavahi-client.so.3.2.9
 7 0x0000007fb3394ebc <unknown> in libavahi-common.so.3.5.4
 8 0x0000007fb4825fa8 <unknown> in libdns_sd.so.1.0.0
 9 NetService.processResult() + 75 in NetServiceLinuxTest at /home/pi/Swift/NetServiceLinuxTest/.build/checkouts/NetService/Sources/NetService/NetService.swift:512:9
   510│
   511│   fileprivate func processResult() {
   512│     DNSServiceProcessResult(serviceRef)
   │     ▲
   513│   }
   514│ }
10 closure #1 in variable initialization expression of _processResult + 375 in NetServiceLinuxTest at /home/pi/Swift/NetServiceLinuxTest/.build/checkouts/NetService/Sources/NetService/NetService.swift:53:13
  51│ private let _processResult: CFSocketCallBack = { (_, _, _, _, info) in
  52│   let service: NetService = Unmanaged.fromOpaque(info!).takeUnretainedValue()
  53│   service.processResult()
   │       ▲
  54│ }
  55│
11 0x0000007fb3ba545c __CFSocketPerformV0 + 1539 in libFoundation.so
12 0x0000007fb3ba27a8 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 23 in libFoundation.so
13 0x0000007fb3ba2404 __CFRunLoopDoSources0 + 359 in libFoundation.so
14 0x0000007fb3b9e1f8 __CFRunLoopRun + 671 in libFoundation.so
15 0x0000007fb3b9dcd8 CFRunLoopRunSpecific + 475 in libFoundation.so
16 0x0000007fb3afc2b0 RunLoop.run(mode:before:) + 243 in libFoundation.so
17 NetServiceLinuxTest_main + 323 in NetServiceLinuxTest at /home/pi/Swift/NetServiceLinuxTest/Sources/main.swift:16:33
  14│ let shouldKeepRunning = true
  15│ //   Run forever
  16│ while shouldKeepRunning == true &&
   │                 ▲
  17│         runLoop.run(mode:.default, before: distantFuture) {}
  18│
18 0x0000007fb33cce18 __libc_start_main + 231 in libc-2.31.so
Segmentation fault

Getting both IP address and port in netServiceDidResolveAddress

This is not directly related to your NetService framework but Apple's. Do you have a recipe for extracting the port number from the sender.addresses property returned by netServiceDidResolveAddress? This code is working to extract the address but I'm unable to modify it to get the port. Insofar as it doesn't account for address types, it's probably insufficient even for the IP address?

` func netServiceDidResolveAddress(_ sender: NetService) {

    var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
    guard let data = sender.addresses?.first else { return }
    do {
        try data.withUnsafeBytes { (pointer:UnsafePointer<sockaddr>) -> Void in
            guard getnameinfo(pointer, socklen_t(data.count), &hostname, socklen_t(hostname.count), nil, 0, NI_NUMERICHOST) == 0 else {
                throw NSError(domain: "domain", code: 0, userInfo: ["error":"unable to get ip address"])
            }
        }
    } catch {
        logError("Could not get service IP address: \(error)")
        print(error)
        return
    }
    let address = String(cString:hostname)
    logDebug("\(sender.name)  IP:\(address)")

}`

Packaging as a library with a branch dependency

I was able to get my library running as an executable on Ubuntu 16.04 referencing the "dns_sd" branch as a dependency. However, when I switched to packaging as a library for another executable to use my library as a dependency, the Package Manager complained about using a branch dependency within a library. It doesn't like using revision id numbers either.

I tried forking your NetService and merging "dns_sd" into master, but although things build, there's no service activity when running. Do you have any suggestions on how I might utilize the"dns_sd" branch in a library, because that is working great?

I realize this question is a bit off the track of NetService itself but I was hoping you might be able to help.

Unseen delegate protocols and stop() method

I have the module importing to my Raspberry Pi project no problem! But for some reason it is not recognizing references to NetServiceDelegate, NetServiceBrowser and NetServiceBrowserDelegate.

In perhaps a related issue, it does not see "stop()" on NetServiceBrowser, but I can see that method implemented in your source (even though I also see an issue from last year saying it requires implementation).

Importing "NetService" is the only module required, correct? Thanks!

Segmentation fault

I'm getting a seg fault and it's occurring in difficult-to-track ways, suggestive a background process getting into trouble.

Sometimes I get all the way through my browsing and connection process, and exchange data over TCP, but then the seg fault occurs. Other times it seems to occur around the time I call stopBrowsing. Another time, it occurred right after the browser found the same service twice and started to resolve.

My guess is that the last case where the same service was found twice may be the best clue.

Warning and no NetService callbacks after publishing service

When I publish a service on Ubuntu 16.04, I get the following warning message:

*** WARNING *** Please fix your application to use the native API of Avahi!
*** WARNING *** For more information see <http://0pointer.de/avahi-compat?s=libdns_sd&e=Linux>

Is that anything to be concerned about?

My client seems to be resolving successfully but the calls to my delegate are not made (they work on iOS and macOS). Any ideas? Thanks!

Re-implement in "pure" Swift?

This library originally did all the networking itself, but Swift support for calling POSIX and POSIX networking in itself was quite difficult to get right. Since 0.5 we're using avahi to provide a dns-sd implementation. Once Swift has great async/await support, a rewrite might be investigated.

Server publishes with multiple ip's

I post the service like this:

var serverDelegate: NetServiceDelegate?

  DispatchQueue.global(qos: .background).async {
            let service = NetService(domain: "local.", type: "_remote._tcp.", name: "MacRemote", port: 0)
            service.publish(options: [.listenForConnections])
                    service.schedule(in: .main, forMode: .defaultRunLoopMode)
            service.delegate = self.serverDelegate

            withExtendedLifetime((service, self.serverDelegate)) {
                RunLoop.main.run()

            }

        }

Browsing with Bonjour Browser mac app displays two services under "MacRemote"- one with my Mac's ip and another with an APIPA adress (169.254....). Is this normal ?
This is causing my client to find the APIPA ip instead of the normal IP.

Any advice is much appreciated.

Bouke NetService on Windows

Hi, now we have Swift working on Windows. I am curious of any experiments for adding Bouke NetService to Windows.
I see two possibilities:

1- Try with the legacy Apple Bonjour SDK for Windows. The SDK is old (2004) but provides the DLL and the dns_sd.h header.
Do you think this Apple dns_sd.h for Windows can simply replace the one you are using for Linux ?

https://developer.apple.com/download/all/?q=Bonjour%20SDK%20for%20Windows

2- Since now Microsoft support Bonjour native on Windows 10/11 with there own Win32 DNS API, we can design a Swift layer over the Microsoft API (windns.h). Longer way but native Win32 support. Any advantages over Apple legacy dns_sd for Windows ? Is anybody have tried this way ?

https://docs.microsoft.com/en-us/windows/win32/api/_dns/

Thanks to Bouke for helping us having a Swift NetService solution for macOS and Linux.
Looking forward for Windows support too.

Compile error on Linux

Hello,

I'm using HAP in a project which depends on NetService. When I compile my project on Linux,
I'm getting errors like:

/home/travis/build/MiCasa-HomeKit/MiCasaPlugin/.build/checkouts/NetService/Sources/NetService/NetServiceBrowser.swift:181:66: error: cannot find 'kCFSocketReadCallBack' in scope
372        socket = CFSocketCreateWithNative(nil, fd, CFOptionFlags(kCFSocketReadCallBack), _processResult, &context)

These errors occur in several files. You can find the full log at:
https://travis-ci.org/github/MiCasa-HomeKit/MiCasaPlugin/jobs/740481068

This is my .travis.yml file:
https://github.com/MiCasa-HomeKit/MiCasaPlugin/blob/feat-%231-setup-ci-cd-pipeline/.travis.yml

Best regards
Thomas

NetService not working on swift Docker Official Image

When trying NetService with NetService-example on the swift Docker Official Image, I am getting the following warning messages and NetService is not working (not publishing, nor browsing services):

root@docker-desktop:/singular/NetService-Example-master# .build/debug/NetService-Example
*** WARNING *** The program 'NetService-Example' uses the Apple Bonjour compatibility layer of Avahi.
*** WARNING *** Please fix your application to use the native API of Avahi!
*** WARNING *** For more information see http://0pointer.de/blog/projects/avahi-compat.html

Building and running NetService-example in Xcode, seems to be working fine.

Any idea on how to get NetService to work on the swift Docker Official Image?

Precondition failed: host name should have suffix .local

Hi Bouke, fellow Dutchie here. Came across your library and integrated it into my project to support Bonjour discovery on Linux. Fine piece of work!

While it works on OSX (well, with some glitches where the local mDNSResponder gets confused doesn't seem to be hearing the advertisements by NetService.. meanwhile I am seeing traffic with the dns-sd utility so I think your library works fine) and compiles on Linux, I get the following error:

precondition failed: host name 43ff16893f97. should have suffix .local: file /root/.build/checkouts/NetService.git--4183857386241951162/Sources/NetService/Responder.swift, line 6

This is where the error is thrown:

let hostname = try gethostname() + "."
        precondition(hostname.hasSuffix(".local."), "host name \(hostname) should have suffix .local")
        self.hostname = hostname

The cause being that gethostname() returns a host without any domain. Now this happens inside a Docker container (which doesn't appear to have a domain name associated). If this is not fixable, perhaps you should throw an exception here instead (as the error is really dependent on runtime configuration).

Condense response messages

Common suffixes in the DNS messages can be referenced from within the message. On unpacking this is already performed. On packing this should be done as well.

Not resolving (iOS client, Linux host)

Publishing service on Linux successfully, and iOS client is seeing the service and attempting to resolve, but nothing happens. Any way I can do some logging with your NetService to help diagnose? This occurs on both "dns_sd" and "master"

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.