snippets / network

All snippets tagged network (1)

  1. reihttpd - a basic web server

    This was part of an assignment for a Computer Networks course. It implemente GET and conditional GET. Supports persistent connections.

      1 // Copyright (c) 2008, Luis Rei (http://luisrei.com <luis.rei@gmail.com>)
    2 // All rights reserved.
    3 //
    4 // Redistribution and use in source and binary forms, with or without
    5 // modification, are permitted provided that the following conditions are met:
    6 //
    7 // * Redistributions of source code must retain the above copyright notice,
    8 // this list of conditions and the following disclaimer.
    9 // * Redistributions in binary form must reproduce the above copyright
    10 // notice, this list of conditions and the following disclaimer in the
    11 // documentation and/or other materials provided with the distribution.
    12 // * Neither the name of the <ORGANIZATION> nor the names of its
    13 // contributors may be used to endorse or promote products derived from
    14 // this software without specific prior written permission.
    15 //
    16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    17 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    18 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    19 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    20 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    21 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    22 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    23 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    24 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    25 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    26 // POSSIBILITY OF SUCH DAMAGE.
    27
    28
    29
    30 #include <sys/types.h>
    31 #include <sys/socket.h>
    32 #include <sys/stat.h>
    33 #include <netinet/in.h>
    34 #include <arpa/inet.h>
    35 #include <sys/wait.h>
    36 #include <stdio.h>
    37 #include <string.h>
    38 #include <getopt.h>
    39 #include <time.h>
    40 #include <stdlib.h>
    41 #include <unistd.h>
    42 #include <errno.h>
    43 #include <fcntl.h>
    44 #include <signal.h>
    45 #include <netdb.h>
    46 #include <strings.h>
    47
    48
    49 #define MAXLEN 1024
    50 #define TERMINATOR1 "\xD\xA\xD\xA" /*CRLFCRLF - separates header from msg*/
    51 #define TERMINATOR2 "\n\n" /*apparently this is supposed to work too*/
    52 #define SEP " \xD\xA\n\0" /*this is used for string processing*/
    53 #define BACKLOG 10
    54 #define DEFAULT_PORT 80
    55 #define DEFAULT_ROOT "/var/www/data"
    56 #define SERVER "reihttpd/0.1.0\n"
    57
    58
    59 #define R_BADREQUEST "<html><head>\n\
    60 <title>400 Bad Request</title>\n\
    61 </head><body>\n\
    62 <h1>Bad Request</h1>\n\
    63 <p>Your browser sent a request that this server could not understand.<br />\n\
    64 </p>\n\
    65 </body></html>\n"
    66
    67 #define R_FORBIDDEN "<html><head>\n\
    68 <title>403 Forbidden</title>\n\
    69 </head><body>\n\
    70 <h1>Forbidden</h1>\n\
    71 <p>You don't have permission to access this resource.</p>\n\
    72 </body></html>\n"
    73
    74 #define R_NOTFOUND "<html><head>\n\
    75 <title>404 Not Found</title>\n\
    76 </head><body>\n\
    77 <h1>Not Found</h1>\n\
    78 <p>The requested URL was not found on this server.</p>\n\
    79 </body></html>\n"
    80
    81 #define R_NOTIMPLEMENTED "<html><head>\n\
    82 <title>501 Not Implemented</title>\n\
    83 </head><body>\n\
    84 <h1>Not Implemented</h1>\n\
    85 <p>The method you tried to use is not supported by this server.</p>\n\
    86 </body></html>\n"
    87
    88 #define R_INTERNAL "<html><head>\n\
    89 <title>500 Internal Server Error</title>\n\
    90 </head><body>\n\
    91 <h1>Internal Server Error</h1>\n\
    92 <p>Something is broken. Try going back to where you came from.</p>\n\
    93 </body></html>\n"
    94
    95 typedef enum _code {
    96 C_OK = 202,
    97 C_NOTMOD = 304,
    98 C_BADREQUEST = 400,
    99 C_FORBIDDEN = 403,
    100 C_NOTFOUND = 404,
    101 C_INTERNAL = 500,
    102 C_NOTIMPLEMENTED = 501
    103 } code;
    104
    105 int port;
    106 char *root;
    107
    108
    109 int process_request(int sockfd, char *buf, int bytes) {
    110 struct stat st;
    111 struct tm since_tm, file_tm;
    112 code rcode;
    113 time_t now, since_tt, file_tt;
    114 size_t fsize;
    115 int file_des;
    116 char *line;
    117 char *request, *location, *protocol, *host, *since, *connection, *rstr;
    118 char *args[MAXLEN];
    119 char rbuf[MAXLEN];
    120 char temp[MAXLEN];
    121 char headers[MAXLEN];
    122 int ret, ii, argc, rbytes, major, minor;
    123 int COND_FLAG;
    124
    125
    126 printf("++++++++++++++++++++++ REQUEST ++++++++++++++++++++++++++++\n");
    127 printf("%s", buf);
    128 printf("---------------------- REQUEST ----------------------------\n");
    129
    130
    131 argc = 0;
    132 line = strtok(buf, "\n");
    133 do {
    134 args[argc] = (char *) malloc(MAXLEN);
    135 if (args[argc] == NULL) {
    136 perror("malloc");
    137 exit(0);
    138 }
    139 strncpy(args[argc], line, MAXLEN-1);
    140
    141 argc++;
    142 } while ((line = strtok(NULL, "\n")) != NULL);
    143
    144 /* Parse the GET request */
    145 request = strtok(args[0], SEP);
    146 location = strtok(NULL, SEP);
    147 protocol = strtok(NULL, SEP);
    148
    149
    150 /*check the protocol used, if < HTTP/1.1 it's a non-persistent connection*/
    151 major = minor = 0;
    152 if (protocol == NULL)
    153 ret = -1; /*protocol not specified*/
    154 else {
    155 strncpy(temp, protocol, MAXLEN-1);
    156 line = strtok(temp, "/");
    157 if (strcmp(temp, "HTTP") != 0)
    158 ret = -1; /*protocol not HTTP*/
    159 else {
    160 line = strtok(NULL, ".");
    161 major = atoi(line);
    162 if(major < 1)
    163 ret = -1; /*< HTTP/1.0*/
    164 else {
    165 line = strtok(NULL, "\0");
    166 minor = atoi(line);
    167 if(major == 1 && minor == 0)
    168 ret = -1; /*HTTP/1.0*/
    169 }
    170 }
    171 }
    172
    173 /*method*/
    174 if (strcmp(request, "GET") == 0) {
    175 if (strcmp(location, "/") == 0)
    176 location = "index.html";
    177 else if(*location == '/')
    178 location++; /*skip forward slash */
    179 else
    180 exit(0);
    181
    182
    183 file_des = open(location, O_RDONLY);
    184 if (file_des < 0) {
    185 if (errno == EACCES)
    186 rcode = C_FORBIDDEN;
    187 else
    188 rcode = C_NOTFOUND; /*as far as I care*/
    189 }
    190 else
    191 rcode = C_OK;
    192
    193 /* Parse Headers */
    194 connection = NULL;
    195 host = NULL;
    196 since = NULL;
    197 COND_FLAG = 0;
    198
    199 for (ii = 1; ii < argc; ii++) {
    200 line = strtok(args[ii], ": ");
    201 if (line == NULL) {
    202 printf("Line is null.\n");
    203 continue;
    204 }
    205
    206 /*host*/
    207 else if (strcmp(line, "Host") == 0)
    208 host = strtok(NULL, " ");
    209
    210 /*If-Modified-Since*/
    211 else if(strcmp(line, "If-Modified-Since") == 0) {
    212 since = strtok(NULL, "\n");
    213 COND_FLAG = 1;
    214 if(since != NULL)
    215 since++; /*skip whitespace*/
    216 else {
    217 COND_FLAG = 0;
    218 continue;
    219 }
    220
    221 if (since[3] == ',') { /*Sun, 06 Nov 1994 08:49:37 GMT*/
    222 rstr = strptime(since, "%a, %d %b %Y %T GMT", &since_tm);
    223 if(rstr == NULL) {
    224 COND_FLAG = 0;
    225 }
    226 }
    227 else if (since[6] == ',') { /*Sunday, 06-Nov-94 08:49:37 GMT*/
    228 rstr = strptime(since, "%a, %d-%b-%y %T GMT", &since_tm);
    229 if(rstr == NULL) {
    230 COND_FLAG = 0;
    231 }
    232 }
    233 else { /*Sun Nov 6 08:49:37 1994*/
    234 rstr = strptime(since, "%a %b %d %T %Y", &since_tm);
    235 if(rstr == NULL) {
    236 COND_FLAG = 0;
    237 }
    238 }
    239 if (COND_FLAG) {
    240 since_tt = mktime(&since_tm);
    241 if (time(NULL) < since_tt)
    242 COND_FLAG = 0; /*this is not the future!*/
    243 }
    244 }
    245
    246 /*Connection*/
    247 else if(strcmp(line, "Connection") == 0)
    248 connection = strtok(NULL, " ");
    249
    250 /*unknown*/
    251 else
    252 continue;
    253 }
    254
    255 /*if protocol >= HTTP/1.1 Host must be specified*/
    256 if ((major == 1 && minor >= 1) || major > 1)
    257 if (host == NULL) {
    258 rcode = C_BADREQUEST;
    259 }
    260 }
    261 else
    262 rcode = C_NOTIMPLEMENTED;
    263
    264
    265 /*build Header*/
    266
    267 /*get current time*/
    268 now = time(NULL);
    269
    270 /*message type*/
    271 if (rcode == C_OK) {
    272 /*get size and last modified date*/
    273 fstat(file_des, &st);
    274
    275
    276 if (COND_FLAG) {
    277 /* convert file time to a time_t value for comparison*/
    278 /*this way of doing is lame but it works*/
    279 strftime(temp, MAXLEN-1, "%a, %d %b %Y %T GMT%n",
    280 gmtime(&st.st_mtimespec.tv_sec));
    281 strptime(temp, "%a, %d %b %Y %T GMT", &file_tm);
    282 file_tt = mktime(&file_tm);
    283
    284 /*compare*/
    285 if(file_tt <= since_tt) { /*the file has not been modified*/
    286 rcode = C_NOTMOD;
    287 fsize = 0;
    288 }
    289 }
    290 }
    291
    292 switch(rcode) {
    293 case C_OK:
    294 fsize = st.st_size;
    295 sprintf(headers, "HTTP/1.1 %d OK\n", C_OK);
    296 break;
    297 case C_NOTMOD:
    298 sprintf(headers, "HTTP/1.1 %d Not Modified\n", C_NOTMOD);
    299 break;
    300 case C_FORBIDDEN:
    301 strcpy(rbuf, R_FORBIDDEN);
    302 sprintf(headers, "HTTP/1.1 %d Forbidden\n", C_FORBIDDEN);
    303 break;
    304 case C_NOTFOUND:
    305 strcpy(rbuf, R_NOTFOUND);
    306 sprintf(headers, "HTTP/1.1 %d Not Found\n", C_NOTFOUND);
    307 break;
    308 case C_NOTIMPLEMENTED:
    309 strcpy(rbuf, R_NOTIMPLEMENTED);
    310 sprintf(headers, "HTTP/1.1 %d Not Implemented\n",
    311 C_NOTIMPLEMENTED);
    312 break;
    313 case C_BADREQUEST:
    314 strcpy(rbuf, R_BADREQUEST);
    315 sprintf(headers, "HTTP/1.1 %d Bad Request\n", C_BADREQUEST);
    316 break;
    317 default: /*internal error*/
    318 strcpy(rbuf, R_INTERNAL);
    319 sprintf(headers, "HTTP/1.1 %d Internal Server Error\n",
    320 C_INTERNAL);
    321 break;
    322 }
    323
    324 if(rcode != C_OK && rcode != C_NOTMOD) {
    325 fsize = strlen(rbuf);
    326 ret = -1;
    327 }
    328
    329
    330
    331 /*example: Date: Mon, 05 May 2008 10:06:01 GMT*/
    332 strcat(headers, "Date: ");
    333 strftime(temp, MAXLEN-1, "%a, %d %b %Y %T GMT%n", gmtime(&now));
    334 strcat(headers, temp);
    335
    336 /*example: Server: Apache/2.2.8 (Unix) [...]*/
    337 strcat(headers, "Server: ");
    338 strcat(headers, SERVER);
    339
    340 if (rcode == C_OK) {
    341 /*example: Last-Modified: Mon, 05 May 2008 10:06:01 GMT*/
    342 strcat(headers, "Last-Modified: ");
    343 strftime(temp, MAXLEN-1, "%a, %d %b %Y %T GMT%n",
    344 gmtime(&st.st_mtimespec.tv_sec));
    345 strcat(headers, temp);
    346 }
    347
    348 /*example: Content-Length: 5754 */
    349 strcat(headers, "Content-Length: ");
    350 sprintf(temp, "%lu\n", (unsigned long) fsize);
    351 strcat(headers, temp);
    352
    353 /*TODO: example: Content-Type: text/html*/
    354
    355 /*add a newline*/
    356 strcat(headers, "\n");
    357
    358
    359 /*send header fields and data*/
    360 printf("++++++++++++++++++++++ REPLY +++++++++++++++++++++++++++\n");
    361 printf("header:\n%s", headers);
    362 send(sockfd, headers, strlen(headers), 0);
    363 if (rcode == C_OK)
    364 while ((rbytes = read(file_des, rbuf, MAXLEN-1)) > 0)
    365 send(sockfd, rbuf, rbytes, 0);
    366 else
    367 send(sockfd, rbuf, fsize, 0);
    368 printf("message:\n%s\n", rbuf);
    369 printf("---------------------- REPLY ---------------------------\n");
    370
    371
    372 if (connection != NULL)
    373 if (strncmp(connection, "close", 5) == 0)
    374 ret = -1; /*client asked to close the connection*/
    375
    376 /*free dynamically allocated memory*/
    377 for (ii=0; ii < argc; ii++)
    378 free(args[ii]);
    379
    380
    381 return ret;
    382 }
    383
    384
    385 int main(int argc, char** argv){
    386
    387 int sockfd, newsockfd;
    388 unsigned int cli_size;
    389 struct sockaddr_in serv_addr, cli_addr;
    390 int childpid;
    391 int opt, long_index;
    392 char buf[MAXLEN], request[MAXLEN];
    393 int bytes, ret;
    394
    395 static const char *opt_string = "hp:r:";
    396 static const struct option long_opts[] = {
    397 {"Port", required_argument, NULL, 'p'},
    398 {"Root", required_argument, NULL, 'r'},
    399 {"help", no_argument, NULL, 'h'},
    400 {NULL, no_argument, NULL, 0}
    401 };
    402
    403 /*set defaults*/
    404 port = DEFAULT_PORT;
    405 root = DEFAULT_ROOT;
    406
    407 /*command-line arguments*/
    408 while ((opt = getopt_long(argc, argv, opt_string, long_opts, &long_index))
    409 != -1) {
    410 switch (opt) {
    411 case 'p':
    412 port = atoi(optarg);
    413 break;
    414
    415 case 'r':
    416 root = optarg;
    417 break;
    418
    419 case 'h':
    420 //display_usage();
    421 break;
    422
    423 case '?':
    424 break;
    425
    426 default:
    427 exit(0);
    428 break;
    429 }
    430 }
    431
    432
    433
    434 /*TODO: read a config file, close the config file*/
    435 /*TODO: if root, map UID/GID of low-priviledge acount*/
    436
    437 /*open a TCP socket*/
    438 if ((sockfd = socket(AF_INET,SOCK_STREAM,0)) < 0) {
    439 perror("socket()");
    440 exit(0);
    441 }
    442 /*bind the local address so that a client can connect*/
    443 bzero((char*)&serv_addr,sizeof(serv_addr));
    444 serv_addr.sin_family = AF_INET;
    445 serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    446 /*server TCP port must be network byte ordered */
    447 serv_addr.sin_port = htons(port);
    448
    449 if (bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
    450 perror("bind()");
    451 exit(0);
    452 }
    453
    454 /*TODO: connect to syslogd*/
    455
    456 /*preload (possibly) dynamically lodaded objects*/
    457 gethostbyname("localhost");
    458
    459 /*change directory*/
    460 if(chdir(root) < 0) {
    461 perror("chdir()");
    462 exit(0);
    463 }
    464 if(geteuid() == 0) { /*root can use chroot*/
    465 if(chroot("./") < 0) {
    466 perror("chroot()");
    467 exit(0);
    468 }
    469 }
    470
    471
    472 /*TODO: if root, change to predefined low-priv user via setXXuid
    473 cf "Setuid Demystified", Chen & Wagner, Usenix 2002*/
    474
    475 listen(sockfd, BACKLOG);
    476
    477 /*main loop*/
    478 while(1) {
    479 /*wait for a connection request (concurrent server)*/
    480 cli_size = sizeof(cli_addr);
    481 newsockfd = accept(sockfd, (struct sockaddr *)&cli_addr, &cli_size);
    482
    483 if (newsockfd < 0) {
    484 perror("accept()");
    485 exit(0);
    486 }
    487
    488 /*fork the server to handle the connections*/
    489 if ((childpid = fork()) < 0) {
    490 perror("fork()");
    491 exit(0);
    492 }
    493 else if (childpid == 0) { /*child process - process client requests*/
    494 close(sockfd);
    495
    496 ret = 0;
    497 while (ret >= 0) {
    498 request[0] = '\0';
    499 while ((strstr(request, TERMINATOR1) == NULL) &&
    500 (strstr(request, TERMINATOR2) == NULL)) {
    501 bytes = recv(newsockfd, buf, MAXLEN-1, 0);
    502 buf[bytes] = '\0';
    503 strncat(request, buf, bytes);
    504 }
    505
    506 ret = process_request(newsockfd, request, strlen(request));
    507 }
    508
    509 exit(0);
    510 }
    511 else { /*parent process - waits the child process to terminate*/
    512 if (waitpid(childpid, NULL, 0) != childpid)
    513 perror("waitpid");
    514 close(newsockfd);
    515 }
    516 }
    517 }
    Posted by lrei to c unix network ... saved by 2 persons ... 0 comments ... 1 month, 3 weeks
showing 10, 25, 50 items per pages

Pages : 1

Flux RSS friendsnippetLatest snippets


More...