2 * Copyright (c) 2010-2024 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.network.internal.dhcp;
15 import java.io.IOException;
16 import java.net.DatagramPacket;
17 import java.net.DatagramSocket;
18 import java.net.InetAddress;
19 import java.net.InetSocketAddress;
20 import java.net.SocketException;
21 import java.net.UnknownHostException;
23 import org.eclipse.jdt.annotation.NonNullByDefault;
24 import org.eclipse.jdt.annotation.Nullable;
25 import org.openhab.binding.network.internal.dhcp.DHCPPacket.BadPacketException;
26 import org.slf4j.Logger;
27 import org.slf4j.LoggerFactory;
30 * Receives UDP messages and try to parse them as DHCP messages.
31 * First try to listen to the DHCP port 67 and if that failes because of missing access rights,
32 * port 6767 will be opened instead (and the user is required to setup a port forarding).
34 * @author David Graeff - Initial contribution
37 public class DHCPPacketListenerServer extends Thread {
38 private static final int PRIVILEGED_PORT = 67;
39 private static final int UNPRIVILEGED_PORT = 6767;
40 private byte[] buffer = new byte[1024];
41 private @Nullable DatagramSocket dsocket;
42 private DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
43 private final IPRequestReceivedCallback listener;
44 private final Logger logger = LoggerFactory.getLogger(DHCPPacketListenerServer.class);
45 private boolean willbeclosed = false;
46 private int currentPort = PRIVILEGED_PORT;
48 DHCPPacketListenerServer(IPRequestReceivedCallback listener) throws SocketException {
49 this.listener = listener;
51 bindSocketTo(currentPort);
52 } catch (SocketException e) {
53 currentPort = UNPRIVILEGED_PORT;
54 bindSocketTo(currentPort);
58 private void bindSocketTo(int port) throws SocketException {
59 DatagramSocket dsocket = new DatagramSocket(null);
60 dsocket.setReuseAddress(true);
61 dsocket.setBroadcast(true);
62 dsocket.bind(new InetSocketAddress(port));
63 this.dsocket = dsocket;
66 protected void receivePacket(DHCPPacket request, @Nullable InetAddress udpRemote)
67 throws BadPacketException, UnknownHostException, IOException {
68 if (request.getOp() != DHCPPacket.BOOTREQUEST) {
69 return; // skipping non BOOTREQUEST message types
72 Byte dhcpMessageType = request.getDHCPMessageType();
74 if (dhcpMessageType == null || dhcpMessageType != DHCPPacket.DHCPREQUEST) {
75 return; // skipping non DHCPREQUEST message types
78 InetAddress requestedAddress = request.getRequestedIPAddress();
79 if (requestedAddress == null) {
80 // There is no requested address field. This may be a DHCPREQUEST message to renew
81 // the lease. Let's deduct the IP by the IP/UDP src.
82 requestedAddress = udpRemote;
83 if (requestedAddress == null) {
84 logger.warn("DHO_DHCP_REQUESTED_ADDRESS field is missing");
88 listener.dhcpRequestReceived(requestedAddress.getHostAddress());
94 logger.info("DHCP request packet listener online");
95 while (!willbeclosed) {
96 packet.setLength(buffer.length);
97 DatagramSocket socket = dsocket;
101 socket.receive(packet);
102 receivePacket(new DHCPPacket(packet), packet.getAddress());
104 } catch (IOException e) {
108 logger.warn("{}", e.getLocalizedMessage());
112 public @Nullable DatagramSocket getSocket() {
116 // Return true if the instance is using port 67 to listen to DHCP traffic (no port forwarding necessary).
117 public boolean usingPrivilegedPort() {
118 return currentPort == PRIVILEGED_PORT;
122 * Closes the socket and waits for the receive thread to finish.
123 * Does nothing if the receive thread is not running.
125 public void close() {
128 DatagramSocket socket = dsocket;
129 if (socket != null) {
134 } catch (InterruptedException e) {
141 public int getCurrentPort() {