mirror of
https://github.com/logicog/RTLPlayground.git
synced 2026-10-29 17:54:14 +08:00
The pages become one index.html holding every section, one app.js and a self-contained login.html. The layout is a sidebar with light, dark and Selenized themes (or follow the OS), a live dashboard with per-port packet rates, and confirmation dialogs in front of the actions that can lock the operator out. Behaviour changes against the previous pages: - every request goes through one queue that waits for the previous response BODY rather than its headers: the firmware shares a single output buffer across connections and delimits responses by closing, so a second request in flight corrupts the first - "Save to flash" merges the command log into the startup configuration with the grammar the console accepts, shows the result before writing, verifies the sector by reading it back and clears the command log only after that; the header shows an "unsaved changes" badge while the running configuration differs from flash - the console section shows the output the firmware returns for a command, including the parser's explanation on a rejected one - the firmware update section checks size, bank header and CRC16 of the image in the browser before uploading, shows upload progress and waits for the switch to come back after the reset - the ports table shows the learned MAC of a port when there is exactly one and the count otherwise; the MAC table sorts per column, filters, and names the LAG a learned address sits behind - the spanning tree section sends only the settings that changed on Apply instead of one command per control event - link aggregation groups are numbered 1-4 like the command line, and applying an empty group clears it The translation table is inline in app.js with English, Japanese and Chinese. The language selector sits in the page header and reloads the page so that dynamically built tables are rebuilt in the new language. Only login.html and the favicon are served before authentication; the stylesheet, the port images and the old script bundle no longer exist. The simulator's allowlist follows the firmware's.
920 lines
27 KiB
C
920 lines
27 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include <time.h>
|
|
#include "httpd_sim.h"
|
|
#include <json.h>
|
|
#include <signal.h>
|
|
#include "../version.h"
|
|
|
|
#define SESSION_ID "1234567890ab"
|
|
#define PASSWORD "1234"
|
|
#define SESSION_TIMEOUT 2000
|
|
|
|
#define PORTS 6
|
|
#define NSFP 1
|
|
|
|
#define N_COUNTERS 52
|
|
|
|
#if PORTS == 9
|
|
const uint8_t physToLogPort[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8};
|
|
#else
|
|
const uint8_t physToLogPort[] = { 4, 5, 6, 7, 3, 8};
|
|
#endif
|
|
|
|
struct l2_entry {
|
|
char mac[18];
|
|
char vlan[4];
|
|
char type;
|
|
uint8_t port;
|
|
uint16_t idx;
|
|
};
|
|
|
|
#define L2_ENTRY_NUM 12
|
|
#define L2_MAX_TRANSFER 5
|
|
const struct l2_entry l2_entries[12] = {
|
|
{ "00:01:2f:00:00:01", "001", 'l', 7, 0x10 },
|
|
{ "00:06:78:00:00:02", "001", 'l', 7, 0x20 },
|
|
{ "00:01:2e:00:00:03", "001", 's', 7, 0x30 },
|
|
{ "60:7d:09:00:00:04", "001", 'l', 7, 0x40 },
|
|
{ "1c:2a:a3:00:00:05", "001", 'l', 7, 0x50 },
|
|
{ "1c:2a:a3:00:00:06", "001", 's', 7, 0x60 },
|
|
{ "02:e0:4c:00:00:07", "001", 'l', 7, 0x70 },
|
|
{ "00:e0:4c:00:00:08", "001", 'l', 4, 0x80 },
|
|
{ "1c:2a:a3:00:00:09", "001", 'l', 9, 0x90 }, // CPU-Port
|
|
{ "3c:8c:f8:00:00:0a", "001", 'l', 7, 0xa0 },
|
|
{ "00:01:2e:00:00:0b", "001", 'l', 7, 0xb0 },
|
|
{ "00:01:2f:00:01:01", "001", 'l', 7, 0xc0 }
|
|
};
|
|
|
|
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], counter_buf[20], counter_name[20];
|
|
char sfp_temp[8], sfp_vcc[8], sfp_txbias[8], sfp_txpower[8], sfp_rxpower[8], sfp_laser[8], sfp_options[6];
|
|
char num_buff[20];
|
|
char l2_idx_buff[20];
|
|
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)
|
|
{
|
|
uint8_t i = 0;
|
|
|
|
while (d[i] && (d[i] == c[i]))
|
|
i++;
|
|
|
|
if (d[i])
|
|
return 0;
|
|
if (c[i] != ' ' && c[i] != '\t' && c[i] != ':' && c[i] != '?' && c[i] != '=' && c[i] != '\n' && c[i] != '\r' && c[i])
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
|
|
int hasSuffix(const char *str, const char *suffix)
|
|
{
|
|
if (!str || !suffix)
|
|
return 0;
|
|
|
|
size_t lenstr = strlen(str);
|
|
size_t lensuffix = strlen(suffix);
|
|
|
|
if (lensuffix > lenstr)
|
|
return 0;
|
|
|
|
return strncmp(str + lenstr - lensuffix, suffix, lensuffix) == 0;
|
|
}
|
|
|
|
|
|
char *getMime(const char *name)
|
|
{
|
|
if (hasSuffix(name, ".html"))
|
|
return "text/html";
|
|
else if (hasSuffix(name, ".svg"))
|
|
return "image/svg+xml";
|
|
else if (hasSuffix(name, ".ico"))
|
|
return "image/svg+xml";
|
|
else if (hasSuffix(name, ".png"))
|
|
return "image/png";
|
|
else if (hasSuffix(name, ".js"))
|
|
return "text/javascript";
|
|
else if (hasSuffix(name, ".css"))
|
|
return "text/css";
|
|
return "text/plain";
|
|
}
|
|
|
|
void send_basic_info(int socket)
|
|
{
|
|
char *response = "HTTP/1.1 200 OK\r\n"
|
|
"Content-Type: application/json; charset=UTF-8\r\n\r\n"
|
|
"{\"ip_address\":\"192.168.10.247\",\"ip_gateway\":\"192.168.2.22\",\"ip_netmask\":\"255.255.255.0\",\"mac_address\":\"1c:2a:a3:23:00:02\",\"sw_ver\":\"" VERSION_SW "\",\"hw_ver\":\"SWGT024-V2.0\"}";
|
|
write(socket, response, strlen(response));
|
|
}
|
|
|
|
void send_vlan(int s, int vlan)
|
|
{
|
|
struct json_object *v;
|
|
const char *jstring;
|
|
char *header = "HTTP/1.1 200 OK\r\n"
|
|
"Content-Type: application/json; charset=UTF-8\r\n\r\n";
|
|
|
|
v = json_object_new_object();
|
|
|
|
sprintf(num_buff, "0x%08x", 0x00060011);
|
|
json_object_object_add(v, "members", json_object_new_string(num_buff));
|
|
|
|
write(s, header, strlen(header));
|
|
|
|
jstring = json_object_to_json_string_ext(v, JSON_C_TO_STRING_PLAIN);
|
|
write(s, jstring, strlen(jstring));
|
|
json_object_put(v);
|
|
}
|
|
|
|
|
|
void send_status(int s)
|
|
{
|
|
struct json_object *ports, *v;
|
|
const char *jstring;
|
|
char *header = "HTTP/1.1 200 OK\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
|
|
|
|
ports = json_object_new_array_ext(PORTS);
|
|
for (int i = 1; i <= PORTS; i++) {
|
|
v = json_object_new_object();
|
|
json_object_object_add(v, "portNum", json_object_new_int(i));
|
|
json_object_object_add(v, "logPort", json_object_new_int(physToLogPort[i-1]));
|
|
json_object_object_add(v, "isSFP", json_object_new_int(i <= PORTS - NSFP ? 0 : 1));
|
|
json_object_object_add(v, "enabled", json_object_new_int((i % 4) ? 1 : 0));
|
|
json_object_object_add(v, "link", json_object_new_int(i % 2 ? ((i == 1)? 5 : 2) : 0));
|
|
if (i % 2) {
|
|
uint64_t rate = (i == 1) ? 2400000000 : 950000000;
|
|
txG[i-1] += rate * (now - last_called);
|
|
rxG[i-1] += rate * (now - last_called);
|
|
txB[i-1] += rate * (now - last_called) / 10000000;
|
|
rxB[i-1] += rate * (now - last_called) / 10000000;
|
|
}
|
|
sprintf(txG_buff, "0x%016lx", txG[i-1]);
|
|
sprintf(txB_buff, "0x%016lx", txB[i-1]);
|
|
sprintf(rxG_buff, "0x%016lx", rxG[i-1]);
|
|
sprintf(rxB_buff, "0x%016lx", rxB[i-1]);
|
|
json_object_object_add(v, "txG", json_object_new_string(txG_buff));
|
|
json_object_object_add(v, "txB", json_object_new_string(txB_buff));
|
|
json_object_object_add(v, "rxG", json_object_new_string(rxG_buff));
|
|
json_object_object_add(v, "rxB", json_object_new_string(rxB_buff));
|
|
if (i >= PORTS - NSFP) {
|
|
uint16_t temp = 0x28fb + rand() / (RAND_MAX / 100);
|
|
uint16_t vcc = 0x7eda + rand() / (RAND_MAX / 100);
|
|
uint16_t txbias = 0x0d24 +rand() / (RAND_MAX / 100);
|
|
uint16_t txpower = 0x14bd + rand() / (RAND_MAX / 100);
|
|
uint16_t rxpower = 0;
|
|
uint16_t laser = 0;
|
|
uint16_t options = 0x68;
|
|
|
|
sprintf(sfp_options, "0x%02x", options);
|
|
sprintf(sfp_temp, "0x%04x", temp);
|
|
sprintf(sfp_vcc, "0x%04x", vcc);
|
|
sprintf(sfp_txbias, "0x%04x", txbias);
|
|
sprintf(sfp_txpower, "0x%04x", txpower);
|
|
sprintf(sfp_rxpower, "0x%04x", rxpower);
|
|
sprintf(sfp_laser, "0x%04x", laser);
|
|
json_object_object_add(v, "sfp_vendor", json_object_new_string("OEM"));
|
|
json_object_object_add(v, "sfp_model", json_object_new_string("10G-SFP+"));
|
|
json_object_object_add(v, "sfp_serial", json_object_new_string("12345678"));
|
|
json_object_object_add(v, "sfp_options", json_object_new_string(sfp_options));
|
|
json_object_object_add(v, "sfp_temp", json_object_new_string(sfp_temp));
|
|
json_object_object_add(v, "sfp_vcc", json_object_new_string(sfp_vcc));
|
|
json_object_object_add(v, "sfp_txbias", json_object_new_string(sfp_txbias));
|
|
json_object_object_add(v, "sfp_txpower", json_object_new_string(sfp_txpower));
|
|
json_object_object_add(v, "sfp_rxpower", json_object_new_string(sfp_rxpower));
|
|
json_object_object_add(v, "sfp_laser", json_object_new_string(sfp_laser));
|
|
} else {
|
|
if (i == 1)
|
|
json_object_object_add(v, "adv", json_object_new_string("100000"));
|
|
else if (i==2)
|
|
json_object_object_add(v, "adv", json_object_new_string("000011"));
|
|
else
|
|
json_object_object_add(v, "adv", json_object_new_string("000100"));
|
|
|
|
}
|
|
json_object_array_add(ports, v);
|
|
}
|
|
last_called = now;
|
|
|
|
write(s, header, strlen(header));
|
|
|
|
jstring = json_object_to_json_string_ext(ports, JSON_C_TO_STRING_PLAIN);
|
|
write(s, jstring, strlen(jstring));
|
|
json_object_put(v);
|
|
}
|
|
|
|
|
|
void send_counters(int s, int port)
|
|
{
|
|
struct json_object *counters;
|
|
const char *jstring;
|
|
char *header = "HTTP/1.1 200 OK\r\n"
|
|
"Cache-Control: no-cache\r\n"
|
|
"Content-Type: application/json; charset=UTF-8\r\n\r\n";
|
|
|
|
printf("Sending counters\n");
|
|
counters = json_object_new_array_ext(N_COUNTERS);
|
|
|
|
for (int i = 0; i < N_COUNTERS; i++) {
|
|
sprintf(counter_buf, "0x%016lx", 0x1234UL + i);
|
|
json_object_array_add(counters, json_object_new_string(counter_buf));
|
|
}
|
|
write(s, header, strlen(header));
|
|
jstring = json_object_to_json_string_ext(counters, JSON_C_TO_STRING_PLAIN);
|
|
write(s, jstring, strlen(jstring));
|
|
json_object_put(counters);
|
|
}
|
|
|
|
|
|
void send_eee(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";
|
|
|
|
ports = json_object_new_array_ext(PORTS);
|
|
for (int i = 1; i <= PORTS; i++) {
|
|
v = json_object_new_object();
|
|
json_object_object_add(v, "portNum", json_object_new_int(i));
|
|
json_object_object_add(v, "isSFP", json_object_new_int(i < 5 ? 0 : 1));
|
|
uint8_t eee = 0;
|
|
eee |= 0x02;
|
|
char eee_buf[20];
|
|
sprintf(eee_buf, "%08b", eee);
|
|
json_object_object_add(v, "eee", json_object_new_string(eee_buf));
|
|
uint8_t eee_lp = 0;
|
|
eee_lp |= 0x04;
|
|
sprintf(eee_buf, "%08b", eee_lp);
|
|
json_object_object_add(v, "eee_lp", json_object_new_string(eee_buf));
|
|
json_object_object_add(v, "active", json_object_new_int((i % 2) ? 1 : 0));
|
|
json_object_array_add(ports, v);
|
|
}
|
|
|
|
write(s, header, strlen(header));
|
|
|
|
jstring = json_object_to_json_string_ext(ports, JSON_C_TO_STRING_PLAIN);
|
|
write(s, jstring, strlen(jstring));
|
|
json_object_put(ports);
|
|
}
|
|
|
|
|
|
void send_bandwidth(int s)
|
|
{
|
|
struct json_object *ports, *v;
|
|
const char *jstring;
|
|
uint32_t bw;
|
|
int limited, fcEnabled;
|
|
char *header = "HTTP/1.1 200 OK\r\n"
|
|
"Content-Type: application/json; charset=UTF-8\r\n\r\n";
|
|
|
|
ports = json_object_new_array_ext(PORTS);
|
|
for (int i = 1; i <= PORTS; i++) {
|
|
v = json_object_new_object();
|
|
json_object_object_add(v, "portNum", json_object_new_int(i));
|
|
if (i % 2) {
|
|
limited = 1;
|
|
fcEnabled = i % 4 ? 1 : 0;
|
|
bw = 0x100;
|
|
} else {
|
|
limited = 0;
|
|
fcEnabled = 0;
|
|
bw = 0xfffff;
|
|
}
|
|
char bw_buf[20];
|
|
sprintf(bw_buf, "%08x", bw);
|
|
json_object_object_add(v, "iLimited", json_object_new_int(limited));
|
|
json_object_object_add(v, "iFC", json_object_new_int(fcEnabled));
|
|
json_object_object_add(v, "iBW", json_object_new_string(bw_buf));
|
|
|
|
if (i % 4) {
|
|
limited = 1;
|
|
bw = 0x1000;
|
|
} else {
|
|
limited = 0;
|
|
bw = 0xfffff;
|
|
}
|
|
sprintf(bw_buf, "%08x", bw);
|
|
json_object_object_add(v, "eLimited", json_object_new_int(limited));
|
|
json_object_object_add(v, "eBW", json_object_new_string(bw_buf));
|
|
|
|
json_object_array_add(ports, v);
|
|
}
|
|
|
|
write(s, header, strlen(header));
|
|
|
|
jstring = json_object_to_json_string_ext(ports, JSON_C_TO_STRING_PLAIN);
|
|
write(s, jstring, strlen(jstring));
|
|
json_object_put(ports);
|
|
}
|
|
|
|
|
|
void send_mirror(int s)
|
|
{
|
|
uint16_t mirror_tx, mirror_rx = 0;
|
|
char mirror_tx_buf[20];
|
|
char mirror_rx_buf[20];
|
|
struct json_object *mirror;
|
|
const char *jstring;
|
|
char *header = "HTTP/1.1 200 OK\r\n"
|
|
"Content-Type: application/json; charset=UTF-8\r\n\r\n";
|
|
|
|
mirror = json_object_new_object();
|
|
json_object_object_add(mirror, "mPort", json_object_new_int(1));
|
|
json_object_object_add(mirror, "enabled", json_object_new_int(1));
|
|
|
|
mirror_tx = 0b000110;
|
|
mirror_rx = 0b000010;
|
|
sprintf(mirror_tx_buf, "%016b", mirror_tx);
|
|
sprintf(mirror_rx_buf, "%016b", mirror_rx);
|
|
json_object_object_add(mirror, "mirror_tx", json_object_new_string(mirror_tx_buf));
|
|
json_object_object_add(mirror, "mirror_rx", json_object_new_string(mirror_rx_buf));
|
|
|
|
write(s, header, strlen(header));
|
|
|
|
jstring = json_object_to_json_string_ext(mirror, JSON_C_TO_STRING_PLAIN);
|
|
write(s, jstring, strlen(jstring));
|
|
json_object_put(mirror);
|
|
}
|
|
|
|
|
|
void send_l2(int s, int idx)
|
|
{
|
|
struct json_object *entries, *v;
|
|
const char *jstring;
|
|
char *header = "HTTP/1.1 200 OK\r\n"
|
|
"Content-Type: application/json; charset=UTF-8\r\n\r\n";
|
|
|
|
entries = json_object_new_array();
|
|
|
|
// We find the next entry to transfer based on the index
|
|
printf("Starting with idx %04x\n", idx);
|
|
int j = 0;
|
|
while (j < L2_ENTRY_NUM && l2_entries[j].idx < idx)
|
|
j++;
|
|
|
|
printf("First entry %d, idx: %04x\n", j, l2_entries[j].idx);
|
|
int transferred = 0;
|
|
while ((j < L2_ENTRY_NUM + 1) && (transferred < L2_MAX_TRANSFER)) {
|
|
// if (rand() / (RAND_MAX / 2) >= 1)
|
|
// continue;
|
|
int i = j % L2_ENTRY_NUM;
|
|
printf("Entry %d, idx: %04x, transferred %d\n", i, l2_entries[i].idx, transferred);
|
|
v = json_object_new_object();
|
|
json_object_object_add(v, "mac", json_object_new_string(l2_entries[i].mac));
|
|
json_object_object_add(v, "vlan", json_object_new_string(l2_entries[i].vlan));
|
|
if (l2_entries[i].type == 'l')
|
|
json_object_object_add(v, "type", json_object_new_string("l"));
|
|
else
|
|
json_object_object_add(v, "type", json_object_new_string("s"));
|
|
json_object_object_add(v, "port", json_object_new_int(l2_entries[i].port));
|
|
sprintf(l2_idx_buff, "%04x", l2_entries[i].idx);
|
|
json_object_object_add(v, "idx", json_object_new_string(l2_idx_buff));
|
|
json_object_array_add(entries, v);
|
|
j++;
|
|
transferred++;
|
|
printf("j %d, transferred %d\n", j, transferred);
|
|
}
|
|
|
|
write(s, header, strlen(header));
|
|
jstring = json_object_to_json_string_ext(entries, JSON_C_TO_STRING_PLAIN);
|
|
write(s, jstring, strlen(jstring));
|
|
json_object_put(entries);
|
|
}
|
|
|
|
|
|
void send_lag(int s)
|
|
{
|
|
char lag_buf[20];
|
|
struct json_object *lags = json_object_new_array_ext(4);
|
|
struct json_object *v;
|
|
const char *jstring;
|
|
char *header = "HTTP/1.1 200 OK\r\n"
|
|
"Content-Type: application/json; charset=UTF-8\r\n\r\n";
|
|
|
|
for (int i = 0; i < 4; i++) {
|
|
v = json_object_new_object();
|
|
json_object_object_add(v, "lagNum", json_object_new_int(i));
|
|
sprintf(lag_buf, "%016b", 0x30 << (i * 2) & 0x1f8);
|
|
json_object_object_add(v, "members", json_object_new_string(lag_buf));
|
|
json_object_object_add(v, "hash", json_object_new_string("0x7e"));
|
|
json_object_array_add(lags, v);
|
|
}
|
|
write(s, header, strlen(header));
|
|
|
|
jstring = json_object_to_json_string_ext(lags, JSON_C_TO_STRING_PLAIN);
|
|
write(s, jstring, strlen(jstring));
|
|
json_object_put(lags);
|
|
}
|
|
|
|
|
|
void send_mtu(int s)
|
|
{
|
|
struct json_object *mtus, *v;
|
|
const char *jstring;
|
|
char *header = "HTTP/1.1 200 OK\r\n"
|
|
"Content-Type: application/json; charset=UTF-8\r\n\r\n";
|
|
|
|
mtus = json_object_new_array_ext(PORTS);
|
|
char mtu[8];
|
|
for (int i = 1; i <= PORTS; i++) {
|
|
v = json_object_new_object();
|
|
json_object_object_add(v, "portNum", json_object_new_int(i));
|
|
sprintf(mtu, "0x%04x", i % 2 ? 16383 : 1522);
|
|
json_object_object_add(v, "mtu", json_object_new_string(mtu));
|
|
json_object_array_add(mtus, v);
|
|
}
|
|
write(s, header, strlen(header));
|
|
|
|
jstring = json_object_to_json_string_ext(mtus, JSON_C_TO_STRING_PLAIN);
|
|
write(s, jstring, strlen(jstring));
|
|
json_object_put(mtus);
|
|
}
|
|
|
|
|
|
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;
|
|
|
|
server.domain = AF_INET;
|
|
server.service = SOCK_STREAM;
|
|
server.port = port;
|
|
server.protocol = 0;
|
|
server.backlog = 10;
|
|
|
|
server.address.sin_family = server.domain;
|
|
server.address.sin_port = htons(port);
|
|
server.address.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
|
|
server.socket = socket(server.domain, server.service, server.protocol);
|
|
if (server.socket < 0) {
|
|
perror("Failed to initialize/connect to socket...\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
int reuse = 1;
|
|
if (setsockopt(server.socket, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
|
|
perror("setsockopt(SO_REUSEADDR) failed");
|
|
|
|
if (bind(server.socket, (struct sockaddr*)&server.address, sizeof(server.address)) < 0) {
|
|
perror("Failed to bind socket...\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
if (listen(server.socket, server.backlog) < 0) {
|
|
perror("Failed to start listening...\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
server.launch = launch;
|
|
return server;
|
|
}
|
|
|
|
void send_not_found(int socket) {
|
|
char *response = "HTTP/1.1 404 Not found\r\n"
|
|
"Content-Type: text/html\r\n\r\n"
|
|
"<!DOCTYPE html> <html><head><title>Not Found</title></head>"
|
|
"<body><h1>Not found!</h1></html>";
|
|
write(socket, response, strlen(response));
|
|
}
|
|
|
|
|
|
void send_bad_request(int socket) {
|
|
char *response = "HTTP/1.1 400 Bad Request\r\n"
|
|
"Content-Type: text/html\r\n\r\n"
|
|
"<!DOCTYPE html> <html><head><title>Bad Request</title></head>"
|
|
"<body><h1>Bad Request!</h1></html>";
|
|
write(socket, response, strlen(response));
|
|
}
|
|
|
|
|
|
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 (is_word(p, "\nContent-Type:"))
|
|
content_type = p + 15;
|
|
else if (is_word(p, "\nCookie:")) {
|
|
session = p + 9;
|
|
while (*session == ' ') session++;
|
|
const char *s = strstr(session, "session=");
|
|
if (s) session = s + 8;
|
|
}
|
|
}
|
|
if (content_type && is_word(content_type, "multipart/form-data; boundary")) {
|
|
printf("Found multiplart\n");
|
|
content_type += 30;
|
|
uint8_t i = 0;
|
|
while (content_type[i] != '\r' && content_type[i] != '\n') {
|
|
boundary[i + 2] = content_type[i];
|
|
i++;
|
|
}
|
|
// The boundary between parts is "--" + the boundary given in the header
|
|
boundary[0] = '-';
|
|
boundary[1] = '-';
|
|
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;
|
|
}
|
|
|
|
|
|
char *skip_boundary(char *p)
|
|
{
|
|
while (*p) {
|
|
if (is_word(p, boundary))
|
|
return p + strlen(boundary);
|
|
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)
|
|
{
|
|
char buffer[BUFFER_SIZE];
|
|
FILE *inptr;
|
|
|
|
last_called = time(NULL);
|
|
for (int i=0; i < PORTS; i++)
|
|
txG[i] = txB[i] = rxG[i] = rxB[i] = 0;
|
|
|
|
while (1) {
|
|
printf("=== Waiting for connection on port %d === \n", server->port);
|
|
int addrlen = sizeof(server->address);
|
|
int new_socket = accept(server->socket, (struct sockaddr*)&server->address, (socklen_t*)&addrlen);
|
|
ssize_t bytesRead = read(new_socket, buffer, BUFFER_SIZE - 1);
|
|
printf("bytesRead: %ld\n", bytesRead);
|
|
int filesize = 0;
|
|
char *mime;
|
|
|
|
if (bytesRead > 0) {
|
|
buffer[bytesRead] = '\0'; // Null terminate the string
|
|
puts(buffer);
|
|
|
|
if (is_word(buffer, "GET")) {
|
|
scan_header(buffer);
|
|
if (!strncmp(&buffer[4], "/status.json", 12)) {
|
|
printf("Status request\n");
|
|
if (!authenticated)
|
|
send_unauthorized(new_socket);
|
|
else
|
|
send_status(new_socket);
|
|
goto done;
|
|
} else if (!strncmp(&buffer[4], "/eee.json", 9)) {
|
|
printf("EEE request\n");
|
|
if (!authenticated)
|
|
send_unauthorized(new_socket);
|
|
else
|
|
send_eee(new_socket);
|
|
goto done;
|
|
} else if (!strncmp(&buffer[4], "/bandwidth.json", 15)) {
|
|
printf("Bandwidth request\n");
|
|
if (!authenticated)
|
|
send_unauthorized(new_socket);
|
|
else
|
|
send_bandwidth(new_socket);
|
|
goto done;
|
|
} else if (!strncmp(&buffer[4], "/information.json", 17)) {
|
|
printf("Status request\n");
|
|
if (!authenticated)
|
|
send_unauthorized(new_socket);
|
|
else
|
|
send_basic_info(new_socket);
|
|
goto done;
|
|
} else if (!strncmp(&buffer[4], "/mirror.json", 12)) {
|
|
printf("Mirror request\n");
|
|
if (!authenticated)
|
|
send_unauthorized(new_socket);
|
|
else
|
|
send_mirror(new_socket);
|
|
goto done;
|
|
} else if (!strncmp(&buffer[4], "/lag.json", 9)) {
|
|
printf("LAG request\n");
|
|
if (!authenticated)
|
|
send_unauthorized(new_socket);
|
|
else
|
|
send_lag(new_socket);
|
|
goto done;
|
|
} else if (!strncmp(&buffer[4], "/l2.json?idx=", 13)) {
|
|
int idx = atoi(&buffer[17]);
|
|
printf("L2 request\n");
|
|
if (!authenticated)
|
|
send_unauthorized(new_socket);
|
|
else
|
|
send_l2(new_socket, idx);
|
|
goto done;
|
|
} else if (!strncmp(&buffer[4], "/mtu.json", 9)) {
|
|
printf("MTU request\n");
|
|
if (!authenticated)
|
|
send_unauthorized(new_socket);
|
|
else
|
|
send_mtu(new_socket);
|
|
goto done;
|
|
} else if (!strncmp(&buffer[4], "/vlan.json?vid=", 15)) {
|
|
int vlan = atoi(&buffer[19]);
|
|
printf("VLAN request for %d\n", 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;
|
|
} else if (!strncmp(&buffer[4], "/counters.json?port=", 20)) {
|
|
int port = atoi(&buffer[24]);
|
|
printf("Counters request for %d\n", port);
|
|
if (!authenticated)
|
|
send_unauthorized(new_socket);
|
|
else
|
|
send_counters(new_socket, port);
|
|
goto done;
|
|
}
|
|
if (!authenticated && !(!strncmp(&buffer[4], "/login.html", 11) || !strncmp(&buffer[4], "/favicon.ico", 12))) {
|
|
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++;
|
|
buffer[4+i] = '\0';
|
|
|
|
printf("Serving file: >%s<, name length %d\n", &buffer[5], i);
|
|
if (i > 1)
|
|
inptr = fopen(&buffer[5], "rb");
|
|
else
|
|
inptr = fopen("/index.html", "rb");
|
|
if (inptr == NULL) {
|
|
printf("Cannot open input file %s\n", &buffer[5]);
|
|
send_not_found(new_socket);
|
|
goto done;
|
|
}
|
|
|
|
mime = getMime(&buffer[5]);
|
|
printf("MIME type: %s\n", mime);
|
|
|
|
fseek(inptr, 0L, SEEK_END);
|
|
filesize = ftell(inptr);
|
|
printf("Filesize: %d\n", filesize);
|
|
rewind(inptr);
|
|
printf("Input file size: %d\n", filesize);
|
|
if (filesize > BUFFER_SIZE) {
|
|
printf("File too large.\n");
|
|
goto done;
|
|
}
|
|
size_t bytes_read = fread(buffer, 1, sizeof(buffer), inptr);
|
|
|
|
printf("Bytes read: %ld\n", bytes_read);
|
|
|
|
if (bytes_read != filesize) {
|
|
printf("Error reading input file.\n");
|
|
goto done;
|
|
}
|
|
fclose(inptr);
|
|
} else if (is_word(buffer, "POST")) {
|
|
printf("POST request\n");
|
|
// Find end of request header
|
|
char *p = buffer;
|
|
boundary[0] ='\0';
|
|
p = scan_header(p);
|
|
printf("Boundary: >%s<\n", boundary);
|
|
if (!*p || !content_type) {
|
|
printf("Bad request, no content type!\n");
|
|
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], "/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"
|
|
"<!DOCTYPE html> <html><head><title>Upload OK</title></head>"
|
|
"<body><h1>Command executed successully</h1></html>";
|
|
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 "; Path=/; SameSite=Strict\r\n"
|
|
"\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);
|
|
goto done;
|
|
}
|
|
// We skip the intial parts as part of the header
|
|
do {
|
|
p = skip_boundary(p);
|
|
if (!*p)
|
|
goto bad_request;
|
|
p = scan_header(p);
|
|
if (!*p || !content_type)
|
|
goto bad_request;
|
|
} 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;
|
|
do {
|
|
if (bptr >= bytesRead) {
|
|
bptr = 0;
|
|
bytesRead = read(new_socket, buffer, BUFFER_SIZE - 1);
|
|
printf("bytesRead: %ld\n", bytesRead);
|
|
if (!bytesRead)
|
|
break;
|
|
}
|
|
if (!boundary[bindex])
|
|
break;
|
|
if (buffer[bptr] == boundary[bindex]) {
|
|
bptr++;
|
|
bindex++;
|
|
} else {
|
|
for (int i = 0; i < bindex; i++)
|
|
*uptr++ = boundary[i];
|
|
*uptr++ = buffer[bptr++];
|
|
bindex = 0;
|
|
}
|
|
} while(1);
|
|
printf("Done reading\n");
|
|
printf("%s", upload_buffer);
|
|
if (!bindex || boundary[bindex])
|
|
goto bad_request;
|
|
char *response = "HTTP/1.1 200 OK\r\n"
|
|
"Content-Type: text/html\r\n\r\n"
|
|
"<!DOCTYPE html> <html><head><title>Upload OK</title></head>"
|
|
"<body><h1>File uploaded successully</h1></html>";
|
|
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"
|
|
"Cache-Control: max-age=60, must-revalidate\r\n"
|
|
"Content-Type: ";
|
|
write(new_socket, response, strlen(response));
|
|
|
|
write(new_socket, mime, strlen(mime));
|
|
response = "; charset=UTF-8\r\n\r\n";
|
|
write(new_socket, response, strlen(response));
|
|
if (filesize)
|
|
write(new_socket, buffer, filesize);
|
|
} else if (bytesRead == 0) {
|
|
printf("EOF\n");
|
|
continue;
|
|
} else {
|
|
perror("Error reading buffer, nothing read...\n");
|
|
}
|
|
|
|
done:
|
|
close(new_socket);
|
|
continue;
|
|
bad_request:
|
|
printf("Bad request!\n");
|
|
send_bad_request(new_socket);
|
|
close(new_socket);
|
|
}
|
|
}
|
|
|
|
|
|
int main()
|
|
{
|
|
// Make sure we can handle writes to a dead client without a signal handler
|
|
signal(SIGPIPE, SIG_IGN);
|
|
|
|
struct Server server = serverConstructor(8080, launch);
|
|
server.launch(&server);
|
|
|
|
return 0;
|
|
}
|