Сокеты ведут себя по-разному в BSD (Mac OS X и OpenBSD) и Linux (Ubuntu).

Сначала я написал прокси-сервер «человек посередине» на своем Mac. По сути, прокси создает сокет и ожидает соединения, а затем подключается к другому приложению. Это работает безупречно в OS X и в OpenBSD; однако при портировании прокси-сервера в Ubuntu он не работает должным образом.

Есть два запущенных экземпляра этого прокси, которые прослушивают два разных порта. Когда это выполняется в Ubuntu, весь трафик проходит через один порт. Я также сталкиваюсь с проблемой при настройке сокета на неблокирующий (через fcntl), что иногда происходит сбой с «Недопустимым аргументом».

Я использую sys/socket.

Какие-нибудь подводные камни во время этого порта, которые я упускаю?

ИЗМЕНИТЬ:

Я считаю, что есть две проблемы. Одним из них является недопустимый аргумент, а другим — то, что трафик перенаправляется на разные порты.

проблема с трафиком

Служба 1 привязывается к экземпляру прокси-сервера 1, который затем привязывается к соответствующей службе в черном ящике, что запускает службу 2. Однако по какой-то причине в Ubuntu он подключается к экземпляру 1, который прослушивает неправильный порт.

РЕДАКТИРОВАТЬ решение недопустимого аргумента для fcntl:

Выяснил, почему я получил неверный аргумент, к сожалению, у меня все еще есть другая проблема. fcntl(fd, cmd, аргумент)

cmd - F_SETFL (длинный)

Я передавал указатель на int вместо длинного примитива.

ИЗМЕНИТЬ:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <netdb.h>
#include <string.h>
#include <signal.h>
#include <assert.h>
#include <syslog.h>


#include <sys/types.h>
#include <sys/select.h>
#include <sys/file.h>
#include <sys/ioctl.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/wait.h>

#include <netinet/in.h>

#include <arpa/ftp.h>
#include <arpa/inet.h>
#include <arpa/telnet.h>


void cleanup(int sig)
{
    syslog(LOG_INFO, "Cleaning up...");
    exit(0);
}

void sigreap(int sig)
{
    int status;
    pid_t p;
    while ((p = waitpid(-1, &status, WNOHANG)) > 0) {
    syslog(LOG_INFO, "sigreap: pid=%d, status=%d\n", (int) p, status);
    }
    /* doh! */
    signal(SIGCHLD, sigreap);
}

void set_nonblock(int fd)
{
    long fl;
    int x;
    fl = fcntl(fd, F_GETFL);
    if (fl < 0) {
    syslog(LOG_ERR, "fcntl F_GETFL: FD %d: %s", fd, strerror(errno));
    exit(1);
    }
    fl |= O_NONBLOCK;
    x = fcntl(fd, F_SETFL, fl);
    if (x < 0) {
    syslog(LOG_ERR, "fcntl F_SETFL: FD %d: %s", fd, strerror(errno));
    exit(1);
    }
}


int create_server_sock(char *addr, int port)
{
    int addrlen, s, on = 1, x;
    static struct sockaddr_in client_addr;

    s = socket(AF_INET, SOCK_STREAM, 0);
    if (s < 0)
        perror("socket"), exit(1);

    addrlen = sizeof(client_addr);
    memset(&client_addr, '\0', addrlen);
    client_addr.sin_family = AF_INET;
    client_addr.sin_addr.s_addr = INADDR_ANY; //inet_addr(addr);
    client_addr.sin_port = htons(port);
    setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, 4);
    x = bind(s, (struct sockaddr *) &client_addr, addrlen);
    if (x < 0)
        perror("bind"), exit(1);

    x = listen(s, 5);
    if (x < 0)
        perror("listen"), exit(1);

    return s;
}

int open_remote_host(char *host, int port)
{
    struct sockaddr_in rem_addr;
    int len, s, x;
    struct hostent *H;
    int on = 1;

    H = gethostbyname(host);
    if (!H)
    return (-2);

    len = sizeof(rem_addr);

    s = socket(AF_INET, SOCK_STREAM, 0);
    if (s < 0)
    return s;

    setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, 4);

    len = sizeof(rem_addr);
    memset(&rem_addr, '\0', len);
    rem_addr.sin_family = AF_INET;
    memcpy(&rem_addr.sin_addr, H->h_addr, H->h_length);
    rem_addr.sin_port = htons(port);
    x = connect(s, (struct sockaddr *) &rem_addr, len);
    if (x < 0) {
    close(s);
    return x;
    }
    set_nonblock(s);
    return s;
}

int get_hinfo_from_sockaddr(struct sockaddr_in addr, int len, char *fqdn)
{
    struct hostent *hostinfo;

    hostinfo = gethostbyaddr((char *) &addr.sin_addr.s_addr, len, AF_INET);
    if (!hostinfo) {
    sprintf(fqdn, "%s", inet_ntoa(addr.sin_addr));
    return 0;
    }
    if (hostinfo && fqdn)
    sprintf(fqdn, "%s [%s]", hostinfo->h_name, inet_ntoa(addr.sin_addr));
    return 0;
}


int wait_for_connection(int s)
{
    int newsock;
    socklen_t len;
    static struct sockaddr_in peer;

    len = sizeof(struct sockaddr);
    newsock = accept(s, (struct sockaddr *) &peer, &len);
    /* dump_sockaddr (peer, len); */
    if (newsock < 0) {
    if (errno != EINTR)
        perror("accept");
    }
    get_hinfo_from_sockaddr(peer, len, client_hostname);
    set_nonblock(newsock);
    return (newsock);
}



static int print_bytes(char * buf, ssize_t length)
{
    int i = 0, ascii_off = 0, hex_off = 0;
    char * hex_bytes = (char *) calloc(32*2,1);
    char * ascii_bytes = (char *) calloc(32*2,1);

    for( i = 0; i < length; i++)
    {
        hex_off += sprintf(hex_bytes+hex_off,"%02X ",(unsigned char)buf[i]);
        if(buf[i] >= '!' && buf[i] <= '~')
            ascii_off += sprintf(ascii_bytes+ascii_off,"%c ",buf[i]);
        else
            ascii_off += sprintf(ascii_bytes+ascii_off,". ");
        if( ((i+1) % 16 == 0) || i == length-1 )
        {
            fprintf(stderr,"%-48s\t%s\n",hex_bytes,ascii_bytes);
            free(hex_bytes);
            free(ascii_bytes);
            hex_bytes = (char *) calloc(32*2,1);
            ascii_bytes = (char *) calloc(32*2,1);
            ascii_off = 0;
            hex_off = 0;
            if(i != length-1)
                fprintf(stderr,"\t");
        }


    }
    free(hex_bytes);
    free(ascii_bytes);
    return 0;
}


int mywrite(int fd, char *buf, int *len)
{
    int x = write(fd, buf, *len);
    print_bytes(buf,*len);
    if (x < 0)
        return x;
    if (x == 0)
        return x;
    if (x != *len)
        memmove(buf, buf+x, (*len)-x);
    *len -= x;
    return x;
}


void service_client(int fd1, int fd2, int injfd)
{
    int maxfd;

    cfd = fd1;
    sfd = fd2;
    char *sbuf;
    char *cbuf;
    int x, n;
    int cbo = 0;
    int sbo = 0;
    int ibo = 0;
    fd_set R;
    int max_clients = 30;
    int i = 0,s = 0, addrlen;
    struct sockaddr_in address;

    sbuf = malloc(BUF_SIZE);
    cbuf = malloc(BUF_SIZE);
    cntrlbuf = calloc(1,BUF_SIZE);
    char * injbuf = malloc(BUF_SIZE);
    maxfd = cfd > sfd ? cfd : sfd; 
    maxfd = injfd > maxfd ? injfd : maxfd;
    maxfd++;
    maxfd++;

    struct inj_con * ptr;

    while (1) {
        struct timeval to;
        if (cbo) {
            process_packet(cbuf,&cbo,sfd);
        }
        if (sbo) {
            process_packet(sbuf,&sbo,cfd);
        }
        if (ibo) {
            process_packet(injbuf,&ibo,cfd);
        }
        if (cntrlo) {
            fprintf(stderr,"\033[33;1mControl->(%d), len = 0x%x (%d):\033[0m\n\t",cfd,cntrlo,cntrlo);
            if (mywrite(cfd, cntrlbuf, &cntrlo) < 0 && errno != EWOULDBLOCK) {
                syslog(LOG_ERR, "write %d: %s", cfd, strerror(errno));
                exit(1);
            }
        }
        FD_ZERO(&R);
        if (cbo < BUF_SIZE)
            FD_SET(cfd, &R);
        if (sbo < BUF_SIZE)
            FD_SET(sfd, &R);
        if (ibo < BUF_SIZE)
            FD_SET(injfd, &R);



        to.tv_sec = 0;
        to.tv_usec = 1000;
        x = select(max_clients+3, &R, 0, 0, &to);
        if (x > 0 || cntrl_q->item_count > 0) {
            if (FD_ISSET(injfd, &R)) {
                int new_socket;
                if((new_socket = accept(injfd, (struct sockaddr *) &address, (socklen_t *) &addrlen)) < 0)
                {
                    perror("accept");
                    exit(1);
                }


                // Truncated
                //
            }

            char * temp_pkt; 

            if (FD_ISSET(cfd, &R)) {
                temp_pkt = (char *) calloc(BUF_SIZE,1);
                n = read(cfd, temp_pkt, BUF_SIZE);
                syslog(LOG_INFO, "read %d bytes from CLIENT (%d)", n, cfd);
                if (n > 0) {
                    push_msg(s_q,temp_pkt,n);
                } else {
                    free(temp_pkt);
                    close(cfd);
                    close(sfd);
                    close_injection_sockets(); 
                    close(injfd);
                    _exit(0);
                }
            } 
            if (FD_ISSET(sfd, &R)) {
                temp_pkt = (char *) calloc(BUF_SIZE,1);
                n = read(sfd, temp_pkt, BUF_SIZE);
                syslog(LOG_INFO, "read %d bytes from SERVER (%d)\n", n, sfd);
                if (n > 0) {
                    push_msg(c_q,temp_pkt,n);
                } else {
                    free(temp_pkt);
                    close(sfd);
                    close(cfd);
                    close_injection_sockets(); 
                    close(injfd);
                    _exit(0);
                }
            }

            if(cntrlo == 0 && cntrl_q->front != NULL)
            {
                struct msg * tmp = next_msg(cntrl_q);
                if(tmp != NULL)
                {
                    memcpy(cntrlbuf,tmp->msg,tmp->len);
                    cntrlo += tmp->len;
                    free(tmp->msg);
                    free(tmp);
                }
            }
            if(sbo == 0 && c_q->front != NULL)
            {
                struct msg * tmp = next_msg(c_q);
                if(tmp != NULL)
                {
                    memcpy(sbuf,tmp->msg,tmp->len);
                    sbo += tmp->len;
                    free(tmp->msg);
                    free(tmp);
                }
            }
            if(cbo == 0 && s_q->front != NULL)
            {
                struct msg * tmp = next_msg(s_q);
                if(tmp != NULL)
                {
                    memcpy(cbuf,tmp->msg,tmp->len);
                    cbo += tmp->len;
                    free(tmp->msg);
                    free(tmp);
                }
            }
            if(ibo == 0 && inj_q->front != NULL)
            {
                struct msg * tmp = next_msg(inj_q);
                if(tmp != NULL)
                {
                    memcpy(injbuf,tmp->msg,tmp->len);
                    ibo += tmp->len;
                    free(tmp->msg);
                    free(tmp);
                }
            }

        } else if (x < 0 && errno != EINTR) {
            close(sfd);
            close(cfd);
            _exit(0);
        }

    }
}


static int create_injection_sock(int injectionport)
{
    struct sockaddr_in serv_addr;
    int portno = injectionport;

    int sockfd = socket(AF_INET, SOCK_STREAM, 0);

    if (sockfd <0)
    {
        perror("ERROR: opening socket");
        exit(1);
    }

    bzero((char *) &serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = INADDR_ANY;
    serv_addr.sin_port = htons(portno);

    if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
    {
        perror("ERROR: on bind");
        exit(1);
    }

    if (listen(sockfd,5) < 0 )
    {
        perror("listen injection");
        exit(1);
    }
    return sockfd;
}


int main(int argc, char *argv[])
{

    if (!(5 == argc || 6 == argc)) {
        fprintf(stderr, "usage: %s laddr lport rhost rport [injectionport]\n", argv[0]);
        exit(1);
    }

    char *localaddr = strdup(argv[1]);
    int localport = atoi(argv[2]);
    char *remoteaddr = strdup(argv[3]);
    int remoteport = atoi(argv[4]);
    int injectionport;
    if(argc == 6)
        injectionport  = atoi(argv[5]);
    int client, server;
    int master_sock;
    int injection_sock = -1;

    cntrl_q = (struct item_queue *) calloc(1,sizeof(struct item_queue));
    inj_q = (struct item_queue *) calloc(1,sizeof(struct item_queue));
    s_q = (struct item_queue *) calloc(1,sizeof(struct item_queue));
    c_q = (struct item_queue *) calloc(1,sizeof(struct item_queue));
    identities = (struct item_queue *) calloc(1,sizeof(struct item_queue));


    assert(localaddr);
    assert(localport > 0);
    assert(remoteaddr);
    assert(remoteport > 0);
    if(argc == 6)
        assert(injectionport > 0);

    openlog(argv[0], LOG_PID, LOG_LOCAL4);

    signal(SIGINT, cleanup);
    signal(SIGCHLD, sigreap);
    if(argc == 6)
        injection_sock = create_injection_sock(injectionport);
    master_sock = create_server_sock(localaddr, localport);
    for (;;) {
        if ((client = wait_for_connection(master_sock)) < 0)
            continue;
        if ((server = open_remote_host(remoteaddr, remoteport)) < 0)
            continue;
        if (!fork()) {
            service_client(client, server, injection_sock);
            }
            close(client);
            close(server);
        }

}

person jbh    schedule 08.01.2014    source источник
comment
Неблокирующий неверный аргумент звучит подозрительно, как какая-то ошибка. Возможно, вам следует опубликовать соответствующий код.   -  person Duck    schedule 08.01.2014
comment
добавлен код для справки   -  person jbh    schedule 08.01.2014
comment
Опубликуйте свой фактический код, а не отправную точку. Какой вызов fcntl() не работает, тот, который вызывает F_GETFL или F_SETFL?   -  person Adam Rosenfield    schedule 08.01.2014
comment
F_SETFL — это место, где он терпит неудачу, но, как ни странно, не всегда.   -  person jbh    schedule 08.01.2014
comment
Вы действительно заботитесь о написании этого низкоуровневого кода сокета? Если нет, взгляните на libevent или libev. Я не использовал libev в гневе, но я использовал libevent для чего-то вроде того, что вы делаете, и нашел его очень эффективным. Избегание этого мема тоже поможет.   -  person kfsone    schedule 08.01.2014
comment
Для записи «человек посередине» — это тип атаки, при которой вы помещаете что-то в соединение, чтобы прослушивать и/или изменять то, что передается. Если вы не используете это для этого, вы делаете прокси. Кроме того, прочитайте Как спросить и убедитесь, что вы следуете этому.   -  person Fund Monica's Lawsuit    schedule 24.10.2017


Ответы (2)


Просто предположение, но я думаю, что ваша проблема в том, как вы обращаетесь с гнездом для инъекций. Я не знаю, что вы удалили в блоке кода // TRUNCATED, но в основном вы делаете следующее:

// Parent process -- bind & listen on the injection socket
injfd = socket(...);
bind(injfd, ...);
listen(injfd, ...);
...
while (1)
{
    client = wait_for_connection();
    ...
    if (!fork())
    {
        // Each child process
        ...
        if (stuff && FD_ISSET(injfd, &R)) {
            new_socket = accept(injfd);
            ...
        }
        ...
    }
    ...
}

Другими словами, все ваши дочерние процессы прослушивают один и тот же сокет внедрения и пытаются принимать соединения. Я не уверен, что это даже четко определенное поведение, но даже в лучшем случае, когда новое соединение поступает на порт инъекции, процесс, который собирается принять соединение, может быть случайным и неуправляемым. Это может быть любой из дочерних процессов или даже родительский процесс.

Чтобы контролировать это поведение, вам нужно решить, кто должен прослушивать соединения в сокете для инъекций. Если только родитель должен слушать, то только родитель должен вызвать для него accept() или передать его в качестве аргумента select(). Точно так же, если только конкретный ребенок должен слушать, то только этот ребенок должен вызвать accept() или передать его select(). Все другие процессы, которые игнорируют этот сокет, должны close() его close() при первой же возможности (например, сразу после возврата fork()), чтобы избежать утечки файловых дескрипторов.

person Adam Rosenfield    schedule 08.01.2014
comment
Это была моя первоначальная мысль по этому вопросу, и я думаю, что это относится к неблокирующей проблеме с недопустимым аргументом. Итак, ранее я удалил форк, но все еще сталкивался с проблемой направления всего трафика на один порт вместо двух, как это должно быть, и с тем, как это работает в системах на основе BSD. - person jbh; 08.01.2014

Оказывается, проблема заключалась в параметре сокета SO_REUSEADDR. Я удалил установку этой опции сокета, и все сработало.

Это Приведи меня к решению.

person jbh    schedule 08.01.2014
comment
Невозможно поверить. - person user207421; 24.10.2017