BSD(Mac OS X和OpenBSD)和Linux(Ubuntu)之间的套接字行为不同

时间:2014-01-08 16:19:55

标签: c linux sockets bsd

我最初在我的mac上写了一个中间人/代理服务器。本质上,代理创建套接字并等待连接,然后连接到另一个应用程序。这在OS X和OpenBSD中完美运行;但是,在将代理移植到Ubuntu时,它无法按预期工作。

此代理有两个实例在运行,侦听两个不同的端口。当在Ubuntu上运行时,所有流量都通过单个端口。将套接字设置为非阻塞(通过fcntl)时遇到问题,有时它会因“无效参数”而失败

我正在使用sys / socket。

我错过了这个港口的任何陷阱?

修改

我相信有两个问题。一个是无效参数,另一个是流量被推送到不同的端口。

traffic issue

服务1绑定到代理实例1,后者然后绑定到黑盒子上的相应服务,这将启动服务2.但是,由于某种原因,在Ubuntu上它连接到正在侦听错误端口的实例1。

编辑fcntl无效参数的解决方案:

发现为什么我得到了无效的论点,遗憾的是我还有另一个问题。 fcntl(fd,cmd,arg)

cmd - F_SETFL(长)

我传入一个指向int而不是long原语的指针。

修改

#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);
        }

}

2 个答案:

答案 0 :(得分:0)

只是一个猜测,但我认为你的问题是你处理注射插座的方式。我不知道你在// 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()它(例如,在fork()之后立即返回)以避免泄漏文件描述符。

答案 1 :(得分:0)

事实证明SO_REUSEADDR套接字选项是个问题。我删除了我设置套接字选项,一切顺利。

This引导我找到解决方案。