uhttpd:
[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 i = 0;
348         struct stat s;
349
350
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));
355
356         /* copy docroot */
357         memcpy(buffer, docroot, sizeof(buffer));
358
359         /* separate query string from url */
360         if( (pathptr = strchr(url, '?')) != NULL )
361         {
362                 p.query = pathptr[1] ? pathptr + 1 : NULL;
363
364                 /* urldecode component w/o query */
365                 if( pathptr > url )
366                         uh_urldecode(
367                                 &buffer[strlen(docroot)],
368                                 sizeof(buffer) - strlen(docroot) - 1,
369                                 url, (int)(pathptr - url) - 1
370                         );
371         }
372
373         /* no query string, decode all of url */
374         else
375         {
376                 uh_urldecode(
377                         &buffer[strlen(docroot)],
378                         sizeof(buffer) - strlen(docroot) - 1,
379                         url, strlen(url)
380                 );
381         }
382
383         /* create canon path */
384         for( i = strlen(buffer); i >= 0; i-- )
385         {
386                 if( (buffer[i] == 0) || (buffer[i] == '/') )
387                 {
388                         memset(path_info, 0, sizeof(path_info));
389                         memcpy(path_info, buffer, min(i + 1, sizeof(path_info) - 1));
390
391                         if( realpath(path_info, path_phys) )
392                         {
393                                 memset(path_info, 0, sizeof(path_info));
394                                 memcpy(path_info, &buffer[i],
395                                         min(strlen(buffer) - i, sizeof(path_info) - 1));
396
397                                 break;
398                         }
399                 }
400         }
401
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)] != '/'))
406         ) {
407                 return NULL;
408         }
409
410         /* test current path */
411         if( ! stat(path_phys, &p.stat) )
412         {
413                 /* is a regular file */
414                 if( p.stat.st_mode & S_IFREG )
415                 {
416                         p.root = docroot;
417                         p.phys = path_phys;
418                         p.name = &path_phys[strlen(docroot)];
419                         p.info = path_info[0] ? path_info : NULL;
420                 }
421
422                 /* is a directory */
423                 else if( (p.stat.st_mode & S_IFDIR) && !strlen(path_info) )
424                 {
425                         /* ensure trailing slash */
426                         if( path_phys[strlen(path_phys)-1] != '/' )
427                                 path_phys[strlen(path_phys)] = '/';
428
429                         /* try to locate index file */
430                         memset(buffer, 0, sizeof(buffer));
431                         memcpy(buffer, path_phys, sizeof(buffer));
432                         pathptr = &buffer[strlen(buffer)];
433
434                         for( i = 0; i < array_size(uh_index_files); i++ )
435                         {
436                                 strncat(buffer, uh_index_files[i], sizeof(buffer));
437
438                                 if( !stat(buffer, &s) && (s.st_mode & S_IFREG) )
439                                 {
440                                         memcpy(path_phys, buffer, sizeof(path_phys));
441                                         memcpy(&p.stat, &s, sizeof(p.stat));
442                                         break;
443                                 }
444
445                                 *pathptr = 0;
446                         }
447
448                         p.root = docroot;
449                         p.phys = path_phys;
450                         p.name = &path_phys[strlen(docroot)];
451                 }
452         }
453
454         return p.phys ? &p : NULL;
455 }
456
457
458 static char uh_listeners[UH_LIMIT_LISTENERS * sizeof(struct listener)] = { 0 };
459 static char uh_clients[UH_LIMIT_CLIENTS * sizeof(struct client)] = { 0 };
460
461 static int uh_listener_count = 0;
462 static int uh_client_count = 0;
463
464
465 struct listener * uh_listener_add(int sock, struct config *conf)
466 {
467         struct listener *new = NULL;
468         socklen_t sl;
469
470         if( uh_listener_count < UH_LIMIT_LISTENERS )
471         {
472                 new = (struct listener *)
473                         &uh_listeners[uh_listener_count * sizeof(struct listener)];
474
475                 new->socket = sock;
476                 new->conf   = conf;
477
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);
482
483                 uh_listener_count++;
484         }
485
486         return new;
487 }
488
489 struct listener * uh_listener_lookup(int sock)
490 {
491         struct listener *cur = NULL;
492         int i;
493
494         for( i = 0; i < uh_listener_count; i++ )
495         {
496                 cur = (struct listener *) &uh_listeners[i * sizeof(struct listener)];
497
498                 if( cur->socket == sock )
499                         return cur;
500         }
501
502         return NULL;
503 }
504
505
506 struct client * uh_client_add(int sock, struct listener *serv)
507 {
508         struct client *new = NULL;
509         socklen_t sl;
510
511         if( uh_client_count < UH_LIMIT_CLIENTS )
512         {
513                 new = (struct client *)
514                         &uh_clients[uh_client_count * sizeof(struct client)];
515
516                 new->socket = sock;
517                 new->server = serv;
518
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);
523
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);
528
529                 uh_client_count++;
530         }
531
532         return new;
533 }
534
535 struct client * uh_client_lookup(int sock)
536 {
537         struct client *cur = NULL;
538         int i;
539
540         for( i = 0; i < uh_client_count; i++ )
541         {
542                 cur = (struct client *) &uh_clients[i * sizeof(struct client)];
543
544                 if( cur->socket == sock )
545                         return cur;
546         }
547
548         return NULL;
549 }
550
551 void uh_client_remove(int sock)
552 {
553         struct client *del = uh_client_lookup(sock);
554
555         if( del )
556         {
557                 memmove(del, del + 1,
558                         sizeof(uh_clients) - (int)((char *)del - uh_clients) - sizeof(struct client));
559
560                 uh_client_count--;
561         }
562 }
563
564