我们正在讨论的网络,
以下所有内容都假定已为您的 LINUX 服务器的 eth0 端口分配了 IP 地址(使用 “ifconfig”)。
此外,重要的是,本文档不限制您仅使用 PPP(也可以是 SLIP、PPTP 等...)。“ppp0” 端口的 IP 地址完全无关紧要。本文档假定您有一个这样的端口,并且它已启动。
以及价值 90 美元的内存(16 兆内存总共花了 60 美元),我获得了一个功能齐全的 Linux 系统,总共花费 270 美元。不打算让该系统负担 NT 或任何其他占用内存、磁盘和 CPU 的操作系统。当然,我的客户端机器是一台 32 兆的 P100 机器,配备两个硬盘(其中一个硬盘被移植为 Linux 机器的硬盘),并运行 95 系统。
Linux 系统放在一个干净的必胜客披萨盒上。我买不起另外 50 美元的机箱,因为我从一位朋友那里获得了一个电源模块。
最后,我们将永远不会有适用于 Linux 的 “Microsoft Explorer 浏览器”,因此我甚至从未考虑过使用 Explorer。而且,某种程度上,我觉得配置 Explorer 不会像在客户端机器(即其他机器)上配置 NETSCAPE 那样容易。
我没有给出安装代理服务器的说明。这是关于在 LINUX 机器上安装 “socks” 功能,客户端机器上的 NETSCAPE 可以使用它来访问互联网。据我所知,NETSCAPE 是唯一在非 UNIX 机器上运行并了解 SOCKS 功能的应用程序。
如果您拥有 TCP/IP 网络,那么您必须为机器至少拥有两个 IP 地址(一个用于 LINUX 机器,另一个用于其中一台客户端机器,如果您有多台客户端机器,则需要更多 IP 地址)。
阅读其他 HOW-TO 文档,了解如何为 TCP/IP 网络上的所有机器分配 IP 地址。(特别是如果您没有注册互联网域名)。
我从单台 LINUX 机器和单台 Win95 机器创建了一个 10.0.1.x 网络。它们分别被分配了 10.0.1.1 和 10.0.1.2。10.0.1.1 是 LINUX 机器的以太网端口 (eth0) 的 IP 地址。ppp0 端口有另一个 IP 地址(我[幸运地]有一个固定 IP 地址)。该 IP 地址与我们无关,并且出于安全原因而被保留。
在 PPP 链接的另一端,我还有一个固定的域名服务器。(大学机器)。
Linux 机器配有调制解调器和 CRONTAB 条目,可以每天在固定时间自动拨号连接到互联网。当我想上网冲浪时,我也会手动连接到互联网。
如果您通过在线服务连接到互联网,请参见下文...
如果您使用 AOL、Compuserve、Sprynet、Netcom 等在线服务连接到外部世界... 那么您可能没有固定的 IP 地址。但这对于将您的 Intranet 连接到外部世界几乎无关紧要。如果您不相信,我请您继续阅读... 并成为信徒...
(对于像我一样想知道到底发生了什么的人...)。其他人可以跳过本节....
从 Linux 机器 “重新路由” 的一个问题是,客户端必须实际将所有数据包发送到 Linux 机器,无论目标地址是什么。为此,Win95 和 WinNT 将仅允许 “代理服务器”(我打算弄清楚,并撰写另一份文档)。
阅读 NET-HOW-TO 文档,位置在/usr/doc/faq/howto在您的 Linux 机器上(如果是 slakware 系统),或者访问www.linux.org并在那里阅读相同的 NET-HOW-TO 文档...
在其中,您将找到如何下载 socks 软件包并编译它。您需要阅读那里的说明来设置内部网络。但欢迎您阅读本文 :-) 。
该文档花费大量时间解释如何配置 UNIX 客户端。特别是对于 “rlogin”、“telnet”、“ftp” 等... 如果您没有 UNIX 客户端,那么在编译 SOCKS 软件包后,请重新开始阅读本文档,以了解如何使用 socks 软件包,而不是该软件包中的 readme 文件。
我将 tar 文件放在/usr/local/ProxyServer并解压了它,创建了一个 “sockd4.2b” 子目录,其中有一个 “Makefile”。正如 howto 文档中提到的,我不得不努力才能成功地在 MAKEFILE 上执行 make 命令。
希望您只需要更改我的 Makefile 副本的第 9 行。
您将在 sockd 子目录中找到一个名为 “sockd” 的可执行文件。
Once you are done compiling, COPY the following files to /usr/local/etc (They SHOULD be in the same dir as the sockd directory) sockd (The executable a.k.a daemon) sockd.conf (configuration file) sockd.route (configuration file) socks.conf (configuration file) # ### make a link called "socks" which points to "sockd" within the same dir. # cd /usr/local/etc # ln -s sockd socks然后编辑这三个配置文件,使它们类似于下面给出的文件(这些是我为由 LINUX “服务器” 和 Win95/WinNT 客户端机器组成的双计算机网络设置的)。
注意:10.0.1.2 是我的 Win95/WinNT 客户端机器的 IP 地址。此 sockd.conf 文件必须位于您的 LINUX 服务器上(在我的情况下,LINUX 服务器的以太网端口的 IP 地址为 10.0.1.1)permit 10.0.1.2 0.0.0.0 deny 0.0.0.0 0.0.0.0 : /usr/ucb/finger @%A | /usr/ucb/mail -s 'SOCKD: rejected -- from %u@%A to host %Z (service %S)' root #BAD_ID: /usr/ucb/finger @%A | /usr/ucb/mail -s '%U pretends to be %u on host %A' root@%A root #NO_IDENTD: /usr/ucb/mail -s 'Please run identd on %A' %u@%A root@%A #[EOF]
注意:此 sockd 程序仅适用于客户端机器。LINUX 机器上的所有应用程序都不需要 sockd 或任何其他软件包来访问互联网,因为此 LINUX 机器使用 PPP 直接连接到互联网。
#! NoShell 10.0.1.1 10.0.1.0 255.255.255.0 #[EOF]注意:第一个 IP 地址是 LINUX 机器的 eth0 端口的地址。第二个 IP 数字不是 IP 地址 -- 它是网络地址(基本上,将 IP 地址的四个数字中的最后一个数字转换为零)。
direct 127.0.0.1 255.255.255.255 direct 10.0.1.1 255.255.255.255 direct 10.0.1.2 255.255.255.255 sockd @=199.99.99.99 10.0.1.1 0.0.0.0
您必须确保这些程序中没有任何一个访问同一台 Linux 机器上的 “named” 守护进程。为此,我们将指定给解析器例程(即,将 www.cnn.com 转换为数字 ip 地址的例程),这些解析器例程必须先检查 /etc/hosts 文件,然后检查 /etc/resolv.conf 中提到的 DNS 服务器
我们如何做到这一点?只需确保 /etc/host.conf 文件是:-
order hosts, bind multi on如果还有其他内容,请删除它,除非您对 DNS 和 “named” 非常了解。
我坚持阻止 LINUX 机器的应用程序访问其自身的 “named” 服务器的原因是,这毫无意义。根据我的经验,这种 “不必要” 的东西可能在技术上看起来安全无害,但迟早会引起足够的麻烦...
Linux 机器显然通过 PPP(或任何链接)链接访问互联网,效果很好。我们正在为客户端机器安装 “sockd” 软件包和 “named” 守护进程。让我们不要打扰 LINUX 系统。
除非有人建议更改该文件,否则不要更改任何文件...
我还将假设您已正确设置 “resolv.conf”,以使您的 LINUX 服务器能够访问互联网和 DNS(在 PPP 连接的 “另一端”)。我的示例 resolv.conf 文件作为示例在最后提供。
*********************************************** WARNING *********************************************** For your own good, I suggest that you setup your machine through the linux installation programs (i.e., while installing linux on your computer.) ************************************************
; boot file for name server forwarders 128.112.129.111 directory /etc cache . root.cache primary 1.0.10.in-addr.arpa named_DNS_for_inTi_xwk ^^^^^^^^^^^^^^^^^^^^^^注意:第 2 行包含您的 LINUX 机器使用 PPP(或任何方式)连接到的网络中的 DNS 服务器的 IP 地址。
注意:最后一行包含名为 “named_DNS_for_inTi_xwk” 的文件名,该文件必须位于 “/etc” 目录中。此文件的内容在下面给出(您可以随意给它起一个更好的名字 :-) )
@ IN SOA 10.0.1.0 hostmaster.10.0.1.0 ( 1 ; Serial 28800 ; Refresh 7200 ; Retry 604800 ; Expire 86400) ; Minimum TTL NS 10.0.1.1 1 PTR MyLinuxMachine注意:最后一行(以 1 开头)包含名称 “MyLinuxMachine”。将其替换为 /etc/HOSTNAME 中的名称。注意:再次,像您到目前为止所做的那样,将 “10.0.1.1” 替换为您的 LINUX 机器 eth0 端口的地址,并将 “10.0.1.0” 替换为该端口的网络地址。注意:我真的不理解上面文件的每一个字符。您最好通过研究 NAMED 守护进程的文档来满足您的好奇心。
要获取此文件,请阅读 NET-HOW-TO 文档和 socks 软件包随附的文档。
这些说明非常简单...
我所做的只是运行那里提到的命令,并将其重定向到一个文件中并将其命名为“/etc/root.cache”
; <<>> DiG 2.1 <<>> ns ;; res options: init recurs defnam dnsrch ;; got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 6 ;; flags: qr rd ra; Ques: 1, Ans: 9, Auth: 0, Addit: 9 ;; QUESTIONS: ;; ., type = NS, class = IN ;; ANSWERS: . 137030 NS C.ROOT-SERVERS.NET. . 137030 NS D.ROOT-SERVERS.NET. . 137030 NS E.ROOT-SERVERS.NET. . 137030 NS I.ROOT-SERVERS.NET. . 137030 NS F.ROOT-SERVERS.NET. . 137030 NS G.ROOT-SERVERS.NET. . 137030 NS A.ROOT-SERVERS.NET. . 137030 NS H.ROOT-SERVERS.NET. . 137030 NS B.ROOT-SERVERS.NET. ;; ADDITIONAL RECORDS: C.ROOT-SERVERS.NET. 410161 A 192.33.4.12 D.ROOT-SERVERS.NET. 410161 A 128.8.10.90 E.ROOT-SERVERS.NET. 410161 A 192.203.230.10 I.ROOT-SERVERS.NET. 167767 A 192.36.148.17 F.ROOT-SERVERS.NET. 410161 A 192.5.5.241 G.ROOT-SERVERS.NET. 410161 A 192.112.36.4 A.ROOT-SERVERS.NET. 410161 A 198.41.0.4 B.ROOT-SERVERS.NET. 410161 A 128.9.0.107 H.ROOT-SERVERS.NET. 410161 A 128.63.2.53 ;; Total query time: 334 msec ;; FROM: svathyam to SERVER: default -- 128.112.129.111 ;; WHEN: Sat Sep 28 21:38:04 1996 ;; MSG SIZE sent: 17 rcvd: 312
(添加以 “socks” 开头的单行...)
# services This file describes the various services that are # available from the TCP/IP subsystem. It should be # consulted instead of using the numbers in the ARPA # include files, or, worse, just guessing them. # Version: @(#)/etc/services 3.02 02/21/93 # Author: Fred N. van Kempen,注意:我认为此行仅由 inetd 守护进程读取。这告诉 inetd 为端口 # 1080 的所有 tcp 连接调用 “socks” 程序。... <lines delete> socks 1080/tcp # sarma: Sep.15.96: Got this from the ~sockd/include/socks.h file. ... <lines delete> # End of services.
# I am just following instructions from ~sockd/doc/sockd.1 man pages... socks stream tcp nowait root /usr/local/etc/socks
执行“tail -f /var/adm/messages” 和 “tail -f /var/adm/syslog”同时尝试使用客户端机器上的 NETSCAPE 进行连接。
现在让我们配置客户端机器的 Netscape...
--------- -------------------------- -------- | socks | | | | 1080 | --------- -------------------------- --------这表明 netscape 已经知道 socks。您所要做的就是告诉 NETSCAPE socks 守护进程在哪里运行。
在上面显示的中间框中键入 linux 服务器的 eth0 端口 IP 地址...
保存此设置并开始使用吧!
如果您无法通过 LINUX 服务器连接到互联网,请停止阅读本文档,并阅读其他 HOW-TO 文档以设置您的 LINUX 机器以通过 PPP 链接访问互联网。
我希望您了解 IP 地址的概念。简而言之,IP 地址具有 “助记符” 格式(如 www.cnn.com)以及数字版本,如 “198.20.186.4”。如果您键入前者 “www.cnn.com”,则必须有人帮助您的计算机将该名称转换为数字格式。
为什么使用数字格式?因为,数字格式编码了一个非常高效的系统,可以告诉每台计算机如何将通信胶囊发送到它想要通信的其他计算机。
因此,如果您在 NETSCAPE 浏览器中键入 “www.cnn.com”,则名为 “DNS 服务器” 的 UNIX 计算机会将该名称转换为您计算机的数字。然后您的计算机将使用该 IP 地址的数字格式实际连接到 www.cnn.com 并向您显示他们的最新新闻。
因此,要旨是:要使用互联网,您需要 DNS 服务器。本文档包含有关设置您的计算机以连接到您附近的 DNS 服务器的说明。
您的 LINUX 机器必须具备以下所有条件:-
“以太网卡” 是您的 “eth0” 端口。该以太网 “端口” 使您可以探索与其连接的以太网网络。
由于您的客户端机器通过以太网电缆连接到 LINUX 机器,因此您的客户端机器与 LINUX 机器通信的任何内容都将仅通过 “eth0” 端口到达 Linux 机器。外部世界发送到您的 LINUX 机器的任何内容都将仅通过 “ppp0 端口” 到达。因此,为这两个端口提供 “不同的地址” 非常重要。
为了让您更容易理解,如果您已经使用 PPP 成功连接到外部世界,那么您已经不知不觉地(或有意识地)为您的 Linux 机器的 PPP 端口分配了一个 IP 数字地址。
SHELL=/bin/bash #SOCKS=-DSOCKS # or SOCKS=-Dconnect=Rconnect -Dgetsockname=Rgetsockname -Dlisten=Rlisten -Daccept=Raccept -Drcmd=Rrcmd -Dbind=Rbind -Dselect=Rselect CFLAGS="$(SOCKS)" # If your system doesn't have PWD defined, define it here: PWD="/usr/local/ProxyServer/socks42b" # It should be this current directory. # If your system has getcwd() but no getwd(), uncomment the next line: #GETCWD=-DGETCWD # Define FASCIST if you want ftp (rftp) to log names of all files transferred #FASCIST=-DFASCIST # Define RCMD and SUPPORT_RCMD if you want to support Rrcmd, which is required # for SOCKSified rlogin, rsh, and rcp. RCMD=Rrcmd.o SUPPORT_RCMD=-DSUPPORT_RCMD # Define FOR_PS if your system is not SYSV and you want to have the # command 'ps' show some details of sockd's activity. FOR_PS=-DFOR_PS # Define SHORTENED_RBIND to make Rbind() take exactly the same # argument list as the regular bind(), i.e., without the additional # 'remhost' argument. SHORTENED_RBIND=-DSHORTENED_RBIND # optimization flag for cc #OPTIMIZE=-g OPTIMIZE=-O6 -fomit-frame-pointer -pipe -m486 # Be careful with the OPTIMIZE flag. SunPro's SC2.0.1, for example, is # knwon to produce incorrect code when -O is used. # Directory into which to install the man pages MAN_DEST_DIR = /usr/local/man # Directory into which the SOCKS server should be installed SERVER_BIN_DIR = /usr/local/ProxyServer ## This was defalted to /usr/local/etc # Directory into which the client programs should be installed CLIENTS_BIN_DIR = /usr/local/ProxyServer ## This was defaulted to /usr/local/bin # LINUX should use CC=gcc RANLIB=ranlib RESOLV_LIB= #OTHER_CFLAGS=-traditional -DLINUX $(GETCWD) $(FASCIST) $(SHORTENED_RBIND) -DCOMPAT OTHER_CFLAGS=-DLINUX $(GETCWD) $(FASCIST) $(SHORTENED_RBIND) -DCOMPAT OS=linux INSTALL=install GETPASS=getpass.o # Remember to include -Dindex=strchr -Drindex=strrchr in OTHER_CFLAGS if # you don't have index() and rindex() (Sys-V camp) # <<<---------------- # The Internet Whois server; used to be nic.ddn.mil. WHOIS_SERVER= WHOIS_SERVER=-DWHOIS_SERVER\'=\"rs.internic.net\"\' SOCKS_LIB=$(PWD)/lib/libsocks.a IDENT_LIB=$(PWD)/libident/libident.a all: LIB LIBIDENT server clients server: LIB LIBIDENT (cd sockd; $(MAKE) CC="$(CC)" RESOLV_LIB="$(RESOLV_LIB)" \ OPTIMIZE="$(OPTIMIZE)" \ SOCKS_LIB="$(SOCKS_LIB)" SUPPORT_RCMD="$(SUPPORT_RCMD)" \ IDENT_LIB="$(IDENT_LIB)" \ OTHER_CFLAGS="$(OTHER_CFLAGS) $(FOR_PS)") clients: RFINGER RFTP RTELNET LIB: (cd lib; $(MAKE) CC="$(CC)" GETPASS="$(GETPASS)" \ OPTIMIZE="$(OPTIMIZE)" \ RCMD="$(RCMD)" SUPPORT_RCMD="$(SUPPORT_RCMD)" \ OTHER_CFLAGS="$(OTHER_CFLAGS) " RANLIB="$(RANLIB)") LIBIDENT: (cd libident; $(MAKE) CC="$(CC)" OTHER_CFLAGS="$(OTHER_CFLAGS)" \ OPTIMIZE="$(OPTIMIZE)" RANLIB="$(RANLIB)") RFINGER: LIB # This also build rwhois (cd rfinger; $(MAKE) CC="$(CC)" $(WHOIS_SERVER) \ OPTIMIZE="$(OPTIMIZE)" SOCKS="$(SOCKS)" \ RESOLV_LIB="$(RESOLV_LIB)" SOCKS_LIB="$(SOCKS_LIB)" \ OTHER_CFLAGS="$(OTHER_CFLAGS) ") RTELNET: LIB (cd rtelnet; $(MAKE) CC="$(CC)" OS="$(OS)" SOCKS_LIB="$(SOCKS_LIB)" \ OPTIMIZE="$(OPTIMIZE)" SOCKS="$(SOCKS)" \ RESOLV_LIB="$(RESOLV_LIB)" OTHER_CFLAGS="$(OTHER_CFLAGS)") RFTP: LIB (cd rftp; $(MAKE) CC="$(CC)" SOCKS_LIB="$(SOCKS_LIB)" \ OPTIMIZE="$(OPTIMIZE)" SOCKS="$(SOCKS)" \ RESOLV_LIB="$(RESOLV_LIB)" OTHER_CFLAGS="$(OTHER_CFLAGS)") install.server: (cd sockd; $(MAKE) INSTALL="$(INSTALL)" MAN_DEST_DIR="$(MAN_DEST_DIR)" \ SERVER_BIN_DIR="$(SERVER_BIN_DIR)" install install.man) install.clients: install.man for i in rfinger rftp rtelnet; do \ (cd $$i ; $(MAKE) INSTALL="$(INSTALL)" \ CLIENTS_BIN_DIR="$(CLIENTS_BIN_DIR)" \ install) done install.man: (cd doc; $(MAKE) INSTALL="$(INSTALL)" MAN_DEST_DIR="$(MAN_DEST_DIR)" \ install) clean: for i in lib libident sockd rfinger rftp rtelnet ; do \ ( cd $$i ; $(MAKE) clean) done
#!/bin/csh # # ppp-on # # Set up a PPP link set LOCKDIR=/var/spool/uucp set DEVICE=cua3 set OUR_IP_ADDR=128.000.111.222 if ( -f $LOCKDIR/LCK..$DEVICE ) then echo 'PPP device is locked' exit 1 endif route del default # Just in case the Ethernetwork (In-House ethernet network) is up.... # if its NOT, then the above command is harmless... route ## To show that the above was successful... /usr/lib/ppp/fix-cua $DEVICE unalias pushd unalias popd pushd /usr/lib/ppp # stty 19200 -tostop # The original code has been commented out below... # if chat -l LCK..$DEVICE ABORT "NO CARRIER" ABORT BUSY "" ATZ OK ATs50=255s111=0DT$PHONE CONNECT "" ogin: $USER ssword: \\q$PASSWORD echo $cwd ls -l ./comserv.dip dip ./comserv.dip ## I removed the -v (DEBUG&VERBOSE) option to 'dip'. set dip_status=$status # echo the return value of dip is $dip_status if $dip_status == '0' then # Now please wait for 10 seconds, while the link is being auto-verified by dip. echo 'About to fork-off pppd (after a delay of 10 secs)...' date echo 'If you see any error msgs below, then we are having SERIOUS problems...' sleep 10 pppd -detach crtscts defaultroute domain remote.princeton.edu mru 1005 mtu 1005 $OUR_IP_ADDR{}: /dev/$DEVICE 38400 & ###### we dont need this for the previous line... < /dev/$DEVICE > /dev/$DEVICE ) & # The pppd deamon is FORKED OFF. See the "&" at the END of above line... # By using "locl" option, I am requesting that /var/spool/uucp be the dir # in which the LOCKS are created... echo 'Now wait another 10 seconds, before I auto-verify internet connection.' sleep 10 cat ~root/@utils/.line ping -v -c 5 genius.eng.wayne.edu cat ~root/@utils/.line traceroute physics.iisc.ernet.in >&! /tmp/$$ cat /tmp/$$ \rm -f /tmp/$$ cat ~root/@utils/.line exit 0 else echo 'PPP setup failed' exit 1 endif popd # [EoF]
注意:此脚本作为示例提供。拥有此示例并不能保证您将拥有互联网连接。此脚本从上面给出的 PPP-ON 脚本调用...
#******************************** comserv.dip ******************************** # # Connection script for SLIP to ........ # STATUS code for "dip" when it executes this script are: # 0 - all ok. # 1 - basic failures, in initializing the modem. # 2 - Failed in the crucial "dial" command. # 3 - Though DIAL command was successful, this script couldnt recognize # the VERY FIRST responses from the other modem. (i.e., Training occured # but, after that nothing happened that was intelligible to this script. # 4 - Modems could nicely link up. But remote server HAS CHANGED syntax. # i.e., the strings output by the server, are assumed to arrive in a # PARTICULAR sequence. If server s/w has changed, then we have this # problem. SOLUTION!!!! Manually connect and note all the strings & # all the sequence of interactions... Then reprogram the script below. # 10 - though dialing and connecting (modem-wise) is successful, ppp failed. ############################################################################ main: redial: # Set the desired serial port and speed. port cua3 speed 38400 # term get $mtu 1005 # Reset the modem and terminal line. reset # Without doing the above reset, nothing below will work! # Initialize the modem and dial comserv. # send ATQ0V1E1X4L1S0 0 \r # wait OK 5 send ATZ\r wait OK 5 if $errlvl != 0 goto error # send ATTQ0V1E1X4S0=0&C1\r # wait OK 5 # if $errlvl != 0 goto error ## For Dial Tone use :- send AT&D2\r send AT&DP\r wait OK 5 if $errlvl != 0 goto error # send ATS10=1\r # wait OK 5 # if $errlvl != 0 goto error print if the line is busy, the dial command will realize that after 30 secs ONLY. dial 258-0000 30 print Return value of DIAL is $errlvl if $errlvl == 1 goto Continue1 if $errlvl == 3 goto busy print unknown error with DIAL command in "dip" script. quit 2 # unknown error with crucial DIAL command... busy: print telephone number is busy... Continue (1) or terminate (2)? get $input ask if $input == 1 goto redial print You have requested to cancel PPP. Quitting... quit 10 # terminated... Continue1: # wait V32 30 # wait CONNECT 10 # if $errlvl != 0 print Couldn't detect a CONNECT # if $errlvl != 0 goto connect_fail # print CONNECT was detected... # We are connected. Login to the system. login: sleep 3 # send \r\r wait Username: 20 if $errlvl != 0 goto error2 send USERID\r wait Password: 5 if $errlvl != 0 goto error2 send __Password+_::\r\r wait comserv> 15 # print Reached Comserv prompt... if $errlvl != 0 goto error2 slipon: send ppp\r wait PPP_STARTED 25 if $errlvl != 0 goto error2 print CONNECTION completed... mode ppp exit 0 error: print Total failure to interact with MODEM!!! quit 1 # basic failure in working with modem, etc... connect_fail: print Couldnt detect a "CONNECT 14400" kind of string after dial in... quit 3 error2: print Modems could nicely link up. But remote server HAS CHANGED syntax/ interaction sequence... quit 4 # basic failure in working with modem, etc... #=================================== EOF ===================================