-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathproxcon.py
executable file
·378 lines (356 loc) · 14.1 KB
/
proxcon.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
#!/usr/bin/env python3
import argparse
from getpass import getpass
from ipaddress import IPv4Address
import os
import sys
from tabulate import tabulate
proxyConf = "/etc/proxychains4.conf"
dotPath = os.path.join(os.path.expanduser(f"~{os.getlogin()}"), ".proxcon")
def genParser() -> argparse.ArgumentParser:
"""Generates a CLI argument parser
@return Argument parser object
"""
parser = argparse.ArgumentParser(description="A utility for quickly " +
"switching proxychains proxies")
parser.set_defaults(temp=False)
subparsers = parser.add_subparsers()
parserSwitch = subparsers.add_parser("switch",
help="switch to a proxy definition")
parserSwitch.set_defaults(func=switch, file=proxyConf)
parserTemp = subparsers.add_parser("temp", help="switch to a temporary " +
"proxy definition")
parserTemp.set_defaults(func=switch, file=proxyConf, temp=True)
parserSwitch.set_defaults(func=switch, file=proxyConf)
parserAdd = subparsers.add_parser("add", help="add a proxy definition")
parserAdd.set_defaults(func=add, file=proxyConf)
parserUpdate = subparsers.add_parser("update",
help="update a proxy definition")
parserUpdate.add_argument('-r', "--rename", action="store",
help="new name for proxy definition")
parserUpdate.set_defaults(func=update, file=proxyConf)
for subparser in [(parserAdd, True), (parserUpdate, False),
(parserTemp, True)]:
subparser[0].add_argument('-t', "--type", action="store",
choices=["http", "raw", "socks4", "socks5"],
help="proxy type", required=subparser[1])
subparser[0].add_argument('-i', "--ipv4", action="store",
type=IPv4Address,
help="proxy server IPv4 address",
required=subparser[1])
subparser[0].add_argument('-p', "--port", action="store", type=int,
help="proxy server port",
required=subparser[1])
subparser[0].add_argument('-u', "--user", action="store",
help="username for proxy authentication")
subparser[0].add_argument('-P', "--pass", action="store_true",
dest="passw", help="a password is required " +
"to access the proxy")
parserList = subparsers.add_parser("list",
help="list all proxy definitions")
parserList.set_defaults(func=listDefs)
parserActive = subparsers.add_parser("active",
help="show active proxy definition")
parserActive.set_defaults(func=showActive, file=proxyConf)
for subparser in [parserAdd, parserUpdate, parserSwitch, parserTemp,
parserActive]:
subparser.add_argument('-f', '--file', action="store",
help="proxychains configuration file to use " +
f"(default: '{proxyConf}')")
parserDelete = subparsers.add_parser("delete",
help="delete a proxy definition")
parserDelete.set_defaults(func=delete)
for subparser in [parserSwitch, parserAdd, parserUpdate, parserDelete]:
subparser.add_argument('-b', "--batch", action="store_true",
help="Suppresses warnings/prompts for use in " +
"scripts")
subparser.add_argument("name", action="store",
help="name of proxy definition")
return parser
def yesNo(prompt: str) -> bool:
"""Prompts the user for a yes/no response
@param prompt: Prompt to display to the user
@return: True if yes, False if no
"""
yn = input(f"{prompt} (y/n): ")
if yn.lower() == 'y':
return True
elif yn.lower() == 'n':
return False
else:
return yesNo(prompt)
def checkArgs(args: argparse.Namespace) -> argparse.Namespace:
"""Validates CLI arguments
@param args: Parsed CLI arguments
@return Validated CLI arguments
"""
if hasattr(args, "ipv4") and args.ipv4 is not None:
args.ipv4 = format(args.ipv4) # Convert to string
if hasattr(args, "port") and args.port is not None:
if not 0 < args.port <= 65535:
sys.exit(f"'{args.port}' is not a valid port number")
if hasattr(args, "user") and hasattr(args, "passw"):
if args.func == add:
if args.user is None and args.passw:
sys.exit("Please specify a username (-u <username>)")
if args.user is not None and args.passw is False:
args.passw = "<PROMPT_ON_SWITCH>"
elif args.func == update:
if args.user is not None and args.passw is False and \
args.batch is False:
yn = yesNo("You have updated a username but not a password. " +
"Do you wish to keep the existing password " +
"configuration?")
if not yn:
args.passw = "<PROMPT_ON_SWITCH>"
print("Users will be prompted for the password on switch")
if hasattr(args, "passw") and args.passw:
if args.temp:
prompt = "Password: "
else:
prompt = "Password (Leave blank to prompt for password on switch): "
passw = getpass(prompt)
if not args.temp and passw == "":
args.passw = "<PROMPT_ON_SWITCH>"
elif passw == getpass("Confirm Password: "):
args.passw = passw
else:
sys.exit("Passwords did not match")
if hasattr(args, "passw") and not args.passw:
args.passw = None
return args
def checkDot():
"""Checks proxcon dot file"""
try:
open(dotPath, 'a+').close()
except:
sys.exit(f"Cannot open proxcon configuration file '{dotPath}" +
"', do you have the correct permissions?")
def checkProx(confPath: str):
"""Checks proxychains config file
@param confPath: Path to proxychains configuration file
"""
if os.path.isfile(confPath):
try:
open(confPath, 'r+').close()
except:
sys.exit(f"Cannot open proxychains configuration file '{confPath}" +
"' for writing. Do you have the correct permissions?")
else:
sys.exit(f"Proxychains configuration file '{confPath}' does not exist")
def getDefs() -> list:
"""Reads the proxcon dot file and returns a list of lines
@return List of proxy definitions
"""
with open(dotPath, 'r') as f:
lines = f.readlines()
defs = []
for line in lines:
if line.strip() == "":
continue
split = line.rstrip().split("\t")
if not 4 <= len(split) <= 6:
sys.exit(f"Line '{line}' is not a valid proxy definition")
user = split[4] if len(split) >= 5 else None
passw = split[5] if len(split) == 6 else None
defs.append({
"Name": split[0],
"Type": split[1],
"IPv4": split[2],
"Port": split[3],
"Username": user,
"Password": passw
})
return defs
def checkName(name: str, defs: list):
"""Checks if a proxy definition with a given name exists and errors if so
@param name: Name to check for
@param defs: List of proxy definitions to check against
"""
for defi in defs:
if name == defi['Name']:
sys.exit(f"A proxy definition with name '{name}' already " +
"exists")
def genOutLine(name: str, type: str, ipv4: str, port: int, user: str,
passw: str) -> str:
"""Generates an output line
@param name: Proxy definition name
@param type: Proxy type
@param ipv4: Proxy IPv4 address
@param port: Proxy port
@param user: Proxy username
@param passw: Proxy password
@return Output line
"""
proxDef = f"{name}\t{type}\t{ipv4}\t{port}"
if user is None:
user = ""
proxDef += f"\t{user}"
if passw is None:
passw = ""
proxDef += f"\t{passw}"
return proxDef
def add(args: argparse.Namespace):
"""Adds a proxy definition with the given parameters
@param args: Validated CLI arguments
"""
defs = getDefs()
checkName(args.name, defs)
proxDef = "\n" if len(defs) else ""
proxDef += genOutLine(args.name, args.type, args.ipv4, args.port, args.user,
args.passw)
with open(dotPath, 'a') as f:
f.write(proxDef)
print("Proxy definition added successfully")
if not args.batch:
if yesNo("Would you like to switch to it now?"):
switch(args)
def update(args: argparse.Namespace):
"""Updates a proxy definition with the given parameters
@param args: Validated CLI arguments
"""
defs = getDefs()
out = ""
for defi in defs:
if defi['Name'] == args.name:
if args.rename is not None:
checkName(args.rename, defs)
defi['Name'] = args.rename
if args.type is not None:
defi['Type'] = args.type
if args.ipv4 is not None:
defi['IPv4'] = args.ipv4
if args.port is not None:
defi['Port'] = args.port
if args.user is not None:
defi['Username'] = args.user
if args.passw is not None:
defi['Password'] = args.passw
out += genOutLine(defi['Name'], defi['Type'], defi['IPv4'],
defi['Port'], defi['Username'], defi['Password'])
out += "\n"
with open(dotPath, 'w') as f:
f.write(out)
print("Proxy definition updated successfully")
if not args.batch:
if yesNo("Would you like to switch to it now?"):
switch(args)
def listDefs(args: argparse.Namespace):
"""Lists proxy definitions
@param args: Validated CLI arguments
"""
defs = getDefs()
for defi in defs:
if defi['Username'] is None or defi['Username'].strip() == "":
defi['Username'] = "N/A"
if defi['Password'] is None or defi['Password'].strip() == "":
defi['Password'] = "N/A"
try:
print(tabulate([x.values() for x in defs], defs[0].keys()))
except:
sys.exit("No proxy definitions configured")
def showActive(args: argparse.Namespace):
"""Show the active proxy configuration
@Param args: Validated CLI arguments
"""
try:
with open(proxyConf, 'r') as f:
lines = f.readlines()
except:
sys.exit("Could not open proxychains configuration file '" +
f"{proxyConf}'")
proxyList = False
for line in lines:
line = line.rstrip("\n")
if proxyList and line.strip() != "" and \
not line.strip().startswith("#"):
print(line)
break
if line.strip() == "[ProxyList]":
proxyList = True
def delete(args: argparse.Namespace):
"""Deletes a specified proxy definition
@param args: Validated CLI arguments
"""
defs = getDefs()
found = False
for i, defi in enumerate(defs):
if defi['Name'] == args.name:
found = True
if args.batch:
yn = True
else:
yn = yesNo("Are you sure you want to delete proxy definition " +
f"'{args.name}'?")
if yn:
defs.pop(i)
else:
sys.exit()
if not found:
sys.exit(f"Proxy definition '{args.name}' does not exist")
out = ""
for defi in defs:
out += genOutLine(defi['Name'], defi['Type'], defi['IPv4'],
defi['Port'], defi['Username'], defi['Password'])
out += "\n"
with open(dotPath, 'w') as f:
f.write(out)
print("Proxy definition deleted successfully")
def switch(args: argparse.Namespace):
"""Switches to a specified proxy definition
@param args: Validated CLI arguments
"""
checkProx(args.file)
if not hasattr(args, "name"):
proxLine = f"{args.type}\t{args.ipv4}\t{args.port}"
if args.user is not None:
proxLine += f"\t{args.user}\t"
if args.passw is not None:
proxLine += f"{args.passw}"
else:
defs = getDefs()
proxLine = ""
for defi in defs:
if defi['Name'] == args.name:
proxLine = f"{defi['Type']}\t{defi['IPv4']}\t{defi['Port']}"
if defi['Password'] == "<PROMPT_ON_SWITCH>":
passw = getpass(f"Password for '{args.name}': ")
if passw == getpass("Confirm Password: "):
defi['Password'] = passw
if defi['Username'] is not None:
proxLine += f"\t{defi['Username']}\t"
if defi['Password'] is not None:
proxLine += f"{defi['Password']}"
break
if proxLine == "":
sys.exit(f"Proxy definition '{args.name}' does not exist")
with open(proxyConf, 'r') as f:
lines = f.readlines()
replace = False
out = ""
for line in lines:
line = line.rstrip("\n")
if replace and line.strip() != "" and not line.strip().startswith("#"):
continue
if line.strip() == "[ProxyList]":
replace = True
out += f"{line}\n"
out += f"{proxLine}\n"
with open(proxyConf, 'w') as f:
f.write(out)
print("Proxychains configuration updated successfully")
def main():
"""Main method"""
try:
parser = genParser()
if len(sys.argv) == 1:
parser.print_usage()
sys.exit()
args = parser.parse_args()
args = checkArgs(args)
checkDot()
args.func(args)
except KeyboardInterrupt:
sys.exit(0)
if __name__ == "__main__":
main()