squid3.0单网卡透明代理服务器[原创]

看网上很多人都做了squid的透明代理,今天自己也试验了一个,使用的是squid3.0stable8版本+iptables,下面就吧我的过程总结如下,首先看下我的拓扑结构
squid单网卡,通过交换机可以直接连接到Internet,A和B两组client可以处在不同的网段或相同的网段,只要可以和squid所在网段互通就行
本文本着只把透明代理实现的最基本过程整理如下,对于squid详细配置,以及iptables相对应的安全配置一概不涉及,有需要的朋友请自行去相应网站查找
言归正传,我的环境如下
vmware6.0模拟两个系统1、centos4.4(squid:192.168.220.128),2、winxp(ip:192.168.220.140)
squid基本配置:

acl manager proto cache_object
acl localhost src 127.0.0.1/32
acl to_localhost dst 127.0.0.0/8
acl localnet src 10.0.0.0/8 # RFC1918 possible internal network
acl localnet src 172.16.0.0/12 # RFC1918 possible internal network
acl localnet src 192.168.0.0/16 # RFC1918 possible internal network
acl SSL_ports port 443
acl Safe_ports port 80 # http
acl Safe_ports port 21 # ftp
acl Safe_ports port 443 # https
acl Safe_ports port 70 # gopher
acl Safe_ports port 210 # wais
acl Safe_ports port 1025-65535 # unregistered ports
acl Safe_ports port 280 # http-mgmt
acl Safe_ports port 488 # gss-http
acl Safe_ports port 591 # filemaker
acl Safe_ports port 777 # multiling http
acl CONNECT method CONNECT

http_access allow manager localhost
http_access deny manager
http_access deny !Safe_ports
http_access deny CONNECT !SSL_ports

http_access allow localnet

http_access deny all
always_direct allow localnet

http_port 3128 transparent       #透明代理的关键参数

hierarchy_stoplist cgi-bin ?

access_log /opt/squid/var/logs/access.log squid
cache_dir ufs /tmp 10 2 4 no-store

refresh_pattern ^ftp: 1440 20% 10080
refresh_pattern ^gopher: 1440 0% 1440
refresh_pattern (cgi-bin|?) 0 0% 0
refresh_pattern . 0 20% 4320

visible_hostname 1

cache_effective_user squid
cache_effective_group squid

然后正常启动squid,测试机xp系统ie中设置代理服务器,可以正常上网,说明,squid目前运行正常

接下来是iptables的配置

echo "1" >/proc/sys/net/ipv4/ip_forward
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-ports 3128

完成后,将xp中ie的代理去掉,更改xp的网关为192.168.220.128,然后配置好dns,直接在访问网页,就可以看到你想要的页面了,而squid中也有正常访问的日志了

这次最简单的squid透明代理就做完了

关于实施中的一点点问题,最先开始的时候,squid中我没有添加transparent这个参数,结果加完iptables后,访问返回的错误的页面如下

ERROR
The requested URL could not be retrieved

--------------------------------------------------------------------------------

While trying to retrieve the URL: /

The following error was encountered:

Invalid URL
Some aspect of the requested URL is incorrect. Possible problems:

Missing or incorrect access protocol (should be `http://'' or similar)
Missing hostname
Illegal double-escape in the URL-Path
Illegal character in hostname; underscores are not allowed
Your cache administrator is webmaster.

后来加上这个参数后就正常了

使用squid实现按源ip选择分流[原创]

先简单介绍一下情况,拓扑图是这样的

        ip-class-1                           ip-class-2
(192.168.21.0、24~25.0、24)          (192.168.31.0~35.0、24)
                          |                                  |
                             |                         |
                               |                   | 
                                   squid-3.0
                                 (192.168.1.1)
                                 |             |
                               |                  |
                            |                        |
                 web-class-1                web-class-3
大概拓扑是这个样子的,可能实际情况比这个更加的负载
我为了实现这样一个拓扑,使用的环境
vmvare6.0,起3个虚拟机,ip分别211.128(squid),211.129(nginx),211.130(apache)
其中两台web又做服务端,又做client
squid的关键配置
                

acl a src 192.168.211.129/32
acl b src 192.168.211.130/32
cache_peer 192.168.211.130 parent 80 0 no-query originserver name=www1 round-robin
cache_peer 192.168.211.129 parent 80 0 no-query originserver name=www2 round-robin
cache_peer_domain www1 a.b.c
cache_peer_domain www2 a.b.c
cache_peer_access www1 allow a
cache_peer_access www2 allow b

第1,2行定义两个client,真实环境请自行更改

第3,4行定义后端的两台RS,并做了round-robin,如果RS很多请自行添加,如果需要做会话保持,请使用sourcehash替换round-robin

第5,6行定义域名走哪个RS,示例中的a.b.c域名使用rr方式投递到两台RS上
第7行定义a过来的请求都投递到www1的RS上
第8行定义b过来的请求都投递到www2的RS上

配置好了后启动squid和web,接下来进行测试
模拟client的两台机器我都用的是wget这个参数,由于我没有仔细研究wget的参数,所以不知道哪个参数包含no-cache头,所以每次wget前我都会用squidclient+purge参数清除缓存,以达到测试的准确性

现在a上进行,wget -S –spider http://a.b.c/,返回的结果是头中包含apache的版本号,而且每次都是apache的版本号,说明,从a ip过来的请求都投递到了www1的RS上
再在b上进行,使用相同的wget命令,返回结果的头中包含nginx的版本号,而且每次都是,说明,从b ip过来的请求都投递到www2的RS上了

如此,如果真实环境,你可以把a的请求或b的请求投递到一组RS上按不同的需求,使用RR或者sourcehash的负载均衡方式。

防止自己的squid被当成代理来用[技术]

今天群里有个人问,自己的squid总被公网上的人拿来当代理上网用,要如何解决
我现在就把自己维护过程中用的几个方法总结一下
1,设定域名的办法
思路:定义好自己的域名,只有自己的域名能被squid代理,自己域名以外的任何域名都被deny
配置:

acl mydomain dstdomain a.b.c     #定义自己的域名
http_access allow mydomain       #只允许自己的域名被访问
http_access deny all             #阻止所有一切访问

然后重启squid,这样别人就无法使用你的squid作为代理了,但你的业务还是保持正常的
但以上办法,被阻止的人会看到你的squid的错误页面,如果你会改源码,可以做个新的错误页面,所以还有个更完美的办法来实现,就是使用deny_info这个参数,配置如下

acl mydomain dstdomain a.b.c      #定义自己的域名
http_access allow mydomain        #只允许自己的域名被访问
http_access deny all              #阻止所有一切访问

deny_info http://a.b.c all        #阻止所有访问的同时,把请求转到a.b.c主页

这样做最终结果就是无论外部的人用你的squid访问什么网站都会跳到你的首页,这招挺损的,哈哈
再说说另外的一个方法吧
2,不使用dns
多数人都喜欢在自己的服务器上配置dns,但往往squid后端的RS都是内网地址,解析都是通过hosts文件走的,或者内部的dns,这样如果你设置了 dns,就可能会被外面的人利用上网,所以我的做法是去掉dns,只使用hosts文件,同时在启动squid是使用-D参数,这个参数的意思就是不检测本地dns

通过以上两个办法足以使你的squid不会被外人拿来使用

apache userdir的使用[原创]

昨天有人在qq群里问了个问题,当他用apache开启了userdir_mod后,总是出现Permission denied的错误日志,为此我也亲自用vmvare测试了一下,我就把我的解决办法写出来
我的apache版本是2.2.9
首先编辑httpd.conf,找到

# Language settings
#Include conf/extra/httpd-languages.conf

# User home directories
Include conf/extra/httpd-userdir.conf        #找到这行,并去掉前面的“#”符号

# Real-time info on requests and configuration
#Include conf/extra/httpd-info.conf

conf/extra/httpd-userdir.conf这个文件可以默认不用修改,然后添加用户

useradd test
su - test
mkdir public_html
echo "test">public_html/index.html

然后启动apache,如果没有报错,就可以使用http://yourip/~test/
就应该可以看到“test”这个内容了
不过遗憾的告诉大家,经过我这样做,结果errorlog中还是有

[Tue Aug 26 18:07:09 2008] [error] [client 192.168.213.1] (13)Permission denied: access to /~test/ denied
[Tue Aug 26 18:07:10 2008] [error] [client 192.168.213.1] (13)Permission denied: access to /~test/ denied
[Tue Aug 26 18:08:11 2008] [error] [client 192.168.213.1] (13)Permission denied: access to /~test/ denied

后来群里那哥们说他把selinux关了之后就搞定,既然兄弟这样说,那我就做吧,先检查了一下目前我的selinux的状态,发现我的selinux本身就是关的,那看来问题不在此处,继续在网上翻阅,最后在一个论坛中发现,如果想访问各个用户,用户目录必须对apache来说有X的权限,所以赶紧试了一下

chmod -R +x /home/test/

这次再访问http://yourip/~test/,久违的test内容果然出现了至此问题解决

总结:1,关闭selinux 
   2,用户目录对apache需要有x的权限

参考文章:http://www.linuxquestions.org/questions/linux-server-73/user-directories-permission-problem-571332/

nginx学习手札—nginx+php整合入门篇[原创]

折腾两天,终于在今天调通了,居然是因为配置文件中有一个参数没有写,真是气死了,看来以后还需要细心些呀,下面我就把php+nginx的实现方法写下来

不像apache和lighttpd,nginx并不能自动的启动spawn FCGI processes,所以需要人为在nginx启动前加载fcgi进程,我使用的是lighttpd带的spawn-fcgi程序,可以下载 lighttpd的源码包,./configure && make后,然后

cp src/spawn-fcgi /usr/local/php/bin/

现在可以手动启动fcgi程序了

/usr/local/php/bin/spawn-fcgi -a 127.0.0.1 -p 10005 -u daemon -f /usr/local/php/bin/php-cgi

这其中daemon是启动nginx程序的用户,其他参数可以执行/usr/local/php/bin/spawn-fcgi查看

接下来是nginx的配置了
nginx安装这里就不在赘述了,php我用的5.2.6,编译时只加了–enable-fastcgi –enable-force-cgi-redirect这两个参数,其他参数请根据各人实际需求添加,这是这两个是必须的
然后在nginx.conf文件中加入

location ~ .*\.php?$ {
            root html;
            index index.php;
            include /usr/local/nginx/conf/fastcgi_params;
            fastcgi_pass 127.0.0.1:10005;
            fastcgi_index index.php;
        }

在这里我使用了nginx自带的fastcgi_params配置文件,具体内容如下

fastcgi_param QUERY_STRING $query_string;
fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param CONTENT_TYPE $content_type;
fastcgi_param CONTENT_LENGTH $content_length;

fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

fastcgi_param SCRIPT_NAME $fastcgi_script_name;
fastcgi_param REQUEST_URI $request_uri;
fastcgi_param DOCUMENT_URI $document_uri;
fastcgi_param DOCUMENT_ROOT $document_root;
fastcgi_param SERVER_PROTOCOL $server_protocol;

fastcgi_param GATEWAY_INTERFACE CGI/1.1;
#fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;
fastcgi_param SERVER_SOFTWARE nginx;

fastcgi_param REMOTE_ADDR $remote_addr;
fastcgi_param REMOTE_PORT $remote_port;
fastcgi_param SERVER_ADDR $server_addr;
fastcgi_param SERVER_PORT $server_port;
fastcgi_param SERVER_NAME $server_name;

# PHP only, required if PHP was built with --enable-force-cgi-redirect
fastcgi_param REDIRECT_STATUS 200

其中蓝色部分是默认配置文件中没有的,也是最关键的,我就是差这一条,卡住了两天,接下来就可以在html文件夹下建立info.php文件了,内容如下

<?php
phpinfo();
?>

保存退出后启动nginx程序

/usr/local/nginx/sbin/nginx

然后通过http://youip/info.php,就可以看到php的详细信息了
到此一个可用的,简单的nginx+php环境就做好了
接下来就对fastcgi_params文件内容按我自己的理解做个解释吧,还望各位看官拍砖
fastcgi_param QUERY_STRING $query_string;以这个为例,这个参数的意思就是,取query_string变量的值付给QUERY_STRING,然后通过fastcgi_param程序去运行,然而这些参数中的变量都是可以通过info.php来查看的。举例来说吧,fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;在info.php中查到$document_root/usr/local/nginx/html,$fastcgi_script_name是所请求的url,在我的例子中就是info.php这文件,当然,如果要是把info.php放到了html/phpinfo文件夹下,那$fastcgi_script_name就是/phpinfo/info.php,这样连起来,SCRIPT_FILENAME就是/usr/local/nginx/html/info.php,然后再交友fastcgi_param来处理,其他参数完全可以按照此理解进行对照

参考文章:http://wiki.codemongers.com/NginxHttpFcgiModule#fastcgi_param
         http://wiki.codemongers.com/NginxFcgiExample

squid2.7方便解决文件描述符大小[技术]

今天上squid官网,无意中发现squid2.7居然在5月31号发布了,出于对新产品的好奇吧,就看了一下 squid.conf中的配置,发现还真是多了不少东西,这其中我认为最有用,也是给squid安装带来方便的参数,max_filedescriptors,这个参数可以指定squid使用的文件描述符大小,但前提是不能超过编译是–with-maxfd这个参数设定的大小,自此,就再也不需要使用ulimit设置文件描述符了,当然squid2.7还增加了其他的一些特性,具体还等之后仔细研究

lvs学习笔记02-ldirectord的使用

上次实现了最基础的LVS,可这种最基础的LVS不能满足真正的实际应用,为什么呢,因为他的实用性不高,一但后端RS有当机的情况,LB并不会发现,如果后端有3台RS就说明会有1/3的用户会看到出错的页面,所以此次的总结就是为了解决这个问题

实现方式,使用heartbeat中自带的ldirector程序,该程序可以对后端RS进行健康监控,发现有RS服务不可用,则将这台RSserver从列表中删除

环境和上次一样
LVS架构:

             LB-2.187(vip:2.186)
               |      |
              |        |
              |         |
            RS01        RS02
          (2.188)    (2.189)

实现目的:
1,实现roundrobin方式的投递请求
2,进行RS的健康检查

Libnet的安装
可以去这里下载libnet.tar.gz安装包
下载后进行解压
按默认安装即可
./configure
make && make install

hearbeat安装
可以去这里下载heartbeat安装包
安装方法也比较简单
解压后按默认安装即可
我使用的2.1.3

groupadd haclient
useradd -g haclient hacluster

./ConfigureMe configure
make && make install

mkdir -p /etc/ha.d/conf
cd heartbeat-2.1.3/ldirectord
cp ldirectord.cf /etc/ha.d/conf

此站点下载对应linux版本的rpm包进行安装,ldirectord启动需要这些包的支持
perl-TimeDate-1.16-1.2.el4.rf.noarch.rpm
perl-MailTools-2.02-1.el4.rf.noarch.rpm

配置Ldirector
vi /etc/ha.d/conf/ldirectord设置如下内容

# Global Directives
checktimeout=3
checkinterval=1
#fallback=127.0.0.1:80
autoreload=yes
logfile="/var/log/ldirectord.log"
logfile="local0"
#emailalert="admin@x.y.z"
#emailalertfreq=3600
#emailalertstatus=all
quiescent=yes

# Sample for an http virtual service
virtual=192.168.2.186:80
        real=192.168.2.188:80 ipip
        real=192.168.2.189:80 ipip
        service=http
        request="index.html"
        scheduler=rr
        #persistent=600
        #netmask=255.255.255.255
        protocol=tcp
        checktype=negotiate
        checkport=80
        request="index.html"

然后执行/usr/sbin/ldirectord start启动ldirectord服务,可以执行/usr/sbin/ldirectord status查看ldirectord运行状态

/usr/sbin/ldirectord status ldirectord for /etc/ha.d/conf/ldirectord.cf is running with pid: 1522

接下来进行测试,使用软件仍然是webserver stress tool,模拟500了连接
1,正常测试,两台RS服务器正常

ipvsadm
IP Virtual Server version 1.2.0 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 192.168.2.186:http rr
  -> 192.168.2.188:http Tunnel 1 0 250
  -> 192.168.2.189:http Tunnel 1 1 250

ipvsadm显示每个RS投递请求各250
来看下websever显示结果:

URL No. Name Clicks Errors Errors [%] Time Spent [ms] Avg. Click Time [ms]
1
251 0 0.00 2,677 11

没有error

2,将任意一台RS80服务关掉

ipvsadm
IP Virtual Server version 1.2.0 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 192.168.2.186:http rr
  -> 192.168.2.188:http Tunnel 0 0 0
  -> 192.168.2.189:http Tunnel 1 0 500

ipvsadm显示结果,已经将所有的请求投递到2.189这个台服务正常的RS上了
来看下Webserver显示结果:

URL No. Name Clicks Errors Errors [%] Time Spent [ms] Avg. Click Time [ms]
1
250 0 0.00 2,658 11

同样没有出现error

到此,一个能用于健康检查的lvs就配置完毕了
关于ldirectord的用法可以到这个网页查看,这是ldirectord的manpage页

 

lvs学习笔记01

最近对LVS技术相当痴迷,相应的技术这里我就不在赘述了,有兴趣的朋友可以去LVS的官网上去看,哪里写的很详细,这里我仅把我的配置心得写出来,有不正确的地方还望各位看客指出

首先说下我的配置环境
大环境:vmware6.0,centOS4.4

LVS架构:

             LB-2.187(vip:2.186)
               |      |
              |        |
             |          |
            RS01       RS02
          (2.188)   (2.189)

实现目的:
    1,简单实现负载均衡
    2,不对后端服务器进行健康检查

IPVS安装
可以到这里下载最新的ipvsadm软件,下载后执行以下操作

ln -s /usr/src/kernels/2.6.9-42.EL-i686/ /usr/src/linux
rpmbuild –rebuild ipvsadm-1.24-6.src.rpm
rpm -ivh /usr/src/redhat/RPMS/i386/ipvsadm-1.24-6.i386.rpm

LB上的配置
在/usr/local/bin下建立startlvs.sh脚本,加入如下内容

echo 1 >/proc/sys/net/ipv4/ip_forward
ifconfig eth0:0 192.168.2.186 netmask 255.255.255.255 broadcast 192.168.2.186 up

/sbin/route add -host 192.168.2.186 dev eth0:0

ipvsadm -A -t 192.168.2.186:80 -s rr
ipvsadm -a -t 192.168.2.186:80 -r 192.168.2.188 -i
ipvsadm -a -t 192.168.2.186:80 -r 192.168.2.189 -i

然后将脚本加入到/etc/rc.local中以便开机自动运行

Realserver上的配置
在/usr/local/bin下建立starttunl.sh,加入如下内容:

modprobe ipip

ifconfig tunl0 192.168.2.186 netmask 255.255.255.255 broadcast 192.168.2.186 up

/sbin/route add -host 192.168.2.186 dev tunl0

echo "2">/proc/sys/net/ipv4/conf/all/arp_announce

echo "1">/proc/sys/net/ipv4/conf/all/arp_ignore

echo "2">/proc/sys/net/ipv4/conf/tunl0/arp_announce
echo "1">/proc/sys/net/ipv4/conf/tunl0/arp_ignore

如此,已经简单的lvs就配置完成了,我使用webserver stress tool进行的模拟用户访问,realsever上启动lighttp,在LB上执行ipvsadm -l,显示如下:

[root@LVS-LB01 bin]# ipvsadm -l

IP Virtual Server version 1.2.0 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 192.168.2.186:http rr
  -> 192.168.2.189:http Tunnel 1 0 250
  -> 192.168.2.188:http Tunnel 1 0 251

webserver stress tool返回的结果如下

URL No. Name Clicks Errors Errors [%] Time Spent [ms] Avg. Click Time [ms]
1
250 0 0.00 3,695 15

如果此时我将后端的RS服务停一台会看到如下显示结果:

IP Virtual Server version 1.2.0 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 192.168.2.186:http rr
  -> 192.168.2.189:http Tunnel 1 0 250
  -> 192.168.2.188:http Tunnel 1 0 250

ipvsadm依然会把请求投递到后端RS
来看下webserver stress tool的结果

URL No. Name Clicks Errors Errors [%] Time Spent [ms] Avg. Click Time [ms]
1
492 242 49.19 1,865 7

很明显已经能看到有一半的errors了

几点说明:
1,此lvs并支持健康检查,一但后端有一台服务停了,LB并不会知道,依旧会把服务投递到后端
2,我是用的VS/TUN的方式实现的LVS
3,策略我使用的rr方式,如需要其他策略,请自行更改

Linux SHELL if命令参数说明

俺反正是经常忘啊,用的时候总是翻箱倒柜的找也不是个事,把它们列在这里吧。

  • -b 当file存在并且是块文件时返回真
  • -c 当file存在并且是字符文件时返回真
  • -d 当pathname存在并且是一个目录时返回真
  • -e 当pathname指定的文件或目录存在时返回真
  • -f 当file存在并且是正规文件时返回真
  • -g 当由pathname指定的文件或目录存在并且设置了SGID位时返回为真
  • -h 当file存在并且是符号链接文件时返回真,该选项在一些老系统上无效
  • -k 当由pathname指定的文件或目录存在并且设置了”粘滞”位时返回真
  • -p 当file存在并且是命令管道时返回为真
  • -r 当由pathname指定的文件或目录存在并且可读时返回为真
  • -s 当file存在文件大小大于0时返回真
  • -u 当由pathname指定的文件或目录存在并且设置了SUID位时返回真
  • -w 当由pathname指定的文件或目录存在并且可执行时返回真。一个目录为了它的内容被访问必然是可执行的。
  • -o 当由pathname指定的文件或目录存在并且被子当前进程的有效用户ID所指定的用户拥有时返回真。

UNIX Shell 里面比较字符写法:

  • -eq 等于
  • -ne 不等于
  • -gt 大于
  • -lt 小于
  • -le 小于等于
  • -ge 大于等于
  • -z 空串
  • = 两个字符相等
  • != 两个字符不等
  • -n 非空串

Bash的字符串匹配共有6种形式

Bash的字符串匹配共有6种形式:

1. ${variable#pattern}
如果pattern匹配variable的开始部分,从variable的开始处删除字符直到第一个匹配的位置,包括匹配部分,返回剩余部分。

2. ${variable##pattern}
如果pattern匹配variable的开始部分,从variable的开始处删除字符直到最后一个匹配的位置,包括匹配部分,返回剩余部分。

3. ${variable%pattern}
如果pattern匹配variable的结尾部分,从variable的结尾处删除字符直到第一个匹配的位置,包括匹配部分,返回剩余部分。

4. ${variable%%pattern}
如果pattern匹配variable的结尾部分,从variable的结尾处删除字符直到最后一个匹配的位置,包括匹配部分,返回剩余部分。

5. ${variable/pattern/string}
6. ${variable//pattern/string}
最后这两种用法用于匹配替换。因为我没用到,先不说了。(busybox 1.0.1 不支持最后这两种语法。)

举例:
str=tftp://hostname.com/onepath/anotherpath

echo ${str#*/}
输出:/hostname.com/onepath/anotherpath

echo ${str##*/}
输出:anotherpath

echo ${str%/*}
输出:tftp://hostname.com/onepath

echo ${str%%/*}
输出:tftp:/

——————–
下面说一说具体怎么用。

假设在某个系统中没有sed和awk, 只有grep,tr和cut。
如果要取得网卡的mac地址,可以:

mac_addr=$(ifconfig eth0 | grep HWaddr | cut -d’ ‘ -f11 | tr -d : )
echo $mac_addr
输出:0150BF9886BF

后面再说为什么我要把冒号去掉。这种方式在cut时要经过实验才知道我们要的是第11个field。现在换另一种方式:

mac_addr=$(ifconfig eth0 | grep HWaddr | tr -d ‘ :’)
echo $mac_addr
输出:eth0LinkencapEthernetHWaddr0150BF9886BF
然后:
mac_addr=${mac_addr#*HWaddr}
echo $mac_addr
输出:0150BF9886BF

现在要求写一个程序,接受命令行给定一个网址去下载一个文件,要求根据网址的协议的不同,采用不同的程序下载。如果给定的网址以.xml结尾,则认为要下载的文件已经在给定的网址中指定,否则要下载的文件名为本机的mac地址加.xml扩展名,不包括mac中的冒号。

例如给定 tftp://host/file.xml,则要用tftp命令下载host上的file.xml文件。
如果给定 http://host/path,则要用wget命令下载host/path上的0150BF9886BF.xml文件。

先取网址的协议,采用从右向左最大匹配”://”:
url=$1
proto=${url%%://*}

再判断文件名是否已经给定, 采用从左向右最大匹配”.xml”:
[ -z “${url##*.xml}” ] || url=$url/$mac_addr.xml

如果 $proto = “http” 或者 “ftp”
则执行
wget $url -O local_file

如果 $proto = “tftp”,这个有点麻烦,因为tftp的用法是:

tftp -g -r remote_file -l local_file host

所以还要把remote_file和host从url中提取出来。

先把url中的tftp://去掉:
tmp=${url#*://}

再从右向左最大匹配”/”得到host:
host=${tmp%%/*}

再从左向右最小匹配”/”得到路径和文件名:
remote_file=${tmp#*/}