boB Rudis
4 years ago
13 changed files with 1631 additions and 12 deletions
@ -0,0 +1,2 @@ |
|||||
|
YEAR: 2020 |
||||
|
COPYRIGHT HOLDER: Bob Rudis |
@ -0,0 +1,21 @@ |
|||||
|
# MIT License |
||||
|
|
||||
|
Copyright (c) 2020 Bob Rudis |
||||
|
|
||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
|
of this software and associated documentation files (the "Software"), to deal |
||||
|
in the Software without restriction, including without limitation the rights |
||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
|
copies of the Software, and to permit persons to whom the Software is |
||||
|
furnished to do so, subject to the following conditions: |
||||
|
|
||||
|
The above copyright notice and this permission notice shall be included in all |
||||
|
copies or substantial portions of the Software. |
||||
|
|
||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
|
SOFTWARE. |
@ -1,4 +1,6 @@ |
|||||
# Generated by roxygen2: do not edit by hand |
# Generated by roxygen2: do not edit by hand |
||||
|
|
||||
import(httr) |
export(int_bnjr_discover) |
||||
importFrom(jsonlite,fromJSON) |
export(int_bnjr_query_send) |
||||
|
importFrom(Rcpp,sourceCpp) |
||||
|
useDynLib(bonjour, .registration = TRUE) |
||||
|
@ -0,0 +1,13 @@ |
|||||
|
# Generated by using Rcpp::compileAttributes() -> do not edit by hand |
||||
|
# Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393 |
||||
|
|
||||
|
#' @export |
||||
|
int_bnjr_discover <- function() { |
||||
|
.Call(`_bonjour_int_bnjr_discover`) |
||||
|
} |
||||
|
|
||||
|
#' @export |
||||
|
int_bnjr_query_send <- function(svc) { |
||||
|
.Call(`_bonjour_int_bnjr_query_send`, svc) |
||||
|
} |
||||
|
|
@ -1,9 +1,11 @@ |
|||||
#' ... |
#' ... |
||||
#' |
#' |
||||
#' @md |
#' @md |
||||
#' @name bonjour |
#' @name bonjour |
||||
#' @keywords internal |
#' @keywords internal |
||||
#' @author Bob Rudis (bob@@rud.is) |
#' @author Bob Rudis (bob@@rud.is) |
||||
#' @import httr |
## usethis namespace: start |
||||
#' @importFrom jsonlite fromJSON |
#' @importFrom Rcpp sourceCpp |
||||
|
#' @useDynLib bonjour, .registration = TRUE |
||||
|
## usethis namespace: end |
||||
"_PACKAGE" |
"_PACKAGE" |
||||
|
@ -0,0 +1,4 @@ |
|||||
|
|
||||
|
# Placeholder with simple test |
||||
|
expect_equal(1 + 1, 2) |
||||
|
|
@ -0,0 +1,3 @@ |
|||||
|
*.o |
||||
|
*.so |
||||
|
*.dll |
@ -0,0 +1,39 @@ |
|||||
|
// Generated by using Rcpp::compileAttributes() -> do not edit by hand
|
||||
|
// Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393
|
||||
|
|
||||
|
#include <Rcpp.h> |
||||
|
|
||||
|
using namespace Rcpp; |
||||
|
|
||||
|
// int_bnjr_discover
|
||||
|
List int_bnjr_discover(); |
||||
|
RcppExport SEXP _bonjour_int_bnjr_discover() { |
||||
|
BEGIN_RCPP |
||||
|
Rcpp::RObject rcpp_result_gen; |
||||
|
Rcpp::RNGScope rcpp_rngScope_gen; |
||||
|
rcpp_result_gen = Rcpp::wrap(int_bnjr_discover()); |
||||
|
return rcpp_result_gen; |
||||
|
END_RCPP |
||||
|
} |
||||
|
// int_bnjr_query_send
|
||||
|
List int_bnjr_query_send(std::string svc); |
||||
|
RcppExport SEXP _bonjour_int_bnjr_query_send(SEXP svcSEXP) { |
||||
|
BEGIN_RCPP |
||||
|
Rcpp::RObject rcpp_result_gen; |
||||
|
Rcpp::RNGScope rcpp_rngScope_gen; |
||||
|
Rcpp::traits::input_parameter< std::string >::type svc(svcSEXP); |
||||
|
rcpp_result_gen = Rcpp::wrap(int_bnjr_query_send(svc)); |
||||
|
return rcpp_result_gen; |
||||
|
END_RCPP |
||||
|
} |
||||
|
|
||||
|
static const R_CallMethodDef CallEntries[] = { |
||||
|
{"_bonjour_int_bnjr_discover", (DL_FUNC) &_bonjour_int_bnjr_discover, 0}, |
||||
|
{"_bonjour_int_bnjr_query_send", (DL_FUNC) &_bonjour_int_bnjr_query_send, 1}, |
||||
|
{NULL, NULL, 0} |
||||
|
}; |
||||
|
|
||||
|
RcppExport void R_init_bonjour(DllInfo *dll) { |
||||
|
R_registerRoutines(dll, NULL, CallEntries, NULL, NULL); |
||||
|
R_useDynamicSymbols(dll, FALSE); |
||||
|
} |
@ -0,0 +1,349 @@ |
|||||
|
#include <Rcpp.h> |
||||
|
#include <stdio.h> |
||||
|
#include <errno.h> |
||||
|
#include "mdns.h" |
||||
|
|
||||
|
using namespace Rcpp; |
||||
|
|
||||
|
#ifdef _WIN32 |
||||
|
# include <iphlpapi.h> |
||||
|
# define sleep(x) Sleep(x * 1000) |
||||
|
#else |
||||
|
# include <netdb.h> |
||||
|
#endif |
||||
|
|
||||
|
static char addrbuffer[64]; |
||||
|
static char namebuffer[256]; |
||||
|
static char sendbuffer[256]; |
||||
|
static mdns_record_txt_t txtbuffer[128]; |
||||
|
|
||||
|
static mdns_string_t |
||||
|
ipv4_address_to_string(char* buffer, size_t capacity, const struct sockaddr_in* addr, size_t addrlen) { |
||||
|
char host[NI_MAXHOST] = {0}; |
||||
|
char service[NI_MAXSERV] = {0}; |
||||
|
int ret = getnameinfo((const struct sockaddr*)addr, addrlen, |
||||
|
host, NI_MAXHOST, service, NI_MAXSERV, |
||||
|
NI_NUMERICSERV | NI_NUMERICHOST); |
||||
|
int len = 0; |
||||
|
if (ret == 0) { |
||||
|
if (addr->sin_port != 0) |
||||
|
len = snprintf(buffer, capacity, "%s:%s", host, service); |
||||
|
else |
||||
|
len = snprintf(buffer, capacity, "%s", host); |
||||
|
} |
||||
|
if (len >= (int)capacity) |
||||
|
len = (int)capacity - 1; |
||||
|
mdns_string_t str = {buffer, len}; |
||||
|
return str; |
||||
|
} |
||||
|
|
||||
|
static mdns_string_t |
||||
|
ipv6_address_to_string(char* buffer, size_t capacity, const struct sockaddr_in6* addr, size_t addrlen) { |
||||
|
char host[NI_MAXHOST] = {0}; |
||||
|
char service[NI_MAXSERV] = {0}; |
||||
|
int ret = getnameinfo((const struct sockaddr*)addr, addrlen, |
||||
|
host, NI_MAXHOST, service, NI_MAXSERV, |
||||
|
NI_NUMERICSERV | NI_NUMERICHOST); |
||||
|
int len = 0; |
||||
|
if (ret == 0) { |
||||
|
if (addr->sin6_port != 0) |
||||
|
len = snprintf(buffer, capacity, "[%s]:%s", host, service); |
||||
|
else |
||||
|
len = snprintf(buffer, capacity, "%s", host); |
||||
|
} |
||||
|
if (len >= (int)capacity) |
||||
|
len = (int)capacity - 1; |
||||
|
mdns_string_t str = {buffer, len}; |
||||
|
return str; |
||||
|
} |
||||
|
|
||||
|
static mdns_string_t |
||||
|
ip_address_to_string(char* buffer, size_t capacity, const struct sockaddr* addr, size_t addrlen) { |
||||
|
if (addr->sa_family == AF_INET6) |
||||
|
return ipv6_address_to_string(buffer, capacity, (const struct sockaddr_in6*)addr, addrlen); |
||||
|
return ipv4_address_to_string(buffer, capacity, (const struct sockaddr_in*)addr, addrlen); |
||||
|
} |
||||
|
|
||||
|
static int query_callback(int sock, const struct sockaddr* from, size_t addrlen, |
||||
|
mdns_entry_type_t entry, uint16_t transaction_id, |
||||
|
uint16_t rtype, uint16_t rclass, uint32_t ttl, |
||||
|
const void* data, size_t size, size_t offset, size_t length, |
||||
|
void* user_data) { |
||||
|
|
||||
|
List *l = static_cast<List *>(user_data); |
||||
|
|
||||
|
mdns_string_t fromaddrstr = ip_address_to_string(addrbuffer, sizeof(addrbuffer), from, addrlen); |
||||
|
|
||||
|
const char* entrytype = (entry == MDNS_ENTRYTYPE_ANSWER) ? "answer" : |
||||
|
((entry == MDNS_ENTRYTYPE_AUTHORITY) ? "authority" : "additional"); |
||||
|
|
||||
|
if (rtype == MDNS_RECORDTYPE_PTR) { |
||||
|
|
||||
|
mdns_string_t namestr = mdns_record_parse_ptr(data, size, offset, length, |
||||
|
namebuffer, sizeof(namebuffer)); |
||||
|
|
||||
|
List ptr_l = List::create( |
||||
|
_["from"] = String(fromaddrstr.str), |
||||
|
_["name"] = String(namestr.str), |
||||
|
_["type"] = String("PTR"), |
||||
|
_["entry_type"] = String(entrytype), |
||||
|
_["rclass"] = rclass, |
||||
|
_["ttl"] = ttl, |
||||
|
_["length"] = length |
||||
|
); |
||||
|
|
||||
|
l->push_back(ptr_l); |
||||
|
|
||||
|
printf("%.*s : %s PTR %.*s rclass 0x%x ttl %u length %d\n", |
||||
|
MDNS_STRING_FORMAT(fromaddrstr), entrytype, |
||||
|
MDNS_STRING_FORMAT(namestr), rclass, ttl, (int)length); |
||||
|
|
||||
|
} else if (rtype == MDNS_RECORDTYPE_SRV) { |
||||
|
|
||||
|
mdns_record_srv_t srv = mdns_record_parse_srv(data, size, offset, length, |
||||
|
namebuffer, sizeof(namebuffer)); |
||||
|
|
||||
|
List srv_l = List::create( |
||||
|
_["from"] = String(fromaddrstr.str), |
||||
|
_["type"] = String("SRV"), |
||||
|
_["entry_type"] = String(entrytype), |
||||
|
_["priority"] = srv.priority, |
||||
|
_["weight"] = srv.weight, |
||||
|
_["port"] = srv.port |
||||
|
); |
||||
|
|
||||
|
l->push_back(srv_l); |
||||
|
|
||||
|
printf("%.*s : %s SRV %.*s priority %d weight %d port %d\n", |
||||
|
MDNS_STRING_FORMAT(fromaddrstr), entrytype, |
||||
|
MDNS_STRING_FORMAT(srv.name), srv.priority, srv.weight, srv.port); |
||||
|
|
||||
|
} else if (rtype == MDNS_RECORDTYPE_A) { |
||||
|
|
||||
|
struct sockaddr_in addr; |
||||
|
mdns_record_parse_a(data, size, offset, length, &addr); |
||||
|
mdns_string_t addrstr = ipv4_address_to_string(namebuffer, sizeof(namebuffer), &addr, sizeof(addr)); |
||||
|
|
||||
|
List a_l = List::create( |
||||
|
_["from"] = String(fromaddrstr.str), |
||||
|
_["type"] = String("A"), |
||||
|
_["entry_type"] = String(entrytype), |
||||
|
_["addr"] = String(addrstr.str) |
||||
|
); |
||||
|
|
||||
|
l->push_back(a_l); |
||||
|
|
||||
|
printf("%.*s : %s A %.*s\n", |
||||
|
MDNS_STRING_FORMAT(fromaddrstr), entrytype, |
||||
|
MDNS_STRING_FORMAT(addrstr)); |
||||
|
|
||||
|
} else if (rtype == MDNS_RECORDTYPE_AAAA) { |
||||
|
|
||||
|
struct sockaddr_in6 addr; |
||||
|
mdns_record_parse_aaaa(data, size, offset, length, &addr); |
||||
|
mdns_string_t addrstr = ipv6_address_to_string(namebuffer, sizeof(namebuffer), &addr, sizeof(addr)); |
||||
|
|
||||
|
List aaaa_l = List::create( |
||||
|
_["from"] = String(fromaddrstr.str), |
||||
|
_["type"] = String("AAAA"), |
||||
|
_["entry_type"] = String(entrytype), |
||||
|
_["addr"] = String(addrstr.str) |
||||
|
); |
||||
|
|
||||
|
l->push_back(aaaa_l); |
||||
|
|
||||
|
printf("%.*s : %s AAAA %.*s\n", |
||||
|
MDNS_STRING_FORMAT(fromaddrstr), entrytype, |
||||
|
MDNS_STRING_FORMAT(addrstr)); |
||||
|
|
||||
|
} else if (rtype == MDNS_RECORDTYPE_TXT) { |
||||
|
|
||||
|
size_t parsed = mdns_record_parse_txt(data, size, offset, length, |
||||
|
txtbuffer, sizeof(txtbuffer) / sizeof(mdns_record_txt_t)); |
||||
|
for (size_t itxt = 0; itxt < parsed; ++itxt) { |
||||
|
|
||||
|
if (txtbuffer[itxt].value.length) { |
||||
|
|
||||
|
printf("%.*s : %s TXT %.*s = %.*s\n", |
||||
|
MDNS_STRING_FORMAT(fromaddrstr), entrytype, |
||||
|
MDNS_STRING_FORMAT(txtbuffer[itxt].key), |
||||
|
MDNS_STRING_FORMAT(txtbuffer[itxt].value)); |
||||
|
|
||||
|
List txt_l = List::create( |
||||
|
_["from"] = String(fromaddrstr.str), |
||||
|
_["type"] = String("TXT"), |
||||
|
_["entry_type"] = String(entrytype), |
||||
|
_["key"] = String(txtbuffer[itxt].key.str), |
||||
|
_["value"] = String(txtbuffer[itxt].value.str) |
||||
|
); |
||||
|
|
||||
|
l->push_back(txt_l); |
||||
|
|
||||
|
} else { |
||||
|
|
||||
|
printf("%.*s : %s TXT %.*s\n", |
||||
|
MDNS_STRING_FORMAT(fromaddrstr), entrytype, |
||||
|
MDNS_STRING_FORMAT(txtbuffer[itxt].key)); |
||||
|
|
||||
|
List txt_l = List::create( |
||||
|
_["from"] = String(fromaddrstr.str), |
||||
|
_["type"] = String("TXT"), |
||||
|
_["entry_type"] = String(entrytype), |
||||
|
_["key"] = String(txtbuffer[itxt].key.str) |
||||
|
); |
||||
|
|
||||
|
l->push_back(txt_l); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
} else { |
||||
|
|
||||
|
List generic_l = List::create( |
||||
|
_["from"] = String(fromaddrstr.str), |
||||
|
_["entry_type"] = entrytype, |
||||
|
_["type"] = NA_STRING, |
||||
|
_["rclass"] = rclass, |
||||
|
_["ttl"] = ttl, |
||||
|
_["length"] = length |
||||
|
); |
||||
|
|
||||
|
l->push_back(generic_l); |
||||
|
|
||||
|
printf("%.*s : %s type %u rclass 0x%x ttl %u length %d\n", |
||||
|
MDNS_STRING_FORMAT(fromaddrstr), entrytype, |
||||
|
rtype, rclass, ttl, (int)length); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
return 0; |
||||
|
|
||||
|
} |
||||
|
|
||||
|
//' @export
|
||||
|
// [[Rcpp::export]]
|
||||
|
List int_bnjr_discover() { |
||||
|
|
||||
|
#ifdef _WIN32 |
||||
|
WORD versionWanted = MAKEWORD(1, 1); |
||||
|
WSADATA wsaData; |
||||
|
if (WSAStartup(versionWanted, &wsaData)) { |
||||
|
printf("Failed to initialize WinSock\n"); |
||||
|
return -1; |
||||
|
} |
||||
|
#endif |
||||
|
|
||||
|
size_t capacity = 2048; |
||||
|
void* buffer = malloc(capacity); |
||||
|
size_t records; |
||||
|
|
||||
|
List out = List::create(); |
||||
|
|
||||
|
int port = 0; |
||||
|
int sock = mdns_socket_open_ipv4(port); |
||||
|
|
||||
|
if (sock < 0) return(out); |
||||
|
|
||||
|
if (mdns_discovery_send(sock)) { |
||||
|
goto failnice; |
||||
|
} |
||||
|
|
||||
|
for (int i = 0; i < 10; ++i) { |
||||
|
|
||||
|
do { |
||||
|
|
||||
|
records = mdns_discovery_recv( |
||||
|
sock, buffer, capacity, |
||||
|
query_callback, &out |
||||
|
); |
||||
|
|
||||
|
} while (records); |
||||
|
|
||||
|
if (records) i = 0; |
||||
|
|
||||
|
sleep(1); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
failnice: |
||||
|
free(buffer); |
||||
|
|
||||
|
mdns_socket_close(sock); |
||||
|
|
||||
|
#ifdef _WIN32 |
||||
|
WSACleanup(); |
||||
|
#endif |
||||
|
|
||||
|
return(out); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
//' @export
|
||||
|
// [[Rcpp::export]]
|
||||
|
List int_bnjr_query_send(std::string svc) { |
||||
|
|
||||
|
#ifdef _WIN32 |
||||
|
WORD versionWanted = MAKEWORD(1, 1); |
||||
|
WSADATA wsaData; |
||||
|
if (WSAStartup(versionWanted, &wsaData)) { |
||||
|
printf("Failed to initialize WinSock\n"); |
||||
|
return -1; |
||||
|
} |
||||
|
#endif |
||||
|
|
||||
|
size_t capacity = 2048; |
||||
|
void* buffer = malloc(capacity); |
||||
|
void* user_data = 0; |
||||
|
size_t records; |
||||
|
|
||||
|
List out = List::create(); |
||||
|
|
||||
|
int port = 0; |
||||
|
int sock = mdns_socket_open_ipv4(port); |
||||
|
|
||||
|
if (sock < 0) return(out); |
||||
|
|
||||
|
Rcout << "SERVICE: " << svc.c_str() << std::endl; |
||||
|
|
||||
|
if (mdns_discovery_send(sock)) { |
||||
|
Rcout << "FAILED" << std::endl; |
||||
|
goto failnice2; |
||||
|
} |
||||
|
|
||||
|
if (mdns_query_send(sock, MDNS_RECORDTYPE_PTR, |
||||
|
svc.c_str(), svc.length(), |
||||
|
buffer, capacity)) { |
||||
|
Rcout << "FAILED" << std::endl; |
||||
|
goto failnice2; |
||||
|
} |
||||
|
|
||||
|
for (int i = 0; i < 10; ++i) { |
||||
|
|
||||
|
do { |
||||
|
records = mdns_query_recv( |
||||
|
sock, buffer, capacity, query_callback, &out, 1 |
||||
|
); |
||||
|
|
||||
|
Rcout << "RECORDS: " << records << std::endl; |
||||
|
|
||||
|
} while (records); |
||||
|
|
||||
|
if (records) i = 0; |
||||
|
|
||||
|
sleep(1); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
failnice2: |
||||
|
free(buffer); |
||||
|
|
||||
|
mdns_socket_close(sock); |
||||
|
|
||||
|
#ifdef _WIN32 |
||||
|
WSACleanup(); |
||||
|
#endif |
||||
|
|
||||
|
return(out); |
||||
|
|
||||
|
} |
File diff suppressed because it is too large
@ -0,0 +1,5 @@ |
|||||
|
|
||||
|
if ( requireNamespace("tinytest", quietly=TRUE) ){ |
||||
|
tinytest::test_package("bonjour") |
||||
|
} |
||||
|
|
Loading…
Reference in new issue