Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
.env
node_modules
native/build
native/build_swift
native/node_modules
!.log
69 changes: 69 additions & 0 deletions native/binding.gyp
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
{
"targets": [{
"target_name": "node-corewlan",
"conditions": [
["OS==\"mac\"", {
"sources": [
"src/mac_wifi_addon.mm",
"src/MacWifiBridge.m"
],
"include_dirs": [
"<!@(node -p \"require('node-addon-api').include\")",
"include",
"build_swift"
],
"dependencies": [
"<!(node -p \"require('node-addon-api').gyp\")"
],
"libraries": [
"<(PRODUCT_DIR)/libMacWifi.dylib",
"-framework CoreWLAN"
],
"cflags!": ["-fno-exceptions"],
"cflags_cc!": ["-fno-exceptions"],
"xcode_settings": {
"GCC_ENABLE_CPP_EXCEPTIONS": "YES",
"CLANG_ENABLE_OBJC_ARC": "YES",
"SWIFT_OBJC_BRIDGING_HEADER": "include/MacWifiBridge.h",
"SWIFT_VERSION": "5.0",
"SWIFT_OBJC_INTERFACE_HEADER_NAME": "mac_wifi-Swift.h",
"MACOSX_DEPLOYMENT_TARGET": "11.0",
"OTHER_CFLAGS": ["-ObjC++", "-fobjc-arc"],
"OTHER_LDFLAGS": [
"-Wl,-rpath,@loader_path",
"-Wl,-install_name,@rpath/libMacWifi.dylib"
],
"HEADER_SEARCH_PATHS": [
"$(SRCROOT)/include",
"$(CONFIGURATION_BUILD_DIR)",
"$(SRCROOT)/build/Release",
"$(SRCROOT)/build_swift"
]
},
"actions": [
{
"action_name": "build_swift",
"inputs": ["src/MacWifi.swift"],
"outputs": [
"build_swift/libMacWifi.dylib",
"build_swift/mac_wifi-Swift.h"
],
"action": [
"sh", "-c",
"mkdir -p ./build_swift && swiftc src/MacWifi.swift -emit-objc-header-path ./build_swift/mac_wifi-Swift.h -emit-library -o ./build_swift/libMacWifi.dylib -emit-module -module-name mac_wifi -module-link-name MacWifi"
]
},
{
"action_name": "copy_swift_lib",
"inputs": ["<(module_root_dir)/build_swift/libMacWifi.dylib"],
"outputs": ["<(PRODUCT_DIR)/libMacWifi.dylib"],
"action": [
"sh", "-c",
"cp -f <(module_root_dir)/build_swift/libMacWifi.dylib <(PRODUCT_DIR)/libMacWifi.dylib && install_name_tool -id @rpath/libMacWifi.dylib <(PRODUCT_DIR)/libMacWifi.dylib"
]
}
]
}]
]
}]
}
11 changes: 11 additions & 0 deletions native/include/MacWifiBridge.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#ifndef MacWifiBridge_h
#define MacWifiBridge_h

#import <Foundation/Foundation.h>

@interface MacWifiBridge : NSObject
+ (NSArray * _Nullable)scanWithIface:(NSString * _Nullable)iface
error:(NSError * _Nullable * _Nullable)error;
@end

#endif
2 changes: 2 additions & 0 deletions native/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
const bindings = require('bindings');
module.exports = bindings('node-corewlan');
63 changes: 63 additions & 0 deletions native/src/MacWifi.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import Foundation
import CoreWLAN

@objc public class MacWifi: NSObject {
@objc public static func scan(iface: String?) throws -> NSArray {
let client = CWWiFiClient.shared()
let interface: CWInterface?

if let ifaceName = iface, !ifaceName.isEmpty {
interface = client.interface(withName: ifaceName)
} else {
interface = client.interface()
}

guard let wifiInterface = interface else {
throw NSError(
domain: "MacWifi", code: 1,
userInfo: [NSLocalizedDescriptionKey: "No Wi-Fi interface available"]
)
}

let networks = try wifiInterface.scanForNetworks(withSSID: nil)
return networks.map { net -> NSDictionary in
[
"ssid": net.ssid ?? NSNull(),
"bssid": net.bssid ?? NSNull(),
"rssi": net.rssiValue,
"noise": net.noiseMeasurement,
"channelNumber": net.wlanChannel?.channelNumber ?? NSNull(),
"channelBand": bandString(net.wlanChannel?.channelBand),
"security": securityList(net)
]
} as NSArray
}

private static func bandString(_ band: CWChannelBand?) -> String {
guard let band = band else { return "unknown" }
switch band {
case .band2GHz: return "2GHz"
case .band5GHz: return "5GHz"
case .band6GHz: return "6GHz"
default: return "unknown"
}
}

private static func securityList(_ net: CWNetwork) -> [String] {
let modes: [(CWSecurity, String)] = [
(.none, "none"),
(.WEP, "WEP"),
(.wpaPersonal, "WPA"),
(.wpaPersonalMixed, "WPA/WPA2"),
(.wpa2Personal, "WPA2"),
(.wpa3Personal, "WPA3"),
(.wpa3Transition, "WPA3-Transition"),
(.wpaEnterprise, "WPA-Enterprise"),
(.wpaEnterpriseMixed, "WPA/WPA2-Enterprise"),
(.wpa2Enterprise, "WPA2-Enterprise"),
(.wpa3Enterprise, "WPA3-Enterprise"),
(.dynamicWEP, "Dynamic-WEP")
]
return modes.compactMap { net.supportsSecurity($0.0) ? $0.1 : nil }
}
}
12 changes: 12 additions & 0 deletions native/src/MacWifiBridge.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#import "MacWifiBridge.h"
#import "mac_wifi-Swift.h"
#import <Foundation/Foundation.h>

@implementation MacWifiBridge

+ (NSArray * _Nullable)scanWithIface:(NSString * _Nullable)iface
error:(NSError * _Nullable * _Nullable)error {
return [MacWifi scanWithIface:iface error:error];
}

@end
96 changes: 96 additions & 0 deletions native/src/mac_wifi_addon.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
#import <Foundation/Foundation.h>
#import "MacWifiBridge.h"
#include <napi.h>

static Napi::String nsStr(Napi::Env env, id val) {
if ([val isKindOfClass:[NSString class]]) {
return Napi::String::New(env, [(NSString *)val UTF8String]);
}
return Napi::String::New(env, "");
}

class ScanWorker : public Napi::AsyncWorker {
public:
ScanWorker(Napi::Env env, Napi::Promise::Deferred deferred, std::string iface)
: Napi::AsyncWorker(env), deferred_(deferred), iface_(std::move(iface)) {}

void Execute() override {
@autoreleasepool {
NSString *ifaceStr = iface_.empty()
? nil
: [NSString stringWithUTF8String:iface_.c_str()];
NSError *error = nil;
networks_ = [MacWifiBridge scanWithIface:ifaceStr error:&error];
if (error) {
SetError(std::string([[error localizedDescription] UTF8String]));
}
}
}

void OnOK() override {
Napi::Env env = Env();
Napi::Array result = Napi::Array::New(env);

NSUInteger i = 0;
for (NSDictionary *net in networks_) {
Napi::Object obj = Napi::Object::New(env);

obj.Set("ssid", nsStr(env, net[@"ssid"]));
obj.Set("bssid", nsStr(env, net[@"bssid"]));
obj.Set("channelBand", nsStr(env, net[@"channelBand"]));

id rssi = net[@"rssi"];
obj.Set("rssi", Napi::Number::New(env, [rssi doubleValue]));

id noise = net[@"noise"];
obj.Set("noise", Napi::Number::New(env, [noise doubleValue]));

id ch = net[@"channelNumber"];
obj.Set("channelNumber", [ch isKindOfClass:[NSNumber class]]
? Napi::Number::New(env, [ch doubleValue])
: (Napi::Value)env.Null());

NSArray *security = net[@"security"];
Napi::Array secArr = Napi::Array::New(env, security.count);
for (NSUInteger j = 0; j < security.count; j++) {
secArr[j] = Napi::String::New(env, [security[j] UTF8String]);
}
obj.Set("security", secArr);

result[i++] = obj;
}

deferred_.Resolve(result);
}

void OnError(const Napi::Error &e) override {
deferred_.Reject(e.Value());
}

private:
Napi::Promise::Deferred deferred_;
std::string iface_;
NSArray *networks_ = nil;
};

Napi::Value Scan(const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(env);

std::string iface;
if (info.Length() > 0 && info[0].IsString()) {
iface = info[0].As<Napi::String>().Utf8Value();
}

auto *worker = new ScanWorker(env, deferred, std::move(iface));
worker->Queue();

return deferred.Promise();
}

static Napi::Object Init(Napi::Env env, Napi::Object exports) {
exports.Set("scan", Napi::Function::New(env, Scan));
return exports;
}

NODE_API_MODULE(node_corewlan, Init)
Loading