1   package com.atlassian.security.auth.trustedapps;
2   
3   import java.util.LinkedList;
4   import java.util.List;
5   import java.util.Set;
6   import java.util.StringTokenizer;
7   
8   /**
9    * simple list based implementation. Matches using exact matches or wildcards like: 192.168.*.* or 192.145.372.*
10   *
11   * TODO: Add support for IPv6 and CIDR
12   */
13  public class DefaultIPMatcher implements IPMatcher
14  {
15      private static class AddressMask
16      {
17          private final int address;
18          private final int mask;
19  
20          public AddressMask(final int address, final int mask)
21          {
22              this.address = address;
23              this.mask = mask;
24          }
25  
26          public boolean matches(final int otherAddress)
27          {
28              return address == (otherAddress & mask);
29          }
30  
31          static AddressMask create(final int[] pattern)
32          {
33              int address = 0;
34              int mask = 0;
35  
36              for (final int element : pattern)
37              {
38                  address = address << 8;
39                  mask = mask << 8;
40  
41                  if (element != -1)
42                  {
43                      address = address | element;
44                      mask = mask | 0xFF;
45                  }
46              }
47  
48              return new AddressMask(address, mask);
49          }
50      }
51  
52      private static final String WILDCARD = "*";
53  
54      private final List<AddressMask> addressMasks;
55  
56      /**
57       * Main ctor.
58       * 
59       * @param patterns the Set<String> of allowed pattern Strings
60       * @throws IPAddressFormatException if the pattern does not represent a valid IP address
61       */
62      public DefaultIPMatcher(final Set<String> patterns) throws IPAddressFormatException
63      {
64          addressMasks = new LinkedList<AddressMask>();
65          for (final String patternStr : patterns)
66          {
67              addressMasks.add(AddressMask.create(parsePatternString(patternStr)));
68          }
69      }
70  
71      public static int[] parsePatternString(final String patternStr)
72      {
73          final int[] pattern = new int[4];
74          final StringTokenizer st = new StringTokenizer(patternStr, ".");
75          if (st.countTokens() != 4)
76          {
77              throw new IPAddressFormatException(patternStr);
78          }
79  
80          for (int i = 0; i < 4; i++)
81          {
82              final String token = st.nextToken().trim();
83              if (WILDCARD.equals(token))
84              {
85                  pattern[i] = -1;
86              }
87              else
88              {
89                  try
90                  {
91                      final int value = Integer.valueOf(token).intValue();
92  
93                      if ((value < 0) || (value > 255))
94                      {
95                          throw new IPAddressFormatException(patternStr);
96                      }
97  
98                      pattern[i] = value;
99                  }
100                 catch (final NumberFormatException e)
101                 {
102                     throw new IPAddressFormatException(patternStr);
103                 }
104             }
105         }
106         return pattern;
107     }
108 
109     public boolean match(final String ipAddress)
110     {
111         if (addressMasks.isEmpty())
112         {
113             return true;
114         }
115 
116         final int address = toAddress(ipAddress);
117 
118         for (final Object element : addressMasks)
119         {
120             final AddressMask addressMask = (AddressMask) element;
121             if (addressMask.matches(address))
122             {
123                 return true;
124             }
125         }
126         return false;
127     }
128 
129     private int toAddress(final String ipAddress)
130     {
131         int address = 0;
132         final int[] parsedIPAddr = parsePatternString(ipAddress);
133         for (final int element : parsedIPAddr)
134         {
135             address = address << 8;
136             address = address | element;
137         }
138         return address;
139     }
140 }