Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- F5 Sideband info
- https://devcentral.f5.com/wiki/iRules.SIDEBAND.ashx
- parse headers:
- set _SBrlist [regexp -line -all -inline -- {^([^:]+):[ \t]*([^\r]*)} $_SBhdrs]
- iRule example:
- #iRule to load user data from an external source
- # source: http://10.0.1.105/data/data.php?uid=parameter
- # based on a sideband rule https://devcentral.f5.com/wiki/iRules.HTTP-Super-SIDEBAND-Requestor-Client-Handles-Redirects-Cookies-Chunked-Transfer-APM-Access-etc.ashx
- when ACCESS_POLICY_AGENT_EVENT {
- #===iRule Source===
- #(Put "set SB_uri "http://foohost/barpath" et-cetera here. Vide infra*)
- #
- #== Mark Seecof's HTTP Super SIDEBAND Requestor! =======================
- #
- #This blob of code implements a nifty HTTP client using SIDEBAND. It
- #handles challenges like redirects (even with cookies-- lets you access
- #APM-protected services!), chunked transfer coding, and Basic AuthN.
- #
- #You supply (*before entering):
- # $SB_uri = absolute URI of target, plus optionally...
- # $SB_virt = (optional) name of target virtual server--if set, used
- # in lieu of host/port in $SB_uri (also see $SB_redir below)
- # If you set $SB_virt then host in $SB_uri can be anything (xxx), but if
- # you do not set $SB_virt you must either put IP (to use port 80) or
- # IP:port in host part of $SB_uri, or ensure your LTM's DNS setup is
- # valid so [RESOLV::lookup] will work.
- #
- #You should supply as well:
- # $SB_tag = a short name for your target just for local logging
- #
- #Optionally, you may also supply:
- # $SB_key = a short ID for this particular request in the local log,
- # such as [ACCESS::session sid]. Default is [IP::client_addr]
- #
- # $SB_method = [GET|POST|...], default is GET
- # $SB_body = data to send (e.g., when method is POST**). Default empty.
- # $SB_headers = a list of alternating HTTP header names and values, like
- # {Accept text/* Accept-Language fr-CA}. Any header set
- # in $SB_headers replaces corresponding default. Default
- # headers: {Accept */* Accept-Language en-US User-Agent
- # {Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1)}
- # Accept-Encoding identity}
- # $SB_cookies = list of alternating cookie names and values. Dfl none.
- # $SB_userid and $SB_passwd for Basic Authentication. Default none.
- # $SB_redir = [0|1|2|3] where 0=ignore redirects; 1=follow redirects and
- # retry POST/PUT after redir out-and-back (so you can POST to
- # an APM-protected resource), but if $SB_virt is set send all
- # requests to it; 2=like 1 except do not retry POST; 3=like
- # 2 but follow redirects away from $SB_virt. Default is 1.
- # $SB_debug = [0|1] where 1 means log debug info. Default is 0.
- #
- #Then we do the rest! Afterward, look at $SB_status, $SB_body,
- #$SB_headers, and $SB_cookies for reply from remote server. ($SB_status
- #"555" indicates some local problem--check LTM log.)
- #
- #**If you POST don't forget your Content-Type header, something like:
- # set SB_headers [list Content-Type "application/json; charset=utf-8"]
- #
- #Copyright &copr; 2013 Mark Seecof. Permission to modify and distribute
- #granted gratis on condition that you include this notice with each copy.
- #
- #
- set uid [ACCESS::session data get session.user.sessionid]
- set SB_uri "http://10.0.1.105/data/data.php?uid=$uid"
- set SB_virt http-aaa-vs
- set SB_tag "GetExtUserInfo"
- set SB_key $uid
- set SB_method "GET"
- set SB_debug 0
- if {![info exists SB_virt]} { set SB_virt "" }
- if {![info exists SB_tag]} { set SB_tag "HTTP Requestor" }
- if {![info exists SB_key]} { set SB_key "client [IP::client_addr]" }
- if {![info exists SB_method]} { set SB_method "GET" }
- if {![info exists SB_body]} { set SB_body "" }
- if {![info exists SB_headers]} { set SB_headers [list] }
- if {![info exists SB_cookies]} { set SB_cookies [list] }
- if {![info exists SB_userid]} { set SB_userid "" }
- if {![info exists SB_passwd]} { set SB_passwd "" }
- if {![info exists SB_redir]} { set SB_redir 1 }
- if {![info exists SB_debug]} { set SB_debug 0 }
- set _SBdone [list Host Content-Length Accept Accept-Language User-Agent Accept-Encoding]
- set SB_status 555
- set _SBprot [set _SBhost [set _SBport ""]]
- set _SBpath /
- set _SBtry -2
- if {![info exists SB_uri]} {
- log local0.err "${SB_tag} ${SB_key}: you must supply \$SB_uri!"
- } elseif {![regexp -nocase {^(https?)://([^:/]+)(:[0-9]+)*(/[^\x23]*)} $SB_uri _SBjunk _SBprot _SBhost _SBport _SBpath]} {
- log local0.err "${SB_tag} ${SB_key}: malformed \$SB_uri: ${SB_uri}"
- } elseif {[string equal -nocase $_SBprot "https"] && ($SB_virt eq "")} {
- log local0.err "${SB_tag} ${SB_key}: SIDEBAND to TLS needs helper virtual-server"
- } else {
- #max redirects we will follow
- set _SBtry 5
- }
- set _SBdest ""
- set _SBwho [set _SBwhere [set _SBhow [set _SBwhat ""]]]
- while {[incr _SBtry -1] >= 0} {
- #what will we connect to?
- if {$_SBdest eq ""} {
- if {$SB_virt ne ""} {
- set _SBdest $SB_virt
- } else {
- if {[catch {IP::addr $_SBhost mask 255.255.255.255}]} {
- if {[set _SBaddr [RESOLV::lookup -a $_SBhost]] eq ""} {
- log local0.err "${SB_tag} ${SB_key}: cannot resolve ${_SBhost} to IP address"
- break
- }
- } else {
- set _SBaddr $_SBhost
- }
- if {$_SBport eq ""} {set _SBport ":80"}
- set _SBdest "${_SBaddr}${_SBport}"
- }
- }
- #retry POST after redirect(s)?
- if {($_SBwhat ne "") && [string equal -nocase $_SBdest $_SBwho] &&
- ($_SBpath eq $_SBwhere)} {
- set SB_method $_SBhow
- set SB_body $_SBwhat
- set _SBwhat ""
- }
- #assemble HTTP request
- set _SBreq "${SB_method} ${_SBpath} HTTP/1.1\r\n"
- set _SBv $_SBhost
- if {[set _SBx [lsearch -regexp $SB_headers "(?i)Host"]] > -1} {
- set _SBv [lindex $SB_headers [incr _SBx]]
- }
- set _SBreq "${_SBreq}Host: ${_SBv}\r\n"
- set _SBv [string length $SB_body]
- if {[set _SBx [lsearch -regexp $SB_headers "(?i)Content-Length"]] > -1} {
- set _SBv [lindex $SB_headers [incr _SBx]]
- }
- set _SBreq "${_SBreq}Content-Length: ${_SBv}\r\n"
- set _SBv "*/*"
- if {[set _SBx [lsearch -regexp $SB_headers "(?i)Accept"]] > -1} {
- set _SBv [lindex $SB_headers [incr _SBx]]
- }
- set _SBreq "${_SBreq}Accept: ${_SBv}\r\n"
- set _SBv "en-US"
- if {[set _SBx [lsearch -regexp $SB_headers "(?i)Accept-Language"]] > -1} {
- set _SBv [lindex $SB_headers [incr _SBx]]
- }
- set _SBreq "${_SBreq}Accept-Language: ${_SBv}\r\n"
- set _SBv "Mozilla/4.0 (compatible;)"
- if {[set _SBx [lsearch -regexp $SB_headers "(?i)User-Agent"]] > -1} {
- set _SBv [lindex $SB_headers [incr _SBx]]
- }
- set _SBreq "${_SBreq}User-Agent: ${_SBv}\r\n"
- set _SBv "identity"
- if {[set _SBx [lsearch -regexp $SB_headers "(?i)Accept-Encoding"]] > -1} {
- set _SBv [lindex $SB_headers [incr _SBx]]
- }
- set _SBreq "${_SBreq}Accept-Encoding: ${_SBv}\r\n"
- if {($SB_userid ne "") && ($SB_passwd ne "")} {
- set _SBreq "${_SBreq}Authorization: Basic [b64encode "${SB_userid}:${SB_passwd}"]\r\n"
- }
- if {[set _SBe [llength $SB_headers]] > 1} {
- set _SBx 0
- while {$_SBx < $_SBe} {
- set _SBn [lindex $SB_headers $_SBx]
- set _SBv [lindex $SB_headers [incr _SBx]]
- if {[lsearch -regexp $_SBdone "(?i)${_SBn}"] == -1} {
- set _SBreq "${_SBreq}${_SBn}: ${_SBv}\r\n"
- }
- incr _SBx
- }
- }
- if {[set _SBe [llength $SB_cookies]] > 1} {
- set _SBx 0
- while {$_SBx < $_SBe} {
- set _SBreq "${_SBreq}Cookie: [lindex $SB_cookies $_SBx]=[lindex $SB_cookies [incr _SBx]]\r\n"
- incr _SBx
- }
- }
- set _SBreq "${_SBreq}\r\n${SB_body}"
- if {$SB_debug} {
- log local0.info "${SB_tag} ${SB_key}: ${_SBdest} request=${_SBreq}"
- log local0.info "${SB_tag} ${SB_key}: attempt connect to ${_SBdest}, 5 sec timeout"
- }
- set _SBstat "error"
- if {[catch {connect -timeout 5000 -idle 15 -status _SBstat $_SBdest} _SBconn] ||
- ($_SBconn eq "")} {
- log local0.err "connect to ${SB_tag} ${_SBdest} for ${SB_key} fails: ${_SBstat}"
- break
- }
- if {$SB_debug} {
- log local0.info "${SB_tag} ${SB_key}: connected to ${_SBdest}, sending [string length $_SBreq]"
- }
- set _SBsent [send -timeout 5000 -status _SBstat $_SBconn $_SBreq]
- if {($_SBstat ne "sent") || ($_SBsent < [string length $_SBreq])} {
- log local0.err "send ${SB_tag} req for ${SB_key} to ${_SBdest} fails: ${_SBstat}"
- break
- }
- #poll every 50ms for server reply, max delay 10 sec
- set _SBctr 200
- set _SBstat [set _SBhdrs [set _SBbody ""]]
- while {($_SBctr > 0) && ($_SBstat ne "failed")} {
- set _SBreply [recv -peek -timeout 50 -status _SBstat $_SBconn]
- set _SBhbrk [string first "\r\n\r\n" $_SBreply]
- if {$_SBhbrk > 0} {
- #encode-decode shuffle below required until f5 fixes regexp copy-on-write corruption bug
- set _SBhdrs [URI::decode [URI::encode [recv [expr {$_SBhbrk + 4}] $_SBconn]]]
- if {$SB_debug} {
- log local0.info "${SB_tag}: reply hdrs [string map [list "\r\n" ", "] $_SBhdrs]"
- }
- if {([regexp -nocase {Content-Length:[ \t]*([0-9]+)} $_SBhdrs _SBjunk _SBclen]) &&
- ($_SBclen > 0)} {
- set _SBbody [recv -timeout [expr {$_SBctr * 50}] -status _SBstat $_SBclen $_SBconn]
- if {[string length $_SBbody] < $_SBclen} {
- log local0.err "recv ${_SBclen} bytes from ${SB_tag} ${_SBdest} for ${SB_key} fails: ${_SBstat}"
- set _SBhdrs ""
- set _SBbody ""
- }
- } elseif {[regexp -nocase {Transfer-Encoding:[ \t]*Chunked} $_SBhdrs]} {
- while {($_SBctr > 0) && ($_SBstat ne "failed")} {
- set _SBchunk [recv -peek -timeout 50 -status _SBstat $_SBconn]
- if {[regexp -nocase {^([0-9a-f]+)[^\r]*[\r][\n]} $_SBchunk _SBjunk _SBhex] &&
- ($_SBhex ne "0")} {
- recv [string length $_SBjunk] $_SBconn
- scan $_SBhex "%x" _SBchunksz
- if {$_SBchunksz > 0} {
- set _SBchunk [recv -timeout [expr {$_SBctr * 50}] -status _SBstat $_SBchunksz $_SBconn]
- if {[string length $_SBchunk] == $_SBchunksz} {
- set _SBbody "${_SBbody}${_SBchunk}"
- } else {
- log local0.err "recv chunk ${_SBhex} from ${SB_tag} ${_SBdest} for ${SB_key} fails: ${_SBstat}"
- set _SBhdrs ""
- set _SBbody ""
- set _SBctr 0
- }
- }
- } elseif {($_SBhex eq 0) || ($_SBchunk starts_with "\r\n")} {
- #ignore any trailing headers (Yeah? So sue me!)
- close $_SBconn
- set _SBctr 0
- } else {
- incr _SBctr -1
- }
- }
- }
- #here we have reply headers, plus body if any, so stop polling
- set _SBctr 0
- }
- incr _SBctr -1
- }
- if {$_SBhdrs eq ""} {
- log local0.err "recv reply from ${SB_tag} ${_SBdest} for ${SB_key} fails: ${_SBstat}"
- break
- }
- #extract HTTP status code to $_SBrslt
- regexp {HTTP[^ ]+ ([0-9]+)} $_SBhdrs _SBjunk _SBrslt
- #process reply headers.
- #This code will not handle headers broken across lines--
- #those are legal but rare enough to disregard here
- set _SBckys [list]
- set _SBrlist [regexp -line -all -inline -- {^([^:]+):[ \t]*([^\r]*)} $_SBhdrs]
- set _SBhlist [list]
- set _SBe [llength $_SBrlist]
- set _SBx 1
- while {$_SBx < $_SBe} {
- set _SBn [lindex $_SBrlist $_SBx]
- set _SBv [lindex $_SBrlist [incr _SBx]]
- if {[string equal -nocase $_SBn "Location"] && $SB_redir &&
- [regexp {^30[1237]$} $_SBrslt]} {
- if {($_SBrslt <= 302) && ($SB_redir == 1) && ($_SBwhat eq "") &&
- ([string first $SB_method "GET HEAD"] < 0)} {
- set _SBwho $_SBdest
- set _SBwhere $_SBpath
- set _SBhow $SB_method
- set _SBwhat $SB_body
- }
- if {$_SBrslt <= 303} {
- set SB_method "GET"
- set SB_body ""
- }
- set _SBp ":80"
- if {$_SBv starts_with "/"} {
- #relative path so contact same host
- set _SBpath $_SBv
- } elseif {[regexp -nocase {^(https?)://([^:/]+)(:[0-9]+)*(/[^\x23]*)} $_SBv _SBjunk _SBt _SBh _SBp _SBa]} {
- set _SBpath $_SBa
- if {![string equal -nocase $_SBhost $_SBh] || ($_SBport ne $_SBp)} {
- set _SBdest ""
- if {$SB_redir == 3} {
- set SB_virt ""
- if {[string equal -nocase $_SBt "https"]} {
- log local0.err "${SB_tag} ${SB_key}: redir to ${_SBv} TLS requires helper virtual-server"
- break
- }
- }
- set _SBprot $_SBt
- }
- } else {
- #oops, Location not HTTP URI
- log local0.err "${SB_tag}: cannot follow redirect from ${_SBdest} to ${_SBv} for ${SB_key}"
- break
- }
- }
- if {[string equal -nocase $_SBn "Set-Cookie"]} {
- set _SBcky [set _SBval ""]
- regexp {^([^=]+)=([^;]*);} $_SBv _SBjunk _SBcky _SBval
- if {$_SBcky ne ""} {
- lappend _SBckys $_SBcky $_SBval
- }
- }
- if {[string equal -nocase $_SBn "WWW-Authenticate"] && ($_SBrslt eq "401") &&
- [regexp -nocase {Basic } $_SBv] && ($SB_userid ne "") && ($SB_passwd ne "")} {
- #We already sent "Authorization: Basic xxx" header but some servers
- #(f5 APM) will 401 first request and only look for creds on second
- #request (and change cookies, too)
- set $_SBrslt "455"
- }
- lappend _SBhlist $_SBn $_SBv
- incr _SBx 2
- }
- #update cookie list (we ignore lifetimes and elide duplicate-name cookies)
- set _SBtmp [list]
- if {[llength $SB_cookies] > 1} {
- foreach {_SBcky _SBval} $SB_cookies {
- #cookie names ARE case-sensitive
- if {([set _SBx [lsearch $_SBckys $_SBcky]] < 0) || ($_SBx & 1)} {
- lappend _SBtmp $_SBcky $_SBval
- }
- }
- }
- set SB_cookies [concat $_SBtmp $_SBckys]
- if {$_SBrslt eq "455"} {
- #retry Basic authz
- continue
- }
- #do we follow a redirect, or are we finished?
- if {!$SB_redir || ![regexp {^30[1237]} $_SBrslt] || ($_SBtry < 1)} {
- set SB_status $_SBrslt
- set SB_body [URI::decode [URI::encode $_SBbody]]
- set SB_headers $_SBhlist
- #SB_cookies already set
- break
- }
- }
- if {$_SBtry == -1} {
- log local0.err "${SB_tag} ${SB_key}: too many redirects"
- }
- #At this point, you may inspect $SB_status, $SB_body, $SB_headers,
- #and $SB_cookies for results of your request.
- #
- #== End of Mark Seecof's HTTP Super SIDEBAND Requestor! ================
- set _SBrlist [regexp -line -all -inline -- {^([^:]+):[ \t]*([^\r]*)} $SB_body]
- set _SBx 1
- while {$_SBx < $_SBe} {
- set _SBn [lindex $_SBrlist $_SBx]
- set _SBv [lindex $_SBrlist [incr _SBx]]
- if {[string equal -nocase $_SBn "X-user-name"] } {
- ACCESS::session data set session.user.xname "$_SBv"
- }
- elseif {[string equal -nocase $_SBn "X-user-id"]} {
- ACCESS::session data set session.user.xuid "$_SBv"
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement