| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866 |
- /*
- * WebSocket lib with support for "wss://" encryption.
- * Copyright 2010 Joel Martin
- * Licensed under LGPL version 3 (see docs/LICENSE.LGPL-3)
- *
- * You can make a cert/key with openssl using:
- * openssl req -new -x509 -days 365 -nodes -out self.pem -keyout self.pem
- * as taken from http://docs.python.org/dev/library/ssl.html#certificates
- */
- #include <unistd.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <errno.h>
- #include <strings.h>
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <sys/stat.h>
- #include <netinet/in.h>
- #include <arpa/inet.h>
- #include <netdb.h>
- #include <signal.h> // daemonizing
- #include <fcntl.h> // daemonizing
- #include <openssl/err.h>
- #include <openssl/ssl.h>
- #include <openssl/md5.h> /* md5 hash */
- #include <openssl/sha.h> /* sha1 hash */
- #include "websocket.h"
-
- int ws_b64_ntop(u_char const *src, size_t srclength, char *target, size_t targsize);
- int ws_b64_pton(char const *src, u_char *target, size_t targsize);
-
- /*
- * Global state
- *
- * Warning: not thread safe
- */
- int ssl_initialized = 0;
- int pipe_error = 0;
- settings_t settings;
-
-
- void traffic(const char * token) {
- if ((settings.verbose) && (! settings.daemon)) {
- fprintf(stdout, "%s", token);
- fflush(stdout);
- }
- }
-
- void error(char *msg)
- {
- perror(msg);
- }
-
- void fatal(char *msg)
- {
- perror(msg);
- exit(1);
- }
-
- /* resolve host with also IP address parsing */
- int resolve_host(struct in_addr *sin_addr, const char *hostname)
- {
- if (!inet_aton(hostname, sin_addr)) {
- struct addrinfo *ai, *cur;
- struct addrinfo hints;
- memset(&hints, 0, sizeof(hints));
- hints.ai_family = AF_INET;
- if (getaddrinfo(hostname, NULL, &hints, &ai))
- return -1;
- for (cur = ai; cur; cur = cur->ai_next) {
- if (cur->ai_family == AF_INET) {
- *sin_addr = ((struct sockaddr_in *)cur->ai_addr)->sin_addr;
- freeaddrinfo(ai);
- return 0;
- }
- }
- freeaddrinfo(ai);
- return -1;
- }
- return 0;
- }
-
-
- /*
- * SSL Wrapper Code
- */
-
- ssize_t ws_recv(ws_ctx_t *ctx, void *buf, size_t len) {
- if (ctx->ssl) {
- //handler_msg("SSL recv\n");
- return SSL_read(ctx->ssl, buf, len);
- } else {
- return recv(ctx->sockfd, buf, len, 0);
- }
- }
-
- ssize_t ws_send(ws_ctx_t *ctx, const void *buf, size_t len) {
- if (ctx->ssl) {
- //handler_msg("SSL send\n");
- return SSL_write(ctx->ssl, buf, len);
- } else {
- return send(ctx->sockfd, buf, len, 0);
- }
- }
-
- ws_ctx_t *alloc_ws_ctx() {
- ws_ctx_t *ctx;
- if (! (ctx = malloc(sizeof(ws_ctx_t))) )
- { fatal("malloc()"); }
-
- if (! (ctx->cin_buf = malloc(BUFSIZE)) )
- { fatal("malloc of cin_buf"); }
- if (! (ctx->cout_buf = malloc(BUFSIZE)) )
- { fatal("malloc of cout_buf"); }
- if (! (ctx->tin_buf = malloc(BUFSIZE)) )
- { fatal("malloc of tin_buf"); }
- if (! (ctx->tout_buf = malloc(BUFSIZE)) )
- { fatal("malloc of tout_buf"); }
-
- ctx->headers = malloc(sizeof(headers_t));
- ctx->ssl = NULL;
- ctx->ssl_ctx = NULL;
- return ctx;
- }
-
- void free_ws_ctx(ws_ctx_t *ctx) {
- free(ctx->cin_buf);
- free(ctx->cout_buf);
- free(ctx->tin_buf);
- free(ctx->tout_buf);
- free(ctx);
- }
-
- ws_ctx_t *ws_socket(ws_ctx_t *ctx, int socket) {
- ctx->sockfd = socket;
- return ctx;
- }
-
- ws_ctx_t *ws_socket_ssl(ws_ctx_t *ctx, int socket, char * certfile, char * keyfile) {
- int ret;
- char msg[1024];
- char * use_keyfile;
- ws_socket(ctx, socket);
-
- if (keyfile && (keyfile[0] != '\0')) {
- // Separate key file
- use_keyfile = keyfile;
- } else {
- // Combined key and cert file
- use_keyfile = certfile;
- }
-
- // Initialize the library
- if (! ssl_initialized) {
- SSL_library_init();
- OpenSSL_add_all_algorithms();
- SSL_load_error_strings();
- ssl_initialized = 1;
-
- }
-
- ctx->ssl_ctx = SSL_CTX_new(TLSv1_server_method());
- if (ctx->ssl_ctx == NULL) {
- ERR_print_errors_fp(stderr);
- fatal("Failed to configure SSL context");
- }
-
- if (SSL_CTX_use_PrivateKey_file(ctx->ssl_ctx, use_keyfile,
- SSL_FILETYPE_PEM) <= 0) {
- sprintf(msg, "Unable to load private key file %s\n", use_keyfile);
- fatal(msg);
- }
-
- if (SSL_CTX_use_certificate_file(ctx->ssl_ctx, certfile,
- SSL_FILETYPE_PEM) <= 0) {
- sprintf(msg, "Unable to load certificate file %s\n", certfile);
- fatal(msg);
- }
-
- // if (SSL_CTX_set_cipher_list(ctx->ssl_ctx, "DEFAULT") != 1) {
- // sprintf(msg, "Unable to set cipher\n");
- // fatal(msg);
- // }
-
- // Associate socket and ssl object
- ctx->ssl = SSL_new(ctx->ssl_ctx);
- SSL_set_fd(ctx->ssl, socket);
-
- ret = SSL_accept(ctx->ssl);
- if (ret < 0) {
- ERR_print_errors_fp(stderr);
- return NULL;
- }
-
- return ctx;
- }
-
- void ws_socket_free(ws_ctx_t *ctx) {
- if (ctx->ssl) {
- SSL_free(ctx->ssl);
- ctx->ssl = NULL;
- }
- if (ctx->ssl_ctx) {
- SSL_CTX_free(ctx->ssl_ctx);
- ctx->ssl_ctx = NULL;
- }
- if (ctx->sockfd) {
- shutdown(ctx->sockfd, SHUT_RDWR);
- close(ctx->sockfd);
- ctx->sockfd = 0;
- }
- }
-
- /* ------------------------------------------------------- */
-
-
- int encode_hixie(u_char const *src, size_t srclength,
- char *target, size_t targsize) {
- int sz = 0, len = 0;
- target[sz++] = '\x00';
- len = ws_b64_ntop(src, srclength, target+sz, targsize-sz);
- if (len < 0) {
- return len;
- }
- sz += len;
- target[sz++] = '\xff';
- return sz;
- }
-
- int decode_hixie(char *src, size_t srclength,
- u_char *target, size_t targsize,
- unsigned int *opcode, unsigned int *left) {
- char *start, *end, cntstr[4];
- int i, len, framecount = 0, retlen = 0;
- unsigned char chr;
- if ((src[0] != '\x00') || (src[srclength-1] != '\xff')) {
- handler_emsg("WebSocket framing error\n");
- return -1;
- }
- *left = srclength;
-
- if (srclength == 2 &&
- (src[0] == '\xff') &&
- (src[1] == '\x00')) {
- // client sent orderly close frame
- *opcode = 0x8; // Close frame
- return 0;
- }
- *opcode = 0x1; // Text frame
-
- start = src+1; // Skip '\x00' start
- do {
- /* We may have more than one frame */
- end = (char *)memchr(start, '\xff', srclength);
- *end = '\x00';
- len = ws_b64_pton(start, target+retlen, targsize-retlen);
- if (len < 0) {
- return len;
- }
- retlen += len;
- start = end + 2; // Skip '\xff' end and '\x00' start
- framecount++;
- } while (end < (src+srclength-1));
- if (framecount > 1) {
- snprintf(cntstr, 3, "%d", framecount);
- traffic(cntstr);
- }
- *left = 0;
- return retlen;
- }
-
- int encode_hybi(u_char const *src, size_t srclength,
- char *target, size_t targsize, unsigned int opcode)
- {
- unsigned long long b64_sz, len_offset = 1, payload_offset = 2;
- int len = 0;
-
- if (opcode != OPCODE_TEXT && opcode != OPCODE_BINARY) {
- handler_emsg("Invalid opcode. Opcode must be 0x01 for text mode, or 0x02 for binary mode.\n");
- return -1;
- }
-
- target[0] = (char)((opcode & 0x0F) | 0x80);
-
- if ((int)srclength <= 0) {
- return 0;
- }
-
- if (opcode & OPCODE_TEXT) {
- len = ((srclength - 1) / 3) * 4 + 4;
- } else {
- len = srclength;
- }
-
- if (len <= 125) {
- target[1] = (char) len;
- payload_offset = 2;
- } else if ((len > 125) && (len < 65536)) {
- target[1] = (char) 126;
- *(u_short*)&(target[2]) = htons(len);
- payload_offset = 4;
- } else {
- handler_emsg("Sending frames larger than 65535 bytes not supported\n");
- return -1;
- //target[1] = (char) 127;
- //*(u_long*)&(target[2]) = htonl(b64_sz);
- //payload_offset = 10;
- }
-
- if (opcode & OPCODE_TEXT) {
- len = ws_b64_ntop(src, srclength, target+payload_offset, targsize-payload_offset);
- } else {
- memcpy(target+payload_offset, src, srclength);
- len = srclength;
- }
-
- if (len < 0) {
- return len;
- }
-
- return len + payload_offset;
- }
-
- int decode_hybi(unsigned char *src, size_t srclength,
- u_char *target, size_t targsize,
- unsigned int *opcode, unsigned int *left)
- {
- unsigned char *frame, *mask, *payload, save_char;
- char cntstr[4];
- int masked = 0;
- int i = 0, len, framecount = 0;
- size_t remaining;
- unsigned int target_offset = 0, hdr_length = 0, payload_length = 0;
-
- *left = srclength;
- frame = src;
-
- //printf("Deocde new frame\n");
- while (1) {
- // Need at least two bytes of the header
- // Find beginning of next frame. First time hdr_length, masked and
- // payload_length are zero
- frame += hdr_length + 4*masked + payload_length;
- //printf("frame[0..3]: 0x%x 0x%x 0x%x 0x%x (tot: %d)\n",
- // (unsigned char) frame[0],
- // (unsigned char) frame[1],
- // (unsigned char) frame[2],
- // (unsigned char) frame[3], srclength);
-
- if (frame > src + srclength) {
- //printf("Truncated frame from client, need %d more bytes\n", frame - (src + srclength) );
- break;
- }
- remaining = (src + srclength) - frame;
- if (remaining < 2) {
- //printf("Truncated frame header from client\n");
- break;
- }
- framecount ++;
-
- *opcode = frame[0] & 0x0f;
- masked = (frame[1] & 0x80) >> 7;
-
- if (*opcode == 0x8) {
- // client sent orderly close frame
- break;
- }
-
- payload_length = frame[1] & 0x7f;
- if (payload_length < 126) {
- hdr_length = 2;
- //frame += 2 * sizeof(char);
- } else if (payload_length == 126) {
- payload_length = (frame[2] << 8) + frame[3];
- hdr_length = 4;
- } else {
- handler_emsg("Receiving frames larger than 65535 bytes not supported\n");
- return -1;
- }
- if ((hdr_length + 4*masked + payload_length) > remaining) {
- continue;
- }
- //printf(" payload_length: %u, raw remaining: %u\n", payload_length, remaining);
- payload = frame + hdr_length + 4*masked;
-
- if (*opcode != OPCODE_TEXT && *opcode != OPCODE_BINARY) {
- handler_msg("Ignoring non-data frame, opcode 0x%x\n", *opcode);
- continue;
- }
-
- if (payload_length == 0) {
- handler_msg("Ignoring empty frame\n");
- continue;
- }
-
- if ((payload_length > 0) && (!masked)) {
- handler_emsg("Received unmasked payload from client\n");
- return -1;
- }
-
- // Terminate with a null for base64 decode
- save_char = payload[payload_length];
- payload[payload_length] = '\0';
-
- // unmask the data
- mask = payload - 4;
- for (i = 0; i < payload_length; i++) {
- payload[i] ^= mask[i%4];
- }
-
- if (*opcode & OPCODE_TEXT) {
- // base64 decode the data
- len = ws_b64_pton((const char*)payload, target+target_offset, targsize);
- } else {
- memcpy(target+target_offset, payload, payload_length);
- len = payload_length;
- }
-
- // Restore the first character of the next frame
- payload[payload_length] = save_char;
- if (len < 0) {
- handler_emsg("Base64 decode error code %d", len);
- return len;
- }
- target_offset += len;
-
- //printf(" len %d, raw %s\n", len, frame);
- }
-
- if (framecount > 1) {
- snprintf(cntstr, 3, "%d", framecount);
- traffic(cntstr);
- }
-
- *left = remaining;
- return target_offset;
- }
-
-
-
- int parse_handshake(ws_ctx_t *ws_ctx, char *handshake) {
- char *start, *end;
- headers_t *headers = ws_ctx->headers;
-
- headers->key1[0] = '\0';
- headers->key2[0] = '\0';
- headers->key3[0] = '\0';
-
- if ((strlen(handshake) < 92) || (bcmp(handshake, "GET ", 4) != 0)) {
- return 0;
- }
- start = handshake+4;
- end = strstr(start, " HTTP/1.1");
- if (!end) { return 0; }
- strncpy(headers->path, start, end-start);
- headers->path[end-start] = '\0';
-
- start = strstr(handshake, "\r\nHost: ");
- if (!start) { return 0; }
- start += 8;
- end = strstr(start, "\r\n");
- strncpy(headers->host, start, end-start);
- headers->host[end-start] = '\0';
-
- headers->origin[0] = '\0';
- start = strstr(handshake, "\r\nOrigin: ");
- if (start) {
- start += 10;
- } else {
- start = strstr(handshake, "\r\nSec-WebSocket-Origin: ");
- if (!start) { return 0; }
- start += 24;
- }
- end = strstr(start, "\r\n");
- strncpy(headers->origin, start, end-start);
- headers->origin[end-start] = '\0';
-
- start = strstr(handshake, "\r\nSec-WebSocket-Version: ");
- if (start) {
- // HyBi/RFC 6455
- start += 25;
- end = strstr(start, "\r\n");
- strncpy(headers->version, start, end-start);
- headers->version[end-start] = '\0';
- ws_ctx->hixie = 0;
- ws_ctx->hybi = strtol(headers->version, NULL, 10);
-
- start = strstr(handshake, "\r\nSec-WebSocket-Key: ");
- if (!start) { return 0; }
- start += 21;
- end = strstr(start, "\r\n");
- strncpy(headers->key1, start, end-start);
- headers->key1[end-start] = '\0';
-
- start = strstr(handshake, "\r\nConnection: ");
- if (!start) { return 0; }
- start += 14;
- end = strstr(start, "\r\n");
- strncpy(headers->connection, start, end-start);
- headers->connection[end-start] = '\0';
-
- start = strstr(handshake, "\r\nSec-WebSocket-Protocol: ");
- if (!start) { return 0; }
- start += 26;
- end = strstr(start, "\r\n");
- strncpy(headers->protocols, start, end-start);
- headers->protocols[end-start] = '\0';
- } else {
- // Hixie 75 or 76
- ws_ctx->hybi = 0;
-
- start = strstr(handshake, "\r\n\r\n");
- if (!start) { return 0; }
- start += 4;
- if (strlen(start) == 8) {
- ws_ctx->hixie = 76;
- strncpy(headers->key3, start, 8);
- headers->key3[8] = '\0';
-
- start = strstr(handshake, "\r\nSec-WebSocket-Key1: ");
- if (!start) { return 0; }
- start += 22;
- end = strstr(start, "\r\n");
- strncpy(headers->key1, start, end-start);
- headers->key1[end-start] = '\0';
-
- start = strstr(handshake, "\r\nSec-WebSocket-Key2: ");
- if (!start) { return 0; }
- start += 22;
- end = strstr(start, "\r\n");
- strncpy(headers->key2, start, end-start);
- headers->key2[end-start] = '\0';
- } else {
- ws_ctx->hixie = 75;
- }
-
- }
-
- return 1;
- }
-
- int parse_hixie76_key(char * key) {
- unsigned long i, spaces = 0, num = 0;
- for (i=0; i < strlen(key); i++) {
- if (key[i] == ' ') {
- spaces += 1;
- }
- if ((key[i] >= 48) && (key[i] <= 57)) {
- num = num * 10 + (key[i] - 48);
- }
- }
- return num / spaces;
- }
-
- int gen_md5(headers_t *headers, char *target) {
- unsigned long key1 = parse_hixie76_key(headers->key1);
- unsigned long key2 = parse_hixie76_key(headers->key2);
- char *key3 = headers->key3;
-
- MD5_CTX c;
- char in[HIXIE_MD5_DIGEST_LENGTH] = {
- key1 >> 24, key1 >> 16, key1 >> 8, key1,
- key2 >> 24, key2 >> 16, key2 >> 8, key2,
- key3[0], key3[1], key3[2], key3[3],
- key3[4], key3[5], key3[6], key3[7]
- };
-
- MD5_Init(&c);
- MD5_Update(&c, (void *)in, sizeof in);
- MD5_Final((void *)target, &c);
-
- target[HIXIE_MD5_DIGEST_LENGTH] = '\0';
-
- return 1;
- }
-
- static void gen_sha1(headers_t *headers, char *target) {
- SHA_CTX c;
- unsigned char hash[SHA_DIGEST_LENGTH];
- int r;
-
- SHA1_Init(&c);
- SHA1_Update(&c, headers->key1, strlen(headers->key1));
- SHA1_Update(&c, HYBI_GUID, 36);
- SHA1_Final(hash, &c);
-
- r = ws_b64_ntop(hash, sizeof hash, target, HYBI10_ACCEPTHDRLEN);
- //assert(r == HYBI10_ACCEPTHDRLEN - 1);
- }
-
-
- ws_ctx_t *do_handshake(int sock) {
- char handshake[4096], response[4096], sha1[29], trailer[17];
- char *scheme, *pre;
- headers_t *headers;
- int len, ret, i, offset;
- ws_ctx_t * ws_ctx;
- char *response_protocol;
-
- // Peek, but don't read the data
- len = recv(sock, handshake, 1024, MSG_PEEK);
- handshake[len] = 0;
- if (len == 0) {
- handler_msg("ignoring empty handshake\n");
- return NULL;
- } else if ((bcmp(handshake, "\x16", 1) == 0) ||
- (bcmp(handshake, "\x80", 1) == 0)) {
- // SSL
- if (!settings.cert) {
- handler_msg("SSL connection but no cert specified\n");
- return NULL;
- } else if (access(settings.cert, R_OK) != 0) {
- handler_msg("SSL connection but '%s' not found\n",
- settings.cert);
- return NULL;
- }
- ws_ctx = alloc_ws_ctx();
- ws_socket_ssl(ws_ctx, sock, settings.cert, settings.key);
- if (! ws_ctx) { return NULL; }
- scheme = "wss";
- handler_msg("using SSL socket\n");
- } else if (settings.ssl_only) {
- handler_msg("non-SSL connection disallowed\n");
- return NULL;
- } else {
- ws_ctx = alloc_ws_ctx();
- ws_socket(ws_ctx, sock);
- if (! ws_ctx) { return NULL; }
- scheme = "ws";
- handler_msg("using plain (not SSL) socket\n");
- }
- offset = 0;
- for (i = 0; i < 10; i++) {
- /* (offset + 1): reserve one byte for the trailing '\0' */
- if (0 > (len = ws_recv(ws_ctx, handshake + offset, sizeof(handshake) - (offset + 1)))) {
- handler_emsg("Read error during handshake: %m\n");
- free_ws_ctx(ws_ctx);
- return NULL;
- } else if (0 == len) {
- handler_emsg("Client closed during handshake\n");
- free_ws_ctx(ws_ctx);
- return NULL;
- }
- offset += len;
- handshake[offset] = 0;
- if (strstr(handshake, "\r\n\r\n")) {
- break;
- } else if (sizeof(handshake) <= (size_t)(offset + 1)) {
- handler_emsg("Oversized handshake\n");
- free_ws_ctx(ws_ctx);
- return NULL;
- } else if (9 == i) {
- handler_emsg("Incomplete handshake\n");
- free_ws_ctx(ws_ctx);
- return NULL;
- }
- usleep(10);
- }
-
- //handler_msg("handshake: %s\n", handshake);
- if (!parse_handshake(ws_ctx, handshake)) {
- handler_emsg("Invalid WS request\n");
- free_ws_ctx(ws_ctx);
- return NULL;
- }
-
- headers = ws_ctx->headers;
-
- if (strstr(headers->protocols, "binary")) {
- ws_ctx->opcode = OPCODE_BINARY;
- response_protocol = "binary";
- } else if (strstr(headers->protocols, "base64")) {
- ws_ctx->opcode = OPCODE_TEXT;
- response_protocol = "base64";
- } else {
- handler_emsg("Invalid protocol '%s', expecting 'binary' or 'base64'\n",
- headers->protocols);
- return NULL;
- }
-
- if (ws_ctx->hybi > 0) {
- handler_msg("using protocol HyBi/IETF 6455 %d\n", ws_ctx->hybi);
- gen_sha1(headers, sha1);
- sprintf(response, SERVER_HANDSHAKE_HYBI, sha1, response_protocol);
- } else {
- if (ws_ctx->hixie == 76) {
- handler_msg("using protocol Hixie 76\n");
- gen_md5(headers, trailer);
- pre = "Sec-";
- } else {
- handler_msg("using protocol Hixie 75\n");
- trailer[0] = '\0';
- pre = "";
- }
- sprintf(response, SERVER_HANDSHAKE_HIXIE, pre, headers->origin, pre, scheme,
- headers->host, headers->path, pre, "base64", trailer);
- }
-
- //handler_msg("response: %s\n", response);
- ws_send(ws_ctx, response, strlen(response));
-
- return ws_ctx;
- }
-
- void signal_handler(int sig) {
- switch (sig) {
- case SIGHUP:
- if (settings.whitelist != NULL) {
- load_whitelist();
- }
- break;
- case SIGPIPE: pipe_error = 1; break; // handle inline
- case SIGTERM:
- remove(settings.pid);
- exit(0);
- break;
- }
- }
-
- void daemonize(int keepfd) {
- int pid, i;
-
- umask(0);
- chdir("/");
- setgid(getgid());
- setuid(getuid());
-
- /* Double fork to daemonize */
- pid = fork();
- if (pid<0) { fatal("fork error"); }
- if (pid>0) { exit(0); } // parent exits
- setsid(); // Obtain new process group
- pid = fork();
- if (pid<0) { fatal("fork error"); }
- if (pid>0) {
- // parent exits
- FILE *pidf = fopen(settings.pid, "w");
- if (pidf) {
- fprintf(pidf, "%d", pid);
- fclose(pidf);
- } else {
- fprintf(stderr, "Could not write daemon PID file '%s': %s\n", settings.pid, strerror(errno));
- }
- exit(0);
- }
-
- /* Signal handling */
- signal(SIGHUP, signal_handler); // catch HUP
- signal(SIGTERM, signal_handler); // catch kill
-
- /* Close open files */
- for (i=getdtablesize(); i>=0; --i) {
- if (i != keepfd) {
- close(i);
- } else if (settings.verbose) {
- printf("keeping fd %d\n", keepfd);
- }
- }
- i=open("/dev/null", O_RDWR); // Redirect stdin
- dup(i); // Redirect stdout
- dup(i); // Redirect stderr
- }
-
-
- void start_server() {
- int lsock, csock, pid, sopt = 1, i;
- struct sockaddr_in serv_addr, cli_addr;
- socklen_t clilen;
- ws_ctx_t *ws_ctx;
-
-
- /* Initialize buffers */
- lsock = socket(AF_INET, SOCK_STREAM, 0);
- if (lsock < 0) { error("ERROR creating listener socket"); }
- bzero((char *) &serv_addr, sizeof(serv_addr));
- serv_addr.sin_family = AF_INET;
- serv_addr.sin_port = htons(settings.listen_port);
-
- /* Resolve listen address */
- if (settings.listen_host && (settings.listen_host[0] != '\0')) {
- if (resolve_host(&serv_addr.sin_addr, settings.listen_host) < -1) {
- fatal("Could not resolve listen address");
- }
- } else {
- serv_addr.sin_addr.s_addr = INADDR_ANY;
- }
-
- setsockopt(lsock, SOL_SOCKET, SO_REUSEADDR, (char *)&sopt, sizeof(sopt));
- if (bind(lsock, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
- fatal("ERROR on binding listener socket");
- }
- listen(lsock,100);
-
- signal(SIGPIPE, signal_handler); // catch pipe
-
- if (settings.daemon) {
- daemonize(lsock);
- }
-
-
- // Reep zombies
- signal(SIGCHLD, SIG_IGN);
-
- printf("Waiting for connections on %s:%d\n",
- settings.listen_host, settings.listen_port);
-
- while (1) {
- clilen = sizeof(cli_addr);
- pipe_error = 0;
- pid = 0;
- csock = accept(lsock,
- (struct sockaddr *) &cli_addr,
- &clilen);
- if (csock < 0) {
- error("ERROR on accept");
- continue;
- }
- handler_msg("got client connection from %s\n",
- inet_ntoa(cli_addr.sin_addr));
-
- if (!settings.run_once) {
- handler_msg("forking handler process\n");
- pid = fork();
- }
-
- if (pid == 0) { // handler process
- ws_ctx = do_handshake(csock);
- if (settings.run_once) {
- if (ws_ctx == NULL) {
- // Not a real WebSocket connection
- continue;
- } else {
- // Successful connection, stop listening for new
- // connections
- close(lsock);
- }
- }
- if (ws_ctx == NULL) {
- handler_msg("No connection after handshake\n");
- break; // Child process exits
- }
-
- settings.handler(ws_ctx);
- if (pipe_error) {
- handler_emsg("Closing due to SIGPIPE\n");
- }
- break; // Child process exits
- } else { // parent process
- settings.handler_id += 1;
- }
- }
- if (pid == 0) {
- if (ws_ctx) {
- ws_socket_free(ws_ctx);
- free_ws_ctx(ws_ctx);
- } else {
- shutdown(csock, SHUT_RDWR);
- close(csock);
- }
- handler_msg("handler exit\n");
- } else {
- handler_msg("websockify exit\n");
- }
-
- }
|