Closed Viper-Bit closed 1 year ago
Hi @Viper-Bit ,
Per the WS-Discovery spec - https://en.wikipedia.org/wiki/WS-Discovery#:~:text=Web%20Services%20Dynamic%20Discovery%20(WS,255.250%20or%20FF02%3A%3AC.
"Web Services Dynamic Discovery (WS-Discovery) is a technical specification that defines a multicast discovery protocol to locate services on a local network. It operates over TCP and UDP port 3702 and uses IP multicast address 239.255.255.250 or FF02::C. As the name suggests, the actual communication between nodes is done using web services standards, notably SOAP-over-UDP."
Are you running the code on Windows, maybe the Windows firewall is blocking the connection. I don't think this is a code related issue.
@faithoflifedev thx for answer, with disabled firewall still get same result i think dart have a problem to create dgram socket on windows with virtual adapters, i wrote a c++ WS-Discovery and use it as a dll in dart in same pc and everything works like charm. and found similar issue in flutter repo #53477
Thanks for the update @Viper-Bit, I tried the work-arounds suggested in #53477 and none worked for me on Windows 11.
Are you able to provide your c++ code and I can look at incorporating it into this package until a better solution is available?
@faithoflifedev yes OfCourse,
onvif.cpp
#include "onvif.h"
char preferred_network_address[16];
int setSocketOptions(int socket) {
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 500000;
int broadcast = 500;
char loopch = 0;
int status = 0;
struct in_addr localInterface;
#ifdef _WIN32
PMIB_IPADDRTABLE pIPAddrTable;
DWORD dwSize = 0;
DWORD dwRetVal = 0;
IN_ADDR IPAddr;
pIPAddrTable = (MIB_IPADDRTABLE*)malloc(sizeof(MIB_IPADDRTABLE));
if (pIPAddrTable) {
if (GetIpAddrTable(pIPAddrTable, &dwSize, 0) == ERROR_INSUFFICIENT_BUFFER) {
free(pIPAddrTable);
pIPAddrTable = (MIB_IPADDRTABLE*)malloc(dwSize);
}
if (pIPAddrTable == NULL) {
printf("Memory allocation failed for GetIpAddrTable\n");
return -1;
}
}
if ((dwRetVal = GetIpAddrTable(pIPAddrTable, &dwSize, 0)) != NO_ERROR) {
printf("GetIpAddrTable failed with error %d\n", dwRetVal);
return -1;
}
int p = 0;
while (p < (int)pIPAddrTable->dwNumEntries) {
IPAddr.S_un.S_addr = (u_long)pIPAddrTable->table[p].dwAddr;
IPAddr.S_un.S_addr = (u_long)pIPAddrTable->table[p].dwMask;
if (pIPAddrTable->table[p].dwAddr != inet_addr("127.0.0.1") && pIPAddrTable->table[p].dwMask == inet_addr("255.255.255.0")) {
if (strlen(preferred_network_address) > 0) {
localInterface.s_addr = inet_addr(preferred_network_address);
}
else {
localInterface.s_addr = pIPAddrTable->table[p].dwAddr;
}
status = setsockopt(socket, IPPROTO_IP, IP_MULTICAST_IF, (const char*)&localInterface, sizeof(localInterface));
if (status < 0)
printf("ip_multicast_if error");
p = (int)pIPAddrTable->dwNumEntries;
}
p++;
}
if (pIPAddrTable) {
free(pIPAddrTable);
pIPAddrTable = NULL;
}
status = setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (const char*)&broadcast, sizeof(broadcast));
#else
if (strlen(preferred_network_address) > 0) {
localInterface.s_addr = inet_addr(preferred_network_address);
status = setsockopt(socket, IPPROTO_IP, IP_MULTICAST_IF, (const char*)&localInterface, sizeof(localInterface));
if (status < 0)
printf("ip_multicast_if error");
}
status = setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (struct timeval*)&tv, sizeof(struct timeval));
#endif
status = setsockopt(socket, IPPROTO_IP, IP_MULTICAST_LOOP, (char*)&loopch, sizeof(loopch));
return 0;
}
int discovery(OnvifDiscoveryData* data, const char * probeMessage, int duration) {
#ifdef _WIN32
WSADATA wsaData;
int wsaStartup = WSAStartup(MAKEWORD(2, 2), &wsaData);
#endif
sockaddr_in broadcast_address = {};
int broadcast_message_length = strlen(probeMessage);
int broadcast_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
setSocketOptions(broadcast_socket);
memset((char*)&broadcast_address, 0, sizeof(broadcast_address));
broadcast_address.sin_family = AF_INET;
broadcast_address.sin_port = htons(3702);
broadcast_address.sin_addr.s_addr = inet_addr("239.255.255.250");
int status = sendto(broadcast_socket, probeMessage, broadcast_message_length, 0, (struct sockaddr*)&broadcast_address, sizeof(broadcast_address));
if (status < 0) {
//error
}
std::this_thread::sleep_for(std::chrono::milliseconds(duration));
int i = 0;
bool loop = true;
socklen_t address_size = sizeof(broadcast_address);
while (loop) {
int len = recvfrom(broadcast_socket, data->buf[i], sizeof(data->buf[i]), 0, (struct sockaddr*)&broadcast_address, &address_size);
if (len > 0) {
i++;
}
else {
loop = false;
if (len < 0) {
//error
}
}
}
#ifdef _WIN32
closesocket(broadcast_socket);
WSACleanup();
#else
close(broadcast_socket);
#endif
return i;
}
onvif.h
#ifndef ONVIF_H
#define ONVIF_H
#include <chrono>
#include <thread>
#include <cstring>
#ifdef _WIN32
#define LIBRARY_API __declspec(dllexport)
#include <ws2tcpip.h>
#include <iphlpapi.h>
#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "iphlpapi.lib")
#else
#define LIBRARY_API
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#endif
#ifdef __MINGW32__
#include <ws2tcpip.h>
#endif
#pragma pack (push, 1)
struct OnvifDiscoveryData {
char buf[128][8192];
};
#pragma pack(pop)
#ifdef __cplusplus
extern "C" {
#endif
LIBRARY_API int discovery(OnvifDiscoveryData* data, const char* probeMessage, int duration);
#ifdef __cplusplus
}
#endif
#endif
and dart side is:
import 'dart:ffi';
import 'dart:io';
import 'package:ffi/ffi.dart';
final Pointer<T> Function<T extends NativeType>(String symbolName) _lookup =
() {
if (Platform.isWindows) {
return DynamicLibrary.open('onvif.dll').lookup;
} else if (Platform.isLinux) {
return DynamicLibrary.open('/usr/local/lib/libonvif.so').lookup;
} else {
throw UnimplementedError();
}
}();
final _discoveryPtr = _lookup<
NativeFunction<
Int32 Function(
Pointer<NativeType>,
Pointer<NativeType>,
Int32,
)>>('discovery');
final _discovery = _discoveryPtr.asFunction<
int Function(
Pointer<NativeType>,
Pointer<NativeType>,
int,
)>();
@Packed(1)
sealed class _OnvifDiscoveryData extends Struct {
@Array<Int8>(128, 8192)
external Array<Array<Int8>> buf;
}
const _probeMessage = '''
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://schemas.xmlsoap.org/ws/2004/08/addressing">
<SOAP-ENV:Header>
<a:Action SOAP-ENV:mustUnderstand="1">http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe</a:Action>
<a:MessageID>urn:uuid:2809d092-cb6c-476a-9a6f-7ee0123265d3</a:MessageID>
<a:ReplyTo>
<a:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address>
</a:ReplyTo>
<a:To SOAP-ENV:mustUnderstand="1">urn:schemas-xmlsoap-org:ws:2005:04:discovery</a:To>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<p:Probe xmlns:p="http://schemas.xmlsoap.org/ws/2005/04/discovery">
<d:Types xmlns:d="http://schemas.xmlsoap.org/ws/2005/04/discovery" xmlns:dp0="http://www.onvif.org/ver10/network/wsdl">dp0:NetworkVideoTransmitter</d:Types>
</p:Probe>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
''';
Future<void> discovery([Duration duration = const Duration(seconds: 5)]) async {
final data = calloc<_OnvifDiscoveryData>();
final probeMessageData = _probeMessage.toNativeUtf8();
final devices = _discovery(data, probeMessageData, duration.inMilliseconds);
//for flutter _discovery cant be called in main thread (locks main thread for duration) so _discovery must be called from isolate
/*
final devices = await compute(
(msg) {
return _discovery(
Pointer.fromAddress(msg['dataAddress'] as int),
Pointer.fromAddress(msg['probeAddress'] as int),
msg['duration'] as int,
);
},
{
'dataAddress': data.address,
'probeAddress': probeMessageData.address,
'duration': duration.inMilliseconds,
},
);
*/
print('Found $devices Devices');
for (var index = 0; index < devices; index++) {
print(Pointer.fromAddress(data.address + index * 8192)
.cast<Utf8>()
.toDartString());
}
malloc.free(probeMessageData);
calloc.free(data);
}
Hi @Viper-Bit , as an update on this, I'm currently readying a new release with the above workaround for Windows OS included. It should be available in a day or two. Thanks for your help with this.
Hi @Viper-Bit , I've just published the new package v2.1.3+1.
Please let me know if this resolves the issue for you, or if you need a better explanation on how to use the fix (see the known issues
section of the README).
hi, i use this code for device descovery:
but when i run it my program crashs with bellow error