mirror of
https://github.com/sshuttle/sshuttle.git
synced 2024-11-21 15:33:23 +01:00
Enable pylint checks
This commit is contained in:
parent
6b75d62d68
commit
f77fd22d87
3
.github/workflows/pythonpackage.yml
vendored
3
.github/workflows/pythonpackage.yml
vendored
@ -30,6 +30,9 @@ jobs:
|
|||||||
- name: Lint with flake8
|
- name: Lint with flake8
|
||||||
run: |
|
run: |
|
||||||
flake8 sshuttle tests --count --show-source --statistics
|
flake8 sshuttle tests --count --show-source --statistics
|
||||||
|
- name: Lint with pylint
|
||||||
|
run: |
|
||||||
|
pylint sshuttle
|
||||||
- name: Test with pytest
|
- name: Test with pytest
|
||||||
run: |
|
run: |
|
||||||
PYTHONPATH=$PWD pytest
|
PYTHONPATH=$PWD pytest
|
||||||
|
591
.pylintrc
Normal file
591
.pylintrc
Normal file
@ -0,0 +1,591 @@
|
|||||||
|
[MASTER]
|
||||||
|
|
||||||
|
# A comma-separated list of package or module names from where C extensions may
|
||||||
|
# be loaded. Extensions are loading into the active Python interpreter and may
|
||||||
|
# run arbitrary code.
|
||||||
|
extension-pkg-whitelist=
|
||||||
|
|
||||||
|
# Specify a score threshold to be exceeded before program exits with error.
|
||||||
|
fail-under=10.0
|
||||||
|
|
||||||
|
# Add files or directories to the blacklist. They should be base names, not
|
||||||
|
# paths.
|
||||||
|
ignore=CVS
|
||||||
|
|
||||||
|
# Add files or directories matching the regex patterns to the blacklist. The
|
||||||
|
# regex matches against base names, not paths.
|
||||||
|
ignore-patterns=
|
||||||
|
|
||||||
|
# Python code to execute, usually for sys.path manipulation such as
|
||||||
|
# pygtk.require().
|
||||||
|
#init-hook=
|
||||||
|
|
||||||
|
# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the
|
||||||
|
# number of processors available to use.
|
||||||
|
jobs=1
|
||||||
|
|
||||||
|
# Control the amount of potential inferred values when inferring a single
|
||||||
|
# object. This can help the performance when dealing with large functions or
|
||||||
|
# complex, nested conditions.
|
||||||
|
limit-inference-results=100
|
||||||
|
|
||||||
|
# List of plugins (as comma separated values of python module names) to load,
|
||||||
|
# usually to register additional checkers.
|
||||||
|
load-plugins=
|
||||||
|
|
||||||
|
# Pickle collected data for later comparisons.
|
||||||
|
persistent=yes
|
||||||
|
|
||||||
|
# When enabled, pylint would attempt to guess common misconfiguration and emit
|
||||||
|
# user-friendly hints instead of false-positive error messages.
|
||||||
|
suggestion-mode=yes
|
||||||
|
|
||||||
|
# Allow loading of arbitrary C extensions. Extensions are imported into the
|
||||||
|
# active Python interpreter and may run arbitrary code.
|
||||||
|
unsafe-load-any-extension=no
|
||||||
|
|
||||||
|
|
||||||
|
[MESSAGES CONTROL]
|
||||||
|
|
||||||
|
# Only show warnings with the listed confidence levels. Leave empty to show
|
||||||
|
# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED.
|
||||||
|
confidence=
|
||||||
|
|
||||||
|
# Disable the message, report, category or checker with the given id(s). You
|
||||||
|
# can either give multiple identifiers separated by comma (,) or put this
|
||||||
|
# option multiple times (only on the command line, not in the configuration
|
||||||
|
# file where it should appear only once). You can also use "--disable=all" to
|
||||||
|
# disable everything first and then reenable specific checks. For example, if
|
||||||
|
# you want to run only the similarities checker, you can use "--disable=all
|
||||||
|
# --enable=similarities". If you want to run only the classes checker, but have
|
||||||
|
# no Warning level messages displayed, use "--disable=all --enable=classes
|
||||||
|
# --disable=W".
|
||||||
|
disable=print-statement,
|
||||||
|
parameter-unpacking,
|
||||||
|
unpacking-in-except,
|
||||||
|
old-raise-syntax,
|
||||||
|
backtick,
|
||||||
|
long-suffix,
|
||||||
|
old-ne-operator,
|
||||||
|
old-octal-literal,
|
||||||
|
import-star-module-level,
|
||||||
|
non-ascii-bytes-literal,
|
||||||
|
raw-checker-failed,
|
||||||
|
bad-inline-option,
|
||||||
|
locally-disabled,
|
||||||
|
file-ignored,
|
||||||
|
suppressed-message,
|
||||||
|
useless-suppression,
|
||||||
|
deprecated-pragma,
|
||||||
|
use-symbolic-message-instead,
|
||||||
|
apply-builtin,
|
||||||
|
basestring-builtin,
|
||||||
|
buffer-builtin,
|
||||||
|
cmp-builtin,
|
||||||
|
coerce-builtin,
|
||||||
|
execfile-builtin,
|
||||||
|
file-builtin,
|
||||||
|
long-builtin,
|
||||||
|
raw_input-builtin,
|
||||||
|
reduce-builtin,
|
||||||
|
standarderror-builtin,
|
||||||
|
unicode-builtin,
|
||||||
|
xrange-builtin,
|
||||||
|
coerce-method,
|
||||||
|
delslice-method,
|
||||||
|
getslice-method,
|
||||||
|
setslice-method,
|
||||||
|
no-absolute-import,
|
||||||
|
old-division,
|
||||||
|
dict-iter-method,
|
||||||
|
dict-view-method,
|
||||||
|
next-method-called,
|
||||||
|
metaclass-assignment,
|
||||||
|
indexing-exception,
|
||||||
|
raising-string,
|
||||||
|
reload-builtin,
|
||||||
|
oct-method,
|
||||||
|
hex-method,
|
||||||
|
nonzero-method,
|
||||||
|
cmp-method,
|
||||||
|
input-builtin,
|
||||||
|
round-builtin,
|
||||||
|
intern-builtin,
|
||||||
|
unichr-builtin,
|
||||||
|
map-builtin-not-iterating,
|
||||||
|
zip-builtin-not-iterating,
|
||||||
|
range-builtin-not-iterating,
|
||||||
|
filter-builtin-not-iterating,
|
||||||
|
using-cmp-argument,
|
||||||
|
eq-without-hash,
|
||||||
|
div-method,
|
||||||
|
idiv-method,
|
||||||
|
rdiv-method,
|
||||||
|
exception-message-attribute,
|
||||||
|
invalid-str-codec,
|
||||||
|
sys-max-int,
|
||||||
|
bad-python3-import,
|
||||||
|
deprecated-string-function,
|
||||||
|
deprecated-str-translate-call,
|
||||||
|
deprecated-itertools-function,
|
||||||
|
deprecated-types-field,
|
||||||
|
next-method-defined,
|
||||||
|
dict-items-not-iterating,
|
||||||
|
dict-keys-not-iterating,
|
||||||
|
dict-values-not-iterating,
|
||||||
|
deprecated-operator-function,
|
||||||
|
deprecated-urllib-function,
|
||||||
|
xreadlines-attribute,
|
||||||
|
deprecated-sys-function,
|
||||||
|
exception-escape,
|
||||||
|
comprehension-escape,
|
||||||
|
too-many-locals,
|
||||||
|
too-many-branches,
|
||||||
|
no-else-return
|
||||||
|
|
||||||
|
# Enable the message, report, category or checker with the given id(s). You can
|
||||||
|
# either give multiple identifier separated by comma (,) or put this option
|
||||||
|
# multiple time (only on the command line, not in the configuration file where
|
||||||
|
# it should appear only once). See also the "--disable" option for examples.
|
||||||
|
enable=c-extension-no-member
|
||||||
|
|
||||||
|
|
||||||
|
[REPORTS]
|
||||||
|
|
||||||
|
# Python expression which should return a score less than or equal to 10. You
|
||||||
|
# have access to the variables 'error', 'warning', 'refactor', and 'convention'
|
||||||
|
# which contain the number of messages in each category, as well as 'statement'
|
||||||
|
# which is the total number of statements analyzed. This score is used by the
|
||||||
|
# global evaluation report (RP0004).
|
||||||
|
evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
|
||||||
|
|
||||||
|
# Template used to display messages. This is a python new-style format string
|
||||||
|
# used to format the message information. See doc for all details.
|
||||||
|
#msg-template=
|
||||||
|
|
||||||
|
# Set the output format. Available formats are text, parseable, colorized, json
|
||||||
|
# and msvs (visual studio). You can also give a reporter class, e.g.
|
||||||
|
# mypackage.mymodule.MyReporterClass.
|
||||||
|
output-format=text
|
||||||
|
|
||||||
|
# Tells whether to display a full report or only the messages.
|
||||||
|
reports=no
|
||||||
|
|
||||||
|
# Activate the evaluation score.
|
||||||
|
score=yes
|
||||||
|
|
||||||
|
|
||||||
|
[REFACTORING]
|
||||||
|
|
||||||
|
# Maximum number of nested blocks for function / method body
|
||||||
|
max-nested-blocks=5
|
||||||
|
|
||||||
|
# Complete name of functions that never returns. When checking for
|
||||||
|
# inconsistent-return-statements if a never returning function is called then
|
||||||
|
# it will be considered as an explicit return statement and no message will be
|
||||||
|
# printed.
|
||||||
|
never-returning-functions=sys.exit
|
||||||
|
|
||||||
|
|
||||||
|
[VARIABLES]
|
||||||
|
|
||||||
|
# List of additional names supposed to be defined in builtins. Remember that
|
||||||
|
# you should avoid defining new builtins when possible.
|
||||||
|
additional-builtins=
|
||||||
|
|
||||||
|
# Tells whether unused global variables should be treated as a violation.
|
||||||
|
allow-global-unused-variables=yes
|
||||||
|
|
||||||
|
# List of strings which can identify a callback function by name. A callback
|
||||||
|
# name must start or end with one of those strings.
|
||||||
|
callbacks=cb_,
|
||||||
|
_cb
|
||||||
|
|
||||||
|
# A regular expression matching the name of dummy variables (i.e. expected to
|
||||||
|
# not be used).
|
||||||
|
dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_
|
||||||
|
|
||||||
|
# Argument names that match this expression will be ignored. Default to name
|
||||||
|
# with leading underscore.
|
||||||
|
ignored-argument-names=_.*|^ignored_|^unused_
|
||||||
|
|
||||||
|
# Tells whether we should check for unused import in __init__ files.
|
||||||
|
init-import=no
|
||||||
|
|
||||||
|
# List of qualified module names which can have objects that can redefine
|
||||||
|
# builtins.
|
||||||
|
redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io
|
||||||
|
|
||||||
|
|
||||||
|
[TYPECHECK]
|
||||||
|
|
||||||
|
# List of decorators that produce context managers, such as
|
||||||
|
# contextlib.contextmanager. Add to this list to register other decorators that
|
||||||
|
# produce valid context managers.
|
||||||
|
contextmanager-decorators=contextlib.contextmanager
|
||||||
|
|
||||||
|
# List of members which are set dynamically and missed by pylint inference
|
||||||
|
# system, and so shouldn't trigger E1101 when accessed. Python regular
|
||||||
|
# expressions are accepted.
|
||||||
|
generated-members=
|
||||||
|
|
||||||
|
# Tells whether missing members accessed in mixin class should be ignored. A
|
||||||
|
# mixin class is detected if its name ends with "mixin" (case insensitive).
|
||||||
|
ignore-mixin-members=yes
|
||||||
|
|
||||||
|
# Tells whether to warn about missing members when the owner of the attribute
|
||||||
|
# is inferred to be None.
|
||||||
|
ignore-none=yes
|
||||||
|
|
||||||
|
# This flag controls whether pylint should warn about no-member and similar
|
||||||
|
# checks whenever an opaque object is returned when inferring. The inference
|
||||||
|
# can return multiple potential results while evaluating a Python object, but
|
||||||
|
# some branches might not be evaluated, which results in partial inference. In
|
||||||
|
# that case, it might be useful to still emit no-member and other checks for
|
||||||
|
# the rest of the inferred objects.
|
||||||
|
ignore-on-opaque-inference=yes
|
||||||
|
|
||||||
|
# List of class names for which member attributes should not be checked (useful
|
||||||
|
# for classes with dynamically set attributes). This supports the use of
|
||||||
|
# qualified names.
|
||||||
|
ignored-classes=optparse.Values,thread._local,_thread._local
|
||||||
|
|
||||||
|
# List of module names for which member attributes should not be checked
|
||||||
|
# (useful for modules/projects where namespaces are manipulated during runtime
|
||||||
|
# and thus existing member attributes cannot be deduced by static analysis). It
|
||||||
|
# supports qualified module names, as well as Unix pattern matching.
|
||||||
|
ignored-modules=
|
||||||
|
|
||||||
|
# Show a hint with possible names when a member name was not found. The aspect
|
||||||
|
# of finding the hint is based on edit distance.
|
||||||
|
missing-member-hint=yes
|
||||||
|
|
||||||
|
# The minimum edit distance a name should have in order to be considered a
|
||||||
|
# similar match for a missing member name.
|
||||||
|
missing-member-hint-distance=1
|
||||||
|
|
||||||
|
# The total number of similar names that should be taken in consideration when
|
||||||
|
# showing a hint for a missing member.
|
||||||
|
missing-member-max-choices=1
|
||||||
|
|
||||||
|
# List of decorators that change the signature of a decorated function.
|
||||||
|
signature-mutators=
|
||||||
|
|
||||||
|
|
||||||
|
[MISCELLANEOUS]
|
||||||
|
|
||||||
|
# List of note tags to take in consideration, separated by a comma.
|
||||||
|
notes=FIXME,
|
||||||
|
XXX,
|
||||||
|
TODO
|
||||||
|
|
||||||
|
# Regular expression of note tags to take in consideration.
|
||||||
|
#notes-rgx=
|
||||||
|
|
||||||
|
|
||||||
|
[SPELLING]
|
||||||
|
|
||||||
|
# Limits count of emitted suggestions for spelling mistakes.
|
||||||
|
max-spelling-suggestions=4
|
||||||
|
|
||||||
|
# Spelling dictionary name. Available dictionaries: none. To make it work,
|
||||||
|
# install the python-enchant package.
|
||||||
|
spelling-dict=
|
||||||
|
|
||||||
|
# List of comma separated words that should not be checked.
|
||||||
|
spelling-ignore-words=
|
||||||
|
|
||||||
|
# A path to a file that contains the private dictionary; one word per line.
|
||||||
|
spelling-private-dict-file=
|
||||||
|
|
||||||
|
# Tells whether to store unknown words to the private dictionary (see the
|
||||||
|
# --spelling-private-dict-file option) instead of raising a message.
|
||||||
|
spelling-store-unknown-words=no
|
||||||
|
|
||||||
|
|
||||||
|
[BASIC]
|
||||||
|
|
||||||
|
# Naming style matching correct argument names.
|
||||||
|
argument-naming-style=snake_case
|
||||||
|
|
||||||
|
# Regular expression matching correct argument names. Overrides argument-
|
||||||
|
# naming-style.
|
||||||
|
argument-rgx=
|
||||||
|
|
||||||
|
# Naming style matching correct attribute names.
|
||||||
|
attr-naming-style=snake_case
|
||||||
|
|
||||||
|
# Regular expression matching correct attribute names. Overrides attr-naming-
|
||||||
|
# style.
|
||||||
|
#attr-rgx=
|
||||||
|
|
||||||
|
# Bad variable names which should always be refused, separated by a comma.
|
||||||
|
bad-names=foo,
|
||||||
|
bar,
|
||||||
|
baz,
|
||||||
|
toto,
|
||||||
|
tutu,
|
||||||
|
tata
|
||||||
|
|
||||||
|
# Bad variable names regexes, separated by a comma. If names match any regex,
|
||||||
|
# they will always be refused
|
||||||
|
bad-names-rgxs=
|
||||||
|
|
||||||
|
# Naming style matching correct class attribute names.
|
||||||
|
class-attribute-naming-style=any
|
||||||
|
|
||||||
|
# Regular expression matching correct class attribute names. Overrides class-
|
||||||
|
# attribute-naming-style.
|
||||||
|
class-attribute-rgx=^[a-z0-9_]{1,30}$
|
||||||
|
|
||||||
|
# Naming style matching correct class names.
|
||||||
|
class-naming-style=PascalCase
|
||||||
|
|
||||||
|
# Regular expression matching correct class names. Overrides class-naming-
|
||||||
|
# style.
|
||||||
|
#class-rgx=
|
||||||
|
|
||||||
|
# Naming style matching correct constant names.
|
||||||
|
const-naming-style=UPPER_CASE
|
||||||
|
|
||||||
|
# Regular expression matching correct constant names. Overrides const-naming-
|
||||||
|
# style.
|
||||||
|
#const-rgx=
|
||||||
|
|
||||||
|
# Minimum line length for functions/classes that require docstrings, shorter
|
||||||
|
# ones are exempt.
|
||||||
|
docstring-min-length=-1
|
||||||
|
|
||||||
|
# Naming style matching correct function names.
|
||||||
|
function-naming-style=snake_case
|
||||||
|
|
||||||
|
# Regular expression matching correct function names. Overrides function-
|
||||||
|
# naming-style.
|
||||||
|
#function-rgx=
|
||||||
|
|
||||||
|
# Good variable names which should always be accepted, separated by a comma.
|
||||||
|
good-names=i,
|
||||||
|
j,
|
||||||
|
k,
|
||||||
|
ex,
|
||||||
|
Run,
|
||||||
|
_
|
||||||
|
|
||||||
|
# Good variable names regexes, separated by a comma. If names match any regex,
|
||||||
|
# they will always be accepted
|
||||||
|
good-names-rgxs=
|
||||||
|
|
||||||
|
# Include a hint for the correct naming format with invalid-name.
|
||||||
|
include-naming-hint=no
|
||||||
|
|
||||||
|
# Naming style matching correct inline iteration names.
|
||||||
|
inlinevar-naming-style=any
|
||||||
|
|
||||||
|
# Regular expression matching correct inline iteration names. Overrides
|
||||||
|
# inlinevar-naming-style.
|
||||||
|
#inlinevar-rgx=
|
||||||
|
|
||||||
|
# Naming style matching correct method names.
|
||||||
|
method-naming-style=snake_case
|
||||||
|
|
||||||
|
# Regular expression matching correct method names. Overrides method-naming-
|
||||||
|
# style.
|
||||||
|
#method-rgx=
|
||||||
|
|
||||||
|
# Naming style matching correct module names.
|
||||||
|
module-naming-style=snake_case
|
||||||
|
|
||||||
|
# Regular expression matching correct module names. Overrides module-naming-
|
||||||
|
# style.
|
||||||
|
#module-rgx=
|
||||||
|
|
||||||
|
# Colon-delimited sets of names that determine each other's naming style when
|
||||||
|
# the name regexes allow several styles.
|
||||||
|
name-group=
|
||||||
|
|
||||||
|
# Regular expression which should only match function or class names that do
|
||||||
|
# not require a docstring.
|
||||||
|
no-docstring-rgx=^_
|
||||||
|
|
||||||
|
# List of decorators that produce properties, such as abc.abstractproperty. Add
|
||||||
|
# to this list to register other decorators that produce valid properties.
|
||||||
|
# These decorators are taken in consideration only for invalid-name.
|
||||||
|
property-classes=abc.abstractproperty
|
||||||
|
|
||||||
|
# Naming style matching correct variable names.
|
||||||
|
variable-naming-style=snake_case
|
||||||
|
|
||||||
|
# Regular expression matching correct variable names. Overrides variable-
|
||||||
|
# naming-style.
|
||||||
|
variable-rgx=^[a-z0-9_]{1,30}$
|
||||||
|
|
||||||
|
|
||||||
|
[STRING]
|
||||||
|
|
||||||
|
# This flag controls whether inconsistent-quotes generates a warning when the
|
||||||
|
# character used as a quote delimiter is used inconsistently within a module.
|
||||||
|
check-quote-consistency=no
|
||||||
|
|
||||||
|
# This flag controls whether the implicit-str-concat should generate a warning
|
||||||
|
# on implicit string concatenation in sequences defined over several lines.
|
||||||
|
check-str-concat-over-line-jumps=no
|
||||||
|
|
||||||
|
|
||||||
|
[SIMILARITIES]
|
||||||
|
|
||||||
|
# Ignore comments when computing similarities.
|
||||||
|
ignore-comments=yes
|
||||||
|
|
||||||
|
# Ignore docstrings when computing similarities.
|
||||||
|
ignore-docstrings=yes
|
||||||
|
|
||||||
|
# Ignore imports when computing similarities.
|
||||||
|
ignore-imports=no
|
||||||
|
|
||||||
|
# Minimum lines number of a similarity.
|
||||||
|
min-similarity-lines=4
|
||||||
|
|
||||||
|
|
||||||
|
[LOGGING]
|
||||||
|
|
||||||
|
# The type of string formatting that logging methods do. `old` means using %
|
||||||
|
# formatting, `new` is for `{}` formatting.
|
||||||
|
logging-format-style=old
|
||||||
|
|
||||||
|
# Logging modules to check that the string format arguments are in logging
|
||||||
|
# function parameter format.
|
||||||
|
logging-modules=logging
|
||||||
|
|
||||||
|
|
||||||
|
[FORMAT]
|
||||||
|
|
||||||
|
# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
|
||||||
|
expected-line-ending-format=
|
||||||
|
|
||||||
|
# Regexp for a line that is allowed to be longer than the limit.
|
||||||
|
ignore-long-lines=^\s*(# )?<?https?://\S+>?$
|
||||||
|
|
||||||
|
# Number of spaces of indent required inside a hanging or continued line.
|
||||||
|
indent-after-paren=4
|
||||||
|
|
||||||
|
# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
|
||||||
|
# tab).
|
||||||
|
indent-string=' '
|
||||||
|
|
||||||
|
# Maximum number of characters on a single line.
|
||||||
|
max-line-length=100
|
||||||
|
|
||||||
|
# Maximum number of lines in a module.
|
||||||
|
max-module-lines=1000
|
||||||
|
|
||||||
|
# Allow the body of a class to be on the same line as the declaration if body
|
||||||
|
# contains single statement.
|
||||||
|
single-line-class-stmt=no
|
||||||
|
|
||||||
|
# Allow the body of an if to be on the same line as the test if there is no
|
||||||
|
# else.
|
||||||
|
single-line-if-stmt=no
|
||||||
|
|
||||||
|
|
||||||
|
[CLASSES]
|
||||||
|
|
||||||
|
# List of method names used to declare (i.e. assign) instance attributes.
|
||||||
|
defining-attr-methods=__init__,
|
||||||
|
__new__,
|
||||||
|
setUp,
|
||||||
|
__post_init__
|
||||||
|
|
||||||
|
# List of member names, which should be excluded from the protected access
|
||||||
|
# warning.
|
||||||
|
exclude-protected=_asdict,
|
||||||
|
_fields,
|
||||||
|
_replace,
|
||||||
|
_source,
|
||||||
|
_make
|
||||||
|
|
||||||
|
# List of valid names for the first argument in a class method.
|
||||||
|
valid-classmethod-first-arg=cls
|
||||||
|
|
||||||
|
# List of valid names for the first argument in a metaclass class method.
|
||||||
|
valid-metaclass-classmethod-first-arg=cls
|
||||||
|
|
||||||
|
|
||||||
|
[DESIGN]
|
||||||
|
|
||||||
|
# Maximum number of arguments for function / method.
|
||||||
|
max-args=5
|
||||||
|
|
||||||
|
# Maximum number of attributes for a class (see R0902).
|
||||||
|
max-attributes=7
|
||||||
|
|
||||||
|
# Maximum number of boolean expressions in an if statement (see R0916).
|
||||||
|
max-bool-expr=5
|
||||||
|
|
||||||
|
# Maximum number of branch for function / method body.
|
||||||
|
max-branches=12
|
||||||
|
|
||||||
|
# Maximum number of locals for function / method body.
|
||||||
|
max-locals=15
|
||||||
|
|
||||||
|
# Maximum number of parents for a class (see R0901).
|
||||||
|
max-parents=7
|
||||||
|
|
||||||
|
# Maximum number of public methods for a class (see R0904).
|
||||||
|
max-public-methods=20
|
||||||
|
|
||||||
|
# Maximum number of return / yield for function / method body.
|
||||||
|
max-returns=6
|
||||||
|
|
||||||
|
# Maximum number of statements in function / method body.
|
||||||
|
max-statements=50
|
||||||
|
|
||||||
|
# Minimum number of public methods for a class (see R0903).
|
||||||
|
min-public-methods=2
|
||||||
|
|
||||||
|
|
||||||
|
[IMPORTS]
|
||||||
|
|
||||||
|
# List of modules that can be imported at any level, not just the top level
|
||||||
|
# one.
|
||||||
|
allow-any-import-level=
|
||||||
|
|
||||||
|
# Allow wildcard imports from modules that define __all__.
|
||||||
|
allow-wildcard-with-all=no
|
||||||
|
|
||||||
|
# Analyse import fallback blocks. This can be used to support both Python 2 and
|
||||||
|
# 3 compatible code, which means that the block might have code that exists
|
||||||
|
# only in one or another interpreter, leading to false positives when analysed.
|
||||||
|
analyse-fallback-blocks=no
|
||||||
|
|
||||||
|
# Deprecated modules which should not be used, separated by a comma.
|
||||||
|
deprecated-modules=optparse,tkinter.tix
|
||||||
|
|
||||||
|
# Create a graph of external dependencies in the given file (report RP0402 must
|
||||||
|
# not be disabled).
|
||||||
|
ext-import-graph=
|
||||||
|
|
||||||
|
# Create a graph of every (i.e. internal and external) dependencies in the
|
||||||
|
# given file (report RP0402 must not be disabled).
|
||||||
|
import-graph=
|
||||||
|
|
||||||
|
# Create a graph of internal dependencies in the given file (report RP0402 must
|
||||||
|
# not be disabled).
|
||||||
|
int-import-graph=
|
||||||
|
|
||||||
|
# Force import order to recognize a module as part of the standard
|
||||||
|
# compatibility libraries.
|
||||||
|
known-standard-library=
|
||||||
|
|
||||||
|
# Force import order to recognize a module as part of a third party library.
|
||||||
|
known-third-party=enchant
|
||||||
|
|
||||||
|
# Couples of modules and preferred modules, separated by a comma.
|
||||||
|
preferred-modules=
|
||||||
|
|
||||||
|
|
||||||
|
[EXCEPTIONS]
|
||||||
|
|
||||||
|
# Exceptions that will emit a warning when being caught. Defaults to
|
||||||
|
# "BaseException, Exception".
|
||||||
|
overgeneral-exceptions=BaseException,
|
||||||
|
Exception
|
@ -5,3 +5,4 @@ pytest-cov==2.10.1
|
|||||||
mock==2.0.0
|
mock==2.0.0
|
||||||
flake8==3.8.4
|
flake8==3.8.4
|
||||||
pyflakes==2.2.0
|
pyflakes==2.2.0
|
||||||
|
pylint==2.6.0
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
"""
|
||||||
|
sshuttle: where transparent proxy meets VPN meets ssh
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
from sshuttle.version import version as __version__
|
from sshuttle.version import version as __version__
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
""" Sshuttle client code """
|
||||||
import errno
|
import errno
|
||||||
import re
|
import re
|
||||||
import signal
|
import signal
|
||||||
@ -40,53 +41,56 @@ except AttributeError:
|
|||||||
_extra_fd = os.open(os.devnull, os.O_RDONLY)
|
_extra_fd = os.open(os.devnull, os.O_RDONLY)
|
||||||
|
|
||||||
|
|
||||||
def got_signal(signum, frame):
|
def got_signal(signum, _frame):
|
||||||
|
""" Process client signal """
|
||||||
log('exiting on signal %d\n' % signum)
|
log('exiting on signal %d\n' % signum)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
_pidname = None
|
_PID_NAME = None
|
||||||
|
|
||||||
|
|
||||||
def check_daemon(pidfile):
|
def check_daemon(pidfile):
|
||||||
global _pidname
|
""" Check if daemon """
|
||||||
_pidname = os.path.abspath(pidfile)
|
global _PID_NAME
|
||||||
|
_PID_NAME = os.path.abspath(pidfile)
|
||||||
try:
|
try:
|
||||||
oldpid = open(_pidname).read(1024)
|
oldpid = open(_PID_NAME).read(1024)
|
||||||
except IOError as e:
|
except IOError as e:
|
||||||
if e.errno == errno.ENOENT:
|
if e.errno == errno.ENOENT:
|
||||||
return # no pidfile, ok
|
return # no pidfile, ok
|
||||||
else:
|
else:
|
||||||
raise Fatal("c : can't read %s: %s" % (_pidname, e))
|
raise Fatal("c : can't read %s: %s" % (_PID_NAME, e))
|
||||||
if not oldpid:
|
if not oldpid:
|
||||||
os.unlink(_pidname)
|
os.unlink(_PID_NAME)
|
||||||
return # invalid pidfile, ok
|
return # invalid pidfile, ok
|
||||||
oldpid = int(oldpid.strip() or 0)
|
oldpid = int(oldpid.strip() or 0)
|
||||||
if oldpid <= 0:
|
if oldpid <= 0:
|
||||||
os.unlink(_pidname)
|
os.unlink(_PID_NAME)
|
||||||
return # invalid pidfile, ok
|
return # invalid pidfile, ok
|
||||||
try:
|
try:
|
||||||
os.kill(oldpid, 0)
|
os.kill(oldpid, 0)
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
if e.errno == errno.ESRCH:
|
if e.errno == errno.ESRCH:
|
||||||
os.unlink(_pidname)
|
os.unlink(_PID_NAME)
|
||||||
return # outdated pidfile, ok
|
return # outdated pidfile, ok
|
||||||
elif e.errno == errno.EPERM:
|
elif e.errno == errno.EPERM:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
raise
|
raise
|
||||||
raise Fatal("%s: sshuttle is already running (pid=%d)"
|
raise Fatal("%s: sshuttle is already running (pid=%d)"
|
||||||
% (_pidname, oldpid))
|
% (_PID_NAME, oldpid))
|
||||||
|
|
||||||
|
|
||||||
def daemonize():
|
def daemonize():
|
||||||
|
""" Run code as daemon """
|
||||||
if os.fork():
|
if os.fork():
|
||||||
os._exit(0)
|
sys.exit(0)
|
||||||
os.setsid()
|
os.setsid()
|
||||||
if os.fork():
|
if os.fork():
|
||||||
os._exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
outfd = os.open(_pidname, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o666)
|
outfd = os.open(_PID_NAME, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o666)
|
||||||
try:
|
try:
|
||||||
os.write(outfd, b'%d\n' % os.getpid())
|
os.write(outfd, b'%d\n' % os.getpid())
|
||||||
finally:
|
finally:
|
||||||
@ -104,8 +108,9 @@ def daemonize():
|
|||||||
|
|
||||||
|
|
||||||
def daemon_cleanup():
|
def daemon_cleanup():
|
||||||
|
""" Clean up after daemon """
|
||||||
try:
|
try:
|
||||||
os.unlink(_pidname)
|
os.unlink(_PID_NAME)
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
if e.errno == errno.ENOENT:
|
if e.errno == errno.ENOENT:
|
||||||
pass
|
pass
|
||||||
@ -114,6 +119,7 @@ def daemon_cleanup():
|
|||||||
|
|
||||||
|
|
||||||
class MultiListener:
|
class MultiListener:
|
||||||
|
""" Listen to multiple sockets """
|
||||||
|
|
||||||
def __init__(self, kind=socket.SOCK_STREAM, proto=0):
|
def __init__(self, kind=socket.SOCK_STREAM, proto=0):
|
||||||
self.type = kind
|
self.type = kind
|
||||||
@ -123,14 +129,14 @@ class MultiListener:
|
|||||||
self.bind_called = False
|
self.bind_called = False
|
||||||
|
|
||||||
def setsockopt(self, level, optname, value):
|
def setsockopt(self, level, optname, value):
|
||||||
assert(self.bind_called)
|
assert self.bind_called
|
||||||
if self.v6:
|
if self.v6:
|
||||||
self.v6.setsockopt(level, optname, value)
|
self.v6.setsockopt(level, optname, value)
|
||||||
if self.v4:
|
if self.v4:
|
||||||
self.v4.setsockopt(level, optname, value)
|
self.v4.setsockopt(level, optname, value)
|
||||||
|
|
||||||
def add_handler(self, handlers, callback, method, mux):
|
def add_handler(self, handlers, callback, method, mux):
|
||||||
assert(self.bind_called)
|
assert self.bind_called
|
||||||
socks = []
|
socks = []
|
||||||
if self.v6:
|
if self.v6:
|
||||||
socks.append(self.v6)
|
socks.append(self.v6)
|
||||||
@ -145,7 +151,7 @@ class MultiListener:
|
|||||||
)
|
)
|
||||||
|
|
||||||
def listen(self, backlog):
|
def listen(self, backlog):
|
||||||
assert(self.bind_called)
|
assert self.bind_called
|
||||||
if self.v6:
|
if self.v6:
|
||||||
self.v6.listen(backlog)
|
self.v6.listen(backlog)
|
||||||
if self.v4:
|
if self.v4:
|
||||||
@ -160,7 +166,7 @@ class MultiListener:
|
|||||||
raise e
|
raise e
|
||||||
|
|
||||||
def bind(self, address_v6, address_v4):
|
def bind(self, address_v6, address_v4):
|
||||||
assert(not self.bind_called)
|
assert not self.bind_called
|
||||||
self.bind_called = True
|
self.bind_called = True
|
||||||
if address_v6 is not None:
|
if address_v6 is not None:
|
||||||
self.v6 = socket.socket(socket.AF_INET6, self.type, self.proto)
|
self.v6 = socket.socket(socket.AF_INET6, self.type, self.proto)
|
||||||
@ -174,7 +180,7 @@ class MultiListener:
|
|||||||
self.v4 = None
|
self.v4 = None
|
||||||
|
|
||||||
def print_listening(self, what):
|
def print_listening(self, what):
|
||||||
assert(self.bind_called)
|
assert self.bind_called
|
||||||
if self.v6:
|
if self.v6:
|
||||||
listenip = self.v6.getsockname()
|
listenip = self.v6.getsockname()
|
||||||
debug1('%s listening on %r.\n' % (what, listenip))
|
debug1('%s listening on %r.\n' % (what, listenip))
|
||||||
@ -186,6 +192,7 @@ class MultiListener:
|
|||||||
|
|
||||||
|
|
||||||
class FirewallClient:
|
class FirewallClient:
|
||||||
|
""" Start firewall code """
|
||||||
|
|
||||||
def __init__(self, method_name, sudo_pythonpath):
|
def __init__(self, method_name, sudo_pythonpath):
|
||||||
self.auto_nets = []
|
self.auto_nets = []
|
||||||
@ -309,8 +316,8 @@ class FirewallClient:
|
|||||||
raise Fatal('%r expected STARTED, got %r' % (self.argv, line))
|
raise Fatal('%r expected STARTED, got %r' % (self.argv, line))
|
||||||
|
|
||||||
def sethostip(self, hostname, ip):
|
def sethostip(self, hostname, ip):
|
||||||
assert(not re.search(br'[^-\w\.]', hostname))
|
assert not re.search(br'[^-\w\.]', hostname)
|
||||||
assert(not re.search(br'[^0-9.]', ip))
|
assert not re.search(br'[^0-9.]', ip)
|
||||||
self.pfile.write(b'HOST %s,%s\n' % (hostname, ip))
|
self.pfile.write(b'HOST %s,%s\n' % (hostname, ip))
|
||||||
self.pfile.flush()
|
self.pfile.flush()
|
||||||
|
|
||||||
@ -326,6 +333,7 @@ udp_by_src = {}
|
|||||||
|
|
||||||
|
|
||||||
def expire_connections(now, mux):
|
def expire_connections(now, mux):
|
||||||
|
""" Expire connections that have timed out """
|
||||||
remove = []
|
remove = []
|
||||||
for chan, timeout in dnsreqs.items():
|
for chan, timeout in dnsreqs.items():
|
||||||
if timeout < now:
|
if timeout < now:
|
||||||
@ -349,6 +357,7 @@ def expire_connections(now, mux):
|
|||||||
|
|
||||||
|
|
||||||
def onaccept_tcp(listener, method, mux, handlers):
|
def onaccept_tcp(listener, method, mux, handlers):
|
||||||
|
""" Accept a new TCP connection """
|
||||||
global _extra_fd
|
global _extra_fd
|
||||||
try:
|
try:
|
||||||
sock, srcip = listener.accept()
|
sock, srcip = listener.accept()
|
||||||
@ -386,6 +395,7 @@ def onaccept_tcp(listener, method, mux, handlers):
|
|||||||
|
|
||||||
|
|
||||||
def udp_done(chan, data, method, sock, dstip):
|
def udp_done(chan, data, method, sock, dstip):
|
||||||
|
""" Respond to UDP request """
|
||||||
(src, srcport, data) = data.split(b",", 2)
|
(src, srcport, data) = data.split(b",", 2)
|
||||||
srcip = (src, int(srcport))
|
srcip = (src, int(srcport))
|
||||||
debug3('doing send from %r to %r\n' % (srcip, dstip,))
|
debug3('doing send from %r to %r\n' % (srcip, dstip,))
|
||||||
@ -393,6 +403,7 @@ def udp_done(chan, data, method, sock, dstip):
|
|||||||
|
|
||||||
|
|
||||||
def onaccept_udp(listener, method, mux, handlers):
|
def onaccept_udp(listener, method, mux, handlers):
|
||||||
|
""" Accept a new UDP request """
|
||||||
now = time.time()
|
now = time.time()
|
||||||
t = method.recv_udp(listener, 4096)
|
t = method.recv_udp(listener, 4096)
|
||||||
if t is None:
|
if t is None:
|
||||||
@ -415,6 +426,7 @@ def onaccept_udp(listener, method, mux, handlers):
|
|||||||
|
|
||||||
|
|
||||||
def dns_done(chan, data, method, sock, srcip, dstip, mux):
|
def dns_done(chan, data, method, sock, srcip, dstip, mux):
|
||||||
|
""" Respond to a DNS request """
|
||||||
debug3('dns_done: channel=%d src=%r dst=%r\n' % (chan, srcip, dstip))
|
debug3('dns_done: channel=%d src=%r dst=%r\n' % (chan, srcip, dstip))
|
||||||
del mux.channels[chan]
|
del mux.channels[chan]
|
||||||
del dnsreqs[chan]
|
del dnsreqs[chan]
|
||||||
@ -422,6 +434,7 @@ def dns_done(chan, data, method, sock, srcip, dstip, mux):
|
|||||||
|
|
||||||
|
|
||||||
def ondns(listener, method, mux, handlers):
|
def ondns(listener, method, mux, handlers):
|
||||||
|
""" Accept a new DNS request """
|
||||||
now = time.time()
|
now = time.time()
|
||||||
t = method.recv_udp(listener, 4096)
|
t = method.recv_udp(listener, 4096)
|
||||||
if t is None:
|
if t is None:
|
||||||
@ -499,7 +512,7 @@ def _main(tcp_listener, udp_listener, fw, ssh_cmd, remotename,
|
|||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
if daemon:
|
if daemon:
|
||||||
daemonize()
|
daemonize()
|
||||||
log('daemonizing (%s).\n' % _pidname)
|
log('daemonizing (%s).\n' % _PID_NAME)
|
||||||
|
|
||||||
def onroutes(routestr):
|
def onroutes(routestr):
|
||||||
if auto_nets:
|
if auto_nets:
|
||||||
@ -581,6 +594,7 @@ def main(listenip_v6, listenip_v4,
|
|||||||
method_name, seed_hosts, auto_hosts, auto_nets,
|
method_name, seed_hosts, auto_hosts, auto_nets,
|
||||||
subnets_include, subnets_exclude, daemon, to_nameserver, pidfile,
|
subnets_include, subnets_exclude, daemon, to_nameserver, pidfile,
|
||||||
user, sudo_pythonpath):
|
user, sudo_pythonpath):
|
||||||
|
""" Main client code """
|
||||||
|
|
||||||
if not remotename:
|
if not remotename:
|
||||||
print("WARNING: You must specify -r/--remote to securely route "
|
print("WARNING: You must specify -r/--remote to securely route "
|
||||||
@ -821,7 +835,7 @@ def main(listenip_v6, listenip_v4,
|
|||||||
raise e
|
raise e
|
||||||
|
|
||||||
if not bound:
|
if not bound:
|
||||||
assert(last_e)
|
assert last_e
|
||||||
raise last_e
|
raise last_e
|
||||||
tcp_listener.listen(10)
|
tcp_listener.listen(10)
|
||||||
tcp_listener.print_listening("TCP redirector")
|
tcp_listener.print_listening("TCP redirector")
|
||||||
@ -867,7 +881,7 @@ def main(listenip_v6, listenip_v4,
|
|||||||
|
|
||||||
dns_listener.print_listening("DNS")
|
dns_listener.print_listening("DNS")
|
||||||
if not bound:
|
if not bound:
|
||||||
assert(last_e)
|
assert last_e
|
||||||
raise last_e
|
raise last_e
|
||||||
else:
|
else:
|
||||||
dnsport_v6 = 0
|
dnsport_v6 = 0
|
||||||
|
@ -197,7 +197,7 @@ def _check_smb(hostname):
|
|||||||
check_workgroup(workgroup)
|
check_workgroup(workgroup)
|
||||||
|
|
||||||
if lines:
|
if lines:
|
||||||
assert(0)
|
assert 0
|
||||||
|
|
||||||
|
|
||||||
def _check_nmb(hostname, is_workgroup, is_master):
|
def _check_nmb(hostname, is_workgroup, is_master):
|
||||||
|
@ -35,7 +35,7 @@ def _notify(message):
|
|||||||
assert isinstance(message, bytes)
|
assert isinstance(message, bytes)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return (sock.sendto(message, addr) > 0)
|
return sock.sendto(message, addr) > 0
|
||||||
except (OSError, IOError) as e:
|
except (OSError, IOError) as e:
|
||||||
debug1("Error notifying systemd: %s\n" % e)
|
debug1("Error notifying systemd: %s\n" % e)
|
||||||
return False
|
return False
|
||||||
|
@ -304,7 +304,7 @@ def main(latency_control, auto_hosts, to_nameserver, auto_nets):
|
|||||||
hw.leftover = b('')
|
hw.leftover = b('')
|
||||||
|
|
||||||
def hostwatch_ready(sock):
|
def hostwatch_ready(sock):
|
||||||
assert(hw.pid)
|
assert hw.pid
|
||||||
content = hw.sock.recv(4096)
|
content = hw.sock.recv(4096)
|
||||||
if content:
|
if content:
|
||||||
lines = (hw.leftover + content).split(b('\n'))
|
lines = (hw.leftover + content).split(b('\n'))
|
||||||
@ -379,7 +379,7 @@ def main(latency_control, auto_hosts, to_nameserver, auto_nets):
|
|||||||
|
|
||||||
while mux.ok:
|
while mux.ok:
|
||||||
if hw.pid:
|
if hw.pid:
|
||||||
assert(hw.pid > 0)
|
assert hw.pid > 0
|
||||||
(rpid, rv) = os.waitpid(hw.pid, os.WNOHANG)
|
(rpid, rv) = os.waitpid(hw.pid, os.WNOHANG)
|
||||||
if rpid:
|
if rpid:
|
||||||
raise Fatal(
|
raise Fatal(
|
||||||
|
@ -1,3 +1,7 @@
|
|||||||
|
"""
|
||||||
|
Run sshuttle via remove ssh session
|
||||||
|
"""
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
@ -16,23 +20,27 @@ from sshuttle.helpers import debug2, which, get_path, Fatal
|
|||||||
|
|
||||||
|
|
||||||
def get_module_source(name):
|
def get_module_source(name):
|
||||||
|
""" Get source code for module """
|
||||||
spec = importlib.util.find_spec(name)
|
spec = importlib.util.find_spec(name)
|
||||||
with open(spec.origin, "rt") as f:
|
with open(spec.origin, "rt") as f:
|
||||||
return f.read().encode("utf-8")
|
return f.read().encode("utf-8")
|
||||||
|
|
||||||
|
|
||||||
def empackage(z, name, data=None):
|
def empackage(zobj, name, data=None):
|
||||||
|
""" Compress and package module source code """
|
||||||
if not data:
|
if not data:
|
||||||
data = get_module_source(name)
|
data = get_module_source(name)
|
||||||
content = z.compress(data)
|
content = zobj.compress(data)
|
||||||
content += z.flush(zlib.Z_SYNC_FLUSH)
|
content += zobj.flush(zlib.Z_SYNC_FLUSH)
|
||||||
|
|
||||||
return b'%s\n%d\n%s' % (name.encode("ASCII"), len(content), content)
|
return b'%s\n%d\n%s' % (name.encode("ASCII"), len(content), content)
|
||||||
|
|
||||||
|
|
||||||
def parse_hostport(rhostport):
|
def parse_hostport(rhostport):
|
||||||
"""
|
"""
|
||||||
parses the given rhostport variable, looking like this:
|
Parses the given rhostport variable
|
||||||
|
|
||||||
|
rhostport looks like this:
|
||||||
|
|
||||||
[username[:password]@]host[:port]
|
[username[:password]@]host[:port]
|
||||||
|
|
||||||
@ -85,6 +93,7 @@ def parse_hostport(rhostport):
|
|||||||
|
|
||||||
|
|
||||||
def connect(ssh_cmd, rhostport, python, stderr, options):
|
def connect(ssh_cmd, rhostport, python, stderr, options):
|
||||||
|
""" Connect to a remote server via ssh and run sshuttle """
|
||||||
username, password, port, host = parse_hostport(rhostport)
|
username, password, port, host = parse_hostport(rhostport)
|
||||||
if username:
|
if username:
|
||||||
rhost = "{}@{}".format(username, host)
|
rhost = "{}@{}".format(username, host)
|
||||||
|
@ -227,7 +227,7 @@ class SockWrapper:
|
|||||||
return 0
|
return 0
|
||||||
|
|
||||||
def write(self, buf):
|
def write(self, buf):
|
||||||
assert(buf)
|
assert buf
|
||||||
return self.uwrite(buf)
|
return self.uwrite(buf)
|
||||||
|
|
||||||
def uread(self):
|
def uread(self):
|
||||||
@ -402,15 +402,15 @@ class Mux(Handler):
|
|||||||
elif cmd == CMD_EXIT:
|
elif cmd == CMD_EXIT:
|
||||||
self.ok = False
|
self.ok = False
|
||||||
elif cmd == CMD_TCP_CONNECT:
|
elif cmd == CMD_TCP_CONNECT:
|
||||||
assert(not self.channels.get(channel))
|
assert not self.channels.get(channel)
|
||||||
if self.new_channel:
|
if self.new_channel:
|
||||||
self.new_channel(channel, data)
|
self.new_channel(channel, data)
|
||||||
elif cmd == CMD_DNS_REQ:
|
elif cmd == CMD_DNS_REQ:
|
||||||
assert(not self.channels.get(channel))
|
assert not self.channels.get(channel)
|
||||||
if self.got_dns_req:
|
if self.got_dns_req:
|
||||||
self.got_dns_req(channel, data)
|
self.got_dns_req(channel, data)
|
||||||
elif cmd == CMD_UDP_OPEN:
|
elif cmd == CMD_UDP_OPEN:
|
||||||
assert(not self.channels.get(channel))
|
assert not self.channels.get(channel)
|
||||||
if self.got_udp_open:
|
if self.got_udp_open:
|
||||||
self.got_udp_open(channel, data)
|
self.got_udp_open(channel, data)
|
||||||
elif cmd == CMD_ROUTES:
|
elif cmd == CMD_ROUTES:
|
||||||
@ -479,8 +479,8 @@ class Mux(Handler):
|
|||||||
if len(self.inbuf) >= (self.want or HDR_LEN):
|
if len(self.inbuf) >= (self.want or HDR_LEN):
|
||||||
(s1, s2, channel, cmd, datalen) = \
|
(s1, s2, channel, cmd, datalen) = \
|
||||||
struct.unpack('!ccHHH', self.inbuf[:HDR_LEN])
|
struct.unpack('!ccHHH', self.inbuf[:HDR_LEN])
|
||||||
assert(s1 == b('S'))
|
assert s1 == b('S')
|
||||||
assert(s2 == b('S'))
|
assert s2 == b('S')
|
||||||
self.want = datalen + HDR_LEN
|
self.want = datalen + HDR_LEN
|
||||||
if self.want and len(self.inbuf) >= self.want:
|
if self.want and len(self.inbuf) >= self.want:
|
||||||
data = self.inbuf[HDR_LEN:self.want]
|
data = self.inbuf[HDR_LEN:self.want]
|
||||||
|
@ -38,7 +38,7 @@ while 1:
|
|||||||
r = [listener] + servers + clients
|
r = [listener] + servers + clients
|
||||||
print('select(%d)' % len(r))
|
print('select(%d)' % len(r))
|
||||||
r, w, x = select.select(r, [], [], 5)
|
r, w, x = select.select(r, [], [], 5)
|
||||||
assert(r)
|
assert r
|
||||||
for i in r:
|
for i in r:
|
||||||
if i == listener:
|
if i == listener:
|
||||||
s, addr = listener.accept()
|
s, addr = listener.accept()
|
||||||
@ -47,7 +47,7 @@ while 1:
|
|||||||
b = i.recv(4096)
|
b = i.recv(4096)
|
||||||
print('srv << %r' % len(b))
|
print('srv << %r' % len(b))
|
||||||
if i not in remain:
|
if i not in remain:
|
||||||
assert(len(b) >= 4)
|
assert len(b) >= 4
|
||||||
want = struct.unpack('I', b[:4])[0]
|
want = struct.unpack('I', b[:4])[0]
|
||||||
b = b[4:]
|
b = b[4:]
|
||||||
# i.send('y'*want)
|
# i.send('y'*want)
|
||||||
@ -55,13 +55,13 @@ while 1:
|
|||||||
want = remain[i]
|
want = remain[i]
|
||||||
if want < len(b):
|
if want < len(b):
|
||||||
print('weird wanted %d bytes, got %d: %r' % (want, len(b), b))
|
print('weird wanted %d bytes, got %d: %r' % (want, len(b), b))
|
||||||
assert(want >= len(b))
|
assert want >= len(b)
|
||||||
want -= len(b)
|
want -= len(b)
|
||||||
remain[i] = want
|
remain[i] = want
|
||||||
if not b: # EOF
|
if not b: # EOF
|
||||||
if want:
|
if want:
|
||||||
print('weird: eof but wanted %d more' % want)
|
print('weird: eof but wanted %d more' % want)
|
||||||
assert(want == 0)
|
assert want == 0
|
||||||
i.close()
|
i.close()
|
||||||
servers.remove(i)
|
servers.remove(i)
|
||||||
del remain[i]
|
del remain[i]
|
||||||
@ -76,13 +76,13 @@ while 1:
|
|||||||
want = remain[i]
|
want = remain[i]
|
||||||
if want < len(b):
|
if want < len(b):
|
||||||
print('weird wanted %d bytes, got %d: %r' % (want, len(b), b))
|
print('weird wanted %d bytes, got %d: %r' % (want, len(b), b))
|
||||||
assert(want >= len(b))
|
assert want >= len(b)
|
||||||
want -= len(b)
|
want -= len(b)
|
||||||
remain[i] = want
|
remain[i] = want
|
||||||
if not b: # EOF
|
if not b: # EOF
|
||||||
if want:
|
if want:
|
||||||
print('weird: eof but wanted %d more' % want)
|
print('weird: eof but wanted %d more' % want)
|
||||||
assert(want == 0)
|
assert want == 0
|
||||||
i.close()
|
i.close()
|
||||||
clients.remove(i)
|
clients.remove(i)
|
||||||
del remain[i]
|
del remain[i]
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
|
"""
|
||||||
|
Manage sudoers file
|
||||||
|
"""
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import getpass
|
import getpass
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
from subprocess import Popen, PIPE
|
from subprocess import Popen, PIPE
|
||||||
from sshuttle.helpers import log, debug1
|
|
||||||
from distutils import spawn
|
from distutils import spawn
|
||||||
|
from sshuttle.helpers import log, debug1
|
||||||
|
|
||||||
path_to_sshuttle = sys.argv[0]
|
path_to_sshuttle = sys.argv[0]
|
||||||
path_to_dist_packages = os.path.dirname(os.path.abspath(__file__))[:-9]
|
path_to_dist_packages = os.path.dirname(os.path.abspath(__file__))[:-9]
|
||||||
@ -13,7 +16,7 @@ path_to_dist_packages = os.path.dirname(os.path.abspath(__file__))[:-9]
|
|||||||
command_alias = 'SSHUTTLE%(num)s' % {'num': uuid4().hex[-3:].upper()}
|
command_alias = 'SSHUTTLE%(num)s' % {'num': uuid4().hex[-3:].upper()}
|
||||||
|
|
||||||
# Template for the sudoers file
|
# Template for the sudoers file
|
||||||
template = '''
|
TEMPLATE = '''
|
||||||
Cmnd_Alias %(ca)s = /usr/bin/env PYTHONPATH=%(dist_packages)s %(py)s %(path)s *
|
Cmnd_Alias %(ca)s = /usr/bin/env PYTHONPATH=%(dist_packages)s %(py)s %(path)s *
|
||||||
|
|
||||||
%(user_name)s ALL=NOPASSWD: %(ca)s
|
%(user_name)s ALL=NOPASSWD: %(ca)s
|
||||||
@ -21,7 +24,8 @@ Cmnd_Alias %(ca)s = /usr/bin/env PYTHONPATH=%(dist_packages)s %(py)s %(path)s *
|
|||||||
|
|
||||||
|
|
||||||
def build_config(user_name):
|
def build_config(user_name):
|
||||||
content = template % {
|
""" Build sudoers config """
|
||||||
|
content = TEMPLATE % {
|
||||||
'ca': command_alias,
|
'ca': command_alias,
|
||||||
'dist_packages': path_to_dist_packages,
|
'dist_packages': path_to_dist_packages,
|
||||||
'py': sys.executable,
|
'py': sys.executable,
|
||||||
@ -33,6 +37,7 @@ def build_config(user_name):
|
|||||||
|
|
||||||
|
|
||||||
def save_config(content, file_name):
|
def save_config(content, file_name):
|
||||||
|
""" Save sudoers config """
|
||||||
process = Popen([
|
process = Popen([
|
||||||
'/usr/bin/sudo',
|
'/usr/bin/sudo',
|
||||||
spawn.find_executable('sudoers-add'),
|
spawn.find_executable('sudoers-add'),
|
||||||
@ -47,18 +52,19 @@ def save_config(content, file_name):
|
|||||||
if returncode:
|
if returncode:
|
||||||
log('Failed updating sudoers file.\n')
|
log('Failed updating sudoers file.\n')
|
||||||
debug1(streamdata)
|
debug1(streamdata)
|
||||||
exit(returncode)
|
sys.exit(returncode)
|
||||||
else:
|
else:
|
||||||
log('Success, sudoers file update.\n')
|
log('Success, sudoers file update.\n')
|
||||||
exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
||||||
def sudoers(user_name=None, no_modify=None, file_name=None):
|
def sudoers(user_name=None, no_modify=None, file_name=None):
|
||||||
|
""" Update sudoers config """
|
||||||
user_name = user_name or getpass.getuser()
|
user_name = user_name or getpass.getuser()
|
||||||
content = build_config(user_name)
|
content = build_config(user_name)
|
||||||
|
|
||||||
if no_modify:
|
if no_modify:
|
||||||
sys.stdout.write(content)
|
sys.stdout.write(content)
|
||||||
exit(0)
|
sys.exit(0)
|
||||||
else:
|
else:
|
||||||
save_config(content, file_name)
|
save_config(content, file_name)
|
||||||
|
Loading…
Reference in New Issue
Block a user