2018-10-09 15:07:54 +00:00
package main
import (
2018-11-22 21:55:23 +00:00
"bytes"
2019-01-12 15:51:20 +00:00
"encoding/base64"
2018-10-09 15:07:54 +00:00
"flag"
"fmt"
"io"
"log"
"net"
"os"
"strings"
"time"
mux "github.com/cbeuw/Cloak/internal/multiplex"
"github.com/cbeuw/Cloak/internal/server"
2018-12-11 23:26:05 +00:00
"github.com/cbeuw/Cloak/internal/server/usermanager"
2018-10-09 15:07:54 +00:00
"github.com/cbeuw/Cloak/internal/util"
)
var version string
func pipe ( dst io . ReadWriteCloser , src io . ReadWriteCloser ) {
2019-01-13 21:28:57 +00:00
// The maximum size of TLS message will be 16396+12. 12 because of the stream header
2018-10-20 20:41:01 +00:00
// 16408 is the max TLS message size on Firefox
buf := make ( [ ] byte , 16396 )
2018-10-09 15:07:54 +00:00
for {
2018-10-20 10:35:50 +00:00
i , err := io . ReadAtLeast ( src , buf , 1 )
2019-01-12 17:06:14 +00:00
if err != nil {
2018-10-20 10:35:50 +00:00
go dst . Close ( )
go src . Close ( )
return
}
i , err = dst . Write ( buf [ : i ] )
2019-01-12 17:06:14 +00:00
if err != nil {
2018-10-09 15:07:54 +00:00
go dst . Close ( )
go src . Close ( )
return
}
}
}
func dispatchConnection ( conn net . Conn , sta * server . State ) {
goWeb := func ( data [ ] byte ) {
webConn , err := net . Dial ( "tcp" , sta . WebServerAddr )
if err != nil {
log . Printf ( "Making connection to redirection server: %v\n" , err )
return
}
webConn . Write ( data )
go pipe ( webConn , conn )
go pipe ( conn , webConn )
}
buf := make ( [ ] byte , 1500 )
conn . SetReadDeadline ( time . Now ( ) . Add ( 3 * time . Second ) )
i , err := io . ReadAtLeast ( conn , buf , 1 )
if err != nil {
go conn . Close ( )
return
}
conn . SetReadDeadline ( time . Time { } )
data := buf [ : i ]
ch , err := server . ParseClientHello ( data )
if err != nil {
log . Printf ( "+1 non SS non (or malformed) TLS traffic from %v\n" , conn . RemoteAddr ( ) )
goWeb ( data )
return
}
2018-11-07 21:16:13 +00:00
isSS , UID , sessionID := server . TouchStone ( ch , sta )
2018-10-09 15:07:54 +00:00
if ! isSS {
log . Printf ( "+1 non SS TLS traffic from %v\n" , conn . RemoteAddr ( ) )
goWeb ( data )
return
}
2018-12-11 23:26:05 +00:00
finishHandshake := func ( ) error {
2018-11-22 21:55:23 +00:00
reply := server . ComposeReply ( ch )
_ , err = conn . Write ( reply )
if err != nil {
go conn . Close ( )
2018-12-11 23:26:05 +00:00
return err
2018-11-22 21:55:23 +00:00
}
// Two discarded messages: ChangeCipherSpec and Finished
discardBuf := make ( [ ] byte , 1024 )
for c := 0 ; c < 2 ; c ++ {
_ , err = util . ReadTLS ( conn , discardBuf )
if err != nil {
go conn . Close ( )
2018-12-11 23:26:05 +00:00
return err
2018-11-22 21:55:23 +00:00
}
}
2018-12-11 23:26:05 +00:00
return nil
}
2018-11-22 21:55:23 +00:00
2019-01-12 15:51:20 +00:00
// adminUID can use the server as normal with unlimited QoS credits. The adminUID is not
// added to the userinfo database. The distinction between going into the admin mode
// and normal proxy mode is that sessionID needs == 0 for admin mode
2018-12-11 23:26:05 +00:00
if bytes . Equal ( UID , sta . AdminUID ) && sessionID == 0 {
err = finishHandshake ( )
if err != nil {
log . Println ( err )
return
}
2018-11-22 21:55:23 +00:00
c := sta . Userpanel . MakeController ( sta . AdminUID )
for {
n , err := conn . Read ( data )
if err != nil {
log . Println ( err )
return
}
resp , err := c . HandleRequest ( data [ : n ] )
if err != nil {
2018-12-11 23:26:05 +00:00
log . Println ( err )
2018-11-22 21:55:23 +00:00
}
_ , err = conn . Write ( resp )
if err != nil {
log . Println ( err )
return
}
}
}
2018-12-11 23:26:05 +00:00
var user * usermanager . User
if bytes . Equal ( UID , sta . AdminUID ) {
user , err = sta . Userpanel . GetAndActivateAdminUser ( UID )
} else {
user , err = sta . Userpanel . GetAndActivateUser ( UID )
}
2018-11-07 21:16:13 +00:00
if err != nil {
log . Printf ( "+1 unauthorised user from %v, uid: %x\n" , conn . RemoteAddr ( ) , UID )
goWeb ( data )
2018-11-22 21:55:23 +00:00
return
2018-11-07 21:16:13 +00:00
}
2018-10-09 15:07:54 +00:00
2018-12-11 23:26:05 +00:00
err = finishHandshake ( )
2018-10-09 15:07:54 +00:00
if err != nil {
2018-12-11 23:26:05 +00:00
log . Println ( err )
2018-10-09 15:07:54 +00:00
return
}
2018-12-29 00:54:10 +00:00
sesh , existing , err := user . GetSession ( sessionID , mux . MakeObfs ( UID ) , mux . MakeDeobfs ( UID ) , util . ReadTLS )
if err != nil {
user . DelSession ( sessionID )
log . Println ( err )
return
}
if existing {
2018-11-08 19:47:53 +00:00
sesh . AddConnection ( conn )
return
} else {
2019-01-12 15:51:20 +00:00
log . Printf ( "New session from UID:%v, sessionID:%v\n" , base64 . StdEncoding . EncodeToString ( UID ) , sessionID )
2018-11-08 19:47:53 +00:00
sesh . AddConnection ( conn )
for {
newStream , err := sesh . AcceptStream ( )
if err != nil {
if err == mux . ErrBrokenSession {
2019-01-12 15:51:20 +00:00
log . Printf ( "Session closed for UID:%v, sessionID:%v\n" , base64 . StdEncoding . EncodeToString ( UID ) , sessionID )
2018-11-08 19:47:53 +00:00
user . DelSession ( sessionID )
return
} else {
continue
}
}
ssConn , err := net . Dial ( "tcp" , sta . SS_LOCAL_HOST + ":" + sta . SS_LOCAL_PORT )
if err != nil {
2018-11-23 23:57:35 +00:00
log . Printf ( "Failed to connect to ssserver: %v\n" , err )
2018-11-07 21:16:13 +00:00
continue
2018-10-09 15:07:54 +00:00
}
2018-11-08 19:47:53 +00:00
go pipe ( ssConn , newStream )
go pipe ( newStream , ssConn )
2018-11-07 21:16:13 +00:00
}
}
2018-10-09 15:07:54 +00:00
}
func main ( ) {
// Should be 127.0.0.1 to listen to ss-server on this machine
var localHost string
// server_port in ss config, same as remotePort in plugin mode
var localPort string
// server in ss config, the outbound listening ip
var remoteHost string
// Outbound listening ip, should be 443
var remotePort string
var pluginOpts string
log . SetFlags ( log . LstdFlags | log . Lshortfile )
if os . Getenv ( "SS_LOCAL_HOST" ) != "" {
localHost = os . Getenv ( "SS_LOCAL_HOST" )
localPort = os . Getenv ( "SS_LOCAL_PORT" )
remoteHost = os . Getenv ( "SS_REMOTE_HOST" )
remotePort = os . Getenv ( "SS_REMOTE_PORT" )
pluginOpts = os . Getenv ( "SS_PLUGIN_OPTIONS" )
} else {
2019-03-23 12:57:30 +00:00
localAddr := flag . String ( "r" , "" , "localAddr: the ip:port ss-server is listening on, set in Shadowsocks' configuration. If ss-server is running locally, it should be 127.0.0.1:some port" )
2018-10-09 15:07:54 +00:00
flag . StringVar ( & remoteHost , "s" , "0.0.0.0" , "remoteHost: outbound listing ip, set to 0.0.0.0 to listen to everything" )
flag . StringVar ( & remotePort , "p" , "443" , "remotePort: outbound listing port, should be 443" )
flag . StringVar ( & pluginOpts , "c" , "server.json" , "pluginOpts: path to server.json or options seperated by semicolons" )
askVersion := flag . Bool ( "v" , false , "Print the version number" )
printUsage := flag . Bool ( "h" , false , "Print this message" )
2018-12-17 22:12:38 +00:00
genUID := flag . Bool ( "u" , false , "Generate a UID" )
genKeyPair := flag . Bool ( "k" , false , "Generate a pair of public and private key, output in the format of pubkey,pvkey" )
2019-01-21 11:49:01 +00:00
pprofAddr := flag . String ( "d" , "" , "debug use: ip:port to be listened by pprof profiler" )
2018-10-09 15:07:54 +00:00
flag . Parse ( )
if * askVersion {
fmt . Printf ( "ck-server %s\n" , version )
return
}
if * printUsage {
flag . Usage ( )
return
}
2018-12-17 22:12:38 +00:00
if * genUID {
fmt . Println ( generateUID ( ) )
return
}
if * genKeyPair {
pub , pv := generateKeyPair ( )
fmt . Printf ( "%v,%v" , pub , pv )
return
}
2018-10-09 15:07:54 +00:00
2019-01-21 11:49:01 +00:00
if * pprofAddr != "" {
2019-01-21 21:17:26 +00:00
startPprof ( * pprofAddr )
2019-01-21 11:49:01 +00:00
}
2018-10-09 15:07:54 +00:00
if * localAddr == "" {
log . Fatal ( "Must specify localAddr" )
}
localHost = strings . Split ( * localAddr , ":" ) [ 0 ]
localPort = strings . Split ( * localAddr , ":" ) [ 1 ]
log . Printf ( "Starting standalone mode, listening on %v:%v to ss at %v:%v\n" , remoteHost , remotePort , localHost , localPort )
}
2018-12-30 00:18:50 +00:00
sta , _ := server . InitState ( localHost , localPort , remoteHost , remotePort , time . Now )
2018-11-07 21:16:13 +00:00
2018-10-09 15:07:54 +00:00
err := sta . ParseConfig ( pluginOpts )
if err != nil {
log . Fatalf ( "Configuration file error: %v" , err )
}
2018-12-03 20:44:08 +00:00
if sta . AdminUID == nil {
log . Fatalln ( "AdminUID cannot be empty!" )
}
2018-10-09 15:07:54 +00:00
go sta . UsedRandomCleaner ( )
listen := func ( addr , port string ) {
listener , err := net . Listen ( "tcp" , addr + ":" + port )
log . Println ( "Listening on " + addr + ":" + port )
if err != nil {
log . Fatal ( err )
}
for {
conn , err := listener . Accept ( )
if err != nil {
log . Printf ( "%v" , err )
continue
}
go dispatchConnection ( conn , sta )
}
}
// When listening on an IPv6 and IPv4, SS gives REMOTE_HOST as e.g. ::|0.0.0.0
listeningIP := strings . Split ( sta . SS_REMOTE_HOST , "|" )
for i , ip := range listeningIP {
if net . ParseIP ( ip ) . To4 ( ) == nil {
// IPv6 needs square brackets
ip = "[" + ip + "]"
}
// The last listener must block main() because the program exits on main return.
if i == len ( listeningIP ) - 1 {
listen ( ip , sta . SS_REMOTE_PORT )
} else {
go listen ( ip , sta . SS_REMOTE_PORT )
}
}
}