├── .gitignore
├── .settings
├── org.eclipse.jdt.core.prefs
└── org.eclipse.jdt.ui.prefs
├── .travis.yml
├── LICENSE
├── README.md
├── Smart Proxy X
├── .gitignore
├── build.gradle
├── proguard-rules.txt
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── ic_launcher-web.png
│ ├── java
│ └── me
│ │ └── smartproxy
│ │ ├── core
│ │ ├── ChinaIpMaskManager.java
│ │ ├── DnsProxy.java
│ │ ├── HttpHostHeaderParser.java
│ │ ├── LocalVpnService.java
│ │ ├── NatSession.java
│ │ ├── NatSessionManager.java
│ │ ├── ProxyConfig.java
│ │ ├── TcpProxyServer.java
│ │ ├── TmpConfig.java
│ │ └── TunnelFactory.java
│ │ ├── dns
│ │ ├── DnsFlags.java
│ │ ├── DnsHeader.java
│ │ ├── DnsPacket.java
│ │ ├── Question.java
│ │ ├── Resource.java
│ │ └── ResourcePointer.java
│ │ ├── tcpip
│ │ ├── CommonMethods.java
│ │ ├── IPHeader.java
│ │ ├── TCPHeader.java
│ │ └── UDPHeader.java
│ │ ├── tunnel
│ │ ├── Config.java
│ │ ├── IEncryptor.java
│ │ ├── RawTunnel.java
│ │ ├── Tunnel.java
│ │ ├── httpconnect
│ │ │ ├── HttpConnectConfig.java
│ │ │ └── HttpConnectTunnel.java
│ │ └── shadowsocks
│ │ │ ├── EncryptorFactory.java
│ │ │ ├── ShadowsocksConfig.java
│ │ │ ├── ShadowsocksTunnel.java
│ │ │ └── TableEncryptor.java
│ │ └── ui
│ │ ├── MainActivity.java
│ │ └── QuickSettingsService.java
│ ├── jniLibs
│ ├── arm64-v8a
│ │ └── libnghttpx.so
│ ├── libnghttpx.so
│ ├── x86
│ │ └── libnghttpx.so
│ └── x86_64
│ │ └── libnghttpx.so
│ └── res
│ ├── drawable-hdpi
│ └── ic_launcher.png
│ ├── drawable-mdpi
│ └── ic_launcher.png
│ ├── drawable-xhdpi
│ └── ic_launcher.png
│ ├── drawable-xxhdpi
│ └── ic_launcher.png
│ ├── layout
│ └── activity_main.xml
│ ├── menu
│ └── main_activity_actions.xml
│ ├── raw
│ └── ipmask
│ ├── values-sw600dp
│ └── dimens.xml
│ ├── values-sw720dp-land
│ └── dimens.xml
│ ├── values-v11
│ └── styles.xml
│ ├── values-v14
│ └── styles.xml
│ ├── values-zh-rCN
│ └── strings.xml
│ └── values
│ ├── dimens.xml
│ ├── strings.xml
│ └── styles.xml
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .gradle
3 | .idea
4 |
5 | /local.properties
6 |
7 | *.iml
8 | release
9 | build
10 |
--------------------------------------------------------------------------------
/.settings/org.eclipse.jdt.core.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
3 | org.eclipse.jdt.core.compiler.compliance=1.6
4 | org.eclipse.jdt.core.compiler.source=1.6
5 | org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
6 | org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
7 | org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
8 | org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=0
9 | org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
10 | org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
11 | org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
12 | org.eclipse.jdt.core.formatter.alignment_for_assignment=0
13 | org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
14 | org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
15 | org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
16 | org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
17 | org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
18 | org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0
19 | org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
20 | org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=0
21 | org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=0
22 | org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80
23 | org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
24 | org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
25 | org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=0
26 | org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
27 | org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=0
28 | org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=0
29 | org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16
30 | org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
31 | org.eclipse.jdt.core.formatter.blank_lines_after_package=1
32 | org.eclipse.jdt.core.formatter.blank_lines_before_field=0
33 | org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
34 | org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
35 | org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
36 | org.eclipse.jdt.core.formatter.blank_lines_before_method=1
37 | org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
38 | org.eclipse.jdt.core.formatter.blank_lines_before_package=0
39 | org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
40 | org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
41 | org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
42 | org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
43 | org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
44 | org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
45 | org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
46 | org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
47 | org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
48 | org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
49 | org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
50 | org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
51 | org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
52 | org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
53 | org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
54 | org.eclipse.jdt.core.formatter.comment.format_block_comments=true
55 | org.eclipse.jdt.core.formatter.comment.format_header=false
56 | org.eclipse.jdt.core.formatter.comment.format_html=true
57 | org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
58 | org.eclipse.jdt.core.formatter.comment.format_line_comments=true
59 | org.eclipse.jdt.core.formatter.comment.format_source_code=true
60 | org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
61 | org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
62 | org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
63 | org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert
64 | org.eclipse.jdt.core.formatter.comment.line_length=80
65 | org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true
66 | org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true
67 | org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false
68 | org.eclipse.jdt.core.formatter.compact_else_if=true
69 | org.eclipse.jdt.core.formatter.continuation_indentation=2
70 | org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
71 | org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off
72 | org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on
73 | org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
74 | org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true
75 | org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
76 | org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
77 | org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
78 | org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
79 | org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
80 | org.eclipse.jdt.core.formatter.indent_empty_lines=false
81 | org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
82 | org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
83 | org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
84 | org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
85 | org.eclipse.jdt.core.formatter.indentation.size=4
86 | org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert
87 | org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
88 | org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert
89 | org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert
90 | org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
91 | org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert
92 | org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert
93 | org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
94 | org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
95 | org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
96 | org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
97 | org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
98 | org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
99 | org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
100 | org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
101 | org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert
102 | org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
103 | org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
104 | org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
105 | org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
106 | org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
107 | org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
108 | org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
109 | org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
110 | org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
111 | org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
112 | org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
113 | org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
114 | org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
115 | org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
116 | org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
117 | org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
118 | org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
119 | org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
120 | org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
121 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
122 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
123 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
124 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
125 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
126 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
127 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
128 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
129 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
130 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
131 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
132 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
133 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
134 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
135 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
136 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
137 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
138 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
139 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
140 | org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
141 | org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
142 | org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
143 | org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
144 | org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
145 | org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
146 | org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
147 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
148 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
149 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
150 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
151 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
152 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
153 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
154 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
155 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
156 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
157 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
158 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
159 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert
160 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
161 | org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
162 | org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
163 | org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
164 | org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
165 | org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
166 | org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert
167 | org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
168 | org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
169 | org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
170 | org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
171 | org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
172 | org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
173 | org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
174 | org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
175 | org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
176 | org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
177 | org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
178 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
179 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
180 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
181 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
182 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
183 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
184 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
185 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
186 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
187 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
188 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
189 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
190 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert
191 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
192 | org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
193 | org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
194 | org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
195 | org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
196 | org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
197 | org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
198 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
199 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
200 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
201 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
202 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
203 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
204 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
205 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
206 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
207 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
208 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
209 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
210 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
211 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
212 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
213 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
214 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
215 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
216 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
217 | org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
218 | org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
219 | org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
220 | org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
221 | org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
222 | org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
223 | org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
224 | org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
225 | org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
226 | org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
227 | org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
228 | org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
229 | org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
230 | org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
231 | org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
232 | org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
233 | org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
234 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
235 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
236 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
237 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
238 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
239 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
240 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
241 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
242 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
243 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
244 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
245 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
246 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert
247 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
248 | org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
249 | org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
250 | org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
251 | org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
252 | org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
253 | org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
254 | org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
255 | org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
256 | org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert
257 | org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
258 | org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
259 | org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
260 | org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
261 | org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
262 | org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
263 | org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
264 | org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
265 | org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
266 | org.eclipse.jdt.core.formatter.join_lines_in_comments=true
267 | org.eclipse.jdt.core.formatter.join_wrapped_lines=false
268 | org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
269 | org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
270 | org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
271 | org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
272 | org.eclipse.jdt.core.formatter.lineSplit=800
273 | org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
274 | org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
275 | org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
276 | org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
277 | org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
278 | org.eclipse.jdt.core.formatter.tabulation.char=tab
279 | org.eclipse.jdt.core.formatter.tabulation.size=4
280 | org.eclipse.jdt.core.formatter.use_on_off_tags=false
281 | org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
282 | org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
283 | org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true
284 | org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=false
285 |
--------------------------------------------------------------------------------
/.settings/org.eclipse.jdt.ui.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | formatter_profile=_test
3 | formatter_settings_version=12
4 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: android
2 | sudo: false
3 | android:
4 | components:
5 | # Uncomment the lines below if you want to
6 | # use the latest revision of Android SDK Tools
7 | - tools
8 | - platform-tools
9 |
10 | # The BuildTools version used by your project
11 | - build-tools-28.0.2
12 |
13 | # The SDK version used to compile your project
14 | - android-27
15 |
16 | # Additional components
17 | - extra-google-m2repository
18 | - extra-android-m2repository
19 |
20 | # Specify at least one system image,
21 | # if you need to run emulator(s) during your tests
22 | - sys-img-armeabi-v7a-android-27
23 |
24 | script: ./gradlew assembleDebug check
25 | before_cache:
26 | - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
27 | - rm -fr $HOME/.gradle/caches/*/plugin-resolution/
28 | cache:
29 | directories:
30 | - $HOME/.gradle/caches/
31 | - $HOME/.gradle/wrapper/
32 | - $HOME/.android/build-cache
33 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | SmartProxy
2 | ==========
3 | [](https://travis-ci.org/zxc111/SmartProxy)
4 |
5 | http2 proxy client for android
6 |
7 | base on [https://github.com/oldman1977/SmartProxy](https://github.com/oldman1977/SmartProxy)
8 |
9 | 服务器端配合[nghttpx](https://github.com/nghttp2/nghttp2)食用。(自行编译)
10 |
11 | 简单粗暴将nghttpx打包进apk中
12 |
13 | #### ChangeLog
14 |
15 | 1. 增加通过 QuickSetting 启动
16 | 2. 调整nghttpx的参数,提升传输效率
17 |
18 | #### 已知问题
19 | 1. ~~要按保存+on才能开始运行。保存开启nghttpx转发,on开启vpnService截取包。~~
20 | 2. ~~性能略差。100k/s吧大概~~
21 | 3. 切换配置麻烦
22 | 4. 远端服务器暂时只支持ip
23 | 5. 有点费电,暂时无解。
24 | 6. 目前放进去的是nghttpx是编译的arm平台的,不能跑在x86下,也就是说emulator下无法真正的测试通信
25 | 7. ~~文件编码有些问题~~
26 | 8. 先这么多吧
27 |
--------------------------------------------------------------------------------
/Smart Proxy X/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/Smart Proxy X/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | google()
4 | mavenCentral()
5 | }
6 | dependencies {
7 | classpath 'com.android.tools.build:gradle:7.0.4'
8 | }
9 | }
10 | apply plugin: 'com.android.application'
11 |
12 | repositories {
13 | google()
14 | mavenCentral()
15 | }
16 |
17 | android {
18 | compileSdkVersion 30
19 | // buildToolsVersion '30.0.2'
20 |
21 | defaultConfig {
22 | minSdkVersion 24
23 | targetSdkVersion 30
24 | versionCode 7
25 | versionName "1.6"
26 |
27 | ndk {
28 | abiFilters 'armeabi', 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'
29 | }
30 | }
31 |
32 | lintOptions {
33 | abortOnError false
34 | }
35 | signingConfigs {
36 | release {
37 | storeFile file("[android.keystore]")
38 | storePassword System.getenv("KSTOREPWD")
39 | keyAlias "[smartproxy]"
40 | keyPassword System.getenv("KEYPWD")
41 | }
42 | }
43 |
44 | buildTypes {
45 | release {
46 | //runProguard false
47 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
48 | signingConfig signingConfigs.release
49 | }
50 | }
51 | }
52 |
53 | dependencies {
54 | // api 'com.embarkmobile:zxing-android-minimal:3.5.0@aar'
55 | // api 'com.embarkmobile:zxing-android-integration:3.5.0@aar'
56 | // api 'com.google.zxing:core:3.5.0'
57 | }
58 |
--------------------------------------------------------------------------------
/Smart Proxy X/proguard-rules.txt:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /usr/local/opt/android-sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the ProGuard
5 | # include property in project.properties.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
--------------------------------------------------------------------------------
/Smart Proxy X/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
16 |
17 |
24 |
25 |
29 |
30 |
31 |
32 |
33 |
34 |
39 |
40 |
41 |
42 |
43 |
44 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/Smart Proxy X/src/main/ic_launcher-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zxc111/SmartProxy/24a12530cab7fe6f6e00c048d973e507da919ab3/Smart Proxy X/src/main/ic_launcher-web.png
--------------------------------------------------------------------------------
/Smart Proxy X/src/main/java/me/smartproxy/core/ChinaIpMaskManager.java:
--------------------------------------------------------------------------------
1 | package me.smartproxy.core;
2 |
3 | import java.io.IOException;
4 | import java.io.InputStream;
5 |
6 | import me.smartproxy.tcpip.CommonMethods;
7 | import android.util.SparseIntArray;
8 |
9 |
10 |
11 | public class ChinaIpMaskManager {
12 |
13 | static SparseIntArray ChinaIpMaskDict = new SparseIntArray(3000);
14 | static SparseIntArray MaskDict = new SparseIntArray();
15 |
16 | public static boolean isIPInChina(int ip){
17 | boolean found=false;
18 | for (int i = 0; i < MaskDict.size(); i++) {
19 | int mask=MaskDict.keyAt(i);
20 | int networkIP = ip & mask;
21 | int mask2 = ChinaIpMaskDict.get(networkIP);
22 | if(mask2==mask){
23 | found= true;
24 | break;
25 | }
26 | }
27 | return found;
28 | }
29 |
30 | public static void loadFromFile(InputStream inputStream){
31 | int count=0;
32 | try {
33 | byte[] buffer=new byte[4096];
34 | while((count = inputStream.read(buffer))>0){
35 | for (int i = 0; i < count; i+=8) {
36 | int ip = CommonMethods.readInt(buffer, i);
37 | int mask = CommonMethods.readInt(buffer, i+4);
38 | ChinaIpMaskDict.put(ip, mask);
39 | MaskDict.put(mask, mask);
40 | //System.out.printf("%s/%s\n", CommonMethods.IP2String(ip),CommonMethods.IP2String(mask));
41 | }
42 | }
43 | inputStream.close();
44 | System.out.printf("ChinaIpMask records count: %d\n", ChinaIpMaskDict.size());
45 | } catch (IOException e) {
46 | // TODO Auto-generated catch block
47 | e.printStackTrace();
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/Smart Proxy X/src/main/java/me/smartproxy/core/DnsProxy.java:
--------------------------------------------------------------------------------
1 | package me.smartproxy.core;
2 |
3 | import java.io.IOException;
4 | import java.net.DatagramPacket;
5 | import java.net.DatagramSocket;
6 | import java.net.InetSocketAddress;
7 | import java.nio.ByteBuffer;
8 | import java.util.concurrent.ConcurrentHashMap;
9 |
10 | import me.smartproxy.dns.DnsPacket;
11 | import me.smartproxy.dns.Question;
12 | import me.smartproxy.dns.Resource;
13 | import me.smartproxy.dns.ResourcePointer;
14 | import me.smartproxy.tcpip.CommonMethods;
15 | import me.smartproxy.tcpip.IPHeader;
16 | import me.smartproxy.tcpip.UDPHeader;
17 |
18 | import android.util.SparseArray;
19 |
20 |
21 | public class DnsProxy implements Runnable {
22 |
23 | private class QueryState
24 | {
25 | public short ClientQueryID;
26 | public long QueryNanoTime;
27 | public int ClientIP;
28 | public short ClientPort;
29 | public int RemoteIP;
30 | public short RemotePort;
31 | }
32 |
33 | public boolean Stopped;
34 | private static final ConcurrentHashMap IPDomainMaps= new ConcurrentHashMap();
35 | private static final ConcurrentHashMap DomainIPMaps= new ConcurrentHashMap();
36 | private final long QUERY_TIMEOUT_NS=10*1000000000L;
37 | private DatagramSocket m_Client;
38 | private Thread m_ReceivedThread;
39 | private short m_QueryID;
40 | private SparseArray m_QueryArray;
41 |
42 | public DnsProxy() throws IOException {
43 | m_QueryArray = new SparseArray();
44 | m_Client = new DatagramSocket(0);
45 | }
46 |
47 | public static String reverseLookup(int ip){
48 | return IPDomainMaps.get(ip);
49 | }
50 |
51 | public void start(){
52 | m_ReceivedThread = new Thread(this);
53 | m_ReceivedThread.setName("DnsProxyThread");
54 | m_ReceivedThread.start();
55 | System.out.println("dns server start");
56 | }
57 |
58 | public void stop(){
59 | Stopped=true;
60 | if( m_Client!=null){
61 | m_Client.close();
62 | m_Client=null;
63 | }
64 | }
65 |
66 | @Override
67 | public void run() {
68 | System.out.println("dns server run");
69 |
70 | try {
71 | byte[] RECEIVE_BUFFER = new byte[2000];
72 | IPHeader ipHeader=new IPHeader(RECEIVE_BUFFER, 0);
73 | ipHeader.Default();
74 | UDPHeader udpHeader=new UDPHeader(RECEIVE_BUFFER, 20);
75 |
76 | ByteBuffer dnsBuffer=ByteBuffer.wrap(RECEIVE_BUFFER);
77 | dnsBuffer.position(28);
78 | dnsBuffer=dnsBuffer.slice();
79 |
80 | DatagramPacket packet = new DatagramPacket(RECEIVE_BUFFER,28, RECEIVE_BUFFER.length-28);
81 |
82 | while (m_Client!=null&&!m_Client.isClosed()){
83 | System.out.println("123123123");
84 |
85 | packet.setLength(RECEIVE_BUFFER.length-28);
86 | m_Client.receive(packet);
87 | System.out.println("111 start");
88 |
89 | dnsBuffer.clear();
90 | dnsBuffer.limit(packet.getLength());
91 | try {
92 | DnsPacket dnsPacket=DnsPacket.FromBytes(dnsBuffer);
93 | if(dnsPacket!=null){
94 | OnDnsResponseReceived(ipHeader,udpHeader,dnsPacket);
95 | System.out.println("111 stop");
96 | }
97 | } catch (Exception e) {
98 | e.printStackTrace();
99 | LocalVpnService.Instance.writeLog("Parse dns error: %s", e);
100 | }
101 | }
102 | } catch (Exception e) {
103 | e.printStackTrace();
104 | } finally{
105 | System.out.println("DnsResolver Thread Exited.");
106 | this.stop();
107 | }
108 | }
109 |
110 | private int getFirstIP(DnsPacket dnsPacket){
111 | for (int i = 0; i < dnsPacket.Header.ResourceCount; i++) {
112 | Resource resource=dnsPacket.Resources[i];
113 | if(resource.Type==1){
114 | int ip=CommonMethods.readInt(resource.Data, 0);
115 | return ip;
116 | }
117 | }
118 | return 0;
119 | }
120 |
121 | private void tamperDnsResponse(byte[] rawPacket,DnsPacket dnsPacket,int newIP){
122 | Question question=dnsPacket.Questions[0];
123 |
124 | dnsPacket.Header.setResourceCount((short)1);
125 | dnsPacket.Header.setAResourceCount((short)0);
126 | dnsPacket.Header.setEResourceCount((short)0);
127 |
128 | ResourcePointer rPointer=new ResourcePointer(rawPacket, question.Offset()+question.Length());
129 | rPointer.setDomain((short)0xC00C);
130 | rPointer.setType(question.Type);
131 | rPointer.setClass(question.Class);
132 | rPointer.setTTL(ProxyConfig.Instance.getDnsTTL());
133 | rPointer.setDataLength((short)4);
134 | rPointer.setIP(newIP);
135 |
136 | dnsPacket.Size=12+question.Length()+16;
137 | }
138 |
139 | private int getOrCreateFakeIP(String domainString){
140 | Integer fakeIP=DomainIPMaps.get(domainString);
141 | if(fakeIP==null){
142 | int hashIP=domainString.hashCode();
143 | do{
144 | fakeIP=ProxyConfig.FAKE_NETWORK_IP | (hashIP&0x0000FFFF);
145 | hashIP++;
146 | }while(IPDomainMaps.containsKey(fakeIP));
147 |
148 | DomainIPMaps.put(domainString,fakeIP);
149 | IPDomainMaps.put(fakeIP, domainString);
150 | }
151 | return fakeIP;
152 | }
153 |
154 | private boolean dnsPollution(byte[] rawPacket,DnsPacket dnsPacket){
155 | if(dnsPacket.Header.QuestionCount>0){
156 | Question question=dnsPacket.Questions[0];
157 | if(question.Type==1){
158 | int realIP=getFirstIP(dnsPacket);
159 | if(ProxyConfig.Instance.needProxy(question.Domain, realIP)){
160 | int fakeIP=getOrCreateFakeIP(question.Domain);
161 | tamperDnsResponse(rawPacket,dnsPacket,fakeIP);
162 | if(ProxyConfig.IS_DEBUG)
163 | System.out.printf("FakeDns: %s=>%s(%s)\n",question.Domain,CommonMethods.ipIntToString(realIP),CommonMethods.ipIntToString(fakeIP));
164 | return true;
165 | }
166 | }
167 | }
168 | return false;
169 | }
170 |
171 | private void OnDnsResponseReceived(IPHeader ipHeader,UDPHeader udpHeader,DnsPacket dnsPacket) {
172 | QueryState state =null;
173 | synchronized (m_QueryArray) {
174 | state=m_QueryArray.get(dnsPacket.Header.ID);
175 | if(state!=null){
176 | m_QueryArray.remove(dnsPacket.Header.ID);
177 | }
178 | }
179 |
180 | if (state != null) {
181 | //DNS污染,默认污染海外网站
182 | dnsPollution(udpHeader.m_Data,dnsPacket);
183 |
184 | dnsPacket.Header.setID(state.ClientQueryID);
185 | ipHeader.setSourceIP(state.RemoteIP);
186 | ipHeader.setDestinationIP(state.ClientIP);
187 | ipHeader.setProtocol(IPHeader.UDP);
188 | ipHeader.setTotalLength(20+8+dnsPacket.Size);
189 | udpHeader.setSourcePort(state.RemotePort);
190 | udpHeader.setDestinationPort(state.ClientPort);
191 | udpHeader.setTotalLength(8+dnsPacket.Size);
192 |
193 | LocalVpnService.Instance.sendUDPPacket(ipHeader, udpHeader);
194 | }
195 | }
196 |
197 | private int getIPFromCache(String domain){
198 | Integer ip=DomainIPMaps.get(domain);
199 | if(ip==null){
200 | return 0;
201 | }
202 | else {
203 | return ip;
204 | }
205 | }
206 |
207 | private boolean interceptDns(IPHeader ipHeader,UDPHeader udpHeader,DnsPacket dnsPacket){
208 | Question question=dnsPacket.Questions[0];
209 | System.out.println("DNS Qeury "+question.Domain);
210 | if(question.Type==1){
211 | if(ProxyConfig.Instance.needProxy(question.Domain, getIPFromCache(question.Domain))){
212 | int fakeIP=getOrCreateFakeIP(question.Domain);
213 | tamperDnsResponse(ipHeader.m_Data,dnsPacket,fakeIP);
214 |
215 | if(ProxyConfig.IS_DEBUG)
216 | System.out.printf("interceptDns FakeDns: %s=>%s\n",question.Domain,CommonMethods.ipIntToString(fakeIP));
217 |
218 | int sourceIP=ipHeader.getSourceIP();
219 | short sourcePort=udpHeader.getSourcePort();
220 | ipHeader.setSourceIP(ipHeader.getDestinationIP());
221 | ipHeader.setDestinationIP(sourceIP);
222 | ipHeader.setTotalLength(20+8+dnsPacket.Size);
223 | udpHeader.setSourcePort(udpHeader.getDestinationPort());
224 | udpHeader.setDestinationPort(sourcePort);
225 | udpHeader.setTotalLength(8+dnsPacket.Size);
226 | LocalVpnService.Instance.sendUDPPacket(ipHeader, udpHeader);
227 | return true;
228 | }
229 | }
230 | return false;
231 | }
232 |
233 | private void clearExpiredQueries(){
234 | long now=System.nanoTime();
235 | for (int i = m_QueryArray.size()-1; i>=0; i--) {
236 | QueryState state=m_QueryArray.valueAt(i);
237 | if ((now - state.QueryNanoTime)> QUERY_TIMEOUT_NS){
238 | m_QueryArray.removeAt(i);
239 | }
240 | }
241 | }
242 |
243 | public void onDnsRequestReceived(IPHeader ipHeader,UDPHeader udpHeader,DnsPacket dnsPacket){
244 | if(!interceptDns(ipHeader,udpHeader,dnsPacket)){
245 | //转发DNS
246 | QueryState state = new QueryState();
247 | state.ClientQueryID =dnsPacket.Header.ID;
248 | state.QueryNanoTime = System.nanoTime();
249 | state.ClientIP = ipHeader.getSourceIP();
250 | state.ClientPort = udpHeader.getSourcePort();
251 | state.RemoteIP = ipHeader.getDestinationIP();
252 | state.RemotePort = udpHeader.getDestinationPort();
253 |
254 | // 转换QueryID
255 | m_QueryID++;// 增加ID
256 | dnsPacket.Header.setID(m_QueryID);
257 |
258 | synchronized (m_QueryArray) {
259 | clearExpiredQueries();//清空过期的查询,减少内存开销。
260 | m_QueryArray.put(m_QueryID, state);// 关联数据
261 | }
262 |
263 | InetSocketAddress remoteAddress = new InetSocketAddress(CommonMethods.ipIntToInet4Address(state.RemoteIP ), state.RemotePort);
264 | DatagramPacket packet = new DatagramPacket(udpHeader.m_Data, udpHeader.m_Offset+8, dnsPacket.Size);
265 | packet.setSocketAddress(remoteAddress);
266 |
267 | try {
268 | if(LocalVpnService.Instance.protect(m_Client)){
269 | System.out.println("111 send");
270 | m_Client.send(packet);
271 | System.out.println("111 send end");
272 | }else {
273 | System.err.println("VPN protect udp socket failed.");
274 | }
275 | } catch (IOException e) {
276 | // TODO Auto-generated catch block
277 | e.printStackTrace();
278 | }
279 | }
280 | }
281 | }
282 |
--------------------------------------------------------------------------------
/Smart Proxy X/src/main/java/me/smartproxy/core/HttpHostHeaderParser.java:
--------------------------------------------------------------------------------
1 | package me.smartproxy.core;
2 |
3 | import java.util.Locale;
4 |
5 | import me.smartproxy.tcpip.CommonMethods;
6 |
7 |
8 | public class HttpHostHeaderParser {
9 |
10 | public static String parseHost(byte[] buffer,int offset,int count) {
11 | try {
12 | switch (buffer[offset]) {
13 | case 'G'://GET
14 | case 'H'://HEAD
15 | case 'P'://POST,PUT
16 | case 'D'://DELETE
17 | case 'O'://OPTIONS
18 | case 'T'://TRACE
19 | case 'C'://CONNECT
20 | return getHttpHost(buffer,offset,count);
21 | case 0x16://SSL
22 | return getSNI(buffer, offset, count);
23 | }
24 | } catch (Exception e) {
25 | e.printStackTrace();
26 | LocalVpnService.Instance.writeLog("Error: parseHost:%s", e);
27 | }
28 | return null;
29 | }
30 |
31 | static String getHttpHost(byte[] buffer,int offset,int count){
32 | String headerString=new String(buffer,offset,count);
33 | String[] headerLines=headerString.split("\\r\\n");
34 | String requestLine=headerLines[0];
35 | if(requestLine.startsWith("GET")||requestLine.startsWith("POST")||requestLine.startsWith("HEAD")||requestLine.startsWith("OPTIONS")){
36 | for (int i = 1; i < headerLines.length; i++) {
37 | String[] nameValueStrings=headerLines[i].split(":");
38 | if(nameValueStrings.length==2){
39 | String name=nameValueStrings[0].toLowerCase(Locale.ENGLISH).trim();
40 | String value=nameValueStrings[1].trim();
41 | if("host".equals(name)){
42 | return value;
43 | }
44 | }
45 | }
46 | }
47 | return null;
48 | }
49 |
50 | static String getSNI(byte[] buffer,int offset,int count){
51 | int limit=offset+count;
52 | if (count > 43 && buffer[offset] == 0x16) {//TLS Client Hello
53 | offset +=43;//skip 43 bytes header
54 |
55 | //read sessionID:
56 | if(offset+1>limit)return null;
57 | int sessionIDLength = buffer[offset++]&0xFF;
58 | offset += sessionIDLength;
59 |
60 | //read cipher suites:
61 | if(offset+2>limit)return null;
62 | int cipherSuitesLength = CommonMethods.readShort(buffer, offset)&0xFFFF;
63 | offset+=2;
64 | offset += cipherSuitesLength;
65 |
66 | //read Compression method:
67 | if(offset+1>limit)return null;
68 | int compressionMethodLength = buffer[offset++]&0xFF;
69 | offset += compressionMethodLength;
70 |
71 | if (offset == limit){
72 | System.err.println("TLS Client Hello packet doesn't contains SNI info.(offset == limit)");
73 | return null;
74 | }
75 |
76 | //read Extensions:
77 | if(offset+2>limit)return null;
78 | int extensionsLength = CommonMethods.readShort(buffer, offset)&0xFFFF;
79 | offset+=2;
80 |
81 | if (offset + extensionsLength > limit){
82 | System.err.println("TLS Client Hello packet is incomplete.");
83 | return null;
84 | }
85 |
86 | while (offset+4 <= limit){
87 | int type0 = buffer[offset++]&0xFF;
88 | int type1 = buffer[offset++]&0xFF;
89 | int length = CommonMethods.readShort(buffer, offset)&0xFFFF;
90 | offset+=2;
91 |
92 | if (type0 == 0x00 && type1 == 0x00 && length > 5){ //have SNI
93 | offset+=5;//skip SNI header.
94 | length-=5;//SNI size;
95 | if(offset+length>limit) return null;
96 | String serverName =new String(buffer, offset, length);
97 | if(ProxyConfig.IS_DEBUG)
98 | System.out.printf("SNI: %s\n", serverName);
99 | return serverName;
100 | }
101 | else {
102 | offset += length;
103 | }
104 | }
105 |
106 | System.err.println("TLS Client Hello packet doesn't contains Host field info.");
107 | return null;
108 | }
109 | else {
110 | System.err.println("Bad TLS Client Hello packet.");
111 | return null;
112 | }
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/Smart Proxy X/src/main/java/me/smartproxy/core/LocalVpnService.java:
--------------------------------------------------------------------------------
1 | package me.smartproxy.core;
2 |
3 | import java.io.FileInputStream;
4 | import java.io.FileOutputStream;
5 | import java.io.IOException;
6 | import java.lang.reflect.Method;
7 | import java.nio.ByteBuffer;
8 | import java.util.ArrayList;
9 | import java.util.Map;
10 | import java.util.UUID;
11 | import java.util.concurrent.ConcurrentHashMap;
12 |
13 | import me.smartproxy.core.ProxyConfig.IPAddress;
14 | import me.smartproxy.dns.DnsPacket;
15 | import me.smartproxy.tcpip.CommonMethods;
16 | import me.smartproxy.tcpip.IPHeader;
17 | import me.smartproxy.tcpip.TCPHeader;
18 | import me.smartproxy.tcpip.UDPHeader;
19 | import me.smartproxy.ui.MainActivity;
20 | import me.smartproxy.R;
21 |
22 | import android.app.PendingIntent;
23 | import android.content.Intent;
24 | import android.content.SharedPreferences;
25 | import android.content.SharedPreferences.Editor;
26 | import android.content.pm.PackageInfo;
27 | import android.content.pm.PackageManager;
28 | import android.net.VpnService;
29 | import android.os.Build;
30 | import android.os.Handler;
31 | import android.os.ParcelFileDescriptor;
32 |
33 | public class LocalVpnService extends VpnService implements Runnable {
34 |
35 | public static LocalVpnService Instance;
36 | public static String ConfigUrl;
37 | public static boolean IsRunning = false;
38 |
39 | private static int ID;
40 | private static int LOCAL_IP;
41 | private static ConcurrentHashMap m_OnStatusChangedListeners = new ConcurrentHashMap();
42 |
43 | private Thread m_VPNThread;
44 | private ParcelFileDescriptor m_VPNInterface;
45 | private TcpProxyServer m_TcpProxyServer;
46 | private DnsProxy m_DnsProxy;
47 | private FileOutputStream m_VPNOutputStream;
48 |
49 | private byte[] m_Packet;
50 | private IPHeader m_IPHeader;
51 | private TCPHeader m_TCPHeader;
52 | private UDPHeader m_UDPHeader;
53 | private ByteBuffer m_DNSBuffer;
54 | private Handler m_Handler;
55 | private long m_SentBytes;
56 | private long m_ReceivedBytes;
57 |
58 | public LocalVpnService() {
59 | ID++;
60 | m_Handler = new Handler();
61 | m_Packet = new byte[1024 * 256];
62 | m_IPHeader = new IPHeader(m_Packet, 0);
63 | m_TCPHeader = new TCPHeader(m_Packet, 20);
64 | m_UDPHeader = new UDPHeader(m_Packet, 20);
65 | m_DNSBuffer = ((ByteBuffer) ByteBuffer.wrap(m_Packet).position(28)).slice();
66 | Instance = this;
67 |
68 | System.out.printf("New VPNService(%d)\n", ID);
69 | }
70 |
71 | @Override
72 | public void onCreate() {
73 | System.out.printf("VPNService(%s) created.\n", ID);
74 | // Start a new session by creating a new thread.
75 | m_VPNThread = new Thread(this, "VPNServiceThread");
76 | m_VPNThread.start();
77 | super.onCreate();
78 | }
79 |
80 | @Override
81 | public int onStartCommand(Intent intent, int flags, int startId) {
82 | IsRunning = true;
83 | return super.onStartCommand(intent, flags, startId);
84 | }
85 |
86 | public interface onStatusChangedListener {
87 | public void onStatusChanged(String status, Boolean isRunning);
88 |
89 | public void onLogReceived(String logString);
90 | }
91 |
92 | public static void addOnStatusChangedListener(onStatusChangedListener listener) {
93 | if (!m_OnStatusChangedListeners.containsKey(listener)) {
94 | m_OnStatusChangedListeners.put(listener, 1);
95 | }
96 | }
97 |
98 | public static void removeOnStatusChangedListener(onStatusChangedListener listener) {
99 | if (m_OnStatusChangedListeners.containsKey(listener)) {
100 | m_OnStatusChangedListeners.remove(listener);
101 | }
102 | }
103 |
104 | private void onStatusChanged(final String status, final boolean isRunning) {
105 | m_Handler.post(new Runnable() {
106 | @Override
107 | public void run() {
108 | for (Map.Entry entry : m_OnStatusChangedListeners.entrySet()) {
109 | entry.getKey().onStatusChanged(status, isRunning);
110 | }
111 | }
112 | });
113 | }
114 |
115 | public void writeLog(final String format, Object... args) {
116 | final String logString = String.format(format, args);
117 | m_Handler.post(new Runnable() {
118 | @Override
119 | public void run() {
120 | for (Map.Entry entry : m_OnStatusChangedListeners.entrySet()) {
121 | entry.getKey().onLogReceived(logString);
122 | }
123 | }
124 | });
125 | }
126 |
127 | public void sendUDPPacket(IPHeader ipHeader, UDPHeader udpHeader) {
128 | try {
129 | CommonMethods.ComputeUDPChecksum(ipHeader, udpHeader);
130 | this.m_VPNOutputStream.write(ipHeader.m_Data, ipHeader.m_Offset, ipHeader.getTotalLength());
131 | } catch (IOException e) {
132 | e.printStackTrace();
133 | }
134 | }
135 |
136 | String getAppInstallID() {
137 | SharedPreferences preferences = getSharedPreferences("SmartProxy", MODE_PRIVATE);
138 | String appInstallID = preferences.getString("AppInstallID", null);
139 | if (appInstallID == null || appInstallID.isEmpty()) {
140 | appInstallID = UUID.randomUUID().toString();
141 | Editor editor = preferences.edit();
142 | editor.putString("AppInstallID", appInstallID);
143 | editor.commit();
144 | }
145 | return appInstallID;
146 | }
147 |
148 | String getVersionName() {
149 | try {
150 | PackageManager packageManager = getPackageManager();
151 | // getPackageName()是你当前类的包名,0代表是获取版本信息
152 | PackageInfo packInfo = packageManager.getPackageInfo(getPackageName(), 0);
153 | String version = packInfo.versionName;
154 | return version;
155 | } catch (Exception e) {
156 | return "0.0";
157 | }
158 | }
159 |
160 | @Override
161 | public synchronized void run() {
162 | try {
163 | System.out.printf("VPNService(%s) work thread is runing...\n", ID);
164 |
165 | ProxyConfig.AppInstallID = getAppInstallID();//获取安装ID
166 | ProxyConfig.AppVersion = getVersionName();//获取版本号
167 | System.out.printf("AppInstallID: %s\n", ProxyConfig.AppInstallID);
168 |
169 | writeLog("Android version: %s", Build.VERSION.RELEASE);
170 | writeLog("App version: %s", ProxyConfig.AppVersion);
171 |
172 |
173 | ChinaIpMaskManager.loadFromFile(getResources().openRawResource(R.raw.ipmask));//加载中国的IP段,用于IP分流。
174 | waitUntilPreapred();//检查是否准备完毕。
175 |
176 | m_TcpProxyServer = new TcpProxyServer(0);
177 | m_TcpProxyServer.start();
178 | writeLog("LocalTcpServer started.");
179 |
180 | m_DnsProxy = new DnsProxy();
181 | m_DnsProxy.start();
182 | writeLog("LocalDnsProxy started.");
183 |
184 | while (true) {
185 | if (IsRunning) {
186 | //加载配置文件
187 |
188 | writeLog("Load config from %s ...", ConfigUrl);
189 | try {
190 |
191 | ProxyConfig.Instance.loadFromUrl(ConfigUrl);
192 |
193 | if (ProxyConfig.Instance.getDefaultProxy() == null) {
194 |
195 | throw new Exception("Invalid config file.");
196 | }
197 | writeLog("PROXY %s", ProxyConfig.Instance.getDefaultProxy());
198 | } catch (Exception e) {
199 |
200 | String errString = e.getMessage();
201 | writeLog("Load config err %s", errString);
202 | if (errString == null || errString.isEmpty()) {
203 | errString = e.toString();
204 | }
205 | System.out.println(errString);
206 |
207 | IsRunning = false;
208 | onStatusChanged(errString, false);
209 | continue;
210 | }
211 |
212 | writeLog("Load config success.");
213 | String welcomeInfoString = ProxyConfig.Instance.getWelcomeInfo();
214 | if (welcomeInfoString != null && !welcomeInfoString.isEmpty()) {
215 | writeLog("%s", ProxyConfig.Instance.getWelcomeInfo());
216 | }
217 | runVPN();
218 | } else {
219 | Thread.sleep(100);
220 | }
221 | }
222 | } catch (InterruptedException e) {
223 |
224 | System.out.println(e);
225 | } catch (Exception e) {
226 |
227 | e.printStackTrace();
228 | writeLog("Fatal error: %s", e.toString());
229 | } finally {
230 |
231 | writeLog("SmartProxy terminated.");
232 | dispose();
233 | }
234 | }
235 |
236 | private void runVPN() throws Exception {
237 |
238 | this.m_VPNInterface = establishVPN();
239 | this.m_VPNOutputStream = new FileOutputStream(m_VPNInterface.getFileDescriptor());
240 |
241 | FileInputStream in = new FileInputStream(m_VPNInterface.getFileDescriptor());
242 | int size = 0;
243 |
244 | while (size != -1 && IsRunning) {
245 | while ((size = in.read(m_Packet)) > 0 && IsRunning) {
246 | if (m_DnsProxy.Stopped || m_TcpProxyServer.Stopped) {
247 | in.close();
248 | throw new Exception("LocalServer stopped.");
249 | }
250 | onIPPacketReceived(m_IPHeader, size);
251 | }
252 | Thread.sleep(100);
253 | }
254 | in.close();
255 | disconnectVPN();
256 | }
257 |
258 | void onIPPacketReceived(IPHeader ipHeader, int size) throws IOException {
259 | switch (ipHeader.getProtocol()) {
260 | case IPHeader.TCP:
261 | TCPHeader tcpHeader = m_TCPHeader;
262 | tcpHeader.m_Offset = ipHeader.getHeaderLength();
263 | // System.out.printf(
264 | // "receive ip: %s to ip: %s\n",
265 | // CommonMethods.ipIntToString(ipHeader.getSourceIP()),
266 | // CommonMethods.ipIntToString(ipHeader.getDestinationIP())
267 | // );
268 |
269 | if (ipHeader.getSourceIP() == LOCAL_IP) {
270 | if (tcpHeader.getSourcePort() == m_TcpProxyServer.Port) {// 收到本地TCP服务器数据
271 | NatSession session = NatSessionManager.getSession(tcpHeader.getDestinationPort());
272 | if (session != null) {
273 | ipHeader.setSourceIP(ipHeader.getDestinationIP());
274 | tcpHeader.setSourcePort(session.RemotePort);
275 | ipHeader.setDestinationIP(LOCAL_IP);
276 |
277 | CommonMethods.ComputeTCPChecksum(ipHeader, tcpHeader);
278 | m_VPNOutputStream.write(ipHeader.m_Data, ipHeader.m_Offset, size);
279 | m_ReceivedBytes += size;
280 | } else {
281 | System.out.printf("NoSession: %s %s\n", ipHeader.toString(), tcpHeader.toString());
282 | }
283 | } else {
284 |
285 | // 添加端口映射
286 | int portKey = tcpHeader.getSourcePort();
287 | NatSession session = NatSessionManager.getSession(portKey);
288 | if (session == null || session.RemoteIP != ipHeader.getDestinationIP() || session.RemotePort != tcpHeader.getDestinationPort()) {
289 | session = NatSessionManager.createSession(portKey, ipHeader.getDestinationIP(), tcpHeader.getDestinationPort());
290 | }
291 |
292 | session.LastNanoTime = System.nanoTime();
293 | session.PacketSent++;//注意顺序
294 |
295 | int tcpDataSize = ipHeader.getDataLength() - tcpHeader.getHeaderLength();
296 | if (session.PacketSent == 3) {
297 | System.out.println(123);
298 | }
299 | if (session.PacketSent == 2 && tcpDataSize == 0) {
300 | return;//丢弃tcp握手的第二个ACK报文。因为客户端发数据的时候也会带上ACK,这样可以在服务器Accept之前分析出HOST信息。
301 | }
302 |
303 | //分析数据,找到host
304 | if (session.BytesSent == 0 && tcpDataSize > 10) {
305 | int dataOffset = tcpHeader.m_Offset + tcpHeader.getHeaderLength();
306 | String host = HttpHostHeaderParser.parseHost(tcpHeader.m_Data, dataOffset, tcpDataSize);
307 | if (host != null) {
308 | session.RemoteHost = host;
309 | System.out.println("host: " + host);
310 | }
311 | }
312 |
313 | // 转发给本地TCP服务器
314 | ipHeader.setSourceIP(ipHeader.getDestinationIP());
315 | ipHeader.setDestinationIP(LOCAL_IP);
316 | tcpHeader.setDestinationPort(m_TcpProxyServer.Port);
317 |
318 | CommonMethods.ComputeTCPChecksum(ipHeader, tcpHeader);
319 | m_VPNOutputStream.write(ipHeader.m_Data, ipHeader.m_Offset, size);
320 | session.BytesSent += tcpDataSize;//注意顺序
321 | m_SentBytes += size;
322 | }
323 | }
324 | break;
325 | case IPHeader.UDP:
326 | // 转发DNS数据包:
327 | UDPHeader udpHeader = m_UDPHeader;
328 | udpHeader.m_Offset = ipHeader.getHeaderLength();
329 | if (ipHeader.getSourceIP() == LOCAL_IP && udpHeader.getDestinationPort() == 53) {
330 | // if (ipHeader.getSourceIP() == LOCAL_IP) {
331 | m_DNSBuffer.clear();
332 | m_DNSBuffer.limit(ipHeader.getDataLength() - 8);
333 | DnsPacket dnsPacket = DnsPacket.FromBytes(m_DNSBuffer);
334 | if (dnsPacket != null && dnsPacket.Header.QuestionCount > 0) {
335 | m_DnsProxy.onDnsRequestReceived(ipHeader, udpHeader, dnsPacket);
336 | }
337 | }
338 | break;
339 | }
340 | }
341 |
342 | private void waitUntilPreapred() {
343 | while (prepare(this) != null) {
344 | try {
345 | Thread.sleep(100);
346 | } catch (InterruptedException e) {
347 | e.printStackTrace();
348 | }
349 | }
350 | }
351 |
352 | private ParcelFileDescriptor establishVPN() throws Exception {
353 |
354 | Builder builder = new Builder();
355 |
356 | builder.setMtu(ProxyConfig.Instance.getMTU());
357 |
358 | if (ProxyConfig.IS_DEBUG)
359 | System.out.printf("setMtu: %d\n", ProxyConfig.Instance.getMTU());
360 |
361 | IPAddress ipAddress = ProxyConfig.Instance.getDefaultLocalIP();
362 | LOCAL_IP = CommonMethods.ipStringToInt(ipAddress.Address);
363 | builder.addAddress(ipAddress.Address, ipAddress.PrefixLength);
364 | builder.addAllowedApplication("me.smartproxy");
365 | builder.addAllowedApplication("com.android.chrome");
366 | if (ProxyConfig.IS_DEBUG)
367 | System.out.printf("addAddress: %s/%d\n", ipAddress.Address, ipAddress.PrefixLength);
368 |
369 | for (ProxyConfig.IPAddress dns : ProxyConfig.Instance.getDnsList()) {
370 | builder.addDnsServer(dns.Address);
371 | if (ProxyConfig.IS_DEBUG)
372 | System.out.printf("addDnsServer: %s\n", dns.Address);
373 | }
374 |
375 | if (ProxyConfig.Instance.getRouteList().size() > 0) {
376 | for (ProxyConfig.IPAddress routeAddress : ProxyConfig.Instance.getRouteList()) {
377 | builder.addRoute(routeAddress.Address, routeAddress.PrefixLength);
378 | if (ProxyConfig.IS_DEBUG)
379 | System.out.printf("addRoute: %s/%d\n", routeAddress.Address, routeAddress.PrefixLength);
380 | }
381 | builder.addRoute(CommonMethods.ipIntToString(ProxyConfig.FAKE_NETWORK_IP), 16);
382 |
383 | if (ProxyConfig.IS_DEBUG)
384 | System.out.printf("addRoute for FAKE_NETWORK: %s/%d\n", CommonMethods.ipIntToString(ProxyConfig.FAKE_NETWORK_IP), 16);
385 | } else {
386 | builder.addRoute("0.0.0.0", 0);
387 | if (ProxyConfig.IS_DEBUG)
388 | System.out.printf("addDefaultRoute: 0.0.0.0/0\n");
389 | }
390 |
391 |
392 | Class> SystemProperties = Class.forName("android.os.SystemProperties");
393 | Method method = SystemProperties.getMethod("get", new Class[]{String.class});
394 | ArrayList servers = new ArrayList();
395 | for (String name : new String[]{"net.dns1", "net.dns2", "net.dns3", "net.dns4",}) {
396 | // for (String name : new String[] { "223.5.5.5", "114.114.114.114", "192.168.50.1"}) {
397 | String value = (String) method.invoke(null, name);
398 | // String value = name;
399 | if (value != null && !"".equals(value) && !servers.contains(value)) {
400 | servers.add(value);
401 | builder.addRoute(value, 32);
402 | if (ProxyConfig.IS_DEBUG)
403 | System.out.printf("%s=%s\n, 111111", name, value);
404 | }
405 | }
406 |
407 | Intent intent = new Intent(this, MainActivity.class);
408 | PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);
409 | builder.setConfigureIntent(pendingIntent);
410 |
411 | builder.setSession(ProxyConfig.Instance.getSessionName());
412 | ParcelFileDescriptor pfdDescriptor = builder.establish();
413 | onStatusChanged(ProxyConfig.Instance.getSessionName() + getString(R.string.vpn_connected_status), true);
414 | return pfdDescriptor;
415 | }
416 |
417 | public void disconnectVPN() {
418 | try {
419 | if (m_VPNInterface != null) {
420 | m_VPNInterface.close();
421 | m_VPNInterface = null;
422 | }
423 | } catch (Exception e) {
424 | // ignore
425 | }
426 | onStatusChanged(ProxyConfig.Instance.getSessionName() + getString(R.string.vpn_disconnected_status), false);
427 | this.m_VPNOutputStream = null;
428 | }
429 |
430 | private synchronized void dispose() {
431 | // 断开VPN
432 | disconnectVPN();
433 |
434 | // 停止TcpServer
435 | if (m_TcpProxyServer != null) {
436 | m_TcpProxyServer.stop();
437 | m_TcpProxyServer = null;
438 | writeLog("LocalTcpServer stopped.");
439 | }
440 |
441 | // 停止DNS解析器
442 | if (m_DnsProxy != null) {
443 | m_DnsProxy.stop();
444 | m_DnsProxy = null;
445 | writeLog("LocalDnsProxy stopped.");
446 | }
447 |
448 | stopSelf();
449 | IsRunning = false;
450 | System.exit(0);
451 | }
452 |
453 | @Override
454 | public void onDestroy() {
455 | System.out.printf("VPNService(%s) destoried.\n", ID);
456 | if (m_VPNThread != null) {
457 | m_VPNThread.interrupt();
458 | }
459 | }
460 |
461 | }
462 |
--------------------------------------------------------------------------------
/Smart Proxy X/src/main/java/me/smartproxy/core/NatSession.java:
--------------------------------------------------------------------------------
1 | package me.smartproxy.core;
2 |
3 | public class NatSession{
4 | public int RemoteIP;
5 | public short RemotePort;
6 | public String RemoteHost;
7 | public int BytesSent;
8 | public int PacketSent;
9 | public long LastNanoTime;
10 | }
11 |
--------------------------------------------------------------------------------
/Smart Proxy X/src/main/java/me/smartproxy/core/NatSessionManager.java:
--------------------------------------------------------------------------------
1 | package me.smartproxy.core;
2 |
3 | import me.smartproxy.tcpip.CommonMethods;
4 | import android.util.SparseArray;
5 |
6 | public class NatSessionManager {
7 |
8 | static final int MAX_SESSION_COUNT=60;
9 | static final long SESSION_TIMEOUT_NS=60*1000000000L;
10 | static final SparseArray Sessions = new SparseArray();
11 |
12 | public static NatSession getSession(int portKey){
13 | return Sessions.get(portKey);
14 | }
15 |
16 | public static int getSessionCount() {
17 | return Sessions.size();
18 | }
19 |
20 | static void clearExpiredSessions(){
21 | long now=System.nanoTime();
22 | for (int i = Sessions.size()-1; i>=0; i--) {
23 | NatSession session=Sessions.valueAt(i);
24 | if(now- session.LastNanoTime>SESSION_TIMEOUT_NS){
25 | Sessions.removeAt(i);
26 | }
27 | }
28 | }
29 |
30 | public static NatSession createSession(int portKey,int remoteIP,short remotePort){
31 | if(Sessions.size()>MAX_SESSION_COUNT){
32 | clearExpiredSessions();//清理过期的会话。
33 | }
34 |
35 | NatSession session=new NatSession();
36 | session.LastNanoTime=System.nanoTime();
37 | session.RemoteIP=remoteIP;
38 | session.RemotePort=remotePort;
39 |
40 | if(ProxyConfig.isFakeIP(remoteIP)){
41 | session.RemoteHost=DnsProxy.reverseLookup(remoteIP);
42 | }
43 |
44 | if(session.RemoteHost==null){
45 | session.RemoteHost=CommonMethods.ipIntToString(remoteIP);
46 | }
47 | Sessions.put(portKey, session);
48 | return session;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/Smart Proxy X/src/main/java/me/smartproxy/core/ProxyConfig.java:
--------------------------------------------------------------------------------
1 | package me.smartproxy.core;
2 |
3 | import android.annotation.SuppressLint;
4 |
5 | import java.io.FileInputStream;
6 | import java.net.InetAddress;
7 | import java.net.InetSocketAddress;
8 | import java.util.ArrayList;
9 | import java.util.HashMap;
10 | import java.util.Locale;
11 | import java.util.Timer;
12 | import java.util.TimerTask;
13 | import java.util.regex.Matcher;
14 | import java.util.regex.Pattern;
15 |
16 | import me.smartproxy.tcpip.CommonMethods;
17 | import me.smartproxy.tunnel.Config;
18 | import me.smartproxy.tunnel.httpconnect.HttpConnectConfig;
19 | import me.smartproxy.tunnel.shadowsocks.ShadowsocksConfig;
20 |
21 | public class ProxyConfig {
22 | public static final ProxyConfig Instance = new ProxyConfig();
23 | public final static boolean IS_DEBUG = true;
24 | public static String AppInstallID;
25 | public static String AppVersion;
26 | public final static int FAKE_NETWORK_MASK = CommonMethods.ipStringToInt("255.255.0.0");
27 | public final static int FAKE_NETWORK_IP = CommonMethods.ipStringToInt("10.231.0.0");
28 |
29 | ArrayList m_IpList;
30 | ArrayList m_DnsList;
31 | ArrayList m_RouteList;
32 | ArrayList m_ProxyList;
33 | HashMap m_DomainMap;
34 |
35 | int m_dns_ttl;
36 | String m_welcome_info;
37 | String m_session_name;
38 | String m_user_agent;
39 | boolean m_outside_china_use_proxy = true;
40 | boolean m_isolate_http_host_header = true;
41 | int m_mtu;
42 |
43 | Timer m_Timer;
44 |
45 | public class IPAddress {
46 | public final String Address;
47 | public final int PrefixLength;
48 |
49 | public IPAddress(String address, int prefixLength) {
50 | this.Address = address;
51 | this.PrefixLength = prefixLength;
52 | }
53 |
54 | public IPAddress(String ipAddresString) {
55 | String[] arrStrings = ipAddresString.split("/");
56 | String address = arrStrings[0];
57 | int prefixLength = 32;
58 | if (arrStrings.length > 1) {
59 | prefixLength = Integer.parseInt(arrStrings[1]);
60 | }
61 | this.Address = address;
62 | this.PrefixLength = prefixLength;
63 | }
64 |
65 | @SuppressLint("DefaultLocale")
66 | @Override
67 | public String toString() {
68 | return String.format("%s/%d", Address, PrefixLength);
69 | }
70 |
71 | @Override
72 | public boolean equals(Object o) {
73 | if (o == null) {
74 | return false;
75 | } else {
76 | return this.toString().equals(o.toString());
77 | }
78 | }
79 | }
80 |
81 | public ProxyConfig() {
82 | m_IpList = new ArrayList();
83 | m_DnsList = new ArrayList();
84 | m_RouteList = new ArrayList();
85 | m_ProxyList = new ArrayList();
86 | m_DomainMap = new HashMap();
87 |
88 | m_Timer = new Timer();
89 | m_Timer.schedule(m_Task, 120000, 120000);//每两分钟刷新一次。
90 | }
91 |
92 | TimerTask m_Task = new TimerTask() {
93 | @Override
94 | public void run() {
95 | refreshProxyServer();//定时更新dns缓存
96 | }
97 |
98 | //定时更新dns缓存
99 | void refreshProxyServer() {
100 | try {
101 | for (int i = 0; i < m_ProxyList.size(); i++) {
102 | try {
103 | Config config = m_ProxyList.get(0);
104 | InetAddress address = InetAddress.getByName(config.ServerAddress.getHostName());
105 | if (address != null && !address.equals(config.ServerAddress.getAddress())) {
106 | config.ServerAddress = new InetSocketAddress(address, config.ServerAddress.getPort());
107 | }
108 | } catch (Exception e) {
109 | }
110 | }
111 | } catch (Exception e) {
112 |
113 | }
114 | }
115 | };
116 |
117 |
118 | public static boolean isFakeIP(int ip) {
119 | return (ip & ProxyConfig.FAKE_NETWORK_MASK) == ProxyConfig.FAKE_NETWORK_IP;
120 | }
121 |
122 | public Config getDefaultProxy() {
123 | if (m_ProxyList.size() > 0) {
124 | return m_ProxyList.get(0);
125 | } else {
126 | return null;
127 | }
128 | }
129 |
130 | public Config getDefaultTunnelConfig(InetSocketAddress destAddress) {
131 | return getDefaultProxy();
132 | }
133 |
134 | public IPAddress getDefaultLocalIP() {
135 | if (m_IpList.size() > 0) {
136 | return m_IpList.get(0);
137 | } else {
138 | return new IPAddress("10.8.0.2", 32);
139 | }
140 | }
141 |
142 | public ArrayList getDnsList() {
143 | return m_DnsList;
144 | }
145 |
146 | public ArrayList getRouteList() {
147 | return m_RouteList;
148 | }
149 |
150 | public int getDnsTTL() {
151 | if (m_dns_ttl < 30) {
152 | m_dns_ttl = 30;
153 | }
154 | return m_dns_ttl;
155 | }
156 |
157 | public String getWelcomeInfo() {
158 | return m_welcome_info;
159 | }
160 |
161 | public String getSessionName() {
162 | if (m_session_name == null) {
163 | m_session_name = getDefaultProxy().ServerAddress.getHostName();
164 | }
165 | return m_session_name;
166 | }
167 |
168 | public String getUserAgent() {
169 | if (m_user_agent == null || m_user_agent.isEmpty()) {
170 | m_user_agent = System.getProperty("http.agent");
171 | }
172 | return m_user_agent;
173 | }
174 |
175 | public int getMTU() {
176 | if (m_mtu > 1400 && m_mtu <= 20000) {
177 | return m_mtu;
178 | } else {
179 | return 1400;
180 | }
181 | }
182 |
183 | private Boolean getDomainState(String domain) {
184 | domain = domain.toLowerCase();
185 | while (domain.length() > 0) {
186 | Boolean stateBoolean = m_DomainMap.get(domain);
187 | if (stateBoolean != null) {
188 | return stateBoolean;
189 | } else {
190 | int start = domain.indexOf('.') + 1;
191 | if (start > 0 && start < domain.length()) {
192 | domain = domain.substring(start);
193 | } else {
194 | return null;
195 | }
196 | }
197 | }
198 | return null;
199 | }
200 |
201 | public boolean needProxy(String host, int ip) {
202 | System.out.println(String.format("%s %s ", host, ip));
203 | if (host != null) {
204 | Boolean stateBoolean = getDomainState(host);
205 | if (stateBoolean != null) {
206 | return stateBoolean.booleanValue();
207 | }
208 | }
209 |
210 | if (isFakeIP(ip))
211 | return true;
212 |
213 | if (m_outside_china_use_proxy && ip != 0) {
214 | return !ChinaIpMaskManager.isIPInChina(ip);
215 | }
216 | return false;
217 | }
218 |
219 | public boolean isIsolateHttpHostHeader() {
220 | return m_isolate_http_host_header;
221 | }
222 |
223 |
224 | private String[] downloadConfig(String url) throws Exception {
225 | try {
226 | /* HttpClient client=new DefaultHttpClient();
227 | HttpGet requestGet=new HttpGet(url);
228 |
229 | requestGet.addHeader("X-Android-MODEL", Build.MODEL);
230 | requestGet.addHeader("X-Android-SDK_INT",Integer.toString(Build.VERSION.SDK_INT));
231 | requestGet.addHeader("X-Android-RELEASE", Build.VERSION.RELEASE);
232 | requestGet.addHeader("X-App-Version", AppVersion);
233 | requestGet.addHeader("X-App-Install-ID", AppInstallID);
234 | requestGet.setHeader("User-Agent", System.getProperty("http.agent"));
235 | HttpResponse response=client.execute(requestGet);
236 |
237 | String configString=EntityUtils.toString(response.getEntity(),"UTF-8");*/
238 | String user_pwd = "";
239 | if (!TmpConfig.UserName.equals("")) {
240 | user_pwd = String.format("%s:%s@", TmpConfig.UserName, TmpConfig.Password);
241 | }
242 | //String configString = String.format("proxy http://%s%s:%s", user_pwd, TmpConfig.remoteIp, TmpConfig.remotePort);
243 | String configString = String.format("proxy http://%s127.0.0.1:9000", user_pwd);
244 | String[] lines = configString.split("\\n");
245 | return lines;
246 | } catch (Exception e) {
247 | throw new Exception(String.format("Download config file from %s failed.", url));
248 | }
249 | }
250 |
251 | private String[] readConfigFromFile(String path) throws Exception {
252 | StringBuilder sBuilder = new StringBuilder();
253 | FileInputStream inputStream = null;
254 | try {
255 | byte[] buffer = new byte[8192];
256 | int count = 0;
257 | inputStream = new FileInputStream(path);
258 | while ((count = inputStream.read(buffer)) > 0) {
259 | sBuilder.append(new String(buffer, 0, count, "UTF-8"));
260 | }
261 | return sBuilder.toString().split("\\n");
262 | } catch (Exception e) {
263 | throw new Exception(String.format("Can't read config file: %s", path));
264 | } finally {
265 | if (inputStream != null) {
266 | try {
267 | inputStream.close();
268 | } catch (Exception e2) {
269 | }
270 | }
271 | }
272 | }
273 |
274 | public void loadFromUrl(String url) throws Exception {
275 | String[] lines = null;
276 | if (url.charAt(0) == '/') {
277 | lines = readConfigFromFile(url);
278 | } else {
279 | lines = downloadConfig(url);
280 | }
281 |
282 | m_IpList.clear();
283 | m_DnsList.clear();
284 | m_RouteList.clear();
285 | m_ProxyList.clear();
286 | m_DomainMap.clear();
287 |
288 | int lineNumber = 0;
289 | for (String line : lines) {
290 | lineNumber++;
291 | String[] items = line.split("\\s+");
292 | if (items.length < 2) {
293 | continue;
294 | }
295 |
296 | String tagString = items[0].toLowerCase(Locale.ENGLISH).trim();
297 | try {
298 | if (!tagString.startsWith("#")) {
299 | if (ProxyConfig.IS_DEBUG)
300 | System.out.println(line);
301 |
302 | if (tagString.equals("ip")) {
303 | addIPAddressToList(items, 1, m_IpList);
304 | } else if (tagString.equals("dns")) {
305 | addIPAddressToList(items, 1, m_DnsList);
306 | } else if (tagString.equals("route")) {
307 | addIPAddressToList(items, 1, m_RouteList);
308 | } else if (tagString.equals("proxy")) {
309 | addProxyToList(items, 1);
310 | } else if (tagString.equals("direct_domain")) {
311 | addDomainToHashMap(items, 1, false);
312 | } else if (tagString.equals("proxy_domain")) {
313 | addDomainToHashMap(items, 1, true);
314 | } else if (tagString.equals("dns_ttl")) {
315 | m_dns_ttl = Integer.parseInt(items[1]);
316 | } else if (tagString.equals("welcome_info")) {
317 | m_welcome_info = line.substring(line.indexOf(" ")).trim();
318 | } else if (tagString.equals("session_name")) {
319 | m_session_name = items[1];
320 | } else if (tagString.equals("user_agent")) {
321 | m_user_agent = line.substring(line.indexOf(" ")).trim();
322 | } else if (tagString.equals("outside_china_use_proxy")) {
323 | m_outside_china_use_proxy = convertToBool(items[1]);
324 | } else if (tagString.equals("isolate_http_host_header")) {
325 | m_isolate_http_host_header = convertToBool(items[1]);
326 | } else if (tagString.equals("mtu")) {
327 | m_mtu = Integer.parseInt(items[1]);
328 | }
329 | }
330 | } catch (Exception e) {
331 | throw new Exception(String.format("SmartProxy config file parse error: line:%d, tag:%s, error:%s", lineNumber, tagString, e));
332 | }
333 |
334 | }
335 |
336 | //查找默认代理。
337 | if (m_ProxyList.size() == 0) {
338 | tryAddProxy(lines);
339 | }
340 | }
341 |
342 | private void tryAddProxy(String[] lines) {
343 | for (String line : lines) {
344 | Pattern p = Pattern.compile("proxy\\s+([^:]+):(\\d+)", Pattern.CASE_INSENSITIVE);
345 | Matcher m = p.matcher(line);
346 | while (m.find()) {
347 | HttpConnectConfig config = new HttpConnectConfig();
348 | config.ServerAddress = new InetSocketAddress(m.group(1), Integer.parseInt(m.group(2)));
349 | if (!m_ProxyList.contains(config)) {
350 | m_ProxyList.add(config);
351 | m_DomainMap.put(config.ServerAddress.getHostName(), false);
352 | }
353 | }
354 | }
355 | }
356 |
357 | private void addProxyToList(String[] items, int offset) throws Exception {
358 | for (int i = offset; i < items.length; i++) {
359 | String proxyString = items[i].trim();
360 | Config config = null;
361 | if (proxyString.startsWith("ss://")) {
362 | config = ShadowsocksConfig.parse(proxyString);
363 | } else {
364 | if (!proxyString.toLowerCase().startsWith("http://")) {
365 | proxyString = "http://" + proxyString;
366 | }
367 | config = HttpConnectConfig.parse(proxyString);
368 | }
369 | if (!m_ProxyList.contains(config)) {
370 | m_ProxyList.add(config);
371 | m_DomainMap.put(config.ServerAddress.getHostName(), false);
372 | }
373 | }
374 | }
375 |
376 | private void addDomainToHashMap(String[] items, int offset, Boolean state) {
377 | for (int i = offset; i < items.length; i++) {
378 | String domainString = items[i].toLowerCase().trim();
379 | if (domainString.charAt(0) == '.') {
380 | domainString = domainString.substring(1);
381 | }
382 | m_DomainMap.put(domainString, state);
383 | }
384 | }
385 |
386 | private boolean convertToBool(String valueString) {
387 | if (valueString == null || valueString.isEmpty())
388 | return false;
389 | valueString = valueString.toLowerCase(Locale.ENGLISH).trim();
390 | if (valueString.equals("on") || valueString.equals("1") || valueString.equals("true") || valueString.equals("yes")) {
391 | return true;
392 | } else {
393 | return false;
394 | }
395 | }
396 |
397 |
398 | private void addIPAddressToList(String[] items, int offset, ArrayList list) {
399 | for (int i = offset; i < items.length; i++) {
400 | String item = items[i].trim().toLowerCase();
401 | if (item.startsWith("#")) {
402 | break;
403 | } else {
404 | IPAddress ip = new IPAddress(item);
405 | if (!list.contains(ip)) {
406 | list.add(ip);
407 | }
408 | }
409 | }
410 | }
411 |
412 | }
413 |
--------------------------------------------------------------------------------
/Smart Proxy X/src/main/java/me/smartproxy/core/TcpProxyServer.java:
--------------------------------------------------------------------------------
1 | package me.smartproxy.core;
2 |
3 | import java.io.IOException;
4 | import java.net.Inet4Address;
5 | import java.net.InetAddress;
6 | import java.net.InetSocketAddress;
7 | import java.nio.channels.SelectionKey;
8 | import java.nio.channels.Selector;
9 | import java.nio.channels.ServerSocketChannel;
10 | import java.nio.channels.SocketChannel;
11 | import java.util.Iterator;
12 |
13 | import me.smartproxy.tcpip.CommonMethods;
14 | import me.smartproxy.tunnel.Tunnel;
15 |
16 | public class TcpProxyServer implements Runnable {
17 |
18 | public boolean Stopped;
19 | public short Port;
20 |
21 | Selector m_Selector;
22 | ServerSocketChannel m_ServerSocketChannel;
23 | Thread m_ServerThread;
24 |
25 | public TcpProxyServer(int port) throws IOException {
26 | m_Selector = Selector.open();
27 | m_ServerSocketChannel = ServerSocketChannel.open();
28 | m_ServerSocketChannel.configureBlocking(false);
29 | InetSocketAddress addr = new InetSocketAddress(InetAddress.getByName("0.0.0.0"), 0);
30 | m_ServerSocketChannel.socket().bind(addr);
31 | m_ServerSocketChannel.register(m_Selector, SelectionKey.OP_ACCEPT);
32 | this.Port=(short) m_ServerSocketChannel.socket().getLocalPort();
33 | System.out.printf("AsyncTcpServer listen on %d success.\n", this.Port&0xFFFF);
34 | System.out.println(m_ServerSocketChannel.getLocalAddress());
35 | }
36 |
37 | public void start(){
38 | m_ServerThread=new Thread(this);
39 | m_ServerThread.setName("TcpProxyServerThread");
40 | m_ServerThread.start();
41 | }
42 |
43 | public void stop(){
44 | this.Stopped=true;
45 | if(m_Selector!=null){
46 | try {
47 | m_Selector.close();
48 | m_Selector=null;
49 | } catch (Exception e) {
50 | e.printStackTrace();
51 | }
52 | }
53 |
54 | if(m_ServerSocketChannel!=null){
55 | try {
56 | m_ServerSocketChannel.close();
57 | m_ServerSocketChannel=null;
58 | } catch (Exception e) {
59 | e.printStackTrace();
60 | }
61 | }
62 | }
63 |
64 | @Override
65 | public void run() {
66 | try {
67 | while (true) {
68 | m_Selector.select();
69 | Iterator keyIterator = m_Selector.selectedKeys().iterator();
70 | while (keyIterator.hasNext()) {
71 | SelectionKey key = keyIterator.next();
72 | if (key.isValid()) {
73 | try {
74 | if (key.isReadable()) {
75 | ((Tunnel)key.attachment()).onReadable(key);
76 | }
77 | else if(key.isWritable()){
78 | ((Tunnel)key.attachment()).onWritable(key);
79 | }
80 | else if (key.isConnectable()) {
81 | ((Tunnel)key.attachment()).onConnectable();
82 | }
83 | else if (key.isAcceptable()) {
84 | onAccepted(key);
85 | }
86 | } catch (Exception e) {
87 | System.out.println(e.toString());
88 | }
89 | }
90 | keyIterator.remove();
91 | }
92 | }
93 | } catch (Exception e) {
94 | e.printStackTrace();
95 | }finally{
96 | this.stop();
97 | System.out.println("TcpServer thread exited.");
98 | }
99 | }
100 |
101 | InetSocketAddress getDestAddress(SocketChannel localChannel){
102 | short portKey=(short)localChannel.socket().getPort();
103 | NatSession session =NatSessionManager.getSession(portKey);
104 | if (session != null) {
105 | // 判断是否走代理
106 | if(ProxyConfig.Instance.needProxy(session.RemoteHost, session.RemoteIP) || !TmpConfig.bypass){
107 | // libnghttpx.so 往远端发不走代理
108 | if (session.RemoteHost.equals(TmpConfig.remoteIp)){
109 | return new InetSocketAddress(localChannel.socket().getInetAddress(),session.RemotePort&0xFFFF);
110 | }
111 |
112 | if(ProxyConfig.IS_DEBUG)
113 | System.out.printf("%d/%d:[PROXY] %s=>%s:%d\n",NatSessionManager.getSessionCount(), Tunnel.SessionCount,session.RemoteHost,CommonMethods.ipIntToString(session.RemoteIP),session.RemotePort&0xFFFF);
114 | return InetSocketAddress.createUnresolved(session.RemoteHost, session.RemotePort&0xFFFF);
115 | }else {
116 | // 直接放行
117 | return new InetSocketAddress(localChannel.socket().getInetAddress(),session.RemotePort&0xFFFF);
118 | }
119 | }
120 | return null;
121 | }
122 |
123 | void onAccepted(SelectionKey key){
124 | Tunnel localTunnel =null;
125 | try {
126 | SocketChannel localChannel=m_ServerSocketChannel.accept();
127 | localTunnel=TunnelFactory.wrap(localChannel, m_Selector);
128 |
129 | InetSocketAddress destAddress=getDestAddress(localChannel);
130 | if(destAddress!=null){
131 | Tunnel remoteTunnel=TunnelFactory.createTunnelByConfig(destAddress,m_Selector);
132 | remoteTunnel.setBrotherTunnel(localTunnel);//关联兄弟
133 | localTunnel.setBrotherTunnel(remoteTunnel);//关联兄弟
134 | remoteTunnel.connect(destAddress);//开始连接
135 | }
136 | else {
137 | LocalVpnService.Instance.writeLog("Error: socket(%s:%d) target host is null.",localChannel.socket().getInetAddress().toString(),localChannel.socket().getPort());
138 | localTunnel.dispose();
139 | }
140 | } catch (Exception e) {
141 | e.printStackTrace();
142 | LocalVpnService.Instance.writeLog("Error: remote socket create failed: %s",e.toString());
143 | if(localTunnel!=null){
144 | localTunnel.dispose();
145 | }
146 | }
147 | }
148 |
149 | }
150 |
--------------------------------------------------------------------------------
/Smart Proxy X/src/main/java/me/smartproxy/core/TmpConfig.java:
--------------------------------------------------------------------------------
1 | package me.smartproxy.core;
2 |
3 | import android.content.Context;
4 | import android.content.SharedPreferences;
5 |
6 | import java.io.File;
7 | import java.io.FileOutputStream;
8 | import java.io.IOException;
9 | import java.io.InputStream;
10 | import java.io.OutputStream;
11 |
12 | import static android.app.PendingIntent.getActivity;
13 | import static android.content.Context.MODE_PRIVATE;
14 |
15 | /**
16 | * Created by gho on 17-11-4.
17 | */
18 |
19 | public class TmpConfig {
20 | public static String UserName, Password, remoteIp, remotePort;
21 | public static boolean bypass = true; // 是否绕过国内
22 |
23 | public static final String UserKey = "UserNameKey";
24 | public static final String PasswordKey = "PasswordKey";
25 | public static final String IpKey = "IpKey";
26 | public static final String PortKey = "PortKey";
27 | public static final String ByPassKey = "ByPass";
28 |
29 | public static final String CONFIG_URL_KEY = "CONFIG_URL_KEY";
30 |
31 | public static String nghttpxCmd = "";
32 |
33 | // public static String exePath = "/data/data/me.smartproxy/";
34 |
35 | private static String username = "", pwd = "", ip = "", port = "";
36 |
37 | private static void checkConfig(Context context) {
38 | UserName = username = readConfigKey(UserKey, context);
39 | Password = pwd = readConfigKey(PasswordKey, context);
40 | remoteIp = ip = readConfigKey(IpKey, context);
41 | remotePort = port = readConfigKey(PortKey, context);
42 | if (readConfigKey(ByPassKey, context).equals("true")) {
43 | bypass = true;
44 | } else {
45 | bypass = false;
46 | }
47 | }
48 |
49 | public static String getConfig(Context context) {
50 |
51 | String user_pwd = "";
52 | checkConfig(context);
53 | if (!username.equals("")) {
54 | user_pwd = String.format("%s:%s@", username, pwd);
55 | }
56 | return String.format("http://%s%s:%s", user_pwd, ip, port);
57 |
58 | }
59 |
60 | public static String readConfigKey(String Key, Context context) {
61 | SharedPreferences preferences = context.getSharedPreferences("SmartProxy", MODE_PRIVATE);
62 | return preferences.getString(Key, "");
63 | }
64 |
65 | public static void CopyAndStart(Context context) {
66 | checkConfig(context);
67 | try {
68 | String filePath = context.getFilesDir().getPath() + "libnghttpx.so";
69 | System.out.println(filePath);
70 | File f = new File(filePath);
71 |
72 | if (!f.exists()) {
73 | copyDataToSD(filePath, "libnghttpx.so", context);
74 | }
75 | File exeFile = new File(filePath);
76 | exeFile.setExecutable(true, true);
77 |
78 | nghttpxCmd = context.getApplicationInfo().nativeLibraryDir + "/libnghttpx.so";
79 | } catch (IOException e1) {
80 | e1.printStackTrace();
81 | }
82 | startNghttpx();
83 | // setBypass(); // 绕过国内
84 | }
85 |
86 | public static void startNghttpx() {
87 | Runnable runnable = new Runnable() {
88 | @Override
89 | public void run() {
90 | try {
91 | execCmd();
92 | } catch (IOException e) {
93 | e.printStackTrace();
94 | LocalVpnService.Instance.writeLog(e.toString());
95 | }
96 | }
97 | };
98 | final Thread thread = new Thread(runnable);
99 | thread.start();
100 |
101 | /* Runnable stop = new Runnable() {
102 | @Override
103 | public void run() {
104 | while (true) {
105 | if (!LocalVpnService.IsRunning){
106 | if (thread.isAlive()) {
107 | thread.interrupt();
108 | }
109 | break;
110 | }
111 | try {
112 | Thread.sleep(1000);
113 | } catch (InterruptedException e) {
114 | e.printStackTrace();
115 | }
116 | }
117 | }
118 | };
119 | final Thread stopThread = new Thread(stop);
120 | stopThread.start();*/
121 | }
122 |
123 | private static void execCmd() throws IOException {
124 | Runtime runtime = Runtime.getRuntime();
125 | String backendConfig = String.format("--backend=%s,%s;;tls;proto=h2", ip, port);
126 |
127 | System.out.println(backendConfig);
128 |
129 | Process process = runtime.exec(new String[]{
130 | TmpConfig.nghttpxCmd,
131 | "-k",
132 | "--frontend=0.0.0.0,9000;no-tls",
133 | backendConfig,
134 | "--http2-proxy",
135 | "--workers=4",
136 | "--backend-http2-window-size=524288",
137 | });
138 |
139 | try {
140 | process.waitFor();
141 | InputStream error = process.getErrorStream();
142 | String err_msg = "";
143 |
144 | for (int i = 0; i < 2048; i++) {
145 | err_msg += (char) error.read();
146 | }
147 | System.out.println(err_msg);
148 |
149 | } catch (InterruptedException e) {
150 | e.printStackTrace();
151 | }
152 |
153 | }
154 |
155 | private static void copyDataToSD(String strOutFileName, String assertFileName, Context context) throws IOException {
156 | InputStream myInput;
157 | OutputStream myOutput = new FileOutputStream(strOutFileName);
158 | myInput = context.getAssets().open(assertFileName);
159 | byte[] buffer = new byte[1024];
160 | int length = myInput.read(buffer);
161 | while (length > 0) {
162 | myOutput.write(buffer, 0, length);
163 | length = myInput.read(buffer);
164 | }
165 | myOutput.flush();
166 | myInput.close();
167 | myOutput.close();
168 | }
169 |
170 | }
171 |
--------------------------------------------------------------------------------
/Smart Proxy X/src/main/java/me/smartproxy/core/TunnelFactory.java:
--------------------------------------------------------------------------------
1 | package me.smartproxy.core;
2 |
3 | import java.net.InetSocketAddress;
4 | import java.nio.channels.Selector;
5 | import java.nio.channels.SocketChannel;
6 |
7 | import me.smartproxy.tunnel.Config;
8 | import me.smartproxy.tunnel.RawTunnel;
9 | import me.smartproxy.tunnel.Tunnel;
10 | import me.smartproxy.tunnel.httpconnect.HttpConnectConfig;
11 | import me.smartproxy.tunnel.httpconnect.HttpConnectTunnel;
12 | import me.smartproxy.tunnel.shadowsocks.ShadowsocksConfig;
13 | import me.smartproxy.tunnel.shadowsocks.ShadowsocksTunnel;
14 |
15 | public class TunnelFactory {
16 |
17 | public static Tunnel wrap(SocketChannel channel,Selector selector){
18 | return new RawTunnel(channel, selector);
19 | }
20 |
21 | public static Tunnel createTunnelByConfig(InetSocketAddress destAddress,Selector selector) throws Exception {
22 | if(destAddress.isUnresolved()){
23 | Config config=ProxyConfig.Instance.getDefaultTunnelConfig(destAddress);
24 | if(config instanceof HttpConnectConfig){
25 | return new HttpConnectTunnel((HttpConnectConfig)config,selector);
26 | }else if(config instanceof ShadowsocksConfig){
27 | return new ShadowsocksTunnel((ShadowsocksConfig)config,selector);
28 | }
29 | throw new Exception("The config is unknow.");
30 | }else {
31 | return new RawTunnel(destAddress, selector);
32 | }
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/Smart Proxy X/src/main/java/me/smartproxy/dns/DnsFlags.java:
--------------------------------------------------------------------------------
1 | package me.smartproxy.dns;
2 |
3 | public class DnsFlags {
4 | public boolean QR;//1 bits
5 | public int OpCode;//4 bits
6 | public boolean AA;//1 bits
7 | public boolean TC;//1 bits
8 | public boolean RD;//1 bits
9 | public boolean RA;//1 bits
10 | public int Zero;//3 bits
11 | public int Rcode;//4 bits
12 |
13 | public static DnsFlags Parse(short value){
14 | int m_Flags=value&0xFFFF;
15 | DnsFlags flags=new DnsFlags();
16 | flags.QR=((m_Flags >> 7) & 0x01) == 1;
17 | flags.OpCode=(m_Flags >> 3) & 0x0F;
18 | flags.AA=((m_Flags >> 2) & 0x01) == 1;
19 | flags.TC=((m_Flags >> 1) & 0x01) == 1;
20 | flags.RD= (m_Flags & 0x01) == 1;
21 | flags.RA= (m_Flags >> 15) == 1;
22 | flags.Zero=(m_Flags >> 12) & 0x07;
23 | flags.Rcode=((m_Flags>>8) & 0xF);
24 | return flags;
25 | }
26 |
27 | public short ToShort(){
28 | int m_Flags=0;
29 | m_Flags|= (this.QR ? 1 : 0) << 7;
30 | m_Flags|= (this.OpCode&0x0F) << 3;
31 | m_Flags|= (this.AA ? 1 : 0) << 2;
32 | m_Flags|=(this.TC ? 1 : 0) << 1;
33 | m_Flags|= this.RD ? 1 : 0;
34 | m_Flags|=(this.RA ? 1 : 0) << 15;
35 | m_Flags|=(this.Zero & 0x07) << 12;
36 | m_Flags|= (this.Rcode & 0x0F) << 8;
37 | return (short)m_Flags;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/Smart Proxy X/src/main/java/me/smartproxy/dns/DnsHeader.java:
--------------------------------------------------------------------------------
1 | package me.smartproxy.dns;
2 |
3 | import java.nio.ByteBuffer;
4 |
5 | import me.smartproxy.tcpip.CommonMethods;
6 |
7 |
8 | public class DnsHeader {
9 | public short ID;
10 | public DnsFlags Flags;
11 | public short QuestionCount;
12 | public short ResourceCount;
13 | public short AResourceCount;
14 | public short EResourceCount;
15 |
16 | public static DnsHeader FromBytes(ByteBuffer buffer) {
17 | DnsHeader header=new DnsHeader(buffer.array(),buffer.arrayOffset()+buffer.position());
18 | header.ID=buffer.getShort();
19 | header.Flags=DnsFlags.Parse(buffer.getShort());
20 | header.QuestionCount=buffer.getShort();
21 | header.ResourceCount=buffer.getShort();
22 | header.AResourceCount=buffer.getShort();
23 | header.EResourceCount=buffer.getShort();
24 | return header;
25 | }
26 |
27 | public void ToBytes(ByteBuffer buffer)
28 | {
29 | buffer.putShort(this.ID);
30 | buffer.putShort(this.Flags.ToShort());
31 | buffer.putShort(this.QuestionCount);
32 | buffer.putShort(this.ResourceCount);
33 | buffer.putShort(this.AResourceCount);
34 | buffer.putShort(this.EResourceCount);
35 | }
36 |
37 | static final short offset_ID=0;
38 | static final short offset_Flags=2;
39 | static final short offset_QuestionCount=4;
40 | static final short offset_ResourceCount=6;
41 | static final short offset_AResourceCount=8;
42 | static final short offset_EResourceCount=10;
43 |
44 | public byte[] Data;
45 | public int Offset;
46 |
47 | public DnsHeader(byte[] data,int offset){
48 | this.Offset=offset;
49 | this.Data=data;
50 | }
51 |
52 | public short getID() {
53 | return CommonMethods.readShort(Data, Offset+offset_ID);
54 | }
55 |
56 | public short getFlags() {
57 | return CommonMethods.readShort(Data, Offset+offset_Flags);
58 | }
59 |
60 | public short getQuestionCount() {
61 | return CommonMethods.readShort(Data, Offset+offset_QuestionCount);
62 | }
63 |
64 | public short getResourceCount() {
65 | return CommonMethods.readShort(Data, Offset+offset_ResourceCount);
66 | }
67 |
68 | public short getAResourceCount() {
69 | return CommonMethods.readShort(Data, Offset+offset_AResourceCount);
70 | }
71 |
72 | public short getEResourceCount() {
73 | return CommonMethods.readShort(Data, Offset+offset_EResourceCount);
74 | }
75 |
76 | public void setID(short value) {
77 | CommonMethods.writeShort(Data, Offset+offset_ID,value);
78 | }
79 |
80 | public void setFlags(short value) {
81 | CommonMethods.writeShort(Data, Offset+offset_Flags,value);
82 | }
83 |
84 | public void setQuestionCount(short value) {
85 | CommonMethods.writeShort(Data, Offset+offset_QuestionCount,value);
86 | }
87 |
88 | public void setResourceCount(short value) {
89 | CommonMethods.writeShort(Data, Offset+offset_ResourceCount,value);
90 | }
91 |
92 | public void setAResourceCount(short value) {
93 | CommonMethods.writeShort(Data, Offset+offset_AResourceCount,value);
94 | }
95 |
96 | public void setEResourceCount(short value) {
97 | CommonMethods.writeShort(Data, Offset+offset_EResourceCount,value);
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/Smart Proxy X/src/main/java/me/smartproxy/dns/DnsPacket.java:
--------------------------------------------------------------------------------
1 | package me.smartproxy.dns;
2 |
3 | import java.nio.ByteBuffer;
4 |
5 | public class DnsPacket {
6 | public DnsHeader Header;
7 | public Question[] Questions;
8 | public Resource[] Resources;
9 | public Resource[] AResources;
10 | public Resource[] EResources;
11 |
12 | public int Size;
13 |
14 | public static DnsPacket FromBytes(ByteBuffer buffer)
15 | {
16 | if (buffer.limit() < 12)
17 | return null;
18 | if (buffer.limit() > 512)
19 | return null;
20 |
21 | DnsPacket packet = new DnsPacket();
22 | packet.Size=buffer.limit();
23 | packet.Header = DnsHeader.FromBytes(buffer);
24 |
25 | if (packet.Header.QuestionCount > 2 || packet.Header.ResourceCount > 50 || packet.Header.AResourceCount > 50 || packet.Header.EResourceCount > 50)
26 | {
27 | return null;
28 | }
29 |
30 | packet.Questions = new Question[packet.Header.QuestionCount];
31 | packet.Resources = new Resource[packet.Header.ResourceCount];
32 | packet.AResources = new Resource[packet.Header.AResourceCount];
33 | packet.EResources = new Resource[packet.Header.EResourceCount];
34 |
35 | for (int i = 0; i < packet.Questions.length; i++)
36 | {
37 | packet.Questions[i] = Question.FromBytes(buffer);
38 | }
39 |
40 | for (int i = 0; i < packet.Resources.length; i++)
41 | {
42 | packet.Resources[i] = Resource.FromBytes(buffer);
43 | }
44 |
45 | for (int i = 0; i < packet.AResources.length; i++)
46 | {
47 | packet.AResources[i] = Resource.FromBytes(buffer);
48 | }
49 |
50 | for (int i = 0; i < packet.EResources.length; i++)
51 | {
52 | packet.EResources[i] = Resource.FromBytes(buffer);
53 | }
54 |
55 | return packet;
56 | }
57 |
58 | public void ToBytes(ByteBuffer buffer)
59 | {
60 | Header.QuestionCount = 0;
61 | Header.ResourceCount = 0;
62 | Header.AResourceCount = 0;
63 | Header.EResourceCount = 0;
64 |
65 | if (Questions != null)
66 | Header.QuestionCount = (short) Questions.length;
67 | if (Resources != null)
68 | Header.ResourceCount = (short) Resources.length;
69 | if (AResources != null)
70 | Header.AResourceCount = (short) AResources.length;
71 | if (EResources != null)
72 | Header.EResourceCount = (short) EResources.length;
73 |
74 | this.Header.ToBytes(buffer);
75 |
76 | for (int i = 0; i < Header.QuestionCount; i++)
77 | {
78 | this.Questions[i].ToBytes(buffer);
79 | }
80 |
81 | for (int i = 0; i < Header.ResourceCount; i++)
82 | {
83 | this.Resources[i].ToBytes(buffer);
84 | }
85 |
86 | for (int i = 0; i < Header.AResourceCount; i++)
87 | {
88 | this.AResources[i].ToBytes(buffer);
89 | }
90 |
91 | for (int i = 0; i < Header.EResourceCount; i++)
92 | {
93 | this.EResources[i].ToBytes(buffer);
94 | }
95 | }
96 |
97 | public static String ReadDomain(ByteBuffer buffer,int dnsHeaderOffset)
98 | {
99 | StringBuilder sb = new StringBuilder();
100 | int len = 0;
101 | while (buffer.hasRemaining() && (len = (buffer.get() & 0xFF)) > 0)
102 | {
103 | if ((len & 0xc0) == 0xc0)// pointer 高2位为11表示是指针。如:1100 0000
104 | {
105 | // 指针的取值是前一字节的后6位加后一字节的8位共14位的值。
106 | int pointer = buffer.get() & 0xFF;// 低8位
107 | pointer |= (len & 0x3F) << 8;// 高6位
108 |
109 | ByteBuffer newBuffer = ByteBuffer.wrap(buffer.array(), dnsHeaderOffset + pointer, dnsHeaderOffset+buffer.limit());
110 | sb.append(ReadDomain(newBuffer,dnsHeaderOffset));
111 | return sb.toString();
112 | }
113 | else
114 | {
115 | while (len > 0 && buffer.hasRemaining())
116 | {
117 | sb.append((char) (buffer.get() & 0xFF));
118 | len--;
119 | }
120 | sb.append('.');
121 | }
122 | }
123 |
124 | if(len==0&&sb.length()>0){
125 | sb.deleteCharAt(sb.length()-1);//去掉末尾的点(.)
126 | }
127 | return sb.toString();
128 | }
129 |
130 | public static void WriteDomain(String domain, ByteBuffer buffer)
131 | {
132 | if (domain == null || domain == "")
133 | {
134 | buffer.put((byte) 0);
135 | return;
136 | }
137 |
138 | String[] arr = domain.split("\\.");
139 | for (String item : arr)
140 | {
141 | if (arr.length > 1)
142 | {
143 | buffer.put((byte) item.length());
144 | }
145 |
146 | for (int i = 0; i < item.length(); i++)
147 | {
148 | buffer.put((byte) item.codePointAt(i));
149 | }
150 | }
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/Smart Proxy X/src/main/java/me/smartproxy/dns/Question.java:
--------------------------------------------------------------------------------
1 | package me.smartproxy.dns;
2 |
3 | import java.nio.ByteBuffer;
4 |
5 | public class Question {
6 | public String Domain;
7 | public short Type;
8 | public short Class;
9 |
10 | private int offset;
11 | public int Offset() {
12 | return offset;
13 | }
14 |
15 | private int length;
16 | public int Length() {
17 | return length;
18 | }
19 |
20 | public static Question FromBytes(ByteBuffer buffer){
21 | Question q=new Question();
22 | q.offset=buffer.arrayOffset()+buffer.position();
23 | q.Domain = DnsPacket.ReadDomain(buffer,buffer.arrayOffset());
24 | q.Type=buffer.getShort();
25 | q.Class=buffer.getShort();
26 | q.length=buffer.arrayOffset()+buffer.position()-q.offset;
27 | return q;
28 | }
29 |
30 | public void ToBytes(ByteBuffer buffer)
31 | {
32 | this.offset=buffer.position();
33 | DnsPacket.WriteDomain(this.Domain, buffer);
34 | buffer.putShort(this.Type);
35 | buffer.putShort(this.Class);
36 | this.length=buffer.position()-this.offset;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Smart Proxy X/src/main/java/me/smartproxy/dns/Resource.java:
--------------------------------------------------------------------------------
1 | package me.smartproxy.dns;
2 |
3 | import java.nio.ByteBuffer;
4 |
5 | public class Resource {
6 | public String Domain;
7 | public short Type;
8 | public short Class;
9 | public int TTL;
10 | public short DataLength;
11 | public byte[] Data;
12 |
13 | private int offset;
14 | public int Offset() {
15 | return offset;
16 | }
17 |
18 | private int length;
19 | public int Length() {
20 | return length;
21 | }
22 |
23 | public static Resource FromBytes(ByteBuffer buffer){
24 |
25 | Resource r = new Resource();
26 | r.offset=buffer.arrayOffset()+ buffer.position();
27 | r.Domain = DnsPacket.ReadDomain(buffer,buffer.arrayOffset());
28 | r.Type=buffer.getShort();
29 | r.Class=buffer.getShort();
30 | r.TTL=buffer.getInt();
31 | r.DataLength=buffer.getShort();
32 | r.Data = new byte[r.DataLength&0xFFFF];
33 | buffer.get(r.Data);
34 | r.length=buffer.arrayOffset()+ buffer.position()-r.offset;
35 | return r;
36 | }
37 |
38 | public void ToBytes(ByteBuffer buffer)
39 | {
40 | if(this.Data==null){
41 | this.Data=new byte[0];
42 | }
43 | this.DataLength=(short)this.Data.length;
44 |
45 | this.offset=buffer.position();
46 | DnsPacket.WriteDomain(this.Domain, buffer);
47 | buffer.putShort(this.Type);
48 | buffer.putShort(this.Class);
49 | buffer.putInt(this.TTL);
50 |
51 | buffer.putShort(this.DataLength);
52 | buffer.put(this.Data);
53 | this.length=buffer.position()-this.offset;
54 | }
55 |
56 |
57 | }
58 |
--------------------------------------------------------------------------------
/Smart Proxy X/src/main/java/me/smartproxy/dns/ResourcePointer.java:
--------------------------------------------------------------------------------
1 | package me.smartproxy.dns;
2 |
3 | import me.smartproxy.tcpip.CommonMethods;
4 |
5 | public class ResourcePointer {
6 | static final short offset_Domain = 0;
7 | static final short offset_Type = 2;
8 | static final short offset_Class = 4;
9 | static final int offset_TTL = 6;
10 | static final short offset_DataLength = 10;
11 | static final int offset_IP = 12;
12 |
13 | byte[] Data;
14 | int Offset;
15 |
16 | public ResourcePointer(byte[] data, int offset) {
17 | this.Data = data;
18 | this.Offset = offset;
19 | }
20 |
21 | public void setDomain(short value) {
22 | CommonMethods.writeShort(Data, Offset + offset_Domain, value);
23 | }
24 |
25 | public short getType() {
26 | return CommonMethods.readShort(Data, Offset + offset_Type);
27 | }
28 |
29 | public void setType(short value) {
30 | CommonMethods.writeShort(Data, Offset + offset_Type, value);
31 | }
32 |
33 | public short getClass(short value) {
34 | return CommonMethods.readShort(Data, Offset + offset_Class);
35 | }
36 |
37 | public void setClass(short value) {
38 | CommonMethods.writeShort(Data, Offset + offset_Class, value);
39 | }
40 |
41 | public int getTTL() {
42 | return CommonMethods.readInt(Data, Offset + offset_TTL);
43 | }
44 |
45 | public void setTTL(int value) {
46 | CommonMethods.writeInt(Data, Offset + offset_TTL, value);
47 | }
48 |
49 | public short getDataLength() {
50 | return CommonMethods.readShort(Data, Offset + offset_DataLength);
51 | }
52 |
53 | public void setDataLength(short value) {
54 | CommonMethods.writeShort(Data, Offset + offset_DataLength, value);
55 | }
56 |
57 | public int getIP() {
58 | return CommonMethods.readInt(Data, Offset + offset_IP);
59 | }
60 |
61 | public void setIP(int value) {
62 | CommonMethods.writeInt(Data, Offset + offset_IP, value);
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/Smart Proxy X/src/main/java/me/smartproxy/tcpip/CommonMethods.java:
--------------------------------------------------------------------------------
1 | package me.smartproxy.tcpip;
2 |
3 | import java.net.Inet4Address;
4 | import java.net.InetAddress;
5 | import java.net.UnknownHostException;
6 |
7 | public class CommonMethods {
8 |
9 | public static InetAddress ipIntToInet4Address(int ip){
10 | byte[] ipAddress=new byte[4];
11 | writeInt(ipAddress, 0, ip);
12 | try {
13 | return Inet4Address.getByAddress(ipAddress);
14 | } catch (UnknownHostException e) {
15 | // TODO Auto-generated catch block
16 | e.printStackTrace();
17 | return null;
18 | }
19 | }
20 |
21 | public static String ipIntToString(int ip) {
22 | return String.format("%s.%s.%s.%s", (ip >> 24) & 0x00FF,
23 | (ip >> 16) & 0x00FF, (ip >> 8) & 0x00FF, ip & 0x00FF);
24 | }
25 |
26 | public static String ipBytesToString(byte[] ip) {
27 | return String.format("%s.%s.%s.%s", ip[0] & 0x00FF,ip[1] & 0x00FF,ip[2] & 0x00FF,ip[3] & 0x00FF);
28 | }
29 |
30 | public static int ipStringToInt(String ip) {
31 | String[] arrStrings = ip.split("\\.");
32 | int r = (Integer.parseInt(arrStrings[0]) << 24)
33 | | (Integer.parseInt(arrStrings[1]) << 16)
34 | | (Integer.parseInt(arrStrings[2]) << 8)
35 | | Integer.parseInt(arrStrings[3]);
36 | return r;
37 | }
38 |
39 | public static int readInt(byte[] data, int offset) {
40 | int r = ((data[offset] & 0xFF) << 24)
41 | | ((data[offset + 1] & 0xFF) << 16)
42 | | ((data[offset + 2] & 0xFF) << 8) | (data[offset + 3] & 0xFF);
43 | return r;
44 | }
45 |
46 | public static short readShort(byte[] data, int offset) {
47 | int r = ((data[offset] & 0xFF) << 8) | (data[offset + 1] & 0xFF);
48 | return (short) r;
49 | }
50 |
51 | public static void writeInt(byte[] data, int offset, int value) {
52 | data[offset] = (byte) (value >> 24);
53 | data[offset + 1] = (byte) (value >> 16);
54 | data[offset + 2] = (byte) (value >> 8);
55 | data[offset + 3] = (byte) (value);
56 | }
57 |
58 | public static void writeShort(byte[] data, int offset, short value) {
59 | data[offset] = (byte) (value >> 8);
60 | data[offset + 1] = (byte) (value);
61 | }
62 |
63 | // 网络字节顺序与主机字节顺序的转换
64 |
65 | public static short htons(short u) {
66 | int r = ((u & 0xFFFF) << 8) | ((u & 0xFFFF) >> 8);
67 | return (short) r;
68 | }
69 |
70 | public static short ntohs(short u) {
71 | int r = ((u & 0xFFFF) << 8) | ((u & 0xFFFF) >> 8);
72 | return (short) r;
73 | }
74 |
75 | public static int hton(int u) {
76 | int r = (u >> 24) & 0x000000FF;
77 | r |= (u >> 8) & 0x0000FF00;
78 | r |= (u << 8) & 0x00FF0000;
79 | r |= (u << 24) & 0xFF000000;
80 | return r;
81 | }
82 |
83 | public static int ntoh(int u) {
84 | int r = (u >> 24) & 0x000000FF;
85 | r |= (u >> 8) & 0x0000FF00;
86 | r |= (u << 8) & 0x00FF0000;
87 | r |= (u << 24) & 0xFF000000;
88 | return r;
89 | }
90 |
91 | // 计算校验和
92 | public static short checksum(long sum, byte[] buf, int offset, int len) {
93 | sum += getsum(buf, offset, len);
94 | while ((sum >> 16) > 0)
95 | sum = (sum & 0xFFFF) + (sum >> 16);
96 | return (short) ~sum;
97 | }
98 |
99 | public static long getsum(byte[] buf, int offset, int len) {
100 | long sum = 0; /* assume 32 bit long, 16 bit short */
101 | while (len > 1) {
102 | sum += readShort(buf, offset) & 0xFFFF;
103 | offset += 2;
104 | len -= 2;
105 | }
106 |
107 | if (len > 0) /* take care of left over byte */
108 | {
109 | sum += (buf[offset] & 0xFF) << 8;
110 | }
111 | return sum;
112 | }
113 |
114 | // 计算IP包的校验和
115 | public static boolean ComputeIPChecksum(IPHeader ipHeader) {
116 | short oldCrc = ipHeader.getCrc();
117 | ipHeader.setCrc((short) 0);// 计算前置零
118 | short newCrc = CommonMethods.checksum(0, ipHeader.m_Data,
119 | ipHeader.m_Offset, ipHeader.getHeaderLength());
120 | ipHeader.setCrc(newCrc);
121 | return oldCrc == newCrc;
122 | }
123 |
124 | // 计算TCP或UDP的校验和
125 | public static boolean ComputeTCPChecksum(IPHeader ipHeader,TCPHeader tcpHeader) {
126 | ComputeIPChecksum(ipHeader);//计算IP校验和
127 | int ipData_len = ipHeader.getTotalLength() - ipHeader.getHeaderLength();// IP数据长度
128 | if (ipData_len < 0)
129 | return false;
130 | // 计算为伪首部和
131 | long sum = getsum(ipHeader.m_Data, ipHeader.m_Offset
132 | + IPHeader.offset_src_ip, 8);
133 | sum += ipHeader.getProtocol()&0xFF;
134 | sum += ipData_len;
135 |
136 | short oldCrc = tcpHeader.getCrc();
137 | tcpHeader.setCrc((short) 0);// 计算前置0
138 |
139 | short newCrc = checksum(sum, tcpHeader.m_Data, tcpHeader.m_Offset, ipData_len);// 计算校验和
140 |
141 | tcpHeader.setCrc(newCrc);
142 | return oldCrc == newCrc;
143 | }
144 |
145 | // 计算TCP或UDP的校验和
146 | public static boolean ComputeUDPChecksum(IPHeader ipHeader,UDPHeader udpHeader) {
147 | ComputeIPChecksum(ipHeader);//计算IP校验和
148 | int ipData_len = ipHeader.getTotalLength() - ipHeader.getHeaderLength();// IP数据长度
149 | if (ipData_len < 0)
150 | return false;
151 | // 计算为伪首部和
152 | long sum = getsum(ipHeader.m_Data, ipHeader.m_Offset
153 | + IPHeader.offset_src_ip, 8);
154 | sum += ipHeader.getProtocol()&0xFF;
155 | sum += ipData_len;
156 |
157 | short oldCrc = udpHeader.getCrc();
158 | udpHeader.setCrc((short) 0);// 计算前置0
159 |
160 | short newCrc = checksum(sum, udpHeader.m_Data, udpHeader.m_Offset, ipData_len);// 计算校验和
161 |
162 | udpHeader.setCrc(newCrc);
163 | return oldCrc == newCrc;
164 | }
165 | }
166 |
--------------------------------------------------------------------------------
/Smart Proxy X/src/main/java/me/smartproxy/tcpip/IPHeader.java:
--------------------------------------------------------------------------------
1 | package me.smartproxy.tcpip;
2 |
3 | public class IPHeader {
4 |
5 | public static final short IP = 0x0800;
6 | public static final byte ICMP = 1;
7 | public static final byte TCP = 6;
8 | public static final byte UDP = 17;
9 |
10 | static final byte offset_ver_ihl = 0; // 0: Version (4 bits) + Internet header length (4// bits)
11 | static final byte offset_tos = 1; // 1: Type of service
12 | static final short offset_tlen = 2; // 2: Total length
13 | static final short offset_identification = 4; // :4 Identification
14 | static final short offset_flags_fo = 6; // 6: Flags (3 bits) + Fragment offset (13 bits)
15 | static final byte offset_ttl = 8; // 8: Time to live
16 | public static final byte offset_proto = 9; // 9: Protocol
17 | static final short offset_crc = 10; // 10: Header checksum
18 | public static final int offset_src_ip = 12; // 12: Source address
19 | public static final int offset_dest_ip = 16; // 16: Destination address
20 | static final int offset_op_pad = 20; // 20: Option + Padding
21 |
22 | public byte[] m_Data;
23 | public int m_Offset;
24 |
25 | public IPHeader(byte[] data, int offset) {
26 | this.m_Data = data;
27 | this.m_Offset = offset;
28 | }
29 |
30 | public void Default() {
31 | setHeaderLength(20);
32 | setTos((byte)0);
33 | setTotalLength(0);
34 | setIdentification(0);
35 | setFlagsAndOffset((short)0);
36 | setTTL((byte)64);
37 | }
38 |
39 | public int getDataLength() {
40 | return this.getTotalLength()-this.getHeaderLength();
41 | }
42 |
43 | public int getHeaderLength() {
44 | return (m_Data[m_Offset + offset_ver_ihl] & 0x0F) * 4;
45 | }
46 |
47 | public void setHeaderLength(int value) {
48 | m_Data[m_Offset+offset_ver_ihl]= (byte)((4<<4)|(value/4));
49 | }
50 |
51 | public byte getTos() {
52 | return m_Data[m_Offset + offset_tos];
53 | }
54 |
55 | public void setTos(byte value) {
56 | m_Data[m_Offset + offset_tos] = value;
57 | }
58 |
59 | public int getTotalLength() {
60 | return CommonMethods.readShort(m_Data, m_Offset + offset_tlen) & 0xFFFF;
61 | }
62 |
63 | public void setTotalLength(int value) {
64 | CommonMethods.writeShort(m_Data, m_Offset + offset_tlen, (short) value);
65 | }
66 |
67 | public int getIdentification() {
68 | return CommonMethods.readShort(m_Data, m_Offset + offset_identification) & 0xFFFF;
69 | }
70 |
71 | public void setIdentification(int value) {
72 | CommonMethods.writeShort(m_Data, m_Offset + offset_identification, (short) value);
73 | }
74 |
75 | public short getFlagsAndOffset() {
76 | return CommonMethods.readShort(m_Data, m_Offset + offset_flags_fo);
77 | }
78 |
79 | public void setFlagsAndOffset(short value) {
80 | CommonMethods.writeShort(m_Data, m_Offset + offset_flags_fo, value);
81 | }
82 |
83 | public byte getTTL() {
84 | return m_Data[m_Offset + offset_ttl];
85 | }
86 |
87 | public void setTTL(byte value) {
88 | m_Data[m_Offset + offset_ttl] = value;
89 | }
90 |
91 | public byte getProtocol() {
92 | return m_Data[m_Offset + offset_proto];
93 | }
94 |
95 | public void setProtocol(byte value) {
96 | m_Data[m_Offset + offset_proto] = value;
97 | }
98 |
99 | public short getCrc() {
100 | return CommonMethods.readShort(m_Data, m_Offset + offset_crc);
101 | }
102 |
103 | public void setCrc(short value) {
104 | CommonMethods.writeShort(m_Data, m_Offset + offset_crc, value);
105 | }
106 |
107 | public int getSourceIP() {
108 | return CommonMethods.readInt(m_Data, m_Offset + offset_src_ip);
109 | }
110 |
111 | public void setSourceIP(int value) {
112 | CommonMethods.writeInt(m_Data, m_Offset + offset_src_ip, value);
113 | }
114 |
115 | public int getDestinationIP() {
116 | return CommonMethods.readInt(m_Data, m_Offset + offset_dest_ip);
117 | }
118 |
119 | public void setDestinationIP(int value) {
120 | CommonMethods.writeInt(m_Data, m_Offset + offset_dest_ip, value);
121 | }
122 |
123 | @Override
124 | public String toString() {
125 | return String.format("%s->%s Pro=%s,HLen=%d", CommonMethods.ipIntToString(getSourceIP()),CommonMethods.ipIntToString(getDestinationIP()),getProtocol(),getHeaderLength());
126 | }
127 |
128 | }
129 |
--------------------------------------------------------------------------------
/Smart Proxy X/src/main/java/me/smartproxy/tcpip/TCPHeader.java:
--------------------------------------------------------------------------------
1 | package me.smartproxy.tcpip;
2 |
3 |
4 | public class TCPHeader {
5 |
6 | public static final int FIN = 1;
7 | public static final int SYN = 2;
8 | public static final int RST = 4;
9 | public static final int PSH = 8;
10 | public static final int ACK = 16;
11 | public static final int URG = 32;
12 |
13 | static final short offset_src_port = 0; // 16位源端口
14 | static final short offset_dest_port = 2; // 16位目的端口
15 | static final int offset_seq = 4; // 32位序列号
16 | static final int offset_ack = 8; // 32位确认号
17 | static final byte offset_lenres = 12; // 4位首部长度/4位保留字
18 | static final byte offset_flag = 13; // 6位标志位
19 | static final short offset_win = 14; // 16位窗口大小
20 | static final short offset_crc = 16; // 16位校验和
21 | static final short offset_urp = 18; // 16位紧急数据偏移量
22 |
23 | public byte[] m_Data;
24 | public int m_Offset;
25 |
26 | public TCPHeader(byte[] data, int offset) {
27 | this.m_Data = data;
28 | this.m_Offset = offset;
29 | }
30 |
31 | public int getHeaderLength(){
32 | int lenres=m_Data[m_Offset+offset_lenres]&0xFF;
33 | return (lenres >> 4) * 4;
34 | }
35 |
36 | public short getSourcePort() {
37 | return CommonMethods.readShort(m_Data, m_Offset + offset_src_port);
38 | }
39 |
40 | public void setSourcePort(short value) {
41 | CommonMethods.writeShort(m_Data, m_Offset + offset_src_port, value);
42 | }
43 |
44 | public short getDestinationPort() {
45 | return CommonMethods.readShort(m_Data, m_Offset + offset_dest_port);
46 | }
47 |
48 | public void setDestinationPort(short value) {
49 | CommonMethods.writeShort(m_Data, m_Offset + offset_dest_port, value);
50 | }
51 |
52 | public byte getFlags() {
53 | return m_Data[m_Offset + offset_flag];
54 | }
55 |
56 | public short getCrc() {
57 | return CommonMethods.readShort(m_Data, m_Offset + offset_crc);
58 | }
59 |
60 | public void setCrc(short value) {
61 | CommonMethods.writeShort(m_Data, m_Offset + offset_crc, value);
62 | }
63 |
64 | public int getSeqID() {
65 | return CommonMethods.readInt(m_Data, m_Offset + offset_seq);
66 | }
67 |
68 | public int getAckID() {
69 | return CommonMethods.readInt(m_Data, m_Offset + offset_ack);
70 | }
71 |
72 | @Override
73 | public String toString() {
74 | // TODO Auto-generated method stub
75 | return String.format("%s%s%s%s%s%s%d->%d %s:%s",
76 | (getFlags()&SYN)==SYN?"SYN ":"",
77 | (getFlags()&ACK)==ACK?"ACK ":"",
78 | (getFlags()&PSH)==PSH?"PSH ":"",
79 | (getFlags()&RST)==RST?"RST ":"",
80 | (getFlags()&FIN)==FIN?"FIN ":"",
81 | (getFlags()&URG)==URG?"URG ":"",
82 | getSourcePort()&0xFFFF,
83 | getDestinationPort()&0xFFFF,
84 | getSeqID(),
85 | getAckID());
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/Smart Proxy X/src/main/java/me/smartproxy/tcpip/UDPHeader.java:
--------------------------------------------------------------------------------
1 | package me.smartproxy.tcpip;
2 |
3 | public class UDPHeader {
4 | static final short offset_src_port = 0; // Source port
5 | static final short offset_dest_port = 2; // Destination port
6 | static final short offset_tlen = 4; // Datagram length
7 | static final short offset_crc = 6; // Checksum
8 |
9 | public byte[] m_Data;
10 | public int m_Offset;
11 |
12 | public UDPHeader(byte[] data, int offset) {
13 | this.m_Data = data;
14 | this.m_Offset = offset;
15 | }
16 |
17 | public short getSourcePort() {
18 | return CommonMethods.readShort(m_Data, m_Offset + offset_src_port);
19 | }
20 |
21 | public void setSourcePort(short value) {
22 | CommonMethods.writeShort(m_Data, m_Offset + offset_src_port, value);
23 | }
24 |
25 | public short getDestinationPort() {
26 | return CommonMethods.readShort(m_Data, m_Offset + offset_dest_port);
27 | }
28 |
29 | public void setDestinationPort(short value) {
30 | CommonMethods.writeShort(m_Data, m_Offset + offset_dest_port, value);
31 | }
32 |
33 | public int getTotalLength() {
34 | return CommonMethods.readShort(m_Data, m_Offset + offset_tlen) & 0xFFFF;
35 | }
36 |
37 | public void setTotalLength(int value) {
38 | CommonMethods.writeShort(m_Data, m_Offset + offset_tlen, (short) value);
39 | }
40 |
41 | public short getCrc() {
42 | return CommonMethods.readShort(m_Data, m_Offset + offset_crc);
43 | }
44 |
45 | public void setCrc(short value) {
46 | CommonMethods.writeShort(m_Data, m_Offset + offset_crc, value);
47 | }
48 |
49 | @Override
50 | public String toString() {
51 | // TODO Auto-generated method stub
52 | return String.format("%d->%d", getSourcePort() & 0xFFFF,
53 | getDestinationPort() & 0xFFFF);
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/Smart Proxy X/src/main/java/me/smartproxy/tunnel/Config.java:
--------------------------------------------------------------------------------
1 | package me.smartproxy.tunnel;
2 |
3 | import java.net.InetSocketAddress;
4 | import java.nio.channels.Selector;
5 |
6 | public abstract class Config {
7 | public InetSocketAddress ServerAddress;
8 | public IEncryptor Encryptor;
9 | }
10 |
--------------------------------------------------------------------------------
/Smart Proxy X/src/main/java/me/smartproxy/tunnel/IEncryptor.java:
--------------------------------------------------------------------------------
1 | package me.smartproxy.tunnel;
2 |
3 | import java.nio.ByteBuffer;
4 |
5 | public interface IEncryptor {
6 |
7 | void encrypt(ByteBuffer buffer);
8 | void decrypt(ByteBuffer buffer);
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/Smart Proxy X/src/main/java/me/smartproxy/tunnel/RawTunnel.java:
--------------------------------------------------------------------------------
1 | package me.smartproxy.tunnel;
2 |
3 | import java.net.InetSocketAddress;
4 | import java.nio.ByteBuffer;
5 | import java.nio.channels.Selector;
6 | import java.nio.channels.SocketChannel;
7 |
8 | public class RawTunnel extends Tunnel {
9 |
10 | public RawTunnel(InetSocketAddress serverAddress,Selector selector) throws Exception{
11 | super(serverAddress,selector);
12 | }
13 |
14 | public RawTunnel(SocketChannel innerChannel, Selector selector) {
15 | super(innerChannel, selector);
16 | // TODO Auto-generated constructor stub
17 | }
18 |
19 | @Override
20 | protected void onConnected(ByteBuffer buffer) throws Exception {
21 | onTunnelEstablished();
22 | }
23 |
24 | @Override
25 | protected void beforeSend(ByteBuffer buffer) throws Exception {
26 | // TODO Auto-generated method stub
27 |
28 | }
29 |
30 | @Override
31 | protected void afterReceived(ByteBuffer buffer) throws Exception {
32 | // TODO Auto-generated method stub
33 |
34 | }
35 |
36 | @Override
37 | protected boolean isTunnelEstablished() {
38 | return true;
39 | }
40 |
41 | @Override
42 | protected void onDispose() {
43 | // TODO Auto-generated method stub
44 |
45 | }
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/Smart Proxy X/src/main/java/me/smartproxy/tunnel/Tunnel.java:
--------------------------------------------------------------------------------
1 | package me.smartproxy.tunnel;
2 |
3 | import android.annotation.SuppressLint;
4 |
5 | import java.io.IOException;
6 | import java.net.InetSocketAddress;
7 | import java.nio.ByteBuffer;
8 | import java.nio.channels.SelectionKey;
9 | import java.nio.channels.Selector;
10 | import java.nio.channels.SocketChannel;
11 |
12 | import me.smartproxy.core.LocalVpnService;
13 | import me.smartproxy.core.ProxyConfig;
14 |
15 | public abstract class Tunnel {
16 |
17 | final static ByteBuffer GL_BUFFER=ByteBuffer.allocate(1024*256);
18 | public static long SessionCount;
19 |
20 | protected abstract void onConnected(ByteBuffer buffer) throws Exception;
21 | protected abstract boolean isTunnelEstablished();
22 | protected abstract void beforeSend(ByteBuffer buffer) throws Exception;
23 | protected abstract void afterReceived(ByteBuffer buffer) throws Exception;
24 | protected abstract void onDispose();
25 |
26 | private SocketChannel m_InnerChannel;
27 | private ByteBuffer m_SendRemainBuffer;
28 | private Selector m_Selector;
29 | private Tunnel m_BrotherTunnel;
30 | private boolean m_Disposed;
31 | private InetSocketAddress m_ServerEP;
32 | protected InetSocketAddress m_DestAddress;
33 |
34 | public Tunnel(SocketChannel innerChannel,Selector selector){
35 | this.m_InnerChannel=innerChannel;
36 | this.m_Selector=selector;
37 | SessionCount++;
38 | }
39 |
40 | public Tunnel(InetSocketAddress serverAddress,Selector selector) throws IOException{
41 | SocketChannel innerChannel=SocketChannel.open();
42 | innerChannel.configureBlocking(false);
43 | this.m_InnerChannel=innerChannel;
44 | this.m_Selector=selector;
45 | this.m_ServerEP=serverAddress;
46 | SessionCount++;
47 | }
48 |
49 | public void setBrotherTunnel(Tunnel brotherTunnel){
50 | m_BrotherTunnel=brotherTunnel;
51 | }
52 |
53 | public void connect(InetSocketAddress destAddress) throws Exception{
54 | if(LocalVpnService.Instance.protect(m_InnerChannel.socket())){//保护socket不走vpn
55 | m_DestAddress=destAddress;
56 | m_InnerChannel.register(m_Selector, SelectionKey.OP_CONNECT,this);//注册连接事件
57 | m_InnerChannel.connect(m_ServerEP);//连接目标
58 | }else {
59 | throw new Exception("VPN protect socket failed.");
60 | }
61 | }
62 |
63 | protected void beginReceive() throws Exception{
64 | if(m_InnerChannel.isBlocking()){
65 | m_InnerChannel.configureBlocking(false);
66 | }
67 | m_InnerChannel.register(m_Selector, SelectionKey.OP_READ,this);//注册读事件
68 | }
69 |
70 |
71 | protected boolean write(ByteBuffer buffer,boolean copyRemainData) throws Exception {
72 | int bytesSent;
73 | while (buffer.hasRemaining()) {
74 | bytesSent=m_InnerChannel.write(buffer);
75 | if(bytesSent==0){
76 | break;//不能再发送了,终止循环
77 | }
78 | }
79 |
80 | if(buffer.hasRemaining()){//数据没有发送完毕
81 | if(copyRemainData){//拷贝剩余数据,然后侦听写入事件,待可写入时写入。
82 | //拷贝剩余数据
83 | if(m_SendRemainBuffer==null){
84 | m_SendRemainBuffer=ByteBuffer.allocate(buffer.capacity());
85 | }
86 | m_SendRemainBuffer.clear();
87 | m_SendRemainBuffer.put(buffer);
88 | m_SendRemainBuffer.flip();
89 | m_InnerChannel.register(m_Selector,SelectionKey.OP_WRITE, this);//注册写事件
90 | }
91 | return false;
92 | }
93 | else {//发送完毕了
94 | return true;
95 | }
96 | }
97 |
98 | protected void onTunnelEstablished() throws Exception{
99 | this.beginReceive();//开始接收数据
100 | m_BrotherTunnel.beginReceive();//兄弟也开始收数据吧
101 | }
102 |
103 | @SuppressLint("DefaultLocale")
104 | public void onConnectable(){
105 | try {
106 | if(m_InnerChannel.finishConnect()){//连接成功
107 | onConnected(GL_BUFFER);//通知子类TCP已连接,子类可以根据协议实现握手等。
108 | }else {//连接失败
109 | LocalVpnService.Instance.writeLog("Error: connect to %s failed.",m_ServerEP);
110 | this.dispose();
111 | }
112 | } catch (Exception e) {
113 | LocalVpnService.Instance.writeLog("Error: connect to %s failed: %s", m_ServerEP,e);
114 | this.dispose();
115 | }
116 | }
117 |
118 | public void onReadable(SelectionKey key){
119 | try {
120 | ByteBuffer buffer=GL_BUFFER;
121 | buffer.clear();
122 | int bytesRead=m_InnerChannel.read(buffer);
123 | if(bytesRead>0){
124 | buffer.flip();
125 | afterReceived(buffer);//先让子类处理,例如解密数据。
126 | if(isTunnelEstablished()&&buffer.hasRemaining()){//将读到的数据,转发给兄弟。
127 | m_BrotherTunnel.beforeSend(buffer);//发送之前,先让子类处理,例如做加密等。
128 | if(!m_BrotherTunnel.write(buffer,true)){
129 | key.cancel();//兄弟吃不消,就取消读取事件。
130 | if(ProxyConfig.IS_DEBUG)
131 | System.out.printf("%s can not read more.\n", m_ServerEP);
132 | }
133 | }
134 | }else if(bytesRead<0) {
135 | this.dispose();//连接已关闭,释放资源。
136 | }
137 | } catch (Exception e) {
138 | e.printStackTrace();
139 | this.dispose();
140 | }
141 | }
142 |
143 | public void onWritable(SelectionKey key){
144 | try {
145 | this.beforeSend(m_SendRemainBuffer);//发送之前,先让子类处理,例如做加密等。
146 | if(this.write(m_SendRemainBuffer, false)) {//如果剩余数据已经发送完毕
147 | key.cancel();//取消写事件。
148 | if(isTunnelEstablished()){
149 | m_BrotherTunnel.beginReceive();//这边数据发送完毕,通知兄弟可以收数据了。
150 | }else {
151 | this.beginReceive();//开始接收代理服务器响应数据
152 | }
153 | }
154 | } catch (Exception e) {
155 | this.dispose();
156 | }
157 | }
158 |
159 | public void dispose(){
160 | disposeInternal(true);
161 | }
162 |
163 | void disposeInternal(boolean disposeBrother) {
164 | if(m_Disposed){
165 | return;
166 | }
167 | else {
168 | try {
169 | m_InnerChannel.close();
170 | } catch (Exception e) {
171 | }
172 |
173 | if(m_BrotherTunnel!=null&&disposeBrother){
174 | m_BrotherTunnel.disposeInternal(false);//把兄弟的资源也释放了。
175 | }
176 |
177 | m_InnerChannel=null;
178 | m_SendRemainBuffer=null;
179 | m_Selector=null;
180 | m_BrotherTunnel=null;
181 | m_Disposed=true;
182 | SessionCount--;
183 |
184 | onDispose();
185 | }
186 | }
187 | }
188 |
--------------------------------------------------------------------------------
/Smart Proxy X/src/main/java/me/smartproxy/tunnel/httpconnect/HttpConnectConfig.java:
--------------------------------------------------------------------------------
1 | package me.smartproxy.tunnel.httpconnect;
2 |
3 | import java.net.InetSocketAddress;
4 |
5 | import android.net.Uri;
6 | import me.smartproxy.tunnel.Config;
7 |
8 | public class HttpConnectConfig extends Config {
9 | public String UserName;
10 | public String Password;
11 |
12 | public static HttpConnectConfig parse(String proxyInfo){
13 | HttpConnectConfig config=new HttpConnectConfig();
14 | Uri uri=Uri.parse(proxyInfo);
15 | String userInfoString=uri.getUserInfo();
16 | if(userInfoString!=null){
17 | String[] userStrings=userInfoString.split(":");
18 | config.UserName=userStrings[0];
19 | if(userStrings.length>=2){
20 | config.Password=userStrings[1];
21 | }
22 | }
23 | config.ServerAddress=new InetSocketAddress(uri.getHost(), uri.getPort());
24 | return config;
25 | }
26 |
27 | @Override
28 | public boolean equals(Object o) {
29 | if(o==null)
30 | return false;
31 | return this.toString().equals(o.toString());
32 | }
33 |
34 | @Override
35 | public String toString() {
36 | return String.format("http://%s:%s@%s", UserName,Password,ServerAddress);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Smart Proxy X/src/main/java/me/smartproxy/tunnel/httpconnect/HttpConnectTunnel.java:
--------------------------------------------------------------------------------
1 | package me.smartproxy.tunnel.httpconnect;
2 |
3 | import android.util.Base64;
4 |
5 | import java.io.IOException;
6 | import java.nio.ByteBuffer;
7 | import java.nio.channels.Selector;
8 |
9 | import me.smartproxy.core.ProxyConfig;
10 | import me.smartproxy.core.TmpConfig;
11 | import me.smartproxy.tunnel.Tunnel;
12 |
13 | public class HttpConnectTunnel extends Tunnel {
14 |
15 | private boolean m_TunnelEstablished;
16 | private HttpConnectConfig m_Config;
17 |
18 | public HttpConnectTunnel(HttpConnectConfig config,Selector selector) throws IOException {
19 | super(config.ServerAddress,selector);
20 | m_Config=config;
21 | }
22 |
23 | @Override
24 | protected void onConnected(ByteBuffer buffer) throws Exception {
25 | String auth = String.format("%s:%s", TmpConfig.UserName, TmpConfig.Password);
26 | if (auth.equals("")){
27 | auth = "";
28 | } else {
29 | auth = Base64.encodeToString(auth.getBytes("UTF-8"), Base64.NO_PADDING);
30 | auth = auth.replace("\n", "");
31 | auth = String.format("\r\nProxy-Authorization: Basic %s", auth);
32 |
33 | }
34 | System.out.print(m_DestAddress.getHostName());
35 | String request = String.format("CONNECT %s:%d HTTP/1.1\r\nHOST: %s:%d\r\nAccept: */*\r\nProxy-Connection: keep-alive\r\nUser-Agent: %s\r\nX-App-Install-ID: %s%s\r\n\r\n",
36 | m_DestAddress.getHostName(),
37 | m_DestAddress.getPort(),
38 | m_DestAddress.getHostName(),
39 | m_DestAddress.getPort(),
40 | ProxyConfig.Instance.getUserAgent(),
41 | ProxyConfig.AppInstallID,
42 | auth
43 | );
44 |
45 | buffer.clear();
46 | buffer.put(request.getBytes());
47 | buffer.flip();
48 | if(this.write(buffer,true)){//发送连接请求到代理服务器
49 | this.beginReceive();//开始接收代理服务器响应数据
50 | }
51 | }
52 |
53 | void trySendPartOfHeader(ByteBuffer buffer) throws Exception {
54 | int bytesSent=0;
55 | int _size = 1024*64;
56 | if(buffer.remaining()>_size){
57 | int pos=buffer.position()+buffer.arrayOffset();
58 | String firString=new String(buffer.array(),pos,_size).toUpperCase();
59 |
60 | int limit=buffer.limit();
61 | buffer.limit(buffer.position()+_size);
62 | super.write(buffer,false);
63 | bytesSent=_size-buffer.remaining();
64 | buffer.limit(limit);
65 | if(ProxyConfig.IS_DEBUG)
66 | System.out.printf("Send %d bytes(%s) to %s\n",bytesSent,firString,m_DestAddress);
67 | }
68 | }
69 |
70 |
71 | @Override
72 | protected void beforeSend(ByteBuffer buffer) throws Exception {
73 | if(ProxyConfig.Instance.isIsolateHttpHostHeader()){
74 | trySendPartOfHeader(buffer);//尝试发送请求头的一部分,让请求头的host在第二个包里面发送,从而绕过机房的白名单机制。
75 | }
76 | }
77 |
78 | @Override
79 | protected void afterReceived(ByteBuffer buffer) throws Exception {
80 | if(!m_TunnelEstablished){
81 | //收到代理服务器响应数据
82 | //分析响应并判断是否连接成功
83 | String response=new String(buffer.array(),buffer.position(),12);
84 | System.out.println(response);
85 |
86 | if(response.matches("^HTTP/1.[01] [0-9]+$")) {
87 | buffer.limit(buffer.position());
88 | }else {
89 | throw new Exception(String.format("Proxy server responsed an error: %s",response));
90 | }
91 |
92 | m_TunnelEstablished=true;
93 | super.onTunnelEstablished();
94 | }
95 | }
96 |
97 | @Override
98 | protected boolean isTunnelEstablished() {
99 | return m_TunnelEstablished;
100 | }
101 |
102 | @Override
103 | protected void onDispose() {
104 | m_Config=null;
105 | }
106 |
107 |
108 | }
109 |
--------------------------------------------------------------------------------
/Smart Proxy X/src/main/java/me/smartproxy/tunnel/shadowsocks/EncryptorFactory.java:
--------------------------------------------------------------------------------
1 | package me.smartproxy.tunnel.shadowsocks;
2 |
3 | import java.util.HashMap;
4 |
5 | import me.smartproxy.tunnel.IEncryptor;
6 |
7 | public class EncryptorFactory {
8 |
9 | private static HashMap EncryptorCache=new HashMap();
10 |
11 | public static IEncryptor createEncryptorByConfig(ShadowsocksConfig config) throws Exception{
12 | if("table".equals(config.EncryptMethod)){
13 | IEncryptor tableEncryptor=EncryptorCache.get(config.toString());
14 | if(tableEncryptor==null){
15 | tableEncryptor= new TableEncryptor(config.Password);
16 | if(EncryptorCache.size()>2){
17 | EncryptorCache.clear();
18 | }
19 | EncryptorCache.put(config.toString(), tableEncryptor);
20 | }
21 | return tableEncryptor;
22 | }
23 | throw new Exception(String.format("Does not support the '%s' method. Only 'table' encrypt method was supported.", config.EncryptMethod));
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Smart Proxy X/src/main/java/me/smartproxy/tunnel/shadowsocks/ShadowsocksConfig.java:
--------------------------------------------------------------------------------
1 | package me.smartproxy.tunnel.shadowsocks;
2 |
3 | import java.net.InetSocketAddress;
4 |
5 | import android.net.Uri;
6 | import android.util.Base64;
7 | import me.smartproxy.tunnel.Config;
8 |
9 | public class ShadowsocksConfig extends Config {
10 | public String EncryptMethod;
11 | public String Password;
12 |
13 | public static ShadowsocksConfig parse(String proxyInfo) throws Exception{
14 | ShadowsocksConfig config=new ShadowsocksConfig();
15 | Uri uri=Uri.parse(proxyInfo);
16 | if(uri.getPort()==-1){
17 | String base64String=uri.getHost();
18 | proxyInfo="ss://"+ new String(Base64.decode(base64String.getBytes("ASCII"),Base64.DEFAULT));
19 | uri=Uri.parse(proxyInfo);
20 | }
21 |
22 | String userInfoString=uri.getUserInfo();
23 | if(userInfoString!=null){
24 | String[] userStrings=userInfoString.split(":");
25 | config.EncryptMethod=userStrings[0];
26 | if(userStrings.length>=2){
27 | config.Password=userStrings[1];
28 | }
29 | }
30 | config.ServerAddress=new InetSocketAddress(uri.getHost(), uri.getPort());
31 | config.Encryptor=EncryptorFactory.createEncryptorByConfig(config);
32 | return config;
33 | }
34 |
35 | @Override
36 | public boolean equals(Object o) {
37 | if(o==null)
38 | return false;
39 | return this.toString().equals(o.toString());
40 | }
41 |
42 | @Override
43 | public String toString() {
44 | return String.format("ss://%s:%s@%s", EncryptMethod,Password,ServerAddress);
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/Smart Proxy X/src/main/java/me/smartproxy/tunnel/shadowsocks/ShadowsocksTunnel.java:
--------------------------------------------------------------------------------
1 | package me.smartproxy.tunnel.shadowsocks;
2 |
3 | import java.nio.ByteBuffer;
4 | import java.nio.channels.Selector;
5 |
6 | import me.smartproxy.tunnel.IEncryptor;
7 | import me.smartproxy.tunnel.Tunnel;
8 |
9 | public class ShadowsocksTunnel extends Tunnel {
10 |
11 | private IEncryptor m_Encryptor;
12 | private ShadowsocksConfig m_Config;
13 | private boolean m_TunnelEstablished;
14 |
15 | public ShadowsocksTunnel(ShadowsocksConfig config,Selector selector) throws Exception {
16 | super(config.ServerAddress,selector);
17 | if(config.Encryptor==null){
18 | throw new Exception("Error: The Encryptor for ShadowsocksTunnel is null.");
19 | }
20 | m_Config=config;
21 | m_Encryptor=config.Encryptor;
22 | }
23 |
24 | @Override
25 | protected void onConnected(ByteBuffer buffer) throws Exception {
26 |
27 | //构造socks5请求(跳过前3个字节)
28 | buffer.clear();
29 | buffer.put((byte)0x03);//domain
30 | byte[] domainBytes=m_DestAddress.getHostName().getBytes();
31 | buffer.put((byte)domainBytes.length);//domain length;
32 | buffer.put(domainBytes);
33 | buffer.putShort((short)m_DestAddress.getPort());
34 | buffer.flip();
35 |
36 | m_Encryptor.encrypt(buffer);
37 | if(write(buffer, true)){
38 | m_TunnelEstablished=true;
39 | onTunnelEstablished();
40 | }else {
41 | m_TunnelEstablished=true;
42 | this.beginReceive();
43 | }
44 | }
45 |
46 | @Override
47 | protected boolean isTunnelEstablished() {
48 | return m_TunnelEstablished;
49 | }
50 |
51 | @Override
52 | protected void beforeSend(ByteBuffer buffer) throws Exception {
53 | m_Encryptor.encrypt(buffer);
54 | }
55 |
56 | @Override
57 | protected void afterReceived(ByteBuffer buffer) throws Exception {
58 | m_Encryptor.decrypt(buffer);
59 | }
60 |
61 | @Override
62 | protected void onDispose() {
63 | m_Config=null;
64 | m_Encryptor=null;
65 | }
66 |
67 | }
68 |
--------------------------------------------------------------------------------
/Smart Proxy X/src/main/java/me/smartproxy/tunnel/shadowsocks/TableEncryptor.java:
--------------------------------------------------------------------------------
1 | package me.smartproxy.tunnel.shadowsocks;
2 |
3 | import java.nio.Buffer;
4 | import java.nio.ByteBuffer;
5 | import java.security.MessageDigest;
6 |
7 | import me.smartproxy.tunnel.IEncryptor;
8 |
9 | public class TableEncryptor implements IEncryptor {
10 |
11 | private byte[] encryptTable = new byte[256];
12 | private byte[] decryptTable = new byte[256];
13 |
14 | public TableEncryptor(String password){
15 |
16 | long a = passwordToInt64(password);
17 | for (int i = 0; i < 256; i++)
18 | {
19 | encryptTable[i] = (byte)i;
20 | }
21 |
22 | System.out.println("mergeSort....");
23 | long startTime=System.nanoTime();
24 | encryptTable = mergeSort(encryptTable, a);
25 | long endTime=System.nanoTime();
26 | System.out.printf("mergeSort: %3fms\n", (endTime-startTime)/1000D/1000D);
27 |
28 | for (int i = 0; i < 256; i++)
29 | {
30 | decryptTable[encryptTable[i]&0xFF] = (byte)i;
31 | }
32 | }
33 |
34 | long passwordToInt64(String password){
35 | try {
36 | byte[] passwordBytes=password.getBytes("UTF-8");
37 | MessageDigest md5 = MessageDigest.getInstance("MD5");
38 | byte[] hashPwd=md5.digest(passwordBytes);
39 | long a = bytesToInt64(hashPwd);
40 | return a;
41 | } catch (Exception e) {
42 | e.printStackTrace();
43 | return 0;
44 | }
45 | }
46 |
47 | long bytesToInt64(byte[] data){
48 | long value=data[0];
49 | value|=((long)(data[1]&0xFF)<<8);
50 | value|=((long)(data[2]&0xFF)<<16);
51 | value|=((long)(data[3]&0xFF)<<24);
52 | value|=((long)(data[4]&0xFF)<<32);
53 | value|=((long)(data[5]&0xFF)<<40);
54 | value|=((long)(data[6]&0xFF)<<48);
55 | value|=((long)(data[7]&0xFF)<<56);
56 | return value;
57 | }
58 |
59 | private byte[] mergeSort(byte[] srcArray , long a ){
60 | byte[] dstArray=new byte[256];
61 | long a1=Long.MAX_VALUE;
62 | long a2=(a&Long.MAX_VALUE);
63 | int stepSize,leftOffset,leftMaxOffset,rightOffset,rightMaxOffset,dstOffset,leftValue,rightValue;
64 |
65 | for (int i = 1; i < 1024; i++) {
66 | for (stepSize = 1;stepSize < 256;stepSize<<=1){
67 | for (dstOffset=0;dstOffset<256;){
68 | leftOffset=dstOffset;
69 | leftMaxOffset = leftOffset + stepSize;
70 | rightOffset=leftMaxOffset;
71 | rightMaxOffset = rightOffset + stepSize;
72 | for (;dstOffset0?((a%leftValue - a%rightValue) <=0):(((a1%leftValue+a2%leftValue+1)%leftValue - (a1%rightValue+a2%rightValue+1)%rightValue)<=0);
82 | if(isLeftSmallThanRight){
83 | dstArray[dstOffset] = srcArray[leftOffset++];
84 | }else {
85 | dstArray[dstOffset] = srcArray[rightOffset++];
86 | }
87 | }
88 | }
89 | }
90 |
91 | byte[] temp = dstArray;
92 | dstArray = srcArray;
93 | srcArray = temp;
94 | }
95 | }
96 | return srcArray;
97 | }
98 |
99 | @Override
100 | public void encrypt(ByteBuffer buffer) {
101 | byte[] data=buffer.array();
102 | for (int i = buffer.arrayOffset()+buffer.position(); i < buffer.limit(); i++) {
103 | data[i]=encryptTable[data[i]&0xFF];
104 | }
105 | }
106 |
107 | @Override
108 | public void decrypt(ByteBuffer buffer) {
109 | byte[] data=buffer.array();
110 | for (int i = buffer.arrayOffset()+buffer.position(); i < buffer.limit(); i++) {
111 | data[i]=decryptTable[data[i]&0xFF];
112 | }
113 | }
114 |
115 | }
116 |
--------------------------------------------------------------------------------
/Smart Proxy X/src/main/java/me/smartproxy/ui/MainActivity.java:
--------------------------------------------------------------------------------
1 | package me.smartproxy.ui;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.app.Activity;
5 | import android.app.AlertDialog;
6 | import android.content.DialogInterface;
7 | import android.content.DialogInterface.OnClickListener;
8 | import android.content.Intent;
9 | import android.content.SharedPreferences;
10 | import android.content.SharedPreferences.Editor;
11 | import android.content.pm.PackageManager;
12 | import android.net.Uri;
13 | import android.os.Bundle;
14 | import android.text.InputType;
15 | import android.text.TextUtils;
16 | import android.util.Log;
17 | import android.view.Menu;
18 | import android.view.MenuItem;
19 | import android.view.View;
20 | import android.widget.*;
21 | import android.widget.CompoundButton.OnCheckedChangeListener;
22 |
23 | import me.smartproxy.R;
24 | import me.smartproxy.core.LocalVpnService;
25 | import me.smartproxy.core.TmpConfig;
26 |
27 | import java.util.Calendar;
28 |
29 | public class MainActivity extends Activity implements
30 | View.OnClickListener,
31 | OnCheckedChangeListener,
32 | LocalVpnService.onStatusChangedListener {
33 |
34 | private static String GL_HISTORY_LOGS;
35 |
36 | private static final String TAG = MainActivity.class.getSimpleName();
37 |
38 | private static final int START_VPN_SERVICE_REQUEST_CODE = 1985;
39 |
40 | private Switch switchProxy;
41 | private TextView textViewLog;
42 | private ScrollView scrollViewLog;
43 | private TextView textViewConfigUrl;
44 | private Calendar mCalendar;
45 | private CheckBox byPass;
46 |
47 | @Override
48 | protected void onCreate(Bundle savedInstanceState) {
49 | super.onCreate(savedInstanceState);
50 | setContentView(R.layout.activity_main);
51 |
52 | scrollViewLog = (ScrollView) findViewById(R.id.scrollViewLog);
53 | textViewLog = (TextView) findViewById(R.id.textViewLog);
54 |
55 | textViewLog.setText(GL_HISTORY_LOGS);
56 | scrollViewLog.fullScroll(ScrollView.FOCUS_DOWN);
57 |
58 | mCalendar = Calendar.getInstance();
59 | LocalVpnService.addOnStatusChangedListener(this);
60 |
61 | byPass = (CheckBox) findViewById(R.id.bypassChina);
62 |
63 | // TmpConfig.bypass = byPass.isChecked();
64 |
65 | final TextView UserNameField = (TextView) findViewById(R.id.UserName);
66 | final TextView PasswordField = (TextView) findViewById(R.id.Password);
67 | final TextView IpField = (TextView) findViewById(R.id.remoteIp);
68 | final TextView PortField = (TextView) findViewById(R.id.remotePort);
69 |
70 | String UserNameFromDB = readConfigKey(TmpConfig.UserKey);
71 | String PasswordDB = readConfigKey(TmpConfig.PasswordKey);
72 | String IpDB = readConfigKey(TmpConfig.IpKey);
73 | String PortDB = readConfigKey(TmpConfig.PortKey);
74 | String ByPassDB = readConfigKey(TmpConfig.ByPassKey);
75 |
76 |
77 | if (TextUtils.isEmpty(UserNameFromDB) == false) {
78 | UserNameField.setText(UserNameFromDB);
79 | TmpConfig.UserName = UserNameFromDB;
80 | }
81 | if (TextUtils.isEmpty(PasswordDB) == false) {
82 | PasswordField.setText(PasswordDB);
83 | TmpConfig.Password = PasswordDB;
84 | }
85 | if (TextUtils.isEmpty(IpDB)) {
86 | IpField.setText("remote_ip");
87 | } else {
88 | IpField.setText(IpDB);
89 | TmpConfig.remoteIp = IpDB;
90 | }
91 | if (TextUtils.isEmpty(PortDB)) {
92 | PortField.setText("remote_port");
93 | } else {
94 | PortField.setText(PortDB);
95 | TmpConfig.remotePort = PortDB;
96 | }
97 |
98 | if (ByPassDB.equals("true")) {
99 | TmpConfig.bypass = true;
100 | } else {
101 | TmpConfig.bypass = false;
102 | }
103 | byPass.setChecked(TmpConfig.bypass);
104 |
105 |
106 | Button button = (Button) findViewById(R.id.confirm);
107 | button.setOnClickListener(new View.OnClickListener() {
108 | @Override
109 | public void onClick(View v) {
110 | String UserName = (String) UserNameField.getText().toString();
111 | setConfigKey(TmpConfig.UserKey, UserName);
112 | TmpConfig.UserName = UserName;
113 | String Password = (String) PasswordField.getText().toString();
114 | setConfigKey(TmpConfig.PasswordKey, Password);
115 | TmpConfig.Password = Password;
116 | String Ip = (String) IpField.getText().toString();
117 | setConfigKey(TmpConfig.IpKey, Ip);
118 | TmpConfig.remoteIp = Ip;
119 | String Port = (String) PortField.getText().toString();
120 | setConfigKey(TmpConfig.PortKey, Port);
121 | TmpConfig.remotePort = Port;
122 | boolean bypass = (boolean) byPass.isChecked();
123 |
124 | if (bypass) {
125 | setConfigKey(TmpConfig.ByPassKey, "true");
126 | } else {
127 | setConfigKey(TmpConfig.ByPassKey, "false");
128 | }
129 | TmpConfig.bypass = bypass;
130 | TmpConfig.remotePort = Port;
131 | }
132 | });
133 | }
134 |
135 | String readConfigUrl() {
136 | SharedPreferences preferences = getSharedPreferences("SmartProxy", MODE_PRIVATE);
137 | return preferences.getString(TmpConfig.CONFIG_URL_KEY, "");
138 | }
139 |
140 | void setConfigUrl(String configUrl) {
141 | SharedPreferences preferences = getSharedPreferences("SmartProxy", MODE_PRIVATE);
142 | Editor editor = preferences.edit();
143 | editor.putString(TmpConfig.CONFIG_URL_KEY, configUrl);
144 | editor.commit();
145 | }
146 |
147 | String readConfigKey(String Key) {
148 | SharedPreferences preferences = getSharedPreferences("SmartProxy", MODE_PRIVATE);
149 | return preferences.getString(Key, "");
150 | }
151 |
152 | public void setConfigKey(String Key, String Value) {
153 | SharedPreferences preferences = getSharedPreferences("SmartProxy", MODE_PRIVATE);
154 | Editor editor = preferences.edit();
155 |
156 | editor.putString(Key, Value);
157 | editor.commit();
158 | }
159 |
160 | String getVersionName() {
161 | PackageManager packageManager = getPackageManager();
162 | if (packageManager == null) {
163 | Log.e(TAG, "null package manager is impossible");
164 | return null;
165 | }
166 |
167 | try {
168 | return packageManager.getPackageInfo(getPackageName(), 0).versionName;
169 | } catch (PackageManager.NameNotFoundException e) {
170 | Log.e(TAG, "package not found is impossible", e);
171 | return null;
172 | }
173 | }
174 |
175 | boolean isValidUrl(String url) {
176 | try {
177 | // TODO clean this
178 | return true;
179 | } catch (Exception e) {
180 | return false;
181 | }
182 | }
183 |
184 | @Override
185 | public void onClick(View v) {
186 | if (switchProxy.isChecked()) {
187 | return;
188 | }
189 |
190 | new AlertDialog.Builder(this)
191 | .setTitle(R.string.config_url)
192 | .setItems(new CharSequence[]{
193 | getString(R.string.config_url_scan),
194 | getString(R.string.config_url_manual)
195 | }, new OnClickListener() {
196 | @Override
197 | public void onClick(DialogInterface dialogInterface, int i) {
198 | switch (i) {
199 | case 0:
200 | break;
201 | case 1:
202 | showConfigUrlInputDialog();
203 | break;
204 | }
205 | }
206 | })
207 | .show();
208 | }
209 |
210 | private void showConfigUrlInputDialog() {
211 | final EditText editText = new EditText(this);
212 | editText.setInputType(InputType.TYPE_TEXT_VARIATION_URI);
213 | editText.setHint(getString(R.string.config_url_hint));
214 | editText.setText(readConfigUrl());
215 |
216 | new AlertDialog.Builder(this)
217 | .setTitle(R.string.config_url)
218 | .setView(editText)
219 | .setPositiveButton(R.string.btn_ok, new OnClickListener() {
220 | @Override
221 | public void onClick(DialogInterface dialog, int which) {
222 | if (editText.getText() == null) {
223 | return;
224 | }
225 |
226 | String configUrl = editText.getText().toString().trim();
227 |
228 | if (isValidUrl(configUrl)) {
229 | setConfigUrl(configUrl);
230 | textViewConfigUrl.setText(configUrl);
231 | } else {
232 | Toast.makeText(MainActivity.this, R.string.err_invalid_url, Toast.LENGTH_SHORT).show();
233 | }
234 | }
235 | })
236 | .setNegativeButton(R.string.btn_cancel, null)
237 | .show();
238 | }
239 |
240 | @SuppressLint("DefaultLocale")
241 | @Override
242 | public void onLogReceived(String logString) {
243 | mCalendar.setTimeInMillis(System.currentTimeMillis());
244 | logString = String.format("[%1$02d:%2$02d:%3$02d] %4$s\n",
245 | mCalendar.get(Calendar.HOUR_OF_DAY),
246 | mCalendar.get(Calendar.MINUTE),
247 | mCalendar.get(Calendar.SECOND),
248 | logString);
249 |
250 | System.out.println(logString);
251 |
252 | if (textViewLog.getLineCount() > 200) {
253 | textViewLog.setText("");
254 | }
255 | textViewLog.append(logString);
256 | scrollViewLog.fullScroll(ScrollView.FOCUS_DOWN);
257 | GL_HISTORY_LOGS = textViewLog.getText() == null ? "" : textViewLog.getText().toString();
258 | }
259 |
260 | @Override
261 | public void onStatusChanged(String status, Boolean isRunning) {
262 | switchProxy.setEnabled(true);
263 | switchProxy.setChecked(isRunning);
264 | onLogReceived(status);
265 | Toast.makeText(this, status, Toast.LENGTH_SHORT).show();
266 | }
267 |
268 | @Override
269 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
270 | if (LocalVpnService.IsRunning != isChecked) {
271 | switchProxy.setEnabled(false);
272 |
273 | if (isChecked) {
274 | TmpConfig.CopyAndStart(this);
275 | startVpn();
276 | } else {
277 | LocalVpnService.IsRunning = false;
278 | }
279 | }
280 | }
281 |
282 | public void startVpn() {
283 | Intent intent = LocalVpnService.prepare(this);
284 | if (intent == null) {
285 | startVPNService();
286 | } else {
287 | startActivityForResult(intent, START_VPN_SERVICE_REQUEST_CODE);
288 | }
289 | }
290 |
291 | private void startVPNService() {
292 | String configUrl = TmpConfig.getConfig(this);
293 | if (!isValidUrl(configUrl)) {
294 | Toast.makeText(this, R.string.err_invalid_url, Toast.LENGTH_SHORT).show();
295 | switchProxy.post(new Runnable() {
296 | @Override
297 | public void run() {
298 | switchProxy.setChecked(false);
299 | switchProxy.setEnabled(true);
300 | }
301 | });
302 | return;
303 | }
304 |
305 | textViewLog.setText("");
306 | GL_HISTORY_LOGS = null;
307 | onLogReceived("starting...");
308 | LocalVpnService.ConfigUrl = configUrl;
309 | startService(new Intent(this, LocalVpnService.class));
310 | }
311 |
312 | @Override
313 | protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
314 | if (requestCode == START_VPN_SERVICE_REQUEST_CODE) {
315 | if (resultCode == RESULT_OK) {
316 | startVPNService();
317 | } else {
318 | switchProxy.setChecked(false);
319 | switchProxy.setEnabled(true);
320 | onLogReceived("canceled.");
321 | }
322 | return;
323 | }
324 |
325 | super.onActivityResult(requestCode, resultCode, intent);
326 | }
327 |
328 | @Override
329 | public boolean onCreateOptionsMenu(Menu menu) {
330 | getMenuInflater().inflate(R.menu.main_activity_actions, menu);
331 |
332 | MenuItem menuItem = menu.findItem(R.id.menu_item_switch);
333 | if (menuItem == null) {
334 | return false;
335 | }
336 |
337 | switchProxy = (Switch) menuItem.getActionView();
338 | if (switchProxy == null) {
339 | return false;
340 | }
341 |
342 | switchProxy.setChecked(LocalVpnService.IsRunning);
343 | switchProxy.setOnCheckedChangeListener(this);
344 |
345 | return true;
346 | }
347 |
348 | @Override
349 | public boolean onOptionsItemSelected(MenuItem item) {
350 | switch (item.getItemId()) {
351 | case R.id.menu_item_about:
352 | new AlertDialog.Builder(this)
353 | .setTitle(getString(R.string.app_name) + getVersionName())
354 | .setMessage(R.string.about_info)
355 | .setPositiveButton(R.string.btn_ok, null)
356 | .setNegativeButton(R.string.btn_more, new OnClickListener() {
357 | @Override
358 | public void onClick(DialogInterface dialog, int which) {
359 | startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/zxc111/SmartProxy")));
360 | }
361 | })
362 | .show();
363 |
364 | return true;
365 | case R.id.menu_item_exit:
366 | if (!LocalVpnService.IsRunning) {
367 | finish();
368 | return true;
369 | }
370 |
371 | new AlertDialog.Builder(this)
372 | .setTitle(R.string.menu_item_exit)
373 | .setMessage(R.string.exit_confirm_info)
374 | .setPositiveButton(R.string.btn_ok, new OnClickListener() {
375 | @Override
376 | public void onClick(DialogInterface dialog, int which) {
377 | LocalVpnService.IsRunning = false;
378 | LocalVpnService.Instance.disconnectVPN();
379 | stopService(new Intent(MainActivity.this, LocalVpnService.class));
380 | System.runFinalization();
381 | System.exit(0);
382 | }
383 | })
384 | .setNegativeButton(R.string.btn_cancel, null)
385 | .show();
386 |
387 | return true;
388 | default:
389 | return super.onOptionsItemSelected(item);
390 | }
391 | }
392 |
393 | @Override
394 | protected void onDestroy() {
395 | LocalVpnService.removeOnStatusChangedListener(this);
396 | super.onDestroy();
397 | }
398 |
399 | }
400 |
--------------------------------------------------------------------------------
/Smart Proxy X/src/main/java/me/smartproxy/ui/QuickSettingsService.java:
--------------------------------------------------------------------------------
1 | package me.smartproxy.ui;
2 |
3 | import android.annotation.TargetApi;
4 | import android.content.Intent;
5 | import android.graphics.drawable.Icon;
6 | import android.os.Build;
7 | import android.service.quicksettings.TileService;
8 | import android.service.quicksettings.Tile;
9 | import android.util.Log;
10 | import android.widget.Switch;
11 |
12 | import me.smartproxy.core.TmpConfig;
13 | import me.smartproxy.R;
14 | import me.smartproxy.core.LocalVpnService;
15 |
16 | /**
17 | * Created by3/27.
18 | */
19 |
20 | @TargetApi(Build.VERSION_CODES.N)
21 | public class QuickSettingsService extends TileService{
22 | //当用户从Edit栏添加到快速设置中调用
23 |
24 | private final int STATE_OFF = 0;
25 | private final int STATE_ON = 1;
26 | private final String LOG_TAG = "QuickSettingService";
27 | private int toggleState = STATE_OFF;
28 | private static final Boolean isrunning = true;
29 |
30 | private Switch switchProxy;
31 | @Override
32 | public void onTileAdded() {
33 | Log.d(LOG_TAG, "onTileAdded");
34 | }
35 | //当用户从快速设置栏中移除的时候调用
36 | @Override
37 | public void onTileRemoved() {
38 | Log.d(LOG_TAG, "onTileRemoved");
39 | }
40 | // 点击的时候
41 | @TargetApi(Build.VERSION_CODES.N)
42 | @Override
43 | public void onClick() {
44 | Icon icon;
45 | icon = Icon.createWithResource(getApplicationContext(), R.drawable.ic_launcher);
46 | if (toggleState == STATE_ON) {
47 | toggleState = STATE_OFF;
48 | getQsTile().setState(Tile.STATE_INACTIVE);// 更改成非活跃状态
49 | // switchProxy.setChecked(false);
50 | if (LocalVpnService.IsRunning) {
51 | LocalVpnService.IsRunning = false;
52 | LocalVpnService.Instance.disconnectVPN();
53 | // stopService(new Intent(this, LocalVpnService.class));
54 | }
55 | } else {
56 | toggleState = STATE_ON;
57 | getQsTile().setState(Tile.STATE_ACTIVE);//更改成活跃状态
58 | TmpConfig.CopyAndStart(this);
59 | String configUrl = TmpConfig.getConfig(this);
60 | System.out.println(configUrl);
61 | LocalVpnService.ConfigUrl = configUrl;
62 | LocalVpnService.IsRunning = true;
63 |
64 | Intent intent = LocalVpnService.prepare(this);
65 | if (intent == null) {
66 | // startVPNService();
67 | startService(new Intent(this, LocalVpnService.class));
68 | } else {
69 | // startActivityForResult(intent, START_VPN_SERVICE_REQUEST_CODE);
70 | }
71 |
72 | }
73 |
74 | getQsTile().setIcon(icon);//设置图标
75 | getQsTile().updateTile();//更新Tile
76 | }
77 |
78 | // 打开下拉菜单的时候调用,当快速设置按钮并没有在编辑栏拖到设置栏中不会调用
79 | //在TleAdded之后会调用一次
80 | @Override
81 | public void onStartListening () {
82 | Icon icon;
83 | icon = Icon.createWithResource(getApplicationContext(), R.drawable.ic_launcher);
84 | if (LocalVpnService.IsRunning) {
85 | toggleState = STATE_ON;
86 | getQsTile().setState(Tile.STATE_ACTIVE);
87 | } else {
88 | toggleState = STATE_OFF;
89 | getQsTile().setState(Tile.STATE_INACTIVE);
90 | }
91 | getQsTile().setIcon(icon);//设置图标
92 | getQsTile().updateTile();//更新Tile
93 | Log.d(LOG_TAG, "onStartListening");
94 | }
95 | // 关闭下拉菜单的时候调用,当快速设置按钮并没有在编辑栏拖到设置栏中不会调用
96 | // 在onTileRemoved移除之前也会调用移除
97 | @Override
98 | public void onStopListening () {
99 | Log.d(LOG_TAG, "onStopListening");
100 | }
101 |
102 | /*public void setStateOn() {
103 | String config = new TmpConfig().getConfig(this);
104 | System.out.println(config);
105 | }
106 | public void setStateOff() {
107 |
108 | }*/
109 | public void QSService() {
110 | QuickSettingsService instance = this;
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/Smart Proxy X/src/main/jniLibs/arm64-v8a/libnghttpx.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zxc111/SmartProxy/24a12530cab7fe6f6e00c048d973e507da919ab3/Smart Proxy X/src/main/jniLibs/arm64-v8a/libnghttpx.so
--------------------------------------------------------------------------------
/Smart Proxy X/src/main/jniLibs/libnghttpx.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zxc111/SmartProxy/24a12530cab7fe6f6e00c048d973e507da919ab3/Smart Proxy X/src/main/jniLibs/libnghttpx.so
--------------------------------------------------------------------------------
/Smart Proxy X/src/main/jniLibs/x86/libnghttpx.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zxc111/SmartProxy/24a12530cab7fe6f6e00c048d973e507da919ab3/Smart Proxy X/src/main/jniLibs/x86/libnghttpx.so
--------------------------------------------------------------------------------
/Smart Proxy X/src/main/jniLibs/x86_64/libnghttpx.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zxc111/SmartProxy/24a12530cab7fe6f6e00c048d973e507da919ab3/Smart Proxy X/src/main/jniLibs/x86_64/libnghttpx.so
--------------------------------------------------------------------------------
/Smart Proxy X/src/main/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zxc111/SmartProxy/24a12530cab7fe6f6e00c048d973e507da919ab3/Smart Proxy X/src/main/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/Smart Proxy X/src/main/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zxc111/SmartProxy/24a12530cab7fe6f6e00c048d973e507da919ab3/Smart Proxy X/src/main/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/Smart Proxy X/src/main/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zxc111/SmartProxy/24a12530cab7fe6f6e00c048d973e507da919ab3/Smart Proxy X/src/main/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/Smart Proxy X/src/main/res/drawable-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zxc111/SmartProxy/24a12530cab7fe6f6e00c048d973e507da919ab3/Smart Proxy X/src/main/res/drawable-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/Smart Proxy X/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
12 |
13 |
18 |
19 |
25 |
26 |
34 |
35 |
36 |
41 |
42 |
48 |
49 |
55 |
56 |
57 |
62 |
63 |
69 |
70 |
76 |
77 |
78 |
83 |
84 |
90 |
91 |
97 |
98 |
99 |
104 |
105 |
110 |
111 |
112 |
113 |
120 |
121 |
122 |
129 |
130 |
134 |
135 |
139 |
140 |
146 |
147 |
148 |
149 |
150 |
--------------------------------------------------------------------------------
/Smart Proxy X/src/main/res/menu/main_activity_actions.xml:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/Smart Proxy X/src/main/res/raw/ipmask:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zxc111/SmartProxy/24a12530cab7fe6f6e00c048d973e507da919ab3/Smart Proxy X/src/main/res/raw/ipmask
--------------------------------------------------------------------------------
/Smart Proxy X/src/main/res/values-sw600dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Smart Proxy X/src/main/res/values-sw720dp-land/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 | 128dp
8 |
9 |
10 |
--------------------------------------------------------------------------------
/Smart Proxy X/src/main/res/values-v11/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/Smart Proxy X/src/main/res/values-v14/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/Smart Proxy X/src/main/res/values-zh-rCN/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | SmartProxy
4 | 代理
5 | 关于
6 | 退出
7 | 配置地址
8 | 未设置
9 | 扫描二维码
10 | 手动输入
11 | 扫描配置地址的二维码
12 | 确定
13 | 取消
14 | 更多…
15 | 已连接
16 | 已断开
17 | http/2 proxy client
18 | 这将会断开VPN连接,确定要退出吗?
19 | 无效的配置地址
20 | URL或本地文件路径
21 |
22 |
--------------------------------------------------------------------------------
/Smart Proxy X/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 16dp
5 | 16dp
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Smart Proxy X/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | SmartProxy
4 | Proxy
5 | About
6 | Exit
7 | Config URL
8 | Not set
9 | Scan an QR code
10 | Type manually
11 | Scan the QR code of your config url
12 | OK
13 | Cancel
14 | More…
15 | connected
16 | disconnected
17 | SmartProxy is a smart TCP transparent proxy software just like its name. It is determined according to the rules of the configuration file using a proxy or directly connected. The biggest advantage is prevent DNS cache pollution, never lost connection, also very save electricity.
18 | This will disconnect the VPN connection, Are you sure to exit?
19 | The config URL is invalid.
20 | URL or local file path
21 |
22 |
--------------------------------------------------------------------------------
/Smart Proxy X/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
14 |
15 |
16 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | google()
6 | mavenCentral()
7 | }
8 |
9 | dependencies {
10 | classpath 'com.android.tools.build:gradle:7.0.4'
11 | }
12 | }
13 |
14 | allprojects {
15 | repositories {
16 | google()
17 | mavenCentral()
18 | }
19 | }
20 |
21 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Settings specified in this file will override any Gradle settings
5 | # configured through the IDE.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zxc111/SmartProxy/24a12530cab7fe6f6e00c048d973e507da919ab3/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Jan 03 22:04:56 CST 2022
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # For Cygwin, ensure paths are in UNIX format before anything is touched.
46 | if $cygwin ; then
47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
48 | fi
49 |
50 | # Attempt to set APP_HOME
51 | # Resolve links: $0 may be a link
52 | PRG="$0"
53 | # Need this for relative symlinks.
54 | while [ -h "$PRG" ] ; do
55 | ls=`ls -ld "$PRG"`
56 | link=`expr "$ls" : '.*-> \(.*\)$'`
57 | if expr "$link" : '/.*' > /dev/null; then
58 | PRG="$link"
59 | else
60 | PRG=`dirname "$PRG"`"/$link"
61 | fi
62 | done
63 | SAVED="`pwd`"
64 | cd "`dirname \"$PRG\"`/" >&-
65 | APP_HOME="`pwd -P`"
66 | cd "$SAVED" >&-
67 |
68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
69 |
70 | # Determine the Java command to use to start the JVM.
71 | if [ -n "$JAVA_HOME" ] ; then
72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
73 | # IBM's JDK on AIX uses strange locations for the executables
74 | JAVACMD="$JAVA_HOME/jre/sh/java"
75 | else
76 | JAVACMD="$JAVA_HOME/bin/java"
77 | fi
78 | if [ ! -x "$JAVACMD" ] ; then
79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
80 |
81 | Please set the JAVA_HOME variable in your environment to match the
82 | location of your Java installation."
83 | fi
84 | else
85 | JAVACMD="java"
86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
87 |
88 | Please set the JAVA_HOME variable in your environment to match the
89 | location of your Java installation."
90 | fi
91 |
92 | # Increase the maximum file descriptors if we can.
93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
94 | MAX_FD_LIMIT=`ulimit -H -n`
95 | if [ $? -eq 0 ] ; then
96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
97 | MAX_FD="$MAX_FD_LIMIT"
98 | fi
99 | ulimit -n $MAX_FD
100 | if [ $? -ne 0 ] ; then
101 | warn "Could not set maximum file descriptor limit: $MAX_FD"
102 | fi
103 | else
104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
105 | fi
106 | fi
107 |
108 | # For Darwin, add options to specify how the application appears in the dock
109 | if $darwin; then
110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
111 | fi
112 |
113 | # For Cygwin, switch paths to Windows format before running java
114 | if $cygwin ; then
115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 | JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 |
164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
165 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':Smart Proxy X'
2 |
--------------------------------------------------------------------------------