OSDN Git Service

Abstruction: via_proxy($host) => ! in_the_net(array $networks, $host)
[pukiwiki/pukiwiki.git] / lib / proxy.php
1 <?php
2 // $Id: proxy.php,v 1.8 2005/06/27 14:47:40 henoheno Exp $
3 // Copyright (C) 2003-2005 PukiWiki Developers Team
4 // License: GPL v2 or (at your option) any later version
5 //
6 // HTTP-Proxy related functions
7
8 // Max number of 'track' redirection message with 301 or 302 response
9 define('PKWK_HTTP_REQUEST_URL_REDIRECT_MAX', 2);
10
11 /*
12  * http_request($url)
13  *     Get / Send data via HTTP request
14  * $url     : URI started with http:// (http://user:pass@host:port/path?query)
15  * $method  : GET, POST, or HEAD
16  * $headers : Additional HTTP headers, ended with "\r\n"
17  * $post    : An array of data to send via POST method ('key'=>'value')
18  * $redirect_max : Max number of HTTP redirect
19  * $content_charset : Content charset. Use '' or CONTENT_CHARSET
20 */
21 function http_request($url, $method = 'GET', $headers = '', $post = array(),
22         $redirect_max = PKWK_HTTP_REQUEST_URL_REDIRECT_MAX, $content_charset = '')
23 {
24         global $use_proxy, $no_proxy, $proxy_host, $proxy_port;
25         global $need_proxy_auth, $proxy_auth_user, $proxy_auth_pass;
26
27         $rc  = array();
28         $arr = parse_url($url);
29
30         $via_proxy = $use_proxy ? ! in_the_net($no_proxy, $arr['host']) : FALSE;
31
32         // query
33         $arr['query'] = isset($arr['query']) ? '?' . $arr['query'] : '';
34         // port
35         $arr['port']  = isset($arr['port'])  ? $arr['port'] : 80;
36
37         $url_base = $arr['scheme'] . '://' . $arr['host'] . ':' . $arr['port'];
38         $url_path = isset($arr['path']) ? $arr['path'] : '/';
39         $url = ($via_proxy ? $url_base : '') . $url_path . $arr['query'];
40
41         $query = $method . ' ' . $url . ' HTTP/1.0' . "\r\n";
42         $query .= 'Host: ' . $arr['host'] . "\r\n";
43         $query .= 'User-Agent: PukiWiki/' . S_VERSION . "\r\n";
44
45         // Basic-auth for HTTP proxy server
46         if ($need_proxy_auth && isset($proxy_auth_user) && isset($proxy_auth_pass))
47                 $query .= 'Proxy-Authorization: Basic '.
48                         base64_encode($proxy_auth_user . ':' . $proxy_auth_pass) . "\r\n";
49
50         // (Normal) Basic-auth for remote host
51         if (isset($arr['user']) && isset($arr['pass']))
52                 $query .= 'Authorization: Basic '.
53                         base64_encode($arr['user'] . ':' . $arr['pass']) . "\r\n";
54
55         $query .= $headers;
56
57         if (strtoupper($method) == 'POST') {
58                 // 'application/x-www-form-urlencoded', especially for TrackBack ping
59                 $POST = array();
60                 foreach ($post as $name=>$val) $POST[] = $name . '=' . urlencode($val);
61                 $data = join('&', $POST);
62
63                 if (preg_match('/^[a-zA-Z0-9_-]+$/', $content_charset)) {
64                         // Legacy but simple
65                         $query .= 'Content-Type: application/x-www-form-urlencoded' . "\r\n";
66                 } else {
67                         // With charset (NOTE: Some implementation may hate this)
68                         $query .= 'Content-Type: application/x-www-form-urlencoded' .
69                                 '; charset=' . strtolower($content_charset) . "\r\n";
70                 }
71
72                 $query .= 'Content-Length: ' . strlen($data) . "\r\n";
73                 $query .= "\r\n";
74                 $query .= $data;
75         } else {
76                 $query .= "\r\n";
77         }
78
79         $errno  = 0;
80         $errstr = '';
81         $fp = fsockopen(
82                 $via_proxy ? $proxy_host : $arr['host'],
83                 $via_proxy ? $proxy_port : $arr['port'],
84                 $errno, $errstr, 30);
85         if ($fp === FALSE) {
86                 return array(
87                         'query'  => $query, // Query string
88                         'rc'     => $errno, // Error number
89                         'header' => '',     // Header
90                         'data'   => $errstr // Error message
91                 );
92         }
93         fputs($fp, $query);
94         $response = '';
95         while (! feof($fp)) $response .= fread($fp, 4096);
96         fclose($fp);
97
98         $resp = explode("\r\n\r\n", $response, 2);
99         $rccd = explode(' ', $resp[0], 3); // array('HTTP/1.1', '200', 'OK\r\n...')
100         $rc   = (integer)$rccd[1];
101
102         switch ($rc) {
103         case 301: // Moved Permanently
104         case 302: // Moved Temporarily
105                 $matches = array();
106                 if (preg_match('/^Location: (.+)$/m', $resp[0], $matches)
107                         && --$redirect_max > 0)
108                 {
109                         $url = trim($matches[1]);
110                         if (! preg_match('/^https?:\//', $url)) {
111                                 // Relative path to Absolute
112                                 if ($url{0} != '/')
113                                         $url = substr($url_path, 0, strrpos($url_path, '/')) . '/' . $url;
114                                 $url = $url_base . $url; // Add sheme, host
115                         }
116                         // Redirect
117                         return http_request($url, $method, $headers, $post, $redirect_max);
118                 }
119         }
120         return array(
121                 'query'  => $query,   // Query String
122                 'rc'     => $rc,      // Response Code
123                 'header' => $resp[0], // Header
124                 'data'   => $resp[1]  // Data
125         );
126 }
127
128 // Separate IPv4 network-address and its netmask
129 define('PKWK_CIDR_NETWORK_REGEX', '/^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})(?:\/([0-9.]+))?$/');
130
131 // Check if the $host is in the specified network(s)
132 function in_the_net($networks = array(), $host = '')
133 {
134         if (empty($networks) || $hosts == '') return FALSE;
135
136         $matches = array();
137
138         if (preg_match(PKWK_CIDR_NETWORK_REGEX, $host, $matches)) {
139                 $ip = $matches[1];
140         } else {
141                 $ip = gethostbyname($host); // May heavy
142         }
143         $l_ip = ip2long($ip);
144
145         if (is_long($l_ip) && long2ip($l_ip) == $ip) {
146                 // $host seems valid IPv4 address
147                 foreach ($networks as $network) {
148                         if (preg_match(PKWK_CIDR_NETWORK_REGEX, $network, $matches)) {
149                                 // Sample: '10.0.0.0/8' or '10.0.0.0/255.0.0.0'
150                                 $l_net = ip2long($matches[1]); // '10.0.0.0'
151                                 $mask  = isset($matches[2]) ? $matches[2] : 32; // '8' or '255.0.0.0'
152                                 $mask  = is_numeric($mask) ?
153                                         pow(2, 32) - pow(2, 32 - $mask) : // '8' means '8-bit mask'
154                                         ip2long($mask);                   // '255.0.0.0' (the same)
155
156                                 if (($l_ip & $mask) == $l_net) return TRUE;
157                         }
158                 }
159         } else {
160                 // $host seems not IPv4 address. May be a DNS name like 'foobar.example.com'?
161                 foreach ($networks as $network)
162                         if (preg_match('/\b' . preg_quote($network, '/') . '$/', $host))
163                                 return TRUE;
164         }
165
166         return FALSE; // Not found
167 }
168 ?>