1
0
mirror of https://github.com/tomahawk-player/tomahawk.git synced 2025-04-22 17:01:51 +02:00

Bye bye, jdns. We're not using you anymore, you can find your own way in jreen :P

This commit is contained in:
Dominik Schmidt 2011-05-25 00:21:25 +02:00
parent d546cc6156
commit 95f5d214c5
26 changed files with 0 additions and 13148 deletions

@ -69,8 +69,6 @@ namespace TomahawkUtils
DLLEXPORT void setNam( QNetworkAccessManager* nam );
DLLEXPORT void setProxy( QNetworkProxy* proxy );
DLLEXPORT DNSResolver* dnsResolver();
}
#endif // TOMAHAWKUTILS_H

@ -1,3 +1,2 @@
ADD_SUBDIRECTORY( jdns )
ADD_SUBDIRECTORY( qxt )
ADD_SUBDIRECTORY( liblastfm2 )

@ -1,54 +0,0 @@
PROJECT(jdns)
CMAKE_MINIMUM_REQUIRED(VERSION 2.6 FATAL_ERROR)
SET(CMAKE_VERBOSE_MAKEFILE ON)
SET( QT_USE_QTNETWORK TRUE )
INCLUDE( ${QT_USE_FILE} )
add_definitions( ${QT_DEFINITIONS} )
add_definitions( -DQT_SHARED )
SET( CMAKE_C_FLAGS ${CLEAN_C_FLAGS} )
if(WIN32)
set(PLATFORM_SPECIFIC_LIBS "ws2_32.dll" "advapi32.dll" )
endif(WIN32)
set(TOMAHAWK_JDNS_SOURCES
jdns/jdns_util.c
jdns/jdns_packet.c
jdns/jdns_mdnsd.c
jdns/jdns_sys.c
jdns/jdns.c
jdns/qjdns_sock.cpp
jdns/qjdns.cpp
jdnsshared/jdnsshared.cpp
)
set(TOMAHAWK_JDNS_HEADERS
jdns/qjdns.h
jdns/qjdns_helpers.h
jdnsshared/jdnsshared.h
jdnsshared/jdnsshared_helpers.h
)
include_directories(
.
${QT_INCLUDE_DIR}
${QT_INCLUDES}
jdns
)
qt4_wrap_cpp( TOMAHAWK_JDNS_MOC ${TOMAHAWK_JDNS_HEADERS} )
ADD_LIBRARY(tomahawk_jdns SHARED ${TOMAHAWK_JDNS_SOURCES} ${TOMAHAWK_JDNS_MOC})
target_link_libraries(tomahawk_jdns
${QT_LIBRARIES}
${PLATFORM_SPECIFIC_LIBS}
)
SET_TARGET_PROPERTIES( tomahawk_jdns PROPERTIES DEFINE_SYMBOL MAKE_JDNS_LIB )
INSTALL(TARGETS tomahawk_jdns DESTINATION lib${LIB_SUFFIX})

@ -1,85 +0,0 @@
JDNS
----
Date: October 1st, 2005
Author: Justin Karneges <justin@affinix.com>
JDNS is a simple DNS implementation that can perform normal DNS queries
of any record type (notably SRV), as well as Multicast DNS queries and
advertising. Multicast support is based on Jeremie Miller's "mdnsd"
implementation.
For maximum flexibility, JDNS is written in C with no direct dependencies,
and is licensed under the MIT license. Your application must supply
functionality to JDNS, such as UDP sending/receiving, via callbacks.
For Qt users there is a wrapper available called QJDns. jdns.pri can
be used to include everything into a qmake project. jdns.pro will build
the sample Qt-based commandline tool 'jdns'.
Features:
- DNS client "stub" resolver
- Can fetch any record type, but provides handy decoding for many
known types: A, AAAA, SRV, MX, TXT, etc.
- Performs retries, caching/expiration, and CNAME following
- Algorithm logic adapted from Q3Dns
- Multicast queries
- Multicast advertising
Why?
- Trolltech is phasing out the Qt DNS implementation, which in Qt 4 has
been relegated to the Qt3Support module. A replacement was desired.
- While there are many DNS libraries available, at the time of this
writing it was (and still may be) hard to find one that satisfies
three essential conditions: cross-platform friendliness (and this
includes Windows 9x!), the ability to integrate into existing
eventloops, sensible licensing (ie, not GPL).
How to use:
- Prepare callbacks and call jdns_session_new()
- Call jdns_init_unicast() or jdns_init_multicast(), depending on
if you want regular or multicast DNS. If you want both kinds, you
can always make two sessions.
- Make queries and have fun
- Call jdns_step() at the right times to advance JDNS processing
What is left to you:
- The callback functions, obviously.
- Querying for several "qualified" names. Here is what Q3Dns does:
Query for name as provided
Query for name + '.domain' (for every domain the computer is in)
- Detecting for '.local' in a name to be queried, and using that
to decide whether to query via Multicast or normal DNS.
- Recognition of IP addresses. If you want an IP address to resolve
to itself, then do that yourself. Passing an IP address as a DNS
name to JDNS won't work (especially since it wouldn't make any
sense in some contexts, like SRV).
- Recognition of known hosts. If you want this, compare inputs against
jdns_system_dnsparams().
- For zeroconf/Bonjour, keep in mind that JDNS only provides Multicast
DNS capability. DNS-SD and any higher layers would be your job.
Using a custom DNS implementation has the drawback that it is difficult
to take advantage of platform-specific features (for example, an OS-wide
DNS cache or LDAP integration).
An application strategy for normal DNS should probably be:
- If an A or AAAA record is desired, use a native lookup.
- Else, if the platform has advanced DNS features already (ie,
res_query), use those.
- Else, use JDNS.
However, it may not be a bad idea at first to use JDNS for all occasions,
so that it can be debugged.
For Multicast DNS, awareness of the platform is doubly important. There
should only be one Multicast DNS "Responder" per computer, and using JDNS
at the same time could result in a conflict.
An application strategy for Multicast DNS should be:
- If the platform has a Multicast DNS daemon installed already, use
it somehow.
- Else, use JDNS.
Have fun!

@ -1,20 +0,0 @@
(nothing)
but, this stuff couldn't hurt:
fields that need to be an explicit size should use int16_t, etc
support for other DNS record types (SOA, NSPTR)
detect CNAME loops, rather than looping max times in order to fail
don't follow CNAME for SRV (or so I'm told?)
if it is not possible to implement DNSSEC outside of jdns, then add the
minimal number of hooks to jdns so that it becomes possible
use hash tables to speed up the list lookups
unit tests
qjdns debug lines reworking:
init should emit debugLinesReady, and debug should be available
immediately after call (the emit will break SS/DS, put a note about
this in a comment about init())
anywhere else, debugLines should conform to SS, but be emitted at the
proper time, not deferred. currently, the debug arrives out of
sequence with the other signals, resulting in strange output in the
commandline tool.
consideration for these changes in jdnsshared

File diff suppressed because it is too large Load Diff

@ -1,507 +0,0 @@
/*
* Copyright (C) 2005,2006 Justin Karneges
*
* 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.
*/
#ifndef JDNS_H
#define JDNS_H
#ifdef __cplusplus
extern "C" {
#endif
typedef void (*jdns_object_dtor_func)(void *);
typedef void *(*jdns_object_cctor_func)(const void *);
#define JDNS_OBJECT \
jdns_object_dtor_func dtor; \
jdns_object_cctor_func cctor;
#define JDNS_OBJECT_NEW(name) \
(name##_t *)jdns_object_new(sizeof(name##_t), \
(jdns_object_dtor_func)name##_delete, \
(jdns_object_cctor_func)name##_copy);
typedef struct jdns_object
{
JDNS_OBJECT
} jdns_object_t;
void *jdns_object_new(int size, void (*dtor)(void *),
void *(*cctor)(const void *));
void *jdns_object_copy(const void *a);
void jdns_object_delete(void *a);
void jdns_object_free(void *a);
#define JDNS_LIST_DECLARE(name) \
JDNS_OBJECT \
int count; \
name##_t **item;
typedef struct jdns_list
{
JDNS_OBJECT
int count;
void **item;
int valueList;
int autoDelete;
} jdns_list_t;
jdns_list_t *jdns_list_new();
jdns_list_t *jdns_list_copy(const jdns_list_t *a);
void jdns_list_delete(jdns_list_t *a);
void jdns_list_clear(jdns_list_t *a);
void jdns_list_insert(jdns_list_t *a, void *item, int pos);
void jdns_list_insert_value(jdns_list_t *a, const void *item, int pos);
void jdns_list_remove(jdns_list_t *a, void *item);
void jdns_list_remove_at(jdns_list_t *a, int pos);
typedef struct jdns_string
{
JDNS_OBJECT
unsigned char *data;
int size;
} jdns_string_t;
jdns_string_t *jdns_string_new();
jdns_string_t *jdns_string_copy(const jdns_string_t *s);
void jdns_string_delete(jdns_string_t *s);
void jdns_string_set(jdns_string_t *s, const unsigned char *str,
int str_len);
void jdns_string_set_cstr(jdns_string_t *s, const char *str);
// overlays jdns_list
typedef struct jdns_stringlist
{
JDNS_OBJECT
int count;
jdns_string_t **item;
} jdns_stringlist_t;
jdns_stringlist_t *jdns_stringlist_new();
jdns_stringlist_t *jdns_stringlist_copy(const jdns_stringlist_t *a);
void jdns_stringlist_delete(jdns_stringlist_t *a);
void jdns_stringlist_append(jdns_stringlist_t *a, const jdns_string_t *str);
typedef struct jdns_address
{
int isIpv6;
union
{
unsigned long int v4;
unsigned char *v6; // 16 bytes
} addr;
char *c_str;
} jdns_address_t;
jdns_address_t *jdns_address_new();
jdns_address_t *jdns_address_copy(const jdns_address_t *a);
void jdns_address_delete(jdns_address_t *a);
void jdns_address_set_ipv4(jdns_address_t *a, unsigned long int ipv4);
void jdns_address_set_ipv6(jdns_address_t *a, const unsigned char *ipv6);
// return 1 if string was ok, else 0. Note: IPv4 addresses only!
int jdns_address_set_cstr(jdns_address_t *a, const char *str);
// return 1 if the same, else 0
int jdns_address_cmp(const jdns_address_t *a, const jdns_address_t *b);
// convenient predefined addresses/ports
#define JDNS_UNICAST_PORT 53
#define JDNS_MULTICAST_PORT 5353
jdns_address_t *jdns_address_multicast4_new(); // 224.0.0.251
jdns_address_t *jdns_address_multicast6_new(); // FF02::FB
typedef struct jdns_server
{
unsigned char *name;
int port; // SRV only
int priority;
int weight; // SRV only
} jdns_server_t;
jdns_server_t *jdns_server_new();
jdns_server_t *jdns_server_copy(const jdns_server_t *s);
void jdns_server_delete(jdns_server_t *s);
void jdns_server_set_name(jdns_server_t *s, const unsigned char *name);
typedef struct jdns_nameserver
{
jdns_address_t *address;
int port;
} jdns_nameserver_t;
jdns_nameserver_t *jdns_nameserver_new();
jdns_nameserver_t *jdns_nameserver_copy(const jdns_nameserver_t *a);
void jdns_nameserver_delete(jdns_nameserver_t *a);
void jdns_nameserver_set(jdns_nameserver_t *a, const jdns_address_t *addr,
int port);
typedef struct jdns_nameserverlist
{
int count;
jdns_nameserver_t **item;
} jdns_nameserverlist_t;
jdns_nameserverlist_t *jdns_nameserverlist_new();
jdns_nameserverlist_t *jdns_nameserverlist_copy(
const jdns_nameserverlist_t *a);
void jdns_nameserverlist_delete(jdns_nameserverlist_t *a);
void jdns_nameserverlist_append(jdns_nameserverlist_t *a,
const jdns_address_t *addr, int port);
typedef struct jdns_dnshost
{
jdns_string_t *name;
jdns_address_t *address;
} jdns_dnshost_t;
typedef struct jdns_dnshostlist
{
int count;
jdns_dnshost_t **item;
} jdns_dnshostlist_t;
typedef struct jdns_dnsparams
{
jdns_nameserverlist_t *nameservers;
jdns_stringlist_t *domains;
jdns_dnshostlist_t *hosts;
} jdns_dnsparams_t;
jdns_dnsparams_t *jdns_dnsparams_new();
jdns_dnsparams_t *jdns_dnsparams_copy(jdns_dnsparams_t *a);
void jdns_dnsparams_delete(jdns_dnsparams_t *a);
void jdns_dnsparams_append_nameserver(jdns_dnsparams_t *a,
const jdns_address_t *addr, int port);
void jdns_dnsparams_append_domain(jdns_dnsparams_t *a,
const jdns_string_t *domain);
void jdns_dnsparams_append_host(jdns_dnsparams_t *a,
const jdns_string_t *name, const jdns_address_t *address);
#define JDNS_RTYPE_A 1
#define JDNS_RTYPE_AAAA 28
#define JDNS_RTYPE_MX 15
#define JDNS_RTYPE_SRV 33
#define JDNS_RTYPE_CNAME 5
#define JDNS_RTYPE_PTR 12
#define JDNS_RTYPE_TXT 16
#define JDNS_RTYPE_HINFO 13
#define JDNS_RTYPE_NS 2
#define JDNS_RTYPE_ANY 255
typedef struct jdns_rr
{
unsigned char *owner;
int ttl;
int type;
int qclass;
int rdlength;
unsigned char *rdata;
int haveKnown;
union
{
jdns_address_t *address; // for A, AAAA
jdns_server_t *server; // for MX, SRV
unsigned char *name; // for CNAME, PTR, NS
jdns_stringlist_t *texts; // for TXT
struct
{
jdns_string_t *cpu;
jdns_string_t *os;
} hinfo; // for HINFO
} data;
} jdns_rr_t;
jdns_rr_t *jdns_rr_new();
jdns_rr_t *jdns_rr_copy(const jdns_rr_t *r);
void jdns_rr_delete(jdns_rr_t *r);
void jdns_rr_set_owner(jdns_rr_t *r, const unsigned char *name);
void jdns_rr_set_record(jdns_rr_t *r, int type, const unsigned char *rdata,
int rdlength);
void jdns_rr_set_A(jdns_rr_t *r, const jdns_address_t *address);
void jdns_rr_set_AAAA(jdns_rr_t *r, const jdns_address_t *address);
void jdns_rr_set_MX(jdns_rr_t *r, const unsigned char *name, int priority);
void jdns_rr_set_SRV(jdns_rr_t *r, const unsigned char *name, int port,
int priority, int weight);
void jdns_rr_set_CNAME(jdns_rr_t *r, const unsigned char *name);
void jdns_rr_set_PTR(jdns_rr_t *r, const unsigned char *name);
void jdns_rr_set_TXT(jdns_rr_t *r, const jdns_stringlist_t *texts);
void jdns_rr_set_HINFO(jdns_rr_t *r, const jdns_string_t *cpu,
const jdns_string_t *os);
void jdns_rr_set_NS(jdns_rr_t *r, const unsigned char *name);
// note: only works on known types
int jdns_rr_verify(const jdns_rr_t *r);
typedef struct jdns_response
{
int answerCount;
jdns_rr_t **answerRecords;
int authorityCount;
jdns_rr_t **authorityRecords;
int additionalCount;
jdns_rr_t **additionalRecords;
} jdns_response_t;
jdns_response_t *jdns_response_new();
jdns_response_t *jdns_response_copy(const jdns_response_t *r);
void jdns_response_delete(jdns_response_t *r);
void jdns_response_append_answer(jdns_response_t *r, const jdns_rr_t *rr);
void jdns_response_append_authority(jdns_response_t *r, const jdns_rr_t *rr);
void jdns_response_append_additional(jdns_response_t *r,
const jdns_rr_t *rr);
#define JDNS_PUBLISH_SHARED 0x0001
#define JDNS_PUBLISH_UNIQUE 0x0002
#define JDNS_STEP_TIMER 0x0001
#define JDNS_STEP_HANDLE 0x0002
#define JDNS_EVENT_RESPONSE 0x0001
#define JDNS_EVENT_PUBLISH 0x0002
#define JDNS_EVENT_SHUTDOWN 0x0003
#define JDNS_STATUS_SUCCESS 0x0001
#define JDNS_STATUS_NXDOMAIN 0x0002
#define JDNS_STATUS_ERROR 0x0003
#define JDNS_STATUS_TIMEOUT 0x0004
#define JDNS_STATUS_CONFLICT 0x0005
typedef struct jdns_session jdns_session_t;
typedef struct jdns_callbacks
{
void *app; // user-supplied context
// time_now:
// s: session
// app: user-supplied context
// return: milliseconds since session started
int (*time_now)(jdns_session_t *s, void *app);
// rand_int:
// s: session
// app: user-supplied context
// return: random integer between 0-65535
int (*rand_int)(jdns_session_t *s, void *app);
// debug_line:
// s: session
// app: user-supplied context
// str: a line of debug text
// return: nothing
void (*debug_line)(jdns_session_t *s, void *app, const char *str);
// udp_bind:
// s: session
// app: user-supplied context
// addr: ip address of interface to bind to. 0 for all
// port: port of interface to bind to. 0 for any
// maddr: multicast address. 0 if not using multicast
// return: handle (>0) of bound socket, or 0 on error
// note: for multicast, the following must be done:
// use SO_REUSEPORT to share with other mdns programs
// use IP_ADD_MEMBERSHIP to associate addr and maddr
// set IP_MULTICAST_TTL to 255
int (*udp_bind)(jdns_session_t *s, void *app,
const jdns_address_t *addr, int port,
const jdns_address_t *maddr);
// udp_unbind:
// s: session
// app: user-supplied context
// handle: handle of socket obtained with udp_bind
// return: nothing
void (*udp_unbind)(jdns_session_t *s, void *app, int handle);
// udp_read:
// s: session
// app: user-supplied context
// handle: handle of socket obtained with udp_bind
// addr: store ip address of sender
// port: store port of sender
// buf: store packet content
// bufsize: value contains max size, to be changed to real size
// return: 1 if packet read, 0 if none available
int (*udp_read)(jdns_session_t *s, void *app, int handle,
jdns_address_t *addr, int *port, unsigned char *buf,
int *bufsize);
// udp_write:
// s: session
// app: user-supplied context
// handle: handle of socket obtained with udp_bind
// addr: ip address of recipient
// port: port of recipient
// buf: packet content
// bufsize: size of packet
// return: 1 if packet taken for writing, 0 if this is a bad time
int (*udp_write)(jdns_session_t *s, void *app, int handle,
const jdns_address_t *addr, int port, unsigned char *buf,
int bufsize);
} jdns_callbacks_t;
typedef struct jdns_event
{
int type; // JDNS_EVENT
int id; // query id or publish id
// for query, this can be SUCCESS, NXDOMAIN, ERROR, or TIMEOUT
// for publish, this can be SUCCESS, ERROR, or CONFLICT
int status;
// for query
jdns_response_t *response;
} jdns_event_t;
void jdns_event_delete(jdns_event_t *e);
// jdns_session_new:
// callbacks: the struct of callbacks
// return: newly allocated session
jdns_session_t *jdns_session_new(jdns_callbacks_t *callbacks);
// jdns_session_delete:
// s: session to free
// return: nothing
void jdns_session_delete(jdns_session_t *s);
// jdns_init_unicast:
// s: session
// addr: ip address of interface to bind to. NULL for all
// port: port of interface to bind to. 0 for any
// return: 1 on success, 0 on failure
int jdns_init_unicast(jdns_session_t *s, const jdns_address_t *addr,
int port);
// jdns_init_multicast:
// s: session
// addr: ip address of interface to bind to. NULL for all
// port: port of interface to bind to. 0 for any
// addr: multicast address to associate with. cannot be NULL
// return: 1 on success, 0 on failure
int jdns_init_multicast(jdns_session_t *s, const jdns_address_t *addr,
int port, const jdns_address_t *maddr);
// jdns_shutdown:
// s: session
// return: nothing
void jdns_shutdown(jdns_session_t *s);
// jdns_set_nameservers:
// s: session
// nslist: list of nameservers
// return nothing
void jdns_set_nameservers(jdns_session_t *s,
const jdns_nameserverlist_t *nslist);
// jdns_probe:
// s: session
// return: nothing
void jdns_probe(jdns_session_t *s);
// jdns_query:
// s: session
// name: the name to look up
// rtype: the record type
// return: id of this operation
int jdns_query(jdns_session_t *s, const unsigned char *name, int rtype);
// jdns_cancel_query:
// s: session
// id: the operation id to cancel
// return: nothing
void jdns_cancel_query(jdns_session_t *s, int id);
// jdns_publish:
// s: session
// mode: JDNS_PUBLISH shared or unique
// rec: the record data
// return: id of this operation
// note: supported record types: A, AAAA, SRV, CNAME, PTR, TXT, and HINFO.
// if the published type is not one of these, raw rdata must be set.
int jdns_publish(jdns_session_t *s, int mode, const jdns_rr_t *rec);
// jdns_update_publish:
// s: session
// id: the operation id to update
// rec: the record data
// return: nothing
// note: update only works on successfully published records, and no event
// is generated for a successful update.
void jdns_update_publish(jdns_session_t *s, int id, const jdns_rr_t *rec);
// jdns_cancel_publish:
// s: session
// id: the operation id to cancel
// return: nothing
void jdns_cancel_publish(jdns_session_t *s, int id);
// jdns_step:
// s: session
// return: JDNS_STEP flags OR'd together
int jdns_step(jdns_session_t *s);
// jdns_next_timer:
// s: session
// return: milliseconds until timeout
int jdns_next_timer(jdns_session_t *s);
// jdns_set_handle_readable:
// s: session
// handle: handle that is now readable
// return: nothing
void jdns_set_handle_readable(jdns_session_t *s, int handle);
// jdns_set_handle_writable:
// s: session
// handle: handle that is now writable
// return: nothing
void jdns_set_handle_writable(jdns_session_t *s, int handle);
// jdns_next_event:
// s: session
// return: newly allocated event, or zero if none are ready
jdns_event_t *jdns_next_event(jdns_session_t *s);
// jdns_system_dnsparams:
// return: newly allocated dnsparams from the system
jdns_dnsparams_t *jdns_system_dnsparams();
// jdns_set_hold_ids_enabled
// s: session
// enabled: whether to enable id holding. default is 0 (disabled)
// return: nothing
// normally, when a unicast query completes or any kind of query or publish
// operation results in an error, the operation is automatically "canceled".
// when id holding is enabled, the operation still stops internally, but the
// id value used by that operation is "held" until the application
// explicitly calls jdns_cancel_query() or jdns_cancel_publish() to release
// it. this allows the application to ensure there is no ambiguity when
// determining which operation a particular event belongs to. it is disabled
// be default so as to not introduce memory leaks in existing applications,
// however new applications really should use it.
void jdns_set_hold_ids_enabled(jdns_session_t *s, int enabled);
#ifdef __cplusplus
}
#endif
#endif

@ -1,27 +0,0 @@
# qmake project include file
QT *= network
windows:{
LIBS += -lWs2_32 -lAdvapi32
}
unix:{
#QMAKE_CFLAGS += -pedantic
}
HEADERS += \
$$PWD/jdns_packet.h \
$$PWD/jdns_mdnsd.h \
$$PWD/jdns_p.h \
$$PWD/jdns.h \
$$PWD/qjdns_sock.h \
$$PWD/qjdns.h
SOURCES += \
$$PWD/jdns_util.c \
$$PWD/jdns_packet.c \
$$PWD/jdns_mdnsd.c \
$$PWD/jdns_sys.c \
$$PWD/jdns.c \
$$PWD/qjdns_sock.cpp \
$$PWD/qjdns.cpp

@ -1,9 +0,0 @@
CONFIG += console
CONFIG -= app_bundle
QT -= gui
QT += network
include(jdns.pri)
SOURCES += \
main.cpp

File diff suppressed because it is too large Load Diff

@ -1,120 +0,0 @@
/*
* Copyright (C) 2005 Jeremie Miller
* Copyright (C) 2005,2006 Justin Karneges
*
* 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.
*/
#ifndef JDNS_MDNSD_H
#define JDNS_MDNSD_H
#include "jdns_p.h"
struct mytimeval
{
unsigned long int tv_sec; /* seconds */
unsigned long int tv_usec; /* microseconds */
};
typedef struct mdnsd_struct *mdnsd; // main daemon data
typedef struct mdnsdr_struct *mdnsdr; // record entry
// answer data
typedef struct mdnsda_struct
{
unsigned char *name;
unsigned short int type;
unsigned long int ttl;
unsigned long int real_ttl;
unsigned short int rdlen;
unsigned char *rdata;
unsigned long int ip; // A
unsigned char *rdname; // NS/CNAME/PTR/SRV
struct { unsigned short int priority, weight, port; } srv; // SRV
} *mdnsda;
///////////
// Global functions
//
// create a new mdns daemon for the given class of names (usually 1) and maximum frame size
mdnsd mdnsd_new(int class, int frame, int port, int (*time_now)(mdnsd d, void *arg), int (*rand_int)(mdnsd d, void *arg), void *arg);
//
// gracefully shutdown the daemon, use mdnsd_out() to get the last packets
void mdnsd_shutdown(mdnsd d);
//
// flush all cached records (network/interface changed)
void mdnsd_flush(mdnsd d);
//
// free given mdnsd (should have used mdnsd_shutdown() first!)
void mdnsd_free(mdnsd d);
//
///////////
///////////
// I/O functions
//
// incoming message from host (to be cached/processed)
void mdnsd_in(mdnsd d, const jdns_packet_t *m, const jdns_response_t *resp, const jdns_address_t *addr, unsigned short int port);
//
// outgoing messge to be delivered to host, returns >0 if one was returned and m/ip/port set
int mdnsd_out(mdnsd d, jdns_packet_t **m, jdns_address_t **addr, unsigned short int *port);
//
// returns the max wait-time until mdnsd_out() needs to be called again
struct mytimeval *mdnsd_sleep(mdnsd d);
//
////////////
///////////
// Q/A functions
//
// register a new query
// answer(record, arg) is called whenever one is found/changes/expires (immediate or anytime after, mdnsda valid until ->ttl==0)
// either answer returns -1, or another mdnsd_query with a NULL answer will remove/unregister this query
void mdnsd_query(mdnsd d, char *host, int type, int (*answer)(mdnsda a, void *arg), void *arg);
//
// returns the first (if last == NULL) or next answer after last from the cache
// mdnsda only valid until an I/O function is called
mdnsda mdnsd_list(mdnsd d, char *host, int type, mdnsda last);
//
///////////
///////////
// Publishing functions
//
// create a new unique record (try mdnsda_list first to make sure it's not used)
// conflict(arg) called at any point when one is detected and unable to recover
// after the first data is set_*(), any future changes effectively expire the old one and attempt to create a new unique record
mdnsdr mdnsd_unique(mdnsd d, char *host, int type, long int ttl, void (*pubresult)(int result, char *host, int type, void *arg), void *arg);
//
// create a new shared record
mdnsdr mdnsd_shared(mdnsd d, char *host, int type, long int ttl);
//
// de-list the given record
void mdnsd_done(mdnsd d, mdnsdr r);
//
// these all set/update the data for the given record, nothing is published until they are called
void mdnsd_set_raw(mdnsd d, mdnsdr r, char *data, int len);
void mdnsd_set_host(mdnsd d, mdnsdr r, char *name);
void mdnsd_set_ip(mdnsd d, mdnsdr r, unsigned long int ip);
void mdnsd_set_srv(mdnsd d, mdnsdr r, int priority, int weight, int port, char *name);
//
///////////
#endif

@ -1,95 +0,0 @@
/*
* Copyright (C) 2005-2008 Justin Karneges
*
* 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.
*/
#ifndef JDNS_P_H
#define JDNS_P_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <ctype.h>
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
# define JDNS_OS_WIN
#else
# define JDNS_OS_UNIX
#endif
#if defined(__FreeBSD__) || defined(__DragonFly__)
# define JDNS_OS_FREEBSD
#elif defined(__NetBSD__)
# define JDNS_OS_NETBSD
#elif defined(sun) || defined(__sun)
# define JDNS_OS_SOLARIS
#elif defined(__APPLE__) && (defined(__GNUC__) || defined(__xlC__) || defined(__xlc__))
# define JDNS_OS_MAC
#endif
#ifdef JDNS_OS_WIN
# include <windows.h>
#endif
#ifdef JDNS_OS_UNIX
# include <unistd.h>
# include <netinet/in.h>
#endif
#include "jdns.h"
#include "jdns_packet.h"
// jdns_util.c
void *jdns_alloc(int size);
void *jdns_realloc(void *p, int size);
void jdns_free(void *p);
char *jdns_strdup(const char *s);
unsigned char *jdns_copy_array(const unsigned char *src, int size);
int jdns_domain_cmp(const unsigned char *a, const unsigned char *b);
int jdns_sprintf_s(char *str, int n, const char *format, ...);
int jdns_vsprintf_s(char *str, int n, const char *format, va_list ap);
FILE *jdns_fopen(const char *path, const char *mode);
jdns_string_t *jdns_getenv(const char *name);
char *jdns_strcpy(char *dst, const char *src);
int jdns_string_indexOf(const jdns_string_t *s, unsigned char c, int pos);
jdns_stringlist_t *jdns_string_split(const jdns_string_t *s, unsigned char sep);
jdns_dnshost_t *jdns_dnshost_new();
jdns_dnshost_t *jdns_dnshost_copy(const jdns_dnshost_t *a);
void jdns_dnshost_delete(jdns_dnshost_t *a);
jdns_dnshostlist_t *jdns_dnshostlist_new();
jdns_dnshostlist_t *jdns_dnshostlist_copy(const jdns_dnshostlist_t *a);
void jdns_dnshostlist_delete(jdns_dnshostlist_t *a);
void jdns_dnshostlist_append(jdns_dnshostlist_t *a, const jdns_dnshost_t *host);
jdns_rr_t *jdns_rr_from_resource(const jdns_packet_resource_t *pr, const jdns_packet_t *ref);
void jdns_response_remove_extra(jdns_response_t *r);
void jdns_response_remove_answer(jdns_response_t *r, int pos);
#define alloc_type(type) (type *)jdns_alloc(sizeof(type))
#define _ustrdup(str) (unsigned char *)jdns_strdup((const char *)str)
#define _ustrlen(str) strlen((const char *)str)
#define _ustrcmp(a, b) strcmp((const char *)a, (const char *)b)
#endif

File diff suppressed because it is too large Load Diff

@ -1,116 +0,0 @@
/*
* Copyright (C) 2006 Justin Karneges
*
* 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.
*/
#ifndef JDNS_PACKET_H
#define JDNS_PACKET_H
#include "jdns.h"
// -- howto --
//
// writing packets:
// 1) call jdns_packet_new()
// 2) populate the jdns_packet_t structure, using the functions
// as necessary
// 3) call jdns_packet_export() to populate the raw data of the packet
//
// reading packets:
// 1) call jdns_packet_new()
// 2) call jdns_packet_import() with the raw data
// 3) the jdns_packet_t structure is now populated
//
// IMPORTANT: all names must be valid. that is, ending in a dot character
int jdns_packet_name_isvalid(const unsigned char *name, int size); // 0 if not valid
typedef struct jdns_packet_question
{
JDNS_OBJECT
jdns_string_t *qname;
unsigned short int qtype, qclass;
} jdns_packet_question_t;
jdns_packet_question_t *jdns_packet_question_new();
jdns_packet_question_t *jdns_packet_question_copy(const jdns_packet_question_t *a);
void jdns_packet_question_delete(jdns_packet_question_t *a);
typedef struct jdns_packet_write jdns_packet_write_t;
typedef struct jdns_packet jdns_packet_t;
typedef struct jdns_packet_resource
{
JDNS_OBJECT
jdns_string_t *qname;
unsigned short int qtype, qclass;
unsigned long int ttl; // 31-bit number, top bit always 0
unsigned short int rdlength;
unsigned char *rdata;
// private
jdns_list_t *writelog; // jdns_packet_write_t
} jdns_packet_resource_t;
jdns_packet_resource_t *jdns_packet_resource_new();
jdns_packet_resource_t *jdns_packet_resource_copy(const jdns_packet_resource_t *a);
void jdns_packet_resource_delete(jdns_packet_resource_t *a);
void jdns_packet_resource_add_bytes(jdns_packet_resource_t *a, const unsigned char *data, int size);
void jdns_packet_resource_add_name(jdns_packet_resource_t *a, const jdns_string_t *name);
int jdns_packet_resource_read_name(const jdns_packet_resource_t *a, const jdns_packet_t *p, int *at, jdns_string_t **name);
struct jdns_packet
{
JDNS_OBJECT
unsigned short int id;
struct
{
unsigned short qr, opcode, aa, tc, rd, ra, z, rcode;
} opts;
// item counts as specified by the packet. do not use these
// for iteration over the item lists, since they can be wrong
// if the packet is truncated.
int qdcount, ancount, nscount, arcount;
// value lists
jdns_list_t *questions; // jdns_packet_question_t
jdns_list_t *answerRecords; // jdns_packet_resource_t
jdns_list_t *authorityRecords; // jdns_packet_resource_t
jdns_list_t *additionalRecords; // jdns_packet_resource_t
// since dns packets are allowed to be truncated, it is possible
// for a packet to not get fully parsed yet still be considered
// successfully parsed. this flag means the packet was fully
// parsed also.
int fully_parsed;
int raw_size;
unsigned char *raw_data;
};
jdns_packet_t *jdns_packet_new();
jdns_packet_t *jdns_packet_copy(const jdns_packet_t *a);
void jdns_packet_delete(jdns_packet_t *a);
int jdns_packet_import(jdns_packet_t **a, const unsigned char *data, int size); // 0 on fail
int jdns_packet_export(jdns_packet_t *a, int maxsize); // 0 on fail
#endif

@ -1,850 +0,0 @@
/*
* Copyright (C) 2005-2008 Justin Karneges
*
* 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.
*/
/*
this code probes the system for dns settings. blah.
q3dns strategies
----------------
windows:
domain name, name server, "search list" found in windows registry here:
HKEY_LOCAL_MACHINE
System\CurrentControlSet\Services\Tcpip\Parameters <-- win nt+
System\CurrentControlSet\Services\VxD\MSTCP <-- win 98
for domain, try DhcpDomain else Domain
for name servers, try DhcpNameServer, else NameServer
for search list, try SearchList
iphlpapi.dll : GetNetworkParams(PFIXED_INFO, PULONG);
info->DomainName
info->DnsServerList (if not null, use it, and loop through ->Next until
null)
no search list
first try getnetworkparams. if that fails, try the nt regkey then the 98
regkey. it seems that search list can only come from the registry, so
maybe we need to grab that entry even if getnetworkparams works.
in the case of the registry, the nameserver and searchlist entries are
delimited by spaces on win nt and commas on win 98. probably a good
idea to simplify white space first (chop away space at start and end,
reduce all sections of spaces to one char). also, lowercase the search
list.
qt doesn't read the hosts file on windows. this might be a good idea, but
probably not worth it.
unix:
read /etc/resolv.conf manually:
for each line, split at spaces
if the first item is "nameserver", then there should be an IP address
following it. note: may contain mixed ipv4 and ipv6 addresses
if the first item is "search", all other items are added to the domain
list
if the first item is "domain", then the next item should be added to the
domain list.
do case-insensitive matching for the item types
for search/domain, the items are in the 8-bit system locale
info can also be fetched using system calls. we use the res_* stuff here.
first we should detect for a "modern res api". this is available from
glibc 2.3 and onward. use the following scheme to check for it:
#if defined(__GLIBC__) && ((__GLIBC__ > 2) || ((__GLIBC__ == 2)
&& (__GLIBC_MINOR__ >= 3)))
// modern res api
#endif
on mac we should look up res_init in the system library. see:
qt_mac_resolve_sys(RTLD_NEXT, "res_init"); for a hint.
otherwise we can just use res_init() straight.
under a modern res api, we do:
struct __res_state res;
res_ninit(&res);
otherwise, we simply call res_init(). for the modern api, we use the "res"
struct that we made. otherwise, we use the global "_res" struct.
read the res struct to obtain the name servers, search list, and domain.
lowercase the search list and domain.
qt tries the file, and if that fails it tries the syscalls. we may want to
do the syscalls first, or even just do both all the time.
read /etc/hosts manually:
for each line
if there is a '#' character in the line, remove it and everything to
the right
simplify white space
convert to lowercase
split the line at spaces
first item is the ip address
all remaining items are hostnames
note: these hosts could also be used for reverse-dns too
note2: Windows has a hosts file as well (like C:\WINDOWS\hosts)
*/
#include "jdns_p.h"
#ifdef JDNS_OS_WIN
# include <windows.h>
#endif
#ifdef JDNS_OS_UNIX
# include <netinet/in.h>
# include <arpa/nameser.h>
# include <resolv.h>
# include <dlfcn.h>
#endif
#define string_indexOf jdns_string_indexOf
#define string_split jdns_string_split
static int char_isspace(unsigned char c)
{
if(c == ' ' || c == '\t' || c == '\n' || c == '\r')
return 1;
return 0;
}
static unsigned char *string_getnextword(unsigned char *in, int size, int pos, int *newpos)
{
int n;
int at;
int len;
unsigned char *out;
at = pos;
// skip any space at the start
while(at < size && char_isspace(in[at]))
++at;
// all space? no word then
if(at >= size)
return 0;
// skip until a space or end
n = at;
while(n < size && !char_isspace(in[n]))
++n;
len = n - at;
// allocate length + zero byte
out = (unsigned char *)jdns_alloc(len + 1);
if(!out)
return 0;
memcpy(out, in + at, len);
out[len] = 0;
*newpos = at + len;
return out;
}
static jdns_string_t *string_simplify(const jdns_string_t *in)
{
int n;
int pos;
int total;
unsigned char *out;
int outlen;
jdns_string_t *outstr;
jdns_stringlist_t *wordlist;
// gather words and total of lengths
pos = 0;
total = 0;
wordlist = jdns_stringlist_new();
while(1)
{
jdns_string_t *word;
unsigned char *str = string_getnextword(in->data, in->size, pos, &pos);
if(!str)
break;
word = jdns_string_new();
jdns_string_set_cstr(word, (char *)str);
jdns_free(str);
jdns_stringlist_append(wordlist, word);
total += word->size;
jdns_string_delete(word);
}
if(total == 0)
{
jdns_stringlist_delete(wordlist);
outstr = jdns_string_new();
jdns_string_set_cstr(outstr, "");
return outstr;
}
// we need to allocate space for total lengths and wordcount-1 spaces
outlen = total + (wordlist->count - 1);
out = (unsigned char *)jdns_alloc(outlen);
// lay out the words
pos = 0;
for(n = 0; n < wordlist->count; ++n)
{
unsigned char *data = wordlist->item[n]->data;
int size = wordlist->item[n]->size;
memcpy(out + pos, data, size);
pos += size;
// if this is not the last word, append a space
if(n + 1 < wordlist->count)
out[pos++] = ' ';
}
jdns_stringlist_delete(wordlist);
outstr = jdns_string_new();
jdns_string_set(outstr, out, outlen);
jdns_free(out);
return outstr;
}
static jdns_string_t *string_tolower(const jdns_string_t *in)
{
int n;
jdns_string_t *out = jdns_string_copy(in);
for(n = 0; n < out->size; ++n)
out->data[n] = tolower(out->data[n]);
return out;
}
static jdns_string_t *file_nextline(FILE *f)
{
int at, size;
unsigned char *buf;
jdns_string_t *str;
size = 1023;
buf = (unsigned char *)jdns_alloc(size);
at = 0;
while(1)
{
unsigned char c = fgetc(f);
if(feof(f))
{
if(at > 0)
{
// if we read at least one char, take it as a
// line
break;
}
else
{
jdns_free(buf);
return 0;
}
}
if(c == '\n')
break;
if(c == '\r')
continue;
if(at < 1023)
buf[at++] = c;
}
str = jdns_string_new();
jdns_string_set(str, buf, at);
jdns_free(buf);
return str;
}
static jdns_dnshostlist_t *read_hosts_file(const char *path)
{
jdns_dnshostlist_t *out;
FILE *f;
jdns_string_t *line, *simp;
jdns_stringlist_t *parts;
jdns_address_t *addr;
int n;
out = jdns_dnshostlist_new();
f = jdns_fopen(path, "r");
if(!f)
return out;
while(1)
{
line = file_nextline(f);
if(!line)
break;
// truncate at comment
n = string_indexOf(line, '#', 0);
if(n != -1)
{
line->size = n;
line->data[n] = 0;
}
simp = string_simplify(line);
jdns_string_delete(line);
parts = string_split(simp, ' ');
jdns_string_delete(simp);
if(parts->count < 2)
{
jdns_stringlist_delete(parts);
continue;
}
addr = jdns_address_new();
if(!jdns_address_set_cstr(addr, (const char *)parts->item[0]->data))
{
jdns_address_delete(addr);
jdns_stringlist_delete(parts);
continue;
}
for(n = 1; n < parts->count; ++n)
{
jdns_dnshost_t *h = jdns_dnshost_new();
h->name = jdns_string_copy(parts->item[n]);
h->address = jdns_address_copy(addr);
jdns_dnshostlist_append(out, h);
jdns_dnshost_delete(h);
}
jdns_address_delete(addr);
jdns_stringlist_delete(parts);
}
fclose(f);
return out;
}
static void apply_hosts_file(jdns_dnsparams_t *a, const char *path)
{
int n;
jdns_dnshostlist_t *list;
list = read_hosts_file(path);
for(n = 0; n < list->count; ++n)
jdns_dnshostlist_append(a->hosts, list->item[n]);
jdns_dnshostlist_delete(list);
}
static int dnsparams_have_domain(const jdns_dnsparams_t *a, const jdns_string_t *domain)
{
int n;
for(n = 0; n < a->domains->count; ++n)
{
jdns_string_t *str = a->domains->item[n];
if(strcmp((const char *)str->data, (const char *)domain->data) == 0)
return 1;
}
return 0;
}
#ifdef JDNS_OS_WIN
// from Microsoft IPTypes.h
#ifndef IP_TYPES_INCLUDED
#define MAX_HOSTNAME_LEN 128
#define MAX_DOMAIN_NAME_LEN 128
#define MAX_SCOPE_ID_LEN 256
typedef struct {
char String[4 * 4];
} IP_ADDRESS_STRING, *PIP_ADDRESS_STRING, IP_MASK_STRING, *PIP_MASK_STRING;
typedef struct _IP_ADDR_STRING {
struct _IP_ADDR_STRING* Next;
IP_ADDRESS_STRING IpAddress;
IP_MASK_STRING IpMask;
DWORD Context;
} IP_ADDR_STRING, *PIP_ADDR_STRING;
typedef struct {
char HostName[MAX_HOSTNAME_LEN + 4] ;
char DomainName[MAX_DOMAIN_NAME_LEN + 4];
PIP_ADDR_STRING CurrentDnsServer;
IP_ADDR_STRING DnsServerList;
UINT NodeType;
char ScopeId[MAX_SCOPE_ID_LEN + 4];
UINT EnableRouting;
UINT EnableProxy;
UINT EnableDns;
} FIXED_INFO, *PFIXED_INFO;
#endif
typedef DWORD (WINAPI *GetNetworkParamsFunc)(PFIXED_INFO, PULONG);
static jdns_string_t *reg_readString(HKEY hk, const char *subkey)
{
char *buf;
DWORD bufsize;
int ret;
jdns_string_t *str = 0;
bufsize = 1024;
buf = (char *)jdns_alloc((int)bufsize);
if(!buf)
return 0;
ret = RegQueryValueExA(hk, subkey, 0, 0, (LPBYTE)buf, &bufsize);
if(ret == ERROR_MORE_DATA)
{
buf = (char *)jdns_realloc(buf, bufsize);
if(!buf)
{
jdns_free(buf);
return 0;
}
ret = RegQueryValueExA(hk, subkey, 0, 0, (LPBYTE)buf, &bufsize);
}
if(ret == ERROR_SUCCESS)
{
str = jdns_string_new();
jdns_string_set_cstr(str, (char *)buf);
}
jdns_free(buf);
return str;
}
static jdns_dnsparams_t *dnsparams_get_winreg()
{
int n;
jdns_dnsparams_t *params;
HKEY key;
int ret;
char sep;
jdns_string_t *str_domain, *str_nameserver, *str_searchlist;
jdns_stringlist_t *list_nameserver, *list_searchlist;
sep = ' ';
ret = RegOpenKeyExA(HKEY_LOCAL_MACHINE,
"System\\CurrentControlSet\\Services\\Tcpip\\Parameters",
0, KEY_READ, &key);
if(ret != ERROR_SUCCESS)
{
sep = ',';
ret = RegOpenKeyExA(HKEY_LOCAL_MACHINE,
"System\\CurrentControlSet\\Services\\VxD\\MSTCP",
0, KEY_READ, &key);
if(ret != ERROR_SUCCESS)
return 0;
}
str_domain = reg_readString(key, "DhcpDomain");
if(!str_domain)
str_domain = reg_readString(key, "Domain");
str_nameserver = reg_readString(key, "DhcpNameServer");
if(!str_nameserver)
str_nameserver = reg_readString(key, "NameServer");
str_searchlist = reg_readString(key, "SearchList");
RegCloseKey(key);
list_nameserver = 0;
if(str_nameserver)
{
list_nameserver = string_split(str_nameserver, sep);
jdns_string_delete(str_nameserver);
}
list_searchlist = 0;
if(str_searchlist)
{
// lowercase the string
jdns_string_t *p = string_tolower(str_searchlist);
jdns_string_delete(str_searchlist);
str_searchlist = p;
list_searchlist = string_split(str_searchlist, sep);
jdns_string_delete(str_searchlist);
}
params = jdns_dnsparams_new();
if(list_nameserver)
{
// qt seems to do a strange thing here by running each name
// server address through the q3dns setLabel function, and
// then pulls the result as a list of addresses. i have
// no idea why they do this, or how one IP address would
// turn into anything else, let alone several addresses.
// so, uh, we're not going to do that.
for(n = 0; n < list_nameserver->count; ++n)
{
jdns_address_t *addr = jdns_address_new();
if(jdns_address_set_cstr(addr, (char *)list_nameserver->item[n]->data))
jdns_dnsparams_append_nameserver(params, addr, JDNS_UNICAST_PORT);
jdns_address_delete(addr);
}
jdns_stringlist_delete(list_nameserver);
}
if(str_domain)
{
if(str_domain->size > 0)
jdns_dnsparams_append_domain(params, str_domain);
jdns_string_delete(str_domain);
}
if(list_searchlist)
{
for(n = 0; n < list_searchlist->count; ++n)
{
if(list_searchlist->item[n]->size > 0)
jdns_dnsparams_append_domain(params, list_searchlist->item[n]);
}
jdns_stringlist_delete(list_searchlist);
}
return params;
}
static jdns_dnsparams_t *dnsparams_get_winsys()
{
jdns_dnsparams_t *params;
GetNetworkParamsFunc myGetNetworkParams;
DWORD ret;
HINSTANCE lib;
jdns_address_t *addr;
jdns_string_t *str;
IP_ADDR_STRING *ipstr;
lib = LoadLibraryA("iphlpapi");
if(!lib)
return 0;
params = 0;
myGetNetworkParams = (GetNetworkParamsFunc)GetProcAddress(lib, "GetNetworkParams");
if(myGetNetworkParams)
{
ULONG bufsize = 0;
ret = myGetNetworkParams(0, &bufsize);
if(ret == ERROR_BUFFER_OVERFLOW)
{
FIXED_INFO *info = (FIXED_INFO *)jdns_alloc((int)bufsize);
ret = myGetNetworkParams(info, &bufsize);
if(ret == ERROR_SUCCESS)
{
params = jdns_dnsparams_new();
ipstr = &info->DnsServerList;
while(ipstr)
{
addr = jdns_address_new();
if(jdns_address_set_cstr(addr, (char *)ipstr->IpAddress.String))
jdns_dnsparams_append_nameserver(params, addr, JDNS_UNICAST_PORT);
jdns_address_delete(addr);
ipstr = ipstr->Next;
}
str = jdns_string_new();
jdns_string_set_cstr(str, info->DomainName);
if(str->size > 0)
jdns_dnsparams_append_domain(params, str);
jdns_string_delete(str);
}
jdns_free(info);
}
}
FreeLibrary(lib);
return params;
}
static void apply_hosts_var_filepath(jdns_dnsparams_t *a, const char *envvar, const char *path)
{
jdns_string_t *e;
char *str;
int elen, plen;
e = jdns_getenv(envvar);
if(!e)
return;
elen = strlen((char *)e->data);
plen = strlen(path);
str = (char *)jdns_alloc(elen + plen + 1);
memcpy(str, e->data, elen);
jdns_string_delete(e);
jdns_strcpy(str + elen, path);
apply_hosts_file(a, str);
jdns_free(str);
}
static void apply_win_hosts_file(jdns_dnsparams_t *a)
{
// windows 64-bit
apply_hosts_var_filepath(a, "SystemRoot", "\\SysWOW64\\drivers\\etc\\hosts");
// winnt+
apply_hosts_var_filepath(a, "SystemRoot", "\\system32\\drivers\\etc\\hosts");
// win9x
apply_hosts_var_filepath(a, "WINDIR", "\\hosts");
}
static jdns_dnsparams_t *dnsparams_get_win()
{
int n;
jdns_dnsparams_t *sys_params, *reg_params;
reg_params = dnsparams_get_winreg();
sys_params = dnsparams_get_winsys();
// no sys params? take the reg params then
if(!sys_params)
{
apply_win_hosts_file(reg_params);
return reg_params;
}
// sys params don't have a search list, so merge the domains from
// the registry if possible
if(reg_params)
{
for(n = 0; n < reg_params->domains->count; ++n)
{
jdns_string_t *reg_str = reg_params->domains->item[n];
// don't add dups
if(!dnsparams_have_domain(sys_params, reg_str))
jdns_dnsparams_append_domain(sys_params, reg_str);
}
jdns_dnsparams_delete(reg_params);
}
apply_win_hosts_file(sys_params);
return sys_params;
}
#endif
#ifdef JDNS_OS_UNIX
static jdns_dnsparams_t *dnsparams_get_unixfiles()
{
FILE *f;
int n;
jdns_dnsparams_t *params;
jdns_string_t *line, *simp;
jdns_stringlist_t *parts;
params = jdns_dnsparams_new();
f = jdns_fopen("/etc/resolv.conf", "r");
if(!f)
return params;
while(1)
{
line = file_nextline(f);
if(!line)
break;
// truncate at comment
n = string_indexOf(line, '#', 0);
if(n != -1)
{
line->size = n;
line->data[n] = 0;
}
simp = string_simplify(line);
jdns_string_delete(line);
parts = string_split(simp, ' ');
jdns_string_delete(simp);
if(parts->count < 2)
{
jdns_stringlist_delete(parts);
continue;
}
simp = string_tolower(parts->item[0]);
if(strcmp((char *)simp->data, "nameserver") == 0)
{
jdns_address_t *addr = jdns_address_new();
jdns_address_set_cstr(addr, (const char *)parts->item[1]->data);
jdns_dnsparams_append_nameserver(params, addr, JDNS_UNICAST_PORT);
jdns_address_delete(addr);
}
else if(strcmp((char *)simp->data, "search") == 0)
{
for(n = 1; n < parts->count; ++n)
{
jdns_dnsparams_append_domain(params, parts->item[n]);
}
}
else if(strcmp((char *)simp->data, "domain") == 0)
{
jdns_dnsparams_append_domain(params, parts->item[1]);
}
jdns_string_delete(simp);
jdns_stringlist_delete(parts);
}
fclose(f);
return params;
}
#if defined(__GLIBC__) && ((__GLIBC__ > 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 3)))
# define JDNS_MODERN_RES_API
#endif
#ifndef JDNS_MODERN_RES_API
typedef int (*res_init_func)();
static int my_res_init()
{
#ifdef JDNS_OS_MAC
res_init_func mac_res_init;
// look up res_init in the system library (qt does this, not sure why)
mac_res_init = (res_init_func)dlsym(RTLD_NEXT, "res_init");
if(!mac_res_init)
return -1;
return mac_res_init();
#else
return res_init();
#endif
}
#endif
// on some platforms, __res_state_ext exists as a struct but it is not
// a define, so the #ifdef doesn't work. as a workaround, we'll explicitly
// specify the platforms that have __res_state_ext
//#ifdef __res_state_ext
#if defined(JDNS_OS_MAC) || defined(JDNS_OS_FREEBSD) || \
defined(JDNS_OS_NETBSD) || defined (JDNS_OS_SOLARIS)
# define USE_EXTEXT
#endif
static jdns_dnsparams_t *dnsparams_get_unixsys()
{
int n;
jdns_dnsparams_t *params;
#ifdef JDNS_MODERN_RES_API
struct __res_state res;
memset(&res, 0, sizeof(struct __res_state));
n = res_ninit(&res);
#define RESVAR res
#else
n = my_res_init();
#define RESVAR _res
#endif
params = jdns_dnsparams_new();
// error initializing?
if(n == -1)
return params;
// nameservers - ipv6
for(n = 0; n < MAXNS && n < RESVAR._u._ext.nscount; ++n)
{
jdns_address_t *addr;
struct sockaddr_in6 *sa6;
#ifdef USE_EXTEXT
sa6 = ((struct sockaddr_in6 *)RESVAR._u._ext.ext) + n;
#else
sa6 = RESVAR._u._ext.nsaddrs[n];
#endif
if(sa6 == NULL)
continue;
addr = jdns_address_new();
jdns_address_set_ipv6(addr, sa6->sin6_addr.s6_addr);
jdns_dnsparams_append_nameserver(params, addr, JDNS_UNICAST_PORT);
jdns_address_delete(addr);
}
// nameservers - ipv4
for(n = 0; n < MAXNS && n < RESVAR.nscount; ++n)
{
jdns_address_t *addr = jdns_address_new();
jdns_address_set_ipv4(addr, ntohl(RESVAR.nsaddr_list[n].sin_addr.s_addr));
jdns_dnsparams_append_nameserver(params, addr, JDNS_UNICAST_PORT);
jdns_address_delete(addr);
}
// domain name
if(strlen(RESVAR.defdname) > 0)
{
jdns_string_t *str;
jdns_string_t *p;
str = jdns_string_new();
jdns_string_set_cstr(str, RESVAR.defdname);
p = string_tolower(str);
jdns_string_delete(str);
str = p;
jdns_dnsparams_append_domain(params, str);
jdns_string_delete(str);
}
// search list
#ifdef MAXDFLSRCH
for(n = 0; n < MAXDFLSRCH && RESVAR.dnsrch[n]; ++n)
{
if(strlen(RESVAR.dnsrch[n]) > 0)
{
jdns_string_t *str;
jdns_string_t *p;
str = jdns_string_new();
jdns_string_set_cstr(str, RESVAR.dnsrch[n]);
p = string_tolower(str);
jdns_string_delete(str);
str = p;
// don't add dups
if(!dnsparams_have_domain(params, str))
jdns_dnsparams_append_domain(params, str);
jdns_string_delete(str);
}
}
#endif
return params;
}
static jdns_dnsparams_t *dnsparams_get_unix()
{
jdns_dnsparams_t *params;
// prefer system calls over files
params = dnsparams_get_unixsys();
if(params->nameservers->count == 0)
{
jdns_dnsparams_delete(params);
params = dnsparams_get_unixfiles();
}
apply_hosts_file(params, "/etc/hosts");
return params;
}
#endif
jdns_dnsparams_t *jdns_system_dnsparams()
{
#ifdef JDNS_OS_WIN
return dnsparams_get_win();
#else
return dnsparams_get_unix();
#endif
}

File diff suppressed because it is too large Load Diff

@ -1,596 +0,0 @@
/*
* Copyright (C) 2005 Justin Karneges
*
* 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.
*/
#include <QtCore>
#include <QtNetwork>
#include "qjdns.h"
QString dataToString(const QByteArray &buf)
{
QString out;
for(int n = 0; n < buf.size(); ++n)
{
unsigned char c = (unsigned char)buf[n];
if(c == '\\')
out += "\\\\";
else if(c >= 0x20 && c < 0x7f)
out += c;
else
out += QString().sprintf("\\x%02x", (unsigned int)c);
}
return out;
}
void print_record(const QJDns::Record &r)
{
switch(r.type)
{
case QJDns::A:
printf(" A: [%s] (ttl=%d)\n", qPrintable(r.address.toString()), r.ttl);
break;
case QJDns::Aaaa:
printf(" AAAA: [%s] (ttl=%d)\n", qPrintable(r.address.toString()), r.ttl);
break;
case QJDns::Mx:
printf(" MX: [%s] priority=%d (ttl=%d)\n", r.name.data(), r.priority, r.ttl);
break;
case QJDns::Srv:
printf(" SRV: [%s] port=%d priority=%d weight=%d (ttl=%d)\n", r.name.data(), r.port, r.priority, r.weight, r.ttl);
break;
case QJDns::Cname:
printf(" CNAME: [%s] (ttl=%d)\n", r.name.data(), r.ttl);
break;
case QJDns::Ptr:
printf(" PTR: [%s] (ttl=%d)\n", r.name.data(), r.ttl);
break;
case QJDns::Txt:
{
printf(" TXT: count=%d (ttl=%d)\n", r.texts.count(), r.ttl);
for(int n = 0; n < r.texts.count(); ++n)
printf(" len=%d [%s]\n", r.texts[n].size(), qPrintable(dataToString(r.texts[n])));
break;
}
case QJDns::Hinfo:
printf(" HINFO: [%s] [%s] (ttl=%d)\n", r.cpu.data(), r.os.data(), r.ttl);
break;
case QJDns::Ns:
printf(" NS: [%s] (ttl=%d)\n", r.name.data(), r.ttl);
break;
default:
printf(" (Unknown): type=%d, size=%d (ttl=%d)\n", r.type, r.rdata.size(), r.ttl);
break;
}
}
class App : public QObject
{
Q_OBJECT
public:
bool opt_debug, opt_ipv6, opt_quit;
int quit_time;
QString mode, type, name, ipaddr;
QStringList nslist;
QList<QJDns::Record> pubitems;
QJDns jdns;
int req_id;
App()
{
connect(&jdns, SIGNAL(resultsReady(int, const QJDns::Response &)), SLOT(jdns_resultsReady(int, const QJDns::Response &)));
connect(&jdns, SIGNAL(published(int)), SLOT(jdns_published(int)));
connect(&jdns, SIGNAL(error(int, QJDns::Error)), SLOT(jdns_error(int, QJDns::Error)));
connect(&jdns, SIGNAL(shutdownFinished()), SLOT(jdns_shutdownFinished()));
connect(&jdns, SIGNAL(debugLinesReady()), SLOT(jdns_debugLinesReady()));
}
~App()
{
}
public slots:
void start()
{
if(mode == "uni")
{
if(!jdns.init(QJDns::Unicast, opt_ipv6 ? QHostAddress::AnyIPv6 : QHostAddress::Any))
{
jdns_debugLinesReady();
printf("unable to bind\n");
emit quit();
return;
}
QList<QJDns::NameServer> addrs;
for(int n = 0; n < nslist.count(); ++n)
{
QJDns::NameServer host;
QString str = nslist[n];
if(str == "mul")
{
if(opt_ipv6)
host.address = QHostAddress("FF02::FB");
else
host.address = QHostAddress("224.0.0.251");
host.port = 5353;
}
else
{
int at = str.indexOf(';');
if(at != -1)
{
host.address = QHostAddress(str.mid(0, at));
host.port = str.mid(at + 1).toInt();
}
else
{
host.address = QHostAddress(str);
}
}
if(host.address.isNull() || host.port <= 0)
{
printf("bad nameserver: [%s]\n", qPrintable(nslist[n]));
emit quit();
return;
}
addrs += host;
}
if(addrs.isEmpty())
addrs = QJDns::systemInfo().nameServers;
if(addrs.isEmpty())
{
printf("no nameservers were detected or specified\n");
emit quit();
return;
}
jdns.setNameServers(addrs);
}
else
{
if(!jdns.init(QJDns::Multicast, opt_ipv6 ? QHostAddress::AnyIPv6 : QHostAddress::Any))
{
jdns_debugLinesReady();
printf("unable to bind\n");
emit quit();
return;
}
}
if(mode == "uni" || mode == "mul")
{
int x = QJDns::A;
if(type == "ptr")
x = QJDns::Ptr;
else if(type == "srv")
x = QJDns::Srv;
else if(type == "a")
x = QJDns::A;
else if(type == "aaaa")
x = QJDns::Aaaa;
else if(type == "mx")
x = QJDns::Mx;
else if(type == "txt")
x = QJDns::Txt;
else if(type == "hinfo")
x = QJDns::Hinfo;
else if(type == "cname")
x = QJDns::Cname;
else if(type == "any")
x = QJDns::Any;
else
{
bool ok;
int y = type.toInt(&ok);
if(ok)
x = y;
}
req_id = jdns.queryStart(name.toLatin1(), x);
printf("[%d] Querying for [%s] type=%d ...\n", req_id, qPrintable(name), x);
}
else // publish
{
for(int n = 0; n < pubitems.count(); ++n)
{
const QJDns::Record &rr = pubitems[n];
QJDns::PublishMode m = QJDns::Unique;
if(rr.type == QJDns::Ptr)
m = QJDns::Shared;
int id = jdns.publishStart(m, rr);
printf("[%d] Publishing [%s] type=%d ...\n", id, rr.owner.data(), rr.type);
}
}
if(opt_quit)
QTimer::singleShot(quit_time * 1000, this, SLOT(doShutdown()));
}
signals:
void quit();
private slots:
void jdns_resultsReady(int id, const QJDns::Response &results)
{
printf("[%d] Results\n", id);
for(int n = 0; n < results.answerRecords.count(); ++n)
print_record(results.answerRecords[n]);
if(mode == "uni")
jdns.shutdown();
}
void jdns_published(int id)
{
printf("[%d] Published\n", id);
}
void jdns_error(int id, QJDns::Error e)
{
QString str;
if(e == QJDns::ErrorGeneric)
str = "Generic";
else if(e == QJDns::ErrorNXDomain)
str = "NXDomain";
else if(e == QJDns::ErrorTimeout)
str = "Timeout";
else if(e == QJDns::ErrorConflict)
str = "Conflict";
printf("[%d] Error: %s\n", id, qPrintable(str));
jdns.shutdown();
}
void jdns_shutdownFinished()
{
emit quit();
}
void jdns_debugLinesReady()
{
QStringList lines = jdns.debugLines();
if(opt_debug)
{
for(int n = 0; n < lines.count(); ++n)
printf("jdns: %s\n", qPrintable(lines[n]));
}
}
void doShutdown()
{
jdns.shutdown();
}
};
#include "main.moc"
void usage()
{
printf("usage: jdns (options) uni [type] [name] (nameserver(;port)|mul ...)\n");
printf(" jdns (options) mul [type] [name]\n");
printf(" jdns (options) pub [items ...]\n");
printf(" jdns sys\n");
printf("\n");
printf("options:\n");
printf(" -d show debug output\n");
printf(" -6 use ipv6\n");
printf(" -q x quit x seconds after starting\n");
printf("\n");
printf("uni/mul types: a aaaa ptr srv mx txt hinfo cname any\n");
printf("pub items: ptr:name,answer srv:name,answer,port a:name,ipaddr\n");
printf(" txt:name,str0,...,strn aaaa:name,ipaddr\n");
printf("\n");
printf("examples:\n");
printf(" jdns uni a jabber.org 192.168.0.1\n");
printf(" jdns uni srv _xmpp-client._tcp.jabber.org 192.168.0.1;53\n");
printf(" jdns uni 10 user@host._presence._tcp.local mul\n");
printf(" jdns mul a foobar.local\n");
printf(" jdns mul ptr _services._dns-sd._udp.local\n");
printf(" jdns pub a:mybox.local.,192.168.0.55\n");
printf("\n");
}
int main(int argc, char **argv)
{
QCoreApplication app(argc, argv);
if(argc < 2)
{
usage();
return 1;
}
// get args
QStringList args;
for(int n = 1; n < argc; ++n)
args += QString(argv[n]);
bool opt_debug = false;
bool opt_ipv6 = false;
bool opt_quit = false;
int quit_time = 0;
QString mode, type, name, ipaddr;
QStringList nslist;
QList<QJDns::Record> pubitems;
// options
for(int n = 0; n < args.count(); ++n)
{
if(args[n].left(1) == "-")
{
if(args[n] == "-d")
opt_debug = true;
else if(args[n] == "-6")
opt_ipv6 = true;
else if(args[n] == "-q")
{
if(n + 1 >= args.count())
{
printf("need to specify number of seconds\n");
usage();
return 1;
}
int x = args[n + 1].toInt();
if(x < 1)
x = 30;
opt_quit = true;
quit_time = x;
args.removeAt(n + 1);
}
else
{
printf("bad option\n");
usage();
return 1;
}
args.removeAt(n);
--n; // adjust position
}
}
mode = args[0];
if(mode == "uni" || mode == "mul")
{
if(args.count() < 3)
{
printf("not enough args\n");
usage();
return 1;
}
type = args[1];
name = args[2];
if(mode == "uni")
{
for(int n = 3; n < args.count(); ++n)
nslist += QString(args[n]);
}
}
else if(mode == "pub")
{
if(args.count() < 2)
{
printf("not enough args\n");
usage();
return 1;
}
for(int n = 1; n < args.count(); ++n)
{
QString arg = args[n];
int at = arg.indexOf(':');
if(at == -1)
{
printf("missing colon\n");
usage();
return 1;
}
QString type = arg.mid(0, at).toLower();
QString val = arg.mid(at + 1);
if(type == "a")
{
QStringList list = val.split(',');
if(list.count() != 2)
{
printf("bad format for A type\n");
usage();
return 1;
}
QHostAddress host(list[1]);
if(host.isNull() || host.protocol() != QAbstractSocket::IPv4Protocol)
{
printf("bad format for A type IP address\n");
usage();
return 1;
}
QJDns::Record rec;
rec.owner = list[0].toLatin1();
rec.type = QJDns::A;
rec.ttl = 120;
rec.haveKnown = true;
rec.address = host;
pubitems += rec;
}
else if(type == "aaaa")
{
QStringList list = val.split(',');
if(list.count() != 2)
{
printf("bad format for AAAA type\n");
usage();
return 1;
}
QHostAddress host(list[1]);
if(host.isNull() || host.protocol() != QAbstractSocket::IPv6Protocol)
{
printf("bad format for AAAA type IP address\n");
usage();
return 1;
}
QJDns::Record rec;
rec.owner = list[0].toLatin1();
rec.type = QJDns::Aaaa;
rec.ttl = 120;
rec.haveKnown = true;
rec.address = host;
pubitems += rec;
}
else if(type == "srv")
{
QStringList list = val.split(',');
if(list.count() != 3)
{
printf("bad format for SRV type\n");
usage();
return 1;
}
QJDns::Record rec;
rec.owner = list[0].toLatin1();
rec.type = QJDns::Srv;
rec.ttl = 120;
rec.haveKnown = true;
rec.name = list[1].toLatin1();
rec.priority = 0;
rec.weight = 0;
rec.port = list[2].toInt();
pubitems += rec;
}
else if(type == "ptr")
{
QStringList list = val.split(',');
if(list.count() != 2)
{
printf("bad format for PTR type\n");
usage();
return 1;
}
QJDns::Record rec;
rec.owner = list[0].toLatin1();
rec.type = QJDns::Ptr;
rec.ttl = 120;
rec.haveKnown = true;
rec.name = list[1].toLatin1();
pubitems += rec;
}
else if(type == "txt")
{
QStringList list = val.split(',');
QList<QByteArray> texts;
for(int n = 1; n < list.count(); ++n)
texts += list[n].toLatin1();
QJDns::Record rec;
rec.owner = list[0].toLatin1();
rec.type = QJDns::Txt;
rec.ttl = 120;
rec.haveKnown = true;
rec.texts = texts;
pubitems += rec;
}
else
{
printf("bad record type [%s]\n", qPrintable(type));
usage();
return 1;
}
}
}
else if(mode == "sys")
{
QJDns::SystemInfo info = QJDns::systemInfo();
printf("DNS System Information\n");
printf(" Name Servers:\n");
if(!info.nameServers.isEmpty())
{
for(int n = 0; n < info.nameServers.count(); ++n)
printf(" %s\n", qPrintable(info.nameServers[n].address.toString()));
}
else
printf(" (None)\n");
printf(" Domains:\n");
if(!info.domains.isEmpty())
{
for(int n = 0; n < info.domains.count(); ++n)
printf(" [%s]\n", info.domains[n].data());
}
else
printf(" (None)\n");
printf(" Hosts:\n");
if(!info.hosts.isEmpty())
{
for(int n = 0; n < info.hosts.count(); ++n)
{
const QJDns::DnsHost &h = info.hosts[n];
printf(" [%s] -> %s\n", h.name.data(), qPrintable(h.address.toString()));
}
}
else
printf(" (None)\n");
QHostAddress addr;
printf("Primary IPv4 Multicast Address: ");
addr = QJDns::detectPrimaryMulticast(QHostAddress::Any);
if(!addr.isNull())
printf("%s\n", qPrintable(addr.toString()));
else
printf("(None)\n");
printf("Primary IPv6 Multicast Address: ");
addr = QJDns::detectPrimaryMulticast(QHostAddress::AnyIPv6);
if(!addr.isNull())
printf("%s\n", qPrintable(addr.toString()));
else
printf("(None)\n");
return 0;
}
else
{
usage();
return 1;
}
App a;
a.opt_debug = opt_debug;
a.opt_ipv6 = opt_ipv6;
a.opt_quit = opt_quit;
a.quit_time = quit_time;
a.mode = mode;
a.type = type.toLower();
a.name = name;
a.ipaddr = ipaddr;
a.nslist = nslist;
a.pubitems = pubitems;
QObject::connect(&a, SIGNAL(quit()), &app, SLOT(quit()));
QTimer::singleShot(0, &a, SLOT(start()));
app.exec();
return 0;
}

@ -1,638 +0,0 @@
/*
* Copyright (C) 2005-2008 Justin Karneges
*
* 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.
*/
#include "qjdns.h"
#include <time.h>
#include "qjdns_sock.h"
#include "qjdns_helpers.h"
#include "jdns.h"
// for fprintf
#include <stdio.h>
//----------------------------------------------------------------------------
// QJDns::NameServer
//----------------------------------------------------------------------------
QJDns::NameServer::NameServer()
{
port = JDNS_UNICAST_PORT;
}
//----------------------------------------------------------------------------
// QJDns::Record
//----------------------------------------------------------------------------
QJDns::Record::Record()
{
ttl = 0;
type = -1;
haveKnown = false;
}
bool QJDns::Record::doVerify() const
{
jdns_rr_t *rr = export_record(*this);
int ok = jdns_rr_verify(rr);
jdns_rr_delete(rr);
return (ok ? true : false);
}
//----------------------------------------------------------------------------
// QJDns
//----------------------------------------------------------------------------
QJDns::Private::Private(QJDns *_q) :
QObject(_q),
q(_q),
stepTrigger(this),
debugTrigger(this),
stepTimeout(this),
pErrors(0),
pPublished(0),
pResponses(0)
{
sess = 0;
shutting_down = false;
new_debug_strings = false;
pending = 0;
connect(&stepTrigger, SIGNAL(timeout()), SLOT(doNextStepSlot()));
stepTrigger.setSingleShot(true);
connect(&debugTrigger, SIGNAL(timeout()), SLOT(doDebug()));
debugTrigger.setSingleShot(true);
connect(&stepTimeout, SIGNAL(timeout()), SLOT(st_timeout()));
stepTimeout.setSingleShot(true);
my_srand();
clock.start();
}
QJDns::Private::~Private()
{
cleanup();
}
void QJDns::Private::cleanup()
{
if(sess)
{
jdns_session_delete(sess);
sess = 0;
}
shutting_down = false;
pending = 0;
// it is safe to delete the QUdpSocket objects here without
// deleteLater, since this code path never occurs when
// a signal from those objects is on the stack
qDeleteAll(socketForHandle);
socketForHandle.clear();
handleForSocket.clear();
stepTrigger.stop();
stepTimeout.stop();
need_handle = 0;
}
bool QJDns::Private::init(QJDns::Mode _mode, const QHostAddress &address)
{
mode = _mode;
jdns_callbacks_t callbacks;
callbacks.app = this;
callbacks.time_now = cb_time_now;
callbacks.rand_int = cb_rand_int;
callbacks.debug_line = cb_debug_line;
callbacks.udp_bind = cb_udp_bind;
callbacks.udp_unbind = cb_udp_unbind;
callbacks.udp_read = cb_udp_read;
callbacks.udp_write = cb_udp_write;
sess = jdns_session_new(&callbacks);
jdns_set_hold_ids_enabled(sess, 1);
next_handle = 1;
need_handle = false;
int ret;
jdns_address_t *baddr = qt2addr(address);
if(mode == Unicast)
{
ret = jdns_init_unicast(sess, baddr, 0);
}
else
{
jdns_address_t *maddr;
if(address.protocol() == QAbstractSocket::IPv6Protocol)
maddr = jdns_address_multicast6_new();
else
maddr = jdns_address_multicast4_new();
ret = jdns_init_multicast(sess, baddr, JDNS_MULTICAST_PORT, maddr);
jdns_address_delete(maddr);
}
jdns_address_delete(baddr);
if(!ret)
{
jdns_session_delete(sess);
sess = 0;
return false;
}
return true;
}
void QJDns::Private::setNameServers(const QList<NameServer> &nslist)
{
jdns_nameserverlist_t *addrs = jdns_nameserverlist_new();
for(int n = 0; n < nslist.count(); ++n)
{
jdns_address_t *addr = qt2addr(nslist[n].address);
jdns_nameserverlist_append(addrs, addr, nslist[n].port);
jdns_address_delete(addr);
}
jdns_set_nameservers(sess, addrs);
jdns_nameserverlist_delete(addrs);
}
void QJDns::Private::process()
{
if(!stepTrigger.isActive())
{
stepTimeout.stop();
stepTrigger.start();
}
}
void QJDns::Private::processDebug()
{
new_debug_strings = true;
if(!debugTrigger.isActive())
debugTrigger.start();
}
void QJDns::Private::doNextStep()
{
if(shutting_down && complete_shutdown)
{
cleanup();
emit q->shutdownFinished();
return;
}
QPointer<QObject> self = this;
int ret = jdns_step(sess);
QList<LateError> errors;
QList<int> published;
QList<LateResponse> responses;
bool finish_shutdown = false;
pErrors = &errors;
pPublished = &published;
pResponses = &responses;
while(1)
{
jdns_event_t *e = jdns_next_event(sess);
if(!e)
break;
if(e->type == JDNS_EVENT_SHUTDOWN)
{
finish_shutdown = true;
}
else if(e->type == JDNS_EVENT_PUBLISH)
{
if(e->status != JDNS_STATUS_SUCCESS)
{
QJDns::Error error;
if(e->status == JDNS_STATUS_CONFLICT)
error = QJDns::ErrorConflict;
else
error = QJDns::ErrorGeneric;
LateError le;
le.source_type = 1;
le.id = e->id;
le.error = error;
errors += le;
}
else
{
published += e->id;
}
}
else if(e->type == JDNS_EVENT_RESPONSE)
{
if(e->status != JDNS_STATUS_SUCCESS)
{
QJDns::Error error;
if(e->status == JDNS_STATUS_NXDOMAIN)
error = QJDns::ErrorNXDomain;
else if(e->status == JDNS_STATUS_TIMEOUT)
error = QJDns::ErrorTimeout;
else
error = QJDns::ErrorGeneric;
LateError le;
le.source_type = 0;
le.id = e->id;
le.error = error;
errors += le;
}
else
{
QJDns::Response out_response;
for(int n = 0; n < e->response->answerCount; ++n)
out_response.answerRecords += import_record(e->response->answerRecords[n]);
LateResponse lr;
lr.id = e->id;
lr.response = out_response;
if(mode == Unicast)
lr.do_cancel = true;
else
lr.do_cancel = false;
responses += lr;
}
}
jdns_event_delete(e);
}
if(ret & JDNS_STEP_TIMER)
stepTimeout.start(jdns_next_timer(sess));
else
stepTimeout.stop();
need_handle = (ret & JDNS_STEP_HANDLE);
// read the lists safely enough so that items can be deleted
// behind our back
while(!errors.isEmpty())
{
LateError i = errors.takeFirst();
if(i.source_type == 0)
jdns_cancel_query(sess, i.id);
else
jdns_cancel_publish(sess, i.id);
emit q->error(i.id, i.error);
if(!self)
return;
}
while(!published.isEmpty())
{
int i = published.takeFirst();
emit q->published(i);
if(!self)
return;
}
while(!responses.isEmpty())
{
LateResponse i = responses.takeFirst();
if(i.do_cancel)
jdns_cancel_query(sess, i.id);
emit q->resultsReady(i.id, i.response);
if(!self)
return;
}
if(finish_shutdown)
{
// if we have pending udp packets to write, stick around
if(pending > 0)
{
pending_wait = true;
}
else
{
complete_shutdown = true;
process();
}
}
pErrors = 0;
pPublished = 0;
pResponses = 0;
}
void QJDns::Private::removeCancelled(int id)
{
if(pErrors)
{
for(int n = 0; n < pErrors->count(); ++n)
{
if(pErrors->at(n).id == id)
{
pErrors->removeAt(n);
--n; // adjust position
}
}
}
if(pPublished)
{
for(int n = 0; n < pPublished->count(); ++n)
{
if(pPublished->at(n) == id)
{
pPublished->removeAt(n);
--n; // adjust position
}
}
}
if(pResponses)
{
for(int n = 0; n < pResponses->count(); ++n)
{
if(pResponses->at(n).id == id)
{
pResponses->removeAt(n);
--n; // adjust position
}
}
}
}
void QJDns::Private::udp_readyRead()
{
QUdpSocket *sock = (QUdpSocket *)sender();
int handle = handleForSocket.value(sock);
if(need_handle)
{
jdns_set_handle_readable(sess, handle);
process();
}
else
{
// eat packet
QByteArray buf(4096, 0);
QHostAddress from_addr;
quint16 from_port;
sock->readDatagram(buf.data(), buf.size(), &from_addr, &from_port);
}
}
void QJDns::Private::udp_bytesWritten(qint64)
{
if(pending > 0)
{
--pending;
if(shutting_down && pending_wait && pending == 0)
{
pending_wait = false;
complete_shutdown = true;
process();
}
}
}
void QJDns::Private::st_timeout()
{
doNextStep();
}
void QJDns::Private::doNextStepSlot()
{
doNextStep();
}
void QJDns::Private::doDebug()
{
if(new_debug_strings)
{
new_debug_strings = false;
if(!debug_strings.isEmpty())
emit q->debugLinesReady();
}
}
QJDns::QJDns(QObject *parent)
:QObject(parent)
{
d = new Private(this);
}
QJDns::~QJDns()
{
delete d;
}
bool QJDns::init(Mode mode, const QHostAddress &address)
{
return d->init(mode, address);
}
void QJDns::shutdown()
{
d->shutting_down = true;
d->pending_wait = false;
d->complete_shutdown = false;
jdns_shutdown(d->sess);
d->process();
}
QStringList QJDns::debugLines()
{
QStringList tmp = d->debug_strings;
d->debug_strings.clear();
return tmp;
}
QJDns::SystemInfo QJDns::systemInfo()
{
SystemInfo out;
jdns_dnsparams_t *params = jdns_system_dnsparams();
for(int n = 0; n < params->nameservers->count; ++n)
{
NameServer ns;
ns.address = addr2qt(params->nameservers->item[n]->address);
out.nameServers += ns;
}
for(int n = 0; n < params->domains->count; ++n)
out.domains += str2qt(params->domains->item[n]);
for(int n = 0; n < params->hosts->count; ++n)
{
DnsHost h;
h.name = str2qt(params->hosts->item[n]->name);
h.address = addr2qt(params->hosts->item[n]->address);
out.hosts += h;
}
jdns_dnsparams_delete(params);
return out;
}
#define PROBE_BASE 20000
#define PROBE_RANGE 100
QHostAddress QJDns::detectPrimaryMulticast(const QHostAddress &address)
{
my_srand();
QUdpSocket *sock = new QUdpSocket;
QUdpSocket::BindMode mode;
mode |= QUdpSocket::ShareAddress;
mode |= QUdpSocket::ReuseAddressHint;
int port = -1;
for(int n = 0; n < PROBE_RANGE; ++n)
{
if(sock->bind(address, PROBE_BASE + n, mode))
{
port = PROBE_BASE + n;
break;
}
}
if(port == -1)
{
delete sock;
return QHostAddress();
}
jdns_address_t *a;
if(address.protocol() == QAbstractSocket::IPv6Protocol)
a = jdns_address_multicast6_new();
else
a = jdns_address_multicast4_new();
QHostAddress maddr = addr2qt(a);
jdns_address_delete(a);
if(address.protocol() == QAbstractSocket::IPv6Protocol)
{
int x;
if(!qjdns_sock_setMulticast6(sock->socketDescriptor(), maddr.toIPv6Address().c, &x))
{
delete sock;
return QHostAddress();
}
qjdns_sock_setTTL6(sock->socketDescriptor(), 0);
}
else
{
int x;
if(!qjdns_sock_setMulticast4(sock->socketDescriptor(), maddr.toIPv4Address(), &x))
{
delete sock;
return QHostAddress();
}
qjdns_sock_setTTL4(sock->socketDescriptor(), 0);
}
QHostAddress result;
QByteArray out(128, 0);
for(int n = 0; n < out.size(); ++n)
out[n] = rand();
if(sock->writeDatagram(out.data(), out.size(), maddr, port) == -1)
{
delete sock;
return QHostAddress();
}
while(1)
{
if(!sock->waitForReadyRead(1000))
{
fprintf(stderr, "QJDns::detectPrimaryMulticast: timeout while checking %s\n", qPrintable(address.toString()));
delete sock;
return QHostAddress();
}
QByteArray in(128, 0);
QHostAddress from_addr;
quint16 from_port;
int ret = sock->readDatagram(in.data(), in.size(), &from_addr, &from_port);
if(ret == -1)
{
delete sock;
return QHostAddress();
}
if(from_port != port)
continue;
in.resize(ret);
if(in != out)
continue;
result = from_addr;
break;
}
delete sock;
return result;
}
void QJDns::setNameServers(const QList<NameServer> &list)
{
d->setNameServers(list);
}
int QJDns::queryStart(const QByteArray &name, int type)
{
int id = jdns_query(d->sess, (const unsigned char *)name.data(), type);
d->process();
return id;
}
void QJDns::queryCancel(int id)
{
jdns_cancel_query(d->sess, id);
d->removeCancelled(id);
d->process();
}
int QJDns::publishStart(PublishMode m, const Record &record)
{
jdns_rr_t *rr = export_record(record);
int pubmode;
if(m == QJDns::Unique)
pubmode = JDNS_PUBLISH_UNIQUE;
else
pubmode = JDNS_PUBLISH_SHARED;
int id = jdns_publish(d->sess, pubmode, rr);
jdns_rr_delete(rr);
d->process();
return id;
}
void QJDns::publishUpdate(int id, const Record &record)
{
jdns_rr_t *rr = export_record(record);
jdns_update_publish(d->sess, id, rr);
jdns_rr_delete(rr);
d->process();
}
void QJDns::publishCancel(int id)
{
jdns_cancel_publish(d->sess, id);
d->removeCancelled(id);
d->process();
}
//#include "qjdns.moc"

@ -1,160 +0,0 @@
/*
* Copyright (C) 2005,2006 Justin Karneges
*
* 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.
*/
// this is the Qt wrapper to jdns. it requires Qt 4.1+
#ifndef QJDNS_H
#define QJDNS_H
#include <QObject>
#include <QHostAddress>
#include <QTime>
#include <QtNetwork>
class QJDns : public QObject
{
Q_OBJECT
public:
enum Mode
{
Unicast,
Multicast
};
enum PublishMode
{
Unique,
Shared
};
enum Type
{
A = 1,
Aaaa = 28,
Mx = 15,
Srv = 33,
Cname = 5,
Ptr = 12,
Txt = 16,
Hinfo = 13,
Ns = 2,
Any = 255
};
enum Error
{
ErrorGeneric,
ErrorNXDomain, // query only
ErrorTimeout, // query only
ErrorConflict // publish only
};
class NameServer
{
public:
QHostAddress address;
int port;
NameServer();
};
class DnsHost
{
public:
QByteArray name;
QHostAddress address;
};
class SystemInfo
{
public:
QList<NameServer> nameServers;
QList<QByteArray> domains;
QList<DnsHost> hosts;
};
class Record
{
public:
QByteArray owner;
int ttl;
int type;
QByteArray rdata;
bool haveKnown;
// known
QHostAddress address; // for A, Aaaa
QByteArray name; // for Mx, Srv, Cname, Ptr, Ns
int priority; // for Mx, Srv
int weight; // for Srv
int port; // for Srv
QList<QByteArray> texts; // for Txt
QByteArray cpu; // for Hinfo
QByteArray os; // for Hinfo
Record();
bool doVerify() const;
};
class Response
{
public:
QList<Record> answerRecords;
QList<Record> authorityRecords;
QList<Record> additionalRecords;
};
QJDns(QObject *parent = 0);
~QJDns();
bool init(Mode mode, const QHostAddress &address);
void shutdown();
QStringList debugLines();
static SystemInfo systemInfo();
static QHostAddress detectPrimaryMulticast(const QHostAddress &address);
void setNameServers(const QList<NameServer> &list);
int queryStart(const QByteArray &name, int type);
void queryCancel(int id);
// for multicast mode only
int publishStart(PublishMode m, const Record &record);
void publishUpdate(int id, const Record &record);
void publishCancel(int id);
signals:
void resultsReady(int id, const QJDns::Response &results);
void published(int id);
void error(int id, QJDns::Error e);
void shutdownFinished();
void debugLinesReady();
private:
class Private;
friend class Private;
Private *d;
};
#endif

@ -1,471 +0,0 @@
/*
* Copyright (C) 2005-2008 Justin Karneges
*
* 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.
*/
#include "qjdns.h"
#include "qjdns_sock.h"
#include "jdns.h"
#include <QTimer>
#include <QUdpSocket>
#include <QPointer>
#include <QStringList>
#include <QMetaEnum>
#include <time.h>
// safeobj stuff, from qca
class QJDnsSafeTimer : public QObject
{
Q_OBJECT
public:
void releaseAndDeleteLater(QObject *owner, QObject *obj)
{
obj->disconnect(owner);
obj->setParent(0);
obj->deleteLater();
}
QJDnsSafeTimer(QObject *parent = 0) :
QObject(parent)
{
t = new QTimer(this);
connect(t, SIGNAL(timeout()), SIGNAL(timeout()));
}
~QJDnsSafeTimer()
{
releaseAndDeleteLater(this, t);
}
int interval() const { return t->interval(); }
bool isActive() const { return t->isActive(); }
bool isSingleShot() const { return t->isSingleShot(); }
void setInterval(int msec) { t->setInterval(msec); }
void setSingleShot(bool singleShot) { t->setSingleShot(singleShot); }
int timerId() const { return t->timerId(); }
public slots:
void start(int msec) { t->start(msec); }
void start() { t->start(); }
void stop() { t->stop(); }
signals:
void timeout();
private:
QTimer *t;
};
static jdns_string_t *qt2str(const QByteArray &in)
{
jdns_string_t *out = jdns_string_new();
jdns_string_set(out, (const unsigned char *)in.data(), in.size());
return out;
}
static QByteArray str2qt(const jdns_string_t *in)
{
return QByteArray((const char *)in->data, in->size);
}
static void qt2addr_set(jdns_address_t *addr, const QHostAddress &host)
{
if(host.protocol() == QAbstractSocket::IPv6Protocol)
jdns_address_set_ipv6(addr, host.toIPv6Address().c);
else
jdns_address_set_ipv4(addr, host.toIPv4Address());
}
static jdns_address_t *qt2addr(const QHostAddress &host)
{
jdns_address_t *addr = jdns_address_new();
qt2addr_set(addr, host);
return addr;
}
static QHostAddress addr2qt(const jdns_address_t *addr)
{
if(addr->isIpv6)
return QHostAddress(addr->addr.v6);
else
return QHostAddress(addr->addr.v4);
}
static QJDns::Record import_record(const jdns_rr_t *in)
{
QJDns::Record out;
out.owner = QByteArray((const char *)in->owner);
out.ttl = in->ttl;
out.type = in->type;
out.rdata = QByteArray((const char *)in->rdata, in->rdlength);
// known
if(in->haveKnown)
{
int type = in->type;
if(type == QJDns::A || type == QJDns::Aaaa)
{
out.haveKnown = true;
out.address = addr2qt(in->data.address);
}
else if(type == QJDns::Mx)
{
out.haveKnown = true;
out.name = QByteArray((const char *)in->data.server->name);
out.priority = in->data.server->priority;
}
else if(type == QJDns::Srv)
{
out.haveKnown = true;
out.name = QByteArray((const char *)in->data.server->name);
out.priority = in->data.server->priority;
out.weight = in->data.server->weight;
out.port = in->data.server->port;
}
else if(type == QJDns::Cname || type == QJDns::Ptr || type == QJDns::Ns)
{
out.haveKnown = true;
out.name = QByteArray((const char *)in->data.name);
}
else if(type == QJDns::Txt)
{
out.haveKnown = true;
out.texts.clear();
for(int n = 0; n < in->data.texts->count; ++n)
out.texts += str2qt(in->data.texts->item[n]);
}
else if(type == QJDns::Hinfo)
{
out.haveKnown = true;
out.cpu = str2qt(in->data.hinfo.cpu);
out.os = str2qt(in->data.hinfo.os);
}
}
return out;
}
static int my_srand_done = 0;
static void my_srand()
{
if(my_srand_done)
return;
// lame attempt at randomizing without srand
int count = ::time(NULL) % 128;
for(int n = 0; n < count; ++n)
rand();
my_srand_done = 1;
}
static jdns_rr_t *export_record(const QJDns::Record &in)
{
jdns_rr_t *out = jdns_rr_new();
jdns_rr_set_owner(out, (const unsigned char *)in.owner.data());
out->ttl = in.ttl;
// if we have known, use that
if(in.haveKnown)
{
int type = in.type;
if(type == QJDns::A)
{
jdns_address_t *addr = qt2addr(in.address);
jdns_rr_set_A(out, addr);
jdns_address_delete(addr);
}
else if(type == QJDns::Aaaa)
{
jdns_address_t *addr = qt2addr(in.address);
jdns_rr_set_AAAA(out, addr);
jdns_address_delete(addr);
}
else if(type == QJDns::Mx)
{
jdns_rr_set_MX(out, (const unsigned char *)in.name.data(), in.priority);
}
else if(type == QJDns::Srv)
{
jdns_rr_set_SRV(out, (const unsigned char *)in.name.data(), in.port, in.priority, in.weight);
}
else if(type == QJDns::Cname)
{
jdns_rr_set_CNAME(out, (const unsigned char *)in.name.data());
}
else if(type == QJDns::Ptr)
{
jdns_rr_set_PTR(out, (const unsigned char *)in.name.data());
}
else if(type == QJDns::Txt)
{
jdns_stringlist_t *list = jdns_stringlist_new();
for(int n = 0; n < in.texts.count(); ++n)
{
jdns_string_t *str = qt2str(in.texts[n]);
jdns_stringlist_append(list, str);
jdns_string_delete(str);
}
jdns_rr_set_TXT(out, list);
jdns_stringlist_delete(list);
}
else if(type == QJDns::Hinfo)
{
jdns_string_t *cpu = qt2str(in.cpu);
jdns_string_t *os = qt2str(in.os);
jdns_rr_set_HINFO(out, cpu, os);
jdns_string_delete(cpu);
jdns_string_delete(os);
}
else if(type == QJDns::Ns)
{
jdns_rr_set_NS(out, (const unsigned char *)in.name.data());
}
}
else
jdns_rr_set_record(out, in.type, (const unsigned char *)in.rdata.data(), in.rdata.size());
return out;
}
class QJDns::Private : public QObject
{
Q_OBJECT
public:
class LateError
{
public:
int source_type; // 0 for query, 1 for publish
int id;
Error error;
};
class LateResponse
{
public:
int id;
QJDns::Response response;
bool do_cancel;
};
QJDns *q;
QJDns::Mode mode;
jdns_session_t *sess;
bool shutting_down;
QJDnsSafeTimer stepTrigger, debugTrigger;
QJDnsSafeTimer stepTimeout;
QTime clock;
QStringList debug_strings;
bool new_debug_strings;
int next_handle;
bool need_handle;
QHash<int,QUdpSocket*> socketForHandle;
QHash<QUdpSocket*,int> handleForSocket;
int pending;
bool pending_wait;
bool complete_shutdown;
// pointers that will point to things we are currently signalling
// about. when a query or publish is cancelled, we can use these
// pointers to extract anything we shouldn't signal.
QList<LateError> *pErrors;
QList<int> *pPublished;
QList<LateResponse> *pResponses;
Private(QJDns *_q);
~Private();
void cleanup();
bool init(QJDns::Mode _mode, const QHostAddress &address);
void setNameServers(const QList<NameServer> &nslist);
void process();
void processDebug();
void doNextStep();
void removeCancelled(int id);
private slots:
void udp_readyRead();
void udp_bytesWritten(qint64);
void st_timeout();
void doNextStepSlot();
void doDebug();
private:
static int cb_time_now(jdns_session_t *, void *app)
{
QJDns::Private *self = (QJDns::Private *)app;
return self->clock.elapsed();
}
static int cb_rand_int(jdns_session_t *, void *)
{
return rand() % 65536;
}
static void cb_debug_line(jdns_session_t *, void *app, const char *str)
{
QJDns::Private *self = (QJDns::Private *)app;
self->debug_strings += QString::fromLatin1(str);
self->processDebug();
}
static int cb_udp_bind(jdns_session_t *, void *app, const jdns_address_t *addr, int port, const jdns_address_t *maddr)
{
QJDns::Private *self = (QJDns::Private *)app;
// we always pass non-null to jdns_init, so this should be a valid address
QHostAddress host = addr2qt(addr);
QUdpSocket *sock = new QUdpSocket(self);
self->connect(sock, SIGNAL(readyRead()), SLOT(udp_readyRead()));
// use queued for bytesWritten, since qt is evil and emits before writeDatagram returns
qRegisterMetaType<qint64>("qint64");
self->connect(sock, SIGNAL(bytesWritten(qint64)), SLOT(udp_bytesWritten(qint64)), Qt::QueuedConnection);
QUdpSocket::BindMode mode;
mode |= QUdpSocket::ShareAddress;
mode |= QUdpSocket::ReuseAddressHint;
if(!sock->bind(host, port, mode))
{
delete sock;
return 0;
}
if(maddr)
{
int sd = sock->socketDescriptor();
bool ok;
int errorCode;
if(maddr->isIpv6)
ok = qjdns_sock_setMulticast6(sd, maddr->addr.v6, &errorCode);
else
ok = qjdns_sock_setMulticast4(sd, maddr->addr.v4, &errorCode);
if(!ok)
{
delete sock;
self->debug_strings += QString("failed to setup multicast on the socket (errorCode=%1)").arg(errorCode);
self->processDebug();
return 0;
}
if(maddr->isIpv6)
{
qjdns_sock_setTTL6(sd, 255);
qjdns_sock_setIPv6Only(sd);
}
else
qjdns_sock_setTTL4(sd, 255);
}
int handle = self->next_handle++;
self->socketForHandle.insert(handle, sock);
self->handleForSocket.insert(sock, handle);
return handle;
}
static void cb_udp_unbind(jdns_session_t *, void *app, int handle)
{
QJDns::Private *self = (QJDns::Private *)app;
QUdpSocket *sock = self->socketForHandle.value(handle);
if(!sock)
return;
self->socketForHandle.remove(handle);
self->handleForSocket.remove(sock);
delete sock;
}
static int cb_udp_read(jdns_session_t *, void *app, int handle, jdns_address_t *addr, int *port, unsigned char *buf, int *bufsize)
{
QJDns::Private *self = (QJDns::Private *)app;
QUdpSocket *sock = self->socketForHandle.value(handle);
if(!sock)
return 0;
// nothing to read?
if(!sock->hasPendingDatagrams())
return 0;
QHostAddress from_addr;
quint16 from_port;
int ret = sock->readDatagram((char *)buf, *bufsize, &from_addr, &from_port);
if(ret == -1)
return 0;
qt2addr_set(addr, from_addr);
*port = (int)from_port;
*bufsize = ret;
return 1;
}
static int cb_udp_write(jdns_session_t *, void *app, int handle, const jdns_address_t *addr, int port, unsigned char *buf, int bufsize)
{
QJDns::Private *self = (QJDns::Private *)app;
QUdpSocket *sock = self->socketForHandle.value(handle);
if(!sock)
return 0;
QHostAddress host = addr2qt(addr);
int ret = sock->writeDatagram((const char *)buf, bufsize, host, port);
if(ret == -1)
{
// this can happen if the datagram to send is too big. i'm not sure what else
// may cause this. if we return 0, then jdns may try to resend the packet,
// which might not work if it is too large (causing the same error over and
// over). we'll return success to jdns, so the result is as if the packet
// was dropped.
return 1;
}
++self->pending;
return 1;
}
};

@ -1,184 +0,0 @@
/*
* Copyright (C) 2005,2006 Justin Karneges
*
* 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.
*/
#include "qjdns_sock.h"
#include <QtGlobal>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef Q_OS_WIN
# include <winsock2.h>
# include <ws2tcpip.h>
#endif
#ifdef Q_OS_UNIX
# include <sys/time.h>
# include <sys/types.h>
# include <sys/socket.h>
# include <netinet/in.h>
# include <fcntl.h>
# include <errno.h>
# include <signal.h>
# include <arpa/inet.h>
#endif
#ifndef QT_NO_IPV6
# define HAVE_IPV6
# ifndef s6_addr
# define IPPROTO_IPV6 41
struct in6_addr
{
union
{
unsigned char _S6_u8[16];
unsigned short _S6_u16[8];
unsigned long _S6_u32[4];
} _S6_un;
};
# define s6_addr _S6_un._S6_u8
# endif
# ifndef IPV6_JOIN_GROUP
# define IPV6_JOIN_GROUP 12
# define IPV6_MULTICAST_HOPS 10
struct ipv6_mreq
{
struct in6_addr ipv6mr_multiaddr;
unsigned int ipv6mr_interface;
};
# endif
#endif
static int get_last_error()
{
int x;
#ifdef Q_OS_WIN
x = WSAGetLastError();
#else
x = errno;
#endif
return x;
}
bool qjdns_sock_setMulticast4(int s, unsigned long int addr, int *errorCode)
{
int ret;
struct ip_mreq mc;
memset(&mc, 0, sizeof(mc));
mc.imr_multiaddr.s_addr = htonl(addr);
mc.imr_interface.s_addr = INADDR_ANY;
ret = setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const char *)&mc, sizeof(mc));
if(ret != 0)
{
if(errorCode)
*errorCode = get_last_error();
return false;
}
return true;
}
bool qjdns_sock_setMulticast6(int s, unsigned char *addr, int *errorCode)
{
#ifdef HAVE_IPV6
int ret;
struct ipv6_mreq mc;
memset(&mc, 0, sizeof(mc));
memcpy(mc.ipv6mr_multiaddr.s6_addr, addr, 16);
mc.ipv6mr_interface = 0;
ret = setsockopt(s, IPPROTO_IPV6, IPV6_JOIN_GROUP, (const char *)&mc, sizeof(mc));
if(ret != 0)
{
if(errorCode)
*errorCode = get_last_error();
return false;
}
return true;
#else
Q_UNUSED(s);
Q_UNUSED(addr);
Q_UNUSED(errorCode);
return false;
#endif
}
bool qjdns_sock_setTTL4(int s, int ttl)
{
unsigned char cttl;
int ret, ittl;
cttl = ttl;
ittl = ttl;
// IP_MULTICAST_TTL might take 1 byte or 4, try both
ret = setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, (const char *)&cttl, sizeof(cttl));
if(ret != 0)
{
ret = setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, (const char *)&ittl, sizeof(ittl));
if(ret != 0)
return false;
}
return true;
}
bool qjdns_sock_setTTL6(int s, int ttl)
{
#ifdef HAVE_IPV6
unsigned char cttl;
int ret, ittl;
cttl = ttl;
ittl = ttl;
// IPV6_MULTICAST_HOPS might take 1 byte or 4, try both
ret = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (const char *)&cttl, sizeof(cttl));
if(ret != 0)
{
ret = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (const char *)&ittl, sizeof(ittl));
if(ret != 0)
return false;
}
return true;
#else
Q_UNUSED(s);
Q_UNUSED(ttl);
return false;
#endif
}
bool qjdns_sock_setIPv6Only(int s)
{
#if defined(HAVE_IPV6) && defined(IPV6_V6ONLY)
int x = 1;
if(setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&x, sizeof(x)) != 0)
return false;
return true;
#else
Q_UNUSED(s);
return false;
#endif
}

@ -1,33 +0,0 @@
/*
* Copyright (C) 2005,2006 Justin Karneges
*
* 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.
*/
#ifndef QJDNS_SOCK_H
#define QJDNS_SOCK_H
bool qjdns_sock_setMulticast4(int s, unsigned long int addr, int *errorCode);
bool qjdns_sock_setMulticast6(int s, unsigned char *addr, int *errorCode);
bool qjdns_sock_setTTL4(int s, int ttl);
bool qjdns_sock_setTTL6(int s, int ttl);
bool qjdns_sock_setIPv6Only(int s);
#endif

@ -1,17 +0,0 @@
#ifndef JDNS_EXPORT_H
#define JDNS_EXPORT_H
#include <QtCore/QtGlobal>
#ifdef Q_WS_WIN
# if defined(MAKE_JDNS_LIB)
# define JDNS_EXPORT Q_DECL_EXPORT
# else
# define JDNS_EXPORT Q_DECL_IMPORT
# endif
#else
# define JDNS_EXPORT Q_DECL_EXPORT
#endif
#endif

File diff suppressed because it is too large Load Diff

@ -1,513 +0,0 @@
/*
* Copyright (C) 2006,2007 Justin Karneges
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#ifndef JDNSSHARED_H
#define JDNSSHARED_H
#include "qjdns.h"
#include "../jdns_export.h"
class JDnsShared;
class JDnsSharedPrivate;
class JDnsSharedRequestPrivate;
class JDnsSharedDebugPrivate;
/**
\brief Collects debugging information from JDnsShared
\note Iris users should utilize NetNames for DNS capabilities, <i>not</i> JDnsSharedDebug. See the JDnsShared documentation for more information.
JDnsSharedDebug is used to collect debugging information from one or many JDnsShared objects. To use it, simply create it and pass it to JDnsShared::setDebug().
Example use:
\code
JDnsSharedDebug *db = new JDnsSharedDebug;
connect(db, SIGNAL(debugLinesReady(const QStringList &)),
SLOT(db_debugLinesReady(const QStringList &)));
JDnsShared *jdnsShared1 = new JDnsShared(JDnsShared::UnicastInternet);
jdnsShared1->setDebug(db, "U");
JDnsShared *jdnsShared2 = new JDnsShared(JDnsShared::UnicastLocal);
jdnsShared2->setDebug(db, "L");
...
void db_debugLinesReady(const QStringList &lines)
{
foreach(QString line, lines)
printf("%s\n", qPrintable(line));
}
\endcode
JDnsShared reports debug lines with the name and interface number prepended to each line. For example, if there is debug information to report about the second interface added to \a jdnsShared2 in the above example, the lines would be prepended with "L1: ".
Do not destroy JDnsSharedDebug until all of the JDnsShared objects associated with it have been destroyed.
\sa JDnsShared JDnsSharedRequest
*/
class JDnsSharedDebug : public QObject
{
Q_OBJECT
public:
/**
\brief Constructs a new object with the given \a parent
*/
JDnsSharedDebug(QObject *parent = 0);
/**
\brief Destroys the object
*/
virtual ~JDnsSharedDebug();
/**
\brief Read the available debug information
Debug information is reported as a series of lines. The lines are of reasonable length, and so if you're storing a backlog of the most recent debug information, it should be safe to make the cut-off point based on lines.
\sa readyRead
*/
QStringList readDebugLines();
signals:
/**
\brief Emitted when there is debug information to report
\sa readDebugLines
*/
void readyRead();
private:
friend class JDnsShared;
friend class JDnsSharedPrivate;
friend class JDnsSharedDebugPrivate;
JDnsSharedDebugPrivate *d;
};
/**
\brief Performs a DNS operation using JDnsShared
\note Iris users should utilize NetNames for DNS capabilities, <i>not</i> JDnsSharedRequest. See the JDnsShared documentation for more information.
JDnsSharedRequest is used to perform DNS operations on a JDnsShared object. Many requests may be performed simultaneously, such that a single JDnsShared object can be "shared" across the application. Please see the JDnsShared documentation for more complete information about how the overall system works.
Call query() to perform a query. Call publish() (or publishUpdate()) to make DNS records available on the local network (JDnsShared::Multicast mode only). When the operation has something to report, the resultsReady() signal is emitted. Call success() to determine the status of the operation. If success() returns false, then the operation has failed and the reason for the failure can be determined with error(). If success() returns true, then the meaning differs depending on the type of operation being performed:
<ul>
<li>For JDnsShared::UnicastInternet and JDnsShared::UnicastLocal modes, call results() to obtain the records obtained by the query. In these modes, resultsReady() is only emitted once, at which point the operation is no longer active.</li>
<li>For JDnsShared::Multicast, operations are long-lived. Query operations never timeout, and resultsReady() may be emitted multiple times. In order to stop the query, either call cancel() or destroy the JDnsSharedRequest object. Similarly, publishing is long-lived. The record stays published as long as the JDnsSharedRequest has not been cancelled or destroyed.</li>
</ul>
Here is how you might look up an A record:
\code
JDnsSharedRequest *req = new JDnsSharedRequest(jdnsShared);
connect(req, SIGNAL(resultsReady()), SLOT(req_resultsReady()));
req->query("psi-im.org", QJDns::A);
...
void req_resultsReady()
{
if(req->success())
{
// print all of the IP addresses obtained
QList<QJDns::Record> results = req->results();
foreach(QJDns::Record r, results)
{
if(r.type == QJDns::A)
printf("%s\n", qPrintable(r.address.toString());
}
}
else
printf("Error resolving!\n");
}
\endcode
Here is an example of publishing a record:
\code
JDnsSharedRequest *pub = new JDnsSharedRequest(jdnsShared);
connect(pub, SIGNAL(resultsReady()), SLOT(pub_resultsReady()));
// let's publish an A record
QJDns::Record rec;
rec.owner = "SomeComputer.local.";
rec.type = QJDns::A;
rec.ttl = 120;
rec.haveKnown = true;
rec.address = QHostAddress("192.168.0.32");
pub->publish(QJDns::Unique, rec);
...
void pub_resultsReady()
{
if(pub->success())
printf("Record published\n");
else
printf("Error publishing!\n");
}
\endcode
To update an existing record, use publishUpdate():
\code
// the IP address of the host changed, so make a new record
QJDns::Record rec;
rec.owner = "SomeComputer.local.";
rec.type = QJDns::A;
rec.ttl = 120;
rec.haveKnown = true;
rec.address = QHostAddress("192.168.0.64");
// update it
pub->publishUpdate(rec);
\endcode
As a special exception, the address value can be left unspecified for A and Aaaa record types, which tells JDnsShared to substitute the address value with the address of whatever interfaces the record gets published on. This is the preferred way to publish the IP address of your own machine, and in fact it is the only way to do so if you have multiple interfaces, because there will likely be a different IP address value for each interface (the record resolves to a different answer depending on which interface a query comes from).
\code
// let's publish our own A record
QJDns::Record rec;
rec.owner = "MyComputer.local.";
rec.type = QJDns::A;
rec.ttl = 120;
rec.haveKnown = true;
rec.address = QHostAddress();
pub->publish(QJDns::Unique, rec);
\endcode
When you want to unpublish, call cancel() or destroy the JDnsSharedRequest.
\sa JDnsShared
*/
class JDNS_EXPORT JDnsSharedRequest : public QObject
{
Q_OBJECT
public:
/**
\brief Operation type
*/
enum Type
{
Query, ///< Query operation, initiated by query()
Publish ///< Publish operation, initiated by publish() or publishUpdate()
};
/**
\brief Request error
*/
enum Error
{
ErrorNoNet, ///< There are no available network interfaces to operate on. This happens if JDnsShared::addInterface() was not called.
ErrorGeneric, ///< Generic error during the operation.
ErrorNXDomain, ///< The name looked up does not exist.
ErrorTimeout, ///< The operation timed out.
ErrorConflict ///< Attempt to publish an already published unique record.
};
/**
\brief Constructs a new object with the given \a jdnsShared and \a parent
*/
JDnsSharedRequest(JDnsShared *jdnsShared, QObject *parent = 0);
/**
\brief Destroys the object
If there is an active operation, it is cancelled.
*/
~JDnsSharedRequest();
/**
\brief The type of operation being performed
*/
Type type();
/**
\brief Perform a query operation
*/
void query(const QByteArray &name, int type);
/**
\brief Perform a publish operation
*/
void publish(QJDns::PublishMode m, const QJDns::Record &record);
/**
\brief Update a record that is currently published
*/
void publishUpdate(const QJDns::Record &record);
/**
\brief Cancels the current operation
*/
void cancel();
/**
\brief Indicates whether or not the operation was successful
*/
bool success() const;
/**
\brief Returns the reason for error
*/
Error error() const;
/**
\brief Returns the results of the operation
*/
QList<QJDns::Record> results() const;
signals:
/**
\brief Indicates that the operation has something to report
After receiving this signal, call success() to check on the status of the operation, followed by results() or error() as appropriate.
*/
void resultsReady();
private:
friend class JDnsShared;
friend class JDnsSharedPrivate;
friend class JDnsSharedRequestPrivate;
JDnsSharedRequestPrivate *d;
};
/**
\brief Abstraction layer on top of QJDns
\note Iris users should utilize NetNames for DNS capabilities, <i>not</i> JDnsShared. JDnsShared is provided for non-Iris users (and it is also used internally by NetNames). To use JDnsShared by itself, simply drop the jdnsshared.h and jdnsshared.cpp files, along with JDNS, into your project. It is not a full replacement for Qt's Q3Dns, as some tasks are left to you, but it covers most of it.
QJDns supports everything a typical application should ever need in DNS. However, it is expected that modern applications will need to maintain multiple QJDns instances at the same time, and this is where things can get complicated. For example, most applications will want at least two QJDns instances: one for IPv4 unicast and one for IPv6 unicast.
A single JDnsShared object encapsulates multiple instances of QJDns that are related. For example, an IPv4 unicast instance and an IPv6 unicast instance could be coupled within JDnsShared. Then, when a unicast operation is performed on the JDnsShared object, both underlying instances will be queried as appropriate. The application would not need to perform two resolutions itself, nor deal with any related complexity.
Further, individual operations are performed using a separate class called JDnsSharedRequest, eliminating the need for the application to directly interface with a central QJDns object or track integer handles. This makes it easier for individual parts of the application to "share" the same instance (or set of instances) of QJDns, hence the name.
JDnsShared is a thin abstraction. QJDns subtypes (e.g. QJDns::Type, QJDns::Record, etc) are still used with JDnsShared. Because of the duplication of documentation effort between NetNames and QJDns, there is no formal documentation for QJDns. Users of JDnsShared will need to read qjdns.h, although a basic explanation of the elements can be found below.
Types:
<table>
<tr><td>QJDns::Type</td><td>This is a convenience enumeration for common DNS record types. For example: A, Aaaa, Srv, etc. The values directly map to the integer values of the DNS protocol (e.g. Srv = 33). See qjdns.h for all of the types and values.</td></tr>
<tr><td>QJDns::Record</td><td>This class holds a DNS record. The main fields are <i>type</i> (integer type, probably something listed in QJDns::Type), <i>rdata</i> (QByteArray of the record value), and <i>haveKnown</i> (boolean to indicate if a decoded form of the record value is also available). See qjdns.h for the possible known fields. You will most-likely always work with known types. Received records that have a type listed in QJDns::Type are guaranteed to be known and will provide a decoded value. If you are creating a record for publishing, you will need to set <i>owner</i>, <i>ttl</i>, and <i>type</i>. If the type to be published is listed in QJDns::Type, then you will need to set <i>haveKnown</i> to true and set the known fields as appropriate, otherwise you need to set <i>rdata</i>. You do not need to supply an encoded form in <i>rdata</i> for known types, it can be left empty in that case.</td></tr>
<tr><td>QJDns::PublishMode</td><td>This is for Multicast DNS, and can either be Unique or Shared. A shared record can be published by multiple owners (for example, a "_ssh._tcp.local." PTR record might resolve to many different SSH services owned by different machines). A unique record can only have one owner (for example, a "mycomputer.local." A record would resolve to the IP address of the machine that published it). Attempting to publish a record on a network where a unique record is already present will result in a conflict error.</td></tr>
</table>
Functions:
<table>
<tr><td>QJDns::detectPrimaryMulticast()</td><td>Detects a multicast interface. Pass QHostAddress::Any or QHostAddress::AnyIPv6, depending on which type of interface is desired.</td></tr>
</table>
To use JDnsShared, first create an instance of it, set it up by calling addInterface() as necessary, and then use JDnsSharedRequest to perform operations on it.
Here is an example of how to create and set up a JDnsShared object for typical DNS resolution:
\code
// construct
JDnsShared *dns = new JDnsShared(JDnsShared::UnicastInternet);
// add IPv4 and IPv6 interfaces
dns->addInterface(QHostAddress::Any);
dns->addInterface(QHostAddress::AnyIPv6);
// at this point, the object is ready for operation
\endcode
Perform a resolution like this:
\code
JDnsSharedRequest *req = new JDnsSharedRequest(dns);
connect(req, SIGNAL(resultsReady()), SLOT(req_resultsReady()));
req->query("psi-im.org", QJDns::A);
...
void req_resultsReady()
{
if(req->success())
{
// print all of the IP addresses obtained
QList<QJDns::Record> results = req->results();
foreach(QJDns::Record r, results)
{
if(r.type == QJDns::A)
printf("%s\n", qPrintable(r.address.toString());
}
}
else
printf("Error resolving!\n");
}
\endcode
It is important to filter the results as shown in the above example. QJDns guarantees at least one record in the results will be of the type queried for, but there may also be CNAME records present (of course, if the query was for a CNAME type, then the results will only be CNAME records). The recommended approach is to simply filter for the record types desired, as shown, rather than single out CNAME specifically.
When you are finished with a JDnsShared object, it should be shut down before deleting:
\code
connect(dns, SIGNAL(shutdownFinished()), SLOT(dns_shutdownFinished()));
dns->shutdown();
...
void dns_shutdownFinished()
{
delete dns;
}
\endcode
Setting up JDnsShared for UnicastLocal and Multicast mode is done the same way as with UnicastInternet.
For example, here is how Multicast mode could be set up:
\code
// construct
JDnsShared *dns = new JDnsShared(JDnsShared::Multicast);
// add IPv4 interface
QHostAddress addr = QJDns::detectPrimaryMulticast(QHostAddress::Any);
dns->addInterface(addr);
// at this point, the object is ready for operation
\endcode
JDnsShared provides a lot of functionality, but certain aspects of DNS are deemed out of its scope. Below are the responsibilities of the user of JDnsShared, if a more complete DNS behavior is desired:
<ul>
<li>Querying for several "qualified" names. You should first query for the name as provided, and if that fails then query for name + ".domain" (for every domain the computer is in). See domains().</li>
<li>Detecting for ".local" in the name to be queried, and using that to decide whether to query via Multicast/UnicastLocal or UnicastInternet.</li>
<li>For zeroconf/Bonjour, keep in mind that JDnsShared only provides low-level record queries. DNS-SD and any higher layers would be your job.</li>
</ul>
Using a custom DNS implementation, such as JDnsShared, has the drawback that it is difficult to take advantage of platform-specific features (for example, an OS-wide DNS cache or LDAP integration). An application strategy for normal DNS should probably be:
<ul>
<li>If an A or AAAA record is desired, use a native lookup.</li>
<li>Else, if the platform has advanced DNS features already (ie, res_query), use those.</li>
<li>Else, use JDnsShared.</li>
</ul>
For Multicast DNS, awareness of the platform is doubly important. There should only be one Multicast DNS "Responder" per computer, and using JDnsShared in Multicast mode at the same time could result in a conflict. An application strategy for Multicast DNS should be:
<ul>
<li>If the platform has a Multicast DNS daemon installed already, use it somehow.</li>
<li>Else, use JDnsShared.</li>
</ul>
\sa JDnsSharedRequest
*/
class JDNS_EXPORT JDnsShared : public QObject
{
Q_OBJECT
public:
/**
\brief The mode to operate in
*/
enum Mode
{
/**
For regular DNS resolution. In this mode, lookups are performed on all interfaces, and the first returned result is used.
*/
UnicastInternet,
/**
Perform regular DNS resolution using the Multicast DNS address. This is used to resolve large and/or known Multicast DNS names without actually multicasting anything.
*/
UnicastLocal,
/**
Multicast DNS querying and publishing.
\note For Multicast mode, JDnsShared supports up to one interface for each IP version (e.g. one IPv4 interface and one IPv6 interface), and expects the default/primary multicast interface for that IP version to be used.
*/
Multicast
};
/**
\brief Constructs a new object with the given \a mode and \a parent
*/
JDnsShared(Mode mode, QObject *parent = 0);
/**
\brief Destroys the object
*/
~JDnsShared();
/**
\brief Sets the debug object to report to
If a debug object is set using this function, then JDnsShared will send output text to it, prefixing each line with \a name.
*/
void setDebug(JDnsSharedDebug *db, const QString &name);
/**
\brief Adds an interface to operate on
For UnicastInternet and UnicastLocal, these will almost always be QHostAddress::Any or QHostAddress::AnyIPv6 (operate on the default interface for IPv4 or IPv6, respectively).
For Multicast, it is expected that the default/primary multicast interface will be used here. Do not pass QHostAddress::Any (or AnyIPv6) with Multicast mode.
Returns true if the interface was successfully added, otherwise returns false.
*/
bool addInterface(const QHostAddress &addr);
/**
\brief Removes a previously-added interface
*/
void removeInterface(const QHostAddress &addr);
/**
\brief Shuts down the object
This operation primarily exists for Multicast mode, so that any published records have a chance to be unpublished. If the JDnsShared object is simply deleted without performing a shutdown, then published records will linger on the network until their TTLs expire.
When shutdown is complete, the shutdownFinished() signal will be emitted.
*/
void shutdown();
/**
\brief The domains to search in
You should perform a separate resolution for every domain configured on this machine.
*/
static QList<QByteArray> domains();
/**
\brief Performs a blocking shutdown of many JDnsShared instances
This function is a convenient way to shutdown multiple JDnsShared instances synchronously. The internal shutdown procedure uses no more than a few cycles of the eventloop, so it should be safe to call without worry of the application being overly stalled. This function takes ownership of the instances passed to it, and will delete them upon completion.
It is worth noting that this function is implemented without the use of a nested eventloop. All of the JDnsShared instances are moved into a temporary thread to perform the shutdown procedure, which should not cause any unexpected behavior in the current thread.
\code
QList<JDnsShared*> list;
list += jdnsShared_unicast;
list += jdnsShared_multicast;
JDnsShared::waitForShutdown(list);
// collect remaining debug information
QStringList finalDebugLines = jdnsSharedDebug.readDebugLines();
\endcode
*/
static void waitForShutdown(const QList<JDnsShared*> &instances);
signals:
/**
\brief Indicates the object has been shut down
*/
void shutdownFinished();
private:
friend class JDnsSharedRequest;
friend class JDnsSharedPrivate;
JDnsSharedPrivate *d;
};
#endif

@ -1,327 +0,0 @@
/*
* Copyright (C) 2006-2008 Justin Karneges
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
// Note: JDnsShared supports multiple interfaces for multicast, but only one
// for IPv4 and one for IPv6. Sharing multiple interfaces of the same IP
// version for multicast is unfortunately not possible without reworking
// the jdns subsystem.
//
// The reason for this limitation is that in order to do multi-interface
// multicast, you have to do a single bind to Any, and then use special
// functions to determine which interface a packet came from and to
// specify which interface a packet should go out on. Again this is just
// not possible with the current system and the assumptions made by jdns.
// Note: When quering against multiple interfaces with multicast, it is
// possible that different answers for a unique record may be reported
// on each interface. We don't do anything about this.
#include "jdnsshared.h"
#include "jdns_export.h"
#include <QTimer>
#include <QMutex>
#include <QWaitCondition>
#include <QThread>
#include <QStringList>
// safeobj stuff, from qca
class JDNS_EXPORT JDnsSharedSafeTimer : public QObject
{
Q_OBJECT
public:
void JDnsSharedReleaseAndDeleteLater(QObject *owner, QObject *obj)
{
obj->disconnect(owner);
obj->setParent(0);
obj->deleteLater();
}
JDnsSharedSafeTimer(QObject *parent = 0) :
QObject(parent)
{
t = new QTimer(this);
connect(t, SIGNAL(timeout()), SIGNAL(timeout()));
}
~JDnsSharedSafeTimer()
{
JDnsSharedReleaseAndDeleteLater(this, t);
}
int interval() const { return t->interval(); }
bool isActive() const { return t->isActive(); }
bool isSingleShot() const { return t->isSingleShot(); }
void setInterval(int msec) { t->setInterval(msec); }
void setSingleShot(bool singleShot) { t->setSingleShot(singleShot); }
int timerId() const { return t->timerId(); }
public slots:
void start(int msec) { t->start(msec); }
void start() { t->start(); }
void stop() { t->stop(); }
signals:
void timeout();
private:
QTimer *t;
};
// for caching system info
class SystemInfoCache
{
public:
QJDns::SystemInfo info;
QTime time;
};
//----------------------------------------------------------------------------
// Handle
//----------------------------------------------------------------------------
// QJDns uses integer handle ids, but they are only unique within
// the relevant QJDns instance. Since we want our handles to be
// unique across all instances, we'll make an instance/id pair.
class Handle
{
public:
QJDns *jdns;
int id;
Handle() : jdns(0), id(-1)
{
}
Handle(QJDns *_jdns, int _id) : jdns(_jdns), id(_id)
{
}
~Handle() {}
bool operator==(const Handle &a) const
{
if(a.jdns == jdns && a.id == id)
return true;
return false;
}
bool operator!=(const Handle &a) const
{
return !(operator==(a));
}
};
//----------------------------------------------------------------------------
// JDnsShutdown
//----------------------------------------------------------------------------
class JDnsShutdownAgent : public QObject
{
Q_OBJECT
public:
JDnsShutdownAgent();
~JDnsShutdownAgent() {}
void start();
signals:
void started();
};
class JDnsShutdownWorker : public QObject
{
Q_OBJECT
public:
QList<JDnsShared*> list;
JDnsShutdownWorker(const QList<JDnsShared*> &_list);
virtual ~JDnsShutdownWorker() {}
signals:
void finished();
private slots:
void jdns_shutdownFinished();
};
class JDnsShutdown : public QThread
{
Q_OBJECT
public:
QMutex m;
QWaitCondition w;
QList<JDnsShared*> list;
JDnsShutdownAgent *agent;
JDnsShutdownWorker *worker;
int phase;
JDnsShutdown();
~JDnsShutdown() {}
void waitForShutdown(const QList<JDnsShared*> &_list);
protected:
virtual void run();
private slots:
void agent_started();
void worker_finished();
};
//----------------------------------------------------------------------------
// JDnsSharedDebug
//----------------------------------------------------------------------------
class JDnsSharedDebugPrivate : public QObject
{
Q_OBJECT
public:
JDnsSharedDebug *q;
QMutex m;
QStringList lines;
bool dirty;
JDnsSharedDebugPrivate(JDnsSharedDebug *_q);
~JDnsSharedDebugPrivate() {}
void addDebug(const QString &name, const QStringList &_lines);
private slots:
void doUpdate();
};
//----------------------------------------------------------------------------
// JDnsSharedRequest
//----------------------------------------------------------------------------
class JDnsSharedPrivate : public QObject
{
Q_OBJECT
public:
class Instance
{
public:
QJDns *jdns;
QHostAddress addr;
int index;
Instance() : jdns(0)
{
}
};
enum PreprocessMode
{
None, // don't muck with anything
FillInAddress, // for A/AAAA
FillInPtrOwner6, // for PTR, IPv6
FillInPtrOwner4, // for PTR, IPv4
};
JDnsShared *q;
JDnsShared::Mode mode;
bool shutting_down;
JDnsSharedDebug *db;
QString dbname;
QList<Instance*> instances;
QHash<QJDns*,Instance*> instanceForQJDns;
QSet<JDnsSharedRequest*> requests;
QHash<Handle,JDnsSharedRequest*> requestForHandle;
JDnsSharedPrivate(JDnsShared *_q);
~JDnsSharedPrivate() {}
JDnsSharedRequest *findRequest(QJDns *jdns, int id) const;
void jdns_link(QJDns *jdns);
int getNewIndex() const;
void addDebug(int index, const QString &line);
void doDebug(QJDns *jdns, int index);
PreprocessMode determinePpMode(const QJDns::Record &in);
QJDns::Record manipulateRecord(const QJDns::Record &in, PreprocessMode ppmode, bool *modified = 0);
bool addInterface(const QHostAddress &addr);
void removeInterface(const QHostAddress &addr);
void queryStart(JDnsSharedRequest *obj, const QByteArray &name, int qType);
void queryCancel(JDnsSharedRequest *obj);
void publishStart(JDnsSharedRequest *obj, QJDns::PublishMode m, const QJDns::Record &record);
void publishUpdate(JDnsSharedRequest *obj, const QJDns::Record &record);
void publishCancel(JDnsSharedRequest *obj);
public slots:
void late_shutdown();
private slots:
void jdns_resultsReady(int id, const QJDns::Response &results);
void jdns_published(int id);
void jdns_error(int id, QJDns::Error e);
void jdns_shutdownFinished();
void jdns_debugLinesReady();
};
class JDnsSharedRequestPrivate : public QObject
{
Q_OBJECT
public:
JDnsSharedRequest *q;
JDnsSharedPrivate *jsp;
// current action
JDnsSharedRequest::Type type;
QByteArray name;
int qType;
QJDns::PublishMode pubmode;
JDnsSharedPrivate::PreprocessMode ppmode;
QJDns::Record pubrecord;
// a single request might have to perform multiple QJDns operations
QList<Handle> handles;
// keep a list of handles that successfully publish
QList<Handle> published;
// use to weed out dups for multicast
QList<QJDns::Record> queryCache;
bool success;
JDnsSharedRequest::Error error;
QList<QJDns::Record> results;
JDnsSharedSafeTimer lateTimer;
JDnsSharedRequestPrivate(JDnsSharedRequest *_q);
~JDnsSharedRequestPrivate() {};
void resetSession();
private slots:
void lateTimer_timeout();
};