/* * Welcome to Proxy Server! * * This thing should work on Wintel and *nix. It supports HTTP GET, * POST, and CONNECT (aka Tunnelling, SSL, Security.) * * Please send me notice of all bugs: malxau@users.sourceforge.net. * * This code is licensed subject to the terms of the GNU General Public * License (see the COPYING file for more.) Note that there is NO * WARRANTY provided, including but not limited to, no warranty for * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * Copyright (c) Malcolm Smith 2003-2004. *//* * Includes common to all platforms */#include <ctype.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#ifdef _WIN32_/* Windoze includes */#include <io.h>#include <winsock.h>#else /* _WIN32_ *//* *nix includes */#include <netinet/in.h>#include <netdb.h>#include <signal.h>#include <sys/select.h>#include <sys/socket.h>#include <sys/types.h>#include <unistd.h>#ifdef THREADING_SUPPORT#include <pthread.h>#endif#endif /* !_WIN32_ */typedef int SOCKLEN_T;#ifndef _WIN32_/*Windows defines a few types, which UNIX has equivalent (different) typesfor. These set up aliases on unix to the Windows version. This shouldmake code easier to read.*/typedef int BOOL;typedef struct hostent HOSTENT;typedef struct sockaddr SOCKADDR;typedef struct sockaddr_in SOCKADDR_IN;typedef int SOCKET;/*Some constants, predefined on Windows.*/#ifndef FALSEconst FALSE = 0;#endifconst INVALID_SOCKET = -1;#ifndef TRUEconst TRUE = 1;#endif#define closesocket(x) close(x)#endif /* !_WIN32_ */#ifdef _WIN32_#define sleep(x) Sleep(x*1000)#endif /* _WIN32_ */typedef enum{ NO, YES, NOTYET }BOOLX;/*As a proxy, we need to listen for new requests coming in from the LAN.This part is a server part - and this is the variable that recordswhere we're listening.*/SOCKET server;#if defined(THREADING_SUPPORT)&&(!defined(_WIN32_))pthread_mutex_t gethostbyname_lock = PTHREAD_MUTEX_INITIALIZER;#endif/*This function sends data, making sure it either sends all the data or fails,no partial send should be considered success*/intsend_block (SOCKET s, char *buf, int len){ int havesent; int sentnow; havesent = 0; while (havesent != len) { sentnow = send (s, buf + havesent, (len - havesent), 0); if (sentnow <= 0) return -1; havesent += sentnow; } return len;}/*This function takes the specified hostname, finds the IP address, andattempts to connect. If it succeeds, it returns where you can findthe new connection, otherwise it returns a token saying no connectionwas made.*/SOCKETConnectToHost (char *szHostName){ SOCKET client; SOCKADDR_IN sin; HOSTENT *phe; int ret; char *port; struct linger lyes; /* If the user has specified a port, find out where it is, and * then pretend it isn't there. */ port = strchr (szHostName, ':'); if (port) port[0] = '\0'; /* Create a new socket for the communication. */ client = socket (AF_INET, SOCK_STREAM, 0); /* Linger - if the socket is closed, ensure that data is sent/ received right up to the last byte. Don't stop just because the connection is closed. */ lyes.l_onoff = 1; lyes.l_linger = 10; setsockopt (client, SOL_SOCKET, SO_LINGER, (char *) &lyes, sizeof (lyes)); /* Clear the structure. */ memset (&sin, 0, sizeof (sin)); /* We want to connect to an IP network (AF_INET) */ sin.sin_family = AF_INET; if (port) { /* If we have a port, use that */ sin.sin_port = htons ((unsigned short) atoi (port + 1)); } else { /* Default port for HTTP is 80 */ sin.sin_port = htons (80); } /* Windows uses Thread-Local storage for this. Unix may not, and needs * this locking mechanism instead. A professional application would use * gethostbyname_r, but the syntax varies between unicies, and I don't * have a configure script, so I'm not using it. */#if defined(THREADING_SUPPORT)&&(!defined(_WIN32_)) pthread_mutex_lock (&gethostbyname_lock);#endif /* Get the IP address for this hostname. */ phe = gethostbyname (szHostName); if (!phe) {#if defined(THREADING_SUPPORT)&&(!defined(_WIN32_)) pthread_mutex_unlock (&gethostbyname_lock);#endif printf ("Could not resolve hostname %s.\n", szHostName); return INVALID_SOCKET; } /* Use the address obtained from GetHostByName */ memcpy (&sin.sin_addr.s_addr, phe->h_addr, phe->h_length);#if defined(THREADING_SUPPORT)&&(!defined(_WIN32_)) pthread_mutex_unlock (&gethostbyname_lock);#endif /* Try to connect */ ret = connect (client, (SOCKADDR *) & sin, sizeof (sin)); if (ret == 0) return client; /* We failed. */ closesocket (client); return INVALID_SOCKET;}/* * This function peeks at input and tries to extract a single, solitary line * and leave the rest of the data in the buffer waiting collection. It will * wait until a line is present. It should be used where a line is definitely * needed before proceeding. */BOOLGetLineFromSocketWait (SOCKET sock, char *szDestination, int nDestination){ char szBuffer[1024]; char *inptr; int len; int sizeremaining; char *ret; int i; fd_set readset; struct timeval timeout; i = 0; inptr = szBuffer; sizeremaining = sizeof (szBuffer) - 1; while (i < 15) { FD_ZERO (&readset); FD_SET (sock, &readset); timeout.tv_sec = 60 * 5; timeout.tv_usec = 0; if (select (sock + 1, &readset, NULL, NULL, &timeout) < 1) return FALSE; /* Peek at all the available data */ len = recv (sock, inptr, sizeremaining, MSG_PEEK); inptr[len] = '\0'; if (len < 1) return NO; /* Find a newline */ ret = strchr (szBuffer, '\n'); if (ret) { /* If we have space in the buffer */ if (ret - szBuffer < (nDestination - 4)) { /* Permanently read the data from the network */ len = recv (sock, inptr, (ret - szBuffer) - (inptr - szBuffer) + 1, 0); if (len < 1) return NO; inptr[len] = '\0'; strcpy (szDestination, szBuffer); return TRUE; } return FALSE; } else { len = recv (sock, inptr, len, 0); inptr[len] = '\0'; inptr += len; sizeremaining -= len; i++; } } return FALSE;}/* * Clients don't like us responding till they've sent their entire request. * This is used to collect their request and throw it away, so we can * reply. */BOOLDiscardHeaders (SOCKET stream, char *szBuffer, int nBuffer){ BOOL finished; char *szHeaderValue; finished = FALSE; while (!finished) { strcpy (szBuffer, ""); if (GetLineFromSocketWait (stream, szBuffer, nBuffer - 1)) { /* Strip of a trailing carriage-return. */ szHeaderValue = strchr (szBuffer, '\r'); if (szHeaderValue) szHeaderValue[0] = '\0'; szHeaderValue = strchr (szBuffer, '\n'); if (szHeaderValue) szHeaderValue[0] = '\0'; /* If there is nothing left (ie: blank line) * the headers are over. */ if (!strcmp (szBuffer, "")) finished = TRUE; } else { /* stream has been closed */ return FALSE; } } return TRUE;}/* * This function extracts an HTTP header name. For example, imagine... * GET http://somewhere/resource.htm HTTP/1.0 * User-Agent: Anything-But-IE * * This function will take in the whole User-Agent line (in * szSourceString) extract just the user-agent part, uppercase it * and return. So you'd end up with "USER-AGENT" in szDestination. * nDest specifies the size of szDestination. */BOOLGetHeaderID (char *szSourceString, char *szDestination, int nDest){ char *colon; /* Find the : */ colon = strchr (szSourceString, ':'); /* If there's no :, it's no header */ if (!colon) return FALSE; /* If we have space in the destination to fit this header */ if ((colon - szSourceString) < nDest) { /* Copy it there */ strncpy (szDestination, szSourceString, colon - szSourceString); szDestination[colon - szSourceString] = '\0'; /* Uppercase it all */ for (colon = szDestination; *colon; colon++) *colon = toupper (*colon); return TRUE; } return FALSE;}/*This function attempts to determine what the hostname was from an HTTPheader request.These requests are normally:GET http://www.someplace.com/someresource HTTP/1.1CONNECT www.someserver.com:80 HTTP/1.0We want 'www.someplace.com'...*/char *GetHostNameFromRequest (char *szLineBuffer){ char *hostend; char *protoend; char *returnvalue; /* Find the // */ protoend = strstr (szLineBuffer, "//"); if (protoend) { /* Jump to AFTER the // */ protoend += 2; /* Find the next / */ hostend = strchr (protoend, '/'); } else { /* If there's no //, it may be a CONNECT request, which * doesn't have one. In that case, look for the first * space. */ protoend = strchr (szLineBuffer, ' '); if (!protoend) return NULL; /* Jump to AFTER the space */ protoend++; /* If there isn't one (CONNECT again) we're looking for the * next space */ hostend = strchr (protoend, ' '); } /* No matter how we tried, if we failed, we failed. */ if (!hostend) return NULL; /* Grab what's in the middle */ returnvalue = (char *) malloc (hostend - protoend + 1); strncpy (returnvalue, protoend, hostend - protoend); returnvalue[hostend - protoend] = '\0'; return returnvalue;}/*This function attempts to determine what the resource was from an HTTPheader request.These requests are normally:GET http://www.someplace.com/someresource HTTP/1.1We want 'someresource'...*/char *GetResourceFromRequest (char *szLineBuffer){ char *hostend; char *nextspace; char *protoend; char *returnvalue; /* We don't need to worry about CONNECT here, because it doens't * specify a resource. */ /* Grab the // */ protoend = strstr (szLineBuffer, "//"); if (!protoend) return NULL; protoend += 2; /* Then find the next / */ hostend = strchr (protoend, '/'); if (!hostend) return NULL; /* Then find the next space */ nextspace = strchr (hostend, ' '); if (!nextspace) return NULL; /* Grab what's between the / and the space */ returnvalue = (char *) malloc (nextspace - hostend + 1); strncpy (returnvalue, hostend, nextspace - hostend); returnvalue[nextspace - hostend] = '\0'; return returnvalue;}/*This function returns true if the request is of the specified method.*/BOOLIsMethod (char *szLineBuffer, char *szMethod){ int ret; int len; len = strlen (szMethod); /* Is the first few characters the same as method? */ ret = strncmp (szLineBuffer, szMethod, len); /* If so, and there's a space immediately afterwards, then * this is the correct method. */ if (ret == 0 && szLineBuffer[len] == ' ') return TRUE; return FALSE;}/*This function returns true if the request is an HTTP request.*/BOOLIsHttp (char *szLineBuffer){ int ret; char *firstspace; /* Find the first space in the HTTP request. */ firstspace = strchr (szLineBuffer, ' '); if (!firstspace) return FALSE; /* Go to what's after the space */ firstspace++; /* Does it begin with http:// ? */ ret = strncmp (firstspace, "http://", 7); /* If yes, it's an http request. */ if (ret == 0) return TRUE; return FALSE;}/*This function returns true if the response is an HTTP response.*/BOOLIsHttpReply (char *szLineBuffer){ int ret; /* Are the first few chars of the response HTTP/1. ? */ ret = strncmp (szLineBuffer, "HTTP/1.", 7); if (ret == 0) return TRUE; return FALSE;}/*This function is used to tell the client if something went wrong.*/BOOLSendFailure (SOCKET stream, int error, char *msg, BOOL bWaitForHeaders){ char szBuffer[768]; char szHTMLBuffer[512]; /* Apparently clients don't like being told they're stupid until * AFTER they finish being stupid. We have to WAIT for ALL the * client headers before telling them their request cannot be * fulfilled. Grr. */ if (bWaitForHeaders) { DiscardHeaders (stream, szBuffer, sizeof (szBuffer) - 1); } /* Format a reply for the client user's grizzling pleasure. */ sprintf (szHTMLBuffer, "<HTML><HEAD><TITLE>%i: %s</TITLE></HEAD><BODY>\r\n" "<H1>%i: %s</H1>\r\n" "<P>This error has been generated by your proxy" " server.</P></BODY></HTML>\r\n", error, msg, error, msg); /* Display it on the console */ sprintf (szBuffer, "HTTP/1.0 %i %s\n" "Content-Type: text/html\n" "Content-Length: %i\n" "\n" "%s", error, msg, strlen (szHTMLBuffer), szHTMLBuffer); printf ("%i: %s (proxy error)\n", error, msg); send_block (stream, szBuffer, strlen (szBuffer)); return TRUE;}/*This function initialises the server.*/BOOLStartServer (){ SOCKADDR_IN sin; int ret; unsigned int yes; struct linger lyes;#ifdef _WIN32_ WSADATA wd; WSAStartup (MAKEWORD (1, 1), &wd);#endif /* */ yes = TRUE; /* Grab a place to listen for connections. */ server = socket (AF_INET, SOCK_STREAM, 0); if (server < 0) { printf ("socket() failed\n"); return FALSE; } /* Don't ignore data received after a close */ lyes.l_onoff = 1; lyes.l_linger = 10; setsockopt (server, SOL_SOCKET, SO_LINGER, (char *) &lyes, sizeof (lyes)); /* If this server has been restarted, don't wait for the old * one to disappear completely */ setsockopt (server, SOL_SOCKET, SO_REUSEADDR, (char *) &yes, sizeof (yes)); /* Listen on IP port 8080, on any interface. This is really bad - you probably only want to listen on the LAN interface, because this server could be used by people on the MODEM accessing your LAN, instead of the other way around ;) There is a hack further down to reduce problems associated with this. */ memset (&sin, 0, sizeof (sin)); sin.sin_family = AF_INET; sin.sin_port = htons (8080); /* FIXME! */ sin.sin_addr.s_addr = INADDR_ANY; ret = bind (server, (SOCKADDR *) & sin, sizeof (sin)); if (ret < 0) { printf ("bind() failed\n"); return FALSE; } /* Start listening for incoming connections. */ ret = listen (server, 5); if (ret == 0) return TRUE; printf ("listen() failed\n"); return FALSE;}/* * As mentioned above, CONNECT is a wierd kind of request. * * The good news is that in normal web browsing you won't see it. * * The bad news is that it's *required* for https connections. * (The worse news is that I hadn't seen it before and don't know * what it's supposed to be doing.) * * This function will be light on comments, because it's pretty much * a guess but I'm leaving it in for when I find out what's right... */BOOLProcessConnect (SOCKET localwebuser, char *szLineBuffer, int nLineBuffer){ SOCKET remotewebserver; char *szHostName; BOOL finished; fd_set fds; int biggest; int len; /* Try to extract the hostname from request. If we can't, somebody is being a prick. */ szHostName = GetHostNameFromRequest (szLineBuffer); if (!szHostName) { SendFailure (localwebuser, 500, "Could not extract hostname - malformed request.", TRUE); return FALSE; } /* Try to connect to the outside world */ remotewebserver = ConnectToHost (szHostName); if (remotewebserver == INVALID_SOCKET) { free (szHostName); SendFailure (localwebuser, 404, "Could not connect to the outside host.", TRUE); return FALSE; } /* Display the response, and request info, for debugging. */ sprintf (szLineBuffer, "%i: %s\n", 200, szHostName); free (szHostName); write (1, szLineBuffer, strlen (szLineBuffer)); /* * Throw away any headers, we don't know what to do with them. */ DiscardHeaders (localwebuser, szLineBuffer, nLineBuffer); /* * Tell the client everything worked fine. */ sprintf (szLineBuffer, "HTTP/1.0 200 Ok\n\n"); send_block (localwebuser, szLineBuffer, strlen (szLineBuffer)); /* * Go into a big loop of forwarding data from one side to the * other. */ finished = FALSE; while (!finished) { FD_ZERO (&fds); FD_SET (localwebuser, &fds); FD_SET (remotewebserver, &fds); if (localwebuser > remotewebserver) biggest = localwebuser; else biggest = remotewebserver; biggest++; /* Wait for message from either side */ select (biggest, &fds, NULL, NULL, NULL); if (FD_ISSET (localwebuser, &fds)) { /* A message from the LAN, forward to the server */ len = recv (localwebuser, szLineBuffer, nLineBuffer - 1, 0); if (len <= 0) { /* Connection closed */ finished = TRUE; } else { szLineBuffer[len] = '\0'; send_block (remotewebserver, szLineBuffer, len); } } if (FD_ISSET (remotewebserver, &fds)) { /* A message from the server, forward to the LAN */ len = recv (remotewebserver, szLineBuffer, nLineBuffer - 1, 0); if (len <= 0) { /* Connection closed */ finished = TRUE; } else { szLineBuffer[len] = '\0'; send_block (localwebuser, szLineBuffer, len); } } } /* Close and finish */ closesocket (remotewebserver); return TRUE;}/* * Confusingly, this function processes both GET and POST requests, but is * called ProcessGet because of history (and to distinguish from CONNECT.) * * localwebuser==network of the local user * szLineBuffer==some space to work with * nLineBuffer==how much space we have to work with */BOOLProcessGet (SOCKET localwebuser, char *szLineBuffer, int nLineBuffer){ BOOL finished; int len; int contentlength; int failcount; SOCKET remotewebserver; char *szHostName; char *szResource; char *szHeaderValue; char szHeaderID[64]; finished = FALSE; /* GET requests only send a request and some headers. POST * also sends some data. Content-length is used to determine * how much data is coming - we need to collect it all. */ contentlength = 0; failcount = 0; /* Test to see if the request is HTTP. If it isn't, bail out. */ if (!IsHttp (szLineBuffer)) { SendFailure (localwebuser, 501, "Only HTTP requests are supported by this proxy.", TRUE); return FALSE; } /* Try to extract the hostname from request. If we can't, somebody is being a prick. */ szHostName = GetHostNameFromRequest (szLineBuffer); if (!szHostName) { SendFailure (localwebuser, 500, "Could not extract hostname - malformed request.", TRUE); return FALSE; } /* Try to extract the resource from request. If we can't, somebody is being a prick. */ szResource = GetResourceFromRequest (szLineBuffer); if (!szResource) { SendFailure (localwebuser, 500, "Could not extract resource - malformed request.", TRUE); free (szHostName); return FALSE; } /* Try to connect to the outside world */ remotewebserver = ConnectToHost (szHostName); if (remotewebserver == INVALID_SOCKET) { free (szHostName); free (szResource); SendFailure (localwebuser, 404, "Could not connect to the outside host.", TRUE); return FALSE; } /* * If we were asked for a POST, we need to also ask our server for * a POST - likewise for GET. Connection: close is used to * tell the server to NOT support keep-alive. We don't support * it and we don't want the server to. */ if (IsMethod (szLineBuffer, "POST")) { sprintf (szLineBuffer, "POST %s HTTP/1.0\r\nConnection: close\r\n", szResource); } else { sprintf (szLineBuffer, "GET %s HTTP/1.1\r\nConnection: close\r\n", szResource); } /* Send the server our request */ send_block (remotewebserver, szLineBuffer, strlen (szLineBuffer)); while (!finished) { /* Now sit here and collect all the headers from the * LAN user. */ strcpy (szLineBuffer, ""); if (GetLineFromSocketWait (localwebuser, szLineBuffer, nLineBuffer)) { failcount = 0; strcpy (szHeaderID, ""); GetHeaderID (szLineBuffer, szHeaderID, sizeof (szHeaderID)); /* If the header is content-length, we need to * record how much content to wait for (POST * support). */ if (!strcmp (szHeaderID, "CONTENT-LENGTH")) { szHeaderValue = strchr (szLineBuffer, ':'); contentlength = atoi (szHeaderValue + 1); } /* Strip of a trailing carriage-return. */ szHeaderValue = strchr (szLineBuffer, '\r'); if (szHeaderValue) szHeaderValue[0] = '\0'; szHeaderValue = strchr (szLineBuffer, '\n'); if (szHeaderValue) szHeaderValue[0] = '\0'; /* If there is nothing left (ie: blank line) * the headers are over. */ if (!strcmp (szLineBuffer, "")) finished = TRUE; /* Send on all headers *except* those containing * the word "Proxy." Those are meant for US, but * we're too stupid to know what to do, so we * ignore them *all*. */ if ((!strstr (szHeaderID, "PROXY")) && (strcmp (szHeaderID, "KEEP-ALIVE"))) { strcat (szLineBuffer, "\r\n"); send_block (remotewebserver, szLineBuffer, strlen (szLineBuffer)); } } else { /* recv returning zero means the client * disconnected. So we should clean up and abort. */ free (szHostName); free (szResource); closesocket (remotewebserver); return FALSE; } } /* * If we have any content to wait for (POST only) we keep * reading until we have it all, and pass it on verbatim. */ while (contentlength > 0) { len = recv (localwebuser, szLineBuffer, nLineBuffer, 0); if (len > 0) { contentlength -= len; send_block (remotewebserver, szLineBuffer, len); } else { /* recv returning zero means the client * disconnected. So we should clean up and abort. */ free (szHostName); free (szResource); closesocket (remotewebserver); return FALSE; } } /* Take the first line from the webserver. */ strcpy (szLineBuffer, ""); GetLineFromSocketWait (remotewebserver, szLineBuffer, nLineBuffer); if (!IsHttpReply (szLineBuffer)) { /* If it's not an HTTP reply or something went wrong, * abort. */ free (szHostName); free (szResource); closesocket (remotewebserver); SendFailure (localwebuser, 500, "Outside host not HTTP?", FALSE); return FALSE; } /* The error code is 9 chars into the response line */ len = atoi (szLineBuffer + 9); /* Display the response, and request info, for debugging. */ sprintf (szLineBuffer, "%i: %s: %s\n", len, szHostName, szResource); free (szHostName); free (szResource); write (1, szLineBuffer, strlen (szLineBuffer)); /* Respond to the LAN client - with the same error */ sprintf (szLineBuffer, "HTTP/1.0 %i %i\n", len, len); send_block (localwebuser, szLineBuffer, strlen (szLineBuffer)); finished = FALSE; /* Read in a pile of data from the web server (this is your page.) */ while ((len = recv (remotewebserver, szLineBuffer, nLineBuffer - 1, 0)) > 0) { /* Send it to the client */ send_block (localwebuser, szLineBuffer, len); } /* We don't care about the outside, it's over */ closesocket (remotewebserver); return TRUE;}void *ChildThread (void *lpData){ SOCKET localwebuser; char szLineBuffer[1024]; localwebuser = (*(SOCKET *) lpData); free (lpData);#if (!defined(_WIN32_))&&defined(THREADING_SUPPORT) pthread_detach (pthread_self ());#endif /* Read in the first line from the request */ if (!GetLineFromSocketWait (localwebuser, szLineBuffer, sizeof (szLineBuffer))) goto barfed; /* Find out what the method is. If it's GET or POST we can handle * it - if it's CONNECT, we try, but as mentioned above, I have no * idea what to do here. */ if (IsMethod (szLineBuffer, "GET") || IsMethod (szLineBuffer, "POST")) { ProcessGet (localwebuser, szLineBuffer, sizeof (szLineBuffer)); } else { if (IsMethod (szLineBuffer, "CONNECT")) { ProcessConnect (localwebuser, szLineBuffer, sizeof (szLineBuffer)); } else { SendFailure (localwebuser, 501, "Only GET, POST and CONNECT requests are supported by this proxy.", TRUE); goto barfed; } } barfed: /* We even don't care about the inside LAN user. If they want us again, they'll call again. */ closesocket (localwebuser); strcpy (szLineBuffer, ""); return NULL;}/*This big messy function processes each incoming request.*/BOOLHandleRequest (){ SOCKET localwebuser; SOCKET *threadcopy; SOCKADDR_IN sin; SOCKLEN_T slt; struct linger lyes; unsigned long ip;#if defined(_WIN32_)&&defined(THREADING_SUPPORT) HANDLE hThread; DWORD tid;#endif#if (!defined(_WIN32_))&&defined(THREADING_SUPPORT) pthread_t tid;#endif slt = sizeof (sin); /* Accept an incoming request, or wait till one arrives. */ printf ("Accepting new requests\n"); localwebuser = accept (server, (SOCKADDR *) & sin, &slt); /* Security hack. Find out who is talking to us, and if they're * not 127.0.0.1 (7f000001 in hex) or between 192.168.0.0 and * 192.168.255.255 (0xc0a80000 and 0xc0a8ffff) then our proxy is * not for them. Hang them up, without warning. */ ip = ntohl (sin.sin_addr.s_addr); if ((ip != 0x7f000001) && ((ip > 0xc0a8ffff) || (ip < 0xc0a80000))) { closesocket (localwebuser); return FALSE; } /* Linger - as described above. */ lyes.l_onoff = 1; lyes.l_linger = 10; setsockopt (localwebuser, SOL_SOCKET, SO_LINGER, (char *) &lyes, sizeof (lyes)); threadcopy = malloc (sizeof (localwebuser)); *threadcopy = localwebuser;#ifndef THREADING_SUPPORT ChildThread (threadcopy); printf ("No thread support\n");#endif#if defined(_WIN32_)&&defined(THREADING_SUPPORT) hThread = CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE) ChildThread, threadcopy, 0, &tid); CloseHandle (hThread);#endif#if (!defined(_WIN32_))&&defined(THREADING_SUPPORT) pthread_create (&tid, NULL, ChildThread, threadcopy);#endif return TRUE;}#ifndef _WIN32_/*UNIX trivia - when is a problem a problem? When you don't ignore it.If you do nothing, well, you're not being ignorant enough. You haveto be explicitly ignorant.*/voidignorer (int x){ signal (SIGPIPE, ignorer);}#endif /* !_WIN32_ *//*main() - you've seen that before.*/intmain (){#ifndef _WIN32_ signal (SIGPIPE, ignorer);#endif /* !_WIN32_ */ if (!StartServer ()) { printf ("Could not initialise server.\n"); return (EXIT_FAILURE); } while (TRUE) { if (!HandleRequest ()) { printf ("Proxy server couldn't handle request.\n"); } } return EXIT_SUCCESS;}#ifdef _WIN32_/* * WinMain is used to bootstrap a GUI app in Windoze. I'm using this so * that proxy can be available in a version with no visible trace ;) */int WINAPIWinMain (HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR szCmd, int nCmdShow){ main ();}#endif /* _WIN32_ */