mirror of
https://github.com/danieldemus/openhab-core.git
synced 2025-01-10 13:21:53 +01:00
IP addon finder: add support for sending local mac address (#4477)
Signed-off-by: Andrew Fiddian-Green <software@whitebear.ch>
This commit is contained in:
parent
76daeeff12
commit
57cf6a5d43
@ -12,7 +12,8 @@
|
||||
*/
|
||||
package org.openhab.core.config.discovery.addon.ip;
|
||||
|
||||
import static org.openhab.core.config.discovery.addon.AddonFinderConstants.*;
|
||||
import static org.openhab.core.config.discovery.addon.AddonFinderConstants.SERVICE_NAME_IP;
|
||||
import static org.openhab.core.config.discovery.addon.AddonFinderConstants.SERVICE_TYPE_IP;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
@ -21,7 +22,9 @@ import java.net.DatagramSocket;
|
||||
import java.net.Inet4Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.net.SocketAddress;
|
||||
import java.net.SocketException;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.net.StandardProtocolFamily;
|
||||
import java.net.StandardSocketOptions;
|
||||
@ -34,6 +37,7 @@ import java.text.ParseException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.HexFormat;
|
||||
import java.util.IllegalFormatException;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -139,6 +143,11 @@ import org.slf4j.LoggerFactory;
|
||||
* <td>timeout to wait for a answers</td>
|
||||
* <td></td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>{@code fmtMac}</td>
|
||||
* <td>format specifier string for mac address</td>
|
||||
* <td>e.g. '%02X', '%02X:', '%02x-'</td>
|
||||
* </tr>
|
||||
* </table>
|
||||
* <p>
|
||||
* <table border="1">
|
||||
@ -154,6 +163,11 @@ import org.slf4j.LoggerFactory;
|
||||
* <td>{@code $srcPort}</td>
|
||||
* <td>source port</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>{@code $srcMac}</td>
|
||||
* <td>source mac address</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>{@code $uuid}</td>
|
||||
* <td>String returned by {@code java.util.UUID.randomUUID()}</td>
|
||||
* </tr>
|
||||
@ -201,6 +215,8 @@ public class IpAddonFinder extends BaseAddonFinder implements NetworkAddressChan
|
||||
private static final String PARAMETER_REQUEST_PLAIN = "requestPlain";
|
||||
private static final String PARAMETER_SRC_IP = "srcIp";
|
||||
private static final String PARAMETER_SRC_PORT = "srcPort";
|
||||
private static final String PARAMETER_SRC_MAC = "srcMac";
|
||||
private static final String PARAMETER_MAC_FORMAT = "fmtMac";
|
||||
private static final String PARAMETER_TIMEOUT_MS = "timeoutMs";
|
||||
private static final String REPLACEMENT_UUID = "uuid";
|
||||
|
||||
@ -346,16 +362,22 @@ public class IpAddonFinder extends BaseAddonFinder implements NetworkAddressChan
|
||||
continue;
|
||||
}
|
||||
}
|
||||
String macFormat = parameters.getOrDefault(PARAMETER_MAC_FORMAT, "%02X:");
|
||||
try {
|
||||
String.format(macFormat, 123);
|
||||
} catch (IllegalFormatException e) {
|
||||
logger.warn("{}: discovery-parameter '{}' invalid format specifier", candidate.getUID(), macFormat);
|
||||
}
|
||||
|
||||
// handle known types
|
||||
try {
|
||||
switch (Objects.toString(type)) {
|
||||
case TYPE_IP_BROADCAST:
|
||||
scanBroadcast(candidate, request, requestPlain, response, timeoutMs, destPort);
|
||||
scanBroadcast(candidate, request, requestPlain, response, timeoutMs, destPort, macFormat);
|
||||
break;
|
||||
case TYPE_IP_MULTICAST:
|
||||
scanMulticast(candidate, request, requestPlain, response, timeoutMs, listenPort, destIp,
|
||||
destPort);
|
||||
destPort, macFormat);
|
||||
break;
|
||||
default:
|
||||
logger.warn("{}: discovery-parameter type \"{}\" is unknown", candidate.getUID(), type);
|
||||
@ -369,7 +391,7 @@ public class IpAddonFinder extends BaseAddonFinder implements NetworkAddressChan
|
||||
}
|
||||
|
||||
private void scanBroadcast(AddonInfo candidate, String request, String requestPlain, String response, int timeoutMs,
|
||||
int destPort) throws ParseException {
|
||||
int destPort, String macFormat) throws ParseException {
|
||||
if (request.isEmpty() && requestPlain.isEmpty()) {
|
||||
logger.warn("{}: match-property request and requestPlain \"{}\" is unknown", candidate.getUID(),
|
||||
TYPE_IP_BROADCAST);
|
||||
@ -391,7 +413,7 @@ public class IpAddonFinder extends BaseAddonFinder implements NetworkAddressChan
|
||||
socket.setBroadcast(true);
|
||||
socket.setSoTimeout(timeoutMs);
|
||||
byte[] sendBuffer = requestPlain.isEmpty() ? buildRequestArray(socket.getLocalSocketAddress(), request)
|
||||
: buildRequestArrayPlain(socket.getLocalSocketAddress(), requestPlain);
|
||||
: buildRequestArrayPlain(socket.getLocalSocketAddress(), requestPlain, macFormat);
|
||||
DatagramPacket sendPacket = new DatagramPacket(sendBuffer, sendBuffer.length,
|
||||
InetAddress.getByName(broadcastAddress), destPort);
|
||||
socket.send(sendPacket);
|
||||
@ -431,7 +453,7 @@ public class IpAddonFinder extends BaseAddonFinder implements NetworkAddressChan
|
||||
}
|
||||
|
||||
private void scanMulticast(AddonInfo candidate, String request, String requestPlain, String response, int timeoutMs,
|
||||
int listenPort, @Nullable InetAddress destIp, int destPort) throws ParseException {
|
||||
int listenPort, @Nullable InetAddress destIp, int destPort, String macFormat) throws ParseException {
|
||||
List<String> ipAddresses = NetUtil.getAllInterfaceAddresses().stream()
|
||||
.filter(a -> a.getAddress() instanceof Inet4Address).map(a -> a.getAddress().getHostAddress()).toList();
|
||||
|
||||
@ -443,7 +465,7 @@ public class IpAddonFinder extends BaseAddonFinder implements NetworkAddressChan
|
||||
Selector selector = Selector.open()) {
|
||||
byte[] requestArray = "".equals(requestPlain)
|
||||
? buildRequestArray(channel.getLocalAddress(), Objects.toString(request))
|
||||
: buildRequestArrayPlain(channel.getLocalAddress(), Objects.toString(requestPlain));
|
||||
: buildRequestArrayPlain(channel.getLocalAddress(), Objects.toString(requestPlain), macFormat);
|
||||
if (logger.isTraceEnabled()) {
|
||||
InetSocketAddress sock = (InetSocketAddress) channel.getLocalAddress();
|
||||
String id = candidate.getUID();
|
||||
@ -489,7 +511,7 @@ public class IpAddonFinder extends BaseAddonFinder implements NetworkAddressChan
|
||||
}
|
||||
|
||||
// build from plaintext string
|
||||
private byte[] buildRequestArrayPlain(SocketAddress address, String request)
|
||||
private byte[] buildRequestArrayPlain(SocketAddress address, String request, String macFormat)
|
||||
throws java.io.IOException, ParseException {
|
||||
InetSocketAddress sock = (InetSocketAddress) address;
|
||||
|
||||
@ -502,6 +524,9 @@ public class IpAddonFinder extends BaseAddonFinder implements NetworkAddressChan
|
||||
while ((p = req.indexOf("$" + PARAMETER_SRC_PORT)) != -1) {
|
||||
req.replace(p, p + PARAMETER_SRC_PORT.length() + 1, "" + sock.getPort());
|
||||
}
|
||||
while ((p = req.indexOf("$" + PARAMETER_SRC_MAC)) != -1) {
|
||||
req.replace(p, p + PARAMETER_SRC_MAC.length() + 1, macFormat(macFormat, macBytesFrom(sock)));
|
||||
}
|
||||
while ((p = req.indexOf("$" + REPLACEMENT_UUID)) != -1) {
|
||||
req.replace(p, p + REPLACEMENT_UUID.length() + 1, UUID.randomUUID().toString());
|
||||
}
|
||||
@ -531,6 +556,10 @@ public class IpAddonFinder extends BaseAddonFinder implements NetworkAddressChan
|
||||
requestFrame.write((byte) ((dPort >> 8) & 0xff));
|
||||
requestFrame.write((byte) (dPort & 0xff));
|
||||
break;
|
||||
case "$" + PARAMETER_SRC_MAC:
|
||||
byte[] mac = macBytesFrom(sock);
|
||||
requestFrame.write(mac);
|
||||
break;
|
||||
case "$" + REPLACEMENT_UUID:
|
||||
String uuid = UUID.randomUUID().toString();
|
||||
requestFrame.write(uuid.getBytes());
|
||||
@ -567,4 +596,36 @@ public class IpAddonFinder extends BaseAddonFinder implements NetworkAddressChan
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get mac address bytes associated with the given Internet socket address
|
||||
*
|
||||
* @param inetSocketAddress the Internet address
|
||||
* @return the mac address as an array of bytes
|
||||
* @throws SocketException if address is not on this PC, or no mac address is associated
|
||||
*/
|
||||
private byte[] macBytesFrom(InetSocketAddress inetSocketAddress) throws SocketException {
|
||||
NetworkInterface networkInterface = NetworkInterface.getByInetAddress(inetSocketAddress.getAddress());
|
||||
if (networkInterface == null) {
|
||||
throw new SocketException("No network interface");
|
||||
}
|
||||
return networkInterface.getHardwareAddress();
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the given format specifier to format an array of mac address bytes
|
||||
*
|
||||
* @param format a standard format specifier; optionally ends with a delimiter e.g. "%02x:" or "%02X"
|
||||
* @param bytes the mac address as an array of bytes
|
||||
*
|
||||
* @return e.g. '01:02:03:04:A5:B6:C7:D8', '01-02-03-04:a5:b6:c7:d8', or '01020304A5B6C7D8'
|
||||
*/
|
||||
private String macFormat(String format, byte[] bytes) {
|
||||
StringBuilder result = new StringBuilder();
|
||||
for (byte byt : bytes) {
|
||||
result.append(String.format(format, byt));
|
||||
}
|
||||
boolean isDelimited = Set.of(':', '-', '.').contains(format.charAt(format.length() - 1));
|
||||
return (isDelimited ? result.substring(0, result.length() - 1) : result).toString();
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user