@@ -469,12 +469,13 @@ const socks5Parser = (proxy = {}) => {
469469} ;
470470
471471const shadowTLSParser = ( proxy = { } ) => {
472+ const pluginOpts = getShadowTLSPluginOpts ( proxy ) ;
472473 const ssPart = {
473474 tag : proxy . name ,
474475 type : 'shadowsocks' ,
475476 method : proxy . cipher ,
476477 password : proxy . password ,
477- detour : ` ${ proxy . name } _shadowtls` ,
478+ detour : getShadowTLSTag ( proxy ) ,
478479 } ;
479480 if ( proxy . uot ) ssPart . udp_over_tcp = true ;
480481 if ( proxy [ 'udp-over-tcp' ] ) {
@@ -487,16 +488,59 @@ const shadowTLSParser = (proxy = {}) => {
487488 : 2 ,
488489 } ;
489490 }
491+ networkParser ( proxy , ssPart ) ;
492+ smuxParser ( proxy . smux , ssPart ) ;
493+ return {
494+ type : 'ss-with-st' ,
495+ ssPart,
496+ stPart : shadowTLSOutboundParser ( proxy , pluginOpts ) ,
497+ } ;
498+ } ;
499+
500+ const getShadowTLSTag = ( proxy = { } ) => `${ proxy . name } _shadowtls` ;
501+
502+ const getShadowTLSPluginOpts = ( proxy = { } ) => {
503+ if ( proxy . plugin === 'shadow-tls' && proxy [ 'plugin-opts' ] ) {
504+ return proxy [ 'plugin-opts' ] ;
505+ }
506+ if (
507+ proxy [ 'shadow-tls-password' ] != null ||
508+ proxy [ 'shadow-tls-sni' ] != null ||
509+ proxy [ 'shadow-tls-version' ] != null
510+ ) {
511+ return {
512+ host : proxy [ 'shadow-tls-sni' ] ,
513+ password : proxy [ 'shadow-tls-password' ] ,
514+ version : proxy [ 'shadow-tls-version' ] ,
515+ } ;
516+ }
517+ return undefined ;
518+ } ;
519+
520+ const normalizeALPN = ( alpn ) => {
521+ if ( typeof alpn === 'string' ) {
522+ return alpn
523+ . split ( ',' )
524+ . map ( ( item ) => item . trim ( ) )
525+ . filter ( ( item ) => item !== '' ) ;
526+ }
527+ if ( Array . isArray ( alpn ) ) return alpn ;
528+ return undefined ;
529+ } ;
530+
531+ const shadowTLSOutboundParser = ( proxy = { } , pluginOpts ) => {
532+ if ( ! pluginOpts ) throw new Error ( 'shadow-tls plugin options are missing' ) ;
533+
490534 const stPart = {
491- tag : ` ${ proxy . name } _shadowtls` ,
535+ tag : getShadowTLSTag ( proxy ) ,
492536 type : 'shadowtls' ,
493537 server : proxy . server ,
494538 server_port : parseInt ( `${ proxy . port } ` , 10 ) ,
495- version : proxy [ 'plugin-opts' ] . version ,
496- password : proxy [ 'plugin-opts' ] . password ,
539+ version : pluginOpts . version ,
540+ password : pluginOpts . password ,
497541 tls : {
498542 enabled : true ,
499- server_name : proxy [ 'plugin-opts' ] . host ,
543+ server_name : pluginOpts . host ,
500544 utls : {
501545 enabled : true ,
502546 fingerprint : proxy [ 'client-fingerprint' ] ,
@@ -505,15 +549,16 @@ const shadowTLSParser = (proxy = {}) => {
505549 } ;
506550 if ( stPart . server_port < 0 || stPart . server_port > 65535 )
507551 throw '端口值非法' ;
552+ const alpn = normalizeALPN ( pluginOpts . alpn ) ;
553+ if ( alpn ) stPart . tls . alpn = alpn ;
508554 if ( proxy [ 'fast-open' ] === true ) stPart . udp_fragment = true ;
509555 tfoParser ( proxy , stPart ) ;
510556 detourParser ( proxy , stPart ) ;
511- networkParser ( proxy , ssPart ) ;
512- smuxParser ( proxy . smux , ssPart ) ;
513557 ipVersionParser ( proxy , stPart ) ;
514558 domainResolverParser ( proxy , stPart ) ;
515- return { type : 'ss-with-st' , ssPart , stPart } ;
559+ return stPart ;
516560} ;
561+
517562const ssParser = ( proxy = { } ) => {
518563 const parsedProxy = {
519564 tag : proxy . name ,
@@ -631,6 +676,56 @@ const ssrParser = (proxy = {}) => {
631676 return parsedProxy ;
632677} ;
633678
679+ const getSnellVersion = ( version ) => {
680+ if ( version == null ) return undefined ;
681+ const normalized = `${ version } ` . trim ( ) ;
682+ if ( ! / ^ \d + $ / . test ( normalized ) ) return NaN ;
683+ return parseInt ( normalized , 10 ) ;
684+ } ;
685+
686+ const snellParser = ( proxy = { } ) => {
687+ const version = getSnellVersion ( proxy . version ) ;
688+ const shadowTLSPluginOpts = getShadowTLSPluginOpts ( proxy ) ;
689+ if (
690+ version != null &&
691+ ( ! [ 1 , 2 , 3 , 4 , 5 ] . includes ( version ) || Number . isNaN ( version ) )
692+ ) {
693+ throw new Error (
694+ `Platform sing-box does not support snell version ${ proxy . version } ` ,
695+ ) ;
696+ }
697+
698+ const parsedProxy = {
699+ tag : proxy . name ,
700+ type : 'snell' ,
701+ server : proxy . server ,
702+ server_port : parseInt ( `${ proxy . port } ` , 10 ) ,
703+ psk : proxy . psk ,
704+ } ;
705+ if ( parsedProxy . server_port < 0 || parsedProxy . server_port > 65535 )
706+ throw 'invalid port' ;
707+ if ( version != null ) parsedProxy . version = version ;
708+ if ( proxy [ 'obfs-opts' ] ?. mode )
709+ parsedProxy . obfs_mode = proxy [ 'obfs-opts' ] . mode ;
710+ if ( proxy [ 'obfs-opts' ] ?. host )
711+ parsedProxy . obfs_host = proxy [ 'obfs-opts' ] . host ;
712+ if ( proxy . reuse && ( version == null || version >= 4 ) )
713+ parsedProxy . reuse = true ;
714+ networkParser ( proxy , parsedProxy ) ;
715+ if ( shadowTLSPluginOpts ) {
716+ parsedProxy . detour = getShadowTLSTag ( proxy ) ;
717+ delete parsedProxy . server ;
718+ delete parsedProxy . server_port ;
719+ } else {
720+ if ( proxy [ 'fast-open' ] ) parsedProxy . udp_fragment = true ;
721+ tfoParser ( proxy , parsedProxy ) ;
722+ detourParser ( proxy , parsedProxy ) ;
723+ ipVersionParser ( proxy , parsedProxy ) ;
724+ domainResolverParser ( proxy , parsedProxy ) ;
725+ }
726+ return parsedProxy ;
727+ } ;
728+
634729const vmessParser = ( proxy = { } ) => {
635730 const parsedProxy = {
636731 tag : proxy . name ,
@@ -1145,6 +1240,25 @@ export default function singbox_Producer() {
11451240 ) ;
11461241 }
11471242 break ;
1243+ case 'snell' :
1244+ if ( opts [ 'include-unsupported-proxy' ] ) {
1245+ list . push ( snellParser ( proxy ) ) ;
1246+ const shadowTLSPluginOpts =
1247+ getShadowTLSPluginOpts ( proxy ) ;
1248+ if ( shadowTLSPluginOpts ) {
1249+ list . push (
1250+ shadowTLSOutboundParser (
1251+ proxy ,
1252+ shadowTLSPluginOpts ,
1253+ ) ,
1254+ ) ;
1255+ }
1256+ } else {
1257+ throw new Error (
1258+ `Platform sing-box does not support proxy type: ${ proxy . type } ` ,
1259+ ) ;
1260+ }
1261+ break ;
11481262 case 'vmess' :
11491263 if (
11501264 ! proxy . network ||
0 commit comments