Avoid recursion when processing residual inbuf data.
[shim.git] / util.c
blob5a97b3de30fe03efaa2bece0afd8992ac0650cdf
1 #include "netheaders.h"
3 #include <assert.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <errno.h>
7 #include "util.h"
8 #include "log.h"
10 void *
11 mem_calloc(size_t nmemb, size_t size)
13 void *ret;
15 ret = calloc(nmemb, size);
16 if (!ret)
17 log_fatal("mem_calloc: alloc failed");
19 return ret;
22 void *
23 mem_malloc(size_t size)
25 void *ret;
27 ret = malloc(size);
28 if (!ret)
29 log_fatal("mem_malloc: alloc failed");
31 return ret;
34 char *
35 mem_strdup(const char *str)
37 char *ret;
39 ret = strdup(str);
40 if (!ret)
41 log_fatal("mem_strdup: alloc failed");
43 return ret;
46 char *
47 mem_strdup_n(const char *str, size_t n)
49 char *ret;
51 ret = mem_calloc(1, n + 1);
52 if (!ret)
53 log_fatal("mem_strdup: alloc failed");
54 memcpy(ret, str, n);
56 return ret;
59 void
60 mem_free(void *buf)
62 if (buf)
63 free(buf);
66 static void
67 add_token(const char *buf, size_t len, struct token_list *tokens)
69 struct token *token;
71 token = mem_calloc(1, sizeof(*token));
72 token->token = mem_strdup_n(buf, len);
73 TAILQ_INSERT_TAIL(tokens, token, next);
76 /* this isn't very fast, but this thing is meant for a single user anyhow */
77 size_t
78 tokenize(const char *buf, const char *sep, int lim,
79 struct token_list *tokens)
81 char *p;
82 size_t ntok;
83 size_t len;
85 ntok = 0;
87 while ((lim < 0 || ntok < (size_t)lim) && (p = strpbrk(buf, sep))) {
88 len = p - buf;
89 add_token(buf, len, tokens);
90 buf += len + 1;
91 ntok++;
94 /* add any remaining */
95 if (*buf) {
96 add_token(buf, strlen(buf), tokens);
97 ntok++;
100 return ntok;
103 void
104 token_list_clear(struct token_list *tokens)
106 struct token *token;
108 while ((token = TAILQ_FIRST(tokens))) {
109 TAILQ_REMOVE(tokens, token, next);
110 if (token->token)
111 mem_free(token->token);
112 mem_free(token);
116 ev_int64_t
117 get_int(const char *buf, int base)
119 char *endp;
120 ev_int64_t rv;
122 rv = evutil_strtoll(buf, &endp, base);
123 // XXX
126 return rv;
129 static int
130 get_port(const char *str)
132 long port;
133 char *endp;
135 errno = 0;
136 port = strtol(str, &endp, 10);
137 if (errno == ERANGE || !endp || *endp != '\0' ||
138 port < 1 || port > 0xffff)
139 return -1;
141 return port;
144 // tokenize a CONNECT host:port request
145 struct url *
146 url_connect_tokenize(const char *str)
148 struct url *url;
149 char *p;
150 int port;
152 url = mem_calloc(1, sizeof(*url));
154 p = strrchr(str, ':');
155 if (!p || p == str)
156 goto fail;
157 port = get_port(p + 1);
158 if (port < 0)
159 goto fail;
161 url->host = mem_strdup_n(str, p - str);
162 url->port = port;
164 return url;
166 fail:
167 url_free(url);
169 return NULL;
172 // XXX should this do more to sanitize the url?
173 struct url *
174 url_tokenize(const char *str)
176 struct url *url;
177 size_t ntokens;
178 char *p;
179 struct token_list tokens;
180 struct token *scheme, *slash, *hostport, *path;
181 size_t len;
182 int port = -1;
183 char *pathstr;
185 url = NULL;
186 TAILQ_INIT(&tokens);
188 ntokens = tokenize(str, "/", 3, &tokens);
190 /* just a path? */
191 if (ntokens >= 1 && TAILQ_FIRST(&tokens)->token[0] == '\0') {
192 url = mem_calloc(1, sizeof(*url));
193 url->path = mem_strdup(str);
194 goto out;
197 if (ntokens < 3)
198 goto out;
200 scheme = TAILQ_FIRST(&tokens);
201 len = strlen(scheme->token);
202 if (len < 2 || scheme->token[len - 1] != ':')
203 goto out;
204 scheme->token[len - 1] = '\0';
206 slash = TAILQ_NEXT(scheme, next);
207 if (slash->token[0] != '\0')
208 goto out;
210 hostport = TAILQ_NEXT(slash, next);
211 if (hostport->token[0] == '\0')
212 goto out;
213 // XXX this could break IPv6 addresses
214 p = strrchr(hostport->token, ':');
215 if (p == hostport->token)
216 goto out;
217 if (p && p[1] != '\0') {
218 *p++ = '\0';
219 port = get_port(p);
220 if (port < 0)
221 goto out;
224 if (ntokens > 3) {
225 // XXX maybe urlencode?
226 assert(ntokens == 4);
227 path = TAILQ_NEXT(hostport, next);
228 len = strlen(path->token);
229 pathstr = mem_calloc(1, 2 + len);
230 pathstr[0] = '/';
231 memcpy(pathstr + 1, path->token, len);
232 } else
233 pathstr = mem_strdup("/");
236 url = mem_calloc(1, sizeof(*url));
237 url->scheme = scheme->token;
238 url->host = hostport->token;
239 url->port = port;
240 url->path = pathstr;
242 scheme->token = NULL;
243 hostport->token = NULL;
245 out:
246 token_list_clear(&tokens);
248 return url;
251 void
252 url_free(struct url *url)
254 if (!url)
255 return;
257 mem_free(url->scheme);
258 mem_free(url->host);
259 mem_free(url->path);
260 mem_free(url);
263 const char *
264 format_addr(const struct sockaddr *addr)
266 const char *r = NULL;
267 static char buf[256];
268 char tmp[256];
270 if (addr->sa_family == AF_INET) {
271 struct sockaddr_in *sin = (struct sockaddr_in *)addr;
272 r = evutil_inet_ntop(AF_INET, &sin->sin_addr, tmp,
273 sizeof(tmp));
274 if (r) {
275 if (sin->sin_port)
276 evutil_snprintf(buf, sizeof(buf), "%s:%hu",
277 tmp, ntohs(sin->sin_port));
278 else
279 strcpy(buf, tmp);
281 } else if (addr->sa_family == AF_INET6) {
282 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)addr;
283 r = evutil_inet_ntop(AF_INET6, &sin6->sin6_addr, tmp,
284 sizeof(tmp));
285 if (r) {
286 if (sin6->sin6_port)
287 evutil_snprintf(buf, sizeof(buf), "[%s]:%hu",
288 tmp, ntohs(sin6->sin6_port));
289 else
290 strcpy(buf, tmp);
294 if (!r)
295 strcpy(buf, "???");
297 return buf;
300 const char *
301 socket_error_string(evutil_socket_t s)
303 return evutil_socket_error_to_string(evutil_socket_geterror(s));
306 #ifdef TEST_UTIL
307 #include <stdio.h>
308 #include <stdlib.h>
310 int main(int argc, char **argv)
312 struct url *url;
314 url = url_tokenize(argv[1]);
315 if (!url) {
316 printf("bad url!\n");
317 return 0;
319 printf("%s, %s, %d, %s\n",
320 url->scheme, url->host, url->port, url->path);
321 url_free(url);
323 return 0;
325 #endif