int ICACHE_FLASH_ATTR http_relay_api_toggle(http_connection *c) { NODE_DBG("http_wifi_api_get_status"); //wait for whole body if(c->state <HTTPD_STATE_BODY_END) return HTTPD_CGI_MORE; int ret = json_count_token(c->body.data,c->body.len); jsonPair fields[1]={ { .key="relay", .value=NULL } }; if(json_parse(&fields[0],1,c->body.data,c->body.len)){ int relayNumber = atoi(fields[0].value); if(relayNumber<0 || relayNumber >=relay_count()){ http_response_BAD_REQUEST(c); NODE_DBG("Wrong relay"); return HTTPD_CGI_DONE; } unsigned status = relay_get_state(relayNumber); status = relay_toggle_state(relayNumber); //write headers http_SET_HEADER(c,HTTP_CONTENT_TYPE,JSON_CONTENT_TYPE); http_response_OK(c); write_json_object_start(c); write_json_pair_int(c,"relay",relayNumber); write_json_list_separator(c); write_json_pair_int(c,"state",status); write_json_object_end(c); return HTTPD_CGI_DONE; } else{ http_response_BAD_REQUEST(c); return HTTPD_CGI_DONE; } http_response_BAD_REQUEST(c); return HTTPD_CGI_DONE; }
int ICACHE_FLASH_ATTR http_relay_api_toggle(http_connection *c) { NODE_DBG("http_wifi_api_get_status"); //wait for whole body if(c->state <HTTPD_STATE_BODY_END) return HTTPD_CGI_MORE; //parse json cJSON * root = cJSON_Parse(c->body.data); if(root==NULL) goto badrequest; cJSON * relay = cJSON_GetObjectItem(root,"relay"); if(relay==NULL) goto badrequest; int relayNumber = relay->valueint; cJSON_Delete(root); if(relayNumber<0 || relayNumber >=relay_count()){ http_response_BAD_REQUEST(c); NODE_DBG("Wrong relay"); return HTTPD_CGI_DONE; } else{ //valid relay unsigned status = relay_get_state(relayNumber); status = relay_toggle_state(relayNumber); //write headers http_SET_HEADER(c,HTTP_CONTENT_TYPE,JSON_CONTENT_TYPE); http_response_OK(c); //create json root = cJSON_CreateObject(); cJSON_AddNumberToObject(root,"relay",relayNumber); cJSON_AddNumberToObject(root,"state",status); http_write_json(c,root); //delete json struct cJSON_Delete(root); return HTTPD_CGI_DONE; } badrequest: http_response_BAD_REQUEST(c); return HTTPD_CGI_DONE; }
// This makes sure we aren't serving static files on POST requests for example int cgi_enforce_method(http_connection *connData) { enum http_method *method = (enum http_method *)connData->cgi.argument; if(connData->state == HTTPD_STATE_BODY_END) if(connData->parser.method!=*method && (int)*method>=0) { HTTP_CGI_DBG("Wrong HTTP method. Enforce is %d and request is %d\n",method,connData->parser.method); http_response_BAD_REQUEST(connData); return HTTPD_CGI_DONE; } return HTTPD_CGI_NEXT_RULE; }
// This makes sure we have a body int ICACHE_FLASH_ATTR cgi_enforce_body(http_connection *connData) { if(connData->state ==HTTPD_STATE_ON_URL){ http_set_save_body(connData); //request body to be saved } //wait for whole body if(connData->state <HTTPD_STATE_BODY_END) return HTTPD_CGI_NEXT_RULE; //if body empty, bad request if(connData->body.len <=0){ http_response_BAD_REQUEST(connData); NODE_DBG("No body"); return HTTPD_CGI_DONE; } else return HTTPD_CGI_NEXT_RULE; }
// This makes sure we have a body int cgi_enforce_body(http_connection *connData) { if(connData->state ==HTTPD_STATE_ON_URL) { http_set_save_body(connData); }//request body to be saved //wait for whole body if(connData->state <HTTPD_STATE_BODY_END) { HTTP_CGI_DBG("cgi_enforce: next_rule\n"); return HTTPD_CGI_NEXT_RULE; } //if body empty, bad request if(connData->body.len <=0) { http_response_BAD_REQUEST(connData); HTTP_CGI_DBG("No body\n"); return HTTPD_CGI_DONE; } else { return HTTPD_CGI_NEXT_RULE; } }
int http_wifi_api_connect_ap(http_connection *c) { CGI_WIFI_DBG("http_wifi_api_connect_ap\n"); //wait for whole body if(c->state <HTTPD_STATE_BODY_END) { return HTTPD_CGI_MORE; } api_cgi_connect_status * status = c->cgi.data; if(status==NULL) { // CGI_WIFI_DBG("http_wifi_api_connect_ap status NULL\n"); status = (api_cgi_connect_status*)os_malloc(sizeof(api_cgi_connect_status)); status->state=1; c->cgi.data=status; //parse json and validate cJSON * root = cJSON_Parse(c->body.data); if(root==NULL) goto badrequest; cJSON * ssid = cJSON_GetObjectItem(root,"ssid"); if(ssid==NULL) goto badrequest; else if(ssid->type != cJSON_String) goto badrequest; cJSON * pwd = cJSON_GetObjectItem(root,"pwd"); if(pwd==NULL) goto badrequest; else if(pwd->type!=cJSON_String) goto badrequest; //parse ok strncpy(status->ssid,ssid->valuestring,32); strncpy(status->pwd,pwd->valuestring,64); //set timer to connect os_timer_disarm(&status->timer); os_timer_setfn(&status->timer, (os_timer_func_t *)http_execute_cgi, c); os_timer_arm(&status->timer, 10, 0); return HTTPD_CGI_MORE; } else if (status->state==1) { CGI_WIFI_DBG("connect_ap status %d\n",status->state); //try connect if(strlen(status->ssid)>32 || strlen(status->pwd)>64) goto badrequest; CGI_WIFI_DBG("ap connect ssid: %s, pwd: %s\n",status->ssid,status->pwd); strcpy(wifi_status.station_config.ssid,status->ssid); strcpy(wifi_status.station_config.password,status->pwd); wifi_status.station_config.bssid_set=0; wifi_station_disconnect(); wifi_station_set_config(&wifi_status.station_config); wifi_station_connect(); //set timer to check status os_timer_disarm(&status->timer); os_timer_setfn(&status->timer, (os_timer_func_t *)http_execute_cgi, c); os_timer_arm(&status->timer, 500, 0); status->state=2; return HTTPD_CGI_MORE; } else if (status->state==2) { CGI_WIFI_DBG("connect_ap status %d\n",status->state); uint8_t c_status = wifi_station_get_connect_status(); CGI_WIFI_DBG("wifi sta status %d\n",c_status); if (c_status>=2 && c_status <= 4 ) { wifi_station_disconnect(); strcpy(wifi_status.station_config.ssid,""); strcpy(wifi_status.station_config.password,""); wifi_station_set_config(&wifi_status.station_config); } if (c_status==1) { //set timer to check status os_timer_disarm(&status->timer); os_timer_setfn(&status->timer, (os_timer_func_t *)http_execute_cgi, c); os_timer_arm(&status->timer, 500, 0); return HTTPD_CGI_MORE; } else { //write headers http_SET_HEADER(c,HTTP_CONTENT_TYPE,JSON_CONTENT_TYPE); http_response_OK(c); //create json cJSON *root = cJSON_CreateObject(); cJSON_AddNumberToObject(root,"status",c_status); //got ip if(c_status==5) { struct ip_info ip; wifi_get_ip_info(0x0,&ip); char *ip_str = (char*)ipaddr_ntoa(&ip.ip); cJSON_AddStringToObject(root,"ip",ip_str); } else { cJSON_AddStringToObject(root,"ip",""); } http_write_json(c,root); //delete json struct cJSON_Delete(root); status->state=99; return HTTPD_CGI_MORE; } } else //status=99 { CGI_WIFI_DBG("connect_ap status %d\n",status->state); //clean os_free(c->cgi.data); return HTTPD_CGI_DONE; } badrequest: http_response_BAD_REQUEST(c); status->state=99; return HTTPD_CGI_MORE; //shut up compiler return HTTPD_CGI_DONE; }
int http_console_api(http_connection *c) { CGI_CONS_DBG("http_console_api\n"); //wait for whole body if(c->state <HTTPD_STATE_BODY_END) return HTTPD_CGI_MORE; http_SET_HEADER(c,HTTP_CONTENT_TYPE,JSON_CONTENT_TYPE); // http_response_OK(c); //parse json and validate cJSON * root = cJSON_Parse(c->body.data); if(root==NULL) goto badrequest; cJSON * first = NULL; unsigned i; for (i=0; i < CONS_CMDS_CT; i++) { if (cJSON_HasObjectItem(root, console_json[i])) { first = cJSON_GetObjectItem(root,console_json[i]); break; } } if ((first==NULL) || i == CONS_CMDS_CT) { goto badrequest; } if ((first->type != cJSON_String) && (i != cons_start_t) && (i != cons_to_node_t)) { goto badrequest; } int tx = 0; int btn_val = 99; cJSON * cmd_ct = NULL; if (i > 0) { cmd_ct = cJSON_GetObjectItem(root,"ctcnt"); if (cmd_ct==NULL) { goto badrequest; } if (cmd_ct->type != cJSON_Number) { goto badrequest; } btn_val = cmd_ct->valueint; } CGI_CONS_DBG("BTN Value: %d index: %d\n", btn_val, i); switch (i) { case cons_start_t: { char buff[256]; if (first->type != cJSON_Number) { goto badrequest; } int start = first->valueint; int len = 0; // length of text in buff int console_len = (console_wr+BUF_MAX-console_rd) % BUF_MAX; // num chars in console_buf if (start < console_pos) { start = 0; } else if (start >= console_pos+console_len) { start = console_len; } else { start = start - console_pos; } int rd = (console_rd+start) % BUF_MAX; while (len < BUF_MAX && rd != console_wr) { uint8_t c = console_buf[rd]; buff[len++] = c; rd = (rd + 1) % BUF_MAX; } buff[len] = '\0'; //null terminate cJSON_AddNumberToObject(root,"len",console_len-start); cJSON *newSt = cJSON_CreateNumber(console_pos+start); cJSON_ReplaceItemInObject(root, "start", newSt); cJSON_AddStringToObject(root,"text", (const char *)buff); http_write_json(c,root); cJSON_Delete(root); return HTTPD_CGI_DONE; } break; case cons_pwr_t: { sendTxBuff[0] = (uint8_t) (49-btn_val); tx = 1; NODE_DBG("sendtxbuff[0]==%02x\n", sendTxBuff[0]); NODE_DBG("Sending %d bytes to node %d\n",tx, 99); generateRfmTxMsg(99, sendTxBuff, tx, false, false); } break; case cons_to_node_t: { /* Get requested payload */ cJSON * dataJson = cJSON_GetObjectItem(root,"data"); if(dataJson==NULL) { goto badrequest; } else if (dataJson->type != cJSON_String) { goto badrequest; } bool reqAck = false; /* Get ack checkbox value */ cJSON * ackJson = cJSON_GetObjectItem(root,"ack"); if ((ackJson!=NULL) && (ackJson->type == cJSON_Number)) { reqAck = (bool) ackJson->valueint; } int len, nodeid; len = strlen(dataJson->valuestring); NODE_DBG("send len = %d\n", len); nodeid = first->valueint; if ((len > 0) && (nodeid>0) && (nodeid < 255)) { CGI_CONS_DBG("Sending %s to node %d\n", dataJson->valuestring, nodeid); generateRfmTxMsg((uint8_t)nodeid, dataJson->valuestring, len, reqAck, false); } } break; case cons_disp_t: { rfm_toggle_all_output(); } break; } /* End cons_method_t Switch() */ http_response_OK(c); cJSON_Delete(root); return HTTPD_CGI_DONE; badrequest: http_response_BAD_REQUEST(c); cJSON_Delete(root); return HTTPD_CGI_DONE; //shut up compiler return HTTPD_CGI_DONE; }
//Cgi that check the request has the correct HOST header //Using it we can ensure our server has a domain of our choice int ICACHE_FLASH_ATTR cgi_check_host(http_connection *connData) { http_server_config *config = (http_server_config*)connData->cgi.argument; if(config==NULL) return HTTPD_CGI_NEXT_RULE; if(config->host_domain==NULL) return HTTPD_CGI_NEXT_RULE; if(connData->state==HTTPD_STATE_ON_URL){ http_set_save_header(connData,HTTP_HOST); return HTTPD_CGI_NEXT_RULE; } if(connData->state==HTTPD_STATE_HEADERS_END){ header *hostHeader = http_get_header(connData,HTTP_HOST); if(hostHeader==NULL){ NODE_DBG("Host header not found"); http_response_BAD_REQUEST(connData); return HTTPD_CGI_DONE; } const char * domain = config->host_domain; NODE_DBG("Host header found: %s, domain: %s",hostHeader->value,domain); if(os_strncmp(hostHeader->value,domain,strlen(domain))==0) //compare ignoring http:// and last / { NODE_DBG("Hosts match"); return HTTPD_CGI_NEXT_RULE; } else{ NODE_DBG("Hosts don't match"); if(config->enable_captive){ //to enable a captive portal we should redirect here char * redirectUrl = (char *)os_zalloc(strlen(domain)+9); // domain lenght + http:// + / + \0 os_strcpy(redirectUrl,"http://"); os_strcat(redirectUrl,domain); os_strcat(redirectUrl,"/"); http_response_REDIRECT(connData, redirectUrl); os_free(redirectUrl); } else{ //bad request http_response_BAD_REQUEST(connData); } return HTTPD_CGI_DONE; } } return HTTPD_CGI_NEXT_RULE; }
//Cgi that check the request has the correct HOST header //Using it we can ensure our server has a domain of our choice int cgi_check_host(http_connection *connData) { http_server_config *config = (http_server_config*)connData->cgi.argument; if(config==NULL) return HTTPD_CGI_NEXT_RULE; if(config->host_domain==NULL) return HTTPD_CGI_NEXT_RULE; if(connData->state==HTTPD_STATE_ON_URL) { http_set_save_header(connData,HTTP_HOST); return HTTPD_CGI_NEXT_RULE; } if(connData->state==HTTPD_STATE_HEADERS_END) { header *hostHeader = http_get_header(connData,HTTP_HOST); if(hostHeader==NULL) { NODE_ERR("Host header not found\n"); http_response_BAD_REQUEST(connData); return HTTPD_CGI_DONE; } const char * domain = config->host_domain; HTTP_CGI_DBG("Host header: %s, domain: %s\n",hostHeader->value,domain); if(strncmp(hostHeader->value,domain,strlen(domain))==0) //compare ignoring http:// and last / { HTTP_CGI_DBG("Domain match\n"); return HTTPD_CGI_NEXT_RULE; } else{ uint8_t op = wifi_get_opmode(); char ipaddrstr[17]; os_bzero(ipaddrstr, sizeof(ipaddrstr)); struct ip_info ipConfig; switch (op) { case STATIONAP_MODE: { wifi_get_ip_info(SOFTAP_IF,&ipConfig); //0x01 ipaddr_ntoa_r(&ipConfig.ip,ipaddrstr, sizeof(ipaddrstr)); if(strncmp(hostHeader->value,ipaddrstr,strlen(ipaddrstr))==0) { HTTP_CGI_DBG("SoftAp ip match"); return HTTPD_CGI_NEXT_RULE; } } case STATION_MODE: { os_bzero(ipaddrstr, sizeof(ipaddrstr)); wifi_get_ip_info(STATION_IF,&ipConfig); //0x00 ipaddr_ntoa_r(&ipConfig.ip,ipaddrstr, sizeof(ipaddrstr)); if(strncmp(hostHeader->value,ipaddrstr,strlen(ipaddrstr))==0) { HTTP_CGI_DBG("Station ip match"); return HTTPD_CGI_NEXT_RULE; } } } HTTP_CGI_DBG("Hosts don't match\n"); if(config->enable_captive) { //to enable a captive portal we should redirect here char * redirectUrl = (char *)os_zalloc(strlen(domain)+9); // domain length + http:// + / + \0 strcpy(redirectUrl,"http://"); os_strcat(redirectUrl,domain); os_strcat(redirectUrl,"/"); http_response_REDIRECT(connData, redirectUrl); os_free(redirectUrl); HTTP_CGI_DBG("Redirect URL = %s\n", redirectUrl); } else { //bad request else http_response_BAD_REQUEST(connData); } return HTTPD_CGI_DONE; } } return HTTPD_CGI_NEXT_RULE; }
int ICACHE_FLASH_ATTR http_ws_handle_connect(http_connection *c) { NODE_DBG("http_ws_handle_connect c =%p",c); if(c->state == HTTPD_STATE_ON_URL){ http_set_save_header(c,HTTP_ORIGIN); http_set_save_header(c,HTTP_CONNECTION); http_set_save_header(c,HTTP_UPGRADE); http_set_save_header(c,HTTP_SEC_WEBSOCKET_KEY); http_set_save_header(c,HTTP_SEC_WEBSOCKET_PROTOCOL); http_set_save_header(c,HTTP_SEC_WEBSOCKET_VERSION); return HTTP_WS_CGI_MORE; } //wait handshake request complete if(c->state != HTTPD_STATE_BODY_END) return HTTP_WS_CGI_MORE; header * upgrade_header = http_get_header(c,HTTP_UPGRADE); header * connection_header = http_get_header(c,HTTP_CONNECTION); header * origin_header = http_get_header(c,HTTP_ORIGIN); header * key_header = http_get_header(c,HTTP_SEC_WEBSOCKET_KEY); if(upgrade_header==NULL) goto badrequest; if(connection_header==NULL) goto badrequest; if(origin_header==NULL) goto badrequest; if(key_header==NULL) goto badrequest; NODE_DBG("headers ok"); if(os_strcasecmp(upgrade_header->value,"websocket")!=0) goto badrequest; // Following (https://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17) //calculate sha1 of concatenetion key+uuid uint8_t digest[20]; //sha1 is always 20 byte long SHA1_CTX ctx; SHA1_Init(&ctx); SHA1_Update(&ctx,key_header->value,os_strlen(key_header->value)); SHA1_Update(&ctx,ws_uuid,os_strlen(ws_uuid)); SHA1_Final(digest,&ctx); char base64Digest[31]; // Base64encode(base64Digest,(const char*)digest,20); //accept the handshake http_SET_HEADER(c,HTTP_UPGRADE,"WebSocket"); http_SET_HEADER(c,HTTP_CONNECTION,"Upgrade"); http_SET_HEADER(c,HTTP_WEBSOCKET_ACCEPT,base64Digest); http_websocket_HANDSHAKE(c); c->handshake_ok=1; if(client_connected_callback!=NULL) client_connected_callback(c); return HTTP_WS_CGI_MORE; badrequest: http_response_BAD_REQUEST(c); return HTTP_WS_CGI_DONE; }