2 #include "uhttpd-utils.h"
5 #include "uhttpd-tls.h"
9 static char *uh_index_files[] = {
17 const char * sa_straddr(void *sa)
19 static char str[INET6_ADDRSTRLEN];
20 struct sockaddr_in *v4 = (struct sockaddr_in *)sa;
21 struct sockaddr_in6 *v6 = (struct sockaddr_in6 *)sa;
23 if( v4->sin_family == AF_INET )
24 return inet_ntop(AF_INET, &(v4->sin_addr), str, sizeof(str));
26 return inet_ntop(AF_INET6, &(v6->sin6_addr), str, sizeof(str));
29 const char * sa_strport(void *sa)
32 snprintf(str, sizeof(str), "%i", sa_port(sa));
38 return ntohs(((struct sockaddr_in6 *)sa)->sin6_port);
41 /* Simple strstr() like function that takes len arguments for both haystack and needle. */
42 char *strfind(char *haystack, int hslen, const char *needle, int ndlen)
47 for( i = 0; i < hslen; i++ )
49 if( haystack[i] == needle[0] )
51 match = ((ndlen == 1) || ((i + ndlen) <= hslen));
53 for( j = 1; (j < ndlen) && ((i + j) < hslen); j++ )
55 if( haystack[i+j] != needle[j] )
71 int uh_tcp_send(struct client *cl, const char *buf, int len)
74 struct timeval timeout;
77 FD_SET(cl->socket, &writer);
80 timeout.tv_usec = 500000;
82 if( select(cl->socket + 1, NULL, &writer, NULL, &timeout) > 0 )
86 return SSL_write(cl->tls, buf, len);
89 return send(cl->socket, buf, len, 0);
95 int uh_tcp_peek(struct client *cl, char *buf, int len)
97 int sz = uh_tcp_recv(cl, buf, len);
99 /* store received data in peek buffer */
103 memcpy(cl->peekbuf, buf, sz);
109 int uh_tcp_recv(struct client *cl, char *buf, int len)
114 /* first serve data from peek buffer */
115 if( cl->peeklen > 0 )
117 sz = min(cl->peeklen, len);
118 len -= sz; cl->peeklen -= sz;
120 memcpy(buf, cl->peekbuf, sz);
121 memmove(cl->peekbuf, &cl->peekbuf[sz], cl->peeklen);
124 /* caller wants more */
129 rsz = SSL_read(cl->tls, (void *)&buf[sz], len);
132 rsz = recv(cl->socket, (void *)&buf[sz], len, 0);
134 if( (sz == 0) || (rsz > 0) )
142 do { if( x < 0 ) return -1; } while(0)
144 int uh_http_sendhf(struct client *cl, int code, const char *summary, const char *fmt, ...)
148 char buffer[UH_LIMIT_MSGHEAD];
151 len = snprintf(buffer, sizeof(buffer),
152 "HTTP/1.1 %03i %s\r\n"
153 "Content-Type: text/plain\r\n"
154 "Transfer-Encoding: chunked\r\n\r\n",
158 ensure(uh_tcp_send(cl, buffer, len));
161 len = vsnprintf(buffer, sizeof(buffer), fmt, ap);
164 ensure(uh_http_sendc(cl, buffer, len));
165 ensure(uh_http_sendc(cl, NULL, 0));
171 int uh_http_sendc(struct client *cl, const char *data, int len)
181 clen = snprintf(chunk, sizeof(chunk), "%X\r\n", len);
182 ensure(uh_tcp_send(cl, chunk, clen));
183 ensure(uh_tcp_send(cl, data, len));
184 ensure(uh_tcp_send(cl, "\r\n", 2));
188 ensure(uh_tcp_send(cl, "0\r\n\r\n", 5));
195 struct client *cl, struct http_request *req, const char *fmt, ...
198 char buffer[UH_LIMIT_MSGHEAD];
202 len = vsnprintf(buffer, sizeof(buffer), fmt, ap);
205 if( (req != NULL) && (req->version > 1.0) )
206 ensure(uh_http_sendc(cl, buffer, len));
208 ensure(uh_tcp_send(cl, buffer, len));
214 struct client *cl, struct http_request *req, const char *buf, int len
219 if( (req != NULL) && (req->version > 1.0) )
220 ensure(uh_http_sendc(cl, buf, len));
222 ensure(uh_tcp_send(cl, buf, len));
228 int uh_urldecode(char *buf, int blen, const char *src, int slen)
234 (((x) <= '9') ? ((x) - '0') : \
235 (((x) <= 'F') ? ((x) - 'A' + 10) : \
238 for( i = 0; (i <= slen) && (i <= blen); i++ )
242 if( ((i+2) <= slen) && isxdigit(src[i+1]) && isxdigit(src[i+2]) )
244 buf[len++] = (char)(16 * hex(src[i+1]) + hex(src[i+2]));
261 int uh_urlencode(char *buf, int blen, const char *src, int slen)
265 const char hex[] = "0123456789abcdef";
267 for( i = 0; (i <= slen) && (i <= blen); i++ )
269 if( isalnum(src[i]) || (src[i] == '-') || (src[i] == '_') ||
270 (src[i] == '.') || (src[i] == '~') )
274 else if( (len+3) <= blen )
277 buf[len++] = hex[(src[i] >> 4) & 15];
278 buf[len++] = hex[(src[i] & 15) & 15];
289 int uh_path_normalize(char *buf, int blen, const char *src, int slen)
294 for( i = 0, skip = 1; (i <= slen) && (src[i] != 0); i++ )
296 /* collapse multiple "/" into one */
299 /* collapse "/../" to "/" */
300 if( ((i+2) <= slen) && (src[i+1] == '.') && (src[i+2] == '.') &&
301 (((i+3) > slen) || (src[i+3] == '/'))
307 /* collapse "/./" to "/" */
308 else if( ((i+1) <= slen) && (src[i+1] == '.') &&
309 (((i+2) > slen) || (src[i+2] == '/'))
315 /* skip repeating "/" */
324 /* finally a harmless char */
337 struct uh_path_info * uh_path_lookup(struct client *cl, const char *url)
339 static char path_phys[PATH_MAX];
340 static char path_info[PATH_MAX];
341 static struct uh_path_info p;
343 char buffer[UH_LIMIT_MSGHEAD];
344 char *docroot = cl->server->conf->docroot;
345 char *pathptr = NULL;
351 memset(path_phys, 0, sizeof(path_phys));
352 memset(path_info, 0, sizeof(path_info));
353 memset(buffer, 0, sizeof(buffer));
354 memset(&p, 0, sizeof(p));
357 memcpy(buffer, docroot, sizeof(buffer));
359 /* separate query string from url */
360 if( (pathptr = strchr(url, '?')) != NULL )
362 p.query = pathptr[1] ? pathptr + 1 : NULL;
364 /* urldecode component w/o query */
367 &buffer[strlen(docroot)],
368 sizeof(buffer) - strlen(docroot) - 1,
369 url, (int)(pathptr - url) - 1
373 /* no query string, decode all of url */
377 &buffer[strlen(docroot)],
378 sizeof(buffer) - strlen(docroot) - 1,
383 /* create canon path */
384 for( i = strlen(buffer); i >= 0; i-- )
386 if( (buffer[i] == 0) || (buffer[i] == '/') )
388 memset(path_info, 0, sizeof(path_info));
389 memcpy(path_info, buffer, min(i + 1, sizeof(path_info) - 1));
391 if( realpath(path_info, path_phys) )
393 memset(path_info, 0, sizeof(path_info));
394 memcpy(path_info, &buffer[i],
395 min(strlen(buffer) - i, sizeof(path_info) - 1));
402 /* check whether found path is within docroot */
403 if( strncmp(path_phys, docroot, strlen(docroot)) ||
404 ((path_phys[strlen(docroot)] != 0) &&
405 (path_phys[strlen(docroot)] != '/'))
410 /* test current path */
411 if( ! stat(path_phys, &p.stat) )
413 /* is a regular file */
414 if( p.stat.st_mode & S_IFREG )
418 p.name = &path_phys[strlen(docroot)];
419 p.info = path_info[0] ? path_info : NULL;
423 else if( (p.stat.st_mode & S_IFDIR) && !strlen(path_info) )
425 /* ensure trailing slash */
426 if( path_phys[strlen(path_phys)-1] != '/' )
427 path_phys[strlen(path_phys)] = '/';
429 /* try to locate index file */
430 memset(buffer, 0, sizeof(buffer));
431 memcpy(buffer, path_phys, sizeof(buffer));
432 pathptr = &buffer[strlen(buffer)];
434 for( i = 0; i < array_size(uh_index_files); i++ )
436 strncat(buffer, uh_index_files[i], sizeof(buffer));
438 if( !stat(buffer, &s) && (s.st_mode & S_IFREG) )
440 memcpy(path_phys, buffer, sizeof(path_phys));
441 memcpy(&p.stat, &s, sizeof(p.stat));
450 p.name = &path_phys[strlen(docroot)];
454 return p.phys ? &p : NULL;
458 static char uh_listeners[UH_LIMIT_LISTENERS * sizeof(struct listener)] = { 0 };
459 static char uh_clients[UH_LIMIT_CLIENTS * sizeof(struct client)] = { 0 };
461 static int uh_listener_count = 0;
462 static int uh_client_count = 0;
465 struct listener * uh_listener_add(int sock, struct config *conf)
467 struct listener *new = NULL;
470 if( uh_listener_count < UH_LIMIT_LISTENERS )
472 new = (struct listener *)
473 &uh_listeners[uh_listener_count * sizeof(struct listener)];
478 /* get local endpoint addr */
479 sl = sizeof(struct sockaddr_in6);
480 memset(&(new->addr), 0, sl);
481 getsockname(sock, (struct sockaddr *) &(new->addr), &sl);
489 struct listener * uh_listener_lookup(int sock)
491 struct listener *cur = NULL;
494 for( i = 0; i < uh_listener_count; i++ )
496 cur = (struct listener *) &uh_listeners[i * sizeof(struct listener)];
498 if( cur->socket == sock )
506 struct client * uh_client_add(int sock, struct listener *serv)
508 struct client *new = NULL;
511 if( uh_client_count < UH_LIMIT_CLIENTS )
513 new = (struct client *)
514 &uh_clients[uh_client_count * sizeof(struct client)];
519 /* get remote endpoint addr */
520 sl = sizeof(struct sockaddr_in6);
521 memset(&(new->peeraddr), 0, sl);
522 getpeername(sock, (struct sockaddr *) &(new->peeraddr), &sl);
524 /* get local endpoint addr */
525 sl = sizeof(struct sockaddr_in6);
526 memset(&(new->servaddr), 0, sl);
527 getsockname(sock, (struct sockaddr *) &(new->servaddr), &sl);
535 struct client * uh_client_lookup(int sock)
537 struct client *cur = NULL;
540 for( i = 0; i < uh_client_count; i++ )
542 cur = (struct client *) &uh_clients[i * sizeof(struct client)];
544 if( cur->socket == sock )
551 void uh_client_remove(int sock)
553 struct client *del = uh_client_lookup(sock);
557 memmove(del, del + 1,
558 sizeof(uh_clients) - (int)((char *)del - uh_clients) - sizeof(struct client));