shadowsocks-libev.js 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. 'use strict';
  2. 'require uci';
  3. 'require form';
  4. 'require network';
  5. var names_options_server = [
  6. 'server',
  7. 'server_port',
  8. 'method',
  9. 'key',
  10. 'password',
  11. 'plugin',
  12. 'plugin_opts',
  13. ];
  14. var names_options_client = [
  15. 'server',
  16. 'local_address',
  17. 'local_port',
  18. ];
  19. var names_options_common = [
  20. 'verbose',
  21. 'ipv6_first',
  22. 'fast_open',
  23. 'no_delay',
  24. 'reuse_port',
  25. 'mode',
  26. 'mtu',
  27. 'timeout',
  28. 'user',
  29. ];
  30. var modes = [
  31. 'tcp_only',
  32. 'tcp_and_udp',
  33. 'udp_only',
  34. ];
  35. var methods = [
  36. // aead
  37. 'aes-128-gcm',
  38. 'aes-192-gcm',
  39. 'aes-256-gcm',
  40. 'chacha20-ietf-poly1305',
  41. 'xchacha20-ietf-poly1305',
  42. // stream
  43. 'table',
  44. 'rc4',
  45. 'rc4-md5',
  46. 'aes-128-cfb',
  47. 'aes-192-cfb',
  48. 'aes-256-cfb',
  49. 'aes-128-ctr',
  50. 'aes-192-ctr',
  51. 'aes-256-ctr',
  52. 'bf-cfb',
  53. 'camellia-128-cfb',
  54. 'camellia-192-cfb',
  55. 'camellia-256-cfb',
  56. 'salsa20',
  57. 'chacha20',
  58. 'chacha20-ietf',
  59. ];
  60. function ucival_to_bool(val) {
  61. return val === 'true' || val === '1' || val === 'yes' || val === 'on';
  62. }
  63. return L.Class.extend({
  64. values_actions: function(o) {
  65. o.value('bypass');
  66. o.value('forward');
  67. if (o.option !== 'dst_default') {
  68. o.value('checkdst');
  69. }
  70. },
  71. values_redir: function(o, xmode) {
  72. uci.sections('shadowsocks-libev', 'ss_redir', function(sdata) {
  73. var disabled = ucival_to_bool(sdata['disabled']),
  74. sname = sdata['.name'],
  75. mode = sdata['mode'] || 'tcp_only';
  76. if (!disabled && mode.indexOf(xmode) !== -1) {
  77. o.value(sname, sname + ' - ' + mode);
  78. }
  79. });
  80. o.value('', '<unset>');
  81. o.default = '';
  82. },
  83. values_serverlist: function(o) {
  84. uci.sections('shadowsocks-libev', 'server', function(sdata) {
  85. var sname = sdata['.name'],
  86. server = sdata['server'],
  87. server_port = sdata['server_port'];
  88. if (server && server_port) {
  89. var disabled = ucival_to_bool(sdata['.disabled']) ? ' - disabled' : '',
  90. desc = '%s - %s:%s%s'.format(sname, server, server_port, disabled);
  91. o.value(sname, desc);
  92. }
  93. });
  94. },
  95. values_ipaddr: function(o, netDevs) {
  96. netDevs.forEach(function(v) {
  97. v.getIPAddrs().forEach(function(a) {
  98. var host = a.split('/')[0];
  99. o.value(host, '%s (%s)'.format(host, v.getShortName()));
  100. });
  101. });
  102. },
  103. options_client: function(s, tab, netDevs) {
  104. var o = s.taboption(tab, form.ListValue, 'server', _('Remote server'));
  105. this.values_serverlist(o);
  106. o = s.taboption(tab, form.Value, 'local_address', _('Local address'));
  107. o.datatype = 'ipaddr';
  108. o.placeholder = '0.0.0.0';
  109. this.values_ipaddr(o, netDevs);
  110. o = s.taboption(tab, form.Value, 'local_port', _('Local port'));
  111. o.datatype = 'port';
  112. },
  113. options_server: function(s, opts) {
  114. var o, optfunc,
  115. tab = opts && opts.tab || null;
  116. if (!tab) {
  117. optfunc = function(/* ... */) {
  118. var o = s.option.apply(s, arguments);
  119. o.editable = true;
  120. return o;
  121. };
  122. } else {
  123. optfunc = function(/* ... */) {
  124. var o = s.taboption.apply(s, L.varargs(arguments, 0, tab));
  125. o.editable = true;
  126. return o;
  127. };
  128. }
  129. o = optfunc(form.Value, 'server', _('Server'));
  130. o.datatype = 'host';
  131. o.size = 16;
  132. o = optfunc(form.Value, 'server_port', _('Server port'));
  133. o.datatype = 'port';
  134. o.size = 5;
  135. o = optfunc(form.ListValue, 'method', _('Method'));
  136. methods.forEach(function(m) {
  137. o.value(m);
  138. });
  139. o = optfunc(form.Value, 'password', _('Password'));
  140. o.password = true;
  141. o.size = 12;
  142. o = optfunc(form.Value, 'key', _('Key (base64)'));
  143. o.datatype = 'base64';
  144. o.password = true;
  145. o.size = 12;
  146. o.modalonly = true;;
  147. optfunc(form.Value, 'plugin', _('Plugin')).modalonly = true;
  148. optfunc(form.Value, 'plugin_opts', _('Plugin Options')).modalonly = true;
  149. },
  150. options_common: function(s, tab) {
  151. var o = s.taboption(tab, form.ListValue, 'mode', _('Mode of operation'));
  152. modes.forEach(function(m) {
  153. o.value(m);
  154. });
  155. o.default = 'tcp_and_udp';
  156. o = s.taboption(tab, form.Value, 'mtu', _('MTU'));
  157. o.datatype = 'uinteger';
  158. o = s.taboption(tab, form.Value, 'timeout', _('Timeout (sec)'));
  159. o.datatype = 'uinteger';
  160. s.taboption(tab, form.Value, 'user', _('Run as'));
  161. s.taboption(tab, form.Flag, 'verbose', _('Verbose'));
  162. s.taboption(tab, form.Flag, 'ipv6_first', _('IPv6 First'), _('Prefer IPv6 addresses when resolving names'));
  163. s.taboption(tab, form.Flag, 'fast_open', _('Enable TCP Fast Open'));
  164. s.taboption(tab, form.Flag, 'no_delay', _('Enable TCP_NODELAY'));
  165. s.taboption(tab, form.Flag, 'reuse_port', _('Enable SO_REUSEPORT'));
  166. },
  167. ucival_to_bool: function(val) {
  168. return ucival_to_bool(val);
  169. },
  170. cfgvalue_overview: function(sdata) {
  171. var stype = sdata['.type'],
  172. lines = [];
  173. if (stype === 'ss_server') {
  174. this.cfgvalue_overview_(sdata, lines, names_options_server);
  175. this.cfgvalue_overview_(sdata, lines, names_options_common);
  176. this.cfgvalue_overview_(sdata, lines, ['bind_address']);
  177. } else if (stype === 'ss_local' || stype === 'ss_redir' || stype === 'ss_tunnel') {
  178. this.cfgvalue_overview_(sdata, lines, names_options_client);
  179. if (stype === 'ss_tunnel') {
  180. this.cfgvalue_overview_(sdata, lines, ['tunnel_address']);
  181. }
  182. this.cfgvalue_overview_(sdata, lines, names_options_common);
  183. } else {
  184. return [];
  185. }
  186. return lines;
  187. },
  188. cfgvalue_overview_: function(sdata, lines, names) {
  189. names.forEach(function(n) {
  190. var v = sdata[n];
  191. if (v) {
  192. if (n === 'key' || n === 'password') {
  193. v = _('<hidden>');
  194. }
  195. var fv = E('var', [v]);
  196. if (sdata['.type'] !== 'ss_server' && n === 'server') {
  197. fv = E('a', {
  198. class: 'label',
  199. href: L.url('admin/services/shadowsocks-libev/servers') + '#edit=' + v,
  200. target: '_blank',
  201. rel: 'noopener'
  202. }, fv);
  203. }
  204. lines.push(n + ': ', fv, E('br'));
  205. }
  206. });
  207. },
  208. option_install_package: function(s, tab) {
  209. var bin = s.sectiontype.replace('_', '-'),
  210. opkg_package = 'shadowsocks-libev-' + bin, o;
  211. if (tab) {
  212. o = s.taboption(tab, form.Button, '_install');
  213. } else {
  214. o = s.option(form.Button, '_install');
  215. }
  216. o.title = _('Package is not installed');
  217. o.inputtitle = _('Install package ' + opkg_package);
  218. o.inputstyle = 'apply';
  219. o.onclick = function() {
  220. window.open(L.url('admin/system/opkg') +
  221. '?query=' + opkg_package, '_blank', 'noopener');
  222. };
  223. },
  224. parse_uri: function(uri) {
  225. var scheme = 'ss://';
  226. if (uri && uri.indexOf(scheme) === 0) {
  227. var atPos = uri.indexOf('@'), hashPos = uri.lastIndexOf('#'), tag;
  228. if (hashPos === -1) {
  229. hashPos = undefined;
  230. } else {
  231. tag = uri.slice(hashPos + 1);
  232. }
  233. if (atPos !== -1) { // SIP002 format https://shadowsocks.org/en/spec/SIP002-URI-Scheme.html
  234. var colonPos = uri.indexOf(':', atPos + 1), slashPos = uri.indexOf('/', colonPos + 1);
  235. if (colonPos === -1) return null;
  236. if (slashPos === -1) slashPos = undefined;
  237. var userinfo = atob(uri.slice(scheme.length, atPos)
  238. .replace(/-/g, '+').replace(/_/g, '/')),
  239. i = userinfo.indexOf(':');
  240. if (i === -1) return null;
  241. var config = {
  242. server: uri.slice(atPos + 1, colonPos),
  243. server_port: uri.slice(colonPos + 1, slashPos ? slashPos : hashPos),
  244. password: userinfo.slice(i + 1),
  245. method: userinfo.slice(0, i)
  246. };
  247. if (slashPos) {
  248. var search = uri.slice(slashPos + 1, hashPos);
  249. if (search[0] === '?') search = search.slice(1);
  250. search.split('&').forEach(function(s) {
  251. var j = s.indexOf('=');
  252. if (j !== -1) {
  253. var k = s.slice(0, j), v = s.slice(j + 1);
  254. if (k === 'plugin') {
  255. v = decodeURIComponent(v);
  256. var k = v.indexOf(';');
  257. if (k !== -1) {
  258. config['plugin'] = v.slice(0, k);
  259. config['plugin_opts'] = v.slice(k + 1);
  260. }
  261. }
  262. }
  263. });
  264. }
  265. return [config, tag];
  266. } else { // Legacy format https://shadowsocks.org/en/config/quick-guide.html
  267. var plain = atob(uri.slice(scheme.length, hashPos)),
  268. firstColonPos = plain.indexOf(':'),
  269. lastColonPos = plain.lastIndexOf(':'),
  270. atPos = plain.lastIndexOf('@', lastColonPos);
  271. if (firstColonPos === -1 ||
  272. lastColonPos === -1 ||
  273. atPos === -1) return null;
  274. var config = {
  275. server: plain.slice(atPos + 1, lastColonPos),
  276. server_port: plain.slice(lastColonPos + 1),
  277. password: plain.slice(firstColonPos + 1, atPos),
  278. method: plain.slice(0, firstColonPos)
  279. };
  280. return [config, tag];
  281. }
  282. }
  283. return null;
  284. }
  285. });