10
\$\begingroup\$

I want to avoid responding to illegal or spoofed addresses. One type that's easy to detect is an address that should not exist on a packet received by my router: a special-use address.

Challenge

Given an IPv4 32-bit address, identify whether it is valid general-use address.

Challenge rules

  • Special-use addresses, as defined by the Internet Engineering Task Force, are considered invalid.
  • Special-use addresses are listed at https://en.wikipedia.org/wiki/IPv4#Special-use_addresses. A copy of the list (omitting explanatory columns) is below.
  • Input will be a character sequence in the dot-decimal notation, consisting of four positive decimal numbers (called "octets") each in the range 0–255 separated by periods (e.g., 169.254.0.31). Each octet represents an 8-bit segment of the address.
  • Output must indicate true or false, using any two indicators of your choice for true and false.

General rules:

  • This is , so shortest answer in bytes wins.
    Don't let code-golf languages discourage you from posting answers with non-codegolfing languages. Try to come up with an as short as possible answer for 'any' programming language.
  • Standard rules apply for your answer with default I/O rules, so you are allowed to use STDIN/STDOUT, functions/method with the proper parameters and return-type, full programs. Your call.
  • Default Loopholes are forbidden.
  • If possible, please add a link with a test for your code (i.e. TIO).
  • Also, adding an explanation for your answer is highly recommended.

Test cases:

  • 192.0.0.254 false
  • 192.0.1.254 true
  • 0.255.0.0 false
  • 128.2.3.82 true
  • 253.8.22.37 false
  • 203.0.114.201 true

Special-use addresses

Address Block Address Range
0.0.0.0/8 0.0.0.0–0.255.255.255
10.0.0.0/8 10.0.0.0–10.255.255.255
100.64.0.0/10 100.64.0.0–100.127.255.255
127.0.0.0/8 127.0.0.0–127.255.255.255
169.254.0.0/16 169.254.0.0–169.254.255.255
172.16.0.0/12 172.16.0.0–172.31.255.255
192.0.0.0/24 192.0.0.0–192.0.0.255
192.0.2.0/24 192.0.2.0–192.0.2.255
192.88.99.0/24 192.88.99.0–192.88.99.255
192.168.0.0/16 192.168.0.0–192.168.255.255
198.18.0.0/15 198.18.0.0–198.19.255.255
198.51.100.0/24 198.51.100.0–198.51.100.255
203.0.113.0/24 203.0.113.0–203.0.113.255
224.0.0.0/4 224.0.0.0–239.255.255.255
240.0.0.0/4 240.0.0.0–255.255.255.255

Note on address blocks. This is a shortcut method of listing an address range, called CIDR notation. The notation is four octets followed by a slash and a decimal number. The decimal number is the count of leading 1 bits in the network mask, and indicates how many leading bits in the underlying 32-bit address range are specified as fixed. For example, a mask of 24 indicates that only the last 8 bits of the address range vary. Thus writing 192.0.2.0/24 indicates that only the last octet varies. The range for 192.0.2.0/24 is 192.0.2.0–192.0.2.255.

\$\endgroup\$
3
  • \$\begingroup\$ Nice first challenge, BTW. Welcome to Code Golf! \$\endgroup\$
    – Arnauld
    Apr 15, 2021 at 0:46
  • 1
    \$\begingroup\$ @Arnauld Yeah, I was wondering whether to be faithful to the source or simplify it for golfing. I've now simplified that by omitting the special line for 255.255.255.255. The change doesn't alter the actual challenge but it does read cleaner. \$\endgroup\$
    – rsjaffe
    Apr 15, 2021 at 2:04
  • 1
    \$\begingroup\$ Also, you could combine the 224.0.0.0 and 240.0.0.0 lines into 1 (224.0.0.0/3) \$\endgroup\$ Apr 17, 2021 at 11:08

6 Answers 6

7
\$\begingroup\$

JavaScript (ES6),  179  178 bytes

Saved 1 byte thanks to @StackMeter's suggestion of merging the last 2 blocks

Expects a string and returns a Boolean value.

s=>'00000g0chhqo3h65u64ekjyo5w90qw5yu2vg6nq96o6nq98g6o5rvk6ojr486vb4096vgz2871grvk7romqd'.match(/.{6}/g,[a,b,c]=s.split`.`).every(S=>(x=parseInt(S,36))>>5^(a<<16|b<<8|c)&-(1<<x))

Try it online!

How?

Encoding of address blocks

Our data string encodes all blocks of special-use addresses.

We can notice that the 4th byte is always \$0\$ and can be ignored. Given a block in CIDR notation A.B.C.D/M, what's actually encoded is:

A << 21 | B << 13 | C << 5 | (24 - M)

This is a 29-bit value which fits in 6 characters in base 36.

For instance, 169.254.0.0/16 is encoded as:

169 << 21 | 254 << 13 | 0 << 5 | (24 - 16) = 356499464

which is 5w90qw in base 36.

IP test

Given an IP address \$s\$, we first extract the first 3 bytes into \$(a,b,c)\$ and ignore the 4th one:

[a, b, c] = s.split`.`

For each 29-bit encoded block address \$x\$, we do the following test:

x >> 5 ^ (a << 16 | b << 8 | c) & -(1 << x)

where -(1 << x) is our 24-bit subnet mask.

Conveniently, we can do 1 << x without isolating the 5 least significant bits of \$x\$ because this is done implicitly by the shift operations, by ECMAScript specification.

\$\endgroup\$
2
  • \$\begingroup\$ Since I eliminated the last entry in the table, you may want to change your text from "last 3 blocks" to "last 2 blocks" \$\endgroup\$
    – rsjaffe
    Apr 15, 2021 at 2:32
  • \$\begingroup\$ @rsjaffe Rephrased accordingly. Thank you. \$\endgroup\$
    – Arnauld
    Apr 15, 2021 at 7:56
2
\$\begingroup\$

Retina 0.8.2, 138 bytes

\.
-
2=`\d+
$*
^(0|10|100-1{64,127}|127|169-1{254}|172-1{16,31}|192--[02]|192-1{88}-99|192-1{168}|198-1{51}-100|203--113|22[4-9]|2[3-5].)-

Try it online! Link includes test cases. Outputs 1 for special use, 0 for general use. Explanation:

\.
-

Change the .s to -s because .s are special in regex.

2=`\d+
$*

Convert the second octet to unary.

^(0|10|100-1{64,127}|127|169-1{254}|172-1{16,31}|192--[02]|192-1{88}-99|192-1{168}|198-1{51}-100|203--113|22[4-9]|2[3-5].)-

Match the given special use addresses.

\$\endgroup\$
1
\$\begingroup\$

Python 3 - 307 bytes

a=input()
f=int
b,c,d,e=a.split(".")
b,c,d,e=f(b),f(c),f(d),f(e)
print(not(not(b==0or b==10or (b==100and 63<c<128)or b==127or(b,c==169,254)or(b==172and 15<c<32)or(b,c,d==192,0,0)or(b,c,d==192,0,2)or(b,c,d==192,88,89)or(b,c==192,168)or(b==198and 17<c<20)or(b,c,d==198,51,100)or(b,c,d==203,0,113)or b>223)))

Try it online!

Fun challenge - hooray for conditionals!

Just runs the appropriate tests and gives a value of true or false (the not(not()) was necessary, else it would return a tuple for some reason).

Fairly optimized for the method (I did use b,c,d == instead of b == and c == and d ==, and stripped many spaces), but this could easily be golfed by someone better. (-34 from Razetime)

\$\endgroup\$
2
  • \$\begingroup\$ you can remove a lot of unnecessary whitespace for -34: Try it online! \$\endgroup\$
    – Razetime
    Apr 17, 2021 at 12:15
  • \$\begingroup\$ This isn't valid. You need to convert the strings b, c, d, e to integers in order to compare them against integers \$\endgroup\$
    – pxeger
    Apr 18, 2021 at 9:13
1
\$\begingroup\$

JavaScript, 154 bytes

s=>[[a,b,c]=s.split`.`,a-10,a%127,a<224,(b|=a<<8)>>6^401,b>>4^2753,b/2^25353,b-43518,b-49320,b-49152|c&-3,d=b-49240<<8|c-99,d-383745,d-698382].some(x=>!x)

Try it online!

This answer simply translate the table to a formula.

Address block Address range Formula
0.0.0.0/8 0.0.0.0 ~ 0.255.255.255 [a,b,c]=s.split`.`,a%127
10.0.0.0/8 10.0.0.0 ~ 10.255.255.255 a-10
127.0.0.0/8 127.0.0.0 ~ 127.255.255.255 a%127
224.0.0.0/4 224.0.0.0 ~ 239.255.255.255 a<224
240.0.0.0/4 240.0.0.0 ~ 255.255.255.255 a<224
100.64.0.0/10 100.64.0.0 ~ 100.127.255.255 (b|=a<<8)>>6^401
172.16.0.0/12 172.16.0.0 ~ 172.31.255.255 b>>4^2753
198.18.0.0/15 198.18.0.0 ~ 198.19.255.255 b/2^25353
169.254.0.0/16 169.254.0.0 ~ 169.254.255.255 b-43518
192.168.0.0/16 192.168.0.0 ~ 192.168.255.255 b-49320
192.0.0.0/24 192.0.0.0 ~ 192.0.0.255 b-49152|c&-3
192.0.2.0/24 192.0.2.0 ~ 192.0.2.255 b-49152|c&-3
192.88.99.0/24 192.88.99.0 ~ 192.88.99.255 d=b-49240<<8|c-99
198.51.100.0/24 198.51.100.0 ~ 198.51.100.255 d-383745
203.0.113.0/24 203.0.113.0 ~ 203.0.113.255 d-698382
\$\endgroup\$
0
\$\begingroup\$

Python 3, 248 bytes

b,c,d,e=[*map(int,input().split("."))]
a=192
print(not(not(b==0 or b==10or(b==100and 63<c<128)or b==127or(b,c==169,254)or(b==172and 15<c<32)or(b,c,d in[a,0,0],[a,0,2],[a,88,89],[198,51,100],[203,0,113])or(b,c==a,168)or(b==198and 17<c<20)or b>223)))

Try it online!

-1 byte thanks to hyper-neutrino

\$\endgroup\$
1
  • 1
    \$\begingroup\$ You can get rid of a few bytes by removing the space after the b==0 at the start as well as in or ( near the front. \$\endgroup\$
    – hyper-neutrino
    May 9, 2021 at 5:58
0
\$\begingroup\$

Python 3, 173 bytes

a,b,c,d=map(int,input().split("."))
b|=a<<8
d=b-49240<<8|c-99
print(214>a-10!=0<a%127<b>>6!=401>0<b>>4^2753<25353!=b>>1<43518!=b!=49320>b-49152|c&-3!=0!=d!=383745<698382!=d)

Try it online!

Never write a!=10and a!=127 in python ! try 10!=a!=127 instead!

\$\endgroup\$

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.