mirror of
				https://github.com/RangeNetworks/openbts.git
				synced 2025-10-31 11:53:33 +00:00 
			
		
		
		
	git-svn-id: http://wush.net/svn/range/software/public/openbts/trunk@3050 19bc5d8c-e614-43d4-8b26-e1612bc8e597
		
			
				
	
	
		
			289 lines
		
	
	
		
			8.0 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			289 lines
		
	
	
		
			8.0 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /**@file RRLPServer */
 | |
| /*
 | |
| * Copyright 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
 | |
| * Copyright 2011 Range Networks, Inc.
 | |
| *
 | |
| * This software is distributed under the terms of the GNU Affero Public License.
 | |
| * See the COPYING file in the main directory for details.
 | |
| *
 | |
| * This use of this software may be subject to additional restrictions.
 | |
| * See the LEGAL file in the main directory for details.
 | |
| 
 | |
| 	This program is free software: you can redistribute it and/or modify
 | |
| 	it under the terms of the GNU Affero General Public License as published by
 | |
| 	the Free Software Foundation, either version 3 of the License, or
 | |
| 	(at your option) any later version.
 | |
| 
 | |
| 	This program is distributed in the hope that it will be useful,
 | |
| 	but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
| 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
| 	GNU Affero General Public License for more details.
 | |
| 
 | |
| 	You should have received a copy of the GNU Affero General Public License
 | |
| 	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | |
| 
 | |
| */
 | |
| #include <Timeval.h>
 | |
| 
 | |
| using namespace std;
 | |
| 
 | |
| #include "ControlCommon.h"
 | |
| #include "RRLPServer.h"
 | |
| 
 | |
| #include <GSMLogicalChannel.h>
 | |
| #include <GSML3MMMessages.h>
 | |
| 
 | |
| #include <Logger.h>
 | |
| 
 | |
| using namespace GSM;
 | |
| using namespace Control;
 | |
| 
 | |
| void clean(char *line)
 | |
| {
 | |
| 	char *p = line + strlen(line) - 1;
 | |
| 	while (p > line && *p <= ' ') *p-- = 0;
 | |
| }
 | |
| 
 | |
| string getConfig()
 | |
| {
 | |
| 	const char *configs[] = {
 | |
| 		"GSM.RRLP.ACCURACY",
 | |
| 		"GSM.RRLP.RESPONSETIME",
 | |
| 		"GSM.RRLP.ALMANAC.URL",
 | |
| 		"GSM.RRLP.EPHEMERIS.URL",
 | |
| 		"GSM.RRLP.ALMANAC.REFRESH.TIME",
 | |
| 		"GSM.RRLP.EPHEMERIS.REFRESH.TIME",
 | |
| 		"GSM.RRLP.SEED.LATITUDE",
 | |
| 		"GSM.RRLP.SEED.LONGITUDE",
 | |
| 		"GSM.RRLP.SEED.ALTITUDE",
 | |
| 		"GSM.RRLP.EPHEMERIS.ASSIST.COUNT",
 | |
| 		"GSM.RRLP.ALMANAC.ASSIST.PRESENT",
 | |
| 		0
 | |
| 	};
 | |
| 	string config = "";
 | |
| 	for (const char **p = configs; *p; p++) {
 | |
| 		string configValue = gConfig.getStr(*p, "");
 | |
| 		if (configValue.length() == 0) return "";
 | |
| 		config.append("&");
 | |
| 		config.append(*p);
 | |
| 		config.append("=");
 | |
| 		if (0 == strcmp((*p) + strlen(*p) - 3, "URL")) {
 | |
| 			// TODO - better to have urlencode and decode, but then I'd have to look for them
 | |
| 			char buf[3];
 | |
| 			buf[2] = 0;
 | |
| 			for (const char *q = configValue.c_str(); *q; q++) {
 | |
| 				sprintf(buf, "%02x", *q);
 | |
| 				config.append(buf);
 | |
| 			}
 | |
| 		} else {
 | |
| 			config.append(configValue);
 | |
| 		}
 | |
| 	}
 | |
| 	return config;
 | |
| }
 | |
| 
 | |
| RRLPServer::RRLPServer(L3MobileIdentity wMobileID, LogicalChannel *wDCCH)
 | |
| {
 | |
| 	trouble = false;
 | |
| 	url = gConfig.getStr("GSM.RRLP.SERVER.URL", "");
 | |
| 	if (url.length() == 0) {
 | |
| 		LOG(INFO) << "RRLP not configured";
 | |
| 		trouble = true;
 | |
| 		return;
 | |
| 	}
 | |
| 	mobileID = wMobileID;
 | |
| 	DCCH = wDCCH;
 | |
| 	// name of subscriber
 | |
| 	name = string("IMSI") + mobileID.digits();
 | |
| 	// don't continue if IMSI has a RRLP support flag and it's false
 | |
| 	unsigned int supported = 0;
 | |
| 	if (sqlite3_single_lookup(gSubscriberRegistry.db(), "sip_buddies", "name", name.c_str(),
 | |
| 							"RRLPSupported", supported) && !supported) {
 | |
| 		LOG(INFO) << "RRLP not supported for " << name;
 | |
| 		trouble = true;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| bool RRLPServer::assist()
 | |
| {
 | |
| 	if (trouble) return false;
 | |
| 	query = "query=assist";
 | |
| 	return transact();
 | |
| }
 | |
| 
 | |
| bool RRLPServer::locate()
 | |
| {
 | |
| 	if (trouble) return false;
 | |
| 	query = "query=loc";
 | |
| 	return transact();
 | |
| }
 | |
| 
 | |
| bool RRLPServer::transact()
 | |
| {
 | |
| 	vector<string> apdus;
 | |
| 	while (true) {
 | |
| 		// bounce off server
 | |
| 		string esc = "'";
 | |
| 		string config = getConfig();
 | |
| 		if (config.length() == 0) return false;
 | |
| 		string cmd = "wget -qO- " + esc + url + "?" + query + config + esc;
 | |
| 		LOG(INFO) << "*************** "  << cmd;
 | |
| 		FILE *result = popen(cmd.c_str(), "r");
 | |
| 		if (!result) {
 | |
| 			LOG(CRIT) << "popen call \"" << cmd << "\" failed";
 | |
| 			return NULL;
 | |
| 		}
 | |
| 		// build map of responses, and list of apdus
 | |
| 		map<string,string> response;
 | |
| 		size_t nbytes = 1500;
 | |
| 		char *line = (char*)malloc(nbytes+1);
 | |
| 		while (!feof(result)) {
 | |
| 			if (!fgets(line, nbytes, result)) break;
 | |
| 			clean(line);
 | |
| 			LOG(INFO) << "server return: " << line;
 | |
| 			char *p = strchr(line, '=');
 | |
| 			if (!p) continue;
 | |
| 			string lhs = string(line, 0, p-line);
 | |
| 			string rhs = string(line, p+1-line, string::npos);
 | |
| 			if (lhs == "apdu") {
 | |
| 				apdus.push_back(rhs);
 | |
| 			} else {
 | |
| 				response[lhs] = rhs;
 | |
| 			}
 | |
| 		}
 | |
| 		free(line);
 | |
| 		pclose(result);
 | |
| 
 | |
| 		// quit if error
 | |
| 		if (response.find("error") != response.end()) {
 | |
| 			LOG(INFO) << "error from server: " << response["error"];
 | |
| 			return false;
 | |
| 		}
 | |
| 
 | |
| 		// quit if ack from assist unless there are more apdu messages
 | |
| 		if (response.find("ack") != response.end()) {
 | |
| 			LOG(INFO) << "ack from mobile, decoded by server";
 | |
| 			if (apdus.size() == 0) {
 | |
| 				return true;
 | |
| 			} else {
 | |
| 				LOG(INFO) << "more apdu messages";
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		// quit if location decoded 
 | |
| 		if (response.find("latitude") != response.end() && response.find("longitude") != response.end() && response.find("positionError") != response.end()) {
 | |
| 			ostringstream os;
 | |
| 			os << "insert into RRLP (name, latitude, longitude, error, time) values (" <<
 | |
| 				'"' << name << '"' << "," <<
 | |
| 				response["latitude"] << "," <<
 | |
| 				response["longitude"] << "," <<
 | |
| 				response["positionError"] << "," <<
 | |
| 				"datetime('now')"
 | |
| 			")";
 | |
| 			LOG(INFO) << os.str();
 | |
| 			if (!sqlite3_command(gSubscriberRegistry.db(), os.str().c_str())) {
 | |
| 				LOG(INFO) << "sqlite3_command problem";
 | |
| 				return false;
 | |
| 			}
 | |
| 			return true;
 | |
| 		}
 | |
| 
 | |
| 		// bounce off mobile
 | |
| 		if (apdus.size() == 0) {
 | |
| 			LOG(INFO) << "missing apdu for mobile";
 | |
| 			return false;
 | |
| 		}
 | |
| 		string apdu = apdus[0];
 | |
| 		apdus.erase(apdus.begin());
 | |
| 		BitVector bv(apdu.size()*4);
 | |
| 		if (!bv.unhex(apdu.c_str())) {
 | |
| 			LOG(INFO) << "BitVector::unhex problem";
 | |
| 			return false;
 | |
| 		}
 | |
| 
 | |
| 		DCCH->send(L3ApplicationInformation(bv));
 | |
| 		// Receive an L3 frame with a timeout.  Timeout loc req response time max + 2 seconds.
 | |
| 		L3Frame* resp = DCCH->recv(130000);
 | |
| 		if (!resp) {
 | |
| 			return false;
 | |
| 		}
 | |
| 		LOG(INFO) << "RRLPQuery returned " << *resp;
 | |
| 		if (resp->primitive() != DATA) {
 | |
| 			LOG(INFO) << "didn't receive data";
 | |
| 			switch (resp->primitive()) {
 | |
| 				case ESTABLISH: LOG(INFO) << "channel establihsment"; break;
 | |
| 				case RELEASE: LOG(INFO) << "normal channel release"; break;
 | |
| 				case DATA: LOG(INFO) << "multiframe data transfer"; break;
 | |
| 				case UNIT_DATA: LOG(INFO) << "datagram-type data transfer"; break;
 | |
| 				case ERROR: LOG(INFO) << "channel error"; break;
 | |
| 				case HARDRELEASE: LOG(INFO) << "forced release after an assignment"; break;
 | |
| 				default: LOG(INFO) << "unrecognized primitive response"; break;
 | |
| 			}
 | |
| 			delete resp;
 | |
| 			return false;
 | |
| 		}
 | |
| 		const unsigned PD_RR = 6;
 | |
| 		LOG(INFO) << "resp.pd = " << resp->PD();
 | |
| 		if (resp->PD() != PD_RR) {
 | |
| 			LOG(INFO) << "didn't receive an RR message";
 | |
| 			delete resp;
 | |
| 			return false;
 | |
| 		}
 | |
| 		const unsigned MTI_RR_STATUS = 18;
 | |
| 		if (resp->MTI() == MTI_RR_STATUS) {
 | |
| 			ostringstream os2;
 | |
| 			int cause = resp->peekField(16, 8);
 | |
| 			delete resp;
 | |
| 			switch (cause) {
 | |
| 				case 97:
 | |
| 					LOG(INFO) << "MS says: message not implemented";
 | |
| 					// flag unsupported in SR so we don't waste time on it again
 | |
| 					os2 << "update sip_buddies set RRLPSupported = \"0\" where name = \"" << name << "\"";
 | |
| 					if (!sqlite3_command(gSubscriberRegistry.db(), os2.str().c_str())) {
 | |
| 						LOG(INFO) << "sqlite3_command problem";
 | |
| 					}
 | |
| 					return false;
 | |
| 				case 98:
 | |
| 					LOG(INFO) << "MS says: message type not compatible with protocol state";
 | |
| 					return false;
 | |
| 				default:
 | |
| 					LOG(INFO) << "unknown RR_STATUS response, cause = " << cause;
 | |
| 					return false;
 | |
| 			}
 | |
| 		}
 | |
| 		const unsigned MTI_RR_APDU = 56;
 | |
| 		if (resp->MTI() != MTI_RR_APDU) {
 | |
| 			LOG(INFO) << "received unexpected RR Message " << resp->MTI();
 | |
| 			delete resp;
 | |
| 			return false;
 | |
| 		}
 | |
| 
 | |
| 		// looks like a good APDU
 | |
| 		BitVector *bv2 = (BitVector*)resp;
 | |
| 		BitVector bv3 = bv2->tail(32);
 | |
| 		ostringstream os;
 | |
| 		bv3.hex(os);
 | |
| 		apdu = os.str();
 | |
| 		delete resp;
 | |
| 
 | |
| 		// next query for server
 | |
| 		query = "query=apdu&apdu=" + apdu;
 | |
| 	}
 | |
| 	// not reached
 | |
| }
 | |
| 
 | |
| bool sendRRLP(GSM::L3MobileIdentity mobileID, GSM::LogicalChannel *LCH)
 | |
| {
 | |
| 	// Query for RRLP
 | |
| 	RRLPServer wRRLPServer(mobileID, LCH);
 | |
| 	if (!wRRLPServer.assist()) {
 | |
| 		LOG(INFO) << "assist problem";
 | |
| 	}
 | |
| 	// can still try to check location even if assist didn't work
 | |
| 	if (!wRRLPServer.locate()) {
 | |
| 		LOG(INFO) << "locate problem";
 | |
| 		return false;
 | |
| 	}
 | |
| 	return true;
 | |
| }
 |