uhttpd: only enable Lua runtime if a handler was specified
[project/luci.git] / contrib / package / uhttpd / src / uhttpd-utils.c
1 #include "uhttpd.h"
2 #include "uhttpd-utils.h"
3
4 #ifdef HAVE_TLS
5 #include "uhttpd-tls.h"
6 #endif
7
8
9 static char *uh_index_files[] = {
10         "index.html",
11         "index.htm",
12         "default.html",
13         "default.htm"
14 };
15
16
17 const char * sa_straddr(void *sa)
18 {
19         static char str[INET6_ADDRSTRLEN];
20         struct sockaddr_in *v4 = (struct sockaddr_in *)sa;
21         struct sockaddr_in6 *v6 = (struct sockaddr_in6 *)sa;
22
23         if( v4->sin_family == AF_INET )
24                 return inet_ntop(AF_INET, &(v4->sin_addr), str, sizeof(str));
25         else
26                 return inet_ntop(AF_INET6, &(v6->sin6_addr), str, sizeof(str));
27 }
28
29 const char * sa_strport(void *sa)
30 {
31         static char str[6];
32         snprintf(str, sizeof(str), "%i", sa_port(sa));
33         return str;
34 }
35
36 int sa_port(void *sa)
37 {
38         return ntohs(((struct sockaddr_in6 *)sa)->sin6_port);
39 }
40
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)
43 {
44         int match = 0;
45         int i, j;
46
47         for( i = 0; i < hslen; i++ )
48         {
49                 if( haystack[i] == needle[0] )
50                 {
51                         match = ((ndlen == 1) || ((i + ndlen) <= hslen));
52
53                         for( j = 1; (j < ndlen) && ((i + j) < hslen); j++ )
54                         {
55                                 if( haystack[i+j] != needle[j] )
56                                 {
57                                         match = 0;
58                                         break;
59                                 }
60                         }
61
62                         if( match )
63                                 return &haystack[i];
64                 }
65         }
66
67         return NULL;
68 }
69
70
71 int uh_tcp_send(struct client *cl, const char *buf, int len)
72 {
73         fd_set writer;
74         struct timeval timeout;
75
76         FD_ZERO(&writer);
77         FD_SET(cl->socket, &writer);
78
79         timeout.tv_sec = 0;
80         timeout.tv_usec = 500000;
81
82         if( select(cl->socket + 1, NULL, &writer, NULL, &timeout) > 0 )
83         {
84 #ifdef HAVE_TLS
85                 if( cl->tls )
86                         return SSL_write(cl->tls, buf, len);
87                 else
88 #endif
89                         return send(cl->socket, buf, len, 0);
90         }
91
92         return -1;
93 }
94
95 int uh_tcp_peek(struct client *cl, char *buf, int len)
96 {
97         int sz = uh_tcp_recv(cl, buf, len);
98
99         /* store received data in peek buffer */
100         if( sz > 0 )
101         {
102                 cl->peeklen = sz;
103                 memcpy(cl->peekbuf, buf, sz);
104         }
105
106         return sz;
107 }
108
109 int uh_tcp_recv(struct client *cl, char *buf, int len)
110 {
111         int sz = 0;
112         int rsz = 0;
113
114         /* first serve data from peek buffer */
115         if( cl->peeklen > 0 )
116         {
117                 sz = min(cl->peeklen, len);
118                 len -= sz; cl->peeklen -= sz;
119
120                 memcpy(buf, cl->peekbuf, sz);
121                 memmove(cl->peekbuf, &cl->peekbuf[sz], cl->peeklen);
122         }
123
124         /* caller wants more */
125         if( len > 0 )
126         {
127 #ifdef HAVE_TLS
128                 if( cl->tls )
129                         rsz = SSL_read(cl->tls, (void *)&buf[sz], len);
130                 else
131 #endif
132                         rsz = recv(cl->socket, (void *)&buf[sz], len, 0);
133
134                 if( (sz == 0) || (rsz > 0) )
135                         sz += rsz;
136         }
137
138         return sz;
139 }
140
141 #define ensure(x) \
142         do { if( x < 0 ) return -1; } while(0)
143
144 int uh_http_sendhf(struct client *cl, int code, const char *summary, const char *fmt, ...)
145 {
146         va_list ap;
147
148         char buffer[UH_LIMIT_MSGHEAD];
149         int len;
150
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",
155                         code, summary
156         );
157
158         ensure(uh_tcp_send(cl, buffer, len));
159
160         va_start(ap, fmt);
161         len = vsnprintf(buffer, sizeof(buffer), fmt, ap);
162         va_end(ap);
163
164         ensure(uh_http_sendc(cl, buffer, len));
165         ensure(uh_http_sendc(cl, NULL, 0));
166
167         return 0;
168 }
169
170
171 int uh_http_sendc(struct client *cl, const char *data, int len)
172 {
173         char chunk[8];
174         int clen;
175
176         if( len == -1 )
177                 len = strlen(data);
178
179         if( len > 0 )
180         {
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));
185         }
186         else
187         {
188                 ensure(uh_tcp_send(cl, "0\r\n\r\n", 5));
189         }
190
191         return 0;
192 }
193
194 int uh_http_sendf(
195         struct client *cl, struct http_request *req, const char *fmt, ...
196 ) {
197         va_list ap;
198         char buffer[UH_LIMIT_MSGHEAD];
199         int len;
200
201         va_start(ap, fmt);
202         len = vsnprintf(buffer, sizeof(buffer), fmt, ap);
203         va_end(ap);
204
205         if( (req != NULL) && (req->version > 1.0) )
206                 ensure(uh_http_sendc(cl, buffer, len));
207         else if( len > 0 )
208                 ensure(uh_tcp_send(cl, buffer, len));
209
210         return 0;
211 }
212
213 int uh_http_send(
214         struct client *cl, struct http_request *req, const char *buf, int len
215 ) {
216         if( len < 0 )
217                 len = strlen(buf);
218
219         if( (req != NULL) && (req->version > 1.0) )
220                 ensure(uh_http_sendc(cl, buf, len));
221         else if( len > 0 )
222                 ensure(uh_tcp_send(cl, buf, len));
223
224         return 0;
225 }
226
227
228 int uh_urldecode(char *buf, int blen, const char *src, int slen)
229 {
230         int i;
231         int len = 0;
232
233 #define hex(x) \
234         (((x) <= '9') ? ((x) - '0') : \
235                 (((x) <= 'F') ? ((x) - 'A' + 10) : \
236                         ((x) - 'a' + 10)))
237
238         for( i = 0; (i <= slen) && (i <= blen); i++ )
239         {
240                 if( src[i] == '%' )
241                 {
242                         if( ((i+2) <= slen) && isxdigit(src[i+1]) && isxdigit(src[i+2]) )
243                         {
244                                 buf[len++] = (char)(16 * hex(src[i+1]) + hex(src[i+2]));
245                                 i += 2;
246                         }
247                         else
248                         {
249                                 buf[len++] = '%';
250                         }
251                 }
252                 else
253                 {
254                         buf[len++] = src[i];
255                 }
256         }
257
258         return len;
259 }
260
261 int uh_urlencode(char *buf, int blen, const char *src, int slen)
262 {
263         int i;
264         int len = 0;
265         const char hex[] = "0123456789abcdef";
266
267         for( i = 0; (i <= slen) && (i <= blen); i++ )
268         {
269                 if( isalnum(src[i]) || (src[i] == '-') || (src[i] == '_') ||
270                     (src[i] == '.') || (src[i] == '~') )
271                 {
272                         buf[len++] = src[i];
273                 }
274                 else if( (len+3) <= blen )
275                 {
276                         buf[len++] = '%';
277                         buf[len++] = hex[(src[i] >> 4) & 15];
278                         buf[len++] = hex[(src[i] & 15) & 15];
279                 }
280                 else
281                 {
282                         break;
283                 }
284         }
285
286         return len;
287 }
288
289 int uh_path_normalize(char *buf, int blen, const char *src, int slen)
290 {
291         int i, skip;
292         int len = 0;
293
294         for( i = 0, skip = 1; (i <= slen) && (src[i] != 0); i++ )
295         {
296                 /* collapse multiple "/" into one */
297                 if( src[i] == '/' )
298                 {
299                         /* collapse "/../" to "/" */
300                         if( ((i+2) <= slen) && (src[i+1] == '.') && (src[i+2] == '.') &&
301                                 (((i+3) > slen) || (src[i+3] == '/'))
302                         ) {
303                                 i += 2;
304                                 continue;
305                         }
306
307                         /* collapse "/./" to "/" */
308                         else if( ((i+1) <= slen) && (src[i+1] == '.') &&
309                             (((i+2) > slen) || (src[i+2] == '/'))
310                         ) {
311                                 i += 1;
312                                 continue;
313                         }
314
315                         /* skip repeating "/" */
316                         else if( skip )
317                         {
318                                 continue;
319                         }
320
321                         skip++;
322                 }
323
324                 /* finally a harmless char */
325                 else
326                 {
327                         skip = 0;
328                 }
329
330                 buf[len++] = src[i];
331         }
332
333         return len;
334 }
335
336
337 struct uh_path_info * uh_path_lookup(struct client *cl, const char *url)
338 {
339         static char path_phys[PATH_MAX];
340         static char path_info[PATH_MAX];
341         static struct uh_path_info p;
342
343         char buffer[UH_LIMIT_MSGHEAD];
344         char *docroot = cl->server->conf->docroot;
345         char *pathptr = NULL;
346
347         int skip = 0;
348         int plen = 0;
349
350         struct stat s;
351
352
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));
357
358         /* first separate query string from url */
359         if( (pathptr = strchr(url, '?')) != NULL )
360         {
361                 p.query = pathptr[1] ? pathptr + 1 : NULL;
362
363                 /* urldecode component w/o query */
364                 if( pathptr > url )
365                         plen = uh_urldecode(
366                                 buffer, sizeof(buffer), url,
367                                 (int)(pathptr - url) - 1
368                         );
369                 else
370                         plen = 0;
371         }
372
373         /* no query string, decode all of url */
374         else
375         {
376                 plen = uh_urldecode(
377                         buffer, sizeof(buffer), url, strlen(url)
378                 );
379         }
380
381         /* copy docroot */
382         memcpy(path_phys, docroot, sizeof(path_phys));
383
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,
389                 buffer, plen
390         );
391
392         /* copy result to info buffer */
393         memcpy(path_info, path_phys, sizeof(path_info));
394
395         /* find path */
396         while( 1 )
397         {
398                 /* test current path */
399                 if( !stat(path_phys, &p.stat) )
400                 {
401                         /* is a regular file */
402                         if( p.stat.st_mode & S_IFREG )
403                         {
404                                 p.root = docroot;
405                                 p.phys = path_phys;
406                                 p.name = &path_phys[strlen(docroot)-1];
407
408                                 /* find workdir */
409                                 if( (pathptr = strrchr(path_phys, '/')) != NULL )
410                                 {
411                                         path_info[(int)(pathptr - path_phys) + 1] = 0;
412                                         p.wdir = path_info;
413                                 }
414                                 else
415                                 {
416                                         p.wdir = docroot;
417                                 }
418
419                                 /* find path info */
420                                 if( path_info[strlen(path_phys)] != 0 )
421                                 {
422                                         p.info = &path_info[strlen(path_phys)];
423                                 }
424
425                                 break;
426                         }
427
428                         /* is a directory */
429                         else if( (p.stat.st_mode & S_IFDIR) && (skip < 1) )
430                         {
431                                 /* ensure trailing slash */
432                                 if( path_phys[plen-1] != '/' )
433                                         path_phys[plen] = '/';
434
435                                 /* try to locate index file */
436                                 memset(buffer, 0, sizeof(buffer));
437                                 memcpy(buffer, path_phys, sizeof(buffer));
438                                 pathptr = &buffer[strlen(buffer)];
439
440                                 for( skip = 0; skip < array_size(uh_index_files); skip++ )
441                                 {
442                                         strncat(buffer, uh_index_files[skip], sizeof(buffer));
443
444                                         if( !stat(buffer, &s) && (s.st_mode & S_IFREG) )
445                                         {
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));
450                                                 p.wdir = path_info;
451                                                 break;
452                                         }
453
454                                         *pathptr = 0;
455                                 }
456
457                                 p.root = docroot;
458                                 p.phys = path_phys;
459                                 p.name = &path_phys[strlen(docroot)-1];
460
461                                 break;
462                         }
463
464                         /* not found */
465                         else if( skip )
466                         {
467                                 break;
468                         }
469                 }
470
471                 else if( (strlen(path_phys) > strlen(docroot)) &&
472                          ((pathptr = strrchr(path_phys, '/')) != NULL)
473                 ) {
474                         *pathptr = 0;
475                         skip = 1;
476                 }
477
478                 else
479                 {
480                         break;
481                 }
482         }
483
484         return p.phys ? &p : NULL;
485 }
486
487
488 static char uh_listeners[UH_LIMIT_LISTENERS * sizeof(struct listener)] = { 0 };
489 static char uh_clients[UH_LIMIT_CLIENTS * sizeof(struct client)] = { 0 };
490
491 static int uh_listener_count = 0;
492 static int uh_client_count = 0;
493
494
495 struct listener * uh_listener_add(int sock, struct config *conf)
496 {
497         struct listener *new = NULL;
498         socklen_t sl;
499
500         if( uh_listener_count < UH_LIMIT_LISTENERS )
501         {
502                 new = (struct listener *)
503                         &uh_listeners[uh_listener_count * sizeof(struct listener)];
504
505                 new->socket = sock;
506                 new->conf   = conf;
507
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);
512
513                 uh_listener_count++;
514         }
515
516         return new;
517 }
518
519 struct listener * uh_listener_lookup(int sock)
520 {
521         struct listener *cur = NULL;
522         int i;
523
524         for( i = 0; i < uh_listener_count; i++ )
525         {
526                 cur = (struct listener *) &uh_listeners[i * sizeof(struct listener)];
527
528                 if( cur->socket == sock )
529                         return cur;
530         }
531
532         return NULL;
533 }
534
535
536 struct client * uh_client_add(int sock, struct listener *serv)
537 {
538         struct client *new = NULL;
539         socklen_t sl;
540
541         if( uh_client_count < UH_LIMIT_CLIENTS )
542         {
543                 new = (struct client *)
544                         &uh_clients[uh_client_count * sizeof(struct client)];
545
546                 new->socket = sock;
547                 new->server = serv;
548
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);
553
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);
558
559                 uh_client_count++;
560         }
561
562         return new;
563 }
564
565 struct client * uh_client_lookup(int sock)
566 {
567         struct client *cur = NULL;
568         int i;
569
570         for( i = 0; i < uh_client_count; i++ )
571         {
572                 cur = (struct client *) &uh_clients[i * sizeof(struct client)];
573
574                 if( cur->socket == sock )
575                         return cur;
576         }
577
578         return NULL;
579 }
580
581 void uh_client_remove(int sock)
582 {
583         struct client *del = uh_client_lookup(sock);
584
585         if( del )
586         {
587                 memmove(del, del + 1,
588                         sizeof(uh_clients) - (int)((char *)del - uh_clients) - sizeof(struct client));
589
590                 uh_client_count--;
591         }
592 }
593
594