prettyprint

2023年12月21日 星期四

Raspberry Pi Pico W || LwIP || MbedTLS: EP.2 HTTPS server Session management

本文章介紹Raspberry Pi Pico W使用LwIP & MbedTLS,利用Cookie建立Session管理。

實驗建立需要登入驗證的網頁與無須登入驗證的網頁兩類別。當開啟需要驗證的網頁時,若尚未登入,則重新導到驗證頁面。



將需要驗證後才能使用的uri存在list中


軟體主要使用LwIP httpd application, 加入Cookies功能的patch後,利用cookie建立client與server的session管理。

logout 清除client 端session cookie,下次開啟需要驗證的網頁時,需再登入一次。

為了實現session management增加三個實作functions:

char *httpd_set_cookies(const void *connection, const char *uri);
執行上述patch後在httpd.c新增的function call,用於加入Set-Cookie: header。

void httpd_received_cookies(const void *connection, char **cookies);
執行上述patch後在httpd.c新增的function call,本專案修改cookies的parameter屬性,因為server在收到 client送來cookie中的session-id,若在server端已不存在(也許timeout), 將重設cookie為NULL。

void httpd_redirect_uri(char* uri, u16_t *uri_len);
若client的cookie為NULL,則server 
將重新導到登入頁面。

每個session設有timeout時間,當在設定的timeout時間內沒有任何存取網頁,則清除server上該session object。當每次存取網頁時就會重設timeout時間。


流程如下圖所示:
本專案測試https與http service同時存在:

HTTPS:


HTTP:


當cookie設定 Secure ,則只能以https方式傳送cookie。因此以http開啟網頁時,因為cookie設定為Secure,即使登入成功,browser也不會送出cookie,因此server將認定為未登入。

詳細程式碼附於文章後面。實驗影片請參閱成果影片。

成果影片





程式碼

  • picow_https_auth.c(main)
  • #include <stdio.h>
    #include "pico/stdlib.h"
    #include "pico/cyw43_arch.h"
    #include "hardware/adc.h"
    
    #include "lwip/altcp_tls.h"
    #include "lwip/altcp_tcp.h"
    #include "lwip/altcp.h"
    #include "lwip/apps/httpd.h"
    #include "lwip/apps/sntp.h"
    
    #include "picow_https_cert.h"
    #include "ff.h"
    #include "pico_storage.h"
    #include "mbedtls/ssl.h"
    
    #define LIGHT_PIN 16
    #define WIFI_SSID "your-ssid"
    #define WIFI_PASSWORD "your-password"
    
    struct altcp_tls_config {
      mbedtls_ssl_config conf;
      mbedtls_x509_crt *cert;
      mbedtls_pk_context *pkey;
      u8_t cert_count;
      u8_t cert_max;
      u8_t pkey_count;
      u8_t pkey_max;
      mbedtls_x509_crt *ca;
    #if defined(MBEDTLS_SSL_CACHE_C) && ALTCP_MBEDTLS_USE_SESSION_CACHE
      /** Inter-connection cache for fast connection startup */
      struct mbedtls_ssl_cache_context cache;
    #endif
    #if defined(MBEDTLS_SSL_SESSION_TICKETS) && ALTCP_MBEDTLS_USE_SESSION_TICKETS
      mbedtls_ssl_ticket_context ticket_ctx;
    #endif
    };
    
    struct altcp_tls_config *tls_config = NULL;
    
    void fs_ex_init(const char *httpd_root_dir);
    
    void netif_status_cb(struct netif *netif) {
        if (netif_is_link_up(netif)) {
            ip_addr_t ipaddr;
            ip_addr_t netmask;
            ip_addr_t gateway;
            ipaddr_aton("192.168.1.70", &ipaddr); 
            ipaddr_aton("255.255.255.0", &netmask);
            ipaddr_aton("192.168.1.1", &gateway);
        
            netif_set_addr(netif, &ipaddr, &netmask, &gateway);
        }   
    }
    bool sntp_time_geted=false;
    void sntp_set_system_time(uint32_t sec, uint32_t us)
    {
      struct tm info;
      time_t tim = sec;
      struct timeval tmv;
      tmv.tv_sec=sec;
      tmv.tv_usec=us;
      settimeofday(&tmv, 0);
      printf("set time :%s\n", ctime(&tim));
      sntp_time_geted=true;
    
    }
    
    void ssi_handler_init();
    void cgi_handler_init();
    
    void format_current_time(char *time_str, uint8_t count) {
        while(!sntp_time_geted) {
          cyw43_arch_poll();
          tight_loop_contents();
        };
        time_t timer=time(NULL);
        struct tm *tm_info  = localtime(&timer);
        strftime(time_str, count, "%a, %d-%b-%Y %X %Z", tm_info);
    }
    
    #include <malloc.h>
    
    uint32_t getTotalHeap(void) {
       extern char __StackLimit, __bss_end__; 
       return &__StackLimit  - &__bss_end__;
    }
    
    uint32_t getFreeHeap(void) {
       struct mallinfo m = mallinfo();
       return getTotalHeap() - m.uordblks;
    }
    
    DWORD get_sd_free_size() {
      FATFS *fs;
      DWORD fre_clust, fre_sect, tot_sect;
        /* Get volume information and free clusters of drive 1 */
        if (f_getfree(SDMMC_PATH, &fre_clust, &fs)!= FR_OK) return 0;
        /* Get total sectors and free sectors */
        tot_sect = (fs->n_fatent - 2) * fs->csize;
        fre_sect = fre_clust * fs->csize;
    
        /* Print the free space (assuming 512 bytes/sector) */
    //    printf("%10lu KiB total drive space.\n%10lu KiB available.\n", tot_sect / 2, fre_sect / 2);
        return fre_sect / 2;
    }
    
    float read_onboard_temperature() {
        /* 12-bit conversion, assume max value == ADC_VREF == 3.3 V */
        const float conversionFactor = 3.3f / (1 << 12);
    
        float adc = (float)adc_read() * conversionFactor;
        float tempC = 27.0f - (adc - 0.706f) / 0.001721f;
        return tempC;
    }
    
    extern void check_timeout_sessions_timer();
    
    void system_error_blink() {
      while(1) {
        cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, !cyw43_arch_gpio_get(CYW43_WL_GPIO_LED_PIN));
        sleep_ms(500);
      }
    }
    
    int main()
    {
        stdio_init_all();
        adc_init();
        adc_set_temp_sensor_enabled(true);
        adc_select_input(4);
    
        if (cyw43_arch_init()) {
            printf("cyw43 arch init error\n");
            system_error_blink();
        }
        gpio_init(LIGHT_PIN);
        gpio_set_dir(LIGHT_PIN, true);
        gpio_put(LIGHT_PIN, 0);
     
        //1. connect to WiFi network and set static IP
        cyw43_arch_enable_sta_mode();
        netif_set_status_callback(netif_default, netif_status_cb);
        int wifi_stat = cyw43_arch_wifi_connect_timeout_ms(WIFI_SSID, WIFI_PASSWORD, CYW43_AUTH_WPA2_AES_PSK, 30000);
        if (wifi_stat) {
            printf("wifi connect error:%d\n", wifi_stat);
            system_error_blink();
        }
        printf("Wifi connected\n");
        printf("get ip addr:%s\n",ipaddr_ntoa(&(cyw43_state.netif[0].ip_addr)));
        
        // 2. set SNTP
        sntp_setoperatingmode(SNTP_OPMODE_POLL);
        sntp_init();
    
        // 3. set TLS connection
        tls_config = altcp_tls_create_config_server_privkey_cert(SERVER_KEY, sizeof(SERVER_KEY), NULL, 0, SERVER_CERT, sizeof(SERVER_CERT));
        mbedtls_x509_crt_parse(tls_config->cert, CA_CERT, sizeof(CA_CERT));
        // for large file size
        mbedtls_ssl_conf_max_frag_len( &tls_config->conf, MBEDTLS_SSL_MAX_FRAG_LEN_4096 );
        //4. mount FS in SD card and start https server
        FATFS fs;
        FIL fil;
        FRESULT res;
        if (f_mount(&fs, SDMMC_PATH, 1) != FR_OK) {
          printf("mount error\n");
          system_error_blink();
        }
    
        int i;
        
        ssi_handler_init();
        cgi_handler_init();
        check_timeout_sessions_timer();
        fs_ex_init(SDMMC_PATH"/");
    
        httpd_inits(tls_config);
        //httpd_init();
    
        char buf[61];
        format_current_time(buf, 60);
        printf(buf);
    
      cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN,1);
        while(1) {
          cyw43_arch_poll();
          sleep_ms(50);
    
        }
        
        return 0;
    }
    
      
  • picow_https_ssi.c

  • #include <stdio.h>
    #include "pico/stdlib.h"
    #include "pico/cyw43_arch.h"
    #include "lwip/altcp_tls.h"
    #include "lwip/altcp_tcp.h"
    #include "lwip/altcp.h"
    #include "lwip/apps/httpd.h"
    #include "lwip/apps/sntp.h"
    #include "ff.h"
    
    #define DOMAIN "httpsserver"
    #define SESSION_TIMEOUT_SEC (10*60)
    #define AUTH_USER   "picow"
    #define AUTH_PASS   "passw0rd"
    #define LIGHT_PIN 16
    
    char* session_check_uri[]={
        "/light.shtml", 
        "/demo1.shtml",
        "/demo2.shtml",
        "/light.cgi"
      };
    
    typedef struct _sess_id_t{
      uint8_t sess_id[26];
      absolute_time_t timeout;
      struct _sess_id_t *next;
    } sess_id_t;
    
    typedef struct _conn_t {
      void *connection;
      time_t start_time;
      enum {
        NORMAL_PAGE=0,
        LOGIN_PAGE=1,
        LOGIN_SUCCESSFUL=2,
        LOGOUT_PAGE=3,
      } verified;
      struct _conn_t *next;
    } conn_t;
    
    char light_img[20];
    sess_id_t *sess_header=NULL;
    conn_t * conn_header=NULL;
    
    void gen_sess_cookie(char *sess_id, char* sess_cookie, uint8_t count) {
      char time_str[40];
      uint8_t sid[26];
    
      time_t timer=time(NULL);
      struct tm *tm_info = localtime(&timer);
    
      srand((unsigned)time(NULL));
      sprintf(sid, "sess_%04d%04d%04d",rand()%10000, rand()%10000, rand()%10000);
      printf("sess_id:%s\n", sid);
      sprintf(sess_id, sid);
      timer+=86400;  // 1 day
      tm_info = localtime(&timer);
     
      strftime(time_str, 40, "%a, %d-%b-%Y %X %Z", tm_info);
      sprintf(sess_cookie, "sess_id=%s; expires=%s; domain=%s; path=/;  Secure", sid, time_str, DOMAIN);
      //sprintf(sess_cookie, "sess_id=%s; expires=%s; domain=%s; path=/; SameSite=Lax", sid, time_str, DOMAIN);
    }
    
    void new_session_id(char *sid) {
      sess_id_t *newid = (sess_id_t*)calloc(1,sizeof(sess_id_t));
      newid->next = NULL;
      newid->timeout=make_timeout_time_ms(SESSION_TIMEOUT_SEC*1000); // 10 min
      strcpy(newid->sess_id, sid);
    
      sess_id_t *tidp;
    
      if (sess_header == NULL) {
          sess_header = newid;
          
          return;
      } else {
        tidp = sess_header;
        while (tidp->next != NULL) {
          tidp = tidp->next;
        }
      }
      
      tidp->next=newid;
    
      return;
    }
    
    extern DWORD get_sd_free_size();
    extern float read_onboard_temperature();
    extern uint32_t getFreeHeap(void);
    
    
    /* ==== cgi begin ======*/
    const char *
    cgi_light_handler(int iIndex, int iNumParams, char *pcParam[], char *pcValue[]) {
        if (iIndex == 0 && iNumParams ==1) {
            if (strcmp(pcParam[0],"action") == 0) {
                strcpy(light_img,"");
                if (strcmp(pcValue[0], "ON")==0) {
                  gpio_put(LIGHT_PIN, 1);
                  strcpy(light_img, "/img/light_on.png");
                } 
                if (strcmp(pcValue[0],"OFF")==0) {
                  gpio_put(LIGHT_PIN, 0);
                  strcpy(light_img, "/img/light_off.png");
                } 
                
                return "/light.shtml";
            }
            
        }
        return "/404.html";  
    }
    
    static const tCGI cgi_handlers[] = {
        { 
            "/light.cgi", cgi_light_handler
        },
        
    };
    void cgi_handler_init() {
      http_set_cgi_handlers(cgi_handlers, LWIP_ARRAYSIZE(cgi_handlers));
    }
    /* ==== cgi end ======*/
    
    /*  === ssi begin =====*/
    const char*  ssi_tags[] = {
        "temp",
        "freeram",
        "freesd",
        "logout",
        "light",
    };
    
    u16_t __time_critical_func(ssi_handler)(int iIndex, char *pcInsert, int iInsertLen, u16_t current_tag_part, u16_t *next_tag_part)
    {
        size_t printed;
        static uint32_t counter=0;
        switch (iIndex) { 
            case 0:     //temp, Temperature
                printed = snprintf(pcInsert, iInsertLen, "%.02f°C", read_onboard_temperature());
            
            break;
            case 1:    // free RAM
                printed = sniprintf(pcInsert, iInsertLen, "%d B", getFreeHeap());
                break;
            case 2:     // SD Free Size
                printed = sniprintf(pcInsert, iInsertLen, "%ld KB", get_sd_free_size());
            break;
            case 3:  // logout uri
            printed = snprintf(pcInsert, iInsertLen,
             "<button onClick='javascript:logout();'>Logout</button>");
             break;
            case 4:  // light
            printed = snprintf(pcInsert, iInsertLen,light_img);
            break;
    
            default: 
              printed = snprintf(pcInsert, iInsertLen, "");
        }
        return printed;
    }
    void ssi_handler_init() {
        http_set_ssi_handler(ssi_handler,
    #if LWIP_HTTPD_SSI_RAW
        NULL, 0
    #else
        ssi_tags, LWIP_ARRAYSIZE(ssi_tags)
    #endif
        );
        if (gpio_get(LIGHT_PIN))
           strcpy(light_img, "/img/light_on.png");
        else 
          strcpy(light_img, "/img/light_off.png");
    
    }
    /*  === ssi end =====*/
    
    /*===== post begin =====*/
    #define USER_PASS_BUFSIZE 16
    
    err_t
    httpd_post_begin(void *connection, const char *uri, const char *http_request,
                     u16_t http_request_len, int content_len, char *response_uri,
                     u16_t response_uri_len, u8_t *post_auto_wnd)
    {
      LWIP_UNUSED_ARG(connection);
      LWIP_UNUSED_ARG(http_request);
      LWIP_UNUSED_ARG(http_request_len);
      LWIP_UNUSED_ARG(content_len);
      LWIP_UNUSED_ARG(post_auto_wnd);
      conn_t *conn = (conn_t*)calloc(1, sizeof(conn_t));
      conn->connection = connection;
      conn->start_time = time(NULL);
      conn->verified = NORMAL_PAGE;
      conn->next=NULL;
      if (conn_header == NULL) {
        conn_header = conn;
      } else {
        conn->next=conn_header;
        conn_header = conn;
      }
    
      if (!memcmp(uri, "/login.cgi", 11)) {
          conn->verified = LOGIN_PAGE;
          snprintf(response_uri, response_uri_len, "/loginfail.html");
          *post_auto_wnd = 1;
          return ERR_OK;
      }
    
      return ERR_VAL;
    }
    
    err_t
    httpd_post_receive_data(void *connection, struct pbuf *p)
    {
      err_t ret;
    
      LWIP_ASSERT("NULL pbuf", p != NULL);
      ret = ERR_VAL;
      
      conn_t *start = conn_header;
      while(start != NULL) { 
        if (start->connection == connection) {
          if (start->verified == LOGIN_PAGE) {
          u16_t token_user = pbuf_memfind(p, "user=", 5, 0);
          u16_t token_pass = pbuf_memfind(p, "pass=", 5, 0);
          if ((token_user != 0xFFFF) && (token_pass != 0xFFFF)) {
            u16_t value_user = token_user + 5;
            u16_t value_pass = token_pass + 5;
            u16_t len_user = 0;
            u16_t len_pass = 0;
            u16_t tmp;
            /* find user len */
            tmp = pbuf_memfind(p, "&", 1, value_user);
            if (tmp != 0xFFFF) {
              len_user = tmp - value_user;
            } else {
              len_user = p->tot_len - value_user;
            }
            /* find pass len */
            tmp = pbuf_memfind(p, "&", 1, value_pass);
            if (tmp != 0xFFFF) {
              len_pass = tmp - value_pass;
            } else {
              len_pass = p->tot_len - value_pass;
            }
            if ((len_user > 0) && (len_user < USER_PASS_BUFSIZE) &&
                (len_pass > 0) && (len_pass < USER_PASS_BUFSIZE)) {
              /* provide contiguous storage if p is a chained pbuf */
              char buf_user[USER_PASS_BUFSIZE];
              char buf_pass[USER_PASS_BUFSIZE];
              char *user = (char *)pbuf_get_contiguous(p, buf_user, sizeof(buf_user), len_user, value_user);
              char *pass = (char *)pbuf_get_contiguous(p, buf_pass, sizeof(buf_pass), len_pass, value_pass);
              if (user && pass) {
                user[len_user] = 0;
                pass[len_pass] = 0;
                if (!strcmp(user, AUTH_USER) && !strcmp(pass, AUTH_PASS)) {
                  /* user and password are correct, create a "session" */
                  start->verified=LOGIN_SUCCESSFUL;
                }
              }
            }
          }
          /* not returning ERR_OK aborts the connection, so return ERR_OK unless the
            connection is unknown */
          ret = ERR_OK;
          break;
        }
      }
        start = start->next;
      }
    
      /* this function must ALWAYS free the pbuf it is passed or it will leak memory */
      pbuf_free(p);
    
      return ret;
    }
    
    void
    httpd_post_finished(void *connection, char *response_uri, u16_t response_uri_len)
    {
      /* default page is "login failed" */
      snprintf(response_uri, response_uri_len, "/loginfail.html");
      conn_t *start = conn_header;
      conn_t *pre_conn = conn_header;
      while (start != NULL) {
        if (start->connection == connection) {
            if (start->verified == LOGIN_SUCCESSFUL) {
              /* login succeeded */
              snprintf(response_uri, response_uri_len, "/session.html");
              break;
            }
            if (start == conn_header) { 
                conn_header = start->next;
            }
            else {
                pre_conn->next = start->next;
            }
    
            free(start);
            break;
    
        }
        pre_conn = start;
        start=start->next;
        break;
      }
    }
    /*===== post end =====*/
    
    /* ==== Session manager ===*/
    #define SET_COOKIE_SIZE 120
    char setCookie[SET_COOKIE_SIZE];
    
    char *httpd_set_cookies(const void *connection, const char *uri) {
      LWIP_UNUSED_ARG(uri);
      char cookie[SET_COOKIE_SIZE];
    
      conn_t *start = conn_header;
      conn_t *pre_conn = start;
    
      while(start!=NULL) {
        if (start->connection == connection && start->verified == LOGIN_SUCCESSFUL) {
          char sessid[26];
          gen_sess_cookie(sessid, cookie, SET_COOKIE_SIZE);
          new_session_id(sessid);
          snprintf(setCookie, SET_COOKIE_SIZE, HDR_HTTP_RES_SET_COOKIE "%s\r\n", cookie);
    
          if (start == conn_header) { 
                conn_header = start->next;
            }
            else {
                pre_conn->next = start->next;
            }
          free(start);
          return setCookie;
        }
        pre_conn = start;
        start=start->next;
      }
      return NULL;
    }
    
    void httpd_received_cookies(const void *connection, char **cookies) {
      char *session = strstr(*cookies, "sess_id=");
      if (session) session+=8;
      sess_id_t* start = sess_header;
      while (start!= NULL) {
        if (strcmp(start->sess_id, session) == 0) {
          // match!  reset timeout
          start->timeout = make_timeout_time_ms(SESSION_TIMEOUT_SEC*1000);
          return;
          break;
        } else {
          start = start->next;
        }
      }
      // not an avail connection cookie, reset cookie to NULL(invalid cookie)
      *cookies=NULL;
      
    }
    
    void httpd_redirect_uri(char* uri, u16_t *uri_len) {
      char new_uri[(*uri_len)+1];
      strncpy(new_uri, uri, *uri_len);
      new_uri[*uri_len]=0;
    
      for (int idx=0; idx < LWIP_ARRAYSIZE(session_check_uri); idx++) {
        if(strstr(new_uri, session_check_uri[idx])){
            // redirect to authenticate web page
            strcpy(uri, "/login.html");
            *uri_len = strlen("/login.html");
            break;
        }
        
      }
      return ;
    }
    
    repeating_timer_t timer;
    
    bool check_timeout_sessions_isr(repeating_timer_t *t) {
      sess_id_t *start = sess_header;
      sess_id_t *pre_sess = start;
      while (start != NULL) {
        if (absolute_time_diff_us(get_absolute_time(), start->timeout) < 0) {
          if (start == sess_header) {
            sess_header = NULL;
            free(start);
            break;
          } else {
            pre_sess->next = start->next;
            free(start);
            start=pre_sess->next;
          }
          } else {
            pre_sess = start;
            start = start->next;
          }
      }
      return true;
    }
    
    void check_timeout_sessions_timer() {
        add_repeating_timer_ms(1000*60, check_timeout_sessions_isr, NULL, &timer);
    }
    
    /* ==== Session manager end ===*/
      
  • picow_https_fs.c

  •  /**
     * @file
     * HTTPD custom file system example
     *
     * This file demonstrates how to add support for an external file system to httpd.
     * It provides access to the specified root directory and uses stdio.h file functions
     * to read files.
     *
     * ATTENTION: This implementation is *not* secure: no checks are added to ensure
     * files are only read below the specified root directory!
     */
     
     /*
     * Copyright (c) 2017 Simon Goldschmidt
     * All rights reserved.
     * 
     * Redistribution and use in source and binary forms, with or without modification, 
     * are permitted provided that the following conditions are met:
     *
     * 1. Redistributions of source code must retain the above copyright notice,
     *    this list of conditions and the following disclaimer.
     * 2. Redistributions in binary form must reproduce the above copyright notice,
     *    this list of conditions and the following disclaimer in the documentation
     *    and/or other materials provided with the distribution.
     * 3. The name of the author may not be used to endorse or promote products
     *    derived from this software without specific prior written permission. 
     *
     * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 
     * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
     * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
     * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
     * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
     * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
     * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
     * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
     * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
     * OF SUCH DAMAGE.
     *
     * This file is part of the lwIP TCP/IP stack.
     * 
     * Author: Simon Goldschmidt <goldsimon@gmx.de>
     *
     */
    
    #include "lwip/opt.h"
    
    #include "lwip/apps/fs.h"
    #include "lwip/def.h"
    #include "lwip/mem.h"
    
    #include "stdio.h"
    #include "string.h"
    #include "ff.h"
    #include "pico_storage.h"
    
    /////////////////////////////////////////////////////////
    
    #if !LWIP_HTTPD_CUSTOM_FILES
    #error This needs LWIP_HTTPD_CUSTOM_FILES
    #endif
    //#if !LWIP_HTTPD_DYNAMIC_HEADERS
    //#error This needs LWIP_HTTPD_DYNAMIC_HEADERS
    //#endif
    //#if !LWIP_HTTPD_DYNAMIC_FILE_READ
    //#error This wants to demonstrate read-after-open, so LWIP_HTTPD_DYNAMIC_FILE_READ is required!
    //#endif
    //#if !LWIP_HTTPD_FS_ASYNC_READ
    //#error This needs LWIP_HTTPD_FS_ASYNC_READ
    //#endif
    #if !LWIP_HTTPD_FILE_EXTENSION
    #error This needs LWIP_HTTPD_FILE_EXTENSION
    #endif
    
    struct fs_custom_data {
      //FILE *f;
      FIL *f;
    
    };
    
    const char* fs_ex_root_dir;
    
    void
    fs_ex_init(const char *httpd_root_dir)
    {
      fs_ex_root_dir = strdup(httpd_root_dir);
    }
    
    #if LWIP_HTTPD_CUSTOM_FILES
    int
    fs_open_custom(struct fs_file *file, const char *name)
    {
      char full_filename[256];
      //FILE *f;
      FIL f;
      FRESULT res;
      snprintf(full_filename, 255, "%s%s", fs_ex_root_dir, name);
      full_filename[255] = 0;
    
      //f = fopen(full_filename, "rb");
      res =f_open(&f, full_filename, FA_READ);
      //if (f != NULL) {
      if (res == FR_OK) {
        //if (!fseek(f, 0, SEEK_END)) {
        if (f_lseek(&f, 0) == FR_OK) { 
          //int len = (int)ftell(f);
          int len = (int)f_size(&f);
          //if(!fseek(f, 0, SEEK_SET)) {
            struct fs_custom_data *data = (struct fs_custom_data *)mem_malloc(sizeof(struct fs_custom_data));
            LWIP_ASSERT("out of memory?", data != NULL);
            memset(file, 0, sizeof(struct fs_file));
    
            file->len = len;
    
            file->flags = FS_FILE_FLAGS_HEADER_PERSISTENT;
            
            data->f = &f;
            file->pextension = data;
            return 1;
          //}
        } else {
          printf("open file error:%s\n", name);
        }
        //fclose(f);
        f_close(&f);
      }
      return 0;
    }
    
    void
    fs_close_custom(struct fs_file *file)
    {
      //FRESULT res;
      if (file && file->pextension) {
        struct fs_custom_data *data = (struct fs_custom_data *)file->pextension;
        if (data->f != NULL) {
          //fclose(data->f);
          f_close(data->f);
          data->f = NULL;
        }
        mem_free(data);
      }
    }
    
    #if LWIP_HTTPD_FS_ASYNC_READ
    u8_t
    fs_canread_custom(struct fs_file *file)
    {
      /* This function is only necessary for asynchronous I/O:
         If reading would block, return 0 and implement fs_wait_read_custom() to call the
         supplied callback if reading works. */
    
      LWIP_UNUSED_ARG(file);
      return 1;
    }
    
    
    
    u8_t
    fs_wait_read_custom(struct fs_file *file, fs_wait_cb callback_fn, void *callback_arg)
    {
    
      //LWIP_ASSERT("not implemented in this example configuration", 0);
    
      LWIP_UNUSED_ARG(file);
      LWIP_UNUSED_ARG(callback_fn);
      LWIP_UNUSED_ARG(callback_arg);
      /* Return
         - 0 if ready to read (at least one byte)
         - 1 if reading should be delayed (call 'tcpip_callback(callback_fn, callback_arg)' when ready) */
      return 1;
    }
    
    int
    fs_read_async_custom(struct fs_file *file, char *buffer, int count, fs_wait_cb callback_fn, void *callback_arg)
    {
      struct fs_custom_data *data = (struct fs_custom_data *)file->pextension;
      //FILE *f;
      FIL *f;
      int len;
      int read_count = count;
      LWIP_ASSERT("data not set", data != NULL);
    
      f = data->f;
      //len = (int)fread(buffer, 1, read_count, f);
    
      f_read(f, buffer,read_count, &len);
    
    
      LWIP_UNUSED_ARG(callback_fn);
      LWIP_UNUSED_ARG(callback_arg);
    
      file->index += len;
    
      /* Return
         - FS_READ_EOF if all bytes have been read
         - FS_READ_DELAYED if reading is delayed (call 'tcpip_callback(callback_fn, callback_arg)' when done) */
      if (len == 0) {
        /* all bytes read already */
        return FS_READ_EOF;
      }
      return len;
    }
    
    #else /* LWIP_HTTPD_FS_ASYNC_READ */
    int
    fs_read_custom(struct fs_file *file, char *buffer, int count)
    {
      struct fs_custom_data *data = (struct fs_custom_data *)file->pextension;
      //FILE *f;
      FIL *f;
      int len;
      int read_count = count;
      LWIP_ASSERT("data not set", data != NULL);
    
    
    
      f = data->f;
      //len = (int)fread(buffer, 1, read_count, f);
     
      f_read(f, buffer, read_count, &len);
    
      file->index += len;
    
      /* Return FS_READ_EOF if all bytes have been read */
      return len;
    }
    
    #endif /* LWIP_HTTPD_FS_ASYNC_READ */
    #endif /* LWIP_HTTPD_CUSTOM_FILES */
    
    
      
  • CMakeLists.txt

  • # Generated Cmake Pico project file
    
    cmake_minimum_required(VERSION 3.13)
    
    set(CMAKE_C_STANDARD 11)
    set(CMAKE_CXX_STANDARD 17)
    
    # Initialise pico_sdk from installed location
    # (note this can come from environment, CMake cache etc)
    set(PICO_SDK_PATH "/home/duser/pico/pico-sdk")
    
    set(PICO_BOARD pico_w CACHE STRING "Board type")
    
    # Pull in Raspberry Pi Pico SDK (must be before project)
    include(pico_sdk_import.cmake)
    
    if (PICO_SDK_VERSION_STRING VERSION_LESS "1.4.0")
      message(FATAL_ERROR "Raspberry Pi Pico SDK version 1.4.0 (or later) required. Your version is ${PICO_SDK_VERSION_STRING}")
    endif()
    
    project(picow_https_auth C CXX ASM)
    
    # Initialise the Raspberry Pi Pico SDK
    pico_sdk_init()
    
    # Add executable. Default name is the project name, version 0.1
    
    add_executable(picow_https_auth 
        picow_https_auth.c 
        picow_https_ssi.c
        picow_httpd_fs.c
        )
    
    pico_set_program_name(picow_https_auth "picow_https_auth")
    pico_set_program_version(picow_https_auth "0.1")
    
    pico_enable_stdio_uart(picow_https_auth 1)
    pico_enable_stdio_usb(picow_https_auth 0)
    
    # Add the standard library to the build
    target_link_libraries(picow_https_auth
            pico_stdlib
            hardware_adc)
    
    # Add the standard include files to the build
    target_include_directories(picow_https_auth PRIVATE
      ${CMAKE_CURRENT_LIST_DIR}
      ${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts or any other standard includes, if required
    )
    
    # Add any user requested libraries
    target_link_libraries(picow_https_auth 
            pico_cyw43_arch_lwip_poll
            pico_lwip_sntp
            pico_lwip_http
            pico_mbedtls
            pico_lwip_mbedtls
            )
    
    add_subdirectory(pico_storage_drv)
    target_link_libraries(picow_https_auth
            pico_storage_drv
            )
    
    pico_add_extra_outputs(picow_https_auth)
    
    # Ignore warnings from lwip code
    set_source_files_properties(
            ${PICO_LWIP_PATH}/src/apps/altcp_tls/altcp_tls_mbedtls.c
            PROPERTIES
            COMPILE_OPTIONS "-Wno-unused-result"
            )
    add_definitions(
      -DSNTP_SERVER_DNS=1
      -DSNTP_SERVER_ADDRESS="pool.ntp.org"
      -DSNTP_SET_SYSTEM_TIME=sntp_set_system_time
      -DSNTP_STARTUP_DELAY=0
      )
      
      
  • lwipopts.h

  • #ifndef __LWIPOPTS_H__
    #define __LWIPOPTS_H__
    
    // Common settings used in most of the pico_w examples
    // (see https://www.nongnu.org/lwip/2_1_x/group__lwip__opts.html for details)
    
    // allow override in some examples
    #ifndef NO_SYS
    #define NO_SYS                      1
    #endif
    // allow override in some examples
    #ifndef LWIP_SOCKET
    #define LWIP_SOCKET                 0
    #endif
    #if PICO_CYW43_ARCH_POLL
    #define MEM_LIBC_MALLOC             1
    #else
    // MEM_LIBC_MALLOC is incompatible with non polling versions
    #define MEM_LIBC_MALLOC             0
    #endif
    #define MEM_ALIGNMENT               4
    #define MEM_SIZE                    4000
    #define MEMP_NUM_TCP_SEG            32
    #define MEMP_NUM_ARP_QUEUE          10
    #define PBUF_POOL_SIZE              24
    #define LWIP_ARP                    1
    #define LWIP_ETHERNET               1
    #define LWIP_ICMP                   1
    #define LWIP_RAW                    1
    #define TCP_WND                     (8 * TCP_MSS)
    #define TCP_MSS                     1460
    #define TCP_SND_BUF                 (8 * TCP_MSS)
    #define TCP_SND_QUEUELEN            ((4 * (TCP_SND_BUF) + (TCP_MSS - 1)) / (TCP_MSS))
    #define LWIP_NETIF_STATUS_CALLBACK  1
    #define LWIP_NETIF_LINK_CALLBACK    1
    #define LWIP_NETIF_HOSTNAME         1
    #define LWIP_NETCONN                0
    #define MEM_STATS                   0
    #define SYS_STATS                   0
    #define MEMP_STATS                  0
    #define LINK_STATS                  0
    // #define ETH_PAD_SIZE                2
    #define LWIP_CHKSUM_ALGORITHM       3
    #define LWIP_DHCP                   1
    #define LWIP_IPV4                   1
    #define LWIP_TCP                    1
    #define LWIP_UDP                    1
    #define LWIP_DNS                    1
    #define LWIP_TCP_KEEPALIVE          1
    #define LWIP_NETIF_TX_SINGLE_PBUF   1
    #define DHCP_DOES_ARP_CHECK         0
    #define LWIP_DHCP_DOES_ACD_CHECK    0
    
    #ifndef NDEBUG
    #define LWIP_DEBUG                  1
    #define LWIP_STATS                  1
    #define LWIP_STATS_DISPLAY          1
    #endif
    
    #define ETHARP_DEBUG                LWIP_DBG_OFF
    #define NETIF_DEBUG                 LWIP_DBG_OFF
    #define PBUF_DEBUG                  LWIP_DBG_OFF
    #define API_LIB_DEBUG               LWIP_DBG_OFF
    #define API_MSG_DEBUG               LWIP_DBG_OFF
    #define SOCKETS_DEBUG               LWIP_DBG_OFF
    #define ICMP_DEBUG                  LWIP_DBG_OFF
    #define INET_DEBUG                  LWIP_DBG_OFF
    #define IP_DEBUG                    LWIP_DBG_OFF
    #define IP_REASS_DEBUG              LWIP_DBG_OFF
    #define RAW_DEBUG                   LWIP_DBG_OFF
    #define MEM_DEBUG                   LWIP_DBG_OFF
    #define MEMP_DEBUG                  LWIP_DBG_OFF
    #define SYS_DEBUG                   LWIP_DBG_OFF
    #define TCP_DEBUG                   LWIP_DBG_OFF
    #define TCP_INPUT_DEBUG             LWIP_DBG_OFF
    #define TCP_OUTPUT_DEBUG            LWIP_DBG_OFF
    #define TCP_RTO_DEBUG               LWIP_DBG_OFF
    #define TCP_CWND_DEBUG              LWIP_DBG_OFF
    #define TCP_WND_DEBUG               LWIP_DBG_OFF
    #define TCP_FR_DEBUG                LWIP_DBG_OFF
    #define TCP_QLEN_DEBUG              LWIP_DBG_OFF
    #define TCP_RST_DEBUG               LWIP_DBG_OFF
    #define UDP_DEBUG                   LWIP_DBG_OFF
    #define TCPIP_DEBUG                 LWIP_DBG_OFF
    #define PPP_DEBUG                   LWIP_DBG_OFF
    #define SLIP_DEBUG                  LWIP_DBG_OFF
    #define DHCP_DEBUG                  LWIP_DBG_OFF
    
    /*  in Pico-SDK above 1.5.0 */
    #define MEMP_NUM_SYS_TIMEOUT            (LWIP_NUM_SYS_TIMEOUT_INTERNAL+1)
    /* TCP WND must be at least 16 kb to match TLS record size
       or you will get a warning "altcp_tls: TCP_WND is smaller than the RX decrypion buffer, connection RX might stall!" */
    // tls
    #undef TCP_WND
    #define TCP_WND  16384
    
    #define LWIP_ALTCP               1
    #define LWIP_ALTCP_TLS           1
    #define LWIP_ALTCP_TLS_MBEDTLS   1
    
    //#define LWIP_DEBUG               1
    #define ALTCP_MBEDTLS_DEBUG  LWIP_DBG_ON
    
    /* set authmode */
    #define ALTCP_MBEDTLS_AUTHMODE MBEDTLS_SSL_VERIFY_NONE
    //#define ALTCP_MBEDTLS_AUTHMODE MBEDTLS_SSL_VERIFY_REQUIRED
    
    // httpd
    #define HTTPD_ENABLE_HTTPS          1   // enable https
    
    #define LWIP_HTTPD 1
    #define LWIP_HTTPD_SSI 1
    #define LWIP_HTTPD_CGI 1
    #define LWIP_HTTPD_SSI_MULTIPART 1
    #define LWIP_HTTPD_SUPPORT_POST 1
    #define LWIP_HTTPD_SSI_INCLUDE_TAG 0
    // extern wbe page files
    #define LWIP_HTTPD_CUSTOM_FILES 1
    #define LWIP_HTTPD_DYNAMIC_HEADERS 1
    #define LWIP_HTTPD_FS_ASYNC_READ 1
    #define LWIP_HTTPD_DYNAMIC_FILE_READ 1
    #define LWIP_HTTPD_FILE_EXTENSION 1
    
    #define LWIP_HTTPD_SUPPORT_COOKIES 1
    
    #endif /* __LWIPOPTS_H__ */
    
      
  • mbedtls_config.h

  •   /* Workaround for some mbedtls source files using INT_MAX without including limits.h */
    #include <limits.h>
    
    #define MBEDTLS_NO_PLATFORM_ENTROPY
    #define MBEDTLS_ENTROPY_HARDWARE_ALT
    
    #define MBEDTLS_SSL_OUT_CONTENT_LEN    2048
    
    #define MBEDTLS_ALLOW_PRIVATE_ACCESS
    #define MBEDTLS_HAVE_TIME
    #define MBEDTLS_HAVE_TIME_DATE
    
    #define MBEDTLS_CIPHER_MODE_CBC
    #define MBEDTLS_ECP_DP_SECP192R1_ENABLED
    #define MBEDTLS_ECP_DP_SECP224R1_ENABLED
    #define MBEDTLS_ECP_DP_SECP256R1_ENABLED
    #define MBEDTLS_ECP_DP_SECP384R1_ENABLED
    #define MBEDTLS_ECP_DP_SECP521R1_ENABLED
    #define MBEDTLS_ECP_DP_SECP192K1_ENABLED
    #define MBEDTLS_ECP_DP_SECP224K1_ENABLED
    #define MBEDTLS_ECP_DP_SECP256K1_ENABLED
    #define MBEDTLS_ECP_DP_BP256R1_ENABLED
    #define MBEDTLS_ECP_DP_BP384R1_ENABLED
    #define MBEDTLS_ECP_DP_BP512R1_ENABLED
    #define MBEDTLS_ECP_DP_CURVE25519_ENABLED
    #define MBEDTLS_KEY_EXCHANGE_RSA_ENABLED
    #define MBEDTLS_PKCS1_V15
    #define MBEDTLS_SHA256_SMALLER
    #define MBEDTLS_SSL_SERVER_NAME_INDICATION
    #define MBEDTLS_AES_C
    #define MBEDTLS_ASN1_PARSE_C
    #define MBEDTLS_BIGNUM_C
    #define MBEDTLS_CIPHER_C
    #define MBEDTLS_CTR_DRBG_C
    #define MBEDTLS_ENTROPY_C
    #define MBEDTLS_ERROR_C
    #define MBEDTLS_MD_C
    #define MBEDTLS_MD5_C
    #define MBEDTLS_OID_C
    #define MBEDTLS_PKCS5_C
    #define MBEDTLS_PK_C
    #define MBEDTLS_PK_PARSE_C
    #define MBEDTLS_PLATFORM_C
    #define MBEDTLS_RSA_C
    #define MBEDTLS_SHA1_C
    #define MBEDTLS_SHA224_C
    #define MBEDTLS_SHA256_C
    #define MBEDTLS_SHA512_C
    #define MBEDTLS_SSL_CLI_C
    #define MBEDTLS_SSL_SRV_C
    #define MBEDTLS_SSL_TLS_C
    #define MBEDTLS_X509_CRT_PARSE_C
    #define MBEDTLS_X509_USE_C
    #define MBEDTLS_AES_FEWER_TABLES
    
    /* TLS 1.2 */
    #define MBEDTLS_SSL_PROTO_TLS1_2
    #define MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
    #define MBEDTLS_GCM_C
    #define MBEDTLS_ECDH_C
    #define MBEDTLS_ECP_C
    #define MBEDTLS_ECDSA_C
    #define MBEDTLS_ASN1_WRITE_C
    
    // The following is needed to parse a certificate
    #define MBEDTLS_PEM_PARSE_C
    #define MBEDTLS_BASE64_C
    
    // for large file size
    #define MBEDTLS_SSL_MAX_FRAGMENT_LENGTH 
    
      

  • index.shtml

  • <html lang="en">
    <head><title>Raspberry Pi Pico W - HTTPS Session</title></head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8">
    <Script>
    function logout() {
        const cookies = document.cookie.split(";");
    
        for (let i = 0; i < cookies.length; i++) {
            const cookie = cookies[i];
            const eqPos = cookie.indexOf("=");
            const name = eqPos > -1 ? cookie.substr(0, eqPos) : cookie;
            if (name=="sess_id"){
                document.cookie = name + "=;expires=Thu, 01 Jan 1970 00:00:00 GMT";
            }
        }
    }
    </Script>
    <body bgcolor="white" text="black">
    <h1><strong>Raspberry Pi Pico W - </strong>HTTPS Session</h1>
    <table style="border-collapse: collapse; width: 1000; height: 127px;" border="1">
    <tbody>
    <tr style="height: 18px;">
    <td style="width: 36.5145%; height: 18px;">
    <p><a title="System Info" href="index.shtml">Sysem Info</a></p>
    <p><button onClick="location.href='login.html'">Login</button>
    <p>Login Require:</p>
    <ul>
    <li><a title="Light Control" href="light.shtml">Light control</a></li>
    <li><a title="Demo Page 1" href="demo1.shtml">Demo page 1</a></li>
    <li><a title="Demo Page 2" href="demo2.shtml">Demo Page2</a></li>
    </ul>
    <p>Login not required:</p>
    <ul>
    <li><a title="Demo Page 3" href="demo3.html">Demo page 3</a></li>
    <li><a title="Demo page 4" href="demo4.html">Demo Page 4</a></li>
    </ul>
    <!--#logout-->
    </td>
    <td style="width: 63.4855%; height: 18px;">
    <p style="text-align: center;"><span style="color: #0000ff; font-size: 24px;"><strong>System information</strong></span></p>
    <div style="margin-left:20px;">
    <p>Onboard Temp.:<font color=red><!--#temp--></font></p>
    <p>Free RAM Size:<font color=red><!--#freeram--></font></p>
    <p>Free SD Size:<font color=red><!--#freesd--></font></p>
    </div>
    </td>
    </tr>
    </tbody>
    </table>
    </body></html>
    
      
  • login.html

  • <html lang="en">
    <head><title>Raspberry Pi Pico W - HTTPS Session</title></head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8">
    <Script>
    function logout() {
        const cookies = document.cookie.split(";");
    
        for (let i = 0; i < cookies.length; i++) {
            const cookie = cookies[i];
            const eqPos = cookie.indexOf("=");
            const name = eqPos > -1 ? cookie.substr(0, eqPos) : cookie;
        
           if (name=="sess_id"){
                document.cookie = name + "=;expires=Thu, 01 Jan 1970 00:00:00 GMT";
            }
    
            document.location.href="index.shtml";
        }
    }
    </Script>
    <body bgcolor="white" text="black">
    <h1><strong>Raspberry Pi Pico W - </strong>HTTPS Session</h1>
    <table style="border-collapse: collapse; width: 1000; height: 127px;" border="1">
    <tbody>
    <tr style="height: 18px;">
    <td style="width: 400; height: 18px;">
    <p><a title="System Info" href="index.shtml">Sysem Info</a></p>
    <p><button onClick="location.href='login.html'">Login</button>
    <p>Login Require:</p>
    <ul>
    <li><a title="Light Control" href="light.shtml">Light control</a></li>
    <li><a title="Demo Page 1" href="demo1.shtml">Demo page 1</a></li>
    <li><a title="Demo Page 2" href="demo2.shtml">Demo Page2</a></li>
    </ul>
    <p>Login not required:</p>
    <ul>
    <li><a title="Demo Page 3" href="demo3.html">Demo page 3</a></li>
    <li><a title="Demo page 4" href="demo4.html">Demo Page 4</a></li>
    </ul>
    <button onClick='javascript:logout();'>Logout</button>
    </td>
    <td style="width: 600; height: 18px;">
       <h1>Login</h1>
       <form name="login" action="login.cgi" method="post">
        <div align="center">
         <label><b>Username:</b></label>
         <input type="text" placeholder="Enter Username" name="user" required><br><br>
         <label><b>Password:</b></label>
         <input type="password" placeholder="Enter Password" name="pass" required><br><br>
         
         <button type="submit">Login</button>
        </div>
       </form> 
      </td>
      
     </tr>
    </table>
    </body>
    </html>
    
      

沒有留言:

張貼留言