gecko-dev/webtools/web-sniffer/proxy.c
erik%vanderpoel.org 6a5734b52e added support for XHTML's empty elements <foo />;
added support for declarations like <!DOCTYPE ...>;
added support for processing instructions like <?xml ...>
2005-02-03 01:06:24 +00:00

666 lines
12 KiB
C

/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is SniffURI.
*
* The Initial Developer of the Original Code is
* Erik van der Poel <erik@vanderpoel.org>.
* Portions created by the Initial Developer are Copyright (C) 1998-2005
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bruce Robson <bns_robson@hotmail.com>
*
* ***** END LICENSE BLOCK ***** */
/* host and port of proxy that this proxy connects to */
#define PROXY_HOST "127.0.0.1"
#define PROXY_PORT 4444
#include "all.h"
typedef int (*Handler)(int fd);
typedef struct FD
{
Handler handler;
int id;
FILE *logFile;
int port;
int suspend;
int writeFD;
} FD;
typedef struct Arg
{
View *view;
} Arg;
static void proxyHTML(App *app, Buf *buf);
static void proxyHTMLAttributeName(App *app, HTML *html, Buf *buf);
static void proxyHTMLAttributeValue(App *app, HTML *html, Buf *buf);
static void proxyHTMLDeclaration(App *app, Buf *buf);
static void proxyHTMLProcessingInstruction(App *app, Buf *buf);
static void proxyHTMLTag(App *app, HTML *html, Buf *buf);
static void proxyHTMLText(App *app, Buf *buf);
static void proxyHTTP(App *app, Buf *buf);
static void proxyHTTPBody(App *app, Buf *buf);
static void proxyHTTPHeaderName(App *app, Buf *buf);
static void proxyHTTPHeaderValue(App *app, Buf *buf, unsigned char *url);
static fd_set fdSet;
static int id = 0;
static unsigned short mainPort = 40404;
static int maxFD = -1;
static FD **table = NULL;
static char suspendStr[1024];
static char *welcome1 =
"HTTP/1.0 200 OK\n"
"Content-Type: text/html\n"
"\n"
"<title>Interceptor</title>\n"
"<h3>HTTP Interceptor Persistent Window</h3>\n"
"<p>\n"
"Keep this window alive as long as you want to continue the session.\n"
"It is recommended that you Minimize (Iconify) this window.\n"
"Do not click the Back button in this window.\n"
"Do not load another document in this window.\n"
"</p>\n"
"<script>\n";
static char *welcome2 =
"interceptorSuspendResumeWindow =\n"
" window.open\n"
" (\n"
" \"\",\n"
" \"interceptorSuspendResumeWindow\",\n"
" \"menubar,toolbar,location,directories,scrollbars,status\"\n"
" );\n"
"interceptorSuspendResumeWindow.document.write(\n"
"\"<title>Welcome to the HTTP Interceptor</title>\" +\n"
"\"<h3>Welcome to the HTTP Interceptor</h3>\" +\n"
"\"<p>\" +\n"
"\"A new HTTP Interceptor session has been started for you. \" +\n";
static char *welcome3 =
"\"To start using this session, set your HTTP Proxy preference to the \" +\n"
"\"following. (Edit | Preferences | Advanced | Proxies | \" +\n"
"\"Manual proxy configuration | View | HTTP)\" +\n"
"\"</p>\" +\n"
"\"<pre>\" +\n"
"\"\\n\" +\n"
"\"\\tHTTP Proxy Server Address %s; Port %d\" +\n"
"\"\\n\" +\n"
"\"</pre>\" +\n"
"\"<h3>How to Suspend and Resume Logging</h3>\" +\n"
"\"<p>\" +\n"
"\"You can temporarily suspend and resume the HTTP Interceptor logging \" +\n"
"\"feature by clicking the links below.\" +\n";
static char *welcome4 =
"\"</p>\" +\n"
"\"<a href=http://%s:%d/suspend/%d>Suspend Logging</a><br>\" +\n"
"\"<a href=http://%s:%d/resume/%d>Resume Logging</a>\" +\n"
"\"<p>\" +\n"
"\"You may find it useful to drag these links to your Personal Toolbar.\" +\n"
"\"</p>\"\n"
");\n"
"</script>\n";
static FD *
addFD(int fd, Handler func)
{
FD *f;
if (fd > maxFD)
{
if (table)
{
table = utilRealloc(table,
(maxFD + 1) * sizeof(*table),
(fd + 1) * sizeof(*table));
}
else
{
table = calloc(fd + 1, sizeof(*table));
}
if (!table)
{
return NULL;
}
maxFD = fd;
}
f = malloc(sizeof(FD));
if (!f)
{
return NULL;
}
f->handler = func;
f->id = -1;
f->logFile = NULL;
f->port = 0;
f->suspend = 0;
f->writeFD = -1;
table[fd] = f;
FD_SET(fd, &fdSet);
return f;
}
static void
removeFD(int fd)
{
FD *f;
f = table[fd];
if (f)
{
FD_CLR(fd, &fdSet);
if (f->logFile && (fileno(f->logFile) == fd))
{
fclose(f->logFile);
}
else
{
close(fd);
}
free(f);
table[fd] = NULL;
}
}
static App *
proxyApp(FD *f)
{
App *app;
app = appAlloc();
app->html = proxyHTML;
app->htmlAttributeName = proxyHTMLAttributeName;
app->htmlAttributeValue = proxyHTMLAttributeValue;
app->htmlDeclaration = proxyHTMLDeclaration;
app->htmlProcessingInstruction = proxyHTMLProcessingInstruction;
app->htmlTag = proxyHTMLTag;
app->htmlText = proxyHTMLText;
app->httpResponse = proxyHTTP;
app->httpResponseBody = proxyHTTPBody;
app->httpResponseHeaderName = proxyHTTPHeaderName;
app->httpResponseHeaderValue = proxyHTTPHeaderValue;
app->view.backslash = 1;
app->view.out = f->logFile;
return app;
}
static int
logRequest(FD *f, Buf *buf)
{
App *app;
HTTP *http;
if
(
(table[fileno(f->logFile)]->suspend) ||
(strstr((char *) current(buf), suspendStr))
)
{
table[f->writeFD]->suspend = 1;
return 0;
}
table[f->writeFD]->suspend = 0;
fprintf
(
f->logFile,
"<script>\n"
"w%d = window.open(\"\", \"%d\", \"scrollbars\");\n"
"w%d.document.write(\""
"<h3>Client Request</h3>"
"<pre><b>",
f->id,
f->id,
f->id
);
http = httpAlloc();
http->in = buf;
app = proxyApp(f);
httpParseRequest(http, app, "logRequest");
free(app);
httpFree(http);
fprintf(f->logFile, "</b></pre>\");\n</script>\n");
fflush(f->logFile);
return 1;
}
static int
readClientRequest(int fd)
{
FD *f;
Buf *buf;
f = table[fd];
buf = readAvailableBytes(fd);
send(f->writeFD, current(buf), inputLength(buf), 0);
if (!logRequest(f, buf))
{
bufFree(buf);
}
return 0;
}
static int
logResponse(FD *f, Buf *buf)
{
App *app;
HTTP *http;
if ((table[fileno(f->logFile)]->suspend) || (f->suspend))
{
return 0;
}
fprintf
(
f->logFile,
"<script>\n"
"w%d.document.write(\""
"<h3>Server Response</h3>"
"<pre><b>",
f->id
);
http = httpAlloc();
http->in = buf;
app = proxyApp(f);
httpParseStream(http, app, (unsigned char *) "readProxyResponse");
free(app);
httpFree(http);
fprintf(f->logFile, "</b></pre>\");\n</script>\n");
fflush(f->logFile);
return 1;
}
static int
readProxyResponse(int fd)
{
FD *f;
Buf *buf;
f = table[fd];
buf = readStream(fd, (unsigned char *) "readProxyResponse");
send(f->writeFD, current(buf), inputLength(buf), 0);
if (!logResponse(f, buf))
{
bufFree(buf);
}
removeFD(f->writeFD);
removeFD(fd);
return 0;
}
static int
acceptNewClient(int fd)
{
FD *client;
int clientFD;
FD *f;
FD *proxy;
int proxyFD;
clientFD = netAccept(fd);
if (clientFD < 0)
{
fprintf(stderr, "netAccept failed\n");
return 0;
}
client = addFD(clientFD, readClientRequest);
if (!client)
{
fprintf(stderr, "addFD failed\n");
return 0;
}
proxyFD = netConnect(NULL, (unsigned char *) PROXY_HOST, PROXY_PORT);
if (proxyFD < 0)
{
fprintf(stderr, "netConnect to proxy %s:%d failed\n",
PROXY_HOST, PROXY_PORT);
return 0;
}
proxy = addFD(proxyFD, readProxyResponse);
if (!proxy)
{
fprintf(stderr, "addFD failed\n");
return 0;
}
client->writeFD = proxyFD;
proxy->writeFD = clientFD;
f = table[fd];
client->logFile = f->logFile;
proxy->logFile = f->logFile;
client->id = id;
proxy->id = id;
id++;
return 0;
}
static int
readLoggerRequest(int fd)
{
char b[10240];
int bytesRead;
int doSuspend;
FD *f;
FILE *file;
unsigned char *host;
int i;
char *p;
unsigned short port;
int proxyListenFD;
char *resume;
char *str;
char *suspend;
int suspendPort;
bytesRead = recv(fd, b, sizeof(b) - 1, 0);
if (bytesRead < 0)
{
if (errno != ECONNRESET)
{
perror("recv");
}
removeFD(fd);
return 0;
}
else if (!bytesRead)
{
removeFD(fd);
return 0;
}
b[bytesRead] = 0;
resume = "/resume";
suspend = "/suspend";
if (strstr(b, "/exit"))
{
char *goodbye =
"HTTP/1.0 200 OK\n"
"Content-Type: text/html\n"
"\n"
"Bye!"
;
send(fd, goodbye, strlen(goodbye), 0);
removeFD(fd);
return 1;
}
else if ((strstr(b, resume)) || (strstr(b, suspend)))
{
if (strstr(b, resume))
{
str = resume;
doSuspend = 0;
}
else
{
str = suspend;
doSuspend = 1;
}
p = strstr(b, str);
p += strlen(str);
if (*p != '/')
{
char *notOK =
"HTTP/1.0 200 OK\n"
"Content-Type: text/html\n"
"\n"
"No backslash after command!"
;
send(fd, notOK, strlen(notOK), 0);
removeFD(fd);
return 0;
}
sscanf(p + 1, "%d", &suspendPort);
for (i = 0; i <= maxFD; i++)
{
if (table[i] && (table[i]->port == suspendPort))
{
table[i]->suspend = doSuspend;
break;
}
}
if (i <= maxFD)
{
char *ok =
"HTTP/1.0 200 OK\n"
"Content-Type: text/html\n"
"\n"
"OK!"
;
send(fd, ok, strlen(ok), 0);
}
else
{
char *notOK =
"HTTP/1.0 200 OK\n"
"Content-Type: text/html\n"
"\n"
"Cannot find port number in table!"
;
send(fd, notOK, strlen(notOK), 0);
}
removeFD(fd);
return 0;
}
/* XXX send(1, b, bytesRead, 0); */
file = fdopen(fd, "w");
if (!file)
{
char *err = "fdopen failed\n";
send(fd, err, strlen(err), 0);
removeFD(fd);
return 0;
}
table[fd]->logFile = file;
port = 0;
proxyListenFD = netListen(NULL, &host, &port);
if (proxyListenFD < 0)
{
fprintf(file, "listen failed\n");
removeFD(fd);
fclose(file);
return 0;
}
f = addFD(proxyListenFD, acceptNewClient);
if (!f)
{
fprintf(file, "addFD failed\n");
removeFD(fd);
fclose(file);
return 0;
}
fprintf(file, welcome1);
fprintf(file, welcome2);
fprintf(file, welcome3, host, port);
fprintf(file, welcome4, host, mainPort, port, host, mainPort, port);
sprintf(suspendStr, "http://%s:%d/suspend/%d", host, mainPort, port);
free(host);
fflush(file);
f->logFile = file;
table[fd]->port = port;
return 0;
}
static int
acceptNewLogger(int fd)
{
FD *f;
int newFD;
newFD = netAccept(fd);
if (newFD < 0)
{
fprintf(stderr, "netAccept failed\n");
return 0;
}
f = addFD(newFD, readLoggerRequest);
if (!f)
{
fprintf(stderr, "addFD failed\n");
return 0;
}
return 0;
}
static void
proxyHTML(App *app, Buf *buf)
{
viewHTML(app, buf);
}
static void
proxyHTMLAttributeName(App *app, HTML *html, Buf *buf)
{
viewHTMLAttributeName(app, buf);
}
static void
proxyHTMLAttributeValue(App *app, HTML *html, Buf *buf)
{
viewHTMLAttributeValue(app, buf);
}
static void
proxyHTMLDeclaration(App *app, Buf *buf)
{
viewHTMLDeclaration(app, buf);
}
static void
proxyHTMLProcessingInstruction(App *app, Buf *buf)
{
viewHTMLProcessingInstruction(app, buf);
}
static void
proxyHTMLTag(App *app, HTML *html, Buf *buf)
{
viewHTMLTag(app, buf);
}
static void
proxyHTMLText(App *app, Buf *buf)
{
viewHTMLText(app, buf);
}
static void
proxyHTTP(App *app, Buf *buf)
{
viewHTTP(app, buf);
}
static void
proxyHTTPBody(App *app, Buf *buf)
{
viewHTTP(app, buf);
}
static void
proxyHTTPHeaderName(App *app, Buf *buf)
{
viewHTTPHeaderName(app, buf);
}
static void
proxyHTTPHeaderValue(App *app, Buf *buf, unsigned char *url)
{
viewHTTPHeaderValue(app, buf);
}
int
main(int argc, char *argv[])
{
FD *f;
int fd;
fd_set localFDSet;
int ret;
if (!netInit())
{
return 1;
}
if (!threadInit())
{
return 1;
}
fd = netListen(NULL, NULL, &mainPort);
if (fd < 0)
{
fprintf(stderr, "netListen failed\n");
return 1;
}
f = addFD(fd, acceptNewLogger);
if (!f)
{
fprintf(stderr, "addFD failed\n");
return 1;
}
while (1)
{
localFDSet = fdSet;
ret = select(maxFD + 1, &localFDSet, NULL, NULL, NULL);
if (ret == -1)
{
perror("select");
}
for (fd = 0; fd <= maxFD; fd++)
{
if (FD_ISSET(fd, &localFDSet))
{
if ((*table[fd]->handler)(fd))
{
for (fd = 0; fd <= maxFD; fd++)
{
removeFD(fd);
}
return 0;
}
}
}
}
return 1;
}