2 * CNTLM is free software; you can redistribute it and/or modify it under the
3 * terms of the GNU General Public License as published by the Free Software
4 * Foundation; either version 2 of the License, or (at your option) any later
7 * CNTLM is distributed in the hope that it will be useful, but WITHOUT ANY
8 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
9 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12 * You should have received a copy of the GNU General Public License along with
13 * this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
14 * St, Fifth Floor, Boston, MA 02110-1301, USA.
16 * Copyright (c) 2007 David Kubicek
20 #include <sys/types.h>
38 * This code is a piece of shit, but it works. Cannot rewrite it now, because
39 * I don't have ISA AV filter anymore - wouldn't be able to test it.
41 int scanner_hook(rr_data_t request
, rr_data_t response
, struct auth_s
*credentials
, int cd
, int *sd
, long maxKBs
) {
42 char *buf
, *line
, *pos
, *tmp
, *pat
, *post
, *isaid
, *uurl
;
43 int bsize
, lsize
, size
, len
, i
, w
, nc
;
44 rr_data_t newreq
, newres
;
49 int headers_initiated
= 0;
50 long c
, progress
= 0, filesize
= 0;
53 * Let's limit the responses we examine to an absolute minimum
55 if (!request
->req
|| response
->code
!= 200
56 || http_has_body(request
, response
) != -1
57 || hlist_subcmp(response
->headers
, "Transfer-Encoding", "chunked")
58 || !hlist_subcmp(response
->headers
, "Proxy-Connection", "close"))
59 return PLUG_SENDHEAD
| PLUG_SENDDATA
;
61 tmp
= hlist_get(request
->headers
, "User-Agent");
63 tmp
= lowercase(strdup(tmp
));
64 list
= scanner_agent_list
;
66 pat
= lowercase(strdup(list
->aux
));
68 printf("scanner_hook: matching U-A header (%s) to %s\n", tmp
, pat
);
69 if (!fnmatch(pat
, tmp
, 0)) {
71 printf("scanner_hook: positive match!\n");
87 size
= read(*sd
, buf
+ len
, SAMPLE
- len
- 1);
89 printf("scanner_hook: read %d of %d\n", size
, SAMPLE
- len
);
92 } while (size
> 0 && len
< SAMPLE
- 1);
94 if (strstr(buf
, "<title>Downloading status</title>") && (pos
=strstr(buf
, "ISAServerUniqueID=")) && (pos
= strchr(pos
, '"'))) {
97 for (i
= 0; i
< c
&& pos
[i
] != '"'; ++i
);
100 isaid
= substr(pos
, 0, i
);
102 printf("scanner_hook: ISA id = %s\n", isaid
);
107 i
= so_recvln(*sd
, &line
, &lsize
);
110 if (len
+ c
>= bsize
) {
112 tmp
= realloc(buf
, bsize
);
123 ((pos
= strstr(line
, "UpdatePage("))
126 ((pos
= strstr(line
, "DownloadFinished("))
130 printf("scanner_hook: %s", line
);
132 if ((pos
= strstr(line
, "To be downloaded"))) {
133 filesize
= atol(pos
+16);
136 printf("scanner_hook: file size detected: %ld KiBs (max: %ld)\n", filesize
/1024, maxKBs
);
138 printf("scanner_hook: file size unknown -- quitting\n");
143 if (maxKBs
&& (maxKBs
== 1 || filesize
/1024 > maxKBs
))
147 * We have to send HTTP protocol ID so we can send the notification
148 * headers during downloading. Once we've done that, it cannot appear
149 * again, which it would if we returned PLUG_SENDHEAD, so we must
150 * remember to not include it.
152 headers_initiated
= 1;
153 tmp
= new(MINIBUF_SIZE
);
154 snprintf(tmp
, MINIBUF_SIZE
, "%s 200 OK\r\n", request
->http
);
155 w
= write(cd
, tmp
, strlen(tmp
));
159 if (!headers_initiated
) {
161 printf("scanner_hook: Giving up, \"To be downloaded\" line not found!\n");
166 * Send a notification header to the client, just so it doesn't timeout
169 tmp
= new(MINIBUF_SIZE
);
170 progress
= atol(line
+12);
171 snprintf(tmp
, MINIBUF_SIZE
, "ISA-Scanner: %ld of %ld\r\n", progress
, filesize
);
172 w
= write(cd
, tmp
, strlen(tmp
));
177 * If download size is unknown beforehand, stop when downloaded amount is over ISAScannerSize
179 if (!filesize
&& maxKBs
&& maxKBs
!= 1 && progress
/1024 > maxKBs
)
182 } while (i
> 0 && !done
);
184 if (i
>= 0 && done
&& (pos
= strstr(line
, "\",\"")+3) && (c
= strchr(pos
, '"') - pos
) > 0) {
185 tmp
= substr(pos
, 0, c
);
186 pos
= urlencode(tmp
);
189 uurl
= urlencode(request
->url
);
192 snprintf(post
, BUFSIZE
-1, "%surl=%s&%sSaveToDisk=YES&%sOrig=%s", isaid
, pos
, isaid
, isaid
, uurl
);
195 printf("scanner_hook: Getting file with URL data = %s\n", request
->url
);
197 tmp
= new(MINIBUF_SIZE
);
198 snprintf(tmp
, MINIBUF_SIZE
, "%d", (int)strlen(post
));
200 newres
= new_rr_data();
201 newreq
= dup_rr_data(request
);
203 free(newreq
->method
);
204 newreq
->method
= strdup("POST");
205 hlist_mod(newreq
->headers
, "Referer", request
->url
, 1);
206 hlist_mod(newreq
->headers
, "Content-Type", "application/x-www-form-urlencoded", 1);
207 hlist_mod(newreq
->headers
, "Content-Length", tmp
, 1);
210 nc
= proxy_connect(credentials
);
211 c
= proxy_authenticate(&nc
, newreq
, newres
, credentials
);
212 if (c
&& newres
->code
== 407) {
214 printf("scanner_hook: Authentication OK, getting the file...\n");
217 printf("scanner_hook: Authentication failed or refused!\n");
223 * The POST request for the real file
225 reset_rr_data(newres
);
226 if (nc
&& headers_send(nc
, newreq
) && write(nc
, post
, strlen(post
)) && headers_recv(nc
, newres
)) {
228 hlist_dump(newres
->headers
);
231 * We always know the filesize here. Send it to the client, because ISA doesn't!!!
232 * The clients progress bar doesn't work without it and it stinks!
234 if (filesize
|| progress
) {
236 snprintf(tmp
, 20, "%ld", filesize
? filesize
: progress
);
237 newres
->headers
= hlist_mod(newres
->headers
, "Content-Length", tmp
, 1);
241 * Here we remember if previous code already sent some headers
242 * to the client. In such case, do not include the HTTP/1.x ID.
244 newres
->skip_http
= headers_initiated
;
245 copy_rr_data(response
, newres
);
250 ok
= PLUG_SENDHEAD
| PLUG_SENDDATA
;
252 printf("scanner_hook: New request failed\n");
263 printf("scanner_hook: ISA id not found\n");
268 printf("scanner_hook: flushing %d original bytes\n", len
);
269 hlist_dump(response
->headers
);
272 if (!headers_send(cd
, response
)) {
274 printf("scanner_hook: failed to send headers\n");
279 size
= write(cd
, buf
, len
);
287 printf("scanner_hook: ending with %d\n", ok
);