httpproxy.subr revision 252022
1if [ ! "$_MEDIA_HTTPPROXY_SUBR" ]; then _MEDIA_HTTPPROXY_SUBR=1
2#
3# Copyright (c) 2012-2013 Devin Teske
4# All Rights Reserved.
5#
6# Redistribution and use in source and binary forms, with or without
7# modification, are permitted provided that the following conditions
8# are met:
9# 1. Redistributions of source code must retain the above copyright
10#    notice, this list of conditions and the following disclaimer.
11# 2. Redistributions in binary form must reproduce the above copyright
12#    notice, this list of conditions and the following disclaimer in the
13#    documentation and/or other materials provided with the distribution.
14#
15# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, THE
17# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20# DAMAGES (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25# SUCH DAMAGE.
26#
27# $FreeBSD: head/usr.sbin/bsdconfig/share/media/httpproxy.subr 252022 2013-06-20 07:29:42Z dteske $
28#
29############################################################ INCLUDES
30
31BSDCFG_SHARE="/usr/share/bsdconfig"
32. $BSDCFG_SHARE/common.subr || exit 1
33f_dprintf "%s: loading includes..." media/httpproxy.subr
34f_include $BSDCFG_SHARE/dialog.subr
35f_include $BSDCFG_SHARE/variable.subr
36f_include $BSDCFG_SHARE/media/ftp.subr
37f_include $BSDCFG_SHARE/media/tcpip.subr
38
39BSDCFG_LIBE="/usr/libexec/bsdconfig"
40f_include_lang $BSDCFG_LIBE/include/messages.subr
41
42############################################################ FUNCTIONS
43
44# f_media_set_http_proxy
45#
46# Return success if we both found and set the media type to be an ftp server,
47# accessed via http proxy.
48#
49# Variables from variable.subr that can be used to script user input:
50#
51# 	VAR_HTTP_PROXY
52# 		HTTP Proxy server to use. Valid examples include:
53# 			myhost
54# 			somename:3128
55# 			192.168.2.3
56# 			[::1]:8080
57# 		The default port if not specified is 3128.
58#
59# Variables from variable.subr that are set after successful execution include
60# the following:
61#
62# 	VAR_HTTP_PROXY_HOST	The host portion of VAR_HTTP_PROXY.
63# 	VAR_HTTP_PROXY_PORT	The TCP port parsed from VAR_HTTP_PROXY.
64#
65# See also f_media_set_ftp() for additional variables.
66#
67f_media_set_http_proxy()
68{
69	FTP_SKIP_RESOLV=1 f_media_set_ftp || return $FAILURE
70
71	f_variable_get_value $VAR_HTTP_PROXY \
72		"$msg_please_enter_the_address_of_the_http_proxy"
73
74	local proxy
75	f_getvar $VAR_HTTP_PROXY proxy
76	[ "$proxy" ] || return $FAILURE
77
78	local hostname="$proxy" port=3128
79	case "$hostname" in
80	"["*"]")
81		hostname="${hostname#\[}"
82		hostname="${hostname%\]}"
83		;;
84	"["*"]:"*)
85		hostname="${hostname#\[}"
86		port="${hostname#*\]:}"
87		port="${port%%[!0-9]*}"
88		hostname="${hostname%%\]:*}"
89		;;
90	*":"*)
91		port="${hostname#*:}"
92		hostname="${hostname%%:*}"
93	esac
94
95	setvar $VAR_HTTP_PROXY_HOST "$hostname"
96	setvar $VAR_HTTP_PROXY_PORT "$port"
97
98	if f_debugging; then
99		f_dprintf "VAR_FTP_PATH : %s" "$( f_getvar $VAR_FTP_PATH )"
100		f_dprintf "VAR_HTTP_PROXY_HOST, _PORT: %s:%s" \
101		          "$( f_getvar $VAR_HTTP_PROXY_HOST )" \
102		          "$( f_getvar $VAR_HTTP_PROXY_PORT )"
103	fi
104
105	# media device has been set by f_media_set_ftp(), overwrite partly:
106	device_media set   type     $DEVICE_TYPE_HTTP_PROXY
107	device_media set   init     f_media_init_http_proxy
108	device_media set   get      f_media_get_http_proxy
109	device_media unset shutdown
110
111	return $SUCCESS
112}
113
114# f_http_proxy_check_access [$connect_only]
115#
116# Return success if able list a remote FTP directory via HTTP proxy. If
117# $connect_only is present and non-null, then returns success if a connection
118# can be made. Variables from variable.subr that can be used to script user
119# input:
120#
121# 	VAR_HTTP_PROXY_HOST
122# 		The HTTP proxy server host name, IPv4 address or IPv6 address.
123# 		Valid examples include:
124# 			myhost
125# 			192.168.2.3
126# 			::1
127# 	VAR_HTTP_PROXY_PORT
128# 		The TCP port to connect to when communicating with the HTTP
129# 		proxy server.
130# 	VAR_HTTP_PROXY_PATH
131# 		The FTP URL sent to the HTTP proxy server. Unused if
132# 		$connect_only is present and non-NULL.
133#
134f_http_proxy_check_access()
135{
136	local connect_only="$1" hosts=""
137
138	local proxy_host proxy_port
139	f_getvar $VAR_HTTP_PROXY_HOST proxy_host
140	f_getvar $VAR_HTTP_PROXY_PORT proxy_port
141
142	if ! {
143		f_validate_ipaddr "$proxy_host" ||
144		f_validate_ipaddr6 "$proxy_host" ||
145		{
146		  f_dprintf "%s: Looking up hostname, %s, using host(1)" \
147		            "f_http_proxy_check_access" "$proxy_host"
148		  f_host_lookup "$proxy_host" hosts
149		}
150	}; then
151		# All the above validations failed
152		[ "$hosts" ] && f_dialog_msgbox "$hosts"
153		unset $VAR_HTTP_PROXY_HOST
154		return $FAILURE
155	elif [ ! "$hosts" ]; then
156		# One of the first two validations passed
157		hosts="$proxy_host"
158	fi
159
160	local host connected=
161	for host in $hosts; do
162		f_quietly nc -nz "$host" "$proxy_port" || continue
163		connected=1; break
164	done
165	if [ ! "$connected" ]; then
166		f_show_msg "$msg_couldnt_connect_to_proxy %s:%s" \
167		           "$proxy_host" "$proxy_port"
168		unset $VAR_HTTP_PROXY_HOST
169		return $FAILURE
170	fi
171	[ "$connect_only" ] && return $SUCCESS
172
173	#
174	# Some proxies fetch files with certain extensions in "ascii mode"
175	# instead of "binary mode" for FTP. The FTP server then translates all
176	# LF to CRLF.
177	#
178	# You can force Squid to use binary mode by appending ";type=i" to the
179	# URL, which is what sysinstall(8) has traditionally done.
180	#
181
182	local proxy_path
183	f_getvar $VAR_HTTP_PROXY_PATH proxy_path
184	f_show_info "$msg_checking_access_to" "$proxy_path"
185
186	local rx
187	if ! rx=$(
188		printf "GET %s/ HTTP/1.0\r\n\r\n" "${proxy_path%/}" |
189			nc -n "$host" "$proxy_port"
190	); then
191		f_show_msg "$msg_couldnt_connect_to_proxy %s:%s" \
192		           "$proxy_host" "$proxy_port"
193		unset $VAR_HTTP_PROXY_HOST
194		return $FAILURE
195	fi
196
197	local hdr
198	hdr=$( echo "$rx" | awk '/^\r$/{exit}{print}' )
199
200	local http_found=$FAILURE
201	if echo "$hdr" | awk '
202		BEGIN { found = 0 }
203		/^HTTP.... 200 / {
204			found = 1
205			exit
206		}
207		END { exit ! found }
208	'; then
209		http_found=$SUCCESS
210	fi
211
212	#
213	# Scan the headers of the response
214	# this is extremely quick'n dity
215	#
216
217	unset $VAR_HTTP_FTP_MODE
218	if echo "$hdr" | awk '
219		BEGIN { found = 0 }
220		{
221			if (!match($0, /^Server: /)) next
222			found = ( substr($0, 9, 5) ~ /[Ss]quid/ )
223		}
224		END { exit ! found }
225	'; then
226		setvar $VAR_HTTP_FTP_MODE ";type=i"
227	else
228		setvar $VAR_HTTP_FTP_MODE ""
229	fi
230
231	return $http_found
232}
233
234# f_media_init_http_proxy $device
235#
236# Initializes the HTTP Proxy media device. Returns success if able to confirm
237# the existence of at least one known FTP server release path via HTTP proxy
238# using f_http_proxy_check_access(), above.
239#
240# Variables from variable.subr that can be used to script user input:
241#
242# 	VAR_HTTP_PROXY_HOST
243#		The HTTP proxy server to connect to. Usually set by having
244# 		f_media_set_http_proxy() parse VAR_HTTP_PROXY. Must be set.
245# 		Also see f_http_proxy_check_access() for additional variables.
246# 	VAR_RELNAME
247# 		Usually set to `uname -r' but can be overridden.
248# 	VAR_FTP_PATH
249# 		The FTP URL to send to the HTTP proxy server. Usually set by
250# 		calling f_media_set_ftp().
251#
252# Meanwhile, after successful execution, the following variables (also from
253# variable.subr) are set:
254#
255# 	VAR_HTTP_PROXY_PATH
256# 		The [possibly] adjusted VAR_FTP_PATH that was found to contain
257# 		a valid FreeBSD repository.
258#
259f_media_init_http_proxy()
260{
261	local dev="$1"
262	f_dprintf "Init routine called for HTTP Proxy device. dev=[%s]" "$dev"
263
264	#
265	# First verify access
266	#
267	local connect_only=1
268	f_http_proxy_check_access $connect_only
269
270	local proxy_host
271	f_getvar $VAR_HTTP_PROXY_HOST proxy_host
272	while [ ! "$proxy_host" ]; do
273		f_media_set_http_proxy || return $FAILURE
274		f_http_proxy_check_access $connect_only
275		f_getvar $VAR_HTTP_PROXY_HOST proxy_host
276	done
277
278	local rel proxy_path http_found=$FAILURE
279	while :; do
280		#
281		# If the release is specified as "__RELEASE" or "any", then
282		# just assume that the path the user gave is ok.
283		#
284		f_getvar $VAR_RELNAME rel
285		f_dprintf "f_media_init_http_proxy: rel=[%s]" "$rel"
286
287		case "$rel" in
288		__RELEASE|any)
289			f_getvar $VAR_FTP_PATH $VAR_HTTP_PROXY_PATH
290			f_http_proxy_check_access
291			http_found=$?
292			;;
293		*)
294			local fdir fp
295			f_getvar $VAR_FTP_PATH fp
296			for fdir in $FTP_DIRS; do
297				setvar $VAR_HTTP_PROXY_PATH "$fp/$fdir/$rel"
298				if f_http_proxy_check_access; then
299					http_found=$SUCCESS
300					break
301				fi
302			done
303		esac
304
305		[ $http_found -eq $SUCCESS ] && break
306
307		f_getvar $VAR_HTTP_PROXY_PATH proxy_path
308		f_show_msg "$msg_please_check_the_url_and_try_again" \
309		           "$proxy_path"
310
311		unset $VAR_HTTP_PROXY_PATH
312		f_media_set_http_proxy || break
313	done
314
315	return $http_found
316}
317
318# f_media_get_http_proxy $device $file [$probe_only]
319#
320# Returns data from $file on an FTP server via HTTP proxy using nc(1). Please
321# note that $device is unused but must be present (even if null). Information
322# is instead gathered from the environment. $probe_only is currently unused by
323# this media type.
324#
325# The variables used to configure the connection are as follows (all of which
326# are configured by f_media_set_http_proxy above):
327#
328# 	VAR_HTTP_PROXY_HOST
329# 		HTTP proxy host to connect. Can be an IPv4 address, IPv6
330# 		address, or DNS hostname of your choice.
331# 	VAR_HTTP_PROXY_PORT
332# 		TCP port to connect on; see f_media_set_http_proxy above.
333# 	VAR_HTTP_PROXY_PATH
334# 		URL (including "ftp://" protocol-prefix) of FTP directory to
335# 		use as a prefix when requesting $file via HTTP proxy.
336#
337# See variable.subr for additional information.
338#
339# Example usage:
340# 	f_media_set_http_proxy
341# 	f_media_get_http_proxy media $file
342#
343f_media_get_http_proxy()
344{
345	local dev="$1" file="$2" probe_only="$3" hosts=
346
347	f_dprintf "f_media_get_http_proxy: dev=[%s] file=[%s] probe_only=%s" \
348	          "$dev" "$file" "$probe_only"
349
350	local proxy_host proxy_port
351	f_getvar $VAR_HTTP_PROXY_HOST proxy_host
352	f_getvar $VAR_HTTP_PROXY_PORT proxy_port
353
354	if ! {
355		f_validate_ipaddr "$proxy_host" ||
356		f_validate_ipaddr6 "$proxy_host" ||
357		{
358		  f_dprintf "%s: Looking up hostname, %s, using host(1)" \
359		            "f_media_get_http_proxy" "$proxy_host"
360		  f_host_lookup "$proxy_host" hosts
361		}
362	}; then
363		# All the above validations failed
364		[ "$hosts" ] && f_dialog_msgbox "$hosts"
365		return $FAILURE
366	elif [ ! "$hosts" ]; then
367		# One of the first two validations passed
368		hosts="$proxy_host"
369	fi
370
371	local host connected=
372	for host in $hosts; do
373		f_quietly nc -nz "$host" "$proxy_port" || continue
374		connected=1; break
375	done
376	if [ ! "$connected" ]; then
377		f_show_msg "$msg_couldnt_connect_to_proxy %s:%s" \
378		           "$proxy_host" "$proxy_port"
379		return $FAILURE
380	fi
381
382	local proxy_path mode
383	f_getvar $VAR_HTTP_PROXY_PATH proxy_path
384	f_getvar $VAR_HTTP_FTP_MODE mode
385	local url="${proxy_path%/}/$file$mode" rx
386
387	f_dprintf "sending http request for: %s" "$url"
388	printf "GET %s HTTP/1.0\r\n\r\n" "$url" | nc -n "$host" "$proxy_port" |
389	(
390		#
391		# scan the headers of the response
392		# this is extremely quick'n dirty
393		#
394
395		rv=0
396		while read LINE; do
397			case "$LINE" in
398			HTTP*)
399				f_dprintf "received response: %s" "$LINE"
400				set -- $LINE; rv=$2
401				f_isinteger "$rv" || rv=0
402				;;
403			*)
404				[ "${LINE%
405}" ] || break # End of headers
406			esac
407		done
408
409		[ $rv -ge 500 ] && exit 5
410		[ $rv -eq 404 ] && exit 44
411		[ $rv -ge 400 ] && exit 4
412		[ $rv -ge 300 ] && exit 3
413		[ $rv -eq 200 ] || exit $FAILURE
414
415		cat # output the rest ``as-is''
416		exit 200
417	)
418	local retval=$?
419	[ $retval -eq 200 ] && return $SUCCESS
420
421	case "$retval" in
422	  5) f_show_msg "$msg_server_error_when_requesting_url" "$url" ;;
423	 44) f_show_msg "$msg_url_was_not_found" "$url" ;;
424	  4) f_show_msg "$msg_client_error" ;;
425	  *) f_show_msg "$msg_error_when_requesting_url" "$url" ;;
426	esac
427	return $FAILURE
428}
429
430############################################################ MAIN
431
432f_dprintf "%s: Successfully loaded." media/httpproxy.subr
433
434fi # ! $_MEDIA_HTTPPROXY_SUBR
435