2014-01-08 5 views
0

처음에는 man-in-the-middle/proxy 서버를 작성했습니다. 기본적으로 프록시는 소켓을 생성하고 연결을 기다린 다음 다른 응용 프로그램에 연결합니다. 이것은 OS X와 ​​OpenBSD에서 완벽하게 작동합니다; 그러나 우분투에 프록시를 이식 할 때 의도 한대로 작동하지 않습니다.BSD (Mac OS X와 ​​OpenBSD)와 Linux (Ubuntu)에서 소켓 동작이 다름

2 개의 개별 포트에서 수신 대기중인이 프록시의 두 인스턴스가 실행 중입니다. 이것이 Ubuntu에서 실행되면 모든 트래픽이 단일 포트를 통과합니다. 또한 소켓을 nonblocking (fcntl을 통해)으로 설정할 때 "잘못된 인수"와 함께 실패 할 때 문제가 발생합니다.

sys/socket을 사용하고 있습니다.

이 포트 중 누락 된 함정이 있습니까?

편집 :

나는 두 가지 문제가있다 생각합니다. 하나는 잘못된 인수이고, 다른 하나는 트래픽이 다른 포트로 푸시되고 있다는 것입니다.

traffic issue

서비스 다음은 인스턴스 하나에 연결 우분투에 어떤 이유로, 그러나 서비스 2 개막 블랙 박스의 해당 서비스에 다시 결합 프록시 인스턴스 1-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); 
     } 

} 
+0

논 블로킹 잘못된 인수는 의심스러운 것처럼 어떤 종류의 버그처럼 들립니다. 관련 코드를 게시하는 것이 좋습니다. – Duck

+0

참조 용 코드 – jbh

+1

** 실제 ** 코드를 게시하십시오. 시작 지점 참조가 아닙니다. 'fcntl()'에 대한 호출 중 실패한 것은'F_GETFL' 또는'F_SETFL'을 호출하는 호출입니까? –

답변

0

SO_REUSEADDR 소켓 옵션이 문제였습니다. 나는 소켓 옵션을 설정하고 모든 것이 잘 작동하지 않게했다.

This 나를 해결책으로 인도하십시오.

+0

믿을 수 없습니다. – EJP

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()가 반환 된 직후) 파일 설명자가 유출되지 않도록합니다.

+0

이것은 문제에 대한 나의 초기 생각이었고, 이것은 논 블럭킹 잘못된 인수 문제와 관련이 있다고 생각합니다. 이전에는 포크를 제거하고 두 포트 대신 단일 포트에 퍼널되는 모든 트래픽 문제와 BSD 기반 시스템에서 어떻게 작동하는지에 대해 계속 조사했습니다. – jbh