MODULE IP;
IMPORT SYSTEM, WSock32, Network, Strings, KernelLog;
CONST
Ok* = 0;
NilAdrIPv4 = 0;
IPv4* = 4;
IPv6* = 6;
NilAdrIdent = -1;
TYPE
Adr* = RECORD
ipv4Adr*: LONGINT;
ipv6Adr*: ARRAY 16 OF CHAR;
usedProtocol*: LONGINT;
data*: LONGINT;
END;
Interface* = OBJECT
VAR
localAdr-: Adr;
END Interface;
VAR
default-: Interface;
NilAdr*: Adr;
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*(CONST s: ARRAY OF CHAR): Adr;
VAR
i, j, x: LONGINT;
adr: ARRAY 4 OF CHAR;
ok: BOOLEAN;
ip: Adr;
BEGIN
i := 0; j := 0; x := -1; ok := FALSE;
LOOP
IF (s[i] = ".") OR (s[i] = 0X) THEN
IF (x < 0) OR (x > 255) OR (j = 4) THEN EXIT END;
adr[j] := CHR(x);
IF s[i] = 0X THEN ok := (j = 3); EXIT END;
x := -1; INC(i); INC(j)
ELSIF (s[i] >= "0") & (s[i] <= "9") THEN
IF x = -1 THEN x := 0 END;
x := x*10 + (ORD(s[i])-ORD("0"));
INC(i)
ELSE
EXIT
END
END;
IF ok THEN
ip.ipv4Adr := SYSTEM.VAL(LONGINT,adr);
ip.usedProtocol := IPv4;
RETURN ip;
ELSE
RETURN NilAdr;
END
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:
ASSERT(LEN(string) >= 16);
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
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 := "";
END;
END;
END AdrToStr;
PROCEDURE ArrayToAdr*(CONST array: 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(array)) THEN
RETURN NilAdr;
END;
SYSTEM.MOVE(SYSTEM.ADR(array[ofs]), SYSTEM.ADR(adr.ipv4Adr), 4);
IF LSBfirst THEN
SwapEndian(adr.ipv4Adr);
END;
adr.usedProtocol := IPv4;
ELSIF protocol = IPv6 THEN
IF ~(ofs + 16 <= LEN(array)) THEN
RETURN NilAdr;
END;
SYSTEM.MOVE(SYSTEM.ADR(array[ofs]), SYSTEM.ADR(adr.ipv6Adr), 16);
IF LSBfirst THEN
FOR i := 0 TO 3 DO
SYSTEM.MOVE(SYSTEM.ADR(adr.ipv6Adr[i*4]), SYSTEM.ADR(swapTemp), 4);
SwapEndian(swapTemp);
SYSTEM.MOVE(SYSTEM.ADR(swapTemp), SYSTEM.ADR(adr.ipv6Adr[i*4]), 4);
END;
END;
adr.usedProtocol := IPv6;
ELSE
RETURN NilAdr;
END;
RETURN adr;
END ArrayToAdr;
PROCEDURE AdrToArray*(adr: Adr; CONST array: 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(array)) THEN
tempAdr := NilAdr;
END;
IF LSBfirst THEN
SwapEndian(tempAdr.ipv4Adr);
END;
SYSTEM.MOVE(SYSTEM.ADR(tempAdr.ipv4Adr), SYSTEM.ADR(array[ofs]), 4);
| IPv6:
IF ~(ofs + 16 <= LEN(array)) THEN
tempAdr := NilAdr;
END;
IF LSBfirst THEN
FOR i := 0 TO 3 DO
SYSTEM.MOVE(SYSTEM.ADR(tempAdr.ipv6Adr[i*4]), SYSTEM.ADR(swapTemp), 4);
SwapEndian(swapTemp);
SYSTEM.MOVE(SYSTEM.ADR(swapTemp), SYSTEM.ADR(tempAdr.ipv6Adr[i*4]), 4);
END;
END;
SYSTEM.MOVE(SYSTEM.ADR(adr.ipv6Adr), SYSTEM.ADR(array[ofs]), 16);
ELSE
END;
END AdrToArray;
PROCEDURE InterfaceByDstIP*(dst: Adr): Interface;
BEGIN
RETURN default
END InterfaceByDstIP;
PROCEDURE Init;
VAR name: ARRAY 256 OF CHAR; res: LONGINT; hint: WSock32.addrinfo; ai: WSock32.Paddrinfo;
BEGIN
KernelLog.String("IP.Init. Hostname ");
res := WSock32.gethostname(name, 256);
IF res = 0 THEN
KernelLog.String("="); KernelLog.String(name)
ELSE
KernelLog.String("failed "); KernelLog.Int(res, 0);KernelLog.Ln;
RETURN
END;
KernelLog.String(", IP ");
hint.aiFlags := {};
hint.aiFamily := WSock32.AFINet;
hint.aiSocktype := WSock32.SockStream;
hint.aiProtocol := WSock32.IPProtoTCP;
hint.aiCanonname := 0;
hint.aiAddr := NIL;
hint.aiNext := NIL;
NilAdr.usedProtocol := IPv4;
IF WSock32.getaddrinfo # NIL THEN
res := WSock32.getaddrinfo(name, NIL, hint, ai);
IF (res = 0) & (ai # NIL) THEN
KernelLog.String("=");
IF (ai.aiFamily = WSock32.AFINet) & (ai.aiAddrlen = SYSTEM.SIZEOF(WSock32.sockaddrIn)) & (ai.aiAddr.sinFamily = WSock32.AFINet) THEN
NEW(default);
default.localAdr.usedProtocol := IPv4;
default.localAdr.ipv4Adr := ai.aiAddr.sinAddr;
AdrToStr(default.localAdr, name); KernelLog.String(name)
ELSE
KernelLog.String(" failed.")
END;
WSock32.freeaddrinfo(ai)
ELSE
KernelLog.String("failed: "); KernelLog.Int(res, 0)
END;
KernelLog.Ln()
ELSE KernelLog.String("failed because getaddrinfo was not found!"); KernelLog.Ln
END
END Init;
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
default := NIL; Init()
END IP.