LTM
191 TopicsBIG-IP Report
Problem this snippet solves: Overview This is a script which will generate a report of the BIG-IP LTM configuration on all your load balancers making it easy to find information and get a comprehensive overview of virtual servers and pools connected to them. This information is used to relay information to NOC and developers to give them insight in where things are located and to be able to plan patching and deploys. I also use it myself as a quick way get information or gather data used as a foundation for RFC's, ie get a list of all external virtual servers without compression profiles. The script has been running on 13 pairs of load balancers, indexing over 1200 virtual servers for several years now and the report is widely used across the company and by many companies and governments across the world. It's easy to setup and use and only requires auditor (read-only) permissions on your devices. Demo/Preview Interactive demo http://loadbalancing.se/bigipreportdemo/ Screen shots The main report: The device overview: Certificate details: How to use this snippet: Installation instructions BigipReport REST This is the only branch we're updating since middle of 2020 and it supports 12.x and upwards (maybe even 11.6). Downloads: https://loadbalancing.se/downloads/bigipreport-v5.7.13.zip Documentation, installation instructions and troubleshooting:https://loadbalancing.se/bigipreport-rest/ Docker support https://loadbalancing.se/2021/01/05/running-bigipreport-on-docker/ Kubernetes support https://loadbalancing.se/2021/04/16/bigipreport-on-kubernetes/ BIG-IP Report (Legacy) Older version of the report that only runs on Windows and is depending on a Powershell plugin originally written by Joe Pruitt (F5) BIG-IP Report (only download this if you have v10 devices): https://loadbalancing.se/downloads/bigipreport-5.4.0-beta.zip iControl Snapin https://loadbalancing.se/downloads/f5-icontrol.zip Documentation and Installation Instructions https://loadbalancing.se/bigip-report/ Upgrade instructions Protect the report using APM and active directory Written by DevCentral member Shann_P: https://loadbalancing.se/2018/04/08/protecting-bigip-report-behind-an-apm-by-shannon-poole/ Got issues/problems/feedback? Still have issues? Drop a comment below. We usually reply quite fast. Any bugs found, issues detected or ideas contributed makes the report better for everyone, so it's always appreciated. --- Join us on Discord: https://discord.gg/7JJvPMYahA Code : BigIP Report Tested this on version: 12, 13, 14, 15, 1613KViews20likes96CommentsProxy Protocol v2 Initiator
Problem this snippet solves: Proxy Protocol v1 related articles have already been posted on DevCentral, but there is no v2 support iRule code available. A customer wanted to support Proxy Protocol v2, so I wrote an iRule code for supporting v2. Proxy protocol for the BIG-IP (f5.com) How to use this snippet: Back-end server must handle Proxy header prior data exchange. Code : when CLIENT_ACCEPTED { # DEBUG On/Off set DEBUG 0 set v2_proxy_header "0d0a0d0a000d0a515549540a" # v2 version and command : 0x21 - version 2 & PROXY command set v2_ver_command "21" # v2 address family and transport protocol : 0x11 - AF_INET (IPv4) & TCP protocol set v2_af_tp "11" # v2 Address Size : 0x000C - 12 bytes for IPv4 + TCP set v2_address_length "000c" # Get TCP port - 2 byte hexadecimal format set src_port [format "%04x" [TCP::client_port]] set dst_port [format "%04x" [TCP::local_port]] # Get Src Address and convert to 4 byte hexadecimal format foreach val [split [IP::client_addr] "."] { append src_addr [format "%02x" $val] } # Get Dst Address and convert to 4 byte hexadecimal format foreach val [split [IP::local_addr] "."] { append dst_addr [format "%02x" $val] } # Build proxy v2 data set proxy_data [binary format H* "${v2_proxy_header}${v2_ver_command}${v2_af_tp}${v2_address_length}${src_addr}${dst_addr}${src_port}${dst_port}"] if { $DEBUG } { binary scan $proxy_data H* proxy_dump log local0. "[IP::client_addr]:[TCP::client_port]_[IP::local_addr]:[TCP::local_port] - proxy_data dump : $proxy_dump" } } when SERVER_CONNECTED { TCP::respond $proxy_data }245Views2likes0CommentsPowerShell module for the F5 LTM REST API
Problem this snippet solves: To report an issue with the F5-LTM or F5-BIGIP modules, please use the Issues sections of the GitHub repos (here and here) instead of commenting here. Thanks! This PowerShell module uses the iControlREST API to manipulate and query pools, pool members, virtual servers, and iRules. It aims to support version 11.5.1 and higher, and to conform to the schedule for technical support of versions, though this may eventually prove to become difficult. The module currently includes some functionality that, strictly speaking, is outside the scope of the LTM module. Hence, there is an active effort to wrap this LTM module into a larger BIG-IP module, and relocate that functionality elsewhere within that parent module, as well as expand the scope of functionality to include BIG-IP DNS (formerly GTM) and possibly other areas. Both the LTM module and the parent BIG-IP module are projects on github. Please use these projects to report any issues you discover. Thanks! The module contains the following functions. Add-iRuleToVirtualServer Add-iRuleToVirtualServer Add-PoolMember Add-PoolMonitor Disable-PoolMember Disable-VirtualServer Enable-PoolMember Enable-VirtualServer Get-CurrentConnectionCount (deprecated; use Get-PoolMemberStats | Select-Object -ExpandProperty 'serverside.curConns') Get-F5Session (will be deprecated in future versions. use New-F5Session) Get-F5Status Get-HealthMonitor Get-HealthMonitorType Get-iRule Get-iRuleCollection (deprecated; use Get-iRule) Get-Node Get-BIGIPPartition Get-Pool Get-PoolList (deprecated; use Get-Pool) Get-PoolMember Get-PoolMemberCollection (deprecated; use Get-PoolMember) Get-PoolMemberCollectionStatus Get-PoolMemberDescription (deprecated; use Get-PoolMember) Get-PoolMemberIP (deprecated; use Get-PoolMember) Get-PoolMembers (deprecated; use Get-PoolMember) Get-PoolMemberStats Get-PoolMemberStatus (deprecated; use Get-PoolMember) Get-PoolMonitor Get-PoolsForMember Get-StatusShape Get-VirtualServer Get-VirtualServeriRuleCollection (deprecated; use Get-VirtualServer | Where rules | Select -ExpandProperty rules) Get-VirtualServerList (deprecated; use Get-VirtualServer) Invoke-RestMethodOverride New-F5Session New-HealthMonitor New-Node New-Pool New-VirtualServer Remove-HealthMonitor Remove-iRule Remove-iRuleFromVirtualServer Remove-Pool Remove-PoolMember Remove-PoolMonitor Remove-ProfileRamCache Remove-Node Remove-VirtualServer Set-iRule Set-PoolLoadBalancingMode (deprecated; use Set-Pool) Set-PoolMemberDescription Set-Pool Set-VirtualServer Sync-DeviceToGroup Test-F5Session Test-Functionality Test-HealthMonitor Test-Node Test-Pool Test-VirtualServer How to use this snippet: To use the module, click 'Download Zip', extract the files, and place them in a folder named F5-LTM beneath your PowerShell modules folder. By default, this is %USERPROFILE%\Documents\WindowsPowerShell\Modules. The WindowsPowerShell and Modules folders may need to be created. You will most likely need to unblock the files after extracting them. Use the Unblock-File PS cmdlet to accomplish this. The Validation.cs class file (based on code posted by Brian Scholer) allows for using the REST API with LTM devices with self-signed SSL certificates. Nearly all of the functions require an F5 session object as a parameter, which contains the base URL for the F5 LTM and a credential object for a user with privileges to manipulate the F5 LTM via the REST API. Use the New-F5session function to create this object. This function expects the following parameters: The name or IP address of the F5 LTM device A credential object for a user with rights to use the REST API An optional TokenLifespan value for extending the life of the authentication token past the default 20 minutes You can create a credential object using Get-Credential and entering the username and password at the prompts, or programmatically like this: $secpasswd = ConvertTo-SecureString "PlainTextPassword" -AsPlainText -Force $mycreds = New-Object System.Management.Automation.PSCredential "username", $secpasswd Thanks to Kotesh Bandhamravuri and his blog entry for this snippet. There is a function called Test-Functionality that takes an F5Session object, a new pool name, a new virtual server, an IP address for the virtual server, and a computer name as a pool member, and validates nearly all the functions in the module. I've also contributed this code sample for how to gather some basic info about your LTM with this PS module. The module has been tested on: 11.5.1 Build 8.0.175 Hotfix 8 and later 11.6.0 Build 5.0.429 Hotfix 4 and later 12.0 / 12.1 13.0 Code : https://github.com/joel74/POSH-LTM-Rest Tested this on version: 11.519KViews2likes150CommentsFQDN nodes in non-default route domains
Problem this snippet solves: Currently there is no support for FQDN nodes in non-default route domains as per Article. With recent increase in cloud deployment, most of the time there is a requirement from F5 to load balance to servers or ELB in the cloud which has FQDN names as those are having dyanamic IP addresses. If you are using route domains in your BIGIP environment then this becomes a challenge. Below iRule script can be used in those scenerios to use F5 to send traffic to FQDN nodes in non-default route domains. How to use this snippet: If FQDN needs to be resolved by your internal DNS, create performance layer 4 VIP (dns_53) load balancing your DNS servers. DNS server can be used directly in the iRule itself, but it would be better to use a VIP to have redundancy. Create iRule and apply to the VIP Code : when CLIENT_ACCEPTED { set host [RESOLV::lookup @dns_53 "server.example.com"] set ip [getfield $host " " 1] node [lindex $ip 0]%<Rd> 443 } Tested this on version: 14.11.4KViews1like3CommentsDNS Query Name Parsing iRule
Problem this snippet solves: This iRule will extract the DNS Query Name in the absence of a DNS profile being applied to a Virtual Server. How to use this snippet: # This is a shameless rip from an old Devcentral post DNS Hostname Parsing iRule that, to the best of my knowledge, never made it to a Code Share. To use this code, simply apply this to a UDP Virtual Server that processes DNS traffic. (No DNS Profile necessary). Code : when FLOW_INIT { #extract QNAME from QUESTION header #${i} is a sanity check so this logic won't spin on invalid QNAMEs set i 0 #initialize our position in the QNAME parsing and the text QNAME set offset 12 set length 1 set endlength 1 set name "" #/extract QNAME from QUESTION header while {${length} > 0 && ${i} < 10} { #length contains the first part length binary scan [string range [DATAGRAM::udp payload] ${offset} ${offset}]] c foo #make the length an unsigned integer set length [expr {${foo} & 0xff}] if {${length} > 0} { #grab a part and put it in our text QNAME section append name [string range [DATAGRAM::udp payload] [expr {${offset} + 1}] [expr {${offset} + ${length}}]] #Watch the DNS QNAME get built during the loop. Remove the following line for production use. log local0.info "BUILDING DNS NAME: [IP::client_addr] queried ${name} offset ${offset} length ${length}" #grab a part and put it in our text QNAME section set offset [expr {${offset} + ${length} +1}] #endlength contains the Last part length binary scan [string range [DATAGRAM::udp payload] ${offset} ${offset}]] c foo #make the length an unsigned integer set endlength [expr {${foo} & 0xff}] if { ${endlength} > 0} { #put a dot between parts like a normal DNS name append name "." } incr i } } #/extract QNAME from QUESTION header #Input the required action here, where "${name}" is the variable that is reviewed for decision making. #Sample action would be a pool statement. The below log statement should be removed for production use. log local0.info "FINAL DNS NAME: [IP::client_addr] queried ${name}" } Tested this on version: 12.1662Views2likes1CommentSFTP file existence monitor
Problem this snippet solves: SFTP file existence monitor How to use this snippet: This monitor definition allows for a monitor to connect to a SFTP server and check for the existence of a file using username/password. Written for a specific implementation where they wouldn't use key pairs, plus it turns out that curl on F5's was compiled with sftp support disabled, so I had to use expect instead. It's based off of the default sample_monitor. Create a monitor definition with 3 variables: $monitor_sftp_USER = Username of SFTP server $monitor_sftp_PASS = Password for $monitor_sftp_USER $monitor_sftp_STRING` = String/Filename to search for I have also written a modified version whereby you can encrypt the password manually using the unit master-key and add that as the password variable, which I can post if wanted. Code : #!/bin/sh # # (c) Copyright 1996-2006, 2010-2013 F5 Networks, Inc. # # This software is confidential and may contain trade secrets that are the # property of F5 Networks, Inc. No part of the software may be disclosed # to other parties without the express written consent of F5 Networks, Inc. # It is against the law to copy the software. No part of the software may # be reproduced, transmitted, or distributed in any form or by any means, # electronic or mechanical, including photocopying, recording, or information # storage and retrieval systems, for any purpose without the express written # permission of F5 Networks, Inc. Our services are only available for legal # users of the program, for instance in the event that we extend our services # by offering the updating of files via the Internet. # # @(#) $Id: //depot/maint/bigip12.1.1/tm_daemon/monitors/sample_monitor#1 $ # # # these arguments supplied automatically for all external pingers: # $1 = IP (::ffff:nnn.nnn.nnn.nnn notation or hostname) # $2 = port (decimal, host byte order) # # The following must all be set as variables in the monitor definition # $monitor_sftp_USER = Username of SFTP server # $monitor_sftp_PASS = Password for $monitor_sftp_USER # $monitor_sftp_STRING` = String/Filename to search for # # $MONITOR_NAME = name of the monitor # # In this sample script, $3 is the regular expression # # Name of the pidfile pidfile="/var/run/$MONITOR_NAME.$1..$2.pid" # Send signal to the process group to kill our former self and any children # as external monitors are run with SIGHUP blocked if [ -f $pidfile ] then kill -9 -`cat $pidfile` > /dev/null 2>&1 fi echo "$$" > $pidfile # Remove the IPv6/IPv4 compatibility prefix node_ip=`echo $1 | sed 's/::ffff://'` # Using expect and sftp to get directory listing from the server. # Search the data received for the expected string. expect -c " spawn sftp -oStrictHostKeyChecking=no -oPort=$2 $monitor_sftp_USER@$node_ip; expect \"password:\"; send $monitor_sftp_PASS\r; expect \"sftp>\"; send \"ls -l\r\"; expect \"sftp>\"; send \"exit\r\" " | grep $monitor_sftp_STRING > /dev/null status=$? if [ $status -eq 0 ] then # Remove the pidfile before the script echoes anything to stdout and is killed by bigd rm -f $pidfile echo "up" fi # Remove the pidfile before the script ends rm -f $pidfile Tested this on version: 12.1630Views0likes1CommentHangman game (Optimized for v11.x)
Problem this snippet solves: This is an iRule implementation of the popular Hangman game. This was originally written by torzillo for v9.x (https://devcentral.f5.com/codeshare/hangman-game). This version has been optimized to use static variables (to prevent CMP Demotion), and utilizes an External Data Group instead of the class' object. For the time being, the External Datagroup is available on Pastebin...It's 21,492 lines, so DevCentral was marking the POST as SPAM. Hangmanwords_external_dg (hosted on http://pastebin.com) How to use this snippet: 1. Create the iRule (Web UI -> Local Traffic -> iRules -> iRule List 2. Create the External Data Group as a Text File on your desktop. * I recommend using Notepad++ to Search and Replace all \r\n (CRLF) with \n (LF) * This is because the LTM Web UI will reject \r\n formatted lines. `</pre> **3.** Import the External Data Group to the LTM <pre>`* Web UI -> System -> File Management -> Data Group File List -> Import Name is arbitrary. File Contents = String Data Group Name = Hangmanwords_external_dg Code : when RULE_INIT { #If $static::hangmandebug is set to 1 then the program will do a lot of logging set static::hangmandebug 1 #This variable will return the number of words in your dictionary. set static::hangmanWordSize [class size Hangmanwords_external_dg] if { $static::hangmandebug} { log local0.debug "$static::hangmanWordSize Words in Dictionary" } #These two variables are commented out because scoring is not yet implemented # #set static::hangmanHighScore 0 #set static::hangmanHighUser "Nobody has won yet." # #This iRule is designed to be done entirely in the HTTP_REQUEST event #The client does GET requests, and the iRule interacts with the user through #cookies and redirects. The path in the URI is used to determine what #action to take. # #The GIFs are base-64 encoded. They could have been placed in /var/class #on the LTM but this would make the code less cut and paste friendly. # #There is an associated external data-group for this iRule. #The basic format of an external data-group is as follows: #"key1" := "value", #"key2" := "value", #"key3" := "value" # #NOTE: There is a comma after every line except the last one #NOTE: The ending character MUST be \n (LF) \r\n (CRLF) Will fail to import. # #IMPORTANT: Create the External Datagroup with the name of Hangmanwords_external_dg #Import the external Data Group to: # WebUI -> System -> File Management -> External Datagroup List # With a type of string #The original Code was written by Tony Torzillo to demonstrate iRule functionality. #The v11.x optimized/compatable version was modified by Jason Adams. #TODO: Implement scoring - thought about using a global associative array #or just continuing to use cookies. Wanted to avoid globals, but if we want #to track the high score for all users we would have to use at least a global #for that. # #Implement cookie encryption - had a lot of cookie debugging to do. Wanted to #implement AES encryption for the cookies so that someone can't cheat. # #Implement input validation - No input validation currently when someone puts in a #userID. This code might be vulnerable to exploits and tampering since a lot of validation #isn't done. There was some attempt to protect against forceful browsing, but it isn't #that robust. # #Implement cookie validation - someone could really mess the code up by putting in some #strange cookie values with something like Fiddler. Also if cookie encryption is implemented #you have to make sure to account for the case where the cookies can't be decrypted. This #can happen if someone loads the iRule into memory in RULE_INIT and overwrites the existing #key. I didn't want to implement cookie encryption without cookie validation. # #This iRule has been tested to work on 11.6.0. It should work for all versions of v11.x, # But I can't guarantee it will work. # #This code should not be used on a production system as it may have security flaws or #problems with continuous redirects. # } when HTTP_REQUEST { if { $static::hangmandebug} { log local0.debug "in HTTP_REQUEST" } #This iRule will do different things based on the path passed in. #Different image names in the path will cause it to display a GIF or PNG #It does this by sending a b64encoded GIF or PNG back with an HTTP response #when that image is referenced - i.e. /hangman.gif could be referenced within #the iRule with #I realize that this could have been put in /var/class to make the code cleaner #but I wanted it to be cut and paste friendly. switch -glob [string tolower [HTTP::path]] { /hangman.gif { HTTP::respond 200 content [b64decode \ "R0lGODlhtgFQAPcAAAAAAIAAAACAAICAAAAAgIAAgACAgICAgMDAwP8AAAD/AP//AAAA//8A/wD/\ /////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMwAAZgAAmQAAzAAA/wAzAAAzMwAzZgAzmQAzzAAz/wBm\ AABmMwBmZgBmmQBmzABm/wCZAACZMwCZZgCZmQCZzACZ/wDMAADMMwDMZgDMmQDMzADM/wD/AAD/\ MwD/ZgD/mQD/zAD//zMAADMAMzMAZjMAmTMAzDMA/zMzADMzMzMzZjMzmTMzzDMz/zNmADNmMzNm\ ZjNmmTNmzDNm/zOZADOZMzOZZjOZmTOZzDOZ/zPMADPMMzPMZjPMmTPMzDPM/zP/ADP/MzP/ZjP/\ mTP/zDP//2YAAGYAM2YAZmYAmWYAzGYA/2YzAGYzM2YzZmYzmWYzzGYz/2ZmAGZmM2ZmZmZmmWZm\ zGZm/2aZAGaZM2aZZmaZmWaZzGaZ/2bMAGbMM2bMZmbMmWbMzGbM/2b/AGb/M2b/Zmb/mWb/zGb/\ /5kAAJkAM5kAZpkAmZkAzJkA/5kzAJkzM5kzZpkzmZkzzJkz/5lmAJlmM5lmZplmmZlmzJlm/5mZ\ AJmZM5mZZpmZmZmZzJmZ/5nMAJnMM5nMZpnMmZnMzJnM/5n/AJn/M5n/Zpn/mZn/zJn//8wAAMwA\ M8wAZswAmcwAzMwA/8wzAMwzM8wzZswzmcwzzMwz/8xmAMxmM8xmZsxmmcxmzMxm/8yZAMyZM8yZ\ ZsyZmcyZzMyZ/8zMAMzMM8zMZszMmczMzMzM/8z/AMz/M8z/Zsz/mcz/zMz///8AAP8AM/8AZv8A\ mf8AzP8A//8zAP8zM/8zZv8zmf8zzP8z//9mAP9mM/9mZv9mmf9mzP9m//+ZAP+ZM/+ZZv+Zmf+Z\ zP+Z///MAP/MM//MZv/Mmf/MzP/M////AP//M///Zv//mf//zP///ywAAAAAtgFQAAAI/wAfCBxI\ sKDBgwgTKlzIsKHDhxAjSpxIsaLFixgzatzIsaPHjyBDihxJsqTJkyhTqlzJsqXLlzBjypxJs6bN\ mzhz6tzJs6fPn0CDCh1KtKjRo0iTKl3KtKnTp1CjSp1KtarVq1izat3KtavXr2DDih1LtqzZs2jT\ ql3Ltq3bt3DjypUIoC4AhXbvJszLke/cnn732v27Mq9ehqnsUhNcdzFCu6n6Dia8M/DjyZRPGnaY\ uDFCankdGwRdN/JGy5ltoja4OnXIzQ1JAxBdUDYA06MVS67rGmdrgr97d4TNUDbugp1LI0xOG2Nw\ 4S2fP5AO3Tl1gbL9LM97nGDy4Zirx/+Ufl08ReIMfxvWfrD8Q/fmR5IPH3/jFLtT3tMfaPgwQdvg\ 8VbfS/MJOOBGyXWnkB+6sWZYcw8YF6B/B6pUIIUVWsRgXew1xFxu6A0k4W4YpgbfRxdmqFGCD7FY\ W38KygbhRSe2VeOEC92ookEbAtBhcZAZlFyD3hGpkY5qIXnkfgUpuaNALsZm149Q9kflAz3OaJGT\ Z3FJI5PAgfnkQz1eidd+/fnooIGniUmYl1u6OZ2cYy4UZUOrTdnji3TS1WdccFaUYp0UlQnRh2He\ hih/yuFYXaDnyQnpgXciZqRxMhbZKIniTeonm+39SaiQQT40InaKZSqikUuC6pqnEQ3/OmpEqjp0\ 6gMflipQlh7B+pWv+rnapKizDlTpQrLlZ+xgtwZHTY9p+jjjb0OmaSZ20Fo7bXjUUJPKhmam8i2H\ Wj4b7ZQTndtatlbGKWyiJUZYLYzbstltcn5oiV1nfojbLa7iKmjZveAe5C24+rK73mUG3puggqh6\ BpFlfqHL6Lu2qTvbmhhmrC6EHp8LYWjjRkvwuTFqHFqsKvOp8sbpSspkyCZznMp9IiOkcJocHyyy\ z9Gm/DLM8Pqx86aa3sYymxZ/F/G786LMMY9Dq4lc1QJX/bKCR7c7cctJqwwxnloL27VhWQ8dbtUc\ lx3tlWfndaXWZtbqEK8ClYr3rfC+/zz1sGwDPnTbbvMsuN9fa3w42J9qHWrgfTO+KuQXFx4t4Yov\ nvnVSHvY4IiyTa704/HO6WqeYqL+rukYvv0v69yl8q/qpSP5XHA6Fk46Y63DKLtjq+Er2sloagt8\ mv4uRjvvmPfbrfIzs2ppo6q6uKjgzGOv/e7b/w2vvza/Dq/35C9dOuvZN0555elXLv74BeFdPuvg\ C/77/LgXL/H8EUqPbKmLapD8ukdA9BWQfQfMm/5ql7oFts98Z1pd7mQmrPydroEXlKADGcbADHaw\ dxp8F7Gephen9Y9DCtxfAg0IvxbiD4Mg/OADWcg/h9wOhpFaXQpLZMEYzrCHNaRhAf+BuMLW2M2G\ k6EYCoXowiFukHtNjCIRoYhAKkJwhlW8IhbJ5sEf4rCIX9RiFhM4wh2m8Ed7AhAHvdjFNYKxjVaU\ YhgbF0EZxkyHTEQiHF8YQh+6MY5c9CMgj+g5z+gKV6k65BsFOUY92jGKjWzkFOm4xTwGMkdlNOMi\ g7i8PwaxjpwEUyaZc6vvHCuSmpQjHj3pxD6y8ZGUZCUkLwnKOzKylbBMZShXGcdJik5fCcGUkR7m\ vx0iTpXnq2QnAalLY0pOjLtMZiWbKaKoGW6WTPSlJbf5m7jtkYy8BJGPrmccnBENc+riozSZmc05\ 4vKYsWTnBPFoRMdt8p7Y1CbsNof/yjwSEkiQoY9xbqg2ddrylcpcoMqUddCEhvOTebTm5fDZz2VG\ k1Tr46YoHyq4PQFuSHXUWL4MGk9cOjSGGmNoQ2XZT0zSk0lluyhFqVlRhT5ToyLk6PeWSBCF1RKa\ 2/wpSeX5RBS5SZ+0xKI2l+pOnObSqTJdU6FgRDW0CTVYt3QkQllKTWAFFanpoSAPm8pUV84UmVtl\ ZiYRaVWXKXKFVw3qSaM6VKPqEKwuTWZryvpUvs71rHityLwgNNi4JrWlf80nWdeq1cQC9YB+lWVk\ ifpNtHL1Ih6rVzFpGtbKSnKxZu0lYw9L18TpFaag7WtqHcvZdoaWIx4zWH+AOdma/3q2q0397Gsf\ a9t1spOmtTWpZHOL291C1a4blVVdW2tR4VI2q4g1bG9zeNoKrna4xgVrcy37W3fVRaU9DVFpXava\ 7BKXvNPMq3Tlmt69Xle05jWubsv71BVxZztvBWxRnbtcxd6WvdMMbHtRG9+nDpC74w0uJMXLmfse\ ZEhjmy5/J4xg/cq3sZedZ3XHKiCHGU282kzOFDQ73vlG6HniylaJGSylYtoGmPvkZ3EHp7l0nnW+\ 8DRfiGK6Upby2L8czvFx4UtjIJPOt+I8539WtiCszU+iVOXc1kpMTW927lATXdbQIqxew1r5v/Us\ soTj+GXoAldHapRtnzLKRJqlSayzMmZuU90824rQzK0ag/GAhUXn/lC5z/s1Mp7jrE8WkxauIjrb\ tYwI5duUS9EkzvDMII3ZeY2G0tTt8qU51K/fQQ/MbirZuagc0Q1N4Vveml2gI2noYrnaJV59tazf\ EutZ2zpJo721rudS6137mioC/rWw0RLsYRt7LMU+trK9kuxlOzsrZX62tMnSaAdP+9pfMWecsc1t\ aItUz90Ot7jHTe5ym/vc6E73rAICADs="] "Content-Type" "image-gif" return ok } /hangman0.png { HTTP::respond 200 content [b64decode \ "iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAYAAABccqhmAAAABmJLR0QA/wD/AP+gvaeTAAAACXBI\ WXMAAAsSAAALEgHS3X78AAAAB3RJTUUH1QUSFAoGtHRxSwAAAslJREFUeJzt3EEKhDAUBUGVuf+V\ 9QgKDnxiV53gbdJkEbJtAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMCdfXoA487pAR+x\ 5Fk6pgcAcwQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwn7TA1jGkk9dH0g/hXYDgDABgDABgDAB\ gDABgDABgDABgDABgDABgDABgDABgDABgDABgDABgDABgDABgDABgDABgDABgDB/AvLVv/54wA0A\ wgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQA\ wgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQA\ wgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQA\ wgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQA\ wgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQA\ wgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQA\ wgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQA\ wgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwgQAwvbpAfzFOT2AMa/OsBsAhAkAhAkAhAkA\ hAkAhAkAhAkAhAkAhAkAhAkAhAkAhAkAhAkAhAkAhAkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\ AAAAAAAAANy7AOu/BIYwGzL3AAAAAElFTkSuQmCC"] "Content-Type" "image-png" return ok } /hangman1.png { HTTP::respond 200 content [b64decode \ "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"] "Content-Type" "image-png" return ok } /hangman2.png { HTTP::respond 200 content [b64decode \ "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"] "Content-Type" "image-png" return ok } /hangman3.png { HTTP::respond 200 content [b64decode \ "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"] "Content-Type" "image-png" return ok } /hangman4.png { HTTP::respond 200 content [b64decode \ "iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAYAAABccqhmAAAABmJLR0QA/wD/AP+gvaeTAAAACXBI WXMAAAsSAAALEgHS3X78AAAAB3RJTUUH1QUSFQgceuKAhAAACjZJREFUeJzt3duLXWcZx/HvZFIT 49RpUmOTalokNWgU07SeqpVKLXpRBFEoeiPeKGJRUW/8G0RBEC+9EK8qkhY1iAc0nkI9tCjaTNFa NWmTtmmaTpMmk0lmxotnLeYlJM5hr73XXuv5fmAYJpNZs8Je7+999/MeApIkSZIkSZIkSZIkSZIk SZIkSZIkSZIkSZIkSZIkSZIkSZIkSZIkSZIkSZIkSZIkaSUTbd+AWrfU9g30RCfb0oa2b0BSewwA KTEDQErMAJASMwCkxAwAKTEDQErMAJASMwCkxAwAKbGNbd+AOqOTS11XIfVSaEcAUmIGgJSYASAl ZgBIiRkAUmIGgJSYASAlZgBIiRkAUmIGgJSYASAlZgBIiRkAUmIGgJSYASAlZgBIiRkAWq2ptm9A zTMAcltLo74PuBXYNqR7UQs8EiynbcBNwD1r+JmvAo8DvwcOAY8BZxu/M41UX89509W9FfggcBew D7gR+BOwCOwAdgKvAl4GTgDPECPFdwMLwCngj8BPgJ8BT4729hvX1JmAtiWNvduBbwFHgQvAr4CT RCOoPy4B89X354lGv1T9vUNEUJwDZoCvVdfssqWGPjrJ1MpjL/AZ4r38LNGQ91bfWyIa+izRox+v vn8dsBu4AbiGGAkcqT7vAZ4FDgLfAR4e0b+jaalHANYActgFfBz4CNG430QM8xeBi8BzwN+B3wGH iWH/JLCV6OHfC9wGvA54MzEC+Auwv7rmPBEeM6P6B0lanS3Ap4kGfoQo3NVD/aeBA8AXiIY+fYWf nwbeAnwO+AERIJeq68wQIfIk8Pmr/Py4S/0WQP33duBB4DxRua+H+/8Fvgm8g9U13C3Vtb4B/JsI gfp6c9Xv6GI9wABQb00DXwGOEQW/JaLHPgl8m+jZ12oP8HXgKSJIDlWf/wN8ke6NAgwA9dbtRM88 x3K1fw74MfC+Aa67H3iAGFWU1/0+6wuVNqUOAFcC9tc0MbzfB/wZeA3R+x8j5vAfGeDa/yRGFE8T i4oOE7MEtwLvIt4uqAMMgP6aIqb5drDcQ10A/gD8kqjkr9dZYtrvCDH8XySepZ3EDMPWAa6tETIA +us6YDsxnbeDaKQniN76WAPXP0osCz5FrBOAGAW8lphiVAcYAP21QDTICaJnXiQa7aM0s4b/BWJJ 8D+IRg8x0thARxfFZGQA9Ncky8t460U/LwBnGvwdx4lFQ5uLP7Pxd4gB0F8XWQ6Al1le/HOxwd9R 9/h1PWGCCJ7OVsWzMQD6a4LlADhRfT1N1AaaciPx/v9k8Tsnibce6gADoN8uVZ+fIfZ9vJGYGmxi mm6a2B+wm9hLABEAi0TwqAMMgP46R2zQWSBe5w1Ej30Py7sAB3ELsZjoepafowtE2AwyxagRMgD6 6yWiQn8KuAN4HthErOe/i8GW7E4DdxIrAmeB9xA9/3Fi09HzA1xbI2QA9NcsMU33KNEzP0YM0bcD dxPD9/XaA7yfWF9QbwG+QJwsdBhHANJY2ALcTyzdXSBCYJHoob9LHPO1VnuJXYR1b1/vLnwC+Czd Oz049V4ADwTpt3PEst87iMM86im7bcC91d95JTFKmF3hWvV24PuADxMNvf6ZS8Sy4IfxoFBprEwB nwT+SqwBeIRopPVI4CDwZaJxX2l2YIo4Beh+4EfElN+Z6jp1738U+BLdPDLcEYB67Syxc+8NxIrA fcRbgkWiYX8AeBtR0f8FsXPwNDGXfy2xw+9OYpffLmKoP0nUEBaJcwF/CPycWGmoDnHZZh57gE8R Q/ibifD/NbF//3qiiHecCIfZ6vv1/x+wk+j1Z4gZBIie/yngIeB7DLa9uE1N9d6dbEudvGmt2x7g E8DHiHn8TURd4DDRm99AzBJsIWoFzxE9/CQx1QfRYC4A/yLOE3yAKC52lQGgVHYRU3gfIob1ryeC YKVnoT5B+CTxNuEg8FOa2VrcJgNA6UwRQXA3sTLwNuJtQF0TmmD52ag3ET1LFBJ/C/yGqPr3Yb7f AFBaW4h5/XcSbw92VF/fRDT6M8RhnzPA34iFRU+w8pRhl6QOAAliae8u4KNEA697/TniPf5+unfa 72o5Daj0ZquP7cQeggWi0DdJPCOn6Vevr4p7AVR6kZjamyOG//PE0NbnpKd8YVWqK/2T1dcbgVcU X6tnDACVyur/AhEG80QwqIcMAJXq56E+N7A+2ssA6CmLgOPpSlXlUUwzbSCG/PVx4hOMrsp9+e9w Wm0EDIDxc7XGVv/5MBvGAjHkv1j9vosMrwi4UqgsYQgMnQHQPWXDabqBLFbXv4blKcAmi4CdnS/v KwOg25oOg2EUAW30Y8wAGD/1++61aiIMmioCNtHoHf6PgAEwnsqHf5AwWGsjWm8RsMle3oY/QgbA +BskDNZaWV9rEdCevuMMgG4pt+iux0ojg5WKgDb4nnEhUDdNXPaxVlfbxXalIuC9xH8wMmjjX++9 aogMgH4YpHGVYVA/D5uJ/0bs1Q3dlw1/TPnC9FNbU29dfJ5SHwhiDaCfLn8Yhx0InXz4ZQBkMWjx 8GrXU8dZA8hlvcXDW9b5cxpzBkBuq23QbgfuKQNA8P+D4ACu5+8tA0Cl3cCDxHmAx6vPngnYY76w Kg17O7DGjAGgkmcCJmMAqOSZgMkYACq1eSagWmAAqDTKMwE1BnxhVbIImIwBoJJFwGQMAJUsAiZj AKhkETAZA0Ali4DJ+MKqZBEwGQNAJYuAyRgAKlkETMYAUMkiYDIGgEoWAZPxhVXJImAyBoBKFgGT MQBUsgiYjAGgkkXAZAwAlSwCJuMLq5JFwGQMAJUsAiZjAKhkETAZA0Ali4DJGAAqWQRMxhdWJYuA yRgAKlkETMYAUMkiYDIGgEoWAZMxAFSyCJiML6xKFgGTMQBUsgiYjAGgkkXAZAwAlSwCJmMAqGQR MBlfWJUsAiZjAKhUFgHrEYBFwB4zAFSqn4dFYDOwqfhaPWQAqDQPvESMAjYRDf8kMNfmTWl4NrZ9 Axorp4GHiAC4lmj8B4AX27wpDc/Eyn9FyUwBW4lC4Hmi8Z9v9Y6Gq6kpzk62pU7etNSg1AFgDUBK zACQEjMApMQMACkxA0BKzACQEjMApMQMACkxA0BKzACQEjMApMQMACkxA0BKzACQEjMApMQMACkx A0BKzACQEjMApMQMACkxA0BKzACQEjMApMQMACkxA0BKzACQEjMApMQMACkxA0BKzACQEjMApMQM ACkxA0BKzACQEjMApMQMACkxA0BKzACQEjMApMQMACkxA0BKzACQEjMApMQMACkxA0BKzACQEjMA pMQMACkxA0BKzACQEjMApMQMACkxA0BKbKLtG1Ajltq+AbVmoDbsCEBKzACQEjMApMQMACkxA0BK zACQEjMApMQMACkxA0BKzACQEjMApMQMACkxA0BKzACQEjMAJEmSJEmSJEmSJEmSJEmSJEmSJEmS JEmSJEmSJEmSJEmSJEmSJEmSJEmSJEmSJEla2f8AEmOb1hmWYikAAAAASUVORK5CYII="] "Content-Type" "image-png" return ok } /hangman5.png { HTTP::respond 200 content [b64decode \ "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"] "Content-Type" "image-png" return ok } /hangman6.png { HTTP::respond 200 content [b64decode \ "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"] "Content-Type" "image-png" return ok } /flash.html { if {[HTTP::cookie exists HangmanCookie]}{ set userID [substr [HTTP::cookie HangmanCookie] 7 ":"] set hangword [findstr [HTTP::cookie HangmanCookie] "hangword," 9 ":"] if {$static::hangmandebug} { log local0.debug "userID=$userID and hangword = $hangword" } } else { #If they got in here it was probably by forceful browsing, so send #them back to the main page. The cookie needs to be set before moving #forward. HTTP::redirect "/index.html" return ok } #Test to see if there are any wrong answers and change that variable to the #number wrong if {[HTTP::cookie exists HangmanWrong]} { set wronganswers [HTTP::cookie HangmanWrong] } else { set wronganswers 0 } #Figure out how long the hangman word is set lettercount [string length $hangword] #Iteratively print the letters as dashes - filling in letters if there is a correct #guess #create a blank string called letterstring set letterstring "" #See if there were any correct guesses first and set a variable to that #value if { [HTTP::cookie exists HangmanGuesses] } { set guesses [HTTP::cookie HangmanGuesses] } else { set guesses "_" } if {$static::hangmandebug} { log local0.debug "Guesses = $guesses" } #create a list of letters from the hangman word #This is used to determine if a character should be printed on the #hangman board or not. This creates a list by making each letter #into a member of the list - so "abcdef" becomes { a b c d e f } #You can then reference them with [lindex $hangletterlist 0] #which in this example would be "a" because it is the 0th letter in #the list. set hangletterlist [split $hangword {}] #OK - iterate through the whole list of letters in the word and determine #if any of the existing guesses there are correct. If so then print them #accordingly #Assume that there are no right guesses and if there are then display them #If the user has as many rightguesses as letters then they win set rightguesses 0 #Wrong guesses isn't used yet - this could be implemented later though #set wrongguesses 0 for { set i 0 } { $i < $lettercount } { incr i } { set currentletter [string toupper [lindex $hangletterlist $i]] if {$static::hangmandebug} { log local0.debug "Currentletter = $currentletter" } if { $guesses contains $currentletter } { set letterstring "$letterstring $currentletter" incr rightguesses } else { set letterstring "$letterstring _" #incr wrongguesses } } #If the number of guesses is the same as the letter count they won. if { $rightguesses >= $lettercount } { if {$static::hangmandebug} { log local0.debug "Going to redirect to win.html" } HTTP::respond 302 Location "/win.html?userID=$userID&hangword=$hangword" #Set-Cookie: $cookies return ok if {$static::hangmandebug} { log local0.debug "Should have redirected so I shouldn't be here" } } if {$static::hangmandebug} { log local0.debug "Letterstring = $letterstring" } #Keystring is the keyboard that is displayed to the user. #If they already guessed wrong then don't display that key as an HREF. Bold it. # set keystring "" #Build a keystring so that if the guesses were wrong you don't include an #HREF. if { [HTTP::cookie exists HangmanWrongGuesses] } { set badguesses "[HTTP::cookie HangmanWrongGuesses]" } else { set badguesses "0" } if {$static::hangmandebug} { log local0.debug "Badguesses = $badguesses" } #Create a list representing the keyboard and iterate through it to determine #if each letter should be represented as a link or as a bolded character. #A bolded character means it has already been pressed, either in a correct guess #or in a wrong guess. # #To add a space into the keyboard, just insert a "-" into the list #To add a space and a line break, just insert a "_" into the list foreach keyletter { Q W E R T Y U I O P _ A S D F G H J K L _ - Z X C V B N M} { if {$static::hangmandebug} { log local0.debug "Keyletter = $keyletter" } if { (not ($keyletter eq "_") ) and ( ($badguesses contains $keyletter) or ($guesses contains $keyletter) )} { set keystring "$keystring\n$keyletter" } else { if {$keyletter eq "_"} { set keystring "$keystring\n" } elseif {$keyletter eq "-"} { set keystring "$keystring\n" } else { set keystring "$keystring\n$keyletter" } } } if {$static::hangmandebug} { log local0.debug "Keystring = $keystring" } #This code will display a keyboard. If a user presses a key #then this will be the path that is called if { $wronganswers < 7 } { #All of this is just a bunch of HTML to create keys that the user #can click on. Then we can check the path and see what letter they pressed. #Kind of a cool way to do user input, since we don't use any forms or anything. #Just good old HTML the way Dr. Berners Lee intended it to be. :) HTTP::respond 200 content " $letterstring $keystring " return ok } else { #The user lost because wronganswers is not < 7 #so now get rid of the cookies if { $static::hangmandebug } { log local0.debug "User lost" } set wronganswers 0 HTTP::respond 302 Location "/lost.html?hangword=$hangword&userID=$userID" return ok } } /lost.html* { if {$static::hangmandebug} { log local0.debug "I'm in your lost section logging your irulez" } #Get the word that they passed in if { [HTTP::query] contains "hangword=" } { set hangword [findstr [HTTP::query] "hangword=" 9 "&"] } else { set hangword "Undefined." } if { [HTTP::query] contains "userID=" } { set userID [findstr [HTTP::query] "userID=" 7 "&"] } else { set userID "Undefined." } HTTP::respond 200 content " I am so sorry that you lost. Please try again. The super tricky word that finally did you in was $hangword. Please press here to start over " return ok } /win.html* { if {$static::hangmandebug} { log local0.debug "I'm in your win section winning your irulez" } if { [HTTP::query] contains "userID=" } { set userID [findstr [HTTP::query] "userID=" 7 "&"] } if { [HTTP::query] contains "hangword=" } { set hangword [findstr [HTTP::query] "hangword=" 9 "&"] } #Before we do any operations on variables we should check to see if they exist first #if userID doesn't exist then we will behave differently. if { [info exists userID] } { HTTP::respond 200 content " You won. You are so awesome $userID. The winning word was $hangword. Please press here to keep going Click Here for a definition of ${hangword} " return ok } else { HTTP::respond 200 content " I am not sure how you arrived here but this is the win page. If you want to play and really win press here. " return ok } } /?.html { #Get the letter that was pressed from the PATH set letter [substr "[HTTP::path]" 1 "."] if {($letter >= "A") and ($letter <= "Z") } { if {$static::hangmandebug} { log local0.debug "Letter pressed was $letter" } if {[HTTP::cookie exists HangmanCookie]}{ set userID [substr [HTTP::cookie HangmanCookie] 7 ":"] set hangword [findstr [HTTP::cookie HangmanCookie] "hangword," 9 ":"] if {$static::hangmandebug} { log local0.debug "Letter pressed was $letter userID=$userID and hangword = $hangword" } #Check to see if the word contains the letter pressed if {"[string toupper $hangword]" contains "$letter" } { #If it has the letter you need to display the new #string - for now just redirect back #Getting in here means that the user guessed the letter properly if {$static::hangmandebug} { log local0.debug "The letter $letter was in $hangword" } if {[HTTP::cookie exists HangmanGuesses]}{ set guesses [HTTP::cookie HangmanGuesses] set guesses "$guesses,$letter" } else { set guesses "$letter" } HTTP::respond 302 Location "/flash.html" "Set-Cookie" \ HangmanGuesses=$guesses return ok } else { #We have to store the letters they guessed wrong as well if {[HTTP::cookie exists HangmanWrongGuesses]}{ set guesses [HTTP::cookie HangmanWrongGuesses] set guesses "$guesses,$letter" } else { set guesses "$letter" } #If there ia a cookie called HangmanWrong, then increment it #if there isn't then create it. if {[HTTP::cookie exists HangmanWrong] } { if {$static::hangmandebug} {log local0.debug "Had a hangman wrong cookie"} set hangmanWrong "[HTTP::cookie HangmanWrong]" incr hangmanWrong set cookies "HangmanWrong=$hangmanWrong\r\nSet-Cookie: HangmanWrongGuesses=$guesses" HTTP::respond 302 Location "/flash.html" Set-Cookie $cookies return ok } else { set hangmanWrong "1" if {$static::hangmandebug} {log local0.debug "No hangmanwrong cookie"} set cookies "HangmanWrong=$hangmanWrong\r\nSet-Cookie: HangmanWrongGuesses=$guesses" HTTP::respond 302 Location "/flash.html" Set-Cookie $cookies return ok } } } else { #If they got in here it was probably by forceful browsing, so send #them back to the main page. The cookie needs to be set before moving #forward. HTTP::redirect "/index.html" return ok } } else { #This code should only execute if someone put something manually into the URL #bar to try and mess with the program. HTTP::respond 200 content "You pressed $letter, which is not a valid letter" return ok } } /cookieclear.html { #This code was necessary because cookies weren't clearing properly when done all at once #Tried this to do them manually. Cookies can be a bit difficult to clean up. #This hack will remove the cookies by setting the expire time to an hour #earlier in relative time # #Create a string with all the cookies to send back #This one expires all the cookies #Internet Explorer doesn't process Max-Age - so much for RFC-2109 #Firefox doesn't process expires=-3600 - not sure why #Must do both to support both browsers if { [HTTP::cookie exists HangmanWrong] or [HTTP::cookie exists HangmanGuesses] or \ [HTTP::cookie exists HangmanCookie] or [HTTP::cookie exists HangmanWrongGuesses] } { HTTP::respond 302 Location "/cookieclear.html" Set-Cookie \ "HangmanWrong=null; expires=-3600; max-age=0;\ \r\nSet-Cookie: HangmanGuesses=null; expires=-3600; max-age=0;\ \r\nSet-Cookie: HangmanCookie=null; expires=-3600; max-age=0;\ \r\nSet-Cookie: HangmanWrongGuesses=null; expires=-3600; max-age=0;" return ok } #If they got to here there are no more cookies in the cookie jar. Time to go to the grocery #store and buy some more. #Ok, nice metaphor but we are just going to redirect back to index.hmtl. If they aren't gone it #will come back. Hope we don't get stuck in a loop. #Keep the userID - should be in the Referer header if { [HTTP::header referer] contains "userID=" } { set userID [findstr "[HTTP::header referer]" "userID=" 7 "&"] HTTP::respond 302 Location "/index.html?userID=$userID" return ok } else { HTTP::respond 302 Location "/index.html" return ok } } /index.html - / { #Check to see if any cookies exist. They shouldn't since this is the start of the program. if { [HTTP::cookie exists "HangmanCookie" ] or [HTTP::cookie exists "HangmanWrong"] \ or [HTTP::cookie exists "HangmanGuesses"] or [HTTP::cookie exists "HangmanWrongGuesses"] } { #If there is already a cookie then the user shouldn't be here. #They probably messed with the URL bar manually. HTTP::respond 302 Location "/cookieclear.html" return ok } else { #This is where we need to set the userID if { [HTTP::uri] contains "userID=" } { #Search for userID in query string set userID [findstr "[HTTP::query]" "userID=" 7 "&"] #This is to search through the data group to get the random word #the variable $static::hangmanWordSize is a variable that represents #the number of words in the data group, or "class" set randomnumber [expr { int ($static::hangmanWordSize * rand()) }] #Since the class is separated by spaces we can just get the one with #that index #The words in the class have the format of "Number Word" #So if you reference "1 aardvark" you can get the word #aardvark using findclass set myword [class element -name "$randomnumber" Hangmanwords_external_dg] if {$static::hangmandebug} { log local0.debug "SELECTED WORD: $myword" } set cookies "HangmanCookie=userID,$userID:hangword,$myword" HTTP::respond 302 Location "/flash.html" Set-Cookie $cookies return ok #This is just the intro page if there was no userID in the URI #Just put the basic page up } else { HTTP::respond 200 content " The hangman game is one in which you attempt to guess the letters in a word. The number of letters in the word will be represented by dashes. You have 7 chances to guess the word, and each time you are wrong you will see a new body part created. Please enter a username below and press enter to start the game. This whole game is done by an iRule with a single event. There are $static::hangmanWordSize words available in this game. Username: " return ok } } } } if {$static::hangmandebug} { log local0.debug "The PATH was [HTTP::path]" } }899Views0likes2Commentspython bigsuds - Get Orphaned iRules
Problem this snippet solves: This python bigsuds script will get a list of all iRules that are not associated with a virtual server (orphaned). How to use this snippet: orphanedrules.py <hostname> <username> Script will prompt for password. Script orphanedrules.py Code : #!/usr/bin/env python __author__ = 'buzzsurfr' __version__ = '0.1' def get_orphaned_rules(obj, recursive = True): ''' Gets a list of orphaned rules. Prototype String [] get_orphaned_rules( BIGIP.bigip obj, bool recursive = True ); Parameters obj of type BIGIP.bigip contains the established connection. recursive of type boolean indicates whether to perform a recursive search throughout the entire configuration. Defaults to True. Return Type String [] containing the list of all orphaned rules. ''' # Get current values to override for search active_folder = obj.System.Session.get_active_folder() recursive_query_state = obj.System.Session.get_recursive_query_state() # Enable fully-recursive search if recursive: obj.System.Session.set_active_folder('/') obj.System.Session.set_recursive_query_state("STATE_ENABLED") # Get list of iRules rules = obj.LocalLB.Rule.get_list() # Create starting list of orphaned iRules. These will be removed from # list as they are found to be in use. orphaned_rules = rules # Get list of all iRules associated on virtual servers vs_rules = obj.LocalLB.VirtualServer.get_rule(obj.LocalLB.VirtualServer.get_list()) # Check each virtual server for iRules and remove from orphaned if exists for virtual_server in vs_rules: for rule in virtual_server: if rule['rule_name'] in rules: # If found, remove from orphaned_rules orphaned_rules.remove(rule['rule_name']) # Reset values overridden for search if recursive: obj.System.Session.set_active_folder(active_folder) obj.System.Session.set_recursive_query_state(recursive_query_state) return orphaned_rules # Instance Mode (Run as script) if __name__ == "__main__": # Standard Library import sys # Related Third-Party import getpass # Local Application/Library Specific import bigsuds if len(sys.argv) < 3: print "\n\n\tUsage: %s ip_address username" % sys.argv[0] sys.exit() # Get password from CLI userpass = getpass.getpass() # Connect to BIG-IP try: bigconn = bigsuds.BIGIP( hostname = sys.argv[1], username = sys.argv[2], password = userpass ) except Exception as e: print e orphans = get_orphaned_rules(bigconn) print "Orphaned iRules" for orphan in orphans: print "\t" + orphan Tested this on version: 11.5198Views0likes0CommentsMinimalistic LDAP proxy (via Simple-Bind) with Base-DN rewrite
Problem this snippet solves: The outlined iRule can be used to access independent LDAP instances behind a single Virtual Server using a single Base-DN. The iRule, will forward LDAP requests to an additional LDAP pool, if the Simple-Bind user account name matches the $static::other_domains list. The iRule would then translate the original Base-DN to the Base-DN matching the additional LDAP instance. Note1: The iRule doesn't adjust ASN.1/BER structures. So the original and changed Base-DN MUST have the same length. You may have to add SPACE paddings to allign the lenght of the Base-DNs. Note2: The iRule wouldn't change the traffic for the default LDAP instance. The traffic is passed through after the Simple-Bind request is completed. Note3: The iRule would translates just the LDAP requests destined to the Base-DN of the additional LDAP instance. The retrived results could then be accessed using their original DNs. Cheers, Kai How to use this snippet: Setup a standard Virtual Server for TCP:386. Apply SNAT and TCP profiles as needed. Assign the default LDAP instance as default_pool. Tweak the RULE_INIT event to match your environment. Code : when RULE_INIT { # # Minimalistic LDAP proxy (via Simple-Bind) with Base-DN rewrite # # Configuration of the other LDAP instance username prefix/suffixes set static::other_domains [list "itacs\\" "@itacs.de"];# List of lower case domain strings # Configuration of the other LDAP instance pool name set static::other_ldap_poolname OTHER_LDAP_POOL_NAME;# Value of the other pool name # Configuration of the Base-DN translation strings # # Important: The Base-DNs MUST have the same lenght. # You have to pad SPACES to match the length # binary scan "OU=xyz,DC=your-domain,DC=tld" H* temp(dn_default) ;# This is the default Base-DN binary scan "OU=f5-team, DC=itacs, DC=de" H* temp(dn_other);# This is the other Base-DN. Pad SPACES to match the length set static::other_base_dn_map [list $temp(dn_default) $temp(dn_other)] unset -nocomplain temp } when CLIENT_ACCEPTED { # TCP session init set session_binding_ldap 1 set session_other_active 0 # Collecting TCP data TCP::collect } when CLIENT_DATA { if { $session_binding_ldap } then { # Searching for simple Bind request to the other LDAP instance set session_binding_ldap 0 foreach temp(domain_string) $static::other_domains { if { [string tolower [TCP::payload]] contains $temp(domain_string) } then { # Forwarding the request to the other LDAP instance set session_other_active 1 pool $static::other_ldap_poolname log -noname local0.debug "LDAP simple bind request for other LDAP instance detected. Forwarding the connection to pool [LB::server pool]" break } } if { $session_other_active == 0 } then { # Forwarding the request to the default LDAP instance log -noname local0.debug "LDAP request for default LDAP instance detected. Forwarding the connection to pool [LB::server pool]" # Releasing TCP data TCP::release unset -nocomplain temp } } if { $session_other_active } then { # Translating Base-DNs for the other LDAP instance binary scan [TCP::payload] H* temp(hex_tcp_payload) set temp(new_tcp_payload) [binary format H* [string map $static::other_base_dn_map $temp(hex_tcp_payload)]] TCP::payload replace 0 [string length [TCP::payload]] $temp(new_tcp_payload) # Releasing TCP data TCP::release # Collecting further TCP data TCP::collect unset -nocomplain temp } } Tested this on version: 12.0562Views0likes0CommentsRewrite http:// to https:// in response content
Problem this snippet solves: (Maybe I missed it, but) I didn't see a code share for using a STREAM profile to rewrite content from http to https. This share is just to make it easier to find a simple iRule to replace http:// links in page content to https://. It's taken directly from the STREAM::expression Wiki page. How to use this snippet: You'll need to assign a STREAM profile to you virtual server in order for this to work (just create an empty stream profile and assign it). Code : # Example which replaces http:// with https:// in response content # Prevents server compression in responses when HTTP_REQUEST { # Disable the stream filter for all requests STREAM::disable # LTM does not uncompress response content, so if the server has compression enabled # and it cannot be disabled on the server, we can prevent the server from # sending a compressed response by removing the compression offerings from the client HTTP::header remove "Accept-Encoding" } when HTTP_RESPONSE { # Check if response type is text if {[HTTP::header value Content-Type] contains "text"}{ # Replace http:// with https:// STREAM::expression {@http://@https://@} # Enable the stream filter for this response only STREAM::enable } } Tested this on version: 11.53.2KViews0likes5Comments