使用ssh远程端口转发使内网对外提供服务

有时候我们的处在内网的开发机器需要让别人访问,如果对方跟我们处于同一局域网可以直接通过ip访问,但有时我们需要外网也能访问的到,有两种解决办法。

第一种是在我们的局域网的路由器上做一个端口转发。但需要我们有操作路由器的权限。

第二种是使用一台拥有外网ip的主机做远程端口转发。具体如下:

拥有外网ip的主机上运行sshd。并且配置文件中的GatewayPorts为yes。然后在内网开发机器上执行ssh -R *:1234:127.0.0.1:8888 root@外网ip主机,如果开发机是windows,使用plink。-R的意思是在远程主机上绑定1234端口,当这个端口接收到数据后,会通过ssh tunnel传给本地,本地再把数据转发给127.0.0.1的8888端口。当本地收到127.0.0.1的8888端口数据后再依次转发给远程主机,远程主机返回给访问者。

如果想放到后台远程,ssh使用-f -N参数。

我在合肥的时候,使用小区宽带,发现分配的ip并不是公网ip。没法做路由器端口映射。当时并没有想到这个办法。今天测试一下可行。

CA证书过期问题

前几天遇到一个https协议的web service证书过期问题。wget https://webservice得到如下错误:

ERROR: cannot verify www.mk-style.com’s certificate, issued by `/C=BE/O=GlobalSign nv-sa/CN=GlobalSign Organization Validation CA – G2′:
Issued certificate has expired.

刚开始以为是网站证书过期了。后来才发现是CA证书过期了。

也不知道CA的证书是放在了哪里。怀疑跟openssl有关。就man openssl。看到ca这个命令用于CA的管理。继续openssl ca,看到了:
Using configuration from /etc/pki/tls/openssl.cnf
继续查看openssl.cnf,看到了
certs = $dir/certs # Where the issued certs are kept
最终找到/etc/pki/tls/certs/ca-bundle.crt,
它就是存放CA信息的文件。
在这个文件中查找GlobalSign,看到了:
Validity
Not Before: Sep 1 12:00:00 1998 GMT
Not After : Jan 28 12:00:00 2014 GMT
果然2014年1月28号它就过期了。

解决办法:
升级openssl:yum update openssl

yum,wget,rpm使用代理访问网络

今天遇到一台机器,因为安全的原因不允许直接访问互联网。但这台机器的php要安装ssh2扩展。所以我借助代理来访问网络。

首先在另一台机器上安装squid:
yum install squid -y
然后编辑/etc/squid/squid.conf把http_access deny all改为http_access allow all。
然后启动squid:/etc/init.d/squid start

因为要ssh2扩展依赖libssh2,这个可以使用yum来安装。但在epel源里。所以先安装epel源。
rpm -Uvh –httpproxy 172.16.0.93 –httpport 3128 http://download.fedoraproject.org/pub/epel/5/i386/epel-release-5-4.noarch.rpm
但,这一步不成功。我觉得是rpm解析download.fedoraproject.org它对应的IP,使用的是/etc/resolv.conf里配置dns,这里面配置的dns是外网的IP。访问不了。
然后
wget -e http_proxy=http://172.16.0.93:3128 http://download.fedoraproject.org/pub/epel/5/i386/epel-release-5-4.noarch.rpm
rpm -Uvh ./epel-release-5-4.noarch.rpm

yum的代理是在yum.conf里面配置。man yum.conf查详细。
yum install libssh2 -y

然后就是编译安装ssh2扩展了。

wget和yum是使用代理来远程解析域名的么?我查了半天没有找到。

补充:我犯2了。使用http代理,本地不做解析,就是简单的把http请求发给代理。代理解析域名然后获取数据,返回数据。
之所以rpm -Uvh –httpproxy 172.16.0.93 –httpport 3128 http://download.fedoraproject.org/pub/epel/5/i386/epel-release-5-4.noarch.rpm不成功是因为这个url返回的是302,rpm不自动跟随。而wget会自动跟随的。

php写的一个根据片假名写出平假名的小程序

地址

<?php
$str2 = 'あいうえおかきくけこさしすせそたちつてとなにぬねのはひふへほまみむめもやいゆえよらいるれろわいうえをん';

$str = 'アイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤイユエヨラリルレロワイウエヲン';

$index = mt_rand(0, mb_strlen($str, 'UTF-8') - 1); 

header('Content-Type: text/html; charset=UTF-8');
?>

<div style="text-align: center;">
    <form action="" method="post">
        <input type="hidden"  name="hidden" value="<?php echo $index?>"/>
        <?php echo mb_substr($str, $index, 1, 'UTF-8');?>
        <br />
        <input type="text" name="input" autofocus="autofocus"/>
        <br />
        <input type="submit" value="submit">
        <br />
        <?php 
            if(isset($_POST['input']))
            {   
                $q = mb_substr( $str, intval($_POST['hidden']), 1, 'UTF-8' );
                $a = mb_substr( $str2, intval($_POST['hidden']), 1, 'UTF-8' );
                $yours = $_POST['input'];
    
                echo 'question is ', $q, ' and answer is ', $a, '.<br /> your answer is ', $yours, '<br />';
                if($a === $yours)
                    echo '<span style="color:green; font-size:20px;">you are right.</span>';
                else
                    echo '<span style="color:red; font-size:64px;">you are wrong.</span>';
            }   
        ?>  
    </form>
</div>

centos安装最新openssh-client

今天遇到个问题,要使用sftp上传文件夹,在我的开发机fedora19上是没有问题的,因为put命令有-r参数,可以递归处理.但在centos6.2的服务器上,sftp的put命令是没有-r参数的,以下是在centos6.2上编译安装openssh-client最新版过程:

这里选择一个合适的下载镜像下载源码并解压之:

cd /usr/local/src

wget http://mirror.team-cymru.org/pub/OpenBSD/OpenSSH/portable/openssh-6.2p2.tar.gz

tar zxvf openssh-6.2p2.tar.gz

cd openssh-6.2p2

查看安装帮助文件

less ./INSTALL

里面提到了依赖与编译安装步骤

./configure –prefix=/usr/local/openssh-client

make && make install

安装成功后

sftp root@ip

help

看到put有-r参数了.

I/O复用之使用select完成端口转发功能

参考:socket select用法:100行代码实现网络数据转发程序

这个是linux版本,使用举例:
在本地使用nc监听8080端口:
nc -l 8080
使用本程序把本地的9090端口转发到本地的8080端口,
./portMap 127.0.0.1 9090 127.0.0.1 8080
再telnet 127.0.0.1 9090,发现nc监听的端口有数据进来。

代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <unistd.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <netinet/in.h>
#include <fcntl.h>

#define CLIENT_COUNT 100

int clients[CLIENT_COUNT] = {0};
int remotes[CLIENT_COUNT] = {0};

static void remove_client(int i)
{
	shutdown(clients[i], SHUT_RD);
	close(clients[i]);
	shutdown(remotes[i], SHUT_RD);
	close(remotes[i]);
	
	clients[i] = remotes[i] = 0;
}

static int get_client()
{
	int i;
	for(i=0; i<CLIENT_COUNT; i++)
	{
		if(!clients[i])
			return i;
	}
	
	return 0;
}


int main(int argc, char* argv[])
{
	if(argc < 5)
	{
		printf("usage: %s local_ip local_port remote_ip remote_port.\n", basename(argv[0]));
		return 1;
	}

	char* local_ip = argv[1];
	int local_port = atoi(argv[2]);
	char* remote_ip = argv[3];
	int remote_port = atoi(argv[4]);

	struct sockaddr_in address;
	bzero(&address, sizeof(address));	
	address.sin_family = AF_INET;
	inet_pton(AF_INET, local_ip, &address.sin_addr);
	address.sin_port = htons(local_port);

	struct sockaddr_in remote_address;
	bzero(&remote_address, sizeof(remote_address));
	remote_address.sin_family = AF_INET;
	inet_pton(AF_INET, remote_ip, &remote_address.sin_addr);
	remote_address.sin_port = htons(remote_port);
	
	int listenfd = socket(PF_INET, SOCK_STREAM, 0);
	assert(listenfd >= 0);

	int ret = bind(listenfd, (struct sockaddr*)&address, sizeof(address));
	assert(ret != -1);

	ret = listen(listenfd, 5);
	assert(ret != -1);

	printf("listen\n");

	fd_set fdreads,fdwrites;
	const int buf_size = 1024 * 32;
	char buf[buf_size];
	
	FD_ZERO(&fdreads);

	for(;;)
	{
		int i,j,k;
		FD_SET(listenfd, &fdreads);

		for(i=0; i<CLIENT_COUNT; i++)
		{
			if(clients[i])
			{
				FD_SET(clients[i], &fdreads);
				FD_SET(remotes[i], &fdreads);
			}
		}
		
		ret = select(CLIENT_COUNT * 2 + 1, &fdreads, NULL, NULL, NULL);

		printf("select ret=%d\n", ret);

		if(ret < 0)
		{
			printf("selecttion failure\n");
			break;
		}

		if(FD_ISSET(listenfd, &fdreads))
		{
			int j = get_client();
			printf("accept %d\n", j);
			
			struct sockaddr client_address;
			socklen_t client_addrlength = sizeof(client_address);	
			
			clients[j] = accept(listenfd, &client_address, &client_addrlength);
			
			remotes[j] = socket(PF_INET, SOCK_STREAM, 0);

			assert(remotes[j] >= 0);
		
			address.sin_port = 0;
			if(bind(remotes[j], (struct sockaddr*)&address, sizeof(address)) < 0)
			{
				printf("errno:%d\n", errno);
				break;
			}

			if(connect(remotes[j], (struct sockaddr*)&remote_address, sizeof(remote_address)) < 0)
			{
				printf("can not connect to remote host.\n");
				break;
			}
		}
		
		for(i=0; i<CLIENT_COUNT; i++)
		{
			if(!clients[i])
			{
				continue;
			}
	
			if(FD_ISSET(clients[i], &fdreads))
			{
				int ret = recv(clients[i], buf, buf_size -1, 0);
				printf("client_read_ret=%d\n", ret);
				if(ret > 0)
				{
					send(remotes[i], buf, ret, 0);
				}
				if(ret <=0 )
				{	
					printf("client disconnected or network error.\n");
					remove_client(i);
				}
			}
			if(FD_ISSET(remotes[i], &fdreads))
			{
				int ret = recv(remotes[i], buf, buf_size, 0);
				printf("remote_read_ret=%d\n", ret);
				if(ret > 0)
				{
					send(clients[i], buf, ret, 0);
				}
				else
				{
					printf("remote host disconnected or network error occured.\n");
					remove_client(i);
				}
			}
		}
	}

	close(listenfd); 

	return 0;
}

I/O复用之select

代码来自《Linux高性能服务器编程》

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <string.h>
#include <assert.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

int main(int argc, char* argv[])
{
	if(argc <= 2)
	{
		printf("usage: %s ip_address port_number\n", basename(argv[0]));
		return 1;
	}

	const char* ip = argv[1];
	int port = atoi(argv[2]);

	struct sockaddr_in address;
	bzero(&address, sizeof(address));
	address.sin_family = AF_INET;
	inet_pton(AF_INET, ip, &address.sin_addr);
	address.sin_port = htons(port);

	int listenfd = socket(PF_INET, SOCK_STREAM, 0);
	assert(listenfd >= 0);

	int ret = bind(listenfd, (struct sockaddr*)&address, sizeof(address));
	assert(ret != -1);
	
	ret = listen(listenfd, 5);
	assert(ret != -1);

	struct sockaddr_in client_address;
	socklen_t client_addrlength = sizeof(client_address);

	int connfd = accept( listenfd, (struct sockaddr*)&client_address, &client_addrlength);
	
	if(connfd < 0)
	{
		printf("errno is : %d\n", errno);
		close(listenfd);
	}

	char buf[1024];
	fd_set read_fds;
	fd_set exception_fds;
	FD_ZERO(&read_fds);
	FD_ZERO(&exception_fds);
	
	while(1)
	{
		memset(buf, '\0', sizeof(buf));

		FD_SET(connfd, &read_fds);
		FD_SET(connfd, &exception_fds);
		ret = select( connfd + 1, &read_fds, NULL, &exception_fds, NULL);

		if(ret < 0)
		{
			printf("selection failure\n");
			break;
		}
		
		if(FD_ISSET(connfd, &read_fds))
		{
			ret = recv(connfd, buf, sizeof(buf) - 1, 0);
			if(ret <= 0)
			{
				break;
			}
			printf("get %d bytes of nornal data: %s\n", ret, buf);
		}
		if(FD_ISSET(connfd, &exception_fds))
		{
			ret = recv(connfd, buf, sizeof(buf) - 1, MSG_OOB);
			if(ret <= 0)
			{
				break;
			}
			printf("get %d bytes of oob data: %s\n", ret, buf);
		}
	}

	close(connfd);
	close(listenfd);

	return 0;	
}

nginx缩放图片并缓存

在网下查了一个nginx缩放图片的方法,我了解到的有两种,一种是使用nginx自带的image filter module,一种是nginx+lua来调用第三方工具(比如Image Magic),后者要更强大,因为image magic强大 。。。。

这里介绍nginx使用自带的image filter module。

在编译nginx的时候,带上–with-http_image_filter参数。

自带的在缩放图片后并不能缓存到内存或者硬盘上以便下次直接返回缩放后的图片。所以这里使用nginx自己反向代理自己来做,因为反向代理是可以缓存的。

具体配置如下:

image filter module指令说明详见:http://wiki.nginx.org/HttpImageFilterModule

 

fedora 19玩英雄联盟

1,安装nvidia官方驱动

lspci | grep vga -i

01:00.0 VGA compatible controller: NVIDIA Corporation GF104 [GeForce GTX 460] (rev a1)

查看驱动型号,到http://www.geforce.cn/drivers下载相应的驱动。

sudo systemctl stop kdm.service停止GUI并运行下载的驱动

中间有提示说是否生成32位的opengl lib,我是64位的机器,但还是选是,因为下面的PlayOnLinux需要。

2,安装PlayOnLinux

到http://rpm.playonlinux.com/这里下载PlayOnLinux的仓库,然后再使用yum来安装它。

cd /etc/yum.repo.d/

sudo wget http://rpm.playonlinux.com/playonlinux.repo

sudo yum install playonlinux -y

3,安装LOL

打开应用程序->游戏->PlayOnLinux,点击install,把testing勾选上。搜索league of legends并安装。

that’s all.