~ubuntu-branches/debian/lenny/italc/lenny

« back to all changes in this revision

Viewing changes to common/ivs/libvncserver/httpd.c

  • Committer: Bazaar Package Importer
  • Author(s): Patrick Winnertz
  • Date: 2008-06-17 13:46:54 UTC
  • mfrom: (1.2.1 upstream) (4.1.1 gutsy)
  • Revision ID: james.westby@ubuntu.com-20080617134654-cl0gi4u524cv1ici
Tags: 1:1.0.9~rc3-1
* Package new upstream version
  - upstream ported the code to qt4.4 (Closes: #481974)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * httpd.c - a simple HTTP server
3
 
 */
4
 
 
5
 
/*
6
 
 *  Copyright (C) 2002 RealVNC Ltd.
7
 
 *  Copyright (C) 1999 AT&T Laboratories Cambridge.  All Rights Reserved.
8
 
 *
9
 
 *  This is free software; you can redistribute it and/or modify
10
 
 *  it under the terms of the GNU General Public License as published by
11
 
 *  the Free Software Foundation; either version 2 of the License, or
12
 
 *  (at your option) any later version.
13
 
 *
14
 
 *  This software is distributed in the hope that it will be useful,
15
 
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 
 *  GNU General Public License for more details.
18
 
 *
19
 
 *  You should have received a copy of the GNU General Public License
20
 
 *  along with this software; if not, write to the Free Software
21
 
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
22
 
 *  USA.
23
 
 */
24
 
 
25
 
#include <rfb/rfb.h>
26
 
 
27
 
#include <ctype.h>
28
 
#ifdef HAVE_UNISTD_H
29
 
#include <unistd.h>
30
 
#endif
31
 
#ifdef HAVE_SYS_TYPES_H
32
 
#include <sys/types.h>
33
 
#endif
34
 
#ifdef HAVE_FCNTL_H
35
 
#include <fcntl.h>
36
 
#endif
37
 
#include <errno.h>
38
 
 
39
 
#ifdef WIN32
40
 
#include <winsock.h>
41
 
#define close closesocket
42
 
#else
43
 
#ifdef HAVE_SYS_TIME_H
44
 
#include <sys/time.h>
45
 
#endif
46
 
#ifdef HAVE_SYS_SOCKET_H
47
 
#include <sys/socket.h>
48
 
#endif
49
 
#ifdef HAVE_NETINET_IN_H
50
 
#include <netinet/in.h>
51
 
#include <netinet/tcp.h>
52
 
#include <netdb.h>
53
 
#include <arpa/inet.h>
54
 
#endif
55
 
#include <pwd.h>
56
 
#endif
57
 
 
58
 
#ifdef USE_LIBWRAP
59
 
#include <tcpd.h>
60
 
#endif
61
 
 
62
 
#define NOT_FOUND_STR "HTTP/1.0 404 Not found\r\n\r\n" \
63
 
    "<HEAD><TITLE>File Not Found</TITLE></HEAD>\n" \
64
 
    "<BODY><H1>File Not Found</H1></BODY>\n"
65
 
 
66
 
#define INVALID_REQUEST_STR "HTTP/1.0 400 Invalid Request\r\n\r\n" \
67
 
    "<HEAD><TITLE>Invalid Request</TITLE></HEAD>\n" \
68
 
    "<BODY><H1>Invalid request</H1></BODY>\n"
69
 
 
70
 
#define OK_STR "HTTP/1.0 200 OK\nContent-Type: text/html\r\n\r\n"
71
 
 
72
 
static void httpProcessInput();
73
 
static rfbBool compareAndSkip(char **ptr, const char *str);
74
 
static rfbBool parseParams(const char *request, char *result, int max_bytes);
75
 
static rfbBool validateString(char *str);
76
 
 
77
 
#define BUF_SIZE 32768
78
 
 
79
 
static char buf[BUF_SIZE];
80
 
static size_t buf_filled=0;
81
 
 
82
 
/*
83
 
 * httpInitSockets sets up the TCP socket to listen for HTTP connections.
84
 
 */
85
 
 
86
 
void
87
 
httpInitSockets(rfbScreenInfoPtr rfbScreen)
88
 
{
89
 
    if (rfbScreen->httpInitDone)
90
 
        return;
91
 
 
92
 
    rfbScreen->httpInitDone = TRUE;
93
 
 
94
 
    if (!rfbScreen->httpDir)
95
 
        return;
96
 
 
97
 
    if (rfbScreen->httpPort == 0) {
98
 
        rfbScreen->httpPort = rfbScreen->rfbPort-100;
99
 
    }
100
 
 
101
 
    rfbLog("Listening for HTTP connections on TCP port %d\n", rfbScreen->httpPort);
102
 
 
103
 
    rfbLog("  URL http://%s:%d\n",rfbScreen->rfbThisHost,rfbScreen->httpPort);
104
 
 
105
 
    if ((rfbScreen->httpListenSock = ListenOnTCPPort(rfbScreen->httpPort)) < 0) {
106
 
        rfbLogPerror("ListenOnTCPPort");
107
 
        return;
108
 
    }
109
 
 
110
 
   /*AddEnabledDevice(httpListenSock);*/
111
 
}
112
 
 
113
 
 
114
 
/*
115
 
 * httpCheckFds is called from ProcessInputEvents to check for input on the
116
 
 * HTTP socket(s).  If there is input to process, httpProcessInput is called.
117
 
 */
118
 
 
119
 
void
120
 
httpCheckFds(rfbScreenInfoPtr rfbScreen)
121
 
{
122
 
    int nfds;
123
 
    fd_set fds;
124
 
    struct timeval tv;
125
 
    struct sockaddr_in addr;
126
 
    size_t addrlen = sizeof(addr);
127
 
 
128
 
    if (!rfbScreen->httpDir)
129
 
        return;
130
 
 
131
 
    FD_ZERO(&fds);
132
 
    FD_SET(rfbScreen->httpListenSock, &fds);
133
 
    if (rfbScreen->httpSock >= 0) {
134
 
        FD_SET(rfbScreen->httpSock, &fds);
135
 
    }
136
 
    tv.tv_sec = 0;
137
 
    tv.tv_usec = 0;
138
 
    nfds = select(max(rfbScreen->httpSock,rfbScreen->httpListenSock) + 1, &fds, NULL, NULL, &tv);
139
 
    if (nfds == 0) {
140
 
        return;
141
 
    }
142
 
    if (nfds < 0) {
143
 
#ifdef WIN32
144
 
                errno = WSAGetLastError();
145
 
#endif
146
 
        if (errno != EINTR)
147
 
                rfbLogPerror("httpCheckFds: select");
148
 
        return;
149
 
    }
150
 
 
151
 
    if ((rfbScreen->httpSock >= 0) && FD_ISSET(rfbScreen->httpSock, &fds)) {
152
 
        httpProcessInput(rfbScreen);
153
 
    }
154
 
 
155
 
    if (FD_ISSET(rfbScreen->httpListenSock, &fds)) {
156
 
        int flags;
157
 
        if (rfbScreen->httpSock >= 0) close(rfbScreen->httpSock);
158
 
 
159
 
        if ((rfbScreen->httpSock = accept(rfbScreen->httpListenSock,
160
 
                               (struct sockaddr *)&addr, &addrlen)) < 0) {
161
 
            rfbLogPerror("httpCheckFds: accept");
162
 
            return;
163
 
        }
164
 
#ifdef USE_LIBWRAP
165
 
        if(!hosts_ctl("vnc",STRING_UNKNOWN,inet_ntoa(addr.sin_addr),
166
 
                      STRING_UNKNOWN)) {
167
 
          rfbLog("Rejected HTTP connection from client %s\n",
168
 
                 inet_ntoa(addr.sin_addr));
169
 
#else
170
 
        flags = fcntl(rfbScreen->httpSock, F_GETFL);
171
 
 
172
 
        if (flags < 0 || fcntl(rfbScreen->httpSock, F_SETFL, flags | O_NONBLOCK) == -1) {
173
 
            rfbLogPerror("httpCheckFds: fcntl");
174
 
#endif
175
 
            close(rfbScreen->httpSock);
176
 
            rfbScreen->httpSock = -1;
177
 
            return;
178
 
        }
179
 
        flags=fcntl(rfbScreen->httpSock,F_GETFL);
180
 
        if(flags==-1 ||
181
 
           fcntl(rfbScreen->httpSock,F_SETFL,flags|O_NONBLOCK)==-1) {
182
 
          rfbLogPerror("httpCheckFds: fcntl");
183
 
          close(rfbScreen->httpSock);
184
 
          rfbScreen->httpSock=-1;
185
 
          return;
186
 
        }
187
 
 
188
 
        /*AddEnabledDevice(httpSock);*/
189
 
    }
190
 
}
191
 
 
192
 
 
193
 
static void
194
 
httpCloseSock(rfbScreenInfoPtr rfbScreen)
195
 
{
196
 
    close(rfbScreen->httpSock);
197
 
    rfbScreen->httpSock = -1;
198
 
    buf_filled = 0;
199
 
}
200
 
 
201
 
static rfbClientRec cl;
202
 
 
203
 
/*
204
 
 * httpProcessInput is called when input is received on the HTTP socket.
205
 
 */
206
 
 
207
 
static void
208
 
httpProcessInput(rfbScreenInfoPtr rfbScreen)
209
 
{
210
 
    struct sockaddr_in addr;
211
 
    size_t addrlen = sizeof(addr);
212
 
    char fullFname[512];
213
 
    char params[1024];
214
 
    char *ptr;
215
 
    char *fname;
216
 
    unsigned int maxFnameLen;
217
 
    FILE* fd;
218
 
    rfbBool performSubstitutions = FALSE;
219
 
    char str[256+32];
220
 
#ifndef WIN32
221
 
    char* user=getenv("USER");
222
 
#endif
223
 
   
224
 
    cl.sock=rfbScreen->httpSock;
225
 
 
226
 
    if (strlen(rfbScreen->httpDir) > 255) {
227
 
        rfbErr("-httpd directory too long\n");
228
 
        httpCloseSock(rfbScreen);
229
 
        return;
230
 
    }
231
 
    strcpy(fullFname, rfbScreen->httpDir);
232
 
    fname = &fullFname[strlen(fullFname)];
233
 
    maxFnameLen = 511 - strlen(fullFname);
234
 
 
235
 
    buf_filled=0;
236
 
 
237
 
    /* Read data from the HTTP client until we get a complete request. */
238
 
    while (1) {
239
 
        ssize_t got;
240
 
 
241
 
        if (buf_filled > sizeof (buf)) {
242
 
            rfbErr("httpProcessInput: HTTP request is too long\n");
243
 
            httpCloseSock(rfbScreen);
244
 
            return;
245
 
        }
246
 
 
247
 
        got = read (rfbScreen->httpSock, buf + buf_filled,
248
 
                            sizeof (buf) - buf_filled - 1);
249
 
 
250
 
        if (got <= 0) {
251
 
            if (got == 0) {
252
 
                rfbErr("httpd: premature connection close\n");
253
 
            } else {
254
 
                if (errno == EAGAIN) {
255
 
                    return;
256
 
                }
257
 
                rfbLogPerror("httpProcessInput: read");
258
 
            }
259
 
            httpCloseSock(rfbScreen);
260
 
            return;
261
 
        }
262
 
 
263
 
        buf_filled += got;
264
 
        buf[buf_filled] = '\0';
265
 
 
266
 
        /* Is it complete yet (is there a blank line)? */
267
 
        if (strstr (buf, "\r\r") || strstr (buf, "\n\n") ||
268
 
            strstr (buf, "\r\n\r\n") || strstr (buf, "\n\r\n\r"))
269
 
            break;
270
 
    }
271
 
 
272
 
 
273
 
    /* Process the request. */
274
 
    if(rfbScreen->httpEnableProxyConnect) {
275
 
        const static char* PROXY_OK_STR = "HTTP/1.0 200 OK\r\nContent-Type: octet-stream\r\nPragma: no-cache\r\n\r\n";
276
 
        if(!strncmp(buf, "CONNECT ", 8)) {
277
 
            if(atoi(strchr(buf, ':')+1)!=rfbScreen->rfbPort) {
278
 
                rfbErr("httpd: CONNECT format invalid.\n");
279
 
                WriteExact(&cl,INVALID_REQUEST_STR, strlen(INVALID_REQUEST_STR));
280
 
                httpCloseSock(rfbScreen);
281
 
                return;
282
 
            }
283
 
            /* proxy connection */
284
 
            rfbLog("httpd: client asked for CONNECT\n");
285
 
            WriteExact(&cl,PROXY_OK_STR,strlen(PROXY_OK_STR));
286
 
            rfbNewClientConnection(rfbScreen,rfbScreen->httpSock);
287
 
            rfbScreen->httpSock = -1;
288
 
            return;
289
 
        }
290
 
        if (!strncmp(buf, "GET ",4) && !strncmp(strchr(buf,'/'),"/proxied.connection HTTP/1.", 27)) {
291
 
            /* proxy connection */
292
 
            rfbLog("httpd: client asked for /proxied.connection\n");
293
 
            WriteExact(&cl,PROXY_OK_STR,strlen(PROXY_OK_STR));
294
 
            rfbNewClientConnection(rfbScreen,rfbScreen->httpSock);
295
 
            rfbScreen->httpSock = -1;
296
 
            return;
297
 
        }          
298
 
    }
299
 
 
300
 
    if (strncmp(buf, "GET ", 4)) {
301
 
        rfbErr("httpd: no GET line\n");
302
 
        httpCloseSock(rfbScreen);
303
 
        return;
304
 
    } else {
305
 
        /* Only use the first line. */
306
 
        buf[strcspn(buf, "\n\r")] = '\0';
307
 
    }
308
 
 
309
 
    if (strlen(buf) > maxFnameLen) {
310
 
        rfbErr("httpd: GET line too long\n");
311
 
        httpCloseSock(rfbScreen);
312
 
        return;
313
 
    }
314
 
 
315
 
    if (sscanf(buf, "GET %s HTTP/1.0", fname) != 1) {
316
 
        rfbErr("httpd: couldn't parse GET line\n");
317
 
        httpCloseSock(rfbScreen);
318
 
        return;
319
 
    }
320
 
 
321
 
    if (fname[0] != '/') {
322
 
        rfbErr("httpd: filename didn't begin with '/'\n");
323
 
        WriteExact(&cl, NOT_FOUND_STR, strlen(NOT_FOUND_STR));
324
 
        httpCloseSock(rfbScreen);
325
 
        return;
326
 
    }
327
 
 
328
 
    if (strchr(fname+1, '/') != NULL) {
329
 
        rfbErr("httpd: asking for file in other directory\n");
330
 
        WriteExact(&cl, NOT_FOUND_STR, strlen(NOT_FOUND_STR));
331
 
        httpCloseSock(rfbScreen);
332
 
        return;
333
 
    }
334
 
 
335
 
    getpeername(rfbScreen->httpSock, (struct sockaddr *)&addr, &addrlen);
336
 
    rfbLog("httpd: get '%s' for %s\n", fname+1,
337
 
           inet_ntoa(addr.sin_addr));
338
 
 
339
 
    /* Extract parameters from the URL string if necessary */
340
 
 
341
 
    params[0] = '\0';
342
 
    ptr = strchr(fname, '?');
343
 
    if (ptr != NULL) {
344
 
       *ptr = '\0';
345
 
       if (!parseParams(&ptr[1], params, 1024)) {
346
 
           params[0] = '\0';
347
 
           rfbErr("httpd: bad parameters in the URL\n");
348
 
       }
349
 
    }
350
 
 
351
 
 
352
 
    /* If we were asked for '/', actually read the file index.vnc */
353
 
 
354
 
    if (strcmp(fname, "/") == 0) {
355
 
        strcpy(fname, "/index.vnc");
356
 
        rfbLog("httpd: defaulting to '%s'\n", fname+1);
357
 
    }
358
 
 
359
 
    /* Substitutions are performed on files ending .vnc */
360
 
 
361
 
    if (strlen(fname) >= 4 && strcmp(&fname[strlen(fname)-4], ".vnc") == 0) {
362
 
        performSubstitutions = TRUE;
363
 
    }
364
 
 
365
 
    /* Open the file */
366
 
 
367
 
    if ((fd = fopen(fullFname, "r")) == 0) {
368
 
        rfbLogPerror("httpProcessInput: open");
369
 
        WriteExact(&cl, NOT_FOUND_STR, strlen(NOT_FOUND_STR));
370
 
        httpCloseSock(rfbScreen);
371
 
        return;
372
 
    }
373
 
 
374
 
    WriteExact(&cl, OK_STR, strlen(OK_STR));
375
 
 
376
 
    while (1) {
377
 
        int n = fread(buf, 1, BUF_SIZE-1, fd);
378
 
        if (n < 0) {
379
 
            rfbLogPerror("httpProcessInput: read");
380
 
            fclose(fd);
381
 
            httpCloseSock(rfbScreen);
382
 
            return;
383
 
        }
384
 
 
385
 
        if (n == 0)
386
 
            break;
387
 
 
388
 
        if (performSubstitutions) {
389
 
 
390
 
            /* Substitute $WIDTH, $HEIGHT, etc with the appropriate values.
391
 
               This won't quite work properly if the .vnc file is longer than
392
 
               BUF_SIZE, but it's reasonable to assume that .vnc files will
393
 
               always be short. */
394
 
 
395
 
            char *ptr = buf;
396
 
            char *dollar;
397
 
            buf[n] = 0; /* make sure it's null-terminated */
398
 
 
399
 
            while ((dollar = strchr(ptr, '$'))!=NULL) {
400
 
                WriteExact(&cl, ptr, (dollar - ptr));
401
 
 
402
 
                ptr = dollar;
403
 
 
404
 
                if (compareAndSkip(&ptr, "$WIDTH")) {
405
 
 
406
 
                    sprintf(str, "%d", rfbScreen->width);
407
 
                    WriteExact(&cl, str, strlen(str));
408
 
 
409
 
                } else if (compareAndSkip(&ptr, "$HEIGHT")) {
410
 
 
411
 
                    sprintf(str, "%d", rfbScreen->height);
412
 
                    WriteExact(&cl, str, strlen(str));
413
 
 
414
 
                } else if (compareAndSkip(&ptr, "$APPLETWIDTH")) {
415
 
 
416
 
                    sprintf(str, "%d", rfbScreen->width);
417
 
                    WriteExact(&cl, str, strlen(str));
418
 
 
419
 
                } else if (compareAndSkip(&ptr, "$APPLETHEIGHT")) {
420
 
 
421
 
                    sprintf(str, "%d", rfbScreen->height + 32);
422
 
                    WriteExact(&cl, str, strlen(str));
423
 
 
424
 
                } else if (compareAndSkip(&ptr, "$PORT")) {
425
 
 
426
 
                    sprintf(str, "%d", rfbScreen->rfbPort);
427
 
                    WriteExact(&cl, str, strlen(str));
428
 
 
429
 
                } else if (compareAndSkip(&ptr, "$DESKTOP")) {
430
 
 
431
 
                    WriteExact(&cl, rfbScreen->desktopName, strlen(rfbScreen->desktopName));
432
 
 
433
 
                } else if (compareAndSkip(&ptr, "$DISPLAY")) {
434
 
 
435
 
                    sprintf(str, "%s:%d", rfbScreen->rfbThisHost, rfbScreen->rfbPort-5900);
436
 
                    WriteExact(&cl, str, strlen(str));
437
 
 
438
 
                } else if (compareAndSkip(&ptr, "$USER")) {
439
 
#ifndef WIN32
440
 
                    if (user) {
441
 
                        WriteExact(&cl, user,
442
 
                                   strlen(user));
443
 
                    } else
444
 
#endif
445
 
                        WriteExact(&cl, "?", 1);
446
 
                } else if (compareAndSkip(&ptr, "$PARAMS")) {
447
 
                    if (params[0] != '\0')
448
 
                        WriteExact(&cl, params, strlen(params));
449
 
                } else {
450
 
                    if (!compareAndSkip(&ptr, "$$"))
451
 
                        ptr++;
452
 
 
453
 
                    if (WriteExact(&cl, "$", 1) < 0) {
454
 
                        fclose(fd);
455
 
                        httpCloseSock(rfbScreen);
456
 
                        return;
457
 
                    }
458
 
                }
459
 
            }
460
 
            if (WriteExact(&cl, ptr, (&buf[n] - ptr)) < 0)
461
 
                break;
462
 
 
463
 
        } else {
464
 
 
465
 
            /* For files not ending .vnc, just write out the buffer */
466
 
 
467
 
            if (WriteExact(&cl, buf, n) < 0)
468
 
                break;
469
 
        }
470
 
    }
471
 
 
472
 
    fclose(fd);
473
 
    httpCloseSock(rfbScreen);
474
 
}
475
 
 
476
 
 
477
 
static rfbBool
478
 
compareAndSkip(char **ptr, const char *str)
479
 
{
480
 
    if (strncmp(*ptr, str, strlen(str)) == 0) {
481
 
        *ptr += strlen(str);
482
 
        return TRUE;
483
 
    }
484
 
 
485
 
    return FALSE;
486
 
}
487
 
 
488
 
/*
489
 
 * Parse the request tail after the '?' character, and format a sequence
490
 
 * of <param> tags for inclusion into an HTML page with embedded applet.
491
 
 */
492
 
 
493
 
static rfbBool
494
 
parseParams(const char *request, char *result, int max_bytes)
495
 
{
496
 
    char param_request[128];
497
 
    char param_formatted[196];
498
 
    const char *tail;
499
 
    char *delim_ptr;
500
 
    char *value_str;
501
 
    int cur_bytes, len;
502
 
 
503
 
    result[0] = '\0';
504
 
    cur_bytes = 0;
505
 
 
506
 
    tail = request;
507
 
    for (;;) {
508
 
        /* Copy individual "name=value" string into a buffer */
509
 
        delim_ptr = strchr((char *)tail, '&');
510
 
        if (delim_ptr == NULL) {
511
 
            if (strlen(tail) >= sizeof(param_request)) {
512
 
                return FALSE;
513
 
            }
514
 
            strcpy(param_request, tail);
515
 
        } else {
516
 
            len = delim_ptr - tail;
517
 
            if (len >= sizeof(param_request)) {
518
 
                return FALSE;
519
 
            }
520
 
            memcpy(param_request, tail, len);
521
 
            param_request[len] = '\0';
522
 
        }
523
 
 
524
 
        /* Split the request into parameter name and value */
525
 
        value_str = strchr(&param_request[1], '=');
526
 
        if (value_str == NULL) {
527
 
            return FALSE;
528
 
        }
529
 
        *value_str++ = '\0';
530
 
        if (strlen(value_str) == 0) {
531
 
            return FALSE;
532
 
        }
533
 
 
534
 
        /* Validate both parameter name and value */
535
 
        if (!validateString(param_request) || !validateString(value_str)) {
536
 
            return FALSE;
537
 
        }
538
 
 
539
 
        /* Prepare HTML-formatted representation of the name=value pair */
540
 
        len = sprintf(param_formatted,
541
 
                      "<PARAM NAME=\"%s\" VALUE=\"%s\">\n",
542
 
                      param_request, value_str);
543
 
        if (cur_bytes + len + 1 > max_bytes) {
544
 
            return FALSE;
545
 
        }
546
 
        strcat(result, param_formatted);
547
 
        cur_bytes += len;
548
 
 
549
 
        /* Go to the next parameter */
550
 
        if (delim_ptr == NULL) {
551
 
            break;
552
 
        }
553
 
        tail = delim_ptr + 1;
554
 
    }
555
 
    return TRUE;
556
 
}
557
 
 
558
 
/*
559
 
 * Check if the string consists only of alphanumeric characters, '+'
560
 
 * signs, underscores, and dots. Replace all '+' signs with spaces.
561
 
 */
562
 
 
563
 
static rfbBool
564
 
validateString(char *str)
565
 
{
566
 
    char *ptr;
567
 
 
568
 
    for (ptr = str; *ptr != '\0'; ptr++) {
569
 
        if (!isalnum(*ptr) && *ptr != '_' && *ptr != '.') {
570
 
            if (*ptr == '+') {
571
 
                *ptr = ' ';
572
 
            } else {
573
 
                return FALSE;
574
 
            }
575
 
        }
576
 
    }
577
 
    return TRUE;
578
 
}
579