@@ -967,11 +967,57 @@ lws_sa46_parse_numeric_address(const char *ads, lws_sockaddr46 *sa46)
967967 sizeof (sa46 -> sa4 .sin_addr .s_addr ));
968968
969969 return 0 ;
970+ #elif defined(LWS_WITH_IPV6 )
971+ /*
972+ * IPv4 literal in a build with IPv4 compiled out: represent it as an
973+ * IPv4-mapped IPv6 address (::ffff:a.b.c.d) so it can be stored in an
974+ * AF_INET6 sockaddr and, on a dual-stack host (or behind NAT64),
975+ * actually reached. On a host with no IPv4 route this connect will
976+ * simply fail cleanly at the socket layer; the alternative ("refuse
977+ * the literal") makes DNS-server discovery and similar config unusable
978+ * on otherwise-capable dual-stack hosts for no benefit.
979+ */
980+ {
981+ static uint8_t did_notice ;
982+
983+ sa46 -> sa6 .sin6_family = AF_INET6 ;
984+ memset (sa46 -> sa6 .sin6_addr .s6_addr , 0 , 10 );
985+ sa46 -> sa6 .sin6_addr .s6_addr [10 ] = 0xff ;
986+ sa46 -> sa6 .sin6_addr .s6_addr [11 ] = 0xff ;
987+ memcpy (& sa46 -> sa6 .sin6_addr .s6_addr [12 ], a , 4 );
988+
989+ if (!did_notice ) {
990+ did_notice = 1 ;
991+ lwsl_notice ("IPv4 literal '%s' mapped to ::ffff: in this "
992+ "IPv6-only build; reachable only on dual-stack "
993+ "or via NAT64\n" , ads );
994+ }
995+
996+ return 0 ;
997+ }
970998#else
971999 return -1 ;
9721000#endif
9731001}
9741002
1003+ int
1004+ lws_sa46_is_ipv4_mapped (const lws_sockaddr46 * sa46 )
1005+ {
1006+ #if defined(LWS_WITH_IPV6 )
1007+ if (sa46 && sa46 -> sa4 .sin_family == AF_INET6 ) {
1008+ const uint8_t * a = sa46 -> sa6 .sin6_addr .s6_addr ;
1009+
1010+ return !a [0 ] && !a [1 ] && !a [2 ] && !a [3 ] && !a [4 ] && !a [5 ] &&
1011+ !a [6 ] && !a [7 ] && !a [8 ] && !a [9 ] &&
1012+ a [10 ] == 0xff && a [11 ] == 0xff ;
1013+ }
1014+ #else
1015+ (void )sa46 ;
1016+ #endif
1017+
1018+ return 0 ;
1019+ }
1020+
9751021int
9761022lws_write_numeric_address (const uint8_t * ads , int size , char * buf , size_t len )
9771023{
@@ -1313,6 +1359,27 @@ lws_is_lan_address(const char *ads)
13131359#if defined(LWS_WITH_IPV6 )
13141360 uint8_t * p = (uint8_t * )& sa46 .sa6 .sin6_addr .s6_addr ;
13151361
1362+ /*
1363+ * An IPv4-mapped address (::ffff:a.b.c.d) is evaluated by its
1364+ * embedded IPv4 address, so "is this LAN" agrees whether the
1365+ * address is stored as AF_INET or as a mapped AF_INET6 (as
1366+ * happens in an IPv6-only build).
1367+ */
1368+ if (lws_sa46_is_ipv4_mapped (& sa46 )) {
1369+ uint8_t * q = & p [12 ];
1370+
1371+ if (q [0 ] == 10 )
1372+ return 1 ;
1373+ if (q [0 ] == 172 && q [1 ] >= 16 && q [1 ] <= 31 )
1374+ return 1 ;
1375+ if (q [0 ] == 192 && q [1 ] == 168 )
1376+ return 1 ;
1377+ if (q [0 ] == 127 )
1378+ return 1 ;
1379+
1380+ return 0 ;
1381+ }
1382+
13161383 /* fc00::/7 */
13171384 if ((p [0 ] & 0xfe ) == 0xfc )
13181385 return 1 ;
0 commit comments