29 */ |
29 */ |
30 |
30 |
31 #include "volta.h" |
31 #include "volta.h" |
32 #include "db.h" |
32 #include "db.h" |
33 |
33 |
|
34 |
|
35 /* |
|
36 * Given a redirect +line+ from squid, send it to the parser, |
|
37 * perform database lookups, and conditonally perform the rewrite. |
|
38 * |
|
39 */ |
34 void |
40 void |
35 process( char *line ) |
41 process( char *line ) |
36 { |
42 { |
37 request *p_request = parse( line ); |
43 parsed *p_request = parse_request( line ), *rule = NULL; |
38 rewrite *p_rewrite = prepare_rewrite( p_request ); |
44 parsed *results[ DB_RESULTS_MAX ] = { NULL }; /* array of response matches */ |
|
45 unsigned int rcount = 0; |
39 |
46 |
40 /* count lines in debugmode */ |
47 /* count lines in debugmode */ |
41 if ( v.debugmode > 2 ) v.timer.lines++; |
48 if ( v.debugmode > 2 ) v.timer.lines++; |
42 |
49 |
43 /* If parsing failed or there wasn't a successful rewrite match, |
50 /* If request parsing failed, return a blank line to squid |
44 * return a blank line to squid to allow the request to pass |
51 to allow the request to pass through unmolested. */ |
45 * through unmolested. */ |
52 if ( p_request == NULL ) { |
46 if ( p_request == NULL || p_rewrite == NULL ) { |
|
47 out( "\n" ); |
53 out( "\n" ); |
48 finish_request( p_request ); |
54 finish_parsed( p_request ); |
49 finish_rewrite( p_rewrite ); |
|
50 return; |
55 return; |
51 } |
56 } |
52 |
57 |
53 if ( v.debugmode < 4 ) { |
58 /* |
54 if ( p_rewrite->redir == REDIR_TEMPORARY ) printf( "302:" ); |
59 * Main rewrite logic. |
55 if ( p_rewrite->redir == REDIR_PERMANENT ) printf( "301:" ); |
60 * |
|
61 * First, try and match the host exactly. |
|
62 * |
|
63 * Second, match the TLD of the host, so separate rules aren't needed for |
|
64 * every possible subdomain of one particular domain. |
|
65 * |
|
66 * Finally, look for '*', if for some reason the rules provided don't care to |
|
67 * match specific hosts, and instead just match on any path. |
|
68 * |
|
69 * If DB matches are found at any step above, the rules are tried in order |
|
70 * to attempt a match against the path. Exact string match attempted |
|
71 * first, then fallback to regexp. |
|
72 * |
|
73 * First rule match wins, and elements of the URL are rewritten based on |
|
74 * what is present in the rule -- any missing parts just use the original |
|
75 * URL element. (this way, you can rewrite just the host and leave the |
|
76 * path intact, or redir to https, for example.) |
|
77 * |
|
78 */ |
|
79 rcount = find_records( p_request->host, results ); |
|
80 rule = find_matching_rule( results, rcount, p_request ); |
56 |
81 |
57 if ( p_request->scheme || p_rewrite->scheme ) |
82 if ( rule == NULL ) { |
58 printf( "%s", p_rewrite->scheme ? p_rewrite->scheme : p_request->scheme ); |
83 reset_results( results, rcount ); |
59 printf( "%s", p_rewrite->host ? p_rewrite->host : p_request->host ); |
84 rcount = find_records( p_request->tld, results ); |
60 printf( "%s", p_rewrite->path ? p_rewrite->path : p_request->path ); |
85 rule = find_matching_rule( results, rcount, p_request ); |
61 if ( p_request->port != 0 || p_rewrite->port != 0 ) |
|
62 printf( ":%d", p_rewrite->port ? p_rewrite->port : p_request->port ); |
|
63 printf("\n"); |
|
64 } |
|
65 else { |
|
66 debug( 5, LOC, "Rewrite match on %s/%s\n", p_request->host, p_request->path ); |
|
67 debug( 5, LOC, " --> %s/%s\n", p_rewrite->host, p_rewrite->path ); |
|
68 } |
86 } |
69 |
87 |
|
88 if ( rule == NULL ) { |
|
89 reset_results( results, rcount ); |
|
90 rcount = find_records( "*", results ); |
|
91 rule = find_matching_rule( results, rcount, p_request ); |
|
92 } |
70 |
93 |
71 /* unsigned long hst, net; */ |
94 /* no matching rule still? no need to rewrite anything. */ |
72 /* hst = inet_lnaof( *(p_request->client_ip) ); */ |
95 if ( rule == NULL ) { |
73 /* net = inet_netof( *(p_request->client_ip) ); */ |
96 out( "\n" ); |
74 /* printf("%14s : net=0x%08lX host=0x%08lX\n", inet_ntoa( *(p_request->client_ip) ), net, hst); */ |
97 } |
75 /* printf("%14s : net=%lu host=%lu\n", inet_ntoa( *(p_request->client_ip) ), net, hst); */ |
98 /* otherwise, perform the rewrite */ |
|
99 else { |
|
100 rewrite( p_request, rule ); |
|
101 } |
76 |
102 |
77 /* |
103 reset_results( results, rcount ); |
78 * create function bigint_to_inet(bigint) returns inet as $$ |
104 finish_parsed( p_request ); |
79 * select |
|
80 * (($1>>24&255)||'.'||($1>>16&255)||'.'||($1>>8&255)||'.'||($1>>0&255))::inet |
|
81 * $$ language sql; |
|
82 * */ |
|
83 |
|
84 /* |
|
85 char ip[ INET_ADDRSTRLEN ]; |
|
86 inet_ntop( AF_INET, p_request->client_ip, ip, INET_ADDRSTRLEN ); |
|
87 printf( "%s\n", ip ); |
|
88 */ |
|
89 |
|
90 finish_request( p_request ); |
|
91 finish_rewrite( p_rewrite ); |
|
92 return; |
105 return; |
93 } |
106 } |
94 |
107 |
|
108 |
|
109 /* |
|
110 * Output a rewritten URL for squid. |
|
111 * |
|
112 */ |
|
113 void |
|
114 rewrite( parsed *request, parsed *rule ) |
|
115 { |
|
116 if ( rule == NULL || v.debugmode >= 5 ) return; |
|
117 |
|
118 if ( rule->redir ) printf( "%s:", rule->redir ); |
|
119 printf( "%s%s", (rule->scheme ? rule->scheme : request->scheme), rule->host ); |
|
120 if ( rule->port ) printf( ":%s", rule->port ); |
|
121 printf( "%s", rule->path ? rule->path : request->path ); |
|
122 |
|
123 printf("\n"); |
|
124 return; |
|
125 } |
|
126 |
|
127 |
|
128 /* |
|
129 * Search through a result set, and return the first |
|
130 * matching path (or NULL). |
|
131 * |
|
132 */ |
|
133 parsed * |
|
134 find_matching_rule( parsed **results, unsigned int resultcount, parsed *p_request ) |
|
135 { |
|
136 unsigned int i = 0; |
|
137 int re_rv; |
|
138 regex_t re; |
|
139 char re_err[128]; |
|
140 parsed *rule = NULL; |
|
141 |
|
142 if ( resultcount == 0 || p_request->path == NULL ) return( NULL ); |
|
143 |
|
144 for ( i = 0; i < resultcount; i++ ) { |
|
145 /* quick comparison */ |
|
146 if ( (strcasecmp( results[i]->path_re, p_request->path ) == 0) || |
|
147 (strcmp( results[i]->path_re, "*" ) == 0) ) { |
|
148 debug( 4, LOC, "Rule %d match (non regexp)\n", i+1 ); |
|
149 rule = results[i]; |
|
150 break; |
|
151 } |
|
152 |
|
153 /* compile the regexp */ |
|
154 if ( (re_rv = regcomp( &re, results[i]->path_re, REG_EXTENDED | REG_NOSUB )) != 0 ) { |
|
155 regerror( re_rv, &re, re_err, 128 ); |
|
156 debug( 4, LOC, "Invalid regex: \"%s\": %s\n", results[i]->path_re, re_err ); |
|
157 regfree( &re ); |
|
158 continue; |
|
159 } |
|
160 |
|
161 /* compare! */ |
|
162 if ( (regexec( &re, p_request->path, 0, NULL, 0 )) == 0 ) { |
|
163 debug( 4, LOC, "Rule %d match (regexp)\n", i+1 ); |
|
164 rule = results[i]; |
|
165 regfree( &re ); |
|
166 break; |
|
167 } |
|
168 } |
|
169 |
|
170 return( rule ); |
|
171 } |
|
172 |
|
173 |
|
174 /* |
|
175 * Clear the results array and free memory. |
|
176 * |
|
177 */ |
|
178 void |
|
179 reset_results( parsed **results, unsigned int count ) |
|
180 { |
|
181 unsigned int i = 0; |
|
182 |
|
183 for ( ; i < count && i < DB_RESULTS_MAX; i++ ) finish_parsed( results[i] ); |
|
184 memset( results, 0, sizeof(results) ); |
|
185 |
|
186 return; |
|
187 } |
|
188 |