proxy.c
资源名称:socks5.zip [点击查看]
上传用户:sddyfurun
上传日期:2007-01-04
资源大小:525k
文件大小:17k
源码类别:
代理服务器
开发平台:
Unix_Linux
- /* Copyright (c) 1995,1996,1997 NEC Corporation. All rights reserved. */
- /* */
- /* The redistribution, use and modification in source or binary forms of */
- /* this software is subject to the conditions set forth in the copyright */
- /* document ("Copyright") included with this distribution. */
- /*
- * $Id: proxy.c,v 1.94.2.2.2.5 1998/11/04 00:03:37 steve Exp $
- */
- #include "socks5p.h"
- #include "daemon.h"
- #include "validate.h"
- #include "protocol.h"
- #include "msgids.h"
- #include "null.h"
- #include "info.h"
- #include "log.h"
- #include "msg.h"
- #include "upwd.h"
- #include "gss.h"
- #include "tcp.h"
- #include "udp.h"
- #include "tracer.h"
- #include "packet.h"
- #ifndef ESTABLISH_TIMEOUT
- #define ESTABLISH_TIMEOUT 10
- #endif
- #ifndef START_TIMEOUT
- #define START_TIMEOUT 60
- #endif
- #ifdef MONITOR
- #include "monSocks.h"
- #define NAMEORNULL(x) ((*(x) == ' ')?NULL:(x))
- #define SINADDR(x) (&(x)->sin)
- typedef S5MonHandle MONH_TYPE;
- #define MON_OPEN(x) S5SocksMonOpen("Socks5 Monitor", S5_MON_MEM, (x))
- #define MON_CLOSE(x) S5SocksMonClose((x))
- #define MON_START(x, y) S5SocksMonPut((x), S5_LINK_START, (y), 0, NULL, 0, NULL)
- #define MON_UPDATE(x, y, i, o) S5SocksMonPut((x), S5_LINK_UPDATE, (y), 0, NULL, 0, NULL)
- #define MON_ERROR(x, y, v, m) S5SocksMonPut((x), S5_LINK_ERROR, (y), 0, NULL, (v), (m))
- #define MON_END(x, y) S5SocksMonPut((x), S5_LINK_END, (y), 0, NULL, 0, NULL)
- #endif
- #define PROXY_IOFLAGS S5_IOFLAGS_TIMED|S5_IOFLAGS_NBYTES|S5_IOFLAGS_RESTART
- static int HandleS4Connection(S5LinkInfo *pri, S5IOInfo *iio, list *auths, double *timerm) {
- #ifdef FORK_SOCKD
- dup2(pri->in, 0);
- dup2(pri->in, 1);
- dup2(pri->in, 2);
- close(pri->in);
- execlp("sockd", "sockd", NULL);
- return EXIT_ERR;
- #else
- char buf[256+256+8], *tmp, resp[] = { SOCKS4_VERSION, SOCKS_FAIL, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff };
- S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(10), 0, "Proxy: Acting as a Socks4 server");
- if (auths) {
- for ( ;auths; auths = auths->next) {
- if (auths->dataint == AUTH_NONE || auths->dataint == AUTH_PASSWD) break;
- }
- if (!auths) {
- S5LogUpdate(S5LogDefaultHandle, S5_LOG_WARNING, MSGID_SERVER_AUTH_NONE, "Socks%d: No acceptable authentication method found", (int)pri->peerVersion);
- if (S5IOSend(iio->fd, NULL, resp, sizeof(resp), 0, PROXY_IOFLAGS, timerm) != sizeof(resp)) return EXIT_ERR;
- return EXIT_AUTH;
- }
- }
- if (S5IORecv(iio->fd, NULL, buf, 8, 0, PROXY_IOFLAGS, timerm) != 8) {
- S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(0), 0, "Socks4: Read failed: %m");
- return EXIT_ERR;
- }
- pri->peerAuth = AUTH_NONE;
- pri->peerVersion = buf[SP_VERSION];
- pri->peerCommand = buf[SP_COMMAND];
- if (lsGetProtoAddr(SOCKS4_VERSION, buf, &pri->dstAddr) < 0) {
- S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(0), 0, "Socks4: Invalid address passed from client");
- return EXIT_ERR;
- }
- for (tmp = buf, *tmp = ' '; tmp < buf+sizeof(buf)-1; *++tmp = ' ') {
- if (S5IORecv(iio->fd, iio, tmp, 1, 0, PROXY_IOFLAGS, timerm) != 1) {
- S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(0), 0, "Socks4: Read failed: %m");
- return EXIT_ERR;
- }
- if (*tmp == ' ') break;
- }
- S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(10), 0, "Socks4: Read user: %s", buf);
- strcpy(pri->srcUser, buf);
- if (lsNullSrvAuth(iio->fd, &iio->auth, pri->srcUser) != AUTH_OK) {
- S5LogUpdate(S5LogDefaultHandle, S5_LOG_WARNING, MSGID_SERVER_AUTH_FAILED, "Socks%d: Authentication method %d failed", (int)pri->peerVersion, 0);
- resp[2] = SOCKS_BAD_ID;
- if (S5IOSend(iio->fd, NULL, resp, sizeof(resp), 0, PROXY_IOFLAGS, timerm) != sizeof(resp)) return EXIT_ERR;
- return EXIT_AUTH;
- }
- /* If we haven't gotten authentication specific read and write functions */
- /* we should set the client functions to be "normal", rather than timed */
- /* Since in all likelyhood, it will take a long time to read in a whole */
- /* buffers worth of data (in fact it may never happen)... */
- if (pri->peerCommand != SOCKS_CONNECT && pri->peerCommand != SOCKS_BIND) {
- S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(0), MSGID_SERVER_BAD_COMMAND, "Socks%d: Invalid command: %d", (int)pri->peerVersion, (int)pri->peerCommand);
- S5IOSend(iio->fd, NULL, resp, sizeof(resp), 0, PROXY_IOFLAGS, timerm);
- return EXIT_ERR;
- }
- S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(10), 0, "Socks4: Done initialization");
- return 0;
- #endif
- }
- static int AuthOK(u_char auth) {
- switch (auth) {
- #ifdef HAVE_LIBGSSAPI_KRB5
- case AUTH_GSSAPI:
- #endif
- case AUTH_PASSWD:
- case AUTH_NONE: return 1;
- default: return 0;
- }
- }
- static int HandleS5Connection(S5LinkInfo *pri, S5IOInfo *iio, list *auths, double *timerm) {
- char fail[] = { 0x05, (char)0xff }, buf[256+2];
- int ret, i;
- list *tl;
- if (S5IORecv(iio->fd, iio, buf, 2, 0, PROXY_IOFLAGS, timerm) != 2) {
- S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(0), 0, "Socks5: Read failed: %m");
- return EXIT_ERR;
- }
- if (S5IORecv(iio->fd, iio, buf+2, (u_char)buf[1], 0, PROXY_IOFLAGS, timerm) != (u_char)buf[1]) {
- S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(0), 0, "Socks5: Read failed: %m");
- return EXIT_ERR;
- }
- pri->peerAuth = 0xff;
- if (!auths) {
- /* Anything is ok, pick the first one the client wanted to do... */
- for (i = 0; i < ((int)(u_char)buf[1]); i++) if (AuthOK((u_char)buf[i+2])) break;
- if (i != ((int)(u_char)buf[1])) pri->peerAuth = (u_char)(buf[i+2]);
- } else for (tl = auths; tl; tl = tl->next) {
- /* For each method (in order) in the server's config file, See if */
- /* the client wanted to do this method...And, of course, make sure */
- /* we can do it... */
- for (i = 0; i < ((int)(u_char)buf[1]); i++) if (tl->dataint == (int)(u_char)buf[i+2]) break;
- if (i == (int)(u_char)buf[1] || !AuthOK((u_char)buf[i+2])) continue;
- pri->peerAuth = (u_char)buf[i+2];
- break;
- }
- buf[1] = (u_char)pri->peerAuth;
- if (S5IOSend(iio->fd, iio, buf, 2, 0, PROXY_IOFLAGS, timerm) != 2) {
- S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(0), 0, "Socks5: Write failed: %m");
- return EXIT_ERR;
- }
- if (pri->peerAuth == 0xff) {
- S5LogUpdate(S5LogDefaultHandle, S5_LOG_WARNING, MSGID_SERVER_AUTH_NONE, "Socks%d: No acceptable authentication method found", (int)pri->peerVersion);
- return EXIT_AUTH;
- }
- S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(10), 0, "Socks5: Told client to do authentication method #%d", (int)pri->peerAuth);
- switch (pri->peerAuth) {
- case AUTH_PASSWD: ret = lsPasswdSrvAuth(iio->fd, &iio->auth, pri->srcUser); break;
- case AUTH_GSSAPI: ret = lsGssapiSrvAuth(iio->fd, &iio->auth, pri->srcUser); break;
- case AUTH_NONE: ret = lsNullSrvAuth (iio->fd, &iio->auth, pri->srcUser); break;
- default:
- S5LogUpdate(S5LogDefaultHandle, S5_LOG_WARNING, MSGID_SERVER_AUTH_BAD, "Socks%d: Bad Authentication method number: %d", (int)pri->peerVersion, (int)pri->peerAuth);
- S5IOSend(iio->fd, iio, fail, sizeof(fail), 0, PROXY_IOFLAGS, timerm);
- ret = AUTH_FAIL;
- }
- if (ret == AUTH_FAIL) {
- S5LogUpdate(S5LogDefaultHandle, S5_LOG_WARNING, MSGID_SERVER_AUTH_FAILED, "Socks%d: Authentication method %d failed", (int)pri->peerVersion, (int)pri->peerAuth);
- return EXIT_AUTH;
- }
- if (lsReadRequest(iio->fd, iio, &pri->dstAddr, &pri->peerVersion, &pri->peerCommand, &pri->peerReserved) < 0) {
- S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(0), 0, "Socks5: Read request failed: %m");
- return EXIT_ERR;
- }
- return EXIT_OK;
- }
- /* HandlePxyConnection will be the routine for Socks5, which handles the */
- /* initial part of the connection. Specifically, we want to do several */
- /* things here. We want to check the version, if its not version 5, we can */
- /* either exec the old sockd, or we can call a separate routine to handle */
- /* the connection establishment. (That routine should read the name, and */
- /* possible make an ident query). If the version is version 5, we have to */
- /* decide what kind of authentication we are going to do. This would mean */
- /* reading the number of methods, and then the method numbers themselves, */
- /* after that we pick the one we'd like to do, and send back a reply with */
- /* my version (5), and the method number we picked, or 0xff if we couldn't */
- /* find one that was acceptable given the information we already have (the */
- /* source host.) We probably want to drop the connection immediately if we */
- /* know there is no acceptable auth from a host, so we don't waste fd's. */
- /* after all this is done, we read the command, then finally do it. Well, */
- /* if we haven't already quit, that is. */
- int HandlePxyConnection(S5IOHandle fd) {
- char buf[] = { SOCKS5_VERSION, SOCKS5_FAIL, 0, SOCKS5_IPV4ADDR, 0, 0, 0, 0, 0, 0 };
- int action, dir, rval = EXIT_ERR, len = sizeof(S5NetAddr), cleaned = 0;
- double timerm = (double)START_TIMEOUT;
- list *auths;
- S5Packet inPacket, outPacket;
- S5CommandInfo ci;
- S5FilterInfo fi;
- S5NetAddr route;
- S5LinkInfo li;
- S5IOInfo iio;
- #ifdef MONITOR
- static MONH_TYPE smonHandle = NULL;
- MONH_TYPE monHandle = NULL;
- MONH_TYPE *mhp = NULL;
- int monStarted = 0;
- #endif
- memset(&li, 0, sizeof(S5LinkInfo));
- memset(&fi, 0, sizeof(S5FilterInfo));
- memset(&ci, 0, sizeof(S5CommandInfo));
- S5BufSetupContext(&iio);
- iio.fd = fd;
- if (getpeername(iio.fd, (ss *)&li.srcAddr, &len) < 0) {
- S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(0), 0, "Proxy: getpeername failed: %m");
- goto cleanup;
- }
- len = sizeof(S5NetAddr);
- if (getsockname(iio.fd, (ss *)&li.bndAddr, &len) < 0) {
- S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(0), 0, "Proxy: getsockname failed: %m");
- goto cleanup;
- }
- GetName(li.srcName, &li.srcAddr);
- if (!GetRoute(&li.srcAddr, li.srcName, "tcp", &route)) {
- /* If somehow we got a connection from somewhere on an interface which */
- /* we wouldn't use to get to that same place, this is bad (IP SPOOF?), */
- /* so we'll quit. */
- if (route.sin.sin_addr.s_addr != INADDR_ANY &&
- route.sin.sin_addr.s_addr != li.bndAddr.sin.sin_addr.s_addr) {
- S5LogUpdate(S5LogDefaultHandle, S5_LOG_WARNING, MSGID_SERVER_WRONG_ROUTE, "Proxy: Received connection via wrong route");
- S5LogUpdate(S5LogDefaultHandle, S5_LOG_INFO, MSGID_SERVER_AUTH_FAILED, "Auth Failed: (%s:%d)", li.srcName, (int)ntohs(lsAddr2Port(&li.srcAddr)));
- goto cleanup;
- }
- }
- /* If this host was "banned", quit... */
- if ((GetAuths(&li, &auths)) < 0) {
- S5LogUpdate(S5LogDefaultHandle, S5_LOG_WARNING, MSGID_SERVER_BANNED_HOST, "Proxy: Received connection from banned host");
- S5LogUpdate(S5LogDefaultHandle, S5_LOG_INFO, MSGID_SERVER_AUTH_FAILED, "Auth Failed: (%s:%d)", li.srcName, (int)ntohs(lsAddr2Port(&li.srcAddr)));
- goto cleanup;
- }
- if ((S5IORecv(iio.fd, NULL, (char *)&li.peerVersion, 1, MSG_PEEK, PROXY_IOFLAGS, &timerm)) != 1) {
- S5LogUpdate(S5LogDefaultHandle, S5_LOG_WARNING, MSGID_SERVER_RECV_VERSION, "Proxy: Unable to determine version number: %m");
- goto cleanup;
- }
- timerm = (double)ESTABLISH_TIMEOUT;
- switch (li.peerVersion) {
- case SOCKS4_VERSION:
- rval = HandleS4Connection(&li, &iio, auths, &timerm);
- break;
- case SOCKS5_VERSION:
- rval = HandleS5Connection(&li, &iio, auths, &timerm);
- break;
- default:
- S5LogUpdate(S5LogDefaultHandle, S5_LOG_WARNING, MSGID_SERVER_BAD_VERSION, "Proxy: Received request with incompatible version number: %d", (int)li.peerVersion);
- S5IOSend(iio.fd, NULL, buf, sizeof(buf), 0, PROXY_IOFLAGS, &timerm);
- rval = EXIT_ERR;
- }
- #ifdef MONITOR
- if (servermode == PREFORKING) mhp = &smonHandle;
- else mhp = &monHandle;
- if (!*mhp) MON_OPEN(mhp);
- if (*mhp) {
- S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(10), 0, "Proxy: Starting monitor");
- if (!MON_START(*mhp, &li)) {
- monStarted = 1;
- S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(10), 0, "Proxy: Monitor started");
- }
- else
- S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(10), 0, "Proxy: Monitor not started
- ");
- }
- #endif
- if (rval < 0) {
- S5LogUpdate(S5LogDefaultHandle, S5_LOG_INFO, MSGID_SERVER_AUTH_FAILED, "Auth Failed: (%s:%d)", li.srcName, (int)ntohs(lsAddr2Port(&li.srcAddr)));
- #ifdef MONITOR
- if (!monStarted) goto cleanup;
- if (rval == EXIT_AUTH) monStarted += MON_ERROR(*mhp, &li, 1, "Authentication failed");
- else monStarted += MON_ERROR(*mhp, &li, 0, "Protocol Initialization failed");
- #endif
- goto cleanup;
- }
- S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(10), 0, "Proxy: vers:%d cmnd:%d addr:%s port:%d user:%s",
- (int)li.peerVersion, (int)li.peerCommand, ADDRANDPORT(&li.dstAddr), li.srcUser);
- /* If we haven't gotten authentication specific read and write functions */
- /* we should set the client functions to be "normal", rather than timed */
- /* Since in all likelyhood, it will take a long time to read in a whole */
- /* buffers worth of data (in fact it may never happen)... */
- switch (li.peerCommand) {
- case SOCKS_PING:
- case SOCKS_TRACER: rval = PTSetup (&iio, &li, &ci); break;
- case SOCKS_CONNECT:
- case SOCKS_BIND: rval = TcpSetup(&iio, &li, &ci); break;
- case SOCKS_UDP: rval = UdpSetup(&iio, &li, &ci); break;
- default:
- S5LogUpdate(S5LogDefaultHandle, S5_LOG_WARNING, MSGID_SERVER_BAD_COMMAND, "Proxy: Bad command number %d", (int)li.peerCommand);
- lsSendResponse(iio.fd, &iio, &li.dstAddr, li.peerVersion, (li.peerVersion == SOCKS5_VERSION)?SOCKS5_BADCMND:SOCKS_BAD_ID, 0, NULL);
- S5BufCleanContext(&iio);
- rval = -1;
- }
- if (rval < 0) {
- S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(10), 0, "Proxy: Command setup failed");
- cleaned = 1;
- #ifdef MONITOR
- if (!monStarted) goto cleanup;
- if (rval == EXIT_AUTH) monStarted += MON_ERROR(*mhp, &li, 1, "Authorization failed");
- else monStarted += MON_ERROR(*mhp, &li, 0, "Command Setup failed");
- #endif
- goto cleanup;
- }
- PacketPrintSetup(&li, &fi);
- inPacket.data = NULL;
- outPacket.data = NULL;
- inPacket.oob = 0;
- outPacket.oob = 0;
- for (action = S5_ACTION_READ, dir = S5_DIRECTION_ANY;; ) {
- switch (action) {
- case S5_ACTION_READ:
- inPacket.off = 0;
- case S5_ACTION_MORE_READ:
- /* Need more info... */
- if ((rval = ci.recvpkt(&inPacket, &li, ci.option, &dir)) <= 0) {
- /* but we had an error... */
- action = S5_ACTION_CLOSE;
- continue;
- } else {
- /* Update the byte count on the read not the write? */
- if (dir == S5_DIRECTION_IN) {
- li.inbc += rval;
- #ifdef MONITOR
- if (monStarted) monStarted += MON_UPDATE(*mhp, &li, 0, rval);
- #endif
- } else {
- li.outbc += rval;
- #ifdef MONITOR
- if (monStarted) monStarted += MON_UPDATE(*mhp, &li, rval, 0);
- #endif
- }
- break;
- }
- case S5_ACTION_WRITE:
- /* Just write... */
- if (ci.sendpkt(&outPacket, &li, ci.option, &dir) <= 0) {
- action = S5_ACTION_CLOSE;
- continue;
- } else {
- /* Go back to reading... */
- dir = S5_DIRECTION_ANY;
- action = S5_ACTION_READ;
- continue;
- }
- case S5_ACTION_MORE_WRITE:
- if (ci.sendpkt(&outPacket, &li, ci.option, &dir) <= 0) {
- action = S5_ACTION_CLOSE;
- continue;
- } else {
- break;
- }
- case S5_ACTION_CLOSE:
- /* Any error messages that need to get printed must be done */
- /* within ci.clean */
- goto cleanup;
- }
- /* Only valid way of getting here is via S5_ACTION_READ */
- if (fi.filter && !fi.filter(&inPacket, &outPacket, &li, fi.option, &dir, &action)) continue;
- action = S5_ACTION_WRITE;
- outPacket = inPacket;
- }
- cleanup:
- #ifdef MONITOR
- if (monStarted) {
- S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(10), 0, "Proxy: sending monitor end message");
- monStarted += MON_END(*mhp, &li);
- }
- if (*mhp && ((mhp != &smonHandle) || !monStarted)) {
- S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(10), 0, "Proxy: closing monitor handle");
- MON_CLOSE(*mhp);
- }
- #endif
- if (ci.option && ci.clean) {
- S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(10), 0, "Proxy: cleaning command context");
- ci.clean(&li, ci.option);
- cleaned = 1;
- }
- if (!cleaned) S5BufCleanContext(&iio);
- if (fi.clean) {
- S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(10), 0, "Proxy: cleaning filter context");
- fi.clean(fi.option);
- }
- S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(10), 0, "Proxy: done cleaning up");
- return rval;
- }