Introduction

Since the release of Windows Server 2008 Streaming Media Services, many ISA admins have wondered how they could combine the two to control the routing of streaming content for their users. There are generally two goals for this configuration:

1.       limit streaming content to approved sources

2.       limit the bandwidth consumed by this traffic

By directing all streaming media requests through your WMS proxy, you can achieve both goals for most streaming content.  If your clients are WPAD-aware, you can create a fairly flexible solution by managing the WPAD script contents manually. There are two methods for achieving this redirection; protocol- and destination-based.

You may benefit from a review of some of the WPAD-related documents available on the Internet:

Ø  TechNet article Automatic Detection Concepts in ISA Server 2006

Ø  ISABlog posting Understanding By-Design Behavior of ISA Server 2006: Using Kerberos Authentication for Web Proxy Requests on ISA Server 2006 with NLB

Ø  Microsoft Knowledgebase article 893241

Ø  Microsoft Knowledgebase article 271361

Considerations

1.       This process only works for applications which can be configured to operate as Web proxy clients that alter their behavior through the use of a proxy configuration script.  SecureNET and Firewall clients cannot make use of this.

2.       The custom script must be delivered from a location other than ISA Server.  ISA is not a web server and will not deliver a static file stored on the local file system.  Likewise, if your client proxy configuration uses the /array.dll?Get.Routing.Script script request form, you will have to change it to use /wpad.dat if you want to use a custom WPAD script.

3.       Because this request for /wpad.dat is always made using HTTP, you’ll need to use a web server in your environment to serve these requests. 

4.       You cannot force authentication for these requests, since some applications rightly expect that this content is served anonymously and will fail if asked for authentication.

5.       You must force client applications to acquire a new script.  By default, ISA delivers this script with a Time-To-live (TTL) of 50 minutes and many applications (IE, for instance) will cache this script for an hour.  While clearing the browser cache is one means of removing the script, if the client’s name cache still directs it to ISA for the script, your web server will not be asked for it.  The best way to force the clients to acquire the script from the new location is to perform the following commands from a command window:

ipconfig/flushdns

nbtstat -R

del \wpad[*.dat /s

gpupdate /force

Note: if the client is Vista or later, you have to execute these commands from an elevated command window.

..executing these commands at the client will ensure:

-          the DNS cache is cleared (if you’ve simply changed the DNS record for “wpad”)

-          the NB name cache is cleared (if you’ve changed the WINS record for “wpad” or the client resolved it through NB broadcast)

-          all instances of the wpad script are deleted from the local drive; you may want to execute this for each drive on the client

-          group policy is updated if your application configuration is specified in Group Policy (IE and WMP apply)

Protocol-based Redirection

The default WPAD script defines a “direct” result for many streaming media protocols. What we want is to direct the client to the WMS proxy for requests that use those protocols.

Original Script

function FindProxyForURL(url, host){

 var hash=0, urllower, i, fIp=false, ip, nocarp=false, skiphost=false;

 var list="", pl, j, score, ibest, bestscore;

 urllower = url.toLowerCase();

 if((urllower.substring(0,5)=="rtsp:") ||

   (urllower.substring(0,6)=="rtspt:") ||

   (urllower.substring(0,6)=="rtspu:") ||

   (urllower.substring(0,4)=="mms:") ||

   (urllower.substring(0,5)=="mmst:") ||

   (urllower.substring(0,5)=="mmsu:"))

  return "DIRECT";

if(UseDirectForLocal){

You can change the script to redirect request using these protocols to your WMS proxy by changing the script as:

Script Change #1

function FindProxyForURL(url, host){

 var hash=0, urllower, i, fIp=false, ip, nocarp=false, skiphost=false;

 var list="", pl, j, score, ibest, bestscore;

 urllower = url.toLowerCase();

 if((urllower.substring(0,5)=="rtsp:") ||

   (urllower.substring(0,6)=="rtspt:") ||

   (urllower.substring(0,6)=="rtspu:") ||

   (urllower.substring(0,4)=="mms:") ||

   (urllower.substring(0,5)=="mmst:") ||

   (urllower.substring(0,5)=="mmsu:"))

  return "YourWmsProxy:port";

if(UseDirectForLocal){

..where YourWmsProxy represents the name or IP address of your WMS proxy server and :port represents the WMS proxy server listening port.  This change has the effect of redirecting requests for URLs that begin with the specified protocols to the WMS proxy. 

While this change will produce the desired results, I have a personal preference for code brevity.  The change I prefer is shown below.

Script Change #2

function FindProxyForURL(url, host){

 var hash=0, urllower, i, fIp=false, ip, nocarp=false, skiphost=false;

 var list="", pl, j, score, ibest, bestscore;

 urllower = url.toLowerCase();

 var oWms = /^(rtsp:|rtspt:|rtspu:|mms:|mmst:|mmsu:)/i;

 if(oWms.test(url))

  return "YourWmsProxy:port";

if(UseDirectForLocal){

This change offers two improvements over the original script and the first change:

1.       It reduces script size.  Many applications (IE6 and earlier, for instance) impose a 16K limit on the WPAD script.  If your ISA configuration includes a lot of direct-access, CARP exceptions, etc., your script may be closer to this limit than you think.

2.       It uses the Jscript Regular Expression object to perform the comparison of the URL against all of the protocols. This is faster and more efficient

You can choose the method that you like best; the results are identical. You cannot use them both, since only the first version will execute for requests using these protocols and executing both methods for any non-target protocols is a waste of script space and time.

Destination-based Redirection

Some streaming sources choose to use HTTP-based delivery, making protocol-based redirection impractical.  In this case, you’ll have to create a list of those destinations that you want redirected through your WMS proxy.

Original Script

function FindProxyForURL(url, host){

 var hash=0, urllower, i, fIp=false, ip, nocarp=false, skiphost=false;

 var list="", pl, j, score, ibest, bestscore;

 urllower = url.toLowerCase();

 if((urllower.substring(0,5)=="rtsp:") ||

   (urllower.substring(0,6)=="rtspt:") ||

   (urllower.substring(0,6)=="rtspu:") ||

   (urllower.substring(0,4)=="mms:") ||

   (urllower.substring(0,5)=="mmst:") ||

   (urllower.substring(0,5)=="mmsu:"))

  return "DIRECT";

if(UseDirectForLocal){

..would become:

New Script

var arrWmProxyDest = new Array(

 "site1.domain.tld",

 "site2.domain.tld");

function FindProxyForURL(url, host){

 var hash=0, urllower, i, fIp=false, ip, nocarp=false, skiphost=false;

 var list="", pl, j, score, ibest, bestscore;

 urllower = url.toLowerCase();

if((urllower.substring(0,5)=="rtsp:") ||

   (urllower.substring(0,6)=="rtspt:") ||

   (urllower.substring(0,6)=="rtspu:") ||

   (urllower.substring(0,4)=="mms:") ||

   (urllower.substring(0,5)=="mmst:") ||

   (urllower.substring(0,5)=="mmsu:"))

  return "DIRECT";

 for(var inx in arrWmProxyDest){

  if(shExpMatch(host.toLowerCase(), arrWmProxyDest[inx]))

   return "YourWmsProxy:port";}

if(UseDirectForLocal){

Note: all except the last entry in the arrWmProxyDest list must end with a comma ,. If you want to add sites to the list after your initial definitions are in place, it’s simplest to copy and edit an existing entry that ends with a comma and paste it just above the last entry.  This process would proceed as shown below.

Original list

var arrWmProxyDest = new Array(

 "site1.domain.tld",

 "site2.domain.tld");

 

New list

var arrWmProxyDest = new Array(

 "site1.domain.tld",

 "site3.domain.tld",

 "site2.domain.tld");

The last entry in the list must end with the ); sequence or you’ll get compilation errors.

Who Says We Can’t Do Both?

Just because the previous examples discussed and illustrated the two options separately to make their differences clearer, this is no reason they have to operate independently of each other.  You’ll find an example of such a change below.

Original Script

function FindProxyForURL(url, host){

 var hash=0, urllower, i, fIp=false, ip, nocarp=false, skiphost=false;

 var list="", pl, j, score, ibest, bestscore;

 urllower = url.toLowerCase();

 if((urllower.substring(0,5)=="rtsp:") ||

   (urllower.substring(0,6)=="rtspt:") ||

   (urllower.substring(0,6)=="rtspu:") ||

   (urllower.substring(0,4)=="mms:") ||

   (urllower.substring(0,5)=="mmst:") ||

   (urllower.substring(0,5)=="mmsu:"))

  return "DIRECT";

if(UseDirectForLocal){

 

Best Script

var arrWmProxyDest = new Array(

 "site1.domain.tld",

 "site2.domain.tld");

function FindProxyForURL(url, host){

 var hash=0, urllower, i, fIp=false, ip, nocarp=false, skiphost=false;

 var list="", pl, j, score, ibest, bestscore;

 urllower = url.toLowerCase();

 var oWms = /^(rtsp:|rtspt:|rtspu:|mms:|mmst:|mmsu:)/i;

 if(oWms.test(url))

  return "YourWmsProxy:port";

 for(var inx in arrWmProxyDest){

  if(shExpMatch(host.toLowerCase(), arrWmProxyDest[inx]))

   return "YourWmsProxy:port";}

if(UseDirectForLocal){

..of course, you’ll want to test the script each time you make any changes to ensure that you don’t break it, which brings us to…

Script Testing

Once you’ve completed the changes, you need to verify that they produce the results you intend.  The last thing you need is your CEO complaining because he can’t watch the Presidential Inauguration because of a mistake in the script. You must satisfy each step in the order presented if you want to ensure intended script behavior.  If you skip around, you’re likely to find yourself experiencing early cranial follicle freedom.

1. Script Compilation

The reason for changing the script’s file extension is to allow you to verify that the changes you make don’t violate Jscript syntax.  If the script fails to compile properly or your code changes result in a runtime error, the results seen by the application are “undefined” (fancy developer term meaning “stuff happens”).  Since the client application (Internet Explorer, Windows Media Player, Outlook) executes the script using the Windows Scripting engine, we’ll use the same engine to validate the syntax.  By working on and testing the WPAD script as a .js file, we can use the Windows script engine to verify that it loads and compiles properly.  To verify that the script compiles properly, you should:

1.       open a command window

2.       navigate to the location where you saved the updated script

3.       execute it by typing cscript wpad.js

When the script compiles properly, you will see results similar to this:

C:\wpadtest>cscript wpad.js

Microsoft (R) Windows Script Host Version 5.7

Copyright (C) Microsoft Corporation. All rights reserved.

 

 

C:\wpadtest>

As you may have noticed, when the script compiles properly, you’ll see nothing at all, since the script doesn’t produce screen output.  You could add something within the script to print a message to the screen, but you must remember to remove it before performing further testing, since there is no console available to the script when it runs within the client application and any console-oriented commands will produce “undefined results” in the application.

If the script produces a compilation error, you’ll see output similar to this;

C:\wpadtest>cscript wpad.js

Microsoft (R) Windows Script Host Version 5.7

Copyright (C) Microsoft Corporation. All rights reserved.

 

C:\ wpadtest\wpad.js(773, 11) Microsoft JScript compilation error: Expected ';'

 

 

C:\ wpadtest >

Jscript provides the line number and character position where it encountered the error.  You should bear in mind that this location is not necessarily the location of the error, but the place where the error caused compilation to fail.  You may have to experiment with your changes to locate the exact error position.  If Jscript writing is not your forte, you should limit your changes to the least amount you can and perform compilation tests for each change.

2. Script Execution

Now that the script compiles properly, you’ll want to see that it operates properly.  The easiest way to do that is to change the client application so that it acquires the script from the local file system.  For Internet Explorer, you would configure the Proxy settings as shown below.

IE Settings for local WPAD script 

Browser configuration for local file WPAD script

The entries in red are mandatory to ensure that the script is taken from the local file system.  The entry in blue describes the proper format for using a script located on the local file system. In order to verify that the script behaves as designed, you will need to use a URL that fits into the script design criteria.  If you used the protocol redirection method, test the script using a URL which uses MMS://, MMST://, MMSU://, RTSP://, RTSPT://or RTSPU://.  If you used the destination redirection method, use a URL which uses a host that is part of the arrWmProxyDest set.

A successful test in either case will result in the client making a connection to the WMS proxy listening port and issuing a request for the desired content.  Ideally, you would use a URL that will produce a connection to real media so that you can test the whole process.

3. Web Server Script Delivery

Once you’re satisfied with script operation, you need to place it on the web server you designated as the wpad replacement.  The thing to remember about testing script acquisition over the network is to:

1.       Rename the script to wpad.dat when you place it on the Web server root

2.       Use the steps provided in Considerations, step 5 to make sure that each request provides a new script

IE Settins for web server WPAD script 

Browser configuration for web server WPAD script

The entries in red are mandatory to ensure that the script is taken from the local file system.  The entry in blue describes the proper format for using a script located on the local file system.

As in Script Execution, the testing process depends on the choices you made for modifying the script behavior.

Windows media Player Caveats

WMP allows you to specify different proxy settings for each streaming media protocol it supports.  By default, WMP is configured so that RTSP-based requests do not use a proxy.  This is important since the browser will defer to the application which is registered in Windows as the RTSP or MMS protocol handler.   The default MS and RTSP protocol handler is Windows Media Player, although this may have changed if you’ve selected another media player as the default.  Unfortunately, you cannot configure WMP RTSP protocols to use a local file WPAD script, making local file script testing impossible for WMP. Since this article assumes that you have autodetection working in your environment, you should ensure WMP is configured as follows.

WMP Default 

WMP default proxy settings

In order to make full use of the kewl new script you’ve built, you’ll have to change this setting.  Click the “Configure” button and select “Autodetect proxy settings” as shown below.

WMP RTSP Autoproxy 

WMP RTSP proxy settings

Note: the option to use the Web browser settings is unavailable for RTSP proxy.

Click Close and your WMP proxy settings should appear as follows:

WMP RTSP Autoproxy results 

WMP adjusted proxy settings

All Done

This posting illustrates a very targeted adjustment of the WPAD script.  Once your changes produce the effect you desire, you can configure your ISA server so that it only allows requests from the WMS proxy for this content; thereby limiting access and the bandwidth required for this. In another blog (or perhaps a Tales from the Edge article) I’ll describe the underpinnings of the WPAD script design and how you can further change it to suit your own requirements.

Happy WPAD Scripting!

Author

Jim Harrison, Program Manager, Forefront Edge CS

Tech Reviewers

Yuri Diogenes, Support Engineer, Forefront Edge

Mohit Saxena, Tech Lead, Forefront Edge