Merge pull request 'develop' (#1) from develop into main
Prepare Build and Testing executable binary / Prepare-Build-Testing-With-Make (push) Successful in 5s
Prepare Build and Testing executable binary / Prepare-Build-Testing-With-CMake (push) Successful in 8s
Prepare Build and Testing executable binary / Prepare-Build-Testing-With-Meson (push) Successful in 7s

Reviewed-on: https://gitea.home.com:3000/TolaMironcenko/dns_service_c/pulls/1
This commit was merged in pull request #1.
This commit is contained in:
wt
2025-09-10 15:35:22 +07:00
14 changed files with 711 additions and 136 deletions
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024 DNS Service Contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+292
View File
@@ -0,0 +1,292 @@
# DNS Service
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
A lightweight DNS management service written in C with a web-based interface for configuring DNS settings and managing dnsmasq configuration.
## Features
- **Web Interface**: Modern responsive web UI for DNS configuration
- **DNS Settings Management**: Configure upstream DNS servers, local domains, and cache settings
- **DHCP Configuration**: Enable/disable DHCP with range configuration
- **File Management**: Direct editing of configuration files
- **SSL/TLS Support**: Optional HTTPS with certificate configuration
- **JSON Configuration**: Easy-to-manage JSON-based configuration
- **Cross-platform**: Supports multiple build systems (Make, CMake, Meson)
## Architecture
The service consists of:
- **HTTP/HTTPS Server**: Built on Mongoose web server
- **REST API**: JSON-based API for configuration management
- **Web Interface**: Bootstrap-based responsive UI
- **Configuration Parser**: Custom dnsmasq config parser
- **File Operations**: Safe file reading/writing utilities
- **Settings Management**: Direct manipulation of dnsmasq configuration files
- Updates dnsmasq main configuration (`/etc/dnsmasq.conf`)
- Manages hosts file (`/etc/dnsmasq.conf.d/hosts`)
- Manages resolv.conf (`/etc/dnsmasq.conf.d/resolv.conf`)
- Automatically restarts dnsmasq service after configuration changes
## API Endpoints
| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/api/settings` | Get current DNS settings |
| POST | `/api/settings` | Update DNS settings (cache_size, resolv servers, hosts) and restart dnsmasq |
| GET | `/api/settings/file` | Get raw configuration file |
| POST | `/api/settings/file` | Update configuration file |
| GET | `/#` | Serve web interface |
## Requirements
### Build Dependencies
- GCC compiler with C23 support
- OpenSSL development libraries
- pthread library
### System Dependencies
- dnsmasq (for DNS service functionality)
- SSL certificates (for HTTPS support)
- System initialization service (systemd, openrc, rc, finit, or init)
- Appropriate privileges to restart system services
## Installation
### Using Make
```bash
make
```
### Using CMake
```bash
mkdir build && cd build
cmake ..
make
```
### Using Meson
```bash
meson setup builddir
meson compile -C builddir
```
## Configuration
Edit `config.json` to configure the service:
```json
{
"address": "0.0.0.0:8321",
"tls_address": "0.0.0.0:8322",
"web": "./web",
"tls_enabled": true,
"tls_cert": "/path/to/certificate.crt",
"tls_key": "/path/to/private.key",
"system_initialization": "systemd",
"dnsmasq_config": "/etc/dnsmasq.conf"
}
```
### Configuration Parameters
- `address`: HTTP server bind address and port
- `tls_address`: HTTPS server bind address and port
- `web`: Path to web interface files
- `tls_enabled`: Enable/disable HTTPS support
- `tls_cert`: Path to SSL certificate file
- `tls_key`: Path to SSL private key file
- `system_initialization`: System initialization type for restarting dnsmasq service
- `"systemd"`: Uses `systemctl restart dnsmasq`
- `"rc"` or `"openrc"`: Uses `rc restart dnsmasq`
- `"finit"`: Uses `initctl restart dnsmasq`
- `"init"`: Uses `/etc/init.d/dnsmasq restart`
- `dnsmasq_config`: Path to dnsmasq configuration file
## Usage
### Starting the Service
```bash
./dns_service
```
The service will start HTTP server on the configured address (default: `0.0.0.0:8321`) and optionally HTTPS server if TLS is enabled.
### Web Interface
Navigate to `http://localhost:8321` (or your configured address) to access the web interface where you can:
- Configure upstream DNS servers (resolv)
- Manage local domain mappings (hosts)
- Enable/disable DHCP with range settings
- Adjust DNS cache size
- Edit configuration files directly
- Save settings directly to dnsmasq configuration
- Automatic dnsmasq service restart after configuration changes
### API Usage
#### Get Current Settings
```bash
curl -X GET http://localhost:8321/api/settings
```
#### Update Settings
```bash
curl -X POST http://localhost:8321/api/settings \
-H "Content-Type: application/json" \
-d '{
"cache_size": 1000,
"resolv": ["8.8.8.8", "8.8.4.4"],
"hosts": {
"192.168.1.1": "router.local",
"192.168.1.100": ["server1.local", "server1"]
}
}'
```
The POST `/api/settings` endpoint accepts the following parameters:
- `cache_size` (integer): DNS cache size for dnsmasq
- `resolv` (array of strings): Upstream DNS servers
- `hosts` (object): Local DNS mappings where keys are IP addresses and values are either single hostnames (string) or arrays of hostnames
**Note**: After successfully updating the configuration files, the service automatically restarts dnsmasq to apply the changes. The restart method depends on the `system_initialization` setting in `config.json`.
#### Settings Configuration Details
**Cache Size (`cache_size`)**
- Configures the DNS cache size in dnsmasq
- Updates the `cache-size` parameter in `/etc/dnsmasq.conf`
- Accepts integer values (recommended: 150-10000 depending on system resources)
**Upstream DNS Servers (`resolv`)**
- Array of DNS server IP addresses to use for upstream resolution
- Creates/updates `/etc/dnsmasq.conf.d/resolv.conf` with `nameserver` entries
- Example: `["8.8.8.8", "1.1.1.1", "8.8.4.4"]`
**Local Hosts (`hosts`)**
- Object mapping IP addresses to hostnames for local DNS resolution
- Creates/updates `/etc/dnsmasq.conf.d/hosts` file
- Supports both single hostnames and multiple hostnames per IP
- Format:
- Single hostname: `"192.168.1.1": "router.local"`
- Multiple hostnames: `"192.168.1.100": ["server1.local", "server1"]`
## Development
### Project Structure
```
dns_service_c/
├── src/
│ ├── lib/ # Third-party libraries
│ │ ├── mongoose.c # HTTP server
│ │ ├── cJSON.c # JSON parser
│ │ └── ...
│ ├── main.c # Application entry point
│ ├── config.c # Configuration management
│ ├── settings.c # DNS settings handlers (GET)
│ ├── set_settings.c # DNS settings handlers (POST)
│ ├── set_dnsmasq_settings_field.c # Update dnsmasq config fields
│ ├── set_hosts.c # Manage hosts file
│ ├── set_resolvs.c # Manage resolv.conf
│ ├── restart_dnsmasq.c # Restart dnsmasq service
│ ├── settings.h # Settings function declarations
│ ├── restart_dnsmasq.h # Restart function declarations
│ └── ...
├── web/ # Web interface files
│ ├── index.html
│ ├── css/
│ └── js/
├── config.json # Service configuration
└── README.md
```
### Building for Development
```bash
# With debug symbols
make CFLAGS="-Wall -g -std=gnu2x -DDEBUG"
# Clean build
make clean && make
```
### Code Style
The project uses `.clang-format` for consistent code formatting:
```bash
clang-format -i src/*.c src/*.h
```
## Security Considerations
- The service runs with system privileges to modify DNS configuration files
- HTTPS is strongly recommended for production deployments
- Ensure proper file permissions on certificate files
- The service directly modifies system files (`/etc/dnsmasq.conf`, `/etc/dnsmasq.conf.d/hosts`, `/etc/dnsmasq.conf.d/resolv.conf`)
- Consider firewall rules to restrict access to management interface
- Validate all input data to prevent configuration file corruption
- Backup configuration files before making changes
- Ensure the service has appropriate write permissions to dnsmasq configuration directories
- The service requires privileges to restart the dnsmasq service via system commands
## Troubleshooting
### Common Issues
1. **Permission denied when accessing dnsmasq config**
- Ensure the service has read/write permissions to the dnsmasq configuration file
- Run with appropriate privileges or adjust file permissions
- Check permissions for `/etc/dnsmasq.conf.d/` directory
3. **Failed to write settings**
- Verify write permissions to `/etc/dnsmasq.conf`, `/etc/dnsmasq.conf.d/hosts`, and `/etc/dnsmasq.conf.d/resolv.conf`
- Ensure the `/etc/dnsmasq.conf.d/` directory exists
- Check disk space availability
4. **Failed to restart dnsmasq**
- Ensure the service has appropriate privileges to restart system services
- Verify the `system_initialization` parameter in `config.json` matches your system
- Supported initialization systems: rc, openrc, systemd, finit, init
- Check that dnsmasq service is properly installed and configured on the system
5. **SSL/TLS certificate errors**
- Verify certificate and key file paths in `config.json`
- Ensure certificate files have correct permissions
- Check certificate validity and format
6. **Port binding errors**
- Verify ports are not already in use
- Check firewall settings
- Ensure proper privileges for binding to privileged ports
7. **Settings not persisting after dnsmasq restart**
- Ensure dnsmasq is configured to read from `/etc/dnsmasq.conf.d/` directory
- Verify the main dnsmasq configuration includes the conf-dir directive
- Check that temporary files are properly cleaned up after configuration updates
### Logging
The service uses Mongoose's built-in logging. Set log level in code or use debug build for verbose output.
## Contributing
1. Fork the repository
2. Create a feature branch
3. Make your changes following the existing code style
4. Test your changes thoroughly
5. Submit a pull request
## License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
### Third-party Libraries
This project includes the following third-party libraries:
- **Mongoose**: HTTP/WebSocket server library
- **cJSON**: JSON parser library
Please refer to their respective licenses for usage terms.
## Support
For issues and questions, please check the project's issue tracker or documentation.
+8 -8
View File
@@ -1,10 +1,10 @@
{
"address": "0.0.0.0:8321",
"tls_address": "0.0.0.0:8322",
"web": "./web",
"tls_enabled": true,
"tls_cert": "/home/tola/certs/localhost/localhost.crt",
"tls_key": "/home/tola/certs/localhost/localhost.key",
"system_initialization": "rc",
"dnsmasq_config": "/etc/dnsmasq.conf"
"address": "0.0.0.0:8321",
"tls_address": "0.0.0.0:8322",
"web": "./web",
"tls_enabled": true,
"tls_cert": "/home/tola/certs/localhost/localhost.crt",
"tls_key": "/home/tola/certs/localhost/localhost.key",
"system_initialization": "systemd",
"dnsmasq_config": "/etc/dnsmasq.conf"
}
+4
View File
@@ -17,6 +17,10 @@ executable(
'src/set_settings_file.c',
'src/get_settings_file.c',
'src/static.c',
'src/set_hosts.c',
'src/set_resolvs.c',
'src/set_dnsmasq_settings_field.c',
'src/restart_dnsmasq.c',
include_directories: include_dirs,
dependencies: deps,
)
-1
View File
@@ -43,7 +43,6 @@ char *write_file(const char *path, const char *data) {
perror("Error opening file");
return nullptr;
}
printf("%s\n%s\n", data, path);
if (fprintf(fp, "%s", data) < 0) {
perror("Error write file");
return nullptr;
+32
View File
@@ -0,0 +1,32 @@
#include "restart_dnsmasq.h"
#include "ConfigManager.h"
#include "config.h"
#include <string.h>
#include <stdlib.h>
char *restart_dnsmasq() {
int res = -2;
char *system_initialization = cm_get_parameter_as_string(config, "system_initialization");
if (!system_initialization) {
res = -1;
}
if (!strcmp(system_initialization, "rc")) {
res = system("rc restart dnsmasq");
}
if (!strcmp(system_initialization, "openrc")) {
res = system("rc restart dnsmasq");
}
if (!strcmp(system_initialization, "systemd")) {
res = system("systemctl restart dnsmasq");
}
if (!strcmp(system_initialization, "finit")) {
res = system("initctl restart dnsmasq");
}
if (!strcmp(system_initialization, "init")) {
res = system("/etc/init.d/dnsmasq restart");
}
if (res != 0) {
return strdup("Failed to restart dnsmasq");
}
return nullptr;
}
+3
View File
@@ -0,0 +1,3 @@
#pragma once
char *restart_dnsmasq();
+44
View File
@@ -0,0 +1,44 @@
#include "config.h"
#include "settings.h"
#define DNSMASQ_CONFIG_FILE_TMP "dnsmasq.conf.tmp"
char *set_dnsmasq_settings_field(const char *key, cJSON *setting) {
char *filename = cm_get_parameter_as_string(config, "dnsmasq_config");
if (!filename) {
return "Failed to read config file";
}
FILE *dnsmasq_config_file = fopen(filename, "r");
FILE *dnsmasq_config_file_tmp = fopen(DNSMASQ_CONFIG_FILE_TMP, "w");
if (dnsmasq_config_file == nullptr || dnsmasq_config_file_tmp == nullptr) {
return "Failed to open config file";
}
char line[256];
char *new_str = malloc(1024);
snprintf(new_str, 1024, "%s=%d", key, setting->valueint);
while (fgets(line, sizeof(line), dnsmasq_config_file)) {
if (strstr(line, key) != nullptr || strcmp(line, key) == 0) {
fputs(new_str, dnsmasq_config_file_tmp);
fputs("\n", dnsmasq_config_file_tmp);
} else {
fputs(line, dnsmasq_config_file_tmp);
}
}
fclose(dnsmasq_config_file);
fclose(dnsmasq_config_file_tmp);
free(new_str);
if (remove(filename) != 0) {
remove(DNSMASQ_CONFIG_FILE_TMP);
return "Failed to remove config file";
}
if (rename(DNSMASQ_CONFIG_FILE_TMP, filename) != 0) {
remove(DNSMASQ_CONFIG_FILE_TMP);
return "Failed to rename config file";
}
return nullptr;
}
+44
View File
@@ -0,0 +1,44 @@
#include "settings.h"
#define DNSMASQ_HOSTS_FILE_TMP "hosts.tmp"
char *set_hosts(cJSON *hosts) {
FILE *hosts_file_tmp = fopen(DNSMASQ_HOSTS_FILE_TMP, "w");
if (!hosts_file_tmp) {
return "can't open hosts file";
}
cJSON *host = hosts->child;
while (host) {
if (!cJSON_IsString(host) && !cJSON_IsArray(host)) {
remove(DNSMASQ_HOSTS_FILE_TMP);
return "Invalid hosts";
}
if (cJSON_IsString(host)) {
fprintf(hosts_file_tmp, "%s %s\n", host->string, host->valuestring);
}
if (cJSON_IsArray(host)) {
int arrsize = cJSON_GetArraySize(host);
if (arrsize > 0) {
fprintf(hosts_file_tmp, "%s ", host->string);
}
for (int i = 0; i < arrsize; i++) {
cJSON *domain = cJSON_GetArrayItem(host, i);
if (domain && cJSON_IsString(domain)) {
fprintf(hosts_file_tmp, "%s ", domain->valuestring);
}
}
fprintf(hosts_file_tmp, "\n");
}
host = host->next;
}
fclose(hosts_file_tmp);
if (rename(DNSMASQ_HOSTS_FILE_TMP, "/etc/dnsmasq.conf.d/hosts") != 0) {
remove(DNSMASQ_HOSTS_FILE_TMP);
return "Failed to rename hosts file";
}
remove(DNSMASQ_HOSTS_FILE_TMP);
return nullptr;
}
+29
View File
@@ -0,0 +1,29 @@
#include "settings.h"
#define DNSMASQ_RESOLV_FILE_TMP "resolv.conf.tmp"
char *set_resolvs(cJSON *resolvs) {
FILE *resolvs_file_tmp = fopen(DNSMASQ_RESOLV_FILE_TMP, "w");
if (!resolvs_file_tmp) {
return "Failed to open resolv file";
}
cJSON *resolv = resolvs->child;
while (resolv) {
if (!cJSON_IsString(resolv)) {
remove(DNSMASQ_RESOLV_FILE_TMP);
return "Invalid resolv";
}
fprintf(resolvs_file_tmp, "nameserver %s\n", resolv->valuestring);
resolv = resolv->next;
}
fclose(resolvs_file_tmp);
if (rename(DNSMASQ_RESOLV_FILE_TMP, "/etc/dnsmasq.conf.d/resolv.conf") != 0) {
remove(DNSMASQ_RESOLV_FILE_TMP);
return "Failed to rename resolv file";
}
remove(DNSMASQ_RESOLV_FILE_TMP);
return nullptr;
}
+67
View File
@@ -1,4 +1,71 @@
#include "settings.h"
#include "restart_dnsmasq.h"
void set_settings(struct mg_connection *c, struct mg_http_message *hm) {
cJSON *jsonbody = cJSON_Parse(hm->body.buf);
if (!jsonbody) {
mg_http_reply(c, 500, HTTP_HEADERS, "{%m:%m}", MG_ESC("error"),
MG_ESC("can't parse JSON body"));
return;
}
cJSON *cache_size = cJSON_GetObjectItemCaseSensitive(jsonbody, "cache_size");
if (!cache_size || !cJSON_IsNumber(cache_size)) {
mg_http_reply(c, 500, HTTP_HEADERS, "{%m:%m}", MG_ESC("error"),
MG_ESC("invalid cache_size"));
cJSON_Delete(jsonbody);
return;
}
char *result_set_dnsmasq_settings_field =
set_dnsmasq_settings_field("cache-size", cache_size);
if (result_set_dnsmasq_settings_field) {
mg_http_reply(c, 200, HTTP_HEADERS, "{%m:%m}", MG_ESC("error"),
MG_ESC(result_set_dnsmasq_settings_field));
cJSON_Delete(jsonbody);
return;
}
cJSON *resolvs = cJSON_GetObjectItemCaseSensitive(jsonbody, "resolv");
if (!resolvs || !cJSON_IsArray(resolvs)) {
mg_http_reply(c, 500, HTTP_HEADERS, "{%m:%m}", MG_ESC("error"),
MG_ESC("invalid resolv"));
cJSON_Delete(jsonbody);
return;
}
char *result_set_resolvs = set_resolvs(resolvs);
if (result_set_resolvs) {
mg_http_reply(c, 500, HTTP_HEADERS, "{%m:%m}", MG_ESC("error"),
MG_ESC(result_set_resolvs));
cJSON_Delete(jsonbody);
return;
}
cJSON *hosts = cJSON_GetObjectItemCaseSensitive(jsonbody, "hosts");
if (!hosts || !cJSON_IsObject(hosts)) {
mg_http_reply(c, 500, HTTP_HEADERS, "{%m:%m}", MG_ESC("error"),
MG_ESC("invalid hosts"));
cJSON_Delete(jsonbody);
return;
}
char *result_set_hosts = set_hosts(hosts);
if (result_set_hosts) {
mg_http_reply(c, 500, HTTP_HEADERS, "{%m:%m}", MG_ESC("error"),
MG_ESC(result_set_hosts));
cJSON_Delete(jsonbody);
return;
}
char *error = restart_dnsmasq();
if (error) {
mg_http_reply(c, 500, HTTP_HEADERS, "{%m:%m}", MG_ESC("error"),
MG_ESC(error));
free(error);
return;
}
mg_http_reply(c, 200, HTTP_HEADERS, "%s", hm->body.buf);
cJSON_Delete(jsonbody);
}
+40 -9
View File
@@ -1,21 +1,52 @@
#include "settings_file.h"
#include <file.h>
#include "cJSON.h"
#include "config.h"
#include "restart_dnsmasq.h"
#include "settings_file.h"
void set_settings_file(struct mg_connection *c, struct mg_http_message *hm) {
char *filename = cm_get_parameter_as_string(config, "dnsmasq_config");
if (!filename) {
mg_http_reply(c, 500, HTTP_HEADERS, "{%m:%m}", MG_ESC("error"),
MG_ESC("can't read config file"));
return;
mg_http_reply(c, 500, HTTP_HEADERS, "{%m:%m}", MG_ESC("error"),
MG_ESC("can't read config file"));
return;
}
char *res = write_file(cm_get_parameter_as_string(config, "dnsmasq_config"), hm->body.buf);
char *res = write_file(cm_get_parameter_as_string(config, "dnsmasq_config"),
hm->body.buf);
if (!res) {
mg_http_reply(c, 500, HTTP_HEADERS, "{%m:%m}", MG_ESC("error"),
MG_ESC("can't write file"));
return;
mg_http_reply(c, 500, HTTP_HEADERS, "{%m:%m}", MG_ESC("error"),
MG_ESC("can't write file"));
return;
}
mg_http_reply(c, 200, "Content-Type: text/plain\r\nAccess-Control-Allow-Origin: *\r\n", res);
char *error = restart_dnsmasq();
if (error) {
mg_http_reply(c, 500, HTTP_HEADERS, "{%m:%m}", MG_ESC("error"),
MG_ESC(error));
free(error);
return;
}
cJSON *response = cJSON_CreateObject();
if (!response) {
mg_http_reply(c, 500, HTTP_HEADERS, "{%m:%m}", MG_ESC("error"),
MG_ESC("can't create response"));
free(filename);
free(res);
return;
}
if (!cJSON_AddStringToObject(response, "data", res)) {
mg_http_reply(c, 500, HTTP_HEADERS, "{%m:%m}", MG_ESC("error"),
MG_ESC("can't add data to response"));
cJSON_Delete(response);
free(filename);
free(res);
return;
}
char *response_str = cJSON_PrintUnformatted(response);
mg_http_reply(
c, 200, "Content-Type: text/plain\r\nAccess-Control-Allow-Origin: *\r\n",
response_str);
cJSON_Delete(response);
free(response_str);
free(filename);
free(res);
}
+3
View File
@@ -11,3 +11,6 @@ void set_settings(struct mg_connection *c, struct mg_http_message *hm);
cJSON *decode_dnsmasq_settings(char *input, char *pair_delim, char *kv_delim);
char **decode_resolvs(char *input, int *count);
cJSON *decode_hosts(const char *input);
char *set_hosts(cJSON* hosts);
char *set_resolvs(cJSON* resolvs);
char *set_dnsmasq_settings_field(const char *key, cJSON *setting);
+124 -118
View File
@@ -1,207 +1,213 @@
const upstream_dns_servers_table_body = document.querySelector(
"#upstream-dns-servers-table-body",
"#upstream-dns-servers-table-body",
);
const local_domains_table_body = document.querySelector(
"#local-domains-table-body",
"#local-domains-table-body",
);
const cache_size_input = document.querySelector("#cache-size-input");
const save_settings_button = document.querySelector("#save-settings-button");
const add_dns_server_button = document.querySelector("#add_dns_server_button");
const add_local_domain_button = document.querySelector(
"#add_local_domain_button",
"#add_local_domain_button",
);
const edit_file_input = document.querySelector("#edit_file_input");
const save_config_file_button = document.querySelector(
"#save_config_file_button",
"#save_config_file_button",
);
var settings = {};
var configFile = "";
const updateDnsServersTable = () => {
upstream_dns_servers_table_body.innerHTML = "";
for (var i = 0; i < settings.resolv.length; i++) {
upstream_dns_servers_table_body.innerHTML += `<tr>
upstream_dns_servers_table_body.innerHTML = "";
for (var i = 0; i < settings.resolv.length; i++) {
upstream_dns_servers_table_body.innerHTML += `<tr>
<th class="d-flex align-items-center justify-content-between">
${settings.resolv[i]}
<button id="${i}" class="delete-table-button btn btn-danger">Del</button>
</th>
</tr>`;
}
}
};
const updateLocalDomainsTable = () => {
local_domains_table_body.innerHTML = "";
for (const [key, value] of Object.entries(settings.hosts)) {
if (Array.isArray(value)) {
for (var i = 0; i < value.length; i++) {
local_domains_table_body.innerHTML += `<tr>
local_domains_table_body.innerHTML = "";
for (const [key, value] of Object.entries(settings.hosts)) {
if (Array.isArray(value)) {
for (var i = 0; i < value.length; i++) {
local_domains_table_body.innerHTML += `<tr>
<th>${key}</th>
<th>${value[i]}</th>
<th><button class="deldomain btn btn-danger" id="${key}|${value[i]}">Del</button>
</th>
</tr>`;
}
} else {
local_domains_table_body.innerHTML += `<tr>
}
} else {
local_domains_table_body.innerHTML += `<tr>
<th>${key}</th>
<th>${value}</th>
<th><button class="deldomain btn btn-danger" id="${key}|${value}">Del</button>
</th>
</tr>`;
}
}
}
};
const updateInterface = () => {
updateDnsServersTable();
updateLocalDomainsTable();
cache_size_input.value = settings.cache_size;
updateDnsServersTable();
updateLocalDomainsTable();
cache_size_input.value = settings.cache_size;
};
const getSettings = () => {
fetch(window.location.origin + "/api/settings")
.then((data) => data.json())
.then((jsondata) => {
settings = jsondata;
updateInterface();
})
.catch((e) => alert(e));
fetch(window.location.origin + "/api/settings")
.then((data) => data.json())
.then((jsondata) => {
settings = jsondata;
updateInterface();
})
.catch((e) => alert(e));
};
const setSettings = () => {
fetch(window.location.origin + "/api/settings", {
method: "POST",
body: JSON.stringify(settings),
fetch(window.location.origin + "/api/settings", {
method: "POST",
body: JSON.stringify(settings),
})
.then((data) => data.json())
.then((jsondata) => {
if (jsondata.error) {
alert(jsondata.error);
return;
}
settings = jsondata;
updateInterface();
alert("Success set settings");
})
.then((data) => data.json())
.then((jsondata) => {
settings = jsondata;
updateInterface();
alert("Success set settings");
})
.catch((e) => alert(e));
.catch((e) => alert(e));
};
save_settings_button.addEventListener("click", () => {
setSettings();
setSettings();
});
const write_cache_size = (value) => {
settings.cache_size = value;
settings.cache_size = parseInt(value);
};
cache_size_input.addEventListener("change", (e) => {
write_cache_size(e.target.value);
write_cache_size(e.target.value);
});
cache_size_input.addEventListener("input", (e) => {
write_cache_size(e.target.value);
write_cache_size(e.target.value);
});
getSettings();
add_dns_server_button.addEventListener("click", () => {
const dns_server = prompt("Input your DNS server:");
if (dns_server === null) return;
if (confirm("Confirm DNS settings? (add DNS server)")) {
if (Array.isArray(settings.resolv)) {
if (!settings.resolv.includes(dns_server)) {
settings.resolv.push(dns_server);
updateDnsServersTable();
}
}
const dns_server = prompt("Input your DNS server:");
if (dns_server === null) return;
if (confirm("Confirm DNS settings? (add DNS server)")) {
if (Array.isArray(settings.resolv)) {
if (!settings.resolv.includes(dns_server)) {
settings.resolv.push(dns_server);
updateDnsServersTable();
}
}
}
});
upstream_dns_servers_table_body.addEventListener("click", (e) => {
if (e.target.classList.contains("delete-table-button")) {
if (
confirm(`Confirm DNS settings? (delete DNS server: ${e.target.id})`)
) {
if (Array.isArray(settings.resolv)) {
settings.resolv.splice(e.target.id, 1);
updateDnsServersTable();
}
}
if (e.target.classList.contains("delete-table-button")) {
if (confirm(`Confirm DNS settings? (delete DNS server: ${e.target.id})`)) {
if (Array.isArray(settings.resolv)) {
settings.resolv.splice(e.target.id, 1);
updateDnsServersTable();
}
}
}
});
add_local_domain_button.addEventListener("click", () => {
const domainip = prompt("Input localdomain IP:");
if (domainip === null) return;
const domain = prompt("Input domain:");
if (domainip === null || domain === null) return;
if (
confirm(
`Confirm DNS settings? (Add Local Domain: ${domainip} ${domain})`,
)
) {
for (const [key, value] of Object.entries(settings.hosts)) {
if (key === domainip) {
if (Array.isArray(value)) {
if (!value.includes(domain)) {
value.push(domain);
updateLocalDomainsTable();
return;
}
return;
}
const domainsarray = [value, domain];
settings.hosts[key] = domainsarray;
updateLocalDomainsTable();
return;
}
const domainip = prompt("Input localdomain IP:");
if (domainip === null) return;
const domain = prompt("Input domain:");
if (domainip === null || domain === null) return;
if (
confirm(`Confirm DNS settings? (Add Local Domain: ${domainip} ${domain})`)
) {
for (const [key, value] of Object.entries(settings.hosts)) {
if (key === domainip) {
if (Array.isArray(value)) {
if (!value.includes(domain)) {
value.push(domain);
updateLocalDomainsTable();
return;
}
return;
}
settings.hosts[domainip] = domain;
const domainsarray = [value, domain];
settings.hosts[key] = domainsarray;
updateLocalDomainsTable();
return;
}
}
settings.hosts[domainip] = domain;
updateLocalDomainsTable();
}
});
local_domains_table_body.addEventListener("click", (e) => {
if (e.target.classList.contains("deldomain")) {
if (confirm(`Confirm DNS settings? (delete domain: ${e.target.id})`)) {
const domainip = e.target.id.split("|")[0];
const domain = e.target.id.split("|")[1];
for (const [key, value] of Object.entries(settings.hosts)) {
if (key === domainip) {
if (Array.isArray(value)) {
if (value.includes(domain)) {
value.splice(value.indexOf(domain), 1);
updateLocalDomainsTable();
return;
}
return;
}
delete settings.hosts[key];
updateLocalDomainsTable();
return;
}
if (e.target.classList.contains("deldomain")) {
if (confirm(`Confirm DNS settings? (delete domain: ${e.target.id})`)) {
const domainip = e.target.id.split("|")[0];
const domain = e.target.id.split("|")[1];
for (const [key, value] of Object.entries(settings.hosts)) {
if (key === domainip) {
if (Array.isArray(value)) {
if (value.includes(domain)) {
value.splice(value.indexOf(domain), 1);
updateLocalDomainsTable();
return;
}
return;
}
delete settings.hosts[key];
updateLocalDomainsTable();
return;
}
}
}
}
});
const getConfigFile = () => {
fetch(window.location.origin + "/api/settings/file")
.then((data) => data.text())
.then((textdata) => (edit_file_input.value = textdata))
.catch((e) => alert(e));
fetch(window.location.origin + "/api/settings/file")
.then((data) => data.text())
.then((textdata) => (edit_file_input.value = textdata))
.catch((e) => alert(e));
};
getConfigFile();
save_config_file_button.addEventListener("click", () => {
if (confirm("Save Config file?")) {
fetch(window.location.origin + "/api/settings/file", {
method: "POST",
body: edit_file_input.value,
})
.then((data) => data.text())
.then((textdata) => {
edit_file_input.value = textdata;
alert("Success write settings file");
})
.catch((e) => alert(e));
save_config_file_button.addEventListener("click", async () => {
if (confirm("Save Config file?")) {
var response = await fetch(window.location.origin + "/api/settings/file", {
method: "POST",
body: edit_file_input.value,
});
if (!response.ok) {
const result = await response.json();
alert(result.error || "Failed to save config file");
return;
}
const result = await response.json();
if (result.error) {
alert(result.error);
} else {
edit_file_input.value = result.data;
alert("Success write settings file");
}
}
});