/***************************************************************************** * * 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; }