]> arthur.barton.de Git - netdata.git/blob - src/socket.c
dns_query_time plugin: replace "." with "_" in dimensions
[netdata.git] / src / socket.c
1 #include "common.h"
2
3 // connect_to()
4 //
5 // definition format:
6 //
7 //    [PROTOCOL:]IP[%INTERFACE][:PORT]
8 //
9 // PROTOCOL  = tcp or udp
10 // IP        = IPv4 or IPv6 IP or hostname, optionally enclosed in [] (required for IPv6)
11 // INTERFACE = for IPv6 only, the network interface to use
12 // PORT      = port number or service name
13
14 int connect_to(const char *definition, int default_port, struct timeval *timeout) {
15     struct addrinfo hints;
16     struct addrinfo *ai_head = NULL, *ai = NULL;
17
18     char buffer[strlen(definition) + 1];
19     strcpy(buffer, definition);
20
21     char default_service[10 + 1];
22     snprintfz(default_service, 10, "%d", default_port);
23
24     char *host = buffer, *service = default_service, *interface = "";
25     int protocol = IPPROTO_TCP, socktype = SOCK_STREAM;
26     uint32_t scope_id = 0;
27
28     if(strncmp(host, "tcp:", 4) == 0) {
29         host += 4;
30         protocol = IPPROTO_TCP;
31         socktype = SOCK_STREAM;
32     }
33     else if(strncmp(host, "udp:", 4) == 0) {
34         host += 4;
35         protocol = IPPROTO_UDP;
36         socktype = SOCK_DGRAM;
37     }
38
39     char *e = host;
40     if(*e == '[') {
41         e = ++host;
42         while(*e && *e != ']') e++;
43         if(*e == ']') {
44             *e = '\0';
45             e++;
46         }
47     }
48     else {
49         while(*e && *e != ':' && *e != '%') e++;
50     }
51
52     if(*e == '%') {
53         *e = '\0';
54         e++;
55         interface = e;
56         while(*e && *e != ':') e++;
57     }
58
59     if(*e == ':') {
60         *e = '\0';
61         e++;
62         service = e;
63     }
64
65     debug(D_CONNECT_TO, "Attempting connection to host = '%s', service = '%s', interface = '%s', protocol = %d (tcp = %d, udp = %d)", host, service, interface, protocol, IPPROTO_TCP, IPPROTO_UDP);
66
67     if(!*host) {
68         error("Definition '%s' does not specify a host.", definition);
69         return -1;
70     }
71
72     if(*interface) {
73         scope_id = if_nametoindex(interface);
74         if(!scope_id)
75             error("Cannot find a network interface named '%s'. Continuing with limiting the network interface", interface);
76     }
77
78     if(!*service)
79         service = default_service;
80
81     memset(&hints, 0, sizeof(hints));
82     hints.ai_family   = PF_UNSPEC;   /* Allow IPv4 or IPv6 */
83     hints.ai_socktype = socktype;
84     hints.ai_protocol = protocol;
85
86     int ai_err = getaddrinfo(host, service, &hints, &ai_head);
87     if (ai_err != 0) {
88         error("Cannot resolve host '%s', port '%s': %s", host, service, gai_strerror(ai_err));
89         return -1;
90     }
91
92     int fd = -1;
93     for (ai = ai_head; ai != NULL && fd == -1; ai = ai->ai_next) {
94
95         if (ai->ai_family == PF_INET6) {
96             struct sockaddr_in6 *pSadrIn6 = (struct sockaddr_in6 *) ai->ai_addr;
97             if(pSadrIn6->sin6_scope_id == 0) {
98                 pSadrIn6->sin6_scope_id = scope_id;
99             }
100         }
101
102         char hostBfr[NI_MAXHOST + 1];
103         char servBfr[NI_MAXSERV + 1];
104
105         getnameinfo(ai->ai_addr,
106                 ai->ai_addrlen,
107                 hostBfr,
108                 sizeof(hostBfr),
109                 servBfr,
110                 sizeof(servBfr),
111                 NI_NUMERICHOST | NI_NUMERICSERV);
112
113         debug(D_CONNECT_TO, "Address info: host = '%s', service = '%s', ai_flags = 0x%02X, ai_family = %d (PF_INET = %d, PF_INET6 = %d), ai_socktype = %d (SOCK_STREAM = %d, SOCK_DGRAM = %d), ai_protocol = %d (IPPROTO_TCP = %d, IPPROTO_UDP = %d), ai_addrlen = %lu (sockaddr_in = %lu, sockaddr_in6 = %lu)",
114                 hostBfr,
115                 servBfr,
116                 (unsigned int)ai->ai_flags,
117                 ai->ai_family,
118                 PF_INET,
119                 PF_INET6,
120                 ai->ai_socktype,
121                 SOCK_STREAM,
122                 SOCK_DGRAM,
123                 ai->ai_protocol,
124                 IPPROTO_TCP,
125                 IPPROTO_UDP,
126                 (unsigned long)ai->ai_addrlen,
127                 (unsigned long)sizeof(struct sockaddr_in),
128                 (unsigned long)sizeof(struct sockaddr_in6));
129
130         switch (ai->ai_addr->sa_family) {
131             case PF_INET: {
132                 struct sockaddr_in *pSadrIn = (struct sockaddr_in *)ai->ai_addr;
133                 debug(D_CONNECT_TO, "ai_addr = sin_family: %d (AF_INET = %d, AF_INET6 = %d), sin_addr: '%s', sin_port: '%s'",
134                         pSadrIn->sin_family,
135                         AF_INET,
136                         AF_INET6,
137                         hostBfr,
138                         servBfr);
139                 break;
140             }
141
142             case PF_INET6: {
143                 struct sockaddr_in6 *pSadrIn6 = (struct sockaddr_in6 *) ai->ai_addr;
144                 debug(D_CONNECT_TO,"ai_addr = sin6_family: %d (AF_INET = %d, AF_INET6 = %d), sin6_addr: '%s', sin6_port: '%s', sin6_flowinfo: %u, sin6_scope_id: %u",
145                         pSadrIn6->sin6_family,
146                         AF_INET,
147                         AF_INET6,
148                         hostBfr,
149                         servBfr,
150                         pSadrIn6->sin6_flowinfo,
151                         pSadrIn6->sin6_scope_id);
152                 break;
153             }
154
155             default: {
156                 debug(D_CONNECT_TO, "Unknown protocol family %d.", ai->ai_family);
157                 continue;
158             }
159         }
160
161         fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
162         if(fd != -1) {
163             if(timeout) {
164                 if(setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (char *) timeout, sizeof(struct timeval)) < 0)
165                     error("Failed to set timeout on the socket to ip '%s' port '%s'", hostBfr, servBfr);
166             }
167
168             if(connect(fd, ai->ai_addr, ai->ai_addrlen) < 0) {
169                 error("Failed to connect to '%s', port '%s'", hostBfr, servBfr);
170                 close(fd);
171                 fd = -1;
172             }
173
174             debug(D_CONNECT_TO, "Connected to '%s' on port '%s'.", hostBfr, servBfr);
175         }
176     }
177
178     freeaddrinfo(ai_head);
179
180     return fd;
181 }
182
183 int connect_to_one_of(const char *destination, int default_port, struct timeval *timeout, size_t *reconnects_counter, char *connected_to, size_t connected_to_size) {
184     int sock = -1;
185
186     const char *s = destination;
187     while(*s) {
188         const char *e = s;
189
190         // skip separators, moving both s(tart) and e(nd)
191         while(isspace(*e) || *e == ',') s = ++e;
192
193         // move e(nd) to the first separator
194         while(*e && !isspace(*e) && *e != ',') e++;
195
196         // is there anything?
197         if(!*s || s == e) break;
198
199         char buf[e - s + 1];
200         strncpyz(buf, s, e - s);
201         if(reconnects_counter) *reconnects_counter += 1;
202         sock = connect_to(buf, default_port, timeout);
203         if(sock != -1) {
204             if(connected_to && connected_to_size) {
205                 strncpy(connected_to, buf, connected_to_size);
206                 connected_to[connected_to_size - 1] = '\0';
207             }
208             break;
209         }
210         s = e;
211     }
212
213     return sock;
214 }
215
216 ssize_t recv_timeout(int sockfd, void *buf, size_t len, int flags, int timeout) {
217     for(;;) {
218         struct pollfd fd = {
219                 .fd = sockfd,
220                 .events = POLLIN,
221                 .revents = 0
222         };
223
224         errno = 0;
225         int retval = poll(&fd, 1, timeout * 1000);
226
227         if(retval == -1) {
228             // failed
229
230             if(errno == EINTR || errno == EAGAIN)
231                 continue;
232
233             return -1;
234         }
235
236         if(!retval) {
237             // timeout
238             return 0;
239         }
240
241         if(fd.events & POLLIN) break;
242     }
243
244     return recv(sockfd, buf, len, flags);
245 }
246
247 ssize_t send_timeout(int sockfd, void *buf, size_t len, int flags, int timeout) {
248     for(;;) {
249         struct pollfd fd = {
250                 .fd = sockfd,
251                 .events = POLLOUT,
252                 .revents = 0
253         };
254
255         errno = 0;
256         int retval = poll(&fd, 1, timeout * 1000);
257
258         if(retval == -1) {
259             // failed
260
261             if(errno == EINTR || errno == EAGAIN)
262                 continue;
263
264             return -1;
265         }
266
267         if(!retval) {
268             // timeout
269             return 0;
270         }
271
272         if(fd.events & POLLOUT) break;
273     }
274
275     return send(sockfd, buf, len, flags);
276 }