stone-2.3d-2.3.2.7/0002755000023200056710000000000010751714360012633 5ustar sengokuklabstone-2.3d-2.3.2.7/README.en.txt0000644000023200056710000003475610751714360014747 0ustar sengokuklab Simple Repeater stone version 2.3e Copyright(c)1995-2008 by Hiroaki Sengoku sengoku@gcd.org Stone is a TCP/IP repeater in the application layer. It repeats TCP and UDP from inside to outside of a firewall, or from outside to inside. Stone has following features: 1. Stone supports Win32. Formerly, UNIX machines are used as firewalls, but recently WindowsNT machines are used, too. You can easily run stone on WindowsNT and Windows95. Of course, available on Linux, FreeBSD, BSD/OS, SunOS, Solaris, HP-UX and so on. 2. Simple. Stone's source code is only 10000 lines long (written in C language), so you can minimize the risk of security holes. 3. Stone supports SSL. Using OpenSSL (http://www.openssl.org/), stone can encrypt/decrypt. Client verifications, and server verifications are also supported. Stone can send a substring of the subject of the certificate to the destination. 4. Stone is a http proxy. Stone can also be a tiny http proxy. 5. POP -> APOP conversion. With stone and a mailer that does not support APOP, you can access to an APOP server. 6. Stone supports IPv6. Stone can convert IP and IPv6 each other. With stone, you can use IP-only software on IPv6 network. HOWTO USE stone [-C ] [-P ] [-Q ] [-N] [-d] [-p] [-n] [-u ] [-f ] [-l] [-L ] [-a ] [-i ] [-X ] [-T ] [-A ] [-r] [-x [,][-]... ... --] [-s ... --] [-b [=]... : :] [-B : :... --] [-I ] [-o ] [-g ] [-t ] [-D] [-c ] [-q ] [-z ] [-M install ] [-M remove ] [-- ]... If the ``-C '' flag is used, the program read these options and ``''s from the configuration file ``''. If the ``-P '' flag is used, the program executes pre-processor to read the configuration file. ``-Q '' can be used to pass options to the pre-processor. If the ``-N'' flag is used, stone will terminate after parsing options without opening the ports. If the ``-d'' flag is used, then increase the debug level. If the ``-p'' flag is used, data repeated by stone are dumped. If the ``-n'' is used, IP addresses and service port numbers are shown instead of host names and service names. If the ``-u '' flag (``'' is integer) is used, the program memorize ``'' UDP sources simultaneously. The default value is 100. If the ``-f '' flag (``'' is integer) is used, the program spawn ``'' child processes. The default behavior is not to spawn any child processes. If the ``-l'' flag is used, the program sends error messages to the syslog instead of stderr. If the ``-L '' (``'' is a file name) flag is used, the program writes error messages to the file. If the ``-a '' flag is used, the program writes accounting to the file. If the ``-i '' flag is used, the program writes its process ID to the file. The ``-X '' flag alters the buffer size of the repeater. The default value is 1000 bytes. If the ``-T '' is used, the timeout of TCP sessions can be specified to ``'' sec. Default: 600. The ``-A'' flag specifies the maximum length the queue of pending connections may grow to. Default: 50. The ``-r'' flag is used, SO_REUSEADDR is set on the socket of . Using the ``-x [,][-]... ... --'' flag, the http proxy (described later) can only connect to :. If more than one ``-x ... --'' flags are designated, the posterior one whose list matches the connecting port. If the ``-x --'' is used, prior ``-x'' flags are ignored. The ``-b : :'' flag designates the backup destination for :. The program checks every seconds whether : is connectable, using the health check script defined by ``-s'' flag described below. If not, the backup is used instead. Alternative can be checked, using ``host='' and alternative , using ``port=''. The ``-s ... --'' flag defines the health check script. Sending , then checks whether the response match the regular expression . The ``-B : :... --'' is for the destination group. If the destination of is :, the program chooses a destination randomly from the group. The destination : that is designated by ``-b'' flag and turned out unhealthy, is excluded from the group. The ``-I '' designates the interface used as the source address of the connection to the desctination. If the ``-o '' or ``-g '' flag is used, the program set its uid or gid to ``'' respectively. If the ``-t '' flag (``'' is a directory) is used, the program change its root to the directory. If the ``-D'' flag is used, stone runs as a daemon. The ``-c '' flag designates the directory for core dump. The ``-M install '' and the ``-M remove '' flags are for NT service. ``'' is the service name. Start the service using the command: net start . To install stone service as the name ``repeater'', for example: C:\>stone -M install repeater -C C:\stone.cfg C:\>net start repeater The ``-q '' and the ``-z '' flags are for SSL encryption. The ``-q '' is for the client mode, that is, when stone connects to the other SSL server as a SSL client. The ``-z '' if for the server mode, that is, when other SSL clients connect to the stone. ``'' is one of the following. default reset SSL options to the default. Using multiple , different SSL options can be designated for each . verbose verbose mode. verify require SSL certificate to the peer. verify,once request a client certificate on the initial TLS/SSL handshake. (-z only) verify,ifany The certificate returned (if any) is checked. (-z only) verify,none never request SSL certificate to the peer. crl_check lookup CRLs. crl_check_all lookup CRLs for whole chain. uniq if the serial number of peer's SSL certificate is different from the previous session, deny it. re= The certificate of the peer must satisfy the . is the depth. re0 means the subject of the certificate, and re1 means the issure. The maximum of is 9. if is negative, re-1 means the root CA and re-2 means its child CA. depth= The maximum of the certificate chain. If the peer's certificate exceeds , the verification fails. The maximum of is 9. tls1 Just use TLSv1 protocol. ssl3 Just use SSLv3 protocol. ssl2 Just use SSLv2 protocol. no_tls1 Turn off TLSv1 protocol. no_ssl3 Turn off SSLv3 protocol. no_ssl2 Turn off SSLv2 protocol. sni Server Name Indication (SNI). servername= The name of the server indicated by SNI. bugs Switch on all SSL implementation bug workarounds. serverpref Use server's cipher preferences (only SSLv2). sid_ctx= Set session ID context. passfile= The filename of the file containing password of the key passfilepat= The pattern of the filename key= The filename of the secret key of the certificate. keypat= The pattern of the filename cert= The filename of the certificate. certpat= The pattern of the filename certkey= The filename of the certificate with the secret key. certkeypat= The pattern of the filename CAfile= The filename of the certificate of the CA. CApath= The directory of the certificate files. pfx= The filename of the PKCS#12 bag. pfxpat= The pattern of the filename store= [Windows] Use the secret key in the Cert Store. designate by "SUBJ:" or "THUMB:" storeCA [Windows] Use CA certificates in the Cert Store. cipher= The list of ciphers. lb= change the destination according to the certificate of the peer. The number calculated from the matched string to the th ( ... ) in the ``regex'' of SSL options (mod ) is used to select the destination from the destination group defined by ``-B'' flag. ``'' is one of the following. Multiple ``'' can be designated, separated by ``--''. (1) : [...] (2) : : [...] (3) proxy [...] (4) :/http [...] (5) :/proxy
[...] (6) health [...] The program repeats the connection on port ``'' to the other machine ``'' port ``''. If the machine, on which the program runs, has two or more interfaces, type (2) can be used to repeat the connection on the specified interface ``''. You can also specify path name that begins with ``/'' or ``./'', instead of ``:'' so that the program handles a unix domain socket. Type (3) is a http proxy. Specify the machine, on which the program runs, and port ``'' in the http proxy settings of your WWW browser. Extentions can be added to the ``proxy'' like ``/''. is: v4only limit the destination within IP addresses. v6only limit the destination within IPv6 addresses. Type (4) relays stream over http request. ``'' is the request specified in HTTP 1.0. In the ``'', ``\'' is the escape character, and the following substitution occurs. \n newline (0x0A) \r return (0x0D) \t tab (0x09) \\ \ itself (0x5C) \a the IP address of the client connecting to the stone. \A : \d the destination IP address \D : (for transparent proxy) \u uid (number) of the client \U user name of the client \g gid (number) of the client \G group name of the client \u \U \g \G are valid in the case of unix domain socket \0 the serial number of peer's SSL certificate. \1 - \9 the matched string in the ``regex'' of SSL options. \?1\:\/ if \1 (\2 - \9 in a similar way) is not null, , otherwise . Type (5) repeats http request with ``
'' in the top of request headers. The above escapes can be also used. If ``/mproxy'' is designated instead of ``/proxy'', ``
'' is added to each request headers. Type (6) designates the port that other programs can check whether the stone runs `healthy' or not. Following commands are available to check the stone. HELO returns the status of the stone STAT # of threads, mutex conflicts FREE length of free lists CLOCK seconds passed CVS_ID CVS ID CONFIG content of the configuration file STONE configuration of each stones LIMIT check the value of is less than ``'' is one of the following: PAIR the number of ``pair'' CONN the number of ``conn'' ESTABLISHED seconds passed since the last conn established READWRITE seconds passed since the last read/write ASYNC the number of threads The response of the stone is 2xx when normal, or 5xx when abnormal on the top of line. If the ``'' are used, only machines or its IP addresses listed in ``'' separated by space character can connect to the program and to be repeated. Extentions can be added to the ``'' like ``/,...''. is: You can designate the length of prefix bits of the netmask, so that only machines on specified. In the case of class C network 192.168.1.0, for example, use ``192.168.1.0/24''. v4 is resolved as the IP address. v6 is resolved as the IPv6 address. p the data repeated by the program are dumped, only if it was connected by the machines specified by . is the dump mode, equivalent to the number of ``-p'' options. Use ``!'' instead of ``'', to deny machines by following ``''. Extentions can be added to the ``'' like ``/,...''. is: udp repeats UDP instead of TCP. ssl forwards with encryption. v6 connects to the destination using IPv6. base forwards with MIME base64 encoding. Extentions can be added to the ``'' like ``/,...''. is: udp repeats UDP instead of TCP. apop converts POP to APOP. The conversion is derived from the RSA Data Security, Inc. MD5 Message-Digest Algorithm. ssl forwards with decryption. v6 accepts connection using IPv6. If is omitted like (1), IP is also acceptable. v6only accepts connection using IPv6 only. Even if is omitted like (1), IP is not acceptable. base forwards with MIME base64 decoding. http relays stream over http. ident identifies the owner of the incoming connection on the peer using ident protocol (RFC1413). EXAMPLES outer: a machine in the outside of the firewall inner: a machine in the inside of the firewall fwall: the firewall on which the stone is executed stone outer:telnet 10023 Repeats the telnet protocol to ``outer''. Run ``telnet fwall 10023'' on ``inner''. stone outer:domain/udp domain/udp Repeats the DNS query to ``outer''. Run ``nslookup - fwall'' on ``inner''. stone outer:ntp/udp ntp/udp Repeats the NTP to ``outer''. Run ``ntpdate fwall'' on ``inner''. stone localhost:http 443/ssl Make WWW server that supports ``https''. Access ``https://fwall/'' using a WWW browser. stone localhost:telnet 10023/ssl Make telnet server that supports SSL. Run ``SSLtelnet -z ssl fwall 10023'' on ``inner''. stone proxy 8080 http proxy. stone outer:110/apop 110 connect to inner:pop using a mailer that does not support APOP. Where fwall is a http proxy (port 8080): stone fwall:8080/http 10023 'POST http://outer:8023 HTTP/1.0' stone localhost:telnet 8023/http Run stones on ``inner'' and ``outer'' respectively. Relays stream over http. stone fwall:8080/proxy 9080 'Proxy-Authorization: Basic c2VuZ29rdTpoaXJvYWtp' for browser that does not support proxy authorization. HOMEPAGE The official homepage of stone is: http://www.gcd.org/sengoku/stone/ COPYRIGHT All rights about this program ``stone'' are reserved by the original author, Hiroaki Sengoku. The program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License (GPL). Furthermore you can link it with openssl. NO WARRANTY This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY. #2939 http://www.gcd.org/sengoku/ Hiroaki Sengoku stone-2.3d-2.3.2.7/README.txt0000644000023200056710000004205410751714360014334 0ustar sengokuklab Simple Repeater stone version 2.3e Copyright(c)1995-2008 by Hiroaki Sengoku sengoku@gcd.org stone $B$O!"%"%W%j%1!<%7%g%s%l%Y%k$N(B TCP & UDP $B%j%T!<%?!<$G$9!#%U%!%$%"(B $B%&%)!<%k$NFb$+$i30$X!"$"$k$$$O30$+$iFb$X!"(BTCP $B$"$k$$$O(B UDP $B$rCf7Q$7$^$9!#(B stone $B$K$O0J2<$N$h$&$JFCD'$,$"$j$^$9!#(B 1. Win32 $B$KBP1~$7$F$$$k(B $B0JA0$O(B UNIX $B%^%7%s$G9=@.$5$l$k$3$H$,B?$+$C$?%U%!%$%"%&%)!<%k$G$9(B $B$,!":G6a$O(B WindowsNT $B$,;H$o$l$k%1!<%9$,A}$($F$-$^$7$?!#(Bstone $B$O(B WindowsNT $B$"$k$$$O(B Windows95 $B>e$G.8B$K$G$-$^$9!#(B 3. SSL $BBP1~(B OpenSSL (http://www.openssl.org/) $B$r;H$&$3$H$K$h$j!"0E9f2=(B/$BI|9f(B $B$7$FCf7Q$G$-$^$9!#$^$?!"%/%i%$%"%s%HG'>Z$*$h$S%5!<%PG'>Z$r%5%]!<(B $B%H$7$F$$$^$9!#$5$i$K!"G'>Z$K$h$C$FF@$i$l$k>ZL@=q$N%5%V%8%'%/%H$N(B $B0lIt$r!"Cf7Q@h$XAw$k$3$H$b$G$-$^$9!#(B 4. http proxy $B4J0W7?(B http proxy $B$H$7$F$b;H$&$3$H$,$G$-$^$9!#(B 5. POP -> APOP $BJQ49(B APOP $B$KBP1~$7$F$$$J$$%a!<%i$H(B stone $B$r;H$&$3$H$G!"(BAPOP $B%5!<%P$X(B $B%"%/%;%9$G$-$^$9!#(B 6. IPv6 $BBP1~(B IP/IPv6 $BJQ49$7$FCf7Q$9$k$3$H$,$G$-$^$9!#(BIPv6 $B$KBP1~$7$F$$$J$$(B $B%=%U%H%&%'%"$r] [-P ] [-Q ] [-N] [-d] [-p] [-n] [-u ] [-f ] [-l] [-L ] [-a ] [-i ] [-X ] [-T ] [-A ] [-r] [-x [,][-]... ... --] [-s ... --] [-b [=]... : :] [-B : :... --] [-I ] [-o ] [-g ] [-t ] [-D] [-c ] [-q ] [-z ] [-M install ] [-M remove ] [-- ]... -C $B$O%*%W%7%g%s$*$h$S(B $B$r%3%^%s%I%i%$%s$G;XDj$9$k$+$o$j$K@_(B $BDj%U%!%$%k$+$iFI$_9~$_$^$9!#(B-P $B$O@_Dj%U%!%$%k$rFI$_9~$`:]$N%W%j(B $B%W%m%;%C%5$r;XDj$7$^$9!#%W%j%W%m%;%C%5$XM?$($k0z?t$O(B -Q $B$G;XDj$G(B $B$-$^$9!#(B-N $B$r;XDj$9$k$H!"%3%^%s%I%i%$%s$*$h$S@_Dj%U%!%$%k$rFI$_(B $B9~$s$@8e!"=*N;$7$^$9!#$D$^$j%]!<%H$r3+$/$3$HL5$/@_Dj%U%!%$%k$N(B $B%A%'%C%/$r9T$J$&$3$H$,$G$-$^$9!#(B $B%*%W%7%g%s$H$7$F(B -d $B$r;XDj$9$k$H!"%G%P%C%0%l%Y%k$rA}2C$5$;$^$9!#(B -p $B$r;XDj$9$k$HCf7Q$7$?%G!<%?$r%@%s%W$7$^$9!#(B-n $B$r;XDj$9$k$H!"%[(B $B%9%HL>$d%5!<%S%9L>$NBe$o$j$K(B IP $B%"%I%l%9$d%5!<%S%9HV9f$rI=<($7$^(B $B$9!#(B -u $B%*%W%7%g%s$OF1;~$K5-21$G$-$k(B UDP $B$NH/?.85$N:GBg?t$r;XDj$7$^$9!#(B $B%G%U%)%k%H$O(B 100 $B$G$9!#(B-f $B%*%W%7%g%s$O;R%W%m%;%9$N?t$r;XDj$7$^$9!#(B $B%G%U%)%k%H$O;R%W%m%;%9L5$7$G$9!#(B -l $B$r;XDj$9$k$H!"%(%i!<%a%C%;!<%8Ey$r(B syslog $B$X=PNO$7$^$9!#(B-L $B$r(B $B;XDj$9$k$H!"%(%i!<%a%C%;!<%8Ey$r(B file $B$X=PNO$7$^$9!#(B-a $B$r;XDj$9(B $B$k$H!"%"%/%;%9%m%0$r(B file $B$X=PNO$7$^$9!#(B-i $B$O(B stone $B$N%W%m%;%9(B ID $B$r=PNO$9$k%U%!%$%k$r;XDj$7$^$9!#(B -X $B$OCf7Q$r9T$J$&:]$N%P%C%U%!$NBg$-$5$r;XDj$7$^$9!#%G%U%)%k%H$O(B 1000 $B%P%$%H$G$9!#(B-T $B$r;XDj$9$k$H(B TCP $B%;%C%7%g%s$N%?%$%`%"%&%H$NIC(B $B?t$rJQ99$G$-$^$9!#%G%U%)%k%H$O(B 600 (10 $BJ,(B) $B$G$9!#(B-A $B$r;XDj$9$k$H(B listen $B8F$S=P$7$NL$=hM}@\B3%-%e!<$N:GBgD9$rJQ99$G$-$^$9!#%G%U%)%k(B $B%H$O(B 50 $B$G$9!#(B-r $B$r;XDj$9$k$H(B $B$N%=%1%C%H$K(B SO_REUSEADDR $B$r@_(B $BDj$7$^$9!#(B -x $B$r;XDj$9$k$H(B http proxy $B$N@\B3@h$r@)8B$G$-$^$9!#@\B3@h$N%]!<(B $B%HHV9f$N%j%9%H(B [,][-]... $B$*$h$S@\B3@h%[%9%H$N(B $B%j%9%H(B ... $B$r;XDj$7$^$9!#(B-x $B$rJ#?t;XDj$9$k$H!":G8e$K;XDj(B $B$7$?$b$N$+$i=g$K!"%]!<%HHV9f$N%j%9%H$,%^%C%A$9$k$b$N$r8!:w$7$^$9!#(B -x -- $B$r;XDj$9$k$H!"$=$l0JA0$N$b$N$O8!:wBP>]$H$J$j$^$;$s!#(B -b $B$OCf7Q@h(B : $B$K@\B3$G$-$J$$$H$-$N%P%C%/%"%C%W$H$7(B $B$F(B : $B$r;XDj$7$^$9!#$9$J$o$A(B $BIC$4$H$K(B : $B$KBP$9$k%X%k%9%A%'%C%/(B ($B8e=R$9$k(B -s $B%*%W%7%g%s$G(B $B@_Dj(B) $B$,@.8y$9$k$+%A%'%C%/$7!"$b$7%A%'%C%/$K<:GT$7$?>l9g$O!"Cf7Q(B $B@h$r(B : $B$XJQ99$7$^$9!#(B $B$H$7$F!V(Bhost$B!W$r;XDj$9(B $B$k$3$H$K$h$j!"(B $B$H$O0[$J$k%[%9%H$r%A%'%C%/$9$k$3$H$,$G$-(B $B$^$9!#F1MM$K!"(B $B$H$7$F!V(Bport$B!W$r;XDj$9$k$3$H$K$h$j!"0[$J$k%]!<(B $B%H$r%A%'%C%/$9$k$3$H$,$G$-$^$9!#(B -s $B$O%X%k%9%A%'%C%/$N%9%/%j%W%H$r@_Dj$7$^$9!#(B $B$rAw?.8e!"%l(B $B%9%]%s%9$,!"@55,I=8=(B $B$K%^%C%A$9$k$+3NG'$7$^$9!#(B -B $B$OCf7Q@h%0%k!<%W$N;XDj$G$9!#Cf7Q@h$,(B : $B$G$"$k>l9g!"(B $B$3$N%0%k!<%W$NCf$+$i%i%s%@%`$K0l$D$NCf7Q@h$rA*$s$GCf7Q$7$^$9!#(B-b $B%*%W%7%g%s$G@_Dj:Q$_$NCf7Q@h$G!"%X%k%9%A%'%C%/$K<:GT$7$?$b$N$O!"(B $BA*Br;^$+$i=|30$7$^$9!#(B -I $B$OCf7Q@h$X@\B3$9$k:]$KMQ$$$k%$%s%?%U%'!<%9$r;XDj$7$^$9!#(B -o $B$H(B -g $B$O$=$l$>$l%f!<%6(B ID $B$H%0%k!<%W(B ID $B$r;XDj$7$^$9!#(BID $B$O?t(B $B;z$N$_;XDj2DG=$G$9!#(B-t $B$r;XDj$9$k$H!"(Bdir $B$X(B chroot $B$7$^$9!#(B-D $B$r(B $B;XDj$9$k$H!"(Bstone $B$r%G!<%b%s$H$7$F5/F0$7$^$9!#(B-c $B$O%3%"%@%s%W$r(B $B9T$J$&%G%#%l%/%H%j$r;XDj$7$^$9!#(B -M $B$O(B stone $B$r(B NT $B%5!<%S%9$H$7$FEPO?(B/$B:o=|$9$k$?$a$N%*%W%7%g%s$G(B $B$9!#%5!<%S%9L>(B $B$r;XDj$7$^$9!#%5!<%S%9$H$7$FEPO?$7$?8e!"(B net start $B%3%^%s%I$rstone -M install repeater -C C:\stone.cfg C:\>net start repeater -q $B$*$h$S(B -z $B$O!"(BSSL $B0E9f2=(B/$BI|9f(B $B$N%*%W%7%g%s$G$9!#(B-q $B$O!"(Bstone $B$,(B SSL $B%/%i%$%"%s%H$H$7$F!"B>$N(B SSL $B%5!<%P$X@\B3$9$k$H$-!"$9$J$o(B $B$ACf7Q@h$,(B SSL $B%5!<%P$N;~$N!"(BSSL $B%*%W%7%g%s$G$9!#(B-z $B$O(B stone $B$,(B SSL $B%5!<%P$H$7$F!"B>$N(B SSL $B%/%i%$%"%s%H$+$i$N@\B3$r $B$O(B SSL $B%*%W%7%g%s$G!" $B$r;XDj$9$k:]!"(B $BKh$K0[$J$k(B SSL $B%*(B $B%W%7%g%s$r;XDj$9$k$3$H$,$G$-$^$9!#(B verbose $B%G%P%C%0MQJ8;zNs$r%m%0$K=PNO$7$^$9!#(B verify SSL $B@\B3AjZL@=q$rMW5a$7$^$9!#(B verify,once $B%;%C%7%g%s3+;O;~$K0lEY$@$1!"(B SSL $B%/%i%$%"%s%H$K>ZL@=q$rMW5a$7$^$9!#(B(-z $B@lMQ(B) verify,ifany SSL $B%/%i%$%"%s%H$+$i>ZL@=q$,Aw$i$l$F$-$?$H$-$N$_(B $BG'>Z$7$^$9!#Aw$i$l$F$3$J$$>l9g$OG'>Z$;$:$K(B $B%;%C%7%g%s$r3+;O$7$^$9!#(B(-z $B@lMQ(B) verify,none SSL $B@\B3AjZL@=q$rMW5a$7$^$;$s!#(B crl_check CRL $B$r%A%'%C%/$7$^$9!#(B crl_check_all $B>ZL@=q%A%'!<%s$NA4$F$K$*$$$F(B CRL $B$r%A%'%C%/$7$^$9!#(B uniq SSL $B@\B3AjZL@=q$N%7%j%"%kHV9f$,A02s$N(B $B@\B3$H0[$J$k>l9g!"@\B3$r5qH]$7$^$9!#(B re= SSL $B>ZL@=q$N%A%'!<%s$,K~$?$9$Y$-@55,I=8=$r;XDj$7$^$9!#(B $B$O(B depth $B$G$9!#(Bre0 $B$,>ZL@=q$N%5%V%8%'%/%H!"(B re1 $B$,$=$NH/9T $B$O(B 9 $B$^$G;XDj$G$-$^$9!#(B $B$,Ii$NCM$N>l9g$O!"(Bre-1 $B$,(B root CA $B$G!"(B re-2 $B$,$=$N;R(B CA $B$r0UL#$7$^$9!#(B depth= SSL $B>ZL@=q%A%'!<%s$ND9$5$N:GBgCM$r;XDj$7$^$9!#(B $B%A%'!<%s$ND9$5$,$3$NCM$r1[$($k$HG'>Z$,<:GT$7$^$9!#(B $B$N:GBgCM$O(B 9 $B$G$9!#(B tls1 $B%W%m%H%3%k$H$7$F(B TLSv1 $B$rMQ$$$^$9!#(B ssl3 $B%W%m%H%3%k$H$7$F(B SSLv3 $B$rMQ$$$^$9!#(B ssl2 $B%W%m%H%3%k$H$7$F(B SSLv2 $B$rMQ$$$^$9!#(B no_tls1 $B%W%m%H%3%k$NA*Br;^$+$i(B TLSv1 $B$r30$7$^$9!#(B no_ssl3 $B%W%m%H%3%k$NA*Br;^$+$i(B SSLv3 $B$r30$7$^$9!#(B no_ssl2 $B%W%m%H%3%k$NA*Br;^$+$i(B SSLv2 $B$r30$7$^$9!#(B sni $B%5!<%PL>DLCN(B (Server Name Indication) $B$r9T$J$$$^$9!#(B servername= SNI $B$GDLCN$9$k%5!<%PL>$r;XDj$7$^$9!#(B bugs SSL $B$N SSL $B%;%C%7%g%s(B ID $B%3%s%F%-%9%H$r@_Dj$7$^$9!#(B passfile= $BHkL)80$N%Q%9%U%l!<%:$r3JG<$7$?%U%!%$%k$r;XDj$7$^$9!#(B passfilepat= $B%U%!%$%kL>$N%Q%?!<%s$r;XDj$7$^$9!#(B key= $B>ZL@=q$NHkL)80%U%!%$%k$r;XDj$7$^$9!#(B keypat= $B%U%!%$%kL>$N%Q%?!<%s$r;XDj$7$^$9!#(B cert= $B>ZL@=q%U%!%$%k$r;XDj$7$^$9!#(B certpat= $B%U%!%$%kL>$N%Q%?!<%s$r;XDj$7$^$9!#(B certkey= $BHkL)80IU>ZL@=q%U%!%$%k$r;XDj$7$^$9!#(B certkeypat= $B%U%!%$%kL>$N%Q%?!<%s$r;XDj$7$^$9!#(B CAfile= $BG'>Z6I$N>ZL@=q%U%!%$%k$r;XDj$7$^$9!#(B CApath= $BG'>Z6I$N>ZL@=q$,$"$k%G%#%l%/%H%j$r;XDj$7$^$9!#(B pfx= PKCS#12 $B%U%!%$%k$r;XDj$7$^$9!#(B pfxpat= $B%U%!%$%kL>$N%Q%?!<%s$r;XDj$7$^$9!#(B store= [Windows] $B>ZL@=q%9%H%"Fb$NHkL)80IU>ZL@=q$r;XDj!#(B "SUBJ:" $B$"$k$$$O(B "THUMB:" storeCA [Windows] $B>ZL@=q%9%H%"Fb$NG'>Z6I>ZL@=q$r;HMQ$7$^$9!#(B cipher= $B0E9f2=%"%k%4%j%:%`$N%j%9%H$r;XDj$7$^$9!#(B lb= SSL $B>ZL@=q$N(B CN $B$K1~$8$FCf7Q@h$r@Z$jBX$($^$9!#(B SSL $B%*%W%7%g%s$N(B re= $B$G;XDj$7$?@55,I=8=Cf!"(B $BHVL\$N(B ( ... ) $BFb$N@55,I=8=$K%^%C%A$7$?J8;z(B $BNs$+$i;;=P$7$??tCM$N>jM>(B $B$K4p$E$$$F!"(B-B $B%*%W(B $B%7%g%s$G;XDj$7$?Cf7Q@h%0%k!<%W$NCf$+$iCf7Q@h$rA*(B $B$S$^$9!#(B $B$O $B$O!V(B--$B!W$G6h@Z$k$3$H$K$h$j!"J#?t8D(B $B;XDj$G$-$^$9!#(B (1) : [...] (2) : : [...] (3) proxy [...] (4) :/http [...] (5) :/proxy
[...] (6) health [...] stone $B$r $B$X$N@\B3$r!"B>$N%^%7(B $B%s(B $B$N%]!<%H(B $B$XCf7Q$7$^$9!#%$%s%?%U%'!<%9$rJ#?t;}$D(B $B%^%7%s$G$O!"(B(2) $B$N$h$&$K%$%s%?%U%'!<%9$N%"%I%l%9(B $B$r;XDj(B $B$9$k$3$H$K$h$j!"FCDj$N%$%s%?%U%'!<%9$X$N@\B3$N$_$rE>Aw$9$k$3$H$,(B $B$G$-$^$9!#(B: $B$NBe$o$j$K!"!V(B/$B!W$J$$$7!V(B./$B!W$+$i;O$^$k(B $B%Q%9L>$r;XDj$9$k$3$H$K$h$j!"(BUNIX $B%I%a%$%s%=%1%C%H$r07$&$3$H$b$G(B $B$-$^$9!#(B (3) $B$O!"(Bhttp proxy $B$G$9!#(BWWW $B%V%i%&%6$N(B http proxy $B$N@_Dj$G!"(B stone $B$r $B$r;XDj$7$^$9!#(B $B!V(Bproxy$B!W$K$O!"!V(B/$B!W$KB3$1$F0J2<$N3HD%;R$rIU$1$k$3$H$,$G$-$^$9!#(B v4only proxy $B$N@\B3@h$r(B IP $B%"%I%l%9$K8BDj$7$^$9!#(B v6only proxy $B$N@\B3@h$r(B IPv6 $B%"%I%l%9$K8BDj$7$^$9!#(B (4) $B$O!"(Bhttp $B%j%/%(%9%H$K$N$;$FCf7Q$7$^$9!#(B $B$O(B HTTP 1.0 $B$G5,Dj$5$l$k%j%/%(%9%H$G$9!#%j%/%(%9%HJ8;zNsCf!"!V(B\$B!W$O%(%9%1!<(B $B%WJ8;z$G$"$j!"(B \g $B@\B385$N%0%k!<%W(BID ($BHV9f(B) \G $B@\B385$N%0%k!<%WL>(B \u \U \g \G $B$O(B UNIX $B%I%a%$%s%=%1%C%H$N>l9g$N$_(B \0 SSL $B>ZL@=q$N%7%j%"%kHV9f(B \1 - \9 SSL $B%*%W%7%g%s$N(B re= $B$G;XDj$7$?@55,I=8=Cf!"(B ( ... ) $BFb$N@55,I=8=$K%^%C%A$7$?J8;zNs(B \?1\:\/ $B$b$7(B \1 (\2 - \9 $B$bF1MM(B) $B$NJ8;zNs$,!"6uJ8;zNs$G(B $B$J$1$l$P(B $B!"6uJ8;zNs$G$"$l$P(B (5) $B$O!"(Bhttp $B%j%/%(%9%H%X%C%@$N@hF,$K(B
$B$rDI2C$7$FCf7Q$7(B $B$^$9!#(B(4) $B$HF1MM$N%(%9%1!<%W$r;H$&$3$H$,$G$-$^$9!#!V(B/proxy$B!W$NBe(B $B$o$j$K!V(B/mproxy$B!W$r;XDj$9$k$H!"%j%/%(%9%H%X%C%@$4$H$K(B
$B$rDI2C$7$^$9!#(B (6) $B$O!"(Bstone $B$,@5>o$KF0:n$7$F$$$k$+8!::$9$k$?$a$N%]!<%H$N;XDj$G(B $B$9!#(B $B$G;XDj$7$?%]!<%H$K@\B3$7$F0J2<$N%3%^%s%I$rAw?.$9$k$H!"(B stone $B$N>uBV$,JV$5$l$^$9!#(B HELO $BG$0U$NJ8;zNs(B stone, pair, trash $BEy$N8D?t(B STAT $B%9%l%C%I$N8D?t(B, mutex $B%3%s%U%j%/%H2s?t(B FREE free $B%j%9%HD9(B CLOCK $B7P2aIC?t(B CVS_ID CVS $B$N(B ID CONFIG config $B%U%!%$%k$NFbMF(B STONE $B3F(B stone $B$N@_DjFbMF(B LIMIT $BJQ?t(B $B$NCM$,(B $BL$K~$+D4$Y$k(B $B$Oo;~$O(B 200 $BHVBf!"0[>o;~$O(B 500 $BHVBf$N?tCM$,(B $B@hF,$K$D$-$^$9!#(B $B$rNs5s$9$k$3$H$K$h$j!"(Bstone $B$X@\B32DG=$J%^%7%s$r@)8B$9$k(B $B$3$H$,$G$-$^$9!#%^%7%sL>!"$"$k$$$O$=$N(B IP $B%"%I%l%9$r6uGr$G6h@Z$C(B $B$F;XDj$9$k$H!"$=$N%^%7%s$+$i$N@\B3$N$_$rCf7Q$7$^$9!#(B $B$K$O!"!V(B/$B!W$KB3$1$F0J2<$N3HD%;R$rIU$1$k$3$H$,$G$-$^$9!#(B $BJ#?t$N3HD%;R$r;XDj$9$k$H$-$O!V(B,$B!W$G6h@Z$j$^$9!#(B $B%M%C%H%o!<%/!&%^%9%/$N%S%C%H?t$r;XDj$9$k$3$H$K$h$j!"FCDj(B $B$N%M%C%H%o!<%/$N%^%7%s$+$i$N@\B3$r5v2D$9$k$3$H$,$G$-$^$9!#(B $BNc$($P!"%/%i%9(B C $B$N%M%C%H%o!<%/(B 192.168.1.0 $B$N>l9g$O!"(B $B!V(B192.168.1.0/24$B!W$H;XDj$7$^$9!#(B v4 $B$r(B IP $B%"%I%l%9$H$7$F07$$$^$9!#(B v6 $B$r(B IPv6 $B%"%I%l%9$H$7$F07$$$^$9!#(B p $B$+$i$N@\B3$N$_!"Cf7Q$7$?%G!<%?$r%@%s%W$7$^$9!#(B $B$O%@%s%WJ}K!$N;XDj$G$9!#(B-p $B%*%W%7%g%s$N8D?t$KAjEv$7(B $B$^$9!#(B $B$NBe$o$j$K!V(B!$B!W$r;XDj$9$k$H!"8eB3$N(B $B$O@\B3$r5qH](B $B$9$k%^%7%s$N;XDj$K$J$j$^$9!#(B $B$K$O!"!V(B/$B!W$KB3$1$F0J2<$N3HD%;R$rIU$1$k$3$H$,$G$-$^$9!#(B $BJ#?t$N3HD%;R$r;XDj$9$k$H$-$O!V(B,$B!W$G6h@Z$j$^$9!#(B udp TCP $B$rCf7Q$9$kBe$o$j$K!"(BUDP $B$rCf7Q$7$^$9!#(B ssl SSL $B$G0E9f2=$7$FCf7Q$7$^$9!#(B v6 $BCf7Q@h$X(B IPv6 $B@\B3$7$^$9!#(B base MIME base64 $B$GId9f2=$7$FCf7Q$7$^$9!#(B $B$K$O!"!V(B/$B!W$KB3$1$F0J2<$N3HD%;R$rIU$1$k$3$H$,$G$-$^$9!#(B $BJ#?t$N3HD%;R$r;XDj$9$k$H$-$O!V(B,$B!W$G6h@Z$j$^$9!#(B udp TCP $B$rCf7Q$9$kBe$o$j$K!"(BUDP $B$rCf7Q$7$^$9!#(B apop POP $B$r(B APOP $B$XJQ49$7$FCf7Q$7$^$9!#(B $BJQ49$K$O(B RSA Data Security $B $B$r;XDj$7$J$$>l9g$O!"(BIP $B@\B3$b $B$r;XDj$7$J$$>l9g$b!"(BIP $B@\B3$r$r>H2q$7$^$9!#(B $BNc(B outer: $B%U%!%$%"%&%)!<%k$N30B&$K$"$k%^%7%s(B inner: $B%U%!%$%"%&%)!<%k$NFbB&$K$"$k%^%7%s(B fwall: $B%U%!%$%"%&%)!<%k(B. $B$3$N%^%7%s>e$G(B stone $B$r$l(B stone $B$rZ$KBP1~$7$F$$$J$$%V%i%&%6MQ(B $B%[!<%`%Z!<%8(B stone $B$N8x<0%[!<%`%Z!<%8$Ol9g$O!"2~JQ!&J#@=$K@)8B(B $B$O$"$j$^$;$s!#G[I[$9$k>l9g$O(B GPL $B$K=>$C$F2<$5$$!#$^$?!"(Bopenssl $B$H%j%s%/$7$F;HMQ$9$k$3$H$r5v2D$7$^$9!#(B $BL5J]>Z(B $B$3$N(B stone $B$OL5J]>Z$G$9!#$3$N(B stone $B$r;H$C$F@8$8$?$$$+$J$kB;32$K(B $BBP$7$F$b!"86Cx:n\$7$/$O(B GPL $B$r;2>H$7$F2<(B $B$5$$!#(B #2939 $B@g@P(B $B9@L@(B http://www.gcd.org/sengoku/ Hiroaki Sengoku stone-2.3d-2.3.2.7/stone.c0000644000023200056710000102733410751714360014137 0ustar sengokuklab/* * stone.c simple repeater * Copyright(c)1995-2008 by Hiroaki Sengoku * Version 1.0 Jan 28, 1995 * Version 1.1 Jun 7, 1995 * Version 1.2 Aug 20, 1995 * Version 1.3 Feb 16, 1996 relay UDP * Version 1.5 Nov 15, 1996 for Win32 * Version 1.6 Jul 5, 1997 for SSL * Version 1.7 Aug 20, 1997 return packet of UDP * Version 1.8 Oct 18, 1997 pseudo parallel using SIGALRM * Version 2.0 Nov 3, 1997 http proxy & over http * Version 2.1 Nov 14, 1998 respawn & pop * Version 2.2 May 25, 2003 Posix Thread, XferBufMax, no ALRM, SSL verify * Version 2.3 Jan 1, 2006 LB, healthCheck, NonBlock, IPv6, sockaddr_un * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Emacs; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * * Usage: stone [-d] [-n] [-u ] [-f ] [-a ] [-L ] [-l] * [-o ] [-g ] [-t ] [-z ] [-D] * [-C ] [-P ] * [-- ]... * := : [...] * | proxy [...] * | :/http [...] * | :/proxy
[...] * := [/udp | /ssl | /apop] * := [:][/udp | /ssl | /http] * := [/] * * Any packets received by are passed to : * as long as these packets are sent from ... * if are not given, any hosts are welcome. * * Make: * gcc -o stone stone.c * or * cl -DWINDOWS stone.c /MT wsock32.lib * or * gcc -DWINDOWS -o stone.exe stone.c -lwsock32 * * POP -> APOP conversion * gcc -DUSE_POP -o stone stone.c md5c.c * or * cl -DWINDOWS -DUSE_POP stone.c md5c.c /MT wsock32.lib * or * gcc -DWINDOWS -DUSE_POP -o stone.exe stone.c md5c.c -lwsock32 * * md5c.c global.h md5.h are contained in RFC1321 * * Using OpenSSL * gcc -DUSE_SSL -I/usr/local/ssl/include -o stone stone.c \ * -L/usr/local/ssl/lib -lssl -lcrypto * or * cl -DWINDOWS -DUSE_SSL stone.c /MT wsock32.lib ssleay32.lib libeay32.lib * or * gcc -DWINDOWS -DUSE_SSL -o stone.exe stone.c -lwsock32 -lssl32 -leay32 * * -DUSE_POP use POP -> APOP conversion * -DUSE_SSL use OpenSSL * -DCPP preprocessor for reading config. file * -DIGN_SIGTERM ignore SIGTERM signal * -DUNIX_DAEMON fork into background and become a UNIX Daemon * -DNO_BCOPY without bcopy(3) * -DNO_SNPRINTF without snprintf(3) * -DNO_SYSLOG without syslog(2) * -DNO_RINDEX without rindex(3) * -DNO_STRDUP without strdup(3) * -DNO_THREAD without thread * -DNO_PID_T without pid_t * -DNO_SOCKLEN_T without socklen_t * -DNO_ADDRINFO without getaddrinfo * -DNO_FAMILY_T without sa_family_t * -DADDRCACHE cache address used in proxy * -DUSE_EPOLL use epoll(4) (Linux) * -DPTHREAD use Posix Thread * -DPRCTL use prctl(2) - operations on a process * -DOS2 OS/2 with EMX * -DWINDOWS Windows95/98/NT * -DNT_SERVICE WindowsNT/2000 native service */ #define VERSION "2.3e" static char *CVS_ID = "@(#) $Id: stone.c,v 2.3.2.7 2008/02/24 23:03:16 hiroaki_sengoku Exp $"; #include #include #include #include #include #include #include #include #ifdef USE_PCRE #include #else #include #endif typedef void (*FuncPtr)(void*); #ifdef WINDOWS #define FD_SETSIZE 4096 #include #include #include #if !defined(EINPROGRESS) && defined(WSAEWOULDBLOCK) #define EINPROGRESS WSAEWOULDBLOCK #endif #if !defined(EMSGSIZE) && defined(WSAEMSGSIZE) #define EMSGSIZE WSAEMSGSIZE #endif #if !defined(EADDRINUSE) && defined(WSAEADDRINUSE) #define EADDRINUSE WSAEADDRINUSE #endif #if !defined(ECONNABORTED) && defined(WSAECONNABORTED) #define ECONNABORTED WSAECONNABORTED #endif #if !defined(ECONNRESET) && defined(WSAECONNRESET) #define ECONNRESET WSAECONNRESET #endif #if !defined(EISCONN) && defined(WSAEISCONN) #define EISCONN WSAEISCONN #endif #include #ifdef NT_SERVICE #include #include "logmsg.h" #endif #define NO_SYSLOG #define NO_FORK #define NO_SETUID #define NO_CHROOT #define NO_GETTIMEOFDAY #define NO_FAMILY_T #define NO_UNIXDOMAIN #define ValidSocket(sd) ((sd) != INVALID_SOCKET) #define FD_SET_BUG #undef EINTR #define EINTR WSAEINTR #define NO_BCOPY #define bzero(b,n) memset(b,0,n) #define usleep(usec) Sleep(usec) #define ASYNC(func,arg) {\ if (Debug > 7) message(LOG_DEBUG, "ASYNC: %d", AsyncCount);\ waitMutex(AsyncMutex);\ AsyncCount++;\ freeMutex(AsyncMutex);\ if (_beginthread((FuncPtr)func, 0, arg) < 0) {\ message(LOG_ERR, "_beginthread error err=%d", errno);\ func(arg);\ }\ } #else /* ! WINDOWS */ #include #ifdef OS2 #define INCL_DOSSEMAPHORES #define INCL_DOSERRORS #include #include #define NO_SYSLOG #define NO_UNIXDOMAIN #define ASYNC(func,arg) {\ if (Debug > 7) message(LOG_DEBUG,"ASYNC: %d",AsyncCount);\ waitMutex(AsyncMutex);\ AsyncCount++;\ freeMutex(AsyncMutex);\ if (_beginthread((FuncPtr)func,NULL,32768,arg) < 0) {\ message(LOG_ERR,"_beginthread error err=%d",errno);\ func(arg);\ }\ } #else /* ! WINDOWS & ! OS2 */ #ifdef PTHREAD #include pthread_attr_t thread_attr; typedef void *(*aync_start_routine) (void *); #define ASYNC(func,arg) {\ pthread_t thread;\ int err;\ if (Debug > 7) message(LOG_DEBUG,"ASYNC: %d",AsyncCount);\ waitMutex(AsyncMutex);\ AsyncCount++;\ freeMutex(AsyncMutex);\ err=pthread_create(&thread,&thread_attr,(aync_start_routine)func,arg);\ if (err) {\ message(LOG_ERR,"pthread_create error err=%d",err);\ func(arg);\ } else if (Debug > 7) {\ message(LOG_DEBUG,"pthread ID=%lu",thread);\ }\ } #else /* ! PTHREAD */ #define ASYNC(func,arg) {\ waitMutex(AsyncMutex);\ AsyncCount++;\ freeMutex(AsyncMutex);\ func(arg);\ } #define NO_THREAD #endif /* ! PTHREAD */ #endif /* ! WINDOWS & ! OS2 */ #include #include #include #include #include #include #include #include #include #include #include #ifndef NO_SETUID #include #endif #ifdef PRCTL #include #endif #ifdef MEMLEAK_CHECK #include #endif typedef int SOCKET; #define INVALID_SOCKET -1 #define ValidSocket(sd) ((sd) >= 0) #define closesocket(sd) close(sd) #endif /* ! WINDOWS */ #define InvalidSocket(sd) (!ValidSocket(sd)) #ifdef USE_EPOLL #include #define EVSMAX 100 #else #ifdef FD_SET_BUG int FdSetBug = 0; #define FdSet(fd,set) do{if (!FdSetBug || !FD_ISSET((fd),(set))) \ FD_SET((fd),(set));}while(0) #else #define FdSet(fd,set) FD_SET((fd),(set)) #endif #endif #ifdef NO_THREAD #define ASYNC_BEGIN /* */ #define _ASYNC_END /* */ #else #define ASYNC_BEGIN \ if (Debug > 7) message(LOG_DEBUG,"ASYNC_BEGIN: %d",AsyncCount) #define _ASYNC_END \ if (Debug > 7) message(LOG_DEBUG,"ASYNC_END: %d",AsyncCount);\ waitMutex(AsyncMutex);\ AsyncCount--;\ freeMutex(AsyncMutex) #endif #ifdef USE_SSL #define ASYNC_END \ _ASYNC_END;\ ERR_remove_state(0) #else #define ASYNC_END _ASYNC_END #endif #ifdef NO_SYSLOG #define LOG_CRIT 2 /* critical conditions */ #define LOG_ERR 3 /* error conditions */ #define LOG_WARNING 4 /* warning conditions */ #define LOG_NOTICE 5 /* normal but signification condition */ #define LOG_INFO 6 /* informational */ #define LOG_DEBUG 7 /* debug-level messages */ #else /* SYSLOG */ #include #endif #define BACKLOG_MAX 50 #define XPORT 6000 #define BUFMAX 2048 #define LONGSTRMAX 1024 #define STRMAX 127 /* > 38 */ #define CONN_TIMEOUT 60 /* 1 min */ #define LB_MAX 100 #define FREE_TIMEOUT 600 /* 10 min */ #ifndef MAXHOSTNAMELEN #define MAXHOSTNAMELEN 255 #endif #define TICK_SELECT 100000 /* 0.1 sec */ #define SPIN_MAX 10 /* 1 sec */ #define NERRS_MAX 10 /* # of select errors */ #define REF_UNIT 10 /* unit of pair->count */ #ifdef USE_SSL #include #include #include #include #include #include #include #include #ifdef CRYPTOAPI int SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop); int CryptoAPI_verify_certificate(X509 *x509); #endif #define NMATCH_MAX 9 /* \1 ... \9 */ #define DEPTH_MAX 10 #ifndef TLSEXT_NAMETYPE_host_name #define OPENSSL_NO_TLSEXT #endif typedef struct { int verbose; int shutdown_mode; int depth; long serial; SSL_CTX *ctx; regex_t *re[DEPTH_MAX]; char *name; unsigned char lbmod; unsigned char lbparm; unsigned char sslparm; } StoneSSL; const int sslparm_ignore = 0x01; const int sslparm_storeca = 0x02; const int sslparm_sni = 0x04; typedef struct { int verbose; int shutdown_mode; int mode; int depth; int vflags; long off; long serial; /* const */ SSL_METHOD *meth; int (*callback)(int, X509_STORE_CTX *); unsigned char *sid_ctx; int useSNI; char *keyFile; char *certFile; char *keyFilePat; char *certFilePat; char *caFile; char *caPath; char *pfxFile; char *pfxFilePat; char *passFile; char *passFilePat; char *passwd; char *servername; int certIgnore; #ifdef CRYPTOAPI int certStoreCA; char *certStore; #endif char *cipherList; char *regexp[DEPTH_MAX]; unsigned char lbmod; unsigned char lbparm; } SSLOpts; SSLOpts ServerOpts; SSLOpts ClientOpts; int PairIndex; int MatchIndex; int NewMatchCount = 0; #ifdef WINDOWS HANDLE *SSLMutex = NULL; #else #ifdef PTHREAD pthread_mutex_t *SSLMutex = NULL; #endif #endif int NSSLMutexs = 0; #include #define MD5Init MD5_Init #define MD5Update MD5_Update #define MD5Final MD5_Final #else #ifdef USE_POP #include "global.h" #include "md5.h" #endif #endif #ifdef CPP char *CppCommand = CPP; char *CppOptions = NULL; #endif #ifdef NO_ADDRINFO #undef AF_INET6 #endif #ifdef NO_SOCKLEN_T typedef int socklen_t; #endif typedef struct _Chat { struct _Chat *next; char *send; int len; regex_t expect; } Chat; typedef struct { socklen_t len; struct sockaddr addr; } SockAddr; #define SockAddrBaseSize ((int)&((SockAddr*)NULL)->addr) typedef struct _XHosts { struct _XHosts *next; short mbits; short mode; SockAddr xhost; /* must be the last member */ } XHosts; #define XHostsBaseSize (sizeof(XHosts) - sizeof(struct sockaddr)) #define XHostsMode_Dump 0xF typedef struct _XPorts { struct _XPorts *next; short from; /* port range from */ short end; /* port range to, or equals to from */ } XPorts; typedef struct _PortXHosts { struct _PortXHosts *next; XPorts *ports; XHosts *xhosts; } PortXHosts; typedef struct _Backup { struct _Backup *next; SockAddr *check; /* host:port for check (usually same as master) */ SockAddr *master; SockAddr *backup; int proto; Chat *chat; /* chat script for health check */ short interval; /* interval of health check */ short bn; /* 0: health, 1: backup */ short used; /* 0: not used, 1: assigned, 2: used */ time_t last; /* last health check */ } Backup; typedef struct _LBSet { struct _LBSet *next; int proto; short ndsts; SockAddr *dsts[0]; } LBSet; #define type_mask 0x000f #define type_pair 0x0001 #define type_origin 0x0002 #define type_stone 0x0003 #define type_pktbuf 0x0004 typedef struct _Stone { int common; SOCKET sd; /* socket descriptor to listen */ int port; SockAddr *listen; short ndsts; /* # of destinations */ SockAddr **dsts; /* destinations */ SockAddr *from; int proto; Backup **backups; struct _Pair *pairs; char *p; int timeout; struct _Stone *next; struct _Stone *children; struct _Stone *parent; #ifdef USE_SSL StoneSSL *ssl_server; StoneSSL *ssl_client; #endif int nhosts; /* # of hosts */ XHosts *xhosts; /* hosts permitted to connect */ } Stone; typedef struct _TimeLog { time_t clock; /* time of beginning */ int pri; /* log priority */ char str[0]; /* Log message */ } TimeLog; const int data_parm_mask = 0x00ff; const int data_apop = 0x0100; const int data_identuser = 0x0200; const int data_ucred = 0x0300; const int data_peeraddr = 0x0400; #define DATA_HEAD_LEN sizeof(int) typedef struct _ExBuf { /* extensible buffer */ struct _ExBuf *next; int start; /* index of buf */ int len; /* last data is at buf[start+len-1] */ int bufmax; /* buffer size */ char buf[BUFMAX]; } ExBuf; typedef struct _Pair { int common; struct _Pair *pair; struct _Pair *prev; struct _Pair *next; Stone *stone; /* parent */ #ifdef USE_SSL SSL *ssl; /* SSL handle */ int ssl_flag; #endif XHosts *xhost; time_t clock; int timeout; SOCKET sd; /* socket descriptor */ int proto; int count; /* reference counter */ ExBuf *d; TimeLog *log; int tx; /* sent bytes */ int rx; /* received bytes */ int loop; /* loop count */ int nbuf; ExBuf *t; /* top */ ExBuf *b; /* bottom */ } Pair; typedef struct _Conn { SockAddr *dst; /* destination */ Pair *pair; int lock; struct _Conn *next; } Conn; typedef struct _Origin { int common; SOCKET sd; /* peer */ Stone *stone; SockAddr *from; /* from where */ int lock; XHosts *xhost; time_t clock; struct _Origin *next; } Origin; typedef struct _PktBuf { /* packet buffer */ int common; struct _PktBuf *next; int type; Origin *origin; int len; int bufmax; /* buffer size */ char buf[BUFMAX]; } PktBuf; typedef struct _Comm { char *str; int (*func)(Pair*, char*, int); } Comm; Stone *stones = NULL; Stone *oldstones = NULL; int ReuseAddr = 0; PortXHosts *portXHosts = NULL; XHosts *XHostsTrue = NULL; Chat *healthChat = NULL; Backup *backups = NULL; LBSet *lbsets = NULL; int MinInterval = 0; time_t lastScanBackups = 0; time_t lastEstablished = 0; time_t lastReadWrite = 0; Pair *PairTop = NULL; Pair trash; Pair *freePairs = NULL; int nFreePairs = 0; ExBuf *freeExBuf = NULL; int nFreeExBuf = 0; ExBuf *freeExBot = NULL; int nFreeExBot = 0; time_t freeExBotClock = 0; Conn conns; Origin *OriginTop = NULL; int OriginMax = 100; PktBuf *freePktBuf = NULL; int nFreePktBuf = 0; #ifdef USE_EPOLL int ePollFd; #else fd_set rin, win, ein; #endif int PairTimeOut = 10 * 60; /* 10 min */ int AsyncCount = 0; int MutexConflict = 0; const int state_mask = 0x00ff; const int proto_command = 0x0f00; /* command (dest. only) */ /* only for Stone */ const int proto_ident = 0x1000; /* need ident */ const int proto_nobackup = 0x2000; /* no backup */ const int proto_udp_s = 0x4000; /* UDP source */ const int proto_udp_d = 0x8000; /* destination */ const int proto_v6_s = 0x10000; /* IPv6 source */ const int proto_v6_d = 0x20000; /* destination */ const int proto_ip_only_s = 0x40000; /* IPv6 only source */ const int proto_ip_only_d = 0x80000; /* destination */ const int proto_unix_s = 0x100000; /* unix socket source */ const int proto_unix_d = 0x200000; /* destination */ const int proto_block_s = 0x400000; /* blocking I/O source */ const int proto_block_d = 0x800000; /* destination*/ const int proto_ssl_s = 0x1000000; /* SSL source */ const int proto_ssl_d = 0x2000000; /* destination */ /* only for Pair */ const int proto_dirty = 0x1000; /* ev must be updated */ const int proto_noconnect = 0x2000; /* no connection needed */ const int proto_connect = 0x4000; /* connection established */ const int proto_dgram = 0x8000; /* UDP */ const int proto_first_r = 0x10000; /* first read packet */ const int proto_first_w = 0x20000; /* first written packet */ const int proto_select_r = 0x40000; /* select to read */ const int proto_select_w = 0x80000; /* select to write */ const int proto_shutdown = 0x100000; /* sent shutdown */ const int proto_close = 0x200000; /* request to close */ const int proto_eof = 0x400000; /* EOF was received */ const int proto_error = 0x800000; /* error reported */ #ifndef USE_EPOLL const int proto_thread = 0x1000000; /* on thread */ #endif const int proto_conninprog = 0x2000000; /* connect in progress */ const int proto_ohttp_s = 0x4000000; /* over http source */ const int proto_ohttp_d = 0x8000000; /* destination */ const int proto_base_s = 0x10000000; /* base64 source */ const int proto_base_d = 0x20000000; /* destination */ #define command_ihead 0x0100 /* insert header */ #define command_iheads 0x0200 /* insert header repeatedly */ #define command_pop 0x0300 /* POP -> APOP conversion */ #define command_health 0x0400 /* is stone healthy ? */ #define command_identd 0x0500 /* identd of stone */ #define command_proxy 0x0600 /* http proxy */ #define command_source 0x0f00 /* source flag */ #define proto_ssl (proto_ssl_s|proto_ssl_d) #define proto_v6 (proto_v6_s|proto_v6_d) #define proto_udp (proto_udp_s|proto_udp_d) #define proto_ip_only (proto_ip_only_s|proto_ip_only_d) #define proto_unix (proto_unix_s|proto_unix_d) #define proto_block (proto_block_s|proto_block_d) #define proto_ohttp (proto_ohttp_s|proto_ohttp_d) #define proto_base (proto_base_s|proto_base_d) #define proto_stone_s (proto_udp_s|proto_command|\ proto_ohttp_s|proto_base_s|\ proto_v6_s|proto_ip_only_s|\ proto_ssl_s|proto_ident) #define proto_stone_d (proto_udp_d|proto_command|\ proto_ohttp_d|proto_base_d|\ proto_v6_d|proto_ip_only_d|\ proto_ssl_d|proto_nobackup) #define proto_pair_s (proto_ohttp_s|proto_base_s) #define proto_pair_d (proto_ohttp_d|proto_base_d|proto_command) #ifdef USE_SSL const int sf_mask = 0x0000f; const int sf_depth = 0x000f0; /* depth of cert chain */ const int sf_depth_bit = 4; const int sf_sb_on_r = 0x00100; /* SSL_shutdown blocked on read */ const int sf_sb_on_w = 0x00200; /* SSL_shutdown blocked on write */ const int sf_wb_on_r = 0x00400; /* SSL_write blocked on read */ const int sf_rb_on_w = 0x00800; /* SSL_read blocked on write */ const int sf_cb_on_r = 0x01000; /* SSL_connect blocked on read */ const int sf_cb_on_w = 0x02000; /* SSL_connect blocked on write */ const int sf_ab_on_r = 0x04000; /* SSL_accept blocked on read */ const int sf_ab_on_w = 0x08000; /* SSL_accept blocked on write */ #endif int BacklogMax = BACKLOG_MAX; int XferBufMax = 1000; /* TCP packet buffer initial size (must < 1024 ?) */ #define PKT_LEN_INI 2048 /* initial size */ int pkt_len_max = PKT_LEN_INI; /* size of UDP packet buffer */ int AddrFlag = 0; #ifndef NO_SYSLOG int Syslog = 0; char SyslogName[STRMAX+1]; #endif FILE *LogFp = NULL; char *LogFileName = NULL; FILE *AccFp = NULL; char *AccFileName = NULL; char *ConfigFile = NULL; char *PidFile = NULL; SockAddr *ConnectFrom = NULL; int DryRun = 0; int ConfigArgc = 0; int OldConfigArgc = 0; char **ConfigArgv = NULL; char **OldConfigArgv = NULL; #ifdef UNIX_DAEMON int DaemonMode = 0; #endif #ifndef NO_CHROOT char *RootDir = NULL; #endif #ifndef NO_SETUID uid_t SetUID = 0; gid_t SetGID = 0; #endif char *CoreDumpDir = NULL; #ifdef NO_PID_T typedef int pid_t; #endif pid_t MyPid; #ifndef NO_FORK int NForks = 0; pid_t *Pid; #endif int Debug = 0; /* debugging level */ #ifdef ADDRCACHE #define CACHE_TIMEOUT 180 /* 3 min */ int AddrCacheSize = 0; #endif #ifdef PTHREAD pthread_mutex_t FastMutex = PTHREAD_MUTEX_INITIALIZER; char FastMutexs[11]; #define PairMutex 0 #define ConnMutex 1 #define OrigMutex 2 #define AsyncMutex 3 #ifndef USE_EPOLL #define FdRinMutex 4 #define FdWinMutex 5 #define FdEinMutex 6 #endif #define ExBufMutex 7 #define FPairMutex 8 #define PkBufMutex 9 #ifdef ADDRCACHE #define HashMutex 10 #endif #endif #ifdef WINDOWS HANDLE PairMutex, ConnMutex, OrigMutex, AsyncMutex; HANDLE FdRinMutex, FdWinMutex, FdEinMutex; HANDLE ExBufMutex, FPairMutex, PkBufMutex; #ifdef ADDRCACHE HANDLE HashMutex; #endif #endif #ifdef OS2 HMTX PairMutex, ConnMutex, OrigMutex, AsyncMutex; HMTX FdRinMutex, FdWinMutex, FdEinMutex; HMTX ExBufMutex, FPairMutex, PkBufMutex; #ifdef ADDRCACHE HMTX HashMutex; #endif #endif #ifdef NT_SERVICE SERVICE_STATUS NTServiceStatus; SERVICE_STATUS_HANDLE NTServiceStatusHandle; #define NTServiceDisplayPrefix "Stone " char *NTServiceDisplayName = NULL; char *NTServiceName = NULL; HANDLE NTServiceLog = NULL; HANDLE NTServiceThreadHandle = NULL; #endif #ifdef NO_VSNPRINTF int vsnprintf(char *str, size_t len, char *fmt, va_list ap) { int ret; ret = vsprintf(str, fmt, ap); if (strlen(str) >= len) { fprintf(stderr, "Buffer overrun\n"); exit(1); } return ret; } #endif #ifdef NO_SNPRINTF int snprintf(char *str, size_t len, char *fmt, ...) { va_list ap; int ret; va_start(ap, fmt); ret = vsnprintf(str, len, fmt, ap); va_end(ap); return ret; } #endif #ifdef NO_BCOPY void bcopy(const void *b1, void *b2, int len) { if (b1 < b2 && (char*)b2 < (char*)b1 + len) { /* overlapping */ char *p, *q; q = (char*)b2 + len - 1; for (p=(char*)b1+len-1; (char*)b1 <= p; p--, q--) *q = *p; } else { memcpy(b2, b1, len); } } #endif #ifdef NO_RINDEX char *rindex(char *p, int ch) { char *save = NULL; do { if (*p == ch) save = p; } while (*p++); return save; } #endif #ifdef NO_STRDUP char *strdup(const char *s) { int len = strlen(s); char *ret = malloc(len+1); if (ret) { bcopy(s, ret, len+1); } return ret; } #endif #ifdef WINDOWS struct tm *localtime_r(const time_t *clock, struct tm *t) { FILETIME utc, local; SYSTEMTIME system; LONGLONG ll; ll = Int32x32To64(*clock, 10000000) + 116444736000000000ULL; utc.dwLowDateTime = (DWORD)ll; utc.dwHighDateTime = ll >> 32; if (!FileTimeToLocalFileTime(&utc, &local)) return NULL; if (!FileTimeToSystemTime(&local, &system)) return NULL; t->tm_sec = system.wSecond; t->tm_min = system.wMinute; t->tm_hour = system.wHour; t->tm_mday = system.wDay; t->tm_mon = system.wMonth-1; t->tm_year = system.wYear-1900; t->tm_wday = system.wDayOfWeek; return t; } #endif static char Month[][4] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; char *strntime(char *str, int len, time_t *clock, long micro) { #ifdef THREAD_UNSAFE struct tm *t = localtime(clock); #else struct tm tm; struct tm *t = localtime_r(clock, &tm); #endif if (micro >= 0) { snprintf(str, len, "%s %2d %02d:%02d:%02d.%06ld ", Month[t->tm_mon], t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec, micro); } else { snprintf(str, len, "%s %2d %02d:%02d:%02d ", Month[t->tm_mon], t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec); } return str; } #ifdef NO_GETTIMEOFDAY int gettimeofday(struct timeval *tv, void *tz) { static u_long start = 0; u_long tick = GetTickCount(); time_t now; time(&now); if (start == 0) start = now - tick / 1000; if (tz) return -1; if (tv) { tv->tv_usec = (tick % 1000) * 1000; tv->tv_sec = start + (tick / 1000); if (now < tv->tv_sec - 1 || tv->tv_sec + 1 < now) { start = 0; tv->tv_usec = -1; /* diff is too large */ } return 0; } return -1; } #endif #if defined (__STDC__) && __STDC__ void message(int pri, char *fmt, ...) __attribute__ ((__format__ (__printf__, 2, 3))); #endif void message(int pri, char *fmt, ...) { char str[LONGSTRMAX+1]; int pos = 0; unsigned long thid = 0; va_list ap; #ifndef NO_SYSLOG if (!Syslog) #endif { struct timeval tv; if (gettimeofday(&tv, NULL) >= 0) { strntime(str+pos, LONGSTRMAX-pos, &tv.tv_sec, tv.tv_usec); } str[LONGSTRMAX] = '\0'; pos = strlen(str); } #ifdef WINDOWS thid = (unsigned long)GetCurrentThreadId(); #else #ifdef PTHREAD thid = (unsigned long)pthread_self(); #endif #endif if (thid) { snprintf(str+pos, LONGSTRMAX-pos, "%lu ", thid); pos += strlen(str+pos); } va_start(ap, fmt); vsnprintf(str+pos, LONGSTRMAX-pos, fmt, ap); va_end(ap); str[LONGSTRMAX] = '\0'; if (LogFp) fprintf(LogFp, "%s\n", str); #ifndef NO_SYSLOG else if (Syslog) { if (Syslog == 1 || pri != LOG_DEBUG) syslog(pri, "%s", str); if (Syslog > 1) fprintf(stdout, "%s\n", str); /* daemontools */ } #elif defined(NT_SERVICE) else if (NTServiceLog) { LPCTSTR msgs[] = {str, NULL}; int type = EVENTLOG_INFORMATION_TYPE; if (pri <= LOG_ERR) type = EVENTLOG_ERROR_TYPE; else if (pri <= LOG_NOTICE) type = EVENTLOG_WARNING_TYPE; ReportEvent(NTServiceLog, type, 0, EVLOG, NULL, 1, 0, msgs, NULL); } #endif } void message_time(Pair *pair, int pri, char *fmt, ...) { va_list ap; char str[LONGSTRMAX+1]; TimeLog *log; log = pair->log; if (log) { pair->log = NULL; free(log); } va_start(ap, fmt); vsnprintf(str, LONGSTRMAX, fmt, ap); va_end(ap); str[LONGSTRMAX] = '\0'; log = (TimeLog*)malloc(sizeof(TimeLog)+strlen(str)+1); if (log) { time(&log->clock); log->pri = pri; strcpy(log->str, str); pair->log = log; } } int priority(Pair *pair) { int pri = LOG_ERR; if (pair) { if (pair->proto & proto_error) pri = LOG_DEBUG; else pair->proto |= proto_error; } return pri; } void packet_dump(char *head, char *buf, int len, XHosts *xhost) { char line[LONGSTRMAX+1]; int mode = (xhost->mode & XHostsMode_Dump); int i, j, k, l; int nb = 8; j = k = l = 0; for (i=0; i < len; i += j) { if (mode <= 2) { nb = 16; l = 0; line[l++] = ' '; for (j=0; k <= j/10 && i+j < len && l < LONGSTRMAX-10; j++) { if (' ' <= buf[i+j] && buf[i+j] <= '~') line[l++] = buf[i+j]; else { sprintf(&line[l], "<%02x>", buf[i+j]); l += strlen(&line[l]); if (buf[i+j] == '\n') { k = 0; j++; break; } if (buf[i+j] != '\t' && buf[i+j] != '\r' && buf[i+j] != '\033') k++; } } } if (k > j/10 || nb < 16) { j = l = 0; for (j=0; j < nb && i+j < len; j++) { if (mode == 1 && (' ' <= buf[i+j] && buf[i+j] <= '~')) { sprintf(&line[l], " '%c", buf[i+j]); } else { sprintf(&line[l], " %02x", (unsigned char)buf[i+j]); if (buf[i+j] == '\n') k = 0; else k++; } l += strlen(&line[l]); } if (nb < 16) { while (l < (nb * 3) + 2) line[l++] = ' '; for (j=0; j < nb && i+j < len; j++) { if (' ' <= buf[i+j] && buf[i+j] <= '~') line[l++] = buf[i+j]; else line[l++] = '.'; } } } line[l] = '\0'; message(LOG_DEBUG, "%s%s", head, line); } } void message_buf(Pair *pair, int len, char *str) { /* dump for debug */ char head[STRMAX+1]; Pair *p = pair->pair; if (p == NULL) return; head[STRMAX] = '\0'; if ((pair->proto & proto_command) == command_source) { snprintf(head, STRMAX, "%d %s%d<%d", pair->stone->sd, str, pair->sd, p->sd); } else { snprintf(head, STRMAX, "%d %s%d>%d", pair->stone->sd, str, p->sd, pair->sd); } packet_dump(head, pair->t->buf + pair->t->start, len, pair->xhost); } char *addr2ip(struct in_addr *addr, char *str, int len) { union { u_long l; unsigned char c[4]; } u; if (len >= 1) { u.l = addr->s_addr; snprintf(str, len-1, "%d.%d.%d.%d", u.c[0], u.c[1], u.c[2], u.c[3]); str[len-1] = '\0'; } return str; } #ifdef AF_INET6 char *addr2ip6(struct in6_addr *addr, char *str, int len) { u_short *s; if (len >= 1) { s = (u_short*)addr; snprintf(str, len-1, "%x:%x:%x:%x:%x:%x:%x:%x", ntohs(s[0]), ntohs(s[1]), ntohs(s[2]), ntohs(s[3]), ntohs(s[4]), ntohs(s[5]), ntohs(s[6]), ntohs(s[7])); str[len-1] = '\0'; } return str; } #endif char *addr2numeric(struct sockaddr *sa, char *str, int len) { if (sa->sa_family == AF_INET) { addr2ip(&((struct sockaddr_in*)sa)->sin_addr, str, len); #ifdef AF_INET6 } else if (sa->sa_family == AF_INET6) { addr2ip6(&((struct sockaddr_in6*)sa)->sin6_addr, str, len); #endif } else { snprintf(str, len, "%s", "???"); } return str; } char *ext2str(int ext, char *str, int len) { char sep = '/'; int i = 0; if (!str || len <= 1) return ""; if (ext & proto_udp) { if (i < len) str[i++] = sep; sep = ','; strncpy(str+i, "udp", len-i); i += 3; } if (ext & proto_ohttp) { if (i < len) str[i++] = sep; sep = ','; strncpy(str+i, "http", len-i); i += 4; } if (ext & proto_ssl) { if (i < len) str[i++] = sep; sep = ','; strncpy(str+i, "ssl", len-i); i += 3; } if (ext & proto_v6) { if (i < len) str[i++] = sep; sep = ','; if (ext & proto_ip_only) { strncpy(str+i, "v6only", len-i); i += 6; } else { strncpy(str+i, "v6", len-i); i += 2; } } else if (ext & proto_ip_only) { sep = ','; strncpy(str+i, "v4only", len-i); i += 6; } if (ext & proto_base) { if (i < len) str[i++] = sep; sep = ','; strncpy(str+i, "base", len-i); i += 4; } if (ext & proto_block) { if (i < len) str[i++] = sep; sep = ','; strncpy(str+i, "block", len-i); i += 5; } if (ext & proto_ident) { if (i < len) str[i++] = sep; sep = ','; strncpy(str+i, "ident", len-i); i += 5; } if (ext & proto_nobackup) { if (i < len) str[i++] = sep; sep = ','; strncpy(str+i, "nobackup", len-i); i += 8; } switch(ext & proto_command) { case command_ihead: if (i < len) str[i++] = sep; sep = ','; strncpy(str+i, "proxy", len-i); i += 5; break; case command_iheads: if (i < len) str[i++] = sep; sep = ','; strncpy(str+i, "mproxy", len-i); i += 6; break; case command_pop: if (i < len) str[i++] = sep; sep = ','; strncpy(str+i, "apop", len-i); i += 4; break; } return str; } int islocalhost(struct sockaddr *sa) { if (sa->sa_family == AF_INET) { if (ntohl(((struct sockaddr_in*)sa)->sin_addr.s_addr) == 0x7F000001L) return 1; /* localhost */ if (ntohl(((struct sockaddr_in*)sa)->sin_addr.s_addr) == 0L) return -1; /* null */ } #ifdef AF_INET6 if (sa->sa_family == AF_INET6) { int i; struct in6_addr *addrp = &((struct sockaddr_in6*)sa)->sin6_addr; for (i=0; i < 12; i+=4) if (*(u_long*)&addrp->s6_addr[i] != 0) return 0; if (*(u_long*)&addrp->s6_addr[i] == ntohl(1)) return 1; /* localhost */ if (*(u_long*)&addrp->s6_addr[i] == 0) return -1; /* null */ } #endif return 0; } #ifdef NO_ADDRINFO #define NTRY_MAX 10 #ifndef NI_NUMERICHOST #define NI_NUMERICHOST 1 #endif char *addr2str(struct sockaddr *sa, socklen_t salen, char *str, int len, int flags) { struct hostent *ent; struct in_addr *addr; int ntry = NTRY_MAX; if (!str || len <= 1) return ""; str[len-1] = '\0'; if (sa->sa_family != AF_INET) { message(LOG_ERR, "Unknown family=%d", sa->sa_family); strncpy(str, "?.?.?.?", len-1); return str; } addr = &((struct sockaddr_in*)sa)->sin_addr; addr2ip(addr, str, len); if (!AddrFlag || flags) { do { ent = gethostbyaddr((char*)&addr->s_addr, sizeof(addr->s_addr), AF_INET); if (ent) { strncpy(str, ent->h_name, len-1); return str; } } while (h_errno == TRY_AGAIN && ntry-- > 0); message(LOG_ERR, "Unknown address: %s err=%d", str, h_errno); } return str; } char *addrport2str(struct sockaddr *sa, socklen_t salen, int proto, char *str, int len, int flags) { struct servent *ent; int port; int i = 0; if (!str || len <= 1) return ""; str[len-1] = '\0'; if (sa->sa_family == AF_INET) { addr2str(sa, salen, str, len, 0); i = strlen(str); if (i < len-2) { str[i++] = ':'; str[i] = '\0'; } } else { message(LOG_ERR, "Unknown address family=%d len=%d", sa->sa_family, salen); } port = ((struct sockaddr_in*)sa)->sin_port; if (!AddrFlag) { ent = getservbyport(port, ((proto & proto_udp) ? "udp" : "tcp")); if (ent) strncpy(str+i, ent->s_name, len-i-5); } if (str[i] == '\0') snprintf(str+i, len-i-5, "%d", ntohs((unsigned short)port)); i = strlen(str); ext2str(proto, str+i, len-i); return str; } #else char *addr2str(struct sockaddr *sa, socklen_t salen, char *str, int len, int flags) { int err; if (AddrFlag) flags |= NI_NUMERICHOST; err = getnameinfo(sa, salen, str, len, NULL, 0, flags); if (err) { #ifdef WINDOWS errno = WSAGetLastError(); #endif addr2numeric(sa, str, len); if (len >= 1) str[len-1] = '\0'; message(LOG_ERR, "Unknown address: %s err=%d errno=%d", str, err, errno); } return str; } char *addrport2str(struct sockaddr *sa, socklen_t salen, int proto, char *str, int len, int flags) { char serv[STRMAX+1]; int err; int i; if (!str || len <= 1) return ""; str[len-1] = '\0'; serv[0] = '\0'; if (AddrFlag) flags |= (NI_NUMERICHOST | NI_NUMERICSERV); else if (proto & proto_udp) flags |= NI_DGRAM; if (!(flags & NI_NUMERICHOST) && islocalhost(sa)) flags |= NI_NUMERICHOST; if (Debug > 10) { addr2numeric(sa, serv, STRMAX); serv[STRMAX] = '\0'; message(LOG_DEBUG, "getnameinfo: %s family=%d len=%d flags=%d", serv, sa->sa_family, salen, flags); } #ifndef NO_UNIXDOMAIN if (sa->sa_family == AF_UNIX) { int j; j = salen - (((struct sockaddr_un*)sa)->sun_path - (char*)sa); strncpy(serv, ((struct sockaddr_un*)sa)->sun_path, j); serv[j] = '\0'; snprintf(str, len, "%s", "unix"); err = 0; } else #endif err = getnameinfo(sa, salen, str, len, serv, STRMAX, flags); #ifdef WSANO_DATA if (err == WSANO_DATA && !(flags & NI_NUMERICSERV)) { /* WinSock32 returns WSANO_DATA if serv can't be lookup although the hostname itself is resolvable. So we must call again without looking up serv */ if (Debug > 10) message(LOG_DEBUG, "getnameinfo: WSANO_DATA flags=%d", flags); flags |= NI_NUMERICSERV; err = getnameinfo(sa, salen, str, len, serv, STRMAX, flags); } #endif if (err) { if (sa->sa_family == AF_INET) { addr2ip(&((struct sockaddr_in*)sa)->sin_addr, str, len); i = strlen(str); snprintf(str+i, len-i-5, ":%d", ntohs(((struct sockaddr_in*)sa)->sin_port)); #ifdef AF_INET6 } else if (sa->sa_family == AF_INET6) { addr2ip6(&((struct sockaddr_in6*)sa)->sin6_addr, str, len); i = strlen(str); snprintf(str+i, len-i-5, ":%d", ntohs(((struct sockaddr_in6*)sa)->sin6_port)); #endif } else { snprintf(str, len, "%s:?", "???"); } #ifdef WINDOWS errno = WSAGetLastError(); #endif message(LOG_ERR, "Unknown node:serv %s len=%d err=%d errno=%d", str, salen, err, errno); } else { i = strlen(str); snprintf(str+i, len-i, ":%s", serv); } i = strlen(str); ext2str(proto, str+i, len-i); return str; } #endif char *addrport2strOnce(struct sockaddr *sa, socklen_t salen, int proto, char *str, int len, int flags) { if (! *str) { addrport2str(sa, salen, proto, str, len, flags); str[len] = '\0'; } return str; } int isdigitstr(char *str) { while (*str && !isspace(*str)) { if (!isdigit(*str)) return 0; str++; } return 1; } int isdigitaddr(char *name) { int ndigits = 0; int isdot = 1; while(*name) { if (*name == '.') { if (isdot) return 0; /* `.' appears twice */ isdot = 1; } else if (isdigit(*name)) { if (isdot) ndigits++; isdot = 0; } else { return 0; /* not digit nor dot */ } name++; } return ndigits; } /* set port into struct sockaddr */ void saPort(struct sockaddr *sa, u_short port) { if (sa->sa_family == AF_INET) { ((struct sockaddr_in*)sa)->sin_port = htons(port); return; } #ifdef AF_INET6 if (sa->sa_family == AF_INET6) { ((struct sockaddr_in6*)sa)->sin6_port = htons(port); return; } #endif message(LOG_ERR, "saPort: unknown family=%d", sa->sa_family); } /* get port from struct sockaddr */ int getport(struct sockaddr *sa) { if (sa->sa_family == AF_INET) { return ntohs(((struct sockaddr_in*)sa)->sin_port); #ifdef AF_INET6 } else if (sa->sa_family == AF_INET6) { return ntohs(((struct sockaddr_in6*)sa)->sin6_port); #endif } return -1; } int hostPortExt(char *str, char *host, char *port) { int port_pos = 0; int ext_pos = 0; int i; for (i=0; i < STRMAX && str[i]; i++) { host[i] = str[i]; port[i-port_pos] = str[i]; if (str[i] == ':') port_pos = i+1; if (str[i] == '/') ext_pos = i+1; } if (!port_pos) return -1; /* illegal format */ host[port_pos-1] = '\0'; if (ext_pos) port[ext_pos - port_pos - 1] = '\0'; else port[i - port_pos] = '\0'; return ext_pos; } #ifdef NO_ADDRINFO int str2port(char *str, char *proto) { /* host byte order */ struct servent *ent; ent = getservbyname(str, proto); if (ent) { return ntohs(ent->s_port); } else if (isdigitstr(str)) { return atoi(str); } else { return -1; } } int host2sa(char *name, char *serv, struct sockaddr *sa, socklen_t *salenp, int *socktypep, int *protocolp, int flags) { struct hostent *hp; int ntry = NTRY_MAX; int port = -1; struct sockaddr_in *sinp = (struct sockaddr_in*)sa; struct in_addr *addrp = &sinp->sin_addr; if (*salenp < sizeof(struct sockaddr_in)) { message(LOG_ERR, "host2sa: too small salen=%d", *salenp); return 0; /* too small */ } *salenp = sizeof(struct sockaddr_in); if (!name) { bzero(sa, *salenp); sa->sa_family = AF_INET; goto hostok; } if (isdigitaddr(name)) { if ((addrp->s_addr=inet_addr(name)) != -1) { sa->sa_family = AF_INET; goto hostok; } } else { do { hp = gethostbyname(name); if (hp) { bcopy(hp->h_addr, (char *)addrp, hp->h_length); sa->sa_family = hp->h_addrtype; hostok: if (serv) { if (protocolp && *protocolp == IPPROTO_UDP) { port = str2port(serv, "udp"); } else { port = str2port(serv, "tcp"); } if (port < 0) { message(LOG_ERR, "Unknown service: %s", serv); return 0; } saPort(sa, port); } return 1; } } while (h_errno == TRY_AGAIN && ntry-- > 0); } message(LOG_ERR, "Unknown host: %s err=%d", name, h_errno); return 0; } #else int host2sa(char *name, char *serv, struct sockaddr *sa, socklen_t *salenp, int *socktypep, int *protocolp, int flags) { struct addrinfo *ai = NULL; struct addrinfo hint; int err; hint.ai_flags = flags; hint.ai_family = sa->sa_family; if (socktypep) hint.ai_socktype = *socktypep; else hint.ai_socktype = SOCK_STREAM; if (protocolp) hint.ai_protocol = *protocolp; else hint.ai_protocol = 0; hint.ai_addrlen = 0; hint.ai_addr = NULL; hint.ai_canonname = NULL; hint.ai_next = NULL; if (Debug > 10) { message(LOG_DEBUG, "getaddrinfo: %s:%s family=%d socktype=%d flags=%d", (name ? name : ""), (serv ? serv : ""), sa->sa_family, hint.ai_socktype, flags); } err = getaddrinfo(name, serv, &hint, &ai); if (err != 0) { #ifdef WINDOWS errno = WSAGetLastError(); #endif message(LOG_ERR, "getaddrinfo for %s:%s failed err=%d errno=%d", (name ? name : ""), (serv ? serv : ""), err, errno); fail: if (ai) freeaddrinfo(ai); return 0; } if (ai->ai_addrlen > *salenp) { message(LOG_ERR, "getaddrinfo for %s:%s returns unexpected addr size=%d", (name ? name : ""), (serv ? serv : ""), ai->ai_addrlen); goto fail; } *salenp = ai->ai_addrlen; if (socktypep) *socktypep = ai->ai_socktype; if (protocolp) *protocolp = ai->ai_protocol; bcopy(ai->ai_addr, sa, *salenp); freeaddrinfo(ai); return 1; } #endif int hostPort2sa(char *str, struct sockaddr *sa, socklen_t *salenp, int flags) { char host[STRMAX+1]; char port[STRMAX+1]; int pos = hostPortExt(str, host, port); if (pos < 0) return 0; #ifdef AF_INET6 if (pos && !strcmp(str+pos, "v6")) sa->sa_family = AF_INET6; #endif return host2sa(host, port, sa, salenp, NULL, NULL, flags); } SockAddr *saDup(struct sockaddr *sa, socklen_t salen) { SockAddr *ret = malloc(SockAddrBaseSize + salen); if (ret) { bcopy(sa, &ret->addr, salen); ret->len = salen; } return ret; } int saComp(struct sockaddr *a, struct sockaddr *b) { if (a->sa_family != b->sa_family) { if (Debug > 10) { message(LOG_DEBUG, "saComp: sa_family differ: %d, %d", a->sa_family, b->sa_family); } return 0; } if (a->sa_family == AF_INET) { struct in_addr *an, *bn; short ap, bp; an = &((struct sockaddr_in*)a)->sin_addr; bn = &((struct sockaddr_in*)b)->sin_addr; ap = ((struct sockaddr_in*)a)->sin_port; bp = ((struct sockaddr_in*)b)->sin_port; if (Debug > 10) { message(LOG_DEBUG, "saComp: %lx:%d, %lx:%d", (long unsigned)ntohl(an->s_addr), ntohs(ap), (long unsigned)ntohl(bn->s_addr), ntohs(bp)); } return (an->s_addr == bn->s_addr) && (ap == bp); } #ifdef AF_INET6 if (a->sa_family == AF_INET6) { struct in6_addr *an, *bn; short ap, bp; int i; an = &((struct sockaddr_in6*)a)->sin6_addr; bn = &((struct sockaddr_in6*)b)->sin6_addr; ap = ((struct sockaddr_in6*)a)->sin6_port; bp = ((struct sockaddr_in6*)b)->sin6_port; if (ap != bp) return 0; for (i=0; i < 16; i+=4) if (*(u_long*)&an->s6_addr[i] != *(u_long*)&bn->s6_addr[i]) return 0; return 1; } #endif message(LOG_ERR, "saComp: unknown family=%d", a->sa_family); return 0; } /* *addrp is permitted to connect to *stone ? */ XHosts *checkXhost(XHosts *xhosts, struct sockaddr *sa, socklen_t salen) { int match = 1; if (!xhosts) return XHostsTrue; /* any hosts can access */ for (; xhosts != NULL; xhosts = xhosts->next) { if (xhosts->mbits < 0) { match = !match; continue; } if (sa->sa_family == AF_INET && xhosts->xhost.addr.sa_family == AF_INET) { if (xhosts->mbits > 0) { u_long addr = ntohl(((struct sockaddr_in*)sa) ->sin_addr.s_addr); u_long xadr = ntohl(((struct sockaddr_in*)&xhosts->xhost.addr) ->sin_addr.s_addr); u_long bits = ((u_long)~0 << (32 - xhosts->mbits)); if ((addr & bits) != (xadr & bits)) continue; } if (match) return xhosts; return NULL; #ifdef AF_INET6 } else if (sa->sa_family == AF_INET6 && xhosts->xhost.addr.sa_family == AF_INET6) { struct in6_addr *adrp = &((struct sockaddr_in6*)sa)->sin6_addr; struct in6_addr *xadp = &((struct sockaddr_in6*) &xhosts->xhost.addr)->sin6_addr; int j, k; for (j=0, k=xhosts->mbits; k > 0; j+=4, k -= 32) { u_long addr, xadr, mask; addr = ntohl(*(u_long*)&adrp->s6_addr[j]); xadr = ntohl(*(u_long*)&xadp->s6_addr[j]); if (k >= 32) mask = (u_long)~0; else mask = ((u_long)~0 << (32-k)); /* premise: k > 0 */ if (Debug > 12) message(LOG_DEBUG, "compare addr=%lx x=%lx m=%lx", addr, xadr, mask); if ((addr & mask) != (xadr & mask)) break; } if (k <= 0) { if (match) return xhosts; return NULL; } } else if (sa->sa_family == AF_INET6 && xhosts->xhost.addr.sa_family == AF_INET) { struct in6_addr *adrp = &((struct sockaddr_in6*)sa)->sin6_addr; if (*(u_long*)&adrp->s6_addr[0] != 0 || *(u_long*)&adrp->s6_addr[4] != 0 || ntohl(*(u_long*)&adrp->s6_addr[8]) != 0xFFFF) continue; if (xhosts->mbits > 0) { u_long addr = ntohl(*(u_long*)&adrp->s6_addr[12]); u_long xadr = ntohl(((struct sockaddr_in*)&xhosts->xhost.addr) ->sin_addr.s_addr); u_long bits = ((u_long)~0 << (32 - xhosts->mbits)); if ((addr & bits) != (xadr & bits)) continue; } if (match) return xhosts; return NULL; #endif } } if (!match) return XHostsTrue; return NULL; } #ifdef WINDOWS void waitMutex(HANDLE h) { DWORD ret; if (h) { ret = WaitForSingleObject(h, 5000); /* 5 sec */ if (ret == WAIT_FAILED) { message(LOG_ERR, "Fail to wait mutex err=%d, existing", (int)GetLastError()); exit(1); } else if (ret == WAIT_TIMEOUT) { message(LOG_ERR, "timeout to wait mutex, existing"); exit(1); } } } void freeMutex(HANDLE h) { if (h) { if (!ReleaseMutex(h)) { message(LOG_ERR, "Fail to release mutex err=%d", (int)GetLastError()); } } } #else /* ! WINDOWS */ #ifdef OS2 void waitMutex(HMTX h) { APIRET ret; if (h) { ret = DosRequestMutexSem(h, 500); /* 0.5 sec */ if (ret == ERROR_TIMEOUT) { message(LOG_WARNING, "timeout to wait mutex"); } else if (ret) { message(LOG_ERR, "Fail to request mutex err=%d", ret); } } } void freeMutex(HMTX h) { APIRET ret; if (h) { ret = DosReleaseMutexSem(h); if (ret) { message(LOG_ERR, "Fail to release mutex err=%d", ret); } } } #else /* ! OS2 & ! WINDOWS */ #ifdef PTHREAD void waitMutex(int h) { int err; for (;;) { err = pthread_mutex_lock(&FastMutex); if (err) { message(LOG_ERR, "Mutex %d err=%d", h, err); } if (FastMutexs[h] == 0) { int i = ++FastMutexs[h]; pthread_mutex_unlock(&FastMutex); if (Debug > 20) message(LOG_DEBUG, "Lock Mutex %d = %d", h, i); break; } pthread_mutex_unlock(&FastMutex); if (Debug > 10) message(LOG_DEBUG, "Mutex conflict %d = %d", h, FastMutexs[h]); MutexConflict++; usleep(100); } } void freeMutex(int h) { int err = pthread_mutex_lock(&FastMutex); if (err) { message(LOG_ERR, "Mutex %d err=%d", h, err); } if (FastMutexs[h] > 0) { if (FastMutexs[h] > 1) message(LOG_ERR, "Mutex %d Locked Recursively (%d)", h, FastMutexs[h]); FastMutexs[h]--; if (Debug > 20) message(LOG_DEBUG, "Unlock Mutex %d = %d", h, FastMutexs[h]); } pthread_mutex_unlock(&FastMutex); } #else /* ! OS2 & ! WINDOWS & PTHREAD */ #define waitMutex(sem) /* */ #define freeMutex(sem) /* */ #endif #endif #endif /* backup */ int healthCheck(struct sockaddr *sa, socklen_t salen, int proto, int timeout, Chat *chat) { SOCKET sd; int ret; char addrport[STRMAX+1]; #ifdef WINDOWS u_long param; #endif #ifdef USE_EPOLL int epfd; struct epoll_event ev; struct epoll_event evs[1]; #endif time_t start, now; time(&start); sd = socket(sa->sa_family, SOCK_STREAM, IPPROTO_TCP); if (InvalidSocket(sd)) { #ifdef WINDOWS errno = WSAGetLastError(); #endif message(LOG_ERR, "health check: can't create socket err=%d", errno); return 1; /* I can't tell the master is healthy or not */ } #ifdef USE_EPOLL epfd = epoll_create(BACKLOG_MAX); if (epfd < 0) { message(LOG_ERR, "health check: can't create epoll err=%d", errno); return 1; /* I can't tell the master is healthy or not */ } ev.events = (EPOLLOUT | EPOLLONESHOT); if (epoll_ctl(epfd, EPOLL_CTL_ADD, sd, &ev) < 0) { message(LOG_ERR, "health check: epoll_ctl ADD err=%d", errno); close(epfd); return 1; /* I can't tell the master is healthy or not */ } #endif addrport[0] = '\0'; if (!(proto & proto_block_d)) { #ifdef WINDOWS param = 1; ioctlsocket(sd, FIONBIO, ¶m); #else fcntl(sd, F_SETFL, O_NONBLOCK); #endif } ret = connect(sd, sa, salen); if (ret < 0) { #ifdef WINDOWS errno = WSAGetLastError(); #endif if (errno == EINPROGRESS) { #ifndef USE_EPOLL fd_set wout; struct timeval tv; #endif int optval; socklen_t optlen = sizeof(optval); do { time(&now); if (now - start >= timeout) goto timeout; #ifndef USE_EPOLL tv.tv_sec = 1; tv.tv_usec = 0; FD_ZERO(&wout); FdSet(sd, &wout); #endif } while ( #ifdef USE_EPOLL epoll_wait(epfd, evs, 1, 1000) == 0 #else select(FD_SETSIZE, NULL, &wout, NULL, &tv) == 0 #endif ); getsockopt(sd, SOL_SOCKET, SO_ERROR, (char*)&optval, &optlen); if (optval) { addrport2strOnce(sa, salen, (proto & proto_pair_d), addrport, STRMAX, 0); message(LOG_ERR, "health check: connect %s getsockopt err=%d", addrport, optval); goto fail; } } else { addrport2strOnce(sa, salen, (proto & proto_pair_d), addrport, STRMAX, 0); message(LOG_ERR, "health check: connect %s err=%d", addrport, errno); goto fail; } } time(&now); if (now - start >= timeout) goto timeout; while (chat) { char buf[BUFMAX]; int len; int err; ret = send(sd, chat->send, chat->len, 0); if (ret < 0 || ret != chat->len) { #ifdef WINDOWS errno = WSAGetLastError(); #endif addrport2strOnce(sa, salen, (proto & proto_pair_d), addrport, STRMAX, 0); message(LOG_ERR, "health check: send %s err=%d", addrport, errno); goto fail; } len = 0; do { #ifdef USE_EPOLL ev.events = (EPOLLIN | EPOLLONESHOT); epoll_ctl(epfd, EPOLL_CTL_MOD, sd, &ev); #else fd_set rout; struct timeval tv; #endif do { time(&now); if (now - start >= timeout) goto timeout; #ifndef USE_EPOLL tv.tv_sec = 1; tv.tv_usec = 0; FD_ZERO(&rout); FdSet(sd, &rout); #endif } while ( #ifdef USE_EPOLL epoll_wait(epfd, evs, 1, 1000) == 0 #else select(FD_SETSIZE, &rout, NULL, NULL, &tv) == 0 #endif ); ret = recv(sd, buf+len, BUFMAX-1-len, 0); if (ret < 0) { #ifdef WINDOWS errno = WSAGetLastError(); #endif addrport2strOnce(sa, salen, (proto & proto_pair_d), addrport, STRMAX, 0); message(LOG_ERR, "health check: recv from %s err=%d", addrport, errno); goto fail; } len += ret; buf[len] = '\0'; err = regexec(&chat->expect, buf, 0, NULL, 0); if (Debug > 8) { addrport2strOnce(sa, salen, (proto & proto_pair_d), addrport, STRMAX, 0); message(LOG_DEBUG, "health check: %s regexec=%d", addrport, err); } if (len > BUFMAX/2) { bcopy(buf+(len-BUFMAX/2), buf, BUFMAX/2); len = BUFMAX/2; } } while (ret > 0 && err == REG_NOMATCH); #ifndef REG_NOERROR #ifdef REG_OK #define REG_NOERROR REG_OK #else #define REG_NOERROR 0 #endif #endif if (err != REG_NOERROR) goto fail; chat = chat->next; } shutdown(sd, 2); #ifdef USE_EPOLL close(epfd); #endif closesocket(sd); return 1; /* healthy ! */ timeout: if (Debug > 8) { addrport2strOnce(sa, salen, (proto & proto_pair_d), addrport, STRMAX, 0); message(LOG_DEBUG, "health check: %s timeout", addrport); } fail: shutdown(sd, 2); #ifdef USE_EPOLL close(epfd); #endif closesocket(sd); return 0; /* fail */ } void asyncHealthCheck(Backup *b) { time_t now; char addrport[STRMAX+1]; ASYNC_BEGIN; time(&now); b->last = now + 60 * 60; /* suppress further check */ addrport[0] = '\0'; if (Debug > 8) { addrport2strOnce(&b->check->addr, b->check->len, (b->proto & proto_pair_d), addrport, STRMAX, 0); message(LOG_DEBUG, "asyncHealthCheck %s", addrport); } if (healthCheck(&b->check->addr, b->check->len, b->proto, b->interval, b->chat)) { /* healthy ? */ if (Debug > 3 || (b->bn && Debug > 1)) { addrport2strOnce(&b->check->addr, b->check->len, (b->proto & proto_pair_d), addrport, STRMAX, 0); message(LOG_DEBUG, "health check %s success", addrport); } if (b->bn) b->bn = 0; } else { /* unhealthy */ if (Debug > 3 || (b->bn == 0 && Debug > 0)) { addrport2strOnce(&b->check->addr, b->check->len, (b->proto & proto_pair_d), addrport, STRMAX, 0); message(LOG_DEBUG, "health check %s fail", addrport); } if (b->bn == 0) b->bn++; } b->last = now; ASYNC_END; } void scanBackups(void) { Backup *b; time_t now; time(&now); for (b=backups; b != NULL; b=b->next) { if (b->used < 2) continue; /* not used */ if (b->interval <= 0 || now - b->last < b->interval) continue; ASYNC(asyncHealthCheck, b); } } Backup *findBackup(struct sockaddr *sa) { Backup *b; for (b=backups; b != NULL; b=b->next) { if (saComp(sa, &b->master->addr)) { /* found */ if (Debug > 1) { char mhostport[STRMAX+1]; char bhostport[STRMAX+1]; addrport2str(&b->master->addr, b->master->len, (b->proto & proto_pair_d), mhostport, STRMAX, 0); mhostport[STRMAX] = '\0'; addrport2str(&b->backup->addr, b->backup->len, (b->proto & proto_pair_d), bhostport, STRMAX, 0); bhostport[STRMAX] = '\0'; message(LOG_DEBUG, "master %s backup %s interval %d", mhostport, bhostport, b->interval); } return b; } } return NULL; } int gcd(int a, int b) { int m; if (a > b) { m = a % b; if (m == 0) return b; return gcd(m, b); } else { m = b % a; if (m == 0) return a; return gcd(m, a); } } int mkBackup(int argc, int argi, char *argv[]) { char master_host[STRMAX+1]; char master_port[STRMAX+1]; char *master_ext = NULL; char backup_host[STRMAX+1]; char backup_port[STRMAX+1]; int pos; char *check_host = NULL; char *check_port = NULL; char *check_ext = NULL; struct sockaddr_storage ss; struct sockaddr *sa = (struct sockaddr*)&ss; socklen_t salen; Backup *b = malloc(sizeof(Backup)); argi++; for ( ; argi < argc; argi++) { if (!strncmp(argv[argi], "host=", 5)) { check_host = argv[argi]+5; } else if (!strncmp(argv[argi], "port=", 5)) { check_port = argv[argi]+5; } else if (!strncmp(argv[argi], "ext=", 4)) { check_ext = argv[argi]+4; } else { break; } } if (argi+2 >= argc) { message(LOG_ERR, "Irregular backup option"); exit(1); } if (b) { b->last = 0; b->bn = 0; /* healthy */ b->used = 0; b->interval = atoi(argv[argi]); } else { memerr: message(LOG_CRIT, "Out of memory, no backup for %s", argv[argi+1]); return argi+2; } if (b->interval > 0) { if (MinInterval > 0) { MinInterval = gcd(MinInterval, b->interval); } else { MinInterval = b->interval; } } else { b->bn = 1; /* force unhealthy */ } b->proto = 0; pos = hostPortExt(argv[argi+1], master_host, master_port); if (pos < 0) { message(LOG_ERR, "Illegal master: %s", argv[argi+1]); free(b); return argi+2; } else if (pos > 0) { master_ext = argv[argi+1] + pos; } salen = sizeof(ss); sa->sa_family = AF_UNSPEC; #ifdef AF_INET6 if (master_ext && !strcmp(master_ext, "v6")) sa->sa_family = AF_INET6; #endif if (host2sa(master_host, master_port, sa, &salen, NULL, NULL, 0)) { b->master = saDup(sa, salen); if (!b->master) { free(b); goto memerr; } b->check = b->master; } else { free(b); return argi+2; } pos = hostPortExt(argv[argi+2], backup_host, backup_port); if (pos < 0) { message(LOG_ERR, "Illegal backup: %s", argv[argi+2]); free(b); return argi+2; } salen = sizeof(ss); sa->sa_family = AF_UNSPEC; #ifdef AF_INET6 if (pos && !strcmp(argv[argi+2]+pos, "v6")) sa->sa_family = AF_INET6; #endif if (host2sa(backup_host, backup_port, sa, &salen, NULL, NULL, 0)) { b->backup = saDup(sa, salen); if (!b->backup) { free(b->master); free(b); goto memerr; } } else { free(b->master); free(b); return argi+2; } if (check_host || check_port || check_ext) { if (!check_host) check_host = master_host; if (!check_port) check_port = master_port; if (!check_ext) check_ext = master_ext; salen = sizeof(ss); sa->sa_family = AF_UNSPEC; #ifdef AF_INET6 if (check_ext && !strcmp(check_ext, "v6")) sa->sa_family = AF_INET6; #endif if (host2sa(check_host, check_port, sa, &salen, NULL, NULL, 0)) { b->check = saDup(sa, salen); if (!b->check) { free(b->backup); free(b->master); free(b); goto memerr; } } } b->chat = healthChat; b->next = backups; backups = b; return argi+2; } int str2num(char **pp, int rad) { char *p; int num; int i; p = *pp; num = 0; for (i=0; i < 3; i++) { /* 3 digit at most */ char c = p[i]; if ('0' <= c && c <= '9') { num = num * rad + c; } else { c = toupper(c); if (rad > 10 && ('A' <= c && c <= ('A' + rad - 11))) { num = num * rad + (c - 'A' + 10); } else { break; } } } *pp = p; return num; } char *str2bin(char *p, int *lenp) { char buf[BUFMAX]; char c; int i = 0; while ((c=*p++) && i < BUFMAX-5) { if (c == '\\') { c = *p++; switch(c) { case 'n': c = '\n'; break; case 'r': c = '\r'; break; case 't': c = '\t'; break; case '0': c = str2num(&p, 8); break; case 'x': c = str2num(&p, 16); break; case '\0': c = '\\'; p--; } } buf[i++] = c; } p = malloc(i); if (!p) { message(LOG_CRIT, "Out of memory, can't make str"); exit(1); } bcopy(buf, p, i); *lenp = i; return p; } int mkChat(int argc, int i, char *argv[]) { Chat *top, *bot; top = bot = NULL; i++; for ( ; i+1 < argc; i+=2) { Chat *cur; int err; if (argv[i][0] == '-' && argv[i][1] == '-') { healthChat = top; return i; } cur = malloc(sizeof(Chat)); if (!cur) { memerr: message(LOG_CRIT, "Out of memory, can't make Chat"); exit(1); } cur->send = str2bin(argv[i], &cur->len); if (!cur->send) { free(cur); goto memerr; } err = regcomp(&cur->expect, argv[i+1], REG_EXTENDED); if (err) { message(LOG_ERR, "RegEx compiling error: \"%s\" err=%d", argv[i+1], err); exit(1); } cur->next = NULL; if (!top) top = cur; if (bot) bot->next = cur; bot = cur; } message(LOG_ERR, "chat script ends unexpectedly"); exit(1); return i; } LBSet *findLBSet(struct sockaddr *sa) { LBSet *s; for (s=lbsets; s != NULL; s=s->next) { if (saComp(&s->dsts[0]->addr, sa)) { /* found */ if (Debug > 1) { char buf[LONGSTRMAX+1]; int len; int i; buf[LONGSTRMAX] = '\0'; strcpy(buf, "LB set:"); len = strlen(buf); for (i=0; i < s->ndsts && len < LONGSTRMAX-2; i++) { buf[len++] = ' '; addrport2str(&s->dsts[i]->addr, s->dsts[i]->len, (s->proto & proto_pair_d), buf+len, LONGSTRMAX-1-len, 0); len += strlen(buf+len); } message(LOG_DEBUG, "%s", buf); } return s; } } return NULL; } int lbsopts(int argc, int i, char *argv[]) { SockAddr *dsts[LB_MAX]; int ndsts = 0; LBSet *lbs; struct sockaddr_storage ss; struct sockaddr *sa = (struct sockaddr*)&ss; socklen_t salen; int proto = 0; i++; for ( ; i < argc; i++) { if (argv[i][0] == '-' && argv[i][1] == '-') break; if (ndsts >= LB_MAX) { message(LOG_ERR, "Too many load balancing hosts"); exit(1); } salen = sizeof(ss); sa->sa_family = AF_UNSPEC; if (!hostPort2sa(argv[i], sa, &salen, 0)) { message(LOG_ERR, "Illegal load balancing host: %s", argv[i]); exit(1); } dsts[ndsts] = saDup(sa, salen); if (!dsts[ndsts]) goto memerr; ndsts++; } lbs = malloc(sizeof(LBSet) + sizeof(SockAddr*) * ndsts); if (lbs) { int j; lbs->next = lbsets; lbs->proto = proto; lbs->ndsts = ndsts; for (j=0; j < ndsts; j++) lbs->dsts[j] = dsts[j]; lbsets = lbs; } else { memerr: message(LOG_CRIT, "Out of memory, can't make LB set"); exit(1); } return i; } char *stone2str(Stone *stone, char *str, int strlen) { int proto; char src[STRMAX+1]; addrport2str(&stone->listen->addr, stone->listen->len, (stone->proto & proto_stone_s), src, STRMAX, 0); src[STRMAX] = '\0'; proto = stone->proto; if ((proto & proto_command) == command_proxy) { snprintf(str, strlen, "stone %d: proxy <- %s", stone->sd, src); } else if ((proto & proto_command) == command_health) { snprintf(str, strlen, "stone %d: health <- %s", stone->sd, src); } else if ((proto & proto_command) == command_identd) { snprintf(str, strlen, "stone %d: identd <- %s", stone->sd, src); } else { char dst[STRMAX+1]; addrport2str(&stone->dsts[0]->addr, stone->dsts[0]->len, (stone->proto & proto_stone_d), dst, STRMAX, 0); dst[STRMAX] = '\0'; snprintf(str, strlen, "stone %d: %s <- %s", stone->sd, dst, src); } str[strlen] = '\0'; return str; } void ungetExBuf(ExBuf *ex) { ExBuf *freeptr = NULL; time_t now; time(&now); waitMutex(ExBufMutex); if (ex->start < 0) { freeMutex(ExBufMutex); message(LOG_ERR, "ungetExBuf duplication. can't happen, ignore"); return; } if (now - freeExBotClock > FREE_TIMEOUT) { if (nFreeExBot > 2) { freeptr = freeExBot->next; freeExBot->next = NULL; nFreeExBuf -= (nFreeExBot - 1); } else { freeExBot = freeExBuf; nFreeExBot = nFreeExBuf; } freeExBotClock = now; } ex->start = -1; ex->len = 0; ex->next = freeExBuf; freeExBuf = ex; nFreeExBuf++; freeMutex(ExBufMutex); if (freeptr) { if (Debug > 3) message(LOG_DEBUG, "freeExBot %d nfex=%d", nFreeExBot, nFreeExBuf); freeExBot = NULL; nFreeExBot = 0; while (freeptr) { ExBuf *p = freeptr; freeptr = freeptr->next; free(p); } } } ExBuf *getExBuf(void) { ExBuf *ret = NULL; time_t now; time(&now); waitMutex(ExBufMutex); if (freeExBuf) { ret = freeExBuf; freeExBuf = ret->next; nFreeExBuf--; if (nFreeExBuf < nFreeExBot) { nFreeExBot = nFreeExBuf; freeExBot = freeExBuf; freeExBotClock = now; } } freeMutex(ExBufMutex); if (!ret) { int size = XferBufMax; do { ret = malloc(sizeof(ExBuf) + size - BUFMAX); } while (!ret && XferBufMax > BUFMAX && (XferBufMax /= 2)); if (!ret) { message(LOG_CRIT, "Out of memory, no ExBuf"); return ret; } ret->bufmax = size; } ret->next = NULL; ret->start = 0; ret->len = 0; return ret; } ExBuf *getExData(Pair *pair, int type, int rmflag) { ExBuf *ex = pair->d; ExBuf *prev = NULL; while (ex) { int t = *(int*)ex->buf; if (t == type) { if (rmflag) { if (prev) prev->next = ex->next; else pair->d = ex->next; } return ex; } prev = ex; ex = ex->next; } return NULL; } ExBuf *newExData(Pair *pair, int type) { ExBuf *ex = getExBuf(); if (!ex) return NULL; *(int*)ex->buf = type; ex->next = pair->d; pair->d = ex; return ex; } /* modify dest if needed */ int modPairDest(Pair *p1, struct sockaddr *dst, socklen_t dstlenmax) { Pair *p2; socklen_t dstlen = 0; int offset = -1; /* offset in load balancing group */ #ifdef USE_SSL SSL *ssl; #endif p2 = p1->pair; if (p2 == NULL) return -1; #ifdef USE_SSL ssl = p2->ssl; if (ssl) { SSL_SESSION *sess = SSL_get1_session(ssl); if (sess) { unsigned char **match; if (Debug > 2) { char str[SSL_MAX_SSL_SESSION_ID_LENGTH * 2 + 1]; int len = sess->session_id_length; int i; if (len > SSL_MAX_SSL_SESSION_ID_LENGTH) len = SSL_MAX_SSL_SESSION_ID_LENGTH; for (i=0; i < len; i++) sprintf(&str[i*2], "%02x", sess->session_id[i]); str[i*2] = '\0'; message(LOG_DEBUG, "%d TCP %d: SSL session ID=%s len=%d", p2->stone->sd, p2->sd, str, sess->session_id_length); } match = SSL_SESSION_get_ex_data(sess, MatchIndex); if (match && p2->stone->ssl_server) { int lbparm = p2->stone->ssl_server->lbparm; int lbmod = p2->stone->ssl_server->lbmod; unsigned char *s; if (0 <= lbparm && lbparm <= 9) s = match[lbparm]; else s = match[1]; if (!s) s = match[0]; if (s && lbmod) { int offset2 = 0; offset = 0; while (*s) { if (offset2 >= 0) { if ('0' <= *s && *s <= '9') { offset2 = offset2 * 10 + (*s - '0'); } else { offset2 = -1; } } offset <<= 6; offset += (*s & 0x3f); s++; } if (offset2 > 0) offset = offset2; offset %= lbmod; if (Debug > 2) message(LOG_DEBUG, "%d TCP %d: pair %d lb%d=%d", p1->stone->sd, p1->sd, p2->sd, lbparm, offset); } } SSL_SESSION_free(sess); } } #endif if (offset < 0 && p1->stone->ndsts > 1) { /* load balancing */ int n = p1->stone->ndsts; offset = (p1->stone->proto & state_mask) % n; if (p1->stone->backups) { int i; for (i=0; i < n; i++) { Backup *b = p1->stone->backups[(offset+i) % n]; if (!b || b->bn == 0) { /* no backup or healthy, use it */ offset = (offset+i) % n; break; } if (Debug > 8) message(LOG_DEBUG, "%d TCP %d: ofs=%d is unhealthy, skipped", p1->stone->sd, p1->sd, (offset+i) % n); } } /* round robin */ p1->stone->proto = ((p1->stone->proto & ~state_mask) | ((offset+1) & state_mask)); } if (offset >= 0) { dstlen = p1->stone->dsts[offset]->len; if (dstlen < dstlenmax) bcopy(&p1->stone->dsts[offset]->addr, dst, dstlen); } if (p1->stone->backups) { Backup *backup; if (offset >= 0) backup = p1->stone->backups[offset]; else backup = p1->stone->backups[0]; if (backup) { backup->used = 2; if (backup->bn) { /* unhealthy */ dstlen = backup->backup->len; if (dstlen < dstlenmax) bcopy(&backup->backup->addr, dst, dstlen); } } } return dstlen; } /* relay UDP */ void message_origin(int pri, Origin *origin) { struct sockaddr_storage ss; struct sockaddr *name = (struct sockaddr*)&ss; socklen_t namelen = sizeof(ss); SOCKET sd; Stone *stone; int i; char str[LONGSTRMAX+1]; str[LONGSTRMAX] = '\0'; strntime(str, LONGSTRMAX, &origin->clock, -1); i = strlen(str); if (ValidSocket(origin->sd)) { if (getsockname(origin->sd, name, &namelen) < 0) { #ifdef WINDOWS errno = WSAGetLastError(); #endif if (Debug > 3) message(LOG_DEBUG, "%d UDP %d: Can't get socket's name err=%d", origin->stone->sd, origin->sd, errno); } else { addrport2str(name, namelen, proto_udp, str+i, LONGSTRMAX-i, 0), i = strlen(str); if (i < LONGSTRMAX-2) str[i++] = ' '; } } if (i > LONGSTRMAX) i = LONGSTRMAX; str[i] = '\0'; stone = origin->stone; if (stone) sd = stone->sd; else sd = INVALID_SOCKET; addrport2str(&origin->from->addr, origin->from->len, proto_udp, str+i, STRMAX-i, 0); str[STRMAX] = '\0'; message(pri, "%d UDP%3d:%3d %s", origin->stone->sd, origin->sd, sd, str); } void ungetPktBuf(PktBuf *pb) { if (pb->bufmax < pkt_len_max) { free(pb); /* never reuse short buffer */ return; } waitMutex(PkBufMutex); pb->next = freePktBuf; freePktBuf = pb; nFreePktBuf++; freeMutex(PkBufMutex); } PktBuf *getPktBuf(void) { PktBuf *ret = NULL; waitMutex(PkBufMutex); if (freePktBuf) { ret = freePktBuf; freePktBuf = ret->next; nFreePktBuf--; } freeMutex(PkBufMutex); if (ret && ret->bufmax < pkt_len_max) { free(ret); /* discard short buffer */ ret = NULL; } if (!ret) { int size = pkt_len_max; do { ret = malloc(sizeof(PktBuf) + size - BUFMAX); } while (!ret && pkt_len_max > BUFMAX && (pkt_len_max /= 2)); if (!ret) { message(LOG_CRIT, "Out of memory, no ExBuf"); return ret; } ret->common = type_pktbuf; ret->bufmax = size; } ret->next = NULL; ret->origin = NULL; ret->len = 0; return ret; } void freeOrigin(Origin *origin) { if (origin->from) free(origin->from); free(origin); } Origin *getOrigins(struct sockaddr *from, socklen_t fromlen, Stone *stone) { Origin *origin; Origin *origins = (Origin*)stone->p; SOCKET sd; #ifdef USE_EPOLL struct epoll_event ev; #endif for (origin=origins->next; origin != NULL && origin->from; origin=origin->next) { if (InvalidSocket(origin->sd)) continue; if (saComp(&origin->from->addr, from)) { origin->lock = 1; /* lock origin */ return origin; } } /* can't find origin, so create */ sd = socket(from->sa_family, SOCK_DGRAM, IPPROTO_UDP); if (InvalidSocket(sd)) { #ifdef WINDOWS errno = WSAGetLastError(); #endif message(LOG_ERR, "%d UDP: can't create datagram socket err=%d", stone->sd, errno); return NULL; } if (Debug > 3) { char addrport[STRMAX+1]; message(LOG_DEBUG, "%d UDP %d: New origin %s", stone->sd, sd, addrport2str(from, fromlen, proto_udp, addrport, STRMAX, 0)); } if (!(stone->proto & proto_block_d)) { #ifdef WINDOWS u_long param; param = 1; ioctlsocket(sd, FIONBIO, ¶m); #else fcntl(sd, F_SETFL, O_NONBLOCK); #endif } origin = malloc(sizeof(Origin)); if (!origin) { memerr: message(LOG_CRIT, "%d UDP %d: Out of memory, closing socket", stone->sd, sd); return NULL; } origin->common = type_origin; origin->sd = sd; origin->stone = stone; origin->from = saDup(from, fromlen); if (!origin->from) { free(origin); goto memerr; } origin->lock = 0; origin->xhost = NULL; #ifdef USE_EPOLL ev.events = EPOLLIN; ev.data.ptr = origin; if (epoll_ctl(ePollFd, EPOLL_CTL_ADD, sd, &ev) < 0) { message(LOG_ERR, "%d UDP %d: epoll_ctl ADD err=%d", stone->sd, sd, errno); freeOrigin(origin); return NULL; } #else waitMutex(FdRinMutex); FdSet(origin->sd, &rin); freeMutex(FdRinMutex); #endif waitMutex(OrigMutex); origin->next = origins->next; /* insert origin */ origins->next = origin; freeMutex(OrigMutex); return origin; } PktBuf *recvUDP(Stone *stone) { struct sockaddr_storage ss; struct sockaddr *from = (struct sockaddr*)&ss; socklen_t fromlen = sizeof(ss); Origin *origin; SOCKET sd; int flags = 0; char *dirstr; PktBuf *pb = getPktBuf(); pb->type = (stone->common & type_mask); if (pb->type == type_origin) { origin = (Origin*)stone; sd = origin->sd; stone = origin->stone; dirstr = "<"; #ifdef MSG_DONTWAIT if (!(stone->proto & proto_block_d)) flags = MSG_DONTWAIT; #endif } else { origin = NULL; sd = stone->sd; dirstr = ">"; #ifdef MSG_DONTWAIT if (!(stone->proto & proto_block_s)) flags = MSG_DONTWAIT; #endif } #ifdef MSG_TRUNC flags |= MSG_TRUNC; #endif pb->len = recvfrom(sd, pb->buf, pb->bufmax, flags, from, &fromlen); if (pb->len < 0) { #ifdef WINDOWS errno = WSAGetLastError(); #endif if (errno == EMSGSIZE) { if (Debug > 4) message(LOG_DEBUG, "%d UDP%s%d: recvfrom received larger msg", stone->sd, dirstr, sd); pb->len = pb->bufmax + 1; } else { message(LOG_ERR, "%d UDP%s%d: recvfrom failed err=%d", stone->sd, dirstr, sd, errno); end: ungetPktBuf(pb); return NULL; } } if (pb->type == type_stone) { /* outward */ XHosts *xhost = checkXhost(stone->xhosts, from, fromlen); if (!xhost) { if (Debug > 4) { char addrport[STRMAX+1]; addrport2str(from, fromlen, proto_udp, addrport, STRMAX, 0); addrport[STRMAX] = '\0'; message(LOG_DEBUG, "%d UDP%s%d: recvfrom denied %s", stone->sd, dirstr, sd, addrport); } goto end; } origin = getOrigins(from, fromlen, stone); if (!origin) goto end; origin->xhost = xhost; time(&origin->clock); } pb->origin = origin; if (pb->len > pb->bufmax || Debug > 4) { char addrport[STRMAX+1]; addrport2str(from, fromlen, proto_udp, addrport, STRMAX, 0); addrport[STRMAX] = '\0'; if (Debug > 4) message(LOG_DEBUG, "%d UDP%s%d: %d bytes received from %s", stone->sd, dirstr, origin->sd, pb->len, addrport); if (pb->len > pb->bufmax) { message(LOG_NOTICE, "%d UDP%s%d: recvfrom failed: larger packet " "(%d bytes) arrived from %s", stone->sd, dirstr, origin->sd, pb->len, addrport); while (pkt_len_max < pb->len) pkt_len_max <<= 1; ungetPktBuf(pb); return NULL; /* drop */ } } return pb; } int sendUDP(PktBuf *pb) { Origin *origin = pb->origin; Stone *stone = origin->stone; SOCKET sd; int flags = 0; struct sockaddr *sa; socklen_t salen; char *dirstr; if (pb->type == type_stone) { sd = origin->sd; sa = &stone->dsts[0]->addr; salen = stone->dsts[0]->len; dirstr = ">"; #ifdef MSG_DONTWAIT if (!(stone->proto & proto_block_d)) flags = MSG_DONTWAIT; #endif } else { sd = stone->sd; sa = &origin->from->addr; salen = origin->from->len; dirstr = "<"; #ifdef MSG_DONTWAIT if (!(stone->proto & proto_block_s)) flags = MSG_DONTWAIT; #endif } if (sendto(sd, pb->buf, pb->len, flags, sa, salen) != pb->len) { char addrport[STRMAX+1]; addrport2str(sa, salen, proto_udp, addrport, STRMAX, 0); addrport[STRMAX] = '\0'; #ifdef WINDOWS errno = WSAGetLastError(); #endif message(LOG_ERR, "%d UDP%s%d: sendto failed err=%d: to %s", stone->sd, dirstr, origin->sd, errno, addrport); return -1; } if (Debug > 4) { char addrport[STRMAX+1]; addrport2str(sa, salen, proto_udp, addrport, STRMAX, 0); addrport[STRMAX] = '\0'; message(LOG_DEBUG, "%d UDP%s%d: %d bytes sent to %s", stone->sd, dirstr, origin->sd, pb->len, addrport); } if ((origin->xhost->mode & XHostsMode_Dump) > 0) { char head[STRMAX+1]; snprintf(head, STRMAX, "%d UDP%s%d:", stone->sd, dirstr, origin->sd); head[STRMAX] = '\0'; packet_dump(head, pb->buf, pb->len, origin->xhost); } return pb->len; } void docloseUDP(Origin *origin) { #ifdef USE_EPOLL SOCKET sd = origin->sd; #endif if (Debug > 2) message(LOG_DEBUG, "%d UDP %d: close", origin->stone->sd, origin->sd); origin->lock = -1; /* request to close */ #ifdef USE_EPOLL origin->sd = INVALID_SOCKET; closesocket(sd); #else waitMutex(FdRinMutex); FD_CLR(origin->sd, &rin); freeMutex(FdRinMutex); #endif } int scanUDP( #ifndef USE_EPOLL fd_set *rop, fd_set *eop, #endif Origin *origins ) { Origin *origin, *prev; int n = 0; int all; time_t now; time(&now); if (origins) { all = 0; } else { origins = OriginTop; all = 1; } prev = origins; for (origin=origins->next; origin != NULL && (all || origin->from != NULL); prev=origin, origin=origin->next) { if (all && origin->from == NULL) { origins = origin; continue; } if (InvalidSocket(origin->sd) || origin->lock > 0) { Origin *old = origin; waitMutex(OrigMutex); if (prev->next == origin) { origin = prev; origin->next = old->next; /* remove `old' from list */ if (InvalidSocket(old->sd)) { freeOrigin(old); } else { old->lock = 0; old->next = origins->next; /* insert old on top */ origins->next = old; } } freeMutex(OrigMutex); goto next; } #ifndef USE_EPOLL if (origin->lock < 0) { int isset; waitMutex(FdRinMutex); isset = FD_ISSET(origin->sd, &rin); if (isset) FD_CLR(origin->sd, &rin); freeMutex(FdRinMutex); if (!isset) { closesocket(origin->sd); origin->sd = INVALID_SOCKET; } goto next; } if (FD_ISSET(origin->sd, rop) && FD_ISSET(origin->sd, &rin)) { PktBuf *pb = recvUDP((Stone*)origin); if (pb) { sendUDP(pb); ungetPktBuf(pb); } goto next; } #endif if (++n >= OriginMax || now - origin->clock > CONN_TIMEOUT) docloseUDP(origin); next: ; } return 1; } #define UDP_HEAD_LEN 2 /* sizeof(short): UDP packet length */ int recvPairUDP(Pair *pair) { Stone *stone = pair->stone; SOCKET sd = pair->sd; Pair *p; ExBuf *ex; ExBuf *t; int len; int flags = 0; struct sockaddr_storage ss; struct sockaddr *from = (struct sockaddr*)&ss; socklen_t fromlen = sizeof(ss); p = pair->pair; if (p == NULL) { /* no pair, no more read */ message(priority(pair), "%d UDP %d: no pair, closing", stone->sd, sd); return -1; } ex = p->b; /* bottom */ if (ex->len > 0) { /* not emply */ ex = getExBuf(); if (!ex) return -1; /* out of memory */ if (Debug > 4) message(LOG_DEBUG, "%d UDP %d: get ExBuf nbuf=%d", stone->sd, p->sd, p->nbuf); } ex->start = 0; #ifdef MSG_DONTWAIT if (!(stone->proto & proto_block_d)) flags = MSG_DONTWAIT; #endif #ifdef MSG_TRUNC flags |= MSG_TRUNC; #endif len = recvfrom(sd, ex->buf + UDP_HEAD_LEN, ex->bufmax - UDP_HEAD_LEN, flags, from, &fromlen); if (len < 0) { #ifdef WINDOWS errno = WSAGetLastError(); #endif message(LOG_ERR, "%d UDP %d: recvfrom err=%d", stone->sd, sd, errno); if (ex != p->b) ungetExBuf(ex); return -1; } time(&pair->clock); p->clock = pair->clock; pair->rx += len; if (Debug > 8) message(LOG_DEBUG, "%d UDP %d: recvfrom len=%d", stone->sd, sd, len); t = getExData(pair, data_peeraddr, 0); if (t) { SockAddr *peer = (SockAddr*)(t->buf + DATA_HEAD_LEN); if (!saComp(&peer->addr, from)) goto unknown; } else { /* from unknown */ char addrport[STRMAX+1]; unknown: addrport2str(from, fromlen, proto_udp, addrport, STRMAX, 0); addrport[STRMAX] = '\0'; message(LOG_ERR, "%d UDP %d: received from unknown %s", stone->sd, sd, addrport); if (ex != p->b) ungetExBuf(ex); return -1; } if (ex != p->b) { p->b->next = ex; p->b = ex; p->nbuf++; } ex->buf[0] = ((unsigned)len >> 8); ex->buf[1] = ((unsigned)len % 256); ex->len += UDP_HEAD_LEN + len; return ex->len; } static int sendPairUDPbuf(Stone *stone, Pair *pair, char *buf, int len) { int flags = 0; ExBuf *t; SockAddr *peer; int issrc = ((pair->proto & proto_command) == command_source); SOCKET sd; Pair *p = pair->pair; #ifdef MSG_DONTWAIT if (!(stone->proto & proto_block_d)) flags = MSG_DONTWAIT; #endif t = getExData(pair, data_peeraddr, 0); if (t) { peer = (SockAddr*)(t->buf + DATA_HEAD_LEN); } else if (!issrc) { int lenmax; int dstlen; t = newExData(pair, data_peeraddr); peer = (SockAddr*)(t->buf + DATA_HEAD_LEN); lenmax = t->bufmax - DATA_HEAD_LEN - SockAddrBaseSize; peer->len = stone->dsts[0]->len; bcopy(&stone->dsts[0]->addr, &peer->addr, peer->len); dstlen = modPairDest(pair, &peer->addr, lenmax); if (dstlen > 0) peer->len = dstlen; /* dest is modified */ } else { message(LOG_ERR, "%d UDPsd, (p ? p->sd : -1)); return -1; } if (issrc) sd = stone->sd; else sd = pair->sd; if (sendto(sd, buf, len, flags, &peer->addr, peer->len) != len) { char addrport[STRMAX+1]; addrport2str(&peer->addr, peer->len, proto_udp, addrport, STRMAX, 0); addrport[STRMAX] = '\0'; #ifdef WINDOWS errno = WSAGetLastError(); #endif if (issrc) { message(LOG_ERR, "%d UDPsd, (p ? p->sd : -1), errno, addrport); } else { message(LOG_ERR, "%d TCP%d>UDP%d: sendto failed err=%d: to %s", stone->sd, (p ? p->sd : -1), pair->sd, errno, addrport); } return -1; /* error */ } time(&pair->clock); if (p) p->clock = pair->clock; pair->tx += len; return 0; /* success */ } int sendPairUDP(Pair *pair) { Stone *stone = pair->stone; ExBuf *next = pair->t; ExBuf *cur = NULL; ExBuf *ex = NULL; /* dummy init to suppress warnings */ unsigned char *buf = NULL; int pos = 0; int len = 0; int err = 0; char prefix[STRMAX+1]; if ((pair->proto & proto_command) == command_source) { Pair *p = pair->pair; snprintf(prefix, STRMAX, "%d UDPsd, (p ? p->sd : -1)); } else { Pair *p = pair->pair; snprintf(prefix, STRMAX, "%d TCP%d>UDP%d:", stone->sd, (p ? p->sd : -1), pair->sd); } while (next) { ex = next; next = ex->next; int add; if (ex->len <= 0) { /* dispose empty buf */ if (ex != pair->b) ungetExBuf(ex); continue; } if (!cur) { cur = ex; buf = (unsigned char*)&cur->buf[cur->start]; pos = cur->len; len = (buf[0] << 8); if (pos == 1) { ExBuf *t; for (t=cur->next; t; t=t->next) { if (t->len > 0) { len += (unsigned)t->buf[t->start]; break; } } if (!t) break; /* must read header */ } else { /* assume UDP_HEAD_LEN == 2 */ len += buf[1]; } if (Debug > 8) message(LOG_DEBUG, "%s sendPairUDP len=%d (curbuf=%d)", prefix, len, cur->len); len += UDP_HEAD_LEN; if (len > cur->bufmax) { message(LOG_ERR, "%s sendPairUDP packet too large len=%d", prefix, len); err = -1; } else if (len > cur->bufmax - cur->start) { if (Debug > 6) message(LOG_DEBUG, "%s sendPairUDP len=%d " "is larger than (bufmax-start=%d)=%d, move", prefix, len, cur->start, cur->bufmax - cur->start); bcopy(cur->buf+cur->start, cur->buf, cur->len); buf = (unsigned char*)cur->buf; cur->start = 0; } if (len < cur->len) { /* cur contains next packet */ cur->start += len; cur->len -= len; goto complete; } else if (len == cur->len) { cur->len = cur->bufmax; /* mark not to be used */ cur->start = 0; goto complete; } else { cur->len = cur->bufmax; /* mark not to be used */ cur->start = 0; } continue; } add = len - pos; if (ex->len > add) { /* ex contains next packet */ ex->start += add; ex->len -= add; } else { /* use entire buf */ add = ex->len; ex->len = ex->bufmax; /* mark not to be used */ ex->start = 0; } if (!err) bcopy(ex->buf+ex->start, buf+pos, add); pos += add; if (ex != pair->b) ungetExBuf(ex); if (pos >= len) { /* complete the packet */ complete: if (!err) { err = sendPairUDPbuf(stone, pair, (char*)(buf+UDP_HEAD_LEN), len-UDP_HEAD_LEN); if (!err) { if ((pair->xhost->mode & XHostsMode_Dump) > 0 || ((pair->proto & proto_first_w) && Debug > 3)) message_buf(pair, len, "tu"); } } if (cur != pair->b) ungetExBuf(cur); cur = NULL; } } if (ex == pair->b) { if (ex->len == ex->bufmax) ex->len = 0; pair->t = ex; } else { if (0 < ex->len && ex->len < ex->bufmax) { pair->t = ex; } else { pair->t = ex->next; ungetExBuf(ex); } } return err; } /* relay TCP */ void message_pair(int pri, Pair *pair) { struct sockaddr_storage ss; struct sockaddr *name = (struct sockaddr*)&ss; socklen_t namelen = sizeof(ss); SOCKET sd, psd; Pair *p; int i; char str[LONGSTRMAX+1]; str[LONGSTRMAX] = '\0'; strntime(str, LONGSTRMAX, &pair->clock, -1); i = strlen(str); sd = pair->sd; if (ValidSocket(sd)) { if (getsockname(sd, name, &namelen) < 0) { #ifdef WINDOWS errno = WSAGetLastError(); #endif if (Debug > 3) message(LOG_DEBUG, "%d TCP %d: Can't get socket's name err=%d", pair->stone->sd, sd, errno); } else { addrport2str(name, namelen, 0, str+i, LONGSTRMAX-i, 0); i = strlen(str); if (i < LONGSTRMAX-2) str[i++] = ' '; } namelen = sizeof(ss); if (getpeername(sd, name, &namelen) < 0) { #ifdef WINDOWS errno = WSAGetLastError(); #endif if (Debug > 3) message(LOG_DEBUG, "%d TCP %d: Can't get peer's name err=%d", pair->stone->sd, sd, errno); } else { addrport2str(name, namelen, 0, str+i, LONGSTRMAX-i, 0); i += strlen(str+i); } } if (i > LONGSTRMAX) i = LONGSTRMAX; str[i] = '\0'; p = pair->pair; if (p) psd = p->sd; else psd = INVALID_SOCKET; message(pri, "%d TCP%3d:%3d %08x %d %s tx:%d rx:%d lp:%d", pair->stone->sd, sd, psd, pair->proto, pair->count, str, pair->tx, pair->rx, pair->loop); } #ifdef USE_SSL static void printSSLinfo(int pri, SSL *ssl) { X509 *peer; char *p = (char *)SSL_get_cipher(ssl); if (p == NULL) p = ""; message(pri, "[SSL cipher=%s]", p); peer = SSL_get_peer_certificate(ssl); if (peer) { char buf[LONGSTRMAX+1]; ASN1_INTEGER *n = X509_get_serialNumber(peer); if (n) message(pri, "[SSL serial=%lx]", ASN1_INTEGER_get(n)); buf[LONGSTRMAX] = '\0'; if (X509_NAME_oneline(X509_get_subject_name(peer), buf, LONGSTRMAX)) message(pri, "[SSL subject=%s]", buf); if (X509_NAME_oneline(X509_get_issuer_name(peer), buf, LONGSTRMAX)) message(pri, "[SSL issuer=%s]", buf); X509_free(peer); } } int doSSL_accept(Pair *pair) { int err, ret; SOCKET sd; SSL *ssl; if (!pair) return -1; sd = pair->sd; if (InvalidSocket(sd)) return -1; ssl = pair->ssl; if (!ssl) { ssl = SSL_new(pair->stone->ssl_server->ctx); if (!ssl) { message(LOG_ERR, "%d TCP %d: SSL_new failed", pair->stone->sd, sd); return -1; } SSL_set_ex_data(ssl, PairIndex, pair); SSL_set_fd(ssl, sd); pair->ssl = ssl; } pair->ssl_flag &= ~(sf_ab_on_r | sf_ab_on_w); pair->proto |= proto_dirty; ret = SSL_accept(ssl); if (Debug > 7) message(LOG_DEBUG, "%d TCP %d: SSL_accept ret=%d, state=%x, " "finished=%x, in_init=%x/%x", pair->stone->sd, sd, ret, SSL_state(ssl), SSL_is_init_finished(ssl), SSL_in_init(ssl), SSL_in_accept_init(ssl)); if (ret > 0) { /* success */ if (SSL_in_accept_init(ssl)) { if (pair->stone->ssl_server->verbose) { message(LOG_NOTICE, "%d TCP %d: SSL_accept unexpected EOF", pair->stone->sd, sd); message_pair(LOG_NOTICE, pair); } return -1; /* unexpected EOF */ } /* src & pair is connected */ pair->proto |= (proto_connect | proto_dirty); if (Debug > 3) { SSL_CTX *ctx = pair->stone->ssl_server->ctx; message(LOG_DEBUG, "%d TCP %d: SSL_accept succeeded " "sess=%ld accept=%ld hits=%ld", pair->stone->sd, sd, SSL_CTX_sess_number(ctx), SSL_CTX_sess_accept(ctx), SSL_CTX_sess_hits(ctx)); } if (pair->stone->ssl_server->verbose) printSSLinfo(LOG_DEBUG, ssl); return ret; } err = SSL_get_error(ssl, ret); if (err == SSL_ERROR_WANT_READ) { pair->ssl_flag |= sf_ab_on_r; ret = 0; } else if (err == SSL_ERROR_WANT_WRITE) { pair->ssl_flag |= sf_ab_on_w; ret = 0; } else if (err == SSL_ERROR_SYSCALL) { unsigned long e = ERR_get_error(); if (e == 0) { #ifdef WINDOWS errno = WSAGetLastError(); #endif if (errno == EINTR || errno == EAGAIN) { pair->ssl_flag |= (sf_ab_on_r | sf_ab_on_r); if (Debug > 8) message(LOG_DEBUG, "%d TCP %d: SSL_accept " "interrupted sf=%x", pair->stone->sd, sd, pair->ssl_flag); return 0; } if (errno == 0) { if (Debug > 0) message(LOG_DEBUG, "%d TCP %d: SSL_accept " "shutdowned by peer sf=%x errno=%d", pair->stone->sd, sd, pair->ssl_flag, errno); return -1; /* shutdowned */ } message(priority(pair), "%d TCP %d: SSL_accept " "I/O error sf=%x errno=%d", pair->stone->sd, sd, pair->ssl_flag, errno); } else { message(priority(pair), "%d TCP %d: SSL_accept sf=%x %s", pair->stone->sd, sd, pair->ssl_flag, ERR_error_string(e, NULL)); } return ret; } else if (err == SSL_ERROR_SSL) { unsigned long e = ERR_get_error(); message(priority(pair), "%d TCP %d: SSL_accept lib %s", pair->stone->sd, sd, ERR_error_string(e, NULL)); return -1; /* error */ } if (Debug > 4) message(LOG_DEBUG, "%d TCP %d: SSL_accept interrupted sf=%x err=%d", pair->stone->sd, sd, pair->ssl_flag, err); return ret; } int doSSL_connect(Pair *pair) { int ret; int err; SOCKET sd; SSL *ssl; if (!pair) return -1; sd = pair->sd; if (InvalidSocket(sd)) return -1; ssl = pair->ssl; if (!ssl) { ssl = SSL_new(pair->stone->ssl_client->ctx); if (!ssl) { message(LOG_ERR, "%d TCP %d: SSL_new failed", pair->stone->sd, sd); return -1; } SSL_set_ex_data(ssl, PairIndex, pair); SSL_set_fd(ssl, sd); pair->ssl = ssl; } #ifndef OPENSSL_NO_TLSEXT if (pair->stone->ssl_client->sslparm & sslparm_sni) { if (!SSL_set_tlsext_host_name(ssl, pair->stone->ssl_client->name)) { message(LOG_ERR, "%d TCP %d: Can't set TLS servername: %s", pair->stone->sd, sd, pair->stone->ssl_client->name); } } #endif pair->ssl_flag &= ~(sf_cb_on_r | sf_cb_on_w); pair->proto |= proto_dirty; ret = SSL_connect(ssl); if (ret > 0) { /* success */ Pair *p = pair->pair; /* pair & dst is connected */ pair->proto |= (proto_connect | proto_dirty); if (p) p->proto |= proto_dirty; /* src */ if (Debug > 3) { SSL_CTX *ctx = pair->stone->ssl_client->ctx; message(LOG_DEBUG, "%d TCP %d: SSL_connect succeeded " "sess=%ld connect=%ld hits=%ld", pair->stone->sd, sd, SSL_CTX_sess_number(ctx), SSL_CTX_sess_connect(ctx), SSL_CTX_sess_hits(ctx)); message_pair(LOG_DEBUG, pair); } if (pair->stone->ssl_client->verbose) printSSLinfo(LOG_DEBUG, ssl); return ret; } err = SSL_get_error(ssl, ret); if (err == SSL_ERROR_WANT_READ) { pair->ssl_flag |= sf_cb_on_r; ret = 0; } else if (err == SSL_ERROR_WANT_WRITE) { pair->ssl_flag |= sf_cb_on_w; ret = 0; } else if (err == SSL_ERROR_SYSCALL) { unsigned long e = ERR_get_error(); if (e == 0) { #ifdef WINDOWS errno = WSAGetLastError(); #endif if (errno == 0) { return 1; /* success ? */ } else if (errno == EINTR || errno == EAGAIN) { pair->ssl_flag |= (sf_cb_on_r | sf_cb_on_r); if (Debug > 8) message(LOG_DEBUG, "%d TCP %d: SSL_connect " "interrupted sf=%x", pair->stone->sd, sd, pair->ssl_flag); return 0; } message(priority(pair), "%d TCP %d: SSL_connect " "I/O error sf=%x errno=%d", pair->stone->sd, sd, pair->ssl_flag, errno); } else { message(priority(pair), "%d TCP %d: SSL_connect sf=%x %s", pair->stone->sd, sd, pair->ssl_flag, ERR_error_string(e, NULL)); } return ret; } if (Debug > 4) message(LOG_DEBUG, "%d TCP %d: SSL_connect interrupted sf=%x err=%d", pair->stone->sd, sd, pair->ssl_flag, err); return ret; } int doSSL_shutdown(Pair *pair, int how) { int ret; int err; int i; SOCKET sd; SSL *ssl; StoneSSL *ss; if (!pair) return -1; sd = pair->sd; if (InvalidSocket(sd)) return -1; ssl = pair->ssl; if (!ssl) return -1; if (how >= 0) pair->ssl_flag = (how & sf_mask); else pair->ssl_flag = sf_mask; if ((pair->proto & proto_command) == command_source) { ss = pair->stone->ssl_server; } else { ss = pair->stone->ssl_client; } if (ss->shutdown_mode) { int state = SSL_get_shutdown(ssl); SSL_set_shutdown(ssl, (state | ss->shutdown_mode)); } for (i=0; i < 4; i++) { ret = SSL_shutdown(ssl); if (ret != 0) break; } if (ret == 0 && ss->shutdown_mode == 0) { if (Debug > 4) message(LOG_DEBUG, "%d TCP %d: SSL_shutdown ret=%d sf=%x, " "so don't wait peer's notify", pair->stone->sd, sd, ret, pair->ssl_flag); SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN); ret = SSL_shutdown(ssl); } if (ret < 0) { err = SSL_get_error(ssl, ret); if (Debug > 4) message(LOG_DEBUG, "%d TCP %d: SSL_shutdown ret=%d err=%d sf=%x", pair->stone->sd, sd, ret, err, pair->ssl_flag); if (err == SSL_ERROR_WANT_READ) { pair->ssl_flag |= sf_sb_on_r; } else if (err == SSL_ERROR_WANT_WRITE) { pair->ssl_flag |= sf_sb_on_w; } else if (err == SSL_ERROR_SYSCALL) { unsigned long e = ERR_get_error(); if (e == 0) { #ifdef WINDOWS errno = WSAGetLastError(); #endif if (errno == 0) { ret = 1; /* success ? */ } else if (errno == EINTR || errno == EAGAIN) { pair->ssl_flag |= (sf_sb_on_r | sf_sb_on_r); if (Debug > 8) message(LOG_DEBUG, "%d TCP %d: SSL_shutdown " "interrupted sf=%x", pair->stone->sd, sd, pair->ssl_flag); } else { message(priority(pair), "%d TCP %d: SSL_shutdown " "I/O error sf=%x errno=%d", pair->stone->sd, sd, pair->ssl_flag, errno); } } else { message(priority(pair), "%d TCP %d: SSL_shutdown sf=%x %s", pair->stone->sd, sd, pair->ssl_flag, ERR_error_string(e, NULL)); } } else { if (Debug > 4) message(LOG_DEBUG, "%d TCP %d: SSL_shutdown interrupted sf=%x err=%d", pair->stone->sd, sd, pair->ssl_flag, err); } } else if (ret == 0) { if (Debug > 4) message(priority(pair), "%d TCP %d: SSL_shutdown error " "ret=%d sf=%x, reset connection", pair->stone->sd, sd, ret, pair->ssl_flag); shutdown(sd, 2); ret = 0; } if (ret > 0) { /* success */ if (Debug > 4) message(LOG_DEBUG, "%d TCP %d: SSL_shutdown sf=%x", pair->stone->sd, sd, pair->ssl_flag); if ((pair->ssl_flag & sf_mask) != sf_mask) shutdown(sd, (pair->ssl_flag & sf_mask)); } return ret; } #endif /* USE_SSL */ int doshutdown(Pair *pair, int how) { #ifdef USE_SSL SSL *ssl; #endif if (!pair) return -1; #ifdef USE_SSL ssl = pair->ssl; if (ssl) return doSSL_shutdown(pair, how); else { #endif if (Debug > 4) message(LOG_DEBUG, "%d TCP %d: shutdown how=%d", pair->stone->sd, pair->sd, how); return shutdown(pair->sd, how); #ifdef USE_SSL } #endif } Pair *newPair(void) { Pair *pair = NULL; waitMutex(FPairMutex); if (freePairs) { pair = freePairs; freePairs = pair->next; nFreePairs--; } freeMutex(FPairMutex); if (!pair) pair = malloc(sizeof(Pair)); if (pair) { pair->common = type_pair; pair->t = getExBuf(); if (!pair->t) { free(pair); return NULL; } pair->nbuf = 1; pair->sd = INVALID_SOCKET; pair->stone = NULL; pair->proto = 0; pair->xhost = NULL; pair->timeout = PairTimeOut; pair->count = 0; pair->b = pair->t; pair->d = NULL; pair->log = NULL; pair->tx = 0; pair->rx = 0; pair->loop = 0; time(&pair->clock); pair->pair = NULL; pair->next = NULL; pair->prev = NULL; #ifdef USE_SSL pair->ssl = NULL; pair->ssl_flag = 0; #endif } return pair; } void freePair(Pair *pair) { SOCKET sd; TimeLog *log; #ifdef USE_SSL SSL *ssl; #endif ExBuf *ex; if (!pair) return; sd = pair->sd; pair->sd = INVALID_SOCKET; if (Debug > 8) message(LOG_DEBUG, "%d TCP %d: freePair", pair->stone->sd, sd); ex = pair->d; pair->d = NULL; while (ex) { ExBuf *f = ex; ex = f->next; f->next = NULL; ungetExBuf(f); } log = pair->log; if (log) { pair->log = NULL; free(log); } #ifdef USE_SSL ssl = pair->ssl; if (ssl) { SSL_CTX *ctx = NULL; int state; pair->ssl = NULL; state = SSL_get_shutdown(ssl); if (!(state & SSL_RECEIVED_SHUTDOWN) && Debug > 2) { message(LOG_DEBUG, "%d TCP %d: SSL close notify was not received", pair->stone->sd, sd); } if (!(state & SSL_SENT_SHUTDOWN) && Debug > 2) { message(LOG_DEBUG, "%d TCP %d: SSL close notify was not sent", pair->stone->sd, sd); SSL_set_shutdown(ssl, (state | SSL_SENT_SHUTDOWN)); } SSL_free(ssl); if (pair->stone->proto & proto_ssl_s) { ctx = pair->stone->ssl_server->ctx; } if (ctx) SSL_CTX_flush_sessions(ctx, pair->clock); } #endif pair->b = NULL; ex = pair->t; pair->t = NULL; while (ex) { ExBuf *f = ex; ex = f->next; f->next = NULL; pair->nbuf--; if (Debug > 4) message(LOG_DEBUG, "%d TCP %d: freePair " "unget ExBuf nbuf=%d nfex=%d", pair->stone->sd, sd, pair->nbuf, nFreeExBuf); ungetExBuf(f); } if (ValidSocket(sd)) { #ifdef USE_EPOLL if (Debug > 6) message(LOG_DEBUG, "%d TCP %d: freePair " "epoll_ctl %d DEL %x", pair->stone->sd, sd, ePollFd, (int)pair); epoll_ctl(ePollFd, EPOLL_CTL_DEL, sd, NULL); #endif closesocket(sd); } waitMutex(FPairMutex); if (pair->clock == 0) { freeMutex(FPairMutex); message(LOG_ERR, "freePair duplication. can't happen, ignore"); return; } pair->clock = 0; pair->next = freePairs; freePairs = pair; nFreePairs++; freeMutex(FPairMutex); } void insertPairs(Pair *p1) { Pair *p2 = p1->pair; Stone *stone = p1->stone; p1->next = p2; /* link pair each other */ p2->prev = p1; waitMutex(PairMutex); p2->next = stone->pairs->next; /* insert pair */ if (stone->pairs->next != NULL) stone->pairs->next->prev = p2; p1->prev = stone->pairs; stone->pairs->next = p1; freeMutex(PairMutex); if (Debug > 4) { message(LOG_DEBUG, "%d TCP %d: pair %d inserted", stone->sd, p1->sd, p2->sd); message_pair(LOG_DEBUG, p1); } } void message_time_log(Pair *pair) { TimeLog *log = pair->log; if (log && log->clock) { #ifdef THREAD_UNSAFE struct tm *t = localtime(&log->clock); #else struct tm tm; struct tm *t = localtime_r(&log->clock, &tm); #endif time_t now; time(&now); message(log->pri, "%02d:%02d:%02d %d %s", t->tm_hour, t->tm_min, t->tm_sec, (int)(now - log->clock), log->str); log->clock = 0; } } /* after connect(2) successfully completed */ void connected(Pair *pair) { Pair *p = pair->pair; if (Debug > 2) message(LOG_DEBUG, "%d TCP %d: established to %d %08x %08x", pair->stone->sd, p->sd, pair->sd, p->proto, pair->proto); time(&lastEstablished); /* now successfully connected */ #ifdef USE_SSL if (pair->stone->proto & proto_ssl_d) { if (doSSL_connect(pair) < 0) { /* SSL_connect fails, shutdown pairs */ if (!(p->proto & proto_shutdown)) if (doshutdown(p, 2) >= 0) p->proto |= (proto_shutdown | proto_dirty); p->proto |= (proto_close | proto_dirty); pair->proto |= (proto_close | proto_dirty); return; } } else #endif /* pair & dst is connected */ { pair->proto |= (proto_connect | proto_dirty); p->proto |= proto_dirty; /* src */ } /* SSL connection may not be established yet, but we can prepare for read/write */ if (pair->t->len > 0) { if (Debug > 8) message(LOG_DEBUG, "%d TCP %d: waiting %d bytes to write", pair->stone->sd, pair->sd, pair->t->len); if (!(pair->proto & proto_shutdown)) pair->proto |= (proto_select_w | proto_dirty); } else if (!(pair->proto & proto_ohttp_d)) { if (Debug > 8) message(LOG_DEBUG, "%d TCP %d: request to read 1st", pair->stone->sd, p->sd); if (!(p->proto & proto_eof)) p->proto |= (proto_select_r | proto_dirty); } if (!(p->proto & proto_ohttp_s)) { if (p->t->len > 0) { if (Debug > 8) message(LOG_DEBUG, "%d TCP %d: waiting %d bytes to write", pair->stone->sd, p->sd, p->t->len); if (!(p->proto & proto_shutdown)) p->proto |= (proto_select_w | proto_dirty); } else { if (Debug > 8) message(LOG_DEBUG, "%d TCP %d: request to read", pair->stone->sd, pair->sd); if (!(pair->proto & proto_eof)) pair->proto |= (proto_select_r | proto_dirty); } } } void message_conn(int pri, Conn *conn) { SOCKET sd = INVALID_SOCKET; Pair *p1, *p2; int proto = 0; int i = 0; char str[LONGSTRMAX+1]; str[LONGSTRMAX] = '\0'; p1 = conn->pair; if (p1) { p2 = p1->pair; strntime(str, LONGSTRMAX, &p1->clock, -1); i = strlen(str); proto = p1->proto; if (p2) sd = p2->sd; } addrport2str(&conn->dst->addr, conn->dst->len, (proto & proto_pair_d), str+i, LONGSTRMAX-i, 0); i = strlen(str); if (i > LONGSTRMAX) i = LONGSTRMAX; str[i] = '\0'; message(pri, "Conn %d: %08x %s", sd, proto, str); } int doconnect(Pair *p1, struct sockaddr *sa, socklen_t salen) { struct sockaddr_storage ss; struct sockaddr *dst = (struct sockaddr*)&ss; /* destination */ socklen_t dstlen; int ret; Pair *p2; time_t clock; char addrport[STRMAX+1]; #ifdef USE_EPOLL struct epoll_event ev; #endif #ifdef WINDOWS u_long param; #endif if (p1 == NULL) return -1; p2 = p1->pair; if (p2 == NULL) return -1; if (!(p2->proto & proto_connect)) return 0; bcopy(sa, dst, salen); dstlen = salen; time(&clock); if (Debug > 8) message(LOG_DEBUG, "%d TCP %d: doconnect", p1->stone->sd, p1->sd); ret = modPairDest(p1, dst, sizeof(ss)); if (ret > 0) dstlen = ret; /* dest is modified */ else if (ret == -2) return ret; /* dest is not detemined yet */ /* now destination is determined, engage */ if (!(p1->stone->proto & proto_block_d)) { #ifdef WINDOWS param = 1; ioctlsocket(p1->sd, FIONBIO, ¶m); #else fcntl(p1->sd, F_SETFL, O_NONBLOCK); #endif } addrport[0] = '\0'; if (Debug > 2) { addrport2strOnce(dst, dstlen, (p1->proto & proto_pair_d), addrport, STRMAX, 0); message(LOG_DEBUG, "%d TCP %d: connecting to TCP %d %s", p1->stone->sd, p2->sd, p1->sd, addrport); } if (p1->proto & proto_dgram) { ret = 0; /* do nothing */ } else { ret = connect(p1->sd, dst, dstlen); } if (ret < 0) { #ifdef WINDOWS errno = WSAGetLastError(); #endif if (errno == EINPROGRESS) { p1->proto |= (proto_conninprog | proto_dirty); if (Debug > 3) message(LOG_DEBUG, "%d TCP %d: connection in progress", p1->stone->sd, p1->sd); goto done; } else if (errno == EINTR) { if (Debug > 4) message(LOG_DEBUG, "%d TCP %d: connect interrupted", p1->stone->sd, p1->sd); if (clock - p1->clock < CONN_TIMEOUT) return 0; addrport2strOnce(dst, dstlen, (p1->proto & proto_pair_d), addrport, STRMAX, 0); message(priority(p2), "%d TCP %d: connect timeout to %s", p2->stone->sd, p2->sd, addrport); } else if (errno == EISCONN || errno == EADDRINUSE #ifdef EALREADY || errno == EALREADY #endif ) { if (Debug > 4) { /* SunOS's bug ? */ message(LOG_DEBUG, "%d TCP %d: connect bug err=%d", p1->stone->sd, p1->sd, errno); message_pair(LOG_DEBUG, p1); } } else { addrport2strOnce(dst, dstlen, (p1->proto & proto_pair_d), addrport, STRMAX, 0); message(priority(p1), "%d TCP %d: can't connect err=%d: to %s", p1->stone->sd, p1->sd, errno, addrport); } } if (ret < 0 /* fail to connect */ || (p1->proto & proto_close) || (p2->proto & proto_close)) { if (!(p2->proto & proto_shutdown)) if (doshutdown(p2, 2) >= 0) p2->proto |= (proto_shutdown | proto_dirty); p2->proto |= (proto_close | proto_dirty); p1->proto |= (proto_close | proto_dirty); return -1; } connected(p1); done: #ifdef USE_EPOLL ev.events = EPOLLONESHOT; ev.data.ptr = p1; if (Debug > 6) message(LOG_DEBUG, "%d TCP %d: doconnect epoll_ctl %d ADD %x", p1->stone->sd, p1->sd, ePollFd, (int)ev.data.ptr); if (epoll_ctl(ePollFd, EPOLL_CTL_ADD, p1->sd, &ev) < 0) { message(LOG_ERR, "%d TCP %d: doconnect epoll_ctl %d ADD err=%d", p1->stone->sd, p1->sd, ePollFd, errno); } #endif return 1; } void freeConn(Conn *conn) { if (conn->dst) free(conn->dst); free(conn); } int reqconn(Pair *pair, /* request pair to connect to destination */ struct sockaddr *dst, socklen_t dstlen) { /* connect to */ int ret; Conn *conn; Pair *p = pair->pair; if ((pair->proto & proto_command) == command_proxy || (pair->proto & proto_command) == command_health || (pair->proto & proto_command) == command_identd) { pair->proto |= proto_noconnect; if (p && !(p->proto & (proto_eof | proto_close))) { /* must read request header */ p->proto |= (proto_select_r | proto_dirty); } return 0; } ret = doconnect(pair, dst, dstlen); if (ret < 0) { if (ret == -2) { /* must read more to determine dest */ p->proto |= (proto_select_r | proto_dirty); return 0; } return -1; /* error */ } if (ret > 0) return ret; /* connected or connection in progress */ conn = malloc(sizeof(Conn)); if (!conn) { memerr: message(LOG_CRIT, "%d TCP %d: out of memory", (p ? p->stone->sd : -1), (p ? p->sd : -1)); return -1; } time(&pair->clock); p->clock = pair->clock; pair->count += REF_UNIT; /* request to connect */ conn->pair = pair; conn->dst = saDup(dst, dstlen); if (!conn->dst) { free(conn); goto memerr; } conn->lock = 0; waitMutex(ConnMutex); conn->next = conns.next; conns.next = conn; freeMutex(ConnMutex); return 0; } void asyncConn(Conn *conn) { Pair *p1, *p2; ASYNC_BEGIN; if (Debug > 8) message(LOG_DEBUG, "asyncConn"); p1 = conn->pair; if (p1 == NULL) { conn->pair = NULL; conn->lock = -1; } else { int ret = doconnect(p1, &conn->dst->addr, conn->dst->len); if (ret == 0 || ret == -2) { conn->lock = 0; } else { /* no more request to connect */ if (p1) p1->count -= REF_UNIT; conn->pair = NULL; conn->lock = -1; } } if (p1) { #ifndef USE_EPOLL p1->proto &= ~proto_thread; #endif p1->proto |= proto_dirty; p2 = p1->pair; } else { p2 = NULL; } if (p2) { #ifndef USE_EPOLL p2->proto &= ~proto_thread; #endif p2->proto |= proto_dirty; } ASYNC_END; } /* scan conn request */ int scanConns(void) { Conn *conn, *pconn; Pair *p1, *p2; if (Debug > 8) message(LOG_DEBUG, "scanConns"); pconn = &conns; for (conn=conns.next; conn != NULL; conn=conn->next) { p1 = conn->pair; if (p1) p2 = p1->pair; if (p1 && !(p1->proto & proto_close) && p2 && !(p2->proto & proto_close)) { if ((p2->proto & proto_connect) && conn->lock == 0 #ifndef USE_EPOLL && !(p1->proto & proto_thread) && !(p2->proto & proto_thread) #endif ) { conn->lock = 1; /* lock conn */ if (Debug > 4) message_conn(LOG_DEBUG, conn); #ifndef USE_EPOLL p1->proto |= (proto_thread | proto_dirty); p2->proto |= (proto_thread | proto_dirty); #endif ASYNC(asyncConn, conn); } } else { waitMutex(ConnMutex); if (pconn->next == conn && conn->lock <= 0) { pconn->next = conn->next; /* remove conn */ freeConn(conn); conn = pconn; } freeMutex(ConnMutex); } pconn = conn; } return 1; } Pair *acceptPair(Stone *stone) { struct sockaddr_storage ss; struct sockaddr *from = (struct sockaddr*)&ss; socklen_t fromlen = sizeof(ss); Pair *pair; #ifdef USE_EPOLL struct epoll_event ev; #endif SOCKET nsd = accept(stone->sd, from, &fromlen); if (InvalidSocket(nsd)) { #ifdef WINDOWS errno = WSAGetLastError(); #endif if (errno == EINTR) { if (Debug > 4) message(LOG_DEBUG, "stone %d: accept interrupted", stone->sd); return NULL; } else if (errno == EAGAIN) { if (Debug > 4) message(LOG_DEBUG, "stone %d: accept no connection", stone->sd); return NULL; } #ifndef NO_FORK else if (errno == EBADF && Debug < 5) { return NULL; } #endif message(LOG_ERR, "stone %d: accept error err=%d", stone->sd, errno); return NULL; } pair = newPair(); if (!pair) { message(LOG_CRIT, "stone %d: out of memory, closing TCP %d", stone->sd, nsd); closesocket(nsd); freePair(pair); return NULL; } bcopy(&fromlen, pair->t->buf, sizeof(fromlen)); /* save to ExBuf */ bcopy(from, pair->t->buf + sizeof(fromlen), fromlen); pair->sd = nsd; pair->stone = stone; pair->proto = ((stone->proto & proto_pair_s & ~proto_command) | proto_first_r | proto_first_w | command_source); pair->timeout = stone->timeout; #ifdef USE_EPOLL ev.events = EPOLLONESHOT; ev.data.ptr = pair; if (Debug > 6) message(LOG_DEBUG, "%d TCP %d: acceptPair epoll_ctl %d ADD %x", stone->sd, pair->sd, ePollFd, (int)ev.data.ptr); if (epoll_ctl(ePollFd, EPOLL_CTL_ADD, pair->sd, &ev) < 0) { message(LOG_ERR, "%d TCP %d: acceptPair epoll_ctl %d ADD err=%d", stone->sd, pair->sd, ePollFd, errno); } #endif return pair; } int getident(char *str, struct sockaddr *sa, socklen_t salen, int cport, struct sockaddr *csa, socklen_t csalen) { /* (size of str) >= STRMAX+1 */ SOCKET sd; struct sockaddr_storage ss; struct sockaddr *peer = (struct sockaddr*)&ss; socklen_t peerlen = sizeof(ss); int sport = getport(sa); char buf[LONGSTRMAX+1]; char c; int len; int ret; char addr[STRMAX+1]; #ifdef WINDOWS u_long param; #endif time_t start, now; #ifdef USE_EPOLL int epfd = INVALID_SOCKET; struct epoll_event ev; struct epoll_event evs[1]; #endif time(&start); bcopy(sa, peer, salen); peerlen = salen; if (str) { str[0] = '\0'; } sd = socket(peer->sa_family, SOCK_STREAM, IPPROTO_TCP); if (InvalidSocket(sd)) { #ifdef WINDOWS errno = WSAGetLastError(); #endif if (Debug > 0) message(LOG_DEBUG, "ident: can't create socket err=%d", errno); return 0; } saPort(csa, 0); if (bind(sd, csa, csalen) < 0) { #ifdef WINDOWS errno = WSAGetLastError(); #endif if (Debug > 0) message(LOG_DEBUG, "ident: can't bind socket err=%d", errno); /* hope default source address is adequate */ } saPort(peer, 113); /* ident protocol */ addr2str(peer, peerlen, addr, STRMAX, 0); addr[STRMAX] = '\0'; #ifdef WINDOWS param = 1; ioctlsocket(sd, FIONBIO, ¶m); #else fcntl(sd, F_SETFL, O_NONBLOCK); #endif #ifdef USE_EPOLL epfd = epoll_create(BACKLOG_MAX); if (epfd < 0) { message(LOG_ERR, "ident: can't create epoll err=%d", errno); epfd = INVALID_SOCKET; goto noconnect; /* I can't tell the master is healthy or not */ } ev.events = (EPOLLOUT | EPOLLONESHOT); if (epoll_ctl(epfd, EPOLL_CTL_ADD, sd, &ev) < 0) { message(LOG_ERR, "ident: epoll_ctl ADD err=%d", errno); goto noconnect; } #endif ret = connect(sd, peer, peerlen); if (ret < 0) { #ifdef WINDOWS errno = WSAGetLastError(); #endif if (errno == EINPROGRESS) { #ifndef USE_EPOLL fd_set wout; struct timeval tv; #endif do { time(&now); if (now - start >= CONN_TIMEOUT) { if (Debug > 0) message(LOG_DEBUG, "ident: connect to %s, timeout", addr); goto noconnect; } #ifndef USE_EPOLL tv.tv_sec = 1; tv.tv_usec = 0; FD_ZERO(&wout); FdSet(sd, &wout); #endif } while ( #ifdef USE_EPOLL epoll_wait(epfd, evs, 1, 1000) == 0 #else select(FD_SETSIZE, NULL, &wout, NULL, &tv) == 0 #endif ); } else { if (Debug > 0) message(LOG_DEBUG, "ident: can't connect to %s, err=%d", addr, errno); noconnect: #ifdef USE_EPOLL if (ValidSocket(epfd)) close(epfd); #endif closesocket(sd); return 0; } } #ifdef USE_EPOLL ev.events = (EPOLLIN | EPOLLONESHOT); if (epoll_ctl(epfd, EPOLL_CTL_MOD, sd, &ev) < 0) { message(LOG_ERR, "ident: epoll_ctl MOD err=%d", errno); goto noconnect; } #endif snprintf(buf, LONGSTRMAX, "%d, %d%c%c", sport, cport, '\r', '\n'); len = strlen(buf); ret = send(sd, buf, len, 0); if (ret != len) { #ifdef WINDOWS errno = WSAGetLastError(); #endif if (Debug > 0) message(LOG_DEBUG, "ident: can't send to %s ret=%d err=%d buf=%s", addr, ret, errno, buf); error: shutdown(sd, 2); #ifdef USE_EPOLL if (ValidSocket(epfd)) close(epfd); #endif closesocket(sd); return 0; } else { #ifndef USE_EPOLL fd_set rout; struct timeval tv; #endif do { time(&now); if (now - start >= CONN_TIMEOUT) { if (Debug > 0) message(LOG_DEBUG, "ident: read from %s, timeout", addr); goto error; } #ifndef USE_EPOLL tv.tv_sec = 1; tv.tv_usec = 0; FD_ZERO(&rout); FdSet(sd, &rout); #endif } while ( #ifdef USE_EPOLL epoll_wait(epfd, evs, 1, 1000) == 0 #else select(FD_SETSIZE, &rout, NULL, NULL, &tv) == 0 #endif ); ret = recv(sd, buf, LONGSTRMAX, 0); if (ret <= 0) { if (Debug > 0) message(LOG_DEBUG, "ident: can't read from %s, ret=%d", addr, ret); goto error; } shutdown(sd, 2); #ifdef USE_EPOLL if (ValidSocket(epfd)) close(epfd); #endif closesocket(sd); } do { ret--; c = buf[ret]; } while (ret > 0 && (c == '\r' || c == '\n')); ret++; buf[ret] = '\0'; if (Debug > 2) message(LOG_DEBUG, "ident: sent %s:%d, %d got %s", addr, sport, cport, buf); if (str) { char *p; p = rindex(buf, ':'); if (p) { int i; do { p++; } while (*p == ' '); for (i=0; i < STRMAX && *p; i++) str[i] = *p++; str[i] = '\0'; } } return 1; } int acceptCheck(Pair *pair1) { struct sockaddr_storage ss; struct sockaddr *from = (struct sockaddr*)&ss; socklen_t fromlen = sizeof(ss); Stone *stone = pair1->stone; Pair *pair2 = NULL; int satype; int saproto = 0; #ifdef ENLARGE int prevXferBufMax = XferBufMax; #endif XHosts *xhost; char ident[STRMAX+1]; char fromstr[STRMAX*2+1]; int fslen; fslen = 0; ident[0] = '\0'; bcopy(pair1->t->buf, &fromlen, sizeof(fromlen)); /* restore */ if (0 < fromlen && fromlen <= sizeof(ss)) { bcopy(pair1->t->buf + sizeof(fromlen), from, fromlen); } else { message(LOG_ERR, "%d TCP %d: acceptCheck Can't happen fromlen=%d", stone->sd, pair1->sd, fromlen); if (getpeername(pair1->sd, from, &fromlen) < 0) { #ifdef WINDOWS errno = WSAGetLastError(); #endif message(LOG_ERR, "%d TCP %d: acceptCheck Can't get peer's name err=%d", stone->sd, pair1->sd, errno); return 0; } } if (stone->proto & proto_ident) { if (getident(ident, from, fromlen, stone->port, &stone->listen->addr, stone->listen->len)) { ExBuf *t = newExData(pair1, data_identuser); strncpy(fromstr, ident, STRMAX); /* (size of ident) <= STRMAX */ fromstr[STRMAX] = '\0'; fslen = strlen(fromstr); if (t) { strcpy(t->buf + DATA_HEAD_LEN, fromstr); t->len = DATA_HEAD_LEN + fslen; } /* omit size check, because fslen <= STRMAX */ fromstr[fslen++] = '@'; } } fromstr[fslen] = '\0'; xhost = checkXhost(stone->xhosts, from, fromlen); if (!xhost) { addrport2strOnce(from, fromlen, (stone->proto & proto_stone_s), fromstr+fslen, STRMAX*2-fslen, 0); message(LOG_WARNING, "stone %d: access denied: from %s", stone->sd, fromstr); shutdown(pair1->sd, 2); return 0; } if (AccFp) { char str[STRMAX+1]; char tstr[STRMAX+1]; short port = 0; time_t clock; time(&clock); if (from->sa_family == AF_INET) { port = ntohs(((struct sockaddr_in*)from)->sin_port); } #ifdef AF_INET6 else if (from->sa_family == AF_INET6) { port = ntohs(((struct sockaddr_in6*)from)->sin6_port); } #endif addr2str(from, fromlen, str, STRMAX, NI_NUMERICHOST); str[STRMAX] = '\0'; strntime(tstr, STRMAX, &clock, -1); tstr[STRMAX] = '\0'; addrport2strOnce(from, fromlen, (stone->proto & proto_stone_s), fromstr+fslen, STRMAX*2-fslen, 0); fprintf(AccFp, "%s%d[%d] %s[%s]%d\n", tstr, stone->port, stone->sd, fromstr, str, port); } if ((xhost->mode & XHostsMode_Dump) > 0 || Debug > 1) { addrport2strOnce(from, fromlen, (stone->proto & proto_stone_s), fromstr+fslen, STRMAX*2-fslen, 0); message(LOG_DEBUG, "stone %d: accepted TCP %d from %s mode=%d", stone->sd, pair1->sd, fromstr, xhost->mode); } pair2 = newPair(); if (!pair2) { message(LOG_CRIT, "stone %d: out of memory, closing TCP %d", stone->sd, pair1->sd); if (pair2) freePair(pair2); return 0; } pair2->stone = stone; pair1->xhost = pair2->xhost = xhost; pair2->proto = ((stone->proto & proto_pair_d) | proto_first_r | proto_first_w); pair2->timeout = stone->timeout; /* now successfully accepted */ if (!(stone->proto & proto_block_d)) { #ifdef WINDOWS u_long param; param = 1; ioctlsocket(pair1->sd, FIONBIO, ¶m); #else fcntl(pair1->sd, F_SETFL, O_NONBLOCK); #endif } #ifdef USE_SSL if (stone->proto & proto_ssl_s) { if (doSSL_accept(pair1) < 0) goto error; } else #endif /* src & pair1 is connected */ pair1->proto |= (proto_connect | proto_dirty); /* SSL connection may not be established yet, but we can prepare the pair for connecting to the destination */ if (stone->proto & proto_udp_d) { pair2->proto |= proto_dgram; satype = SOCK_DGRAM; saproto = IPPROTO_UDP; } else { satype = SOCK_STREAM; saproto = IPPROTO_TCP; } #ifdef AF_LOCAL if (stone->proto & proto_unix_d) { saproto = 0; pair2->sd = socket(AF_LOCAL, satype, saproto); } else #endif #ifdef AF_INET6 if (stone->proto & proto_v6_d) pair2->sd = socket(AF_INET6, satype, saproto); else #endif pair2->sd = socket(AF_INET, satype, saproto); if (InvalidSocket(pair2->sd)) { #ifdef WINDOWS errno = WSAGetLastError(); #endif message(priority(pair1), "%d TCP %d: can't create socket err=%d", stone->sd, pair1->sd, errno); #ifdef USE_SSL error: #endif freePair(pair2); return 0; } if (stone->from) { if (bind(pair2->sd, &stone->from->addr, stone->from->len) < 0) { char str[STRMAX+1]; #ifdef WINDOWS errno = WSAGetLastError(); #endif addrport2str(&stone->from->addr, stone->from->len, 0, str, STRMAX, 0); str[STRMAX] = '\0'; message(LOG_ERR, "stone %d: can't bind %s err=%d", stone->sd, str, errno); } } pair2->pair = pair1; pair1->pair = pair2; return 1; } int strnAddr(char *buf, int limit, SOCKET sd, int which, int isport) { struct sockaddr_storage ss; struct sockaddr *name = (struct sockaddr*)&ss; socklen_t namelen = sizeof(ss); int len; char str[STRMAX+1]; int ret; switch (which) { #ifdef SO_ORIGINAL_DST case 1: /* original destination */ ret = getsockopt(sd, SOL_IP, SO_ORIGINAL_DST, name, &namelen); break; #endif default: /* peer */ ret = getpeername(sd, name, &namelen); } if (ret < 0) { if (isport) { strcpy(str, "0.0.0.0:0"); } else { strcpy(str, "0.0.0.0"); } } else { if (isport) { addrport2str(name, namelen, 0, str, STRMAX, 0); } else { addr2str(name, namelen, str, STRMAX, 0); } str[STRMAX] = '\0'; } len = strlen(str); if (len > limit) len = limit; strncpy(buf, str, len); return len; } #ifdef SO_PEERCRED #include #endif int strnUser(char *buf, int limit, Pair *pair, int which) { #if defined(AF_LOCAL) && defined(SO_PEERCRED) Stone *stone = pair->stone; #endif ExBuf *ex; int len; char str[STRMAX+1]; str[0] = '\0'; if (which == 2 && (ex = getExData(pair, data_identuser, 0))) { len = ex->len - DATA_HEAD_LEN; strncpy(str, ex->buf + DATA_HEAD_LEN, len); str[len] = '\0'; } else #if defined(AF_LOCAL) && defined(SO_PEERCRED) if (stone->listen->addr.sa_family == AF_LOCAL) { struct ucred *cred = NULL; ex = getExData(pair, data_ucred, 0); if (ex) { cred = (struct ucred*)(ex->buf + DATA_HEAD_LEN); } else { socklen_t optlen = sizeof(*cred); ex = newExData(pair, data_ucred); if (ex) { cred = (struct ucred*)(ex->buf + DATA_HEAD_LEN); if (getsockopt(pair->sd, SOL_SOCKET, SO_PEERCRED, cred, &optlen) < 0) { message(LOG_ERR, "%d TCP %d: Can't get PEERCRED err=%d", stone->sd, pair->sd, errno); ungetExBuf(ex); cred = NULL; } } } switch (which) { case 1: /* gid */ snprintf(str, STRMAX, "%d", (cred ? cred->gid : -1)); break; case 2: /* user name */ *str = '\0'; if (cred) { #ifdef THREAD_UNSAFE struct passwd *passwd = getpwuid(cred->uid); if (passwd) snprintf(str, STRMAX, "%s", passwd->pw_name); #else struct passwd pwbuf; char sbuf[STRMAX+1]; struct passwd *passwd; int ret = getpwuid_r(cred->uid, &pwbuf, sbuf, STRMAX, &passwd); if (ret == 0) snprintf(str, STRMAX, "%s", passwd->pw_name); #endif } break; case 3: /* group name */ *str = '\0'; if (cred) { #ifdef THREAD_UNSAFE struct group *group = getgrgid(cred->gid); if (group) snprintf(str, STRMAX, "%s", group->gr_name); #else struct group gbuf; char sbuf[STRMAX+1]; struct group *group; int ret = getgrgid_r(cred->gid, &gbuf, sbuf, STRMAX, &group); if (ret == 0) snprintf(str, STRMAX, "%s", group->gr_name); #endif } break; default: /* uid */ snprintf(str, STRMAX, "%d", (cred ? cred->uid : -1)); break; } } #endif len = strlen(str); if (len > limit) len = limit; strncpy(buf, str, len); return len; } int strnparse(char *buf, int limit, char **pp, Pair *pair, char term) { int i = 0; char *p; char c; #ifdef USE_SSL char **match = NULL; SSL *ssl = pair->ssl; SSL_SESSION *sess = NULL; int cond; #endif p = *pp; while (i < limit && (c = *p++)) { if (c == '\\') { c = *p++; if (c == term) break; #ifdef USE_SSL cond = -1; if (c == '?') { cond = 0; c = *p++; } if ('0' <= c && c <= '9') { if (ssl && !match) { sess = SSL_get1_session(ssl); if (sess) match = SSL_SESSION_get_ex_data(sess, MatchIndex); if (!match) ssl = NULL; /* now (match || ssl == NULL) holds */ } if (match) { int num = c - '0'; if (match[num]) { if (cond >= 0) { if (*match[num]) cond = 1; } else { int len = strlen(match[num]); if (len >= limit - i) len = limit - i; if (buf) { strncpy(buf+i, match[num], len); i += len; } } } } if (cond > 0) { if (buf) { i += strnparse(buf+i, limit-i, &p, pair, ':'); strnparse(NULL, limit-i, &p, pair, '/'); } } else if (cond == 0) { if (buf) { strnparse(NULL, limit-i, &p, pair, ':'); i += strnparse(buf+i, limit-i, &p, pair, '/'); } } continue; } #endif switch(c) { case 'n': c = '\n'; break; case 'r': c = '\r'; break; case 't': c = '\t'; break; case 'a': /* peer address */ if (buf) i += strnAddr(buf+i, limit-i, pair->sd, 0, 0); continue; case 'A': /* peer address:port */ if (buf) i += strnAddr(buf+i, limit-i, pair->sd, 0, 1); continue; #ifdef SO_ORIGINAL_DST case 'd': /* dst address */ if (buf) i += strnAddr(buf+i, limit-i, pair->sd, 1, 0); continue; case 'D': /* dst address:port (transparent proxy) */ if (buf) i += strnAddr(buf+i, limit-i, pair->sd, 1, 1); continue; #endif case 'u': if (buf) i += strnUser(buf+i, limit-i, pair, 0); continue; case 'g': if (buf) i += strnUser(buf+i, limit-i, pair, 1); continue; case 'U': if (buf) i += strnUser(buf+i, limit-i, pair, 2); continue; case 'G': if (buf) i += strnUser(buf+i, limit-i, pair, 3); continue; case '\0': c = '\\'; p--; } } if (buf) buf[i++] = c; } #ifdef USE_SSL if (sess) SSL_SESSION_free(sess); #endif if (buf) buf[i] = '\0'; *pp = p; return i; } int scanClose(Pair *pairs) { /* scan close request */ Pair *p1, *p2, *p; int n = 0; int m = 0; int all; if (pairs) { all = 0; } else { pairs = PairTop; all = 1; } p1 = trash.next; while (p1 != NULL) { SOCKET sd; p2 = p1; p1 = p1->next; #ifndef USE_EPOLL if (p2->proto & proto_thread) continue; #endif if (p2->count > 0) { p2->count--; n++; continue; } sd = p2->sd; if (p2->proto & (proto_select_r | proto_select_w)) { p2->proto &= ~(proto_select_r | proto_select_w); p2->proto |= proto_dirty; p2->count = REF_UNIT; } #ifdef USE_SSL if (p2->ssl_flag) { p2->ssl_flag = 0; p2->count = REF_UNIT; } #endif p = p2->prev; if (p) p->next = p1; /* remove `p2' from trash */ if (p1) p1->prev = p; freePair(p2); m++; } if (Debug > 8 && (n > 0 || m > 0)) message(LOG_DEBUG, "trash: queued=%d, removed=%d", n, m); p1 = pairs->next; while (p1 != NULL) { if (p1->clock == -1) { /* top */ if (all) { pairs = p1; p1 = pairs->next; continue; } else { break; } } p2 = p1; p1 = p1->next; if (!(p2->proto & proto_close)) continue; /* skip */ if (p2->count > 0) { p2->count--; continue; } waitMutex(PairMutex); p = p2->prev; if (p) p->next = p1; /* remove `p2' from list */ if (p1) p1->prev = p; p = p2->pair; if (p) p->pair = NULL; freeMutex(PairMutex); if (trash.next) trash.next->prev = p2; /* push `p2' to trash */ p2->prev = &trash; p2->pair = NULL; p2->count = REF_UNIT; p2->next = trash.next; trash.next = p2; } return 1; } void message_pairs(int pri) { /* dump for debug */ Pair *pair; for (pair=PairTop; pair != NULL; pair=pair->next) { if (pair->clock != -1) { /* not top */ message_pair(pri, pair); } else if (Debug > 2) { message(LOG_DEBUG, "%d TCP %d: top", pair->stone->sd, pair->sd); } } } void message_origins(int pri) { /* dump for debug */ Origin *origin; for (origin=OriginTop; origin != NULL; origin=origin->next) { if (origin->from) { message_origin(pri, origin); } else if (Debug > 2) { message(LOG_DEBUG, "%d UDP %d: top", origin->stone->sd, origin->sd); } } } void message_conns(int pri) { /* dump for debug */ Conn *conn; for (conn=conns.next; conn != NULL; conn=conn->next) message_conn(pri, conn); } /* read write thread */ /* no Mutex are needed because in the single thread */ void setclose(Pair *pair, int flag) { /* set close flag */ SOCKET sd = pair->sd; message_time_log(pair); if (!(pair->proto & proto_close)) { /* request to close */ pair->proto |= (flag | proto_close); if (Debug > 2 && ValidSocket(sd)) message(LOG_DEBUG, "%d TCP %d: close tx:%d rx:%d lp:%d", pair->stone->sd, sd, pair->tx, pair->rx, pair->loop); } #ifdef USE_EPOLL if (ValidSocket(sd)) { pair->sd = INVALID_SOCKET; closesocket(sd); } #endif } int dowrite(Pair *pair) { /* write from buf from pair->t->start */ SOCKET sd = pair->sd; Pair *p; int len; ExBuf *ex; ex = pair->t; /* top */ if (!ex) return 0; while (ex->len <= 0 && ex->next) { pair->t = ex->next; ex->next = NULL; pair->nbuf--; if (Debug > 4) message(LOG_DEBUG, "%d TCP %d: before dowrite " "unget ExBuf nbuf=%d nfex=%d", pair->stone->sd, pair->sd, pair->nbuf, nFreeExBuf); ungetExBuf(ex); } if (ex->len <= 0) return 0; /* nothing to write */ if (Debug > 5) message(LOG_DEBUG, "%d TCP %d: write %d bytes", pair->stone->sd, sd, ex->len); if (InvalidSocket(sd)) return -1; #ifdef USE_SSL if (pair->ssl) { len = SSL_write(pair->ssl, &ex->buf[ex->start], ex->len); if (pair->proto & proto_close) return -1; if (len <= 0) { int err; err = SSL_get_error(pair->ssl, len); if (err == SSL_ERROR_NONE || err == SSL_ERROR_WANT_WRITE) { if (Debug > 4) message(LOG_DEBUG, "%d TCP %d: SSL_write interrupted err=%d", pair->stone->sd, sd, err); return 0; /* EINTR */ } else if (err == SSL_ERROR_WANT_READ) { if (Debug > 4) message(LOG_DEBUG, "%d TCP %d: SSL_write blocked on read err=%d", pair->stone->sd, sd, err); pair->ssl_flag |= sf_wb_on_r; return 0; /* EINTR */ } if (err == SSL_ERROR_SYSCALL) { unsigned long e = ERR_get_error(); if (e == 0) { #ifdef WINDOWS errno = WSAGetLastError(); #endif if (errno == EINTR) { if (Debug > 4) message(LOG_DEBUG, "%d TCP %d: SSL_write I/O interrupted", pair->stone->sd, sd); return 0; } message(priority(pair), "%d TCP %d: SSL_write I/O error err=%d, closing", pair->stone->sd, sd, errno); message_pair(LOG_ERR, pair); } else { message(priority(pair), "%d TCP %d: SSL_write I/O %s, closing", pair->stone->sd, sd, ERR_error_string(e, NULL)); message_pair(LOG_ERR, pair); } return -1; /* error */ } else if (err != SSL_ERROR_ZERO_RETURN) { message(priority(pair), "%d TCP %d: SSL_write err=%d %s, closing", pair->stone->sd, sd, err, ERR_error_string(ERR_get_error(), NULL)); message_pair(LOG_ERR, pair); return len; /* error */ } } } else { #endif len = send(sd, &ex->buf[ex->start], ex->len, 0); if (pair->proto & proto_close) return -1; if (len < 0) { #ifdef WINDOWS errno = WSAGetLastError(); #endif if (errno == EINTR) { if (Debug > 4) message(LOG_DEBUG, "%d TCP %d: write interrupted", pair->stone->sd, sd); return 0; } if (errno == ECONNABORTED) { if (Debug > 3) message(LOG_DEBUG, "%d TCP %d: write aborted", pair->stone->sd, sd); return -1; } message(priority(pair), "%d TCP %d: write error err=%d, closing", pair->stone->sd, sd, errno); message_pair(LOG_ERR, pair); return len; /* error */ } #ifdef USE_SSL } #endif if (Debug > 4) message(LOG_DEBUG, "%d TCP %d: %d bytes written", pair->stone->sd, sd, len); if ((pair->xhost->mode & XHostsMode_Dump) > 0 || ((pair->proto & proto_first_w) && Debug > 3)) message_buf(pair, len, ""); time(&pair->clock); p = pair->pair; if (p) p->clock = pair->clock; if (ex->len <= len) { ex->start = 0; } else { ex->start += len; message(LOG_NOTICE, "%d TCP %d: write %d bytes, but only %d bytes written", pair->stone->sd, sd, ex->len, len); message_pair(LOG_NOTICE, pair); } ex->len -= len; if (ex->len <= 0 && ex->next) { pair->t = ex->next; ex->next = NULL; pair->nbuf--; if (Debug > 4) message(LOG_DEBUG, "%d TCP %d: after dowrite " "unget ExBuf nbuf=%d nfex=%d", pair->stone->sd, pair->sd, pair->nbuf, nFreeExBuf); ungetExBuf(ex); } pair->tx += len; if ((p->proto & proto_command) != command_health) lastReadWrite = pair->clock; return len; } static unsigned char basis_64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; int baseEncode(unsigned char *buf, int len, int max) { unsigned char *org = buf + max - len; unsigned char c1; unsigned char c2 = 0; /* dummy init to suppress warnings */ unsigned char c3 = 0; int blen = 0; int i; bcopy(buf, org, len); for (i=0; i < len; i += 3) { switch (len - i) { case 1: c2 = '\0'; buf[blen+2] = '='; case 2: c3 = '\0'; buf[blen+3] = '='; } switch (len - i) { default: c3 = org[i+2]; buf[blen+3] = basis_64[c3 & 0x3F]; case 2: c2 = org[i+1]; buf[blen+2] = basis_64[((c2 & 0xF) << 2) | ((c3 & 0xC0) >>6)]; case 1: c1 = org[i]; buf[blen+1] = basis_64[((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4)]; buf[blen] = basis_64[c1>>2]; } blen += 4; } if (buf[blen-1] != '=') buf[blen++] = '='; return blen; } #define XX 255 /* illegal base64 char */ #define EQ 254 /* padding */ static unsigned char index_64[256] = { XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,62, XX,XX,XX,63, 52,53,54,55, 56,57,58,59, 60,61,XX,XX, XX,EQ,XX,XX, XX, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14, 15,16,17,18, 19,20,21,22, 23,24,25,XX, XX,XX,XX,XX, XX,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40, 41,42,43,44, 45,46,47,48, 49,50,51,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, }; int baseDecode(unsigned char *buf, int len, char *rest) { int blen = 0; unsigned char c[4], o[4]; int i, j; j = 0; for (i=0; i < len; i++) { c[j] = index_64[buf[i]]; if (c[j] == XX) continue; if (j == 0 && c[j] == EQ) continue; o[j++] = buf[i]; if (j == 4) { j = 0; buf[blen++] = (c[0] << 2) | ((c[1] & 0x30) >> 4); if (c[2] == EQ) continue; buf[blen++] = ((c[1] & 0x0F) << 4) | ((c[2] & 0x3C) >> 2); if (c[3] == EQ) continue; buf[blen++] = ((c[2] & 0x03) << 6) | c[3]; } } *rest = j; for (i=0; i < j; i++) *(rest-1-i) = o[i]; return blen; } int doread(Pair *pair) { /* read into buf from pair->pair->b->start */ SOCKET sd = pair->sd; Pair *p; int len, i; ExBuf *ex; int bufmax, start; if (InvalidSocket(sd)) return -1; if (Debug > 5) message(LOG_DEBUG, "%d TCP %d: read", pair->stone->sd, sd); p = pair->pair; if (p == NULL) { /* no pair, no more read */ char _buf[BUFMAX]; #ifdef USE_SSL if (pair->ssl) { len = SSL_read(pair->ssl, _buf, BUFMAX); } else #endif len = recv(sd, _buf, BUFMAX, 0); if (pair->proto & proto_close) return -1; if (Debug > 4) message(LOG_DEBUG, "%d TCP %d: read %d bytes", pair->stone->sd, sd, len); if (len == 0) return -1; /* EOF w/o pair */ if (len > 0) { message(priority(pair), "%d TCP %d: no pair, closing", pair->stone->sd, sd); message_pair(LOG_ERR, pair); len = -1; } return len; } ex = p->b; /* bottom */ if (ex->len > 0) { /* not emply */ ex = getExBuf(); if (!ex) return -1; /* out of memory */ p->b->next = ex; p->b = ex; p->nbuf++; if (Debug > 4) message(LOG_DEBUG, "%d TCP %d: get ExBuf nbuf=%d", pair->stone->sd, p->sd, p->nbuf); } bufmax = ex->bufmax - ex->start - ex->len; start = ex->start + ex->len; if (p->proto & proto_base) bufmax = (bufmax - 1) / 4 * 3; else if (pair->proto & proto_base) { if (!(pair->proto & proto_first_r)) { len = *(ex->buf+ex->bufmax-1); for (i=0; i < len; i++) { ex->buf[start++] = ex->buf[ex->bufmax-2-i]; } bufmax -= len; } *(ex->buf+ex->bufmax-1) = 0; bufmax -= 5; } if (((p->proto & proto_command) == command_ihead) || ((p->proto & proto_command) == command_iheads)) bufmax = bufmax / 2; #ifdef USE_SSL if (pair->ssl) { len = SSL_read(pair->ssl, &ex->buf[start], bufmax); if (pair->proto & proto_close) return -1; if (len < 0) { int err; err = SSL_get_error(pair->ssl, len); if (err == SSL_ERROR_NONE || err == SSL_ERROR_WANT_READ) { if (Debug > 4) message(LOG_DEBUG, "%d TCP %d: SSL_read interrupted err=%d", pair->stone->sd, sd, err); return 0; /* EINTR */ } else if (err == SSL_ERROR_WANT_WRITE) { if (Debug > 4) message(LOG_DEBUG, "%d TCP %d: SSL_read blocked on write err=%d", pair->stone->sd, sd, err); pair->ssl_flag |= sf_rb_on_w; return 0; /* EINTR */ } if (err == SSL_ERROR_SYSCALL) { unsigned long e = ERR_get_error(); if (e == 0) { #ifdef WINDOWS errno = WSAGetLastError(); #endif if (errno == EINTR) { if (Debug > 4) message(LOG_DEBUG, "%d TCP %d: SSL_read I/O interrupted", pair->stone->sd, sd); return 0; } message(priority(pair), "%d TCP %d: SSL_read I/O error err=%d, closing", pair->stone->sd, sd, errno); message_pair(LOG_ERR, pair); } else { message(priority(pair), "%d TCP %d: SSL_read I/O %s, closing", pair->stone->sd, sd, ERR_error_string(e, NULL)); message_pair(LOG_ERR, pair); } return -1; /* error */ } else if (err != SSL_ERROR_ZERO_RETURN) { message(priority(pair), "%d TCP %d: SSL_read err=%d %s, closing", pair->stone->sd, sd, err, ERR_error_string(ERR_get_error(), NULL)); message_pair(LOG_ERR, pair); return -1; /* error */ } } } else { #endif len = recv(sd, &ex->buf[start], bufmax, 0); if (pair->proto & proto_close) return -1; if (len < 0) { #ifdef WINDOWS errno = WSAGetLastError(); #endif if (errno == EINTR) { if (Debug > 4) message(LOG_DEBUG, "%d TCP %d: read interrupted", pair->stone->sd, sd); return 0; /* EINTR */ } if (errno == ECONNRESET) { if (Debug > 3) message(LOG_DEBUG, "%d TCP %d: read but reset by peer", pair->stone->sd, sd); return -1; } message(priority(pair), "%d TCP %d: read error err=%d, closing", pair->stone->sd, sd, errno); message_pair(LOG_ERR, pair); return len; /* error */ } #ifdef USE_SSL } #endif if (len > 0) { pair->rx += len; #ifdef ENLARGE if (len > ex->bufmax - 10 && XferBufMax < ex->bufmax * 2) { XferBufMax = ex->bufmax * 2; message(LOG_NOTICE, "%d TCP %d: XferBufMax becomes %d byte", pair->stone->sd, sd, XferBufMax); } #endif ex->len = start + len - ex->start; if (Debug > 4) { SOCKET psd = p->sd; if (start > ex->start) { message(LOG_DEBUG, "%d TCP %d: read %d+%d bytes to %d", pair->stone->sd, sd, len, start - ex->start, psd); } else { message(LOG_DEBUG, "%d TCP %d: read %d bytes to %d", pair->stone->sd, sd, ex->len, psd); } } time(&pair->clock); p->clock = pair->clock; if (p->proto & proto_base) { ex->len = baseEncode((unsigned char*)&ex->buf[ex->start], ex->len, ex->bufmax - ex->start); } else if (pair->proto & proto_base) { ex->len = baseDecode((unsigned char*)&ex->buf[ex->start], ex->len, ex->buf+ex->bufmax-1); len = *(ex->buf+ex->bufmax-1); if (Debug > 4 && len > 0) { /* len < 4 */ char str[STRMAX+1]; for (i=0; i < len; i++) sprintf(&str[i*3], " %02x", ex->buf[ex->bufmax-2-i]); str[0] = '('; message(LOG_DEBUG, "%d TCP %d: save %d bytes \"%s\")", pair->stone->sd, sd, len, str); } } if ((p->proto & proto_command) != command_health) lastReadWrite = pair->clock; } if (p->t->len <= 0) { /* top */ message_time_log(pair); if (Debug > 2) message(LOG_DEBUG, "%d TCP %d: EOF", pair->stone->sd, sd); return -2; /* EOF w/ pair */ } return p->t->len; } /* http */ #define METHOD_LEN_MAX 10 int commOutput(Pair *pair, char *fmt, ...) { Pair *p = pair->pair; ExBuf *ex; SOCKET psd; char *str; va_list ap; if (p == NULL) return -1; psd = p->sd; if ((p->proto & (proto_shutdown | proto_close)) || InvalidSocket(psd)) return -1; ex = p->b; /* bottom */ if (ex->bufmax - (ex->start + ex->len) < STRMAX+1) { ExBuf *new = getExBuf(); if (new) { ex = new; p->b->next = ex; p->b = ex; p->nbuf++; if (Debug > 4) message(LOG_DEBUG, "%d TCP %d: get ExBuf nbuf=%d", pair->stone->sd, p->sd, p->nbuf); } } str = &ex->buf[ex->start + ex->len]; ex->buf[ex->bufmax-1] = '\0'; va_start(ap, fmt); vsnprintf(str, ex->bufmax-1 - (ex->start + ex->len), fmt, ap); va_end(ap); if (p->proto & proto_base) ex->len += baseEncode((unsigned char*)str, strlen(str), ex->bufmax-1 - (ex->start + ex->len)); else ex->len += strlen(str); p->proto |= (proto_select_w | proto_dirty); /* need to write */ return ex->len; } static char *comm_match(char *buf, char *str) { while (*str) { if (toupper(*buf++) != *str++) return NULL; /* unmatch */ } if (*buf) { if (!isspace(*buf)) return NULL; /* while (isspace(*buf)) buf++; */ if (*buf == ' ') buf++; } return buf; } #ifdef ADDRCACHE unsigned int str2hash(char *str) { unsigned int hash = 0; while (*str) { hash = hash * 7 + *str; str++; } return hash; } struct hashtable { char *host; char *serv; time_t clock; int len; struct sockaddr_storage ss; } *hashtable; int addrcache(char *name, char *serv, struct sockaddr *sa, socklen_t *salenp) { struct hashtable *t; time_t now; time(&now); if (!hashtable) { hashtable = malloc(AddrCacheSize * sizeof(struct hashtable)); if (!hashtable) { message(LOG_ERR, "addrcache: out of memory"); return host2sa(name, serv, sa, salenp, NULL, NULL, 0); } bzero(hashtable, AddrCacheSize * sizeof(struct hashtable)); } t = &hashtable[(str2hash(name) ^ str2hash(serv)) % AddrCacheSize]; waitMutex(HashMutex); if (t->host && strcmp(t->host, name) == 0 && t->serv && strcmp(t->serv, serv) == 0 && t->len <= *salenp && now - t->clock < CACHE_TIMEOUT) { bcopy(&t->ss, sa, t->len); *salenp = t->len; freeMutex(HashMutex); if (Debug > 5) message(LOG_DEBUG, "addrcache hit: %s:%s %d", name, serv, (int)(now - t->clock)); return 1; } freeMutex(HashMutex); if (Debug > 9) message(LOG_DEBUG, "addrcache %s %s", name, serv); if (!host2sa(name, serv, sa, salenp, NULL, NULL, 0)) { return 0; } waitMutex(HashMutex); if ((t->host && strcmp(t->host, name) != 0) || (t->serv && strcmp(t->serv, serv) != 0) || (t->len > *salenp)) { free(t->host); free(t->serv); t->host = NULL; t->serv = NULL; t->len = sizeof(t->ss); } if (!t->host) t->host = strdup(name); if (!t->serv) t->serv = strdup(serv); bcopy(sa, &t->ss, *salenp); t->len = *salenp; t->clock = now; freeMutex(HashMutex); return 1; } #endif int doproxy(Pair *pair, char *host, char *serv) { SOCKET sd = pair->sd; int reconnect = 0; PortXHosts *pxh; struct sockaddr_storage name_s; struct sockaddr *name = (struct sockaddr*)&name_s; struct sockaddr_storage ss; struct sockaddr *sa = (struct sockaddr*)&ss; socklen_t namelen = sizeof(name_s); socklen_t salen = sizeof(ss); if ((pair->stone->proto & proto_ip_only_d)) { #ifdef AF_INET6 if ((pair->stone->proto & proto_v6_d)) sa->sa_family = AF_INET6; else #endif sa->sa_family = AF_INET; } else { sa->sa_family = AF_UNSPEC; } #ifdef ADDRCACHE if (AddrCacheSize > 0) { if (!addrcache(host, serv, sa, &salen)) return -1; } else #endif if (!host2sa(host, serv, sa, &salen, NULL, NULL, 0)) return -1; if (islocalhost(sa)) { TimeLog *log = pair->log; pair->log = NULL; if (log) free(log); } if ((pair->stone->proto & proto_nobackup) == 0) { Backup *backup = findBackup(sa); if (backup && backup->bn) { /* unhealthy */ sa = &backup->backup->addr; salen = backup->backup->len; } } pxh = (PortXHosts*)pair->stone->dsts[1]; if (pxh) { for (; pxh; pxh=pxh->next) { XPorts *ports; XHosts *xhost; int isok = 0; int port = getport(sa); for (ports=pxh->ports; ports; ports=ports->next) { if (ports->from <= port && port <= ports->end) { isok = 1; } } if (!isok) continue; xhost = checkXhost(pxh->xhosts, sa, salen); if (xhost) { if (xhost->mode) { Pair *p = pair->pair; pair->xhost = xhost; if (p) p->xhost = xhost; } if (Debug > 7) { message(LOG_DEBUG, "stone %d: proxy can connect to %s:%s mode=%d", pair->stone->sd, host, serv, xhost->mode); } break; } else { message(LOG_WARNING, "stone %d: proxy may not connect to %s", pair->stone->sd, host); return -1; } } if (!pxh) { message(LOG_WARNING, "stone %d: proxy may not connect to port %s", pair->stone->sd, serv); return -1; } } if ((pair->proto & proto_connect) && !(pair->proto & proto_close) && getpeername(sd, name, &namelen) >= 0) { /* reconnect proxy */ Pair *p = pair->pair; if (Debug > 7) { char str[STRMAX+1]; message(LOG_DEBUG, "%d TCP %d: old proxy connection: %s", pair->stone->sd, sd, addrport2str(name, namelen, 0, str, STRMAX, 0)); } if (p) p->proto |= (proto_first_w | proto_dirty); if (saComp(sa, name)) return 0; /* same sa, so need not to connect */ reconnect = 1; } if (reconnect || ((pair->stone->proto & proto_v6_d) && sa->sa_family == AF_INET) #ifdef AF_INET6 || (!(pair->stone->proto & proto_v6_d) && sa->sa_family == AF_INET6) #endif ) { SOCKET nsd = socket(sa->sa_family, SOCK_STREAM, IPPROTO_TCP); if (ValidSocket(nsd)) { Pair *p = pair->pair; pair->sd = nsd; message(LOG_INFO, "%d TCP %d: close %d %08x, " "reopen %d as family=%d", pair->stone->sd, (p ? p->sd : INVALID_SOCKET), sd, pair->proto, nsd, sa->sa_family); closesocket(sd); } } pair->proto &= ~(proto_connect | proto_command); if (reqconn(pair, sa, salen) < 0) return -1; if ((pair->proto & state_mask) == 1) { if (Debug > 7) message(LOG_DEBUG, "%d TCP %d: command_proxy again", pair->stone->sd, pair->sd); pair->proto |= command_proxy; } return 0; } int proxyCONNECT(Pair *pair, char *parm, int start) { char *port = "443"; /* default: https */ char *r = parm; char *q = NULL; Pair *p; message_time(pair, LOG_INFO, "CONNECT %s", parm); while (*r) { if (isspace(*r)) { *r = '\0'; break; } if (*r == ':') q = r; r++; } if (q) { port = q + 1; *q = '\0'; } pair->b->len += pair->b->start; pair->b->start = 0; p = pair->pair; if (p) p->proto |= proto_ohttp_s; /* remove request header */ return doproxy(pair, parm, port); } int proxyCommon(Pair *pair, char *parm, int start) { char *port = "80"; /* default port of http:// */ char *host; ExBuf *ex; char *top; char *p, *q; int i; ex = pair->b; /* bottom */ top = &ex->buf[start]; for (i=0; i < METHOD_LEN_MAX; i++) { if (parm[i] == ':') break; } if (strncmp(parm, "http", i) != 0 || parm[i+1] != '/' || parm[i+2] != '/') { message(LOG_ERR, "Unknown URL format: %s", parm); return -1; } host = &parm[i+3]; p = host; while (*p) { if (*p == ':') { port = p + 1; *p++ = '\0'; continue; } if (isspace(*p) || *p == '/') { *p = '\0'; break; } p++; } i = p - parm; /* length of 'http://host' */ p = top; while (!isspace(*p)) p++; /* skip 'GET http://host' */ while (isspace(*p)) p++; /* now p points url */ q = p + i; /* now q points path */ if (*q != '/') *--q = '/'; bcopy(q, p, ex->start + ex->len - (q - top)); ex->len = ex->start + ex->len - (q - p); ex->start = 0; if (Debug > 1) { Pair *r = pair->pair; message(LOG_DEBUG, "proxy %d -> http://%s:%s", (r ? r->sd : INVALID_SOCKET), host, port); } pair->proto &= ~(proto_noconnect | state_mask); pair->proto |= (proto_dirty | 1); return doproxy(pair, host, port); } int proxyGET(Pair *pair, char *parm, int start) { message_time(pair, LOG_INFO, "GET %s", parm); return proxyCommon(pair, parm, start); } int proxyHEAD(Pair *pair, char *parm, int start) { message_time(pair, LOG_INFO, "HEAD %s", parm); return proxyCommon(pair, parm, start); } int proxyPOST(Pair *pair, char *parm, int start) { message_time(pair, LOG_INFO, "POST %s", parm); return proxyCommon(pair, parm, start); } int proxyErr(Pair *pair, char *parm, int start) { message(LOG_ERR, "Unknown method: %s", parm); return -1; } Comm proxyComm[] = { { "CONNECT", proxyCONNECT }, { "POST", proxyPOST }, { "GET", proxyGET }, { "HEAD", proxyHEAD }, { NULL, proxyErr }, }; #ifdef USE_POP int popUSER(Pair *pair, char *parm, int start) { int ulen, tlen; char *data; ExBuf *ex = getExData(pair, data_apop, 0); if (!ex) { message(LOG_ERR, "%d TCP %d: popUSER Can't happen no ExData", pair->stone->sd, pair->sd); return -1; } data = ex->buf + DATA_HEAD_LEN; if (Debug) message(LOG_DEBUG, ": USER %s", parm); ulen = strlen(parm); tlen = strlen(data); if (ulen + 1 + tlen + 1 >= BUFMAX-1) { commOutput(pair, "+Err Too long user name\r\n"); return -1; } bcopy(data, data + ulen + 1, tlen + 1); strcpy(data, parm); commOutput(pair, "+OK Password required for %s\r\n", parm); pair->proto &= ~state_mask; pair->proto |= 1; return -2; /* read more */ } #define DIGEST_LEN 16 int popPASS(Pair *pair, char *parm, int start) { MD5_CTX context; unsigned char digest[DIGEST_LEN]; char *str; int ulen, tlen, plen, i; int state = (pair->proto & state_mask); ExBuf *ex; ExBuf *t; char *data; int max; if (Debug > 5) message(LOG_DEBUG, ": PASS %s", parm); if (state < 1) { commOutput(pair, "-ERR USER first\r\n"); return -2; /* read more */ } t = getExData(pair, data_apop, 1); data = t->buf + DATA_HEAD_LEN; max = t->bufmax - DATA_HEAD_LEN; ulen = strlen(data); str = data + ulen + 1; tlen = strlen(str); plen = strlen(parm); if (ulen + 1 + tlen + plen + 1 >= max-1) { commOutput(pair, "+Err Too long password\r\n"); return -1; } strcat(str, parm); ex = pair->b; /* bottom */ sprintf(ex->buf, "APOP %s ", data); ulen = strlen(ex->buf); MD5Init(&context); MD5Update(&context, str, tlen + plen); MD5Final(digest, &context); ungetExBuf(t); for (i=0; i < DIGEST_LEN; i++) { sprintf(ex->buf + ulen + i*2, "%02x", digest[i]); } message_time(pair, LOG_INFO, "POP -> %s", ex->buf); strcat(ex->buf, "\r\n"); ex->start = 0; ex->len = strlen(ex->buf); return 0; } int popAUTH(Pair *pair, char *parm, int start) { if (Debug) message(LOG_DEBUG, ": AUTH %s", parm); commOutput(pair, "-ERR authorization first\r\n"); return -2; /* read more */ } int popCAPA(Pair *pair, char *parm, int start) { if (Debug) message(LOG_DEBUG, ": CAPA %s", parm); commOutput(pair, "-ERR authorization first\r\n"); return -2; /* read more */ } int popAPOP(Pair *pair, char *parm, int start) { ExBuf *ex = pair->b; /* bottom */ message_time(pair, LOG_INFO, "APOP %s", parm); ex->len += ex->start - start; ex->start = start; return 0; } int popErr(Pair *pair, char *parm, int start) { message(LOG_ERR, "Unknown POP command: %s", parm); return -1; } Comm popComm[] = { { "USER", popUSER }, { "PASS", popPASS }, { "APOP", popAPOP }, { "AUTH", popAUTH }, { "CAPA", popCAPA }, { NULL, popErr }, }; #endif Pair *identd(int cport, struct sockaddr *ssa, socklen_t ssalen) { struct sockaddr_storage ss; struct sockaddr *sa = (struct sockaddr*)&ss; socklen_t salen; Pair *pair; for (pair=PairTop; pair != NULL; pair=pair->next) { SOCKET sd; if ((pair->proto & proto_command) == command_source) continue; sd = pair->sd; salen = sizeof(ss); if (InvalidSocket(sd) || getsockname(sd, sa, &salen) < 0) { continue; } if (getport(sa) != cport) continue; salen = sizeof(ss); if (getpeername(sd, sa, &salen) < 0) { continue; } if (!saComp(sa, ssa)) continue; return pair; } return NULL; } int identdQUERY(Pair *pair, char *parm, int start) { int cport = 0; int sport = 0; char mesg[STRMAX+1]; struct sockaddr_storage ss; struct sockaddr *sa = (struct sockaddr*)&ss; socklen_t salen = sizeof(ss); Pair *p = pair->pair; strcpy(mesg, "ERROR : NO-USER"); if (p) { SOCKET sd = p->sd; if (sscanf(parm, "%d,%d", &cport, &sport) == 2 && ValidSocket(sd) && getpeername(sd, sa, &salen) >= 0) { if (Debug > 8) { char addrport[STRMAX+1]; addrport2str(sa, salen, 0, addrport, STRMAX, 0); message(LOG_DEBUG, "%d TCP %d: identd query %d,%d from %s", pair->stone->sd, sd, cport, sport, addrport); } saPort(sa, sport); p = identd(cport, sa, salen); if (p) { int port = -1; Stone *stone = p->stone; if (stone) port = stone->port; snprintf(mesg, STRMAX, "USERID : STONE : %d", port); } if (Debug > 2) { char addrport[STRMAX+1]; addrport2str(sa, salen, 0, addrport, STRMAX, 0); message(LOG_DEBUG, "identd %d %s %s", cport, addrport, mesg); } } else { return -1; } } commOutput(pair, "%d , %d : %s\r\n", cport, sport, mesg); return -2; /* read more */ } int identdQUIT(Pair *pair, char *parm, int start) { if (Debug) message(LOG_DEBUG, "identd QUIT %s", parm); return -1; } Comm identdComm[] = { { "QUIT", identdQUIT }, { "", identdQUERY }, { NULL, identdQUERY }, }; int nStones(void) { int n = 0; Stone *stone; for (stone=stones; stone != NULL; stone=stone->next) n++; return n; } int nPairs(Pair *top) { int n = 0; Pair *pair; for (pair=top; pair != NULL; pair=pair->next) if (pair->clock != -1) n++; /* not top */ return n; } int nConns(void) { int n = 0; Conn *conn; for (conn=conns.next; conn != NULL; conn=conn->next) n++; return n; } int nOrigins(void) { int n = 0; Origin *origin; for (origin=OriginTop; origin != NULL; origin=origin->next) if (origin->from) n++; return n; } int limitCommon(Pair *pair, int var, int limit, char *str) { if (Debug) message(LOG_DEBUG, ": LIMIT %s %d: %d", str, limit, var); if (var < limit) { commOutput(pair, "200 %s=%d is less than %d\r\n", str, var, limit); } else { commOutput(pair, "500 %s=%d is not less than %d\r\n", str, var, limit); } return -2; /* read more */ } int limitPair(Pair *pair, char *parm, int start) { return limitCommon(pair, nPairs(PairTop), atoi(parm), "pair"); } int limitConn(Pair *pair, char *parm, int start) { return limitCommon(pair, nConns(), atoi(parm), "conn"); } int limitEstablished(Pair *pair, char *parm, int start) { time_t now; time(&now); return limitCommon(pair, (int)(now - lastEstablished), atoi(parm), "established"); } int limitReadWrite(Pair *pair, char *parm, int start) { time_t now; time(&now); return limitCommon(pair, (int)(now - lastReadWrite), atoi(parm), "readwrite"); } int limitAsync(Pair *pair, char *parm, int start) { return limitCommon(pair, AsyncCount, atoi(parm), "async"); } int limitErr(Pair *pair, char *parm, int start) { if (Debug) message(LOG_ERR, ": Illegal LIMIT %s", parm); commOutput(pair, "500 Illegal LIMIT\r\n"); return -2; /* read more */ } Comm limitComm[] = { { "PAIR", limitPair }, { "CONN", limitConn }, { "ESTABLISHED", limitEstablished }, { "READWRITE", limitReadWrite }, { "ASYNC", limitAsync }, { NULL, limitErr }, }; int healthHELO(Pair *pair, char *parm, int start) { char str[LONGSTRMAX+1]; snprintf(str, LONGSTRMAX, "stone=%d pair=%d trash=%d conn=%d origin=%d", nStones(), nPairs(PairTop), nPairs(trash.next), nConns(), nOrigins()); str[LONGSTRMAX] = '\0'; if (Debug) message(LOG_DEBUG, ": HELO %s: %s", parm, str); commOutput(pair, "250 stone:%s debug=%d %s\r\n", VERSION, Debug, str); return -2; /* read more */ } int healthSTAT(Pair *pair, char *parm, int start) { char str[LONGSTRMAX+1]; int mc = MutexConflict; MutexConflict = 0; snprintf(str, LONGSTRMAX, "async=%d mutex=%d", AsyncCount, mc); str[LONGSTRMAX] = '\0'; if (Debug) message(LOG_DEBUG, ": STAT %s: %s", parm, str); commOutput(pair, "250 stone:%s debug=%d %s\r\n", VERSION, Debug, str); return -2; /* read more */ } int healthFREE(Pair *pair, char *parm, int start) { char str[LONGSTRMAX+1]; snprintf(str, LONGSTRMAX, "fpair=%d nfexbuf=%d nfexbot=%d nfpktbuf=%d", nFreePairs, nFreeExBuf, nFreeExBot, nFreePktBuf); str[LONGSTRMAX] = '\0'; if (Debug) message(LOG_DEBUG, ": FREE %s: %s", parm, str); commOutput(pair, "250 stone:%s debug=%d %s\r\n", VERSION, Debug, str); return -2; /* read more */ } int healthCLOCK(Pair *pair, char *parm, int start) { char str[LONGSTRMAX+1]; time_t now; time(&now); snprintf(str, LONGSTRMAX, "now=%ld established=%d readwrite=%d", (long)now, (int)(now - lastEstablished), (int)(now - lastReadWrite)); str[LONGSTRMAX] = '\0'; if (Debug) message(LOG_DEBUG, ": CLOCK %s: %s", parm, str); commOutput(pair, "250 stone:%s debug=%d %s\r\n", VERSION, Debug, str); return -2; /* read more */ } int healthCVS_ID(Pair *pair, char *parm, int start) { commOutput(pair, "200 stone %s %s\r\n", VERSION, CVS_ID); return -2; /* read more */ } int healthCONFIG(Pair *pair, char *parm, int start) { int i; for (i=1; i < ConfigArgc; i++) commOutput(pair, "200%c%s\n", (i < ConfigArgc-1 ? '-' : ' '), ConfigArgv[i]); return -2; /* read more */ } int healthSTONE(Pair *pair, char *parm, int start) { Stone *stone; char str[STRMAX+1]; for (stone=stones; stone != NULL; stone=stone->next) { Stone *child; for (child=stone->children; child != NULL; child=child->children) commOutput(pair, "200-%s\n", stone2str(child, str, STRMAX)); commOutput(pair, "200%c%s\n", (stone->next ? '-' : ' '), stone2str(stone, str, STRMAX)); } return -2; /* read more */ } int healthLIMIT(Pair *pair, char *parm, int start) { Comm *comm = limitComm; char *q = NULL; while (comm->str) { if ((q=comm_match(parm, comm->str)) != NULL) break; comm++; } if (!q) return limitErr(pair, parm, start); return (*comm->func)(pair, q, start); } int healthQUIT(Pair *pair, char *parm, int start) { if (Debug) message(LOG_DEBUG, ": QUIT %s", parm); return -1; } int healthErr(Pair *pair, char *parm, int start) { if (*parm) message(LOG_ERR, "Unknown health command: %s", parm); return -1; } Comm healthComm[] = { { "HELO", healthHELO }, { "STAT", healthSTAT }, { "FREE", healthFREE }, { "CLOCK", healthCLOCK }, { "CVS_ID", healthCVS_ID }, { "CONFIG", healthCONFIG }, { "STONE", healthSTONE }, { "LIMIT", healthLIMIT }, { "QUIT", healthQUIT }, { NULL, healthErr }, }; int memCheck(void) { char *buf = malloc(BUFMAX * 10); if (buf) { free(buf); return 1; } message(LOG_CRIT, "memCheck: out of memory"); return 0; } int docomm(Pair *pair, Comm *comm) { ExBuf *ex = pair->b; /* bottom */ char buf[BUFMAX]; char *p; char *q = &ex->buf[ex->start + ex->len]; int start, i; for (p=&ex->buf[ex->start]; p < q; p++) { if (*p == '\r' || *p == '\n') break; } if (p >= q && p < &ex->buf[ex->bufmax]) { ex->start += ex->len; ex->len = 0; return -2; /* read more */ } for (start=p-ex->buf-1; start >= 0; start--) { if (ex->buf[start] == '\r' || ex->buf[start] == '\n') break; } start++; while ((*p == '\r' || *p == '\n') && p < q) p++; ex->start = p - ex->buf; if (p < q) { ex->len = q - p; } else { ex->len = 0; } while (comm->str) { if ((q=comm_match(&ex->buf[start], comm->str)) != NULL) break; comm++; } if (q == NULL) q = &ex->buf[start]; for (i=0; q < p && i < BUFMAX-1; i++) { if (*q == '\r' || *q == '\n') break; buf[i] = *q++; } buf[i] = '\0'; return (*comm->func)(pair, buf, start); } int insheader(Pair *pair) { /* insert header */ ExBuf *ex = pair->b; /* bottom */ char *p; int bufmax = ex->bufmax; int len, i; len = ex->start + ex->len; for (i=ex->start; i < len; i++) { if (ex->buf[i] == '\n') break; } if (i >= len) { if (Debug > 3) message(LOG_DEBUG, "%d TCP %d: insheader needs more", pair->stone->sd, pair->sd); return -1; } i++; len -= i; if (len > 0) { bufmax -= len; /* reserve */ /* save rest header */ bcopy(&ex->buf[i], &ex->buf[bufmax], len); } p = pair->stone->p; i += strnparse(&ex->buf[i], bufmax - i, &p, pair->pair, 0xFF); ex->buf[i++] = '\r'; ex->buf[i++] = '\n'; if (Debug > 5) { message(LOG_DEBUG, "%d TCP %d: insheader start=%d, ins=%d, rest=%d, max=%d", pair->stone->sd, pair->sd, ex->start, i-ex->start, len, ex->bufmax); } if (len > 0) /* restore */ bcopy(&ex->buf[bufmax], &ex->buf[i], len); ex->len = i - ex->start + len; return ex->len; } int rmheader(Pair *pair) { /* remove header */ ExBuf *ex = pair->b; /* bottom */ char *p; char *q = &ex->buf[ex->start+ex->len]; int state = (pair->proto & state_mask); if (Debug > 3) message_buf(pair, ex->len, "rm"); for (p=&ex->buf[ex->start]; p < q; p++) { if (*p == '\r') continue; if (*p == '\n') { state++; if (state >= 3) { p++; break; /* end of header */ } } else { state = 1; } } if (state < 3) { ex->len = ex->start = 0; pair->proto = ((pair->proto & ~state_mask) | state); return -2; /* header will continue... */ } ex->len = q - p; /* remove header */ ex->start = p - ex->buf; pair->proto &= ~state_mask; return ex->len; } int first_read(Pair *pair) { SOCKET sd = pair->sd; SOCKET psd; Pair *p = pair->pair; ExBuf *ex; Stone *stone = pair->stone; int len; if (p == NULL || (p->proto & (proto_shutdown | proto_close)) || InvalidSocket(sd)) return -1; ex = p->b; /* bottom */ psd = p->sd; len = ex->len; pair->proto &= ~proto_first_r; if (p->proto & proto_command) { /* proxy */ switch(p->proto & proto_command) { case command_proxy: len = docomm(p, proxyComm); break; #ifdef USE_POP case command_pop: if (getExData(p, data_apop, 0)) len = docomm(p, popComm); break; #endif case command_health: if (!memCheck()) len = -1; else len = docomm(p, healthComm); break; case command_identd: len = docomm(p, identdComm); break; default: ; } if (len == -2) { /* read more */ if (Debug > 3) { message(LOG_DEBUG, "%d TCP %d: read more from %d", stone->sd, psd, sd); } } else if (len < 0) { int flag = 0; if (!(pair->proto & proto_shutdown)) if (doshutdown(pair, 2) >= 0) flag = proto_shutdown; setclose(pair, flag); if (ValidSocket(psd)) { flag = 0; if (!(p->proto & proto_shutdown)) if (doshutdown(p, 2) >= 0) flag = proto_shutdown; setclose(p, flag); } return -1; } else { len = ex->len; } } if (pair->proto & proto_ohttp) { /* over http */ len = rmheader(p); if (len >= 0) { if (pair->proto & proto_ohttp_s) { commOutput(p, "HTTP/1.0 200 OK\r\n\r\n"); pair->proto &= ~proto_ohttp_s; } else if (pair->proto & proto_ohttp_d) { if (Debug > 3) message(LOG_DEBUG, "%d TCP %d: request to read, " "because response header from %d finished", stone->sd, psd, sd); p->proto |= (proto_select_r | proto_dirty); } } } #ifdef USE_POP if ((pair->proto & proto_command) == command_pop /* apop */ && !getExData(pair, data_apop, 0)) { int i; char *q; for (i=ex->start; i < ex->start + ex->len; i++) { if (ex->buf[i] == '<') { /* time stamp of APOP banner */ ExBuf *t = newExData(pair, data_apop); if (!t) break; q = t->buf + DATA_HEAD_LEN; for (; i < ex->start + ex->len; i++) { *q++ = ex->buf[i]; if (ex->buf[i] == '>') break; } *q = '\0'; if (Debug > 6) message(LOG_DEBUG, "%d TCP %d: APOP challenge: %s", stone->sd, sd, t->buf + DATA_HEAD_LEN); break; } } } #endif if (len <= 0 && !(pair->proto & (proto_eof | proto_close))) { if (Debug > 8) { message(LOG_DEBUG, "%d TCP %d: read more", stone->sd, sd); } pair->proto |= (proto_select_r | proto_dirty); /* read more */ if (len < 0) pair->proto |= (proto_first_r | proto_dirty); } return len; } #ifndef USE_EPOLL static void message_select(int pri, char *msg, fd_set *rout, fd_set *wout, fd_set *eout) { int i, r, w, e; for (i=0; i < FD_SETSIZE; i++) { r = FD_ISSET(i, rout); w = FD_ISSET(i, wout); e = FD_ISSET(i, eout); if (r || w || e) message(pri, "%s %d: %c%c%c", msg, i, (r ? 'r' : ' '), (w ? 'w' : ' '), (e ? 'e' : ' ')); } } #endif /* main event loop */ void proto2fdset( #ifndef USE_EPOLL int isthread, fd_set *routp, fd_set *woutp, fd_set *eoutp, #endif Pair *pair) { SOCKET sd; #ifdef USE_EPOLL struct epoll_event ev; ev.events = EPOLLONESHOT; ev.data.ptr = pair; #endif if (!pair) return; sd = pair->sd; if (InvalidSocket(sd)) return; #ifndef USE_EPOLL if (!isthread && (pair->proto & proto_thread)) return; #endif #ifdef USE_SSL if (pair->ssl_flag & (sf_sb_on_r | sf_sb_on_w)) { #ifdef USE_EPOLL if (pair->ssl_flag & sf_sb_on_r) ev.events |= EPOLLIN; if (pair->ssl_flag & sf_sb_on_w) ev.events |= EPOLLOUT; epoll_ctl(ePollFd, EPOLL_CTL_MOD, sd, &ev); #else FD_CLR(sd, routp); FD_CLR(sd, woutp); if (pair->ssl_flag & sf_sb_on_r) FdSet(sd, routp); if (pair->ssl_flag & sf_sb_on_w) FdSet(sd, woutp); #endif } else #endif if (pair->proto & proto_close) { if (ValidSocket(sd)) { pair->sd = INVALID_SOCKET; closesocket(sd); } return; } else if (pair->proto & proto_conninprog) { #ifdef USE_EPOLL ev.events |= (EPOLLOUT | EPOLLPRI); epoll_ctl(ePollFd, EPOLL_CTL_MOD, sd, &ev); #else FdSet(sd, woutp); FdSet(sd, eoutp); #endif #ifdef USE_SSL } else if (pair->ssl_flag & sf_wb_on_r) { #ifdef USE_EPOLL ev.events |= EPOLLIN; epoll_ctl(ePollFd, EPOLL_CTL_MOD, sd, &ev); #else FD_CLR(sd, woutp); FdSet(sd, routp); #endif } else if (pair->ssl_flag & sf_rb_on_w) { #ifdef USE_EPOLL ev.events |= EPOLLOUT; epoll_ctl(ePollFd, EPOLL_CTL_MOD, sd, &ev); #else FD_CLR(sd, routp); FdSet(sd, woutp); #endif } else if (pair->ssl_flag & (sf_cb_on_r | sf_cb_on_w)) { Pair *p = pair->pair; if (p) { /* suppress hasty read/write until established connection. assumes p is located before pair in pairs list */ SOCKET psd = p->sd; if (ValidSocket(psd)) { #ifdef USE_EPOLL struct epoll_event pev; pev.events = EPOLLONESHOT; pev.data.ptr = p; epoll_ctl(ePollFd, EPOLL_CTL_MOD, psd, &pev); if (Debug > 7) message(LOG_DEBUG, "%d TCP %d: proto2fdset2 " "epoll_ctl %d MOD %x events=%x", p->stone->sd, psd, ePollFd, (int)pev.data.ptr, pev.events); #else FD_CLR(psd, routp); FD_CLR(psd, woutp); #endif } } #ifdef USE_EPOLL if (pair->ssl_flag & (sf_cb_on_r)) ev.events |= EPOLLIN; if (pair->ssl_flag & (sf_cb_on_w)) ev.events |= EPOLLOUT; epoll_ctl(ePollFd, EPOLL_CTL_MOD, sd, &ev); #else FD_CLR(sd, routp); FD_CLR(sd, woutp); if (pair->ssl_flag & (sf_cb_on_r)) FdSet(sd, routp); if (pair->ssl_flag & (sf_cb_on_w)) FdSet(sd, woutp); #endif } else if (pair->ssl_flag & (sf_ab_on_r | sf_ab_on_w)) { #ifdef USE_EPOLL if (pair->ssl_flag & (sf_ab_on_r)) ev.events |= EPOLLIN; if (pair->ssl_flag & (sf_ab_on_w)) ev.events |= EPOLLOUT; epoll_ctl(ePollFd, EPOLL_CTL_MOD, sd, &ev); #else FD_CLR(sd, routp); FD_CLR(sd, woutp); if (pair->ssl_flag & (sf_ab_on_r)) FdSet(sd, routp); if (pair->ssl_flag & (sf_ab_on_w)) FdSet(sd, woutp); #endif #endif } else if (pair->proto & proto_connect) { int isset = 0; if (!(pair->proto & proto_eof) && (pair->proto & proto_select_r)) { #ifdef USE_EPOLL ev.events |= EPOLLIN; #else FdSet(sd, routp); #endif isset = 1; } if (!(pair->proto & proto_shutdown) && (pair->proto & proto_select_w)) { #ifdef USE_EPOLL ev.events |= EPOLLOUT; #else FdSet(sd, woutp); #endif isset = 1; } if (isset) #ifdef USE_EPOLL ev.events |= EPOLLPRI; epoll_ctl(ePollFd, EPOLL_CTL_MOD, sd, &ev); #else FdSet(sd, eoutp); #endif } #ifdef USE_EPOLL if (Debug > 7) message(LOG_DEBUG, "%d TCP %d: proto2fdset " "epoll_ctl %d MOD %x events=%x", pair->stone->sd, sd, ePollFd, (int)ev.data.ptr, ev.events); #endif pair->proto &= ~proto_dirty; } enum { RW_LEAVE = 0, RW_CONTINUE, RW_EINTR, RW_ONCE, }; int doReadWritePair(Pair *pair, Pair *opposite, int ready_r, int ready_w, int ready_e, int hangup, int error) { Pair *rPair, *wPair; Stone *stone; SOCKET stsd, sd, rsd, wsd; int len; int ret = RW_CONTINUE; /* assume to continue */ sd = pair->sd; if (InvalidSocket(sd)) return ret; stone = pair->stone; stsd = stone->sd; pair->loop++; if (hangup && (pair->proto & proto_connect)) ready_r = 1; if ((pair->proto & proto_conninprog) && (ready_w || ready_e || hangup)) { int optval; socklen_t optlen = sizeof(optval); pair->proto &= ~proto_conninprog; pair->proto |= proto_dirty; if (getsockopt(sd, SOL_SOCKET, SO_ERROR, (char*)&optval, &optlen) < 0) { #ifdef WINDOWS errno = WSAGetLastError(); #endif message(LOG_ERR, "%d TCP %d: getsockopt err=%d", stsd, sd, errno); pair->proto |= (proto_close | proto_dirty); if (opposite) opposite->proto |= (proto_close | proto_dirty); return RW_LEAVE; /* leave */ } if (optval) { message(LOG_ERR, "%d TCP %d: connect getsockopt err=%d", stsd, sd, optval); pair->proto |= (proto_close | proto_dirty); if (opposite) opposite->proto |= (proto_close | proto_dirty); return RW_LEAVE; /* leave */ } else { /* succeed in connecting */ if (Debug > 4) message(LOG_DEBUG, "%d TCP %d: connecting completed", stsd, sd); connected(pair); } } else if (ready_e) { /* Out-of-Band Data */ char buf[1]; len = recv(sd, buf, 1, MSG_OOB); if (len == 1) { if (opposite) wsd = opposite->sd; else wsd = INVALID_SOCKET; if (Debug > 3) message(LOG_DEBUG, "%d TCP %d: MSG_OOB 0x%02x to %d", stsd, sd, buf[0], wsd); if (ValidSocket(wsd)) { len = send(wsd, buf, 1, MSG_OOB); if (len != 1) { #ifdef WINDOWS errno = WSAGetLastError(); #endif message(LOG_ERR, "%d TCP %d: send MSG_OOB ret=%d, err=%d", stsd, sd, len, errno); } } } else { #ifdef WINDOWS errno = WSAGetLastError(); #endif message(LOG_ERR, "%d TCP %d: recv MSG_OOB ret=%d, err=%d", stsd, sd, len, errno); } #ifdef USE_SSL } else if (((pair->ssl_flag & sf_sb_on_r) && ready_r) || ((pair->ssl_flag & sf_sb_on_w) && ready_w) ) { pair->ssl_flag &= ~(sf_sb_on_r | sf_sb_on_w); pair->proto |= proto_dirty; doSSL_shutdown(pair, -1); } else if (((pair->ssl_flag & sf_cb_on_r) && ready_r) || ((pair->ssl_flag & sf_cb_on_w) && ready_w)) { pair->ssl_flag &= ~(sf_cb_on_r | sf_cb_on_w); pair->proto |= proto_dirty; if (doSSL_connect(pair) < 0) { /* SSL_connect fails, shutdown pairs */ if (opposite) { if (!(opposite->proto & proto_shutdown)) if (doshutdown(opposite, 2) >= 0) opposite->proto |= (proto_shutdown | proto_dirty); opposite->proto |= (proto_close | proto_dirty); } pair->proto |= (proto_close | proto_dirty); } } else if (((pair->ssl_flag & sf_ab_on_r) && ready_r) || ((pair->ssl_flag & sf_ab_on_w) && ready_w)) { pair->ssl_flag &= ~(sf_ab_on_r | sf_ab_on_w); pair->proto |= proto_dirty; if (doSSL_accept(pair) < 0) { /* SSL_accept fails */ pair->proto |= (proto_close | proto_dirty); if (opposite) opposite->proto |= (proto_close | proto_dirty); return RW_LEAVE; /* leave */ } if (pair->proto & proto_connect) if (opposite) reqconn(opposite, &stone->dsts[0]->addr, stone->dsts[0]->len); #endif } else if (((pair->proto & proto_select_r) && ready_r /* read */ #ifdef USE_SSL && !(pair->ssl_flag & sf_wb_on_r)) || ((pair->ssl_flag & sf_rb_on_w) && ready_w /* WANT_WRITE */ #endif )) { #ifdef USE_SSL pair->ssl_flag &= ~sf_rb_on_w; pair->proto |= proto_dirty; #endif rPair = pair; wPair = opposite; rsd = sd; if (wPair) wsd = wPair->sd; else wsd = INVALID_SOCKET; #ifdef USE_SSL read_pending: #endif rPair->proto &= ~proto_select_r; rPair->proto |= proto_dirty; if (rPair->proto & proto_dgram) { /* TCP <= UDP */ len = recvPairUDP(rPair); } else { rPair->count += REF_UNIT; len = doread(rPair); rPair->count -= REF_UNIT; } if (len < 0 || (rPair->proto & proto_close) || wPair == NULL) { if (len == -2 /* if EOF w/ pair, */ && !(rPair->proto & proto_shutdown) /* and not yet shutdowned, */ && wPair && !(wPair->proto & (proto_eof | proto_shutdown | proto_close)) /* and not bi-directional EOF and peer is not yet shutdowned, */ && (wPair->proto & proto_connect) && ValidSocket(wsd)) { /* and pair is valid, */ /* recevied EOF from rPair, so reply SSL notify to rPair and send SSL notify and FIN to wPair... */ /* no more to read */ rPair->proto |= (proto_eof | proto_dirty); /* Don't send notify, or further SSL_write will fail if (rPair->ssl) doSSL_shutdown(rPair, 0); */ if (!(wPair->proto & proto_shutdown)) if (doshutdown(wPair, 1) >= 0) /* send FIN */ wPair->proto |= (proto_shutdown | proto_dirty); wPair->proto &= ~proto_select_w; wPair->proto |= proto_dirty; } else { /* error, already shutdowned, or bi-directional EOF, so reply SSL notify to rPair, send SSL notify to wPair and shutdown wPair, set close flag */ int flag = 0; if (!(rPair->proto & proto_shutdown)) if (doshutdown(rPair, 2) >= 0) flag = proto_shutdown; rPair->proto &= ~proto_select_w; rPair->proto |= proto_dirty; setclose(rPair, (proto_eof | flag)); flag = 0; if (wPair) { if (!(wPair->proto & proto_shutdown)) if (doshutdown(wPair, 2) >= 0) flag = proto_shutdown; wPair->proto &= ~proto_select_w; wPair->proto |= proto_dirty; setclose(wPair, flag); } } } else { if (len > 0) { int first_flag; first_flag = (rPair->proto & proto_first_r); if (first_flag) len = first_read(rPair); if (wPair->proto & proto_dgram) { rPair->proto |= (proto_select_r | proto_dirty); if (sendPairUDP(wPair) < 0) { int flag = 0; if (!(rPair->proto & proto_shutdown)) if (doshutdown(rPair, 2) >= 0) flag = proto_shutdown; rPair->proto &= ~proto_select_w; rPair->proto |= proto_dirty; setclose(rPair, (proto_eof | flag)); } } else if (len > 0 && ValidSocket(wsd) && (wPair->proto & proto_connect) && !(wPair->proto & (proto_shutdown | proto_close)) && !(rPair->proto & proto_close)) { /* (wPair->proto & proto_eof) may be true */ wPair->proto |= (proto_select_w | proto_dirty); #ifdef ALWAYS_BUFFERING rPair->proto |= (proto_select_r | proto_dirty); #endif } else { return RW_LEAVE; /* leave */ } } else { /* EINTR */ rPair->proto |= (proto_select_r | proto_dirty); ret = RW_EINTR; } } } else if (((pair->proto & proto_select_w) && ready_w) /* write */ #ifdef USE_SSL || ((pair->ssl_flag & sf_wb_on_r) && ready_r) /* WANT_READ */ #endif ) { #ifdef USE_SSL pair->ssl_flag &= ~sf_wb_on_r; pair->proto |= proto_dirty; #endif wPair = pair; rPair = opposite; wsd = sd; if (rPair) rsd = rPair->sd; else rsd = INVALID_SOCKET; wPair->proto &= ~proto_select_w; wPair->proto |= proto_dirty; if (((wPair->proto & proto_command) == command_ihead) || ((wPair->proto & proto_command) == command_iheads)) { int state = (wPair->proto & state_mask); if (state == 0) { if (insheader(wPair) >= 0) /* insert header */ wPair->proto |= ++state; } } wPair->count += REF_UNIT; len = dowrite(wPair); wPair->count -= REF_UNIT; if (len < 0 || (wPair->proto & proto_close) || rPair == NULL) { int flag = 0; if (rPair) { if (ValidSocket(rsd) && !(rPair->proto & proto_shutdown)) if (doshutdown(rPair, 2) >= 0) flag = proto_shutdown; rPair->proto &= ~proto_select_w; rPair->proto |= proto_dirty; setclose(rPair, flag); } flag = 0; if (!(wPair->proto & proto_shutdown)) if (doshutdown(wPair, 2) >= 0) flag = proto_shutdown; setclose(wPair, flag); } else { ExBuf *ex; ex = wPair->t; /* top */ /* (wPair->proto & proto_eof) may be true */ if (ex->len <= 0) { /* all written */ if (wPair->proto & proto_first_w) { wPair->proto &= ~proto_first_w; wPair->proto |= proto_dirty; if (rPair && ValidSocket(rsd) && ((rPair->proto & proto_command) == command_proxy) && ((rPair->proto & state_mask) == 1)) { message_time_log(rPair); if (Debug > 7) message(LOG_DEBUG, "%d TCP %d: reconnect proxy", stsd, wPair->sd); wPair->proto |= (proto_first_r | proto_dirty); } } if (rPair && ValidSocket(rsd) && ((rPair->proto & proto_command) == command_iheads)) { if (Debug > 7) message(LOG_DEBUG, "%d TCP %d: insheader again", stsd, wPair->sd); rPair->proto &= ~state_mask; } if (rPair && ValidSocket(rsd) && (rPair->proto & proto_connect) && !(rPair->proto & (proto_eof | proto_close)) && !(wPair->proto & (proto_shutdown | proto_close)) ) { #ifdef USE_SSL if (rPair->ssl && SSL_pending(rPair->ssl)) { if (Debug > 4) message(LOG_DEBUG, "%d TCP %d: SSL_pending, read again", stsd, rPair->sd); ret = RW_ONCE; /* read once */ goto read_pending; } #endif rPair->proto |= (proto_select_r | proto_dirty); } else { return RW_LEAVE; /* leave */ } } else { /* EINTR */ wPair->proto |= (proto_select_w | proto_dirty); ret = RW_EINTR; } } } else if (error) { if (Debug > 3) message(LOG_DEBUG, "%d TCP %d: error", stsd, sd); pair->proto |= (proto_close | proto_dirty); if (opposite) opposite->proto |= (proto_close | proto_dirty); return RW_LEAVE; /* leave */ } return ret; } #ifndef USE_EPOLL void doReadWrite(Pair *pair) { /* pair must be source side */ int npairs = 1; Pair *p[2]; SOCKET stsd; int loop; int rx[2]; int tx[2]; int i; fd_set ri, wi, ei; fd_set ro, wo, eo; struct timeval tv; p[0] = pair; p[1] = pair->pair; stsd = pair->stone->sd; if (Debug > 8) message(LOG_DEBUG, "%d TCP %d, %d: doReadWrite", stsd, (p[0] ? p[0]->sd : INVALID_SOCKET), (p[1] ? p[1]->sd : INVALID_SOCKET)); if (p[1]) npairs++; FD_ZERO(&ri); FD_ZERO(&wi); FD_ZERO(&ei); rx[0] = p[0]->rx; tx[0] = p[0]->tx; if (p[1]) { rx[1] = p[1]->rx; tx[1] = p[1]->tx; } else { rx[1] = -1; tx[1] = -1; } loop = 0; for (;;) { /* loop until timeout or EOF/error */ tv.tv_sec = 0; tv.tv_usec = TICK_SELECT; ro = ri; wo = wi; eo = ei; for (i=0; i < npairs; i++) proto2fdset(1, &ro, &wo, &eo, p[i]); if (Debug > 10) message_select(LOG_DEBUG, "selectReadWrite1", &ro, &wo, &eo); if (select(FD_SETSIZE, &ro, &wo, &eo, &tv) <= 0) goto exit_loop; if (Debug > 10) message_select(LOG_DEBUG, "selectReadWrite2", &ro, &wo, &eo); for (i=0; i < npairs; i++) { SOCKET sd; int ret; if (!p[i]) continue; sd = p[i]->sd; if (InvalidSocket(sd)) continue; ret = doReadWritePair(p[i], p[1-i], FD_ISSET(sd, &ro), FD_ISSET(sd, &wo), FD_ISSET(sd, &eo), 0, 0); if (ret == RW_LEAVE) goto exit_loop; if (ret == RW_ONCE) break; /* read once */ if (ret == RW_EINTR) loop = 0; /* EINTR */ } if (++loop > 10) { /* check if spin occured */ if (rx[0] == p[0]->rx && tx[0] == p[0]->tx /* no update => spin */ && (!p[1] || (rx[1] == p[1]->rx && tx[1] == p[1]->tx))) { message(LOG_ERR, "%d TCP %d, %d: doReadWrite Can't happen " "spin occured tx/rx: %d/%d, %d/%d", stsd, (p[0] ? p[0]->sd : INVALID_SOCKET), (p[1] ? p[1]->sd : INVALID_SOCKET), tx[0], rx[0], tx[1], rx[1]); goto exit_loop; } rx[0] = p[0]->rx; tx[0] = p[0]->tx; if (p[1]) { rx[1] = p[1]->rx; tx[1] = p[1]->tx; } loop = 0; } } exit_loop: for (i=0; i < npairs; i++) { p[i]->proto &= ~proto_thread; p[i]->proto |= proto_dirty; p[i]->count -= REF_UNIT; } if (Debug > 8) message(LOG_DEBUG, "%d TCP %d, %d: doReadWrite end", stsd, (p[0] ? p[0]->sd : INVALID_SOCKET), (p[1] ? p[1]->sd : INVALID_SOCKET)); } void asyncReadWrite(Pair *pair) { /* pair must be source side */ ASYNC_BEGIN; doReadWrite(pair); ASYNC_END; } int doPair(Pair *pair) { SOCKET psd; Pair *p = pair->pair; if (!p || (pair->proto & proto_thread)) return 0; psd = p->sd; if (InvalidSocket(psd)) return 0; pair->count += REF_UNIT; p->count += REF_UNIT; pair->proto |= (proto_thread | proto_dirty); p->proto |= (proto_thread | proto_dirty); if ((pair->proto & proto_command) == command_source) { ASYNC(asyncReadWrite, pair); } else { ASYNC(asyncReadWrite, p); } return 1; } #endif int doAcceptConnect(Pair *p1) { Stone *stone = p1->stone; Pair *p2; int ret; if (Debug > 8) message(LOG_DEBUG, "%d TCP %d: doAcceptConnect", stone->sd, p1->sd); if (!acceptCheck(p1)) { freePair(p1); return 0; /* pair is disposed */ } p2 = p1->pair; if (p2->proto & proto_ohttp_d) { int i; char *p = stone->p; ExBuf *ex = p2->b; /* bottom */ i = strnparse(ex->buf, ex->bufmax - 5, &p, p1, 0xFF); ex->buf[i++] = '\r'; ex->buf[i++] = '\n'; ex->buf[i++] = '\r'; ex->buf[i++] = '\n'; ex->len = i; } ret = -1; if ((p1->proto & proto_connect) || (p1->proto & proto_dgram)) { ret = reqconn(p2, &stone->dsts[0]->addr, /* 0 is default */ stone->dsts[0]->len); if (ret < 0) { freePair(p2); freePair(p1); return 0; /* pair is disposed */ } } #ifndef USE_EPOLL if (ret >= 0) { p1->proto |= (proto_thread | proto_dirty); p2->proto |= (proto_thread | proto_dirty); doReadWrite(p1); } #endif if (!(p1->proto & proto_close)) { p1->proto |= proto_dirty; p2->proto |= proto_dirty; insertPairs(p1); return 1; /* pair is inserted */ } else { freePair(p2); freePair(p1); return 0; /* pair is disposed */ } } void asyncAcceptConnect(Pair *pair) { ASYNC_BEGIN; doAcceptConnect(pair); ASYNC_END; } Pair *getPairUDP(struct sockaddr *from, socklen_t fromlen, Stone *stone) { Pair *pair; ExBuf *t; SockAddr *peer; for (pair=stone->pairs->next; pair && pair->clock != -1; pair=pair->next) { Pair *p = pair->pair; if ((pair->proto & proto_dgram) && p && (p->proto & proto_connect)) { ExBuf *t = getExData(pair, data_peeraddr, 0); SockAddr *dst; dst = (SockAddr*)(t->buf + DATA_HEAD_LEN); if (saComp(&dst->addr, from)) { time(&pair->clock); return pair; } } } /* can't find pair, so create */ pair = newPair(); if (!pair) return NULL; /* save `from' to ExBuf to check in doAcceptConnect */ bcopy(&fromlen, pair->t->buf, DATA_HEAD_LEN); bcopy(from, pair->t->buf + DATA_HEAD_LEN, fromlen); pair->stone = stone; pair->proto = (proto_dgram | command_source); pair->timeout = stone->timeout; t = newExData(pair, data_peeraddr); peer = (SockAddr*)(t->buf + DATA_HEAD_LEN); peer->len = fromlen; bcopy(from, &peer->addr, fromlen); if (doAcceptConnect(pair)) return pair; return NULL; /* pair is disposed */ } void recvStoneUDP(Stone *stone) { if (stone->proto & proto_udp_d) { /* UDP => UDP */ PktBuf *pb = recvUDP(stone); if (pb) { sendUDP(pb); ungetPktBuf(pb); } } else { /* UDP => TCP */ struct sockaddr_storage ss; struct sockaddr *from = (struct sockaddr*)&ss; socklen_t fromlen = sizeof(ss); int flags = 0; int len; Pair *rPair; Pair *wPair; ExBuf *ex; char addrport[STRMAX+1]; ex = getExBuf(); if (!ex) { message(LOG_CRIT, "%d UDP: out of memory", stone->sd); return; } ex->start = 0; #ifdef MSG_DONTWAIT if (!(stone->proto & proto_block_s)) flags = MSG_DONTWAIT; #endif #ifdef MSG_TRUNC flags |= MSG_TRUNC; #endif len = recvfrom(stone->sd, ex->buf + UDP_HEAD_LEN, ex->bufmax - UDP_HEAD_LEN, flags, from, &fromlen); addrport[0] = '\0'; if (len < 0) { #ifdef WINDOWS errno = WSAGetLastError(); #endif addrport2strOnce(from, fromlen, proto_udp, addrport, STRMAX, 0); message(LOG_ERR, "%d UDP: recvfrom err=%d %s", stone->sd, errno, addrport); ungetExBuf(ex); return; } ex->buf[0] = ((unsigned)len >> 8); ex->buf[1] = ((unsigned)len % 256); ex->len += UDP_HEAD_LEN + len; if (Debug > 8) { addrport2strOnce(from, fromlen, proto_udp, addrport, STRMAX, 0); message(LOG_DEBUG, "%d UDP: recvfrom len=%d %s", stone->sd, len, addrport); } rPair = getPairUDP(from, fromlen, stone); if (!rPair) { message(LOG_ERR, "%d UDP: fail to get pair", stone->sd); ungetExBuf(ex); return; } rPair->rx += len; wPair = rPair->pair; if (wPair) { wPair->clock = rPair->clock; wPair->b->next = ex; wPair->b = ex; if (wPair->t->len <= 0) { ExBuf *t = wPair->t; wPair->t = wPair->t->next; /* drop top */ ungetExBuf(t); } wPair->proto |= (proto_select_w | proto_dirty); } } } #ifdef USE_EPOLL void dispatch(int epfd, struct epoll_event *evs, int nevs) { int i; for (i=0; i < nevs; i++) { int common; int other; struct epoll_event ev = evs[i]; union { Stone stone; Pair pair; Origin origin; } *p; if (Debug > 8) message(LOG_DEBUG, "epoll %d: evs[%d].data=%x", epfd, i, (int)ev.data.ptr); common = *(int*)ev.data.ptr; other = (ev.events & ~(EPOLLIN | EPOLLPRI | EPOLLOUT)); p = ev.data.ptr; switch(common & type_mask) { case type_stone: if (Debug > 10 || (other && Debug > 2)) message(LOG_DEBUG, "stone %d: epoll %d events=%x type=%d", p->stone.sd, epfd, ev.events, common); if (p->stone.proto & proto_udp_s) { recvStoneUDP(&p->stone); } else { Pair *pair = acceptPair(&p->stone); if (pair) { if (p->stone.proto & proto_ident) { ASYNC(asyncAcceptConnect, pair); } else { doAcceptConnect(pair); } } } break; case type_pair: if (Debug > 10 || (other && Debug > 2)) message(LOG_DEBUG, "TCP %d: epoll %d events=%x type=%d", p->pair.sd, epfd, ev.events, common); doReadWritePair(&p->pair, p->pair.pair, (ev.events & EPOLLIN) != 0, (ev.events & EPOLLOUT) != 0, (ev.events & EPOLLPRI) != 0, (ev.events & EPOLLHUP) != 0, (ev.events & EPOLLERR) != 0); break; case type_origin: { Origin *origin = &p->origin; PktBuf *pb; if (Debug > 10 || (other && Debug > 2)) message(LOG_DEBUG, "%d UDP %d: epoll %d events=%x type=%d", origin->stone->sd, origin->sd, epfd, ev.events, common); pb = recvUDP((Stone*)origin); if (pb) { sendUDP(pb); ungetPktBuf(pb); } } break; default: message(LOG_ERR, "Irregular event events=%x type=%d", ev.events, common); } } } #endif int scanPairs( #ifndef USE_EPOLL fd_set *rop, fd_set *wop, fd_set *eop, #endif Pair *pairs ) { Pair *pair; int ret = 1; int all; if (Debug > 8) message(LOG_DEBUG, "scanPairs"); if (pairs) { all = 0; } else { pairs = PairTop; all = 1; } for (pair=pairs->next; pair != NULL && (all || pair->clock != -1); /* until top */ pair=pair->next) { SOCKET sd = pair->sd; if (all && pair->clock == -1) { /* skip top */ pairs = pair; continue; } if (ValidSocket(sd)) { time_t clock; int idle = 1; /* assume no events happen on sd */ #ifndef USE_EPOLL if (pair->proto & proto_thread) continue; if (FD_ISSET(sd, rop) || FD_ISSET(sd, wop) || FD_ISSET(sd, eop)) { Pair *p = pair->pair; if (p && (p->proto & proto_dgram)) { doReadWritePair(pair, p, FD_ISSET(sd, rop), FD_ISSET(sd, wop), FD_ISSET(sd, eop), 0, 0); idle = 0; } else if (doPair(pair)) idle = 0; } #endif if (idle && pair->timeout > 0 && (time(&clock), clock - pair->clock > pair->timeout)) { Pair *p = pair->pair; if (Debug > 2) { message(LOG_DEBUG, "%d TCP %d: idle time exceeds", pair->stone->sd, sd); message_pair(LOG_DEBUG, pair); } setclose(pair, proto_shutdown); if (p) setclose(p, proto_shutdown); } } } if (Debug > 8) message(LOG_DEBUG, "scanPairs done"); return ret; } #ifndef USE_EPOLL int scanStones(fd_set *rop, fd_set *wop, fd_set *eop) { Stone *stone; for (stone=stones; stone != NULL; stone=stone->next) { int isset; waitMutex(FdEinMutex); isset = (FD_ISSET(stone->sd, eop) && FD_ISSET(stone->sd, &ein)); if (isset) FD_CLR(stone->sd, &ein); freeMutex(FdEinMutex); if (isset) { message(LOG_ERR, "stone %d: exception", stone->sd); } else { if (FD_ISSET(stone->sd, rop) && FD_ISSET(stone->sd, &rin)) { if (stone->proto & proto_udp_s) { recvStoneUDP(stone); } else { Pair *pair = acceptPair(stone); if (pair) ASYNC(asyncAcceptConnect, pair); } } } if ((stone->proto & proto_udp_s) && (stone->proto & proto_udp_d)) { scanUDP(rop, eop, (Origin *)stone->p); } else { scanPairs(rop, wop, eop, stone->pairs); } } return 1; } #endif /* stone */ #ifdef USE_SSL static int newMatch(void *parent, void *ptr, CRYPTO_EX_DATA *ad, int idx, long argl, void *argp) { char **match = malloc(sizeof(char*) * (NMATCH_MAX+1)); if (match) { int i; for (i=0; i <= NMATCH_MAX; i++) match[i] = NULL; if (Debug > 4) message(LOG_DEBUG, "newMatch %d: %x", NewMatchCount++, (int)match); return CRYPTO_set_ex_data(ad, idx, match); } return 0; } static void freeMatch(void *parent, void *ptr, CRYPTO_EX_DATA *ad, int idx, long argl, void *argp) { char **match = ptr; int i; for (i=0; i <= NMATCH_MAX; i++) { if (match[i]) free(match[i]); } if (Debug > 4) message(LOG_DEBUG, "freeMatch %d: %x", --NewMatchCount, (int)match); free(match); } static int hostcmp(char *pat, char *host) { char a, b; while (*pat) { if (*pat == '*') { pat++; while (*host) { if (*host == *pat) break; host++; } } a = toupper(*pat); b = toupper(*host); if (a != b) return a - b; pat++; host++; } return *host; } static int hostcheck(Pair *pair, X509 *cert, char *host) { X509_EXTENSION *ext; GENERAL_NAMES *ialt; char name[LONGSTRMAX+1]; int i = X509_get_ext_by_NID(cert, NID_subject_alt_name, -1); if (i >= 0 && (ext=X509_get_ext(cert, i)) && (ialt=X509V3_EXT_d2i(ext))) { int done = 0; for (i=0; !done && i < sk_GENERAL_NAME_num(ialt); i++) { GENERAL_NAME *gen = sk_GENERAL_NAME_value(ialt, i); if (gen->type == GEN_DNS && gen->d.ia5) { int len = gen->d.ia5->length; if (len > LONGSTRMAX) len = LONGSTRMAX; strncpy(name, (char*)gen->d.ia5->data, len); name[len] = '\0'; if (hostcmp(name, host) == 0) { if (Debug > 4) message(LOG_DEBUG, "match %s dNSName=%s", host, name); done = 1; /* match */ } else if (Debug > 5) message(LOG_DEBUG, "dNSName: %s", name); } GENERAL_NAME_free(gen); } sk_GENERAL_NAME_free(ialt); if (done) return 1; } if (X509_NAME_get_text_by_NID(X509_get_subject_name(cert), NID_commonName, name, sizeof(name)) >= 0) { if (hostcmp(name, host) == 0) { if (Debug > 4) message(LOG_DEBUG, "match %s CN=%s", host, name); return 1; /* match */ } message(LOG_ERR, "%d TCP %d: connect to %s, but CN=%s", pair->stone->sd, pair->sd, host, name); return 0; } message(LOG_ERR, "%d TCP %d: no dNSName nor CN", pair->stone->sd, pair->sd); return 0; } static int verify_callback(int preverify_ok, X509_STORE_CTX *ctx) { X509 *err_cert; int err, depth, depthmax; regex_t *re; long serial = -1; SSL *ssl; Pair *pair; StoneSSL *ss; char buf[BUFMAX]; char *p; err_cert = X509_STORE_CTX_get_current_cert(ctx); err = X509_STORE_CTX_get_error(ctx); depth = X509_STORE_CTX_get_error_depth(ctx); ssl = X509_STORE_CTX_get_ex_data (ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); if (!ssl) { message(LOG_ERR, "SSL callback can't get SSL object"); return 0; /* always fail */ } pair = SSL_get_ex_data(ssl, PairIndex); if (!pair) { message(LOG_ERR, "SSL callback don't have ex_data, verify fails"); return 0; /* always fail */ } if ((pair->proto & proto_command) == command_source) { ss = pair->stone->ssl_server; } else { ss = pair->stone->ssl_client; } depthmax = ((pair->ssl_flag & sf_depth) >> sf_depth_bit); if (depth >= depthmax) { depthmax = depth + 1; pair->ssl_flag = ((pair->ssl_flag & ~sf_depth) | (depthmax << sf_depth_bit)); } if (depth == 0) { ASN1_INTEGER *n = X509_get_serialNumber(err_cert); if (n) serial = ASN1_INTEGER_get(n); if (ss->serial == -1 && serial >= 0) { ss->serial = serial; } else if (ss->serial >= 0 && serial != ss->serial) { message(LOG_ERR, "%d TCP %d: SSL callback serial number mismatch " "%lx != %lx", pair->stone->sd, pair->sd, serial, ss->serial); return 0; /* fail */ } if (ss->name && !ss->re[depth] && !hostcheck(pair, err_cert, ss->name)) return 0; } if (Debug > 3) message(LOG_DEBUG, "%d TCP %d: callback: err=%d, depth=%d/%d, preverify=%d", pair->stone->sd, pair->sd, err, depth, depth - depthmax, preverify_ok); p = X509_NAME_oneline(X509_get_subject_name(err_cert), buf, BUFMAX-1); if (!p) return 0; if (ss->verbose) message(LOG_DEBUG, "%d TCP %d: [depth%d=%s]", pair->stone->sd, pair->sd, depth, p); if (depth > ss->depth) { preverify_ok = 0; X509_STORE_CTX_set_error(ctx, X509_V_ERR_CERT_CHAIN_TOO_LONG); } if (!preverify_ok) { #ifdef CRYPTOAPI if (ss->sslparm & sslparm_storeca) { int ret = CryptoAPI_verify_certificate(err_cert); if (ret < 0) { if (ss->verbose) message(LOG_DEBUG, "%d TCP %d: verify error err=%d %s, " "CryptoAPI verify %ld", pair->stone->sd, pair->sd, err, X509_verify_cert_error_string(err), ERR_get_error()); return 0; } else if (ret == 0) { if (ss->verbose) message(LOG_DEBUG, "%d TCP %d: verify error err=%d %s, " "CryptoAPI certificate is not trusted", pair->stone->sd, pair->sd, err, X509_verify_cert_error_string(err)); return 0; } } else { #endif if (ss->verbose) message(LOG_DEBUG, "%d TCP %d: verify error err=%d %s", pair->stone->sd, pair->sd, err, X509_verify_cert_error_string(err)); if (!(ss->sslparm & sslparm_ignore)) return 0; #ifdef CRYPTOAPI } #endif } re = ss->re[DEPTH_MAX - depthmax + depth]; if (!re) re = ss->re[depth]; if (depth < DEPTH_MAX && re) { SSL_SESSION *sess = NULL; regmatch_t pmatch[NMATCH_MAX]; char **match; err = regexec(re, p, (size_t)NMATCH_MAX, pmatch, 0); if (Debug > 3) message(LOG_DEBUG, "%d TCP %d: regexec%d=%d", pair->stone->sd, pair->sd, depth, err); if (err) return 0; /* not match */ sess = SSL_get1_session(ssl); if (sess && (match = SSL_SESSION_get_ex_data(sess, MatchIndex))) { int i; int j = 1; if (serial >= 0) { char str[STRMAX+1]; int len; snprintf(str, STRMAX, "%lx", serial); len = strlen(str); if (match[0]) free(match[0]); match[0] = malloc(len+1); if (match[0]) { strncpy(match[0], str, len); match[0][len] = '\0'; } } for (i=1; i <= NMATCH_MAX; i++) { if (match[i]) continue; if (pmatch[j].rm_so >= 0) { int len = pmatch[j].rm_eo - pmatch[j].rm_so; match[i] = malloc(len+1); if (match[i]) { strncpy(match[i], p + pmatch[j].rm_so, len); match[i][len] = '\0'; if (Debug > 4) message(LOG_DEBUG, "%d TCP %d: \\%d=%s", pair->stone->sd, pair->sd, i, match[i]); } j++; } } } else { message(LOG_ERR, "%d TCP %d: SSL callback can't get session's ex_data", pair->stone->sd, pair->sd); } if (sess) SSL_SESSION_free(sess); } else { if (Debug > 3) message(LOG_DEBUG, "%d TCP %d: re%d=NULL", pair->stone->sd, pair->sd, depth); } return 1; /* if re is null, always succeed */ } static int passwd_callback(char *buf, int size, int rwflag, void *passwd) { strncpy(buf, (char *)(passwd), size); buf[size-1] = '\0'; return(strlen(buf)); } #ifndef OPENSSL_NO_TLSEXT static int ssl_servername_callback(SSL *ssl, int *ad, void *arg) { Pair *pair = SSL_get_ex_data(ssl, PairIndex); StoneSSL *ss = pair->stone->ssl_server; Stone *stone; const char *name = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); if (!ss || !ss->name) return SSL_TLSEXT_ERR_NOACK; if (!name) { if (ss && ss->verbose) message(LOG_DEBUG, "%d TCP %d: No servername, expects: %s", pair->stone->sd, pair->sd, ss->name); return SSL_TLSEXT_ERR_OK; } if (strcmp(name, ss->name) == 0) return SSL_TLSEXT_ERR_OK; for (stone=pair->stone->children; stone; stone=stone->children) { StoneSSL *sn = stone->ssl_server; if (!sn || !sn->name) return SSL_TLSEXT_ERR_NOACK; if (strcmp(name, sn->name) == 0) { if (sn->verbose) message(LOG_DEBUG, "%d TCP %d: Switching server context: %s", stone->sd, pair->sd, sn->name); SSL_set_SSL_CTX(ssl, sn->ctx); pair->stone = stone; return SSL_TLSEXT_ERR_OK; } } return SSL_TLSEXT_ERR_ALERT_FATAL; } #endif StoneSSL *mkStoneSSL(SSLOpts *opts, int isserver) { StoneSSL *ss; int err; int i; ss = malloc(sizeof(StoneSSL)); if (!ss) { memerr: message(LOG_CRIT, "Out of memory"); exit(1); } ss->verbose = opts->verbose; ss->shutdown_mode = opts->shutdown_mode; ss->name = opts->servername; ss->ctx = SSL_CTX_new(opts->meth); if (!ss->ctx) { message(LOG_ERR, "SSL_CTX_new error"); goto error; } SSL_CTX_set_options(ss->ctx, opts->off); SSL_CTX_set_mode(ss->ctx, SSL_MODE_ENABLE_PARTIAL_WRITE); SSL_CTX_set_verify(ss->ctx, opts->mode, opts->callback); SSL_CTX_set_verify_depth(ss->ctx, opts->depth + 1); ss->depth = opts->depth; ss->serial = opts->serial; ss->lbmod = opts->lbmod; ss->lbparm = opts->lbparm; if (opts->caFile || opts->caPath) { if (!SSL_CTX_load_verify_locations(ss->ctx, opts->caFile, opts->caPath)) { message(LOG_ERR, "SSL_CTX_load_verify_locations(%s,%s) error", opts->caFile, opts->caPath); goto error; } if (opts->vflags) X509_STORE_set_flags(SSL_CTX_get_cert_store(ss->ctx), opts->vflags); } if (opts->pfxFile) { FILE *fp = fopen(opts->pfxFile, "r"); PKCS12 *p12; EVP_PKEY *key; X509 *cert; if (!fp) { message(LOG_ERR, "Can't open pfx file: %s", opts->pfxFile); goto error; } p12 = d2i_PKCS12_fp(fp, NULL); if (!p12) { message(LOG_ERR, "Can't read pfx file: %s", opts->pfxFile); fclose(fp); goto error; } fclose(fp); key = NULL; cert = NULL; if (!PKCS12_parse(p12, opts->passwd, &key, &cert, NULL)) { message(LOG_ERR, "Can't parse PKCS12(%s) %s", opts->pfxFile, ERR_error_string(ERR_get_error(), NULL)); goto error; } if (cert) { if (!SSL_CTX_use_certificate(ss->ctx, cert)) { message(LOG_ERR, "SSL_CTX_use_certificate(%s) %s", opts->pfxFile, ERR_error_string(ERR_get_error(), NULL)); X509_free(cert); goto error; } X509_free(cert); } if (key) { if (!SSL_CTX_use_PrivateKey(ss->ctx, key)) { message(LOG_ERR, "SSL_CTX_use_PrivateKey(%s) %s", opts->pfxFile, ERR_error_string(ERR_get_error(), NULL)); EVP_PKEY_free(key); goto error; } EVP_PKEY_free(key); } PKCS12_free(p12); } else { if (opts->passwd) { SSL_CTX_set_default_passwd_cb(ss->ctx, passwd_callback); SSL_CTX_set_default_passwd_cb_userdata(ss->ctx, opts->passwd); } if (opts->keyFile && !SSL_CTX_use_PrivateKey_file (ss->ctx, opts->keyFile, X509_FILETYPE_PEM)) { message(LOG_ERR, "SSL_CTX_use_PrivateKey_file(%s) %s", opts->keyFile, ERR_error_string(ERR_get_error(), NULL)); goto error; } if (opts->certFile && !SSL_CTX_use_certificate_file(ss->ctx, opts->certFile, X509_FILETYPE_PEM)) { message(LOG_ERR, "SSL_CTX_use_certificate_file(%s) error", opts->certFile); goto error; } } ss->sslparm = 0; if (opts->useSNI) ss->sslparm |= sslparm_sni; if (opts->certIgnore) ss->sslparm |= sslparm_ignore; #ifdef CRYPTOAPI if (opts->certStoreCA) ss->sslparm |= sslparm_storeca; if (opts->certStore) { if (!SSL_CTX_use_CryptoAPI_certificate(ss->ctx, opts->certStore)) { message(LOG_ERR, "Can't load certificate \"%s\" " "from Microsoft Certificate Store, %s", opts->certStore, ERR_error_string(ERR_get_error(), NULL)); goto error; } } #endif if (opts->cipherList && !SSL_CTX_set_cipher_list(ss->ctx, opts->cipherList)) { message(LOG_ERR, "SSL_CTX_set_cipher_list(%s) error", opts->cipherList); goto error; } for (i=0; i < DEPTH_MAX; i++) { if (opts->regexp[i]) { ss->re[i] = malloc(sizeof(regex_t)); if (!ss->re) goto memerr; err = regcomp(ss->re[i], opts->regexp[i], REG_EXTENDED|REG_ICASE); if (err) { message(LOG_ERR, "RegEx compiling error %d", err); goto error; } if (Debug > 5) { message(LOG_DEBUG, "regexp[%d]=%s", i, opts->regexp[i]); } } else { ss->re[i] = NULL; } } if (isserver) { #ifndef OPENSSL_NO_TLSEXT if (ss->sslparm & sslparm_sni) { SSL_CTX_set_tlsext_servername_callback (ss->ctx, ssl_servername_callback); } #endif if (opts->sid_ctx) { int ret; int len = strlen((char*)opts->sid_ctx); ret = SSL_CTX_set_session_id_context(ss->ctx, opts->sid_ctx, len); if (!ret) { len = SSL_MAX_SSL_SESSION_ID_LENGTH; opts->sid_ctx[len] = '\0'; message(LOG_ERR, "Too long sid_ctx, truncated to '%s'", opts->sid_ctx); ret = SSL_CTX_set_session_id_context(ss->ctx, opts->sid_ctx, len); if (!ret) { message(LOG_ERR, "SSL_CTX_set_session_id_context error"); } } } SSL_CTX_set_session_cache_mode (ss->ctx, (SSL_SESS_CACHE_SERVER | SSL_SESS_CACHE_NO_AUTO_CLEAR)); } return ss; error: if (opts->verbose) message(LOG_INFO, "%s", ERR_error_string(ERR_get_error(), NULL)); exit(1); } void rmStoneSSL(StoneSSL *ss) { int i; SSL_CTX_free(ss->ctx); for (i=0; i < DEPTH_MAX; i++) { if (ss->re[i]) { regfree(ss->re[i]); free(ss->re[i]); } } free(ss); } char *exPatFile(char *pat, char *name, char *src, char *dst) { char str[STRMAX+1]; char *p; int pos, len, nlen, slen, dlen; int l; if (!name) name = ""; if (!src) src = ""; if (!dst) dst = ""; nlen = strlen(name); slen = strlen(src); dlen = strlen(dst); len = 0; for (pos=0; pos < STRMAX; pos++) { if (pat[pos] == '\0') { str[len] = '\0'; break; } else if (pat[pos] == '%') { switch (pat[++pos]) { case 'n': l = nlen; p = name; break; case 's': l = slen; p = src; break; case 't': l = dlen; p = dst; break; default: l = 1; p = &pat[pos]; } if (len + l >= STRMAX) l = STRMAX - len; strncpy(str+len, p, l); len += l; } else { str[len++] = pat[pos]; } } str[STRMAX] = '\0'; return strdup(str); } void exPatOpts(SSLOpts *opts, char *src, char *dst) { if (opts->pfxFilePat) { opts->pfxFile = exPatFile(opts->pfxFilePat, opts->servername, src, dst); if (Debug > 3) message(LOG_DEBUG, "exPatPfx: %s => %s", opts->pfxFilePat, opts->pfxFile); } else { if (opts->certFilePat) { opts->certFile = exPatFile(opts->certFilePat, opts->servername, src, dst); if (Debug > 3) message(LOG_DEBUG, "exPatCert: %s => %s", opts->certFilePat, opts->certFile); } if (opts->keyFilePat) { opts->keyFile = exPatFile(opts->keyFilePat, opts->servername, src, dst); if (Debug > 3) message(LOG_DEBUG, "exPatKey: %s => %s", opts->keyFilePat, opts->keyFile); } } if (opts->passFilePat) { opts->passFile = exPatFile(opts->passFilePat, opts->servername, src, dst); if (Debug > 3) message(LOG_DEBUG, "exPatPass: %s => %s", opts->passFilePat, opts->passFile); } if (opts->passFile) { FILE *fp = fopen(opts->passFile, "r"); char str[STRMAX+1]; int i; if (!fp) { message(LOG_ERR, "Can't open passwd file: %s", opts->passFile); exit(1); } for (i=0; i < STRMAX; i++) { int c = getc(fp); if (c == '\r' || c == '\n' || c == EOF) break; str[i] = c; } str[i] = '\0'; fclose(fp); opts->passwd = strdup(str); } } #endif void rmoldstone(void) { Stone *stone, *next; stone = oldstones; oldstones = NULL; for ( ; stone != NULL; stone=next) { next = stone->next; if (stone->port) { #ifdef USE_EPOLL epoll_ctl(ePollFd, EPOLL_CTL_DEL, stone->sd, NULL); #else waitMutex(FdRinMutex); waitMutex(FdEinMutex); FD_CLR(stone->sd, &rin); FD_CLR(stone->sd, &ein); freeMutex(FdEinMutex); freeMutex(FdRinMutex); #endif closesocket(stone->sd); } #ifdef USE_SSL if (stone->ssl_server) rmStoneSSL(stone->ssl_server); if (stone->ssl_client) rmStoneSSL(stone->ssl_client); #endif free(stone); } } void rmoldconfig(void) { int i; for (i=0; i < OldConfigArgc; i++) { free(OldConfigArgv[i]); } OldConfigArgc = 0; free(OldConfigArgv); OldConfigArgv = NULL; } void repeater(void) { int ret; static int spin = 0; static int nerrs = 0; static time_t scantime = 0; time_t now; Pair *pair; #ifdef USE_EPOLL int timeout; struct epoll_event evs[EVSMAX]; for (pair=PairTop; pair != NULL; pair=pair->next) if (pair->clock != -1 && /* not top */ (pair->proto & proto_dirty)) proto2fdset(pair); if (conns.next || trash.next || spin > 0 || AsyncCount > 0) { if (AsyncCount == 0 && spin > 0) spin--; timeout = TICK_SELECT / 1000; } else if (MinInterval > 0) { timeout = MinInterval * 1000; } else { timeout = -1; } ret = epoll_wait(ePollFd, evs, EVSMAX, timeout); if (Debug > 10) { message(LOG_DEBUG, "epoll %d: %d", ePollFd, ret); } #else struct timeval tv, *timeout; fd_set rout, wout, eout; rout = rin; wout = win; eout = ein; for (pair=PairTop; pair != NULL; pair=pair->next) if (pair->clock != -1 && /* not top */ !(pair->proto & proto_thread)) proto2fdset(0, &rout, &wout, &eout, pair); if (conns.next || trash.next || spin > 0 || AsyncCount > 0) { if (AsyncCount == 0 && spin > 0) spin--; timeout = &tv; timeout->tv_sec = 0; timeout->tv_usec = TICK_SELECT; } else if (MinInterval > 0) { timeout = &tv; timeout->tv_sec = MinInterval; timeout->tv_usec = 0; } else { timeout = NULL; /* block indefinitely */ } if (Debug > 10) { message(LOG_DEBUG, "select main(%ld)", (timeout ? timeout->tv_usec : 0)); message_select(LOG_DEBUG, "select main IN ", &rout, &wout, &eout); } ret = select(FD_SETSIZE, &rout, &wout, &eout, timeout); if (Debug > 10) { message(LOG_DEBUG, "select main: %d", ret); message_select(LOG_DEBUG, "select main OUT", &rout, &wout, &eout); } #endif if (ret > 0) { nerrs = 0; spin = SPIN_MAX; #ifdef USE_EPOLL dispatch(ePollFd, evs, ret); #else (void)(scanStones(&rout, &wout, &eout) > 0); #endif } else if (ret < 0) { #ifdef WINDOWS errno = WSAGetLastError(); #endif if (errno != EINTR) { #ifdef USE_EPOLL message(LOG_ERR, "epoll %d error err=%d", ePollFd, errno); #else message(LOG_ERR, "select error err=%d", errno); #endif if (++nerrs >= NERRS_MAX) { #ifdef USE_EPOLL message(LOG_ERR, "epoll %d error %d times, exiting", ePollFd, nerrs); #else message(LOG_ERR, "select error %d times, exiting", nerrs); message_select(LOG_INFO, "IN", &rin, &win, &ein); #endif message_pairs(LOG_INFO); message_origins(LOG_INFO); message_conns(LOG_INFO); exit(1); } } usleep(TICK_SELECT); } if (conns.next) scanConns(); time(&now); if (now == scantime) return; scantime = now; if (backups && scantime - lastScanBackups >= MinInterval) { lastScanBackups = scantime; scanBackups(); } #ifdef USE_EPOLL if (PairTop) scanPairs(NULL); if (OriginTop) scanUDP(NULL); #endif if (PairTop) scanClose(NULL); if (oldstones) rmoldstone(); if (OldConfigArgc) rmoldconfig(); #ifdef USE_SSL ERR_remove_state(0); #endif } int reusestone(Stone *stone) { Stone *s; if (!oldstones) return 0; for (s=oldstones; s != NULL; s=s->next) { if (s->port == stone->port && s->proto == stone->proto) { if (Debug > 5) message(LOG_DEBUG, "stone %d: reused port %d", s->sd, s->port); stone->sd = s->sd; s->port = 0; return 1; } } return 0; } #ifdef NO_FAMILY_T typedef int sa_family_t; #endif void mkXhostsExt(char *host, char *str, XHosts *ext) { int kind = 0; char *top = NULL; /* dummy init to suppress warnings */ u_long num = 0; int i = 0; do { switch(kind) { case -3: /* pass if digit or '.' until ',' */ if (str[i] == '.') break; case -2: /* pass if digit until ',' */ if (isdigit(str[i])) break; case -1: /* pass ',' */ if (str[i] == ',' || str[i] == '\0') { kind = 0; /* found next ext */ break; } error: message(LOG_ERR, "Unknown extension: \"%s\" in %s/%s", &str[i], host, str); exit(1); case 0: top = &str[i]; if (isdigit(*top)) { num = *top - '0'; kind = 1; break; } if (*top == 'v') { i++; if (top[1] == '4') { ext->xhost.addr.sa_family = AF_INET; #ifdef AF_INET6 } else if (top[1] == '6') { ext->xhost.addr.sa_family = AF_INET6; #endif } else { goto error; } kind = -1; /* expect ',' or end of string */ break; } if (*top == 'p') { if (isdigit(top[1])) { ext->mode = atoi(top+1); } else { ext->mode = 1; } kind = -2; /* skip to the next ext */ break; } goto error; case 1: /* net mask */ if (str[i] == ',' || str[i] == '\0') { ext->mbits = num; if (ext->mbits > 32) { #ifdef AF_INET6 /* force to set IPv6 */ ext->xhost.addr.sa_family = AF_INET6; } if (ext->mbits > 128) { #endif goto error; } kind = 0; /* found next ext */ break; } case 2: /* nnn..nnn.nnn */ case 3: /* nnn.nnn..nnn */ if (str[i] == '.') { i++; num <<= 8; kind++; } case 4: /* nnn.nnn.nnn. */ if (isdigit(str[i])) { num = ((num & 0xFFFFFF00) | ((num & 0xFF) * 10 + (str[i] - '0'))); break; } ext->xhost.addr.sa_family = AF_INET; /* force to set IPv4 */ for (ext->mbits=0; ext->mbits < 32 && num; ext->mbits++) { if (!(num & 0x80000000)) { message(LOG_ERR, "netmask by bits pattern " "is deprecated: %s/%s", host, top); exit(1); } num <<= 1; } i--; /* unget */ kind = -1; /* expect ',' or end of string */ break; default: message(LOG_ERR, "Can't happen: kind=%d in mkXhostsExt", kind); exit(1); } } while (str[i++]); if (Debug > 9) message(LOG_DEBUG, "mkXhostsExt: host=%s ext=%s " "family=%d mbits=%d mode=%d", host, str, ext->xhost.addr.sa_family, ext->mbits, ext->mode); } XHosts *mkXhosts(int nhosts, char *hosts[], sa_family_t family, char *mesg) { XHosts *top = NULL; XHosts *bot = NULL; char xhost[STRMAX+1]; int allow = 1; int i; char *p; for (i=0; i < nhosts; i++) { XHosts *new; if (Debug > 10) message(LOG_DEBUG, "xhost[%d]=\"%s\"", i, hosts[i]); if (!strcmp(hosts[i], "!")) { new = malloc(XHostsBaseSize); if (!new) goto memerr; new->mbits = -1; allow = !allow; } else { short mbits = -1; short mode = 0; struct sockaddr_storage ss; struct sockaddr *sa = (struct sockaddr*)&ss; socklen_t salen = sizeof(ss); strcpy(xhost, hosts[i]); p = strchr(xhost, '/'); if (p) { XHosts ext; *p++ = '\0'; ext.mbits = mbits; ext.mode = mode; ext.xhost.addr.sa_family = family; mkXhostsExt(xhost, p, &ext); mbits = ext.mbits; mode = ext.mode; family = ext.xhost.addr.sa_family; } sa->sa_family = family; if (!host2sa(xhost, NULL, sa, &salen, NULL, NULL, 0)) exit(1); new = malloc(XHostsBaseSize+salen); if (!new) goto memerr; new->xhost.len = salen; bcopy(sa, &new->xhost.addr, salen); if (mbits < 0) { if (sa->sa_family == AF_INET) { mbits = 32; #ifdef AF_INET6 } else if (sa->sa_family == AF_INET6) { mbits = 128; #endif } else { message(LOG_ERR, "mkXhosts: unknown family=%d", sa->sa_family); exit(1); } } new->mbits = mbits; new->mode = mode; if (mesg) { char str[STRMAX+1]; int pos = 0; addr2str(&new->xhost.addr, new->xhost.len, str, STRMAX, NI_NUMERICHOST); pos = strlen(str); snprintf(str+pos, STRMAX-pos, "/%d", new->mbits); pos += strlen(str+pos); message(LOG_DEBUG, "%s%s is %s", mesg, str, (allow ? "permitted" : "denied")); } } new->next = NULL; if (!top) top = new; if (bot) bot->next = new; bot = new; } return top; memerr: message(LOG_CRIT, "Out of memory"); exit(1); } int mkPortXhosts(int argc, int i, char *argv[]) { PortXHosts *pxh; XPorts *top = NULL; XPorts *bot = NULL; char **hosts; char *p, *q; char str[STRMAX+1]; int isnum; int from; int j; i++; if (!strcmp(argv[i], "--")) { portXHosts = NULL; return i; } p = argv[i]; q = str; isnum = 1; from = -1; for (;;) { if (*p == ',' || *p == '-' || *p == '\0') { int port; *q = '\0'; if (str[0]) { if (isnum) port = atoi(str); else { struct sockaddr_storage ss; struct sockaddr *sa = (struct sockaddr*)&ss; socklen_t salen = sizeof(ss); if (!host2sa(NULL, str, sa, &salen, NULL, NULL, 0)) { goto opterr; } port = getport(sa); } } else { opterr: message(LOG_ERR, "Illegal option: -x requires port list: %s", argv[i]); exit(1); } if (*p == '-') { from = port; } else { XPorts *new = malloc(sizeof(XPorts)); if (!new) goto memerr; new->next = NULL; if (from >= 0) new->from = from; else new->from = port; new->end = port; from = -1; if (bot) bot->next = new; bot = new; if (!top) top = new; if (*p == '\0') break; } p++; q = str; isnum = 1; continue; } else if (!isdigit(*p)) { isnum = 0; } *q++ = *p++; } if (Debug > 5) { char buf[BUFMAX]; XPorts *cur; j = 0; for (cur=top; j < BUFMAX && cur; cur=cur->next) { if (j > 0) buf[j++] = ','; snprintf(buf+j, BUFMAX-1-j, "%d-%d", cur->from, cur->end); j += strlen(buf+j); } buf[j] = '\0'; message(LOG_DEBUG, "XPorts: %s", buf); } i++; hosts = &argv[i]; j = 0; for (; i < argc; i++, j++) if (!strcmp(argv[i], "--")) break; pxh = malloc(sizeof(PortXHosts)); if (!pxh) goto memerr; pxh->ports = top; if (Debug > 5) p = "XHosts: "; else p = NULL; pxh->xhosts = mkXhosts(j, hosts, AF_UNSPEC, p); pxh->next = portXHosts; portXHosts = pxh; return i; memerr: message(LOG_CRIT, "Out of memory"); exit(1); } Stone *getStone(struct sockaddr *sa, socklen_t salen, int proto) { Stone *stone; proto &= proto_udp_s; for (stone=stones; stone != NULL; stone=stone->next) { if ((stone->proto & proto_udp_s) == proto && saComp(&stone->listen->addr, sa)) { return stone; } } return NULL; } /* make stone */ Stone *mkstone( char *dhost, /* destination hostname */ char *dserv, /* destination port */ char *host, /* listening host */ char *serv, /* listening port */ int nhosts, /* # of hosts to permit */ char *hosts[], /* hosts to permit */ int proto) { /* UDP/TCP/SSL */ Stone *stone; Stone *st; struct sockaddr_storage ss; struct sockaddr *sa = (struct sockaddr*)&ss; socklen_t salen = sizeof(ss); int satype; int saproto = 0; sa_family_t family; char *mesg; char str[STRMAX+1]; stone = calloc(1, sizeof(Stone)); if (!stone) { message(LOG_CRIT, "Out of memory"); exit(1); } stone->next = NULL; stone->children = NULL; stone->parent = NULL; stone->common = type_stone; stone->p = NULL; stone->timeout = PairTimeOut; if (proto & proto_udp_s) { satype = SOCK_DGRAM; saproto = IPPROTO_UDP; } else { satype = SOCK_STREAM; saproto = IPPROTO_TCP; } #ifdef AF_LOCAL if (proto & proto_unix_s) { struct sockaddr_un *sun = (struct sockaddr_un*)sa; salen = sizeof(struct sockaddr_un); bzero(sa, salen); sun->sun_family = AF_LOCAL; snprintf(sun->sun_path, sizeof(sun->sun_path)-1, "%s", host); saproto = 0; } else #endif #ifdef AF_INET6 if (proto & proto_v6_s) { struct sockaddr_in6 *sin6p = (struct sockaddr_in6*)sa; sa->sa_family = AF_INET6; if (!host2sa(host, serv, sa, &salen, &satype, &saproto, AI_PASSIVE)) exit(1); stone->port = ntohs(sin6p->sin6_port); } else #endif { struct sockaddr_in *sinp = (struct sockaddr_in*)sa; sa->sa_family = AF_INET; if (!host2sa(host, serv, sa, &salen, &satype, &saproto, AI_PASSIVE)) exit(1); stone->port = ntohs(sinp->sin_port); } if ((proto & proto_command) == command_proxy || (proto & proto_command) == command_health || (proto & proto_command) == command_identd) { stone->ndsts = 1; if ((proto & proto_command) == command_proxy) { stone->dsts = malloc(sizeof(SockAddr*) + sizeof(PortXHosts*)); if (stone->dsts) ((PortXHosts**)stone->dsts)[1] = portXHosts; /* only proxy stone needs portXHosts, so we divert dsts into holding current portXHosts */ } else { stone->dsts = malloc(sizeof(SockAddr*)); /* dummy */ } if (!stone->dsts) goto memerr; stone->dsts[0] = saDup(sa, salen); /* dummy */ #ifdef AF_LOCAL } else if (proto & proto_unix_d) { struct sockaddr_storage dss; struct sockaddr_un *sun = (struct sockaddr_un*)&dss; stone->ndsts = 1; stone->dsts = malloc(sizeof(SockAddr*)); if (!stone->dsts) goto memerr; bzero(sun, sizeof(dss)); sun->sun_family = AF_LOCAL; snprintf(sun->sun_path, sizeof(sun->sun_path)-1, "%s", dhost); stone->dsts[0] = saDup((struct sockaddr*)sun, sizeof(struct sockaddr_un)); if (!stone->dsts[0]) goto memerr; #endif } else { struct sockaddr_storage dss; struct sockaddr *dsa = (struct sockaddr*)&dss; socklen_t dsalen = sizeof(dss); int dsatype; int dsaproto; LBSet *lbset; #ifdef AF_INET6 if (proto & proto_v6_d) dsa->sa_family = AF_INET6; else #endif dsa->sa_family = AF_INET; if (proto & proto_udp_d) { dsatype = SOCK_DGRAM; dsaproto = IPPROTO_UDP; } else { dsatype = SOCK_STREAM; dsaproto = IPPROTO_TCP; } if (!host2sa(dhost, dserv, dsa, &dsalen, &dsatype, &dsaproto, 0)) { exit(1); } lbset = findLBSet(dsa); if (lbset) { stone->ndsts = lbset->ndsts; stone->dsts = lbset->dsts; } else { stone->ndsts = 1; stone->dsts = malloc(sizeof(SockAddr*)); if (!stone->dsts) { memerr: message(LOG_CRIT, "Out of memory"); exit(1); } stone->dsts[0] = saDup(dsa, dsalen); if (!stone->dsts[0]) goto memerr; } } stone->proto = proto; stone->from = ConnectFrom; if (!reusestone(stone)) { /* recycle stone */ stone->sd = socket(sa->sa_family, satype, saproto); if (InvalidSocket(stone->sd)) { #ifdef WINDOWS errno = WSAGetLastError(); #endif message(LOG_ERR, "stone %d: Can't get socket " "family=%d type=%d proto=%d err=%d", stone->sd, sa->sa_family, satype, saproto, errno); exit(1); } #ifdef IPV6_V6ONLY if ((proto & proto_v6_s) && (proto & proto_ip_only_s)) { int i = 1; setsockopt(stone->sd, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&i, sizeof(i)); } #endif if (!(proto & proto_udp_s) && ReuseAddr) { int i = 1; setsockopt(stone->sd, SOL_SOCKET, SO_REUSEADDR, (char*)&i, sizeof(i)); } if ((st=getStone(sa, salen, proto))) { closesocket(stone->sd); stone->parent = st; stone->children = st->children; st->children = stone; stone->sd = st->sd; } else if (!DryRun) { if (bind(stone->sd, sa, salen) < 0) { char str[STRMAX+1]; #ifdef WINDOWS errno = WSAGetLastError(); #endif addrport2str(sa, salen, 0, str, STRMAX, 0); str[STRMAX] = '\0'; message(LOG_ERR, "stone %d: Can't bind %s err=%d", stone->sd, str, errno); exit(1); } if (!(stone->proto & proto_block_s)) { #ifdef WINDOWS u_long param; param = 1; ioctlsocket(stone->sd, FIONBIO, ¶m); #else fcntl(stone->sd, F_SETFL, O_NONBLOCK); #endif } if (stone->port == 0) { salen = sizeof(ss); if (getsockname(stone->sd, sa, &salen) >= 0) { stone->port = getport(sa); } } if (!(proto & proto_udp_s)) { /* TCP */ if (listen(stone->sd, BacklogMax) < 0) { #ifdef WINDOWS errno = WSAGetLastError(); #endif message(LOG_ERR, "stone %d: Can't listen err=%d", stone->sd, errno); exit(1); } } } /* !DryRun */ } stone->listen = saDup(sa, salen); #ifdef USE_SSL if (proto & proto_ssl_s) { /* server side SSL */ exPatOpts(&ServerOpts, host, dhost); stone->ssl_server = mkStoneSSL(&ServerOpts, 1); if (stone->ssl_server->lbmod) { if (stone->ssl_server->lbmod > stone->ndsts) { message(LOG_WARNING, "LB set (%d) < lbmod (%d)", stone->ndsts, stone->ssl_server->lbmod); stone->ssl_server->lbmod = stone->ndsts; } } } else { stone->ssl_server = NULL; } if (proto & proto_ssl_d) { /* client side SSL */ exPatOpts(&ClientOpts, host, dhost); stone->ssl_client = mkStoneSSL(&ClientOpts, 0); if (!(stone->ssl_client->name && *stone->ssl_client->name)) stone->ssl_client->name = dhost; } else { stone->ssl_client = NULL; } #endif mesg = NULL; if (Debug > 1) { mesg = str; if ((proto & proto_command) == command_proxy) { snprintf(mesg, STRMAX, "stone %d: using proxy by ", stone->sd); } else if ((proto & proto_command) == command_health) { snprintf(mesg, STRMAX, "stone %d: health check by ", stone->sd); } else if ((proto & proto_command) == command_identd) { snprintf(mesg, STRMAX, "stone %d: ident query by ", stone->sd); } else { char addrport[STRMAX+1]; addrport2str(&stone->dsts[0]->addr, stone->dsts[0]->len, (stone->proto & proto_stone_d), addrport, STRMAX, 0); addrport[STRMAX] = '\0'; snprintf(mesg, STRMAX, "stone %d: connecting to %s by ", stone->sd, addrport); } } family = AF_INET; #ifdef AF_INET6 if (stone->proto & proto_v6_s) { if (host == NULL && !(stone->proto & proto_ip_only_s)) { family = AF_UNSPEC; } else { family = AF_INET6; } } #endif stone->xhosts = mkXhosts(nhosts, hosts, family, mesg); message(LOG_INFO, "%s", stone2str(stone, str, STRMAX)); stone->backups = NULL; if ((proto & proto_command) != command_proxy && (proto & proto_command) != command_health && (proto & proto_command) != command_identd && (proto & proto_nobackup) == 0) { Backup *bs[LB_MAX]; int found = 0; int i; for (i=0; i < stone->ndsts; i++) { bs[i] = findBackup(&stone->dsts[i]->addr); if (bs[i]) { found = 1; bs[i]->used = 1; } } if (found) { stone->backups = malloc(sizeof(Backup*) * stone->ndsts); if (stone->backups) { for (i=0; i < stone->ndsts; i++) stone->backups[i] = bs[i]; } } } return stone; } /* main */ void help(char *com, char *sub) { message(LOG_INFO, "stone %s http://www.gcd.org/sengoku/stone/", VERSION); message(LOG_INFO, "%s", "Copyright(C)2007 by Hiroaki Sengoku "); #ifdef USE_SSL message(LOG_INFO, "%s", "using " OPENSSL_VERSION_TEXT " http://www.openssl.org/"); #ifdef CRYPTOAPI message(LOG_INFO, "%s", "using cryptoapi.c by Peter 'Luna' Runestig "); #endif #endif if (!sub) { help: fprintf(stderr, "Usage: %s ... [-- ]...\n" "opt: -h opt ; help for more\n" " -h stone ; help for \n" #ifdef USE_SSL " -h ssl ; help for , see -q/-z opt\n" #endif , com); return; } if (!strcmp(sub, "opt")) { fprintf(stderr, "Usage: %s ... [-- ]...\n" "opt: -C ; configuration file\n" #ifdef CPP " -P ; preprocessor for config. file\n" " -Q ; options for preprocessor\n" #endif " -N ; configuration check only\n" " -d ; increase debug level\n" " -p ; packet dump\n" " -n ; numerical address\n" " -u ; # of UDP sessions\n" #ifndef NO_FORK " -f ; # of child processes\n" #endif #ifndef NO_SYSLOG " -l ; use syslog\n" " -ll ; run under daemontools\n" #endif " -L ; write log to \n" " -a ; write accounting to \n" " -i ; write process ID to \n" " -X ; size [byte] of Xfer buffer\n" " -T ; timeout [sec] of TCP sessions\n" " -A ; length of backlog\n" " -r ; reuse socket\n" " -x [,][-]... --\n" " ; permit connecting to :\n" " -s ... --\n" " ; health check script\n" " -b : :\n" " ; check : every sec\n" " ; use :, if check failed\n" " -B :... --\n" " ; load balancing hosts\n" #ifdef ADDRCACHE " -H ; cache addresses used in proxy\n" #endif " -I ; local end of its connections to\n" #ifndef NO_SETUID " -o ; set uid to \n" " -g ; set gid to \n" #endif #ifndef NO_CHROOT " -t ; chroot to \n" #endif #ifdef UNIX_DAEMON " -D ; become UNIX Daemon\n" #endif " -c ; core dump to \n" #ifdef USE_SSL " -q ; SSL client option\n" " -z ; SSL server option\n" " ; `-h ssl' for \n" #endif #ifdef NT_SERVICE " -M install ; install service as \n" " -M remove ; remove service \n" #endif , com); } else if (!strcmp(sub, "stone")) { fprintf(stderr, "Usage: %s ... [-- ]...\n" "stone: : [...]\n" " proxy" #ifdef AF_INET6 "[/[v4only | v6only]]" #endif " [...]\n" " health [...]\n" " identd [...]\n" " :/http " " [...]\n" " :/proxy
[...]\n" " :/mproxy
[...]\n" "port: [/[,]...]\n" "ext: tcp | udp" #ifdef USE_SSL " | ssl" #endif #ifdef AF_INET6 " | v6" #endif #ifdef USE_POP " | apop" #endif " | base | block | nobackup\n" "sport: [:][/[,]...]\n" "exts: tcp | udp" #ifdef USE_SSL " | ssl" #endif #ifdef AF_INET6 " | v6 | v6only" #endif " | http | base | block | ident\n" "xhost: [/[,]...]\n" "ex: <#bits> | p" #ifdef AF_INET6 " | v6" #endif "\n" , com); #ifdef USE_SSL } else if (!strcmp(sub, "ssl")) { fprintf(stderr, "opt: -q ; SSL client option\n" " -z ; SSL server option\n" "SSL: default ; reset to default\n" " verbose ; verbose mode\n" " verify ; require peer's certificate\n" " verify,once ; verify client's certificate only once\n" " verify,ifany ; verify client's certificate if any\n" " verify,none ; don't require peer's certificate\n" " crl_check ; lookup CRLs\n" " crl_check_all ; lookup CRLs for whole chain\n" " uniq ; check serial # of peer's certificate\n" " re= ; verify depth with \n" " depth= ; set verification depth to \n" #ifndef OPENSSL_NO_TLS1 " tls1 ; just use TLSv1\n" #endif #ifndef OPENSSL_NO_SSL3 " ssl3 ; just use SSLv3\n" #endif #ifndef OPENSSL_NO_SSL2 " ssl2 ; just use SSLv2\n" #endif " no_tls1 ; turn off TLSv1\n" " no_ssl3 ; turn off SSLv3\n" " no_ssl2 ; turn off SSLv2\n" #ifndef OPENSSL_NO_TLSEXT " sni ; Server Name Indication\n" " servername= ; Server Name\n" #endif " bugs ; SSL implementation bug workarounds\n" #ifdef SSL_OP_CIPHER_SERVER_PREFERENCE " serverpref ; use server's cipher preferences (SSLv2)\n" #endif " shutdown= ; accurate, nowait, unclean\n" " sid_ctx= ; set session ID context\n" " passfile= ; password file\n" " passfilepat= ; password file pattern\n" " key= ; key file\n" " keypat= ; key file pattern\n" " cert= ; certificate file\n" " certpat= ; certificate file pattern\n" " certkey= ; certificate & key file\n" " certkeypat= ; certificate & key file pattern\n" " CAfile= ; certificate file of CA\n" " CApath= ; dir of CAs\n" " pfx= ; PKCS#12 file\n" " pfxpat= ; PKCS#12 file pattern\n" #ifdef CRYPTOAPI " store= ; \"SUBJ:\" or \"THUMB:\"\n" " storeCA ; use CA cert in Windows cert store\n" #endif " cipher= ; list of ciphers\n" " lb= ; load balancing based on CN\n" ); #endif } else { goto help; } } static void skipcomment(FILE *fp) { int c; while ((c=getc(fp)) != EOF && c != '\r' && c != '\n') ; while ((c=getc(fp)) != EOF && (c == '\r' || c == '\n')) ; if (c != EOF) ungetc(c, fp); } static int getvar(FILE *fp, char *buf, int bufmax) { char var[STRMAX+1]; char *val; int i = 0; int paren = 0; int c = getc(fp); if (c == EOF) { return 0; } else if (c == '{') { paren = 1; } else { ungetc(c, fp); } while ((c=getc(fp)) != EOF && i < STRMAX) { if (paren && c == '}') { break; } else if (isalnum(c) || c == '_') { var[i++] = c; } else { ungetc(c, fp); break; } } var[i] = '\0'; if (*var == '\0') return 0; val = getenv(var); if (val == NULL) return 0; i = strlen(val); if (i > bufmax) i = bufmax; strncpy(buf, val, i); return i; } static int gettoken(FILE *fp, char *buf) { int i = 0; int quote = 0; int c; for (;;) { c = getc(fp); if (c == EOF) return -1; if (c == '#') { skipcomment(fp); continue; } if (!isspace(c)) { ungetc(c, fp); break; } } while (i < BUFMAX-1) { c = getc(fp); if (c == EOF) { if (i > 0) break; return -1; } if (quote != '\'') { if (c == '$') { i += getvar(fp, &buf[i], BUFMAX-1-i); continue; } if (c == '\\') { /* escape a char */ c = getc(fp); if (c == EOF) break; switch(c) { case 'n': c = '\n'; break; case 'r': c = '\r'; break; case 't': c = '\t'; break; } } } if (quote) { if (c == quote) { quote = 0; continue; } } else if (c == '\'' || c == '\"') { quote = c; continue; } else if (isspace(c)) { c = getc(fp); if (c != ':' && c != '=') { ungetc(c, fp); break; } } else if (c == '#') { skipcomment(fp); continue; } buf[i++] = c; } buf[i] = '\0'; return i; } FILE *openconfig(void) { #ifdef CPP int pfd[2]; char host[MAXHOSTNAMELEN]; if (CppCommand != NULL && *CppCommand != '\0') { if (gethostname(host, MAXHOSTNAMELEN-1) < 0) { message(LOG_ERR, "gethostname err=%d", errno); exit(1); } if (pipe(pfd) < 0) { message(LOG_ERR, "Can't get pipe err=%d", errno); exit(1); } if (!fork()) { char *argv[BUFMAX/2]; int i = 0; char buf[BUFMAX]; int len = 0; char *p; if (CppOptions) { snprintf(buf, BUFMAX-1, "%s %s", CppCommand, CppOptions); } else { strncpy(buf, CppCommand, BUFMAX-1); } argv[i] = "cpp"; while (buf[len]) { if (isspace(buf[len])) { buf[len++] = '\0'; while (buf[len] && isspace(buf[len])) len++; if (buf[len]) argv[++i] = &buf[len]; else break; } len++; } len++; argv[++i] = buf + len; snprintf(argv[i], BUFMAX-len, "-DHOST=%s", host); len += strlen(argv[i]) + 1; argv[++i] = buf + len; for (p=host; *p; p++) if (*p == '.') *p = '_'; snprintf(argv[i], BUFMAX-len, "-DHOST_%s", host); len += strlen(argv[i]) + 1; if (getenv("HOME")) { argv[++i] = buf + len; snprintf(argv[i], BUFMAX-len, "-DHOME=%s", getenv("HOME")); len += strlen(argv[i]) + 1; } argv[++i] = ConfigFile; argv[++i] = NULL; close(pfd[0]); close(1); dup(pfd[1]); close(pfd[1]); if (Debug > 9) { char str[BUFMAX]; snprintf(str, BUFMAX, "%s: ", buf); for (i=0; argv[i]; i++) { len = strlen(str); snprintf(&str[len], BUFMAX-len, " %s", argv[i]); } message(LOG_DEBUG, "%s", str); } execv(buf, argv); } close(pfd[1]); return fdopen(pfd[0], "r"); } else #endif return fopen(ConfigFile, "r"); } void getconfig(void) { FILE *fp; int nptr = 0; char **new; char buf[BUFMAX]; int len; if (ConfigFile == NULL) return; ConfigArgc = 0; ConfigArgv = NULL; fp = openconfig(); if (fp == NULL) { message(LOG_ERR, "Can't open config file: %s err=%d", ConfigFile, errno); exit(1); } strcpy(buf, ConfigFile); len = strlen(buf); do { if (Debug > 9) message(LOG_DEBUG, "token: \"%s\"", buf); if (ConfigArgc >= nptr) { /* allocate new ptrs */ new = malloc((nptr+BUFMAX)*sizeof(*ConfigArgv)); if (new == NULL) { message(LOG_CRIT, "Out of memory"); exit(1); } if (ConfigArgv) { bcopy(ConfigArgv, new, nptr*sizeof(*ConfigArgv)); free(ConfigArgv); } ConfigArgv = new; nptr += BUFMAX; } ConfigArgv[ConfigArgc] = malloc(len+1); bcopy(buf, ConfigArgv[ConfigArgc], len+1); ConfigArgc++; } while ((len=gettoken(fp, buf)) >= 0); fclose(fp); #ifdef CPP if (CppCommand != NULL && *CppCommand != '\0') { wait(NULL); } #endif } int getdist( /* return pos where serv begins */ char *p, int *protop) { char *port_str, *proto_str, *top; top = p; port_str = proto_str = NULL; *protop = 0; /* default */ #ifdef AF_LOCAL if (p[0] == '.' || p[0] == '/') { struct stat st; p++; while (*p) { if (*p == '/') proto_str = ++p; else p++; } if (proto_str) { *(proto_str-1) = '\0'; if (stat(top, &st) >=0 && S_ISDIR(st.st_mode)) { *(proto_str-1) = '/'; /* restore */ proto_str = NULL; } } *protop |= proto_unix; } else #endif while (*p) { if (*p == ':') port_str = ++p; else if (*p == '/') proto_str = ++p; else p++; } if (proto_str) { *(proto_str-1) = '\0'; p = proto_str; do { if (!strncmp(p, "tcp", 3)) { p += 3; *protop &= ~proto_udp; } else if (!strncmp(p, "udp", 3)) { p += 3; *protop |= proto_udp; } else if (!strncmp(p, "http", 4)) { p += 4; *protop |= proto_ohttp; } else if (!strncmp(p, "base", 4)) { p += 4; *protop |= proto_base; } else if (!strncmp(p, "ident", 5)) { p += 5; *protop |= proto_ident; } else if (!strncmp(p, "proxy", 5)) { p += 5; *protop &= ~proto_command; *protop |= command_ihead; } else if (!strncmp(p, "mproxy", 6)) { p += 6; *protop &= ~proto_command; *protop |= command_iheads; } else if (!strncmp(p, "nobackup", 8)) { p += 8; *protop |= proto_nobackup; #ifdef USE_SSL } else if (!strncmp(p, "ssl", 3)) { p += 3; *protop |= proto_ssl; #endif #ifdef AF_INET6 } else if (!strncmp(p, "v6", 2)) { p += 2; *protop |= proto_v6; if (!strncmp(p, "only", 4)) { p += 4; *protop |= proto_ip_only; } #endif } else if (!strncmp(p, "v4only", 6)) { p += 6; *protop |= proto_ip_only; } else if (!strncmp(p, "block", 5)) { p += 5; *protop |= proto_block; #ifdef USE_POP } else if (!strncmp(p, "apop", 4)) { p += 4; *protop &= ~proto_command; *protop |= command_pop; #endif } else return -1; /* error */ } while ((*p == ',' || *p == '/') && p++); } if (port_str) { *(port_str-1) = '\0'; return port_str - top; /* host & serv */ } else { #ifdef AF_LOCAL if (*protop & proto_unix) { return 1; } #endif if (!strcmp(top, "proxy")) { *protop &= ~proto_command; *protop |= command_proxy; return 1; /* host only */ } if (!strcmp(top, "health")) { *protop &= ~proto_command; *protop |= command_health; return 1; /* host only */ } if (!strcmp(top, "identd")) { *protop &= ~proto_command; *protop |= command_identd; return 1; /* host only */ } return 0; /* serv only */ } } #ifdef USE_SSL void sslopts_default(SSLOpts *opts, int isserver) { int i; opts->verbose = 0; opts->shutdown_mode = 0; opts->mode = SSL_VERIFY_NONE; opts->depth = DEPTH_MAX - 1; opts->vflags = 0; opts->off = 0; opts->serial = -2; opts->callback = verify_callback; opts->sid_ctx = NULL; opts->useSNI = 0; if (isserver) { char path[BUFMAX]; snprintf(path, BUFMAX-1, "%s/stone.pem", X509_get_default_cert_dir()); opts->keyFile = opts->certFile = strdup(path); opts->keyFilePat = opts->certFilePat = NULL; #if !defined(OPENSSL_NO_SSL2) && !defined(OPENSSL_NO_SSL3) opts->meth = SSLv23_server_method(); #elif !defined(OPENSSL_NO_SSL3) opts->meth = SSLv3_server_method(); #elif !defined(OPENSSL_NO_SSL2) opts->meth = SSLv2_server_method(); #endif } else { opts->keyFile = opts->certFile = NULL; opts->keyFilePat = opts->certFilePat = NULL; #if !defined(OPENSSL_NO_SSL2) && !defined(OPENSSL_NO_SSL3) opts->meth = SSLv23_client_method(); #elif !defined(OPENSSL_NO_SSL3) opts->meth = SSLv3_client_method(); #elif !defined(OPENSSL_NO_SSL2) opts->meth = SSLv2_client_method(); #endif } opts->caFile = opts->caPath = NULL; opts->pfxFile = NULL; opts->pfxFilePat = NULL; opts->passFile = NULL; opts->passFilePat = NULL; opts->passwd = NULL; opts->servername = NULL; opts->certIgnore = 0; #ifdef CRYPTOAPI opts->certStoreCA = 0; opts->certStore = NULL; #endif opts->cipherList = getenv("SSL_CIPHER"); for (i=0; i < DEPTH_MAX; i++) opts->regexp[i] = NULL; opts->lbmod = 0; opts->lbparm = 0xFF; opts->shutdown_mode = 0; } int sslopts(int argc, int argi, char *argv[], SSLOpts *opts, int isserver) { if (!strcmp(argv[argi], "default")) { sslopts_default(opts, isserver); } else if (!strcmp(argv[argi], "verbose")) { opts->verbose++; } else if (!strncmp(argv[argi], "shutdown=", 9)) { if (!strcmp(argv[argi]+9, "nowait")) { opts->shutdown_mode = SSL_RECEIVED_SHUTDOWN; } else if (!strcmp(argv[argi]+9, "accurate")) { opts->shutdown_mode = 0; } else if (!strcmp(argv[argi]+9, "unclean")) { opts->shutdown_mode = (SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN); } } else if (!strncmp(argv[argi], "verify", 6) && (argv[argi][6] == '\0' || argv[argi][6] == ',')) { if (!strcmp(argv[argi]+6, ",none")) { opts->mode = SSL_VERIFY_NONE; } else if (isserver) { opts->mode = (SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT); if (argv[argi][6] == ',') { if (!strcmp(argv[argi]+7, "ifany")) { opts->mode = (SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE); } else if (!strcmp(argv[argi]+7, "once")) { opts->mode |= SSL_VERIFY_CLIENT_ONCE; } } } else if (argv[argi][6] == '\0') { opts->mode = SSL_VERIFY_PEER; } else { goto error; } } else if (!strncmp(argv[argi], "crl_check", 9)) { opts->vflags |= X509_V_FLAG_CRL_CHECK; } else if (!strncmp(argv[argi], "crl_check_all", 13)) { opts->vflags |= (X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL); } else if (!strncmp(argv[argi], "re", 2) && isdigit(argv[argi][2]) && argv[argi][3] == '=') { int depth = atoi(argv[argi]+2); if (0 <= depth && depth < DEPTH_MAX) { opts->regexp[depth] = strdup(argv[argi]+4); } else { goto error; } } else if (!strncmp(argv[argi], "re-", 3) && isdigit(argv[argi][3]) && argv[argi][4] == '=') { int depth = atoi(argv[argi]+3); if (0 < depth && depth <= DEPTH_MAX) { opts->regexp[DEPTH_MAX-depth] = strdup(argv[argi]+5); } else { goto error; } } else if (!strncmp(argv[argi], "depth=", 6)) { opts->depth = atoi(argv[argi]+6); if (opts->depth >= DEPTH_MAX) opts->depth = DEPTH_MAX - 1; else if (opts->depth < 0) opts->depth = 0; } else if (!strcmp(argv[argi], "bugs")) { opts->off |= SSL_OP_ALL; #ifndef OPENSSL_NO_TLS1 } else if (!strcmp(argv[argi], "tls1")) { if (isserver) opts->meth = TLSv1_server_method(); else opts->meth = TLSv1_client_method(); #endif #ifndef OPENSSL_NO_SSL3 } else if (!strcmp(argv[argi], "ssl3")) { if (isserver) opts->meth = SSLv3_server_method(); else opts->meth = SSLv3_client_method(); #endif #ifndef OPENSSL_NO_SSL2 } else if (!strcmp(argv[argi], "ssl2")) { if (isserver) opts->meth = SSLv2_server_method(); else opts->meth = SSLv2_client_method(); #endif } else if (!strcmp(argv[argi], "no_tls1")) { opts->off |= SSL_OP_NO_TLSv1; } else if (!strcmp(argv[argi], "no_ssl3")) { opts->off |= SSL_OP_NO_SSLv3; } else if (!strcmp(argv[argi], "no_ssl2")) { opts->off |= SSL_OP_NO_SSLv2; #ifdef SSL_OP_CIPHER_SERVER_PREFERENCE } else if (!strcmp(argv[argi], "serverpref")) { opts->off |= SSL_OP_CIPHER_SERVER_PREFERENCE; #endif } else if (!strcmp(argv[argi], "uniq")) { opts->serial = -1; } else if (!strncmp(argv[argi], "sid_ctx=", 8)) { opts->sid_ctx = (unsigned char*)strdup(argv[argi]+8); } else if (!strcmp(argv[argi], "sni")) { opts->useSNI = 1; } else if (!strncmp(argv[argi], "servername=", 11)) { opts->servername = strdup(argv[argi]+11); } else if (!strncmp(argv[argi], "key=", 4)) { opts->keyFile = strdup(argv[argi]+4); opts->keyFilePat = NULL; opts->pfxFile = NULL; } else if (!strncmp(argv[argi], "keypat=", 7)) { opts->keyFilePat = strdup(argv[argi]+7); opts->pfxFile = NULL; } else if (!strncmp(argv[argi], "cert=", 5)) { opts->certFile = strdup(argv[argi]+5); opts->certFilePat = NULL; opts->pfxFile = NULL; } else if (!strncmp(argv[argi], "certpat=", 8)) { opts->certFilePat = strdup(argv[argi]+8); opts->pfxFile = NULL; } else if (!strncmp(argv[argi], "certkey=", 8)) { opts->keyFile = opts->certFile = strdup(argv[argi]+8); opts->keyFilePat = opts->certFilePat = NULL; opts->pfxFile = NULL; } else if (!strncmp(argv[argi], "certkeypat=", 11)) { opts->keyFilePat = opts->certFilePat = strdup(argv[argi]+11); opts->pfxFile = NULL; } else if (!strncmp(argv[argi], "CAfile=", 7)) { opts->caFile = strdup(argv[argi]+7); } else if (!strncmp(argv[argi], "CApath=", 7)) { opts->caPath = strdup(argv[argi]+7); } else if (!strncmp(argv[argi], "pfx=", 4)) { opts->pfxFile = strdup(argv[argi]+4); opts->pfxFilePat = NULL; opts->keyFile = opts->certFile = NULL; opts->keyFilePat = opts->certFilePat = NULL; } else if (!strncmp(argv[argi], "pfxpat=", 7)) { opts->pfxFilePat = strdup(argv[argi]+7); opts->keyFile = opts->certFile = NULL; opts->keyFilePat = opts->certFilePat = NULL; } else if (!strncmp(argv[argi], "passfile=", 9)) { opts->passFile = strdup(argv[argi]+9); opts->passFilePat = NULL; } else if (!strncmp(argv[argi], "passfilepat=", 12)) { opts->passFilePat = strdup(argv[argi]+12); opts->passFile = NULL; } else if (!strncmp(argv[argi], "ignore", 6)) { opts->certIgnore = 1; #ifdef CRYPTOAPI } else if (!strncmp(argv[argi], "storeCA", 7)) { opts->certStoreCA = 1; } else if (!strncmp(argv[argi], "store=", 6)) { opts->certStore = strdup(argv[argi]+6); #endif } else if (!strncmp(argv[argi], "cipher=", 7)) { opts->cipherList = strdup(argv[argi]+7); } else if (!strncmp(argv[argi], "lb", 2) && isdigit(argv[argi][2]) && argv[argi][3] == '=') { opts->lbparm = argv[argi][2] - '0'; opts->lbmod = atoi(argv[argi]+4); } else { error: message(LOG_ERR, "Invalid SSL Option: %s", argv[argi]); help(argv[0], "ssl"); exit(1); } return argi; } #ifndef NO_THREAD /* SSL callback */ unsigned long sslthread_id_callback(void) { unsigned long ret; #ifdef WINDOWS ret = (unsigned long)GetCurrentThreadId(); #else #ifdef PTHREAD ret = (unsigned long)pthread_self(); #endif #endif if (Debug > 19) message(LOG_DEBUG, "SSL_thread id=%ld", ret); return ret; } void sslthread_lock_callback(int mode, int n, const char *file, int line) { if (mode & CRYPTO_LOCK) { if (Debug > 19) message(LOG_DEBUG, "SSL_lock mode=%x n=%d file=%s line=%d", mode, n, file, line); #ifdef WINDOWS WaitForSingleObject(SSLMutex[n], 500); #else #ifdef PTHREAD pthread_mutex_lock(&SSLMutex[n]); #endif #endif } else { if (Debug > 19) message(LOG_DEBUG, "SSL_unlock mode=%x n=%d file=%s line=%d", mode, n, file, line); #ifdef WINDOWS ReleaseMutex(SSLMutex[n]); #else #ifdef PTHREAD pthread_mutex_unlock(&SSLMutex[n]); #endif #endif } } int sslthread_initialize(void) { int i; NSSLMutexs = CRYPTO_num_locks(); SSLMutex = malloc(NSSLMutexs * sizeof(*SSLMutex)); if (!SSLMutex) return -1; if (Debug > 1) message(LOG_DEBUG, "SSL thread nlocks=%d", NSSLMutexs); for (i=0; i < NSSLMutexs; i++) { #ifdef WINDOWS SSLMutex[i] = CreateMutex(NULL, FALSE, NULL); if (!SSLMutex[i]) return -1; #else #ifdef PTHREAD pthread_mutex_init(&SSLMutex[i], NULL); #endif #endif } #if defined(WINDOWS) || defined(PTHREAD) CRYPTO_set_id_callback(sslthread_id_callback); CRYPTO_set_locking_callback(sslthread_lock_callback); return 1; #else return 0; #endif } #endif #endif int dohyphen(char opt, int argc, char *argv[], int argi) { switch(opt) { case 'd': Debug++; break; case 'p': XHostsTrue->mode = ((XHostsTrue->mode & ~XHostsMode_Dump) | (((XHostsTrue->mode & XHostsMode_Dump) + 1) & XHostsMode_Dump)); break; #ifndef NO_SYSLOG case 'l': Syslog++; break; #endif case 'L': if (++argi >= argc) { message(LOG_ERR, "option -%c requires log ", opt); exit(1); } if (DryRun) break; if (!strcmp(argv[argi], "-")) { LogFp = stdout; } else { if (LogFp && LogFp != stderr) fclose(LogFp); LogFp = fopen(argv[argi], "a"); if (LogFp == NULL) { LogFp = stderr; message(LOG_ERR, "Can't create log file: %s err=%d", argv[argi], errno); exit(1); } LogFileName = strdup(argv[argi]); } setbuf(LogFp, NULL); break; case 'a': if (++argi >= argc) { message(LOG_ERR, "option -%c requires accounting ", opt); exit(1); } if (DryRun) break; if (!strcmp(argv[argi], "-")) { AccFp = stdout; } else { if (AccFp && AccFp != stdout) fclose(AccFp); AccFp = fopen(argv[argi], "a"); if (AccFp == NULL) { message(LOG_ERR, "Can't create account log file: %s err=%d", argv[argi], errno); exit(1); } AccFileName = strdup(argv[argi]); } setbuf(AccFp, NULL); break; case 'i': if (++argi >= argc) { message(LOG_ERR, "option -%c requires pid ", opt); exit(1); } PidFile = strdup(argv[argi]); break; #ifndef NO_CHROOT case 't': if (++argi >= argc) { message(LOG_ERR, "option -%c requires ", opt); exit(1); } RootDir = strdup(argv[argi]); break; #endif case 'n': AddrFlag = 1; break; case 'u': if (++argi >= argc) { message(LOG_ERR, "option -%c requires # of UDP sessions", opt); exit(1); } OriginMax = atoi(argv[argi]); break; case 'X': if (++argi >= argc) { message(LOG_ERR, "option -%c requires size of Xfer buffer ", opt); exit(1); } XferBufMax = atoi(argv[argi]); break; case 'T': if (++argi >= argc) { message(LOG_ERR, "option -%c requires timeout ", opt); exit(1); } PairTimeOut = atoi(argv[argi]); break; case 'A': if (++argi >= argc) { message(LOG_ERR, "option -%c requires length of backlog ", opt); exit(1); } BacklogMax = atoi(argv[argi]); break; #ifndef NO_SETUID case 'o': if (++argi >= argc) { message(LOG_ERR, "option -%c requires ", opt); exit(1); } SetUID = atoi(argv[argi]); break; case 'g': if (++argi >= argc) { message(LOG_ERR, "option -%c requires ", opt); exit(1); } SetGID = atoi(argv[argi]); break; #endif case 'c': if (++argi >= argc) { message(LOG_ERR, "option -%c requires for core dump", opt); exit(1); } CoreDumpDir = strdup(argv[argi]); break; #ifndef NO_FORK case 'f': if (++argi >= argc) { message(LOG_ERR, "option -%c requires # of child processes ", opt); exit(1); } NForks = atoi(argv[argi]); break; #endif #ifdef UNIX_DAEMON case 'D': DaemonMode = 1; break; #endif case 'r': ReuseAddr = 1; break; case 'x': argi = mkPortXhosts(argc, argi, argv); break; case 's': argi = mkChat(argc, argi, argv); break; case 'b': argi = mkBackup(argc, argi, argv); break; case 'B': argi = lbsopts(argc, argi, argv); break; #ifdef ADDRCACHE case 'H': if (++argi >= argc) { message(LOG_ERR, "option -%c requires addr cache size ", opt); exit(1); } AddrCacheSize = atoi(argv[argi]); break; #endif case 'I': if (++argi >= argc) { message(LOG_ERR, "option -%c requires local interface ", opt); exit(1); } if (!argv[argi] || argv[argi][0] == '\0') { ConnectFrom = NULL; } else { char host[STRMAX+1]; char port[STRMAX+1]; struct sockaddr_storage ss; struct sockaddr *sa = (struct sockaddr*)&ss; socklen_t salen = sizeof(ss); int pos = hostPortExt(argv[argi], host, port); if (pos < 0) { sa->sa_family = AF_UNSPEC; if (!host2sa(argv[argi], NULL, sa, &salen, NULL, NULL, 0)) { return -1; } } else { sa->sa_family = AF_UNSPEC; #ifdef AF_INET6 if (pos && !strcmp(argv[argi]+pos, "v6")) sa->sa_family = AF_INET6; #endif if (!host2sa(host, port, sa, &salen, NULL, NULL, 0)) { return -1; } } ConnectFrom = saDup(sa, salen); if (!ConnectFrom) { message(LOG_CRIT, "Out of memory"); exit(1); } } break; #ifdef USE_SSL case 'q': if (++argi >= argc) { message(LOG_ERR, "Illegal Option: -q without "); exit(1); } argi = sslopts(argc, argi, argv, &ClientOpts, 0); break; case 'z': if (++argi >= argc) { message(LOG_ERR, "Illegal Option: -z without "); exit(1); } argi = sslopts(argc, argi, argv, &ServerOpts, 1); break; #endif #ifdef CPP case 'P': if (++argi >= argc) { message(LOG_ERR, "option -%c requires preprocessor ", opt); exit(1); } CppCommand = strdup(argv[argi]); break; case 'Q': if (++argi >= argc) { message(LOG_ERR, "option -%c requires for preprocessor", opt); exit(1); } CppOptions = strdup(argv[argi]); break; #endif default: return -1; } return argi; } #ifdef NT_SERVICE int quoteToken(char *dst, char *src) { char buf[STRMAX+1]; int len; if (strchr(src, ' ')) { snprintf(buf, STRMAX, "\"%s\"", src); len = strlen(buf); if (dst) strncpy(dst, buf, len); } else { len = strlen(src); if (dst) strncpy(dst, src, len); } return len; } void installService(int argc, char *argv[]) { SC_HANDLE scManager; SC_HANDLE scService; char exeName[STRMAX+1]; char *command; int commax, len; int i; int state; char *p; if (!GetModuleFileName(0, exeName, sizeof(exeName))) { message(LOG_ERR, "Can't determine exe name err=%d", (int)GetLastError()); exit(1); } scManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); if (!scManager) { message(LOG_ERR, "Can't open service control manager err=%d", (int)GetLastError()); exit(1); } len = strlen(exeName); for (i=1; i < argc; i++) { len += 1 + quoteToken(NULL, argv[i]); } commax = len; len++; /* for '\0' */ command = malloc(len); if (!command) { message(LOG_CRIT, "Out of memory"); exit(1); } strcpy(command, exeName); len = strlen(command); state = 0; for (i=1; i < argc; i++) { p = argv[i]; switch(state) { case 0: if (!strcmp(p, "-M")) state++; break; case 1: if (!strcmp(p, "install")) p = "run_svc"; /* assume same length */ break; } command[len++] = ' '; len += quoteToken(command+len, p); } command[len] = '\0'; if (Debug > 1) message(LOG_DEBUG, "install: %s", command); scService = CreateService(scManager, NTServiceName, NTServiceDisplayName, SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, command, NULL, NULL, "TcpIp\0\0", NULL, NULL); if (!scService) { message(LOG_ERR, "Can't install service: %s err=%d", NTServiceName, (int)GetLastError()); CloseServiceHandle(scManager); exit(1); } message(LOG_INFO, "service installed: %s", NTServiceName); CloseServiceHandle(scService); CloseServiceHandle(scManager); } void removeService(void) { SC_HANDLE scManager; SC_HANDLE scService; scManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); if (!scManager) { message(LOG_ERR, "Can't open service control manager err=%d", (int)GetLastError()); exit(1); } scService = OpenService(scManager, NTServiceName, SERVICE_ALL_ACCESS); if (!scService) { message(LOG_ERR, "Can't open service: %s err=%d", NTServiceName, (int)GetLastError()); CloseServiceHandle(scManager); exit(1); } if (ControlService(scService, SERVICE_CONTROL_STOP, &NTServiceStatus)) { do { usleep(1000); } while (QueryServiceStatus(scService, &NTServiceStatus), NTServiceStatus.dwCurrentState == SERVICE_STOP_PENDING); if (NTServiceStatus.dwCurrentState == SERVICE_STOPPED) { message(LOG_INFO, "%s stopped", NTServiceName); } else { message(LOG_ERR, "failed to stop %s", NTServiceName); } } if (!DeleteService(scService)) { message(LOG_ERR, "failed to remove service: %s err=%d", NTServiceName, (int)GetLastError()); CloseServiceHandle(scService); CloseServiceHandle(scManager); exit(1); } CloseServiceHandle(scService); CloseServiceHandle(scManager); message(LOG_INFO, "service removed: %s", NTServiceName); } void addEventSource(char *name) { HKEY hk; char key[LONGSTRMAX+1]; char exeName[STRMAX+1]; DWORD data; snprintf(key, LONGSTRMAX, "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\%s", name); if (RegCreateKey(HKEY_LOCAL_MACHINE, key, &hk)) return; if (!GetModuleFileName(0, exeName, sizeof(exeName))) return; if (RegSetValueEx(hk, "EventMessageFile", 0, REG_EXPAND_SZ, (BYTE*)exeName, strlen(exeName)+1)) return; data = (EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE); if (RegSetValueEx(hk, "TypesSupported", 0, REG_DWORD, (LPBYTE)&data, sizeof(DWORD))) return; RegCloseKey(hk); } #endif int doopts(int argc, char *argv[]) { int i; char *p; for (i=1; i < argc; i++) { p = argv[i]; if (*p == '-') { p++; while(*p) { int ret = dohyphen(*p, argc, argv, i); if (ret >= 0) { i = ret; } else switch(*p) { case '-': /* end of global options */ return i+1; case 'h': help(argv[0], argv[i+1]); exit(1); break; case 'N': DryRun = 1; break; #ifdef NT_SERVICE case 'M': i++; if (i+1 >= argc) { message(LOG_ERR, "Illegal Option: -M without args"); exit(1); } NTServiceName = strdup(argv[i+1]); NTServiceDisplayName = malloc(strlen(NTServiceName) + strlen(NTServiceDisplayPrefix) + 1); if (!NTServiceDisplayName) { message(LOG_CRIT, "Out of memory"); exit(1); } strcpy(NTServiceDisplayName, NTServiceDisplayPrefix); strcat(NTServiceDisplayName, NTServiceName); if (!strcmp(argv[i], "install")) { installService(argc, argv); exit(0); } else if (!strcmp(argv[i], "remove")) { removeService(); exit(0); } else if (!strcmp(argv[i], "run_svc")) { addEventSource(NTServiceName); NTServiceLog = RegisterEventSource(NULL, NTServiceName); } else { message(LOG_ERR, "Illegal Option: -M %s %s", argv[i], argv[i+1]); exit(1); } i++; break; #endif case 'C': if (!ConfigFile) { i++; ConfigFile = malloc(strlen(argv[i]) + 1); if (ConfigFile == NULL) { message(LOG_CRIT, "Out of memory"); exit(1); } strcpy(ConfigFile, argv[i]); break; } /* drop through */ default: message(LOG_ERR, "Invalid Option: %s", argv[i]); help(argv[0], "opt"); exit(1); } p++; } } else break; } return i; } void doargs(int argc, int i, char *argv[]) { Stone *stone; char *host, *shost; char *serv, *sserv; int proto, sproto, dproto; char *p; int j, k; proto = sproto = dproto = 0; /* default: TCP */ if (argc - i < 1) { help(argv[0], NULL); exit(1); } for (; i < argc; i++) { p = argv[i]; if (*p == '-') { p++; while(*p) { int ret = dohyphen(*p, argc, argv, i); if (ret >= 0) { i = ret; } else { message(LOG_ERR, "Invalid Option: %s", argv[i]); help(argv[0], "opt"); exit(1); } p++; } continue; } host = strdup(argv[i]); j = getdist(host, &dproto); if (j > 0) { /* with hostname */ i++; if (j > 1) serv = host + j; else serv = NULL; if (argc <= i) { help(argv[0], NULL); exit(1); } shost = strdup(argv[i]); j = getdist(shost, &sproto); if (j > 0) { if (j > 1) sserv = shost + j; else sserv = NULL; } else if (j == 0) { sserv = shost; shost = NULL; } else { message(LOG_ERR, "Invalid : %s", argv[i]); exit(1); } } else { message(LOG_ERR, "Invalid :: %s", argv[i]); exit(1); } i++; j = 0; k = i; for (; i < argc; i++, j++) if (!strcmp(argv[i], "--")) break; if ((sproto & proto_udp)) { proto |= proto_udp_s; if (sproto & proto_v6) proto |= proto_v6_s; if (sproto & proto_ip_only) proto |= proto_ip_only_s; } else { if (sproto & proto_ohttp) proto |= proto_ohttp_s; if (sproto & proto_ssl) proto |= proto_ssl_s; if (sproto & proto_v6) proto |= proto_v6_s; if (sproto & proto_ip_only) proto |= proto_ip_only_s; if (sproto & proto_unix) proto |= proto_unix_s; if (sproto & proto_block) proto |= proto_block_s; if (sproto & proto_base) proto |= proto_base_s; if (sproto & proto_ident) proto |= proto_ident; } if ((dproto & proto_udp)) { proto |= proto_udp_d; if (dproto & proto_v6) proto |= proto_v6_d; if (dproto & proto_ip_only) proto |= proto_ip_only_d; } else { if ((dproto & proto_command) == command_proxy) { proto &= ~proto_command; proto |= command_proxy; #ifdef USE_POP } else if ((dproto & proto_command) == command_pop) { proto &= ~proto_command; proto |= command_pop; #endif } else if (dproto & proto_ohttp) { proto |= proto_ohttp_d; goto extra_arg; } else if ((dproto & proto_command) == command_ihead) { proto &= ~proto_command; proto |= command_ihead; extra_arg: p = argv[k++]; j--; if (k > argc || j < 0) { help(argv[0], NULL); exit(1); } } else if ((dproto & proto_command) == command_iheads) { proto &= ~proto_command; proto |= command_iheads; goto extra_arg; } else if ((dproto & proto_command) == command_health) { proto &= ~proto_command; proto |= command_health; } else if ((dproto & proto_command) == command_identd) { proto &= ~proto_command; proto |= command_identd; } if (dproto & proto_ssl) proto |= proto_ssl_d; if (dproto & proto_v6) proto |= proto_v6_d; if (dproto & proto_ip_only) proto |= proto_ip_only_d; if (dproto & proto_unix) proto |= proto_unix_d; if (dproto & proto_block) proto |= proto_block_d; if (dproto & proto_base) proto |= proto_base_d; if (dproto & proto_nobackup) proto |= proto_nobackup; } stone = mkstone(host, serv, shost, sserv, j, &argv[k], proto); if ((proto & proto_udp_s) && (proto & proto_udp_d)) { /* UDP => UDP */ Origin *origin = (Origin*)malloc(sizeof(Origin)); if (origin == NULL) { memerr: message(LOG_CRIT, "Out of memory"); exit(1); } bzero(origin, sizeof(Origin)); origin->stone = stone; origin->common = type_origin; origin->sd = INVALID_SOCKET; origin->from = NULL; origin->next = OriginTop; OriginTop = origin; stone->p = (char*)origin; } else if (proto & proto_ohttp_d) { stone->p = strdup(p); } else if (((proto & proto_command) == command_ihead) || ((proto & proto_command) == command_iheads)) { stone->p = strdup(p); } if (!(proto & proto_udp_s) || !(proto & proto_udp_d)) { stone->pairs = newPair(); if (!stone->pairs) goto memerr; stone->pairs->clock = -1; /* top */ stone->pairs->stone = stone; stone->pairs->next = PairTop; if (PairTop) PairTop->prev = stone->pairs; PairTop = stone->pairs; } if (!stone->parent) { /* stone is parent */ stone->next = stones; stones = stone; } proto = sproto = dproto = 0; /* default: TCP */ } #ifndef USE_EPOLL for (stone=stones; stone != NULL; stone=stone->next) { FdSet(stone->sd, &rin); FdSet(stone->sd, &ein); } #endif } #ifdef FD_SET_BUG void checkFdSetBug(void) { fd_set set; FD_ZERO(&set); FD_SET(0, &set); FD_SET(0, &set); FD_CLR(0, &set); if (FD_ISSET(0, &set)) { if (Debug > 0) message(LOG_DEBUG, "FD_SET bug detected"); FdSetBug = 1; } } #endif #ifndef WINDOWS static void handler(int sig) { int i; switch(sig) { case SIGHUP: if (Debug > 4) message(LOG_DEBUG, "SIGHUP"); #ifndef NO_FORK if (NForks) { /* mother process */ if (ConfigFile && !oldstones) { oldstones = stones; stones = NULL; OldConfigArgc = ConfigArgc; OldConfigArgv = ConfigArgv; Debug = 0; getconfig(); /* reconfigure */ i = doopts(ConfigArgc, ConfigArgv); doargs(ConfigArgc, i, ConfigArgv); for (i=0; i < NForks; i++) { kill(Pid[i], SIGHUP); kill(Pid[i], SIGINT); } } } else { /* child process */ #endif message_pairs(LOG_INFO); message_origins(LOG_INFO); message_conns(LOG_INFO); #ifndef NO_FORK } #endif if (LogFileName) { fclose(LogFp); LogFp = fopen(LogFileName, "a"); if (LogFp == NULL) { LogFp = stderr; message(LOG_ERR, "Can't re-create log file: %s err=%d", LogFileName, errno); exit(1); } setbuf(LogFp, NULL); } if (AccFileName) { fclose(AccFp); AccFp = fopen(AccFileName, "a"); if (AccFp == NULL) { message(LOG_ERR, "Can't re-create account log file: %s err=%d", AccFileName, errno); exit(1); } setbuf(AccFp, NULL); } signal(SIGHUP, handler); break; case SIGTERM: #ifdef IGN_SIGTERM Debug = 0; message(LOG_INFO, "SIGTERM. clear Debug level"); signal(SIGTERM, handler); break; #endif case SIGINT: #ifndef NO_FORK if (NForks) { /* mother process */ message(LOG_INFO, "SIGTERM/INT. killing children and exiting"); for (i=0; i < NForks; i++) kill(Pid[i], sig); } else #endif message(LOG_INFO, "SIGTERM/INT. exiting"); /* child process */ exit(1); case SIGUSR1: Debug++; message(LOG_INFO, "SIGUSR1. increase Debug level to %d", Debug); #ifndef NO_FORK if (NForks) { /* mother process */ for (i=0; i < NForks; i++) kill(Pid[i], sig); } else { #endif message_pairs(LOG_INFO); message_origins(LOG_INFO); message_conns(LOG_INFO); #ifndef NO_FORK } #endif signal(SIGUSR1, handler); break; case SIGUSR2: if (Debug > 0) Debug--; message(LOG_INFO, "SIGUSR2. decrease Debug level to %d", Debug); #ifndef NO_FORK if (NForks) { /* mother process */ for (i=0; i < NForks; i++) kill(Pid[i], sig); } #endif signal(SIGUSR2, handler); break; case SIGPIPE: if (Debug > 0) message(LOG_DEBUG, "SIGPIPE"); signal(SIGPIPE, handler); break; case SIGSEGV: case SIGBUS: case SIGILL: case SIGFPE: if (CoreDumpDir) { message(LOG_ERR, "Signal %d, core dumping to %s", sig, CoreDumpDir); if (chdir(CoreDumpDir) < 0) { message(LOG_ERR, "Can't chdir to %s err=%d", CoreDumpDir, errno); } else { abort(); } } else { message(LOG_ERR, "Signal %d, exiting", sig); } exit(1); break; default: message(LOG_INFO, "signal %d. Debug level: %d", sig, Debug); } } #endif #ifdef UNIX_DAEMON void daemonize(void) { pid_t pid; pid = fork(); if (pid < 0) { message(LOG_ERR, "Can't create daemon err=%d", errno); exit(1); } if (pid > 0) _exit(0); MyPid = getpid(); if (setsid() < 0) message(LOG_WARNING, "Can't create new session err=%d", errno); if (chdir("/") < 0) message(LOG_WARNING, "Can't change directory to / err=%d", errno); umask(0022); if (close(0) != 0) message(LOG_WARNING, "Can't close stdin err=%d", errno); if (close(1) != 0) message(LOG_WARNING, "Can't close stdout err=%d", errno); #ifndef NO_SYSLOG if (Syslog > 1) Syslog = 1; #endif if (!LogFileName) LogFp = NULL; if (close(2) != 0) message(LOG_WARNING, "Can't close stderr err=%d", errno); } #endif void initialize(int argc, char *argv[]) { int i; int j = 0; /* dummy init to suppress warnings */ #ifdef WINDOWS WSADATA WSAData; if (WSAStartup(MAKEWORD(1, 1), &WSAData)) { message(LOG_ERR, "Can't find winsock"); exit(1); } atexit((void(*)(void))WSACleanup); #endif MyPid = getpid(); LogFp = stderr; setbuf(stderr, NULL); #ifdef USE_SSL SSL_library_init(); SSL_load_error_strings(); OpenSSL_add_all_algorithms(); PairIndex = SSL_get_ex_new_index(0, "Pair index", NULL, NULL, NULL); MatchIndex = SSL_SESSION_get_ex_new_index(0, "Match index", newMatch, NULL, freeMatch); RAND_poll(); if (!RAND_status()) { message(LOG_WARNING, "Can't collect enough random seeds"); srand(time(NULL)); do { u_short rnd = (u_short)rand(); RAND_seed(&rnd, sizeof(rnd)); } while (!RAND_status()); } sslopts_default(&ServerOpts, 1); sslopts_default(&ClientOpts, 0); #endif XHostsTrue = malloc(XHostsBaseSize + sizeof(struct sockaddr_storage)); if (!XHostsTrue) { message(LOG_CRIT, "Out of memory"); exit(1); } XHostsTrue->next = NULL; XHostsTrue->mbits = 0; XHostsTrue->mode = 0; XHostsTrue->xhost.len = sizeof(struct sockaddr_storage); bzero(&XHostsTrue->xhost.addr, XHostsTrue->xhost.len); XHostsTrue->xhost.addr.sa_family = AF_UNSPEC; i = doopts(argc, argv); if (ConfigFile) { getconfig(); j = doopts(ConfigArgc, ConfigArgv); } #ifdef UNIX_DAEMON if (DaemonMode) daemonize(); #endif if (!DryRun && PidFile) { FILE *fp = fopen(PidFile, "w"); if (fp) { fprintf(fp, "%d\n", MyPid); fclose(fp); } } #ifndef NO_SYSLOG if (Syslog) { snprintf(SyslogName, STRMAX, "stone[%d]", MyPid); SyslogName[STRMAX] = '\0'; openlog(SyslogName, 0, LOG_DAEMON); if (Syslog > 1) setbuf(stdout, NULL); } #endif message(LOG_INFO, "start (%s) [%d]", VERSION, MyPid); if (Debug > 0) { message(LOG_DEBUG, "Debug level: %d", Debug); } trash.next = NULL; conns.next = NULL; #ifndef USE_EPOLL #ifdef FD_SET_BUG checkFdSetBug(); #endif FD_ZERO(&rin); FD_ZERO(&win); FD_ZERO(&ein); #endif if (ConfigFile && ConfigArgc > j) { if (argc > i) doargs(argc, i, argv); doargs(ConfigArgc, j, ConfigArgv); } else { doargs(argc, i, argv); } #ifndef WINDOWS signal(SIGHUP, handler); signal(SIGTERM, handler); signal(SIGINT, handler); signal(SIGPIPE, handler); signal(SIGUSR1, handler); signal(SIGUSR2, handler); signal(SIGSEGV, handler); signal(SIGBUS, handler); signal(SIGILL, handler); signal(SIGFPE, handler); #endif #ifndef NO_FORK if (!DryRun && NForks) { Pid = malloc(sizeof(pid_t) * NForks); if (!Pid) { message(LOG_CRIT, "Out of memory"); exit(1); } for (i=0; i < NForks; i++) { Pid[i] = fork(); if (!Pid[i]) break; } if (i >= NForks) { /* the mother process */ pid_t id; for (;;) { int status; id = wait(&status); if (id < 0) continue; message(LOG_WARNING, "Process died pid=%d, status=%x", id, status); for (i=0; i < NForks; i++) { if (Pid[i] == id) break; } if (i < NForks) { id = fork(); if (!id) break; /* respawned child */ else Pid[i] = id; } else { message(LOG_ERR, "This can't happen pid=%d", id); } } } free(Pid); /* child process */ Pid = NULL; NForks = 0; MyPid = getpid(); #ifndef NO_SYSLOG if (Syslog) { closelog(); snprintf(SyslogName, STRMAX, "stone[%d]", MyPid); SyslogName[STRMAX] = '\0'; openlog(SyslogName, 0, LOG_DAEMON); } #endif message(LOG_INFO, "child start (%s) [%d]", VERSION, MyPid); } #endif #ifdef PTHREAD pthread_attr_init(&thread_attr); pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED); #endif #ifdef WINDOWS PairMutex = ConnMutex = OrigMutex = AsyncMutex = NULL; if (!(PairMutex=CreateMutex(NULL, FALSE, NULL)) || !(ConnMutex=CreateMutex(NULL, FALSE, NULL)) || !(OrigMutex=CreateMutex(NULL, FALSE, NULL)) || !(AsyncMutex=CreateMutex(NULL, FALSE, NULL)) || #ifndef USE_EPOLL !(FdRinMutex=CreateMutex(NULL, FALSE, NULL)) || !(FdWinMutex=CreateMutex(NULL, FALSE, NULL)) || !(FdEinMutex=CreateMutex(NULL, FALSE, NULL)) || #endif !(ExBufMutex=CreateMutex(NULL, FALSE, NULL)) || !(FPairMutex=CreateMutex(NULL, FALSE, NULL)) || #ifdef ADDRCACHE !(HashMutex=CreateMutex(NULL, FALSE, NULL)) || #endif !(PkBufMutex=CreateMutex(NULL, FALSE, NULL)) ) { message(LOG_ERR, "Can't create Mutex err=%d", (int)GetLastError()); } #endif #ifdef OS2 PairMutex = ConnMutex = OrigMutex = AsyncMutex = NULLHANDLE; if ((j=DosCreateMutexSem(NULL, &PairMutex, 0, FALSE)) || (j=DosCreateMutexSem(NULL, &ConnMutex, 0, FALSE)) || (j=DosCreateMutexSem(NULL, &OrigMutex, 0, FALSE)) || (j=DosCreateMutexSem(NULL, &AsyncMutex, 0, FALSE)) || #ifndef USE_EPOLL (j=DosCreateMutexSem(NULL, &FdRinMutex, 0, FALSE)) || (j=DosCreateMutexSem(NULL, &FdWinMutex, 0, FALSE)) || (j=DosCreateMutexSem(NULL, &FdEinMutex, 0, FALSE)) || #endif (j=DosCreateMutexSem(NULL, &ExBufMutex, 0, FALSE)) || (j=DosCreateMutexSem(NULL, &FPairMutex, 0, FALSE)) || #ifdef ADDRCACHE (j=DosCreateMutexSem(NULL, &HashMutex, 0, FALSE)) || #endif (j=DosCreateMutexSem(NULL, &PkBufMutex, 0, FALSE)) ) { message(LOG_ERR, "Can't create Mutex err=%d", j); } #endif #ifndef NO_THREAD #ifdef USE_SSL if (sslthread_initialize() < 0) { message(LOG_ERR, "Fail to initialize SSL callback"); } #endif #endif #ifndef NO_CHROOT if (RootDir) { char cwd[BUFMAX]; int len = strlen(RootDir); getcwd(cwd, BUFMAX-1); if (strncmp(cwd, RootDir, len) != 0) len = -1; if (chroot(RootDir) < 0) { message(LOG_WARNING, "Can't change root directory to %s", RootDir); } else if (len <= 0) { if (Debug > 0) message(LOG_DEBUG, "cwd=%s is outside chroot=%s, so chdir /", cwd, RootDir); if (chdir("/") < 0) { message(LOG_WARNING, "Can't change directory to chroot / err=%d", errno); } } } #endif #ifndef NO_SETUID if (SetUID || SetGID) { if (AccFileName) fchown(fileno(AccFp), SetUID, SetGID); if (LogFileName) fchown(fileno(LogFp), SetUID, SetGID); } if (SetGID) if (setgid(SetGID) < 0 || setgroups(1, &SetGID) < 0) { message(LOG_WARNING, "Can't set gid err=%d", errno); } if (SetUID) if (setuid(SetUID) < 0) { message(LOG_WARNING, "Can't set uid err=%d", errno); } #endif #ifdef PR_SET_DUMPABLE if (CoreDumpDir && (SetUID || SetGID)) { if (prctl(PR_SET_DUMPABLE, 1) < 0) { message(LOG_ERR, "prctl err=%d", errno); } } #endif if (MinInterval > 0) { if (Debug > 1) message(LOG_DEBUG, "MinInterval: %d", MinInterval); } time(&lastEstablished); lastReadWrite = lastEstablished; #ifdef USE_EPOLL /* ePollFd must be created in each process */ ePollFd = epoll_create(BACKLOG_MAX); if (ePollFd < 0) { message(LOG_CRIT, "Can't create epoll err=%d", errno); exit(1); } else { Stone *stone; for (stone=stones; stone != NULL; stone=stone->next) { struct epoll_event ev; ev.events = (EPOLLIN | EPOLLPRI); ev.data.ptr = stone; if (Debug > 6) message(LOG_DEBUG, "stone %d: epoll_ctl %d ADD %x", stone->sd, ePollFd, (int)ev.data.ptr); if (epoll_ctl(ePollFd, EPOLL_CTL_ADD, stone->sd, &ev) < 0) { message(LOG_CRIT, "stone %d: epoll_ctl %d ADD err=%d", stone->sd, ePollFd, errno); exit(1); } } } #endif } #ifdef NT_SERVICE void scReportStatus(DWORD curState, DWORD exitCode, DWORD hint) { static DWORD checkPoint = 1; if (curState == SERVICE_START_PENDING) NTServiceStatus.dwControlsAccepted = 0; else NTServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP; NTServiceStatus.dwCurrentState = curState; NTServiceStatus.dwWin32ExitCode = exitCode; NTServiceStatus.dwWaitHint = hint; if ((curState == SERVICE_RUNNING) || (curState == SERVICE_STOPPED)) NTServiceStatus.dwCheckPoint = 0; else NTServiceStatus.dwCheckPoint = checkPoint++; SetServiceStatus(NTServiceStatusHandle, &NTServiceStatus); } void WINAPI serviceCtrl(DWORD code) { switch(code) { case SERVICE_CONTROL_STOP: scReportStatus(SERVICE_STOP_PENDING, NO_ERROR, 0); message(LOG_INFO, "Service stopping.."); if (WaitForSingleObject(NTServiceThreadHandle, 1000) == WAIT_TIMEOUT) TerminateThread(NTServiceThreadHandle, 0); break; default: break; } } DWORD WINAPI serviceThread(LPVOID lpParms) { do { repeater(); } while (NTServiceStatus.dwCurrentState == SERVICE_RUNNING); ExitThread(0); return 0; } void WINAPI serviceMain(DWORD argc, LPTSTR *argv) { DWORD thid; NTServiceStatusHandle = RegisterServiceCtrlHandler(NTServiceName, serviceCtrl); if (!NTServiceStatusHandle) { message(LOG_ERR, "Can't register ServiceCtrlHandler"); return; } NTServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; NTServiceStatus.dwServiceSpecificExitCode = 0; scReportStatus(SERVICE_START_PENDING, NO_ERROR, 3000); message(LOG_INFO, "Service started"); scReportStatus(SERVICE_RUNNING, NO_ERROR, 0); NTServiceThreadHandle = CreateThread(0, 0, serviceThread, NULL, 0, &thid); if (NTServiceThreadHandle) { WaitForSingleObject(NTServiceThreadHandle, INFINITE); CloseHandle(NTServiceThreadHandle); } message(LOG_INFO, "Service stopped"); scReportStatus(SERVICE_STOPPED, NO_ERROR, 0); } #endif #ifdef CLEAR_ARGS static void clear_args(int argc, char *argv[]) { char *argend = argv[argc-1] + strlen(argv[argc-1]); char *p; for (p=argv[1]; p < argend; p++) *p = '\0'; /* clear args */ } #endif int main(int argc, char *argv[]) { initialize(argc, argv); if (DryRun) return 0; #ifdef NT_SERVICE if (NTServiceName) { SERVICE_TABLE_ENTRY dispatchTable[] = { { NTServiceName, (LPSERVICE_MAIN_FUNCTION)serviceMain }, { NULL, NULL } }; if (!StartServiceCtrlDispatcher(dispatchTable)) message(LOG_ERR, "StartServiceCtrlDispatcher failed"); return 0; } #endif #ifdef CLEAR_ARGS clear_args(argc, argv); #endif #ifdef MEMLEAK_CHECK mtrace(); #endif for (;;) repeater(); return 0; } /* For Gnu Emacs. Local Variables: tab-width: 8 c-basic-offset: 4 End: */ stone-2.3d-2.3.2.7/Makefile0000644000023200056710000001365110751714360014277 0ustar sengokuklab# stone simple repeater # Copyright(c)1995-2004 by Hiroaki Sengoku # # -DUSE_POP use POP -> APOP conversion # -DUSE_SSL use OpenSSL # -DCPP preprocessor for reading config. file # -DIGN_SIGTERM ignore SIGTERM signal # -DUNIX_DAEMON fork into background and become a UNIX Daemon # -DNO_BCOPY without bcopy(3) # -DNO_SNPRINTF without snprintf(3) # -DNO_SYSLOG without syslog(2) # -DNO_RINDEX without rindex(3) # -DNO_THREAD without thread # -DNO_PID_T without pid_t # -DNO_ADDRINFO without getaddrinfo # -DPTHREAD use Posix Thread # -DPRCTL use prctl(2) - operations on a process # -DOS2 OS/2 with EMX # -DWINDOWS Windows95/98/NT # -DNT_SERVICE WindowsNT/2000 native service CFLAGS= # -g SSL= /usr/local/ssl SSL_FLAGS= -DUSE_SSL SSL_LIBS= -lssl -lcrypto POP_FLAGS= -DUSE_POP POP_LIBS= md5c.o MINGWCC= mingw32-gcc MC= wmc RC= wrc WINDRES= windres SVC_LIBS= logmsg.o all: @echo "run make with one of the following arguments" @echo "linux ; for Linux" @echo "zaurus ; for Linux Zaurus" @echo "fon ; for La Fonera" @echo "bsd ; for FreeBSD or BSD/OS" @echo "macosx ; for Mac OS X" @echo "sun ; for SunOS 4.x with gcc" @echo "solaris ; for Solaris with gcc" @echo "hp ; for HP-UX with gcc" @echo "irix ; for IRIX" @echo "win ; for Windows 95/NT with VC++" @echo "win-svc ; for Windows NT service with VC++" @echo "mingw ; for Windows 95/NT with MinGW" @echo "mingw-svc ; for Windows NT service with MinGW" @echo "emx ; for OS/2 with EMX" @echo "using POP -> APOP conv., add '-pop' (example: linux-pop)" @echo "using above conv. and OpenSSL, add '-ssl' (example: linux-ssl)" clean: rm -f stone $(POP_LIBS) stone.exe stone.obj md5c.obj stone.o $(SVC_LIBS) MSG00001.bin logmsg.h logmsg.rc cryptoapi.o md5c.c: @echo "*** md5c.c is contained in RFC1321" stone: stone.c $(CC) $(CFLAGS) $(FLAGS) -o $@ $? $(LIBS) pop_stone: $(POP_LIBS) $(MAKE) FLAGS="$(POP_FLAGS)" LIBS="$(POP_LIBS)" $(TARGET) ssl_stone: $(MAKE) FLAGS="$(POP_FLAGS) $(SSL_FLAGS) $(FLAGS)" LIBS="$(LIBS) $(SSL_LIBS)" $(TARGET) stone.exe: stone.c $(CC) $(CFLAGS) $(FLAGS) $? $(LIBS) pop_stone.exe: md5c.obj $(MAKE) FLAGS=-DUSE_POP LIBS="md5c.obj" $(TARGET) ssl_stone.exe: $(MAKE) FLAGS="-DUSE_POP -DUSE_SSL" LIBS="ssleay32.lib libeay32.lib" $(TARGET) # $(MAKE) FLAGS=-DUSE_SSL LIBS="ssl32.lib crypt32.lib" $(TARGET) svc_stone.exe: logmsg.res $(MAKE) FLAGS="/DNT_SERVICE $(FLAGS)" LIBS="logmsg.res advapi32.lib user32.lib gdi32.lib shell32.lib kernel32.lib" $(TARGET) logmsg.rc: logmsg.mc $(MC) -i $? logmsg.res: logmsg.rc $(RC) $? logmsg.o: logmsg.res $(WINDRES) $? -o $@ cryptoapi.o: cryptoapi.c $(MINGWCC) -c $? -o $@ svc_stone: logmsg.rc $(SVC_LIBS) $(MAKE) FLAGS="-DNT_SERVICE $(FLAGS)" LIBS="$(LIBS) $(SVC_LIBS) -ladvapi32 -luser32 -lshell32 -lkernel32" $(TARGET) linux: $(MAKE) FLAGS="-O -Wall -DCPP='\"/usr/bin/cpp -traditional\"' -DPTHREAD -DUNIX_DAEMON -DPRCTL -DSO_ORIGINAL_DST=80 -DUSE_EPOLL $(FLAGS)" LIBS="-lpthread $(LIBS)" stone linux-pop: $(MAKE) TARGET=linux pop_stone linux-ssl: $(MAKE) TARGET=linux ssl_stone LIBS="-ldl" zaurus: $(MAKE) CC="arm-linux-gcc" FLAGS="-O -Wall -DPTHREAD -DUNIX_DAEMON $(FLAGS)" LIBS="-lpthread $(LIBS)" stone arm-linux-strip stone zaurus-pop: $(MAKE) CC="arm-linux-gcc" TARGET=zaurus pop_stone zaurus-ssl: $(MAKE) CC="arm-linux-gcc" SSL_LIBS="-lssl -lcrypto" TARGET=zaurus ssl_stone fon: $(MAKE) CC="mips-linux-uclibc-gcc" FLAGS="-O -Wall -DPTHREAD -DUNIX_DAEMON -DPRCTL $(FLAGS)" LIBS="-lpthread $(LIBS)" stone mips-linux-uclibc-strip stone fon-pop: $(MAKE) CC="mips-linux-uclibc-gcc" TARGET=fon pop_stone fon-ssl: $(MAKE) CC="mips-linux-uclibc-gcc" SSL_LIBS="-lssl -lcrypto" TARGET=fon ssl_stone bsd: $(MAKE) FLAGS="-DCPP='\"/usr/bin/cpp -traditional\"' -D_THREAD_SAFE -DPTHREAD -DREG_NOERROR=0 $(FLAGS)" LIBS="-pthread $(LIBS)" stone bsd-pop: $(MAKE) TARGET=bsd pop_stone bsd-ssl: $(MAKE) TARGET=bsd ssl_stone macosx: $(MAKE) FLAGS="-DCPP='\"/usr/bin/cpp -traditional\"' -D_THREAD_SAFE -DPTHREAD $(FLAGS)" stone macosx-pop: $(MAKE) TARGET=macosx pop_stone macosx-ssl: $(MAKE) TARGET=macosx SSL=/usr ssl_stone sun: $(MAKE) CC=gcc FLAGS="-DNO_ADDRINFO -DNO_SNPRINTF -DIGN_SIGTERM -DCPP='\"/usr/lib/cpp\"' $(FLAGS)" stone sun-pop: $(MAKE) TARGET=sun pop_stone sun-ssl: $(MAKE) TARGET=sun ssl_stone solaris: $(MAKE) CC=gcc FLAGS="-DPTHREAD -D_REENTRANT $(FLAGS)" LIBS="-lnsl -lsocket -lpthread -lthread $(LIBS)" stone solaris-pop: $(MAKE) TARGET=solaris pop_stone solaris-ssl: $(MAKE) TARGET=solaris ssl_stone hp: $(MAKE) CC=gcc FLAGS="-DNO_SNPRINTF -DH_ERRNO -DCPP='\"/lib/cpp\"' $(FLAGS)" stone hp-pop: $(MAKE) TARGET=hp pop_stone hp-ssl: $(MAKE) TARGET=hp ssl_stone irix: $(MAKE) FLAGS="-DNO_SNPRINTF $(FLAGS)" stone irix-pop: $(MAKE) TARGET=irix pop_stone irix-ssl: $(MAKE) TARGET=irix ssl_stone win: $(MAKE) FLAGS="/Zi /DWINDOWS /DNO_RINDEX /DNO_SNPRINTF /DNO_VSNPRINTF /DNO_PID_T $(FLAGS)" LIBS="/MT wsock32.lib $(LIBS) /link /NODEFAULTLIB:LIBC" stone.exe win-pop: $(MAKE) TARGET=win pop_stone.exe win-ssl: $(MAKE) TARGET=win ssl_stone.exe win-svc: $(MAKE) TARGET=win svc_stone.exe mingw.exe: stone.c $(MINGWCC) $(CFLAGS) $(FLAGS) -o stone.exe $? $(LIBS) mingw: $(MAKE) CC="$(MINGWCC)" FLAGS="-O -Wall -D_WIN32_WINNT=0x0501 -DWINDOWS -DNO_RINDEX -DADDRCACHE $(FLAGS)" LIBS="$(LIBS) -lws2_32 -lregex -lgdi32" mingw.exe mingw-pop: $(MAKE) CC="$(MINGWCC)" TARGET=mingw pop_stone mingw-ssl: cryptoapi.o $(MAKE) CC="$(MINGWCC)" FLAGS="$(FLAGS)" SSL_FLAGS="-DUSE_SSL -DCRYPTOAPI" SSL_LIBS="cryptoapi.o -lcrypt32 -lssl32 -leay32" TARGET=mingw ssl_stone mingw-me: $(MAKE) CC="$(MINGWCC)" FLAGS="-DNO_ADDRINFO" mingw-ssl mingw-nt: $(MAKE) CC="$(MINGWCC)" FLAGS="-DNO_ADDRINFO" TARGET=mingw-ssl svc_stone mingw-svc: $(MAKE) CC="$(MINGWCC)" TARGET=mingw-ssl svc_stone emx: $(MAKE) CC=gcc FLAGS="-DOS2 -Zmts -Zsysv-signals $(FLAGS)" LIBS="$(LIBS) -lsocket" stone.exe emx-pop: $(MAKE) TARGET=emx pop_stone emx-ssl: $(MAKE) TARGET=emx ssl_stone stone-2.3d-2.3.2.7/GPL.txt0000644000023200056710000005207210210630760014011 0ustar sengokuklab GNU $B0lHL8xM-;HMQ5vBz=q(B ======================= 1991 $BG/(B6 $B7n!$%P!<%8%g%s(B2 Copyright (C) 1989,1991 Free Software Foundation, Inc. 675 Mass Ave, Cambridge, MA 02139, USA $B2??M$b!"0J2<$NFbMF$rJQ99$7$J$$$G$=$N$^$^J#l9g$K8B$j!"K\;HMQ5vBz=q$rJ#@=$7(B $B$?$jHRI[$9$k$3$H$,$G$-$^$9!#(B $B$O$8$a$K(B -------- $B$[$H$s$I$N%=%U%H%&%'%"$N;HMQ5vBz$O!"%=%U%H%&%'%"$r6&M-$7!"JQ99$9$k%f!<%6$N<+M3$r(B $BC%$&$3$H$r0U?^$7$F$$$^$9!#$=$l$KBP$7$F!"2f!9$N(BGNU $B0lHL8xM-;HMQ5vBz$O!"%U%j!Z$9$k$?$a$N$b$N!"B($A%U%j!Z$9$k$?$a$N$b$N$G$9!#K\;HMQ(B $B5vBz$O!"(BFree Software Foundation $B$N$[$H$s$IA4$F$N%=%U%H%&%'%"$KE,MQ$5$l$k$@$1$G(B $B$J$/!"%W%m%0%i%`$N:n@.l9g$N$=$N%W%m%0%i%`$K$bE,MQ$9$k(B $B$3$H$,$G$-$^$9!#(B($B$=$NB>$N(BFree Software Foundation $B$N%=%U%H%&%'%"$N$$$/$D$+$O!"K\(B $B5vBz=q$G$O$J$/!"(BGNU $B%i%$%V%i%j0lHL8xM-;HMQ5vBz$GJ]8n$5$l$^$9!#(B) $B$"$J$?$O<+J,$N%W(B $B%m%0%i%`$K$b$3$l$rE,MQ$G$-$^$9!#2f!9$,%U%j!l9g$O<+M3(B $B$N$3$H$K8@5Z$7$F$$$k$N$G$"$C$F!"2A3J$N$3$H$G$O$"$j$^$;$s!#2f!9$N0lHL8xM-;HMQ5vBz(B $B$N3F>r9`$O!"$`$J$i$"$J$?(B $B$N$3$N%5!<%S%9$KBP$7$FBP2A$r@A5a$G$-$k$3$H(B)$B!#(B $B!&(B $B%=!<%9!&%3!<%I$r$7$5$($9$l$P$=$l$rF~e$N3FFbMF$r9T$J$&$3$H$,$G$-$k$H$$$&$3$H$r%f!<%6<+?H$,CN$C$F$$$k$3$H!#(B $B$3$N$h$&$J%f!<%6$N8"Mx$rr9`$r@_$1$kI,MW$,$"$j$^(B $B$9!#$3$l$i$N@)8B>r9`$O!"%f!<%6$,!"%U%j!l9g$K$O!"$=$N%f!<%6<+?H$,l9g!"M-=~$+L5=~$+$K$+$+$o$i$:!"$"$J$?$O<+J,(B $B$N;}$C$F$$$k8"Mx$rA4$FAjr9`$rCN$i$7$a$J$1$l$P$J$j$^$;$s!#(B $B2f!9$Or9`$N2<$G%=%U%H%&%'%"$rJ#@=!&HRI[!&JQ99$9$k8"Mx$r%f!<%6$KM?$($^(B $B$9!#(B $B$^$?!"3F:n@.Z$G$"$k$3$H$r(B $BA4$F$N?M!9$,N;2r$7$F$$$kI,MW$,$"$j$^$9!#$5$i$K!"B>$NC/$+$K$h$C$FJQ99$5$l$?%=%U%H(B $B%&%'%"$,HRI[$5$l$?>l9g!"?M$N4XM?$K$h$C$F863+H/2A$,1F6A$5$l$J$$$h$&$K$9$k$?$a$G$9!#(B $B:G8e$K!"$I$N%U%j!e$=$N%W%m%0%i%`$r<+J,(B $B$N:b;:$K$7$F$7$^$&$H$$$&4m81$rHr$1$?$$$H4j$C$F$$$^$9!#$3$l$rKI$0$?$a$K2f!9$O!"$$(B $B$:$l$NFC5v$b!"C/$G$b<+M3$K;HMQ$G$-$k$h$&$K;HMQ5vBz$5$l$k$Y$-$+!"$"$k$$$O2??M$KBP(B $B$7$F$bA4$/;HMQ$5$;$J$$$+$N!"$$$:$l$+$K$9$Y$-$G$"$k$3$H$rL@$i$+$K$7$F$-$^$7$?!#(B $BJ#r9`$H>r7o$rr9`$H>r7o(B ============================================================== 1. $BK\;HMQ5vBz$O!"K\0lHL8xM-;HMQ5vBz$N3F>r9`$K=>$C$FHRI[$5$l$k$H$$$&Cx:n8"$N:n@.J*$KE,MQ$5$l$^$9!#0J2<$K(B $B$*$$$F!V%W%m%0%i%`!W$H$O!"$=$N$h$&$J%W%m%0%i%`$d:n@.J*$r;X$9$b$N$H$7!"$^$?!"(B $B!V%W%m%0%i%`@8@.J*!W$H$O!">e=R$7$?!V%W%m%0%i%`!W<+?H!"$^$?$O!"Cx:n8"K!2<$K(B $B$*$1$kA4$F$NGI@8J*!($9$J$o$A!"$=$N!V%W%m%0%i%`!W$NA4ItKt$O0lIt$r!"$=$N$^$^(B $BKt$OJQ99$7$F!"3n$D!?Kt$OB>$N8@8l$KJQ49$7$F!"FbIt$KAH$_9~$s$@:n@.J*$r0UL#$7(B $B$^$9!#(B($B0J2r7o$K4^$^$l$k$b$N$H$7$^$9!#(B) $BK\;HMQ5vBz$K$h$C$F5vBz$r]$H$7$^$;$s!#$=$l$i$OK\;HMQ5vBz(B $B$NHO0O30$G$9!#!V%W%m%0%i%`!W$rl9g$K8B$jK\;HMQ5vBz$NBP>]$H$J$j(B $B$^$9!#$3$l$,Ev$F$O$^$k$+$I$&$+$O!"!V%W%m%0%i%`!W$,2?$r$9$k$b$N$+$K0M$j$^$9!#(B 2. $B$"$J$?$O!"$I$N$h$&$JG^BN>e$XJ#@=$7$h$&$H$9$k>l9g$G$"$C$F$b!"F~e$GE,@5$JCx:n8"I=<($HJ](B $B>Z$NJ|4~$rL@3N!"3n$DE,@5$KIU5-$9$k>l9g$K8B$j!"J#@=Kt$OHRI[$9$k$3$H$,$G$-$^(B $B$9!#$=$N>l9g!"K\;HMQ5vBz5Z$SL5J]>Z$K4X$9$k5-:\ItJ,$O!"A4$F85$N$^$^$N7A$GI=(B $B<($7$F$/$@$5$$!#$^$?!"!V%W%m%0(B $B%i%`!W$NHRI[@h$KBP$7$F$O!"!V%W%m%0%i%`!W$H(B $B6&$KK\;HMQ5vBz=q$NZ$r9T$J$&>l9g$O$=$l$rM-=~$H$9$k$3(B $B$H$,$G$-$^$9!#(B 3. $Br7o$rA4$FK~$?$7$F$$$k8B$j!"$"$J$?$O!"!V%W%m%0%i%`!WKt$O$=$N0lItJ,$r(B $BJQ99$7$F!V%W%m%0%i%`@8@.J*!W$H$9$k$3$H$,$G$-!"$5$i$K!"JQ99HG$d1&:n@.J*$r>e(B $B5-Bh(B 2 $B9`$K=>$C$FJ#@=Kt$OHRI[$9$k$3$H$b$G$-$^$9!#(B (a) $B%U%!%$%k$rJQ99$7$?;]$H$=$NJQ99F|$H$r!"JQ99$7$?%U%!%$%k>e$KL@3N$KI=<($9(B $B$k$3$H!#(B (b) $BJQ99$7$?$+H]$+$rLd$o$:!"K^$=!V%W%m%0%i%`!WKt$O$=$N0lItJ,$rFbIt$KAH$_9~(B $B$s$G$$$k$+Kt$O$=$l$+$iGI@8$7$?@8@.J*$rHRI[$9$k>l9g$K$O!"$=$NA4BN$rK\;H(B $BMQ5vBz$N>r9`$K=>$C$FBh;0o$NBPOCE*$JJ}K!$G%3%^%s%I$rFI$`$h$&$K$J$C(B $B$F$$$k$H$9$l$P!":G$bIaDL$NJ}K!$GBPOCE*$K$=$N%W%m%0%i%`$rZ$G$"$k$3$H(B($B$"$J$?$,FH<+$KJ]>Z$9$k>l9g$O!"$=$N;](B)$B!#(B $B!&HRI[$rr9`$K=>$C$F!V%W%m%0%i%`!W$r:FHR(B $BI[$G$-$k$3$H!#(B $B!&HRI[$rH$9$kJ}K!!#(B ($BNc30$H$7$F!"!V%W%m%0%i%`!W<+BN$OBPOCE*$G$"$C$F$b5/F0;~$NJ88@$rDL>o$O0u(B $B;z$7$J$$$N$J$i$P!"$"$J$?$N!V%W%m%0%i%`@8@.J*!W$O$3$N$h$&$JJ88@$r0u;z$9(B $B$kI,MW$O$"$j$^$;$s!#(B) $B$3$l$i$NMW7o$OJQ99$5$l$?:n@.J*$K$bA4$FE,MQ$5$l$^$9!#$=$NJQ99HG$N0?$kItJ,$,(B $B!V%W%m%0%i%`!W$NGI@8J*$G$O$J$/!"$7$+$b$=$l<+BNFHN)$G0[$J$k:n@.J*$@$H9gM}E*$K(B $B9M$($i$l$k>l9g!"$"$J$?$,$=$l$i$rJL$N:n@.J*$H$7$FHRI[$7$?;~$O!"K\;HMQ5vBz$H(B $B$=$N>r9`$O$=$l$i$NItJ,$K$OE,MQ$5$l$^$;$s!#$7$+$7!"$=$l$i$r!V%W%m%0%i%`@8@.(B $BJ*!W$N0lIt$H$7$FHRI[$9$k>l9g$O!"A4BN$,K\;HMQ5vBz$N>r9`$K=>$C$FHRI[$5$l$J$1(B $B$l$P$J$i$:!";HMQ5vBz$r$NA4$F$Nr9`$N0U?^$9$k$H$3$m$O!"40A4$K$"$J$?$K$h$C$F=q$+$l$?:n@.J*$K(B $B$D$$$F!"8"Mx$rMW5a$7$?$j!"$"$J$?$H8"Mx4X78$rAh$&$3$H$G$O$"$j$^$;$s!#$`$7$m(B $B$=$NL\E*$O!":n@.J*$,!V%W%m%0%i%`@8@.J*!W$G$"$k>l9g$K$=$NGI@8J*$d=89gJ*$NHR(B $BI[$r5,@)$9$k$3$H$K$"$j$^$9!#(B $B$5$i$K!"!V%W%m%0%i%`!W(B($BKt$O!V%W%m%0%i%`@8@.J*!W(B) $B$H!V%W%m%0%i%`@8@.J*!W$H$O(B $B$J$i$J$$B>$N%W%m%0%i%`$H$r!"C1$KJ]4I$dHRI[$N$?$a$KF10l$NG^BN>e$K$^$H$a$F5-(B $BO?$7$?$H$7$F$b!"K\;HMQ5vBz$OB>$N%W%m%0%i%`$K$OE,MQ$5$l$^$;$s!#(B 4. $B$"$J$?$O!"0J2<$N$&$A$$$:$l$+(B1 $B$D$rK~$?$98B$j!">e5-Bh(B2 $B9`5Z$SBh(B3 $B9`$K=>$C$F(B $B!V%W%m%0%i%`!W(B($BKt$O!">e5-Bh(B3 $B9`$G8@5Z$7$F$$$k!V%W%m%0%i%`@8@.J*!W(B)$B$r%*%V%8%'(B $B%/%H!&%3!<%IKt$Ol(B $B9g!"$=$N%=!<%9!&%3!<%I$N0z$-EO$7$O>e5-Bh(B2 $B9`5Z$SBh(B 3 $B9`$K=>$C$F!"DL>o%=(B $B%U%H%&%'%"$N8r49$KMQ$$$i$l$kG^BN$G9T$J$o$l$k$3$H!#(B (b) $B>/$J$/$H$b(B3 $BG/4V$NM-8z4|4V$rDj$a!"3n$D$=$N4|4VFb$G$"$l$PBP1~$9$k5!3#FI$_(B $Be$NBP2A$r(B $BMW5a$;$:$KDs6!$9$k;]!"5Z$S$=$N>l9g$K$O>e5-Bh(B2 $B9`5Z$SBh(B3 $B9`$K=>$C$F!"DL>o(B $B%=%U%H%&%'%"$N8r49$KMQ$$$i$l$kG^BN$GDs6!$5$l$k;]$r5-:\$7$?=qLL$r!"Bh;0pJs$r0l=o$K0z$-(B $BEO$9$3$H!#(B($B$3$NA*Br;h$O!"1DMx$rL\E*$H$7$J$$HRI[$G$"$C$F!"3n$D$"$J$?$,>e(B $B5-$N(B (b) $B9`$K4p$E$$$F!"%*%V%8%'%/%H!&%3!<%I0?$$$Ol9g$K8B$jE,MQ$5$l$kA*Br9`L\$G$9!#(B) $B$J$*!"%=!<%9!&%3!<%I$H$O!"JQ99:n6H$KE,$7$?5-=R7A<0$r;X$7$^$9!#$^$?!"l9g$K8B$j!"HRI[$5$l$k%=!<%9!&%3!<%I$K4^$a$kI,MW$O$"$j$^$;$s!#(B $Bl=j$+$i$NJ#@=$N$?(B $B$a$N%"%/%;%98"$NIjM?$G$"$k>l9g!"F1$8>l=j$+$i$N%=!<%9!&%3!<%I$NJ#@=$N$?$a$NF1(B $BEy$J%"%/%;%98"$rIjM?$9$l$P!"$?$H$(Bh;0l9g$r=|$-!"$"$J$?$O!"!V%W%m%0%i%`!W$rJ#@=!"(B $BJQ99!"%5%V%i%$%;%s%9!"HRI[$9$k$3$H$,$G$-$^$;$s!#K\;HMQ5vBz$K=>$o$:$K!V%W%m%0(B $B%i%`!W$rJ#@=!"JQ99!"%5%V%i%$%;%s%9!"HRI[$7$h$&$H$9$k9T0Y$O!"$=$l<+BN$,L58z$G(B $B$"$j!"3n$D!"K\;HMQ5vBz$,$"$J$?$K5vBz$7$F$$$k!V%W%m%0%i%`!W$N8"Mx$r<+F0E*$K>C(B $BLG$5$;$^$9!#$=$N>l9g!"K\;HMQ5vBz$K=>$C$F$"$J$?$+$iJ#@=J*$d$=$N8"Mx$rF@$F$$$k(B $BBh;0$C$F$$$k>l9g$K8B$j!"0zB3$-M-8z$J;HMQ8"8B$r;}$D(B $B$b$N$H$7$^$9!#(B 6. $B$"$J$?$O$^$@F10U$N0u$H$7$F=pL>$7$F$$$J$$$N$G!"K\;HMQ5vBz$r$C$F!"$"$J$?$,(B $B!V%W%m%0%i%`!W(B($BKt$O!V%W%m%0%i%`@8@.J*!W(B) $B$NJQ99Kt$OHRI[$r9T$($P!"$=$l<+BN$G$"(B $B$J$?$OK\;HMQ5vBz$rr9`$H>r7o$NA4$F$rr9`$K=>$C$F!V%W%m%0%i%`!W$rJ#(B $B@=!"HRI[!"JQ99$9$k$3$H$rFbMF$H$9$k;HMQ5vBz$rr7o$,K\;HMQ5vBz$HAjF~$l$J$$$b$N$G$"$C$?$H$7(B $B$F$b(B($B:[H==j$NL?Na!"7@Ls!"$=$NB>$K$h$k$b$N$G$"$l(B)$B!"K\;HMQ5vBz$N>r7o$,LH=|$5$l(B $B$k$b$N$G$O$"$j$^$;$s!#K\;HMQ5vBz$K$h$k@UL3$H!"$=$NB>$N2?$i$+$N4XO"@UL3$rF1;~(B $B$KK~$?$9BVMM$GHRI[$9$k$3$H$,$G$-$J$$$J$i$P!"$"$J$?$O!V%W%m%0%i%`!W$rA4$/HRI[(B $B$7$F$O$$$1$^$;$s!#Nc$($P!"FC5v8"$NFbMF$,!"$"$J$?$+$iD>@\Kt$O4V@\$KJ#@=$re$NMW@A$HK\;HMQ5vBz$NN>J}$rK~B-$5$;$kJ}K!$O!"!V%W%m%0%i%`!W$NHR(B $BI[$r40A4$KCGG0$9$k$3$H$@$1$G$9!#(B $BK\>r9`$N0?$kItJ,$,2?$i$+$NFCJL$J>u672<$GL58z$^$?$OE,MQIT2DG=$K$J$C$?>l9g!"K\(B $B>r9`$N$=$NB>$N;D$j$NItJ,$,E,MQ$5$l$k$h$&$K0U?^$5$l$F$*$j!"$^$?!"K\>r9`$OA4BN(B $B$H$7$F$=$NB>$N>u67$KEv$F$O$^$k$h$&$K0U?^$5$l$F$$$^$9!#(B $BK\>r9`$NL\E*$O!"FC5v$d$=$NB>$N:b;:8"$r?/32$7$?$j!"$=$N$h$&$J8"Mx$K4p$E$/r9`$NM#0l$NL\E*$O!"(B $B%U%j!$N2?$i$+$N%7%9%F%`$rDL$8$F%=%U%H%&%'%"$rHR(B $BI[$7$?$$$H7h$a$k$3$H$OH`$i$N<+M30U;V$G$"$j!";HMQ5vBz$rr9`$O!"K\;HMQ5vBz$NB>$N>r9`$N0UL#FbMF$,2?$G$"$k$+$r40A4$KL@$i$+$K$9$k$3$H(B $B$r0U?^$7$F$$$^$9!#(B 9. $B!V%W%m%0%i%`!W$NHRI[!&;HMQ$,!"$"$k9q$K$*$$$FFC5vKt$OCx:n8"$GJ]8n$5$l$?%$%s%?(B $B%U%'!<%9$N$I$A$i$+$G@)8B$5$l$k>l9g!"!V%W%m%0%i%`!W$rK\;HMQ5vBz2<$K$*$$$?86Cx(B $B:n8"J];}l(B $B9g!"$=$N@)8B$rK\;HMQ5vBz$NK\J8$K$"$?$+$b=q$+$l$F$$$k$+$N$h$&$KK\;HMQ5vBz$NCf(B $B$KAH$_F~$l$i$l$k$b$N$H$7$^$9!#(B 10. Free Software Foundation $B$O?o;~!"K\0lHL8xM-;HMQ5vBz$N2~D{HG!"Kt$O?7HG$r8xI=(B $B$9$k$3$H$,$"$j$^$9!#$=$N$h$&$J?7$7$$%P!<%8%g%s$O!"8=9T$N%P!<%8%g%s$H4pK\E*$K(B $BJQ$o$k$H$3$m$O$"$j$^$;$s$,!"?7$7$$LdBj$d7|0F;v9`$KBP1~$9$k$?$a$K:YIt$G$O0[$J(B $B$k$+$b$7$l$^$;$s!#(B $B3F%P!<%8%g%s$O!"%P!<%8%g%sHV9f$K$h$C$F6hJL$7$^$9!#!V%W%m%0%i%`!WCf$KK\;HMQ5v(B $BBz$N%P!<%8%g%sHV9f$N;XDj$,$"$k>l9g$O!"$=$N;XDj$5$l$?%P!<%8%g%s$+!"Kt$O$=$N8e(B $B$K(B Free Software Foundation $B$+$i8xI=$5$l$F$$$k$$$:$l$+$N%P!<%8%g%s$+$i(B1 $B$D$r(B $BA*Br$7$F!"$=$N>r9`$H>r7o$K=>$C$F$/$@$5$$!#!V%W%m%0%i%`!WCf$KK\;HMQ5vBz$N%P!<(B $B%8%g%sHV9f$N;XDj$,$J$$>l9g$O!"(BFree Software Foundation $B$,8xI=$7$?$I$N%P!<%8%g(B $B%s$G$bA*Br$9$k$3$H$,$G$-$^$9!#(B 11. $B!V%W%m%0%i%`!W$N0lIt$rHRI[>r7o$N0[$J$kB>$N%U%j!l(B $B9g$O!"$=$N3+H/l9g$KBP1~$9$k$?$a$K2f!9$ONc30E*=hM}$r$9$k$3$H$b$"(B $B$j$^$9$,!"$=$NH=CG4p=`$H$J$k$N$O!"uBV$K(B $BJ]$D$3$H$G$"$j!"$b$$D$O%=%U%H%&%'%"$N6&M-$H:FMxMQ$H$r9-$/B%?J$5$;$k$3$H$G(B $B$9!#(B $BL5J]>Z(B ------ 12. $B!V%W%m%0%i%`!W$OL5=~$G;HMQ5vBz$5$l$^$9$N$G!"E,MQK!Na$NHO0OFb$G!"!V%W%m%0%i%`!W(B $B$NJ]>Z$O0l@Z$"$j$^$;$s!#Cx:n8"$NBh;0Z$G!V$=$N$^$^!W(B $B$N>uBV$G!"3n$D!"L@<($+0EL[$G$"$k$+$rLd$o$:0l@Z$NJ]>Z$r$D$1$J$$$GDs6!$9$k$b$N(B $B$H$7$^$9!#$3$3$G$$$&J]>Z$H$O!";T>l@-$dFCDjL\E*E,9g@-$K$D$$$F$N0EL[$NJ]>Z$b4^(B $B$^$l$^$9$,!"$=$l$K8BDj$5$l$k$b$N$G$O$"$j$^$;$s!#!V%W%m%0%i%`!W$NIJl9g!"$=$l$KH<$&0l@Z$NGI@8HqMQ$d=$M}!&D{@5$KMW$9$kHqMQ$OA4$F$"$J$?$NIi(B $BC4$H$7$^$9!#(B 13. $BE,MQK!Na$NDj$a!"Kt$O=qLL$K$h$k9g0U$,$"$k>l9g$r=|$-!"Cx:n8"e5-5vBz$rl9g$G$bF1MM$G$9!#$J$*!"$3$3$G$$$&B;32$K$ODL>oB;32!"FCJLB;32!"6vH/B;32!"4V(B $B@\B;32$,4^$^$l$^$9(B($B%G!<%?$N>C<:!"Kt$O$=$N@53N$5$NAS<:!"$"$J$?$dBh;0$N%W%m%0%i%`$H$N%$%s%?%U%'!<%9$NITE,9g2=!"Ey$b4^$^$l$^$9$,!"$3$l$K(B $B8BDj$5$l$k$b$N$G$O$"$j$^$;$s(B)$B!#(B $B0J>e(B $BCm0U(B **** $B1QJ8J8=q(B(GNU General Public Licence) $B$r@5<0J8=q$H$9$k!#$3$NOBJ8J8=q$OJ[8n;N$N0U(B $B8+$r:N$jF~$l$F!"$G$-$k$@$1@53N$K1QJ8J8=q$rK]Lu$7$?$b$N$G$"$k$,!"K!N'E*$KM-8z$J7@(B $BLs=q$G$O$J$$!#(B $BOBJ8J8=q<+BN$N:FG[I[$K4X$7$F(B **************************** $B$$$+$J$kG^BN$G$br7o$,$9$Y$FK~$?$5$l$F$$$k>l9g$K8B$j!"K\OBJ8J8=q$r$=$N$^$^(B $BJ#l9g$K8B$j!":FG[I[$9$k$3$H$,5v2D$5$l$F$$$^$9!#(B $B!&l9g!"$=$NG[I[r9`$rE,MQ$9$kJ}K!(B ==================================================== $B$"$J$?$,?7$7$/%W%m%0%i%`$r:n@.$7!"$=$l$r8xMQ$K6!$7$?$$>l9g$O!"%W%m%0%i%`$r%U%j!e$N3F>r9`$K=>$C$F$3$l$r:FHRI[$dJQ99$r$9$k$3$H(B $B$,$G$-$k$h$&$K$9$k$N$,:GNI$NJ}K!$G$9!#(B $B$=$&$9$k$?$a$K$O!"%W%m%0%i%`$K0J2<$NI=<($r$7$F$/$@$5$$!#$=$N>l9g!"L5J]>Z$G$"$k$H(B $B$$$&$3$H$r:G$b8z2LE*$KEA$($k$?$a$K!"%=!<%9!&%U%!%$%k$NKAF,$K$=$NA4J8$rI=<($9$l$P(B $B:G$b0BA4$G$9$,!"$=$NB>$NJ}K!$GI=<($9$k>l9g$G$b!"!VCx:n8"I=<(!W$HA4J8$rFI$_=P$90Y(B $B$N%"%I%l%9$X$N%]%$%s%?$@$1$O%U%!%$%k>e$KI=<($7$F$*$$$F$/$@$5$$!#(B $B%W%m%0%i%`L>$H$I$s$JF0:n$r$9$k$b$N$+$K$D$$$F$N4JC1$J@bL@$N9T(B ------------------------------------------------------------ Copyright (C) 19 $B!{!{G/!"Cx:n8"(B ---------- $BK\%W%m%0%i%`$O%U%j!r9`$K=>$C$FK\%W%m%0%i%`(B $B$r:FHRI[$^$?$OJQ99$9$k$3$H$,$G$-$^$9!#(B $BK\%W%m%0%i%`$OM-MQ$H$O;W$$$^$9$,!"HRI[$K$"$?$C$F$O!";T>l@-5Z$SFCDjL\E*E,9g(B $B@-$K$D$$$F$N0EL[$NJ]>Z$r4^$a$F!"$$$+$J$kJ]>Z$b9T$J$$$^$;$s!#>\:Y$K$D$$$F$O(B GNU $B0lHL8xM-;HMQ5vBz=q$r$*FI$_$/$@$5$$!#(B $B$"$J$?$O!"K\%W%m%0%i%`$H0l=o$K(BGNU $B0lHL8xM-;HMQ5vBz$Nl9g$O!"(BFree Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA $B$XpJs$b=q$-E:$($F(B $B$/$@$5$$!#(B $B%W%m%0%i%`$,BPOCE*$KF0:n$9$k>l9g$O!"BPOC%b!<%I$G5/F0$7$?;~$K(B ---------- Gnomovision $B$O40A4$KL5J]>Z$G$9!#>\:Y$O(B show w $B$H%?%$%W$7$F$/$@$5$$!#$3$l$O(B $B%U%j!r7o$N2<$G$3$l$r:FHRI[$9$k$3$H$,$G$-$^$9!#(B $B>\:Y$O(B show c $B$H%?%$%W$7$F$/$@$5$$!#(B $B>e5-$N(Bshow w $B$d(Bshow c $B$O3F!9!"K\0lHL8xM-;HMQ5vBz$N4XO"$9$kItJ,$rI=<($9$k%3%^%s%I(B $B$r;X$7$^$9!#$b$A$m$s!"$"$J$?$,;H$&$3$l$i$N%3%^%s%I$O(Bshow w $B$d(Bshow c $B$H$$$C$?8F$S(B $BL>$G$J$/$F$b9=$$$^$;$s!#$5$i$K!"$=$l$i$N%3%^%s%I$O$"$J$?$N%W%m%0%i%`$K9g$o$;$k0Y(B $B$K!"%^%&%9$G%/%j%C%/$7$?$j%a%K%e!<7A<0$K$9$k$3$H$b$G$-$^$9!#(B $B$^$?!"I,MW$HG'$a$?>l9g$K$O!"$"$J$?$N8[$$l(B $B9g(B) $B$d:_@R$9$k3X9;$+$i!"$=$N%W%m%0%i%`$KBP$9$k!VCx:n8"J|4~!W$rG'$a$?=pL>F~$j$N=q(B $BLL$rF~A0$OJQ$($F$/$@$5$$!#(B Yoyodyne, Inc. $B$O!"(BJames Hacker $B$,3+H/$7$?%W%m%0%i%`(B`Gnomovision' ($B%3%s%Q%$(B $B%i$K$D$J$2$k%W%m%0%i%`(B) $B$K$D$$$F$NCx:n8"K!>e$NA4$F$N8"Mx$rJ|4~$9$k!#(B Ty Coon $B$N=pL>(B, 1 April 1989 -------------- Ty Coon, $BI{]$H$J$C$F$$$kB>$N%W%m%0%i%`(B $B$KAH$_9~$`$3$H$OG'$a$F$$$^$;$s!#$"$J$?$N%W%m%0%i%`$,%5%V%k!<%A%s!&%i%$%V%i%j$G$"$C(B $B$F!"$"$J$?$,$=$N%i%$%V%i%j$r:b;:8"$NBP>]$H$J$C$F$$$kB>$N%"%W%j%1!<%7%g%s$H%j%s%/(B $B$5$;$k$3$H$K$h$C$F!"$5$i$KM-MQ$J$b$N$K$7$h$&$H$9$k>l9g$K$O!"K\;HMQ5vBz=q$NBe$o$j(B $B$K!"(BGNU $B%i%$%V%i%j0lHL8xM-;HMQ5vBz=q$K=>$C$F$/$@$5$$!#(B stone-2.3d-2.3.2.7/logmsg.mc0000644000023200056710000000007410751714360014443 0ustar sengokuklabMessageId=0x1 SymbolicName=EVLOG Language=English %1 . stone-2.3d-2.3.2.7/cryptoapi.c0000644000023200056710000004425510751714360015021 0ustar sengokuklab/* * Copyright (c) 2004 Peter 'Luna' Runestig * All rights reserved. * * Redistribution and use in source and binary forms, with or without modifi- * cation, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright no- * tice, this list of conditions and the following disclaimer in the do- * cumentation and/or other materials provided with the distribution. * * o The names of the contributors may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LI- * ABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUEN- * TIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEV- * ER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABI- * LITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #ifdef __MINGW32_VERSION /* MinGW w32api is incomplete when it comes to CryptoAPI, as per version 3.1 * anyway. This is a hack around that problem. */ #define CALG_SSL3_SHAMD5 (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SSL3SHAMD5) #define CERT_SYSTEM_STORE_LOCATION_SHIFT 16 #define CERT_SYSTEM_STORE_CURRENT_USER_ID 1 #define CERT_SYSTEM_STORE_CURRENT_USER (CERT_SYSTEM_STORE_CURRENT_USER_ID << CERT_SYSTEM_STORE_LOCATION_SHIFT) #define CERT_STORE_READONLY_FLAG 0x00008000 #define CERT_STORE_OPEN_EXISTING_FLAG 0x00004000 #define CRYPT_ACQUIRE_COMPARE_KEY_FLAG 0x00000004 static HINSTANCE crypt32dll = NULL; static BOOL WINAPI (*CryptAcquireCertificatePrivateKey) (PCCERT_CONTEXT pCert, DWORD dwFlags, void *pvReserved, HCRYPTPROV *phCryptProv, DWORD *pdwKeySpec, BOOL *pfCallerFreeProv) = NULL; static PCCERT_CONTEXT WINAPI (*CertCreateCertificateContext) (DWORD dwCertEncodingType, const BYTE *pbCertEncoded, DWORD cbCertEncoded) = NULL; #endif /* Size of an SSL signature: MD5+SHA1 */ #define SSL_SIG_LENGTH 36 /* try to funnel any Windows/CryptoAPI error messages to OpenSSL ERR_... */ #define ERR_LIB_CRYPTOAPI (ERR_LIB_USER + 69) /* 69 is just a number... */ #define CRYPTOAPIerr(f) err_put_ms_error(GetLastError(), (f), __FILE__, __LINE__) #define CRYPTOAPI_F_CERT_OPEN_SYSTEM_STORE 100 #define CRYPTOAPI_F_CERT_FIND_CERTIFICATE_IN_STORE 101 #define CRYPTOAPI_F_CRYPT_ACQUIRE_CERTIFICATE_PRIVATE_KEY 102 #define CRYPTOAPI_F_CRYPT_CREATE_HASH 103 #define CRYPTOAPI_F_CRYPT_GET_HASH_PARAM 104 #define CRYPTOAPI_F_CRYPT_SET_HASH_PARAM 105 #define CRYPTOAPI_F_CRYPT_SIGN_HASH 106 #define CRYPTOAPI_F_LOAD_LIBRARY 107 #define CRYPTOAPI_F_GET_PROC_ADDRESS 108 #define CRYPTOAPI_F_CERT_CREATE_CERT_CONTEXT 109 #define CRYPTOAPI_F_CERT_GET_CERT_CHAIN 110 static ERR_STRING_DATA CRYPTOAPI_str_functs[] = { { ERR_PACK(ERR_LIB_CRYPTOAPI, 0, 0), "microsoft cryptoapi"}, { ERR_PACK(0, CRYPTOAPI_F_CERT_OPEN_SYSTEM_STORE, 0), "CertOpenSystemStore" }, { ERR_PACK(0, CRYPTOAPI_F_CERT_FIND_CERTIFICATE_IN_STORE, 0), "CertFindCertificateInStore" }, { ERR_PACK(0, CRYPTOAPI_F_CRYPT_ACQUIRE_CERTIFICATE_PRIVATE_KEY, 0), "CryptAcquireCertificatePrivateKey" }, { ERR_PACK(0, CRYPTOAPI_F_CRYPT_CREATE_HASH, 0), "CryptCreateHash" }, { ERR_PACK(0, CRYPTOAPI_F_CRYPT_GET_HASH_PARAM, 0), "CryptGetHashParam" }, { ERR_PACK(0, CRYPTOAPI_F_CRYPT_SET_HASH_PARAM, 0), "CryptSetHashParam" }, { ERR_PACK(0, CRYPTOAPI_F_CRYPT_SIGN_HASH, 0), "CryptSignHash" }, { ERR_PACK(0, CRYPTOAPI_F_LOAD_LIBRARY, 0), "LoadLibrary" }, { ERR_PACK(0, CRYPTOAPI_F_GET_PROC_ADDRESS, 0), "GetProcAddress" }, { ERR_PACK(0, CRYPTOAPI_F_CERT_CREATE_CERT_CONTEXT, 0), "CertCreateCertificateContext" }, { ERR_PACK(0, CRYPTOAPI_F_CERT_GET_CERT_CHAIN, 0), "CertGetCertificateChain" }, { 0, NULL } }; typedef struct _CAPI_DATA { const CERT_CONTEXT *cert_context; HCRYPTPROV crypt_prov; DWORD key_spec; BOOL free_crypt_prov; } CAPI_DATA; static char *ms_error_text(DWORD ms_err) { LPVOID lpMsgBuf = NULL; char *rv = NULL; FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, ms_err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default language */ (LPTSTR) &lpMsgBuf, 0, NULL); if (lpMsgBuf) { char *p; rv = strdup(lpMsgBuf); LocalFree(lpMsgBuf); /* trim to the left */ if (rv) for (p = rv + strlen(rv) - 1; p >= rv; p--) { if (isspace(*p)) *p = '\0'; else break; } } return rv; } static void err_put_ms_error(DWORD ms_err, int func, const char *file, int line) { static int init = 0; # define ERR_MAP_SZ 16 static struct { int err; DWORD ms_err; /* I don't think we get more than 16 *different* errors */ } err_map[ERR_MAP_SZ]; /* in here, before we give up the whole thing... */ int i; if (ms_err == 0) /* 0 is not an error */ return; if (!init) { ERR_load_strings(ERR_LIB_CRYPTOAPI, CRYPTOAPI_str_functs); memset(&err_map, 0, sizeof(err_map)); init++; } /* since MS error codes are 32 bit, and the ones in the ERR_... system is * only 12, we must have a mapping table between them. */ for (i = 0; i < ERR_MAP_SZ; i++) { if (err_map[i].ms_err == ms_err) { ERR_PUT_error(ERR_LIB_CRYPTOAPI, func, err_map[i].err, file, line); break; } else if (err_map[i].ms_err == 0 ) { /* end of table, add new entry */ ERR_STRING_DATA *esd = calloc(2, sizeof(*esd)); if (esd == NULL) break; err_map[i].ms_err = ms_err; err_map[i].err = esd->error = i + 100; esd->string = ms_error_text(ms_err); ERR_load_strings(ERR_LIB_CRYPTOAPI, esd); ERR_PUT_error(ERR_LIB_CRYPTOAPI, func, err_map[i].err, file, line); break; } } } /* encrypt */ static int rsa_pub_enc(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding) { /* I haven't been able to trigger this one, but I want to know if it happens... */ assert(0); return 0; } /* verify arbitrary data */ static int rsa_pub_dec(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding) { /* I haven't been able to trigger this one, but I want to know if it happens... */ assert(0); return 0; } /* sign arbitrary data */ static int rsa_priv_enc(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding) { CAPI_DATA *cd = (CAPI_DATA *) rsa->meth->app_data; HCRYPTHASH hash; DWORD hash_size, len, i; unsigned char *buf; if (cd == NULL) { RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, ERR_R_PASSED_NULL_PARAMETER); return 0; } if (padding != RSA_PKCS1_PADDING) { /* AFAICS, CryptSignHash() *always* uses PKCS1 padding. */ RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, RSA_R_UNKNOWN_PADDING_TYPE); return 0; } /* Unfortunately, there is no "CryptSign()" function in CryptoAPI, that would * be way to straightforward for M$, I guess... So we have to do it this * tricky way instead, by creating a "Hash", and load the already-made hash * from 'from' into it. */ /* For now, we only support NID_md5_sha1 */ if (flen != SSL_SIG_LENGTH) { RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, RSA_R_INVALID_MESSAGE_LENGTH); return 0; } if (!CryptCreateHash(cd->crypt_prov, CALG_SSL3_SHAMD5, 0, 0, &hash)) { CRYPTOAPIerr(CRYPTOAPI_F_CRYPT_CREATE_HASH); return 0; } len = sizeof(hash_size); if (!CryptGetHashParam(hash, HP_HASHSIZE, (BYTE *) &hash_size, &len, 0)) { CRYPTOAPIerr(CRYPTOAPI_F_CRYPT_GET_HASH_PARAM); CryptDestroyHash(hash); return 0; } if ((int) hash_size != flen) { RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, RSA_R_INVALID_MESSAGE_LENGTH); CryptDestroyHash(hash); return 0; } if (!CryptSetHashParam(hash, HP_HASHVAL, (BYTE * ) from, 0)) { CRYPTOAPIerr(CRYPTOAPI_F_CRYPT_SET_HASH_PARAM); CryptDestroyHash(hash); return 0; } len = RSA_size(rsa); buf = malloc(len); if (buf == NULL) { RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, ERR_R_MALLOC_FAILURE); CryptDestroyHash(hash); return 0; } if (!CryptSignHash(hash, cd->key_spec, NULL, 0, buf, &len)) { CRYPTOAPIerr(CRYPTOAPI_F_CRYPT_SIGN_HASH); CryptDestroyHash(hash); free(buf); return 0; } /* and now, we have to reverse the byte-order in the result from CryptSignHash()... */ for (i = 0; i < len; i++) to[i] = buf[len - i - 1]; free(buf); CryptDestroyHash(hash); return len; } /* decrypt */ static int rsa_priv_dec(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding) { /* I haven't been able to trigger this one, but I want to know if it happens... */ assert(0); return 0; } /* called at RSA_new */ static int init(RSA *rsa) { return 0; } /* called at RSA_free */ static int finish(RSA *rsa) { CAPI_DATA *cd = (CAPI_DATA *) rsa->meth->app_data; if (cd == NULL) return 0; if (cd->crypt_prov && cd->free_crypt_prov) CryptReleaseContext(cd->crypt_prov, 0); if (cd->cert_context) CertFreeCertificateContext(cd->cert_context); free(rsa->meth->app_data); free((char *) rsa->meth); rsa->meth = NULL; return 1; } static const CERT_CONTEXT *find_certificate_in_store(const char *cert_prop, HCERTSTORE cert_store) { /* Find, and use, the desired certificate from the store. The * 'cert_prop' certificate search string can look like this: * SUBJ: * THUMB:, e.g. * THUMB:f6 49 24 41 01 b4 fb 44 0c ce f4 36 ae d0 c4 c9 df 7a b6 28 */ const CERT_CONTEXT *rv = NULL; if (!strncmp(cert_prop, "SUBJ:", 5)) { /* skip the tag */ cert_prop += 5; rv = CertFindCertificateInStore(cert_store, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_SUBJECT_STR_A, cert_prop, NULL); } else if (!strncmp(cert_prop, "THUMB:", 6)) { unsigned char hash[255]; char *p; int i, x = 0; CRYPT_HASH_BLOB blob; /* skip the tag */ cert_prop += 6; for (p = (char *) cert_prop, i = 0; *p && i < sizeof(hash); i++) { if (*p >= '0' && *p <= '9') x = (*p - '0') << 4; else if (*p >= 'A' && *p <= 'F') x = (*p - 'A' + 10) << 4; else if (*p >= 'a' && *p <= 'f') x = (*p - 'a' + 10) << 4; if (!*++p) /* unexpected end of string */ break; if (*p >= '0' && *p <= '9') x += *p - '0'; else if (*p >= 'A' && *p <= 'F') x += *p - 'A' + 10; else if (*p >= 'a' && *p <= 'f') x += *p - 'a' + 10; hash[i] = x; /* skip any space(s) between hex numbers */ for (p++; *p && *p == ' '; p++); } blob.cbData = i; blob.pbData = (unsigned char *) &hash; rv = CertFindCertificateInStore(cert_store, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_HASH, &blob, NULL); } return rv; } int SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop) { HCERTSTORE cs; X509 *cert = NULL; RSA *rsa = NULL, *pub_rsa; CAPI_DATA *cd = calloc(1, sizeof(*cd)); RSA_METHOD *my_rsa_method = calloc(1, sizeof(*my_rsa_method)); if (cd == NULL || my_rsa_method == NULL) { SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_MALLOC_FAILURE); goto err; } /* search CURRENT_USER first, then LOCAL_MACHINE */ cs = CertOpenStore((LPCSTR) CERT_STORE_PROV_SYSTEM, 0, 0, CERT_SYSTEM_STORE_CURRENT_USER | CERT_STORE_OPEN_EXISTING_FLAG | CERT_STORE_READONLY_FLAG, L"MY"); if (cs == NULL) { CRYPTOAPIerr(CRYPTOAPI_F_CERT_OPEN_SYSTEM_STORE); goto err; } cd->cert_context = find_certificate_in_store(cert_prop, cs); CertCloseStore(cs, 0); if (!cd->cert_context) { cs = CertOpenStore((LPCSTR) CERT_STORE_PROV_SYSTEM, 0, 0, CERT_SYSTEM_STORE_LOCAL_MACHINE | CERT_STORE_OPEN_EXISTING_FLAG | CERT_STORE_READONLY_FLAG, L"MY"); if (cs == NULL) { CRYPTOAPIerr(CRYPTOAPI_F_CERT_OPEN_SYSTEM_STORE); goto err; } cd->cert_context = find_certificate_in_store(cert_prop, cs); CertCloseStore(cs, 0); if (cd->cert_context == NULL) { CRYPTOAPIerr(CRYPTOAPI_F_CERT_FIND_CERTIFICATE_IN_STORE); goto err; } } /* cert_context->pbCertEncoded is the cert X509 DER encoded. */ cert = d2i_X509(NULL, (const unsigned char **) &cd->cert_context->pbCertEncoded, cd->cert_context->cbCertEncoded); if (cert == NULL) { SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_ASN1_LIB); goto err; } /* set up stuff to use the private key */ #ifdef __MINGW32_VERSION /* MinGW w32api is incomplete when it comes to CryptoAPI, as per version 3.1 * anyway. This is a hack around that problem. */ if (crypt32dll == NULL) { crypt32dll = LoadLibrary("crypt32"); if (crypt32dll == NULL) { CRYPTOAPIerr(CRYPTOAPI_F_LOAD_LIBRARY); goto err; } } if (CryptAcquireCertificatePrivateKey == NULL) { CryptAcquireCertificatePrivateKey = GetProcAddress(crypt32dll, "CryptAcquireCertificatePrivateKey"); if (CryptAcquireCertificatePrivateKey == NULL) { CRYPTOAPIerr(CRYPTOAPI_F_GET_PROC_ADDRESS); goto err; } } #endif if (!CryptAcquireCertificatePrivateKey(cd->cert_context, CRYPT_ACQUIRE_COMPARE_KEY_FLAG, NULL, &cd->crypt_prov, &cd->key_spec, &cd->free_crypt_prov)) { /* if we don't have a smart card reader here, and we try to access a * smart card certificate, we get: * "Error 1223: The operation was canceled by the user." */ CRYPTOAPIerr(CRYPTOAPI_F_CRYPT_ACQUIRE_CERTIFICATE_PRIVATE_KEY); goto err; } /* here we don't need to do CryptGetUserKey() or anything; all necessary key * info is in cd->cert_context, and then, in cd->crypt_prov. */ my_rsa_method->name = "Microsoft CryptoAPI RSA Method"; my_rsa_method->rsa_pub_enc = rsa_pub_enc; my_rsa_method->rsa_pub_dec = rsa_pub_dec; my_rsa_method->rsa_priv_enc = rsa_priv_enc; my_rsa_method->rsa_priv_dec = rsa_priv_dec; /* my_rsa_method->init = init; */ my_rsa_method->finish = finish; my_rsa_method->flags = RSA_METHOD_FLAG_NO_CHECK; my_rsa_method->app_data = (char *) cd; rsa = RSA_new(); if (rsa == NULL) { SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_MALLOC_FAILURE); goto err; } /* cert->cert_info->key->pkey is NULL until we call SSL_CTX_use_certificate(), * so we do it here then... */ if (!SSL_CTX_use_certificate(ssl_ctx, cert)) goto err; /* the public key */ pub_rsa = cert->cert_info->key->pkey->pkey.rsa; /* SSL_CTX_use_certificate() increased the reference count in 'cert', so * we decrease it here with X509_free(), or it will never be cleaned up. */ X509_free(cert); cert = NULL; /* I'm not sure about what we have to fill in in the RSA, trying out stuff... */ /* rsa->n indicates the key size */ rsa->n = BN_dup(pub_rsa->n); rsa->flags |= RSA_FLAG_EXT_PKEY; if (!RSA_set_method(rsa, my_rsa_method)) goto err; if (!SSL_CTX_use_RSAPrivateKey(ssl_ctx, rsa)) goto err; /* SSL_CTX_use_RSAPrivateKey() increased the reference count in 'rsa', so * we decrease it here with RSA_free(), or it will never be cleaned up. */ RSA_free(rsa); return 1; err: if (cert) X509_free(cert); if (rsa) RSA_free(rsa); else { if (my_rsa_method) free(my_rsa_method); if (cd) { if (cd->free_crypt_prov && cd->crypt_prov) CryptReleaseContext(cd->crypt_prov, 0); if (cd->cert_context) CertFreeCertificateContext(cd->cert_context); free(cd); } } return 0; } int CryptoAPI_verify_certificate(X509 *x509) { int ret = -1; int len; unsigned char *buf = NULL; PCCERT_CONTEXT pCertContext = NULL; PCCERT_CHAIN_CONTEXT pChainContext = NULL; CERT_ENHKEY_USAGE EnhkeyUsage; CERT_USAGE_MATCH CertUsage; CERT_CHAIN_PARA ChainPara; /* Convert from internal X509 format to DER */ len = i2d_X509(x509, &buf); if (len < 0) { SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_ASN1_LIB); goto err; } #ifdef __MINGW32_VERSION /* MinGW w32api is incomplete when it comes to CryptoAPI, as per version 3.1 * anyway. This is a hack around that problem. */ if (crypt32dll == NULL) { crypt32dll = LoadLibrary("crypt32"); if (crypt32dll == NULL) { CRYPTOAPIerr(CRYPTOAPI_F_LOAD_LIBRARY); goto err; } } if (CertCreateCertificateContext == NULL) { CertCreateCertificateContext = GetProcAddress(crypt32dll, "CertCreateCertificateContext"); if (CertCreateCertificateContext == NULL) { CRYPTOAPIerr(CRYPTOAPI_F_GET_PROC_ADDRESS); goto err; } } #endif /* Create a certificate context based on the above certificate */ pCertContext = CertCreateCertificateContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, buf, len); if (pCertContext == NULL) { CRYPTOAPIerr(CRYPTOAPI_F_CERT_CREATE_CERT_CONTEXT); goto err; } /* Create an empty issuer list */ EnhkeyUsage.cUsageIdentifier = 0; EnhkeyUsage.rgpszUsageIdentifier = NULL; CertUsage.dwType = USAGE_MATCH_TYPE_AND; CertUsage.Usage = EnhkeyUsage; /* Searching and matching criteria to be used when building the chain */ ChainPara.cbSize = sizeof(CERT_CHAIN_PARA); ChainPara.RequestedUsage = CertUsage; /* Get the certificate chain of our certificate */ if (!CertGetCertificateChain(NULL, pCertContext, NULL, NULL, &ChainPara, 0, NULL, &pChainContext)) { CRYPTOAPIerr(CRYPTOAPI_F_CERT_GET_CERT_CHAIN); goto err; } /* return 1 when the certificate is trusted, 0 when it's not; -1 on error */ ret = (pChainContext->TrustStatus.dwErrorStatus == CERT_TRUST_NO_ERROR); err: if (buf) OPENSSL_free(buf); if (pChainContext) CertFreeCertificateChain(pChainContext); if (pCertContext) CertFreeCertificateContext(pCertContext); return ret; } stone-2.3d-2.3.2.7/stone.spec0000644000023200056710000000171210751714360014636 0ustar sengokuklabSummary: Simple Repeater Name: stone Version: 2.3d-2.3.2.7 Release: 1 URL: http://www.gcd.org/sengoku/stone/ Source0: %{name}-%{version}.tar.gz License: GPL Group: network BuildRoot: %{_tmppath}/%{name}-root %description Stone is a TCP/IP repeater in the application layer. It repeats TCP and UDP from inside to outside of a firewall, or from outside to inside. %prep %setup -q %build make linux-ssl SSL_FLAGS='-DUSE_SSL' SSL_LIBS='-lssl -lcrypto' %install rm -rf $RPM_BUILD_ROOT mkdir -p $RPM_BUILD_ROOT/usr/{bin,share/man/{,ja/}man1} install stone $RPM_BUILD_ROOT/usr/bin/ install -m 644 stone.1 $RPM_BUILD_ROOT/usr/share/man/man1/ install -m 644 stone.1.ja $RPM_BUILD_ROOT/usr/share/man/ja/man1/stone.1 %clean rm -rf $RPM_BUILD_ROOT %files %defattr(-,root,root) /usr/bin/stone /usr/share/man/man1/stone.1.gz /usr/share/man/ja/man1/stone.1.gz %doc GPL.txt README.* %changelog * Sun Jan 12 2003 iNOUE Koich! - Initial build.