Back to beaubozarth.com

util_sock.c

/*
 * Socket Routines for Authentication server
 * Copyright beaubozarth.com 2008
*/

#include "includes.h"

/*
 * the last IP received from
 */
struct in_addr lastip;

/*
 * the last port received from
 */
int lastport=0;

int smb_read_error = 0;

static char *get_socket_addr(int fd)
{
    struct sockaddr sa;
    struct sockaddr_in *sockin = (struct sockaddr_in *) (&sa);
    socklen_t length = sizeof(sa);
    static fstring addr_buf;

    fstrcpy(addr_buf,"0.0.0.0");

    if (fd == -1)
    {
        return addr_buf;
    }
    
    if (getsockname(fd, &sa, &length) < 0)
    {
        DEBUG(0,("getsockname failed. Error was %s\n", strerror(errno) ));
        return addr_buf;
    }
    
    fstrcpy(addr_buf,(char *)inet_ntoa(sockin->sin_addr));
    
    return addr_buf;
}

static int get_socket_port(int fd)
{
    struct sockaddr sa;
    struct sockaddr_in *sockin = (struct sockaddr_in *) (&sa);
    socklen_t length = sizeof(sa);

    if (fd == -1)
        return -1;
    
    if (getsockname(fd, &sa, &length) < 0)
    {
        DEBUG(0,("getsockname failed. Error was %s\n", strerror(errno) ));
        return -1;
    }
    
    return ntohs(sockin->sin_port);
}

/*
 * Is this file descriptor a socket.
 */
BOOL socket_yes( int fd )
{
    int v;
    socklen_t l;
    l = sizeof(int);
    return( getsockopt(fd, SOL_SOCKET, SO_TYPE, ( char * ) &v, &l ) == 0);
}

/*
 *Set user socket options.
 */

void set_socket_opts(int fd, const char *options)
{
    fstring tok;

    while (next_token(&options,tok," \t,", sizeof(tok)))
    {
        int ret=0,i;
        int value = 1;
        char *p;
        BOOL got_value = False;

        if ((p = strchr_m(tok,'=')))
        {
            *p = 0;
            value = atoi(p+1);
            got_value = True;
        }

        for (i=0;socket_options[i].name;i++)
            if (strequal(socket_options[i].name,tok))
                break;

        if (!socket_options[i].name)
        {
            DEBUG(0,("Unknown socket option %s\n",tok));
            continue;
        }

        switch (socket_options[i].opttype)
        {
        case OPT_BOOL:
        case OPT_INT:
            ret = setsockopt(fd,socket_options[i].level,
                        socket_options[i].option,(char *)&value,sizeof(int));
            break;

        case OPT_ON:
            if (got_value)
                DEBUG(0,("syntax error - %s does not take a value\n",tok));

            {
                int on = socket_options[i].value;
                ret = setsockopt(fd,socket_options[i].level,
                            socket_options[i].option,(char *)&on,sizeof(int));
            }
            break;      
        }
      
        if (ret != 0)
            DEBUG(0,("Failed to set socket option %s (Error %s)\n",tok, strerror(errno) ));
    }

    print_socket_options(fd);
}


BOOL send_keepalive(int client)
{
    unsigned char buf[4];

    buf[0] = SMBkeepalive;
    buf[1] = buf[2] = buf[3] = 0;

    return(write_socket_data(client,(char *)buf,4) == 4);
}


/*
 * Open a socket of the specified type, port, and address for incoming data.
 */

int open_socket_in(int type, int port, int dlevel, uint32 socket_addr, BOOL rebind)
{
    struct sockaddr_in sock;
    int res;

    memset((char *)&sock, '\0', sizeof(sock));

#ifdef HAVE_SOCK_SIN_LEN
    sock.sin_len         = sizeof(sock);
#endif
    sock.sin_port        = htons( port );
    sock.sin_family      = AF_INET;
    sock.sin_addr.s_addr = socket_addr;

    res = socket(AF_INET, type, 0);
    if( res == -1 )
    {
        if(DEBUGLVL(0))
        {
            dbgtext("open_socket_in(): socket() call failed: ");
            dbgtext("%s\n", strerror( errno ));
        }
        return -1;
    }

    /*
     * This block sets/clears the SO_REUSEADDR and possibly SO_REUSEPORT.
     */
    {
        int val = rebind ? 1 : 0;
        if(setsockopt(res,SOL_SOCKET,SO_REUSEADDR,(char *)&val,sizeof(val)) == -1)
        {
            if(DEBUGLVL( dlevel))
            {
                dbgtext("open_socket_in(): setsockopt: ");
                dbgtext("SO_REUSEADDR = %s ", val?"True":"False");
                dbgtext("on port %d failed ", port);
                dbgtext("with error = %s\n", strerror(errno));
            }
        }
#ifdef SO_REUSEPORT
        if(setsockopt(res,SOL_SOCKET,SO_REUSEPORT,(char *)&val,sizeof(val)) == -1)
        {
            if(DEBUGLVL( dlevel))
            {
                dbgtext("open_socket_in(): setsockopt: ");
                dbgtext("SO_REUSEPORT = %s ", val?"True":"False");
                dbgtext("on port %d failed ", port );
                dbgtext("with error = %s\n", strerror(errno));
            }
        }
#endif /* SO_REUSEPORT */
    }

    /*
     * now we've got a socket - we need to bind it
     */
    if(bind( res, (struct sockaddr *)&sock, sizeof(sock) ) == -1)
    {
        if(DEBUGLVL(dlevel) && (port == SMB_PORT1 || port == SMB_PORT2 || port == NMB_PORT))
        {
            dbgtext("bind failed on port %d ", port);
            dbgtext("socket_addr = %s.\n", inet_ntoa(sock.sin_addr));
            dbgtext("Error = %s\n", strerror(errno));
        }
        close(res);
        return(-1);
    }

    DEBUG( 10, ("bind succeeded on port %d\n", port));

    return(res);
 }

/*
 * Create an outgoing socket. timeout is in milliseconds.
 */

int open_socket_out(int type, struct in_addr *addr, int port ,int timeout)
{
    struct sockaddr_in sock_out;
    int res,ret;
    int connect_loop = 10;
    int increment = 10;

    /*
     * create a socket to write to
     */
    res = socket(PF_INET, type, 0);
    if (res == -1)
    {
                DEBUG(0,("socket error (%s)\n", strerror(errno)));
        return -1;
    }

    if (type != SOCK_STREAM)
        return(res);
 
    memset((char *)&sock_out,'\0',sizeof(sock_out));
    putip((char *)&sock_out.sin_addr,(char *)addr);
 
    sock_out.sin_port = htons( port );
    sock_out.sin_family = PF_INET;

    /*
     * set it non-blocking
     */
    set_blocking(res,False);

    DEBUG(3,("Connecting to %s at port %d\n",inet_ntoa(*addr),port));
 
    /*
     * and connect it to the destination
     */
  connect_again:

    ret = connect(res,(struct sockaddr *)&sock_out,sizeof(sock_out));

    /*
     * Some systems return EAGAIN when they mean EINPROGRESS
     */
    if (ret < 0 && (errno == EINPROGRESS || errno == EALREADY ||
            errno == EAGAIN) && (connect_loop < timeout) )
    {
        smb_msleep(connect_loop);
        timeout -= connect_loop;
        connect_loop += increment;
        if (increment < 250)
        {
            /*
             * After 8 rounds we end up at a max of 255 msec
             */
            increment *= 1.5;
        }
        goto connect_again;
    }

    if (ret < 0 && (errno == EINPROGRESS || errno == EALREADY ||
            errno == EAGAIN)) {
        DEBUG(1,("timeout connecting to %s:%d\n",inet_ntoa(*addr),port));
        close(res);
        return -1;
    }

#ifdef EISCONN

    if (ret < 0 && errno == EISCONN)
    {
        errno = 0;
        ret = 0;
    }
#endif

    if (ret < 0) {
        DEBUG(2,("error connecting to %s:%d (%s)\n",
                inet_ntoa(*addr),port,strerror(errno)));
        close(res);
        return -1;
    }

    /*
     * set it blocking again
     */
    set_blocking(res,True);

    return res;
}

static int client_fd = -1;

void client_setfd(int fd)
{
    client_fd = fd;
}

char *client_name(void)
{
    return get_peer_name(client_fd,False);
}

char *client_addr(void)
{
    return get_peer_addr(client_fd);
}

char *client_socket_addr(void)
{
    return get_socket_addr(client_fd);
}

int client_socket_port(void)
{
    return get_socket_port(client_fd);
}

struct in_addr *client_inaddr(struct sockaddr *sa)
{
    struct sockaddr_in *sockin = (struct sockaddr_in *) (sa);
    socklen_t  length = sizeof(*sa);
    
    if (getpeername(client_fd, sa, &length) < 0)
    {
        DEBUG(0,("getpeername failed. Error was %s\n", strerror(errno) ));
        return NULL;
    }
    
    return &sockin->sin_addr;
}