|
|
/*****************************************************************************
* * PROXYcc * * Copyright (c) 1997 Microsoft Corporation. All Rights Reserved. * * Abstract: * * Does all the bookkeeping part of proxying. * *****************************************************************************/
#include "msnspa.h"
/*****************************************************************************
* * init_send_socket * * Create a socket that talks to the real world. * *****************************************************************************/
SOCKET INTERNAL init_send_socket(SOCKET scfd, LPCSTR pszHost, u_short port, LPCSTR pszErrMsg) { SOCKET s; struct hostent *phe; struct sockaddr_in saddr;
/*
* Find out who the target is. */ ZeroMemory(&saddr, sizeof(saddr)); phe = gethostbyname(pszHost);
if (!phe) { Squirt("Couldn't build address of gateway"); send(scfd, pszErrMsg, lstrlen(pszErrMsg), 0); s = INVALID_SOCKET; goto done; }
/*
* Build the socket address packet for the open. */ saddr.sin_family = AF_INET; saddr.sin_port = htons(port); CopyMemory(&saddr.sin_addr, phe->h_addr, phe->h_length);
/*
* Open sesame. */ s = socket(AF_INET, SOCK_STREAM, 0); if (s == INVALID_SOCKET) { Squirt("Couldn't create send socket\r\n"); send(scfd, pszErrMsg, lstrlen(pszErrMsg), 0); s = INVALID_SOCKET; goto done; }
/*
* One ringy-dingy... */ if (connect(s, (struct sockaddr *)&saddr, sizeof(saddr))) { Squirt("Couldn't connect"); closesocket(s); send(scfd, pszErrMsg, lstrlen(pszErrMsg), 0); s = INVALID_SOCKET; goto done; }
done:; return s;
}
/*****************************************************************************
* * set_sock_opt_int * * Set an integer socket option or die trying. * *****************************************************************************/
void set_sock_opt_int(SOCKET s, int optname, int val) { if (setsockopt(s, SOL_SOCKET, optname, (PV)&val, sizeof(val)) == -1) { Die("set sock opt"); } }
/*****************************************************************************
* * create_listen_socket * * Start listening on a port. * *****************************************************************************/
SOCKET INTERNAL create_listen_socket(u_short port) { SOCKET isckdes; struct hostent *phe; /* my host entry table */ struct sockaddr_in saddr; /* my socket address */ char hostname[64];
/*
* Find out who I am. */ gethostname(hostname, 64); phe = gethostbyname(hostname); /* Get my own hostent */
if (!phe) { Die("Couldn't build address of localhost"); return INVALID_SOCKET; }
/*
* Build the socket address packet for the open. */ ZeroMemory(&saddr, sizeof(saddr)); /* start fresh */ CopyMemory(&saddr.sin_addr, phe->h_addr, phe->h_length); /* Copy the IP */
saddr.sin_family = AF_INET; saddr.sin_addr.s_addr = htonl(INADDR_ANY); saddr.sin_port = htons(port); /* Listen on this port */
/*
* Open sesame. */ isckdes = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (isckdes == INVALID_SOCKET) { Die("Couldn't create listen socket"); return INVALID_SOCKET; }
/*
* Set some socket options. */ set_sock_opt_int(isckdes, SO_REUSEADDR, 1); set_sock_opt_int(isckdes, SO_KEEPALIVE, 1);
/*
* All right, let's bind to it already. */ if (bind(isckdes, (struct sockaddr *)&saddr, sizeof(saddr)) == -1) { Die("Couldn't bind to recv. socket"); }
return isckdes; }
/*****************************************************************************
* * ProxyPeekCommand * * Study the incoming command to see if it is something we * have a canned response to. * *****************************************************************************/
BOOL INTERNAL ProxyPeekCommand(PCONNECTIONSTATE pcxs) { PPROXYINFO pproxy = pcxs->pproxy;
/*
* Now peek to see if we got a specific ignorable * four-letter command from * the client. If so, then spit back the canned response. */ if (pcxs->nread > 4 && pcxs->buf[4] == ' ') { char szWord[5]; szWord[0] = pcxs->buf[0]; szWord[1] = pcxs->buf[1]; szWord[2] = pcxs->buf[2]; szWord[3] = pcxs->buf[3]; szWord[4] = 0;
/*
* Are the first four letters an ignored command? */ if (lstrcmpi(szWord, pproxy->szIgnore1) == 0 || lstrcmpi(szWord, pproxy->szIgnore2) == 0) {
/*
* Then spit back the canned response. */
if (sendsz(pcxs->scfd, pproxy->pszResponse) == SOCKET_ERROR) { Squirt("Write failed" EOL); } return TRUE;
} }
return FALSE; }
/*****************************************************************************
* * PROXYTHREADSTATE * * Tiny chunk of memory used to transfer proxy state between * the ProxyThread() and the ProxyWorkerThread(). * *****************************************************************************/
typedef struct PROXYTHREADSTATE { PPROXYINFO pproxy; /* Who we are */ SOCKET scfd; /* Newly-accepted socket to client */ } PROXYTHREADSTATE, *PPROXYTHREADSTATE;
/*****************************************************************************
* * ProxyWorkerThread * * Hold two phones together. * *****************************************************************************/
DWORD WINAPI ProxyWorkerThread(LPVOID pvRef) { PPROXYTHREADSTATE ppts = pvRef; CONNECTIONSTATE cxs;
cxs.scfd = ppts->scfd; cxs.pproxy = ppts->pproxy;
LocalFree(ppts);
Squirt("Connection %d..." EOL, GetCurrentThreadId());
++*cxs.pproxy->piUsers; UI_UpdateCounts();
/* open the target socket */ cxs.ssfd = init_send_socket(cxs.scfd, cxs.pproxy->pszHost, cxs.pproxy->serverport, cxs.pproxy->pszError);
if (cxs.ssfd != INVALID_SOCKET) { #if 0
Squirt("ssfd = %d; scfd = %d, &ssfd = %08x" EOL, cxs.ssfd, cxs.scfd, &cxs.ssfd); #endif
if (!cxs.pproxy->Negotiate(cxs.ssfd)) { sendsz(cxs.scfd, cxs.pproxy->pszErrorPwd); goto byebye; }
sendsz(cxs.scfd, cxs.pproxy->pszResponse);
for (;;) { fd_set fdrd, fder; SOCKET sfrom, sto;
fdrd.fd_count = 2; fdrd.fd_array[0] = cxs.ssfd; fdrd.fd_array[1] = cxs.scfd;
fder.fd_count = 2; fder.fd_array[0] = cxs.ssfd; fder.fd_array[1] = cxs.scfd;
cxs.nread = select(32, &fdrd, 0, &fder, 0);
if (cxs.nread != SOCKET_ERROR) { char *ptszSrc; char *ptszDst;
if (fder.fd_count) { /* error on a socket, e.g., EOF */ break; /* outta here */ }
if (fdrd.fd_count == 0) { /* Huh?? */ continue; }
if (fdrd.fd_array[0] == cxs.scfd) { sfrom = cxs.scfd; sto = cxs.ssfd; } else if (fdrd.fd_array[0] == cxs.ssfd) { sfrom = cxs.ssfd; sto = cxs.scfd; } else { continue; }
cxs.nread = recv(sfrom, cxs.buf, BUFSIZE, 0); /* read a hunk */
if (cxs.nread > 0) {
/*
* If it's from the client, then peek at it * in case we need to munge it. */ if (sfrom == cxs.scfd) { if (ProxyPeekCommand(&cxs)) { continue; } }
if (send(sto, cxs.buf, cxs.nread, 0) == SOCKET_ERROR) { Squirt("Write failed" EOL); } #ifdef DBG
cxs.buf[cxs.nread] = 0; if (sto == cxs.scfd) { /*
* Walk the buffer studying each line. */ int ich = 0; while (ich < cxs.nread) { int ichEnd; DWORD dwFirst;
for (ichEnd = ich; ichEnd < cxs.nread && cxs.buf[ichEnd] != '\n'; ichEnd++) { }
dwFirst = *(LPDWORD)&cxs.buf[ich]; #define PLUSOK 0x004B4F2B
#define DASHERR 0x5252452D
#define SUBJECT 0x6A627553
if ((dwFirst & 0x00FFFFFF) == PLUSOK || dwFirst == DASHERR || dwFirst == SUBJECT) {
cxs.buf[ichEnd] = 0; Squirt("<%s\n", &cxs.buf[ich]); }
ich = ichEnd + 1; }
} else { Squirt(">%s", cxs.buf); }
#endif
} else { /* EOF */ break; } } else { /* Panic */ Squirt("select %d", WSAGetLastError()); break; } } byebye:; Sleep(250); /* wait for socket to drain */ closesocket(cxs.ssfd); } closesocket(cxs.scfd); Squirt("End connection %d..." EOL, GetCurrentThreadId());
--*cxs.pproxy->piUsers; UI_UpdateCounts();
return 0; }
/*****************************************************************************
* * ProxyThread * * Thread procedure for proxies. * *****************************************************************************/
DWORD CALLBACK ProxyThread(LPVOID pvRef) { PPROXYINFO pproxy = pvRef; SOCKET ic_sck; SOCKET scfd;
ic_sck = create_listen_socket(pproxy->localport);
for (;;) { HANDLE hThread; DWORD dwThid; PPROXYTHREADSTATE ppts;
Squirt("listening..." EOL); if (listen(ic_sck, SOMAXCONN) == -1) { Squirt("listen failed %d" EOL, WSAGetLastError()); // APPCOMPAT: For Win95, Close the socket and try again
closesocket(ic_sck); ic_sck = create_listen_socket(pproxy->localport); continue; }
/*
* OLD COMMENT * * We ought to put a timeout in here, and then * if there are no connections, reap any zombies * and go back to listening... so if we've blocked some * sockets other people don't get refused... -- mikeg */
Squirt("accept waiting..." EOL); scfd = accept(ic_sck, NULL, NULL); /* wait for a connection */
if (scfd == INVALID_SOCKET) { Squirt("accept failed %d" EOL, WSAGetLastError()); break; }
ppts = LocalAlloc(LMEM_FIXED, sizeof(PROXYTHREADSTATE)); if (ppts) { ppts->pproxy = pproxy; ppts->scfd = scfd;
hThread = CreateThread(0, 0, ProxyWorkerThread, ppts, 0, &dwThid); if (hThread) { CloseHandle(hThread); } else { Squirt("Can't spawn worker thread; tossing connection" EOL); closesocket(scfd); } } else { Squirt("Out of memory; tossing connection" EOL); closesocket(scfd); } } closesocket(ic_sck); return 0; }
|