2 * Copyright (c) 2010-2022 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.BindException;
17 import java.net.DatagramPacket;
18 import java.net.DatagramSocket;
19 import java.net.InetAddress;
20 import java.net.InetSocketAddress;
21 import java.net.SocketException;
22 import java.net.UnknownHostException;
24 import org.eclipse.jdt.annotation.NonNullByDefault;
25 import org.eclipse.jdt.annotation.Nullable;
26 import org.openhab.binding.network.internal.dhcp.DHCPPacket.BadPacketException;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
31 * Receives UDP messages and try to parse them as DHCP messages.
32 * First try to listen to the DHCP port 67 and if that failes because of missing access rights,
33 * port 6767 will be opened instead (and the user is required to setup a port forarding).
35 * @author David Graeff - Initial contribution
38 public class DHCPPacketListenerServer extends Thread {
39 private byte[] buffer = new byte[1024];
40 protected @Nullable DatagramSocket dsocket;
41 private DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
42 boolean willbeclosed = false;
43 Logger logger = LoggerFactory.getLogger(DHCPPacketListenerServer.class);
44 private boolean useUnprevilegedPort = false;
45 private final IPRequestReceivedCallback listener;
47 DHCPPacketListenerServer(IPRequestReceivedCallback listener) throws SocketException, BindException {
48 this.listener = listener;
51 } catch (SocketException e) {
52 useUnprevilegedPort = true;
57 protected void bindSocketTo(int port) throws SocketException {
58 DatagramSocket dsocket = new DatagramSocket(null);
59 dsocket.setReuseAddress(true);
60 dsocket.setBroadcast(true);
61 dsocket.bind(new InetSocketAddress(port));
62 this.dsocket = dsocket;
65 protected void receivePacket(DHCPPacket request, @Nullable InetAddress udpRemote)
66 throws BadPacketException, UnknownHostException, IOException {
67 if (request.getOp() != DHCPPacket.BOOTREQUEST) {
68 return; // skipping non BOOTREQUEST message types
71 Byte dhcpMessageType = request.getDHCPMessageType();
73 if (dhcpMessageType != DHCPPacket.DHCPREQUEST) {
74 return; // skipping non DHCPREQUEST message types
77 InetAddress requestedAddress = request.getRequestedIPAddress();
78 if (requestedAddress == null) {
79 // There is no requested address field. This may be a DHCPREQUEST message to renew
80 // the lease. Let's deduct the IP by the IP/UDP src.
81 requestedAddress = udpRemote;
82 if (requestedAddress == null) {
83 logger.warn("DHO_DHCP_REQUESTED_ADDRESS field is missing");
87 listener.dhcpRequestReceived(requestedAddress.getHostAddress());
93 logger.info("DHCP request packet listener online");
94 while (!willbeclosed) {
95 packet.setLength(buffer.length);
96 DatagramSocket socket = dsocket;
100 socket.receive(packet);
101 receivePacket(new DHCPPacket(packet), packet.getAddress());
103 } catch (IOException e) {
107 logger.warn("{}", e.getLocalizedMessage());
111 public @Nullable DatagramSocket getSocket() {
115 // Return true if the instance couldn't bind to port 67 and used port 6767 instead
116 // to listen to DHCP traffic (port forwarding necessary).
117 public boolean isUseUnprevilegedPort() {
118 return useUnprevilegedPort;
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) {