Пример #1
0
int rmt_tcp_context_connect(tcp_context *tc, const char *host, int port,
                                   const struct timeval *timeout,
                                   const char *source_addr) {
    tc->port = port;

    if(tc->host != NULL)
    {
        free(tc->host);
        tc->host = NULL;
    }

    tc->host = rmt_strdup(host);

    if (timeout) {
        if (tc->timeout == NULL) {
            tc->timeout = rmt_alloc(sizeof(struct timeval));
        }

        rmt_memcpy(tc->timeout, timeout, sizeof(struct timeval));
    } else {
        if (tc->timeout)
            rmt_free(tc->timeout);
        tc->timeout = NULL;
    }

    if (tc->source_addr != NULL) {
        free(tc->source_addr);
        tc->source_addr = NULL;
    }

    if (source_addr) {
        tc->source_addr = rmt_strdup(source_addr);
    }

    if (!(tc->flags & RMT_RECONNECT)) {
        tc->flags |= RMT_RECONNECT;
    }
    
    return _rmt_tcp_context_connect(tc);
}
Пример #2
0
int rmt_tcp_context_connect_old(tcp_context *tc, const char *host, int port,
                                   const struct timeval *timeout,
                                   const char *source_addr) {
    int ret;
    int s, n;
    char _port[6];  /* strlen("65535"); */
    struct addrinfo hints, *servinfo, *bservinfo, *p, *b;
    int blocking = (tc->flags & RMT_BLOCK);
    int reuseaddr = (tc->flags & RMT_REUSEADDR);
    int reuses = 0;
    int yes = 1;

    tc->port = port;

    if(tc->host != NULL)
    {
        free(tc->host);
        tc->host = NULL;
    }

    tc->host = rmt_strdup(host);

    if (timeout) {
        if (tc->timeout == NULL) {
            tc->timeout = rmt_alloc(sizeof(struct timeval));
        }

        rmt_memcpy(tc->timeout, timeout, sizeof(struct timeval));
    } else {
        if (tc->timeout)
            rmt_free(tc->timeout);
        tc->timeout = NULL;
    }

    if (tc->source_addr != NULL) {
        free(tc->source_addr);
        tc->source_addr = NULL;
    }

    if (source_addr) {
        tc->source_addr = rmt_strdup(source_addr);
    }

    rmt_snprintf(_port, 6, "%d", port);
    rmt_memset(&hints,0,sizeof(hints));
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;

    /* Try with IPv6 if no IPv4 address was found. We do it in this order since
     * in a Redis client you can't afford to test if you have IPv6 connectivity
     * as this would add latency to every connect. Otherwise a more sensible
     * route could be: Use IPv6 if both addresses are available and there is IPv6
     * connectivity. */
    if ((ret = rmt_getaddrinfo(tc->host,_port,&hints,&servinfo)) != 0) {
         hints.ai_family = AF_INET6;
         if ((ret = rmt_getaddrinfo(tc->host,_port,&hints,&servinfo)) != 0) {
            log_error("ERROR: rmt_getaddrinfo error: %s", gai_strerror(ret));
            return RMT_ERROR;
        }
    }
    
    for (p = servinfo; p != NULL; p = p->ai_next) {
addrretry:
        if ((s = socket(p->ai_family,p->ai_socktype,p->ai_protocol)) == -1)
            continue;

        tc->sd = s;
        if (rmt_set_nonblocking(tc->sd) < 0)
        {
            log_error("ERROR: set nonblock on socket %d on addr '%s:%d' failed: %s", 
                s, tc->host, tc->port, strerror(errno));
            rmt_tcp_context_close_sd(tc);
            goto error;
        }
        
        if (tc->source_addr) {
            int bound = 0;
            /* Using getaddrinfo saves us from self-determining IPv4 vs IPv6 */
            if ((ret = rmt_getaddrinfo(tc->source_addr, NULL, &hints, &bservinfo)) != 0) {
                log_error("ERROR: can't get %s addr: %s", tc->source_addr, gai_strerror(ret));
                rmt_tcp_context_close_sd(tc);
                goto error;
            }

            if (reuseaddr) {
                n = 1;
                if (rmt_setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*) &n,
                               sizeof(n)) < 0) 
                {
                    log_error("ERROR: set SO_REUSEADDR on socket %d on addr '%s:%d' failed: %s", 
                        tc->sd, tc->host, tc->port, strerror(errno));
                    rmt_tcp_context_close_sd(tc);
                    goto error;
                }
            }

            for (b = bservinfo; b != NULL; b = b->ai_next) {
                if (bind(s,b->ai_addr,b->ai_addrlen) != -1) {
                    bound = 1;
                    break;
                }
            }
            
            freeaddrinfo(bservinfo);
            
            if (!bound) {
                log_error("ERROR: can't bind socket: %s", strerror(errno));
                rmt_tcp_context_close_sd(tc);
                goto error;
            }
        }
        
        if (connect(s,p->ai_addr,p->ai_addrlen) == -1) {
            if (errno == EHOSTUNREACH) {
                rmt_tcp_context_close_sd(tc);
                continue;
            } else if (errno == EINPROGRESS && !blocking) {
                /* This is ok. */
            } else if (errno == EADDRNOTAVAIL && reuseaddr) {
                if (++reuses >= RMT_CONNECT_RETRIES) {
                    log_error("ERROR: retry too many times: %s", strerror(errno));
                    rmt_tcp_context_close_sd(tc);
                    goto error;
                } else {
                    goto addrretry;
                }
            } else {
                if (rmt_tcp_context_wait_ready(tc,tc->timeout) != RMT_OK)
                    goto error;
            }
        }
        
        if (blocking && rmt_set_blocking(tc->sd) < 0)
        {
            log_error("ERROR: set block on socket %d on addr '%s:%d' failed: %s", 
                tc->sd, tc->host, tc->port, strerror(errno));
            rmt_tcp_context_close_sd(tc);
            goto error;
        }

        if (rmt_setsockopt(tc->sd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) == -1)
        {
            log_error("ERROR: set TCP_NODELAY on socket %d on addr '%s:%d' failed: %s", 
                tc->sd, tc->host, tc->port, strerror(errno));
            rmt_tcp_context_close_sd(tc);
            goto error;
        }
        
        tc->flags |= RMT_CONNECTED;
        ret = RMT_OK;
        goto end;
    }
    
    if (p == NULL) {
        log_error("ERROR: can't create socket: %s", strerror(errno));
        goto error;
    }

error:
    ret = RMT_ERROR;
end:
    freeaddrinfo(servinfo);
    return ret;  // Need to return RMT_OK if alright
}