// This may look like C code, but it is really -*- C++ -*-
// 
// <copyright> 
//  
//  Copyright (c) 1996
//  Institute for Information Processing and Computer Supported New Media (IICM), 
//  Graz University of Technology, Austria. 
//  
// </copyright> 
// 
// 
// <file> 
// 
// Name:        socks4.C
// 
// Purpose:     
// 
// Created:     7 Jan 1997   Joerg Faschingbauer
// 
// Modified:    
// 
// Description: 
// 
// $Id: socks4.C,v 1.3 1997/02/13 12:32:28 gorasche Exp $
// 
// $Log: socks4.C,v $
// Revision 1.3  1997/02/13 12:32:28  gorasche
// changes for WIN32
// - enums cast
// - types.h included for bool type
//
// Revision 1.2  1997/02/12 20:15:22  jfasch
// moved to utils
//
// Revision 1.1  1997/02/12 20:14:44  jfasch
// Initial revision
//
// 
// </file> 
#include "socks4.h"

#include "assert.h"
#include "buffer.h"
#include "verbose.h"

// --------------------------------------------------------------------
const char* SOCKS4ConnectRequest :: version1 = "SOCKS4ConnectRequest: $Id: socks4.C,v 1.3 1997/02/13 12:32:28 gorasche Exp $" ;

SOCKS4ConnectRequest :: SOCKS4ConnectRequest (const INETAddress& a, const RString& uid)
: addr_(a),
  uid_(uid) {}

void SOCKS4ConnectRequest :: write (OBuffer& buf) const {
   buf << (char)4 ; // SOCKS version number
   buf << (char)1 ;  // CONNECT
   buf.write ((const char*)(&addr_.sin_port), sizeof(addr_.sin_port)) ;
   buf.write ((const char*)(&addr_.sin_addr.s_addr), sizeof(addr_.sin_addr.s_addr)) ;
   if (uid_.length())
      buf.write (uid_.string(), uid_.length()) ;
   buf << '\0' ;
}

// void SOCKS4ConnectRequest :: write (char* buf) const {
//    *buf++ = (char)4 ;
//    *buf++ = (char)1 ;
//    ::memcpy (buf, (const char*)(&addr_.sin_port), sizeof(addr_.sin_port)) ;
//    buf += sizeof(addr_.sin_port) ;
//    ::memcpy (buf, (const char*)(&addr_.sin_addr.s_addr), sizeof(addr_.sin_addr.s_addr)) ;
//    buf += sizeof(addr_.sin_addr.s_addr) ;
//    if  (uid_.length()) {
//       ::memcpy (buf, uid_.string(), uid_.length()) ;
//       buf += uid_.length() ;
//    }
//    *buf = '\0' ;
// }

// --------------------------------------------------------------------
const char* SOCKS4ConnectReply :: version1 = "SOCKS4ConnectReply: $Id: socks4.C,v 1.3 1997/02/13 12:32:28 gorasche Exp $" ;

const char* SOCKS4ConnectReply :: errordescr_[] = {
  "No error, why ask?",
  "Request rejected or failed",
  "Request rejected becasue SOCKS server cannot connect to identd on the client",
  "Request rejected because the client program and identd report different user-ids",
  "Error not yet set"
} ;

SOCKS4ConnectReply :: SOCKS4ConnectReply() 
: state_(S4C_INIT),
  error_(S4CE_NOTINIT) {}

int SOCKS4ConnectReply :: add (const char* s, int l) {
   int accepted = 0 ;
   while (ok() && !complete() && l--) {
      accepted += add(*s++)? 1: 0 ;
   }
   return accepted ;
}

bool SOCKS4ConnectReply :: add (char c) {
   switch (state_) {
     case S4C_INIT:
        // socks reply version number ("should be zero", they say)
        if (c != 0) {
           DEBUGNL ("SOCKS4ConnectReply::add(): bad reply version number: "<<int(c)) ;
           state_ = S4C_ERROR ;
        }
        else
           state_ = S4C_VN ;
        break ;
     case S4C_VN:
        switch (c) {
          case 90:
          case 91:
          case 92:
          case 93:
             error_ = SOCKS4Error (c - 90) ;
             state_ = S4C_CD ;
             DEBUGNL ("SOCKS4ConnectReply::add(): error: "<<
                      errorDescription()<<" ("<<int(c)<<')') ;
             break ;
          default:
             DEBUGNL ("SOCKS4ConnectReply::add(): protocol error: undefined error "<<int(c)) ;
             state_ = S4C_ERROR ;
             break ;
        }
        break ;
        // the rest of the reply must be ignored (sais the spec.)
     case S4C_CD:
        state_ = S4C_DSTPORT0 ;
        break ;
     case S4C_DSTPORT0:
        state_ = S4C_DSTPORT1 ;
        break ;
     case S4C_DSTPORT1:
        state_ = S4C_DSTIP0 ;
        break ;
     case S4C_DSTIP0:
        state_ = S4C_DSTIP1 ;
        break ;
     case S4C_DSTIP1:
        state_ = S4C_DSTIP2 ;
        break ;
     case S4C_DSTIP2:
        state_ = S4C_DSTIP3 ;
        break ;
     case S4C_DSTIP3:
        hgassert (false, "SOCKS4ConnectReply::add(): already complete, didn\'t see that?") ;
        break ;
     case S4C_ERROR:
        hgassert (false, "SOCKS4ConnectReply::add(): "
                  "already encountered an error, didn\'t see that?") ;
        break ;
   }
   hgassert (state_>=S4C_INIT&&state_<=S4C_ERROR,
             "SOCKS4ConnectReply::add(): invalid state: "<<(int)state_) ;
   return state_ != S4C_ERROR ;
}

void SOCKS4ConnectReply :: write (OBuffer& buf) const {
   buf << '\4' ;
   buf << char (90 + error_) ;
   buf << '\0' << '\0' << '\0' << '\0' << '\0' << '\0' ;
}





