Files
umn/Sources/umn/main.swift
T
2026-08-31 16:44:54 -05:00

102 lines
4.3 KiB
Swift

import Foundation
import UltraMeshCore
let args = Array(CommandLine.arguments.dropFirst())
func usage() -> Never {
print("""
Ultra Mesh Network
umn status | address | peers | routes | interface status | ping <target>
umn alias set <name> <address> | remove <name> | list
umn firewall allow|revoke <overlay|tcp|udp> <port> --from <address|any> | list
umn text send <target> <port> <message> | listen <port>
umn expose <mesh-port> --to <host:port>
umn proxy [--listen 127.0.0.1:<port>]
""")
exit(2)
}
func parsePort(_ value: String) -> UInt16 {
guard let port = UInt16(value), port > 0 else { usage() }
return port
}
do {
guard let command = args.first else { usage() }
if command == "expose" {
guard args.count == 4, args[2] == "--to", let split = args[3].lastIndex(of: ":") else { usage() }
let host = String(args[3][..<split])
let localPort = parsePort(String(args[3][args[3].index(after: split)...]))
try runExpose(meshPort: parsePort(args[1]), host: host, localPort: localPort)
}
if command == "proxy" {
var listenPort: UInt16 = 1080
if args.count == 3, args[1] == "--listen", let split = args[2].lastIndex(of: ":") {
listenPort = parsePort(String(args[2][args[2].index(after: split)...]))
} else if args.count != 1 { usage() }
try runSOCKS(port: listenPort)
}
let daemon = try DaemonConnection()
var request: IPCMessage
switch command {
case "status": request = IPCMessage(operation: .status)
case "address": request = IPCMessage(operation: .address)
case "peers": request = IPCMessage(operation: .peers)
case "routes": request = IPCMessage(operation: .routes)
case "interface":
guard args.count == 2, args[1] == "status" else { usage() }
request = IPCMessage(operation: .interfaceStatus)
case "ping":
guard args.count == 2 else { usage() }
request = IPCMessage(operation: .ping); request.target = args[1]
case "alias":
guard args.count >= 2 else { usage() }
switch args[1] {
case "list": request = IPCMessage(operation: .aliasList)
case "set":
guard args.count == 4 else { usage() }
request = IPCMessage(operation: .aliasSet); request.name = args[2]; request.target = args[3]
case "remove":
guard args.count == 3 else { usage() }
request = IPCMessage(operation: .aliasRemove); request.name = args[2]
default: usage()
}
case "firewall":
guard args.count >= 2 else { usage() }
if args[1] == "list" { request = IPCMessage(operation: .firewallList) }
else {
guard args.count == 6, args[4] == "--from", ["allow", "revoke"].contains(args[1]),
let protocolKind = FirewallProtocol(rawValue: args[2]) else { usage() }
request = IPCMessage(operation: args[1] == "allow" ? .firewallAllow : .firewallRevoke)
request.firewallProtocol = protocolKind; request.port = parsePort(args[3]); request.source = args[5]
}
case "text":
guard args.count >= 3 else { usage() }
if args[1] == "listen" {
request = IPCMessage(operation: .bindText); request.port = parsePort(args[2])
guard printResponse(try daemon.request(request)) else { exit(1) }
while let event = try daemon.receive() {
if event.operation == .textSend, let data = event.data,
let text = String(data: data, encoding: .utf8) {
print("[\(event.source ?? "unknown")] \(text)")
}
}
throw UMNError.message("daemon disconnected")
} else if args[1] == "send" {
guard args.count >= 5 else { usage() }
request = IPCMessage(operation: .textSend); request.target = args[2]
request.port = parsePort(args[3]); request.data = Data(args.dropFirst(4).joined(separator: " ").utf8)
} else { usage() }
default: usage()
}
let result = try daemon.request(request)
exit(printResponse(result) ? 0 : 1)
} catch {
fputs("umn: \(error.localizedDescription)\n", stderr)
if (error as NSError).domain == NSPOSIXErrorDomain {
fputs("Is umnd running? Install it or start with `swift run umnd`.\n", stderr)
}
exit(1)
}