Merge pull request #1194 from urbit/de-dns

rewrites dynamic dns, disabling indirection, proxying, and automation
This commit is contained in:
Joe Bryan 2019-06-27 12:11:01 -07:00 committed by GitHub
commit 74a05a7782
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 1667 additions and 1541 deletions

View File

@ -1 +1 @@
https://ci-piers.urbit.org/zod-e35c639b161799faad2fb55cde89c150e68b804f.tgz
https://ci-piers.urbit.org/zod-d71780001aed3ba464d8b24f223f6bc597236718.tgz

View File

@ -12,6 +12,14 @@
::
++ de-base64url
~(de base64 | &)
:: +join-turf
::
++ join-turf
|= hot=(list turf)
^- cord
%+ rap 3
%- (bake join ,[cord wain])
[', ' (turn hot en-turf:html)]
:: |octn: encode/decode unsigned atoms as big-endian octet stream
::
++ octn
@ -23,7 +31,7 @@
::
++ body
|%
+$ acct [id=@t wen=@t sas=@t]
+$ acct [wen=@t sas=@t]
::
+$ order
$: exp=@t
@ -74,7 +82,7 @@
^- $-(json acct:body)
:: ignoring key, contact, initialIp
::
(ot 'id'^no 'createdAt'^json-date 'status'^so ~)
(ot 'createdAt'^json-date 'status'^so ~)
:: +order: parse certificate order
::
++ order
@ -457,8 +465,8 @@
?~ rod
:: XX shouldn't happen
::
(join '.' /network/arvo/(crip +:(scow %p our.bow)))
(join ', ' (turn ~(tap in dom.u.rod) |=(a=turf (join '.' a))))
(en-turf:html /network/arvo/(crip +:(scow %p our.bow)))
(join-turf ~(tap in dom.u.rod))
'. retrying in ~d7.'
==
(emit (notify msg ~))
@ -474,7 +482,7 @@
' too many certificates issued for '
:: XX get from detail
::
(join '.' /network/arvo)
(en-turf:html /network/arvo)
'. retrying in '
(scot %dr lul) '.'
==
@ -588,7 +596,7 @@
:- %a
%+ turn
~(tap in ~(key by `(map turf *)`u.next-order))
|=(a=turf [%o (my type+s+'dns' value+s+(join '.' a) ~)])
|=(a=turf [%o (my type+s+'dns' value+s+(en-turf:html a) ~)])
==
=/ wire-params [try %new-order /(scot %da now.bow)]
(stateful-request wire-params new-order.dir json)
@ -762,7 +770,7 @@
=/ msg=cord
%+ rap 3
:~ 'unable to reach ' (scot %p our.bow)
' via http at ' (join '.' turf.i.item) ':80'
' via http at ' (en-turf:html turf.i.item) ':80'
==
(emit(next-order ~) (notify msg [(sell !>(rep)) ~]))
?: ?=(~ (skip ~(val by u.next-order) |=([@ud valid=?] valid)))
@ -957,7 +965,7 @@
=> =/ msg=cord
%+ rap 3
:~ 'received https certificate for '
(join ', ' (turn ~(tap in dom.u.liv) |=(a=turf (join '.' a))))
(join-turf ~(tap in dom.u.liv))
==
(emit (notify msg ~))
:: set renewal timer, install certificate in %eyre
@ -1029,7 +1037,7 @@
=/ msg=cord
%+ rap 3
:~ 'unable to retrieve self-hosted domain validation token '
'via ' (join '.' dom.aut) '. '
'via ' (en-turf:html dom.aut) '. '
'please confirm your urbit has network connectivity.'
==
(emit (notify msg [(sell !>(rep)) ~]))
@ -1260,7 +1268,7 @@
~& [%cert `wain`cer.u.liv]
~& [%expires exp.u.liv]
~& :- %domains
(join ', ' (turn ~(tap in dom.u.liv) |=(a=turf (join '.' a))))
(join-turf ~(tap in dom.u.liv))
this
::
%dbug-history
@ -1382,7 +1390,7 @@
=/ msg=cord
%+ rap 3
:~ 'requesting an https certificate for '
(join ', ' (turn ~(tap in dom) |=(a=turf (join '.' a))))
(join-turf ~(tap in dom))
==
(emit (notify msg ~))
:: if registered, create order

895
app/dns-bind.hoon Normal file
View File

@ -0,0 +1,895 @@
/- *dns-bind, dns, hall
/+ tapp, stdio
::
:: tapp types and boilerplate
::
=> |%
++ collector-app `dock`[~zod %dns-collector]
+$ app-state
$: %0
:: nem: authoritative state
::
nem=(unit nameserver)
==
+$ peek-data _!!
+$ in-poke-data
$% [%dns-authority =authority]
[%dns-bind =ship =target]
[%handle-http-request =inbound-request:http-server]
==
+$ out-poke-data
$% [%dns-bind =ship =target]
[%dns-complete =ship =binding:dns]
[%drum-unlink =dock]
==
+$ in-peer-data
$% [%dns-request =request:dns]
==
+$ out-peer-data ~
++ tapp
%: ^tapp
app-state
peek-data
in-poke-data
out-poke-data
in-peer-data
out-peer-data
==
++ tapp-async tapp-async:tapp
++ stdio (^stdio out-poke-data out-peer-data)
--
::
:: oauth2 implementation
::
=> |%
:: +oauth2-config: as one would expect
::
+$ oauth2-config
$: auth-url=@t
exchange-url=@t
domain=turf
initial-path=path
redirect-path=path
scopes=(list @t)
==
:: +oauth2: library core
::
++ oauth2
|_ [our=@p now=@da config=oauth2-config code=@t =hart:eyre secrets=@t]
::
++ local-uri
|= [our=ship =path]
^- @t
:: XX can't scry in +mule
::
:: =/ =hart:eyre .^(hart:eyre %r /(scot %p our)/host/real)
(crip (en-purl:html [hart [~ path] ~]))
::
:: XX can't scry in +mule
::
:: ++ code
:: ^- @t
:: %- crip
:: +:(scow %p .^(@p %j /(scot %p our)/code/(scot %da now)/(scot %p our)))
::
:: to initialize these values: |init-oauth2 /com/googleapis
::
++ oauth2-secrets
^- [client-id=@t client-secret=@t]
=; =wain
?> ?=([@t @t ~] wain)
[i.wain i.t.wain]
::
%- to-wain:format
%- need
%+ de:crub:crypto code
%+ slav %uw
:: XX can't scry in +mule
::
:: .^(@ %cx :(weld /(scot %p our)/home/(scot %da now)/sec domain.config /atom))
secrets
::
++ initial-uri (local-uri our initial-path.config)
++ redirect-uri (local-uri our redirect-path.config)
::
++ redirect-to-provider
^- @t
=/ url (need (de-purl:html auth-url.config))
=. r.url
:* ['access_type' 'offline']
['response_type' 'code']
['prompt' 'consent']
['client_id' client-id:oauth2-secrets]
['redirect_uri' redirect-uri]
['scope' (rap 3 (join ' ' scopes.config))]
r.url
==
(crip (en-purl:html url))
::
++ retrieve-access-token
|= code=@t
^- request:http
=/ hed
:~ ['Accept' 'application/json']
['Content-Type' 'application/x-www-form-urlencoded']
==
=/ bod
%- some %- as-octt:mimes:html
%- tail %- tail:en-purl:html
:~ ['client_id' client-id:oauth2-secrets]
:: note: required, unused parameter
::
['redirect_uri' redirect-uri]
['client_secret' client-secret:oauth2-secrets]
['grant_type' 'authorization_code']
['code' code]
==
[%'POST' exchange-url.config hed bod]
::
++ parse-token-response
|= =octs
^- (unit [access=@t expires=@u refresh=@t])
%. q.octs
;~ biff
de-json:html
=, dejs-soft:format
(ot 'access_token'^so 'expires_in'^ni 'refresh_token'^so ~)
==
:: XX implement
::
++ refresh-token !!
--
--
::
:: helpers
::
=> |%
:: +name: fully-qualified domain name for :ship
::
++ name
|= [=ship =turf]
(cat 3 (en-turf:html (weld turf /(crip +:(scow %p ship)))) '.')
:: +lame: domain name for :ship (without trailing '.')
::
++ lame
|= [=ship =turf]
(en-turf:html (weld turf /(crip +:(scow %p ship))))
:: +endpoint: append :path to :purl
::
++ endpoint
|= [=purl:eyre =path]
^+ purl
purl(q.q (weld q.q.purl path))
:: +params: append :params to :purl
::
++ params
|= [=purl:eyre =quay:eyre]
^+ purl
purl(r (weld r.purl quay))
:: +json-octs: deserialize json and apply reparser
::
++ json-octs
|* [bod=octs wit=fist:dejs:format]
=/ jon (de-json:html q.bod)
?~ jon ~
(wit u.jon)
:: +ship-turf: parse ship from first subdomain
::
++ ship-turf
|= [nam=@t aut-dom=turf]
^- (unit ship)
=/ dom=(unit host:eyre)
(rush nam ;~(sfix thos:de-purl:html dot))
?: ?| ?=(~ dom)
?=(%| -.u.dom)
?=(~ p.u.dom)
==
~
=/ who
(rush (head (flop p.u.dom)) fed:ag)
?~ who ~
?. =(aut-dom (flop (tail (flop p.u.dom))))
~
:: galaxies always excluded
::
?: ?=(%czar (clan:title u.who))
~
who
--
::
:: service providers
::
=> |%
:: +provider: initialize provider-specific core
::
++ provider
|= aut=authority
?- -.pro.aut
%fcloud ~(. fcloud aut)
%gcloud ~(. gcloud aut)
==
:: |fcloud: Cloudflare provider
::
++ fcloud
=> |%
++ parse-raw-record
|= aut-dom=turf
^- $- json
(unit [=ship id=@ta tar=target])
=, dejs:format
%+ cu
|= [id=@t typ=@t nam=@t dat=@t]
^- (unit [=ship id=@ta tar=target])
:: XX fix this
::
=/ him (ship-turf (cat 3 nam '.') aut-dom)
?: ?=(~ him)
~
?+ typ
~
::
%'A'
=/ adr (rush dat lip:ag)
?~ adr ~
`[u.him `@ta`id %direct %if u.adr]
::
%'CNAME'
:: XX fix this
::
=/ for (ship-turf (cat 3 dat '.') aut-dom)
?~ for ~
`[u.him `@ta`id %indirect u.for]
==
:: XX parse dates, proxied, ttl?
::
%- ot :~
'id'^so
'type'^so
'name'^so
'content'^so
==
--
::
|_ aut=authority
:: +base: provider service endpoint
::
++ base
^- purl:eyre
(need (de-purl:html 'https://api.cloudflare.com/client/v4'))
:: +headers: standard HTTP headers for all |fcloud requests
::
++ headers
|= aut=authority
?> ?=(%fcloud -.pro.aut)
%- ~(gas by *math:eyre)
:~ ['Content-Type' ['application/json' ~]]
['X-Auth-Email' [email.auth.pro.aut ~]]
['X-Auth-Key' [key.auth.pro.aut ~]]
==
:: +zone: provider-specific zone info request
::
++ zone
^- hiss:eyre
?> ?=(%fcloud -.pro.aut)
[(endpoint base /zones/[zone.pro.aut]) %get (headers aut) ~]
:: +record: JSON-formatted provider-specific dns record
::
++ record
|= [him=ship tar=target]
^- json
?> ?=(%fcloud -.pro.aut)
=/ type
?:(?=(%direct -.tar) 'A' 'CNAME')
=/ data
?: ?=(%direct -.tar)
(crip +:(scow %if p.tar))
(lame p.tar dom.aut)
:- %o
%- ~(gas by *(map @t json))
:~ ['name' %s (lame him dom.aut)]
['type' %s type]
['content' %s data]
:: XX make configureable?
::
['ttl' %n ~.1]
['proxied' %b %.n]
==
:: +create: provider-specific record-creation request
::
++ create
|= [him=ship tar=target pre=(unit [id=@ta tar=target])]
^- hiss:eyre
?> ?=(%fcloud -.pro.aut)
=/ bod=octs
%- as-octt:mimes:html
%- en-json:html
(record him tar)
?~ pre
:- (endpoint base /zones/[zone.pro.aut]/['dns_records'])
[%post (headers aut) `bod]
:- (endpoint base /zones/[zone.pro.aut]/['dns_records']/[id.u.pre])
[%put (headers aut) `bod]
:: +existing: list existing records stored by provider
::
++ existing
|= page=(unit @t)
^- hiss:eyre
?> ?=(%fcloud -.pro.aut)
:: XX more url params:
:: ?type ?per-page ?order ?direction
::
:- %+ params
(endpoint base /zones/[zone.pro.aut]/['dns_records'])
?~(page ~ ['page' u.page]~)
[%get (headers aut) ~]
:: +parse-list: existing records stored by provider
::
++ parse-list
^- $- json
(pair (list [=ship id=@ta tar=target]) (unit @t))
?> ?=(%fcloud -.pro.aut)
=, dejs:format
%+ cu
|= $: success=?
response=(list (unit [=ship id=@ta tar=target]))
paginate=[page=@ud per-page=@ud count=@ud total-count=@ud]
==
^- (pair (list [=ship id=@ta tar=target]) (unit @t))
?. success [~ ~]
:- (murn response same)
:: XX calculate next page number if applicable
::
~
:: XX parse errors and messages?
::
%- ot :~
'success'^bo
'result'^(ar (parse-raw-record dom.aut))
:- 'result_info'
%- ot :~
'page'^ni
'per_page'^ni
'count'^ni
'total_count'^ni
==
==
:: +parse-record: single record stored by provider
::
++ parse-record
^- $- json
(unit [=ship id=@ta tar=target])
?> ?=(%fcloud -.pro.aut)
=, dejs:format
%+ cu
|= [success=? response=(unit [=ship id=@ta tar=target])]
^- (unit [=ship id=@ta tar=target])
?. success ~
response
:: XX parse errors and messages?
::
%- ot :~
'success'^bo
'result'^(parse-raw-record dom.aut)
==
--
:: |gcloud: GCP provider
::
++ gcloud
|_ aut=authority
:: +base: provider service endpoint
::
++ base
^- purl:eyre
(need (de-purl:html 'https://www.googleapis.com/dns/v1/projects'))
:: +headers: standard HTTP headers for all |gcloud requests
::
++ headers
|= aut=authority
?> ?=(%gcloud -.pro.aut)
?. ?=(^ auth.pro.aut)
~| %gcloud-missing-auth !!
%- ~(gas by *math:eyre)
:~ ['Content-Type' ['application/json' ~]]
['Authorization' [`@t`(cat 3 'Bearer ' access.u.auth.pro.aut) ~]]
==
:: +zone: provider-specific zone info request
::
++ zone
^- hiss:eyre
?> ?=(%gcloud -.pro.aut)
:- (endpoint base /[project.pro.aut]/['managedZones']/[zone.pro.aut])
[%get (headers aut) ~]
:: +record: JSON-formatted provider-specific dns record
::
++ record
|= [him=ship tar=target]
^- json
?> ?=(%gcloud -.pro.aut)
=/ type
?:(?=(%direct -.tar) 'A' 'CNAME')
=/ data
?: ?=(%direct -.tar)
[%s (crip +:(scow %if p.tar))]
[%s (name p.tar dom.aut)]
:- %o
%- ~(gas by *(map @t json))
:~ ['name' %s (name him dom.aut)]
['type' %s type]
:: XX make configureable?
::
['ttl' %n ~.300]
['rrdatas' %a data ~]
==
:: +create: provider-specific record-creation request
::
++ create
=, eyre
|= [him=ship tar=target pre=(unit [id=@ta tar=target])]
^- hiss
?> ?=(%gcloud -.pro.aut)
=/ url=purl
%+ endpoint base
/[project.pro.aut]/['managedZones']/[zone.pro.aut]/changes
=/ bod=octs
%- as-octt:mimes:html
%- en-json:html
:- %o
%- ~(gas by *(map @t json))
:- ['additions' %a (record him tar) ~]
?~ pre ~
[['deletions' %a (record him tar.u.pre) ~] ~]
[url %post (headers aut) `bod]
:: +existing: list existing records stored by provider
::
++ existing
=, eyre
|= page=(unit @t)
^- hiss
?> ?=(%gcloud -.pro.aut)
=/ url=purl
%+ endpoint base
/[project.pro.aut]/['managedZones']/[zone.pro.aut]/rrsets
=/ hed=math (headers aut)
=? hed ?=(^ page)
(~(put by hed) 'pageToken' [u.page]~)
[url %get hed ~]
:: +parse-list: existing records stored by provider
::
++ parse-list
^- $- json
(pair (list [=ship id=@ta tar=target]) (unit @t))
?> ?=(%gcloud -.pro.aut)
=, dejs:format
=> |%
++ page (uf ~ (mu so))
++ records
%+ uf ~
%+ cu
|*(a=(list (unit)) (murn a same))
(ar parse-record)
--
:: XX parse but don't produce
:: 'kind'^(su (jest "dns#resourceRecordSetsListResponse'))
::
(ou 'rrsets'^records 'nextPageToken'^page ~)
:: +parse-record: single record stored by provider
::
++ parse-record
^- $- json
(unit [=ship id=@ta tar=target])
?> ?=(%gcloud -.pro.aut)
=, dejs:format
%+ cu
|= [typ=@t nam=@t dat=(list @t)]
^- (unit [=ship id=@ta tar=target])
:: gcloud doesn't expose UUIDs for bindings
::
=/ id %$
=/ him (ship-turf nam dom.aut)
?: |(?=(~ him) ?=(~ dat) ?=(^ t.dat))
~
?+ typ
~
::
%'A'
=/ adr (rush i.dat lip:ag)
?~ adr ~
`[u.him id %direct %if u.adr]
::
%'CNAME'
=/ for (ship-turf i.dat dom.aut)
?~ for ~
`[u.him id %indirect u.for]
==
::
%- ot :~
:: 'kind'^(su (jest "dns#resourceRecordSet'))
::
'type'^so
'name'^so
'rrdatas'^(ar so)
==
--
--
::
:: monadic helpers (XX move to stdio?)
::
=> |%
:: +backoff: exponential backoff timer
::
++ backoff
|= [try=@ud limit=@dr]
=/ m (async:stdio ,~)
^- form:m
;< eny=@uvJ bind:m get-entropy:stdio
;< now=@da bind:m get-time:stdio
%- wait:stdio
%+ add now
%+ min limit
?: =(0 try) ~s0
%+ add
(mul ~s1 (bex (dec try)))
(mul ~s0..0001 (~(rad og eny) 1.000))
::
++ request
|= =hiss:eyre
=/ m (async:stdio (unit httr:eyre))
^- form:m
;< ~ bind:m (send-hiss:stdio hiss)
take-maybe-sigh:stdio
::
++ request-retry
|= [=hiss:eyre max=@ud limit=@dr]
=/ m (async:stdio (unit httr:eyre))
=/ try=@ud 0
|- ^- form:m
=* loop $
?: =(try max)
(pure:m ~)
;< ~ bind:m (backoff try limit)
;< rep=(unit httr:eyre) bind:m (request hiss)
:: XX needs a better predicate. LTE will make this easier
::
?: &(?=(^ rep) =(200 p.u.rep))
(pure:m (some u.rep))
loop(try +(try))
--
::
:: application actions
::
=> |%
++ confirm-authority
|= =authority
=/ m (async:stdio ?)
^- form:m
;< rep=(unit httr:eyre) bind:m
(request-retry zone:(provider authority) 5 ~m10)
(pure:m &(?=(^ rep) =(200 p.u.rep)))
::
++ retrieve-existing
|= =authority
=/ m (async:stdio (map ship bound))
^- form:m
=| existing=(map ship bound)
=| next-page=(unit @t)
;< now=@da bind:m get-time:stdio
|- ^- form:m
=* loop $
;< rep=(unit httr:eyre) bind:m
(request-retry (existing:(provider authority) next-page) 5 ~m10)
?: ?| ?=(~ rep)
?=(~ r.u.rep)
==
(pure:m existing)
::
=* octs u.r.u.rep
=+ ^- [dat=(list [=ship id=@ta =target]) page=(unit @t)]
:: XX gross
::
=- ?~(- [~ ~] -)
(json-octs octs parse-list:(provider authority))
=. existing
|- ^+ existing
?~ dat
existing
=/ =bound [now id.i.dat target.i.dat ~]
$(dat t.dat, existing (~(put by existing) ship.i.dat bound))
?~ page
(pure:m existing)
loop(next-page page)
::
++ create-binding
|= [=authority =ship =target existing=(unit bound)]
=/ m (async:stdio (unit bound))
^- form:m
?: &(?=(^ existing) =(target cur.u.existing))
~| %bind-duplicate-wat-do !!
::
=/ pre=(unit [@ta ^target])
?~(existing ~ (some [id cur]:u.existing))
;< rep=(unit httr:eyre) bind:m
(request (create:(provider authority) ship target pre))
:: XX retryable?
::
?. &(?=(^ rep) =(200 p.u.rep))
(pure:m ~)
::
=* httr u.rep
=/ id=@ta
?. ?=(%fcloud -.pro.authority) ~.
?. ?=(^ r.httr)
~| [%authority-create-confirm-id rep] !!
=/ dat=(unit [^ship id=@ta ^target])
(json-octs u.r.httr parse-record:(provider authority))
?~(dat ~. id.u.dat)
::
=/ =address:dns
?>(?=(%direct -.target) +.target)
=/ =turf
(weld dom.authority /(crip +:(scow %p ship)))
;< ~ bind:m (poke-app:stdio collector-app [%dns-complete ship address turf])
;< now=@da bind:m get-time:stdio
=/ =bound
[now id target ?~(existing ~ [[wen cur] hit]:u.existing)]
(pure:m (some bound))
::
++ initialize-authority
|= [aut=authority state=app-state]
=/ m tapp-async
^- form:m
?> ?=(^ nem.state)
=* nam u.nem.state
;< good=? bind:m (confirm-authority aut)
?. good
~& %dns-authority-failed
(pure:m state(nem ~))
::
:: XX wait-effect
::
;< existing=(map ship bound) bind:m (retrieve-existing aut)
=. bon.nam (~(uni by bon.nam) existing)
=. nem.state (some nam)
::
:: XX wait-effect
::
;< ~ bind:m (peer-app:stdio collector-app /requests)
(pure:m state)
--
::
:: |oauth2-core: configured oauth functionality (for |gcloud only)
::
=> |%
++ oauth2-core
|= [=bowl:gall code=@t =hart:eyre secrets=@t]
=/ =oauth2-config
:* auth-url='https://accounts.google.com/o/oauth2/v2/auth'
exchange-url='https://www.googleapis.com/oauth2/v4/token'
domain=/com/googleapis
redirect-path=/dns/oauth
initial-path=/dns/oauth/result
:~ 'https://www.googleapis.com/auth/ndev.clouddns.readwrite'
'https://www.googleapis.com/auth/cloud-platform.read-only'
== ==
~(. oauth2 our.bowl now.bowl oauth2-config code hart secrets)
--
::
:: the app itself
::
=* default-tapp default-tapp:tapp
%- create-tapp-all:tapp
^- tapp-core-all:tapp
|_ [=bowl:gall state=app-state]
::
++ handle-peek handle-peek:default-tapp
++ handle-peer handle-peer:default-tapp
::
++ handle-init
=/ m tapp-async
^- form:m
;< success=? bind:m (bind-route:stdio [~ /dns/oauth] dap.bowl)
~| %dns-unable-to-bind-route
?> success
;< ~ bind:m (poke-app:stdio [[our %hood] [%drum-unlink our dap]]:bowl)
(pure:m state)
::
++ handle-poke
|= =in-poke-data
=/ m tapp-async
^- form:m
?. (team:title [our src]:bowl)
~| %bind-yoself !!
?- -.in-poke-data
::
%dns-authority
?. =(~ nem.state)
~| %authority-reset-wat-do !!
=* aut authority.in-poke-data
=/ nam=nameserver [aut ~ ~]
=. nem.state (some nam)
:: XX move this into the provider interface
::
?: ?& ?=(%gcloud -.pro.aut)
?=(~ auth.pro.aut)
==
~& %do-the-oauth-thing
~& initial-uri:(oauth2-core bowl scry.pro.aut)
(pure:m state)
::
(initialize-authority aut state)
::
%dns-bind
?~ nem.state
~| %bind-not-authority !!
=* nam u.nem.state
=* who ship.in-poke-data
=* tar target.in-poke-data
?. ?=(%indirect -.tar)
~| %indirect-unsupported !!
:: defer %indirect where target isn't yet bound
::
:: ?: ?& ?=(%indirect -.tar)
:: !(~(has by bon.nam) p.tar)
:: ==
:: =. dep.nam (~(put ju dep.nam) p.tar [who tar])
:: =. nem.state (some nam)
:: (pure:m state)
=/ existing (~(get by bon.nam) who)
;< new=(unit bound) bind:m (create-binding aut.nam who tar existing)
?~ new
~& [%bind-failed in-poke-data]
(pure:m state)
=. bon.nam (~(put by bon.nam) who u.new)
=. nem.state (some nam)
::
:: XX wait-effect
::
=/ dep=(list [=ship =target])
~(tap in (~(get ju dep.nam) who))
|- ^- form:m
=* loop $
?~ dep
=. dep.nam (~(del by dep.nam) who)
=. nem.state (some nam)
(pure:m state)
;< ~ bind:m (poke-app:stdio [our dap]:bowl [%dns-bind ship target]:i.dep)
loop(dep t.dep)
::
:: XX need to %handle-http-cancel as well
::
%handle-http-request
:: always stash request bone for giving response
::
=/ =bone ost.bowl
:: XX maybe always (set-raw-contract %request) so transaction failure is captured?
::
=* inbound-request inbound-request.in-poke-data
?~ nem.state
~& :* %not-an-authority
%http-request
=> inbound-request
[authenticated secure address [method url]:request]
==
;< ~ bind:m
(send-effect-on-bone:stdio bone [%http-response %start [%403 ~] ~ %.y])
(pure:m state)
::
=* nam u.nem.state
?> ?=(%gcloud -.pro.aut.nam)
::
=/ parsed=(unit (pair pork:eyre quay:eyre))
%+ rush
url.request.inbound-request
;~(plug ;~(pose apat:de-purl:html (easy *pork:eyre)) yque:de-purl:html)
::
?. ?=(^ parsed)
~| [%invalid-url url.request.inbound-request] !!
=* url q.p.u.parsed
=* ext p.p.u.parsed
=* params q.u.parsed
::
?+ url
;< ~ bind:m
(send-effect-on-bone:stdio bone [%http-response %start [%404 ~] ~ %.y])
(pure:m state)
::
[%dns %oauth ~]
=/ link (trip redirect-to-provider:(oauth2-core bowl scry.pro.aut.nam))
=/ bod=(unit octs)
%- some
%- as-octt:mimes:html
%- en-xml:html
;html
;head
;title: :dns oauth
==
;body
;p make sure that the oauth credential is configured
with a redirect uri of {(trip redirect-uri:(oauth2-core bowl scry.pro.aut.nam))}
==
;a(href link): {link}
==
==
;< ~ bind:m
(send-effect-on-bone:stdio bone [%http-response %start [%200 ~] bod %.y])
(pure:m state)
::
[%dns %oauth %result ~]
=/ code (~(got by (my params)) %code)
:: XX make path configurable
::
=/ hed [['Location' '/dns/oauth/success'] ~]
::
;< ~ bind:m
(send-request:stdio (retrieve-access-token:(oauth2-core bowl scry.pro.aut.nam) code))
;< rep=(unit client-response:http-client) bind:m
take-maybe-response:stdio
:: XX retry
::
?> ?& ?=(^ rep)
?=(%finished -.u.rep)
?=(^ full-file.u.rep)
==
=/ data (parse-token-response:oauth2 data.u.full-file.u.rep)
=. auth.pro.aut.nam (some [access refresh]:(need data))
=. nem.state (some nam)
:: XX use expiry to set refresh timer
::
:: XX may need to send this as a card so we don't wait
::
;< ~ bind:m
(send-effect-on-bone:stdio bone [%http-response %start [%301 hed] ~ %.y])
(initialize-authority aut.nam state)
::
[%dns %oauth %success ~]
=/ bod=(unit octs)
%- some
%- as-octt:mimes:html
%- en-xml:html
;html
;head
;title: :dns oauth
==
;body
;p: you may close the browser window
;p
;span: XX remove me
:: XX make path configurable
::
;a(href "/dns/oauth"): again
==
==
==
;< ~ bind:m (send-effect:stdio %http-response %start [%201 ~] bod %.y)
(pure:m state)
==
==
::
++ handle-diff
|= [=dock =path =in-peer-data]
=/ m tapp-async
^- form:m
?. =(dock collector-app)
(pure:m state)
=* req request.in-peer-data
=/ =target [%direct address.req]
;< ~ bind:m (poke-app:stdio [our dap]:bowl [%dns-bind ship.req target])
(pure:m state)
::
++ handle-take
|= =sign:tapp
=/ m tapp-async
^- form:m
?. ?=(%quit -.sign)
:: XX handle stuff
::
(pure:m state)
::
?. ?& =(dock.sign collector-app)
=(path.sign /requests)
==
~& [%unexpected-quit-wat-do [dock path]:sign]
(pure:m state)
::
;< ~ bind:m (peer-app:stdio collector-app /requests)
(pure:m state)
--

158
app/dns-collector.hoon Normal file
View File

@ -0,0 +1,158 @@
/- dns
::
:: app types and boilerplate
::
=> |%
+$ app-state
$: %0
requested=(map ship address:dns)
completed=(map ship binding:dns)
==
+$ peek-data [%noun (list (pair ship address:dns))]
+$ in-poke-data
$% [%dns-address =address:dns]
[%dns-complete =ship =binding:dns]
==
+$ out-poke-data
$% [%drum-unlink =dock]
==
+$ out-peer-data
$% [%dns-binding =binding:dns]
[%dns-request =request:dns]
==
+$ card
$% [%diff out-peer-data]
[%poke wire =dock out-poke-data]
==
+$ move [bone card]
--
::
=| moves=(list move)
|_ [=bowl:gall state=app-state]
::
++ this .
::
++ abet
^- (quip move _this)
[(flop moves) this(moves ~)]
::
++ emit
|= mov=move
^+ this
this(moves [mov moves])
::
++ emil
|= moz=(list move)
|- ^+ this
?~ moz
this
$(moz t.moz, ..this (emit i.moz))
::
++ poke-app
|= [=wire =dock =out-poke-data]
^+ this
(emit [ost.bowl %poke wire dock out-poke-data])
::
++ give-result
|= [=the=path =out-peer-data]
^+ this
%- emil
%+ turn
^- (list bone)
%+ murn ~(tap by sup.bowl)
|= [ost=bone =ship =sub=path]
`(unit bone)`?.(=(the-path sub-path) ~ (some ost))
|= =bone
[bone %diff out-peer-data]
::
++ prep
|= old=(unit app-state)
^- (quip move _this)
=< abet
?~ old
(poke-app /unlink [[our %hood] [%drum-unlink our dap]]:bowl)
this(state u.old)
::
++ poke
|= =in-poke-data
^- (quip move _this)
=< abet
?- -.in-poke-data
%dns-address
=* who src.bowl
=* adr address.in-poke-data
=/ rac (clan:title who)
?. ?=(?(%king %duke) rac)
~| [%dns-collector-bind-invalid who] !!
?: (reserved:eyre if.adr)
~| [%dns-collector-reserved-address who if.adr] !!
::
=/ req=(unit address:dns) (~(get by requested.state) who)
=/ dun=(unit binding:dns) (~(get by completed.state) who)
?: &(?=(^ dun) =(adr address.u.dun))
=. requested.state (~(del by requested.state) who)
(give-result /(scot %p who) %dns-binding u.dun)
::
?: &(?=(^ req) =(adr u.req))
this
:: XX check address?
=/ =request:dns [who adr]
=. requested.state (~(put by requested.state) request)
(give-result /requests %dns-request request)
::
%dns-complete
:: XX or confirm valid binding?
::
?. (team:title [our src]:bowl)
~| %complete-yoself !!
=* who ship.in-poke-data
=* adr address.binding.in-poke-data
=* tuf turf.binding.in-poke-data
=/ req=(unit address:dns) (~(get by requested.state) who)
:: ignore established bindings that don't match requested
::
?: ?& ?=(^ req)
!=(adr u.req)
==
this
=: requested.state (~(del by requested.state) who)
completed.state (~(put by completed.state) who [adr tuf])
==
(give-result /(scot %p who) %dns-binding adr tuf)
==
::
++ peek
|= =path
^- (unit (unit peek-data))
~& path
?+ path [~ ~]
[%x %requested ~]
[~ ~ %noun ~(tap by requested.state)]
==
::
++ peer
|= =path
^- (quip move _this)
=< abet
:: will be immediately unlinked, see +prep
::
?: ?=([%sole *] path)
this
?. ?=([@ ~] path)
~| %invalid-path !!
?: ?=(%requests i.path)
=/ requests ~(tap by requested.state)
|- ^+ this
=* loop $
?~ requests
this
=. ..this (give-result path %dns-request i.requests)
loop(requests t.requests)
::
=/ who (slaw %p i.path)
?~ who
~| %invalid-path !!
?~ dun=(~(get by completed.state) who)
this
(give-result path %dns-binding u.dun)
--

File diff suppressed because it is too large Load Diff

View File

@ -2,14 +2,26 @@
::
:::: /hoon/authority/dns/gen
::
/- *dns, *sole
/- *dns-bind, *sole
/+ *generators
:- %ask
|= $: [now=@da eny=@uvJ bec=beak]
[arg=$@(~ [dom=turf ~])]
~
==
^- (sole-result [%dns-command %authority authority])
^- (sole-result [%dns-authority authority])
=* our p.bec
:: XX must be evaluated outside tapp core due to +mule
::
=/ =hart:eyre .^(hart:eyre %r /(scot %p our)/host/real)
:: XX terrible
=/ domain /com/googleapis
=/ code
%- crip
+:(scow %p .^(@p %j /(scot %p our)/code/(scot %da now)/(scot %p our)))
=/ secrets
.^(@t %cx :(weld /(scot %p our)/home/(scot %da now)/sec domain /atom))
::
=- ?~ arg -
(fun.q.q [%& dom.arg])
%+ prompt
@ -27,4 +39,4 @@
%+ parse urs:ab
|= zone=@ta
%- produce
[%dns-command %authority [p.hot %gcloud project zone ~]]
[%dns-authority [p.hot %gcloud project zone [code hart secrets] ~]]

26
gen/dns/auto.hoon Normal file
View File

@ -0,0 +1,26 @@
:: DNS: configure automatically
::
:::: /hoon/auto/dns/gen
::
/- *sole
/+ *generators
:- %ask
|= [[now=@da eny=@uvJ bec=beak] ~ ~]
^- (sole-result [%dns-auto ~])
=* our p.bec
=/ rac (clan:title our)
::
?: ?=(?(%earl %pawn) rac)
=/ msg1 "domain names are not provided for comets and moons"
=/ msg2 "see XX for BYOD"
%+ print leaf+msg2
(print leaf+msg1 no-product)
::
?. ?=(%czar rac)
=/ msg1 ":dns|auto is only supported for galaxies"
=/ msg2 "use :dns|request with your ship's public IP address"
=/ msg3 "see XX for more details, or to BYOD"
%+ print leaf+msg3
%+ print leaf+msg2
(print leaf+msg1 no-product)
(produce [%dns-auto ~])

View File

@ -1,25 +0,0 @@
:: DNS: configure ip address
::
:::: /hoon/authority/dns/gen
::
/- *dns, *sole
/+ *generators
:- %ask
|= $: [now=@da eny=@uvJ bec=beak]
[arg=$@(~ [addr=@if ~])]
~
==
^- (sole-result [%dns-command command])
=* our p.bec
=- ?~ arg -
(fun.q.q addr.arg)
%+ prompt
[%& %dns-address "ipv4 address: "]
%+ parse
`$-(nail (like @if))`;~(pfix ;~(pose dot (easy ~)) lip:ag)
|= addr=@if
?: (reserved:eyre addr)
=/ msg "unable to bind reserved ipv4 address {(scow %if addr)}"
(print leaf+msg no-product)
%- produce
[%dns-command %ip %if addr]

43
gen/dns/request.hoon Normal file
View File

@ -0,0 +1,43 @@
:: DNS: configure ip address
::
:::: /hoon/request/dns/gen
::
/- *dns, *sole
/+ *generators
:- %ask
|= $: [now=@da eny=@uvJ bec=beak]
[arg=$@(~ [addr=@if ~])]
~
==
^- (sole-result [%dns-address address])
=* our p.bec
=/ rac (clan:title our)
::
?: ?=(%czar rac)
=/ msg1 "galaxy domain requests must be made out-of-band"
=/ msg2 "use :dns|auto if you already have an urbit domain"
=/ msg3 "see XX for more details or to BYOD"
%+ print leaf+msg3
%+ print leaf+msg2
(print leaf+msg1 no-product)
::
?: ?=(?(%earl %pawn) rac)
=/ msg1 "domain names are not provided for comets and moons"
=/ msg2 "see XX for BYOD"
%+ print leaf+msg2
(print leaf+msg1 no-product)
:: invoke parser with arg if present
::
=- ?~ arg -
(fun.q.q addr.arg)
%+ prompt
[%& %dns-address "ipv4 address: "]
%+ parse
^- $-(nail (like @if))
;~(pfix ;~(pose dot (easy ~)) lip:ag)
|= addr=@if
?: (reserved:eyre addr)
=/ msg "unable to bind reserved ipv4 address {(scow %if addr)}"
(print leaf+msg no-product)
%- produce
[%dns-address %if addr]

View File

@ -3,20 +3,6 @@
=* rsa primitive-rsa
:::: %/lib/pkcs
|%
:: +join: join list of cords with separator
::
:: XX move to zuse?
::
++ join
|= [sep=@t hot=(list @t)]
^- @t
=| out=(list @t)
?> ?=(^ hot)
|- ^- @t
?~ t.hot
(rap 3 [i.hot out])
$(out [sep i.hot out], hot t.hot)
::
:: +rs256: RSA signatures over a sha-256 digest
::
++ rs256
@ -312,7 +298,7 @@
=> |%
:: +csr:pkcs10: certificate request
::
+= csr [key=key:rsa hot=(list (list @t))]
+$ csr [key=key:rsa hot=(list turf)]
--
|%
:: |spec:pkcs10: ASN.1 specs for certificate signing requests
@ -362,13 +348,13 @@
:: +san:en:spec:pkcs10: subject-alternate-names
::
++ san
|= hot=(list (list @t))
|= hot=(list turf)
^- spec:asn1
:- %seq
%+ turn hot
:: implicit, context-specific tag #2 (IA5String)
:: XX sanitize string?
|=(h=(list @t) [%con `bespoke:asn1`[& 2] (rip 3 (join '.' h))])
|=(=turf [%con `bespoke:asn1`[& 2] (trip (en-turf:html turf))])
--
:: |de:spec:pkcs10: ASN.1 decoding for certificate signing requests
++ de !!

View File

@ -62,21 +62,27 @@
::
:: ----
::
:: HTTP requests
:: Outgoing HTTP requests
::
++ send-request
|= =request:http
=/ m (async ,~)
^- form:m
=/ =card
[%request / request *outbound-config:http-client]
;< ~ bind:m (send-raw-card card)
(set-raw-contract & %request ~)
::
++ send-hiss
|= =hiss:eyre
=/ m (async ,~)
^- form:m
=/ =card
[%request / (hiss-to-request:html hiss) *outbound-config:http-client]
;< ~ bind:m (send-raw-card card)
(set-raw-contract & %request ~)
(send-request (hiss-to-request:html hiss))
::
:: Wait until we get an HTTP response or cancelation
::
++ take-sigh-raw
=/ m (async (unit httr:eyre))
++ take-response-raw
=/ m (async (unit client-response:http-client))
^- form:m
|= =async-input
:^ ~ ~ ~
@ -97,18 +103,27 @@
[%done ~]
::
%finished
[%done (some (to-httr:http-client +.response.sign))]
[%done (some response.sign)]
==
:: Wait until we get an HTTP response or cancelation and unset contract
::
++ take-maybe-response
=/ m (async (unit client-response:http-client))
^- form:m
;< rep=(unit client-response:http-client) bind:m
take-response-raw
;< ~ bind:m (set-raw-contract | %request ~)
(pure:m rep)
::
:: Wait until we get an HTTP response and unset contract
::
++ take-sigh
=/ m (async ,httr:eyre)
++ take-response
=/ m (async (unit client-response:http-client))
^- form:m
;< rep=(unit httr:eyre) bind:m take-sigh-raw
;< ~ bind:m (set-raw-contract | %request ~)
;< rep=(unit client-response:http-client) bind:m
take-maybe-response
?^ rep
(pure:m u.rep)
(pure:m rep)
|= =async-input
[~ ~ ~ %fail %http-canceled ~]
::
@ -117,9 +132,26 @@
++ take-maybe-sigh
=/ m (async (unit httr:eyre))
^- form:m
;< rep=(unit httr:eyre) bind:m take-sigh-raw
;< ~ bind:m (set-raw-contract | %request ~)
(pure:m rep)
;< rep=(unit client-response:http-client) bind:m
take-maybe-response
?~ rep
(pure:m ~)
:: XX s/b impossible
::
?. ?=(%finished -.u.rep)
(pure:m ~)
(pure:m (some (to-httr:http-client +.u.rep)))
::
:: Wait until we get an HTTP response and unset contract
::
++ take-sigh
=/ m (async ,httr:eyre)
^- form:m
;< rep=(unit httr:eyre) bind:m take-maybe-sigh
?^ rep
(pure:m u.rep)
|= =async-input
[~ ~ ~ %fail %http-canceled ~]
::
:: Extract body from raw httr
::
@ -163,6 +195,35 @@
::
:: ----
::
:: Incoming HTTP requests
::
++ bind-route-raw
|= [=binding:http-server =term]
=/ m (async ,~)
^- form:m
(send-raw-card [%connect / binding term])
::
++ take-bound
=/ m (async ?)
^- form:m
|= =async-input
:^ ~ ~ ~
?~ in.async-input
[%wait ~]
=* sign sign.u.in.async-input
?. ?=(%bound -.sign)
[%fail %expected-bound >got=-.sign< ~]
[%done success.sign]
::
++ bind-route
|= [=binding:http-server =term]
=/ m (async ?)
^- form:m
;< ~ bind:m (bind-route-raw binding term)
take-bound
::
:: ----
::
:: Identity is immutable
::
:: XX should be statefully cycled
@ -320,4 +381,14 @@
(pure:m ~)
;< ~ bind:m (send-effect-on-bone i.bones %diff out-peer-data)
loop(bones t.bones)
::
:: ----
::
:: Handle domains
::
++ install-domain
|= =turf
=/ m (async ,~)
^- form:m
(send-effect %rule / %turf %put turf)
--

View File

@ -32,7 +32,7 @@
+$ move (pair bone card)
++ tapp-async (async state-type)
+$ tapp-state
$: waiting=(qeu command)
$: waiting=(qeu [=bone command])
active=(unit eval-form:eval:tapp-async)
app-state=state-type
==
@ -246,7 +246,7 @@
^- (quip move _this-tapp)
?~ old-state
~& [%tapp-init dap.bowl]
=. waiting (~(put to waiting) %init ~)
=. waiting (~(put to waiting) ost.bowl [%init ~])
start-async
::
=/ old ((soft tapp-state) u.old-state)
@ -285,7 +285,7 @@
(oob-fail-async %tapp-admin-restart ~)
==
::
=. waiting (~(put to waiting) %poke tapp-in-poke-data)
=. waiting (~(put to waiting) ost.bowl [%poke tapp-in-poke-data])
?^ active
~& [%waiting-until-current-async-finishes waiting]
`this-tapp
@ -304,7 +304,7 @@
?> ?=([@ @ *] wire)
=/ her (slav %p i.wire)
=* app i.t.wire
=. waiting (~(put to waiting) %take %coup [her app] error)
=. waiting (~(put to waiting) ost.bowl [%take %coup [her app] error])
?^ active
`this-tapp
start-async
@ -330,7 +330,7 @@
++ peer
|= =path
^- (quip move _this-tapp)
=. waiting (~(put to waiting) %peer path)
=. waiting (~(put to waiting) ost.bowl [%peer path])
?^ active
`this-tapp
start-async
@ -344,7 +344,7 @@
=/ her (slav %p i.wire)
=* app i.t.wire
=* pax t.t.wire
=. waiting (~(put to waiting) %take %quit [her app] pax)
=. waiting (~(put to waiting) ost.bowl [%take %quit [her app] pax])
?^ active
`this-tapp
start-async
@ -358,7 +358,7 @@
=/ her (slav %p i.wire)
=* app i.t.wire
=* pax t.t.wire
=. waiting (~(put to waiting) %take %reap [her app] pax error)
=. waiting (~(put to waiting) ost.bowl [%take %reap [her app] pax error])
?^ active
`this-tapp
start-async
@ -372,7 +372,7 @@
=/ her (slav %p i.wire)
=* app i.t.wire
=* pax t.t.wire
=. waiting (~(put to waiting) %diff [her app] pax in-peer-data)
=. waiting (~(put to waiting) ost.bowl [%diff [her app] pax in-peer-data])
?^ active
`this-tapp
start-async
@ -398,11 +398,18 @@
++ wake-effect
|= [=wire error=(unit tang)]
^- (quip move _this-tapp)
=. waiting (~(put to waiting) %take %wake error)
=. waiting (~(put to waiting) ost.bowl [%take %wake error])
?^ active
`this-tapp
start-async
::
:: Receive route binding notification
::
++ bound
|= [=wire success=? =binding:http-server]
^- (quip move _this-tapp)
(take-async bowl `[wire %bound success binding])
::
:: Continue computing async
::
++ take-async
@ -474,7 +481,7 @@
^- (quip move _this-tapp)
?. =(~ active)
~| %async-already-active !!
=/ next=(unit command) ~(top to waiting)
=/ next=(unit [=bone =command]) ~(top to waiting)
?~ next
`this-tapp
=. active
@ -483,12 +490,14 @@
^- form:tapp-async
=/ out
%- mule |.
?- -.u.next
=. ost.bowl bone.u.next
=* input +.command.u.next
?- -.command.u.next
%init ~(handle-init handler bowl app-state)
%poke (~(handle-poke handler bowl app-state) +.u.next)
%peer (~(handle-peer handler bowl app-state) +.u.next)
%diff (~(handle-diff handler bowl app-state) +.u.next)
%take (~(handle-take handler bowl app-state) +.u.next)
%poke (~(handle-poke handler bowl app-state) input)
%peer (~(handle-peer handler bowl app-state) input)
%diff (~(handle-diff handler bowl app-state) input)
%take (~(handle-take handler bowl app-state) input)
==
?- -.out
%& p.out

7
mar/dns/address.hoon Normal file
View File

@ -0,0 +1,7 @@
/- *dns
|_ address
++ grab
|%
++ noun address
--
--

7
mar/dns/binding.hoon Normal file
View File

@ -0,0 +1,7 @@
/- *dns
|_ binding
++ grab
|%
++ noun binding
--
--

View File

@ -1,10 +0,0 @@
::
:::: /mar/dns/bind/hoon
::
/- *dns
|_ command
++ grab
|%
++ noun command
--
--

7
mar/dns/complete.hoon Normal file
View File

@ -0,0 +1,7 @@
/- *dns
|_ [ship binding]
++ grab
|%
+$ noun [ship binding]
--
--

58
sur/dns-bind.hoon Normal file
View File

@ -0,0 +1,58 @@
|%
:: +provider: DNS service provider (gcloud only for now)
::
+$ provider
$% [%fcloud zone=@ta auth=[email=@t key=@t]]
$: %gcloud
project=@ta
zone=@ta
:: XX passed as params since we can't scry in +mule
::
scry=[code=@t =hart:eyre secrets=@t]
auth=(unit [access=@t refresh=@t])
==
==
:: +authority: responsibility for a DNS zone
::
+$ authority
$: :: dom: authority over a fully-qualified domain
::
dom=turf
:: pro: DNS service provider
::
pro=provider
==
:: +target: a ship is bound to a ...
::
+$ target
$% :: %direct: an A record
::
[%direct %if p=@if]
:: %indirect: a CNAME record
::
[%indirect p=ship]
==
:: +bound: an established binding, plus history
::
+$ bound
$: :: wen: established
::
wen=@da
:: id: binding UUID (unused by gcloud)
::
id=@ta
:: cur: current target
::
cur=target
:: hit: historical targets
::
hit=(list (pair @da target))
==
:: +nameserver: a b s o l u t e p o w e r
::
+$ nameserver
$: aut=authority
bon=(map ship bound)
dep=(jug ship (pair ship target))
==
--

View File

@ -1,71 +1,5 @@
|%
:: +provider: DNS service provider (gcloud only for now)
::
+$ provider
$% [%fcloud zone=@ta auth=[email=@t key=@t]]
[%gcloud project=@ta zone=@ta auth=(unit [access=@t refresh=@t])]
==
:: +authority: responsibility for a DNS zone
::
+$ authority
$: :: dom: authority over a fully-qualified domain
::
dom=turf
:: pro: DNS service provider
::
pro=provider
==
:: +target: a ship is bound to a ...
::
+$ target
$% :: %direct: an A record
::
[%direct %if p=@if]
:: %indirect: a CNAME record
::
[%indirect p=ship]
==
:: +bound: an established binding, plus history
::
+$ bound
$: :: wen: established
::
wen=@da
:: id: binding UUID (unused by gcloud)
::
id=@ta
:: cur: current target
::
cur=target
:: hit: historical targets
::
hit=(list (pair @da target))
==
:: +nameserver: a b s o l u t e p o w e r
::
+$ nameserver
$: aut=authority
bon=(map ship bound)
dep=(jar ship (pair ship target))
pen=(map ship target)
==
:: +relay: a good parent keeps track
::
+$ relay
$: wen=@da
wer=(unit @if)
:: XX track bound state per domain
::
dom=(unit turf)
tar=target
==
:: +command: top-level app actions
::
+$ command
$% [%authority aut=authority]
[%bind for=ship him=ship tar=target]
[%bond for=ship him=ship dom=turf]
[%ip %if addr=@if]
[%meet him=ship]
==
+$ address [%if if=@if]
+$ binding [=address =turf]
+$ request [=ship =address]
--

View File

@ -12,6 +12,9 @@
[%diff out-peer-data]
[%request wire request:http outbound-config:http-client]
[%cancel-request wire ~]
[%connect wire binding:http-server term]
[%http-response =http-event:http]
[%rule wire %turf %put turf]
==
::
:: Possible async responses
@ -21,6 +24,7 @@
[%coup =dock error=(unit tang)]
[%quit =dock =path]
[%reap =dock =path error=(unit tang)]
[%bound success=? =binding:http-server]
[%http-response response=client-response:http-client]
==
::

View File

@ -555,6 +555,17 @@
|@ ++ $ ?:(*? ~ [i=(snag 0 a) t=$])
--
a
:: +join: construct a new list, placing .sep between every pair in .lit
::
++ join
|* [sep=* lit=(list)]
=. sep `_?>(?=(^ lit) i.lit)`sep
?~ lit ~
=| out=(list _?>(?=(^ lit) i.lit))
|- ^+ out
?~ t.lit
(flop [i.lit out])
$(out [sep i.lit out], lit t.lit)
::
:: +bake: convert wet gate to dry gate by specifying argument mold
::

View File

@ -6786,6 +6786,23 @@
(stag %| ;~(plug apat yque))
==
-- ::de-purl
:: +en-turf: encode +turf as a TLD-last domain string
::
++ en-turf
|= =turf
^- @t
(rap 3 (flop (join '.' turf)))
:: +de-turf: parse a TLD-last domain string into a TLD first +turf
::
++ de-turf
|= host=@t
^- (unit turf)
%+ rush host
%+ sear
|= =host:eyre
?.(?=(%& -.host) ~ (some p.host))
thos:de-purl:html
::
:: MOVEME
:: :: ++fuel:html
++ fuel :: parse urbit fcgi

View File

@ -1,20 +0,0 @@
/+ *test
::
/= app /: /===/app/dns
/!noun/
::
|%
:: tests that :dns preps without moves
::
++ test-prep
:: .our explicitly set to not-a-galaxy to avoid failing %jael scry
:: (can't control the scry product without virtualizing)
::
=/ bow=bowl:gall =>(*bowl:gall .(our ~marzod))
=^ moves app (~(prep app bow *state:app) ~)
%+ expect-eq
!> ^- (list move:app)
:~ [ost.bow %connect /dns/oauth [~ /dns/oauth] %dns]
==
!> moves
--