commit 55c18b30a97f807bee5c7e13f6a2fa34af995b56
Author: Scott Ullrich <sullrich@pfsense.org>
Date:   Tue Jan 25 11:55:07 2011 -0500

    Adding preliminary version of allowed hostnames.  Allowed hostnames function similar to allowed IP addresses and permit the captive portal to pass traffic out.  An example usage of this is to allow access to a hotel web page freely and then require authentcation hotlinking from this point.

diff --git a/etc/inc/captiveportal.inc b/etc/inc/captiveportal.inc
index 50ddf90e77..e171fe976f 100644
--- a/etc/inc/captiveportal.inc
+++ b/etc/inc/captiveportal.inc
@@ -639,9 +639,13 @@ EOD;
 	/* generate passthru mac database */
 	$cprules .= captiveportal_passthrumac_configure(true);
 	$cprules .= "\n";
+
 	/* allowed ipfw rules to make allowed ip work */
 	$cprules .= captiveportal_allowedip_configure();
 
+	/* allowed ipfw rules to make allowed hostnames work */
+	$cprules .= captiveportal_allowedhostname_configure();
+	
 	/* load rules */
 	if ($reinit == true)
 		$cprules = "table all flush\nflush\n{$cprules}";
@@ -1046,6 +1050,7 @@ function captiveportal_allowedip_configure_entry($ipent) {
 	A change results in reloading the ruleset.
 */
 function setup_dnsfilter_entries($hostname) {
+	global $g, $config;
 	$cp_filterdns_filename = "{$g['varetc_path']}/filterdns-captiveportal.conf";
 	$fd = fopen($cp_filterdns_filename, "w");
 	if (is_array($config['captiveportal']['allowedhostname'])) 
@@ -1053,11 +1058,7 @@ function setup_dnsfilter_entries($hostname) {
 			fwrite($fd, $hostnameent . "\n");
 	fclose($fd);
 	killbypid("{$g['tmp_path']}/dnswatch-cpah.pid");
-	mwexec("/usr/local/sbin/dnswatch {$g['tmp_path']}/dnswatch-cpah.pid 300 '/etc/rc.filter_configure_sync' {$g['varetc_path']}/dnswatch-captiveportal.conf");
-}
-
-function captiveportal_allowedhostname_configure_entry($ipent) {
-
+	mwexec("/usr/local/sbin/dnswatch {$g['tmp_path']}/dnswatch-cpah.pid 300 '/etc/rc.captiveportal_configure' {$g['varetc_path']}/dnswatch-captiveportal.conf");
 }
 
 function captiveportal_allowedhostname_configure() {
@@ -1065,10 +1066,13 @@ function captiveportal_allowedhostname_configure() {
 
 	$rules = "";
 	setup_dnsfilter_entries();
-	if (is_array($config['captiveportal']['allowedhostname'])) 
-		foreach ($config['captiveportal']['allowedhostname'] as $hostnameent) 
-			$rules .= captiveportal_allowedip_configure_entry($hostnameent);
-
+	if (is_array($config['captiveportal']['allowedhostname'])) {
+		foreach ($config['captiveportal']['allowedhostname'] as $hostnameent) {
+			$ipaddress = gethostbyname($hostnameent);
+			if(is_ipaddr($ipaddress)) 
+				$rules .= captiveportal_allowedip_configure_entry($ipaddress);
+		}
+	}
 	return $rules;
 }
 
@@ -1077,9 +1081,8 @@ function captiveportal_allowedip_configure() {
 
 	$rules = "";
 	if (is_array($config['captiveportal']['allowedip'])) {
-		foreach ($config['captiveportal']['allowedip'] as $ipent) {
+		foreach ($config['captiveportal']['allowedip'] as $ipent) 
 			$rules .= captiveportal_allowedip_configure_entry($ipent);
-		}
 	}
 
 	return $rules;
diff --git a/usr/local/www/services_captiveportal_hostname.php b/usr/local/www/services_captiveportal_hostname.php
new file mode 100755
index 0000000000..28404791c3
--- /dev/null
+++ b/usr/local/www/services_captiveportal_hostname.php
@@ -0,0 +1,192 @@
+<?php
+/*
+	services_captiveportal_hostname.php
+	Copyright (C) 2011 Scott Ullrich <sullrich@gmail.com>
+	All rights reserved.
+
+	Originally part of m0n0wall (http://m0n0.ch/wall)
+	Copyright (C) 2004 Dinesh Nair <dinesh@alphaque.com>
+	All rights reserved.
+
+	Redistribution and use in source and binary forms, with or without
+	modification, are permitted provided that the following conditions are met:
+
+	1. Redistributions of source code must retain the above copyright notice,
+	   this list of conditions and the following disclaimer.
+
+	2. Redistributions in binary form must reproduce the above copyright
+	   notice, this list of conditions and the following disclaimer in the
+	   documentation and/or other materials provided with the distribution.
+
+	THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+	INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+	AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+	AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+	OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+	SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+	INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+	CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+	POSSIBILITY OF SUCH DAMAGE.
+*/
+/*
+	pfSense_BUILDER_BINARIES:	/sbin/ipfw
+	pfSense_MODULE:	captiveportal
+*/
+
+##|+PRIV
+##|*IDENT=page-services-captiveportal-allowedhostnames
+##|*NAME=Services: Captive portal: Allowed IPs page
+##|*DESCR=Allow access to the 'Services: Captive portal: Allowed IPs' page.
+##|*MATCH=services_captiveportal_ip.php*
+##|-PRIV
+
+$statusurl = "status_captiveportal.php";
+$logurl = "diag_logs_auth.php";
+
+require("guiconfig.inc");
+require("functions.inc");
+require("filter.inc");
+require("shaper.inc");
+require("captiveportal.inc");
+
+$pgtitle = array(gettext("Services"),gettext("Captive portal"));
+
+if (!is_array($config['captiveportal']['allowedhostname']))
+	$config['captiveportal']['allowedhostname'] = array();
+
+$a_allowedhostnames = &$config['captiveportal']['allowedhostname'] ;
+
+if ($_GET['act'] == "del") {
+	if ($a_allowedhostnames[$_GET['id']]) {
+		$ipent = $a_allowedhostnames[$_GET['id']];
+		
+		if (isset($config['captiveportal']['enable'])) {
+			if (!empty($ipent['sn']))
+				$ipent['ip'] .= "/{$ipent['sn']}";
+			$ip = gethostbyname($ipent['ip']);
+			if(is_ipaddr($ip)) {
+				mwexec("/sbin/ipfw table 3 delete {$ip}");
+				mwexec("/sbin/ipfw table 4 delete {$ip}");
+				mwexec("/sbin/ipfw table 5 delete {$ip}");
+				mwexec("/sbin/ipfw table 6 delete {$ip}");
+				mwexec("/sbin/ipfw table 7 delete {$ip}");
+				mwexec("/sbin/ipfw table 8 delete {$ip}");
+				mwexec("/sbin/ipfw table 9 delete {$ip}");
+				mwexec("/sbin/ipfw table 10 delete {$ip}");
+			}
+		}
+			
+		unset($a_allowedhostnames[$_GET['id']]);
+		write_config();
+		header("Location: services_captiveportal_ip.php");
+		exit;
+	}
+}
+
+
+include("head.inc");
+?>
+<?php include("fbegin.inc"); ?>
+<body link="#0000CC" vlink="#0000CC" alink="#0000CC">
+<form action="services_captiveportal_ip.php" method="post">
+<?php if ($savemsg) print_info_box($savemsg); ?>
+<table width="100%" border="0" cellpadding="0" cellspacing="0">
+  <tr><td class="tabnavtbl">
+<?php
+	$tab_array = array();
+	$tab_array[] = array(gettext("Captive portal"), false, "services_captiveportal.php");
+	$tab_array[] = array(gettext("Pass-through MAC"), false, "services_captiveportal_mac.php");
+	$tab_array[] = array(gettext("Allowed IP Addresses"), true, "services_captiveportal_ip.php");
+	$tab_array[] = array(gettext("Allowed Hostnames"), true, "services_captiveportal_hostname.php");
+	$tab_array[] = array(gettext("Vouchers"), false, "services_captiveportal_vouchers.php");
+	$tab_array[] = array(gettext("File Manager"), false, "services_captiveportal_filemanager.php");
+	display_top_tabs($tab_array);
+?>
+  </td></tr>
+  <tr>
+  <td class="tabcont">
+  <table width="100%" border="0" cellpadding="0" cellspacing="0">
+	<tr>
+	  <td width="30%" class="listhdrr"><?=gettext("Hostname"); ?></td>
+	  <td width="60%" class="listhdr"><?=gettext("Description"); ?></td>
+	  <td width="10%" class="list">
+		<table border="0" cellspacing="0" cellpadding="1">
+		   <tr>
+			<td width="17" heigth="17"></td>
+			<td><a href="services_captiveportal_ip_edit.php"><img src="/themes/<?php echo $g['theme']; ?>/images/icons/icon_plus.gif" title="<?=gettext("add address"); ?>" width="17" height="17" border="0"></a></td>
+		   </tr>
+		</table>
+	  </td>
+	</tr>
+  <?php $i = 0; foreach ($a_allowedhostnames as $ip): ?>
+	<tr ondblclick="document.location='services_captiveportal_ip_edit.php?id=<?=$i;?>'">
+	  <td class="listlr">
+		<?php
+		if($ip['dir'] == "to") {
+			echo "any <img src=\"/themes/{$g['theme']}/images/icons/icon_in.gif\" width=\"11\" height=\"11\" align=\"absmiddle\"> ";
+		}
+		if($ip['dir'] == "both") {
+			echo "<img src=\"/themes/{$g['theme']}/images/icons/icon_pass.gif\" width=\"11\" height=\"11\" align=\"absmiddle\">   ";
+		}
+		echo strtolower($ip['ip']);
+		if($ip['sn'] != "32" && is_numeric($ip['sn'])) {
+			$sn = $ip['sn'];
+			echo "/$sn";	
+		}
+		if($ip['dir'] == "from") {
+			echo "<img src=\"/themes/{$g['theme']}/images/icons/icon_in.gif\" width=\"11\" height=\"11\" align=\"absmiddle\"> any";
+		}
+		
+		?>	
+	  </td>
+	  <td class="listbg">
+		<?=htmlspecialchars($ip['descr']);?>&nbsp;
+	  </td>
+	  <td valign="middle" nowrap class="list"> <a href="services_captiveportal_ip_edit.php?id=<?=$i;?>"><img src="/themes/<?php echo $g['theme']; ?>/images/icons/icon_e.gif" title="<?=gettext("edit address"); ?>" width="17" height="17" border="0"></a>
+		 &nbsp;<a href="services_captiveportal_ip.php?act=del&id=<?=$i;?>" onclick="return confirm('<?=gettext("Do you really want to delete this address?"); ?>')"><img src="/themes/<?php echo $g['theme']; ?>/images/icons/icon_x.gif" title="<?=gettext("delete address"); ?>" width="17" height="17" border="0"></a></td>
+	</tr>
+  <?php $i++; endforeach; ?>
+	<tr>
+	  <td class="list" colspan="2">&nbsp;</td>
+	  <td class="list">
+		<table border="0" cellspacing="0" cellpadding="1">
+		   <tr>
+			<td width="17" heigth="17"></td>
+			<td><a href="services_captiveportal_ip_edit.php"><img src="/themes/<?php echo $g['theme']; ?>/images/icons/icon_plus.gif" title="<?=gettext("add address"); ?>" width="17" height="17" border="0"></a></td>
+		   </tr>
+		</table>
+	  </td>
+	</tr>
+	<tr>
+	<td colspan="2" class="list"><p class="vexpl"><span class="red"><strong>
+	  <?=gettext("Note:"); ?><br>
+	  </strong></span>
+	  <?=gettext("Adding allowed Hostnamees will allow IP access to/from these addresses through the captive portal without being taken to the portal page. This can be used for a web server serving images for the portal page or a DNS server on another network, for example. By specifying <em>from</em> addresses, it may be used to always allow pass-through access from a client behind the captive portal."); ?></p>
+	  <table border="0" cellspacing="0" cellpadding="0">
+		<tr>
+		  <td><span class="vexpl"><?=gettext("any"); ?> <img src="/themes/<?=$g['theme'];?>/images/icons/icon_in.gif" width="11" height="11" align="absmiddle"> x.x.x.x </span></td>
+		  <td><span class="vexpl"><?=gettext("All connections"); ?> <strong><?=gettext("to"); ?></strong> <?=gettext("the Hostname are allowed"); ?></span></td>
+		</tr>
+		<tr>
+		  <td colspan="5" height="4"></td>
+		</tr>
+		<tr>
+		  <td>x.x.x.x <span class="vexpl"><img src="/themes/<?=$g['theme'];?>/images/icons/icon_in.gif" width="11" height="11" align="absmiddle"></span> <?=gettext("any"); ?>&nbsp;&nbsp;&nbsp; </td>
+		  <td><span class="vexpl"><?=gettext("All connections"); ?> <strong><?=gettext("from"); ?></strong> <?=gettext("the Hostname are allowed"); ?> </span></td>
+		</tr>
+		<tr>
+		  <td><span class="vexpl"><img src="/themes/<?=$g['theme'];?>/images/icons/icon_pass.gif" width="11" height="11" align="right"></span>&nbsp;&nbsp;&nbsp;&nbsp; </td>
+		  <td><span class="vexpl"> All connections <strong>to</strong> and <strong>from</strong> the Hostname are allowed </span></td>
+		</tr>
+	  </table></td>
+	<td class="list">&nbsp;</td>
+	</tr>
+  </table>
+  </td>
+  </tr>
+  </table>
+</form>
+<?php include("fend.inc"); ?>
+</body>
+</html>
diff --git a/usr/local/www/services_captiveportal_hostname_edit.php b/usr/local/www/services_captiveportal_hostname_edit.php
new file mode 100755
index 0000000000..ca166d4dfa
--- /dev/null
+++ b/usr/local/www/services_captiveportal_hostname_edit.php
@@ -0,0 +1,218 @@
+<?php 
+/*
+	services_captiveportal_hostname_edit.php
+	Copyright (C) 2011 Scott Ullrich <sullrich@gmail.com>
+	All rights reserved.
+
+	Originally part of m0n0wall (http://m0n0.ch/wall)
+	Copyright (C) 2004 Dinesh Nair <dinesh@alphaque.com>
+	All rights reserved.
+	
+	Redistribution and use in source and binary forms, with or without
+	modification, are permitted provided that the following conditions are met:
+	
+	1. Redistributions of source code must retain the above copyright notice,
+	   this list of conditions and the following disclaimer.
+	
+	2. Redistributions in binary form must reproduce the above copyright
+	   notice, this list of conditions and the following disclaimer in the
+	   documentation and/or other materials provided with the distribution.
+	
+	THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+	INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+	AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+	AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+	OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+	SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+	INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+	CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+	POSSIBILITY OF SUCH DAMAGE.
+*/
+/*
+	pfSense_BUILDER_BINARIES:	/sbin/ipfw
+	pfSense_MODULE:	captiveportal
+*/
+
+##|+PRIV
+##|*IDENT=page-services-captiveportal-editallowedhostnames
+##|*NAME=Services: Captive portal: Edit Allowed IPs page
+##|*DESCR=Allow access to the 'Services: Captive portal: Edit Allowed IPs' page.
+##|*MATCH=services_captiveportal_ip_edit.php*
+##|-PRIV
+
+function allowedhostnamescmp($a, $b) {
+	return strcmp($a['hostname'], $b['hostname']);
+}
+
+function allowedhostnames_sort() {
+        global $g, $config;
+
+        usort($config['captiveportal']['allowedhostname'],"allowedhostnamescmp");
+}
+
+$statusurl = "status_captiveportal.php";
+$logurl = "diag_logs_auth.php";
+
+require("guiconfig.inc");
+require("functions.inc");
+require("filter.inc");
+require("shaper.inc");
+require("captiveportal.inc");
+
+$pgtitle = array(gettext("Services"),gettext("Captive portal"),gettext("Edit allowed Hostname"));
+
+if (!is_array($config['captiveportal']['allowedhostname']))
+	$config['captiveportal']['allowedhostname'] = array();
+
+$a_allowedhostnames = &$config['captiveportal']['allowedhostname'];
+
+$id = $_GET['id'];
+if (isset($_POST['id']))
+	$id = $_POST['id'];
+
+if (isset($id) && $a_allowedhostnames[$id]) {
+	$pconfig['hostname'] = $a_allowedhostnames[$id]['hostname'];
+	$pconfig['sn'] = $a_allowedhostnames[$id]['sn'];
+	$pconfig['dir'] = $a_allowedhostnames[$id]['dir'];
+	$pconfig['bw_up'] = $a_allowedhostnames[$id]['bw_up'];
+	$pconfig['bw_down'] = $a_allowedhostnames[$id]['bw_down'];
+	$pconfig['descr'] = $a_allowedhostnames[$id]['descr'];
+}
+
+if ($_POST) {
+
+	unset($input_errors);
+	$pconfig = $_POST;
+
+	/* input validation */
+	$reqdfields = explode(" ", "ip");
+	$reqdfieldsn = array(gettext("Allowed Hostname"));
+	
+	do_input_validation($_POST, $reqdfields, $reqdfieldsn, &$input_errors);
+	
+	if (($_POST['hostname'] && !is_hostname($_POST['hostname']))) {
+		$input_errors[] = sprintf(gettext("A valid Hostname must be specified. [%s]"), $_POST['hostname']);
+	}
+	if ($_POST['bw_up'] && !is_numeric($_POST['bw_up']))
+                $input_errors[] = gettext("Upload speed needs to be an integer");
+        if ($_POST['bw_down'] && !is_numeric($_POST['bw_down']))
+                $input_errors[] = gettext("Download speed needs to be an integer");
+
+	foreach ($a_allowedhostnames as $ipent) {
+		if (isset($id) && ($a_allowedhostnames[$id]) && ($a_allowedhostnames[$id] === $ipent))
+			continue;
+		
+		if ($ipent['hostname'] == $_POST['hostname']){
+			$input_errors[] = sprintf("[%s] %s.", $_POST['hostname'], gettext("already allowed")) ;
+			break ;
+		}	
+	}
+
+	if (!$input_errors) {
+		$ip = array();
+		$ip['hostname'] = $_POST['hostname'];
+		$ip['sn'] = $_POST['sn'];
+		$ip['dir'] = $_POST['dir'];
+		$ip['descr'] = $_POST['descr'];
+		if ($_POST['bw_up'])
+			$ip['bw_up'] = $_POST['bw_up'];
+		if ($_POST['bw_down'])
+			$ip['bw_down'] = $_POST['bw_down'];
+		if (isset($id) && $a_allowedhostnames[$id]) {
+			$oldip = $a_allowedhostnames[$id]['hostname'];
+			if (!empty($a_allowedhostnames[$id]['sn']))
+				$oldip .= "/{$a_allowedhostnames[$id]['sn']}";
+			$a_allowedhostnames[$id] = $ip;
+		} else {
+			$oldip = $ip['hostname'];
+			if (!empty($$ip['sn']))
+				$oldip .= "/{$$ip['sn']}";
+			$a_allowedhostnames[] = $ip;
+		}
+		allowedhostnames_sort();
+		
+		write_config();
+
+		if (isset($config['captiveportal']['enable']) && is_module_loaded("ipfw.ko")) {
+			$rules = "";
+			$hostname = gethostbyname($oldip);
+			if($hostname)
+				for ($i = 3; $i < 10; $i++)
+					$rules .= "table {$i} delete {$hostname}\n";
+			$hostname = gethostbyname($ip);
+			if(is_ipaddr($hostname))
+				$rules .= captiveportal_allowedip_configure_entry($hostname);
+			file_put_contents("{$g['tmp_path']}/allowedhostname_tmp{$id}", $rules);
+			mwexec("/sbin/ipfw -q {$g['tmp_path']}/allowedhostname_tmp{$id}");
+			@unlink("{$g['tmp_path']}/allowedhostname_tmp{$id}");
+		}
+		
+		header("Location: services_captiveportal_ip.php");
+		exit;
+	}
+}
+
+include("head.inc");
+
+?>
+<?php include("fbegin.inc"); ?>
+<body link="#0000CC" vlink="#0000CC" alink="#0000CC">
+<?php if ($input_errors) print_input_errors($input_errors); ?>
+		<form action="services_captiveportal_ip_edit.php" method="post" name="iform" id="iform">
+		<table width="100%" border="0" cellpadding="6" cellspacing="0">
+		<tr>
+			<td width="22%" valign="top" class="vncellreq"><?=gettext("Direction"); ?></td>
+			<td width="78%" class="vtable"> 
+			<select name="dir" class="formfld">
+		<?php 
+			$dirs = array(gettext("Both"),gettext("From"),gettext("To")) ;
+			foreach ($dirs as $dir): ?>
+				<option value="<?=strtolower($dir);?>" <?php if (strtolower($dir) == strtolower($pconfig['dir'])) echo "selected";?> >
+				<?=htmlspecialchars($dir);?>
+				</option>
+		<?php endforeach; ?>
+			</select>
+			<br> 
+			<span class="vexpl"><?=gettext("Use"); ?> <em><?=gettext("From"); ?></em> <?=gettext("to always allow an Hostname through the captive portal (without authentication)"); ?>. 
+				<?=gettext("Use"); ?> <em><?=gettext("To"); ?></em> <?=gettext("to allow access from all clients (even non-authenticated ones) behind the portal to this Hostname"); ?>.</span></td>
+		</tr>
+		<tr>
+			<td width="22%" valign="top" class="vncellreq"><?=gettext("Hostname"); ?></td>
+			<td width="78%" class="vtable"> 
+				<?=$mandfldhtml;?><input name="ip" type="text" class="formfld unknown" id="ip" size="17" value="<?=htmlspecialchars($pconfig['hostname']);?>">
+			<br> 
+			<span class="vexpl"><?=gettext("Hostname");?>.</span></td>
+		</tr>
+		<tr>
+			<td width="22%" valign="top" class="vncell"><?=gettext("Description"); ?></td>
+			<td width="78%" class="vtable"> 
+			<input name="descr" type="text" class="formfld unknown" id="descr" size="40" value="<?=htmlspecialchars($pconfig['descr']);?>">
+			<br> <span class="vexpl"><?=gettext("You may enter a description here for your reference (not parsed)"); ?>.</span></td>
+			</tr>
+		<tr>
+			<td width="22%" valign="top" class="vncell"><?=gettext("Bandwidth up"); ?></td>
+			<td width="78%" class="vtable">
+			<input name="bw_up" type="text" class="formfld unknown" id="bw_up" size="10" value="<?=htmlspecialchars($pconfig['bw_up']);?>">
+			<br> <span class="vexpl"><?=gettext("Enter a upload limit to be enforced on this Hostname in Kbit/s"); ?></span></td>
+		</tr>
+		<tr>
+			<td width="22%" valign="top" class="vncell"><?=gettext("Bandwidth down"); ?></td>
+			<td width="78%" class="vtable">
+			<input name="bw_down" type="text" class="formfld unknown" id="bw_down" size="10" value="<?=htmlspecialchars($pconfig['bw_down']);?>">
+			<br> <span class="vexpl"><?=gettext("Enter a download limit to be enforced on this Hostname in Kbit/s"); ?></span></td>
+		</tr>
+		<tr>
+			<td width="22%" valign="top">&nbsp;</td>
+			<td width="78%"> 
+				<input name="Submit" type="submit" class="formbtn" value="<?=gettext("Save"); ?>">
+				<?php if (isset($id) && $a_allowedhostnames[$id]): ?>
+					<input name="id" type="hidden" value="<?=htmlspecialchars($id);?>">
+				<?php endif; ?>
+			</td>
+		</tr>
+	</table>
+</form>
+<?php include("fend.inc"); ?>
+</body>
+</html>
