rustd-resolved 0.2.3

Native DNS resolver and name-service daemon for RustD
! routing_score.f90 — EWMA RTT + failure penalty + DNSSEC preference
! Build: gfortran -O3 -fPIC -c routing_score.f90
!        gfortran -shared -o librouting_score.so routing_score.o
! ISO_C_BINDING exports for Rust `src/routing.rs`.

module routing_score
  use, intrinsic :: iso_c_binding
  implicit none
  private
  public :: rs_score_servers, rs_update_sample, rs_pick_best
  public :: rs_init_table, rs_reset_server

  integer, parameter :: MAX_SERVERS = 64
  real(c_double), parameter :: EWMA_ALPHA = 0.25d0
  real(c_double), parameter :: FAIL_PENALTY = 50.0d0   ! ms equivalent
  real(c_double), parameter :: DNSSEC_BONUS = 5.0d0    ! ms equivalent
  real(c_double), parameter :: UNREACH_SCORE = 1.0d12

  type, bind(C) :: server_metrics
     real(c_double) :: ewma_rtt_ms
     real(c_double) :: ewma_fail          ! 0..1
     integer(c_int)  :: samples
     integer(c_int)  :: consecutive_fail
     integer(c_int)  :: dnssec_ok         ! 1/0
     integer(c_int)  :: reachable         ! 1/0
     integer(c_int)  :: family_pref       ! lower = better (v6/v4 policy)
     integer(c_int)  :: scope_pref        ! link/global ordering
  end type server_metrics

contains

  subroutine rs_init_table(n, table) bind(C, name="rs_init_table")
    integer(c_int), intent(in), value :: n
    type(server_metrics), intent(out) :: table(MAX_SERVERS)
    integer :: i, m
    m = min(int(n), MAX_SERVERS)
    do i = 1, MAX_SERVERS
       table(i)%ewma_rtt_ms = 100.0d0
       table(i)%ewma_fail = 0.0d0
       table(i)%samples = 0
       table(i)%consecutive_fail = 0
       table(i)%dnssec_ok = 0
       table(i)%reachable = 1
       table(i)%family_pref = 0
       table(i)%scope_pref = 0
    end do
    if (m < 0) return
  end subroutine rs_init_table

  subroutine rs_reset_server(idx0, table) bind(C, name="rs_reset_server")
    integer(c_int), intent(in), value :: idx0
    type(server_metrics), intent(inout) :: table(MAX_SERVERS)
    integer :: i
    i = int(idx0) + 1
    if (i < 1 .or. i > MAX_SERVERS) return
    table(i)%ewma_rtt_ms = 100.0d0
    table(i)%ewma_fail = 0.0d0
    table(i)%samples = 0
    table(i)%consecutive_fail = 0
    table(i)%reachable = 1
  end subroutine rs_reset_server

  ! success=1 rtt_ms used; success=0 counts as failure (rtt ignored)
  subroutine rs_update_sample(idx0, success, rtt_ms, table) &
       bind(C, name="rs_update_sample")
    integer(c_int), intent(in), value :: idx0, success
    real(c_double), intent(in), value :: rtt_ms
    type(server_metrics), intent(inout) :: table(MAX_SERVERS)
    integer :: i
    real(c_double) :: a, r, f
    i = int(idx0) + 1
    if (i < 1 .or. i > MAX_SERVERS) return
    a = EWMA_ALPHA
    if (success /= 0) then
       r = max(rtt_ms, 0.0d0)
       if (table(i)%samples == 0) then
          table(i)%ewma_rtt_ms = r
       else
          table(i)%ewma_rtt_ms = a * r + (1.0d0 - a) * table(i)%ewma_rtt_ms
       end if
       f = 0.0d0
       table(i)%consecutive_fail = 0
       table(i)%reachable = 1
    else
       f = 1.0d0
       table(i)%consecutive_fail = table(i)%consecutive_fail + 1
       if (table(i)%consecutive_fail >= 5) table(i)%reachable = 0
    end if
    if (table(i)%samples == 0) then
       table(i)%ewma_fail = f
    else
       table(i)%ewma_fail = a * f + (1.0d0 - a) * table(i)%ewma_fail
    end if
    table(i)%samples = table(i)%samples + 1
  end subroutine rs_update_sample

  pure real(c_double) function score_one(s) result(sc)
    type(server_metrics), intent(in) :: s
    real(c_double) :: pen, bonus, fam, sco
    if (s%reachable == 0) then
       sc = UNREACH_SCORE
       return
    end if
    pen = FAIL_PENALTY * s%ewma_fail * (1.0d0 + 0.25d0 * dble(s%consecutive_fail))
    bonus = 0.0d0
    if (s%dnssec_ok /= 0) bonus = DNSSEC_BONUS
    fam = 0.5d0 * dble(s%family_pref)
    sco = 0.25d0 * dble(s%scope_pref)
    sc = s%ewma_rtt_ms + pen - bonus + fam + sco
  end function score_one

  subroutine rs_score_servers(n, table, out_scores) bind(C, name="rs_score_servers")
    integer(c_int), intent(in), value :: n
    type(server_metrics), intent(in) :: table(MAX_SERVERS)
    real(c_double), intent(out) :: out_scores(MAX_SERVERS)
    integer :: i, m
    m = min(int(n), MAX_SERVERS)
    ! Vector-friendly tight loop — gfortran auto-SIMDs at -O3
    do i = 1, m
       out_scores(i) = score_one(table(i))
    end do
    do i = m + 1, MAX_SERVERS
       out_scores(i) = UNREACH_SCORE
    end do
  end subroutine rs_score_servers

  ! Returns 0-based index of best server, or -1 if none.
  function rs_pick_best(n, table) result(best) bind(C, name="rs_pick_best")
    integer(c_int), intent(in), value :: n
    type(server_metrics), intent(in) :: table(MAX_SERVERS)
    integer(c_int) :: best
    integer :: i, m
    real(c_double) :: sc, best_sc
    best = -1_c_int
    best_sc = UNREACH_SCORE
    m = min(int(n), MAX_SERVERS)
    do i = 1, m
       sc = score_one(table(i))
       if (sc < best_sc) then
          best_sc = sc
          best = int(i - 1, c_int)
       end if
    end do
  end function rs_pick_best

end module routing_score