Forum Discussion
Scott_Larson
Nimbostratus
Jul 03, 2007Block until NAME::lookup returns?
For my iRule, I need to perform a reverse DNS lookup, and determine if a client IP is from a domain ending in .mil or .gov. This determines whether or not they need to authenticate. The authentication works fine; even the NAME::lookup works fine (thru much trial and tribulation due to lacking documentation!!!) The problem is that since NAME::lookup is an asynchronous function, I need to block (wait) all HTTP requests until the lookup returns and I can determine whether the client is or isn't from .mil/.gov. The lookup is quick - nearly immediate - but it's not synchronous.
How does one block until the NAME_RESOLVED event fires?
Or, is there a synchronous version of the NAME::lookup function? or a TCL version of the function that might work?
Or does anyone have any other ideas?
11 Replies
- Deb_Allen_18Historic F5 AccountYou'll want to run HTTP::collect before calling the NAME::lookup command, then call HTTP::release inside of the NAME_RESOLVED event.
I've just updated the wiki page with syntax & some examples.
/deb - Scott_Larson
Nimbostratus
Unfortunately, that doesn't seem to do the trick. After my call to HTTP::collect, execution of the event handler continues on, and it "slips" into the rest of the iRule, which forces the user to authenticate if they're not from a .gov or .mil address. However, if it worked as planned, it'd block until the name resolved to blah.mil, then it'd know it should skip authentication.
Here's a chronologically accurate copy from my log file, with notes.
In the code, I do this:
if { $::DEBUG } { log local0. "before lookup..." }
HTTP::collect
NAME::lookup $::clientIP
if { $::DEBUG } { log local0. "after lookup..." }
and thus there are log messages in NAME_RESOLVED event that should come inbetween "before lookup..." and "after lookup...", but you'll see below that "before lookup..." and "after lookup..." are right next to each other in the log:
**** original request is sent:
: Request for home.dcma.mil/
: Checking to see if this request is coming form a DoD client address - performing reverse-lookup on xxx.xxx.xxx.xxx (xx'ed out for security).
: before lookup...
**** the following code should execute AFTER the NAME_RESOLVED event releases the HTTP collect, but it's executing immediately (it seems HTTP collect doesn't do anything)!!! ****
: after lookup...
: User not recognized as being DCMA, DOD, or AUTHED...setting to EXT and mustauth to 1.
*** Next, here's the NAME_RESOLVED event firing - too late!!! ****
: Client IP (xxx.xxx.xxx.xxx) resolved to hostname (www.test.mil)
: This is a DoD client (.mil or .gov) address.
**** Unfortunately, the authentication prompt is going to be shown next ****
: Failed authentication (AUTH::status=2)
**** However, subsequent requests all are recognized as DOD (thru use of cookies), but it's just that first one that won't wait quite long enough for the NAME_RESOLVED event to fire... ***
: Request for home.dcma.mil/templates/dcmastyle.css
: clientType: DOD
: URI is not protected for DoD users, no need to authenticate. - Deb_Allen_18Historic F5 AccountNice job logging and debugging. This is exactly the way you should be approaching it.
I think the problem is the logical flow. This isn't well-documented but it seems that although HTTP::collect holds the data, it doesn't stop the processing of the current event (ie. HTTP_REQUEST), which continues to process the remaining commands while NAME::lookup (?? runs in the background? Not sure how they interact.)
Since in most cases NAME::lookup implies a pause in the action, and the remainder of the logic depends on it (as yours does), I'm pretty sure you just need to jump then from NAME::lookup to NAME_RESOLVED event and continue the action there.
So basically, I'd take all your other HTTP_REQUEST logic following the NAME::lookup command that depend on the results of the lookup, and move it down into the NAME_RESOLVED event before HTTP::release command.
HTH
/deb - Scott_Larson
Nimbostratus
Ah yes, I thought of that too, and I tried that, but unfortunately that doesn't do it either, as the subsequent logic contains directives (HTTP and AUTH) which are not valid within the context of the NAME_RESOLVED event. Ghar!!!!!
Is there any way to "wait"? Sleep? pause the thread's execution for a second? I mean, I'm almost at the point where I'll put it in a busy wait loop until a global variable gets set in the NAME_RESOLVED event.
Why doesn't the LTM simply support a synchronous lookup? Can that be submitted as an enhancement request?
Any other ideas? - Scott_Larson
Nimbostratus
btw I forgot to add, thanks for your help, all your ideas are good...I'm hopeful we can keep thinking of other innovative ways to solve this problem. - Deb_Allen_18Historic F5 AccountWell, there are ways to implement a wait loop, but don't -- it will lock up the thread and cause a watchdog reboot and failover if the name lookup takes too long.
Instead, I'd use "TCP::notify request" command to trigger the USER_REQUEST event and carry on there. You'd need to save off to local vars all the info required for auth, then collect & call NAME::lookup in HTTP_REQUEST, call TCP::notify from NAME_RESOLVED, and continue in USER_RESPONSE event with the auth commands using the vars saved earlier. (I see you are already setting a "mustauth" flag", so where you are evaluating that flag might be the place to use TCP::notify.)
I mentioned local variables specifically because I noticed you are using a global variable for the client IP ($::clientIP). Connection-specific data should NOT be saved in global variables, or you are sharing data across connections, in this case you could be looking up the wrong client IP if another connection has populated that variable since this connection first did. If, for example, your mustauth flag were set as a global, the flag value could change several times during the current connection as other connections are processed in parallel, and again, you might be acting on the wrong data to make your auth decision.
It's also worth pointing out how easy it is to spoof the name for a reverse resolution, and since the intent here is to bypass auth based on such a perilous assumption as the accuracy of a PTR record, you might want to consider re-evaluating this approach security-wise.
HTH
/deb - Deb_Allen_18Historic F5 AccountI like Spark's idea re: lookup in CLIENT_ACCEPTED better than mine re: using TCP::notify -- it's simpler, and more efficient, then you can use the result anywhere in the iRule at your leisure.
/d - spark_86682Historic F5 AccountActually, this version works better:
when CLIENT_ACCEPTED { set clientIP [IP::client_addr] set resolved 0 TCP::collect NAME::lookup $clientIP } when NAME_RESOLVED { set client_name "[NAME::response]" set resolved 1 TCP::release } when CLIENT_DATA { if { $resolved == 0 } { TCP::collect } else { TCP::release } }
It solves a sort-of race condition between getting data and the name resolving. - Scott_Larson
Nimbostratus
Spark & Deb, thanks so much for your dilligent assistance and innovative thinking. I have tried Spark's idea and it does seem to be working at first blush. THANKS SPARK! I will continue testing this tomorrow and will post again when I have some results to share.
-Scott - Scott_Larson
Nimbostratus
Our testing confirms that this solution works like a champ. Thanks again, Deb and Spark!!
Help guide the future of your DevCentral Community!
What tools do you use to collaborate? (1min - anonymous)Recent Discussions
Related Content
DevCentral Quicklinks
* Getting Started on DevCentral
* Community Guidelines
* Community Terms of Use / EULA
* Community Ranking Explained
* Community Resources
* Contact the DevCentral Team
* Update MFA on account.f5.com
Discover DevCentral Connects