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