Root/package/uhttpd/src/uhttpd-utils.c

1/*
2 * uhttpd - Tiny single-threaded httpd - Utility functions
3 *
4 * Copyright (C) 2010 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
30static char *uh_index_files[] = {
31    "index.html",
32    "index.htm",
33    "default.html",
34    "default.htm"
35};
36
37
38const 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
50const 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
57int sa_port(void *sa)
58{
59    return ntohs(((struct sockaddr_in6 *)sa)->sin6_port);
60}
61
62int 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. */
78char *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/* interruptable select() */
107int select_intr(int n, fd_set *r, fd_set *w, fd_set *e, struct timeval *t)
108{
109    int rv;
110    sigset_t ssn, sso;
111
112    /* unblock SIGCHLD */
113    sigemptyset(&ssn);
114    sigaddset(&ssn, SIGCHLD);
115    sigprocmask(SIG_UNBLOCK, &ssn, &sso);
116
117    rv = select(n, r, w, e, t);
118
119    /* restore signal mask */
120    sigprocmask(SIG_SETMASK, &sso, NULL);
121
122    return rv;
123}
124
125
126int uh_tcp_send(struct client *cl, const char *buf, int len)
127{
128    fd_set writer;
129    struct timeval timeout;
130
131    FD_ZERO(&writer);
132    FD_SET(cl->socket, &writer);
133
134    timeout.tv_sec = cl->server->conf->network_timeout;
135    timeout.tv_usec = 0;
136
137    if( select(cl->socket + 1, NULL, &writer, NULL, &timeout) > 0 )
138    {
139#ifdef HAVE_TLS
140        if( cl->tls )
141            return cl->server->conf->tls_send(cl, (void *)buf, len);
142        else
143#endif
144            return send(cl->socket, buf, len, 0);
145    }
146
147    return -1;
148}
149
150int uh_tcp_peek(struct client *cl, char *buf, int len)
151{
152    int sz = uh_tcp_recv(cl, buf, len);
153
154    /* store received data in peek buffer */
155    if( sz > 0 )
156    {
157        cl->peeklen = sz;
158        memcpy(cl->peekbuf, buf, sz);
159    }
160
161    return sz;
162}
163
164int uh_tcp_recv(struct client *cl, char *buf, int len)
165{
166    int sz = 0;
167    int rsz = 0;
168
169    /* first serve data from peek buffer */
170    if( cl->peeklen > 0 )
171    {
172        sz = min(cl->peeklen, len);
173        len -= sz; cl->peeklen -= sz;
174
175        memcpy(buf, cl->peekbuf, sz);
176        memmove(cl->peekbuf, &cl->peekbuf[sz], cl->peeklen);
177    }
178
179    /* caller wants more */
180    if( len > 0 )
181    {
182#ifdef HAVE_TLS
183        if( cl->tls )
184            rsz = cl->server->conf->tls_recv(cl, (void *)&buf[sz], len);
185        else
186#endif
187            rsz = recv(cl->socket, (void *)&buf[sz], len, 0);
188
189        if( (sz == 0) || (rsz > 0) )
190            sz += rsz;
191    }
192
193    return sz;
194}
195
196#define ensure(x) \
197    do { if( x < 0 ) return -1; } while(0)
198
199int uh_http_sendhf(struct client *cl, int code, const char *summary, const char *fmt, ...)
200{
201    va_list ap;
202
203    char buffer[UH_LIMIT_MSGHEAD];
204    int len;
205
206    len = snprintf(buffer, sizeof(buffer),
207        "HTTP/1.1 %03i %s\r\n"
208        "Connection: close\r\n"
209        "Content-Type: text/plain\r\n"
210        "Transfer-Encoding: chunked\r\n\r\n",
211            code, summary
212    );
213
214    ensure(uh_tcp_send(cl, buffer, len));
215
216    va_start(ap, fmt);
217    len = vsnprintf(buffer, sizeof(buffer), fmt, ap);
218    va_end(ap);
219
220    ensure(uh_http_sendc(cl, buffer, len));
221    ensure(uh_http_sendc(cl, NULL, 0));
222
223    return 0;
224}
225
226
227int uh_http_sendc(struct client *cl, const char *data, int len)
228{
229    char chunk[8];
230    int clen;
231
232    if( len == -1 )
233        len = strlen(data);
234
235    if( len > 0 )
236    {
237         clen = snprintf(chunk, sizeof(chunk), "%X\r\n", len);
238        ensure(uh_tcp_send(cl, chunk, clen));
239        ensure(uh_tcp_send(cl, data, len));
240        ensure(uh_tcp_send(cl, "\r\n", 2));
241    }
242    else
243    {
244        ensure(uh_tcp_send(cl, "0\r\n\r\n", 5));
245    }
246
247    return 0;
248}
249
250int uh_http_sendf(
251    struct client *cl, struct http_request *req, const char *fmt, ...
252) {
253    va_list ap;
254    char buffer[UH_LIMIT_MSGHEAD];
255    int len;
256
257    va_start(ap, fmt);
258    len = vsnprintf(buffer, sizeof(buffer), fmt, ap);
259    va_end(ap);
260
261    if( (req != NULL) && (req->version > 1.0) )
262        ensure(uh_http_sendc(cl, buffer, len));
263    else if( len > 0 )
264        ensure(uh_tcp_send(cl, buffer, len));
265
266    return 0;
267}
268
269int uh_http_send(
270    struct client *cl, struct http_request *req, const char *buf, int len
271) {
272    if( len < 0 )
273        len = strlen(buf);
274
275    if( (req != NULL) && (req->version > 1.0) )
276        ensure(uh_http_sendc(cl, buf, len));
277    else if( len > 0 )
278        ensure(uh_tcp_send(cl, buf, len));
279
280    return 0;
281}
282
283
284int uh_urldecode(char *buf, int blen, const char *src, int slen)
285{
286    int i;
287    int len = 0;
288
289#define hex(x) \
290    (((x) <= '9') ? ((x) - '0') : \
291        (((x) <= 'F') ? ((x) - 'A' + 10) : \
292            ((x) - 'a' + 10)))
293
294    for( i = 0; (i <= slen) && (i <= blen); i++ )
295    {
296        if( src[i] == '%' )
297        {
298            if( ((i+2) <= slen) && isxdigit(src[i+1]) && isxdigit(src[i+2]) )
299            {
300                buf[len++] = (char)(16 * hex(src[i+1]) + hex(src[i+2]));
301                i += 2;
302            }
303            else
304            {
305                buf[len++] = '%';
306            }
307        }
308        else
309        {
310            buf[len++] = src[i];
311        }
312    }
313
314    return len;
315}
316
317int uh_urlencode(char *buf, int blen, const char *src, int slen)
318{
319    int i;
320    int len = 0;
321    const char hex[] = "0123456789abcdef";
322
323    for( i = 0; (i <= slen) && (i <= blen); i++ )
324    {
325        if( isalnum(src[i]) || (src[i] == '-') || (src[i] == '_') ||
326            (src[i] == '.') || (src[i] == '~') )
327        {
328            buf[len++] = src[i];
329        }
330        else if( (len+3) <= blen )
331        {
332            buf[len++] = '%';
333            buf[len++] = hex[(src[i] >> 4) & 15];
334            buf[len++] = hex[(src[i] & 15) & 15];
335        }
336        else
337        {
338            break;
339        }
340    }
341
342    return len;
343}
344
345int uh_b64decode(char *buf, int blen, const unsigned char *src, int slen)
346{
347    int i = 0;
348    int len = 0;
349
350    unsigned int cin = 0;
351    unsigned int cout = 0;
352
353
354    for( i = 0; (i <= slen) && (src[i] != 0); i++ )
355    {
356        cin = src[i];
357
358        if( (cin >= '0') && (cin <= '9') )
359            cin = cin - '0' + 52;
360        else if( (cin >= 'A') && (cin <= 'Z') )
361            cin = cin - 'A';
362        else if( (cin >= 'a') && (cin <= 'z') )
363            cin = cin - 'a' + 26;
364        else if( cin == '+' )
365            cin = 62;
366        else if( cin == '/' )
367            cin = 63;
368        else if( cin == '=' )
369            cin = 0;
370        else
371            continue;
372
373        cout = (cout << 6) | cin;
374
375        if( (i % 4) == 3 )
376        {
377            if( (len + 3) < blen )
378            {
379                buf[len++] = (char)(cout >> 16);
380                buf[len++] = (char)(cout >> 8);
381                buf[len++] = (char)(cout);
382            }
383            else
384            {
385                break;
386            }
387        }
388    }
389
390    buf[len++] = 0;
391    return len;
392}
393
394static char * canonpath(const char *path, char *path_resolved)
395{
396    char path_copy[PATH_MAX];
397    char *path_cpy = path_copy;
398    char *path_res = path_resolved;
399
400    struct stat s;
401
402
403    /* relative -> absolute */
404    if( *path != '/' )
405    {
406        getcwd(path_copy, PATH_MAX);
407        strncat(path_copy, "/", PATH_MAX - strlen(path_copy));
408        strncat(path_copy, path, PATH_MAX - strlen(path_copy));
409    }
410    else
411    {
412        strncpy(path_copy, path, PATH_MAX);
413    }
414
415    /* normalize */
416    while( (*path_cpy != '\0') && (path_cpy < (path_copy + PATH_MAX - 2)) )
417    {
418        if( *path_cpy == '/' )
419        {
420            /* skip repeating / */
421            if( path_cpy[1] == '/' )
422            {
423                path_cpy++;
424                continue;
425            }
426
427            /* /./ or /../ */
428            else if( path_cpy[1] == '.' )
429            {
430                /* skip /./ */
431                if( (path_cpy[2] == '/') || (path_cpy[2] == '\0') )
432                {
433                    path_cpy += 2;
434                    continue;
435                }
436
437                /* collapse /x/../ */
438                else if( (path_cpy[2] == '.') &&
439                         ((path_cpy[3] == '/') || (path_cpy[3] == '\0'))
440                ) {
441                    while( (path_res > path_resolved) && (*--path_res != '/') )
442                        ;
443
444                    path_cpy += 3;
445                    continue;
446                }
447            }
448        }
449
450        *path_res++ = *path_cpy++;
451    }
452
453    /* remove trailing slash if not root / */
454    if( (path_res > (path_resolved+1)) && (path_res[-1] == '/') )
455        path_res--;
456    else if( path_res == path_resolved )
457        *path_res++ = '/';
458
459    *path_res = '\0';
460
461    /* test access */
462    if( !stat(path_resolved, &s) && (s.st_mode & S_IROTH) )
463        return path_resolved;
464
465    return NULL;
466}
467
468struct path_info * uh_path_lookup(struct client *cl, const char *url)
469{
470    static char path_phys[PATH_MAX];
471    static char path_info[PATH_MAX];
472    static struct path_info p;
473
474    char buffer[UH_LIMIT_MSGHEAD];
475    char *docroot = cl->server->conf->docroot;
476    char *pathptr = NULL;
477
478    int no_sym = cl->server->conf->no_symlinks;
479    int i = 0;
480    struct stat s;
481
482    /* back out early if url is undefined */
483    if ( url == NULL )
484        return NULL;
485
486    memset(path_phys, 0, sizeof(path_phys));
487    memset(path_info, 0, sizeof(path_info));
488    memset(buffer, 0, sizeof(buffer));
489    memset(&p, 0, sizeof(p));
490
491    /* copy docroot */
492    memcpy(buffer, docroot,
493        min(strlen(docroot), sizeof(buffer) - 1));
494
495    /* separate query string from url */
496    if( (pathptr = strchr(url, '?')) != NULL )
497    {
498        p.query = pathptr[1] ? pathptr + 1 : NULL;
499
500        /* urldecode component w/o query */
501        if( pathptr > url )
502            uh_urldecode(
503                &buffer[strlen(docroot)],
504                sizeof(buffer) - strlen(docroot) - 1,
505                url, (int)(pathptr - url) - 1
506            );
507    }
508
509    /* no query string, decode all of url */
510    else
511    {
512        uh_urldecode(
513            &buffer[strlen(docroot)],
514            sizeof(buffer) - strlen(docroot) - 1,
515            url, strlen(url)
516        );
517    }
518
519    /* create canon path */
520    for( i = strlen(buffer); i >= 0; i-- )
521    {
522        if( (buffer[i] == 0) || (buffer[i] == '/') )
523        {
524            memset(path_info, 0, sizeof(path_info));
525            memcpy(path_info, buffer, min(i + 1, sizeof(path_info) - 1));
526
527            if( no_sym ? realpath(path_info, path_phys)
528                       : canonpath(path_info, path_phys)
529            ) {
530                memset(path_info, 0, sizeof(path_info));
531                memcpy(path_info, &buffer[i],
532                    min(strlen(buffer) - i, sizeof(path_info) - 1));
533
534                break;
535            }
536        }
537    }
538
539    /* check whether found path is within docroot */
540    if( strncmp(path_phys, docroot, strlen(docroot)) ||
541        ((path_phys[strlen(docroot)] != 0) &&
542         (path_phys[strlen(docroot)] != '/'))
543    ) {
544        return NULL;
545    }
546
547    /* test current path */
548    if( ! stat(path_phys, &p.stat) )
549    {
550        /* is a regular file */
551        if( p.stat.st_mode & S_IFREG )
552        {
553            p.root = docroot;
554            p.phys = path_phys;
555            p.name = &path_phys[strlen(docroot)];
556            p.info = path_info[0] ? path_info : NULL;
557        }
558
559        /* is a directory */
560        else if( (p.stat.st_mode & S_IFDIR) && !strlen(path_info) )
561        {
562            /* ensure trailing slash */
563            if( path_phys[strlen(path_phys)-1] != '/' )
564                path_phys[strlen(path_phys)] = '/';
565
566            /* try to locate index file */
567            memset(buffer, 0, sizeof(buffer));
568            memcpy(buffer, path_phys, sizeof(buffer));
569            pathptr = &buffer[strlen(buffer)];
570
571            if( cl->server->conf->index_file )
572            {
573                strncat(buffer, cl->server->conf->index_file, sizeof(buffer));
574
575                if( !stat(buffer, &s) && (s.st_mode & S_IFREG) )
576                {
577                    memcpy(path_phys, buffer, sizeof(path_phys));
578                    memcpy(&p.stat, &s, sizeof(p.stat));
579                }
580            }
581            else
582            {
583                for( i = 0; i < array_size(uh_index_files); i++ )
584                {
585                    strncat(buffer, uh_index_files[i], sizeof(buffer));
586
587                    if( !stat(buffer, &s) && (s.st_mode & S_IFREG) )
588                    {
589                        memcpy(path_phys, buffer, sizeof(path_phys));
590                        memcpy(&p.stat, &s, sizeof(p.stat));
591                        break;
592                    }
593
594                    *pathptr = 0;
595                }
596            }
597
598            p.root = docroot;
599            p.phys = path_phys;
600            p.name = &path_phys[strlen(docroot)];
601        }
602    }
603
604    return p.phys ? &p : NULL;
605}
606
607
608static char uh_realms[UH_LIMIT_AUTHREALMS * sizeof(struct auth_realm)] = { 0 };
609static int uh_realm_count = 0;
610
611struct auth_realm * uh_auth_add(char *path, char *user, char *pass)
612{
613    struct auth_realm *new = NULL;
614    struct passwd *pwd;
615    struct spwd *spwd;
616
617    if( uh_realm_count < UH_LIMIT_AUTHREALMS )
618    {
619        new = (struct auth_realm *)
620            &uh_realms[uh_realm_count * sizeof(struct auth_realm)];
621
622        memset(new, 0, sizeof(struct auth_realm));
623
624        memcpy(new->path, path,
625            min(strlen(path), sizeof(new->path) - 1));
626
627        memcpy(new->user, user,
628            min(strlen(user), sizeof(new->user) - 1));
629
630        /* given password refers to a passwd entry */
631        if( (strlen(pass) > 3) && !strncmp(pass, "$p$", 3) )
632        {
633            /* try to resolve shadow entry */
634            if( ((spwd = getspnam(&pass[3])) != NULL) && spwd->sp_pwdp )
635            {
636                memcpy(new->pass, spwd->sp_pwdp,
637                    min(strlen(spwd->sp_pwdp), sizeof(new->pass) - 1));
638            }
639
640            /* try to resolve passwd entry */
641            else if( ((pwd = getpwnam(&pass[3])) != NULL) && pwd->pw_passwd &&
642                (pwd->pw_passwd[0] != '!') && (pwd->pw_passwd[0] != 0)
643            ) {
644                memcpy(new->pass, pwd->pw_passwd,
645                    min(strlen(pwd->pw_passwd), sizeof(new->pass) - 1));
646            }
647        }
648
649        /* ordinary pwd */
650        else
651        {
652            memcpy(new->pass, pass,
653                min(strlen(pass), sizeof(new->pass) - 1));
654        }
655
656        if( new->pass[0] )
657        {
658            uh_realm_count++;
659            return new;
660        }
661    }
662
663    return NULL;
664}
665
666int uh_auth_check(
667    struct client *cl, struct http_request *req, struct path_info *pi
668) {
669    int i, plen, rlen, protected;
670    char buffer[UH_LIMIT_MSGHEAD];
671    char *user = NULL;
672    char *pass = NULL;
673
674    struct auth_realm *realm = NULL;
675
676    plen = strlen(pi->name);
677    protected = 0;
678
679    /* check whether at least one realm covers the requested url */
680    for( i = 0; i < uh_realm_count; i++ )
681    {
682        realm = (struct auth_realm *)
683            &uh_realms[i * sizeof(struct auth_realm)];
684
685        rlen = strlen(realm->path);
686
687        if( (plen >= rlen) && !strncasecmp(pi->name, realm->path, rlen) )
688        {
689            req->realm = realm;
690            protected = 1;
691            break;
692        }
693    }
694
695    /* requested resource is covered by a realm */
696    if( protected )
697    {
698        /* try to get client auth info */
699        foreach_header(i, req->headers)
700        {
701            if( !strcasecmp(req->headers[i], "Authorization") &&
702                (strlen(req->headers[i+1]) > 6) &&
703                !strncasecmp(req->headers[i+1], "Basic ", 6)
704            ) {
705                memset(buffer, 0, sizeof(buffer));
706                uh_b64decode(buffer, sizeof(buffer) - 1,
707                    (unsigned char *) &req->headers[i+1][6],
708                    strlen(req->headers[i+1]) - 6);
709
710                if( (pass = strchr(buffer, ':')) != NULL )
711                {
712                    user = buffer;
713                    *pass++ = 0;
714                }
715
716                break;
717            }
718        }
719
720        /* have client auth */
721        if( user && pass )
722        {
723            /* find matching realm */
724            for( i = 0, realm = NULL; i < uh_realm_count; i++ )
725            {
726                realm = (struct auth_realm *)
727                    &uh_realms[i * sizeof(struct auth_realm)];
728
729                rlen = strlen(realm->path);
730
731                if( (plen >= rlen) &&
732                    !strncasecmp(pi->name, realm->path, rlen) &&
733                    !strcmp(user, realm->user)
734                ) {
735                    req->realm = realm;
736                    break;
737                }
738
739                realm = NULL;
740            }
741
742            /* found a realm matching the username */
743            if( realm )
744            {
745                /* is a crypt passwd */
746                if( realm->pass[0] == '$' )
747                    pass = crypt(pass, realm->pass);
748
749                /* check user pass */
750                if( !strcmp(pass, realm->pass) )
751                    return 1;
752            }
753        }
754
755        /* 401 */
756        uh_http_sendf(cl, NULL,
757            "HTTP/%.1f 401 Authorization Required\r\n"
758            "WWW-Authenticate: Basic realm=\"%s\"\r\n"
759            "Content-Type: text/plain\r\n"
760            "Content-Length: 23\r\n\r\n"
761            "Authorization Required\n",
762                req->version, cl->server->conf->realm
763        );
764
765        return 0;
766    }
767
768    return 1;
769}
770
771
772static char uh_listeners[UH_LIMIT_LISTENERS * sizeof(struct listener)] = { 0 };
773static char uh_clients[UH_LIMIT_CLIENTS * sizeof(struct client)] = { 0 };
774
775static int uh_listener_count = 0;
776static int uh_client_count = 0;
777
778
779struct listener * uh_listener_add(int sock, struct config *conf)
780{
781    struct listener *new = NULL;
782    socklen_t sl;
783
784    if( uh_listener_count < UH_LIMIT_LISTENERS )
785    {
786        new = (struct listener *)
787            &uh_listeners[uh_listener_count * sizeof(struct listener)];
788
789        new->socket = sock;
790        new->conf = conf;
791
792        /* get local endpoint addr */
793        sl = sizeof(struct sockaddr_in6);
794        memset(&(new->addr), 0, sl);
795        getsockname(sock, (struct sockaddr *) &(new->addr), &sl);
796
797        uh_listener_count++;
798    }
799
800    return new;
801}
802
803struct listener * uh_listener_lookup(int sock)
804{
805    struct listener *cur = NULL;
806    int i;
807
808    for( i = 0; i < uh_listener_count; i++ )
809    {
810        cur = (struct listener *) &uh_listeners[i * sizeof(struct listener)];
811
812        if( cur->socket == sock )
813            return cur;
814    }
815
816    return NULL;
817}
818
819
820struct client * uh_client_add(int sock, struct listener *serv)
821{
822    struct client *new = NULL;
823    socklen_t sl;
824
825    if( uh_client_count < UH_LIMIT_CLIENTS )
826    {
827        new = (struct client *)
828            &uh_clients[uh_client_count * sizeof(struct client)];
829
830        new->socket = sock;
831        new->server = serv;
832
833        /* get remote endpoint addr */
834        sl = sizeof(struct sockaddr_in6);
835        memset(&(new->peeraddr), 0, sl);
836        getpeername(sock, (struct sockaddr *) &(new->peeraddr), &sl);
837
838        /* get local endpoint addr */
839        sl = sizeof(struct sockaddr_in6);
840        memset(&(new->servaddr), 0, sl);
841        getsockname(sock, (struct sockaddr *) &(new->servaddr), &sl);
842
843        uh_client_count++;
844    }
845
846    return new;
847}
848
849struct client * uh_client_lookup(int sock)
850{
851    struct client *cur = NULL;
852    int i;
853
854    for( i = 0; i < uh_client_count; i++ )
855    {
856        cur = (struct client *) &uh_clients[i * sizeof(struct client)];
857
858        if( cur->socket == sock )
859            return cur;
860    }
861
862    return NULL;
863}
864
865void uh_client_remove(int sock)
866{
867    struct client *del = uh_client_lookup(sock);
868
869    if( del )
870    {
871        memmove(del, del + 1,
872            sizeof(uh_clients) - (int)((char *)del - uh_clients) - sizeof(struct client));
873
874        uh_client_count--;
875    }
876}
877
878
879

Archive Download this file



interactive