Add xmpp-dox module, support xmpp:// listeners/resolvers
This commit is contained in:
parent
f118f6410b
commit
9c925d2046
1
pom.xml
1
pom.xml
@ -49,6 +49,7 @@
|
|||||||
</properties>
|
</properties>
|
||||||
<modules>
|
<modules>
|
||||||
<module>jDnsProxy</module>
|
<module>jDnsProxy</module>
|
||||||
|
<module>xmpp-dox</module>
|
||||||
</modules>
|
</modules>
|
||||||
<build>
|
<build>
|
||||||
<finalName>${project.artifactId}</finalName>
|
<finalName>${project.artifactId}</finalName>
|
||||||
|
@ -14,18 +14,22 @@ Sample/default configuration is in [jdnsproxy.properties](https://github.com/mop
|
|||||||
Build/run like so:
|
Build/run like so:
|
||||||
```
|
```
|
||||||
mvn clean package
|
mvn clean package
|
||||||
java -jar target/jDnsProxy.jar ./jdnsproxy.properties
|
java -jar jDnsProxy/target/jDnsProxy.jar ./jdnsproxy.properties
|
||||||
|
|
||||||
|
# or with xmpp:// listener+resolver support:
|
||||||
|
java -cp jDnsProxy/target/jDnsProxy.jar:xmpp-dox/target/xmpp-dox.jar com.moparisthebest.dns.DnsProxy xmpp-dox/jdnsproxy.xmpp.resolver.properties
|
||||||
```
|
```
|
||||||
|
|
||||||
Implemented specs:
|
Implemented specs:
|
||||||
|
|
||||||
* [RFC-1035: DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION](https://tools.ietf.org/html/rfc1035)
|
* [RFC-1035: DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION](https://tools.ietf.org/html/rfc1035)
|
||||||
* [RFC-7858: Specification for DNS over Transport Layer Security (TLS)](https://tools.ietf.org/html/rfc7858)
|
* [RFC-7858: Specification for DNS over Transport Layer Security (TLS)](https://tools.ietf.org/html/rfc7858)
|
||||||
* [Draft: DNS Queries over HTTPS](https://tools.ietf.org/html/draft-hoffman-dns-over-https)
|
* [RFC 8484: DNS Queries over HTTPS (DoH)](http://tools.ietf.org/html/rfc8484)
|
||||||
* [Draft: Serving Stale Data to Improve DNS Resiliency](https://tools.ietf.org/html/draft-ietf-dnsop-serve-stale)
|
* [Draft: Serving Stale Data to Improve DNS Resiliency](https://tools.ietf.org/html/draft-ietf-dnsop-serve-stale)
|
||||||
* [RFC-6891: Extension Mechanisms for DNS (EDNS(0))](https://tools.ietf.org/html/rfc6891)
|
* [RFC-6891: Extension Mechanisms for DNS (EDNS(0))](https://tools.ietf.org/html/rfc6891)
|
||||||
* [DNS EDNS0 Option Codes (OPT)](https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-11)
|
* [DNS EDNS0 Option Codes (OPT)](https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-11)
|
||||||
* [RFC-3225: Indicating Resolver Support of DNSSEC](https://tools.ietf.org/html/rfc3225)
|
* [RFC-3225: Indicating Resolver Support of DNSSEC](https://tools.ietf.org/html/rfc3225)
|
||||||
|
* [XEP-xxxx: DNS Queries over XMPP (DoX)](https://xmpp.org/extensions/inbox/dox.html)
|
||||||
|
|
||||||
Use these for quick testing:
|
Use these for quick testing:
|
||||||
```
|
```
|
||||||
|
34
xmpp-dox/dox.md
Normal file
34
xmpp-dox/dox.md
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
XEP-XXXX DNS Queries over XMPP (DoX)
|
||||||
|
------------------------------------
|
||||||
|
|
||||||
|
Submitted [XEP](https://xmpp.org/extensions/inbox/dox.html)
|
||||||
|
|
||||||
|
```
|
||||||
|
# put your jid+pass details in jdns.xmpp.resolver.properties
|
||||||
|
$ java -jar jDnsProxy.jar jdns.xmpp.resolver.properties &
|
||||||
|
$ dig -p5353 @127.0.0.1 +short +tcp example.org
|
||||||
|
93.184.216.34
|
||||||
|
```
|
||||||
|
|
||||||
|
wire format of protocol (this is the request+response from the query above, A record for example.org):
|
||||||
|
|
||||||
|
request:
|
||||||
|
```xml
|
||||||
|
<iq to='dns@example.org/listener' id='27tZp-7' type='get'>
|
||||||
|
<dns xmlns='urn:xmpp:dox:0'>vOIBIAABAAAAAAABB2V4YW1wbGUDb3JnAAABAAEAACkQAAAAAAAADAAKAAj5HO5JuEe+mA</dns>
|
||||||
|
</iq>
|
||||||
|
```
|
||||||
|
|
||||||
|
response:
|
||||||
|
```xml
|
||||||
|
<iq to='dns@example.org/resolver' id='27tZp-7' type='result'>
|
||||||
|
<dns xmlns='urn:xmpp:dox:0'>vOKBoAABAAEAAAABB2V4YW1wbGUDb3JnAAABAAHADAABAAEAAAhjAARduNgiAAApEAAAAAAAAAA</dns>
|
||||||
|
</iq>
|
||||||
|
```
|
||||||
|
|
||||||
|
The content of the dns element is the DNS on-the-wire format is defined in [RFC1035].
|
||||||
|
The body MUST be encoded with base64 [RFC4648].
|
||||||
|
Padding characters for base64 MUST NOT be included.
|
||||||
|
|
||||||
|
[RFC1035]: https://tools.ietf.org/html/rfc1035
|
||||||
|
[RFC4648]: https://tools.ietf.org/html/rfc4648
|
52
xmpp-dox/jdnsproxy.xmpp.listener.properties
Normal file
52
xmpp-dox/jdnsproxy.xmpp.listener.properties
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
# minTtl: rewrite TTLs lower than this to this value, default 600, 0 disables this feature
|
||||||
|
minTtl=600
|
||||||
|
|
||||||
|
# staleResponseTimeout: milliseconds to wait for response to query before serving a stale record if we have it, default 1000
|
||||||
|
staleResponseTimeout=1000
|
||||||
|
# staleResponseTtl: TTL to apply to stale record when above timeout is met and stale record is served, default 10
|
||||||
|
staleResponseTtl=10
|
||||||
|
|
||||||
|
# cacheFile: path to file to persist cache to at an interval
|
||||||
|
cacheFile=dnscache.map
|
||||||
|
# cacheDelayMinutes: how often to write the cache to disk
|
||||||
|
cacheDelayMinutes=60
|
||||||
|
|
||||||
|
# packetQueueLength: maximum requests queued waiting for responses from upstream, all resolvers specified process from this queue, cached responses don't enter this queue, default 100, 0 means unlimited
|
||||||
|
packetQueueLength=100
|
||||||
|
|
||||||
|
# listeners: list of listeners, currently supports tcp:// and udp:// with no options, default 'tcp://127.0.0.1:5353 udp://127.0.0.1:5353'
|
||||||
|
# also supports xmpp:// (DNS-over-XMPP), put IP:port of XMPP server, along with username/password to login with
|
||||||
|
listeners=tcp://127.0.0.1:5353 udp://127.0.0.1:5353 xmpp://208.68.163.210:5222#user=anyjid@example.org/listener;pass=y0urPa55W0rDHere
|
||||||
|
|
||||||
|
# resolvers: list of resolvers with or without options, whitespace separated, options are in fragment separated by ;
|
||||||
|
# currently support tcp:// (regular DNS-over-TCP), tls:// (DNS-over-TLS), http:// https:// (DNS-over-HTTPS)
|
||||||
|
# both tls:// and https:// support option pubKeyPinsSha256 with a comma-separated list of base64 public key hashes like HPKP, not supplying this causes TLS connections to be unauthenticated (vulnerable to MITM)
|
||||||
|
# https:// also validates the hostname for now like a browser would
|
||||||
|
# default 'https://dns.google.com/experimental?ct#name=dns.google.com'
|
||||||
|
resolvers=\
|
||||||
|
tls://89.233.43.71#name=unicast.censurfridns.dk;pubKeyPinsSha256=wikE3jYAA6jQmXYTr/rbHeEPmC78dQwZbQp6WdrseEs= \
|
||||||
|
tls://145.100.185.15#name=dnsovertls.sinodun.com;pubKeyPinsSha256=62lKu9HsDVbyiPenApnc4sfmSYTHOVfFgL3pyB+cBL4= \
|
||||||
|
tls://145.100.185.16#name=dnsovertls1.sinodun.com;pubKeyPinsSha256=cE2ecALeE5B+urJhDrJlVFmf38cJLAvqekONvjvpqUA= \
|
||||||
|
tls://185.49.141.37#name=getdnsapi.net;pubKeyPinsSha256=foxZRnIh9gZpWnl+zEiKa0EJ2rdCGroMWm02gaxSc9Q= \
|
||||||
|
https://dns.google.com/experimental?ct#name=dns.google.com
|
||||||
|
#resolvers=https://dns.google.com/experimental?ct
|
||||||
|
#resolvers=tcp://8.8.4.4:53
|
||||||
|
#resolvers=tls://89.233.43.71:853#pubKeyPinsSha256=wikE3jYAA6jQmXYTr/rbHeEPmC78dQwZbQp6WdrseEs=
|
||||||
|
|
||||||
|
# below here are resolver options that may be defined here and/or at the resolver level, if both resolver level wins
|
||||||
|
|
||||||
|
# proxy: defines a proxy to use for all connections to this resolver, supports socks:// and http://, default none
|
||||||
|
#proxy=socks://127.0.0.1:9050
|
||||||
|
|
||||||
|
# pubKeyPinsSha256: should be on an individual resolver level, specify comma-seperated base64 public key hashes like HPKP, not supplying this causes TLS connections to be unauthenticated (vulnerable to MITM), default none
|
||||||
|
# https:// also validates the hostname for now like a browser would
|
||||||
|
#pubKeyPinsSha256=wikE3jYAA6jQmXYTr/rbHeEPmC78dQwZbQp6WdrseEs=
|
||||||
|
|
||||||
|
# maxRetries: maximum number of times a request is re-queued to be resolved upstream due to failure before giving up, this is maximum retries total, not per-resolver, default resolvers.length * 2
|
||||||
|
#maxRetries=5
|
||||||
|
|
||||||
|
# name: human-readable name of resolver, might end up in logs, default full resolver URI
|
||||||
|
#name=somename
|
||||||
|
|
||||||
|
# connectTimeout: TCP connection timeout in milliseconds to upstream resolver, default 500
|
||||||
|
connectTimeout=500
|
48
xmpp-dox/jdnsproxy.xmpp.resolver.properties
Normal file
48
xmpp-dox/jdnsproxy.xmpp.resolver.properties
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
# minTtl: rewrite TTLs lower than this to this value, default 600, 0 disables this feature
|
||||||
|
minTtl=600
|
||||||
|
|
||||||
|
# staleResponseTimeout: milliseconds to wait for response to query before serving a stale record if we have it, default 1000
|
||||||
|
staleResponseTimeout=1000
|
||||||
|
# staleResponseTtl: TTL to apply to stale record when above timeout is met and stale record is served, default 10
|
||||||
|
staleResponseTtl=10
|
||||||
|
|
||||||
|
# cacheFile: path to file to persist cache to at an interval
|
||||||
|
cacheFile=dnscache.map
|
||||||
|
# cacheDelayMinutes: how often to write the cache to disk
|
||||||
|
cacheDelayMinutes=60
|
||||||
|
|
||||||
|
# packetQueueLength: maximum requests queued waiting for responses from upstream, all resolvers specified process from this queue, cached responses don't enter this queue, default 100, 0 means unlimited
|
||||||
|
packetQueueLength=100
|
||||||
|
|
||||||
|
# listeners: list of listeners, currently supports tcp:// and udp:// with no options, default 'tcp://127.0.0.1:5353 udp://127.0.0.1:5353'
|
||||||
|
listeners=tcp://127.0.0.1:5353 udp://127.0.0.1:5353
|
||||||
|
|
||||||
|
# resolvers: list of resolvers with or without options, whitespace separated, options are in fragment separated by ;
|
||||||
|
# currently support tcp:// (regular DNS-over-TCP), tls:// (DNS-over-TLS), http:// https:// (DNS-over-HTTPS)
|
||||||
|
# both tls:// and https:// support option pubKeyPinsSha256 with a comma-separated list of base64 public key hashes like HPKP, not supplying this causes TLS connections to be unauthenticated (vulnerable to MITM)
|
||||||
|
# https:// also validates the hostname for now like a browser would
|
||||||
|
# default 'https://dns.google.com/experimental?ct#name=dns.google.com'
|
||||||
|
# also supports xmpp:// (DNS-over-XMPP), put IP:port of XMPP server, along with username/password to login with, and a resolverJid
|
||||||
|
resolvers=\
|
||||||
|
xmpp://208.68.163.210:5222#user=anyjid@example.org/resolver;pass=y0urPa55W0rDHere;resolverJid=dns@moparisthebest.com/listener
|
||||||
|
#resolvers=https://dns.google.com/experimental?ct
|
||||||
|
#resolvers=tcp://8.8.4.4:53
|
||||||
|
#resolvers=tls://89.233.43.71:853#pubKeyPinsSha256=wikE3jYAA6jQmXYTr/rbHeEPmC78dQwZbQp6WdrseEs=
|
||||||
|
|
||||||
|
# below here are resolver options that may be defined here and/or at the resolver level, if both resolver level wins
|
||||||
|
|
||||||
|
# proxy: defines a proxy to use for all connections to this resolver, supports socks:// and http://, default none
|
||||||
|
#proxy=socks://127.0.0.1:9050
|
||||||
|
|
||||||
|
# pubKeyPinsSha256: should be on an individual resolver level, specify comma-seperated base64 public key hashes like HPKP, not supplying this causes TLS connections to be unauthenticated (vulnerable to MITM), default none
|
||||||
|
# https:// also validates the hostname for now like a browser would
|
||||||
|
#pubKeyPinsSha256=wikE3jYAA6jQmXYTr/rbHeEPmC78dQwZbQp6WdrseEs=
|
||||||
|
|
||||||
|
# maxRetries: maximum number of times a request is re-queued to be resolved upstream due to failure before giving up, this is maximum retries total, not per-resolver, default resolvers.length * 2
|
||||||
|
#maxRetries=5
|
||||||
|
|
||||||
|
# name: human-readable name of resolver, might end up in logs, default full resolver URI
|
||||||
|
#name=somename
|
||||||
|
|
||||||
|
# connectTimeout: TCP connection timeout in milliseconds to upstream resolver, default 500
|
||||||
|
connectTimeout=500
|
34
xmpp-dox/pom.xml
Normal file
34
xmpp-dox/pom.xml
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||||
|
<parent>
|
||||||
|
<groupId>com.moparisthebest.dns</groupId>
|
||||||
|
<artifactId>jDnsProxy-parent</artifactId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<artifactId>xmpp-dox</artifactId>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
<name>${project.artifactId}</name>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>${project.groupId}</groupId>
|
||||||
|
<artifactId>jDnsProxy</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.igniterealtime.smack</groupId>
|
||||||
|
<artifactId>smack-tcp</artifactId>
|
||||||
|
<version>4.3.2</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.igniterealtime.smack</groupId>
|
||||||
|
<artifactId>smack-java7</artifactId>
|
||||||
|
<version>4.3.2</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
<build>
|
||||||
|
<finalName>${project.artifactId}</finalName>
|
||||||
|
</build>
|
||||||
|
</project>
|
@ -0,0 +1,153 @@
|
|||||||
|
package com.moparisthebest.dns.listen;
|
||||||
|
|
||||||
|
import com.moparisthebest.dns.dto.Packet;
|
||||||
|
import com.moparisthebest.dns.net.ParsedUrl;
|
||||||
|
import com.moparisthebest.dns.resolve.BaseRequestResponse;
|
||||||
|
import com.moparisthebest.dns.resolve.Resolver;
|
||||||
|
import com.moparisthebest.dns.xmpp.ConnectionDetails;
|
||||||
|
import com.moparisthebest.dns.xmpp.DnsIq;
|
||||||
|
import org.jivesoftware.smack.AbstractXMPPConnection;
|
||||||
|
import org.jivesoftware.smack.SmackException;
|
||||||
|
import org.jivesoftware.smack.XMPPException;
|
||||||
|
import org.jivesoftware.smack.iqrequest.IQRequestHandler;
|
||||||
|
import org.jivesoftware.smack.packet.*;
|
||||||
|
import org.jxmpp.jid.Jid;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
|
||||||
|
public class XmppListener implements Listener {
|
||||||
|
|
||||||
|
private final ConnectionDetails cd;
|
||||||
|
|
||||||
|
private final Resolver resolver;
|
||||||
|
private final ExecutorService executor;
|
||||||
|
|
||||||
|
private boolean running = true;
|
||||||
|
private Thread thisThread = null;
|
||||||
|
|
||||||
|
public XmppListener(final ParsedUrl parsedUrl, final Resolver resolver, final ExecutorService executor) {
|
||||||
|
this.cd = new ConnectionDetails(parsedUrl);
|
||||||
|
this.resolver = resolver;
|
||||||
|
this.executor = executor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
while (running)
|
||||||
|
try {
|
||||||
|
final AbstractXMPPConnection connection = cd.login();
|
||||||
|
|
||||||
|
thisThread = Thread.currentThread();
|
||||||
|
|
||||||
|
connection.registerIQRequestHandler(new IQRequestHandler() {
|
||||||
|
@Override
|
||||||
|
public IQ handleIQRequest(final IQ req) {
|
||||||
|
//System.out.println("request: " + req);
|
||||||
|
//System.out.println("request XML: " + req.toXML(StreamOpen.CLIENT_NAMESPACE));
|
||||||
|
|
||||||
|
final byte[] request = DnsIq.parseDnsIq(req);
|
||||||
|
if (request != null) {
|
||||||
|
//System.out.println("good request: " + req);
|
||||||
|
final XmppRequestResponse requestResponse = new XmppRequestResponse(req.getFrom(), new Packet(request));
|
||||||
|
|
||||||
|
resolver.resolveAsync(requestResponse).whenCompleteAsync((urr, t) -> {
|
||||||
|
if (t != null) {
|
||||||
|
t.printStackTrace();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//debugPacket(urr.getResponse().getBuf());
|
||||||
|
|
||||||
|
final IQ resp = DnsIq.responseFor(req, urr.getResponse().getBuf());
|
||||||
|
|
||||||
|
try {
|
||||||
|
//System.out.println("dns response: " + resp.toString());
|
||||||
|
//System.out.println("dns response XML: " + resp.toXML(StreamOpen.CLIENT_NAMESPACE));
|
||||||
|
connection.sendStanza(resp);
|
||||||
|
} catch (SmackException.NotConnectedException | InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}, executor);
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo: respond with error Iq?
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mode getMode() {
|
||||||
|
return Mode.sync;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IQ.Type getType() {
|
||||||
|
return IQ.Type.get;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getElement() {
|
||||||
|
return DnsIq.ELEMENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getNamespace() {
|
||||||
|
return DnsIq.NAMESPACE;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
while (running)
|
||||||
|
Thread.sleep(Long.MAX_VALUE);
|
||||||
|
|
||||||
|
} catch (IOException | XMPPException | SmackException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
try {
|
||||||
|
// let's not burn the CPU
|
||||||
|
Thread.sleep(1000);
|
||||||
|
} catch (InterruptedException e1) {
|
||||||
|
//ignore
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
if (running) {
|
||||||
|
// not good
|
||||||
|
e.printStackTrace();
|
||||||
|
try {
|
||||||
|
// let's not burn the CPU
|
||||||
|
Thread.sleep(1000);
|
||||||
|
} catch (InterruptedException e1) {
|
||||||
|
//ignore
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// being shutdown
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
running = false;
|
||||||
|
if (thisThread != null)
|
||||||
|
thisThread.interrupt();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class XmppRequestResponse extends BaseRequestResponse {
|
||||||
|
|
||||||
|
private final Jid requester;
|
||||||
|
|
||||||
|
public XmppRequestResponse(final Jid requester, final Packet request) {
|
||||||
|
super(request);
|
||||||
|
this.requester = requester;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Jid getRequester() {
|
||||||
|
return requester;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "XmppRequestResponse{" +
|
||||||
|
"requester=" + requester +
|
||||||
|
"} " + super.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
package com.moparisthebest.dns.listen;
|
||||||
|
|
||||||
|
import com.moparisthebest.dns.net.ParsedUrl;
|
||||||
|
import com.moparisthebest.dns.resolve.Resolver;
|
||||||
|
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
|
||||||
|
public class XmppServices implements Services {
|
||||||
|
@Override
|
||||||
|
public Listener getListener(ParsedUrl parsedUrl, final Resolver resolver, final ExecutorService executor) {
|
||||||
|
if ("xmpp".equals(parsedUrl.getProtocol())) {
|
||||||
|
return new XmppListener(parsedUrl, resolver, executor);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
package com.moparisthebest.dns.resolve;
|
||||||
|
|
||||||
|
import com.moparisthebest.dns.dto.Packet;
|
||||||
|
import com.moparisthebest.dns.net.ParsedUrl;
|
||||||
|
import com.moparisthebest.dns.xmpp.ConnectionDetails;
|
||||||
|
import com.moparisthebest.dns.xmpp.DnsIq;
|
||||||
|
import org.jivesoftware.smack.AbstractXMPPConnection;
|
||||||
|
import org.jivesoftware.smack.SmackException;
|
||||||
|
import org.jivesoftware.smack.XMPPException;
|
||||||
|
import org.jivesoftware.smack.packet.IQ;
|
||||||
|
import org.jxmpp.jid.Jid;
|
||||||
|
import org.jxmpp.jid.impl.JidCreate;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
|
||||||
|
public class XmppResolver implements Resolver {
|
||||||
|
|
||||||
|
private final Jid to;
|
||||||
|
private final AbstractXMPPConnection connection;
|
||||||
|
|
||||||
|
public XmppResolver(final ParsedUrl parsedUrl) {
|
||||||
|
try {
|
||||||
|
to = JidCreate.from(parsedUrl.getProps().get("resolverJid"));
|
||||||
|
this.connection = new ConnectionDetails(parsedUrl).login();
|
||||||
|
} catch (InterruptedException | XMPPException | SmackException | IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Packet resolve(final Packet request) throws Exception {
|
||||||
|
// todo: should do this async
|
||||||
|
final IQ req = DnsIq.requestTo(to, request.getBuf());
|
||||||
|
//System.out.println("dns request: " + req.toString());
|
||||||
|
//System.out.println("dns request XML: " + req.toXML(StreamOpen.CLIENT_NAMESPACE));
|
||||||
|
final IQ resp = connection.sendIqRequestAndWaitForResponse(req);
|
||||||
|
final byte[] ret = DnsIq.parseDnsIq(resp);
|
||||||
|
if(ret == null)
|
||||||
|
throw new Exception("XMPP request failed");
|
||||||
|
return new Packet(ret);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
package com.moparisthebest.dns.resolve;
|
||||||
|
|
||||||
|
import com.moparisthebest.dns.net.ParsedUrl;
|
||||||
|
|
||||||
|
public class XmppServices implements Services {
|
||||||
|
@Override
|
||||||
|
public Resolver getResolver(ParsedUrl parsedUrl) {
|
||||||
|
if ("xmpp".equals(parsedUrl.getProtocol())) {
|
||||||
|
return new XmppResolver(parsedUrl);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,57 @@
|
|||||||
|
package com.moparisthebest.dns.xmpp;
|
||||||
|
|
||||||
|
import com.moparisthebest.dns.net.ParsedUrl;
|
||||||
|
import org.jivesoftware.smack.AbstractXMPPConnection;
|
||||||
|
import org.jivesoftware.smack.SmackException;
|
||||||
|
import org.jivesoftware.smack.XMPPException;
|
||||||
|
import org.jivesoftware.smack.tcp.XMPPTCPConnection;
|
||||||
|
import org.jivesoftware.smack.tcp.XMPPTCPConnectionConfiguration;
|
||||||
|
import org.jxmpp.util.XmppStringUtils;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public class ConnectionDetails {
|
||||||
|
|
||||||
|
private final String jid;
|
||||||
|
private final String password;
|
||||||
|
private final InetSocketAddress isa;
|
||||||
|
|
||||||
|
private final long replyTimeout;
|
||||||
|
|
||||||
|
public ConnectionDetails(final String jid, final String password, final InetSocketAddress isa, final long replyTimeout) {
|
||||||
|
Objects.requireNonNull(jid, "user must be non-null");
|
||||||
|
Objects.requireNonNull(isa, "isa must be non-null");
|
||||||
|
this.jid = jid;
|
||||||
|
this.password = password;
|
||||||
|
this.isa = isa;
|
||||||
|
this.replyTimeout = replyTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConnectionDetails(final ParsedUrl parsedUrl) {
|
||||||
|
this(parsedUrl.getProps().get("user"), parsedUrl.getProps().get("pass"),
|
||||||
|
(InetSocketAddress) parsedUrl.getAddr(),
|
||||||
|
Long.parseLong(parsedUrl.getProps().getOrDefault("replyTimeout", "5000")));
|
||||||
|
}
|
||||||
|
|
||||||
|
public AbstractXMPPConnection login() throws InterruptedException, XMPPException, SmackException, IOException {
|
||||||
|
|
||||||
|
final XMPPTCPConnectionConfiguration.Builder builder = XMPPTCPConnectionConfiguration.builder()
|
||||||
|
.setUsernameAndPassword(XmppStringUtils.parseLocalpart(jid), password)
|
||||||
|
.setXmppDomain(XmppStringUtils.parseDomain(jid))
|
||||||
|
.setHostAddress(isa.getAddress())
|
||||||
|
.setPort(isa.getPort());
|
||||||
|
|
||||||
|
final String resource = XmppStringUtils.parseResource(jid);
|
||||||
|
if(resource != null && !resource.isEmpty()) {
|
||||||
|
builder.setResource(resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
final AbstractXMPPConnection connection = new XMPPTCPConnection(builder.build());
|
||||||
|
connection.setReplyTimeout(replyTimeout);
|
||||||
|
connection.connect().login();
|
||||||
|
|
||||||
|
return connection;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,56 @@
|
|||||||
|
package com.moparisthebest.dns.xmpp;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.packet.IQ;
|
||||||
|
import org.jivesoftware.smack.packet.UnparsedIQ;
|
||||||
|
import org.jxmpp.jid.Jid;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
|
public class DnsIq extends IQ {
|
||||||
|
|
||||||
|
private static final Base64.Decoder decoder = Base64.getDecoder();
|
||||||
|
private static final Base64.Encoder encoder = Base64.getEncoder().withoutPadding();
|
||||||
|
|
||||||
|
public static final String ELEMENT = "dns";
|
||||||
|
public static final String NAMESPACE = "urn:xmpp:dox:0";
|
||||||
|
|
||||||
|
private final ByteBuffer bb;
|
||||||
|
|
||||||
|
private DnsIq(final ByteBuffer bb) {
|
||||||
|
super(ELEMENT, NAMESPACE);
|
||||||
|
this.bb = bb;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DnsIq responseFor(final IQ iq, final ByteBuffer bb) {
|
||||||
|
final DnsIq ret = new DnsIq(bb);
|
||||||
|
ret.setStanzaId(iq.getStanzaId());
|
||||||
|
ret.setTo(iq.getFrom());
|
||||||
|
ret.setType(Type.result);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DnsIq requestTo(final Jid to, final ByteBuffer bb) {
|
||||||
|
final DnsIq ret = new DnsIq(bb);
|
||||||
|
ret.setTo(to);
|
||||||
|
ret.setType(Type.get);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] parseDnsIq(final IQ iq) {
|
||||||
|
// todo: yikes this whole method is awful, what happened to XML ? investigate this later
|
||||||
|
if(!(iq instanceof UnparsedIQ))
|
||||||
|
return null;
|
||||||
|
final UnparsedIQ uiq = (UnparsedIQ) iq;
|
||||||
|
final String actualRequest = uiq.getContent().toString().replaceAll("<[^>]+>", "");
|
||||||
|
//System.out.println("actualRequest: " + actualRequest);
|
||||||
|
return decoder.decode(actualRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected IQChildElementXmlStringBuilder getIQChildElementBuilder(final IQChildElementXmlStringBuilder buf) {
|
||||||
|
buf.rightAngleBracket();
|
||||||
|
buf.append(encoder.encodeToString(bb.array()));
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
com.moparisthebest.dns.listen.XmppServices
|
@ -0,0 +1 @@
|
|||||||
|
com.moparisthebest.dns.resolve.XmppServices
|
Loading…
Reference in New Issue
Block a user