Skip to content

Commit c9a9a96

Browse files
authored
Merge pull request #24 from konstruktoid/refactor
Refactor
2 parents 3192573 + f496470 commit c9a9a96

File tree

6 files changed

+93
-157
lines changed

6 files changed

+93
-157
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
__pycache__
22
.sonar
33
.scannerwork
4+
.vagrant/

README.md

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -64,15 +64,19 @@ sites:
6464
- "cloudflare.com"
6565
- "duckduckgo.com"
6666
- "github.com"
67+
- "proton.me"
6768
- "stackoverflow.com"
6869
- "xkcd.com"
6970
user_agents:
7071
- "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36"
72+
- "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36"
73+
- "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.2 Safari/605.1.15"
7174
- "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 YaBrowser/19.6.2.599 Yowser/2.5 Safari/537.36"
75+
- "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36 Edg/108.0.1462.54"
76+
- "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:108.0) Gecko/20100101 Firefox/108.0"
7277
- "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko"
7378
- "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36"
7479
- "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:66.0) Gecko/20100101 Firefox/66.0"
75-
...
7680
```
7781
7882
The above configuration will make `tymely` randomly choose a site and user agent
@@ -82,18 +86,12 @@ from the lists presented in the file, and verbosely return any information.
8286
$ ./tymely.py -c tymely.yaml -t
8387
Verbose mode enabled
8488
Configuration file: tymely.yaml
85-
{'verbose': 1, 'sites': ['cloudflare.com', 'duckduckgo.com', 'github.com', 'stackoverflow.com', 'xkcd.com'], 'user_agents': ['Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.
86-
169 Safari/537.36', 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 YaBrowser/19.6.2.599 Yowser/2.5 Safari/537.36', 'Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like
87-
Gecko', 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36', 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:66.0) Gecko/20100101 Firefox/66.0']}
88-
URL: duckduckgo.com
89-
User agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36
90-
Response headers: HTTPHeaderDict({'Server': 'nginx', 'Date': 'Fri, 02 Oct 2020 17:45:11 GMT', 'Content-Type': 'text/html; charset=UTF-8', 'Content-Length': '5763', 'Connection': 'keep-alive', 'Vary': 'Accept-Encoding', 'ETag': '"5
91-
f761b76-1683"', 'Strict-Transport-Security': 'max-age=31536000', 'X-Frame-Options': 'SAMEORIGIN', 'Content-Security-Policy': "default-src https: blob: data: 'unsafe-inline' 'unsafe-eval'; frame-ancestors 'self'", 'X-XSS-Protection
92-
': '1;mode=block', 'X-Content-Type-Options': 'nosniff', 'Referrer-Policy': 'origin', 'Expect-CT': 'max-age=0', 'Expires': 'Fri, 02 Oct 2020 17:45:10 GMT', 'Cache-Control': 'no-cache', 'Accept-Ranges': 'bytes'})
93-
Verify_mode: VerifyMode.CERT_REQUIRED
94-
TLS context options: Options.OP_NO_TLSv1_1|OP_NO_TLSv1|OP_NO_SSLv3|OP_CIPHER_SERVER_PREFERENCE|OP_ENABLE_MIDDLEBOX_COMPAT|OP_NO_COMPRESSION|OP_ALL
95-
[{'id': 50336514, 'name': 'TLS_AES_256_GCM_SHA384', 'protocol': 'TLSv1.3', 'description': 'TLS_AES_256_GCM_SHA384 TLSv1.3 Kx=any Au=any Enc=AESGCM(256) Mac=AEAD', 'strength_bits': 256, 'alg_bits': 256, 'aead': True, 'symmetric': 'aes-256-gcm', 'digest': None, 'kea': 'kx-any', 'auth': 'auth-any'},{...}]
96-
Fri, 02 Oct 2020 17:45:11 GMT returned but not set
89+
{'verbose': 1, 'sites': ['cloudflare.com', 'duckduckgo.com', 'github.com', 'proton.me', 'stackoverflow.com', 'xkcd.com'], 'user_agents': ['Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.2 Safari/605.1.15', 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 YaBrowser/19.6.2.599 Yowser/2.5 Safari/537.36', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36 Edg/108.0.1462.54', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:108.0) Gecko/20100101 Firefox/108.0', 'Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko', 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36', 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:66.0) Gecko/20100101 Firefox/66.0']}
90+
URL: proton.me
91+
User agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:108.0) Gecko/20100101 Firefox/108.0
92+
Response headers: {'date': 'Wed, 28 Dec 2022 15:38:52 GMT', 'set-cookie': 'Session-Id=Y6xjDCybqTYU-tqqBvhDmwAAABo; Domain=proton.me; Path=/; HttpOnly; Secure; Max-Age=7776000, Tag=default; Path=/; Secure; Max-Age=7776000', 'last-modified': 'Wed, 28 Dec 2022 10:24:00 GMT', 'accept-ranges': 'bytes', 'cache-control': 'max-age=0, no-cache, no-store, must-revalidate', 'expires': 'Wed, 11 Jan 1984 05:00:00 GMT', 'vary': 'Accept-Encoding', 'content-encoding': 'gzip', 'pragma': 'no-cache', 'content-length': '21937', 'content-type': 'text/html; charset=utf-8', 'content-security-policy-report-only': "default-src 'self'; media-src https://static.zdassets.com; connect-src 'self' wss: https://protonmail.zendesk.com https://ekr.zdassets.com blob: https://account.proton.me https://reports.proton.me https://*.algolia.net https://*.algolianet.com; script-src 'self' blob: 'unsafe-eval' 'unsafe-inline' https://static.zdassets.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: https:; object-src 'self' data: blob:; frame-src 'self' data: blob: https://www.youtube-nocookie.com; child-src 'self' data: blob:; report-uri https://reports.proton.me/reports/csp; frame-ancestors 'self';", 'strict-transport-security': 'max-age=31536000; includeSubDomains; preload', 'expect-ct': 'max-age=2592000, enforce, report-uri="https://reports.protonmail.com/reports/tls"', 'public-key-pins-report-only': 'pin-sha256="CT56BhOTmj5ZIPgb/xD5mH8rY3BLo/MlhP7oPyJUEDo="; report-uri="https://reports.protonmail.com/reports/tls"', 'x-frame-options': 'sameorigin', 'x-content-type-options': 'nosniff', 'x-xss-protection': '0', 'referrer-policy': 'strict-origin-when-cross-origin', 'x-permitted-cross-domain-policies': 'none', 'onion-location': 'https://protonmailrmez3lotccipshtkleegetolb73fuirgj7r4o4vfu7ozyd.onion/'}
93+
Verify_mode: /etc/ssl/certs/ca-certificates.crt
94+
Wed, 28 Dec 2022 15:38:52 GMT from proton.me returned but not set
9795
```
9896
9997
### Related documentation

Vagrantfile

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ Vagrant.configure("2") do |config|
77
end
88

99
config.vm.boot_timeout = 600
10-
config.vm.network "private_network", ip:"10.2.3.55"
1110
config.ssh.insert_key = true
12-
config.vm.box = "ubuntu/focal64"
11+
config.vm.box = "ubuntu/jammy64"
1312
end

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@ argparse
22
certifi
33
datetime
44
pyyaml
5-
urllib3
5+
requests

tymely/tymely.py

Lines changed: 74 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -5,30 +5,21 @@
55
import datetime
66
import os
77
import random
8-
import shutil
9-
import ssl
10-
import subprocess # nosec B404
8+
import subprocess # nosec B404,S404
119
import sys
10+
import shutil
11+
import requests
1212
import certifi
13-
import urllib3
1413
import yaml
1514

16-
__minimal__ = "{'verbose': 0}"
17-
__url__ = "duckduckgo.com"
18-
__version__ = "0.0.1"
19-
20-
ARGS = None
21-
CONF = None
22-
EXCEPTION_STR = "Exception:"
23-
RESPONSE = None
24-
URL = None
25-
USER_AGENT = None
15+
__version__ = "0.1.0"
2616

2717

2818
def arguments():
29-
"""Command line arguments and help information."""
30-
global ARGS
31-
19+
"""
20+
Parse command line arguments using argparse module and return the parsed
21+
arguments.
22+
"""
3223
parser = argparse.ArgumentParser(
3324
description="tymely fetches HTTP-date over HTTPS and sets the system time",
3425
epilog="version: " + __version__,
@@ -43,167 +34,110 @@ def arguments():
4334
action="store_true",
4435
)
4536

46-
ARGS = parser.parse_args()
47-
37+
return parser.parse_args()
4838

49-
def config():
50-
"""Returns CONF with yaml configuration files or default values."""
51-
global CONF
5239

40+
def config(args):
41+
"""
42+
Read and parse the configuration file specified in the command line arguments.
43+
Return the configuration as a dictionary.
44+
"""
5345
try:
54-
if ARGS.config:
55-
if not os.path.isfile(ARGS.config):
56-
print(ARGS.config, "can't be found.")
46+
if args.config:
47+
if not os.path.isfile(args.config):
48+
print(args.config, "can't be found.")
5749
sys.exit(1)
5850
else:
59-
with open(ARGS.config, "r", encoding="utf-8") as args_file:
60-
CONF = yaml.safe_load(args_file)
61-
conf_file = ARGS.config
51+
with open(args.config, "r", encoding="utf-8") as args_file:
52+
conf = yaml.safe_load(args_file)
6253
else:
63-
CONF = yaml.safe_load(__minimal__)
64-
conf_file = "None"
54+
conf = {"verbose": 0}
6555

66-
if CONF.get("verbose", 0):
56+
if conf.get("verbose", 0):
6757
print("Verbose mode enabled", file=sys.stdout)
68-
print("Configuration file:", conf_file, file=sys.stdout)
69-
print(CONF, file=sys.stdout)
70-
71-
except IOError as exception_string:
72-
print(EXCEPTION_STR, str(exception_string), file=sys.stderr)
73-
sys.exit(1)
74-
58+
print("Configuration file:", args.config, file=sys.stdout)
59+
print(conf, file=sys.stdout)
7560
except KeyError as exception_string:
76-
print(EXCEPTION_STR, str(exception_string), file=sys.stderr)
61+
print("Exception:", str(exception_string), file=sys.stderr)
7762
sys.exit(1)
78-
7963
except UnboundLocalError as exception_string:
80-
print(EXCEPTION_STR, str(exception_string), file=sys.stderr)
64+
print("Exception:", str(exception_string), file=sys.stderr)
8165
sys.exit(1)
66+
return conf
8267

83-
return CONF
8468

69+
def get_site_and_agent(conf):
70+
"""
71+
Choose a site and user agent string from the configuration dictionary.
72+
Return the site URL and user agent string.
73+
"""
74+
user_agent = None
8575

86-
def sites():
87-
"""Get site URLs from configuration file or use default."""
88-
global URL
76+
if conf.get("sites"):
77+
url = conf.get("sites", False)
78+
url = random.SystemRandom().choice(url)
79+
else:
80+
url = "duckduckgo.com"
8981

90-
try:
91-
system_random = random.SystemRandom()
92-
URL = CONF.get("sites", False)
93-
if URL:
94-
URL = system_random.choice(CONF["sites"])
95-
else:
96-
URL = __url__
82+
tymely_agent = "tymely/" + __version__
83+
user_agent = conf.get("user_agents", tymely_agent)
84+
if user_agent != tymely_agent:
85+
user_agent = random.SystemRandom().choice(conf["user_agents"])
9786

98-
except UnboundLocalError as exception_string:
99-
print(EXCEPTION_STR, str(exception_string), file=sys.stderr)
100-
sys.exit(1)
87+
if conf.get("verbose", 0):
88+
print("URL:", url, file=sys.stdout)
89+
print("User agent:", user_agent, file=sys.stdout)
10190

102-
if CONF.get("verbose", 0):
103-
print("URL:", URL, file=sys.stdout)
91+
return url, user_agent
10492

105-
return URL
10693

107-
108-
def user_agents():
109-
"""Get user agents from configuration file or use default."""
110-
global USER_AGENT
94+
def main():
95+
"""
96+
Main function that fetches the current date over HTTPS, sets the system time
97+
if not in test mode, and prints the date if in test mode.
98+
"""
99+
args = arguments()
100+
conf = config(args)
101+
url, user_agent = get_site_and_agent(conf)
111102

112103
try:
113-
system_random = random.SystemRandom()
114-
115-
try:
116-
tymely_version = __version__
117-
tymely_agent = "tymely/" + tymely_version
118-
119-
USER_AGENT = CONF.get("user_agents", tymely_agent)
120-
if USER_AGENT != tymely_agent:
121-
USER_AGENT = system_random.choice(CONF["user_agents"])
122-
123-
except UnboundLocalError as exception_string:
124-
print(EXCEPTION_STR, str(exception_string), file=sys.stderr)
125-
sys.exit(1)
126-
127-
except UnboundLocalError as exception_string:
128-
print(EXCEPTION_STR, str(exception_string), file=sys.stderr)
129-
sys.exit(1)
130-
131-
if CONF.get("verbose", 0):
132-
print("User agent:", USER_AGENT, file=sys.stdout)
133-
134-
return USER_AGENT
135-
136-
137-
def connection():
138-
"""Configure TLS and return the response."""
139-
global RESPONSE
140-
141-
tls_cont = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
142-
tls_cont.options |= ssl.OP_NO_SSLv2
143-
tls_cont.options |= ssl.OP_NO_SSLv3
144-
tls_cont.options |= ssl.OP_NO_TLSv1
145-
tls_cont.options |= ssl.OP_NO_TLSv1_1
146-
147-
https = urllib3.PoolManager(
148-
ssl_context=tls_cont,
149-
cert_reqs="CERT_REQUIRED",
150-
ca_certs=certifi.where(),
151-
retries=False,
152-
timeout=1.0,
153-
)
154-
155-
try:
156-
RESPONSE = https.request(
157-
"HEAD",
158-
"https://" + URL,
159-
headers={"User-Agent": USER_AGENT},
104+
response = requests.head(
105+
"https://" + url,
106+
headers={"User-Agent": user_agent},
107+
verify=certifi.where(),
108+
allow_redirects=True,
109+
timeout=1.0,
160110
)
161111

162-
if CONF.get("verbose", 0):
163-
print("Response headers:", RESPONSE.headers, file=sys.stdout)
164-
print("Verify_mode:", tls_cont.verify_mode)
165-
print("TLS context options:", tls_cont.options)
166-
print(tls_cont.get_ciphers())
167-
168-
except urllib3.exceptions.NewConnectionError:
169-
print("Connection failed to", URL, file=sys.stderr)
112+
if conf.get("verbose", 0):
113+
print("Response headers:", response.headers, file=sys.stdout)
114+
print("Verify_mode:", requests.certs.where())
115+
except requests.exceptions.ConnectionError:
116+
print("Connection failed to", url, file=sys.stderr)
170117
sys.exit(1)
171118

172-
except UnboundLocalError as exception_string:
173-
print(EXCEPTION_STR, str(exception_string), file=sys.stderr)
174-
sys.exit(1)
175-
176-
return RESPONSE
177-
178-
179-
def http_date():
180-
"""Return the http-date from URL using USER_AGENT."""
181119
try:
182-
if RESPONSE.status != 200:
183-
print("Response code", RESPONSE.status, "from", URL, file=sys.stderr)
120+
if response.status_code != 200:
121+
print("Response code", response.status_code, "from", url, file=sys.stderr)
184122
sys.exit(1)
185123

186-
date_str = RESPONSE.headers["Date"]
124+
date_str = response.headers["Date"]
187125

188126
datetime.datetime.strptime(date_str, "%a, %d %b %Y %H:%M:%S GMT").timestamp()
189127

190-
if ARGS.test:
191-
print(date_str + " returned but not set", file=sys.stdout)
128+
if args.test:
129+
print(f"{date_str} from {url} returned but not set", file=sys.stdout)
192130
else:
193131
date_cmd = shutil.which("date")
194132
subprocess.run(
195-
[date_cmd, "-s", date_str], shell=False, check=True # nosec B603
133+
[date_cmd, "-s", date_str],
134+
shell=False, # nosec B603
135+
check=True,
196136
)
197-
198137
except UnboundLocalError as exception_string:
199-
print(EXCEPTION_STR, str(exception_string), file=sys.stderr)
138+
print("Exception:", str(exception_string), file=sys.stderr)
200139
sys.exit(1)
201140

202141

203142
if __name__ == "__main__":
204-
arguments()
205-
config()
206-
sites()
207-
user_agents()
208-
connection()
209-
http_date()
143+
main()

tymely/tymely.yaml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,16 @@ sites:
44
- "cloudflare.com"
55
- "duckduckgo.com"
66
- "github.com"
7+
- "proton.me"
78
- "stackoverflow.com"
89
- "xkcd.com"
910
user_agents:
1011
- "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36"
12+
- "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36"
13+
- "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.2 Safari/605.1.15"
1114
- "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 YaBrowser/19.6.2.599 Yowser/2.5 Safari/537.36"
15+
- "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36 Edg/108.0.1462.54"
16+
- "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:108.0) Gecko/20100101 Firefox/108.0"
1217
- "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko"
1318
- "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36"
1419
- "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:66.0) Gecko/20100101 Firefox/66.0"
15-
...

0 commit comments

Comments
 (0)