Project

General

Profile

Download (47.6 KB) Statistics
| Branch: | Tag: | Revision:
1 5b237745 Scott Ullrich
<?php
2
/*
3
	captiveportal.inc
4 b260c8e0 Scott Ullrich
	part of pfSense (http://www.pfSense.org)
5 36254e4a Scott Ullrich
6 b260c8e0 Scott Ullrich
	originally part of m0n0wall (http://m0n0.ch/wall)
7
8
	Copyright (C) 2010 Scott Ullrich <sullrich@gmail.com>
9
	Copyright (C) 2009 Ermal Lu?i <ermal.luci@gmail.com>
10 0bd34ed6 Scott Ullrich
	Copyright (C) 2003-2006 Manuel Kasper <mk@neon1.net>.
11 5b237745 Scott Ullrich
	All rights reserved.
12 36254e4a Scott Ullrich
13 5b237745 Scott Ullrich
	Redistribution and use in source and binary forms, with or without
14
	modification, are permitted provided that the following conditions are met:
15 36254e4a Scott Ullrich
16 5b237745 Scott Ullrich
	1. Redistributions of source code must retain the above copyright notice,
17
	   this list of conditions and the following disclaimer.
18 36254e4a Scott Ullrich
19 5b237745 Scott Ullrich
	2. Redistributions in binary form must reproduce the above copyright
20
	   notice, this list of conditions and the following disclaimer in the
21
	   documentation and/or other materials provided with the distribution.
22 36254e4a Scott Ullrich
23 5b237745 Scott Ullrich
	THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
24
	INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
25
	AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26
	AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
27
	OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28
	SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29
	INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30
	CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31
	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32
	POSSIBILITY OF SUCH DAMAGE.
33 ca83c6ea Scott Ullrich
34 9699028a Scott Ullrich
	This version of captiveportal.inc has been modified by Rob Parker
35
	<rob.parker@keycom.co.uk> to include changes for per-user bandwidth management
36
	via returned RADIUS attributes. This page has been modified to delete any
37
	added rules which may have been created by other per-user code (index.php, etc).
38
	These changes are (c) 2004 Keycom PLC.
39 523855b0 Scott Ullrich
	
40 bf444c34 Ermal
	pfSense_BUILDER_BINARIES:	/sbin/ipfw	/sbin/sysctl	/sbin/kldunload
41 523855b0 Scott Ullrich
	pfSense_BUILDER_BINARIES:	/usr/local/sbin/lighttpd	/usr/local/bin/minicron	/sbin/pfctl
42
	pfSense_BUILDER_BINARIES:	/bin/hostname	/bin/cp	
43
	pfSense_MODULE:	captiveportal
44 9699028a Scott Ullrich
*/
45 36254e4a Scott Ullrich
46 5b237745 Scott Ullrich
/* include all configuration functions */
47 a55cdcc0 Ermal Lu?i
require_once("config.inc");
48
require_once("functions.inc");
49 71fdaecd Ermal
require_once("filter.inc");
50 856e58a6 Scott Ullrich
require_once("radius.inc");
51 156487ed Ermal Lu?i
require_once("voucher.inc");
52 0bd34ed6 Scott Ullrich
53 023aa1f2 Scott Ullrich
function get_default_captive_portal_html() {
54 5b237745 Scott Ullrich
	global $config, $g;
55 023aa1f2 Scott Ullrich
	// Detect if vouchers are being used and default to the voucher page
56 10370262 Scott Ullrich
	if(isset($config['voucher']['enable'])) {
57 023aa1f2 Scott Ullrich
			$htmltext = <<<EOD
58
<html> 
59
	<body> 
60
		<form method="post" action="\$PORTAL_ACTION\$"> 
61
			<center>
62
				<table cellpadding="6" cellspacing="0" width="550" height="380" style="border:1px solid #000000">
63
					<tr height="10" bgcolor="#990000">
64
						<td style="border-bottom:1px solid #000000">
65
							<font color='white'>
66
								<b>
67
									Guest Voucher code required to continue
68
								</b>
69
							</font>
70
						</td>
71
					</tr>
72
					<tr>
73
						<td>
74
							<div id="mainlevel">
75
								<center>
76
									<table width="100%" border="0" cellpadding="5" cellspacing="0">
77
								 		<tr>
78
								    		<td>
79
												<center>
80
													<div id="mainarea">
81
														<center>
82
															<table width="100%" border="0" cellpadding="5" cellspacing="5">
83
																<tr>
84
																	<td>
85
																		<div id="maindivarea">
86
																			<center>
87
																				<div id='statusbox'>
88
																					<font color='red' face='arial' size='+1'>
89
																						<b>
90
																							\$PORTAL_MESSAGE\$
91
																						</b>
92
																					</font>
93
																				</div>
94
																				<p/>
95
																				<div id='loginbox'>
96
																					Enter Voucher Code: 
97
																					<input name="auth_voucher" type="text" style="border:1px dashed;" size="22"> 
98
																					<input name="redirurl" type="hidden" value="\$PORTAL_REDIRURL\$"> 
99
																					<input name="accept" type="submit" value="Continue"> 
100
																				</div>
101
																			</center>
102
																		</div>
103
										     						</td>
104
																</tr>
105
															</table>
106
														</center>
107
													</div>
108
												</center>
109
											</td>
110
										</tr>
111
									</table>
112
								</center>
113
							</div>
114
						</td>
115
					</tr>
116
				</table>
117
			</center>
118
		</form>
119
	</body> 
120
</html>
121 36254e4a Scott Ullrich
122 023aa1f2 Scott Ullrich
EOD;
123 c2056357 Scott Ullrich
		return $htmltext;
124
	}
125 0bd34ed6 Scott Ullrich
126 023aa1f2 Scott Ullrich
	// Vouchers are not found, return the normal user/pass auth page
127
	$htmltext = <<<EOD
128 b260c8e0 Scott Ullrich
<html> 
129
	<body> 
130 ce7c3bb5 Scott Ullrich
		<form method="post" action="\$PORTAL_ACTION\$"> 
131
		<input name="redirurl" type="hidden" value="\$PORTAL_REDIRURL\$">
132 b260c8e0 Scott Ullrich
			<center>
133
				<table cellpadding="6" cellspacing="0" width="550" height="380" style="border:1px solid #000000">
134
					<tr height="10" bgcolor="#990000">
135
						<td style="border-bottom:1px solid #000000">
136
							<font color='white'>
137
								<b>
138
									{$g['product_name']} captive portal
139
								</b>
140
							</font>
141
						</td>
142
					</tr>
143
					<tr>
144
						<td>
145
							<div id="mainlevel">
146
								<center>
147
									<table width="100%" border="0" cellpadding="5" cellspacing="0">
148
								 		<tr>
149
								    		<td>
150
												<center>
151
													<div id="mainarea">
152
														<center>
153
															<table width="100%" border="0" cellpadding="5" cellspacing="5">
154
																<tr>
155
										     						<td>
156
																		<div id="maindivarea">
157
																			<center>
158
																				<div id='statusbox'>
159
																					<font color='red' face='arial' size='+1'>
160
																						<b>
161 ce7c3bb5 Scott Ullrich
																							\$PORTAL_MESSAGE\$
162 b260c8e0 Scott Ullrich
																						</b>
163
																					</font>
164
																				</div>
165
																				<br/>
166
																				<div id='loginbox'>
167
																					<table>
168
																					   <tr><td colspan="2"><center>Welcome to the {$g['product_name']} Captive Portal!</td></tr>
169
																					   <tr><td>&nbsp;</td></tr>
170
																					   <tr><td align="right">Username:</td><td><input name="auth_user" type="text" style="border: 1px dashed;"></td></tr>
171
																					   <tr><td align="right">Password:</td><td><input name="auth_pass" type="password" style="border: 1px dashed;"></td></tr>
172
																					   <tr><td>&nbsp;</td></tr>
173
																					   <tr>
174
																					     <td colspan="2">
175
																							<center><input name="accept" type="submit" value="Continue"></center>
176
																					     </td>
177
																					   </tr>
178
																					</table>
179
																				</div>
180
																			</center>
181
																		</div>
182
										     						</td>
183
																</tr>
184
															</table>
185
														</center>
186
													</div>
187
												</center>
188
											</td>
189
										</tr>
190
									</table>
191
								</center>
192
							</div>
193
						</td>
194
					</tr>
195
				</table>
196
			</center>
197
		</form>
198
	</body> 
199 5b237745 Scott Ullrich
</html>
200
201 023aa1f2 Scott Ullrich
EOD;
202 0bd34ed6 Scott Ullrich
203 023aa1f2 Scott Ullrich
	return $htmltext;
204
}
205 0bd34ed6 Scott Ullrich
206 023aa1f2 Scott Ullrich
function captiveportal_configure() {
207
	global $config, $g;
208
209
	$captiveportallck = lock('captiveportal');
210
	
211
	if (isset($config['captiveportal']['enable'])) {
212
213
		if ($g['booting'])
214
			echo "Starting captive portal... ";
215
216
		/* kill any running mini_httpd */
217
		killbypid("{$g['varrun_path']}/lighty-CaptivePortal.pid");
218
		killbypid("{$g['varrun_path']}/lighty-CaptivePortal-SSL.pid");
219
220
		/* remove old information */
221
		unlink_if_exists("{$g['vardb_path']}/captiveportal.db");
222
		unlink_if_exists("{$g['vardb_path']}/captiveportal_mac.db");
223
		unlink_if_exists("{$g['vardb_path']}/captiveportal_ip.db");
224
		unlink_if_exists("{$g['vardb_path']}/captiveportal_radius.db");
225
226
		/* setup new database in case someone tries to access the status -> captive portal page */
227
		touch("{$g['vardb_path']}/captiveportal.db");
228
229
		/* kill any running minicron */
230
		killbypid("{$g['varrun_path']}/minicron.pid");
231
232
		/* init ipfw rules */
233
		captiveportal_init_rules(true);
234
235
		/* stop accounting on all clients */
236
		captiveportal_radius_stop_all(true);
237
238
		/* initialize minicron interval value */
239
		$croninterval = $config['captiveportal']['croninterval'] ? $config['captiveportal']['croninterval'] : 60;
240
241
		/* double check if the $croninterval is numeric and at least 10 seconds. If not we set it to 60 to avoid problems */
242
		if ((!is_numeric($croninterval)) || ($croninterval < 10)) { $croninterval = 60; }
243
244
		/* write portal page */
245
		if ($config['captiveportal']['page']['htmltext'])
246
			$htmltext = base64_decode($config['captiveportal']['page']['htmltext']);
247
		else {
248
			/* example/template page */
249
			$htmltext = get_default_captive_portal_html();
250 5b237745 Scott Ullrich
		}
251
252
		$fd = @fopen("{$g['varetc_path']}/captiveportal.html", "w");
253
		if ($fd) {
254 7a7e94a7 Scott Ullrich
			// Special case handling.  Convert so that we can pass this page
255
			// through the PHP interpreter later without clobbering the vars.
256
			$htmltext = str_replace("\$PORTAL_REDIRURL\$", "#PORTAL_REDIRURL#", $htmltext);
257
			$htmltext = str_replace("\$PORTAL_MESSAGE\$", "#PORTAL_MESSAGE#", $htmltext);
258
			$htmltext = str_replace("\$CLIENT_MAC\$", "#CLIENT_MAC#", $htmltext);
259
			$htmltext = str_replace("\$CLIENT_IP\$", "#CLIENT_IP#", $htmltext);
260
			$htmltext = str_replace("\$ORIGINAL_PORTAL_IP\$", "#ORIGINAL_PORTAL_IP#", $htmltext);
261
			$htmltext = str_replace("\$PORTAL_ACTION\$", "#PORTAL_ACTION#", $htmltext);
262 5b237745 Scott Ullrich
			fwrite($fd, $htmltext);
263 36254e4a Scott Ullrich
			fclose($fd);
264 5b237745 Scott Ullrich
		}
265 36254e4a Scott Ullrich
266 5b237745 Scott Ullrich
		/* write error page */
267
		if ($config['captiveportal']['page']['errtext'])
268
			$errtext = base64_decode($config['captiveportal']['page']['errtext']);
269
		else {
270
			/* example page */
271
			$errtext = <<<EOD
272 b260c8e0 Scott Ullrich
<html> 
273
	<body> 
274 ce7c3bb5 Scott Ullrich
		<form method="post" action="\$PORTAL_ACTION\$"> 
275
		<input name="redirurl" type="hidden" value="\$PORTAL_REDIRURL\$">
276 b260c8e0 Scott Ullrich
			<center>
277
				<table cellpadding="6" cellspacing="0" width="550" height="380" style="border:1px solid #000000">
278
					<tr height="10" bgcolor="#990000">
279
						<td style="border-bottom:1px solid #000000">
280
							<font color='white'>
281
								<b>
282
									{$g['product_name']} captive portal
283
								</b>
284
							</font>
285
						</td>
286
					</tr>
287
					<tr>
288
						<td>
289
							<div id="mainlevel">
290
								<center>
291
									<table width="100%" border="0" cellpadding="5" cellspacing="0">
292
								 		<tr>
293
								    		<td>
294
												<center>
295
													<div id="mainarea">
296
														<center>
297
															<table width="100%" border="0" cellpadding="5" cellspacing="5">
298
																<tr>
299
										     						<td>
300
																		<div id="maindivarea">
301
																			<center>
302
																				<div id='statusbox'>
303
																					<font color='red' face='arial' size='+1'>
304
																						<b>
305 ce7c3bb5 Scott Ullrich
																							\$PORTAL_MESSAGE\$
306 b260c8e0 Scott Ullrich
																						</b>
307
																					</font>
308
																				</div>
309
																				<br/>
310
																				<div id='loginbox'>
311
																					<table>
312
																					   <tr><td colspan="2"><center>Welcome to the {$g['product_name']} Captive Portal!</td></tr>
313
																					   <tr><td>&nbsp;</td></tr>
314
																					   <tr><td align="right">Username:</td><td><input name="auth_user" type="text" style="border: 1px dashed;"></td></tr>
315
																					   <tr><td align="right">Password:</td><td><input name="auth_pass" type="password" style="border: 1px dashed;"></td></tr>
316
																					   <tr><td>&nbsp;</td></tr>
317
																					   <tr>
318
																					     <td colspan="2">
319
																							<center><input name="accept" type="submit" value="Continue"></center>
320
																					     </td>
321
																					   </tr>
322
																					</table>
323
																				</div>
324
																			</center>
325
																		</div>
326
										     						</td>
327
																</tr>
328
															</table>
329
														</center>
330
													</div>
331
												</center>
332
											</td>
333
										</tr>
334
									</table>
335
								</center>
336
							</div>
337
						</td>
338
					</tr>
339
				</table>
340
			</center>
341
		</form>
342
	</body> 
343 5b237745 Scott Ullrich
</html>
344
345
EOD;
346
		}
347
348
		$fd = @fopen("{$g['varetc_path']}/captiveportal-error.html", "w");
349
		if ($fd) {
350 7a7e94a7 Scott Ullrich
			// Special case handling.  Convert so that we can pass this page
351
			// through the PHP interpreter later without clobbering the vars.
352
			$errtext = str_replace("\$PORTAL_REDIRURL\$", "#PORTAL_REDIRURL#", $errtext);
353
			$errtext = str_replace("\$PORTAL_MESSAGE\$", "#PORTAL_MESSAGE#", $errtext);
354
			$errtext = str_replace("\$CLIENT_MAC\$", "#CLIENT_MAC#", $errtext);
355
			$errtext = str_replace("\$CLIENT_IP\$", "#CLIENT_IP#", $errtext);
356
			$errtext = str_replace("\$ORIGINAL_PORTAL_IP\$", "#ORIGINAL_PORTAL_IP#", $errtext);
357
			$errtext = str_replace("\$PORTAL_ACTION\$", "#PORTAL_ACTION#", $errtext);
358 5b237745 Scott Ullrich
			fwrite($fd, $errtext);
359 36254e4a Scott Ullrich
			fclose($fd);
360 5b237745 Scott Ullrich
		}
361 36254e4a Scott Ullrich
362 5b87b24e Ermal
		/* write error page */
363
		if ($config['captiveportal']['page']['logouttext'])
364
			$logouttext = base64_decode($config['captiveportal']['page']['logouttext']);
365
		else {
366
			/* example page */
367
			$logouttext = <<<EOD
368
<HTML>
369
<HEAD><TITLE>Redirecting...</TITLE></HEAD>
370
<BODY>
371
<SPAN STYLE="font-family: Tahoma, Verdana, Arial, Helvetica, sans-serif; font-size: 11px;">
372
<B>Redirecting to <A HREF="{$my_redirurl}">{$my_redirurl}</A>...</B>
373
</SPAN>
374
<SCRIPT LANGUAGE="JavaScript">
375
<!--
376
LogoutWin = window.open('', 'Logout', 'toolbar=0,scrollbars=0,location=0,statusbar=0,menubar=0,resizable=0,width=256,height=64');
377
if (LogoutWin) {
378
    LogoutWin.document.write('<HTML>');
379
    LogoutWin.document.write('<HEAD><TITLE>Logout</TITLE></HEAD>') ;
380
    LogoutWin.document.write('<BODY BGCOLOR="#435370">');
381
    LogoutWin.document.write('<DIV ALIGN="center" STYLE="color: #ffffff; font-family: Tahoma, Verdana, Arial, Helvetica, sans-serif; font-size: 11px;">') ;
382
    LogoutWin.document.write('<B>Click the button below to disconnect</B><P>');
383
    LogoutWin.document.write('<FORM METHOD="POST" ACTION="{$logouturl}">');
384
    LogoutWin.document.write('<INPUT NAME="logout_id" TYPE="hidden" VALUE="{$sessionid}">');
385
    LogoutWin.document.write('<INPUT NAME="logout" TYPE="submit" VALUE="Logout">');
386
    LogoutWin.document.write('</FORM>');
387
    LogoutWin.document.write('</DIV></BODY>');
388
    LogoutWin.document.write('</HTML>');
389
    LogoutWin.document.close();
390
}
391
392
document.location.href="{$my_redirurl}";
393
-->
394
</SCRIPT>
395
</BODY>
396
</HTML>
397
398
EOD;
399
		}
400
401
		$fd = @fopen("{$g['varetc_path']}/captiveportal-logout.html", "w");
402
		if ($fd) {
403
			fwrite($fd, $logouttext);
404
			fclose($fd);
405
		}
406 0bd34ed6 Scott Ullrich
		/* write elements */
407
		captiveportal_write_elements();
408 5b237745 Scott Ullrich
409 769e254e Ermal
		/* start up the webserving daemon */
410
		captiveportal_init_webgui();
411 36254e4a Scott Ullrich
412 0bd34ed6 Scott Ullrich
		/* start pruning process (interval defaults to 60 seconds) */
413
		mwexec("/usr/local/bin/minicron $croninterval {$g['varrun_path']}/minicron.pid " .
414 c6c92abf Scott Ullrich
			"/etc/rc.prunecaptiveportal");
415 36254e4a Scott Ullrich
416 d99f7864 Scott Ullrich
		/* generate radius server database */
417
		if ($config['captiveportal']['radiusip'] && (!isset($config['captiveportal']['auth_method']) ||
418
				($config['captiveportal']['auth_method'] == "radius"))) {
419
			$radiusip = $config['captiveportal']['radiusip'];
420
			$radiusip2 = ($config['captiveportal']['radiusip2']) ? $config['captiveportal']['radiusip2'] : null;
421
422
			if ($config['captiveportal']['radiusport'])
423
				$radiusport = $config['captiveportal']['radiusport'];
424
			else
425
				$radiusport = 1812;
426
427
			if ($config['captiveportal']['radiusacctport'])
428
				$radiusacctport = $config['captiveportal']['radiusacctport'];
429
			else
430
				$radiusacctport = 1813;
431
432
			if ($config['captiveportal']['radiusport2'])
433
				$radiusport2 = $config['captiveportal']['radiusport2'];
434
			else
435
				$radiusport2 = 1812;
436
437
			$radiuskey = $config['captiveportal']['radiuskey'];
438
			$radiuskey2 = ($config['captiveportal']['radiuskey2']) ? $config['captiveportal']['radiuskey2'] : null;
439
440
			$fd = @fopen("{$g['vardb_path']}/captiveportal_radius.db", "w");
441
			if (!$fd) {
442
				printf("Error: cannot open radius DB file in captiveportal_configure().\n");
443
				return 1;
444
			} else if (isset($radiusip2, $radiuskey2)) {
445
				fwrite($fd,$radiusip . "," . $radiusport . "," . $radiusacctport . "," . $radiuskey . "\n"
446
					 . $radiusip2 . "," . $radiusport2 . "," . $radiusacctport . "," . $radiuskey2);
447
			} else {
448
				fwrite($fd,$radiusip . "," . $radiusport . "," . $radiusacctport . "," . $radiuskey);
449
			}
450
			fclose($fd);
451
		}
452 36254e4a Scott Ullrich
453 d99f7864 Scott Ullrich
		if ($g['booting'])
454
			echo "done\n";
455 36254e4a Scott Ullrich
456 5b237745 Scott Ullrich
	} else {
457 23a0c341 Scott Ullrich
		killbypid("{$g['varrun_path']}/lighty-CaptivePortal.pid");
458 c6c92abf Scott Ullrich
		killbypid("{$g['varrun_path']}/minicron.pid");
459 12ee8fe4 Scott Ullrich
460 dedf51a2 Ermal Lu?i
		captiveportal_radius_stop_all(true);
461 3db19cf1 Scott Ullrich
462
		mwexec("/sbin/sysctl net.link.ether.ipfw=0");
463
464 5209079f Ermal Luçi
		/* unload ipfw */
465 faebbab3 Scott Ullrich
		if (is_module_loaded("ipfw.ko"))		
466
			mwexec("/sbin/kldunload ipfw.ko");
467 85250056 Ermal Lu?i
		$listifs = get_configured_interface_list_by_realif();
468 bbc6768b Ermal Lu?i
		foreach ($listifs as $listrealif => $listif) {
469 7c587b9f Ermal Lu?i
			if (!empty($listrealif)) {
470 da9d6701 Ermal
				if (does_interface_exist($listrealif)) {
471 bf444c34 Ermal
					pfSense_interface_flags($listrealif, -IFF_IPFW_FILTER);
472 da9d6701 Ermal
					$carpif = link_ip_to_carp_interface(find_interface_ip($listrealif));
473
                        		if (!empty($carpif)) {
474
						$carpsif = explode(" ", $carpif);
475
						foreach ($carpsif as $cpcarp)
476 bf444c34 Ermal
							pfSense_interface_flags($cpcarp, -IFF_IPFW_FILTER);
477 da9d6701 Ermal
					}
478 2ee45728 Ermal Lu?i
				}
479 bbc6768b Ermal Lu?i
			}
480
		}
481 3db19cf1 Scott Ullrich
	}
482 36254e4a Scott Ullrich
483 f8b11310 Ermal Lu?i
	unlock($captiveportallck);
484 c3214b80 Scott Ullrich
	
485 5b237745 Scott Ullrich
	return 0;
486
}
487
488 769e254e Ermal
function captiveportal_init_webgui() {
489
	global $g, $config;
490
491
	 if (!isset($config['captiveportal']['enable']))
492
                return;
493
494
	if ($config['captiveportal']['maxproc'])
495
		$maxproc = $config['captiveportal']['maxproc'];
496
	else
497
		$maxproc = 16;
498
499
	$use_fastcgi = true;
500
501
	if (isset($config['captiveportal']['httpslogin'])) {
502
		$cert = base64_decode($config['captiveportal']['certificate']);
503
		if (isset($config['captiveportal']['cacertificate']))
504
			$cacert = base64_decode($config['captiveportal']['cacertificate']);
505
		else
506
			$cacert = "";
507
		$key = base64_decode($config['captiveportal']['private-key']);
508
		/* generate lighttpd configuration */
509
		system_generate_lighty_config("{$g['varetc_path']}/lighty-CaptivePortal-SSL.conf",
510
			$cert, $key, $cacert, "lighty-CaptivePortal-ssl.pid", "8001", "/usr/local/captiveportal/",
511
			"cert-portal.pem", "ca-portal.pem", "1", $maxproc, $use_fastcgi, true);
512
	}
513
514
	/* generate lighttpd configuration */
515
	system_generate_lighty_config("{$g['varetc_path']}/lighty-CaptivePortal.conf",
516
		"", "", "", "lighty-CaptivePortal.pid", "8000", "/usr/local/captiveportal/",
517
		"cert-portal.pem", "ca-portal.pem", "1", $maxproc, $use_fastcgi, true);
518
519
	/* attempt to start lighttpd */
520
	$res = mwexec("/usr/local/sbin/lighttpd -f {$g['varetc_path']}/lighty-CaptivePortal.conf");
521
522
	/* fire up https instance */
523
	if (isset($config['captiveportal']['httpslogin']))
524
		$res = mwexec("/usr/local/sbin/lighttpd -f {$g['varetc_path']}/lighty-CaptivePortal-SSL.conf");
525
}
526
527 1d9e9cca Ermal
function captiveportal_init_rules($reinit = false) {
528 3db19cf1 Scott Ullrich
	global $config, $g;
529 36254e4a Scott Ullrich
530 769e254e Ermal
	if (!isset($config['captiveportal']['enable']))
531
		return;
532
533
	$cpips = array();
534
	$ifaces = get_configured_interface_list();
535
	foreach ($ifaces as $kiface => $kiface2) {
536
		$tmpif = get_real_interface($kiface);
537
		pfSense_interface_flags($tmpif, -IFF_IPFW_FILTER);
538
	}
539
	$cpinterfaces = explode(",", $config['captiveportal']['interface']);
540
	$firsttime = 0;
541
	foreach ($cpinterfaces as $cpifgrp) {
542
		if (!isset($ifaces[$cpifgrp]))
543
			continue;
544
		$tmpif = get_real_interface($cpifgrp);
545
		if (!empty($tmpif)) {
546
			if ($firsttime > 0)
547
				$cpinterface .= " or ";
548
			$cpinterface .= "via {$tmpif}";
549
			$firsttime = 1;
550
			$cpipm = get_interface_ip($cpifgrp);
551
			if (is_ipaddr($cpipm)) {
552
				$carpif = link_ip_to_carp_interface($cpipm);
553
				if (!empty($carpif)) {
554
					$carpsif = explode(" ", $carpif);
555
					foreach ($carpsif as $cpcarp) {
556
						pfSense_interface_flags($cpcarp, IFF_IPFW_FILTER);
557
						$carpip = find_interface_ip($cpcarp);
558
						if (is_ipaddr($carpip))
559
							$cpips[] = $carpip;
560
					}
561
				}
562
				$cpips[] = $cpipm;
563
				pfSense_interface_flags($tmpif, IFF_IPFW_FILTER);
564
			}
565
		}
566
	}
567
	if (count($cpips) > 0) {
568
		$cpactive = true;
569
		$cpinterface = "{ {$cpinterface} } ";
570
        } else
571
		return false;
572
573 eade409a Ermal
	if ($reinit == false)
574
		$captiveportallck = lock('captiveportal');
575
576 1d9e9cca Ermal
	/* init dummynet/ipfw rules number database */
577
	captiveportal_init_ipfw_ruleno();
578
579 769e254e Ermal
	/* make sure ipfw is loaded */
580
	if (!is_module_loaded("ipfw.ko"))
581
		filter_load_ipfw();
582
	/* Always load dummynet now that even allowed ip and mac passthrough use it. */
583
	if (!is_module_loaded("dummynet.ko"))
584
		mwexec("/sbin/kldload dummynet");
585
586 b01792a0 Ermal
	$cprules =  "add 65291 set 1 allow pfsync from any to any\n";
587
	$cprules .= "add 65292 set 1 allow carp from any to any\n";
588 181a843c Scott Ullrich
589 3db19cf1 Scott Ullrich
	$cprules .= <<<EOD
590 b01792a0 Ermal
# add 65300 set 1 skipto 65534 all from any to any not layer2
591 3db19cf1 Scott Ullrich
# layer 2: pass ARP
592 b01792a0 Ermal
add 65301 set 1 pass layer2 mac-type arp
593 b9d1d810 Scott Ullrich
# pfsense requires for WPA
594 b01792a0 Ermal
add 65302 set 1 pass layer2 mac-type 0x888e
595
add 65303 set 1 pass layer2 mac-type 0x88c7
596 684c787e Scott Ullrich
597 36254e4a Scott Ullrich
# PPP Over Ethernet Discovery Stage
598 b01792a0 Ermal
add 65304 set 1 pass layer2 mac-type 0x8863
599 684c787e Scott Ullrich
# PPP Over Ethernet Session Stage
600 b01792a0 Ermal
add 65305 set 1 pass layer2 mac-type 0x8864
601 55f5c311 Ermal Lu?i
# Allow WPA
602 b01792a0 Ermal
add 65306 set 1 pass layer2 mac-type 0x888e
603 684c787e Scott Ullrich
604 3db19cf1 Scott Ullrich
# layer 2: block anything else non-IP
605 b01792a0 Ermal
add 65307 set 1 deny layer2 not mac-type ip
606 3db19cf1 Scott Ullrich
607
EOD;
608
609 b01792a0 Ermal
	$rulenum = 65310;
610 fb516dda Chris Buechler
	$ipcount = 0;
611
	foreach ($cpips as $cpip) {
612
		if($ipcount == 0) {
613
			$ips = "{$cpip} ";
614
		} else {
615
			$ips .= "or {$cpip} ";
616
		}
617
		$ipcount++;
618
	}
619 2f27dffd Ermal
	$ips = "{ {$ips} }";
620 746e60c9 Ermal
	$cprules .= "add {$rulenum} set 1 pass ip from any to {$ips} in\n";
621 2f27dffd Ermal
	$rulenum++;
622 746e60c9 Ermal
	$cprules .= "add {$rulenum} set 1 pass ip from {$ips} to any out\n";
623 2f27dffd Ermal
	$rulenum++;
624
	$cprules .= "add {$rulenum} set 1 pass icmp from {$ips} to any out icmptype 0\n";
625
	$rulenum++;
626
	$cprules .= "add {$rulenum} set 1 pass icmp from any to {$ips} in icmptype 8 \n";
627
	$rulenum++;
628 b01792a0 Ermal
	/* Allowed ips */
629
	$cprules .= "add {$rulenum} allow ip from table(3) to any in\n";
630
	$rulenum++;
631
	$cprules .= "add {$rulenum} allow ip from any to table(4) out\n";
632
	$rulenum++;
633
	$cprules .= "add {$rulenum} pipe tablearg ip from table(5) to any in\n";
634
	$rulenum++;
635
	$cprules .= "add {$rulenum} pipe tablearg ip from any to table(6) out\n";
636
	$rulenum++;
637
	$cprules .= "add {$rulenum} allow ip from any to table(7) in\n";
638
	$rulenum++;
639
	$cprules .= "add {$rulenum} allow ip from table(8) to any out\n";
640
	$rulenum++;
641
	$cprules .= "add {$rulenum} pipe tablearg ip from any to table(9) in\n";
642
	$rulenum++;
643
	$cprules .= "add {$rulenum} pipe tablearg ip from table(10) to any out\n";
644
	$rulenum++;
645
646
	/* Authenticated users rules. */
647 f9f71ad3 Ermal Lu?i
	if (isset($config['captiveportal']['peruserbw'])) {
648 6ce61a8f Ermal
		$cprules .= "add {$rulenum} set 1 pipe tablearg ip from table(1) to any in\n";
649 f9f71ad3 Ermal Lu?i
		$rulenum++;
650 6ce61a8f Ermal
		$cprules .= "add {$rulenum} set 1 pipe tablearg ip from any to table(2) out\n";
651 f9f71ad3 Ermal Lu?i
		$rulenum++;
652
	} else {
653 6ce61a8f Ermal
		$cprules .= "add {$rulenum} set 1 allow ip from table(1) to any in\n";
654 f9f71ad3 Ermal Lu?i
                $rulenum++;
655 6ce61a8f Ermal
                $cprules .= "add {$rulenum} set 1 allow ip from any to table(2) out\n";
656 f9f71ad3 Ermal Lu?i
                $rulenum++;
657
	}
658
	
659
       $cprules .= <<<EOD
660 5480497a Scott Ullrich
661 d44bccc7 Scott Ullrich
# redirect non-authenticated clients to captive portal
662 6ce61a8f Ermal
add 65531 set 1 fwd 127.0.0.1,8000 tcp from any to any in
663 3db19cf1 Scott Ullrich
# let the responses from the captive portal web server back out
664 6ce61a8f Ermal
add 65532 set 1 pass tcp from any to any out
665 3db19cf1 Scott Ullrich
# block everything else
666 6ce61a8f Ermal
add 65533 set 1 deny all from any to any
667 3db19cf1 Scott Ullrich
# pass everything else on layer2
668 6ce61a8f Ermal
add 65534 set 1 pass all from any to any layer2
669 3db19cf1 Scott Ullrich
670
EOD;
671
672 769e254e Ermal
	/* generate passthru mac database */
673
	$cprules .= captiveportal_passthrumac_configure(true);
674
	$cprules .= "\n";
675
	/* allowed ipfw rules to make allowed ip work */
676
	$cprules .= captiveportal_allowedip_configure();
677
678
	/* load rules */
679 1d9e9cca Ermal
	if ($reinit == true)
680
		$cprules = "table all flush\nflush\n{$cprules}";
681
	else {
682
		$tmprules = "table 3 flush\n";
683
		$tmprules .= "table 4 flush\n";
684
		$tmprules .= "table 5 flush\n";
685
		$tmprules .= "table 6 flush\n";
686
		$tmprules .= "table 7 flush\n";
687
		$tmprules .= "table 8 flush\n";
688
		$tmprules .= "table 9 flush\n";
689
		$tmprules .= "table 10 flush\n";
690
		$tmprules .= "flush\n";
691
		$cprules = "{$tmprules}\n{$cprules}";
692
	}
693 eade409a Ermal
694
	file_put_contents("{$g['tmp_path']}/ipfw.cp.rules", $cprules);
695
	mwexec("/sbin/ipfw -q {$g['tmp_path']}/ipfw.cp.rules", true);
696
	@unlink("{$g['tmp_path']}/ipfw.cp.rules");
697
698
	if ($reinit == false)
699
		unlock($captiveportallck);
700
701 769e254e Ermal
702
	/* filter on layer2 as well so we can check MAC addresses */
703
	mwexec("/sbin/sysctl net.link.ether.ipfw=1");
704
705
	return $cprules;
706 3db19cf1 Scott Ullrich
}
707
708 5b237745 Scott Ullrich
/* remove clients that have been around for longer than the specified amount of time */
709 36254e4a Scott Ullrich
/* db file structure:
710 0bd34ed6 Scott Ullrich
timestamp,ipfw_rule_no,clientip,clientmac,username,sessionid,password,session_timeout,idle_timeout,session_terminate_time */
711
712 3db19cf1 Scott Ullrich
/* (password is in Base64 and only saved when reauthentication is enabled) */
713 5b237745 Scott Ullrich
function captiveportal_prune_old() {
714 0bd34ed6 Scott Ullrich
715 23c4f978 Scott Ullrich
    global $g, $config;
716
717
    /* check for expired entries */
718
    if ($config['captiveportal']['timeout'])
719
        $timeout = $config['captiveportal']['timeout'] * 60;
720
    else
721
        $timeout = 0;
722
723
    if ($config['captiveportal']['idletimeout'])
724
        $idletimeout = $config['captiveportal']['idletimeout'] * 60;
725
    else
726
        $idletimeout = 0;
727
728 336e3c1c Charlie
    if (!$timeout && !$idletimeout && !isset($config['captiveportal']['reauthenticate']) && 
729
		!isset($config['captiveportal']['radiussession_timeout']) && !isset($config['voucher']['enable']))
730 23c4f978 Scott Ullrich
        return;
731
732 dedf51a2 Ermal Lu?i
    $captiveportallck = lock('captiveportal');
733 23c4f978 Scott Ullrich
734
    /* read database */
735
    $cpdb = captiveportal_read_db();
736
737
    $radiusservers = captiveportal_get_radius_servers();
738
739 2d53158f stompro
    /*  To make sure we iterate over ALL accounts on every run the count($cpdb) is moved
740
     *  outside of the loop. Otherwise the loop would evaluate count() on every iteration
741
     *  and since $i would increase and count() would decrement they would meet before we
742
     *  had a chance to iterate over all accounts.
743 f56a73f1 Scott Ullrich
     */
744 8e51cc6a Ermal Lu?i
    $unsetindexes = array();
745 f56a73f1 Scott Ullrich
    $no_users = count($cpdb);
746
    for ($i = 0; $i < $no_users; $i++) {
747 23c4f978 Scott Ullrich
748
        $timedout = false;
749
        $term_cause = 1;
750
751
        /* hard timeout? */
752
        if ($timeout) {
753
            if ((time() - $cpdb[$i][0]) >= $timeout) {
754
                $timedout = true;
755
                $term_cause = 5; // Session-Timeout
756
            }
757
        }
758
759
        /* Session-Terminate-Time */
760
        if (!$timedout && !empty($cpdb[$i][9])) {
761
            if (time() >= $cpdb[$i][9]) {
762
                $timedout = true;
763
                $term_cause = 5; // Session-Timeout
764
            }
765
        }
766
767
        /* check if the radius idle_timeout attribute has been set and if its set change the idletimeout to this value */
768
        $idletimeout = (is_numeric($cpdb[$i][8])) ? $cpdb[$i][8] : $idletimeout;
769
        /* if an idle timeout is specified, get last activity timestamp from ipfw */
770
        if (!$timedout && $idletimeout) {
771 f9f71ad3 Ermal Lu?i
            $lastact = captiveportal_get_last_activity($cpdb[$i][2]);
772 2d53158f stompro
			/*  If the user has logged on but not sent any traffic they will never be logged out.
773
			 *  We "fix" this by setting lastact to the login timestamp. 
774 f56a73f1 Scott Ullrich
			 */
775
			$lastact = $lastact ? $lastact : $cpdb[$i][0];
776 23c4f978 Scott Ullrich
            if ($lastact && ((time() - $lastact) >= $idletimeout)) {
777
                $timedout = true;
778
                $term_cause = 4; // Idle-Timeout
779
                $stop_time = $lastact; // Entry added to comply with WISPr
780
            }
781
        }
782
783 336e3c1c Charlie
	/* if vouchers are configured, activate session timeouts */
784
	if (!$timedout && isset($config['voucher']['enable']) && !empty($cpdb[$i][7])) {
785
		if (time() >= ($cpdb[$i][0] + $cpdb[$i][7])) {
786
			$timedout = true;
787
			$term_cause = 5; // Session-Timeout
788
		}
789
	}
790
791 23c4f978 Scott Ullrich
        /* if radius session_timeout is enabled and the session_timeout is not null, then check if the user should be logged out */
792
        if (!$timedout && isset($config['captiveportal']['radiussession_timeout']) && !empty($cpdb[$i][7])) {
793
            if (time() >= ($cpdb[$i][0] + $cpdb[$i][7])) {
794
                $timedout = true;
795
                $term_cause = 5; // Session-Timeout
796
            }
797
        }
798
799
        if ($timedout) {
800
            captiveportal_disconnect($cpdb[$i], $radiusservers,$term_cause,$stop_time);
801
            captiveportal_logportalauth($cpdb[$i][4], $cpdb[$i][3], $cpdb[$i][2], "TIMEOUT");
802 8e51cc6a Ermal Lu?i
	    $unsetindexes[$i] = $i;
803 23c4f978 Scott Ullrich
        }
804
805
        /* do periodic RADIUS reauthentication? */
806
        if (!$timedout && isset($config['captiveportal']['reauthenticate']) &&
807 40b48c6c Ermal Lu?i
            !empty($radiusservers)) {
808 23c4f978 Scott Ullrich
809
            if (isset($config['captiveportal']['radacct_enable'])) {
810
                if ($config['captiveportal']['reauthenticateacct'] == "stopstart") {
811
                    /* stop and restart accounting */
812
                    RADIUS_ACCOUNTING_STOP($cpdb[$i][1], // ruleno
813
                                           $cpdb[$i][4], // username
814
                                           $cpdb[$i][5], // sessionid
815
                                           $cpdb[$i][0], // start time
816 40b48c6c Ermal Lu?i
                                           $radiusservers,
817 23c4f978 Scott Ullrich
                                           $cpdb[$i][2], // clientip
818
                                           $cpdb[$i][3], // clientmac
819
                                           10); // NAS Request
820 6ce61a8f Ermal
                    exec("/sbin/ipfw table 1 entryzerostats {$cpdb[$i][2]}");
821
                    exec("/sbin/ipfw table 2 entryzerostats {$cpdb[$i][2]}");
822 23c4f978 Scott Ullrich
                    RADIUS_ACCOUNTING_START($cpdb[$i][1], // ruleno
823
                                            $cpdb[$i][4], // username
824
                                            $cpdb[$i][5], // sessionid
825 40b48c6c Ermal Lu?i
                                            $radiusservers,
826 23c4f978 Scott Ullrich
                                            $cpdb[$i][2], // clientip
827
                                            $cpdb[$i][3]); // clientmac
828
                } else if ($config['captiveportal']['reauthenticateacct'] == "interimupdate") {
829
                    RADIUS_ACCOUNTING_STOP($cpdb[$i][1], // ruleno
830
                                           $cpdb[$i][4], // username
831
                                           $cpdb[$i][5], // sessionid
832
                                           $cpdb[$i][0], // start time
833 40b48c6c Ermal Lu?i
                                           $radiusservers,
834 23c4f978 Scott Ullrich
                                           $cpdb[$i][2], // clientip
835
                                           $cpdb[$i][3], // clientmac
836
                                           10, // NAS Request
837
                                           true); // Interim Updates
838
                }
839
            }
840
841
            /* check this user against RADIUS again */
842
            $auth_list = RADIUS_AUTHENTICATION($cpdb[$i][4], // username
843
                                          base64_decode($cpdb[$i][6]), // password
844
                                            $radiusservers,
845
                                          $cpdb[$i][2], // clientip
846
                                          $cpdb[$i][3], // clientmac
847
                                          $cpdb[$i][1]); // ruleno
848
849
            if ($auth_list['auth_val'] == 3) {
850
                captiveportal_disconnect($cpdb[$i], $radiusservers, 17);
851
                captiveportal_logportalauth($cpdb[$i][4], $cpdb[$i][3], $cpdb[$i][2], "RADIUS_DISCONNECT", $auth_list['reply_message']);
852 8e51cc6a Ermal Lu?i
	        $unsetindexes[$i] = $i;
853 23c4f978 Scott Ullrich
            }
854
        }
855
    }
856 8e51cc6a Ermal Lu?i
    /* This is a kludge to overcome some php weirdness */
857
    foreach($unsetindexes as $unsetindex)
858
	unset($cpdb[$unsetindex]);
859 23c4f978 Scott Ullrich
860
    /* write database */
861
    captiveportal_write_db($cpdb);
862
863 dedf51a2 Ermal Lu?i
    unlock($captiveportallck);
864 5b237745 Scott Ullrich
}
865
866 3db19cf1 Scott Ullrich
/* remove a single client according to the DB entry */
867 0bd34ed6 Scott Ullrich
function captiveportal_disconnect($dbent, $radiusservers,$term_cause = 1,$stop_time = null) {
868 36254e4a Scott Ullrich
869 d99f7864 Scott Ullrich
	global $g, $config;
870
871
	$stop_time = (empty($stop_time)) ? time() : $stop_time;
872
873
	/* this client needs to be deleted - remove ipfw rules */
874 40b48c6c Ermal Lu?i
	if (isset($config['captiveportal']['radacct_enable']) && !empty($radiusservers)) {
875 d99f7864 Scott Ullrich
		RADIUS_ACCOUNTING_STOP($dbent[1], // ruleno
876
							   $dbent[4], // username
877
							   $dbent[5], // sessionid
878
							   $dbent[0], // start time
879 40b48c6c Ermal Lu?i
							   $radiusservers,
880 d99f7864 Scott Ullrich
							   $dbent[2], // clientip
881
							   $dbent[3], // clientmac
882
							   $term_cause, // Acct-Terminate-Cause
883
							   false,
884
							   $stop_time);
885
	}
886 2d53158f stompro
	/* Delete client's ip entry from tables 3 and 4. */
887 6ce61a8f Ermal
	mwexec("/sbin/ipfw table 1 delete {$dbent[2]}");
888
	mwexec("/sbin/ipfw table 2 delete {$dbent[2]}");
889 1dbe445a Ermal
890 6ce61a8f Ermal
	/* Release the ruleno so it can be reallocated to new clients. */
891
	captiveportal_free_ipfw_ruleno($dbent[1]);
892 f9f71ad3 Ermal Lu?i
893
	/* 
894
	* These are the pipe numbers we use to control traffic shaping for each logged in user via captive portal
895
	* We could get an error if the pipe doesn't exist but everything should still be fine
896
	*/
897
	if (isset($config['captiveportal']['peruserbw'])) {
898
		mwexec("/sbin/ipfw pipe " . ($dbent[1]+20000) . " delete");
899
		mwexec("/sbin/ipfw pipe " . ($dbent[1]+20001) . " delete");
900
	}
901 7a7abeba Scott Ullrich
902 6ce61a8f Ermal
	/* XXX: Redundant?! Ensure all pf(4) states are killed. */
903 7a7abeba Scott Ullrich
	mwexec("pfctl -k {$dbent[2]}");
904 b1cc2eb2 Ermal Luçi
	mwexec("pfctl -K {$dbent[2]}");
905 7a7abeba Scott Ullrich
906 3db19cf1 Scott Ullrich
}
907 12ee8fe4 Scott Ullrich
908 3db19cf1 Scott Ullrich
/* remove a single client by ipfw rule number */
909 0bd34ed6 Scott Ullrich
function captiveportal_disconnect_client($id,$term_cause = 1) {
910 36254e4a Scott Ullrich
911 d99f7864 Scott Ullrich
	global $g, $config;
912 36254e4a Scott Ullrich
913 dedf51a2 Ermal Lu?i
	$captiveportallck = lock('captiveportal');
914 36254e4a Scott Ullrich
915 d99f7864 Scott Ullrich
	/* read database */
916
	$cpdb = captiveportal_read_db();
917
	$radiusservers = captiveportal_get_radius_servers();
918
919
	/* find entry */
920 889b0934 Ermal Lu?i
	$tmpindex = 0;
921 6ce61a8f Ermal
	$cpdbcount = count($cpdb);
922
	for ($i = 0; $i < $cpdbcount; $i++) {
923 d99f7864 Scott Ullrich
		if ($cpdb[$i][1] == $id) {
924
			captiveportal_disconnect($cpdb[$i], $radiusservers, $term_cause);
925
			captiveportal_logportalauth($cpdb[$i][4], $cpdb[$i][3], $cpdb[$i][2], "DISCONNECT");
926 dd35bb5a Chris Buechler
			unset($cpdb[$i]);
927 d99f7864 Scott Ullrich
			break;
928
		}
929 dd35bb5a Chris Buechler
	}		
930 36254e4a Scott Ullrich
931 d99f7864 Scott Ullrich
	/* write database */
932
	captiveportal_write_db($cpdb);
933 36254e4a Scott Ullrich
934 dedf51a2 Ermal Lu?i
	unlock($captiveportallck);
935 5b237745 Scott Ullrich
}
936
937
/* send RADIUS acct stop for all current clients */
938 dedf51a2 Ermal Lu?i
function captiveportal_radius_stop_all($lock = false) {
939 d99f7864 Scott Ullrich
	global $g, $config;
940
941
	if (!isset($config['captiveportal']['radacct_enable']))
942
		return;
943
944 90455aeb Ermal Lu?i
	if (!$lock)
945 dedf51a2 Ermal Lu?i
		$captiveportallck = lock('captiveportal');
946
947 d99f7864 Scott Ullrich
	$cpdb = captiveportal_read_db();
948
949
	$radiusservers = captiveportal_get_radius_servers();
950 40b48c6c Ermal Lu?i
	if (!empty($radiusservers)) {
951 d99f7864 Scott Ullrich
		for ($i = 0; $i < count($cpdb); $i++) {
952
			RADIUS_ACCOUNTING_STOP($cpdb[$i][1], // ruleno
953
								   $cpdb[$i][4], // username
954
								   $cpdb[$i][5], // sessionid
955
								   $cpdb[$i][0], // start time
956 40b48c6c Ermal Lu?i
								   $radiusservers,
957 d99f7864 Scott Ullrich
								   $cpdb[$i][2], // clientip
958
								   $cpdb[$i][3], // clientmac
959
								   7); // Admin Reboot
960
		}
961
	}
962 90455aeb Ermal Lu?i
	if (!$lock)
963 dedf51a2 Ermal Lu?i
		unlock($captiveportallck);
964 5b237745 Scott Ullrich
}
965
966 d5ae560d Ermal
function captiveportal_passthrumac_configure_entry($macent) {
967
	$rules = "";
968
        $enBwup = isset($macent['bw_up']);
969
        $enBwdown = isset($macent['bw_down']);
970
	$actionup = "allow";
971
	$actiondown = "allow";
972
973
        if ($enBwup && $enBwdown)
974
                $ruleno = captiveportal_get_next_ipfw_ruleno(2000, 49899, true);
975
        else
976
                $ruleno = captiveportal_get_next_ipfw_ruleno(2000, 49899, false);
977
978
	if ($enBwup) {
979
                $bw_up = $ruleno + 20000;
980
                $rules .= "pipe {$bw_up} config bw {$macent['bw_up']}Kbit/s queue 100\n";
981
		$actionup = "pipe {$bw_up}";
982
        }
983
        if ($enBwdown) {
984
		$bw_down = $ruleno + 20001;
985
		$rules .= "pipe {$bw_down} config bw {$macent['bw_down']}Kbit/s queue 100\n";
986
		$actiondown = "pipe {$bw_down}";
987
        }
988 ffcf81bb Chris Buechler
	$rules .= "add {$ruleno} {$actiondown} ip from any to any MAC {$macent['mac']} any\n";
989 d5ae560d Ermal
	$ruleno++;
990 ffcf81bb Chris Buechler
	$rules .= "add {$ruleno} {$actionup} ip from any to any MAC any {$macent['mac']}\n";
991 d5ae560d Ermal
992
	return $rules;
993
}
994
995 dedf51a2 Ermal Lu?i
function captiveportal_passthrumac_configure($lock = false) {
996 5b237745 Scott Ullrich
	global $config, $g;
997 36254e4a Scott Ullrich
998 d5ae560d Ermal
	$rules = "";
999 36254e4a Scott Ullrich
1000 5b237745 Scott Ullrich
	if (is_array($config['captiveportal']['passthrumac'])) {
1001 1dbe445a Ermal
		$macdb = array();
1002 5b237745 Scott Ullrich
		foreach ($config['captiveportal']['passthrumac'] as $macent) {
1003 d5ae560d Ermal
			$rules .= captiveportal_passthrumac_configure_entry($macent);
1004 1dbe445a Ermal
			$macdb[$macent['mac']]['active']  = true;
1005
1006 5b237745 Scott Ullrich
		}
1007
	}
1008 0bd34ed6 Scott Ullrich
1009 d5ae560d Ermal
	return $rules;
1010 5b237745 Scott Ullrich
}
1011
1012 fac13a5e Ermal
function captiveportal_passthrumac_findbyname($username) {
1013
	global $config;
1014
1015
	if (is_array($config['captiveportal']['passthrumac'])) {
1016
		foreach ($config['captiveportal']['passthrumac'] as $macent) {
1017
			if ($macent['username'] == $username)
1018
				return $macent;
1019
		}
1020
	}
1021
	return NULL;
1022
}
1023
1024 b01792a0 Ermal
/* 
1025
 * table (3=IN)/(4=OUT) hold allowed ip's without bw limits
1026
 * table (5=IN)/(6=OUT) hold allowed ip's with bw limit.
1027
 */
1028
function captiveportal_allowedip_configure_entry($ipent) {
1029
1030
	$rules = "";
1031
	$enBwup = isset($ipent['bw_up']);
1032
	$enBwdown = isset($ipent['bw_down']);
1033
	$bw_up = "";
1034
        $bw_down = "";
1035
        $tablein = array();
1036
        $tableout = array();
1037
1038
	if ($enBwup && $enBwdown)
1039
		$ruleno = captiveportal_get_next_ipfw_ruleno(2000, 49899, true);
1040
	else
1041
		$ruleno = captiveportal_get_next_ipfw_ruleno(2000, 49899, false);
1042
1043
        if ($ipent['dir'] == "from") {
1044
        	if ($enBwup)
1045
                	$tablein[] = 5;
1046
                else
1047
                	$tablein[] = 3;
1048
                if ($enBwdown)
1049
                        $tableout[] = 6;
1050
                else
1051
                        $tableout[] = 4;
1052
        } else if ($ipent['dir'] == "to") {
1053
                if ($enBwup)
1054
                	$tablein[] = 9;
1055
                else
1056
                        $tablein[] = 7;
1057
                if ($enBwdown)
1058
                        $tableout[] = 10;
1059
                else
1060
                        $tableout[] = 8;
1061
        } else if ($ipent['dir'] == "both") {
1062
                if ($enBwup) {
1063
                        $tablein[] = 5;
1064
                        $tablein[] = 9;
1065
                } else {
1066
                        $tablein[] = 3;
1067
                        $tablein[] = 7;
1068
                }
1069
        	if ($enBwdown) {
1070
                        $tableout[] = 6;
1071
                        $tableout[] = 10;
1072
                } else {
1073
                        $tableout[] = 4;
1074
                	$tableout[] = 8;
1075
                }
1076
        }
1077
        if ($enBwup) {
1078
                $bw_up = $ruleno + 20000;
1079
        	$rules .= "pipe {$bw_up} config bw {$ipent['bw_up']}Kbit/s queue 100\n";
1080
        }
1081 d6a0379d Ermal
	$subnet = "";
1082
	if (!empty($ipent['sn']))
1083
		$subnet = "/{$ipent['sn']}";
1084 b01792a0 Ermal
	foreach ($tablein as $table)
1085 d6a0379d Ermal
               $rules .= "table {$table} add {$ipent['ip']}{$subnet} {$bw_up}\n";
1086 b01792a0 Ermal
        if ($enBwdown) {
1087
               $bw_down = $ruleno + 20001;
1088
               $rules .= "pipe {$bw_down} config bw {$ipent['bw_down']}Kbit/s queue 100\n";
1089
        }
1090
        foreach ($tableout as $table)
1091 d6a0379d Ermal
        	$rules .= "table {$table} add {$ipent['ip']}{$subnet} {$bw_down}\n";
1092 b01792a0 Ermal
1093
	return $rules;
1094
}
1095
1096 cb0a2913 Ermal Lu?i
function captiveportal_allowedip_configure() {
1097 5b237745 Scott Ullrich
	global $config, $g;
1098 36254e4a Scott Ullrich
1099 6ce61a8f Ermal
	$rules = "";
1100 5b237745 Scott Ullrich
	if (is_array($config['captiveportal']['allowedip'])) {
1101 cb0a2913 Ermal Lu?i
		foreach ($config['captiveportal']['allowedip'] as $ipent) {
1102 b01792a0 Ermal
			$rules .= captiveportal_allowedip_configure_entry($ipent);
1103 cb0a2913 Ermal Lu?i
		}
1104
	}
1105 36254e4a Scott Ullrich
1106 6ce61a8f Ermal
	return $rules;
1107 5b237745 Scott Ullrich
}
1108
1109 2d53158f stompro
/* get last activity timestamp given client IP address */
1110 f9f71ad3 Ermal Lu?i
function captiveportal_get_last_activity($ip) {
1111 36254e4a Scott Ullrich
1112 d99f7864 Scott Ullrich
	$ipfwoutput = "";
1113 36254e4a Scott Ullrich
1114 6ce61a8f Ermal
	exec("/sbin/ipfw table 1 entrystats {$ip} 2>/dev/null", $ipfwoutput);
1115 f9f71ad3 Ermal Lu?i
	/* Reading only from one of the tables is enough of approximation. */
1116 d99f7864 Scott Ullrich
	if ($ipfwoutput[0]) {
1117
		$ri = explode(" ", $ipfwoutput[0]);
1118 f9f71ad3 Ermal Lu?i
		if ($ri[4])
1119
			return $ri[4];
1120 d99f7864 Scott Ullrich
	}
1121 36254e4a Scott Ullrich
1122 d99f7864 Scott Ullrich
	return 0;
1123 5b237745 Scott Ullrich
}
1124
1125
/* read RADIUS servers into array */
1126
function captiveportal_get_radius_servers() {
1127 0bd34ed6 Scott Ullrich
1128
        global $g;
1129
1130
        if (file_exists("{$g['vardb_path']}/captiveportal_radius.db")) {
1131 2f70eac7 Ermal Lu?i
                $radiusservers = array();
1132 a48acf9a Ermal Lu?i
		$cpradiusdb = file("{$g['vardb_path']}/captiveportal_radius.db", 
1133
			FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
1134
		if ($cpradiusdb)
1135 2f70eac7 Ermal Lu?i
		foreach($cpradiusdb as $cpradiusentry) {
1136
                	$line = trim($cpradiusentry);
1137
                        if ($line) {
1138
                        	$radsrv = array();
1139
                                list($radsrv['ipaddr'],$radsrv['port'],$radsrv['acctport'],$radsrv['key']) = explode(",",$line);
1140
                        	$radiusservers[] = $radsrv;
1141 0bd34ed6 Scott Ullrich
                        }
1142 2f70eac7 Ermal Lu?i
		}
1143 0bd34ed6 Scott Ullrich
1144 2f70eac7 Ermal Lu?i
		return $radiusservers;
1145 0bd34ed6 Scott Ullrich
        }
1146
1147
        return false;
1148 5b237745 Scott Ullrich
}
1149
1150 3db19cf1 Scott Ullrich
/* log successful captive portal authentication to syslog */
1151
/* part of this code from php.net */
1152 0bd34ed6 Scott Ullrich
function captiveportal_logportalauth($user,$mac,$ip,$status, $message = null) {
1153 d99f7864 Scott Ullrich
	$message = trim($message);
1154
	// Log it
1155
	if (!$message)
1156 f56a73f1 Scott Ullrich
		$message = "$status: $user, $mac, $ip";
1157 d99f7864 Scott Ullrich
	else
1158 f56a73f1 Scott Ullrich
		$message = "$status: $user, $mac, $ip, $message";
1159
	captiveportal_syslog($message);
1160
	closelog();
1161
}
1162
1163
/* log simple messages to syslog */
1164
function captiveportal_syslog($message) {
1165
	define_syslog_variables();
1166
	$message = trim($message);
1167
	openlog("logportalauth", LOG_PID, LOG_LOCAL4);
1168
	// Log it
1169
	syslog(LOG_INFO, $message);
1170 d99f7864 Scott Ullrich
	closelog();
1171 3db19cf1 Scott Ullrich
}
1172
1173 0bd34ed6 Scott Ullrich
function radius($username,$password,$clientip,$clientmac,$type) {
1174 d44bccc7 Scott Ullrich
    global $g, $config;
1175 d99f7864 Scott Ullrich
1176 d44bccc7 Scott Ullrich
    /* Start locking from the beginning of an authentication session */
1177 dedf51a2 Ermal Lu?i
    $captiveportallck = lock('captiveportal');
1178 0bd34ed6 Scott Ullrich
1179 d44bccc7 Scott Ullrich
    $ruleno = captiveportal_get_next_ipfw_ruleno();
1180
1181 2d53158f stompro
    /* If the pool is empty, return appropriate message and fail authentication */
1182 d44bccc7 Scott Ullrich
    if (is_null($ruleno)) {
1183
        $auth_list = array();
1184
        $auth_list['auth_val'] = 1;
1185
        $auth_list['error'] = "System reached maximum login capacity";
1186 dedf51a2 Ermal Lu?i
        unlock($captiveportallck);
1187 d44bccc7 Scott Ullrich
        return $auth_list;
1188
    }
1189
1190 2f70eac7 Ermal Lu?i
    /*
1191
     * Drop the lock since radius takes some time to finish.
1192
     * The implementation is reentrant so we gain speed with this.
1193
     */
1194
    unlock($captiveportallck);
1195
1196 d44bccc7 Scott Ullrich
    $radiusservers = captiveportal_get_radius_servers();
1197
1198
    $auth_list = RADIUS_AUTHENTICATION($username,
1199
                    $password,
1200
                    $radiusservers,
1201
                    $clientip,
1202
                    $clientmac,
1203
                    $ruleno);
1204
1205 2f70eac7 Ermal Lu?i
    $captiveportallck = lock('captiveportal');
1206
1207 d44bccc7 Scott Ullrich
    if ($auth_list['auth_val'] == 2) {
1208
        captiveportal_logportalauth($username,$clientmac,$clientip,$type);
1209
        $sessionid = portal_allow($clientip,
1210
                    $clientmac,
1211
                    $username,
1212
                    $password,
1213
                    $auth_list,
1214
                    $ruleno);
1215
    }
1216 9befcca7 Ermal Lu?i
1217 dedf51a2 Ermal Lu?i
    unlock($captiveportallck);
1218 d44bccc7 Scott Ullrich
1219
    return $auth_list;
1220 0bd34ed6 Scott Ullrich
1221
}
1222
1223
/* read captive portal DB into array */
1224
function captiveportal_read_db() {
1225
1226
        global $g;
1227
1228
        $cpdb = array();
1229
        $fd = @fopen("{$g['vardb_path']}/captiveportal.db", "r");
1230
        if ($fd) {
1231
                while (!feof($fd)) {
1232
                        $line = trim(fgets($fd));
1233
                        if ($line) {
1234
                                $cpdb[] = explode(",", $line);
1235
                        }
1236
                }
1237
                fclose($fd);
1238
        }
1239
        return $cpdb;
1240
}
1241
1242
/* write captive portal DB */
1243
function captiveportal_write_db($cpdb) {
1244 d44bccc7 Scott Ullrich
                 
1245 0bd34ed6 Scott Ullrich
        global $g;
1246 d44bccc7 Scott Ullrich
                
1247 0bd34ed6 Scott Ullrich
        $fd = @fopen("{$g['vardb_path']}/captiveportal.db", "w");
1248 d44bccc7 Scott Ullrich
        if ($fd) { 
1249 0bd34ed6 Scott Ullrich
                foreach ($cpdb as $cpent) {
1250
                        fwrite($fd, join(",", $cpent) . "\n");
1251 d44bccc7 Scott Ullrich
                }       
1252 0bd34ed6 Scott Ullrich
                fclose($fd);
1253 d44bccc7 Scott Ullrich
        }       
1254 0bd34ed6 Scott Ullrich
}
1255
1256
function captiveportal_write_elements() {
1257 769e254e Ermal
	global $g, $config;
1258 d44bccc7 Scott Ullrich
    
1259 769e254e Ermal
	/* delete any existing elements */
1260
	if (is_dir($g['captiveportal_element_path'])) {
1261
		$dh = opendir($g['captiveportal_element_path']);
1262
		while (($file = readdir($dh)) !== false) {
1263
			if ($file != "." && $file != "..")
1264
				unlink($g['captiveportal_element_path'] . "/" . $file);
1265
		}
1266
		closedir($dh);
1267
	} else
1268
		@mkdir($g['captiveportal_element_path']);
1269
1270 1fadb31d Scott Ullrich
	if (is_array($config['captiveportal']['element'])) {
1271
		conf_mount_rw();
1272
		foreach ($config['captiveportal']['element'] as $data) {
1273
			$fd = @fopen($g['captiveportal_element_path'] . '/' . $data['name'], "wb");
1274
			if (!$fd) {
1275
				printf("Error: cannot open '{$data['name']}' in captiveportal_write_elements().\n");
1276
				return 1;
1277
			}
1278
			$decoded = base64_decode($data['content']);
1279
			fwrite($fd,$decoded);
1280
			fclose($fd);
1281
			unlink_if_exists("{$g['captiveportal_path']}/{$data['name']}");
1282
			unlink_if_exists("{$g['captiveportal_path']}/{$data['name']}");
1283
			mwexec("cd {$g['captiveportal_path']}/ && ln -s {$g['captiveportal_element_path']}/{$data['name']} {$data['name']}");
1284
		}
1285
		conf_mount_ro();
1286
	}
1287 d44bccc7 Scott Ullrich
    
1288 769e254e Ermal
	return 0;
1289 0bd34ed6 Scott Ullrich
}
1290
1291 6ce61a8f Ermal
function captiveportal_init_ipfw_ruleno($rulenos_start = 2000, $rulenos_range_max = 49899) {
1292
	global $g;
1293
1294
	@unlink("{$g['vardb_path']}/captiveportal.rules");
1295
	$rules = array_pad(array(), $rulenos_range_max - $rulenos_start, false);
1296
	file_put_contents("{$g['vardb_path']}/captiveportal.rules", serialize($rules));
1297
}
1298
1299 920cafaf Scott Ullrich
/*
1300
 * This function will calculate the lowest free firewall ruleno
1301 f9f71ad3 Ermal Lu?i
 * within the range specified based on the actual logged on users
1302 920cafaf Scott Ullrich
 *
1303
 */
1304 b01792a0 Ermal
function captiveportal_get_next_ipfw_ruleno($rulenos_start = 2000, $rulenos_range_max = 49899, $usebw = false) {
1305 f9f71ad3 Ermal Lu?i
	global $config, $g;
1306 6ce61a8f Ermal
1307 de752609 Scott Ullrich
	if(!isset($config['captiveportal']['enable']))
1308 01d57b8c Scott Ullrich
		return NULL;
1309 6ce61a8f Ermal
1310 f9f71ad3 Ermal Lu?i
	$ruleno = 0;
1311 6ce61a8f Ermal
	if (file_exists("{$g['vardb_path']}/captiveportal.rules")) {
1312
		$rules = unserialize(file_get_contents("{$g['vardb_path']}/captiveportal.rules"));
1313
		for ($ridx = 2; $ridx < ($rulenos_range_max - $rulenos_start); $ridx++) {
1314
			if ($rules[$ridx]) {
1315
				/* 
1316
	 			 * This allows our traffic shaping pipes to be the in pipe the same as ruleno 
1317
	 			 * and the out pipe ruleno + 1. This removes limitation that where present in 
1318
	 			 * previous version of the peruserbw.
1319
	 			 */
1320
				if (isset($config['captiveportal']['peruserbw']))
1321
					$ridx++;
1322
				continue;
1323
			}
1324
			$ruleno = $ridx;
1325
			$rules[$ridx] = "used";
1326 b01792a0 Ermal
			if (isset($config['captiveportal']['peruserbw']) || $usebw == true)
1327 6ce61a8f Ermal
				$rules[++$ridx] = "used";
1328
			break;
1329
		}
1330
	} else {
1331
		$rules = array_pad(array(), $rulenos_range_max - $rulenos_start, false);
1332
		$rules[2] = "used";
1333
		$ruleno = 2;
1334
	}
1335
	file_put_contents("{$g['vardb_path']}/captiveportal.rules", serialize($rules));
1336
	return $ruleno;
1337
}
1338
1339 b01792a0 Ermal
function captiveportal_free_ipfw_ruleno($ruleno, $usedbw = false) {
1340 6ce61a8f Ermal
	global $config, $g;
1341
1342
	if(!isset($config['captiveportal']['enable']))
1343
		return NULL;
1344
1345
	if (file_exists("{$g['vardb_path']}/captiveportal.rules")) {
1346
		$rules = unserialize(file_get_contents("{$g['vardb_path']}/captiveportal.rules"));
1347
		$rules[$ruleno] = false;
1348 b01792a0 Ermal
		if (isset($config['captiveportal']['peruserbw']) || $usedbw == true)
1349 6ce61a8f Ermal
			$rules[++$ruleno] = false;
1350
		file_put_contents("{$g['vardb_path']}/captiveportal.rules", serialize($rules));
1351 f9f71ad3 Ermal Lu?i
	}
1352 6ce61a8f Ermal
}
1353
1354 d5ae560d Ermal
function captiveportal_get_ipfw_passthru_ruleno($value) {
1355 6ce61a8f Ermal
	global $config, $g;
1356
1357
	if(!isset($config['captiveportal']['enable']))
1358
                return NULL;
1359
1360
        if (file_exists("{$g['vardb_path']}/captiveportal.rules")) {
1361
                $rules = unserialize(file_get_contents("{$g['vardb_path']}/captiveportal.rules"));
1362 d5ae560d Ermal
		$ruleno = intval(`/sbin/ipfw show | /usr/bin/grep {$value} |  /usr/bin/grep -v grep | /usr/bin/cut -d " " -f 1 | /usr/bin/head -n 1`);
1363 6ce61a8f Ermal
		if ($rules[$ruleno])
1364
			return $ruleno;
1365
        }
1366
1367 f9f71ad3 Ermal Lu?i
	return NULL;
1368 920cafaf Scott Ullrich
}
1369
1370 360d815d Scott Ullrich
/**
1371
 * This function will calculate the traffic produced by a client
1372
 * based on its firewall rule
1373
 *
1374
 * Point of view: NAS
1375
 *
1376
 * Input means: from the client
1377
 * Output means: to the client
1378
 *
1379
 */
1380
1381 f9f71ad3 Ermal Lu?i
function getVolume($ip) {
1382 360d815d Scott Ullrich
1383
    $volume = array();
1384
1385
    // Initialize vars properly, since we don't want NULL vars
1386
    $volume['input_pkts'] = $volume['input_bytes'] = $volume['output_pkts'] = $volume['output_bytes'] = 0 ;
1387
1388
    // Ingress
1389 f9f71ad3 Ermal Lu?i
    $ipfwin = "";
1390
    $ipfwout = "";
1391
    $matchesin = "";
1392
    $matchesout = "";
1393 6ce61a8f Ermal
    exec("/sbin/ipfw table 1 entrystats {$ip}", $ipfwin);
1394 f9f71ad3 Ermal Lu?i
    if ($ipfwin[0]) {
1395 523855b0 Scott Ullrich
		$ipfwin = split(" ", $ipfwin[0]);
1396
		$volume['input_pkts'] = $ipfwin[2];
1397
		$volume['input_bytes'] = $ipfwin[3];
1398 f9f71ad3 Ermal Lu?i
    }
1399
1400 6ce61a8f Ermal
    exec("/sbin/ipfw table 2 entrystats {$ip}", $ipfwout);
1401 f9f71ad3 Ermal Lu?i
    if ($ipfwout[0]) {
1402
        $ipfwout = split(" ", $ipfwout[0]);
1403
        $volume['output_pkts'] = $ipfwout[2];
1404
        $volume['output_bytes'] = $ipfwout[3];
1405
    }
1406 360d815d Scott Ullrich
1407
    return $volume;
1408
}
1409
1410 856e58a6 Scott Ullrich
/**
1411
 * Get the NAS-Identifier
1412
 *
1413
 * We will use our local hostname to make up the nas_id
1414
 */
1415
function getNasID()
1416
{
1417 84e5047d Scott Ullrich
    $nasId = "";
1418 856e58a6 Scott Ullrich
    exec("/bin/hostname", $nasId);
1419
    if(!$nasId[0])
1420 36d0358b Scott Ullrich
        $nasId[0] = "{$g['product_name']}";
1421 856e58a6 Scott Ullrich
    return $nasId[0];
1422
}
1423
1424
/**
1425
 * Get the NAS-IP-Address based on the current wan address
1426
 *
1427
 * Use functions in interfaces.inc to find this out
1428
 *
1429
 */
1430
1431
function getNasIP()
1432
{
1433 64c0462b Ermal
	global $config;
1434
1435 36ff7f81 Ermal
	if (empty($config['captiveportal']['radiussrcip_attribute']))
1436
    		$nasIp = get_interface_ip();
1437
	else {
1438 4a756e9b Ermal
		if (is_ipaddr($config['captiveportal']['radiussrcip_attribute']))
1439
                        $nasIp = $config['captiveportal']['radiussrcip_attribute'];
1440
                else
1441
                        $nasIp = get_interface_ip($config['captiveportal']['radiussrcip_attribute']);
1442 36ff7f81 Ermal
	}
1443 64c0462b Ermal
		
1444
    	if(!is_ipaddr($nasIp))
1445
        	$nasIp = "0.0.0.0";
1446
1447
	return $nasIp;
1448 856e58a6 Scott Ullrich
}
1449
1450 f8b11310 Ermal Lu?i
function portal_ip_from_client_ip($cliip) {
1451
	global $config;
1452
1453
	$interfaces = explode(",", $config['captiveportal']['interface']);
1454
	foreach ($interfaces as $cpif) {
1455
		$ip = get_interface_ip($cpif);
1456
		$sn = get_interface_subnet($cpif);
1457
		if (ip_in_subnet($cliip, "{$ip}/{$sn}"))
1458
			return $ip;
1459
	}
1460
1461 cc125e13 Chris Buechler
	// doesn't match up to any particular interface
1462
	// so let's set the portal IP to what PHP says 
1463
	// the server IP issuing the request is. 
1464
	// allows same behavior as 1.2.x where IP isn't 
1465
	// in the subnet of any CP interface (static routes, etc.)
1466
	// rather than forcing to DNS hostname resolution
1467
	$ip = $_SERVER['SERVER_ADDR'];
1468
	if (is_ipaddr($ip))
1469
		return $ip;
1470
1471 f8b11310 Ermal Lu?i
	return false;
1472
}
1473
1474 b260c8e0 Scott Ullrich
?>