编译mipsle版lantern

使用的环境为 腾讯云 Ubuntu Server 16.04.1 LTS 32位

  1. 安装依赖包

    sudo apt-get update
    sudo apt-get install git gcc libgtk2.0-dev
    sudo apt-get install gcc-mips-linux-gnu g++-mips-linux-gnu gcc-arm-linux-gnueabi g++-arm-linux-gnueabi gcc-mipsel-linux-gnu g++-mipsel-linux-gnu
    sudo apt-get install npm nodejs-legacy
    sudo apt-get install axel

  1. 由于lantern使用的golang是lantern修改过的golang, 所以我们需要将golang官方的版本修改成可以兼容lantern的版本.

    wget https://coding.net/u/ilanyu/p/lantern/git/raw/master/lantern_linux_386_server.tar.gz
    tar -zxvf lantern_linux_386_server.tar.gz
    ./lantern_linux_386_server -proxyall &> lantern.log &
    export http_proxy=http://127.0.0.1:8787
    export https_proxy=http://127.0.0.1:8787
    axel -n 50 https://storage.googleapis.com/golang/go1.8.linux-386.tar.gz
    sudo tar -C /usr/local -xzf go1.8.linux-386.tar.gz
    export PATH=$PATH:/usr/local/go/bin
    axel -n 50 https://storage.googleapis.com/golang/go1.8.src.tar.gz
    tar -xvf go1.8.src.tar.gz
    cd ./go/src
    

    修改 ~/go/src/net/http/server.go~/go/src/net/http/transport.go

    server.go中

    第291行开始

    if req.ProtoAtLeast(1, 1) && (!haveHost || len(hosts) == 0) && !isH2Upgrade {
        return nil, badRequestError("missing required Host header")
    }
    if len(hosts) > 1 {
        return nil, badRequestError("too many Host headers")
    }
    if len(hosts) == 1 && !httplex.ValidHostHeader(hosts[0]) {
        return nil, badRequestError("malformed Host header")
    }
    for k, vv := range req.Header {
        if !httplex.ValidHeaderFieldName(k) {
            return nil, badRequestError("invalid header name")
        }
        for _, v := range vv {
            if !httplex.ValidHeaderFieldValue(v) {
                return nil, badRequestError("invalid header value")
            }
        }
    }
    

    修改为

    if !c.server.AcceptAnyHostHeader {
        if req.ProtoAtLeast(1, 1) && (!haveHost || len(hosts) == 0) && !isH2Upgrade {
            return nil, badRequestError("missing required Host header")
        }
        if len(hosts) > 1 {
            return nil, badRequestError("too many Host headers")
        }
        if len(hosts) == 1 && !httplex.ValidHostHeader(hosts[0]) {
            return nil, badRequestError("malformed Host header")
        }
        for k, vv := range req.Header {
            if !httplex.ValidHeaderFieldName(k) {
                return nil, badRequestError("invalid header name")
            }
            for _, v := range vv {
                if !httplex.ValidHeaderFieldValue(v) {
                    return nil, badRequestError("invalid header value")
                }
            }
        }
    }
    

    第 2372 行

    // standard logger.
    ErrorLog *log.Logger
    
    disableKeepAlives int32     // accessed atomically.
    

    修改为

    // standard logger.
    ErrorLog *log.Logger
    
    // AcceptAnyHostHeader, if set to true, forces the server to skip validation
    // of host headers.
    AcceptAnyHostHeader bool
    
    disableKeepAlives int32     // accessed atomically.
    

    transport.go中

    第145行

    // MaxIdleConnsPerHost, if non-zero, controls the maximum idle
    // (keep-alive) connections to keep per-host. If zero,
    // DefaultMaxIdleConnsPerHost is used.
    MaxIdleConnsPerHost int
    
    // IdleConnTimeout is the maximum amount of time an idle
    // (keep-alive) connection will remain idle before closing
    // itself.
    // Zero means no limit.
    IdleConnTimeout time.Duration
    

    修改为

    // MaxIdleConnsPerHost, if non-zero, controls the maximum idle
    // (keep-alive) connections to keep per-host. If zero,
    // DefaultMaxIdleConnsPerHost is used.
    MaxIdleConnsPerHost int
    
    // MaxIdleTime, if non-zero, controls how long to hold on to keep-alive
    // connections. Once a connection hits its MaxIdleTime, it will be closed.
    // You need to call EnforceMaxIdleTime() at least once for this to be
    // enforced on an ongoing basis.
    MaxIdleTime time.Duration
    
    // IdleConnTimeout is the maximum amount of time an idle
    // (keep-alive) connection will remain idle before closing
    // itself.
    // Zero means no limit.
    IdleConnTimeout time.Duration
    

    第485行

    // CloseIdleConnections closes any connections which were previously
    // connected from previous requests but are now sitting idle in
    // a "keep-alive" state. It does not interrupt any connections currently
    // in use.
    func (t *Transport) CloseIdleConnections() {
        t.nextProtoOnce.Do(t.onceSetNextProtoDefaults)
        t.idleMu.Lock()
        m := t.idleConn
        t.idleConn = nil
        t.idleConnCh = nil
        t.wantIdle = true
        t.idleLRU = connLRU{}
        t.idleMu.Unlock()
        for _, conns := range m {
            for _, pconn := range conns {
                pconn.close(errCloseIdleConns)
            }
        }
        if t2 := t.h2transport; t2 != nil {
            t2.CloseIdleConnections()
        }
    }
    
    // CancelRequest cancels an in-flight request by closing its connection.
    // CancelRequest should only be called after RoundTrip has returned.
    //
    // Deprecated: Use Request.WithContext to create a request with a
    // cancelable context instead. CancelRequest cannot cancel HTTP/2
    // requests.
    func (t *Transport) CancelRequest(req *Request) {
        t.cancelRequest(req, errRequestCanceled)
    }
    

    修改为

    // CloseIdleConnections closes any connections which were previously
    // connected from previous requests but are now sitting idle in
    // a "keep-alive" state. It does not interrupt any connections currently
    // in use.
    func (t *Transport) CloseIdleConnections() {
        t.nextProtoOnce.Do(t.onceSetNextProtoDefaults)
        t.idleMu.Lock()
        m := t.idleConn
        t.idleConn = nil
        t.idleConnCh = nil
        t.wantIdle = true
        t.idleLRU = connLRU{}
        t.idleMu.Unlock()
        for _, conns := range m {
            for _, pconn := range conns {
                pconn.close(errCloseIdleConns)
            }
        }
        if t2 := t.h2transport; t2 != nil {
            t2.CloseIdleConnections()
        }
    }
    
    func (t *Transport) EnforceMaxIdleTime() {
        if t.MaxIdleTime == 0 {
            // Nothing to maintain
            return
        }
        t.maintainIdleOnce.Do(func() {
            go func() {
                for {
                    time.Sleep(t.MaxIdleTime / 10)
                    now := time.Now()
                    toClose := make([]*persistConn, 0)
                    t.idleMu.Lock()
                    for key, conns := range t.idleConn {
                        retainedConns := make([]*persistConn, 0, len(conns))
                        for _, pconn := range conns {
                            if now.Sub(pconn.lastIdled) > t.MaxIdleTime {
                                toClose = append(toClose, pconn)
                            } else {
                                retainedConns = append(retainedConns, pconn)
                            }
                        }
                        if len(retainedConns) == 0 {
                            delete(t.idleConn, key)
                        } else {
                            t.idleConn[key] = retainedConns
                        }
                    }
                    t.idleMu.Unlock()
    
                    // Close connections outside of mutex to minimize locking time
                    for _, pconn := range toClose {
                        pconn.close(errCloseIdleConns)
                    }
                }
            }()
        })
    }
    
    // CancelRequest cancels an in-flight request by closing its connection.
    // CancelRequest should only be called after RoundTrip has returned.
    //
    // Deprecated: Use Request.WithContext to create a request with a
    // cancelable context instead. CancelRequest cannot cancel HTTP/2
    // requests.
    func (t *Transport) CancelRequest(req *Request) {
        t.cancelRequest(req, errRequestCanceled)
    }
    

    第696行

    for _, exist := range idles {
        if exist == pconn {
            log.Fatalf("dup idle pconn %p in freelist", pconn)
        }
    }
    t.idleConn[key] = append(idles, pconn)
    t.idleLRU.add(pconn)
    

    修改为

    for _, exist := range idles {
        if exist == pconn {
            log.Fatalf("dup idle pconn %p in freelist", pconn)
        }
    }
    pconn.lastIdled = time.Now()
    t.idleConn[key] = append(idles, pconn)
    t.idleLRU.add(pconn)
    

    第1296行

        // mutateHeaderFunc is an optional func to modify extra
        // headers on each outbound request before it's written. (the
        // original Request given to RoundTrip is not modified)
        mutateHeaderFunc func(Header)
    }
    

    修改为

        // mutateHeaderFunc is an optional func to modify extra
        // headers on each outbound request before it's written. (the
        // original Request given to RoundTrip is not modified)
        mutateHeaderFunc func(Header)
    
        // when this connection was last idled
        lastIdled time.Time
    }
    

    源码修改完成
    下面编译修改后的golang

    GOROOT_BOOTSTRAP=/usr/local/go ./make.bash
    

    将:/usr/local/go/bin从PATH中去除

    echo $PATH
    

    得到结果

    # /home/ubuntu/bin:/home/ubuntu/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/usr/local/go/bin

    将最后的 /usr/local/go/bin 修改为 /home/ubuntu/go/bin

    export PATH=/home/ubuntu/bin:/home/ubuntu/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/home/ubuntu/go/bin
    
  2. 编译 lantern

    unset http_proxy
    unset https_proxy
    git clone --depth=1 https://github.com/getlantern/lantern.git
    cd lantern
    sudo npm config set registry https://registry.npm.taobao.org
    sudo npm i gulp-cli -g
    cp /home/ubuntu/lantern/src/github.com/getlantern/pac/pac_bytes_linux_386.go /home/ubuntu/lantern/src/github.com/getlantern/pac/pac_bytes_linux_mips.go
    cp /home/ubuntu/lantern/src/github.com/getlantern/pac/pac_bytes_linux_386.go /home/ubuntu/lantern/src/github.com/getlantern/pac/pac_bytes_linux_mipsle.go
    export VERSION=9.9.9
    

    由于lantern使用了golang中部分在mipsle未实现的功能(主要是获取用户主目录的时候使用了user.Current()), 因此需要修改这部分代码.

    ~/lantern/src/github.com/getlantern/appdir/appdir.go

    func InHomeDir(filename string) string {
        usr, err := user.Current()
        if err != nil {
            panic(fmt.Errorf("Unable to determine user's home directory: %s", err))
        }
        return filepath.Join(usr.HomeDir, filename)
    }
    

    修改为

    func InHomeDir(filename string) string {
        usr, err := user.Current()
        if err != nil {
            log.Printf("[github.com/getlantern/appdir/appdir.go] Unable to determine user's home directory: %s, try $HOME as user's home directory", err)
            return filepath.Join(os.Getenv("HOME"), filename)
        }
        return filepath.Join(usr.HomeDir, filename)
    }
    

    ~/lantern/src/github.com/getlantern/byteexec/byteexec.go

    func inHomeDir(filename string) (string, error) {
        log.Tracef("Determining user's home directory")
        usr, err := user.Current()
        if err != nil {
            return "", fmt.Errorf("Unable to determine user's home directory: %s", err)
        }
        return filepath.Join(usr.HomeDir, filename), nil
    }
    

    修改为

    func inHomeDir(filename string) (string, error) {
        log.Tracef("Determining user's home directory")
        usr, err := user.Current()
        if err != nil {
            return filepath.Join(os.Getenv("HOME"), filename), nil
        }
        return filepath.Join(usr.HomeDir, filename), nil
    }
    

    ~/lantern/src/github.com/getlantern/keyman/keyman_trust_linux.go

    func getUserNssdb() (string, error) {
        // get the user's home dir
        usr, err := user.Current()
        if err != nil {
            return "", fmt.Errorf("Unable to get current user: %s", err)
        }
        return "sql:" + usr.HomeDir + "/.pki/nssdb", nil
    }
    

    修改为

    func getUserNssdb() (string, error) {
        // get the user's home dir
        usr, err := user.Current()
        if err != nil {
            return "sql:" + os.Getenv("HOME") + "/.pki/nssdb", nil
        }
        return "sql:" + usr.HomeDir + "/.pki/nssdb", nil
    }
    

    Makefile

    第25行

    GIT_REVISION := $(shell git describe --abbrev=0 --tags --exact-match 2> /dev/null || git rev-parse --short HEAD)
    GIT_REVISION_DATE := $(shell git show -s --format=%ci $(GIT_REVISION_SHORTCODE))
    

    修改为

    GIT_REVISION := 9.9.9
    GIT_REVISION_DATE := 2018-11-15 00:54:06 -0800
    

    第249行

    linux-arm: $(RESOURCES_DOT_GO) $(SOURCES)
        @source setenv.bash && \
        HEADLESS=1 && \
        $(call build-tags) && \
        CGO_ENABLED=1 CC=arm-linux-gnueabi-gcc CXX=arm-linux-gnueabi-g++ CGO_ENABLED=1 GOOS=linux GOARCH=arm GOARM=7 go build -a -o lantern_linux_arm -tags="$$BUILD_TAGS" -ldflags="$(LDFLAGS) $$EXTRA_LDFLAGS -linkmode internal -extldflags \"-static\"" github.com/getlantern/flashlight/main
    
    windows: $(RESOURCES_DOT_GO) $(SOURCES)
        @source setenv.bash && \
        $(call build-tags) && \
        CGO_ENABLED=1 GOOS=windows GOARCH=386 go build -a -o lantern_windows_386.exe -tags="$$BUILD_TAGS" -ldflags="$(LDFLAGS) $$EXTRA_LDFLAGS -H=windowsgui" github.com/getlantern/flashlight/main;
    

    修改为

    linux-arm: $(RESOURCES_DOT_GO) $(SOURCES)
        @source setenv.bash && \
        HEADLESS=1 && \
        $(call build-tags) && \
        CGO_ENABLED=1 CC=arm-linux-gnueabi-gcc CXX=arm-linux-gnueabi-g++ CGO_ENABLED=1 GOOS=linux GOARCH=arm GOARM=7 go build -a -o lantern_linux_arm -tags="$$BUILD_TAGS" -ldflags="$(LDFLAGS) $$EXTRA_LDFLAGS -linkmode internal -extldflags \"-static\"" github.com/getlantern/flashlight/main
    
    linux-mipsle: $(RESOURCES_DOT_GO) $(SOURCES)
        @source setenv.bash && \
        HEADLESS=1 && \
        $(call build-tags) && \
        CGO_ENABLED=1 CC=mipsel-linux-gnu-gcc CXX=mipsel-linux-gnu-g++ CGO_ENABLED=1 GOOS=linux GOARCH=mipsle go build -a -o lantern_linux_mipsle -tags="$$BUILD_TAGS" -ldflags="$(LDFLAGS) $$EXTRA_LDFLAGS -linkmode external -extldflags \"-static\"" github.com/getlantern/flashlight/main
    
    windows: $(RESOURCES_DOT_GO) $(SOURCES)
        @source setenv.bash && \
        $(call build-tags) && \
        CGO_ENABLED=1 GOOS=windows GOARCH=386 go build -a -o lantern_windows_386.exe -tags="$$BUILD_TAGS" -ldflags="$(LDFLAGS) $$EXTRA_LDFLAGS -H=windowsgui" github.com/getlantern/flashlight/main;
    

    lantern 修改完成

    cd /home/ubuntu/lantern/
    make linux-mipsle
    

    得到 lantern_linux_mipsle, 大小有15M左右, 可以使用upx压缩,压缩后大小在5M左右.
    注: 编译后得到的mipsle版lantern无法使用pac功能.

  3. 在路由器上运行

    我这里使用的路由器为 小米路由mini, 使用的系统为在 http://www.right.com.cn/forum/thread-161324-1-1.html 下载的Padavan固件, 插入一个8G大的U盘, 卷标为LY.

    mkdir /media/LY/temp
    dd if=/dev/zero of=/media/LY/temp/swapfile count=200000
    chmod 777 /media/LY/temp/swapfile
    mkswap /media/LY/temp/swapfile
    swapon /media/LY/temp/swapfile
    chmod +x /media/LY/temp/lantern_linux_mipsle
    ./lantern_linux_mipsle -proxyall &> ./lantern.log &
    

    lantern运行成功, 可使用netstat -lntp查看是否8787和8788端口被lantern占用

运行截图

标签: none

已有 2 条评论

  1. hzexe hzexe

    伸手党来了. 这些涉及fix有没有patch或者编译完成的lantern....我CPU和你的一样子的.

    1. @hzexe
      https://github.com/ilanyu/lantern/releases/download/4.5.9-bin/lantern_linux_mipsle

评论已关闭