0

I'm trying to exclude some internal IP addresses and some internal IP address formats from viewing certain logos and links in the site.I have multiple range of IP addresses(sample given below). Is it possible to write a regex that could match all the IP addresses in the list below using javascript?

10.X.X.X
12.122.X.X
12.211.X.X
64.X.X.X
64.23.X.X
74.23.211.92
and 10 more
2
  • 4
    Yes, it is possible: .* matches all these... Your question is vague - do you want any kind of IPv4 address to match? If not, what are the rules for matching? Is that a real X or did you mean this as a placeholder for "anything"? What are the "10 more" IP addresses? Commented Apr 19, 2012 at 21:04
  • I have 15 IP address format(Note: "format" some are exact IP addresses and some that need to match the formats like 10.X.X.X) I need to match. I have provided few samples. Commented Apr 19, 2012 at 21:10

5 Answers 5

9

Quote the periods, replace the X's with \d+, and join them all together with pipes:

const allowedIPpatterns = [ 
  "10.X.X.X", 
  "12.122.X.X",
  "12.211.X.X",
  "64.X.X.X",
  "64.23.X.X",
  "74.23.211.92"   //, etc.
];

const allowedRegexStr = '^(?:' + 
  allowedIPpatterns.
    join('|').
    replace(/\./g, '\\.').
    replace(/X/g, '\\d+') + 
  ')$';

 const allowedRegexp = new RegExp(allowedRegexStr);

Then you're all set:

 '10.1.2.3'.match(allowedRegexp) // => ['10.1.2.3']
 '100.1.2.3'.match(allowedRegexp) // => null

How it works:

First, we have to turn the individual IP patterns into regular expressions matching their intent. One regular expression for "all IPs of the form '12.122.X.X'" is this:

^12\.122\.\d+\.\d+$

  • ^ means the match has to start at the beginning of the string; otherwise, 112.122.X.X IPs would also match.
  • 12 etc: digits match themselves
  • \.: a period in a regex matches any character at all; we want literal periods, so we put a backslash in front.
  • \d: shorthand for [0-9]; matches any digit.
  • +: means "1 or more" - 1 or more digits, in this case.
  • $: similarly to ^, this means the match has to end at the end of the string.

So, we turn the IP patterns into regexes like that. For an individual pattern you could use code like this:

const regexStr = `^` + ipXpattern. 
  replace(/\./g, '\\.').
  replace(/X/g, '\\d+') + 
  `$`;

Which just replaces all .s with \. and Xs with \d+ and sticks the ^ and $ on the ends.

(Note the doubled backslashes; both string parsing and regex parsing use backslashes, so wherever we want a literal one to make it past the string parser to the regular expression parser, we have to double it.)

In a regular expression, the alternation this|that matches anything that matches either this or that. So we can check for a match against all the IP's at once if we to turn the list into a single regex of the form re1|re2|re3|...|relast.

Then we can do some refactoring to make the regex matcher's job easier; in this case, since all the regexes are going to have ^...$, we can move those constraints out of the individual regexes and put them on the whole thing: ^(10\.\d+\.\d+\.\d+|12\.122\.\d+\.\d+|...)$. The parentheses keep the ^ from being only part of the first pattern and $ from being only part of the last. But since plain parentheses capture as well as group, and we don't need to capture anything, I replaced them with the non-grouping version (?:..).

And in this case we can do the global search-and-replace once on the giant string instead of individually on each pattern. So the result is the code above:

const allowedRegexStr = '^(?:' + 
  allowedIPpatterns.
    join('|').
    replace(/\./g, '\\.').
    replace(/X/g, '\\d+') + 
  ')$';

That's still just a string; we have to turn it into an actual RegExp object to do the matching:

const allowedRegexp = new RegExp(allowedRegexStr);

As written, this doesn't filter out illegal IPs - for instance, 10.1234.5678.9012 would match the first pattern. If you want to limit the individual byte values to the decimal range 0-255, you can use a more complicated regex than \d+, like this:

(?:\d{1,2}|1\d{2}|2[0-4]\d|25[0-5])

That matches "any one or two digits, or '1' followed by any two digits, or '2' followed by any of '0' through '4' followed by any digit, or '25' followed by any of '0' through '5'". Replacing the \d with that turns the full string-munging expression into this:

const allowedRegexStr = '^(?:' + 
  allowedIPpatterns.
    join('|').
    replace(/\./g, '\\.').
    replace(/X/g, '(?:\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5])') + 
  ')$';

And makes the actual regex look much more unwieldy:

^(?:10\.(?:\d{1,2}|1\d{2}|2[0-4]\d|25[0-5])\.(?:\d{1,2}|1\d{2}|2[0-4]\d|25[0-5]).(?:\d{1,2}|1\d{2}|2[0-4]\d|25[0-5])|12\.122\....

but you don't have to look at it, just match against it. :)

Sign up to request clarification or add additional context in comments.

1 Comment

could you explain what this line of code does? '^(?:' + allowedIPs.join('|').replace(/\./g, '\\.').replace(/X/g, '[^.]+') + ')$' I see it working as per my requirement, very curious how it does..
1

You could do it in regex, but it's not going to be pretty, especially since JavaScript doesn't even support verbose regexes, which means that it has to be one humongous line of regex without any comments. Furthermore, regexes are ill-suited for matching ranges of numbers. I suspect that there are better tools for dealing with this.

Well, OK, here goes (for the samples you provided):

var myregexp = /\b(?:74\.23\.211\.92|(?:12\.(?:122|211)|64\.23)\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])|(?:10|64)\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9]))\b/g;

As a verbose ("readable") regex:

\b                 # start of number
(?:                # Either match...
 74\.23\.211\.92   # an explicit address
|                  # or
 (?:               # an address that starts with
  12\.(?:122|211)  # 12.122 or 12.211
 |                 # or
  64\.23           # 64.23
 )
 \.                # . 
 (?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\.  # followed by 0..255 and a dot
 (?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])    # followed by 0..255
|                  # or
 (?:10|64)         # match 10 or 64
 \.                # . 
 (?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\.  # followed by 0..255 and a dot
 (?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\.  # followed by 0..255 and a dot
 (?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])    # followed by 0..255
)
\b                 # end of number

Comments

0

/^(X|\d{1,3})(\.(X|\d{1,3})){3}$/ should do it.

3 Comments

could you care to explain what this regex does?
It matches a string in the format X.X.X.X, where X can be 1-3 numeric digits or a capital X. The meat of it is this sub-expression \.(X|\d{1,3}). That matches a period, followed by either 1-3 numeric digits or an X.
This is a pretty good reference for regex syntax: regular-expressions.info/reference.html
0

If you don't actually need to match the "X" character you could use this:

\b(?:\d{1,3}\.){3}\d{1,3}\b

Otherwise I would use the solution cebarrett provided.

Comments

0

I'm not entirely sure of what you're trying to achieve here (doesn't look anyone else is either).

However, if it's validation, then here's a solution to validate an IP address that doesn't use RegEx. First, split the input string at the dot. Then using parseInt on the number, make sure it isn't higher than 255.

function ipValidator(ipAddress) {
var ipSegments = ipAddress.split('.');
for(var i=0;i<ipSegments.length;i++)
    {
        if(parseInt(ipSegments[i]) > 255){
            return 'fail';
        }
    }
return 'match';
}

Running the following returns 'match':

document.write(ipValidator('10.255.255.125'));

Whereas this will return 'fail':

document.write(ipValidator('10.255.256.125'));

Here's a noted version in a jsfiddle with some examples, http://jsfiddle.net/VGp2p/2/

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.