Commit bfaf1bee authored by Pascal Engélibert's avatar Pascal Engélibert

Config: JSON instead of ini

parent a04dfb61
......@@ -28,7 +28,7 @@ servers with different configs) (note that each server must have different dir)
python3 server.py -i -d ~/.gmixer-g1
Edit config and set your values. If you are not using any proxy, `Bind_` and `Public_` addresses should be the same.
Edit config and set your values. If you are not using any proxy, `bind_` and `public_` addresses should be the same.
If `salt` or `password` are empty, they will be asked at runtime. For security reasons, it is not a good idea to write them in commandline.
Now, start the server:
......@@ -38,25 +38,25 @@ Now, start the server:
### Proxy
To use a SOCKS5 proxy for client connections, set the `Proxy` value in `config.ini` as following:
To use a SOCKS5 proxy for client connections, set the `client.proxy` value in `config.json` as following:
Proxy: 127.0.0.1 8080 # address port
Proxy_OnionOnly: 0
"proxy": ["127.0.0.1", 8080], // ["address", port] or null
"proxy_onion_only": false
To use a proxy only when connecting to a `.onion` address, set the `Proxy_OnionOnly` value to `1`:
To use a proxy only when connecting to a `.onion` address, set the `client.proxy_onion_only` value to `true`:
Proxy: 127.0.0.1 9050
Proxy_OnionOnly: 1
"proxy": ["127.0.0.1", 9050],
"proxy_onion_only": true
If using a reverse proxy (i.e. Tor hidden service), example:
Bind_Host: 127.0.0.1 # local address set in torrc file
Bind_Port: 10951
Public_Host: svetsae7j3usrycn.onion # other nodes contact me with this address
Public_Port: 10951
"bind_host": "127.0.0.1", # local address set in torrc file
"bind_port": 10951,
"public_host": "svetsae7j3usrycn.onion", // other nodes contact me with this address
"public_port": 10951
To unactivate proxy, leave empty or remove the `Proxy` line.
To unactivate proxy, set `client.proxy` to `null`.
## Protocols
......
......@@ -17,7 +17,7 @@
along with ĞMixer-py. If not, see <https://www.gnu.org/licenses/>.
"""
import configparser, json, asyncio, getpass, secrets, random
import json, asyncio, getpass, secrets, random
from threading import Thread
import ubjson
import plyvel
......@@ -39,15 +39,13 @@ out_ = something I'm sending to a node
DIR = "~/.gmixer"
BIND_HOST = socket.gethostname()
BIND_PORT = "10951"
BIND_PORT = 10951
PUBLIC_HOST = BIND_HOST
PUBLIC_PORT = BIND_PORT
BMA_HOSTS = "g1.duniter.fr 443,g1.duniter.org 443,g1.presles.fr 443,g1.cgeek.fr 443,ts.g1.librelois.fr 443"
ID_SALT = "" # if empty, will be asked at runtime
ID_PSW = "" # if empty, will be asked at runtime
MIX_INTERVAL = "60"
MIX_MIN_TXS = "5" # minimum amount of txs to mix
MIX_REQ_AGE_MAX = "604800" # maximum mix request age before return to sender
BMA_HOSTS = ["g1.duniter.fr 443", "g1.duniter.org 443", "g1.presles.fr 443", "g1.cgeek.fr 443", "ts.g1.librelois.fr 443"]
MIX_INTERVAL = 60
MIX_MIN_TXS = 5 # minimum amount of txs to mix
MIX_REQ_AGE_MAX = 604800 # maximum mix request age before return to sender
def sendResponse(client, code, resp, dataformat="ubjson"):
if dataformat == "ubjson":
......@@ -148,48 +146,37 @@ def save_txs(db_txs, pool):
tx.export_ubjson(db_txs)
# Read ini config file
def readConfig(dir):
if not os.path.isfile(dir+"/config.ini"):
open(dir+"/config.ini", "w").close()
conf = configparser.ConfigParser()
conf.read(dir+"/config.ini")
if not "Server" in conf:
conf["Server"] = {}
if not "Client" in conf:
conf["Client"] = {}
if not "Crypto" in conf:
conf["Crypto"] = {}
if not "Mix" in conf:
conf["Mix"] = {}
if not "Bind_Host" in conf["Server"]:
conf["Server"]["Bind_Host"] = BIND_HOST
if not "Bind_Port" in conf["Server"]:
conf["Server"]["Bind_Port"] = BIND_PORT
if not "Public_Host" in conf["Server"]:
conf["Server"]["Public_Host"] = PUBLIC_HOST
if not "Public_Port" in conf["Server"]:
conf["Server"]["Public_Port"] = PUBLIC_PORT
if not "BMA_Hosts" in conf["Client"]:
conf["Client"]["BMA_Hosts"] = BMA_HOSTS
if not "Proxy" in conf["Client"]:
conf["Client"]["Proxy"] = ""
if not "Proxy_OnionOnly" in conf["Client"]:
conf["Client"]["Proxy_OnionOnly"] = "0"
if not "ID_Salt" in conf["Crypto"]:
conf["Crypto"]["ID_Salt"] = ID_SALT
if not "ID_Password" in conf["Crypto"]:
conf["Crypto"]["ID_Password"] = ID_PSW
if not "MixInterval" in conf["Mix"]:
conf["Mix"]["MixInterval"] = MIX_INTERVAL
if not "MixMinTxs" in conf["Mix"]:
conf["Mix"]["MixMinTxs"] = MIX_MIN_TXS
if not "MixReqAgeMax" in conf["Mix"]:
conf["Mix"]["MixReqAgeMax"] = MIX_REQ_AGE_MAX
with open(dir+"/config.ini", "w") as configfile:
conf.write(configfile)
def readConfig(cdir):
if not os.path.isfile(cdir+"/config.json"):
configfile = open(cdir+"/config.json", "w")
configfile.write("{}")
configfile.close()
with open(cdir+"/config.json", "r") as configfile:
try:
conf = json.load(configfile)
except json.JSONDecodeError:
logPrint("Config: bad JSON => abort", LOG_ERROR)
exit(1)
conf.setdefault("server", {})
conf["server"].setdefault("bind_host", BIND_HOST)
conf["server"].setdefault("bind_port", BIND_PORT)
conf["server"].setdefault("public_host", PUBLIC_HOST)
conf["server"].setdefault("public_port", PUBLIC_PORT)
conf.setdefault("client", {})
conf["client"].setdefault("bma_hosts", BMA_HOSTS)
conf["client"].setdefault("proxy", None)
conf["client"].setdefault("proxy_onion_only", False)
conf.setdefault("crypto", {})
conf["crypto"].setdefault("id_salt", "")
conf["crypto"].setdefault("id_password", "")
conf.setdefault("mix", {})
conf["mix"].setdefault("mix_interval", MIX_INTERVAL)
conf["mix"].setdefault("mix_req_age_max", MIX_REQ_AGE_MAX)
with open(cdir+"/config.json", "w") as configfile:
json.dump(conf, configfile, indent=1)
return conf
......@@ -210,7 +197,7 @@ class ServerThread(Thread):
self.work = True
def run(self):
server_addr = (self.conf["Server"]["Bind_Host"], int(self.conf["Server"]["Bind_Port"]))
server_addr = (self.conf["server"]["bind_host"], self.conf["server"]["bind_port"])
if ":" in server_addr[0]: # IPv6
self.sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
else: # IPv4
......@@ -250,7 +237,7 @@ class ServerThread(Thread):
content = parts[1]
try:
content_len = int(p_clen.search(header.decode()).group(1))
except AttributeError:
except (AttributeError, ValueError):
content_len = 0
break
if lf > 1:
......@@ -336,7 +323,7 @@ class ServerThread(Thread):
# Save tx in pool
t = time.time()
tx = TX(sender_pubkey, receiver_pubkey, onetime_pubkey, in_amount, in_base, in_amount, in_base, message, in_comment, out_comment, send_confirm, t, t+int(self.conf["Mix"]["MixReqAgeMax"]))
tx = TX(sender_pubkey, receiver_pubkey, onetime_pubkey, in_amount, in_base, in_amount, in_base, message, in_comment, out_comment, send_confirm, t, t+self.conf["mix"]["mix_req_age_max"])
last_node = len(message) == 0
tx.need_send = not last_node
tx.can_confirm = last_node
......@@ -475,13 +462,8 @@ class ClientThread(Thread):
self.tx_out_index = tx_out_index
self.db_txs = db_txs
self.bma_endpoints = ["BMAS "+host for host in conf["Client"]["BMA_Hosts"].split(",")]
self.bma_endpoints = ["BMAS "+host for host in conf["client"]["bma_hosts"]]
self.work = True
self.proxy = None
if conf["Client"]["Proxy"] != "":
_proxy = conf["Client"]["Proxy"].split(" ")
self.proxy = (_proxy[0], int(_proxy[1]))
self.proxy_onion_only = conf["Client"]["Proxy_OnionOnly"] == "1"
def detectPeers(self):# Check known peers and ask them for their known peer list
logPrint("Start peers detection", LOG_TRACE)
......@@ -498,15 +480,15 @@ class ClientThread(Thread):
message = ubjson.dumpb({
"pubkey": self.keys.pubkey,
"host": self.conf["Server"]["Public_Host"],
"port": self.conf["Server"]["Public_Port"],
"host": self.conf["server"]["public_host"],
"port": self.conf["server"]["public_port"],
"time": time.time()
})
message = peer.keys.encrypt_seal(message) # Encrypt
message = self.keys.sign(message) # Sign
logPrint("Ask "+str(peer), LOG_TRACE)
try:
header, content = sdata((peer.host, peer.port), "POST", "/list/new/"+self.keys.pubkey, message, proxy=self.proxy, proxy_onion_only=self.proxy_onion_only) # Send
header, content = sdata((peer.host, peer.port), "POST", "/list/new/"+self.keys.pubkey, message, proxy=self.conf["client"]["proxy"], proxy_onion_only=self.conf["client"]["proxy_onion_only"]) # Send
except (ConnectionRefusedError, socks.GeneralProxyError):
peer.up = False
logPrint("Down "+str(peer), LOG_TRACE)
......@@ -567,7 +549,7 @@ class ClientThread(Thread):
ready_txs = []
# Get txs history
history = await client(bma.tx.times, self.keys.pubkey, int(t-int(self.conf["Mix"]["MixReqAgeMax"])), int(t))
history = await client(bma.tx.times, self.keys.pubkey, int(t-self.conf["mix"]["mix_req_age_max"]), int(t))
await client.close()
for tx in self.pool:
......@@ -589,7 +571,7 @@ class ClientThread(Thread):
ready_txs.append(tx)
print(wanted_output)
if len(ready_txs) >= int(self.conf["Mix"]["MixMinTxs"]):
if len(ready_txs) >= self.conf["mix"]["mix_min_txs"]:
random.shuffle(ready_txs)
for tx in ready_txs:
try:
......@@ -610,7 +592,7 @@ class ClientThread(Thread):
async def startClient(self):
next_peers_detection = 0
next_mix = time.time() + int(self.conf["Mix"]["MixInterval"])
next_mix = time.time() + self.conf["mix"]["mix_interval"]
while self.work:
t = time.time()
......@@ -623,7 +605,7 @@ class ClientThread(Thread):
# Mix
if t > next_mix:
await self.mix()
next_mix = time.time() + int(self.conf["Mix"]["MixInterval"])
next_mix = time.time() + self.conf["mix"]["mix_interval"]
for tx in self.pool:
......@@ -634,7 +616,7 @@ class ClientThread(Thread):
message = self.keys.sign(tx.out_comment.encode() + tx.message) # Sign
try:
header, content = sdata((peer.host, peer.port), "POST", "/mix/"+self.keys.pubkey+"/"+tx.out_amount+"/"+tx.out_base, message, proxy=self.proxy, proxy_onion_only=self.proxy_onion_only)
header, content = sdata((peer.host, peer.port), "POST", "/mix/"+self.keys.pubkey+"/"+tx.out_amount+"/"+tx.out_base, message, proxy=self.conf["client"]["proxy"], proxy_onion_only=self.conf["client"]["proxy_onion_only"])
data = ubjson.loadb(content)
assert data["mix_ok"] == tx.out_comment
tx.need_send = False
......@@ -658,7 +640,7 @@ class ClientThread(Thread):
message = tx.genMixConfirm(self.keys)
try:
header, content = sdata((peer.host, peer.port), "POST", "/confirm/"+self.keys.pubkey+"/"+tx.in_comment, message, proxy=self.proxy, proxy_onion_only=self.proxy_onion_only)
header, content = sdata((peer.host, peer.port), "POST", "/confirm/"+self.keys.pubkey+"/"+tx.in_comment, message, proxy=self.conf["client"]["proxy"], proxy_onion_only=self.conf["client"]["proxy_onion_only"])
data = ubjson.loadb(content)
assert data["confirm_ok"] == tx.in_comment
tx.need_confirm = False
......@@ -677,7 +659,7 @@ class ClientThread(Thread):
# Remove expired requests
expire_txs = []
expire_t = t - int(self.conf["Mix"]["MixReqAgeMax"])
expire_t = t - self.conf["mix"]["mix_req_age_max"]
for tx in self.pool:
if tx.date < expire_t:
expire_txs.append(tx)
......@@ -690,10 +672,10 @@ class ClientThread(Thread):
time.sleep(5)
def getCredentials(conf):
salt = conf["Crypto"]["ID_Salt"]
salt = conf["crypto"]["id_salt"]
if salt == "":
salt = getpass.getpass("Enter your passphrase (salt): ")
password = conf["Crypto"]["ID_Password"]
password = conf["crypto"]["id_password"]
if password == "":
password = getpass.getpass("Enter your password: ")
return salt, password
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment