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;
353 memset(path_phys, 0, sizeof(path_phys));
354 memset(path_info, 0, sizeof(path_info));
355 memset(buffer, 0, sizeof(buffer));
356 memset(&p, 0, sizeof(p));
358 /* first separate query string from url */
359 if( (pathptr = strchr(url, '?')) != NULL )
361 p.query = pathptr[1] ? pathptr + 1 : NULL;
363 /* urldecode component w/o query */
366 buffer, sizeof(buffer), url,
367 (int)(pathptr - url) - 1
373 /* no query string, decode all of url */
377 buffer, sizeof(buffer), url, strlen(url)
382 memcpy(path_phys, docroot, sizeof(path_phys));
384 /* append normalized path, leave two bytes free
385 * for trailing slash and terminating zero byte */
386 plen = strlen(docroot) + uh_path_normalize(
387 &path_phys[strlen(docroot)],
388 sizeof(path_phys) - strlen(docroot) - 2,
392 /* copy result to info buffer */
393 memcpy(path_info, path_phys, sizeof(path_info));
398 /* test current path */
399 if( !stat(path_phys, &p.stat) )
401 /* is a regular file */
402 if( p.stat.st_mode & S_IFREG )
406 p.name = &path_phys[strlen(docroot)-1];
409 if( (pathptr = strrchr(path_phys, '/')) != NULL )
411 path_info[(int)(pathptr - path_phys) + 1] = 0;
420 if( path_info[strlen(path_phys)] != 0 )
422 p.info = &path_info[strlen(path_phys)];
429 else if( (p.stat.st_mode & S_IFDIR) && (skip < 1) )
431 /* ensure trailing slash */
432 if( path_phys[plen-1] != '/' )
433 path_phys[plen] = '/';
435 /* try to locate index file */
436 memset(buffer, 0, sizeof(buffer));
437 memcpy(buffer, path_phys, sizeof(buffer));
438 pathptr = &buffer[strlen(buffer)];
440 for( skip = 0; skip < array_size(uh_index_files); skip++ )
442 strncat(buffer, uh_index_files[skip], sizeof(buffer));
444 if( !stat(buffer, &s) && (s.st_mode & S_IFREG) )
446 memset(path_info, 0, sizeof(path_info));
447 memcpy(path_info, path_phys, strlen(path_phys));
448 memcpy(path_phys, buffer, sizeof(path_phys));
449 memcpy(&p.stat, &s, sizeof(p.stat));
459 p.name = &path_phys[strlen(docroot)-1];
471 else if( (strlen(path_phys) > strlen(docroot)) &&
472 ((pathptr = strrchr(path_phys, '/')) != NULL)
484 return p.phys ? &p : NULL;
488 static char uh_listeners[UH_LIMIT_LISTENERS * sizeof(struct listener)] = { 0 };
489 static char uh_clients[UH_LIMIT_CLIENTS * sizeof(struct client)] = { 0 };
491 static int uh_listener_count = 0;
492 static int uh_client_count = 0;
495 struct listener * uh_listener_add(int sock, struct config *conf)
497 struct listener *new = NULL;
500 if( uh_listener_count < UH_LIMIT_LISTENERS )
502 new = (struct listener *)
503 &uh_listeners[uh_listener_count * sizeof(struct listener)];
508 /* get local endpoint addr */
509 sl = sizeof(struct sockaddr_in6);
510 memset(&(new->addr), 0, sl);
511 getsockname(sock, (struct sockaddr *) &(new->addr), &sl);
519 struct listener * uh_listener_lookup(int sock)
521 struct listener *cur = NULL;
524 for( i = 0; i < uh_listener_count; i++ )
526 cur = (struct listener *) &uh_listeners[i * sizeof(struct listener)];
528 if( cur->socket == sock )
536 struct client * uh_client_add(int sock, struct listener *serv)
538 struct client *new = NULL;
541 if( uh_client_count < UH_LIMIT_CLIENTS )
543 new = (struct client *)
544 &uh_clients[uh_client_count * sizeof(struct client)];
549 /* get remote endpoint addr */
550 sl = sizeof(struct sockaddr_in6);
551 memset(&(new->peeraddr), 0, sl);
552 getpeername(sock, (struct sockaddr *) &(new->peeraddr), &sl);
554 /* get local endpoint addr */
555 sl = sizeof(struct sockaddr_in6);
556 memset(&(new->servaddr), 0, sl);
557 getsockname(sock, (struct sockaddr *) &(new->servaddr), &sl);
565 struct client * uh_client_lookup(int sock)
567 struct client *cur = NULL;
570 for( i = 0; i < uh_client_count; i++ )
572 cur = (struct client *) &uh_clients[i * sizeof(struct client)];
574 if( cur->socket == sock )
581 void uh_client_remove(int sock)
583 struct client *del = uh_client_lookup(sock);
587 memmove(del, del + 1,
588 sizeof(uh_clients) - (int)((char *)del - uh_clients) - sizeof(struct client));