diff --git a/cmd_parser.c b/cmd_parser.c index c667381..fc57c75 100644 --- a/cmd_parser.c +++ b/cmd_parser.c @@ -5,9 +5,6 @@ // #define DEBUG // #define REGDBG 1 -#define CONFIG_START 0x70000 -#define CONFIG_LEN 0x1000 - #include "rtl837x_common.h" #include "rtl837x_port.h" #include "rtl837x_flash.h" @@ -38,6 +35,8 @@ extern __code uint8_t * __code hex; extern __xdata uint8_t flash_buf[512]; extern __xdata struct flash_region_t flash_region; +extern __xdata char passwd[21]; + __xdata uint8_t vlan_names[VLAN_NAMES_SIZE]; __xdata uint16_t vlan_ptr; __xdata uint8_t gpio_last_value[8] = { 0 }; @@ -54,12 +53,16 @@ __xdata uint8_t cmd_available; __xdata uint8_t l; __xdata uint8_t line_ptr; __xdata char is_white; +__xdata char save_cmd; __xdata uint8_t ip[4]; #define N_WORDS SBUF_SIZE __xdata signed char cmd_words_b[N_WORDS]; +__xdata uint8_t cmd_history[CMD_HISTORY_SIZE]; +__xdata uint16_t cmd_history_ptr; + // Maps the physical port (starting from 0) to the logical port __code uint8_t phys_to_log_port[6] = { 4, 5, 6, 7, 3, 8 @@ -431,6 +434,36 @@ err: } +void parse_rnd(void) +{ + // In order to get a new random numner, this bit has to be set each time! + reg_bit_set(RTL837X_RLDP_RLPP, RLDP_RND_EN); + reg_read_m(RTL837X_RAND_NUM1); + print_byte(sfr_data[2]); + print_byte(sfr_data[3]); + reg_read_m(RTL837X_RAND_NUM0); + print_byte(sfr_data[0]); + print_byte(sfr_data[1]); + print_byte(sfr_data[2]); + print_byte(sfr_data[3]); + write_char('\n'); +} + + +void parse_passwd(void) +{ + if (cmd_words_b[2] > 0) { + signed char i; + signed char j = 0; + for (i = cmd_words_b[1]; (i != cmd_words_b[2] && i - cmd_words_b[1] < 20); i++) + passwd[j++] = cmd_buffer[i]; + passwd[j] = '\0'; + return; + } + print_string("Missing password\n"); +} + + // Parse command into words uint8_t cmd_tokenize(void) __banked { @@ -679,6 +712,12 @@ void cmd_parser(void) __banked if (cmd_compare(0, "regset")) { parse_regset(); } + if (cmd_compare(0, "rnd")) { + parse_rnd(); + } + if (cmd_compare(0, "passwd")) { + parse_passwd(); + } if (cmd_compare(0, "eee")) { int8_t port = -1; if (cmd_words_b[3] > 0) { @@ -706,21 +745,55 @@ void cmd_parser(void) __banked if (cmd_compare(0, "version")) { print_sw_version(); } + if (cmd_compare(0, "history")) { + __xdata uint16_t p = (cmd_history_ptr + 1) & CMD_HISTORY_MASK; + __xdata uint8_t found_begin = 0; + print_string("History ptr: "); + print_short(cmd_history_ptr); write_char('\n'); + while (p != cmd_history_ptr) { + print_short(p); write_char(' '); + if (!cmd_history[p] || cmd_history[p] == '\n') + found_begin = 1; + if (found_begin && cmd_history[p]) + write_char(cmd_history[p]); + p = (p + 1) & CMD_HISTORY_MASK; + } + } + if (save_cmd) { + uint8_t i; + for (i = 0; i < N_WORDS; i++) { + if (cmd_words_b[i] < 0) + break; + } + if (i < N_WORDS) { + i = cmd_words_b[--i]; + cmd_history_ptr = (cmd_history_ptr + i) & CMD_HISTORY_MASK; + __xdata uint16_t p = cmd_history_ptr; + cmd_history[cmd_history_ptr++] = '\n'; + do { + i--; + cmd_history[--p & CMD_HISTORY_MASK] = cmd_buffer[i]; + } while (i); + } + } } } -#define FLASH_READ_BURST_SIZE 0x100; +#define FLASH_READ_BURST_SIZE 0x100 +#define PASSWORD "1234" void execute_config(void) __banked { - memcpyc(flash_buf, "test", 5); - print_string_x(flash_buf); + __xdata uint32_t pos = CONFIG_START; + __xdata uint16_t len_left = CONFIG_LEN; - __xdata uint32_t pos = CONFIG_START; - __xdata uint16_t len_left = CONFIG_LEN; - do { + // Set default password, it can be overwritten in the configuration file + strtox(passwd, PASSWORD); + save_cmd = 0; + + do { flash_region.addr = pos; flash_region.len = FLASH_READ_BURST_SIZE; - write_char('-'); print_long(flash_region.addr); write_char(':'); print_short(flash_region.len); write_char('\n'); + write_char('-'); print_long(flash_region.addr); write_char(':'); print_short(flash_region.len); write_char('\n'); flash_read_bulk(flash_buf); @@ -737,7 +810,7 @@ void execute_config(void) __banked if (cmd_idx && !cmd_tokenize()) cmd_parser(); if (c == 0) - return; + goto config_done; break; } @@ -748,5 +821,12 @@ void execute_config(void) __banked len_left -= FLASH_READ_BURST_SIZE; pos += FLASH_READ_BURST_SIZE; - } while(len_left); + } while(len_left); + +config_done: + // Start saving commands to cmd_history + save_cmd = 1; + for (cmd_history_ptr = 0; cmd_history_ptr < CMD_HISTORY_SIZE; cmd_history_ptr++) + cmd_history[cmd_history_ptr] = 0; + cmd_history_ptr = 0; } diff --git a/html/config b/html/config new file mode 120000 index 0000000..f1a6cc6 --- /dev/null +++ b/html/config @@ -0,0 +1 @@ +../config.txt \ No newline at end of file diff --git a/html/config.js b/html/config.js new file mode 100644 index 0000000..2d48021 --- /dev/null +++ b/html/config.js @@ -0,0 +1,55 @@ +var configInterval = Number(); +var configuration = []; +const conf_cmds = [ + /ip\s+(\d{1,3}\.){3}\d{1,3}/, /gw\s+(\d{1,3}\.){3}\d{1,3}/, /netmask\s+(\d{1,3}\.){3}\d{1,3}/, + /eee(\s+\d)?\s+(on|off)/, /mirror(\s+(\d|10))(\s+(\d|10)(t|r)?)+/, /vlan\s+(\d{1,4})(\s+(\d|10)(t|u)?)+/ +]; +const conf_overwrite = [ + /ip/, /gw/, /netmask/, /eee\s+\w+/, /eee(\s+\w)/, /mirror/, /vlan\s+(\d{1,4})/ +]; + +function parseConf(s){ + var a = s.split(/\r\n|\n/); + for (var l = 0; l < a.length; l++) { + if (!a[l].length || a[l] == "\n" || a[l] == "\r\n") + continue; + console.log(l + ' --> ' + a[l]); + var ignore = true; + for (const x of conf_cmds) + if (x.test(a[l])) ignore = false; + if (ignore) continue; + for (const x of conf_overwrite) { + if (x.test(a[l])) { + console.log("Match ", x, " to ", a[l]); + m = a[l].match(x); + console.log("Starts with ", m[0]); + configuration = configuration.filter(item => !(item.startsWith(m[0]))); + } + } + configuration.push(a[l]); + } + console.log("Configuration now:"); + for (const x of configuration) { console.log(x); } +} + +async function fetchConfig() { + try { + const response = await fetch('/config'); + console.log("CONFIG: ", response); + const t = await response.text(); + return t; + } catch(err) { + console.error("Error: ", err); + } +} + +async function fetchCmdLog() { + try { + const response = await fetch('/cmd_log'); + console.log("CMD-Log: ", response); + const t = await response.text(); + return t; + } catch(error) { + console.error("Error: ", err); + } +} diff --git a/html/login.html b/html/login.html new file mode 100644 index 0000000..81a96ea --- /dev/null +++ b/html/login.html @@ -0,0 +1,30 @@ + + + RTL Switch Login + + + + + +
+

RTL Switch Login

+
+
+ + + +
+ +

+
+ + + diff --git a/html/main.js b/html/main.js index 27a8587..eac133e 100644 --- a/html/main.js +++ b/html/main.js @@ -10,6 +10,8 @@ var numPorts = 0; function update() { var xhttp = new XMLHttpRequest(); xhttp.onreadystatechange = function() { + if (this.readyState == 4 && this.status == 401) + document.location = "/login.html" if (this.readyState == 4 && this.status == 200) { const s = JSON.parse(xhttp.responseText); if (!numPorts) { diff --git a/html/navigation.js b/html/navigation.js index 92366c2..8b88bb8 100644 --- a/html/navigation.js +++ b/html/navigation.js @@ -5,5 +5,6 @@ document.getElementById('sidebar').innerHTML = + "
  • Mirroring
  • " + "
  • Port Aggregation
  • " + "
  • EEE
  • " + + "
  • System Settings
  • " + "
  • Firmware Update
  • "; diff --git a/html/style.css b/html/style.css index b1cdecc..a4edf4d 100644 --- a/html/style.css +++ b/html/style.css @@ -1,4 +1,4 @@ -h1, h2 { +h1, h2, h3 { color: #226; } ul { @@ -38,10 +38,17 @@ td { text-align: right; } -input[type=submit] { padding: 8px 16px;background-color:#aaf;color:#000; margin-bottom: 2em;} +input[type=submit] { padding: 8px 16px;background-color:#aaf;color:#000; margin-bottom: 2em;border-radius: 15px;width: 100%;} input[type=submit]:hover { background-color: #226; color: white;} -button {padding: 8px; background-color:#99f; color:#000;} +input[type=file] { padding: 8px 16px;background-color:#aaf;color:#000; margin-bottom: 2em;border-radius: 15px;width: 60%;} +input[type=file]:hover { background-color: #226; color: white;} +b +button {padding: 8px; background-color:#99f; color:#000;border-radius: 15px;} button:hover { background-color: #226; color: white;} +.action{padding: 8px 16px;margin-top: 2em;margin-right: 3em; background-color: #aaf;color: #000;border-radius: 15px; width: 100%;} +.action:hover { background-color: #226; color: white;} + +/* Port selection inputs */ .psel { position: absolute; opacity: 0; @@ -73,4 +80,41 @@ object { .isSFP{ opacity: .4; background-color: #660;} .isNOK{ color: #900;} .isOK{ color: #090;} -.action{padding: 8px 16px;margin-top: 2em;margin-right: 3em; background-color: #aaf;color: #000;} +.ip{padding:8px 16px;margin-bottom: 1em;margin-left: 1em} +.row {display: flex;} +.rcol {flex: 90%;} +.lcol {flex: 10%;} +span::before{ content:''; position: absolute; top: 40px; left:0; width: 100%; height: 2px; background: #aaf;} + +/* Login page */ +.login_page {margin: 0; padding: 0; background: #aaf; height: 100vh; overflow: hidden;} +.center{ + position: absolute; top: 50%; left: 50%; + transform: translate(-50%, -40% ); + width: 500px; height: 400px; + background: white; + border-radius: 2px; +} + +.center h1 { + text-align: center; + border-bottom: 1px solid silver; +} + +.center form { +padding: 0 60px; +box-sizing: border-box; +} +form .txt_field{ +position: relative; +border-bottom: 2px solid #adadad; +margin: 30px 0; +} +.txt_field input { width: 100%; padding: 0 5px; height: 40px; font-size: 16px; border: none; + background: none; + outline: none;} +.txt_field label { position: absolute; top: 50%; left: 5px; color:#adadad; transform: translateY(-50%); + font-size: 16px; pointer-events: none;transition: .5s; } +.txt_field span::before{ content:''; position: absolute; top: 40px; left:0; width: 100%; height: 2px; background: #aaf;} +.txt_field input:focus ~ label, +.txt_field input:valid ~ label { top: -5px; color: #aaf; } diff --git a/html/system.html b/html/system.html new file mode 100644 index 0000000..57e6a74 --- /dev/null +++ b/html/system.html @@ -0,0 +1,36 @@ + + + + + System Settings + + + +
    +
    +

    System Settings

    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    + When updating the above settings, remember to point your browser to the new IP afterwards:
    +
    +

    + Save all current settings to Flash:
    + + +
    + + + + + diff --git a/html/system.js b/html/system.js new file mode 100644 index 0000000..50beec5 --- /dev/null +++ b/html/system.js @@ -0,0 +1,76 @@ +var systemInterval = Number(); +const ips = ["ip", "netmask", "gw"]; + +function checkIp(ip) { + const ipv4 = /^(\d{1,3}\.){3}\d{1,3}$/; + if (!ipv4.test(ip)) {alert(`Invalid ip:${ip}`); return false }; + return true; +} + +async function ipSub() { + for (let i=0;i<3;i++) { + if (!checkIp(document.getElementById(ips[i]).value)) + return; + } + for (let i=0; i<3;i++){ + var cmd = ips[i]+' '+document.getElementById(ips[i]).value; + try { + const response = await fetch('/cmd', { + method: 'POST', + body: cmd + }); + console.log('Completed!', response); + fetchIP(); + } catch(err) { + console.error(`Error: ${err}`); + } + } +} + +async function sendConfig(c) { + const form = new FormData(); + form.append("MAX_FILE_SIZE", "4096"); + form.append("configuration", new Blob([c], {type: "application/octet-stream"})); + try { + const response = await fetch('/config', { + method: 'POST', + body: form + }); + console.log('Completed!', response); + } catch(err) { + console.error(`Error: ${err}`); + } +} + +async function flashSave() { + fetchConfig().then((s) => { + parseConf(s); + fetchCmdLog().then((s) => { + parseConf(s); + var body = ""; + for (const x of configuration) { body = body + x + "\n"; } + console.log("CONFIGURATION to save: ", body); + sendConfig(body); + }); + }); +} + +function fetchIP() { + var xhttp = new XMLHttpRequest(); + xhttp.onreadystatechange = function() { + if (this.readyState == 4 && this.status == 200) { + const s = JSON.parse(xhttp.responseText); + console.log("IP: ", s); + document.getElementById("ip").value=s.ip_address; + document.getElementById("netmask").value=s.ip_netmask; + document.getElementById("gw").value=s.ip_gateway; + clearInterval(systemInterval); + } + } + xhttp.open("GET", `/information.json`, true); + xhttp.send(); +} + +window.addEventListener("load", function() { + systemInterval = setInterval(fetchIP, 1000); +}); diff --git a/html/update.html b/html/update.html index 22ee927..30d4ea0 100644 --- a/html/update.html +++ b/html/update.html @@ -6,13 +6,13 @@ -
    -
    +

    Firmware Update

    - Choose a firmware update file to upload:
    - + Choose a firmware update file to upload:

    +
    +
    diff --git a/httpd/httpd.c b/httpd/httpd.c index fb4d81a..eb25cfd 100644 --- a/httpd/httpd.c +++ b/httpd/httpd.c @@ -2,11 +2,15 @@ #include "httpd.h" #include "page_impl.h" #include "../rtl837x_common.h" +#include "../rtl837x_regs.h" #include "../cmd_parser.h" #include "../rtl837x_flash.h" #include "uip.h" #include "../html_data.h" +#define SESSION_ID_LENGTH 12 +#define SESSION_TIMEOUT 200 + // Upload Firmware to 1M #define FIRMWARE_UPLOAD_START 0x100000 @@ -18,6 +22,8 @@ #pragma codeseg BANK1 #pragma constseg BANK1 +extern volatile __xdata uint8_t sfr_data[4]; +extern __code uint8_t * __code hex; extern __code struct f_data f_data[]; extern __code char * __code mime_strings[]; extern __xdata struct flash_region_t flash_region; @@ -33,16 +39,27 @@ __xdata uint16_t slen; __xdata uint16_t o_idx; __xdata uint16_t mpos; __xdata uint16_t len_left; +__xdata uint16_t cont_len; +__xdata uint32_t cont_addr; // HTTP header properties __xdata uint8_t boundary[72]; __xdata uint8_t *content_type = 0; +__xdata uint8_t *session = 0; // Global variables holding POST state __xdata uint16_t bindex; // Current index into the boundary - +__xdata uint8_t verify_crc; +__xdata uint32_t max_upload; __xdata uint16_t short_parsed; +__xdata char passwd[21]; +__xdata char session_id[SESSION_ID_LENGTH + 1]; +__xdata uint8_t authenticated; +__xdata uint32_t now; +__xdata uint8_t *timeptr; +__xdata uint32_t last_session_use; + #define TSTATE_NONE 0 #define TSTATE_TX 1 #define TSTATE_ACKED 2 @@ -161,6 +178,19 @@ void send_bad_request(void) } +void send_to_login(void) +{ + slen = strtox(outbuf, "HTTP/1.1 302 Found\r\n" \ + "Location: login.html\r\n\r\n"); +} + + +void send_unauthorized(void) +{ + slen = strtox(outbuf, "HTTP/1.1 401 Unauthorized\r\n\r\n"); +} + + __xdata uint8_t *skip_boundary(__xdata uint8_t *p) { while (*p) { @@ -175,6 +205,8 @@ __xdata uint8_t *skip_boundary(__xdata uint8_t *p) __xdata uint8_t *scan_header(__xdata uint8_t *p) { content_type = 0; + session = 0; + authenticated = 0; while (*p != '\r' || *(p + 1) != '\n' || *(p + 2) != '\r' || *(p + 3) != '\n') { write_char(*p); @@ -182,9 +214,11 @@ __xdata uint8_t *scan_header(__xdata uint8_t *p) break; if (is_word(p, "\nContent-Type:")) content_type = p + 15; + else if (is_word(p, "\nCookie:")) + session = p + 17; } if (content_type && is_word(content_type, "multipart/form-data; boundary")) { - print_string("\nFound multiplart\n"); + print_string("\nFound multipart\n"); content_type += 30; uint8_t i = 0; while (content_type[i] != '\r' && content_type[i] != '\n') { @@ -198,10 +232,37 @@ __xdata uint8_t *scan_header(__xdata uint8_t *p) boundary[3] = '-'; boundary[i + 4] = 0; } + + read_reg_timer(&now); + + if (session) { + if (now - last_session_use > SESSION_TIMEOUT) { + print_string("Session expired\n"); + } else { + if (is_word_x(session, session_id)) + authenticated = 1; + else + print_string("Invalid session cookie!\n"); + } + } return p; } +void gen_random_bytes(__xdata uint8_t *b, uint8_t bytes) +{ + __xdata uint8_t i = 0; + while (bytes) { + if (!i) + get_random_32(); + b[--bytes] = itohex(sfr_data[i]); + if (!bytes) { break; } + b[--bytes] = itohex(sfr_data[i] >> 4 | sfr_data[i] << 4); + i = (i + 1) & 0x3; + } +} + + /* * Reads post data from the http stream and writes it into flash memory * Input: the current position in the TCP buffer (uip_appdata) @@ -230,14 +291,21 @@ uint8_t stream_upload(uint16_t bptr) uptr += write_len; write_len = 0; // TODO: This is a bit premature, what about a nice web-page saying the device will reset??? - print_string("CRC16: "); print_short(crc_final); write_char('\n'); - if (crc_final == 0xb001) { - print_string("Checksum OK."); - } else { - print_string("Checksum incorrect!"); + if (verify_crc) { + print_string("CRC16: "); print_short(crc_final); write_char('\n'); + if (crc_final == 0xb001) { + print_string("Checksum OK."); + } else { + print_string("Checksum incorrect!"); + } + print_string("Upload to flash done, will reset!\n"); + reset_chip(); } - print_string("Upload to flash done, will reset!\n"); - reset_chip(); + // Make sure there is a 0 at the end of the uploaded data + flash_buf[0] = 0; + flash_region.addr = uptr; + flash_region.len = 1; + flash_write_bytes(flash_buf); if (bptr >= uip_len) return 0; return 1; @@ -302,13 +370,38 @@ void handle_post(void) if (is_word(request_path, "cmd")) { register uint8_t i = 0; p += 4; + if (!authenticated) { + send_unauthorized(); + return; + } while (*p && *p != '\n' && *p != '\r') cmd_buffer[i++] = *p++; cmd_buffer[i] = '\0'; if (i) cmd_available = 1; - } else if (is_word(request_path, "upload")) { - print_string("POST upload request\n"); + } else if (is_word(request_path, "login")) { + print_string("POST login\n"); + p += 8; // Read also over "pwd=" + if (is_word_x(p, passwd)) { + print_string("Password accepted!\n"); + read_reg_timer(&last_session_use); + gen_random_bytes(session_id, SESSION_ID_LENGTH); + session_id[SESSION_ID_LENGTH] = '\0'; + slen = strtox(outbuf, "HTTP/1.1 302 Found\r\nLocation: index.html\r\n" \ + "Set-Cookie: session="); + for (register uint8_t i = 0; i < SESSION_ID_LENGTH; i++) + outbuf[slen++] = session_id[i]; + slen += strtox(outbuf + slen, "; SameSite=Strict\r\n\r\n"); + } else { + slen = strtox(outbuf, "HTTP/1.1 302 Found\r\nLocation: login.html\r\n\r\n"); + } + return; + } else if (is_word(request_path, "upload") || is_word(request_path, "config")) { + print_string("POST upload/config request\n"); + if (!authenticated) { + send_unauthorized(); + return; + } if (!boundary[0]) { print_string("Bad request, no boundary!\n"); send_bad_request(); @@ -328,7 +421,18 @@ void handle_post(void) print_string("Have content octets\n"); p += 4; // Skip \r\n\r\n sequence at end of preamble of part - uptr = FIRMWARE_UPLOAD_START; + if (is_word(request_path, "upload")) { + uptr = FIRMWARE_UPLOAD_START; + verify_crc = 1; + max_upload = 1024576; + } else { + print_string("Configuration upload, erasing config mem!\n"); + uptr = CONFIG_START; + verify_crc = 0; + max_upload = 2048; + flash_region.addr = CONFIG_START; + flash_sector_erase(); + } crc_value = 0; bindex = 0; write_len = 0; @@ -386,17 +490,34 @@ void httpd_appcall(void) if (slen > uip_mss()) { print_string("Sending A: "); print_short(slen); write_char('\n'); uip_send(outbuf + o_idx, uip_mss()); - print_string("Sending A done\n"); s->tstate = TSTATE_TX; } else if (slen > 0) { print_string("Sending B: "); print_short(slen); write_char('\n'); uip_send(outbuf + o_idx, slen); - print_string("Sending B done\n"); + s->tstate = TSTATE_TX; + } else if (cont_len) { + print_string("CONT cont_len: "); print_short(cont_len); + slen = cont_len > uip_mss() ? uip_mss() : cont_len; + if (slen > TCP_OUTBUF_SIZE) + slen = TCP_OUTBUF_SIZE; + flash_region.addr = cont_addr; + flash_region.len = slen; + flash_read_bulk(outbuf); + uip_send(outbuf, slen); + cont_len -= slen; + cont_addr += slen; s->tstate = TSTATE_TX; } } else if (uip_newdata() && s->tstate == TSTATE_POST) { - stream_upload(0); + // Check here maxupload by subtracting uip_len and close socekt if fails! + if (max_upload - uip_len > 0) { + stream_upload(0); + } else { + send_bad_request(); + goto do_send; + } } else if (uip_newdata() && s->tstate != TSTATE_TX) { + cont_len = 0; write_char('<'); print_short(uip_len); write_char('\n'); __xdata uint8_t *p = uip_appdata; // Mark end of request header with \0 @@ -418,6 +539,7 @@ void httpd_appcall(void) if (is_word(p, "GET")) print_string("GET request "); p += 4; + scan_header(p); __xdata uint8_t *q = p; while (!is_separator(*p)) p++; @@ -429,6 +551,11 @@ void httpd_appcall(void) entry = find_entry(q); print_string("Entry is: "); print_byte(entry); write_char('\n'); if (entry == 0xff) { + if (!authenticated) { + print_string("Not authorized!\n"); + send_unauthorized(); + goto do_send; + } print_string("Not file entry\n"); if (!strcmp(q, "/status.json")) { send_status(); @@ -443,16 +570,34 @@ void httpd_appcall(void) send_eee(); } else if (is_word(q, "/mirror.json")) { send_mirror(); + } else if (is_word(q, "/config")) { + send_config(); + } else if (is_word(q, "/cmd_log")) { + send_cmd_log(); } else { send_not_found(); } } else { - print_string("Have entry\n"); + print_string("Have entry, authenticated: "); print_byte(authenticated); write_char('\n'); + if (!authenticated && !(f_data[entry].start == FDATA_START_login_html + || f_data[entry].start == FDATA_START_style_css)) { + send_to_login(); + goto do_send; + } + // A web-page is actively accessed, we can reset session time-out + reg_read_m(RTL837X_REG_SEC_COUNTER); + timeptr = (uint8_t*)&last_session_use; // last_session_use is Little endian + timeptr[0] = sfr_data[3]; timeptr[1] = sfr_data[2]; timeptr[2] = sfr_data[1]; timeptr[3] = sfr_data[0]; + slen = strtox(outbuf, "HTTP/1.1 200 OK\r\nContent-Type: "); slen += strtox(outbuf + slen, mime_strings[f_data[entry].mime]); - slen += strtox(outbuf + slen, "\r\nCache-Control: max-age=2592000\r\n\r\n"); + slen += strtox(outbuf + slen, "\r\nCache-Control: max-age=60, must-revalidate\r\n\r\n"); len_left = f_data[entry].len; - + if (len_left > (TCP_OUTBUF_SIZE - slen)) { + cont_len = len_left - (TCP_OUTBUF_SIZE - slen); + len_left = TCP_OUTBUF_SIZE - slen; + cont_addr = f_data[entry].start + len_left; + } print_string("MIME: "); print_string(mime_strings[f_data[entry].mime]); write_char('\n'); flash_region.addr = f_data[entry].start; flash_region.len = len_left; diff --git a/httpd/page_impl.c b/httpd/page_impl.c index b787d4a..5c66f99 100644 --- a/httpd/page_impl.c +++ b/httpd/page_impl.c @@ -4,17 +4,21 @@ #include "../rtl837x_common.h" #include "../rtl837x_regs.h" #include "../rtl837x_port.h" +#include "../rtl837x_flash.h" #include "uip.h" #include "../html_data.h" #include #include "../phy.h" #include "../version.h" +#include "page_impl.h" #pragma codeseg BANK1 #pragma constseg BANK1 extern __xdata uint8_t outbuf[TCP_OUTBUF_SIZE]; extern __xdata uint16_t slen; +extern __xdata uint16_t cont_len; +extern __xdata uint32_t cont_addr; extern __code uint8_t * __code hex; extern __xdata uip_ipaddr_t uip_hostaddr, uip_draddr, uip_netmask; extern __code struct uip_eth_addr uip_ethaddr; @@ -30,22 +34,13 @@ extern __xdata uint8_t isRTL8373; extern __xdata uint8_t sfp_pins_last; extern __xdata uint8_t vlan_names[VLAN_NAMES_SIZE]; +extern __xdata uint8_t cmd_history[CMD_HISTORY_SIZE]; +extern __xdata uint16_t cmd_history_ptr; + +extern __xdata struct flash_region_t flash_region; + __code uint8_t * __code HTTP_RESPONCE_JSON = "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\r\n"; - -/* Convert only the lower nibble to ascii HEX char. - For convenience the upper nibble is masked out. -*/ -inline char itohex(uint8_t val) { - // Ignore upper nibble for convenience. - val &= 0x0f; - val -= 10; - - // 10 or above - if ((int8_t)val >= 0) - val += ('a' - '0' - 10); - - return val + ('0' + 10); -} +__code uint8_t * __code HTTP_RESPONCE_TXT = "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\n"; // Convert uint8_t to ascii HEX char push on html-buffer. void charhex_to_html(char c) @@ -371,3 +366,46 @@ void send_status(void) char_to_html(']'); } } + + +void send_config(void) +{ + print_string("send_config called\n"); + __xdata uint32_t pos = CONFIG_START; // 70000 , 6c000 / 0xc000 = 9 + + extern __xdata uint16_t len_left = CONFIG_LEN; + slen = strtox(outbuf, HTTP_RESPONCE_TXT); + while (read_flash((CONFIG_START-CODE0_SIZE) / CODE_BANK_SIZE + 1, + (__code uint8_t *) (((CONFIG_START + len_left - CODE0_SIZE) % CODE_BANK_SIZE) + CODE0_SIZE + len_left)) == 0xff) { + print_short(len_left); + len_left--; + } + len_left++; + + if (len_left > (TCP_OUTBUF_SIZE - slen)) { + cont_len = len_left - (TCP_OUTBUF_SIZE - slen); + len_left = TCP_OUTBUF_SIZE - slen; + cont_addr = len_left; + } + flash_region.addr = CONFIG_START; + flash_region.len = len_left; + flash_read_bulk(outbuf + slen); + slen += len_left; +} + +void send_cmd_log(void) +{ + print_string("send_cmd_log called\n"); + slen = strtox(outbuf, HTTP_RESPONCE_TXT); + __xdata uint16_t p = (cmd_history_ptr + 1) & CMD_HISTORY_MASK; + __xdata uint8_t found_begin = 0; + print_string("History ptr: "); + print_short(cmd_history_ptr); write_char('\n'); + while (p != cmd_history_ptr) { + if (!cmd_history[p] || cmd_history[p] == '\n') + found_begin = 1; + if (found_begin && cmd_history[p]) + outbuf[slen++] = cmd_history[p]; + p = (p + 1) & CMD_HISTORY_MASK; + } +} diff --git a/httpd/page_impl.h b/httpd/page_impl.h index 9692989..709cbd3 100644 --- a/httpd/page_impl.h +++ b/httpd/page_impl.h @@ -3,9 +3,26 @@ void send_counters(char port); void send_status(void); -void send_vlan(register uint16_t vlan); +void send_vlan(uint16_t vlan); void send_basic_info(void); void send_eee(void); void send_mirror(void); +void send_config(void); +void send_cmd_log(void); + +/* Convert only the lower nibble to ascii HEX char. + For convenience the upper nibble is masked out. +*/ +inline char itohex(uint8_t val) { + // Ignore upper nibble for convenience. + val &= 0x0f; + val -= 10; + + // 10 or above + if ((int8_t)val >= 0) + val += ('a' - '0' - 10); + + return val + ('0' + 10); +} #endif diff --git a/rtl837x_common.h b/rtl837x_common.h index 29f6536..06e4a23 100644 --- a/rtl837x_common.h +++ b/rtl837x_common.h @@ -47,6 +47,15 @@ #define IS_SFP(port) (i == maxPort || i == 3) #endif +#define CONFIG_START 0x70000 +#define CONFIG_LEN 0x1000 +#define CODE0_SIZE 0x4000 +#define CODE_BANK_SIZE 0xc000 + +// Constants for the circular command buffer, the size must be 2^n +#define CMD_HISTORY_SIZE 0x400 +#define CMD_HISTORY_MASK (CMD_HISTORY_SIZE - 1) + /** * Representation of a 48-bit Ethernet address. */ @@ -94,6 +103,8 @@ uint16_t strlen_x(register __xdata const char *s); uint16_t strtox(register __xdata uint8_t *dst, register __code const char *s); void tcpip_output(void); void print_string_x(__xdata char *p); - +uint8_t read_flash(uint8_t bank, __code uint8_t *addr); +void get_random_32(void); +void read_reg_timer(uint32_t * tmr); #endif diff --git a/rtl837x_regs.h b/rtl837x_regs.h index 754b98c..ce6052e 100644 --- a/rtl837x_regs.h +++ b/rtl837x_regs.h @@ -170,6 +170,13 @@ #define EEE_1000 0x04 #define EEE_2G5 0x10 +/* + * RANDOM + */ +#define RTL837X_RLDP_RLPP 0x106C +#define RLDP_RND_EN 3 +#define RTL837X_RAND_NUM0 0x107C +#define RTL837X_RAND_NUM1 0x1080 #ifdef REGDBG diff --git a/rtlplayground.c b/rtlplayground.c index 946e472..073ca78 100644 --- a/rtlplayground.c +++ b/rtlplayground.c @@ -402,6 +402,18 @@ void sfr_set_zero(void) { } } + +/* + * Create 32 random number in sfr_data + */ +void get_random_32(void) +{ + // In order to get a new random numner, this bit has to be set each time! + reg_bit_set(RTL837X_RLDP_RLPP, RLDP_RND_EN); + reg_read_m(RTL837X_RAND_NUM0); +} + + /* * Transfer Network Interface RX data from the ASIC to the 8051 XMEM * data will be stored in the rx_header structure @@ -590,6 +602,20 @@ void cpy_4(__xdata uint8_t dest[], __xdata uint8_t source[]) } +void read_reg_timer(uint32_t * tmr) +{ + uint8_t * val = (uint8_t *)tmr; + SFR_REG_ADDR_U16 = RTL837X_REG_SEC_COUNTER; + SFR_EXEC_GO = SFR_EXEC_READ_REG; + do { + } while (SFR_EXEC_STATUS != 0); + *val++ = SFR_DATA_0; + *val++ = SFR_DATA_8; + *val++ = SFR_DATA_16; + *val = SFR_DATA_24; +} + + void sds_config_mac(uint8_t sds, uint8_t mode) { reg_read_m(RTL837X_REG_SDS_MODES); diff --git a/tools/httpd_sim.c b/tools/httpd_sim.c index 0cd3e03..8d45d19 100644 --- a/tools/httpd_sim.c +++ b/tools/httpd_sim.c @@ -4,14 +4,20 @@ #include #include #include +#include #include #include "httpd_sim.h" #include #include #include "../version.h" +#define SESSION_ID "1234567890ab" +#define PASSWORD "1234" +#define SESSION_TIMEOUT 20 + #define PORTS 6 time_t last_called; +time_t last_session_use; uint64_t txG[PORTS], txB[PORTS], rxG[PORTS], rxB[PORTS]; char txG_buff[20], txB_buff[20], rxG_buff[20], rxB_buff[20]; @@ -20,6 +26,12 @@ char upload_buffer[4194304]; // 4MB char *content_type = NULL; char boundary[72]; +char cmd_history[1024][256]; +uint16_t cmd_ptr = 0; +char *uploaded_config = NULL; +int uploaded_config_len; +const char *session = NULL; +bool authenticated = false; char is_word(char *c, char *d) { @@ -101,8 +113,10 @@ void send_status(int s) struct json_object *ports, *v; const char *jstring; char *header = "HTTP/1.1 200 OK\r\n" - "Content-Type: application/json; charset=UTF-8\r\n\r\n"; + "Cache-Control: no-cache\r\n" + "Content-Type: application/json; charset=UTF-8\r\n\r\n"; + printf("Sending status.\n"); time_t now = time(NULL); now = last_called ? last_called + 1 : now; // Make sure we don't divide by 0 for rates @@ -202,6 +216,32 @@ void send_mirror(int s) } +void send_cmd_log(int s) +{ + char *header = "HTTP/1.1 200 OK\r\n" + "Content-Type: text/plain; charset=UTF-8\r\n\r\n"; + write(s, header, strlen(header)); + + for (int i = 0; i < cmd_ptr; i++) { + write(s, cmd_history[i], strlen(cmd_history[i])); + write(s, "\n", 1); + printf("%d: %s\n", i, cmd_history[i]); + } +} + + +void send_config(int s) +{ + char *header = "HTTP/1.1 200 OK\r\n" + "Content-Type: text/plain; charset=UTF-8\r\n\r\n"; + printf("Sending uploaded config\n"); + write(s, header, strlen(header)); + printf("Uploaded config len: %d\n", uploaded_config_len); + printf(">%s<", uploaded_config); + write(s, uploaded_config, uploaded_config_len); +} + + struct Server serverConstructor(int port, void (*launch)(struct Server *server)) { struct Server server; @@ -253,13 +293,30 @@ void send_bad_request(int socket) { } +void send_to_login(int socket) { + char *response = "HTTP/1.1 302 Found\r\n" + "Location: login.html\r\n\r\n"; + write(socket, response, strlen(response)); +} + + +void send_unauthorized(int socket) { + char *response = "HTTP/1.1 401 Unauthorized\r\n\r\n"; + write(socket, response, strlen(response)); +} + + char *scan_header(char *p) { + session = 0; + authenticated = false; while (*p != '\r' || *(p + 1) != '\n' || *(p + 2) != '\r' || *(p + 3) != '\n') { if (!*p++) break; - if (*p == '\n' && is_word(p + 1, "Content-Type:")) + if (is_word(p, "\nContent-Type:")) content_type = p + 15; + else if (is_word(p, "\nCookie:")) + session = p + 17; } if (content_type && is_word(content_type, "multipart/form-data; boundary")) { printf("Found multiplart\n"); @@ -275,6 +332,19 @@ char *scan_header(char *p) boundary[i + 2] = 0; } + time_t now = time(NULL); + if (session) { + printf("Session: >%s<. time now: %ld last %ld\n", session, now, last_session_use); + if (now - last_session_use > SESSION_TIMEOUT) { + printf("Session expired\n"); + } else { + if (!strncmp(session, SESSION_ID, 12)) + authenticated = true; + else + printf("Invalid session cookie!\n"); + } + } + printf("Time now: %ld last %ld\n", now, last_session_use); return p; } @@ -289,6 +359,11 @@ char *skip_boundary(char *p) return p; } +void print_cmd_history(void) +{ + for (int i = 0; i < cmd_ptr; i++) + printf("%d: %s\n", i, cmd_history[i]); +} void launch(struct Server *server) { @@ -313,33 +388,66 @@ void launch(struct Server *server) puts(buffer); if (is_word(buffer, "GET")) { - printf("GET request\n"); + scan_header(buffer); if (!strncmp(&buffer[4], "/status.json", 12)) { printf("Status request\n"); - send_status(new_socket); + if (!authenticated) + send_unauthorized(new_socket); + else + send_status(new_socket); goto done; - } - if (!strncmp(&buffer[4], "/eee.json", 9)) { + } else if (!strncmp(&buffer[4], "/eee.json", 9)) { printf("EEE request\n"); - send_eee(new_socket); + if (!authenticated) + send_unauthorized(new_socket); + else + send_eee(new_socket); goto done; - } - if (!strncmp(&buffer[4], "/information.json", 12)) { + } else if (!strncmp(&buffer[4], "/information.json", 17)) { printf("Status request\n"); - send_basic_info(new_socket); + if (!authenticated) + send_unauthorized(new_socket); + else + send_basic_info(new_socket); goto done; - } - if (!strncmp(&buffer[4], "/mirror.json", 12)) { + } else if (!strncmp(&buffer[4], "/mirror.json", 12)) { printf("Mirror request\n"); - send_mirror(new_socket); + if (!authenticated) + send_unauthorized(new_socket); + else + send_mirror(new_socket); goto done; - } - if (!strncmp(&buffer[4], "/vlan.json?vid=", 15)) { + } else if (!strncmp(&buffer[4], "/vlan.json?vid=", 15)) { int vlan = atoi(&buffer[19]); printf("VLAN request for %d\n", vlan); - send_vlan(new_socket, vlan); + if (!authenticated) + send_unauthorized(new_socket); + else + send_vlan(new_socket, vlan); + goto done; + } else if (!strncmp(&buffer[4], "/cmd_log", 8)) { + printf("Request cmd_log\n"); + if (!authenticated) + send_unauthorized(new_socket); + else + send_cmd_log(new_socket); + goto done; + } else if (!strncmp(&buffer[4], "/config ", 8) && uploaded_config) { + printf("Request current config.\n"); + if (!authenticated) + send_unauthorized(new_socket); + else + send_config(new_socket); goto done; } + if (!authenticated && !(!strncmp(&buffer[4], "/login.html", 11) || !strncmp(&buffer[4], "/style.css", 10))) { + send_to_login(new_socket); + goto done; + } + + // A web-page is actively accessed, we can reset session time-out + last_session_use = time(NULL); + int i = 0; while (!isspace(buffer[4 + i])) i++; @@ -389,10 +497,44 @@ void launch(struct Server *server) send_bad_request(new_socket); goto done; } - + if (!authenticated && !is_word(&buffer[5], "/login")) { + send_unauthorized(new_socket); + goto done; + } printf("Bytes read %ld\n", bytesRead); - if (is_word(&buffer[5], "/upload")) { - printf("POST upload request\n"); + if (is_word(&buffer[5], "/cmd")) { + printf("POST cmd\n"); + printf("CMD: %s\n", p + 4); + strcpy(cmd_history[cmd_ptr], p + 4); + cmd_ptr++; + print_cmd_history(); + char *response = "HTTP/1.1 200 OK\r\n" + "Content-Type: text/html\r\n\r\n" + " Upload OK" + "

    Command executed successully

    "; + write(new_socket, response, strlen(response)); + goto done; + } else if (is_word(&buffer[5], "/login")) { + printf("POST login\n"); + p += 4; + p += 4; // Read also over "pwd=" + char *response; + if (is_word(p, PASSWORD)) { + printf("Password accepted!\n"); + response = "HTTP/1.1 302 Found\r\n" + "Location: index.html\r\n" + "Set-Cookie: session=" SESSION_ID "; SameSite=Strict\r\n"; + } else { + response = "HTTP/1.1 302 Found\r\n" + "Location: login.html\r\n\r\n"; + } + write(new_socket, response, strlen(response)); + goto done; + } else if (is_word(&buffer[5], "/upload") || is_word(&buffer[5], "/config")) { + printf("POST upload/config request\n"); + bool config_upload = false; + if (is_word(&buffer[5], "/config")) + config_upload = true; if (!boundary[0]) { printf("Bad request, no boundary!\n"); send_bad_request(new_socket); @@ -409,6 +551,7 @@ void launch(struct Server *server) } while (!is_word(content_type, "application/octet-stream")); printf("Have content: >%s<\n", content_type); + p += 4; // Skip \r\n\r\n after content type char *uptr = upload_buffer; int bindex = 0; int bptr = p - buffer; @@ -441,11 +584,19 @@ void launch(struct Server *server) " Upload OK" "

    File uploaded successully

    "; write(new_socket, response, strlen(response)); + if (config_upload) { + uploaded_config_len = (uptr - upload_buffer); + if (uploaded_config) + free (uploaded_config); + uploaded_config = malloc(uploaded_config_len + 1); + memcpy(uploaded_config, upload_buffer, uploaded_config_len); + } goto done; } } char *response = "HTTP/1.1 200 OK\r\n" - "Content-Type: "; + "Cache-Control: max-age=60, must-revalidate\r\n" + "Content-Type: "; write(new_socket, response, strlen(response)); write(new_socket, mime, strlen(mime)); diff --git a/tools/httpd_sim.h b/tools/httpd_sim.h index 412d2f8..b0542ad 100644 --- a/tools/httpd_sim.h +++ b/tools/httpd_sim.h @@ -3,7 +3,7 @@ #include -#define BUFFER_SIZE 2400 +#define BUFFER_SIZE 24000 struct Server { int domain;