MODULE IP;
IMPORT S := SYSTEM, KernelLog, Strings, Network;
CONST
Ok* = 0;
NilAdrIPv4 = 0;
IPv4* = 4;
IPv6* = 6;
NilAdrIdent = -1;
MaxNofDNS = 4;
TYPE
Adr* = RECORD
ipv4Adr*: LONGINT;
ipv6Adr*: ARRAY 16 OF CHAR;
usedProtocol*: LONGINT;
data*: LONGINT;
END;
Packet* = POINTER TO ARRAY OF CHAR;
Name* = ARRAY 128 OF CHAR;
ARPHandler* = PROCEDURE {DELEGATE} ( ip: Adr; complete: BOOLEAN;
link: Network.LinkAdr;
size, sendTime, updateTime, updateDate, hash: LONGINT);
Interface* = OBJECT
VAR
localAdr-, maskAdr-, gatewayAdr-, subnetAdr-, broadAdr-: Adr;
name-: Name;
dev-: Network.LinkDevice;
DNS-: ARRAY MaxNofDNS OF Adr;
DNScount-: LONGINT;
next*: Interface;
closed-: BOOLEAN;
protocol-: LONGINT;
PROCEDURE & Init*( addr: Adr );
BEGIN
localAdr := addr;
name := "dummy";
END Init;
END Interface;
InterfaceHandler* = PROCEDURE {DELEGATE} (int: Interface);
VAR
NilAdr*: Adr;
preferredProtocol*: LONGINT;
PROCEDURE IsNilAdr*( adr: Adr ): BOOLEAN;
VAR isNil: BOOLEAN; i: LONGINT;
BEGIN
CASE adr.usedProtocol OF
| IPv4: RETURN (adr.ipv4Adr = NilAdrIPv4)
| IPv6: isNil := TRUE; i := 0;
WHILE ((i < 16) & isNil) DO
IF adr.ipv6Adr[i] # 0X THEN isNil := FALSE END;
INC( i );
END;
RETURN isNil;
| NilAdrIdent:
RETURN TRUE;
ELSE
RETURN TRUE;
END;
END IsNilAdr;
PROCEDURE AdrsEqual*( adr1, adr2: Adr ): BOOLEAN;
VAR equal: BOOLEAN; i: LONGINT;
BEGIN
IF adr1.usedProtocol # adr2.usedProtocol THEN RETURN FALSE END;
CASE adr1.usedProtocol OF
| IPv4: IF adr1.ipv4Adr = adr2.ipv4Adr THEN RETURN TRUE END;
| IPv6: equal := TRUE; i := 0;
WHILE ((i < 16) & equal) DO
IF adr1.ipv6Adr[i] # adr2.ipv6Adr[i] THEN equal := FALSE END;
INC( i );
END;
IF adr1.data # adr2.data THEN equal := FALSE END;
RETURN equal;
| NilAdrIdent:
IF adr2.usedProtocol = NilAdrIdent THEN RETURN TRUE ELSE RETURN FALSE END
ELSE RETURN FALSE
END;
RETURN FALSE
END AdrsEqual;
PROCEDURE StrToAdr*( ipString: ARRAY OF CHAR ): Adr;
VAR retAdr: Adr; i, j, x: LONGINT;
adr: ARRAY 4 OF CHAR;
ok: BOOLEAN;
charCount: LONGINT;
ipv6AdrPart: ARRAY 6 OF CHAR;
ipv6AdrRight: ARRAY 16 OF CHAR;
hexToChar: ARRAY 3 OF CHAR;
leftParts: LONGINT;
rightParts: LONGINT;
val, res: LONGINT;
state: LONGINT;
dPointOcc: BOOLEAN;
prefixVal: LONGINT;
PROCEDURE ComputeIPv6Part( ): BOOLEAN;
BEGIN
CASE charCount OF
| 0: RETURN TRUE;
| 1, 2: IF dPointOcc THEN ipv6AdrRight[rightParts] := 0X; INC( rightParts );
ELSE retAdr.ipv6Adr[leftParts] := 0X; INC( leftParts );
END;
Strings.HexStrToInt( ipv6AdrPart, val, res );
IF res = Strings.Ok THEN
IF dPointOcc THEN ipv6AdrRight[rightParts] := CHR( val ); INC( rightParts );
ELSE retAdr.ipv6Adr[leftParts] := CHR( val ); INC( leftParts );
END;
ELSE RETURN FALSE
END;
| 3: hexToChar[0] := ipv6AdrPart[0]; hexToChar[1] := 0X;
Strings.HexStrToInt( hexToChar, val, res );
IF res = Strings.Ok THEN
IF dPointOcc THEN ipv6AdrRight[rightParts] := CHR( val ); INC( rightParts );
ELSE retAdr.ipv6Adr[leftParts] := CHR( val ); INC( leftParts );
END;
ELSE RETURN FALSE
END;
ipv6AdrPart[0] := "0"; Strings.HexStrToInt( ipv6AdrPart, val, res );
IF res = Strings.Ok THEN
IF dPointOcc THEN ipv6AdrRight[rightParts] := CHR( val ); INC( rightParts );
ELSE retAdr.ipv6Adr[leftParts] := CHR( val ); INC( leftParts );
END;
ELSE RETURN FALSE
END;
| 4: hexToChar[0] := ipv6AdrPart[0]; hexToChar[1] := ipv6AdrPart[1]; hexToChar[2] := 0X;
Strings.HexStrToInt( hexToChar, val, res );
IF res = Strings.Ok THEN
IF dPointOcc THEN ipv6AdrRight[rightParts] := CHR( val ); INC( rightParts );
ELSE retAdr.ipv6Adr[leftParts] := CHR( val ); INC( leftParts );
END;
ELSE RETURN FALSE
END;
ipv6AdrPart[0] := "0"; ipv6AdrPart[1] := "0"; Strings.HexStrToInt( ipv6AdrPart, val, res );
IF res = Strings.Ok THEN
IF dPointOcc THEN ipv6AdrRight[rightParts] := CHR( val ); INC( rightParts );
ELSE retAdr.ipv6Adr[leftParts] := CHR( val ); INC( leftParts );
END;
ELSE RETURN FALSE
END;
ELSE RETURN FALSE;
END;
charCount := 0; RETURN TRUE;
END ComputeIPv6Part;
BEGIN
retAdr := NilAdr;
IF IsValidIPv4Str( ipString ) THEN
i := 0; j := 0; x := -1; ok := FALSE;
LOOP
IF (ipString[i] = ".") OR (ipString[i] = 0X) THEN
IF (x < 0) OR (x > 255) OR (j = 4) THEN EXIT END;
adr[j] := CHR( x );
IF ipString[i] = 0X THEN ok := (j = 3); EXIT END;
x := -1; INC( i ); INC( j )
ELSIF (ipString[i] >= "0") & (ipString[i] <= "9") THEN
IF x = -1 THEN x := 0 END;
x := x*10 + (ORD( ipString[i] ) - ORD( "0" )); INC( i )
ELSE EXIT
END
END;
IF ok THEN retAdr.ipv4Adr := S.VAL( LONGINT, adr ); retAdr.usedProtocol := IPv4; RETURN retAdr;
ELSE RETURN NilAdr;
END
ELSIF IsValidIPv6Str( ipString ) THEN
i := 0; state := 1; charCount := 0; dPointOcc := FALSE;
retAdr.usedProtocol := 6; retAdr.ipv4Adr := NilAdrIPv4;
i := 0; j := 0; charCount := 0; leftParts := 0; rightParts := 0; prefixVal := 0;
Strings.UpperCase( ipString );
WHILE (i < (LEN( ipString ) - 1)) & (ipString[i] # 0X) DO
CASE state OF
| -1:
RETURN NilAdr;
| 1:
IF ipString[i] = ":" THEN
ipv6AdrPart[charCount] := 0X;
IF ~ComputeIPv6Part() THEN RETURN NilAdr END;
state := 2;
ELSIF ipString[i] = "/" THEN
ipv6AdrPart[charCount] := 0X;
IF ~ComputeIPv6Part() THEN RETURN NilAdr END;
state := 3;
ELSE
ipv6AdrPart[charCount] := ipString[i]; INC( charCount );
END;
| 2:
IF ipString[i] = ":" THEN dPointOcc := TRUE; state := 4
ELSE
state := 1; charCount := 0; ipv6AdrPart[charCount] := ipString[i]; INC( charCount );
END;
| 3:
prefixVal := (prefixVal*10) + (ORD( ipString[i] ) - ORD( "0" ));
| 4:
IF ipString[i] = "/" THEN state := 3
ELSE
IF ~ComputeIPv6Part() THEN RETURN NilAdr END;
state := 1; charCount := 0; ipv6AdrPart[charCount] := ipString[i]; INC( charCount )
END;
ELSE
END;
INC( i );
END;
ipv6AdrPart[charCount] := 0X;
IF charCount # 0 THEN
IF ~ComputeIPv6Part() THEN RETURN NilAdr END;
END;
IF dPointOcc THEN
FOR i := leftParts TO ((LEN( retAdr.ipv6Adr ) - 1) - rightParts) DO retAdr.ipv6Adr[i] := 0X END;
FOR i := 0 TO (rightParts - 1) DO
retAdr.ipv6Adr[(LEN( retAdr.ipv6Adr ) - rightParts) + i] := ipv6AdrRight[i]
END;
END;
IF prefixVal > 64 THEN RETURN NilAdr END;
retAdr.data := prefixVal; RETURN retAdr;
END;
RETURN NilAdr;
END StrToAdr;
PROCEDURE AdrToStr*( adr: Adr; VAR string: ARRAY OF CHAR );
VAR i, j, x: LONGINT;
a: ARRAY 4 OF CHAR;
val: LONGINT;
hexToStr: ARRAY 5 OF CHAR;
prefixLenStr: ARRAY 64 OF CHAR;
maxZeroRow: LONGINT; currentZeroRow: LONGINT;
maxZeroStart: LONGINT; currentZeroStart: LONGINT;
lastZero: BOOLEAN; lastDPoint: BOOLEAN; countEnded: BOOLEAN;
BEGIN
CASE adr.usedProtocol OF
| IPv4:
Network.Put4( a, 0, adr.ipv4Adr );
i := 0;
FOR j := 0 TO 3 DO
x := ORD( a[j] );
IF x >= 100 THEN string[i] := CHR( ORD( "0" ) + x DIV 100 ); INC( i ) END;
IF x >= 10 THEN string[i] := CHR( ORD( "0" ) + x DIV 10 MOD 10 ); INC( i ) END;
string[i] := CHR( ORD( "0" ) + x MOD 10 ); INC( i );
IF j = 3 THEN string[i] := 0X ELSE string[i] := "." END;
INC( i )
END
| IPv6:
FOR i := 0 TO LEN( adr.ipv6Adr ) - 1 BY 2 DO
val := ORD( adr.ipv6Adr[i] )*256;
val := val + ORD( adr.ipv6Adr[i + 1] );
Strings.IntToHexStr( val, 3, hexToStr );
WHILE (hexToStr[0] = "0") & (hexToStr[1] # 0X) DO Strings.Delete( hexToStr, 0, 1 ) END;
Strings.Append( string, hexToStr );
IF i # (LEN( adr.ipv6Adr ) - 2) THEN Strings.Append( string, ":" ) END;
END;
maxZeroRow := 0; currentZeroRow := 0;
maxZeroStart := 0; currentZeroStart := 0; i := 0;
lastZero := FALSE; lastDPoint := TRUE; countEnded := TRUE;
WHILE string[i] # 0X DO
IF string[i] = "0" THEN
IF lastDPoint THEN
INC( currentZeroRow ); lastZero := TRUE; lastDPoint := FALSE;
IF countEnded THEN currentZeroStart := i; countEnded := FALSE END;
END;
ELSIF string[i] = ":" THEN
lastDPoint := TRUE;
IF lastZero THEN lastZero := FALSE END;
ELSE
IF lastDPoint THEN
lastDPoint := FALSE; countEnded := TRUE;
IF currentZeroRow > maxZeroRow THEN
maxZeroRow := currentZeroRow; maxZeroStart := currentZeroStart;
END;
END;
END;
INC( i );
END;
IF ~countEnded THEN
IF currentZeroRow > maxZeroRow THEN
maxZeroRow := currentZeroRow; maxZeroStart := currentZeroStart;
END;
END;
IF maxZeroRow # 0 THEN
IF maxZeroStart = 0 THEN
string[0] := ":"; i := 1;
WHILE ((string[i] # 0X) & ~((string[i] # "0") & (string[i] # ":"))) DO INC( i ) END;
IF string[i] = 0X THEN COPY( "::", string ) ELSE Strings.Delete( string, 1, i - 2 ) END;
ELSE
i := maxZeroStart;
WHILE ((string[i] = "0") OR (string[i] = ":")) DO INC( i ) END;
IF string[i] = 0X THEN string[maxZeroStart] := ":"; string[maxZeroStart + 1] := 0X;
ELSE Strings.Delete( string, maxZeroStart, i - maxZeroStart - 1 );
END;
END;
END;
IF adr.data # 0 THEN
Strings.IntToStr( adr.data, prefixLenStr ); Strings.Append( string, "/" );
Strings.Append( string, prefixLenStr );
END;
ELSE
IF IsNilAdr( adr ) THEN string[0] := 0X END;
END;
END AdrToStr;
PROCEDURE ArrayToAdr*( CONST arr: ARRAY OF CHAR; ofs, protocol: LONGINT; LSBfirst: BOOLEAN ): Adr;
VAR adr: Adr; i, swapTemp: LONGINT;
BEGIN
ASSERT( (protocol = 4) OR (protocol = 6) );
IF protocol = IPv4 THEN
IF ~(ofs + 4 <= LEN( arr )) THEN RETURN NilAdr END;
S.MOVE( S.ADR( arr[ofs] ), S.ADR( adr.ipv4Adr ), 4 );
IF LSBfirst THEN SwapEndian( adr.ipv4Adr ) END;
adr.usedProtocol := IPv4;
ELSIF protocol = IPv6 THEN
IF ~(ofs + 16 <= LEN( arr )) THEN RETURN NilAdr END;
S.MOVE( S.ADR( arr[ofs] ), S.ADR( adr.ipv6Adr ), 16 );
IF LSBfirst THEN
FOR i := 0 TO 3 DO
S.MOVE( S.ADR( adr.ipv6Adr[i*4] ), S.ADR( swapTemp ), 4 );
SwapEndian( swapTemp );
S.MOVE( S.ADR( swapTemp ), S.ADR( adr.ipv6Adr[i*4] ), 4 );
END;
END;
adr.usedProtocol := IPv6;
ELSE
RETURN NilAdr;
END;
RETURN adr;
END ArrayToAdr;
PROCEDURE AdrToArray*( adr: Adr; VAR arr: ARRAY OF CHAR; ofs: LONGINT; LSBfirst: BOOLEAN );
VAR tempAdr: Adr; i, swapTemp: LONGINT;
BEGIN
tempAdr := adr;
CASE adr.usedProtocol OF
| IPv4:
IF ~(ofs + 4 <= LEN( arr )) THEN tempAdr := NilAdr END;
IF LSBfirst THEN SwapEndian( tempAdr.ipv4Adr ) END;
S.MOVE( S.ADR( tempAdr.ipv4Adr ), S.ADR( arr[ofs] ), 4 );
| IPv6:
IF ~(ofs + 16 <= LEN( arr )) THEN tempAdr := NilAdr END;
IF LSBfirst THEN
FOR i := 0 TO 3 DO
S.MOVE( S.ADR( tempAdr.ipv6Adr[i*4] ), S.ADR( swapTemp ), 4 );
SwapEndian( swapTemp );
S.MOVE( S.ADR( swapTemp ), S.ADR( tempAdr.ipv6Adr[i*4] ), 4 );
END;
END;
S.MOVE( S.ADR( adr.ipv6Adr ), S.ADR( arr[ofs] ), 16 );
ELSE
END;
END AdrToArray;
PROCEDURE IPConfig*( par: ANY ): ANY;
BEGIN
KernelLog.String( "Interfaces:" ); KernelLog.Ln; RETURN NIL;
END IPConfig;
PROCEDURE MatchPrefix*( adr: Adr; prefix: Adr ): BOOLEAN;
VAR
bytesToCheck: LONGINT; bitsToCheck: LONGINT; i: LONGINT; matches: BOOLEAN; diffSet: SET;
BEGIN
matches := TRUE;
bytesToCheck := prefix.data DIV 8; bitsToCheck := prefix.data MOD 8;
FOR i := 0 TO bytesToCheck - 1 DO
IF adr.ipv6Adr[i] # prefix.ipv6Adr[i] THEN matches := FALSE END;
END;
IF bitsToCheck # 0 THEN
diffSet := {};
FOR i := 0 TO 8 - bitsToCheck - 1 DO diffSet := diffSet + {i} END;
FOR i := 0 TO bitsToCheck - 1 DO
IF (S.VAL( SET, adr.ipv6Adr[bytesToCheck] ) - diffSet) #
(S.VAL( SET, prefix.ipv6Adr[bytesToCheck] ) - diffSet) THEN matches := FALSE
END
END
END;
RETURN matches
END MatchPrefix;
PROCEDURE IsValidIPv4Str( CONST ipString: ARRAY OF CHAR ): BOOLEAN;
VAR i, j: LONGINT; ipNr: LONGINT;
digits: ARRAY 4 OF CHAR;
startClass: LONGINT;
BEGIN
i := 0;
WHILE (i < Strings.Length( ipString )) & (ipString[i] # '.') & (i < 3) DO digits[i] := ipString[i]; INC( i ) END;
digits[i] := 0X;
IF ipString[i] # '.' THEN RETURN FALSE END;
j := 0;
WHILE digits[j] # 0X DO
IF (ORD( digits[j] ) - ORD( "0" )) > 9 THEN RETURN FALSE END;
INC( j );
END;
Strings.StrToInt( digits, ipNr );
IF ipNr > 255 THEN RETURN FALSE END;
INC( i ); startClass := i;
WHILE (i < Strings.Length( ipString )) & (ipString[i] # '.') & (i - startClass <= 3) DO
digits[i - startClass] := ipString[i]; INC( i );
END;
digits[i - startClass] := 0X;
IF ipString[i] # '.' THEN RETURN FALSE END;
j := 0;
WHILE digits[j] # 0X DO
IF (ORD( digits[j] ) - ORD( "0" )) > 9 THEN RETURN FALSE END;
INC( j );
END;
Strings.StrToInt( digits, ipNr );
IF ipNr > 255 THEN RETURN FALSE END;
INC( i ); startClass := i;
WHILE (i < Strings.Length( ipString )) & (ipString[i] # '.') & (i - startClass <= 3) DO
digits[i - startClass] := ipString[i]; INC( i );
END;
digits[i - startClass] := 0X;
IF ipString[i] # '.' THEN RETURN FALSE END;
j := 0;
WHILE digits[j] # 0X DO
IF (ORD( digits[j] ) - ORD( "0" )) > 9 THEN RETURN FALSE END;
INC( j );
END;
Strings.StrToInt( digits, ipNr );
IF ipNr > 255 THEN RETURN FALSE END;
INC( i ); startClass := i;
WHILE (i < Strings.Length( ipString )) & (i - startClass <= 3) DO digits[i - startClass] := ipString[i]; INC( i ) END;
digits[i - startClass] := 0X;
j := 0;
WHILE digits[j] # 0X DO
IF (ORD( digits[j] ) - ORD( "0" )) > 9 THEN RETURN FALSE END;
INC( j );
END;
Strings.StrToInt( digits, ipNr );
IF ipNr > 255 THEN RETURN FALSE END;
RETURN TRUE;
END IsValidIPv4Str;
PROCEDURE IsValidIPv6Str( ipString: ARRAY OF CHAR ): BOOLEAN;
VAR i: LONGINT;
state: LONGINT;
charCount: LONGINT;
ascD: LONGINT; ascH: LONGINT;
dPointOcc: BOOLEAN;
prefixLenArr: ARRAY 3 OF LONGINT;
prefixLen: LONGINT;
BEGIN
i := 0; state := 1; dPointOcc := FALSE;
Strings.UpperCase( ipString );
WHILE (i < (LEN( ipString ) - 1)) & (ipString[i] # 0X) DO
CASE state OF
-1: RETURN FALSE;
| 1:
ascD := ORD( ipString[i] ) - ORD( "0" );
ascH := ORD( ipString[i] ) - ORD( "A" );
IF ((ascD >= 0) & (ascD <= 9)) OR ((ascH >= 0) & (ascH <= 5)) THEN
INC( charCount );
IF charCount > 4 THEN state := -1 END;
ELSIF ipString[i] = ":" THEN charCount := 0; state := 2;
ELSIF ipString[i] = "/" THEN charCount := 0; state := 3;
ELSE state := -1;
END;
| 2: ascD := ORD( ipString[i] ) - ORD( "0" );
ascH := ORD( ipString[i] ) - ORD( "A" );
IF ipString[i] = ":" THEN
IF dPointOcc THEN state := -1 ELSE dPointOcc := TRUE; state := 4 END
ELSIF ((ascD >= 0) & (ascD <= 9)) OR ((ascH >= 0) & (ascH <= 5)) THEN INC( charCount ); state := 1;
ELSE state := -1;
END;
| 3: ascD := ORD( ipString[i] ) - ORD( "0" );
IF ~((ascD >= 0) & (ascD <= 9)) THEN state := -1;
ELSE
IF charCount > 3 THEN state := -1 ELSE prefixLenArr[charCount] := ascD; INC( charCount ) END;
END;
| 4: ascD := ORD( ipString[i] ) - ORD( "0" );
ascH := ORD( ipString[i] ) - ORD( "A" );
IF ipString[i] = "/" THEN state := 3;
ELSIF ((ascD >= 0) & (ascD <= 9)) OR ((ascH >= 0) & (ascH <= 5)) THEN INC( charCount ); state := 1;
ELSE state := -1;
END;
ELSE
END;
INC( i );
END;
CASE state OF
| 1: RETURN TRUE;
| 3: IF charCount > 0 THEN prefixLen := 0;
FOR i := 0 TO charCount - 1 DO prefixLen := prefixLen*10; INC( prefixLen, prefixLenArr[i] ) END;
IF prefixLen <= 64 THEN RETURN TRUE ELSE RETURN FALSE END;
ELSE RETURN FALSE;
END;
| 4: RETURN TRUE;
ELSE
RETURN FALSE;
END;
RETURN FALSE;
END IsValidIPv6Str;
PROCEDURE SetIPv6AdrNil( adr: Adr );
VAR i: LONGINT;
BEGIN
FOR i := 0 TO 15 DO adr.ipv6Adr[i] := 0X END;
END SetIPv6AdrNil;
PROCEDURE -SwapEndian( VAR adr: LONGINT );
CODE {SYSTEM.i386}
POP EAX
MOV ECX, [EAX]
XCHG CL, CH
ROL ECX, 16
XCHG CL, CH
MOV [EAX], ECX
END SwapEndian;
BEGIN
NilAdr.ipv4Adr := NilAdrIPv4;
SetIPv6AdrNil( NilAdr );
NilAdr.usedProtocol := NilAdrIdent;
END IP.