| Previous | 199869 Revisions | Next |
| r41598 Wednesday 4th November, 2015 at 16:18:52 UTC by Shideravan |
|---|
| It's better to snes to use IPT_START... ...Rather IPT_START1, for start button. |
| [3rdparty/mongoose] | LICENSE* README.md* mongoose.c* mongoose.h* |
| [3rdparty/mongoose/docs] | API.md* AndroidBuild.md* BasicWebsite.md* Embed.md* FAQ.md* FileSharing.md* Internals.md* Options.md* PhpWebsite.md* ReleaseNotes.md* SSL.md* Usage.md* |
| [3rdparty/mongoose/examples] | .gitignore* Makefile* |
| [3rdparty/mongoose/examples/array_vars] | Makefile* array_vars.c* |
| [3rdparty/mongoose/examples/big_upload] | Makefile* big_upload.c* |
| [3rdparty/mongoose/examples/cookie_authentication] | Makefile* cookie_auth.c* index.html* login.html* |
| [3rdparty/mongoose/examples/csharp] | example.cs* mongoose.cs* |
| [3rdparty/mongoose/examples/digest_authentication] | Makefile* digest_auth.c* |
| [3rdparty/mongoose/examples/file_upload] | Makefile* file_upload.c* |
| [3rdparty/mongoose/examples/form_submit] | Makefile* form_submit.c* |
| [3rdparty/mongoose/examples/hello_world] | Makefile* hello_world.c* |
| [3rdparty/mongoose/examples/http_client] | Makefile* http_client.c* |
| [3rdparty/mongoose/examples/mjpg_streamer] | Makefile* mjpg_streamer.c* |
| [3rdparty/mongoose/examples/multi_threaded_server] | Makefile* multi_threaded_server.c* |
| [3rdparty/mongoose/examples/proxy_server] | Makefile* proxy_server.c* ssl_cert.pem* |
| [3rdparty/mongoose/examples/proxy_server/proxy_web_root] | index.html* |
| [3rdparty/mongoose/examples/proxy_server/proxy_web_root/app1] | index.html* |
| [3rdparty/mongoose/examples/proxy_server/proxy_web_root/app2] | index.html* |
| [3rdparty/mongoose/examples/restful_api] | Makefile* index.html* restful_api.c* |
| [3rdparty/mongoose/examples/send_file] | Makefile* send_file.c* |
| [3rdparty/mongoose/examples/web_server] | Makefile* web_server.c* |
| [3rdparty/mongoose/examples/web_server/certs] | cert.pem* |
| [3rdparty/mongoose/examples/websocket_chat] | Makefile* index.html* websocket_chat.c* |
| [3rdparty/mongoose/examples/websocket_echo_server] | Makefile* index.html* websocket_echo_server.c* |
| [3rdparty/mongoose/examples/websocket_ssl_proxy] | Makefile* net_skeleton.h* ssl_wrapper.c* ssl_wrapper.h* ws_ssl.c* ws_ssl.html* |
| [3rdparty/mongoose/examples/websocket_ssl_proxy/certs] | ws1_ca.pem* ws1_client.pem* ws1_server.pem* ws2_ca.pem* ws2_client.pem* ws2_server.pem* |
| [3rdparty/mongoose/jni] | Android.mk* |
| [3rdparty/mongoose/scripts] | embed_binary_files.pl* |
| [3rdparty/mongoose/test] | Makefile* unit_test.c* |
| [scripts/src] | 3rdparty.lua emu.lua main.lua |
| [src/devices/bus/snes_ctrl] | joypad.c |
| [src/devices/cpu/am29000] | am29ops.h |
| [src/devices/cpu/avr8] | avr8dasm.c |
| [src/devices/cpu/e132xs] | e132xs.c |
| [src/devices/cpu/hd61700] | hd61700.c |
| [src/devices/cpu/score] | scoredsm.c |
| [src/devices/cpu/tms7000] | tms70op.inc |
| [src/devices/machine] | mos6530n.c |
| [src/devices/video] | 315_5313.c mc6845.c |
| [src/emu] | emuopts.c emuopts.h luaengine.c machine.c mame.c mame.h webengine.c* webengine.h* |
| [src/emu/debug] | debugcpu.c |
| [src/emu/ui] | ui.c |
| [src/mame] | arcade.lst |
| [src/mame/drivers] | a2600.c aristmk5.c atarittl.c champbas.c dorachan.c equites.c goldnpkr.c mz700.c pacman.c polyplay.c pong.c |
| [src/mame/includes] | mz700.h |
| [src/mame/machine] | alpha8201.c megadriv.c mz700.c namcoio.c |
| [src/mame/video] | fuukifg3.c mcr.c mcr3.c mcr68.c mz700.c |
| [web] | favicon.ico* index.html* |
| [web/css] | jquery.mobile.css* |
| [web/css/images] | ajax-loader.gif* icons-18-black.png* icons-18-white.png* icons-36-black.png* icons-36-white.png* |
| [web/images] | logo-mame-small.png* |
| [web/js] | jquery.js* jquery.mobile.js* |
| r0 | r250110 | |
|---|---|---|
| 1 | Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com> | |
| 2 | Copyright (c) 2013-2015 Cesanta Software Limited | |
| 3 | All rights reserved | |
| 4 | ||
| 5 | This code is dual-licensed: you can redistribute it and/or modify | |
| 6 | it under the terms of the GNU General Public License version 2 as | |
| 7 | published by the Free Software Foundation. For the terms of this | |
| 8 | license, see <http://www.gnu.org/licenses>. | |
| 9 | ||
| 10 | You are free to use this code under the terms of the GNU General | |
| 11 | Public License, but WITHOUT ANY WARRANTY; without even the implied | |
| 12 | warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | |
| 13 | See the GNU General Public License for more details. | |
| 14 | ||
| 15 | Alternatively, you can license this code under a commercial | |
| 16 | license, as set out in <http://cesanta.com/>. |
| r0 | r250110 | |
|---|---|---|
| 1 | # <img src="http://cesanta.com/images/mongoose_logo.png" width="64" height="64"> Mongoose Web Server | |
| 2 | ||
| 3 | [](https://gitter.im/cesanta/mongoose?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) | |
| 4 | ||
| 5 | Mongoose is the most easy to use web server on the planet. A web server of choice for Web developers (PHP, Ruby, Python, etc) and Web designers. | |
| 6 | ||
| 7 | Mongoose is built on top of Libmongoose embedded library, which can turn | |
| 8 | anything into a web server in 5 minutes worth of effort and few lines of code. | |
| 9 | Libmongoose is used to serve Web GUI on embedded devices, implement RESTful | |
| 10 | services, RPC frameworks (e.g. JSON-RPC), handle telemetry data exchange, and | |
| 11 | perform many other tasks in various different industries including aerospace, | |
| 12 | manufacturing, finance, research, automotive, gaming, IT. | |
| 13 | ||
| 14 | ||
| 15 | * [Mailing list](http://groups.google.com/group/mongoose-users) | |
| 16 | * [Downloads](http://cesanta.com/products.shtml) | |
| 17 | * [Documentation](http://cesanta.com/docs.shtml) | |
| 18 | ||
| 19 | Check out Fossa - our [embedded multi-protocol library](https://github.com/cesanta/fossa) with TCP,UDP,HTTP,Websocket,MQTT,DNS support, designed for Internet Of Things! | |
| 20 | ||
| 21 | # Features | |
| 22 | ||
| 23 | - Works on Windows, Mac, UNIX/Linux, iPhone, Android eCos, QNX | |
| 24 | and many other platforms | |
| 25 | - CGI, SSI, SSL, Digest auth, Websocket, WEbDAV, Resumed download, | |
| 26 | URL rewrite, file blacklist | |
| 27 | - Custom error pages, Virtual hosts, IP-based ACL, Windows service, | |
| 28 | HTTP/HTTPS client | |
| 29 | - Simple and clean | |
| 30 | [embedding API](https://github.com/cesanta/mongoose/blob/master/mongoose.h). | |
| 31 | The source is in single | |
| 32 | [mongoose.c](https://github.com/cesanta/mongoose/blob/master/mongoose.c) file | |
| 33 | to make embedding easy | |
| 34 | - Extremely lightweight, has a core of under 40kB and tiny runtime footprint | |
| 35 | - Asynchronous, non-blocking core supporting single- or multi-threaded usage | |
| 36 | - On the market since 2004 with over 1 million cumulative downloads | |
| 37 | - Stable, mature and tested, has several man-years invested | |
| 38 | in continuous improvement and refinement | |
| 39 | ||
| 40 | # Screenshots | |
| 41 | ||
| 42 | Download, double-click to start, run browser -- that's all! | |
| 43 | ||
| 44 |  | |
| 45 |  | |
| 46 | ||
| 47 |  | |
| 48 |  | |
| 49 | ||
| 50 | # Contributions | |
| 51 | ||
| 52 | People who have agreed to the | |
| 53 | [Cesanta CLA](http://cesanta.com/contributors_la.html) | |
| 54 | can make contributions. Note that the CLA isn't a copyright | |
| 55 | _assigment_ but rather a copyright _license_. | |
| 56 | You retain the copyright on your contributions. | |
| 57 | ||
| 58 | # Licensing | |
| 59 | ||
| 60 | Mongoose is released under commercial and | |
| 61 | [GNU GPL v.2](http://www.gnu.org/licenses/old-licenses/gpl-2.0.html) open | |
| 62 | source licenses. The GPLv2 open source License does not generally permit | |
| 63 | incorporating this software into non-open source programs. | |
| 64 | For those customers who do not wish to comply with the GPLv2 open | |
| 65 | source license requirements, | |
| 66 | [Cesanta](http://cesanta.com) offers a full, | |
| 67 | royalty-free commercial license and professional support | |
| 68 | without any of the GPL restrictions. | |
| 69 | ||
| 70 | # Other products by Cesanta | |
| 71 | ||
| 72 | - [Fossa](http://github.com/cesanta/fossa) - Multi-protocol networking library | |
| 73 | - [SSL Wrapper](https://github.com/cesanta/ssl_wrapper) - application to | |
| 74 | secure network communications | |
| 75 | - [Frozen](https://github.com/cesanta/frozen) - JSON parser and generator | |
| 76 | - [SLRE](https://github.com/cesanta/slre) - Super Light Regular Expression | |
| 77 | library | |
| 78 | - [V7](https://github.com/cesanta/v7) - Embedded JavaScript engine |
| r0 | r250110 | |
|---|---|---|
| 1 | # Mongoose API Reference | |
| 2 | ||
| 3 | struct mg_server *mg_create_server(void *server_param, mg_handler_t handler); | |
| 4 | ||
| 5 | Creates web server instance. Returns opaque instance pointer, or NULL if | |
| 6 | there is not enough memory. `server_param`: Could be any pointer, or NULL. | |
| 7 | This pointer will be passed | |
| 8 | to the callback functions as `struct mg_connection::server_param` field. | |
| 9 | A common use case is to pass `this` pointer of the C++ wrapper class | |
| 10 | as `user_param`, to let the callback get the pointer to the C++ object. | |
| 11 | ||
| 12 | Note that this function doesn't make the | |
| 13 | server instance to serve. Serving is done by `mg_poll_server()` function. | |
| 14 | Mongoose has single-threaded, event-driven, asynchronous, non-blocking core. | |
| 15 | When server instance is created, it contains an information about | |
| 16 | the configuration and the state of each connection. | |
| 17 | Server instance is capable on listening on only one port. After creation, | |
| 18 | `struct mg_server` has a list | |
| 19 | of active connections and configuration parameters. | |
| 20 | ||
| 21 | Side-effect: on UNIX, `mg_create_server()` ignores SIGPIPE signals. If custom | |
| 22 | processing is required SIGPIPE, signal handler must be set up after | |
| 23 | calling `mg_create_server()`. | |
| 24 | ||
| 25 | Important: Mongoose does not install `SIGCHLD` handler. If CGI is used, | |
| 26 | `SIGCHLD` handler must be set up to reap CGI zombie processes. | |
| 27 | ||
| 28 | ||
| 29 | void mg_destroy_server(struct mg_server **server); | |
| 30 | ||
| 31 | Deallocates web server instance, closes all pending connections, and makes | |
| 32 | server pointer a NULL pointer. | |
| 33 | ||
| 34 | const char mg_set_option(struct mg_server *server, const char *name, | |
| 35 | const char *value); | |
| 36 | ||
| 37 | Sets a particular server option. Note that at least one option, | |
| 38 | `listening_port`, must be specified. To serve static files, `document_root` | |
| 39 | must be specified too. If `document_root` option is left unset, Mongoose | |
| 40 | will not access filesystem at all. `mg_set_option()` returns NULL if option was | |
| 41 | set successfully, otherwise it returns human-readable error string. It is | |
| 42 | allowed to call `mg_set_option()` by the same thread that does | |
| 43 | `mg_poll_server()` (Mongoose thread) and change server configuration while it | |
| 44 | is serving, in between `mg_poll_server()` calls. | |
| 45 | ||
| 46 | int mg_poll_server(struct mg_server *server, int milliseconds); | |
| 47 | ||
| 48 | Performs one iteration of IO loop by iterating over all | |
| 49 | active connections, performing `select()` syscall on all sockets with a timeout | |
| 50 | of `milliseconds`. When `select()` returns, Mongoose | |
| 51 | does an IO for each socket that has data to be sent or received. Application | |
| 52 | code must call `mg_poll_server()` in a loop. It is an error to have more then | |
| 53 | one thread calling `mg_poll_server()`, `mg_set_option()` or any other function | |
| 54 | that take `struct mg_server *` parameter. Mongoose does not | |
| 55 | mutex-protect `struct mg_server *`, therefore only single thread | |
| 56 | (Mongoose thread) should make Mongoose calls. | |
| 57 | ||
| 58 | `mg_poll_server()` calls user-specified event handler when certain events | |
| 59 | occur. Sequence of events for the accepted connection is this: | |
| 60 | ||
| 61 | * `MG_AUTH` - Mongoose asks whether this connection is authorized. If event | |
| 62 | handler returns `MG_FALSE`, then Mongoose does not serve the request but | |
| 63 | sends authorization request to the client. If `MG_TRUE` is returned, | |
| 64 | then Mongoose continues on with the request. | |
| 65 | * `MG_REQUEST` - Mongoose asks event handler to serve the request. If | |
| 66 | event handler serves the request by sending a reply, | |
| 67 | it should return `MG_TRUE`. Otherwise, | |
| 68 | it should return `MG_FALSE` which tells Mongoose that request is not | |
| 69 | served and Mongoose should serve it. For example, event handler might | |
| 70 | choose to serve only RESTful API requests with URIs that start with | |
| 71 | certain prefix, and let Mongoose serve all static files. | |
| 72 | If event handler decides to serve the request, but doesn't have | |
| 73 | all the data at the moment, it should return `MG_MORE`. That tells | |
| 74 | Mongoose to keep the connection open after callback returns. | |
| 75 | ||
| 76 | `mg_connection::connection_param` pointer is a placeholder to keep | |
| 77 | user-specific data. For example, handler could decide to open a DB | |
| 78 | connection and store DB connection handle in `connection_param`. | |
| 79 | * `MG_POLL` is sent to every connection on every iteration of | |
| 80 | `mg_poll_server()`. Event handler should return `MG_FALSE` to ignore | |
| 81 | this event. If event handler returns `MG_TRUE`, then Mongoose assumes | |
| 82 | that event handler has finished sending data, and Mongoose will | |
| 83 | close the connection. | |
| 84 | * `MG_HTTP_ERROR` sent when Mongoose is about to send HTTP error back | |
| 85 | to the client. Event handler can choose to send a reply itself, in which | |
| 86 | case event handler must return `MG_TRUE`. Otherwise, event handler must | |
| 87 | return `MG_FALSE`. | |
| 88 | * `MG_CLOSE` is sent when the connection is closed. This event is used | |
| 89 | to cleanup per-connection state stored in `connection_param` | |
| 90 | if it was allocated. Event handler return value is ignored. | |
| 91 | ||
| 92 | Sequence of events for the client connection is this: | |
| 93 | ||
| 94 | * `MG_CONNECT` sent when Mongoose has connected to the remote host. | |
| 95 | This event is sent to the connection initiated by `mg_connect()` call. | |
| 96 | Connection status is held in `mg_connection::status_code`: if zero, | |
| 97 | then connection was successful, otherwise connection was not established. | |
| 98 | User should send a request upon successful connection. | |
| 99 | Event handler should return `MG_TRUE` if connection was successful and | |
| 100 | HTTP request has been sent. Otherwise, it should send `MG_FALSE`. | |
| 101 | * `MG_REPLY` is sent when response has been received from the remote host. | |
| 102 | If event handler sends another request, then it should return `MG_TRUE`. | |
| 103 | Otherwise it should return `MG_FALSE` and Mongoose will close the connection. | |
| 104 | * `MG_CLOSE` same as for the accepted connection. | |
| 105 | ||
| 106 | ||
| 107 | When mongoose buffers in HTTP request and successfully parses it, it sends | |
| 108 | `MG_REQUEST` event for GET requests immediately. For POST requests, | |
| 109 | Mongoose delays the call until the whole POST request is buffered in memory. | |
| 110 | POST data is available to the callback as `struct mg_connection::content`, | |
| 111 | and POST data length is in `struct mg_connection::content_len`. | |
| 112 | ||
| 113 | Note that websocket connections are treated the same way. Mongoose buffers | |
| 114 | websocket frame in memory, and calls event handler when frame is fully | |
| 115 | buffered. Frame data is available `struct mg_connection::content`, and | |
| 116 | data length is in `struct mg_connection::content_len`, i.e. very similar to | |
| 117 | the POST request. `struct mg_connection::is_websocket` flag indicates | |
| 118 | whether the request is websocket or not. Also, for websocket requests, | |
| 119 | there is `struct mg_connection::wsbits` field which contains first byte | |
| 120 | of the websocket frame which URI handler can examine. Note that to | |
| 121 | reply to the websocket client, `mg_websocket_write()` should be used. | |
| 122 | To reply to the plain HTTP client, `mg_write_data()` should be used. | |
| 123 | ||
| 124 | Return value: number of active connections. | |
| 125 | ||
| 126 | ||
| 127 | const char **mg_get_valid_option_names(void); | |
| 128 | ||
| 129 | Returns a NULL-terminated array of option names and their default values. | |
| 130 | There are two entries per option in an array: an option name followed by a | |
| 131 | default value. A default value could be NULL. A NULL name indicates an end | |
| 132 | of the array. | |
| 133 | ||
| 134 | const char *mg_get_option(const struct mg_server *server, const char *name); | |
| 135 | ||
| 136 | Returns the value of particular configuration parameter. If | |
| 137 | given parameter name is not valid, NULL is returned. For valid names, return | |
| 138 | value is guaranteed to be non-NULL. If parameter is not set, zero-length string | |
| 139 | is returned. | |
| 140 | ||
| 141 | void mg_wakeup_server_ex(struct mg_server *, mg_handler_t func, | |
| 142 | const char *fmt, ...); | |
| 143 | ||
| 144 | Sends string message to a server. Function `func` is called for every active | |
| 145 | connection. String message is passed in `struct mg_connection::callback_param`. | |
| 146 | This function is designed to push data to the connected clients, and | |
| 147 | can be called from any thread. There is a limitation on the length of | |
| 148 | the message, currently at 8 kilobytes. | |
| 149 | ||
| 150 | void mg_send_status(struct mg_connection *, int status_code); | |
| 151 | void mg_send_header(struct mg_connection *, const char *name, | |
| 152 | const char *value); | |
| 153 | void mg_send_data(struct mg_connection *, const void *data, int data_len); | |
| 154 | void mg_printf_data(struct mg_connection *, const char *format, ...); | |
| 155 | ||
| 156 | These functions are used to construct a response to the client. HTTP response | |
| 157 | consists of three parts: a status line, zero or more HTTP headers, | |
| 158 | a response body. Mongoose provides functions for all three parts: | |
| 159 | * `mg_send_status()` is used to create status line. This function can be | |
| 160 | called zero or once. If `mg_send_status()` is not called, then Mongoose | |
| 161 | will send status 200 (success) implicitly. | |
| 162 | * `mg_send_header()` adds HTTP header to the response. This function could | |
| 163 | be called zero or more times. | |
| 164 | * `mg_send_data()` and `mg_printf_data()` are used to send data to the | |
| 165 | client. Note that Mongoose adds `Transfer-Encoding: chunked` header | |
| 166 | implicitly, and sends data in chunks. Therefore, it is not necessary to | |
| 167 | set `Content-Length` header. Note that `mg_send_data()` and | |
| 168 | `mg_printf_data()` do not send data immediately. Instead, they spool | |
| 169 | data in memory, and Mongoose sends that data later after URI handler | |
| 170 | returns. If data to be sent is huge, an URI handler might | |
| 171 | send data in pieces by saving state in | |
| 172 | `struct mg_connection::connection_param` variable and returning `0`. Then | |
| 173 | Mongoose will call a handler repeatedly after each socket write. | |
| 174 | ||
| 175 | <!-- --> | |
| 176 | ||
| 177 | void mg_send_file(struct mg_connection *, const char *path); | |
| 178 | ||
| 179 | Tells Mongoose to serve given file. Mongoose handles file according to | |
| 180 | it's extensions, i.e. Mongoose will invoke CGI script if `path` has CGI | |
| 181 | extension, it'll render SSI file if `path` has SSI extension, etc. If `path` | |
| 182 | points to a directory, Mongoose will show directory listing. If this function | |
| 183 | is used, no calls to `mg_send*` or `mg_printf*` functions must be made, and | |
| 184 | event handler must return `MG_MORE`. | |
| 185 | ||
| 186 | size_t mg_websocket_write(struct mg_connection* conn, int opcode, | |
| 187 | const char *data, size_t data_len); | |
| 188 | size_t mg_websocket_printf(struct mg_connection* conn, int opcode, | |
| 189 | const char *fmt, ...); | |
| 190 | ||
| 191 | ||
| 192 | Similar to `mg_write()` and `mg_printf()`, but wraps the data into a | |
| 193 | websocket frame with a given websocket `opcode`. | |
| 194 | ||
| 195 | const char *mg_get_header(const struct mg_connection *, const char *name); | |
| 196 | ||
| 197 | Get the value of particular HTTP header. This is a helper function. | |
| 198 | It traverses http_headers array, and if the header is present in the array, | |
| 199 | returns its value. If it is not present, NULL is returned. | |
| 200 | ||
| 201 | ||
| 202 | int mg_get_var(const struct mg_connection *conn, const char *var_name, | |
| 203 | char *buf, size_t buf_len); | |
| 204 | ||
| 205 | Gets HTTP form variable. Both POST buffer and query string are inspected. | |
| 206 | Form variable is url-decoded and written to the buffer. On success, this | |
| 207 | function returns the length of decoded variable. On error, -1 is returned if | |
| 208 | variable not found, and -2 is returned if destination buffer is too small | |
| 209 | to hold the variable. Destination buffer is guaranteed to be | |
| 210 | '\0' - terminated if it is not NULL or zero length. | |
| 211 | ||
| 212 | int mg_parse_header(const char *hdr, const char *var_name, char *buf, | |
| 213 | size_t buf_size); | |
| 214 | ||
| 215 | This function parses HTTP header and fetches given variable's value in a buffer. | |
| 216 | A header should be like `x=123, y=345, z="other value"`. This function is | |
| 217 | designed to parse Cookie headers, Authorization headers, and similar. Returns | |
| 218 | the length of the fetched value, or 0 if variable not found. | |
| 219 | ||
| 220 | int mg_modify_passwords_file(const char *passwords_file_name, | |
| 221 | const char *domain, | |
| 222 | const char *user, | |
| 223 | const char *password); | |
| 224 | ||
| 225 | Add, edit or delete the entry in the passwords file. | |
| 226 | This function allows an application to manipulate .htpasswd files on the | |
| 227 | fly by adding, deleting and changing user records. This is one of the | |
| 228 | several ways of implementing authentication on the server side. | |
| 229 | If password is not NULL, entry is added (or modified if already exists). | |
| 230 | If password is NULL, entry is deleted. | |
| 231 | Return: 1 on success, 0 on error. | |
| 232 | ||
| 233 | ||
| 234 | int mg_parse_multipart(const char *buf, int buf_len, | |
| 235 | char *var_name, int var_name_len, | |
| 236 | char *file_name, int file_name_len, | |
| 237 | const char **data, int *data_len); | |
| 238 | ||
| 239 | Parses a buffer that contains multipart form data. Stores chunk name | |
| 240 | in a `var_name` buffer. If chunk is an uploaded file, then `file_name` | |
| 241 | will have a file name. `data` and `data_len` will point to the chunk data. | |
| 242 | Returns number of bytes to skip to the next chunk. | |
| 243 | ||
| 244 | struct mg_connection *mg_connect(struct mg_server *server, | |
| 245 | const char *host, int port, int use_ssl); | |
| 246 | ||
| 247 | Create connection to the remote host. Returns `NULL` on error, non-null | |
| 248 | if the connection has been scheduled for connection. Upon a connection, | |
| 249 | Mongoose will send `MG_CONNECT` event to the event handler. |
| r0 | r250110 | |
|---|---|---|
| 1 | # Mongoose Build on Android | |
| 2 | ||
| 3 | This is a small guide to help you run mongoose on Android. Currently it is | |
| 4 | tested on the HTC Wildfire. If you have managed to run it on other devices | |
| 5 | as well, please comment or drop an email in the mailing list. | |
| 6 | Note : You dont need root access to run mongoose on Android. | |
| 7 | ||
| 8 | - Clone Mongoose Git repo | |
| 9 | - Download the Android NDK from [http://developer.android.com/tools/sdk/ndk/index.html](http://developer.android.com/tools/sdk/ndk/index.html) | |
| 10 | - Run `/path-to-ndk/ndk-build -C /path/to/mongoose` | |
| 11 | That should generate mongoose/lib/armeabi/mongoose | |
| 12 | - Using the adb tool (you need to have Android SDK installed for that), | |
| 13 | push the generated mongoose binary to `/data/local` folder on device. | |
| 14 | - From adb shell, navigate to `/data/local` and execute `./mongoose`. | |
| 15 | - To test if the server is running fine, visit your web-browser and | |
| 16 | navigate to `http://127.0.0.1:8080` You should see the `Index of /` page. | |
| 17 | ||
| 18 |  | |
| 19 | ||
| 20 | ||
| 21 | Notes: | |
| 22 | ||
| 23 | - `jni` stands for Java Native Interface. Read up on Android NDK if you want | |
| 24 | to know how to interact with the native C functions of mongoose in Android | |
| 25 | Java applications. | |
| 26 | - TODO: A Java application that interacts with the native binary or a | |
| 27 | shared library. |
| r0 | r250110 | |
|---|---|---|
| 1 | How To Create Basic Website With Mongoose | |
| 2 | =========================================== | |
| 3 | ||
| 4 | ## 1. Create a directory which will contain your website files. For example, on drive `C:\`, create a directory called `my_website`: | |
| 5 | ||
| 6 |  | |
| 7 | ||
| 8 | ## 2. Inside `my_website` directory, create a new file called "index". This will be the default web page shown when the website is visited. | |
| 9 | ||
| 10 |  | |
| 11 | ||
| 12 | ## 3. Open index file with your favorite editor (for example, Notepad) and enter some HTML code: | |
| 13 | ||
| 14 |  | |
| 15 | ||
| 16 | ## 4. Save this file as `index.html`: | |
| 17 | ||
| 18 |  | |
| 19 | ||
| 20 | ||
| 21 | ## 5. Download Mongoose executable from http://cesanta.com/mongoose.shtml and copy the executable inside `my_website` directory: | |
| 22 | ||
| 23 |  | |
| 24 | ||
| 25 | ## 6. Double-click mongoose executable. An icon will appear on a system tray in the bottom right corner of the desktop: | |
| 26 | ||
| 27 |  | |
| 28 | ||
| 29 | ## 7. Click on the mongoose icon and choose "Go to my address" menu: | |
| 30 |  | |
| 31 | ||
| 32 | ## 8. A browser will popup displaying `index.html` file. Now, you can expand your website by adding more content. | |
| 33 | ||
| 34 |  |
| r0 | r250110 | |
|---|---|---|
| 1 | # Mongoose Embedding Guide | |
| 2 | ||
| 3 | Embedding Mongoose is done in two steps: | |
| 4 | ||
| 5 | 1. Copy | |
| 6 | [mongoose.c](https://raw.github.com/cesanta/mongoose/master/mongoose.c) and | |
| 7 | [mongoose.h](https://raw.github.com/cesanta/mongoose/master/mongoose.h) | |
| 8 | to your application's source tree and include them in the build. | |
| 9 | 2. Somewhere in the application code, call `mg_create_server()` to create | |
| 10 | a server, configure it with `mg_set_option()` and loop with | |
| 11 | `mg_poll_server()` until done. Call `mg_destroy_server()` to cleanup. | |
| 12 | ||
| 13 | Here's a minimal application `app.c` that embeds mongoose: | |
| 14 | ||
| 15 | #include "mongoose.h" | |
| 16 | ||
| 17 | int main(void) { | |
| 18 | struct mg_server *server = mg_create_server(NULL, NULL); | |
| 19 | mg_set_option(server, "document_root", "."); // Serve current directory | |
| 20 | mg_set_option(server, "listening_port", "8080"); // Open port 8080 | |
| 21 | ||
| 22 | for (;;) { | |
| 23 | mg_poll_server(server, 1000); // Infinite loop, Ctrl-C to stop | |
| 24 | } | |
| 25 | mg_destroy_server(&server); | |
| 26 | ||
| 27 | return 0; | |
| 28 | } | |
| 29 | ||
| 30 | To compile it, put `mongoose.c`, `mongoose.h` and `app.c` into one | |
| 31 | folder, start terminal on UNIX or Visual Studio command line prompt on Windows, | |
| 32 | and run the following command: | |
| 33 | ||
| 34 | cc app.c mongoose.c -pthread -o app # on Unix | |
| 35 | cl.exe app.c mongoose.c /TC /MD # on Windows | |
| 36 | ||
| 37 | When run, this simple application opens port 8080 and serves static files, | |
| 38 | CGI files and lists directory content in the current working directory. | |
| 39 | ||
| 40 | It is possible to generate HTML page content. Mongoose can call user-defined | |
| 41 | function when certain events occur. | |
| 42 | That function is called _an event handler_, and it is the second parameter | |
| 43 | to `mg_create_server()` function. Here is the example event handler function: | |
| 44 | ||
| 45 | int event_handler(struct mg_connection *conn, enum mg_event ev) { | |
| 46 | switch (ev) { | |
| 47 | case MG_AUTH: return MG_TRUE; | |
| 48 | default: return MG_FALSE; | |
| 49 | } | |
| 50 | } | |
| 51 | ||
| 52 | Event handler is called by Mongoose with `struct mg_connection *` | |
| 53 | pointer and an event number. `struct mg_connection *conn` | |
| 54 | has all information about the request: HTTP headers, POST or websocket | |
| 55 | data buffer, etcetera. `enum mg_event ev` tells which exactly event is sent. | |
| 56 | For each event, an event handler returns a value which tells Mongoose how | |
| 57 | to behave. | |
| 58 | ||
| 59 | The sequence of events for every connection is this: | |
| 60 | ||
| 61 | * `MG_AUTH` - Mongoose asks whether this connection is authorized. If event | |
| 62 | handler returns `MG_FALSE`, then Mongoose does not serve the request but | |
| 63 | sends authorization request to the client. If `MG_TRUE` is returned, | |
| 64 | then Mongoose continues on with the request. | |
| 65 | * `MG_REQUEST` - Mongoose asks event handler to serve the request. If | |
| 66 | event handler serves the request by sending a reply, | |
| 67 | it should return `MG_TRUE`. Otherwise, | |
| 68 | it should return `MG_FALSE` which tells Mongoose that request is not | |
| 69 | served and Mongoose should serve it. For example, event handler might | |
| 70 | choose to serve only RESTful API requests with URIs that start with | |
| 71 | certain prefix, and let Mongoose serve all static files. | |
| 72 | If event handler decides to serve the request, but doesn't have | |
| 73 | all the data at the moment, it should return `MG_MORE`. That tells | |
| 74 | Mongoose to keep the connection open after callback returns. | |
| 75 | ||
| 76 | `mg_connection::connection_param` pointer is a placeholder to keep | |
| 77 | user-specific data. For example, handler could decide to open a DB | |
| 78 | connection and store DB connection handle in `connection_param`. | |
| 79 | * `MG_POLL` is sent to every connection on every iteration of | |
| 80 | `mg_poll_server()`. Event handler should return `MG_FALSE` to ignore | |
| 81 | this event. If event handler returns `MG_TRUE`, then Mongoose assumes | |
| 82 | that event handler has finished sending data, and Mongoose will | |
| 83 | close the connection. | |
| 84 | * `MG_HTTP_ERROR` sent when Mongoose is about to send HTTP error back | |
| 85 | to the client. Event handler can choose to send a reply itself, in which | |
| 86 | case event handler must return `MG_TRUE`. Otherwise, event handler must | |
| 87 | return `MG_FALSE` | |
| 88 | * `MG_CLOSE` is sent when the connection is closed. This event is used | |
| 89 | to cleanup per-connection state stored in `connection_param` | |
| 90 | if it was allocated. | |
| 91 | ||
| 92 | Let's extend our minimal application example and | |
| 93 | create an URI that will be served by user's C code. The app will handle | |
| 94 | `/hello` URI by showing a hello message. So, when app is run, | |
| 95 | http://127.0.0.1:8080/hello will say hello, and here's the code: | |
| 96 | ||
| 97 | #include <string.h> | |
| 98 | #include "mongoose.h" | |
| 99 | ||
| 100 | static int event_handler(struct mg_connection *conn, enum mg_event ev) { | |
| 101 | if (ev == MG_AUTH) { | |
| 102 | return MG_TRUE; // Authorize all requests | |
| 103 | } else if (ev == MG_REQUEST && !strcmp(conn->uri, "/hello")) { | |
| 104 | mg_printf_data(conn, "%s", "Hello world"); | |
| 105 | return MG_TRUE; // Mark as processed | |
| 106 | } else { | |
| 107 | return MG_FALSE; // Rest of the events are not processed | |
| 108 | } | |
| 109 | } | |
| 110 | ||
| 111 | int main(void) { | |
| 112 | struct mg_server *server = mg_create_server(NULL, event_handler); | |
| 113 | mg_set_option(server, "document_root", "."); | |
| 114 | mg_set_option(server, "listening_port", "8080"); | |
| 115 | ||
| 116 | for (;;) { | |
| 117 | mg_poll_server(server, 1000); // Infinite loop, Ctrl-C to stop | |
| 118 | } | |
| 119 | mg_destroy_server(&server); | |
| 120 | ||
| 121 | return 0; | |
| 122 | } | |
| 123 | ||
| 124 | ## Example code | |
| 125 | ||
| 126 | Mongoose source code contains number of examples, located in the | |
| 127 | [examples](https://github.com/cesanta/mongoose/blob/master/examples/) directory. | |
| 128 | To build any example, go to the respective directory and run `make`. | |
| 129 | ||
| 130 | ## Compilation flags | |
| 131 | ||
| 132 | Below is the list of compilation flags that enable or disable certain | |
| 133 | features. By default, some features are enabled, and could be disabled | |
| 134 | by setting appropriate `NO_*` flag. Features that are disabled by default | |
| 135 | could be enabled by setting appropriate `USE_*` flag. Bare bones Mongoose | |
| 136 | is quite small, about 30 kilobytes of compiled x86 code. Each feature adds | |
| 137 | a couple of kilobytes to the executable size, and also has some runtime penalty. | |
| 138 | ||
| 139 | Note that some flags start with `NS_` prefix. This is because Mongoose uses | |
| 140 | [Net Skeleton](http://github.com/cesanta/net_skeleton) as a low-level | |
| 141 | networking engine. If user code has `#include <net_skeleton.h>`, then | |
| 142 | all Net Skeleton functions will be available too. | |
| 143 | ||
| 144 | ||
| 145 | -DMONGOOSE_NO_AUTH Disable MD5 authorization support | |
| 146 | -DMONGOOSE_NO_CGI Disable CGI support | |
| 147 | -DMONGOOSE_NO_DAV Disable WebDAV support | |
| 148 | (PUT, DELETE, MKCOL, PROPFIND methods) | |
| 149 | -DMONGOOSE_NO_DIRECTORY_LISTING Disable directory listing | |
| 150 | -DMONGOOSE_NO_FILESYSTEM Disables all file IO, serving from memory only | |
| 151 | -DMONGOOSE_NO_LOGGING Disable access/error logging | |
| 152 | -DMONGOOSE_ENABLE_THREADS Enable mg_start_thread() function | |
| 153 | -DMONGOOSE_NO_WEBSOCKET Disable WebSocket support | |
| 154 | -DMONGOOSE_NO_USER No concept of a user on used platform. | |
| 155 | (Platform does not provide getpwnam, setgid or setuid) | |
| 156 | ||
| 157 | -DMONGOOSE_USE_IDLE_TIMEOUT_SECONDS=X Idle connection timeout, default is 30 | |
| 158 | -DMONGOOSE_USE_LUA Enable Lua scripting | |
| 159 | -DMONGOOSE_USE_LUA_SQLITE3 Enable sqlite3 binding for Lua | |
| 160 | -DMONGOOSE_USE_POST_SIZE_LIMIT=X POST requests larger than X will be | |
| 161 | rejected, not set by default | |
| 162 | -DMONGOOSE_USE_EXTRA_HTTP_HEADERS=X Append X to the HTTP headers | |
| 163 | for static files, empty by default | |
| 164 | ||
| 165 | -DNS_ENABLE_DEBUG Enables debug messages on stdout, very noisy | |
| 166 | -DNS_ENABLE_SSL Enable SSL | |
| 167 | -DNS_ENABLE_IPV6 Enable IPv6 support | |
| 168 | -DNS_ENABLE_HEXDUMP Enables hexdump of sent and received traffic | |
| 169 | -DNS_STACK_SIZE=X Sets stack size to X for ns_start_thread() | |
| 170 | -DNS_DISABLE_THREADS Disable threads support | |
| 171 | -DNS_DISABLE_SOCKETPAIR For systems without loopback interface | |
| 172 | -DMONGOOSE_SEND_NS_EVENTS Send Net Skeleton events to the event handler | |
| 173 | in addition to the Mongoose events |
| r0 | r250110 | |
|---|---|---|
| 1 | # Mongoose FAQ | |
| 2 | ||
| 3 | ## My Antivirus Software reports Mongoose as a security threat | |
| 4 | ||
| 5 | Mongoose doesn't contain any malicious logic. Antivirus reports a | |
| 6 | [false positive](http://en.wikipedia.org/wiki/Type_I_and_type_II_errors#False_positive_error). | |
| 7 | This is when certain byte sequence in Mongoose accidentally matches | |
| 8 | virus signature in the Antivirus database. | |
| 9 | ||
| 10 | ## Download page doesn't work | |
| 11 | ||
| 12 | Please make sure Javascript is enabled in your browser, and that the | |
| 13 | antivirus software is not blocking the download. | |
| 14 | ||
| 15 | ||
| 16 | ## MacOS message: "Mongoose.app is damaged and can’t be opened. You should move it to the Trash" | |
| 17 | ||
| 18 | This happens on newer MacOS systems. The reason for the message | |
| 19 | is the fact Mongoose.app is not digitally signed. | |
| 20 | Mongoose download procedure changes the app on the fly by injecting | |
| 21 | user information in the binary, making any prior digital signature void. | |
| 22 | Open "System Preferences" -> "Security" and set "Allow apps downloaded from" | |
| 23 | to "Anywhere". Revert the settings once Mongoose is installed. | |
| 24 | ||
| 25 | ## PHP doesn't work: getting empty page, or 'File not found' error | |
| 26 | ||
| 27 | The reason for that is wrong paths to the interpreter. Remember that with PHP, | |
| 28 | correct interpreter is `php-cgi.exe` (`php-cgi` on UNIX). Solution: specify | |
| 29 | full path to the PHP interpreter, e.g.: | |
| 30 | ||
| 31 | mongoose -cgi_interpreter /full/path/to/php-cgi | |
| 32 | ||
| 33 | ## Mongoose fails to start | |
| 34 | ||
| 35 | If Mongoose exits immediately when run, this | |
| 36 | usually indicates a syntax error in the configuration file | |
| 37 | (named `mongoose.conf` by default) or the command-line arguments. | |
| 38 | Syntax checking is omitted from Mongoose to keep its size low. However, | |
| 39 | the Manual should be of help. Note: the syntax changes from time to time, | |
| 40 | so updating the config file might be necessary after executable update. | |
| 41 | ||
| 42 | ### Embedding with OpenSSL on Windows might fail because of calling convention | |
| 43 | ||
| 44 | To force Mongoose to use `__stdcall` convention, add `/Gz` compilation | |
| 45 | flag to the Visual Studio project settings. |
| r0 | r250110 | |
|---|---|---|
| 1 | How To Share Files With Mongoose | |
| 2 | =========================================== | |
| 3 | ||
| 4 | ## 1. Download Mongoose executable from http://cesanta.com/mongoose.shtml and copy the executable inside the directory you want to share: | |
| 5 | ||
| 6 |  | |
| 7 | ||
| 8 | ## 2. Double-click mongoose executable. A browser will start automatically, an icon will appear on a system tray in the bottom right corner of the desktop: | |
| 9 | ||
| 10 |  | |
| 11 | ||
| 12 | ## 3. Click on the mongoose icon | |
| 13 |  | |
| 14 | ||
| 15 | ||
| 16 | ## 4. Click on "Go to my address" to launch a browser locally. Or, to access a folder from another machine, launch a browser and type in the URL: | |
| 17 | ||
| 18 |  |
| r0 | r250110 | |
|---|---|---|
| 1 | # Mongoose Internals | |
| 2 | ||
| 3 | Mongoose has single-threaded, event-driven, asynchronous, non-blocking core. | |
| 4 | `mg_create_server()` creates a web server instance. An instance is a container | |
| 5 | for the config options and list of active connections. To do the actual | |
| 6 | serving, user must call `mg_poll_server()`, which iterates over all | |
| 7 | active connections, performing `select()` syscall on all sockets with a | |
| 8 | timeout of specified number of milliseconds. When `select()` returns, Mongoose | |
| 9 | does an IO for each socket that has data to be sent or received. Application | |
| 10 | code must call `mg_poll_server()` in a loop. | |
| 11 | ||
| 12 | Mongoose server instance is designed to be used by a single thread. | |
| 13 | It is an error to have more then | |
| 14 | one thread calling `mg_poll_server()`, `mg_set_option()` or any other function | |
| 15 | that take `struct mg_server *` parameter. Mongoose does not | |
| 16 | mutex-protect `struct mg_server *`, therefore the best practice is | |
| 17 | to call server management functions from the same thread (an IO thread). | |
| 18 | On a multi-core systems, many server instances can be created, sharing the | |
| 19 | same listening socket and managed by separate threads (see [multi_threaded.c](https://github.com/cesanta/mongoose/blob/master/examples/multi_threaded.c)) | |
| 20 | example. | |
| 21 | ||
| 22 | It is an error to pass and store `struct mg_connection *` pointers for | |
| 23 | later use to send data. The reason is that they can be invalidated by the | |
| 24 | next `mg_poll_server()` call. For such a task, | |
| 25 | there is `mg_iterate_over_connections()` API | |
| 26 | exists, which sends a callback function to the IO thread, then IO thread | |
| 27 | calls specified function for all active connection. | |
| 28 | ||
| 29 | When mongoose buffers in HTTP request and successfully parses it, it calls | |
| 30 | appropriate URI handler immediately for GET requests. For POST requests, | |
| 31 | Mongoose delays the call until the whole POST request is buffered in memory. | |
| 32 | POST data is available to the callback as `struct mg_connection::content`, | |
| 33 | and POST data length is in `struct mg_connection::content_len`. | |
| 34 | ||
| 35 | Note that websocket connections are treated the same way. Mongoose buffers | |
| 36 | websocket frame in memory, and calls URI handler when frame is fully | |
| 37 | buffered. Frame data is available `struct mg_connection::content`, and | |
| 38 | data length is in `struct mg_connection::content_len`, i.e. very similar to | |
| 39 | the POST request. `struct mg_connection::is_websocket` flag indicates | |
| 40 | whether the request is websocket or not. Also, for websocket requests, | |
| 41 | there is `struct mg_connection::wsbits` field which contains first byte | |
| 42 | of the websocket frame which URI handler can examine. Note that to | |
| 43 | reply to the websocket client, `mg_websocket_write()` should be used. | |
| 44 | To reply to the plain HTTP client, `mg_write()` should be used. |
| r0 | r250110 | |
|---|---|---|
| 1 | # Mongoose Configuration Options | |
| 2 | ||
| 3 | ### access\_control\_list | |
| 4 | An Access Control List (ACL) allows restrictions to be put on the list of IP | |
| 5 | addresses which have access to the web server. In the case of the Mongoose | |
| 6 | web server, the ACL is a comma separated list of IP subnets, where each | |
| 7 | subnet is prepended by either a `-` or a `+` sign. A plus sign means allow, | |
| 8 | where a minus sign means deny. If a subnet mask is omitted, such as `-1.2.3.4`, | |
| 9 | this means to deny only that single IP address. | |
| 10 | ||
| 11 | Subnet masks may vary from 0 to 32, inclusive. The default setting is to allow | |
| 12 | all accesses. On each request the full list is traversed, and | |
| 13 | the last match wins. Example: `$ mongoose -access_control_list -0.0.0.0/0,+192.168/16` to deny all acccesses except those from `192.168/16` subnet. Note that if the option is set, then all accesses are forbidden | |
| 14 | by default. Thus in a previous example, `-0.0.0.0` part is not necessary. | |
| 15 | For example, `$mongoose access_control_list +10.0.0.0/8` | |
| 16 | means disallow all, allow subnet 10/8 only. | |
| 17 | ||
| 18 | To learn more about subnet masks, see the | |
| 19 | [Wikipedia page on Subnetwork](http://en.wikipedia.org/wiki/Subnetwork) | |
| 20 | ||
| 21 | Default: not set, all accesses are allowed. | |
| 22 | ||
| 23 | ### access\_log\_file | |
| 24 | Path to a file for access logs. Either full path, or relative to the | |
| 25 | mongoose executable. Default: not set, no query logging is done. | |
| 26 | ||
| 27 | ### auth_domain | |
| 28 | Authorization realm used in `.htpasswd` authorization. Default: `mydomain.com` | |
| 29 | ||
| 30 | ### cgi_interpreter | |
| 31 | Path to an executable to be used use as an interpreter for __all__ CGI scripts | |
| 32 | regardless script extension. Default: not set, Mongoose looks at | |
| 33 | [shebang line](http://en.wikipedia.org/wiki/Shebang_(Unix\). | |
| 34 | ||
| 35 | For example, if both PHP and perl CGIs are used, then | |
| 36 | `#!/path/to/php-cgi.exe` and `#!/path/to/perl.exe` must be first lines of the | |
| 37 | respective CGI scripts. Note that paths should be either full file paths, | |
| 38 | or file paths relative to the directory where mongoose executable is located. | |
| 39 | ||
| 40 | If all CGIs use the same interpreter, for example they are all PHP, then | |
| 41 | `cgi_interpreter` option can be set to the path to `php-cgi.exe` executable and | |
| 42 | shebang line in the CGI scripts can be omitted. | |
| 43 | **Note**: PHP scripts must use `php-cgi.exe`, not `php.exe`. | |
| 44 | ||
| 45 | ### cgi_pattern | |
| 46 | All files that match `cgi_pattern` are treated as CGI files. Default pattern | |
| 47 | allows CGI files be anywhere. To restrict CGIs to a certain directory, | |
| 48 | use `/path/to/cgi-bin/**.cgi` as a pattern. Note that **full file path** is | |
| 49 | matched against the pattern, not the URI. | |
| 50 | ||
| 51 | When Mongoose starts CGI program, it creates new environment for it (in | |
| 52 | contrast, usually child program inherits the environment from parent). Several | |
| 53 | environment variables however are inherited from Mongoose's environment, | |
| 54 | they are: `PATH`, `TMP`, `TEMP`, `TMPDIR`, `PERLLIB`, `MONGOOSE_CGI`. On UNIX | |
| 55 | it is also `LD_LIBRARY_PATH`. On Windows it is also `COMSPEC`, `SYSTEMROOT`, | |
| 56 | `SystemDrive`, `ProgramFiles`, `ProgramFiles(x86)`, `CommonProgramFiles(x86)`. | |
| 57 | ||
| 58 | Default: `**.cgi$|**.pl$|**.php$` | |
| 59 | ||
| 60 | ### dav\_auth\_file | |
| 61 | Authentication file for WebDAV mutation requests: `PUT`, `DELETE`, `MKCOL`. | |
| 62 | The format of that file is the same as for the `.htpasswd` file | |
| 63 | used for digest authentication. It can be created and managed by | |
| 64 | `mongoose -A` command. Default: not set, WebDAV mutations are disallowed. | |
| 65 | ||
| 66 | ### document_root | |
| 67 | A directory to serve. Default: current working directory. | |
| 68 | ||
| 69 | ### enable\_directory\_listing | |
| 70 | Enable directory listing, either `yes` or `no`. Default: `yes`. | |
| 71 | ||
| 72 | ### enable\_proxy | |
| 73 | Enable proxy functionality, either `yes` or `no`. If set to `yes`, then | |
| 74 | browsers can be configured to use Mongoose as a proxy. Default: `no`. | |
| 75 | ||
| 76 | ||
| 77 | ### extra\_mime\_types | |
| 78 | Extra mime types to recognize, in form `extension1=type1,extension2=type2,...`. | |
| 79 | Extension must include dot. Example: | |
| 80 | `mongoose -extra_mime_types .cpp=plain/text,.java=plain/text`. Default: not set. | |
| 81 | ||
| 82 | ||
| 83 | ### global\_auth\_file | |
| 84 | Path to a global passwords file, either full path or relative to the mongoose | |
| 85 | executable. If set, per-directory `.htpasswd` files are ignored, | |
| 86 | and all requests are authorised against that file. Use `mongoose -A` to | |
| 87 | manage passwords, or third party utilities like | |
| 88 | [htpasswd-generator](http://www.askapache.com/online-tools/htpasswd-generator). | |
| 89 | Default: not set, per-directory `.htpasswd` files are respected. | |
| 90 | ||
| 91 | ### hide\_files\_patterns | |
| 92 | A pattern for the files to hide. Files that match the pattern will not | |
| 93 | show up in directory listing and return `404 Not Found` if requested. Pattern | |
| 94 | must be for a file name only, not including directory name, e.g. | |
| 95 | `mongoose -hide_files_patterns secret.txt|even_more_secret.txt`. Default: | |
| 96 | not set. | |
| 97 | ||
| 98 | ### index_files | |
| 99 | Comma-separated list of files to be treated as directory index | |
| 100 | files. Default: `index.html,index.htm,index.cgi,index.shtml,index.php` | |
| 101 | ||
| 102 | ### listening_port | |
| 103 | Port to listen on. Port could be prepended by the specific IP address to bind | |
| 104 | to, e.g. `mongoose -listening_port 127.0.0.1:8080`. Otherwise Mongoose | |
| 105 | will bind to all addresses. To enable SSL, build Mongoose with | |
| 106 | `-DNS_ENABLE_SSL` compilation option, and specify `listening_port` as | |
| 107 | `ssl://PORT:SSL_CERTIFICATE.PEM`. Example SSL listener: | |
| 108 | `mongoose -listening_port ssl://8043:ssl_cert.pem`. Note that PEM file should | |
| 109 | be in PEM format, and must have both certificate and private key in it, | |
| 110 | concatenated together. More than one listening port can be specified, | |
| 111 | separated by comma, | |
| 112 | for example `mongoose -listening_port 8080,8000`. Default: 8080. | |
| 113 | ||
| 114 | ### run\_as\_user | |
| 115 | Switch to given user credentials after startup. UNIX-only. This option is | |
| 116 | required when mongoose needs to bind on privileged port on UNIX, e.g. | |
| 117 | ||
| 118 | $ sudo mongoose -listening_port 80 -run_as_user nobody | |
| 119 | ||
| 120 | Default: not set. | |
| 121 | ||
| 122 | ### url\_rewrites | |
| 123 | Comma-separated list of URL rewrites in the form of | |
| 124 | `uri_pattern=file_or_directory_path`. When Mongoose receives the request, | |
| 125 | it constructs the file name to show by combining `document_root` and the URI. | |
| 126 | However, if the rewrite option is used and `uri_pattern` matches the | |
| 127 | requested URI, then `document_root` is ignored. Instead, | |
| 128 | `file_or_directory_path` is used, which should be a full path name or | |
| 129 | a path relative to the web server's current working directory. Note that | |
| 130 | `uri_pattern`, as all mongoose patterns, is a prefix pattern. If `uri_pattern` | |
| 131 | is a number, then it is treated as HTTP error code, and `file_or_directory_path` | |
| 132 | should be an URI to redirect to. Mongoose will issue `302` temporary redirect | |
| 133 | to the specified URI with following parameters: | |
| 134 | `?code=HTTP_ERROR_CODE&orig_uri=ORIGINAL_URI&query_string=QUERY_STRING`. | |
| 135 | ||
| 136 | If `uri_pattern` starts with `@` symbol, then Mongoose compares | |
| 137 | it with the `HOST` header of the request. If they are equal, Mongoose sets | |
| 138 | document root to `file_or_directory_path`, implementing virtual hosts support. | |
| 139 | ||
| 140 | Examples: | |
| 141 | ||
| 142 | # Redirect all accesses to `.doc` files to a special script | |
| 143 | mongoose -url_rewrites **.doc$=/path/to/cgi-bin/handle_doc.cgi | |
| 144 | ||
| 145 | # Implement user home directories support | |
| 146 | mongoose -url_rewrites /~joe/=/home/joe/,/~bill=/home/bill/ | |
| 147 | ||
| 148 | # Redirect 404 errors to a specific error page | |
| 149 | mongoose -url_rewrites 404=/cgi-bin/error.cgi | |
| 150 | ||
| 151 | # Virtual hosts example: serve foo.com domain from different directory | |
| 152 | mongoose -url_rewrites @foo.com=/var/www/foo.com | |
| 153 | ||
| 154 | Default: not set. |
| r0 | r250110 | |
|---|---|---|
| 1 | How To Create A PHP Website With Mongoose | |
| 2 | =========================================== | |
| 3 | ||
| 4 | ## 1. Create a directory which will contain your website files. For example, on drive `C:\`, create a directory called `my_website`: | |
| 5 | ||
| 6 |  | |
| 7 | ||
| 8 | ## 2. Inside `my_website` directory, create a new file called "index". This will be the default web page shown when the website is visited. | |
| 9 | ||
| 10 |  | |
| 11 | ||
| 12 | ## 3. Open index file with your favorite editor (for example, Notepad) and enter some HTML / PHP code: | |
| 13 | ||
| 14 |  | |
| 15 | ||
| 16 | ## 4. Save this file as `index.php`: | |
| 17 | ||
| 18 |  | |
| 19 | ||
| 20 | ||
| 21 | ## 5. Download Mongoose executable from http://cesanta.com/mongoose.shtml and copy the executable inside `my_website` directory: | |
| 22 | ||
| 23 |  | |
| 24 | ||
| 25 | ## 6. Double-click mongoose executable. An icon will appear on a system tray in the bottom right corner of the desktop: | |
| 26 | ||
| 27 |  | |
| 28 | ||
| 29 | ## 7. Download PHP 5.3 zip (do NOT download PHP 5.5 cause you might have missing DLLs problem) from http://windows.php.net/download and extract it to `C:\php5` directory: | |
| 30 |  | |
| 31 | ||
| 32 | ## 8. Click on the mongoose icon and choose "Edit Settings" menu.: | |
| 33 |  | |
| 34 | ||
| 35 | ## 9. A settings dialog will appear. Click on `cgi_interpreter` button: | |
| 36 | ||
| 37 |  | |
| 38 | ||
| 39 | ## 10. Choose `C:\php5\php-cgi.exe` and click "Save Settings": | |
| 40 | ||
| 41 |  | |
| 42 | ||
| 43 | ## 11. Click on the mongoose icon and choose "Go to my address" menu: | |
| 44 |  | |
| 45 | ||
| 46 | ||
| 47 | ## 12. A browser will popup displaying `index.php`. | |
| 48 | ||
| 49 |  |
| r0 | r250110 | |
|---|---|---|
| 1 | # Mongoose Release Notes | |
| 2 | ||
| 3 | ## Release 5.6, 2015-03-17 | |
| 4 | ||
| 5 | Changes in Libmongoose library: | |
| 6 | ||
| 7 | - Added `-dav_root` configuration option that gives an ability to mount | |
| 8 | a different root directory (not document_root) | |
| 9 | - Fixes for build under Win23 and MinGW | |
| 10 | - Bugfix: Double dots removal | |
| 11 | - Bugfix: final chunked response double-send | |
| 12 | - Fixed compilation in 64-bit environments | |
| 13 | - Added OS/2 compatibility | |
| 14 | - Added `getaddrinfo()` call and `NS_ENABLE_GETADDRINFO` | |
| 15 | - Various SSL-related fixes | |
| 16 | - Added integer overflow protection in `iobuf_append()` and `deliver_websocket_frame()` | |
| 17 | - Fixed NetBSD build | |
| 18 | - Enabled `NS_ENABLE_IPV6` build for Visual Studio 2008+ | |
| 19 | - Enhanced comma detection in `parse_header()` | |
| 20 | - Fixed unchanged memory accesses on ARM | |
| 21 | - Added ability to use custom memory allocator through NS_MALLOC, NS_FREE, NS_REALLOC | |
| 22 | ||
| 23 | Changes in Mongoose binary: | |
| 24 | ||
| 25 | - Added `-start_browser` option to disable automatic browser launch | |
| 26 | - Added experimental SSL support. To listen on HTTPS port, use `ssl://PORT:SSL_CERT` format. For example, to listen on HTTP port 8080 and HTTPS port 8043, use `-listening_port 8080,ssl://8043:ssl_cert.pem` | |
| 27 | ||
| 28 | ## Release 5.5, October 28 2014 | |
| 29 | ||
| 30 | Changes in Libmongoose library: | |
| 31 | ||
| 32 | - Added new API function: `mg_forward()` for proxying functionality | |
| 33 | - Added new API function: `mg_send_file_data()` for sending file data | |
| 34 | - Added new utility API functions: `mg_mmap() and mg_munmap()` | |
| 35 | - Changed the way SSL settings are handled: removed `ssl_certificate` and | |
| 36 | `ssl_ca_certificate` options, and instead made `listening_port` accept | |
| 37 | `ssl://PORT:SSL_CERT:CA_CERT` notation | |
| 38 | - Added ability to listen on multiple ports, see `listening_port` documentation | |
| 39 | - Added `enable_proxy` option | |
| 40 | - Added [cookie_authentication](https://github.com/cesanta/mongoose/tree/master/examples/cookie_authentication) example | |
| 41 | - Added [websocket\_ssl\_proxy](https://github.com/cesanta/mongoose/tree/master/examples/websocket_ssl_proxy) example | |
| 42 | - Added [http_client](https://github.com/cesanta/mongoose/tree/master/examples/http_client) example | |
| 43 | - Increased default 'idle connection' timeout from 30 to 300 seconds | |
| 44 | - Fixed MinGW build | |
| 45 | - Refactored all examples, put each in it's own directory with dedicated build | |
| 46 | - Many smaller bugfixed, including SSL, CGI, API, proxy, etc | |
| 47 | ||
| 48 | Changes in pre-compiled binaries: | |
| 49 | ||
| 50 | - Support for multiple listening ports | |
| 51 | - Fixed CGI handling for scripts that specify interpreter in the hashbang line | |
| 52 | ||
| 53 | ## Release 5.4, July 28 2014 | |
| 54 | ||
| 55 | Changes in Libmongoose library: | |
| 56 | ||
| 57 | - Added `hexdump_file` option for low-level request/reply debugging | |
| 58 | - Added `mg_template()` API function for generating HTML pages from | |
| 59 | templates with expansions | |
| 60 | - Fixed `struct mg_connection::local_ip` handling, `mg_set_option()` | |
| 61 | behavior with NULL values | |
| 62 | - Added `mg_send_file()` call to send arbitrary file to the client | |
| 63 | - Added `mg_terminate_ssl()` for SSL termination functionality | |
| 64 | - Added HTTP proxy support, `enable_proxy` config option | |
| 65 | - Added `mg_next()` for iterating over existing active connections | |
| 66 | - Added client-side SSL auth, `ssl_ca_certificate` option | |
| 67 | - Added `mg_wakeup_server_ex()` for pushing messages to existing connections | |
| 68 | - Added `MG_WS_HANDSHAKE` and `MG_WS_CONNECT` events that are sent on | |
| 69 | Websocket handshake is connection establishment, respectively | |
| 70 | - Removed server-side Lua support | |
| 71 | - Filesystem access, reading from socket/SSL performance improvements | |
| 72 | - DAV PROPFIND memory leak fixed | |
| 73 | - Added `big_upload.c` and enhanced `upload.c` example | |
| 74 | - Added `proxy.c` example that demonstrates proxy functionality and SSE pushes | |
| 75 | - Added `websocket2.c` example that shows simple web chat implementation | |
| 76 | over websockets | |
| 77 | - Various minor fixes | |
| 78 | ||
| 79 | ||
| 80 | Changes in pre-compiled binaries: | |
| 81 | ||
| 82 | - Created HTML administration console | |
| 83 | - When server is started, browser is started automatically | |
| 84 | - Fixed directory listing bug when directory contains `#` character | |
| 85 | - Removed built-in Lua Server Pages in the binary, and instead | |
| 86 | added Mongoose + Lua developer bundle which has Lua Server Pages support. | |
| 87 | That also solves external Lua modules loading problem. | |
| 88 | ||
| 89 | ||
| 90 | ## Release 5.3, March 10 2014 | |
| 91 | ||
| 92 | Changes in Libmongoose library: | |
| 93 | ||
| 94 | * Moved to the evented API. Updated API documentation is at | |
| 95 | http://cesanta.com/docs/Embed.shtml | |
| 96 | http://cesanta.com/docs/API.shtml | |
| 97 | * Added `MG_LUA` event for exporting custom variables to the Lua environment | |
| 98 | * Added virtual hosts capability, see `url_rewrites` option description at | |
| 99 | http://cesanta.com/docs/Options.shtml | |
| 100 | * Added mjpg serving example | |
| 101 | * Cleaned up and documented HTTP client API, with unit tests | |
| 102 | * Added `mg_wakeup_server()` to awaken `mg_poll_server()` | |
| 103 | from another thread | |
| 104 | * Moved Mongoose IO core to [https://github.com/cesanta/net_skeleton](Net Skeleton) | |
| 105 | * Added connection hexdump functionality for developers | |
| 106 | * Bug fixes | |
| 107 | ||
| 108 | Changes in pre-compiled binaries: | |
| 109 | ||
| 110 | * New awesome Mongoose logos by our designer Katrin - thanks Katrin! | |
| 111 | Check them out at http://cesanta.com/products.shtml | |
| 112 | * Added Lua Server Pages support to the free version, quick intro is at | |
| 113 | http://cesanta.com/docs/Lua.shtml | |
| 114 | * Added quick "Set shared directory" menu item to set `document_root` | |
| 115 | * Added SSI support to the Pro version | |
| 116 | * Removed SSL support from the Pro version | |
| 117 | ||
| 118 | ## Release 5.2, Feb 1 2014 | |
| 119 | ||
| 120 | * Windows binary made fully UNICODE aware. In previous versions, | |
| 121 | the presence of non-ASCII chars in document root, CGI script name, | |
| 122 | or directory name might have broken Mongoose as stand-alone | |
| 123 | or as Windows service. Now Mongoose works with non-ASCII paths properly. | |
| 124 | Internally, Mongoose uses UTF8 encoding. When making WinAPI calls, | |
| 125 | mongoose converts UTF8 strings to wide chars and calls UNICODE API. | |
| 126 | * Enhanced authorization API by providing `mg_set_auth_handler()` and | |
| 127 | `mg_authorize_digest()` | |
| 128 | * Removed `mg_add_uri_handler()`, added `mg_set_request_handler()`. | |
| 129 | There is only oneURI handler that handles all requests, just like in 4.x. | |
| 130 | The reason for this change is to provide an ability to catch all URIs, | |
| 131 | and at the same time signal Mongoose to continue handling specific URIs. | |
| 132 | * Added `mg_parse_multipart()` API for file uploads. | |
| 133 | Note that the restriction on uploading huge files still exists, | |
| 134 | and will be eliminated in the next release. | |
| 135 | * Allowing mongoose to bind to port 0, in which case it'll bind to any | |
| 136 | random unused port. | |
| 137 | * Moved `idle_timeout_ms` run-time option to compile-time flag | |
| 138 | * Added asynchronous HTTP client, not documented yet. Documentation and | |
| 139 | examples are coming in the next couple of weeks. Async Websocket client | |
| 140 | is scheduled for the next release. See usage examples at `unit_test.c` | |
| 141 | * Windows and MacOS pre-built binaries are now split to free and paid ones, | |
| 142 | paid binaries include CGI, SSL, Lua, Sqlite, support and updates. | |
| 143 | Linux pre-built binary includes all functionality and is free, and will | |
| 144 | continue to be free. Source code for Windows and MacOS GUI is closed. | |
| 145 | Disclaimer: source code for the command line stand-alone server, | |
| 146 | as well as Mongoose library itself, will never be closed. | |
| 147 | * Multiple bug fixes and minor enhancements | |
| 148 | ||
| 149 | ## Release 5.1, Jan 10 2014 | |
| 150 | ||
| 151 | * CGI-related bugs where fixed, primarily for Windows platform | |
| 152 | * Bugs on Windows related to UNICODE support were fixed | |
| 153 | * Added a feature to support "error pages" through redirect. | |
| 154 | Done using `-url_redirects` option, details are on | |
| 155 | http://cesanta.com/docs/Options.shtml | |
| 156 | ||
| 157 | ## Release 5.0, Jan 6 2014 | |
| 158 | ||
| 159 | * Internal core has been changed from blocking, thread-per-connection to | |
| 160 | non-blocking, asynchronous, one thread for all. | |
| 161 | * API modification for server creation and response creation. That allowed | |
| 162 | keep-alive support for dynamic requests, boosting the embedded performance | |
| 163 | to 100+ thousands requests per second on a single core | |
| 164 | (as measured on my development MacBook laptop) | |
| 165 | * Unified handling of POST requests and Websocket requests by putting a | |
| 166 | payload into `conn->content`, `conn->content_len` attributes. | |
| 167 | That simplified user code and eliminated the need of `mg_read()`, | |
| 168 | since mongoose buffers all data prior to calling the callback | |
| 169 | * keep-alive support is the default | |
| 170 | * Dropped SSI support and throttling support | |
| 171 | * Several configuration parameters are gone: | |
| 172 | * `cgi_environment` (replaced with MONGOOSE_CGI), | |
| 173 | * `protect_uri` (not useful) | |
| 174 | * `ssi_pattern` (SSI support is gone) | |
| 175 | * `throttle` (throttling support is gone) | |
| 176 | * `error_log_file` (not used) | |
| 177 | * `enable_keep_alive` (enabled by default) | |
| 178 | * `listening_ports` (renamed to listening_port) | |
| 179 | * `num_threads` (core has changed to single thread) | |
| 180 | * `put_delete_auth_file` (renamed to dav_auth_file) | |
| 181 | * `authentication_domain` (renamed to auth_domain) | |
| 182 | * Due to the async, non-blocking nature of the core, few restrictions | |
| 183 | are now in place: | |
| 184 | * user callbacks must not block | |
| 185 | * POST and Websocket data are now buffered, and cannot be huge | |
| 186 | * mongoose is now capable on listening on only one port | |
| 187 | ||
| 188 | ## Release 4.1, Oct 2013 | |
| 189 | ## Release 4.0, Oct 2013 | |
| 190 | ## Release 3.8, Sep 2013 | |
| 191 | ||
| 192 | ## Release 3.7, Feb 2 2013 | |
| 193 | ||
| 194 | * Added "redirect to SSL port" functionality, e.g. if you specify | |
| 195 | `-listening_ports 8080r,8043s` | |
| 196 | then all requests to HTTP port 8080 will be redirected to HTTPS port 8043 | |
| 197 | * Added `mg_download()` API, an HTTP client interface! | |
| 198 | * Lua server pages now must output HTTP headers -- full control for Lua | |
| 199 | * Added pre-built binary for MacOS, with initial GUI support | |
| 200 | * API change: got rid of events, moved to struct `mg_callbacks` | |
| 201 | * Bugfixes, thanks to contributors | |
| 202 | ||
| 203 | ||
| 204 | ## Release 3.7, Jan 18 2013 | |
| 205 | * Fixed source code archive (main.c was missing) | |
| 206 | * Extended Windows GUI functionality: | |
| 207 | * Added "Start browser" systray popup menu item | |
| 208 | * Enhanced configuration editor | |
| 209 | * Renamed config options: | |
| 210 | * `put_delete_passwords_file` -> `put_delete_auth_file` | |
| 211 | * `global_passwords_file` -> `global_auth_file` | |
| 212 | * `select()` changed to `poll()`, to avoid big file descriptor | |
| 213 | `FD_SET` problem on UNIX | |
| 214 | * Couple of bugfixes, thanks to contributors | |
| 215 | ||
| 216 | ||
| 217 | Earlier release notes could be found by searching | |
| 218 | [Mongoose mailing list](https://groups.google.com/forum/#!forum/mongoose-users) |
| r0 | r250110 | |
|---|---|---|
| 1 | # Mongoose SSL guide | |
| 2 | ||
| 3 | SSL is a protocol that makes web communication secure. To enable SSL | |
| 4 | in mongoose, 2 steps are required: | |
| 5 | ||
| 6 | 1. Create valid SSL certificate file | |
| 7 | 2. Append SSL certificate file path to the `listening_ports` option | |
| 8 | ||
| 9 | Below is the `mongoose.conf` file snippet for typical SSL setup: | |
| 10 | ||
| 11 | document_root www_root # Serve files in www_root directory | |
| 12 | listening_ports 80,443:cert.pem # Listen on ports 80 and 443 | |
| 13 | ||
| 14 | ## How to create SSL certificate file | |
| 15 | ||
| 16 | SSL certificate file is a text file that must contain at least two | |
| 17 | sections: | |
| 18 | ||
| 19 | 1. A private key | |
| 20 | 2. A certificate | |
| 21 | ||
| 22 | Both sections should be chunks of text in PEM format. When PEM file is | |
| 23 | opened in a text editor, it looks like this: | |
| 24 | ||
| 25 | -----BEGIN RSA PRIVATE KEY----- | |
| 26 | MIIEogIBAAKCAQEAwONaLOP7EdegqjRuQKSDXzvHmFMZfBufjhELhNjo5KsL4ieH | |
| 27 | hYN0Zii2yTb63jGxKY6gH1R/r9dL8kXaJmcZrfSa3AgywnteJWg= | |
| 28 | -----END RSA PRIVATE KEY----- | |
| 29 | -----BEGIN CERTIFICATE----- | |
| 30 | MIIDBjCCAe4CCQCX05m0b053QzANBgkqhkiG9w0BAQQFADBFMQswCQYDVQQGEwJB | |
| 31 | SEGI4JSxV56lYg== | |
| 32 | -----END CERTIFICATE----- | |
| 33 | ||
| 34 | Two aforementioned sections are clearly seen. Typically, those section | |
| 35 | are bigger then in the example shown. The text between the `BEGIN` and | |
| 36 | `END` is the text representation of binary data, a private key and a | |
| 37 | certificate. Therefore, in order to create a certificate file, | |
| 38 | ||
| 39 | * private key must be converted to PEM format | |
| 40 | * certificate must be converted to PEM format | |
| 41 | * those two should be concatenated into a single file | |
| 42 | ||
| 43 | If the certificate chain in used, a chain file also needs to be | |
| 44 | converted into PEM format and appended to the certificate file. | |
| 45 | ||
| 46 | ## How SSL works | |
| 47 | ||
| 48 | SSL is a protocol that can encrypt communication between two parties. If third | |
| 49 | party observes all messages passed by, it would be very | |
| 50 | hard for the third party (though not impossible) to decrypt the communication. | |
| 51 | ||
| 52 | The idea is based on so-called public key encryption. Communicating parties | |
| 53 | have two keys: a public key and a private key. A public key is advertised | |
| 54 | to everybody, and it is contained in a certificate. A private key is kept | |
| 55 | secret. Security algorithm works in a way that anybody can encrypt | |
| 56 | a message using public key, and only private key can decrypt it. | |
| 57 | ||
| 58 | This is why web server needs both private key and certificate: private key | |
| 59 | is used to decrypt incoming messages, and certificate is used to tell the | |
| 60 | public key to the other party. When communication starts, parties exchange | |
| 61 | their public keys, and keep private keys to themselves. Man-in-the-middle | |
| 62 | who observes the communication is unable to decrypt the messages cause | |
| 63 | private keys are required for decryption. | |
| 64 | ||
| 65 | Encryption algorithms are built on top of hard mathematical problem, which | |
| 66 | makes it very expensive for man-in-the-middle to compute private keys. | |
| 67 | For example, RSA algorithm is based on a mathematical problem of factorization. | |
| 68 | It is easy to generate two very large prime numbers `P` and `Q` and make | |
| 69 | a product `P * Q`. But given a product, it is very hard to recover these | |
| 70 | two prime numbers - this is called factorization. |
| r0 | r250110 | |
|---|---|---|
| 1 | # Mongoose User Guide | |
| 2 | ||
| 3 | Mongoose is small and easy to use web server built on top of | |
| 4 | mongoose library. It is designed with maximum simplicity in mind. For example, | |
| 5 | to share any directory, just drop mongoose executable in that directory, | |
| 6 | double-click it (on UNIX, run it from shell) and launch a browser at | |
| 7 | [http://localhost:8080](http://localhost:8080) Note that 'localhost' should | |
| 8 | be changed to a machine's name if a folder is accessed from other computer. | |
| 9 | ||
| 10 | On Windows and Mac, Mongoose iconifies itself to the system tray when started. | |
| 11 | Right-click on the icon to pop up a menu, where it is possible to stop | |
| 12 | mongoose, or configure it. | |
| 13 | ||
| 14 | On UNIX, `mongoose` is a command line utility. Running `mongoose` in | |
| 15 | terminal, optionally followed by configuration parameters | |
| 16 | (`mongoose [OPTIONS]`) or configuration file name | |
| 17 | (`mongoose [config_file_name]`) starts the | |
| 18 | web server: | |
| 19 | ||
| 20 | $ mongoose -document_root /var/www # Running mongoose with cmdline options | |
| 21 | $ mongoose /etc/my_config.txt # Running mongoose with config file | |
| 22 | $ mongoose # Running with no parameters. This will | |
| 23 | # serve current directory on port 8080 | |
| 24 | ||
| 25 | Mongoose does not detach from terminal. Pressing `Ctrl-C` keys | |
| 26 | stops the server. | |
| 27 | ||
| 28 | When started, mongoose first searches for the configuration file. | |
| 29 | If configuration file is specified explicitly in the command line, then | |
| 30 | specified configuration file is used. | |
| 31 | Otherwise, mongoose would search for file `mongoose.conf` in the same directory | |
| 32 | where binary is located, and use it. Configuration file can be absent. | |
| 33 | ||
| 34 | Configuration file is a sequence of lines, each line containing | |
| 35 | command line argument name and it's value. Empty lines and lines beginning | |
| 36 | with `#` are ignored. Here is the example of `mongoose.conf` file: | |
| 37 | ||
| 38 | # This is a comment | |
| 39 | document_root C:\www | |
| 40 | listening_port 80 | |
| 41 | ssl_certificate C:\mongoose\ssl_cert.pem | |
| 42 | ||
| 43 | Command line arguments are highest priority and can override | |
| 44 | configuration file settings. For example, if `mongoose.conf` has line | |
| 45 | `document_root /var/www`, and mongoose has been started as | |
| 46 | `mongoose -document_root /etc`, then `/etc` directory will be used as | |
| 47 | document root. | |
| 48 | ||
| 49 | Note that configuration options on the command line must start with `-`, | |
| 50 | and their names are the same as in the config file. Exampli gratia, | |
| 51 | the following two setups are equivalent: | |
| 52 | ||
| 53 | $ mongoose -listening_port 1234 -document_root /var/www | |
| 54 | ||
| 55 | $ cat > mongoose.conf | |
| 56 | listening_ports 1234 | |
| 57 | document_root /var/www | |
| 58 | ^D | |
| 59 | $ mongoose | |
| 60 | ||
| 61 | Mongoose can also be used to modify `.htpasswd` passwords file: | |
| 62 | ||
| 63 | $ mongoose -A .htpasswd mydomain.com user_name user_password | |
| 64 | ||
| 65 | Unlike other web servers, mongoose does not require CGI scripts be located in | |
| 66 | a special directory. CGI scripts can be anywhere. CGI (and SSI) files are | |
| 67 | recognized by the file name pattern. Mongoose uses shell-like glob | |
| 68 | patterns. Pattern match starts at the beginning of the string, so essentially | |
| 69 | patterns are prefix patterns. Syntax is as follows: | |
| 70 | ||
| 71 | ** Matches everything | |
| 72 | * Matches everything but slash character, '/' | |
| 73 | ? Matches any character | |
| 74 | $ Matches the end of the string | |
| 75 | | Matches if pattern on the left side or the right side matches. | |
| 76 | ||
| 77 | All other characters in the pattern match themselves. Examples: | |
| 78 | ||
| 79 | # Pattern Meaning | |
| 80 | **.cgi$ Any string that ends with .cgi | |
| 81 | /foo Any string that begins with /foo | |
| 82 | **a$|**b$ Any string that ends with a or b | |
| 83 | ||
| 84 | To restrict CGI files only to `/cgi-bin/` directory, use this setting: | |
| 85 | ||
| 86 | $ mongoose -cgi_pattern /cgi-bin/*.cgi # Emulate /cgi-bin/ restriction |
| r0 | r250110 | |
|---|---|---|
| 1 | *.exe |
| r0 | r250110 | |
|---|---|---|
| 1 | # Copyright (c) 2014 Cesanta Software | |
| 2 | # All rights reserved | |
| 3 | ||
| 4 | SUBDIRS = $(sort $(filter-out csharp/, $(dir $(wildcard */)))) | |
| 5 | X = $(SUBDIRS) | |
| 6 | ifdef WINDIR | |
| 7 | # appending the Winsock2 library at the end of the compiler | |
| 8 | # invocation | |
| 9 | CFLAGS_EXTRA += -lws2_32 | |
| 10 | endif | |
| 11 | ||
| 12 | .PHONY: $(SUBDIRS) | |
| 13 | ||
| 14 | all: $(SUBDIRS) | |
| 15 | ||
| 16 | $(SUBDIRS): | |
| 17 | @$(MAKE) CFLAGS_EXTRA="$(CFLAGS_EXTRA)" -C $@ | |
| 18 | ||
| 19 | clean: | |
| 20 | for d in $(SUBDIRS) ; do $(MAKE) -C $$d clean ; done |
| r0 | r250110 | |
|---|---|---|
| 1 | # Copyright (c) 2014 Cesanta Software | |
| 2 | # All rights reserved | |
| 3 | ||
| 4 | PROG = array_vars | |
| 5 | CFLAGS = -W -Wall -I../.. -pthread -g -O0 $(CFLAGS_EXTRA) | |
| 6 | SOURCES = $(PROG).c ../../mongoose.c | |
| 7 | ||
| 8 | all: $(PROG) | |
| 9 | ||
| 10 | run: $(PROG) | |
| 11 | ./$(PROG) | |
| 12 | ||
| 13 | $(PROG): $(SOURCES) Makefile | |
| 14 | $(CC) -o $(PROG) $(SOURCES) $(CFLAGS) | |
| 15 | ||
| 16 | win: | |
| 17 | wine cl $(SOURCES) /MD /nologo /DNDEBUG /O1 /I../.. /Fe$(PROG).exe | |
| 18 | wine $(PROG).exe | |
| 19 | ||
| 20 | clean: | |
| 21 | rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib *.gc* |
| r0 | r250110 | |
|---|---|---|
| 1 | // Copyright (c) 2014 Cesanta Software | |
| 2 | // All rights reserved | |
| 3 | // | |
| 4 | // This example demostrates how to use array get variables using mg_get_n_var | |
| 5 | // $Date: 2014-09-09 22:20:23 UTC $ | |
| 6 | ||
| 7 | #include <stdio.h> | |
| 8 | #include <string.h> | |
| 9 | #include "mongoose.h" | |
| 10 | ||
| 11 | static int ev_handler(struct mg_connection *conn, enum mg_event ev) { | |
| 12 | switch (ev) { | |
| 13 | case MG_AUTH: return MG_TRUE; | |
| 14 | case MG_REQUEST: | |
| 15 | { | |
| 16 | mg_printf_data(conn, "Hello! Requested URI is [%s] ", conn->uri); | |
| 17 | char buffer[1024]; | |
| 18 | int i, ret; | |
| 19 | for(i=0; (ret = mg_get_var_n(conn, "foo[]", buffer, 1024, i)) > 0; i++) | |
| 20 | mg_printf_data(conn, "\nfoo[%d] = %s", i, buffer); | |
| 21 | ||
| 22 | return MG_TRUE; | |
| 23 | } | |
| 24 | default: return MG_FALSE; | |
| 25 | } | |
| 26 | } | |
| 27 | ||
| 28 | int main(void) { | |
| 29 | struct mg_server *server; | |
| 30 | ||
| 31 | // Create and configure the server | |
| 32 | server = mg_create_server(NULL, ev_handler); | |
| 33 | mg_set_option(server, "listening_port", "8080"); | |
| 34 | ||
| 35 | // Serve request. Hit Ctrl-C to terminate the program | |
| 36 | printf("Starting on port %s\n", mg_get_option(server, "listening_port")); | |
| 37 | for (;;) { | |
| 38 | mg_poll_server(server, 1000); | |
| 39 | } | |
| 40 | ||
| 41 | // Cleanup, and free server instance | |
| 42 | mg_destroy_server(&server); | |
| 43 | ||
| 44 | return 0; | |
| 45 | } |
| r0 | r250110 | |
|---|---|---|
| 1 | # Copyright (c) 2014 Cesanta Software | |
| 2 | # All rights reserved | |
| 3 | ||
| 4 | PROG = big_upload | |
| 5 | CFLAGS = -W -Wall -pthread -I../.. -g -O0 $(CFLAGS_EXTRA) | |
| 6 | SOURCES = $(PROG).c ../../mongoose.c | |
| 7 | ||
| 8 | $(PROG): $(SOURCES) | |
| 9 | $(CC) -o $(PROG) $(SOURCES) $(CFLAGS) | |
| 10 | ||
| 11 | clean: | |
| 12 | rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib |
| r0 | r250110 | |
|---|---|---|
| 1 | #include <stdio.h> | |
| 2 | #include <string.h> | |
| 3 | #include <stdlib.h> | |
| 4 | #include "mongoose.h" | |
| 5 | ||
| 6 | static int handle_request(struct mg_connection *conn) { | |
| 7 | if (strcmp(conn->uri, "/upload") == 0) { | |
| 8 | FILE *fp = (FILE *) conn->connection_param; | |
| 9 | if (fp != NULL) { | |
| 10 | fwrite(conn->content, 1, conn->content_len, fp); // Write last bits | |
| 11 | mg_printf(conn, "HTTP/1.1 200 OK\r\n" | |
| 12 | "Content-Type: text/plain\r\n" | |
| 13 | "Connection: close\r\n\r\n" | |
| 14 | "Written %ld of POST data to a temp file:\n\n", | |
| 15 | (long) ftell(fp)); | |
| 16 | ||
| 17 | // Temp file will be destroyed after fclose(), do something with the | |
| 18 | // data here -- for example, parse it and extract uploaded files. | |
| 19 | // As an example, we just echo the whole POST buffer back to the client. | |
| 20 | rewind(fp); | |
| 21 | mg_send_file_data(conn, fileno(fp)); | |
| 22 | return MG_MORE; // Tell Mongoose reply is not completed yet | |
| 23 | } else { | |
| 24 | mg_printf_data(conn, "%s", "Had no data to write..."); | |
| 25 | return MG_TRUE; // Tell Mongoose we're done with this request | |
| 26 | } | |
| 27 | } else { | |
| 28 | mg_printf_data(conn, "%s", | |
| 29 | "<html><body>Upload example." | |
| 30 | "<form method=\"POST\" action=\"/upload\" " | |
| 31 | " enctype=\"multipart/form-data\">" | |
| 32 | "<input type=\"file\" name=\"file\" /> <br/>" | |
| 33 | "<input type=\"submit\" value=\"Upload\" />" | |
| 34 | "</form></body></html>"); | |
| 35 | return MG_TRUE; // Tell mongoose to close this connection | |
| 36 | } | |
| 37 | } | |
| 38 | ||
| 39 | // Mongoose sends MG_RECV for every received POST chunk. | |
| 40 | // When last POST chunk is received, Mongoose sends MG_REQUEST, then MG_CLOSE. | |
| 41 | static int handle_recv(struct mg_connection *conn) { | |
| 42 | FILE *fp = (FILE *) conn->connection_param; | |
| 43 | ||
| 44 | // Open temporary file where we going to write data | |
| 45 | if (fp == NULL && ((conn->connection_param = fp = tmpfile())) == NULL) { | |
| 46 | return -1; // Close connection on error | |
| 47 | } | |
| 48 | ||
| 49 | // Return number of bytes written to a temporary file: that is how many | |
| 50 | // bytes we want to discard from the receive buffer | |
| 51 | return fwrite(conn->content, 1, conn->content_len, fp); | |
| 52 | } | |
| 53 | ||
| 54 | // Make sure we free all allocated resources | |
| 55 | static int handle_close(struct mg_connection *conn) { | |
| 56 | if (conn->connection_param != NULL) { | |
| 57 | fclose((FILE *) conn->connection_param); | |
| 58 | conn->connection_param = NULL; | |
| 59 | } | |
| 60 | return MG_TRUE; | |
| 61 | } | |
| 62 | ||
| 63 | static int ev_handler(struct mg_connection *conn, enum mg_event ev) { | |
| 64 | switch (ev) { | |
| 65 | case MG_AUTH: return MG_TRUE; | |
| 66 | case MG_REQUEST: return handle_request(conn); | |
| 67 | case MG_RECV: return handle_recv(conn); | |
| 68 | case MG_CLOSE: return handle_close(conn); | |
| 69 | default: return MG_FALSE; | |
| 70 | } | |
| 71 | } | |
| 72 | ||
| 73 | int main(void) { | |
| 74 | struct mg_server *server = mg_create_server(NULL, ev_handler); | |
| 75 | mg_set_option(server, "listening_port", "8080"); | |
| 76 | printf("Starting on port %s\n", mg_get_option(server, "listening_port")); | |
| 77 | ||
| 78 | for (;;) { | |
| 79 | mg_poll_server(server, 1000); | |
| 80 | } | |
| 81 | ||
| 82 | mg_destroy_server(&server); | |
| 83 | return 0; | |
| 84 | } |
| r0 | r250110 | |
|---|---|---|
| 1 | # Copyright (c) 2014 Cesanta Software | |
| 2 | # All rights reserved | |
| 3 | ||
| 4 | PROG = cookie_auth | |
| 5 | CFLAGS = -W -Wall -I../.. -pthread -g -O0 $(CFLAGS_EXTRA) | |
| 6 | SOURCES = $(PROG).c ../../mongoose.c | |
| 7 | ||
| 8 | $(PROG): $(SOURCES) | |
| 9 | $(CC) -o $(PROG) $(SOURCES) $(CFLAGS) | |
| 10 | ||
| 11 | clean: | |
| 12 | rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib |
| r0 | r250110 | |
|---|---|---|
| 1 | // Copyright (c) 2014 Cesanta Software | |
| 2 | // All rights reserved | |
| 3 | ||
| 4 | #include <stdio.h> | |
| 5 | #include <string.h> | |
| 6 | #include <time.h> | |
| 7 | #include "mongoose.h" | |
| 8 | ||
| 9 | static const char *s_login_uri = "/login.html"; | |
| 10 | static const char *s_secret = ":-)"; // Must be known only to server | |
| 11 | ||
| 12 | static void generate_ssid(const char *user_name, const char *expiration_date, | |
| 13 | char *ssid, size_t ssid_size) { | |
| 14 | char hash[33]; | |
| 15 | mg_md5(hash, user_name, ":", expiration_date, ":", s_secret, NULL); | |
| 16 | snprintf(ssid, ssid_size, "%s|%s|%s", user_name, expiration_date, hash); | |
| 17 | } | |
| 18 | ||
| 19 | static int check_auth(struct mg_connection *conn) { | |
| 20 | char ssid[100], calculated_ssid[100], name[100], expire[100]; | |
| 21 | ||
| 22 | // Always authenticate requests to login page | |
| 23 | if (strcmp(conn->uri, s_login_uri) == 0) { | |
| 24 | return MG_TRUE; | |
| 25 | } | |
| 26 | ||
| 27 | // Look for session ID in the Cookie. | |
| 28 | // That session ID can be validated against the database that stores | |
| 29 | // current active sessions. | |
| 30 | mg_parse_header(mg_get_header(conn, "Cookie"), "ssid", ssid, sizeof(ssid)); | |
| 31 | if (sscanf(ssid, "%[^|]|%[^|]|", name, expire) == 2) { | |
| 32 | generate_ssid(name, expire, calculated_ssid, sizeof(calculated_ssid)); | |
| 33 | if (strcmp(ssid, calculated_ssid) == 0) { | |
| 34 | return MG_TRUE; // Authenticate | |
| 35 | } | |
| 36 | } | |
| 37 | ||
| 38 | // Auth failed, do NOT authenticate, redirect to login page | |
| 39 | mg_printf(conn, "HTTP/1.1 302 Moved\r\nLocation: %s\r\n\r\n", s_login_uri); | |
| 40 | return MG_FALSE; | |
| 41 | } | |
| 42 | ||
| 43 | static int check_login_form_submission(struct mg_connection *conn) { | |
| 44 | char name[100], password[100], ssid[100], expire[100], expire_epoch[100]; | |
| 45 | ||
| 46 | mg_get_var(conn, "name", name, sizeof(name)); | |
| 47 | mg_get_var(conn, "password", password, sizeof(password)); | |
| 48 | ||
| 49 | // A real authentication mechanism should be employed here. | |
| 50 | // Also, the whole site should be served through HTTPS. | |
| 51 | if (strcmp(name, "Joe") == 0 && strcmp(password, "Doe") == 0) { | |
| 52 | // Generate expiry date | |
| 53 | time_t t = time(NULL) + 3600; // Valid for 1 hour | |
| 54 | snprintf(expire_epoch, sizeof(expire_epoch), "%lu", (unsigned long) t); | |
| 55 | strftime(expire, sizeof(expire), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&t)); | |
| 56 | generate_ssid(name, expire_epoch, ssid, sizeof(ssid)); | |
| 57 | // Set "session id" cookie, there could be some data encoded in it. | |
| 58 | mg_printf(conn, | |
| 59 | "HTTP/1.1 302 Moved\r\n" | |
| 60 | "Set-Cookie: ssid=%s; expire=\"%s\"; http-only; HttpOnly;\r\n" | |
| 61 | "Content-Length: 0\r\n" | |
| 62 | "Location: /\r\n\r\n", | |
| 63 | ssid, expire); | |
| 64 | return MG_TRUE; | |
| 65 | } | |
| 66 | return MG_FALSE; | |
| 67 | } | |
| 68 | ||
| 69 | static int serve_request(struct mg_connection *conn) { | |
| 70 | if (strcmp(conn->uri, s_login_uri) == 0 && | |
| 71 | strcmp(conn->request_method, "POST") == 0) { | |
| 72 | return check_login_form_submission(conn); | |
| 73 | } | |
| 74 | return MG_FALSE; // Serve files in the document_root | |
| 75 | } | |
| 76 | ||
| 77 | static int ev_handler(struct mg_connection *conn, enum mg_event ev) { | |
| 78 | switch (ev) { | |
| 79 | case MG_AUTH: return check_auth(conn); | |
| 80 | case MG_REQUEST: return serve_request(conn); | |
| 81 | default: return MG_FALSE; | |
| 82 | } | |
| 83 | } | |
| 84 | ||
| 85 | int main(void) { | |
| 86 | struct mg_server *server = mg_create_server(NULL, ev_handler); | |
| 87 | mg_set_option(server, "listening_port", "8080"); | |
| 88 | mg_set_option(server, "document_root", "."); | |
| 89 | ||
| 90 | printf("Starting on port %s\n", mg_get_option(server, "listening_port")); | |
| 91 | for (;;) { | |
| 92 | mg_poll_server(server, 1000); | |
| 93 | } | |
| 94 | mg_destroy_server(&server); | |
| 95 | ||
| 96 | return 0; | |
| 97 | } |
| r0 | r250110 | |
|---|---|---|
| 1 | <!DOCTYPE html> | |
| 2 | <html lang="en"> | |
| 3 | <head> | |
| 4 | <meta charset="utf-8" /> | |
| 5 | <title>WebSocket Test</title> | |
| 6 | <meta name="viewport" content="width=device-width, initial-scale=1" /> | |
| 7 | <style type="text/css"> | |
| 8 | body { | |
| 9 | background-color: #cde; margin: 0; | |
| 10 | padding: 0; font: 14px Helvetica, Arial, sans-serif; | |
| 11 | } | |
| 12 | * { outline: none; } | |
| 13 | div.content { | |
| 14 | width: 800px; margin: 2em auto; padding: 20px 50px; | |
| 15 | background-color: #fff; border-radius: 1em; | |
| 16 | } | |
| 17 | label { display: inline-block; min-width: 7em; } | |
| 18 | input { border: 1px solid #ccc; padding: 0.4em; margin: 0 0 10px 0; } | |
| 19 | a:link, a:visited { color: #69c; text-decoration: none; } | |
| 20 | @media (max-width: 700px) { | |
| 21 | body { background-color: #fff; } | |
| 22 | div.content { | |
| 23 | width: auto; margin: 0 auto; border-radius: 0; padding: 1em; | |
| 24 | } | |
| 25 | } | |
| 26 | </style> | |
| 27 | ||
| 28 | <body> | |
| 29 | <div class="content"> | |
| 30 | <h1>Mongoose Cookie Base Authentication</h1> | |
| 31 | <p>This is an index page. Authentication succeeded.</p> | |
| 32 | </body> | |
| 33 | </html> | |
| No newline at end of file |
| r0 | r250110 | |
|---|---|---|
| 1 | <!DOCTYPE html> | |
| 2 | <html lang="en"> | |
| 3 | <head> | |
| 4 | <meta charset="utf-8" /> | |
| 5 | <title>WebSocket Test</title> | |
| 6 | <meta name="viewport" content="width=device-width, initial-scale=1" /> | |
| 7 | <style type="text/css"> | |
| 8 | body { | |
| 9 | background-color: #cde; margin: 0; | |
| 10 | padding: 0; font: 14px Helvetica, Arial, sans-serif; | |
| 11 | } | |
| 12 | * { outline: none; } | |
| 13 | div.content { | |
| 14 | width: 800px; margin: 2em auto; padding: 20px 50px; | |
| 15 | background-color: #fff; border-radius: 1em; | |
| 16 | } | |
| 17 | label { display: inline-block; min-width: 7em; } | |
| 18 | input { border: 1px solid #ccc; padding: 0.4em; margin: 0 0 10px 0; } | |
| 19 | a:link, a:visited { color: #69c; text-decoration: none; } | |
| 20 | @media (max-width: 700px) { | |
| 21 | body { background-color: #fff; } | |
| 22 | div.content { | |
| 23 | width: auto; margin: 0 auto; border-radius: 0; padding: 1em; | |
| 24 | } | |
| 25 | } | |
| 26 | </style> | |
| 27 | ||
| 28 | <body> | |
| 29 | <div class="content"> | |
| 30 | <h1>Mongoose Cookie Based Authentication</h1> | |
| 31 | <p>Use name "Joe", password "Doe" to login.</p> | |
| 32 | <form method="POST"> | |
| 33 | <div> | |
| 34 | <label>Name:</label> | |
| 35 | <input type="text" name="name"/> | |
| 36 | </div><div> | |
| 37 | <label>Password:</label> | |
| 38 | <input type="password" name="password"/> | |
| 39 | </div><div> | |
| 40 | <input type="submit" value="Login"/> | |
| 41 | </div> | |
| 42 | </form> | |
| 43 | </body> | |
| 44 | </html> | |
| No newline at end of file |
| r0 | r250110 | |
|---|---|---|
| 1 | // This file is part of mongoose web server project, | |
| 2 | // https://github.com/cesanta/mongoose | |
| 3 | ||
| 4 | using System; | |
| 5 | ||
| 6 | public class Program { | |
| 7 | static private int EventHandler(IntPtr conn_ptr, int ev) { | |
| 8 | MongooseConnection conn = (MongooseConnection) | |
| 9 | System.Runtime.InteropServices.Marshal.PtrToStructure( | |
| 10 | conn_ptr , typeof(MongooseConnection)); | |
| 11 | ||
| 12 | if (ev == 102) { | |
| 13 | // MG_AUTH | |
| 14 | return 1; | |
| 15 | } else if (ev == 103) { | |
| 16 | // MG_REQUEST | |
| 17 | Mongoose.send_data(conn_ptr, "Hello from C#!\n"); | |
| 18 | Mongoose.send_data(conn_ptr, "URI: " + conn.uri + "\n"); | |
| 19 | Mongoose.send_data(conn_ptr, "HTTP Headers:\n"); | |
| 20 | ||
| 21 | for (int i = 0; i < conn.num_headers; i++) { | |
| 22 | IntPtr name = conn.http_headers[i].name; | |
| 23 | IntPtr val = conn.http_headers[i].value; | |
| 24 | System.Runtime.InteropServices.Marshal.PtrToStringAnsi(name); | |
| 25 | Mongoose.send_data(conn_ptr, " " + | |
| 26 | System.Runtime.InteropServices.Marshal.PtrToStringAnsi(name) + ": " + | |
| 27 | System.Runtime.InteropServices.Marshal.PtrToStringAnsi(val) + "\n"); | |
| 28 | } | |
| 29 | return 1; | |
| 30 | } | |
| 31 | return 0; | |
| 32 | } | |
| 33 | ||
| 34 | static void Main() { | |
| 35 | Mongoose web_server = new Mongoose(".", "9001", | |
| 36 | new MongooseEventHandler(EventHandler)); | |
| 37 | ||
| 38 | Console.WriteLine("Mongoose started, press Ctrl-C to exit."); | |
| 39 | for (;;) { | |
| 40 | web_server.poll(1000); | |
| 41 | } | |
| 42 | } | |
| 43 | } |
| r0 | r250110 | |
|---|---|---|
| 1 | // This file is part of mongoose web server project, | |
| 2 | // https://github.com/cesanta/mongoose | |
| 3 | ||
| 4 | using System; | |
| 5 | using System.Runtime.InteropServices; | |
| 6 | ||
| 7 | [StructLayout(LayoutKind.Sequential)] public struct MongooseHeader { | |
| 8 | [MarshalAs(UnmanagedType.LPTStr)] public IntPtr name; | |
| 9 | [MarshalAs(UnmanagedType.LPTStr)] public IntPtr value; | |
| 10 | }; | |
| 11 | ||
| 12 | // mongoose.h :: struct mg_connection | |
| 13 | [StructLayout(LayoutKind.Sequential)] public struct MongooseConnection { | |
| 14 | [MarshalAs(UnmanagedType.LPTStr)] public string request_method; | |
| 15 | [MarshalAs(UnmanagedType.LPTStr)] public string uri; | |
| 16 | [MarshalAs(UnmanagedType.LPTStr)] public string http_version; | |
| 17 | [MarshalAs(UnmanagedType.LPTStr)] public string query_string; | |
| 18 | ||
| 19 | [MarshalAs(UnmanagedType.ByValArray,SizeConst=48)] public char[] remote_ip; | |
| 20 | [MarshalAs(UnmanagedType.LPTStr)] public string local_ip; | |
| 21 | [MarshalAs(UnmanagedType.U2)] public short remote_port; | |
| 22 | [MarshalAs(UnmanagedType.U2)] public short local_port; | |
| 23 | ||
| 24 | [MarshalAs(UnmanagedType.SysInt)] public int num_headers; | |
| 25 | [MarshalAs(UnmanagedType.ByValArray,SizeConst=30)] | |
| 26 | public MongooseHeader[] http_headers; | |
| 27 | ||
| 28 | [MarshalAs(UnmanagedType.LPTStr)] public IntPtr content; | |
| 29 | [MarshalAs(UnmanagedType.SysInt)] public int content_len; | |
| 30 | ||
| 31 | [MarshalAs(UnmanagedType.SysInt)] public int is_websocket; | |
| 32 | [MarshalAs(UnmanagedType.SysInt)] public int status_code; | |
| 33 | [MarshalAs(UnmanagedType.SysInt)] public int wsbits; | |
| 34 | }; | |
| 35 | ||
| 36 | public delegate int MongooseEventHandler(IntPtr c, int ev); | |
| 37 | ||
| 38 | public class Mongoose { | |
| 39 | public const string dll_ = "mongoose"; | |
| 40 | private IntPtr server_; | |
| 41 | ||
| 42 | [DllImport(dll_)] private static extern IntPtr | |
| 43 | mg_create_server(IntPtr user_data, MongooseEventHandler eh); | |
| 44 | [DllImport(dll_)] private static extern int | |
| 45 | mg_poll_server(IntPtr server, int milli); | |
| 46 | [DllImport(dll_)] private static extern IntPtr | |
| 47 | mg_set_option(IntPtr server, string name, string value); | |
| 48 | [DllImport(dll_)] public static extern int | |
| 49 | mg_send_data(IntPtr conn, string data, int length); | |
| 50 | ||
| 51 | public Mongoose(string document_root, | |
| 52 | string listening_port, | |
| 53 | MongooseEventHandler event_handler) { | |
| 54 | server_ = mg_create_server(IntPtr.Zero, event_handler); | |
| 55 | mg_set_option(server_, "document_root", document_root); | |
| 56 | mg_set_option(server_, "listening_port", listening_port); | |
| 57 | } | |
| 58 | ||
| 59 | public static int send_data(IntPtr conn, string data) { | |
| 60 | return mg_send_data(conn, data, data.Length); | |
| 61 | } | |
| 62 | ||
| 63 | public void poll(int milli) { | |
| 64 | mg_poll_server(server_, milli); | |
| 65 | } | |
| 66 | ||
| 67 | // TODO: add destructor and call mg_destroy_server() | |
| 68 | } |
| r0 | r250110 | |
|---|---|---|
| 1 | # Copyright (c) 2014 Cesanta Software | |
| 2 | # All rights reserved | |
| 3 | ||
| 4 | PROG = digest_auth | |
| 5 | CFLAGS = -W -Wall -I../.. -pthread -g -O0 $(CFLAGS_EXTRA) | |
| 6 | SOURCES = $(PROG).c ../../mongoose.c | |
| 7 | ||
| 8 | $(PROG): $(SOURCES) | |
| 9 | $(CC) -o $(PROG) $(SOURCES) $(CFLAGS) | |
| 10 | ||
| 11 | clean: | |
| 12 | rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib |
| r0 | r250110 | |
|---|---|---|
| 1 | #include <stdio.h> | |
| 2 | #include <string.h> | |
| 3 | #include "mongoose.h" | |
| 4 | ||
| 5 | static int ev_handler(struct mg_connection *conn, enum mg_event ev) { | |
| 6 | ||
| 7 | if (ev == MG_AUTH) { | |
| 8 | int result = MG_FALSE; // Not authorized | |
| 9 | FILE *fp; | |
| 10 | ||
| 11 | // To populate passwords file, do | |
| 12 | // mongoose -A my_passwords.txt mydomain.com admin admin | |
| 13 | if ((fp = fopen("my_passwords.txt", "r")) != NULL) { | |
| 14 | result = mg_authorize_digest(conn, fp); | |
| 15 | fclose(fp); | |
| 16 | } | |
| 17 | ||
| 18 | return result; | |
| 19 | } | |
| 20 | ||
| 21 | return MG_FALSE; | |
| 22 | } | |
| 23 | ||
| 24 | int main(void) { | |
| 25 | struct mg_server *server = mg_create_server(NULL, ev_handler); | |
| 26 | mg_set_option(server, "listening_port", "8080"); | |
| 27 | mg_set_option(server, "document_root", "."); | |
| 28 | ||
| 29 | printf("Starting on port %s\n", mg_get_option(server, "listening_port")); | |
| 30 | for (;;) { | |
| 31 | mg_poll_server(server, 1000); | |
| 32 | } | |
| 33 | mg_destroy_server(&server); | |
| 34 | ||
| 35 | return 0; | |
| 36 | } |
| r0 | r250110 | |
|---|---|---|
| 1 | # Copyright (c) 2014 Cesanta Software | |
| 2 | # All rights reserved | |
| 3 | ||
| 4 | PROG = file_upload | |
| 5 | CFLAGS = -W -Wall -I../.. -pthread -g -O0 $(CFLAGS_EXTRA) | |
| 6 | SOURCES = $(PROG).c ../../mongoose.c | |
| 7 | ||
| 8 | all: $(PROG) | |
| 9 | ||
| 10 | run: $(PROG) | |
| 11 | ./$(PROG) | |
| 12 | ||
| 13 | $(PROG): $(SOURCES) Makefile | |
| 14 | $(CC) -o $(PROG) $(SOURCES) $(CFLAGS) | |
| 15 | ||
| 16 | win: | |
| 17 | wine cl $(SOURCES) /MD /nologo /DNDEBUG /O1 /I../.. /Fe$(PROG).exe | |
| 18 | ||
| 19 | clean: | |
| 20 | rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib *.gc* |
| r0 | r250110 | |
|---|---|---|
| 1 | // Copyright (c) 2004-2012 Sergey Lyubka | |
| 2 | // This file is a part of mongoose project, http://github.com/valenok/mongoose | |
| 3 | ||
| 4 | #include <stdio.h> | |
| 5 | #include <string.h> | |
| 6 | #include "mongoose.h" | |
| 7 | ||
| 8 | static int send_index_page(struct mg_connection *conn) { | |
| 9 | const char *data; | |
| 10 | int data_len, n1, n2; | |
| 11 | char var_name[100], file_name[100]; | |
| 12 | ||
| 13 | mg_printf_data(conn, "%s", | |
| 14 | "<html><body>Upload example." | |
| 15 | "<form method=\"POST\" action=\"/handle_post_request\" " | |
| 16 | " enctype=\"multipart/form-data\">" | |
| 17 | "<input type=\"file\" name=\"file1\" /> <br/>" | |
| 18 | "<input type=\"file\" name=\"file2\" /> <br/>" | |
| 19 | "<input type=\"submit\" value=\"Upload\" />" | |
| 20 | "</form>"); | |
| 21 | ||
| 22 | n1 = n2 = 0; | |
| 23 | while ((n2 = mg_parse_multipart(conn->content + n1, conn->content_len - n1, | |
| 24 | var_name, sizeof(var_name), file_name, | |
| 25 | sizeof(file_name), &data, &data_len)) > 0) { | |
| 26 | mg_printf_data(conn, "var: %s, file_name: %s, size: %d bytes<br>", | |
| 27 | var_name, file_name, data_len); | |
| 28 | n1 += n2; | |
| 29 | } | |
| 30 | ||
| 31 | mg_printf_data(conn, "%s", "</body></html>"); | |
| 32 | ||
| 33 | return MG_TRUE; | |
| 34 | } | |
| 35 | ||
| 36 | static int ev_handler(struct mg_connection *conn, enum mg_event ev) { | |
| 37 | switch (ev) { | |
| 38 | case MG_AUTH: return MG_TRUE; | |
| 39 | case MG_REQUEST: return send_index_page(conn); | |
| 40 | default: return MG_FALSE; | |
| 41 | } | |
| 42 | } | |
| 43 | ||
| 44 | int main(void) { | |
| 45 | struct mg_server *server; | |
| 46 | ||
| 47 | // Create and configure the server | |
| 48 | server = mg_create_server(NULL, ev_handler); | |
| 49 | mg_set_option(server, "listening_port", "8080"); | |
| 50 | ||
| 51 | // Serve request. Hit Ctrl-C to terminate the program | |
| 52 | printf("Starting on port %s\n", mg_get_option(server, "listening_port")); | |
| 53 | for (;;) { | |
| 54 | mg_poll_server(server, 1000); | |
| 55 | } | |
| 56 | ||
| 57 | // Cleanup, and free server instance | |
| 58 | mg_destroy_server(&server); | |
| 59 | ||
| 60 | return 0; | |
| 61 | } |
| r0 | r250110 | |
|---|---|---|
| 1 | # Copyright (c) 2014 Cesanta Software | |
| 2 | # All rights reserved | |
| 3 | ||
| 4 | PROG = form_submit | |
| 5 | CFLAGS = -W -Wall -I../.. -pthread -g -O0 $(CFLAGS_EXTRA) | |
| 6 | SOURCES = $(PROG).c ../../mongoose.c | |
| 7 | ||
| 8 | $(PROG): $(SOURCES) | |
| 9 | $(CC) -o $(PROG) $(SOURCES) $(CFLAGS) | |
| 10 | ||
| 11 | clean: | |
| 12 | rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib |
| r0 | r250110 | |
|---|---|---|
| 1 | #include <stdio.h> | |
| 2 | #include <string.h> | |
| 3 | #include "mongoose.h" | |
| 4 | ||
| 5 | static const char *html_form = | |
| 6 | "<html><body>POST example." | |
| 7 | "<form method=\"POST\" action=\"/handle_post_request\">" | |
| 8 | "Input 1: <input type=\"text\" name=\"input_1\" /> <br/>" | |
| 9 | "Input 2: <input type=\"text\" name=\"input_2\" /> <br/>" | |
| 10 | "<input type=\"submit\" />" | |
| 11 | "</form></body></html>"; | |
| 12 | ||
| 13 | static void send_reply(struct mg_connection *conn) { | |
| 14 | char var1[500], var2[500]; | |
| 15 | ||
| 16 | if (strcmp(conn->uri, "/handle_post_request") == 0) { | |
| 17 | // User has submitted a form, show submitted data and a variable value | |
| 18 | // Parse form data. var1 and var2 are guaranteed to be NUL-terminated | |
| 19 | mg_get_var(conn, "input_1", var1, sizeof(var1)); | |
| 20 | mg_get_var(conn, "input_2", var2, sizeof(var2)); | |
| 21 | ||
| 22 | // Send reply to the client, showing submitted form values. | |
| 23 | // POST data is in conn->content, data length is in conn->content_len | |
| 24 | mg_send_header(conn, "Content-Type", "text/plain"); | |
| 25 | mg_printf_data(conn, | |
| 26 | "Submitted data: [%.*s]\n" | |
| 27 | "Submitted data length: %d bytes\n" | |
| 28 | "input_1: [%s]\n" | |
| 29 | "input_2: [%s]\n", | |
| 30 | conn->content_len, conn->content, | |
| 31 | conn->content_len, var1, var2); | |
| 32 | } else { | |
| 33 | // Show HTML form. | |
| 34 | mg_send_data(conn, html_form, strlen(html_form)); | |
| 35 | } | |
| 36 | } | |
| 37 | ||
| 38 | static int ev_handler(struct mg_connection *conn, enum mg_event ev) { | |
| 39 | if (ev == MG_REQUEST) { | |
| 40 | send_reply(conn); | |
| 41 | return MG_TRUE; | |
| 42 | } else if (ev == MG_AUTH) { | |
| 43 | return MG_TRUE; | |
| 44 | } else { | |
| 45 | return MG_FALSE; | |
| 46 | } | |
| 47 | } | |
| 48 | ||
| 49 | int main(void) { | |
| 50 | struct mg_server *server = mg_create_server(NULL, ev_handler); | |
| 51 | ||
| 52 | mg_set_option(server, "listening_port", "8080"); | |
| 53 | ||
| 54 | printf("Starting on port %s\n", mg_get_option(server, "listening_port")); | |
| 55 | for (;;) { | |
| 56 | mg_poll_server(server, 1000); | |
| 57 | } | |
| 58 | ||
| 59 | mg_destroy_server(&server); | |
| 60 | ||
| 61 | return 0; | |
| 62 | } |
| r0 | r250110 | |
|---|---|---|
| 1 | # Copyright (c) 2014 Cesanta Software | |
| 2 | # All rights reserved | |
| 3 | ||
| 4 | PROG = hello_world | |
| 5 | CFLAGS = -W -Wall -I../.. -pthread -g -O0 $(CFLAGS_EXTRA) | |
| 6 | SOURCES = $(PROG).c ../../mongoose.c | |
| 7 | ||
| 8 | all: $(PROG) | |
| 9 | ||
| 10 | run: $(PROG) | |
| 11 | ./$(PROG) | |
| 12 | ||
| 13 | $(PROG): $(SOURCES) Makefile | |
| 14 | $(CC) -o $(PROG) $(SOURCES) $(CFLAGS) | |
| 15 | ||
| 16 | win: | |
| 17 | wine cl $(SOURCES) /MD /nologo /DNDEBUG /O1 /I../.. /Fe$(PROG).exe | |
| 18 | wine $(PROG).exe | |
| 19 | ||
| 20 | clean: | |
| 21 | rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib *.gc* |
| r0 | r250110 | |
|---|---|---|
| 1 | // Copyright (c) 2014 Cesanta Software | |
| 2 | // All rights reserved | |
| 3 | // | |
| 4 | // This example demostrates basic use of Mongoose embedded web server. | |
| 5 | // $Date: 2014-09-09 22:20:23 UTC $ | |
| 6 | ||
| 7 | #include <stdio.h> | |
| 8 | #include <string.h> | |
| 9 | #include "mongoose.h" | |
| 10 | ||
| 11 | static int ev_handler(struct mg_connection *conn, enum mg_event ev) { | |
| 12 | switch (ev) { | |
| 13 | case MG_AUTH: return MG_TRUE; | |
| 14 | case MG_REQUEST: | |
| 15 | mg_printf_data(conn, "Hello! Requested URI is [%s]", conn->uri); | |
| 16 | return MG_TRUE; | |
| 17 | default: return MG_FALSE; | |
| 18 | } | |
| 19 | } | |
| 20 | ||
| 21 | int main(void) { | |
| 22 | struct mg_server *server; | |
| 23 | ||
| 24 | // Create and configure the server | |
| 25 | server = mg_create_server(NULL, ev_handler); | |
| 26 | mg_set_option(server, "listening_port", "8080"); | |
| 27 | ||
| 28 | // Serve request. Hit Ctrl-C to terminate the program | |
| 29 | printf("Starting on port %s\n", mg_get_option(server, "listening_port")); | |
| 30 | for (;;) { | |
| 31 | mg_poll_server(server, 1000); | |
| 32 | } | |
| 33 | ||
| 34 | // Cleanup, and free server instance | |
| 35 | mg_destroy_server(&server); | |
| 36 | ||
| 37 | return 0; | |
| 38 | } |
| r0 | r250110 | |
|---|---|---|
| 1 | # Copyright (c) 2014 Cesanta Software | |
| 2 | # All rights reserved | |
| 3 | ||
| 4 | PROG = http_client | |
| 5 | CFLAGS = -W -Wall -I../.. -pthread -g -O0 $(CFLAGS_EXTRA) | |
| 6 | SOURCES = $(PROG).c ../../mongoose.c | |
| 7 | ||
| 8 | unix: $(SOURCES) | |
| 9 | $(CC) -o $(PROG) $(SOURCES) $(CFLAGS) | |
| 10 | ||
| 11 | clean: | |
| 12 | rm -rf $(PROG) *.exe *.dSYM *.obj *.exp *.o *.lib |
| r0 | r250110 | |
|---|---|---|
| 1 | // Copyright (c) 2014 Cesanta Software | |
| 2 | // All rights reserved | |
| 3 | // | |
| 4 | // This example demostrates how to connect to the remote Web server, | |
| 5 | // download data, process it and send back a reply. | |
| 6 | ||
| 7 | #include <signal.h> | |
| 8 | #include <stdlib.h> | |
| 9 | ||
| 10 | #include "mongoose.h" | |
| 11 | ||
| 12 | static int s_received_signal = 0; | |
| 13 | static struct mg_server *s_server = NULL; | |
| 14 | static const char *s_remote_addr = "glosbe.com:80"; | |
| 15 | ||
| 16 | static void signal_handler(int sig_num) { | |
| 17 | signal(sig_num, signal_handler); | |
| 18 | s_received_signal = sig_num; | |
| 19 | } | |
| 20 | ||
| 21 | static int ev_handler(struct mg_connection *conn, enum mg_event ev) { | |
| 22 | struct mg_connection *client, *orig; | |
| 23 | ||
| 24 | switch (ev) { | |
| 25 | case MG_AUTH: | |
| 26 | return MG_TRUE; | |
| 27 | ||
| 28 | case MG_CONNECT: | |
| 29 | // Send request to the remote host. | |
| 30 | // TODO(lsm): handle connect error here. | |
| 31 | mg_printf(conn, "GET %s HTTP/1.0\r\nHost: %s\r\n\r\n", | |
| 32 | "/gapi/translate?from=eng&dest=fra&format=json&phrase=cat", | |
| 33 | s_remote_addr); | |
| 34 | return MG_TRUE; | |
| 35 | ||
| 36 | case MG_REPLY: | |
| 37 | // Send reply to the original connection | |
| 38 | orig = (struct mg_connection *) conn->connection_param; | |
| 39 | mg_send_status(orig, conn->status_code); | |
| 40 | mg_send_header(orig, "Content-Type", "text/plain"); | |
| 41 | mg_send_data(orig, conn->content, conn->content_len); | |
| 42 | mg_send_data(orig, "", 0); // Last chunk: mark the end of reply | |
| 43 | ||
| 44 | // Disconnect connections | |
| 45 | orig->connection_param = NULL; | |
| 46 | conn->connection_param = NULL; | |
| 47 | return MG_TRUE; | |
| 48 | ||
| 49 | case MG_REQUEST: | |
| 50 | if ((client = mg_connect(s_server, s_remote_addr)) != NULL) { | |
| 51 | // Interconnect requests | |
| 52 | client->connection_param = conn; | |
| 53 | conn->connection_param = client; | |
| 54 | return MG_MORE; | |
| 55 | } else { | |
| 56 | mg_printf_data(conn, "%s", "cannot send API request"); | |
| 57 | return MG_TRUE; | |
| 58 | } | |
| 59 | ||
| 60 | default: | |
| 61 | return MG_FALSE; | |
| 62 | } | |
| 63 | } | |
| 64 | ||
| 65 | int main(void) { | |
| 66 | s_server = mg_create_server(NULL, ev_handler); | |
| 67 | ||
| 68 | mg_set_option(s_server, "listening_port", "8080"); | |
| 69 | ||
| 70 | // Setup signal handlers | |
| 71 | signal(SIGTERM, signal_handler); | |
| 72 | signal(SIGINT, signal_handler); | |
| 73 | ||
| 74 | printf("Listening on port %s\n", mg_get_option(s_server, "listening_port")); | |
| 75 | while (s_received_signal == 0) { | |
| 76 | mg_poll_server(s_server, 1000); | |
| 77 | } | |
| 78 | mg_destroy_server(&s_server); | |
| 79 | printf("Existing on signal %d\n", s_received_signal); | |
| 80 | ||
| 81 | return EXIT_SUCCESS; | |
| 82 | } |
| r0 | r250110 | |
|---|---|---|
| 1 | # Copyright (c) 2014 Cesanta Software | |
| 2 | # All rights reserved | |
| 3 | ||
| 4 | PROG = mjpg_streamer | |
| 5 | CFLAGS = -W -Wall -I../.. -pthread -g -O0 $(CFLAGS_EXTRA) | |
| 6 | SOURCES = $(PROG).c ../../mongoose.c | |
| 7 | ||
| 8 | $(PROG): $(SOURCES) | |
| 9 | $(CC) -o $(PROG) $(SOURCES) $(CFLAGS) | |
| 10 | ||
| 11 | clean: | |
| 12 | rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib |
| r0 | r250110 | |
|---|---|---|
| 1 | #include <sys/stat.h> | |
| 2 | #include <stdio.h> | |
| 3 | #include <string.h> | |
| 4 | #include <stdlib.h> | |
| 5 | #include <time.h> | |
| 6 | #include "mongoose.h" | |
| 7 | ||
| 8 | static void send_file(struct mg_connection *conn, const char *path) { | |
| 9 | char buf[1024]; | |
| 10 | struct stat st; | |
| 11 | int n; | |
| 12 | FILE *fp; | |
| 13 | ||
| 14 | if (stat(path, &st) == 0 && (fp = fopen(path, "rb")) != NULL) { | |
| 15 | mg_printf(conn, "--w00t\r\nContent-Type: image/jpeg\r\n" | |
| 16 | "Content-Length: %lu\r\n\r\n", (unsigned long) st.st_size); | |
| 17 | while ((n = fread(buf, 1, sizeof(buf), fp)) > 0) { | |
| 18 | mg_write(conn, buf, n); | |
| 19 | } | |
| 20 | fclose(fp); | |
| 21 | mg_write(conn, "\r\n", 2); | |
| 22 | } | |
| 23 | } | |
| 24 | ||
| 25 | struct conn_state { | |
| 26 | int file_index; | |
| 27 | time_t last_poll; | |
| 28 | }; | |
| 29 | ||
| 30 | static int ev_handler(struct mg_connection *conn, enum mg_event ev) { | |
| 31 | const char **file_names = (const char **) conn->server_param; | |
| 32 | struct conn_state *state; | |
| 33 | time_t now = time(NULL); | |
| 34 | ||
| 35 | switch (ev) { | |
| 36 | ||
| 37 | case MG_AUTH: | |
| 38 | return MG_TRUE; | |
| 39 | ||
| 40 | case MG_REQUEST: | |
| 41 | if (strcmp(conn->uri, "/stream") != 0) { | |
| 42 | mg_send_header(conn, "Content-Type", "text/html"); | |
| 43 | mg_printf_data(conn, "%s", | |
| 44 | "Go to <a href=/stream>/stream</a> for MJPG stream"); | |
| 45 | return MG_TRUE; | |
| 46 | } | |
| 47 | ||
| 48 | mg_printf(conn, "%s", | |
| 49 | "HTTP/1.0 200 OK\r\n" "Cache-Control: no-cache\r\n" | |
| 50 | "Pragma: no-cache\r\nExpires: Thu, 01 Dec 1994 16:00:00 GMT\r\n" | |
| 51 | "Connection: close\r\nContent-Type: multipart/x-mixed-replace; " | |
| 52 | "boundary=--w00t\r\n\r\n"); | |
| 53 | ||
| 54 | send_file(conn, file_names[0]); | |
| 55 | ||
| 56 | state = (struct conn_state *) malloc(sizeof(*state)); | |
| 57 | conn->connection_param = state; | |
| 58 | state->file_index = 1; // First file is already sent | |
| 59 | state->last_poll = time(NULL); | |
| 60 | return MG_MORE; | |
| 61 | ||
| 62 | case MG_POLL: | |
| 63 | state = (struct conn_state *) conn->connection_param; | |
| 64 | ||
| 65 | if (state != NULL && now > state->last_poll) { | |
| 66 | if (file_names[state->file_index] != NULL) { | |
| 67 | send_file(conn, file_names[state->file_index]); | |
| 68 | state->file_index++; | |
| 69 | if (file_names[state->file_index] == NULL) { | |
| 70 | return MG_TRUE; // No more images, close connection | |
| 71 | } | |
| 72 | } | |
| 73 | state->last_poll = now; | |
| 74 | } | |
| 75 | return MG_FALSE; | |
| 76 | ||
| 77 | case MG_CLOSE: | |
| 78 | free(conn->connection_param); | |
| 79 | conn->connection_param = NULL; | |
| 80 | return MG_FALSE; | |
| 81 | ||
| 82 | default: | |
| 83 | return MG_FALSE; | |
| 84 | } | |
| 85 | } | |
| 86 | ||
| 87 | int main(int argc, char *argv[]) { | |
| 88 | struct mg_server *server; | |
| 89 | ||
| 90 | if (argc < 3) { | |
| 91 | printf("Usage: %s image1.jpg image2.jpg ...\n", argv[0]); | |
| 92 | return 1; | |
| 93 | } | |
| 94 | ||
| 95 | server = mg_create_server(&argv[1], ev_handler); | |
| 96 | mg_set_option(server, "listening_port", "8080"); | |
| 97 | ||
| 98 | printf("Starting on port %s\n", mg_get_option(server, "listening_port")); | |
| 99 | for (;;) { | |
| 100 | mg_poll_server(server, 1000); | |
| 101 | } | |
| 102 | mg_destroy_server(&server); | |
| 103 | ||
| 104 | return 0; | |
| 105 | } |
| r0 | r250110 | |
|---|---|---|
| 1 | # Copyright (c) 2014 Cesanta Software | |
| 2 | # All rights reserved | |
| 3 | ||
| 4 | PROG = multi_threaded_server | |
| 5 | CFLAGS = -W -Wall -I../.. -pthread -g -O0 -DMONGOOSE_ENABLE_THREADS $(CFLAGS_EXTRA) | |
| 6 | SOURCES = $(PROG).c ../../mongoose.c | |
| 7 | ||
| 8 | $(PROG): $(SOURCES) | |
| 9 | $(CC) -o $(PROG) $(SOURCES) $(CFLAGS) | |
| 10 | ||
| 11 | clean: | |
| 12 | rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib |
| r0 | r250110 | |
|---|---|---|
| 1 | #include "mongoose.h" | |
| 2 | ||
| 3 | // Start a browser and hit refresh couple of times. The replies will | |
| 4 | // come from both server instances. | |
| 5 | static int ev_handler(struct mg_connection *conn, enum mg_event ev) { | |
| 6 | if (ev == MG_REQUEST) { | |
| 7 | mg_send_header(conn, "Content-Type", "text/plain"); | |
| 8 | mg_printf_data(conn, "This is a reply from server instance # %s", | |
| 9 | (char *) conn->server_param); | |
| 10 | return MG_TRUE; | |
| 11 | } else if (ev == MG_AUTH) { | |
| 12 | return MG_TRUE; | |
| 13 | } else { | |
| 14 | return MG_FALSE; | |
| 15 | } | |
| 16 | } | |
| 17 | ||
| 18 | static void *serve(void *server) { | |
| 19 | for (;;) mg_poll_server((struct mg_server *) server, 1000); | |
| 20 | return NULL; | |
| 21 | } | |
| 22 | ||
| 23 | int main(void) { | |
| 24 | struct mg_server *server1, *server2; | |
| 25 | ||
| 26 | server1 = mg_create_server((void *) "1", ev_handler); | |
| 27 | server2 = mg_create_server((void *) "2", ev_handler); | |
| 28 | ||
| 29 | // Make both server1 and server2 listen on the same sockets | |
| 30 | mg_set_option(server1, "listening_port", "8080"); | |
| 31 | mg_copy_listeners(server1, server2); | |
| 32 | ||
| 33 | // server1 goes to separate thread, server 2 runs in main thread. | |
| 34 | // IMPORTANT: NEVER LET DIFFERENT THREADS HANDLE THE SAME SERVER. | |
| 35 | mg_start_thread(serve, server1); | |
| 36 | mg_start_thread(serve, server2); | |
| 37 | getchar(); | |
| 38 | ||
| 39 | return 0; | |
| 40 | } |
| r0 | r250110 | |
|---|---|---|
| 1 | # Copyright (c) 2014 Cesanta Software | |
| 2 | # All rights reserved | |
| 3 | ||
| 4 | PROG = proxy_server | |
| 5 | FLAGS = -I../.. -DNS_ENABLE_SSL | |
| 6 | CFLAGS = -W -Wall -g -O0 -pthread -lssl -DMONGOOSE_ENABLE_THREADS $(FLAGS) $(CFLAGS_EXTRA) | |
| 7 | SOURCES = $(PROG).c ../../mongoose.c | |
| 8 | ||
| 9 | unix: $(SOURCES) | |
| 10 | $(CC) -o $(PROG) $(SOURCES) $(CFLAGS) | |
| 11 | ||
| 12 | clean: | |
| 13 | rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib |
| r0 | r250110 | |
|---|---|---|
| 1 | // Copyright (c) 2014 Cesanta Software Limited | |
| 2 | // All rights reserved | |
| 3 | // | |
| 4 | // To build and run this example: | |
| 5 | // git clone https://github.com/cesanta/net_skeleton.git | |
| 6 | // git clone https://github.com/cesanta/mongoose.git | |
| 7 | // cd mongoose/examples | |
| 8 | // make proxy | |
| 9 | // ./proxy | |
| 10 | // | |
| 11 | // Configure your browser to use localhost:2014 as a proxy for all protocols | |
| 12 | // Then, navigate to https://cesanta.com | |
| 13 | ||
| 14 | #include <sys/stat.h> | |
| 15 | #include <signal.h> | |
| 16 | #include <stdarg.h> | |
| 17 | #include <stdio.h> | |
| 18 | #include <stdlib.h> | |
| 19 | #include <string.h> | |
| 20 | #include <time.h> | |
| 21 | ||
| 22 | #ifdef _WIN32 | |
| 23 | #define sleep(x) Sleep((x) * 1000) | |
| 24 | #else | |
| 25 | #include <unistd.h> | |
| 26 | #endif | |
| 27 | ||
| 28 | #include "mongoose.h" | |
| 29 | ||
| 30 | static int s_received_signal = 0; | |
| 31 | static struct mg_server *s_server = NULL; | |
| 32 | ||
| 33 | #define SSE_CONNECTION ((void *) 1) | |
| 34 | ||
| 35 | static void elog(int do_exit, const char *fmt, ...) { | |
| 36 | va_list ap; | |
| 37 | va_start(ap, fmt); | |
| 38 | vfprintf(stderr, fmt, ap); | |
| 39 | va_end(ap); | |
| 40 | fputc('\n', stderr); | |
| 41 | if (do_exit) exit(EXIT_FAILURE); | |
| 42 | } | |
| 43 | ||
| 44 | static void signal_handler(int sig_num) { | |
| 45 | signal(sig_num, signal_handler); | |
| 46 | s_received_signal = sig_num; | |
| 47 | } | |
| 48 | ||
| 49 | static int sse_push(struct mg_connection *conn, enum mg_event ev) { | |
| 50 | if (ev == MG_POLL && conn->connection_param == SSE_CONNECTION) { | |
| 51 | mg_printf(conn, "data: %s\r\n\r\n", (const char *) conn->callback_param); | |
| 52 | } | |
| 53 | return MG_TRUE; | |
| 54 | } | |
| 55 | ||
| 56 | static void *sse_pusher_thread_func(void *param) { | |
| 57 | while (s_received_signal == 0) { | |
| 58 | mg_wakeup_server_ex(s_server, sse_push, "%lu %s", | |
| 59 | (unsigned long) time(NULL), (const char *) param); | |
| 60 | sleep(1); | |
| 61 | } | |
| 62 | return NULL; | |
| 63 | } | |
| 64 | ||
| 65 | // Return: 1 if regular file, 2 if directory, 0 if not found | |
| 66 | static int exists(const char *path) { | |
| 67 | struct stat st; | |
| 68 | return stat(path, &st) != 0 ? 0 : S_ISDIR(st.st_mode) == 0 ? 1 : 2; | |
| 69 | } | |
| 70 | ||
| 71 | // Return: 1 if regular file, 2 if directory, 0 if not found | |
| 72 | static int is_local_file(const char *uri, char *path, size_t path_len) { | |
| 73 | snprintf(path, path_len, "%s/%s", | |
| 74 | mg_get_option(s_server, "document_root"), uri); | |
| 75 | return exists(path); | |
| 76 | } | |
| 77 | ||
| 78 | static int try_to_serve_locally(struct mg_connection *conn) { | |
| 79 | char path[500], buf[2000]; | |
| 80 | int n, res; | |
| 81 | FILE *fp = NULL; | |
| 82 | ||
| 83 | if ((res = is_local_file(conn->uri, path, sizeof(path))) == 2) { | |
| 84 | strncat(path, "/index.html", sizeof(path) - strlen(path) - 1); | |
| 85 | res = exists(path); | |
| 86 | printf("PATH: [%s]\n", path); | |
| 87 | } | |
| 88 | if (res == 0) return MG_FALSE; | |
| 89 | ||
| 90 | if ((fp = fopen(path, "rb")) != NULL) { | |
| 91 | printf("Serving [%s] locally \n", path); | |
| 92 | mg_send_header(conn, "Connection", "close"); | |
| 93 | mg_send_header(conn, "Content-Type", mg_get_mime_type(path, "text/plain")); | |
| 94 | while ((n = fread(buf, 1, sizeof(buf), fp)) > 0) { | |
| 95 | mg_send_data(conn, buf, n); | |
| 96 | } | |
| 97 | mg_send_data(conn, "", 0); | |
| 98 | fclose(fp); | |
| 99 | } | |
| 100 | return fp == NULL ? MG_FALSE : MG_TRUE; | |
| 101 | } | |
| 102 | ||
| 103 | static int is_resource_present_locally(const char *uri) { | |
| 104 | char path[500]; | |
| 105 | return is_local_file(uri, path, sizeof(path)) || strcmp(uri, "/api/sse") == 0; | |
| 106 | } | |
| 107 | ||
| 108 | static int proxy_event_handler(struct mg_connection *conn, enum mg_event ev) { | |
| 109 | static const char target_url[] = "http://cesanta.com"; | |
| 110 | static int target_url_size = sizeof(target_url) - 1; | |
| 111 | const char *host; | |
| 112 | ||
| 113 | switch (ev) { | |
| 114 | case MG_REQUEST: | |
| 115 | host = mg_get_header(conn, "Host"); | |
| 116 | printf("[%s] [%s] [%s]\n", conn->request_method, conn->uri, | |
| 117 | host == NULL ? "" : host); | |
| 118 | if (strstr(conn->uri, "/qqq") != NULL) s_received_signal = SIGTERM; | |
| 119 | ||
| 120 | // Proxied HTTPS requests use "CONNECT foo.com:443" | |
| 121 | // Proxied HTTP requests use "GET http://..... " | |
| 122 | // Serve requests for target_url from the local FS. | |
| 123 | if (memcmp(conn->uri, target_url, target_url_size) == 0 && | |
| 124 | is_resource_present_locally(conn->uri + target_url_size)) { | |
| 125 | conn->uri += target_url_size; // Leave only path in the URI | |
| 126 | } | |
| 127 | ||
| 128 | if (strcmp(conn->uri, "/api/sse") == 0) { | |
| 129 | conn->connection_param = SSE_CONNECTION; | |
| 130 | mg_printf(conn, "%s", "HTTP/1.0 200 OK\r\n" | |
| 131 | "Content-Type: text/event-stream\r\n" | |
| 132 | "Cache-Control: no-cache\r\n\r\n"); | |
| 133 | return MG_MORE; | |
| 134 | } | |
| 135 | ||
| 136 | if (host != NULL && strstr(host, "cesanta") != NULL) { | |
| 137 | return try_to_serve_locally(conn); | |
| 138 | } | |
| 139 | ||
| 140 | // Enable man-in-the-middle SSL mode for oracle.com | |
| 141 | if (!strcmp(conn->request_method, "CONNECT") && | |
| 142 | !strcmp(host, "oracle.com")) { | |
| 143 | mg_terminate_ssl(conn, "ssl_cert.pem"); // MUST return MG_MORE after | |
| 144 | return MG_MORE; | |
| 145 | } | |
| 146 | ||
| 147 | return MG_FALSE; | |
| 148 | case MG_AUTH: | |
| 149 | return MG_TRUE; | |
| 150 | default: | |
| 151 | return MG_FALSE; | |
| 152 | } | |
| 153 | } | |
| 154 | ||
| 155 | static void setopt(struct mg_server *s, const char *opt, const char *val) { | |
| 156 | const char *err_msg = mg_set_option(s, opt, val); | |
| 157 | if (err_msg != NULL) { | |
| 158 | elog(1, "Error setting [%s]: [%s]", opt, err_msg); | |
| 159 | } | |
| 160 | } | |
| 161 | ||
| 162 | int main(int argc, char *argv[]) { | |
| 163 | const char *port = "2014", *dump = NULL, *root = "proxy_web_root"; | |
| 164 | int i; | |
| 165 | ||
| 166 | // Parse command line options | |
| 167 | for (i = 1; i < argc; i++) { | |
| 168 | if (strcmp(argv[i], "-port") == 0 && i + 1 < argc) { | |
| 169 | port = argv[++i]; | |
| 170 | } else if (strcmp(argv[i], "-root") == 0 && i + 1 < argc) { | |
| 171 | root = argv[++i]; | |
| 172 | } else if (strcmp(argv[i], "-dump") == 0 && i + 1 < argc) { | |
| 173 | dump = argv[++i]; | |
| 174 | } else { | |
| 175 | elog(1, "Usage: %s [-cert FILE] [-ca_cert FILE] [-port PORT]", argv[0]); | |
| 176 | } | |
| 177 | } | |
| 178 | ||
| 179 | signal(SIGTERM, signal_handler); | |
| 180 | signal(SIGINT, signal_handler); | |
| 181 | ||
| 182 | // Create and configure proxy server | |
| 183 | s_server = mg_create_server(NULL, &proxy_event_handler); | |
| 184 | setopt(s_server, "enable_proxy", "yes"); | |
| 185 | setopt(s_server, "document_root", root); | |
| 186 | setopt(s_server, "listening_port", port); | |
| 187 | setopt(s_server, "hexdump_file", dump); | |
| 188 | ||
| 189 | // Start two SSE pushing threads | |
| 190 | mg_start_thread(sse_pusher_thread_func, (void *) "sse_pusher_thread_1"); | |
| 191 | mg_start_thread(sse_pusher_thread_func, (void *) "sse_pusher_thread_2"); | |
| 192 | ||
| 193 | // Start serving in the main thread | |
| 194 | printf("Starting on port %s\n", mg_get_option(s_server, "listening_port")); | |
| 195 | while (s_received_signal == 0) { | |
| 196 | mg_poll_server(s_server, 1000); | |
| 197 | } | |
| 198 | printf("Existing on signal %d\n", s_received_signal); | |
| 199 | mg_destroy_server(&s_server); | |
| 200 | ||
| 201 | return EXIT_SUCCESS; | |
| 202 | } |
| r0 | r250110 | |
|---|---|---|
| 1 | <html> | |
| 2 | <head> | |
| 3 | <title>App1 Index</title> | |
| 4 | <style> | |
| 5 | img { height: 40px; } | |
| 6 | </style> | |
| 7 | </head> | |
| 8 | <body> | |
| 9 | ||
| 10 | <h1>App1 index page. Served locally from the the proxy server filesystem</h1> | |
| 11 | ||
| 12 | <p>image that references non-existent local resource. Forwarded to | |
| 13 | the 'real' proxy target:</p> | |
| 14 | <img src="http://cesanta.com/images/logo.png" /> | |
| 15 | ||
| 16 | <p>Google logo via HTTPS (external resource, served by remote host):</p> | |
| 17 | <img src="https://www.google.ie/images/srpr/logo11w.png" /> | |
| 18 | ||
| 19 | <p>Same image via HTTP:</p> | |
| 20 | <img src="http://www.google.ie/images/srpr/logo11w.png" /> | |
| 21 | ||
| 22 | </body> | |
| 23 | </html> |
| r0 | r250110 | |
|---|---|---|
| 1 | <html> | |
| 2 | <head> | |
| 3 | <title>App2 Index</title> | |
| 4 | <meta charset="utf-8"> | |
| 5 | <script> | |
| 6 | window.onload = function() { | |
| 7 | // Using secure websocket connection, wss:// | |
| 8 | var ws = new WebSocket('wss://echo.websocket.org'); | |
| 9 | var div = document.getElementById('events'); | |
| 10 | ws.onmessage = function(ev) { | |
| 11 | var el = document.createElement('div'); | |
| 12 | el.innerHTML = 'websocket message: ' + ev.data; | |
| 13 | div.appendChild(el); | |
| 14 | // Keep only last 5 messages in the list | |
| 15 | while (div.childNodes.length > 5) div.removeChild(div.firstChild); | |
| 16 | }; | |
| 17 | ||
| 18 | // Send random stuff to the websocket connection periodically. | |
| 19 | // websocket server much echo that stuff back. | |
| 20 | window.setInterval(function() { | |
| 21 | var d = new Date(); | |
| 22 | ws.send(d.toString()); | |
| 23 | }, 1000); | |
| 24 | }; | |
| 25 | </script> | |
| 26 | </head> | |
| 27 | <body> | |
| 28 | <h1>App2 index page. Served locally from the | |
| 29 | the proxy's filesystem.</h1> | |
| 30 | <p> | |
| 31 | Following div shows proxy forwarding of websocket connection, served by | |
| 32 | ws://echo.websocket.org: | |
| 33 | </p> | |
| 34 | ||
| 35 | <div id="events"></div> | |
| 36 | </body> | |
| 37 | </html> |
| r0 | r250110 | |
|---|---|---|
| 1 | <html> | |
| 2 | <head> | |
| 3 | <title> proxy index </title> | |
| 4 | <script type="text/javascript"> | |
| 5 | window.onload = function() { | |
| 6 | var es = new EventSource("/api/sse"); | |
| 7 | var div = document.getElementById('events'); | |
| 8 | es.onmessage = function(ev) { | |
| 9 | var el = document.createElement('div'); | |
| 10 | el.innerHTML = 'sse message: ' + ev.data; | |
| 11 | div.appendChild(el); | |
| 12 | // Keep only last 5 messages in the list | |
| 13 | while (div.childNodes.length > 5) div.removeChild(div.firstChild); | |
| 14 | }; | |
| 15 | }; | |
| 16 | </script> | |
| 17 | </head> | |
| 18 | <body> | |
| 19 | <h1> proxy index page.</h1> | |
| 20 | <ul> | |
| 21 | <li><a href="app1">App1</a> - App1 root</li> | |
| 22 | <li><a href="app2">App2</a> - App2 root</li> | |
| 23 | </ul> | |
| 24 | ||
| 25 | <h2>SSE pushes, done by separate threads at random times:</h2> | |
| 26 | <div id="events"></div> | |
| 27 | ||
| 28 | </body> | |
| 29 | </html> |
| r0 | r250110 | |
|---|---|---|
| 1 | -----BEGIN RSA PRIVATE KEY----- | |
| 2 | MIIEogIBAAKCAQEAwONaLOP7EdegqjRuQKSDXzvHmFMZfBufjhELhNjo5KsL4ieH | |
| 3 | hMSGCcSV6y32hzhqR5lvTViaQez+xhc58NZRu+OUgEhodRBW/vAOjpz/xdMz5HaC | |
| 4 | EhP3E9W1pkitVseS8B5rrgJo1BfCGai1fPav1nutPq2Kj7vMy24+g460Lonf6ln1 | |
| 5 | di4aTIRtAqXtUU6RFpPJP35PkCXbTK65O8HJSxxt/XtfoezHCU5+UIwmZGYx46UB | |
| 6 | Wzg3IfK6bGPSiHU3pdiTol0uMPt/GUK+x4NyZJ4/ImsNAicRwMBdja4ywHKXJehH | |
| 7 | gXBthsVIHbL21x+4ibsg9eVM/XioTV6tW3IrdwIDAQABAoIBACFfdLutmkQFBcRN | |
| 8 | HAJNNHmmsyr0vcUOVnXTFyYeDXV67qxrYHQlOHe6LqIpKq1Mon7O2kYMnWvooFAP | |
| 9 | trOnsS6L+qaTYJdYg2TKjgo4ubw1hZXytyB/mdExuaMSkgMgtpia+tB5lD+V+LxN | |
| 10 | x1DesZ+veFMO3Zluyckswt4qM5yVa04YFrt31H0E1rJfIen61lidXIKYmHHWuRxK | |
| 11 | SadjFfbcqJ6P9ZF22BOkleg5Fm5NaxJmyQynOWaAkSZa5w1XySFfRjRfsbDr64G6 | |
| 12 | +LSG8YtRuvfxnvUNhynVPHcpE40eiPo6v8Ho6yZKXpV5klCKciodXAORsswSoGJa | |
| 13 | N3nnu/ECgYEA6Yb2rM3QUEPIALdL8f/OzZ1GBSdiQB2WSAxzl9pR/dLF2H+0pitS | |
| 14 | to0830mk92ppVmRVD3JGxYDRZQ56tlFXyGaCzJBMRIcsotAhBoNbjV0i9n5bLJYf | |
| 15 | BmjU9yvWcgsTt0tr3B0FrtYyp2tCvwHqlxvFpFdUCj2oRw2uGpkhmNkCgYEA03M6 | |
| 16 | WxFhsix3y6eVCVvShfbLBSOqp8l0qiTEty+dgVQcWN4CO/5eyaZXKxlCG9KMmKxy | |
| 17 | Yx+YgxZrDhfaZ0cxhHGPRKEAxM3IKwT2C8/wCaSiLWXZZpTifnSD99vtOt4wEfrG | |
| 18 | +AghNd5kamFiM9tU0AyvhJc2vdJFuXrfeC7ntM8CgYBGDA+t4cZcbRhu7ow/OKYF | |
| 19 | kulP3nJgHP/Y+LMrl3cEldZ2jEfZmCElVNQvfd2XwTl7injhOzvzPiKRF3jDez7D | |
| 20 | g8w0JAxceddvttJRK9GoY4l7OoeKpjUELSnEQkf+yUfOsTbXPXVY7jMfeNL6jE6b | |
| 21 | qN7t3qv8rmXtejMBE3G6cQKBgGR5W2BMiRSlxqKx1cKlrApV87BUe1HRCyuR3xuA | |
| 22 | d6Item7Lx1oEi7vb242yKdSYnpApWQ06xTh83Y/Ly87JaIEbiM0+h+P8OEIg0F1a | |
| 23 | iB+86AcUX1I8KseVy+Np0HbpfwP8GrFfA5DaRPK7pXMopEtby8cAJ1XZZaI1/ZvZ | |
| 24 | BebHAoGAcQU9WvCkT+nIp9FpXfBybYUsvgkaizMIqp66/l3GYgYAq8p1VLGvN4v5 | |
| 25 | ec0dW58SJrCpqsM3NP78DtEzQf9OOsk+FsjBFzDU2RkeUreyt2/nQBj/2mN/+hEy | |
| 26 | hYN0Zii2yTb63jGxKY6gH1R/r9dL8kXaJmcZrfSa3AgywnteJWg= | |
| 27 | -----END RSA PRIVATE KEY----- | |
| 28 | -----BEGIN CERTIFICATE----- | |
| 29 | MIIDBjCCAe4CCQCX05m0b053QzANBgkqhkiG9w0BAQQFADBFMQswCQYDVQQGEwJB | |
| 30 | VTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0 | |
| 31 | cyBQdHkgTHRkMB4XDTA4MTIwNzEwMjUyMloXDTE4MTIwNTEwMjUyMlowRTELMAkG | |
| 32 | A1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0 | |
| 33 | IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB | |
| 34 | AMDjWizj+xHXoKo0bkCkg187x5hTGXwbn44RC4TY6OSrC+Inh4TEhgnElest9oc4 | |
| 35 | akeZb01YmkHs/sYXOfDWUbvjlIBIaHUQVv7wDo6c/8XTM+R2ghIT9xPVtaZIrVbH | |
| 36 | kvAea64CaNQXwhmotXz2r9Z7rT6tio+7zMtuPoOOtC6J3+pZ9XYuGkyEbQKl7VFO | |
| 37 | kRaTyT9+T5Al20yuuTvByUscbf17X6HsxwlOflCMJmRmMeOlAVs4NyHyumxj0oh1 | |
| 38 | N6XYk6JdLjD7fxlCvseDcmSePyJrDQInEcDAXY2uMsBylyXoR4FwbYbFSB2y9tcf | |
| 39 | uIm7IPXlTP14qE1erVtyK3cCAwEAATANBgkqhkiG9w0BAQQFAAOCAQEAW4yZdqpB | |
| 40 | oIdiuXRosr86Sg9FiMg/cn+2OwQ0QIaA8ZBwKsc+wIIHEgXCS8J6316BGQeUvMD+ | |
| 41 | plNe0r4GWzzmlDMdobeQ5arPRB89qd9skE6pAMdLg3FyyfEjz3A0VpskolW5VBMr | |
| 42 | P5R7uJ1FLgH12RyAjZCWYcCRqEMOffqvyMCH6oAjyDmQOA5IssRKX/HsHntSH/HW | |
| 43 | W7slTcP45ty1b44Nq22/ubYk0CJRQgqKOIQ3cLgPomN1jNFQbAbfVTaK1DpEysrQ | |
| 44 | 5V8a8gNW+3sVZmV6d1Mj3pN2Le62wUKuV2g6BNU7iiwcoY8HI68aRxz2hVMS+t5f | |
| 45 | SEGI4JSxV56lYg== | |
| 46 | -----END CERTIFICATE----- | |
| 47 | -----BEGIN DH PARAMETERS----- | |
| 48 | MEYCQQD+ef8hZ4XbdoyIpJyCTF2UrUEfX6mYDvxuS5O1UNYcslUqlj6JkA11e/yS | |
| 49 | 6DK8Z86W6mSj5CEk4IjbyEOECXH7AgEC | |
| 50 | -----END DH PARAMETERS----- |
| r0 | r250110 | |
|---|---|---|
| 1 | # Copyright (c) 2014 Cesanta Software | |
| 2 | # All rights reserved | |
| 3 | ||
| 4 | PROG = restful_api | |
| 5 | CFLAGS = -W -Wall -I../.. -pthread -g -O0 $(CFLAGS_EXTRA) | |
| 6 | SOURCES = $(PROG).c ../../mongoose.c | |
| 7 | ||
| 8 | $(PROG): $(SOURCES) | |
| 9 | $(CC) -o $(PROG) $(SOURCES) $(CFLAGS) | |
| 10 | ||
| 11 | clean: | |
| 12 | rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib |
| r0 | r250110 | |
|---|---|---|
| 1 | <!DOCTYPE html> | |
| 2 | <html lang="en"> | |
| 3 | <head> | |
| 4 | <meta charset="utf-8" /> | |
| 5 | <title>RESTful API demo</title> | |
| 6 | <meta name="viewport" content="width=device-width, initial-scale=1" /> | |
| 7 | <style type="text/css"> | |
| 8 | * { outline: none; font: 16px/1.4 Helvetica, Arial, sans-serif; } | |
| 9 | body { | |
| 10 | background-color: #cde; margin: 0; | |
| 11 | padding: 0; font: 16px/1.4 Helvetica, Arial, sans-serif; | |
| 12 | } | |
| 13 | div.content { | |
| 14 | width: 800px; margin: 2em auto; padding: 20px 50px; | |
| 15 | background-color: #fff; border-radius: 1em; | |
| 16 | } | |
| 17 | label { display: inline-block; min-width: 7em; } | |
| 18 | input { border: 1px solid #ccc; padding: 0.2em; } | |
| 19 | a:link, a:visited { color: #69c; text-decoration: none; } | |
| 20 | @media (max-width: 700px) { | |
| 21 | body { background-color: #fff; } | |
| 22 | div.content { width: auto; margin: 0 auto; padding: 1em; } | |
| 23 | } | |
| 24 | </style> | |
| 25 | ||
| 26 | <script src="http://code.jquery.com/jquery-1.11.0.min.js"></script> | |
| 27 | <script language="javascript" type="text/javascript"> | |
| 28 | jQuery(function() { | |
| 29 | ||
| 30 | $(document).on('keyup', '#n1, #n2', function() { | |
| 31 | $.ajax({ | |
| 32 | url: '/api/sum', | |
| 33 | method: 'POST', | |
| 34 | dataType: 'json', | |
| 35 | data: { n1: $('#n1').val(), n2: $('#n2').val() }, | |
| 36 | success: function(json) { | |
| 37 | $('#result').html(json.result); | |
| 38 | } | |
| 39 | }); | |
| 40 | }); | |
| 41 | ||
| 42 | }); | |
| 43 | </script> | |
| 44 | </head> | |
| 45 | <body> | |
| 46 | <div class="content"> | |
| 47 | <h1>RESTful API demo.</h1> | |
| 48 | ||
| 49 | <p> | |
| 50 | This page demonstrates how Mongoose web server could be used to implement | |
| 51 | RESTful APIs. Enter numbers below, and press Submit. Browser will send | |
| 52 | two numbers to <tt>/api/sum</tt> URI, Mongoose calclulates the sum of | |
| 53 | two and returns the result. | |
| 54 | </p> | |
| 55 | ||
| 56 | <div> | |
| 57 | <label>Number 1:</label> <input type="text" id="n1" /> | |
| 58 | </div><div> | |
| 59 | <label>Number 2:</label> <input type="text" id="n2" /> | |
| 60 | </div><div> | |
| 61 | <label>Result:</label> <span id="result"> </span> | |
| 62 | </div><div> | |
| 63 | ||
| 64 | </div> | |
| 65 | </body> | |
| 66 | </html> |
| r0 | r250110 | |
|---|---|---|
| 1 | #include <stdio.h> | |
| 2 | #include <string.h> | |
| 3 | #include <stdlib.h> | |
| 4 | #include "mongoose.h" | |
| 5 | ||
| 6 | static const char *s_no_cache_header = | |
| 7 | "Cache-Control: max-age=0, post-check=0, " | |
| 8 | "pre-check=0, no-store, no-cache, must-revalidate\r\n"; | |
| 9 | ||
| 10 | static void handle_restful_call(struct mg_connection *conn) { | |
| 11 | char n1[100], n2[100]; | |
| 12 | ||
| 13 | // Get form variables | |
| 14 | mg_get_var(conn, "n1", n1, sizeof(n1)); | |
| 15 | mg_get_var(conn, "n2", n2, sizeof(n2)); | |
| 16 | ||
| 17 | mg_printf_data(conn, "{ \"result\": %lf }", strtod(n1, NULL) + strtod(n2, NULL)); | |
| 18 | } | |
| 19 | ||
| 20 | static int ev_handler(struct mg_connection *conn, enum mg_event ev) { | |
| 21 | switch (ev) { | |
| 22 | case MG_AUTH: return MG_TRUE; | |
| 23 | case MG_REQUEST: | |
| 24 | if (!strcmp(conn->uri, "/api/sum")) { | |
| 25 | handle_restful_call(conn); | |
| 26 | return MG_TRUE; | |
| 27 | } | |
| 28 | mg_send_file(conn, "index.html", s_no_cache_header); | |
| 29 | return MG_MORE; | |
| 30 | default: return MG_FALSE; | |
| 31 | } | |
| 32 | } | |
| 33 | ||
| 34 | int main(void) { | |
| 35 | struct mg_server *server; | |
| 36 | ||
| 37 | // Create and configure the server | |
| 38 | server = mg_create_server(NULL, ev_handler); | |
| 39 | mg_set_option(server, "listening_port", "8000"); | |
| 40 | ||
| 41 | // Serve request. Hit Ctrl-C to terminate the program | |
| 42 | printf("Starting on port %s\n", mg_get_option(server, "listening_port")); | |
| 43 | for (;;) { | |
| 44 | mg_poll_server(server, 1000); | |
| 45 | } | |
| 46 | ||
| 47 | // Cleanup, and free server instance | |
| 48 | mg_destroy_server(&server); | |
| 49 | ||
| 50 | return 0; | |
| 51 | } |
| r0 | r250110 | |
|---|---|---|
| 1 | # Copyright (c) 2014 Cesanta Software | |
| 2 | # All rights reserved | |
| 3 | ||
| 4 | PROG = send_file | |
| 5 | CFLAGS = -W -Wall -I../.. -pthread -g -O0 $(CFLAGS_EXTRA) | |
| 6 | SOURCES = $(PROG).c ../../mongoose.c | |
| 7 | ||
| 8 | all: $(PROG) | |
| 9 | ||
| 10 | run: $(PROG) | |
| 11 | ./$(PROG) | |
| 12 | ||
| 13 | $(PROG): $(SOURCES) Makefile | |
| 14 | $(CC) -o $(PROG) $(SOURCES) $(CFLAGS) | |
| 15 | ||
| 16 | win: | |
| 17 | wine cl $(SOURCES) /MD /nologo /DNDEBUG /O1 /I../.. /Fe$(PROG).exe | |
| 18 | wine $(PROG).exe | |
| 19 | ||
| 20 | clean: | |
| 21 | rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib *.gc* |
| r0 | r250110 | |
|---|---|---|
| 1 | // Copyright (c) 2014 Cesanta Software | |
| 2 | // All rights reserved | |
| 3 | // | |
| 4 | // This example demostrates how to send arbitrary files to the client. | |
| 5 | ||
| 6 | #include "mongoose.h" | |
| 7 | ||
| 8 | static int ev_handler(struct mg_connection *conn, enum mg_event ev) { | |
| 9 | switch (ev) { | |
| 10 | case MG_REQUEST: | |
| 11 | mg_send_file(conn, "send_file.c", NULL); // Also could be a dir, or CGI | |
| 12 | return MG_MORE; // It is important to return MG_MORE after mg_send_file! | |
| 13 | case MG_AUTH: return MG_TRUE; | |
| 14 | default: return MG_FALSE; | |
| 15 | } | |
| 16 | } | |
| 17 | ||
| 18 | int main(void) { | |
| 19 | struct mg_server *server = mg_create_server(NULL, ev_handler); | |
| 20 | mg_set_option(server, "listening_port", "8080"); | |
| 21 | ||
| 22 | printf("Starting on port %s\n", mg_get_option(server, "listening_port")); | |
| 23 | for (;;) mg_poll_server(server, 1000); | |
| 24 | mg_destroy_server(&server); | |
| 25 | ||
| 26 | return 0; | |
| 27 | } |
| r0 | r250110 | |
|---|---|---|
| 1 | # Copyright (c) 2014 Cesanta Software | |
| 2 | # All rights reserved | |
| 3 | ||
| 4 | PROG = web_server | |
| 5 | CFLAGS = -W -Wall -I../.. -g -O0 $(CFLAGS_EXTRA) | |
| 6 | SOURCES = $(PROG).c ../../mongoose.c | |
| 7 | OPENSSL_FLAGS = -DNS_ENABLE_SSL -lssl | |
| 8 | ||
| 9 | # PolarSSL paths and flags | |
| 10 | POLARSSL_PATH = /usr/local | |
| 11 | POLARSSLCOMPAT_PATH = ./../../../polar | |
| 12 | SOURCES_POLAR = $(SOURCES) $(POLARSSLCOMPAT_PATH)/polarssl_compat.c | |
| 13 | INCDIR_POLAR = -I$(POLARSSLCOMPAT_PATH) -I$(POLARSSL_PATH)/include | |
| 14 | LDFLAGS_POLAR = -L$(POLARSSL_PATH)/lib -lmbedtls | |
| 15 | CFLAGS_POLAR = $(CFLAGS) $(INCDIR_POLAR) -DNS_ENABLE_SSL | |
| 16 | ||
| 17 | $(PROG): $(SOURCES) | |
| 18 | $(CC) -o $(PROG) $(SOURCES) $(CFLAGS) | |
| 19 | ||
| 20 | $(PROG).exe: $(SOURCES) | |
| 21 | cl -Fo $(PROG) $(SOURCES) -nologo -MD -I../.. | |
| 22 | ||
| 23 | openssl: | |
| 24 | $(CC) -o $(PROG) $(SOURCES) $(CFLAGS) $(OPENSSL_FLAGS) | |
| 25 | ||
| 26 | polarssl: | |
| 27 | $(CC) -o $(PROG) $(SOURCES_POLAR) $(LDFLAGS_POLAR) $(CFLAGS_POLAR) | |
| 28 | ||
| 29 | clean: | |
| 30 | rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib |
| r0 | r250110 | |
|---|---|---|
| 1 | -----BEGIN CERTIFICATE----- | |
| 2 | MIIC+zCCAeOgAwIBAgIJAPhB8jbL+G82MA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNV | |
| 3 | BAMMCTEyNy4wLjAuMTAeFw0xNTAzMDYxMjQzMzNaFw0yNTAzMDMxMjQzMzNaMBQx | |
| 4 | EjAQBgNVBAMMCTEyNy4wLjAuMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC | |
| 5 | ggEBALi3b3daMgzUEROKob1Caf68i+//cTRkPdBJv2cOBak21CdQzY0Nvx73GLzf | |
| 6 | 5TKB347BCHNbYRKGJXDbYdmFp20/WeBHkY7RS3Ad2Q5lzyx66u9PxNx7hJIiqBgF | |
| 7 | 58VU+E3o/I+o8QNIoOT+wtCiq3Nwkp+zGBJmS32rzMEV9bcKxSzMrkfRhF+XAREd | |
| 8 | DwM9vfPg6WRb/b+vv06uvVwcw390RprLautGfBdaRddVYkIAKJGRRTqZAvTRFW1J | |
| 9 | FcIVOxlN+iA7qP7xjr3tUP78qMmlu0MXsHrUR2cgfveZK2sdUW5G804yHsU5sC8l | |
| 10 | FbtLKMEOyLsk2bEIScOXgum7g2sCAwEAAaNQME4wHQYDVR0OBBYEFHtLzUqAsXkH | |
| 11 | Il8S5sMhJuVhRJLdMB8GA1UdIwQYMBaAFHtLzUqAsXkHIl8S5sMhJuVhRJLdMAwG | |
| 12 | A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAEzHc0AOr+qs0OFvWMfcSMi7 | |
| 13 | O/aYlLS6f7Sos+lli69+61EcmCTJVarVeAVUsAoqmzBKDbeOpAK1hGX6/GGcXjR2 | |
| 14 | BmuU0hUKyX9l1lwdMKU45BayH/riElwnvAyj2GxKoPpdIjlHns4SAITOCUx9NfpM | |
| 15 | agd7kjolton0ZQ5DI/2a43PkqHv1lY4Dp60wJlxit9U68bsGOycCJ/BsAyrPROb2 | |
| 16 | D1MkpMBIdfHc8uxRywM3/l9buFX8yrrMUGOYKgfjDwdzbj0iwIixoGpHL7IfeBtu | |
| 17 | dvGO/g2rEhbtAP+xIgOR3GvzqjZh30er3no7zjDMn65tTME18Aq3tBQY7vPDKms= | |
| 18 | -----END CERTIFICATE----- | |
| 19 | -----BEGIN PRIVATE KEY----- | |
| 20 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC4t293WjIM1BET | |
| 21 | iqG9Qmn+vIvv/3E0ZD3QSb9nDgWpNtQnUM2NDb8e9xi83+Uygd+OwQhzW2EShiVw | |
| 22 | 22HZhadtP1ngR5GO0UtwHdkOZc8seurvT8Tce4SSIqgYBefFVPhN6PyPqPEDSKDk | |
| 23 | /sLQoqtzcJKfsxgSZkt9q8zBFfW3CsUszK5H0YRflwERHQ8DPb3z4OlkW/2/r79O | |
| 24 | rr1cHMN/dEaay2rrRnwXWkXXVWJCACiRkUU6mQL00RVtSRXCFTsZTfogO6j+8Y69 | |
| 25 | 7VD+/KjJpbtDF7B61EdnIH73mStrHVFuRvNOMh7FObAvJRW7SyjBDsi7JNmxCEnD | |
| 26 | l4Lpu4NrAgMBAAECggEAaFuqbAHXOQwuwZ2XFzgIblTTsrncmT7w9VZU/sIbTKif | |
| 27 | X771AnX7vmDX5w2PjeN2DE7emV3NEAwd5w7qz1wFZWFfQ6jrgYaZWjRixxGZ5IVl | |
| 28 | aeLlU7OtCGrwEPJ1KTWCO3IgDoHh+Hr1+6o7Imhk+QlmrTcfqHWGvO9s9MGVWt2S | |
| 29 | RLAnSTFiOe5brdJnmlqq1sKZmnLmpydBaPUOYpZGAgRasrjdMZB+lZOazd1x23/5 | |
| 30 | GAcm0rDREMnO9b2Jt+TNEZHT6d5KpVoExztZEZj8QCLXoic/SpFIqHGtpNlQXa+d | |
| 31 | BVqgQbIYjO8ldldxZ8YIyJDVF+9e/uBBwu6jBIIsEQKBgQDspEHCyyuh4LG+7BbZ | |
| 32 | eXlsfCxPTM6K9w31ZwHAwRtAuGqrOrE+pFJG9CEsFZbAI1aOGmZZdjexuSMcOlXl | |
| 33 | TIVJTQHoFtoGEsanYEXO4O1t02Ab/DCYSpXusXUraRBRPpsTC77Sh5mxLUNd23d9 | |
| 34 | NhnDBuwChAmC+IYexjkXeqPYFwKBgQDH08PEd+2PVo4MD8UVKUlEcgoyCr6ESiyp | |
| 35 | HfYyhhfd5x3DbZLoKCkunDfBs/hakQk8DA2nn4tl4ZjfmzXmX0EBx+E5YTdYshW7 | |
| 36 | ZcjN5x64B5PEOAR/NZA6agNlp3XGXXXgX+gnN6pgE49eVU22nZ4G+QBKD6NcCviB | |
| 37 | LBPUxMbvzQKBgHgZYRqonGtaqzsXfP1AjmSFnMNeWtDiU95BOf2Gw/sT3WcrsXr2 | |
| 38 | UJ+cFR3XkxvOk4YpVdp/igKT0ILqBGAMdvTdtWMB/gLpEpMt5B/7veRoS7XIRy1z | |
| 39 | ZSawP6QZfWOOX4vKAT29/j2SmEcRNFKC245EfBFGy8EBuqfxuFX3MyJfAoGBAJ0y | |
| 40 | tjsErVmpma1baosvI3g4zlR3p1CimWehLmCopHXorr1iocMIdP0535L+ZU258y3N | |
| 41 | vaA0HpFTW9PsYgaMwLMJ7uAY3lVkIzx84e849i2HqHMgLkl0dbW+WFXL2xblxylv | |
| 42 | yU2wuNNED/EB4lTawcpycAvTKYvrBXt4lVE4S9exAoGAGl6vZV3zyw4jpIw4uDfk | |
| 43 | LTPYUrghFDDGKExyeOnC/W9pqR2veqzfBz02C3jqwhewoqgAcnNc2sg0rJmM+6Oz | |
| 44 | Z2mmGZTHO9xR++7+W7e8AkQBbS6TB8a+7yNcM4USLP+b9sX5N+8gFhFs9tG7j/no | |
| 45 | G44qLsJ/yve7/QsOA37uEMs= | |
| 46 | -----END PRIVATE KEY----- |
| r0 | r250110 | |
|---|---|---|
| 1 | // Copyright (c) 2004-2013 Sergey Lyubka | |
| 2 | // Copyright (c) 2013-2014 Cesanta Software Limited | |
| 3 | ||
| 4 | #undef UNICODE // Use ANSI WinAPI functions | |
| 5 | #undef _UNICODE // Use multibyte encoding on Windows | |
| 6 | #define _MBCS // Use multibyte encoding on Windows | |
| 7 | #define _WIN32_WINNT 0x500 // Enable MIIM_BITMAP | |
| 8 | #define _CRT_SECURE_NO_WARNINGS // Disable deprecation warning in VS2005 | |
| 9 | #define _XOPEN_SOURCE 600 // For PATH_MAX on linux | |
| 10 | #undef WIN32_LEAN_AND_MEAN // Let windows.h always include winsock2.h | |
| 11 | ||
| 12 | #include <sys/stat.h> | |
| 13 | #include <stdio.h> | |
| 14 | #include <stdlib.h> | |
| 15 | #include <signal.h> | |
| 16 | #include <string.h> | |
| 17 | #include <errno.h> | |
| 18 | #include <limits.h> | |
| 19 | #include <stddef.h> | |
| 20 | #include <stdarg.h> | |
| 21 | #include <ctype.h> | |
| 22 | #include <time.h> | |
| 23 | ||
| 24 | #include "mongoose.h" | |
| 25 | ||
| 26 | #ifdef _WIN32 | |
| 27 | #include <windows.h> | |
| 28 | #include <direct.h> // For chdir() | |
| 29 | #include <winsvc.h> | |
| 30 | #include <shlobj.h> | |
| 31 | ||
| 32 | #ifndef PATH_MAX | |
| 33 | #define PATH_MAX MAX_PATH | |
| 34 | #endif | |
| 35 | ||
| 36 | #ifndef S_ISDIR | |
| 37 | #define S_ISDIR(x) ((x) & _S_IFDIR) | |
| 38 | #endif | |
| 39 | ||
| 40 | #define DIRSEP '\\' | |
| 41 | #define snprintf _snprintf | |
| 42 | #define vsnprintf _vsnprintf | |
| 43 | #ifndef sleep | |
| 44 | #define sleep(x) Sleep((x) * 1000) | |
| 45 | #endif | |
| 46 | #define abs_path(rel, abs, abs_size) _fullpath((abs), (rel), (abs_size)) | |
| 47 | #define SIGCHLD 0 | |
| 48 | typedef struct _stat file_stat_t; | |
| 49 | #define stat(x, y) _stat((x), (y)) | |
| 50 | #else | |
| 51 | typedef struct stat file_stat_t; | |
| 52 | #include <sys/wait.h> | |
| 53 | #include <unistd.h> | |
| 54 | ||
| 55 | #ifdef IOS | |
| 56 | #include <ifaddrs.h> | |
| 57 | #endif | |
| 58 | ||
| 59 | #define DIRSEP '/' | |
| 60 | #define __cdecl | |
| 61 | #define abs_path(rel, abs, abs_size) realpath((rel), (abs)) | |
| 62 | #endif // _WIN32 | |
| 63 | ||
| 64 | #define MAX_OPTIONS 100 | |
| 65 | #define MAX_CONF_FILE_LINE_SIZE (8 * 1024) | |
| 66 | ||
| 67 | #ifndef MVER | |
| 68 | #define MVER MONGOOSE_VERSION | |
| 69 | #endif | |
| 70 | ||
| 71 | static int exit_flag; | |
| 72 | static char server_name[50]; // Set by init_server_name() | |
| 73 | static char s_config_file[PATH_MAX]; // Set by process_command_line_arguments | |
| 74 | static struct mg_server *server; // Set by start_mongoose() | |
| 75 | static const char *s_default_document_root = "."; | |
| 76 | static const char *s_default_listening_port = "8080"; | |
| 77 | static char **s_argv = { NULL }; | |
| 78 | ||
| 79 | static void set_options(char *argv[]); | |
| 80 | ||
| 81 | #if !defined(CONFIG_FILE) | |
| 82 | #define CONFIG_FILE "mongoose.conf" | |
| 83 | #endif /* !CONFIG_FILE */ | |
| 84 | ||
| 85 | static void __cdecl signal_handler(int sig_num) { | |
| 86 | // Reinstantiate signal handler | |
| 87 | signal(sig_num, signal_handler); | |
| 88 | ||
| 89 | #ifndef _WIN32 | |
| 90 | // Do not do the trick with ignoring SIGCHLD, cause not all OSes (e.g. QNX) | |
| 91 | // reap zombies if SIGCHLD is ignored. On QNX, for example, waitpid() | |
| 92 | // fails if SIGCHLD is ignored, making system() non-functional. | |
| 93 | if (sig_num == SIGCHLD) { | |
| 94 | do {} while (waitpid(-1, &sig_num, WNOHANG) > 0); | |
| 95 | } else | |
| 96 | #endif | |
| 97 | { exit_flag = sig_num; } | |
| 98 | } | |
| 99 | ||
| 100 | static void vnotify(const char *fmt, va_list ap, int must_exit) { | |
| 101 | vfprintf(stderr, fmt, ap); | |
| 102 | fputc('\n', stderr); | |
| 103 | if (must_exit) { | |
| 104 | exit(EXIT_FAILURE); | |
| 105 | } | |
| 106 | } | |
| 107 | ||
| 108 | static void notify(const char *fmt, ...) { | |
| 109 | va_list ap; | |
| 110 | va_start(ap, fmt); | |
| 111 | vnotify(fmt, ap, 0); | |
| 112 | va_end(ap); | |
| 113 | } | |
| 114 | ||
| 115 | static void die(const char *fmt, ...) { | |
| 116 | va_list ap; | |
| 117 | va_start(ap, fmt); | |
| 118 | vnotify(fmt, ap, 1); | |
| 119 | va_end(ap); | |
| 120 | } | |
| 121 | ||
| 122 | static void show_usage_and_exit(void) { | |
| 123 | const char **names; | |
| 124 | int i; | |
| 125 | ||
| 126 | fprintf(stderr, "Mongoose version %s (c) Sergey Lyubka, built on %s\n", | |
| 127 | MVER, __DATE__); | |
| 128 | fprintf(stderr, "Usage:\n"); | |
| 129 | #if !defined(MONGOOSE_NO_AUTH) && !defined(MONGOOSE_NO_FILESYSTEM) | |
| 130 | fprintf(stderr, " mongoose -A <htpasswd_file> <realm> <user> <passwd>\n"); | |
| 131 | #endif | |
| 132 | fprintf(stderr, " mongoose [config_file]\n"); | |
| 133 | fprintf(stderr, " mongoose [-option value ...]\n"); | |
| 134 | fprintf(stderr, "\nOPTIONS:\n"); | |
| 135 | ||
| 136 | names = mg_get_valid_option_names(); | |
| 137 | for (i = 0; names[i] != NULL; i += 2) { | |
| 138 | fprintf(stderr, " -%s %s\n", | |
| 139 | names[i], names[i + 1] == NULL ? "<empty>" : names[i + 1]); | |
| 140 | } | |
| 141 | exit(EXIT_FAILURE); | |
| 142 | } | |
| 143 | ||
| 144 | #define EV_HANDLER NULL | |
| 145 | ||
| 146 | static char *sdup(const char *str) { | |
| 147 | char *p; | |
| 148 | if ((p = (char *) malloc(strlen(str) + 1)) != NULL) { | |
| 149 | strcpy(p, str); | |
| 150 | } | |
| 151 | return p; | |
| 152 | } | |
| 153 | ||
| 154 | static void set_option(char **options, const char *name, const char *value) { | |
| 155 | int i; | |
| 156 | ||
| 157 | for (i = 0; i < MAX_OPTIONS - 3; i++) { | |
| 158 | if (options[i] == NULL) { | |
| 159 | options[i] = sdup(name); | |
| 160 | options[i + 1] = sdup(value); | |
| 161 | options[i + 2] = NULL; | |
| 162 | break; | |
| 163 | } else if (!strcmp(options[i], name)) { | |
| 164 | free(options[i + 1]); | |
| 165 | options[i + 1] = sdup(value); | |
| 166 | break; | |
| 167 | } | |
| 168 | } | |
| 169 | ||
| 170 | if (i == MAX_OPTIONS - 3) { | |
| 171 | die("%s", "Too many options specified"); | |
| 172 | } | |
| 173 | } | |
| 174 | ||
| 175 | static void process_command_line_arguments(char *argv[], char **options) { | |
| 176 | char line[MAX_CONF_FILE_LINE_SIZE], opt[sizeof(line)], val[sizeof(line)], | |
| 177 | *p, cpath[PATH_MAX]; | |
| 178 | FILE *fp = NULL; | |
| 179 | size_t i, cmd_line_opts_start = 1, line_no = 0; | |
| 180 | ||
| 181 | // Should we use a config file ? | |
| 182 | if (argv[1] != NULL && argv[1][0] != '-') { | |
| 183 | snprintf(cpath, sizeof(cpath), "%s", argv[1]); | |
| 184 | cmd_line_opts_start = 2; | |
| 185 | } else if ((p = strrchr(argv[0], DIRSEP)) == NULL) { | |
| 186 | // No command line flags specified. Look where binary lives | |
| 187 | snprintf(cpath, sizeof(cpath), "%s", CONFIG_FILE); | |
| 188 | } else { | |
| 189 | snprintf(cpath, sizeof(cpath), "%.*s%c%s", | |
| 190 | (int) (p - argv[0]), argv[0], DIRSEP, CONFIG_FILE); | |
| 191 | } | |
| 192 | abs_path(cpath, s_config_file, sizeof(s_config_file)); | |
| 193 | ||
| 194 | fp = fopen(s_config_file, "r"); | |
| 195 | ||
| 196 | // If config file was set in command line and open failed, die | |
| 197 | if (cmd_line_opts_start == 2 && fp == NULL) { | |
| 198 | die("Cannot open config file %s: %s", s_config_file, strerror(errno)); | |
| 199 | } | |
| 200 | ||
| 201 | // Load config file settings first | |
| 202 | if (fp != NULL) { | |
| 203 | fprintf(stderr, "Loading config file %s\n", s_config_file); | |
| 204 | ||
| 205 | // Loop over the lines in config file | |
| 206 | while (fgets(line, sizeof(line), fp) != NULL) { | |
| 207 | line_no++; | |
| 208 | ||
| 209 | // Ignore empty lines and comments | |
| 210 | for (i = 0; isspace(* (unsigned char *) &line[i]); ) i++; | |
| 211 | if (line[i] == '#' || line[i] == '\0') { | |
| 212 | continue; | |
| 213 | } | |
| 214 | ||
| 215 | if (sscanf(line, "%s %[^\r\n#]", opt, val) != 2) { | |
| 216 | printf("%s: line %d is invalid, ignoring it:\n %s", | |
| 217 | s_config_file, (int) line_no, line); | |
| 218 | } else { | |
| 219 | set_option(options, opt, val); | |
| 220 | } | |
| 221 | } | |
| 222 | ||
| 223 | fclose(fp); | |
| 224 | } | |
| 225 | ||
| 226 | // If we're under MacOS and started by launchd, then the second | |
| 227 | // argument is process serial number, -psn_..... | |
| 228 | // In this case, don't process arguments at all. | |
| 229 | if (argv[1] == NULL || memcmp(argv[1], "-psn_", 5) != 0) { | |
| 230 | // Handle command line flags. | |
| 231 | // They override config file and default settings. | |
| 232 | for (i = cmd_line_opts_start; argv[i] != NULL; i += 2) { | |
| 233 | if (argv[i][0] != '-' || argv[i + 1] == NULL) { | |
| 234 | show_usage_and_exit(); | |
| 235 | } | |
| 236 | set_option(options, &argv[i][1], argv[i + 1]); | |
| 237 | } | |
| 238 | } | |
| 239 | } | |
| 240 | ||
| 241 | static void init_server_name(void) { | |
| 242 | const char *descr = ""; | |
| 243 | snprintf(server_name, sizeof(server_name), "Mongoose web server v.%s%s", | |
| 244 | MVER, descr); | |
| 245 | } | |
| 246 | ||
| 247 | static int is_path_absolute(const char *path) { | |
| 248 | #ifdef _WIN32 | |
| 249 | return path != NULL && | |
| 250 | ((path[0] == '\\' && path[1] == '\\') || // UNC path, e.g. \\server\dir | |
| 251 | (isalpha(path[0]) && path[1] == ':' && path[2] == '\\')); // E.g. X:\dir | |
| 252 | #else | |
| 253 | return path != NULL && path[0] == '/'; | |
| 254 | #endif | |
| 255 | } | |
| 256 | ||
| 257 | static char *get_option(char **options, const char *option_name) { | |
| 258 | int i; | |
| 259 | ||
| 260 | for (i = 0; options[i] != NULL; i++) | |
| 261 | if (!strcmp(options[i], option_name)) | |
| 262 | return options[i + 1]; | |
| 263 | ||
| 264 | return NULL; | |
| 265 | } | |
| 266 | ||
| 267 | static void *serving_thread_func(void *param) { | |
| 268 | struct mg_server *srv = (struct mg_server *) param; | |
| 269 | while (exit_flag == 0) { | |
| 270 | mg_poll_server(srv, 1000); | |
| 271 | } | |
| 272 | return NULL; | |
| 273 | } | |
| 274 | ||
| 275 | static int path_exists(const char *path, int is_dir) { | |
| 276 | file_stat_t st; | |
| 277 | return path == NULL || (stat(path, &st) == 0 && | |
| 278 | ((S_ISDIR(st.st_mode) ? 1 : 0) == is_dir)); | |
| 279 | } | |
| 280 | ||
| 281 | static void verify_existence(char **options, const char *name, int is_dir) { | |
| 282 | const char *path = get_option(options, name); | |
| 283 | if (!path_exists(path, is_dir)) { | |
| 284 | notify("Invalid path for %s: [%s]: (%s). Make sure that path is either " | |
| 285 | "absolute, or it is relative to mongoose executable.", | |
| 286 | name, path, strerror(errno)); | |
| 287 | } | |
| 288 | } | |
| 289 | ||
| 290 | static void set_absolute_path(char *options[], const char *option_name) { | |
| 291 | char path[PATH_MAX], abs[PATH_MAX], *option_value; | |
| 292 | const char *p; | |
| 293 | ||
| 294 | // Check whether option is already set | |
| 295 | option_value = get_option(options, option_name); | |
| 296 | ||
| 297 | // If option is already set and it is an absolute path, | |
| 298 | // leave it as it is -- it's already absolute. | |
| 299 | if (option_value != NULL && !is_path_absolute(option_value)) { | |
| 300 | // Not absolute. Use the directory where mongoose executable lives | |
| 301 | // be the relative directory for everything. | |
| 302 | // Extract mongoose executable directory into path. | |
| 303 | if ((p = strrchr(s_config_file, DIRSEP)) == NULL) { | |
| 304 | getcwd(path, sizeof(path)); | |
| 305 | } else { | |
| 306 | snprintf(path, sizeof(path), "%.*s", (int) (p - s_config_file), | |
| 307 | s_config_file); | |
| 308 | } | |
| 309 | ||
| 310 | strncat(path, "/", sizeof(path) - 1); | |
| 311 | strncat(path, option_value, sizeof(path) - 1); | |
| 312 | ||
| 313 | // Absolutize the path, and set the option | |
| 314 | abs_path(path, abs, sizeof(abs)); | |
| 315 | set_option(options, option_name, abs); | |
| 316 | } | |
| 317 | } | |
| 318 | ||
| 319 | #if !defined(MONGOOSE_NO_AUTH) && !defined(MONGOOSE_NO_FILESYSTEM) | |
| 320 | int modify_passwords_file(const char *fname, const char *domain, | |
| 321 | const char *user, const char *pass) { | |
| 322 | int found; | |
| 323 | char line[512], u[512], d[512], ha1[33], tmp[PATH_MAX]; | |
| 324 | FILE *fp, *fp2; | |
| 325 | ||
| 326 | found = 0; | |
| 327 | fp = fp2 = NULL; | |
| 328 | ||
| 329 | // Regard empty password as no password - remove user record. | |
| 330 | if (pass != NULL && pass[0] == '\0') { | |
| 331 | pass = NULL; | |
| 332 | } | |
| 333 | ||
| 334 | (void) snprintf(tmp, sizeof(tmp), "%s.tmp", fname); | |
| 335 | ||
| 336 | // Create the file if does not exist | |
| 337 | if ((fp = fopen(fname, "a+")) != NULL) { | |
| 338 | fclose(fp); | |
| 339 | } | |
| 340 | ||
| 341 | // Open the given file and temporary file | |
| 342 | if ((fp = fopen(fname, "r")) == NULL) { | |
| 343 | return 0; | |
| 344 | } else if ((fp2 = fopen(tmp, "w+")) == NULL) { | |
| 345 | fclose(fp); | |
| 346 | return 0; | |
| 347 | } | |
| 348 | ||
| 349 | // Copy the stuff to temporary file | |
| 350 | while (fgets(line, sizeof(line), fp) != NULL) { | |
| 351 | if (sscanf(line, "%[^:]:%[^:]:%*s", u, d) != 2) { | |
| 352 | continue; | |
| 353 | } | |
| 354 | ||
| 355 | if (!strcmp(u, user) && !strcmp(d, domain)) { | |
| 356 | found++; | |
| 357 | if (pass != NULL) { | |
| 358 | mg_md5(ha1, user, ":", domain, ":", pass, NULL); | |
| 359 | fprintf(fp2, "%s:%s:%s\n", user, domain, ha1); | |
| 360 | } | |
| 361 | } else { | |
| 362 | fprintf(fp2, "%s", line); | |
| 363 | } | |
| 364 | } | |
| 365 | ||
| 366 | // If new user, just add it | |
| 367 | if (!found && pass != NULL) { | |
| 368 | mg_md5(ha1, user, ":", domain, ":", pass, NULL); | |
| 369 | fprintf(fp2, "%s:%s:%s\n", user, domain, ha1); | |
| 370 | } | |
| 371 | ||
| 372 | // Close files | |
| 373 | fclose(fp); | |
| 374 | fclose(fp2); | |
| 375 | ||
| 376 | // Put the temp file in place of real file | |
| 377 | remove(fname); | |
| 378 | rename(tmp, fname); | |
| 379 | ||
| 380 | return 1; | |
| 381 | } | |
| 382 | #endif | |
| 383 | ||
| 384 | static void start_mongoose(int argc, char *argv[]) { | |
| 385 | s_argv = argv; | |
| 386 | if ((server = mg_create_server(NULL, EV_HANDLER)) == NULL) { | |
| 387 | die("%s", "Failed to start Mongoose."); | |
| 388 | } | |
| 389 | ||
| 390 | #if !defined(MONGOOSE_NO_AUTH) && !defined(MONGOOSE_NO_FILESYSTEM) | |
| 391 | // Edit passwords file if -A option is specified | |
| 392 | if (argc > 1 && !strcmp(argv[1], "-A")) { | |
| 393 | if (argc != 6) { | |
| 394 | show_usage_and_exit(); | |
| 395 | } | |
| 396 | exit(modify_passwords_file(argv[2], argv[3], argv[4], argv[5]) ? | |
| 397 | EXIT_SUCCESS : EXIT_FAILURE); | |
| 398 | } | |
| 399 | #endif | |
| 400 | ||
| 401 | // Show usage if -h or --help options are specified | |
| 402 | if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))) { | |
| 403 | show_usage_and_exit(); | |
| 404 | } | |
| 405 | set_options(argv); | |
| 406 | } | |
| 407 | ||
| 408 | static void set_options(char *argv[]) { | |
| 409 | char *options[MAX_OPTIONS]; | |
| 410 | int i; | |
| 411 | ||
| 412 | options[0] = NULL; | |
| 413 | set_option(options, "document_root", s_default_document_root); | |
| 414 | set_option(options, "listening_port", s_default_listening_port); | |
| 415 | ||
| 416 | // Update config based on command line arguments | |
| 417 | process_command_line_arguments(argv, options); | |
| 418 | ||
| 419 | // Make sure we have absolute paths for files and directories | |
| 420 | // https://github.com/valenok/mongoose/issues/181 | |
| 421 | set_absolute_path(options, "document_root"); | |
| 422 | set_absolute_path(options, "dav_auth_file"); | |
| 423 | set_absolute_path(options, "cgi_interpreter"); | |
| 424 | set_absolute_path(options, "access_log_file"); | |
| 425 | set_absolute_path(options, "global_auth_file"); | |
| 426 | set_absolute_path(options, "ssl_certificate"); | |
| 427 | ||
| 428 | if (!path_exists(get_option(options, "document_root"), 1)) { | |
| 429 | set_option(options, "document_root", s_default_document_root); | |
| 430 | set_absolute_path(options, "document_root"); | |
| 431 | notify("Setting document_root to [%s]", | |
| 432 | mg_get_option(server, "document_root")); | |
| 433 | } | |
| 434 | ||
| 435 | // Make extra verification for certain options | |
| 436 | verify_existence(options, "document_root", 1); | |
| 437 | verify_existence(options, "cgi_interpreter", 0); | |
| 438 | verify_existence(options, "ssl_certificate", 0); | |
| 439 | ||
| 440 | for (i = 0; options[i] != NULL; i += 2) { | |
| 441 | const char *msg = mg_set_option(server, options[i], options[i + 1]); | |
| 442 | if (msg != NULL) { | |
| 443 | notify("Failed to set option [%s] to [%s]: %s", | |
| 444 | options[i], options[i + 1], msg); | |
| 445 | if (!strcmp(options[i], "listening_port")) { | |
| 446 | mg_set_option(server, "listening_port", s_default_listening_port); | |
| 447 | notify("Setting %s to [%s]", options[i], s_default_listening_port); | |
| 448 | } | |
| 449 | } | |
| 450 | free(options[i]); | |
| 451 | free(options[i + 1]); | |
| 452 | } | |
| 453 | ||
| 454 | // Change current working directory to document root. This way, | |
| 455 | // scripts can use relative paths. | |
| 456 | chdir(mg_get_option(server, "document_root")); | |
| 457 | ||
| 458 | #if 0 | |
| 459 | // Add an ability to pass listening socket to mongoose | |
| 460 | { | |
| 461 | const char *env = getenv("MONGOOSE_LISTENING_SOCKET"); | |
| 462 | if (env != NULL && atoi(env) > 0 ) { | |
| 463 | mg_set_listening_socket(server, atoi(env)); | |
| 464 | } | |
| 465 | } | |
| 466 | #endif | |
| 467 | ||
| 468 | // Setup signal handler: quit on Ctrl-C | |
| 469 | signal(SIGTERM, signal_handler); | |
| 470 | signal(SIGINT, signal_handler); | |
| 471 | #ifndef _WIN32 | |
| 472 | signal(SIGCHLD, signal_handler); | |
| 473 | #endif | |
| 474 | } | |
| 475 | ||
| 476 | int main(int argc, char *argv[]) { | |
| 477 | init_server_name(); | |
| 478 | start_mongoose(argc, argv); | |
| 479 | printf("%s serving [%s] on port %s\n", | |
| 480 | server_name, mg_get_option(server, "document_root"), | |
| 481 | mg_get_option(server, "listening_port")); | |
| 482 | fflush(stdout); // Needed, Windows terminals might not be line-buffered | |
| 483 | serving_thread_func(server); | |
| 484 | printf("Exiting on signal %d ...", exit_flag); | |
| 485 | fflush(stdout); | |
| 486 | mg_destroy_server(&server); | |
| 487 | printf("%s\n", " done."); | |
| 488 | ||
| 489 | return EXIT_SUCCESS; | |
| 490 | } |
| r0 | r250110 | |
|---|---|---|
| 1 | # Copyright (c) 2014 Cesanta Software | |
| 2 | # All rights reserved | |
| 3 | ||
| 4 | PROG = websocket_chat | |
| 5 | CFLAGS = -W -Wall -I../.. -pthread -g -O0 $(CFLAGS_EXTRA) | |
| 6 | SOURCES = $(PROG).c ../../mongoose.c | |
| 7 | ||
| 8 | $(PROG): $(SOURCES) | |
| 9 | $(CC) -o $(PROG) $(SOURCES) $(CFLAGS) | |
| 10 | ||
| 11 | clean: | |
| 12 | rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib |
| r0 | r250110 | |
|---|---|---|
| 1 | <!DOCTYPE html> | |
| 2 | <html lang="en"> | |
| 3 | <head> | |
| 4 | <meta charset="utf-8" /> | |
| 5 | <title>WebSocket Test</title> | |
| 6 | <meta name="viewport" content="width=device-width, initial-scale=1" /> | |
| 7 | <style type="text/css"> | |
| 8 | body { | |
| 9 | background-color: #cde; margin: 0; | |
| 10 | padding: 0; font: 14px Helvetica, Arial, sans-serif; | |
| 11 | } | |
| 12 | div.content { | |
| 13 | width: 800px; margin: 2em auto; padding: 20px 50px; | |
| 14 | background-color: #fff; border-radius: 1em; | |
| 15 | } | |
| 16 | #messages { | |
| 17 | border: 2px solid #fec; border-radius: 1em; | |
| 18 | height: 10em; overflow: scroll; padding: 0.5em 1em; | |
| 19 | } | |
| 20 | a:link, a:visited { color: #69c; text-decoration: none; } | |
| 21 | @media (max-width: 700px) { | |
| 22 | body { background-color: #fff; } | |
| 23 | div.content { | |
| 24 | width: auto; margin: 0 auto; border-radius: 0; | |
| 25 | padding: 1em; | |
| 26 | } | |
| 27 | } | |
| 28 | </style> | |
| 29 | ||
| 30 | <script language="javascript" type="text/javascript"> | |
| 31 | ||
| 32 | var rooms = []; | |
| 33 | var ws = new WebSocket('ws://' + location.host + '/ws'); | |
| 34 | ||
| 35 | if (!window.console) { window.console = { log: function() {} } }; | |
| 36 | ||
| 37 | ws.onopen = function(ev) { console.log(ev); }; | |
| 38 | ws.onerror = function(ev) { console.log(ev); }; | |
| 39 | ws.onclose = function(ev) { console.log(ev); }; | |
| 40 | ws.onmessage = function(ev) { | |
| 41 | console.log(ev); | |
| 42 | var m = (ev.data || '').match(/^(\S+) (.+)/); | |
| 43 | if (m[1] == 'id') { | |
| 44 | document.getElementById('my_id').innerText = m[2]; | |
| 45 | } else if (m[1] == 'msg') { | |
| 46 | var div = document.createElement('div'); | |
| 47 | div.innerHTML = m[2]; | |
| 48 | document.getElementById('messages').appendChild(div); | |
| 49 | } | |
| 50 | }; | |
| 51 | ||
| 52 | window.onload = function() { | |
| 53 | document.getElementById('send_button').onclick = function(ev) { | |
| 54 | var msg = document.getElementById('send_input').value; | |
| 55 | ws.send('msg ' + msg); | |
| 56 | }; | |
| 57 | document.getElementById('room_sel').onchange = function(ev) { | |
| 58 | var roomName = this.value || '?'; | |
| 59 | ws.send('join ' + roomName); | |
| 60 | }; | |
| 61 | }; | |
| 62 | </script> | |
| 63 | </head> | |
| 64 | <body> | |
| 65 | <div class="content"> | |
| 66 | <h1>Websocket PubSub Demonstration</h1> | |
| 67 | ||
| 68 | <p> | |
| 69 | This page demonstrates how Mongoose web server could be used to implement | |
| 70 | <a href="http://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern"> | |
| 71 | publish–subscribe pattern</a>. Open this page in several browser | |
| 72 | windows. Each window initiates persistent | |
| 73 | <a href="http://en.wikipedia.org/wiki/WebSocket">WebSocket</a> | |
| 74 | connection with Mongoose, making each browser window a websocket client. | |
| 75 | Join a room, send messages, and see messages sent by other clients. | |
| 76 | </p> | |
| 77 | ||
| 78 | <p> | |
| 79 | My ID: <b><span id="my_id"></b></span> | |
| 80 | </p> | |
| 81 | <p> | |
| 82 | Join room: <select id="room_sel"> | |
| 83 | <option value="">-- select room -- </option> | |
| 84 | <option>A</option> | |
| 85 | <option>B</option> | |
| 86 | </select> | |
| 87 | </p> | |
| 88 | ||
| 89 | <div id="messages"> | |
| 90 | </div> | |
| 91 | ||
| 92 | <p> | |
| 93 | <input type="text" id="send_input" /> | |
| 94 | <button id="send_button">Send Message</button> | |
| 95 | </p> | |
| 96 | </div> | |
| 97 | </body> | |
| 98 | </html> |
| r0 | r250110 | |
|---|---|---|
| 1 | // Copyright (c) 2013-2014 Cesanta Software Limited | |
| 2 | // $Date: 2014-09-09 17:07:55 UTC $ | |
| 3 | ||
| 4 | #include <string.h> | |
| 5 | #include <time.h> | |
| 6 | #include <signal.h> | |
| 7 | #include <stdlib.h> | |
| 8 | #include "mongoose.h" | |
| 9 | ||
| 10 | static int s_signal_received = 0; | |
| 11 | static struct mg_server *s_server = NULL; | |
| 12 | ||
| 13 | // Data associated with each websocket connection | |
| 14 | struct conn_data { | |
| 15 | int room; | |
| 16 | }; | |
| 17 | ||
| 18 | static void signal_handler(int sig_num) { | |
| 19 | signal(sig_num, signal_handler); // Reinstantiate signal handler | |
| 20 | s_signal_received = sig_num; | |
| 21 | } | |
| 22 | ||
| 23 | static void handle_websocket_message(struct mg_connection *conn) { | |
| 24 | struct conn_data *d = (struct conn_data *) conn->connection_param; | |
| 25 | struct mg_connection *c; | |
| 26 | ||
| 27 | printf("[%.*s]\n", (int) conn->content_len, conn->content); | |
| 28 | if (conn->content_len > 5 && !memcmp(conn->content, "join ", 5)) { | |
| 29 | // Client joined new room | |
| 30 | d->room = conn->content[5]; | |
| 31 | } else if (conn->content_len > 4 && !memcmp(conn->content, "msg ", 4) && | |
| 32 | d->room != 0 && d->room != '?') { | |
| 33 | // Client has sent a message. Push this message to all clients | |
| 34 | // that are subscribed to the same room as client | |
| 35 | for (c = mg_next(s_server, NULL); c != NULL; c = mg_next(s_server, c)) { | |
| 36 | struct conn_data *d2 = (struct conn_data *) c->connection_param; | |
| 37 | if (!c->is_websocket || d2->room != d->room) continue; | |
| 38 | mg_websocket_printf(c, WEBSOCKET_OPCODE_TEXT, "msg %c %p %.*s", | |
| 39 | (char) d->room, conn, | |
| 40 | conn->content_len - 4, conn->content + 4); | |
| 41 | } | |
| 42 | } | |
| 43 | } | |
| 44 | ||
| 45 | static int ev_handler(struct mg_connection *conn, enum mg_event ev) { | |
| 46 | switch (ev) { | |
| 47 | case MG_REQUEST: | |
| 48 | if (conn->is_websocket) { | |
| 49 | handle_websocket_message(conn); | |
| 50 | return MG_TRUE; | |
| 51 | } else { | |
| 52 | mg_send_file(conn, "index.html", NULL); // Return MG_MORE after! | |
| 53 | return MG_MORE; | |
| 54 | } | |
| 55 | case MG_WS_CONNECT: | |
| 56 | // New websocket connection. Send connection ID back to the client. | |
| 57 | conn->connection_param = calloc(1, sizeof(struct conn_data)); | |
| 58 | mg_websocket_printf(conn, WEBSOCKET_OPCODE_TEXT, "id %p", conn); | |
| 59 | return MG_FALSE; | |
| 60 | case MG_CLOSE: | |
| 61 | free(conn->connection_param); | |
| 62 | return MG_TRUE; | |
| 63 | case MG_AUTH: | |
| 64 | return MG_TRUE; | |
| 65 | default: | |
| 66 | return MG_FALSE; | |
| 67 | } | |
| 68 | } | |
| 69 | ||
| 70 | int main(void) { | |
| 71 | s_server = mg_create_server(NULL, ev_handler); | |
| 72 | mg_set_option(s_server, "listening_port", "8080"); | |
| 73 | ||
| 74 | signal(SIGTERM, signal_handler); | |
| 75 | signal(SIGINT, signal_handler); | |
| 76 | ||
| 77 | printf("Started on port %s\n", mg_get_option(s_server, "listening_port")); | |
| 78 | while (s_signal_received == 0) { | |
| 79 | mg_poll_server(s_server, 100); | |
| 80 | } | |
| 81 | mg_destroy_server(&s_server); | |
| 82 | return 0; | |
| 83 | } |
| r0 | r250110 | |
|---|---|---|
| 1 | # Copyright (c) 2014 Cesanta Software | |
| 2 | # All rights reserved | |
| 3 | ||
| 4 | PROG = websocket_echo_server | |
| 5 | CFLAGS = -W -Wall -I../.. -pthread -g -O0 $(CFLAGS_EXTRA) | |
| 6 | SOURCES = $(PROG).c ../../mongoose.c | |
| 7 | ||
| 8 | $(PROG): $(SOURCES) | |
| 9 | $(CC) -o $(PROG) $(SOURCES) $(CFLAGS) | |
| 10 | ||
| 11 | clean: | |
| 12 | rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib |
| r0 | r250110 | |
|---|---|---|
| 1 | <!DOCTYPE html> | |
| 2 | <meta charset="utf-8" /> | |
| 3 | <title>WebSocket Test</title> | |
| 4 | <script language="javascript" type="text/javascript"> | |
| 5 | ||
| 6 | var out = function(message) { | |
| 7 | var div = document.createElement('div'); | |
| 8 | div.innerHTML = message; | |
| 9 | document.getElementById('output').appendChild(div); | |
| 10 | }; | |
| 11 | ||
| 12 | window.onload = function() { | |
| 13 | var url = 'ws://' + location.host + '/ws'; | |
| 14 | var num_messages = 0; | |
| 15 | ||
| 16 | websocket = new WebSocket(url); | |
| 17 | websocket.onopen = function(ev) { | |
| 18 | out('CONNECTED'); | |
| 19 | var msg = 'Не всё подчиняется разуму. Но всё подчиняется упорству. '; | |
| 20 | out('SENT: ' + msg); | |
| 21 | websocket.send(msg); | |
| 22 | }; | |
| 23 | websocket.onclose = function(ev) { | |
| 24 | out('DISCONNECTED'); | |
| 25 | }; | |
| 26 | websocket.onmessage = function(ev) { | |
| 27 | if (!ev.data) { | |
| 28 | out('<span style="color: blue;">PING... </span>'); | |
| 29 | } else { | |
| 30 | out('<span style="color: blue;">RESPONSE: ' + ev.data + ' </span>'); | |
| 31 | num_messages++; | |
| 32 | } | |
| 33 | if (num_messages > 3) { | |
| 34 | websocket.send('exit'); | |
| 35 | } | |
| 36 | }; | |
| 37 | websocket.onerror = function(ev) { | |
| 38 | out('<span style="color: red; ">ERROR: </span> ' + ev.data); | |
| 39 | }; | |
| 40 | }; | |
| 41 | </script> | |
| 42 | <style> div {font: small Verdana; } </style> | |
| 43 | <h2>Mongoose WebSocket Test</h2> | |
| 44 | ||
| 45 | <div id="output"></div> | |
| 46 | </html> |
| r0 | r250110 | |
|---|---|---|
| 1 | // Copyright (c) 2013-2014 Cesanta Software Limited | |
| 2 | // $Date: 2014-09-09 17:07:55 UTC $ | |
| 3 | ||
| 4 | #include <string.h> | |
| 5 | #include <time.h> | |
| 6 | #include "mongoose.h" | |
| 7 | ||
| 8 | static void push_message(struct mg_server *server, time_t current_time) { | |
| 9 | struct mg_connection *c; | |
| 10 | char buf[20]; | |
| 11 | int len = sprintf(buf, "%lu", (unsigned long) current_time); | |
| 12 | ||
| 13 | // Iterate over all connections, and push current time message to websocket ones. | |
| 14 | for (c = mg_next(server, NULL); c != NULL; c = mg_next(server, c)) { | |
| 15 | if (c->is_websocket) { | |
| 16 | mg_websocket_write(c, 1, buf, len); | |
| 17 | } | |
| 18 | } | |
| 19 | } | |
| 20 | ||
| 21 | static int send_reply(struct mg_connection *conn) { | |
| 22 | if (conn->is_websocket) { | |
| 23 | // This handler is called for each incoming websocket frame, one or more | |
| 24 | // times for connection lifetime. | |
| 25 | // Echo websocket data back to the client. | |
| 26 | mg_websocket_write(conn, 1, conn->content, conn->content_len); | |
| 27 | return conn->content_len == 4 && !memcmp(conn->content, "exit", 4) ? | |
| 28 | MG_FALSE : MG_TRUE; | |
| 29 | } else { | |
| 30 | mg_send_file(conn, "index.html", NULL); | |
| 31 | return MG_MORE; | |
| 32 | } | |
| 33 | } | |
| 34 | ||
| 35 | static int ev_handler(struct mg_connection *conn, enum mg_event ev) { | |
| 36 | switch (ev) { | |
| 37 | case MG_AUTH: return MG_TRUE; | |
| 38 | case MG_REQUEST: return send_reply(conn); | |
| 39 | default: return MG_FALSE; | |
| 40 | } | |
| 41 | } | |
| 42 | ||
| 43 | int main(void) { | |
| 44 | struct mg_server *server = mg_create_server(NULL, ev_handler); | |
| 45 | time_t current_timer = 0, last_timer = time(NULL); | |
| 46 | ||
| 47 | mg_set_option(server, "listening_port", "8080"); | |
| 48 | ||
| 49 | printf("Started on port %s\n", mg_get_option(server, "listening_port")); | |
| 50 | for (;;) { | |
| 51 | mg_poll_server(server, 100); | |
| 52 | current_timer = time(NULL); | |
| 53 | if (current_timer - last_timer > 0) { | |
| 54 | last_timer = current_timer; | |
| 55 | push_message(server, current_timer); | |
| 56 | } | |
| 57 | } | |
| 58 | ||
| 59 | mg_destroy_server(&server); | |
| 60 | return 0; | |
| 61 | } |
| r0 | r250110 | |
|---|---|---|
| 1 | # Copyright (c) 2014 Cesanta Software | |
| 2 | # All rights reserved | |
| 3 | ||
| 4 | PROG = ws_ssl | |
| 5 | CFLAGS = -W -Wall -I../.. -I. -pthread -g -O0 -DMONGOOSE_ENABLE_THREADS -DNS_ENABLE_SSL -DSSL_WRAPPER_USE_AS_LIBRARY $(CFLAGS_EXTRA) | |
| 6 | LDFLAGS = -lssl | |
| 7 | SOURCES = ws_ssl.c ../../mongoose.c ssl_wrapper.c | |
| 8 | ||
| 9 | # PolarSSL paths and flags | |
| 10 | POLARSSL_PATH = /usr/local | |
| 11 | POLARSSLCOMPAT_PATH = ./../../../polar | |
| 12 | SOURCES_POLAR = $(SOURCES) $(POLARSSLCOMPAT_PATH)/polarssl_compat.c | |
| 13 | INCDIR_POLAR = -I$(POLARSSLCOMPAT_PATH) -I$(POLARSSL_PATH)/include | |
| 14 | LDFLAGS_POLAR = -L$(POLARSSL_PATH)/lib -lmbedtls | |
| 15 | CFLAGS_POLAR = $(CFLAGS) $(INCDIR_POLAR) | |
| 16 | # | |
| 17 | ||
| 18 | all: $(PROG) | |
| 19 | ||
| 20 | $(PROG): $(SOURCES) | |
| 21 | $(CC) -o $(PROG) $(SOURCES) $(LDFLAGS) $(CFLAGS) | |
| 22 | ||
| 23 | polarssl: $(SOURCES_POLAR) | |
| 24 | $(CC) -o $(PROG) $(SOURCES_POLAR) $(LDFLAGS_POLAR) $(CFLAGS_POLAR) | |
| 25 | ||
| 26 | clean: | |
| 27 | rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib |
| r0 | r250110 | |
|---|---|---|
| 1 | -----BEGIN RSA PRIVATE KEY----- | |
| 2 | MIIEowIBAAKCAQEAwizPnrCx+/kPSdEeSJFLDXrBH+cSQsSLrCm99G1hCjzqSlIk | |
| 3 | 1BhkZMEHxBaiVLky4+M/nwhjwwRHI10h6U2Or3tbPLv7z94cPf+uCx1aF7TE3Fm3 | |
| 4 | 6YnDk+CjrYVFN5GRPGOPPdFxGoc+vFvQJyAAimvchnS1ZoEQFvermwzOnKspA6gc | |
| 5 | Px+7wnOeju9TyJuDr5ngtDXFnkcpkBNPxz3En4MJY4xJiaueafh9pIES2vSl7uP0 | |
| 6 | J/qot9v2rdiL7nt1H1vwseeEkZhQ+NLB5e2z4psyktJcwDX7wQ6j7JnKfHeP+ixO | |
| 7 | TUORgV4foBMVOqo//Guo92Q5HoLNK77V0y4+ZQIDAQABAoIBAGEsx+LlDs3JQQty | |
| 8 | KjOq8uKWElyC6bKcZkIMydGvg6b6AU6ceW3jnyqFJ/vMUAUSghNmQQq3yiVo2Kks | |
| 9 | DLKTa9sKYwisE0NeJsgoUtOhJttCTlrwU4f+t/AjtgY68f7zTLnqIV+Ql4ftM0pU | |
| 10 | sIFEFMExZbWsZrQb1w+Hd0wrRqNEbSOfSjHeigvuw1T3GH2tSBUTGTpcoewCzy7U | |
| 11 | PKS5pkYyiKySQQNqZTac3NHPjxdK6xxzwURZp1irKdiPdt04KHLVLX8KXelt/J0k | |
| 12 | AeYkVbpFIeQ9rNBerMEp6uRBt+nE5mvP+xx1XPqKRxuxbMyTnBXeOM2zS/a/dBiz | |
| 13 | fwokwcECgYEA9RSsv9AQ/AR8tt+aPEQvjhJ5pn/YbCb1DA9IDXpaq3tzacGd8JHj | |
| 14 | 3kUtb79nosu85LvSkAYmtzgfJs6xZyUkscra6q+xlsJ12QRxLzqfxcp9Y0wsdqM4 | |
| 15 | AcOwuiPKrjkWxOQpyWPWRwbmAefLfRMekHt4Y/QY0CwhslpnsOsj3O0CgYEAytOE | |
| 16 | 8I4GBfSQfSjXwfrso++Oi75VSsl5ZeiMGihfEhYFTE8/3rEZf7nf9iFSkN3TT+7f | |
| 17 | pFqQzddzPBZXlpVM6k1jcEjdpJizbeR8DmICpABFrZvKz1o8pQ2Yw+FYI86ih0x4 | |
| 18 | 806snMNgg/RgcVijXKFrC5joJOI+DVgwWoQyMFkCgYBxt4MkiV2oIkjf7ca6GgVa | |
| 19 | zbXGjOGV5Umkq96J6nDxyplVw/IN8xOhScX4aP6kahaep4vfKguCzjaeIh/stS5e | |
| 20 | lLqZVKZ5Roe6B7ag7HnAI+GkVm73KWrOXse8xui/iFvJRfkhqgJ9+HR3A9/GjD2N | |
| 21 | Ws0Uy+lLhn6oLAya6bA9TQKBgAVfZP4aRP6TY+Bs3Io+41XUWqpI+GlqvNR+PHfU | |
| 22 | 6e/ItYs37jEv78T6X3xdlZpQxfAwG6x22a8aLetBjEBo5Aiw1Bl9VKGvidE3ZDHd | |
| 23 | VsSRXUckAVNMyJ52pb1KktMf/h4nYGzRgLEGW+Ai8QsPlgQ2ImfEPSH8/DfORjmf | |
| 24 | ltTBAoGBAMxIZ52DrJvuxogSOfA1MoCD6a90trkXCquvi+A/fXojZ8BHmMQshvhK | |
| 25 | rAO7SDIV1i1Nh3jQ/oFWE8KOprqrOLO6jNTyF65vh+zk7ztGsEME9FkDhHasUiXf | |
| 26 | t5PE9KeTChHRvIa4FGCl9We9GftE5Ii77LWMOIq22pyxYbvHQFEf | |
| 27 | -----END RSA PRIVATE KEY----- | |
| 28 | -----BEGIN CERTIFICATE----- | |
| 29 | MIIDlDCCAnygAwIBAgIJAIOoO+AapJ5WMA0GCSqGSIb3DQEBBQUAMDoxDDAKBgNV | |
| 30 | BAMTA3dzMTEMMAoGA1UEChMDd3MxMQswCQYDVQQGEwJJRTEPMA0GA1UEBxMGRHVi | |
| 31 | bGluMB4XDTE0MDgwMzA5MTU0NVoXDTI0MDczMTA5MTU0NVowOjEMMAoGA1UEAxMD | |
| 32 | d3MxMQwwCgYDVQQKEwN3czExCzAJBgNVBAYTAklFMQ8wDQYDVQQHEwZEdWJsaW4w | |
| 33 | ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDCLM+esLH7+Q9J0R5IkUsN | |
| 34 | esEf5xJCxIusKb30bWEKPOpKUiTUGGRkwQfEFqJUuTLj4z+fCGPDBEcjXSHpTY6v | |
| 35 | e1s8u/vP3hw9/64LHVoXtMTcWbfpicOT4KOthUU3kZE8Y4890XEahz68W9AnIACK | |
| 36 | a9yGdLVmgRAW96ubDM6cqykDqBw/H7vCc56O71PIm4OvmeC0NcWeRymQE0/HPcSf | |
| 37 | gwljjEmJq55p+H2kgRLa9KXu4/Qn+qi32/at2Ivue3UfW/Cx54SRmFD40sHl7bPi | |
| 38 | mzKS0lzANfvBDqPsmcp8d4/6LE5NQ5GBXh+gExU6qj/8a6j3ZDkegs0rvtXTLj5l | |
| 39 | AgMBAAGjgZwwgZkwHQYDVR0OBBYEFL54xAgtJTW6US4Mbr4QG0yKzvaxMGoGA1Ud | |
| 40 | IwRjMGGAFL54xAgtJTW6US4Mbr4QG0yKzvaxoT6kPDA6MQwwCgYDVQQDEwN3czEx | |
| 41 | DDAKBgNVBAoTA3dzMTELMAkGA1UEBhMCSUUxDzANBgNVBAcTBkR1YmxpboIJAIOo | |
| 42 | O+AapJ5WMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAJz/RzMa9Wa2 | |
| 43 | eEXed7ijH1gcWtgVsVT1xZo0ksFl+QJ5Be1AJpOIe8nKdzYjxPWUkofIoaGHdMLL | |
| 44 | Uc/udRzsXncup+0mD+Yos6Cqyo9yHq7L1HbXfKYZtBXIjWHdF2+RP8j9tHfITXYI | |
| 45 | Pb2zsQ+A6PYpp5OLGZTDAnI2qffqsmwXFNhPfFhOANrGlOjsvy1P7JDzvymj/90m | |
| 46 | NomlO3vjxLHOf6MvedTgCB0dRcAoUWPgbxPWifjBmGBjQjA4ukMQ58wbBQgvIoCW | |
| 47 | obrXmLCNZIkpWTw4gMRYquY880IYK/OuFNJH/dawxx/WzuVr7IdLmbFY15zf5TUb | |
| 48 | ZpIpwqRCysg= | |
| 49 | -----END CERTIFICATE----- |
| r0 | r250110 | |
|---|---|---|
| 1 | -----BEGIN RSA PRIVATE KEY----- | |
| 2 | MIIEpQIBAAKCAQEAwOp1RS9RnE8L5TszDPIOmpf/1pqb+ki99l/sGhqB/KZBKCuq | |
| 3 | uoCc2VPK3PCByBE15/xJ7t691FnJfForI9DO5p2R0FPD6o357hqRsh0dJNBm0VgG | |
| 4 | iNtLQ8lyYoE72HJbkgCAUZW4N0OBibOmvp/s3Fmr7rEjW5LmZxOtX+iULKIUZ0w6 | |
| 5 | gJlM8G5QA1SuqndN0PbPx+RZKSXZCoJj+Nboqw03nyJUexzs+lynR9ITMziUaaVM | |
| 6 | 4rzG+P6joQAnUkDNydxo/d4tj4xaioZdKxWLYEbj2BUtOlJJydotWo2wcG85ONrT | |
| 7 | Gw0ltR1vku1hidMm2QL30uGIL5SeqyE8TqWk4QIDAQABAoIBAQCxHtKauc45MA4g | |
| 8 | 4hCGAzvLTmETnRI2YlEfAoTYlpvf5pkOE8GFyI25r4gjACJ4GO0gWG9dBF7Pt7wZ | |
| 9 | EwRmttEvxV3aIv5OvRnKNdSs7rQSV9D+xc4CGy1oSG1f6X2TxbMzQoiN32OqQa2O | |
| 10 | S0Z94IFs8lu8JCDtc9tcqiFVXEmnC3RvJZOShWpsCsbmh5ue1Xed0MQQ47vt7Zt7 | |
| 11 | I0cutvwSFJMsZkZUJp5+KjVNYo9TEJxVD3m2NJNJxBfBoRVHXNii3hUEHcTIdIAz | |
| 12 | omtRwBU8AKgJirGIRo1h2ZFyubI8ScGOJWIiWMQvQqTHKiOaz3yUar1NSG+kFn0U | |
| 13 | cj7s3FhdAoGBAOQbx8Jwhtp4iOkP6aW1nVTgiaTj4LMlyJZioFwgPFRIcA3oRHt9 | |
| 14 | 5SRetmgFZNvcuNw1udfeaObKmlzxwUruprwOpihgAQWJFTtOjQNrt2gsYuX4l3W6 | |
| 15 | T46dO2W1pV+mW4A5gt0aqhLv7pCS4lpreNAqyHSPqcQWcCeiTzmp/LfDAoGBANiB | |
| 16 | FQOTyMElR9OzKwmcGfIvnhUfJQwi5qNE3R+xXiP5x36a9HQBey5ri2lnBle0Ksr/ | |
| 17 | G+RKoflmk1eFXVHN0w35yw0dVco//WE4vOknldNEnIT85k02ld8lDTa2Q/EJZtPH | |
| 18 | un6zeU0Q2/4SZ/GXPssEZPlpPM7WtQzztlH3+sqLAoGBAKnhppvAgi4ippQsLa4j | |
| 19 | 29BiiSAsNiQ1d3XIbfUubL+4UvuIh7gQwp6biu1dVwgHEgWuXYHPOgDn0p51zaao | |
| 20 | pbRYlJZtKVWeChnpHkv15NnIdL8grGwZHTbxElNlPIxHsM2GB1fzi8YeumUhf0In | |
| 21 | 2AnwUum8NIq8yzo5PxeK6ZNRAoGBAIEA2Q6ankJH/nZsCbbeJq+iI+Wd+ysyGI8s | |
| 22 | Vz2tJ9Tz3iTYG9SLlWRhfF4/nw3fMqhmPa5Xsg+zSRQbSTGXHKz1LEISOq4aVtX5 | |
| 23 | QscCaUnLVh//uRJE9iRSJX92NyGGYpjKJ5ubQSnkY9EOEpVnc2jwo2HhjPQKBzNC | |
| 24 | fF53Dh5lAoGALwTN5uxrBZLPu4DtZkOosKkv4l+kzFoOjLJR4vA7ONBx2CSe9G7F | |
| 25 | tSsH7lZS3b0mxBWjO90WhaSvtMWWrfqq8vrqmoTE795fYxNoLfCLK13W31aTDUsI | |
| 26 | pQRJIL30MPvASbcFHN2MD2dXz2nQzY8C9lvtvap/krYiDKDU2L7+iP8= | |
| 27 | -----END RSA PRIVATE KEY----- | |
| 28 | -----BEGIN CERTIFICATE----- | |
| 29 | MIIC7DCCAdQCBRQHBXNHMA0GCSqGSIb3DQEBBQUAMDoxDDAKBgNVBAMTA3dzMTEM | |
| 30 | MAoGA1UEChMDd3MxMQswCQYDVQQGEwJJRTEPMA0GA1UEBxMGRHVibGluMB4XDTE0 | |
| 31 | MDgwMzA5MTU0NVoXDTI0MDczMTA5MTU0NVowOjEMMAoGA1UEAxMDd3MxMQwwCgYD | |
| 32 | VQQKEwN3czExCzAJBgNVBAYTAklFMQ8wDQYDVQQHEwZHYWx3YXkwggEiMA0GCSqG | |
| 33 | SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDA6nVFL1GcTwvlOzMM8g6al//Wmpv6SL32 | |
| 34 | X+waGoH8pkEoK6q6gJzZU8rc8IHIETXn/Enu3r3UWcl8Wisj0M7mnZHQU8Pqjfnu | |
| 35 | GpGyHR0k0GbRWAaI20tDyXJigTvYcluSAIBRlbg3Q4GJs6a+n+zcWavusSNbkuZn | |
| 36 | E61f6JQsohRnTDqAmUzwblADVK6qd03Q9s/H5FkpJdkKgmP41uirDTefIlR7HOz6 | |
| 37 | XKdH0hMzOJRppUzivMb4/qOhACdSQM3J3Gj93i2PjFqKhl0rFYtgRuPYFS06UknJ | |
| 38 | 2i1ajbBwbzk42tMbDSW1HW+S7WGJ0ybZAvfS4YgvlJ6rITxOpaThAgMBAAEwDQYJ | |
| 39 | KoZIhvcNAQEFBQADggEBABPLmq6zKOMY0WRjtBoSymq6f+vXeEwtWCfVejdG6RlG | |
| 40 | /PTdCKNvp3OL7FDnmQQ+r5rMs4+Os4fX/g315QFKXu01rqxmFb2XVNhhaECdUWtK | |
| 41 | QP6ZoVZviUiDjhK6a+05aerPCJpkGy/lz0W6gmj4qhuAQbobxb6UbzqTRYY+ZwGk | |
| 42 | +SI3TAVCdmXFlxN/M9b0DbmkseRG8GGFmyRYyRb84vbV6zemFI++5ROUT9zXT7ey | |
| 43 | nYfFJvAAk5jJhY5UP2aMlVWYYa4jUZrrPLoiBLUuRrp67EKGebCH9mgCIf8ztNJF | |
| 44 | fpuvcz++LUeRyTlAGDefe+FyHGIwFzIfZn39D7CaRvM= | |
| 45 | -----END CERTIFICATE----- |
| r0 | r250110 | |
|---|---|---|
| 1 | -----BEGIN RSA PRIVATE KEY----- | |
| 2 | MIIEpAIBAAKCAQEAyal0BZKRYd+Wpxv4MB8LjjgXv/MxSN5oSAKThlCZ/AWG0FEP | |
| 3 | d4nrBACT2xUxwo+xbYl3joiwL/eCPAp6QNKRGhvXVOnSIFVSjKZWbdX+toqK9pDS | |
| 4 | QMDTL4ZJvK6pLZXknyHjEr0PxZh22F7iS1+C8HxBPj0Xgg/u5/+jPhFPPZ1d5elv | |
| 5 | 4cm/z+xy6RjFlA80aIeK7dcWssOsOIPjUNFfmoYgR63ScZIlUZj6j8VX9oX7fJID | |
| 6 | jumGajDxgD2nBWFbHcGKin6bz/wZ+OIhXOCDdY7oKuMW4JiBwbfBtedkQuQYS11s | |
| 7 | PRFFYDLoZH59Ivcu0c4F2tomE86qM8THsI910wIDAQABAoIBAG55FAQRfO8/C0rU | |
| 8 | eavy9eOdOvV+hltC66G3N5X3BcQYSvhHz89OkJ6KqnT0MWRCT5KQIhzFKK++SWwW | |
| 9 | 2U41jCPfaKEtzlzEIQrH/MUC3Byn3OSiBWxPteFtEWv5ytgcKzg52iljxQYcNc7m | |
| 10 | e9WKpzKS/zLXSM+JZvlVA9p2pRA88kUZ/EE5H+FW3kHj5eGNqX+cxUVpL0VKTiLv | |
| 11 | aXWjYotpmDJW/Rn9wRQethm6Gdx3bvo+LEVlJRELNq8NM5H/tZIVRzudJOgzsw5v | |
| 12 | 3OQGhfKB6Eg/vqSFoZxX6ApXDxmtaSO83B0kK550bDVv4sBnOExGjKCzybt04tet | |
| 13 | KtLPPoECgYEA5WUD+mFL99sCX6pzIyUVlxp9eUhVy5nvhoF6u3mvhay2XsZUY0wy | |
| 14 | +/qVqYSZTvuvJ6JSXc4iVIX8u/gj7914805AwujepIF/8E0AaXLBMndzDE4ze5S5 | |
| 15 | 2RHI7Cy4/3AWOcQ9wFFmUdSs7/6oAkcQtvzP40hGg3J2jAEhIdCqmbMCgYEA4Q0G | |
| 16 | BYP9XeTdh0C+BcP9B5VUEC0jerYS8VqVqriB+9JfT3InI7K08sOG2DiQQBhAHuzL | |
| 17 | RhCECU2a9j0+u5F5JNeY5m3IhU73Lw+lOlUkMaAO6x7JJEzhXhonE7Kv8fWygr/0 | |
| 18 | OB7yzqz+YsWdQ2VOPZ88ntlAYE65vzcaVswZY2ECgYEAr7Gt2VA6Ei0A5Wq0Yr+d | |
| 19 | iKz2WzUG2TkelqOG8B4kTDrbNz2qFp+fERV9GWgAz9i+75lIgqZF7vzsdL96LtYv | |
| 20 | NBLEUURwegjhh5hCb4E/7bpFOLCQh9+CdHpFrHYYfzRHIZlnPmxZ9OTyS6J85bmu | |
| 21 | WKjLRKXvs++wUkzvJmoesDcCgYEAkTOB6xUZ5/a+J4HSGI43NylVr4owFgBbgHVd | |
| 22 | k2SwGPXGoM+aCSJINUmKOv9jsrbyyAEntfD5/7aegLlLPGHDs82WzTWP5tLoEOkb | |
| 23 | ReOhEpOejHy0ckNYNQrSo5bqhkZsAogu3fa52jcrejbeHJnEPWX8CtFJA9pHZeP7 | |
| 24 | jnzo9IECgYBefHg0dymSj2xxN0XmC+S5cvQu2K/NYUpatoWvHnPiQ87wIM0AWz0O | |
| 25 | D0ghEI+Ze57NbtSrrcTE7hY/LHrAqXGAB9XNIM5g9Pp/lM+XjzKVr1FMf4xpuHf1 | |
| 26 | VJJRHrOU14CvMvKbgbPrL6B0d5yrYmeex7GxNw0ZVvtjCa502Eck+w== | |
| 27 | -----END RSA PRIVATE KEY----- | |
| 28 | -----BEGIN CERTIFICATE----- | |
| 29 | MIIC7DCCAdQCBRQHBXNGMA0GCSqGSIb3DQEBBQUAMDoxDDAKBgNVBAMTA3dzMTEM | |
| 30 | MAoGA1UEChMDd3MxMQswCQYDVQQGEwJJRTEPMA0GA1UEBxMGRHVibGluMB4XDTE0 | |
| 31 | MDgwMzA5MTU0NVoXDTI0MDczMTA5MTU0NVowOjEMMAoGA1UEAxMDd3MxMQwwCgYD | |
| 32 | VQQKEwN3czExCzAJBgNVBAYTAklFMQ8wDQYDVQQHEwZHYWx3YXkwggEiMA0GCSqG | |
| 33 | SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDJqXQFkpFh35anG/gwHwuOOBe/8zFI3mhI | |
| 34 | ApOGUJn8BYbQUQ93iesEAJPbFTHCj7FtiXeOiLAv94I8CnpA0pEaG9dU6dIgVVKM | |
| 35 | plZt1f62ior2kNJAwNMvhkm8rqktleSfIeMSvQ/FmHbYXuJLX4LwfEE+PReCD+7n | |
| 36 | /6M+EU89nV3l6W/hyb/P7HLpGMWUDzRoh4rt1xayw6w4g+NQ0V+ahiBHrdJxkiVR | |
| 37 | mPqPxVf2hft8kgOO6YZqMPGAPacFYVsdwYqKfpvP/Bn44iFc4IN1jugq4xbgmIHB | |
| 38 | t8G152RC5BhLXWw9EUVgMuhkfn0i9y7RzgXa2iYTzqozxMewj3XTAgMBAAEwDQYJ | |
| 39 | KoZIhvcNAQEFBQADggEBAE20gAykuuaCoP49GnZ/Z6ZItFry4Fl6iCWBDdEsWI9R | |
| 40 | wRNYumeaeejdFPXfSJdTT7UlrVK1WWGLQLq+ixHRDX+V9T67ou85F92H/OxbUoPr | |
| 41 | iz/TZAEBTC1GvTJl49lsfPl1dTWH8T4Ej2hxCUvIJrkCkI2Ov4Wwef6A26USrwBt | |
| 42 | S/CPInjCe6qkE5E8xfTDl8k5IIgMadTPhi5sbV2piBJoN4floJPqR0hdDKbgUymn | |
| 43 | 5WNSiRkuI6UIDZwQEp+A8TmFBHbSwfTGt2Sz5iI27P8J6pFvR5eRA1k57dRUWNXC | |
| 44 | WAU1nqteP3QAjj9L3o8IO0T62scaiJX8x01gTmVVe2I= | |
| 45 | -----END CERTIFICATE----- |
| r0 | r250110 | |
|---|---|---|
| 1 | -----BEGIN RSA PRIVATE KEY----- | |
| 2 | MIIEowIBAAKCAQEAwXIMpHuTNsro2pxe6y1mu27md2Olhvfx26rO3maO0/stIC2z | |
| 3 | G/xQatFDLIWzfKFYOT0iSEj252ENFDCw6aDRKpiaUFtXcMAWkNkkKntEyoEgE45k | |
| 4 | rTrvpay0v0B+ojwjA1Jz/9v35cgvDwTs3vNFno5HhI0m2YF4ocTmeHJ6u0xRL/qy | |
| 5 | atfKsfuVq5s5CXOYCXp3Ux6kJ1c22J0EdZMvS75SVjAZgRkqQpqt9L3e2ZBCEgUr | |
| 6 | w0KwlERvpeJF+sJJOshXjfrDzvwL8IpPnGZLJNINFbSJMk5MFGcMyq/28pSZLB9E | |
| 7 | Dh8vk5D5gUnxM60ONUy2nYPcYr5p1PLDiC8hfQIDAQABAoIBAB0Twpi6xn8W8vdh | |
| 8 | R9c75NRJsDTD8q6d+GnXe+7sJY3xlG/gzqpnO8NCn0FC+57BNdystsl8xjgzW17s | |
| 9 | jrsfZDFt7MwlXrhg90NgkFIeY1G5JRQrdDChykHx+t1AmYhTV8P5EdykuNd+RqyQ | |
| 10 | RfahRJa3tkJTYUKSdoqCaU4zjwU2CSxltuJx24V+WoZE12EwewJ8HPg2XTnbsGE7 | |
| 11 | Fnx5s29O4ItM70CC0536AY/OgfuPix5z573VQiilqqfOQkVkKa1fHd6tGpWU+3kH | |
| 12 | X9FnhEBf9cN9tVgmaB0sCSVVrfgqSXg1EwKHqe/+FCumuesA68Q35+/K3b+QrNiR | |
| 13 | ka2yliECgYEA+V/4pbgG/lPYvTwWhKxGXXdJmrSPgZC0mwE+fRuYkeptbIkS0pwt | |
| 14 | /UDTXk9nttj1f1ZJ3NgQbT/1w8jpXfsCJ8VeGzL9+ADhRKWVFRlu/nyFCMXawLEV | |
| 15 | rot7SEr8BW/m8moHgY5lYipM3dXJPk0F+KLrN60U/aNmFUtPGW802BkCgYEAxpWy | |
| 16 | FGL2sEQ0QaRDTcqqF5faVqw+5rVGvN+EX5o26E0QWWnoo3L2c2/5X93iBl+Fqtnm | |
| 17 | 9jSIQUC3rYOawKnZ/HcIE2ergFit/p6JaV9NiLDRtDUmSzlffEGVCj0neYFsnWp5 | |
| 18 | zcmkUyZ6fr19EmKQWfdteNBlXue32TjVlFbfUQUCgYAfMbgi0sBdNBPaqBeRBRPQ | |
| 19 | QUm9xnRlGrrc4Oz2LWuKZS7G8uad3deK5H8MPxaUMtOS2DJpI8X6RJPzp8A5d1qv | |
| 20 | quq4sEpAqauEMMpTV1khEGZ70HQqwnwZ12zWgDrCW1siW80QkcVw4CW5YjLITk4+ | |
| 21 | 6fJOhqInkDcG1uLQJa8QkQKBgQCfs8l4DbJ4RRGFbLXXvNGXkb68j18yqLxPrq3F | |
| 22 | OL9JiJhKYBsAP7clVPrG9ykLmQxlP0I35D1jxMkymLD+mlo9Z/itqmTJHggnyZWW | |
| 23 | kVdIQ3MSKuA2BNjek9tpVY8Gb2hLHFMChVRKrpo6jOclvvB5+bsnOukbLtyyq7tP | |
| 24 | xaFohQKBgByCmlltjOBWZLFLeA1x8j3inm9zM/FAJuANbHUOZ1RwrRcNFbDv/FXm | |
| 25 | rLPnPCaH5AwAWhVRJcNHo37Ee0s/xqe+Q4dG4xL943k+6KlopAw1SXhuXF6PnBfF | |
| 26 | y+ArVlh9d2oWN5cBEzRddnWnKJuMi70kMzYf6dIW9s/dHbq/gFDy | |
| 27 | -----END RSA PRIVATE KEY----- | |
| 28 | -----BEGIN CERTIFICATE----- | |
| 29 | MIIDlDCCAnygAwIBAgIJAJDtcXU2wiJIMA0GCSqGSIb3DQEBBQUAMDoxDDAKBgNV | |
| 30 | BAMTA3dzMjEMMAoGA1UEChMDd3MyMQswCQYDVQQGEwJJRTEPMA0GA1UEBxMGRHVi | |
| 31 | bGluMB4XDTE0MDgwMzA5MTU0OFoXDTI0MDczMTA5MTU0OFowOjEMMAoGA1UEAxMD | |
| 32 | d3MyMQwwCgYDVQQKEwN3czIxCzAJBgNVBAYTAklFMQ8wDQYDVQQHEwZEdWJsaW4w | |
| 33 | ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDBcgyke5M2yujanF7rLWa7 | |
| 34 | buZ3Y6WG9/Hbqs7eZo7T+y0gLbMb/FBq0UMshbN8oVg5PSJISPbnYQ0UMLDpoNEq | |
| 35 | mJpQW1dwwBaQ2SQqe0TKgSATjmStOu+lrLS/QH6iPCMDUnP/2/flyC8PBOze80We | |
| 36 | jkeEjSbZgXihxOZ4cnq7TFEv+rJq18qx+5WrmzkJc5gJendTHqQnVzbYnQR1ky9L | |
| 37 | vlJWMBmBGSpCmq30vd7ZkEISBSvDQrCURG+l4kX6wkk6yFeN+sPO/Avwik+cZksk | |
| 38 | 0g0VtIkyTkwUZwzKr/bylJksH0QOHy+TkPmBSfEzrQ41TLadg9xivmnU8sOILyF9 | |
| 39 | AgMBAAGjgZwwgZkwHQYDVR0OBBYEFLK4flD5QD/mRufsPx63xlEKM8pwMGoGA1Ud | |
| 40 | IwRjMGGAFLK4flD5QD/mRufsPx63xlEKM8pwoT6kPDA6MQwwCgYDVQQDEwN3czIx | |
| 41 | DDAKBgNVBAoTA3dzMjELMAkGA1UEBhMCSUUxDzANBgNVBAcTBkR1YmxpboIJAJDt | |
| 42 | cXU2wiJIMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAEYD+CReikYr | |
| 43 | Rzvk+/Vdi/7IcaH9CFknIdtineSIw1y98nxnbnNJqxwfNaRblbYvg6OFdUI3POuI | |
| 44 | +rdYLCFl8z3tWqRHLkGqHSJA9xcng3jLJxz0+ctiVcekJvXaB3O6eSZjhGbmmI/s | |
| 45 | CQhdy2zpEIVOeUq50DrSJp9CknyGu/IkaGx5GOZtkiHMrpig50CRjX1lS6qrMNYp | |
| 46 | vB8gfuqpjsL4Ar3vg+lgMSwNWXBNHrIRPHB5VEzBEdmLFZlvueR0ooEMCklpwX/a | |
| 47 | lFImVc6JcY1pBEkHTiTLGMpGAHG3I1aVUaWb3L+V+2ym/KNRNL5C2+1eiqql5u8m | |
| 48 | HUaOcNC90ew= | |
| 49 | -----END CERTIFICATE----- |
| r0 | r250110 | |
|---|---|---|
| 1 | -----BEGIN RSA PRIVATE KEY----- | |
| 2 | MIIEpAIBAAKCAQEA0ucemqFwBFziVOgTgx4mZII4WnGDpA/rWAGHvUZOqy2dQ3Pz | |
| 3 | woGKxBaVPAl5kxEosROVGa7dTuq5yFZ4XIGvwCKxF30vCmdGCytqq6MMp904jG32 | |
| 4 | KikkmjCApIMGxMO4eoBHZZxiyVvKTbg9M2CRXErwnYWhFH/qGdPnuo0CEaHJA4VK | |
| 5 | A9incT9dpeEhU30R6ajAe/je9rCj2OMLMFSMfd51L/VYfO60zDwUNY7YVIghQZgJ | |
| 6 | e44EVGsp1pXaqaD6o3PvGY3ohw2aZjJzlJ7MJHbKV9lft98R3pklbpBzMH849cEy | |
| 7 | Q/51L/rlfTUgCpTy7wEZpWHQNtHfu/1rhJjpNQIDAQABAoIBAQCUNIHXG/dBuZv7 | |
| 8 | GpMLotZL7w520Co30lAJqhmfMpb5x7YpvoPffXTsUwpQBECAzqAPv7kZMT6nxF8F | |
| 9 | n245Y5EDrd1QqlGyN9yK4Nm2/39XPygL1wITopHsIIVmFgVdpEQxIZAKoZjx8yT4 | |
| 10 | 9K1dO1Eq0CbCKzOE2lbCC51eBNUdWZIMxwC6O/j/KoIkZ/HwlG2hpUuXg8x/XawA | |
| 11 | ZJDCoTcWHCjYP10FxKVN3vAyWM2IM44o9IbsAGEOswR4gUwRsgq6Ehc1U59XUHi+ | |
| 12 | x30oda55I1/8xD+SfP/zk2dDPHkv/hq5+258GU/THsw2+AAexocvSIS/g9EppTEg | |
| 13 | biFaDKzJAoGBAPqey10JeyiOlHbBjoSSa7lJYUjocQANFQ4ayOAgfNX72iyabrKF | |
| 14 | p9sVAeO/nM00+DPrm2wWws03ScsPeh+9BDJMPRBUHfSNp7+F+oyj7PWHBEFHbyO9 | |
| 15 | 5HnYZP+1vhdG2dYPIY2gRSFXpiGn3j0M1D0w9c7Ilm8ee3krdR4E/mw3AoGBANdu | |
| 16 | EfS1dK3+m7sEgc2+3U32z83GpuCHeUIKGPYMLI0fIb1CPpboHU9YjOFJZH9iIIdl | |
| 17 | 00JC963O3+pqLe0XbMOmBVt9QjZfhfB+AY+JHtbPgoQVLtq9X/WvW7h3xn6S971r | |
| 18 | Crkhqay3Cs4BzsgYDYraQCTw3oq4twR9Nuy4etfzAoGAXOsG5wWe3diO/sCggFJx | |
| 19 | Eg88vHVBgA1ZoxMXKtGgtw1bRHI1XIblRvqw6qmeDw72fvl5dEe0DbXT7C9ezemc | |
| 20 | ZrGRaj5lpMfoS7/2trIIJrfaQgGkGRJMZUhvmcbeJW8lUJHnlMS5HLWMaKn+YZAi | |
| 21 | GFXQrMv9ylD44mHUWD7tvV0CgYBNctPfvvCQsQ05ofgsiKa1Jbs1hmpuJCYy2MB6 | |
| 22 | jIvjvEJ78PnhdNc8tGAJikIoDZYWN0RI+RxkDxCvDLcwGpDOkbwxVQnd1F+pwxM6 | |
| 23 | kBhXL8kDRT5QA28hO4bk/aKN1LZeEcKMJg8C+ddXkozNoOAVgDs5TKMlCh057u41 | |
| 24 | EmmPgwKBgQDOlYi7fPYOCy0yjHMxSrp2SZOS06AMWGbbCoGkjRtvuP+FmKSNB+LZ | |
| 25 | pOSEPJgzjsRutKjneww4LpV6dViAyTcP5JoeQpokHf7UVo7yq2QH/iwF3zJwsC/S | |
| 26 | OuVLkqpZzWuye/QCH5NOTfw27ye8jG8VcQW2QPbcbkLXLM7zg2yX7g== | |
| 27 | -----END RSA PRIVATE KEY----- | |
| 28 | -----BEGIN CERTIFICATE----- | |
| 29 | MIIC7DCCAdQCBRQHBXNJMA0GCSqGSIb3DQEBBQUAMDoxDDAKBgNVBAMTA3dzMjEM | |
| 30 | MAoGA1UEChMDd3MyMQswCQYDVQQGEwJJRTEPMA0GA1UEBxMGRHVibGluMB4XDTE0 | |
| 31 | MDgwMzA5MTU0OFoXDTI0MDczMTA5MTU0OFowOjEMMAoGA1UEAxMDd3MyMQwwCgYD | |
| 32 | VQQKEwN3czIxCzAJBgNVBAYTAklFMQ8wDQYDVQQHEwZHYWx3YXkwggEiMA0GCSqG | |
| 33 | SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDS5x6aoXAEXOJU6BODHiZkgjhacYOkD+tY | |
| 34 | AYe9Rk6rLZ1Dc/PCgYrEFpU8CXmTESixE5UZrt1O6rnIVnhcga/AIrEXfS8KZ0YL | |
| 35 | K2qrowyn3TiMbfYqKSSaMICkgwbEw7h6gEdlnGLJW8pNuD0zYJFcSvCdhaEUf+oZ | |
| 36 | 0+e6jQIRockDhUoD2KdxP12l4SFTfRHpqMB7+N72sKPY4wswVIx93nUv9Vh87rTM | |
| 37 | PBQ1jthUiCFBmAl7jgRUaynWldqpoPqjc+8ZjeiHDZpmMnOUnswkdspX2V+33xHe | |
| 38 | mSVukHMwfzj1wTJD/nUv+uV9NSAKlPLvARmlYdA20d+7/WuEmOk1AgMBAAEwDQYJ | |
| 39 | KoZIhvcNAQEFBQADggEBACCCAJypO9DFU6GeOH+FwE0JCLTypHoIwERWxNL7xfjg | |
| 40 | rwVqIxwAEo+fJjL+QY7JbAb/eqKaXIBYkAF2lFc4iEmecXX/A3Aqw95AYi78o7HD | |
| 41 | MwRPqJha9mxLcCWwjX8XK8pT152BvYFPNhi+6jd++rDRxKDfmNvgdUQ2/YW6a5Wv | |
| 42 | zEmLDPUWRIuMQIEmOa2/JhlllDviMExTw51nbqYgCghycRvDACyQAuu8re7P6gcg | |
| 43 | bXObNlfxcU/8Ph6MFI+2S9ODtQ4BHyuKd4kRNsYn8vV42a0h3bCYSGPk3kSIgxd7 | |
| 44 | XijwHT/o8E9ddH2BvDv+6Nhno9C6/MbezEOIs4nlhRk= | |
| 45 | -----END CERTIFICATE----- |
| r0 | r250110 | |
|---|---|---|
| 1 | -----BEGIN RSA PRIVATE KEY----- | |
| 2 | MIIEpQIBAAKCAQEA29iFnMf4pjty6knOt4wT0joPUlU2dGCFWxQ5Eg77Yx3Lou2a | |
| 3 | FOzNGp7zLYH8gauOK+mgY+B9cBv8PvVtUQQKB0SKTTV8ZNKVyP3O5F2gRSJ+tHtT | |
| 4 | FqaPfG0WPOn/f02YbOpAe6Rk+NnlJoeuBxG4uapUSq7r0mTGJQe+52y1LcMs23ID | |
| 5 | ENBfDoUMVt5vCnF+cYK5ndeFVLyPO3JjohDH1zv3a9ECG28rtjKHLpNYFDsPJaD7 | |
| 6 | UPuyyk3hIvfPCZoJOUlEVfpLT/lM+9oCPnq9PwIp5NqYofkuc3eKooCo7N4r4IlP | |
| 7 | Ajktaao6b0ScNwNQB3leOIdEUFSIYy8N1JszVwIDAQABAoIBAFlIvjrGG/2m9yyf | |
| 8 | fQyeHw6p9b8CTHNHH+G1fNgQrZe7ahBpXsJQyZueIjTBLcOb4MmEwFbPvSHiu7b2 | |
| 9 | Bcd5VHlPJLvmlPZ9b8eJDJVCUOzC7aJu03fHfU6THwzuG42f/d9942JTiY5nL+FO | |
| 10 | CSdl0xfUTRdnou53buFrG+TxCUPj13HP1HY6DAVzEIq1H4TZwIZo7KRRTIYpTB3P | |
| 11 | 6yvr9xsISLlnmfQ4tp2pApl5o+bHJEhr1VO6SAT/pSyShi79KmMMqYtyTmOMz7w6 | |
| 12 | VJkre5ybnXBDN6tfMHWqdobJ4gRWK9rqf+LIZig5XQnyzkue8k+I7aPgO4xNFh56 | |
| 13 | dkejQcECgYEA9MDCWViqpfvof+epiKzccqnIRnz1EfHdRQjiKsKGRe39+K+pyaqJ | |
| 14 | FOOPFy3aOw6M4cFWwcdMYzKTItvzyLVhDqMzT5eup/NVqo5tPoy93XPf2qRYiTl4 | |
| 15 | 2j5wvm0RVkYEONd3pk2lbfbUmn7XQXj0+AG60SvsqErF/UhIBGec/xsCgYEA5fLC | |
| 16 | EdiiC98kr4sVaE8G854WI+aAkStqO5YXyrnsMzRsqk8KVVYE1yCC9rYyymDBYmlx | |
| 17 | uEW+vbWqLc8PO3v4ty3o5ff303lPMWIrvKiUldjqMjS6ncWxsQjU0bygGVgOgHO7 | |
| 18 | c8rjiDH5M0JgWSREYUVFT5mW/5+Y1LVT8mYNlHUCgYEAhMSX6N23XGkFW3Twu2qB | |
| 19 | /1Vohgw86OoaDNvfzDBPpFmQ3rlz0ijHSeSTd5BxBH5FICXACUgygNErjcphOSxj | |
| 20 | JQyUxgVTQlo2y1mNm1O/nwS/lxx1xqK9ky4x/Kqvr+w1WBxSFI2kQr2V4OUTobma | |
| 21 | sXpGvDcmnrhJJLd0EaefO6cCgYEA3Xw/S9tC8nZjqqYn34nHI16Q6tF54tpTf8Np | |
| 22 | dT4x8Xw8cqqhRGMPVHsfSi1irKYXfwgbnienuqlBmtAHVv9pKF+TJfb7gXkmO2XY | |
| 23 | xOYIAHGn2uYJHjCun9vmyYKLHv4/MaDH3Jd/I88mviXgEdyp9Js5UJua4utB1Rg3 | |
| 24 | HJMJ34UCgYEAr0PpHEBMbZXZBybNU96+jRTgkrNeJpzlnMy7et2IsRAtLjZ0mpbn | |
| 25 | NaX8i8eO+ubweqFdhOvbh7Hd0zr7BzrYcUG1e3njhtxJE1MgWL5plnLVUbIyDAm3 | |
| 26 | iBpIHIBASNCN3sqeq+VqXvavRmeZh5O0vyLP46/kxZx0rzR/NCi9xxU= | |
| 27 | -----END RSA PRIVATE KEY----- | |
| 28 | -----BEGIN CERTIFICATE----- | |
| 29 | MIIC7DCCAdQCBRQHBXNIMA0GCSqGSIb3DQEBBQUAMDoxDDAKBgNVBAMTA3dzMjEM | |
| 30 | MAoGA1UEChMDd3MyMQswCQYDVQQGEwJJRTEPMA0GA1UEBxMGRHVibGluMB4XDTE0 | |
| 31 | MDgwMzA5MTU0OFoXDTI0MDczMTA5MTU0OFowOjEMMAoGA1UEAxMDd3MyMQwwCgYD | |
| 32 | VQQKEwN3czIxCzAJBgNVBAYTAklFMQ8wDQYDVQQHEwZHYWx3YXkwggEiMA0GCSqG | |
| 33 | SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDb2IWcx/imO3LqSc63jBPSOg9SVTZ0YIVb | |
| 34 | FDkSDvtjHcui7ZoU7M0anvMtgfyBq44r6aBj4H1wG/w+9W1RBAoHRIpNNXxk0pXI | |
| 35 | /c7kXaBFIn60e1MWpo98bRY86f9/TZhs6kB7pGT42eUmh64HEbi5qlRKruvSZMYl | |
| 36 | B77nbLUtwyzbcgMQ0F8OhQxW3m8KcX5xgrmd14VUvI87cmOiEMfXO/dr0QIbbyu2 | |
| 37 | Mocuk1gUOw8loPtQ+7LKTeEi988Jmgk5SURV+ktP+Uz72gI+er0/Aink2pih+S5z | |
| 38 | d4qigKjs3ivgiU8COS1pqjpvRJw3A1AHeV44h0RQVIhjLw3UmzNXAgMBAAEwDQYJ | |
| 39 | KoZIhvcNAQEFBQADggEBALi/RmqeXGazT/WRj9+ZqdcnbcHwK5wwr2/YkpFPJ0Hf | |
| 40 | ZDm+2vgjDdTT6cJS6fau0M5nliYdz89aQQo1j9RSRZnzlc/2YCFXyRLCOJYaINbj | |
| 41 | 1MEUAvNDGL7xTpepK9hVkXASRkbyNXERXRKFI1N+vpKu6UorT6/osEV/qM+MFJ3s | |
| 42 | 24xE8/J3J4MirVQVt6eY6Jb+tkliOPMIugr6YQlLsqJygEWATP8Qsr81XSfcZhVq | |
| 43 | rXzVt7QV8dO0nStMjKK5omrtEAhVnASk7w1tFHkpBF1rqXGoo9ML40RnFZ+E5zqi | |
| 44 | iZtzp+NzzLnEnWMNs+fJpPJ96P0kbq2bQzuSBcUynq0= | |
| 45 | -----END CERTIFICATE----- |
| r0 | r250110 | |
|---|---|---|
| 1 | // Copyright (c) 2014 Cesanta Software Limited | |
| 2 | // All rights reserved | |
| 3 | // | |
| 4 | // This software is dual-licensed: you can redistribute it and/or modify | |
| 5 | // it under the terms of the GNU General Public License version 2 as | |
| 6 | // published by the Free Software Foundation. For the terms of this | |
| 7 | // license, see <http://www.gnu.org/licenses/>. | |
| 8 | // | |
| 9 | // You are free to use this software under the terms of the GNU General | |
| 10 | // Public License, but WITHOUT ANY WARRANTY; without even the implied | |
| 11 | // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | |
| 12 | // See the GNU General Public License for more details. | |
| 13 | // | |
| 14 | // Alternatively, you can license this software under a commercial | |
| 15 | // license, as set out in <http://cesanta.com/>. | |
| 16 | // | |
| 17 | // $Date: 2014-09-28 05:04:41 UTC $ | |
| 18 | ||
| 19 | #ifndef NS_SKELETON_HEADER_INCLUDED | |
| 20 | #define NS_SKELETON_HEADER_INCLUDED | |
| 21 | ||
| 22 | #define NS_SKELETON_VERSION "2.1.0" | |
| 23 | ||
| 24 | #undef UNICODE // Use ANSI WinAPI functions | |
| 25 | #undef _UNICODE // Use multibyte encoding on Windows | |
| 26 | #define _MBCS // Use multibyte encoding on Windows | |
| 27 | #define _INTEGRAL_MAX_BITS 64 // Enable _stati64() on Windows | |
| 28 | #define _CRT_SECURE_NO_WARNINGS // Disable deprecation warning in VS2005+ | |
| 29 | #undef WIN32_LEAN_AND_MEAN // Let windows.h always include winsock2.h | |
| 30 | #define _XOPEN_SOURCE 600 // For flockfile() on Linux | |
| 31 | #define __STDC_FORMAT_MACROS // <inttypes.h> wants this for C++ | |
| 32 | #define __STDC_LIMIT_MACROS // C++ wants that for INT64_MAX | |
| 33 | #ifndef _LARGEFILE_SOURCE | |
| 34 | #define _LARGEFILE_SOURCE // Enable fseeko() and ftello() functions | |
| 35 | #endif | |
| 36 | #define _FILE_OFFSET_BITS 64 // Enable 64-bit file offsets | |
| 37 | ||
| 38 | #ifdef _MSC_VER | |
| 39 | #pragma warning (disable : 4127) // FD_SET() emits warning, disable it | |
| 40 | #pragma warning (disable : 4204) // missing c99 support | |
| 41 | #endif | |
| 42 | ||
| 43 | #include <sys/types.h> | |
| 44 | #include <sys/stat.h> | |
| 45 | #include <assert.h> | |
| 46 | #include <ctype.h> | |
| 47 | #include <errno.h> | |
| 48 | #include <fcntl.h> | |
| 49 | #include <stdarg.h> | |
| 50 | #include <stddef.h> | |
| 51 | #include <stdio.h> | |
| 52 | #include <stdlib.h> | |
| 53 | #include <string.h> | |
| 54 | #include <time.h> | |
| 55 | #include <signal.h> | |
| 56 | ||
| 57 | #ifdef _WIN32 | |
| 58 | #ifdef _MSC_VER | |
| 59 | #pragma comment(lib, "ws2_32.lib") // Linking with winsock library | |
| 60 | #endif | |
| 61 | #include <windows.h> | |
| 62 | #include <process.h> | |
| 63 | #ifndef EINPROGRESS | |
| 64 | #define EINPROGRESS WSAEINPROGRESS | |
| 65 | #endif | |
| 66 | #ifndef EWOULDBLOCK | |
| 67 | #define EWOULDBLOCK WSAEWOULDBLOCK | |
| 68 | #endif | |
| 69 | #ifndef __func__ | |
| 70 | #define STRX(x) #x | |
| 71 | #define STR(x) STRX(x) | |
| 72 | #define __func__ __FILE__ ":" STR(__LINE__) | |
| 73 | #endif | |
| 74 | #ifndef va_copy | |
| 75 | #define va_copy(x,y) x = y | |
| 76 | #endif // MINGW #defines va_copy | |
| 77 | #define snprintf _snprintf | |
| 78 | #define vsnprintf _vsnprintf | |
| 79 | #define sleep(x) Sleep((x) * 1000) | |
| 80 | #define to64(x) _atoi64(x) | |
| 81 | typedef int socklen_t; | |
| 82 | typedef unsigned char uint8_t; | |
| 83 | typedef unsigned int uint32_t; | |
| 84 | typedef unsigned short uint16_t; | |
| 85 | typedef unsigned __int64 uint64_t; | |
| 86 | typedef __int64 int64_t; | |
| 87 | typedef SOCKET sock_t; | |
| 88 | typedef struct _stati64 ns_stat_t; | |
| 89 | #ifndef S_ISDIR | |
| 90 | #define S_ISDIR(x) ((x) & _S_IFDIR) | |
| 91 | #endif | |
| 92 | #else | |
| 93 | #include <errno.h> | |
| 94 | #include <fcntl.h> | |
| 95 | #include <netdb.h> | |
| 96 | #include <pthread.h> | |
| 97 | #include <stdarg.h> | |
| 98 | #include <unistd.h> | |
| 99 | #include <arpa/inet.h> // For inet_pton() when NS_ENABLE_IPV6 is defined | |
| 100 | #include <netinet/in.h> | |
| 101 | #include <sys/socket.h> | |
| 102 | #include <sys/select.h> | |
| 103 | #define closesocket(x) close(x) | |
| 104 | #define __cdecl | |
| 105 | #define INVALID_SOCKET (-1) | |
| 106 | #define to64(x) strtoll(x, NULL, 10) | |
| 107 | typedef int sock_t; | |
| 108 | typedef struct stat ns_stat_t; | |
| 109 | #endif | |
| 110 | ||
| 111 | #ifdef NS_ENABLE_DEBUG | |
| 112 | #define DBG(x) do { printf("%-20s ", __func__); printf x; putchar('\n'); \ | |
| 113 | fflush(stdout); } while(0) | |
| 114 | #else | |
| 115 | #define DBG(x) | |
| 116 | #endif | |
| 117 | ||
| 118 | #ifndef ARRAY_SIZE | |
| 119 | #define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0])) | |
| 120 | #endif | |
| 121 | ||
| 122 | #ifdef NS_ENABLE_SSL | |
| 123 | #ifdef __APPLE__ | |
| 124 | #pragma GCC diagnostic ignored "-Wdeprecated-declarations" | |
| 125 | #endif | |
| 126 | #include <openssl/ssl.h> | |
| 127 | #else | |
| 128 | typedef void *SSL; | |
| 129 | typedef void *SSL_CTX; | |
| 130 | #endif | |
| 131 | ||
| 132 | #ifdef __cplusplus | |
| 133 | extern "C" { | |
| 134 | #endif // __cplusplus | |
| 135 | ||
| 136 | union socket_address { | |
| 137 | struct sockaddr sa; | |
| 138 | struct sockaddr_in sin; | |
| 139 | #ifdef NS_ENABLE_IPV6 | |
| 140 | struct sockaddr_in6 sin6; | |
| 141 | #else | |
| 142 | struct sockaddr sin6; | |
| 143 | #endif | |
| 144 | }; | |
| 145 | ||
| 146 | // Describes chunk of memory | |
| 147 | struct ns_str { | |
| 148 | const char *p; | |
| 149 | size_t len; | |
| 150 | }; | |
| 151 | ||
| 152 | // IO buffers interface | |
| 153 | struct iobuf { | |
| 154 | char *buf; | |
| 155 | size_t len; | |
| 156 | size_t size; | |
| 157 | }; | |
| 158 | ||
| 159 | void iobuf_init(struct iobuf *, size_t initial_size); | |
| 160 | void iobuf_free(struct iobuf *); | |
| 161 | size_t iobuf_append(struct iobuf *, const void *data, size_t data_size); | |
| 162 | void iobuf_remove(struct iobuf *, size_t data_size); | |
| 163 | void iobuf_resize(struct iobuf *, size_t new_size); | |
| 164 | ||
| 165 | // Callback function (event handler) prototype, must be defined by user. | |
| 166 | // Net skeleton will call event handler, passing events defined above. | |
| 167 | struct ns_connection; | |
| 168 | typedef void (*ns_callback_t)(struct ns_connection *, int event_num, void *evp); | |
| 169 | ||
| 170 | // Events. Meaning of event parameter (evp) is given in the comment. | |
| 171 | #define NS_POLL 0 // Sent to each connection on each call to ns_mgr_poll() | |
| 172 | #define NS_ACCEPT 1 // New connection accept()-ed. union socket_address *addr | |
| 173 | #define NS_CONNECT 2 // connect() succeeded or failed. int *success_status | |
| 174 | #define NS_RECV 3 // Data has benn received. int *num_bytes | |
| 175 | #define NS_SEND 4 // Data has been written to a socket. int *num_bytes | |
| 176 | #define NS_CLOSE 5 // Connection is closed. NULL | |
| 177 | ||
| 178 | ||
| 179 | struct ns_mgr { | |
| 180 | struct ns_connection *active_connections; | |
| 181 | const char *hexdump_file; // Debug hexdump file path | |
| 182 | sock_t ctl[2]; // Socketpair for mg_wakeup() | |
| 183 | void *user_data; // User data | |
| 184 | }; | |
| 185 | ||
| 186 | ||
| 187 | struct ns_connection { | |
| 188 | struct ns_connection *next, *prev; // ns_mgr::active_connections linkage | |
| 189 | struct ns_connection *listener; // Set only for accept()-ed connections | |
| 190 | struct ns_mgr *mgr; | |
| 191 | ||
| 192 | sock_t sock; // Socket | |
| 193 | union socket_address sa; // Peer address | |
| 194 | struct iobuf recv_iobuf; // Received data | |
| 195 | struct iobuf send_iobuf; // Data scheduled for sending | |
| 196 | SSL *ssl; | |
| 197 | SSL_CTX *ssl_ctx; | |
| 198 | void *user_data; // User-specific data | |
| 199 | void *proto_data; // Application protocol-specific data | |
| 200 | time_t last_io_time; // Timestamp of the last socket IO | |
| 201 | ns_callback_t callback; // Event handler function | |
| 202 | ||
| 203 | unsigned int flags; | |
| 204 | #define NSF_FINISHED_SENDING_DATA (1 << 0) | |
| 205 | #define NSF_BUFFER_BUT_DONT_SEND (1 << 1) | |
| 206 | #define NSF_SSL_HANDSHAKE_DONE (1 << 2) | |
| 207 | #define NSF_CONNECTING (1 << 3) | |
| 208 | #define NSF_CLOSE_IMMEDIATELY (1 << 4) | |
| 209 | #define NSF_WANT_READ (1 << 5) | |
| 210 | #define NSF_WANT_WRITE (1 << 6) | |
| 211 | #define NSF_LISTENING (1 << 7) | |
| 212 | #define NSF_UDP (1 << 8) | |
| 213 | ||
| 214 | #define NSF_USER_1 (1 << 20) | |
| 215 | #define NSF_USER_2 (1 << 21) | |
| 216 | #define NSF_USER_3 (1 << 22) | |
| 217 | #define NSF_USER_4 (1 << 23) | |
| 218 | #define NSF_USER_5 (1 << 24) | |
| 219 | #define NSF_USER_6 (1 << 25) | |
| 220 | }; | |
| 221 | ||
| 222 | void ns_mgr_init(struct ns_mgr *, void *user_data); | |
| 223 | void ns_mgr_free(struct ns_mgr *); | |
| 224 | time_t ns_mgr_poll(struct ns_mgr *, int milli); | |
| 225 | void ns_broadcast(struct ns_mgr *, ns_callback_t, void *, size_t); | |
| 226 | ||
| 227 | struct ns_connection *ns_next(struct ns_mgr *, struct ns_connection *); | |
| 228 | struct ns_connection *ns_add_sock(struct ns_mgr *, sock_t, | |
| 229 | ns_callback_t, void *); | |
| 230 | struct ns_connection *ns_bind(struct ns_mgr *, const char *, | |
| 231 | ns_callback_t, void *); | |
| 232 | struct ns_connection *ns_connect(struct ns_mgr *, const char *, | |
| 233 | ns_callback_t, void *); | |
| 234 | ||
| 235 | int ns_send(struct ns_connection *, const void *buf, int len); | |
| 236 | int ns_printf(struct ns_connection *, const char *fmt, ...); | |
| 237 | int ns_vprintf(struct ns_connection *, const char *fmt, va_list ap); | |
| 238 | ||
| 239 | // Utility functions | |
| 240 | void *ns_start_thread(void *(*f)(void *), void *p); | |
| 241 | int ns_socketpair(sock_t [2]); | |
| 242 | int ns_socketpair2(sock_t [2], int sock_type); // SOCK_STREAM or SOCK_DGRAM | |
| 243 | void ns_set_close_on_exec(sock_t); | |
| 244 | void ns_sock_to_str(sock_t sock, char *buf, size_t len, int flags); | |
| 245 | int ns_hexdump(const void *buf, int len, char *dst, int dst_len); | |
| 246 | int ns_avprintf(char **buf, size_t size, const char *fmt, va_list ap); | |
| 247 | int ns_resolve(const char *domain_name, char *ip_addr_buf, size_t buf_len); | |
| 248 | ||
| 249 | #ifdef __cplusplus | |
| 250 | } | |
| 251 | #endif // __cplusplus | |
| 252 | ||
| 253 | #endif // NS_SKELETON_HEADER_INCLUDED |
| r0 | r250110 | |
|---|---|---|
| 1 | // Copyright (c) 2014 Cesanta Software Limited | |
| 2 | // All rights reserved | |
| 3 | // | |
| 4 | // This software is dual-licensed: you can redistribute it and/or modify | |
| 5 | // it under the terms of the GNU General Public License version 2 as | |
| 6 | // published by the Free Software Foundation. For the terms of this | |
| 7 | // license, see <http://www.gnu.org/licenses/>. | |
| 8 | // | |
| 9 | // You are free to use this software under the terms of the GNU General | |
| 10 | // Public License, but WITHOUT ANY WARRANTY; without even the implied | |
| 11 | // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | |
| 12 | // See the GNU General Public License for more details. | |
| 13 | // | |
| 14 | // Alternatively, you can license this software under a commercial | |
| 15 | // license, as set out in <http://cesanta.com/products.html>. | |
| 16 | // | |
| 17 | // $Date$ | |
| 18 | ||
| 19 | #include "net_skeleton.h" | |
| 20 | #include "ssl_wrapper.h" | |
| 21 | ||
| 22 | static void ev_handler(struct ns_connection *nc, int ev, void *p) { | |
| 23 | const char *target_addr = (const char *) nc->mgr->user_data; | |
| 24 | struct ns_connection *pc = (struct ns_connection *) nc->user_data; | |
| 25 | struct iobuf *io = &nc->recv_iobuf; | |
| 26 | ||
| 27 | (void) p; | |
| 28 | switch (ev) { | |
| 29 | case NS_ACCEPT: | |
| 30 | // Create a connection to the target, and interlink both connections | |
| 31 | nc->user_data = ns_connect(nc->mgr, target_addr, ev_handler, nc); | |
| 32 | if (nc->user_data == NULL) { | |
| 33 | nc->flags |= NSF_CLOSE_IMMEDIATELY; | |
| 34 | } | |
| 35 | break; | |
| 36 | ||
| 37 | case NS_CLOSE: | |
| 38 | // If either connection closes, unlink them and shedule closing | |
| 39 | if (pc != NULL) { | |
| 40 | pc->flags |= NSF_FINISHED_SENDING_DATA; | |
| 41 | pc->user_data = NULL; | |
| 42 | } | |
| 43 | nc->user_data = NULL; | |
| 44 | break; | |
| 45 | ||
| 46 | case NS_RECV: | |
| 47 | // Forward arrived data to the other connection, and discard from buffer | |
| 48 | if (pc != NULL) { | |
| 49 | ns_send(pc, io->buf, io->len); | |
| 50 | iobuf_remove(io, io->len); | |
| 51 | } | |
| 52 | break; | |
| 53 | ||
| 54 | default: | |
| 55 | break; | |
| 56 | } | |
| 57 | } | |
| 58 | ||
| 59 | void *ssl_wrapper_init(const char *local_addr, const char *target_addr, | |
| 60 | const char **err_msg) { | |
| 61 | struct ns_mgr *mgr = (struct ns_mgr *) calloc(1, sizeof(mgr[0])); | |
| 62 | *err_msg = NULL; | |
| 63 | ||
| 64 | if (mgr == NULL) { | |
| 65 | *err_msg = "malloc failed"; | |
| 66 | } else { | |
| 67 | ns_mgr_init(mgr, (void *) target_addr); | |
| 68 | if (ns_bind(mgr, local_addr, ev_handler, NULL) == NULL) { | |
| 69 | *err_msg = "ns_bind() failed: bad listening_port"; | |
| 70 | ns_mgr_free(mgr); | |
| 71 | free(mgr); | |
| 72 | mgr = NULL; | |
| 73 | } | |
| 74 | } | |
| 75 | ||
| 76 | return mgr; | |
| 77 | } | |
| 78 | ||
| 79 | void ssl_wrapper_serve(void *param, volatile int *quit) { | |
| 80 | struct ns_mgr *mgr = (struct ns_mgr *) param; | |
| 81 | ||
| 82 | while (*quit == 0) { | |
| 83 | ns_mgr_poll(mgr, 1000); | |
| 84 | } | |
| 85 | ns_mgr_free(mgr); | |
| 86 | free(mgr); | |
| 87 | } | |
| 88 | ||
| 89 | #ifndef SSL_WRAPPER_USE_AS_LIBRARY | |
| 90 | static int s_received_signal = 0; | |
| 91 | ||
| 92 | static void signal_handler(int sig_num) { | |
| 93 | signal(sig_num, signal_handler); | |
| 94 | s_received_signal = sig_num; | |
| 95 | } | |
| 96 | ||
| 97 | static void show_usage_and_exit(const char *prog) { | |
| 98 | fprintf(stderr, "Usage: %s <listening_address> <target_address>\n", prog); | |
| 99 | exit(EXIT_FAILURE); | |
| 100 | } | |
| 101 | ||
| 102 | int main(int argc, char *argv[]) { | |
| 103 | void *wrapper; | |
| 104 | const char *err_msg; | |
| 105 | ||
| 106 | if (argc != 3) { | |
| 107 | show_usage_and_exit(argv[0]); | |
| 108 | } | |
| 109 | ||
| 110 | // Setup signal handlers | |
| 111 | signal(SIGTERM, signal_handler); | |
| 112 | signal(SIGINT, signal_handler); | |
| 113 | signal(SIGPIPE, SIG_IGN); | |
| 114 | ||
| 115 | if ((wrapper = ssl_wrapper_init(argv[1], argv[2], &err_msg)) == NULL) { | |
| 116 | fprintf(stderr, "Error: %s\n", err_msg); | |
| 117 | exit(EXIT_FAILURE); | |
| 118 | } | |
| 119 | ssl_wrapper_serve(wrapper, &s_received_signal); | |
| 120 | ||
| 121 | return EXIT_SUCCESS; | |
| 122 | } | |
| 123 | #endif // SSL_WRAPPER_USE_AS_LIBRARY |
| r0 | r250110 | |
|---|---|---|
| 1 | // Copyright (c) 2014 Cesanta Software Limited | |
| 2 | // All rights reserved | |
| 3 | // | |
| 4 | // This software is dual-licensed: you can redistribute it and/or modify | |
| 5 | // it under the terms of the GNU General Public License version 2 as | |
| 6 | // published by the Free Software Foundation. For the terms of this | |
| 7 | // license, see <http://www.gnu.org/licenses/>. | |
| 8 | // | |
| 9 | // You are free to use this software under the terms of the GNU General | |
| 10 | // Public License, but WITHOUT ANY WARRANTY; without even the implied | |
| 11 | // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | |
| 12 | // See the GNU General Public License for more details. | |
| 13 | // | |
| 14 | // Alternatively, you can license this software under a commercial | |
| 15 | // license, as set out in <http://cesanta.com/products.html>. | |
| 16 | // | |
| 17 | // $Date$ | |
| 18 | ||
| 19 | #ifndef SSL_WRAPPER_HEADER_INCLUDED | |
| 20 | #define SSL_WRAPPER_HEADER_INCLUDED | |
| 21 | ||
| 22 | #ifdef __cplusplus | |
| 23 | extern "C" { | |
| 24 | #endif // __cplusplus | |
| 25 | ||
| 26 | void *ssl_wrapper_init(const char *listen_addr, const char *target_addr, | |
| 27 | const char **err_msg); | |
| 28 | void ssl_wrapper_serve(void *, volatile int *stop_marker); | |
| 29 | ||
| 30 | #ifdef __cplusplus | |
| 31 | } | |
| 32 | #endif // __cplusplus | |
| 33 | ||
| 34 | #endif // SSL_WRAPPER_HEADER_INCLUDED |
| r0 | r250110 | |
|---|---|---|
| 1 | // Copyright (c) 2014 Cesanta Software | |
| 2 | // All rights reserved | |
| 3 | // | |
| 4 | // This example demostrates proxying of WebSocket traffic, regardless of the | |
| 5 | // protocol (ws:// or wss://). | |
| 6 | // To use this example: | |
| 7 | // 1. configure your browser to use a proxy on port 2014 | |
| 8 | // 2. import certs/ws1_ca.pem and certs/ws2_ca.pem into the trusted | |
| 9 | // certificates list on your browser | |
| 10 | // 3. make && ./ws_ssl | |
| 11 | // 4. Point your browser to http://ws_ssl.com | |
| 12 | // A page with 4 sections should appear, showing websocket echoes | |
| 13 | ||
| 14 | #include "net_skeleton.h" | |
| 15 | #include "mongoose.h" | |
| 16 | #include "ssl_wrapper.h" | |
| 17 | ||
| 18 | #define S1_PEM "certs/ws1_server.pem" | |
| 19 | #define C1_PEM "certs/ws1_client.pem" | |
| 20 | #define CA1_PEM "certs/ws1_ca.pem" | |
| 21 | #define S2_PEM "certs/ws2_server.pem" | |
| 22 | #define C2_PEM "certs/ws2_client.pem" | |
| 23 | #define CA2_PEM "certs/ws2_ca.pem" | |
| 24 | ||
| 25 | struct config { | |
| 26 | const char *uri; | |
| 27 | const char *wrapper_server_addr; | |
| 28 | const char *wrapper_client_addr; | |
| 29 | const char *target_addr; | |
| 30 | }; | |
| 31 | ||
| 32 | static struct config s_wrappers[] = { | |
| 33 | { | |
| 34 | "ws1:80", | |
| 35 | "tcp://127.0.0.1:7001", | |
| 36 | "tcp://127.0.0.1:7001", | |
| 37 | "tcp://127.0.0.1:9001" | |
| 38 | }, | |
| 39 | { | |
| 40 | "ws1:443", | |
| 41 | "ssl://127.0.0.1:7002:" S1_PEM, | |
| 42 | "tcp://127.0.0.1:7002", | |
| 43 | "tcp://127.0.0.1:9001" | |
| 44 | }, | |
| 45 | { | |
| 46 | "ws2:80", | |
| 47 | "tcp://127.0.0.1:7003", | |
| 48 | "tcp://127.0.0.1:7003", | |
| 49 | "ssl://127.0.0.1:9002:" C2_PEM ":" CA2_PEM | |
| 50 | }, | |
| 51 | { | |
| 52 | "ws2:443", | |
| 53 | "ssl://127.0.0.1:7004:" S2_PEM, | |
| 54 | "tcp://127.0.0.1:7004", | |
| 55 | "ssl://127.0.0.1:9002:" C2_PEM ":" CA2_PEM | |
| 56 | }, | |
| 57 | }; | |
| 58 | ||
| 59 | static int s_received_signal = 0; | |
| 60 | ||
| 61 | static void signal_handler(int sig_num) { | |
| 62 | signal(sig_num, signal_handler); | |
| 63 | s_received_signal = sig_num; | |
| 64 | } | |
| 65 | ||
| 66 | static int ev_handler(struct mg_connection *conn, enum mg_event ev) { | |
| 67 | int i; | |
| 68 | ||
| 69 | switch (ev) { | |
| 70 | case MG_AUTH: | |
| 71 | return MG_TRUE; | |
| 72 | ||
| 73 | case MG_REQUEST: | |
| 74 | printf("==> [%s] [%s]\n", conn->request_method, conn->uri); | |
| 75 | ||
| 76 | if (strcmp(conn->request_method, "CONNECT") == 0) { | |
| 77 | // Iterate over configured wrappers, see if we can use one of them | |
| 78 | for (i = 0; i < (int) ARRAY_SIZE(s_wrappers); i++) { | |
| 79 | if (strcmp(conn->uri, s_wrappers[i].uri) == 0) { | |
| 80 | mg_forward(conn, s_wrappers[i].wrapper_client_addr); | |
| 81 | return MG_MORE; | |
| 82 | } | |
| 83 | } | |
| 84 | ||
| 85 | // No suitable wrappers found. Disallow that CONNECT request. | |
| 86 | mg_send_status(conn, 405); | |
| 87 | return MG_TRUE; | |
| 88 | } | |
| 89 | ||
| 90 | // Not a CONNECT request, serve HTML file. | |
| 91 | mg_send_file(conn, "ws_ssl.html", NULL); | |
| 92 | return MG_MORE; | |
| 93 | ||
| 94 | default: | |
| 95 | return MG_FALSE; | |
| 96 | } | |
| 97 | } | |
| 98 | ||
| 99 | static int ws_handler(struct mg_connection *conn, enum mg_event ev) { | |
| 100 | switch (ev) { | |
| 101 | case MG_AUTH: | |
| 102 | return MG_TRUE; | |
| 103 | case MG_REQUEST: | |
| 104 | if (conn->is_websocket) { | |
| 105 | // Simple websocket echo server | |
| 106 | mg_websocket_write(conn, WEBSOCKET_OPCODE_TEXT, | |
| 107 | conn->content, conn->content_len); | |
| 108 | } else { | |
| 109 | mg_printf_data(conn, "%s", "websocket connection expected"); | |
| 110 | } | |
| 111 | return MG_TRUE; | |
| 112 | default: | |
| 113 | return MG_FALSE; | |
| 114 | } | |
| 115 | } | |
| 116 | ||
| 117 | static void *serve_thread_func(void *param) { | |
| 118 | struct mg_server *server = (struct mg_server *) param; | |
| 119 | printf("Listening on port %s\n", mg_get_option(server, "listening_port")); | |
| 120 | while (s_received_signal == 0) { | |
| 121 | mg_poll_server(server, 1000); | |
| 122 | } | |
| 123 | mg_destroy_server(&server); | |
| 124 | return NULL; | |
| 125 | } | |
| 126 | ||
| 127 | static void *wrapper_thread_func(void *param) { | |
| 128 | struct config *c = (struct config *) param; | |
| 129 | const char *err_msg; | |
| 130 | void *wrapper; | |
| 131 | ||
| 132 | wrapper = ssl_wrapper_init(c->wrapper_server_addr, c->target_addr, &err_msg); | |
| 133 | if (wrapper == NULL) { | |
| 134 | fprintf(stderr, "Error: %s\n", err_msg); | |
| 135 | exit(EXIT_FAILURE); | |
| 136 | } | |
| 137 | //((struct ns_mgr *) wrapper)->hexdump_file = "/dev/stderr"; | |
| 138 | ssl_wrapper_serve(wrapper, &s_received_signal); | |
| 139 | ||
| 140 | return NULL; | |
| 141 | } | |
| 142 | ||
| 143 | int main(void) { | |
| 144 | struct mg_server *proxy_server = mg_create_server(NULL, ev_handler); | |
| 145 | struct mg_server *ws1_server = mg_create_server(NULL, ws_handler); | |
| 146 | struct mg_server *ws2_server = mg_create_server(NULL, ws_handler); | |
| 147 | size_t i; | |
| 148 | ||
| 149 | ((struct ns_mgr *) proxy_server)->hexdump_file = "/dev/stderr"; | |
| 150 | ||
| 151 | // Configure proxy server to listen on port 2014 | |
| 152 | mg_set_option(proxy_server, "listening_port", "2014"); | |
| 153 | //mg_set_option(proxy_server, "enable_proxy", "yes"); | |
| 154 | ||
| 155 | // Configure two websocket echo servers: | |
| 156 | // ws1 is WS, listening on 9001 | |
| 157 | // ws2 is WSS, listening on 9002 | |
| 158 | // Note that HTML page thinks that ws1 is WSS, and ws2 is WS, | |
| 159 | // where in reality it is vice versa and proxy server makes the decision. | |
| 160 | mg_set_option(ws1_server, "listening_port", "tcp://127.0.0.1:9001"); | |
| 161 | mg_set_option(ws2_server, "listening_port", | |
| 162 | "ssl://127.0.0.1:9002:" S2_PEM ":" CA2_PEM); | |
| 163 | ||
| 164 | // Setup signal handlers | |
| 165 | signal(SIGTERM, signal_handler); | |
| 166 | signal(SIGINT, signal_handler); | |
| 167 | ||
| 168 | // Start SSL wrappers, each in it's own thread | |
| 169 | for (i = 0; i < ARRAY_SIZE(s_wrappers); i++) { | |
| 170 | ns_start_thread(wrapper_thread_func, &s_wrappers[i]); | |
| 171 | } | |
| 172 | ||
| 173 | // Start websocket servers in separate threads | |
| 174 | mg_start_thread(serve_thread_func, ws1_server); | |
| 175 | mg_start_thread(serve_thread_func, ws2_server); | |
| 176 | ||
| 177 | // Finally, start proxy server in this thread: this call blocks | |
| 178 | serve_thread_func(proxy_server); | |
| 179 | ||
| 180 | printf("Existing on signal %d\n", s_received_signal); | |
| 181 | return EXIT_SUCCESS; | |
| 182 | } |
| r0 | r250110 | |
|---|---|---|
| 1 | <html> | |
| 2 | <head> | |
| 3 | <title>Websocket Proxy SSL Test</title> | |
| 4 | <meta charset="utf-8"> | |
| 5 | <script> | |
| 6 | window.onload = function() { | |
| 7 | var protocols = ['ws://', 'wss://']; | |
| 8 | var websocketServers = ['ws1', 'ws2']; | |
| 9 | //protocols = ['wss://']; | |
| 10 | //websocketServers = ['ws1'] | |
| 11 | ||
| 12 | var createWebsocketConnection = function(proto, server) { | |
| 13 | var conn = new WebSocket(proto + server); | |
| 14 | ||
| 15 | var div = document.createElement('div'); | |
| 16 | var h2 = document.createElement('h2'); | |
| 17 | h2.innerHTML = 'Connection to ' + proto + server; | |
| 18 | document.body.appendChild(h2); | |
| 19 | document.body.appendChild(div); | |
| 20 | ||
| 21 | conn.onmessage = function(ev) { | |
| 22 | var el = document.createElement('div'); | |
| 23 | el.innerHTML = 'websocket message: ' + ev.data; | |
| 24 | div.appendChild(el); | |
| 25 | // Keep only last 5 messages in the list | |
| 26 | while (div.childNodes.length > 5) div.removeChild(div.firstChild); | |
| 27 | }; | |
| 28 | ||
| 29 | // Send some string to the websocket connection periodically. | |
| 30 | // websocket server much echo it back. | |
| 31 | window.setInterval(function() { conn.send(Math.random()); }, 1000); | |
| 32 | }; | |
| 33 | ||
| 34 | for (var i = 0; i < protocols.length; i++) { | |
| 35 | for (var j = 0; j < websocketServers.length; j++) { | |
| 36 | createWebsocketConnection(protocols[i], websocketServers[j]); | |
| 37 | } | |
| 38 | } | |
| 39 | }; | |
| 40 | </script> | |
| 41 | <style> | |
| 42 | body > div { | |
| 43 | border: 1px solid #ccc; background: #f0f0f0; padding: 0 1em; | |
| 44 | margin: 0 2em; min-height: 4em; max-width: 40em; | |
| 45 | } | |
| 46 | </style> | |
| 47 | </head> | |
| 48 | <body> | |
| 49 | </body> | |
| 50 | </html> | |
| No newline at end of file |
| r0 | r250110 | |
|---|---|---|
| 1 | LOCAL_PATH := $(call my-dir)/.. | |
| 2 | include $(CLEAR_VARS) | |
| 3 | ||
| 4 | LOCAL_CFLAGS := -std=c99 -O2 -W -Wall -pthread -pipe $(COPT) | |
| 5 | LOCAL_MODULE := mongoose | |
| 6 | LOCAL_SRC_FILES := examples/web_server/web_server.c mongoose.c | |
| 7 | ||
| 8 | include $(BUILD_EXECUTABLE) |
| r0 | r250110 | |
|---|---|---|
| 1 | // Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com> | |
| 2 | // Copyright (c) 2013-2014 Cesanta Software Limited | |
| 3 | // All rights reserved | |
| 4 | // | |
| 5 | // This library is dual-licensed: you can redistribute it and/or modify | |
| 6 | // it under the terms of the GNU General Public License version 2 as | |
| 7 | // published by the Free Software Foundation. For the terms of this | |
| 8 | // license, see <http://www.gnu.org/licenses/>. | |
| 9 | // | |
| 10 | // You are free to use this library under the terms of the GNU General | |
| 11 | // Public License, but WITHOUT ANY WARRANTY; without even the implied | |
| 12 | // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | |
| 13 | // See the GNU General Public License for more details. | |
| 14 | // | |
| 15 | // Alternatively, you can license this library under a commercial | |
| 16 | // license, as set out in <http://cesanta.com/>. | |
| 17 | ||
| 18 | #ifdef NOEMBED_NET_SKELETON | |
| 19 | #include "net_skeleton.h" | |
| 20 | #else | |
| 21 | // net_skeleton start | |
| 22 | // Copyright (c) 2014 Cesanta Software Limited | |
| 23 | // All rights reserved | |
| 24 | // | |
| 25 | // This software is dual-licensed: you can redistribute it and/or modify | |
| 26 | // it under the terms of the GNU General Public License version 2 as | |
| 27 | // published by the Free Software Foundation. For the terms of this | |
| 28 | // license, see <http://www.gnu.org/licenses/>. | |
| 29 | // | |
| 30 | // You are free to use this software under the terms of the GNU General | |
| 31 | // Public License, but WITHOUT ANY WARRANTY; without even the implied | |
| 32 | // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | |
| 33 | // See the GNU General Public License for more details. | |
| 34 | // | |
| 35 | // Alternatively, you can license this software under a commercial | |
| 36 | // license, as set out in <http://cesanta.com/>. | |
| 37 | ||
| 38 | #ifndef NS_SKELETON_HEADER_INCLUDED | |
| 39 | #define NS_SKELETON_HEADER_INCLUDED | |
| 40 | ||
| 41 | #define NS_SKELETON_VERSION "2.1.0" | |
| 42 | ||
| 43 | #undef UNICODE // Use ANSI WinAPI functions | |
| 44 | #undef _UNICODE // Use multibyte encoding on Windows | |
| 45 | #define _MBCS // Use multibyte encoding on Windows | |
| 46 | #define _INTEGRAL_MAX_BITS 64 // Enable _stati64() on Windows | |
| 47 | #ifndef _CRT_SECURE_NO_WARNINGS | |
| 48 | #define _CRT_SECURE_NO_WARNINGS // Disable deprecation warning in VS2005+ | |
| 49 | #endif | |
| 50 | #undef WIN32_LEAN_AND_MEAN // Let windows.h always include winsock2.h | |
| 51 | #ifdef __Linux__ | |
| 52 | #define _XOPEN_SOURCE 600 // For flockfile() on Linux | |
| 53 | #endif | |
| 54 | #define __STDC_FORMAT_MACROS // <inttypes.h> wants this for C++ | |
| 55 | #define __STDC_LIMIT_MACROS // C++ wants that for INT64_MAX | |
| 56 | #ifndef _LARGEFILE_SOURCE | |
| 57 | #define _LARGEFILE_SOURCE // Enable fseeko() and ftello() functions | |
| 58 | #endif | |
| 59 | #define _FILE_OFFSET_BITS 64 // Enable 64-bit file offsets | |
| 60 | ||
| 61 | #ifdef _MSC_VER | |
| 62 | #pragma warning (disable : 4127) // FD_SET() emits warning, disable it | |
| 63 | #pragma warning (disable : 4204) // missing c99 support | |
| 64 | #endif | |
| 65 | ||
| 66 | #if defined(_WIN32) && !defined(MONGOOSE_NO_CGI) && !defined(MONGOOSE_ENABLE_THREADS) | |
| 67 | #define MONGOOSE_ENABLE_THREADS /* Windows uses stdio threads for CGI */ | |
| 68 | #endif | |
| 69 | ||
| 70 | #ifndef MONGOOSE_ENABLE_THREADS | |
| 71 | #define NS_DISABLE_THREADS | |
| 72 | #endif | |
| 73 | ||
| 74 | #ifdef __OS2__ | |
| 75 | #define _MMAP_DECLARED // Prevent dummy mmap() declaration in stdio.h | |
| 76 | #endif | |
| 77 | ||
| 78 | #include <sys/types.h> | |
| 79 | #include <sys/stat.h> | |
| 80 | #include <assert.h> | |
| 81 | #include <ctype.h> | |
| 82 | #include <errno.h> | |
| 83 | #include <fcntl.h> | |
| 84 | #include <stdarg.h> | |
| 85 | #include <stddef.h> | |
| 86 | #include <stdio.h> | |
| 87 | #include <stdlib.h> | |
| 88 | #include <string.h> | |
| 89 | #include <time.h> | |
| 90 | #include <signal.h> | |
| 91 | ||
| 92 | #ifdef _WIN32 | |
| 93 | #ifdef _MSC_VER | |
| 94 | #pragma comment(lib, "ws2_32.lib") // Linking with winsock library | |
| 95 | #include <BaseTsd.h> | |
| 96 | typedef SSIZE_T ssize_t; | |
| 97 | #endif | |
| 98 | #ifndef FD_SETSIZE | |
| 99 | #define FD_SETSIZE 1024 | |
| 100 | #endif | |
| 101 | #include <winsock2.h> | |
| 102 | #include <ws2tcpip.h> | |
| 103 | #include <windows.h> | |
| 104 | #include <process.h> | |
| 105 | #ifndef EINPROGRESS | |
| 106 | #define EINPROGRESS WSAEINPROGRESS | |
| 107 | #endif | |
| 108 | #ifndef EWOULDBLOCK | |
| 109 | #define EWOULDBLOCK WSAEWOULDBLOCK | |
| 110 | #endif | |
| 111 | #ifndef __func__ | |
| 112 | #define STRX(x) #x | |
| 113 | #define STR(x) STRX(x) | |
| 114 | #define __func__ __FILE__ ":" STR(__LINE__) | |
| 115 | #endif | |
| 116 | #ifndef va_copy | |
| 117 | #define va_copy(x,y) x = y | |
| 118 | #endif // MINGW #defines va_copy | |
| 119 | #define snprintf _snprintf | |
| 120 | #define vsnprintf _vsnprintf | |
| 121 | #define sleep(x) Sleep((x) * 1000) | |
| 122 | #define to64(x) _atoi64(x) | |
| 123 | typedef int socklen_t; | |
| 124 | typedef unsigned char uint8_t; | |
| 125 | typedef unsigned int uint32_t; | |
| 126 | typedef unsigned short uint16_t; | |
| 127 | typedef unsigned __int64 uint64_t; | |
| 128 | typedef __int64 int64_t; | |
| 129 | typedef SOCKET sock_t; | |
| 130 | typedef struct _stati64 ns_stat_t; | |
| 131 | #ifndef S_ISDIR | |
| 132 | #define S_ISDIR(x) ((x) & _S_IFDIR) | |
| 133 | #endif | |
| 134 | #else | |
| 135 | #include <errno.h> | |
| 136 | #include <fcntl.h> | |
| 137 | #include <netdb.h> | |
| 138 | #include <pthread.h> | |
| 139 | #include <stdarg.h> | |
| 140 | #include <unistd.h> | |
| 141 | #include <arpa/inet.h> // For inet_pton() when NS_ENABLE_IPV6 is defined | |
| 142 | #include <netinet/in.h> | |
| 143 | #include <sys/socket.h> | |
| 144 | #include <sys/select.h> | |
| 145 | #define closesocket(x) close(x) | |
| 146 | #ifndef __OS2__ | |
| 147 | #define __cdecl | |
| 148 | #else | |
| 149 | #include <sys/time.h> | |
| 150 | typedef int socklen_t; | |
| 151 | #endif | |
| 152 | #define INVALID_SOCKET (-1) | |
| 153 | #define to64(x) strtoll(x, NULL, 10) | |
| 154 | typedef int sock_t; | |
| 155 | typedef struct stat ns_stat_t; | |
| 156 | #endif | |
| 157 | ||
| 158 | #ifdef NS_ENABLE_DEBUG | |
| 159 | #define DBG(x) do { printf("%-20s ", __func__); printf x; putchar('\n'); \ | |
| 160 | fflush(stdout); } while(0) | |
| 161 | #else | |
| 162 | #define DBG(x) | |
| 163 | #endif | |
| 164 | ||
| 165 | #ifndef ARRAY_SIZE | |
| 166 | #define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0])) | |
| 167 | #endif | |
| 168 | ||
| 169 | #ifdef NS_ENABLE_SSL | |
| 170 | #ifdef __APPLE__ | |
| 171 | #pragma GCC diagnostic ignored "-Wdeprecated-declarations" | |
| 172 | #endif | |
| 173 | #include <openssl/ssl.h> | |
| 174 | #else | |
| 175 | typedef void *SSL; | |
| 176 | typedef void *SSL_CTX; | |
| 177 | #endif | |
| 178 | ||
| 179 | #ifdef __cplusplus | |
| 180 | extern "C" { | |
| 181 | #endif // __cplusplus | |
| 182 | ||
| 183 | union socket_address { | |
| 184 | struct sockaddr sa; | |
| 185 | struct sockaddr_in sin; | |
| 186 | #ifdef NS_ENABLE_IPV6 | |
| 187 | struct sockaddr_in6 sin6; | |
| 188 | #else | |
| 189 | struct sockaddr sin6; | |
| 190 | #endif | |
| 191 | }; | |
| 192 | ||
| 193 | // Describes chunk of memory | |
| 194 | struct ns_str { | |
| 195 | const char *p; | |
| 196 | size_t len; | |
| 197 | }; | |
| 198 | ||
| 199 | // IO buffers interface | |
| 200 | struct iobuf { | |
| 201 | char *buf; | |
| 202 | size_t len; | |
| 203 | size_t size; | |
| 204 | }; | |
| 205 | ||
| 206 | void iobuf_init(struct iobuf *, size_t initial_size); | |
| 207 | void iobuf_free(struct iobuf *); | |
| 208 | size_t iobuf_append(struct iobuf *, const void *data, size_t data_size); | |
| 209 | void iobuf_remove(struct iobuf *, size_t data_size); | |
| 210 | void iobuf_resize(struct iobuf *, size_t new_size); | |
| 211 | ||
| 212 | // Callback function (event handler) prototype, must be defined by user. | |
| 213 | // Net skeleton will call event handler, passing events defined above. | |
| 214 | struct ns_connection; | |
| 215 | typedef void (*ns_callback_t)(struct ns_connection *, int event_num, void *evp); | |
| 216 | ||
| 217 | // Events. Meaning of event parameter (evp) is given in the comment. | |
| 218 | #define NS_POLL 0 // Sent to each connection on each call to ns_mgr_poll() | |
| 219 | #define NS_ACCEPT 1 // New connection accept()-ed. union socket_address *addr | |
| 220 | #define NS_CONNECT 2 // connect() succeeded or failed. int *success_status | |
| 221 | #define NS_RECV 3 // Data has benn received. int *num_bytes | |
| 222 | #define NS_SEND 4 // Data has been written to a socket. int *num_bytes | |
| 223 | #define NS_CLOSE 5 // Connection is closed. NULL | |
| 224 | ||
| 225 | ||
| 226 | struct ns_mgr { | |
| 227 | struct ns_connection *active_connections; | |
| 228 | const char *hexdump_file; // Debug hexdump file path | |
| 229 | sock_t ctl[2]; // Socketpair for mg_wakeup() | |
| 230 | void *user_data; // User data | |
| 231 | }; | |
| 232 | ||
| 233 | ||
| 234 | struct ns_connection { | |
| 235 | struct ns_connection *next, *prev; // ns_mgr::active_connections linkage | |
| 236 | struct ns_connection *listener; // Set only for accept()-ed connections | |
| 237 | struct ns_mgr *mgr; | |
| 238 | ||
| 239 | sock_t sock; // Socket | |
| 240 | union socket_address sa; // Peer address | |
| 241 | size_t recv_iobuf_limit; /* Max size of recv buffer */ | |
| 242 | struct iobuf recv_iobuf; // Received data | |
| 243 | struct iobuf send_iobuf; // Data scheduled for sending | |
| 244 | SSL *ssl; | |
| 245 | SSL_CTX *ssl_ctx; | |
| 246 | void *user_data; // User-specific data | |
| 247 | void *proto_data; // Application protocol-specific data | |
| 248 | time_t last_io_time; // Timestamp of the last socket IO | |
| 249 | ns_callback_t callback; // Event handler function | |
| 250 | ||
| 251 | unsigned int flags; | |
| 252 | #define NSF_FINISHED_SENDING_DATA (1 << 0) | |
| 253 | #define NSF_BUFFER_BUT_DONT_SEND (1 << 1) | |
| 254 | #define NSF_SSL_HANDSHAKE_DONE (1 << 2) | |
| 255 | #define NSF_CONNECTING (1 << 3) | |
| 256 | #define NSF_CLOSE_IMMEDIATELY (1 << 4) | |
| 257 | #define NSF_WANT_READ (1 << 5) | |
| 258 | #define NSF_WANT_WRITE (1 << 6) | |
| 259 | #define NSF_LISTENING (1 << 7) | |
| 260 | #define NSF_UDP (1 << 8) | |
| 261 | #define NSF_DISCARD (1 << 9) | |
| 262 | ||
| 263 | #define NSF_USER_1 (1 << 20) | |
| 264 | #define NSF_USER_2 (1 << 21) | |
| 265 | #define NSF_USER_3 (1 << 22) | |
| 266 | #define NSF_USER_4 (1 << 23) | |
| 267 | #define NSF_USER_5 (1 << 24) | |
| 268 | #define NSF_USER_6 (1 << 25) | |
| 269 | }; | |
| 270 | ||
| 271 | void ns_mgr_init(struct ns_mgr *, void *user_data); | |
| 272 | void ns_mgr_free(struct ns_mgr *); | |
| 273 | time_t ns_mgr_poll(struct ns_mgr *, int milli); | |
| 274 | void ns_broadcast(struct ns_mgr *, ns_callback_t, void *, size_t); | |
| 275 | ||
| 276 | struct ns_connection *ns_next(struct ns_mgr *, struct ns_connection *); | |
| 277 | struct ns_connection *ns_add_sock(struct ns_mgr *, sock_t, | |
| 278 | ns_callback_t, void *); | |
| 279 | struct ns_connection *ns_bind(struct ns_mgr *, const char *, | |
| 280 | ns_callback_t, void *); | |
| 281 | struct ns_connection *ns_connect(struct ns_mgr *, const char *, | |
| 282 | ns_callback_t, void *); | |
| 283 | ||
| 284 | int ns_send(struct ns_connection *, const void *buf, size_t len); | |
| 285 | int ns_printf(struct ns_connection *, const char *fmt, ...); | |
| 286 | int ns_vprintf(struct ns_connection *, const char *fmt, va_list ap); | |
| 287 | ||
| 288 | // Utility functions | |
| 289 | void *ns_start_thread(void *(*f)(void *), void *p); | |
| 290 | int ns_socketpair(sock_t [2]); | |
| 291 | int ns_socketpair2(sock_t [2], int sock_type); // SOCK_STREAM or SOCK_DGRAM | |
| 292 | void ns_set_close_on_exec(sock_t); | |
| 293 | void ns_sock_to_str(sock_t sock, char *buf, size_t len, int flags); | |
| 294 | int ns_hexdump(const void *buf, int len, char *dst, int dst_len); | |
| 295 | int ns_avprintf(char **buf, size_t size, const char *fmt, va_list ap); | |
| 296 | int ns_resolve(const char *domain_name, char *ip_addr_buf, size_t buf_len); | |
| 297 | ||
| 298 | #ifdef __cplusplus | |
| 299 | } | |
| 300 | #endif // __cplusplus | |
| 301 | ||
| 302 | #endif // NS_SKELETON_HEADER_INCLUDED | |
| 303 | // Copyright (c) 2014 Cesanta Software Limited | |
| 304 | // All rights reserved | |
| 305 | // | |
| 306 | // This software is dual-licensed: you can redistribute it and/or modify | |
| 307 | // it under the terms of the GNU General Public License version 2 as | |
| 308 | // published by the Free Software Foundation. For the terms of this | |
| 309 | // license, see <http://www.gnu.org/licenses/>. | |
| 310 | // | |
| 311 | // You are free to use this software under the terms of the GNU General | |
| 312 | // Public License, but WITHOUT ANY WARRANTY; without even the implied | |
| 313 | // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | |
| 314 | // See the GNU General Public License for more details. | |
| 315 | // | |
| 316 | // Alternatively, you can license this software under a commercial | |
| 317 | // license, as set out in <http://cesanta.com/>. | |
| 318 | // | |
| 319 | // $Date: 2014-09-28 05:04:41 UTC $ | |
| 320 | ||
| 321 | ||
| 322 | #ifndef NS_MALLOC | |
| 323 | #define NS_MALLOC malloc | |
| 324 | #endif | |
| 325 | ||
| 326 | #ifndef NS_REALLOC | |
| 327 | #define NS_REALLOC realloc | |
| 328 | #endif | |
| 329 | ||
| 330 | #ifndef NS_FREE | |
| 331 | #define NS_FREE free | |
| 332 | #endif | |
| 333 | ||
| 334 | #ifndef NS_CALLOC | |
| 335 | #define NS_CALLOC calloc | |
| 336 | #endif | |
| 337 | ||
| 338 | #define NS_MAX_SOCKETPAIR_ATTEMPTS 10 | |
| 339 | #define NS_CTL_MSG_MESSAGE_SIZE (8 * 1024) | |
| 340 | #define NS_READ_BUFFER_SIZE 2048 | |
| 341 | #define NS_UDP_RECEIVE_BUFFER_SIZE 2000 | |
| 342 | #define NS_VPRINTF_BUFFER_SIZE 500 | |
| 343 | ||
| 344 | struct ctl_msg { | |
| 345 | ns_callback_t callback; | |
| 346 | char message[NS_CTL_MSG_MESSAGE_SIZE]; | |
| 347 | }; | |
| 348 | ||
| 349 | void iobuf_resize(struct iobuf *io, size_t new_size) { | |
| 350 | char *p; | |
| 351 | if ((new_size > io->size || (new_size < io->size && new_size >= io->len)) && | |
| 352 | (p = (char *) NS_REALLOC(io->buf, new_size)) != NULL) { | |
| 353 | io->size = new_size; | |
| 354 | io->buf = p; | |
| 355 | } | |
| 356 | } | |
| 357 | ||
| 358 | void iobuf_init(struct iobuf *iobuf, size_t initial_size) { | |
| 359 | iobuf->len = iobuf->size = 0; | |
| 360 | iobuf->buf = NULL; | |
| 361 | iobuf_resize(iobuf, initial_size); | |
| 362 | } | |
| 363 | ||
| 364 | void iobuf_free(struct iobuf *iobuf) { | |
| 365 | if (iobuf != NULL) { | |
| 366 | if (iobuf->buf != NULL) NS_FREE(iobuf->buf); | |
| 367 | iobuf_init(iobuf, 0); | |
| 368 | } | |
| 369 | } | |
| 370 | ||
| 371 | size_t iobuf_append(struct iobuf *io, const void *buf, size_t len) { | |
| 372 | char *p = NULL; | |
| 373 | ||
| 374 | assert(io != NULL); | |
| 375 | assert(io->len <= io->size); | |
| 376 | ||
| 377 | /* check overflow */ | |
| 378 | if (len > ~(size_t)0 - (size_t)(io->buf + io->len)) { | |
| 379 | return 0; | |
| 380 | } | |
| 381 | ||
| 382 | if (len <= 0) { | |
| 383 | } else if (io->len + len <= io->size) { | |
| 384 | memcpy(io->buf + io->len, buf, len); | |
| 385 | io->len += len; | |
| 386 | } else if ((p = (char *) NS_REALLOC(io->buf, io->len + len)) != NULL) { | |
| 387 | io->buf = p; | |
| 388 | memcpy(io->buf + io->len, buf, len); | |
| 389 | io->len += len; | |
| 390 | io->size = io->len; | |
| 391 | } else { | |
| 392 | len = 0; | |
| 393 | } | |
| 394 | ||
| 395 | return len; | |
| 396 | } | |
| 397 | ||
| 398 | void iobuf_remove(struct iobuf *io, size_t n) { | |
| 399 | if (n > 0 && n <= io->len) { | |
| 400 | memmove(io->buf, io->buf + n, io->len - n); | |
| 401 | io->len -= n; | |
| 402 | } | |
| 403 | } | |
| 404 | ||
| 405 | static size_t ns_out(struct ns_connection *nc, const void *buf, size_t len) { | |
| 406 | if (nc->flags & NSF_UDP) { | |
| 407 | long n = sendto(nc->sock, (const char *) buf, len, 0, &nc->sa.sa, | |
| 408 | sizeof(nc->sa.sin)); | |
| 409 | DBG(("%p %d send %ld (%d %s)", nc, nc->sock, n, errno, strerror(errno))); | |
| 410 | return n < 0 ? 0 : n; | |
| 411 | } else { | |
| 412 | return iobuf_append(&nc->send_iobuf, buf, len); | |
| 413 | } | |
| 414 | } | |
| 415 | ||
| 416 | #ifndef NS_DISABLE_THREADS | |
| 417 | void *ns_start_thread(void *(*f)(void *), void *p) { | |
| 418 | #ifdef _WIN32 | |
| 419 | return (void *) _beginthread((void (__cdecl *)(void *)) f, 0, p); | |
| 420 | #else | |
| 421 | pthread_t thread_id = (pthread_t) 0; | |
| 422 | pthread_attr_t attr; | |
| 423 | ||
| 424 | (void) pthread_attr_init(&attr); | |
| 425 | (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); | |
| 426 | ||
| 427 | #if defined(NS_STACK_SIZE) && NS_STACK_SIZE > 1 | |
| 428 | (void) pthread_attr_setstacksize(&attr, NS_STACK_SIZE); | |
| 429 | #endif | |
| 430 | ||
| 431 | pthread_create(&thread_id, &attr, f, p); | |
| 432 | pthread_attr_destroy(&attr); | |
| 433 | ||
| 434 | return (void *) thread_id; | |
| 435 | #endif | |
| 436 | } | |
| 437 | #endif // NS_DISABLE_THREADS | |
| 438 | ||
| 439 | static void ns_add_conn(struct ns_mgr *mgr, struct ns_connection *c) { | |
| 440 | c->next = mgr->active_connections; | |
| 441 | mgr->active_connections = c; | |
| 442 | c->prev = NULL; | |
| 443 | if (c->next != NULL) c->next->prev = c; | |
| 444 | } | |
| 445 | ||
| 446 | static void ns_remove_conn(struct ns_connection *conn) { | |
| 447 | if (conn->prev == NULL) conn->mgr->active_connections = conn->next; | |
| 448 | if (conn->prev) conn->prev->next = conn->next; | |
| 449 | if (conn->next) conn->next->prev = conn->prev; | |
| 450 | } | |
| 451 | ||
| 452 | // Print message to buffer. If buffer is large enough to hold the message, | |
| 453 | // return buffer. If buffer is to small, allocate large enough buffer on heap, | |
| 454 | // and return allocated buffer. | |
| 455 | int ns_avprintf(char **buf, size_t size, const char *fmt, va_list ap) { | |
| 456 | va_list ap_copy; | |
| 457 | int len; | |
| 458 | ||
| 459 | va_copy(ap_copy, ap); | |
| 460 | len = vsnprintf(*buf, size, fmt, ap_copy); | |
| 461 | va_end(ap_copy); | |
| 462 | ||
| 463 | if (len < 0) { | |
| 464 | // eCos and Windows are not standard-compliant and return -1 when | |
| 465 | // the buffer is too small. Keep allocating larger buffers until we | |
| 466 | // succeed or out of memory. | |
| 467 | *buf = NULL; | |
| 468 | while (len < 0) { | |
| 469 | if (*buf) NS_FREE(*buf); | |
| 470 | size *= 2; | |
| 471 | if ((*buf = (char *) NS_MALLOC(size)) == NULL) break; | |
| 472 | va_copy(ap_copy, ap); | |
| 473 | len = vsnprintf(*buf, size, fmt, ap_copy); | |
| 474 | va_end(ap_copy); | |
| 475 | } | |
| 476 | } else if (len > (int) size) { | |
| 477 | // Standard-compliant code path. Allocate a buffer that is large enough. | |
| 478 | if ((*buf = (char *) NS_MALLOC(len + 1)) == NULL) { | |
| 479 | len = -1; | |
| 480 | } else { | |
| 481 | va_copy(ap_copy, ap); | |
| 482 | len = vsnprintf(*buf, len + 1, fmt, ap_copy); | |
| 483 | va_end(ap_copy); | |
| 484 | } | |
| 485 | } | |
| 486 | ||
| 487 | return len; | |
| 488 | } | |
| 489 | ||
| 490 | int ns_vprintf(struct ns_connection *nc, const char *fmt, va_list ap) { | |
| 491 | char mem[NS_VPRINTF_BUFFER_SIZE], *buf = mem; | |
| 492 | int len; | |
| 493 | ||
| 494 | if ((len = ns_avprintf(&buf, sizeof(mem), fmt, ap)) > 0) { | |
| 495 | ns_out(nc, buf, len); | |
| 496 | } | |
| 497 | if (buf != mem && buf != NULL) { | |
| 498 | NS_FREE(buf); | |
| 499 | } | |
| 500 | ||
| 501 | return len; | |
| 502 | } | |
| 503 | ||
| 504 | int ns_printf(struct ns_connection *conn, const char *fmt, ...) { | |
| 505 | int len; | |
| 506 | va_list ap; | |
| 507 | va_start(ap, fmt); | |
| 508 | len = ns_vprintf(conn, fmt, ap); | |
| 509 | va_end(ap); | |
| 510 | return len; | |
| 511 | } | |
| 512 | ||
| 513 | static void hexdump(struct ns_connection *nc, const char *path, | |
| 514 | int num_bytes, int ev) { | |
| 515 | const struct iobuf *io = ev == NS_SEND ? &nc->send_iobuf : &nc->recv_iobuf; | |
| 516 | FILE *fp; | |
| 517 | char *buf, src[60], dst[60]; | |
| 518 | int buf_size = num_bytes * 5 + 100; | |
| 519 | ||
| 520 | if ((fp = fopen(path, "a")) != NULL) { | |
| 521 | ns_sock_to_str(nc->sock, src, sizeof(src), 3); | |
| 522 | ns_sock_to_str(nc->sock, dst, sizeof(dst), 7); | |
| 523 | fprintf(fp, "%lu %p %s %s %s %d\n", (unsigned long) time(NULL), | |
| 524 | nc->user_data, src, | |
| 525 | ev == NS_RECV ? "<-" : ev == NS_SEND ? "->" : | |
| 526 | ev == NS_ACCEPT ? "<A" : ev == NS_CONNECT ? "C>" : "XX", | |
| 527 | dst, num_bytes); | |
| 528 | if (num_bytes > 0 && (buf = (char *) NS_MALLOC(buf_size)) != NULL) { | |
| 529 | ns_hexdump(io->buf + (ev == NS_SEND ? 0 : io->len) - | |
| 530 | (ev == NS_SEND ? 0 : num_bytes), num_bytes, buf, buf_size); | |
| 531 | fprintf(fp, "%s", buf); | |
| 532 | NS_FREE(buf); | |
| 533 | } | |
| 534 | fclose(fp); | |
| 535 | } | |
| 536 | } | |
| 537 | ||
| 538 | static void ns_call(struct ns_connection *nc, int ev, void *p) { | |
| 539 | if (nc->mgr->hexdump_file != NULL && ev != NS_POLL) { | |
| 540 | int len = (ev == NS_RECV || ev == NS_SEND) ? * (int *) p : 0; | |
| 541 | hexdump(nc, nc->mgr->hexdump_file, len, ev); | |
| 542 | } | |
| 543 | ||
| 544 | nc->callback(nc, ev, p); | |
| 545 | } | |
| 546 | ||
| 547 | static void ns_destroy_conn(struct ns_connection *conn) { | |
| 548 | closesocket(conn->sock); | |
| 549 | iobuf_free(&conn->recv_iobuf); | |
| 550 | iobuf_free(&conn->send_iobuf); | |
| 551 | #ifdef NS_ENABLE_SSL | |
| 552 | if (conn->ssl != NULL) { | |
| 553 | SSL_free(conn->ssl); | |
| 554 | } | |
| 555 | if (conn->ssl_ctx != NULL) { | |
| 556 | SSL_CTX_free(conn->ssl_ctx); | |
| 557 | } | |
| 558 | #endif | |
| 559 | NS_FREE(conn); | |
| 560 | } | |
| 561 | ||
| 562 | static void ns_close_conn(struct ns_connection *conn) { | |
| 563 | DBG(("%p %d", conn, conn->flags)); | |
| 564 | ns_call(conn, NS_CLOSE, NULL); | |
| 565 | ns_remove_conn(conn); | |
| 566 | ns_destroy_conn(conn); | |
| 567 | } | |
| 568 | ||
| 569 | void ns_set_close_on_exec(sock_t sock) { | |
| 570 | #ifdef _WIN32 | |
| 571 | (void) SetHandleInformation((HANDLE) sock, HANDLE_FLAG_INHERIT, 0); | |
| 572 | #else | |
| 573 | fcntl(sock, F_SETFD, FD_CLOEXEC); | |
| 574 | #endif | |
| 575 | } | |
| 576 | ||
| 577 | static void ns_set_non_blocking_mode(sock_t sock) { | |
| 578 | #ifdef _WIN32 | |
| 579 | unsigned long on = 1; | |
| 580 | ioctlsocket(sock, FIONBIO, &on); | |
| 581 | #else | |
| 582 | int flags = fcntl(sock, F_GETFL, 0); | |
| 583 | fcntl(sock, F_SETFL, flags | O_NONBLOCK); | |
| 584 | #endif | |
| 585 | } | |
| 586 | ||
| 587 | #ifndef NS_DISABLE_SOCKETPAIR | |
| 588 | int ns_socketpair2(sock_t sp[2], int sock_type) { | |
| 589 | union socket_address sa; | |
| 590 | sock_t sock; | |
| 591 | socklen_t len = sizeof(sa.sin); | |
| 592 | int ret = 0; | |
| 593 | ||
| 594 | sp[0] = sp[1] = INVALID_SOCKET; | |
| 595 | ||
| 596 | (void) memset(&sa, 0, sizeof(sa)); | |
| 597 | sa.sin.sin_family = AF_INET; | |
| 598 | sa.sin.sin_port = htons(0); | |
| 599 | sa.sin.sin_addr.s_addr = htonl(0x7f000001); | |
| 600 | ||
| 601 | if ((sock = socket(AF_INET, sock_type, 0)) != INVALID_SOCKET && | |
| 602 | !bind(sock, &sa.sa, len) && | |
| 603 | (sock_type == SOCK_DGRAM || !listen(sock, 1)) && | |
| 604 | !getsockname(sock, &sa.sa, &len) && | |
| 605 | (sp[0] = socket(AF_INET, sock_type, 0)) != INVALID_SOCKET && | |
| 606 | !connect(sp[0], &sa.sa, len) && | |
| 607 | (sock_type == SOCK_STREAM || | |
| 608 | (!getsockname(sp[0], &sa.sa, &len) && !connect(sock, &sa.sa, len))) && | |
| 609 | (sp[1] = (sock_type == SOCK_DGRAM ? sock : | |
| 610 | accept(sock, &sa.sa, &len))) != INVALID_SOCKET) { | |
| 611 | ns_set_close_on_exec(sp[0]); | |
| 612 | ns_set_close_on_exec(sp[1]); | |
| 613 | ret = 1; | |
| 614 | } else { | |
| 615 | if (sp[0] != INVALID_SOCKET) closesocket(sp[0]); | |
| 616 | if (sp[1] != INVALID_SOCKET) closesocket(sp[1]); | |
| 617 | sp[0] = sp[1] = INVALID_SOCKET; | |
| 618 | } | |
| 619 | if (sock_type != SOCK_DGRAM) closesocket(sock); | |
| 620 | ||
| 621 | return ret; | |
| 622 | } | |
| 623 | ||
| 624 | int ns_socketpair(sock_t sp[2]) { | |
| 625 | return ns_socketpair2(sp, SOCK_STREAM); | |
| 626 | } | |
| 627 | #endif // NS_DISABLE_SOCKETPAIR | |
| 628 | ||
| 629 | // TODO(lsm): use non-blocking resolver | |
| 630 | static int ns_resolve2(const char *host, struct in_addr *ina) { | |
| 631 | #ifdef NS_ENABLE_GETADDRINFO | |
| 632 | int rv = 0; | |
| 633 | struct addrinfo hints, *servinfo, *p; | |
| 634 | struct sockaddr_in *h = NULL; | |
| 635 | ||
| 636 | memset(&hints, 0, sizeof hints); | |
| 637 | hints.ai_family = AF_INET; | |
| 638 | hints.ai_socktype = SOCK_STREAM; | |
| 639 | ||
| 640 | if((rv = getaddrinfo(host, NULL , NULL, &servinfo)) != 0) { | |
| 641 | DBG(("getaddrinfo(%s) failed: %s", host, strerror(errno))); | |
| 642 | return 0; | |
| 643 | } | |
| 644 | ||
| 645 | for(p = servinfo; p != NULL; p = p->ai_next) { | |
| 646 | memcpy(&h, &p->ai_addr, sizeof(struct sockaddr_in *)); | |
| 647 | memcpy(ina, &h->sin_addr, sizeof(ina)); | |
| 648 | } | |
| 649 | ||
| 650 | freeaddrinfo(servinfo); | |
| 651 | return 1; | |
| 652 | #else | |
| 653 | struct hostent *he; | |
| 654 | if ((he = gethostbyname(host)) == NULL) { | |
| 655 | DBG(("gethostbyname(%s) failed: %s", host, strerror(errno))); | |
| 656 | } else { | |
| 657 | memcpy(ina, he->h_addr_list[0], sizeof(*ina)); | |
| 658 | return 1; | |
| 659 | } | |
| 660 | return 0; | |
| 661 | #endif | |
| 662 | } | |
| 663 | ||
| 664 | // Resolve FDQN "host", store IP address in the "ip". | |
| 665 | // Return > 0 (IP address length) on success. | |
| 666 | int ns_resolve(const char *host, char *buf, size_t n) { | |
| 667 | struct in_addr ad; | |
| 668 | return ns_resolve2(host, &ad) ? snprintf(buf, n, "%s", inet_ntoa(ad)) : 0; | |
| 669 | } | |
| 670 | ||
| 671 | // Address format: [PROTO://][IP_ADDRESS:]PORT[:CERT][:CA_CERT] | |
| 672 | static int ns_parse_address(const char *str, union socket_address *sa, | |
| 673 | int *proto, int *use_ssl, char *cert, char *ca) { | |
| 674 | unsigned int a, b, c, d, port; | |
| 675 | int n = 0, len = 0; | |
| 676 | char host[200]; | |
| 677 | #ifdef NS_ENABLE_IPV6 | |
| 678 | char buf[100]; | |
| 679 | #endif | |
| 680 | ||
| 681 | // MacOS needs that. If we do not zero it, subsequent bind() will fail. | |
| 682 | // Also, all-zeroes in the socket address means binding to all addresses | |
| 683 | // for both IPv4 and IPv6 (INADDR_ANY and IN6ADDR_ANY_INIT). | |
| 684 | memset(sa, 0, sizeof(*sa)); | |
| 685 | sa->sin.sin_family = AF_INET; | |
| 686 | ||
| 687 | *proto = SOCK_STREAM; | |
| 688 | *use_ssl = 0; | |
| 689 | cert[0] = ca[0] = '\0'; | |
| 690 | ||
| 691 | if (memcmp(str, "ssl://", 6) == 0) { | |
| 692 | str += 6; | |
| 693 | *use_ssl = 1; | |
| 694 | } else if (memcmp(str, "udp://", 6) == 0) { | |
| 695 | str += 6; | |
| 696 | *proto = SOCK_DGRAM; | |
| 697 | } else if (memcmp(str, "tcp://", 6) == 0) { | |
| 698 | str += 6; | |
| 699 | } | |
| 700 | ||
| 701 | if (sscanf(str, "%u.%u.%u.%u:%u%n", &a, &b, &c, &d, &port, &len) == 5) { | |
| 702 | // Bind to a specific IPv4 address, e.g. 192.168.1.5:8080 | |
| 703 | sa->sin.sin_addr.s_addr = htonl((a << 24) | (b << 16) | (c << 8) | d); | |
| 704 | sa->sin.sin_port = htons((uint16_t) port); | |
| 705 | #ifdef NS_ENABLE_IPV6 | |
| 706 | } else if (sscanf(str, "[%99[^]]]:%u%n", buf, &port, &len) == 2 && | |
| 707 | inet_pton(AF_INET6, buf, &sa->sin6.sin6_addr)) { | |
| 708 | // IPv6 address, e.g. [3ffe:2a00:100:7031::1]:8080 | |
| 709 | sa->sin6.sin6_family = AF_INET6; | |
| 710 | sa->sin6.sin6_port = htons((uint16_t) port); | |
| 711 | #endif | |
| 712 | } else if (sscanf(str, "%199[^ :]:%u%n", host, &port, &len) == 2) { | |
| 713 | sa->sin.sin_port = htons((uint16_t) port); | |
| 714 | ns_resolve2(host, &sa->sin.sin_addr); | |
| 715 | } else if (sscanf(str, "%u%n", &port, &len) == 1) { | |
| 716 | // If only port is specified, bind to IPv4, INADDR_ANY | |
| 717 | sa->sin.sin_port = htons((uint16_t) port); | |
| 718 | } | |
| 719 | ||
| 720 | if (*use_ssl && (sscanf(str + len, ":%99[^:,]:%99[^:,]%n", cert, ca, &n) == 2 || | |
| 721 | sscanf(str + len, ":%99[^:,]%n", cert, &n) == 1)) { | |
| 722 | len += n; | |
| 723 | } | |
| 724 | ||
| 725 | return port < 0xffff && str[len] == '\0' ? len : 0; | |
| 726 | } | |
| 727 | ||
| 728 | // 'sa' must be an initialized address to bind to | |
| 729 | static sock_t ns_open_listening_socket(union socket_address *sa, int proto) { | |
| 730 | socklen_t sa_len = (sa->sa.sa_family == AF_INET) ? | |
| 731 | sizeof(sa->sin) : sizeof(sa->sin6); | |
| 732 | sock_t sock = INVALID_SOCKET; | |
| 733 | #ifndef _WIN32 | |
| 734 | int on = 1; | |
| 735 | #endif | |
| 736 | ||
| 737 | if ((sock = socket(sa->sa.sa_family, proto, 0)) != INVALID_SOCKET && | |
| 738 | #ifndef _WIN32 | |
| 739 | // SO_RESUSEADDR is not enabled on Windows because the semantics of | |
| 740 | // SO_REUSEADDR on UNIX and Windows is different. On Windows, | |
| 741 | // SO_REUSEADDR allows to bind a socket to a port without error even if | |
| 742 | // the port is already open by another program. This is not the behavior | |
| 743 | // SO_REUSEADDR was designed for, and leads to hard-to-track failure | |
| 744 | // scenarios. Therefore, SO_REUSEADDR was disabled on Windows. | |
| 745 | !setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *) &on, sizeof(on)) && | |
| 746 | #endif | |
| 747 | !bind(sock, &sa->sa, sa_len) && | |
| 748 | (proto == SOCK_DGRAM || listen(sock, SOMAXCONN) == 0)) { | |
| 749 | ns_set_non_blocking_mode(sock); | |
| 750 | // In case port was set to 0, get the real port number | |
| 751 | (void) getsockname(sock, &sa->sa, &sa_len); | |
| 752 | } else if (sock != INVALID_SOCKET) { | |
| 753 | closesocket(sock); | |
| 754 | sock = INVALID_SOCKET; | |
| 755 | } | |
| 756 | ||
| 757 | return sock; | |
| 758 | } | |
| 759 | ||
| 760 | #ifdef NS_ENABLE_SSL | |
| 761 | // Certificate generation script is at | |
| 762 | // https://github.com/cesanta/net_skeleton/blob/master/scripts/gen_certs.sh | |
| 763 | ||
| 764 | static int ns_use_ca_cert(SSL_CTX *ctx, const char *cert) { | |
| 765 | if (ctx == NULL) { | |
| 766 | return -1; | |
| 767 | } else if (cert == NULL || cert[0] == '\0') { | |
| 768 | return 0; | |
| 769 | } | |
| 770 | SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, 0); | |
| 771 | return SSL_CTX_load_verify_locations(ctx, cert, NULL) == 1 ? 0 : -2; | |
| 772 | } | |
| 773 | ||
| 774 | static int ns_use_cert(SSL_CTX *ctx, const char *pem_file) { | |
| 775 | if (ctx == NULL) { | |
| 776 | return -1; | |
| 777 | } else if (pem_file == NULL || pem_file[0] == '\0') { | |
| 778 | return 0; | |
| 779 | } else if (SSL_CTX_use_certificate_file(ctx, pem_file, 1) == 0 || | |
| 780 | SSL_CTX_use_PrivateKey_file(ctx, pem_file, 1) == 0) { | |
| 781 | return -2; | |
| 782 | } else { | |
| 783 | SSL_CTX_set_mode(ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); | |
| 784 | SSL_CTX_use_certificate_chain_file(ctx, pem_file); | |
| 785 | return 0; | |
| 786 | } | |
| 787 | } | |
| 788 | #endif // NS_ENABLE_SSL | |
| 789 | ||
| 790 | struct ns_connection *ns_bind(struct ns_mgr *srv, const char *str, | |
| 791 | ns_callback_t callback, void *user_data) { | |
| 792 | union socket_address sa; | |
| 793 | struct ns_connection *nc = NULL; | |
| 794 | int use_ssl, proto; | |
| 795 | char cert[100], ca_cert[100]; | |
| 796 | sock_t sock; | |
| 797 | ||
| 798 | ns_parse_address(str, &sa, &proto, &use_ssl, cert, ca_cert); | |
| 799 | if (use_ssl && cert[0] == '\0') return NULL; | |
| 800 | ||
| 801 | if ((sock = ns_open_listening_socket(&sa, proto)) == INVALID_SOCKET) { | |
| 802 | } else if ((nc = ns_add_sock(srv, sock, callback, NULL)) == NULL) { | |
| 803 | closesocket(sock); | |
| 804 | } else { | |
| 805 | nc->sa = sa; | |
| 806 | nc->flags |= NSF_LISTENING; | |
| 807 | nc->user_data = user_data; | |
| 808 | nc->callback = callback; | |
| 809 | ||
| 810 | if (proto == SOCK_DGRAM) { | |
| 811 | nc->flags |= NSF_UDP; | |
| 812 | } | |
| 813 | ||
| 814 | #ifdef NS_ENABLE_SSL | |
| 815 | if (use_ssl) { | |
| 816 | nc->ssl_ctx = SSL_CTX_new(SSLv23_server_method()); | |
| 817 | if (ns_use_cert(nc->ssl_ctx, cert) != 0 || | |
| 818 | ns_use_ca_cert(nc->ssl_ctx, ca_cert) != 0) { | |
| 819 | ns_close_conn(nc); | |
| 820 | nc = NULL; | |
| 821 | } | |
| 822 | } | |
| 823 | #endif | |
| 824 | ||
| 825 | DBG(("%p sock %d/%d ssl %p %p", nc, sock, proto, nc->ssl_ctx, nc->ssl)); | |
| 826 | } | |
| 827 | ||
| 828 | return nc; | |
| 829 | } | |
| 830 | ||
| 831 | static struct ns_connection *accept_conn(struct ns_connection *ls) { | |
| 832 | struct ns_connection *c = NULL; | |
| 833 | union socket_address sa; | |
| 834 | socklen_t len = sizeof(sa); | |
| 835 | sock_t sock = INVALID_SOCKET; | |
| 836 | ||
| 837 | // NOTE(lsm): on Windows, sock is always > FD_SETSIZE | |
| 838 | if ((sock = accept(ls->sock, &sa.sa, &len)) == INVALID_SOCKET) { | |
| 839 | } else if ((c = ns_add_sock(ls->mgr, sock, ls->callback, | |
| 840 | ls->user_data)) == NULL) { | |
| 841 | closesocket(sock); | |
| 842 | #ifdef NS_ENABLE_SSL | |
| 843 | } else if (ls->ssl_ctx != NULL && | |
| 844 | ((c->ssl = SSL_new(ls->ssl_ctx)) == NULL || | |
| 845 | SSL_set_fd(c->ssl, sock) != 1)) { | |
| 846 | DBG(("SSL error")); | |
| 847 | ns_close_conn(c); | |
| 848 | c = NULL; | |
| 849 | #endif | |
| 850 | } else { | |
| 851 | c->listener = ls; | |
| 852 | c->proto_data = ls->proto_data; | |
| 853 | ns_call(c, NS_ACCEPT, &sa); | |
| 854 | DBG(("%p %d %p %p", c, c->sock, c->ssl_ctx, c->ssl)); | |
| 855 | } | |
| 856 | ||
| 857 | return c; | |
| 858 | } | |
| 859 | ||
| 860 | static int ns_is_error(int n) { | |
| 861 | return n == 0 || | |
| 862 | (n < 0 && errno != EINTR && errno != EINPROGRESS && | |
| 863 | errno != EAGAIN && errno != EWOULDBLOCK | |
| 864 | #ifdef _WIN32 | |
| 865 | && WSAGetLastError() != WSAEINTR && WSAGetLastError() != WSAEWOULDBLOCK | |
| 866 | #endif | |
| 867 | ) | |
| 868 | #ifdef NS_ENABLE_SSL | |
| 869 | /* | |
| 870 | * OpenSSL can return an error when the peer is closing the socket. | |
| 871 | * We don't encounter this error with openssl actually, but it's returned | |
| 872 | * by our polarssl <-> openssl wrapper who tries to speak the openssl API | |
| 873 | * as we understood it. | |
| 874 | */ | |
| 875 | || n == SSL_AD_CLOSE_NOTIFY | |
| 876 | #endif | |
| 877 | ; | |
| 878 | } | |
| 879 | ||
| 880 | void ns_sock_to_str(sock_t sock, char *buf, size_t len, int flags) { | |
| 881 | union socket_address sa; | |
| 882 | socklen_t slen = sizeof(sa); | |
| 883 | ||
| 884 | if (buf != NULL && len > 0) { | |
| 885 | buf[0] = '\0'; | |
| 886 | memset(&sa, 0, sizeof(sa)); | |
| 887 | if (flags & 4) { | |
| 888 | getpeername(sock, &sa.sa, &slen); | |
| 889 | } else { | |
| 890 | getsockname(sock, &sa.sa, &slen); | |
| 891 | } | |
| 892 | if (flags & 1) { | |
| 893 | #if defined(NS_ENABLE_IPV6) | |
| 894 | inet_ntop(sa.sa.sa_family, sa.sa.sa_family == AF_INET ? | |
| 895 | (void *) &sa.sin.sin_addr : | |
| 896 | (void *) &sa.sin6.sin6_addr, buf, len); | |
| 897 | #elif defined(_WIN32) | |
| 898 | // Only Windoze Vista (and newer) have inet_ntop() | |
| 899 | strncpy(buf, inet_ntoa(sa.sin.sin_addr), len); | |
| 900 | #else | |
| 901 | inet_ntop(sa.sa.sa_family, (void *) &sa.sin.sin_addr, buf,(socklen_t)len); | |
| 902 | #endif | |
| 903 | } | |
| 904 | if (flags & 2) { | |
| 905 | snprintf(buf + strlen(buf), len - (strlen(buf) + 1), "%s%d", | |
| 906 | flags & 1 ? ":" : "", (int) ntohs(sa.sin.sin_port)); | |
| 907 | } | |
| 908 | } | |
| 909 | } | |
| 910 | ||
| 911 | int ns_hexdump(const void *buf, int len, char *dst, int dst_len) { | |
| 912 | const unsigned char *p = (const unsigned char *) buf; | |
| 913 | char ascii[17] = ""; | |
| 914 | int i, idx, n = 0; | |
| 915 | ||
| 916 | for (i = 0; i < len; i++) { | |
| 917 | idx = i % 16; | |
| 918 | if (idx == 0) { | |
| 919 | if (i > 0) n += snprintf(dst + n, dst_len - n, " %s\n", ascii); | |
| 920 | n += snprintf(dst + n, dst_len - n, "%04x ", i); | |
| 921 | } | |
| 922 | n += snprintf(dst + n, dst_len - n, " %02x", p[i]); | |
| 923 | ascii[idx] = p[i] < 0x20 || p[i] > 0x7e ? '.' : p[i]; | |
| 924 | ascii[idx + 1] = '\0'; | |
| 925 | } | |
| 926 | ||
| 927 | while (i++ % 16) n += snprintf(dst + n, dst_len - n, "%s", " "); | |
| 928 | n += snprintf(dst + n, dst_len - n, " %s\n\n", ascii); | |
| 929 | ||
| 930 | return n; | |
| 931 | } | |
| 932 | ||
| 933 | #ifdef NS_ENABLE_SSL | |
| 934 | static int ns_ssl_err(struct ns_connection *conn, int res) { | |
| 935 | int ssl_err = SSL_get_error(conn->ssl, res); | |
| 936 | if (ssl_err == SSL_ERROR_WANT_READ) conn->flags |= NSF_WANT_READ; | |
| 937 | if (ssl_err == SSL_ERROR_WANT_WRITE) conn->flags |= NSF_WANT_WRITE; | |
| 938 | return ssl_err; | |
| 939 | } | |
| 940 | #endif | |
| 941 | ||
| 942 | static void ns_read_from_socket(struct ns_connection *conn) { | |
| 943 | char buf[NS_READ_BUFFER_SIZE]; | |
| 944 | int n = 0; | |
| 945 | ||
| 946 | if (conn->flags & NSF_CONNECTING) { | |
| 947 | int ok = 1, ret; | |
| 948 | socklen_t len = sizeof(ok); | |
| 949 | ||
| 950 | ret = getsockopt(conn->sock, SOL_SOCKET, SO_ERROR, (char *) &ok, &len); | |
| 951 | (void) ret; | |
| 952 | #ifdef NS_ENABLE_SSL | |
| 953 | if (ret == 0 && ok == 0 && conn->ssl != NULL) { | |
| 954 | int res = SSL_connect(conn->ssl); | |
| 955 | int ssl_err = ns_ssl_err(conn, res); | |
| 956 | if (res == 1) { | |
| 957 | conn->flags |= NSF_SSL_HANDSHAKE_DONE; | |
| 958 | } else if (ssl_err == SSL_ERROR_WANT_READ || | |
| 959 | ssl_err == SSL_ERROR_WANT_WRITE) { | |
| 960 | return; // Call us again | |
| 961 | } else { | |
| 962 | ok = 1; | |
| 963 | } | |
| 964 | conn->flags &= ~(NSF_WANT_READ | NSF_WANT_WRITE); | |
| 965 | } | |
| 966 | #endif | |
| 967 | conn->flags &= ~NSF_CONNECTING; | |
| 968 | DBG(("%p ok=%d", conn, ok)); | |
| 969 | if (ok != 0) { | |
| 970 | conn->flags |= NSF_CLOSE_IMMEDIATELY; | |
| 971 | } | |
| 972 | ns_call(conn, NS_CONNECT, &ok); | |
| 973 | return; | |
| 974 | } | |
| 975 | ||
| 976 | #ifdef NS_ENABLE_SSL | |
| 977 | if (conn->ssl != NULL) { | |
| 978 | if (conn->flags & NSF_SSL_HANDSHAKE_DONE) { | |
| 979 | // SSL library may have more bytes ready to read then we ask to read. | |
| 980 | // Therefore, read in a loop until we read everything. Without the loop, | |
| 981 | // we skip to the next select() cycle which can just timeout. | |
| 982 | while ((n = SSL_read(conn->ssl, buf, sizeof(buf))) > 0) { | |
| 983 | DBG(("%p %d <- %d bytes (SSL)", conn, conn->flags, n)); | |
| 984 | iobuf_append(&conn->recv_iobuf, buf, n); | |
| 985 | ns_call(conn, NS_RECV, &n); | |
| 986 | } | |
| 987 | ns_ssl_err(conn, n); | |
| 988 | } else { | |
| 989 | int res = SSL_accept(conn->ssl); | |
| 990 | int ssl_err = ns_ssl_err(conn, res); | |
| 991 | if (res == 1) { | |
| 992 | conn->flags |= NSF_SSL_HANDSHAKE_DONE; | |
| 993 | conn->flags &= ~(NSF_WANT_READ | NSF_WANT_WRITE); | |
| 994 | } else if (ssl_err == SSL_ERROR_WANT_READ || | |
| 995 | ssl_err == SSL_ERROR_WANT_WRITE) { | |
| 996 | return; // Call us again | |
| 997 | } else { | |
| 998 | conn->flags |= NSF_CLOSE_IMMEDIATELY; | |
| 999 | } | |
| 1000 | return; | |
| 1001 | } | |
| 1002 | } else | |
| 1003 | #endif | |
| 1004 | { | |
| 1005 | while ((n = (int) recv(conn->sock, buf, sizeof(buf), 0)) > 0) { | |
| 1006 | DBG(("%p %d <- %d bytes (PLAIN)", conn, conn->flags, n)); | |
| 1007 | iobuf_append(&conn->recv_iobuf, buf, n); | |
| 1008 | ns_call(conn, NS_RECV, &n); | |
| 1009 | } | |
| 1010 | } | |
| 1011 | ||
| 1012 | if (ns_is_error(n)) { | |
| 1013 | conn->flags |= NSF_CLOSE_IMMEDIATELY; | |
| 1014 | } | |
| 1015 | } | |
| 1016 | ||
| 1017 | static void ns_write_to_socket(struct ns_connection *conn) { | |
| 1018 | struct iobuf *io = &conn->send_iobuf; | |
| 1019 | int n = 0; | |
| 1020 | ||
| 1021 | #ifdef NS_ENABLE_SSL | |
| 1022 | if (conn->ssl != NULL) { | |
| 1023 | n = SSL_write(conn->ssl, io->buf, io->len); | |
| 1024 | if (n <= 0) { | |
| 1025 | int ssl_err = ns_ssl_err(conn, n); | |
| 1026 | if (ssl_err == SSL_ERROR_WANT_READ || ssl_err == SSL_ERROR_WANT_WRITE) { | |
| 1027 | return; // Call us again | |
| 1028 | } else { | |
| 1029 | conn->flags |= NSF_CLOSE_IMMEDIATELY; | |
| 1030 | } | |
| 1031 | } else { | |
| 1032 | conn->flags &= ~(NSF_WANT_READ | NSF_WANT_WRITE); | |
| 1033 | } | |
| 1034 | } else | |
| 1035 | #endif | |
| 1036 | { n = (int) send(conn->sock, io->buf, io->len, 0); } | |
| 1037 | ||
| 1038 | DBG(("%p %d -> %d bytes", conn, conn->flags, n)); | |
| 1039 | ||
| 1040 | ns_call(conn, NS_SEND, &n); | |
| 1041 | if (ns_is_error(n)) { | |
| 1042 | conn->flags |= NSF_CLOSE_IMMEDIATELY; | |
| 1043 | } else if (n > 0) { | |
| 1044 | iobuf_remove(io, n); | |
| 1045 | } | |
| 1046 | } | |
| 1047 | ||
| 1048 | int ns_send(struct ns_connection *conn, const void *buf, size_t len) { | |
| 1049 | return (int) ns_out(conn, buf, len); | |
| 1050 | } | |
| 1051 | ||
| 1052 | static void ns_handle_udp(struct ns_connection *ls) { | |
| 1053 | struct ns_connection nc; | |
| 1054 | char buf[NS_UDP_RECEIVE_BUFFER_SIZE]; | |
| 1055 | ssize_t n; | |
| 1056 | socklen_t s_len = sizeof(nc.sa); | |
| 1057 | ||
| 1058 | memset(&nc, 0, sizeof(nc)); | |
| 1059 | n = recvfrom(ls->sock, buf, sizeof(buf), 0, &nc.sa.sa, &s_len); | |
| 1060 | if (n <= 0) { | |
| 1061 | DBG(("%p recvfrom: %s", ls, strerror(errno))); | |
| 1062 | } else { | |
| 1063 | nc.mgr = ls->mgr; | |
| 1064 | nc.recv_iobuf.buf = buf; | |
| 1065 | nc.recv_iobuf.len = nc.recv_iobuf.size = n; | |
| 1066 | nc.sock = ls->sock; | |
| 1067 | nc.callback = ls->callback; | |
| 1068 | nc.user_data = ls->user_data; | |
| 1069 | nc.proto_data = ls->proto_data; | |
| 1070 | nc.mgr = ls->mgr; | |
| 1071 | nc.listener = ls; | |
| 1072 | nc.flags = NSF_UDP; | |
| 1073 | DBG(("%p %d bytes received", ls, n)); | |
| 1074 | ns_call(&nc, NS_RECV, &n); | |
| 1075 | } | |
| 1076 | } | |
| 1077 | ||
| 1078 | static void ns_add_to_set(sock_t sock, fd_set *set, sock_t *max_fd) { | |
| 1079 | if ( (sock != INVALID_SOCKET) && (sock < FD_SETSIZE) ) { | |
| 1080 | FD_SET(sock, set); | |
| 1081 | if (*max_fd == INVALID_SOCKET || sock > *max_fd) { | |
| 1082 | *max_fd = sock; | |
| 1083 | } | |
| 1084 | } | |
| 1085 | } | |
| 1086 | ||
| 1087 | time_t ns_mgr_poll(struct ns_mgr *mgr, int milli) { | |
| 1088 | struct ns_connection *conn, *tmp_conn; | |
| 1089 | struct timeval tv; | |
| 1090 | fd_set read_set, write_set; | |
| 1091 | sock_t max_fd = INVALID_SOCKET; | |
| 1092 | time_t current_time = time(NULL); | |
| 1093 | ||
| 1094 | FD_ZERO(&read_set); | |
| 1095 | FD_ZERO(&write_set); | |
| 1096 | ns_add_to_set(mgr->ctl[1], &read_set, &max_fd); | |
| 1097 | ||
| 1098 | for (conn = mgr->active_connections; conn != NULL; conn = tmp_conn) { | |
| 1099 | tmp_conn = conn->next; | |
| 1100 | if (!(conn->flags & (NSF_LISTENING | NSF_CONNECTING))) { | |
| 1101 | ns_call(conn, NS_POLL, ¤t_time); | |
| 1102 | } | |
| 1103 | if (conn->flags & NSF_CLOSE_IMMEDIATELY) { | |
| 1104 | ns_close_conn(conn); | |
| 1105 | } else { | |
| 1106 | if (!(conn->flags & NSF_WANT_WRITE)) { | |
| 1107 | //DBG(("%p read_set", conn)); | |
| 1108 | ns_add_to_set(conn->sock, &read_set, &max_fd); | |
| 1109 | } | |
| 1110 | if (((conn->flags & NSF_CONNECTING) && !(conn->flags & NSF_WANT_READ)) || | |
| 1111 | (conn->send_iobuf.len > 0 && !(conn->flags & NSF_CONNECTING) && | |
| 1112 | !(conn->flags & NSF_BUFFER_BUT_DONT_SEND))) { | |
| 1113 | //DBG(("%p write_set", conn)); | |
| 1114 | ns_add_to_set(conn->sock, &write_set, &max_fd); | |
| 1115 | } | |
| 1116 | } | |
| 1117 | } | |
| 1118 | ||
| 1119 | tv.tv_sec = milli / 1000; | |
| 1120 | tv.tv_usec = (milli % 1000) * 1000; | |
| 1121 | ||
| 1122 | if (select((int) max_fd + 1, &read_set, &write_set, NULL, &tv) < 0) { | |
| 1123 | return 0; | |
| 1124 | } else { | |
| 1125 | // select() might have been waiting for a long time, reset current_time | |
| 1126 | // now to prevent last_io_time being set to the past. | |
| 1127 | current_time = time(NULL); | |
| 1128 | ||
| 1129 | // Read wakeup messages | |
| 1130 | if (mgr->ctl[1] != INVALID_SOCKET && | |
| 1131 | FD_ISSET(mgr->ctl[1], &read_set)) { | |
| 1132 | struct ctl_msg ctl_msg; | |
| 1133 | int len = (int) recv(mgr->ctl[1], (char *) &ctl_msg, sizeof(ctl_msg), 0); | |
| 1134 | send(mgr->ctl[1], ctl_msg.message, 1, 0); | |
| 1135 | if (len >= (int) sizeof(ctl_msg.callback) && ctl_msg.callback != NULL) { | |
| 1136 | struct ns_connection *c; | |
| 1137 | for (c = ns_next(mgr, NULL); c != NULL; c = ns_next(mgr, c)) { | |
| 1138 | ctl_msg.callback(c, NS_POLL, ctl_msg.message); | |
| 1139 | } | |
| 1140 | } | |
| 1141 | } | |
| 1142 | ||
| 1143 | for (conn = mgr->active_connections; conn != NULL; conn = tmp_conn) { | |
| 1144 | tmp_conn = conn->next; | |
| 1145 | if (FD_ISSET(conn->sock, &read_set)) { | |
| 1146 | if (conn->flags & NSF_LISTENING) { | |
| 1147 | if (conn->flags & NSF_UDP) { | |
| 1148 | ns_handle_udp(conn); | |
| 1149 | } else { | |
| 1150 | // We're not looping here, and accepting just one connection at | |
| 1151 | // a time. The reason is that eCos does not respect non-blocking | |
| 1152 | // flag on a listening socket and hangs in a loop. | |
| 1153 | accept_conn(conn); | |
| 1154 | } | |
| 1155 | } else { | |
| 1156 | conn->last_io_time = current_time; | |
| 1157 | ns_read_from_socket(conn); | |
| 1158 | } | |
| 1159 | } | |
| 1160 | ||
| 1161 | if (FD_ISSET(conn->sock, &write_set)) { | |
| 1162 | if (conn->flags & NSF_CONNECTING) { | |
| 1163 | ns_read_from_socket(conn); | |
| 1164 | } else if (!(conn->flags & NSF_BUFFER_BUT_DONT_SEND)) { | |
| 1165 | conn->last_io_time = current_time; | |
| 1166 | ns_write_to_socket(conn); | |
| 1167 | } | |
| 1168 | } | |
| 1169 | } | |
| 1170 | } | |
| 1171 | ||
| 1172 | for (conn = mgr->active_connections; conn != NULL; conn = tmp_conn) { | |
| 1173 | tmp_conn = conn->next; | |
| 1174 | if ((conn->flags & NSF_CLOSE_IMMEDIATELY) || | |
| 1175 | (conn->send_iobuf.len == 0 && | |
| 1176 | (conn->flags & NSF_FINISHED_SENDING_DATA))) { | |
| 1177 | ns_close_conn(conn); | |
| 1178 | } | |
| 1179 | } | |
| 1180 | ||
| 1181 | return current_time; | |
| 1182 | } | |
| 1183 | ||
| 1184 | struct ns_connection *ns_connect(struct ns_mgr *mgr, const char *address, | |
| 1185 | ns_callback_t callback, void *user_data) { | |
| 1186 | sock_t sock = INVALID_SOCKET; | |
| 1187 | struct ns_connection *nc = NULL; | |
| 1188 | union socket_address sa; | |
| 1189 | char cert[100], ca_cert[100]; | |
| 1190 | int rc, use_ssl, proto; | |
| 1191 | ||
| 1192 | ns_parse_address(address, &sa, &proto, &use_ssl, cert, ca_cert); | |
| 1193 | if ((sock = socket(AF_INET, proto, 0)) == INVALID_SOCKET) { | |
| 1194 | return NULL; | |
| 1195 | } | |
| 1196 | ns_set_non_blocking_mode(sock); | |
| 1197 | rc = (proto == SOCK_DGRAM) ? 0 : connect(sock, &sa.sa, sizeof(sa.sin)); | |
| 1198 | ||
| 1199 | if (rc != 0 && ns_is_error(rc)) { | |
| 1200 | closesocket(sock); | |
| 1201 | return NULL; | |
| 1202 | } else if ((nc = ns_add_sock(mgr, sock, callback, user_data)) == NULL) { | |
| 1203 | closesocket(sock); | |
| 1204 | return NULL; | |
| 1205 | } | |
| 1206 | ||
| 1207 | nc->sa = sa; // Important, cause UDP conns will use sendto() | |
| 1208 | nc->flags = (proto == SOCK_DGRAM) ? NSF_UDP : NSF_CONNECTING; | |
| 1209 | ||
| 1210 | #ifdef NS_ENABLE_SSL | |
| 1211 | if (use_ssl) { | |
| 1212 | if ((nc->ssl_ctx = SSL_CTX_new(SSLv23_client_method())) == NULL || | |
| 1213 | ns_use_cert(nc->ssl_ctx, cert) != 0 || | |
| 1214 | ns_use_ca_cert(nc->ssl_ctx, ca_cert) != 0 || | |
| 1215 | (nc->ssl = SSL_new(nc->ssl_ctx)) == NULL) { | |
| 1216 | ns_close_conn(nc); | |
| 1217 | return NULL; | |
| 1218 | } else { | |
| 1219 | SSL_set_fd(nc->ssl, sock); | |
| 1220 | } | |
| 1221 | } | |
| 1222 | #endif | |
| 1223 | ||
| 1224 | return nc; | |
| 1225 | } | |
| 1226 | ||
| 1227 | struct ns_connection *ns_add_sock(struct ns_mgr *s, sock_t sock, | |
| 1228 | ns_callback_t callback, void *user_data) { | |
| 1229 | struct ns_connection *conn; | |
| 1230 | if ((conn = (struct ns_connection *) NS_MALLOC(sizeof(*conn))) != NULL) { | |
| 1231 | memset(conn, 0, sizeof(*conn)); | |
| 1232 | ns_set_non_blocking_mode(sock); | |
| 1233 | ns_set_close_on_exec(sock); | |
| 1234 | conn->sock = sock; | |
| 1235 | conn->user_data = user_data; | |
| 1236 | conn->callback = callback; | |
| 1237 | conn->mgr = s; | |
| 1238 | conn->last_io_time = time(NULL); | |
| 1239 | ns_add_conn(s, conn); | |
| 1240 | DBG(("%p %d", conn, sock)); | |
| 1241 | } | |
| 1242 | return conn; | |
| 1243 | } | |
| 1244 | ||
| 1245 | struct ns_connection *ns_next(struct ns_mgr *s, struct ns_connection *conn) { | |
| 1246 | return conn == NULL ? s->active_connections : conn->next; | |
| 1247 | } | |
| 1248 | ||
| 1249 | void ns_broadcast(struct ns_mgr *mgr, ns_callback_t cb,void *data, size_t len) { | |
| 1250 | struct ctl_msg ctl_msg; | |
| 1251 | if (mgr->ctl[0] != INVALID_SOCKET && data != NULL && | |
| 1252 | len < sizeof(ctl_msg.message)) { | |
| 1253 | ctl_msg.callback = cb; | |
| 1254 | memcpy(ctl_msg.message, data, len); | |
| 1255 | send(mgr->ctl[0], (char *) &ctl_msg, | |
| 1256 | offsetof(struct ctl_msg, message) + len, 0); | |
| 1257 | recv(mgr->ctl[0], (char *) &len, 1, 0); | |
| 1258 | } | |
| 1259 | } | |
| 1260 | ||
| 1261 | void ns_mgr_init(struct ns_mgr *s, void *user_data) { | |
| 1262 | memset(s, 0, sizeof(*s)); | |
| 1263 | s->ctl[0] = s->ctl[1] = INVALID_SOCKET; | |
| 1264 | s->user_data = user_data; | |
| 1265 | ||
| 1266 | #ifdef _WIN32 | |
| 1267 | { WSADATA data; WSAStartup(MAKEWORD(2, 2), &data); } | |
| 1268 | #else | |
| 1269 | // Ignore SIGPIPE signal, so if client cancels the request, it | |
| 1270 | // won't kill the whole process. | |
| 1271 | signal(SIGPIPE, SIG_IGN); | |
| 1272 | #endif | |
| 1273 | ||
| 1274 | #ifndef NS_DISABLE_SOCKETPAIR | |
| 1275 | { | |
| 1276 | int attempts = 0, max_attempts = NS_MAX_SOCKETPAIR_ATTEMPTS; | |
| 1277 | do { | |
| 1278 | ns_socketpair2(s->ctl, SOCK_DGRAM); | |
| 1279 | } while (s->ctl[0] == INVALID_SOCKET && ++attempts < max_attempts); | |
| 1280 | } | |
| 1281 | #endif | |
| 1282 | ||
| 1283 | #ifdef NS_ENABLE_SSL | |
| 1284 | {static int init_done; if (!init_done) { SSL_library_init(); init_done++; }} | |
| 1285 | #endif | |
| 1286 | } | |
| 1287 | ||
| 1288 | void ns_mgr_free(struct ns_mgr *s) { | |
| 1289 | struct ns_connection *conn, *tmp_conn; | |
| 1290 | ||
| 1291 | DBG(("%p", s)); | |
| 1292 | if (s == NULL) return; | |
| 1293 | // Do one last poll, see https://github.com/cesanta/mongoose/issues/286 | |
| 1294 | ns_mgr_poll(s, 0); | |
| 1295 | ||
| 1296 | if (s->ctl[0] != INVALID_SOCKET) closesocket(s->ctl[0]); | |
| 1297 | if (s->ctl[1] != INVALID_SOCKET) closesocket(s->ctl[1]); | |
| 1298 | s->ctl[0] = s->ctl[1] = INVALID_SOCKET; | |
| 1299 | ||
| 1300 | for (conn = s->active_connections; conn != NULL; conn = tmp_conn) { | |
| 1301 | tmp_conn = conn->next; | |
| 1302 | ns_close_conn(conn); | |
| 1303 | } | |
| 1304 | } | |
| 1305 | // net_skeleton end | |
| 1306 | #endif // NOEMBED_NET_SKELETON | |
| 1307 | ||
| 1308 | #include <ctype.h> | |
| 1309 | ||
| 1310 | #ifdef _WIN32 //////////////// Windows specific defines and includes | |
| 1311 | #include <io.h> // For _lseeki64 | |
| 1312 | #include <direct.h> // For _mkdir | |
| 1313 | #ifndef S_ISDIR | |
| 1314 | #define S_ISDIR(x) ((x) & _S_IFDIR) | |
| 1315 | #endif | |
| 1316 | #ifdef stat | |
| 1317 | #undef stat | |
| 1318 | #endif | |
| 1319 | #ifdef lseek | |
| 1320 | #undef lseek | |
| 1321 | #endif | |
| 1322 | #ifdef popen | |
| 1323 | #undef popen | |
| 1324 | #endif | |
| 1325 | #ifdef pclose | |
| 1326 | #undef pclose | |
| 1327 | #endif | |
| 1328 | #define stat(x, y) mg_stat((x), (y)) | |
| 1329 | #define fopen(x, y) mg_fopen((x), (y)) | |
| 1330 | #define open(x, y, z) mg_open((x), (y), (z)) | |
| 1331 | #define close(x) _close(x) | |
| 1332 | #define fileno(x) _fileno(x) | |
| 1333 | #define lseek(x, y, z) _lseeki64((x), (y), (z)) | |
| 1334 | #define read(x, y, z) _read((x), (y), (z)) | |
| 1335 | #define write(x, y, z) _write((x), (y), (z)) | |
| 1336 | #define popen(x, y) _popen((x), (y)) | |
| 1337 | #define pclose(x) _pclose(x) | |
| 1338 | #define mkdir(x, y) _mkdir(x) | |
| 1339 | #define rmdir(x) _rmdir(x) | |
| 1340 | #define strdup(x) _strdup(x) | |
| 1341 | #ifndef __func__ | |
| 1342 | #define STRX(x) #x | |
| 1343 | #define STR(x) STRX(x) | |
| 1344 | #define __func__ __FILE__ ":" STR(__LINE__) | |
| 1345 | #endif | |
| 1346 | // find proper defines for this for VS and other compilers | |
| 1347 | #if defined(__USE_MINGW_ANSI_STDIO) | |
| 1348 | #define INT64_FMT "lld" | |
| 1349 | #else | |
| 1350 | #define INT64_FMT "I64d" | |
| 1351 | #endif | |
| 1352 | #define flockfile(x) ((void) (x)) | |
| 1353 | #define funlockfile(x) ((void) (x)) | |
| 1354 | typedef struct _stati64 file_stat_t; | |
| 1355 | typedef HANDLE process_id_t; | |
| 1356 | ||
| 1357 | #else ////////////// UNIX specific defines and includes | |
| 1358 | ||
| 1359 | #if !defined(MONGOOSE_NO_FILESYSTEM) &&\ | |
| 1360 | (!defined(MONGOOSE_NO_DAV) || !defined(MONGOOSE_NO_DIRECTORY_LISTING)) | |
| 1361 | #include <dirent.h> | |
| 1362 | #endif | |
| 1363 | #if !defined(MONGOOSE_NO_FILESYSTEM) && !defined(MONGOOSE_NO_DL) | |
| 1364 | #include <dlfcn.h> | |
| 1365 | #endif | |
| 1366 | #include <inttypes.h> | |
| 1367 | #include <pwd.h> | |
| 1368 | #if !defined(O_BINARY) | |
| 1369 | #define O_BINARY 0 | |
| 1370 | #endif | |
| 1371 | #define INT64_FMT PRId64 | |
| 1372 | typedef struct stat file_stat_t; | |
| 1373 | typedef pid_t process_id_t; | |
| 1374 | #endif //////// End of platform-specific defines and includes | |
| 1375 | ||
| 1376 | #include "mongoose.h" | |
| 1377 | ||
| 1378 | #define MAX_REQUEST_SIZE 16384 | |
| 1379 | #define IOBUF_SIZE 8192 | |
| 1380 | #define MAX_PATH_SIZE 8192 | |
| 1381 | #define DEFAULT_CGI_PATTERN "**.cgi$|**.pl$|**.php$" | |
| 1382 | #define CGI_ENVIRONMENT_SIZE 8192 | |
| 1383 | #define MAX_CGI_ENVIR_VARS 64 | |
| 1384 | #define ENV_EXPORT_TO_CGI "MONGOOSE_CGI" | |
| 1385 | #define PASSWORDS_FILE_NAME ".htpasswd" | |
| 1386 | ||
| 1387 | #ifndef MONGOOSE_USE_WEBSOCKET_PING_INTERVAL | |
| 1388 | #define MONGOOSE_USE_WEBSOCKET_PING_INTERVAL 5 | |
| 1389 | #endif | |
| 1390 | ||
| 1391 | // Extra HTTP headers to send in every static file reply | |
| 1392 | #if !defined(MONGOOSE_USE_EXTRA_HTTP_HEADERS) | |
| 1393 | #define MONGOOSE_USE_EXTRA_HTTP_HEADERS "" | |
| 1394 | #endif | |
| 1395 | ||
| 1396 | #ifndef MONGOOSE_POST_SIZE_LIMIT | |
| 1397 | #define MONGOOSE_POST_SIZE_LIMIT 0 | |
| 1398 | #endif | |
| 1399 | ||
| 1400 | #ifndef MONGOOSE_IDLE_TIMEOUT_SECONDS | |
| 1401 | #define MONGOOSE_IDLE_TIMEOUT_SECONDS 300 | |
| 1402 | #endif | |
| 1403 | ||
| 1404 | #if defined(NS_DISABLE_SOCKETPAIR) && !defined(MONGOOSE_NO_CGI) | |
| 1405 | #define MONGOOSE_NO_CGI | |
| 1406 | #endif | |
| 1407 | ||
| 1408 | #ifdef MONGOOSE_NO_FILESYSTEM | |
| 1409 | #define MONGOOSE_NO_AUTH | |
| 1410 | #if !defined(MONGOOSE_NO_CGI) | |
| 1411 | #define MONGOOSE_NO_CGI | |
| 1412 | #endif | |
| 1413 | #define MONGOOSE_NO_DAV | |
| 1414 | #define MONGOOSE_NO_DIRECTORY_LISTING | |
| 1415 | #define MONGOOSE_NO_LOGGING | |
| 1416 | #define MONGOOSE_NO_SSI | |
| 1417 | #define MONGOOSE_NO_DL | |
| 1418 | #endif | |
| 1419 | ||
| 1420 | struct vec { | |
| 1421 | const char *ptr; | |
| 1422 | size_t len; | |
| 1423 | }; | |
| 1424 | ||
| 1425 | // For directory listing and WevDAV support | |
| 1426 | struct dir_entry { | |
| 1427 | struct connection *conn; | |
| 1428 | char *file_name; | |
| 1429 | file_stat_t st; | |
| 1430 | }; | |
| 1431 | ||
| 1432 | // NOTE(lsm): this enum should be in sync with the config_options. | |
| 1433 | enum { | |
| 1434 | ACCESS_CONTROL_LIST, | |
| 1435 | #ifndef MONGOOSE_NO_FILESYSTEM | |
| 1436 | ACCESS_LOG_FILE, | |
| 1437 | #ifndef MONGOOSE_NO_AUTH | |
| 1438 | AUTH_DOMAIN, | |
| 1439 | #endif | |
| 1440 | #ifndef MONGOOSE_NO_CGI | |
| 1441 | CGI_INTERPRETER, | |
| 1442 | CGI_PATTERN, | |
| 1443 | #endif | |
| 1444 | DAV_AUTH_FILE, | |
| 1445 | DAV_ROOT, | |
| 1446 | DOCUMENT_ROOT, | |
| 1447 | #ifndef MONGOOSE_NO_DIRECTORY_LISTING | |
| 1448 | ENABLE_DIRECTORY_LISTING, | |
| 1449 | #endif | |
| 1450 | #endif | |
| 1451 | ENABLE_PROXY, | |
| 1452 | EXTRA_MIME_TYPES, | |
| 1453 | #if !defined(MONGOOSE_NO_FILESYSTEM) && !defined(MONGOOSE_NO_AUTH) | |
| 1454 | GLOBAL_AUTH_FILE, | |
| 1455 | #endif | |
| 1456 | #ifndef MONGOOSE_NO_FILESYSTEM | |
| 1457 | HIDE_FILES_PATTERN, | |
| 1458 | HEXDUMP_FILE, | |
| 1459 | INDEX_FILES, | |
| 1460 | #endif | |
| 1461 | LISTENING_PORT, | |
| 1462 | #ifndef _WIN32 | |
| 1463 | RUN_AS_USER, | |
| 1464 | #endif | |
| 1465 | #ifndef MONGOOSE_NO_SSI | |
| 1466 | SSI_PATTERN, | |
| 1467 | #endif | |
| 1468 | URL_REWRITES, | |
| 1469 | NUM_OPTIONS | |
| 1470 | }; | |
| 1471 | ||
| 1472 | static const char *static_config_options[] = { | |
| 1473 | "access_control_list", NULL, | |
| 1474 | #ifndef MONGOOSE_NO_FILESYSTEM | |
| 1475 | "access_log_file", NULL, | |
| 1476 | #ifndef MONGOOSE_NO_AUTH | |
| 1477 | "auth_domain", "mydomain.com", | |
| 1478 | #endif | |
| 1479 | #ifndef MONGOOSE_NO_CGI | |
| 1480 | "cgi_interpreter", NULL, | |
| 1481 | "cgi_pattern", DEFAULT_CGI_PATTERN, | |
| 1482 | #endif | |
| 1483 | "dav_auth_file", NULL, | |
| 1484 | "dav_root", NULL, | |
| 1485 | "document_root", NULL, | |
| 1486 | #ifndef MONGOOSE_NO_DIRECTORY_LISTING | |
| 1487 | "enable_directory_listing", "yes", | |
| 1488 | #endif | |
| 1489 | #endif | |
| 1490 | "enable_proxy", NULL, | |
| 1491 | "extra_mime_types", NULL, | |
| 1492 | #if !defined(MONGOOSE_NO_FILESYSTEM) && !defined(MONGOOSE_NO_AUTH) | |
| 1493 | "global_auth_file", NULL, | |
| 1494 | #endif | |
| 1495 | #ifndef MONGOOSE_NO_FILESYSTEM | |
| 1496 | "hide_files_patterns", NULL, | |
| 1497 | "hexdump_file", NULL, | |
| 1498 | "index_files","index.html,index.htm,index.shtml,index.cgi,index.php", | |
| 1499 | #endif | |
| 1500 | "listening_port", NULL, | |
| 1501 | #ifndef _WIN32 | |
| 1502 | "run_as_user", NULL, | |
| 1503 | #endif | |
| 1504 | #ifndef MONGOOSE_NO_SSI | |
| 1505 | "ssi_pattern", "**.shtml$|**.shtm$", | |
| 1506 | #endif | |
| 1507 | "url_rewrites", NULL, | |
| 1508 | NULL | |
| 1509 | }; | |
| 1510 | ||
| 1511 | struct mg_server { | |
| 1512 | struct ns_mgr ns_mgr; | |
| 1513 | union socket_address lsa; // Listening socket address | |
| 1514 | mg_handler_t event_handler; | |
| 1515 | char *config_options[NUM_OPTIONS]; | |
| 1516 | }; | |
| 1517 | ||
| 1518 | // Local endpoint representation | |
| 1519 | union endpoint { | |
| 1520 | int fd; // Opened regular local file | |
| 1521 | struct ns_connection *nc; // CGI or proxy->target connection | |
| 1522 | }; | |
| 1523 | ||
| 1524 | enum endpoint_type { | |
| 1525 | EP_NONE, EP_FILE, EP_CGI, EP_USER, EP_PUT, EP_CLIENT, EP_PROXY | |
| 1526 | }; | |
| 1527 | ||
| 1528 | #define MG_HEADERS_SENT NSF_USER_1 | |
| 1529 | #define MG_USING_CHUNKED_API NSF_USER_2 | |
| 1530 | #define MG_CGI_CONN NSF_USER_3 | |
| 1531 | #define MG_PROXY_CONN NSF_USER_4 | |
| 1532 | #define MG_PROXY_DONT_PARSE NSF_USER_5 | |
| 1533 | ||
| 1534 | struct connection { | |
| 1535 | struct ns_connection *ns_conn; // NOTE(lsm): main.c depends on this order | |
| 1536 | struct mg_connection mg_conn; | |
| 1537 | struct mg_server *server; | |
| 1538 | union endpoint endpoint; | |
| 1539 | enum endpoint_type endpoint_type; | |
| 1540 | char *path_info; | |
| 1541 | char *request; | |
| 1542 | int64_t num_bytes_recv; // Total number of bytes received | |
| 1543 | int64_t cl; // Reply content length, for Range support | |
| 1544 | ssize_t request_len; // Request length, including last \r\n after last header | |
| 1545 | }; | |
| 1546 | ||
| 1547 | #define MG_CONN_2_CONN(c) ((struct connection *) ((char *) (c) - \ | |
| 1548 | offsetof(struct connection, mg_conn))) | |
| 1549 | ||
| 1550 | static void open_local_endpoint(struct connection *conn, int skip_user); | |
| 1551 | static void close_local_endpoint(struct connection *conn); | |
| 1552 | static void mg_ev_handler(struct ns_connection *nc, int ev, void *p); | |
| 1553 | ||
| 1554 | static const struct { | |
| 1555 | const char *extension; | |
| 1556 | size_t ext_len; | |
| 1557 | const char *mime_type; | |
| 1558 | } static_builtin_mime_types[] = { | |
| 1559 | {".html", 5, "text/html"}, | |
| 1560 | {".htm", 4, "text/html"}, | |
| 1561 | {".shtm", 5, "text/html"}, | |
| 1562 | {".shtml", 6, "text/html"}, | |
| 1563 | {".css", 4, "text/css"}, | |
| 1564 | {".js", 3, "application/javascript"}, | |
| 1565 | {".ico", 4, "image/x-icon"}, | |
| 1566 | {".gif", 4, "image/gif"}, | |
| 1567 | {".jpg", 4, "image/jpeg"}, | |
| 1568 | {".jpeg", 5, "image/jpeg"}, | |
| 1569 | {".png", 4, "image/png"}, | |
| 1570 | {".svg", 4, "image/svg+xml"}, | |
| 1571 | {".txt", 4, "text/plain"}, | |
| 1572 | {".torrent", 8, "application/x-bittorrent"}, | |
| 1573 | {".wav", 4, "audio/x-wav"}, | |
| 1574 | {".mp3", 4, "audio/x-mp3"}, | |
| 1575 | {".mid", 4, "audio/mid"}, | |
| 1576 | {".m3u", 4, "audio/x-mpegurl"}, | |
| 1577 | {".ogg", 4, "application/ogg"}, | |
| 1578 | {".ram", 4, "audio/x-pn-realaudio"}, | |
| 1579 | {".xml", 4, "text/xml"}, | |
| 1580 | {".json", 5, "application/json"}, | |
| 1581 | {".xslt", 5, "application/xml"}, | |
| 1582 | {".xsl", 4, "application/xml"}, | |
| 1583 | {".ra", 3, "audio/x-pn-realaudio"}, | |
| 1584 | {".doc", 4, "application/msword"}, | |
| 1585 | {".exe", 4, "application/octet-stream"}, | |
| 1586 | {".zip", 4, "application/x-zip-compressed"}, | |
| 1587 | {".xls", 4, "application/excel"}, | |
| 1588 | {".tgz", 4, "application/x-tar-gz"}, | |
| 1589 | {".tar", 4, "application/x-tar"}, | |
| 1590 | {".gz", 3, "application/x-gunzip"}, | |
| 1591 | {".arj", 4, "application/x-arj-compressed"}, | |
| 1592 | {".rar", 4, "application/x-rar-compressed"}, | |
| 1593 | {".rtf", 4, "application/rtf"}, | |
| 1594 | {".pdf", 4, "application/pdf"}, | |
| 1595 | {".swf", 4, "application/x-shockwave-flash"}, | |
| 1596 | {".mpg", 4, "video/mpeg"}, | |
| 1597 | {".webm", 5, "video/webm"}, | |
| 1598 | {".mpeg", 5, "video/mpeg"}, | |
| 1599 | {".mov", 4, "video/quicktime"}, | |
| 1600 | {".mp4", 4, "video/mp4"}, | |
| 1601 | {".m4v", 4, "video/x-m4v"}, | |
| 1602 | {".asf", 4, "video/x-ms-asf"}, | |
| 1603 | {".avi", 4, "video/x-msvideo"}, | |
| 1604 | {".bmp", 4, "image/bmp"}, | |
| 1605 | {".ttf", 4, "application/x-font-ttf"}, | |
| 1606 | {NULL, 0, NULL} | |
| 1607 | }; | |
| 1608 | ||
| 1609 | #ifdef MONGOOSE_ENABLE_THREADS | |
| 1610 | void *mg_start_thread(void *(*f)(void *), void *p) { | |
| 1611 | return ns_start_thread(f, p); | |
| 1612 | } | |
| 1613 | #endif // MONGOOSE_ENABLE_THREADS | |
| 1614 | ||
| 1615 | #ifndef MONGOOSE_NO_MMAP | |
| 1616 | #ifdef _WIN32 | |
| 1617 | static void *mmap(void *addr, int64_t len, int prot, int flags, int fd, | |
| 1618 | int offset) { | |
| 1619 | HANDLE fh = (HANDLE) _get_osfhandle(fd); | |
| 1620 | HANDLE mh = CreateFileMapping(fh, 0, PAGE_READONLY, 0, 0, 0); | |
| 1621 | void *p = MapViewOfFile(mh, FILE_MAP_READ, 0, 0, (size_t) len); | |
| 1622 | CloseHandle(mh); | |
| 1623 | return p; | |
| 1624 | } | |
| 1625 | #define munmap(x, y) UnmapViewOfFile(x) | |
| 1626 | #define MAP_FAILED NULL | |
| 1627 | #define MAP_PRIVATE 0 | |
| 1628 | #define PROT_READ 0 | |
| 1629 | #elif defined(__OS2__) | |
| 1630 | static void *mmap(void *addr, int64_t len, int prot, int flags, int fd, | |
| 1631 | int offset) { | |
| 1632 | void *p; | |
| 1633 | ||
| 1634 | int pos = lseek( fd, 0, SEEK_CUR ); /* Get a current position */ | |
| 1635 | ||
| 1636 | if (pos == -1) | |
| 1637 | return NULL; | |
| 1638 | ||
| 1639 | /* Seek to offset offset */ | |
| 1640 | if (lseek( fd, offset, SEEK_SET) == -1) | |
| 1641 | return NULL; | |
| 1642 | ||
| 1643 | p = malloc(len); | |
| 1644 | ||
| 1645 | /* Read in a file */ | |
| 1646 | if (!p || read(fd, p, len) == -1) { | |
| 1647 | free(p); | |
| 1648 | p = NULL; | |
| 1649 | } | |
| 1650 | ||
| 1651 | /* Restore the position */ | |
| 1652 | lseek(fd, pos, SEEK_SET); | |
| 1653 | ||
| 1654 | return p; | |
| 1655 | } | |
| 1656 | #define munmap(x, y) free(x) | |
| 1657 | #define MAP_FAILED NULL | |
| 1658 | #define MAP_PRIVATE 0 | |
| 1659 | #define PROT_READ 0 | |
| 1660 | #else | |
| 1661 | #include <sys/mman.h> | |
| 1662 | #endif | |
| 1663 | ||
| 1664 | void *mg_mmap(FILE *fp, size_t size) { | |
| 1665 | void *p = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fileno(fp), 0); | |
| 1666 | return p == MAP_FAILED ? NULL : p; | |
| 1667 | } | |
| 1668 | ||
| 1669 | void mg_munmap(void *p, size_t size) { | |
| 1670 | munmap(p, size); | |
| 1671 | } | |
| 1672 | #endif // MONGOOSE_NO_MMAP | |
| 1673 | ||
| 1674 | #if defined(_WIN32) && !defined(MONGOOSE_NO_FILESYSTEM) | |
| 1675 | // Encode 'path' which is assumed UTF-8 string, into UNICODE string. | |
| 1676 | // wbuf and wbuf_len is a target buffer and its length. | |
| 1677 | static void to_wchar(const char *path, wchar_t *wbuf, size_t wbuf_len) { | |
| 1678 | char buf[MAX_PATH_SIZE * 2], buf2[MAX_PATH_SIZE * 2], *p; | |
| 1679 | ||
| 1680 | strncpy(buf, path, sizeof(buf)); | |
| 1681 | buf[sizeof(buf) - 1] = '\0'; | |
| 1682 | ||
| 1683 | // Trim trailing slashes. Leave backslash for paths like "X:\" | |
| 1684 | p = buf + strlen(buf) - 1; | |
| 1685 | while (p > buf && p[-1] != ':' && (p[0] == '\\' || p[0] == '/')) *p-- = '\0'; | |
| 1686 | ||
| 1687 | // Convert to Unicode and back. If doubly-converted string does not | |
| 1688 | // match the original, something is fishy, reject. | |
| 1689 | memset(wbuf, 0, wbuf_len * sizeof(wchar_t)); | |
| 1690 | MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, (int) wbuf_len); | |
| 1691 | WideCharToMultiByte(CP_UTF8, 0, wbuf, (int) wbuf_len, buf2, sizeof(buf2), | |
| 1692 | NULL, NULL); | |
| 1693 | if (strcmp(buf, buf2) != 0) { | |
| 1694 | wbuf[0] = L'\0'; | |
| 1695 | } | |
| 1696 | } | |
| 1697 | ||
| 1698 | static int mg_stat(const char *path, file_stat_t *st) { | |
| 1699 | wchar_t wpath[MAX_PATH_SIZE]; | |
| 1700 | to_wchar(path, wpath, ARRAY_SIZE(wpath)); | |
| 1701 | DBG(("[%ls] -> %d", wpath, _wstati64(wpath, st))); | |
| 1702 | return _wstati64(wpath, st); | |
| 1703 | } | |
| 1704 | ||
| 1705 | static FILE *mg_fopen(const char *path, const char *mode) { | |
| 1706 | wchar_t wpath[MAX_PATH_SIZE], wmode[10]; | |
| 1707 | to_wchar(path, wpath, ARRAY_SIZE(wpath)); | |
| 1708 | to_wchar(mode, wmode, ARRAY_SIZE(wmode)); | |
| 1709 | return _wfopen(wpath, wmode); | |
| 1710 | } | |
| 1711 | ||
| 1712 | static int mg_open(const char *path, int flag, int mode) { | |
| 1713 | wchar_t wpath[MAX_PATH_SIZE]; | |
| 1714 | to_wchar(path, wpath, ARRAY_SIZE(wpath)); | |
| 1715 | return _wopen(wpath, flag, mode); | |
| 1716 | } | |
| 1717 | #endif // _WIN32 && !MONGOOSE_NO_FILESYSTEM | |
| 1718 | ||
| 1719 | // A helper function for traversing a comma separated list of values. | |
| 1720 | // It returns a list pointer shifted to the next value, or NULL if the end | |
| 1721 | // of the list found. | |
| 1722 | // Value is stored in val vector. If value has form "x=y", then eq_val | |
| 1723 | // vector is initialized to point to the "y" part, and val vector length | |
| 1724 | // is adjusted to point only to "x". | |
| 1725 | static const char *next_option(const char *list, struct vec *val, | |
| 1726 | struct vec *eq_val) { | |
| 1727 | if (list == NULL || *list == '\0') { | |
| 1728 | // End of the list | |
| 1729 | list = NULL; | |
| 1730 | } else { | |
| 1731 | val->ptr = list; | |
| 1732 | if ((list = strchr(val->ptr, ',')) != NULL) { | |
| 1733 | // Comma found. Store length and shift the list ptr | |
| 1734 | val->len = list - val->ptr; | |
| 1735 | list++; | |
| 1736 | } else { | |
| 1737 | // This value is the last one | |
| 1738 | list = val->ptr + strlen(val->ptr); | |
| 1739 | val->len = list - val->ptr; | |
| 1740 | } | |
| 1741 | ||
| 1742 | if (eq_val != NULL) { | |
| 1743 | // Value has form "x=y", adjust pointers and lengths | |
| 1744 | // so that val points to "x", and eq_val points to "y". | |
| 1745 | eq_val->len = 0; | |
| 1746 | eq_val->ptr = (const char *) memchr(val->ptr, '=', val->len); | |
| 1747 | if (eq_val->ptr != NULL) { | |
| 1748 | eq_val->ptr++; // Skip over '=' character | |
| 1749 | eq_val->len = val->ptr + val->len - eq_val->ptr; | |
| 1750 | val->len = (eq_val->ptr - val->ptr) - 1; | |
| 1751 | } | |
| 1752 | } | |
| 1753 | } | |
| 1754 | ||
| 1755 | return list; | |
| 1756 | } | |
| 1757 | ||
| 1758 | // Like snprintf(), but never returns negative value, or a value | |
| 1759 | // that is larger than a supplied buffer. | |
| 1760 | static int mg_vsnprintf(char *buf, size_t buflen, const char *fmt, va_list ap) { | |
| 1761 | int n; | |
| 1762 | if (buflen < 1) return 0; | |
| 1763 | n = vsnprintf(buf, buflen, fmt, ap); | |
| 1764 | if (n < 0) { | |
| 1765 | n = 0; | |
| 1766 | } else if (n >= (int) buflen) { | |
| 1767 | n = (int) buflen - 1; | |
| 1768 | } | |
| 1769 | buf[n] = '\0'; | |
| 1770 | return n; | |
| 1771 | } | |
| 1772 | ||
| 1773 | static int mg_snprintf(char *buf, size_t buflen, const char *fmt, ...) { | |
| 1774 | va_list ap; | |
| 1775 | int n; | |
| 1776 | va_start(ap, fmt); | |
| 1777 | n = mg_vsnprintf(buf, buflen, fmt, ap); | |
| 1778 | va_end(ap); | |
| 1779 | return n; | |
| 1780 | } | |
| 1781 | ||
| 1782 | // Check whether full request is buffered. Return: | |
| 1783 | // -1 if request is malformed | |
| 1784 | // 0 if request is not yet fully buffered | |
| 1785 | // >0 actual request length, including last \r\n\r\n | |
| 1786 | static int get_request_len(const char *s, size_t buf_len) { | |
| 1787 | const unsigned char *buf = (unsigned char *) s; | |
| 1788 | size_t i; | |
| 1789 | ||
| 1790 | for (i = 0; i < buf_len; i++) { | |
| 1791 | // Control characters are not allowed but >=128 are. | |
| 1792 | // Abort scan as soon as one malformed character is found. | |
| 1793 | if (!isprint(buf[i]) && buf[i] != '\r' && buf[i] != '\n' && buf[i] < 128) { | |
| 1794 | return -1; | |
| 1795 | } else if (buf[i] == '\n' && i + 1 < buf_len && buf[i + 1] == '\n') { | |
| 1796 | return i + 2; | |
| 1797 | } else if (buf[i] == '\n' && i + 2 < buf_len && buf[i + 1] == '\r' && | |
| 1798 | buf[i + 2] == '\n') { | |
| 1799 | return i + 3; | |
| 1800 | } | |
| 1801 | } | |
| 1802 | ||
| 1803 | return 0; | |
| 1804 | } | |
| 1805 | ||
| 1806 | // Skip the characters until one of the delimiters characters found. | |
| 1807 | // 0-terminate resulting word. Skip the rest of the delimiters if any. | |
| 1808 | // Advance pointer to buffer to the next word. Return found 0-terminated word. | |
| 1809 | static char *skip(char **buf, const char *delimiters) { | |
| 1810 | char *p, *begin_word, *end_word, *end_delimiters; | |
| 1811 | ||
| 1812 | begin_word = *buf; | |
| 1813 | end_word = begin_word + strcspn(begin_word, delimiters); | |
| 1814 | end_delimiters = end_word + strspn(end_word, delimiters); | |
| 1815 | ||
| 1816 | for (p = end_word; p < end_delimiters; p++) { | |
| 1817 | *p = '\0'; | |
| 1818 | } | |
| 1819 | ||
| 1820 | *buf = end_delimiters; | |
| 1821 | ||
| 1822 | return begin_word; | |
| 1823 | } | |
| 1824 | ||
| 1825 | // Parse HTTP headers from the given buffer, advance buffer to the point | |
| 1826 | // where parsing stopped. | |
| 1827 | static void parse_http_headers(char **buf, struct mg_connection *ri) { | |
| 1828 | size_t i; | |
| 1829 | ||
| 1830 | for (i = 0; i < ARRAY_SIZE(ri->http_headers); i++) { | |
| 1831 | ri->http_headers[i].name = skip(buf, ": "); | |
| 1832 | ri->http_headers[i].value = skip(buf, "\r\n"); | |
| 1833 | if (ri->http_headers[i].name[0] == '\0') | |
| 1834 | break; | |
| 1835 | ri->num_headers = i + 1; | |
| 1836 | } | |
| 1837 | } | |
| 1838 | ||
| 1839 | static const char *status_code_to_str(int status_code) { | |
| 1840 | switch (status_code) { | |
| 1841 | ||
| 1842 | case 100: return "Continue"; | |
| 1843 | case 101: return "Switching Protocols"; | |
| 1844 | case 102: return "Processing"; | |
| 1845 | ||
| 1846 | case 200: return "OK"; | |
| 1847 | case 201: return "Created"; | |
| 1848 | case 202: return "Accepted"; | |
| 1849 | case 203: return "Non-Authoritative Information"; | |
| 1850 | case 204: return "No Content"; | |
| 1851 | case 205: return "Reset Content"; | |
| 1852 | case 206: return "Partial Content"; | |
| 1853 | case 207: return "Multi-Status"; | |
| 1854 | case 208: return "Already Reported"; | |
| 1855 | case 226: return "IM Used"; | |
| 1856 | ||
| 1857 | case 300: return "Multiple Choices"; | |
| 1858 | case 301: return "Moved Permanently"; | |
| 1859 | case 302: return "Found"; | |
| 1860 | case 303: return "See Other"; | |
| 1861 | case 304: return "Not Modified"; | |
| 1862 | case 305: return "Use Proxy"; | |
| 1863 | case 306: return "Switch Proxy"; | |
| 1864 | case 307: return "Temporary Redirect"; | |
| 1865 | case 308: return "Permanent Redirect"; | |
| 1866 | ||
| 1867 | case 400: return "Bad Request"; | |
| 1868 | case 401: return "Unauthorized"; | |
| 1869 | case 402: return "Payment Required"; | |
| 1870 | case 403: return "Forbidden"; | |
| 1871 | case 404: return "Not Found"; | |
| 1872 | case 405: return "Method Not Allowed"; | |
| 1873 | case 406: return "Not Acceptable"; | |
| 1874 | case 407: return "Proxy Authentication Required"; | |
| 1875 | case 408: return "Request Timeout"; | |
| 1876 | case 409: return "Conflict"; | |
| 1877 | case 410: return "Gone"; | |
| 1878 | case 411: return "Length Required"; | |
| 1879 | case 412: return "Precondition Failed"; | |
| 1880 | case 413: return "Payload Too Large"; | |
| 1881 | case 414: return "URI Too Long"; | |
| 1882 | case 415: return "Unsupported Media Type"; | |
| 1883 | case 416: return "Requested Range Not Satisfiable"; | |
| 1884 | case 417: return "Expectation Failed"; | |
| 1885 | case 418: return "I\'m a teapot"; | |
| 1886 | case 422: return "Unprocessable Entity"; | |
| 1887 | case 423: return "Locked"; | |
| 1888 | case 424: return "Failed Dependency"; | |
| 1889 | case 426: return "Upgrade Required"; | |
| 1890 | case 428: return "Precondition Required"; | |
| 1891 | case 429: return "Too Many Requests"; | |
| 1892 | case 431: return "Request Header Fields Too Large"; | |
| 1893 | case 451: return "Unavailable For Legal Reasons"; | |
| 1894 | ||
| 1895 | case 500: return "Internal Server Error"; | |
| 1896 | case 501: return "Not Implemented"; | |
| 1897 | case 502: return "Bad Gateway"; | |
| 1898 | case 503: return "Service Unavailable"; | |
| 1899 | case 504: return "Gateway Timeout"; | |
| 1900 | case 505: return "HTTP Version Not Supported"; | |
| 1901 | case 506: return "Variant Also Negotiates"; | |
| 1902 | case 507: return "Insufficient Storage"; | |
| 1903 | case 508: return "Loop Detected"; | |
| 1904 | case 510: return "Not Extended"; | |
| 1905 | case 511: return "Network Authentication Required"; | |
| 1906 | ||
| 1907 | default: return "Server Error"; | |
| 1908 | } | |
| 1909 | } | |
| 1910 | ||
| 1911 | static int call_user(struct connection *conn, enum mg_event ev) { | |
| 1912 | return conn != NULL && conn->server != NULL && | |
| 1913 | conn->server->event_handler != NULL ? | |
| 1914 | conn->server->event_handler(&conn->mg_conn, ev) : MG_FALSE; | |
| 1915 | } | |
| 1916 | ||
| 1917 | static void send_http_error(struct connection *conn, int code, | |
| 1918 | const char *fmt, ...) { | |
| 1919 | const char *message = status_code_to_str(code); | |
| 1920 | const char *rewrites = conn->server->config_options[URL_REWRITES]; | |
| 1921 | char headers[200], body[200]; | |
| 1922 | struct vec a, b; | |
| 1923 | va_list ap; | |
| 1924 | int body_len, headers_len, match_code; | |
| 1925 | ||
| 1926 | conn->mg_conn.status_code = code; | |
| 1927 | ||
| 1928 | // Invoke error handler if it is set | |
| 1929 | if (call_user(conn, MG_HTTP_ERROR) == MG_TRUE) { | |
| 1930 | close_local_endpoint(conn); | |
| 1931 | return; | |
| 1932 | } | |
| 1933 | ||
| 1934 | // Handle error code rewrites | |
| 1935 | while ((rewrites = next_option(rewrites, &a, &b)) != NULL) { | |
| 1936 | if ((match_code = atoi(a.ptr)) > 0 && match_code == code) { | |
| 1937 | struct mg_connection *c = &conn->mg_conn; | |
| 1938 | c->status_code = 302; | |
| 1939 | mg_printf(c, "HTTP/1.1 %d Moved\r\n" | |
| 1940 | "Location: %.*s?code=%d&orig_uri=%s&query_string=%s\r\n\r\n", | |
| 1941 | c->status_code, b.len, b.ptr, code, c->uri, | |
| 1942 | c->query_string == NULL ? "" : c->query_string); | |
| 1943 | close_local_endpoint(conn); | |
| 1944 | return; | |
| 1945 | } | |
| 1946 | } | |
| 1947 | ||
| 1948 | body_len = mg_snprintf(body, sizeof(body), "%d %s\n", code, message); | |
| 1949 | if (fmt != NULL) { | |
| 1950 | va_start(ap, fmt); | |
| 1951 | body_len += mg_vsnprintf(body + body_len, sizeof(body) - body_len, fmt, ap); | |
| 1952 | va_end(ap); | |
| 1953 | } | |
| 1954 | if ((code >= 300 && code <= 399) || code == 204) { | |
| 1955 | // 3xx errors do not have body | |
| 1956 | body_len = 0; | |
| 1957 | } | |
| 1958 | headers_len = mg_snprintf(headers, sizeof(headers), | |
| 1959 | "HTTP/1.1 %d %s\r\nContent-Length: %d\r\n" | |
| 1960 | "Content-Type: text/plain\r\n\r\n", | |
| 1961 | code, message, body_len); | |
| 1962 | ns_send(conn->ns_conn, headers, headers_len); | |
| 1963 | ns_send(conn->ns_conn, body, body_len); | |
| 1964 | close_local_endpoint(conn); // This will write to the log file | |
| 1965 | } | |
| 1966 | ||
| 1967 | static void write_chunk(struct connection *conn, const char *buf, int len) { | |
| 1968 | char chunk_size[50]; | |
| 1969 | int n = mg_snprintf(chunk_size, sizeof(chunk_size), "%X\r\n", len); | |
| 1970 | ns_send(conn->ns_conn, chunk_size, n); | |
| 1971 | ns_send(conn->ns_conn, buf, len); | |
| 1972 | ns_send(conn->ns_conn, "\r\n", 2); | |
| 1973 | } | |
| 1974 | ||
| 1975 | size_t mg_printf(struct mg_connection *conn, const char *fmt, ...) { | |
| 1976 | va_list ap; | |
| 1977 | int ret; | |
| 1978 | ||
| 1979 | va_start(ap, fmt); | |
| 1980 | ret = mg_vprintf(conn, fmt, ap); | |
| 1981 | va_end(ap); | |
| 1982 | ||
| 1983 | return ret; | |
| 1984 | } | |
| 1985 | ||
| 1986 | size_t mg_vprintf(struct mg_connection *conn, const char *fmt, va_list ap) { | |
| 1987 | struct connection *c = MG_CONN_2_CONN(conn); | |
| 1988 | ||
| 1989 | ns_vprintf(c->ns_conn, fmt, ap); | |
| 1990 | ||
| 1991 | return c->ns_conn->send_iobuf.len; | |
| 1992 | } | |
| 1993 | ||
| 1994 | static void ns_forward(struct ns_connection *from, struct ns_connection *to) { | |
| 1995 | DBG(("%p -> %p %lu bytes", from, to, (unsigned long)from->recv_iobuf.len)); | |
| 1996 | ns_send(to, from->recv_iobuf.buf, from->recv_iobuf.len); | |
| 1997 | iobuf_remove(&from->recv_iobuf, from->recv_iobuf.len); | |
| 1998 | } | |
| 1999 | ||
| 2000 | #ifndef MONGOOSE_NO_CGI | |
| 2001 | #ifdef _WIN32 | |
| 2002 | struct threadparam { | |
| 2003 | sock_t s; | |
| 2004 | HANDLE hPipe; | |
| 2005 | }; | |
| 2006 | ||
| 2007 | static int wait_until_ready(sock_t sock, int for_read) { | |
| 2008 | fd_set set; | |
| 2009 | if ( (sock == INVALID_SOCKET) || (sock >= FD_SETSIZE) ) | |
| 2010 | return 0; | |
| 2011 | FD_ZERO(&set); | |
| 2012 | FD_SET(sock, &set); | |
| 2013 | select(sock + 1, for_read ? &set : 0, for_read ? 0 : &set, 0, 0); | |
| 2014 | return 1; | |
| 2015 | } | |
| 2016 | ||
| 2017 | static void *push_to_stdin(void *arg) { | |
| 2018 | struct threadparam *tp = (struct threadparam *)arg; | |
| 2019 | int n, sent, stop = 0; | |
| 2020 | DWORD k; | |
| 2021 | char buf[IOBUF_SIZE]; | |
| 2022 | ||
| 2023 | while (!stop && wait_until_ready(tp->s, 1) && | |
| 2024 | (n = recv(tp->s, buf, sizeof(buf), 0)) > 0) { | |
| 2025 | if (n == -1 && GetLastError() == WSAEWOULDBLOCK) continue; | |
| 2026 | for (sent = 0; !stop && sent < n; sent += k) { | |
| 2027 | if (!WriteFile(tp->hPipe, buf + sent, n - sent, &k, 0)) stop = 1; | |
| 2028 | } | |
| 2029 | } | |
| 2030 | DBG(("%s", "FORWARDED EVERYTHING TO CGI")); | |
| 2031 | CloseHandle(tp->hPipe); | |
| 2032 | NS_FREE(tp); | |
| 2033 | _endthread(); | |
| 2034 | return NULL; | |
| 2035 | } | |
| 2036 | ||
| 2037 | static void *pull_from_stdout(void *arg) { | |
| 2038 | struct threadparam *tp = (struct threadparam *)arg; | |
| 2039 | int k = 0, stop = 0; | |
| 2040 | DWORD n, sent; | |
| 2041 | char buf[IOBUF_SIZE]; | |
| 2042 | ||
| 2043 | while (!stop && ReadFile(tp->hPipe, buf, sizeof(buf), &n, NULL)) { | |
| 2044 | for (sent = 0; !stop && sent < n; sent += k) { | |
| 2045 | if (wait_until_ready(tp->s, 0) && | |
| 2046 | (k = send(tp->s, buf + sent, n - sent, 0)) <= 0) stop = 1; | |
| 2047 | } | |
| 2048 | } | |
| 2049 | DBG(("%s", "EOF FROM CGI")); | |
| 2050 | CloseHandle(tp->hPipe); | |
| 2051 | shutdown(tp->s, 2); // Without this, IO thread may get truncated data | |
| 2052 | closesocket(tp->s); | |
| 2053 | NS_FREE(tp); | |
| 2054 | _endthread(); | |
| 2055 | return NULL; | |
| 2056 | } | |
| 2057 | ||
| 2058 | static void spawn_stdio_thread(sock_t sock, HANDLE hPipe, | |
| 2059 | void *(*func)(void *)) { | |
| 2060 | struct threadparam *tp = (struct threadparam *)NS_MALLOC(sizeof(*tp)); | |
| 2061 | if (tp != NULL) { | |
| 2062 | tp->s = sock; | |
| 2063 | tp->hPipe = hPipe; | |
| 2064 | mg_start_thread(func, tp); | |
| 2065 | } | |
| 2066 | } | |
| 2067 | ||
| 2068 | static void abs_path(const char *utf8_path, char *abs_path, size_t len) { | |
| 2069 | wchar_t buf[MAX_PATH_SIZE], buf2[MAX_PATH_SIZE]; | |
| 2070 | to_wchar(utf8_path, buf, ARRAY_SIZE(buf)); | |
| 2071 | GetFullPathNameW(buf, ARRAY_SIZE(buf2), buf2, NULL); | |
| 2072 | WideCharToMultiByte(CP_UTF8, 0, buf2, wcslen(buf2) + 1, abs_path, len, 0, 0); | |
| 2073 | } | |
| 2074 | ||
| 2075 | static process_id_t start_process(char *interp, const char *cmd, | |
| 2076 | const char *env, const char *envp[], | |
| 2077 | const char *dir, sock_t sock) { | |
| 2078 | STARTUPINFOW si; | |
| 2079 | PROCESS_INFORMATION pi; | |
| 2080 | HANDLE a[2], b[2], me = GetCurrentProcess(); | |
| 2081 | wchar_t wcmd[MAX_PATH_SIZE], full_dir[MAX_PATH_SIZE]; | |
| 2082 | char buf[MAX_PATH_SIZE], buf4[MAX_PATH_SIZE], buf5[MAX_PATH_SIZE], | |
| 2083 | cmdline[MAX_PATH_SIZE], *p; | |
| 2084 | DWORD flags = DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS; | |
| 2085 | FILE *fp; | |
| 2086 | ||
| 2087 | memset(&si, 0, sizeof(si)); | |
| 2088 | memset(&pi, 0, sizeof(pi)); | |
| 2089 | ||
| 2090 | si.cb = sizeof(si); | |
| 2091 | si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; | |
| 2092 | si.wShowWindow = SW_HIDE; | |
| 2093 | si.hStdError = GetStdHandle(STD_ERROR_HANDLE); | |
| 2094 | ||
| 2095 | CreatePipe(&a[0], &a[1], NULL, 0); | |
| 2096 | CreatePipe(&b[0], &b[1], NULL, 0); | |
| 2097 | DuplicateHandle(me, a[0], me, &si.hStdInput, 0, TRUE, flags); | |
| 2098 | DuplicateHandle(me, b[1], me, &si.hStdOutput, 0, TRUE, flags); | |
| 2099 | ||
| 2100 | if (interp == NULL && (fp = fopen(cmd, "r")) != NULL) { | |
| 2101 | buf[0] = buf[1] = '\0'; | |
| 2102 | fgets(buf, sizeof(buf), fp); | |
| 2103 | buf[sizeof(buf) - 1] = '\0'; | |
| 2104 | if (buf[0] == '#' && buf[1] == '!') { | |
| 2105 | interp = buf + 2; | |
| 2106 | for (p = interp + strlen(interp) - 1; | |
| 2107 | isspace(* (uint8_t *) p) && p > interp; p--) *p = '\0'; | |
| 2108 | } | |
| 2109 | fclose(fp); | |
| 2110 | } | |
| 2111 | ||
| 2112 | if (interp != NULL) { | |
| 2113 | abs_path(interp, buf4, ARRAY_SIZE(buf4)); | |
| 2114 | interp = buf4; | |
| 2115 | } | |
| 2116 | abs_path(dir, buf5, ARRAY_SIZE(buf5)); | |
| 2117 | to_wchar(dir, full_dir, ARRAY_SIZE(full_dir)); | |
| 2118 | mg_snprintf(cmdline, sizeof(cmdline), "%s%s\"%s\"", | |
| 2119 | interp ? interp : "", interp ? " " : "", cmd); | |
| 2120 | to_wchar(cmdline, wcmd, ARRAY_SIZE(wcmd)); | |
| 2121 | ||
| 2122 | if (CreateProcessW(NULL, wcmd, NULL, NULL, TRUE, CREATE_NEW_PROCESS_GROUP, | |
| 2123 | (void *) env, full_dir, &si, &pi) != 0) { | |
| 2124 | spawn_stdio_thread(sock, a[1], push_to_stdin); | |
| 2125 | spawn_stdio_thread(sock, b[0], pull_from_stdout); | |
| 2126 | } else { | |
| 2127 | CloseHandle(a[1]); | |
| 2128 | CloseHandle(b[0]); | |
| 2129 | closesocket(sock); | |
| 2130 | } | |
| 2131 | DBG(("CGI command: [%ls] -> %p", wcmd, pi.hProcess)); | |
| 2132 | ||
| 2133 | // Not closing a[0] and b[1] because we've used DUPLICATE_CLOSE_SOURCE | |
| 2134 | CloseHandle(si.hStdOutput); | |
| 2135 | CloseHandle(si.hStdInput); | |
| 2136 | //CloseHandle(pi.hThread); | |
| 2137 | //CloseHandle(pi.hProcess); | |
| 2138 | ||
| 2139 | return pi.hProcess; | |
| 2140 | } | |
| 2141 | #else | |
| 2142 | static process_id_t start_process(const char *interp, const char *cmd, | |
| 2143 | const char *env, const char *envp[], | |
| 2144 | const char *dir, sock_t sock) { | |
| 2145 | char buf[500]; | |
| 2146 | process_id_t pid = fork(); | |
| 2147 | (void) env; | |
| 2148 | ||
| 2149 | if (pid == 0) { | |
| 2150 | (void) chdir(dir); | |
| 2151 | (void) dup2(sock, 0); | |
| 2152 | (void) dup2(sock, 1); | |
| 2153 | closesocket(sock); | |
| 2154 | ||
| 2155 | // After exec, all signal handlers are restored to their default values, | |
| 2156 | // with one exception of SIGCHLD. According to POSIX.1-2001 and Linux's | |
| 2157 | // implementation, SIGCHLD's handler will leave unchanged after exec | |
| 2158 | // if it was set to be ignored. Restore it to default action. | |
| 2159 | signal(SIGCHLD, SIG_DFL); | |
| 2160 | ||
| 2161 | if (interp == NULL) { | |
| 2162 | execle(cmd, cmd, (char *) 0, envp); // Using (char *) 0 to avoid warning | |
| 2163 | } else { | |
| 2164 | execle(interp, interp, cmd, (char *) 0, envp); | |
| 2165 | } | |
| 2166 | snprintf(buf, sizeof(buf), "Status: 500\r\n\r\n" | |
| 2167 | "500 Server Error: %s%s%s: %s", interp == NULL ? "" : interp, | |
| 2168 | interp == NULL ? "" : " ", cmd, strerror(errno)); | |
| 2169 | send(1, buf, strlen(buf), 0); | |
| 2170 | exit(EXIT_FAILURE); // exec call failed | |
| 2171 | } | |
| 2172 | ||
| 2173 | return pid; | |
| 2174 | } | |
| 2175 | #endif // _WIN32 | |
| 2176 | ||
| 2177 | // This structure helps to create an environment for the spawned CGI program. | |
| 2178 | // Environment is an array of "VARIABLE=VALUE\0" ASCIIZ strings, | |
| 2179 | // last element must be NULL. | |
| 2180 | // However, on Windows there is a requirement that all these VARIABLE=VALUE\0 | |
| 2181 | // strings must reside in a contiguous buffer. The end of the buffer is | |
| 2182 | // marked by two '\0' characters. | |
| 2183 | // We satisfy both worlds: we create an envp array (which is vars), all | |
| 2184 | // entries are actually pointers inside buf. | |
| 2185 | struct cgi_env_block { | |
| 2186 | struct mg_connection *conn; | |
| 2187 | char buf[CGI_ENVIRONMENT_SIZE]; // Environment buffer | |
| 2188 | const char *vars[MAX_CGI_ENVIR_VARS]; // char *envp[] | |
| 2189 | int len; // Space taken | |
| 2190 | int nvars; // Number of variables in envp[] | |
| 2191 | }; | |
| 2192 | ||
| 2193 | // Append VARIABLE=VALUE\0 string to the buffer, and add a respective | |
| 2194 | // pointer into the vars array. | |
| 2195 | static char *addenv(struct cgi_env_block *block, const char *fmt, ...) { | |
| 2196 | int n, space; | |
| 2197 | char *added; | |
| 2198 | va_list ap; | |
| 2199 | ||
| 2200 | // Calculate how much space is left in the buffer | |
| 2201 | space = sizeof(block->buf) - block->len - 2; | |
| 2202 | assert(space >= 0); | |
| 2203 | ||
| 2204 | // Make a pointer to the free space int the buffer | |
| 2205 | added = block->buf + block->len; | |
| 2206 | ||
| 2207 | // Copy VARIABLE=VALUE\0 string into the free space | |
| 2208 | va_start(ap, fmt); | |
| 2209 | n = mg_vsnprintf(added, (size_t) space, fmt, ap); | |
| 2210 | va_end(ap); | |
| 2211 | ||
| 2212 | // Make sure we do not overflow buffer and the envp array | |
| 2213 | if (n > 0 && n + 1 < space && | |
| 2214 | block->nvars < (int) ARRAY_SIZE(block->vars) - 2) { | |
| 2215 | // Append a pointer to the added string into the envp array | |
| 2216 | block->vars[block->nvars++] = added; | |
| 2217 | // Bump up used length counter. Include \0 terminator | |
| 2218 | block->len += n + 1; | |
| 2219 | } | |
| 2220 | ||
| 2221 | return added; | |
| 2222 | } | |
| 2223 | ||
| 2224 | static void addenv2(struct cgi_env_block *blk, const char *name) { | |
| 2225 | const char *s; | |
| 2226 | if ((s = getenv(name)) != NULL) addenv(blk, "%s=%s", name, s); | |
| 2227 | } | |
| 2228 | ||
| 2229 | static void prepare_cgi_environment(struct connection *conn, | |
| 2230 | const char *prog, | |
| 2231 | struct cgi_env_block *blk) { | |
| 2232 | struct mg_connection *ri = &conn->mg_conn; | |
| 2233 | const char *s, *slash; | |
| 2234 | char *p, **opts = conn->server->config_options; | |
| 2235 | int i; | |
| 2236 | ||
| 2237 | blk->len = blk->nvars = 0; | |
| 2238 | blk->conn = ri; | |
| 2239 | ||
| 2240 | if ((s = getenv("SERVER_NAME")) != NULL) { | |
| 2241 | addenv(blk, "SERVER_NAME=%s", s); | |
| 2242 | } else { | |
| 2243 | addenv(blk, "SERVER_NAME=%s", ri->local_ip); | |
| 2244 | } | |
| 2245 | addenv(blk, "SERVER_ROOT=%s", opts[DOCUMENT_ROOT]); | |
| 2246 | addenv(blk, "DOCUMENT_ROOT=%s", opts[DOCUMENT_ROOT]); | |
| 2247 | addenv(blk, "SERVER_SOFTWARE=%s/%s", "Mongoose", MONGOOSE_VERSION); | |
| 2248 | ||
| 2249 | // Prepare the environment block | |
| 2250 | addenv(blk, "%s", "GATEWAY_INTERFACE=CGI/1.1"); | |
| 2251 | addenv(blk, "%s", "SERVER_PROTOCOL=HTTP/1.1"); | |
| 2252 | addenv(blk, "%s", "REDIRECT_STATUS=200"); // For PHP | |
| 2253 | ||
| 2254 | // TODO(lsm): fix this for IPv6 case | |
| 2255 | //addenv(blk, "SERVER_PORT=%d", ri->remote_port); | |
| 2256 | ||
| 2257 | addenv(blk, "REQUEST_METHOD=%s", ri->request_method); | |
| 2258 | addenv(blk, "REMOTE_ADDR=%s", ri->remote_ip); | |
| 2259 | addenv(blk, "REMOTE_PORT=%d", ri->remote_port); | |
| 2260 | addenv(blk, "REQUEST_URI=%s%s%s", ri->uri, | |
| 2261 | ri->query_string == NULL ? "" : "?", | |
| 2262 | ri->query_string == NULL ? "" : ri->query_string); | |
| 2263 | ||
| 2264 | // SCRIPT_NAME | |
| 2265 | if (conn->path_info != NULL) { | |
| 2266 | addenv(blk, "SCRIPT_NAME=%.*s", | |
| 2267 | (int) (strlen(ri->uri) - strlen(conn->path_info)), ri->uri); | |
| 2268 | addenv(blk, "PATH_INFO=%s", conn->path_info); | |
| 2269 | } else { | |
| 2270 | s = strrchr(prog, '/'); | |
| 2271 | slash = strrchr(ri->uri, '/'); | |
| 2272 | addenv(blk, "SCRIPT_NAME=%.*s%s", | |
| 2273 | slash == NULL ? 0 : (int) (slash - ri->uri), ri->uri, | |
| 2274 | s == NULL ? prog : s); | |
| 2275 | } | |
| 2276 | ||
| 2277 | addenv(blk, "SCRIPT_FILENAME=%s", prog); | |
| 2278 | addenv(blk, "PATH_TRANSLATED=%s", prog); | |
| 2279 | addenv(blk, "HTTPS=%s", conn->ns_conn->ssl != NULL ? "on" : "off"); | |
| 2280 | ||
| 2281 | if ((s = mg_get_header(ri, "Content-Type")) != NULL) | |
| 2282 | addenv(blk, "CONTENT_TYPE=%s", s); | |
| 2283 | ||
| 2284 | if (ri->query_string != NULL) | |
| 2285 | addenv(blk, "QUERY_STRING=%s", ri->query_string); | |
| 2286 | ||
| 2287 | if ((s = mg_get_header(ri, "Content-Length")) != NULL) | |
| 2288 | addenv(blk, "CONTENT_LENGTH=%s", s); | |
| 2289 | ||
| 2290 | addenv2(blk, "PATH"); | |
| 2291 | addenv2(blk, "TMP"); | |
| 2292 | addenv2(blk, "TEMP"); | |
| 2293 | addenv2(blk, "TMPDIR"); | |
| 2294 | addenv2(blk, "PERLLIB"); | |
| 2295 | addenv2(blk, ENV_EXPORT_TO_CGI); | |
| 2296 | ||
| 2297 | #if defined(_WIN32) | |
| 2298 | addenv2(blk, "COMSPEC"); | |
| 2299 | addenv2(blk, "SYSTEMROOT"); | |
| 2300 | addenv2(blk, "SystemDrive"); | |
| 2301 | addenv2(blk, "ProgramFiles"); | |
| 2302 | addenv2(blk, "ProgramFiles(x86)"); | |
| 2303 | addenv2(blk, "CommonProgramFiles(x86)"); | |
| 2304 | #else | |
| 2305 | addenv2(blk, "LD_LIBRARY_PATH"); | |
| 2306 | #endif // _WIN32 | |
| 2307 | ||
| 2308 | // Add all headers as HTTP_* variables | |
| 2309 | for (i = 0; i < ri->num_headers; i++) { | |
| 2310 | p = addenv(blk, "HTTP_%s=%s", | |
| 2311 | ri->http_headers[i].name, ri->http_headers[i].value); | |
| 2312 | ||
| 2313 | // Convert variable name into uppercase, and change - to _ | |
| 2314 | for (; *p != '=' && *p != '\0'; p++) { | |
| 2315 | if (*p == '-') | |
| 2316 | *p = '_'; | |
| 2317 | *p = (char) toupper(* (unsigned char *) p); | |
| 2318 | } | |
| 2319 | } | |
| 2320 | ||
| 2321 | blk->vars[blk->nvars++] = NULL; | |
| 2322 | blk->buf[blk->len++] = '\0'; | |
| 2323 | ||
| 2324 | assert(blk->nvars < (int) ARRAY_SIZE(blk->vars)); | |
| 2325 | assert(blk->len > 0); | |
| 2326 | assert(blk->len < (int) sizeof(blk->buf)); | |
| 2327 | } | |
| 2328 | ||
| 2329 | static const char cgi_status[] = "HTTP/1.1 200 OK\r\n"; | |
| 2330 | ||
| 2331 | static void open_cgi_endpoint(struct connection *conn, const char *prog) { | |
| 2332 | struct cgi_env_block blk; | |
| 2333 | char dir[MAX_PATH_SIZE]; | |
| 2334 | const char *p; | |
| 2335 | sock_t fds[2]; | |
| 2336 | ||
| 2337 | prepare_cgi_environment(conn, prog, &blk); | |
| 2338 | // CGI must be executed in its own directory. 'dir' must point to the | |
| 2339 | // directory containing executable program, 'p' must point to the | |
| 2340 | // executable program name relative to 'dir'. | |
| 2341 | if ((p = strrchr(prog, '/')) == NULL) { | |
| 2342 | mg_snprintf(dir, sizeof(dir), "%s", "."); | |
| 2343 | } else { | |
| 2344 | mg_snprintf(dir, sizeof(dir), "%.*s", (int) (p - prog), prog); | |
| 2345 | } | |
| 2346 | ||
| 2347 | // Try to create socketpair in a loop until success. ns_socketpair() | |
| 2348 | // can be interrupted by a signal and fail. | |
| 2349 | // TODO(lsm): use sigaction to restart interrupted syscall | |
| 2350 | { | |
| 2351 | int attempts = 0, max_attempts = NS_MAX_SOCKETPAIR_ATTEMPTS; | |
| 2352 | do { | |
| 2353 | ns_socketpair(fds); | |
| 2354 | } while (fds[0] == INVALID_SOCKET && ++attempts < max_attempts); | |
| 2355 | ||
| 2356 | if (fds[0] == INVALID_SOCKET) { | |
| 2357 | closesocket(fds[0]); | |
| 2358 | send_http_error(conn, 500, "ns_socketpair() failed"); | |
| 2359 | } | |
| 2360 | } | |
| 2361 | ||
| 2362 | if (start_process(conn->server->config_options[CGI_INTERPRETER], | |
| 2363 | prog, blk.buf, blk.vars, dir, fds[1]) != 0) { | |
| 2364 | conn->endpoint_type = EP_CGI; | |
| 2365 | conn->endpoint.nc = ns_add_sock(&conn->server->ns_mgr, fds[0], | |
| 2366 | mg_ev_handler, conn); | |
| 2367 | conn->endpoint.nc->flags |= MG_CGI_CONN; | |
| 2368 | ns_send(conn->ns_conn, cgi_status, sizeof(cgi_status) - 1); | |
| 2369 | conn->mg_conn.status_code = 200; | |
| 2370 | conn->ns_conn->flags |= NSF_BUFFER_BUT_DONT_SEND; | |
| 2371 | // Pass POST data to the CGI process | |
| 2372 | conn->endpoint.nc->send_iobuf = conn->ns_conn->recv_iobuf; | |
| 2373 | iobuf_init(&conn->ns_conn->recv_iobuf, 0); | |
| 2374 | } else { | |
| 2375 | closesocket(fds[0]); | |
| 2376 | send_http_error(conn, 500, "start_process(%s) failed", prog); | |
| 2377 | } | |
| 2378 | ||
| 2379 | #ifndef _WIN32 | |
| 2380 | closesocket(fds[1]); // On Windows, CGI stdio thread closes that socket | |
| 2381 | #endif | |
| 2382 | } | |
| 2383 | ||
| 2384 | static void on_cgi_data(struct ns_connection *nc) { | |
| 2385 | struct connection *conn = (struct connection *) nc->user_data; | |
| 2386 | const char *status = "500"; | |
| 2387 | struct mg_connection c; | |
| 2388 | ||
| 2389 | if (!conn) return; | |
| 2390 | ||
| 2391 | // Copy CGI data from CGI socket to the client send buffer | |
| 2392 | ns_forward(nc, conn->ns_conn); | |
| 2393 | ||
| 2394 | // If reply has not been parsed yet, parse it | |
| 2395 | if (conn->ns_conn->flags & NSF_BUFFER_BUT_DONT_SEND) { | |
| 2396 | struct iobuf *io = &conn->ns_conn->send_iobuf; | |
| 2397 | size_t s_len = sizeof(cgi_status) - 1; | |
| 2398 | int len = get_request_len(io->buf + s_len, io->len - s_len); | |
| 2399 | char buf[MAX_REQUEST_SIZE], *s = buf; | |
| 2400 | ||
| 2401 | if (len == 0) return; | |
| 2402 | ||
| 2403 | if (len < 0 || len > (int) sizeof(buf)) { | |
| 2404 | len = io->len; | |
| 2405 | iobuf_remove(io, io->len); | |
| 2406 | send_http_error(conn, 500, "CGI program sent malformed headers: [%.*s]", | |
| 2407 | len, io->buf); | |
| 2408 | } else { | |
| 2409 | memset(&c, 0, sizeof(c)); | |
| 2410 | memcpy(buf, io->buf + s_len, len); | |
| 2411 | buf[len - 1] = '\0'; | |
| 2412 | parse_http_headers(&s, &c); | |
| 2413 | if (mg_get_header(&c, "Location") != NULL) { | |
| 2414 | status = "302"; | |
| 2415 | } else if ((status = (char *) mg_get_header(&c, "Status")) == NULL) { | |
| 2416 | status = "200"; | |
| 2417 | } | |
| 2418 | memcpy(io->buf + 9, status, 3); | |
| 2419 | conn->mg_conn.status_code = atoi(status); | |
| 2420 | } | |
| 2421 | conn->ns_conn->flags &= ~NSF_BUFFER_BUT_DONT_SEND; | |
| 2422 | } | |
| 2423 | } | |
| 2424 | #endif // !MONGOOSE_NO_CGI | |
| 2425 | ||
| 2426 | static char *mg_strdup(const char *str) { | |
| 2427 | char *copy = (char *) NS_MALLOC(strlen(str) + 1); | |
| 2428 | if (copy != NULL) { | |
| 2429 | strcpy(copy, str); | |
| 2430 | } | |
| 2431 | return copy; | |
| 2432 | } | |
| 2433 | ||
| 2434 | static int isbyte(int n) { | |
| 2435 | return n >= 0 && n <= 255; | |
| 2436 | } | |
| 2437 | ||
| 2438 | static int parse_net(const char *spec, uint32_t *net, uint32_t *mask) { | |
| 2439 | int n, a, b, c, d, slash = 32, len = 0; | |
| 2440 | ||
| 2441 | if ((sscanf(spec, "%d.%d.%d.%d/%d%n", &a, &b, &c, &d, &slash, &n) == 5 || | |
| 2442 | sscanf(spec, "%d.%d.%d.%d%n", &a, &b, &c, &d, &n) == 4) && | |
| 2443 | isbyte(a) && isbyte(b) && isbyte(c) && isbyte(d) && | |
| 2444 | slash >= 0 && slash < 33) { | |
| 2445 | len = n; | |
| 2446 | *net = ((uint32_t)a << 24) | ((uint32_t)b << 16) | ((uint32_t)c << 8) | d; | |
| 2447 | *mask = slash ? 0xffffffffU << (32 - slash) : 0; | |
| 2448 | } | |
| 2449 | ||
| 2450 | return len; | |
| 2451 | } | |
| 2452 | ||
| 2453 | // Verify given socket address against the ACL. | |
| 2454 | // Return -1 if ACL is malformed, 0 if address is disallowed, 1 if allowed. | |
| 2455 | static int check_acl(const char *acl, uint32_t remote_ip) { | |
| 2456 | int allowed, flag; | |
| 2457 | uint32_t net, mask; | |
| 2458 | struct vec vec; | |
| 2459 | ||
| 2460 | // If any ACL is set, deny by default | |
| 2461 | allowed = acl == NULL ? '+' : '-'; | |
| 2462 | ||
| 2463 | while ((acl = next_option(acl, &vec, NULL)) != NULL) { | |
| 2464 | flag = vec.ptr[0]; | |
| 2465 | if ((flag != '+' && flag != '-') || | |
| 2466 | parse_net(&vec.ptr[1], &net, &mask) == 0) { | |
| 2467 | return -1; | |
| 2468 | } | |
| 2469 | ||
| 2470 | if (net == (remote_ip & mask)) { | |
| 2471 | allowed = flag; | |
| 2472 | } | |
| 2473 | } | |
| 2474 | ||
| 2475 | return allowed == '+'; | |
| 2476 | } | |
| 2477 | ||
| 2478 | // Protect against directory disclosure attack by removing '..', | |
| 2479 | // excessive '/' and '\' characters | |
| 2480 | static void remove_double_dots_and_double_slashes(char *s) { | |
| 2481 | char *p = s; | |
| 2482 | ||
| 2483 | while (*s != '\0') { | |
| 2484 | *p++ = *s++; | |
| 2485 | if (s[-1] == '/' || s[-1] == '\\') { | |
| 2486 | // Skip all following slashes, backslashes and double-dots | |
| 2487 | while (s[0] != '\0') { | |
| 2488 | if (s[0] == '/' || s[0] == '\\') { s++; } | |
| 2489 | else if (s[0] == '.' && (s[1] == '/' || s[1] == '\\')) { s += 2; } | |
| 2490 | else if (s[0] == '.' && s[1] == '.' && s[2] == '\0') { s += 2; } | |
| 2491 | else if (s[0] == '.' && s[1] == '.' && (s[2] == '/' || s[2] == '\\')) { s += 3; } | |
| 2492 | else { break; } | |
| 2493 | } | |
| 2494 | } | |
| 2495 | } | |
| 2496 | *p = '\0'; | |
| 2497 | } | |
| 2498 | ||
| 2499 | int mg_url_decode(const char *src, size_t src_len, char *dst, | |
| 2500 | size_t dst_len, int is_form_url_encoded) { | |
| 2501 | size_t i, j = 0; | |
| 2502 | int a, b; | |
| 2503 | #define HEXTOI(x) (isdigit(x) ? (x) - '0' : (x) - 'W') | |
| 2504 | ||
| 2505 | for (i = j = 0; i < src_len && j < dst_len - 1; i++, j++) { | |
| 2506 | if (src[i] == '%' && i + 2 < src_len && | |
| 2507 | isxdigit(* (const unsigned char *) (src + i + 1)) && | |
| 2508 | isxdigit(* (const unsigned char *) (src + i + 2))) { | |
| 2509 | a = tolower(* (const unsigned char *) (src + i + 1)); | |
| 2510 | b = tolower(* (const unsigned char *) (src + i + 2)); | |
| 2511 | dst[j] = (char) ((HEXTOI(a) << 4) | HEXTOI(b)); | |
| 2512 | i += 2; | |
| 2513 | } else if (is_form_url_encoded && src[i] == '+') { | |
| 2514 | dst[j] = ' '; | |
| 2515 | } else { | |
| 2516 | dst[j] = src[i]; | |
| 2517 | } | |
| 2518 | } | |
| 2519 | ||
| 2520 | dst[j] = '\0'; // Null-terminate the destination | |
| 2521 | ||
| 2522 | return i >= src_len ? j : -1; | |
| 2523 | } | |
| 2524 | ||
| 2525 | static int is_valid_http_method(const char *s) { | |
| 2526 | return !strcmp(s, "GET") || !strcmp(s, "POST") || !strcmp(s, "HEAD") || | |
| 2527 | !strcmp(s, "CONNECT") || !strcmp(s, "PUT") || !strcmp(s, "DELETE") || | |
| 2528 | !strcmp(s, "OPTIONS") || !strcmp(s, "PROPFIND") || !strcmp(s, "MKCOL") || | |
| 2529 | !strcmp(s, "PATCH"); | |
| 2530 | } | |
| 2531 | ||
| 2532 | // Parse HTTP request, fill in mg_request structure. | |
| 2533 | // This function modifies the buffer by NUL-terminating | |
| 2534 | // HTTP request components, header names and header values. | |
| 2535 | // Note that len must point to the last \n of HTTP headers. | |
| 2536 | static size_t parse_http_message(char *buf, size_t len, | |
| 2537 | struct mg_connection *ri) { | |
| 2538 | int is_request, n; | |
| 2539 | ||
| 2540 | // Reset the connection. Make sure that we don't touch fields that are | |
| 2541 | // set elsewhere: remote_ip, remote_port, server_param | |
| 2542 | ri->request_method = ri->uri = ri->http_version = ri->query_string = NULL; | |
| 2543 | ri->num_headers = ri->status_code = ri->is_websocket = ri->content_len = 0; | |
| 2544 | ||
| 2545 | if (len < 1) return ~0; | |
| 2546 | ||
| 2547 | buf[len - 1] = '\0'; | |
| 2548 | ||
| 2549 | // RFC says that all initial whitespaces should be ignored | |
| 2550 | while (*buf != '\0' && isspace(* (unsigned char *) buf)) { | |
| 2551 | buf++; | |
| 2552 | } | |
| 2553 | ri->request_method = skip(&buf, " "); | |
| 2554 | ri->uri = skip(&buf, " "); | |
| 2555 | ri->http_version = skip(&buf, "\r\n"); | |
| 2556 | ||
| 2557 | // HTTP message could be either HTTP request or HTTP response, e.g. | |
| 2558 | // "GET / HTTP/1.0 ...." or "HTTP/1.0 200 OK ..." | |
| 2559 | is_request = is_valid_http_method(ri->request_method); | |
| 2560 | if ((is_request && memcmp(ri->http_version, "HTTP/", 5) != 0) || | |
| 2561 | (!is_request && memcmp(ri->request_method, "HTTP/", 5) != 0)) { | |
| 2562 | len = ~0; | |
| 2563 | } else { | |
| 2564 | if (is_request) { | |
| 2565 | ri->http_version += 5; | |
| 2566 | } else { | |
| 2567 | ri->status_code = atoi(ri->uri); | |
| 2568 | } | |
| 2569 | parse_http_headers(&buf, ri); | |
| 2570 | ||
| 2571 | if ((ri->query_string = strchr(ri->uri, '?')) != NULL) { | |
| 2572 | *(char *) ri->query_string++ = '\0'; | |
| 2573 | } | |
| 2574 | n = (int) strlen(ri->uri); | |
| 2575 | mg_url_decode(ri->uri, n, (char *) ri->uri, n + 1, 0); | |
| 2576 | if (*ri->uri == '/' || *ri->uri == '.') { | |
| 2577 | remove_double_dots_and_double_slashes((char *) ri->uri); | |
| 2578 | } | |
| 2579 | } | |
| 2580 | ||
| 2581 | return len; | |
| 2582 | } | |
| 2583 | ||
| 2584 | static int lowercase(const char *s) { | |
| 2585 | return tolower(* (const unsigned char *) s); | |
| 2586 | } | |
| 2587 | ||
| 2588 | static int mg_strcasecmp(const char *s1, const char *s2) { | |
| 2589 | int diff; | |
| 2590 | ||
| 2591 | do { | |
| 2592 | diff = lowercase(s1++) - lowercase(s2++); | |
| 2593 | } while (diff == 0 && s1[-1] != '\0'); | |
| 2594 | ||
| 2595 | return diff; | |
| 2596 | } | |
| 2597 | ||
| 2598 | static int mg_strncasecmp(const char *s1, const char *s2, size_t len) { | |
| 2599 | int diff = 0; | |
| 2600 | ||
| 2601 | if (len > 0) | |
| 2602 | do { | |
| 2603 | diff = lowercase(s1++) - lowercase(s2++); | |
| 2604 | } while (diff == 0 && s1[-1] != '\0' && --len > 0); | |
| 2605 | ||
| 2606 | return diff; | |
| 2607 | } | |
| 2608 | ||
| 2609 | // Return HTTP header value, or NULL if not found. | |
| 2610 | const char *mg_get_header(const struct mg_connection *ri, const char *s) { | |
| 2611 | int i; | |
| 2612 | ||
| 2613 | for (i = 0; i < ri->num_headers; i++) | |
| 2614 | if (!mg_strcasecmp(s, ri->http_headers[i].name)) | |
| 2615 | return ri->http_headers[i].value; | |
| 2616 | ||
| 2617 | return NULL; | |
| 2618 | } | |
| 2619 | ||
| 2620 | // Perform case-insensitive match of string against pattern | |
| 2621 | int mg_match_prefix(const char *pattern, ssize_t pattern_len, const char *str) { | |
| 2622 | const char *or_str; | |
| 2623 | int len, res, i = 0, j = 0; | |
| 2624 | ||
| 2625 | if ((or_str = (const char *) memchr(pattern, '|', pattern_len)) != NULL) { | |
| 2626 | res = mg_match_prefix(pattern, or_str - pattern, str); | |
| 2627 | return res > 0 ? res : mg_match_prefix(or_str + 1, | |
| 2628 | (pattern + pattern_len) - (or_str + 1), str); | |
| 2629 | } | |
| 2630 | ||
| 2631 | for (; i < pattern_len; i++, j++) { | |
| 2632 | if (pattern[i] == '?' && str[j] != '\0') { | |
| 2633 | continue; | |
| 2634 | } else if (pattern[i] == '$') { | |
| 2635 | return str[j] == '\0' ? j : -1; | |
| 2636 | } else if (pattern[i] == '*') { | |
| 2637 | i++; | |
| 2638 | if (pattern[i] == '*') { | |
| 2639 | i++; | |
| 2640 | len = (int) strlen(str + j); | |
| 2641 | } else { | |
| 2642 | len = (int) strcspn(str + j, "/"); | |
| 2643 | } | |
| 2644 | if (i == pattern_len) { | |
| 2645 | return j + len; | |
| 2646 | } | |
| 2647 | do { | |
| 2648 | res = mg_match_prefix(pattern + i, pattern_len - i, str + j + len); | |
| 2649 | } while (res == -1 && len-- > 0); | |
| 2650 | return res == -1 ? -1 : j + res + len; | |
| 2651 | } else if (lowercase(&pattern[i]) != lowercase(&str[j])) { | |
| 2652 | return -1; | |
| 2653 | } | |
| 2654 | } | |
| 2655 | return j; | |
| 2656 | } | |
| 2657 | ||
| 2658 | // This function prints HTML pages, and expands "{{something}}" blocks | |
| 2659 | // inside HTML by calling appropriate callback functions. | |
| 2660 | // Note that {{@path/to/file}} construct outputs embedded file's contents, | |
| 2661 | // which provides SSI-like functionality. | |
| 2662 | void mg_template(struct mg_connection *conn, const char *s, | |
| 2663 | struct mg_expansion *expansions) { | |
| 2664 | int i, j, pos = 0, inside_marker = 0; | |
| 2665 | ||
| 2666 | for (i = 0; s[i] != '\0'; i++) { | |
| 2667 | if (inside_marker == 0 && !memcmp(&s[i], "{{", 2)) { | |
| 2668 | if (i > pos) { | |
| 2669 | mg_send_data(conn, &s[pos], i - pos); | |
| 2670 | } | |
| 2671 | pos = i; | |
| 2672 | inside_marker = 1; | |
| 2673 | } | |
| 2674 | if (inside_marker == 1 && !memcmp(&s[i], "}}", 2)) { | |
| 2675 | for (j = 0; expansions[j].keyword != NULL; j++) { | |
| 2676 | const char *kw = expansions[j].keyword; | |
| 2677 | if ((int) strlen(kw) == i - (pos + 2) && | |
| 2678 | memcmp(kw, &s[pos + 2], i - (pos + 2)) == 0) { | |
| 2679 | expansions[j].handler(conn); | |
| 2680 | pos = i + 2; | |
| 2681 | break; | |
| 2682 | } | |
| 2683 | } | |
| 2684 | inside_marker = 0; | |
| 2685 | } | |
| 2686 | } | |
| 2687 | if (i > pos) { | |
| 2688 | mg_send_data(conn, &s[pos], i - pos); | |
| 2689 | } | |
| 2690 | } | |
| 2691 | ||
| 2692 | #ifndef MONGOOSE_NO_FILESYSTEM | |
| 2693 | static int is_dav_request(const struct connection *conn) { | |
| 2694 | const char *s = conn->mg_conn.request_method; | |
| 2695 | return !strcmp(s, "PUT") || !strcmp(s, "DELETE") || | |
| 2696 | !strcmp(s, "MKCOL") || !strcmp(s, "PROPFIND"); | |
| 2697 | } | |
| 2698 | ||
| 2699 | static int must_hide_file(struct connection *conn, const char *path) { | |
| 2700 | const char *pw_pattern = "**" PASSWORDS_FILE_NAME "$"; | |
| 2701 | const char *pattern = conn->server->config_options[HIDE_FILES_PATTERN]; | |
| 2702 | return mg_match_prefix(pw_pattern, strlen(pw_pattern), path) > 0 || | |
| 2703 | (pattern != NULL && mg_match_prefix(pattern, strlen(pattern), path) > 0); | |
| 2704 | } | |
| 2705 | ||
| 2706 | // Return 1 if real file has been found, 0 otherwise | |
| 2707 | static int convert_uri_to_file_name(struct connection *conn, char *buf, | |
| 2708 | size_t buf_len, file_stat_t *st) { | |
| 2709 | struct vec a, b; | |
| 2710 | const char *rewrites = conn->server->config_options[URL_REWRITES]; | |
| 2711 | const char *root = | |
| 2712 | #ifndef MONGOOSE_NO_DAV | |
| 2713 | is_dav_request(conn) && conn->server->config_options[DAV_ROOT] != NULL ? | |
| 2714 | conn->server->config_options[DAV_ROOT] : | |
| 2715 | #endif | |
| 2716 | conn->server->config_options[DOCUMENT_ROOT]; | |
| 2717 | #ifndef MONGOOSE_NO_CGI | |
| 2718 | const char *cgi_pat = conn->server->config_options[CGI_PATTERN]; | |
| 2719 | char *p; | |
| 2720 | #endif | |
| 2721 | const char *uri = conn->mg_conn.uri; | |
| 2722 | const char *domain = mg_get_header(&conn->mg_conn, "Host"); | |
| 2723 | // Important: match_len has to be declared as int, unless rewrites break. | |
| 2724 | int match_len, root_len = root == NULL ? 0 : strlen(root); | |
| 2725 | ||
| 2726 | // Perform virtual hosting rewrites | |
| 2727 | if (rewrites != NULL && domain != NULL) { | |
| 2728 | const char *colon = strchr(domain, ':'); | |
| 2729 | size_t domain_len = colon == NULL ? strlen(domain) : colon - domain; | |
| 2730 | ||
| 2731 | while ((rewrites = next_option(rewrites, &a, &b)) != NULL) { | |
| 2732 | if (a.len > 1 && a.ptr[0] == '@' && a.len == domain_len + 1 && | |
| 2733 | mg_strncasecmp(a.ptr + 1, domain, domain_len) == 0) { | |
| 2734 | root = b.ptr; | |
| 2735 | root_len = b.len; | |
| 2736 | break; | |
| 2737 | } | |
| 2738 | } | |
| 2739 | } | |
| 2740 | ||
| 2741 | // No filesystem access | |
| 2742 | if (root == NULL || root_len == 0) return 0; | |
| 2743 | ||
| 2744 | // Handle URL rewrites | |
| 2745 | mg_snprintf(buf, buf_len, "%.*s%s", root_len, root, uri); | |
| 2746 | rewrites = conn->server->config_options[URL_REWRITES]; // Re-initialize! | |
| 2747 | while ((rewrites = next_option(rewrites, &a, &b)) != NULL) { | |
| 2748 | if ((match_len = mg_match_prefix(a.ptr, a.len, uri)) > 0) { | |
| 2749 | mg_snprintf(buf, buf_len, "%.*s%s", (int) b.len, b.ptr, uri + match_len); | |
| 2750 | break; | |
| 2751 | } | |
| 2752 | } | |
| 2753 | ||
| 2754 | if (stat(buf, st) == 0) return 1; | |
| 2755 | ||
| 2756 | #ifndef MONGOOSE_NO_CGI | |
| 2757 | // Support PATH_INFO for CGI scripts. | |
| 2758 | for (p = buf + strlen(root) + 2; *p != '\0'; p++) { | |
| 2759 | if (*p == '/') { | |
| 2760 | *p = '\0'; | |
| 2761 | if (mg_match_prefix(cgi_pat, strlen(cgi_pat), buf) > 0 && | |
| 2762 | !stat(buf, st)) { | |
| 2763 | DBG(("!!!! [%s]", buf)); | |
| 2764 | *p = '/'; | |
| 2765 | conn->path_info = mg_strdup(p); | |
| 2766 | *p = '\0'; | |
| 2767 | return 1; | |
| 2768 | } | |
| 2769 | *p = '/'; | |
| 2770 | } | |
| 2771 | } | |
| 2772 | #endif | |
| 2773 | ||
| 2774 | return 0; | |
| 2775 | } | |
| 2776 | #endif // MONGOOSE_NO_FILESYSTEM | |
| 2777 | ||
| 2778 | static int should_keep_alive(const struct mg_connection *conn) { | |
| 2779 | struct connection *c = MG_CONN_2_CONN(conn); | |
| 2780 | const char *method = conn->request_method; | |
| 2781 | const char *http_version = conn->http_version; | |
| 2782 | const char *header = mg_get_header(conn, "Connection"); | |
| 2783 | return method != NULL && | |
| 2784 | (!strcmp(method, "GET") || c->endpoint_type == EP_USER) && | |
| 2785 | ((header != NULL && !mg_strcasecmp(header, "keep-alive")) || | |
| 2786 | (header == NULL && http_version && !strcmp(http_version, "1.1"))); | |
| 2787 | } | |
| 2788 | ||
| 2789 | size_t mg_write(struct mg_connection *c, const void *buf, size_t len) { | |
| 2790 | struct connection *conn = MG_CONN_2_CONN(c); | |
| 2791 | ns_send(conn->ns_conn, buf, len); | |
| 2792 | return conn->ns_conn->send_iobuf.len; | |
| 2793 | } | |
| 2794 | ||
| 2795 | void mg_send_status(struct mg_connection *c, int status) { | |
| 2796 | struct connection *conn = MG_CONN_2_CONN(c); | |
| 2797 | if (c->status_code == 0) { | |
| 2798 | c->status_code = status; | |
| 2799 | mg_printf(c, "HTTP/1.1 %d %s\r\n", status, status_code_to_str(status)); | |
| 2800 | } | |
| 2801 | conn->ns_conn->flags |= MG_USING_CHUNKED_API; | |
| 2802 | } | |
| 2803 | ||
| 2804 | void mg_send_header(struct mg_connection *c, const char *name, const char *v) { | |
| 2805 | struct connection *conn = MG_CONN_2_CONN(c); | |
| 2806 | if (c->status_code == 0) { | |
| 2807 | c->status_code = 200; | |
| 2808 | mg_printf(c, "HTTP/1.1 %d %s\r\n", 200, status_code_to_str(200)); | |
| 2809 | } | |
| 2810 | mg_printf(c, "%s: %s\r\n", name, v); | |
| 2811 | conn->ns_conn->flags |= MG_USING_CHUNKED_API; | |
| 2812 | } | |
| 2813 | ||
| 2814 | static void terminate_headers(struct mg_connection *c) { | |
| 2815 | struct connection *conn = MG_CONN_2_CONN(c); | |
| 2816 | if (!(conn->ns_conn->flags & MG_HEADERS_SENT)) { | |
| 2817 | mg_send_header(c, "Transfer-Encoding", "chunked"); | |
| 2818 | mg_write(c, "\r\n", 2); | |
| 2819 | conn->ns_conn->flags |= MG_HEADERS_SENT; | |
| 2820 | } | |
| 2821 | } | |
| 2822 | ||
| 2823 | size_t mg_send_data(struct mg_connection *c, const void *data, int data_len) { | |
| 2824 | struct connection *conn = MG_CONN_2_CONN(c); | |
| 2825 | terminate_headers(c); | |
| 2826 | write_chunk(MG_CONN_2_CONN(c), (const char *) data, data_len); | |
| 2827 | return conn->ns_conn->send_iobuf.len; | |
| 2828 | } | |
| 2829 | ||
| 2830 | size_t mg_printf_data(struct mg_connection *c, const char *fmt, ...) { | |
| 2831 | va_list ap; | |
| 2832 | int ret; | |
| 2833 | ||
| 2834 | va_start(ap, fmt); | |
| 2835 | ret = mg_vprintf_data(c, fmt, ap); | |
| 2836 | va_end(ap); | |
| 2837 | ||
| 2838 | return ret; | |
| 2839 | } | |
| 2840 | ||
| 2841 | size_t mg_vprintf_data(struct mg_connection *c, const char *fmt, va_list ap) { | |
| 2842 | struct connection *conn = MG_CONN_2_CONN(c); | |
| 2843 | int len; | |
| 2844 | char mem[IOBUF_SIZE], *buf = mem; | |
| 2845 | ||
| 2846 | terminate_headers(c); | |
| 2847 | ||
| 2848 | len = ns_avprintf(&buf, sizeof(mem), fmt, ap); | |
| 2849 | ||
| 2850 | if (len >= 0) { | |
| 2851 | write_chunk((struct connection *) conn, buf, len); | |
| 2852 | } | |
| 2853 | if (buf != mem && buf != NULL) { | |
| 2854 | NS_FREE(buf); | |
| 2855 | } | |
| 2856 | return conn->ns_conn->send_iobuf.len; | |
| 2857 | } | |
| 2858 | ||
| 2859 | #if !defined(MONGOOSE_NO_WEBSOCKET) || !defined(MONGOOSE_NO_AUTH) | |
| 2860 | static int is_big_endian(void) { | |
| 2861 | static const int n = 1; | |
| 2862 | return ((char *) &n)[0] == 0; | |
| 2863 | } | |
| 2864 | #endif | |
| 2865 | ||
| 2866 | #ifndef MONGOOSE_NO_WEBSOCKET | |
| 2867 | // START OF SHA-1 code | |
| 2868 | // Copyright(c) By Steve Reid <steve@edmweb.com> | |
| 2869 | #define SHA1HANDSOFF | |
| 2870 | #if defined(__sun) | |
| 2871 | #include "solarisfixes.h" | |
| 2872 | #endif | |
| 2873 | ||
| 2874 | union char64long16 { unsigned char c[64]; uint32_t l[16]; }; | |
| 2875 | ||
| 2876 | #define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) | |
| 2877 | ||
| 2878 | static uint32_t blk0(union char64long16 *block, int i) { | |
| 2879 | // Forrest: SHA expect BIG_ENDIAN, swap if LITTLE_ENDIAN | |
| 2880 | if (!is_big_endian()) { | |
| 2881 | block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) | | |
| 2882 | (rol(block->l[i], 8) & 0x00FF00FF); | |
| 2883 | } | |
| 2884 | return block->l[i]; | |
| 2885 | } | |
| 2886 | ||
| 2887 | /* Avoid redefine warning (ARM /usr/include/sys/ucontext.h define R0~R4) */ | |
| 2888 | #undef blk | |
| 2889 | #undef R0 | |
| 2890 | #undef R1 | |
| 2891 | #undef R2 | |
| 2892 | #undef R3 | |
| 2893 | #undef R4 | |
| 2894 | ||
| 2895 | #define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ | |
| 2896 | ^block->l[(i+2)&15]^block->l[i&15],1)) | |
| 2897 | #define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(block, i)+0x5A827999+rol(v,5);w=rol(w,30); | |
| 2898 | #define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); | |
| 2899 | #define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); | |
| 2900 | #define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); | |
| 2901 | #define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); | |
| 2902 | ||
| 2903 | typedef struct { | |
| 2904 | uint32_t state[5]; | |
| 2905 | uint32_t count[2]; | |
| 2906 | unsigned char buffer[64]; | |
| 2907 | } SHA1_CTX; | |
| 2908 | ||
| 2909 | static void SHA1Transform(uint32_t state[5], const unsigned char buffer[64]) { | |
| 2910 | uint32_t a, b, c, d, e; | |
| 2911 | union char64long16 block[1]; | |
| 2912 | ||
| 2913 | memcpy(block, buffer, 64); | |
| 2914 | a = state[0]; | |
| 2915 | b = state[1]; | |
| 2916 | c = state[2]; | |
| 2917 | d = state[3]; | |
| 2918 | e = state[4]; | |
| 2919 | R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); | |
| 2920 | R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); | |
| 2921 | R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); | |
| 2922 | R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); | |
| 2923 | R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); | |
| 2924 | R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); | |
| 2925 | R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); | |
| 2926 | R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); | |
| 2927 | R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); | |
| 2928 | R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); | |
| 2929 | R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); | |
| 2930 | R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); | |
| 2931 | R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); | |
| 2932 | R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); | |
| 2933 | R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); | |
| 2934 | R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); | |
| 2935 | R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); | |
| 2936 | R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); | |
| 2937 | R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); | |
| 2938 | R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); | |
| 2939 | state[0] += a; | |
| 2940 | state[1] += b; | |
| 2941 | state[2] += c; | |
| 2942 | state[3] += d; | |
| 2943 | state[4] += e; | |
| 2944 | // Erase working structures. The order of operations is important, | |
| 2945 | // used to ensure that compiler doesn't optimize those out. | |
| 2946 | memset(block, 0, sizeof(block)); | |
| 2947 | a = b = c = d = e = 0; | |
| 2948 | (void) a; (void) b; (void) c; (void) d; (void) e; | |
| 2949 | } | |
| 2950 | ||
| 2951 | static void SHA1Init(SHA1_CTX *context) { | |
| 2952 | context->state[0] = 0x67452301; | |
| 2953 | context->state[1] = 0xEFCDAB89; | |
| 2954 | context->state[2] = 0x98BADCFE; | |
| 2955 | context->state[3] = 0x10325476; | |
| 2956 | context->state[4] = 0xC3D2E1F0; | |
| 2957 | context->count[0] = context->count[1] = 0; | |
| 2958 | } | |
| 2959 | ||
| 2960 | static void SHA1Update(SHA1_CTX *context, const unsigned char *data, | |
| 2961 | size_t len) { | |
| 2962 | size_t i, j; | |
| 2963 | ||
| 2964 | j = context->count[0]; | |
| 2965 | if ((context->count[0] += len << 3) < j) | |
| 2966 | context->count[1]++; | |
| 2967 | context->count[1] += (len>>29); | |
| 2968 | j = (j >> 3) & 63; | |
| 2969 | if ((j + len) > 63) { | |
| 2970 | memcpy(&context->buffer[j], data, (i = 64-j)); | |
| 2971 | SHA1Transform(context->state, context->buffer); | |
| 2972 | for ( ; i + 63 < len; i += 64) { | |
| 2973 | SHA1Transform(context->state, &data[i]); | |
| 2974 | } | |
| 2975 | j = 0; | |
| 2976 | } | |
| 2977 | else i = 0; | |
| 2978 | memcpy(&context->buffer[j], &data[i], len - i); | |
| 2979 | } | |
| 2980 | ||
| 2981 | static void SHA1Final(unsigned char digest[20], SHA1_CTX *context) { | |
| 2982 | unsigned i; | |
| 2983 | unsigned char finalcount[8], c; | |
| 2984 | ||
| 2985 | for (i = 0; i < 8; i++) { | |
| 2986 | finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] | |
| 2987 | >> ((3-(i & 3)) * 8) ) & 255); | |
| 2988 | } | |
| 2989 | c = 0200; | |
| 2990 | SHA1Update(context, &c, 1); | |
| 2991 | while ((context->count[0] & 504) != 448) { | |
| 2992 | c = 0000; | |
| 2993 | SHA1Update(context, &c, 1); | |
| 2994 | } | |
| 2995 | SHA1Update(context, finalcount, 8); | |
| 2996 | for (i = 0; i < 20; i++) { | |
| 2997 | digest[i] = (unsigned char) | |
| 2998 | ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); | |
| 2999 | } | |
| 3000 | memset(context, '\0', sizeof(*context)); | |
| 3001 | memset(&finalcount, '\0', sizeof(finalcount)); | |
| 3002 | } | |
| 3003 | // END OF SHA1 CODE | |
| 3004 | ||
| 3005 | static void base64_encode(const unsigned char *src, int src_len, char *dst) { | |
| 3006 | static const char *b64 = | |
| 3007 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; | |
| 3008 | int i, j, a, b, c; | |
| 3009 | ||
| 3010 | for (i = j = 0; i < src_len; i += 3) { | |
| 3011 | a = src[i]; | |
| 3012 | b = i + 1 >= src_len ? 0 : src[i + 1]; | |
| 3013 | c = i + 2 >= src_len ? 0 : src[i + 2]; | |
| 3014 | ||
| 3015 | dst[j++] = b64[a >> 2]; | |
| 3016 | dst[j++] = b64[((a & 3) << 4) | (b >> 4)]; | |
| 3017 | if (i + 1 < src_len) { | |
| 3018 | dst[j++] = b64[(b & 15) << 2 | (c >> 6)]; | |
| 3019 | } | |
| 3020 | if (i + 2 < src_len) { | |
| 3021 | dst[j++] = b64[c & 63]; | |
| 3022 | } | |
| 3023 | } | |
| 3024 | while (j % 4 != 0) { | |
| 3025 | dst[j++] = '='; | |
| 3026 | } | |
| 3027 | dst[j++] = '\0'; | |
| 3028 | } | |
| 3029 | ||
| 3030 | static void send_websocket_handshake(struct mg_connection *conn, | |
| 3031 | const char *key) { | |
| 3032 | static const char *magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; | |
| 3033 | char buf[500], sha[20], b64_sha[sizeof(sha) * 2]; | |
| 3034 | SHA1_CTX sha_ctx; | |
| 3035 | ||
| 3036 | mg_snprintf(buf, sizeof(buf), "%s%s", key, magic); | |
| 3037 | SHA1Init(&sha_ctx); | |
| 3038 | SHA1Update(&sha_ctx, (unsigned char *) buf, strlen(buf)); | |
| 3039 | SHA1Final((unsigned char *) sha, &sha_ctx); | |
| 3040 | base64_encode((unsigned char *) sha, sizeof(sha), b64_sha); | |
| 3041 | mg_snprintf(buf, sizeof(buf), "%s%s%s", | |
| 3042 | "HTTP/1.1 101 Switching Protocols\r\n" | |
| 3043 | "Upgrade: websocket\r\n" | |
| 3044 | "Connection: Upgrade\r\n" | |
| 3045 | "Sec-WebSocket-Accept: ", b64_sha, "\r\n\r\n"); | |
| 3046 | ||
| 3047 | mg_write(conn, buf, strlen(buf)); | |
| 3048 | } | |
| 3049 | ||
| 3050 | static size_t deliver_websocket_frame(struct connection *conn) { | |
| 3051 | // Having buf unsigned char * is important, as it is used below in arithmetic | |
| 3052 | unsigned char *buf = (unsigned char *) conn->ns_conn->recv_iobuf.buf; | |
| 3053 | size_t i, len, buf_len = conn->ns_conn->recv_iobuf.len, frame_len = 0, | |
| 3054 | mask_len = 0, header_len = 0, data_len = 0, buffered = 0; | |
| 3055 | ||
| 3056 | if (buf_len >= 2) { | |
| 3057 | len = buf[1] & 127; | |
| 3058 | mask_len = buf[1] & 128 ? 4 : 0; | |
| 3059 | if (len < 126 && buf_len >= mask_len) { | |
| 3060 | data_len = len; | |
| 3061 | header_len = 2 + mask_len; | |
| 3062 | } else if (len == 126 && buf_len >= 4 + mask_len) { | |
| 3063 | header_len = 4 + mask_len; | |
| 3064 | data_len = ((((size_t) buf[2]) << 8) + buf[3]); | |
| 3065 | } else if (buf_len >= 10 + mask_len) { | |
| 3066 | header_len = 10 + mask_len; | |
| 3067 | data_len = (size_t) (((uint64_t) htonl(* (uint32_t *) &buf[2])) << 32) + | |
| 3068 | htonl(* (uint32_t *) &buf[6]); | |
| 3069 | } | |
| 3070 | } | |
| 3071 | ||
| 3072 | frame_len = header_len + data_len; | |
| 3073 | buffered = frame_len > 0 && frame_len <= buf_len; | |
| 3074 | ||
| 3075 | if (buffered) { | |
| 3076 | conn->mg_conn.content_len = data_len; | |
| 3077 | conn->mg_conn.content = (char *) buf + header_len; | |
| 3078 | conn->mg_conn.wsbits = buf[0]; | |
| 3079 | ||
| 3080 | // Apply mask if necessary | |
| 3081 | if (mask_len > 0) { | |
| 3082 | for (i = 0; i < data_len; i++) { | |
| 3083 | buf[i + header_len] ^= (buf + header_len - mask_len)[i % 4]; | |
| 3084 | } | |
| 3085 | } | |
| 3086 | ||
| 3087 | // Call the handler and remove frame from the iobuf | |
| 3088 | if (call_user(conn, MG_REQUEST) == MG_FALSE || | |
| 3089 | (buf[0] & 0x0f) == WEBSOCKET_OPCODE_CONNECTION_CLOSE) { | |
| 3090 | conn->ns_conn->flags |= NSF_FINISHED_SENDING_DATA; | |
| 3091 | } | |
| 3092 | iobuf_remove(&conn->ns_conn->recv_iobuf, frame_len); | |
| 3093 | } | |
| 3094 | ||
| 3095 | return buffered; | |
| 3096 | } | |
| 3097 | ||
| 3098 | size_t mg_websocket_write(struct mg_connection *conn, int opcode, | |
| 3099 | const char *data, size_t data_len) { | |
| 3100 | unsigned char mem[4192], *copy = mem; | |
| 3101 | size_t copy_len = 0; | |
| 3102 | ||
| 3103 | /* Check overflow */ | |
| 3104 | if (data_len > ~(size_t)0 - (size_t)10) { | |
| 3105 | return 0; | |
| 3106 | } | |
| 3107 | ||
| 3108 | if (data_len + 10 > sizeof(mem) && | |
| 3109 | (copy = (unsigned char *) NS_MALLOC(data_len + 10)) == NULL) { | |
| 3110 | return 0; | |
| 3111 | } | |
| 3112 | ||
| 3113 | copy[0] = 0x80 + (opcode & 0x0f); | |
| 3114 | ||
| 3115 | // Frame format: http://tools.ietf.org/html/rfc6455#section-5.2 | |
| 3116 | if (data_len < 126) { | |
| 3117 | // Inline 7-bit length field | |
| 3118 | copy[1] = data_len; | |
| 3119 | memcpy(copy + 2, data, data_len); | |
| 3120 | copy_len = 2 + data_len; | |
| 3121 | } else if (data_len <= 0xFFFF) { | |
| 3122 | // 16-bit length field | |
| 3123 | copy[1] = 126; | |
| 3124 | * (uint16_t *) (copy + 2) = (uint16_t) htons((uint16_t) data_len); | |
| 3125 | memcpy(copy + 4, data, data_len); | |
| 3126 | copy_len = 4 + data_len; | |
| 3127 | } else { | |
| 3128 | // 64-bit length field | |
| 3129 | const uint32_t hi = htonl((uint32_t) ((uint64_t) data_len >> 32)); | |
| 3130 | const uint32_t lo = htonl(data_len & 0xffffffff); | |
| 3131 | copy[1] = 127; | |
| 3132 | memcpy(copy+2,&hi,sizeof(hi)); | |
| 3133 | memcpy(copy+6,&lo,sizeof(lo)); | |
| 3134 | memcpy(copy + 10, data, data_len); | |
| 3135 | copy_len = 10 + data_len; | |
| 3136 | } | |
| 3137 | ||
| 3138 | if (copy_len > 0) { | |
| 3139 | mg_write(conn, copy, copy_len); | |
| 3140 | } | |
| 3141 | if (copy != mem) { | |
| 3142 | NS_FREE(copy); | |
| 3143 | } | |
| 3144 | ||
| 3145 | // If we send closing frame, schedule a connection to be closed after | |
| 3146 | // data is drained to the client. | |
| 3147 | if (opcode == WEBSOCKET_OPCODE_CONNECTION_CLOSE) { | |
| 3148 | MG_CONN_2_CONN(conn)->ns_conn->flags |= NSF_FINISHED_SENDING_DATA; | |
| 3149 | } | |
| 3150 | ||
| 3151 | return MG_CONN_2_CONN(conn)->ns_conn->send_iobuf.len; | |
| 3152 | } | |
| 3153 | ||
| 3154 | size_t mg_websocket_printf(struct mg_connection *conn, int opcode, | |
| 3155 | const char *fmt, ...) { | |
| 3156 | char mem[4192], *buf = mem; | |
| 3157 | va_list ap; | |
| 3158 | int len; | |
| 3159 | ||
| 3160 | va_start(ap, fmt); | |
| 3161 | if ((len = ns_avprintf(&buf, sizeof(mem), fmt, ap)) > 0) { | |
| 3162 | mg_websocket_write(conn, opcode, buf, len); | |
| 3163 | } | |
| 3164 | va_end(ap); | |
| 3165 | ||
| 3166 | if (buf != mem && buf != NULL) { | |
| 3167 | NS_FREE(buf); | |
| 3168 | } | |
| 3169 | ||
| 3170 | return MG_CONN_2_CONN(conn)->ns_conn->send_iobuf.len; | |
| 3171 | } | |
| 3172 | ||
| 3173 | static void send_websocket_handshake_if_requested(struct mg_connection *conn) { | |
| 3174 | const char *ver = mg_get_header(conn, "Sec-WebSocket-Version"), | |
| 3175 | *key = mg_get_header(conn, "Sec-WebSocket-Key"); | |
| 3176 | if (ver != NULL && key != NULL) { | |
| 3177 | conn->is_websocket = 1; | |
| 3178 | if (call_user(MG_CONN_2_CONN(conn), MG_WS_HANDSHAKE) == MG_FALSE) { | |
| 3179 | send_websocket_handshake(conn, key); | |
| 3180 | } | |
| 3181 | call_user(MG_CONN_2_CONN(conn), MG_WS_CONNECT); | |
| 3182 | } | |
| 3183 | } | |
| 3184 | ||
| 3185 | static void ping_idle_websocket_connection(struct connection *conn, time_t t) { | |
| 3186 | if (t - conn->ns_conn->last_io_time > MONGOOSE_USE_WEBSOCKET_PING_INTERVAL) { | |
| 3187 | mg_websocket_write(&conn->mg_conn, WEBSOCKET_OPCODE_PING, "", 0); | |
| 3188 | } | |
| 3189 | } | |
| 3190 | #else | |
| 3191 | #define ping_idle_websocket_connection(conn, t) | |
| 3192 | #endif // !MONGOOSE_NO_WEBSOCKET | |
| 3193 | ||
| 3194 | static void write_terminating_chunk(struct connection *conn) { | |
| 3195 | mg_write(&conn->mg_conn, "0\r\n\r\n", 5); | |
| 3196 | } | |
| 3197 | ||
| 3198 | static int call_request_handler(struct connection *conn) { | |
| 3199 | int result; | |
| 3200 | conn->mg_conn.content = conn->ns_conn->recv_iobuf.buf; | |
| 3201 | if ((result = call_user(conn, MG_REQUEST)) == MG_TRUE) { | |
| 3202 | if (conn->ns_conn->flags & MG_USING_CHUNKED_API) { | |
| 3203 | terminate_headers(&conn->mg_conn); | |
| 3204 | write_terminating_chunk(conn); | |
| 3205 | } | |
| 3206 | close_local_endpoint(conn); | |
| 3207 | } | |
| 3208 | return result; | |
| 3209 | } | |
| 3210 | ||
| 3211 | const char *mg_get_mime_type(const char *path, const char *default_mime_type) { | |
| 3212 | const char *ext; | |
| 3213 | size_t i, path_len; | |
| 3214 | ||
| 3215 | path_len = strlen(path); | |
| 3216 | ||
| 3217 | for (i = 0; static_builtin_mime_types[i].extension != NULL; i++) { | |
| 3218 | ext = path + (path_len - static_builtin_mime_types[i].ext_len); | |
| 3219 | if (path_len > static_builtin_mime_types[i].ext_len && | |
| 3220 | mg_strcasecmp(ext, static_builtin_mime_types[i].extension) == 0) { | |
| 3221 | return static_builtin_mime_types[i].mime_type; | |
| 3222 | } | |
| 3223 | } | |
| 3224 | ||
| 3225 | return default_mime_type; | |
| 3226 | } | |
| 3227 | ||
| 3228 | #ifndef MONGOOSE_NO_FILESYSTEM | |
| 3229 | // Convert month to the month number. Return -1 on error, or month number | |
| 3230 | static int get_month_index(const char *s) { | |
| 3231 | static const char *month_names[] = { | |
| 3232 | "Jan", "Feb", "Mar", "Apr", "May", "Jun", | |
| 3233 | "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" | |
| 3234 | }; | |
| 3235 | int i; | |
| 3236 | ||
| 3237 | for (i = 0; i < (int) ARRAY_SIZE(month_names); i++) | |
| 3238 | if (!strcmp(s, month_names[i])) | |
| 3239 | return i; | |
| 3240 | ||
| 3241 | return -1; | |
| 3242 | } | |
| 3243 | ||
| 3244 | static int num_leap_years(int year) { | |
| 3245 | return year / 4 - year / 100 + year / 400; | |
| 3246 | } | |
| 3247 | ||
| 3248 | // Parse UTC date-time string, and return the corresponding time_t value. | |
| 3249 | static time_t parse_date_string(const char *datetime) { | |
| 3250 | static const unsigned short days_before_month[] = { | |
| 3251 | 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 | |
| 3252 | }; | |
| 3253 | char month_str[32]; | |
| 3254 | int second, minute, hour, day, month, year, leap_days, days; | |
| 3255 | time_t result = (time_t) 0; | |
| 3256 | ||
| 3257 | if (((sscanf(datetime, "%d/%3s/%d %d:%d:%d", | |
| 3258 | &day, month_str, &year, &hour, &minute, &second) == 6) || | |
| 3259 | (sscanf(datetime, "%d %3s %d %d:%d:%d", | |
| 3260 | &day, month_str, &year, &hour, &minute, &second) == 6) || | |
| 3261 | (sscanf(datetime, "%*3s, %d %3s %d %d:%d:%d", | |
| 3262 | &day, month_str, &year, &hour, &minute, &second) == 6) || | |
| 3263 | (sscanf(datetime, "%d-%3s-%d %d:%d:%d", | |
| 3264 | &day, month_str, &year, &hour, &minute, &second) == 6)) && | |
| 3265 | year > 1970 && | |
| 3266 | (month = get_month_index(month_str)) != -1) { | |
| 3267 | leap_days = num_leap_years(year) - num_leap_years(1970); | |
| 3268 | year -= 1970; | |
| 3269 | days = year * 365 + days_before_month[month] + (day - 1) + leap_days; | |
| 3270 | result = days * 24 * 3600 + hour * 3600 + minute * 60 + second; | |
| 3271 | } | |
| 3272 | ||
| 3273 | return result; | |
| 3274 | } | |
| 3275 | ||
| 3276 | // Look at the "path" extension and figure what mime type it has. | |
| 3277 | // Store mime type in the vector. | |
| 3278 | static void get_mime_type(const struct mg_server *server, const char *path, | |
| 3279 | struct vec *vec) { | |
| 3280 | struct vec ext_vec, mime_vec; | |
| 3281 | const char *list, *ext; | |
| 3282 | size_t path_len; | |
| 3283 | ||
| 3284 | path_len = strlen(path); | |
| 3285 | ||
| 3286 | // Scan user-defined mime types first, in case user wants to | |
| 3287 | // override default mime types. | |
| 3288 | list = server->config_options[EXTRA_MIME_TYPES]; | |
| 3289 | while ((list = next_option(list, &ext_vec, &mime_vec)) != NULL) { | |
| 3290 | // ext now points to the path suffix | |
| 3291 | ext = path + path_len - ext_vec.len; | |
| 3292 | if (mg_strncasecmp(ext, ext_vec.ptr, ext_vec.len) == 0) { | |
| 3293 | *vec = mime_vec; | |
| 3294 | return; | |
| 3295 | } | |
| 3296 | } | |
| 3297 | ||
| 3298 | vec->ptr = mg_get_mime_type(path, "text/plain"); | |
| 3299 | vec->len = strlen(vec->ptr); | |
| 3300 | } | |
| 3301 | ||
| 3302 | static const char *suggest_connection_header(const struct mg_connection *conn) { | |
| 3303 | return should_keep_alive(conn) ? "keep-alive" : "close"; | |
| 3304 | } | |
| 3305 | ||
| 3306 | static void construct_etag(char *buf, size_t buf_len, const file_stat_t *st) { | |
| 3307 | mg_snprintf(buf, buf_len, "\"%lx.%" INT64_FMT "\"", | |
| 3308 | (unsigned long) st->st_mtime, (int64_t) st->st_size); | |
| 3309 | } | |
| 3310 | ||
| 3311 | // Return True if we should reply 304 Not Modified. | |
| 3312 | static int is_not_modified(const struct connection *conn, | |
| 3313 | const file_stat_t *stp) { | |
| 3314 | char etag[64]; | |
| 3315 | const char *ims = mg_get_header(&conn->mg_conn, "If-Modified-Since"); | |
| 3316 | const char *inm = mg_get_header(&conn->mg_conn, "If-None-Match"); | |
| 3317 | construct_etag(etag, sizeof(etag), stp); | |
| 3318 | return (inm != NULL && !mg_strcasecmp(etag, inm)) || | |
| 3319 | (ims != NULL && stp->st_mtime <= parse_date_string(ims)); | |
| 3320 | } | |
| 3321 | ||
| 3322 | // For given directory path, substitute it to valid index file. | |
| 3323 | // Return 0 if index file has been found, -1 if not found. | |
| 3324 | // If the file is found, it's stats is returned in stp. | |
| 3325 | static int find_index_file(struct connection *conn, char *path, | |
| 3326 | size_t path_len, file_stat_t *stp) { | |
| 3327 | const char *list = conn->server->config_options[INDEX_FILES]; | |
| 3328 | file_stat_t st; | |
| 3329 | struct vec filename_vec; | |
| 3330 | size_t n = strlen(path); | |
| 3331 | int found = 0; | |
| 3332 | ||
| 3333 | // The 'path' given to us points to the directory. Remove all trailing | |
| 3334 | // directory separator characters from the end of the path, and | |
| 3335 | // then append single directory separator character. | |
| 3336 | while (n > 0 && path[n - 1] == '/') { | |
| 3337 | n--; | |
| 3338 | } | |
| 3339 | path[n] = '/'; | |
| 3340 | ||
| 3341 | // Traverse index files list. For each entry, append it to the given | |
| 3342 | // path and see if the file exists. If it exists, break the loop | |
| 3343 | while ((list = next_option(list, &filename_vec, NULL)) != NULL) { | |
| 3344 | ||
| 3345 | if (path_len <= n + 2) { | |
| 3346 | continue; | |
| 3347 | } | |
| 3348 | ||
| 3349 | // Ignore too long entries that may overflow path buffer | |
| 3350 | if (filename_vec.len > (path_len - (n + 2))) | |
| 3351 | continue; | |
| 3352 | ||
| 3353 | // Prepare full path to the index file | |
| 3354 | strncpy(path + n + 1, filename_vec.ptr, filename_vec.len); | |
| 3355 | path[n + 1 + filename_vec.len] = '\0'; | |
| 3356 | ||
| 3357 | //DBG(("[%s]", path)); | |
| 3358 | ||
| 3359 | // Does it exist? | |
| 3360 | if (!stat(path, &st)) { | |
| 3361 | // Yes it does, break the loop | |
| 3362 | *stp = st; | |
| 3363 | found = 1; | |
| 3364 | break; | |
| 3365 | } | |
| 3366 | } | |
| 3367 | ||
| 3368 | // If no index file exists, restore directory path | |
| 3369 | if (!found) { | |
| 3370 | path[n] = '/'; | |
| 3371 | path[n + 1] = '\0'; | |
| 3372 | } | |
| 3373 | ||
| 3374 | return found; | |
| 3375 | } | |
| 3376 | ||
| 3377 | static int parse_range_header(const char *header, int64_t *a, int64_t *b) { | |
| 3378 | // return sscanf(header, "bytes=%" INT64_FMT "-%" INT64_FMT, a, b); | |
| 3379 | // return sscanf(header, "bytes=%ld-%ld" INT64_FMT, a, b); | |
| 3380 | return 0; | |
| 3381 | } | |
| 3382 | ||
| 3383 | static void gmt_time_string(char *buf, size_t buf_len, time_t *t) { | |
| 3384 | strftime(buf, buf_len, "%a, %d %b %Y %H:%M:%S GMT", gmtime(t)); | |
| 3385 | } | |
| 3386 | ||
| 3387 | static void open_file_endpoint(struct connection *conn, const char *path, | |
| 3388 | file_stat_t *st, const char *extra_headers) { | |
| 3389 | char date[64], lm[64], etag[64], range[64], headers[1000]; | |
| 3390 | const char *msg = "OK", *hdr; | |
| 3391 | time_t t, curtime = time(NULL); | |
| 3392 | int64_t r1, r2; | |
| 3393 | struct vec mime_vec; | |
| 3394 | int n; | |
| 3395 | ||
| 3396 | conn->endpoint_type = EP_FILE; | |
| 3397 | ns_set_close_on_exec(conn->endpoint.fd); | |
| 3398 | conn->mg_conn.status_code = 200; | |
| 3399 | ||
| 3400 | get_mime_type(conn->server, path, &mime_vec); | |
| 3401 | conn->cl = st->st_size; | |
| 3402 | range[0] = '\0'; | |
| 3403 | ||
| 3404 | // If Range: header specified, act accordingly | |
| 3405 | r1 = r2 = 0; | |
| 3406 | hdr = mg_get_header(&conn->mg_conn, "Range"); | |
| 3407 | if (hdr != NULL && (n = parse_range_header(hdr, &r1, &r2)) > 0 && | |
| 3408 | r1 >= 0 && r2 >= 0) { | |
| 3409 | conn->mg_conn.status_code = 206; | |
| 3410 | conn->cl = n == 2 ? (r2 > conn->cl ? conn->cl : r2) - r1 + 1: conn->cl - r1; | |
| 3411 | mg_snprintf(range, sizeof(range), "Content-Range: bytes " | |
| 3412 | "%" INT64_FMT "-%" INT64_FMT "/%" INT64_FMT "\r\n", | |
| 3413 | r1, r1 + conn->cl - 1, (int64_t) st->st_size); | |
| 3414 | msg = "Partial Content"; | |
| 3415 | lseek(conn->endpoint.fd, r1, SEEK_SET); | |
| 3416 | } | |
| 3417 | ||
| 3418 | // Prepare Etag, Date, Last-Modified headers. Must be in UTC, according to | |
| 3419 | // http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3 | |
| 3420 | gmt_time_string(date, sizeof(date), &curtime); | |
| 3421 | t = st->st_mtime; // store in local variable for NDK compile | |
| 3422 | gmt_time_string(lm, sizeof(lm), &t); | |
| 3423 | construct_etag(etag, sizeof(etag), st); | |
| 3424 | ||
| 3425 | n = mg_snprintf(headers, sizeof(headers), | |
| 3426 | "HTTP/1.1 %d %s\r\n" | |
| 3427 | "Date: %s\r\n" | |
| 3428 | "Last-Modified: %s\r\n" | |
| 3429 | "Etag: %s\r\n" | |
| 3430 | "Content-Type: %.*s\r\n" | |
| 3431 | "Content-Length: %" INT64_FMT "\r\n" | |
| 3432 | "Connection: %s\r\n" | |
| 3433 | "Accept-Ranges: bytes\r\n" | |
| 3434 | "%s%s%s\r\n", | |
| 3435 | conn->mg_conn.status_code, msg, date, lm, etag, | |
| 3436 | (int) mime_vec.len, mime_vec.ptr, conn->cl, | |
| 3437 | suggest_connection_header(&conn->mg_conn), | |
| 3438 | range, extra_headers == NULL ? "" : extra_headers, | |
| 3439 | MONGOOSE_USE_EXTRA_HTTP_HEADERS); | |
| 3440 | ns_send(conn->ns_conn, headers, n); | |
| 3441 | ||
| 3442 | if (!strcmp(conn->mg_conn.request_method, "HEAD")) { | |
| 3443 | conn->ns_conn->flags |= NSF_FINISHED_SENDING_DATA; | |
| 3444 | close(conn->endpoint.fd); | |
| 3445 | conn->endpoint_type = EP_NONE; | |
| 3446 | } | |
| 3447 | } | |
| 3448 | ||
| 3449 | void mg_send_file_data(struct mg_connection *c, int fd) { | |
| 3450 | struct connection *conn = MG_CONN_2_CONN(c); | |
| 3451 | conn->endpoint_type = EP_FILE; | |
| 3452 | conn->endpoint.fd = fd; | |
| 3453 | ns_set_close_on_exec(conn->endpoint.fd); | |
| 3454 | } | |
| 3455 | #endif // MONGOOSE_NO_FILESYSTEM | |
| 3456 | ||
| 3457 | static void call_request_handler_if_data_is_buffered(struct connection *conn) { | |
| 3458 | #ifndef MONGOOSE_NO_WEBSOCKET | |
| 3459 | if (conn->mg_conn.is_websocket) { | |
| 3460 | do { } while (deliver_websocket_frame(conn)); | |
| 3461 | } else | |
| 3462 | #endif | |
| 3463 | if (conn->num_bytes_recv >= (conn->cl + conn->request_len) && | |
| 3464 | call_request_handler(conn) == MG_FALSE) { | |
| 3465 | open_local_endpoint(conn, 1); | |
| 3466 | } | |
| 3467 | } | |
| 3468 | ||
| 3469 | #if !defined(MONGOOSE_NO_DIRECTORY_LISTING) || !defined(MONGOOSE_NO_DAV) | |
| 3470 | ||
| 3471 | #ifdef _WIN32 | |
| 3472 | struct dirent { | |
| 3473 | char d_name[MAX_PATH_SIZE]; | |
| 3474 | }; | |
| 3475 | ||
| 3476 | typedef struct DIR { | |
| 3477 | HANDLE handle; | |
| 3478 | WIN32_FIND_DATAW info; | |
| 3479 | struct dirent result; | |
| 3480 | } DIR; | |
| 3481 | ||
| 3482 | // Implementation of POSIX opendir/closedir/readdir for Windows. | |
| 3483 | static DIR *opendir(const char *name) { | |
| 3484 | DIR *dir = NULL; | |
| 3485 | wchar_t wpath[MAX_PATH_SIZE]; | |
| 3486 | DWORD attrs; | |
| 3487 | ||
| 3488 | if (name == NULL) { | |
| 3489 | SetLastError(ERROR_BAD_ARGUMENTS); | |
| 3490 | } else if ((dir = (DIR *) NS_MALLOC(sizeof(*dir))) == NULL) { | |
| 3491 | SetLastError(ERROR_NOT_ENOUGH_MEMORY); | |
| 3492 | } else { | |
| 3493 | to_wchar(name, wpath, ARRAY_SIZE(wpath)); | |
| 3494 | attrs = GetFileAttributesW(wpath); | |
| 3495 | if (attrs != 0xFFFFFFFF && | |
| 3496 | ((attrs & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)) { | |
| 3497 | (void) wcscat(wpath, L"\\*"); | |
| 3498 | dir->handle = FindFirstFileW(wpath, &dir->info); | |
| 3499 | dir->result.d_name[0] = '\0'; | |
| 3500 | } else { | |
| 3501 | NS_FREE(dir); | |
| 3502 | dir = NULL; | |
| 3503 | } | |
| 3504 | } | |
| 3505 | ||
| 3506 | return dir; | |
| 3507 | } | |
| 3508 | ||
| 3509 | static int closedir(DIR *dir) { | |
| 3510 | int result = 0; | |
| 3511 | ||
| 3512 | if (dir != NULL) { | |
| 3513 | if (dir->handle != INVALID_HANDLE_VALUE) | |
| 3514 | result = FindClose(dir->handle) ? 0 : -1; | |
| 3515 | ||
| 3516 | NS_FREE(dir); | |
| 3517 | } else { | |
| 3518 | result = -1; | |
| 3519 | SetLastError(ERROR_BAD_ARGUMENTS); | |
| 3520 | } | |
| 3521 | ||
| 3522 | return result; | |
| 3523 | } | |
| 3524 | ||
| 3525 | static struct dirent *readdir(DIR *dir) { | |
| 3526 | struct dirent *result = 0; | |
| 3527 | ||
| 3528 | if (dir) { | |
| 3529 | if (dir->handle != INVALID_HANDLE_VALUE) { | |
| 3530 | result = &dir->result; | |
| 3531 | (void) WideCharToMultiByte(CP_UTF8, 0, | |
| 3532 | dir->info.cFileName, -1, result->d_name, | |
| 3533 | sizeof(result->d_name), NULL, NULL); | |
| 3534 | ||
| 3535 | if (!FindNextFileW(dir->handle, &dir->info)) { | |
| 3536 | (void) FindClose(dir->handle); | |
| 3537 | dir->handle = INVALID_HANDLE_VALUE; | |
| 3538 | } | |
| 3539 | ||
| 3540 | } else { | |
| 3541 | SetLastError(ERROR_FILE_NOT_FOUND); | |
| 3542 | } | |
| 3543 | } else { | |
| 3544 | SetLastError(ERROR_BAD_ARGUMENTS); | |
| 3545 | } | |
| 3546 | ||
| 3547 | return result; | |
| 3548 | } | |
| 3549 | #endif // _WIN32 POSIX opendir/closedir/readdir implementation | |
| 3550 | ||
| 3551 | static int scan_directory(struct connection *conn, const char *dir, | |
| 3552 | struct dir_entry **arr) { | |
| 3553 | char path[MAX_PATH_SIZE]; | |
| 3554 | struct dir_entry *p; | |
| 3555 | struct dirent *dp; | |
| 3556 | int arr_size = 0, arr_ind = 0, inc = 100; | |
| 3557 | DIR *dirp; | |
| 3558 | ||
| 3559 | *arr = NULL; | |
| 3560 | if ((dirp = (opendir(dir))) == NULL) return 0; | |
| 3561 | ||
| 3562 | while ((dp = readdir(dirp)) != NULL) { | |
| 3563 | // Do not show current dir and hidden files | |
| 3564 | if (!strcmp(dp->d_name, ".") || | |
| 3565 | !strcmp(dp->d_name, "..") || | |
| 3566 | must_hide_file(conn, dp->d_name)) { | |
| 3567 | continue; | |
| 3568 | } | |
| 3569 | mg_snprintf(path, sizeof(path), "%s%c%s", dir, '/', dp->d_name); | |
| 3570 | ||
| 3571 | // Resize the array if necessary | |
| 3572 | if (arr_ind >= arr_size) { | |
| 3573 | if ((p = (struct dir_entry *) | |
| 3574 | NS_REALLOC(*arr, (inc + arr_size) * sizeof(**arr))) != NULL) { | |
| 3575 | // Memset new chunk to zero, otherwise st_mtime will have garbage which | |
| 3576 | // can make strftime() segfault, see | |
| 3577 | // http://code.google.com/p/mongoose/issues/detail?id=79 | |
| 3578 | memset(p + arr_size, 0, sizeof(**arr) * inc); | |
| 3579 | ||
| 3580 | *arr = p; | |
| 3581 | arr_size += inc; | |
| 3582 | } | |
| 3583 | } | |
| 3584 | ||
| 3585 | if (arr_ind < arr_size) { | |
| 3586 | (*arr)[arr_ind].conn = conn; | |
| 3587 | (*arr)[arr_ind].file_name = strdup(dp->d_name); | |
| 3588 | stat(path, &(*arr)[arr_ind].st); | |
| 3589 | arr_ind++; | |
| 3590 | } | |
| 3591 | } | |
| 3592 | closedir(dirp); | |
| 3593 | ||
| 3594 | return arr_ind; | |
| 3595 | } | |
| 3596 | ||
| 3597 | size_t mg_url_encode(const char *src, size_t s_len, char *dst, size_t dst_len) { | |
| 3598 | static const char *dont_escape = "._-$,;~()"; | |
| 3599 | static const char *hex = "0123456789abcdef"; | |
| 3600 | size_t i = 0, j = 0; | |
| 3601 | ||
| 3602 | for (i = j = 0; dst_len > 0 && i < s_len && j + 2 < dst_len - 1; i++, j++) { | |
| 3603 | if (isalnum(* (const unsigned char *) (src + i)) || | |
| 3604 | strchr(dont_escape, * (const unsigned char *) (src + i)) != NULL) { | |
| 3605 | dst[j] = src[i]; | |
| 3606 | } else if (j + 3 < dst_len) { | |
| 3607 | dst[j] = '%'; | |
| 3608 | dst[j + 1] = hex[(* (const unsigned char *) (src + i)) >> 4]; | |
| 3609 | dst[j + 2] = hex[(* (const unsigned char *) (src + i)) & 0xf]; | |
| 3610 | j += 2; | |
| 3611 | } | |
| 3612 | } | |
| 3613 | ||
| 3614 | dst[j] = '\0'; | |
| 3615 | return j; | |
| 3616 | } | |
| 3617 | #endif // !NO_DIRECTORY_LISTING || !MONGOOSE_NO_DAV | |
| 3618 | ||
| 3619 | #ifndef MONGOOSE_NO_DIRECTORY_LISTING | |
| 3620 | ||
| 3621 | static void print_dir_entry(const struct dir_entry *de) { | |
| 3622 | char size[64], mod[64], href[MAX_PATH_SIZE * 3]; | |
| 3623 | int64_t fsize = de->st.st_size; | |
| 3624 | int is_dir = S_ISDIR(de->st.st_mode); | |
| 3625 | const char *slash = is_dir ? "/" : ""; | |
| 3626 | time_t t; | |
| 3627 | ||
| 3628 | if (is_dir) { | |
| 3629 | mg_snprintf(size, sizeof(size), "%s", "[DIRECTORY]"); | |
| 3630 | } else { | |
| 3631 | // We use (signed) cast below because MSVC 6 compiler cannot | |
| 3632 | // convert unsigned __int64 to double. | |
| 3633 | if (fsize < 1024) { | |
| 3634 | mg_snprintf(size, sizeof(size), "%d", (int) fsize); | |
| 3635 | } else if (fsize < 0x100000) { | |
| 3636 | mg_snprintf(size, sizeof(size), "%.1fk", (double) fsize / 1024.0); | |
| 3637 | } else if (fsize < 0x40000000) { | |
| 3638 | mg_snprintf(size, sizeof(size), "%.1fM", (double) fsize / 1048576); | |
| 3639 | } else { | |
| 3640 | mg_snprintf(size, sizeof(size), "%.1fG", (double) fsize / 1073741824); | |
| 3641 | } | |
| 3642 | } | |
| 3643 | t = de->st.st_mtime; // store in local variable for NDK compile | |
| 3644 | strftime(mod, sizeof(mod), "%d-%b-%Y %H:%M", localtime(&t)); | |
| 3645 | mg_url_encode(de->file_name, strlen(de->file_name), href, sizeof(href)); | |
| 3646 | mg_printf_data(&de->conn->mg_conn, | |
| 3647 | "<tr><td><a href=\"%s%s\">%s%s</a></td>" | |
| 3648 | "<td> %s</td><td> %s</td></tr>\n", | |
| 3649 | href, slash, de->file_name, slash, mod, size); | |
| 3650 | } | |
| 3651 | ||
| 3652 | // Sort directory entries by size, or name, or modification time. | |
| 3653 | // On windows, __cdecl specification is needed in case if project is built | |
| 3654 | // with __stdcall convention. qsort always requires __cdels callback. | |
| 3655 | static int __cdecl compare_dir_entries(const void *p1, const void *p2) { | |
| 3656 | const struct dir_entry *a = (const struct dir_entry *) p1, | |
| 3657 | *b = (const struct dir_entry *) p2; | |
| 3658 | const char *qs = a->conn->mg_conn.query_string ? | |
| 3659 | a->conn->mg_conn.query_string : "na"; | |
| 3660 | int cmp_result = 0; | |
| 3661 | ||
| 3662 | if (S_ISDIR(a->st.st_mode) && !S_ISDIR(b->st.st_mode)) { | |
| 3663 | return -1; // Always put directories on top | |
| 3664 | } else if (!S_ISDIR(a->st.st_mode) && S_ISDIR(b->st.st_mode)) { | |
| 3665 | return 1; // Always put directories on top | |
| 3666 | } else if (*qs == 'n') { | |
| 3667 | cmp_result = strcmp(a->file_name, b->file_name); | |
| 3668 | } else if (*qs == 's') { | |
| 3669 | cmp_result = a->st.st_size == b->st.st_size ? 0 : | |
| 3670 | a->st.st_size > b->st.st_size ? 1 : -1; | |
| 3671 | } else if (*qs == 'd') { | |
| 3672 | cmp_result = a->st.st_mtime == b->st.st_mtime ? 0 : | |
| 3673 | a->st.st_mtime > b->st.st_mtime ? 1 : -1; | |
| 3674 | } | |
| 3675 | ||
| 3676 | return qs[1] == 'd' ? -cmp_result : cmp_result; | |
| 3677 | } | |
| 3678 | ||
| 3679 | static void send_directory_listing(struct connection *conn, const char *dir) { | |
| 3680 | struct dir_entry *arr = NULL; | |
| 3681 | int i, num_entries, sort_direction = conn->mg_conn.query_string != NULL && | |
| 3682 | conn->mg_conn.query_string[1] == 'd' ? 'a' : 'd'; | |
| 3683 | ||
| 3684 | mg_send_header(&conn->mg_conn, "Transfer-Encoding", "chunked"); | |
| 3685 | mg_send_header(&conn->mg_conn, "Content-Type", "text/html; charset=utf-8"); | |
| 3686 | ||
| 3687 | mg_printf_data(&conn->mg_conn, | |
| 3688 | "<html><head><title>Index of %s</title>" | |
| 3689 | "<style>th {text-align: left;}</style></head>" | |
| 3690 | "<body><h1>Index of %s</h1><pre><table cellpadding=\"0\">" | |
| 3691 | "<tr><th><a href=\"?n%c\">Name</a></th>" | |
| 3692 | "<th><a href=\"?d%c\">Modified</a></th>" | |
| 3693 | "<th><a href=\"?s%c\">Size</a></th></tr>" | |
| 3694 | "<tr><td colspan=\"3\"><hr></td></tr>", | |
| 3695 | conn->mg_conn.uri, conn->mg_conn.uri, | |
| 3696 | sort_direction, sort_direction, sort_direction); | |
| 3697 | ||
| 3698 | num_entries = scan_directory(conn, dir, &arr); | |
| 3699 | if (arr) { | |
| 3700 | qsort(arr, num_entries, sizeof(arr[0]), compare_dir_entries); | |
| 3701 | for (i = 0; i < num_entries; i++) { | |
| 3702 | print_dir_entry(&arr[i]); | |
| 3703 | NS_FREE(arr[i].file_name); | |
| 3704 | } | |
| 3705 | NS_FREE(arr); | |
| 3706 | } | |
| 3707 | ||
| 3708 | write_terminating_chunk(conn); | |
| 3709 | close_local_endpoint(conn); | |
| 3710 | } | |
| 3711 | #endif // MONGOOSE_NO_DIRECTORY_LISTING | |
| 3712 | ||
| 3713 | #ifndef MONGOOSE_NO_DAV | |
| 3714 | static void print_props(struct connection *conn, const char *uri, | |
| 3715 | file_stat_t *stp) { | |
| 3716 | char mtime[64]; | |
| 3717 | time_t t = stp->st_mtime; // store in local variable for NDK compile | |
| 3718 | gmt_time_string(mtime, sizeof(mtime), &t); | |
| 3719 | mg_printf(&conn->mg_conn, | |
| 3720 | "<d:response>" | |
| 3721 | "<d:href>%s</d:href>" | |
| 3722 | "<d:propstat>" | |
| 3723 | "<d:prop>" | |
| 3724 | "<d:resourcetype>%s</d:resourcetype>" | |
| 3725 | "<d:getcontentlength>%" INT64_FMT "</d:getcontentlength>" | |
| 3726 | "<d:getlastmodified>%s</d:getlastmodified>" | |
| 3727 | "</d:prop>" | |
| 3728 | "<d:status>HTTP/1.1 200 OK</d:status>" | |
| 3729 | "</d:propstat>" | |
| 3730 | "</d:response>\n", | |
| 3731 | uri, S_ISDIR(stp->st_mode) ? "<d:collection/>" : "", | |
| 3732 | (int64_t) stp->st_size, mtime); | |
| 3733 | } | |
| 3734 | ||
| 3735 | static void handle_propfind(struct connection *conn, const char *path, | |
| 3736 | file_stat_t *stp, int exists) { | |
| 3737 | static const char header[] = "HTTP/1.1 207 Multi-Status\r\n" | |
| 3738 | "Connection: close\r\n" | |
| 3739 | "Content-Type: text/xml; charset=utf-8\r\n\r\n" | |
| 3740 | "<?xml version=\"1.0\" encoding=\"utf-8\"?>" | |
| 3741 | "<d:multistatus xmlns:d='DAV:'>\n"; | |
| 3742 | static const char footer[] = "</d:multistatus>"; | |
| 3743 | const char *depth = mg_get_header(&conn->mg_conn, "Depth"); | |
| 3744 | #ifdef MONGOOSE_NO_DIRECTORY_LISTING | |
| 3745 | const char *list_dir = "no"; | |
| 3746 | #else | |
| 3747 | const char *list_dir = conn->server->config_options[ENABLE_DIRECTORY_LISTING]; | |
| 3748 | #endif | |
| 3749 | ||
| 3750 | conn->mg_conn.status_code = 207; | |
| 3751 | ||
| 3752 | // Print properties for the requested resource itself | |
| 3753 | if (!exists) { | |
| 3754 | conn->mg_conn.status_code = 404; | |
| 3755 | mg_printf(&conn->mg_conn, "%s", "HTTP/1.1 404 Not Found\r\n\r\n"); | |
| 3756 | } else if (S_ISDIR(stp->st_mode) && mg_strcasecmp(list_dir, "yes") != 0) { | |
| 3757 | conn->mg_conn.status_code = 403; | |
| 3758 | mg_printf(&conn->mg_conn, "%s", | |
| 3759 | "HTTP/1.1 403 Directory Listing Denied\r\n\r\n"); | |
| 3760 | } else { | |
| 3761 | ns_send(conn->ns_conn, header, sizeof(header) - 1); | |
| 3762 | print_props(conn, conn->mg_conn.uri, stp); | |
| 3763 | ||
| 3764 | if (S_ISDIR(stp->st_mode) && | |
| 3765 | (depth == NULL || strcmp(depth, "0") != 0)) { | |
| 3766 | struct dir_entry *arr = NULL; | |
| 3767 | int i, num_entries = scan_directory(conn, path, &arr); | |
| 3768 | ||
| 3769 | for (i = 0; i < num_entries; i++) { | |
| 3770 | char buf[MAX_PATH_SIZE * 3]; | |
| 3771 | struct dir_entry *de = &arr[i]; | |
| 3772 | mg_url_encode(de->file_name, strlen(de->file_name), buf, sizeof(buf)); | |
| 3773 | print_props(conn, buf, &de->st); | |
| 3774 | NS_FREE(de->file_name); | |
| 3775 | } | |
| 3776 | NS_FREE(arr); | |
| 3777 | } | |
| 3778 | ns_send(conn->ns_conn, footer, sizeof(footer) - 1); | |
| 3779 | } | |
| 3780 | ||
| 3781 | close_local_endpoint(conn); | |
| 3782 | } | |
| 3783 | ||
| 3784 | static void handle_mkcol(struct connection *conn, const char *path) { | |
| 3785 | int status_code = 500; | |
| 3786 | ||
| 3787 | if (conn->mg_conn.content_len > 0) { | |
| 3788 | status_code = 415; | |
| 3789 | } else if (!mkdir(path, 0755)) { | |
| 3790 | status_code = 201; | |
| 3791 | } else if (errno == EEXIST) { | |
| 3792 | status_code = 405; | |
| 3793 | } else if (errno == EACCES) { | |
| 3794 | status_code = 403; | |
| 3795 | } else if (errno == ENOENT) { | |
| 3796 | status_code = 409; | |
| 3797 | } | |
| 3798 | send_http_error(conn, status_code, NULL); | |
| 3799 | } | |
| 3800 | ||
| 3801 | static int remove_directory(const char *dir) { | |
| 3802 | char path[MAX_PATH_SIZE]; | |
| 3803 | struct dirent *dp; | |
| 3804 | file_stat_t st; | |
| 3805 | DIR *dirp; | |
| 3806 | ||
| 3807 | if ((dirp = opendir(dir)) == NULL) return 0; | |
| 3808 | ||
| 3809 | while ((dp = readdir(dirp)) != NULL) { | |
| 3810 | if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) continue; | |
| 3811 | mg_snprintf(path, sizeof(path), "%s%c%s", dir, '/', dp->d_name); | |
| 3812 | stat(path, &st); | |
| 3813 | if (S_ISDIR(st.st_mode)) { | |
| 3814 | remove_directory(path); | |
| 3815 | } else { | |
| 3816 | remove(path); | |
| 3817 | } | |
| 3818 | } | |
| 3819 | closedir(dirp); | |
| 3820 | rmdir(dir); | |
| 3821 | ||
| 3822 | return 1; | |
| 3823 | } | |
| 3824 | ||
| 3825 | static void handle_delete(struct connection *conn, const char *path) { | |
| 3826 | file_stat_t st; | |
| 3827 | ||
| 3828 | if (stat(path, &st) != 0) { | |
| 3829 | send_http_error(conn, 404, NULL); | |
| 3830 | } else if (S_ISDIR(st.st_mode)) { | |
| 3831 | remove_directory(path); | |
| 3832 | send_http_error(conn, 204, NULL); | |
| 3833 | } else if (remove(path) == 0) { | |
| 3834 | send_http_error(conn, 204, NULL); | |
| 3835 | } else { | |
| 3836 | send_http_error(conn, 423, NULL); | |
| 3837 | } | |
| 3838 | } | |
| 3839 | ||
| 3840 | // For a given PUT path, create all intermediate subdirectories | |
| 3841 | // for given path. Return 0 if the path itself is a directory, | |
| 3842 | // or -1 on error, 1 if OK. | |
| 3843 | static int put_dir(const char *path) { | |
| 3844 | char buf[MAX_PATH_SIZE]; | |
| 3845 | const char *s, *p; | |
| 3846 | file_stat_t st; | |
| 3847 | ||
| 3848 | // Create intermediate directories if they do not exist | |
| 3849 | for (s = p = path + 1; (p = strchr(s, '/')) != NULL; s = ++p) { | |
| 3850 | if (p - path >= (int) sizeof(buf)) return -1; // Buffer overflow | |
| 3851 | memcpy(buf, path, p - path); | |
| 3852 | buf[p - path] = '\0'; | |
| 3853 | if (stat(buf, &st) != 0 && mkdir(buf, 0755) != 0) return -1; | |
| 3854 | if (p[1] == '\0') return 0; // Path is a directory itself | |
| 3855 | } | |
| 3856 | ||
| 3857 | return 1; | |
| 3858 | } | |
| 3859 | ||
| 3860 | static void handle_put(struct connection *conn, const char *path) { | |
| 3861 | file_stat_t st; | |
| 3862 | const char *range, *cl_hdr = mg_get_header(&conn->mg_conn, "Content-Length"); | |
| 3863 | int64_t r1, r2; | |
| 3864 | int rc; | |
| 3865 | ||
| 3866 | conn->mg_conn.status_code = !stat(path, &st) ? 200 : 201; | |
| 3867 | if ((rc = put_dir(path)) == 0) { | |
| 3868 | mg_printf(&conn->mg_conn, "HTTP/1.1 %d OK\r\n\r\n", | |
| 3869 | conn->mg_conn.status_code); | |
| 3870 | close_local_endpoint(conn); | |
| 3871 | } else if (rc == -1) { | |
| 3872 | send_http_error(conn, 500, "put_dir: %s", strerror(errno)); | |
| 3873 | } else if (cl_hdr == NULL) { | |
| 3874 | send_http_error(conn, 411, NULL); | |
| 3875 | } else if ((conn->endpoint.fd = | |
| 3876 | open(path, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0644)) < 0) { | |
| 3877 | send_http_error(conn, 500, "open(%s): %s", path, strerror(errno)); | |
| 3878 | } else { | |
| 3879 | DBG(("PUT [%s] %lu", path, (unsigned long) conn->ns_conn->recv_iobuf.len)); | |
| 3880 | conn->endpoint_type = EP_PUT; | |
| 3881 | ns_set_close_on_exec(conn->endpoint.fd); | |
| 3882 | range = mg_get_header(&conn->mg_conn, "Content-Range"); | |
| 3883 | conn->cl = to64(cl_hdr); | |
| 3884 | r1 = r2 = 0; | |
| 3885 | if (range != NULL && parse_range_header(range, &r1, &r2) > 0) { | |
| 3886 | conn->mg_conn.status_code = 206; | |
| 3887 | lseek(conn->endpoint.fd, r1, SEEK_SET); | |
| 3888 | conn->cl = r2 > r1 ? r2 - r1 + 1: conn->cl - r1; | |
| 3889 | } | |
| 3890 | mg_printf(&conn->mg_conn, "HTTP/1.1 %d OK\r\nContent-Length: 0\r\n\r\n", | |
| 3891 | conn->mg_conn.status_code); | |
| 3892 | } | |
| 3893 | } | |
| 3894 | ||
| 3895 | static void forward_put_data(struct connection *conn) { | |
| 3896 | struct iobuf *io = &conn->ns_conn->recv_iobuf; | |
| 3897 | size_t k = conn->cl < (int64_t) io->len ? conn->cl : (int64_t) io->len; // To write | |
| 3898 | size_t n = write(conn->endpoint.fd, io->buf, k); // Write them! | |
| 3899 | if (n > 0) { | |
| 3900 | iobuf_remove(io, n); | |
| 3901 | conn->cl -= n; | |
| 3902 | } | |
| 3903 | if (conn->cl <= 0) { | |
| 3904 | close_local_endpoint(conn); | |
| 3905 | } | |
| 3906 | } | |
| 3907 | #endif // MONGOOSE_NO_DAV | |
| 3908 | ||
| 3909 | static void send_options(struct connection *conn) { | |
| 3910 | conn->mg_conn.status_code = 200; | |
| 3911 | mg_printf(&conn->mg_conn, "%s", | |
| 3912 | "HTTP/1.1 200 OK\r\nAllow: GET, POST, HEAD, CONNECT, PUT, " | |
| 3913 | "DELETE, OPTIONS, PROPFIND, MKCOL\r\nDAV: 1\r\n\r\n"); | |
| 3914 | close_local_endpoint(conn); | |
| 3915 | } | |
| 3916 | ||
| 3917 | #ifndef MONGOOSE_NO_AUTH | |
| 3918 | void mg_send_digest_auth_request(struct mg_connection *c) { | |
| 3919 | struct connection *conn = MG_CONN_2_CONN(c); | |
| 3920 | c->status_code = 401; | |
| 3921 | mg_printf(c, | |
| 3922 | "HTTP/1.1 401 Unauthorized\r\n" | |
| 3923 | "Content-Length: 0\r\n" | |
| 3924 | "WWW-Authenticate: Digest qop=\"auth\", " | |
| 3925 | "realm=\"%s\", nonce=\"%lu\"\r\n\r\n", | |
| 3926 | conn->server->config_options[AUTH_DOMAIN], | |
| 3927 | (unsigned long) time(NULL)); | |
| 3928 | if (conn->cl > 0) { | |
| 3929 | conn->ns_conn->flags |= NSF_DISCARD; | |
| 3930 | } else { | |
| 3931 | close_local_endpoint(conn); | |
| 3932 | } | |
| 3933 | } | |
| 3934 | ||
| 3935 | // Use the global passwords file, if specified by auth_gpass option, | |
| 3936 | // or search for .htpasswd in the requested directory. | |
| 3937 | static FILE *open_auth_file(struct connection *conn, const char *path, | |
| 3938 | int is_directory) { | |
| 3939 | char name[MAX_PATH_SIZE]; | |
| 3940 | const char *p, *gpass = conn->server->config_options[GLOBAL_AUTH_FILE]; | |
| 3941 | FILE *fp = NULL; | |
| 3942 | ||
| 3943 | if (gpass != NULL) { | |
| 3944 | // Use global passwords file | |
| 3945 | fp = fopen(gpass, "r"); | |
| 3946 | } else if (is_directory) { | |
| 3947 | mg_snprintf(name, sizeof(name), "%s%c%s", path, '/', PASSWORDS_FILE_NAME); | |
| 3948 | fp = fopen(name, "r"); | |
| 3949 | } else { | |
| 3950 | // Try to find .htpasswd in requested directory. | |
| 3951 | if ((p = strrchr(path, '/')) == NULL) p = path; | |
| 3952 | mg_snprintf(name, sizeof(name), "%.*s%c%s", | |
| 3953 | (int) (p - path), path, '/', PASSWORDS_FILE_NAME); | |
| 3954 | fp = fopen(name, "r"); | |
| 3955 | } | |
| 3956 | ||
| 3957 | return fp; | |
| 3958 | } | |
| 3959 | ||
| 3960 | #if !defined(HAVE_MD5) && !defined(MONGOOSE_NO_AUTH) | |
| 3961 | /* | |
| 3962 | * This code implements the MD5 message-digest algorithm. | |
| 3963 | * The algorithm is due to Ron Rivest. This code was | |
| 3964 | * written by Colin Plumb in 1993, no copyright is claimed. | |
| 3965 | * This code is in the public domain; do with it what you wish. | |
| 3966 | * | |
| 3967 | * Equivalent code is available from RSA Data Security, Inc. | |
| 3968 | * This code has been tested against that, and is equivalent, | |
| 3969 | * except that you don't need to include two pages of legalese | |
| 3970 | * with every copy. | |
| 3971 | * | |
| 3972 | * To compute the message digest of a chunk of bytes, declare an | |
| 3973 | * MD5Context structure, pass it to MD5Init, call MD5Update as | |
| 3974 | * needed on buffers full of bytes, and then call MD5Final, which | |
| 3975 | * will fill a supplied 16-byte array with the digest. | |
| 3976 | */ | |
| 3977 | ||
| 3978 | typedef struct MD5Context { | |
| 3979 | uint32_t buf[4]; | |
| 3980 | uint32_t bits[2]; | |
| 3981 | unsigned char in[64]; | |
| 3982 | } MD5_CTX; | |
| 3983 | ||
| 3984 | static void byteReverse(unsigned char *buf, unsigned longs) { | |
| 3985 | uint32_t t; | |
| 3986 | ||
| 3987 | // Forrest: MD5 expect LITTLE_ENDIAN, swap if BIG_ENDIAN | |
| 3988 | if (is_big_endian()) { | |
| 3989 | do { | |
| 3990 | t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 | | |
| 3991 | ((unsigned) buf[1] << 8 | buf[0]); | |
| 3992 | * (uint32_t *) buf = t; | |
| 3993 | buf += 4; | |
| 3994 | } while (--longs); | |
| 3995 | } | |
| 3996 | } | |
| 3997 | ||
| 3998 | #define F1(x, y, z) (z ^ (x & (y ^ z))) | |
| 3999 | #define F2(x, y, z) F1(z, x, y) | |
| 4000 | #define F3(x, y, z) (x ^ y ^ z) | |
| 4001 | #define F4(x, y, z) (y ^ (x | ~z)) | |
| 4002 | ||
| 4003 | #define MD5STEP(f, w, x, y, z, data, s) \ | |
| 4004 | ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x ) | |
| 4005 | ||
| 4006 | // Start MD5 accumulation. Set bit count to 0 and buffer to mysterious | |
| 4007 | // initialization constants. | |
| 4008 | static void MD5Init(MD5_CTX *ctx) { | |
| 4009 | ctx->buf[0] = 0x67452301; | |
| 4010 | ctx->buf[1] = 0xefcdab89; | |
| 4011 | ctx->buf[2] = 0x98badcfe; | |
| 4012 | ctx->buf[3] = 0x10325476; | |
| 4013 | ||
| 4014 | ctx->bits[0] = 0; | |
| 4015 | ctx->bits[1] = 0; | |
| 4016 | } | |
| 4017 | ||
| 4018 | static void MD5Transform(uint32_t buf[4], uint32_t const in[16]) { | |
| 4019 | register uint32_t a, b, c, d; | |
| 4020 | ||
| 4021 | a = buf[0]; | |
| 4022 | b = buf[1]; | |
| 4023 | c = buf[2]; | |
| 4024 | d = buf[3]; | |
| 4025 | ||
| 4026 | MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); | |
| 4027 | MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); | |
| 4028 | MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); | |
| 4029 | MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); | |
| 4030 | MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); | |
| 4031 | MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); | |
| 4032 | MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); | |
| 4033 | MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); | |
| 4034 | MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); | |
| 4035 | MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); | |
| 4036 | MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); | |
| 4037 | MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); | |
| 4038 | MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); | |
| 4039 | MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); | |
| 4040 | MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); | |
| 4041 | MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); | |
| 4042 | ||
| 4043 | MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); | |
| 4044 | MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); | |
| 4045 | MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); | |
| 4046 | MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); | |
| 4047 | MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); | |
| 4048 | MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); | |
| 4049 | MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); | |
| 4050 | MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); | |
| 4051 | MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); | |
| 4052 | MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); | |
| 4053 | MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); | |
| 4054 | MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); | |
| 4055 | MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); | |
| 4056 | MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); | |
| 4057 | MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); | |
| 4058 | MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); | |
| 4059 | ||
| 4060 | MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); | |
| 4061 | MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); | |
| 4062 | MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); | |
| 4063 | MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); | |
| 4064 | MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); | |
| 4065 | MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); | |
| 4066 | MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); | |
| 4067 | MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); | |
| 4068 | MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); | |
| 4069 | MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); | |
| 4070 | MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); | |
| 4071 | MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); | |
| 4072 | MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); | |
| 4073 | MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); | |
| 4074 | MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); | |
| 4075 | MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); | |
| 4076 | ||
| 4077 | MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); | |
| 4078 | MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); | |
| 4079 | MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); | |
| 4080 | MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); | |
| 4081 | MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); | |
| 4082 | MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); | |
| 4083 | MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); | |
| 4084 | MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); | |
| 4085 | MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); | |
| 4086 | MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); | |
| 4087 | MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); | |
| 4088 | MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); | |
| 4089 | MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); | |
| 4090 | MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); | |
| 4091 | MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); | |
| 4092 | MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); | |
| 4093 | ||
| 4094 | buf[0] += a; | |
| 4095 | buf[1] += b; | |
| 4096 | buf[2] += c; | |
| 4097 | buf[3] += d; | |
| 4098 | } | |
| 4099 | ||
| 4100 | static void MD5Update(MD5_CTX *ctx, unsigned char const *buf, unsigned len) { | |
| 4101 | uint32_t t; | |
| 4102 | ||
| 4103 | t = ctx->bits[0]; | |
| 4104 | if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t) | |
| 4105 | ctx->bits[1]++; | |
| 4106 | ctx->bits[1] += len >> 29; | |
| 4107 | ||
| 4108 | t = (t >> 3) & 0x3f; | |
| 4109 | ||
| 4110 | if (t) { | |
| 4111 | unsigned char *p = (unsigned char *) ctx->in + t; | |
| 4112 | ||
| 4113 | t = 64 - t; | |
| 4114 | if (len < t) { | |
| 4115 | memcpy(p, buf, len); | |
| 4116 | return; | |
| 4117 | } | |
| 4118 | memcpy(p, buf, t); | |
| 4119 | byteReverse(ctx->in, 16); | |
| 4120 | MD5Transform(ctx->buf, (uint32_t *) ctx->in); | |
| 4121 | buf += t; | |
| 4122 | len -= t; | |
| 4123 | } | |
| 4124 | ||
| 4125 | while (len >= 64) { | |
| 4126 | memcpy(ctx->in, buf, 64); | |
| 4127 | byteReverse(ctx->in, 16); | |
| 4128 | MD5Transform(ctx->buf, (uint32_t *) ctx->in); | |
| 4129 | buf += 64; | |
| 4130 | len -= 64; | |
| 4131 | } | |
| 4132 | ||
| 4133 | memcpy(ctx->in, buf, len); | |
| 4134 | } | |
| 4135 | ||
| 4136 | static void MD5Final(unsigned char digest[16], MD5_CTX *ctx) { | |
| 4137 | unsigned count; | |
| 4138 | unsigned char *p; | |
| 4139 | uint32_t *a; | |
| 4140 | ||
| 4141 | count = (ctx->bits[0] >> 3) & 0x3F; | |
| 4142 | ||
| 4143 | p = ctx->in + count; | |
| 4144 | *p++ = 0x80; | |
| 4145 | count = 64 - 1 - count; | |
| 4146 | if (count < 8) { | |
| 4147 | memset(p, 0, count); | |
| 4148 | byteReverse(ctx->in, 16); | |
| 4149 | MD5Transform(ctx->buf, (uint32_t *) ctx->in); | |
| 4150 | memset(ctx->in, 0, 56); | |
| 4151 | } else { | |
| 4152 | memset(p, 0, count - 8); | |
| 4153 | } | |
| 4154 | byteReverse(ctx->in, 14); | |
| 4155 | ||
| 4156 | a = (uint32_t *)ctx->in; | |
| 4157 | a[14] = ctx->bits[0]; | |
| 4158 | a[15] = ctx->bits[1]; | |
| 4159 | ||
| 4160 | MD5Transform(ctx->buf, (uint32_t *) ctx->in); | |
| 4161 | byteReverse((unsigned char *) ctx->buf, 4); | |
| 4162 | memcpy(digest, ctx->buf, 16); | |
| 4163 | memset((char *) ctx, 0, sizeof(*ctx)); | |
| 4164 | } | |
| 4165 | #endif // !HAVE_MD5 | |
| 4166 | ||
| 4167 | ||
| 4168 | ||
| 4169 | // Stringify binary data. Output buffer must be twice as big as input, | |
| 4170 | // because each byte takes 2 bytes in string representation | |
| 4171 | static void bin2str(char *to, const unsigned char *p, size_t len) { | |
| 4172 | static const char *hex = "0123456789abcdef"; | |
| 4173 | ||
| 4174 | for (; len--; p++) { | |
| 4175 | *to++ = hex[p[0] >> 4]; | |
| 4176 | *to++ = hex[p[0] & 0x0f]; | |
| 4177 | } | |
| 4178 | *to = '\0'; | |
| 4179 | } | |
| 4180 | ||
| 4181 | // Return stringified MD5 hash for list of strings. Buffer must be 33 bytes. | |
| 4182 | char *mg_md5(char buf[33], ...) { | |
| 4183 | unsigned char hash[16]; | |
| 4184 | const char *p; | |
| 4185 | va_list ap; | |
| 4186 | MD5_CTX ctx; | |
| 4187 | ||
| 4188 | MD5Init(&ctx); | |
| 4189 | ||
| 4190 | va_start(ap, buf); | |
| 4191 | while ((p = va_arg(ap, const char *)) != NULL) { | |
| 4192 | MD5Update(&ctx, (const unsigned char *) p, (unsigned) strlen(p)); | |
| 4193 | } | |
| 4194 | va_end(ap); | |
| 4195 | ||
| 4196 | MD5Final(hash, &ctx); | |
| 4197 | bin2str(buf, hash, sizeof(hash)); | |
| 4198 | return buf; | |
| 4199 | } | |
| 4200 | ||
| 4201 | // Check the user's password, return 1 if OK | |
| 4202 | static int check_password(const char *method, const char *ha1, const char *uri, | |
| 4203 | const char *nonce, const char *nc, const char *cnonce, | |
| 4204 | const char *qop, const char *response) { | |
| 4205 | char ha2[32 + 1], expected_response[32 + 1]; | |
| 4206 | ||
| 4207 | #if 0 | |
| 4208 | // Check for authentication timeout | |
| 4209 | if ((unsigned long) time(NULL) - (unsigned long) to64(nonce) > 3600 * 2) { | |
| 4210 | return 0; | |
| 4211 | } | |
| 4212 | #endif | |
| 4213 | ||
| 4214 | mg_md5(ha2, method, ":", uri, NULL); | |
| 4215 | mg_md5(expected_response, ha1, ":", nonce, ":", nc, | |
| 4216 | ":", cnonce, ":", qop, ":", ha2, NULL); | |
| 4217 | ||
| 4218 | return mg_strcasecmp(response, expected_response) == 0 ? MG_TRUE : MG_FALSE; | |
| 4219 | } | |
| 4220 | ||
| 4221 | ||
| 4222 | // Authorize against the opened passwords file. Return 1 if authorized. | |
| 4223 | int mg_authorize_digest(struct mg_connection *c, FILE *fp) { | |
| 4224 | struct connection *conn = MG_CONN_2_CONN(c); | |
| 4225 | const char *hdr; | |
| 4226 | char line[256], f_user[256], ha1[256], f_domain[256], user[100], nonce[100], | |
| 4227 | uri[MAX_REQUEST_SIZE], cnonce[100], resp[100], qop[100], nc[100]; | |
| 4228 | ||
| 4229 | if (c == NULL || fp == NULL) return 0; | |
| 4230 | if ((hdr = mg_get_header(c, "Authorization")) == NULL || | |
| 4231 | mg_strncasecmp(hdr, "Digest ", 7) != 0) return 0; | |
| 4232 | if (!mg_parse_header(hdr, "username", user, sizeof(user))) return 0; | |
| 4233 | if (!mg_parse_header(hdr, "cnonce", cnonce, sizeof(cnonce))) return 0; | |
| 4234 | if (!mg_parse_header(hdr, "response", resp, sizeof(resp))) return 0; | |
| 4235 | if (!mg_parse_header(hdr, "uri", uri, sizeof(uri))) return 0; | |
| 4236 | if (!mg_parse_header(hdr, "qop", qop, sizeof(qop))) return 0; | |
| 4237 | if (!mg_parse_header(hdr, "nc", nc, sizeof(nc))) return 0; | |
| 4238 | if (!mg_parse_header(hdr, "nonce", nonce, sizeof(nonce))) return 0; | |
| 4239 | ||
| 4240 | while (fgets(line, sizeof(line), fp) != NULL) { | |
| 4241 | if (sscanf(line, "%[^:]:%[^:]:%s", f_user, f_domain, ha1) == 3 && | |
| 4242 | !strcmp(user, f_user) && | |
| 4243 | // NOTE(lsm): due to a bug in MSIE, we do not compare URIs | |
| 4244 | !strcmp(conn->server->config_options[AUTH_DOMAIN], f_domain)) | |
| 4245 | return check_password(c->request_method, ha1, uri, | |
| 4246 | nonce, nc, cnonce, qop, resp); | |
| 4247 | } | |
| 4248 | return MG_FALSE; | |
| 4249 | } | |
| 4250 | ||
| 4251 | ||
| 4252 | // Return 1 if request is authorised, 0 otherwise. | |
| 4253 | static int is_authorized(struct connection *conn, const char *path, | |
| 4254 | int is_directory) { | |
| 4255 | FILE *fp; | |
| 4256 | int authorized = MG_TRUE; | |
| 4257 | ||
| 4258 | if ((fp = open_auth_file(conn, path, is_directory)) != NULL) { | |
| 4259 | authorized = mg_authorize_digest(&conn->mg_conn, fp); | |
| 4260 | fclose(fp); | |
| 4261 | } | |
| 4262 | ||
| 4263 | return authorized; | |
| 4264 | } | |
| 4265 | ||
| 4266 | static int is_authorized_for_dav(struct connection *conn) { | |
| 4267 | const char *auth_file = conn->server->config_options[DAV_AUTH_FILE]; | |
| 4268 | const char *method = conn->mg_conn.request_method; | |
| 4269 | FILE *fp; | |
| 4270 | int authorized = MG_FALSE; | |
| 4271 | ||
| 4272 | // If dav_auth_file is not set, allow non-authorized PROPFIND | |
| 4273 | if (method != NULL && !strcmp(method, "PROPFIND") && auth_file == NULL) { | |
| 4274 | authorized = MG_TRUE; | |
| 4275 | } else if (auth_file != NULL && (fp = fopen(auth_file, "r")) != NULL) { | |
| 4276 | authorized = mg_authorize_digest(&conn->mg_conn, fp); | |
| 4277 | fclose(fp); | |
| 4278 | } | |
| 4279 | ||
| 4280 | return authorized; | |
| 4281 | } | |
| 4282 | #endif // MONGOOSE_NO_AUTH | |
| 4283 | ||
| 4284 | static int parse_header(const char *str, size_t str_len, const char *var_name, | |
| 4285 | char *buf, size_t buf_size) { | |
| 4286 | int ch = ' ', ch1 = ',', len = 0; | |
| 4287 | size_t n = strlen(var_name); | |
| 4288 | const char *p, *end = str + str_len, *s = NULL; | |
| 4289 | ||
| 4290 | if (buf != NULL && buf_size > 0) buf[0] = '\0'; | |
| 4291 | ||
| 4292 | // Find where variable starts | |
| 4293 | for (s = str; s != NULL && s + n < end; s++) { | |
| 4294 | if ((s == str || s[-1] == ch || s[-1] == ch1) && s[n] == '=' && | |
| 4295 | !memcmp(s, var_name, n)) break; | |
| 4296 | } | |
| 4297 | ||
| 4298 | if (s != NULL && &s[n + 1] < end) { | |
| 4299 | s += n + 1; | |
| 4300 | if (*s == '"' || *s == '\'') ch = ch1 = *s++; | |
| 4301 | p = s; | |
| 4302 | while (p < end && p[0] != ch && p[0] != ch1 && len < (int) buf_size) { | |
| 4303 | if (ch == ch1 && p[0] == '\\' && p[1] == ch) p++; | |
| 4304 | buf[len++] = *p++; | |
| 4305 | } | |
| 4306 | if (len >= (int) buf_size || (ch != ' ' && *p != ch)) { | |
| 4307 | len = 0; | |
| 4308 | } else { | |
| 4309 | if (len > 0 && s[len - 1] == ',') len--; | |
| 4310 | if (len > 0 && s[len - 1] == ';') len--; | |
| 4311 | buf[len] = '\0'; | |
| 4312 | } | |
| 4313 | } | |
| 4314 | ||
| 4315 | return len; | |
| 4316 | } | |
| 4317 | ||
| 4318 | int mg_parse_header(const char *s, const char *var_name, char *buf, | |
| 4319 | size_t buf_size) { | |
| 4320 | return parse_header(s, s == NULL ? 0 : strlen(s), var_name, buf, buf_size); | |
| 4321 | } | |
| 4322 | ||
| 4323 | #ifndef MONGOOSE_NO_SSI | |
| 4324 | static void send_ssi_file(struct mg_connection *, const char *, FILE *, int); | |
| 4325 | ||
| 4326 | static void send_file_data(struct mg_connection *conn, FILE *fp) { | |
| 4327 | char buf[IOBUF_SIZE]; | |
| 4328 | size_t n; | |
| 4329 | while ((n = fread(buf, 1, sizeof(buf), fp)) > 0) { | |
| 4330 | mg_write(conn, buf, n); | |
| 4331 | } | |
| 4332 | } | |
| 4333 | ||
| 4334 | static void do_ssi_include(struct mg_connection *conn, const char *ssi, | |
| 4335 | char *tag, int include_level) { | |
| 4336 | char file_name[IOBUF_SIZE], path[MAX_PATH_SIZE], *p; | |
| 4337 | char **opts = (MG_CONN_2_CONN(conn))->server->config_options; | |
| 4338 | FILE *fp; | |
| 4339 | ||
| 4340 | // sscanf() is safe here, since send_ssi_file() also uses buffer | |
| 4341 | // of size MG_BUF_LEN to get the tag. So strlen(tag) is always < MG_BUF_LEN. | |
| 4342 | if (sscanf(tag, " virtual=\"%[^\"]\"", file_name) == 1) { | |
| 4343 | // File name is relative to the webserver root | |
| 4344 | mg_snprintf(path, sizeof(path), "%s%c%s", | |
| 4345 | opts[DOCUMENT_ROOT], '/', file_name); | |
| 4346 | } else if (sscanf(tag, " abspath=\"%[^\"]\"", file_name) == 1) { | |
| 4347 | // File name is relative to the webserver working directory | |
| 4348 | // or it is absolute system path | |
| 4349 | mg_snprintf(path, sizeof(path), "%s", file_name); | |
| 4350 | } else if (sscanf(tag, " file=\"%[^\"]\"", file_name) == 1 || | |
| 4351 | sscanf(tag, " \"%[^\"]\"", file_name) == 1) { | |
| 4352 | // File name is relative to the current document | |
| 4353 | mg_snprintf(path, sizeof(path), "%s", ssi); | |
| 4354 | if ((p = strrchr(path, '/')) != NULL) { | |
| 4355 | p[1] = '\0'; | |
| 4356 | } | |
| 4357 | mg_snprintf(path + strlen(path), sizeof(path) - strlen(path), "%s", | |
| 4358 | file_name); | |
| 4359 | } else { | |
| 4360 | mg_printf(conn, "Bad SSI #include: [%s]", tag); | |
| 4361 | return; | |
| 4362 | } | |
| 4363 | ||
| 4364 | if ((fp = fopen(path, "rb")) == NULL) { | |
| 4365 | mg_printf(conn, "Cannot open SSI #include: [%s]: fopen(%s): %s", | |
| 4366 | tag, path, strerror(errno)); | |
| 4367 | } else { | |
| 4368 | ns_set_close_on_exec(fileno(fp)); | |
| 4369 | if (mg_match_prefix(opts[SSI_PATTERN], strlen(opts[SSI_PATTERN]), | |
| 4370 | path) > 0) { | |
| 4371 | send_ssi_file(conn, path, fp, include_level + 1); | |
| 4372 | } else { | |
| 4373 | send_file_data(conn, fp); | |
| 4374 | } | |
| 4375 | fclose(fp); | |
| 4376 | } | |
| 4377 | } | |
| 4378 | ||
| 4379 | #ifndef MONGOOSE_NO_POPEN | |
| 4380 | static void do_ssi_exec(struct mg_connection *conn, char *tag) { | |
| 4381 | char cmd[IOBUF_SIZE]; | |
| 4382 | FILE *fp; | |
| 4383 | ||
| 4384 | if (sscanf(tag, " \"%[^\"]\"", cmd) != 1) { | |
| 4385 | mg_printf(conn, "Bad SSI #exec: [%s]", tag); | |
| 4386 | } else if ((fp = popen(cmd, "r")) == NULL) { | |
| 4387 | mg_printf(conn, "Cannot SSI #exec: [%s]: %s", cmd, strerror(errno)); | |
| 4388 | } else { | |
| 4389 | send_file_data(conn, fp); | |
| 4390 | pclose(fp); | |
| 4391 | } | |
| 4392 | } | |
| 4393 | #endif // !MONGOOSE_NO_POPEN | |
| 4394 | ||
| 4395 | static void send_ssi_file(struct mg_connection *conn, const char *path, | |
| 4396 | FILE *fp, int include_level) { | |
| 4397 | char buf[IOBUF_SIZE]; | |
| 4398 | int ch, offset, len, in_ssi_tag; | |
| 4399 | ||
| 4400 | if (include_level > 10) { | |
| 4401 | mg_printf(conn, "SSI #include level is too deep (%s)", path); | |
| 4402 | return; | |
| 4403 | } | |
| 4404 | ||
| 4405 | in_ssi_tag = len = offset = 0; | |
| 4406 | while ((ch = fgetc(fp)) != EOF) { | |
| 4407 | if (in_ssi_tag && ch == '>') { | |
| 4408 | in_ssi_tag = 0; | |
| 4409 | buf[len++] = (char) ch; | |
| 4410 | buf[len] = '\0'; | |
| 4411 | assert(len <= (int) sizeof(buf)); | |
| 4412 | if (len < 6 || memcmp(buf, "<!--#", 5) != 0) { | |
| 4413 | // Not an SSI tag, pass it | |
| 4414 | (void) mg_write(conn, buf, (size_t) len); | |
| 4415 | } else { | |
| 4416 | if (!memcmp(buf + 5, "include", 7)) { | |
| 4417 | do_ssi_include(conn, path, buf + 12, include_level); | |
| 4418 | #if !defined(MONGOOSE_NO_POPEN) | |
| 4419 | } else if (!memcmp(buf + 5, "exec", 4)) { | |
| 4420 | do_ssi_exec(conn, buf + 9); | |
| 4421 | #endif // !NO_POPEN | |
| 4422 | } else { | |
| 4423 | mg_printf(conn, "%s: unknown SSI " "command: \"%s\"", path, buf); | |
| 4424 | } | |
| 4425 | } | |
| 4426 | len = 0; | |
| 4427 | } else if (in_ssi_tag) { | |
| 4428 | if (len == 5 && memcmp(buf, "<!--#", 5) != 0) { | |
| 4429 | // Not an SSI tag | |
| 4430 | in_ssi_tag = 0; | |
| 4431 | } else if (len == (int) sizeof(buf) - 2) { | |
| 4432 | mg_printf(conn, "%s: SSI tag is too large", path); | |
| 4433 | len = 0; | |
| 4434 | } | |
| 4435 | buf[len++] = ch & 0xff; | |
| 4436 | } else if (ch == '<') { | |
| 4437 | in_ssi_tag = 1; | |
| 4438 | if (len > 0) { | |
| 4439 | mg_write(conn, buf, (size_t) len); | |
| 4440 | } | |
| 4441 | len = 0; | |
| 4442 | buf[len++] = ch & 0xff; | |
| 4443 | } else { | |
| 4444 | buf[len++] = ch & 0xff; | |
| 4445 | if (len == (int) sizeof(buf)) { | |
| 4446 | mg_write(conn, buf, (size_t) len); | |
| 4447 | len = 0; | |
| 4448 | } | |
| 4449 | } | |
| 4450 | } | |
| 4451 | ||
| 4452 | // Send the rest of buffered data | |
| 4453 | if (len > 0) { | |
| 4454 | mg_write(conn, buf, (size_t) len); | |
| 4455 | } | |
| 4456 | } | |
| 4457 | ||
| 4458 | static void handle_ssi_request(struct connection *conn, const char *path) { | |
| 4459 | FILE *fp; | |
| 4460 | struct vec mime_vec; | |
| 4461 | ||
| 4462 | if ((fp = fopen(path, "rb")) == NULL) { | |
| 4463 | send_http_error(conn, 500, "fopen(%s): %s", path, strerror(errno)); | |
| 4464 | } else { | |
| 4465 | ns_set_close_on_exec(fileno(fp)); | |
| 4466 | get_mime_type(conn->server, path, &mime_vec); | |
| 4467 | conn->mg_conn.status_code = 200; | |
| 4468 | mg_printf(&conn->mg_conn, | |
| 4469 | "HTTP/1.1 %d OK\r\n" | |
| 4470 | "Content-Type: %.*s\r\n" | |
| 4471 | "Connection: close\r\n\r\n", | |
| 4472 | conn->mg_conn.status_code, (int) mime_vec.len, mime_vec.ptr); | |
| 4473 | send_ssi_file(&conn->mg_conn, path, fp, 0); | |
| 4474 | fclose(fp); | |
| 4475 | close_local_endpoint(conn); | |
| 4476 | } | |
| 4477 | } | |
| 4478 | #endif | |
| 4479 | ||
| 4480 | static void proxy_request(struct ns_connection *pc, struct mg_connection *c) { | |
| 4481 | int i, sent_close_header = 0; | |
| 4482 | ||
| 4483 | ns_printf(pc, "%s %s%s%s HTTP/%s\r\n", c->request_method, c->uri, | |
| 4484 | c->query_string ? "?" : "", | |
| 4485 | c->query_string ? c->query_string : "", | |
| 4486 | c->http_version); | |
| 4487 | for (i = 0; i < c->num_headers; i++) { | |
| 4488 | if (mg_strcasecmp(c->http_headers[i].name, "Connection") == 0) { | |
| 4489 | // Force connection close, cause we don't parse proxy replies | |
| 4490 | // therefore we don't know message boundaries | |
| 4491 | ns_printf(pc, "%s: %s\r\n", "Connection", "close"); | |
| 4492 | sent_close_header = 1; | |
| 4493 | } else { | |
| 4494 | ns_printf(pc, "%s: %s\r\n", c->http_headers[i].name, | |
| 4495 | c->http_headers[i].value); | |
| 4496 | } | |
| 4497 | } | |
| 4498 | if (!sent_close_header) { | |
| 4499 | ns_printf(pc, "%s: %s\r\n", "Connection", "close"); | |
| 4500 | } | |
| 4501 | ns_printf(pc, "%s", "\r\n"); | |
| 4502 | ns_send(pc, c->content, c->content_len); | |
| 4503 | ||
| 4504 | } | |
| 4505 | ||
| 4506 | #ifdef NS_ENABLE_SSL | |
| 4507 | int mg_terminate_ssl(struct mg_connection *c, const char *cert) { | |
| 4508 | static const char ok[] = "HTTP/1.0 200 OK\r\n\r\n"; | |
| 4509 | struct connection *conn = MG_CONN_2_CONN(c); | |
| 4510 | SSL_CTX *ctx; | |
| 4511 | ||
| 4512 | DBG(("%p MITM", conn)); | |
| 4513 | if ((ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) return 0; | |
| 4514 | ||
| 4515 | SSL_CTX_use_certificate_file(ctx, cert, 1); | |
| 4516 | SSL_CTX_use_PrivateKey_file(ctx, cert, 1); | |
| 4517 | SSL_CTX_use_certificate_chain_file(ctx, cert); | |
| 4518 | ||
| 4519 | // When clear-text reply is pushed to client, switch to SSL mode. | |
| 4520 | // TODO(lsm): check for send() failure | |
| 4521 | send(conn->ns_conn->sock, ok, sizeof(ok) - 1, 0); | |
| 4522 | //DBG(("%p %lu %d SEND", c, (unsigned long) sizeof(ok) - 1, n)); | |
| 4523 | conn->ns_conn->send_iobuf.len = 0; | |
| 4524 | conn->endpoint_type = EP_USER; // To keep-alive in close_local_endpoint() | |
| 4525 | close_local_endpoint(conn); // Clean up current CONNECT request | |
| 4526 | if ((conn->ns_conn->ssl = SSL_new(ctx)) != NULL) { | |
| 4527 | SSL_set_fd(conn->ns_conn->ssl, conn->ns_conn->sock); | |
| 4528 | } | |
| 4529 | SSL_CTX_free(ctx); | |
| 4530 | return 1; | |
| 4531 | } | |
| 4532 | #endif | |
| 4533 | ||
| 4534 | int mg_forward(struct mg_connection *c, const char *addr) { | |
| 4535 | static const char ok[] = "HTTP/1.1 200 OK\r\n\r\n"; | |
| 4536 | struct connection *conn = MG_CONN_2_CONN(c); | |
| 4537 | struct ns_connection *pc; | |
| 4538 | ||
| 4539 | if ((pc = ns_connect(&conn->server->ns_mgr, addr, | |
| 4540 | mg_ev_handler, conn)) == NULL) { | |
| 4541 | conn->ns_conn->flags |= NSF_CLOSE_IMMEDIATELY; | |
| 4542 | return 0; | |
| 4543 | } | |
| 4544 | ||
| 4545 | // Interlink two connections | |
| 4546 | pc->flags |= MG_PROXY_CONN; | |
| 4547 | conn->endpoint_type = EP_PROXY; | |
| 4548 | conn->endpoint.nc = pc; | |
| 4549 | DBG(("%p [%s] [%s] -> %p %p", conn, c->uri, addr, pc, conn->ns_conn->ssl)); | |
| 4550 | ||
| 4551 | if (strcmp(c->request_method, "CONNECT") == 0) { | |
| 4552 | // For CONNECT request, reply with 200 OK. Tunnel is established. | |
| 4553 | // TODO(lsm): check for send() failure | |
| 4554 | (void) send(conn->ns_conn->sock, ok, sizeof(ok) - 1, 0); | |
| 4555 | } else { | |
| 4556 | // Strip "http://host:port" part from the URI | |
| 4557 | if (memcmp(c->uri, "http://", 7) == 0) c->uri += 7; | |
| 4558 | while (*c->uri != '\0' && *c->uri != '/') c->uri++; | |
| 4559 | proxy_request(pc, c); | |
| 4560 | } | |
| 4561 | return 1; | |
| 4562 | } | |
| 4563 | ||
| 4564 | static void proxify_connection(struct connection *conn) { | |
| 4565 | char proto[10], host[500], cert[500], addr[1000]; | |
| 4566 | unsigned short port = 80; | |
| 4567 | struct mg_connection *c = &conn->mg_conn; | |
| 4568 | int n = 0; | |
| 4569 | const char *url = c->uri; | |
| 4570 | ||
| 4571 | proto[0] = host[0] = cert[0] = '\0'; | |
| 4572 | if (sscanf(url, "%499[^: ]:%hu%n", host, &port, &n) != 2 && | |
| 4573 | sscanf(url, "%9[a-z]://%499[^: ]:%hu%n", proto, host, &port, &n) != 3 && | |
| 4574 | sscanf(url, "%9[a-z]://%499[^/ ]%n", proto, host, &n) != 2) { | |
| 4575 | n = 0; | |
| 4576 | } | |
| 4577 | ||
| 4578 | snprintf(addr, sizeof(addr), "%s://%s:%hu", | |
| 4579 | conn->ns_conn->ssl != NULL ? "ssl" : "tcp", host, port); | |
| 4580 | if (n <= 0 || !mg_forward(c, addr)) { | |
| 4581 | conn->ns_conn->flags |= NSF_CLOSE_IMMEDIATELY; | |
| 4582 | } | |
| 4583 | } | |
| 4584 | ||
| 4585 | #ifndef MONGOOSE_NO_FILESYSTEM | |
| 4586 | void mg_send_file_internal(struct mg_connection *c, const char *file_name, | |
| 4587 | file_stat_t *st, int exists, | |
| 4588 | const char *extra_headers) { | |
| 4589 | struct connection *conn = MG_CONN_2_CONN(c); | |
| 4590 | char path[MAX_PATH_SIZE]; | |
| 4591 | const int is_directory = S_ISDIR(st->st_mode); | |
| 4592 | #ifndef MONGOOSE_NO_CGI | |
| 4593 | const char *cgi_pat = conn->server->config_options[CGI_PATTERN]; | |
| 4594 | #else | |
| 4595 | const char *cgi_pat = DEFAULT_CGI_PATTERN; | |
| 4596 | #endif | |
| 4597 | #ifndef MONGOOSE_NO_DIRECTORY_LISTING | |
| 4598 | const char *dir_lst = conn->server->config_options[ENABLE_DIRECTORY_LISTING]; | |
| 4599 | #else | |
| 4600 | const char *dir_lst = "yes"; | |
| 4601 | #endif | |
| 4602 | ||
| 4603 | mg_snprintf(path, sizeof(path), "%s", file_name); | |
| 4604 | ||
| 4605 | if (!exists || must_hide_file(conn, path)) { | |
| 4606 | send_http_error(conn, 404, NULL); | |
| 4607 | } else if (is_directory && | |
| 4608 | conn->mg_conn.uri[strlen(conn->mg_conn.uri) - 1] != '/') { | |
| 4609 | conn->mg_conn.status_code = 301; | |
| 4610 | mg_printf(&conn->mg_conn, "HTTP/1.1 301 Moved Permanently\r\n" | |
| 4611 | "Location: %s/\r\n\r\n", conn->mg_conn.uri); | |
| 4612 | close_local_endpoint(conn); | |
| 4613 | } else if (is_directory && !find_index_file(conn, path, sizeof(path), st)) { | |
| 4614 | if (!mg_strcasecmp(dir_lst, "yes")) { | |
| 4615 | #ifndef MONGOOSE_NO_DIRECTORY_LISTING | |
| 4616 | send_directory_listing(conn, path); | |
| 4617 | #else | |
| 4618 | send_http_error(conn, 501, NULL); | |
| 4619 | #endif | |
| 4620 | } else { | |
| 4621 | send_http_error(conn, 403, NULL); | |
| 4622 | } | |
| 4623 | } else if (mg_match_prefix(cgi_pat, strlen(cgi_pat), path) > 0) { | |
| 4624 | #if !defined(MONGOOSE_NO_CGI) | |
| 4625 | open_cgi_endpoint(conn, path); | |
| 4626 | #else | |
| 4627 | send_http_error(conn, 501, NULL); | |
| 4628 | #endif // !MONGOOSE_NO_CGI | |
| 4629 | #ifndef MONGOOSE_NO_SSI | |
| 4630 | } else if (mg_match_prefix(conn->server->config_options[SSI_PATTERN], | |
| 4631 | strlen(conn->server->config_options[SSI_PATTERN]), | |
| 4632 | path) > 0) { | |
| 4633 | handle_ssi_request(conn, path); | |
| 4634 | #endif | |
| 4635 | } else if (is_not_modified(conn, st)) { | |
| 4636 | send_http_error(conn, 304, NULL); | |
| 4637 | } else if ((conn->endpoint.fd = open(path, O_RDONLY | O_BINARY, 0)) != -1) { | |
| 4638 | // O_BINARY is required for Windows, otherwise in default text mode | |
| 4639 | // two bytes \r\n will be read as one. | |
| 4640 | open_file_endpoint(conn, path, st, extra_headers); | |
| 4641 | } else { | |
| 4642 | send_http_error(conn, 404, NULL); | |
| 4643 | } | |
| 4644 | } | |
| 4645 | void mg_send_file(struct mg_connection *c, const char *file_name, | |
| 4646 | const char *extra_headers) { | |
| 4647 | file_stat_t st; | |
| 4648 | const int exists = stat(file_name, &st) == 0; | |
| 4649 | mg_send_file_internal(c, file_name, &st, exists, extra_headers); | |
| 4650 | } | |
| 4651 | #endif // !MONGOOSE_NO_FILESYSTEM | |
| 4652 | ||
| 4653 | static void open_local_endpoint(struct connection *conn, int skip_user) { | |
| 4654 | #ifndef MONGOOSE_NO_FILESYSTEM | |
| 4655 | char path[MAX_PATH_SIZE]; | |
| 4656 | file_stat_t st; | |
| 4657 | int exists = 0; | |
| 4658 | #endif | |
| 4659 | ||
| 4660 | // If EP_USER was set in a prev call, reset it | |
| 4661 | conn->endpoint_type = EP_NONE; | |
| 4662 | ||
| 4663 | #ifndef MONGOOSE_NO_AUTH | |
| 4664 | if (conn->server->event_handler && call_user(conn, MG_AUTH) == MG_FALSE) { | |
| 4665 | mg_send_digest_auth_request(&conn->mg_conn); | |
| 4666 | return; | |
| 4667 | } | |
| 4668 | #endif | |
| 4669 | ||
| 4670 | // Call URI handler if one is registered for this URI | |
| 4671 | if (skip_user == 0 && conn->server->event_handler != NULL) { | |
| 4672 | conn->endpoint_type = EP_USER; | |
| 4673 | #if MONGOOSE_POST_SIZE_LIMIT > 1 | |
| 4674 | { | |
| 4675 | const char *cl = mg_get_header(&conn->mg_conn, "Content-Length"); | |
| 4676 | if ((strcmp(conn->mg_conn.request_method, "POST") == 0 || | |
| 4677 | strcmp(conn->mg_conn.request_method, "PUT") == 0) && | |
| 4678 | (cl == NULL || to64(cl) > MONGOOSE_POST_SIZE_LIMIT)) { | |
| 4679 | send_http_error(conn, 500, "POST size > %lu", | |
| 4680 | (unsigned long) MONGOOSE_POST_SIZE_LIMIT); | |
| 4681 | } | |
| 4682 | } | |
| 4683 | #endif | |
| 4684 | return; | |
| 4685 | } | |
| 4686 | ||
| 4687 | if (strcmp(conn->mg_conn.request_method, "CONNECT") == 0 || | |
| 4688 | mg_strncasecmp(conn->mg_conn.uri, "http", 4) == 0) { | |
| 4689 | const char *enp = conn->server->config_options[ENABLE_PROXY]; | |
| 4690 | if (enp == NULL || strcmp(enp, "yes") != 0) { | |
| 4691 | send_http_error(conn, 405, NULL); | |
| 4692 | } else { | |
| 4693 | proxify_connection(conn); | |
| 4694 | } | |
| 4695 | return; | |
| 4696 | } | |
| 4697 | ||
| 4698 | if (!strcmp(conn->mg_conn.request_method, "OPTIONS")) { | |
| 4699 | send_options(conn); | |
| 4700 | return; | |
| 4701 | } | |
| 4702 | ||
| 4703 | #ifdef MONGOOSE_NO_FILESYSTEM | |
| 4704 | send_http_error(conn, 404, NULL); | |
| 4705 | #else | |
| 4706 | exists = convert_uri_to_file_name(conn, path, sizeof(path), &st); | |
| 4707 | ||
| 4708 | if (!strcmp(conn->mg_conn.request_method, "OPTIONS")) { | |
| 4709 | send_options(conn); | |
| 4710 | } else if (conn->server->config_options[DOCUMENT_ROOT] == NULL) { | |
| 4711 | send_http_error(conn, 404, NULL); | |
| 4712 | #ifndef MONGOOSE_NO_AUTH | |
| 4713 | } else if ((!is_dav_request(conn) && !is_authorized(conn, path, | |
| 4714 | exists && S_ISDIR(st.st_mode))) || | |
| 4715 | (is_dav_request(conn) && !is_authorized_for_dav(conn))) { | |
| 4716 | mg_send_digest_auth_request(&conn->mg_conn); | |
| 4717 | close_local_endpoint(conn); | |
| 4718 | #endif | |
| 4719 | #ifndef MONGOOSE_NO_DAV | |
| 4720 | } else if (must_hide_file(conn, path)) { | |
| 4721 | send_http_error(conn, 404, NULL); | |
| 4722 | } else if (!strcmp(conn->mg_conn.request_method, "PROPFIND")) { | |
| 4723 | handle_propfind(conn, path, &st, exists); | |
| 4724 | } else if (!strcmp(conn->mg_conn.request_method, "MKCOL")) { | |
| 4725 | handle_mkcol(conn, path); | |
| 4726 | } else if (!strcmp(conn->mg_conn.request_method, "DELETE")) { | |
| 4727 | handle_delete(conn, path); | |
| 4728 | } else if (!strcmp(conn->mg_conn.request_method, "PUT")) { | |
| 4729 | handle_put(conn, path); | |
| 4730 | #endif | |
| 4731 | } else { | |
| 4732 | mg_send_file_internal(&conn->mg_conn, path, &st, exists, NULL); | |
| 4733 | } | |
| 4734 | #endif // MONGOOSE_NO_FILESYSTEM | |
| 4735 | } | |
| 4736 | ||
| 4737 | static void send_continue_if_expected(struct connection *conn) { | |
| 4738 | static const char expect_response[] = "HTTP/1.1 100 Continue\r\n\r\n"; | |
| 4739 | const char *expect_hdr = mg_get_header(&conn->mg_conn, "Expect"); | |
| 4740 | ||
| 4741 | if (expect_hdr != NULL && !mg_strcasecmp(expect_hdr, "100-continue")) { | |
| 4742 | ns_send(conn->ns_conn, expect_response, sizeof(expect_response) - 1); | |
| 4743 | } | |
| 4744 | } | |
| 4745 | ||
| 4746 | // Conform to http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2 | |
| 4747 | static int is_valid_uri(const char *uri) { | |
| 4748 | unsigned short n; | |
| 4749 | return uri[0] == '/' || | |
| 4750 | strcmp(uri, "*") == 0 || // OPTIONS method can use asterisk URI | |
| 4751 | mg_strncasecmp(uri, "http", 4) == 0 || // Naive check for the absolute URI | |
| 4752 | sscanf(uri, "%*[^ :]:%hu", &n) > 0; // CONNECT method can use host:port | |
| 4753 | } | |
| 4754 | ||
| 4755 | static void try_parse(struct connection *conn) { | |
| 4756 | struct iobuf *io = &conn->ns_conn->recv_iobuf; | |
| 4757 | ||
| 4758 | if (conn->request_len == 0 && | |
| 4759 | (conn->request_len = get_request_len(io->buf, io->len)) > 0) { | |
| 4760 | // If request is buffered in, remove it from the iobuf. This is because | |
| 4761 | // iobuf could be reallocated, and pointers in parsed request could | |
| 4762 | // become invalid. | |
| 4763 | conn->request = (char *) NS_MALLOC(conn->request_len); | |
| 4764 | if (conn->request == NULL) { | |
| 4765 | conn->ns_conn->flags |= NSF_CLOSE_IMMEDIATELY; | |
| 4766 | return; | |
| 4767 | } | |
| 4768 | memcpy(conn->request, io->buf, conn->request_len); | |
| 4769 | //DBG(("%p [%.*s]", conn, conn->request_len, conn->request)); | |
| 4770 | iobuf_remove(io, conn->request_len); | |
| 4771 | conn->request_len = parse_http_message(conn->request, conn->request_len, | |
| 4772 | &conn->mg_conn); | |
| 4773 | if (conn->request_len > 0) { | |
| 4774 | const char *cl_hdr = mg_get_header(&conn->mg_conn, "Content-Length"); | |
| 4775 | conn->cl = cl_hdr == NULL ? 0 : to64(cl_hdr); | |
| 4776 | conn->mg_conn.content_len = (size_t) conn->cl; | |
| 4777 | } | |
| 4778 | } | |
| 4779 | } | |
| 4780 | ||
| 4781 | static void do_proxy(struct connection *conn) { | |
| 4782 | if (0 && conn->request_len == 0) { | |
| 4783 | try_parse(conn); | |
| 4784 | DBG(("%p parsing -> %d", conn, conn->request_len)); | |
| 4785 | if (conn->request_len > 0 && call_user(conn, MG_REQUEST) == MG_FALSE) { | |
| 4786 | proxy_request(conn->endpoint.nc, &conn->mg_conn); | |
| 4787 | } else if (conn->request_len < 0) { | |
| 4788 | ns_forward(conn->ns_conn, conn->endpoint.nc); | |
| 4789 | } | |
| 4790 | } else { | |
| 4791 | DBG(("%p forwarding", conn)); | |
| 4792 | ns_forward(conn->ns_conn, conn->endpoint.nc); | |
| 4793 | } | |
| 4794 | } | |
| 4795 | ||
| 4796 | static void on_recv_data(struct connection *conn) { | |
| 4797 | struct iobuf *io = &conn->ns_conn->recv_iobuf; | |
| 4798 | int n; | |
| 4799 | ||
| 4800 | if (conn->endpoint_type == EP_PROXY) { | |
| 4801 | if (conn->endpoint.nc != NULL) do_proxy(conn); | |
| 4802 | return; | |
| 4803 | } | |
| 4804 | ||
| 4805 | if (conn->ns_conn->flags & NSF_DISCARD) { | |
| 4806 | size_t n = conn->cl; | |
| 4807 | if (n > io->len) { | |
| 4808 | n = io->len; | |
| 4809 | } | |
| 4810 | iobuf_remove(io, n); | |
| 4811 | conn->cl -= n; | |
| 4812 | if (conn->cl == 0) { | |
| 4813 | close_local_endpoint(conn); | |
| 4814 | } | |
| 4815 | return; | |
| 4816 | } | |
| 4817 | ||
| 4818 | try_parse(conn); | |
| 4819 | DBG(("%p %d %lu %d", conn, conn->request_len, (unsigned long)io->len, | |
| 4820 | conn->ns_conn->flags)); | |
| 4821 | if (conn->request_len < 0 || | |
| 4822 | (conn->request_len > 0 && !is_valid_uri(conn->mg_conn.uri))) { | |
| 4823 | send_http_error(conn, 400, NULL); | |
| 4824 | } else if (conn->request_len == 0 && io->len > MAX_REQUEST_SIZE) { | |
| 4825 | send_http_error(conn, 413, NULL); | |
| 4826 | } else if (conn->request_len > 0 && | |
| 4827 | strcmp(conn->mg_conn.http_version, "1.0") != 0 && | |
| 4828 | strcmp(conn->mg_conn.http_version, "1.1") != 0) { | |
| 4829 | send_http_error(conn, 505, NULL); | |
| 4830 | } else if (conn->request_len > 0 && conn->endpoint_type == EP_NONE) { | |
| 4831 | #ifndef MONGOOSE_NO_WEBSOCKET | |
| 4832 | send_websocket_handshake_if_requested(&conn->mg_conn); | |
| 4833 | #endif | |
| 4834 | send_continue_if_expected(conn); | |
| 4835 | open_local_endpoint(conn, 0); | |
| 4836 | } | |
| 4837 | ||
| 4838 | #ifndef MONGOOSE_NO_CGI | |
| 4839 | if (conn->endpoint_type == EP_CGI && conn->endpoint.nc != NULL) { | |
| 4840 | ns_forward(conn->ns_conn, conn->endpoint.nc); | |
| 4841 | } | |
| 4842 | #endif | |
| 4843 | if (conn->endpoint_type == EP_USER) { | |
| 4844 | conn->mg_conn.content = io->buf; | |
| 4845 | conn->mg_conn.content_len = io->len; | |
| 4846 | n = call_user(conn, MG_RECV); | |
| 4847 | if (n < 0) { | |
| 4848 | conn->ns_conn->flags |= NSF_FINISHED_SENDING_DATA; | |
| 4849 | } else if ((size_t) n <= io->len) { | |
| 4850 | iobuf_remove(io, n); | |
| 4851 | } | |
| 4852 | call_request_handler_if_data_is_buffered(conn); | |
| 4853 | } | |
| 4854 | #ifndef MONGOOSE_NO_DAV | |
| 4855 | if (conn->endpoint_type == EP_PUT && io->len > 0) { | |
| 4856 | forward_put_data(conn); | |
| 4857 | } | |
| 4858 | #endif | |
| 4859 | } | |
| 4860 | ||
| 4861 | static void call_http_client_handler(struct connection *conn) { | |
| 4862 | //conn->mg_conn.status_code = code; | |
| 4863 | // For responses without Content-Lengh, use the whole buffer | |
| 4864 | if (conn->cl == 0) { | |
| 4865 | conn->mg_conn.content_len = conn->ns_conn->recv_iobuf.len; | |
| 4866 | } | |
| 4867 | conn->mg_conn.content = conn->ns_conn->recv_iobuf.buf; | |
| 4868 | if (call_user(conn, MG_REPLY) == MG_FALSE) { | |
| 4869 | conn->ns_conn->flags |= NSF_CLOSE_IMMEDIATELY; | |
| 4870 | } | |
| 4871 | iobuf_remove(&conn->ns_conn->recv_iobuf, conn->mg_conn.content_len); | |
| 4872 | conn->mg_conn.status_code = 0; | |
| 4873 | conn->cl = conn->num_bytes_recv = conn->request_len = 0; | |
| 4874 | NS_FREE(conn->request); | |
| 4875 | conn->request = NULL; | |
| 4876 | } | |
| 4877 | ||
| 4878 | static void process_response(struct connection *conn) { | |
| 4879 | struct iobuf *io = &conn->ns_conn->recv_iobuf; | |
| 4880 | ||
| 4881 | try_parse(conn); | |
| 4882 | DBG(("%p %d %lu", conn, conn->request_len, (unsigned long)io->len)); | |
| 4883 | if (conn->request_len < 0 || | |
| 4884 | (conn->request_len == 0 && io->len > MAX_REQUEST_SIZE)) { | |
| 4885 | call_http_client_handler(conn); | |
| 4886 | } else if ((int64_t) io->len >= conn->cl) { | |
| 4887 | call_http_client_handler(conn); | |
| 4888 | } | |
| 4889 | } | |
| 4890 | ||
| 4891 | struct mg_connection *mg_connect(struct mg_server *server, const char *addr) { | |
| 4892 | struct ns_connection *nsconn; | |
| 4893 | struct connection *conn; | |
| 4894 | ||
| 4895 | nsconn = ns_connect(&server->ns_mgr, addr, mg_ev_handler, NULL); | |
| 4896 | if (nsconn == NULL) return 0; | |
| 4897 | ||
| 4898 | if ((conn = (struct connection *) NS_CALLOC(1, sizeof(*conn))) == NULL) { | |
| 4899 | nsconn->flags |= NSF_CLOSE_IMMEDIATELY; | |
| 4900 | return 0; | |
| 4901 | } | |
| 4902 | ||
| 4903 | // Interlink two structs | |
| 4904 | conn->ns_conn = nsconn; | |
| 4905 | nsconn->user_data = conn; | |
| 4906 | ||
| 4907 | conn->server = server; | |
| 4908 | conn->endpoint_type = EP_CLIENT; | |
| 4909 | //conn->handler = handler; | |
| 4910 | conn->mg_conn.server_param = server->ns_mgr.user_data; | |
| 4911 | conn->ns_conn->flags = NSF_CONNECTING; | |
| 4912 | ||
| 4913 | return &conn->mg_conn; | |
| 4914 | } | |
| 4915 | ||
| 4916 | #ifndef MONGOOSE_NO_LOGGING | |
| 4917 | static void log_header(const struct mg_connection *conn, const char *header, | |
| 4918 | FILE *fp) { | |
| 4919 | const char *header_value; | |
| 4920 | ||
| 4921 | if ((header_value = mg_get_header(conn, header)) == NULL) { | |
| 4922 | (void) fprintf(fp, "%s", " -"); | |
| 4923 | } else { | |
| 4924 | (void) fprintf(fp, " \"%s\"", header_value); | |
| 4925 | } | |
| 4926 | } | |
| 4927 | ||
| 4928 | static void log_access(const struct connection *conn, const char *path) { | |
| 4929 | const struct mg_connection *c = &conn->mg_conn; | |
| 4930 | FILE *fp = (path == NULL) ? NULL : fopen(path, "a+"); | |
| 4931 | char date[64], user[100]; | |
| 4932 | time_t now; | |
| 4933 | ||
| 4934 | if (fp == NULL) return; | |
| 4935 | now = time(NULL); | |
| 4936 | strftime(date, sizeof(date), "%d/%b/%Y:%H:%M:%S %z", localtime(&now)); | |
| 4937 | ||
| 4938 | flockfile(fp); | |
| 4939 | mg_parse_header(mg_get_header(&conn->mg_conn, "Authorization"), "username", | |
| 4940 | user, sizeof(user)); | |
| 4941 | fprintf(fp, "%s - %s [%s] \"%s %s%s%s HTTP/%s\" %d 0", | |
| 4942 | c->remote_ip, user[0] == '\0' ? "-" : user, date, | |
| 4943 | c->request_method ? c->request_method : "-", | |
| 4944 | c->uri ? c->uri : "-", c->query_string ? "?" : "", | |
| 4945 | c->query_string ? c->query_string : "", | |
| 4946 | c->http_version, c->status_code); | |
| 4947 | log_header(c, "Referer", fp); | |
| 4948 | log_header(c, "User-Agent", fp); | |
| 4949 | fputc('\n', fp); | |
| 4950 | fflush(fp); | |
| 4951 | ||
| 4952 | funlockfile(fp); | |
| 4953 | fclose(fp); | |
| 4954 | } | |
| 4955 | #endif | |
| 4956 | ||
| 4957 | static void close_local_endpoint(struct connection *conn) { | |
| 4958 | struct mg_connection *c = &conn->mg_conn; | |
| 4959 | // Must be done before free() | |
| 4960 | int keep_alive = should_keep_alive(&conn->mg_conn) && | |
| 4961 | (conn->endpoint_type == EP_FILE || conn->endpoint_type == EP_USER); | |
| 4962 | DBG(("%p %d %d %d", conn, conn->endpoint_type, keep_alive, | |
| 4963 | conn->ns_conn->flags)); | |
| 4964 | ||
| 4965 | switch (conn->endpoint_type) { | |
| 4966 | case EP_PUT: | |
| 4967 | case EP_FILE: | |
| 4968 | close(conn->endpoint.fd); | |
| 4969 | break; | |
| 4970 | case EP_CGI: | |
| 4971 | case EP_PROXY: | |
| 4972 | if (conn->endpoint.nc != NULL) { | |
| 4973 | DBG(("%p %p %p :-)", conn, conn->ns_conn, conn->endpoint.nc)); | |
| 4974 | conn->endpoint.nc->flags |= NSF_CLOSE_IMMEDIATELY; | |
| 4975 | conn->endpoint.nc->user_data = NULL; | |
| 4976 | } | |
| 4977 | break; | |
| 4978 | default: break; | |
| 4979 | } | |
| 4980 | ||
| 4981 | #ifndef MONGOOSE_NO_LOGGING | |
| 4982 | if (c->status_code > 0 && conn->endpoint_type != EP_CLIENT && | |
| 4983 | c->status_code != 400) { | |
| 4984 | log_access(conn, conn->server->config_options[ACCESS_LOG_FILE]); | |
| 4985 | } | |
| 4986 | #endif | |
| 4987 | ||
| 4988 | // Gobble possible POST data sent to the URI handler | |
| 4989 | iobuf_free(&conn->ns_conn->recv_iobuf); | |
| 4990 | NS_FREE(conn->request); | |
| 4991 | NS_FREE(conn->path_info); | |
| 4992 | conn->endpoint.nc = NULL; | |
| 4993 | conn->request = conn->path_info = NULL; | |
| 4994 | ||
| 4995 | conn->endpoint_type = EP_NONE; | |
| 4996 | conn->cl = conn->num_bytes_recv = conn->request_len = 0; | |
| 4997 | conn->ns_conn->flags &= ~(NSF_FINISHED_SENDING_DATA | NSF_DISCARD | | |
| 4998 | NSF_BUFFER_BUT_DONT_SEND | NSF_CLOSE_IMMEDIATELY | | |
| 4999 | MG_HEADERS_SENT | MG_USING_CHUNKED_API); | |
| 5000 | ||
| 5001 | // Do not memset() the whole structure, as some of the fields | |
| 5002 | // (IP addresses & ports, server_param) must survive. Nullify the rest. | |
| 5003 | c->request_method = c->uri = c->http_version = c->query_string = NULL; | |
| 5004 | c->num_headers = c->status_code = c->is_websocket = c->content_len = 0; | |
| 5005 | c->callback_param = NULL; | |
| 5006 | ||
| 5007 | if (keep_alive) { | |
| 5008 | on_recv_data(conn); // Can call us recursively if pipelining is used | |
| 5009 | } else { | |
| 5010 | conn->ns_conn->flags |= conn->ns_conn->send_iobuf.len == 0 ? | |
| 5011 | NSF_CLOSE_IMMEDIATELY : NSF_FINISHED_SENDING_DATA; | |
| 5012 | } | |
| 5013 | } | |
| 5014 | ||
| 5015 | static void transfer_file_data(struct connection *conn) { | |
| 5016 | char buf[IOBUF_SIZE]; | |
| 5017 | size_t n; | |
| 5018 | ||
| 5019 | // If output buffer is too big, don't send anything. Wait until | |
| 5020 | // mongoose drains already buffered data to the client. | |
| 5021 | if (conn->ns_conn->send_iobuf.len > sizeof(buf) * 2) return; | |
| 5022 | ||
| 5023 | // Do not send anyt | |
| 5024 | n = read(conn->endpoint.fd, buf, conn->cl < (int64_t) sizeof(buf) ? | |
| 5025 | (int) conn->cl : (int) sizeof(buf)); | |
| 5026 | ||
| 5027 | if (n <= 0) { | |
| 5028 | close_local_endpoint(conn); | |
| 5029 | } else if (n > 0) { | |
| 5030 | conn->cl -= n; | |
| 5031 | ns_send(conn->ns_conn, buf, n); | |
| 5032 | if (conn->cl <= 0) { | |
| 5033 | close_local_endpoint(conn); | |
| 5034 | } | |
| 5035 | } | |
| 5036 | } | |
| 5037 | ||
| 5038 | time_t mg_poll_server(struct mg_server *server, int milliseconds) { | |
| 5039 | return ns_mgr_poll(&server->ns_mgr, milliseconds); | |
| 5040 | } | |
| 5041 | ||
| 5042 | void mg_destroy_server(struct mg_server **server) { | |
| 5043 | if (server != NULL && *server != NULL) { | |
| 5044 | struct mg_server *s = *server; | |
| 5045 | int i; | |
| 5046 | ||
| 5047 | ns_mgr_free(&s->ns_mgr); | |
| 5048 | for (i = 0; i < (int) ARRAY_SIZE(s->config_options); i++) { | |
| 5049 | NS_FREE(s->config_options[i]); // It is OK to free(NULL) | |
| 5050 | } | |
| 5051 | NS_FREE(s); | |
| 5052 | *server = NULL; | |
| 5053 | } | |
| 5054 | } | |
| 5055 | ||
| 5056 | struct mg_connection *mg_next(struct mg_server *s, struct mg_connection *c) { | |
| 5057 | struct ns_connection *nc = ns_next(&s->ns_mgr, c == NULL ? NULL : | |
| 5058 | MG_CONN_2_CONN(c)->ns_conn); | |
| 5059 | if (nc != NULL && nc->user_data != NULL) { | |
| 5060 | return & ((struct connection *) nc->user_data)->mg_conn; | |
| 5061 | } else { | |
| 5062 | return NULL; | |
| 5063 | } | |
| 5064 | } | |
| 5065 | ||
| 5066 | static int get_var(const char *data, size_t data_len, const char *name, | |
| 5067 | char *dst, size_t dst_len, int n) { | |
| 5068 | const char *p, *e = data + data_len, *s; | |
| 5069 | size_t name_len; | |
| 5070 | int i = 0, len = -1; | |
| 5071 | ||
| 5072 | if (dst == NULL || dst_len == 0) { | |
| 5073 | len = -2; | |
| 5074 | } else if (data == NULL || name == NULL || data_len == 0) { | |
| 5075 | dst[0] = '\0'; | |
| 5076 | } else { | |
| 5077 | name_len = strlen(name); | |
| 5078 | dst[0] = '\0'; | |
| 5079 | ||
| 5080 | // data is "var1=val1&var2=val2...". Find variable first | |
| 5081 | for (p = data; p + name_len < e; p++) { | |
| 5082 | if ((p == data || p[-1] == '&') && p[name_len] == '=' && | |
| 5083 | !mg_strncasecmp(name, p, name_len)) { | |
| 5084 | ||
| 5085 | if (n != i++) continue; | |
| 5086 | ||
| 5087 | // Point p to variable value | |
| 5088 | p += name_len + 1; | |
| 5089 | ||
| 5090 | // Point s to the end of the value | |
| 5091 | s = (const char *) memchr(p, '&', (size_t)(e - p)); | |
| 5092 | if (s == NULL) { | |
| 5093 | s = e; | |
| 5094 | } | |
| 5095 | assert(s >= p); | |
| 5096 | ||
| 5097 | // Decode variable into destination buffer | |
| 5098 | len = mg_url_decode(p, (size_t)(s - p), dst, dst_len, 1); | |
| 5099 | ||
| 5100 | // Redirect error code from -1 to -2 (destination buffer too small). | |
| 5101 | if (len == -1) { | |
| 5102 | len = -2; | |
| 5103 | } | |
| 5104 | break; | |
| 5105 | } | |
| 5106 | } | |
| 5107 | } | |
| 5108 | ||
| 5109 | return len; | |
| 5110 | } | |
| 5111 | ||
| 5112 | int mg_get_var_n(const struct mg_connection *conn, const char *name, | |
| 5113 | char *dst, size_t dst_len, int n) { | |
| 5114 | int len = get_var(conn->query_string, conn->query_string == NULL ? 0 : | |
| 5115 | strlen(conn->query_string), name, dst, dst_len, n); | |
| 5116 | if (len == -1) { | |
| 5117 | len = get_var(conn->content, conn->content_len, name, dst, dst_len, n); | |
| 5118 | } | |
| 5119 | return len; | |
| 5120 | } | |
| 5121 | ||
| 5122 | int mg_get_var(const struct mg_connection *conn, const char *name, | |
| 5123 | char *dst, size_t dst_len) { | |
| 5124 | return mg_get_var_n(conn, name, dst, dst_len, 0); | |
| 5125 | } | |
| 5126 | ||
| 5127 | static int get_line_len(const char *buf, int buf_len) { | |
| 5128 | int len = 0; | |
| 5129 | while (len < buf_len && buf[len] != '\n') len++; | |
| 5130 | return buf[len] == '\n' ? len + 1: -1; | |
| 5131 | } | |
| 5132 | ||
| 5133 | int mg_parse_multipart(const char *buf, int buf_len, | |
| 5134 | char *var_name, int var_name_len, | |
| 5135 | char *file_name, int file_name_len, | |
| 5136 | const char **data, int *data_len) { | |
| 5137 | static const char cd[] = "Content-Disposition: "; | |
| 5138 | //struct mg_connection c; | |
| 5139 | int hl, bl, n, ll, pos, cdl = sizeof(cd) - 1; | |
| 5140 | //char *p; | |
| 5141 | ||
| 5142 | if (buf == NULL || buf_len <= 0) return 0; | |
| 5143 | if ((hl = get_request_len(buf, buf_len)) <= 0) return 0; | |
| 5144 | if (buf[0] != '-' || buf[1] != '-' || buf[2] == '\n') return 0; | |
| 5145 | ||
| 5146 | // Get boundary length | |
| 5147 | bl = get_line_len(buf, buf_len); | |
| 5148 | ||
| 5149 | // Loop through headers, fetch variable name and file name | |
| 5150 | var_name[0] = file_name[0] = '\0'; | |
| 5151 | for (n = bl; (ll = get_line_len(buf + n, hl - n)) > 0; n += ll) { | |
| 5152 | if (mg_strncasecmp(cd, buf + n, cdl) == 0) { | |
| 5153 | parse_header(buf + n + cdl, ll - (cdl + 2), "name", | |
| 5154 | var_name, var_name_len); | |
| 5155 | parse_header(buf + n + cdl, ll - (cdl + 2), "filename", | |
| 5156 | file_name, file_name_len); | |
| 5157 | } | |
| 5158 | } | |
| 5159 | ||
| 5160 | // Scan body, search for terminating boundary | |
| 5161 | for (pos = hl; pos + (bl - 2) < buf_len; pos++) { | |
| 5162 | if (buf[pos] == '-' && !memcmp(buf, &buf[pos], bl - 2)) { | |
| 5163 | if (data_len != NULL) *data_len = (pos - 2) - hl; | |
| 5164 | if (data != NULL) *data = buf + hl; | |
| 5165 | return pos; | |
| 5166 | } | |
| 5167 | } | |
| 5168 | ||
| 5169 | return 0; | |
| 5170 | } | |
| 5171 | ||
| 5172 | const char **mg_get_valid_option_names(void) { | |
| 5173 | return static_config_options; | |
| 5174 | } | |
| 5175 | ||
| 5176 | void mg_copy_listeners(struct mg_server *s, struct mg_server *to) { | |
| 5177 | struct ns_connection *c; | |
| 5178 | for (c = ns_next(&s->ns_mgr, NULL); c != NULL; c = ns_next(&s->ns_mgr, c)) { | |
| 5179 | struct ns_connection *tmp; | |
| 5180 | if ((c->flags & NSF_LISTENING) && | |
| 5181 | (tmp = (struct ns_connection *) NS_MALLOC(sizeof(*tmp))) != NULL) { | |
| 5182 | memcpy(tmp, c, sizeof(*tmp)); | |
| 5183 | ||
| 5184 | #if defined(NS_ENABLE_SSL) && defined(HEADER_SSL_H) | |
| 5185 | /* OpenSSL only. See https://github.com/cesanta/mongoose/issues/441 */ | |
| 5186 | if (tmp->ssl_ctx != NULL) { | |
| 5187 | tmp->ssl_ctx->references++; | |
| 5188 | } | |
| 5189 | #endif | |
| 5190 | ||
| 5191 | tmp->mgr = &to->ns_mgr; | |
| 5192 | ns_add_conn(tmp->mgr, tmp); | |
| 5193 | } | |
| 5194 | } | |
| 5195 | } | |
| 5196 | ||
| 5197 | static int get_option_index(const char *name) { | |
| 5198 | int i; | |
| 5199 | ||
| 5200 | for (i = 0; static_config_options[i * 2] != NULL; i++) { | |
| 5201 | if (strcmp(static_config_options[i * 2], name) == 0) { | |
| 5202 | return i; | |
| 5203 | } | |
| 5204 | } | |
| 5205 | return -1; | |
| 5206 | } | |
| 5207 | ||
| 5208 | static void set_default_option_values(char **opts) { | |
| 5209 | const char *value, **all_opts = mg_get_valid_option_names(); | |
| 5210 | int i; | |
| 5211 | ||
| 5212 | for (i = 0; all_opts[i * 2] != NULL; i++) { | |
| 5213 | value = all_opts[i * 2 + 1]; | |
| 5214 | if (opts[i] == NULL && value != NULL) { | |
| 5215 | opts[i] = mg_strdup(value); | |
| 5216 | } | |
| 5217 | } | |
| 5218 | } | |
| 5219 | ||
| 5220 | const char *mg_set_option(struct mg_server *server, const char *name, | |
| 5221 | const char *value) { | |
| 5222 | int ind = get_option_index(name); | |
| 5223 | const char *error_msg = NULL; | |
| 5224 | char **v = NULL; | |
| 5225 | ||
| 5226 | if (ind < 0) return "No such option"; | |
| 5227 | v = &server->config_options[ind]; | |
| 5228 | ||
| 5229 | // Return success immediately if setting to the same value | |
| 5230 | if ((*v == NULL && value == NULL) || | |
| 5231 | (value != NULL && *v != NULL && !strcmp(value, *v))) { | |
| 5232 | return NULL; | |
| 5233 | } | |
| 5234 | ||
| 5235 | if (*v != NULL) { | |
| 5236 | NS_FREE(*v); | |
| 5237 | *v = NULL; | |
| 5238 | } | |
| 5239 | ||
| 5240 | if (value == NULL || value[0] == '\0') return NULL; | |
| 5241 | ||
| 5242 | *v = mg_strdup(value); | |
| 5243 | DBG(("%s [%s]", name, *v)); | |
| 5244 | ||
| 5245 | if (ind == LISTENING_PORT) { | |
| 5246 | char buf[500] = ""; | |
| 5247 | size_t n = 0; | |
| 5248 | struct vec vec; | |
| 5249 | ||
| 5250 | /* | |
| 5251 | * Ports can be specified as 0, meaning that OS has to choose any | |
| 5252 | * free port that is available. In order to pass chosen port number to | |
| 5253 | * the user, we rewrite all 0 port to chosen values. | |
| 5254 | */ | |
| 5255 | while ((value = next_option(value, &vec, NULL)) != NULL) { | |
| 5256 | struct ns_connection *c = ns_bind(&server->ns_mgr, vec.ptr, | |
| 5257 | mg_ev_handler, NULL); | |
| 5258 | if (c == NULL) { | |
| 5259 | error_msg = "Cannot bind to port"; | |
| 5260 | break; | |
| 5261 | } else { | |
| 5262 | char buf2[50], cert[100], ca[100]; | |
| 5263 | union socket_address sa; | |
| 5264 | int proto, use_ssl; | |
| 5265 | ||
| 5266 | ns_parse_address(vec.ptr, &sa, &proto, &use_ssl, cert, ca); | |
| 5267 | ns_sock_to_str(c->sock, buf2, sizeof(buf2), | |
| 5268 | memchr(vec.ptr, ':', vec.len) == NULL ? 2 : 3); | |
| 5269 | ||
| 5270 | n += snprintf(buf + n, sizeof(buf) - n, "%s%s%s%s%s%s%s", | |
| 5271 | n > 0 ? "," : "", | |
| 5272 | use_ssl ? "ssl://" : "", | |
| 5273 | buf2, cert[0] ? ":" : "", cert, ca[0] ? ":" : "", ca); | |
| 5274 | } | |
| 5275 | } | |
| 5276 | buf[sizeof(buf) - 1] = '\0'; | |
| 5277 | NS_FREE(*v); | |
| 5278 | *v = mg_strdup(buf); | |
| 5279 | #ifndef MONGOOSE_NO_FILESYSTEM | |
| 5280 | } else if (ind == HEXDUMP_FILE) { | |
| 5281 | server->ns_mgr.hexdump_file = *v; | |
| 5282 | #endif | |
| 5283 | #if !defined(_WIN32) && !defined(MONGOOSE_NO_USER) | |
| 5284 | } else if (ind == RUN_AS_USER) { | |
| 5285 | struct passwd *pw; | |
| 5286 | if ((pw = getpwnam(value)) == NULL) { | |
| 5287 | error_msg = "Unknown user"; | |
| 5288 | } else if (setgid(pw->pw_gid) != 0) { | |
| 5289 | error_msg = "setgid() failed"; | |
| 5290 | } else if (setuid(pw->pw_uid) != 0) { | |
| 5291 | error_msg = "setuid() failed"; | |
| 5292 | } | |
| 5293 | #endif | |
| 5294 | } | |
| 5295 | ||
| 5296 | return error_msg; | |
| 5297 | } | |
| 5298 | ||
| 5299 | static void set_ips(struct ns_connection *nc, int is_rem) { | |
| 5300 | struct connection *conn = (struct connection *) nc->user_data; | |
| 5301 | struct mg_connection *c = &conn->mg_conn; | |
| 5302 | char buf[100]; | |
| 5303 | ||
| 5304 | ns_sock_to_str(nc->sock, buf, sizeof(buf), is_rem ? 7 : 3); | |
| 5305 | sscanf(buf, "%47[^:]:%hu", | |
| 5306 | is_rem ? c->remote_ip : c->local_ip, | |
| 5307 | is_rem ? &c->remote_port : &c->local_port); | |
| 5308 | //DBG(("%p %s %s", conn, is_rem ? "rem" : "loc", buf)); | |
| 5309 | } | |
| 5310 | ||
| 5311 | static void on_accept(struct ns_connection *nc, union socket_address *sa) { | |
| 5312 | struct mg_server *server = (struct mg_server *) nc->mgr; | |
| 5313 | struct connection *conn; | |
| 5314 | ||
| 5315 | if (!check_acl(server->config_options[ACCESS_CONTROL_LIST], | |
| 5316 | ntohl(* (uint32_t *) &sa->sin.sin_addr)) || | |
| 5317 | (conn = (struct connection *) NS_CALLOC(1, sizeof(*conn))) == NULL) { | |
| 5318 | nc->flags |= NSF_CLOSE_IMMEDIATELY; | |
| 5319 | } else { | |
| 5320 | // Circularly link two connection structures | |
| 5321 | nc->user_data = conn; | |
| 5322 | conn->ns_conn = nc; | |
| 5323 | ||
| 5324 | // Initialize the rest of connection attributes | |
| 5325 | conn->server = server; | |
| 5326 | conn->mg_conn.server_param = nc->mgr->user_data; | |
| 5327 | set_ips(nc, 1); | |
| 5328 | set_ips(nc, 0); | |
| 5329 | } | |
| 5330 | } | |
| 5331 | ||
| 5332 | static void process_udp(struct ns_connection *nc) { | |
| 5333 | struct iobuf *io = &nc->recv_iobuf; | |
| 5334 | struct connection conn; | |
| 5335 | ||
| 5336 | memset(&conn, 0, sizeof(conn)); | |
| 5337 | conn.ns_conn = nc; | |
| 5338 | conn.server = (struct mg_server *) nc->mgr; | |
| 5339 | conn.request_len = parse_http_message(io->buf, io->len, &conn.mg_conn); | |
| 5340 | on_recv_data(&conn); | |
| 5341 | //ns_printf(nc, "%s", "HTTP/1.0 200 OK\r\n\r\n"); | |
| 5342 | } | |
| 5343 | ||
| 5344 | #ifdef MONGOOSE_SEND_NS_EVENTS | |
| 5345 | static void send_ns_event(struct ns_connection *nc, int ev, void *p) { | |
| 5346 | struct connection *conn = (struct connection *) nc->user_data; | |
| 5347 | if (conn != NULL) { | |
| 5348 | void *param[2] = { nc, p }; | |
| 5349 | conn->mg_conn.callback_param = param; | |
| 5350 | call_user(conn, (enum mg_event) ev); | |
| 5351 | } | |
| 5352 | } | |
| 5353 | #else | |
| 5354 | static void send_ns_event(struct ns_connection *nc, int ev, void *p) { | |
| 5355 | (void) nc; (void) p; (void) ev; | |
| 5356 | } | |
| 5357 | #endif | |
| 5358 | ||
| 5359 | static void mg_ev_handler(struct ns_connection *nc, int ev, void *p) { | |
| 5360 | struct connection *conn = (struct connection *) nc->user_data; | |
| 5361 | ||
| 5362 | // Send NS event to the handler. Note that call_user won't send an event | |
| 5363 | // if conn == NULL. Therefore, repeat this for NS_ACCEPT event as well. | |
| 5364 | send_ns_event(nc, ev, p); | |
| 5365 | ||
| 5366 | switch (ev) { | |
| 5367 | case NS_ACCEPT: | |
| 5368 | on_accept(nc, (union socket_address *) p); | |
| 5369 | send_ns_event(nc, ev, p); | |
| 5370 | break; | |
| 5371 | ||
| 5372 | case NS_CONNECT: | |
| 5373 | if (nc->user_data != NULL) { | |
| 5374 | set_ips(nc, 1); | |
| 5375 | set_ips(nc, 0); | |
| 5376 | } | |
| 5377 | conn->mg_conn.status_code = * (int *) p; | |
| 5378 | if (conn->mg_conn.status_code != 0 || | |
| 5379 | (!(nc->flags & MG_PROXY_CONN) && | |
| 5380 | call_user(conn, MG_CONNECT) == MG_FALSE)) { | |
| 5381 | nc->flags |= NSF_CLOSE_IMMEDIATELY; | |
| 5382 | } | |
| 5383 | break; | |
| 5384 | ||
| 5385 | case NS_RECV: | |
| 5386 | if (conn != NULL) { | |
| 5387 | conn->num_bytes_recv += * (int *) p; | |
| 5388 | } | |
| 5389 | ||
| 5390 | if (nc->flags & NSF_UDP) { | |
| 5391 | process_udp(nc); | |
| 5392 | } else if (nc->listener != NULL) { | |
| 5393 | on_recv_data(conn); | |
| 5394 | #ifndef MONGOOSE_NO_CGI | |
| 5395 | } else if (nc->flags & MG_CGI_CONN) { | |
| 5396 | on_cgi_data(nc); | |
| 5397 | #endif | |
| 5398 | } else if (nc->flags & MG_PROXY_CONN) { | |
| 5399 | if (conn != NULL) { | |
| 5400 | ns_forward(nc, conn->ns_conn); | |
| 5401 | } | |
| 5402 | } else { | |
| 5403 | process_response(conn); | |
| 5404 | } | |
| 5405 | break; | |
| 5406 | ||
| 5407 | case NS_SEND: | |
| 5408 | break; | |
| 5409 | ||
| 5410 | case NS_CLOSE: | |
| 5411 | nc->user_data = NULL; | |
| 5412 | if (nc->flags & (MG_CGI_CONN | MG_PROXY_CONN)) { | |
| 5413 | DBG(("%p %p closing cgi/proxy conn", conn, nc)); | |
| 5414 | if (conn && conn->ns_conn) { | |
| 5415 | conn->ns_conn->flags &= ~NSF_BUFFER_BUT_DONT_SEND; | |
| 5416 | conn->ns_conn->flags |= conn->ns_conn->send_iobuf.len > 0 ? | |
| 5417 | NSF_FINISHED_SENDING_DATA : NSF_CLOSE_IMMEDIATELY; | |
| 5418 | conn->endpoint.nc = NULL; | |
| 5419 | } | |
| 5420 | } else if (conn != NULL) { | |
| 5421 | DBG(("%p %p %d closing", conn, nc, conn->endpoint_type)); | |
| 5422 | ||
| 5423 | if (conn->endpoint_type == EP_CLIENT && nc->recv_iobuf.len > 0) { | |
| 5424 | call_http_client_handler(conn); | |
| 5425 | } | |
| 5426 | ||
| 5427 | call_user(conn, MG_CLOSE); | |
| 5428 | close_local_endpoint(conn); | |
| 5429 | conn->ns_conn = NULL; | |
| 5430 | NS_FREE(conn); | |
| 5431 | } | |
| 5432 | break; | |
| 5433 | ||
| 5434 | case NS_POLL: | |
| 5435 | if (conn != NULL) { | |
| 5436 | if (call_user(conn, MG_POLL) == MG_TRUE) { | |
| 5437 | if (conn->ns_conn->flags & MG_HEADERS_SENT) { | |
| 5438 | write_terminating_chunk(conn); | |
| 5439 | } | |
| 5440 | close_local_endpoint(conn); | |
| 5441 | /* | |
| 5442 | * MG_POLL callback returned MG_TRUE, | |
| 5443 | * i.e. data is sent, set corresponding flag | |
| 5444 | */ | |
| 5445 | conn->ns_conn->flags |= NSF_FINISHED_SENDING_DATA; | |
| 5446 | } | |
| 5447 | ||
| 5448 | if (conn->endpoint_type == EP_FILE) { | |
| 5449 | transfer_file_data(conn); | |
| 5450 | } | |
| 5451 | } | |
| 5452 | ||
| 5453 | // Expire idle connections | |
| 5454 | { | |
| 5455 | time_t current_time = * (time_t *) p; | |
| 5456 | ||
| 5457 | if (conn != NULL && conn->mg_conn.is_websocket) { | |
| 5458 | ping_idle_websocket_connection(conn, current_time); | |
| 5459 | } | |
| 5460 | ||
| 5461 | if (nc->listener != NULL && | |
| 5462 | nc->last_io_time + MONGOOSE_IDLE_TIMEOUT_SECONDS < current_time) { | |
| 5463 | mg_ev_handler(nc, NS_CLOSE, NULL); | |
| 5464 | nc->flags |= NSF_CLOSE_IMMEDIATELY; | |
| 5465 | } | |
| 5466 | } | |
| 5467 | break; | |
| 5468 | ||
| 5469 | default: | |
| 5470 | break; | |
| 5471 | } | |
| 5472 | } | |
| 5473 | ||
| 5474 | static void iter2(struct ns_connection *nc, int ev, void *param) { | |
| 5475 | mg_handler_t func = NULL; | |
| 5476 | struct connection *conn = (struct connection *) nc->user_data; | |
| 5477 | const char *msg = (const char *) param; | |
| 5478 | int n; | |
| 5479 | (void) ev; | |
| 5480 | ||
| 5481 | //DBG(("%p [%s]", conn, msg)); | |
| 5482 | if (sscanf(msg, "%p %n", (void **) &func, &n) && func != NULL && conn != NULL) { | |
| 5483 | conn->mg_conn.callback_param = (void *) (msg + n); | |
| 5484 | func(&conn->mg_conn, MG_POLL); | |
| 5485 | } | |
| 5486 | } | |
| 5487 | ||
| 5488 | void mg_wakeup_server_ex(struct mg_server *server, mg_handler_t cb, | |
| 5489 | const char *fmt, ...) { | |
| 5490 | va_list ap; | |
| 5491 | char buf[8 * 1024]; | |
| 5492 | int len; | |
| 5493 | ||
| 5494 | // Encode callback (cb) into a buffer | |
| 5495 | len = snprintf(buf, sizeof(buf), "%p ", cb); | |
| 5496 | va_start(ap, fmt); | |
| 5497 | len += vsnprintf(buf + len, sizeof(buf) - len, fmt, ap); | |
| 5498 | va_end(ap); | |
| 5499 | ||
| 5500 | // "len + 1" is to include terminating \0 in the message | |
| 5501 | ns_broadcast(&server->ns_mgr, iter2, buf, len + 1); | |
| 5502 | } | |
| 5503 | ||
| 5504 | void mg_wakeup_server(struct mg_server *server) { | |
| 5505 | ns_broadcast(&server->ns_mgr, NULL, (void *) "", 0); | |
| 5506 | } | |
| 5507 | ||
| 5508 | const char *mg_get_option(const struct mg_server *server, const char *name) { | |
| 5509 | const char **opts = (const char **) server->config_options; | |
| 5510 | int i = get_option_index(name); | |
| 5511 | return i == -1 ? NULL : opts[i] == NULL ? "" : opts[i]; | |
| 5512 | } | |
| 5513 | ||
| 5514 | struct mg_server *mg_create_server(void *server_data, mg_handler_t handler) { | |
| 5515 | struct mg_server *server = (struct mg_server *) NS_CALLOC(1, sizeof(*server)); | |
| 5516 | ns_mgr_init(&server->ns_mgr, server_data); | |
| 5517 | set_default_option_values(server->config_options); | |
| 5518 | server->event_handler = handler; | |
| 5519 | return server; | |
| 5520 | } |
| r0 | r250110 | |
|---|---|---|
| 1 | // Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com> | |
| 2 | // Copyright (c) 2013-2014 Cesanta Software Limited | |
| 3 | // All rights reserved | |
| 4 | // | |
| 5 | // This software is dual-licensed: you can redistribute it and/or modify | |
| 6 | // it under the terms of the GNU General Public License version 2 as | |
| 7 | // published by the Free Software Foundation. For the terms of this | |
| 8 | // license, see <http://www.gnu.org/licenses/>. | |
| 9 | // | |
| 10 | // You are free to use this software under the terms of the GNU General | |
| 11 | // Public License, but WITHOUT ANY WARRANTY; without even the implied | |
| 12 | // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | |
| 13 | // See the GNU General Public License for more details. | |
| 14 | // | |
| 15 | // Alternatively, you can license this software under a commercial | |
| 16 | // license, as set out in <http://cesanta.com/>. | |
| 17 | ||
| 18 | #ifndef MONGOOSE_HEADER_INCLUDED | |
| 19 | #define MONGOOSE_HEADER_INCLUDED | |
| 20 | ||
| 21 | #define MONGOOSE_VERSION "5.6" | |
| 22 | ||
| 23 | #include <stdarg.h> // required for va_list | |
| 24 | #include <stdio.h> // required for FILE | |
| 25 | #include <stddef.h> // required for size_t | |
| 26 | #include <sys/types.h> // required for time_t | |
| 27 | ||
| 28 | #ifdef __cplusplus | |
| 29 | extern "C" { | |
| 30 | #endif // __cplusplus | |
| 31 | ||
| 32 | // This structure contains information about HTTP request. | |
| 33 | struct mg_connection { | |
| 34 | const char *request_method; // "GET", "POST", etc | |
| 35 | const char *uri; // URL-decoded URI | |
| 36 | const char *http_version; // E.g. "1.0", "1.1" | |
| 37 | const char *query_string; // URL part after '?', not including '?', or NULL | |
| 38 | ||
| 39 | char remote_ip[48]; // Max IPv6 string length is 45 characters | |
| 40 | char local_ip[48]; // Local IP address | |
| 41 | unsigned short remote_port; // Client's port | |
| 42 | unsigned short local_port; // Local port number | |
| 43 | ||
| 44 | int num_headers; // Number of HTTP headers | |
| 45 | struct mg_header { | |
| 46 | const char *name; // HTTP header name | |
| 47 | const char *value; // HTTP header value | |
| 48 | } http_headers[30]; | |
| 49 | ||
| 50 | char *content; // POST (or websocket message) data, or NULL | |
| 51 | size_t content_len; // Data length | |
| 52 | ||
| 53 | int is_websocket; // Connection is a websocket connection | |
| 54 | int status_code; // HTTP status code for HTTP error handler | |
| 55 | int wsbits; // First byte of the websocket frame | |
| 56 | void *server_param; // Parameter passed to mg_create_server() | |
| 57 | void *connection_param; // Placeholder for connection-specific data | |
| 58 | void *callback_param; | |
| 59 | }; | |
| 60 | ||
| 61 | struct mg_server; // Opaque structure describing server instance | |
| 62 | enum mg_result { MG_FALSE, MG_TRUE, MG_MORE }; | |
| 63 | enum mg_event { | |
| 64 | MG_POLL = 100, // If callback returns MG_TRUE connection closes | |
| 65 | // after all of data is sent | |
| 66 | MG_CONNECT, // If callback returns MG_FALSE, connect fails | |
| 67 | MG_AUTH, // If callback returns MG_FALSE, authentication fails | |
| 68 | MG_REQUEST, // If callback returns MG_FALSE, Mongoose continues with req | |
| 69 | MG_REPLY, // If callback returns MG_FALSE, Mongoose closes connection | |
| 70 | MG_RECV, // Mongoose has received POST data chunk. | |
| 71 | // Callback should return a number of bytes to discard from | |
| 72 | // the receive buffer, or -1 to close the connection. | |
| 73 | MG_CLOSE, // Connection is closed, callback return value is ignored | |
| 74 | MG_WS_HANDSHAKE, // New websocket connection, handshake request | |
| 75 | MG_WS_CONNECT, // New websocket connection established | |
| 76 | MG_HTTP_ERROR // If callback returns MG_FALSE, Mongoose continues with err | |
| 77 | }; | |
| 78 | typedef int (*mg_handler_t)(struct mg_connection *, enum mg_event); | |
| 79 | ||
| 80 | // Websocket opcodes, from http://tools.ietf.org/html/rfc6455 | |
| 81 | enum { | |
| 82 | WEBSOCKET_OPCODE_CONTINUATION = 0x0, | |
| 83 | WEBSOCKET_OPCODE_TEXT = 0x1, | |
| 84 | WEBSOCKET_OPCODE_BINARY = 0x2, | |
| 85 | WEBSOCKET_OPCODE_CONNECTION_CLOSE = 0x8, | |
| 86 | WEBSOCKET_OPCODE_PING = 0x9, | |
| 87 | WEBSOCKET_OPCODE_PONG = 0xa | |
| 88 | }; | |
| 89 | ||
| 90 | // Server management functions | |
| 91 | struct mg_server *mg_create_server(void *server_param, mg_handler_t handler); | |
| 92 | void mg_destroy_server(struct mg_server **); | |
| 93 | const char *mg_set_option(struct mg_server *, const char *opt, const char *val); | |
| 94 | time_t mg_poll_server(struct mg_server *, int milliseconds); | |
| 95 | const char **mg_get_valid_option_names(void); | |
| 96 | const char *mg_get_option(const struct mg_server *server, const char *name); | |
| 97 | void mg_copy_listeners(struct mg_server *from, struct mg_server *to); | |
| 98 | struct mg_connection *mg_next(struct mg_server *, struct mg_connection *); | |
| 99 | void mg_wakeup_server(struct mg_server *); | |
| 100 | void mg_wakeup_server_ex(struct mg_server *, mg_handler_t, const char *, ...); | |
| 101 | struct mg_connection *mg_connect(struct mg_server *, const char *); | |
| 102 | ||
| 103 | // Connection management functions | |
| 104 | void mg_send_status(struct mg_connection *, int status_code); | |
| 105 | void mg_send_header(struct mg_connection *, const char *name, const char *val); | |
| 106 | size_t mg_send_data(struct mg_connection *, const void *data, int data_len); | |
| 107 | size_t mg_printf_data(struct mg_connection *, const char *format, ...); | |
| 108 | size_t mg_vprintf_data(struct mg_connection *, const char *format, va_list ap); | |
| 109 | size_t mg_write(struct mg_connection *, const void *buf, size_t len); | |
| 110 | size_t mg_printf(struct mg_connection *conn, const char *fmt, ...); | |
| 111 | size_t mg_vprintf(struct mg_connection *conn, const char *fmt, va_list ap); | |
| 112 | ||
| 113 | size_t mg_websocket_write(struct mg_connection *, int opcode, | |
| 114 | const char *data, size_t data_len); | |
| 115 | size_t mg_websocket_printf(struct mg_connection* conn, int opcode, | |
| 116 | const char *fmt, ...); | |
| 117 | ||
| 118 | void mg_send_file(struct mg_connection *, const char *path, const char *); | |
| 119 | void mg_send_file_data(struct mg_connection *, int fd); | |
| 120 | ||
| 121 | const char *mg_get_header(const struct mg_connection *, const char *name); | |
| 122 | const char *mg_get_mime_type(const char *name, const char *default_mime_type); | |
| 123 | int mg_get_var(const struct mg_connection *conn, const char *var_name, | |
| 124 | char *buf, size_t buf_len); | |
| 125 | int mg_get_var_n(const struct mg_connection *conn, const char *var_name, | |
| 126 | char *buf, size_t buf_len, int n); | |
| 127 | int mg_parse_header(const char *hdr, const char *var_name, char *buf, size_t); | |
| 128 | int mg_parse_multipart(const char *buf, int buf_len, | |
| 129 | char *var_name, int var_name_len, | |
| 130 | char *file_name, int file_name_len, | |
| 131 | const char **data, int *data_len); | |
| 132 | ||
| 133 | ||
| 134 | // Utility functions | |
| 135 | void *mg_start_thread(void *(*func)(void *), void *param); | |
| 136 | char *mg_md5(char buf[33], ...); | |
| 137 | int mg_authorize_digest(struct mg_connection *c, FILE *fp); | |
| 138 | size_t mg_url_encode(const char *src, size_t s_len, char *dst, size_t dst_len); | |
| 139 | int mg_url_decode(const char *src, size_t src_len, char *dst, size_t dst_len, int); | |
| 140 | int mg_terminate_ssl(struct mg_connection *c, const char *cert); | |
| 141 | int mg_forward(struct mg_connection *c, const char *addr); | |
| 142 | void *mg_mmap(FILE *fp, size_t size); | |
| 143 | void mg_munmap(void *p, size_t size); | |
| 144 | ||
| 145 | ||
| 146 | // Templates support | |
| 147 | struct mg_expansion { | |
| 148 | const char *keyword; | |
| 149 | void (*handler)(struct mg_connection *); | |
| 150 | }; | |
| 151 | void mg_template(struct mg_connection *, const char *text, | |
| 152 | struct mg_expansion *expansions); | |
| 153 | ||
| 154 | #ifdef __cplusplus | |
| 155 | } | |
| 156 | #endif // __cplusplus | |
| 157 | ||
| 158 | #endif // MONGOOSE_HEADER_INCLUDED |
| r0 | r250110 | |
|---|---|---|
| 1 | # This program is used to embed arbitrary data into a C binary. It takes | |
| 2 | # a list of files as an input, and produces a .c data file that contains | |
| 3 | # contents of all these files as collection of char arrays. | |
| 4 | # | |
| 5 | # Usage: perl <this_file> <file1> [file2, ...] > embedded_data.c | |
| 6 | ||
| 7 | foreach my $i (0 .. $#ARGV) { | |
| 8 | open FD, '<:raw', $ARGV[$i] or die "Cannot open $ARGV[$i]: $!\n"; | |
| 9 | printf("static const unsigned char v%d[] = {", $i); | |
| 10 | my $byte; | |
| 11 | my $j = 0; | |
| 12 | while (read(FD, $byte, 1)) { | |
| 13 | if (($j % 12) == 0) { | |
| 14 | print "\n"; | |
| 15 | } | |
| 16 | printf ' %#04x,', ord($byte); | |
| 17 | $j++; | |
| 18 | } | |
| 19 | print " 0x00\n};\n"; | |
| 20 | close FD; | |
| 21 | } | |
| 22 | ||
| 23 | print <<EOS; | |
| 24 | ||
| 25 | #include <stddef.h> | |
| 26 | #include <string.h> | |
| 27 | ||
| 28 | static const struct embedded_file { | |
| 29 | const char *name; | |
| 30 | const unsigned char *data; | |
| 31 | size_t size; | |
| 32 | } embedded_files[] = { | |
| 33 | EOS | |
| 34 | ||
| 35 | foreach my $i (0 .. $#ARGV) { | |
| 36 | print " {\"$ARGV[$i]\", v$i, sizeof(v$i) - 1},\n"; | |
| 37 | } | |
| 38 | ||
| 39 | print <<EOS; | |
| 40 | {NULL, NULL, 0} | |
| 41 | }; | |
| 42 | ||
| 43 | const char *find_embedded_file(const char *name, size_t *size) { | |
| 44 | const struct embedded_file *p; | |
| 45 | for (p = embedded_files; p->name != NULL; p++) { | |
| 46 | if (!strcmp(p->name, name)) { | |
| 47 | if (size != NULL) { *size = p->size; } | |
| 48 | return (const char *) p->data; | |
| 49 | } | |
| 50 | } | |
| 51 | return NULL; | |
| 52 | } | |
| 53 | EOS |
| r0 | r250110 | |
|---|---|---|
| 1 | # Copyright (c) 2014 Cesanta Software | |
| 2 | # All rights reserved | |
| 3 | ||
| 4 | PROG = unit_test | |
| 5 | PROF_FLAGS = -fprofile-arcs -ftest-coverage -g -O0 -DGUI | |
| 6 | CFLAGS = -W -Wall -pthread -I.. $(PROF_FLAGS) $(CFLAGS_EXTRA) | |
| 7 | SOURCES = $(PROG).c | |
| 8 | ||
| 9 | all: $(PROG) | |
| 10 | ./$(PROG) | |
| 11 | gcov -b $(PROG).c | egrep '^(File|Lines)' | |
| 12 | ||
| 13 | $(PROG): $(SOURCES) Makefile ../mongoose.c clean | |
| 14 | $(CC) -o $(PROG) $(SOURCES) $(CFLAGS) -ldl -lssl | |
| 15 | ||
| 16 | win: | |
| 17 | wine cl $(SOURCES) /MD /nologo /DNDEBUG /O1 /Fe$(PROG).exe | |
| 18 | wine $(PROG).exe | |
| 19 | ||
| 20 | clean: | |
| 21 | rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib *.gc* |
| r0 | r250110 | |
|---|---|---|
| 1 | // Unit test for the mongoose web server. | |
| 2 | // g++ -W -Wall -pedantic -g unit_test.c -lssl && ./a.out | |
| 3 | // cl unit_test.c /MD | |
| 4 | ||
| 5 | #ifndef _WIN32 | |
| 6 | #define NS_ENABLE_IPV6 | |
| 7 | #define NS_ENABLE_SSL | |
| 8 | #endif | |
| 9 | #define MONGOOSE_POST_SIZE_LIMIT 999 | |
| 10 | ||
| 11 | // USE_* definitions must be made before #include "mongoose.c" ! | |
| 12 | #include "../mongoose.c" | |
| 13 | ||
| 14 | #define FAIL(str, line) do { \ | |
| 15 | printf("Fail on line %d: [%s]\n", line, str); \ | |
| 16 | return str; \ | |
| 17 | } while (0) | |
| 18 | ||
| 19 | #define ASSERT(expr) do { \ | |
| 20 | static_num_tests++; \ | |
| 21 | if (!(expr)) FAIL(#expr, __LINE__); \ | |
| 22 | } while (0) | |
| 23 | ||
| 24 | #define RUN_TEST(test) do { const char *msg = test(); \ | |
| 25 | if (msg) return msg; } while (0) | |
| 26 | ||
| 27 | #define HTTP_PORT "45772" | |
| 28 | #define LISTENING_ADDR "127.0.0.1:" HTTP_PORT | |
| 29 | ||
| 30 | static int static_num_tests = 0; | |
| 31 | ||
| 32 | #if 0 | |
| 33 | // Connects to host:port, and sends formatted request to it. Returns | |
| 34 | // malloc-ed reply and reply length, or NULL on error. Reply contains | |
| 35 | // everything including headers, not just the message body. | |
| 36 | static char *wget(const char *host, int port, int *len, const char *fmt, ...) { | |
| 37 | char buf[2000], *reply = NULL; | |
| 38 | int request_len, reply_size = 0, n, sock = -1; | |
| 39 | struct sockaddr_in sin; | |
| 40 | struct hostent *he = NULL; | |
| 41 | va_list ap; | |
| 42 | ||
| 43 | if (host != NULL && | |
| 44 | (he = gethostbyname(host)) != NULL && | |
| 45 | (sock = socket(PF_INET, SOCK_STREAM, 0)) != -1) { | |
| 46 | sin.sin_family = AF_INET; | |
| 47 | sin.sin_port = htons((uint16_t) port); | |
| 48 | sin.sin_addr = * (struct in_addr *) he->h_addr_list[0]; | |
| 49 | if (connect(sock, (struct sockaddr *) &sin, sizeof(sin)) == 0) { | |
| 50 | ||
| 51 | // Format and send the request. | |
| 52 | va_start(ap, fmt); | |
| 53 | request_len = vsnprintf(buf, sizeof(buf), fmt, ap); | |
| 54 | va_end(ap); | |
| 55 | while (request_len > 0 && (n = send(sock, buf, request_len, 0)) > 0) { | |
| 56 | request_len -= n; | |
| 57 | } | |
| 58 | if (request_len == 0) { | |
| 59 | *len = 0; | |
| 60 | while ((n = recv(sock, buf, sizeof(buf), 0)) > 0) { | |
| 61 | if (*len + n > reply_size) { | |
| 62 | // Leak possible | |
| 63 | reply = (char *) realloc(reply, reply_size + sizeof(buf)); | |
| 64 | reply_size += sizeof(buf); | |
| 65 | } | |
| 66 | if (reply != NULL) { | |
| 67 | memcpy(reply + *len, buf, n); | |
| 68 | *len += n; | |
| 69 | } | |
| 70 | } | |
| 71 | } | |
| 72 | closesocket(sock); | |
| 73 | } | |
| 74 | } | |
| 75 | ||
| 76 | return reply; | |
| 77 | } | |
| 78 | #endif | |
| 79 | ||
| 80 | static char *read_file(const char *path, int *size) { | |
| 81 | FILE *fp; | |
| 82 | struct stat st; | |
| 83 | char *data = NULL; | |
| 84 | if ((fp = fopen(path, "rb")) != NULL && !fstat(fileno(fp), &st)) { | |
| 85 | *size = (int) st.st_size; | |
| 86 | data = (char *) malloc(*size); | |
| 87 | fread(data, 1, *size, fp); | |
| 88 | fclose(fp); | |
| 89 | } | |
| 90 | return data; | |
| 91 | } | |
| 92 | ||
| 93 | static const char *test_parse_http_message() { | |
| 94 | struct mg_connection ri; | |
| 95 | char req1[] = "GET / HTTP/1.1\r\n\r\n"; | |
| 96 | char req2[] = "BLAH / HTTP/1.1\r\n\r\n"; | |
| 97 | char req3[] = "GET / HTTP/1.1\r\nBah\r\n"; | |
| 98 | char req4[] = "GET / HTTP/1.1\r\nA: foo bar\r\nB: bar\r\nbaz\r\n\r\n"; | |
| 99 | char req5[] = "GET / HTTP/1.1\r\n\r\n"; | |
| 100 | char req6[] = "G"; | |
| 101 | char req7[] = " blah "; | |
| 102 | char req8[] = " HTTP/1.1 200 OK \n\n"; | |
| 103 | char req9[] = "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n"; | |
| 104 | ||
| 105 | ASSERT(get_request_len("\r\n", 3) == -1); | |
| 106 | ASSERT(get_request_len("\r\n", 2) == 0); | |
| 107 | ASSERT(get_request_len("GET", 3) == 0); | |
| 108 | ASSERT(get_request_len("\n\n", 2) == 2); | |
| 109 | ASSERT(get_request_len("\n\r\n", 3) == 3); | |
| 110 | ASSERT(get_request_len("\xdd\xdd", 2) == 0); | |
| 111 | ASSERT(get_request_len("\xdd\x03", 2) == -1); | |
| 112 | ASSERT(get_request_len(req3, sizeof(req3) - 1) == 0); | |
| 113 | ASSERT(get_request_len(req6, sizeof(req6) - 1) == 0); | |
| 114 | ASSERT(get_request_len(req7, sizeof(req7) - 1) == 0); | |
| 115 | ||
| 116 | ASSERT(parse_http_message(req9, sizeof(req9) - 1, &ri) == sizeof(req9) - 1); | |
| 117 | ASSERT(ri.num_headers == 1); | |
| 118 | ||
| 119 | ASSERT(parse_http_message(req1, sizeof(req1) - 1, &ri) == sizeof(req1) - 1); | |
| 120 | ASSERT(strcmp(ri.http_version, "1.1") == 0); | |
| 121 | ASSERT(ri.num_headers == 0); | |
| 122 | ||
| 123 | ASSERT(parse_http_message(req2, sizeof(req2) - 1, &ri) == (size_t) ~0); | |
| 124 | ASSERT(parse_http_message(req6, 0, &ri) == (size_t) ~0); | |
| 125 | ASSERT(parse_http_message(req8, sizeof(req8) - 1, &ri) == sizeof(req8) - 1); | |
| 126 | ||
| 127 | // TODO(lsm): Fix this. Header value may span multiple lines. | |
| 128 | ASSERT(parse_http_message(req4, sizeof(req4) - 1, &ri) == sizeof(req4) - 1); | |
| 129 | ASSERT(strcmp(ri.http_version, "1.1") == 0); | |
| 130 | ASSERT(ri.num_headers == 3); | |
| 131 | ASSERT(strcmp(ri.http_headers[0].name, "A") == 0); | |
| 132 | ASSERT(strcmp(ri.http_headers[0].value, "foo bar") == 0); | |
| 133 | ASSERT(strcmp(ri.http_headers[1].name, "B") == 0); | |
| 134 | ASSERT(strcmp(ri.http_headers[1].value, "bar") == 0); | |
| 135 | ASSERT(strcmp(ri.http_headers[2].name, "baz\r\n\r") == 0); | |
| 136 | ASSERT(strcmp(ri.http_headers[2].value, "") == 0); | |
| 137 | ||
| 138 | ASSERT(parse_http_message(req5, sizeof(req5) - 1, &ri) == sizeof(req5) - 1); | |
| 139 | ASSERT(strcmp(ri.request_method, "GET") == 0); | |
| 140 | ASSERT(strcmp(ri.http_version, "1.1") == 0); | |
| 141 | ||
| 142 | return NULL; | |
| 143 | } | |
| 144 | ||
| 145 | static const char *test_should_keep_alive(void) { | |
| 146 | struct mg_connection conn; | |
| 147 | char req1[] = "GET / HTTP/1.1\r\n\r\n"; | |
| 148 | char req2[] = "GET / HTTP/1.0\r\n\r\n"; | |
| 149 | char req3[] = "GET / HTTP/1.1\r\nConnection: close\r\n\r\n"; | |
| 150 | char req4[] = "GET / HTTP/1.1\r\nConnection: keep-alive\r\n\r\n"; | |
| 151 | ||
| 152 | memset(&conn, 0, sizeof(conn)); | |
| 153 | ASSERT(parse_http_message(req1, sizeof(req1) - 1, &conn) == sizeof(req1) - 1); | |
| 154 | ASSERT(should_keep_alive(&conn) != 0); | |
| 155 | ||
| 156 | parse_http_message(req2, sizeof(req2) - 1, &conn); | |
| 157 | ASSERT(should_keep_alive(&conn) == 0); | |
| 158 | ||
| 159 | parse_http_message(req3, sizeof(req3) - 1, &conn); | |
| 160 | ASSERT(should_keep_alive(&conn) == 0); | |
| 161 | ||
| 162 | parse_http_message(req4, sizeof(req4) - 1, &conn); | |
| 163 | ASSERT(should_keep_alive(&conn) != 0); | |
| 164 | ||
| 165 | return NULL; | |
| 166 | } | |
| 167 | ||
| 168 | static const char *test_match_prefix(void) { | |
| 169 | ASSERT(mg_match_prefix("/api", 4, "/api") == 4); | |
| 170 | ASSERT(mg_match_prefix("/a/", 3, "/a/b/c") == 3); | |
| 171 | ASSERT(mg_match_prefix("/a/", 3, "/ab/c") == -1); | |
| 172 | ASSERT(mg_match_prefix("/blog/", 6, "/") == -1); | |
| 173 | ASSERT(mg_match_prefix("/*/", 3, "/ab/c") == 4); | |
| 174 | ASSERT(mg_match_prefix("**", 2, "/a/b/c") == 6); | |
| 175 | ASSERT(mg_match_prefix("/*", 2, "/a/b/c") == 2); | |
| 176 | ASSERT(mg_match_prefix("*/*", 3, "/a/b/c") == 2); | |
| 177 | ASSERT(mg_match_prefix("**/", 3, "/a/b/c") == 5); | |
| 178 | ASSERT(mg_match_prefix("**.foo|**.bar", 13, "a.bar") == 5); | |
| 179 | ASSERT(mg_match_prefix("a|b|cd", 6, "cdef") == 2); | |
| 180 | ASSERT(mg_match_prefix("a|b|c?", 6, "cdef") == 2); | |
| 181 | ASSERT(mg_match_prefix("a|?|cd", 6, "cdef") == 1); | |
| 182 | ASSERT(mg_match_prefix("/a/**.cgi", 9, "/foo/bar/x.cgi") == -1); | |
| 183 | ASSERT(mg_match_prefix("/a/**.cgi", 9, "/a/bar/x.cgi") == 12); | |
| 184 | ASSERT(mg_match_prefix("**/", 3, "/a/b/c") == 5); | |
| 185 | ASSERT(mg_match_prefix("**/$", 4, "/a/b/c") == -1); | |
| 186 | ASSERT(mg_match_prefix("**/$", 4, "/a/b/") == 5); | |
| 187 | ASSERT(mg_match_prefix("$", 1, "") == 0); | |
| 188 | ASSERT(mg_match_prefix("$", 1, "x") == -1); | |
| 189 | ASSERT(mg_match_prefix("*$", 2, "x") == 1); | |
| 190 | ASSERT(mg_match_prefix("/$", 2, "/") == 1); | |
| 191 | ASSERT(mg_match_prefix("**/$", 4, "/a/b/c") == -1); | |
| 192 | ASSERT(mg_match_prefix("**/$", 4, "/a/b/") == 5); | |
| 193 | ASSERT(mg_match_prefix("*", 1, "/hello/") == 0); | |
| 194 | ASSERT(mg_match_prefix("**.a$|**.b$", 11, "/a/b.b/") == -1); | |
| 195 | ASSERT(mg_match_prefix("**.a$|**.b$", 11, "/a/b.b") == 6); | |
| 196 | ASSERT(mg_match_prefix("**.a$|**.b$", 11, "/a/B.A") == 6); | |
| 197 | ASSERT(mg_match_prefix("**o$", 4, "HELLO") == 5); | |
| 198 | return NULL; | |
| 199 | } | |
| 200 | ||
| 201 | static const char *test_remove_double_dots() { | |
| 202 | struct { char before[30], after[30]; } data[] = { | |
| 203 | {"////a", "/a"}, | |
| 204 | {"/.....", "/....."}, | |
| 205 | {"/......", "/......"}, | |
| 206 | {"...", "..."}, | |
| 207 | {"/...///", "/.../"}, | |
| 208 | {"/a...///", "/a.../"}, | |
| 209 | {"/.x", "/.x"}, | |
| 210 | {"/\\", "/"}, | |
| 211 | {"/a\\", "/a\\"}, | |
| 212 | {"/a\\\\...", "/a\\..."}, | |
| 213 | {"foo/x..y/././y/../../..", "foo/x..y/y/"}, | |
| 214 | {"foo/..x", "foo/..x"}, | |
| 215 | }; | |
| 216 | size_t i; | |
| 217 | ||
| 218 | for (i = 0; i < ARRAY_SIZE(data); i++) { | |
| 219 | remove_double_dots_and_double_slashes(data[i].before); | |
| 220 | ASSERT(strcmp(data[i].before, data[i].after) == 0); | |
| 221 | } | |
| 222 | ||
| 223 | return NULL; | |
| 224 | } | |
| 225 | ||
| 226 | static const char *test_get_var(void) { | |
| 227 | static const char *data = "a=1&&b=2&d&=&c=3%20&e=&k=aa&a=23"; | |
| 228 | static const char *data2 = "q=&st=2012%2F11%2F13+17%3A05&et=&team_id="; | |
| 229 | char buf[20]; | |
| 230 | ||
| 231 | ASSERT(get_var(data, strlen(data), "a", buf, sizeof(buf), 0) == 1); | |
| 232 | ASSERT(buf[0] == '1' && buf[1] == '\0'); | |
| 233 | ASSERT(get_var(data, strlen(data), "a", buf, sizeof(buf), 1) == 2); | |
| 234 | ASSERT(strcmp(buf, "23") == 0); | |
| 235 | ASSERT(get_var(data, strlen(data), "b", buf, sizeof(buf), 0) == 1); | |
| 236 | ASSERT(buf[0] == '2' && buf[1] == '\0'); | |
| 237 | ASSERT(get_var(data, strlen(data), "c", buf, sizeof(buf), 0) == 2); | |
| 238 | ASSERT(buf[0] == '3' && buf[1] == ' ' && buf[2] == '\0'); | |
| 239 | ASSERT(get_var(data, strlen(data), "e", buf, sizeof(buf), 0) == 0); | |
| 240 | ASSERT(buf[0] == '\0'); | |
| 241 | ||
| 242 | ASSERT(get_var(data, strlen(data), "d", buf, sizeof(buf), 0) == -1); | |
| 243 | ASSERT(get_var(data, strlen(data), "c", buf, 2, 0) == -2); | |
| 244 | ||
| 245 | ASSERT(get_var(data, strlen(data), "x", NULL, 10, 0) == -2); | |
| 246 | ASSERT(get_var(data, strlen(data), "x", buf, 0, 0) == -2); | |
| 247 | ASSERT(get_var(data2, strlen(data2), "st", buf, 16, 0) == -2); | |
| 248 | ASSERT(get_var(data2, strlen(data2), "st", buf, 17, 0) == 16); | |
| 249 | return NULL; | |
| 250 | } | |
| 251 | ||
| 252 | static const char *test_url_decode(void) { | |
| 253 | char buf[100]; | |
| 254 | ||
| 255 | ASSERT(mg_url_decode("foo", 3, buf, 3, 0) == -1); // No space for \0 | |
| 256 | ASSERT(mg_url_decode("foo", 3, buf, 4, 0) == 3); | |
| 257 | ASSERT(strcmp(buf, "foo") == 0); | |
| 258 | ||
| 259 | ASSERT(mg_url_decode("a+", 2, buf, sizeof(buf), 0) == 2); | |
| 260 | ASSERT(strcmp(buf, "a+") == 0); | |
| 261 | ||
| 262 | ASSERT(mg_url_decode("a+", 2, buf, sizeof(buf), 1) == 2); | |
| 263 | ASSERT(strcmp(buf, "a ") == 0); | |
| 264 | ||
| 265 | ASSERT(mg_url_decode("%61", 1, buf, sizeof(buf), 1) == 1); | |
| 266 | printf("[%s]\n", buf); | |
| 267 | ASSERT(strcmp(buf, "%") == 0); | |
| 268 | ||
| 269 | ASSERT(mg_url_decode("%61", 2, buf, sizeof(buf), 1) == 2); | |
| 270 | ASSERT(strcmp(buf, "%6") == 0); | |
| 271 | ||
| 272 | ASSERT(mg_url_decode("%61", 3, buf, sizeof(buf), 1) == 1); | |
| 273 | ASSERT(strcmp(buf, "a") == 0); | |
| 274 | return NULL; | |
| 275 | } | |
| 276 | ||
| 277 | static const char *test_url_encode(void) { | |
| 278 | char buf[100]; | |
| 279 | ASSERT(mg_url_encode("", 0, buf, sizeof(buf)) == 0); | |
| 280 | ASSERT(buf[0] == '\0'); | |
| 281 | ASSERT(mg_url_encode("foo", 3, buf, sizeof(buf)) == 3); | |
| 282 | ASSERT(strcmp(buf, "foo") == 0); | |
| 283 | ASSERT(mg_url_encode("f o", 3, buf, sizeof(buf)) == 5); | |
| 284 | ASSERT(strcmp(buf, "f%20o") == 0); | |
| 285 | return NULL; | |
| 286 | } | |
| 287 | ||
| 288 | static const char *test_to64(void) { | |
| 289 | ASSERT(to64("0") == 0); | |
| 290 | ASSERT(to64("") == 0); | |
| 291 | ASSERT(to64("123") == 123); | |
| 292 | ASSERT(to64("-34") == -34); | |
| 293 | ASSERT(to64("3566626116") == 3566626116); | |
| 294 | return NULL; | |
| 295 | } | |
| 296 | ||
| 297 | static const char *test_base64_encode(void) { | |
| 298 | const char *in[] = {"a", "ab", "abc", "abcd", NULL}; | |
| 299 | const char *out[] = {"YQ==", "YWI=", "YWJj", "YWJjZA=="}; | |
| 300 | char buf[100]; | |
| 301 | int i; | |
| 302 | ||
| 303 | for (i = 0; in[i] != NULL; i++) { | |
| 304 | base64_encode((unsigned char *) in[i], strlen(in[i]), buf); | |
| 305 | ASSERT(!strcmp(buf, out[i])); | |
| 306 | } | |
| 307 | ||
| 308 | return NULL; | |
| 309 | } | |
| 310 | ||
| 311 | static const char *test_mg_parse_header(void) { | |
| 312 | const char *str = "xx=1 kl yy, ert=234 kl=123, uri=\"/?name=x,y\", " | |
| 313 | "ii=\"12\\\"34\" zz='aa bb',tt=2,gf=\"xx d=1234"; | |
| 314 | char buf[20]; | |
| 315 | ASSERT(mg_parse_header(str, "yy", buf, sizeof(buf)) == 0); | |
| 316 | ASSERT(mg_parse_header(str, "ert", buf, sizeof(buf)) == 3); | |
| 317 | ASSERT(strcmp(buf, "234") == 0); | |
| 318 | ASSERT(mg_parse_header(str, "ert", buf, 2) == 0); | |
| 319 | ASSERT(mg_parse_header(str, "ert", buf, 3) == 0); | |
| 320 | ASSERT(mg_parse_header(str, "ert", buf, 4) == 3); | |
| 321 | ASSERT(mg_parse_header(str, "gf", buf, sizeof(buf)) == 0); | |
| 322 | ASSERT(mg_parse_header(str, "zz", buf, sizeof(buf)) == 5); | |
| 323 | ASSERT(strcmp(buf, "aa bb") == 0); | |
| 324 | ASSERT(mg_parse_header(str, "d", buf, sizeof(buf)) == 4); | |
| 325 | ASSERT(strcmp(buf, "1234") == 0); | |
| 326 | buf[0] = 'x'; | |
| 327 | ASSERT(mg_parse_header(str, "MMM", buf, sizeof(buf)) == 0); | |
| 328 | ASSERT(buf[0] == '\0'); | |
| 329 | ASSERT(mg_parse_header(str, "kl", buf, sizeof(buf)) == 3); | |
| 330 | ASSERT(strcmp(buf, "123") == 0); | |
| 331 | ASSERT(mg_parse_header(str, "xx", buf, sizeof(buf)) == 1); | |
| 332 | ASSERT(strcmp(buf, "1") == 0); | |
| 333 | ASSERT(mg_parse_header(str, "ii", buf, sizeof(buf)) == 5); | |
| 334 | ASSERT(strcmp(buf, "12\"34") == 0); | |
| 335 | ASSERT(mg_parse_header(str, "tt", buf, sizeof(buf)) == 1); | |
| 336 | ASSERT(strcmp(buf, "2") == 0); | |
| 337 | ASSERT(mg_parse_header(str, "uri", buf, sizeof(buf)) == 10); | |
| 338 | return NULL; | |
| 339 | } | |
| 340 | ||
| 341 | static const char *test_next_option(void) { | |
| 342 | const char *p, *list = "x/8,/y**=1;2k,z"; | |
| 343 | struct vec a, b; | |
| 344 | int i; | |
| 345 | ||
| 346 | ASSERT(next_option(NULL, &a, &b) == NULL); | |
| 347 | for (i = 0, p = list; (p = next_option(p, &a, &b)) != NULL; i++) { | |
| 348 | ASSERT(i != 0 || (a.ptr == list && a.len == 3 && b.len == 0)); | |
| 349 | ASSERT(i != 1 || (a.ptr == list + 4 && a.len == 4 && b.ptr == list + 9 && | |
| 350 | b.len == 4)); | |
| 351 | ||
| 352 | ASSERT(i != 2 || (a.ptr == list + 14 && a.len == 1 && b.len == 0)); | |
| 353 | } | |
| 354 | return NULL; | |
| 355 | } | |
| 356 | ||
| 357 | static int evh1(struct mg_connection *conn, enum mg_event ev) { | |
| 358 | char *buf = (char *) conn->connection_param; | |
| 359 | int result = MG_FALSE; | |
| 360 | ||
| 361 | switch (ev) { | |
| 362 | case MG_CONNECT: | |
| 363 | mg_printf(conn, "GET %s HTTP/1.0\r\n\r\n", | |
| 364 | buf[0] == '1' ? "/cb1" : "/non_exist"); | |
| 365 | result = MG_TRUE; | |
| 366 | break; | |
| 367 | case MG_HTTP_ERROR: | |
| 368 | mg_printf(conn, "HTTP/1.0 404 NF\r\n\r\nERR: %d", conn->status_code); | |
| 369 | result = MG_TRUE; | |
| 370 | break; | |
| 371 | case MG_REQUEST: | |
| 372 | if (!strcmp(conn->uri, "/cb1")) { | |
| 373 | mg_printf(conn, "HTTP/1.0 200 OK\r\n\r\n%s %s %s", | |
| 374 | (char *) conn->server_param, | |
| 375 | buf == NULL ? "?" : "!", conn->remote_ip); | |
| 376 | result = MG_TRUE; | |
| 377 | } | |
| 378 | break; | |
| 379 | case MG_REPLY: | |
| 380 | if (buf != NULL) { | |
| 381 | sprintf(buf + 1, "%.*s", (int) conn->content_len, conn->content); | |
| 382 | } | |
| 383 | break; | |
| 384 | case MG_AUTH: | |
| 385 | result = MG_TRUE; | |
| 386 | break; | |
| 387 | default: | |
| 388 | break; | |
| 389 | } | |
| 390 | ||
| 391 | return result; | |
| 392 | } | |
| 393 | ||
| 394 | static const char *test_server(void) { | |
| 395 | char buf1[100] = "1", buf2[100] = "2"; | |
| 396 | struct mg_server *server = mg_create_server((void *) "foo", evh1); | |
| 397 | struct mg_connection *conn; | |
| 398 | ||
| 399 | ASSERT(server != NULL); | |
| 400 | ASSERT(mg_set_option(server, "listening_port", LISTENING_ADDR) == NULL); | |
| 401 | ASSERT(mg_set_option(server, "document_root", ".") == NULL); | |
| 402 | ||
| 403 | ASSERT((conn = mg_connect(server, "127.0.0.1:" HTTP_PORT)) != NULL); | |
| 404 | conn->connection_param = buf1; | |
| 405 | ASSERT((conn = mg_connect(server, "127.0.0.1:" HTTP_PORT)) != NULL); | |
| 406 | conn->connection_param = buf2; | |
| 407 | ||
| 408 | { int i; for (i = 0; i < 50; i++) mg_poll_server(server, 1); } | |
| 409 | ASSERT(strcmp(buf1, "1foo ? 127.0.0.1") == 0); | |
| 410 | ASSERT(strcmp(buf2, "2ERR: 404") == 0); | |
| 411 | ||
| 412 | ASSERT(strcmp(static_config_options[URL_REWRITES * 2], "url_rewrites") == 0); | |
| 413 | mg_destroy_server(&server); | |
| 414 | ASSERT(server == NULL); | |
| 415 | return NULL; | |
| 416 | } | |
| 417 | ||
| 418 | #define DISP "Content-Disposition: form/data; " | |
| 419 | #define CRLF "\r\n" | |
| 420 | #define BOUNDARY "--xyz" | |
| 421 | static const char *test_parse_multipart(void) { | |
| 422 | char a[100], b[100]; | |
| 423 | const char *p; | |
| 424 | static const char f1[] = BOUNDARY CRLF DISP "name=f1" CRLF CRLF | |
| 425 | "some_content" CRLF BOUNDARY CRLF | |
| 426 | BOUNDARY CRLF DISP "name=f2; filename=\"foo bar.txt\"" CRLF CRLF | |
| 427 | "another_content" CRLF BOUNDARY CRLF | |
| 428 | "--" CRLF; | |
| 429 | int n, n2, len, f1_len = sizeof(f1) - 1; | |
| 430 | ||
| 431 | ASSERT(mg_parse_multipart("", 0, a, sizeof(a), b, sizeof(b), &p, &len) == 0); | |
| 432 | ASSERT(mg_parse_multipart("a", 1, a, sizeof(a), b, sizeof(b), &p, &len) == 0); | |
| 433 | ASSERT((n = mg_parse_multipart(f1, f1_len, a, sizeof(a), | |
| 434 | b, sizeof(b), &p, &len)) > 0); | |
| 435 | ASSERT(len == 12); | |
| 436 | ASSERT(memcmp(p, "some_content", len) == 0); | |
| 437 | ASSERT(strcmp(a, "f1") == 0); | |
| 438 | ASSERT(b[0] == '\0'); | |
| 439 | ||
| 440 | ASSERT((n2 = mg_parse_multipart(f1 + n, f1_len - n, a, sizeof(a), | |
| 441 | b, sizeof(b), &p, &len)) > 0); | |
| 442 | ASSERT(len == 15); | |
| 443 | ASSERT(memcmp(p, "another_content", len) == 0); | |
| 444 | ASSERT(strcmp(a, "f2") == 0); | |
| 445 | ASSERT(strcmp(b, "foo bar.txt") == 0); | |
| 446 | ||
| 447 | ASSERT((n2 = mg_parse_multipart(f1 + n + n2, f1_len - (n + n2), a, sizeof(a), | |
| 448 | b, sizeof(b), &p, &len)) == 0); | |
| 449 | ||
| 450 | return NULL; | |
| 451 | } | |
| 452 | ||
| 453 | static int evh2(struct mg_connection *conn, enum mg_event ev) { | |
| 454 | char *file_data, *cp = (char *) conn->connection_param; | |
| 455 | int file_size, result = MG_FALSE; | |
| 456 | ||
| 457 | switch (ev) { | |
| 458 | case MG_AUTH: | |
| 459 | result = MG_TRUE; | |
| 460 | break; | |
| 461 | case MG_CONNECT: | |
| 462 | mg_printf(conn, "GET /%s HTTP/1.0\r\n\r\n", cp); | |
| 463 | result = MG_TRUE; | |
| 464 | break; | |
| 465 | case MG_REQUEST: | |
| 466 | break; | |
| 467 | case MG_REPLY: | |
| 468 | file_data = read_file("unit_test.c", &file_size); | |
| 469 | sprintf(cp, "%d %s", (size_t) file_size == conn->content_len && | |
| 470 | memcmp(file_data, conn->content, file_size) == 0 ? 1 : 0, | |
| 471 | conn->query_string == NULL ? "?" : conn->query_string); | |
| 472 | free(file_data); | |
| 473 | break; | |
| 474 | default: | |
| 475 | break; | |
| 476 | } | |
| 477 | ||
| 478 | return result; | |
| 479 | } | |
| 480 | ||
| 481 | static const char *test_mg_set_option(void) { | |
| 482 | struct mg_server *server = mg_create_server(NULL, NULL); | |
| 483 | ASSERT(mg_set_option(server, "listening_port", "0") == NULL); | |
| 484 | ASSERT(mg_get_option(server, "listening_port")[0] != '\0'); | |
| 485 | mg_destroy_server(&server); | |
| 486 | return NULL; | |
| 487 | } | |
| 488 | ||
| 489 | static const char *test_rewrites(void) { | |
| 490 | char buf1[100] = "xx", addr[50]; | |
| 491 | struct mg_server *server = mg_create_server(NULL, evh2); | |
| 492 | struct mg_connection *conn; | |
| 493 | const char *port; | |
| 494 | ||
| 495 | ASSERT(mg_set_option(server, "listening_port", "0") == NULL); | |
| 496 | ASSERT(mg_set_option(server, "document_root", ".") == NULL); | |
| 497 | ASSERT(mg_set_option(server, "url_rewrites", "/xx=unit_test.c") == NULL); | |
| 498 | ASSERT((port = mg_get_option(server, "listening_port")) != NULL); | |
| 499 | snprintf(addr, sizeof(addr), "127.0.0.1:%s", port); | |
| 500 | ASSERT((conn = mg_connect(server, addr)) != NULL); | |
| 501 | conn->connection_param = buf1; | |
| 502 | ||
| 503 | { int i; for (i = 0; i < 50; i++) mg_poll_server(server, 1); } | |
| 504 | ||
| 505 | ASSERT(strcmp(buf1, "1 ?") == 0); | |
| 506 | mg_destroy_server(&server); | |
| 507 | return NULL; | |
| 508 | } | |
| 509 | ||
| 510 | static const char *run_all_tests(void) { | |
| 511 | RUN_TEST(test_should_keep_alive); | |
| 512 | RUN_TEST(test_match_prefix); | |
| 513 | RUN_TEST(test_remove_double_dots); | |
| 514 | RUN_TEST(test_parse_http_message); | |
| 515 | RUN_TEST(test_to64); | |
| 516 | RUN_TEST(test_url_decode); | |
| 517 | RUN_TEST(test_url_encode); | |
| 518 | RUN_TEST(test_base64_encode); | |
| 519 | RUN_TEST(test_mg_parse_header); | |
| 520 | RUN_TEST(test_get_var); | |
| 521 | RUN_TEST(test_next_option); | |
| 522 | RUN_TEST(test_parse_multipart); | |
| 523 | RUN_TEST(test_mg_set_option); | |
| 524 | RUN_TEST(test_server); | |
| 525 | RUN_TEST(test_rewrites); | |
| 526 | return NULL; | |
| 527 | } | |
| 528 | ||
| 529 | int __cdecl main(void) { | |
| 530 | const char *fail_msg = run_all_tests(); | |
| 531 | printf("%s, tests run: %d\n", fail_msg ? "FAIL" : "PASS", static_num_tests); | |
| 532 | return fail_msg == NULL ? EXIT_SUCCESS : EXIT_FAILURE; | |
| 533 | } |
| r250109 | r250110 | |
|---|---|---|
| 461 | 461 | } |
| 462 | 462 | |
| 463 | 463 | -------------------------------------------------- |
| 464 | -- mongoose library objects | |
| 465 | -------------------------------------------------- | |
| 466 | ||
| 467 | project "mongoose" | |
| 468 | uuid "ff05b529-2b6f-4166-9dff-5fe2aef89c40" | |
| 469 | kind "StaticLib" | |
| 470 | ||
| 471 | configuration { "vs*" } | |
| 472 | buildoptions { | |
| 473 | "/wd4996", -- warning C4996: 'function': was declared deprecated | |
| 474 | "/wd4100", -- warning C4100: 'xxx' : unreferenced formal parameter | |
| 475 | "/wd4245", -- warning C4245: 'conversion' : conversion from 'type1' to 'type2', signed/unsigned mismatch | |
| 476 | "/wd4267", -- warning C4267: 'var' : conversion from 'size_t' to 'type', possible loss of data | |
| 477 | "/wd4244", -- warning C4244: 'argument' : conversion from 'xxx' to 'xxx', possible loss of data | |
| 478 | } | |
| 479 | ||
| 480 | configuration { "vs2015" } | |
| 481 | buildoptions { | |
| 482 | "/wd4456", -- warning C4456: declaration of 'xxx' hides previous local declaration | |
| 483 | } | |
| 484 | ||
| 485 | configuration { } | |
| 486 | ||
| 487 | options { | |
| 488 | "ForceCPP", | |
| 489 | } | |
| 490 | defines { | |
| 491 | "MONGOOSE_ENABLE_THREADS", | |
| 492 | "NS_STACK_SIZE=0" | |
| 493 | } | |
| 494 | ||
| 495 | includedirs { | |
| 496 | MAME_DIR .. "3rdparty/mongoose", | |
| 497 | } | |
| 498 | ||
| 499 | files { | |
| 500 | MAME_DIR .. "3rdparty/mongoose/mongoose.c", | |
| 501 | } | |
| 502 | ||
| 503 | -------------------------------------------------- | |
| 464 | 504 | -- jsoncpp library objects |
| 465 | 505 | -------------------------------------------------- |
| 466 | 506 |
| r250109 | r250110 | |
|---|---|---|
| 245 | 245 | MAME_DIR .. "src/emu/debug/textbuf.h", |
| 246 | 246 | MAME_DIR .. "src/emu/profiler.c", |
| 247 | 247 | MAME_DIR .. "src/emu/profiler.h", |
| 248 | MAME_DIR .. "src/emu/webengine.c", | |
| 249 | MAME_DIR .. "src/emu/webengine.h", | |
| 248 | 250 | MAME_DIR .. "src/emu/sound/filter.c", |
| 249 | 251 | MAME_DIR .. "src/emu/sound/filter.h", |
| 250 | 252 | MAME_DIR .. "src/devices/sound/flt_vol.c", |
| r250109 | r250110 | |
|---|---|---|
| 116 | 116 | "lua", |
| 117 | 117 | "lsqlite3", |
| 118 | 118 | "jsoncpp", |
| 119 | "mongoose", | |
| 119 | 120 | } |
| 120 | 121 | |
| 121 | 122 | if _OPTIONS["with-bundled-zlib"] then |
| r250109 | r250110 | |
|---|---|---|
| 20 | 20 | PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("B") |
| 21 | 21 | PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Y") |
| 22 | 22 | PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_NAME("Select") |
| 23 | PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_START | |
| 23 | PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_START ) PORT_NAME("Start") | |
| 24 | 24 | PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) |
| 25 | 25 | PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) |
| 26 | 26 | PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) |
| r250109 | r250110 | |
|---|---|---|
| 956 | 956 | |
| 957 | 957 | if (!FREEZE_MODE) |
| 958 | 958 | { |
| 959 | m_chc = ((m_exec_ir << 8) & 0xff) | CHC_LS | RA << CHC_TR_SHIFT | CHC_CV; | |
| 959 | m_chc = ((m_exec_ir << 8) & 0xff) | | |
| 960 | CHC_LS | | |
| 961 | RA << CHC_TR_SHIFT | | |
| 962 | CHC_CV; | |
| 963 | ||
| 960 | 964 | m_cha = addr; |
| 961 | 965 | m_chd = r; |
| 962 | 966 | |
| 963 | if (!(m_cfg & CFG_DW) && INST_SB_BIT) | |
| 964 | { | |
| 965 | SET_ALU_BP(addr); | |
| 966 | } | |
| 967 | if (!(m_cfg & CFG_DW) && (m_exec_ir & INST_SB_BIT)) | |
| 968 | SET_ALU_BP(addr & 3); | |
| 967 | 969 | } |
| 968 | 970 | |
| 969 | 971 | m_r[RA] = r; |
| 970 | 972 | |
| 971 | 973 | if (m_cfg & CFG_DW) |
| 972 | { | |
| 973 | 974 | logerror("DW ON A STORE"); |
| 974 | } | |
| 975 | 975 | } |
| 976 | 976 | |
| 977 | 977 | void am29000_cpu_device::LOADL() |
| r250109 | r250110 | |
| 987 | 987 | void am29000_cpu_device::LOADM() |
| 988 | 988 | { |
| 989 | 989 | UINT32 addr = INST_M_BIT ? I8: GET_RB_VAL; |
| 990 | UINT32 r; | |
| 990 | 991 | |
| 991 | 992 | if (INST_UA_BIT) |
| 992 | { | |
| 993 | 993 | fatalerror("Am29000: UA bit set on LOAD\n"); |
| 994 | } | |
| 995 | 994 | |
| 996 | UINT32 r; | |
| 997 | 995 | if (INST_CE_BIT) |
| 998 | 996 | { |
| 999 | 997 | logerror("Am29000: Attempting a co-processor LOAD!\n"); |
| r250109 | r250110 | |
| 1021 | 1019 | { |
| 1022 | 1020 | // TODO |
| 1023 | 1021 | m_chc &= (CHC_CR_MASK << CHC_CR_SHIFT); |
| 1024 | m_chc |= ((m_exec_ir << 8) & 0xff) | RA << CHC_TR_SHIFT | CHC_CV; | |
| 1022 | m_chc |= ((m_exec_ir << 8) & 0xff) | | |
| 1023 | RA << CHC_TR_SHIFT | | |
| 1024 | CHC_CV; | |
| 1025 | ||
| 1025 | 1026 | m_cha = addr; |
| 1026 | 1027 | m_chd = r; // ????? |
| 1027 | 1028 | |
| 1028 | if (!(m_cfg & CFG_DW) && INST_SB_BIT) | |
| 1029 | { | |
| 1030 | SET_ALU_BP(addr); | |
| 1031 | } | |
| 1029 | if (!(m_cfg & CFG_DW) && (m_exec_ir & INST_SB_BIT)) | |
| 1030 | SET_ALU_BP(addr & 3); | |
| 1032 | 1031 | } |
| 1033 | 1032 | |
| 1034 | 1033 | r = RA; |
| 1035 | 1034 | |
| 1036 | for (INT32 cnt = 0; cnt <= GET_CHC_CR; ++cnt) | |
| 1037 | 1035 | { |
| 1038 | m_r[r] = m_data->read_dword(addr); | |
| 1036 | int cnt; | |
| 1037 | for (cnt = 0; cnt <= GET_CHC_CR; ++cnt) | |
| 1038 | { | |
| 1039 | m_r[r] = m_data->read_dword(addr); | |
| 1039 | 1040 | |
| 1040 | 1041 | // SET_CHC_CR(cnt - 1); |
| 1041 | addr += 4; | |
| 1042 | addr += 4; | |
| 1042 | 1043 | |
| 1043 | if (++r == 256) | |
| 1044 | { | |
| 1045 | r = 128; | |
| 1044 | if (++r == 256) | |
| 1045 | r = 128; | |
| 1046 | 1046 | } |
| 1047 | 1047 | } |
| 1048 | 1048 | } |
| r250109 | r250110 | |
| 1050 | 1050 | void am29000_cpu_device::STORE() |
| 1051 | 1051 | { |
| 1052 | 1052 | UINT32 addr = INST_M_BIT ? I8: GET_RB_VAL; |
| 1053 | // UINT32 r; | |
| 1053 | 1054 | |
| 1054 | 1055 | if (INST_UA_BIT) |
| 1055 | { | |
| 1056 | 1056 | fatalerror("Am29000: UA bit set on LOAD\n"); |
| 1057 | } | |
| 1058 | 1057 | |
| 1059 | 1058 | if (INST_CE_BIT) |
| 1060 | 1059 | { |
| 1061 | 1060 | logerror("Am29000: Attempting a co-processor LOAD!\n"); |
| 1061 | // r = 0; | |
| 1062 | 1062 | } |
| 1063 | 1063 | else |
| 1064 | 1064 | { |
| r250109 | r250110 | |
| 1073 | 1073 | SIGNAL_EXCEPTION(EXCEPTION_PROTECTION_VIOLATION); |
| 1074 | 1074 | return; |
| 1075 | 1075 | } |
| 1076 | ||
| 1076 | 1077 | } |
| 1077 | 1078 | } |
| 1078 | 1079 | |
| r250109 | r250110 | |
| 1080 | 1081 | |
| 1081 | 1082 | if (!FREEZE_MODE) |
| 1082 | 1083 | { |
| 1083 | m_chc = ((m_exec_ir << 8) & 0xff) | RA << CHC_TR_SHIFT | CHC_CV; | |
| 1084 | m_chc = ((m_exec_ir << 8) & 0xff) | | |
| 1085 | RA << CHC_TR_SHIFT | | |
| 1086 | CHC_CV; | |
| 1087 | ||
| 1084 | 1088 | m_cha = addr; |
| 1085 | 1089 | |
| 1086 | if (!(m_cfg & CFG_DW) && INST_SB_BIT) | |
| 1087 | { | |
| 1088 | SET_ALU_BP(addr); | |
| 1089 | } | |
| 1090 | if (!(m_cfg & CFG_DW) && (m_exec_ir & INST_SB_BIT)) | |
| 1091 | SET_ALU_BP(addr & 3); | |
| 1090 | 1092 | } |
| 1091 | 1093 | |
| 1092 | 1094 | if (m_cfg & CFG_DW) |
| 1093 | { | |
| 1094 | 1095 | logerror("DW ON A STORE"); |
| 1095 | } | |
| 1096 | 1096 | } |
| 1097 | 1097 | |
| 1098 | 1098 | void am29000_cpu_device::STOREL() |
| r250109 | r250110 | |
| 1103 | 1103 | void am29000_cpu_device::STOREM() |
| 1104 | 1104 | { |
| 1105 | 1105 | UINT32 addr = INST_M_BIT ? I8: GET_RB_VAL; |
| 1106 | UINT32 r; | |
| 1106 | 1107 | |
| 1107 | 1108 | if (INST_UA_BIT) |
| 1108 | { | |
| 1109 | 1109 | fatalerror("Am29000: UA bit set on LOAD\n"); |
| 1110 | } | |
| 1111 | 1110 | |
| 1112 | UINT32 r; | |
| 1113 | 1111 | if (INST_CE_BIT) |
| 1114 | 1112 | { |
| 1115 | 1113 | logerror("Am29000: Attempting a co-processor LOAD!\n"); |
| r250109 | r250110 | |
| 1128 | 1126 | SIGNAL_EXCEPTION(EXCEPTION_PROTECTION_VIOLATION); |
| 1129 | 1127 | return; |
| 1130 | 1128 | } |
| 1129 | ||
| 1131 | 1130 | } |
| 1132 | 1131 | } |
| 1133 | 1132 | |
| r250109 | r250110 | |
| 1135 | 1134 | { |
| 1136 | 1135 | // TODO |
| 1137 | 1136 | m_chc &= (CHC_CR_MASK << CHC_CR_SHIFT); |
| 1138 | m_chc |= ((m_exec_ir << 8) & 0xff) | RA << CHC_TR_SHIFT | CHC_CV; | |
| 1137 | m_chc |= ((m_exec_ir << 8) & 0xff) | | |
| 1138 | RA << CHC_TR_SHIFT | | |
| 1139 | CHC_CV; | |
| 1140 | ||
| 1139 | 1141 | m_cha = addr; |
| 1140 | 1142 | |
| 1141 | if (!(m_cfg & CFG_DW) && INST_SB_BIT) | |
| 1142 | { | |
| 1143 | SET_ALU_BP(addr); | |
| 1144 | } | |
| 1143 | if (!(m_cfg & CFG_DW) && (m_exec_ir & INST_SB_BIT)) | |
| 1144 | SET_ALU_BP(addr & 3); | |
| 1145 | 1145 | } |
| 1146 | 1146 | |
| 1147 | 1147 | r = RA; |
| 1148 | 1148 | |
| 1149 | for (INT32 cnt = 0; cnt <= GET_CHC_CR; ++cnt) | |
| 1150 | 1149 | { |
| 1151 | m_data->write_dword(addr, m_r[r]); | |
| 1150 | int cnt; | |
| 1151 | for (cnt = 0; cnt <= GET_CHC_CR; ++cnt) | |
| 1152 | { | |
| 1153 | m_data->write_dword(addr, m_r[r]); | |
| 1152 | 1154 | |
| 1153 | addr += 4; | |
| 1155 | // SET_CHC_CR(cnt - 1); | |
| 1156 | addr += 4; | |
| 1154 | 1157 | |
| 1155 | if (++r == 256) | |
| 1156 | { | |
| 1157 | r = 128; | |
| 1158 | if (++r == 256) | |
| 1159 | r = 128; | |
| 1158 | 1160 | } |
| 1159 | 1161 | } |
| 1160 | 1162 | } |
| r250109 | r250110 | |
|---|---|---|
| 592 | 592 | switch(op & 0x0007) |
| 593 | 593 | { |
| 594 | 594 | case 0x0000: |
| 595 | output += sprintf( output, "BRLO %08x", (((op & 0x0200) ? (KCONST7(op) | 0xff80) : KCONST7(op)) << 1) ); | |
| 595 | output += sprintf( output, "BRLO %08x", (((op & 0x0200) ? ((KCONST7(op) & 0x007f) | 0xff80) : KCONST7(op)) << 1) ); | |
| 596 | 596 | break; |
| 597 | 597 | case 0x0001: |
| 598 | output += sprintf( output, "BREQ %08x", (((op & 0x0200) ? (KCONST7(op) | 0xff80) : KCONST7(op)) << 1) ); | |
| 598 | output += sprintf( output, "BREQ %08x", (((op & 0x0200) ? ((KCONST7(op) & 0x007f) | 0xff80) : KCONST7(op)) << 1) ); | |
| 599 | 599 | break; |
| 600 | 600 | case 0x0002: |
| 601 | output += sprintf( output, "BRMI %08x", (((op & 0x0200) ? (KCONST7(op) | 0xff80) : KCONST7(op)) << 1) ); | |
| 601 | output += sprintf( output, "BRMI %08x", (((op & 0x0200) ? ((KCONST7(op) & 0x007f) | 0xff80) : KCONST7(op)) << 1) ); | |
| 602 | 602 | break; |
| 603 | 603 | case 0x0003: |
| 604 | output += sprintf( output, "BRVS %08x", (((op & 0x0200) ? (KCONST7(op) | 0xff80) : KCONST7(op)) << 1) ); | |
| 604 | output += sprintf( output, "BRVS %08x", (((op & 0x0200) ? ((KCONST7(op) & 0x007f) | 0xff80) : KCONST7(op)) << 1) ); | |
| 605 | 605 | break; |
| 606 | 606 | case 0x0004: |
| 607 | output += sprintf( output, "BRLT %08x", (((op & 0x0200) ? (KCONST7(op) | 0xff80) : KCONST7(op)) << 1) ); | |
| 607 | output += sprintf( output, "BRLT %08x", (((op & 0x0200) ? ((KCONST7(op) & 0x007f) | 0xff80) : KCONST7(op)) << 1) ); | |
| 608 | 608 | break; |
| 609 | 609 | case 0x0005: |
| 610 | output += sprintf( output, "BRHS %08x", (((op & 0x0200) ? (KCONST7(op) | 0xff80) : KCONST7(op)) << 1) ); | |
| 610 | output += sprintf( output, "BRHS %08x", (((op & 0x0200) ? ((KCONST7(op) & 0x007f) | 0xff80) : KCONST7(op)) << 1) ); | |
| 611 | 611 | break; |
| 612 | 612 | case 0x0006: |
| 613 | output += sprintf( output, "BRTS %08x", (((op & 0x0200) ? (KCONST7(op) | 0xff80) : KCONST7(op)) << 1) ); | |
| 613 | output += sprintf( output, "BRTS %08x", (((op & 0x0200) ? ((KCONST7(op) & 0x007f) | 0xff80) : KCONST7(op)) << 1) ); | |
| 614 | 614 | break; |
| 615 | 615 | case 0x0007: |
| 616 | output += sprintf( output, "BRIE %08x", (((op & 0x0200) ? (KCONST7(op) | 0xff80) : KCONST7(op)) << 1) ); | |
| 616 | output += sprintf( output, "BRIE %08x", (((op & 0x0200) ? ((KCONST7(op) & 0x007f) | 0xff80) : KCONST7(op)) << 1) ); | |
| 617 | 617 | break; |
| 618 | 618 | } |
| 619 | 619 | break; |
| r250109 | r250110 | |
| 621 | 621 | switch(op & 0x0007) |
| 622 | 622 | { |
| 623 | 623 | case 0x0000: |
| 624 | output += sprintf( output, "BRSH %08x", (((op & 0x0200) ? (KCONST7(op) | 0xff80) : KCONST7(op)) << 1) ); | |
| 624 | output += sprintf( output, "BRSH %08x", (((op & 0x0200) ? ((KCONST7(op) & 0x007f) | 0xff80) : KCONST7(op)) << 1) ); | |
| 625 | 625 | break; |
| 626 | 626 | case 0x0001: |
| 627 | output += sprintf( output, "BRNE %08x", (((op & 0x0200) ? (KCONST7(op) | 0xff80) : KCONST7(op)) << 1) ); | |
| 627 | output += sprintf( output, "BRNE %08x", (((op & 0x0200) ? ((KCONST7(op) & 0x007f) | 0xff80) : KCONST7(op)) << 1) ); | |
| 628 | 628 | break; |
| 629 | 629 | case 0x0002: |
| 630 | output += sprintf( output, "BRPL %08x", (((op & 0x0200) ? (KCONST7(op) | 0xff80) : KCONST7(op)) << 1) ); | |
| 630 | output += sprintf( output, "BRPL %08x", (((op & 0x0200) ? ((KCONST7(op) & 0x007f) | 0xff80) : KCONST7(op)) << 1) ); | |
| 631 | 631 | break; |
| 632 | 632 | case 0x0003: |
| 633 | output += sprintf( output, "BRVC %08x", (((op & 0x0200) ? (KCONST7(op) | 0xff80) : KCONST7(op)) << 1) ); | |
| 633 | output += sprintf( output, "BRVC %08x", (((op & 0x0200) ? ((KCONST7(op) & 0x007f) | 0xff80) : KCONST7(op)) << 1) ); | |
| 634 | 634 | break; |
| 635 | 635 | case 0x0004: |
| 636 | output += sprintf( output, "BRGE %08x", (((op & 0x0200) ? (KCONST7(op) | 0xff80) : KCONST7(op)) << 1) ); | |
| 636 | output += sprintf( output, "BRGE %08x", (((op & 0x0200) ? ((KCONST7(op) & 0x007f) | 0xff80) : KCONST7(op)) << 1) ); | |
| 637 | 637 | break; |
| 638 | 638 | case 0x0005: |
| 639 | output += sprintf( output, "BRHC %08x", (((op & 0x0200) ? (KCONST7(op) | 0xff80) : KCONST7(op)) << 1) ); | |
| 639 | output += sprintf( output, "BRHC %08x", (((op & 0x0200) ? ((KCONST7(op) & 0x007f) | 0xff80) : KCONST7(op)) << 1) ); | |
| 640 | 640 | break; |
| 641 | 641 | case 0x0006: |
| 642 | output += sprintf( output, "BRTC %08x", (((op & 0x0200) ? (KCONST7(op) | 0xff80) : KCONST7(op)) << 1) ); | |
| 642 | output += sprintf( output, "BRTC %08x", (((op & 0x0200) ? ((KCONST7(op) & 0x007f) | 0xff80) : KCONST7(op)) << 1) ); | |
| 643 | 643 | break; |
| 644 | 644 | case 0x0007: |
| 645 | output += sprintf( output, "BRID %08x", (((op & 0x0200) ? (KCONST7(op) | 0xff80) : KCONST7(op)) << 1) ); | |
| 645 | output += sprintf( output, "BRID %08x", (((op & 0x0200) ? ((KCONST7(op) & 0x007f) | 0xff80) : KCONST7(op)) << 1) ); | |
| 646 | 646 | break; |
| 647 | 647 | } |
| 648 | 648 | break; |
| r250109 | r250110 | |
|---|---|---|
| 3141 | 3141 | |
| 3142 | 3142 | case 2: |
| 3143 | 3143 | |
| 3144 | load = READ_HW(EXTRA_S); | |
| 3144 | load = READ_HW(EXTRA_S & ~1); | |
| 3145 | 3145 | |
| 3146 | 3146 | if( EXTRA_S & 1 ) // LDHS.A |
| 3147 | 3147 | { |
| r250109 | r250110 | |
| 3417 | 3417 | |
| 3418 | 3418 | case 2: |
| 3419 | 3419 | |
| 3420 | WRITE_HW(EXTRA_S, SREG & 0xffff); | |
| 3420 | WRITE_HW(EXTRA_S & ~1, SREG & 0xffff); | |
| 3421 | 3421 | |
| 3422 | 3422 | /* |
| 3423 | 3423 | if( EXTRA_S & 1 ) // STHS.A |
| r250109 | r250110 | |
|---|---|---|
| 508 | 508 | case 0x15: //psr |
| 509 | 509 | { |
| 510 | 510 | UINT8 arg = read_op(); |
| 511 | WRITE_SREG(arg, READ_REG(arg)); | |
| 511 | WRITE_SREG(arg, READ_REG(arg)&0x1f); | |
| 512 | 512 | |
| 513 | 513 | check_optional_jr(arg); |
| 514 | 514 | m_icount -= 3; |
| r250109 | r250110 | |
| 1038 | 1038 | case 0x55: //psr |
| 1039 | 1039 | { |
| 1040 | 1040 | UINT8 arg = read_op(); |
| 1041 | WRITE_SREG(arg, arg); | |
| 1041 | WRITE_SREG(arg, arg&0x1f); | |
| 1042 | 1042 | |
| 1043 | 1043 | m_icount -= 3; |
| 1044 | 1044 | } |
| r250109 | r250110 | |
| 2893 | 2893 | } |
| 2894 | 2894 | else |
| 2895 | 2895 | { |
| 2896 | return READ_SREG(arg); | |
| 2896 | return READ_SREG(arg)&0x1f; | |
| 2897 | 2897 | } |
| 2898 | 2898 | } |
| 2899 | 2899 | |
| r250109 | r250110 | |
| 2905 | 2905 | } |
| 2906 | 2906 | else |
| 2907 | 2907 | { |
| 2908 | return READ_SREG(arg); | |
| 2908 | return READ_SREG(arg)&0x1f; | |
| 2909 | 2909 | } |
| 2910 | 2910 | } |
| 2911 | 2911 |
| r250109 | r250110 | |
|---|---|---|
| 224 | 224 | buffer += sprintf(buffer, "j%s! 0x%08x", GET_J_LK(opcode) ? "l": "", (pc & 0xfffff000) | (GET_J_DISP11(opcode) << 1)); |
| 225 | 225 | break; |
| 226 | 226 | case 0x04: |
| 227 | buffer += sprintf(buffer, "b%s! 0x%08x", m_cond[GET_BX_EC(opcode)], pc + sign_extend(GET_BX_DISP8(opcode) << 1, 9)); | |
| 227 | buffer += sprintf(buffer, "b%s! 0x%08x", m_cond[GET_BX_EC(opcode) & 0x0f], pc + sign_extend(GET_BX_DISP8(opcode) << 1, 9)); | |
| 228 | 228 | break; |
| 229 | 229 | case 0x05: |
| 230 | 230 | buffer += sprintf(buffer, "ldiu! r%d, 0x%02x", GET_I2_RD(opcode), GET_I2_IMM8(opcode)); |
| r250109 | r250110 | |
|---|---|---|
| 252 | 252 | { |
| 253 | 253 | m_icount -= 39; |
| 254 | 254 | UINT16 t = param1 * param2; |
| 255 | SET_CNZ(t >> 8); | |
| 255 | SET_CNZ(t >> 8 & 0xff); | |
| 256 | 256 | write_mem16(0, t); // always writes result to regs A-B |
| 257 | 257 | return WB_NO; |
| 258 | 258 | } |
| r250109 | r250110 | |
| 502 | 502 | m_icount -= 15; |
| 503 | 503 | UINT16 t = imm16(); |
| 504 | 504 | write_r16(imm8(), t); |
| 505 | SET_CNZ(t >> 8); | |
| 505 | SET_CNZ(t >> 8 & 0xff); | |
| 506 | 506 | } |
| 507 | 507 | |
| 508 | 508 | void tms7000_device::movd_inx() |
| r250109 | r250110 | |
| 510 | 510 | m_icount -= 17; |
| 511 | 511 | UINT16 t = imm16() + read_r8(1); |
| 512 | 512 | write_r16(imm8(), t); |
| 513 | SET_CNZ(t >> 8); | |
| 513 | SET_CNZ(t >> 8 & 0xff); | |
| 514 | 514 | } |
| 515 | 515 | |
| 516 | 516 | void tms7000_device::movd_ind() |
| r250109 | r250110 | |
| 518 | 518 | m_icount -= 14; |
| 519 | 519 | UINT16 t = read_r16(imm8()); |
| 520 | 520 | write_r16(imm8(), t); |
| 521 | SET_CNZ(t >> 8); | |
| 521 | SET_CNZ(t >> 8 & 0xff); | |
| 522 | 522 | } |
| 523 | 523 | |
| 524 | 524 | // long branch |
| r250109 | r250110 | |
|---|---|---|
| 58 | 58 | |
| 59 | 59 | DEVICE_ADDRESS_MAP_START( io_map, 8, mos6532_t ) |
| 60 | 60 | ADDRESS_MAP_GLOBAL_MASK(0x1f) |
| 61 | AM_RANGE(0x00, 0x00) AM_MIRROR(0x18) AM_READWRITE(pa_data_r, pa_data_w) // SWCHA | |
| 62 | AM_RANGE(0x01, 0x01) AM_MIRROR(0x18) AM_READWRITE(pa_ddr_r, pa_ddr_w) // SWACNT | |
| 63 | AM_RANGE(0x02, 0x02) AM_MIRROR(0x18) AM_READWRITE(pb_data_r, pb_data_w) // SWCHB | |
| 64 | AM_RANGE(0x03, 0x03) AM_MIRROR(0x18) AM_READWRITE(pb_ddr_r, pb_ddr_w) // SWBCNT | |
| 61 | AM_RANGE(0x00, 0x00) AM_MIRROR(0x18) AM_READWRITE(pa_data_r, pa_data_w) | |
| 62 | AM_RANGE(0x01, 0x01) AM_MIRROR(0x18) AM_READWRITE(pa_ddr_r, pa_ddr_w) | |
| 63 | AM_RANGE(0x02, 0x02) AM_MIRROR(0x18) AM_READWRITE(pb_data_r, pb_data_w) | |
| 64 | AM_RANGE(0x03, 0x03) AM_MIRROR(0x18) AM_READWRITE(pb_ddr_r, pb_ddr_w) | |
| 65 | 65 | AM_RANGE(0x14, 0x17) AM_WRITE(timer_off_w) |
| 66 | 66 | AM_RANGE(0x1c, 0x1f) AM_WRITE(timer_on_w) |
| 67 | 67 | AM_RANGE(0x04, 0x04) AM_MIRROR(0x12) AM_READ(timer_off_r) |
| r250109 | r250110 | |
| 276 | 276 | |
| 277 | 277 | void mos6530_base_t::device_reset() |
| 278 | 278 | { |
| 279 | m_pa_out = 0 | |
| 279 | m_pa_out = 0; | |
| 280 | 280 | m_pa_ddr = 0; |
| 281 | m_pb_out = 0 | |
| 281 | m_pb_out = 0; | |
| 282 | 282 | m_pb_ddr = 0; |
| 283 | 283 | |
| 284 | 284 | m_ie_timer = false; |
| r250109 | r250110 | |
|---|---|---|
| 1452 | 1452 | |
| 1453 | 1453 | xxx = (xpos+xtile*8)&0x1ff; |
| 1454 | 1454 | |
| 1455 | gfxdata = MEGADRIV_VDP_VRAM(base_addr+1) | (MEGADRIV_VDP_VRAM(base_addr)<<16); | |
| 1455 | gfxdata = MEGADRIV_VDP_VRAM((base_addr+1)&0x7fff) | (MEGADRIV_VDP_VRAM((base_addr+0)&0x7fff)<<16); | |
| 1456 | 1456 | |
| 1457 | 1457 | for(loopcount=0;loopcount<8;loopcount++) |
| 1458 | 1458 | { |
| r250109 | r250110 | |
|---|---|---|
| 1032 | 1032 | m_max_ras_addr = 0x1f; |
| 1033 | 1033 | m_vert_char_total = 0x7f; |
| 1034 | 1034 | |
| 1035 | m_supports_disp_start_addr_r = | |
| 1035 | m_supports_disp_start_addr_r = true; | |
| 1036 | 1036 | m_supports_vert_sync_width = false; |
| 1037 | 1037 | m_supports_status_reg_d5 = false; |
| 1038 | 1038 | m_supports_status_reg_d6 = false; |
| r250109 | r250110 | |
| 1168 | 1168 | { |
| 1169 | 1169 | mc6845_device::device_start(); |
| 1170 | 1170 | |
| 1171 | m_supports_disp_start_addr_r = | |
| 1171 | m_supports_disp_start_addr_r = false; | |
| 1172 | 1172 | m_supports_vert_sync_width = true; |
| 1173 | 1173 | m_supports_status_reg_d5 = false; |
| 1174 | 1174 | m_supports_status_reg_d6 = false; |
| r250109 | r250110 | |
|---|---|---|
| 1931 | 1931 | // flush any pending updates before waiting again |
| 1932 | 1932 | machine.debug_view().flush_osd_updates(); |
| 1933 | 1933 | |
| 1934 | machine.manager().web()->serve(); | |
| 1935 | ||
| 1934 | 1936 | // clear the memory modified flag and wait |
| 1935 | 1937 | global->memory_modified = false; |
| 1936 | 1938 | if (machine.debug_flags & DEBUG_FLAG_OSD_ENABLED) |
| r250109 | r250110 | |
|---|---|---|
| 185 | 185 | { OPTION_AUTOBOOT_COMMAND ";ab", NULL, OPTION_STRING, "command to execute after machine boot" }, |
| 186 | 186 | { OPTION_AUTOBOOT_DELAY, "2", OPTION_INTEGER, "timer delay in sec to trigger command execution on autoboot" }, |
| 187 | 187 | { OPTION_AUTOBOOT_SCRIPT ";script", NULL, OPTION_STRING, "lua script to execute after machine boot" }, |
| 188 | { OPTION_HTTP, "0", OPTION_BOOLEAN, "enable local http server" }, | |
| 189 | { OPTION_HTTP_PORT, "8080", OPTION_STRING, "http server listener port" }, | |
| 190 | { OPTION_HTTP_PATH, "web", OPTION_STRING, "path to web files" }, | |
| 191 | { OPTION_CONSOLE, "0", OPTION_BOOLEAN, "enable emulator LUA console" }, | |
| 188 | 192 | { NULL } |
| 189 | 193 | }; |
| 190 | 194 |
| r250109 | r250110 | |
|---|---|---|
| 189 | 189 | #define OPTION_AUTOBOOT_DELAY "autoboot_delay" |
| 190 | 190 | #define OPTION_AUTOBOOT_SCRIPT "autoboot_script" |
| 191 | 191 | |
| 192 | #define OPTION_HTTP "http" | |
| 193 | #define OPTION_HTTP_PORT "http_port" | |
| 194 | #define OPTION_HTTP_PATH "http_path" | |
| 195 | #define OPTION_CONSOLE "console" | |
| 196 | ||
| 192 | 197 | //************************************************************************** |
| 193 | 198 | // TYPE DEFINITIONS |
| 194 | 199 | //************************************************************************** |
| r250109 | r250110 | |
| 361 | 366 | int autoboot_delay() const { return int_value(OPTION_AUTOBOOT_DELAY); } |
| 362 | 367 | const char *autoboot_script() const { return value(OPTION_AUTOBOOT_SCRIPT); } |
| 363 | 368 | |
| 369 | bool http() const { return bool_value(OPTION_HTTP); } | |
| 370 | const char *http_port() const { return value(OPTION_HTTP_PORT); } | |
| 371 | const char *http_path() const { return value(OPTION_HTTP_PATH); } | |
| 372 | bool console() const { return bool_value(OPTION_CONSOLE); } | |
| 373 | ||
| 364 | 374 | // FIXME: Couriersud: This should be in image_device_exit |
| 365 | 375 | void remove_device_options(); |
| 366 | 376 |
| r250109 | r250110 | |
|---|---|---|
| 17 | 17 | #include "osdepend.h" |
| 18 | 18 | #include "drivenum.h" |
| 19 | 19 | #include "ui/ui.h" |
| 20 | #include "mongoose/mongoose.h" | |
| 20 | 21 | |
| 21 | 22 | //************************************************************************** |
| 22 | 23 | // LUA ENGINE |
| r250109 | r250110 | |
| 861 | 862 | } while (1); |
| 862 | 863 | } |
| 863 | 864 | |
| 864 | /* | |
| 865 | 865 | static void *serve_lua(void *param) |
| 866 | 866 | { |
| 867 | 867 | lua_engine *engine = (lua_engine *)param; |
| 868 | 868 | engine->serve_lua(); |
| 869 | 869 | return NULL; |
| 870 | 870 | } |
| 871 | */ | |
| 872 | 871 | |
| 872 | ||
| 873 | 873 | //------------------------------------------------- |
| 874 | 874 | // lua_engine - constructor |
| 875 | 875 | //------------------------------------------------- |
| r250109 | r250110 | |
| 1025 | 1025 | |
| 1026 | 1026 | void lua_engine::start_console() |
| 1027 | 1027 | { |
| 1028 | mg_start_thread(::serve_lua, this); | |
| 1028 | 1029 | } |
| 1029 | 1030 | |
| 1030 | 1031 | //------------------------------------------------- |
| r250109 | r250110 | |
|---|---|---|
| 393 | 393 | js_set_main_loop(this); |
| 394 | 394 | #endif |
| 395 | 395 | |
| 396 | manager().web()->serve(); | |
| 397 | ||
| 396 | 398 | // execute CPUs if not paused |
| 397 | 399 | if (!m_paused) |
| 398 | 400 | m_scheduler.timeslice(); |
| r250109 | r250110 | |
|---|---|---|
| 117 | 117 | machine_manager::machine_manager(emu_options &options,osd_interface &osd) |
| 118 | 118 | : m_osd(osd), |
| 119 | 119 | m_options(options), |
| 120 | m_web(options), | |
| 120 | 121 | m_new_driver_pending(NULL), |
| 121 | 122 | m_machine(NULL) |
| 122 | 123 | { |
| r250109 | r250110 | |
| 154 | 155 | void machine_manager::update_machine() |
| 155 | 156 | { |
| 156 | 157 | m_lua.set_machine(m_machine); |
| 158 | m_web.set_machine(m_machine); | |
| 159 | if (m_machine!=NULL) m_web.push_message("update_machine"); | |
| 157 | 160 | } |
| 158 | 161 | |
| 159 | 162 | /*------------------------------------------------- |
| r250109 | r250110 | |
| 172 | 175 | int error = MAMERR_NONE; |
| 173 | 176 | |
| 174 | 177 | m_lua.initialize(); |
| 178 | if (m_options.console()) { | |
| 179 | m_lua.start_console(); | |
| 180 | } | |
| 175 | 181 | while (error == MAMERR_NONE && !exit_pending) |
| 176 | 182 | { |
| 177 | 183 | m_new_driver_pending = NULL; |
| r250109 | r250110 | |
|---|---|---|
| 18 | 18 | |
| 19 | 19 | #include <time.h> |
| 20 | 20 | |
| 21 | #include "webengine.h" | |
| 22 | ||
| 21 | 23 | class osd_interface; |
| 22 | 24 | |
| 23 | 25 | //************************************************************************** |
| r250109 | r250110 | |
| 87 | 89 | |
| 88 | 90 | osd_interface &osd() const; |
| 89 | 91 | emu_options &options() const { return m_options; } |
| 92 | web_engine *web() { return &m_web; } | |
| 90 | 93 | lua_engine *lua() { return &m_lua; } |
| 91 | 94 | |
| 92 | 95 | running_machine *machine() { return m_machine; } |
| r250109 | r250110 | |
| 102 | 105 | osd_interface & m_osd; // reference to OSD system |
| 103 | 106 | emu_options & m_options; // reference to options |
| 104 | 107 | |
| 108 | web_engine m_web; | |
| 105 | 109 | lua_engine m_lua; |
| 106 | 110 | |
| 107 | 111 | const game_driver * m_new_driver_pending; // pointer to the next pending driver |
| r250109 | r250110 | |
|---|---|---|
| 374 | 374 | // loop while we have a handler |
| 375 | 375 | while (m_handler_callback != handler_ingame && !machine().scheduled_event_pending() && !ui_menu::stack_has_special_main_menu()) |
| 376 | 376 | { |
| 377 | machine().manager().web()->serve(); | |
| 377 | 378 | machine().video().frame_update(); |
| 378 | 379 | } |
| 379 | 380 |
| r0 | r250110 | |
|---|---|---|
| 1 | // license:BSD-3-Clause | |
| 2 | // copyright-holders:Miodrag Milanovic | |
| 3 | /*************************************************************************** | |
| 4 | ||
| 5 | webengine.c | |
| 6 | ||
| 7 | Handle MAME internal web server. | |
| 8 | ||
| 9 | ***************************************************************************/ | |
| 10 | ||
| 11 | #include "mongoose/mongoose.h" | |
| 12 | #include "jsoncpp/include/json/json.h" | |
| 13 | #include "emu.h" | |
| 14 | #include "emuopts.h" | |
| 15 | #include "ui/ui.h" | |
| 16 | #include "webengine.h" | |
| 17 | #include "lua.hpp" | |
| 18 | ||
| 19 | #include "osdepend.h" | |
| 20 | ||
| 21 | //************************************************************************** | |
| 22 | // WEB ENGINE | |
| 23 | //************************************************************************** | |
| 24 | ||
| 25 | char* websanitize_statefilename ( char* unsanitized ) | |
| 26 | { | |
| 27 | // It's important that we remove any dangerous characters from any filename | |
| 28 | // we receive from a web client. This can be a serious security hole. | |
| 29 | // As MAME/MESS policy is lowercase filenames, also lowercase it. | |
| 30 | ||
| 31 | char* sanitized = new char[64]; | |
| 32 | int insertpoint =0; | |
| 33 | char charcompare; | |
| 34 | ||
| 35 | while (*unsanitized != 0) | |
| 36 | { | |
| 37 | charcompare = *unsanitized; | |
| 38 | // ASCII 48-57 are 0-9 | |
| 39 | // ASCII 97-122 are lowercase A-Z | |
| 40 | ||
| 41 | if ((charcompare >= 48 && charcompare <= 57) || (charcompare >= 97 && charcompare <= 122)) | |
| 42 | { | |
| 43 | sanitized[insertpoint] = charcompare; | |
| 44 | insertpoint++; | |
| 45 | sanitized[insertpoint] = '\0'; // Make sure we're null-terminated. | |
| 46 | } | |
| 47 | // ASCII 65-90 are uppercase A-Z. These need to be lowercased. | |
| 48 | if (charcompare >= 65 && charcompare <= 90) | |
| 49 | { | |
| 50 | sanitized[insertpoint] = tolower(charcompare); // Lowercase it | |
| 51 | insertpoint++; | |
| 52 | sanitized[insertpoint] = '\0'; // Make sure we're null-terminated. | |
| 53 | } | |
| 54 | unsanitized++; | |
| 55 | } | |
| 56 | return (sanitized); | |
| 57 | } | |
| 58 | ||
| 59 | int web_engine::json_game_handler(struct mg_connection *conn) | |
| 60 | { | |
| 61 | Json::Value data; | |
| 62 | data["name"] = m_machine->system().name; | |
| 63 | data["description"] = m_machine->system().description; | |
| 64 | data["year"] = m_machine->system().year; | |
| 65 | data["manufacturer"] = m_machine->system().manufacturer; | |
| 66 | data["parent"] = m_machine->system().parent; | |
| 67 | data["source_file"] = m_machine->system().source_file; | |
| 68 | data["flags"] = m_machine->system().flags; | |
| 69 | data["ispaused"] = m_machine->paused(); | |
| 70 | ||
| 71 | Json::FastWriter writer; | |
| 72 | std::string json = writer.write(data); | |
| 73 | // Send HTTP reply to the client | |
| 74 | mg_printf(conn, | |
| 75 | "HTTP/1.1 200 OK\r\n" | |
| 76 | "Content-Type: application/json\r\n" | |
| 77 | "Content-Length: %d\r\n" // Always set Content-Length | |
| 78 | "\r\n" | |
| 79 | "%s", | |
| 80 | (int)json.length(), json.c_str()); | |
| 81 | ||
| 82 | // Returning non-zero tells mongoose that our function has replied to | |
| 83 | // the client, and mongoose should not send client any more data. | |
| 84 | ||
| 85 | return MG_TRUE; | |
| 86 | } | |
| 87 | ||
| 88 | int web_engine::json_slider_handler(struct mg_connection *conn) | |
| 89 | { | |
| 90 | const slider_state *curslider; | |
| 91 | std::string tempstring; | |
| 92 | Json::Value array(Json::arrayValue); | |
| 93 | ||
| 94 | // add all sliders | |
| 95 | for (curslider = machine().ui().get_slider_list(); curslider != NULL; curslider = curslider->next) | |
| 96 | { | |
| 97 | INT32 curval = (*curslider->update)(machine(), curslider->arg, &tempstring, SLIDER_NOCHANGE); | |
| 98 | Json::Value data; | |
| 99 | data["description"] = curslider->description; | |
| 100 | data["minval"] = curslider->minval; | |
| 101 | data["maxval"] = curslider->maxval; | |
| 102 | data["defval"] = curslider->defval; | |
| 103 | data["incval"] = curslider->incval; | |
| 104 | data["curval"] = curval; | |
| 105 | array.append(data); | |
| 106 | } | |
| 107 | ||
| 108 | // add all sliders | |
| 109 | for (curslider = (slider_state*)machine().osd().get_slider_list(); curslider != NULL; curslider = curslider->next) | |
| 110 | { | |
| 111 | INT32 curval = (*curslider->update)(machine(), curslider->arg, &tempstring, SLIDER_NOCHANGE); | |
| 112 | Json::Value data; | |
| 113 | data["description"] = curslider->description; | |
| 114 | data["minval"] = curslider->minval; | |
| 115 | data["maxval"] = curslider->maxval; | |
| 116 | data["defval"] = curslider->defval; | |
| 117 | data["incval"] = curslider->incval; | |
| 118 | data["curval"] = curval; | |
| 119 | array.append(data); | |
| 120 | } | |
| 121 | Json::FastWriter writer; | |
| 122 | std::string json = writer.write(array); | |
| 123 | // Send HTTP reply to the client | |
| 124 | mg_printf(conn, | |
| 125 | "HTTP/1.1 200 OK\r\n" | |
| 126 | "Content-Type: application/json\r\n" | |
| 127 | "Content-Length: %d\r\n" // Always set Content-Length | |
| 128 | "\r\n" | |
| 129 | "%s", | |
| 130 | (int)json.length(), json.c_str()); | |
| 131 | ||
| 132 | return MG_TRUE; | |
| 133 | } | |
| 134 | ||
| 135 | void reg_string(struct lua_State *L, const char *name, const char *val) { | |
| 136 | lua_pushstring(L, name); | |
| 137 | lua_pushstring(L, val); | |
| 138 | lua_rawset(L, -3); | |
| 139 | } | |
| 140 | ||
| 141 | void reg_int(struct lua_State *L, const char *name, int val) { | |
| 142 | lua_pushstring(L, name); | |
| 143 | lua_pushinteger(L, val); | |
| 144 | lua_rawset(L, -3); | |
| 145 | } | |
| 146 | ||
| 147 | void reg_function(struct lua_State *L, const char *name, | |
| 148 | lua_CFunction func, struct mg_connection *conn) { | |
| 149 | lua_pushstring(L, name); | |
| 150 | lua_pushlightuserdata(L, conn); | |
| 151 | lua_pushcclosure(L, func, 1); | |
| 152 | lua_rawset(L, -3); | |
| 153 | } | |
| 154 | ||
| 155 | static int lua_write(lua_State *L) { | |
| 156 | int i, num_args; | |
| 157 | const char *str; | |
| 158 | size_t size; | |
| 159 | struct mg_connection *conn = (struct mg_connection *) | |
| 160 | lua_touserdata(L, lua_upvalueindex(1)); | |
| 161 | ||
| 162 | num_args = lua_gettop(L); | |
| 163 | for (i = 1; i <= num_args; i++) { | |
| 164 | if (lua_isstring(L, i)) { | |
| 165 | str = lua_tolstring(L, i, &size); | |
| 166 | mg_send_data(conn, str, size); | |
| 167 | } | |
| 168 | } | |
| 169 | ||
| 170 | return 0; | |
| 171 | } | |
| 172 | ||
| 173 | static int lua_header(lua_State *L) { | |
| 174 | struct mg_connection *conn = (struct mg_connection *) | |
| 175 | lua_touserdata(L, lua_upvalueindex(1)); | |
| 176 | ||
| 177 | const char *header = luaL_checkstring(L,1); | |
| 178 | const char *value = luaL_checkstring(L,2); | |
| 179 | ||
| 180 | mg_send_header(conn, header, value); | |
| 181 | ||
| 182 | return 0; | |
| 183 | } | |
| 184 | ||
| 185 | ||
| 186 | static void prepare_lua_environment(struct mg_connection *ri, lua_State *L) { | |
| 187 | extern void luaL_openlibs(lua_State *); | |
| 188 | int i; | |
| 189 | ||
| 190 | luaL_openlibs(L); | |
| 191 | ||
| 192 | if (ri == NULL) return; | |
| 193 | ||
| 194 | // Register mg module | |
| 195 | lua_newtable(L); | |
| 196 | reg_function(L, "write", lua_write, ri); | |
| 197 | reg_function(L, "header", lua_header, ri); | |
| 198 | ||
| 199 | // Export request_info | |
| 200 | lua_pushstring(L, "request_info"); | |
| 201 | lua_newtable(L); | |
| 202 | reg_string(L, "request_method", ri->request_method); | |
| 203 | reg_string(L, "uri", ri->uri); | |
| 204 | reg_string(L, "http_version", ri->http_version); | |
| 205 | reg_string(L, "query_string", ri->query_string); | |
| 206 | reg_string(L, "remote_ip", ri->remote_ip); | |
| 207 | reg_int(L, "remote_port", ri->remote_port); | |
| 208 | reg_string(L, "local_ip", ri->local_ip); | |
| 209 | reg_int(L, "local_port", ri->local_port); | |
| 210 | lua_pushstring(L, "content"); | |
| 211 | lua_pushlstring(L, ri->content == NULL ? "" : ri->content, ri->content_len); | |
| 212 | lua_rawset(L, -3); | |
| 213 | reg_int(L, "num_headers", ri->num_headers); | |
| 214 | lua_pushstring(L, "http_headers"); | |
| 215 | lua_newtable(L); | |
| 216 | for (i = 0; i < ri->num_headers; i++) { | |
| 217 | reg_string(L, ri->http_headers[i].name, ri->http_headers[i].value); | |
| 218 | } | |
| 219 | lua_rawset(L, -3); | |
| 220 | lua_rawset(L, -3); | |
| 221 | ||
| 222 | lua_setglobal(L, "mg"); | |
| 223 | ||
| 224 | } | |
| 225 | ||
| 226 | ||
| 227 | static void lsp(struct mg_connection *conn, const char *p, int len, lua_State *L) { | |
| 228 | int i, j, pos = 0; | |
| 229 | for (i = 0; i < len; i++) { | |
| 230 | if (p[i] == '<' && p[i + 1] == '?') { | |
| 231 | for (j = i + 1; j < len ; j++) { | |
| 232 | if (p[j] == '?' && p[j + 1] == '>') { | |
| 233 | if (i-pos!=0) mg_send_data(conn, p + pos, i - pos); | |
| 234 | if (luaL_loadbuffer(L, p + (i + 2), j - (i + 2), "") == 0) { | |
| 235 | lua_pcall(L, 0, LUA_MULTRET, 0); | |
| 236 | } | |
| 237 | pos = j + 2; | |
| 238 | i = pos - 1; | |
| 239 | break; | |
| 240 | } | |
| 241 | } | |
| 242 | } | |
| 243 | } | |
| 244 | if (i > pos) { | |
| 245 | mg_send_data(conn, p + pos, i - pos); | |
| 246 | } | |
| 247 | } | |
| 248 | ||
| 249 | static int filename_endswith(const char *str, const char *suffix) | |
| 250 | { | |
| 251 | if (!str || !suffix) | |
| 252 | return 0; | |
| 253 | size_t lenstr = strlen(str); | |
| 254 | size_t lensuffix = strlen(suffix); | |
| 255 | if (lensuffix > lenstr) | |
| 256 | return 0; | |
| 257 | return strncmp(str + lenstr - lensuffix, suffix, lensuffix) == 0; | |
| 258 | } | |
| 259 | ||
| 260 | // This function will be called by mongoose on every new request. | |
| 261 | int web_engine::begin_request_handler(struct mg_connection *conn) | |
| 262 | { | |
| 263 | std::string file_path = std::string(mg_get_option(m_server, "document_root")).append(PATH_SEPARATOR).append(conn->uri); | |
| 264 | if (filename_endswith(file_path.c_str(), ".lp")) | |
| 265 | { | |
| 266 | FILE *fp = NULL; | |
| 267 | if ((fp = fopen(file_path.c_str(), "rb")) != NULL) { | |
| 268 | fseek (fp, 0, SEEK_END); | |
| 269 | size_t size = ftell(fp); | |
| 270 | fseek (fp, 0, SEEK_SET); | |
| 271 | char *data = (char*)mg_mmap(fp,size); | |
| 272 | ||
| 273 | lua_State *L = luaL_newstate(); | |
| 274 | prepare_lua_environment(conn, L); | |
| 275 | lsp(conn, data, (int) size, L); | |
| 276 | if (L != NULL) lua_close(L); | |
| 277 | mg_munmap(data,size); | |
| 278 | fclose(fp); | |
| 279 | return MG_TRUE; | |
| 280 | } else { | |
| 281 | return MG_FALSE; | |
| 282 | } | |
| 283 | } | |
| 284 | else if (!strncmp(conn->uri, "/json/",6)) | |
| 285 | { | |
| 286 | if (!strcmp(conn->uri, "/json/game")) | |
| 287 | { | |
| 288 | return json_game_handler(conn); | |
| 289 | } | |
| 290 | if (!strcmp(conn->uri, "/json/slider")) | |
| 291 | { | |
| 292 | return json_slider_handler(conn); | |
| 293 | } | |
| 294 | } | |
| 295 | else if (!strncmp(conn->uri, "/keypost",8)) | |
| 296 | { | |
| 297 | // Is there any sane way to determine the length of the buffer before getting it? | |
| 298 | // A request for a way was previously filed with the mongoose devs, | |
| 299 | // but it looks like it was never implemented. | |
| 300 | ||
| 301 | // For now, we'll allow a paste buffer of 32k. | |
| 302 | // To-do: Send an error if the paste is too big? | |
| 303 | char cmd_val[32768]; | |
| 304 | ||
| 305 | int pastelength = mg_get_var(conn, "val", cmd_val, sizeof(cmd_val)); | |
| 306 | if (pastelength > 0) { | |
| 307 | machine().ioport().natkeyboard().post_utf8(cmd_val); | |
| 308 | } | |
| 309 | // Send HTTP reply to the client | |
| 310 | mg_printf(conn, | |
| 311 | "HTTP/1.1 200 OK\r\n" | |
| 312 | "Content-Type: text/plain\r\n" | |
| 313 | "Content-Length: 2\r\n" // Always set Content-Length | |
| 314 | "\r\n" | |
| 315 | "OK"); | |
| 316 | ||
| 317 | // Returning non-zero tells mongoose that our function has replied to | |
| 318 | // the client, and mongoose should not send client any more data. | |
| 319 | return MG_TRUE; | |
| 320 | } | |
| 321 | else if (!strncmp(conn->uri, "/keyupload",8)) | |
| 322 | { | |
| 323 | char *upload_data; | |
| 324 | int data_length, ofs = 0; | |
| 325 | char var_name[100], file_name[255]; | |
| 326 | while ((ofs = mg_parse_multipart(conn->content + ofs, conn->content_len - ofs, var_name, sizeof(var_name), file_name, sizeof(file_name), (const char **)&upload_data, &data_length)) > 0) { | |
| 327 | mg_printf_data(conn, "File: %s, size: %d bytes", file_name, data_length); | |
| 328 | } | |
| 329 | ||
| 330 | // That upload_data contains more than we need. It also has the headers. | |
| 331 | // We'll need to strip it down to just what we want. | |
| 332 | ||
| 333 | if ((&data_length > 0) && (sizeof(file_name) > 0)) | |
| 334 | { | |
| 335 | // MSVC doesn't yet support variable-length arrays, so chop the string the old-fashioned way | |
| 336 | upload_data[data_length] = '\0'; | |
| 337 | ||
| 338 | // Now paste the stripped down paste_data.. | |
| 339 | machine().ioport().natkeyboard().post_utf8(upload_data); | |
| 340 | } | |
| 341 | return MG_TRUE; | |
| 342 | } | |
| 343 | else if (!strncmp(conn->uri, "/cmd",4)) | |
| 344 | { | |
| 345 | char cmd_name[64]; | |
| 346 | mg_get_var(conn, "name", cmd_name, sizeof(cmd_name)); | |
| 347 | ||
| 348 | if(!strcmp(cmd_name,"softreset")) | |
| 349 | { | |
| 350 | m_machine->schedule_soft_reset(); | |
| 351 | } | |
| 352 | else if(!strcmp(cmd_name,"hardreset")) | |
| 353 | { | |
| 354 | m_machine->schedule_hard_reset(); | |
| 355 | } | |
| 356 | else if(!strcmp(cmd_name,"exit")) | |
| 357 | { | |
| 358 | m_machine->schedule_exit(); | |
| 359 | } | |
| 360 | else if(!strcmp(cmd_name,"togglepause")) | |
| 361 | { | |
| 362 | if (m_machine->paused()) | |
| 363 | m_machine->resume(); | |
| 364 | else | |
| 365 | m_machine->pause(); | |
| 366 | } | |
| 367 | else if(!strcmp(cmd_name,"savestate")) | |
| 368 | { | |
| 369 | char cmd_val[64]; | |
| 370 | mg_get_var(conn, "val", cmd_val, sizeof(cmd_val)); | |
| 371 | char *filename = websanitize_statefilename(cmd_val); | |
| 372 | m_machine->schedule_save(filename); | |
| 373 | } | |
| 374 | else if(!strcmp(cmd_name,"loadstate")) | |
| 375 | { | |
| 376 | char cmd_val[64]; | |
| 377 | mg_get_var(conn, "val", cmd_val, sizeof(cmd_val)); | |
| 378 | char *filename = cmd_val; | |
| 379 | m_machine->schedule_load(filename); | |
| 380 | } | |
| 381 | else if(!strcmp(cmd_name,"loadauto")) | |
| 382 | { | |
| 383 | // This is here to just load the autosave and only the autosave. | |
| 384 | m_machine->schedule_load("auto"); | |
| 385 | } | |
| 386 | ||
| 387 | // Send HTTP reply to the client | |
| 388 | mg_printf(conn, | |
| 389 | "HTTP/1.1 200 OK\r\n" | |
| 390 | "Content-Type: text/plain\r\n" | |
| 391 | "Content-Length: 2\r\n" // Always set Content-Length | |
| 392 | "\r\n" | |
| 393 | "OK"); | |
| 394 | ||
| 395 | // Returning non-zero tells mongoose that our function has replied to | |
| 396 | // the client, and mongoose should not send client any more data. | |
| 397 | return MG_TRUE; | |
| 398 | } | |
| 399 | else if (!strncmp(conn->uri, "/slider",7)) | |
| 400 | { | |
| 401 | char cmd_id[64]; | |
| 402 | char cmd_val[64]; | |
| 403 | mg_get_var(conn, "id", cmd_id, sizeof(cmd_id)); | |
| 404 | mg_get_var(conn, "val", cmd_val, sizeof(cmd_val)); | |
| 405 | int cnt = 0; | |
| 406 | int id = atoi(cmd_id); | |
| 407 | const slider_state *curslider; | |
| 408 | for (curslider = machine().ui().get_slider_list(); curslider != NULL; curslider = curslider->next) | |
| 409 | { | |
| 410 | if (cnt==id) | |
| 411 | (*curslider->update)(machine(), curslider->arg, NULL, atoi(cmd_val)); | |
| 412 | cnt++; | |
| 413 | } | |
| 414 | for (curslider = (slider_state*)machine().osd().get_slider_list(); curslider != NULL; curslider = curslider->next) | |
| 415 | { | |
| 416 | if (cnt==id) | |
| 417 | (*curslider->update)(machine(), curslider->arg, NULL, atoi(cmd_val)); | |
| 418 | cnt++; | |
| 419 | } | |
| 420 | ||
| 421 | // Send HTTP reply to the client | |
| 422 | mg_printf(conn, | |
| 423 | "HTTP/1.1 200 OK\r\n" | |
| 424 | "Content-Type: text/plain\r\n" | |
| 425 | "Content-Length: 2\r\n" // Always set Content-Length | |
| 426 | "\r\n" | |
| 427 | "OK"); | |
| 428 | ||
| 429 | // Returning non-zero tells mongoose that our function has replied to | |
| 430 | // the client, and mongoose should not send client any more data. | |
| 431 | return MG_TRUE; | |
| 432 | } | |
| 433 | else if (!strncmp(conn->uri, "/screenshot.png",15)) | |
| 434 | { | |
| 435 | screen_device_iterator iter(m_machine->root_device()); | |
| 436 | screen_device *screen = iter.first(); | |
| 437 | ||
| 438 | if (screen == NULL) | |
| 439 | { | |
| 440 | return 0; | |
| 441 | } | |
| 442 | ||
| 443 | std::string fname("screenshot.png"); | |
| 444 | emu_file file(m_machine->options().snapshot_directory(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS); | |
| 445 | file_error filerr = file.open(fname.c_str()); | |
| 446 | ||
| 447 | if (filerr != FILERR_NONE) | |
| 448 | { | |
| 449 | return 0; | |
| 450 | } | |
| 451 | ||
| 452 | m_machine->video().save_snapshot(screen, file); | |
| 453 | std::string fullpath(file.fullpath()); | |
| 454 | file.close(); | |
| 455 | mg_send_header(conn, "Cache-Control", "no-cache, no-store, must-revalidate"); | |
| 456 | mg_send_header(conn, "Pragma", "no-cache"); | |
| 457 | mg_send_header(conn, "Expires", "0"); | |
| 458 | mg_send_file(conn, fullpath.c_str(), NULL); | |
| 459 | return MG_MORE; // It is important to return MG_MORE after mg_send_file! | |
| 460 | } | |
| 461 | return 0; | |
| 462 | } | |
| 463 | ||
| 464 | static int ev_handler(struct mg_connection *conn, enum mg_event ev) { | |
| 465 | if (ev == MG_REQUEST) { | |
| 466 | if (conn->is_websocket) { | |
| 467 | // This handler is called for each incoming websocket frame, one or more | |
| 468 | // times for connection lifetime. | |
| 469 | // Echo websocket data back to the client. | |
| 470 | return conn->content_len == 4 && !memcmp(conn->content, "exit", 4) ? MG_FALSE : MG_TRUE; | |
| 471 | } else { | |
| 472 | web_engine *engine = static_cast<web_engine *>(conn->server_param); | |
| 473 | return engine->begin_request_handler(conn); | |
| 474 | } | |
| 475 | } else if (ev== MG_WS_CONNECT) { | |
| 476 | // New websocket connection. Send connection ID back to the client. | |
| 477 | mg_websocket_printf(conn, WEBSOCKET_OPCODE_TEXT, "update_machine"); | |
| 478 | return MG_FALSE; | |
| 479 | } else if (ev == MG_AUTH) { | |
| 480 | return MG_TRUE; | |
| 481 | } else { | |
| 482 | return MG_FALSE; | |
| 483 | } | |
| 484 | } | |
| 485 | ||
| 486 | //------------------------------------------------- | |
| 487 | // web_engine - constructor | |
| 488 | //------------------------------------------------- | |
| 489 | ||
| 490 | web_engine::web_engine(emu_options &options) | |
| 491 | : m_options(options), | |
| 492 | m_machine(NULL), | |
| 493 | m_server(NULL), | |
| 494 | //m_lastupdatetime(0), | |
| 495 | m_exiting_core(false), | |
| 496 | m_http(m_options.http()) | |
| 497 | ||
| 498 | { | |
| 499 | if (m_http) { | |
| 500 | m_server = mg_create_server(this, ev_handler); | |
| 501 | ||
| 502 | mg_set_option(m_server, "listening_port", options.http_port()); | |
| 503 | mg_set_option(m_server, "document_root", options.http_path()); | |
| 504 | } | |
| 505 | ||
| 506 | } | |
| 507 | ||
| 508 | //------------------------------------------------- | |
| 509 | // ~web_engine - destructor | |
| 510 | //------------------------------------------------- | |
| 511 | ||
| 512 | web_engine::~web_engine() | |
| 513 | { | |
| 514 | if (m_http) | |
| 515 | close(); | |
| 516 | } | |
| 517 | ||
| 518 | //------------------------------------------------- | |
| 519 | // close - close and cleanup of lua engine | |
| 520 | //------------------------------------------------- | |
| 521 | ||
| 522 | void web_engine::close() | |
| 523 | { | |
| 524 | m_exiting_core = 1; | |
| 525 | // Cleanup, and free server instance | |
| 526 | mg_destroy_server(&m_server); | |
| 527 | } | |
| 528 | ||
| 529 | void web_engine::serve() | |
| 530 | { | |
| 531 | if (m_http) mg_poll_server(m_server, 0); | |
| 532 | } | |
| 533 | ||
| 534 | void web_engine::push_message(const char *message) | |
| 535 | { | |
| 536 | struct mg_connection *c; | |
| 537 | if (m_server!=NULL) { | |
| 538 | // Iterate over all connections, and push current time message to websocket ones. | |
| 539 | for (c = mg_next(m_server, NULL); c != NULL; c = mg_next(m_server, c)) { | |
| 540 | if (c->is_websocket) { | |
| 541 | mg_websocket_write(c, 1, message, strlen(message)); | |
| 542 | } | |
| 543 | } | |
| 544 | } | |
| 545 | } |
| r0 | r250110 | |
|---|---|---|
| 1 | // license:BSD-3-Clause | |
| 2 | // copyright-holders:Miodrag Milanovic | |
| 3 | /*************************************************************************** | |
| 4 | ||
| 5 | webengine.h | |
| 6 | ||
| 7 | Handle MAME internal web server. | |
| 8 | ||
| 9 | ***************************************************************************/ | |
| 10 | ||
| 11 | #pragma once | |
| 12 | ||
| 13 | #ifndef __WEB_ENGINE_H__ | |
| 14 | #define __WEB_ENGINE_H__ | |
| 15 | ||
| 16 | struct mg_server; // Handle for the HTTP server itself | |
| 17 | struct mg_connection; // Handle for the individual connection | |
| 18 | ||
| 19 | class web_engine | |
| 20 | { | |
| 21 | public: | |
| 22 | // construction/destruction | |
| 23 | web_engine(emu_options &options); | |
| 24 | ~web_engine(); | |
| 25 | ||
| 26 | void serve(); | |
| 27 | void push_message(const char *message); | |
| 28 | void close(); | |
| 29 | ||
| 30 | void set_machine(running_machine *machine) { m_machine = machine; } | |
| 31 | int begin_request_handler(struct mg_connection *conn); | |
| 32 | protected: | |
| 33 | // getters | |
| 34 | running_machine &machine() const { return *m_machine; } | |
| 35 | ||
| 36 | int json_game_handler(struct mg_connection *conn); | |
| 37 | int json_slider_handler(struct mg_connection *conn); | |
| 38 | private: | |
| 39 | // internal state | |
| 40 | emu_options & m_options; | |
| 41 | running_machine * m_machine; | |
| 42 | struct mg_server * m_server; | |
| 43 | //osd_ticks_t m_lastupdatetime; | |
| 44 | bool m_exiting_core; | |
| 45 | bool m_http; | |
| 46 | }; | |
| 47 | ||
| 48 | #endif /* __web_engine_H__ */ |
| r250109 | r250110 | |
|---|---|---|
| 12100 | 12100 | adonis // (c) 1998 |
| 12101 | 12101 | adonisa // (c) 1998 |
| 12102 | 12102 | swheart2 // (c) 1998 |
| 12103 | thgamblr // (c) 1998 | |
| 12104 | 12103 | reelrock // (c) 1998 |
| 12105 | 12104 | indiandr // (c) 1998 |
| 12106 | 12105 | chariotc // (c) 1998 |
| r250109 | r250110 | |
| 12111 | 12110 | magicmska // (c) 2000 |
| 12112 | 12111 | margmgc // (c) 2000 |
| 12113 | 12112 | marmagic // (c) 2000 |
| 12114 | prtygras // (c) 2001 | |
| 12115 | 12113 | geishanz // (c) 2001 |
| 12116 | 12114 | adonise // (c) 2001 |
| 12117 | 12115 | koalamnt // (c) 2001 |
| r250109 | r250110 | |
| 13239 | 13237 | // Atari TTL logic games + roms |
| 13240 | 13238 | antiairc // (c) 1975 Atari |
| 13241 | 13239 | crashnsc // (c) 1975 Atari |
| 13242 | indy4 // (c) 1976 Atari | |
| 13240 | indy4 // (c) 1976 Atari | |
| 13243 | 13241 | indy800 // (c) 1975 Atari / Kee |
| 13244 | 13242 | jetfight // (c) 1975 Atari |
| 13245 | 13243 | jetfighta // (c) 1975 Atari |
| 13246 | 13244 | outlaw // (c) 1976 Atari |
| 13247 | sharkjaw // (c) 1975 Atari | |
| 13245 | sharkjaw // (c) 1975 Atari | |
| 13248 | 13246 | steeplec // (c) 1975 Atari |
| 13249 | 13247 | stuntcyc // (c) 1976 Atari |
| 13250 | tank // (c) 1974 Atari / Kee | |
| 13251 | tankii // (c) 1975 Atari / Kee | |
| 13248 | tank // (c) 1974 Atari | |
| 13252 | 13249 | |
| 13253 | 13250 | // Atari TTL Missing Rom Dumps |
| 13254 | //astrotrf // (c) 1975 Atari | |
| 13255 | //lemans // (c) 1974 Atari | |
| 13256 | //gtrak10 // (c) 1974 Atari / Kee | |
| 13257 | //gtrak20 // (c) 1976 Atari / Kee | |
| 13258 | //qwak // (c) 1974 Atari | |
| 13251 | //astrotrf | |
| 13252 | //lemans | |
| 13253 | //gtrak10 | |
| 13254 | //gtrak20 | |
| 13255 | //quack | |
| 13259 | 13256 | |
| 13260 | 13257 | // Atari 100% TTL |
| 13261 | 13258 | pong // (c) 1972 Atari |
| 13259 | pongd // (c) 1975 Atari | |
| 13262 | 13260 | pongf // (c) 1972 Atari |
| 13263 | pongd // (c) 1973 Atari | |
| 13264 | 13261 | breakout // (c) 1976 Atari |
| 13265 | //cktpong // (c) 1974 Atari / National Entertainment Co. | |
| 13266 | //coupedav // (c) 1973 Atari France | |
| 13267 | //coupfran // (c) 1974 Atari Europe | |
| 13268 | //drpong // (c) 1974 Atari | |
| 13269 | //pupppong // (c) 1974 Atari | |
| 13270 | //snoopong // (c) 1974 Atari | |
| 13271 | //suprpong // (c) 1974 Atari | |
| 13272 | //breakckt // (c) 1976 Atari | |
| 13273 | //consolet // (c) 1976 Atari Europe | |
| 13274 | //crossfir // (c) 1975 Atari / Kee | |
| 13275 | //eliminat // (c) 1973 Atari / Kee | |
| 13276 | //goaliv // (c) 1975 Atari | |
| 13277 | //gotchaat // (c) 1973 Atari | |
| 13278 | //gotchaatc // (c) 1973 Atari | |
| 13279 | //hiway // (c) 1975 Atari | |
| 13280 | //pinpong // (c) 1974 Atari | |
| 13281 | //pursuit // (c) 1975 Atari / Kee | |
| 13282 | //quadpong // (c) 1974 Atari | |
| 13283 | //rebound // (c) 1974 Atari / Kee | |
| 13284 | //spacrace // (c) 1973 Atari | |
| 13285 | //touchme // (c) 1974 Atari | |
| 13286 | //worldcup // (c) 1974 Atari | |
| 13262 | //coupedem | |
| 13263 | //goal4 | |
| 13264 | //gotchaat | |
| 13265 | //gotchaatc | |
| 13266 | //highway | |
| 13267 | //pinpong | |
| 13268 | //pursuit | |
| 13269 | //quadpong | |
| 13270 | //rebound | |
| 13271 | //spacrace | |
| 13272 | //touchme | |
| 13287 | 13273 | |
| 13288 | 13274 | // Meadows TTL |
| 13289 | bombaway // (c) 1976 Meadows | |
| 13290 | ckidzo // (c) 1976 Meadows | |
| 13291 | cgunship // (c) 1976 Meadows | |
| 13292 | mead4in1 // (c) 197? Meadows | |
| 13275 | bombaway | |
| 13276 | ckidzo | |
| 13277 | cgunship | |
| 13278 | mead4in1 | |
| 13293 | 13279 | |
| 13294 | 13280 | // Misc TTL + roms |
| 13281 | tv21 // (c) 197? A-1 Supply | |
| 13282 | tv21_3 // (c) 197? A-1 Supply | |
| 13283 | tvpoker // (c) 197? A-1 Supply | |
| 13295 | 13284 | sburners // (c) 1975 Allied Leisure |
| 13296 | 13285 | fun4 // (c) 1976 Bailey |
| 13297 | 13286 | fun4a // (c) 1976 Bailey |
| r250109 | r250110 | |
| 13303 | 13292 | biplane4 // (c) 1976 Fun Games |
| 13304 | 13293 | take5 // (c) 1975 Fun Games |
| 13305 | 13294 | dpatrol // (c) 1977 PSE |
| 13306 | //knightar // (c) 1976 PSE | |
| 13307 | //gametree // (c) 1978 PSE | |
| 13308 | 13295 | vollyrmt // (c) 1973 Ramtek |
| 13309 | 13296 | hockyrmt // (c) 1973 Ramtek |
| 13310 | 13297 | soccrrmt // (c) 1973 Ramtek |
| r250109 | r250110 | |
| 13321 | 13308 | ttblock // (c) 1977 Taito |
| 13322 | 13309 | zzblock // (c) 1979 Taito |
| 13323 | 13310 | |
| 13324 | // A-1 Supply | |
| 13325 | tv21 // (c) 197? A-1 Supply | |
| 13326 | tv21_3 // (c) 197? A-1 Supply | |
| 13327 | tvpoker // (c) 197? A-1 Supply | |
| 13328 | ||
| 13329 | 13311 | // JPM System 5 + Video Expansion 2 |
| 13330 | 13312 | monopoly // Monopoly (JPM) |
| 13331 | 13313 | monopolya // Monopoly (JPM) |
| r250109 | r250110 | |
|---|---|---|
| 10 | 10 | |
| 11 | 11 | ***************************************************************************/ |
| 12 | 12 | |
| 13 | // the new RIOT does not work with the SuperCharger | |
| 14 | // for example "mame64 a2600 scharger -cass offifrog" fails to load after playing the tape | |
| 15 | #define USE_NEW_RIOT 0 | |
| 16 | ||
| 17 | ||
| 18 | 13 | #include "emu.h" |
| 19 | ||
| 14 | #include "machine/mos6530n.h" | |
| 20 | 15 | #include "cpu/m6502/m6507.h" |
| 21 | 16 | #include "sound/tiaintf.h" |
| 22 | 17 | #include "video/tia.h" |
| r250109 | r250110 | |
| 28 | 23 | #include "bus/vcs/compumat.h" |
| 29 | 24 | #include "bus/vcs_ctrl/ctrl.h" |
| 30 | 25 | |
| 31 | ||
| 32 | ||
| 33 | #if USE_NEW_RIOT | |
| 34 | #include "machine/mos6530n.h" | |
| 35 | #else | |
| 36 | #include "machine/6532riot.h" | |
| 37 | #endif | |
| 38 | ||
| 39 | ||
| 40 | 26 | #define CONTROL1_TAG "joyport1" |
| 41 | 27 | #define CONTROL2_TAG "joyport2" |
| 42 | 28 | |
| 43 | 29 | |
| 44 | ||
| 45 | 30 | class a2600_state : public driver_device |
| 46 | 31 | { |
| 47 | 32 | public: |
| r250109 | r250110 | |
| 88 | 73 | required_device<m6507_device> m_maincpu; |
| 89 | 74 | required_device<screen_device> m_screen; |
| 90 | 75 | required_ioport m_swb; |
| 91 | #if USE_NEW_RIOT | |
| 92 | 76 | required_device<mos6532_t> m_riot; |
| 93 | #else | |
| 94 | required_device<riot6532_device> m_riot; | |
| 95 | #endif | |
| 96 | ||
| 97 | 77 | }; |
| 98 | 78 | |
| 99 | 79 | |
| r250109 | r250110 | |
| 108 | 88 | static ADDRESS_MAP_START(a2600_mem, AS_PROGRAM, 8, a2600_state ) // 6507 has 13-bit address space, 0x0000 - 0x1fff |
| 109 | 89 | AM_RANGE(0x0000, 0x007f) AM_MIRROR(0x0f00) AM_DEVREADWRITE("tia_video", tia_video_device, read, write) |
| 110 | 90 | AM_RANGE(0x0080, 0x00ff) AM_MIRROR(0x0d00) AM_RAM AM_SHARE("riot_ram") |
| 111 | #if USE_NEW_RIOT | |
| 112 | 91 | AM_RANGE(0x0280, 0x029f) AM_MIRROR(0x0d00) AM_DEVICE("riot", mos6532_t, io_map) |
| 113 | #else | |
| 114 | AM_RANGE(0x0280, 0x029f) AM_MIRROR(0x0d00) AM_DEVREADWRITE("riot", riot6532_device, read, write) | |
| 115 | #endif | |
| 116 | 92 | // AM_RANGE(0x1000, 0x1fff) is cart data and it is configured at reset time, depending on the mounted cart! |
| 117 | 93 | ADDRESS_MAP_END |
| 118 | 94 | |
| r250109 | r250110 | |
| 140 | 116 | } |
| 141 | 117 | else if (masked_offset < 0x2a0) |
| 142 | 118 | { |
| 143 | #if USE_NEW_RIOT | |
| 144 | 119 | ret = m_riot->io_r(space, masked_offset); |
| 145 | #else | |
| 146 | ret = m_riot->read(space, masked_offset); | |
| 147 | #endif | |
| 148 | 120 | } |
| 149 | 121 | else if (masked_offset < 0x300) |
| 150 | 122 | { |
| r250109 | r250110 | |
| 176 | 148 | } |
| 177 | 149 | else if (masked_offset < 0x2a0) |
| 178 | 150 | { |
| 179 | #if USE_NEW_RIOT | |
| 180 | 151 | m_riot->io_w(space, masked_offset, data); |
| 181 | #else | |
| 182 | m_riot->write(space, masked_offset, data); | |
| 183 | #endif | |
| 184 | 152 | } |
| 185 | 153 | else if (masked_offset < 0x300) |
| 186 | 154 | { |
| r250109 | r250110 | |
| 595 | 563 | MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.90) |
| 596 | 564 | |
| 597 | 565 | /* devices */ |
| 598 | #if USE_NEW_RIOT | |
| 599 | 566 | MCFG_DEVICE_ADD("riot", MOS6532n, MASTER_CLOCK_NTSC / 3) |
| 600 | MCFG_MOS6530n_IN_PA_CB(READ8(a2600_state, switch_A_r)) | |
| 601 | MCFG_MOS6530n_OUT_PA_CB(WRITE8(a2600_state, switch_A_w)) | |
| 602 | MCFG_MOS6530n_IN_PB_CB(READ8(a2600_state, riot_input_port_8_r)) | |
| 603 | MCFG_MOS6530n_OUT_PB_CB(WRITE8(a2600_state, switch_B_w)) | |
| 604 | MCFG_MOS6530n_IRQ_CB(WRITELINE(a2600_state, irq_callback)) | |
| 605 | #else | |
| 606 | MCFG_DEVICE_ADD("riot", RIOT6532, MASTER_CLOCK_NTSC / 3) | |
| 607 | MCFG_RIOT6532_IN_PA_CB(READ8(a2600_state, switch_A_r)) | |
| 608 | MCFG_RIOT6532_OUT_PA_CB(WRITE8(a2600_state, switch_A_w)) | |
| 609 | MCFG_RIOT6532_IN_PB_CB(READ8(a2600_state, riot_input_port_8_r)) | |
| 610 | MCFG_RIOT6532_OUT_PB_CB(WRITE8(a2600_state, switch_B_w)) | |
| 611 | MCFG_RIOT6532_IRQ_CB(WRITELINE(a2600_state, irq_callback)) | |
| 612 | #endif | |
| 567 | MCFG_MOS6530n_IN_PA_CB(READ8(a2600_state, switch_A_r)) | |
| 568 | MCFG_MOS6530n_OUT_PA_CB(WRITE8(a2600_state, switch_A_w)) | |
| 569 | MCFG_MOS6530n_IN_PB_CB(READ8(a2600_state, riot_input_port_8_r)) | |
| 570 | MCFG_MOS6530n_OUT_PB_CB(WRITE8(a2600_state, switch_B_w)) | |
| 571 | MCFG_MOS6530n_IRQ_CB(WRITELINE(a2600_state, irq_callback)) | |
| 613 | 572 | |
| 614 | 573 | MCFG_VCS_CONTROL_PORT_ADD(CONTROL1_TAG, vcs_control_port_devices, "joy") |
| 615 | 574 | MCFG_VCS_CONTROL_PORT_ADD(CONTROL2_TAG, vcs_control_port_devices, NULL) |
| r250109 | r250110 | |
| 645 | 604 | MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.90) |
| 646 | 605 | |
| 647 | 606 | /* devices */ |
| 648 | #if USE_NEW_RIOT | |
| 649 | MCFG_DEVICE_ADD("riot", MOS6532n, MASTER_CLOCK_PAL / 3) | |
| 650 | MCFG_MOS6530n_IN_PA_CB(READ8(a2600_state, switch_A_r)) | |
| 651 | MCFG_MOS6530n_OUT_PA_CB(WRITE8(a2600_state, switch_A_w)) | |
| 652 | MCFG_MOS6530n_IN_PB_CB(READ8(a2600_state, riot_input_port_8_r)) | |
| 653 | MCFG_MOS6530n_OUT_PB_CB(WRITE8(a2600_state, switch_B_w)) | |
| 654 | MCFG_MOS6530n_IRQ_CB(WRITELINE(a2600_state, irq_callback)) | |
| 655 | #else | |
| 656 | MCFG_DEVICE_ADD("riot", RIOT6532, MASTER_CLOCK_PAL / 3) | |
| 657 | MCFG_RIOT6532_IN_PA_CB(READ8(a2600_state, switch_A_r)) | |
| 658 | MCFG_RIOT6532_OUT_PA_CB(WRITE8(a2600_state, switch_A_w)) | |
| 659 | MCFG_RIOT6532_IN_PB_CB(READ8(a2600_state, riot_input_port_8_r)) | |
| 660 | MCFG_RIOT6532_OUT_PB_CB(WRITE8(a2600_state, switch_B_w)) | |
| 661 | MCFG_RIOT6532_IRQ_CB(WRITELINE(a2600_state, irq_callback)) | |
| 662 | #endif | |
| 607 | MCFG_DEVICE_ADD("riot", MOS6532n, MASTER_CLOCK_PAL / 3) | |
| 608 | MCFG_MOS6530n_IN_PA_CB(READ8(a2600_state, switch_A_r)) | |
| 609 | MCFG_MOS6530n_OUT_PA_CB(WRITE8(a2600_state, switch_A_w)) | |
| 610 | MCFG_MOS6530n_IN_PB_CB(READ8(a2600_state, riot_input_port_8_r)) | |
| 611 | MCFG_MOS6530n_OUT_PB_CB(WRITE8(a2600_state, switch_B_w)) | |
| 612 | MCFG_MOS6530n_IRQ_CB(WRITELINE(a2600_state, irq_callback)) | |
| 663 | 613 | |
| 664 | 614 | MCFG_VCS_CONTROL_PORT_ADD(CONTROL1_TAG, vcs_control_port_devices, "joy") |
| 665 | 615 | MCFG_VCS_CONTROL_PORT_ADD(CONTROL2_TAG, vcs_control_port_devices, NULL) |
| r250109 | r250110 | |
|---|---|---|
| 1115 | 1115 | ROM_REGION( 0x20000*4, "sram", ROMREGION_ERASE00 ) |
| 1116 | 1116 | ROM_END |
| 1117 | 1117 | |
| 1118 | // MV4084/1 - 10 Credit Multiplier / 9 Line Multiline. | |
| 1119 | // THE GAMBLER - Export A - 30/10/98. | |
| 1120 | // Marked as EHG0916 and 92.268%. | |
| 1121 | // All devices are 27c4002 instead of 27c4096. | |
| 1122 | ROM_START( thgamblr ) | |
| 1123 | ARISTOCRAT_MK5_BIOS | |
| 1124 | ROM_REGION( 0x400000, "game_prg", ROMREGION_ERASEFF ) | |
| 1125 | ROM_LOAD32_WORD( "ehg0916_the_gambler.u7", 0x000000, 0x80000, CRC(7524c954) SHA1(0a895d1e2d09a2c873bbbbeb37bc59c25f3c577c) ) // 92.268% | |
| 1126 | ROM_LOAD32_WORD( "ehg0916_the_gambler.u11", 0x000002, 0x80000, CRC(f29a6932) SHA1(17761218a04d36a599c987b4e13c0e3f46b7793f) ) // 92.268% | |
| 1127 | ROM_LOAD32_WORD( "ehg0916_the_gambler.u8", 0x100000, 0x80000, CRC(e2221fdf) SHA1(8a7b2d5de68ae66fe1915a6faac6277249e3fb53) ) // base | |
| 1128 | ROM_LOAD32_WORD( "ehg0916_the_gambler.u12", 0x100002, 0x80000, CRC(ebe957f9) SHA1(539945ec9beafe2c83051208370588fce2334f16) ) // base | |
| 1129 | ||
| 1130 | ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 ) /* ARM Code */ | |
| 1131 | ||
| 1132 | ROM_REGION( 0x200000, "vram", ROMREGION_ERASE00 ) | |
| 1133 | ||
| 1134 | ROM_REGION( 0x20000*4, "sram", ROMREGION_ERASE00 ) | |
| 1135 | ROM_END | |
| 1136 | ||
| 1137 | 1118 | // MV4098 - 10 Credit Multiplier / 9 Line Multiline. |
| 1138 | 1119 | // BOOT SCOOTIN' - Export A - 25/08/99. |
| 1139 | 1120 | // All devices are 27c4002 instead of 27c4096. |
| r250109 | r250110 | |
| 1277 | 1258 | ROM_REGION( 0x20000*4, "sram", ROMREGION_ERASE00 ) |
| 1278 | 1259 | ROM_END |
| 1279 | 1260 | |
| 1280 | // MV4115/3 - 20 Line Multiline / 3,5,10,20,25,50 Credit Multiplier. | |
| 1281 | // Party Gras - Export B - 06/02/2001. | |
| 1282 | // Marked as BHG1284 and touch. | |
| 1283 | ROM_START( prtygras ) | |
| 1284 | ARISTOCRAT_MK5_BIOS | |
| 1285 | ROM_REGION( 0x400000, "game_prg", ROMREGION_ERASEFF ) | |
| 1286 | ROM_LOAD32_WORD( "bhg1284_party_grass.u7", 0x000000, 0x80000, CRC(02ed0631) SHA1(ae2c89c876a030d325ec94490d293deba772630e) ) | |
| 1287 | ROM_LOAD32_WORD( "bhg1284_party_grass.u11", 0x000002, 0x80000, CRC(7ac80cd9) SHA1(70e910784a1e1ea8820005082e76223a85a3c346) ) | |
| 1288 | ROM_LOAD32_WORD( "bhg1284_party_grass.u8", 0x100000, 0x80000, CRC(28774b9a) SHA1(ebdd738a73ffa7c5238640f4d7956751f7bb6243) ) | |
| 1289 | ROM_LOAD32_WORD( "bhg1284_party_grass.u12", 0x100002, 0x80000, CRC(942835c1) SHA1(fefc509311716559ac6b836a56b2c981907d499b) ) | |
| 1290 | ||
| 1291 | ROM_REGION( 0x800000, "maincpu", ROMREGION_ERASE00 ) /* ARM Code */ | |
| 1292 | ||
| 1293 | ROM_REGION( 0x200000, "vram", ROMREGION_ERASE00 ) | |
| 1294 | ||
| 1295 | ROM_REGION( 0x20000*4, "sram", ROMREGION_ERASE00 ) | |
| 1296 | ROM_END | |
| 1297 | ||
| 1298 | 1261 | // MV4115/6 - 9/20 Line Multiline Multiplier. |
| 1299 | 1262 | // Party Gras [Reel Game] - Export A - 10/11/2001. |
| 1300 | 1263 | // All devices are 27c4002 instead of 27c4096. |
| r250109 | r250110 | |
| 1344 | 1307 | GAME( 1998, adonisa, adonis, aristmk5, aristmk5, aristmk5_state, aristmk5, ROT0, "Aristocrat", "Adonis (0100751V, NSW/ACT)", MACHINE_NOT_WORKING|MACHINE_IMPERFECT_SOUND ) // 602/9, A - 25/05/98, Rev 9 |
| 1345 | 1308 | GAME( 1998, swheart2, aristmk5, aristmk5_usa, aristmk5, aristmk5_state, aristmk5, ROT0, "Aristocrat", "Sweet Hearts II (PHG0742, Export, 92.252%)", MACHINE_NOT_WORKING|MACHINE_IMPERFECT_SOUND ) // MV4061, A - 29/06/98 |
| 1346 | 1309 | GAME( 1998, reelrock, 0, aristmk5, aristmk5, aristmk5_state, aristmk5, ROT0, "Aristocrat", "Reelin-n-Rockin (0100779V, Local)", MACHINE_NOT_WORKING|MACHINE_IMPERFECT_SOUND ) // 628, A - 13/07/98 |
| 1347 | GAME( 1998, thgamblr, aristmk5, aristmk5_usa, aristmk5, aristmk5_state, aristmk5, ROT0, "Aristocrat", "The Gambler (EHG0916, Export, 92.268%)", MACHINE_NOT_WORKING|MACHINE_IMPERFECT_SOUND ) // MV4084/1, A - 30/10/98 | |
| 1348 | 1310 | GAME( 1998, indiandr, 0, aristmk5, aristmk5, aristmk5_state, aristmk5, ROT0, "Aristocrat", "Indian Dreaming (0100845V, Local)", MACHINE_NOT_WORKING|MACHINE_IMPERFECT_SOUND ) // 628/1, B - 15/12/98 |
| 1349 | 1311 | GAME( 1998, chariotc, 0, aristmk5, aristmk5, aristmk5_state, aristmk5, ROT0, "Aristocrat", "The Chariot Challenge (04J00714, NSW/ACT)", MACHINE_NOT_WORKING|MACHINE_IMPERFECT_SOUND ) // 630, A - 10/08/98, Rev 12 |
| 1350 | 1312 | GAME( 1999, wtiger, 0, aristmk5, aristmk5, aristmk5_state, aristmk5, ROT0, "Aristocrat", "White Tiger Classic (0200954V, NSW/ACT)", MACHINE_NOT_WORKING|MACHINE_IMPERFECT_SOUND ) // 638/1, B - 08/07/99 |
| r250109 | r250110 | |
| 1354 | 1316 | GAME( 2000, magicmska, magicmsk, aristmk5_usa, aristmk5, aristmk5_state, aristmk5, ROT0, "Aristocrat", "Magic Mask (MV4115, Export, set 2)", MACHINE_NOT_WORKING|MACHINE_IMPERFECT_SOUND ) // MV4115, A - 09/05/00 |
| 1355 | 1317 | GAME( 2000, margmgc, 0, aristmk5, aristmk5, aristmk5_state, aristmk5, ROT0, "Aristocrat", "Margarita Magic (01J00101, NSW/ACT)", MACHINE_NOT_WORKING|MACHINE_IMPERFECT_SOUND ) // JB005, A - 07/07/00 |
| 1356 | 1318 | GAME( 2000, marmagic, aristmk5, aristmk5_usa, aristmk5, aristmk5_state, aristmk5, ROT0, "Aristocrat", "Margarita Magic (EHG1559, NSW/ACT)", MACHINE_NOT_WORKING|MACHINE_IMPERFECT_SOUND ) // US003, A - 07/07/00 |
| 1357 | GAME( 2001, prtygras, aristmk5, aristmk5_usa, aristmk5, aristmk5_state, aristmk5, ROT0, "Aristocrat", "Party Gras (MV4115/3, Export, touch)", MACHINE_NOT_WORKING|MACHINE_IMPERFECT_SOUND ) // MV4115/3, B - 06/02/01 | |
| 1358 | 1319 | GAME( 2001, geishanz, 0, aristmk5, aristmk5, aristmk5_state, aristmk5, ROT0, "Aristocrat", "Geisha (0101408V, New Zealand)", MACHINE_NOT_WORKING|MACHINE_IMPERFECT_SOUND ) // MV4127, A - 05/03/01 |
| 1359 | 1320 | GAME( 2001, adonise, aristmk5, aristmk5_usa, aristmk5, aristmk5_state, aristmk5, ROT0, "Aristocrat", "Adonis (MV4124/1, Export)", MACHINE_NOT_WORKING|MACHINE_IMPERFECT_SOUND ) // MV4124/1, B - 31/07/01 |
| 1360 | 1321 | GAME( 2001, koalamnt, aristmk5, aristmk5_usa, aristmk5, aristmk5_state, aristmk5, ROT0, "Aristocrat", "Koala Mint (MV4137, Export)", MACHINE_NOT_WORKING|MACHINE_IMPERFECT_SOUND ) // MV4137, A - 12/09/01 |
| r250109 | r250110 | |
|---|---|---|
| 5 | 5 | Atari / Kee Games Driver - Discrete Games made in the 1970's |
| 6 | 6 | |
| 7 | 7 | |
| 8 | Atari / Kee Games List | |
| 8 | Atari / Kee Games List and Data based, in part from: | |
| 9 | 9 | |
| 10 | 10 | - Andy's collection of Bronzeage Atari Video Arcade PCB's" |
| 11 | 11 | http://www.andysarcade.net/personal/bronzeage/index.htm |
| r250109 | r250110 | |
| 19 | 19 | Technical Manual #s Game Name(s) Atari Part #'s Data PROM/ROM Chip Numbers |
| 20 | 20 | -------------------+----------------------------------------------------------+----------------------------------+---------+--------------------------------------- |
| 21 | 21 | TM-025 Anti-Aircraft (1975) A000951 YES 003127 |
| 22 | TM-058 Breakout/Breakout Cocktail/Consolette (1976) A004533 NO | |
| 23 | TM-015 Cocktail Pong/Coup Franc (1974) ??????? NO | |
| 22 | 24 | TM-048 Crash 'N Score/Stock Car (1975) A004256 YES 003186(x2), 003187(x2), 004248, 004247 |
| 23 | TM-030 Crossfire (1975) ??????? NO? | |
| 24 | TM-022 Elimination! (1973) A000845 NO | |
| 25 | TM-035 Goal IV (1975) A000823 NO | |
| 25 | TM-030 Crossfire (1975) ??????? NO | |
| 26 | TM-0?? Dr. Pong/Puppy Pong/Snoopy Pong (1974) ??????? NO | |
| 27 | TM-035 Goal IV/Goal/4 (1975) A000823 NO | |
| 26 | 28 | TM-016 Gotcha/Gotcha Color? (1973) A000816 NO |
| 27 | 29 | TM-003,005,011,020 Gran Trak 10/Trak 10/Formula K (1974) A000872,A000872 K3RT YES 74186 Racetrack Prom (K5) |
| 28 | 30 | TM-004,021 Gran Trak 20/Trak 20/Twin Racer (1974) A001791(RT20),A001793(A20-K4DRTA) YES 74186 Racetrack prom (K5) |
| 29 | TM-028 Hi | |
| 31 | TM-028 Highway/Hi-Way (1975) A003211 NO | |
| 30 | 32 | TM-055 Indy 4 (1976) A003000,A006268,A006270 YES 003186, 003187, 005502-01, 05503-01 |
| 31 | 33 | TM-026 Indy 800 (1975) A003000,A003170,A003182 YES 003186-003189 (4) |
| 32 | 34 | A003184,A003191,A003198,A003199 |
| 33 | 35 | TM-027,052 Jet Fighter/Jet Fighter Cocktail/Launch Aircraft (1975) A004254,A004255 YES 004250-004252, 004253-01 to 03 (3) |
| 34 | TM-077 Le | |
| 36 | TM-077 LeMans (1976) A005844,A005845 YES 005837-01, 005838-01, 005839-01 | |
| 35 | 37 | TM-040 Outlaw (1976) A003213 YES 003323 - ROM (8205 @ J4) |
| 36 | 38 | TM-007 Pin Pong (1974) A001660 NO |
| 37 | TM-019 Pursuit (1975) K8P-B 90128 NO | |
| 38 | TM-012,034 Quadrapong (1974) A000845 NO | |
| 39 | TM-013 Pong (1972) A001433 NO | |
| 40 | TM-014 Pong Doubles/Coupe Davis (1974) A000785 NO | |
| 41 | TM-019 Pursuit (1975) K8P-B 90128 YES | |
| 42 | TM-012,022,034 Quadrapong/Elimination (1974) A000845 NO | |
| 39 | 43 | TM-009 Qwak!/Quack (1974) A000937,A000953 YES 72074/37-2530N (K9) |
| 40 | TM-001,023,032 Rebound/Spike/Volleyball (1974) A000517,A000846,SPIKE-(A or B) NO | |
| 41 | TM-047 Shark JAWS (1975) A003806 YES 004182, 004183 | |
| 42 | TM-008 Space Race (1973) A000803 NO | |
| 44 | TM-001,032 Rebound/Volleyball (1974) A000517,A000846 NO | |
| 45 | TM-047 Shark Jaws (1975) A003806 YES 004182, 004183 | |
| 46 | TM-008 Space Race (1974) A000803 NO | |
| 47 | TM-023 Spike (1974) SPIKE-(A or B) NO | |
| 43 | 48 | TM-046 Steeplechase/Astroturf (1975) A003750 YES 003774 ROM Bugle (C8), 003773-01 "A" Horse (C4), 003773-02 "B" Horse (D4) |
| 44 | 49 | TM-057 Stunt Cycle (1976) A004128 YES 004275 ROM Motorcycle/Bus (1F), 004811 ROM Score Translator (D7) |
| 45 | TM-010,036 Tank/Tank Cocktail (1974) A003111 (K5T-F 90124) YES 90-2006 004800SD Tank Rom (K10) | |
| 46 | TM-049 Tank II (1975) K5T-F 90124 YES 90-2006 | |
| 47 | TM-002 Touch-Me (1974) ??????? NO | |
| 50 | 422 Superpong (1974) A000423 NO | |
| 51 | TM-010,036,049 Tank/Tank Cocktail/Tank II (1974/1975) A003111 (K5T-F 90124) YES 90-2006 | |
| 52 | TM-002 Touch Me (1974) ??????? NO | |
| 48 | 53 | TM-006,017 World Cup/World Cup Football/Coupe du Monde (1974) A000823 NO |
| 49 | 54 | |
| 50 | 55 | - Not Known to be released or produced, but at least announced. |
| 51 | 56 | |
| 52 | 57 | TM-0?? Arcade Driver/Driver First Person (Not Produced/Released) (197?) |
| 53 | 58 | TM-018 Dodgeball/Dodgem (Not Produced/Released) (1975) |
| 54 | TM-024 Qwakers (Not Produced/Released) (1974?) | |
| 59 | TM-024 Qwakers (Not Produced/Released) (1974?) | |
| 55 | 60 | |
| 56 | 61 | |
| 57 | 62 | ***************************************************************************/ |
| r250109 | r250110 | |
| 304 | 309 | ROM_LOAD( "90-2006.k10" ,0x0000, 0x0801, CRC(c25f6014) SHA1(7bd3fca5f64c928a645ca27c643b736667cef216) ) |
| 305 | 310 | ROM_END |
| 306 | 311 | |
| 307 | ROM_START( tankii ) | |
| 308 | ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASE00 ) | |
| 309 | 312 | |
| 310 | /* The "custom" 24-pin ROM used in Atari/Kee Games "Tank" is known as a MOSTEK MK28000P. */ | |
| 311 | ROM_REGION( 0x0801, "gfx", ROMREGION_ERASE00 ) // 2049 Byte Size? | |
| 312 | ROM_LOAD( "90-2006.k10" ,0x0000, 0x0801, CRC(c25f6014) SHA1(7bd3fca5f64c928a645ca27c643b736667cef216) ) | |
| 313 | ROM_END | |
| 314 | ||
| 315 | 313 | /* // NO DUMPED ROMS |
| 316 | 314 | |
| 315 | // Astroturf (1975) | |
| 317 | 316 | ROM_START( astrotrf ) |
| 318 | 317 | ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASE00 ) |
| 319 | 318 | |
| r250109 | r250110 | |
| 322 | 321 | ROM_LOAD( "003773-02.c4", 0x0100, 0x0100, NO_DUMP ) // Graphics (Astroturf - Rev.A) |
| 323 | 322 | ROM_END |
| 324 | 323 | |
| 324 | // Gran Trak 10 / Trak 10 / Formula K / Race Circuit (1974) | |
| 325 | 325 | ROM_START( gtrak10 ) // Unknown size, assumed 2K Bytes |
| 326 | 326 | ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASE00 ) |
| 327 | 327 | |
| r250109 | r250110 | |
| 329 | 329 | ROM_LOAD( "74168.k5", 0x0000, 0x0800, NO_DUMP) // Racetrack |
| 330 | 330 | ROM_END |
| 331 | 331 | |
| 332 | // Gran Trak 20 / Trak 20 / Twin Racer (1974) | |
| 332 | 333 | ROM_START( gtrak20 ) // Unknown size, assumed 2K Bytes |
| 333 | 334 | ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASE00 ) |
| 334 | 335 | |
| r250109 | r250110 | |
| 336 | 337 | ROM_LOAD( "74168.k5", 0x0000, 0x0800, NO_DUMP) // Racetrack |
| 337 | 338 | ROM_END |
| 338 | 339 | |
| 340 | // LeMans (1976) | |
| 339 | 341 | ROM_START( lemans ) |
| 340 | 342 | ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASE00 ) |
| 341 | 343 | |
| r250109 | r250110 | |
| 345 | 347 | ROM_LOAD( "005839-01.n6", 0x0200, 0x0100, NO_DUMP ) // Rom 3 |
| 346 | 348 | ROM_END |
| 347 | 349 | |
| 350 | // Qwak! / Quack (1974) | |
| 348 | 351 | ROM_START( qwak ) |
| 349 | 352 | ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASE00 ) |
| 350 | 353 | |
| r250109 | r250110 | |
| 367 | 370 | ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASE00 ) |
| 368 | 371 | ROM_END |
| 369 | 372 | |
| 370 | ROM_START( | |
| 373 | ROM_START( goal4 ) | |
| 371 | 374 | ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASE00 ) |
| 372 | 375 | ROM_END |
| 373 | 376 | |
| 374 | ROM_START( goa | |
| 377 | ROM_START( gotcha ) | |
| 375 | 378 | ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASE00 ) |
| 376 | 379 | ROM_END |
| 377 | 380 | |
| 378 | ROM_START( gotcha | |
| 381 | ROM_START( gotchac ) | |
| 379 | 382 | ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASE00 ) |
| 380 | 383 | ROM_END |
| 381 | 384 | |
| 382 | ROM_START( g | |
| 385 | ROM_START( highway ) | |
| 383 | 386 | ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASE00 ) |
| 384 | 387 | ROM_END |
| 385 | 388 | |
| 386 | ROM_START( hiway ) | |
| 387 | ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASE00 ) | |
| 388 | ROM_END | |
| 389 | ||
| 390 | 389 | ROM_START( pinpong ) |
| 391 | 390 | ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASE00 ) |
| 392 | 391 | ROM_END |
| r250109 | r250110 | |
| 415 | 414 | ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASE00 ) |
| 416 | 415 | ROM_END |
| 417 | 416 | |
| 418 | ROM_START( worldcup ) | |
| 419 | ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASE00 ) | |
| 420 | ROM_END | |
| 421 | ||
| 422 | 417 | */ |
| 423 | 418 | |
| 424 | 419 | |
| 425 | 420 | |
| 426 | 421 | GAME(1975, antiairc, 0, atarikee, 0, driver_device, 0, ROT0, "Atari", "Anti-Aircraft [TTL]", MACHINE_IS_SKELETON) |
| 427 | 422 | GAME(1975, crashnsc, 0, atarikee, 0, driver_device, 0, ROT0, "Atari", "Crash 'n Score/Stock Car [TTL]", MACHINE_IS_SKELETON) |
| 428 | GAME(1976, indy4, 0, atarikee, 0, driver_device, 0, ROT0, "Atari | |
| 423 | GAME(1976, indy4, 0, atarikee, 0, driver_device, 0, ROT0, "Atari", "Indy 4 [TTL]", MACHINE_IS_SKELETON) | |
| 429 | 424 | GAME(1975, indy800, 0, atarikee, 0, driver_device, 0, ROT90, "Atari/Kee", "Indy 800 [TTL]", MACHINE_IS_SKELETON) |
| 430 | 425 | GAME(1975, jetfight, 0, atarikee, 0, driver_device, 0, ROT0, "Atari", "Jet Fighter/Jet Fighter Cocktail/Launch Aircraft (set 1) [TTL]", MACHINE_IS_SKELETON) |
| 431 | 426 | GAME(1975, jetfighta, jetfight, atarikee, 0, driver_device, 0, ROT0, "Atari", "Jet Fighter/Jet Fighter Cocktail/Launch Aircraft (set 2) [TTL]", MACHINE_IS_SKELETON) |
| 432 | 427 | GAME(1976, outlaw, 0, atarikee, 0, driver_device, 0, ROT0, "Atari", "Outlaw [TTL]", MACHINE_IS_SKELETON) |
| 433 | GAME(1975, sharkjaw, 0, atarikee, 0, driver_device, 0, ROT0, "Atari/Horror Games", "Shark JAWS [TTL]", | |
| 428 | GAME(1975, sharkjaw, 0, atarikee, 0, driver_device, 0, ROT0, "Atari/Horror Games", "Shark JAWS [TTL]",MACHINE_IS_SKELETON) | |
| 434 | 429 | GAME(1975, steeplec, 0, atarikee, 0, driver_device, 0, ROT0, "Atari", "Steeplechase [TTL]", MACHINE_IS_SKELETON) |
| 435 | 430 | GAME(1976, stuntcyc, 0, atarikee, 0, driver_device, 0, ROT0, "Atari", "Stunt Cycle [TTL]", MACHINE_IS_SKELETON) |
| 436 | GAME(1974, tank, 0, atarikee, 0, driver_device, 0, ROT0, "Atari/Kee", "Tank/Tank Cocktail [TTL]", MACHINE_IS_SKELETON) | |
| 437 | GAME(1975, tankii, 0, atarikee, 0, driver_device, 0, ROT0, "Atari/Kee", "Tank II [TTL]", MACHINE_IS_SKELETON) | |
| 431 | GAME(1974, tank, 0, atarikee, 0, driver_device, 0, ROT0, "Atari/Kee", "Tank/Tank Cocktail/Tank II [TTL]", MACHINE_IS_SKELETON) | |
| 438 | 432 | |
| 439 | 433 | // MISSING ROM DUMPS |
| 440 | 434 | //GAME(1975, astrotrf, steeplec, atarikee, 0, driver_device, 0, ROT0, "Atari", "Astroturf [TTL]", MACHINE_IS_SKELETON) |
| 441 | 435 | //GAME(1974, gtrak10, 0, atarikee, 0, driver_device, 0, ROT0, "Atari/Kee", "Gran Trak 10/Trak 10/Formula K [TTL]", MACHINE_IS_SKELETON) //? |
| 442 | //GAME(1974, gtrak20, 0, atarikee, 0, driver_device, 0, ROT0, "Atari/Kee", "Gran Trak 20/Trak 20/Twin Racer [TTL]", MACHINE_IS_SKELETON) //? | |
| 443 | //GAME(1976, lemans, 0, atarikee, 0, driver_device, 0, ROT0, "Atari", "Le Mans [TTL]", MACHINE_IS_SKELETON) | |
| 444 | //GAME(1974, qwak, 0, atarikee, 0, driver_device, 0, ROT0, "Atari", "Qwak!/Quack [TTL]", MACHINE_IS_SKELETON) | |
| 436 | //GAME(1974, gtrak20, 0, atarikee, 0, driver_device, 0, ROT0, "Atari/Kee", "Gran Trak 20/Trak 20/Twin Racer [TTL]", MACHINE_IS_SKELETON) //? | |
| 437 | //GAME(1976, lemans, 0, atarikee, 0, driver_device, 0, ROT0, "Atari", "LeMans [TTL]", MACHINE_IS_SKELETON) | |
| 438 | //GAME(1974, quack, 0, atarikee, 0, driver_device, 0, ROT0, "Atari", "Qwak!/Quack [TTL]", MACHINE_IS_SKELETON) | |
| 445 | 439 | |
| 446 | 440 | // 100% TTL |
| 447 | //GAME(1975, crossfir, 0, atarikee, 0, driver_device, 0, ROT0, "Atari/Kee", "Crossfire [TTL]", MACHINE_IS_SKELETON) | |
| 448 | //GAME(1973, eliminat, 0, atarikee, 0, driver_device, 0, ROT0, "Atari/Kee", "Elimination! [TTL]", MACHINE_IS_SKELETON) | |
| 449 | //GAME(1975, goaliv, 0, atarikee, 0, driver_device, 0, ROT0, "Atari", "Goal IV [TTL]", MACHINE_IS_SKELETON) | |
| 441 | //GAME(1974, coupedem, 0, atarikee, 0, driver_device, 0, ROT0, "Atari", "Coupe De Monde [TTL]", MACHINE_IS_SKELETON) | |
| 442 | //GAME(1975, crossfir, 0, atarikee, 0, driver_device, 0, ROT0, "Atari", "Crossfire [TTL]", MACHINE_IS_SKELETON) | |
| 443 | //GAME(1975, goal4, 0, atarikee, 0, driver_device, 0, ROT0, "Atari", "Goal 4/World Cup/Coupe De Monde [TTL]", MACHINE_IS_SKELETON) | |
| 450 | 444 | //GAME(1973, gotchaat, 0, atarikee, 0, driver_device, 0, ROT0, "Atari", "Gotcha [TTL]", MACHINE_IS_SKELETON) //? |
| 451 | 445 | //GAME(1973, gotchaatc, 0, atarikee, 0, driver_device, 0, ROT0, "Atari", "Gotcha Color [TTL]", MACHINE_IS_SKELETON) //? |
| 452 | //GAME(1975, hiway, | |
| 446 | //GAME(1975, highway, 0, atarikee, 0, driver_device, 0, ROT0, "Atari", "Highway/Hiway [TTL]", MACHINE_IS_SKELETON) | |
| 453 | 447 | //GAME(1974, pinpong, 0, atarikee, 0, driver_device, 0, ROT0, "Atari", "Pin Pong [TTL]", MACHINE_IS_SKELETON) |
| 454 | 448 | //GAME(1975, pursuit, 0, atarikee, 0, driver_device, 0, ROT0, "Atari", "Pursuit [TTL]", MACHINE_IS_SKELETON) |
| 455 | //GAME(1974, quadpong, eliminat, atarikee, 0, driver_device, 0, ROT0, "Atari", "Quadrapong [TTL]", MACHINE_IS_SKELETON) | |
| 456 | //GAME(1974, rebound, 0, atarikee, 0, driver_device, 0, ROT0, "Atari/Kee", "Rebound/Spike/Volleyball [TTL]", MACHINE_IS_SKELETON) | |
| 457 | //GAME(1973, spacrace, 0, atarikee, 0, driver_device, 0, ROT0, "Atari", "Space Race [TTL]", MACHINE_IS_SKELETON) | |
| 458 | //GAME(1974, touchme, 0, atarikee, 0, driver_device, 0, ROT0, "Atari", "Touch-Me [TTL]", MACHINE_IS_SKELETON) //? | |
| 459 | //GAME(1974, worldcup, 0, atarikee, 0, driver_device, 0, ROT0, "Atari", "World Cup/World Cup Football/Coupe du Monde [TTL]", MACHINE_IS_SKELETON) | |
| 449 | //GAME(1973, quadpong, 0, atarikee, 0, driver_device, 0, ROT0, "Atari/Kee", "Quadrapong/Elimination [TTL]", MACHINE_IS_SKELETON) | |
| 450 | //GAME(1974, rebound, 0, atarikee, 0, driver_device, 0, ROT0, "Atari/Kee", "Rebound/Spike/Volleyball [TTL]", MACHINE_IS_SKELETON) | |
| 451 | //GAME(1974, spacrace, 0, atarikee, 0, driver_device, 0, ROT0, "Atari", "Space Race [TTL]", MACHINE_IS_SKELETON) | |
| 452 | //GAME(1974, touchme, 0, atarikee, 0, driver_device, 0, ROT0, "Atari", "Touch Me [TTL]", MACHINE_IS_SKELETON) //? |
| r250109 | r250110 | |
|---|---|---|
| 36 | 36 | 7000 8910 write |
| 37 | 37 | 7001 8910 control |
| 38 | 38 | 8ff0-8fff sprites |
| 39 | a000 | |
| 39 | a000 ? | |
| 40 | 40 | a006 MCU HALT control |
| 41 | 41 | a007 NOP (MCU shared RAM switch) |
| 42 | 42 | a060-a06f sprites |
| r250109 | r250110 | |
| 191 | 191 | |
| 192 | 192 | AM_RANGE(0xa000, 0xa000) AM_READ_PORT("P1") |
| 193 | 193 | AM_RANGE(0xa040, 0xa040) AM_READ_PORT("P2") |
| 194 | AM_RANGE(0xa080, 0xa080) AM_ | |
| 194 | AM_RANGE(0xa080, 0xa080) AM_READ_PORT("DSW") | |
| 195 | 195 | AM_RANGE(0xa0c0, 0xa0c0) AM_READ_PORT("SYSTEM") |
| 196 | 196 | |
| 197 | 197 | AM_RANGE(0xa000, 0xa000) AM_WRITE(irq_enable_w) |
| r250109 | r250110 | |
| 218 | 218 | |
| 219 | 219 | // different protection for champbasja |
| 220 | 220 | static ADDRESS_MAP_START( champbasja_map, AS_PROGRAM, 8, champbas_state ) |
| 221 | AM_RANGE(0x6000, 0x63ff) AM_RAM | |
| 222 | 221 | AM_RANGE(0x6800, 0x68ff) AM_READ(champbja_protection_r) |
| 223 | 222 | AM_IMPORT_FROM( champbas_map ) |
| 224 | 223 | ADDRESS_MAP_END |
| r250109 | r250110 | |
| 1200 | 1199 | /* YEAR NAME PARENT MACHINE INPUT INIT MONITOR, COMPANY, FULLNAME, FLAGS */ |
| 1201 | 1200 | GAME( 1982, talbot, 0, talbot, talbot, driver_device, 0, ROT270, "Alpha Denshi Co. (Volt Electronics license)", "Talbot", MACHINE_SUPPORTS_SAVE ) |
| 1202 | 1201 | |
| 1203 | GAME( 1983, champbas, 0, champbas, champbas, champbas_state, champbas, ROT0, "Alpha Denshi Co. (Sega license)", "Champion Base Ball", MACHINE_SUPPORTS_SAVE ) | |
| 1202 | GAME( 1983, champbas, 0, champbas, champbas, champbas_state, champbas, ROT0, "Alpha Denshi Co. (Sega license)", "Champion Base Ball", MACHINE_SUPPORTS_SAVE ) | |
| 1204 | 1203 | GAME( 1983, champbasj, champbas, champbasj, champbas, champbas_state, champbas, ROT0, "Alpha Denshi Co.", "Champion Base Ball (Japan set 1)", MACHINE_SUPPORTS_SAVE ) |
| 1205 | GAME( 1983, champbasja, champbas, champbasja, champbas, champbas_state, champbas, ROT0, "Alpha Denshi Co.", "Champion Base Ball (Japan set 2)", MACHINE_SUPPORTS_SAVE ) | |
| 1204 | GAME( 1983, champbasja, champbas, champbasja, champbas, champbas_state, champbas, ROT0, "Alpha Denshi Co.", "Champion Base Ball (Japan set 2)", MACHINE_SUPPORTS_SAVE ) | |
| 1206 | 1205 | GAME( 1983, champbb2, 0, champbb2, champbas, champbas_state, champbas, ROT0, "Alpha Denshi Co. (Sega license)", "Champion Base Ball Part-2 (set 1)", MACHINE_SUPPORTS_SAVE ) |
| 1207 | 1206 | GAME( 1983, champbb2a, champbb2, champbb2, champbas, champbas_state, champbas, ROT0, "Alpha Denshi Co.", "Champion Base Ball Part-2 (set 2)", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE ) // incomplete dump |
| 1208 | 1207 | GAME( 1983, champbb2j, champbb2, champbb2, champbas, champbas_state, champbas, ROT0, "Alpha Denshi Co.", "Champion Base Ball Part-2 (Japan)", MACHINE_SUPPORTS_SAVE ) |
| r250109 | r250110 | |
| 1212 | 1211 | GAME( 1983, exctsccra, exctsccr, exctsccr, exctsccr, champbas_state, exctsccr, ROT270, "Alpha Denshi Co.", "Exciting Soccer (alternate music)", MACHINE_SUPPORTS_SAVE ) |
| 1213 | 1212 | GAME( 1983, exctsccrj, exctsccr, exctsccr, exctsccr, champbas_state, exctsccr, ROT270, "Alpha Denshi Co.", "Exciting Soccer (Japan)", MACHINE_SUPPORTS_SAVE ) |
| 1214 | 1213 | GAME( 1983, exctsccrjo, exctsccr, exctsccr, exctsccr, champbas_state, exctsccr, ROT270, "Alpha Denshi Co.", "Exciting Soccer (Japan, older)", MACHINE_SUPPORTS_SAVE ) |
| 1215 | GAME( 1983, exctsccrb, exctsccr, exctsccrb, exctsccr, champbas_state, exctsccr, ROT270, "bootleg (Kazutomi)", "Exciting Soccer (bootleg)", MACHINE_SUPPORTS_SAVE ) | |
| 1214 | GAME( 1983, exctsccrb, exctsccr, exctsccrb, exctsccr, champbas_state, exctsccr, ROT270, "bootleg (Kazutomi)", "Exciting Soccer (bootleg)", MACHINE_SUPPORTS_SAVE ) | |
| 1216 | 1215 | GAME( 1984, exctscc2, 0, exctsccr, exctsccr, champbas_state, exctsccr, ROT270, "Alpha Denshi Co.", "Exciting Soccer II", MACHINE_SUPPORTS_SAVE ) |
| r250109 | r250110 | |
|---|---|---|
| 19 | 19 | public: |
| 20 | 20 | dorachan_state(const machine_config &mconfig, device_type type, const char *tag) |
| 21 | 21 | : driver_device(mconfig, type, tag), |
| 22 | m_videoram(*this, "videoram"), | |
| 22 | 23 | m_maincpu(*this, "maincpu"), |
| 23 | 24 | m_screen(*this, "screen"), |
| 24 | 25 | m_palette(*this, "palette"), |
| 25 | m_videoram(*this, "videoram"), | |
| 26 | m_colors(*this, "colors") | |
| 27 | { } | |
| 26 | m_colors(*this, "colors") { } | |
| 28 | 27 | |
| 29 | // devices, memory pointers | |
| 28 | /* memory pointers */ | |
| 29 | required_shared_ptr<UINT8> m_videoram; | |
| 30 | ||
| 31 | /* video-related */ | |
| 32 | UINT8 m_flip_screen; | |
| 33 | ||
| 34 | /* devices */ | |
| 35 | DECLARE_WRITE8_MEMBER(dorachan_ctrl_w); | |
| 36 | DECLARE_CUSTOM_INPUT_MEMBER(dorachan_protection_r); | |
| 37 | DECLARE_CUSTOM_INPUT_MEMBER(dorachan_v128_r); | |
| 38 | virtual void machine_start(); | |
| 39 | virtual void machine_reset(); | |
| 40 | UINT32 screen_update_dorachan(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect); | |
| 30 | 41 | required_device<cpu_device> m_maincpu; |
| 31 | 42 | required_device<screen_device> m_screen; |
| 32 | 43 | required_device<palette_device> m_palette; |
| 33 | ||
| 34 | required_shared_ptr<UINT8> m_videoram; | |
| 35 | 44 | required_region_ptr<UINT8> m_colors; |
| 45 | }; | |
| 36 | 46 | |
| 37 | // internal state | |
| 38 | UINT8 m_flip_screen; | |
| 39 | 47 | |
| 40 | DECLARE_WRITE8_MEMBER(control_w); | |
| 41 | DECLARE_READ8_MEMBER(protection_r); | |
| 42 | DECLARE_READ8_MEMBER(v128_r); | |
| 43 | UINT32 screen_update_dorachan(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect); | |
| 48 | /************************************* | |
| 49 | * | |
| 50 | * Protection handling | |
| 51 | * | |
| 52 | *************************************/ | |
| 44 | 53 | |
| 45 | virtual void machine_start(); | |
| 46 | virtual void machine_reset(); | |
| 47 | }; | |
| 54 | CUSTOM_INPUT_MEMBER(dorachan_state::dorachan_protection_r) | |
| 55 | { | |
| 56 | UINT8 ret = 0; | |
| 48 | 57 | |
| 58 | switch (m_maincpu->pcbase()) | |
| 59 | { | |
| 60 | case 0x70ce: ret = 0xf2; break; | |
| 61 | case 0x72a2: ret = 0xd5; break; | |
| 62 | case 0x72b5: ret = 0xcb; break; | |
| 49 | 63 | |
| 64 | default: | |
| 65 | osd_printf_debug("unhandled $2400 read @ %x\n", m_maincpu->pcbase()); | |
| 66 | break; | |
| 67 | } | |
| 50 | 68 | |
| 69 | return ret; | |
| 70 | } | |
| 71 | ||
| 72 | ||
| 73 | ||
| 51 | 74 | /************************************* |
| 52 | 75 | * |
| 53 | 76 | * Video system |
| r250109 | r250110 | |
| 87 | 110 | } |
| 88 | 111 | |
| 89 | 112 | |
| 90 | ||
| 91 | /************************************* | |
| 92 | * | |
| 93 | * I/O handlers | |
| 94 | * | |
| 95 | *************************************/ | |
| 96 | ||
| 97 | READ8_MEMBER(dorachan_state::protection_r) | |
| 113 | WRITE8_MEMBER(dorachan_state::dorachan_ctrl_w) | |
| 98 | 114 | { |
| 99 | UINT8 ret = 0; | |
| 100 | ||
| 101 | switch (m_maincpu->pcbase()) | |
| 102 | { | |
| 103 | case 0x70ce: ret = 0xf2; break; | |
| 104 | case 0x72a2: ret = 0xd5; break; | |
| 105 | case 0x72b5: ret = 0xcb; break; | |
| 106 | ||
| 107 | default: | |
| 108 | osd_printf_debug("unhandled $2400 read @ %x\n", m_maincpu->pcbase()); | |
| 109 | break; | |
| 110 | } | |
| 111 | ||
| 112 | return ret; | |
| 115 | m_flip_screen = (data >> 6) & 0x01; | |
| 113 | 116 | } |
| 114 | 117 | |
| 115 | READ8_MEMBER(dorachan_state::v128_r) | |
| 116 | { | |
| 117 | // to avoid resetting (when player 2 starts) bit 0 need to be inverted when screen is flipped | |
| 118 | return 0xfe | ((m_screen->vpos() >> 7 & 1) ^ m_flip_screen); | |
| 119 | } | |
| 120 | 118 | |
| 121 | ||
| 119 | CUSTOM_INPUT_MEMBER(dorachan_state::dorachan_v128_r) | |
| 122 | 120 | { |
| 123 | // d6: flip screen | |
| 124 | // other: ? | |
| 125 | m_flip_screen = data >> 6 & 1; | |
| 121 | /* to avoid resetting (when player 2 starts) bit 0 need to be inverted when screen is flipped */ | |
| 122 | return ((m_screen->vpos() >> 7) & 0x01) ^ m_flip_screen; | |
| 126 | 123 | } |
| 127 | 124 | |
| 128 | 125 | |
| 126 | ||
| 127 | /************************************* | |
| 128 | * | |
| 129 | * Memory handlers | |
| 130 | * | |
| 131 | *************************************/ | |
| 132 | ||
| 129 | 133 | static ADDRESS_MAP_START( dorachan_map, AS_PROGRAM, 8, dorachan_state ) |
| 130 | 134 | AM_RANGE(0x0000, 0x17ff) AM_ROM |
| 131 | 135 | AM_RANGE(0x1800, 0x1fff) AM_RAM |
| 132 | 136 | AM_RANGE(0x2000, 0x23ff) AM_ROM |
| 133 | AM_RANGE(0x2400, 0x2400) AM_MIRROR(0x03ff) AM_READ(protection_r) | |
| 134 | AM_RANGE(0x2800, 0x2800) AM_MIRROR(0x03ff) AM_READ_PORT("IN0") | |
| 135 | AM_RANGE(0x2c00, 0x2c00) AM_MIRROR(0x03ff) AM_READ_PORT("IN1") | |
| 136 | AM_RANGE(0x3800, 0x3800) AM_MIRROR(0x03ff) AM_READ(v128_r) | |
| 137 | AM_RANGE(0x2400, 0x2400) AM_MIRROR(0x03ff) AM_READ_PORT("PROT") | |
| 138 | AM_RANGE(0x2800, 0x2800) AM_MIRROR(0x03ff) AM_READ_PORT("SYSTEM") | |
| 139 | AM_RANGE(0x2c00, 0x2c00) AM_MIRROR(0x03ff) AM_READ_PORT("JOY") | |
| 140 | AM_RANGE(0x3800, 0x3800) AM_MIRROR(0x03ff) AM_READ_PORT("V128") | |
| 137 | 141 | AM_RANGE(0x4000, 0x5fff) AM_RAM AM_SHARE("videoram") |
| 138 | 142 | AM_RANGE(0x6000, 0x77ff) AM_ROM |
| 139 | 143 | ADDRESS_MAP_END |
| 140 | 144 | |
| 145 | ||
| 146 | ||
| 147 | /************************************* | |
| 148 | * | |
| 149 | * Port handlers | |
| 150 | * | |
| 151 | *************************************/ | |
| 152 | ||
| 141 | 153 | static ADDRESS_MAP_START( dorachan_io_map, AS_IO, 8, dorachan_state ) |
| 142 | 154 | ADDRESS_MAP_GLOBAL_MASK(0xff) |
| 143 | 155 | AM_RANGE(0x01, 0x01) AM_WRITENOP |
| 144 | 156 | AM_RANGE(0x02, 0x02) AM_WRITENOP |
| 145 | AM_RANGE(0x03, 0x03) AM_WRITE( | |
| 157 | AM_RANGE(0x03, 0x03) AM_WRITE(dorachan_ctrl_w) | |
| 146 | 158 | ADDRESS_MAP_END |
| 147 | 159 | |
| 148 | 160 | |
| r250109 | r250110 | |
| 154 | 166 | *************************************/ |
| 155 | 167 | |
| 156 | 168 | static INPUT_PORTS_START( dorachan ) |
| 157 | PORT_START("IN0") | |
| 169 | PORT_START("PROT") | |
| 170 | PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_SPECIAL ) PORT_CUSTOM_MEMBER(DEVICE_SELF, dorachan_state,dorachan_protection_r, NULL) | |
| 171 | ||
| 172 | PORT_START("SYSTEM") | |
| 158 | 173 | PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_COIN1 ) |
| 159 | 174 | PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_START1 ) |
| 160 | 175 | PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_START2 ) |
| r250109 | r250110 | |
| 170 | 185 | PORT_DIPSETTING( 0x80, DEF_STR( Off ) ) |
| 171 | 186 | PORT_DIPSETTING( 0x00, DEF_STR( On ) ) |
| 172 | 187 | |
| 173 | PORT_START(" | |
| 188 | PORT_START("JOY") | |
| 174 | 189 | PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_COCKTAIL |
| 175 | 190 | PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_COCKTAIL |
| 176 | 191 | PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_COCKTAIL |
| r250109 | r250110 | |
| 179 | 194 | PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) |
| 180 | 195 | PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) |
| 181 | 196 | PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) |
| 197 | ||
| 198 | PORT_START("V128") | |
| 199 | PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_SPECIAL ) PORT_CUSTOM_MEMBER(DEVICE_SELF, dorachan_state,dorachan_v128_r, NULL) | |
| 200 | PORT_BIT( 0xfe, IP_ACTIVE_LOW, IPT_UNUSED ) | |
| 182 | 201 | INPUT_PORTS_END |
| 183 | 202 | |
| 184 | 203 | |
| r250109 | r250110 | |
| 200 | 219 | } |
| 201 | 220 | |
| 202 | 221 | static MACHINE_CONFIG_START( dorachan, dorachan_state ) |
| 203 | ||
| 204 | 222 | /* basic machine hardware */ |
| 205 | 223 | MCFG_CPU_ADD("maincpu", Z80, 2000000) |
| 206 | 224 | MCFG_CPU_PROGRAM_MAP(dorachan_map) |
| r250109 | r250110 | |
| 210 | 228 | /* video hardware */ |
| 211 | 229 | MCFG_SCREEN_ADD("screen", RASTER) |
| 212 | 230 | MCFG_SCREEN_SIZE(32*8, 32*8) |
| 213 | MCFG_SCREEN_VISIBLE_AREA( | |
| 231 | MCFG_SCREEN_VISIBLE_AREA(1*8, 31*8-1, 1*8, 31*8-1) | |
| 214 | 232 | MCFG_SCREEN_REFRESH_RATE(60) |
| 215 | 233 | MCFG_SCREEN_UPDATE_DRIVER(dorachan_state, screen_update_dorachan) |
| 216 | 234 | |
| r250109 | r250110 | |
| 253 | 271 | * |
| 254 | 272 | *************************************/ |
| 255 | 273 | |
| 256 | GAME( 1980, dorachan, 0, dorachan, dorachan, driver_device, 0, ROT270, " | |
| 274 | GAME( 1980, dorachan, 0, dorachan, dorachan, driver_device, 0, ROT270, "Craul Denshi", "Dorachan", MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE ) |
| r250109 | r250110 | |
|---|---|---|
| 2 | 2 | // copyright-holders:Acho A. Tang, Nicola Salmoria |
| 3 | 3 | /******************************************************************************* |
| 4 | 4 | |
| 5 | Equites (c) 1984 Alpha Denshi Co./Sega | |
| 6 | Bull Fighter (c) 1984 Alpha Denshi Co./Sega | |
| 7 | Gekisou (c) 1985 Eastern Corp. | |
| 8 | The Koukouyakyuh (c) 1985 Alpha Denshi Co. | |
| 9 | Splendor Blast (c) 1985 Alpha Denshi Co. | |
| 10 | High Voltage (c) 1985 Alpha Denshi Co. | |
| 5 | Equites (c) 1984 Alpha Denshi Co./Sega 8303 | |
| 6 | Bull Fighter (c) 1984 Alpha Denshi Co./Sega 8303 | |
| 7 | Gekisou (c) 1985 Eastern Corp. 8304 | |
| 8 | Violent Run (c) 1985 Eastern Corp. 8304? (probably on Equites HW) | |
| 9 | The Koukouyakyuh (c) 1985 Alpha Denshi Co. 8304 | |
| 10 | Splendor Blast (c) 1985 Alpha Denshi Co. 8303 | |
| 11 | High Voltage (c) 1985 Alpha Denshi Co. 8304 (POST says 8404) | |
| 11 | 12 | |
| 12 | The following are not dumped yet: | |
| 13 | Champion Croquet (c) 1984 Alpha Denshi Co. (sports) | |
| 14 | Violent Run (c) 1985 Eastern Corp. (export version of Gekisou) | |
| 15 | Tune Pit(?) (c) 1985 Alpha Denshi Co. | |
| 16 | Perfect Janputer (c) 1984 Alpha Denshi Co. (4-player Mahjong) | |
| 17 | ||
| 18 | ||
| 19 | 13 | Driver by Acho A. Tang, Nicola Salmoria |
| 20 | 14 | Many thanks to Corrado Tomaselli for precious hardware info. |
| 21 | 15 | |
| r250109 | r250110 | |
| 182 | 176 | Yasuhiro Ogawa for the correct Equites ROM information |
| 183 | 177 | |
| 184 | 178 | |
| 179 | Other unemulated Alpha Denshi and SNK games that may use similar hardware: | |
| 180 | ----------------------------------------------------------- | |
| 181 | Maker Year Genre Name Japanese Name | |
| 182 | ----------------------------------------------------------- | |
| 183 | Alpha Denshi 1984 (SPT) Champion Croquet ?`?????s?I???N???b?P?[ | |
| 184 | Alpha Denshi 1985 (???) Tune Pit(?) ?`?F?[???s?b?g | |
| 185 | Alpha Denshi 1985 (MAJ) Perfect Janputer ?p?[?t?F?N?g?W?????s???[?^?[ | |
| 186 | ||
| 187 | ||
| 185 | 188 | ******************************************************************************* |
| 186 | 189 | |
| 187 | 190 | Bull Fighter |
| r250109 | r250110 | |
| 258 | 261 | -----------------------------------------------------------------------| |
| 259 | 262 | |
| 260 | 263 | |
| 264 | ||
| 265 | ||
| 261 | 266 | -----------------------------------------------------------------------------------| |
| 262 | 267 | |ALPHA DENSHI CO, LTD. MAIN CONNECTOR BOARD | |
| 263 | 268 | | | |
| r250109 | r250110 | |
| 309 | 314 | |----------------------------------------------------------------------------------| |
| 310 | 315 | |
| 311 | 316 | |
| 317 | ||
| 318 | ||
| 312 | 319 | -------------------------------------------------------------------------------------| |
| 313 | 320 | |ALPHA DENSHI CO, LTD. LOWER BOARD | |
| 314 | 321 | | | |
| r250109 | r250110 | |
| 357 | 364 | |
| 358 | 365 | |
| 359 | 366 | *******************************************************************************/ |
| 367 | // Directives | |
| 360 | 368 | |
| 361 | 369 | #include "emu.h" |
| 362 | 370 | #include "cpu/m68000/m68000.h" |
| r250109 | r250110 | |
| 1901 | 1909 | GAME( 1984, equitess, equites, equites, equites, equites_state, equites, ROT90, "Alpha Denshi Co. (Sega license)", "Equites (Sega)", MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE ) |
| 1902 | 1910 | GAME( 1984, bullfgtr, 0, equites, bullfgtr, equites_state, bullfgtr, ROT90, "Alpha Denshi Co.", "Bull Fighter", MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE ) |
| 1903 | 1911 | GAME( 1984, bullfgtrs,bullfgtr, equites, bullfgtr, equites_state, bullfgtr, ROT90, "Alpha Denshi Co. (Sega license)", "Bull Fighter (Sega)", MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE ) |
| 1904 | GAME( 1985, kouyakyu, 0, equites, kouyakyu, equites_state, kouyakyu, ROT0, "Alpha Denshi Co.", "The Koukou | |
| 1912 | GAME( 1985, kouyakyu, 0, equites, kouyakyu, equites_state, kouyakyu, ROT0, "Alpha Denshi Co.", "The Koukouyakyuh", MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE ) | |
| 1905 | 1913 | GAME( 1985, gekisou, 0, gekisou, gekisou, equites_state, gekisou, ROT90, "Eastern Corp.", "Gekisou (Japan)", MACHINE_UNEMULATED_PROTECTION | MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS | MACHINE_SUPPORTS_SAVE ) |
| 1906 | 1914 | |
| 1907 | 1915 | // Splendor Blast Hardware |
| r250109 | r250110 | |
|---|---|---|
| 464 | 464 | the driver init till we can get more evidence about. |
| 465 | 465 | |
| 466 | 466 | |
| 467 | * Super 98 | |
| 468 | ||
| 469 | This game looks like a Golden Poker / Potten's Poker set, but could be set to play | |
| 470 | 2 or 3 deals per hand. It's running in a ICP-1 PCB. | |
| 471 | ||
| 472 | Entering the service mode (key 0), you can enter to a submenu for settings pressing | |
| 473 | DEAL (key 2). Use HOLD keys (keys ZXCVB) to navigate through the menu and change | |
| 474 | the values. Press CANCEL to exit the settings menu. | |
| 475 | ||
| 476 | Program is currently not working because seems to fill some zeropage registers and | |
| 477 | check for existent values and changes... Maybe an external device is writting them. | |
| 478 | This is NVRAM zone, so some values could be previously harcoded. | |
| 479 | ||
| 480 | Also seems to expect some inputs combination entered to boot. | |
| 481 | ||
| 482 | To run... | |
| 483 | 1) Start the game. | |
| 484 | 2) Break into debugger and do a pc=cfa1 | |
| 485 | ||
| 486 | Debug notes... | |
| 487 | ||
| 488 | From interrupts routine: | |
| 489 | ||
| 490 | CF99: LDA $0846 ; load from PIA | |
| 491 | CF9C: TSX ; transfer stack pointer to X | |
| 492 | CF9D: CPX #$C8 ; compare with 0xC8 | |
| 493 | CF9F: BCS $CFA4 ; not?... branch to $CFA4 | |
| 494 | CFA1: JMP $CEC6 ; yes?... jump to $CEC6 | |
| 495 | CFA4: JSR $C0E1 ; continue... | |
| 496 | ... | |
| 497 | ||
| 498 | Forcing the first time the comparation at $CF9D --> true, the game boots and is | |
| 499 | fully working. | |
| 500 | ||
| 501 | ||
| 502 | 467 | ************************************************************************************ |
| 503 | 468 | |
| 504 | 469 | |
| r250109 | r250110 | |
| 1034 | 999 | properly. Now you can choose the program through a DIP switch. |
| 1035 | 1000 | |
| 1036 | 1001 | |
| 1037 | [2015-11-04] | |
| 1038 | ||
| 1039 | - Added new sets: | |
| 1040 | * Genie (ICP-1, set 2). | |
| 1041 | * Super 98 (ICP-1). | |
| 1042 | * Jack Potten's Poker (set 8, Australian). | |
| 1043 | ||
| 1044 | - Derived a new machine with improved memory map for this new Genie set. | |
| 1045 | - Minor fixes and clean-ups. | |
| 1046 | - Added games & technical notes. | |
| 1047 | ||
| 1048 | ||
| 1049 | 1002 | TODO: |
| 1050 | 1003 | |
| 1051 | 1004 | - Missing PIA connections. |
| r250109 | r250110 | |
| 10002 | 9955 | Super 98', |
| 10003 | 9956 | running in the ICP-1 boardset. |
| 10004 | 9957 | |
| 10005 | Please read the 'Games Notes' section | |
| 10006 | for game and debug notes / issues... | |
| 9958 | Program seems to fill some zeropage registers | |
| 9959 | and check for existent values and changes... | |
| 9960 | Maybe an external device is writting them. | |
| 9961 | This is NVRAM zone, so some values could be | |
| 9962 | previously harcoded. | |
| 9963 | ||
| 9964 | Also seems to expect some inputs combination entered to boot. | |
| 9965 | ||
| 9966 | To run... | |
| 9967 | 1) Start the game. | |
| 9968 | 2) Break into debugger and do a pc=cfa1 | |
| 10007 | 9969 | */ |
| 10008 | 9970 | |
| 10009 | 9971 | ROM_START( super98 ) |
| r250109 | r250110 | |
|---|---|---|
| 59 | 59 | * D000-DFFF videoram or RAM |
| 60 | 60 | * E000-FFFF memory mapped IO or RAM |
| 61 | 61 | * |
| 62 | * ToDo: | |
| 63 | - slows down while making sound | |
| 64 | - MZ800: | |
| 65 | - Had to patch the rom to load cassettes | |
| 66 | - Port CF not done. | |
| 67 | - Dips not connected. | |
| 68 | - MZ800-mode display not working /Hi-res not coded. | |
| 69 | - The CRTC is a very complex custom device, mostly unemulated. | |
| 70 | - MZ1500: | |
| 71 | - Various ports not done. | |
| 72 | - Floppy disk and quick disk not done. | |
| 73 | - F4 display is blank. | |
| 74 | - Need manuals. | |
| 75 | ||
| 76 | Note: MZ800 hardware starts in memory map (mode A), but switches to MZ700 | |
| 77 | compatibility mode (mode B) as soon as it starts up. We start in Mode B | |
| 78 | because it helps MZ1500 get started and it doesn't break anything. | |
| 79 | ||
| 80 | * | |
| 81 | 62 | *****************************************************************************/ |
| 82 | 63 | |
| 83 | 64 | #include "emu.h" |
| r250109 | r250110 | |
| 116 | 97 | ***************************************************************************/ |
| 117 | 98 | |
| 118 | 99 | static ADDRESS_MAP_START( mz700_mem, AS_PROGRAM, 8, mz_state ) |
| 119 | AM_RANGE(0x0000, 0x0fff) AM_READ_BANK("bankr0") AM_WRITE_BANK("bankw0") | |
| 120 | AM_RANGE(0x1000, 0xcfff) AM_RAM | |
| 121 | AM_RANGE(0xd000, 0xdfff) AM_RAMBANK("bankd") | |
| 122 | AM_RANGE(0xe000, 0xffff) AM_DEVICE("banke", address_map_bank_device, amap8) | |
| 123 | 100 | ADDRESS_MAP_END |
| 124 | 101 | |
| 125 | static ADDRESS_MAP_START( mz700_banke, AS_PROGRAM, 8, mz_state ) | |
| 126 | // bank 0: ram (mz700_bank1) | |
| 127 | AM_RANGE(0x0000, 0x1fff) AM_RAM | |
| 128 | // bank 1: devices (mz700_bank3) | |
| 129 | AM_RANGE(0x2000, 0x2003) AM_MIRROR(0x1ff0) AM_DEVREADWRITE("ppi8255", i8255_device, read, write) | |
| 130 | AM_RANGE(0x2004, 0x2007) AM_MIRROR(0x1ff0) AM_DEVREADWRITE("pit8253", pit8253_device, read, write) | |
| 131 | AM_RANGE(0x2008, 0x200b) AM_MIRROR(0x1ff0) AM_READWRITE(mz700_e008_r,mz700_e008_w) | |
| 132 | AM_RANGE(0x200c, 0x200f) AM_MIRROR(0x1ff0) AM_NOP | |
| 133 | // bank 2: switched out (mz700_bank5) | |
| 134 | AM_RANGE(0x4000, 0x5fff) AM_NOP | |
| 135 | ADDRESS_MAP_END | |
| 136 | ||
| 137 | 102 | static ADDRESS_MAP_START( mz700_io, AS_IO, 8, mz_state ) |
| 138 | 103 | ADDRESS_MAP_GLOBAL_MASK(0xff) |
| 139 | 104 | AM_RANGE(0xe0, 0xe0) AM_WRITE(mz700_bank_0_w) |
| r250109 | r250110 | |
| 146 | 111 | ADDRESS_MAP_END |
| 147 | 112 | |
| 148 | 113 | static ADDRESS_MAP_START( mz800_mem, AS_PROGRAM, 8, mz_state ) |
| 149 | AM_RANGE(0x0000, 0x0fff) AM_READ_BANK("bankr0") AM_WRITE_BANK("bankw0") | |
| 150 | AM_RANGE(0x1000, 0x1fff) AM_RAMBANK("bank1") | |
| 151 | AM_RANGE(0x2000, 0x7fff) AM_RAM | |
| 152 | AM_RANGE(0x8000, 0xbfff) AM_RAMBANK("banka") | |
| 153 | AM_RANGE(0xc000, 0xcfff) AM_RAMBANK("bankc") | |
| 154 | AM_RANGE(0xd000, 0xdfff) AM_RAMBANK("bankd") | |
| 155 | AM_RANGE(0xe000, 0xffff) AM_DEVICE("bankf", address_map_bank_device, amap8) | |
| 156 | 114 | ADDRESS_MAP_END |
| 157 | 115 | |
| 158 | static ADDRESS_MAP_START( mz800_bankf, AS_PROGRAM, 8, mz_state ) | |
| 159 | // bank 0: ram (mz700_bank1) | |
| 160 | AM_RANGE(0x0000, 0x1fff) AM_RAM | |
| 161 | // bank 1: devices (mz700_bank3) | |
| 162 | AM_RANGE(0x2000, 0x2003) AM_DEVREADWRITE("ppi8255", i8255_device, read, write) | |
| 163 | AM_RANGE(0x2004, 0x2007) AM_DEVREADWRITE("pit8253", pit8253_device, read, write) | |
| 164 | AM_RANGE(0x2008, 0x200b) AM_READWRITE(mz700_e008_r,mz700_e008_w) | |
| 165 | AM_RANGE(0x200c, 0x200f) AM_NOP | |
| 166 | AM_RANGE(0x2010, 0x3fff) AM_ROM AM_REGION("monitor", 0x2010) | |
| 167 | // bank 2: switched out (mz700_bank5) | |
| 168 | AM_RANGE(0x4000, 0x5fff) AM_NOP | |
| 169 | ADDRESS_MAP_END | |
| 170 | ||
| 171 | 116 | static ADDRESS_MAP_START( mz800_io, AS_IO, 8, mz_state ) |
| 172 | 117 | ADDRESS_MAP_GLOBAL_MASK(0xff) |
| 173 | 118 | AM_RANGE(0xcc, 0xcc) AM_WRITE(mz800_write_format_w ) |
| r250109 | r250110 | |
| 358 | 303 | }; |
| 359 | 304 | |
| 360 | 305 | static GFXDECODE_START( mz700 ) |
| 361 | GFXDECODE_ENTRY("cgrom", 0, mz700_layout, 0, | |
| 306 | GFXDECODE_ENTRY("cgrom", 0, mz700_layout, 0, 256) | |
| 362 | 307 | GFXDECODE_END |
| 363 | 308 | |
| 364 | 309 | static GFXDECODE_START( mz800 ) |
| 365 | GFXDECODE_ENTRY("monitor", 0x1000, mz700_layout, 0, 4) // for mz800 viewer only | |
| 310 | GFXDECODE_ENTRY(NULL, 0, mz700_layout, 0, 256) | |
| 311 | GFXDECODE_ENTRY("monitor", 0x1000, mz700_layout, 0, 256) // for mz800 viewer only | |
| 366 | 312 | GFXDECODE_END |
| 367 | 313 | |
| 368 | 314 | |
| r250109 | r250110 | |
| 375 | 321 | MCFG_CPU_ADD("maincpu", Z80, XTAL_17_73447MHz/5) |
| 376 | 322 | MCFG_CPU_PROGRAM_MAP(mz700_mem) |
| 377 | 323 | MCFG_CPU_IO_MAP(mz700_io) |
| 378 | MCFG_DEVICE_ADD("banke", ADDRESS_MAP_BANK, 0) | |
| 379 | MCFG_DEVICE_PROGRAM_MAP(mz700_banke) | |
| 380 | MCFG_ADDRESS_MAP_BANK_ENDIANNESS(ENDIANNESS_LITTLE) | |
| 381 | MCFG_ADDRESS_MAP_BANK_DATABUS_WIDTH(8) | |
| 382 | MCFG_ADDRESS_MAP_BANK_ADDRBUS_WIDTH(16) | |
| 383 | MCFG_ADDRESS_MAP_BANK_STRIDE(0x2000) | |
| 384 | 324 | |
| 385 | MCFG_MACHINE_RESET_OVERRIDE(mz_state, mz700) | |
| 386 | 325 | |
| 387 | 326 | /* video hardware */ |
| 388 | 327 | MCFG_SCREEN_ADD("screen", RASTER) |
| 389 | 328 | MCFG_SCREEN_RAW_PARAMS(XTAL_17_73447MHz/2, 568, 0, 40*8, 312, 0, 25*8) |
| 390 | 329 | MCFG_SCREEN_UPDATE_DRIVER(mz_state, screen_update_mz700) |
| 391 | 330 | MCFG_SCREEN_PALETTE("palette") |
| 392 | MCFG_PALETTE_ADD_3BIT_RGB("palette") | |
| 393 | 331 | |
| 394 | 332 | MCFG_GFXDECODE_ADD("gfxdecode", "palette", mz700) |
| 333 | MCFG_PALETTE_ADD("palette", 256*2) | |
| 334 | MCFG_PALETTE_INDIRECT_ENTRIES(8) | |
| 335 | MCFG_PALETTE_INIT_OWNER(mz_state, mz) | |
| 395 | 336 | |
| 396 | 337 | /* sound hardware */ |
| 397 | 338 | MCFG_SPEAKER_STANDARD_MONO("mono") |
| 398 | 339 | MCFG_SOUND_WAVE_ADD(WAVE_TAG, "cassette") |
| 399 | MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0. | |
| 340 | MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.25) | |
| 400 | 341 | MCFG_SOUND_ADD("speaker", SPEAKER_SOUND, 0) |
| 401 | 342 | MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.50) |
| 402 | 343 | |
| r250109 | r250110 | |
| 435 | 376 | |
| 436 | 377 | |
| 437 | 378 | static MACHINE_CONFIG_DERIVED( mz800, mz700 ) |
| 438 | MCFG_DEVICE_REMOVE("banke") | |
| 439 | 379 | |
| 440 | 380 | /* basic machine hardware */ |
| 441 | 381 | MCFG_CPU_MODIFY("maincpu") |
| 442 | 382 | MCFG_CPU_PROGRAM_MAP(mz800_mem) |
| 443 | 383 | MCFG_CPU_IO_MAP(mz800_io) |
| 444 | MCFG_DEVICE_ADD("bankf", ADDRESS_MAP_BANK, 0) | |
| 445 | MCFG_DEVICE_PROGRAM_MAP(mz800_bankf) | |
| 446 | MCFG_ADDRESS_MAP_BANK_ENDIANNESS(ENDIANNESS_LITTLE) | |
| 447 | MCFG_ADDRESS_MAP_BANK_DATABUS_WIDTH(8) | |
| 448 | MCFG_ADDRESS_MAP_BANK_ADDRBUS_WIDTH(16) | |
| 449 | MCFG_ADDRESS_MAP_BANK_STRIDE(0x2000) | |
| 450 | 384 | |
| 451 | MCFG_MACHINE_RESET_OVERRIDE(mz_state, mz800) | |
| 452 | 385 | MCFG_GFXDECODE_MODIFY("gfxdecode",mz800) |
| 453 | 386 | |
| 387 | MCFG_VIDEO_START_OVERRIDE(mz_state,mz800) | |
| 388 | ||
| 454 | 389 | MCFG_SCREEN_MODIFY("screen") |
| 455 | 390 | MCFG_SCREEN_UPDATE_DRIVER(mz_state, screen_update_mz800) |
| 456 | 391 | |
| r250109 | r250110 | |
| 500 | 435 | ROM_START( mz800 ) |
| 501 | 436 | ROM_REGION( 0x4000, "monitor", 0 ) |
| 502 | 437 | ROM_LOAD( "mz800.rom", 0x0000, 0x4000, CRC(600d17e1) SHA1(950ce4b51429916f8036e41ba6130fac149b36e4) ) |
| 503 | // fix cassette loading | |
| 504 | ROM_FILL(0x761,1,0x13) | |
| 505 | ROM_FILL(0xA4B,1,0x45) | |
| 506 | ||
| 507 | ROM_REGION( 0x10000, "user1", ROMREGION_ERASE00 ) // ramdisk | |
| 508 | 438 | ROM_END |
| 509 | 439 | |
| 510 | 440 | ROM_START( mz1500 ) |
| 511 | 441 | ROM_REGION( 0x4000, "monitor", 0 ) |
| 512 | ROM_LOAD( "9z-502m.rom", 0x0000, 0x1000, CRC(643db428) SHA1(c2ad8af2ef00db32afde54d5741b07de5d4da16a)) | |
| 513 | ROM_CONTINUE(0x2800, 0x1800) | |
| 514 | ||
| 442 | ROM_LOAD( "9z-502m.rom", 0x0000, 0x2800, CRC(643db428) SHA1(c2ad8af2ef00db32afde54d5741b07de5d4da16a)) | |
| 515 | 443 | ROM_REGION( 0x1000, "cgrom", 0 ) |
| 516 | 444 | //ROM_LOAD( "mz700fon.jp", 0x0000, 0x1000, CRC(697ec121) SHA1(5eb1d42d273b1fd2cab120486279ab8ff6c85dc7)) |
| 517 | 445 | ROM_LOAD( "mz700fon.jpn", 0x0000, 0x1000, CRC(425eedf5) SHA1(bd2cc750f2d2f63e50a59786668509e81a276e32) ) |
| 518 | ||
| 519 | ROM_REGION( 0x10000, "user1", ROMREGION_ERASE00 ) // ramdisk | |
| 520 | 446 | ROM_END |
| 521 | 447 | |
| 522 | 448 | /*************************************************************************** |
| r250109 | r250110 | |
|---|---|---|
| 6912 | 6912 | GAME( 1981, crush4, crush, crush4, crush4, driver_device, 0, ROT90, "Alpha Denshi Co. / Kural TWT", "Crush Roller (set 4)", MACHINE_SUPPORTS_SAVE ) |
| 6913 | 6913 | GAME( 1981, maketrax, crush, pacmanp, maketrax, pacman_state, maketrax, ROT270, "Alpha Denshi Co. / Kural (Williams license)", "Make Trax (US set 1)", MACHINE_SUPPORTS_SAVE ) |
| 6914 | 6914 | GAME( 1981, maketrxb, crush, pacmanp, maketrax, pacman_state, maketrax, ROT270, "Alpha Denshi Co. / Kural (Williams license)", "Make Trax (US set 2)", MACHINE_SUPPORTS_SAVE ) |
| 6915 | GAME( 1981, korosuke, crush, pacmanp, korosuke, pacman_state, korosuke, ROT90, "Alpha Denshi Co. / Kural Electric, Ltd.", "Korosuke Roller (Japan)", MACHINE_SUPPORTS_SAVE ) | |
| 6915 | GAME( 1981, korosuke, crush, pacmanp, korosuke, pacman_state, korosuke, ROT90, "Alpha Denshi Co. / Kural Electric, Ltd.", "Korosuke Roller (Japan)", MACHINE_SUPPORTS_SAVE ) | |
| 6916 | 6916 | GAME( 1981, crushrlf, crush, pacman, maketrax, driver_device, 0, ROT90, "bootleg", "Crush Roller (Famaresa PCB)", MACHINE_SUPPORTS_SAVE ) |
| 6917 | 6917 | GAME( 1981, crushbl, crush, pacman, maketrax, driver_device, 0, ROT90, "bootleg", "Crush Roller (bootleg set 1)", MACHINE_SUPPORTS_SAVE ) |
| 6918 | 6918 | GAME( 1981, crushbl2, crush, pacmanp, mbrush, pacman_state, maketrax, ROT90, "bootleg", "Crush Roller (bootleg set 2)", MACHINE_SUPPORTS_SAVE ) |
| r250109 | r250110 | |
|---|---|---|
| 76 | 76 | |
| 77 | 77 | Uniquely the Poly-Play has a light organ which totally confuses you whilst |
| 78 | 78 | playing the automaton. Bits 1-5 of PORTB control the organ but it's not |
| 79 | emulated now. ;) (An external artwork file could be created that simulates | |
| 80 | this.) | |
| 79 | emulated now. ;) | |
| 81 | 80 | |
| 82 | 81 | ***************************************************************************/ |
| 83 | 82 |
| r250109 | r250110 | |
|---|---|---|
| 3 | 3 | /*************************************************************************** |
| 4 | 4 | |
| 5 | 5 | Pong (c) 1972 Atari |
| 6 | Pong Doubles (c) 1973 Atari | |
| 7 | Breakout (c) 1976 Atari | |
| 8 | 6 | |
| 9 | 7 | driver by Couriersud |
| 10 | 8 | |
| 11 | ||
| 12 | Atari Pong Games List - Data based, in part from: | |
| 13 | ||
| 14 | - "Andy's collection of Bronzeage Atari Video Arcade PCBs" | |
| 15 | http://www.andysarcade.net/personal/bronzeage/index.htm | |
| 16 | ||
| 17 | - "Atari's Technical Manual Log" | |
| 18 | http://www.atarigames.com/manuals.txt | |
| 19 | ||
| 20 | Suspected "same games" are grouped together. These are usually the exact same game but different cabinet/name. | |
| 21 | ||
| 22 | Technical Manual #s Game Name(s) Atari Part #'s Data | |
| 23 | -------------------+----------------------------------------------------------+----------------------------------+---------+ | |
| 24 | TM-058 Breakout/Breakout Cocktail/Consolette (1976) A004533 NO | |
| 25 | TM-015 Cocktail Pong/Coup Franc (1974) A001433? NO | |
| 26 | TM-0?? Dr. Pong/Puppy Pong/Snoopy Pong (1974) A001433? NO | |
| 27 | TM-013 Pong (1972) A001433 NO | |
| 28 | TM-014 Pong Doubles/Coupe Davis (1973) A000785 NO | |
| 29 | 422 Superpong (1974) A000423 NO | |
| 30 | ||
| 31 | 9 | Notes: |
| 32 | 10 | |
| 33 | TODO: Please see netlist include files | |
| 34 | TODO: Breakout Cocktail and Consolette are believed to use the Breakout PCB with different cabinet designs, this needs to be verified. | |
| 35 | TODO: Coupe Davis is believed to use the Pong Doubles PCB, just a different cabinet design, this needs to be verified. | |
| 36 | TODO: Dr. Pong, Puppy Pong, Snoopy Pong, Cocktail Pong and Coup Franc are all believed to use the Pong (Rev E) PCB, but | |
| 37 | different cabinet designs; this needs to be verified. | |
| 38 | TODO: Superpong is believed to use the Pong (Rev E) PCB with some minor modifications, this needs to be verified. | |
| 11 | TODO: please see netlist include files | |
| 39 | 12 | |
| 40 | 13 | ***************************************************************************/ |
| 41 | 14 | |
| r250109 | r250110 | |
| 517 | 490 | ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASE00 ) |
| 518 | 491 | ROM_END |
| 519 | 492 | |
| 520 | /* // 100% TTL - NO ROMS | |
| 521 | ||
| 522 | ROM_START( coupedav ) // dummy to satisfy game entry | |
| 523 | ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASE00 ) | |
| 524 | ROM_END | |
| 525 | ||
| 526 | ROM_START( coupfran ) // dummy to satisfy game entry | |
| 527 | ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASE00 ) | |
| 528 | ROM_END | |
| 529 | ||
| 530 | ROM_START( cktpong ) // dummy to satisfy game entry | |
| 531 | ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASE00 ) | |
| 532 | ROM_END | |
| 533 | ||
| 534 | ROM_START( drpong ) // dummy to satisfy game entry | |
| 535 | ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASE00 ) | |
| 536 | ROM_END | |
| 537 | ||
| 538 | ROM_START( pupppong ) // dummy to satisfy game entry | |
| 539 | ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASE00 ) | |
| 540 | ROM_END | |
| 541 | ||
| 542 | ROM_START( snoopong ) // dummy to satisfy game entry | |
| 543 | ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASE00 ) | |
| 544 | ROM_END | |
| 545 | ||
| 546 | ROM_START( suprpong ) // dummy to satisfy game entry | |
| 547 | ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASE00 ) | |
| 548 | ROM_END | |
| 549 | ||
| 550 | ROM_START( breakckt ) // dummy to satisfy game entry | |
| 551 | ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASE00 ) | |
| 552 | ROM_END | |
| 553 | ||
| 554 | ROM_START( consolet ) // dummy to satisfy game entry | |
| 555 | ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASE00 ) | |
| 556 | ROM_END | |
| 557 | */ | |
| 558 | ||
| 559 | 493 | GAME( 1972, pong, 0, pong, pong, driver_device, 0, ROT0, "Atari", "Pong (Rev E) external [TTL]", MACHINE_SUPPORTS_SAVE) |
| 560 | GAME( 1972, pongf, 0, pongf, pong, driver_device, 0, ROT0, "Atari", "Pong (Rev E) [TTL]", MACHINE_SUPPORTS_SAVE) | |
| 561 | GAME( 1973, pongd, 0, pongd, pongd, driver_device, 0, ROT0, "Atari", "Pong Doubles [TTL]", MACHINE_SUPPORTS_SAVE) | |
| 494 | GAME( 1972, pongf, 0, pongf, pong, driver_device, 0, ROT0, "Atari", "Pong (Rev E) [TTL]", MACHINE_SUPPORTS_SAVE ) | |
| 495 | GAME( 1974, pongd, 0, pongd, pongd, driver_device, 0, ROT0, "Atari", "Pong Doubles [TTL]", MACHINE_SUPPORTS_SAVE ) | |
| 562 | 496 | GAMEL( 1976, breakout, 0, breakout, breakout, driver_device, 0, ROT90, "Atari", "Breakout [TTL]", MACHINE_SUPPORTS_SAVE, layout_breakout) |
| 563 | ||
| 564 | // 100% TTL | |
| 565 | //GAME( 1973, coupedav, pongd, pongd, pongd, driver_device, 0, ROT0, "Atari France", "Coupe Davis [TTL]", MACHINE_SUPPORTS_SAVE) | |
| 566 | //GAME( 1974, coupfran, pong, pong, pong, driver_device, 0, ROT0, "Atari Europe", "Coup Franc [TTL]", MACHINE_SUPPORTS_SAVE) | |
| 567 | //GAME( 1974, cktpong, pong, pong, pong, driver_device, 0, ROT0, "Atari / National Entertainment Co.", "Cocktail Pong [TTL]", MACHINE_SUPPORTS_SAVE) | |
| 568 | //GAME( 1974, drpong, pong, pong, pong, driver_device, 0, ROT0, "Atari", "Dr. Pong [TTL]", MACHINE_SUPPORTS_SAVE) | |
| 569 | //GAME( 1974, pupppong, pong, pong, pong, driver_device, 0, ROT0, "Atari", "Puppy Pong [TTL]", MACHINE_SUPPORTS_SAVE) | |
| 570 | //GAME( 1974, snoopong, pong, pong, pong, driver_device, 0, ROT0, "Atari", "Snoopy Pong [TTL]", MACHINE_SUPPORTS_SAVE) | |
| 571 | //GAME( 1974, suprpong, 0, suprpong, pong, driver_device, 0, ROT0, "Atari", "Superpong [TTL]", MACHINE_SUPPORTS_SAVE) | |
| 572 | //GAMEL( 1976, breakckt, breakout, breakout, breakout, driver_device, 0, ROT90, "Atari", "Breakout Cocktail [TTL]", MACHINE_SUPPORTS_SAVE, layout_breakckt) | |
| 573 | //GAMEL( 1976, consolet, breakout, breakout, breakout, driver_device, 0, ROT90, "Atari Europe", "Consolette [TTL]", MACHINE_SUPPORTS_SAVE, layout_consolet) |
| r250109 | r250110 | |
|---|---|---|
| 16 | 16 | #include "sound/speaker.h" |
| 17 | 17 | #include "imagedev/cassette.h" |
| 18 | 18 | #include "bus/centronics/ctronics.h" |
| 19 | #include "machine/bankdev.h" | |
| 20 | 19 | #include "machine/ram.h" |
| 21 | 20 | |
| 22 | 21 | class mz_state : public driver_device |
| 23 | 22 | { |
| 24 | 23 | public: |
| 25 | 24 | mz_state(const machine_config &mconfig, device_type type, const char *tag) |
| 26 | : driver_device(mconfig, type, tag) | |
| 27 | , m_maincpu(*this, "maincpu") | |
| 28 | , m_speaker(*this, "speaker") | |
| 29 | , m_pit(*this, "pit8253") | |
| 30 | , m_ppi(*this, "ppi8255") | |
| 31 | , m_cassette(*this, "cassette") | |
| 32 | , m_centronics(*this, "centronics") | |
| 33 | , m_ram(*this, RAM_TAG) | |
| 34 | , m_palette(*this, "palette") | |
| 35 | , m_banke(*this, "banke") | |
| 36 | , m_bankf(*this, "bankf") | |
| 37 | { } | |
| 25 | : driver_device(mconfig, type, tag), | |
| 26 | m_maincpu(*this, "maincpu"), | |
| 27 | m_speaker(*this, "speaker"), | |
| 28 | m_pit(*this, "pit8253"), | |
| 29 | m_ppi(*this, "ppi8255"), | |
| 30 | m_cassette(*this, "cassette"), | |
| 31 | m_centronics(*this, "centronics"), | |
| 32 | m_ram(*this, RAM_TAG), | |
| 33 | m_gfxdecode(*this, "gfxdecode"), | |
| 34 | m_palette(*this, "palette") { } | |
| 38 | 35 | |
| 36 | int m_mz700; /* 1 if running on an mz700 */ | |
| 37 | ||
| 38 | int m_cursor_timer; | |
| 39 | int m_other_timer; | |
| 40 | ||
| 41 | int m_intmsk; /* PPI8255 pin PC2 */ | |
| 42 | ||
| 43 | int m_mz700_ram_lock; /* 1 if ram lock is active */ | |
| 44 | int m_mz700_ram_vram; /* 1 if vram is banked in */ | |
| 45 | ||
| 46 | /* mz800 specific */ | |
| 47 | UINT8 *m_cgram; | |
| 48 | ||
| 49 | int m_mz700_mode; /* 1 if in mz700 mode */ | |
| 50 | int m_mz800_ram_lock; /* 1 if lock is active */ | |
| 51 | int m_mz800_ram_monitor; /* 1 if monitor rom banked in */ | |
| 52 | ||
| 53 | int m_hires_mode; /* 1 if in 640x200 mode */ | |
| 54 | int m_screennum; /* screen designation */ | |
| 55 | ||
| 56 | int m_centronics_busy; | |
| 57 | int m_centronics_perror; | |
| 58 | ||
| 59 | UINT8 *m_colorram; | |
| 60 | UINT8 *m_videoram; | |
| 61 | UINT8 m_speaker_level; | |
| 62 | UINT8 m_prev_state; | |
| 63 | UINT16 m_mz800_ramaddr; | |
| 64 | UINT8 m_mz800_palette[4]; | |
| 65 | UINT8 m_mz800_palette_bank; | |
| 39 | 66 | DECLARE_READ8_MEMBER(mz700_e008_r); |
| 40 | 67 | DECLARE_WRITE8_MEMBER(mz700_e008_w); |
| 41 | 68 | DECLARE_READ8_MEMBER(mz800_bank_0_r); |
| r250109 | r250110 | |
| 60 | 87 | DECLARE_WRITE8_MEMBER(mz800_cgram_w); |
| 61 | 88 | DECLARE_DRIVER_INIT(mz800); |
| 62 | 89 | DECLARE_DRIVER_INIT(mz700); |
| 63 | DECLARE_MACHINE_RESET(mz700); | |
| 64 | DECLARE_MACHINE_RESET(mz800); | |
| 65 | 90 | virtual void machine_start(); |
| 91 | DECLARE_PALETTE_INIT(mz); | |
| 92 | DECLARE_VIDEO_START(mz800); | |
| 66 | 93 | UINT32 screen_update_mz700(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect); |
| 67 | 94 | UINT32 screen_update_mz800(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect); |
| 68 | 95 | TIMER_DEVICE_CALLBACK_MEMBER(ne556_cursor_callback); |
| r250109 | r250110 | |
| 78 | 105 | DECLARE_WRITE8_MEMBER(mz800_z80pio_port_a_w); |
| 79 | 106 | DECLARE_WRITE_LINE_MEMBER(write_centronics_busy); |
| 80 | 107 | DECLARE_WRITE_LINE_MEMBER(write_centronics_perror); |
| 81 | ||
| 82 | private: | |
| 83 | int m_mz700; /* 1 if running on an mz700 */ | |
| 84 | ||
| 85 | int m_cursor_timer; | |
| 86 | int m_other_timer; | |
| 87 | ||
| 88 | int m_intmsk; /* PPI8255 pin PC2 */ | |
| 89 | ||
| 90 | int m_mz700_ram_lock; /* 1 if ram lock is active */ | |
| 91 | int m_mz700_ram_vram; /* 1 if vram is banked in */ | |
| 92 | ||
| 93 | /* mz800 specific */ | |
| 94 | UINT8 *m_cgram; | |
| 95 | UINT8 *m_p_chargen; | |
| 96 | ||
| 97 | int m_mz700_mode; /* 1 if in mz700 mode */ | |
| 98 | int m_mz800_ram_lock; /* 1 if lock is active */ | |
| 99 | int m_mz800_ram_monitor; /* 1 if monitor rom banked in */ | |
| 100 | ||
| 101 | int m_hires_mode; /* 1 if in 640x200 mode */ | |
| 102 | int m_screennum; /* screen designation */ | |
| 103 | ||
| 104 | int m_centronics_busy; | |
| 105 | int m_centronics_perror; | |
| 106 | ||
| 107 | UINT8 *m_colorram; | |
| 108 | UINT8 *m_videoram; | |
| 109 | UINT8 m_speaker_level; | |
| 110 | UINT8 m_prev_state; | |
| 111 | UINT16 m_mz800_ramaddr; | |
| 112 | UINT8 m_mz800_palette[4]; | |
| 113 | UINT8 m_mz800_palette_bank; | |
| 114 | ||
| 115 | 108 | required_device<cpu_device> m_maincpu; |
| 116 | 109 | required_device<speaker_sound_device> m_speaker; |
| 117 | 110 | required_device<pit8253_device> m_pit; |
| r250109 | r250110 | |
| 119 | 112 | required_device<cassette_image_device> m_cassette; |
| 120 | 113 | optional_device<centronics_device> m_centronics; |
| 121 | 114 | required_device<ram_device> m_ram; |
| 115 | required_device<gfxdecode_device> m_gfxdecode; | |
| 122 | 116 | required_device<palette_device> m_palette; |
| 123 | optional_device<address_map_bank_device> m_banke; | |
| 124 | optional_device<address_map_bank_device> m_bankf; | |
| 125 | 117 | }; |
| 126 | 118 | |
| 127 | 119 | #endif /* MZ700_H_ */ |
| r250109 | r250110 | |
|---|---|---|
| 24 | 24 | Bull Fighter 1984 8303 (post) |
| 25 | 25 | Splendor Blast 1985 8303 (post) |
| 26 | 26 | Gekisou 1985 8304 (post) |
| 27 | The Koukou Yakyuu 1985 8304 (post) | |
| 28 | High Voltage 1985 8304?(post says 8404, but readme says 8304) | |
| 27 | The Koukouyakyuh 1985 8304 (post) | |
| 28 | High Voltage 1985 8404?(post says 8404, but readme says 8304) | |
| 29 | 29 | |
| 30 | 30 | ALPHA-8201: "44801A75" -> HD44801, ROM code = A75 |
| 31 | 31 | ALPHA-8302: "44801B35" -> HD44801, ROM code = B35 |
| r250109 | r250110 | |
|---|---|---|
| 324 | 324 | { |
| 325 | 325 | case 0: |
| 326 | 326 | logerror("%06x read version register\n", space.device().safe_pc()); |
| 327 | retdata = m_version_hi_nibble | 0x01; // Version number contained in bits 3-0 | |
| 327 | retdata = m_version_hi_nibble | | |
| 328 | 0x00 | // Bit 3 of Version Number | |
| 329 | 0x00 | // Bit 2 of Version Number | |
| 330 | 0x00 | // Bit 1 of Version Number | |
| 331 | 0x01 ; // Bit 0 of Version Number | |
| 328 | 332 | break; |
| 329 | 333 | |
| 330 | 334 | /* Joystick Port Registers */ |
| r250109 | r250110 | |
|---|---|---|
| 42 | 42 | m_mz700 = TRUE; |
| 43 | 43 | m_mz700_mode = TRUE; |
| 44 | 44 | |
| 45 | m_videoram = auto_alloc_array(machine(), UINT8, 0x1000); | |
| 46 | memset(m_videoram, 0, sizeof(UINT8) * 0x1000); | |
| 47 | m_colorram = m_videoram + 0x800; | |
| 48 | ||
| 49 | m_p_chargen = memregion("cgrom")->base(); | |
| 50 | UINT8 *rom = memregion("monitor")->base(); | |
| 51 | UINT8 *ram = m_ram->pointer(); | |
| 52 | membank("bankr0")->configure_entry(0, &ram[0]); // ram | |
| 53 | membank("bankr0")->configure_entry(1, &rom[0]); // rom | |
| 54 | membank("bankw0")->configure_entry(0, &ram[0]); // ram | |
| 55 | membank("bankd")->configure_entry(0, &ram[0xd000]); // ram | |
| 56 | membank("bankd")->configure_entry(1, m_videoram); // vram | |
| 45 | m_videoram = auto_alloc_array(machine(), UINT8, 0x800); | |
| 46 | memset(m_videoram, 0, sizeof(UINT8) * 0x800); | |
| 47 | m_colorram = auto_alloc_array(machine(), UINT8, 0x800); | |
| 48 | memset(m_colorram, 0, sizeof(UINT8) * 0x800); | |
| 57 | 49 | } |
| 58 | 50 | |
| 59 | 51 | DRIVER_INIT_MEMBER(mz_state,mz800) |
| 60 | 52 | { |
| 61 | 53 | m_mz700 = FALSE; |
| 62 | m_mz700_mode = | |
| 54 | m_mz700_mode = FALSE; | |
| 63 | 55 | |
| 64 | 56 | /* video ram */ |
| 65 | 57 | m_videoram = auto_alloc_array(machine(), UINT8, 0x4000); |
| r250109 | r250110 | |
| 69 | 61 | /* character generator ram */ |
| 70 | 62 | m_cgram = auto_alloc_array(machine(), UINT8, 0x1000); |
| 71 | 63 | memset(m_cgram, 0, sizeof(UINT8) * 0x1000); |
| 72 | ||
| 73 | m_p_chargen = memregion("cgrom")->base(); | |
| 74 | if (!m_p_chargen) | |
| 75 | m_p_chargen = m_cgram; | |
| 76 | UINT8 *rom = memregion("monitor")->base(); | |
| 77 | UINT8 *ram = m_ram->pointer(); | |
| 78 | // configure banks (0 = RAM in all cases) | |
| 79 | membank("bankr0")->configure_entry(0, &ram[0]); // ram | |
| 80 | membank("bankr0")->configure_entry(1, &rom[0]); // rom | |
| 81 | membank("bankw0")->configure_entry(0, &ram[0]); // ram | |
| 82 | membank("bank1")->configure_entry(0, &ram[0x1000]); // ram | |
| 83 | membank("bank1")->configure_entry(1, &rom[0x1000]); // chargen | |
| 84 | membank("banka")->configure_entry(0, &ram[0x8000]); // ram | |
| 85 | membank("banka")->configure_entry(1, m_videoram); // vram in mz800 mode | |
| 86 | membank("bankc")->configure_entry(0, &ram[0xc000]); // ram | |
| 87 | membank("bankc")->configure_entry(1, m_cgram); // cgram in mz800 mode | |
| 88 | membank("bankd")->configure_entry(0, &ram[0xd000]); // ram | |
| 89 | membank("bankd")->configure_entry(1, m_videoram); // vram in mz700 mode | |
| 90 | 64 | } |
| 91 | 65 | |
| 92 | 66 | void mz_state::machine_start() |
| 93 | 67 | { |
| 94 | 68 | /* reset memory map to defaults */ |
| 95 | mz700_bank_4_w(m_maincpu->space(AS_ | |
| 69 | mz700_bank_4_w(m_maincpu->space(AS_PROGRAM), 0, 0); | |
| 96 | 70 | } |
| 97 | 71 | |
| 98 | MACHINE_RESET_MEMBER( mz_state, mz700 ) | |
| 99 | { | |
| 100 | membank("bankr0")->set_entry(1); //rom | |
| 101 | membank("bankw0")->set_entry(0); //ram | |
| 102 | membank("bankd")->set_entry(1); //vram | |
| 103 | m_banke->set_bank(1); //devices | |
| 104 | } | |
| 105 | 72 | |
| 106 | MACHINE_RESET_MEMBER( mz_state, mz800 ) | |
| 107 | { | |
| 108 | // default to mz700 mode or mz1500 won't start. | |
| 109 | membank("bankr0")->set_entry(1); //rom | |
| 110 | membank("bankw0")->set_entry(0); //ram | |
| 111 | membank("bank1")->set_entry(0); //ram | |
| 112 | membank("banka")->set_entry(0); //ram | |
| 113 | membank("bankc")->set_entry(0); //ram | |
| 114 | membank("bankd")->set_entry(1); //vram | |
| 115 | m_bankf->set_bank(1); //devices | |
| 116 | } | |
| 117 | ||
| 118 | ||
| 119 | 73 | /*************************************************************************** |
| 120 | 74 | MMIO |
| 121 | 75 | ***************************************************************************/ |
| r250109 | r250110 | |
| 145 | 99 | |
| 146 | 100 | READ8_MEMBER(mz_state::mz800_bank_0_r) |
| 147 | 101 | { |
| 148 | //address_space &spc = m_maincpu->space(AS_PROGRAM); | |
| 102 | UINT8 *videoram = m_videoram; | |
| 103 | address_space &spc = m_maincpu->space(AS_PROGRAM); | |
| 149 | 104 | |
| 150 | 105 | /* switch in cgrom */ |
| 151 | //spc.install_read_bank(0x1000, 0x1fff, "bank2"); | |
| 152 | //spc.nop_write(0x1000, 0x1fff); | |
| 153 | //membank("bank2")->set_base(memregion("monitor")->base() + 0x1000); | |
| 154 | membank("bank1")->set_entry(1); | |
| 106 | spc.install_read_bank(0x1000, 0x1fff, "bank2"); | |
| 107 | spc.nop_write(0x1000, 0x1fff); | |
| 108 | membank("bank2")->set_base(memregion("monitor")->base() + 0x1000); | |
| 155 | 109 | |
| 156 | 110 | if (m_mz700_mode) |
| 157 | 111 | { |
| 158 | 112 | /* cgram from 0xc000 to 0xcfff */ |
| 159 | //spc.install_read_bank(0xc000, 0xcfff, "bank6"); | |
| 160 | //spc.install_write_handler(0xc000, 0xcfff, write8_delegate(FUNC(mz_state::mz800_cgram_w),this)); | |
| 161 | //membank("bank6")->set_base(m_cgram); | |
| 162 | membank("bankc")->set_entry(1); | |
| 113 | spc.install_read_bank(0xc000, 0xcfff, "bank6"); | |
| 114 | spc.install_write_handler(0xc000, 0xcfff, write8_delegate(FUNC(mz_state::mz800_cgram_w),this)); | |
| 115 | membank("bank6")->set_base(m_cgram); | |
| 163 | 116 | } |
| 164 | 117 | else |
| 165 | 118 | { |
| 166 | 119 | if (m_hires_mode) |
| 167 | 120 | { |
| 168 | 121 | /* vram from 0x8000 to 0xbfff */ |
| 169 | //spc.install_readwrite_bank(0x8000, 0xbfff, "bank4"); | |
| 170 | //membank("bank4")->set_base(m_videoram); | |
| 171 | membank("banka")->set_entry(1); | |
| 122 | spc.install_readwrite_bank(0x8000, 0xbfff, "bank4"); | |
| 123 | membank("bank4")->set_base(videoram); | |
| 172 | 124 | } |
| 173 | 125 | else |
| 174 | 126 | { |
| 175 | 127 | /* vram from 0x8000 to 0x9fff */ |
| 176 | //spc.install_readwrite_bank(0x8000, 0x9fff, "bank4"); | |
| 177 | //membank("bank4")->set_base(m_videoram); | |
| 128 | spc.install_readwrite_bank(0x8000, 0x9fff, "bank4"); | |
| 129 | membank("bank4")->set_base(videoram); | |
| 178 | 130 | |
| 179 | 131 | /* ram from 0xa000 to 0xbfff */ |
| 180 | //spc.install_readwrite_bank(0xa000, 0xbfff, "bank5"); | |
| 181 | //membank("bank5")->set_base(m_ram->pointer() + 0xa000); | |
| 182 | membank("bank1")->set_entry(1); | |
| 132 | spc.install_readwrite_bank(0xa000, 0xbfff, "bank5"); | |
| 133 | membank("bank5")->set_base(m_ram->pointer() + 0xa000); | |
| 183 | 134 | } |
| 184 | 135 | } |
| 185 | 136 | |
| r250109 | r250110 | |
| 188 | 139 | |
| 189 | 140 | WRITE8_MEMBER(mz_state::mz700_bank_0_w) |
| 190 | 141 | { |
| 191 | | |
| 142 | address_space &spc = m_maincpu->space(AS_PROGRAM); | |
| 192 | 143 | |
| 193 | //spc.install_readwrite_bank(0x0000, 0x0fff, "bank1a"); | |
| 194 | //membank("bank1a")->set_base(m_ram->pointer()); | |
| 195 | membank("bankr0")->set_entry(0); // ram | |
| 144 | spc.install_readwrite_bank(0x0000, 0x0fff, "bank1"); | |
| 145 | membank("bank1")->set_base(m_ram->pointer()); | |
| 196 | 146 | } |
| 197 | 147 | |
| 198 | 148 | WRITE8_MEMBER(mz_state::mz800_bank_0_w) |
| 199 | 149 | { |
| 200 | | |
| 150 | address_space &spc = m_maincpu->space(AS_PROGRAM); | |
| 201 | 151 | |
| 202 | //spc.install_readwrite_bank(0x0000, 0x7fff, "bank1a"); | |
| 203 | //membank("bank1a")->set_base(m_ram->pointer()); | |
| 204 | membank("bank1")->set_entry(0); // ram | |
| 205 | membank("bankr0")->set_entry(0); // ram | |
| 152 | spc.install_readwrite_bank(0x0000, 0x7fff, "bank1"); | |
| 153 | membank("bank1")->set_base(m_ram->pointer()); | |
| 206 | 154 | } |
| 207 | 155 | |
| 208 | 156 | READ8_MEMBER(mz_state::mz800_bank_1_r) |
| 209 | 157 | { |
| 210 | | |
| 158 | address_space &spc = m_maincpu->space(AS_PROGRAM); | |
| 211 | 159 | |
| 212 | 160 | /* switch in ram from 0x1000 to 0x1fff */ |
| 213 | //spc.install_readwrite_bank(0x1000, 0x1fff, "bank2"); | |
| 214 | //membank("bank2")->set_base(m_ram->pointer() + 0x1000); | |
| 215 | membank("bank1")->set_entry(0); // ram | |
| 161 | spc.install_readwrite_bank(0x1000, 0x1fff, "bank2"); | |
| 162 | membank("bank2")->set_base(m_ram->pointer() + 0x1000); | |
| 216 | 163 | |
| 217 | 164 | if (m_mz700_mode) |
| 218 | 165 | { |
| 219 | 166 | /* ram from 0xc000 to 0xcfff */ |
| 220 | //spc.install_readwrite_bank(0xc000, 0xcfff, "bank6"); | |
| 221 | //membank("bank6")->set_base(m_ram->pointer() + 0xc000); | |
| 222 | membank("bankc")->set_entry(0); // ram | |
| 167 | spc.install_readwrite_bank(0xc000, 0xcfff, "bank6"); | |
| 168 | membank("bank6")->set_base(m_ram->pointer() + 0xc000); | |
| 223 | 169 | } |
| 224 | 170 | else |
| 225 | 171 | { |
| 226 | 172 | /* ram from 0x8000 to 0xbfff */ |
| 227 | //spc.install_readwrite_bank(0x8000, 0xbfff, "bank4"); | |
| 228 | //membank("bank4")->set_base(m_ram->pointer() + 0x8000); | |
| 229 | membank("banka")->set_entry(0); // ram | |
| 173 | spc.install_readwrite_bank(0x8000, 0xbfff, "bank4"); | |
| 174 | membank("bank4")->set_base(m_ram->pointer() + 0x8000); | |
| 230 | 175 | } |
| 231 | 176 | |
| 232 | 177 | return 0xff; |
| r250109 | r250110 | |
| 234 | 179 | |
| 235 | 180 | WRITE8_MEMBER(mz_state::mz700_bank_1_w) |
| 236 | 181 | { |
| 237 | //address_space &spc = m_maincpu->space(AS_PROGRAM); | |
| 238 | membank("bankd")->set_entry(0); // ram | |
| 182 | address_space &spc = m_maincpu->space(AS_PROGRAM); | |
| 239 | 183 | |
| 240 | 184 | if (m_mz700_mode) |
| 241 | 185 | { |
| 242 | 186 | /* switch in ram when not locked */ |
| 243 | 187 | if (!m_mz700_ram_lock) |
| 244 | 188 | { |
| 245 | if (m_mz700) | |
| 246 | { | |
| 247 | //membank("bankd")->set_entry(0); // ram | |
| 248 | m_banke->set_bank(0); //ram | |
| 249 | } | |
| 250 | else | |
| 251 | { | |
| 252 | //spc.install_readwrite_bank(0xd000, 0xffff, "bank7"); | |
| 253 | //spc.install_readwrite_bank(0xd000, 0xdfff, "bank7"); | |
| 254 | //membank("bank7")->set_base(m_ram->pointer() + 0xd000); | |
| 255 | m_bankf->set_bank(0); //ram | |
| 256 | } | |
| 189 | spc.install_readwrite_bank(0xd000, 0xffff, "bank7"); | |
| 190 | membank("bank7")->set_base(m_ram->pointer() + 0xd000); | |
| 257 | 191 | m_mz700_ram_vram = FALSE; |
| 258 | 192 | } |
| 259 | 193 | } |
| r250109 | r250110 | |
| 262 | 196 | /* switch in ram when not locked */ |
| 263 | 197 | if (!m_mz800_ram_lock) |
| 264 | 198 | { |
| 265 | //spc.install_readwrite_bank(0xe000, 0xffff, "bank8"); | |
| 266 | //membank("bank8")->set_base(m_ram->pointer() + 0xe000); | |
| 267 | m_bankf->set_bank(0); //ram | |
| 199 | spc.install_readwrite_bank(0xe000, 0xffff, "bank8"); | |
| 200 | membank("bank8")->set_base(m_ram->pointer() + 0xe000); | |
| 268 | 201 | m_mz800_ram_monitor = FALSE; |
| 269 | 202 | } |
| 270 | 203 | } |
| r250109 | r250110 | |
| 272 | 205 | |
| 273 | 206 | WRITE8_MEMBER(mz_state::mz700_bank_2_w) |
| 274 | 207 | { |
| 275 | | |
| 208 | address_space &spc = m_maincpu->space(AS_PROGRAM); | |
| 276 | 209 | |
| 277 | //spc.install_read_bank(0x0000, 0x0fff, "bank1a"); | |
| 278 | //spc.nop_write(0x0000, 0x0fff); | |
| 279 | //membank("bank1a")->set_base(memregion("monitor")->base()); | |
| 280 | membank("bankr0")->set_entry(1); // rom | |
| 281 | ||
| 210 | spc.install_read_bank(0x0000, 0x0fff, "bank1"); | |
| 211 | spc.nop_write(0x0000, 0x0fff); | |
| 212 | membank("bank1")->set_base(memregion("monitor")->base()); | |
| 282 | 213 | } |
| 283 | 214 | |
| 284 | 215 | WRITE8_MEMBER(mz_state::mz700_bank_3_w) |
| 285 | 216 | { |
| 286 | //address_space &spc = m_maincpu->space(AS_PROGRAM); | |
| 217 | UINT8 *videoram = m_videoram; | |
| 218 | address_space &spc = m_maincpu->space(AS_PROGRAM); | |
| 287 | 219 | |
| 288 | 220 | if (m_mz700_mode) |
| 289 | 221 | { |
| 290 | 222 | if (!m_mz700_ram_lock) |
| 291 | 223 | { |
| 292 | if (m_mz700) | |
| 293 | membank("bankd")->set_entry(1); | |
| 294 | else | |
| 295 | { | |
| 296 | /* switch in videoram */ | |
| 297 | //spc.install_readwrite_bank(0xd000, 0xd7ff, "bank7"); | |
| 298 | //membank("bank7")->set_base(m_videoram); | |
| 224 | /* switch in videoram */ | |
| 225 | spc.install_readwrite_bank(0xd000, 0xd7ff, "bank7"); | |
| 226 | membank("bank7")->set_base(videoram); | |
| 299 | 227 | |
| 300 | /* switch in colorram */ | |
| 301 | //spc.install_readwrite_bank(0xd800, 0xdfff, "bank9"); | |
| 302 | //membank("bank9")->set_base(m_colorram); | |
| 303 | membank("bankd")->set_entry(1); | |
| 304 | } | |
| 228 | /* switch in colorram */ | |
| 229 | spc.install_readwrite_bank(0xd800, 0xdfff, "bank9"); | |
| 230 | membank("bank9")->set_base(m_colorram); | |
| 231 | ||
| 305 | 232 | m_mz700_ram_vram = TRUE; |
| 306 | 233 | |
| 307 | 234 | /* switch in memory mapped i/o devices */ |
| 308 | 235 | if (m_mz700) |
| 309 | 236 | { |
| 310 | m_banke->set_bank(1); //devices | |
| 237 | spc.install_readwrite_handler(0xe000, 0xfff3, 0, 0x1ff0, read8_delegate(FUNC(i8255_device::read), (i8255_device*)m_ppi), write8_delegate(FUNC(i8255_device::write), (i8255_device*)m_ppi)); | |
| 238 | spc.install_readwrite_handler(0xe004, 0xfff7, 0, 0x1ff0, read8_delegate(FUNC(pit8253_device::read), (pit8253_device*)m_pit), write8_delegate(FUNC(pit8253_device::write), (pit8253_device*)m_pit)); | |
| 239 | spc.install_readwrite_handler(0xe008, 0xfff8, 0, 0x1ff0, read8_delegate(FUNC(mz_state::mz700_e008_r),this), write8_delegate(FUNC(mz_state::mz700_e008_w),this)); | |
| 311 | 240 | } |
| 312 | 241 | else |
| 313 | 242 | { |
| 314 | m_bankf->set_bank(1); //devices | |
| 243 | spc.install_readwrite_handler(0xe000, 0xe003, read8_delegate(FUNC(i8255_device::read), (i8255_device*)m_ppi), write8_delegate(FUNC(i8255_device::write), (i8255_device*)m_ppi)); | |
| 244 | spc.install_readwrite_handler(0xe004, 0xe007, read8_delegate(FUNC(pit8253_device::read), (pit8253_device*)m_pit), write8_delegate(FUNC(pit8253_device::write), (pit8253_device*)m_pit)); | |
| 245 | spc.install_readwrite_handler(0xe008, 0xe008, read8_delegate(FUNC(mz_state::mz700_e008_r),this), write8_delegate(FUNC(mz_state::mz700_e008_w),this)); | |
| 315 | 246 | } |
| 316 | 247 | } |
| 317 | 248 | } |
| r250109 | r250110 | |
| 320 | 251 | if (!m_mz800_ram_lock) |
| 321 | 252 | { |
| 322 | 253 | /* switch in mz800 monitor rom if not locked */ |
| 323 | //spc.install_read_bank(0xe000, 0xffff, "bank8"); | |
| 324 | //spc.nop_write(0xe000, 0xffff); | |
| 325 | //membank("bank8")->set_base(memregion("monitor")->base() + 0x2000); | |
| 326 | m_bankf->set_bank(1); // devices + rom | |
| 254 | spc.install_read_bank(0xe000, 0xffff, "bank8"); | |
| 255 | spc.nop_write(0xe000, 0xffff); | |
| 256 | membank("bank8")->set_base(memregion("monitor")->base() + 0x2000); | |
| 327 | 257 | m_mz800_ram_monitor = TRUE; |
| 328 | 258 | } |
| 329 | 259 | } |
| r250109 | r250110 | |
| 331 | 261 | |
| 332 | 262 | WRITE8_MEMBER(mz_state::mz700_bank_4_w) |
| 333 | 263 | { |
| 334 | //address_space &spc = m_maincpu->space(AS_PROGRAM); | |
| 264 | UINT8 *videoram = m_videoram; | |
| 265 | address_space &spc = m_maincpu->space(AS_PROGRAM); | |
| 335 | 266 | |
| 336 | 267 | if (m_mz700_mode) |
| 337 | 268 | { |
| r250109 | r250110 | |
| 339 | 270 | mz700_bank_2_w(space, 0, 0); /* switch in monitor rom */ |
| 340 | 271 | mz700_bank_3_w(space, 0, 0); /* switch in videoram, colorram, and mmio */ |
| 341 | 272 | |
| 342 | if (!m_mz700) | |
| 343 | { | |
| 344 | /* rest is ram is always ram in mz700 mode */ | |
| 345 | //spc.install_readwrite_bank(0x1000, 0xcfff, "bank2"); | |
| 346 | //membank("bank2")->set_base(m_ram->pointer() + 0x1000); | |
| 347 | membank("bankr0")->set_entry(1); // rom | |
| 348 | membank("bank1")->set_entry(0); // ram | |
| 349 | membank("bankc")->set_entry(0); // ram | |
| 350 | } | |
| 273 | /* rest is ram is always ram in mz700 mode */ | |
| 274 | spc.install_readwrite_bank(0x1000, 0xcfff, "bank2"); | |
| 275 | membank("bank2")->set_base(m_ram->pointer() + 0x1000); | |
| 351 | 276 | } |
| 352 | 277 | else |
| 353 | 278 | { |
| 354 | 279 | /* monitor rom and cgrom */ |
| 355 | //spc.install_read_bank(0x0000, 0x1fff, "bank1a"); | |
| 356 | //spc.nop_write(0x0000, 0x1fff); | |
| 357 | //membank("bank1a")->set_base(memregion("monitor")->base()); | |
| 358 | membank("bankr0")->set_entry(1); // rom | |
| 359 | membank("bank1")->set_entry(1); // rom | |
| 280 | spc.install_read_bank(0x0000, 0x1fff, "bank1"); | |
| 281 | spc.nop_write(0x0000, 0x1fff); | |
| 282 | membank("bank1")->set_base(memregion("monitor")->base()); | |
| 360 | 283 | |
| 361 | 284 | /* ram from 0x2000 to 0x7fff */ |
| 362 | //spc.install_readwrite_bank(0x2000, 0x7fff, "bank3"); | |
| 363 | //membank("bank3")->set_base(m_ram->pointer()); | |
| 285 | spc.install_readwrite_bank(0x2000, 0x7fff, "bank3"); | |
| 286 | membank("bank3")->set_base(m_ram->pointer()); | |
| 364 | 287 | |
| 365 | 288 | if (m_hires_mode) |
| 366 | 289 | { |
| 367 | 290 | /* vram from 0x8000 to 0xbfff */ |
| 368 | //spc.install_readwrite_bank(0x8000, 0xbfff, "bank4"); | |
| 369 | //membank("bank4")->set_base(m_videoram); | |
| 370 | membank("banka")->set_entry(1); // vram | |
| 291 | spc.install_readwrite_bank(0x8000, 0xbfff, "bank4"); | |
| 292 | membank("bank4")->set_base(videoram); | |
| 371 | 293 | } |
| 372 | 294 | else |
| 373 | 295 | { |
| 374 | 296 | /* vram from 0x8000 to 0x9fff */ |
| 375 | //spc.install_readwrite_bank(0x8000, 0x9fff, "bank4"); | |
| 376 | //membank("bank4")->set_base(m_videoram); | |
| 377 | membank("banka")->set_entry(1); // vram | |
| 297 | spc.install_readwrite_bank(0x8000, 0x9fff, "bank4"); | |
| 298 | membank("bank4")->set_base(videoram); | |
| 378 | 299 | |
| 379 | 300 | /* ram from 0xa000 to 0xbfff */ |
| 380 | //spc.install_readwrite_bank(0xa000, 0xbfff, "bank5"); | |
| 381 | //membank("bank5")->set_base(m_ram->pointer() + 0xa000); | |
| 301 | spc.install_readwrite_bank(0xa000, 0xbfff, "bank5"); | |
| 302 | membank("bank5")->set_base(m_ram->pointer() + 0xa000); | |
| 382 | 303 | } |
| 383 | 304 | |
| 384 | 305 | /* ram from 0xc000 to 0xdfff */ |
| 385 | //spc.install_readwrite_bank(0xc000, 0xdfff, "bank6"); | |
| 386 | //membank("bank6")->set_base(m_ram->pointer() + 0xc000); | |
| 387 | membank("bankd")->set_entry(0); // ram | |
| 306 | spc.install_readwrite_bank(0xc000, 0xdfff, "bank6"); | |
| 307 | membank("bank6")->set_base(m_ram->pointer() + 0xc000); | |
| 388 | 308 | |
| 389 | 309 | /* mz800 monitor rom from 0xe000 to 0xffff */ |
| 390 | //spc.install_read_bank(0xe000, 0xffff, "bank8"); | |
| 391 | //spc.nop_write(0xe000, 0xffff); | |
| 392 | //membank("bank8")->set_base(memregion("monitor")->base() + 0x2000); | |
| 393 | m_bankf->set_bank(1); // devices + rom | |
| 310 | spc.install_read_bank(0xe000, 0xffff, "bank8"); | |
| 311 | spc.nop_write(0xe000, 0xffff); | |
| 312 | membank("bank8")->set_base(memregion("monitor")->base() + 0x2000); | |
| 394 | 313 | m_mz800_ram_monitor = TRUE; |
| 395 | 314 | |
| 396 | 315 | m_mz800_ram_lock = FALSE; /* reset lock? */ |
| r250109 | r250110 | |
| 399 | 318 | |
| 400 | 319 | WRITE8_MEMBER(mz_state::mz700_bank_5_w) |
| 401 | 320 | { |
| 402 | | |
| 321 | address_space &spc = m_maincpu->space(AS_PROGRAM); | |
| 403 | 322 | |
| 404 | 323 | if (m_mz700_mode) |
| 405 | 324 | { |
| 406 | 325 | /* prevent access from 0xd000 to 0xffff */ |
| 407 | 326 | m_mz700_ram_lock = TRUE; |
| 408 | if (m_mz700) | |
| 409 | m_banke->set_bank(2); | |
| 410 | else | |
| 411 | //spc.nop_readwrite(0xd000, 0xdfff); | |
| 412 | //spc.nop_readwrite(0xd000, 0xffff); | |
| 413 | m_bankf->set_bank(2); | |
| 327 | spc.nop_readwrite(0xd000, 0xffff); | |
| 414 | 328 | } |
| 415 | 329 | else |
| 416 | 330 | { |
| 417 | 331 | /* prevent access from 0xe000 to 0xffff */ |
| 418 | 332 | m_mz800_ram_lock = TRUE; |
| 419 | //spc.nop_readwrite(0xe000, 0xffff); | |
| 420 | m_bankf->set_bank(2); | |
| 333 | spc.nop_readwrite(0xe000, 0xffff); | |
| 421 | 334 | } |
| 422 | 335 | } |
| 423 | 336 | |
| r250109 | r250110 | |
| 522 | 435 | LOG(2,"mz700_pio_port_a_w",("%02X\n", data),machine()); |
| 523 | 436 | |
| 524 | 437 | /* the ls145 is connected to PA0-PA3 */ |
| 525 | dynamic_cast<ttl74145_device *>(device)->write(data & 0x0 | |
| 438 | dynamic_cast<ttl74145_device *>(device)->write(data & 0x07); | |
| 526 | 439 | |
| 527 | 440 | /* ne556 reset is connected to PA7 */ |
| 528 | 441 | timer->enable(BIT(data, 7)); |
| r250109 | r250110 | |
|---|---|---|
| 285 | 285 | |
| 286 | 286 | //popmessage("%x %x %x %x %x %x %x %x",IORAM_READ(8),IORAM_READ(9),IORAM_READ(10),IORAM_READ(11),IORAM_READ(12),IORAM_READ(13),IORAM_READ(14),IORAM_READ(15)); |
| 287 | 287 | |
| 288 | m_out_0_cb((offs_t)0, IORAM_READ(9)); // output to pins 13-16 (motos, pacnpal, gaplus) | |
| 289 | m_out_1_cb((offs_t)0, IORAM_READ(10)); // output to pins 17-20 (gaplus) | |
| 288 | m_out_0_cb((offs_t)0, IORAM_READ(9) & 0x0f); // output to pins 13-16 (motos, pacnpal, gaplus) | |
| 289 | m_out_1_cb((offs_t)0, IORAM_READ(10) & 0x0f); // output to pins 17-20 (gaplus) | |
| 290 | 290 | break; |
| 291 | 291 | |
| 292 | 292 | case 2: // initialize coinage settings |
| r250109 | r250110 | |
| 388 | 388 | |
| 389 | 389 | //popmessage("%x %x %x %x %x %x %x %x",IORAM_READ(8),IORAM_READ(9),IORAM_READ(10),IORAM_READ(11),IORAM_READ(12),IORAM_READ(13),IORAM_READ(14),IORAM_READ(15)); |
| 390 | 390 | |
| 391 | m_out_0_cb((offs_t)0, IORAM_READ(9)); // output to pins 13-16 (toypop) | |
| 392 | m_out_1_cb((offs_t)0, IORAM_READ(10)); // output to pins 17-20 (toypop) | |
| 391 | m_out_0_cb((offs_t)0, IORAM_READ(9) & 0x0f); // output to pins 13-16 (toypop) | |
| 392 | m_out_1_cb((offs_t)0, IORAM_READ(10) & 0x0f); // output to pins 17-20 (toypop) | |
| 393 | 393 | break; |
| 394 | 394 | |
| 395 | 395 | case 2: // initialize coinage settings |
| r250109 | r250110 | |
|---|---|---|
| 54 | 54 | { |
| 55 | 55 | UINT16 code = (m_vram[_N_][tile_index] & 0xffff0000) >> 16; |
| 56 | 56 | UINT16 attr = (m_vram[_N_][tile_index] & 0x0000ffff); |
| 57 | SET_TILE_INFO_MEMBER(1 + _N_, code, (attr & 0x3f) >> 4, TILE_FLIPYX(attr >> 6)); | |
| 57 | SET_TILE_INFO_MEMBER(1 + _N_, code, (attr & 0x3f) >> 4, TILE_FLIPYX((attr >> 6) & 3)); | |
| 58 | 58 | } |
| 59 | 59 | |
| 60 | 60 | TILE_GET_INFO_MEMBER(fuuki32_state::get_tile_info_0){ get_tile_info8bpp(tileinfo, tile_index, 0); } |
| r250109 | r250110 | |
| 64 | 64 | { |
| 65 | 65 | UINT16 code = (m_vram[_N_][tile_index] & 0xffff0000) >> 16; |
| 66 | 66 | UINT16 attr = (m_vram[_N_][tile_index] & 0x0000ffff); |
| 67 | SET_TILE_INFO_MEMBER(1 + _N_, code, attr & 0x3f, TILE_FLIPYX(attr >> 6)); | |
| 67 | SET_TILE_INFO_MEMBER(1 + _N_, code, attr & 0x3f, TILE_FLIPYX((attr >> 6) & 3)); | |
| 68 | 68 | } |
| 69 | 69 | |
| 70 | 70 | TILE_GET_INFO_MEMBER(fuuki32_state::get_tile_info_2){ get_tile_info4bpp(tileinfo, tile_index, 2); } |
| r250109 | r250110 | |
|---|---|---|
| 57 | 57 | int data = videoram[tile_index * 2] | (videoram[tile_index * 2 + 1] << 8); |
| 58 | 58 | int code = data & 0x1ff; |
| 59 | 59 | int color = (data >> 11) & 3; |
| 60 | SET_TILE_INFO_MEMBER(0, code, color, TILE_FLIPYX(data >> 9)); | |
| 60 | SET_TILE_INFO_MEMBER(0, code, color, TILE_FLIPYX((data >> 9) & 3)); | |
| 61 | 61 | |
| 62 | 62 | /* sprite color base comes from the top 2 bits */ |
| 63 | 63 | tileinfo.category = (data >> 14) & 3; |
| r250109 | r250110 | |
| 83 | 83 | int data = videoram[tile_index * 2] | (videoram[tile_index * 2 + 1] << 8); |
| 84 | 84 | int code = data & 0x3ff; |
| 85 | 85 | int color = (data >> 12) & 3; |
| 86 | SET_TILE_INFO_MEMBER(0, code, color, TILE_FLIPYX(data >> 10)); | |
| 86 | SET_TILE_INFO_MEMBER(0, code, color, TILE_FLIPYX((data >> 10) & 3)); | |
| 87 | 87 | |
| 88 | 88 | /* sprite color base might come from the top 2 bits */ |
| 89 | 89 | tileinfo.category = (data >> 14) & 3; |
| r250109 | r250110 | |
|---|---|---|
| 36 | 36 | int data = videoram[tile_index * 2] | (videoram[tile_index * 2 + 1] << 8); |
| 37 | 37 | int code = (data & 0x3ff) | ((data >> 4) & 0x400); |
| 38 | 38 | int color = ((data >> 12) & 3) ^ 3; |
| 39 | SET_TILE_INFO_MEMBER(0, code, color, TILE_FLIPYX(data >> 10)); | |
| 39 | SET_TILE_INFO_MEMBER(0, code, color, TILE_FLIPYX((data >> 10) & 3)); | |
| 40 | 40 | } |
| 41 | 41 | |
| 42 | 42 |
| r250109 | r250110 | |
|---|---|---|
| 26 | 26 | int data = LOW_BYTE(videoram[tile_index * 2]) | (LOW_BYTE(videoram[tile_index * 2 + 1]) << 8); |
| 27 | 27 | int code = (data & 0x3ff) | ((data >> 4) & 0xc00); |
| 28 | 28 | int color = (~data >> 12) & 3; |
| 29 | SET_TILE_INFO_MEMBER(0, code, color, TILE_FLIPYX(data >> 10)); | |
| 29 | SET_TILE_INFO_MEMBER(0, code, color, TILE_FLIPYX((data >> 10) & 3)); | |
| 30 | 30 | if (m_gfxdecode->gfx(0)->elements() < 0x1000) |
| 31 | 31 | tileinfo.category = (data >> 15) & 1; |
| 32 | 32 | } |
| r250109 | r250110 | |
| 37 | 37 | UINT16 *videoram = m_videoram; |
| 38 | 38 | int data = videoram[tile_index]; |
| 39 | 39 | int color = (data >> 13) & 7; |
| 40 | SET_TILE_INFO_MEMBER(0, data & 0x3ff, color, TILE_FLIPYX(data >> 11)); | |
| 40 | SET_TILE_INFO_MEMBER(0, data & 0x3ff, color, TILE_FLIPYX((data >> 11) & 3)); | |
| 41 | 41 | } |
| 42 | 42 | |
| 43 | 43 | |
| r250109 | r250110 | |
| 46 | 46 | UINT16 *videoram = m_videoram; |
| 47 | 47 | int data = videoram[tile_index]; |
| 48 | 48 | int color = (data >> 13) & 7; |
| 49 | SET_TILE_INFO_MEMBER(2, data & 0x3ff, color, TILE_FLIPYX(data >> 11)); | |
| 49 | SET_TILE_INFO_MEMBER(2, data & 0x3ff, color, TILE_FLIPYX((data >> 11) & 3)); | |
| 50 | 50 | tileinfo.category = (color != 0); |
| 51 | 51 | } |
| 52 | 52 |
| r250109 | r250110 | |
|---|---|---|
| 15 | 15 | #include "includes/mz700.h" |
| 16 | 16 | |
| 17 | 17 | |
| 18 | #ifndef VERBOSE | |
| 19 | #define VERBOSE 1 | |
| 20 | #endif | |
| 21 | ||
| 22 | #define LOG(N,M,A) \ | |
| 23 | do { \ | |
| 24 | if(VERBOSE>=N) \ | |
| 25 | { \ | |
| 26 | if( M ) \ | |
| 27 | logerror("%11.6f: %-24s",machine.time().as_double(),(char*)M ); \ | |
| 28 | logerror A; \ | |
| 29 | } \ | |
| 30 | } while (0) | |
| 31 | ||
| 32 | ||
| 33 | PALETTE_INIT_MEMBER(mz_state, mz) | |
| 34 | { | |
| 35 | int i; | |
| 36 | ||
| 37 | for (i = 0; i < 8; i++) | |
| 38 | m_palette->set_indirect_color(i, rgb_t((i & 2) ? 0xff : 0x00, (i & 4) ? 0xff : 0x00, (i & 1) ? 0xff : 0x00)); | |
| 39 | ||
| 40 | for (i = 0; i < 256; i++) | |
| 41 | { | |
| 42 | m_palette->set_pen_indirect(i*2, i & 7); | |
| 43 | m_palette->set_pen_indirect(i*2+1, (i >> 4) & 7); | |
| 44 | } | |
| 45 | } | |
| 46 | ||
| 47 | ||
| 18 | 48 | UINT32 mz_state::screen_update_mz700(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect) |
| 19 | 49 | { |
| 20 | UINT8 y,ra,gfx,col,bg=0,fg=0,oldcol=0x7e; | |
| 21 | UINT16 sy=0,ma=0,x,chr; | |
| 50 | UINT8 *videoram = m_videoram; | |
| 51 | int offs; | |
| 22 | 52 | |
| 23 | for (y = 0; y < 25; y++) | |
| 53 | bitmap.fill(m_palette->black_pen(), cliprect); | |
| 54 | ||
| 55 | for(offs = 0; offs < 40*25; offs++) | |
| 24 | 56 | { |
| 25 | for (ra = 0; ra < 8; ra++) | |
| 26 | { | |
| 27 | UINT16 *p = &bitmap.pix16(sy++); | |
| 57 | int sx, sy, code, color; | |
| 28 | 58 | |
| 29 | for (x = ma; x < ma + 40; x++) | |
| 30 | { | |
| 31 | col = m_colorram[x]; | |
| 32 | if (col != oldcol) | |
| 33 | { | |
| 34 | oldcol = col; | |
| 35 | col = BITSWAP8(col, 7, 3, 4, 6, 5, 0, 2, 1); // turn BRG into RGB | |
| 36 | bg = col & 7; | |
| 37 | fg = (col >> 3) & 7; | |
| 38 | } | |
| 39 | chr = m_videoram[x] | (BIT(col, 7)<<8); | |
| 40 | gfx = m_p_chargen[(chr<<3) | ra ]; | |
| 59 | sy = (offs / 40) * 8; | |
| 60 | sx = (offs % 40) * 8; | |
| 41 | 61 | |
| 42 | /* Display a scanline of a character */ | |
| 43 | *p++ = BIT(gfx, 0) ? fg : bg; | |
| 44 | *p++ = BIT(gfx, 1) ? fg : bg; | |
| 45 | *p++ = BIT(gfx, 2) ? fg : bg; | |
| 46 | *p++ = BIT(gfx, 3) ? fg : bg; | |
| 47 | *p++ = BIT(gfx, 4) ? fg : bg; | |
| 48 | *p++ = BIT(gfx, 5) ? fg : bg; | |
| 49 | *p++ = BIT(gfx, 6) ? fg : bg; | |
| 50 | *p++ = BIT(gfx, 7) ? fg : bg; | |
| 51 | } | |
| 52 | } | |
| 53 | ma+=40; | |
| 62 | color = m_colorram[offs]; | |
| 63 | code = videoram[offs] | (color & 0x80) << 1; | |
| 64 | ||
| 65 | m_gfxdecode->gfx(0)->opaque(bitmap,cliprect, code, color, 0, 0, sx, sy); | |
| 54 | 66 | } |
| 67 | ||
| 55 | 68 | return 0; |
| 56 | 69 | } |
| 57 | 70 | |
| 58 | 71 | |
| 59 | 72 | /*************************************************************************** |
| 60 | 73 | MZ-800 |
| 61 | Not working. | |
| 62 | 74 | ***************************************************************************/ |
| 63 | 75 | |
| 76 | VIDEO_START_MEMBER(mz_state,mz800) | |
| 77 | { | |
| 78 | m_gfxdecode->gfx(0)->set_source(m_cgram); | |
| 79 | } | |
| 80 | ||
| 64 | 81 | UINT32 mz_state::screen_update_mz800(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect) |
| 65 | 82 | { |
| 83 | UINT8 *videoram = m_videoram; | |
| 84 | ||
| 85 | bitmap.fill(m_palette->black_pen(), cliprect); | |
| 86 | ||
| 66 | 87 | if (m_mz700_mode) |
| 67 | 88 | return screen_update_mz700(screen, bitmap, cliprect); |
| 68 | 89 | else |
| r250109 | r250110 | |
| 73 | 94 | else |
| 74 | 95 | { |
| 75 | 96 | int x, y; |
| 97 | UINT8 *start_addr = videoram; | |
| 76 | 98 | |
| 77 | 99 | for (x = 0; x < 40; x++) |
| 78 | 100 | { |
| 79 | 101 | for (y = 0; y < 200; y++) |
| 80 | 102 | { |
| 81 | bitmap.pix16(y, x * 8 + 0) = BIT(m_videoram[x * 8 + y], 0) ? 7 : 0; | |
| 82 | bitmap.pix16(y, x * 8 + 1) = BIT(m_videoram[x * 8 + y], 1) ? 7 : 0; | |
| 83 | bitmap.pix16(y, x * 8 + 2) = BIT(m_videoram[x * 8 + y], 2) ? 7 : 0; | |
| 84 | bitmap.pix16(y, x * 8 + 3) = BIT(m_videoram[x * 8 + y], 3) ? 7 : 0; | |
| 85 | bitmap.pix16(y, x * 8 + 4) = BIT(m_videoram[x * 8 + y], 4) ? 7 : 0; | |
| 86 | bitmap.pix16(y, x * 8 + 5) = BIT(m_videoram[x * 8 + y], 5) ? 7 : 0; | |
| 87 | bitmap.pix16(y, x * 8 + 6) = BIT(m_videoram[x * 8 + y], 6) ? 7 : 0; | |
| 88 | bitmap.pix16(y, x * 8 + 7) = BIT(m_videoram[x * 8 + y], 7) ? 7 : 0; | |
| 103 | bitmap.pix16(y, x * 8 + 0) = BIT(start_addr[x * 8 + y], 0); | |
| 104 | bitmap.pix16(y, x * 8 + 1) = BIT(start_addr[x * 8 + y], 1); | |
| 105 | bitmap.pix16(y, x * 8 + 2) = BIT(start_addr[x * 8 + y], 2); | |
| 106 | bitmap.pix16(y, x * 8 + 3) = BIT(start_addr[x * 8 + y], 3); | |
| 107 | bitmap.pix16(y, x * 8 + 4) = BIT(start_addr[x * 8 + y], 4); | |
| 108 | bitmap.pix16(y, x * 8 + 5) = BIT(start_addr[x * 8 + y], 5); | |
| 109 | bitmap.pix16(y, x * 8 + 6) = BIT(start_addr[x * 8 + y], 6); | |
| 110 | bitmap.pix16(y, x * 8 + 7) = BIT(start_addr[x * 8 + y], 7); | |
| 89 | 111 | } |
| 90 | 112 | } |
| 91 | 113 | } |
| r250109 | r250110 | |
| 101 | 123 | WRITE8_MEMBER(mz_state::mz800_cgram_w) |
| 102 | 124 | { |
| 103 | 125 | m_cgram[offset] = data; |
| 126 | ||
| 127 | m_gfxdecode->gfx(0)->mark_dirty(offset/8); | |
| 104 | 128 | } |
| r0 | r250110 |
|---|
| Previous | 199869 Revisions | Next |