src/n_ssrf.c
SSRF address guard (see n_ssrf.h). More...
Functions
| Name | |
|---|---|
| int | zero_run(const uint8_t * ip, int lo, int hi) |
| int | v6_blocked(const uint8_t ip[16]) |
| int | v4_blocked(const uint8_t ip[4]) |
| int | n_ssrf_ipv6_allowed(const uint8_t ip[16]) classify an IPv6 address as safe-to-connect or not |
| int | n_ssrf_ipv4_allowed(const uint8_t ip[4]) classify an IPv4 address as safe-to-connect or not |
Detailed Description
SSRF address guard (see n_ssrf.h).
Author: Castagnier Mickael
Version: 1.0
Pure byte classification.
Functions Documentation
function zero_run
static int zero_run(
const uint8_t * ip,
int lo,
int hi
)
function v6_blocked
static int v6_blocked(
const uint8_t ip[16]
)
function v4_blocked
static int v4_blocked(
const uint8_t ip[4]
)
function n_ssrf_ipv6_allowed
int n_ssrf_ipv6_allowed(
const uint8_t ip[16]
)
classify an IPv6 address as safe-to-connect or not
Parameters:
- ip 16 bytes, network order
Return: 1 if ip is a public routable address; 0 otherwise. Rejects unspecified (::), loopback (::1), ULA (fc00::/7), link-local (fe80::/10), multicast (ff00::/8), documentation (2001:db8::/32) and discard (100::/64). IPv4-mapped (::ffff:0:0/96), NAT64 (64:ff9b::/96) and 6to4 (2002::/16) are unwrapped and their embedded IPv4 re-checked, so a private v4 cannot be smuggled in. NULL is rejected (0).
function n_ssrf_ipv4_allowed
int n_ssrf_ipv4_allowed(
const uint8_t ip[4]
)
classify an IPv4 address as safe-to-connect or not
Parameters:
- ip 4 bytes, network order
Return: 1 if ip is a public routable unicast address; 0 if it is unspecified / private / loopback / CGNAT / link-local / documentation / benchmark / multicast / reserved (RFC 1918, 6598, 5735, 5737, 3927, 2544, ...). NULL is rejected (0).
Source code
/*
* Nilorea Library
* Copyright (C) 2005-2026 Castagnier Mickael
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "nilorea/n_ssrf.h"
/* 1 if the IPv4 address is NOT safe to connect to (private/reserved/etc.). */
static int v4_blocked(const uint8_t ip[4]) {
uint8_t a = ip[0], b = ip[1], c = ip[2];
if (a == 0)
return 1; /* 0.0.0.0/8 "this host" */
if (a == 10)
return 1; /* 10.0.0.0/8 private (RFC1918) */
if (a == 127)
return 1; /* 127.0.0.0/8 loopback */
if (a == 100 && (b & 0xC0) == 0x40)
return 1; /* 100.64.0.0/10 CGNAT (RFC6598) */
if (a == 169 && b == 254)
return 1; /* 169.254.0.0/16 link-local */
if (a == 172 && (b & 0xF0) == 0x10)
return 1; /* 172.16.0.0/12 private */
if (a == 192 && b == 168)
return 1; /* 192.168.0.0/16 private */
if (a == 192 && b == 0 && c == 0)
return 1; /* 192.0.0.0/24 IETF protocol */
if (a == 192 && b == 0 && c == 2)
return 1; /* 192.0.2.0/24 TEST-NET-1 */
if (a == 198 && (b & 0xFE) == 18)
return 1; /* 198.18.0.0/15 benchmarking */
if (a == 198 && b == 51 && c == 100)
return 1; /* 198.51.100.0/24 TEST-NET-2 */
if (a == 203 && b == 0 && c == 113)
return 1; /* 203.0.113.0/24 TEST-NET-3 */
if (a >= 224)
return 1; /* 224/4 multicast + 240/4 reserved + 255.. */
return 0;
}
int n_ssrf_ipv4_allowed(const uint8_t ip[4]) {
return ip && !v4_blocked(ip);
}
/* Are bytes [lo, hi) of \p ip all zero? */
static int zero_run(const uint8_t* ip, int lo, int hi) {
int i;
for (i = lo; i < hi; i++)
if (ip[i])
return 0;
return 1;
}
static int v6_blocked(const uint8_t ip[16]) {
if (zero_run(ip, 0, 16)) /* :: unspecified */
return 1;
if (zero_run(ip, 0, 15) && ip[15] == 1) /* ::1 loopback */
return 1;
/* ::ffff:0:0/96 IPv4-mapped -> classify the embedded IPv4 */
if (zero_run(ip, 0, 10) && ip[10] == 0xff && ip[11] == 0xff)
return v4_blocked(ip + 12);
/* 64:ff9b::/96 NAT64 -> classify the embedded IPv4 */
if (ip[0] == 0x00 && ip[1] == 0x64 && ip[2] == 0xff && ip[3] == 0x9b &&
zero_run(ip, 4, 12))
return v4_blocked(ip + 12);
/* 2002::/16 6to4 -> classify the embedded IPv4 (bytes 2..5) */
if (ip[0] == 0x20 && ip[1] == 0x02)
return v4_blocked(ip + 2);
if ((ip[0] & 0xFE) == 0xFC) /* fc00::/7 unique-local (ULA) */
return 1;
if (ip[0] == 0xFE && (ip[1] & 0xC0) == 0x80) /* fe80::/10 link-local */
return 1;
if (ip[0] == 0xFF) /* ff00::/8 multicast */
return 1;
if (ip[0] == 0x20 && ip[1] == 0x01 && ip[2] == 0x0D && ip[3] == 0xB8)
return 1; /* 2001:db8::/32 documentation */
if (ip[0] == 0x01 && ip[1] == 0x00 && zero_run(ip, 2, 8))
return 1; /* 100::/64 discard-only */
return 0;
}
int n_ssrf_ipv6_allowed(const uint8_t ip[16]) {
return ip && !v6_blocked(ip);
}
Modules
- AABB3D
- ACCEPT_POOL
- ALLEGRO4
- ALLEGRO5
- ANIM_LIB
- ASTAR
- ATLAS_LIB
- AVRO
- CLOCK_SYNC
- COMMONS
- CONFIGFILE
- CYPHER_BASE64
- CYPHER_VIGENERE
- DEAD_RECKONING
- ENUMS
- EXCEPTIONS
- GAME_ENV
- GIT
- GUI
- HASH_TABLE
- ISOMETRIC_ENGINE
- LIST
- LOG
- LOGNODUP
- LZ4
- NETWORKING
- NETWORK_MSG
- N_COOKIES
- N_DIFF
- N_DIGEST
- N_DISK_CACHE
- N_DNS
- N_ECC
- N_ENTROPY
- N_FILES
- N_FLUIDS
- N_GUI_COLOR
- N_GUI_COLORWHEEL
- N_GUI_CURVE
- N_GUI_GRID
- N_GUI_POPUP
- N_HEX
- N_HTTP3
- N_HTTP_COOKIE
- N_HTTP_FORM
- N_HTTP_MIME
- N_HTTP_REQ
- N_HTTP_RESP
- N_HTTP_ROUTER
- N_HTTP_SERVER
- N_ICC
- N_JSON
- N_KAFKA
- N_LINALG
- N_MODULE
- N_OPENSSL_COMPAT
- N_PRETTY
- N_PTH
- N_QUERY
- N_RANDOM
- N_SHA256
- N_STR
- N_TIFF
- N_TIME
- N_USER
- N_X509
- N_ZIP
- PARTICLES
- PCRE
- PHYSICS
- SIGNALS
- STACK
- THREADS
- TRAJECTORY
- TREE
- ZLIB