]> git.basschouten.com Git - openhab-addons.git/blob
572d038c255f7863fa4ef5d5d69f8a5e84e0ab5c
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2021 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.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;
23
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;
29
30 /**
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).
34  *
35  * @author David Graeff - Initial contribution
36  */
37 @NonNullByDefault
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;
46
47     DHCPPacketListenerServer(IPRequestReceivedCallback listener) throws SocketException, BindException {
48         this.listener = listener;
49         try {
50             bindSocketTo(67);
51         } catch (SocketException e) {
52             useUnprevilegedPort = true;
53             bindSocketTo(6767);
54         }
55     }
56
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;
63     }
64
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
69         }
70
71         Byte dhcpMessageType = request.getDHCPMessageType();
72
73         if (dhcpMessageType != DHCPPacket.DHCPREQUEST) {
74             return; // skipping non DHCPREQUEST message types
75         }
76
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");
84                 return;
85             }
86         }
87         listener.dhcpRequestReceived(requestedAddress.getHostAddress());
88     }
89
90     @Override
91     public void run() {
92         try {
93             logger.info("DHCP request packet listener online");
94             while (!willbeclosed) {
95                 packet.setLength(buffer.length);
96                 DatagramSocket socket = dsocket;
97                 if (socket == null) {
98                     return;
99                 }
100                 socket.receive(packet);
101                 receivePacket(new DHCPPacket(packet), packet.getAddress());
102             }
103         } catch (IOException e) {
104             if (willbeclosed) {
105                 return;
106             }
107             logger.warn("{}", e.getLocalizedMessage());
108         }
109     }
110
111     public @Nullable DatagramSocket getSocket() {
112         return dsocket;
113     }
114
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;
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 }