1 /*
2 * SPDX-FileCopyrightText: 2019-2021 Espressif Systems (Shanghai) CO LTD
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6 #include "sdkconfig.h"
7
8 #include <string.h>
9 #include <esp_log.h>
10 #include <esp_err.h>
11 #include <esp_wifi.h>
12
13 #include <mdns.h>
14 #include <protocomm.h>
15 #include <protocomm_httpd.h>
16
17 #include "wifi_provisioning/scheme_softap.h"
18 #include "wifi_provisioning_priv.h"
19
20 typedef struct softap_config {
21 protocomm_httpd_config_t httpd_config;
22 char ssid[33];
23 char password[65];
24 } wifi_prov_softap_config_t;
25
26 static const char *TAG = "wifi_prov_scheme_softap";
27
28 extern const wifi_prov_scheme_t wifi_prov_scheme_softap;
29 static void *scheme_softap_prov_httpd_handle;
start_wifi_ap(const char * ssid,const char * pass)30 static esp_err_t start_wifi_ap(const char *ssid, const char *pass)
31 {
32 /* Build Wi-Fi configuration for AP mode */
33 wifi_config_t wifi_config = {
34 .ap = {
35 .max_connection = 5,
36 },
37 };
38
39 /* SSID can be a non NULL terminated string if `ap.ssid_len` is specified
40 * Hence, memcpy is used to support 32 bytes long SSID per 802.11 standard */
41 const size_t ssid_len = strnlen(ssid, sizeof(wifi_config.ap.ssid));
42 memcpy(wifi_config.ap.ssid, ssid, ssid_len);
43 wifi_config.ap.ssid_len = ssid_len;
44
45 if (strlen(pass) == 0) {
46 memset(wifi_config.ap.password, 0, sizeof(wifi_config.ap.password));
47 wifi_config.ap.authmode = WIFI_AUTH_OPEN;
48 } else {
49 strlcpy((char *) wifi_config.ap.password, pass, sizeof(wifi_config.ap.password));
50 wifi_config.ap.authmode = WIFI_AUTH_WPA_WPA2_PSK;
51 }
52
53 /* Run Wi-Fi in AP + STA mode with configuration built above */
54 esp_err_t err = esp_wifi_set_mode(WIFI_MODE_APSTA);
55 if (err != ESP_OK) {
56 ESP_LOGE(TAG, "Failed to set Wi-Fi mode : %d", err);
57 return err;
58 }
59 err = esp_wifi_set_config(WIFI_IF_AP, &wifi_config);
60 if (err != ESP_OK) {
61 ESP_LOGE(TAG, "Failed to set Wi-Fi config : %d", err);
62 return err;
63 }
64
65 return ESP_OK;
66 }
67
prov_start(protocomm_t * pc,void * config)68 static esp_err_t prov_start(protocomm_t *pc, void *config)
69 {
70 if (!pc) {
71 ESP_LOGE(TAG, "Protocomm handle cannot be null");
72 return ESP_ERR_INVALID_ARG;
73 }
74
75 if (!config) {
76 ESP_LOGE(TAG, "Cannot start with null configuration");
77 return ESP_ERR_INVALID_ARG;
78 }
79
80 wifi_prov_softap_config_t *softap_config = (wifi_prov_softap_config_t *) config;
81
82 protocomm_httpd_config_t *httpd_config = &softap_config->httpd_config;
83
84 if (scheme_softap_prov_httpd_handle) {
85 httpd_config->ext_handle_provided = true;
86 httpd_config->data.handle = scheme_softap_prov_httpd_handle;
87 }
88
89 /* Start protocomm server on top of HTTP */
90 esp_err_t err = protocomm_httpd_start(pc, httpd_config);
91 if (err != ESP_OK) {
92 ESP_LOGE(TAG, "Failed to start protocomm HTTP server");
93 return err;
94 }
95
96 /* Start Wi-Fi softAP with specified ssid and password */
97 err = start_wifi_ap(softap_config->ssid, softap_config->password);
98 if (err != ESP_OK) {
99 ESP_LOGE(TAG, "Failed to start Wi-Fi AP");
100 protocomm_httpd_stop(pc);
101 return err;
102 }
103
104 /* Add mDNS service for allowing discovery of provisioning
105 * service on the SoftAP network (Optional). Even though
106 * this is an http service we identify it by _esp_wifi_prov so
107 * that application is free to use _http without conflict */
108 err = mdns_service_add("Wi-Fi Provisioning Service", "_esp_wifi_prov", "_tcp",
109 softap_config->httpd_config.data.config.port, NULL, 0);
110 if (err != ESP_OK) {
111 /* mDNS is not mandatory for provisioning to work,
112 * so print warning and return without failure */
113 ESP_LOGW(TAG, "Error adding mDNS service! Check if mDNS is running");
114 } else {
115 /* Information to identify the roles of the various
116 * protocomm endpoint URIs provided by the service */
117 err |= mdns_service_txt_item_set("_esp_wifi_prov", "_tcp", "version_endpoint", "/proto-ver");
118 err |= mdns_service_txt_item_set("_esp_wifi_prov", "_tcp", "session_endpoint", "/prov-session");
119 err |= mdns_service_txt_item_set("_esp_wifi_prov", "_tcp", "config_endpoint", "/prov-config");
120 if (err != ESP_OK) {
121 ESP_LOGE(TAG, "Error adding mDNS service text item");
122 }
123 }
124 return ESP_OK;
125 }
126
prov_stop(protocomm_t * pc)127 static esp_err_t prov_stop(protocomm_t *pc)
128 {
129 esp_err_t err = protocomm_httpd_stop(pc);
130 if (err != ESP_OK) {
131 ESP_LOGW(TAG, "Error occurred while stopping protocomm_httpd");
132 }
133
134 mdns_service_remove("_esp_wifi_prov", "_tcp");
135 return err;
136 }
137
new_config(void)138 static void *new_config(void)
139 {
140 wifi_prov_softap_config_t *softap_config = calloc(1, sizeof(wifi_prov_softap_config_t));
141 if (!softap_config) {
142 ESP_LOGE(TAG, "Error allocating memory for new configuration");
143 return NULL;
144 }
145 protocomm_httpd_config_t default_config = {
146 .data = {
147 .config = PROTOCOMM_HTTPD_DEFAULT_CONFIG()
148 }
149 };
150 softap_config->httpd_config = default_config;
151 return softap_config;
152 }
153
delete_config(void * config)154 static void delete_config(void *config)
155 {
156 if (!config) {
157 ESP_LOGE(TAG, "Cannot delete null configuration");
158 return;
159 }
160
161 wifi_prov_softap_config_t *softap_config = (wifi_prov_softap_config_t *) config;
162 free(softap_config);
163 }
164
set_config_service(void * config,const char * service_name,const char * service_key)165 static esp_err_t set_config_service(void *config, const char *service_name, const char *service_key)
166 {
167 if (!config) {
168 ESP_LOGE(TAG, "Cannot set null configuration");
169 return ESP_ERR_INVALID_ARG;
170 }
171
172 if (!service_name) {
173 ESP_LOGE(TAG, "Service name cannot be NULL");
174 return ESP_ERR_INVALID_ARG;
175 }
176
177 wifi_prov_softap_config_t *softap_config = (wifi_prov_softap_config_t *) config;
178 if (service_key) {
179 const int service_key_len = strlen(service_key);
180 if (service_key_len < 8 || service_key_len >= sizeof(softap_config->password)) {
181 ESP_LOGE(TAG, "Incorrect passphrase length for softAP: %d (Expected: Min - 8, Max - 64)", service_key_len);
182 return ESP_ERR_INVALID_ARG;
183 }
184 strlcpy(softap_config->password, service_key, sizeof(softap_config->password));
185 }
186 strlcpy(softap_config->ssid, service_name, sizeof(softap_config->ssid));
187 return ESP_OK;
188 }
189
set_config_endpoint(void * config,const char * endpoint_name,uint16_t uuid)190 static esp_err_t set_config_endpoint(void *config, const char *endpoint_name, uint16_t uuid)
191 {
192 return ESP_OK;
193 }
194
wifi_prov_scheme_softap_set_httpd_handle(void * handle)195 void wifi_prov_scheme_softap_set_httpd_handle(void *handle)
196 {
197 scheme_softap_prov_httpd_handle = handle;
198 }
199
200 const wifi_prov_scheme_t wifi_prov_scheme_softap = {
201 .prov_start = prov_start,
202 .prov_stop = prov_stop,
203 .new_config = new_config,
204 .delete_config = delete_config,
205 .set_config_service = set_config_service,
206 .set_config_endpoint = set_config_endpoint,
207 .wifi_mode = WIFI_MODE_APSTA
208 };
209