Implements #88, documents adjust, uniformed default values

This commit is contained in:
Matteo ℱan 2020-09-03 23:52:59 +02:00
parent 27aa94a6e4
commit 33cedce48c
No known key found for this signature in database
GPG Key ID: 3C30A05BC133D9B6
14 changed files with 144 additions and 142 deletions

1
.gitignore vendored
View File

@ -4,6 +4,7 @@ pykms_logclient.log*
pykms_newlines.txt* pykms_newlines.txt*
pykms_clean_newlines.txt* pykms_clean_newlines.txt*
pykms_config.pickle* pykms_config.pickle*
pykms_database.db*
etrigan.log* etrigan.log*
# Byte-compiled / optimized / DLL files # Byte-compiled / optimized / DLL files

View File

@ -29,23 +29,19 @@ _py-kms_ is a port of node-kms created by [cyrozap](http://forums.mydigitallife.
- Microsoft Office 2013 ( Volume License ) - Microsoft Office 2013 ( Volume License )
- Microsoft Office 2016 ( Volume License ) - Microsoft Office 2016 ( Volume License )
- Microsoft Office 2019 ( Volume License ) - Microsoft Office 2019 ( Volume License )
- It's written in Python (tested with Python 3.6.7) - It's written in Python (tested with Python 3.6.9).
- Supports execution by `Docker`, `systemd`, `Upstart` and many more... - Supports execution by `Docker`, `systemd`, `Upstart` and many more...
- Includes a GUI for simlpe managing - Includes a GUI for simple managing.
- Uses `sqlite` for persistent data storage - Uses `sqlite` for persistent data storage.
## [Documentation](https://py-kms.readthedocs.io/en/latest/) ## Documentation
The documentation has been completly reworked and is now available on [readthedocs.com](https://py-kms.readthedocs.io/en/latest/Usage.html#start-parameters). It should you provide all necessary information how to get py-kms up The wiki has been completly reworked and is now available on [readthedocs.com](https://py-kms.readthedocs.io/en/latest/). It should you provide all necessary information how to setup and to use _py-kms_ , all without clumping this readme. The documentation also houses more details about activation with _py-kms_ and how to get GVLK keys.
and running using your favourite tools - all without clumping this readme up. The documentation also houses more information about activation with _py-kms_ and to how get GVLK keys.
## Get it running (fast)... ## Quick start
- To start the server manually, execute `python3 pykms_Server.py [IPADDRESS] [PORT]`, the default `IPADDRESS` is `::` ( all ipv6-interfaces ) and the default `PORT` is `1688`. Note that both the address and port are optional. - To start the server, execute `python3 pykms_Server.py [IPADDRESS] [PORT]`, the default _IPADDRESS_ is `0.0.0.0` ( all interfaces ) and the default _PORT_ is `1688`. Note that both the address and port are optional. It's allowed to use IPv4 and IPv6 addresses. If you have a IPv6-capable dual-stack OS, a dual-stack socket is created when using a IPv6 address.
Also note that it is recommended to use an IPv6 address - even if you are just plan to use IPv4 (the kernel maps the incoming IPv4 requests automatically to IPv6)!
- To start the server automatically using Docker, execute `docker run -d --name py-kms --restart always -p 1688:1688 pykmsorg/py-kms`. - To start the server automatically using Docker, execute `docker run -d --name py-kms --restart always -p 1688:1688 pykmsorg/py-kms`.
- To show the help pages type: `python3 pykms_Server.py -h` and `python3 pykms_Client.py -h`. - To show the help pages type: `python3 pykms_Server.py -h` and `python3 pykms_Client.py -h`.
- For launching py-kms GUI make the file `pykms_Server.py` executable with `chmod +x /path/to/folder/py-kms/pykms_Server.py`, then simply run `pykms_Server.py` by double-clicking. - For launching _py-kms_ GUI make the file `pykms_Server.py` executable with `chmod +x /path/to/folder/py-kms/pykms_Server.py`, then simply run `pykms_Server.py` by double-clicking.
_If you wish to learn more about using py-kms, please refer to the documentation [here](Getting Started.md)!_
## License ## License
- _py-kms_ is [![Unlicense](https://img.shields.io/badge/license-unlicense-lightgray.svg)](https://github.com/SystemRage/py-kms/blob/master/LICENSE) - _py-kms_ is [![Unlicense](https://img.shields.io/badge/license-unlicense-lightgray.svg)](https://github.com/SystemRage/py-kms/blob/master/LICENSE)

View File

@ -9,17 +9,17 @@ docker run -it -d --name py3-kms \
-e HWID=RANDOM \ -e HWID=RANDOM \
-e LOGLEVEL=INFO \ -e LOGLEVEL=INFO \
-e LOGSIZE=2 \ -e LOGSIZE=2 \
-e LOGFILE=/var/log/py3-kms.log \ -e LOGFILE=/var/log/pykms_logserver.log \
-v /etc/localtime:/etc/localtime:ro \ -v /etc/localtime:/etc/localtime:ro \
-v /var/log:/var/log:rw \ -v /var/log:/var/log:rw \
--restart unless-stopped pykmsorg/py-kms:[TAG] --restart unless-stopped pykmsorg/py-kms:[TAG]
``` ```
_Make sure to insert at `[TAG]` your wanted edition! The default is `latest`, which does not include SQLLite support. For all available tag check [this](https://hub.docker.com/r/pykmsorg/py-kms/tags) out._ _Make sure to insert at `[TAG]` your wanted edition! The default is `latest`, which does not include SQLite support. For all available tag check [this](https://hub.docker.com/r/pykmsorg/py-kms/tags)._
Therefore you can omit the `-e SQLITE=...` and `-p 8080:8080` option if you plan to use the `minimal` or `latest` image. Therefore you can omit the `-e SQLITE=...` and `-p 8080:8080` option if you plan to use the `minimal` or `latest` image.
# Sqlite-web # Sqlite-web
A web-based SQLite database browser written in Python. A web-based SQLite database browser written in Python.
Start on http://example.com:8080/ in read-only mode for clients.db. Start on http://example.com:8080/ in read-only mode for _pykms_database.db_.
# Options # Options
``` ```

View File

@ -1,8 +1,8 @@
# This is a minimized version from docker/docker-py3-kms/Dockerfile without SQLLite support to further reduce image size # This is a minimized version from docker/docker-py3-kms/Dockerfile without SQLite support to further reduce image size
FROM alpine:3.8 FROM alpine:3.8
ENV IP :: ENV IP 0.0.0.0
ENV PORT 1688 ENV PORT 1688
ENV EPID "" ENV EPID ""
ENV LCID 1033 ENV LCID 1033

View File

@ -9,8 +9,8 @@ FROM alpine:3.8
# EN: IP-address # EN: IP-address
# RU: IP-адрес # RU: IP-адрес
ENV IP :: ENV IP 0.0.0.0
# The IP address to listen on. The default is "::" (all interfaces). # The IP address to listen on. The default is "0.0.0.0" (all interfaces).
# EN: TCP-port # EN: TCP-port
# RU: TCP-порт # RU: TCP-порт
@ -68,7 +68,7 @@ ENV LOGFILE /var/log/pykms_logserver.log
# EN: log file size in MB # EN: log file size in MB
# RU: Максимальный размер Лог-файл в мегабайтах # RU: Максимальный размер Лог-файл в мегабайтах
ENV LOGSIZE "" ENV LOGSIZE ""
# Use this flag to set a maximum size (in MB) to the output log file. Desactivated by default. # Use this flag to set a maximum size (in MB) to the output log file. Deactivated by default.
# EN: Startup script # EN: Startup script
# RU: Скрипт автозапуска # RU: Скрипт автозапуска

View File

@ -9,8 +9,8 @@ docker run -d --name py3-kms \
-e SQLITE=true \ -e SQLITE=true \
-e HWID=RANDOM \ -e HWID=RANDOM \
-e LOGLEVEL=INFO \ -e LOGLEVEL=INFO \
-e LOGFILE=/var/log/py3-kms.log \ -e LOGFILE=/var/log/pykms_logserver.log \
-e LOGSIZE=2 \ -e LOGSIZE=2 \
-v /etc/localtime:/etc/localtime:ro \ -v /etc/localtime:/etc/localtime:ro \
-v /var/log:/var/log:rw \ -v /var/log:/var/log:rw \
--restart unless-stopped pykms/pykms:py3-kms --restart unless-stopped pykmsorg/py-kms:python3

View File

@ -28,12 +28,12 @@ else
/bin/bash -c "/usr/bin/python3 pykms_Server.py ${IP} ${PORT} -l ${LCID} -c ${CLIENT_COUNT} -a ${ACTIVATION_INTERVAL} -r ${RENEWAL_INTERVAL} -s -w ${HWID} -V ${LOGLEVEL} -F ${LOGFILE} &" /bin/bash -c "/usr/bin/python3 pykms_Server.py ${IP} ${PORT} -l ${LCID} -c ${CLIENT_COUNT} -a ${ACTIVATION_INTERVAL} -r ${RENEWAL_INTERVAL} -s -w ${HWID} -V ${LOGLEVEL} -F ${LOGFILE} &"
sleep 5 sleep 5
/usr/bin/python3 pykms_Client.py ${IP} ${PORT} -m Windows10 & /usr/bin/python3 pykms_Client.py ${IP} ${PORT} -m Windows10 &
/usr/bin/python3 /home/sqlite_web/sqlite_web.py -H ${IP} -x ${PWD}/clients.db --read-only /usr/bin/python3 /home/sqlite_web/sqlite_web.py -H ${IP} -x ${PWD}/pykms_database.db --read-only
else else
/bin/bash -c "/usr/bin/python3 pykms_Server.py ${IP} ${PORT} -l ${LCID} -c ${CLIENT_COUNT} -a ${ACTIVATION_INTERVAL} -r ${RENEWAL_INTERVAL} -s -w ${HWID} -V ${LOGLEVEL} -F ${LOGFILE} -S ${LOGSIZE} &" /bin/bash -c "/usr/bin/python3 pykms_Server.py ${IP} ${PORT} -l ${LCID} -c ${CLIENT_COUNT} -a ${ACTIVATION_INTERVAL} -r ${RENEWAL_INTERVAL} -s -w ${HWID} -V ${LOGLEVEL} -F ${LOGFILE} -S ${LOGSIZE} &"
sleep 5 sleep 5
/usr/bin/python3 pykms_Client.py ${IP} ${PORT} -m Windows10 & /usr/bin/python3 pykms_Client.py ${IP} ${PORT} -m Windows10 &
/usr/bin/python3 /home/sqlite_web/sqlite_web.py -H ${IP} -x ${PWD}/clients.db --read-only /usr/bin/python3 /home/sqlite_web/sqlite_web.py -H ${IP} -x ${PWD}/pykms_database.db --read-only
fi fi
else else
if [ "$LOGSIZE" == "" ]; if [ "$LOGSIZE" == "" ];
@ -41,12 +41,12 @@ else
/bin/bash -c "/usr/bin/python3 pykms_Server.py ${IP} ${PORT} -e ${EPID} -l ${LCID} -c ${CLIENT_COUNT} -a ${ACTIVATION_INTERVAL} -r ${RENEWAL_INTERVAL} -s -w ${HWID} -V ${LOGLEVEL} -F ${LOGFILE} &" /bin/bash -c "/usr/bin/python3 pykms_Server.py ${IP} ${PORT} -e ${EPID} -l ${LCID} -c ${CLIENT_COUNT} -a ${ACTIVATION_INTERVAL} -r ${RENEWAL_INTERVAL} -s -w ${HWID} -V ${LOGLEVEL} -F ${LOGFILE} &"
sleep 5 sleep 5
/usr/bin/python3 pykms_Client.py ${IP} ${PORT} -m Windows10 & /usr/bin/python3 pykms_Client.py ${IP} ${PORT} -m Windows10 &
/usr/bin/python3 /home/sqlite_web/sqlite_web.py -H ${IP} -x ${PWD}/clients.db --read-only /usr/bin/python3 /home/sqlite_web/sqlite_web.py -H ${IP} -x ${PWD}/pykms_database.db --read-only
else else
/bin/sh -c "/usr/bin/python3 pykms_Server.py ${IP} ${PORT} -e ${EPID} -l ${LCID} -c ${CLIENT_COUNT} -a ${ACTIVATION_INTERVAL} -r ${RENEWAL_INTERVAL} -s -w ${HWID} -V ${LOGLEVEL} -F ${LOGFILE} -S ${LOGSIZE} &" /bin/sh -c "/usr/bin/python3 pykms_Server.py ${IP} ${PORT} -e ${EPID} -l ${LCID} -c ${CLIENT_COUNT} -a ${ACTIVATION_INTERVAL} -r ${RENEWAL_INTERVAL} -s -w ${HWID} -V ${LOGLEVEL} -F ${LOGFILE} -S ${LOGSIZE} &"
sleep 5 sleep 5
/usr/bin/python3 pykms_Client.py ${IP} ${PORT} -m Windows10 & /usr/bin/python3 pykms_Client.py ${IP} ${PORT} -m Windows10 &
/usr/bin/python3 /home/sqlite_web/sqlite_web.py -H ${IP} -x ${PWD}/clients.db --read-only /usr/bin/python3 /home/sqlite_web/sqlite_web.py -H ${IP} -x ${PWD}/pykms_database.db --read-only
fi fi
fi fi
fi fi

View File

@ -1,5 +1,5 @@
# Documentation # Documentation
What follows are some detailed explanations how some parts work. What follows are some detailed explanations how the KMS infrastructure works.
## Understanding Key Management Service ## Understanding Key Management Service
KMS activates Microsoft products on a local network, eliminating the need for individual computers to connect to Microsoft. To do this, KMS uses a clientserver topology. A KMS client locates a KMS server by using DNS or a static KMS activates Microsoft products on a local network, eliminating the need for individual computers to connect to Microsoft. To do this, KMS uses a clientserver topology. A KMS client locates a KMS server by using DNS or a static
@ -324,7 +324,7 @@ Where command line options are:
</tbody> </tbody>
</table> </table>
## Further references ## Further References
* <sup>[1]</sup> https://forums.mydigitallife.net/threads/emulated-kms-servers-on-non-windows-platforms.50234 * <sup>[1]</sup> https://forums.mydigitallife.net/threads/emulated-kms-servers-on-non-windows-platforms.50234
* <sup>[2]</sup> https://forums.mydigitallife.net/threads/discussion-microsoft-office-2019.75232 * <sup>[2]</sup> https://forums.mydigitallife.net/threads/discussion-microsoft-office-2019.75232
* <sup>[3]</sup> https://forums.mydigitallife.net/threads/miscellaneous-kms-related-developments.52594 * <sup>[3]</sup> https://forums.mydigitallife.net/threads/miscellaneous-kms-related-developments.52594

View File

@ -1,10 +1,9 @@
# Getting Started # Getting Started
What follows are some guides how to start the `pykms_Server.py` script, which provides the server. What follows are some guides how to start the `pykms_Server.py` script, which provides the emulated server.
## Running as a service ## Running as a service
*** ***
You can simply manage a daemon that runs as a background process. This can be achieved by using any of the notes below or by writing your own solution.
You can simply manage a daemon that runs as a background process. This can be achieved by using any of the guides below or by writing your own solution.
### Docker ### Docker
![docker-auto](https://img.shields.io/docker/cloud/automated/pykmsorg/py-kms) ![docker-auto](https://img.shields.io/docker/cloud/automated/pykmsorg/py-kms)
@ -12,19 +11,20 @@ You can simply manage a daemon that runs as a background process. This can be ac
![docker-pulls](https://img.shields.io/docker/pulls/pykmsorg/py-kms) ![docker-pulls](https://img.shields.io/docker/pulls/pykmsorg/py-kms)
![docker-size](https://img.shields.io/docker/image-size/pykmsorg/py-kms) ![docker-size](https://img.shields.io/docker/image-size/pykmsorg/py-kms)
If you wish to get _py-kms_ just up and running without installing any dependencies or writing own scripts: Just use Docker! Docker also solves problems regarding the explicit IPv4 and IPv6 usage: It just supports both. The following If you wish to get _py-kms_ just up and running without installing any dependencies or writing own scripts: Just use Docker !
Docker also solves problems regarding the explicit IPv4 and IPv6 usage (it just supports both). The following
command will download, "install" and start _py-kms_ and also keep it alive after any service disruption. command will download, "install" and start _py-kms_ and also keep it alive after any service disruption.
```bash ```bash
docker run -d --name py-kms --restart always -p 1688:1688 pykmsorg/py-kms docker run -d --name py-kms --restart always -p 1688:1688 pykmsorg/py-kms
``` ```
There are currently three tags of the image available (select one just by appending `:<tag>` to the image from above): There are currently three tags of the image available (select one just by appending `:<tag>` to the image from above):
* `latest`, currently the same like minimal... * `latest`, currently the same like `minimal`.
* `minimal`, wich is based on the python3 minimal configuration of py-kms. _This tag does NOT include `sqlite` support!_ * `minimal`, which is based on the python3 minimal configuration of py-kms. _This tag does NOT include `sqlite` support !_
* `python3`, which is fully configurable and equipped with `sqlite` support and a web interface for management. * `python3`, which is fully configurable and equipped with `sqlite` support and a web interface for management.
If you just want to use the image and don't want to build them yourself, you can always use the official image at the [Docker Hub](https://hub.docker.com/r/pykmsorg/py-kms) (`pykmsorg/py-kms`). To ensure that you are using always the If you just want to use the image and don't want to build them yourself, you can always use the official image at the [Docker Hub](https://hub.docker.com/r/pykmsorg/py-kms) (`pykmsorg/py-kms`). To ensure that you are using always the
latest version you should check something like [watchtower](https://github.com/containrrr/watchtower) out! latest version you should check something like [watchtower](https://github.com/containrrr/watchtower) out !
### Systemd ### Systemd
If you are running a Linux distro using `systemd`, create the file: `sudo nano /etc/systemd/system/py3-kms.service`, then add the following (change it where needed) and save: If you are running a Linux distro using `systemd`, create the file: `sudo nano /etc/systemd/system/py3-kms.service`, then add the following (change it where needed) and save:
@ -114,14 +114,19 @@ if __name__ == '__main__':
Now in a command prompt type `C:\Windows\Python27\python.exe kms-winservice.py install` to install the service. Display all the services with `services.msc` and find the service associated with _py-kms_, change the startup type Now in a command prompt type `C:\Windows\Python27\python.exe kms-winservice.py install` to install the service. Display all the services with `services.msc` and find the service associated with _py-kms_, change the startup type
from `manual` to `auto`. Finally `Start` the service. If this approach fails, you can try to use [Non-Sucking Service Manager](https://nssm.cc/) or Task Scheduler as described [here](https://blogs.esri.com/esri/arcgis/2013/07/30/scheduling-a-scrip/). from `manual` to `auto`. Finally `Start` the service. If this approach fails, you can try to use [Non-Sucking Service Manager](https://nssm.cc/) or Task Scheduler as described [here](https://blogs.esri.com/esri/arcgis/2013/07/30/scheduling-a-scrip/).
## Manual execution ### Other Platforms
They might be useful to you:
- [FreeNAS](https://github.com/SystemRage/py-kms/issues/56)
- [FreeBSD](https://github.com/SystemRage/py-kms/issues/89)
## Manual Execution
*** ***
### Dependencies ### Dependencies
- Python 3.x. - Python 3.x.
- Tkinter module (for the GUI). - Tkinter module (for the GUI).
- If the `tzlocal` module is installed, the "Request Time" in the verbose output will be converted into local time. Otherwise, it will be in UTC. - If the `tzlocal` module is installed, the "Request Time" in the verbose output will be converted into local time. Otherwise, it will be in UTC.
- It can use the `sqlite3` module so you can use the database function, storing activation data so it can be recalled again. - It can use the `sqlite3` module, storing activation data in a database so it can be recalled again.
- Installation example on Ubuntu / Mint: - Installation example on Ubuntu / Mint:
- `sudo apt-get update` - `sudo apt-get update`
- `sudo apt-get install python3-tk python3-pip` - `sudo apt-get install python3-tk python3-pip`
@ -146,7 +151,7 @@ user@host ~ $ ip addr
inet6 ****::****:****:****:****/64 scope link noprefixroute inet6 ****::****:****:****:****/64 scope link noprefixroute
valid_lft forever preferred_lft forever valid_lft forever preferred_lft forever
``` ```
In the example above is `192.168.1.102` the ip we want to listen on, so it is this command (**note you can omit the ip AND port specification if you just wish to listen on all interfaces**): In the example above is `192.168.1.102` the ip we want to listen on, so it is this command (**note you can omit the ip AND port specification if you just wish to listen on all interfaces with port 1688**):
``` ```
user@host ~/path/to/folder/py-kms $ python3 pykms_Server.py 192.168.1.102 1688 user@host ~/path/to/folder/py-kms $ python3 pykms_Server.py 192.168.1.102 1688
@ -155,8 +160,8 @@ user@host ~/path/to/folder/py-kms $ python3 pykms_Server.py 192.168.1.102 1688
To stop `pykms_Server.py`, in the same bash window where code running, simply press `CTRL+C`. To stop `pykms_Server.py`, in the same bash window where code running, simply press `CTRL+C`.
Alternatively, in a new bash window, use `kill <pid>` command (you can type `ps aux` first and have the process <pid>) or `killall <name_of_server>`. Alternatively, in a new bash window, use `kill <pid>` command (you can type `ps aux` first and have the process <pid>) or `killall <name_of_server>`.
#### Notes ### Quick Guide
The following are just some quick notes - for a more detailed list of parameters see [here](Usage.md). The following are just some brief notes about parameters handling. For a more detailed description see [here](Usage.md).
- To generate a random HWID use `-w` option: `python3 pykms_Server.py -w RANDOM`. - To generate a random HWID use `-w` option: `python3 pykms_Server.py -w RANDOM`.
- To get the HWID from any server use the client, for example type: `python3 pykms_Client.py :: 1688 -m Windows8.1 -V INFO`. - To get the HWID from any server use the client, for example type: `python3 pykms_Client.py :: 1688 -m Windows8.1 -V INFO`.

View File

@ -54,7 +54,8 @@ e.g. because it could not reach the server. The default is 120 minutes (2 hours)
> Instructs clients to renew activation every _RENEWALINTERVAL_ minutes. The default is 10080 minutes (7 days). > Instructs clients to renew activation every _RENEWALINTERVAL_ minutes. The default is 10080 minutes (7 days).
-s or --sqlite -s or --sqlite
> Use this option to store request information from unique clients in an SQLite database. > Use this option to store request information from unique clients in an SQLite database. Deactivated by default.
If enabled the default database file is _pykms_database.db_. You can also provide a specific location.
-t0 or --timeout-idle <TIMEOUT> -t0 or --timeout-idle <TIMEOUT>
> Maximum inactivity time (in seconds) after which the connection with the client is closed. > Maximum inactivity time (in seconds) after which the connection with the client is closed.
@ -63,7 +64,7 @@ Default setting is serve forever (no timeout).
-y or --async-msg -y or --async-msg
> With high levels of logging (e.g hundreds of log statements), in a traditional synchronous log model, > With high levels of logging (e.g hundreds of log statements), in a traditional synchronous log model,
the overhead involved becomes more expensive, so using this option you enable printing (pretty / logging) messages the overhead involved becomes more expensive, so using this option you enable printing (pretty / logging) messages
asynchronously reducing time-consuming. Desactivated by default. asynchronously reducing time-consuming. Deactivated by default.
-V or --loglevel <{CRITICAL, ERROR, WARNING, INFO, DEBUG, MINI}> -V or --loglevel <{CRITICAL, ERROR, WARNING, INFO, DEBUG, MINI}>
> Use this flag to set a logging loglevel. The default is _ERROR_. > Use this flag to set a logging loglevel. The default is _ERROR_.
@ -81,7 +82,7 @@ Mon, 12 Jun 2017 22:09:00 INFO HWID: 364F463A8863D35F
> Creates a _LOGFILE.log_ logging file. The default is named _pykms_logserver.log_. > Creates a _LOGFILE.log_ logging file. The default is named _pykms_logserver.log_.
example: example:
``` ```
user@user ~/path/to/folder/py-kms $ python3 pykms_Server.py 192.168.1.102 8080 -F ~/path/to/folder/py-kms/newlogfile.log -V INFO -w RANDOM user@host ~/path/to/folder/py-kms $ python3 pykms_Server.py 192.168.1.102 8080 -F ~/path/to/folder/py-kms/newlogfile.log -V INFO -w RANDOM
``` ```
creates _newlogfile.log_ with these initial messages: creates _newlogfile.log_ with these initial messages:
``` ```
@ -134,7 +135,7 @@ You can also enable other suboptions of `-F` doing what is reported in the follo
</table> </table>
-S or --logsize <MAXSIZE> -S or --logsize <MAXSIZE>
> Use this flag to set a maximum size (in MB) to the output log file. Desactivated by default. > Use this flag to set a maximum size (in MB) to the output log file. Deactivated by default.
### pykms_Client.py ### pykms_Client.py
If _py-kms_ server doesn't works correctly, you can test it with the KMS client `pykms_Client.py`, running on the same machine where you started `pykms_Server.py`. If _py-kms_ server doesn't works correctly, you can test it with the KMS client `pykms_Client.py`, running on the same machine where you started `pykms_Server.py`.
@ -142,7 +143,7 @@ If _py-kms_ server doesn't works correctly, you can test it with the KMS client
For example (in separated bash windows) run these commands: For example (in separated bash windows) run these commands:
``` ```
user@host ~/path/to/folder/py-kms $ python3 pykms_Server.py -V DEBUG user@host ~/path/to/folder/py-kms $ python3 pykms_Server.py -V DEBUG
user@host ~/path/to/folder/py-kms $ python3 pykms_Client.py 0.0.0.0 1688 -V DEBUG user@host ~/path/to/folder/py-kms $ python3 pykms_Client.py -V DEBUG
``` ```
Or if you want better specify: Or if you want better specify:
@ -176,7 +177,7 @@ activate regardless of CMID being unique for a subset of specific machines or no
> Use this flag to manually specify an ASCII _MACHINENAME_ to use. If no _MACHINENAME_ is specified a random one will be generated. > Use this flag to manually specify an ASCII _MACHINENAME_ to use. If no _MACHINENAME_ is specified a random one will be generated.
-y or --async-msg -y or --async-msg
> Prints pretty / logging messages asynchronously. Desactivated by default. > Prints pretty / logging messages asynchronously. Deactivated by default.
-V or --loglevel <{CRITICAL, ERROR, WARNING, INFO, DEBUG, MINI}> -V or --loglevel <{CRITICAL, ERROR, WARNING, INFO, DEBUG, MINI}>
> Use this flag to set a logging loglevel. The default is _ERROR_. > Use this flag to set a logging loglevel. The default is _ERROR_.
@ -186,11 +187,10 @@ activate regardless of CMID being unique for a subset of specific machines or no
You can enable same _pykms_Server.py_ suboptions of `-F`. You can enable same _pykms_Server.py_ suboptions of `-F`.
-S or --logsize <MAXSIZE> -S or --logsize <MAXSIZE>
> Use this flag to set a maximum size (in MB) to the output log file. Desactivated by default. > Use this flag to set a maximum size (in MB) to the output log file. Deactivated by default.
## Activation Procedure ## Activation Procedure
Briefly the product asks for a key during installation, so it needs you to enter the GVLK. Then the user can set connection parameters, while KMS server must already be running on server machine. Finally with specific The product asks for a key during installation, so it needs you to enter the GVLK. Then the user can set connection parameters, while KMS server must already be running on server machine. Finally with specific commands, activation occurs automatically and can be extended later every time for another 180 (or 30 or 45) days.
commands activation occurs automatically and can be extended later every time for another 180 (or 30 or 45) days.
### Windows ### Windows
*** ***
@ -203,7 +203,7 @@ The `//nologo` option of `cscript` was used only to hide the startup logo.
0. Run a Command Prompt as Administrator (you are directly in `C:\Windows\System32` path). 0. Run a Command Prompt as Administrator (you are directly in `C:\Windows\System32` path).
1. This is optional, it's for unistalling any existing product key. 1. This is optional, it's for unistalling any existing product key.
2. Then put in your product's GVLK. 2. Then put in your product's GVLK.
3. Set connection parameters... 3. Set connection parameters.
4. Try online activation, but... if that fails with error `0xC004F074` youll most likely have to configure your firewall that it accepts incoming connections on TCP port 1688. So for Linux users (server-side with `pykms_Server.py` 4. Try online activation, but... if that fails with error `0xC004F074` youll most likely have to configure your firewall that it accepts incoming connections on TCP port 1688. So for Linux users (server-side with `pykms_Server.py`
running): `sudo ufw allow 1688` (to remove this rule `sudo ufw delete allow 1688`) running): `sudo ufw allow 1688` (to remove this rule `sudo ufw delete allow 1688`)
5. Attempt online activation (now with traffic on 1688 enabled). 5. Attempt online activation (now with traffic on 1688 enabled).

View File

@ -107,8 +107,8 @@ class kmsBase:
return 4 + (((~bodyLength & 3) + 1) & 3) return 4 + (((~bodyLength & 3) + 1) & 3)
def serverLogic(self, kmsRequest): def serverLogic(self, kmsRequest):
if self.srv_config['sqlite'] and self.srv_config['dbSupport']: if self.srv_config['sqlite']:
self.dbName = sql_initialize() sql_initialize(self.srv_config['sqlite'])
pretty_printer(num_text = 15, where = "srv") pretty_printer(num_text = 15, where = "srv")
kmsRequest = byterize(kmsRequest) kmsRequest = byterize(kmsRequest)
@ -210,8 +210,8 @@ could be detected as not genuine !{end}" %currentClientCount)
'status' : infoDict["licenseStatus"], 'status' : infoDict["licenseStatus"],
'product' : infoDict["skuId"]}) 'product' : infoDict["skuId"]})
if self.srv_config['sqlite'] and self.srv_config['dbSupport']: if self.srv_config['sqlite']:
sql_update(self.dbName, infoDict) sql_update(self.srv_config['sqlite'], infoDict)
return self.createKmsResponse(kmsRequest, currentClientCount) return self.createKmsResponse(kmsRequest, currentClientCount)
@ -233,8 +233,8 @@ could be detected as not genuine !{end}" %currentClientCount)
response['vLActivationInterval'] = self.srv_config["activation"] response['vLActivationInterval'] = self.srv_config["activation"]
response['vLRenewalInterval'] = self.srv_config["renewal"] response['vLRenewalInterval'] = self.srv_config["renewal"]
if self.srv_config['sqlite'] and self.srv_config['dbSupport']: if self.srv_config['sqlite']:
response = sql_update_epid(self.dbName, kmsRequest, response) response = sql_update_epid(self.srv_config['sqlite'], kmsRequest, response)
loggersrv.info("Server ePID: %s" % response["kmsEpid"].decode('utf-16le')) loggersrv.info("Server ePID: %s" % response["kmsEpid"].decode('utf-16le'))

View File

@ -59,7 +59,7 @@ clt_options = {
'def' : None, 'des' : "cmid"}, 'def' : None, 'des' : "cmid"},
'name' : {'help' : 'Use this flag to manually specify an ASCII machine name to use. If no machine name is specified a random one \ 'name' : {'help' : 'Use this flag to manually specify an ASCII machine name to use. If no machine name is specified a random one \
will be generated.', 'def' : None, 'des' : "machine"}, will be generated.', 'def' : None, 'des' : "machine"},
'asyncmsg' : {'help' : 'Prints pretty / logging messages asynchronously. Desactivated by default.', 'asyncmsg' : {'help' : 'Prints pretty / logging messages asynchronously. Deactivated by default.',
'def' : False, 'des' : "asyncmsg"}, 'def' : False, 'des' : "asyncmsg"},
'llevel' : {'help' : 'Use this option to set a log level. The default is \"ERROR\".', 'def' : "ERROR", 'des' : "loglevel", 'llevel' : {'help' : 'Use this option to set a log level. The default is \"ERROR\".', 'def' : "ERROR", 'des' : "loglevel",
'choi' : ["CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG", "MINI"]}, 'choi' : ["CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG", "MINI"]},
@ -67,7 +67,7 @@ will be generated.', 'def' : None, 'des' : "machine"},
Type \"STDOUT\" to view log info on stdout. Type \"FILESTDOUT\" to combine previous actions. \ Type \"STDOUT\" to view log info on stdout. Type \"FILESTDOUT\" to combine previous actions. \
Use \"STDOUTOFF\" to disable stdout messages. Use \"FILEOFF\" if you not want to create logfile.', Use \"STDOUTOFF\" to disable stdout messages. Use \"FILEOFF\" if you not want to create logfile.',
'def' : os.path.join('.', 'pykms_logclient.log'), 'des' : "logfile"}, 'def' : os.path.join('.', 'pykms_logclient.log'), 'des' : "logfile"},
'lsize' : {'help' : 'Use this flag to set a maximum size (in MB) to the output log file. Desactivated by default.', 'def' : 0, 'des': "logsize"}, 'lsize' : {'help' : 'Use this flag to set a maximum size (in MB) to the output log file. Deactivated by default.', 'def' : 0, 'des': "logsize"},
} }
def client_options(): def client_options():

View File

@ -188,13 +188,14 @@ for server OSes and Office >=5', 'def' : None, 'des' : "clientcount"},
'def' : 120, 'des': "activation"}, 'def' : 120, 'des': "activation"},
'renewal' : {'help' : 'Use this option to specify the renewal interval (in minutes). Default is \"10080\" minutes (7 days).', 'renewal' : {'help' : 'Use this option to specify the renewal interval (in minutes). Default is \"10080\" minutes (7 days).',
'def' : 1440 * 7, 'des' : "renewal"}, 'def' : 1440 * 7, 'des' : "renewal"},
'sql' : {'help' : 'Use this option to store request information from unique clients in an SQLite database. Desactivated by default.', 'sql' : {'help' : 'Use this option to store request information from unique clients in an SQLite database. Deactivated by default. \
If enabled the default .db file is \"pykms_database.db\". You can also provide a specific location.',
'def' : False, 'des' : "sqlite"}, 'def' : False, 'des' : "sqlite"},
'hwid' : {'help' : 'Use this option to specify a HWID. The HWID must be an 16-character string of hex characters. \ 'hwid' : {'help' : 'Use this option to specify a HWID. The HWID must be an 16-character string of hex characters. \
The default is \"364F463A8863D35F\" or type \"RANDOM\" to auto generate the HWID.', 'def' : "364F463A8863D35F", 'des' : "hwid"}, The default is \"364F463A8863D35F\" or type \"RANDOM\" to auto generate the HWID.', 'def' : "364F463A8863D35F", 'des' : "hwid"},
'time0' : {'help' : 'Maximum inactivity time (in seconds) after which the connection with the client is closed. If \"None\" (default) serve forever.', 'time0' : {'help' : 'Maximum inactivity time (in seconds) after which the connection with the client is closed. If \"None\" (default) serve forever.',
'def' : None, 'des' : "timeoutidle"}, 'def' : None, 'des' : "timeoutidle"},
'asyncmsg' : {'help' : 'Prints pretty / logging messages asynchronously. Desactivated by default.', 'asyncmsg' : {'help' : 'Prints pretty / logging messages asynchronously. Deactivated by default.',
'def' : False, 'des' : "asyncmsg"}, 'def' : False, 'des' : "asyncmsg"},
'llevel' : {'help' : 'Use this option to set a log level. The default is \"ERROR\".', 'def' : "ERROR", 'des' : "loglevel", 'llevel' : {'help' : 'Use this option to set a log level. The default is \"ERROR\".', 'def' : "ERROR", 'des' : "loglevel",
'choi' : ["CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG", "MINI"]}, 'choi' : ["CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG", "MINI"]},
@ -202,7 +203,7 @@ The default is \"364F463A8863D35F\" or type \"RANDOM\" to auto generate the HWID
Type \"STDOUT\" to view log info on stdout. Type \"FILESTDOUT\" to combine previous actions. \ Type \"STDOUT\" to view log info on stdout. Type \"FILESTDOUT\" to combine previous actions. \
Use \"STDOUTOFF\" to disable stdout messages. Use \"FILEOFF\" if you not want to create logfile.', Use \"STDOUTOFF\" to disable stdout messages. Use \"FILEOFF\" if you not want to create logfile.',
'def' : os.path.join('.', 'pykms_logserver.log'), 'des' : "logfile"}, 'def' : os.path.join('.', 'pykms_logserver.log'), 'des' : "logfile"},
'lsize' : {'help' : 'Use this flag to set a maximum size (in MB) to the output log file. Desactivated by default.', 'def' : 0, 'des': "logsize"}, 'lsize' : {'help' : 'Use this flag to set a maximum size (in MB) to the output log file. Deactivated by default.', 'def' : 0, 'des': "logsize"},
} }
def server_options(): def server_options():
@ -219,8 +220,8 @@ def server_options():
default = srv_options['activation']['def'], help = srv_options['activation']['help'], type = int) default = srv_options['activation']['def'], help = srv_options['activation']['help'], type = int)
server_parser.add_argument("-r", "--renewal-interval", action = "store", dest = srv_options['renewal']['des'], server_parser.add_argument("-r", "--renewal-interval", action = "store", dest = srv_options['renewal']['des'],
default = srv_options['renewal']['def'], help = srv_options['renewal']['help'], type = int) default = srv_options['renewal']['def'], help = srv_options['renewal']['help'], type = int)
server_parser.add_argument("-s", "--sqlite", action = "store_true", dest = srv_options['sql']['des'], server_parser.add_argument("-s", "--sqlite", nargs = "?", dest = srv_options['sql']['des'], const = True,
default = srv_options['sql']['def'], help = srv_options['sql']['help']) default = srv_options['sql']['def'], help = srv_options['sql']['help'], type = str)
server_parser.add_argument("-w", "--hwid", action = "store", dest = srv_options['hwid']['des'], default = srv_options['hwid']['def'], server_parser.add_argument("-w", "--hwid", action = "store", dest = srv_options['hwid']['des'], default = srv_options['hwid']['def'],
help = srv_options['hwid']['help'], type = str) help = srv_options['hwid']['help'], type = str)
server_parser.add_argument("-t0", "--timeout-idle", action = "store", dest = srv_options['time0']['des'], default = srv_options['time0']['def'], server_parser.add_argument("-t0", "--timeout-idle", action = "store", dest = srv_options['time0']['des'], default = srv_options['time0']['def'],
@ -298,7 +299,6 @@ def server_options():
except KmsParserException as e: except KmsParserException as e:
pretty_printer(put_text = "{reverse}{red}{bold}%s. Exiting...{end}" %str(e), to_exit = True) pretty_printer(put_text = "{reverse}{red}{bold}%s. Exiting...{end}" %str(e), to_exit = True)
class Etrigan_Check(Etrigan_check): class Etrigan_Check(Etrigan_check):
def emit_opt_err(self, msg): def emit_opt_err(self, msg):
pretty_printer(put_text = "{reverse}{red}{bold}%s{end}" %msg, to_exit = True) pretty_printer(put_text = "{reverse}{red}{bold}%s{end}" %msg, to_exit = True)
@ -316,7 +316,7 @@ class Etrigan(Etrigan):
def server_daemon(): def server_daemon():
if 'etrigan' in srv_config.values(): if 'etrigan' in srv_config.values():
path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'pykms_config.pickle') path = os.path.join('.', 'pykms_config.pickle')
if srv_config['operation'] in ['stop', 'restart', 'status'] and len(sys.argv[1:]) > 2: if srv_config['operation'] in ['stop', 'restart', 'status'] and len(sys.argv[1:]) > 2:
pretty_printer(put_text = "{reverse}{red}{bold}too much arguments with etrigan '%s'. Exiting...{end}" %srv_config['operation'], pretty_printer(put_text = "{reverse}{red}{bold}too much arguments with etrigan '%s'. Exiting...{end}" %srv_config['operation'],
@ -394,15 +394,18 @@ def server_check():
srv_config['lcid'] = check_lcid(srv_config['lcid'], loggersrv.warning) srv_config['lcid'] = check_lcid(srv_config['lcid'], loggersrv.warning)
# Check sqlite. # Check sqlite.
try: if srv_config['sqlite']:
import sqlite3 if (isinstance(srv_config['sqlite'], str)) and (not srv_config['sqlite'].lower().endswith('.db')):
except: pretty_printer(log_obj = loggersrv.error, to_exit = True,
pretty_printer(log_obj = loggersrv.warning, put_text = "{reverse}{red}{bold}Not a sqlite file (.db).{end}")
put_text = "{reverse}{yellow}{bold}Module 'sqlite3' is not installed, database support disabled.{end}") try:
srv_config['dbSupport'] = False import sqlite3
else: if isinstance(srv_config['sqlite'], bool):
srv_config['dbSupport'] = True srv_config['sqlite'] = os.path.join('.', 'pykms_database.db')
except ImportError:
pretty_printer(log_obj = loggersrv.warning,
put_text = "{reverse}{yellow}{bold}Module 'sqlite3' is not installed, database support disabled.{end}")
srv_config['sqlite'] = False
# Check other specific server options. # Check other specific server options.
list_dest = ['clientcount', 'timeoutidle'] list_dest = ['clientcount', 'timeoutidle']

View File

@ -5,9 +5,9 @@ import logging
# sqlite3 is optional. # sqlite3 is optional.
try: try:
import sqlite3 import sqlite3
except ImportError: except ImportError:
pass pass
from pykms_Format import pretty_printer from pykms_Format import pretty_printer
@ -15,87 +15,84 @@ from pykms_Format import pretty_printer
loggersrv = logging.getLogger('logsrv') loggersrv = logging.getLogger('logsrv')
def sql_initialize(): def sql_initialize(dbName):
dbName = 'clients.db' if not os.path.isfile(dbName):
if not os.path.isfile(dbName): # Initialize the database.
# Initialize the database. con = None
con = None try:
try: con = sqlite3.connect(dbName)
con = sqlite3.connect(dbName) cur = con.cursor()
cur = con.cursor() cur.execute("CREATE TABLE clients(clientMachineId TEXT, machineName TEXT, applicationId TEXT, skuId TEXT, \
cur.execute("CREATE TABLE clients(clientMachineId TEXT, machineName TEXT, applicationId TEXT, skuId TEXT, \
licenseStatus TEXT, lastRequestTime INTEGER, kmsEpid TEXT, requestCount INTEGER)") licenseStatus TEXT, lastRequestTime INTEGER, kmsEpid TEXT, requestCount INTEGER)")
except sqlite3.Error as e: except sqlite3.Error as e:
pretty_printer(log_obj = loggersrv.error, to_exit = True, pretty_printer(log_obj = loggersrv.error, to_exit = True,
put_text = "{reverse}{red}{bold}%s. Exiting...{end}" %str(e)) put_text = "{reverse}{red}{bold}%s. Exiting...{end}" %str(e))
finally: finally:
if con: if con:
con.commit() con.commit()
con.close() con.close()
return dbName
def sql_update(dbName, infoDict): def sql_update(dbName, infoDict):
con = None con = None
try: try:
con = sqlite3.connect(dbName) con = sqlite3.connect(dbName)
cur = con.cursor() cur = con.cursor()
cur.execute("SELECT * FROM clients WHERE clientMachineId=:clientMachineId;", infoDict) cur.execute("SELECT * FROM clients WHERE clientMachineId=:clientMachineId;", infoDict)
try: try:
data = cur.fetchone() data = cur.fetchone()
if not data: if not data:
# Insert row. # Insert row.
cur.execute("INSERT INTO clients (clientMachineId, machineName, applicationId, \ cur.execute("INSERT INTO clients (clientMachineId, machineName, applicationId, \
skuId, licenseStatus, lastRequestTime, requestCount) VALUES (:clientMachineId, :machineName, :appId, :skuId, :licenseStatus, :requestTime, 1);", infoDict) skuId, licenseStatus, lastRequestTime, requestCount) VALUES (:clientMachineId, :machineName, :appId, :skuId, :licenseStatus, :requestTime, 1);", infoDict)
else: else:
# Update data. # Update data.
if data[1] != infoDict["machineName"]: if data[1] != infoDict["machineName"]:
cur.execute("UPDATE clients SET machineName=:machineName WHERE clientMachineId=:clientMachineId;", infoDict) cur.execute("UPDATE clients SET machineName=:machineName WHERE clientMachineId=:clientMachineId;", infoDict)
if data[2] != infoDict["appId"]: if data[2] != infoDict["appId"]:
cur.execute("UPDATE clients SET applicationId=:appId WHERE clientMachineId=:clientMachineId;", infoDict) cur.execute("UPDATE clients SET applicationId=:appId WHERE clientMachineId=:clientMachineId;", infoDict)
if data[3] != infoDict["skuId"]: if data[3] != infoDict["skuId"]:
cur.execute("UPDATE clients SET skuId=:skuId WHERE clientMachineId=:clientMachineId;", infoDict) cur.execute("UPDATE clients SET skuId=:skuId WHERE clientMachineId=:clientMachineId;", infoDict)
if data[4] != infoDict["licenseStatus"]: if data[4] != infoDict["licenseStatus"]:
cur.execute("UPDATE clients SET licenseStatus=:licenseStatus WHERE clientMachineId=:clientMachineId;", infoDict) cur.execute("UPDATE clients SET licenseStatus=:licenseStatus WHERE clientMachineId=:clientMachineId;", infoDict)
if data[5] != infoDict["requestTime"]: if data[5] != infoDict["requestTime"]:
cur.execute("UPDATE clients SET lastRequestTime=:requestTime WHERE clientMachineId=:clientMachineId;", infoDict) cur.execute("UPDATE clients SET lastRequestTime=:requestTime WHERE clientMachineId=:clientMachineId;", infoDict)
# Increment requestCount # Increment requestCount
cur.execute("UPDATE clients SET requestCount=requestCount+1 WHERE clientMachineId=:clientMachineId;", infoDict) cur.execute("UPDATE clients SET requestCount=requestCount+1 WHERE clientMachineId=:clientMachineId;", infoDict)
except sqlite3.Error as e: except sqlite3.Error as e:
pretty_printer(log_obj = loggersrv.error, to_exit = True, pretty_printer(log_obj = loggersrv.error, to_exit = True,
put_text = "{reverse}{red}{bold}%s. Exiting...{end}" %str(e)) put_text = "{reverse}{red}{bold}%s. Exiting...{end}" %str(e))
except sqlite3.Error as e: except sqlite3.Error as e:
pretty_printer(log_obj = loggersrv.error, to_exit = True, pretty_printer(log_obj = loggersrv.error, to_exit = True,
put_text = "{reverse}{red}{bold}%s. Exiting...{end}" %str(e)) put_text = "{reverse}{red}{bold}%s. Exiting...{end}" %str(e))
finally: finally:
if con: if con:
con.commit() con.commit()
con.close() con.close()
def sql_update_epid(dbName, kmsRequest, response): def sql_update_epid(dbName, kmsRequest, response):
cmid = str(kmsRequest['clientMachineId'].get()) cmid = str(kmsRequest['clientMachineId'].get())
con = None con = None
try: try:
con = sqlite3.connect(dbName) con = sqlite3.connect(dbName)
cur = con.cursor() cur = con.cursor()
cur.execute("SELECT * FROM clients WHERE clientMachineId=?;", [cmid]) cur.execute("SELECT * FROM clients WHERE clientMachineId=?;", [cmid])
try: try:
data = cur.fetchone() data = cur.fetchone()
if data[6]: if data[6]:
response["kmsEpid"] = data[6].encode('utf-16le') response["kmsEpid"] = data[6].encode('utf-16le')
else: else:
cur.execute("UPDATE clients SET kmsEpid=? WHERE clientMachineId=?;", (str(response["kmsEpid"].decode('utf-16le')), cur.execute("UPDATE clients SET kmsEpid=? WHERE clientMachineId=?;", (str(response["kmsEpid"].decode('utf-16le')),
cmid)) cmid))
except sqlite3.Error as e: except sqlite3.Error as e:
pretty_printer(log_obj = loggersrv.error, to_exit = True, pretty_printer(log_obj = loggersrv.error, to_exit = True,
put_text = "{reverse}{red}{bold}%s. Exiting...{end}" %str(e)) put_text = "{reverse}{red}{bold}%s. Exiting...{end}" %str(e))
except sqlite3.Error as e: except sqlite3.Error as e:
pretty_printer(log_obj = loggersrv.error, to_exit = True, pretty_printer(log_obj = loggersrv.error, to_exit = True,
put_text = "{reverse}{red}{bold}%s. Exiting...{end}" %str(e)) put_text = "{reverse}{red}{bold}%s. Exiting...{end}" %str(e))
finally: finally:
if con: if con:
con.commit() con.commit()
con.close() con.close()
return response return response