#include #include #include #include #include #include #include #include #include #include #include #include //~ #include "json.h" #include "jsmn.h" //~ #define MAXLINE 4096 #define MAXLINE 5000 static char logline[MAXLINE]=""; static char current_time[21]=""; static time_t epoch_time=-1; static unsigned short PORT = 8080; static int socket_fd; static int st_log_to_file=0; static FILE *file_fd=NULL; #define LATEST 500 #define MAXDEVEUI 100 static char deveui[MAXDEVEUI][17]; static unsigned short deveui_count=0; sqlite3 *db; const char sqlite3_name[]="http-okay.db"; static int rc; char *errMsg = 0; const char *sql_table_cnx = "CREATE TABLE IF NOT EXISTS cnx(" "id INTEGER PRIMARY KEY AUTOINCREMENT," "ip CHAR(16)," "port," "date CHAR(21));"; const char *sql_table_content = "CREATE TABLE IF NOT EXISTS content(" "id INTEGER PRIMARY KEY AUTOINCREMENT," "date CHAR(21)," "epoch INTEGER," "method CHAR(16)," "host CHAR(256)," "path CHAR(2048)," "version CHAR(16)," "user_agent CHAR(256)," "content CHAR(4096));"; const char sql_table_name[] = "content"; const char sql_query_format[] = "SELECT date,content FROM %s ORDER BY id DESC LIMIT %d;"; const char html_output[]="index.2.html"; #define APPNAME "ApplicationServer" const char html_header[]="\n" "\n" "\n" "\n" "\n" ""APPNAME"\n" "\n" "\n" "\n" "

"APPNAME"

\n"; const char html_header_2[]="\n" "\n" "\n"; const char html_stat_start[]="\n\n" "
\n" "
\n"; const char html_stat_end[]="
\n
\n"; static char *get_rfc3339() { epoch_time = time(NULL); struct tm *tm_info; tm_info = gmtime(&epoch_time); strftime(current_time, 21, "%Y-%m-%dT%H:%M:%SZ", tm_info); return current_time; } const char* colorSnr(float f) { if( f < -7.5) { return "sf12"; } else if( f < -5) { return "sf11"; } else if( f < -2.5) { return "sf10"; } else if( f < 0) { return "sf9"; } else if( f < 2.5) { return "sf8"; } else { return "sf7"; } } const char* colorRssi(float f) { if( f < -105) { return "sf12"; } else if( f < -95) { return "sf11"; } else if( f < -85) { return "sf10"; } else if( f < 80) { return "sf9"; } else { return "sf7"; } } static void *stats_box(char *stats) { //~ char stats[4096]=""; strncat(stats,html_stat_start,sizeof(html_stat_start)); for(int i =0; i < deveui_count;i++) { char temp[256]=""; sprintf(temp,"
%s
\n", deveui[i], deveui[i], deveui[i]); strcat(stats,temp); } strncat(stats,html_stat_end,sizeof(html_stat_end)); } static void usage() { printf("Usage: http-okay \n"); exit(0); } static void mylog() { printf("%s",logline); fflush(stdout); if( 0 != st_log_to_file ) { if( NULL != file_fd ) { fwrite(logline,1,strlen(logline),file_fd); fflush(file_fd); } } } static int jsoneq(const char *json, jsmntok_t *tok, const char *s) { if (tok->type == JSMN_STRING && (int)strlen(s) == tok->end - tok->start && strncmp(json + tok->start, s, tok->end - tok->start) == 0) { return 0; } return -1; } typedef struct { char method[16]; char path[2048]; char version[16]; char user_agent[256]; char host[256]; char content[4096]; } http_req_t; void http_req_parse(const char *request, http_req_t *http_request) { char *request_copy = strdup(request); char *line = strtok(request_copy, "\r\n"); if (line) { sscanf(line, "%15s %255s %15s", http_request->method, http_request->path, http_request->version); } bzero(&http_request->content, 4096); bzero(&http_request->host, 256); bzero(&http_request->user_agent, 256); int content_length = 0; while (line != NULL && strlen(line) > 0) { if (strncmp(line, "User-Agent: ", 12) == 0) { sscanf(line + 12, "%255[^\r\n]", http_request->user_agent); } else if (strncmp(line, "Host: ", 6) == 0) { sscanf(line + 6, "%255[^\r\n]", http_request->host); } else if (strncmp(line, "Content-Length: ", 16) == 0) { sscanf(line + 16, "%d", &content_length); } //printf("DEBUG: %s\n", line); line = strtok(NULL, "\r\n"); } if( content_length ) { strncpy(http_request->content,request+strlen(request)-content_length,content_length); } free(request_copy); } static void signal_int_handler() { printf("\nExiting\n"); close(socket_fd); if( NULL != file_fd ) { fclose(file_fd); } exit(0); } static int mysqlite3_callback(void *NotUsed, int argc, char **argv, char **azColName) { for (int i = 0; i < argc; i++) { printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL"); } printf("\n"); return 0; } void mysqlite3_create() { rc = sqlite3_open(sqlite3_name, &db); if(rc) { fprintf(stderr,"Can't open database: %s\n", sqlite3_errmsg(db)); } else { snprintf(logline, MAXLINE, "%s : Database %s open success\n",get_rfc3339(), sqlite3_name); mylog(); } rc = sqlite3_exec(db, sql_table_cnx, mysqlite3_callback, 0, &errMsg); if (rc != SQLITE_OK) { fprintf(stderr,"SQL error: %s\n", errMsg); sqlite3_free(errMsg); } else { snprintf(logline, MAXLINE, "%s : Table CNX create success\n", get_rfc3339()); mylog(); } rc = sqlite3_exec(db, sql_table_content, mysqlite3_callback, 0, &errMsg); if (rc != SQLITE_OK) { fprintf(stderr,"SQL error: %s\n", errMsg); sqlite3_free(errMsg); } else { snprintf(logline, MAXLINE, "%s : Table CONTENT create success\n", get_rfc3339()); mylog(); } } void mysqlite3_begin() { const char *sqlBeginTransaction = "BEGIN TRANSACTION;"; rc = sqlite3_exec(db, sqlBeginTransaction, mysqlite3_callback, 0, &errMsg); if (rc != SQLITE_OK) { fprintf(stderr, "SQL error (Begin Transaction): %s\n", errMsg); sqlite3_free(errMsg); } } void mysqlite3_commit() { const char *sqlCommit = "COMMIT;"; rc = sqlite3_exec(db, sqlCommit, mysqlite3_callback, 0, &errMsg); if (rc != SQLITE_OK) { fprintf(stderr, "SQL error (Commit): %s\n", errMsg); sqlite3_free(errMsg); } } void mysqlite3_content_insert(http_req_t *http_req) { mysqlite3_begin(); const char *sql_insert_content_templ = "INSERT INTO content (date, epoch, host, method, path, version, user_agent, content) " "VALUES ('%s', %d, '%s', '%s', '%s', '%s', '%s', '%s');"; char sql_insert_content_values[4096]; bzero(sql_insert_content_values, 4096); strcat(sql_insert_content_values,"INSERT INTO content (date, epoch, host, method, path, version, user_agent, content) VALUES ("); char sql_str_templ[16]="'%s',"; char sql_int_templ[16]="%d,"; char sql_str_templ_last[16]="'%s');"; char sql_str_temp[4095]; sprintf(sql_str_temp,sql_str_templ,get_rfc3339()); strcat(sql_insert_content_values,sql_str_temp); sprintf(sql_str_temp,sql_int_templ,epoch_time); strcat(sql_insert_content_values,sql_str_temp); sprintf(sql_str_temp,sql_str_templ,http_req->host); strcat(sql_insert_content_values,sql_str_temp); sprintf(sql_str_temp,sql_str_templ,http_req->method); strcat(sql_insert_content_values,sql_str_temp); sprintf(sql_str_temp,sql_str_templ,http_req->path); strcat(sql_insert_content_values,sql_str_temp); sprintf(sql_str_temp,sql_str_templ,http_req->version); strcat(sql_insert_content_values,sql_str_temp); sprintf(sql_str_temp,sql_str_templ,http_req->user_agent); strcat(sql_insert_content_values,sql_str_temp); sprintf(sql_str_temp,sql_str_templ_last,http_req->content); strcat(sql_insert_content_values,sql_str_temp); rc = sqlite3_exec(db, sql_insert_content_values, mysqlite3_callback, 0, &errMsg); if (rc != SQLITE_OK) { fprintf(stderr, "SQL error (Insert): %s\n", errMsg); sqlite3_free(errMsg); } mysqlite3_commit(); } void mysqlite3_cnx_insert(char *date,char *ip,unsigned short port) { mysqlite3_begin(); const char *sql_insert_cnx_templ = "INSERT INTO cnx (ip, port, date) " "VALUES ('%s', %d, '%s'); "; char sql_insert_cnx_values[256]=""; snprintf(sql_insert_cnx_values,256,sql_insert_cnx_templ,ip,port,date); rc = sqlite3_exec(db, sql_insert_cnx_values, mysqlite3_callback, 0, &errMsg); if (rc != SQLITE_OK) { fprintf(stderr, "SQL error (Insert): %s\n", errMsg); sqlite3_free(errMsg); } mysqlite3_commit(); } void mysqlite3_query(int limit) { char sql_query[256]; snprintf(sql_query, sizeof(sql_query), sql_query_format, sql_table_name, limit); snprintf(logline, MAXLINE, "%s : Extracting latest %d records\n", get_rfc3339(), limit); mylog(); sqlite3_stmt *stmt; if (sqlite3_prepare_v2(db, sql_query, -1, &stmt, NULL) == SQLITE_OK) { int count=0; FILE *fd = fopen(html_output,"wt"); fwrite(html_header,sizeof(html_header)-1,1,fd); fwrite(html_header_2,sizeof(html_header_2)-1,1,fd); char generated_date[64]=""; //memset(generated_date, 0x0, 64 ); bzero(generated_date,64); sprintf(generated_date,"\n

Generated at %s

\n", get_rfc3339()); fwrite(generated_date,strlen(generated_date),1,fd); //~ fwrite(html_stat_start,sizeof(html_stat_start),1,fd); //~ fwrite(html_stat_end,sizeof(html_stat_end),1,fd); char html_table[4096]=""; memset(html_table, 0x0, 4096); snprintf(html_table, 4096, "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); fwrite(html_table,strlen(html_table),1,fd); while (sqlite3_step(stmt) == SQLITE_ROW) { const unsigned char *rec_content = sqlite3_column_text(stmt, 1); const unsigned char *rec_date = sqlite3_column_text(stmt, 0); int r; jsmn_parser p; jsmn_init(&p); jsmntok_t t[256]; r = jsmn_parse(&p, rec_content, strlen(rec_content), t, sizeof(t) / sizeof(t[0])); if (r < 0) { snprintf(logline, MAXLINE, "%s : Failed to parse JSON: %d\n", get_rfc3339(), r); mylog(); } if (r < 1 || t[0].type != JSMN_OBJECT) { snprintf(logline, MAXLINE, "%s : Missing object\n", get_rfc3339()); mylog(); } else { char *p_Time= NULL; char *p_DevEUI= NULL; char *p_FPort= NULL; char *p_FCntUp= NULL; char *p_FCntDn= NULL; char *p_payload_hex= NULL; char *p_SpFact= NULL; char *p_LrrRSSI= NULL; float f_LrrRSSI= -199; char *p_LrrSNR= NULL; float f_LrrSNR= 99; char *p_Channel= NULL; char *p_Lrrid= NULL; char *p_Late= NULL; unsigned short i_Late=0; char *p_DevLrrCnt= NULL; char *p_rawMacCommands= NULL; char empty[]=""; int free_p_rawMacCommands=0; for (int i = 1; i < r; i++) { if (jsoneq(rec_content, &t[i], "Time") == 0) { p_Time=strndup(rec_content + t[i + 1].start,t[i + 1].end - t[i + 1].start); //~ printf(" p_Time: %s\n", p_Time); i++; } else if (jsoneq(rec_content, &t[i], "DevEUI") == 0) { p_DevEUI=strndup(rec_content + t[i + 1].start,t[i + 1].end - t[i + 1].start); //~ printf(" p_DevEUI: %s\n", p_DevEUI); i++; } else if (jsoneq(rec_content, &t[i], "FPort") == 0) { p_FPort=strndup(rec_content + t[i + 1].start,t[i + 1].end - t[i + 1].start); //~ printf(" p_FPort: %s\n", p_FPort); i++; } else if (jsoneq(rec_content, &t[i], "FCntUp") == 0) { p_FCntUp=strndup(rec_content + t[i + 1].start,t[i + 1].end - t[i + 1].start); //~ printf(" p_FCntUp: %s\n", p_FCntUp); i++; } else if (jsoneq(rec_content, &t[i], "FCntDn") == 0) { p_FCntDn=strndup(rec_content + t[i + 1].start,t[i + 1].end - t[i + 1].start); //~ printf(" p_FCntDn: %s\n", p_FCntDn); i++; } else if (jsoneq(rec_content, &t[i], "payload_hex") == 0) { p_payload_hex=strndup(rec_content + t[i + 1].start,t[i + 1].end - t[i + 1].start); //~ printf(" p_payload_hex: %s\n", p_payload_hex); i++; } else if (jsoneq(rec_content, &t[i], "SpFact") == 0) { p_SpFact=strndup(rec_content + t[i + 1].start,t[i + 1].end - t[i + 1].start); //~ printf(" p_SpFact: %s\n", p_SpFact); i++; } else if (jsoneq(rec_content, &t[i], "LrrRSSI") == 0) { p_LrrRSSI=strndup(rec_content + t[i + 1].start,t[i + 1].end - t[i + 1].start); f_LrrRSSI=(float)strtod(p_LrrRSSI,NULL); //~ printf(" p_LrrRSSI: %s\n", p_LrrRSSI); i++; } else if (jsoneq(rec_content, &t[i], "LrrSNR") == 0) { p_LrrSNR=strndup(rec_content + t[i + 1].start,t[i + 1].end - t[i + 1].start); //~ printf(" p_LrrSNR: %s\n", p_LrrSNR); f_LrrSNR=(float)strtod(p_LrrSNR,NULL); i++; } else if (jsoneq(rec_content, &t[i], "Channel") == 0) { p_Channel=strndup(rec_content + t[i + 1].start,t[i + 1].end - t[i + 1].start); //~ printf(" p_Channel: %s\n", p_Channel); i++; } else if (jsoneq(rec_content, &t[i], "Lrrid") == 0) { p_Lrrid=strndup(rec_content + t[i + 1].start,t[i + 1].end - t[i + 1].start); //~ printf(" p_Lrrid: %s\n", p_Lrrid); i++; } else if (jsoneq(rec_content, &t[i], "Late") == 0) { p_Late=strndup(rec_content + t[i + 1].start,t[i + 1].end - t[i + 1].start); //~ printf(" p_Late: %s\n", p_Late); i_Late=atoi(p_Late); //~ printf(" i_Late: %d\n", i_Late); i++; } else if (jsoneq(rec_content, &t[i], "DevLrrCnt") == 0) { p_DevLrrCnt=strndup(rec_content + t[i + 1].start,t[i + 1].end - t[i + 1].start); //~ printf(" p_DevLrrCnt: %s\n", p_DevLrrCnt); i++; } else if (jsoneq(rec_content, &t[i], "rawMacCommands") == 0) { //~ printf(" p_rawMacCommands: %s\n", p_rawMacCommands); if( 0 == t[i + 1].end - t[i + 1].start ) { p_rawMacCommands=(char *)empty; } else { p_rawMacCommands=strndup(rec_content + t[i + 1].start,t[i + 1].end - t[i + 1].start); free_p_rawMacCommands=1; } i++; } } char uplink_type[32]="arrow-up"; if(i_Late) { strcpy(uplink_type,"arrow-up-gray"); } if(!p_rawMacCommands) { p_rawMacCommands=empty; } unsigned short found=0; for(int j = 0; j\n", p_DevEUI, uplink_type, rec_content, p_Time, p_DevEUI, p_FPort, p_FCntUp, p_FCntDn, p_payload_hex, p_SpFact, p_SpFact, colorRssi(f_LrrRSSI), p_LrrRSSI, colorSnr(f_LrrSNR), p_LrrSNR, p_Channel, p_Lrrid, p_Late, p_DevLrrCnt, p_rawMacCommands); fwrite(table_line,strlen(table_line),1,fd); if(p_Time) free(p_Time); if(p_DevEUI) free(p_DevEUI); if(p_FPort) free(p_FPort); if(p_FCntUp) free(p_FCntUp); if(p_FCntDn) free(p_FCntDn); if(p_payload_hex) free(p_payload_hex); if(p_SpFact) free(p_SpFact); if(p_LrrRSSI) free(p_LrrRSSI); if(p_LrrSNR) free(p_LrrSNR); if(p_Channel) free(p_Channel); if(p_Lrrid) free(p_Lrrid); if(p_Late) free(p_Late); if(p_DevLrrCnt) free(p_DevLrrCnt); if(p_rawMacCommands && free_p_rawMacCommands ) free(p_rawMacCommands); } //snprintf(logline, MAXLINE, "%s : Record%d: %s\n", get_rfc3339(), count, rec_date); //mylog(); count++; } fwrite("
TimeDevEUIFPortFCntUpFCntDnPayloadSFRSSISNRLCLRRIDLateCountMAC
%s
%s
%s%s%s%s%s%s%s%s%s%s%s%s%s
\n",strlen("\n"),1,fd); char all_stats[4096]; bzero(all_stats,4096); stats_box(all_stats); fwrite(all_stats,strlen(all_stats),1,fd); fclose(fd); sqlite3_finalize(stmt); } else { fprintf(stderr, "SQL error: %s\n", sqlite3_errmsg(db)); } } void response_to_client(int connection_fd) { char buf[4096] = {0}; while (1) { const int read_size = read(connection_fd,&buf, sizeof(buf)); if (read_size == -1) { if (errno == EINTR) { continue; } printf("Unable to read socket : %s\n", strerror(errno)); exit(1); } if (read_size == 0) { return; } break; } snprintf(logline, MAXLINE, "%s : %s\n", get_rfc3339(), buf); mylog(); snprintf(logline, MAXLINE, "%s : ------------\n",get_rfc3339()); mylog(); http_req_t http_request; http_req_parse(buf,&http_request); /* printf(" Method: %s\n", http_request.method); printf(" Host: %s\n", http_request.host); printf(" Path: %s\n", http_request.path); printf(" Version: %s\n", http_request.version); printf(" User-Agent: %s\n", http_request.user_agent); printf(" Content: %s\n", http_request.content); */ mysqlite3_content_insert(&http_request); const char msg[] = "HTTP/1.1 200 OK\r\n" "Content-Type: text/plain\r\n" "Content-Length: 2\r\n\r\n" "OK\r\n"; int offset = 0; while (offset < strlen(msg)) { while (1) { int write_response = write(connection_fd, msg+offset, strlen(msg+offset)); if (write_response == -1) { if (errno == EINTR) { continue; } printf("Unable to write socket : %s\n", strerror(errno)); exit(1); } offset += write_response; break; } } } void main(int argc,char **argv) { signal(SIGINT, signal_int_handler); if(argc > 1) { if( 0 == strcmp(argv[1], "help")) { usage(); } else if( 0 == strcmp(argv[1], "--help")) { usage(); } if( argc > 2 ) { st_log_to_file=1; file_fd=fopen(argv[2],"at+"); } } for(int i = 0; i < MAXDEVEUI; i++ ){ strcpy(deveui[i],"-1"); } //SQLITE mysqlite3_create(); mysqlite3_query(LATEST); socket_fd = socket(AF_INET, SOCK_STREAM, 0); if (socket_fd == -1) { fprintf(stderr,"Unable to create socket file descriptor: %s\n", strerror(errno)); exit(1); } struct sockaddr_in server_address, client_address; bzero(&server_address, sizeof(server_address)); server_address.sin_family = AF_INET; if ( argc > 1 ) { PORT = atoi(argv[1]); } server_address.sin_port = htons(PORT); snprintf(logline, MAXLINE, "%s : Listening on %d\n", get_rfc3339(), PORT); mylog(); server_address.sin_addr.s_addr = htonl(INADDR_ANY); const int reuse = 1; const int reuse_addr_response = setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)); if (reuse_addr_response == -1) { fprintf(stderr, "Unable to set SO_REUSEADDR to socket\n"); exit(1); } const int bind_response = bind(socket_fd, (struct sockaddr *)&server_address, sizeof(server_address)); if (bind_response == -1) { fprintf(stderr, "Unable to bind the socket to file descriptor : %s\n", strerror(errno)); exit(1); } const int listen_response = listen(socket_fd, 5); if (listen_response != 0) { fprintf(stderr, "Unable to listen to port %d: %s", PORT, strerror(errno)); exit(1); } while (1) { unsigned int client_socket_len = sizeof(client_address); const int connection_fd = accept(socket_fd, (struct sockaddr*)&client_address, &client_socket_len); if (connection_fd == -1) { fprintf(stderr, "Unable to accept incoming connection : %s\n", strerror(errno)); } char client_ip[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &client_address.sin_addr, client_ip, INET_ADDRSTRLEN); snprintf(logline, MAXLINE, "%s : Connection from %s:%d\n", get_rfc3339(), client_ip, ntohs(client_address.sin_port)); mylog(); mysqlite3_cnx_insert(get_rfc3339(),client_ip,ntohs(client_address.sin_port)); response_to_client(connection_fd); close(connection_fd); mysqlite3_query(LATEST); } }