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