1 // Copyright 2019 Espressif Systems (Shanghai) PTE LTD
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include <stdio.h>
16 #include <string.h>
17 #include <esp_err.h>
18 #include <esp_log.h>
19 
20 #include <esp_wifi.h>
21 #include <esp_netif.h>
22 
23 #include "wifi_provisioning/wifi_config.h"
24 #include "wifi_provisioning/wifi_scan.h"
25 #include "wifi_provisioning/manager.h"
26 #include "wifi_provisioning_priv.h"
27 
28 static const char *TAG = "wifi_prov_handlers";
29 
30 /* Provide definition of wifi_prov_ctx_t */
31 struct wifi_prov_ctx {
32     wifi_config_t wifi_cfg;
33 };
34 
get_config(wifi_prov_ctx_t ** ctx)35 static wifi_config_t *get_config(wifi_prov_ctx_t **ctx)
36 {
37     return (*ctx ? & (*ctx)->wifi_cfg : NULL);
38 }
39 
new_config(wifi_prov_ctx_t ** ctx)40 static wifi_config_t *new_config(wifi_prov_ctx_t **ctx)
41 {
42     free(*ctx);
43     (*ctx) = (wifi_prov_ctx_t *) calloc(1, sizeof(wifi_prov_ctx_t));
44     return get_config(ctx);
45 }
46 
free_config(wifi_prov_ctx_t ** ctx)47 static void free_config(wifi_prov_ctx_t **ctx)
48 {
49     free(*ctx);
50     *ctx = NULL;
51 }
52 
get_status_handler(wifi_prov_config_get_data_t * resp_data,wifi_prov_ctx_t ** ctx)53 static esp_err_t get_status_handler(wifi_prov_config_get_data_t *resp_data, wifi_prov_ctx_t **ctx)
54 {
55     /* Initialize to zero */
56     memset(resp_data, 0, sizeof(wifi_prov_config_get_data_t));
57 
58     if (wifi_prov_mgr_get_wifi_state(&resp_data->wifi_state) != ESP_OK) {
59         ESP_LOGW(TAG, "Wi-Fi provisioning manager not running");
60         return ESP_ERR_INVALID_STATE;
61     }
62 
63     if (resp_data->wifi_state == WIFI_PROV_STA_CONNECTED) {
64         ESP_LOGD(TAG, "Got state : connected");
65 
66         /* IP Addr assigned to STA */
67         esp_netif_ip_info_t ip_info;
68         esp_netif_get_ip_info(esp_netif_get_handle_from_ifkey("WIFI_STA_DEF"), &ip_info);
69         esp_ip4addr_ntoa(&ip_info.ip, resp_data->conn_info.ip_addr, sizeof(resp_data->conn_info.ip_addr));
70 
71 
72         /* AP information to which STA is connected */
73         wifi_ap_record_t ap_info;
74         esp_wifi_sta_get_ap_info(&ap_info);
75         memcpy(resp_data->conn_info.bssid, (char *)ap_info.bssid, sizeof(ap_info.bssid));
76         memcpy(resp_data->conn_info.ssid,  (char *)ap_info.ssid,  sizeof(ap_info.ssid));
77         resp_data->conn_info.channel   = ap_info.primary;
78         resp_data->conn_info.auth_mode = ap_info.authmode;
79 
80         /* Tell manager to stop provisioning service */
81         wifi_prov_mgr_done();
82     } else if (resp_data->wifi_state == WIFI_PROV_STA_DISCONNECTED) {
83         ESP_LOGD(TAG, "Got state : disconnected");
84 
85         /* If disconnected, convey reason */
86         wifi_prov_mgr_get_wifi_disconnect_reason(&resp_data->fail_reason);
87     } else {
88         ESP_LOGD(TAG, "Got state : connecting");
89     }
90     return ESP_OK;
91 }
92 
set_config_handler(const wifi_prov_config_set_data_t * req_data,wifi_prov_ctx_t ** ctx)93 static esp_err_t set_config_handler(const wifi_prov_config_set_data_t *req_data, wifi_prov_ctx_t **ctx)
94 {
95     wifi_config_t *wifi_cfg = get_config(ctx);
96     if (wifi_cfg) {
97         free_config(ctx);
98     }
99 
100     wifi_cfg = new_config(ctx);
101     if (!wifi_cfg) {
102         ESP_LOGE(TAG, "Unable to allocate Wi-Fi config");
103         return ESP_ERR_NO_MEM;
104     }
105 
106     ESP_LOGD(TAG, "Wi-Fi Credentials Received");
107 
108     /* Using memcpy allows the max SSID length to be 32 bytes (as per 802.11 standard).
109      * But this doesn't guarantee that the saved SSID will be null terminated, because
110      * wifi_cfg->sta.ssid is also 32 bytes long (without extra 1 byte for null character).
111      * Although, this is not a matter for concern because esp_wifi library reads the SSID
112      * upto 32 bytes in absence of null termination */
113     const size_t ssid_len = strnlen(req_data->ssid, sizeof(wifi_cfg->sta.ssid));
114     /* Ensure SSID less than 32 bytes is null terminated */
115     memset(wifi_cfg->sta.ssid, 0, sizeof(wifi_cfg->sta.ssid));
116     memcpy(wifi_cfg->sta.ssid, req_data->ssid, ssid_len);
117 
118     /* Using strlcpy allows both max passphrase length (63 bytes) and ensures null termination
119      * because size of wifi_cfg->sta.password is 64 bytes (1 extra byte for null character) */
120     strlcpy((char *) wifi_cfg->sta.password, req_data->password, sizeof(wifi_cfg->sta.password));
121     return ESP_OK;
122 }
123 
apply_config_handler(wifi_prov_ctx_t ** ctx)124 static esp_err_t apply_config_handler(wifi_prov_ctx_t **ctx)
125 {
126     wifi_config_t *wifi_cfg = get_config(ctx);
127     if (!wifi_cfg) {
128         ESP_LOGE(TAG, "Wi-Fi config not set");
129         return ESP_ERR_INVALID_STATE;
130     }
131 
132     esp_err_t ret = wifi_prov_mgr_configure_sta(wifi_cfg);
133     if (ret == ESP_OK) {
134         ESP_LOGD(TAG, "Wi-Fi Credentials Applied");
135     } else {
136         ESP_LOGE(TAG, "Failed to apply Wi-Fi Credentials");
137     }
138 
139     free_config(ctx);
140     return ret;
141 }
142 
get_wifi_prov_handlers(wifi_prov_config_handlers_t * ptr)143 esp_err_t get_wifi_prov_handlers(wifi_prov_config_handlers_t *ptr)
144 {
145     if (!ptr) {
146         return ESP_ERR_INVALID_ARG;
147     }
148     ptr->get_status_handler   = get_status_handler;
149     ptr->set_config_handler   = set_config_handler;
150     ptr->apply_config_handler = apply_config_handler;
151     ptr->ctx = NULL;
152     return ESP_OK;
153 }
154 
155 /*************************************************************************/
156 
scan_start(bool blocking,bool passive,uint8_t group_channels,uint32_t period_ms,wifi_prov_scan_ctx_t ** ctx)157 static esp_err_t scan_start(bool blocking, bool passive,
158                             uint8_t group_channels, uint32_t period_ms,
159                             wifi_prov_scan_ctx_t **ctx)
160 {
161     return wifi_prov_mgr_wifi_scan_start(blocking, passive, group_channels, period_ms);
162 }
163 
scan_status(bool * scan_finished,uint16_t * result_count,wifi_prov_scan_ctx_t ** ctx)164 static esp_err_t scan_status(bool *scan_finished,
165                              uint16_t *result_count,
166                              wifi_prov_scan_ctx_t **ctx)
167 {
168     *scan_finished = wifi_prov_mgr_wifi_scan_finished();
169     *result_count  = wifi_prov_mgr_wifi_scan_result_count();
170     return ESP_OK;
171 }
172 
scan_result(uint16_t result_index,wifi_prov_scan_result_t * result,wifi_prov_scan_ctx_t ** ctx)173 static esp_err_t scan_result(uint16_t result_index,
174                              wifi_prov_scan_result_t *result,
175                              wifi_prov_scan_ctx_t **ctx)
176 {
177     const wifi_ap_record_t *record = wifi_prov_mgr_wifi_scan_result(result_index);
178     if (!record) {
179         return ESP_FAIL;
180     }
181 
182     /* Compile time check ensures memory safety in case SSID length in
183      * record / result structure definition changes in future */
184     _Static_assert(sizeof(result->ssid) == sizeof(record->ssid),
185                    "source and destination should be of same size");
186     memcpy(result->ssid, record->ssid, sizeof(record->ssid));
187     memcpy(result->bssid, record->bssid, sizeof(record->bssid));
188     result->channel = record->primary;
189     result->rssi = record->rssi;
190     result->auth = record->authmode;
191     return ESP_OK;
192 }
193 
get_wifi_scan_handlers(wifi_prov_scan_handlers_t * ptr)194 esp_err_t get_wifi_scan_handlers(wifi_prov_scan_handlers_t *ptr)
195 {
196     if (!ptr) {
197         return ESP_ERR_INVALID_ARG;
198     }
199     ptr->scan_start  = scan_start;
200     ptr->scan_status = scan_status;
201     ptr->scan_result = scan_result;
202     ptr->ctx = NULL;
203     return ESP_OK;
204 }
205