mirror of
				https://github.com/Telecominfraproject/wlan-ap.git
				synced 2025-10-31 18:38:10 +00:00 
			
		
		
		
	uspot: refactor code
* add a common.uc class * add ucode ubus calls Signed-off-by: John Crispin <john@phrozen.org>
This commit is contained in:
		
							
								
								
									
										137
									
								
								feeds/ucentral/uspot/files/usr/share/uspot/common.uc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								feeds/ucentral/uspot/files/usr/share/uspot/common.uc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,137 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| let ubus = require('ubus'); | ||||
| let fs = require('fs'); | ||||
| let uci = require('uci').cursor(); | ||||
| let config = uci.get_all('uspot'); | ||||
|  | ||||
| let file = fs.open(config.config.web_root == 1 ? '/tmp/ucentral/www-uspot/header.html' : '/usr/share/uspot/header', 'r'); | ||||
| let header = file.read('all'); | ||||
| file.close(); | ||||
|  | ||||
| file = fs.open(config.config.web_root == 1 ? '/tmp/ucentral/www-uspot/footer.html' : '/usr/share/uspot/footer', 'r'); | ||||
| let footer = file.read('all'); | ||||
| file.close(); | ||||
|  | ||||
| function PO(id, english) { | ||||
| 	return english; | ||||
| } | ||||
|  | ||||
| return { | ||||
| 	fs, | ||||
| 	rtnl: require('rtnl'), | ||||
| 	uam: require('uam'), | ||||
| 	uci, | ||||
| 	config, | ||||
| 	header, | ||||
| 	footer, | ||||
|  | ||||
| 	// wrapper for scraping external tools stdout | ||||
| 	fs_popen: function(cmd) { | ||||
| 		let stdout = fs.popen(cmd); | ||||
| 	        if (!stdout) | ||||
| 			return null; | ||||
|  | ||||
| 		let reply = null; | ||||
| 		try { | ||||
| 			reply = json(stdout.read('all')); | ||||
| 		} catch(e) { | ||||
|  | ||||
| 		} | ||||
| 		stdout.close(); | ||||
| 		return reply; | ||||
| 	}, | ||||
|  | ||||
| 	// give a client access to the internet | ||||
| 	allow_client: function(ctx) { | ||||
| 		ctx.ubus.call('spotfilter', 'client_set', { | ||||
| 			"interface": "hotspot", | ||||
| 			"address": replace(ctx.mac, '-', ':'), | ||||
| 			"state": 1, | ||||
| 			"dns_state": 1, | ||||
| 			"accounting": [ "dl", "ul"], | ||||
| 			"data": { | ||||
| 				"connect": time() | ||||
| 			} | ||||
| 		}); | ||||
| 		if (ctx.query_string.userurl) | ||||
| 			include('redir.uc', { redir_location: ctx.query_string.userurl }); | ||||
| 		else | ||||
| 			include('allow.uc', ctx); | ||||
| 	}, | ||||
|  | ||||
| 	// generate the default radius auth payload | ||||
| 	radius_init: function(ctx) { | ||||
| 		return { | ||||
| 			server: sprintf('%s:%s:%s', this.config.radius.auth_server, this.config.radius.auth_port, this.config.radius.auth_secret), | ||||
| 			acct_session: "0123456789", | ||||
| 			client_ip: ctx.env.REMOTE_ADDR, | ||||
| 			called_station: ctx.mac, | ||||
| 			calling_station: this.config.uam.nasmac, | ||||
| 			nas_ip: ctx.env.SERVER_ADDR, | ||||
| 			nas_id: this.config.uam.nasid | ||||
| 		}; | ||||
| 	}, | ||||
|  | ||||
| 	radius_call: function(ctx, payload) { | ||||
| 		let cfg = fs.open('/tmp/' + ctx.mac + '.json', 'w'); | ||||
| 		cfg.write(payload); | ||||
| 		cfg.close(); | ||||
|  | ||||
| 		return this.fs_popen('/usr/bin/radius-client /tmp/' + ctx.mac + '.json'); | ||||
| 	}, | ||||
|  | ||||
| 	handle_request: function(env) { | ||||
| 		let mac; | ||||
| 		let form_data = {}; | ||||
| 		let query_string = {}; | ||||
| 		let post_data = ''; | ||||
| 		let ctx = { env, header: this.header, footer: this.footer, mac, form_data, post_data, query_string, config: this.config, PO }; | ||||
|  | ||||
| 		// lookup the peers MAC | ||||
| 		let macs = this.rtnl.request(this.rtnl.const.RTM_GETNEIGH, this.rtnl.const.NLM_F_DUMP, { }); | ||||
| 		for (let m in macs) | ||||
| 			if (m.dst == env.REMOTE_HOST) | ||||
| 				ctx.mac = replace(m.lladdr, ':', '-'); | ||||
|  | ||||
| 		// if the MAC lookup failed, go to the error page | ||||
| 		if (!ctx.mac) { | ||||
| 			include('error.uc', ctx); | ||||
| 			return NULL; | ||||
| 		} | ||||
|  | ||||
| 		// check if a client is already connected | ||||
| 		ctx.ubus = ubus.connect(); | ||||
| 		let connected = ctx.ubus.call('spotfilter', 'client_get', { | ||||
| 			'interface': 'hotspot', | ||||
| 			'address': ctx.mac | ||||
| 		}); | ||||
| 		if (connected?.state) { | ||||
| 			include('connected.uc', ctx); | ||||
| 			return NULL; | ||||
| 		} | ||||
|  | ||||
| 		// split QUERY_STRING | ||||
| 		if (env.QUERY_STRING) | ||||
| 			for (let chunk in split(env.QUERY_STRING, '&')) { | ||||
| 				let m = match(chunk, /^([^=]+)=(.*)$/); | ||||
| 				if (!m) continue; | ||||
| 				ctx.query_string[m[1]] = replace(m[2], /%([[:xdigit:]][[:xdigit:]])/g, (m, h) => chr(hex(h) || 0x20)); | ||||
| 			} | ||||
|  | ||||
| 		// recv POST data | ||||
| 		if (env.CONTENT_LENGTH > 0) | ||||
| 			for (let chunk = uhttpd.recv(64); chunk != null; chunk = uhttpd.recv(64)) | ||||
| 				post_data += replace(chunk, /[^[:graph:]]/g, '.'); | ||||
|  | ||||
| 		// split POST data into an array | ||||
| 		if (post_data) | ||||
| 			for (let chunk in split(post_data, '&')) { | ||||
| 				let var = split(chunk, '='); | ||||
| 				if (length(var) != 2) | ||||
| 					continue; | ||||
| 				ctx.form_data[var[0]] = var[1]; | ||||
| 			} | ||||
| 		return ctx; | ||||
| 	} | ||||
| }; | ||||
| @@ -2,104 +2,45 @@ | ||||
|  | ||||
| 'use strict'; | ||||
|  | ||||
| let fs = require('fs'); | ||||
| let rtnl = require('rtnl'); | ||||
|  | ||||
| let file = fs.open('/usr/share/uspot/header', 'r'); | ||||
| let header = file.read('all'); | ||||
| file.close(); | ||||
|  | ||||
| file = fs.open('/usr/share/uspot/footer', 'r'); | ||||
| let footer = file.read('all'); | ||||
| file.close(); | ||||
|  | ||||
| let uci = require('uci').cursor(); | ||||
| let config = uci.get_all('uspot'); | ||||
| push(REQUIRE_SEARCH_PATH, "/usr/share/uspot/*.uc"); | ||||
|  | ||||
| let portal = require('common'); | ||||
| let uam = require('uam'); | ||||
|  | ||||
| // give a client access to the internet | ||||
| function allow_client(ctx) { | ||||
| 	system('ubus call spotfilter client_set \'{ "interface": "hotspot", "address": "' + replace(ctx.mac, '-', ':') + '", "state": 1, "dns_state": 1}\''); | ||||
| 	if (ctx.query_string.userurl) | ||||
| 		include('uam.uc', { uam_location: ctx.query_string.userurl }); | ||||
| 	else | ||||
| 		include('allow.uc', ctx); | ||||
| } | ||||
|  | ||||
| // log the client in via radius | ||||
| function auth_client(ctx) { | ||||
| 	let password; | ||||
| 	let payload = { | ||||
| 		server: sprintf('%s:%s:%s',config.radius.auth_server, config.radius.auth_port, config.radius.auth_secret), | ||||
| 		acct_session: "0123456789", | ||||
| 		client_ip: ctx.env.REMOTE_ADDR, | ||||
| 		called_station: ctx.mac, | ||||
| 		calling_station: config.uam.nasmac, | ||||
| 		nas_ip: ctx.env.SERVER_ADDR, | ||||
| 		nas_id: config.uam.nasid | ||||
| 	}; | ||||
| 	let payload = portal.radius_init(ctx); | ||||
|  | ||||
| 	if (ctx.query_string.username && ctx.query_string.response) { | ||||
| 		let challenge = uam.md5(config.uam.challenge, ctx.mac); | ||||
| 		let challenge = uam.md5(portal.config.uam.challenge, ctx.mac); | ||||
|  | ||||
| 		payload.type = 'uam-chap-auth'; | ||||
| 		payload.username = ctx.query_string.username; | ||||
| 		payload.chap_password = ctx.query_string.response; | ||||
| 		if (config.uam.secret) | ||||
| 			payload.chap_challenge = uam.chap_challenge(challenge, config.uam.uam_secret); | ||||
| 		if (portal.config.uam.secret) | ||||
| 			payload.chap_challenge = uam.chap_challenge(challenge, portal.config.uam.uam_secret); | ||||
| 		else | ||||
| 			payload.chap_challenge = challenge; | ||||
| 	} else if (ctx.query_string.username && ctx.query_string.password) { | ||||
| 		payload.type = 'uam-auth'; | ||||
| 		payload.username = ctx.mac; | ||||
| 		payload.password = uam.password(uam.md5(config.uam.challenge, ctx.mac), ctx.query_string.password, config.uam.uam_secret); | ||||
| 		payload.password = uam.password(uam.md5(portal.config.uam.challenge, ctx.mac), ctx.query_string.password, portal.config.uam.uam_secret); | ||||
| 	} | ||||
|  | ||||
| 	let cfg = fs.open('/tmp/' + ctx.mac + '.json', 'w'); | ||||
| 	cfg.write(payload); | ||||
| 	cfg.close(); | ||||
|  | ||||
| 	let stdout = fs.popen('/usr/bin/radius-client /tmp/' + ctx.mac + '.json'); | ||||
|         let reply; | ||||
|         if (!stdout) { | ||||
| 		request_start({ ...ctx, error: 1 }); | ||||
| 		return; | ||||
|         } | ||||
| 	reply = json(stdout.read('all')); | ||||
| 	stdout.close(); | ||||
|         let reply = portal.radius_call(ctx, payload); | ||||
| 	if (reply['access-accept']) { | ||||
|                 allow_client(ctx); | ||||
|                 portal.allow_client(ctx); | ||||
|                 return; | ||||
|         } | ||||
| 	include('error.uc', ctx); | ||||
| } | ||||
|  | ||||
| global.handle_request = function(env) { | ||||
| 	let mac; | ||||
| 	let form_data = {}; | ||||
| 	let query_string = {}; | ||||
| 	let post_data = ''; | ||||
| 	let ctx = { env, header, footer, mac, form_data, post_data, query_string, config }; | ||||
| 	let ctx = portal.handle_request(env); | ||||
|  | ||||
| 	// lookup the peers MAC | ||||
| 	let macs = rtnl.request(rtnl.const.RTM_GETNEIGH, rtnl.const.NLM_F_DUMP, { }); | ||||
| 	for (let m in macs) | ||||
| 		if (m.dst == env.REMOTE_HOST) | ||||
| 			ctx.mac = replace(m.lladdr, ':', '-'); | ||||
|  | ||||
| 	// if the MAC lookup failed, go to the error page | ||||
| 	if (!ctx.mac) | ||||
| 		include('error.uc', ctx); | ||||
|  | ||||
| 	// split QUERY_STRING | ||||
| 	if (env.QUERY_STRING) | ||||
| 		for (let chunk in split(env.QUERY_STRING, '&')) { | ||||
| 			let m = match(chunk, /^([^=]+)=(.*)$/); | ||||
| 			if (!m) continue; | ||||
| 			ctx.query_string[m[1]] = replace(m[2], /%([[:xdigit:]][[:xdigit:]])/g, (m, h) => chr(hex(h) || 0x20)); | ||||
| 		} | ||||
| 	auth_client(ctx); | ||||
| 	if (ctx) | ||||
| 		auth_client(ctx); | ||||
| }; | ||||
|  | ||||
| %} | ||||
|   | ||||
| @@ -2,46 +2,12 @@ | ||||
|  | ||||
| 'use strict'; | ||||
|  | ||||
| let fs = require('fs'); | ||||
| let rtnl = require('rtnl'); | ||||
| let uam = require('uam'); | ||||
|  | ||||
| let uci = require('uci').cursor(); | ||||
| let config = uci.get_all('uspot'); | ||||
|  | ||||
| let file = fs.open(config.config.web_root == 1 ? '/tmp/ucentral/www-uspot/header.html' : '/usr/share/uspot/header', 'r'); | ||||
| let header = file.read('all'); | ||||
| file.close(); | ||||
|  | ||||
| file = fs.open(config.config.web_root == 1 ? '/tmp/ucentral/www-uspot/footer.html' : '/usr/share/uspot/footer', 'r'); | ||||
| let footer = file.read('all'); | ||||
| file.close(); | ||||
|  | ||||
| // fs.open wrapper | ||||
| function fs_open(cmd) { | ||||
| 	let stdout = fs.popen(cmd); | ||||
|         if (!stdout) | ||||
| 		return null; | ||||
|  | ||||
| 	let reply = null; | ||||
| 	try { | ||||
| 		reply = json(stdout.read('all')); | ||||
| 	} catch(e) { | ||||
|  | ||||
| 	} | ||||
| 	stdout.close(); | ||||
| 	return reply; | ||||
| } | ||||
|  | ||||
| // give a client access to the internet | ||||
| function allow_client(ctx) { | ||||
| 	system('ubus call spotfilter client_set \'{ "interface": "hotspot", "address": "' + replace(ctx.mac, '-', ':') + '", "state": 1, "dns_state": 1}\''); | ||||
| 	include('allow.uc', ctx); | ||||
| } | ||||
| push(REQUIRE_SEARCH_PATH, "/usr/share/uspot/*.uc"); | ||||
| let portal = require('common'); | ||||
|  | ||||
| // delegate an initial connection to the correct handler | ||||
| function request_start(ctx) { | ||||
| 	switch (config?.config?.auth_mode) { | ||||
| 	switch (portal.config?.config?.auth_mode) { | ||||
| 	case 'click-to-continue': | ||||
| 		include('click.uc', ctx); | ||||
| 		return; | ||||
| @@ -52,17 +18,17 @@ function request_start(ctx) { | ||||
| 		include('radius.uc', ctx); | ||||
| 		return; | ||||
| 	case 'uam': | ||||
| 		ctx.uam_location = config.uam.uam_server + | ||||
| 		ctx.redir_location = portal.config.uam.uam_server + | ||||
| 			'?res=notyet' + | ||||
| 			'&uamip=' + ctx.env.SERVER_ADDR + | ||||
| 			'&uamport=' + config.uam.uam_port + | ||||
| 			'&challenge=' + uam.md5(config.uam.challenge, ctx.mac) + | ||||
| 			'&uamport=' + portal.config.uam.uam_port + | ||||
| 			'&challenge=' + portal.uam.md5(portal.config.uam.challenge, ctx.mac) + | ||||
| 			'&mac=' + replace(ctx.mac, ':', '-') + | ||||
| 			'&ip=' + ctx.env.REMOTE_ADDR + | ||||
| 			'&called=' + config.uam.nasmac + | ||||
| 			'&nasid=' + config.uam.nasid; | ||||
| 		ctx.uam_location += '&md=' + uam.md5(ctx.uam_location, config.uam.uam_secret); | ||||
| 		include('uam.uc', ctx); | ||||
| 			'&called=' + portal.config.uam.nasmac + | ||||
| 			'&nasid=' + portal.config.uam.nasid; | ||||
| 		ctx.redir_location += '&md=' + portal.uam.md5(ctx.uam_location, portal.config.uam.uam_secret); | ||||
| 		include('redir.uc', ctx); | ||||
| 		return; | ||||
| 	default: | ||||
| 		include('error.uc', ctx); | ||||
| @@ -73,7 +39,7 @@ function request_start(ctx) { | ||||
| // delegate a local click-to-continue authentication | ||||
| function request_click(ctx) { | ||||
| 	// make sure this is the right auth_mode | ||||
| 	if (config?.config?.auth_mode != 'click-to-continue') { | ||||
| 	if (portal.config?.config?.auth_mode != 'click-to-continue') { | ||||
| 		include('error.uc', ctx); | ||||
|                 return; | ||||
| 	} | ||||
| @@ -83,13 +49,13 @@ function request_click(ctx) { | ||||
| 		request_start({ ...ctx, error: 1 }); | ||||
|                 return; | ||||
| 	} | ||||
| 	allow_client(ctx); | ||||
| 	portal.allow_client(ctx); | ||||
| } | ||||
|  | ||||
| // delegate a local username/password authentication | ||||
| function request_credentials(ctx) { | ||||
| 	// make sure this is the right auth_mode | ||||
| 	if (config?.config?.auth_mode != 'credentials') { | ||||
| 	if (portal/config?.config?.auth_mode != 'credentials') { | ||||
| 		include('error.uc', ctx); | ||||
|                 return; | ||||
| 	} | ||||
| @@ -101,8 +67,8 @@ function request_credentials(ctx) { | ||||
| 	} | ||||
|  | ||||
| 	// check if the credentials are valid | ||||
| 	for (let k in config) { | ||||
| 		let cred = config[k]; | ||||
| 	for (let k in portal.config) { | ||||
| 		let cred = portal.config[k]; | ||||
|  | ||||
| 		if (cred['.type'] != 'credentials') | ||||
| 			continue; | ||||
| @@ -110,7 +76,7 @@ function request_credentials(ctx) { | ||||
| 		    ctx.form_data.password != cred.password) | ||||
| 			continue; | ||||
|  | ||||
| 		allow_client(ctx); | ||||
| 		portal.allow_client(ctx); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| @@ -121,7 +87,7 @@ function request_credentials(ctx) { | ||||
| // delegate a radius username/password authentication | ||||
| function request_radius(ctx) { | ||||
| 	// make sure this is the right auth_mode | ||||
| 	if (config?.config?.auth_mode != 'radius') { | ||||
| 	if (portal.config?.config?.auth_mode != 'radius') { | ||||
| 		include('error.uc', ctx); | ||||
|                 return; | ||||
| 	} | ||||
| @@ -132,29 +98,15 @@ function request_radius(ctx) { | ||||
|                 return; | ||||
| 	} | ||||
|  | ||||
| 	let cfg = fs.open('/tmp/' + ctx.mac + '.json', 'w'); | ||||
| 	cfg.write({ | ||||
| 		type: 'auth', | ||||
| 		server: sprintf('%s:%s:%s',config.radius.auth_server, config.radius.auth_port, config.radius.auth_secret), | ||||
| 		username: ctx.form_data.username, | ||||
| 		password: ctx.form_data.password, | ||||
| 		acct_session: "0123456789", | ||||
| 		client_ip: ctx.env.REMOTE_ADDR, | ||||
| 		called_station: ctx.mac, | ||||
| 		nas_ip: ctx.env.SERVER_ADDR, | ||||
| 	}); | ||||
| 	cfg.close(); | ||||
| 	// trigger the radius auth | ||||
| 	let payload = radius_init(ctx); | ||||
| 	payload.type = 'auth'; | ||||
| 	payload.username = ctx.form_data.username; | ||||
| 	payload.password = ctx.form_data.password; | ||||
|  | ||||
| 	let stdout = fs.popen('/usr/bin/radius-client /tmp/' + ctx.mac + '.json'); | ||||
|         let reply; | ||||
|         if (!stdout) { | ||||
| 		request_start({ ...ctx, error: 1 }); | ||||
| 		return; | ||||
|         } | ||||
| 	reply = json(stdout.read('all')); | ||||
| 	stdout.close(); | ||||
|         let reply = portal.radius_call(ctx, payload); | ||||
| 	if (reply['access-accept']) { | ||||
|                 allow_client(ctx); | ||||
|                 portal.allow_client(ctx); | ||||
|                 return; | ||||
|         } | ||||
|  | ||||
| @@ -162,70 +114,24 @@ function request_radius(ctx) { | ||||
| 	request_start({ ...ctx, error: 1 }); | ||||
| } | ||||
|  | ||||
| function PO(id, english) { | ||||
| 	return english; | ||||
| } | ||||
|  | ||||
| global.handle_request = function(env) { | ||||
| 	let mac; | ||||
| 	let form_data = {}; | ||||
| 	let query_string = {}; | ||||
| 	let post_data = ''; | ||||
| 	let ctx = { env, header, footer, mac, form_data, post_data, query_string, config, PO }; | ||||
| 	let ctx = portal.handle_request(env); | ||||
|  | ||||
| 	// lookup the peers MAC | ||||
| 	let macs = rtnl.request(rtnl.const.RTM_GETNEIGH, rtnl.const.NLM_F_DUMP, { }); | ||||
| 	for (let m in macs) | ||||
| 		if (m.dst == env.REMOTE_HOST) | ||||
| 			ctx.mac = replace(m.lladdr, ':', '-'); | ||||
|  | ||||
| 	// if the MAC lookup failed, go to the error page | ||||
| 	if (!ctx.mac) | ||||
| 		include('error.uc', ctx); | ||||
|  | ||||
| 	// check if a client is already connected | ||||
| 	let connected = fs_open('ubus call spotfilter client_get \'{"interface": "hotspot", "address": "' + ctx.mac + '"}\''); | ||||
| 	if (connected?.state) { | ||||
| 		include('connected.uc', ctx); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	// split QUERY_STRING | ||||
| 	if (env.QUERY_STRING) | ||||
| 		for (let chunk in split(env.QUERY_STRING, '&')) { | ||||
| 			let m = match(chunk, /^([^=]+)=(.*)$/); | ||||
| 			if (!m) continue; | ||||
| 			ctx.query_string[m[1]] = replace(m[2], /%([[:xdigit:]][[:xdigit:]])/g, (m, h) => chr(hex(h) || 0x20)); | ||||
| 	if (ctx) | ||||
| 		switch (ctx.form_data.action) { | ||||
| 		case 'credentials': | ||||
| 			request_credentials(ctx); | ||||
| 			return; | ||||
| 		case 'radius': | ||||
| 			request_radius(ctx); | ||||
| 			return; | ||||
| 		case 'click': | ||||
| 			request_click(ctx); | ||||
| 			return; | ||||
| 		default: | ||||
| 			request_start(ctx); | ||||
| 			return; | ||||
| 		} | ||||
|  | ||||
| 	// recv POST data | ||||
| 	if (env.CONTENT_LENGTH > 0) | ||||
| 		for (let chunk = uhttpd.recv(64); chunk != null; chunk = uhttpd.recv(64)) | ||||
| 			post_data += replace(chunk, /[^[:graph:]]/g, '.'); | ||||
|  | ||||
| 	// split POST data into an array | ||||
| 	if (post_data) | ||||
| 		for (let chunk in split(post_data, '&')) { | ||||
| 			let var = split(chunk, '='); | ||||
| 			if (length(var) != 2) | ||||
| 				continue; | ||||
| 			ctx.form_data[var[0]] = var[1]; | ||||
| 		} | ||||
|  | ||||
| 	switch (ctx.form_data.action) { | ||||
| 	case 'credentials': | ||||
| 		request_credentials(ctx); | ||||
| 		return; | ||||
| 	case 'radius': | ||||
| 		request_radius(ctx); | ||||
| 		return; | ||||
| 	case 'click': | ||||
| 		request_click(ctx); | ||||
| 		return; | ||||
| 	default: | ||||
| 		request_start(ctx); | ||||
| 		return; | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| %} | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| Status: 302 Found | ||||
| Location: {{ uam_location }} | ||||
| Location: {{ redir_location }} | ||||
| Content-Type: text/html | ||||
| 
 | ||||
| 
 | ||||
		Reference in New Issue
	
	Block a user
	 John Crispin
					John Crispin