Merge branch 'develop' into pkova/eauth-localhost

This commit is contained in:
Pyry Kovanen 2024-03-21 13:39:50 +02:00 committed by pkova
commit bf60b47407
7 changed files with 196 additions and 381 deletions

View File

@ -168,7 +168,7 @@
::
;~ pfix tis
;~ pose
(parse-variable (jest %dir) ;~(pfix ace :(stag 0 %ex parse-rood)))
(parse-variable (cold %dir (jest 'dir ')) :(stag 0 %ex parse-rood))
(parse-variable sym ;~(pfix ace parse-source))
==
==

View File

@ -20,23 +20,86 @@
::
++ nat-timeout ~s25
::
:: How often to check our IP when we know we're not behind a NAT.
::
++ ip-timeout ~m5
::
:: Chosen because it's run by Cloudflare, and others I tried were
:: inconsistently slow.
::
++ ip-reflector 'https://icanhazip.com'
::
+$ card card:agent:gall
+$ ship-state
::
+$ state-3
$: %3
mode=?(%formal %informal)
pokes=@ud
timer=(unit [=wire date=@da])
galaxy=@p
==
--
::
%- agent:dbug
::
=| state=state-3
=> |%
++ galaxy-for
|= [=ship =bowl:gall]
^- @p
=/ next (sein:title our.bowl now.bowl ship)
?: ?=(%czar (clan:title next))
next
$(ship next)
::
++ wait-card
|= [=wire now=@da]
^- card
[%pass wire %arvo %b %wait (add nat-timeout now)]
::
++ ping
|= [=ship force=?]
^- (quip card _state)
?: &(!force (gth pokes.state 0) =(ship galaxy.state))
[~ state]
:_ state(pokes +(pokes.state), galaxy ship)
[%pass /ping %agent [ship %ping] %poke %noun !>(~)]~
--
%+ verb |
^- agent:gall
|_ =bowl:gall
+* this .
def ~(. (default-agent this %|) bowl)
::
:: +on-init: initializing on startup
::
++ on-init
^- [(list card) _this]
=. mode.state %formal
=. pokes.state 0
=. galaxy.state (galaxy-for our.bowl bowl)
[~ this]
::
++ on-load
|= old-vase=vase
|^
=/ old !<(state-any old-vase)
=? old ?=(%0 -.old) (state-0-to-1 old)
=? old ?=(%1 -.old) (state-1-to-2 old)
=? old ?=(%2 -.old) (state-2-to-3 old)
?> ?=(%3 -.old)
=. state old
[~ this]
::
+$ ship-state
$% [%idle ~]
[%poking ~]
[%http until=@da]
[%waiting until=@da]
==
+$ state-2
+$ state-any $%(state-0 state-1 state-2 state-3)
+$ state-0 [%0 ships=(map ship [=rift =ship-state])]
+$ state-1
$: %1
ships=(set ship)
nonce=@ud
$= plan
$~ [%nat ~]
$% [%nat ~]
[%pub ip=(unit @t)]
== ==
+$ state-2
$: %2
ships=(set ship)
nonce=@ud
@ -48,293 +111,6 @@
[%one ~]
==
==
--
::
%- agent:dbug
::
=| state=state-2
=> |%
:: Bind for the the writer monad on (quip effect state)
::
++ rind
|* [effect=mold state=*]
|* state-type=mold
|= $: m-b=(quip effect state-type)
fun=$-(state-type (quip effect state-type))
==
^- (quip effect state-type)
=^ effects-1=(list effect) state m-b
=^ effects-2=(list effect) state (fun state)
[(weld effects-1 effects-2) state]
::
++ once
|= =cord
=(cord (scot %uw nonce.state))
::
:: Subsystem to keep track of which ships to ping across breaches
:: and sponsorship changes
::
++ ships
=| force=_|
|%
++ rind (^rind card state)
++ kick
|= [our=@p now=@da]
^- (quip card _state)
:: ?: =(%czar (clan:title our))
:: `state
::
:: NB: !! This includes our own ship, and for moons, this is
:: what has caused Jael to fetch our own rift from our parent.
:: This role may be taken by Ames's subscription to
:: %public-keys, but this must be tested before changing the
:: behavior here.
::
=/ new-ships (~(gas in *(set ship)) (saxo:title our now our))
=/ removed (~(dif in ships.state) new-ships)
=/ added (~(dif in new-ships) ships.state)
;< new-state=_state rind
?~ removed `state
[[%pass /jael %arvo %j %nuke removed]~ state]
=. state new-state
::
;< new-state=_state rind
?~ added `state
[[%pass /jael %arvo %j %public-keys added]~ state]
=. state new-state
::
:: Kick even if ships weren't added or removed
::
(kick-pings our now new-ships force)
::
:: Kick whenever we get a response. We really care about
:: breaches and sponsorship changes.
::
:: Delay until next event in case of breach, so that ames can
:: clear its state.
::
++ take-jael
|= now=@da
^- (quip card _state)
[[%pass /jael/delay %arvo %b %wait now]~ state]
::
++ take-delay %*(kick ships force %.y)
--
::
:: Starts pinging a new set of `ships`.
::
++ kick-pings
|= [our=@p now=@da ships=(set ship) force=?]
^- (quip card _state)
=: nonce.state +(nonce.state)
ships.state ships
==
::
?: force (kick:nat our)
?- -.plan.state
%off `state
%nat (kick:nat our)
%one (kick:one our)
%pub (kick:pub our now)
==
::
:: Subsystem for pinging our sponsors when we might be behind a NAT
:: XX no longer true if using STUN-enabled vere 2.XX
:: Ping each ship every 25 seconds to keep the pinhole open.
:: This is expensive, but if you don't do it and you are behind a
:: NAT, you will stop receiving packets from other ships except
:: during the 30 seconds following each packet you send.
::
++ nat
?> ?=(%nat -.plan.state)
|%
++ rind (^rind card state)
++ kick
|= our=@p
^- (quip card _state)
=/ ships ~(tap in ships.state)
|- ^- (quip card _state)
?~ ships `state
?: =(our i.ships) $(ships t.ships)
;< new-state=_state rind (send-ping i.ships)
=. state new-state
$(ships t.ships)
::
++ send-ping
|= =ship
^- (quip card _state)
:_ state
=/ wire /nat/(scot %uw nonce.state)/ping/(scot %p ship)
[%pass wire %agent [ship %ping] %poke %noun !>(~)]~
::
++ take-ping
|= [now=@da =wire error=(unit tang)]
^- (quip card _state)
?. ?=([%nat @ %ping @ ~] wire) `state
?. (once i.t.wire) `state
=/ ship (slav %p i.t.t.t.wire)
%- (slog ?~(error ~ ['ping: got nack' >ship< u.error]))
:_ state
=/ wire /nat/(scot %uw nonce.state)/wait/(scot %p ship)
[%pass wire %arvo %b %wait (add nat-timeout now)]~
::
++ take-wait
|= =wire
^- (quip card _state)
?. ?=([%nat @ %wait @ ~] wire) `state
?. (once i.t.wire) `state
=/ ship (slav %p i.t.t.t.wire)
(send-ping ship)
--
::
:: Subsystem for pinging our sponsors when we know we're not behind a NAT
::
:: Check our IP address every minute, and only if it changes,
:: ping all our sponsors.
::
++ pub
?> ?=(%pub -.plan.state)
|%
++ rind (^rind card state)
++ kick
|= [our=@p now=@da]
^- (quip card _state)
;< new-state=_state rind (send-pings our)
=. state new-state
::
;< new-state=_state rind check-ip
=. state new-state
::
(set-timer now)
::
++ send-pings
|= our=@p
^- (quip card _state)
:_ state
%+ murn ~(tap in ships.state)
|= =ship
?: =(our ship)
~
=/ wire /pub/(scot %uw nonce.state)/ping/(scot %p ship)
`u=[%pass wire %agent [ship %ping] %poke %noun !>(~)]
::
++ take-pings
|= [=wire error=(unit tang)]
^- (quip card _state)
?. ?=([%pub @ %ping @ ~] wire) `state
?. (once i.t.wire) `state
=/ ship (slav %p i.t.t.t.wire)
%- (slog ?~(error ~ ['ping: got nack' >ship< u.error]))
`state
::
++ check-ip
^- (quip card _state)
:_ state
=/ wire /pub/(scot %uw nonce.state)/ip
=/ =request:http [%'GET' ip-reflector ~ ~]
[%pass wire %arvo %i %request request *outbound-config:iris]~
::
++ take-ip
|= [our=@p =wire resp=client-response:iris]
^- (quip card _state)
?. ?=([%pub @ %ip ~] wire) `state
?. (once i.t.wire) `state
::
?. ?=(%finished -.resp) `state :: will retry in a minute
?. ?=(%200 status-code.response-header.resp)
=* s status-code.response-header.resp
%- (slog leaf+"ping: ip check failed: {<s>}" ~)
`state
::
?~ full-file.resp
%- (slog 'ping: ip check body empty' ~)
`state
::
=* body q.data.u.full-file.resp
?~ body
%- (slog 'ping: ip check body empty' ~)
`state
::
=/ ip (end [3 (dec (met 3 body))] body)
?: =(ip.plan.state `ip) `state
::
=. ip.plan.state `ip
(send-pings our)
::
++ set-timer
|= now=@da
^- (quip card _state)
=/ =wire /pub/(scot %uw nonce.state)/wait
[[%pass wire %arvo %b %wait (add ip-timeout now)]~ state]
::
++ take-wait
|= [our=@p now=@da =wire]
^- (quip card _state)
?. ?=([%pub @ %wait ~] wire) `state
?. (once i.t.wire) `state
;< new-state=_state rind check-ip
=. state new-state
::
(set-timer now)
--
:: Subsystem for formally acknowledging a change in our IP:PORT
::
:: If our sponsor sends a STUN response, with an IP different than what
:: we had previously cached, we formally acknowledge this change by
:: sending one %poke to every ship in the sponsorship chain.
::
++ one
?> ?=(%one -.plan.state)
|%
++ kick
|= our=@p
^- (quip card _state)
:_ state
%- ~(rep in ships.state)
|= [=ship cards=(list card)]
?: =(our ship) cards
=/ wire /one/(scot %uw nonce.state)/ping/(scot %p ship)
:_ cards ^- card
[%pass wire %agent [ship %ping] %poke %noun !>(~)]
--
--
%+ verb |
^- agent:gall
|_ =bowl:gall
+* this .
def ~(. (default-agent this %|) bowl)
::
:: +on-init: initializing on startup
::
++ on-init
^- [(list card) _this]
=. plan.state [%nat ~]
=^ cards state (kick:ships our.bowl now.bowl)
[cards this]
::
++ on-save !>(state)
++ on-load
|= old-vase=vase
|^
=/ old !<(state-any old-vase)
=? old ?=(%0 -.old) (state-0-to-1 old)
=? old ?=(%1 -.old) (state-1-to-2 old)
?> ?=(%2 -.old)
=. state old
=^ cards state (kick:ships our.bowl now.bowl)
[cards this]
::
+$ state-any $%(state-0 state-1 state-2)
+$ state-0 [%0 ships=(map ship [=rift =ship-state])]
+$ state-1
$: %1
ships=(set ship)
nonce=@ud
$= plan
$~ [%nat ~]
$% [%nat ~]
[%pub ip=(unit @t)]
== ==
::
++ state-0-to-1
|= old=state-0
@ -345,6 +121,18 @@
|= old=state-1
^- state-2
old(- %2)
::
++ state-2-to-3
|= old=state-2
^- state-3
:* %3 %formal 0 ~
=/ galaxy=(list @p)
%+ skim ~(tap in ships.old)
|=(p=@p ?=(%czar (clan:title p)))
?: =(1 (lent galaxy))
-.galaxy
(head (flop (^saxo:title our.bowl)))
==
--
:: +on-poke: positively acknowledge pokes
::
@ -355,48 +143,16 @@
::
=^ cards state
?: ?=([%kick ?] q.vase)
:: NB: ames calls this on %born (with fail=%.n) and after not hearing STUN
:: responses for more than ~s5 (with fail=%.y)
=? mode.state =(+.q.vase %.y)
%formal
(ping (galaxy-for our.bowl bowl) %.n)
::
:: if %ping was turned off (due to a successfull STUN) but we failed
:: to get a STUN response in time switch to %nat and start a ~s25 timer
::
:: if the %kick has fail=%.n (e.g. for every %born), the plan will remain
:: unchanged, but we will innitiate a new round of %poke pings with
:: increasing nonce.
::
:: if we get repeated [%stun fail=&], but we are already in either %nat
:: or %pub, do nothing, since there are already timers in place to %ping
:: repeatedly.
::
=/ stun-failed=? &(?=([%off ~] plan.state) =(+.q.vase %.y))
?: &(?=([%off ~] plan.state) =(+.q.vase %.n))
:: ignore restarts if we were already STUNning, if ip:port changed
:: %once will trigger one formal %ping
::
`state
=? plan.state stun-failed
[%nat ~]
?: &(!stun-failed =(+.q.vase %.y))
`state
(kick:ships our.bowl now.bowl)
?: =(q.vase %stop) :: NB: ames calls this on [%stun fail=%.n]
=. plan.state [%off ~]
(kick:ships our.bowl now.bowl)
?: &(=(q.vase %once) =(%off -.plan.state)) :: NB: ames calls this on %once
=. plan.state [%one ~]
(kick:ships our.bowl now.bowl)
?: =(q.vase %nat)
=. plan.state [%nat ~]
(kick:ships our.bowl now.bowl)
?: =(q.vase %no-nat)
=. plan.state [%pub ~]
(kick:ships our.bowl now.bowl)
?: |(=(q.vase %once) =(q.vase %stop)) :: NB: ames calls this on %once
=. mode.state %informal
(ping (galaxy-for our.bowl bowl) %.y)
`state
[cards this]
::
++ on-watch on-watch:def
++ on-leave on-leave:def
++ on-peek
|= =path
^- (unit (unit cage))
@ -406,25 +162,18 @@
++ on-agent
|= [=wire =sign:agent:gall]
^- [(list card) _this]
=^ cards state
?+ wire `state
[%nat *]
?. ?=(%nat -.plan.state) `state
?. ?=(%poke-ack -.sign) `state
(take-ping:nat now.bowl wire p.sign)
::
[%pub *]
?. ?=(%pub -.plan.state) `state
?. ?=(%poke-ack -.sign) `state
(take-pings:pub wire p.sign)
::
[%one *]
?. ?=(%one -.plan.state) `state
?: ?=(%poke-ack -.sign) `state
:: XX handle error?
`state
==
[cards this]
?. ?=([%ping *] wire)
`this
?. ?=(%poke-ack -.sign)
`this
=. pokes.state (dec pokes.state)
?. =(pokes.state 0)
`this
?. |(?=(%formal mode.state) ?=(^ p.sign))
`this
=/ wir /wait
=. timer.state `[wir now.bowl]
[[(wait-card wir now.bowl)]~ this]
:: +on-arvo: handle timer firing
::
++ on-arvo
@ -432,36 +181,20 @@
^- [(list card) _this]
=^ cards state
?+ wire `state
[%jael %delay ~]
[%wait *]
?. ?=(%formal mode.state) `state
?> ?=(%wake +<.sign-arvo)
?^ error.sign-arvo
%- (slog 'ping: strange jael wake fail!' u.error.sign-arvo)
%- (slog 'ping: strange wake fail!' u.error.sign-arvo)
`state
(take-delay:ships our.bowl now.bowl)
=. timer.state ~
(ping (galaxy-for our.bowl bowl) %.n)
::
[%jael ~]
?> ?=(%public-keys +<.sign-arvo)
(take-jael:ships now.bowl)
::
[%nat *]
?. ?=(%nat -.plan.state) `state
?> ?=(%wake +<.sign-arvo)
?^ error.sign-arvo
%- (slog 'ping: strange nat wake fail!' u.error.sign-arvo)
`state
(take-wait:nat wire)
::
[%pub @ %ip *]
?. ?=(%pub -.plan.state) `state
?> ?=(%http-response +<.sign-arvo)
(take-ip:pub our.bowl wire client-response.sign-arvo)
::
[%pub @ %wait *]
?. ?=(%pub -.plan.state) `state
?> ?=(%wake +<.sign-arvo)
(take-wait:pub our.bowl now.bowl wire)
==
[cards this]
::
++ on-save !>(state)
++ on-fail on-fail:def
++ on-watch on-watch:def
++ on-leave on-leave:def
--

View File

@ -490,9 +490,9 @@
^- [(list card) _state]
%+ roll cards.r
|= [=card cards=(list card) s=_state]
:_ =? scrying.s ?=([%pass ^ %arvo %a %keen @ *] card)
:: wire ship path
scrying.s :: (~(put ju scrying.s) tid [&2 +>+>+>]:card)
:_ =? scrying.s ?=([%pass ^ %arvo %a %keen ?(~ ^) @ *] card)
:: &2=wire &7=ship 7|=path
(~(put ju scrying.s) tid [&2 &7 |7]:card)
s
:_ cards
^- ^card

View File

@ -894,7 +894,7 @@
+$ address @uxaddress
:: $verb: verbosity flag for ames
::
+$ verb ?(%snd %rcv %odd %msg %ges %for %rot %kay %fin)
+$ verb ?(%snd %rcv %odd %msg %ges %for %rot %kay %fin %sun)
:: $blob: raw atom to or from unix, representing a packet
::
+$ blob @uxblob

View File

@ -101,6 +101,7 @@
rot=`?`%.n :: routing attempts
kay=`?`%.n :: is ok/not responding
fin=`?`%.n :: remote-scry
sun=`?`%.n :: STUN
==
=/ packet-size 13
=>
@ -619,7 +620,7 @@
:: dead: dead flow consolidation timer and recork timer, if set
::
+$ ames-state
$+ ames-state-19
$+ ames-state-20
$: peers=(map ship ship-state)
=unix=duct
=life
@ -850,7 +851,7 @@
=life
=rift
crypto-core=acru:ames
=bug
bug=bug-19
snub=[form=?(%allow %deny) ships=(set ship)]
cong=[msg=@ud mem=@ud]
==
@ -1150,7 +1151,7 @@
=life
=rift
crypto-core=acru:ames
=bug
bug=bug-19
snub=[form=?(%allow %deny) ships=(set ship)]
cong=[msg=_5 mem=_100.000]
::
@ -1204,7 +1205,7 @@
=life
=rift
crypto-core=acru:ames
=bug
bug=bug-19
snub=[form=?(%allow %deny) ships=(set ship)]
cong=[msg=@ud mem=@ud]
==
@ -1327,6 +1328,29 @@
deep-task-14 :: introduced in state %14, modified in %19
$<(?(%keen %deep) task)
==
::
+$ bug-19
$: veb=_[`?`%.n `?`%.n `?`%.n `?`%.n `?`%.n `?`%.n `?`%.n `?`%.n `?`%.n]
ships=(set ship)
==
::
+$ ames-state-19
$+ ames-state-19
$: peers=(map ship ship-state)
=unix=duct
=life
=rift
crypto-core=acru:ames
bug=bug-19
snub=[form=?(%allow %deny) ships=(set ship)]
cong=[msg=@ud mem=@ud]
$= dead
$: flow=[%flow (unit dead-timer)]
cork=[%cork (unit dead-timer)]
==
::
=chain
==
:: $bug: debug printing configuration
::
:: veb: verbosity toggles
@ -1479,7 +1503,8 @@
[%16 ames-state-16]
[%17 ames-state-17]
[%18 ames-state-17]
[%19 ^ames-state]
[%19 ames-state-19]
[%20 ^ames-state]
==
::
|= [now=@da eny=@ rof=roof]
@ -1602,7 +1627,7 @@
:: lifecycle arms; mostly pass-throughs to the contained adult ames
::
++ scry scry:adult-core
++ stay [%19 %larva queued-events ames-state.adult-gate]
++ stay [%20 %larva queued-events ames-state.adult-gate]
++ load
|= $= old
$% $: %4
@ -1711,6 +1736,13 @@
[%adult state=ames-state-18]
== ==
$: %19 :: %keen & %deep modified
$% $: %larva
events=(qeu queued-event)
state=ames-state-19
==
[%adult state=ames-state-19]
== ==
$: %20 :: start informal %ping
$% $: %larva
events=(qeu queued-event)
state=_ames-state.adult-gate
@ -1899,12 +1931,23 @@
=. queued-events (event-17-and-18-to-last events.old)
larval-gate
::
[%19 %adult *] (load:adult-core %19 state.old)
[%19 %adult *]
=. cached-state `[%19 state.old]
~> %slog.0^leaf/"ames: larva %19 reload"
larval-gate
::
[%19 %larva *]
~> %slog.1^leaf/"ames: larva %19 load"
=. cached-state `[%19 state.old]
=. queued-events events.old
=. adult-gate (load:adult-core %19 state.old)
larval-gate
::
[%20 %adult *] (load:adult-core %20 state.old)
::
[%20 %larva *]
~> %slog.1^leaf/"ames: larva %20 load"
=. queued-events events.old
=. adult-gate (load:adult-core %20 state.old)
larval-gate
==
::
@ -2017,7 +2060,22 @@
::
=? u.cached-state ?=(%18 -.u.cached-state)
19+(state-18-to-19:load:adult-core +.u.cached-state)
?> ?=(%19 -.u.cached-state)
=^ moz u.cached-state
?. ?=(%19 -.u.cached-state) [~ u.cached-state]
:_ 20+(state-19-to-20:load:adult-core +.u.cached-state)
:: if we didn't have a unix-duct, the larval stage will be expecting
:: a %born task from unix, which will in turn emit the %saxo that will
:: start sending informal pings to the sponsorship chain
::
?~ unix-duct.+.u.cached-state
moz
~> %slog.0^leaf/"ames: retrieving sponsorship chain"
^- (list move)
:_ moz
=+ ev-core=(ev [now eny rof] [/saxo]~ ames-state.adult-gate)
[unix-duct.+.u.cached-state %give %saxo get-sponsors:ev-core]
::
?> ?=(%20 -.u.cached-state)
=. ames-state.adult-gate +.u.cached-state
[moz larval-core(cached-state ~)]
--
@ -2198,6 +2256,7 @@
%rot acc(rot %.y)
%kay acc(kay %.y)
%fin acc(fin %.y)
%sun acc(sun %.y)
==
event-core
:: +on-prod: re-send a packet per flow to each of .ships
@ -2767,6 +2826,19 @@
++ on-stun
|= =stun
^+ event-core
%- %^ ev-trace sun.veb ship.stun
=/ lane=tape
?: &
:: turn off until correct parsing ip/port in ames.c
:: (see https://github.com/urbit/vere/pull/623)
""
?: ?=(%& -.lane.stun)
"from {<p.lane.stun>}"
=, lane.stun
=/ ip=@if (end [0 32] p)
=/ pt=@ud (cut 0 [32 16] p)
"lane {(scow %if ip)}:{((d-co:co 1) pt)} ({(scow %ux p)})"
|.("inject %stun {<-.stun>} {lane}")
%- emit
%^ poke-ping-app unix-duct.ames-state our
?. ?=(%fail -.stun) -.stun
@ -5392,15 +5464,15 @@
[moves ames-gate]
:: +stay: extract state before reload
::
++ stay [%19 %adult ames-state]
++ stay [%20 %adult ames-state]
:: +load: load in old state after reload
::
++ load
=< |= $= old-state
$% [%19 ^ames-state]
$% [%20 ^ames-state]
==
^+ ames-gate
?> ?=(%19 -.old-state)
?> ?=(%20 -.old-state)
ames-gate(ames-state +.old-state)
:: all state transitions are called from larval ames
::
@ -5634,7 +5706,7 @@
::
++ state-18-to-19
|= old=ames-state-18
^- ^ames-state
^- ames-state-19
%= old
::
dead [dead.old ~]
@ -5672,6 +5744,13 @@
==
==
==
::
++ state-19-to-20
|= old=ames-state-19
^- ^ames-state
%= old
veb.bug [&1 &2 &3 &4 &5 &6 &7 &8 |8 %.n]:veb.bug.old
==
--
:: +scry: dereference namespace
::

View File

@ -1311,7 +1311,7 @@
::
?& ?=(^ host)
?| ?=(~ auth.endpoint.auth.state)
!=('localhost' (fall (rush u.host host-sans-port) '')))
!=('localhost' (fall (rush u.host host-sans-port) ''))
== ==
%- (trace 2 |.("eauth: storing endpoint at {(trip u.host)}"))
=/ new-auth=(unit @t)

View File

@ -654,7 +654,10 @@
++ public-keys-give
|= [yen=(set duct) =public-keys-result]
|^
=+ yez=(sort ~(tap in yen) sorter)
=/ yaz %+ skid ~(tap in yen)
|= d=duct
&(?=([[%ames @ @ *] *] d) !=(%public-keys i.t.i.d))
=/ yez (weld p.yaz (sort q.yaz sorter))
|- ^+ this-su
?~ yez this-su
=* d i.yez