1/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.net;
18
19import android.os.Parcel;
20import android.os.Parcelable;
21
22import java.net.UnknownHostException;
23import java.net.InetAddress;
24import java.net.Inet4Address;
25import java.net.Inet6Address;
26
27import java.util.Collection;
28
29/**
30 * A simple container for route information.
31 *
32 * @hide
33 */
34public class RouteInfo implements Parcelable {
35    /**
36     * The IP destination address for this route.
37     */
38    private final LinkAddress mDestination;
39
40    /**
41     * The gateway address for this route.
42     */
43    private final InetAddress mGateway;
44
45    private final boolean mIsDefault;
46    private final boolean mIsHost;
47
48    public RouteInfo(LinkAddress destination, InetAddress gateway) {
49        if (destination == null) {
50            if (gateway != null) {
51                if (gateway instanceof Inet4Address) {
52                    destination = new LinkAddress(Inet4Address.ANY, 0);
53                } else {
54                    destination = new LinkAddress(Inet6Address.ANY, 0);
55                }
56            } else {
57                // no destination, no gateway. invalid.
58                throw new RuntimeException("Invalid arguments passed in.");
59            }
60        }
61        if (gateway == null) {
62            if (destination.getAddress() instanceof Inet4Address) {
63                gateway = Inet4Address.ANY;
64            } else {
65                gateway = Inet6Address.ANY;
66            }
67        }
68        mDestination = new LinkAddress(NetworkUtils.getNetworkPart(destination.getAddress(),
69                destination.getNetworkPrefixLength()), destination.getNetworkPrefixLength());
70        mGateway = gateway;
71        mIsDefault = isDefault();
72        mIsHost = isHost();
73    }
74
75    public RouteInfo(InetAddress gateway) {
76        this(null, gateway);
77    }
78
79    public static RouteInfo makeHostRoute(InetAddress host) {
80        return makeHostRoute(host, null);
81    }
82
83    public static RouteInfo makeHostRoute(InetAddress host, InetAddress gateway) {
84        if (host == null) return null;
85
86        if (host instanceof Inet4Address) {
87            return new RouteInfo(new LinkAddress(host, 32), gateway);
88        } else {
89            return new RouteInfo(new LinkAddress(host, 128), gateway);
90        }
91    }
92
93    private boolean isHost() {
94        return (mGateway.equals(Inet4Address.ANY) || mGateway.equals(Inet6Address.ANY));
95    }
96
97    private boolean isDefault() {
98        boolean val = false;
99        if (mGateway != null) {
100            if (mGateway instanceof Inet4Address) {
101                val = (mDestination == null || mDestination.getNetworkPrefixLength() == 0);
102            } else {
103                val = (mDestination == null || mDestination.getNetworkPrefixLength() == 0);
104            }
105        }
106        return val;
107    }
108
109
110    public LinkAddress getDestination() {
111        return mDestination;
112    }
113
114    public InetAddress getGateway() {
115        return mGateway;
116    }
117
118    public boolean isDefaultRoute() {
119        return mIsDefault;
120    }
121
122    public boolean isHostRoute() {
123        return mIsHost;
124    }
125
126    public String toString() {
127        String val = "";
128        if (mDestination != null) val = mDestination.toString();
129        if (mGateway != null) val += " -> " + mGateway.getHostAddress();
130        return val;
131    }
132
133    public int describeContents() {
134        return 0;
135    }
136
137    public void writeToParcel(Parcel dest, int flags) {
138        if (mDestination == null) {
139            dest.writeByte((byte) 0);
140        } else {
141            dest.writeByte((byte) 1);
142            dest.writeByteArray(mDestination.getAddress().getAddress());
143            dest.writeInt(mDestination.getNetworkPrefixLength());
144        }
145
146        if (mGateway == null) {
147            dest.writeByte((byte) 0);
148        } else {
149            dest.writeByte((byte) 1);
150            dest.writeByteArray(mGateway.getAddress());
151        }
152    }
153
154    @Override
155    public boolean equals(Object obj) {
156        if (this == obj) return true;
157
158        if (!(obj instanceof RouteInfo)) return false;
159
160        RouteInfo target = (RouteInfo) obj;
161
162        boolean sameDestination = ( mDestination == null) ?
163                target.getDestination() == null
164                : mDestination.equals(target.getDestination());
165
166        boolean sameAddress = (mGateway == null) ?
167                target.getGateway() == null
168                : mGateway.equals(target.getGateway());
169
170        return sameDestination && sameAddress
171            && mIsDefault == target.mIsDefault;
172    }
173
174    @Override
175    public int hashCode() {
176        return (mDestination == null ? 0 : mDestination.hashCode())
177            + (mGateway == null ? 0 :mGateway.hashCode())
178            + (mIsDefault ? 3 : 7);
179    }
180
181    public static final Creator<RouteInfo> CREATOR =
182        new Creator<RouteInfo>() {
183        public RouteInfo createFromParcel(Parcel in) {
184            InetAddress destAddr = null;
185            int prefix = 0;
186            InetAddress gateway = null;
187
188            if (in.readByte() == 1) {
189                byte[] addr = in.createByteArray();
190                prefix = in.readInt();
191
192                try {
193                    destAddr = InetAddress.getByAddress(addr);
194                } catch (UnknownHostException e) {}
195            }
196
197            if (in.readByte() == 1) {
198                byte[] addr = in.createByteArray();
199
200                try {
201                    gateway = InetAddress.getByAddress(addr);
202                } catch (UnknownHostException e) {}
203            }
204
205            LinkAddress dest = null;
206
207            if (destAddr != null) {
208                dest = new LinkAddress(destAddr, prefix);
209            }
210
211            return new RouteInfo(dest, gateway);
212        }
213
214        public RouteInfo[] newArray(int size) {
215            return new RouteInfo[size];
216        }
217    };
218
219    private boolean matches(InetAddress destination) {
220        if (destination == null) return false;
221
222        // if the destination is present and the route is default.
223        // return true
224        if (isDefault()) return true;
225
226        // match the route destination and destination with prefix length
227        InetAddress dstNet = NetworkUtils.getNetworkPart(destination,
228                mDestination.getNetworkPrefixLength());
229
230        return mDestination.getAddress().equals(dstNet);
231    }
232
233    /**
234     * Find the route from a Collection of routes that best matches a given address.
235     * May return null if no routes are applicable.
236     * @param routes a Collection of RouteInfos to chose from
237     * @param dest the InetAddress your trying to get to
238     * @return the RouteInfo from the Collection that best fits the given address
239     */
240    public static RouteInfo selectBestRoute(Collection<RouteInfo> routes, InetAddress dest) {
241        if ((routes == null) || (dest == null)) return null;
242
243        RouteInfo bestRoute = null;
244        // pick a longest prefix match under same address type
245        for (RouteInfo route : routes) {
246            if (NetworkUtils.addressTypeMatches(route.mDestination.getAddress(), dest)) {
247                if ((bestRoute != null) &&
248                        (bestRoute.mDestination.getNetworkPrefixLength() >=
249                        route.mDestination.getNetworkPrefixLength())) {
250                    continue;
251                }
252                if (route.matches(dest)) bestRoute = route;
253            }
254        }
255        return bestRoute;
256    }
257}
258