分类归档: Programming

编程编程编程。。。

不改路由表实现智能选择线路,提升访问国内外网站速度

互联网发明以后,我们可以很容易的去访问世界各地的知识资源。但是受限于网络环境的原因,部分资源我们去访问的时候会很慢,或者访问不了,这时候我们可以通过跳板、隧道、虚拟私有网络等形式去进行访问。在使用虚拟私有网络的过程中,大部分软件会通过修改网关地址将所有数据都通过虚拟私有网络进行传递,然而我们很多时候访问某些特定资源,比如本地资源时并不希望数据通过虚拟私有网络,这怎么办呢?

池建强大哥在这篇博文《VPN – 长城内外,惟余莽莽》里提到了用路由表解决这个问题。可是改路由表这么麻烦的事情,想想都觉得不美。

博主之前一直用的是国外VPS+ssh tunnel方式“连接国外主机”,用proxy-switchysharp自动切换出口,非常方便。

现在博主的VPS搬到国内了,功能没有了,买了个虚拟私有网络。为了继续使用proxy-switchysharp,博主用go写了个简单的本地代理服务器,让请求通过指定的IP地址转发出去从而实现跨地区资源访问的目的,而因为虚拟私有网络关闭了“Send all traffic over VPN connection”,国内资源的访问依然快速。

目前项目托管在github,博主水平很差,欢迎各位改进
项目地址:https://github.com/HessianZ/daisy-proxy

可执行文件下载:
DaisyProxy For Mac
DaisyProxy For Windows 64

Read: 1834

用goproxy实现基于VPN的本地HTTP代理

我最近用VPN的时候觉得有些地方不太好用,比如说用HTTP代理的时候可以用Chrome的proxy-switchysharp插件做自动切换,这样访问国内资源时和访问国外资源时都很快。因此我用goproxy写了几行代码做了个可以指定本地出口IP的http proxy,连VPN时去掉“Send all traffic over VPN connection“选项,然后用proxy-switchysharp自动切换出口。

不过现在程序还有一些问题,我尝试去访问facebook和twitter都失败了,不知道为什么,同样是https,google和stackoverflow都是正常的,求高手指点。

#########

好了,我知道为什么脸书和推推不能访问了,因为DNS墙了。。。解析出来的地址就不对。

不知道有没有办法在go里面指定ResolveTCPAddr的dns服务器,我现在只能在hosts里面加上正确的IP地址来访问。

#########

代码如下:

package main

import (
    "github.com/elazarl/goproxy"
    "log"
    "net"
    "flag"
    "net/http"
)

var (
    listen = flag.String("listen", "localhost:8080", "listen on address")
    ip = flag.String("ip", "", "listen on address")
    verbose = flag.Bool("verbose", false, "verbose output")
)

func main() {
    flag.Parse()

    if *ip == "" {
        log.Fatal("IP address must be speicified")
    }

    proxy := goproxy.NewProxyHttpServer()
    proxy.Verbose = *verbose
    proxy.Tr.Dial = func (network, addr string) (c net.Conn, err error) {
        if network == "tcp" {
            localAddr, err := net.ResolveTCPAddr(network, *ip + ":0");
            if err != nil {
                return nil, err;
            }
            remoteAddr, err := net.ResolveTCPAddr(network, addr);
            if err != nil {
                return nil, err;
            }
            return net.DialTCP(network, localAddr, remoteAddr);
        }

        return net.Dial(network, addr);
    }
    log.Fatal(http.ListenAndServe(*listen, proxy))
}

使用方法

go run proxy.go -ip VPN虚拟网卡的IP地址

启动代理之后就可以像普通http代理一样在浏览器中使用他咯,非常方便。

 

另外看到一个也是基于goproxy的项目功能也挺有意思,主要是为开发者解决切换开发、生产环境麻烦的问题。有时间的话想整合一下这些功能,但是好像作者已经不更新了,而且是发布在bitbucket的。。。

http://rongmayisheng.com/post/goproxy-灵活的反向代理和静态资源代理

Read: 6863

一个Go语言SSH客户端的例子

an example of ssh client in golang

Google搜了好几个例子都用不了,好像是因为ssh库更新了,没办法只好自己摸索了。

这里用的是RSA Private Key来连接,要改成用密码连接也很容易。

参考文档:https://godoc.org/code.google.com/p/go.crypto/ssh

// 运行前得先安装ssh支持库: go get code.google.com/p/go.crypto/ssh

package main

import (
    "code.google.com/p/go.crypto/ssh"
    "io"
    "log"
)

var (
    pemBytes = `-----BEGIN RSA PRIVATE KEY-----
.......
.......
.......
-----END RSA PRIVATE KEY-----`
)

type keychain struct {
    signer ssh.Signer
}

func (k *keychain) Key(i int) (ssh.PublicKey, error) {
    if i != 0 {
        return nil, nil
    }
    pk := k.signer.PublicKey()
    return pk, nil
}

func (k *keychain) Sign(i int, rand io.Reader, data []byte) (sig []byte, err error) {
    return k.signer.Sign(rand, data)
}

func main() {
    signer, err := ssh.ParsePrivateKey([]byte(pemBytes))
    if err != nil {
        panic(err)
    }

    clientKey := &keychain{signer}

    config := &ssh.ClientConfig{
        User: "Hessian",
        Auth: []ssh.ClientAuth{
            ssh.ClientAuthKeyring(clientKey),
        },
    }
    c, err := ssh.Dial("tcp", "YOUR_HOST:22", config)
    if err != nil {
        log.Println("unable to dial remote side:", err)
    }
    defer c.Close()

    // Create a session
    session, err := c.NewSession()
    if err != nil {
        log.Fatalf("unable to create session: %s", err)
    }
    defer session.Close()

    b, err := session.Output("ls -l")
    if err != nil {
        log.Fatalf("failed to execute: %s", err)
    }
    log.Println("Output: ", string(b))

    return
}

Read: 2140

*HACK* 为QT的TCP socket连接绑定本地IP地址

有时候我们会在机器上同时绑定多个IP地址(可能都是在一个网卡上),但是我们却无法指定程序使用哪个IP地址去建立连接。
虽然我之前的另一篇文章《在Windows/Linux下程序指定IP地址》讲过一种hack的办法,但是并不总是那么有效。就比如博主最近遇到的一个QT写的程序,不知道为什么就是bind不了,不过万幸的是我们有源代码。
通过修改QT框架的QNativeSocketEngine的实现我们还是可以bind到我们需要的IP地址的。

源代码文件:src/network/socket/qnativesocketengine.cpp

在大概410行的位置,在 d->socketType = socketType 之前添加以下代码

if (socketType == QAbstractSocket::TcpSocket && protocol == QAbstractSocket::IPv4Protocol) {
        char* bind_addr;
        if (bind_addr = getenv("BIND_ADDR")) {
            QHostAddress bind_hostaddr = QHostAddress(QString::fromLocal8Bit(bind_addr));

            if (!d->nativeBind(bind_hostaddr, 0)) {
                qDebug("BIND %s failed", bind_addr);
            }

            // bind之后状态是QAbstractSocket::BoundState,但是connectToHost要求必须是UnconnectedState
            d->socketState = QAbstractSocket::UnconnectedState;
        }
    }

使用方法

BIND_ADDR="你的IP地址" /path/to/your/program

好吧,用环境变量的办法当然不是那么美型,但是最直接有效。简单就是美啊。。。哈哈

Read: 1587

在Windows/Linux下让程序指定出口IP地址

Windows/Unix* 系统都支持为一个网卡绑定多个IP地址,但是通常操作系统会根据路由表自动选择IP地址,应用程序使用哪个IP地址用户无法主动控制。本文分别讲解在Linux和Windows下为应用程序绑定指定IP地址的方法。

关于Windows如何选择IP地址可以参考这篇文章:《Source IP address selection on a Multi-Homed Windows Computer》

一、 如何让Linux下的程序指定使用的IP地址

英文原文:《BINDING APPLICATIONS TO A SPECIFIC IP》

作者Daniel Ryde采用了LD_PRELOAD进行HACK,为应用程序注入一个动态库bind.so,这个动态库中对bind和connect函数加钩子,程序建立socket连接前绑定指定的本地IP地址。

使用方法:

Hessian:bind/ $ BIND_ADDR="192.168.8.9" LD_PRELOAD=./bind.so YOUR_PROGRAME

程序源码见文末

编译方法:

Hessian:bind/ $ gcc -nostartfiles -fpic -shared bind.c -o bind.so -ldl -D_GNU_SOURCE
Hessian:bind/ $ strip bind.so

二、 如何让Windows下的程序指定使用的IP地址

在Windows实现这个功能要相对麻烦一些,博主没有找到十分简单的办法,找了很久才发现这个高大上的东西——ForceBindIP – Bind any Windows application to a specific interface

程序原理就不翻译了,反正也没源码,不过大体跟linux版本的实现是差不多的,不过这边还多挂了WSA函数的钩子,覆盖的更完全。因为只会注入目标程序,如果网络访问是目标程序fork出去的进程发起的则不会受影响。

ForceBindIP works in two stages – the loader, ForceBindIP.exe will load the target application in a suspended state. It will then inject a DLL (BindIP.dll) which loads WS2_32.DLL into memory and intercepts the bind(), connect(), sendto(), WSAConnect() and WSASendTo() functions, redirecting them to code in the DLL which verifies which interface they will be bound to and if not the one specified, (re)binds the socket. Once the function intercepts are complete, the target application is resumed. Note that some applications with anti-debugger / injection techniques may not work correctly when an injected DLL is present; for the vast majority of applications though this technique should work fine.

作者声明支持的系统版本有:Windows NT/2000/XP/2003.

作者测试过可用的软件: DC++, uTorrent, Quake II, Quake III, Diablo II, StarCraft, Internet Explorer, Mozilla Firefox, Google Earth, Infantry, Real Player, Unreal Tournament 2004 (requires -i), Outlook 2000 (requires -i).

不可用的软件: GetRight (anti-debugger / forking techniques), WinCVS (forks cvs.exe)

博主测试过在Windows7上无法正常工作。搜狗浏览器也不知道是什么原因没有效果。

使用方法:

ForceBindIP 1.2.3.4 c:fullpathtoapp.exe

还可以通过网卡GUID进行绑定,GUID可以从注册表中找到[HKEY_LOCAL_MACHINESYSTEMCurrentControlSetServicesTcpipParametersInterfaces]

ForceBindIP {4FA65F75-7A5F-4BCA-A3A2-59824B2F5CA0} c:pathtoapp.exe

如果遇到程序崩溃或者什么意外情况可以尝试-i参数,这会让ForceBindIP等待目标程序进入它的消息循环后再注入DLL。

ForceBindIP -i 1.2.3.4 c:fullpathtoapp.exe

bind.c

/*
   Copyright (C) 2000  Daniel Ryde

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.
*/

/*
   LD_PRELOAD library to make bind and connect to use a virtual
   IP address as localaddress. Specified via the enviroment
   variable BIND_ADDR.

   Compile on Linux with:
   gcc -nostartfiles -fpic -shared bind.c -o bind.so -ldl -D_GNU_SOURCE


   Example in bash to make inetd only listen to the localhost
   lo interface, thus disabling remote connections and only
   enable to/from localhost:

   BIND_ADDR="127.0.0.1" LD_PRELOAD=./bind.so /sbin/inetd


   Example in bash to use your virtual IP as your outgoing
   sourceaddress for ircII:

   BIND_ADDR="your-virt-ip" LD_PRELOAD=./bind.so ircII

   Note that you have to set up your servers virtual IP first.


   This program was made by Daniel Ryde
   email: daniel@ryde.net
   web:   http://www.ryde.net/

   TODO: I would like to extend it to the accept calls too, like a
   general tcp-wrapper. Also like an junkbuster for web-banners.
   For libc5 you need to replace socklen_t with int.
*/



#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <dlfcn.h>
#include <errno.h>

int (*real_bind)(int, const struct sockaddr *, socklen_t);
int (*real_connect)(int, const struct sockaddr *, socklen_t);

char *bind_addr_env;
unsigned long int bind_addr_saddr;
unsigned long int inaddr_any_saddr;
struct sockaddr_in local_sockaddr_in[] = { 0 };

void _init (void)
{
	const char *err;

	real_bind = dlsym (RTLD_NEXT, "bind");
	if ((err = dlerror ()) != NULL) {
		fprintf (stderr, "dlsym (bind): %sn", err);
	}

	real_connect = dlsym (RTLD_NEXT, "connect");
	if ((err = dlerror ()) != NULL) {
		fprintf (stderr, "dlsym (connect): %sn", err);
	}

	inaddr_any_saddr = htonl (INADDR_ANY);
	if (bind_addr_env = getenv ("BIND_ADDR")) {
		bind_addr_saddr = inet_addr (bind_addr_env);
		local_sockaddr_in->sin_family = AF_INET;
		local_sockaddr_in->sin_addr.s_addr = bind_addr_saddr;
		local_sockaddr_in->sin_port = htons (0);
	}
}

int bind (int fd, const struct sockaddr *sk, socklen_t sl)
{
	static struct sockaddr_in *lsk_in;

	lsk_in = (struct sockaddr_in *)sk;
/*	printf("bind: %d %s:%dn", fd, inet_ntoa (lsk_in->sin_addr.s_addr),
		ntohs (lsk_in->sin_port));*/
        if ((lsk_in->sin_family == AF_INET)
		&& (lsk_in->sin_addr.s_addr == inaddr_any_saddr)
		&& (bind_addr_env)) {
		lsk_in->sin_addr.s_addr = bind_addr_saddr;
	}
	return real_bind (fd, sk, sl);
}

int connect (int fd, const struct sockaddr *sk, socklen_t sl)
{
	static struct sockaddr_in *rsk_in;
	
	rsk_in = (struct sockaddr_in *)sk;
/*	printf("connect: %d %s:%dn", fd, inet_ntoa (rsk_in->sin_addr.s_addr),
		ntohs (rsk_in->sin_port));*/
        if ((rsk_in->sin_family == AF_INET)
		&& (bind_addr_env)) {
		real_bind (fd, (struct sockaddr *)local_sockaddr_in, sizeof (struct sockaddr));
	}
	return real_connect (fd, sk, sl);
}

Read: 3085