]> git.basschouten.com Git - openhab-addons.git/blob
e339658becd99f45c52760dc7791f6439604d0c7
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2024 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.network.internal.dhcp;
14
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;
22
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;
28
29 /**
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).
33  *
34  * @author David Graeff - Initial contribution
35  */
36 @NonNullByDefault
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;
47
48     DHCPPacketListenerServer(IPRequestReceivedCallback listener) throws SocketException {
49         this.listener = listener;
50         try {
51             bindSocketTo(currentPort);
52         } catch (SocketException e) {
53             currentPort = UNPRIVILEGED_PORT;
54             bindSocketTo(currentPort);
55         }
56     }
57
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;
64     }
65
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
70         }
71
72         Byte dhcpMessageType = request.getDHCPMessageType();
73
74         if (dhcpMessageType == null || dhcpMessageType != DHCPPacket.DHCPREQUEST) {
75             return; // skipping non DHCPREQUEST message types
76         }
77
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");
85                 return;
86             }
87         }
88         listener.dhcpRequestReceived(requestedAddress.getHostAddress());
89     }
90
91     @Override
92     public void run() {
93         try {
94             logger.info("DHCP request packet listener online");
95             while (!willbeclosed) {
96                 packet.setLength(buffer.length);
97                 DatagramSocket socket = dsocket;
98                 if (socket == null) {
99                     return;
100                 }
101                 socket.receive(packet);
102                 receivePacket(new DHCPPacket(packet), packet.getAddress());
103             }
104         } catch (IOException e) {
105             if (willbeclosed) {
106                 return;
107             }
108             logger.warn("{}", e.getLocalizedMessage());
109         }
110     }
111
112     public @Nullable DatagramSocket getSocket() {
113         return dsocket;
114     }
115
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;
119     }
120
121     /**
122      * Closes the socket and waits for the receive thread to finish.
123      * Does nothing if the receive thread is not running.
124      */
125     public void close() {
126         if (isAlive()) {
127             willbeclosed = true;
128             DatagramSocket socket = dsocket;
129             if (socket != null) {
130                 socket.close();
131             }
132             try {
133                 join(1000);
134             } catch (InterruptedException e) {
135             }
136             interrupt();
137             dsocket = null;
138         }
139     }
140
141     public int getCurrentPort() {
142         return currentPort;
143     }
144 }