#include #include #include #include #include #include #include #include #include #include "libyuarel/yuarel.h" #include "json-c/json.h" #include "sensob/sensob.h" #include "aes.h" //#include "../../status.h" #include "../../feeder.h" #define RECVD_HISTORY_SIZE 32//must be dividable by 8 typedef struct unit_t { uint32_t id; uint8_t recvd[RECVD_HISTORY_SIZE]; uint8_t pos, recvd_size; uint32_t seq; uint8_t ack; struct unit_t * next; } unit_t; static struct unit_t * units, * unit_now; static uint64_t jid = 0; //static uint32_t seq; static uint8_t ack; static cfg_t * cfg; static void (* feederLog)(int priority, const char * fmt, ...); int setLog(void * func) { feederLog = (void(*)(int, const char * , ...))func; return 0; } int init(void * param) { (void)param; // seq = 0; units = unit_now = NULL; ack = 0; cfg_opt_t cfg_opts[] = { CFG_STR((char *)"url", 0, CFGF_NONE), CFG_STR((char *)"user", 0, CFGF_NONE), CFG_STR((char *)"pass", 0, CFGF_NONE), CFG_END() }; cfg = cfg_init(cfg_opts, CFGF_NONE); if (cfg_parse(cfg, "cra.conf") != CFG_SUCCESS) { return -1; } curl_global_init(CURL_GLOBAL_DEFAULT); return 0; } struct unit_t * unitFind(struct unit_t * unit, uint32_t id) { while (unit != NULL) { if (unit->id == id) return unit; unit = unit->next; } return NULL; } struct unit_t * unitCreate(uint32_t id) { struct unit_t * unit; unit = (struct unit_t *)malloc(sizeof(struct unit_t)); unit->id = id; memset(unit->recvd, 0, RECVD_HISTORY_SIZE); unit->seq = 0; unit->pos = 0; unit->next = NULL; return unit; } void unitUpdateRecvd(struct unit_t * unit, uint8_t flag) { unit->recvd[unit->pos++] = flag; if (unit->pos > RECVD_HISTORY_SIZE) unit->pos = 0; } void stringToArrayn(uint8_t * data, char * str, uint16_t len) { uint16_t i; uint8_t j; char c; for (i = 0; i < len; i++) { data[i] = 0; for (j = 0; j < 2; j++) { c = str[i * 2 + j]; if (c >= 'a') //to high case c -= 0x20; data[i] += (c - ((c < 'A')?'0':'7')) << ((j == 0)?4:0); } } } void arraynToString(char * str, uint8_t * data, uint16_t len) { uint16_t i; uint8_t j; char c; for (i = 0; i < len; i++) { for (j = 0; j < 2; j++) { c = (data[i] >> ((j == 0)?4:0)) & 0x0F; str[i * 2 + j] = c + ((c < 0x0A)?'0':'7'); } } str[len * 2] = '\0'; } #define CMD_DATETIME_REQ 0xD0 #define CMD_DATETIME_SYNC 0xD1 #define CMD_DATETIME_RESPONSE 0xD2 #define CMD_VARVAL 0xD3 #define CMD_NONE 0xCF typedef struct cmd_datetimereq_t { uint32_t tm; } __attribute__((packed)) cmd_datetimereq_t; typedef struct cmd_datetimeresponse_t { int32_t diff; } __attribute__((packed)) cmd_datetimeresponse_t; typedef struct cmd_datetimesync_t { } __attribute__((packed)) cmd_datetimesync_t; typedef struct cmd_header_t { // uint8_t delimiter; // uint64_t id; uint8_t cmd; } __attribute__((packed)) cmd_header_t; typedef struct cmd_footer_t { // uint16_t crc; } __attribute__((packed)) cmd_footer_t; int curlDebug(CURL * curl, curl_infotype info, char * string, size_t size, void * data) { (void)curl; (void)data; if (feederLog != NULL) { if (info == CURLINFO_TEXT) { string[size] = 0; feederLog(LOG_DEBUG, "cra: curl debug: %s", string); } } return 0; } typedef struct cb_t { uint8_t cmd; } cb_t; size_t curlWriteCB(void *contents, size_t size, size_t nmemb, void *userp) { size_t realsize; char * fetch = (char *)userp; realsize = size * nmemb; feederLog(LOG_DEBUG, "cra: Fetched %s\n", (char *)contents); memcpy(fetch, contents, realsize); fetch[realsize] = 0; /* return size */ return realsize; } void craApi(char * method, char * header_id, char * req_body, char * fetch) { CURL *ch; CURLcode res; char url[1024]; char post[2048]; char header[256]; int info; struct curl_slist * headers = NULL; ch = curl_easy_init(); if (ch) { curl_easy_setopt(ch, CURLOPT_DEBUGFUNCTION, curlDebug); curl_easy_setopt(ch, CURLOPT_VERBOSE, 1L); // strcpy(url, "http://82.99.180.180/Message"); // strcpy(url, "https://api.iot.cra.cz/cxf/IOTServices/v2/"); strcpy(url, cfg_getstr(cfg, "url")); strcpy(url + strlen(url), method); curl_easy_setopt(ch, CURLOPT_URL, url); curl_easy_setopt(ch, CURLOPT_POST, 1L); headers = curl_slist_append(headers, "Accept: application/json"); headers = curl_slist_append(headers, "Content-Type: application/json"); if (header_id != NULL) { strcpy(header, "SessionId: "); strcpy(header + strlen(header), header_id); headers = curl_slist_append(headers, header); } curl_easy_setopt(ch, CURLOPT_HTTPHEADER, headers); // curl_easy_setopt(ch, CURLOPT_HEADER, 0); // curl_easy_setopt(ch, CURLOPT_SSL_VERIFYPEER, 0); /* sprintf((char *)post, "{\r\n\t\"EUI\": \"%s\",\r\n\t\"data\": \"{\\\"cmd\\\":\\\"tx\\\",\\\"port\\\":1,\\\"data\\\":\\\"%s\\\",\\\"EUI\\\":\\\"%s\\\"}\"\r\n}\r\n", "0004A30B0022088F", "2C","0004A30B0022088F"); */ strcpy(post, req_body); curl_easy_setopt(ch, CURLOPT_CUSTOMREQUEST, "POST"); curl_easy_setopt(ch, CURLOPT_POSTFIELDS, post); curl_easy_setopt(ch, CURLOPT_POSTFIELDSIZE, strlen(post)); // cf.payload = (char *)calloc(1, sizeof(cf.payload)); // cf.size = 0; // curl_easy_setopt(ch, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_0); //!!!!!!!!!!!! if (fetch != NULL) { curl_easy_setopt(ch, CURLOPT_WRITEFUNCTION, curlWriteCB); curl_easy_setopt(ch, CURLOPT_WRITEDATA, (void *)fetch); } // res = curl_fetch_url(curl_g, path, &cf); feederLog(LOG_INFO, "cra: %s %s\n", url, post); res = curl_easy_perform(ch); curl_easy_cleanup(ch); curl_easy_getinfo(ch, CURLINFO_HTTP_CODE, &info); if (res != CURLE_OK) { feederLog(LOG_ERR, "cra: curl POST error %d\n", res); return; } if (info != 200) { feederLog(LOG_ERR, "cra: curl page error %d\n", info); return; } } } char * craLogin(void) { char fetch[2048]; char req[1024]; json_object * json; enum json_tokener_error jerr = json_tokener_success; char * id = NULL; // craApi((char *)"Login", NULL, (char *)"{\r\n\t\"username\": \"krivanek@lesprojekt.cz\",\r\n\t\"password\": \"#LESPloRa2020@\"\r\n}\r\n", fetch);//todo: conf sprintf(req, (char *)"{\r\n\t\"username\": \"%s\",\r\n\t\"password\": \"%s\"\r\n}\r\n", cfg_getstr(cfg, "user"), cfg_getstr(cfg, "pass")); craApi((char *)"Login", NULL, req, fetch);//todo: conf json = json_tokener_parse_verbose(fetch, &jerr); if (jerr != json_tokener_success) { feederLog(LOG_ERR, "cra: Failed to parse json err %s\n", json_tokener_error_desc(jerr)); json_object_put(json); return NULL; } id = (char *)json_object_get_string(json_object_object_get(json, "sessionId")); feederLog(LOG_INFO, "cra: Session id %s\n", id); return id; } void cmdMessage(uint8_t * d, uint64_t id, uint8_t cmd) { struct cmd_header_t * header; // struct cmd_footer_t * footer; (void)id; header = (struct cmd_header_t *)(d); // footer = (struct cmd_footer_t *)(d + sizeof(struct cmd_header_t) + cmd_tyope_size);//todo: footer // header->delimiter = '.'; // header->id = id; header->cmd = cmd; } void cmdFooter(uint8_t * d, uint16_t len) { struct cmd_footer_t * footer; footer = (struct cmd_footer_t *)(d + len); // footer->crc = 0;//todo: spocitat } void cmdDateTimeReq(struct cmd_datetimereq_t * req, uint64_t id) { char body[2048]; char message[256]; uint8_t data[32]; struct cmd_datetimeresponse_t * response; char * sid; time_t tm; int32_t diff; uint16_t len; tm = time(NULL); diff = tm - byteSwap32(req->tm); feederLog(LOG_INFO, "cra: device time %lld, system time %lld, difference %lld\n", byteSwap32(req->tm), tm, diff); cmdMessage(data, 0, CMD_DATETIME_RESPONSE); response = (struct cmd_datetimeresponse_t *)(data + sizeof(struct cmd_header_t)); response->diff = byteSwap32(diff); cmdFooter(data, sizeof(struct cmd_header_t) + sizeof(struct cmd_datetimeresponse_t)); len = sizeof(struct cmd_header_t) + sizeof(struct cmd_datetimeresponse_t) + sizeof(struct cmd_footer_t); sid = craLogin(); if (sid != NULL) { arraynToString(message, data, len); /* sprintf((char *)body, "{\r\n\t\"EUI\": \"%s\",\r\n\t\"data\": \"{\\\"cmd\\\":\\\"tx\\\",\\\"port\\\":1,\\\"data\\\":\\\"%s\\\",\\\"EUI\\\":\\\"%s\\\"}\"\r\n}\r\n", "0004A30B0022088F", message,"0004A30B0022088F");*/ sprintf((char *)body, "{\r\n\t\"EUI\": \"%016llX\",\r\n\t\"data\": \"{\\\"cmd\\\":\\\"tx\\\",\\\"port\\\":1,\\\"data\\\":\\\"%s\\\",\\\"EUI\\\":\\\"%016llX\\\"}\"\r\n}\r\n", id, message, id); craApi((char *)"Message", sid, body, NULL); } } void cmdParse(uint8_t * data, uint16_t len, uint64_t id) { struct cmd_header_t * header = (struct cmd_header_t *)data; (void)len; switch (header->cmd) { case CMD_DATETIME_REQ: feederLog(LOG_DEBUG, "cra: Cmd datetimereq\n"); cmdDateTimeReq((struct cmd_datetimereq_t *)(data + sizeof(struct cmd_header_t)), id); break; case CMD_DATETIME_SYNC: feederLog(LOG_DEBUG, "cra: Cmd datetimesync\n"); break; default: feederLog(LOG_ERR, "cra: Unknowen command %d\n", header->cmd); break; } } #define LW_KEY_LEN (16) unsigned int process(void *lib_data, int sock, unsigned char *data, unsigned int length, unsigned long long int *id, time_t * tm, double *result_array, uint64_t * sensors, unsigned int *type) { uint32_t seq; json_object *json, *json_data, * jdata; enum json_tokener_error jerr = json_tokener_success; char * data_json, * json_data_data_str; uint8_t json_data_data_bin[255]; uint32_t json_data_ts_int; uint8_t json_data_bat_int; unsigned int res; (void)lib_data; (void)sock; if ((data == NULL) || (length == 0)) { res = sensob_parse(NULL, 0, (uint64_t *)id, tm, result_array, sensors, type); *id = jid; // feederLog(LOG_DEBUG, "cra: item sensor %lu, value %f, type %d\n", sensors[0], result_array[0], *type); return res; } while (data[length - 1] < ' ') length--; data[length] = 0; feederLog(LOG_DEBUG, "cra: raw data in:\n%s\n", (char *)data); seq = 0; *id = 0; feederLog(LOG_DEBUG, "cra:1\n"); data_json = strchr((char *)data, '{');//todo: proparsit i hlavicku if (data_json == NULL) { feederLog(LOG_ERR, "cra: Json not found\n"); return -1; } json = json_tokener_parse_verbose(data_json, &jerr); if (jerr != json_tokener_success) { feederLog(LOG_ERR, "cra: Failed to parse json err %s\n", json_tokener_error_desc(jerr)); json_object_put(json); return -1; } jdata = json_object_object_get(json, "data"); // todo: check // feederLog(LOG_DEBUG, "cra: %s\n", json_object_get_string(jdata)); json_data = json_tokener_parse_verbose(json_object_get_string(jdata), &jerr); jid = strtol(json_object_get_string(json_object_object_get(json_data, "EUI")), NULL, 16); feederLog(LOG_DEBUG, "cra: id %lld\n", *id); json_data_data_str = (char *)json_object_get_string(json_object_object_get(json_data, "data")); json_data_ts_int = json_object_get_int64(json_object_object_get(json_data, "ts")) / 1000; json_data_bat_int = json_object_get_int64(json_object_object_get(json_data, "bat")); if ((json_data_data_str == NULL) || (strlen(json_data_data_str) == 0)) { /* json_data_data_str = (char *)json_object_get_string(json_object_object_get(json_data, "encdata")); feederLog(LOG_DEBUG, "cra: encdata %s\n", json_data_data_str); stringToArrayn(json_data_data_bin, json_data_data_str, strlen(json_data_data_str) / 2); aes_context aesContext; uint8_t aeskey[] = {0x45, 0xf3, 0x22, 0x67, 0x98, 0x95, 0x6f, 0xcd, 0x46, 0x67, 0x0b, 0x8c, 0x13, 0xb8, 0x23, 0x15}; aes_set_key(aeskey, LW_KEY_LEN, &aesContext); aes_encrypt(json_data_data_bin, json_data_data_bin, &aesContext); */ // feederLog(LOG_ERR, "cra: Encoded data not yet supported\n"); feederLog(LOG_ERR, "cra: Data shape not yet supported\n"); json_object_put(json); return -1; } else { feederLog(LOG_DEBUG, "cra: data %s\n", json_data_data_str); stringToArrayn(json_data_data_bin, json_data_data_str, strlen(json_data_data_str) / 2); } feederLog(LOG_DEBUG, "cra: binary data: "); uint16_t i; for (i = 0; i < (strlen(json_data_data_str) / 2); i++) feederLog(LOG_DEBUG, "%02X ", json_data_data_bin[i]); feederLog(LOG_DEBUG, "\n"); if (json_data_data_bin[0] == 0xFF)//this is sensob message { feederLog(LOG_DEBUG, "cra: sensob detected\n"); res = sensob_parse(json_data_data_bin, strlen(json_data_data_str) / 2, (uint64_t *)id, tm, result_array, sensors, type); feederLog(LOG_DEBUG, "cra: item sensor %lu, value %f, type %d\n", sensors[0], result_array[0], *type); *id = jid; } // else if (json_data_data_bin[0] == 0xFE)//this is command todo: nebo rozlisit podle portu else if (json_data_data_bin[0] > 0xC0)//this is command todo: nebo rozlisit podle portu { feederLog(LOG_DEBUG, "cra: cmd detected\n"); cmdParse(json_data_data_bin, strlen(json_data_data_str) / 2, jid); } else if (json_data_data_bin[0] >= 0xA0) //this is sensob tiny message { feederLog(LOG_DEBUG, "cra: sensob tiny detected\n"); res = sensobtiny_parse(json_data_data_bin, strlen(json_data_data_str) / 2, json_data_ts_int, tm, result_array, sensors, type); *id = jid; } else //this is sensob lite message { feederLog(LOG_DEBUG, "cra: sensob lite detected\n"); res = sensoblite_parse(json_data_data_bin, strlen(json_data_data_str) / 2, tm, result_array, sensors, type); *id = jid; } result_array[res] = (double)json_data_bat_int; //get lora battery status as sensor measurement sensors[res] = 560030000; res++; return res; } int reply(void * lib_data, int sock, unsigned char *data) { struct unit_t * unit; char * buf; (void)lib_data; (void)sock; unit = unit_now; if (unit == NULL) { //todo: odpovededet nejakym chybou return 0; } if (unit->seq == 0) return 0; time((time_t *)data); /*todo memset(data + 4, 0, 4); for (i = 0; i < RECVD_HISTORY_SIZE; i++) { j = unit->pos - (RECVD_HISTORY_SIZE + i); if (j > RECVD_HISTORY_SIZE) //rotated (less then zero) j += RECVD_HISTORY_SIZE; data[4 + i / 8] |= unit->recvd[j] << (i % 8); } */ buf = (char *)data; time_t now = time(0); struct tm tm = *gmtime(&now); sprintf(buf, "HTTP/1.0 204 NO CONTENT\r\nServer: feeder\r\nContent-Length: %d\r\nDate: ", 0); strftime(buf + strlen(buf), 1000, "%a, %d %b %Y %H:%M:%S %Z", &tm); sprintf(buf + strlen(buf), "\r\n\r\n"); feederLog(LOG_DEBUG, "cra: raw data out: \n%s\n", (char *)data); feederLog(LOG_DEBUG, "cra: replying\n"); unit->seq = 0; return strlen((char *)data); }