packages: sort network related packages into package/network/
[openwrt.git] / package / network / services / uhttpd / src / uhttpd-utils.c
1 /*
2  * uhttpd - Tiny single-threaded httpd - Utility functions
3  *
4  *   Copyright (C) 2010-2012 Jo-Philipp Wich <xm@subsignal.org>
5  *
6  *  Licensed under the Apache License, Version 2.0 (the "License");
7  *  you may not use this file except in compliance with the License.
8  *  You may obtain a copy of the License at
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
12  *  Unless required by applicable law or agreed to in writing, software
13  *  distributed under the License is distributed on an "AS IS" BASIS,
14  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  *  See the License for the specific language governing permissions and
16  *  limitations under the License.
17  */
18
19 #define _XOPEN_SOURCE 500       /* crypt() */
20 #define _BSD_SOURCE                     /* strcasecmp(), strncasecmp() */
21
22 #include "uhttpd.h"
23 #include "uhttpd-utils.h"
24
25 #ifdef HAVE_TLS
26 #include "uhttpd-tls.h"
27 #endif
28
29
30 static char *uh_index_files[] = {
31         "index.html",
32         "index.htm",
33         "default.html",
34         "default.htm"
35 };
36
37
38 const char * sa_straddr(void *sa)
39 {
40         static char str[INET6_ADDRSTRLEN];
41         struct sockaddr_in *v4 = (struct sockaddr_in *)sa;
42         struct sockaddr_in6 *v6 = (struct sockaddr_in6 *)sa;
43
44         if (v4->sin_family == AF_INET)
45                 return inet_ntop(AF_INET, &(v4->sin_addr), str, sizeof(str));
46         else
47                 return inet_ntop(AF_INET6, &(v6->sin6_addr), str, sizeof(str));
48 }
49
50 const char * sa_strport(void *sa)
51 {
52         static char str[6];
53         snprintf(str, sizeof(str), "%i", sa_port(sa));
54         return str;
55 }
56
57 int sa_port(void *sa)
58 {
59         return ntohs(((struct sockaddr_in6 *)sa)->sin6_port);
60 }
61
62 int sa_rfc1918(void *sa)
63 {
64         struct sockaddr_in *v4 = (struct sockaddr_in *)sa;
65         unsigned long a = htonl(v4->sin_addr.s_addr);
66
67         if (v4->sin_family == AF_INET)
68         {
69                 return ((a >= 0x0A000000) && (a <= 0x0AFFFFFF)) ||
70                        ((a >= 0xAC100000) && (a <= 0xAC1FFFFF)) ||
71                        ((a >= 0xC0A80000) && (a <= 0xC0A8FFFF));
72         }
73
74         return 0;
75 }
76
77 /* Simple strstr() like function that takes len arguments for both haystack and needle. */
78 char *strfind(char *haystack, int hslen, const char *needle, int ndlen)
79 {
80         int match = 0;
81         int i, j;
82
83         for (i = 0; i < hslen; i++)
84         {
85                 if (haystack[i] == needle[0])
86                 {
87                         match = ((ndlen == 1) || ((i + ndlen) <= hslen));
88
89                         for (j = 1; (j < ndlen) && ((i + j) < hslen); j++)
90                         {
91                                 if (haystack[i+j] != needle[j])
92                                 {
93                                         match = 0;
94                                         break;
95                                 }
96                         }
97
98                         if (match)
99                                 return &haystack[i];
100                 }
101         }
102
103         return NULL;
104 }
105
106 bool uh_socket_wait(int fd, int sec, bool write)
107 {
108         int rv;
109         struct timeval timeout;
110
111         fd_set fds;
112
113         FD_ZERO(&fds);
114         FD_SET(fd, &fds);
115
116         timeout.tv_sec = sec;
117         timeout.tv_usec = 0;
118
119         while (((rv = select(fd+1, write ? NULL : &fds, write ? &fds : NULL,
120                                                  NULL, &timeout)) < 0) && (errno == EINTR))
121         {
122                 D("IO: FD(%d) select interrupted: %s\n",
123                                 fd, strerror(errno));
124
125                 continue;
126         }
127
128         if (rv <= 0)
129         {
130                 D("IO: FD(%d) appears dead (rv=%d)\n", fd, rv);
131                 return false;
132         }
133
134         return true;
135 }
136
137 static int __uh_raw_send(struct client *cl, const char *buf, int len, int sec,
138                                                  int (*wfn) (struct client *, const char *, int))
139 {
140         ssize_t rv;
141         int fd = cl->fd.fd;
142
143         while (true)
144         {
145                 if ((rv = wfn(cl, buf, len)) < 0)
146                 {
147                         if (errno == EINTR)
148                         {
149                                 D("IO: FD(%d) interrupted\n", cl->fd.fd);
150                                 continue;
151                         }
152                         else if ((sec > 0) && (errno == EAGAIN || errno == EWOULDBLOCK))
153                         {
154                                 if (!uh_socket_wait(fd, sec, true))
155                                         return -1;
156                         }
157                         else
158                         {
159                                 D("IO: FD(%d) write error: %s\n", fd, strerror(errno));
160                                 return -1;
161                         }
162                 }
163                 /*
164                  * It is not entirely clear whether rv = 0 on nonblocking sockets
165                  * is an error. In real world fuzzing tests, not handling it as close
166                  * led to tight infinite loops in this send procedure, so treat it as
167                  * closed and break out.
168                  */
169                 else if (rv == 0)
170                 {
171                         D("IO: FD(%d) appears closed\n", fd);
172                         return 0;
173                 }
174                 else if (rv < len)
175                 {
176                         D("IO: FD(%d) short write %d/%d bytes\n", fd, rv, len);
177                         len -= rv;
178                         buf += rv;
179                         continue;
180                 }
181                 else
182                 {
183                         D("IO: FD(%d) sent %d/%d bytes\n", fd, rv, len);
184                         return rv;
185                 }
186         }
187 }
188
189 int uh_tcp_send_lowlevel(struct client *cl, const char *buf, int len)
190 {
191         return write(cl->fd.fd, buf, len);
192 }
193
194 int uh_raw_send(int fd, const char *buf, int len, int sec)
195 {
196         struct client_light cl = { .fd = { .fd = fd } };
197         return __uh_raw_send((struct client *)&cl, buf, len, sec,
198                                                  uh_tcp_send_lowlevel);
199 }
200
201 int uh_tcp_send(struct client *cl, const char *buf, int len)
202 {
203         int seconds = cl->server->conf->network_timeout;
204 #ifdef HAVE_TLS
205         if (cl->tls)
206                 return __uh_raw_send(cl, buf, len, seconds,
207                                                          cl->server->conf->tls_send);
208 #endif
209         return __uh_raw_send(cl, buf, len, seconds, uh_tcp_send_lowlevel);
210 }
211
212 static int __uh_raw_recv(struct client *cl, char *buf, int len, int sec,
213                                                  int (*rfn) (struct client *, char *, int))
214 {
215         ssize_t rv;
216         int fd = cl->fd.fd;
217
218         while (true)
219         {
220                 if ((rv = rfn(cl, buf, len)) < 0)
221                 {
222                         if (errno == EINTR)
223                         {
224                                 continue;
225                         }
226                         else if ((sec > 0) && (errno == EAGAIN || errno == EWOULDBLOCK))
227                         {
228                                 if (!uh_socket_wait(fd, sec, false))
229                                         return -1;
230                         }
231                         else
232                         {
233                                 D("IO: FD(%d) read error: %s\n", fd, strerror(errno));
234                                 return -1;
235                         }
236                 }
237                 else if (rv == 0)
238                 {
239                         D("IO: FD(%d) appears closed\n", fd);
240                         return 0;
241                 }
242                 else
243                 {
244                         D("IO: FD(%d) read %d bytes\n", fd, rv);
245                         return rv;
246                 }
247         }
248 }
249
250 int uh_tcp_recv_lowlevel(struct client *cl, char *buf, int len)
251 {
252         return read(cl->fd.fd, buf, len);
253 }
254
255 int uh_raw_recv(int fd, char *buf, int len, int sec)
256 {
257         struct client_light cl = { .fd = { .fd = fd } };
258         return __uh_raw_recv((struct client *)&cl, buf, len, sec,
259                                                  uh_tcp_recv_lowlevel);
260 }
261
262 int uh_tcp_recv(struct client *cl, char *buf, int len)
263 {
264         int seconds = cl->server->conf->network_timeout;
265 #ifdef HAVE_TLS
266         if (cl->tls)
267                 return __uh_raw_recv(cl, buf, len, seconds,
268                                                          cl->server->conf->tls_recv);
269 #endif
270         return __uh_raw_recv(cl, buf, len, seconds, uh_tcp_recv_lowlevel);
271 }
272
273
274 int uh_http_sendhf(struct client *cl, int code, const char *summary,
275                                    const char *fmt, ...)
276 {
277         va_list ap;
278
279         char buffer[UH_LIMIT_MSGHEAD];
280         int len;
281
282         len = snprintf(buffer, sizeof(buffer),
283                 "HTTP/1.1 %03i %s\r\n"
284                 "Connection: close\r\n"
285                 "Content-Type: text/plain\r\n"
286                 "Transfer-Encoding: chunked\r\n\r\n",
287                         code, summary
288         );
289
290         ensure_ret(uh_tcp_send(cl, buffer, len));
291
292         va_start(ap, fmt);
293         len = vsnprintf(buffer, sizeof(buffer), fmt, ap);
294         va_end(ap);
295
296         ensure_ret(uh_http_sendc(cl, buffer, len));
297         ensure_ret(uh_http_sendc(cl, NULL, 0));
298
299         return 0;
300 }
301
302
303 int uh_http_sendc(struct client *cl, const char *data, int len)
304 {
305         char chunk[8];
306         int clen;
307
308         if (len == -1)
309                 len = strlen(data);
310
311         if (len > 0)
312         {
313                 clen = snprintf(chunk, sizeof(chunk), "%X\r\n", len);
314                 ensure_ret(uh_tcp_send(cl, chunk, clen));
315                 ensure_ret(uh_tcp_send(cl, data, len));
316                 ensure_ret(uh_tcp_send(cl, "\r\n", 2));
317         }
318         else
319         {
320                 ensure_ret(uh_tcp_send(cl, "0\r\n\r\n", 5));
321         }
322
323         return 0;
324 }
325
326 int uh_http_sendf(struct client *cl, struct http_request *req,
327                                   const char *fmt, ...)
328 {
329         va_list ap;
330         char buffer[UH_LIMIT_MSGHEAD];
331         int len;
332
333         va_start(ap, fmt);
334         len = vsnprintf(buffer, sizeof(buffer), fmt, ap);
335         va_end(ap);
336
337         if ((req != NULL) && (req->version > UH_HTTP_VER_1_0))
338                 ensure_ret(uh_http_sendc(cl, buffer, len));
339         else if (len > 0)
340                 ensure_ret(uh_tcp_send(cl, buffer, len));
341
342         return 0;
343 }
344
345 int uh_http_send(struct client *cl, struct http_request *req,
346                                  const char *buf, int len)
347 {
348         if (len < 0)
349                 len = strlen(buf);
350
351         if ((req != NULL) && (req->version > UH_HTTP_VER_1_0))
352                 ensure_ret(uh_http_sendc(cl, buf, len));
353         else if (len > 0)
354                 ensure_ret(uh_tcp_send(cl, buf, len));
355
356         return 0;
357 }
358
359
360 /* blen is the size of buf; slen is the length of src.  The input-string need
361 ** not be, and the output string will not be, null-terminated.  Returns the
362 ** length of the decoded string, -1 on buffer overflow, -2 on malformed string. */
363 int uh_urldecode(char *buf, int blen, const char *src, int slen)
364 {
365         int i;
366         int len = 0;
367
368 #define hex(x) \
369         (((x) <= '9') ? ((x) - '0') : \
370                 (((x) <= 'F') ? ((x) - 'A' + 10) : \
371                         ((x) - 'a' + 10)))
372
373         for (i = 0; (i < slen) && (len < blen); i++)
374         {
375                 if (src[i] == '%')
376                 {
377                         if (((i+2) < slen) && isxdigit(src[i+1]) && isxdigit(src[i+2]))
378                         {
379                                 buf[len++] = (char)(16 * hex(src[i+1]) + hex(src[i+2]));
380                                 i += 2;
381                         }
382                         else
383                         {
384                                 /* Encoding error: it's hard to think of a
385                                 ** scenario in which returning an incorrect
386                                 ** 'decoding' of the malformed string is
387                                 ** preferable to signaling an error condition. */
388                                 #if 0 /* WORSE_IS_BETTER */
389                                     buf[len++] = '%';
390                                 #else
391                                     return -2;
392                                 #endif
393                         }
394                 }
395                 else
396                 {
397                         buf[len++] = src[i];
398                 }
399         }
400
401         return (i == slen) ? len : -1;
402 }
403
404 /* blen is the size of buf; slen is the length of src.  The input-string need
405 ** not be, and the output string will not be, null-terminated.  Returns the
406 ** length of the encoded string, or -1 on error (buffer overflow) */
407 int uh_urlencode(char *buf, int blen, const char *src, int slen)
408 {
409         int i;
410         int len = 0;
411         const char hex[] = "0123456789abcdef";
412
413         for (i = 0; (i < slen) && (len < blen); i++)
414         {
415                 if( isalnum(src[i]) || (src[i] == '-') || (src[i] == '_') ||
416                     (src[i] == '.') || (src[i] == '~') )
417                 {
418                         buf[len++] = src[i];
419                 }
420                 else if ((len+3) <= blen)
421                 {
422                         buf[len++] = '%';
423                         buf[len++] = hex[(src[i] >> 4) & 15];
424                         buf[len++] = hex[ src[i]       & 15];
425                 }
426                 else
427                 {
428                         len = -1;
429                         break;
430                 }
431         }
432
433         return (i == slen) ? len : -1;
434 }
435
436 int uh_b64decode(char *buf, int blen, const unsigned char *src, int slen)
437 {
438         int i = 0;
439         int len = 0;
440
441         unsigned int cin  = 0;
442         unsigned int cout = 0;
443
444
445         for (i = 0; (i <= slen) && (src[i] != 0); i++)
446         {
447                 cin = src[i];
448
449                 if ((cin >= '0') && (cin <= '9'))
450                         cin = cin - '0' + 52;
451                 else if ((cin >= 'A') && (cin <= 'Z'))
452                         cin = cin - 'A';
453                 else if ((cin >= 'a') && (cin <= 'z'))
454                         cin = cin - 'a' + 26;
455                 else if (cin == '+')
456                         cin = 62;
457                 else if (cin == '/')
458                         cin = 63;
459                 else if (cin == '=')
460                         cin = 0;
461                 else
462                         continue;
463
464                 cout = (cout << 6) | cin;
465
466                 if ((i % 4) == 3)
467                 {
468                         if ((len + 3) < blen)
469                         {
470                                 buf[len++] = (char)(cout >> 16);
471                                 buf[len++] = (char)(cout >> 8);
472                                 buf[len++] = (char)(cout);
473                         }
474                         else
475                         {
476                                 break;
477                         }
478                 }
479         }
480
481         buf[len++] = 0;
482         return len;
483 }
484
485 static char * canonpath(const char *path, char *path_resolved)
486 {
487         char path_copy[PATH_MAX];
488         char *path_cpy = path_copy;
489         char *path_res = path_resolved;
490
491         struct stat s;
492
493
494         /* relative -> absolute */
495         if (*path != '/')
496         {
497                 getcwd(path_copy, PATH_MAX);
498                 strncat(path_copy, "/", PATH_MAX - strlen(path_copy));
499                 strncat(path_copy, path, PATH_MAX - strlen(path_copy));
500         }
501         else
502         {
503                 strncpy(path_copy, path, PATH_MAX);
504         }
505
506         /* normalize */
507         while ((*path_cpy != '\0') && (path_cpy < (path_copy + PATH_MAX - 2)))
508         {
509                 if (*path_cpy == '/')
510                 {
511                         /* skip repeating / */
512                         if (path_cpy[1] == '/')
513                         {
514                                 path_cpy++;
515                                 continue;
516                         }
517
518                         /* /./ or /../ */
519                         else if (path_cpy[1] == '.')
520                         {
521                                 /* skip /./ */
522                                 if ((path_cpy[2] == '/') || (path_cpy[2] == '\0'))
523                                 {
524                                         path_cpy += 2;
525                                         continue;
526                                 }
527
528                                 /* collapse /x/../ */
529                                 else if ((path_cpy[2] == '.') &&
530                                                  ((path_cpy[3] == '/') || (path_cpy[3] == '\0')))
531                                 {
532                                         while ((path_res > path_resolved) && (*--path_res != '/'))
533                                                 ;
534
535                                         path_cpy += 3;
536                                         continue;
537                                 }
538                         }
539                 }
540
541                 *path_res++ = *path_cpy++;
542         }
543
544         /* remove trailing slash if not root / */
545         if ((path_res > (path_resolved+1)) && (path_res[-1] == '/'))
546                 path_res--;
547         else if (path_res == path_resolved)
548                 *path_res++ = '/';
549
550         *path_res = '\0';
551
552         /* test access */
553         if (!stat(path_resolved, &s) && (s.st_mode & S_IROTH))
554                 return path_resolved;
555
556         return NULL;
557 }
558
559 /* Returns NULL on error.
560 ** NB: improperly encoded URL should give client 400 [Bad Syntax]; returning
561 ** NULL here causes 404 [Not Found], but that's not too unreasonable. */
562 struct path_info * uh_path_lookup(struct client *cl, const char *url)
563 {
564         static char path_phys[PATH_MAX];
565         static char path_info[PATH_MAX];
566         static struct path_info p;
567
568         char buffer[UH_LIMIT_MSGHEAD];
569         char *docroot = cl->server->conf->docroot;
570         char *pathptr = NULL;
571
572         int slash = 0;
573         int no_sym = cl->server->conf->no_symlinks;
574         int i = 0;
575         struct stat s;
576
577         /* back out early if url is undefined */
578         if (url == NULL)
579                 return NULL;
580
581         memset(path_phys, 0, sizeof(path_phys));
582         memset(path_info, 0, sizeof(path_info));
583         memset(buffer, 0, sizeof(buffer));
584         memset(&p, 0, sizeof(p));
585
586         /* copy docroot */
587         memcpy(buffer, docroot,
588                    min(strlen(docroot), sizeof(buffer) - 1));
589
590         /* separate query string from url */
591         if ((pathptr = strchr(url, '?')) != NULL)
592         {
593                 p.query = pathptr[1] ? pathptr + 1 : NULL;
594
595                 /* urldecode component w/o query */
596                 if (pathptr > url)
597                 {
598                         if (uh_urldecode(&buffer[strlen(docroot)],
599                                                          sizeof(buffer) - strlen(docroot) - 1,
600                                                          url, pathptr - url ) < 0)
601                         {
602                                 return NULL; /* bad URL */
603                         }
604                 }
605         }
606
607         /* no query string, decode all of url */
608         else
609         {
610                 if (uh_urldecode(&buffer[strlen(docroot)],
611                                                  sizeof(buffer) - strlen(docroot) - 1,
612                                                  url, strlen(url) ) < 0)
613                 {
614                         return NULL; /* bad URL */
615                 }
616         }
617
618         /* create canon path */
619         for (i = strlen(buffer), slash = (buffer[max(0, i-1)] == '/'); i >= 0; i--)
620         {
621                 if ((buffer[i] == 0) || (buffer[i] == '/'))
622                 {
623                         memset(path_info, 0, sizeof(path_info));
624                         memcpy(path_info, buffer, min(i + 1, sizeof(path_info) - 1));
625
626                         if (no_sym ? realpath(path_info, path_phys)
627                                    : canonpath(path_info, path_phys))
628                         {
629                                 memset(path_info, 0, sizeof(path_info));
630                                 memcpy(path_info, &buffer[i],
631                                            min(strlen(buffer) - i, sizeof(path_info) - 1));
632
633                                 break;
634                         }
635                 }
636         }
637
638         /* check whether found path is within docroot */
639         if (strncmp(path_phys, docroot, strlen(docroot)) ||
640                 ((path_phys[strlen(docroot)] != 0) &&
641                  (path_phys[strlen(docroot)] != '/')))
642         {
643                 return NULL;
644         }
645
646         /* test current path */
647         if (!stat(path_phys, &p.stat))
648         {
649                 /* is a regular file */
650                 if (p.stat.st_mode & S_IFREG)
651                 {
652                         p.root = docroot;
653                         p.phys = path_phys;
654                         p.name = &path_phys[strlen(docroot)];
655                         p.info = path_info[0] ? path_info : NULL;
656                 }
657
658                 /* is a directory */
659                 else if ((p.stat.st_mode & S_IFDIR) && !strlen(path_info))
660                 {
661                         /* ensure trailing slash */
662                         if (path_phys[strlen(path_phys)-1] != '/')
663                                 path_phys[strlen(path_phys)] = '/';
664
665                         /* try to locate index file */
666                         memset(buffer, 0, sizeof(buffer));
667                         memcpy(buffer, path_phys, sizeof(buffer));
668                         pathptr = &buffer[strlen(buffer)];
669
670                         /* if requested url resolves to a directory and a trailing slash
671                            is missing in the request url, redirect the client to the same
672                            url with trailing slash appended */
673                         if (!slash)
674                         {
675                                 uh_http_sendf(cl, NULL,
676                                         "HTTP/1.1 302 Found\r\n"
677                                         "Location: %s%s%s\r\n"
678                                         "Connection: close\r\n\r\n",
679                                                 &path_phys[strlen(docroot)],
680                                                 p.query ? "?" : "",
681                                                 p.query ? p.query : ""
682                                 );
683
684                                 p.redirected = 1;
685                         }
686                         else if (cl->server->conf->index_file)
687                         {
688                                 strncat(buffer, cl->server->conf->index_file, sizeof(buffer));
689
690                                 if (!stat(buffer, &s) && (s.st_mode & S_IFREG))
691                                 {
692                                         memcpy(path_phys, buffer, sizeof(path_phys));
693                                         memcpy(&p.stat, &s, sizeof(p.stat));
694                                 }
695                         }
696                         else
697                         {
698                                 for (i = 0; i < array_size(uh_index_files); i++)
699                                 {
700                                         strncat(buffer, uh_index_files[i], sizeof(buffer));
701
702                                         if (!stat(buffer, &s) && (s.st_mode & S_IFREG))
703                                         {
704                                                 memcpy(path_phys, buffer, sizeof(path_phys));
705                                                 memcpy(&p.stat, &s, sizeof(p.stat));
706                                                 break;
707                                         }
708
709                                         *pathptr = 0;
710                                 }
711                         }
712
713                         p.root = docroot;
714                         p.phys = path_phys;
715                         p.name = &path_phys[strlen(docroot)];
716                 }
717         }
718
719         return p.phys ? &p : NULL;
720 }
721
722
723 static struct auth_realm *uh_realms = NULL;
724
725 struct auth_realm * uh_auth_add(char *path, char *user, char *pass)
726 {
727         struct auth_realm *new = NULL;
728         struct passwd *pwd;
729
730 #ifdef HAVE_SHADOW
731         struct spwd *spwd;
732 #endif
733
734         if((new = (struct auth_realm *)malloc(sizeof(struct auth_realm))) != NULL)
735         {
736                 memset(new, 0, sizeof(struct auth_realm));
737
738                 memcpy(new->path, path,
739                            min(strlen(path), sizeof(new->path) - 1));
740
741                 memcpy(new->user, user,
742                            min(strlen(user), sizeof(new->user) - 1));
743
744                 /* given password refers to a passwd entry */
745                 if ((strlen(pass) > 3) && !strncmp(pass, "$p$", 3))
746                 {
747 #ifdef HAVE_SHADOW
748                         /* try to resolve shadow entry */
749                         if (((spwd = getspnam(&pass[3])) != NULL) && spwd->sp_pwdp)
750                         {
751                                 memcpy(new->pass, spwd->sp_pwdp,
752                                            min(strlen(spwd->sp_pwdp), sizeof(new->pass) - 1));
753                         }
754
755                         else
756 #endif
757
758                         /* try to resolve passwd entry */
759                         if (((pwd = getpwnam(&pass[3])) != NULL) && pwd->pw_passwd &&
760                                 (pwd->pw_passwd[0] != '!') && (pwd->pw_passwd[0] != 0))
761                         {
762                                 memcpy(new->pass, pwd->pw_passwd,
763                                            min(strlen(pwd->pw_passwd), sizeof(new->pass) - 1));
764                         }
765                 }
766
767                 /* ordinary pwd */
768                 else
769                 {
770                         memcpy(new->pass, pass,
771                                 min(strlen(pass), sizeof(new->pass) - 1));
772                 }
773
774                 if (new->pass[0])
775                 {
776                         new->next = uh_realms;
777                         uh_realms = new;
778
779                         return new;
780                 }
781
782                 free(new);
783         }
784
785         return NULL;
786 }
787
788 int uh_auth_check(struct client *cl, struct http_request *req,
789                                   struct path_info *pi)
790 {
791         int i, plen, rlen, protected;
792         char buffer[UH_LIMIT_MSGHEAD];
793         char *user = NULL;
794         char *pass = NULL;
795
796         struct auth_realm *realm = NULL;
797
798         plen = strlen(pi->name);
799         protected = 0;
800
801         /* check whether at least one realm covers the requested url */
802         for (realm = uh_realms; realm; realm = realm->next)
803         {
804                 rlen = strlen(realm->path);
805
806                 if ((plen >= rlen) && !strncasecmp(pi->name, realm->path, rlen))
807                 {
808                         req->realm = realm;
809                         protected = 1;
810                         break;
811                 }
812         }
813
814         /* requested resource is covered by a realm */
815         if (protected)
816         {
817                 /* try to get client auth info */
818                 foreach_header(i, req->headers)
819                 {
820                         if (!strcasecmp(req->headers[i], "Authorization") &&
821                                 (strlen(req->headers[i+1]) > 6) &&
822                                 !strncasecmp(req->headers[i+1], "Basic ", 6))
823                         {
824                                 memset(buffer, 0, sizeof(buffer));
825                                 uh_b64decode(buffer, sizeof(buffer) - 1,
826                                         (unsigned char *) &req->headers[i+1][6],
827                                         strlen(req->headers[i+1]) - 6);
828
829                                 if ((pass = strchr(buffer, ':')) != NULL)
830                                 {
831                                         user = buffer;
832                                         *pass++ = 0;
833                                 }
834
835                                 break;
836                         }
837                 }
838
839                 /* have client auth */
840                 if (user && pass)
841                 {
842                         /* find matching realm */
843                         for (realm = uh_realms; realm; realm = realm->next)
844                         {
845                                 rlen = strlen(realm->path);
846
847                                 if ((plen >= rlen) &&
848                                         !strncasecmp(pi->name, realm->path, rlen) &&
849                                         !strcmp(user, realm->user))
850                                 {
851                                         req->realm = realm;
852                                         break;
853                                 }
854                         }
855
856                         /* found a realm matching the username */
857                         if (realm)
858                         {
859                                 /* check user pass */
860                                 if (!strcmp(pass, realm->pass) ||
861                                     !strcmp(crypt(pass, realm->pass), realm->pass))
862                                         return 1;
863                         }
864                 }
865
866                 /* 401 */
867                 uh_http_sendf(cl, NULL,
868                               "%s 401 Authorization Required\r\n"
869                               "WWW-Authenticate: Basic realm=\"%s\"\r\n"
870                               "Content-Type: text/plain\r\n"
871                               "Content-Length: 23\r\n\r\n"
872                               "Authorization Required\n",
873                               http_versions[req->version],
874                               cl->server->conf->realm);
875
876                 return 0;
877         }
878
879         return 1;
880 }
881
882
883 static struct listener *uh_listeners = NULL;
884 static struct client *uh_clients = NULL;
885
886 struct listener * uh_listener_add(int sock, struct config *conf)
887 {
888         struct listener *new = NULL;
889         socklen_t sl;
890
891         if ((new = (struct listener *)malloc(sizeof(struct listener))) != NULL)
892         {
893                 memset(new, 0, sizeof(struct listener));
894
895                 new->fd.fd = sock;
896                 new->conf  = conf;
897
898
899                 /* get local endpoint addr */
900                 sl = sizeof(struct sockaddr_in6);
901                 memset(&(new->addr), 0, sl);
902                 getsockname(sock, (struct sockaddr *) &(new->addr), &sl);
903
904                 new->next = uh_listeners;
905                 uh_listeners = new;
906
907                 return new;
908         }
909
910         return NULL;
911 }
912
913 struct listener * uh_listener_lookup(int sock)
914 {
915         struct listener *cur = NULL;
916
917         for (cur = uh_listeners; cur; cur = cur->next)
918                 if (cur->fd.fd == sock)
919                         return cur;
920
921         return NULL;
922 }
923
924
925 struct client * uh_client_add(int sock, struct listener *serv,
926                               struct sockaddr_in6 *peer)
927 {
928         struct client *new = NULL;
929         socklen_t sl;
930
931         if ((new = (struct client *)malloc(sizeof(struct client))) != NULL)
932         {
933                 memset(new, 0, sizeof(struct client));
934                 memcpy(&new->peeraddr, peer, sizeof(new->peeraddr));
935
936                 new->fd.fd  = sock;
937                 new->server = serv;
938
939                 new->rpipe.fd = -1;
940                 new->wpipe.fd = -1;
941
942                 /* get local endpoint addr */
943                 sl = sizeof(struct sockaddr_in6);
944                 getsockname(sock, (struct sockaddr *) &(new->servaddr), &sl);
945
946                 new->next = uh_clients;
947                 uh_clients = new;
948
949                 serv->n_clients++;
950
951                 D("IO: Client(%d) allocated\n", new->fd.fd);
952         }
953
954         return new;
955 }
956
957 struct client * uh_client_lookup(int sock)
958 {
959         struct client *cur = NULL;
960
961         for (cur = uh_clients; cur; cur = cur->next)
962                 if (cur->fd.fd == sock)
963                         return cur;
964
965         return NULL;
966 }
967
968 void uh_client_shutdown(struct client *cl)
969 {
970 #ifdef HAVE_TLS
971         /* free client tls context */
972         if (cl->server && cl->server->conf->tls)
973                 cl->server->conf->tls_close(cl);
974 #endif
975
976         /* remove from global client list */
977         uh_client_remove(cl);
978 }
979
980 void uh_client_remove(struct client *cl)
981 {
982         struct client *cur = NULL;
983         struct client *prv = NULL;
984
985         for (cur = uh_clients; cur; prv = cur, cur = cur->next)
986         {
987                 if (cur == cl)
988                 {
989                         if (prv)
990                                 prv->next = cur->next;
991                         else
992                                 uh_clients = cur->next;
993
994                         if (cur->timeout.pending)
995                                 uloop_timeout_cancel(&cur->timeout);
996
997                         if (cur->proc.pid)
998                                 uloop_process_delete(&cur->proc);
999
1000                         D("IO: Client(%d) freeing\n", cur->fd.fd);
1001
1002                         uh_ufd_remove(&cur->rpipe);
1003                         uh_ufd_remove(&cur->wpipe);
1004                         uh_ufd_remove(&cur->fd);
1005
1006                         cur->server->n_clients--;
1007
1008                         free(cur);
1009                         break;
1010                 }
1011         }
1012 }
1013
1014
1015 void uh_ufd_add(struct uloop_fd *u, uloop_fd_handler h, unsigned int ev)
1016 {
1017         if (h != NULL)
1018         {
1019                 u->cb = h;
1020                 uloop_fd_add(u, ev);
1021                 D("IO: FD(%d) added to uloop\n", u->fd);
1022         }
1023 }
1024
1025 void uh_ufd_remove(struct uloop_fd *u)
1026 {
1027         if (u->cb != NULL)
1028         {
1029                 uloop_fd_delete(u);
1030                 D("IO: FD(%d) removed from uloop\n", u->fd);
1031                 u->cb = NULL;
1032         }
1033
1034         if (u->fd > -1)
1035         {
1036                 close(u->fd);
1037                 D("IO: FD(%d) closed\n", u->fd);
1038                 u->fd = -1;
1039         }
1040 }
1041
1042
1043 #ifdef HAVE_CGI
1044 static struct interpreter *uh_interpreters = NULL;
1045
1046 struct interpreter * uh_interpreter_add(const char *extn, const char *path)
1047 {
1048         struct interpreter *new = NULL;
1049
1050         if ((new = (struct interpreter *)malloc(sizeof(struct interpreter))) != NULL)
1051         {
1052                 memset(new, 0, sizeof(struct interpreter));
1053
1054                 memcpy(new->extn, extn, min(strlen(extn), sizeof(new->extn)-1));
1055                 memcpy(new->path, path, min(strlen(path), sizeof(new->path)-1));
1056
1057                 new->next = uh_interpreters;
1058                 uh_interpreters = new;
1059
1060                 return new;
1061         }
1062
1063         return NULL;
1064 }
1065
1066 struct interpreter * uh_interpreter_lookup(const char *path)
1067 {
1068         struct interpreter *cur = NULL;
1069         const char *e;
1070
1071         for (cur = uh_interpreters; cur; cur = cur->next)
1072         {
1073                 e = &path[max(strlen(path) - strlen(cur->extn), 0)];
1074
1075                 if (!strcmp(e, cur->extn))
1076                         return cur;
1077         }
1078
1079         return NULL;
1080 }
1081 #endif