Network streaming support
[lttng-tools.git] / src / common / uri.c
1 /*
2 * Copyright (C) 2012 - David Goulet <dgoulet@efficios.com>
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License, version 2 only, as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11 * more details.
12 *
13 * You should have received a copy of the GNU General Public License along with
14 * this program; if not, write to the Free Software Foundation, Inc., 51
15 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16 */
17
18 #define _GNU_SOURCE
19 #include <arpa/inet.h>
20 #include <netdb.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <sys/socket.h>
24
25 #include <common/common.h>
26 #include <common/defaults.h>
27
28 #include "uri.h"
29
30 enum uri_proto_code {
31 P_NET, P_NET6, P_FILE, P_TCP, P_TCP6,
32 };
33
34 struct uri_proto {
35 char *name;
36 enum uri_proto_code code;
37 enum lttng_proto_type type;
38 enum lttng_dst_type dtype;
39 };
40
41 /* Supported protocols */
42 static const struct uri_proto proto_uri[] = {
43 { .name = "file", .code = P_FILE, .type = 0, .dtype = LTTNG_DST_PATH},
44 { .name = "net", .code = P_NET, .type = LTTNG_TCP, .dtype = LTTNG_DST_IPV4 },
45 { .name = "net6", .code = P_NET6, .type = LTTNG_TCP, .dtype = LTTNG_DST_IPV6 },
46 { .name = "tcp", .code = P_TCP, .type = LTTNG_TCP, .dtype = LTTNG_DST_IPV4 },
47 { .name = "tcp6", .code = P_TCP6, .type = LTTNG_TCP, .dtype = LTTNG_DST_IPV6 },
48 { .name = NULL }
49 };
50
51 /*
52 * Validate if proto is a supported protocol from proto_uri array.
53 */
54 static const struct uri_proto *validate_protocol(char *proto)
55 {
56 const struct uri_proto *supported;
57
58 /* Safety net */
59 if (proto == NULL) {
60 goto end;
61 }
62
63 for (supported = &proto_uri[0];
64 supported->name != NULL; ++supported) {
65 if (strncmp(proto, supported->name, strlen(proto)) == 0) {
66 goto end;
67 }
68 }
69
70 /* Proto not found */
71 return NULL;
72
73 end:
74 return supported;
75 }
76
77 /*
78 * Set network address from string into dst. Supports both IP string and
79 * hostname.
80 */
81 static int set_ip_address(const char *addr, int af, char *dst, size_t size)
82 {
83 int ret;
84 unsigned char buf[sizeof(struct in6_addr)];
85 struct hostent *record;
86
87 /* Network protocol */
88 ret = inet_pton(af, addr, buf);
89 if (ret < 1) {
90 /* We consider the dst to be an hostname or an invalid IP char */
91 record = gethostbyname2(addr, af);
92 if (record == NULL) {
93 /* At this point, the IP or the hostname is bad */
94 printf("bad hostname\n");
95 goto error;
96 }
97
98 /* Translate IP to string */
99 (void) inet_ntop(af, record->h_addr_list[0], dst, size);
100 } else {
101 memcpy(dst, addr, size);
102 }
103
104 return 0;
105
106 error:
107 return -1;
108 }
109
110 /*
111 * Compare two URIs.
112 *
113 * Return 0 if equal else 1.
114 */
115 int uri_compare(struct lttng_uri *uri1, struct lttng_uri *uri2)
116 {
117 return memcmp(uri1, uri2, sizeof(struct lttng_uri));
118 }
119
120 /*
121 * Free URI memory.
122 */
123 void uri_free(struct lttng_uri *uri)
124 {
125 /* Safety check */
126 if (uri != NULL) {
127 free(uri);
128 }
129 }
130
131 /*
132 * Return an allocated URI.
133 */
134 struct lttng_uri *uri_create(void)
135 {
136 struct lttng_uri *uri;
137
138 uri = zmalloc(sizeof(struct lttng_uri));
139 if (uri == NULL) {
140 PERROR("zmalloc uri");
141 }
142
143 return uri;
144 }
145
146 /*
147 * Parses a string URI to a lttng_uri. This function can potentially return
148 * more than one URI in uris so the size of the array is returned and uris is
149 * allocated and populated. Caller must free(3) the array.
150 *
151 * This function can not detect the stream type of the URI so the caller has to
152 * make sure the correct type (stype) is set on the return URI(s). The default
153 * port must also be set by the caller if the returned URI has its port set to
154 * zero.
155 */
156 ssize_t uri_parse(const char *str_uri, struct lttng_uri **uris)
157 {
158 int ret;
159 size_t str_offset = 0;
160 /* Size of the uris array. Default is 1 */
161 ssize_t size = 1;
162 char net[6], dst[LTTNG_MAX_DNNAME + 1], subdir[PATH_MAX];
163 unsigned int ctrl_port = 0;
164 unsigned int data_port = 0;
165 struct lttng_uri *uri;
166 const struct uri_proto *proto;
167
168 /*
169 * The first part is the protocol portion of a maximum of 5 bytes for now.
170 * The second part is the hostname or IP address. The 255 bytes size is the
171 * limit found in the RFC 1035 for the total length of a domain name
172 * (https://www.ietf.org/rfc/rfc1035.txt). Finally, for the net://
173 * protocol, two ports CAN be specified.
174 */
175
176 ret = sscanf(str_uri, "%5[^:]://", net);
177 if (ret < 1) {
178 printf("bad protocol\n");
179 goto error;
180 }
181
182 DBG3("URI string: %s", str_uri);
183
184 proto = validate_protocol(net);
185 if (proto == NULL) {
186 printf("no protocol\n");
187 ret = -1;
188 goto error;
189 }
190
191 if (proto->code == P_NET || proto->code == P_NET6) {
192 /* Special case for net:// which requires two URI object */
193 size = 2;
194 }
195
196 memset(subdir, 0, sizeof(subdir));
197 str_offset += strlen(net);
198
199 /* Parse the rest of the URI */
200 if (sscanf(str_uri + str_offset, "://%255[^:]:%u:%u/%s", dst, &ctrl_port,
201 &data_port, subdir) == 4) {
202 /* All set */
203 } else if (sscanf(str_uri + str_offset, "://%255[^:]:%u:%u", dst,
204 &ctrl_port, &data_port) == 3) {
205 } else if (sscanf(str_uri + str_offset, "://%255[^:]:%u/%s", dst,
206 &ctrl_port, subdir) == 3) {
207 } else if (sscanf(str_uri + str_offset, "://%255[^:]:%u", dst,
208 &ctrl_port) == 2) {
209 } else if (sscanf(str_uri + str_offset, "://%255[^/]/%s", dst,
210 subdir) == 2) {
211 } else {
212 ret = sscanf(str_uri + str_offset, "://%255[^:]", dst);
213 if (ret < 0) {
214 ERR("Bad URI");
215 goto error;
216 }
217 }
218
219 /* We have enough valid information to create URI(s) object */
220
221 /* Allocate URI array */
222 uri = zmalloc(sizeof(struct lttng_uri) * size);
223 if (uri == NULL) {
224 PERROR("zmalloc uri");
225 goto error;
226 }
227
228 /* Copy generic information */
229 uri[0].dtype = proto->dtype;
230 uri[0].proto = proto->type;
231 uri[0].port = ctrl_port;
232 strncpy(uri[0].subdir, subdir, sizeof(uri[0].subdir));
233
234 DBG3("URI dtype: %d, proto: %d, host: %s, subdir: %s, ctrl: %d, data: %d",
235 proto->dtype, proto->type, dst, subdir, ctrl_port, data_port);
236
237 switch (proto->code) {
238 case P_FILE:
239 memcpy(uri[0].dst.path, dst, sizeof(uri[0].dst.path));
240 /* Reset port for the file:// URI */
241 uri[0].port = 0;
242 DBG3("URI file destination: %s", dst);
243 break;
244 case P_NET:
245 ret = set_ip_address(dst, AF_INET, uri[0].dst.ipv4,
246 sizeof(uri[0].dst.ipv4));
247 if (ret < 0) {
248 goto free_error;
249 }
250
251 memcpy(uri[1].dst.ipv4, uri[0].dst.ipv4, sizeof(uri[1].dst.ipv4));
252
253 uri[1].dtype = proto->dtype;
254 uri[1].proto = proto->type;
255 uri[1].port = data_port;
256 break;
257 case P_NET6:
258 ret = set_ip_address(dst, AF_INET6, uri[0].dst.ipv6,
259 sizeof(uri[0].dst.ipv6));
260 if (ret < 0) {
261 goto free_error;
262 }
263
264 memcpy(uri[1].dst.ipv6, uri[0].dst.ipv6, sizeof(uri[1].dst.ipv6));
265
266 uri[1].dtype = proto->dtype;
267 uri[1].proto = proto->type;
268 uri[1].port = data_port;
269 break;
270 case P_TCP:
271 ret = set_ip_address(dst, AF_INET, uri[0].dst.ipv4,
272 sizeof(uri[0].dst.ipv4));
273 if (ret < 0) {
274 goto free_error;
275 }
276 break;
277 case P_TCP6:
278 ret = set_ip_address(dst, AF_INET6, uri[0].dst.ipv6,
279 sizeof(uri[0].dst.ipv6));
280 if (ret < 0) {
281 goto free_error;
282 }
283 break;
284 default:
285 goto free_error;
286 }
287
288 *uris = uri;
289
290 return size;
291
292 free_error:
293 free(uri);
294 error:
295 return -1;
296 }
This page took 0.034227 seconds and 4 git commands to generate.