home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 2 / AACD 2.iso / AACD / Online / Socks5 / src / server / proxy.c < prev    next >
Encoding:
C/C++ Source or Header  |  1999-03-10  |  14.5 KB  |  393 lines

  1. /* Copyright (c) 1995-1999 NEC USA, Inc.  All rights reserved.               */
  2. /*                                                                           */
  3. /* The redistribution, use and modification in source or binary forms of     */
  4. /* this software is subject to the conditions set forth in the copyright     */
  5. /* document ("Copyright") included with this distribution.                   */
  6.  
  7. /*
  8.  * $Id: proxy.c,v 1.94.2.2.2.9 1999/02/26 00:04:59 wlu Exp $
  9.  */
  10.  
  11. #include "socks5p.h"
  12. #include "daemon.h"
  13. #include "validate.h"
  14. #include "protocol.h"
  15. #include "msgids.h"
  16. #include "null.h"
  17. #include "info.h"
  18. #include "log.h"
  19. #include "msg.h"
  20.  
  21. #include "upwd.h"
  22. #include "gss.h"
  23. #include "tcp.h"
  24. #include "udp.h"
  25. #include "tracer.h"
  26. #include "packet.h"
  27.  
  28. #ifndef ESTABLISH_TIMEOUT
  29. #define ESTABLISH_TIMEOUT 10
  30. #endif
  31.     
  32. #ifndef START_TIMEOUT
  33. #define START_TIMEOUT 60
  34. #endif
  35.  
  36.  
  37. #define PROXY_IOFLAGS S5_IOFLAGS_TIMED|S5_IOFLAGS_NBYTES|S5_IOFLAGS_RESTART
  38.  
  39. static int HandleS4Connection(S5LinkInfo *pri, S5IOInfo *iio, list *auths, double *timerm) {
  40. #ifdef FORK_SOCKD
  41.     dup2(pri->in, 0);
  42.     dup2(pri->in, 1);
  43.     dup2(pri->in, 2);
  44.     close(pri->in);
  45.     
  46.     execlp("sockd", "sockd", NULL);
  47.     return EXIT_ERR;
  48. #else
  49.     char buf[256+256+8], *tmp, resp[] = { SOCKS4_VERSION, SOCKS_FAIL, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff }; 
  50.  
  51.     S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(10), 0, "Proxy: Acting as a Socks4 server");
  52.  
  53.     if (auths) {
  54.     for ( ;auths; auths = auths->next) {
  55.         if (auths->dataint == AUTH_NONE || auths->dataint == AUTH_PASSWD) break;
  56.     }
  57.  
  58.     if (!auths) {
  59.         S5LogUpdate(S5LogDefaultHandle, S5_LOG_WARNING, MSGID_SERVER_AUTH_NONE, "Socks%d: No acceptable authentication method found", (int)pri->peerVersion);
  60.         if (S5IOSend(iio->fd, NULL, resp, sizeof(resp), 0, PROXY_IOFLAGS, timerm) != sizeof(resp)) return EXIT_ERR; 
  61.         return EXIT_AUTH;
  62.     }
  63.     }
  64.     
  65.     if (S5IORecv(iio->fd, NULL, buf, 8, 0, PROXY_IOFLAGS, timerm) != 8) {
  66.     S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(0), 0, "Socks4: Read failed: %m");
  67.     return EXIT_ERR;
  68.     }
  69.  
  70.     pri->peerAuth     = AUTH_NONE;
  71.     pri->peerVersion  = buf[SP_VERSION];
  72.     pri->peerCommand  = buf[SP_COMMAND];
  73.     if (lsGetProtoAddr(SOCKS4_VERSION, buf, &pri->dstAddr) < 0) {
  74.     S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(0), 0, "Socks4: Invalid address passed from client");
  75.     return EXIT_ERR;
  76.     }
  77.  
  78.     for (tmp = buf, *tmp = '\0'; tmp < buf+sizeof(buf)-1; *++tmp = '\0') { 
  79.     if (S5IORecv(iio->fd, iio, tmp, 1, 0, PROXY_IOFLAGS, timerm) != 1) {
  80.         S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(0), 0, "Socks4: Read failed: %m");
  81.         return EXIT_ERR;
  82.     }
  83.  
  84.     if (*tmp == '\0') break; 
  85.     }
  86.  
  87.     S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(10), 0, "Socks4: Read user: %s", buf);
  88.  
  89.     strcpy(pri->srcUser, buf);
  90.     if (lsNullSrvAuth(iio->fd, &iio->auth, pri->srcUser) != AUTH_OK) {
  91.     S5LogUpdate(S5LogDefaultHandle, S5_LOG_WARNING, MSGID_SERVER_AUTH_FAILED, "Socks%d: Authentication method %d failed", (int)pri->peerVersion, 0);
  92.     resp[2] = SOCKS_BAD_ID;
  93.     if (S5IOSend(iio->fd, NULL, resp, sizeof(resp), 0, PROXY_IOFLAGS, timerm) != sizeof(resp)) return EXIT_ERR; 
  94.     return EXIT_AUTH;
  95.     }
  96.  
  97.     /* If we haven't gotten authentication specific read and write functions */
  98.     /* we should set the client functions to be "normal", rather than timed  */
  99.     /* Since in all likelyhood, it will take a long time to read in a whole  */
  100.     /* buffers worth of data (in fact it may never happen)...                */
  101.     if (pri->peerCommand != SOCKS_CONNECT && pri->peerCommand != SOCKS_BIND) {
  102.     S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(0), MSGID_SERVER_BAD_COMMAND, "Socks%d: Invalid command: %d", (int)pri->peerVersion, (int)pri->peerCommand);
  103.     S5IOSend(iio->fd, NULL, resp, sizeof(resp), 0, PROXY_IOFLAGS, timerm); 
  104.     return EXIT_ERR;
  105.     }
  106.  
  107.     S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(10), 0, "Socks4: Done initialization");
  108.     return 0;
  109. #endif
  110. }
  111.  
  112. static int AuthOK(u_char auth) {
  113.     switch (auth) {    
  114. #ifdef HAVE_LIBGSSAPI_KRB5
  115.     case AUTH_GSSAPI: 
  116. #endif
  117.     case AUTH_PASSWD:   
  118.     case AUTH_NONE:   return 1;
  119.     default:          return 0;
  120.     }
  121. }
  122.  
  123. static int HandleS5Connection(S5LinkInfo *pri, S5IOInfo *iio, list *auths, double *timerm) {
  124.     char fail[] = { 0x05, (char)0xff }, buf[256+2];
  125.     int ret, i;
  126.     list *tl;
  127.  
  128.   
  129.     if (S5IORecv(iio->fd, iio, buf, 2, 0, PROXY_IOFLAGS, timerm) != 2) {
  130.     S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(0), 0, "Socks5: Read failed: %m");
  131.     return EXIT_ERR;
  132.     }
  133.  
  134.     if (S5IORecv(iio->fd, iio, buf+2, (u_char)buf[1], 0, PROXY_IOFLAGS, timerm) != (u_char)buf[1]) {
  135.     S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(0), 0, "Socks5: Read failed: %m");
  136.     return EXIT_ERR;
  137.     }
  138.  
  139.     pri->peerAuth = 0xff;
  140.     
  141.     if (!auths) {
  142.     /* Anything is ok, pick the first one the client wanted to do...    */
  143.     for (i = 0; i < ((int)(u_char)buf[1]); i++) if (AuthOK((u_char)buf[i+2])) break;
  144.     if (i != ((int)(u_char)buf[1])) pri->peerAuth = (u_char)(buf[i+2]);
  145.     } else for (tl = auths; tl; tl = tl->next) {
  146.     /* For each method (in order) in the server's config file, See if    */
  147.     /* the client wanted to do this method...And, of course, make sure   */
  148.     /* we can do it...                                                   */
  149.     for (i = 0; i < ((int)(u_char)buf[1]); i++) if (tl->dataint == (int)(u_char)buf[i+2]) break;
  150.     if (i == (int)(u_char)buf[1] || !AuthOK((u_char)buf[i+2])) continue;
  151.  
  152.     pri->peerAuth = (u_char)buf[i+2];
  153.     break;
  154.     }
  155.  
  156.     buf[1] = (u_char)pri->peerAuth;
  157.  
  158.     if (S5IOSend(iio->fd, iio, buf, 2, 0, PROXY_IOFLAGS, timerm) != 2) {
  159.     S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(0), 0, "Socks5: Write failed: %m");
  160.     return EXIT_ERR;
  161.     }
  162.  
  163.     if (pri->peerAuth == 0xff) {
  164.     S5LogUpdate(S5LogDefaultHandle, S5_LOG_WARNING, MSGID_SERVER_AUTH_NONE, "Socks%d: No acceptable authentication method found", (int)pri->peerVersion);
  165.     return EXIT_AUTH;
  166.     }
  167.     
  168.     S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(10), 0, "Socks5: Told client to do authentication method #%d", (int)pri->peerAuth);
  169.  
  170.     switch (pri->peerAuth) {
  171.     case AUTH_PASSWD: ret = lsPasswdSrvAuth(iio->fd, &iio->auth, pri->srcUser); break;
  172.     case AUTH_GSSAPI: ret = lsGssapiSrvAuth(iio->fd, &iio->auth, pri->srcUser); break;
  173.     case AUTH_NONE:   ret = lsNullSrvAuth  (iio->fd, &iio->auth, pri->srcUser); break;
  174.     default:
  175.         S5LogUpdate(S5LogDefaultHandle, S5_LOG_WARNING, MSGID_SERVER_AUTH_BAD, "Socks%d: Bad Authentication method number: %d", (int)pri->peerVersion, (int)pri->peerAuth);
  176.         S5IOSend(iio->fd, iio, fail, sizeof(fail), 0, PROXY_IOFLAGS, timerm);
  177.         ret = AUTH_FAIL;
  178.     }
  179.  
  180.     if (ret == AUTH_FAIL) {
  181.     S5LogUpdate(S5LogDefaultHandle, S5_LOG_WARNING, MSGID_SERVER_AUTH_FAILED, "Socks%d: Authentication method %d failed", (int)pri->peerVersion, (int)pri->peerAuth);
  182.     return EXIT_AUTH;
  183.     } 
  184.  
  185.     if (lsReadRequest(iio->fd, iio, &pri->dstAddr, &pri->peerVersion, &pri->peerCommand, &pri->peerReserved) < 0) {
  186.     S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(0), 0, "Socks5: Read request failed: %m");
  187.     return EXIT_ERR;
  188.     }
  189.  
  190.     return EXIT_OK;
  191. }
  192.  
  193. /* HandlePxyConnection will be the routine for Socks5, which handles the     */
  194. /* initial part of the connection.  Specifically, we want to do several      */
  195. /* things here.  We want to check the version, if its not version 5, we can  */
  196. /* either exec the old sockd, or we can call a separate routine to handle    */
  197. /* the connection establishment.  (That routine should read the name, and    */
  198. /* possible make an ident query).  If the version is version 5, we have to   */
  199. /* decide what kind of authentication we are going to do.  This would mean   */
  200. /* reading the number of methods, and then the method numbers themselves,    */
  201. /* after that we pick the one we'd like to do, and send back a reply with    */
  202. /* my version (5), and the method number we picked, or 0xff if we couldn't   */
  203. /* find one that was acceptable given the information we already have (the   */
  204. /* source host.)  We probably want to drop the connection immediately if we  */
  205. /* know there is no acceptable auth from a host, so we don't waste fd's.     */
  206. /* after all this is done, we read the command, then finally do it.  Well,   */
  207. /* if we haven't already quit, that is.                                      */
  208. int HandlePxyConnection(S5IOHandle fd) {
  209.     char buf[] = { SOCKS5_VERSION, SOCKS5_FAIL, 0, SOCKS5_IPV4ADDR, 0, 0, 0, 0, 0, 0 };
  210.     int action, dir, rval = EXIT_ERR, len = sizeof(S5NetAddr), cleaned = 0;
  211.     double timerm = (double)START_TIMEOUT;
  212.     list *auths;
  213.  
  214.     S5Packet inPacket, outPacket;
  215.     S5CommandInfo ci;
  216.     S5FilterInfo fi;
  217.     S5NetAddr route;
  218.     S5LinkInfo li;
  219.     S5IOInfo iio;
  220.  
  221.     memset(&li, 0, sizeof(S5LinkInfo));
  222.     memset(&fi, 0, sizeof(S5FilterInfo));
  223.     memset(&ci, 0, sizeof(S5CommandInfo));
  224.     S5BufSetupContext(&iio);
  225.     iio.fd = fd;
  226.  
  227.     if (getpeername(iio.fd, (ss *)&li.srcAddr, &len) < 0) {
  228.     S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(0), 0, "Proxy: getpeername failed: %m");
  229.     goto cleanup;
  230.     }
  231.  
  232.     len = sizeof(S5NetAddr);
  233.  
  234.     if (getsockname(iio.fd, (ss *)&li.bndAddr, &len) < 0) {
  235.     S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(0), 0, "Proxy: getsockname failed: %m");
  236.     goto cleanup;
  237.     }
  238.  
  239.     GetName(li.srcName, &li.srcAddr);
  240.  
  241.     if (!GetRoute(&li.srcAddr, li.srcName, "tcp", &route)) {
  242.         /* If somehow we got a connection from somewhere on an interface which   */
  243.         /* we wouldn't use to get to that same place, this is bad (IP SPOOF?),   */
  244.         /* so we'll quit.                                                        */
  245.         if (route.sin.sin_addr.s_addr != INADDR_ANY &&
  246.         route.sin.sin_addr.s_addr != li.bndAddr.sin.sin_addr.s_addr) {
  247.         S5LogUpdate(S5LogDefaultHandle, S5_LOG_WARNING, MSGID_SERVER_WRONG_ROUTE, "Proxy: Received connection via wrong route");
  248.         S5LogUpdate(S5LogDefaultHandle, S5_LOG_INFO, MSGID_SERVER_AUTH_FAILED, "Auth Failed: (%s:%d)", li.srcName, (int)ntohs(lsAddr2Port(&li.srcAddr)));
  249.         goto cleanup;
  250.         }
  251.     }
  252.  
  253.     /* If this host was "banned", quit...                                    */
  254.     if ((GetAuths(&li, &auths)) < 0) {
  255.     S5LogUpdate(S5LogDefaultHandle, S5_LOG_WARNING, MSGID_SERVER_BANNED_HOST, "Proxy: Received connection from banned host");
  256.     S5LogUpdate(S5LogDefaultHandle, S5_LOG_INFO, MSGID_SERVER_AUTH_FAILED, "Auth Failed: (%s:%d)", li.srcName, (int)ntohs(lsAddr2Port(&li.srcAddr)));
  257.     goto cleanup;
  258.     }
  259.     
  260.     if ((S5IORecv(iio.fd, NULL, (char *)&li.peerVersion, 1, MSG_PEEK, PROXY_IOFLAGS, &timerm)) != 1) {
  261.     S5LogUpdate(S5LogDefaultHandle, S5_LOG_WARNING, MSGID_SERVER_RECV_VERSION, "Proxy: Unable to determine version number: %m");
  262.     goto cleanup;
  263.     }
  264.  
  265.     timerm = (double)ESTABLISH_TIMEOUT;
  266.  
  267.     switch (li.peerVersion) {
  268.     case SOCKS5_VERSION:
  269.         rval = HandleS5Connection(&li, &iio, auths, &timerm);
  270.         break;
  271.         case SOCKS4_VERSION:
  272.         if (getenv("SOCKS5_V4SUPPORT")) {
  273.         rval = HandleS4Connection(&li, &iio, auths, &timerm);
  274.         break;
  275.         }
  276.         /* fall through... */
  277.     default:
  278.         S5LogUpdate(S5LogDefaultHandle, S5_LOG_WARNING, MSGID_SERVER_BAD_VERSION, "Proxy: Received request with incompatible version number: %d", (int)li.peerVersion);
  279.         S5IOSend(iio.fd, NULL, buf, sizeof(buf), 0, PROXY_IOFLAGS, &timerm);
  280.         rval = EXIT_ERR;
  281.     }
  282.  
  283.  
  284.     if (rval < 0) {
  285.     S5LogUpdate(S5LogDefaultHandle, S5_LOG_INFO, MSGID_SERVER_AUTH_FAILED, "Auth Failed: (%s:%d)", li.srcName, (int)ntohs(lsAddr2Port(&li.srcAddr)));
  286.     goto cleanup;
  287.     }
  288.  
  289.     S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(10), 0, "Proxy: vers:%d cmnd:%d addr:%s port:%d user:%s",
  290.         (int)li.peerVersion, (int)li.peerCommand, ADDRANDPORT(&li.dstAddr), li.srcUser);
  291.  
  292.     /* If we haven't gotten authentication specific read and write functions */
  293.     /* we should set the client functions to be "normal", rather than timed  */
  294.     /* Since in all likelyhood, it will take a long time to read in a whole  */
  295.     /* buffers worth of data (in fact it may never happen)...                */
  296.     switch (li.peerCommand) {
  297.      case SOCKS_PING:    
  298.      case SOCKS_TRACER:  rval = PTSetup (&iio, &li, &ci);  break;
  299.     case SOCKS_CONNECT: 
  300.     case SOCKS_BIND:    rval = TcpSetup(&iio, &li, &ci); break;
  301.     case SOCKS_UDP:     rval = UdpSetup(&iio, &li, &ci); break;
  302.     default:
  303.         S5LogUpdate(S5LogDefaultHandle, S5_LOG_WARNING, MSGID_SERVER_BAD_COMMAND, "Proxy: Bad command number %d", (int)li.peerCommand);
  304.  
  305.          lsSendResponse(iio.fd, &iio, &li.dstAddr, li.peerVersion, (li.peerVersion == SOCKS5_VERSION)?SOCKS5_BADCMND:SOCKS_BAD_ID, 0, NULL);
  306.             S5BufCleanContext(&iio);
  307.         rval = -1;
  308.     }
  309.  
  310.     if (rval < 0) {
  311.     S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(10), 0, "Proxy: Command setup failed");
  312.     cleaned = 1;
  313.     goto cleanup;
  314.     }
  315.  
  316.     PacketPrintSetup(&li, &fi);
  317.  
  318.     inPacket.data  = NULL;
  319.     outPacket.data = NULL;
  320.     inPacket.oob  = 0;
  321.     outPacket.oob = 0;
  322.  
  323.     for (action = S5_ACTION_READ, dir = S5_DIRECTION_ANY;; ) {
  324.     switch (action) {
  325.         case S5_ACTION_READ:
  326.         inPacket.off = 0;
  327.         case S5_ACTION_MORE_READ:
  328.         /* Need more info...                                         */
  329.         if ((rval = ci.recvpkt(&inPacket, &li, ci.option, &dir)) <= 0) {
  330.             /* but we had an error...                                */
  331.             action = S5_ACTION_CLOSE;
  332.             continue;
  333.         } else {
  334.             /* Update the byte count on the read not the write?      */
  335.             if (dir == S5_DIRECTION_IN) {
  336.             li.inbc += rval;
  337.             } else {
  338.             li.outbc += rval;
  339.             }
  340.  
  341.             break;
  342.         }
  343.         case S5_ACTION_WRITE:
  344.         /* Just write...                                             */
  345.         if (ci.sendpkt(&outPacket, &li, ci.option, &dir) <= 0) {
  346.             action = S5_ACTION_CLOSE;
  347.             continue;
  348.         } else {
  349.             /* Go back to reading...                                 */
  350.             dir    = S5_DIRECTION_ANY;
  351.             action = S5_ACTION_READ;
  352.             continue;
  353.         }
  354.         case S5_ACTION_MORE_WRITE:
  355.         if (ci.sendpkt(&outPacket, &li, ci.option, &dir) <= 0) {
  356.             action = S5_ACTION_CLOSE;
  357.             continue;
  358.         } else {
  359.             break;
  360.         }
  361.         case S5_ACTION_CLOSE:
  362.         /* Any error messages that need to get printed must be done  */
  363.         /* within ci.clean                                           */
  364.         goto cleanup;
  365.     }
  366.  
  367.     /* Only valid way of getting here is via S5_ACTION_READ              */
  368.     if (fi.filter && !fi.filter(&inPacket, &outPacket, &li, fi.option, &dir, &action)) continue;
  369.  
  370.     action    = S5_ACTION_WRITE;
  371.     outPacket = inPacket;
  372.     }
  373.  
  374.   cleanup:
  375.  
  376.     if (ci.option && ci.clean) {
  377.     S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(10), 0, "Proxy: cleaning command context");
  378.     ci.clean(&li, ci.option);
  379.     cleaned = 1;
  380.     }
  381.  
  382.     if (!cleaned) S5BufCleanContext(&iio);
  383.  
  384.     if (fi.clean) {
  385.     S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(10), 0, "Proxy: cleaning filter context");
  386.     fi.clean(fi.option);
  387.     }
  388.  
  389.     S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(10), 0, "Proxy: done cleaning up");
  390.     return rval;
  391. }
  392.  
  393.