Coverage Report

Created: 2023-06-07 06:21

/src/h2o/deps/libyrmcds/connect.c
Line
Count
Source (jump to first uncovered line)
1
// (C) 2013, 2016 Cybozu.
2
3
#include "yrmcds.h"
4
5
#include <errno.h>
6
#include <fcntl.h>
7
#include <netdb.h>
8
#include <netinet/in.h>
9
#include <netinet/tcp.h>
10
#include <poll.h>
11
#include <stdio.h>
12
#include <stdlib.h>
13
#include <string.h>
14
#include <sys/socket.h>
15
#include <sys/types.h>
16
#include <unistd.h>
17
18
// workaround for a known-bug in NetBSD, from https://lists.gnu.org/archive/html/bug-gnulib/2010-02/msg00071.html
19
#ifndef AI_V4MAPPED
20
#define AI_V4MAPPED 0
21
#endif
22
23
0
static yrmcds_error connect_to_server(const char* node, uint16_t port, int* server_fd) {
24
0
    if( node == NULL )
25
0
        return YRMCDS_BAD_ARGUMENT;
26
27
0
    long fl;
28
0
    char sport[8];
29
0
    snprintf(sport, sizeof(sport), "%u", (unsigned int)port);
30
31
0
    struct addrinfo hint, *res;
32
0
    memset(&hint, 0, sizeof(hint));
33
0
    hint.ai_family = AF_INET;  // prefer IPv4
34
0
    hint.ai_socktype = SOCK_STREAM;
35
0
    hint.ai_flags = AI_NUMERICSERV|AI_ADDRCONFIG;
36
0
    int e = getaddrinfo(node, sport, &hint, &res);
37
0
    if( e == EAI_FAMILY || e == EAI_NONAME
38
0
#ifdef EAI_ADDRFAMILY
39
0
        || e == EAI_ADDRFAMILY
40
0
#endif
41
0
#ifdef EAI_NODATA
42
0
        || e == EAI_NODATA
43
0
#endif
44
0
        ) {
45
0
        hint.ai_family = AF_INET6;
46
        // intentionally drop AI_ADDRCONFIG to support IPv6 link-local address.
47
        // see https://github.com/cybozu/yrmcds/issues/40
48
0
        hint.ai_flags = AI_NUMERICSERV|AI_V4MAPPED;
49
0
        e = getaddrinfo(node, sport, &hint, &res);
50
0
    }
51
0
    if( e == EAI_SYSTEM ) {
52
0
        return YRMCDS_SYSTEM_ERROR;
53
0
    } else if( e != 0 ) {
54
0
        return YRMCDS_NOT_RESOLVED;
55
0
    }
56
57
0
    int s = socket(res->ai_family,
58
0
                   res->ai_socktype
59
0
#ifdef __linux__
60
0
                   | SOCK_NONBLOCK | SOCK_CLOEXEC
61
0
#endif
62
0
                   , res->ai_protocol);
63
0
    if( s == -1 ) {
64
0
        e = errno;
65
0
        freeaddrinfo(res);
66
0
        errno = e;
67
0
        return YRMCDS_SYSTEM_ERROR;
68
0
    }
69
#ifndef __linux__
70
    fl = fcntl(s, F_GETFD, 0);
71
    fcntl(s, F_SETFD, fl | FD_CLOEXEC);
72
    fl = fcntl(s, F_GETFL, 0);
73
    fcntl(s, F_SETFL, fl | O_NONBLOCK);
74
#endif
75
0
    e = connect(s, res->ai_addr, res->ai_addrlen);
76
0
    freeaddrinfo(res);
77
0
    if( e == -1 && errno != EINPROGRESS ) {
78
0
        e = errno;
79
0
        close(s);
80
0
        errno = e;
81
0
        return YRMCDS_SYSTEM_ERROR;
82
0
    }
83
84
0
    if( e != 0 ) {
85
0
        struct pollfd fds;
86
0
        fds.fd = s;
87
0
        fds.events = POLLOUT;
88
0
        int n = poll(&fds, 1, 5000);
89
0
        if( n == 0 ) { // timeout
90
0
            close(s);
91
0
            return YRMCDS_TIMEOUT;
92
0
        }
93
0
        if( n == -1 ) {
94
0
            e = errno;
95
0
            close(s);
96
0
            errno = e;
97
0
            return YRMCDS_SYSTEM_ERROR;
98
0
        }
99
100
0
        if( fds.revents & (POLLERR|POLLHUP|POLLNVAL) ) {
101
0
            close(s);
102
0
            return YRMCDS_DISCONNECTED;
103
0
        }
104
0
        socklen_t l = sizeof(e);
105
0
        if( getsockopt(s, SOL_SOCKET, SO_ERROR, &e, &l) == -1 ) {
106
0
            close(s);
107
0
            return YRMCDS_SYSTEM_ERROR;
108
0
        }
109
0
        if( e != 0 ) {
110
0
            close(s);
111
0
            errno = e;
112
0
            return YRMCDS_SYSTEM_ERROR;
113
0
        }
114
0
    }
115
0
    fl = fcntl(s, F_GETFL, 0);
116
0
    if( fcntl(s, F_SETFL, fl & ~O_NONBLOCK) == -1 ) {
117
0
        e = errno;
118
0
        close(s);
119
0
        errno = e;
120
0
        return YRMCDS_SYSTEM_ERROR;
121
0
    }
122
0
    int ok = 1;
123
0
    if( setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &ok, sizeof(ok)) == -1 ) {
124
0
        e = errno;
125
0
        close(s);
126
0
        errno = e;
127
0
        return YRMCDS_SYSTEM_ERROR;
128
0
    }
129
0
    *server_fd = s;
130
0
    return YRMCDS_OK;
131
0
}
132
133
0
yrmcds_error yrmcds_connect(yrmcds* c, const char* node, uint16_t port) {
134
0
    if( c == NULL )
135
0
        return YRMCDS_BAD_ARGUMENT;
136
0
#ifndef LIBYRMCDS_NO_INTERNAL_LOCK
137
0
    int e = pthread_mutex_init(&(c->lock), NULL);
138
0
    if( e != 0 ) {
139
0
        errno = e;
140
0
        return YRMCDS_SYSTEM_ERROR;
141
0
    }
142
0
#endif // ! LIBYRMCDS_NO_INTERNAL_LOCK
143
0
    int server_fd;
144
0
    yrmcds_error err = connect_to_server(node, port, &server_fd);
145
0
    if( err != YRMCDS_OK )
146
0
        return err;
147
0
    c->sock = server_fd;
148
0
    c->serial = 0;
149
0
    c->compress_size = 0;
150
0
    c->recvbuf = (char*)malloc(1 << 20);
151
0
    if( c->recvbuf == NULL ) {
152
0
        close(server_fd);
153
0
#ifndef LIBYRMCDS_NO_INTERNAL_LOCK
154
0
        pthread_mutex_destroy(&(c->lock));
155
0
#endif
156
0
        return YRMCDS_OUT_OF_MEMORY;
157
0
    }
158
0
    c->capacity = 1 << 20;
159
0
    c->used = 0;
160
0
    c->last_size = 0;
161
0
    c->decompressed = NULL;
162
0
    c->invalid = 0;
163
0
    c->text_mode = 0;
164
0
    c->rserial = 0;
165
0
    return YRMCDS_OK;
166
0
}
167
168
0
yrmcds_error yrmcds_cnt_connect(yrmcds_cnt* c, const char* node, uint16_t port) {
169
0
    if( c == NULL )
170
0
        return YRMCDS_BAD_ARGUMENT;
171
0
#ifndef LIBYRMCDS_NO_INTERNAL_LOCK
172
0
    int e = pthread_mutex_init(&(c->lock), NULL);
173
0
    if( e != 0 ) {
174
0
        errno = e;
175
0
        return YRMCDS_SYSTEM_ERROR;
176
0
    }
177
0
#endif // ! LIBYRMCDS_NO_INTERNAL_LOCK
178
0
    int server_fd;
179
0
    yrmcds_error err = connect_to_server(node, port, &server_fd);
180
0
    if( err != YRMCDS_OK )
181
0
        return err;
182
0
    c->sock = server_fd;
183
0
    c->serial = 0;
184
0
    c->recvbuf = (char*)malloc(4096);
185
0
    if( c->recvbuf == NULL ) {
186
0
        close(server_fd);
187
0
#ifndef LIBYRMCDS_NO_INTERNAL_LOCK
188
0
        pthread_mutex_destroy(&(c->lock));
189
0
#endif
190
0
        return YRMCDS_OUT_OF_MEMORY;
191
0
    }
192
0
    c->capacity = 4096;
193
0
    c->used = 0;
194
0
    c->last_size = 0;
195
0
    c->invalid = 0;
196
0
    c->stats.count = c->stats.capacity = 0;
197
0
    c->stats.records = NULL;
198
0
    return YRMCDS_OK;
199
0
}