Search code examples
clinuxsocketssocks

Socks 5 client in C


I'd like to write socks 5 client using stream sockets in C.

Here is my code:

/*
** client_socks.c -- a stream socket socks 5 client demo
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>

#include <arpa/inet.h>

#define PORT "9050" // the port client will be connecting to 

#define MAXDATASIZE 100 // max number of bytes we can get at once 

// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa)
{
    if (sa->sa_family == AF_INET) {
        return &(((struct sockaddr_in*)sa)->sin_addr);
    }

    return &(((struct sockaddr_in6*)sa)->sin6_addr);
}

int main(int argc, char *argv[])
{
    int sockfd, numbytes;  
    char buf[MAXDATASIZE];
    struct addrinfo hints, *servinfo, *p;
    int rv;
    char s[INET6_ADDRSTRLEN];

    if (argc != 2) {
        fprintf(stderr,"usage: client hostname\n");
        exit(1);
    }

    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;

    if ((rv = getaddrinfo(argv[1], PORT, &hints, &servinfo)) != 0) {
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
        return 1;
    }

    // loop through all the results and connect to the first we can
    for(p = servinfo; p != NULL; p = p->ai_next) {
        if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
            perror("client: socket");
            continue;
        }

        if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
            perror("client: connect");
            close(sockfd);
            continue;
        }

        break;
    }

    if (p == NULL) {
        fprintf(stderr, "client: failed to connect\n");
        return 2;
    }

    inet_ntop(p->ai_family, get_in_addr((struct sockaddr *)p->ai_addr), s, sizeof s);
    printf("client: connecting to %s\n", s);

    freeaddrinfo(servinfo); // all done with this structure

    /* Version 5, one method: no authentication */
    char buffer[256], *ptrBuff;
    ptrBuff = buffer;

    ptrBuff[0] = 5; // socks version
    ptrBuff[1] = 1;
    ptrBuff[2] = 0; // SOCKS_NOAUTH

    if (send(sockfd, ptrBuff, buffer-ptrBuff, 0) == -1)
                perror("send");

    if ((numbytes = recv(sockfd, buffer, MAXDATASIZE-1, 0)) == -1) {
        perror("recv");
        exit(1);
    }

    buffer[numbytes] = '\0';

    printf("client: received '%s'\n",buffer);

    close(sockfd);

    return 0;
}

But it's not working - It's writing:

client: connecting to 127.0.0.1 

and waiting, that's all

I have not found really useful examples how to do this in C, so I would greatly appreciate any help you can give me.

How to realize auth and no auth methods?

New corrected code:

/*
** client_proxy.c -- a stream socket socks 5 client demo
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>

#include <arpa/inet.h>

#define PORT "9050" // the port client will be connecting to 

#define MAXDATASIZE 100 // max number of bytes we can get at once 

// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa)
{
    if (sa->sa_family == AF_INET) {
        return &(((struct sockaddr_in*)sa)->sin_addr);
    }

    return &(((struct sockaddr_in6*)sa)->sin6_addr);
}

int main(int argc, char *argv[])
{
    int sockfd, numbytes;  
    char buf[MAXDATASIZE];
    struct addrinfo hints, *servinfo, *p;
    int rv;
    char s[INET6_ADDRSTRLEN];

    if (argc != 2) {
        fprintf(stderr,"usage: client hostname\n");
        exit(1);
    }

    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;

    if ((rv = getaddrinfo(argv[1], PORT, &hints, &servinfo)) != 0) {
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
        return 1;
    }

    // loop through all the results and connect to the first we can
    for(p = servinfo; p != NULL; p = p->ai_next) {
        if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
            perror("client: socket");
            continue;
        }

        if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
            perror("client: connect");
            close(sockfd);
            continue;
        }

        break;
    }

    if (p == NULL) {
        fprintf(stderr, "client: failed to connect\n");
        return 2;
    }

    inet_ntop(p->ai_family, get_in_addr((struct sockaddr *)p->ai_addr), s, sizeof s);
    printf("client: connecting to %s\n", s);

    freeaddrinfo(servinfo); // all done with this structure

    /* Version 5, one method: no authentication */
    char buffer[256], *ptrBuff, bufferf[256];
    ptrBuff = buffer;

    *(ptrBuff++) = 5; // socks version
    *(ptrBuff++) = 2;
    *(ptrBuff++) = 0x00; // no auth 
    *(ptrBuff++) = 0x02; // user pass auth

    if (send(sockfd, ptrBuff, buffer-ptrBuff, 0) == -1)
                perror("send");

    printf("sent\n");

    if ((numbytes = recv(sockfd, bufferf, MAXDATASIZE-1, 0)) == -1) {
        perror("recv");
        exit(1);
    }

    printf("client: received '%d'\n", bufferf[1]);

    close(sockfd);

    return 0;
}

I got:

client: connecting to 127.0.0.1
sent
client: received '97

Every time received number is different, what's wrong?


Solution

  • Take a deeper look at this code:

    char buffer[256], *ptrBuff;
    ptrBuff = buffer;
    
    ptrBuff[0] = 5; // socks version
    ptrBuff[1] = 1;
    ptrBuff[2] = 0; // SOCKS_NOAUTH
    
    if (send(sockfd, ptrBuff, buffer-ptrBuff, 0) == -1)
                perror("send");
    

    You are passing to send function the len: buffer-ptrBuff. But those values (addresses) are the same address so the result is 0.

    I guess, that what you want to do is

    char buffer[256], *ptrBuff;
    ptrBuff = buffer;
    
    *(ptrBuff++) = 5; // socks version
    *(ptrBuff++) = 1;
    *(ptrBuff++) = 0; // SOCKS_NOAUTH
    
    if (send(sockfd, ptrBuff, buffer-ptrBuff, 0) == -1)
                perror("send");
    

    In this case ptrBuff value (address pointed) is incremented at each assignment so when send is called len (buffer-ptrBuff) will be 3.

    At the end what you are seeing is correct due to the fact that you are sending nothing to server side, and no answer can be received on client side. So recv waits forever (blocking mode) for an answer that will never answered.