package ru.kusoft.qsafe; import java.io.IOException; import java.net.*; /** Pure validation: never resolves DNS and never opens a socket. */ public final class LanPolicy { private LanPolicy() {} public static byte[] ipv4(String text) throws IOException { if(text==null||!text.matches("[0-9]{1,3}(\\.[0-9]{1,3}){3}"))throw new IOException("Нужен локальный IPv4 Pi, без доменного имени"); String[] parts=text.split("\\.");byte[] out=new byte[4]; for(int i=0;i<4;i++){int value=Integer.parseInt(parts[i]);if(value>255||(parts[i].length()>1&&parts[i].charAt(0)=='0'))throw new IOException("Неверный IPv4");out[i]=(byte)value;} int a=out[0]&255,b=out[1]&255; if(!(a==10||a==172&&b>=16&&b<=31||a==192&&b==168))throw new IOException("Внешние адреса базы запрещены. Нужен локальный IPv4 Pi"); return out; } public static String url(String address) throws IOException { try { URI uri=new URI(address==null?"":address.trim()); if(!"https".equalsIgnoreCase(uri.getScheme())||uri.getHost()==null||uri.getRawUserInfo()!=null||uri.getRawQuery()!=null||uri.getRawFragment()!=null)throw new IOException("Нужен локальный HTTPS-адрес Pi без логина и параметров"); ipv4(uri.getHost());String result=uri.toASCIIString();return result.endsWith("/")?result:result+"/"; }catch(URISyntaxException e){throw new IOException("Неверный адрес Pi",e);} } public static void identity(String ssid,String pin)throws IOException { if(ssid==null||ssid.isEmpty()||ssid.getBytes(java.nio.charset.StandardCharsets.UTF_8).length>32)throw new IOException("Укажите точное имя домашнего Wi-Fi"); if(pin==null||!pin.matches("[0-9a-fA-F]{64}"))throw new IOException("Нужен SHA-256 локального сертификата Pi из файла подключения"); } public static boolean sameSubnet(byte[] target,byte[] local,int prefix) { if(target.length!=4||local.length!=4||prefix<16||prefix>30||java.util.Arrays.equals(target,local))return false; long value=0;for(byte b:target)value=value<<8|(b&255);long hostMask=(1L<<(32-prefix))-1; if((value&hostMask)==0||(value&hostMask)==hostMask)return false; for(int i=0;i<4;i++){int bits=Math.max(0,Math.min(8,prefix-i*8));int mask=255<<(8-bits)&255;if((target[i]&mask)!=(local[i]&mask))return false;} return true; } }