From 2e969e5e75fb8f544ff468584fb4e33d891e2954 Mon Sep 17 00:00:00 2001 From: beware Date: Mon, 6 Oct 2025 22:42:03 +0000 Subject: [PATCH] FreeBSD support --- binipstuff.pas | 16 +++++++++++ lcorelocalips.pas | 73 ++++++++++++++++++++++++++++++++++++++++------- lcorernd.pas | 12 ++++++++ lcoreselect.pas | 40 ++++++++++++++++++++++++++ 4 files changed, 131 insertions(+), 10 deletions(-) diff --git a/binipstuff.pas b/binipstuff.pas index 1d7a7c2..9f84721 100644 --- a/binipstuff.pas +++ b/binipstuff.pas @@ -88,7 +88,12 @@ type {$endif} TLInetSockAddr4 = packed Record + {$ifdef bsd} + len:byte; + family:byte; + {$else} family:Word; + {$endif} port :Word; addr :uint32; pad :array [0..7] of byte; //zipplet 20170204 - originally this was 1..8 for some reason @@ -96,7 +101,12 @@ type {$ifdef ipv6} TLInetSockAddr6 = packed record + {$ifdef bsd} + sin6_len:byte; + sin6_family:byte; + {$else} sin6_family: word; + {$endif} sin6_port: word; sin6_flowinfo: uint32; sin6_addr: tin6_addr; @@ -206,6 +216,9 @@ begin fillchar(inaddr,sizeof(inaddr),0); //writeln('converted address '+addr+' to binip '+ipbintostr(biniptemp)); if addr.family = AF_INET then begin + {$ifdef bsd} + inAddr.InAddr.Len := sizeof(tlinetsockaddr4); + {$endif} inAddr.InAddr.family:=AF_INET; inAddr.InAddr.port:=htons(strtointdef(port,0)); inAddr.InAddr.addr:=addr.ip; @@ -213,6 +226,9 @@ begin end else {$ifdef ipv6} if addr.family = AF_INET6 then begin + {$ifdef bsd} + inAddr.InAddr6.sin6_len := sizeof(tlinetsockaddr6); + {$endif} inAddr.InAddr6.sin6_family:=AF_INET6; inAddr.InAddr6.sin6_port:=htons(strtointdef(port,0)); inAddr.InAddr6.sin6_addr:=addr.ip6; diff --git a/lcorelocalips.pas b/lcorelocalips.pas index 07752ca..f1e87c1 100644 --- a/lcorelocalips.pas +++ b/lcorelocalips.pas @@ -71,15 +71,13 @@ implementation uses baseunix,unix,sockets,sysutils; +{$ifdef linux} function getlocalips_internal(wantfamily:integer):tbiniplist; const IF_NAMESIZE=16; + SIOCGIFCONF=$8912; - {$ifdef linux}SIOCGIFCONF=$8912;{$endif} - {$ifdef bsd}{$ifdef cpu386}SIOCGIFCONF=$C0086924;{$endif}{$endif} - - {amd64: mac OS X: $C00C6924; freeBSD: $c0106924} type tifconf=packed record ifc_len:taddrint; @@ -125,11 +123,11 @@ begin if (fpioctl(s,SIOCGIFCONF,@ifc) < 0) then begin raise exception.create('getv4localips ioctl failed'); end; - if (lastlen = ifc.ifc_len) then break; + if (lastlen = ifc.ifc_len) then break; lastlen := ifc.ifc_len; len := len * 2; until false; - + ifr2 := ifr; ifrmax := pointer(taddrint(ifr) + ifc.ifc_len); while (ifr2 < ifrmax) do begin @@ -138,10 +136,6 @@ begin {calculate len} ad := @ifr2.ifru_addr; - {$ifdef bsd} - len := ad.inaddr.len + IF_NAMESIZE; - if (len < sizeof(tifrec)) then - {$endif} len := sizeof(tifrec); if (len < sizeof(tifrec)) then break; {not enough left} @@ -155,14 +149,72 @@ begin FileClose(s); end; +{$endif} //linux + +{$ifdef bsd} + +type + pifaddrs = ^Tifaddrs; + Tifaddrs = record + ifa_next: pifaddrs; + ifa_name: pansichar; + ifa_flags: cuint; // Interface flags (IFF_UP, IFF_BROADCAST, etc.) + ifa_addr: Pinetsockaddrv; + ifa_netmask: psockaddr; + ifa_dstaddr: psockaddr; // union: Destination address (P-t-P) or broadcast address + ifa_data: Pointer; + end; + +const + IFF_UP=1; //interface is administratively enabled + +function getifaddrs(var ifap: pifaddrs): cint; cdecl; external 'c' name 'getifaddrs'; +function freeifaddrs(ifap: pifaddrs): cint; cdecl; external 'c' name 'freeifaddrs'; + + +function getlocalips_internal(wantfamily:integer):tbiniplist; +var + IfList: pifaddrs; + IfPtr: pifaddrs; + sa: PinetSockAddrV; +begin + result := biniplist_new; + + if getifaddrs(IfList) <> 0 then raise exception.create('getlocalips getifaddrs failed'); + + IfPtr := IfList; + while IfPtr <> nil do begin + if ((IfPtr^.ifa_flags and IFF_UP) <> 0) then begin + sa := IfPtr^.ifa_addr; + //if (sa <> nil) then writeln(sa^.inaddr.len,' ',sa^.inaddr.family); + + if (sa <> nil) and (sa^.inaddr.family = wantfamily) then begin + biniplist_add(result, inaddrvtobinip(sa^)); + end; + end; + IfPtr := IfPtr^.ifa_next; + end; + + freeifaddrs(IfList); +end; + +{$endif} //bsd + + {$ifdef ipv6} function getv6localips:tbiniplist; +{$ifndef bsd} var t:textfile; s,s2:ansistring; ip:tbinip; a:integer; +{$endif} begin + {$ifdef bsd} + result := getlocalips_internal(AF_INET6); + {$else} + //linux result := biniplist_new; assignfile(t,'/proc/net/if_inet6'); @@ -183,6 +235,7 @@ begin if ip.family <> 0 then biniplist_add(result,ip); end; closefile(t); + {$endif} end; {$endif} //ipv6 diff --git a/lcorernd.pas b/lcorernd.pas index b7a3bf2..aa3d253 100644 --- a/lcorernd.pas +++ b/lcorernd.pas @@ -285,6 +285,13 @@ begin end; +{$ifdef bsd} +function arc4random: cardinal; cdecl; external 'c' name 'arc4random'; +procedure arc4random_buf(buf: Pointer; nbytes: SizeUInt); cdecl; external 'c' name 'arc4random_buf'; +function arc4random_uniform(upper_bound: cardinal): cardinal; cdecl; external 'c' name 'arc4random_uniform'; +{$endif} + + {$ifdef linux} {$ifdef i386} const sys_getrandom = 355; @@ -317,6 +324,11 @@ begin a := do_syscall(sys_getrandom,tsysparam(@l.devrnd),sizeof(l.devrnd),0); {$endif} + {$ifdef bsd} + a := sizeof(l.devrnd); + arc4random_buf(@l.devrnd, a); + {$endif} + if (a < sizeof(l.devrnd)) then begin {if syscall misses or fails, fall back to /dev/urandom} assignfile(f,'/dev/urandom'); diff --git a/lcoreselect.pas b/lcoreselect.pas index ad81600..d3685ae 100644 --- a/lcoreselect.pas +++ b/lcoreselect.pas @@ -220,10 +220,22 @@ begin end; +{ +select in linux/sysV subtracts from timeout for time spent in it, but in BSD it doesn't +enabling select_no_autotv here makes doSelect mimic the decrement behavior, in case the caller needs it +the caller here in lcoreselect does not need it, and enabling it would have a slight perf hit. +it is safe for this to be enabled even if the OS does it too (it will not subtract twice) +it is currently disabled but can be enabled if needed +} +{$ifndef linux}{-$define select_no_autotv}{$endif} + Function doSelect(timeOut:PTimeVal):longint;//inline; var localtimeval : ttimeval; maxslocal : integer; + {$ifdef select_no_autotv} + timeoutcopy,tvstart,tvend : ttimeval; + {$endif} begin //unblock signals //zeromemory(@sset,sizeof(sset)); @@ -243,6 +255,15 @@ begin {$ifndef nosignal} sigprocmask(SIG_UNBLOCK,@blockset,nil); {$endif} + + {$ifdef select_no_autotv} + if assigned(timeout) then begin + timeoutcopy.tv_sec := timeOut.tv_sec; + timeoutcopy.tv_usec := timeOut.tv_usec; + gettimemonotonic(tvstart); + end; + {$endif} + result := select(maxslocal+1,@FDSR,@FDSW,nil,timeout); if result <= 0 then begin fd_zero(FDSR); @@ -253,8 +274,27 @@ begin end else begin raise esocketexception.create('select returned error '+inttostr(linuxerror)); end; + end + {$ifdef select_no_autotv} + else if (result = 0) and assigned(timeout) then begin + //timeout reached: zero the timeval + timeout.tv_sec := 0; + timeout.tv_usec := 0; end; + end else if assigned(timeout) then begin + //successful result: subtract elapsed time + gettimemonotonic(tvend); + tv_subtract(tvend,tvstart); + tv_subtract(timeoutcopy,tvend); + timeout.tv_sec := timeoutcopy.tv_sec; + timeout.tv_usec := timeoutcopy.tv_usec; + if (timeout.tv_sec < 0) then begin + timeout.tv_sec := 0; + timeout.tv_usec := 0; + end; + {$endif} //select_no_autotv end; + {$ifndef nosignal} sigprocmask(SIG_BLOCK,@blockset,nil); {$endif} -- 2.30.2