[core] Properly reply to IQ requests

Properly reply to IQ requests with feature-not-implemented or
service-unavailable. In case there is no IQ request handler but the IQ
namespace is known, we reply with feature-not-implemented, otherwise
with service-unavailable.
This commit is contained in:
Florian Schmaus 2021-04-01 13:57:36 +02:00
parent 252cea1149
commit 533695c1b4
2 changed files with 53 additions and 13 deletions

View File

@ -377,6 +377,8 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
private final Map<QName, IQRequestHandler> setIqRequestHandler = new HashMap<>(); private final Map<QName, IQRequestHandler> setIqRequestHandler = new HashMap<>();
private final Map<QName, IQRequestHandler> getIqRequestHandler = new HashMap<>(); private final Map<QName, IQRequestHandler> getIqRequestHandler = new HashMap<>();
private final Set<String> iqRequestHandlerNamespaces = new CopyOnWriteArraySet<>();
private final Map<String, Integer> iqRequestHandlerNamespacesReferenceCounters = new HashMap<>();
private final StanzaFactory stanzaFactory; private final StanzaFactory stanzaFactory;
@ -1524,15 +1526,18 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
throw new IllegalStateException("Should only encounter IQ type 'get' or 'set'"); throw new IllegalStateException("Should only encounter IQ type 'get' or 'set'");
} }
if (iqRequestHandler == null) { if (iqRequestHandler == null) {
final String iqNamespace = key.getNamespaceURI();
StanzaError.Condition replyCondition; StanzaError.Condition replyCondition;
switch (unknownIqRequestReplyMode) { switch (unknownIqRequestReplyMode) {
case doNotReply: case doNotReply:
return; return;
case replyFeatureNotImplemented: case reply:
replyCondition = StanzaError.Condition.feature_not_implemented; boolean isKnownNamespace = iqRequestHandlerNamespaces.contains(iqNamespace);
break; if (isKnownNamespace) {
case replyServiceUnavailable: replyCondition = StanzaError.Condition.feature_not_implemented;
replyCondition = StanzaError.Condition.service_unavailable; } else {
replyCondition = StanzaError.Condition.service_unavailable;
}
break; break;
default: default:
throw new AssertionError(); throw new AssertionError();
@ -2043,18 +2048,35 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
@Override @Override
public IQRequestHandler registerIQRequestHandler(final IQRequestHandler iqRequestHandler) { public IQRequestHandler registerIQRequestHandler(final IQRequestHandler iqRequestHandler) {
final QName key = iqRequestHandler.getQName(); final QName key = iqRequestHandler.getQName();
IQRequestHandler previous;
switch (iqRequestHandler.getType()) { switch (iqRequestHandler.getType()) {
case set: case set:
synchronized (setIqRequestHandler) { synchronized (setIqRequestHandler) {
return setIqRequestHandler.put(key, iqRequestHandler); previous = setIqRequestHandler.put(key, iqRequestHandler);
} }
break;
case get: case get:
synchronized (getIqRequestHandler) { synchronized (getIqRequestHandler) {
return getIqRequestHandler.put(key, iqRequestHandler); previous = getIqRequestHandler.put(key, iqRequestHandler);
} }
break;
default: default:
throw new IllegalArgumentException("Only IQ type of 'get' and 'set' allowed"); throw new IllegalArgumentException("Only IQ type of 'get' and 'set' allowed");
} }
final String iqNamespace = key.getNamespaceURI();
synchronized (iqRequestHandlerNamespacesReferenceCounters) {
Integer newValue;
Integer counter = iqRequestHandlerNamespacesReferenceCounters.get(iqNamespace);
if (counter == null) {
iqRequestHandlerNamespaces.add(iqNamespace);
newValue = 0;
} else {
newValue = counter.intValue() + 1;
}
iqRequestHandlerNamespacesReferenceCounters.put(iqNamespace, newValue);
}
return previous;
} }
@Override @Override
@ -2065,19 +2087,38 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
@Override @Override
public IQRequestHandler unregisterIQRequestHandler(String element, String namespace, IQ.Type type) { public IQRequestHandler unregisterIQRequestHandler(String element, String namespace, IQ.Type type) {
IQRequestHandler unregisteredHandler;
final QName key = new QName(namespace, element); final QName key = new QName(namespace, element);
switch (type) { switch (type) {
case set: case set:
synchronized (setIqRequestHandler) { synchronized (setIqRequestHandler) {
return setIqRequestHandler.remove(key); unregisteredHandler = setIqRequestHandler.remove(key);
} }
break;
case get: case get:
synchronized (getIqRequestHandler) { synchronized (getIqRequestHandler) {
return getIqRequestHandler.remove(key); unregisteredHandler = getIqRequestHandler.remove(key);
} }
break;
default: default:
throw new IllegalArgumentException("Only IQ type of 'get' and 'set' allowed"); throw new IllegalArgumentException("Only IQ type of 'get' and 'set' allowed");
} }
if (unregisteredHandler == null) {
return null;
}
synchronized (iqRequestHandlerNamespacesReferenceCounters) {
int newValue = iqRequestHandlerNamespacesReferenceCounters.get(namespace).intValue() - 1;
if (newValue == 0) {
iqRequestHandlerNamespacesReferenceCounters.remove(namespace);
iqRequestHandlerNamespaces.remove(namespace);
} else {
iqRequestHandlerNamespacesReferenceCounters.put(namespace, newValue);
}
}
return unregisteredHandler;
} }
private long lastStanzaReceived; private long lastStanzaReceived;

View File

@ -1,6 +1,6 @@
/** /**
* *
* Copyright 2003-2007 Jive Software, 2018-2020 Florian Schmaus. * Copyright 2003-2007 Jive Software, 2018-2021 Florian Schmaus.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -343,11 +343,10 @@ public final class SmackConfiguration {
public enum UnknownIqRequestReplyMode { public enum UnknownIqRequestReplyMode {
doNotReply, doNotReply,
replyFeatureNotImplemented, reply,
replyServiceUnavailable,
} }
private static UnknownIqRequestReplyMode unknownIqRequestReplyMode = UnknownIqRequestReplyMode.replyFeatureNotImplemented; private static UnknownIqRequestReplyMode unknownIqRequestReplyMode = UnknownIqRequestReplyMode.reply;
public static UnknownIqRequestReplyMode getUnknownIqRequestReplyMode() { public static UnknownIqRequestReplyMode getUnknownIqRequestReplyMode() {
return unknownIqRequestReplyMode; return unknownIqRequestReplyMode;