From: Michael J. Pounders Date: Mon, 22 Sep 2014 01:50:44 +0000 (-0400) Subject: update X-Git-Url: http://git.code-monkey.de/?p=umurmur.git;a=commitdiff_plain;h=610a57b7036f0868102952062bcda45b56d80a59 update shm_utils/mon-umurmurd/README.md shm_utils/umurmurd-websocket/README.md --- diff --git a/shm_utils/mon-umurmurd/README.md b/shm_utils/mon-umurmurd/README.md new file mode 100644 index 0000000..9d0d519 --- /dev/null +++ b/shm_utils/mon-umurmurd/README.md @@ -0,0 +1,7 @@ +mon-umurmurd +============ + +outputs server/client data to term useing the sharedmemory API added to umurmur for testing + +make +./mon-umurmurd \ No newline at end of file diff --git a/shm_utils/umurmurd-websocket/README.md b/shm_utils/umurmurd-websocket/README.md new file mode 100644 index 0000000..fa2611c --- /dev/null +++ b/shm_utils/umurmurd-websocket/README.md @@ -0,0 +1,29 @@ +uMurmurd-Websocket - HTTP/JSON server example +============================================= + +HTTP/JSON server in one. Useing a js prettyprinter type lib to display the output +It not the best looking but its just a demo. Hopeing someone else with better web +skills can make something better. + + +Instructions for building from source +------------------------------------- +1. Requirements + * [OpenSSL](http://www.openssl.org/) + * [libwebsockets](http://libwebsockets.org) library. I used this [tarball]([http://git.libwebsockets.org/cgi-bin/cgit/libwebsockets/snapshot/libwebsockets-1.23-chrome32-firefox24.tar.gz) + * [Jansson](www.digip.org/jansson/) library. I used this [tarball](http://www.digip.org/jansson/releases/jansson-2.6.tar.gz) Jansson is a C library for encoding, decoding and manipulating JSON data + * [CMake](http://cmake.org) + +2. Build - CMake + * Create a build folder and cd into it + * `cmake ../` + * `make` + +3. Run + * start umurmurd from this git like this ./umurmurd -d -c + - the -d option is just to make it easier to see debug info + * start uMurmurd-Websocket with ./uMurmurd-Websocket --ssl + - option --ssl is for https://, http:// otherwise + * point web browser to https://localhost:7681 + + \ No newline at end of file diff --git a/shm_utils/umurmurd-websocket/src/uMurmurd_Websocket.c b/shm_utils/umurmurd-websocket/src/uMurmurd_Websocket.c new file mode 100644 index 0000000..b5e7e69 --- /dev/null +++ b/shm_utils/umurmurd-websocket/src/uMurmurd_Websocket.c @@ -0,0 +1,632 @@ +/* + * uMurmurd Websocket server - HTTP/JSON serverexample + * + * Copyright (C) 2014 Michael P. Pounders <> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +#ifdef CMAKE_BUILD +#include "lws_config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#include +#include "../../../src/sharedmemory.h" + +int max_poll_elements; + +struct pollfd *pollfds; +int *fd_lookup; +int count_pollfds; +int force_exit = 0; + +enum demo_protocols { + /* always first */ + PROTOCOL_HTTP = 0, + + PROTOCOL_JSON_UMURMURD, + + /* always last */ + DEMO_PROTOCOL_COUNT +}; + + + +char *resource_path = "../web"; + +/* + * We take a strict whitelist approach to stop ../ attacks + */ + +struct serveable { + const char *urlpath; + const char *mimetype; +}; + +static const struct serveable whitelist[] = { + { "/favicon.ico", "image/x-icon" }, + { "/css/mon_umurmurd.css", "text/css" }, + { "/css/json.human.css", "text/css" }, + { "/js/crel.js", "text/javascript" }, + { "/js/json.human.js", "text/javascript" }, + { "/js/jquery.min.js", "text/javascript" }, + + /* last one is the default served if no match */ + { "/mon_umurmurd.html", "text/html" }, +}; + +struct per_session_data__http { + int fd; +}; + +/* this protocol server (always the first one) just knows how to do HTTP */ + +static int callback_http( struct libwebsocket_context *context, + struct libwebsocket *wsi, + enum libwebsocket_callback_reasons reason, void *user, + void *in, size_t len) +{ +#if 0 + char client_name[128]; + char client_ip[128]; +#endif + char buf[256]; + char leaf_path[1024]; + int n, m; + unsigned char *p; + static unsigned char buffer[4096]; + struct stat stat_buf; + struct per_session_data__http *pss = + (struct per_session_data__http *)user; + + switch (reason) { + case LWS_CALLBACK_HTTP: + + /* check for the "send a big file by hand" example case */ + + if (!strcmp((const char *)in, "/leaf.jpg")) { + if (strlen(resource_path) > sizeof(leaf_path) - 10) + return -1; + sprintf(leaf_path, "%s/leaf.jpg", resource_path); + + /* well, let's demonstrate how to send the hard way */ + + p = buffer; + + pss->fd = open(leaf_path, O_RDONLY); + + if (pss->fd < 0) + return -1; + + fstat(pss->fd, &stat_buf); + + /* + * we will send a big jpeg file, but it could be + * anything. Set the Content-Type: appropriately + * so the browser knows what to do with it. + */ + + p += sprintf((char *)p, + "HTTP/1.0 200 OK\x0d\x0a" + "Server: libwebsockets\x0d\x0a" + "Content-Type: image/jpeg\x0d\x0a" + "Content-Length: %u\x0d\x0a\x0d\x0a", + (unsigned int)stat_buf.st_size); + + /* + * send the http headers... + * this won't block since it's the first payload sent + * on the connection since it was established + * (too small for partial) + */ + + n = libwebsocket_write(wsi, buffer, + p - buffer, LWS_WRITE_HTTP); + + if (n < 0) { + close(pss->fd); + return -1; + } + /* + * book us a LWS_CALLBACK_HTTP_WRITEABLE callback + */ + libwebsocket_callback_on_writable(context, wsi); + break; + } + + /* if not, send a file the easy way */ + + for (n = 0; n < (sizeof(whitelist) / sizeof(whitelist[0]) - 1); n++) + if (in && strcmp((const char *)in, whitelist[n].urlpath) == 0) + break; + + sprintf(buf, "%s%s", resource_path, whitelist[n].urlpath); + + if (libwebsockets_serve_http_file(context, wsi, buf, whitelist[n].mimetype)) + return -1; /* through completion or error, close the socket */ + + /* + * notice that the sending of the file completes asynchronously, + * we'll get a LWS_CALLBACK_HTTP_FILE_COMPLETION callback when + * it's done + */ + + break; + + case LWS_CALLBACK_HTTP_FILE_COMPLETION: +// lwsl_info("LWS_CALLBACK_HTTP_FILE_COMPLETION seen\n"); + /* kill the connection after we sent one file */ + return -1; + + case LWS_CALLBACK_HTTP_WRITEABLE: + /* + * we can send more of whatever it is we were sending + */ + + do { + n = read(pss->fd, buffer, sizeof buffer); + /* problem reading, close conn */ + if (n < 0) + goto bail; + /* sent it all, close conn */ + if (n == 0) + goto bail; + /* + * because it's HTTP and not websocket, don't need to take + * care about pre and postamble + */ + m = libwebsocket_write(wsi, buffer, n, LWS_WRITE_HTTP); + if (m < 0) + /* write failed, close conn */ + goto bail; + if (m != n) + /* partial write, adjust */ + lseek(pss->fd, m - n, SEEK_CUR); + + } while (!lws_send_pipe_choked(wsi)); + libwebsocket_callback_on_writable(context, wsi); + break; + +bail: + close(pss->fd); + return -1; + + /* + * callback for confirming to continue with client IP appear in + * protocol 0 callback since no websocket protocol has been agreed + * yet. You can just ignore this if you won't filter on client IP + * since the default uhandled callback return is 0 meaning let the + * connection continue. + */ + + case LWS_CALLBACK_FILTER_NETWORK_CONNECTION: +#if 0 + libwebsockets_get_peer_addresses(context, wsi, (int)(long)in, client_name, + sizeof(client_name), client_ip, sizeof(client_ip)); + + fprintf(stderr, "Received network connect from %s (%s)\n", + client_name, client_ip); +#endif + /* if we returned non-zero from here, we kill the connection */ + break; + + default: + break; + } + + return 0; +} + +/* + * this is just an example of parsing handshake headers, you don't need this + * in your code unless you will filter allowing connections by the header + * content + */ + +static void +dump_handshake_info(struct libwebsocket *wsi) +{ + int n; + static const char *token_names[WSI_TOKEN_COUNT] = { + /*[WSI_TOKEN_GET_URI] =*/ "GET URI", + /*[WSI_TOKEN_HOST] =*/ "Host", + /*[WSI_TOKEN_CONNECTION] =*/ "Connection", + /*[WSI_TOKEN_KEY1] =*/ "key 1", + /*[WSI_TOKEN_KEY2] =*/ "key 2", + /*[WSI_TOKEN_PROTOCOL] =*/ "Protocol", + /*[WSI_TOKEN_UPGRADE] =*/ "Upgrade", + /*[WSI_TOKEN_ORIGIN] =*/ "Origin", + /*[WSI_TOKEN_DRAFT] =*/ "Draft", + /*[WSI_TOKEN_CHALLENGE] =*/ "Challenge", + + /* new for 04 */ + /*[WSI_TOKEN_KEY] =*/ "Key", + /*[WSI_TOKEN_VERSION] =*/ "Version", + /*[WSI_TOKEN_SWORIGIN] =*/ "Sworigin", + + /* new for 05 */ + /*[WSI_TOKEN_EXTENSIONS] =*/ "Extensions", + + /* client receives these */ + /*[WSI_TOKEN_ACCEPT] =*/ "Accept", + /*[WSI_TOKEN_NONCE] =*/ "Nonce", + /*[WSI_TOKEN_HTTP] =*/ "Http", + /*[WSI_TOKEN_MUXURL] =*/ "MuxURL", + }; + char buf[256]; + + for (n = 0; n < WSI_TOKEN_COUNT; n++) { + if (!lws_hdr_total_length(wsi, n)) + continue; + + lws_hdr_copy(wsi, buf, sizeof buf, n); + + fprintf(stderr, " %s = %s\n", token_names[n], buf); + } +} + +void *getJsonData( unsigned char * buf, int *n ) +{ + +int cc; +json_t *jarr1; +char *result = NULL; + +json_t *root = NULL, *server = NULL, *client, *clients; + + root = json_object(); + clients = json_object(); + + + server = json_pack( "{s:{:s:i,s:i}}", + "server", + "clients_max", shmptr->server_max_clients, + "clients_connected", shmptr->clientcount ); + + json_object_update( root, server ); + + if( shmptr->clientcount ) + { + jarr1 = json_array(); + + for( cc = 0 ; cc < shmptr->server_max_clients ; cc++ ) + { + + if( !shmptr->client[cc].authenticated ) + continue; + + client = json_pack( "{:s:s,s:s,s:i,s:s,s:I,s:I,s:I}", "username", + shmptr->client[cc].username, + "ipaddress", + shmptr->client[cc].ipaddress, + "udp_port", + shmptr->client[cc].udp_port, + "channel", + shmptr->client[cc].channel, + "lastactivity", + shmptr->client[cc].lastActivity, + "connecttime", + shmptr->client[cc].connectTime, + "idleTime", + (long long unsigned int)shmptr->client[cc].lastActivity - shmptr->client[cc].idleTime + ); + json_array_append_new( jarr1, client ); + } + json_object_set_new( clients, "clients", jarr1 ); + json_object_update( root, clients ); +} + json_dump_file(root, "json.txt", JSON_PRESERVE_ORDER | JSON_INDENT(4) ); + result = json_dumps(root, JSON_PRESERVE_ORDER | JSON_COMPACT ); + + *n = sprintf( (char *)&buf[LWS_SEND_BUFFER_PRE_PADDING], "%s", result ); + + + if( result ) + free( result ); + + json_decref(root); + return 0; +} + +struct per_session_data__umurmur_json { + int test; +}; + +static int +callback_umurmur_json( struct libwebsocket_context *context, + struct libwebsocket *wsi, + enum libwebsocket_callback_reasons reason, + void *user, void *in, size_t len) +{ + int m, n; + + unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 4096 + + LWS_SEND_BUFFER_POST_PADDING]; + + //struct per_session_data__umurmur_json *pss = (struct per_session_data__umurmur_json *)user; + + switch (reason) { + + case LWS_CALLBACK_ESTABLISHED: + lwsl_info("callback_umurmur_json: LWS_CALLBACK_ESTABLISHED\n"); + break; + + case LWS_CALLBACK_SERVER_WRITEABLE: + getJsonData( buf, &n ); + m = libwebsocket_write(wsi, &buf[LWS_SEND_BUFFER_PRE_PADDING], n, LWS_WRITE_TEXT); //printf("N: %d M: %d\n", n, m ); + + if( m == n ) + return 1; + break; + + case LWS_CALLBACK_RECEIVE: + //fprintf(stderr, "rx %d\n", (int)len); + if (len < 6) + break; + if (strcmp((const char *)in, "update\n") == 0) + libwebsocket_callback_on_writable_all_protocol(libwebsockets_get_protocol( wsi )); + + break; + /* + * this just demonstrates how to use the protocol filter. If you won't + * study and reject connections based on header content, you don't need + * to handle this callback + */ + + case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION: + dump_handshake_info(wsi); + /* you could return non-zero here and kill the connection */ + break; + + default: + break; + } + + return 0; +} + + + +/* list of supported protocols and callbacks */ + +static struct libwebsocket_protocols protocols[] = { + /* first protocol must always be HTTP handler */ + + { + "http-only", /* name */ + callback_http, /* callback */ + sizeof (struct per_session_data__http), /* per_session_data_size */ + 0, /* max frame size / rx buffer */ + }, + { + "umurmur-json-protocol", + callback_umurmur_json, + sizeof(struct per_session_data__umurmur_json), + 128, + }, + { NULL, NULL, 0, 0 } /* terminator */ +}; + +void sighandler(int sig) +{ + force_exit = 1; +} + +static struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "debug", required_argument, NULL, 'd' }, + { "port", required_argument, NULL, 'p' }, + { "ssl", no_argument, NULL, 's' }, + { "interface", required_argument, NULL, 'i' }, + { "daemonize", no_argument, NULL, 'D' }, + { "resource_path", required_argument, NULL, 'r' }, + { NULL, 0, 0, 0 } +}; + +int main(int argc, char **argv) +{ + char cert_path[1024]; + char key_path[1024]; + int n = 0; + int use_ssl = 0; + struct libwebsocket_context *context; + int opts = 0; + char interface_name[128] = ""; + const char *iface = NULL; +// unsigned int oldus = 0; + struct lws_context_creation_info info; + + int syslog_options = LOG_PID | LOG_PERROR; + + int debug_level = 7; + + int daemonize = 0; + + + memset(&info, 0, sizeof info); + info.port = 7681; + + while (n >= 0) { + n = getopt_long(argc, argv, "i:hsp:d:Dr:", options, NULL); + if (n < 0) + continue; + switch (n) { + case 'D': + daemonize = 1; + syslog_options &= ~LOG_PERROR; + break; + + case 'd': + debug_level = atoi(optarg); + break; + case 's': + use_ssl = 1; + break; + case 'p': + info.port = atoi(optarg); + break; + case 'i': + strncpy(interface_name, optarg, sizeof interface_name); + interface_name[(sizeof interface_name) - 1] = '\0'; + iface = interface_name; + break; + case 'r': + resource_path = optarg; + printf("Setting resource path to \"%s\"\n", resource_path); + break; + case 'h': + fprintf(stderr, "Usage: test-server " + "[--port=

] [--ssl] " + "[-d ] " + "[--resource_path ]\n"); + exit(1); + } + } + +key_t key = 0x53021d79; + + if( ( shmid = shmget( key, 0, 0) ) == -1 ) + { + perror("shmget"); + printf( "umurmurd doesn't seem to be running\n\r" ); + exit(EXIT_FAILURE); + } + + + if( ( shmptr = shmat( shmid,0, 0 ) ) == (void *) -1 ) + { + perror("shmat"); + exit(EXIT_FAILURE); + } + + + /* + * normally lock path would be /var/lock/lwsts or similar, to + * simplify getting started without having to take care about + * permissions or running as root, set to /tmp/.lwsts-lock + */ + if (daemonize && lws_daemonize("/tmp/.lwsts-lock")) { + fprintf(stderr, "Failed to daemonize\n"); + return 1; + } + + + signal(SIGINT, sighandler); + + + /* we will only try to log things according to our debug_level */ + setlogmask(LOG_UPTO (LOG_DEBUG)); + openlog("lwsts", syslog_options, LOG_DAEMON); + + + /* tell the library what debug level to emit and to send it to syslog */ + lws_set_log_level(debug_level, lwsl_emit_syslog); + + lwsl_notice("uMurmurd Websocket server - " + "(C) Copyright 2014 Michael J. Pounders <> - " + "licensed under LGPL2.1\n"); + + info.iface = iface; + info.protocols = protocols; + + info.extensions = libwebsocket_get_internal_extensions(); + + if (!use_ssl) { + info.ssl_cert_filepath = NULL; + info.ssl_private_key_filepath = NULL; + } else { + if (strlen(resource_path) > sizeof(cert_path) - 32) { + lwsl_err("resource path too long\n"); + return -1; + } + sprintf(cert_path, "%s/ssl/umurmurd_websocket.pem", + resource_path); + if (strlen(resource_path) > sizeof(key_path) - 32) { + lwsl_err("resource path too long\n"); + return -1; + } + sprintf(key_path, "%s/ssl/umurmurd_websocket.key.pem", + resource_path); + + info.ssl_cert_filepath = cert_path; + info.ssl_private_key_filepath = key_path; + } + info.gid = -1; + info.uid = -1; + info.options = opts; + + context = libwebsocket_create_context(&info); + if (context == NULL) { + lwsl_err("libwebsocket init failed\n"); + return -1; + } + + n = 0; + while (n >= 0 && !force_exit) { + struct timeval tv; + + gettimeofday(&tv, NULL); + + /* + * This provokes the LWS_CALLBACK_SERVER_WRITEABLE for every + * live websocket connection using the DUMB_INCREMENT protocol, + * as soon as it can take more packets (usually immediately) + */ + +// if (((unsigned int)tv.tv_usec - oldus) > 50000) { +// libwebsocket_callback_on_writable_all_protocol(&protocols[PROTOCOL_JSON_UMURMURD]); +// oldus = tv.tv_usec; +// } + + + /* + * If libwebsockets sockets are all we care about, + * you can use this api which takes care of the poll() + * and looping through finding who needed service. + * + * If no socket needs service, it'll return anyway after + * the number of ms in the second argument. + */ + + n = libwebsocket_service(context, 50); + + } + + + libwebsocket_context_destroy(context); + + lwsl_notice("umurmur_websocket server exited cleanly\n"); + + shmdt( shmptr ); + closelog(); + + return 0; +}