/* Copyright (c) 1995-1999 NEC USA, Inc.  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: info.c,v 1.42.4.4 1999/02/03 22:35:35 steve Exp $
 */

/* This file has the code for dealing with the global protocol variables.    */
#include "socks5p.h"
#include "threads.h"
#include "daemon.h"
#include "validate.h"
#include "info.h"
#include "log.h"

/* Return a malloc'd string representing the closest thing you can get to a  */
/* Hostname for ia...                                                        */
/*                                                                           */
/* Arguments: ia, the address whose name we are looking up...                */
void GetName(char *name, S5NetAddr *addr) {
    const char *res = NULL, *eres;
    const struct hostent *hp = NULL;

    memset(name, 0, S5_HOSTNAME_SIZE);

    if (addr->sa.sa_family == AF_S5NAME) {
    	strncpy(name, addr->sn.sn_name, MIN(strlen(addr->sn.sn_name)+1, S5_HOSTNAME_SIZE));
    	if (strlen(addr->sn.sn_name)+1 > S5_HOSTNAME_SIZE) name[S5_HOSTNAME_SIZE-1] = '\0';
	return;
    }

    MUTEX_LOCK(env_mutex);
    eres = getenv("SOCKS5_REVERSEMAP");
    MUTEX_UNLOCK(env_mutex);
	
    if (eres) {
	MUTEX_LOCK(gh_mutex);
	hp = gethostbyaddr((char *)&addr->sin.sin_addr, sizeof(struct in_addr), AF_INET);
	if (!hp) { MUTEX_UNLOCK(gh_mutex); }
	else res = hp->h_name;
    }

    if (res != NULL || (res = lsAddr2Ascii(addr)) != NULL) {
    	strncpy(name, res, MIN(strlen(res)+1, S5_HOSTNAME_SIZE));
    	if (strlen(res)+1 > S5_HOSTNAME_SIZE) name[S5_HOSTNAME_SIZE-1] = '\0';
    }

    if (!hp) return;
    MUTEX_UNLOCK(gh_mutex);
}

/* Returns a malloc'd string representing the closest then you can get to a  */
/* Service name for port.                                                    */
/*                                                                           */
/* Arguments: port, the port whose service name we are looking up...         */
void GetServ(char *name, u_short port, char *proto) {
    struct servent *srv;
    char *tmp;

    memset(name, 0, S5_APPNAME_SIZE);

    MUTEX_LOCK(env_mutex);
    tmp = getenv("SOCKS5_SERVICENAME");
    MUTEX_UNLOCK(env_mutex);

    MUTEX_LOCK(gs_mutex);
    if (tmp && (srv = getservbyport(port, proto))) {
	strncpy(name, srv->s_name, MIN(strlen(srv->s_name)+1, S5_APPNAME_SIZE));
	if (strlen(srv->s_name)+1 > S5_APPNAME_SIZE) name[S5_APPNAME_SIZE-1] = '\0';
    } else {
	sprintf(name, "%d", (int)ntohs(port));
    }
    MUTEX_UNLOCK(gs_mutex);
}

/* Resolves any hosts/services which were passed by name instead of by       */
/* address, if there are any...Also it finds out which socks server to use   */
/* and logs an error if it cannot pass the name to the next one (i.e. the    */
/* next one is not a socks5 serve).                                          */
/*                                                                           */
/* Globals Used/Affected: dstsin -- filled in with destination address...    */
/*                        scksin -- filled in with server address...         */
int ResolveNames(S5LinkInfo *pri) {
    S5NetAddr tmp;

    S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(10), 0, "Resolve Names: Starting");

    pri->retName[0] = '\0';
    memset((char *)&pri->retAddr, 0, sizeof(pri->retAddr));
    pri->retAddr.sa.sa_family = AF_INET;
    pri->retAddr.sin.sin_addr.s_addr = INVALIDADDR;
    
    /* If the destination address is of type S5NAME, copy the name to        */
    /* pri->retAddr, resolve the name and copy the reslved address to both   */
    /* pri->dstAddr and pri->retAddr...                                      */
    /*                                                                       */
    /* If the destination address is of type INET, copy the address to       */
    /* pri->retAddr, reverse map the name and copy the mapped name to        */
    /* pri->retName...                                                       */
    if (pri->dstAddr.sa.sa_family == AF_S5NAME) {
	S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(10), 0, "Resolve names: Unresolved host name: %s", pri->dstAddr.sn.sn_name);

 	strncpy(pri->retName, pri->dstAddr.sn.sn_name, MIN(strlen(pri->dstAddr.sn.sn_name) + 1, S5_HOSTNAME_SIZE));
	if (strlen(pri->dstAddr.sn.sn_name) + 1 > S5_HOSTNAME_SIZE) pri->retName[S5_HOSTNAME_SIZE-1] = '\0';

	if (!lsName2Addr(pri->dstAddr.sn.sn_name, &tmp)) {
	    S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(10), 0, "Resolve Names: host name: %s is %s", pri->dstAddr.sn.sn_name, lsAddr2Ascii(&tmp));
	    lsAddrSetPort(&tmp, lsAddr2Port(&pri->dstAddr));
	    lsAddrCopy(&pri->dstAddr, &tmp, lsAddrSize(&tmp));
    	    lsAddrCopy(&pri->retAddr, &pri->dstAddr, lsAddrSize(&pri->dstAddr));
	} else {
	    S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(10), 0, "Resolve Names: Unresolved host name: %s still unresolved", pri->dstAddr.sn.sn_name);
	    lsAddrSetPort(&pri->retAddr, lsAddr2Port(&pri->dstAddr));
	}

        GetName(pri->dstName, &pri->dstAddr);
    } else {
	if (pri->dstAddr.sin.sin_addr.s_addr != INADDR_ANY &&
		!(pri->dstAddr.sin.sin_addr.s_addr & inet_addr("255.255.255.0"))) {
	    S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(10), 0, "Resolve Names: Wrong host: %s", lsAddr2Ascii(&pri->dstAddr));
	    return -1;
	}

    	lsAddrCopy(&pri->retAddr, &pri->dstAddr, lsAddrSize(&pri->dstAddr));
        GetName(pri->dstName, &pri->dstAddr);

	if (inet_addr(pri->dstName) == INVALIDADDR) strcpy(pri->retName, pri->dstName);
    }

    S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(10), 0, "Resolve Names: Looking up service name");
    GetServ(pri->dstServ, lsAddr2Port(&pri->dstAddr), (pri->peerCommand == SOCKS_UDP)?"udp":"tcp");

    S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(10), 0, "Resolve Names: Looking up next proxy");
    GetProxy(&pri->dstAddr, pri->dstName, (pri->peerCommand == SOCKS_UDP)?"udp":"tcp", pri->altSckAddrs, &pri->nAltSckAddrs, &pri->nextVersion);

    if (pri->nextVersion) {
	lsAddrCopy(&pri->sckAddr, &pri->altSckAddrs[0], lsAddrSize(&pri->altSckAddrs[0]));
    	S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(10), 0, "Resolve Names: Looking up next proxy's name");
    	GetName(pri->sckName, &pri->sckAddr);
	S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(10), 0, "Resolve Names: Next Proxy: (%s:%d)", ADDRANDPORT(&pri->sckAddr));
    } else {
	memset(&pri->sckAddr, 0, sizeof(pri->sckAddr));
	S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(10), 0, "Resolve Names: No Next Proxy");
    }
    
    return 0;
}

