Switch to JDT annotations for Nullable and NonNull
[netvirt.git] / dhcpservice / api / src / main / java / org / opendaylight / netvirt / dhcpservice / api / DHCPOptions.java
1 /*
2  * Copyright (c) 2015 Ericsson India Global Services Pvt Ltd. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8
9 package org.opendaylight.netvirt.dhcpservice.api;
10
11 import static org.opendaylight.netvirt.dhcpservice.api.DHCPConstants.OPT_END;
12 import static org.opendaylight.netvirt.dhcpservice.api.DHCPConstants.OPT_PAD;
13
14 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
15 import java.net.InetAddress;
16 import java.net.UnknownHostException;
17 import java.nio.charset.StandardCharsets;
18 import java.util.LinkedHashMap;
19 import java.util.List;
20 import org.apache.commons.lang3.ArrayUtils;
21 import org.eclipse.jdt.annotation.Nullable;
22 import org.opendaylight.openflowplugin.libraries.liblldp.HexEncode;
23 import org.opendaylight.openflowplugin.libraries.liblldp.NetUtils;
24
25 public class DHCPOptions {
26
27     private static class DhcpOption {
28         private byte code;
29         private byte length;
30         private byte[] value;
31
32         DhcpOption(byte code, byte[] value) {
33             if (code != OPT_PAD && code != OPT_END && value != null) {
34                 this.code = code;
35                 this.value = value;
36                 this.length = (byte) value.length;
37             }
38         }
39
40         public byte getCode() {
41             return this.code;
42         }
43
44         public byte[] getValue() {
45             return this.value;
46         }
47
48         public byte[] serialize() {
49             byte[] opt1 = new byte[2];
50             opt1[0] = this.code;
51             opt1[1] = this.length;
52             return ArrayUtils.addAll(opt1, this.value);
53         }
54
55         @Override
56         public String toString() {
57             StringBuilder sb = new StringBuilder();
58             sb.append("{ ")
59                     .append("code: ").append(this.code)
60                     .append(", len: ").append(this.length)
61                     .append(", value: 0x").append(HexEncode.bytesToHexString(this.value))
62                     .append(" }");
63             return sb.toString();
64         }
65     }
66
67     private final LinkedHashMap<Byte, DhcpOption> options;
68
69     public DHCPOptions() {
70         options = new LinkedHashMap<>();
71     }
72
73     private DhcpOption getOption(byte code) {
74         return this.options.get(code);
75     }
76
77     private void setOption(DhcpOption opt) {
78         this.options.put(opt.getCode(), opt);
79     }
80
81     public void setOption(byte code, byte[] opt) {
82         this.setOption(new DhcpOption(code, opt));
83     }
84
85     // It's unclear from all the callers if returning an empty array would be safe.
86     @SuppressFBWarnings("PZLA_PREFER_ZERO_LENGTH_ARRAYS")
87     @Nullable
88     public byte[] getOptionBytes(byte code) {
89         DhcpOption option = this.getOption(code);
90         return option != null ? option.getValue() : null;
91     }
92
93     public void setOptionByte(byte code, byte opt) {
94         this.setOption(new DhcpOption(code, DHCPUtils.byteToByteArray(opt)));
95     }
96
97     public byte getOptionByte(byte code) {
98         return this.getOption(code).getValue()[0];
99     }
100
101     public void setOptionShort(byte code, short opt) {
102         this.setOption(new DhcpOption(code, DHCPUtils.shortToByteArray(opt)));
103     }
104
105     public short getOptionShort(byte code) {
106         byte[] opt = this.getOptionBytes(code);
107         return DHCPUtils.byteArrayToShort(opt);
108     }
109
110     public void setOptionInt(byte code, int opt) {
111         this.setOption(new DhcpOption(code, DHCPUtils.intToByteArray(opt)));
112     }
113
114     public int getOptionInt(byte code) {
115         byte[] opt = this.getOptionBytes(code);
116         return NetUtils.byteArray4ToInt(opt);
117     }
118
119     public void setOptionInetAddr(byte code, InetAddress opt) {
120         this.setOption(new DhcpOption(code, DHCPUtils.inetAddrToByteArray(opt)));
121     }
122
123     @Nullable
124     public InetAddress getOptionInetAddr(byte code) {
125         byte[] opt = this.getOptionBytes(code);
126         try {
127             return InetAddress.getByAddress(opt);
128         } catch (UnknownHostException | NullPointerException e) {
129             return null;
130         }
131     }
132
133     public void setOptionStrAddr(byte code, String opt) throws UnknownHostException {
134         this.setOption(new DhcpOption(code, DHCPUtils.strAddrToByteArray(opt)));
135     }
136
137     @Nullable
138     public String getOptionStrAddr(byte code) {
139         byte[] opt = this.getOptionBytes(code);
140         try {
141             return InetAddress.getByAddress(opt).getHostAddress();
142         } catch (UnknownHostException | NullPointerException e) {
143             return null;
144         }
145     }
146
147     public void setOptionStrAddrs(byte code, List<String> addrs) throws UnknownHostException {
148         if (!addrs.isEmpty()) {
149             this.setOption(new DhcpOption(code, DHCPUtils.strListAddrsToByteArray(addrs)));
150         }
151     }
152
153     public void setOptionString(byte code, String str) {
154         this.setOption(new DhcpOption(code, str.getBytes(StandardCharsets.UTF_8)));
155     }
156
157     public byte[] serialize() {
158         byte[] serializedOptions = new byte[0];
159         for (DhcpOption dhcpOption: this.options.values()) {
160             serializedOptions = ArrayUtils.addAll(serializedOptions, dhcpOption.serialize());
161         }
162         byte[] end = new byte[] {(byte)255};
163         serializedOptions = ArrayUtils.addAll(serializedOptions, end);
164         return serializedOptions;
165     }
166
167     private byte[] getOptionValArray(byte[] opt, int pos, int len) {
168         byte[] val = new byte[len];
169         for (int i = 0; i < len; i++) {
170             val[i] = opt[pos + i];
171         }
172         return val;
173     }
174
175     public void deserialize(byte[] serializedOptions) {
176         int pos = 0;
177         byte code;
178         byte len;
179         byte[] value;
180         if (serializedOptions != null) {
181             while (pos < serializedOptions.length) {
182                 code = serializedOptions[pos++];
183                 if (code == OPT_END) {
184                     break;
185                 }
186                 len = serializedOptions[pos++];
187                 if (len + pos > serializedOptions.length) {
188                     // Throw exception???
189                     break;
190                 }
191                 value = getOptionValArray(serializedOptions, pos, len);
192                 setOption(code, value);
193                 pos += len;
194             }
195         }
196     }
197
198     @Override
199     public String toString() {
200         StringBuilder sb = new StringBuilder();
201         sb.append("{");
202         int count = 1;
203         for (DhcpOption dhcpOption: this.options.values()) {
204             //options = ArrayUtils.addAll(options, dOpt.serialize());
205             sb.append("Option").append(count++).append(dhcpOption.toString());
206         }
207         sb.append("}");
208         return sb.toString();
209     }
210
211     public boolean containsOption(byte code) {
212         return options.containsKey(code);
213     }
214
215     public DhcpOption unsetOption(byte code) {
216         return options.remove(code);
217     }
218
219 }
220