From 60ae0e6fcd768c8a0b4aed6727ce92c65f093323 Mon Sep 17 00:00:00 2001 From: Bill Nottingham Date: Wed, 25 Nov 2009 22:47:36 +0000 Subject: [PATCH 01/45] Fix typo that causes a failure to update the common directory. (releng #2781) --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 158f3c8..91cf132 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ NAME := connect-proxy SPECFILE = $(firstword $(wildcard *.spec)) define find-makefile-common -for d in common ../common ../../common ; do if [ -f $$d/Makefile.common ] ; then if [ -f $$d/CVS/Root -a -w $$/Makefile.common ] ; then cd $$d ; cvs -Q update ; fi ; echo "$$d/Makefile.common" ; break ; fi ; done +for d in common ../common ../../common ; do if [ -f $$d/Makefile.common ] ; then if [ -f $$d/CVS/Root -a -w $$d/Makefile.common ] ; then cd $$d ; cvs -Q update ; fi ; echo "$$d/Makefile.common" ; break ; fi ; done endef MAKEFILE_COMMON := $(shell $(find-makefile-common)) From 7576a1be2727cb1094a272063299badc03ef1a4a Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Wed, 28 Jul 2010 12:06:13 +0000 Subject: [PATCH 02/45] dist-git conversion --- .cvsignore => .gitignore | 0 Makefile | 21 --------------------- 2 files changed, 21 deletions(-) rename .cvsignore => .gitignore (100%) delete mode 100644 Makefile diff --git a/.cvsignore b/.gitignore similarity index 100% rename from .cvsignore rename to .gitignore diff --git a/Makefile b/Makefile deleted file mode 100644 index 91cf132..0000000 --- a/Makefile +++ /dev/null @@ -1,21 +0,0 @@ -# Makefile for source rpm: connect-proxy -# $Id$ -NAME := connect-proxy -SPECFILE = $(firstword $(wildcard *.spec)) - -define find-makefile-common -for d in common ../common ../../common ; do if [ -f $$d/Makefile.common ] ; then if [ -f $$d/CVS/Root -a -w $$d/Makefile.common ] ; then cd $$d ; cvs -Q update ; fi ; echo "$$d/Makefile.common" ; break ; fi ; done -endef - -MAKEFILE_COMMON := $(shell $(find-makefile-common)) - -ifeq ($(MAKEFILE_COMMON),) -# attept a checkout -define checkout-makefile-common -test -f CVS/Root && { cvs -Q -d $$(cat CVS/Root) checkout common && echo "common/Makefile.common" ; } || { echo "ERROR: I can't figure out how to checkout the 'common' module." ; exit -1 ; } >&2 -endef - -MAKEFILE_COMMON := $(shell $(checkout-makefile-common)) -endif - -include $(MAKEFILE_COMMON) From 3652dfd5f316aa2be2ce82d857e1c8d05a964951 Mon Sep 17 00:00:00 2001 From: Dennis Gilmore Date: Tue, 8 Feb 2011 05:01:10 -0600 Subject: [PATCH 03/45] - Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild --- connect-proxy.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/connect-proxy.spec b/connect-proxy.spec index 92acfbb..c53e0e4 100644 --- a/connect-proxy.spec +++ b/connect-proxy.spec @@ -1,6 +1,6 @@ Name: connect-proxy Version: 1.100 -Release: 4%{?dist} +Release: 5%{?dist} Summary: SSH Proxy command helper Group: Applications/System @@ -56,6 +56,9 @@ rm -rf $RPM_BUILD_ROOT %{_bindir}/%{name} %changelog +* Tue Feb 08 2011 Fedora Release Engineering - 1.100-5 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild + * Fri Jul 24 2009 Fedora Release Engineering - 1.100-4 - Rebuilt for https://fedoraproject.org/wiki/Fedora_12_Mass_Rebuild From 863f3472656ec18c6d25f8ac82a504a4aae7bf86 Mon Sep 17 00:00:00 2001 From: Dennis Gilmore Date: Thu, 12 Jan 2012 17:49:23 -0600 Subject: [PATCH 04/45] - Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild --- connect-proxy.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/connect-proxy.spec b/connect-proxy.spec index c53e0e4..e077474 100644 --- a/connect-proxy.spec +++ b/connect-proxy.spec @@ -1,6 +1,6 @@ Name: connect-proxy Version: 1.100 -Release: 5%{?dist} +Release: 6%{?dist} Summary: SSH Proxy command helper Group: Applications/System @@ -56,6 +56,9 @@ rm -rf $RPM_BUILD_ROOT %{_bindir}/%{name} %changelog +* Thu Jan 12 2012 Fedora Release Engineering - 1.100-6 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild + * Tue Feb 08 2011 Fedora Release Engineering - 1.100-5 - Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild From 3e07f447a918880fe5ab1c2a6628850ef839cb33 Mon Sep 17 00:00:00 2001 From: Dennis Gilmore Date: Wed, 18 Jul 2012 14:37:18 -0500 Subject: [PATCH 05/45] - Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild --- connect-proxy.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/connect-proxy.spec b/connect-proxy.spec index e077474..2dee61f 100644 --- a/connect-proxy.spec +++ b/connect-proxy.spec @@ -1,6 +1,6 @@ Name: connect-proxy Version: 1.100 -Release: 6%{?dist} +Release: 7%{?dist} Summary: SSH Proxy command helper Group: Applications/System @@ -56,6 +56,9 @@ rm -rf $RPM_BUILD_ROOT %{_bindir}/%{name} %changelog +* Wed Jul 18 2012 Fedora Release Engineering - 1.100-7 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild + * Thu Jan 12 2012 Fedora Release Engineering - 1.100-6 - Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild From ec509acbe1bc342645b46af2a707a2181be4c7f4 Mon Sep 17 00:00:00 2001 From: Dennis Gilmore Date: Wed, 13 Feb 2013 12:56:14 -0600 Subject: [PATCH 06/45] - Rebuilt for https://fedoraproject.org/wiki/Fedora_19_Mass_Rebuild --- connect-proxy.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/connect-proxy.spec b/connect-proxy.spec index 2dee61f..1cb2722 100644 --- a/connect-proxy.spec +++ b/connect-proxy.spec @@ -1,6 +1,6 @@ Name: connect-proxy Version: 1.100 -Release: 7%{?dist} +Release: 8%{?dist} Summary: SSH Proxy command helper Group: Applications/System @@ -56,6 +56,9 @@ rm -rf $RPM_BUILD_ROOT %{_bindir}/%{name} %changelog +* Wed Feb 13 2013 Fedora Release Engineering - 1.100-8 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_19_Mass_Rebuild + * Wed Jul 18 2012 Fedora Release Engineering - 1.100-7 - Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild From ce3f95d45b5b8404da300a66b6ffb67f943734fd Mon Sep 17 00:00:00 2001 From: Dennis Gilmore Date: Sat, 3 Aug 2013 00:46:19 -0500 Subject: [PATCH 07/45] - Rebuilt for https://fedoraproject.org/wiki/Fedora_20_Mass_Rebuild --- connect-proxy.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/connect-proxy.spec b/connect-proxy.spec index 1cb2722..036dcd8 100644 --- a/connect-proxy.spec +++ b/connect-proxy.spec @@ -1,6 +1,6 @@ Name: connect-proxy Version: 1.100 -Release: 8%{?dist} +Release: 9%{?dist} Summary: SSH Proxy command helper Group: Applications/System @@ -56,6 +56,9 @@ rm -rf $RPM_BUILD_ROOT %{_bindir}/%{name} %changelog +* Sat Aug 03 2013 Fedora Release Engineering - 1.100-9 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_20_Mass_Rebuild + * Wed Feb 13 2013 Fedora Release Engineering - 1.100-8 - Rebuilt for https://fedoraproject.org/wiki/Fedora_19_Mass_Rebuild From 49acd80f509326cf9b545fa2acb6c3c33c7d96ad Mon Sep 17 00:00:00 2001 From: Dennis Gilmore Date: Sat, 7 Jun 2014 00:47:10 -0500 Subject: [PATCH 08/45] - Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild --- connect-proxy.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/connect-proxy.spec b/connect-proxy.spec index 036dcd8..82552c3 100644 --- a/connect-proxy.spec +++ b/connect-proxy.spec @@ -1,6 +1,6 @@ Name: connect-proxy Version: 1.100 -Release: 9%{?dist} +Release: 10%{?dist} Summary: SSH Proxy command helper Group: Applications/System @@ -56,6 +56,9 @@ rm -rf $RPM_BUILD_ROOT %{_bindir}/%{name} %changelog +* Sat Jun 07 2014 Fedora Release Engineering - 1.100-10 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild + * Sat Aug 03 2013 Fedora Release Engineering - 1.100-9 - Rebuilt for https://fedoraproject.org/wiki/Fedora_20_Mass_Rebuild From 2c5feda564fe3e31b8d594ce0487b255b4c1bbba Mon Sep 17 00:00:00 2001 From: Peter Robinson Date: Sat, 16 Aug 2014 01:10:29 +0000 Subject: [PATCH 09/45] - Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild --- connect-proxy.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/connect-proxy.spec b/connect-proxy.spec index 82552c3..2c9248b 100644 --- a/connect-proxy.spec +++ b/connect-proxy.spec @@ -1,6 +1,6 @@ Name: connect-proxy Version: 1.100 -Release: 10%{?dist} +Release: 11%{?dist} Summary: SSH Proxy command helper Group: Applications/System @@ -56,6 +56,9 @@ rm -rf $RPM_BUILD_ROOT %{_bindir}/%{name} %changelog +* Sat Aug 16 2014 Fedora Release Engineering - 1.100-11 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild + * Sat Jun 07 2014 Fedora Release Engineering - 1.100-10 - Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild From 09bfdbf460167258eb267fa5dbe4a2a032f7ab60 Mon Sep 17 00:00:00 2001 From: Dennis Gilmore Date: Wed, 17 Jun 2015 03:11:25 +0000 Subject: [PATCH 10/45] - Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild --- connect-proxy.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/connect-proxy.spec b/connect-proxy.spec index 2c9248b..c840b8c 100644 --- a/connect-proxy.spec +++ b/connect-proxy.spec @@ -1,6 +1,6 @@ Name: connect-proxy Version: 1.100 -Release: 11%{?dist} +Release: 12%{?dist} Summary: SSH Proxy command helper Group: Applications/System @@ -56,6 +56,9 @@ rm -rf $RPM_BUILD_ROOT %{_bindir}/%{name} %changelog +* Wed Jun 17 2015 Fedora Release Engineering - 1.100-12 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild + * Sat Aug 16 2014 Fedora Release Engineering - 1.100-11 - Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild From 1c15fe75a70d8e547aa97608f6b42bda40d7563f Mon Sep 17 00:00:00 2001 From: Dennis Gilmore Date: Wed, 3 Feb 2016 18:04:07 +0000 Subject: [PATCH 11/45] - Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild --- connect-proxy.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/connect-proxy.spec b/connect-proxy.spec index c840b8c..9aa6d16 100644 --- a/connect-proxy.spec +++ b/connect-proxy.spec @@ -1,6 +1,6 @@ Name: connect-proxy Version: 1.100 -Release: 12%{?dist} +Release: 13%{?dist} Summary: SSH Proxy command helper Group: Applications/System @@ -56,6 +56,9 @@ rm -rf $RPM_BUILD_ROOT %{_bindir}/%{name} %changelog +* Wed Feb 03 2016 Fedora Release Engineering - 1.100-13 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild + * Wed Jun 17 2015 Fedora Release Engineering - 1.100-12 - Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild From 647158ade1e3e016f810eb3dd0b2b45d9ae03338 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Fri, 10 Feb 2017 07:51:23 +0000 Subject: [PATCH 12/45] - Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild --- connect-proxy.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/connect-proxy.spec b/connect-proxy.spec index 9aa6d16..7bf58be 100644 --- a/connect-proxy.spec +++ b/connect-proxy.spec @@ -1,6 +1,6 @@ Name: connect-proxy Version: 1.100 -Release: 13%{?dist} +Release: 14%{?dist} Summary: SSH Proxy command helper Group: Applications/System @@ -56,6 +56,9 @@ rm -rf $RPM_BUILD_ROOT %{_bindir}/%{name} %changelog +* Fri Feb 10 2017 Fedora Release Engineering - 1.100-14 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild + * Wed Feb 03 2016 Fedora Release Engineering - 1.100-13 - Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild From f2a1accbf56e454e674a352327a996db372dfda6 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Wed, 26 Jul 2017 05:24:12 +0000 Subject: [PATCH 13/45] - Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild --- connect-proxy.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/connect-proxy.spec b/connect-proxy.spec index 7bf58be..f1d11d6 100644 --- a/connect-proxy.spec +++ b/connect-proxy.spec @@ -1,6 +1,6 @@ Name: connect-proxy Version: 1.100 -Release: 14%{?dist} +Release: 15%{?dist} Summary: SSH Proxy command helper Group: Applications/System @@ -56,6 +56,9 @@ rm -rf $RPM_BUILD_ROOT %{_bindir}/%{name} %changelog +* Wed Jul 26 2017 Fedora Release Engineering - 1.100-15 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild + * Fri Feb 10 2017 Fedora Release Engineering - 1.100-14 - Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild From 2b5213f79234c7d15d74a6a06af68adce2a67506 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Wed, 2 Aug 2017 19:08:06 +0000 Subject: [PATCH 14/45] - Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild --- connect-proxy.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/connect-proxy.spec b/connect-proxy.spec index f1d11d6..b424424 100644 --- a/connect-proxy.spec +++ b/connect-proxy.spec @@ -1,6 +1,6 @@ Name: connect-proxy Version: 1.100 -Release: 15%{?dist} +Release: 16%{?dist} Summary: SSH Proxy command helper Group: Applications/System @@ -56,6 +56,9 @@ rm -rf $RPM_BUILD_ROOT %{_bindir}/%{name} %changelog +* Wed Aug 02 2017 Fedora Release Engineering - 1.100-16 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild + * Wed Jul 26 2017 Fedora Release Engineering - 1.100-15 - Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild From 92c50853827be33914f98ab978be8d46b4dd766f Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Wed, 7 Feb 2018 05:38:25 +0000 Subject: [PATCH 15/45] - Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild Signed-off-by: Fedora Release Engineering --- connect-proxy.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/connect-proxy.spec b/connect-proxy.spec index b424424..97425dc 100644 --- a/connect-proxy.spec +++ b/connect-proxy.spec @@ -1,6 +1,6 @@ Name: connect-proxy Version: 1.100 -Release: 16%{?dist} +Release: 17%{?dist} Summary: SSH Proxy command helper Group: Applications/System @@ -56,6 +56,9 @@ rm -rf $RPM_BUILD_ROOT %{_bindir}/%{name} %changelog +* Wed Feb 07 2018 Fedora Release Engineering - 1.100-17 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild + * Wed Aug 02 2017 Fedora Release Engineering - 1.100-16 - Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild From 46b89a500d8e0a32dce8524704fcf2f622b7185f Mon Sep 17 00:00:00 2001 From: Igor Gnatenko Date: Tue, 13 Feb 2018 23:09:55 +0100 Subject: [PATCH 16/45] Remove BuildRoot definition None of currently supported distributions need that. It was needed last for EL5 which is EOL now Signed-off-by: Igor Gnatenko --- connect-proxy.spec | 1 - 1 file changed, 1 deletion(-) diff --git a/connect-proxy.spec b/connect-proxy.spec index 97425dc..bd6b310 100644 --- a/connect-proxy.spec +++ b/connect-proxy.spec @@ -11,7 +11,6 @@ Source0: connect-%{version}.c #Source0: http://www.taiyo.co.jp/~gotoh/ssh/connect.c Source1: http://www.taiyo.co.jp/~gotoh/ssh/connect.html Patch0: connect-proxy-make-man.patch -BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) Requires: openssh From 94d307952c3150ba1be88b653dfc456ea62e2050 Mon Sep 17 00:00:00 2001 From: Igor Gnatenko Date: Wed, 14 Feb 2018 07:55:41 +0100 Subject: [PATCH 17/45] Remove %clean section None of currently supported distributions need that. Last one was EL5 which is EOL for a while. Signed-off-by: Igor Gnatenko --- connect-proxy.spec | 3 --- 1 file changed, 3 deletions(-) diff --git a/connect-proxy.spec b/connect-proxy.spec index bd6b310..ee0fb98 100644 --- a/connect-proxy.spec +++ b/connect-proxy.spec @@ -45,9 +45,6 @@ make DESTDIR=$RPM_BUILD_ROOT install mkdir -p $RPM_BUILD_ROOT%{_mandir}/man1 cp -p %{name}.1 $RPM_BUILD_ROOT%{_mandir}/man1/ -%clean -rm -rf $RPM_BUILD_ROOT - %files %defattr(-,root,root,-) %doc connect.html From db1e5e1ad6c31aa636e808e603394e16982651ae Mon Sep 17 00:00:00 2001 From: Jason Tibbitts Date: Tue, 10 Jul 2018 00:32:58 -0500 Subject: [PATCH 18/45] Remove needless use of %defattr --- connect-proxy.spec | 1 - 1 file changed, 1 deletion(-) diff --git a/connect-proxy.spec b/connect-proxy.spec index ee0fb98..cb32c82 100644 --- a/connect-proxy.spec +++ b/connect-proxy.spec @@ -46,7 +46,6 @@ mkdir -p $RPM_BUILD_ROOT%{_mandir}/man1 cp -p %{name}.1 $RPM_BUILD_ROOT%{_mandir}/man1/ %files -%defattr(-,root,root,-) %doc connect.html %{_mandir}/man1/* %{_bindir}/%{name} From f316a72580a37c290019b4e4a808dd64657ded87 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Thu, 12 Jul 2018 22:11:18 +0000 Subject: [PATCH 19/45] - Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild Signed-off-by: Fedora Release Engineering --- connect-proxy.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/connect-proxy.spec b/connect-proxy.spec index cb32c82..4269421 100644 --- a/connect-proxy.spec +++ b/connect-proxy.spec @@ -1,6 +1,6 @@ Name: connect-proxy Version: 1.100 -Release: 17%{?dist} +Release: 18%{?dist} Summary: SSH Proxy command helper Group: Applications/System @@ -51,6 +51,9 @@ cp -p %{name}.1 $RPM_BUILD_ROOT%{_mandir}/man1/ %{_bindir}/%{name} %changelog +* Thu Jul 12 2018 Fedora Release Engineering - 1.100-18 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild + * Wed Feb 07 2018 Fedora Release Engineering - 1.100-17 - Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild From 2f892521836e50ef72c6a8b5d9d2f6366f338e75 Mon Sep 17 00:00:00 2001 From: Igor Gnatenko Date: Tue, 24 Jul 2018 17:08:03 +0200 Subject: [PATCH 20/45] Add missing BuildRequires on gcc make: cc: Command not found References: https://bugzilla.redhat.com/show_bug.cgi?id=1603697 Signed-off-by: Igor Gnatenko --- connect-proxy.spec | 1 + 1 file changed, 1 insertion(+) diff --git a/connect-proxy.spec b/connect-proxy.spec index 4269421..1cf15e3 100644 --- a/connect-proxy.spec +++ b/connect-proxy.spec @@ -14,6 +14,7 @@ Patch0: connect-proxy-make-man.patch Requires: openssh +BuildRequires: gcc %description connect-proxy is the simple relaying command to make network connection via SOCKS and https proxy. It is mainly intended to be used as proxy command From 913902890a0b0d189f9f0b794ff8ca7783037130 Mon Sep 17 00:00:00 2001 From: Igor Gnatenko Date: Mon, 28 Jan 2019 20:17:41 +0100 Subject: [PATCH 21/45] Remove obsolete Group tag References: https://fedoraproject.org/wiki/Changes/Remove_Group_Tag --- connect-proxy.spec | 1 - 1 file changed, 1 deletion(-) diff --git a/connect-proxy.spec b/connect-proxy.spec index 1cf15e3..bd3f0f9 100644 --- a/connect-proxy.spec +++ b/connect-proxy.spec @@ -3,7 +3,6 @@ Version: 1.100 Release: 18%{?dist} Summary: SSH Proxy command helper -Group: Applications/System License: GPLv2+ URL: http://www.taiyo.co.jp/~gotoh/ssh/connect.html Source0: connect-%{version}.c From 2135aa0096213d2cf7085f4c123e7611b3ff6d8c Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Thu, 31 Jan 2019 16:11:32 +0000 Subject: [PATCH 22/45] - Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild Signed-off-by: Fedora Release Engineering --- connect-proxy.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/connect-proxy.spec b/connect-proxy.spec index bd3f0f9..f0232f2 100644 --- a/connect-proxy.spec +++ b/connect-proxy.spec @@ -1,6 +1,6 @@ Name: connect-proxy Version: 1.100 -Release: 18%{?dist} +Release: 19%{?dist} Summary: SSH Proxy command helper License: GPLv2+ @@ -51,6 +51,9 @@ cp -p %{name}.1 $RPM_BUILD_ROOT%{_mandir}/man1/ %{_bindir}/%{name} %changelog +* Thu Jan 31 2019 Fedora Release Engineering - 1.100-19 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild + * Thu Jul 12 2018 Fedora Release Engineering - 1.100-18 - Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild From 5dbae3a7cab3495624edb918eaef2508bb5b98a1 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Wed, 24 Jul 2019 20:54:52 +0000 Subject: [PATCH 23/45] - Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild Signed-off-by: Fedora Release Engineering --- connect-proxy.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/connect-proxy.spec b/connect-proxy.spec index f0232f2..945c15f 100644 --- a/connect-proxy.spec +++ b/connect-proxy.spec @@ -1,6 +1,6 @@ Name: connect-proxy Version: 1.100 -Release: 19%{?dist} +Release: 20%{?dist} Summary: SSH Proxy command helper License: GPLv2+ @@ -51,6 +51,9 @@ cp -p %{name}.1 $RPM_BUILD_ROOT%{_mandir}/man1/ %{_bindir}/%{name} %changelog +* Wed Jul 24 2019 Fedora Release Engineering - 1.100-20 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild + * Thu Jan 31 2019 Fedora Release Engineering - 1.100-19 - Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild From d1e6bfc24a1ec1917daa83021ae0807c1b000709 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Tue, 28 Jan 2020 14:42:30 +0000 Subject: [PATCH 24/45] - Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild Signed-off-by: Fedora Release Engineering --- connect-proxy.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/connect-proxy.spec b/connect-proxy.spec index 945c15f..a2e26c8 100644 --- a/connect-proxy.spec +++ b/connect-proxy.spec @@ -1,6 +1,6 @@ Name: connect-proxy Version: 1.100 -Release: 20%{?dist} +Release: 21%{?dist} Summary: SSH Proxy command helper License: GPLv2+ @@ -51,6 +51,9 @@ cp -p %{name}.1 $RPM_BUILD_ROOT%{_mandir}/man1/ %{_bindir}/%{name} %changelog +* Tue Jan 28 2020 Fedora Release Engineering - 1.100-21 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild + * Wed Jul 24 2019 Fedora Release Engineering - 1.100-20 - Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild From 4dff3206cf2e2821892d141cdc1d116e6e22c9a7 Mon Sep 17 00:00:00 2001 From: jjkeijser Date: Tue, 12 May 2020 10:33:31 +0200 Subject: [PATCH 25/45] Initial commit --- README.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..fce8439 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# connect-proxy +Make socket connection using SOCKS4/5, telnet HTTP or HTTPS tunnel. From 13d09a67075c86d8db11830931db9969a5d3c314 Mon Sep 17 00:00:00 2001 From: Jan Just Keijser Date: Tue, 12 May 2020 10:58:02 +0200 Subject: [PATCH 26/45] First release using HTTPS proxy support --- INSTALL.md | 39 + README.md | 149 +++ connect.c | 3725 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 3913 insertions(+) create mode 100644 INSTALL.md create mode 100644 connect.c diff --git a/INSTALL.md b/INSTALL.md new file mode 100644 index 0000000..8af8d72 --- /dev/null +++ b/INSTALL.md @@ -0,0 +1,39 @@ +# connect-proxy +Make socket connection using SOCKS4/5, telnet HTTP or HTTPS tunnel. + +************************************************************************* + +QUICK START: + + Unix: + gcc -o connect connect.c -lssl -lcrypto + +************************************************************************* + +The development version can be found here: + + https://github.com/jjkeijser/connect-proxy/ + + +How To Compile +============== +On Linux/UNIX environment: + + gcc -o connect connect.c -lssl -lcrypto + +Or using a specific OpenSSL installation: + + gcc -o connect connect.c -I../openssl-1.1.1g/include + -L../openssl-1.1.1g -lssl -lcrypto + +The default CA certificate file is the RHEL/CentOS/Fedora default: + + /etc/pki/tls/certs/ca-bundle.crt + +You can specify an alternative location using + + gcc -o connect connect.c -D__DEFAULT_CA_PATH__=\"/some/path\" + -lssl -lcrypto + +(mind the quotes!) + diff --git a/README.md b/README.md index fce8439..be16042 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,151 @@ # connect-proxy Make socket connection using SOCKS4/5, telnet HTTP or HTTPS tunnel. + +Based on connect.c from Shun-ichi GOTO +* Added HTTPS proxy support +* Made code gcc-9 and valgrind clean + +How To Compile +============== +On Linux/UNIX environment: + + $ gcc connect.c -o connect -lssl -lcrypto + +How To Use +========== +* You can specify proxy method in an environment variable or in a command line option. +* usage: + + ./connect [-dnhstx45] [-p local-port][-R resolve] [-w timeout] + [-S [user@]socks-server[:port]] + [-H [user@]proxy-server[:port]] + [-T proxy-server[:port] [-c telnet-proxy-command] + [-X [user@]proxy-server[:port]] + [--help] + [--socks-server [user@]socks-server[:port]] + [--http-proxy [user@]proxy-server[:port]] + [--telnet-proxy proxy-server[:port] + [--https-proxy [user@]proxy-server[:port]] + [--https-proxy-certname name] + [--https-user-cert certfile.pem] + [--https-user-key keyfile.pem] + [--no-check-certificate] + host port + +* "host" and "port" is for the target hostname and port-number to connect to. +* The '-H' or '--http-proxy' option specifies a hostname and port number of the http proxy server to + relay. If port is omitted, 80 is used. You can specify this value in the environment variable + HTTP_PROXY and pass the '-h' option to use it. +* The '-X' or '--https-proxy' option specifies a hostname and port number of the https proxy server to + relay. If port is omitted, 443 is used. You can specify this value in the environment variable + HTTPS_PROXY and pass the '-x' option to use it. +* The '-S' or '--socks-proxy' option specifies the hostname and port number of the SOCKS server to + relay. Like '-H', port number can be omitted and the default is 1080. You can also specify this + value pair in the environment variable SOCKS5_SERVER and give the '-s' option to use it. +* The '-4' and the '-5' options are for specifying SOCKS relaying and indicates protocol version + to use. It is valid only when used with '-s' or '-S'. Default is '-5' (protocol version 5) +* The '-R' option is for specifying method to resolve the hostname. Three keywords ("local", + "remote", "both") or dot-notation IP address are acceptable. The keyword "both" means, "Try local + first, then remote". If a dot-notation IP address is specified, use this host as nameserver. The + default is "remote" for SOCKS5 or "local" for others. On SOCKS4 protocol, remote resolving method + ("remote" and "both") requires protocol 4a supported server. +* The '-p' option will forward a local TCP port instead of using the standard input and output. +* The '-P' option is same to '-p' except keep remote session. The program repeats waiting the port + with holding remote session without + disconnecting. To disconnect the remote session, send EOF to stdin or kill the program. +* The '-w' option specifys timeout seconds for making connection with TARGET host. +* The '-d' option is used for debug. If you fail to connect, use this and check request to and + response from server. + + You can omit the "port" argument when program name is special format containing port number +itself. For example, + + $ ln -s connect connect-25 + means this connect-25 command is spcifying port number 25 already so you need not 2nd argument +(and ignored if specified). +* To use proxy, this example is for SOCKS5 connection to connect to 'host' at port 25 via SOCKS5 +server on 'firewall' host. + + $ connect -S firewall host 25 + or + + $ SOCKS5_SERVER=firewall; export SOCKS5_SERVER + $ connect -s host 25 +* For a HTTP-PROXY connection: + + $ connect -H proxy-server:8080 host 25 + or + + $ HTTP_PROXY=proxy-server:8080; export HTTP_PROXY + $ connect -h host 25 +* For a HTTPS-PROXY connection: + + $ connect -H proxy-server:443 host 25 + or + + $ HTTPS_PROXY=proxy-server:443; export HTTPS_PROXY + $ connect -x host 25 + +TIPS +==== +* Connect.c doesn't have any configuration to specify the SOCKS server. + If you are a mobile user, this limitation might bother you. However, + You can compile connect.c and link with other standard SOCKS library + like the NEC SOCKS5 library or Dante. This means connect.c is + socksified and uses a configration file like to other SOCKSified + network commands and you can switch configuration file any time + (ex. when ppp startup) that brings you switching of SOCKS server for + connect.c in same way with other commands. For this case, you can + write ~/.ssh/config like this: + + ProxyCommand connect -n %h %p + +SOCKS5 authentication +===================== +* Only USER/PASS authentication is supported. + +HTTP Proxy authentication +========================= +* Only BASIC scheme is supported. + +HTTPS proxy authentication +========================== +* BASIC scheme is supported. +* The server certificate can be verified against a CA certificate (or list of CA + certficates) by specifying either '--https-ca-file' or '--https-ca-path'. + (default file: /etc/pki/tls/certs/ca-bundle.crt). +* By default, the server certificate name (/CN=...) is checked against the hostname + of the https_proxy server. It is possible to specify an alternative name using + '--http-proxy-certname'. +* You can disable server certificate verification by specifying '--no-certificate-check'. +* Certificate based authentication is supported. Use the '--https-user-cert' and + '--https-user-key' parameters to specify the user certificate and key. If the private + key is protected using a passphrase, the $SSH_ASKPASS program will be used to query the user. + +The following environment variables can be used to specify the above parameters: +* HTTPS proxy server: $HTTPS_PROXY +* proxy user: $HTTPS_PROXY_USER +* proxy password: $HTTPS_PROXY_PASSWORD +* server certificate name: $HTTPS_PROXY_CERTNAME +* CA certificate name: $HTTPS_PROXY_CA_FILE +* CA certificate path: $HTTPS_PROXY_CA_PATH +* client certificate file: $HTTPS_PROXY_USERCERT +* client privatekey file: $HTTPS_PROXY_USERKEY + +Authentication information +========================== +The User name for authentication is specifed by an environment variable or system login name. And +password is specified from environment variable or external program (specified in $SSH_ASKPASS) or +tty. +The following environment variable is used for specifying user name. +- SOCKS: $SOCKS5_USER, $LOGNAME, $USER +- HTTP Proxy: $HTTP_PROXY_USER, $LOGNAME, $USER +- HTTPS Proxy: $HTTPS_PROXY_USER, $LOGNAME, $USER + +ssh-askpass support +=================== +You can use ssh-askpass (came from OpenSSH or else) to specify password on graphical environment +(X-Window or MS Windows). To use this, set program name to environment variable SSH_ASKPASS. On +UNIX, X-Window must be required, so $DISPLAY environment variable is also needed. On Win32 +environment, $DISPLAY is not mentioned. + diff --git a/connect.c b/connect.c new file mode 100644 index 0000000..dcfd21c --- /dev/null +++ b/connect.c @@ -0,0 +1,3725 @@ +/*********************************************************************** + * connect.c -- Make socket connection using SOCKS4/5 and HTTP tunnel. + * + * Copyright (c) 2000-2006 Shun-ichi Goto + * Copyright (c) 2002, J. Grant (English Corrections) + * Copyright (c) 2020, J.J. Keijser (added SSL support, made -Wpendantic clean) + * + * 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 + * of the License, 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * --------------------------------------------------------- + * PROJECT: My Test Program + * AUTHOR: Shun-ichi GOTO + * CREATE: Wed Jun 21, 2000 + * REVISION: $Revision: 100 $ + * --------------------------------------------------------- + * + * Getting Source + * ============== + * + * Recent version of 'connect.c' is available from + * http://www.taiyo.co.jp/~gotoh/ssh/connect.c + * + * Related tool, ssh-askpass.exe (alternative ssh-askpass on UNIX) + * is available: + * http://www.taiyo.co.jp/~gotoh/ssh/ssh-askpass.exe.gz + * + * See more detail: + * http://www.taiyo.co.jp/~gotoh/ssh/connect.html + * + * How To Compile + * ============== + * + * On UNIX environment: + * $ gcc connect.c -o connect + * + * On SOLARIS: + * $ gcc -o connect -lresolv -lsocket -lnsl connect.c + * + * on Win32 environment: + * $ cl connect.c wsock32.lib advapi32.lib + * or + * $ bcc32 connect.c wsock32.lib advapi32.lib + * or + * $ gcc connect.c -o connect + * + * on Mac OS X environment: + * $ gcc connect.c -o connect -lresolv + * or + * $ gcc connect.c -o connect -DBIND_8_COMPAT=1 + * + * How To Use + * ========== + * + * You can specify proxy method in an environment variable or in a + * command line option. + * + * usage: connect [-dnhst45] [-R resolve] [-p local-port] [-w sec] + * [-H [user@]proxy-server[:port]] + * [-S [user@]socks-server[:port]] + * [-T proxy-server[:port]] + * [-c telnet proxy command] + * host port + * + * "host" and "port" is for the target hostname and port-number to + * connect to. + * + * The -H option specifys a hostname and port number of the http proxy + * server to relay. If port is omitted, 80 is used. You can specify this + * value in the environment variable HTTP_PROXY and pass the -h option + * to use it. + * + * The -S option specifys the hostname and port number of the SOCKS + * server to relay. Like -H, port number can be omitted and the default + * is 1080. You can also specify this value pair in the environment + * variable SOCKS5_SERVER and give the -s option to use it. + * + * The '-4' and the '-5' options are for specifying SOCKS relaying and + * indicates protocol version to use. It is valid only when used with + * '-s' or '-S'. Default is '-5' (protocol version 5) + * + * The '-R' option is for specifying method to resolve the + * hostname. Three keywords ("local", "remote", "both") or dot-notation + * IP address are acceptable. The keyword "both" means, "Try local + * first, then remote". If a dot-notation IP address is specified, use + * this host as nameserver. The default is "remote" for SOCKS5 or + * "local" for others. On SOCKS4 protocol, remote resolving method + * ("remote" and "both") requires protocol 4a supported server. + * + * The '-p' option will forward a local TCP port instead of using the + * standard input and output. + * + * The '-P' option is same to '-p' except keep remote session. The + * program repeats waiting the port with holding remote session without + * disconnecting. To disconnect the remote session, send EOF to stdin or + * kill the program. + * + * The '-w' option specifys timeout seconds for making connection with + * TARGET host. + * + * The '-d' option is used for debug. If you fail to connect, use this + * and check request to and response from server. + * + * You can omit the "port" argument when program name is special format + * containing port number itself. For example, + * $ ln -s connect connect-25 + * means this connect-25 command is spcifying port number 25 already + * so you need not 2nd argument (and ignored if specified). + * + * To use proxy, this example is for SOCKS5 connection to connect to + * 'host' at port 25 via SOCKS5 server on 'firewall' host. + * $ connect -S firewall host 25 + * or + * $ SOCKS5_SERVER=firewall; export SOCKS5_SERVER + * $ connect -s host 25 + * + * For a HTTP-PROXY connection: + * $ connect -H proxy-server:8080 host 25 + * or + * $ HTTP_PROXY=proxy-server:8080; export HTTP_PROXY + * $ connect -h host 25 + * To forward a local port, for example to use ssh: + * $ connect -p 5550 -H proxy-server:8080 host 22 + * ($ ssh -l user -p 5550 localhost ) + * + * TIPS + * ==== + * + * Connect.c doesn't have any configuration to specify the SOCKS server. + * If you are a mobile user, this limitation might bother you. However, + * You can compile connect.c and link with other standard SOCKS library + * like the NEC SOCKS5 library or Dante. This means connect.c is + * socksified and uses a configration file like to other SOCKSified + * network commands and you can switch configuration file any time + * (ex. when ppp startup) that brings you switching of SOCKS server for + * connect.c in same way with other commands. For this case, you can + * write ~/.ssh/config like this: + * + * ProxyCommand connect -n %h %p + * + * SOCKS5 authentication + * ===================== + * + * Only USER/PASS authentication is supported. + * + * Proxy authentication + * ==================== + * + * Only BASIC scheme is supported. + * + * Authentication informations + * =========================== + * + * User name for authentication is specifed by an environment variable + * or system login name. And password is specified from environment + * variable or external program (specified in $SSH_ASKPASS) or tty. + * + * Following environment variable is used for specifying user name. + * SOCKS: $SOCKS5_USER, $LOGNAME, $USER + * HTTP Proxy: $HTTP_PROXY_USER, $LOGNAME, $USER + * + * ssh-askpass support + * =================== + * + * You can use ssh-askpass (came from OpenSSH or else) to specify + * password on graphical environment (X-Window or MS Windows). To use + * this, set program name to environment variable SSH_ASKPASS. On UNIX, + * X-Window must be required, so $DISPLAY environment variable is also + * needed. On Win32 environment, $DISPLAY is not mentioned. + * + * Related Informations + * ==================== + * + * SOCKS5 -- RFC 1928, RFC 1929, RFC 1961 + * NEC SOCKS Reference Implementation is available from: + * http://www.socks.nec.com + * DeleGate version 5 or earlier can be SOCKS4 server, + * and version 6 can be SOCKS5 and SOCKS4 server. + * and version 7.7.0 or later can be SOCKS5 and SOCKS4a server. + * http://www.delegate.org/delegate/ + * + * HTTP-Proxy -- + * Many http proxy servers supports this, but https should + * be allowed as configuration on your host. + * For example on DeleGate, you should add "https" to the + * "REMITTABLE" parameter to allow HTTP-Proxy like this: + * delegated -Pxxxx ...... REMITTABLE="+,https" ... + * + * Hypertext Transfer Protocol -- HTTP/1.1 -- RFC 2616 + * HTTP Authentication: Basic and Digest Access Authentication -- RFC 2617 + * For proxy authentication, refer these documents. + * + ***********************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __CYGWIN32__ +#undef _WIN32 +#endif + +#ifdef _WIN32 +#include +#include +#include +#include +#include +#else /* !_WIN32 */ +#include +#include +#include +#include +#ifndef __hpux +#include +#endif /* __hpux */ +#include +#include +#include +#include +#if !defined(_WIN32) && !defined(__CYGWIN32__) +#define WITH_RESOLVER 1 +#include +#include +#else /* not ( not _WIN32 && not __CYGWIN32__) */ +#undef WITH_RESOLVER +#endif /* not ( not _WIN32 && not __CYGWIN32__) */ +#endif /* !_WIN32 */ + +#ifdef _WIN32 +#define ECONNRESET WSAECONNRESET +#endif /* _WI32 */ + +#include +#include +#include +#include +#include +#include +#include + +/* Microsoft Visual C/C++ has _snprintf() and _vsnprintf() */ +#ifdef _MSC_VER +#define snprintf _snprintf +#define vsnprintf _vsnprintf +#endif + +/* consider Borland C */ +#ifdef __BORLANDC__ +#define _kbhit kbhit +#define _setmode setmode +#endif + +/* help message. + Win32 environment does not support -R option (vc and cygwin) + Win32 native compilers does not support -w option, yet (vc) +*/ +static char *usage1 = "usage: %s [-dnhstx45] [-p local-port]" +#ifdef _WIN32 +#ifdef __CYGWIN32__ +"[-w timeout] \n" /* cygwin cannot -R */ +#else /* not __CYGWIN32__ */ +" \n" /* VC cannot -w nor -R */ +#endif /* not __CYGWIN32__ */ +#else /* not _WIN32 */ +/* help message for UNIX */ +"[-R resolve] [-w timeout] \n" +#endif /* not _WIN32 */ +" [-S [user@]socks-server[:port]]\n" +" [-H [user@]proxy-server[:port]]\n" +" [-T proxy-server[:port] [-c telnet-proxy-command]\n" +" [-X [user@]proxy-server[:port]]\n"; + +static char *usage2 = +" [--help]\n" +" [--socks-server [user@]socks-server[:port]]\n" +" [--http-proxy [user@]proxy-server[:port]]\n" +" [--telnet-proxy proxy-server[:port]\n" +" [--https-proxy [user@]proxy-server[:port]]\n" +" [--https-proxy-certname name]\n" +" [--https-user-cert certfile.pem]\n" +" [--https-user-key keyfile.pem]\n" +" [--no-check-certificate]\n" +" host port\n"; + +/* name of this program */ +char *progname = NULL; +static char *progdesc = "connect --- simple relaying command via proxy."; +static char *revstr = "2.0"; + +/* set of character for strspn() */ +const char *digits = "0123456789"; +const char *dotdigits = "0123456789."; + +/* options */ +int f_debug = 0; + +/* report flag to hide secure information */ +int f_report = 1; + +int connect_timeout = 0; + +/* local input type */ +#define LOCAL_STDIO 0 +#define LOCAL_SOCKET 1 +char *local_type_names[] = { "stdio", "socket" }; +int local_type = LOCAL_STDIO; +u_short local_port = 0; /* option 'p' */ +int f_hold_session = 0; /* option 'P' */ + +char *telnet_command = "telnet %h %p"; + + + +/* utiity types, pair holder of number and string */ +typedef struct { + int num; + const char *str; +} LOOKUP_ITEM; + +/* relay method, server and port */ +#define METHOD_UNDECIDED 0 +#define METHOD_DIRECT 1 +#define METHOD_SOCKS 2 +#define METHOD_HTTP 3 +#define METHOD_HTTPS 4 +#define METHOD_TELNET 5 +char *method_names[] = { "UNDECIDED", "DIRECT", "SOCKS", "HTTP", "HTTPS", "TELNET" }; + +int relay_method = METHOD_UNDECIDED; /* relaying method */ +char *relay_host = NULL; /* hostname of relay server */ +u_short relay_port = 0; /* port of relay server */ +char *relay_user = NULL; /* user name for auth */ + +/* HTTPS specific stuff*/ +#if OPENSSL_VERSION_NUMBER < 0x10100000L +#define TLS_method SSLv23_method +#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */ +SSL *ssl = NULL; +char sslbuf[65536]; +size_t sslbuf_idx = 0; + +#ifdef __DEFAULT_CA_PATH__ +char *https_ca_file = __DEFAULT_CA_PATH__; /* system wide default */ +#else +char *https_ca_file = "/etc/pki/tls/certs/ca-bundle.crt"; /* system wide default */ +#endif +char *https_ca_path = NULL; +char *https_usercert_file = NULL; +char *https_userkey_file = NULL; +int https_check_certificate = 1; /* do a proxy server cert check by default */ +char *https_proxy_certname = NULL; + +/* destination target host and port */ +char *dest_host = NULL; +struct sockaddr_in dest_addr; +u_short dest_port = 0; + +/* informations for SOCKS */ +#define SOCKS5_REP_SUCCEEDED 0x00 /* succeeded */ +#define SOCKS5_REP_FAIL 0x01 /* general SOCKS serer failure */ +#define SOCKS5_REP_NALLOWED 0x02 /* connection not allowed by ruleset */ +#define SOCKS5_REP_NUNREACH 0x03 /* Network unreachable */ +#define SOCKS5_REP_HUNREACH 0x04 /* Host unreachable */ +#define SOCKS5_REP_REFUSED 0x05 /* connection refused */ +#define SOCKS5_REP_EXPIRED 0x06 /* TTL expired */ +#define SOCKS5_REP_CNOTSUP 0x07 /* Command not supported */ +#define SOCKS5_REP_ANOTSUP 0x08 /* Address not supported */ +#define SOCKS5_REP_INVADDR 0x09 /* Inalid address */ + +LOOKUP_ITEM socks5_rep_names[] = { + { SOCKS5_REP_SUCCEEDED, "succeeded"}, + { SOCKS5_REP_FAIL, "general SOCKS server failure"}, + { SOCKS5_REP_NALLOWED, "connection not allowed by ruleset"}, + { SOCKS5_REP_NUNREACH, "Network unreachable"}, + { SOCKS5_REP_HUNREACH, "Host unreachable"}, + { SOCKS5_REP_REFUSED, "connection refused"}, + { SOCKS5_REP_EXPIRED, "TTL expired"}, + { SOCKS5_REP_CNOTSUP, "Command not supported"}, + { SOCKS5_REP_ANOTSUP, "Address not supported"}, + { SOCKS5_REP_INVADDR, "Invalid address"}, + { -1, NULL } +}; + +/* SOCKS5 authentication methods */ +#define SOCKS5_AUTH_REJECT 0xFF /* No acceptable auth method */ +#define SOCKS5_AUTH_NOAUTH 0x00 /* without authentication */ +#define SOCKS5_AUTH_GSSAPI 0x01 /* GSSAPI */ +#define SOCKS5_AUTH_USERPASS 0x02 /* User/Password */ +#define SOCKS5_AUTH_CHAP 0x03 /* Challenge-Handshake Auth Proto. */ +#define SOCKS5_AUTH_EAP 0x05 /* Extensible Authentication Proto. */ +#define SOCKS5_AUTH_MAF 0x08 /* Multi-Authentication Framework */ + +#define SOCKS4_REP_SUCCEEDED 90 /* rquest granted (succeeded) */ +#define SOCKS4_REP_REJECTED 91 /* request rejected or failed */ +#define SOCKS4_REP_IDENT_FAIL 92 /* cannot connect identd */ +#define SOCKS4_REP_USERID 93 /* user id not matched */ + +LOOKUP_ITEM socks4_rep_names[] = { + { SOCKS4_REP_SUCCEEDED, "request granted (succeeded)"}, + { SOCKS4_REP_REJECTED, "request rejected or failed"}, + { SOCKS4_REP_IDENT_FAIL, "cannot connect identd"}, + { SOCKS4_REP_USERID, "user id not matched"}, + { -1, NULL } +}; + +#define RESOLVE_UNKNOWN 0 +#define RESOLVE_LOCAL 1 +#define RESOLVE_REMOTE 2 +#define RESOLVE_BOTH 3 +char *resolve_names[] = { "UNKNOWN", "LOCAL", "REMOTE", "BOTH" }; + +int socks_version = 5; /* SOCKS protocol version */ +int socks_resolve = RESOLVE_UNKNOWN; +struct sockaddr_in socks_ns; +char *socks5_auth = NULL; + +/* Environment variable names */ +#define ENV_SOCKS_SERVER "SOCKS_SERVER" /* SOCKS server */ +#define ENV_SOCKS5_SERVER "SOCKS5_SERVER" +#define ENV_SOCKS4_SERVER "SOCKS4_SERVER" + +#define ENV_SOCKS_RESOLVE "SOCKS_RESOLVE" /* resolve method */ +#define ENV_SOCKS5_RESOLVE "SOCKS5_RESOLVE" +#define ENV_SOCKS4_RESOLVE "SOCKS4_RESOLVE" + +#define ENV_SOCKS5_USER "SOCKS5_USER" /* auth user for SOCKS5 */ +#define ENV_SOCKS4_USER "SOCKS4_USER" /* auth user for SOCKS4 */ +#define ENV_SOCKS_USER "SOCKS_USER" /* auth user for SOCKS */ +#define ENV_SOCKS5_PASSWD "SOCKS5_PASSWD" /* auth password for SOCKS5 */ +#define ENV_SOCKS5_PASSWORD "SOCKS5_PASSWORD" /* old style */ + +#define ENV_HTTP_PROXY "HTTP_PROXY" /* common env var */ +#define ENV_HTTP_PROXY_USER "HTTP_PROXY_USER" /* auth user */ +#define ENV_HTTP_PROXY_PASSWORD "HTTP_PROXY_PASSWORD" /* auth password */ + +#define ENV_HTTPS_PROXY "HTTPS_PROXY" /* common env var */ +#define ENV_HTTPS_PROXY_USER "HTTPS_PROXY_USER" /* auth user */ +#define ENV_HTTPS_PROXY_PASSWORD "HTTPS_PROXY_PASSWORD" /* auth password */ +#define ENV_HTTPS_PROXY_CERTNAME "HTTPS_PROXY_CERTNAME" /* Certificate /CN name of the proxy server */ +#define ENV_HTTPS_PROXY_CA_FILE "HTTPS_PROXY_CA_FILE" /* CA certificate file of proxy server */ +#define ENV_HTTPS_PROXY_CA_PATH "HTTPS_PROXY_CA_PATH" /* CA certificate path to verify proxy server */ +#define ENV_HTTPS_PROXY_USERCERT "HTTPS_PROXY_USERCERT" /* User certificiate for authentication */ +#define ENV_HTTPS_PROXY_USERKEY "HTTPS_PROXY_USERKEY" /* User certificiate for authentication */ + +#define ENV_TELNET_PROXY "TELNET_PROXY" /* common env var */ + +#define ENV_CONNECT_USER "CONNECT_USER" /* default auth user name */ +#define ENV_CONNECT_PASSWORD "CONNECT_PASSWORD" /* default auth password */ + +#define ENV_SOCKS_DIRECT "SOCKS_DIRECT" /* addr-list for non-proxy */ +#define ENV_SOCKS5_DIRECT "SOCKS5_DIRECT" +#define ENV_SOCKS4_DIRECT "SOCKS4_DIRECT" +#define ENV_HTTP_DIRECT "HTTP_DIRECT" +#define ENV_HTTPS_DIRECT "HTTPS_DIRECT" +#define ENV_CONNECT_DIRECT "CONNECT_DIRECT" + +#define ENV_SOCKS5_AUTH "SOCKS5_AUTH" +#define ENV_SSH_ASKPASS "SSH_ASKPASS" /* askpass program */ + +/* Prefix string of HTTP_PROXY */ +#define HTTP_PROXY_PREFIX "http://" +/* Prefix string of HTTP_PROXY */ +#define HTTPS_PROXY_PREFIX "https://" +#define PROXY_AUTH_NONE 0 +#define PROXY_AUTH_BASIC 1 +#define PROXY_AUTH_DIGEST 2 +int proxy_auth_type = PROXY_AUTH_NONE; + +/* reason of end repeating */ +#define REASON_UNK -2 +#define REASON_ERROR -1 +#define REASON_CLOSED_BY_LOCAL 0 +#define REASON_CLOSED_BY_REMOTE 1 + +/* return value of relay start function. */ +#define START_FORBIDDEN -2 +#define START_ERROR -1 +#define START_OK 0 +#define START_RETRY 1 + +/* socket related definitions */ +#ifndef _WIN32 +#define SOCKET int +#endif +#ifndef SOCKET_ERROR +#define SOCKET_ERROR -1 +#endif + +#ifdef _WIN32 +#define socket_errno() WSAGetLastError() +#else /* !_WIN32 */ +#define closesocket close +#define socket_errno() (errno) +#endif /* !_WIN32 */ + +#ifdef _WIN32 +#define popen _popen +#endif /* WIN32 */ + +/* packet operation macro */ +#define PUT_BYTE(ptr,data) (*(char*)ptr = data) + + +void cleanup_and_exit( int exit_code ) +{ + /* clean up to make valgrind happy */ + if ( relay_host ) + free( relay_host ); + if ( relay_user ) + free( relay_user ); + if ( ssl ) + { + SSL_free (ssl); +#if OPENSSL_VERSION_NUMBER < 0x010100000L + ERR_remove_state (0); +#endif + ERR_free_strings(); + EVP_cleanup(); + SSL_COMP_free_compression_methods(); + CRYPTO_cleanup_all_ex_data(); + } + +#ifdef _WIN32 + WSACleanup(); +#endif /* _WIN32 */ + + exit( exit_code ); +} + +void +print_help( int exit_code ) +{ + fprintf( stderr, "%s\nVersion %s\n", progdesc, revstr ); + fprintf( stderr, usage1, progname ); + fprintf( stderr, usage2 ); + exit( exit_code ); +} + +/* debug message output */ +void +debug( const char *fmt, ... ) +{ + va_list args; + if ( f_debug ) { + va_start( args, fmt ); + fprintf(stderr, "DEBUG: "); + vfprintf( stderr, fmt, args ); + va_end( args ); + } +} + +void +debug_( const char *fmt, ... ) /* without prefix */ +{ + va_list args; + if ( f_debug ) { + va_start( args, fmt ); + vfprintf( stderr, fmt, args ); + va_end( args ); + } +} + +/* error message output */ +void +error( const char *fmt, ... ) +{ + va_list args; + va_start( args, fmt ); + fprintf(stderr, "ERROR: "); + vfprintf( stderr, fmt, args ); + va_end( args ); +} + +/* print out the SSL error stack */ +void +ssl_error( const char *label ) +{ + int err; + + if (ERR_peek_error() == 0) debug( "Hit ssl_error code=0 on '%s'\n", label); + while ((err = ERR_get_error())) + { + error("%s: %s\n", label, ERR_error_string(err, NULL)); + } +} + +void +fatal( const char *fmt, ... ) +{ + va_list args; + va_start( args, fmt ); + fprintf(stderr, "FATAL: "); + vfprintf( stderr, fmt, args ); + va_end( args ); + cleanup_and_exit (EXIT_FAILURE); +} + + +void * +xmalloc (size_t size) +{ + void *ret = malloc(size); + if (ret == NULL) + fatal("Cannot allocate memory: %d bytes.\n", size); + return ret; +} + +char * +downcase( char *str ) +{ + char *buf = str; + while ( *buf ) { + if ( isupper(*buf) ) + *buf = tolower( *buf ); + buf++; + } + return str; /* return converted arg */ +} + +char * +expand_host_and_port (const char *fmt, const char *host, int port) +{ + const char *src; + char *buf, *dst; + size_t len = strlen(fmt) + strlen(host) + 20; + buf = xmalloc (len); + dst = buf; + src = fmt; + + while (*src) { + if (*src == '%') { + switch (src[1]) { + case 'h': + strcpy (dst, host); + src += 2; + break; + case 'p': + snprintf (dst, len, "%d", port); + src += 2; + break; + default: + src ++; + break; + } + dst = buf + strlen (buf); + } else if (*src == '\\') { + switch (src[1]) { + case 'r': /* CR */ + *dst++ = '\r'; + src += 2; + break; + case 'n': /* LF */ + *dst++ = '\n'; + src += 2; + break; + case 't': /* TAB */ + *dst++ = '\t'; + src += 2; + break; + default: + src ++; + break; + } + } else { + /* usual */ + *dst++ = *src++; + } + *dst = '\0'; + } + assert (strlen(buf) < len); + return buf; +} + + +int +lookup_resolve( char *str ) +{ + int ret; + + if ( strncasecmp( str, "both", 4 ) == 0 ) + ret = RESOLVE_BOTH; + else if ( strncasecmp( str, "remote", 6 ) == 0 ) + ret = RESOLVE_REMOTE; + else if ( strncasecmp( str, "local", 5 ) == 0 ) + ret = RESOLVE_LOCAL; + else if ( strspn(str, dotdigits) == strlen(str) ) { +#ifndef WITH_RESOLVER + fatal("Sorry, you can't specify to resolve the hostname with the -R option on Win32 environment."); +#endif /* not WITH_RESOLVER */ + ret = RESOLVE_LOCAL; /* this case is also 'local' */ + socks_ns.sin_addr.s_addr = inet_addr(str); + socks_ns.sin_family = AF_INET; + } + else + ret = RESOLVE_UNKNOWN; + return ret; +} + +char * +getusername(void) +{ +#ifdef _WIN32 + static char buf[1024]; + DWORD size = sizeof(buf); + buf[0] = '\0'; + GetUserName( buf, &size); + return buf; +#else /* not _WIN32 */ + struct passwd *pw = getpwuid(getuid()); + if ( pw == NULL ) + fatal("getpwuid() failed for uid: %d\n", getuid()); + return pw->pw_name; +#endif /* not _WIN32 */ +} + +/* expect + check STR is begin with substr with case-ignored comparison. + Return 1 if matched, otherwise 0. +*/ +int +expect( char *str, char *substr) +{ + int len = strlen(substr); + while ( 0 < len-- ) { + if ( toupper(*str) != toupper(*substr) ) + return 0; /* not matched */ + str++, substr++; + } + return 1; /* good, matched */ +} + + +/** PARAMETER operation **/ +#define PARAMETER_FILE "/etc/connectrc" +#define PARAMETER_DOTFILE ".connectrc" +typedef struct { + char* name; + char* value; +} PARAMETER_ITEM; +PARAMETER_ITEM parameter_table[] = { + { ENV_SOCKS_SERVER, NULL }, + { ENV_SOCKS5_SERVER, NULL }, + { ENV_SOCKS4_SERVER, NULL }, + { ENV_SOCKS_RESOLVE, NULL }, + { ENV_SOCKS5_RESOLVE, NULL }, + { ENV_SOCKS4_RESOLVE, NULL }, + { ENV_SOCKS5_USER, NULL }, + { ENV_SOCKS5_PASSWD, NULL }, + { ENV_SOCKS5_PASSWORD, NULL }, + { ENV_HTTP_PROXY, NULL }, + { ENV_HTTP_PROXY_USER, NULL }, + { ENV_HTTP_PROXY_PASSWORD, NULL }, + { ENV_HTTPS_PROXY, NULL }, + { ENV_HTTPS_PROXY_USER, NULL }, + { ENV_HTTPS_PROXY_PASSWORD, NULL }, + { ENV_CONNECT_USER, NULL }, + { ENV_CONNECT_PASSWORD, NULL }, + { ENV_SSH_ASKPASS, NULL }, + { ENV_SOCKS5_DIRECT, NULL }, + { ENV_SOCKS4_DIRECT, NULL }, + { ENV_SOCKS_DIRECT, NULL }, + { ENV_HTTP_DIRECT, NULL }, + { ENV_HTTPS_DIRECT, NULL }, + { ENV_CONNECT_DIRECT, NULL }, + { ENV_SOCKS5_AUTH, NULL }, + { NULL, NULL } +}; + +PARAMETER_ITEM* +find_parameter_item(const char* name) +{ + int i; + for( i = 0; parameter_table[i].name != NULL; i++ ){ + if ( strcmp(name, parameter_table[i].name) == 0 ) + return ¶meter_table[i]; + } + return NULL; +} + +void +read_parameter_file_1(const char* name) +{ + FILE* f; + int line; + char lbuf[1025]; + f = fopen(name, "r"); + if( f ){ + debug("Reading parameter file(%s)\n", name); + for ( line = 1; fgets(lbuf, 1024, f); line++ ) { + char *p, *q, *param, *value; + p = strchr(lbuf, '\n'); + if ( p == NULL ) + fatal("%s:%d: buffer overflow\n", name, line); + *p = '\0'; + p = strchr(lbuf, '#'); + if ( p ) + *p = '\0'; + for ( p = lbuf; *p; p++ ) + if( *p != ' ' && *p != '\t' ) break; + if ( *p == '\0' ) continue; + param = p; + p = strchr(p, '='); + if ( p == NULL ) { + error("%s:%d: missing equal sign\n", name, line); + continue; + } + for ( q = p - 1; q >= lbuf; q-- ) + if ( *q != ' ' && *q != '\t' ) break; + *++q = '\0'; + for ( ++p; *p; p++ ) + if ( *p != ' ' && *p != '\t' ) break; + value = p; + for ( ; *p; p++ ); + for ( p--; p >= lbuf; p-- ) + if ( *p != ' ' && *p != '\t' ) break; + *++p = '\0'; + if ( param && value ) { + PARAMETER_ITEM *item; + item = find_parameter_item(param); + if ( item == NULL ) { + error("%s:%d: unknown parameter `%s'\n", name, line, param); + continue; + } + item->value = strdup(value); + debug("Parameter `%s' is set to `%s'\n", param, value); + } + } + } +} + +void +read_parameter_file(void) +{ +#if !defined(_WIN32) || defined(cygwin) + char *name; + struct passwd *pw; +#endif + + read_parameter_file_1(PARAMETER_FILE); +#if !defined(_WIN32) || defined(cygwin) + pw = getpwuid(getuid()); + if ( pw == NULL ) + fatal("getpwuid() failed for uid: %d\n", getuid()); + name = xmalloc(strlen(pw->pw_dir) + strlen(PARAMETER_DOTFILE) + 2); + strcpy(name, pw->pw_dir); + strcat(name, "/" PARAMETER_DOTFILE); + read_parameter_file_1(name); + free(name); +#endif /* _WIN32 */ +} + +char* +getparam(const char* name) +{ + char *value = getenv(name); + if ( value == NULL ){ + PARAMETER_ITEM *item = find_parameter_item(name); + if ( item != NULL ) + value = item->value; + } + return value; +} + + +/** DIRECT connection **/ +#define MAX_DIRECT_ADDR_LIST 256 + +struct ADDRPAIR { + struct in_addr addr; + struct in_addr mask; + char *name; + int negative; +}; + +struct ADDRPAIR direct_addr_list[MAX_DIRECT_ADDR_LIST]; +int n_direct_addr_list = 0; + +void +mask_addr (void *addr, void *mask, int addrlen) +{ + char *a, *m; + a = addr; + m = mask; + while ( 0 < addrlen-- ) + *a++ &= *m++; +} + +int +add_direct_addr (struct in_addr *addr, struct in_addr *mask, int negative) +{ + struct in_addr iaddr; + char *s; + if ( MAX_DIRECT_ADDR_LIST <= n_direct_addr_list ) { + error("direct address table is full!\n"); + return -1; + } + iaddr = *addr; + mask_addr(&iaddr, mask, sizeof(iaddr)); + s = strdup(inet_ntoa(iaddr)); + debug("adding direct addr entry: %s%s/%s\n", + negative? "!": "", s, inet_ntoa(*mask)); + free(s); + memcpy( &direct_addr_list[n_direct_addr_list].addr, + &iaddr, sizeof(iaddr)); + memcpy( &direct_addr_list[n_direct_addr_list].mask, + mask, sizeof(*mask)); + direct_addr_list[n_direct_addr_list].name = NULL; + direct_addr_list[n_direct_addr_list].negative = negative; + n_direct_addr_list++; + return 0; +} + + +/* add domain/host name entry to direct name table */ +int +add_direct_host(char *name, int negative) +{ + if ( MAX_DIRECT_ADDR_LIST <= n_direct_addr_list ) { + error("direct address table is full!\n"); + return -1; + } + if (*name == '*') + name++; + if (*name == '.') + name++; + debug("adding direct name entry: %s%s\n", negative? "!": "", name); + direct_addr_list[n_direct_addr_list].name = name; + direct_addr_list[n_direct_addr_list].negative = negative; + n_direct_addr_list++; + return 0; +} + + +int +parse_addr_pair (const char *str, struct in_addr *addr, struct in_addr *mask) +{ + /* NOTE: */ + /* Assume already be splitted by separator + and formatted as folowing: + 1) 12.34.56.789/255.255.255.0 + 2) 12.34.56.789/24 + 3) 12.34.56. + All above generates same addr/mask pair 12.34.56.0 and 255.255.255.0 + */ + const char *ptr; + u_char *dsta, *dstm; + int i, n; + + assert( str != NULL ); + addr->s_addr = 0; + mask->s_addr = 0; + ptr = str; + dsta = (u_char*)&addr->s_addr; + dstm = (u_char*)&mask->s_addr; + for (i=0; i<4; i++ ) { + if ( *ptr == '\0' ) + break; /* case of format #3 */ + if ( !isdigit(*ptr) ) + return -1; /* format error: */ + *dsta++ = atoi( ptr ); + *dstm++ = 255; /* automatic mask for format #3 */ + while ( isdigit(*ptr) ) /* skip digits */ + ptr++; + if ( *ptr == '.' ) + ptr++; + else + break; + } + /* At this point, *ptr points '/' or EOS ('\0') */ + if ( *ptr == '\0' ) + return 0; /* complete as format #3 */ + if ( *ptr != '/' ) + return -1; /* format error */ + /* Now parse mask for format #1 or #2 */ + ptr++; + mask->s_addr = 0; /* clear automatic mask */ + + if ( strchr( ptr, '.') ) { + /* case of format #1 */ + dstm = (u_char*)&mask->s_addr; + for (i=0; i<4; i++) { + if ( !isdigit(*ptr) ) + return -1; /* format error: */ + *dstm++ = atoi(ptr); + while ( isdigit(*ptr) ) /* skip digits */ + ptr++; + if ( *ptr == '.' ) + ptr++; + else + break; /* from for loop */ + } + /* complete as format #1 */ + } else { + /* case of format #2 */ + if ( !isdigit(*ptr) ) + return -1; /* format error: */ + n = atoi(ptr); + if ( n<0 || 32s_addr = (n==0)? 0: htonl(((u_long)0xFFFFFFFF)<<(32-n)); + /* complete as format #1 */ + } + return 0; +} + +void +initialize_direct_addr (void) +{ + int negative; + int n_entries; + char *env = NULL, *beg, *next, *envkey = NULL; + struct in_addr addr, mask; + + if ( relay_method == METHOD_SOCKS ){ + if ( socks_version == 5 ) + envkey = ENV_SOCKS5_DIRECT; + else + envkey = ENV_SOCKS4_DIRECT; + env = getparam(envkey); + if ( env == NULL ) + env = getparam(ENV_SOCKS_DIRECT); + } else if ( relay_method == METHOD_HTTP ){ + env = getparam(ENV_HTTP_DIRECT); + } + + if ( env == NULL ) + env = getparam(ENV_CONNECT_DIRECT); + + if ( env == NULL ) + return; /* no entry */ + debug("making direct addr list from: '%s'\n", env); + env = strdup( env ); /* reallocate to modify */ + beg = next = env; + n_entries = 0; + do { + if ( MAX_DIRECT_ADDR_LIST <= n_entries ) { + error("too many entries in %s", envkey); + break; /* from do loop */ + } + next = strchr( beg, ','); + if ( next != NULL ) + *next++ = '\0'; + addr.s_addr = 0; + mask.s_addr = 0; + if (*beg == '!') { + negative = 1; + beg++; + } else + negative = 0; + if ( !parse_addr_pair( beg, &addr, &mask ) ) { + add_direct_addr( &addr, &mask, negative ); + } else { + add_direct_host( beg, negative ); + } + if ( next != NULL ) + beg = next; + } while ( next != NULL ); + + free( env ); + return; +} + +int +cmp_addr (void *addr1, void *addr2, int addrlen) +{ + return memcmp( addr1, addr2, addrlen ); +} + +int +is_direct_address (const struct in_addr addr) +{ + int i, neg; + struct in_addr iaddr; + + /* Note: assume IPV4 address !! */ + for (i=0; i 1 (exact match) + ends_with("foo.bar.com", "bar.com") => 1 (domain match) + ends_with("foo.beebar.com", "bar.com") => 0 (partial match) + ends_with("bar", "bar.com") => 0 (shorter) + */ +int +domain_match(const char *s1, const char *s2) +{ + int len1, len2; + const char *tail1, *tail2; + len1 = strlen(s1); + len2 = strlen(s2); + if (len1 < len2 || len1 == 0 || len2 == 0) + return 0; /* not match */ + tail1 = s1 + len1; + tail2 = s2 + len2; + while (0 < len1 && 0 < len2) { + if (*--tail1 != *--tail2) + break; /* not match */ + len1--, len2--; + } + if (len2 != 0) + return 0; /* not match */ + /* Now exact match, domain match or partial match. + Return true if exact or domain match. + Or continue checking. */ + if (tail1 == s1 || tail1[-1] == '.') + return 1; /* match! */ + return 0; /* not match */ +} + +/* Check given NAME is ends with one of + registered direct name entry. + Return 1 if matched, or 0. +*/ +int +is_direct_name (const char *name) +{ + int len, i; + debug("checking %s is for direct?\n", name); + len = strlen(name); + if (len < 1) + return 0; /* false */ + for (i=0; is_port); + debug("service: %s => %d\n", service, port); + } + } + return (u_short)port; +} + +int +getarg( int argc, char **argv ) +{ + int err = 0; + char *ptr, *server = (char*)NULL; + int method = METHOD_DIRECT; + + progname = *argv; + argc--; + argv++; + + /* check options */ + while ( (0 < argc) && (**argv == '-') ) { + ptr = *argv + 1; + while ( ptr && *ptr ) + { + switch ( *ptr ) { + case 's': /* use SOCKS */ + method = METHOD_SOCKS; + break; + case 'n': /* no proxy */ + method = METHOD_DIRECT; + break; + case 'h': /* use http-proxy */ + method = METHOD_HTTP; + break; + case 'x': /* use https-proxy */ + method = METHOD_HTTPS; + break; + case 't': + method = METHOD_TELNET; + break; + case 'S': /* specify SOCKS server */ + if ( 1 < argc ) { + argc--; + argv++; + method = METHOD_SOCKS; + server = *argv; + } else { + error("option '-%c' needs argument.\n", *ptr); + err++; + } + break; + case 'H': /* specify http-proxy server */ + if ( 1 < argc ) { + argc--; + argv++; + method = METHOD_HTTP; + server = *argv; + } else { + error("option '-%c' needs argument.\n", *ptr); + err++; + } + break; + case 'X': /* specify https-proxy server */ + if ( 1 < argc ) { + argc--; + argv++; + method = METHOD_HTTPS; + server = *argv; + } else { + error("option '-%c' needs argument.\n", *ptr); + err++; + } + break; + case 'T': /* specify telnet proxy server */ + if ( 1 < argc ) { + argc--; + argv++; + method = METHOD_TELNET; + server = *argv; + } else { + error("option '-%c' needs argument.\n", *ptr); + err++; + } + break; + case 'c': + if (1 < argc) { + argc--; + argv++; + telnet_command = *argv; + } else { + error("option '%c' needs argument.\n", *ptr); + err++; + } + break; + case 'P': + f_hold_session = 1; + /* without break */ + case 'p': /* specify port to forward */ + if ( 1 < argc ) { + argc--; + argv++; + local_type = LOCAL_SOCKET; + local_port = resolve_port(*argv); + } else { + error("option '-%c' needs argument.\n", *ptr); + err++; + } + break; +#ifndef _WIN32 + case 'w': + if ( 1 < argc ) { + argc--; + argv++; + connect_timeout = atoi(*argv); + } else { + error("option '-%c' needs argument.\n", *ptr); + err++; + } + break; +#endif /* not _WIN32 */ + case '4': + socks_version = 4; + break; + case '5': + socks_version = 5; + break; + case 'a': + if ( 1 < argc ) { + argc--; + argv++; + socks5_auth = *argv; + } else { + error("option '-%c' needs argument.\n", *ptr); + err++; + } + break; + case 'R': /* specify resolve method */ + if ( 1 < argc ) { + argc--; + argv++; + socks_resolve = lookup_resolve( *argv ); + } else { + error("option '-%c' needs argument.\n", *ptr); + err++; + } + break; + case 'V': /* print version */ + fprintf(stderr, "%s\nVersion %s\n", progdesc, revstr); + exit(0); + case 'd': /* debug mode */ + f_debug++; + break; + case '-': /* long options */ + if (!strcmp(*argv, "--help")) + { + print_help(0); + } + if (!strcmp(*argv, "--socks-proxy")) + { + if ( 1 < argc ) + { + argc--; + argv++; + method = METHOD_SOCKS; + server = *argv; + } + else + { + error("option '%s' needs argument.\n", *argv); + err++; + } + } + else if (!strcmp(*argv, "--http-proxy")) + { + if ( 1 < argc ) + { + argc--; + argv++; + method = METHOD_HTTP; + server = *argv; + } + else + { + error("option '%s' needs argument.\n", *argv); + err++; + } + } + else if (!strcmp(*argv, "--https-proxy")) + { + if ( 1 < argc ) + { + argc--; + argv++; + method = METHOD_HTTPS; + server = *argv; + } + else + { + error("option '%s' needs argument.\n", *argv); + err++; + } + } + else if (!strcmp(*argv, "--https-proxy-ca")) + { + if ( 1 < argc ) + { + argc--; + argv++; + https_ca_file = *argv; + } + else + { + error("option '%s' needs argument.\n", *argv); + err++; + } + } + else if (!strcmp(*argv, "--https-proxy-ca-path")) + { + if ( 1 < argc ) + { + argc--; + argv++; + https_ca_path = *argv; + } + else + { + error("option '%s' needs argument.\n", *argv); + err++; + } + } + else if (!strcmp(*argv, "--no-check-certificate")) + { + https_check_certificate = 0; + } + else if (!strcmp(*argv, "--https-proxy-certname")) + { + if ( 1 < argc ) + { + argc--; + argv++; + https_proxy_certname = *argv; + } + else + { + error("option '%s' needs argument.\n", *argv); + err++; + } + } + else if (!strcmp(*argv, "--https-user-cert")) + { + if ( 1 < argc ) + { + argc--; + argv++; + https_usercert_file = *argv; + } + else + { + error("option '%s' needs argument.\n", *argv); + err++; + } + } + else if (!strcmp(*argv, "--https-user-key")) + { + if ( 1 < argc ) + { + argc--; + argv++; + https_userkey_file = *argv; + } + else + { + error("option '%s' needs argument.\n", *argv); + err++; + } + } + else if (!strcmp(*argv, "--telnet-proxy")) + { + if ( 1 < argc ) + { + argc--; + argv++; + method = METHOD_TELNET; + server = *argv; + } + else + { + error("option '%s' needs argument.\n", *argv); + err++; + } + } + else + { + error("unknown option '%s'\n", *argv); + err++; + } + + ptr = NULL; + break; + + default: + error("unknown option '-%c'\n", *ptr); + err++; + } + if (ptr) ptr++; + } + argc--; + argv++; + } + + /* check error */ + if ( 0 < err ) + goto quit; + + set_relay( method, server ); + + /* check destination HOST (MUST) */ + if ( argc == 0 ) + print_help(0); + + dest_host = argv[0]; + /* decide port or service name from programname or argument */ + if ( ((ptr=strrchr( progname, '/' )) != NULL) || + ((ptr=strchr( progname, '\\')) != NULL) ) + ptr++; + else + ptr = progname; + if ( dest_port == 0 ) { + /* accept only if -P is not specified. */ + if ( 1 < argc ) { + /* get port number from argument (prior to progname) */ + /* NOTE: This way is for cvs ext method. */ + dest_port = resolve_port(argv[1]); + } else if ( strncmp( ptr, "connect-", 8) == 0 ) { + /* decide port number from program name */ + char *str = strdup( ptr+8 ); + str[strcspn( str, "." )] = '\0'; + dest_port = resolve_port(str); + free(str); + } + } + /* check port number */ + if ( dest_port <= 0 ) { + error( "You must specify the destination port correctly.\n"); + err++; + goto quit; + } + if ( (relay_method != METHOD_DIRECT) && (relay_port <= 0) ) { + error("Invalid relay port: %d\n", dest_port); + err++; + goto quit; + } + +quit: + /* report for debugging */ + debug("relay_method = %s (%d)\n", + method_names[relay_method], relay_method); + if ( relay_method != METHOD_DIRECT ) { + debug("relay_host=%s\n", relay_host); + debug("relay_port=%d\n", relay_port); + debug("relay_user=%s\n", relay_user); + } + if ( relay_method == METHOD_SOCKS ) { + debug("socks_version=%d\n", socks_version); + debug("socks_resolve=%s (%d)\n", + resolve_names[socks_resolve], socks_resolve); + } + debug("local_type=%s\n", local_type_names[local_type]); + if ( local_type == LOCAL_SOCKET ) { + debug("local_port=%d\n", local_port); + if (f_hold_session) + debug (" with holding remote session.\n"); + } + debug("dest_host=%s\n", dest_host); + debug("dest_port=%d\n", dest_port); + if ( 0 < err ) + print_help(1); + + return 0; +} + +#ifndef _WIN32 +/* Time-out feature is not allowed for Win32 native compilers. */ +/* MSVC and Borland C cannot but Cygwin and UNIXes can. */ + +/* timeout signal hander */ +static void +sig_timeout(const int signum) +{ + signal( SIGALRM, SIG_IGN ); + alarm( 0 ); + error( "timed out\n" ); + exit(1); +} + +/* set timeout param = seconds, 0 clears */ +void +set_timeout(int timeout) +{ + /* This feature is allowed for UNIX or cygwin environments, currently */ + if ( timeout == 0 ) { + debug( "clearing timeout\n" ); + signal( SIGALRM, SIG_IGN ); + alarm( 0 ); + } else { + debug( "setting timeout: %d seconds\n", timeout ); + signal(SIGALRM, sig_timeout); + alarm( timeout ); + } +} +#endif + +#if !defined(_WIN32) && !defined(__CYGWIN32__) +void +switch_ns (struct sockaddr_in *ns) +{ + res_init(); + memcpy (&_res.nsaddr_list[0], ns, sizeof(*ns)); + _res.nscount = 1; + debug("Using nameserver at %s\n", inet_ntoa(ns->sin_addr)); +} +#endif /* !_WIN32 && !__CYGWIN32__ */ + +/* TODO: IPv6 + TODO: fallback if askpass execution failed. + */ + +int +local_resolve (const char *host, struct sockaddr_in *addr) +{ + struct hostent *ent; + if ( strspn(host, dotdigits) == strlen(host) ) { + /* given by IPv4 address */ + addr->sin_family = AF_INET; + addr->sin_addr.s_addr = inet_addr(host); + } else { + debug("resolving host by name: %s\n", host); + ent = gethostbyname (host); + if ( ent ) { + memcpy (&addr->sin_addr, ent->h_addr, ent->h_length); + addr->sin_family = ent->h_addrtype; + debug("resolved: %s (%s)\n", + host, inet_ntoa(addr->sin_addr)); + } else { + debug("failed to resolve locally.\n"); + return -1; /* failed */ + } + } + return 0; /* good */ +} + +int +open_connection( const char *host, u_short port ) +{ + SOCKET s; + struct sockaddr_in saddr; + + /* resolve address of proxy or direct target */ + if (local_resolve (host, &saddr) < 0) { + error("can't resolve hostname: %s\n", host); + return SOCKET_ERROR; + } + saddr.sin_port = htons(port); + + debug("connecting to %s:%u\n", inet_ntoa(saddr.sin_addr), port); + s = socket( AF_INET, SOCK_STREAM, 0 ); + if ( connect( s, (struct sockaddr *)&saddr, sizeof(saddr)) + == SOCKET_ERROR) { + debug( "connect() failed.\n"); + return SOCKET_ERROR; + } + return s; +} + +void +report_text( char *prefix, char *buf ) +{ + static char work[1024]; + char *tmp; + + if ( !f_debug ) + return; + if ( !f_report ) + return; /* don't report */ + debug("%s \"", prefix); + while ( *buf ) { + memset( work, 0, sizeof(work)); + tmp = work; + while ( *buf && ((tmp-work) < (int)sizeof(work)-5) ) { + switch ( *buf ) { + case '\t': *tmp++ = '\\'; *tmp++ = 't'; break; + case '\r': *tmp++ = '\\'; *tmp++ = 'r'; break; + case '\n': *tmp++ = '\\'; *tmp++ = 'n'; break; + case '\\': *tmp++ = '\\'; *tmp++ = '\\'; break; + default: + if ( isprint(*buf) ) { + *tmp++ = *buf; + } else { + int consumed = tmp - work; + snprintf( tmp, sizeof(work)-consumed, + "\\x%02X", (char)*buf); + tmp += strlen(tmp); + } + } + buf++; + *tmp = '\0'; + } + debug_("%s", work); + } + + debug_("\"\n"); +} + + +void +report_bytes( char *prefix, char *buf, int len ) +{ + if ( ! f_debug ) + return; + debug( "%s", prefix ); + while ( 0 < len ) { + fprintf( stderr, " %02x", *buf); + buf++; + len--; + } + fprintf(stderr, "\n"); + return; +} + +int +atomic_out( SOCKET s, char *buf, int size ) +{ + int ret, len; + + assert( buf != NULL ); + assert( 0<=size ); + /* do atomic out */ + ret = 0; + while ( 0 < size ) { + len = send( s, buf+ret, size, 0 ); + if ( len == -1 ) + fatal("atomic_out() failed to send(), %d\n", socket_errno()); + ret += len; + size -= len; + } + if (!f_report) { + debug("atomic_out() [some bytes]\n"); + debug(">>> xx xx xx xx ...\n"); + } else { + debug("atomic_out() [%d bytes]\n", ret); + report_bytes(">>>", buf, ret); + } + return ret; +} + +int +atomic_in( SOCKET s, char *buf, int size ) +{ + int ret, len; + + assert( buf != NULL ); + assert( 0<=size ); + + /* do atomic in */ + ret = 0; + while ( 0 < size ) { + len = recv( s, buf+ret, size, 0 ); + if ( len == -1 ) { + fatal("atomic_in() failed to recv(), %d\n", socket_errno()); + } else if ( len == 0 ) { + fatal( "Connection closed by peer.\n"); + } + ret += len; + size -= len; + } + if (!f_report) { + debug("atomic_in() [some bytes]\n"); + debug("<<< xx xx xx xx ...\n"); + } else { + debug("atomic_in() [%d bytes]\n", ret); + report_bytes("<<<", buf, ret); + } + return ret; +} + +int +line_input( SOCKET s, char *buf, int size ) +{ + char *dst = buf; + if ( size == 0 ) + return 0; /* no error */ + size--; + while ( 0 < size ) { + switch ( recv( s, dst, 1, 0) ) { /* recv one-by-one */ + case SOCKET_ERROR: + error("recv() error\n"); + return -1; /* error */ + case 0: + size = 0; /* end of stream */ + break; + default: + /* continue reading until last 1 char is EOL? */ + if ( *dst == '\n' ) { + /* finished */ + size = 0; + } else { + /* more... */ + size--; + } + dst++; + } + } + *dst = '\0'; + report_text( "<<<", buf); + return 0; +} + +/* cut_token() + Span token in given string STR until char in DELIM is appeared. + Then replace contiguous DELIMS with '\0' for string termination + and returns next pointer. + If no next token, return NULL. +*/ +char * +cut_token( char *str, char *delim) +{ + char *ptr = str + strcspn(str, delim); + char *end = ptr + strspn(ptr, delim); + if ( ptr == str ) + return NULL; + while ( ptr < end ) + *ptr++ = '\0'; + return ptr; +} + +const char * +lookup(int num, LOOKUP_ITEM *items) +{ + int i = 0; + while (0 <= items[i].num) { + if (items[i].num == num) + return items[i].str; + i++; + } + return "(unknown)"; +} + +/* readpass() + password input routine + Use ssh-askpass (same mechanism to OpenSSH) +*/ +char * +readpass( const char* prompt, ...) +{ + static char buf[1000]; /* XXX, don't be fix length */ + va_list args; + va_start(args, prompt); + vsnprintf(buf, sizeof(buf), prompt, args); + va_end(args); + + if ( getparam(ENV_SSH_ASKPASS) +#if !defined(_WIN32) && !defined(__CYGWIN32__) + && getenv("DISPLAY") +#endif /* not _WIN32 && not __CYGWIN32__ */ + ) { + /* use ssh-askpass to get password */ + FILE *fp; + char *askpass = getparam(ENV_SSH_ASKPASS), *cmd; + int cmd_size = strlen(askpass) +1 +1 +strlen(buf) +1 +1; + cmd = xmalloc(cmd_size); + snprintf(cmd, cmd_size, "%s \"%s\"", askpass, buf); + fp = popen(cmd, "r"); + free(cmd); + if ( fp == NULL ) + return NULL; /* fail */ + buf[0] = '\0'; + if (fgets(buf, sizeof(buf), fp) == NULL) + return NULL; /* fail */ + fclose(fp); + } else { + tty_readpass( buf, buf, sizeof(buf)); + } + buf[strcspn(buf, "\r\n")] = '\0'; + return buf; +} + + +/* SSL callback routing for reading private key passphrase, if necessary */ +int password_callback (char *buf, int size, int rwflag, void *u) +{ + char *pass; + + if (buf) + { + pass = readpass("Enter privatekey passphrase for\n%s: ", https_userkey_file); + if (pass) + { + strncpy(buf, pass, size); + return strlen (buf); + } + } + return 0; +} + + +static int +socks5_do_auth_userpass( int s ) +{ + char buf[1024], *ptr; + char *pass = NULL; + int len; + + /* do User/Password authentication. */ + /* This feature requires username and password from + command line argument or environment variable, + or terminal. */ + if (relay_user == NULL) + fatal("cannot determine user name.\n"); + + /* get password from environment variable if exists. */ + if ((pass=determine_relay_password()) == NULL && + (pass=readpass("Enter SOCKS5 password for %s@%s: ", + relay_user, relay_host)) == NULL) + fatal("Cannot get password for user: %s\n", relay_user); + + /* make authentication packet */ + ptr = buf; + PUT_BYTE( ptr++, 1 ); /* subnegotiation ver.: 1 */ + len = strlen( relay_user ); /* ULEN and UNAME */ + PUT_BYTE( ptr++, len ); + strcpy( ptr, relay_user ); + ptr += len; + len = strlen( pass ); /* PLEN and PASSWD */ + PUT_BYTE( ptr++, strlen(pass)); + strcpy( ptr, pass ); + ptr += len; + memset (pass, 0, strlen(pass)); /* erase password */ + + /* send it and get answer */ + f_report = 0; + atomic_out( s, buf, ptr-buf ); + f_report = 1; + atomic_in( s, buf, 2 ); + + /* check status */ + if ( buf[1] == 0 ) + return 0; /* success */ + else + return -1; /* fail */ +} + +static const char * +socks5_getauthname( int auth ) +{ + switch ( auth ) { + case SOCKS5_AUTH_REJECT: return "REJECTED"; + case SOCKS5_AUTH_NOAUTH: return "NO-AUTH"; + case SOCKS5_AUTH_GSSAPI: return "GSSAPI"; + case SOCKS5_AUTH_USERPASS: return "USERPASS"; + case SOCKS5_AUTH_CHAP: return "CHAP"; + case SOCKS5_AUTH_EAP: return "EAP"; + case SOCKS5_AUTH_MAF: return "MAF"; + default: return "(unknown)"; + } +} + +typedef struct { + char* name; + char auth; +} AUTH_METHOD_ITEM; + +AUTH_METHOD_ITEM socks5_auth_table[] = { + { "none", SOCKS5_AUTH_NOAUTH }, + { "gssapi", SOCKS5_AUTH_GSSAPI }, + { "userpass", SOCKS5_AUTH_USERPASS }, + { "chap", SOCKS5_AUTH_CHAP }, + { NULL, -1 }, +}; + +int +socks5_auth_parse_1(char *start, char *end) +{ + int i, len; + for ( ; *start; start++ ) + if ( *start != ' ' && *start != '\t') break; + for ( end--; end >= start; end-- ) { + if ( *end != ' ' && *end != '\t'){ + end++; + break; + } + } + len = end - start; + for ( i = 0; socks5_auth_table[i].name != NULL; i++ ){ + if ( strncmp(start, socks5_auth_table[i].name, len) == 0) { + return socks5_auth_table[i].auth; + } + } + fatal("Unknown auth method: %s\n", start); + return -1; +} + +int +socks5_auth_parse(char *start, int *auth_list, int max_auth) +{ + char *end; + int i = 0; + while ( i < max_auth ) { + end = strchr(start, ','); + if (*start && end) { + auth_list[i++] = socks5_auth_parse_1(start, end); + start = ++end; + } else { + break; + } + } + if ( *start && ( i < max_auth ) ){ + for( end = start; *end; end++ ); + auth_list[i++] = socks5_auth_parse_1(start, end); + } else { + fatal("Too much auth method.\n"); + } + return i; +} + +/* begin SOCKS5 relaying + And no authentication is supported. + */ +int +begin_socks5_relay( SOCKET s ) +{ + char buf[256], *ptr, *env = socks5_auth; + unsigned char n_auth = 0, auth_method; + int len, auth_list[10], auth_result, i; + + debug( "begin_socks_relay()\n"); + + /* request authentication */ + ptr = buf; + PUT_BYTE( ptr++, 5); /* SOCKS version (5) */ + + if ( env == NULL ) + env = getparam(ENV_SOCKS5_AUTH); + if ( env == NULL ) + { + /* add no-auth authentication */ + auth_list[n_auth++] = SOCKS5_AUTH_NOAUTH; + /* add user/pass authentication */ + auth_list[n_auth++] = SOCKS5_AUTH_USERPASS; + } + else + { + n_auth = socks5_auth_parse(env, auth_list, 10); + } + + PUT_BYTE( ptr++, n_auth); /* num auth */ + for (i=0; i>8); /* DST.PORT */ + PUT_BYTE( ptr++, dest_port&0xFF); + atomic_out( s, buf, ptr-buf); /* send request */ + atomic_in( s, buf, 4 ); /* recv response */ + if ( (buf[1] != SOCKS5_REP_SUCCEEDED) ) { /* check reply code */ + error("Got error response from SOCKS server: %d (%s).\n", + buf[1], lookup(buf[1], socks5_rep_names)); + return -1; + } + ptr = buf + 4; + switch ( buf[3] ) { /* case by ATYP */ + case 1: /* IP v4 ADDR*/ + atomic_in( s, ptr, 4+2 ); /* recv IPv4 addr and port */ + break; + case 3: /* DOMAINNAME */ + atomic_in( s, ptr, 1 ); /* recv name and port */ + atomic_in( s, ptr+1, *(ptr + 2)); + break; + case 4: /* IP v6 ADDR */ + atomic_in( s, ptr, 16+2 ); /* recv IPv6 addr and port */ + break; + } + + /* Conguraturation, connected via SOCKS5 server! */ + return 0; +} + +/* begin SOCKS protocol 4 relaying + And no authentication is supported. + + There's SOCKS protocol version 4 and 4a. Protocol version + 4a has capability to resolve hostname by SOCKS server, so + we don't need resolving IP address of destination host on + local machine. + + Environment variable SOCKS_RESOLVE directs how to resolve + IP addess. There's 3 keywords allowed; "local", "remote" + and "both" (case insensitive). Keyword "local" means taht + target host name is resolved by localhost resolver + (usualy with gethostbyname()), "remote" means by remote + SOCKS server, "both" means to try resolving by localhost + then remote. + + SOCKS4 protocol and authentication of SOCKS5 protocol + requires user name on connect request. + User name is determined by following method. + + 1. If server spec has user@hostname:port format then + user part is used for this SOCKS server. + + 2. Get user name from environment variable LOGNAME, USER + (in this order). + +*/ +int +begin_socks4_relay( SOCKET s ) +{ + char buf[256], *ptr; + + debug( "begin_socks_relay()\n"); + + /* make connect request packet + protocol v4: + VN:1, CD:1, PORT:2, ADDR:4, USER:n, NULL:1 + protocol v4a: + VN:1, CD:1, PORT:2, DUMMY:4, USER:n, NULL:1, HOSTNAME:n, NULL:1 + */ + ptr = buf; + PUT_BYTE( ptr++, 4); /* protocol version (4) */ + PUT_BYTE( ptr++, 1); /* CONNECT command */ + PUT_BYTE( ptr++, dest_port>>8); /* destination Port */ + PUT_BYTE( ptr++, dest_port&0xFF); + /* destination IP */ + memcpy(ptr, &dest_addr.sin_addr, sizeof(dest_addr.sin_addr)); + ptr += sizeof(dest_addr.sin_addr); + if ( dest_addr.sin_addr.s_addr == 0 ) + *(ptr-1) = 1; /* fake, protocol 4a */ + /* username */ + if (relay_user == NULL) + fatal( "Cannot determine user name.\n"); + strcpy( ptr, relay_user ); + ptr += strlen( relay_user ) +1; + /* destination host name (for protocol 4a) */ + if ( (socks_version == 4) && (dest_addr.sin_addr.s_addr == 0)) { + strncpy( ptr, dest_host, sizeof(buf)+buf-ptr ); + ptr += strlen( dest_host ) +1; + } + /* send command and get response + response is: VN:1, CD:1, PORT:2, ADDR:4 */ + atomic_out( s, buf, ptr-buf); /* send request */ + atomic_in( s, buf, 8 ); /* recv response */ + if ( (buf[1] != SOCKS4_REP_SUCCEEDED) ) { /* check reply code */ + error("Got error response: %d: '%s'.\n", + buf[1], lookup(buf[1], socks4_rep_names)); + return -1; /* failed */ + } + + /* Conguraturation, connected via SOCKS4 server! */ + return 0; +} + +int +sendf(SOCKET s, const char *fmt,...) +{ + static char buf[10240]; /* xxx, enough? */ + + va_list args; + va_start( args, fmt ); + vsnprintf( buf, sizeof(buf), fmt, args ); + va_end( args ); + + report_text(">>>", buf); + if (relay_method == METHOD_HTTP) + { + if ( send(s, buf, strlen(buf), 0) == SOCKET_ERROR ) + { + debug("failed to send http request. errno=%d\n", socket_errno()); + return -1; + } + } + else + { + if (SSL_write(ssl, buf, strlen(buf)) < 0) + { + debug("failed to send https request.\n"); + ssl_error( "SSL_write()" ); + return -1; + } + } + return 0; +} + +int +sslread(char *buf, size_t size) +{ + int ret = 0; + + if ( size == 0 ) + return 0; /* no error */ + size--; + + bzero(buf, size); + + debug("sslread: bytes waiting: %d\n", SSL_pending(ssl)); + ret = SSL_read(ssl, buf, size); + debug("SSL_read: read %d bytes\n", ret); + if (ret < 0) + { + debug("failed receiving https reply.\n"); + ssl_error( "SSL_read()" ); + return -1; + } + else + report_text("<<<", buf); + return ret; +} + +int +ssl_line_input(char *buf, size_t size) +{ + char *dst = sslbuf; + size_t len = 0; + + if ( size == 0 ) + return 0; /* no error */ + + /* global buffer empty, fill it with new data and reset the index */ + if (strlen(sslbuf) == 0) + { + if (sslread(sslbuf, sizeof(sslbuf)) < 0) + { + debug("no reply\n"); + return -1; + } + sslbuf_idx = 0; + } + + dst += sslbuf_idx; + while (*dst != '\n' && sslbuf_idx+len < sizeof(sslbuf) && len < size) + { + dst++; + len++; + } + +#if 0 + report_text("<<< dst=" , dst); +#endif + + /* no valid input found, or input too long for buffer */ + if ((len > size) || (*dst != '\n')) + return -1; + + dst = sslbuf; + dst += sslbuf_idx; + memcpy(buf, dst, ++len); + buf[len] = '\0'; + + report_text("<<< ", buf); + sslbuf_idx += len; + + return 0; +} + +const char *base64_table = +"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +char * +make_base64_string(const char *str) +{ + static char *buf; + unsigned char *src, *dst; + int bits, data, src_len, dst_len; + /* make base64 string */ + src_len = strlen(str); + dst_len = (src_len+2)/3*4; + buf = xmalloc(dst_len+1); + bits = data = 0; + src = (unsigned char *)str; + dst = (unsigned char *)buf; + while ( dst_len-- ) { + if ( bits < 6 ) { + data = (data << 8) | *src; + bits += 8; + if ( *src != 0 ) + src++; + } + *dst++ = base64_table[0x3F & (data >> (bits-6))]; + bits -= 6; + } + *dst = '\0'; + /* fix-up tail padding */ + switch ( src_len%3 ) { + case 1: + *--dst = '='; + case 2: + *--dst = '='; + } + return buf; +} + + +int +basic_auth (SOCKET s) +{ + char *userpass; + char *cred; + const char *user = relay_user; + char *pass = NULL; + int len, ret; + + /* Get username/password for authentication */ + if (user == NULL) + fatal("Cannot decide username for proxy authentication."); + if ((pass = determine_relay_password ()) == NULL && + (pass = readpass("Enter proxy authentication password for %s@%s: ", + relay_user, relay_host)) == NULL) + fatal("Cannot decide password for proxy authentication."); + + len = strlen(user)+strlen(pass)+1; + userpass = xmalloc(len+1); + snprintf(userpass, len+1, "%s:%s", user, pass); + memset (pass, 0, strlen(pass)); + cred = make_base64_string(userpass); + memset (userpass, 0, len); + + f_report = 0; /* don't report for security */ + ret = sendf(s, "Proxy-Authorization: Basic %s\r\n", cred); + f_report = 1; + report_text(">>>", "Proxy-Authorization: Basic xxxxx\r\n"); + + memset(cred, 0, strlen(cred)); + free(cred); + free(userpass); + + return ret; +} + +/* begin relaying via HTTP proxy + Directs CONNECT method to proxy server to connect to + destination host (and port). It may not be allowed on your + proxy server. + */ +int +begin_http_relay( SOCKET s ) +{ + char buf[4096]; + int result; + char *auth_what; + + debug("begin_http_relay()\n"); + + if (sendf(s,"CONNECT %s:%d HTTP/1.1\r\nHost: %s\r\n", dest_host, dest_port, dest_host) < 0) + return START_ERROR; + if (proxy_auth_type == PROXY_AUTH_BASIC && basic_auth (s) < 0) + return START_ERROR; + if (sendf(s,"\r\n") < 0) + return START_ERROR; + + /* get response */ + if ( line_input(s, buf, sizeof(buf)) < 0 ) { + debug("failed to read http response.\n"); + return START_ERROR; + } + + /* check status */ + if (!strchr(buf, ' ')) { + error ("Unexpected http response: '%s'.\n", buf); + return START_ERROR; + } + result = atoi(strchr(buf,' ')); + + switch ( result ) { + case 200: + /* Conguraturation, connected via http proxy server! */ + debug("connected, start user session.\n"); + break; + case 302: /* redirect */ + do { + if (line_input(s, buf, sizeof(buf))) + break; + if (expect(buf, "Location: ")) { + relay_host = cut_token(buf, "//"); + cut_token(buf, "/"); + relay_port = atoi(cut_token(buf, ":")); + } + } while (strcmp(buf,"\r\n") != 0); + return START_RETRY; + + /* We handle both 401 and 407 codes here: 401 is WWW-Authenticate, which + * not strictly the correct response, but some proxies do send this (e.g. + * Symantec's Raptor firewall) */ + case 401: /* WWW-Auth required */ + case 407: /* Proxy-Auth required */ + /** NOTE: As easy implementation, we support only BASIC scheme + and ignore realm. */ + /* If proxy_auth_type is PROXY_AUTH_BASIC and get + this result code, authentication was failed. */ + if (proxy_auth_type != PROXY_AUTH_NONE) { + error("Authentication failed.\n"); + return START_FORBIDDEN; + } + auth_what = (result == 401) ? "WWW-Authenticate:" : "Proxy-Authenticate:"; + do { + if ( line_input(s, buf, sizeof(buf)) ) { + break; + } + downcase(buf); + if (expect(buf, auth_what)) { + /* parse type and realm */ + char *scheme, *realm; + scheme = cut_token(buf, " "); + realm = cut_token(scheme, " "); + if ( scheme == NULL || realm == NULL ) { + debug("Invalid format of %s field.", auth_what); + return START_ERROR; /* fail */ + } + /* check supported auth type */ + if (expect(scheme, "basic")) { + proxy_auth_type = PROXY_AUTH_BASIC; + } else { + debug("Unsupported authentication type: %s", scheme); + } + } + } while (strcmp(buf,"\r\n") != 0); + if ( proxy_auth_type == PROXY_AUTH_NONE ) { + debug("Can't find %s in response header.", auth_what); + return START_ERROR; + } else { + return START_RETRY; + } + + default: + /* Not allowed */ + debug("http proxy is not allowed.\n"); + return START_FORBIDDEN; + } + /* skip to end of response header */ + do { + if ( line_input(s, buf, sizeof(buf) ) ) { + debug("Can't skip response headers\n"); + return START_ERROR; + } + } + while ( strcmp(buf,"\r\n") != 0 ); + + return START_OK; +} + +/* begin relaying via HTTPS proxy + Directs CONNECT method to proxy server to connect to + destination host (and port). It may not be allowed on your + proxy server. + */ +int +begin_https_relay( SOCKET s ) +{ + char buf[4096]; + int result; + char *auth_what; + + SSL_CTX *ctx = NULL; +#ifdef X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT + X509_VERIFY_PARAM *param = NULL; +#endif + + debug("begin_https_relay()\n"); + + if (https_proxy_certname == NULL) + https_proxy_certname = relay_host; + debug("https_proxy = %s\n", relay_host); + + if (https_proxy_certname == NULL) + https_proxy_certname = getparam(ENV_HTTPS_PROXY_CERTNAME); + if (https_ca_file == NULL) + https_ca_file = getparam(ENV_HTTPS_PROXY_CA_FILE); + if (https_ca_path == NULL) + https_ca_path = getparam(ENV_HTTPS_PROXY_CA_PATH); + if (https_usercert_file == NULL) + https_usercert_file = getparam(ENV_HTTPS_PROXY_USERCERT); + if (https_userkey_file == NULL) + https_userkey_file = getparam(ENV_HTTPS_PROXY_USERKEY); + + debug("https_proxy_certname = %s\n", https_proxy_certname); + debug("https_ca_file = %s\n", https_ca_file); + debug("https_ca_path = %s\n", https_ca_path); + debug("https_usercert = %s\n", https_usercert_file); + debug("https_userkey = %s\n", https_userkey_file); + + OpenSSL_add_all_algorithms(); + OpenSSL_add_all_ciphers(); + OpenSSL_add_all_digests(); + SSL_load_error_strings(); + + /* OpenSSL preparation */ + SSL_library_init(); + + if ((ctx = SSL_CTX_new(TLS_method())) == NULL) + ssl_error( "SSL_CTX_new()" ); + if (SSL_CTX_load_verify_locations(ctx, https_ca_file, https_ca_path) != 1) + ssl_error( "SSL_CTX_load_verify_locations()" ); + if (https_check_certificate) + { +#ifdef X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT + debug( "Adding https proxy server certificate check\n" ); + param = X509_VERIFY_PARAM_new(); + if (X509_VERIFY_PARAM_set1_host(param, https_proxy_certname, strlen(https_proxy_certname)) != 1) + ssl_error( "X509_VERIFY_PARAM_set1_host()" ); + if (SSL_CTX_set1_param(ctx, param) != 1) + ssl_error( "SSL_CTX_set1_param()" ); + X509_VERIFY_PARAM_free(param); +#else + debug( "Your version of OpenSSL only has limited server certificate check support!" ); +#endif + SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER| SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL); + } + + SSL_CTX_set_default_passwd_cb (ctx, password_callback); + + if (https_usercert_file && SSL_CTX_use_certificate_chain_file(ctx, https_usercert_file) <= 0) + ssl_error( "SSL_CTX_use_certificate_chain_file" ); + if (https_userkey_file && SSL_CTX_use_PrivateKey_file(ctx, https_userkey_file, SSL_FILETYPE_PEM) <= 0) + ssl_error( "SSL_CTX_use_PrivateKey_file" ); + + if ((ssl = SSL_new(ctx)) == NULL) + ssl_error( "SSL_new()" ); + if (SSL_set_fd(ssl, s) == 0) + ssl_error( "SSL_set_fd()" ); + if ((result = SSL_connect(ssl)) <= 0) + ssl_error( "SSL_connect()" ); + + SSL_CTX_free (ctx); + + if (sendf(s,"CONNECT %s:%d HTTP/1.1\r\nHost: %s\r\n", dest_host, dest_port, dest_host) < 0) + return START_ERROR; + + if (proxy_auth_type == PROXY_AUTH_BASIC) + { + debug("Doing basic auth\n"); + if (basic_auth (s) < 0) + return START_ERROR; + } + + if (sendf(s,"\r\n") < 0) + return START_ERROR; + + /* get response */ + bzero(buf, sizeof(buf)); + bzero(sslbuf, sizeof(sslbuf)); + if (ssl_line_input(buf, sizeof(buf)) < 0) + { + debug("failed to read https response, retry.\n"); + + /* retry (needed to work around certificate based auth) */ + bzero(sslbuf, sizeof(sslbuf)); + if (ssl_line_input(buf, sizeof(buf)) < 0) + debug("failed to read https response.\n"); + } + + /* check status */ + if (!strchr(buf, ' ')) + { + error ("Unexpected https response: '%s'.\n", buf); + return START_ERROR; + } + result = atoi(strchr(buf,' ')); + + switch ( result ) { + case 200: + /* Conguraturation, connected via http proxy server! */ + debug("connected, start user session.\n"); + break; + case 302: /* redirect */ + do { + if (ssl_line_input(buf, sizeof(buf))) + break; + if (expect(buf, "Location: ")) { + relay_host = cut_token(buf, "//"); + cut_token(buf, "/"); + relay_port = atoi(cut_token(buf, ":")); + } + } while (strcmp(buf,"\r\n") != 0); + return START_RETRY; + + /* We handle both 401 and 407 codes here: 401 is WWW-Authenticate, which + * not strictly the correct response, but some proxies do send this (e.g. + * Symantec's Raptor firewall) */ + case 401: /* WWW-Auth required */ + case 407: /* Proxy-Auth required */ + /** NOTE: As easy implementation, we support only BASIC scheme + and ignore realm. */ + /* If proxy_auth_type is PROXY_AUTH_BASIC and get + this result code, authentication was failed. */ + if (proxy_auth_type != PROXY_AUTH_NONE) { + error("Authentication failed.\n"); + return START_FORBIDDEN; + } + auth_what = (result == 401) ? "WWW-Authenticate:" : "Proxy-Authenticate:"; + do { + if ( ssl_line_input(buf, sizeof(buf)) ) { + break; + } + downcase(buf); + if (expect(buf, auth_what)) { + /* parse type and realm */ + char *scheme, *realm; + scheme = cut_token(buf, " "); + realm = cut_token(scheme, " "); + if ( scheme == NULL || realm == NULL ) { + debug("Invalid format of %s field.\n", auth_what); + return START_ERROR; /* fail */ + } + /* check supported auth type */ + if (expect(scheme, "basic")) { + proxy_auth_type = PROXY_AUTH_BASIC; + } else { + debug("Unsupported authentication type: %s\n", scheme); + } + } + } while (strcmp(buf,"\r\n") != 0); + if ( proxy_auth_type == PROXY_AUTH_NONE ) { + debug("Can't find %s in response header.\n", auth_what); + return START_ERROR; + } else { + debug("got a 401/407 but going for the retry!\n"); + return START_RETRY; + } + + + default: + /* Not allowed */ + debug("https proxy is not allowed.\n"); + return START_FORBIDDEN; + } + /* skip to end of response header */ + do + { + if (ssl_line_input(buf, sizeof(buf))) + { + debug("Can't skip response headers\n"); + return START_ERROR; + } + } while ( strcmp(buf,"\r\n") != 0 ); + + return START_OK; +} + +/* begin relaying via TELNET proxy. + Sends string specified by telnet_command (-c option) with + replacing host name and port number to the socket. */ +int +begin_telnet_relay( SOCKET s ) +{ + char buf[1024]; + char *cmd; + char *good_phrase = "connected to"; + char *bad_phrase_list[] = { + " failed", " refused", " rejected", " closed" + }; + char sep = ' '; + int i; + + debug("begin_telnet_relay()\n"); + + /* report phrase */ + debug("good phrase: '%s'\n", good_phrase); + debug("bad phrases"); + sep = ':'; + for (i=0; i< (sizeof(bad_phrase_list) / sizeof(char*)); i++) { + debug_("%c '%s'", sep, bad_phrase_list[i]); + sep = ','; + } + debug_("\n"); + + /* make request string with replacing %h by destination hostname + and %p by port number, etc. */ + cmd = expand_host_and_port(telnet_command, dest_host, dest_port); + + /* Sorry, we send request string now without waiting a prompt. */ + if (sendf(s, "%s\r\n", cmd) < 0) { + free(cmd); + return START_ERROR; + } + free(cmd); + + /* Process answer from proxy until good or bad phrase is detected. We + assume that the good phrase should be appeared only in the final + line of proxy responses. Bad keywods in the line causes operation + fail. First checks a good phrase, then checks bad phrases. + If no match, continue reading line from proxy. */ + while (!line_input(s, buf, sizeof(buf)) && buf[0] != '\0') { + downcase(buf); + /* first, check good phrase */ + if (strstr(buf, good_phrase)) { + debug("good phrase is detected: '%s'\n", good_phrase); + return START_OK; + } + /* then, check bad phrase */ + for (i=0; i<(sizeof(bad_phrase_list)/sizeof(char*)); i++) { + if (strstr(buf, bad_phrase_list[i]) != NULL) { + debug("bad phrase is detected: '%s'\n", bad_phrase_list[i]); + return START_ERROR; + } + } + } + debug("error reading from telnet proxy\n"); + + return START_ERROR; +} + + +#ifdef _WIN32 +/* ddatalen() + Returns 1 if data is available, otherwise return 0 + */ +int +stdindatalen (void) +{ + DWORD len = 0; + struct stat st; + fstat( 0, &st ); + if ( st.st_mode & _S_IFIFO ) { + /* in case of PIPE */ + if ( !PeekNamedPipe( GetStdHandle(STD_INPUT_HANDLE), + NULL, 0, NULL, &len, NULL) ) { + if ( GetLastError() == ERROR_BROKEN_PIPE ) { + /* PIPE source is closed */ + /* read() will detects EOF */ + len = 1; + } else { + fatal("PeekNamedPipe() failed, errno=%d\n", + GetLastError()); + } + } + } else if ( st.st_mode & _S_IFREG ) { + /* in case of regular file (redirected) */ + len = 1; /* always data ready */ + } else if ( _kbhit() ) { + /* in case of console */ + len = 1; + } + return len; +} +#endif /* _WIN32 */ + +/* relay byte from stdin to socket and fro socket to stdout. + returns reason of termination */ +int +do_repeater( SOCKET local_in, SOCKET local_out, SOCKET remote ) +{ + /** vars for local input data **/ + char lbuf[4096]; /* local input buffer */ + int lbuf_len; /* available data in lbuf */ + int f_local; /* read local input more? */ + /** vars for remote input data **/ + char rbuf[4096]; /* remote input buffer */ + int rbuf_len; /* available data in rbuf */ + int f_remote; /* read remote input more? */ + int close_reason = REASON_UNK; /* reason of end repeating */ + /** other variables **/ + int nfds, len; + fd_set ifds, ofds; + struct timeval *tmo; +#ifdef _WIN32 + struct timeval win32_tmo; +#endif /* _WIN32 */ + + /* repeater between stdin/out and socket */ + nfds = ((local_in local */ + if ( FD_ISSET(remote, &ifds) && (rbuf_len < (int)sizeof(rbuf)) ) { + len = recv( remote, rbuf + rbuf_len, sizeof(rbuf)-rbuf_len, 0); + if ( len == 0 || (len == -1 && socket_errno() == ECONNRESET)) { + debug("connection %s by peer\n", + (len==0)? "closed": "reset"); + close_reason = REASON_CLOSED_BY_REMOTE; + f_remote = 0; /* no more read from socket */ + f_local = 0; + } else if ( len == -1 ) { + /* error */ + fatal("recv() failed, %d\n", socket_errno()); + } else { + debug("recv %d bytes\n", len); + if ( 1 < f_debug ) /* more verbose */ + report_bytes( "<<<", rbuf+rbuf_len, len); + rbuf_len += len; + } + } + + /* local => remote */ + if ( FD_ISSET(local_in, &ifds) && (lbuf_len < (int)sizeof(lbuf)) ) { + if (local_type == LOCAL_SOCKET) + len = recv(local_in, lbuf + lbuf_len, + sizeof(lbuf)-lbuf_len, 0); + else + len = read(local_in, lbuf + lbuf_len, sizeof(lbuf)-lbuf_len); + if ( len == 0 ) { + /* stdin is EOF */ + debug("local input is EOF\n"); + if (!f_hold_session) + shutdown(remote, 1); /* no-more writing */ + f_local = 0; + close_reason = REASON_CLOSED_BY_LOCAL; + } else if ( len == -1 ) { + /* error on reading from stdin */ + if (f_hold_session) { + debug ("failed to read from local\n"); + f_local = 0; + close_reason = REASON_CLOSED_BY_LOCAL; + } else + fatal("recv() failed, errno = %d\n", errno); + } else { + /* repeat */ + lbuf_len += len; + } + } + + /* flush data in buffer to socket */ + if ( 0 < lbuf_len ) { + len = send(remote, lbuf, lbuf_len, 0); + if ( len == -1 ) { + fatal("send() failed, %d\n", socket_errno()); + } else if ( 0 < len ) { + if ( 1 < f_debug ) /* more verbose */ + report_bytes( ">>>", lbuf, len); + /* move data on to top of buffer */ + debug("sent %d bytes\n", len); + lbuf_len -= len; + if ( 0 < lbuf_len ) + memcpy( lbuf, lbuf+len, lbuf_len ); + assert( 0 <= lbuf_len ); + } + } + + /* flush data in buffer to local output */ + if ( 0 < rbuf_len ) { + if (local_type == LOCAL_SOCKET) + len = send( local_out, rbuf, rbuf_len, 0); + else + len = write( local_out, rbuf, rbuf_len); + if ( len == -1 ) { + fatal("output (local) failed, errno=%d\n", errno); + } + rbuf_len -= len; + if ( len < rbuf_len ) + memcpy( rbuf, rbuf+len, rbuf_len ); + assert( 0 <= rbuf_len ); + } + if (f_local == 0 && f_hold_session) { + debug ("closing local port without disconnecting from remote\n"); + f_remote = 0; + shutdown (local_out, 2); + close (local_out); + break; + } + } + + return close_reason; +} + +/* relay byte from stdin to socket and fro socket to stdout. + returns reason of termination */ +int +do_ssl_repeater( SOCKET local_in, SOCKET local_out, SOCKET remote) +{ + /** vars for local input data **/ + char lbuf[4096]; /* local input buffer */ + int lbuf_len; /* available data in lbuf */ + int f_local; /* read local input more? */ + /** vars for remote input data **/ + char rbuf[4096]; /* remote input buffer */ + int rbuf_len; /* available data in rbuf */ + int f_remote; /* read remote input more? */ + int close_reason = REASON_UNK; /* reason of end repeating */ + /** other variables **/ + int nfds, len; + fd_set ifds, ofds; + struct timeval *tmo; +#ifdef _WIN32 + struct timeval win32_tmo; +#endif /* _WIN32 */ + + /* repeater between stdin/out and socket */ + nfds = ((local_in local */ + if ( FD_ISSET(remote, &ifds) && (rbuf_len < (int)sizeof(rbuf)) ) { + len = sslread(rbuf + rbuf_len, sizeof(rbuf)-rbuf_len); + if ( len == 0 || (len == -1 && socket_errno() == ECONNRESET)) { + debug("connection %s by peer\n", + (len==0)? "closed": "reset"); + close_reason = REASON_CLOSED_BY_REMOTE; + f_remote = 0; /* no more read from socket */ + f_local = 0; + } else if ( len == -1 ) { + /* error */ + fatal("recv() failed, %d\n", socket_errno()); + } else { + debug("recv %d bytes\n", len); + if ( 1 < f_debug ) /* more verbose */ + report_bytes( "<<<", rbuf+rbuf_len, len); + rbuf_len += len; + } + } + + /* local => remote */ + if ( FD_ISSET(local_in, &ifds) && (lbuf_len < (int)sizeof(lbuf)) ) { + if (local_type == LOCAL_SOCKET) + len = recv(local_in, lbuf + lbuf_len, + sizeof(lbuf)-lbuf_len, 0); + else + len = read(local_in, lbuf + lbuf_len, sizeof(lbuf)-lbuf_len); + if ( len == 0 ) { + /* stdin is EOF */ + debug("local input is EOF\n"); + if (!f_hold_session) + shutdown(remote, 1); /* no-more writing */ + f_local = 0; + close_reason = REASON_CLOSED_BY_LOCAL; + } else if ( len == -1 ) { + /* error on reading from stdin */ + if (f_hold_session) { + debug ("failed to read from local\n"); + f_local = 0; + close_reason = REASON_CLOSED_BY_LOCAL; + } else + fatal("recv() failed, errno = %d\n", errno); + } else { + /* repeat */ + lbuf_len += len; + } + } + + /* flush data in buffer to socket */ + if ( 0 < lbuf_len ) { + len = SSL_write(ssl, lbuf, lbuf_len); + if ( len == -1 ) { + fatal("send() failed, %d\n", socket_errno()); + } else if ( 0 < len ) { + if ( 1 < f_debug ) /* more verbose */ + report_bytes( ">>>", lbuf, len); + /* move data on to top of buffer */ + debug("sent %d bytes\n", len); + lbuf_len -= len; + if ( 0 < lbuf_len ) + memcpy( lbuf, lbuf+len, lbuf_len ); + assert( 0 <= lbuf_len ); + } + } + + /* flush data in buffer to local output */ + if ( 0 < rbuf_len ) { + if (local_type == LOCAL_SOCKET) + len = send( local_out, rbuf, rbuf_len, 0); + else + len = write( local_out, rbuf, rbuf_len); + if ( len == -1 ) { + fatal("output (local) failed, errno=%d\n", errno); + } + rbuf_len -= len; + if ( len < rbuf_len ) + memcpy( rbuf, rbuf+len, rbuf_len ); + assert( 0 <= rbuf_len ); + } + if (f_local == 0 && f_hold_session) { + debug ("closing local port without disconnecting from remote\n"); + f_remote = 0; + shutdown (local_out, 2); + close (local_out); + break; + } + } + + return close_reason; +} + +int +accept_connection (u_short port) +{ + static int sock = -1; + int connection; + struct sockaddr_in name; + struct sockaddr client; + unsigned int socklen; + fd_set ifds; + int nfds; + int sockopt; + + /* Create the socket. */ + debug("Creating source port to forward.\n"); + sock = socket (PF_INET, SOCK_STREAM, 0); + if (sock < 0) + fatal("socket() failed, errno=%d\n", socket_errno()); + sockopt = 1; + setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, + (void*)&sockopt, sizeof(sockopt)); + + /* Give the socket a name. */ + name.sin_family = AF_INET; + name.sin_port = htons (port); + name.sin_addr.s_addr = htonl (INADDR_ANY); + if (bind (sock, (struct sockaddr *) &name, sizeof (name)) < 0) + fatal ("bind() failed, errno=%d\n", socket_errno()); + + if (listen( sock, 1) < 0) + fatal ("listen() failed, errno=%d\n", socket_errno()); + + /* wait for new connection with watching EOF of stdin. */ + debug ("waiting new connection at port %d (socket=%d)\n", port, sock); + nfds = sock + 1; + do { + int n; + struct timeval *ptmo = NULL; +#ifdef _WIN32 + struct timeval tmo; + tmo.tv_sec = 0; + tmo.tv_usec = 100*1000; /* On Windows, 100ms timeout */ + ptmo = &tmo; +#endif /* _WIN32 */ + FD_ZERO (&ifds); + FD_SET ((SOCKET)sock, &ifds); +#ifndef _WIN32 + FD_SET (0, &ifds); /* watch stdin */ +#endif + n = select (nfds, &ifds, NULL, NULL, ptmo); + if (n == -1) { + fatal ("select() failed, %d\n", socket_errno()); + exit (1); + } +#ifdef _WIN32 + if (0 < stdindatalen()) { + FD_SET (0, &ifds); /* fake */ + n++; + } +#endif + if (0 < n) { + if (FD_ISSET(0, &ifds) && (getchar() <= 0)) { + /* EOF */ + debug ("Give-up waiting port because stdin is closed."); + exit(0); + } + if (FD_ISSET(sock, &ifds)) + break; /* socket is stimulated */ + } + } while (1); + socklen = sizeof(client); + connection = accept( sock, &client, &socklen); + if ( connection < 0 ) + fatal ("accept() failed, errno=%d\n", socket_errno()); + return connection; +} + + + +/** Main of program **/ +int +main( int argc, char **argv ) +{ + int ret; + int remote; /* socket */ + int local_in; /* Local input */ + int local_out; /* Local output */ + int reason; +#ifdef _WIN32 + WSADATA wsadata; + WSAStartup( 0x101, &wsadata); +#endif /* _WIN32 */ + + /* initialization */ + getarg( argc, argv ); + + /* Open local_in and local_out if forwarding a port */ + if ( local_type == LOCAL_SOCKET ) { + /* Relay between local port and destination */ + local_in = local_out = accept_connection( local_port ); + } else { + /* Relay between stdin/stdout and desteination */ + local_in = 0; + local_out = 1; +#ifdef _WIN32 + _setmode(local_in, O_BINARY); + _setmode(local_out, O_BINARY); +#endif + } + +retry: +#ifndef _WIN32 + if (0 < connect_timeout) + set_timeout (connect_timeout); +#endif /* not _WIN32 */ + + if (check_direct(dest_host)) + relay_method = METHOD_DIRECT; + /* make connection */ + if ( relay_method == METHOD_DIRECT ) { + remote = open_connection (dest_host, dest_port); + if ( remote == SOCKET_ERROR ) + fatal( "Unable to connect to destination host, errno=%d\n", + socket_errno()); + } else { + remote = open_connection (relay_host, relay_port); + if ( remote == SOCKET_ERROR ) + fatal( "Unable to connect to relay host, errno=%d\n", + socket_errno()); + } + + /** resolve destination host (SOCKS) **/ +#if !defined(_WIN32) && !defined(__CYGWIN32__) + if (socks_ns.sin_addr.s_addr != 0) + switch_ns (&socks_ns); +#endif /* not _WIN32 && not __CYGWIN32__ */ + if (relay_method == METHOD_SOCKS && + socks_resolve == RESOLVE_LOCAL && + local_resolve (dest_host, &dest_addr) < 0) { + fatal("Unknown host: %s", dest_host); + } + + /** relay negociation **/ + switch ( relay_method ) { + case METHOD_SOCKS: + if ( ((socks_version == 5) && (begin_socks5_relay(remote) < 0)) || + ((socks_version == 4) && (begin_socks4_relay(remote) < 0)) ) + fatal( "failed to begin relaying via SOCKS.\n"); + break; + + case METHOD_HTTP: + ret = begin_http_relay(remote); + switch (ret) { + case START_FORBIDDEN: + close (remote); + fatal("failed to begin relaying via HTTP (Forbidden).\n"); + case START_ERROR: + close (remote); + fatal("failed to begin relaying via HTTP.\n"); + case START_OK: + break; + case START_RETRY: + /* retry with authentication */ + close (remote); + goto retry; + } + break; + case METHOD_HTTPS: + ret = begin_https_relay(remote); + switch (ret) { + case START_FORBIDDEN: + close (remote); + fatal("failed to begin relaying via HTTPS (Forbidden).\n"); + case START_ERROR: + close (remote); + fatal("failed to begin relaying via HTTPS.\n"); + case START_OK: + break; + case START_RETRY: + /* retry with authentication */ + close (remote); + goto retry; + } + break; + case METHOD_TELNET: + if (begin_telnet_relay(remote) < 0) + fatal("failed to begin relaying via telnet.\n"); + break; + } + debug("connected\n"); + +#ifndef _WIN32 + if (0 < connect_timeout) + set_timeout (0); +#endif /* not _WIN32 */ + + /* main loop */ + debug ("start relaying.\n"); +do_repeater: + if (relay_method == METHOD_HTTPS) + { + reason = do_ssl_repeater(local_in, local_out, remote); + } + else + { + reason = do_repeater(local_in, local_out, remote); + } + debug ("relaying done.\n"); + if (local_type == LOCAL_SOCKET && reason == REASON_CLOSED_BY_LOCAL && f_hold_session) + { + /* re-wait at local port without closing remote session */ + debug ("re-waiting at local port %d\n", local_port); + local_in = local_out = accept_connection( local_port ); + debug ("re-start relaying\n"); + goto do_repeater; + } + closesocket(remote); + if ( local_type == LOCAL_SOCKET) + closesocket(local_in); + + debug ("that's all, bye.\n"); + + cleanup_and_exit( 0 ); + return 0; +} + +/* ------------------------------------------------------------ + Local Variables: + compile-command: "gcc -Wall -Wpedantic -o connect connect.c -lssl -lcrypto" + tab-width: 8 + fill-column: 74 + comment-column: 48 + End: + ------------------------------------------------------------ */ + +/*** end of connect.c ***/ From b7b4e39bab71d5bea333d2620db501b97b62f48d Mon Sep 17 00:00:00 2001 From: Jan Just Keijser Date: Wed, 20 May 2020 18:29:41 +0200 Subject: [PATCH 27/45] Added manual page; sync help with man page --- connect-proxy.1 | 198 ++++++++++++++++++++++++++++++++++++++++++++++++ connect.c | 4 +- 2 files changed, 201 insertions(+), 1 deletion(-) create mode 100644 connect-proxy.1 diff --git a/connect-proxy.1 b/connect-proxy.1 new file mode 100644 index 0000000..058ccb7 --- /dev/null +++ b/connect-proxy.1 @@ -0,0 +1,198 @@ +.TH "CONNECT-PROXY" "1" +.SH "NAME" +connect-proxy \(em connect over SOCKS4/5, HTTP or HTTPS proxy +.SH "SYNOPSIS" +.PP +\fBconnect-proxy\fR [\fB-dnhsxt45\fP] [\fB-R \fIresolve\fR \fP] [\fB-p \fIlocal-port\fR \fP] [\fB-w \fIsecs\fR \fP] [\fB-H \fI[user@]proxy-server[:port]]\fR \fP] [\fB-S \fI[user@]socks-server[:port]]\fR \fP] [\fB-a \fIsocks-auth-method\fR \fP] [\fB-T \fIproxy-server[:port]\fR \fP] [\fB-c \fItelnet-proxy-command\fR \fP] [\fB-X \fI[user@]proxy-server:[port]]\fR \fP] [host] [port] +.SH "DESCRIPTION" +.PP +\fBconnect-proxy\fR opens a connection to a remote host over SOCKS4/5, HTTP or HTTPS proxies. +.PP +Please, note that any HTTP-Proxy tunnel won't work with content-inspection firewall (unless using SSL). +.SH "OPTIONS" +.TP +\fB\-\-help +Show options. +.\"********************************************************* +.TP +\fB\-H [user@]proxy-server[:port] \fRor\fB \-\-http-proxy-server [user@]proxy-server[:port] +specifies a hostname and port number of the HTTP proxy server to relay. +If the port is omitted, 80 is used. You can specify this value in the environment variable +HTTP_PROXY and pass the \-h option to use it. +If the user is omitted, the current userid is used. You can specify this value in the environment variable +HTTP_PROXY_USER. Simple HTTP Basic-Auth is supported. +.TP +.\"********************************************************* +\fB\-S [user@]proxy-server[:port] \fRor\fB \-\-socks-proxy-server [user@]proxy-server[:port] +specifies the hostname and port number of the SOCKS server to relay. +Like \-H, the port number can be omitted and the default is 1080. +You can also specify this value pair in the environment +variable SOCKS_SERVER or SOCKS5_SERVER and give the \-s option to use it. +If the user is omitted, the current userid is used. You can specify this value in the environment variable +SOCKS_USER or SOCKS5_USER. +.\"********************************************************* +.TP +\fB\-T proxy-server[:port] \fRor\fB \-\-telnet-server proxy-server[:port] +(EXPERIMENTAL) specifies a hostname and port number of the Telnet proxy server to relay. +If the port is omitted, 22 is used. You can specify this value in the environment variable +TELNET_PROXY and pass the \-t option to use it. +.\"********************************************************* +.TP +\fB\-X [user@]proxy-server[:port] \fRor\fB \-\-https-proxy-server [user@]proxy-server[:port] +specifies a hostname and port number of the HTTPS proxy server to relay. +If the port is omitted, 443 is used. You can specify this value in the environment variable +HTTPS_PROXY and pass the \-x option to use it. +If the user is omitted, the current userid is used. You can specify this value in the environment variable +HTTPS_PROXY_USER. Simple HTTPS Basic-Auth as well as client-side certificate authentication is supported. +If a password is required for remote authentiation, either a simple terminal prompt or the $SSH_ASKPASS +program will be used to query the user for the password. +.\"********************************************************* +.TP +\fB\-\-https-proxy-ca CA-cert-file.pem +specifies a PEM-formatted file containing the Certificate Authorities (CA\'s) to trust when connecting +to an HTTPS proxy server. +.\"********************************************************* +.TP +\fB\-\-https-proxy-ca-path CA-dir-path +specifies a directory containing hashed PEM-formatted public certificate files of the Certificate +Authorities (CA\'s) to trust when connecting to an HTTPS proxy server. +.\"********************************************************* +.TP +\fB \-\-https-proxy-certname name +specifies the name of the HTTPS proxy server certificate (/CN=...) if this name is different from +the remote hostname of the HTTPS proxy server itself. +.\"********************************************************* +.TP +\fB--no-check-certificate +disable the verification of the HTTPS proxy server certificate and hostname. +.\"********************************************************* +.TP +\fB\-\-https-user-cert certfile.pem +specifies a PEM-formatted file containing the user (client-side) certificate. Use this, together +with the \'--http-user-key\' option to perform client-side certificate authentication when +connecting to an HTTPS proxy server. +.\"********************************************************* +.TP +\fB\-\-https-user-key keyfile.pem +specifies a PEM-formatted file containing the user (client-side) private key. Use this, together +with the \'--http-user-cert\' option to perform client-side certificate authentication when +connecting to an HTTPS proxy server. +If the private key is protected using a passphrase, either a simple terminal prompt or the $SSH_ASKPASS +program will be used to query the user for the passphrase. +.\"********************************************************* +.TP +\fB-4 +specifies SOCKS relaying and indicates protocol version to use. +It is valid only when used with '\-s' or '\-S'. +Default is '\-5' (protocol version 5). +.\"********************************************************* +.TP +\fB-a socks5-auth-method +(EXPERIMENTAL) specifies the authentication method when connecting to a SOCKS5 server. +The keywords "none", "gssapi", "userpass" and "chap" are acceptable. +You can specify this value in the environment variable SOCKS5_AUTH. +.\"********************************************************* +.TP +\fB-c telnet-command +(EXPERIMENTAL) specifies the \'telnet\' command to use when connecting to a Telnet proxy server. +.\"********************************************************* +.TP +\fB-R +specifies the method to resolve the hostname when connecting to a SOCKS server. +Three keywords ("local", "remote", "both") or dot-notation IP address are acceptable. +The keyword "both" means, "Try local first, then remote". +If a dot-notation IP address is specified, use this host as nameserver. The default is "remote" for SOCKS5 or +"local" for SOCKS4. +On SOCKS4 protocol, remote resolving method ("remote" and "both") requires protocol 4a supported server. +You can specify this value in the environment variable SOCKS_RESOLVE or SOCKS5_RESOLVE. +.\"********************************************************* +.TP +\fB-p local-port +will forward a local TCP port instead of using the standard input and output. +.\"********************************************************* +.TP +\fB-P local-port +same to '\-p' except keep remote session. The program repeats waiting the port with holding +remote session without disconnecting. To connect the remote session, send EOF to stdin or +kill the program. +.\"********************************************************* +.TP +\fB-w secs +timeout in seconds for making connection with TARGET host. +.\"********************************************************* +.TP +\fB-d +used for debug. If you fail to connect, use this and check request to and response from server. + +.SH "USAGE" +.PP +To use proxy, this example is for SOCKS5 connection to connect to +\host\' at port 25 via SOCKS5 server on \'firewall\' host. + +\fBconnect-proxy \-S firewall host 25\fR + +\fBSOCKS5_SERVER=firewall; export SOCKS5_SERVER; +connect-proxy \-s host 25\fR +.PP +For a HTTP-PROXY connection: + +\fBconnect-proxy \-H proxy-server:8080 host 25\fR + +\fBHTTP_PROXY=proxy-server:8080; export HTTP_PROXY; +connect-proxy \-h host 25\fR +.PP +To forward a local port, for example to use ssh: + +\fBconnect-proxy \-H proxy-server:8080 host 22 \fR +\fBssh \-l user \-p 5550 localhost\fR +.PP +For an HTTPS PROXY connection: + +\fBconnect-proxy \-X proxy-server:443 host 25\fR + +\fBHTTPS_PROXY=proxy-server:443; export HTTPS_PROXY; +connect-proxy \-x host 25\fR +.PP +For an HTTPS PROXY connection with client-side certificate authentication: + +\fBconnect-proxy \-X proxy-server:8443 --https-user-cert ~/.config/usercert.pem +--https-user-key ~/.config/userkey.pem host 25\fR + +.PP +To use it along ssh transparently: +\fB # file://~/.ssh/config +Host * +ProxyCommand connect-proxy \-H proxy-server:8080 %h %p\fR +.SH "ENVIRONMENT" +.PP +LOGNAME, USER, SSH_ASKPASS, +.TP +SOCKS_PROXY, SOCKS_USER, SOCKS_RESOLVE, +.TP +SOCKS5_PROXY, SOCKS5_USER, SOCKS5_RESOLVE, SOCKS5_AUTH, +.TP +HTTP_PROXY, HTTP_PROXY_USER, HTTPS_PROXY, HTTPS_PROXY_USER, +.TP +HTTPS_PROXY_CERTNAME, HTTPS_PROXY_CA_FILE, HTTPS_PROXY_CA_PATH, +HTTPS_PROXY_USERCERT, HTTPS_PROXY_USERKEY + +.SH "SEE ALSO" +.PP +ssh (1). +.SH "WWW" +.PP +https://github.com/jjkeijser/connect-proxy +.PP +http://www.taiyo.co.jp/~gotoh/ssh/connect.html +.SH "COPYRIGHT" +.PP +Permission is granted to copy, distribute and/or modify this document under +the terms of the GNU General Public License, Version 2 any +later version published by the Free Software Foundation. +.SH "AUTHOR" +.PP +This manual page was adapted by Jan Just Keijser jan.just.keijser@gmail.com +from the \fBDebian\fP manual page, written by Philippe COVAL Philippe.COVAL@laposte.net. +.PP +HTTPS support and the \'long\' format options were added by Jan Just Keijser +jan.just.keijser@gmail.com. diff --git a/connect.c b/connect.c index dcfd21c..9bb66c1 100644 --- a/connect.c +++ b/connect.c @@ -296,6 +296,8 @@ static char *usage2 = " [--http-proxy [user@]proxy-server[:port]]\n" " [--telnet-proxy proxy-server[:port]\n" " [--https-proxy [user@]proxy-server[:port]]\n" +" [--https-proxy-ca PEM format file of CA's]\n" +" [--https-proxy-ca-path PEM format directory of CA's]\n" " [--https-proxy-certname name]\n" " [--https-user-cert certfile.pem]\n" " [--https-user-key keyfile.pem]\n" @@ -305,7 +307,7 @@ static char *usage2 = /* name of this program */ char *progname = NULL; static char *progdesc = "connect --- simple relaying command via proxy."; -static char *revstr = "2.0"; +static char *revstr = "2.01"; /* set of character for strspn() */ const char *digits = "0123456789"; From 712e9b8773a00ecf1a3aa190e84257d58f43eaf6 Mon Sep 17 00:00:00 2001 From: Jan Just Keijser Date: Wed, 20 May 2020 18:33:27 +0200 Subject: [PATCH 28/45] sync README.md with man page --- README.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index be16042..2e619ba 100644 --- a/README.md +++ b/README.md @@ -16,22 +16,24 @@ How To Use * You can specify proxy method in an environment variable or in a command line option. * usage: - ./connect [-dnhstx45] [-p local-port][-R resolve] [-w timeout] + /connect [-dnhstx45] [-p local-port][-R resolve] [-w timeout] [-S [user@]socks-server[:port]] [-H [user@]proxy-server[:port]] - [-T proxy-server[:port] [-c telnet-proxy-command] + [-T proxy-server[:port] [-c telnet-proxy-command] [-X [user@]proxy-server[:port]] [--help] [--socks-server [user@]socks-server[:port]] [--http-proxy [user@]proxy-server[:port]] [--telnet-proxy proxy-server[:port] [--https-proxy [user@]proxy-server[:port]] + [--https-proxy-ca PEM format file of CA's] + [--https-proxy-ca-path PEM format directory of CA's] [--https-proxy-certname name] [--https-user-cert certfile.pem] [--https-user-key keyfile.pem] [--no-check-certificate] host port - + * "host" and "port" is for the target hostname and port-number to connect to. * The '-H' or '--http-proxy' option specifies a hostname and port number of the http proxy server to relay. If port is omitted, 80 is used. You can specify this value in the environment variable @@ -57,7 +59,7 @@ How To Use * The '-d' option is used for debug. If you fail to connect, use this and check request to and response from server. - You can omit the "port" argument when program name is special format containing port number +You can omit the "port" argument when program name is special format containing port number itself. For example, $ ln -s connect connect-25 @@ -69,7 +71,7 @@ server on 'firewall' host. $ connect -S firewall host 25 or - $ SOCKS5_SERVER=firewall; export SOCKS5_SERVER + $ SOCKS5_SERVER=firewall; export SOCKS5_SERVER $ connect -s host 25 * For a HTTP-PROXY connection: From 29ccd0c3cebf2f97f07a1b38f387a7a78a1a183d Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Mon, 27 Jul 2020 14:29:26 +0000 Subject: [PATCH 29/45] - Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild Signed-off-by: Fedora Release Engineering --- connect-proxy.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/connect-proxy.spec b/connect-proxy.spec index a2e26c8..f4aadcc 100644 --- a/connect-proxy.spec +++ b/connect-proxy.spec @@ -1,6 +1,6 @@ Name: connect-proxy Version: 1.100 -Release: 21%{?dist} +Release: 22%{?dist} Summary: SSH Proxy command helper License: GPLv2+ @@ -51,6 +51,9 @@ cp -p %{name}.1 $RPM_BUILD_ROOT%{_mandir}/man1/ %{_bindir}/%{name} %changelog +* Mon Jul 27 2020 Fedora Release Engineering - 1.100-22 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild + * Tue Jan 28 2020 Fedora Release Engineering - 1.100-21 - Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild From 471dc2c6c30f2da06e093938388667acbf62a687 Mon Sep 17 00:00:00 2001 From: Tom Stellard Date: Thu, 17 Dec 2020 03:51:22 +0000 Subject: [PATCH 30/45] Add BuildRequires: make https://fedoraproject.org/wiki/Changes/Remove_make_from_BuildRoot --- connect-proxy.spec | 1 + 1 file changed, 1 insertion(+) diff --git a/connect-proxy.spec b/connect-proxy.spec index f4aadcc..5c9ea10 100644 --- a/connect-proxy.spec +++ b/connect-proxy.spec @@ -14,6 +14,7 @@ Patch0: connect-proxy-make-man.patch Requires: openssh BuildRequires: gcc +BuildRequires: make %description connect-proxy is the simple relaying command to make network connection via SOCKS and https proxy. It is mainly intended to be used as proxy command From 33bd937632ac375fbb62da5d7e726a2c987091c7 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Tue, 26 Jan 2021 02:28:25 +0000 Subject: [PATCH 31/45] - Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild Signed-off-by: Fedora Release Engineering --- connect-proxy.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/connect-proxy.spec b/connect-proxy.spec index 5c9ea10..6adbc48 100644 --- a/connect-proxy.spec +++ b/connect-proxy.spec @@ -1,6 +1,6 @@ Name: connect-proxy Version: 1.100 -Release: 22%{?dist} +Release: 23%{?dist} Summary: SSH Proxy command helper License: GPLv2+ @@ -52,6 +52,9 @@ cp -p %{name}.1 $RPM_BUILD_ROOT%{_mandir}/man1/ %{_bindir}/%{name} %changelog +* Tue Jan 26 2021 Fedora Release Engineering - 1.100-23 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild + * Mon Jul 27 2020 Fedora Release Engineering - 1.100-22 - Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild From e7308bd1b78f7e1cef4b18f16e6efca34be1b547 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Wed, 21 Jul 2021 14:59:50 +0000 Subject: [PATCH 32/45] - Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild Signed-off-by: Fedora Release Engineering From 725323a91e11dfa56063e7a7651dc6d50c418464 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Wed, 21 Jul 2021 19:58:15 +0000 Subject: [PATCH 33/45] - Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild Signed-off-by: Fedora Release Engineering --- connect-proxy.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/connect-proxy.spec b/connect-proxy.spec index 6adbc48..07628e4 100644 --- a/connect-proxy.spec +++ b/connect-proxy.spec @@ -1,6 +1,6 @@ Name: connect-proxy Version: 1.100 -Release: 23%{?dist} +Release: 24%{?dist} Summary: SSH Proxy command helper License: GPLv2+ @@ -52,6 +52,9 @@ cp -p %{name}.1 $RPM_BUILD_ROOT%{_mandir}/man1/ %{_bindir}/%{name} %changelog +* Wed Jul 21 2021 Fedora Release Engineering - 1.100-24 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild + * Tue Jan 26 2021 Fedora Release Engineering - 1.100-23 - Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild From fc4a8eaf190e1f7769a4199b1a2134889b669b06 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Wed, 19 Jan 2022 23:45:30 +0000 Subject: [PATCH 34/45] - Rebuilt for https://fedoraproject.org/wiki/Fedora_36_Mass_Rebuild Signed-off-by: Fedora Release Engineering --- connect-proxy.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/connect-proxy.spec b/connect-proxy.spec index 07628e4..3cdab65 100644 --- a/connect-proxy.spec +++ b/connect-proxy.spec @@ -1,6 +1,6 @@ Name: connect-proxy Version: 1.100 -Release: 24%{?dist} +Release: 25%{?dist} Summary: SSH Proxy command helper License: GPLv2+ @@ -52,6 +52,9 @@ cp -p %{name}.1 $RPM_BUILD_ROOT%{_mandir}/man1/ %{_bindir}/%{name} %changelog +* Wed Jan 19 2022 Fedora Release Engineering - 1.100-25 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_36_Mass_Rebuild + * Wed Jul 21 2021 Fedora Release Engineering - 1.100-24 - Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild From 9b4a41c52e73906882788cf345bd49baf494d0f4 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Wed, 20 Jul 2022 23:29:56 +0000 Subject: [PATCH 35/45] Rebuilt for https://fedoraproject.org/wiki/Fedora_37_Mass_Rebuild Signed-off-by: Fedora Release Engineering --- connect-proxy.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/connect-proxy.spec b/connect-proxy.spec index 3cdab65..be3c75f 100644 --- a/connect-proxy.spec +++ b/connect-proxy.spec @@ -1,6 +1,6 @@ Name: connect-proxy Version: 1.100 -Release: 25%{?dist} +Release: 26%{?dist} Summary: SSH Proxy command helper License: GPLv2+ @@ -52,6 +52,9 @@ cp -p %{name}.1 $RPM_BUILD_ROOT%{_mandir}/man1/ %{_bindir}/%{name} %changelog +* Wed Jul 20 2022 Fedora Release Engineering - 1.100-26 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_37_Mass_Rebuild + * Wed Jan 19 2022 Fedora Release Engineering - 1.100-25 - Rebuilt for https://fedoraproject.org/wiki/Fedora_36_Mass_Rebuild From 5c9f4a931c151d7a99896297911ab90d9e6ba2e9 Mon Sep 17 00:00:00 2001 From: Florian Weimer Date: Tue, 6 Dec 2022 07:23:53 +0100 Subject: [PATCH 36/45] Port to C99 (#2151093) Add a missing int type. Related to: --- connect-1.100.c | 1 + connect-proxy.spec | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/connect-1.100.c b/connect-1.100.c index b275b77..ba9d452 100644 --- a/connect-1.100.c +++ b/connect-1.100.c @@ -1028,6 +1028,7 @@ is_direct_address (const struct in_addr addr) ends_with("foo.beebar.com", "bar.com") => 0 (partial match) ends_with("bar", "bar.com") => 0 (shorter) */ +int domain_match(const char *s1, const char *s2) { int len1, len2; diff --git a/connect-proxy.spec b/connect-proxy.spec index be3c75f..9cddb16 100644 --- a/connect-proxy.spec +++ b/connect-proxy.spec @@ -1,6 +1,6 @@ Name: connect-proxy Version: 1.100 -Release: 26%{?dist} +Release: 27%{?dist} Summary: SSH Proxy command helper License: GPLv2+ @@ -52,6 +52,9 @@ cp -p %{name}.1 $RPM_BUILD_ROOT%{_mandir}/man1/ %{_bindir}/%{name} %changelog +* Tue Dec 6 2022 Florian Weimer - 1.100-27 +- Port to C99 (#2151093) + * Wed Jul 20 2022 Fedora Release Engineering - 1.100-26 - Rebuilt for https://fedoraproject.org/wiki/Fedora_37_Mass_Rebuild From c91e03019f5d95fc73636d39ccdf9e794d64498e Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Thu, 19 Jan 2023 00:25:28 +0000 Subject: [PATCH 37/45] Rebuilt for https://fedoraproject.org/wiki/Fedora_38_Mass_Rebuild Signed-off-by: Fedora Release Engineering --- connect-proxy.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/connect-proxy.spec b/connect-proxy.spec index 9cddb16..57783e6 100644 --- a/connect-proxy.spec +++ b/connect-proxy.spec @@ -1,6 +1,6 @@ Name: connect-proxy Version: 1.100 -Release: 27%{?dist} +Release: 28%{?dist} Summary: SSH Proxy command helper License: GPLv2+ @@ -52,6 +52,9 @@ cp -p %{name}.1 $RPM_BUILD_ROOT%{_mandir}/man1/ %{_bindir}/%{name} %changelog +* Thu Jan 19 2023 Fedora Release Engineering - 1.100-28 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_38_Mass_Rebuild + * Tue Dec 6 2022 Florian Weimer - 1.100-27 - Port to C99 (#2151093) From 4aba2c588ce34d81852077e889afd367b7fbf275 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Wed, 19 Jul 2023 16:17:18 +0000 Subject: [PATCH 38/45] Rebuilt for https://fedoraproject.org/wiki/Fedora_39_Mass_Rebuild Signed-off-by: Fedora Release Engineering --- connect-proxy.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/connect-proxy.spec b/connect-proxy.spec index 57783e6..1ffcaa2 100644 --- a/connect-proxy.spec +++ b/connect-proxy.spec @@ -1,6 +1,6 @@ Name: connect-proxy Version: 1.100 -Release: 28%{?dist} +Release: 29%{?dist} Summary: SSH Proxy command helper License: GPLv2+ @@ -52,6 +52,9 @@ cp -p %{name}.1 $RPM_BUILD_ROOT%{_mandir}/man1/ %{_bindir}/%{name} %changelog +* Wed Jul 19 2023 Fedora Release Engineering - 1.100-29 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_39_Mass_Rebuild + * Thu Jan 19 2023 Fedora Release Engineering - 1.100-28 - Rebuilt for https://fedoraproject.org/wiki/Fedora_38_Mass_Rebuild From a49ac18b1693cd2e1962677dab12b3436a0c68f9 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Fri, 19 Jan 2024 16:05:51 +0000 Subject: [PATCH 39/45] Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild --- connect-proxy.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/connect-proxy.spec b/connect-proxy.spec index 1ffcaa2..a727e0f 100644 --- a/connect-proxy.spec +++ b/connect-proxy.spec @@ -1,6 +1,6 @@ Name: connect-proxy Version: 1.100 -Release: 29%{?dist} +Release: 30%{?dist} Summary: SSH Proxy command helper License: GPLv2+ @@ -52,6 +52,9 @@ cp -p %{name}.1 $RPM_BUILD_ROOT%{_mandir}/man1/ %{_bindir}/%{name} %changelog +* Fri Jan 19 2024 Fedora Release Engineering - 1.100-30 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild + * Wed Jul 19 2023 Fedora Release Engineering - 1.100-29 - Rebuilt for https://fedoraproject.org/wiki/Fedora_39_Mass_Rebuild From d1a983ae4676fbb52081f4678ec4fbbb281b1351 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Wed, 24 Jan 2024 07:57:16 +0000 Subject: [PATCH 40/45] Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild --- connect-proxy.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/connect-proxy.spec b/connect-proxy.spec index a727e0f..e0924ac 100644 --- a/connect-proxy.spec +++ b/connect-proxy.spec @@ -1,6 +1,6 @@ Name: connect-proxy Version: 1.100 -Release: 30%{?dist} +Release: 31%{?dist} Summary: SSH Proxy command helper License: GPLv2+ @@ -52,6 +52,9 @@ cp -p %{name}.1 $RPM_BUILD_ROOT%{_mandir}/man1/ %{_bindir}/%{name} %changelog +* Wed Jan 24 2024 Fedora Release Engineering - 1.100-31 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild + * Fri Jan 19 2024 Fedora Release Engineering - 1.100-30 - Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild From a6d9d307693ce57217b7be39e1f218a260374ded Mon Sep 17 00:00:00 2001 From: Timotheus Pokorra Date: Fri, 17 May 2024 09:23:23 +0200 Subject: [PATCH 41/45] Update to upstream sources 1.105 --- .gitignore | 1 + INSTALL.md | 39 - README.md | 153 -- connect-1.100.c | 2983 ----------------------- connect-proxy-1.105-socklen.patch | 11 + connect-proxy.spec | 33 +- connect.c | 3727 ----------------------------- connect.html | 1142 --------- sources | 1 + 9 files changed, 30 insertions(+), 8060 deletions(-) delete mode 100644 INSTALL.md delete mode 100644 README.md delete mode 100644 connect-1.100.c create mode 100644 connect-proxy-1.105-socklen.patch delete mode 100644 connect.c delete mode 100644 connect.html diff --git a/.gitignore b/.gitignore index e69de29..c0a15ec 100644 --- a/.gitignore +++ b/.gitignore @@ -0,0 +1 @@ +/ssh-connect-1.105.tar.gz diff --git a/INSTALL.md b/INSTALL.md deleted file mode 100644 index 8af8d72..0000000 --- a/INSTALL.md +++ /dev/null @@ -1,39 +0,0 @@ -# connect-proxy -Make socket connection using SOCKS4/5, telnet HTTP or HTTPS tunnel. - -************************************************************************* - -QUICK START: - - Unix: - gcc -o connect connect.c -lssl -lcrypto - -************************************************************************* - -The development version can be found here: - - https://github.com/jjkeijser/connect-proxy/ - - -How To Compile -============== -On Linux/UNIX environment: - - gcc -o connect connect.c -lssl -lcrypto - -Or using a specific OpenSSL installation: - - gcc -o connect connect.c -I../openssl-1.1.1g/include - -L../openssl-1.1.1g -lssl -lcrypto - -The default CA certificate file is the RHEL/CentOS/Fedora default: - - /etc/pki/tls/certs/ca-bundle.crt - -You can specify an alternative location using - - gcc -o connect connect.c -D__DEFAULT_CA_PATH__=\"/some/path\" - -lssl -lcrypto - -(mind the quotes!) - diff --git a/README.md b/README.md deleted file mode 100644 index 2e619ba..0000000 --- a/README.md +++ /dev/null @@ -1,153 +0,0 @@ -# connect-proxy -Make socket connection using SOCKS4/5, telnet HTTP or HTTPS tunnel. - -Based on connect.c from Shun-ichi GOTO -* Added HTTPS proxy support -* Made code gcc-9 and valgrind clean - -How To Compile -============== -On Linux/UNIX environment: - - $ gcc connect.c -o connect -lssl -lcrypto - -How To Use -========== -* You can specify proxy method in an environment variable or in a command line option. -* usage: - - /connect [-dnhstx45] [-p local-port][-R resolve] [-w timeout] - [-S [user@]socks-server[:port]] - [-H [user@]proxy-server[:port]] - [-T proxy-server[:port] [-c telnet-proxy-command] - [-X [user@]proxy-server[:port]] - [--help] - [--socks-server [user@]socks-server[:port]] - [--http-proxy [user@]proxy-server[:port]] - [--telnet-proxy proxy-server[:port] - [--https-proxy [user@]proxy-server[:port]] - [--https-proxy-ca PEM format file of CA's] - [--https-proxy-ca-path PEM format directory of CA's] - [--https-proxy-certname name] - [--https-user-cert certfile.pem] - [--https-user-key keyfile.pem] - [--no-check-certificate] - host port - -* "host" and "port" is for the target hostname and port-number to connect to. -* The '-H' or '--http-proxy' option specifies a hostname and port number of the http proxy server to - relay. If port is omitted, 80 is used. You can specify this value in the environment variable - HTTP_PROXY and pass the '-h' option to use it. -* The '-X' or '--https-proxy' option specifies a hostname and port number of the https proxy server to - relay. If port is omitted, 443 is used. You can specify this value in the environment variable - HTTPS_PROXY and pass the '-x' option to use it. -* The '-S' or '--socks-proxy' option specifies the hostname and port number of the SOCKS server to - relay. Like '-H', port number can be omitted and the default is 1080. You can also specify this - value pair in the environment variable SOCKS5_SERVER and give the '-s' option to use it. -* The '-4' and the '-5' options are for specifying SOCKS relaying and indicates protocol version - to use. It is valid only when used with '-s' or '-S'. Default is '-5' (protocol version 5) -* The '-R' option is for specifying method to resolve the hostname. Three keywords ("local", - "remote", "both") or dot-notation IP address are acceptable. The keyword "both" means, "Try local - first, then remote". If a dot-notation IP address is specified, use this host as nameserver. The - default is "remote" for SOCKS5 or "local" for others. On SOCKS4 protocol, remote resolving method - ("remote" and "both") requires protocol 4a supported server. -* The '-p' option will forward a local TCP port instead of using the standard input and output. -* The '-P' option is same to '-p' except keep remote session. The program repeats waiting the port - with holding remote session without - disconnecting. To disconnect the remote session, send EOF to stdin or kill the program. -* The '-w' option specifys timeout seconds for making connection with TARGET host. -* The '-d' option is used for debug. If you fail to connect, use this and check request to and - response from server. - -You can omit the "port" argument when program name is special format containing port number -itself. For example, - - $ ln -s connect connect-25 - means this connect-25 command is spcifying port number 25 already so you need not 2nd argument -(and ignored if specified). -* To use proxy, this example is for SOCKS5 connection to connect to 'host' at port 25 via SOCKS5 -server on 'firewall' host. - - $ connect -S firewall host 25 - or - - $ SOCKS5_SERVER=firewall; export SOCKS5_SERVER - $ connect -s host 25 -* For a HTTP-PROXY connection: - - $ connect -H proxy-server:8080 host 25 - or - - $ HTTP_PROXY=proxy-server:8080; export HTTP_PROXY - $ connect -h host 25 -* For a HTTPS-PROXY connection: - - $ connect -H proxy-server:443 host 25 - or - - $ HTTPS_PROXY=proxy-server:443; export HTTPS_PROXY - $ connect -x host 25 - -TIPS -==== -* Connect.c doesn't have any configuration to specify the SOCKS server. - If you are a mobile user, this limitation might bother you. However, - You can compile connect.c and link with other standard SOCKS library - like the NEC SOCKS5 library or Dante. This means connect.c is - socksified and uses a configration file like to other SOCKSified - network commands and you can switch configuration file any time - (ex. when ppp startup) that brings you switching of SOCKS server for - connect.c in same way with other commands. For this case, you can - write ~/.ssh/config like this: - - ProxyCommand connect -n %h %p - -SOCKS5 authentication -===================== -* Only USER/PASS authentication is supported. - -HTTP Proxy authentication -========================= -* Only BASIC scheme is supported. - -HTTPS proxy authentication -========================== -* BASIC scheme is supported. -* The server certificate can be verified against a CA certificate (or list of CA - certficates) by specifying either '--https-ca-file' or '--https-ca-path'. - (default file: /etc/pki/tls/certs/ca-bundle.crt). -* By default, the server certificate name (/CN=...) is checked against the hostname - of the https_proxy server. It is possible to specify an alternative name using - '--http-proxy-certname'. -* You can disable server certificate verification by specifying '--no-certificate-check'. -* Certificate based authentication is supported. Use the '--https-user-cert' and - '--https-user-key' parameters to specify the user certificate and key. If the private - key is protected using a passphrase, the $SSH_ASKPASS program will be used to query the user. - -The following environment variables can be used to specify the above parameters: -* HTTPS proxy server: $HTTPS_PROXY -* proxy user: $HTTPS_PROXY_USER -* proxy password: $HTTPS_PROXY_PASSWORD -* server certificate name: $HTTPS_PROXY_CERTNAME -* CA certificate name: $HTTPS_PROXY_CA_FILE -* CA certificate path: $HTTPS_PROXY_CA_PATH -* client certificate file: $HTTPS_PROXY_USERCERT -* client privatekey file: $HTTPS_PROXY_USERKEY - -Authentication information -========================== -The User name for authentication is specifed by an environment variable or system login name. And -password is specified from environment variable or external program (specified in $SSH_ASKPASS) or -tty. -The following environment variable is used for specifying user name. -- SOCKS: $SOCKS5_USER, $LOGNAME, $USER -- HTTP Proxy: $HTTP_PROXY_USER, $LOGNAME, $USER -- HTTPS Proxy: $HTTPS_PROXY_USER, $LOGNAME, $USER - -ssh-askpass support -=================== -You can use ssh-askpass (came from OpenSSH or else) to specify password on graphical environment -(X-Window or MS Windows). To use this, set program name to environment variable SSH_ASKPASS. On -UNIX, X-Window must be required, so $DISPLAY environment variable is also needed. On Win32 -environment, $DISPLAY is not mentioned. - diff --git a/connect-1.100.c b/connect-1.100.c deleted file mode 100644 index ba9d452..0000000 --- a/connect-1.100.c +++ /dev/null @@ -1,2983 +0,0 @@ -/*********************************************************************** - * connect.c -- Make socket connection using SOCKS4/5 and HTTP tunnel. - * - * Copyright (c) 2000-2006 Shun-ichi Goto - * Copyright (c) 2002, J. Grant (English Corrections) - * - * 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 - * of the License, 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 this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - * --------------------------------------------------------- - * PROJECT: My Test Program - * AUTHOR: Shun-ichi GOTO - * CREATE: Wed Jun 21, 2000 - * REVISION: $Revision$ - * --------------------------------------------------------- - * - * Getting Source - * ============== - * - * Recent version of 'connect.c' is available from - * http://www.taiyo.co.jp/~gotoh/ssh/connect.c - * - * Related tool, ssh-askpass.exe (alternative ssh-askpass on UNIX) - * is available: - * http://www.taiyo.co.jp/~gotoh/ssh/ssh-askpass.exe.gz - * - * See more detail: - * http://www.taiyo.co.jp/~gotoh/ssh/connect.html - * - * How To Compile - * ============== - * - * On UNIX environment: - * $ gcc connect.c -o connect - * - * On SOLARIS: - * $ gcc -o connect -lresolv -lsocket -lnsl connect.c - * - * on Win32 environment: - * $ cl connect.c wsock32.lib advapi32.lib - * or - * $ bcc32 connect.c wsock32.lib advapi32.lib - * or - * $ gcc connect.c -o connect - * - * on Mac OS X environment: - * $ gcc connect.c -o connect -lresolv - * or - * $ gcc connect.c -o connect -DBIND_8_COMPAT=1 - * - * How To Use - * ========== - * - * You can specify proxy method in an environment variable or in a - * command line option. - * - * usage: connect [-dnhst45] [-R resolve] [-p local-port] [-w sec] - * [-H [user@]proxy-server[:port]] - * [-S [user@]socks-server[:port]] - * [-T proxy-server[:port]] - * [-c telnet proxy command] - * host port - * - * "host" and "port" is for the target hostname and port-number to - * connect to. - * - * The -H option specifys a hostname and port number of the http proxy - * server to relay. If port is omitted, 80 is used. You can specify this - * value in the environment variable HTTP_PROXY and pass the -h option - * to use it. - * - * The -S option specifys the hostname and port number of the SOCKS - * server to relay. Like -H, port number can be omitted and the default - * is 1080. You can also specify this value pair in the environment - * variable SOCKS5_SERVER and give the -s option to use it. - * - * The '-4' and the '-5' options are for specifying SOCKS relaying and - * indicates protocol version to use. It is valid only when used with - * '-s' or '-S'. Default is '-5' (protocol version 5) - * - * The '-R' option is for specifying method to resolve the - * hostname. Three keywords ("local", "remote", "both") or dot-notation - * IP address are acceptable. The keyword "both" means, "Try local - * first, then remote". If a dot-notation IP address is specified, use - * this host as nameserver. The default is "remote" for SOCKS5 or - * "local" for others. On SOCKS4 protocol, remote resolving method - * ("remote" and "both") requires protocol 4a supported server. - * - * The '-p' option will forward a local TCP port instead of using the - * standard input and output. - * - * The '-P' option is same to '-p' except keep remote session. The - * program repeats waiting the port with holding remote session without - * disconnecting. To disconnect the remote session, send EOF to stdin or - * kill the program. - * - * The '-w' option specifys timeout seconds for making connection with - * TARGET host. - * - * The '-d' option is used for debug. If you fail to connect, use this - * and check request to and response from server. - * - * You can omit the "port" argument when program name is special format - * containing port number itself. For example, - * $ ln -s connect connect-25 - * means this connect-25 command is spcifying port number 25 already - * so you need not 2nd argument (and ignored if specified). - * - * To use proxy, this example is for SOCKS5 connection to connect to - * 'host' at port 25 via SOCKS5 server on 'firewall' host. - * $ connect -S firewall host 25 - * or - * $ SOCKS5_SERVER=firewall; export SOCKS5_SERVER - * $ connect -s host 25 - * - * For a HTTP-PROXY connection: - * $ connect -H proxy-server:8080 host 25 - * or - * $ HTTP_PROXY=proxy-server:8080; export HTTP_PROXY - * $ connect -h host 25 - * To forward a local port, for example to use ssh: - * $ connect -p 5550 -H proxy-server:8080 host 22 - * ($ ssh -l user -p 5550 localhost ) - * - * TIPS - * ==== - * - * Connect.c doesn't have any configuration to specify the SOCKS server. - * If you are a mobile user, this limitation might bother you. However, - * You can compile connect.c and link with other standard SOCKS library - * like the NEC SOCKS5 library or Dante. This means connect.c is - * socksified and uses a configration file like to other SOCKSified - * network commands and you can switch configuration file any time - * (ex. when ppp startup) that brings you switching of SOCKS server for - * connect.c in same way with other commands. For this case, you can - * write ~/.ssh/config like this: - * - * ProxyCommand connect -n %h %p - * - * SOCKS5 authentication - * ===================== - * - * Only USER/PASS authentication is supported. - * - * Proxy authentication - * ==================== - * - * Only BASIC scheme is supported. - * - * Authentication informations - * =========================== - * - * User name for authentication is specifed by an environment variable - * or system login name. And password is specified from environment - * variable or external program (specified in $SSH_ASKPASS) or tty. - * - * Following environment variable is used for specifying user name. - * SOCKS: $SOCKS5_USER, $LOGNAME, $USER - * HTTP Proxy: $HTTP_PROXY_USER, $LOGNAME, $USER - * - * ssh-askpass support - * =================== - * - * You can use ssh-askpass (came from OpenSSH or else) to specify - * password on graphical environment (X-Window or MS Windows). To use - * this, set program name to environment variable SSH_ASKPASS. On UNIX, - * X-Window must be required, so $DISPLAY environment variable is also - * needed. On Win32 environment, $DISPLAY is not mentioned. - * - * Related Informations - * ==================== - * - * SOCKS5 -- RFC 1928, RFC 1929, RFC 1961 - * NEC SOCKS Reference Implementation is available from: - * http://www.socks.nec.com - * DeleGate version 5 or earlier can be SOCKS4 server, - * and version 6 can be SOCKS5 and SOCKS4 server. - * and version 7.7.0 or later can be SOCKS5 and SOCKS4a server. - * http://www.delegate.org/delegate/ - * - * HTTP-Proxy -- - * Many http proxy servers supports this, but https should - * be allowed as configuration on your host. - * For example on DeleGate, you should add "https" to the - * "REMITTABLE" parameter to allow HTTP-Proxy like this: - * delegated -Pxxxx ...... REMITTABLE="+,https" ... - * - * Hypertext Transfer Protocol -- HTTP/1.1 -- RFC 2616 - * HTTP Authentication: Basic and Digest Access Authentication -- RFC 2617 - * For proxy authentication, refer these documents. - * - ***********************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef __CYGWIN32__ -#undef _WIN32 -#endif - -#ifdef _WIN32 -#include -#include -#include -#include -#include -#else /* !_WIN32 */ -#include -#include -#include -#include -#ifndef __hpux -#include -#endif /* __hpux */ -#include -#include -#include -#include -#if !defined(_WIN32) && !defined(__CYGWIN32__) -#define WITH_RESOLVER 1 -#include -#include -#else /* not ( not _WIN32 && not __CYGWIN32__) */ -#undef WITH_RESOLVER -#endif /* not ( not _WIN32 && not __CYGWIN32__) */ -#endif /* !_WIN32 */ - -#ifdef _WIN32 -#define ECONNRESET WSAECONNRESET -#endif /* _WI32 */ - - - -#ifndef LINT -static char *vcid = "$Id$"; -#endif - -/* Microsoft Visual C/C++ has _snprintf() and _vsnprintf() */ -#ifdef _MSC_VER -#define snprintf _snprintf -#define vsnprintf _vsnprintf -#endif - -/* consider Borland C */ -#ifdef __BORLANDC__ -#define _kbhit kbhit -#define _setmode setmode -#endif - -/* help message. - Win32 environment does not support -R option (vc and cygwin) - Win32 native compilers does not support -w option, yet (vc) -*/ -static char *usage = "usage: %s [-dnhst45] [-p local-port]" -#ifdef _WIN32 -#ifdef __CYGWIN32__ -"[-w timeout] \n" /* cygwin cannot -R */ -#else /* not __CYGWIN32__ */ -" \n" /* VC cannot -w nor -R */ -#endif /* not __CYGWIN32__ */ -#else /* not _WIN32 */ -/* help message for UNIX */ -"[-R resolve] [-w timeout] \n" -#endif /* not _WIN32 */ -" [-H proxy-server[:port]] [-S [user@]socks-server[:port]] \n" -" [-T proxy-server[:port]]\n" -" [-c telnet-proxy-command]\n" -" host port\n"; - -/* name of this program */ -char *progname = NULL; -char *progdesc = "connect --- simple relaying command via proxy."; -char *rcs_revstr = "$Revision$"; -char *revstr = NULL; -int major_version = 1; -int minor_version = 0; - -/* set of character for strspn() */ -const char *digits = "0123456789"; -const char *dotdigits = "0123456789."; - -/* options */ -int f_debug = 0; - -/* report flag to hide secure information */ -int f_report = 1; - -int connect_timeout = 0; - -/* local input type */ -#define LOCAL_STDIO 0 -#define LOCAL_SOCKET 1 -char *local_type_names[] = { "stdio", "socket" }; -int local_type = LOCAL_STDIO; -u_short local_port = 0; /* option 'p' */ -int f_hold_session = 0; /* option 'P' */ - -char *telnet_command = "telnet %h %p"; - -/* utiity types, pair holder of number and string */ -typedef struct { - int num; - const char *str; -} LOOKUP_ITEM; - -/* relay method, server and port */ -#define METHOD_UNDECIDED 0 -#define METHOD_DIRECT 1 -#define METHOD_SOCKS 2 -#define METHOD_HTTP 3 -#define METHOD_TELNET 4 -char *method_names[] = { "UNDECIDED", "DIRECT", "SOCKS", "HTTP", "TELNET" }; - -int relay_method = METHOD_UNDECIDED; /* relaying method */ -char *relay_host = NULL; /* hostname of relay server */ -u_short relay_port = 0; /* port of relay server */ -char *relay_user = NULL; /* user name for auth */ - -/* destination target host and port */ -char *dest_host = NULL; -struct sockaddr_in dest_addr; -u_short dest_port = 0; - -/* informations for SOCKS */ -#define SOCKS5_REP_SUCCEEDED 0x00 /* succeeded */ -#define SOCKS5_REP_FAIL 0x01 /* general SOCKS serer failure */ -#define SOCKS5_REP_NALLOWED 0x02 /* connection not allowed by ruleset */ -#define SOCKS5_REP_NUNREACH 0x03 /* Network unreachable */ -#define SOCKS5_REP_HUNREACH 0x04 /* Host unreachable */ -#define SOCKS5_REP_REFUSED 0x05 /* connection refused */ -#define SOCKS5_REP_EXPIRED 0x06 /* TTL expired */ -#define SOCKS5_REP_CNOTSUP 0x07 /* Command not supported */ -#define SOCKS5_REP_ANOTSUP 0x08 /* Address not supported */ -#define SOCKS5_REP_INVADDR 0x09 /* Inalid address */ - -LOOKUP_ITEM socks5_rep_names[] = { - { SOCKS5_REP_SUCCEEDED, "succeeded"}, - { SOCKS5_REP_FAIL, "general SOCKS server failure"}, - { SOCKS5_REP_NALLOWED, "connection not allowed by ruleset"}, - { SOCKS5_REP_NUNREACH, "Network unreachable"}, - { SOCKS5_REP_HUNREACH, "Host unreachable"}, - { SOCKS5_REP_REFUSED, "connection refused"}, - { SOCKS5_REP_EXPIRED, "TTL expired"}, - { SOCKS5_REP_CNOTSUP, "Command not supported"}, - { SOCKS5_REP_ANOTSUP, "Address not supported"}, - { SOCKS5_REP_INVADDR, "Invalid address"}, - { -1, NULL } -}; - -/* SOCKS5 authentication methods */ -#define SOCKS5_AUTH_REJECT 0xFF /* No acceptable auth method */ -#define SOCKS5_AUTH_NOAUTH 0x00 /* without authentication */ -#define SOCKS5_AUTH_GSSAPI 0x01 /* GSSAPI */ -#define SOCKS5_AUTH_USERPASS 0x02 /* User/Password */ -#define SOCKS5_AUTH_CHAP 0x03 /* Challenge-Handshake Auth Proto. */ -#define SOCKS5_AUTH_EAP 0x05 /* Extensible Authentication Proto. */ -#define SOCKS5_AUTH_MAF 0x08 /* Multi-Authentication Framework */ - -#define SOCKS4_REP_SUCCEEDED 90 /* rquest granted (succeeded) */ -#define SOCKS4_REP_REJECTED 91 /* request rejected or failed */ -#define SOCKS4_REP_IDENT_FAIL 92 /* cannot connect identd */ -#define SOCKS4_REP_USERID 93 /* user id not matched */ - -LOOKUP_ITEM socks4_rep_names[] = { - { SOCKS4_REP_SUCCEEDED, "request granted (succeeded)"}, - { SOCKS4_REP_REJECTED, "request rejected or failed"}, - { SOCKS4_REP_IDENT_FAIL, "cannot connect identd"}, - { SOCKS4_REP_USERID, "user id not matched"}, - { -1, NULL } -}; - -#define RESOLVE_UNKNOWN 0 -#define RESOLVE_LOCAL 1 -#define RESOLVE_REMOTE 2 -#define RESOLVE_BOTH 3 -char *resolve_names[] = { "UNKNOWN", "LOCAL", "REMOTE", "BOTH" }; - -int socks_version = 5; /* SOCKS protocol version */ -int socks_resolve = RESOLVE_UNKNOWN; -struct sockaddr_in socks_ns; -char *socks5_auth = NULL; - -/* Environment variable names */ -#define ENV_SOCKS_SERVER "SOCKS_SERVER" /* SOCKS server */ -#define ENV_SOCKS5_SERVER "SOCKS5_SERVER" -#define ENV_SOCKS4_SERVER "SOCKS4_SERVER" - -#define ENV_SOCKS_RESOLVE "SOCKS_RESOLVE" /* resolve method */ -#define ENV_SOCKS5_RESOLVE "SOCKS5_RESOLVE" -#define ENV_SOCKS4_RESOLVE "SOCKS4_RESOLVE" - -#define ENV_SOCKS5_USER "SOCKS5_USER" /* auth user for SOCKS5 */ -#define ENV_SOCKS4_USER "SOCKS4_USER" /* auth user for SOCKS4 */ -#define ENV_SOCKS_USER "SOCKS_USER" /* auth user for SOCKS */ -#define ENV_SOCKS5_PASSWD "SOCKS5_PASSWD" /* auth password for SOCKS5 */ -#define ENV_SOCKS5_PASSWORD "SOCKS5_PASSWORD" /* old style */ - -#define ENV_HTTP_PROXY "HTTP_PROXY" /* common env var */ -#define ENV_HTTP_PROXY_USER "HTTP_PROXY_USER" /* auth user */ -#define ENV_HTTP_PROXY_PASSWORD "HTTP_PROXY_PASSWORD" /* auth password */ - -#define ENV_TELNET_PROXY "TELNET_PROXY" /* common env var */ - -#define ENV_CONNECT_USER "CONNECT_USER" /* default auth user name */ -#define ENV_CONNECT_PASSWORD "CONNECT_PASSWORD" /* default auth password */ - -#define ENV_SOCKS_DIRECT "SOCKS_DIRECT" /* addr-list for non-proxy */ -#define ENV_SOCKS5_DIRECT "SOCKS5_DIRECT" -#define ENV_SOCKS4_DIRECT "SOCKS4_DIRECT" -#define ENV_HTTP_DIRECT "HTTP_DIRECT" -#define ENV_CONNECT_DIRECT "CONNECT_DIRECT" - -#define ENV_SOCKS5_AUTH "SOCKS5_AUTH" -#define ENV_SSH_ASKPASS "SSH_ASKPASS" /* askpass program */ - -/* Prefix string of HTTP_PROXY */ -#define HTTP_PROXY_PREFIX "http://" -#define PROXY_AUTH_NONE 0 -#define PROXY_AUTH_BASIC 1 -#define PROXY_AUTH_DIGEST 2 -int proxy_auth_type = PROXY_AUTH_NONE; - -/* reason of end repeating */ -#define REASON_UNK -2 -#define REASON_ERROR -1 -#define REASON_CLOSED_BY_LOCAL 0 -#define REASON_CLOSED_BY_REMOTE 1 - -/* return value of relay start function. */ -#define START_ERROR -1 -#define START_OK 0 -#define START_RETRY 1 - -/* socket related definitions */ -#ifndef _WIN32 -#define SOCKET int -#endif -#ifndef SOCKET_ERROR -#define SOCKET_ERROR -1 -#endif - -#ifdef _WIN32 -#define socket_errno() WSAGetLastError() -#else /* !_WIN32 */ -#define closesocket close -#define socket_errno() (errno) -#endif /* !_WIN32 */ - -#ifdef _WIN32 -#define popen _popen -#endif /* WIN32 */ - -/* packet operation macro */ -#define PUT_BYTE(ptr,data) (*(unsigned char*)ptr = data) - -/* debug message output */ -void -debug( const char *fmt, ... ) -{ - va_list args; - if ( f_debug ) { - va_start( args, fmt ); - fprintf(stderr, "DEBUG: "); - vfprintf( stderr, fmt, args ); - va_end( args ); - } -} - -void -debug_( const char *fmt, ... ) /* without prefix */ -{ - va_list args; - if ( f_debug ) { - va_start( args, fmt ); - vfprintf( stderr, fmt, args ); - va_end( args ); - } -} - -/* error message output */ -void -error( const char *fmt, ... ) -{ - va_list args; - va_start( args, fmt ); - fprintf(stderr, "ERROR: "); - vfprintf( stderr, fmt, args ); - va_end( args ); -} - -void -fatal( const char *fmt, ... ) -{ - va_list args; - va_start( args, fmt ); - fprintf(stderr, "FATAL: "); - vfprintf( stderr, fmt, args ); - va_end( args ); - exit (EXIT_FAILURE); -} - - -void * -xmalloc (size_t size) -{ - void *ret = malloc(size); - if (ret == NULL) - fatal("Cannot allocate memory: %d bytes.\n", size); - return ret; -} - -char * -downcase( char *str ) -{ - char *buf = str; - while ( *buf ) { - if ( isupper(*buf) ) - *buf += 'a'-'A'; - buf++; - } - return str; /* return converted arg */ -} - -char * -expand_host_and_port (const char *fmt, const char *host, int port) -{ - const char *src; - char *buf, *dst, *ptr; - size_t len = strlen(fmt) + strlen(host) + 20; - buf = xmalloc (len); - dst = buf; - src = fmt; - - while (*src) { - if (*src == '%') { - switch (src[1]) { - case 'h': - strcpy (dst, host); - src += 2; - break; - case 'p': - snprintf (dst, len, "%d", port); - src += 2; - break; - default: - src ++; - break; - } - dst = buf + strlen (buf); - } else if (*src == '\\') { - switch (src[1]) { - case 'r': /* CR */ - *dst++ = '\r'; - src += 2; - break; - case 'n': /* LF */ - *dst++ = '\n'; - src += 2; - break; - case 't': /* TAB */ - *dst++ = '\t'; - src += 2; - break; - default: - src ++; - break; - } - } else { - /* usual */ - *dst++ = *src++; - } - *dst = '\0'; - } - assert (strlen(buf) < len); - return buf; -} - - -int -lookup_resolve( const char *str ) -{ - char *buf = strdup( str ); - int ret; - - downcase( buf ); - if ( strcmp( buf, "both" ) == 0 ) - ret = RESOLVE_BOTH; - else if ( strcmp( buf, "remote" ) == 0 ) - ret = RESOLVE_REMOTE; - else if ( strcmp( buf, "local" ) == 0 ) - ret = RESOLVE_LOCAL; - else if ( strspn(buf, dotdigits) == strlen(buf) ) { -#ifndef WITH_RESOLVER - fatal("Sorry, you can't specify to resolve the hostname with the -R option on Win32 environment."); -#endif /* not WITH_RESOLVER */ - ret = RESOLVE_LOCAL; /* this case is also 'local' */ - socks_ns.sin_addr.s_addr = inet_addr(buf); - socks_ns.sin_family = AF_INET; - } - else - ret = RESOLVE_UNKNOWN; - free(buf); - return ret; -} - -char * -getusername(void) -{ -#ifdef _WIN32 - static char buf[1024]; - DWORD size = sizeof(buf); - buf[0] = '\0'; - GetUserName( buf, &size); - return buf; -#else /* not _WIN32 */ - struct passwd *pw = getpwuid(getuid()); - if ( pw == NULL ) - fatal("getpwuid() failed for uid: %d\n", getuid()); - return pw->pw_name; -#endif /* not _WIN32 */ -} - -/* expect - check STR is begin with substr with case-ignored comparison. - Return 1 if matched, otherwise 0. -*/ -int -expect( char *str, char *substr) -{ - int len = strlen(substr); - while ( 0 < len-- ) { - if ( toupper(*str) != toupper(*substr) ) - return 0; /* not matched */ - str++, substr++; - } - return 1; /* good, matched */ -} - - -/** PARAMETER operation **/ -#define PARAMETER_FILE "/etc/connectrc" -#define PARAMETER_DOTFILE ".connectrc" -typedef struct { - char* name; - char* value; -} PARAMETER_ITEM; -PARAMETER_ITEM parameter_table[] = { - { ENV_SOCKS_SERVER, NULL }, - { ENV_SOCKS5_SERVER, NULL }, - { ENV_SOCKS4_SERVER, NULL }, - { ENV_SOCKS_RESOLVE, NULL }, - { ENV_SOCKS5_RESOLVE, NULL }, - { ENV_SOCKS4_RESOLVE, NULL }, - { ENV_SOCKS5_USER, NULL }, - { ENV_SOCKS5_PASSWD, NULL }, - { ENV_SOCKS5_PASSWORD, NULL }, - { ENV_HTTP_PROXY, NULL }, - { ENV_HTTP_PROXY_USER, NULL }, - { ENV_HTTP_PROXY_PASSWORD, NULL }, - { ENV_CONNECT_USER, NULL }, - { ENV_CONNECT_PASSWORD, NULL }, - { ENV_SSH_ASKPASS, NULL }, - { ENV_SOCKS5_DIRECT, NULL }, - { ENV_SOCKS4_DIRECT, NULL }, - { ENV_SOCKS_DIRECT, NULL }, - { ENV_HTTP_DIRECT, NULL }, - { ENV_CONNECT_DIRECT, NULL }, - { ENV_SOCKS5_AUTH, NULL }, - { NULL, NULL } -}; - -PARAMETER_ITEM* -find_parameter_item(const char* name) -{ - int i; - for( i = 0; parameter_table[i].name != NULL; i++ ){ - if ( strcmp(name, parameter_table[i].name) == 0 ) - return ¶meter_table[i]; - } - return NULL; -} - -void -read_parameter_file_1(const char* name) -{ - FILE* f; - int line; - char lbuf[1025]; - f = fopen(name, "r"); - if( f ){ - debug("Reading parameter file(%s)\n", name); - for ( line = 1; fgets(lbuf, 1024, f); line++ ) { - char *p, *q, *param, *value; - p = strchr(lbuf, '\n'); - if ( p == NULL ) - fatal("%s:%d: buffer overflow\n", name, line); - *p = '\0'; - p = strchr(lbuf, '#'); - if ( p ) - *p = '\0'; - for ( p = lbuf; *p; p++ ) - if( *p != ' ' && *p != '\t' ) break; - if ( *p == '\0' ) continue; - param = p; - p = strchr(p, '='); - if ( p == NULL ) { - error("%s:%d: missing equal sign\n", name, line); - continue; - } - for ( q = p - 1; q >= lbuf; q-- ) - if ( *q != ' ' && *q != '\t' ) break; - *++q = '\0'; - for ( ++p; *p; p++ ) - if ( *p != ' ' && *p != '\t' ) break; - value = p; - for ( ; *p; p++ ); - for ( p--; p >= lbuf; p-- ) - if ( *p != ' ' && *p != '\t' ) break; - *++p = '\0'; - if ( param && value ) { - PARAMETER_ITEM *item; - item = find_parameter_item(param); - if ( item == NULL ) { - error("%s:%d: unknown parameter `%s'\n", name, line, param); - continue; - } - item->value = strdup(value); - debug("Parameter `%s' is set to `%s'\n", param, value); - } - } - } -} - -void -read_parameter_file(void) -{ -#if !defined(_WIN32) || defined(cygwin) - char *name; - struct passwd *pw; -#endif - - read_parameter_file_1(PARAMETER_FILE); -#if !defined(_WIN32) || defined(cygwin) - pw = getpwuid(getuid()); - if ( pw == NULL ) - fatal("getpwuid() failed for uid: %d\n", getuid()); - name = xmalloc(strlen(pw->pw_dir) + strlen(PARAMETER_DOTFILE) + 2); - strcpy(name, pw->pw_dir); - strcat(name, "/" PARAMETER_DOTFILE); - read_parameter_file_1(name); - free(name); -#endif /* _WIN32 */ -} - -char* -getparam(const char* name) -{ - char *value = getenv(name); - if ( value == NULL ){ - PARAMETER_ITEM *item = find_parameter_item(name); - if ( item != NULL ) - value = item->value; - } - return value; -} - - -/** DIRECT connection **/ -#define MAX_DIRECT_ADDR_LIST 256 - -struct ADDRPAIR { - struct in_addr addr; - struct in_addr mask; - char *name; - int negative; -}; - -struct ADDRPAIR direct_addr_list[MAX_DIRECT_ADDR_LIST]; -int n_direct_addr_list = 0; - -void -mask_addr (void *addr, void *mask, int addrlen) -{ - char *a, *m; - a = addr; - m = mask; - while ( 0 < addrlen-- ) - *a++ &= *m++; -} - -int -add_direct_addr (struct in_addr *addr, struct in_addr *mask, int negative) -{ - struct in_addr iaddr; - char *s; - if ( MAX_DIRECT_ADDR_LIST <= n_direct_addr_list ) { - error("direct address table is full!\n"); - return -1; - } - iaddr = *addr; - mask_addr(&iaddr, mask, sizeof(iaddr)); - s = strdup(inet_ntoa(iaddr)); - debug("adding direct addr entry: %s%s/%s\n", - negative? "!": "", s, inet_ntoa(*mask)); - free(s); - memcpy( &direct_addr_list[n_direct_addr_list].addr, - &iaddr, sizeof(iaddr)); - memcpy( &direct_addr_list[n_direct_addr_list].mask, - mask, sizeof(*mask)); - direct_addr_list[n_direct_addr_list].name = NULL; - direct_addr_list[n_direct_addr_list].negative = negative; - n_direct_addr_list++; - return 0; -} - - -/* add domain/host name entry to direct name table */ -int -add_direct_host( const char *name, int negative) -{ - if ( MAX_DIRECT_ADDR_LIST <= n_direct_addr_list ) { - error("direct address table is full!\n"); - return -1; - } - if (*name == '*') - name++; - if (*name == '.') - name++; - debug("adding direct name entry: %s%s\n", negative? "!": "", name); - direct_addr_list[n_direct_addr_list].name = downcase(strdup(name)); - direct_addr_list[n_direct_addr_list].negative = negative; - n_direct_addr_list++; - return 0; -} - - -int -parse_addr_pair (const char *str, struct in_addr *addr, struct in_addr *mask) -{ - /* NOTE: */ - /* Assume already be splitted by separator - and formatted as folowing: - 1) 12.34.56.789/255.255.255.0 - 2) 12.34.56.789/24 - 3) 12.34.56. - All above generates same addr/mask pair 12.34.56.0 and 255.255.255.0 - */ - const char *ptr; - u_char *dsta, *dstm; - int i, n; - - assert( str != NULL ); - addr->s_addr = 0; - mask->s_addr = 0; - ptr = str; - dsta = (u_char*)&addr->s_addr; - dstm = (u_char*)&mask->s_addr; - for (i=0; i<4; i++ ) { - if ( *ptr == '\0' ) - break; /* case of format #3 */ - if ( !isdigit(*ptr) ) - return -1; /* format error: */ - *dsta++ = atoi( ptr ); - *dstm++ = 255; /* automatic mask for format #3 */ - while ( isdigit(*ptr) ) /* skip digits */ - ptr++; - if ( *ptr == '.' ) - ptr++; - else - break; - } - /* At this point, *ptr points '/' or EOS ('\0') */ - if ( *ptr == '\0' ) - return 0; /* complete as format #3 */ - if ( *ptr != '/' ) - return -1; /* format error */ - /* Now parse mask for format #1 or #2 */ - ptr++; - mask->s_addr = 0; /* clear automatic mask */ - - if ( strchr( ptr, '.') ) { - /* case of format #1 */ - dstm = (u_char*)&mask->s_addr; - for (i=0; i<4; i++) { - if ( !isdigit(*ptr) ) - return -1; /* format error: */ - *dstm++ = atoi(ptr); - while ( isdigit(*ptr) ) /* skip digits */ - ptr++; - if ( *ptr == '.' ) - ptr++; - else - break; /* from for loop */ - } - /* complete as format #1 */ - } else { - /* case of format #2 */ - if ( !isdigit(*ptr) ) - return -1; /* format error: */ - n = atoi(ptr); - if ( n<0 || 32s_addr = (n==0)? 0: htonl(((u_long)0xFFFFFFFF)<<(32-n)); - /* complete as format #1 */ - } - return 0; -} - -void -initialize_direct_addr (void) -{ - int negative; - int n_entries; - char *env = NULL, *beg, *next, *envkey = NULL; - struct in_addr addr, mask; - - if ( relay_method == METHOD_SOCKS ){ - if ( socks_version == 5 ) - envkey = ENV_SOCKS5_DIRECT; - else - envkey = ENV_SOCKS4_DIRECT; - env = getparam(envkey); - if ( env == NULL ) - env = getparam(ENV_SOCKS_DIRECT); - } else if ( relay_method == METHOD_HTTP ){ - env = getparam(ENV_HTTP_DIRECT); - } - - if ( env == NULL ) - env = getparam(ENV_CONNECT_DIRECT); - - if ( env == NULL ) - return; /* no entry */ - debug("making direct addr list from: '%s'\n", env); - env = strdup( env ); /* reallocate to modify */ - beg = next = env; - n_entries = 0; - do { - if ( MAX_DIRECT_ADDR_LIST <= n_entries ) { - error("too many entries in %s", envkey); - break; /* from do loop */ - } - next = strchr( beg, ','); - if ( next != NULL ) - *next++ = '\0'; - addr.s_addr = 0; - mask.s_addr = 0; - if (*beg == '!') { - negative = 1; - beg++; - } else - negative = 0; - if ( !parse_addr_pair( beg, &addr, &mask ) ) { - add_direct_addr( &addr, &mask, negative ); - } else { - add_direct_host( beg, negative ); - } - if ( next != NULL ) - beg = next; - } while ( next != NULL ); - - free( env ); - return; -} - -int -cmp_addr (void *addr1, void *addr2, int addrlen) -{ - return memcmp( addr1, addr2, addrlen ); -} - -int -is_direct_address (const struct in_addr addr) -{ - int i, neg; - struct in_addr iaddr; - - /* Note: assume IPV4 address !! */ - for (i=0; i 1 (exact match) - ends_with("foo.bar.com", "bar.com") => 1 (domain match) - ends_with("foo.beebar.com", "bar.com") => 0 (partial match) - ends_with("bar", "bar.com") => 0 (shorter) - */ -int -domain_match(const char *s1, const char *s2) -{ - int len1, len2; - const char *tail1, *tail2; - len1 = strlen(s1); - len2 = strlen(s2); - if (len1 < len2 || len1 == 0 || len2 == 0) - return 0; /* not match */ - tail1 = s1 + len1; - tail2 = s2 + len2; - while (0 < len1 && 0 < len2) { - if (*--tail1 != *--tail2) - break; /* not match */ - len1--, len2--; - } - if (len2 != 0) - return 0; /* not match */ - /* Now exact match, domain match or partial match. - Return true if exact or domain match. - Or continue checking. */ - if (tail1 == s1 || tail1[-1] == '.') - return 1; /* match! */ - return 0; /* not match */ -} - -/* Check given NAME is ends with one of - registered direct name entry. - Return 1 if matched, or 0. -*/ -int -is_direct_name (const char *name) -{ - int len, i; - const char *tail; - debug("checking %s is for direct?\n", name); - name = downcase(strdup(name)); - len = strlen(name); - if (len < 1) - return 0; /* false */ - tail = &name[len]; - for (i=0; is_port); - debug("service: %s => %d\n", service, port); - } - } - return (u_short)port; -} - -void -make_revstr(void) -{ - char *ptr; - size_t len; - ptr = strstr(rcs_revstr, ": "); - if (!ptr) { - revstr = strdup("unknown"); - return; - } - ptr += 2; - /* assume subversion's keyword expansion like "Revision: 96". */ - minor_version = atoi(ptr); - revstr = xmalloc(20); - snprintf(revstr, 20, "%d.%d", major_version, minor_version); -} - -int -getarg( int argc, char **argv ) -{ - int err = 0; - char *ptr, *server = (char*)NULL; - int method = METHOD_DIRECT; - - progname = *argv; - argc--, argv++; - - /* check optinos */ - while ( (0 < argc) && (**argv == '-') ) { - ptr = *argv + 1; - while ( *ptr ) { - switch ( *ptr ) { - case 's': /* use SOCKS */ - method = METHOD_SOCKS; - break; - - case 'n': /* no proxy */ - method = METHOD_DIRECT; - break; - - case 'h': /* use http-proxy */ - method = METHOD_HTTP; - break; - case 't': - method = METHOD_TELNET; - break; - - case 'S': /* specify SOCKS server */ - if ( 1 < argc ) { - argv++, argc--; - method = METHOD_SOCKS; - server = *argv; - } else { - error("option '-%c' needs argument.\n", *ptr); - err++; - } - break; - - case 'H': /* specify http-proxy server */ - if ( 1 < argc ) { - argv++, argc--; - method = METHOD_HTTP; - server = *argv; - } else { - error("option '-%c' needs argument.\n", *ptr); - err++; - } - break; - case 'T': /* specify telnet proxy server */ - if ( 1 < argc ) { - argv++, argc--; - method = METHOD_TELNET; - server = *argv; - } else { - error("option '-%c' needs argument.\n", *ptr); - err++; - } - break; - - case 'c': - if (1 < argc) { - argv++, argc--; - telnet_command = *argv; - } else { - error("option '%c' needs argument.\n", *ptr); - err++; - } - break; - - case 'P': - f_hold_session = 1; - /* without break */ - case 'p': /* specify port to forward */ - if ( 1 < argc ) { - argv++, argc--; - local_type = LOCAL_SOCKET; - local_port = resolve_port(*argv); - } else { - error("option '-%c' needs argument.\n", *ptr); - err++; - } - break; - -#ifndef _WIN32 - case 'w': - if ( 1 < argc ) { - argv++, argc--; - connect_timeout = atoi(*argv); - } else { - error("option '-%c' needs argument.\n", *ptr); - err++; - } - break; -#endif /* not _WIN32 */ - - case '4': - socks_version = 4; - break; - - case '5': - socks_version = 5; - break; - - case 'a': - if ( 1 < argc ) { - argv++, argc--; - socks5_auth = *argv; - } else { - error("option '-%c' needs argument.\n", *ptr); - err++; - } - break; - - case 'R': /* specify resolve method */ - if ( 1 < argc ) { - argv++, argc--; - socks_resolve = lookup_resolve( *argv ); - } else { - error("option '-%c' needs argument.\n", *ptr); - err++; - } - break; - - case 'V': /* print version */ - fprintf(stderr, "%s\nVersion %s\n", progdesc, revstr); - exit(0); - - case 'd': /* debug mode */ - f_debug++; - break; - - default: - error("unknown option '-%c'\n", *ptr); - err++; - } - ptr++; - } - argc--, argv++; - } - - /* check error */ - if ( 0 < err ) - goto quit; - - set_relay( method, server ); - - /* check destination HOST (MUST) */ - if ( argc == 0 ) { - fprintf(stderr, "%s\nVersion %s\n", progdesc, revstr); - fprintf(stderr, usage, progname); - exit(0); - } - dest_host = argv[0]; - /* decide port or service name from programname or argument */ - if ( ((ptr=strrchr( progname, '/' )) != NULL) || - ((ptr=strchr( progname, '\\')) != NULL) ) - ptr++; - else - ptr = progname; - if ( dest_port == 0 ) { - /* accept only if -P is not specified. */ - if ( 1 < argc ) { - /* get port number from argument (prior to progname) */ - /* NOTE: This way is for cvs ext method. */ - dest_port = resolve_port(argv[1]); - } else if ( strncmp( ptr, "connect-", 8) == 0 ) { - /* decide port number from program name */ - char *str = strdup( ptr+8 ); - str[strcspn( str, "." )] = '\0'; - dest_port = resolve_port(str); - free(str); - } - } - /* check port number */ - if ( dest_port <= 0 ) { - error( "You must specify the destination port correctly.\n"); - err++; - goto quit; - } - if ( (relay_method != METHOD_DIRECT) && (relay_port <= 0) ) { - error("Invalid relay port: %d\n", dest_port); - err++; - goto quit; - } - -quit: - /* report for debugging */ - debug("relay_method = %s (%d)\n", - method_names[relay_method], relay_method); - if ( relay_method != METHOD_DIRECT ) { - debug("relay_host=%s\n", relay_host); - debug("relay_port=%d\n", relay_port); - debug("relay_user=%s\n", relay_user); - } - if ( relay_method == METHOD_SOCKS ) { - debug("socks_version=%d\n", socks_version); - debug("socks_resolve=%s (%d)\n", - resolve_names[socks_resolve], socks_resolve); - } - debug("local_type=%s\n", local_type_names[local_type]); - if ( local_type == LOCAL_SOCKET ) { - debug("local_port=%d\n", local_port); - if (f_hold_session) - debug (" with holding remote session.\n"); - } - debug("dest_host=%s\n", dest_host); - debug("dest_port=%d\n", dest_port); - if ( 0 < err ) { - fprintf(stderr, usage, progname); - exit(1); - } - return 0; -} - -#ifndef _WIN32 -/* Time-out feature is not allowed for Win32 native compilers. */ -/* MSVC and Borland C cannot but Cygwin and UNIXes can. */ - -/* timeout signal hander */ -void -sig_timeout(void) -{ - signal( SIGALRM, SIG_IGN ); - alarm( 0 ); - error( "timed out\n" ); - exit(1); -} - -/* set timeout param = seconds, 0 clears */ -void -set_timeout(int timeout) -{ - /* This feature is allowed for UNIX or cygwin environments, currently */ - if ( timeout == 0 ) { - debug( "clearing timeout\n" ); - signal( SIGALRM, SIG_IGN ); - alarm( 0 ); - } else { - debug( "setting timeout: %d seconds\n", timeout ); - signal(SIGALRM, (void *)sig_timeout); - alarm( timeout ); - } -} -#endif - -#if !defined(_WIN32) && !defined(__CYGWIN32__) -void -switch_ns (struct sockaddr_in *ns) -{ - res_init(); - memcpy (&_res.nsaddr_list[0], ns, sizeof(*ns)); - _res.nscount = 1; - debug("Using nameserver at %s\n", inet_ntoa(ns->sin_addr)); -} -#endif /* !_WIN32 && !__CYGWIN32__ */ - -/* TODO: IPv6 - TODO: fallback if askpass execution failed. - */ - -int -local_resolve (const char *host, struct sockaddr_in *addr) -{ - struct hostent *ent; - if ( strspn(host, dotdigits) == strlen(host) ) { - /* given by IPv4 address */ - addr->sin_family = AF_INET; - addr->sin_addr.s_addr = inet_addr(host); - } else { - debug("resolving host by name: %s\n", host); - ent = gethostbyname (host); - if ( ent ) { - memcpy (&addr->sin_addr, ent->h_addr, ent->h_length); - addr->sin_family = ent->h_addrtype; - debug("resolved: %s (%s)\n", - host, inet_ntoa(addr->sin_addr)); - } else { - debug("failed to resolve locally.\n"); - return -1; /* failed */ - } - } - return 0; /* good */ -} - -int -open_connection( const char *host, u_short port ) -{ - SOCKET s; - struct sockaddr_in saddr; - - /* resolve address of proxy or direct target */ - if (local_resolve (host, &saddr) < 0) { - error("can't resolve hostname: %s\n", host); - return SOCKET_ERROR; - } - saddr.sin_port = htons(port); - - debug("connecting to %s:%u\n", inet_ntoa(saddr.sin_addr), port); - s = socket( AF_INET, SOCK_STREAM, 0 ); - if ( connect( s, (struct sockaddr *)&saddr, sizeof(saddr)) - == SOCKET_ERROR) { - debug( "connect() failed.\n"); - return SOCKET_ERROR; - } - return s; -} - -void -report_text( char *prefix, char *buf ) -{ - static char work[1024]; - char *tmp; - - if ( !f_debug ) - return; - if ( !f_report ) - return; /* don't report */ - debug("%s \"", prefix); - while ( *buf ) { - memset( work, 0, sizeof(work)); - tmp = work; - while ( *buf && ((tmp-work) < (int)sizeof(work)-5) ) { - switch ( *buf ) { - case '\t': *tmp++ = '\\'; *tmp++ = 't'; break; - case '\r': *tmp++ = '\\'; *tmp++ = 'r'; break; - case '\n': *tmp++ = '\\'; *tmp++ = 'n'; break; - case '\\': *tmp++ = '\\'; *tmp++ = '\\'; break; - default: - if ( isprint(*buf) ) { - *tmp++ = *buf; - } else { - int consumed = tmp - work; - snprintf( tmp, sizeof(work)-consumed, - "\\x%02X", (unsigned char)*buf); - tmp += strlen(tmp); - } - } - buf++; - *tmp = '\0'; - } - debug_("%s", work); - } - - debug_("\"\n"); -} - - -void -report_bytes( char *prefix, char *buf, int len ) -{ - if ( ! f_debug ) - return; - debug( "%s", prefix ); - while ( 0 < len ) { - fprintf( stderr, " %02x", *(unsigned char *)buf); - buf++; - len--; - } - fprintf(stderr, "\n"); - return; -} - -int -atomic_out( SOCKET s, char *buf, int size ) -{ - int ret, len; - - assert( buf != NULL ); - assert( 0<=size ); - /* do atomic out */ - ret = 0; - while ( 0 < size ) { - len = send( s, buf+ret, size, 0 ); - if ( len == -1 ) - fatal("atomic_out() failed to send(), %d\n", socket_errno()); - ret += len; - size -= len; - } - if (!f_report) { - debug("atomic_out() [some bytes]\n"); - debug(">>> xx xx xx xx ...\n"); - } else { - debug("atomic_out() [%d bytes]\n", ret); - report_bytes(">>>", buf, ret); - } - return ret; -} - -int -atomic_in( SOCKET s, char *buf, int size ) -{ - int ret, len; - - assert( buf != NULL ); - assert( 0<=size ); - - /* do atomic in */ - ret = 0; - while ( 0 < size ) { - len = recv( s, buf+ret, size, 0 ); - if ( len == -1 ) { - fatal("atomic_in() failed to recv(), %d\n", socket_errno()); - } else if ( len == 0 ) { - fatal( "Connection closed by peer.\n"); - } - ret += len; - size -= len; - } - if (!f_report) { - debug("atomic_in() [some bytes]\n"); - debug("<<< xx xx xx xx ...\n"); - } else { - debug("atomic_in() [%d bytes]\n", ret); - report_bytes("<<<", buf, ret); - } - return ret; -} - -int -line_input( SOCKET s, char *buf, int size ) -{ - char *dst = buf; - if ( size == 0 ) - return 0; /* no error */ - size--; - while ( 0 < size ) { - switch ( recv( s, dst, 1, 0) ) { /* recv one-by-one */ - case SOCKET_ERROR: - error("recv() error\n"); - return -1; /* error */ - case 0: - size = 0; /* end of stream */ - break; - default: - /* continue reading until last 1 char is EOL? */ - if ( *dst == '\n' ) { - /* finished */ - size = 0; - } else { - /* more... */ - size--; - } - dst++; - } - } - *dst = '\0'; - report_text( "<<<", buf); - return 0; -} - -/* cut_token() - Span token in given string STR until char in DELIM is appeared. - Then replace contiguous DELIMS with '\0' for string termination - and returns next pointer. - If no next token, return NULL. -*/ -char * -cut_token( char *str, char *delim) -{ - char *ptr = str + strcspn(str, delim); - char *end = ptr + strspn(ptr, delim); - if ( ptr == str ) - return NULL; - while ( ptr < end ) - *ptr++ = '\0'; - return ptr; -} - -const char * -lookup(int num, LOOKUP_ITEM *items) -{ - int i = 0; - while (0 <= items[i].num) { - if (items[i].num == num) - return items[i].str; - i++; - } - return "(unknown)"; -} - -/* readpass() - password input routine - Use ssh-askpass (same mechanism to OpenSSH) -*/ -char * -readpass( const char* prompt, ...) -{ - static char buf[1000]; /* XXX, don't be fix length */ - va_list args; - va_start(args, prompt); - vsnprintf(buf, sizeof(buf), prompt, args); - va_end(args); - - if ( getparam(ENV_SSH_ASKPASS) -#if !defined(_WIN32) && !defined(__CYGWIN32__) - && getenv("DISPLAY") -#endif /* not _WIN32 && not __CYGWIN32__ */ - ) { - /* use ssh-askpass to get password */ - FILE *fp; - char *askpass = getparam(ENV_SSH_ASKPASS), *cmd; - int cmd_size = strlen(askpass) +1 +1 +strlen(buf) +1 +1; - cmd = xmalloc(cmd_size); - snprintf(cmd, cmd_size, "%s \"%s\"", askpass, buf); - fp = popen(cmd, "r"); - free(cmd); - if ( fp == NULL ) - return NULL; /* fail */ - buf[0] = '\0'; - if (fgets(buf, sizeof(buf), fp) == NULL) - return NULL; /* fail */ - fclose(fp); - } else { - tty_readpass( buf, buf, sizeof(buf)); - } - buf[strcspn(buf, "\r\n")] = '\0'; - return buf; -} - -static int -socks5_do_auth_userpass( int s ) -{ - unsigned char buf[1024], *ptr; - char *pass = NULL; - int len; - - /* do User/Password authentication. */ - /* This feature requires username and password from - command line argument or environment variable, - or terminal. */ - if (relay_user == NULL) - fatal("cannot determine user name.\n"); - - /* get password from environment variable if exists. */ - if ((pass=determine_relay_password()) == NULL && - (pass=readpass("Enter SOCKS5 password for %s@%s: ", - relay_user, relay_host)) == NULL) - fatal("Cannot get password for user: %s\n", relay_user); - - /* make authentication packet */ - ptr = buf; - PUT_BYTE( ptr++, 1 ); /* subnegotiation ver.: 1 */ - len = strlen( relay_user ); /* ULEN and UNAME */ - PUT_BYTE( ptr++, len ); - strcpy( ptr, relay_user ); - ptr += len; - len = strlen( pass ); /* PLEN and PASSWD */ - PUT_BYTE( ptr++, strlen(pass)); - strcpy( ptr, pass ); - ptr += len; - memset (pass, 0, strlen(pass)); /* erase password */ - - /* send it and get answer */ - f_report = 0; - atomic_out( s, buf, ptr-buf ); - f_report = 1; - atomic_in( s, buf, 2 ); - - /* check status */ - if ( buf[1] == 0 ) - return 0; /* success */ - else - return -1; /* fail */ -} - -static const char * -socks5_getauthname( int auth ) -{ - switch ( auth ) { - case SOCKS5_AUTH_REJECT: return "REJECTED"; - case SOCKS5_AUTH_NOAUTH: return "NO-AUTH"; - case SOCKS5_AUTH_GSSAPI: return "GSSAPI"; - case SOCKS5_AUTH_USERPASS: return "USERPASS"; - case SOCKS5_AUTH_CHAP: return "CHAP"; - case SOCKS5_AUTH_EAP: return "EAP"; - case SOCKS5_AUTH_MAF: return "MAF"; - default: return "(unknown)"; - } -} - -typedef struct { - char* name; - unsigned char auth; -} AUTH_METHOD_ITEM; - -AUTH_METHOD_ITEM socks5_auth_table[] = { - { "none", SOCKS5_AUTH_NOAUTH }, - { "gssapi", SOCKS5_AUTH_GSSAPI }, - { "userpass", SOCKS5_AUTH_USERPASS }, - { "chap", SOCKS5_AUTH_CHAP }, - { NULL, -1 }, -}; - -int -socks5_auth_parse_1(char *start, char *end){ - int i, len; - for ( ; *start; start++ ) - if ( *start != ' ' && *start != '\t') break; - for ( end--; end >= start; end-- ) { - if ( *end != ' ' && *end != '\t'){ - end++; - break; - } - } - len = end - start; - for ( i = 0; socks5_auth_table[i].name != NULL; i++ ){ - if ( strncmp(start, socks5_auth_table[i].name, len) == 0) { - return socks5_auth_table[i].auth; - } - } - fatal("Unknown auth method: %s\n", start); - return -1; -} - -int -socks5_auth_parse(char *start, unsigned char *auth_list, int max_auth){ - char *end; - int i = 0; - while ( i < max_auth ) { - end = strchr(start, ','); - if (*start && end) { - auth_list[i++] = socks5_auth_parse_1(start, end); - start = ++end; - } else { - break; - } - } - if ( *start && ( i < max_auth ) ){ - for( end = start; *end; end++ ); - auth_list[i++] = socks5_auth_parse_1(start, end); - } else { - fatal("Too much auth method.\n"); - } - return i; -} - -/* begin SOCKS5 relaying - And no authentication is supported. - */ -int -begin_socks5_relay( SOCKET s ) -{ - unsigned char buf[256], *ptr, *env = socks5_auth; - unsigned char n_auth = 0; unsigned char auth_list[10], auth_method; - int len, auth_result, i; - - debug( "begin_socks_relay()\n"); - - /* request authentication */ - ptr = buf; - PUT_BYTE( ptr++, 5); /* SOCKS version (5) */ - - if ( env == NULL ) - env = getparam(ENV_SOCKS5_AUTH); - if ( env == NULL ) { - /* add no-auth authentication */ - auth_list[n_auth++] = SOCKS5_AUTH_NOAUTH; - /* add user/pass authentication */ - auth_list[n_auth++] = SOCKS5_AUTH_USERPASS; - } else { - n_auth = socks5_auth_parse(env, auth_list, 10); - } - PUT_BYTE( ptr++, n_auth); /* num auth */ - for (i=0; i>8); /* DST.PORT */ - PUT_BYTE( ptr++, dest_port&0xFF); - atomic_out( s, buf, ptr-buf); /* send request */ - atomic_in( s, buf, 4 ); /* recv response */ - if ( (buf[1] != SOCKS5_REP_SUCCEEDED) ) { /* check reply code */ - error("Got error response from SOCKS server: %d (%s).\n", - buf[1], lookup(buf[1], socks5_rep_names)); - return -1; - } - ptr = buf + 4; - switch ( buf[3] ) { /* case by ATYP */ - case 1: /* IP v4 ADDR*/ - atomic_in( s, ptr, 4+2 ); /* recv IPv4 addr and port */ - break; - case 3: /* DOMAINNAME */ - atomic_in( s, ptr, 1 ); /* recv name and port */ - atomic_in( s, ptr+1, *(unsigned char*)ptr + 2); - break; - case 4: /* IP v6 ADDR */ - atomic_in( s, ptr, 16+2 ); /* recv IPv6 addr and port */ - break; - } - - /* Conguraturation, connected via SOCKS5 server! */ - return 0; -} - -/* begin SOCKS protocol 4 relaying - And no authentication is supported. - - There's SOCKS protocol version 4 and 4a. Protocol version - 4a has capability to resolve hostname by SOCKS server, so - we don't need resolving IP address of destination host on - local machine. - - Environment variable SOCKS_RESOLVE directs how to resolve - IP addess. There's 3 keywords allowed; "local", "remote" - and "both" (case insensitive). Keyword "local" means taht - target host name is resolved by localhost resolver - (usualy with gethostbyname()), "remote" means by remote - SOCKS server, "both" means to try resolving by localhost - then remote. - - SOCKS4 protocol and authentication of SOCKS5 protocol - requires user name on connect request. - User name is determined by following method. - - 1. If server spec has user@hostname:port format then - user part is used for this SOCKS server. - - 2. Get user name from environment variable LOGNAME, USER - (in this order). - -*/ -int -begin_socks4_relay( SOCKET s ) -{ - unsigned char buf[256], *ptr; - - debug( "begin_socks_relay()\n"); - - /* make connect request packet - protocol v4: - VN:1, CD:1, PORT:2, ADDR:4, USER:n, NULL:1 - protocol v4a: - VN:1, CD:1, PORT:2, DUMMY:4, USER:n, NULL:1, HOSTNAME:n, NULL:1 - */ - ptr = buf; - PUT_BYTE( ptr++, 4); /* protocol version (4) */ - PUT_BYTE( ptr++, 1); /* CONNECT command */ - PUT_BYTE( ptr++, dest_port>>8); /* destination Port */ - PUT_BYTE( ptr++, dest_port&0xFF); - /* destination IP */ - memcpy(ptr, &dest_addr.sin_addr, sizeof(dest_addr.sin_addr)); - ptr += sizeof(dest_addr.sin_addr); - if ( dest_addr.sin_addr.s_addr == 0 ) - *(ptr-1) = 1; /* fake, protocol 4a */ - /* username */ - if (relay_user == NULL) - fatal( "Cannot determine user name.\n"); - strcpy( ptr, relay_user ); - ptr += strlen( relay_user ) +1; - /* destination host name (for protocol 4a) */ - if ( (socks_version == 4) && (dest_addr.sin_addr.s_addr == 0)) { - strcpy( ptr, dest_host ); - ptr += strlen( dest_host ) +1; - } - /* send command and get response - response is: VN:1, CD:1, PORT:2, ADDR:4 */ - atomic_out( s, buf, ptr-buf); /* send request */ - atomic_in( s, buf, 8 ); /* recv response */ - if ( (buf[1] != SOCKS4_REP_SUCCEEDED) ) { /* check reply code */ - error("Got error response: %d: '%s'.\n", - buf[1], lookup(buf[1], socks4_rep_names)); - return -1; /* failed */ - } - - /* Conguraturation, connected via SOCKS4 server! */ - return 0; -} - -int -sendf(SOCKET s, const char *fmt,...) -{ - static char buf[10240]; /* xxx, enough? */ - - va_list args; - va_start( args, fmt ); - vsnprintf( buf, sizeof(buf), fmt, args ); - va_end( args ); - - report_text(">>>", buf); - if ( send(s, buf, strlen(buf), 0) == SOCKET_ERROR ) { - debug("failed to send http request. errno=%d\n", socket_errno()); - return -1; - } - return 0; -} - -const char *base64_table = -"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - -char * -make_base64_string(const char *str) -{ - static char *buf; - unsigned char *src; - char *dst; - int bits, data, src_len, dst_len; - /* make base64 string */ - src_len = strlen(str); - dst_len = (src_len+2)/3*4; - buf = xmalloc(dst_len+1); - bits = data = 0; - src = (unsigned char *)str; - dst = (unsigned char *)buf; - while ( dst_len-- ) { - if ( bits < 6 ) { - data = (data << 8) | *src; - bits += 8; - if ( *src != 0 ) - src++; - } - *dst++ = base64_table[0x3F & (data >> (bits-6))]; - bits -= 6; - } - *dst = '\0'; - /* fix-up tail padding */ - switch ( src_len%3 ) { - case 1: - *--dst = '='; - case 2: - *--dst = '='; - } - return buf; -} - - -int -basic_auth (SOCKET s) -{ - char *userpass; - char *cred; - const char *user = relay_user; - char *pass = NULL; - int len, ret; - - /* Get username/password for authentication */ - if (user == NULL) - fatal("Cannot decide username for proxy authentication."); - if ((pass = determine_relay_password ()) == NULL && - (pass = readpass("Enter proxy authentication password for %s@%s: ", - relay_user, relay_host)) == NULL) - fatal("Cannot decide password for proxy authentication."); - - len = strlen(user)+strlen(pass)+1; - userpass = xmalloc(len+1); - snprintf(userpass, len+1, "%s:%s", user, pass); - memset (pass, 0, strlen(pass)); - cred = make_base64_string(userpass); - memset (userpass, 0, len); - - f_report = 0; /* don't report for security */ - ret = sendf(s, "Proxy-Authorization: Basic %s\r\n", cred); - f_report = 1; - report_text(">>>", "Proxy-Authorization: Basic xxxxx\r\n"); - - memset(cred, 0, strlen(cred)); - free(cred); - - return ret; -} - -/* begin relaying via HTTP proxy - Directs CONNECT method to proxy server to connect to - destination host (and port). It may not be allowed on your - proxy server. - */ -int -begin_http_relay( SOCKET s ) -{ - char buf[1024]; - int result; - char *auth_what; - - debug("begin_http_relay()\n"); - - if (sendf(s,"CONNECT %s:%d HTTP/1.0\r\n", dest_host, dest_port) < 0) - return START_ERROR; - if (proxy_auth_type == PROXY_AUTH_BASIC && basic_auth (s) < 0) - return START_ERROR; - if (sendf(s,"\r\n") < 0) - return START_ERROR; - - /* get response */ - if ( line_input(s, buf, sizeof(buf)) < 0 ) { - debug("failed to read http response.\n"); - return START_ERROR; - } - - /* check status */ - if (!strchr(buf, ' ')) { - error ("Unexpected http response: '%s'.\n", buf); - return START_ERROR; - } - result = atoi(strchr(buf,' ')); - - switch ( result ) { - case 200: - /* Conguraturation, connected via http proxy server! */ - debug("connected, start user session.\n"); - break; - case 302: /* redirect */ - do { - if (line_input(s, buf, sizeof(buf))) - break; - downcase(buf); - if (expect(buf, "Location: ")) { - relay_host = cut_token(buf, "//"); - cut_token(buf, "/"); - relay_port = atoi(cut_token(buf, ":")); - } - } while (strcmp(buf,"\r\n") != 0); - return START_RETRY; - - /* We handle both 401 and 407 codes here: 401 is WWW-Authenticate, which - * not strictly the correct response, but some proxies do send this (e.g. - * Symantec's Raptor firewall) */ - case 401: /* WWW-Auth required */ - case 407: /* Proxy-Auth required */ - /** NOTE: As easy implementation, we support only BASIC scheme - and ignore realm. */ - /* If proxy_auth_type is PROXY_AUTH_BASIC and get - this result code, authentication was failed. */ - if (proxy_auth_type != PROXY_AUTH_NONE) { - error("Authentication failed.\n"); - return START_ERROR; - } - auth_what = (result == 401) ? "WWW-Authenticate:" : "Proxy-Authenticate:"; - do { - if ( line_input(s, buf, sizeof(buf)) ) { - break; - } - downcase(buf); - if (expect(buf, auth_what)) { - /* parse type and realm */ - char *scheme, *realm; - scheme = cut_token(buf, " "); - realm = cut_token(scheme, " "); - if ( scheme == NULL || realm == NULL ) { - debug("Invalid format of %s field.", auth_what); - return START_ERROR; /* fail */ - } - /* check supported auth type */ - if (expect(scheme, "basic")) { - proxy_auth_type = PROXY_AUTH_BASIC; - } else { - debug("Unsupported authentication type: %s", scheme); - } - } - } while (strcmp(buf,"\r\n") != 0); - if ( proxy_auth_type == PROXY_AUTH_NONE ) { - debug("Can't find %s in response header.", auth_what); - return START_ERROR; - } else { - return START_RETRY; - } - - default: - /* Not allowed */ - debug("http proxy is not allowed.\n"); - return START_ERROR; - } - /* skip to end of response header */ - do { - if ( line_input(s, buf, sizeof(buf) ) ) { - debug("Can't skip response headers\n"); - return START_ERROR; - } - } while ( strcmp(buf,"\r\n") != 0 ); - - return START_OK; -} - -/* begin relaying via TELNET proxy. - Sends string specified by telnet_command (-c option) with - replacing host name and port number to the socket. */ -int -begin_telnet_relay( SOCKET s ) -{ - char buf[1024]; - char *cmd; - char *good_phrase = "connected to"; - char *bad_phrase_list[] = { - " failed", " refused", " rejected", " closed" - }; - char sep = ' '; - int i; - - debug("begin_telnet_relay()\n"); - - /* report phrase */ - debug("good phrase: '%s'\n", good_phrase); - debug("bad phrases"); - sep = ':'; - for (i=0; i< (sizeof(bad_phrase_list) / sizeof(char*)); i++) { - debug_("%c '%s'", sep, bad_phrase_list[i]); - sep = ','; - } - debug_("\n"); - - /* make request string with replacing %h by destination hostname - and %p by port number, etc. */ - cmd = expand_host_and_port(telnet_command, dest_host, dest_port); - - /* Sorry, we send request string now without waiting a prompt. */ - if (sendf(s, "%s\r\n", cmd) < 0) { - free(cmd); - return START_ERROR; - } - free(cmd); - - /* Process answer from proxy until good or bad phrase is detected. We - assume that the good phrase should be appeared only in the final - line of proxy responses. Bad keywods in the line causes operation - fail. First checks a good phrase, then checks bad phrases. - If no match, continue reading line from proxy. */ - while (!line_input(s, buf, sizeof(buf)) && buf[0] != '\0') { - downcase(buf); - /* first, check good phrase */ - if (strstr(buf, good_phrase)) { - debug("good phrase is detected: '%s'\n", good_phrase); - return START_OK; - } - /* then, check bad phrase */ - for (i=0; i<(sizeof(bad_phrase_list)/sizeof(char*)); i++) { - if (strstr(buf, bad_phrase_list[i]) != NULL) { - debug("bad phrase is detected: '%s'\n", bad_phrase_list[i]); - return START_ERROR; - } - } - } - debug("error reading from telnet proxy\n"); - - return START_ERROR; -} - - -#ifdef _WIN32 -/* ddatalen() - Returns 1 if data is available, otherwise return 0 - */ -int -stdindatalen (void) -{ - DWORD len = 0; - struct stat st; - fstat( 0, &st ); - if ( st.st_mode & _S_IFIFO ) { - /* in case of PIPE */ - if ( !PeekNamedPipe( GetStdHandle(STD_INPUT_HANDLE), - NULL, 0, NULL, &len, NULL) ) { - if ( GetLastError() == ERROR_BROKEN_PIPE ) { - /* PIPE source is closed */ - /* read() will detects EOF */ - len = 1; - } else { - fatal("PeekNamedPipe() failed, errno=%d\n", - GetLastError()); - } - } - } else if ( st.st_mode & _S_IFREG ) { - /* in case of regular file (redirected) */ - len = 1; /* always data ready */ - } else if ( _kbhit() ) { - /* in case of console */ - len = 1; - } - return len; -} -#endif /* _WIN32 */ - -/* relay byte from stdin to socket and fro socket to stdout. - returns reason of termination */ -int -do_repeater( SOCKET local_in, SOCKET local_out, SOCKET remote ) -{ - /** vars for local input data **/ - char lbuf[1024]; /* local input buffer */ - int lbuf_len; /* available data in lbuf */ - int f_local; /* read local input more? */ - /** vars for remote input data **/ - char rbuf[1024]; /* remote input buffer */ - int rbuf_len; /* available data in rbuf */ - int f_remote; /* read remote input more? */ - int close_reason = REASON_UNK; /* reason of end repeating */ - /** other variables **/ - int nfds, len; - fd_set ifds, ofds; - struct timeval *tmo; -#ifdef _WIN32 - struct timeval win32_tmo; -#endif /* _WIN32 */ - - /* repeater between stdin/out and socket */ - nfds = ((local_in local */ - if ( FD_ISSET(remote, &ifds) && (rbuf_len < (int)sizeof(rbuf)) ) { - len = recv( remote, rbuf + rbuf_len, sizeof(rbuf)-rbuf_len, 0); - if ( len == 0 || (len == -1 && socket_errno() == ECONNRESET)) { - debug("connection %s by peer\n", - (len==0)? "closed": "reset"); - close_reason = REASON_CLOSED_BY_REMOTE; - f_remote = 0; /* no more read from socket */ - f_local = 0; - } else if ( len == -1 ) { - /* error */ - fatal("recv() failed, %d\n", socket_errno()); - } else { - debug("recv %d bytes\n", len); - if ( 1 < f_debug ) /* more verbose */ - report_bytes( "<<<", rbuf+rbuf_len, len); - rbuf_len += len; - } - } - - /* local => remote */ - if ( FD_ISSET(local_in, &ifds) && (lbuf_len < (int)sizeof(lbuf)) ) { - if (local_type == LOCAL_SOCKET) - len = recv(local_in, lbuf + lbuf_len, - sizeof(lbuf)-lbuf_len, 0); - else - len = read(local_in, lbuf + lbuf_len, sizeof(lbuf)-lbuf_len); - if ( len == 0 ) { - /* stdin is EOF */ - debug("local input is EOF\n"); - if (!f_hold_session) - shutdown(remote, 1); /* no-more writing */ - f_local = 0; - close_reason = REASON_CLOSED_BY_LOCAL; - } else if ( len == -1 ) { - /* error on reading from stdin */ - if (f_hold_session) { - debug ("failed to read from local\n"); - f_local = 0; - close_reason = REASON_CLOSED_BY_LOCAL; - } else - fatal("recv() failed, errno = %d\n", errno); - } else { - /* repeat */ - lbuf_len += len; - } - } - - /* flush data in buffer to socket */ - if ( 0 < lbuf_len ) { - len = send(remote, lbuf, lbuf_len, 0); - if ( len == -1 ) { - fatal("send() failed, %d\n", socket_errno()); - } else if ( 0 < len ) { - if ( 1 < f_debug ) /* more verbose */ - report_bytes( ">>>", lbuf, len); - /* move data on to top of buffer */ - debug("sent %d bytes\n", len); - lbuf_len -= len; - if ( 0 < lbuf_len ) - memcpy( lbuf, lbuf+len, lbuf_len ); - assert( 0 <= lbuf_len ); - } - } - - /* flush data in buffer to local output */ - if ( 0 < rbuf_len ) { - if (local_type == LOCAL_SOCKET) - len = send( local_out, rbuf, rbuf_len, 0); - else - len = write( local_out, rbuf, rbuf_len); - if ( len == -1 ) { - fatal("output (local) failed, errno=%d\n", errno); - } - rbuf_len -= len; - if ( len < rbuf_len ) - memcpy( rbuf, rbuf+len, rbuf_len ); - assert( 0 <= rbuf_len ); - } - if (f_local == 0 && f_hold_session) { - debug ("closing local port without disconnecting from remote\n"); - f_remote = 0; - shutdown (local_out, 2); - close (local_out); - break; - } - } - - return close_reason; -} - -int -accept_connection (u_short port) -{ - static int sock = -1; - int connection; - struct sockaddr_in name; - struct sockaddr client; - int socklen; - fd_set ifds; - int nfds; - int sockopt; - - /* Create the socket. */ - debug("Creating source port to forward.\n"); - sock = socket (PF_INET, SOCK_STREAM, 0); - if (sock < 0) - fatal("socket() failed, errno=%d\n", socket_errno()); - sockopt = 1; - setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, - (void*)&sockopt, sizeof(sockopt)); - - /* Give the socket a name. */ - name.sin_family = AF_INET; - name.sin_port = htons (port); - name.sin_addr.s_addr = htonl (INADDR_ANY); - if (bind (sock, (struct sockaddr *) &name, sizeof (name)) < 0) - fatal ("bind() failed, errno=%d\n", socket_errno()); - - if (listen( sock, 1) < 0) - fatal ("listen() failed, errno=%d\n", socket_errno()); - - /* wait for new connection with watching EOF of stdin. */ - debug ("waiting new connection at port %d (socket=%d)\n", port, sock); - nfds = sock + 1; - do { - int n; - struct timeval *ptmo = NULL; -#ifdef _WIN32 - struct timeval tmo; - tmo.tv_sec = 0; - tmo.tv_usec = 100*1000; /* On Windows, 100ms timeout */ - ptmo = &tmo; -#endif /* _WIN32 */ - FD_ZERO (&ifds); - FD_SET ((SOCKET)sock, &ifds); -#ifndef _WIN32 - FD_SET (0, &ifds); /* watch stdin */ -#endif - n = select (nfds, &ifds, NULL, NULL, ptmo); - if (n == -1) { - fatal ("select() failed, %d\n", socket_errno()); - exit (1); - } -#ifdef _WIN32 - if (0 < stdindatalen()) { - FD_SET (0, &ifds); /* fake */ - n++; - } -#endif - if (0 < n) { - if (FD_ISSET(0, &ifds) && (getchar() <= 0)) { - /* EOF */ - debug ("Give-up waiting port because stdin is closed."); - exit(0); - } - if (FD_ISSET(sock, &ifds)) - break; /* socket is stimulated */ - } - } while (1); - socklen = sizeof(client); - connection = accept( sock, &client, &socklen); - if ( connection < 0 ) - fatal ("accept() failed, errno=%d\n", socket_errno()); - return connection; -} - - - -/** Main of program **/ -int -main( int argc, char **argv ) -{ - int ret; - int remote; /* socket */ - int local_in; /* Local input */ - int local_out; /* Local output */ - int reason; -#ifdef _WIN32 - WSADATA wsadata; - WSAStartup( 0x101, &wsadata); -#endif /* _WIN32 */ - - /* initialization */ - make_revstr(); - getarg( argc, argv ); - debug("Program is $Revision$\n"); - - /* Open local_in and local_out if forwarding a port */ - if ( local_type == LOCAL_SOCKET ) { - /* Relay between local port and destination */ - local_in = local_out = accept_connection( local_port ); - } else { - /* Relay between stdin/stdout and desteination */ - local_in = 0; - local_out = 1; -#ifdef _WIN32 - _setmode(local_in, O_BINARY); - _setmode(local_out, O_BINARY); -#endif - } - -retry: -#ifndef _WIN32 - if (0 < connect_timeout) - set_timeout (connect_timeout); -#endif /* not _WIN32 */ - - if (check_direct(dest_host)) - relay_method = METHOD_DIRECT; - /* make connection */ - if ( relay_method == METHOD_DIRECT ) { - remote = open_connection (dest_host, dest_port); - if ( remote == SOCKET_ERROR ) - fatal( "Unable to connect to destination host, errno=%d\n", - socket_errno()); - } else { - remote = open_connection (relay_host, relay_port); - if ( remote == SOCKET_ERROR ) - fatal( "Unable to connect to relay host, errno=%d\n", - socket_errno()); - } - - /** resolve destination host (SOCKS) **/ -#if !defined(_WIN32) && !defined(__CYGWIN32__) - if (socks_ns.sin_addr.s_addr != 0) - switch_ns (&socks_ns); -#endif /* not _WIN32 && not __CYGWIN32__ */ - if (relay_method == METHOD_SOCKS && - socks_resolve == RESOLVE_LOCAL && - local_resolve (dest_host, &dest_addr) < 0) { - fatal("Unknown host: %s", dest_host); - } - - /** relay negociation **/ - switch ( relay_method ) { - case METHOD_SOCKS: - if ( ((socks_version == 5) && (begin_socks5_relay(remote) < 0)) || - ((socks_version == 4) && (begin_socks4_relay(remote) < 0)) ) - fatal( "failed to begin relaying via SOCKS.\n"); - break; - - case METHOD_HTTP: - ret = begin_http_relay(remote); - switch (ret) { - case START_ERROR: - close (remote); - fatal("failed to begin relaying via HTTP.\n"); - case START_OK: - break; - case START_RETRY: - /* retry with authentication */ - close (remote); - goto retry; - } - break; - case METHOD_TELNET: - if (begin_telnet_relay(remote) < 0) - fatal("failed to begin relaying via telnet.\n"); - break; - } - debug("connected\n"); - -#ifndef _WIN32 - if (0 < connect_timeout) - set_timeout (0); -#endif /* not _WIN32 */ - - /* main loop */ - debug ("start relaying.\n"); -do_repeater: - reason = do_repeater(local_in, local_out, remote); - debug ("relaying done.\n"); - if (local_type == LOCAL_SOCKET && - reason == REASON_CLOSED_BY_LOCAL && - f_hold_session) { - /* re-wait at local port without closing remote session */ - debug ("re-waiting at local port %d\n", local_port); - local_in = local_out = accept_connection( local_port ); - debug ("re-start relaying\n"); - goto do_repeater; - } - closesocket(remote); - if ( local_type == LOCAL_SOCKET) - closesocket(local_in); -#ifdef _WIN32 - WSACleanup(); -#endif /* _WIN32 */ - debug ("that's all, bye.\n"); - - return 0; -} - -/* ------------------------------------------------------------ - Local Variables: - compile-command: "cc connect.c -o connect" - tab-width: 8 - fill-column: 74 - comment-column: 48 - End: - ------------------------------------------------------------ */ - -/*** end of connect.c ***/ diff --git a/connect-proxy-1.105-socklen.patch b/connect-proxy-1.105-socklen.patch new file mode 100644 index 0000000..d2c1d87 --- /dev/null +++ b/connect-proxy-1.105-socklen.patch @@ -0,0 +1,11 @@ +--- a/connect.c 2024-05-17 09:06:35.176046528 +0200 ++++ b/connect.c 2024-05-17 09:02:00.234749758 +0200 +@@ -2822,7 +2822,7 @@ + int connection; + struct sockaddr_in name; + struct sockaddr client; +- SOCKLEN_T socklen; ++ socklen_t socklen; + fd_set ifds; + int nfds; + int sockopt; diff --git a/connect-proxy.spec b/connect-proxy.spec index e0924ac..cfc1ad8 100644 --- a/connect-proxy.spec +++ b/connect-proxy.spec @@ -1,20 +1,21 @@ Name: connect-proxy -Version: 1.100 -Release: 31%{?dist} +Version: 1.105 +Release: 1%{?dist} Summary: SSH Proxy command helper License: GPLv2+ URL: http://www.taiyo.co.jp/~gotoh/ssh/connect.html -Source0: connect-%{version}.c +Source0: ssh-connect-%{version}.tar.gz # Real source listed below, it was renamed for sanity's sake -#Source0: http://www.taiyo.co.jp/~gotoh/ssh/connect.c -Source1: http://www.taiyo.co.jp/~gotoh/ssh/connect.html -Patch0: connect-proxy-make-man.patch +#Source0: https://github.com/gotoh/ssh-connect/archive/refs/tags/1.105-tar.gz +Source1: connect-proxy.1 +Patch0: connect-proxy-1.105-socklen.patch Requires: openssh BuildRequires: gcc -BuildRequires: make +BuildRequires: make +BuildRequires: add-determinism %description connect-proxy is the simple relaying command to make network connection via SOCKS and https proxy. It is mainly intended to be used as proxy command @@ -30,28 +31,28 @@ Features of connect-proxy are: * You can also relay local socket stream instead of standard I/O. %prep -#setup -q -T -c -n %{name}-%{version} -%setup -q -T -c -cp %{SOURCE0} connect.c -cp %{SOURCE1} . -%patch0 -p1 +%setup -q -n ssh-connect-%{version} +%patch -P 0 -p1 %build make CFLAGS="$RPM_OPT_FLAGS" %{?_smp_mflags} %install rm -rf $RPM_BUILD_ROOT -mkdir -p $RPM_BUILD_ROOT -make DESTDIR=$RPM_BUILD_ROOT install +mkdir -p $RPM_BUILD_ROOT/%{_bindir} +cp connect $RPM_BUILD_ROOT/%{_bindir}/connect-proxy mkdir -p $RPM_BUILD_ROOT%{_mandir}/man1 -cp -p %{name}.1 $RPM_BUILD_ROOT%{_mandir}/man1/ +cp -p %{SOURCE1} $RPM_BUILD_ROOT%{_mandir}/man1/ %files -%doc connect.html +%doc doc/manual.html %{_mandir}/man1/* %{_bindir}/%{name} %changelog +* Fri May 17 2024 Timotheus Pokorra - 1.105-1 +- Update to upstream 1.105 + * Wed Jan 24 2024 Fedora Release Engineering - 1.100-31 - Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild diff --git a/connect.c b/connect.c deleted file mode 100644 index 9bb66c1..0000000 --- a/connect.c +++ /dev/null @@ -1,3727 +0,0 @@ -/*********************************************************************** - * connect.c -- Make socket connection using SOCKS4/5 and HTTP tunnel. - * - * Copyright (c) 2000-2006 Shun-ichi Goto - * Copyright (c) 2002, J. Grant (English Corrections) - * Copyright (c) 2020, J.J. Keijser (added SSL support, made -Wpendantic clean) - * - * 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 - * of the License, 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 this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - * --------------------------------------------------------- - * PROJECT: My Test Program - * AUTHOR: Shun-ichi GOTO - * CREATE: Wed Jun 21, 2000 - * REVISION: $Revision: 100 $ - * --------------------------------------------------------- - * - * Getting Source - * ============== - * - * Recent version of 'connect.c' is available from - * http://www.taiyo.co.jp/~gotoh/ssh/connect.c - * - * Related tool, ssh-askpass.exe (alternative ssh-askpass on UNIX) - * is available: - * http://www.taiyo.co.jp/~gotoh/ssh/ssh-askpass.exe.gz - * - * See more detail: - * http://www.taiyo.co.jp/~gotoh/ssh/connect.html - * - * How To Compile - * ============== - * - * On UNIX environment: - * $ gcc connect.c -o connect - * - * On SOLARIS: - * $ gcc -o connect -lresolv -lsocket -lnsl connect.c - * - * on Win32 environment: - * $ cl connect.c wsock32.lib advapi32.lib - * or - * $ bcc32 connect.c wsock32.lib advapi32.lib - * or - * $ gcc connect.c -o connect - * - * on Mac OS X environment: - * $ gcc connect.c -o connect -lresolv - * or - * $ gcc connect.c -o connect -DBIND_8_COMPAT=1 - * - * How To Use - * ========== - * - * You can specify proxy method in an environment variable or in a - * command line option. - * - * usage: connect [-dnhst45] [-R resolve] [-p local-port] [-w sec] - * [-H [user@]proxy-server[:port]] - * [-S [user@]socks-server[:port]] - * [-T proxy-server[:port]] - * [-c telnet proxy command] - * host port - * - * "host" and "port" is for the target hostname and port-number to - * connect to. - * - * The -H option specifys a hostname and port number of the http proxy - * server to relay. If port is omitted, 80 is used. You can specify this - * value in the environment variable HTTP_PROXY and pass the -h option - * to use it. - * - * The -S option specifys the hostname and port number of the SOCKS - * server to relay. Like -H, port number can be omitted and the default - * is 1080. You can also specify this value pair in the environment - * variable SOCKS5_SERVER and give the -s option to use it. - * - * The '-4' and the '-5' options are for specifying SOCKS relaying and - * indicates protocol version to use. It is valid only when used with - * '-s' or '-S'. Default is '-5' (protocol version 5) - * - * The '-R' option is for specifying method to resolve the - * hostname. Three keywords ("local", "remote", "both") or dot-notation - * IP address are acceptable. The keyword "both" means, "Try local - * first, then remote". If a dot-notation IP address is specified, use - * this host as nameserver. The default is "remote" for SOCKS5 or - * "local" for others. On SOCKS4 protocol, remote resolving method - * ("remote" and "both") requires protocol 4a supported server. - * - * The '-p' option will forward a local TCP port instead of using the - * standard input and output. - * - * The '-P' option is same to '-p' except keep remote session. The - * program repeats waiting the port with holding remote session without - * disconnecting. To disconnect the remote session, send EOF to stdin or - * kill the program. - * - * The '-w' option specifys timeout seconds for making connection with - * TARGET host. - * - * The '-d' option is used for debug. If you fail to connect, use this - * and check request to and response from server. - * - * You can omit the "port" argument when program name is special format - * containing port number itself. For example, - * $ ln -s connect connect-25 - * means this connect-25 command is spcifying port number 25 already - * so you need not 2nd argument (and ignored if specified). - * - * To use proxy, this example is for SOCKS5 connection to connect to - * 'host' at port 25 via SOCKS5 server on 'firewall' host. - * $ connect -S firewall host 25 - * or - * $ SOCKS5_SERVER=firewall; export SOCKS5_SERVER - * $ connect -s host 25 - * - * For a HTTP-PROXY connection: - * $ connect -H proxy-server:8080 host 25 - * or - * $ HTTP_PROXY=proxy-server:8080; export HTTP_PROXY - * $ connect -h host 25 - * To forward a local port, for example to use ssh: - * $ connect -p 5550 -H proxy-server:8080 host 22 - * ($ ssh -l user -p 5550 localhost ) - * - * TIPS - * ==== - * - * Connect.c doesn't have any configuration to specify the SOCKS server. - * If you are a mobile user, this limitation might bother you. However, - * You can compile connect.c and link with other standard SOCKS library - * like the NEC SOCKS5 library or Dante. This means connect.c is - * socksified and uses a configration file like to other SOCKSified - * network commands and you can switch configuration file any time - * (ex. when ppp startup) that brings you switching of SOCKS server for - * connect.c in same way with other commands. For this case, you can - * write ~/.ssh/config like this: - * - * ProxyCommand connect -n %h %p - * - * SOCKS5 authentication - * ===================== - * - * Only USER/PASS authentication is supported. - * - * Proxy authentication - * ==================== - * - * Only BASIC scheme is supported. - * - * Authentication informations - * =========================== - * - * User name for authentication is specifed by an environment variable - * or system login name. And password is specified from environment - * variable or external program (specified in $SSH_ASKPASS) or tty. - * - * Following environment variable is used for specifying user name. - * SOCKS: $SOCKS5_USER, $LOGNAME, $USER - * HTTP Proxy: $HTTP_PROXY_USER, $LOGNAME, $USER - * - * ssh-askpass support - * =================== - * - * You can use ssh-askpass (came from OpenSSH or else) to specify - * password on graphical environment (X-Window or MS Windows). To use - * this, set program name to environment variable SSH_ASKPASS. On UNIX, - * X-Window must be required, so $DISPLAY environment variable is also - * needed. On Win32 environment, $DISPLAY is not mentioned. - * - * Related Informations - * ==================== - * - * SOCKS5 -- RFC 1928, RFC 1929, RFC 1961 - * NEC SOCKS Reference Implementation is available from: - * http://www.socks.nec.com - * DeleGate version 5 or earlier can be SOCKS4 server, - * and version 6 can be SOCKS5 and SOCKS4 server. - * and version 7.7.0 or later can be SOCKS5 and SOCKS4a server. - * http://www.delegate.org/delegate/ - * - * HTTP-Proxy -- - * Many http proxy servers supports this, but https should - * be allowed as configuration on your host. - * For example on DeleGate, you should add "https" to the - * "REMITTABLE" parameter to allow HTTP-Proxy like this: - * delegated -Pxxxx ...... REMITTABLE="+,https" ... - * - * Hypertext Transfer Protocol -- HTTP/1.1 -- RFC 2616 - * HTTP Authentication: Basic and Digest Access Authentication -- RFC 2617 - * For proxy authentication, refer these documents. - * - ***********************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef __CYGWIN32__ -#undef _WIN32 -#endif - -#ifdef _WIN32 -#include -#include -#include -#include -#include -#else /* !_WIN32 */ -#include -#include -#include -#include -#ifndef __hpux -#include -#endif /* __hpux */ -#include -#include -#include -#include -#if !defined(_WIN32) && !defined(__CYGWIN32__) -#define WITH_RESOLVER 1 -#include -#include -#else /* not ( not _WIN32 && not __CYGWIN32__) */ -#undef WITH_RESOLVER -#endif /* not ( not _WIN32 && not __CYGWIN32__) */ -#endif /* !_WIN32 */ - -#ifdef _WIN32 -#define ECONNRESET WSAECONNRESET -#endif /* _WI32 */ - -#include -#include -#include -#include -#include -#include -#include - -/* Microsoft Visual C/C++ has _snprintf() and _vsnprintf() */ -#ifdef _MSC_VER -#define snprintf _snprintf -#define vsnprintf _vsnprintf -#endif - -/* consider Borland C */ -#ifdef __BORLANDC__ -#define _kbhit kbhit -#define _setmode setmode -#endif - -/* help message. - Win32 environment does not support -R option (vc and cygwin) - Win32 native compilers does not support -w option, yet (vc) -*/ -static char *usage1 = "usage: %s [-dnhstx45] [-p local-port]" -#ifdef _WIN32 -#ifdef __CYGWIN32__ -"[-w timeout] \n" /* cygwin cannot -R */ -#else /* not __CYGWIN32__ */ -" \n" /* VC cannot -w nor -R */ -#endif /* not __CYGWIN32__ */ -#else /* not _WIN32 */ -/* help message for UNIX */ -"[-R resolve] [-w timeout] \n" -#endif /* not _WIN32 */ -" [-S [user@]socks-server[:port]]\n" -" [-H [user@]proxy-server[:port]]\n" -" [-T proxy-server[:port] [-c telnet-proxy-command]\n" -" [-X [user@]proxy-server[:port]]\n"; - -static char *usage2 = -" [--help]\n" -" [--socks-server [user@]socks-server[:port]]\n" -" [--http-proxy [user@]proxy-server[:port]]\n" -" [--telnet-proxy proxy-server[:port]\n" -" [--https-proxy [user@]proxy-server[:port]]\n" -" [--https-proxy-ca PEM format file of CA's]\n" -" [--https-proxy-ca-path PEM format directory of CA's]\n" -" [--https-proxy-certname name]\n" -" [--https-user-cert certfile.pem]\n" -" [--https-user-key keyfile.pem]\n" -" [--no-check-certificate]\n" -" host port\n"; - -/* name of this program */ -char *progname = NULL; -static char *progdesc = "connect --- simple relaying command via proxy."; -static char *revstr = "2.01"; - -/* set of character for strspn() */ -const char *digits = "0123456789"; -const char *dotdigits = "0123456789."; - -/* options */ -int f_debug = 0; - -/* report flag to hide secure information */ -int f_report = 1; - -int connect_timeout = 0; - -/* local input type */ -#define LOCAL_STDIO 0 -#define LOCAL_SOCKET 1 -char *local_type_names[] = { "stdio", "socket" }; -int local_type = LOCAL_STDIO; -u_short local_port = 0; /* option 'p' */ -int f_hold_session = 0; /* option 'P' */ - -char *telnet_command = "telnet %h %p"; - - - -/* utiity types, pair holder of number and string */ -typedef struct { - int num; - const char *str; -} LOOKUP_ITEM; - -/* relay method, server and port */ -#define METHOD_UNDECIDED 0 -#define METHOD_DIRECT 1 -#define METHOD_SOCKS 2 -#define METHOD_HTTP 3 -#define METHOD_HTTPS 4 -#define METHOD_TELNET 5 -char *method_names[] = { "UNDECIDED", "DIRECT", "SOCKS", "HTTP", "HTTPS", "TELNET" }; - -int relay_method = METHOD_UNDECIDED; /* relaying method */ -char *relay_host = NULL; /* hostname of relay server */ -u_short relay_port = 0; /* port of relay server */ -char *relay_user = NULL; /* user name for auth */ - -/* HTTPS specific stuff*/ -#if OPENSSL_VERSION_NUMBER < 0x10100000L -#define TLS_method SSLv23_method -#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */ -SSL *ssl = NULL; -char sslbuf[65536]; -size_t sslbuf_idx = 0; - -#ifdef __DEFAULT_CA_PATH__ -char *https_ca_file = __DEFAULT_CA_PATH__; /* system wide default */ -#else -char *https_ca_file = "/etc/pki/tls/certs/ca-bundle.crt"; /* system wide default */ -#endif -char *https_ca_path = NULL; -char *https_usercert_file = NULL; -char *https_userkey_file = NULL; -int https_check_certificate = 1; /* do a proxy server cert check by default */ -char *https_proxy_certname = NULL; - -/* destination target host and port */ -char *dest_host = NULL; -struct sockaddr_in dest_addr; -u_short dest_port = 0; - -/* informations for SOCKS */ -#define SOCKS5_REP_SUCCEEDED 0x00 /* succeeded */ -#define SOCKS5_REP_FAIL 0x01 /* general SOCKS serer failure */ -#define SOCKS5_REP_NALLOWED 0x02 /* connection not allowed by ruleset */ -#define SOCKS5_REP_NUNREACH 0x03 /* Network unreachable */ -#define SOCKS5_REP_HUNREACH 0x04 /* Host unreachable */ -#define SOCKS5_REP_REFUSED 0x05 /* connection refused */ -#define SOCKS5_REP_EXPIRED 0x06 /* TTL expired */ -#define SOCKS5_REP_CNOTSUP 0x07 /* Command not supported */ -#define SOCKS5_REP_ANOTSUP 0x08 /* Address not supported */ -#define SOCKS5_REP_INVADDR 0x09 /* Inalid address */ - -LOOKUP_ITEM socks5_rep_names[] = { - { SOCKS5_REP_SUCCEEDED, "succeeded"}, - { SOCKS5_REP_FAIL, "general SOCKS server failure"}, - { SOCKS5_REP_NALLOWED, "connection not allowed by ruleset"}, - { SOCKS5_REP_NUNREACH, "Network unreachable"}, - { SOCKS5_REP_HUNREACH, "Host unreachable"}, - { SOCKS5_REP_REFUSED, "connection refused"}, - { SOCKS5_REP_EXPIRED, "TTL expired"}, - { SOCKS5_REP_CNOTSUP, "Command not supported"}, - { SOCKS5_REP_ANOTSUP, "Address not supported"}, - { SOCKS5_REP_INVADDR, "Invalid address"}, - { -1, NULL } -}; - -/* SOCKS5 authentication methods */ -#define SOCKS5_AUTH_REJECT 0xFF /* No acceptable auth method */ -#define SOCKS5_AUTH_NOAUTH 0x00 /* without authentication */ -#define SOCKS5_AUTH_GSSAPI 0x01 /* GSSAPI */ -#define SOCKS5_AUTH_USERPASS 0x02 /* User/Password */ -#define SOCKS5_AUTH_CHAP 0x03 /* Challenge-Handshake Auth Proto. */ -#define SOCKS5_AUTH_EAP 0x05 /* Extensible Authentication Proto. */ -#define SOCKS5_AUTH_MAF 0x08 /* Multi-Authentication Framework */ - -#define SOCKS4_REP_SUCCEEDED 90 /* rquest granted (succeeded) */ -#define SOCKS4_REP_REJECTED 91 /* request rejected or failed */ -#define SOCKS4_REP_IDENT_FAIL 92 /* cannot connect identd */ -#define SOCKS4_REP_USERID 93 /* user id not matched */ - -LOOKUP_ITEM socks4_rep_names[] = { - { SOCKS4_REP_SUCCEEDED, "request granted (succeeded)"}, - { SOCKS4_REP_REJECTED, "request rejected or failed"}, - { SOCKS4_REP_IDENT_FAIL, "cannot connect identd"}, - { SOCKS4_REP_USERID, "user id not matched"}, - { -1, NULL } -}; - -#define RESOLVE_UNKNOWN 0 -#define RESOLVE_LOCAL 1 -#define RESOLVE_REMOTE 2 -#define RESOLVE_BOTH 3 -char *resolve_names[] = { "UNKNOWN", "LOCAL", "REMOTE", "BOTH" }; - -int socks_version = 5; /* SOCKS protocol version */ -int socks_resolve = RESOLVE_UNKNOWN; -struct sockaddr_in socks_ns; -char *socks5_auth = NULL; - -/* Environment variable names */ -#define ENV_SOCKS_SERVER "SOCKS_SERVER" /* SOCKS server */ -#define ENV_SOCKS5_SERVER "SOCKS5_SERVER" -#define ENV_SOCKS4_SERVER "SOCKS4_SERVER" - -#define ENV_SOCKS_RESOLVE "SOCKS_RESOLVE" /* resolve method */ -#define ENV_SOCKS5_RESOLVE "SOCKS5_RESOLVE" -#define ENV_SOCKS4_RESOLVE "SOCKS4_RESOLVE" - -#define ENV_SOCKS5_USER "SOCKS5_USER" /* auth user for SOCKS5 */ -#define ENV_SOCKS4_USER "SOCKS4_USER" /* auth user for SOCKS4 */ -#define ENV_SOCKS_USER "SOCKS_USER" /* auth user for SOCKS */ -#define ENV_SOCKS5_PASSWD "SOCKS5_PASSWD" /* auth password for SOCKS5 */ -#define ENV_SOCKS5_PASSWORD "SOCKS5_PASSWORD" /* old style */ - -#define ENV_HTTP_PROXY "HTTP_PROXY" /* common env var */ -#define ENV_HTTP_PROXY_USER "HTTP_PROXY_USER" /* auth user */ -#define ENV_HTTP_PROXY_PASSWORD "HTTP_PROXY_PASSWORD" /* auth password */ - -#define ENV_HTTPS_PROXY "HTTPS_PROXY" /* common env var */ -#define ENV_HTTPS_PROXY_USER "HTTPS_PROXY_USER" /* auth user */ -#define ENV_HTTPS_PROXY_PASSWORD "HTTPS_PROXY_PASSWORD" /* auth password */ -#define ENV_HTTPS_PROXY_CERTNAME "HTTPS_PROXY_CERTNAME" /* Certificate /CN name of the proxy server */ -#define ENV_HTTPS_PROXY_CA_FILE "HTTPS_PROXY_CA_FILE" /* CA certificate file of proxy server */ -#define ENV_HTTPS_PROXY_CA_PATH "HTTPS_PROXY_CA_PATH" /* CA certificate path to verify proxy server */ -#define ENV_HTTPS_PROXY_USERCERT "HTTPS_PROXY_USERCERT" /* User certificiate for authentication */ -#define ENV_HTTPS_PROXY_USERKEY "HTTPS_PROXY_USERKEY" /* User certificiate for authentication */ - -#define ENV_TELNET_PROXY "TELNET_PROXY" /* common env var */ - -#define ENV_CONNECT_USER "CONNECT_USER" /* default auth user name */ -#define ENV_CONNECT_PASSWORD "CONNECT_PASSWORD" /* default auth password */ - -#define ENV_SOCKS_DIRECT "SOCKS_DIRECT" /* addr-list for non-proxy */ -#define ENV_SOCKS5_DIRECT "SOCKS5_DIRECT" -#define ENV_SOCKS4_DIRECT "SOCKS4_DIRECT" -#define ENV_HTTP_DIRECT "HTTP_DIRECT" -#define ENV_HTTPS_DIRECT "HTTPS_DIRECT" -#define ENV_CONNECT_DIRECT "CONNECT_DIRECT" - -#define ENV_SOCKS5_AUTH "SOCKS5_AUTH" -#define ENV_SSH_ASKPASS "SSH_ASKPASS" /* askpass program */ - -/* Prefix string of HTTP_PROXY */ -#define HTTP_PROXY_PREFIX "http://" -/* Prefix string of HTTP_PROXY */ -#define HTTPS_PROXY_PREFIX "https://" -#define PROXY_AUTH_NONE 0 -#define PROXY_AUTH_BASIC 1 -#define PROXY_AUTH_DIGEST 2 -int proxy_auth_type = PROXY_AUTH_NONE; - -/* reason of end repeating */ -#define REASON_UNK -2 -#define REASON_ERROR -1 -#define REASON_CLOSED_BY_LOCAL 0 -#define REASON_CLOSED_BY_REMOTE 1 - -/* return value of relay start function. */ -#define START_FORBIDDEN -2 -#define START_ERROR -1 -#define START_OK 0 -#define START_RETRY 1 - -/* socket related definitions */ -#ifndef _WIN32 -#define SOCKET int -#endif -#ifndef SOCKET_ERROR -#define SOCKET_ERROR -1 -#endif - -#ifdef _WIN32 -#define socket_errno() WSAGetLastError() -#else /* !_WIN32 */ -#define closesocket close -#define socket_errno() (errno) -#endif /* !_WIN32 */ - -#ifdef _WIN32 -#define popen _popen -#endif /* WIN32 */ - -/* packet operation macro */ -#define PUT_BYTE(ptr,data) (*(char*)ptr = data) - - -void cleanup_and_exit( int exit_code ) -{ - /* clean up to make valgrind happy */ - if ( relay_host ) - free( relay_host ); - if ( relay_user ) - free( relay_user ); - if ( ssl ) - { - SSL_free (ssl); -#if OPENSSL_VERSION_NUMBER < 0x010100000L - ERR_remove_state (0); -#endif - ERR_free_strings(); - EVP_cleanup(); - SSL_COMP_free_compression_methods(); - CRYPTO_cleanup_all_ex_data(); - } - -#ifdef _WIN32 - WSACleanup(); -#endif /* _WIN32 */ - - exit( exit_code ); -} - -void -print_help( int exit_code ) -{ - fprintf( stderr, "%s\nVersion %s\n", progdesc, revstr ); - fprintf( stderr, usage1, progname ); - fprintf( stderr, usage2 ); - exit( exit_code ); -} - -/* debug message output */ -void -debug( const char *fmt, ... ) -{ - va_list args; - if ( f_debug ) { - va_start( args, fmt ); - fprintf(stderr, "DEBUG: "); - vfprintf( stderr, fmt, args ); - va_end( args ); - } -} - -void -debug_( const char *fmt, ... ) /* without prefix */ -{ - va_list args; - if ( f_debug ) { - va_start( args, fmt ); - vfprintf( stderr, fmt, args ); - va_end( args ); - } -} - -/* error message output */ -void -error( const char *fmt, ... ) -{ - va_list args; - va_start( args, fmt ); - fprintf(stderr, "ERROR: "); - vfprintf( stderr, fmt, args ); - va_end( args ); -} - -/* print out the SSL error stack */ -void -ssl_error( const char *label ) -{ - int err; - - if (ERR_peek_error() == 0) debug( "Hit ssl_error code=0 on '%s'\n", label); - while ((err = ERR_get_error())) - { - error("%s: %s\n", label, ERR_error_string(err, NULL)); - } -} - -void -fatal( const char *fmt, ... ) -{ - va_list args; - va_start( args, fmt ); - fprintf(stderr, "FATAL: "); - vfprintf( stderr, fmt, args ); - va_end( args ); - cleanup_and_exit (EXIT_FAILURE); -} - - -void * -xmalloc (size_t size) -{ - void *ret = malloc(size); - if (ret == NULL) - fatal("Cannot allocate memory: %d bytes.\n", size); - return ret; -} - -char * -downcase( char *str ) -{ - char *buf = str; - while ( *buf ) { - if ( isupper(*buf) ) - *buf = tolower( *buf ); - buf++; - } - return str; /* return converted arg */ -} - -char * -expand_host_and_port (const char *fmt, const char *host, int port) -{ - const char *src; - char *buf, *dst; - size_t len = strlen(fmt) + strlen(host) + 20; - buf = xmalloc (len); - dst = buf; - src = fmt; - - while (*src) { - if (*src == '%') { - switch (src[1]) { - case 'h': - strcpy (dst, host); - src += 2; - break; - case 'p': - snprintf (dst, len, "%d", port); - src += 2; - break; - default: - src ++; - break; - } - dst = buf + strlen (buf); - } else if (*src == '\\') { - switch (src[1]) { - case 'r': /* CR */ - *dst++ = '\r'; - src += 2; - break; - case 'n': /* LF */ - *dst++ = '\n'; - src += 2; - break; - case 't': /* TAB */ - *dst++ = '\t'; - src += 2; - break; - default: - src ++; - break; - } - } else { - /* usual */ - *dst++ = *src++; - } - *dst = '\0'; - } - assert (strlen(buf) < len); - return buf; -} - - -int -lookup_resolve( char *str ) -{ - int ret; - - if ( strncasecmp( str, "both", 4 ) == 0 ) - ret = RESOLVE_BOTH; - else if ( strncasecmp( str, "remote", 6 ) == 0 ) - ret = RESOLVE_REMOTE; - else if ( strncasecmp( str, "local", 5 ) == 0 ) - ret = RESOLVE_LOCAL; - else if ( strspn(str, dotdigits) == strlen(str) ) { -#ifndef WITH_RESOLVER - fatal("Sorry, you can't specify to resolve the hostname with the -R option on Win32 environment."); -#endif /* not WITH_RESOLVER */ - ret = RESOLVE_LOCAL; /* this case is also 'local' */ - socks_ns.sin_addr.s_addr = inet_addr(str); - socks_ns.sin_family = AF_INET; - } - else - ret = RESOLVE_UNKNOWN; - return ret; -} - -char * -getusername(void) -{ -#ifdef _WIN32 - static char buf[1024]; - DWORD size = sizeof(buf); - buf[0] = '\0'; - GetUserName( buf, &size); - return buf; -#else /* not _WIN32 */ - struct passwd *pw = getpwuid(getuid()); - if ( pw == NULL ) - fatal("getpwuid() failed for uid: %d\n", getuid()); - return pw->pw_name; -#endif /* not _WIN32 */ -} - -/* expect - check STR is begin with substr with case-ignored comparison. - Return 1 if matched, otherwise 0. -*/ -int -expect( char *str, char *substr) -{ - int len = strlen(substr); - while ( 0 < len-- ) { - if ( toupper(*str) != toupper(*substr) ) - return 0; /* not matched */ - str++, substr++; - } - return 1; /* good, matched */ -} - - -/** PARAMETER operation **/ -#define PARAMETER_FILE "/etc/connectrc" -#define PARAMETER_DOTFILE ".connectrc" -typedef struct { - char* name; - char* value; -} PARAMETER_ITEM; -PARAMETER_ITEM parameter_table[] = { - { ENV_SOCKS_SERVER, NULL }, - { ENV_SOCKS5_SERVER, NULL }, - { ENV_SOCKS4_SERVER, NULL }, - { ENV_SOCKS_RESOLVE, NULL }, - { ENV_SOCKS5_RESOLVE, NULL }, - { ENV_SOCKS4_RESOLVE, NULL }, - { ENV_SOCKS5_USER, NULL }, - { ENV_SOCKS5_PASSWD, NULL }, - { ENV_SOCKS5_PASSWORD, NULL }, - { ENV_HTTP_PROXY, NULL }, - { ENV_HTTP_PROXY_USER, NULL }, - { ENV_HTTP_PROXY_PASSWORD, NULL }, - { ENV_HTTPS_PROXY, NULL }, - { ENV_HTTPS_PROXY_USER, NULL }, - { ENV_HTTPS_PROXY_PASSWORD, NULL }, - { ENV_CONNECT_USER, NULL }, - { ENV_CONNECT_PASSWORD, NULL }, - { ENV_SSH_ASKPASS, NULL }, - { ENV_SOCKS5_DIRECT, NULL }, - { ENV_SOCKS4_DIRECT, NULL }, - { ENV_SOCKS_DIRECT, NULL }, - { ENV_HTTP_DIRECT, NULL }, - { ENV_HTTPS_DIRECT, NULL }, - { ENV_CONNECT_DIRECT, NULL }, - { ENV_SOCKS5_AUTH, NULL }, - { NULL, NULL } -}; - -PARAMETER_ITEM* -find_parameter_item(const char* name) -{ - int i; - for( i = 0; parameter_table[i].name != NULL; i++ ){ - if ( strcmp(name, parameter_table[i].name) == 0 ) - return ¶meter_table[i]; - } - return NULL; -} - -void -read_parameter_file_1(const char* name) -{ - FILE* f; - int line; - char lbuf[1025]; - f = fopen(name, "r"); - if( f ){ - debug("Reading parameter file(%s)\n", name); - for ( line = 1; fgets(lbuf, 1024, f); line++ ) { - char *p, *q, *param, *value; - p = strchr(lbuf, '\n'); - if ( p == NULL ) - fatal("%s:%d: buffer overflow\n", name, line); - *p = '\0'; - p = strchr(lbuf, '#'); - if ( p ) - *p = '\0'; - for ( p = lbuf; *p; p++ ) - if( *p != ' ' && *p != '\t' ) break; - if ( *p == '\0' ) continue; - param = p; - p = strchr(p, '='); - if ( p == NULL ) { - error("%s:%d: missing equal sign\n", name, line); - continue; - } - for ( q = p - 1; q >= lbuf; q-- ) - if ( *q != ' ' && *q != '\t' ) break; - *++q = '\0'; - for ( ++p; *p; p++ ) - if ( *p != ' ' && *p != '\t' ) break; - value = p; - for ( ; *p; p++ ); - for ( p--; p >= lbuf; p-- ) - if ( *p != ' ' && *p != '\t' ) break; - *++p = '\0'; - if ( param && value ) { - PARAMETER_ITEM *item; - item = find_parameter_item(param); - if ( item == NULL ) { - error("%s:%d: unknown parameter `%s'\n", name, line, param); - continue; - } - item->value = strdup(value); - debug("Parameter `%s' is set to `%s'\n", param, value); - } - } - } -} - -void -read_parameter_file(void) -{ -#if !defined(_WIN32) || defined(cygwin) - char *name; - struct passwd *pw; -#endif - - read_parameter_file_1(PARAMETER_FILE); -#if !defined(_WIN32) || defined(cygwin) - pw = getpwuid(getuid()); - if ( pw == NULL ) - fatal("getpwuid() failed for uid: %d\n", getuid()); - name = xmalloc(strlen(pw->pw_dir) + strlen(PARAMETER_DOTFILE) + 2); - strcpy(name, pw->pw_dir); - strcat(name, "/" PARAMETER_DOTFILE); - read_parameter_file_1(name); - free(name); -#endif /* _WIN32 */ -} - -char* -getparam(const char* name) -{ - char *value = getenv(name); - if ( value == NULL ){ - PARAMETER_ITEM *item = find_parameter_item(name); - if ( item != NULL ) - value = item->value; - } - return value; -} - - -/** DIRECT connection **/ -#define MAX_DIRECT_ADDR_LIST 256 - -struct ADDRPAIR { - struct in_addr addr; - struct in_addr mask; - char *name; - int negative; -}; - -struct ADDRPAIR direct_addr_list[MAX_DIRECT_ADDR_LIST]; -int n_direct_addr_list = 0; - -void -mask_addr (void *addr, void *mask, int addrlen) -{ - char *a, *m; - a = addr; - m = mask; - while ( 0 < addrlen-- ) - *a++ &= *m++; -} - -int -add_direct_addr (struct in_addr *addr, struct in_addr *mask, int negative) -{ - struct in_addr iaddr; - char *s; - if ( MAX_DIRECT_ADDR_LIST <= n_direct_addr_list ) { - error("direct address table is full!\n"); - return -1; - } - iaddr = *addr; - mask_addr(&iaddr, mask, sizeof(iaddr)); - s = strdup(inet_ntoa(iaddr)); - debug("adding direct addr entry: %s%s/%s\n", - negative? "!": "", s, inet_ntoa(*mask)); - free(s); - memcpy( &direct_addr_list[n_direct_addr_list].addr, - &iaddr, sizeof(iaddr)); - memcpy( &direct_addr_list[n_direct_addr_list].mask, - mask, sizeof(*mask)); - direct_addr_list[n_direct_addr_list].name = NULL; - direct_addr_list[n_direct_addr_list].negative = negative; - n_direct_addr_list++; - return 0; -} - - -/* add domain/host name entry to direct name table */ -int -add_direct_host(char *name, int negative) -{ - if ( MAX_DIRECT_ADDR_LIST <= n_direct_addr_list ) { - error("direct address table is full!\n"); - return -1; - } - if (*name == '*') - name++; - if (*name == '.') - name++; - debug("adding direct name entry: %s%s\n", negative? "!": "", name); - direct_addr_list[n_direct_addr_list].name = name; - direct_addr_list[n_direct_addr_list].negative = negative; - n_direct_addr_list++; - return 0; -} - - -int -parse_addr_pair (const char *str, struct in_addr *addr, struct in_addr *mask) -{ - /* NOTE: */ - /* Assume already be splitted by separator - and formatted as folowing: - 1) 12.34.56.789/255.255.255.0 - 2) 12.34.56.789/24 - 3) 12.34.56. - All above generates same addr/mask pair 12.34.56.0 and 255.255.255.0 - */ - const char *ptr; - u_char *dsta, *dstm; - int i, n; - - assert( str != NULL ); - addr->s_addr = 0; - mask->s_addr = 0; - ptr = str; - dsta = (u_char*)&addr->s_addr; - dstm = (u_char*)&mask->s_addr; - for (i=0; i<4; i++ ) { - if ( *ptr == '\0' ) - break; /* case of format #3 */ - if ( !isdigit(*ptr) ) - return -1; /* format error: */ - *dsta++ = atoi( ptr ); - *dstm++ = 255; /* automatic mask for format #3 */ - while ( isdigit(*ptr) ) /* skip digits */ - ptr++; - if ( *ptr == '.' ) - ptr++; - else - break; - } - /* At this point, *ptr points '/' or EOS ('\0') */ - if ( *ptr == '\0' ) - return 0; /* complete as format #3 */ - if ( *ptr != '/' ) - return -1; /* format error */ - /* Now parse mask for format #1 or #2 */ - ptr++; - mask->s_addr = 0; /* clear automatic mask */ - - if ( strchr( ptr, '.') ) { - /* case of format #1 */ - dstm = (u_char*)&mask->s_addr; - for (i=0; i<4; i++) { - if ( !isdigit(*ptr) ) - return -1; /* format error: */ - *dstm++ = atoi(ptr); - while ( isdigit(*ptr) ) /* skip digits */ - ptr++; - if ( *ptr == '.' ) - ptr++; - else - break; /* from for loop */ - } - /* complete as format #1 */ - } else { - /* case of format #2 */ - if ( !isdigit(*ptr) ) - return -1; /* format error: */ - n = atoi(ptr); - if ( n<0 || 32s_addr = (n==0)? 0: htonl(((u_long)0xFFFFFFFF)<<(32-n)); - /* complete as format #1 */ - } - return 0; -} - -void -initialize_direct_addr (void) -{ - int negative; - int n_entries; - char *env = NULL, *beg, *next, *envkey = NULL; - struct in_addr addr, mask; - - if ( relay_method == METHOD_SOCKS ){ - if ( socks_version == 5 ) - envkey = ENV_SOCKS5_DIRECT; - else - envkey = ENV_SOCKS4_DIRECT; - env = getparam(envkey); - if ( env == NULL ) - env = getparam(ENV_SOCKS_DIRECT); - } else if ( relay_method == METHOD_HTTP ){ - env = getparam(ENV_HTTP_DIRECT); - } - - if ( env == NULL ) - env = getparam(ENV_CONNECT_DIRECT); - - if ( env == NULL ) - return; /* no entry */ - debug("making direct addr list from: '%s'\n", env); - env = strdup( env ); /* reallocate to modify */ - beg = next = env; - n_entries = 0; - do { - if ( MAX_DIRECT_ADDR_LIST <= n_entries ) { - error("too many entries in %s", envkey); - break; /* from do loop */ - } - next = strchr( beg, ','); - if ( next != NULL ) - *next++ = '\0'; - addr.s_addr = 0; - mask.s_addr = 0; - if (*beg == '!') { - negative = 1; - beg++; - } else - negative = 0; - if ( !parse_addr_pair( beg, &addr, &mask ) ) { - add_direct_addr( &addr, &mask, negative ); - } else { - add_direct_host( beg, negative ); - } - if ( next != NULL ) - beg = next; - } while ( next != NULL ); - - free( env ); - return; -} - -int -cmp_addr (void *addr1, void *addr2, int addrlen) -{ - return memcmp( addr1, addr2, addrlen ); -} - -int -is_direct_address (const struct in_addr addr) -{ - int i, neg; - struct in_addr iaddr; - - /* Note: assume IPV4 address !! */ - for (i=0; i 1 (exact match) - ends_with("foo.bar.com", "bar.com") => 1 (domain match) - ends_with("foo.beebar.com", "bar.com") => 0 (partial match) - ends_with("bar", "bar.com") => 0 (shorter) - */ -int -domain_match(const char *s1, const char *s2) -{ - int len1, len2; - const char *tail1, *tail2; - len1 = strlen(s1); - len2 = strlen(s2); - if (len1 < len2 || len1 == 0 || len2 == 0) - return 0; /* not match */ - tail1 = s1 + len1; - tail2 = s2 + len2; - while (0 < len1 && 0 < len2) { - if (*--tail1 != *--tail2) - break; /* not match */ - len1--, len2--; - } - if (len2 != 0) - return 0; /* not match */ - /* Now exact match, domain match or partial match. - Return true if exact or domain match. - Or continue checking. */ - if (tail1 == s1 || tail1[-1] == '.') - return 1; /* match! */ - return 0; /* not match */ -} - -/* Check given NAME is ends with one of - registered direct name entry. - Return 1 if matched, or 0. -*/ -int -is_direct_name (const char *name) -{ - int len, i; - debug("checking %s is for direct?\n", name); - len = strlen(name); - if (len < 1) - return 0; /* false */ - for (i=0; is_port); - debug("service: %s => %d\n", service, port); - } - } - return (u_short)port; -} - -int -getarg( int argc, char **argv ) -{ - int err = 0; - char *ptr, *server = (char*)NULL; - int method = METHOD_DIRECT; - - progname = *argv; - argc--; - argv++; - - /* check options */ - while ( (0 < argc) && (**argv == '-') ) { - ptr = *argv + 1; - while ( ptr && *ptr ) - { - switch ( *ptr ) { - case 's': /* use SOCKS */ - method = METHOD_SOCKS; - break; - case 'n': /* no proxy */ - method = METHOD_DIRECT; - break; - case 'h': /* use http-proxy */ - method = METHOD_HTTP; - break; - case 'x': /* use https-proxy */ - method = METHOD_HTTPS; - break; - case 't': - method = METHOD_TELNET; - break; - case 'S': /* specify SOCKS server */ - if ( 1 < argc ) { - argc--; - argv++; - method = METHOD_SOCKS; - server = *argv; - } else { - error("option '-%c' needs argument.\n", *ptr); - err++; - } - break; - case 'H': /* specify http-proxy server */ - if ( 1 < argc ) { - argc--; - argv++; - method = METHOD_HTTP; - server = *argv; - } else { - error("option '-%c' needs argument.\n", *ptr); - err++; - } - break; - case 'X': /* specify https-proxy server */ - if ( 1 < argc ) { - argc--; - argv++; - method = METHOD_HTTPS; - server = *argv; - } else { - error("option '-%c' needs argument.\n", *ptr); - err++; - } - break; - case 'T': /* specify telnet proxy server */ - if ( 1 < argc ) { - argc--; - argv++; - method = METHOD_TELNET; - server = *argv; - } else { - error("option '-%c' needs argument.\n", *ptr); - err++; - } - break; - case 'c': - if (1 < argc) { - argc--; - argv++; - telnet_command = *argv; - } else { - error("option '%c' needs argument.\n", *ptr); - err++; - } - break; - case 'P': - f_hold_session = 1; - /* without break */ - case 'p': /* specify port to forward */ - if ( 1 < argc ) { - argc--; - argv++; - local_type = LOCAL_SOCKET; - local_port = resolve_port(*argv); - } else { - error("option '-%c' needs argument.\n", *ptr); - err++; - } - break; -#ifndef _WIN32 - case 'w': - if ( 1 < argc ) { - argc--; - argv++; - connect_timeout = atoi(*argv); - } else { - error("option '-%c' needs argument.\n", *ptr); - err++; - } - break; -#endif /* not _WIN32 */ - case '4': - socks_version = 4; - break; - case '5': - socks_version = 5; - break; - case 'a': - if ( 1 < argc ) { - argc--; - argv++; - socks5_auth = *argv; - } else { - error("option '-%c' needs argument.\n", *ptr); - err++; - } - break; - case 'R': /* specify resolve method */ - if ( 1 < argc ) { - argc--; - argv++; - socks_resolve = lookup_resolve( *argv ); - } else { - error("option '-%c' needs argument.\n", *ptr); - err++; - } - break; - case 'V': /* print version */ - fprintf(stderr, "%s\nVersion %s\n", progdesc, revstr); - exit(0); - case 'd': /* debug mode */ - f_debug++; - break; - case '-': /* long options */ - if (!strcmp(*argv, "--help")) - { - print_help(0); - } - if (!strcmp(*argv, "--socks-proxy")) - { - if ( 1 < argc ) - { - argc--; - argv++; - method = METHOD_SOCKS; - server = *argv; - } - else - { - error("option '%s' needs argument.\n", *argv); - err++; - } - } - else if (!strcmp(*argv, "--http-proxy")) - { - if ( 1 < argc ) - { - argc--; - argv++; - method = METHOD_HTTP; - server = *argv; - } - else - { - error("option '%s' needs argument.\n", *argv); - err++; - } - } - else if (!strcmp(*argv, "--https-proxy")) - { - if ( 1 < argc ) - { - argc--; - argv++; - method = METHOD_HTTPS; - server = *argv; - } - else - { - error("option '%s' needs argument.\n", *argv); - err++; - } - } - else if (!strcmp(*argv, "--https-proxy-ca")) - { - if ( 1 < argc ) - { - argc--; - argv++; - https_ca_file = *argv; - } - else - { - error("option '%s' needs argument.\n", *argv); - err++; - } - } - else if (!strcmp(*argv, "--https-proxy-ca-path")) - { - if ( 1 < argc ) - { - argc--; - argv++; - https_ca_path = *argv; - } - else - { - error("option '%s' needs argument.\n", *argv); - err++; - } - } - else if (!strcmp(*argv, "--no-check-certificate")) - { - https_check_certificate = 0; - } - else if (!strcmp(*argv, "--https-proxy-certname")) - { - if ( 1 < argc ) - { - argc--; - argv++; - https_proxy_certname = *argv; - } - else - { - error("option '%s' needs argument.\n", *argv); - err++; - } - } - else if (!strcmp(*argv, "--https-user-cert")) - { - if ( 1 < argc ) - { - argc--; - argv++; - https_usercert_file = *argv; - } - else - { - error("option '%s' needs argument.\n", *argv); - err++; - } - } - else if (!strcmp(*argv, "--https-user-key")) - { - if ( 1 < argc ) - { - argc--; - argv++; - https_userkey_file = *argv; - } - else - { - error("option '%s' needs argument.\n", *argv); - err++; - } - } - else if (!strcmp(*argv, "--telnet-proxy")) - { - if ( 1 < argc ) - { - argc--; - argv++; - method = METHOD_TELNET; - server = *argv; - } - else - { - error("option '%s' needs argument.\n", *argv); - err++; - } - } - else - { - error("unknown option '%s'\n", *argv); - err++; - } - - ptr = NULL; - break; - - default: - error("unknown option '-%c'\n", *ptr); - err++; - } - if (ptr) ptr++; - } - argc--; - argv++; - } - - /* check error */ - if ( 0 < err ) - goto quit; - - set_relay( method, server ); - - /* check destination HOST (MUST) */ - if ( argc == 0 ) - print_help(0); - - dest_host = argv[0]; - /* decide port or service name from programname or argument */ - if ( ((ptr=strrchr( progname, '/' )) != NULL) || - ((ptr=strchr( progname, '\\')) != NULL) ) - ptr++; - else - ptr = progname; - if ( dest_port == 0 ) { - /* accept only if -P is not specified. */ - if ( 1 < argc ) { - /* get port number from argument (prior to progname) */ - /* NOTE: This way is for cvs ext method. */ - dest_port = resolve_port(argv[1]); - } else if ( strncmp( ptr, "connect-", 8) == 0 ) { - /* decide port number from program name */ - char *str = strdup( ptr+8 ); - str[strcspn( str, "." )] = '\0'; - dest_port = resolve_port(str); - free(str); - } - } - /* check port number */ - if ( dest_port <= 0 ) { - error( "You must specify the destination port correctly.\n"); - err++; - goto quit; - } - if ( (relay_method != METHOD_DIRECT) && (relay_port <= 0) ) { - error("Invalid relay port: %d\n", dest_port); - err++; - goto quit; - } - -quit: - /* report for debugging */ - debug("relay_method = %s (%d)\n", - method_names[relay_method], relay_method); - if ( relay_method != METHOD_DIRECT ) { - debug("relay_host=%s\n", relay_host); - debug("relay_port=%d\n", relay_port); - debug("relay_user=%s\n", relay_user); - } - if ( relay_method == METHOD_SOCKS ) { - debug("socks_version=%d\n", socks_version); - debug("socks_resolve=%s (%d)\n", - resolve_names[socks_resolve], socks_resolve); - } - debug("local_type=%s\n", local_type_names[local_type]); - if ( local_type == LOCAL_SOCKET ) { - debug("local_port=%d\n", local_port); - if (f_hold_session) - debug (" with holding remote session.\n"); - } - debug("dest_host=%s\n", dest_host); - debug("dest_port=%d\n", dest_port); - if ( 0 < err ) - print_help(1); - - return 0; -} - -#ifndef _WIN32 -/* Time-out feature is not allowed for Win32 native compilers. */ -/* MSVC and Borland C cannot but Cygwin and UNIXes can. */ - -/* timeout signal hander */ -static void -sig_timeout(const int signum) -{ - signal( SIGALRM, SIG_IGN ); - alarm( 0 ); - error( "timed out\n" ); - exit(1); -} - -/* set timeout param = seconds, 0 clears */ -void -set_timeout(int timeout) -{ - /* This feature is allowed for UNIX or cygwin environments, currently */ - if ( timeout == 0 ) { - debug( "clearing timeout\n" ); - signal( SIGALRM, SIG_IGN ); - alarm( 0 ); - } else { - debug( "setting timeout: %d seconds\n", timeout ); - signal(SIGALRM, sig_timeout); - alarm( timeout ); - } -} -#endif - -#if !defined(_WIN32) && !defined(__CYGWIN32__) -void -switch_ns (struct sockaddr_in *ns) -{ - res_init(); - memcpy (&_res.nsaddr_list[0], ns, sizeof(*ns)); - _res.nscount = 1; - debug("Using nameserver at %s\n", inet_ntoa(ns->sin_addr)); -} -#endif /* !_WIN32 && !__CYGWIN32__ */ - -/* TODO: IPv6 - TODO: fallback if askpass execution failed. - */ - -int -local_resolve (const char *host, struct sockaddr_in *addr) -{ - struct hostent *ent; - if ( strspn(host, dotdigits) == strlen(host) ) { - /* given by IPv4 address */ - addr->sin_family = AF_INET; - addr->sin_addr.s_addr = inet_addr(host); - } else { - debug("resolving host by name: %s\n", host); - ent = gethostbyname (host); - if ( ent ) { - memcpy (&addr->sin_addr, ent->h_addr, ent->h_length); - addr->sin_family = ent->h_addrtype; - debug("resolved: %s (%s)\n", - host, inet_ntoa(addr->sin_addr)); - } else { - debug("failed to resolve locally.\n"); - return -1; /* failed */ - } - } - return 0; /* good */ -} - -int -open_connection( const char *host, u_short port ) -{ - SOCKET s; - struct sockaddr_in saddr; - - /* resolve address of proxy or direct target */ - if (local_resolve (host, &saddr) < 0) { - error("can't resolve hostname: %s\n", host); - return SOCKET_ERROR; - } - saddr.sin_port = htons(port); - - debug("connecting to %s:%u\n", inet_ntoa(saddr.sin_addr), port); - s = socket( AF_INET, SOCK_STREAM, 0 ); - if ( connect( s, (struct sockaddr *)&saddr, sizeof(saddr)) - == SOCKET_ERROR) { - debug( "connect() failed.\n"); - return SOCKET_ERROR; - } - return s; -} - -void -report_text( char *prefix, char *buf ) -{ - static char work[1024]; - char *tmp; - - if ( !f_debug ) - return; - if ( !f_report ) - return; /* don't report */ - debug("%s \"", prefix); - while ( *buf ) { - memset( work, 0, sizeof(work)); - tmp = work; - while ( *buf && ((tmp-work) < (int)sizeof(work)-5) ) { - switch ( *buf ) { - case '\t': *tmp++ = '\\'; *tmp++ = 't'; break; - case '\r': *tmp++ = '\\'; *tmp++ = 'r'; break; - case '\n': *tmp++ = '\\'; *tmp++ = 'n'; break; - case '\\': *tmp++ = '\\'; *tmp++ = '\\'; break; - default: - if ( isprint(*buf) ) { - *tmp++ = *buf; - } else { - int consumed = tmp - work; - snprintf( tmp, sizeof(work)-consumed, - "\\x%02X", (char)*buf); - tmp += strlen(tmp); - } - } - buf++; - *tmp = '\0'; - } - debug_("%s", work); - } - - debug_("\"\n"); -} - - -void -report_bytes( char *prefix, char *buf, int len ) -{ - if ( ! f_debug ) - return; - debug( "%s", prefix ); - while ( 0 < len ) { - fprintf( stderr, " %02x", *buf); - buf++; - len--; - } - fprintf(stderr, "\n"); - return; -} - -int -atomic_out( SOCKET s, char *buf, int size ) -{ - int ret, len; - - assert( buf != NULL ); - assert( 0<=size ); - /* do atomic out */ - ret = 0; - while ( 0 < size ) { - len = send( s, buf+ret, size, 0 ); - if ( len == -1 ) - fatal("atomic_out() failed to send(), %d\n", socket_errno()); - ret += len; - size -= len; - } - if (!f_report) { - debug("atomic_out() [some bytes]\n"); - debug(">>> xx xx xx xx ...\n"); - } else { - debug("atomic_out() [%d bytes]\n", ret); - report_bytes(">>>", buf, ret); - } - return ret; -} - -int -atomic_in( SOCKET s, char *buf, int size ) -{ - int ret, len; - - assert( buf != NULL ); - assert( 0<=size ); - - /* do atomic in */ - ret = 0; - while ( 0 < size ) { - len = recv( s, buf+ret, size, 0 ); - if ( len == -1 ) { - fatal("atomic_in() failed to recv(), %d\n", socket_errno()); - } else if ( len == 0 ) { - fatal( "Connection closed by peer.\n"); - } - ret += len; - size -= len; - } - if (!f_report) { - debug("atomic_in() [some bytes]\n"); - debug("<<< xx xx xx xx ...\n"); - } else { - debug("atomic_in() [%d bytes]\n", ret); - report_bytes("<<<", buf, ret); - } - return ret; -} - -int -line_input( SOCKET s, char *buf, int size ) -{ - char *dst = buf; - if ( size == 0 ) - return 0; /* no error */ - size--; - while ( 0 < size ) { - switch ( recv( s, dst, 1, 0) ) { /* recv one-by-one */ - case SOCKET_ERROR: - error("recv() error\n"); - return -1; /* error */ - case 0: - size = 0; /* end of stream */ - break; - default: - /* continue reading until last 1 char is EOL? */ - if ( *dst == '\n' ) { - /* finished */ - size = 0; - } else { - /* more... */ - size--; - } - dst++; - } - } - *dst = '\0'; - report_text( "<<<", buf); - return 0; -} - -/* cut_token() - Span token in given string STR until char in DELIM is appeared. - Then replace contiguous DELIMS with '\0' for string termination - and returns next pointer. - If no next token, return NULL. -*/ -char * -cut_token( char *str, char *delim) -{ - char *ptr = str + strcspn(str, delim); - char *end = ptr + strspn(ptr, delim); - if ( ptr == str ) - return NULL; - while ( ptr < end ) - *ptr++ = '\0'; - return ptr; -} - -const char * -lookup(int num, LOOKUP_ITEM *items) -{ - int i = 0; - while (0 <= items[i].num) { - if (items[i].num == num) - return items[i].str; - i++; - } - return "(unknown)"; -} - -/* readpass() - password input routine - Use ssh-askpass (same mechanism to OpenSSH) -*/ -char * -readpass( const char* prompt, ...) -{ - static char buf[1000]; /* XXX, don't be fix length */ - va_list args; - va_start(args, prompt); - vsnprintf(buf, sizeof(buf), prompt, args); - va_end(args); - - if ( getparam(ENV_SSH_ASKPASS) -#if !defined(_WIN32) && !defined(__CYGWIN32__) - && getenv("DISPLAY") -#endif /* not _WIN32 && not __CYGWIN32__ */ - ) { - /* use ssh-askpass to get password */ - FILE *fp; - char *askpass = getparam(ENV_SSH_ASKPASS), *cmd; - int cmd_size = strlen(askpass) +1 +1 +strlen(buf) +1 +1; - cmd = xmalloc(cmd_size); - snprintf(cmd, cmd_size, "%s \"%s\"", askpass, buf); - fp = popen(cmd, "r"); - free(cmd); - if ( fp == NULL ) - return NULL; /* fail */ - buf[0] = '\0'; - if (fgets(buf, sizeof(buf), fp) == NULL) - return NULL; /* fail */ - fclose(fp); - } else { - tty_readpass( buf, buf, sizeof(buf)); - } - buf[strcspn(buf, "\r\n")] = '\0'; - return buf; -} - - -/* SSL callback routing for reading private key passphrase, if necessary */ -int password_callback (char *buf, int size, int rwflag, void *u) -{ - char *pass; - - if (buf) - { - pass = readpass("Enter privatekey passphrase for\n%s: ", https_userkey_file); - if (pass) - { - strncpy(buf, pass, size); - return strlen (buf); - } - } - return 0; -} - - -static int -socks5_do_auth_userpass( int s ) -{ - char buf[1024], *ptr; - char *pass = NULL; - int len; - - /* do User/Password authentication. */ - /* This feature requires username and password from - command line argument or environment variable, - or terminal. */ - if (relay_user == NULL) - fatal("cannot determine user name.\n"); - - /* get password from environment variable if exists. */ - if ((pass=determine_relay_password()) == NULL && - (pass=readpass("Enter SOCKS5 password for %s@%s: ", - relay_user, relay_host)) == NULL) - fatal("Cannot get password for user: %s\n", relay_user); - - /* make authentication packet */ - ptr = buf; - PUT_BYTE( ptr++, 1 ); /* subnegotiation ver.: 1 */ - len = strlen( relay_user ); /* ULEN and UNAME */ - PUT_BYTE( ptr++, len ); - strcpy( ptr, relay_user ); - ptr += len; - len = strlen( pass ); /* PLEN and PASSWD */ - PUT_BYTE( ptr++, strlen(pass)); - strcpy( ptr, pass ); - ptr += len; - memset (pass, 0, strlen(pass)); /* erase password */ - - /* send it and get answer */ - f_report = 0; - atomic_out( s, buf, ptr-buf ); - f_report = 1; - atomic_in( s, buf, 2 ); - - /* check status */ - if ( buf[1] == 0 ) - return 0; /* success */ - else - return -1; /* fail */ -} - -static const char * -socks5_getauthname( int auth ) -{ - switch ( auth ) { - case SOCKS5_AUTH_REJECT: return "REJECTED"; - case SOCKS5_AUTH_NOAUTH: return "NO-AUTH"; - case SOCKS5_AUTH_GSSAPI: return "GSSAPI"; - case SOCKS5_AUTH_USERPASS: return "USERPASS"; - case SOCKS5_AUTH_CHAP: return "CHAP"; - case SOCKS5_AUTH_EAP: return "EAP"; - case SOCKS5_AUTH_MAF: return "MAF"; - default: return "(unknown)"; - } -} - -typedef struct { - char* name; - char auth; -} AUTH_METHOD_ITEM; - -AUTH_METHOD_ITEM socks5_auth_table[] = { - { "none", SOCKS5_AUTH_NOAUTH }, - { "gssapi", SOCKS5_AUTH_GSSAPI }, - { "userpass", SOCKS5_AUTH_USERPASS }, - { "chap", SOCKS5_AUTH_CHAP }, - { NULL, -1 }, -}; - -int -socks5_auth_parse_1(char *start, char *end) -{ - int i, len; - for ( ; *start; start++ ) - if ( *start != ' ' && *start != '\t') break; - for ( end--; end >= start; end-- ) { - if ( *end != ' ' && *end != '\t'){ - end++; - break; - } - } - len = end - start; - for ( i = 0; socks5_auth_table[i].name != NULL; i++ ){ - if ( strncmp(start, socks5_auth_table[i].name, len) == 0) { - return socks5_auth_table[i].auth; - } - } - fatal("Unknown auth method: %s\n", start); - return -1; -} - -int -socks5_auth_parse(char *start, int *auth_list, int max_auth) -{ - char *end; - int i = 0; - while ( i < max_auth ) { - end = strchr(start, ','); - if (*start && end) { - auth_list[i++] = socks5_auth_parse_1(start, end); - start = ++end; - } else { - break; - } - } - if ( *start && ( i < max_auth ) ){ - for( end = start; *end; end++ ); - auth_list[i++] = socks5_auth_parse_1(start, end); - } else { - fatal("Too much auth method.\n"); - } - return i; -} - -/* begin SOCKS5 relaying - And no authentication is supported. - */ -int -begin_socks5_relay( SOCKET s ) -{ - char buf[256], *ptr, *env = socks5_auth; - unsigned char n_auth = 0, auth_method; - int len, auth_list[10], auth_result, i; - - debug( "begin_socks_relay()\n"); - - /* request authentication */ - ptr = buf; - PUT_BYTE( ptr++, 5); /* SOCKS version (5) */ - - if ( env == NULL ) - env = getparam(ENV_SOCKS5_AUTH); - if ( env == NULL ) - { - /* add no-auth authentication */ - auth_list[n_auth++] = SOCKS5_AUTH_NOAUTH; - /* add user/pass authentication */ - auth_list[n_auth++] = SOCKS5_AUTH_USERPASS; - } - else - { - n_auth = socks5_auth_parse(env, auth_list, 10); - } - - PUT_BYTE( ptr++, n_auth); /* num auth */ - for (i=0; i>8); /* DST.PORT */ - PUT_BYTE( ptr++, dest_port&0xFF); - atomic_out( s, buf, ptr-buf); /* send request */ - atomic_in( s, buf, 4 ); /* recv response */ - if ( (buf[1] != SOCKS5_REP_SUCCEEDED) ) { /* check reply code */ - error("Got error response from SOCKS server: %d (%s).\n", - buf[1], lookup(buf[1], socks5_rep_names)); - return -1; - } - ptr = buf + 4; - switch ( buf[3] ) { /* case by ATYP */ - case 1: /* IP v4 ADDR*/ - atomic_in( s, ptr, 4+2 ); /* recv IPv4 addr and port */ - break; - case 3: /* DOMAINNAME */ - atomic_in( s, ptr, 1 ); /* recv name and port */ - atomic_in( s, ptr+1, *(ptr + 2)); - break; - case 4: /* IP v6 ADDR */ - atomic_in( s, ptr, 16+2 ); /* recv IPv6 addr and port */ - break; - } - - /* Conguraturation, connected via SOCKS5 server! */ - return 0; -} - -/* begin SOCKS protocol 4 relaying - And no authentication is supported. - - There's SOCKS protocol version 4 and 4a. Protocol version - 4a has capability to resolve hostname by SOCKS server, so - we don't need resolving IP address of destination host on - local machine. - - Environment variable SOCKS_RESOLVE directs how to resolve - IP addess. There's 3 keywords allowed; "local", "remote" - and "both" (case insensitive). Keyword "local" means taht - target host name is resolved by localhost resolver - (usualy with gethostbyname()), "remote" means by remote - SOCKS server, "both" means to try resolving by localhost - then remote. - - SOCKS4 protocol and authentication of SOCKS5 protocol - requires user name on connect request. - User name is determined by following method. - - 1. If server spec has user@hostname:port format then - user part is used for this SOCKS server. - - 2. Get user name from environment variable LOGNAME, USER - (in this order). - -*/ -int -begin_socks4_relay( SOCKET s ) -{ - char buf[256], *ptr; - - debug( "begin_socks_relay()\n"); - - /* make connect request packet - protocol v4: - VN:1, CD:1, PORT:2, ADDR:4, USER:n, NULL:1 - protocol v4a: - VN:1, CD:1, PORT:2, DUMMY:4, USER:n, NULL:1, HOSTNAME:n, NULL:1 - */ - ptr = buf; - PUT_BYTE( ptr++, 4); /* protocol version (4) */ - PUT_BYTE( ptr++, 1); /* CONNECT command */ - PUT_BYTE( ptr++, dest_port>>8); /* destination Port */ - PUT_BYTE( ptr++, dest_port&0xFF); - /* destination IP */ - memcpy(ptr, &dest_addr.sin_addr, sizeof(dest_addr.sin_addr)); - ptr += sizeof(dest_addr.sin_addr); - if ( dest_addr.sin_addr.s_addr == 0 ) - *(ptr-1) = 1; /* fake, protocol 4a */ - /* username */ - if (relay_user == NULL) - fatal( "Cannot determine user name.\n"); - strcpy( ptr, relay_user ); - ptr += strlen( relay_user ) +1; - /* destination host name (for protocol 4a) */ - if ( (socks_version == 4) && (dest_addr.sin_addr.s_addr == 0)) { - strncpy( ptr, dest_host, sizeof(buf)+buf-ptr ); - ptr += strlen( dest_host ) +1; - } - /* send command and get response - response is: VN:1, CD:1, PORT:2, ADDR:4 */ - atomic_out( s, buf, ptr-buf); /* send request */ - atomic_in( s, buf, 8 ); /* recv response */ - if ( (buf[1] != SOCKS4_REP_SUCCEEDED) ) { /* check reply code */ - error("Got error response: %d: '%s'.\n", - buf[1], lookup(buf[1], socks4_rep_names)); - return -1; /* failed */ - } - - /* Conguraturation, connected via SOCKS4 server! */ - return 0; -} - -int -sendf(SOCKET s, const char *fmt,...) -{ - static char buf[10240]; /* xxx, enough? */ - - va_list args; - va_start( args, fmt ); - vsnprintf( buf, sizeof(buf), fmt, args ); - va_end( args ); - - report_text(">>>", buf); - if (relay_method == METHOD_HTTP) - { - if ( send(s, buf, strlen(buf), 0) == SOCKET_ERROR ) - { - debug("failed to send http request. errno=%d\n", socket_errno()); - return -1; - } - } - else - { - if (SSL_write(ssl, buf, strlen(buf)) < 0) - { - debug("failed to send https request.\n"); - ssl_error( "SSL_write()" ); - return -1; - } - } - return 0; -} - -int -sslread(char *buf, size_t size) -{ - int ret = 0; - - if ( size == 0 ) - return 0; /* no error */ - size--; - - bzero(buf, size); - - debug("sslread: bytes waiting: %d\n", SSL_pending(ssl)); - ret = SSL_read(ssl, buf, size); - debug("SSL_read: read %d bytes\n", ret); - if (ret < 0) - { - debug("failed receiving https reply.\n"); - ssl_error( "SSL_read()" ); - return -1; - } - else - report_text("<<<", buf); - return ret; -} - -int -ssl_line_input(char *buf, size_t size) -{ - char *dst = sslbuf; - size_t len = 0; - - if ( size == 0 ) - return 0; /* no error */ - - /* global buffer empty, fill it with new data and reset the index */ - if (strlen(sslbuf) == 0) - { - if (sslread(sslbuf, sizeof(sslbuf)) < 0) - { - debug("no reply\n"); - return -1; - } - sslbuf_idx = 0; - } - - dst += sslbuf_idx; - while (*dst != '\n' && sslbuf_idx+len < sizeof(sslbuf) && len < size) - { - dst++; - len++; - } - -#if 0 - report_text("<<< dst=" , dst); -#endif - - /* no valid input found, or input too long for buffer */ - if ((len > size) || (*dst != '\n')) - return -1; - - dst = sslbuf; - dst += sslbuf_idx; - memcpy(buf, dst, ++len); - buf[len] = '\0'; - - report_text("<<< ", buf); - sslbuf_idx += len; - - return 0; -} - -const char *base64_table = -"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - -char * -make_base64_string(const char *str) -{ - static char *buf; - unsigned char *src, *dst; - int bits, data, src_len, dst_len; - /* make base64 string */ - src_len = strlen(str); - dst_len = (src_len+2)/3*4; - buf = xmalloc(dst_len+1); - bits = data = 0; - src = (unsigned char *)str; - dst = (unsigned char *)buf; - while ( dst_len-- ) { - if ( bits < 6 ) { - data = (data << 8) | *src; - bits += 8; - if ( *src != 0 ) - src++; - } - *dst++ = base64_table[0x3F & (data >> (bits-6))]; - bits -= 6; - } - *dst = '\0'; - /* fix-up tail padding */ - switch ( src_len%3 ) { - case 1: - *--dst = '='; - case 2: - *--dst = '='; - } - return buf; -} - - -int -basic_auth (SOCKET s) -{ - char *userpass; - char *cred; - const char *user = relay_user; - char *pass = NULL; - int len, ret; - - /* Get username/password for authentication */ - if (user == NULL) - fatal("Cannot decide username for proxy authentication."); - if ((pass = determine_relay_password ()) == NULL && - (pass = readpass("Enter proxy authentication password for %s@%s: ", - relay_user, relay_host)) == NULL) - fatal("Cannot decide password for proxy authentication."); - - len = strlen(user)+strlen(pass)+1; - userpass = xmalloc(len+1); - snprintf(userpass, len+1, "%s:%s", user, pass); - memset (pass, 0, strlen(pass)); - cred = make_base64_string(userpass); - memset (userpass, 0, len); - - f_report = 0; /* don't report for security */ - ret = sendf(s, "Proxy-Authorization: Basic %s\r\n", cred); - f_report = 1; - report_text(">>>", "Proxy-Authorization: Basic xxxxx\r\n"); - - memset(cred, 0, strlen(cred)); - free(cred); - free(userpass); - - return ret; -} - -/* begin relaying via HTTP proxy - Directs CONNECT method to proxy server to connect to - destination host (and port). It may not be allowed on your - proxy server. - */ -int -begin_http_relay( SOCKET s ) -{ - char buf[4096]; - int result; - char *auth_what; - - debug("begin_http_relay()\n"); - - if (sendf(s,"CONNECT %s:%d HTTP/1.1\r\nHost: %s\r\n", dest_host, dest_port, dest_host) < 0) - return START_ERROR; - if (proxy_auth_type == PROXY_AUTH_BASIC && basic_auth (s) < 0) - return START_ERROR; - if (sendf(s,"\r\n") < 0) - return START_ERROR; - - /* get response */ - if ( line_input(s, buf, sizeof(buf)) < 0 ) { - debug("failed to read http response.\n"); - return START_ERROR; - } - - /* check status */ - if (!strchr(buf, ' ')) { - error ("Unexpected http response: '%s'.\n", buf); - return START_ERROR; - } - result = atoi(strchr(buf,' ')); - - switch ( result ) { - case 200: - /* Conguraturation, connected via http proxy server! */ - debug("connected, start user session.\n"); - break; - case 302: /* redirect */ - do { - if (line_input(s, buf, sizeof(buf))) - break; - if (expect(buf, "Location: ")) { - relay_host = cut_token(buf, "//"); - cut_token(buf, "/"); - relay_port = atoi(cut_token(buf, ":")); - } - } while (strcmp(buf,"\r\n") != 0); - return START_RETRY; - - /* We handle both 401 and 407 codes here: 401 is WWW-Authenticate, which - * not strictly the correct response, but some proxies do send this (e.g. - * Symantec's Raptor firewall) */ - case 401: /* WWW-Auth required */ - case 407: /* Proxy-Auth required */ - /** NOTE: As easy implementation, we support only BASIC scheme - and ignore realm. */ - /* If proxy_auth_type is PROXY_AUTH_BASIC and get - this result code, authentication was failed. */ - if (proxy_auth_type != PROXY_AUTH_NONE) { - error("Authentication failed.\n"); - return START_FORBIDDEN; - } - auth_what = (result == 401) ? "WWW-Authenticate:" : "Proxy-Authenticate:"; - do { - if ( line_input(s, buf, sizeof(buf)) ) { - break; - } - downcase(buf); - if (expect(buf, auth_what)) { - /* parse type and realm */ - char *scheme, *realm; - scheme = cut_token(buf, " "); - realm = cut_token(scheme, " "); - if ( scheme == NULL || realm == NULL ) { - debug("Invalid format of %s field.", auth_what); - return START_ERROR; /* fail */ - } - /* check supported auth type */ - if (expect(scheme, "basic")) { - proxy_auth_type = PROXY_AUTH_BASIC; - } else { - debug("Unsupported authentication type: %s", scheme); - } - } - } while (strcmp(buf,"\r\n") != 0); - if ( proxy_auth_type == PROXY_AUTH_NONE ) { - debug("Can't find %s in response header.", auth_what); - return START_ERROR; - } else { - return START_RETRY; - } - - default: - /* Not allowed */ - debug("http proxy is not allowed.\n"); - return START_FORBIDDEN; - } - /* skip to end of response header */ - do { - if ( line_input(s, buf, sizeof(buf) ) ) { - debug("Can't skip response headers\n"); - return START_ERROR; - } - } - while ( strcmp(buf,"\r\n") != 0 ); - - return START_OK; -} - -/* begin relaying via HTTPS proxy - Directs CONNECT method to proxy server to connect to - destination host (and port). It may not be allowed on your - proxy server. - */ -int -begin_https_relay( SOCKET s ) -{ - char buf[4096]; - int result; - char *auth_what; - - SSL_CTX *ctx = NULL; -#ifdef X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT - X509_VERIFY_PARAM *param = NULL; -#endif - - debug("begin_https_relay()\n"); - - if (https_proxy_certname == NULL) - https_proxy_certname = relay_host; - debug("https_proxy = %s\n", relay_host); - - if (https_proxy_certname == NULL) - https_proxy_certname = getparam(ENV_HTTPS_PROXY_CERTNAME); - if (https_ca_file == NULL) - https_ca_file = getparam(ENV_HTTPS_PROXY_CA_FILE); - if (https_ca_path == NULL) - https_ca_path = getparam(ENV_HTTPS_PROXY_CA_PATH); - if (https_usercert_file == NULL) - https_usercert_file = getparam(ENV_HTTPS_PROXY_USERCERT); - if (https_userkey_file == NULL) - https_userkey_file = getparam(ENV_HTTPS_PROXY_USERKEY); - - debug("https_proxy_certname = %s\n", https_proxy_certname); - debug("https_ca_file = %s\n", https_ca_file); - debug("https_ca_path = %s\n", https_ca_path); - debug("https_usercert = %s\n", https_usercert_file); - debug("https_userkey = %s\n", https_userkey_file); - - OpenSSL_add_all_algorithms(); - OpenSSL_add_all_ciphers(); - OpenSSL_add_all_digests(); - SSL_load_error_strings(); - - /* OpenSSL preparation */ - SSL_library_init(); - - if ((ctx = SSL_CTX_new(TLS_method())) == NULL) - ssl_error( "SSL_CTX_new()" ); - if (SSL_CTX_load_verify_locations(ctx, https_ca_file, https_ca_path) != 1) - ssl_error( "SSL_CTX_load_verify_locations()" ); - if (https_check_certificate) - { -#ifdef X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT - debug( "Adding https proxy server certificate check\n" ); - param = X509_VERIFY_PARAM_new(); - if (X509_VERIFY_PARAM_set1_host(param, https_proxy_certname, strlen(https_proxy_certname)) != 1) - ssl_error( "X509_VERIFY_PARAM_set1_host()" ); - if (SSL_CTX_set1_param(ctx, param) != 1) - ssl_error( "SSL_CTX_set1_param()" ); - X509_VERIFY_PARAM_free(param); -#else - debug( "Your version of OpenSSL only has limited server certificate check support!" ); -#endif - SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER| SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL); - } - - SSL_CTX_set_default_passwd_cb (ctx, password_callback); - - if (https_usercert_file && SSL_CTX_use_certificate_chain_file(ctx, https_usercert_file) <= 0) - ssl_error( "SSL_CTX_use_certificate_chain_file" ); - if (https_userkey_file && SSL_CTX_use_PrivateKey_file(ctx, https_userkey_file, SSL_FILETYPE_PEM) <= 0) - ssl_error( "SSL_CTX_use_PrivateKey_file" ); - - if ((ssl = SSL_new(ctx)) == NULL) - ssl_error( "SSL_new()" ); - if (SSL_set_fd(ssl, s) == 0) - ssl_error( "SSL_set_fd()" ); - if ((result = SSL_connect(ssl)) <= 0) - ssl_error( "SSL_connect()" ); - - SSL_CTX_free (ctx); - - if (sendf(s,"CONNECT %s:%d HTTP/1.1\r\nHost: %s\r\n", dest_host, dest_port, dest_host) < 0) - return START_ERROR; - - if (proxy_auth_type == PROXY_AUTH_BASIC) - { - debug("Doing basic auth\n"); - if (basic_auth (s) < 0) - return START_ERROR; - } - - if (sendf(s,"\r\n") < 0) - return START_ERROR; - - /* get response */ - bzero(buf, sizeof(buf)); - bzero(sslbuf, sizeof(sslbuf)); - if (ssl_line_input(buf, sizeof(buf)) < 0) - { - debug("failed to read https response, retry.\n"); - - /* retry (needed to work around certificate based auth) */ - bzero(sslbuf, sizeof(sslbuf)); - if (ssl_line_input(buf, sizeof(buf)) < 0) - debug("failed to read https response.\n"); - } - - /* check status */ - if (!strchr(buf, ' ')) - { - error ("Unexpected https response: '%s'.\n", buf); - return START_ERROR; - } - result = atoi(strchr(buf,' ')); - - switch ( result ) { - case 200: - /* Conguraturation, connected via http proxy server! */ - debug("connected, start user session.\n"); - break; - case 302: /* redirect */ - do { - if (ssl_line_input(buf, sizeof(buf))) - break; - if (expect(buf, "Location: ")) { - relay_host = cut_token(buf, "//"); - cut_token(buf, "/"); - relay_port = atoi(cut_token(buf, ":")); - } - } while (strcmp(buf,"\r\n") != 0); - return START_RETRY; - - /* We handle both 401 and 407 codes here: 401 is WWW-Authenticate, which - * not strictly the correct response, but some proxies do send this (e.g. - * Symantec's Raptor firewall) */ - case 401: /* WWW-Auth required */ - case 407: /* Proxy-Auth required */ - /** NOTE: As easy implementation, we support only BASIC scheme - and ignore realm. */ - /* If proxy_auth_type is PROXY_AUTH_BASIC and get - this result code, authentication was failed. */ - if (proxy_auth_type != PROXY_AUTH_NONE) { - error("Authentication failed.\n"); - return START_FORBIDDEN; - } - auth_what = (result == 401) ? "WWW-Authenticate:" : "Proxy-Authenticate:"; - do { - if ( ssl_line_input(buf, sizeof(buf)) ) { - break; - } - downcase(buf); - if (expect(buf, auth_what)) { - /* parse type and realm */ - char *scheme, *realm; - scheme = cut_token(buf, " "); - realm = cut_token(scheme, " "); - if ( scheme == NULL || realm == NULL ) { - debug("Invalid format of %s field.\n", auth_what); - return START_ERROR; /* fail */ - } - /* check supported auth type */ - if (expect(scheme, "basic")) { - proxy_auth_type = PROXY_AUTH_BASIC; - } else { - debug("Unsupported authentication type: %s\n", scheme); - } - } - } while (strcmp(buf,"\r\n") != 0); - if ( proxy_auth_type == PROXY_AUTH_NONE ) { - debug("Can't find %s in response header.\n", auth_what); - return START_ERROR; - } else { - debug("got a 401/407 but going for the retry!\n"); - return START_RETRY; - } - - - default: - /* Not allowed */ - debug("https proxy is not allowed.\n"); - return START_FORBIDDEN; - } - /* skip to end of response header */ - do - { - if (ssl_line_input(buf, sizeof(buf))) - { - debug("Can't skip response headers\n"); - return START_ERROR; - } - } while ( strcmp(buf,"\r\n") != 0 ); - - return START_OK; -} - -/* begin relaying via TELNET proxy. - Sends string specified by telnet_command (-c option) with - replacing host name and port number to the socket. */ -int -begin_telnet_relay( SOCKET s ) -{ - char buf[1024]; - char *cmd; - char *good_phrase = "connected to"; - char *bad_phrase_list[] = { - " failed", " refused", " rejected", " closed" - }; - char sep = ' '; - int i; - - debug("begin_telnet_relay()\n"); - - /* report phrase */ - debug("good phrase: '%s'\n", good_phrase); - debug("bad phrases"); - sep = ':'; - for (i=0; i< (sizeof(bad_phrase_list) / sizeof(char*)); i++) { - debug_("%c '%s'", sep, bad_phrase_list[i]); - sep = ','; - } - debug_("\n"); - - /* make request string with replacing %h by destination hostname - and %p by port number, etc. */ - cmd = expand_host_and_port(telnet_command, dest_host, dest_port); - - /* Sorry, we send request string now without waiting a prompt. */ - if (sendf(s, "%s\r\n", cmd) < 0) { - free(cmd); - return START_ERROR; - } - free(cmd); - - /* Process answer from proxy until good or bad phrase is detected. We - assume that the good phrase should be appeared only in the final - line of proxy responses. Bad keywods in the line causes operation - fail. First checks a good phrase, then checks bad phrases. - If no match, continue reading line from proxy. */ - while (!line_input(s, buf, sizeof(buf)) && buf[0] != '\0') { - downcase(buf); - /* first, check good phrase */ - if (strstr(buf, good_phrase)) { - debug("good phrase is detected: '%s'\n", good_phrase); - return START_OK; - } - /* then, check bad phrase */ - for (i=0; i<(sizeof(bad_phrase_list)/sizeof(char*)); i++) { - if (strstr(buf, bad_phrase_list[i]) != NULL) { - debug("bad phrase is detected: '%s'\n", bad_phrase_list[i]); - return START_ERROR; - } - } - } - debug("error reading from telnet proxy\n"); - - return START_ERROR; -} - - -#ifdef _WIN32 -/* ddatalen() - Returns 1 if data is available, otherwise return 0 - */ -int -stdindatalen (void) -{ - DWORD len = 0; - struct stat st; - fstat( 0, &st ); - if ( st.st_mode & _S_IFIFO ) { - /* in case of PIPE */ - if ( !PeekNamedPipe( GetStdHandle(STD_INPUT_HANDLE), - NULL, 0, NULL, &len, NULL) ) { - if ( GetLastError() == ERROR_BROKEN_PIPE ) { - /* PIPE source is closed */ - /* read() will detects EOF */ - len = 1; - } else { - fatal("PeekNamedPipe() failed, errno=%d\n", - GetLastError()); - } - } - } else if ( st.st_mode & _S_IFREG ) { - /* in case of regular file (redirected) */ - len = 1; /* always data ready */ - } else if ( _kbhit() ) { - /* in case of console */ - len = 1; - } - return len; -} -#endif /* _WIN32 */ - -/* relay byte from stdin to socket and fro socket to stdout. - returns reason of termination */ -int -do_repeater( SOCKET local_in, SOCKET local_out, SOCKET remote ) -{ - /** vars for local input data **/ - char lbuf[4096]; /* local input buffer */ - int lbuf_len; /* available data in lbuf */ - int f_local; /* read local input more? */ - /** vars for remote input data **/ - char rbuf[4096]; /* remote input buffer */ - int rbuf_len; /* available data in rbuf */ - int f_remote; /* read remote input more? */ - int close_reason = REASON_UNK; /* reason of end repeating */ - /** other variables **/ - int nfds, len; - fd_set ifds, ofds; - struct timeval *tmo; -#ifdef _WIN32 - struct timeval win32_tmo; -#endif /* _WIN32 */ - - /* repeater between stdin/out and socket */ - nfds = ((local_in local */ - if ( FD_ISSET(remote, &ifds) && (rbuf_len < (int)sizeof(rbuf)) ) { - len = recv( remote, rbuf + rbuf_len, sizeof(rbuf)-rbuf_len, 0); - if ( len == 0 || (len == -1 && socket_errno() == ECONNRESET)) { - debug("connection %s by peer\n", - (len==0)? "closed": "reset"); - close_reason = REASON_CLOSED_BY_REMOTE; - f_remote = 0; /* no more read from socket */ - f_local = 0; - } else if ( len == -1 ) { - /* error */ - fatal("recv() failed, %d\n", socket_errno()); - } else { - debug("recv %d bytes\n", len); - if ( 1 < f_debug ) /* more verbose */ - report_bytes( "<<<", rbuf+rbuf_len, len); - rbuf_len += len; - } - } - - /* local => remote */ - if ( FD_ISSET(local_in, &ifds) && (lbuf_len < (int)sizeof(lbuf)) ) { - if (local_type == LOCAL_SOCKET) - len = recv(local_in, lbuf + lbuf_len, - sizeof(lbuf)-lbuf_len, 0); - else - len = read(local_in, lbuf + lbuf_len, sizeof(lbuf)-lbuf_len); - if ( len == 0 ) { - /* stdin is EOF */ - debug("local input is EOF\n"); - if (!f_hold_session) - shutdown(remote, 1); /* no-more writing */ - f_local = 0; - close_reason = REASON_CLOSED_BY_LOCAL; - } else if ( len == -1 ) { - /* error on reading from stdin */ - if (f_hold_session) { - debug ("failed to read from local\n"); - f_local = 0; - close_reason = REASON_CLOSED_BY_LOCAL; - } else - fatal("recv() failed, errno = %d\n", errno); - } else { - /* repeat */ - lbuf_len += len; - } - } - - /* flush data in buffer to socket */ - if ( 0 < lbuf_len ) { - len = send(remote, lbuf, lbuf_len, 0); - if ( len == -1 ) { - fatal("send() failed, %d\n", socket_errno()); - } else if ( 0 < len ) { - if ( 1 < f_debug ) /* more verbose */ - report_bytes( ">>>", lbuf, len); - /* move data on to top of buffer */ - debug("sent %d bytes\n", len); - lbuf_len -= len; - if ( 0 < lbuf_len ) - memcpy( lbuf, lbuf+len, lbuf_len ); - assert( 0 <= lbuf_len ); - } - } - - /* flush data in buffer to local output */ - if ( 0 < rbuf_len ) { - if (local_type == LOCAL_SOCKET) - len = send( local_out, rbuf, rbuf_len, 0); - else - len = write( local_out, rbuf, rbuf_len); - if ( len == -1 ) { - fatal("output (local) failed, errno=%d\n", errno); - } - rbuf_len -= len; - if ( len < rbuf_len ) - memcpy( rbuf, rbuf+len, rbuf_len ); - assert( 0 <= rbuf_len ); - } - if (f_local == 0 && f_hold_session) { - debug ("closing local port without disconnecting from remote\n"); - f_remote = 0; - shutdown (local_out, 2); - close (local_out); - break; - } - } - - return close_reason; -} - -/* relay byte from stdin to socket and fro socket to stdout. - returns reason of termination */ -int -do_ssl_repeater( SOCKET local_in, SOCKET local_out, SOCKET remote) -{ - /** vars for local input data **/ - char lbuf[4096]; /* local input buffer */ - int lbuf_len; /* available data in lbuf */ - int f_local; /* read local input more? */ - /** vars for remote input data **/ - char rbuf[4096]; /* remote input buffer */ - int rbuf_len; /* available data in rbuf */ - int f_remote; /* read remote input more? */ - int close_reason = REASON_UNK; /* reason of end repeating */ - /** other variables **/ - int nfds, len; - fd_set ifds, ofds; - struct timeval *tmo; -#ifdef _WIN32 - struct timeval win32_tmo; -#endif /* _WIN32 */ - - /* repeater between stdin/out and socket */ - nfds = ((local_in local */ - if ( FD_ISSET(remote, &ifds) && (rbuf_len < (int)sizeof(rbuf)) ) { - len = sslread(rbuf + rbuf_len, sizeof(rbuf)-rbuf_len); - if ( len == 0 || (len == -1 && socket_errno() == ECONNRESET)) { - debug("connection %s by peer\n", - (len==0)? "closed": "reset"); - close_reason = REASON_CLOSED_BY_REMOTE; - f_remote = 0; /* no more read from socket */ - f_local = 0; - } else if ( len == -1 ) { - /* error */ - fatal("recv() failed, %d\n", socket_errno()); - } else { - debug("recv %d bytes\n", len); - if ( 1 < f_debug ) /* more verbose */ - report_bytes( "<<<", rbuf+rbuf_len, len); - rbuf_len += len; - } - } - - /* local => remote */ - if ( FD_ISSET(local_in, &ifds) && (lbuf_len < (int)sizeof(lbuf)) ) { - if (local_type == LOCAL_SOCKET) - len = recv(local_in, lbuf + lbuf_len, - sizeof(lbuf)-lbuf_len, 0); - else - len = read(local_in, lbuf + lbuf_len, sizeof(lbuf)-lbuf_len); - if ( len == 0 ) { - /* stdin is EOF */ - debug("local input is EOF\n"); - if (!f_hold_session) - shutdown(remote, 1); /* no-more writing */ - f_local = 0; - close_reason = REASON_CLOSED_BY_LOCAL; - } else if ( len == -1 ) { - /* error on reading from stdin */ - if (f_hold_session) { - debug ("failed to read from local\n"); - f_local = 0; - close_reason = REASON_CLOSED_BY_LOCAL; - } else - fatal("recv() failed, errno = %d\n", errno); - } else { - /* repeat */ - lbuf_len += len; - } - } - - /* flush data in buffer to socket */ - if ( 0 < lbuf_len ) { - len = SSL_write(ssl, lbuf, lbuf_len); - if ( len == -1 ) { - fatal("send() failed, %d\n", socket_errno()); - } else if ( 0 < len ) { - if ( 1 < f_debug ) /* more verbose */ - report_bytes( ">>>", lbuf, len); - /* move data on to top of buffer */ - debug("sent %d bytes\n", len); - lbuf_len -= len; - if ( 0 < lbuf_len ) - memcpy( lbuf, lbuf+len, lbuf_len ); - assert( 0 <= lbuf_len ); - } - } - - /* flush data in buffer to local output */ - if ( 0 < rbuf_len ) { - if (local_type == LOCAL_SOCKET) - len = send( local_out, rbuf, rbuf_len, 0); - else - len = write( local_out, rbuf, rbuf_len); - if ( len == -1 ) { - fatal("output (local) failed, errno=%d\n", errno); - } - rbuf_len -= len; - if ( len < rbuf_len ) - memcpy( rbuf, rbuf+len, rbuf_len ); - assert( 0 <= rbuf_len ); - } - if (f_local == 0 && f_hold_session) { - debug ("closing local port without disconnecting from remote\n"); - f_remote = 0; - shutdown (local_out, 2); - close (local_out); - break; - } - } - - return close_reason; -} - -int -accept_connection (u_short port) -{ - static int sock = -1; - int connection; - struct sockaddr_in name; - struct sockaddr client; - unsigned int socklen; - fd_set ifds; - int nfds; - int sockopt; - - /* Create the socket. */ - debug("Creating source port to forward.\n"); - sock = socket (PF_INET, SOCK_STREAM, 0); - if (sock < 0) - fatal("socket() failed, errno=%d\n", socket_errno()); - sockopt = 1; - setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, - (void*)&sockopt, sizeof(sockopt)); - - /* Give the socket a name. */ - name.sin_family = AF_INET; - name.sin_port = htons (port); - name.sin_addr.s_addr = htonl (INADDR_ANY); - if (bind (sock, (struct sockaddr *) &name, sizeof (name)) < 0) - fatal ("bind() failed, errno=%d\n", socket_errno()); - - if (listen( sock, 1) < 0) - fatal ("listen() failed, errno=%d\n", socket_errno()); - - /* wait for new connection with watching EOF of stdin. */ - debug ("waiting new connection at port %d (socket=%d)\n", port, sock); - nfds = sock + 1; - do { - int n; - struct timeval *ptmo = NULL; -#ifdef _WIN32 - struct timeval tmo; - tmo.tv_sec = 0; - tmo.tv_usec = 100*1000; /* On Windows, 100ms timeout */ - ptmo = &tmo; -#endif /* _WIN32 */ - FD_ZERO (&ifds); - FD_SET ((SOCKET)sock, &ifds); -#ifndef _WIN32 - FD_SET (0, &ifds); /* watch stdin */ -#endif - n = select (nfds, &ifds, NULL, NULL, ptmo); - if (n == -1) { - fatal ("select() failed, %d\n", socket_errno()); - exit (1); - } -#ifdef _WIN32 - if (0 < stdindatalen()) { - FD_SET (0, &ifds); /* fake */ - n++; - } -#endif - if (0 < n) { - if (FD_ISSET(0, &ifds) && (getchar() <= 0)) { - /* EOF */ - debug ("Give-up waiting port because stdin is closed."); - exit(0); - } - if (FD_ISSET(sock, &ifds)) - break; /* socket is stimulated */ - } - } while (1); - socklen = sizeof(client); - connection = accept( sock, &client, &socklen); - if ( connection < 0 ) - fatal ("accept() failed, errno=%d\n", socket_errno()); - return connection; -} - - - -/** Main of program **/ -int -main( int argc, char **argv ) -{ - int ret; - int remote; /* socket */ - int local_in; /* Local input */ - int local_out; /* Local output */ - int reason; -#ifdef _WIN32 - WSADATA wsadata; - WSAStartup( 0x101, &wsadata); -#endif /* _WIN32 */ - - /* initialization */ - getarg( argc, argv ); - - /* Open local_in and local_out if forwarding a port */ - if ( local_type == LOCAL_SOCKET ) { - /* Relay between local port and destination */ - local_in = local_out = accept_connection( local_port ); - } else { - /* Relay between stdin/stdout and desteination */ - local_in = 0; - local_out = 1; -#ifdef _WIN32 - _setmode(local_in, O_BINARY); - _setmode(local_out, O_BINARY); -#endif - } - -retry: -#ifndef _WIN32 - if (0 < connect_timeout) - set_timeout (connect_timeout); -#endif /* not _WIN32 */ - - if (check_direct(dest_host)) - relay_method = METHOD_DIRECT; - /* make connection */ - if ( relay_method == METHOD_DIRECT ) { - remote = open_connection (dest_host, dest_port); - if ( remote == SOCKET_ERROR ) - fatal( "Unable to connect to destination host, errno=%d\n", - socket_errno()); - } else { - remote = open_connection (relay_host, relay_port); - if ( remote == SOCKET_ERROR ) - fatal( "Unable to connect to relay host, errno=%d\n", - socket_errno()); - } - - /** resolve destination host (SOCKS) **/ -#if !defined(_WIN32) && !defined(__CYGWIN32__) - if (socks_ns.sin_addr.s_addr != 0) - switch_ns (&socks_ns); -#endif /* not _WIN32 && not __CYGWIN32__ */ - if (relay_method == METHOD_SOCKS && - socks_resolve == RESOLVE_LOCAL && - local_resolve (dest_host, &dest_addr) < 0) { - fatal("Unknown host: %s", dest_host); - } - - /** relay negociation **/ - switch ( relay_method ) { - case METHOD_SOCKS: - if ( ((socks_version == 5) && (begin_socks5_relay(remote) < 0)) || - ((socks_version == 4) && (begin_socks4_relay(remote) < 0)) ) - fatal( "failed to begin relaying via SOCKS.\n"); - break; - - case METHOD_HTTP: - ret = begin_http_relay(remote); - switch (ret) { - case START_FORBIDDEN: - close (remote); - fatal("failed to begin relaying via HTTP (Forbidden).\n"); - case START_ERROR: - close (remote); - fatal("failed to begin relaying via HTTP.\n"); - case START_OK: - break; - case START_RETRY: - /* retry with authentication */ - close (remote); - goto retry; - } - break; - case METHOD_HTTPS: - ret = begin_https_relay(remote); - switch (ret) { - case START_FORBIDDEN: - close (remote); - fatal("failed to begin relaying via HTTPS (Forbidden).\n"); - case START_ERROR: - close (remote); - fatal("failed to begin relaying via HTTPS.\n"); - case START_OK: - break; - case START_RETRY: - /* retry with authentication */ - close (remote); - goto retry; - } - break; - case METHOD_TELNET: - if (begin_telnet_relay(remote) < 0) - fatal("failed to begin relaying via telnet.\n"); - break; - } - debug("connected\n"); - -#ifndef _WIN32 - if (0 < connect_timeout) - set_timeout (0); -#endif /* not _WIN32 */ - - /* main loop */ - debug ("start relaying.\n"); -do_repeater: - if (relay_method == METHOD_HTTPS) - { - reason = do_ssl_repeater(local_in, local_out, remote); - } - else - { - reason = do_repeater(local_in, local_out, remote); - } - debug ("relaying done.\n"); - if (local_type == LOCAL_SOCKET && reason == REASON_CLOSED_BY_LOCAL && f_hold_session) - { - /* re-wait at local port without closing remote session */ - debug ("re-waiting at local port %d\n", local_port); - local_in = local_out = accept_connection( local_port ); - debug ("re-start relaying\n"); - goto do_repeater; - } - closesocket(remote); - if ( local_type == LOCAL_SOCKET) - closesocket(local_in); - - debug ("that's all, bye.\n"); - - cleanup_and_exit( 0 ); - return 0; -} - -/* ------------------------------------------------------------ - Local Variables: - compile-command: "gcc -Wall -Wpedantic -o connect connect.c -lssl -lcrypto" - tab-width: 8 - fill-column: 74 - comment-column: 48 - End: - ------------------------------------------------------------ */ - -/*** end of connect.c ***/ diff --git a/connect.html b/connect.html deleted file mode 100644 index 53db26e..0000000 --- a/connect.html +++ /dev/null @@ -1,1142 +0,0 @@ - - - - - SSH Proxy Command -- connect.c - - - - - - - - -

SSH Proxy Command -- connect.c

- -

-connect.c is the simple relaying command to make network -connection via SOCKS and https proxy. It is mainly intended to -be used as proxy command of OpenSSH. You can make SSH session -beyond the firewall with this command, - -

- -

-Features of connect.c are: - -

- -
    -
  • Supports SOCKS (version 4/4a/5) and https CONNECT method. -
  • -
  • Supports NO-AUTH and USERPASS authentication of SOCKS -
  • -
  • Partially supports telnet proxy (experimental). -
  • -
  • You can input password from tty, ssh-askpass or - environment variable. -
  • -
  • Run on UNIX or Windows platform. -
  • -
  • You can compile with various C compiler (cc, gcc, Visual C, Borland C. etc.) -
  • -
  • Simple and general program independent from OpenSSH. -
  • -
  • You can also relay local socket stream instead of standard I/O. -
  • -
- -

-Download source code from: -http://www.taiyo.co.jp/~gotoh/ssh/connect.c -
-For windows user, pre-compiled binary is also available: -http://www.taiyo.co.jp/~gotoh/ssh/connect.exe (compiled with MSVC) - -

- -

Contents

-
-
-News -
-
-What is 'proxy command' -
-
-How to Use -
-
-
-
-Get Source -
-
-Compile and Install -
-
-Modify your ~/.ssh/config -
-
-Use SSH -
-
-Have trouble? -
-
-
-
-More Detail -
-
-Specifying user name via environment variables -
-
-Specifying password via environment variables -
-
-Limitations -
-
-
-
-SOCKS5 authentication -
-
-HTTP authentication -
-
-Switching proxy server -
-
-Telnet Proxy -
-
-
-
-Tips -
-
-
-
-Proxying socket connection -
-
-Use with ssh-askpass command -
-
-Use for Network Stream of Emacs -
-
-Remote resolver -
-
-Hopping Connection via SSH -
-
-
-
-Break The More Restricted Wall -
-
-F.Y.I. -
-
-
-
-Difference between SOCKS versions. -
-
-Configuration to use HTTPS -
-
-SOCKS5 Servers -
-
-Specifications -
-
-Related Links -
-
-Similars -
-
-
-
-hisotry -
-
- - -

News

-
-
2005-07-08
-
-Rev. 1.95. Buf fix for previous change. The bug causes the fail of - basic authentication. And also fixed bug of parameter file handling. - Thanks reporting, Johannes Schindelin . -
-
2005-07-07
-
-Rev. 1.94. Changed to use snprintf()/vsnprintf() for security issue - that gcc complained them on OpenBSD 3.7/x86. The features are not - changed. -
-
2005-03-04
-
-Updated compile option for Mac OS X. -
-
2005-02-21
-
-Rev.1.92. Removed assertions which has no mean and worse for windows - suggested by OZAWA Takahiro. -
-
2005-01-12
-
-Rev.1.90. Fixed not to cause seg-fault on accessing to non HTTP - port. This problem is reported by Jason Armstrong . -
-
2004-10-30
-
-Rev.1.89. Partial support for telnet proxy. - Thanks to Gregory Shimansky <gshimansky at mail dot ru>. - (Note: This is ad-hoc implementation, so it is not enough for - various type of telnet proxies. - And password interaction is not supported.) -
-
- -

What is 'proxy command'

- -

-OpenSSH development team decides to stop supporting SOCKS and any -other tunneling mechanism. It was aimed to separate complexity to -support various mechanism of proxying from core code. And they -recommends more flexible mechanism: ProxyCommand option -instead. - -

- -

-Proxy command mechanism is delegation of network stream -communication. If ProxyCommand options is specified, SSH -invoke specified external command and talk with standard I/O of thid -command. Invoked command undertakes network communication with -relaying to/from standard input/output including iniitial -communication or negotiation for proxying. Thus, ssh can split out -proxying code into external command. - -

- -

-The connect.c program was made for this purpose. - -

- -

How to Use

- -

Get Source

- -

-Download source code from here. -
-If you are MS Windows user, you can get pre-compiled binary from -here. - -

- -

Compile and Install

- -

-In most environment, you can compile connect.c simply. -On UNIX environment, you can use cc or gcc. -On Windows environment, you can use Microsoft Visual C, Borland C or Cygwin gcc. - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Compilercommand line to compile
UNIX cccc connect.c -o connect
UNIX gccgcc connect.c -o connect
Solarisgcc connect.c -o connect -lnsl -lsocket -lresolv
Microsoft Visual C/C++cl connect.c wsock32.lib advapi32.lib
Borland Cbcc32 connect.c wsock32.lib advapi32.lib
Cygwin gccgcc connect.c -o connect
Mac OS Xgcc connect.c -o connect -lresolv
or
gcc connect.c -o connect -DBIND_8_COMPAT=1
- -

-To install connect command, simply copy compiled binary to directory -in your PATH (ex. /usr/local/bin). Like this: - -

- -
-$ cp connect /usr/local/bin
-
- -

Modify your ~/.ssh/config

- -

-Modify your ~/.ssh/config file to use connect command as -proxy command. For the case of SOCKS server is running on -firewall host socks.local.net with port 1080, you can add -ProxyCommand option in ~/.ssh/config, like this: - -

- -
-Host remote.outside.net
-  ProxyCommand connect -S socks.local.net %h %p
-
- -

-%h and %p will be replaced on invoking proxy command with -target hostname and port specified to SSH command. - -

- -

-If you hate writing many entries of remote hosts, following example -may help you. - -

- -
-## Inside of the firewall, use connect command with direct connection.
-Host *.local.net
-  ProxyCommand connect %h %p
-
-## Outside of the firewall, use connect command with SOCKS conenction.
-Host *
-  ProxyCommand connect -S socks.local.net %h %p
-
- -

-If you want to use http proxy, use -H option instead of -S -option in examle above, like this: - -

- -
-## Inside of the firewall, direct
-Host *.local.net
-  ProxyCommand connect %h %p
-
-## Outside of the firewall, with HTTP proxy
-Host *
-  ProxyCommand connect -H proxy.local.net:8080 %h %p
-
- -

Use SSH

- -

-After editing your ~/.ssh/config file, you are ready to use ssh. -You can execute ssh without any special options as if remote host is -IP reachable host. Following is an example to execute hostname -command on host remote.outside.net. - -

- -
-$ ssh remote.outside.net hostname
-remote.outside.net
-$
-
- -

Have trouble?

- -

-If you have trouble, execute connect command from command line -with -d option to see what is happened. Some debug message may -appear and reports progress. This information may tell you what is -wrong. In this example, error has occurred on authentication stage of -SOCKS5 protocol. - -

- -
-$ connect -d -S socks.local.net unknown.remote.outside.net 110
-DEBUG: relay_method = SOCKS (2)
-DEBUG: relay_host=socks.local.net
-DEBUG: relay_port=1080
-DEBUG: relay_user=gotoh
-DEBUG: socks_version=5
-DEBUG: socks_resolve=REMOTE (2)
-DEBUG: local_type=stdio
-DEBUG: dest_host=unknown.remote.outside.net
-DEBUG: dest_port=110
-DEBUG: Program is $Revision: 1.1 $
-DEBUG: connecting to xxx.xxx.xxx.xxx:1080
-DEBUG: begin_socks_relay()
-DEBUG: atomic_out()  [4 bytes]
-DEBUG: >>> 05 02 00 02
-DEBUG: atomic_in() [2 bytes]
-DEBUG: <<< 05 02
-DEBUG: auth method: USERPASS
-DEBUG: atomic_out()  [some bytes]
-DEBUG: >>> xx xx xx xx ...
-DEBUG: atomic_in() [2 bytes]
-DEBUG: <<< 01 01
-ERROR: Authentication faield.
-FATAL: failed to begin relaying via SOCKS.
-
- -

More Detail

- -

-Command line usage is here: - -

- -
-usage:  connect [-dnhst45] [-R resolve] [-p local-port] [-w sec]
-		[-H [user@]proxy-server[:port]]
-		[-S [user@]socks-server[:port]]
-		[-T socks-server:[port]]
-                [-c telnet-proxy-command]
-		host port
-
- -

-host and port is target hostname and port-number to connect. - -

- -

--H option specify hostname and port number of http proxy server to -relay. If port is omitted, 80 is used. You can specify this value by -environment variable HTTP_PROXY and give -h option to use it. - -

- -

--S option specify hostname and port number of SOCKS server to -relay. Like -H option, port number can be omit and default is 1080. -You can also specify this value pair by environment variable -SOCKS5_SERVER and give -s option to use it. - -

- -

--T option specify hostname and port number of telnet proxy to -relay. The port number can be omit and default is 23. -You can also specify this value pair by environment variable -TELNET_PROXY and give -t option to use it. - -

- -

--4 and -5 is for specifying SOCKS protocol version. It is -valid only using with -s or -S. Default is -5 -(protocol version 5) - -

- -

--R is for specifying method to resolve hostname. 3 keywords -(local, remote, both) or dot-notation IP address is -allowed. Keyword both means; "Try local first, then -remote". If dot-notation IP address is specified, use this host as -nameserver (UNIX only). Default is remote for SOCKS5 or local -for others. On SOCKS4 protocol, remote resolving method (remote -and both) use protocol version 4a. - -

- -

-The -p option specifys to wait a local TCP port and make relaying -with it instead of standard input and output. - -

- -

-The -w option specifys timeout seconds on making connection with -target host. - -

- -

-The -c option specifys request string against telnet -proxy server. The special word '%h' and '%p' in this string are replaced -as hostname and port number before sending. -For telnet proxy by DeleGate, both "telnet %h %p" and "%h:%p" -are acceptable. -Default is "telnet %h %p". - -

- -

-The -a option specifiys user intended authentication methods -separated by comma. Currently userpass and none are -supported. Default is userpass. You can also specifying this -parameter by the environment variable SOCKS5_AUTH. - -

- -

-The -d option is used for debug. If you fail to connect, use this -and check request to and response from server. - -

- -

-You can omit port argument when program name is special format -containing port number itself. For example, - -

- -
-$ ln -s connect connect-25
-$ ./connect-25 smtphost.outside.net
-220 smtphost.outside.net ESMTP Sendmail
-QUIT
-221 2.0.0 smtphost.remote.net closing connection
-$
-
- -

-This example means that the command name "connect-25" contains port number -25 so you can omit 2nd argument (and used if specified explicitly). - -

- -

Specifying user name via environment variables

- -

-There are 5 environemnt variables to specify -user name without command line option. This mechanism is usefull -for the user who using another user name different from system account. - -

- -
-
SOCKS5_USER
-
-Used for SOCKS v5 access. -
-
SOCKS4_USER
-
-Used for SOCKS v4 access. -
-
SOCKS_USER
-
-Used for SOCKS v5 or v4 access and varaibles above are not defined. -
-
HTTP_PROXY_USER
-
-Used for HTTP proxy access. -
-
CONNECT_USER
-
-Used for all type of access if all above are not defined. -
-
- -

-Following table describes how user name is determined. -Left most number is order to check. If variable is not defined, -check next variable, and so on. - -

- - - - - - - -
SOCKS v5SOCKS v4HTTP proxy
1SOCKS5_USERSOCKS4_USERHTTP_PROXY_USER
2SOCKS_USER
3CONNECT_USER
4(query user name to system)
- -

Specifying password via environment variables

- -

-There are 5 environemnt variables to specify -password. If you use this feature, please note that it is -not secure way. - -

- -
-
SOCKS5_PASSWD
-
-Used for SOCKS v5 access. This variables is compatible - with NEC SOCKS implementation. -
-
SOCKS5_PASSWORD
-
-Used for SOCKS v5 access if SOCKS5_PASSWD is not defined. -
-
SOCKS_PASSWORD
-
-Used for SOCKS v5 (or v4) access all above is not defined. -
-
HTTP_PROXY_PASSWORD
-
-Used for HTTP proxy access. -
-
CONNECT_PASSWORD
-
-Used for all type of access if all above are not defined. -
-
- -

-Following table describes how password is determined. -Left most number is order to check. If variable is not defined, -check next variable, and so on. Finally ask to user interactively -using external program or tty input. - -

- - - - - - - -
SOCKS v5HTTP proxy
1SOCKS5_PASSWDHTTP_PROXY_PASSWORD
2SOCKS_PASSWORD
3CONNECT_PASSWORD
4(ask to user interactively)
- -

Limitations

- -

SOCKS5 authentication

- -

-Only NO-AUTH and USER/PASSWORD authentications are supported. -GSSAPI authentication (RFC 1961) and other draft authentications (CHAP, -EAP, MAF, etc.) is not supported. - -

- -

HTTP authentication

- -

-BASIC authentication is supported but DIGEST authentication is not. - -

- -

Switching proxy server

- -

-There is no mechanism to switch proxy server regarding to PC environment. -This limitation might be bad news for mobile user. -Since I do not want to make this program complex, I do not want to -support although this feature is already requested. Please advice me -if there is good idea of detecting environment to swich and simple way -to specify conditioned directive of servers. - -

- -

-One tricky workaround exists. It is replacing ~/.ssh/config file -by script on ppp up/down. - -

- -

-There's another example of wrapper script (contributed by Darren Tucker). -This script costs executing ifconfig and grep to detect -current environment, but it works. (NOTE: you should modify addresses -if you use it.) - -

- -
-#!/bin/sh
-## ~/bin/myconnect --- Proxy server switching wrapper
-
-if ifconfig eth0 |grep "inet addr:192\.168\.1" >/dev/null; then
-	opts="-S 192.168.1.1:1080"  
-elif ifconfig eth0 |grep "inet addr:10\." >/dev/null; then
-	opts="-H 10.1.1.1:80"
-else
-	opts="-s"
-fi
-exec /usr/local/bin/connect $opts $@
-
- -

Telnet Proxy

- -

-At first, note that the telnet proxy support is an partial feature. -In this implementation, connect single requestinting and proxy -returns some success/error detective in talked back lines including -greeting, prompt and connected messages. - -

- -

-The connect simply send request after connection to proxy is -established before any response reading, then repeat reading response -strings from proxy to decide remote connection request is succeeded or -not by checking pre-defined phrase in each lines. There are -pre-defined phrases which are good-phrase and bad-phrases. First -good-phrase is checked and change state as relaying if exist. -connect treat this line as final response from proxy before -starting acutal communication with remote host. Or if good-phrase is -not matched, bad-phrases will be checked. If one of bad-phrase -matched, it cause connection error immediately. - -

- -

-The pre-defined phrases are currently fixed string so you cannot -change without modifying and compiling. The good-phrase is: -"connected to". The bad-phrases are: " failed", " refused", " -rejected", " closed". - -

- -

Tips

- -

Proxying socket connection

- -

-In usual, connect.c relays network connection to/from standard -input/output. By specifying -p option, however, connect.c -relays local network stream instead of standard input/output. -With this option, connect command waits connection -from other program, then start relaying between both network stream. - -

- -

-This feature may be useful for the program which is hard to SOCKSify. - -

- -

Use with ssh-askpass command

- -

-connect.c ask you password when authentication is required. If -you are using on tty/pty terminal, connect can input from terminal -with prompt. But you can also use ssh-askpass program to input -password. If you are graphical environment like X Window or MS -Windows, and program does not have tty/pty, and environment variable -SSH_ASKPASS is specified, then connect.c invoke command -specified by environment variable SSH_ASKPASS to input password. -ssh-askpass program might be installed if you are using OpenSSH on -UNIX environment. On Windows environment, pre-compiled binary is -available from -here. - -

- -

-This feature is limited on window system environment. - -

- -

-And also useful on Emacs on MS Windows (NT Emacs or Meadow). It is -hard to send passphrase to connect command (and also ssh) -because external command is invoked on hidden terminal and do I/O with -this terminal. Using ssh-askpass avoids this problem. - -

- -

Use for Network Stream of Emacs

- -

-Although connect.c is made for OpenSSH, it is generic and -independent from OpenSSH. So we can use this for other purpose. For -example, you can use this command in Emacs to open network connection -with remote host over the firewall via SOCKS or HTTP proxy without -SOCKSifying Emacs itself. - -

- -

-There is sample code: -http://www.taiyo.co.jp/~gotoh/lisp/relay.el - -

- -

-With this code, you can use relay-open-network-stream function -instead of open-network-stream to make network connection. See top -comments of source for more detail. - -

- -

Remote resolver

- -

-If you are SOCKS4 user on UNIX environment, you might want specify -nameserver to resolve remote hostname. You can do it specifying --R option followed by IP address of resolver. - -

- -

Hopping Connection via SSH

- -

-Conbination of ssh and connect command have more interesting usage. -Following command makes indirect connection to host2:port from your -current host via host1. - -

- -
-ssh host1 connect host2 port
-
- -

-This method is useful for the situations like: - -

- -
    -
  • You are outside of organizasion now, but you want to access an - internal host barriered by firewall. -
  • -
  • You want to use some service which is allowed only from some - limited hosts. -
  • -
- -

-For example, I want to use local NetNews service in my office -from home. I cannot make NNTP session directly because NNTP host is -barriered by firewall. Fortunately, I have ssh account on internal -host and allowed using SOCKS5 on firewall from outside. So I use -following command to connect to NNTP service. - -

- -
-$ ssh host1 connect news 119
-200 news.my-office.com InterNetNews NNRP server INN 2.3.2 ready (posting ok).
-quit
-205 .
-$
-
- -

-By combinating hopping connection and relay.el, I can read NetNews -using Wanderlust on Emacs at home. - -

- -
-                        |
-    External (internet) | Internal (office)
-                        |
-+------+           +----------+          +-------+           +-----------+
-| HOME |           | firewall |          | host1 |           | NNTP host |
-+------+           +----------+          +-------+           +-----------+
- emacs <-------------- ssh ---------------> sshd <-- connect --> nntpd
-       <-- connect --> socksd <-- SOCKS -->
-
- -

-As an advanced example, you can use SSH hopping as fetchmail's plug-in -program to access via secure tunnel. This method requires that -connect program is insatalled on remote host. There's example -of .fetchmailrc bellow. When fetchmail access to mail-server, you will -login to remote host using SSH then execute connect program on -remote host to relay conversation with pop server. Thus fetchmail can -retrieve mails in secure. - -

- -
-poll mail-server
-  protocol pop3
-  plugin "ssh %h connect localhost %p"
-  username "username"
-  password "password"
-</exmaple>
-
-* <a name="sec23" id="sec23"></a>Break The More Restricted Wall
-
-If firewall does not provide SOCKS nor HTTPS other than port 443, you
-cannot break the wall in usual way.  But if you have you own host
-which is accessible from internet, you can make ssh connection to your
-own host by configuring sshd as waiting at port 443 instead of
-standard 22. By this, you can login to your own host via port 443.
-Once you have logged-in to extenal home machine, you can execute
-**connect** as second hop to make connection from your own host to
-final target host, like this:
-
-<example>
-$ cat ~/.ssh/config
-Host home
-  ProxyCommand connect -H firewall:8080 %h 443
-
-Host server
-  ProxyCommand ssh home connect %h %p
-...
-internal$ ssh home
-You are logged in to home!
-home# exit
-internal$ ssh server
-You are logged in to server!
-server# exit
-internal$
-
- -

-This way is similar to "Hopping connection via SSH" except configuring -outer sshd as waiting at port 443 (https). This means that you have a -capability to break the strongly restricted wall if you have own host -out side of the wall. - -

- -
-                        |
-      Internal (office) | External (internet)
-                        |
-+--------+         +----------+                 +------+          +--------+
-| office |         | firewall |                 | home |          | server |
-+--------+         +----------+                 +------+          +--------+
-   <------------------ ssh --------------------->sshd:443
-    <-- connect --> http-proxy <-- https:443 -->                      any
-                                                 connect <-- tcp -->  port
-
- -

-NOTE: If you wanna use this, you should give up hosting https service -at port 443 on you external host 'home'. - -

- -

F.Y.I.

- -

Difference between SOCKS versions.

- -

-SOCKS version 4 is first popular implementation which is documented -here. Since -this protocol provide IP address based requesting, client program -should resolve name of outer host by itself. Version 4a (documented -here) is -enhanced to allow request by hostname instead of IP address. - -

- -

-SOCKS version 5 is re-designed protocol stands on experience of -version 4 and 4a. There is no compativility with previous -versions. Instead, there's some improvement: IPv6 support, request by -hostname, UDP proxying, etc. - -

- -

Configuration to use HTTPS

- -

-Many http proxy servers implementation supports https CONNECT method -(SLL). You might add configuration to allow using https. For the -example of DeleGate ( -DeleGate is a multi-purpose application level gateway, or a proxy -server) , you should add https to REMITTABLE parameter to -allow HTTP-Proxy like this: - -

- -
-delegated -Pxxxx ...... REMITTABLE='+,https' ...
-
- -

-For the case of Squid, you should allow target ports via https by ACL, -and so on. - -

- -

SOCKS5 Servers

- -
-
NEC SOCKS Reference Implementation
-
-Reference implementation of SOKCS server and library. -
-
Dante
-
-Dante is free implementation of SOKCS server and library. - Many enhancements and modulalized. -
-
DeleGate
-
-DeleGate is multi function proxy service provider. - DeleGate 5.x.x or earlier can be SOCKS4 server, - and 6.x.x can be SOCKS5 and SOCKS4 server. - and 7.7.0 or later can be SOCKS5 and SOCKS4a server. -
-
- -

Specifications

- -
-
socks4.protocol.txt
-
-SOCKS: A protocol for TCP proxy across firewalls -
-
socks4a.protocol.txt
-
-SOCKS 4A: A Simple Extension to SOCKS 4 Protocol -
-
RFC 1928
-
-SOCKS Protocol Version 5 -
-
RFC 1929
-
-Username/Password Authentication for SOCKS V5 -
-
RFC 2616
-
-Hypertext Transfer Protocol -- HTTP/1.1 -
-
RFC 2617
-
-HTTP Authentication: Basic and Digest Access Authentication -
-
- -

Related Links

- - - -

Similars

- -
    -
  • Proxy Tunnel -- Proxying command using https CONNECT. -
  • -
  • stunnel -- Proxy through an https tunnel (Perl script) -
  • -
- -

hisotry

- -
-
2004-07-21
-
-Rev.1.84. Fixed some typo. -
-
2004-05-18
-
-Rev.1.83. Fixed problem not work on Solaris. -
-
2004-04-27
-
-Rev.1.82. Bug fix of memory clear on http proxying. -
-
2004-04-22
-
-Rev. 1.81. Fixed memory violation and memory leak bug. New environment - variable SOCKS5_PASSWD for sharing value with NEC SOCKS implementation. - And document (this page) is updated. -
-
2004-03-30
-
-Rev. 1.76. Fixed to accept multiple 'Proxy-Authorization' response. -
-
2003-01-07
-
-Rev. 1.68. Fixed a trouble around timeout support. -
-
2002-11-21
-
-Rev. 1.64 supports reading parameters from file /etc/connectrc or - ~/.connectrc instead of specifying via environment variables. For - examle, you can use this feature to switch setting by replacing file - when network environment is changed. And added SOCKS_DIRECT, - SOCKS5_DIRECT, SOCKS4_DIRECT, HTTP_DIRECT, SOCKS5_AUTH, environment - parameters. (Thanks Masatoshi TSUCHIYA) -
-
2002-11-20
-
-Rev. 1.63 supports some old proxies which make response 401 with - WWW-Authenticate: header. And fixed to use username specified in - proxy host by -H option correctly. (contributed from Des Herriott, thanks) -
-
2002-10-14
-
-Rev. 1.61 with New option -w for specifying connection timeout. - Currently, it works on UNIX only. (contributed from Darren Tucker, thanks) -
-
2002-09-29
-
-Add sample script for switching proxy server - advised from Darren Tucker, thanks. -
-
2002-08-27
-
-connect.c is updataed to rev. 1.60. -
-
2002-04-08
-
-Updated "Using OpenSSH through a SOCKS compatible PROXY on your LAN" written by J. Grant. (version 0.8) -
-
2002-02-20
-
-Add link of new document "Using OpenSSH through a SOCKS compatible PROXY on your LAN" - written by J. Grant. -
-
2002-01-31
-
-Rev. 1.53 -- On Win32 and with MSVC, handle password - input from console correctly. -
-
2002-01-30
-
-Rev. 1.50 -- [Security Fix] Do not print secure info in debug mode. -
-
2002-01-09
-
-Web page was made. - connect.c is rev. 1.48. -
-
-
- - - - - diff --git a/sources b/sources index e69de29..1d448f2 100644 --- a/sources +++ b/sources @@ -0,0 +1 @@ +SHA512 (ssh-connect-1.105.tar.gz) = f49001043a8ffbda3823d0ea3640cc85536ce3d23302fd4d704d8a520f99271a2ed66fda11a5402382edc1dcf874988339ebcaf1d6335249a8dd2a8b4ae965a3 From be58325f86a2e654ff2f9323d8e05a99185dd68e Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Wed, 17 Jul 2024 19:54:31 +0000 Subject: [PATCH 42/45] Rebuilt for https://fedoraproject.org/wiki/Fedora_41_Mass_Rebuild --- connect-proxy.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/connect-proxy.spec b/connect-proxy.spec index cfc1ad8..50ccadf 100644 --- a/connect-proxy.spec +++ b/connect-proxy.spec @@ -1,6 +1,6 @@ Name: connect-proxy Version: 1.105 -Release: 1%{?dist} +Release: 2%{?dist} Summary: SSH Proxy command helper License: GPLv2+ @@ -50,6 +50,9 @@ cp -p %{SOURCE1} $RPM_BUILD_ROOT%{_mandir}/man1/ %{_bindir}/%{name} %changelog +* Wed Jul 17 2024 Fedora Release Engineering - 1.105-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_41_Mass_Rebuild + * Fri May 17 2024 Timotheus Pokorra - 1.105-1 - Update to upstream 1.105 From 363e2a0088d439cb322c53135ef7d5c1bb37fcf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20Such=C3=BD?= Date: Thu, 25 Jul 2024 23:21:34 +0200 Subject: [PATCH 43/45] convert GPLv2+ license to SPDX This is part of https://fedoraproject.org/wiki/Changes/SPDX_Licenses_Phase_4 --- connect-proxy.spec | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/connect-proxy.spec b/connect-proxy.spec index 50ccadf..0857f52 100644 --- a/connect-proxy.spec +++ b/connect-proxy.spec @@ -1,9 +1,10 @@ Name: connect-proxy Version: 1.105 -Release: 2%{?dist} +Release: 3%{?dist} Summary: SSH Proxy command helper -License: GPLv2+ +# Automatically converted from old format: GPLv2+ - review is highly recommended. +License: GPL-2.0-or-later URL: http://www.taiyo.co.jp/~gotoh/ssh/connect.html Source0: ssh-connect-%{version}.tar.gz # Real source listed below, it was renamed for sanity's sake @@ -50,6 +51,9 @@ cp -p %{SOURCE1} $RPM_BUILD_ROOT%{_mandir}/man1/ %{_bindir}/%{name} %changelog +* Thu Jul 25 2024 Miroslav Suchý - 1.105-3 +- convert license to SPDX + * Wed Jul 17 2024 Fedora Release Engineering - 1.105-2 - Rebuilt for https://fedoraproject.org/wiki/Fedora_41_Mass_Rebuild From 011dd993e6efbf900f83856074707528a3e96187 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Thu, 16 Jan 2025 14:25:27 +0000 Subject: [PATCH 44/45] Rebuilt for https://fedoraproject.org/wiki/Fedora_42_Mass_Rebuild --- connect-proxy.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/connect-proxy.spec b/connect-proxy.spec index 0857f52..89d4ae0 100644 --- a/connect-proxy.spec +++ b/connect-proxy.spec @@ -1,6 +1,6 @@ Name: connect-proxy Version: 1.105 -Release: 3%{?dist} +Release: 4%{?dist} Summary: SSH Proxy command helper # Automatically converted from old format: GPLv2+ - review is highly recommended. @@ -51,6 +51,9 @@ cp -p %{SOURCE1} $RPM_BUILD_ROOT%{_mandir}/man1/ %{_bindir}/%{name} %changelog +* Thu Jan 16 2025 Fedora Release Engineering - 1.105-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_42_Mass_Rebuild + * Thu Jul 25 2024 Miroslav Suchý - 1.105-3 - convert license to SPDX From a6dfeeaaf41e0b2c1bc99a6fb5abea9a48e3d939 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Wed, 23 Jul 2025 18:37:18 +0000 Subject: [PATCH 45/45] Rebuilt for https://fedoraproject.org/wiki/Fedora_43_Mass_Rebuild --- connect-proxy.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/connect-proxy.spec b/connect-proxy.spec index 89d4ae0..57ec0c3 100644 --- a/connect-proxy.spec +++ b/connect-proxy.spec @@ -1,6 +1,6 @@ Name: connect-proxy Version: 1.105 -Release: 4%{?dist} +Release: 5%{?dist} Summary: SSH Proxy command helper # Automatically converted from old format: GPLv2+ - review is highly recommended. @@ -51,6 +51,9 @@ cp -p %{SOURCE1} $RPM_BUILD_ROOT%{_mandir}/man1/ %{_bindir}/%{name} %changelog +* Wed Jul 23 2025 Fedora Release Engineering - 1.105-5 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_43_Mass_Rebuild + * Thu Jan 16 2025 Fedora Release Engineering - 1.105-4 - Rebuilt for https://fedoraproject.org/wiki/Fedora_42_Mass_Rebuild