Merge master

This commit is contained in:
Stefanos Kornilios Mitsis Poiitidis 2015-05-08 17:29:06 +02:00
commit 5d3c71bee7
240 changed files with 31036 additions and 2129 deletions

13
.gitignore vendored
View File

@ -15,6 +15,7 @@ shell/android/tools
# iOS/Xcode general stuff
.DS_Store
*/build/*
**/build/*
*.pbxuser
!default.pbxuser
*.mode1v3
@ -36,3 +37,15 @@ build/*
*Karen_angelXwind*
local.properties
ant.properties
*build_obj
Workdir/data
Workdir/reicast*
Workdir/vmu*
Workdir/webui*
Workdir/emu.cfg
Workdir/lib*ant.properties
# Linux stuff
shell/lin86/.map
shell/lin86/nosym-reicast.elf
shell/lin86/reicast.elf

View File

@ -1,35 +1,55 @@
language:
cpp
compiler:
gcc
notifications:
irc: chat.freenode.net#reicast
language: cpp
compiler: gcc
before_install:
- sudo apt-get install aria2 -qq
- download_extract() { aria2c -x 16 $1 -o $2 && tar -xf $2; }
- download_extract_zip() { aria2c -x 16 $1 -o $2 && unzip -qq $2; }
- sudo apt-get -qq update
- sudo apt-get -qq -y install openjdk-7-jdk ant lib32z1-dev lib32stdc++6
- GIT_BUILD=$(git log --pretty=format:'%h' -n 1)
- openssl aes-256-cbc -K $encrypted_c726d225a9d9_key -iv $encrypted_c726d225a9d9_iv
-in debug.keystore.enc -out debug.keystore -d
- sudo apt-get install aria2 -qq
- download_extract() { aria2c -x 16 $1 -o $2 && tar -xf $2; }
- download_extract_zip() { aria2c -x 16 $1 -o $2 && unzip -qq $2; }
- sudo apt-get -qq update
- sudo apt-get -qq -y install openjdk-7-jdk ant lib32z1-dev lib32stdc++6
- GIT_HASH=`git log --pretty=format:'%h' -n 1`
- GIT_BUILD=`git describe --all`-$GIT_HASH
install:
- download_extract http://dl.google.com/android/ndk/android-ndk-r9c-linux-x86_64.tar.bz2 android-ndk-r9c-linux-x86_64.tar.bz2
- download_extract_zip http://dl.google.com/android/adt/adt-bundle-linux-x86_64-20131030.zip adt-bundle-linux-x86_64-20131030.zip
- mv android-ndk-r9c android-ndk
- mv adt-bundle-linux-x86_64-20131030/sdk/ android-sdk
- rm android-ndk-r9c-linux-x86_64.tar.bz2
- rm adt-bundle-linux-x86_64-20131030.zip
- export ANDROID_HOME=$(pwd)/android-sdk
- export NDK=$(pwd)/android-ndk
- export PATH=${PATH}:$ANDROID_HOME/tools
- export PATH=${PATH}:$ANDROID_HOME/platform-tools
- export PATH=${PATH}:$NDK
before_script:
- download_extract http://dl.google.com/android/ndk/android-ndk-r9c-linux-x86_64.tar.bz2
android-ndk-r9c-linux-x86_64.tar.bz2
- download_extract_zip http://dl.google.com/android/adt/adt-bundle-linux-x86_64-20131030.zip
adt-bundle-linux-x86_64-20131030.zip
- mv android-ndk-r9c android-ndk
- mv adt-bundle-linux-x86_64-20131030/sdk/ android-sdk
- rm android-ndk-r9c-linux-x86_64.tar.bz2
- rm adt-bundle-linux-x86_64-20131030.zip
- export ANDROID_HOME=$(pwd)/android-sdk
- export NDK=$(pwd)/android-ndk
- export ANDROID_NDK=$(pwd)/android-ndk
- export PATH=${PATH}:$ANDROID_HOME/tools
- export PATH=${PATH}:$ANDROID_HOME/platform-tools
- export PATH=${PATH}:$NDK
- mkdir -p ~/.android
- mv debug.keystore ~/.android/debug.keystore
before_script:
script:
- cd shell/android
- android update project -p .
- ndk-build -j 4
- ant debug
after_script:
- cp /home/travis/build/MrYadro/reicast-emulator/shell/android/bin/reicast-debug.apk reicast-debug-$GIT_BUILD.apk
- curl --upload-file reicast-debug-$GIT_BUILD.apk ftp://reicast:reicast@109.120.165.109/
- echo "http://mryadro.ru/reicast/reicast-debug-$GIT_BUILD.apk"
- cd shell/android
- android update project -p . --target "android-19"
- export NUMBER_OF_PROCESSORS=2
- ant debug
before_deploy:
- cd ../../
- mkdir -p artifacts/$GIT_BUILD/
- cp shell/android/bin/reicast-debug.apk artifacts/$GIT_BUILD/reicast-android-debug-$GIT_HASH.apk
deploy:
provider: s3
access_key_id: AKIAJR5J3OHAQUP5BHHQ
secret_access_key:
secure: dDmBiadS1cc2XCWWDZ1kwZGIoLkwEOLsdi5nzVdn1dHMsEKxN3u+3WFuuOfJHZYcdrtN7VhojCarPa+eGDDeiVme4m3y+QKMbI9lecHX9+t4jDNUyYep5TpeIVUkZ7fj4JsMi7U9/YFBTg7QnqvVsMAHE9vobkvjegjAYToBrlM=
bucket: reicast-builds
local-dir: artifacts
upload-dir: builds
skip_cleanup: true
on:
all_branches: true
repo: reicast/reicast-emulator
after_deploy:
- echo "done"

View File

@ -24,8 +24,9 @@ Tools required:
* NDK r8b or newer
- https://developer.android.com/tools/sdk/ndk/index.html
- If are not using r9c+, comment the "NDK_TOOLCHAIN_VERSION := 4.8" in shell/android/jni/Application.mk and shell/android/xperia/jni/Application.mk
* Android 4.4 (API 19) & Android 2.3.1 (API 9)
* Android 5.0.1 (API 21) & Android 2.3.1 (API 9)
- http://developer.android.com/sdk/installing/adding-packages.html
- note that API 9 is hidden (you must check to show obsolete in SDK manager)
* Ant
- http://ant.apache.org/
@ -35,7 +36,7 @@ export ANDROID_NDK=/ # Type the full path to your NDK here
cd shell/android/
android update project -p . --target "android-19"
android update project -p . --target "android-21"
ant debug
```
@ -58,6 +59,22 @@ cd shell/ios/
xcodebuild -configuration Release
```
Building for Linux
------------------
The code is only currently compatible with 32bit architectures so you will need
to install 32bit libs to build reicast.
Requirements:
* build-essential
* libasound
* libegl1-mesa-dev
* libgles2-mesa-dev
* libasound2-dev
To build the project, go to shell/lin86 and run make
Translations
------------
New and updated translations are always appreciated!
@ -68,7 +85,12 @@ Translations can be submitted as a pull request
Beta versions
-------------
Automated Git builds can be found at http://reicast.angelxwind.net/ for iOS and Android.
[![Build Status](https://travis-ci.org/reicast/reicast-emulator.svg?branch=master)](https://travis-ci.org/reicast/reicast-emulator)
Official builds from the reicast ci service can be found at http://builds.reicast.com/
Additional builds (iOS & android) can be found at [angelxwind's](http://reicast.angelxwind.net/) buildbot
Donations and stuff
-------------------
@ -76,7 +98,9 @@ Well, glad you liked the project so far!
We're currently short on hardware.
If you would like to donate some devices, get in touch at team@reicast.com. GLES3 capable stuff, some mainstream hardware and rarities would be extremely appreciated.
If you would like to donate some devices, get in touch at team@reicast.com.
GLES3 capable stuff, some mainstream hardware and rarities would be extremely
appreciated.
Keep in mind we're located in Greece for the most part
This has been tested/developed on
@ -88,44 +112,51 @@ This has been tested/developed on
* Various development boards
* GCW Zero
We had to buy all of these, except the GCW Zero and a BeagleBone which were donated (Thanks! You rock!)
We had to buy all of these, except the GCW Zero and a BeagleBone which were
donated (Thanks! You rock!)
Apart from that, we don't accept monetary donations right now.
We also don't plan to be releasing a premium version at any store.
Most of the project has been developed by drk||Raziel (aka, skmp, drk, Raz, etc) but it has been based on the
works of multiple people. It would be extremely unfair to charge for it and get all the credit :)
Most of the project has been developed by drk||Raziel (aka, skmp, drk, Raz,
etc) but it has been based on the works of multiple people. It would be
extremely unfair to charge for it and get all the credit :)
We're planning for an indiegogo campaign later on to help with sustained development and running costs, so follow @reicastdc on twitter for updates
We're planning for an indiegogo campaign later on to help with sustained
development and running costs, so follow @reicastdc on twitter for updates
Other Testing
-------------
These devices are tested by Karen/angelXwind:
Devices tested by the reicast team:
* Apple iPhone 4 GSM Rev0 (N90AP)
* Apple iPhone 4 CDMA (N92AP)
* Apple iPod touch 4 (N81AP)
* Apple iPod touch 3G (N18AP)
* Apple iPhone 3GS (N88AP)
* Apple iPhone 5s
* Apple iPad 3
* Sony Xperia X10a (es209ra)
* Amazon Kindle Fire HD 7 (tate-pvt-08)
These devices are tested by contributors regularly:
* Nvidia Shield
* Nexus 5 / 7
* Xperia Play
* Nvidia Shield portable
* Nvidia Shield tablet
* Samsung Galaxy Note 4
* LG Nexus 5
* Asus Nexus 7 (2013)
Team
----
You can check the currently active committers on [the pretty graphs page](https://github.com/reicast/reicast-emulator/graphs/contributors)
Our IRC channel is [#reicast @ chat.freenode.net](irc://chat.freenode.net/reicast).
Our IRC channel is [#reicast @ chat.freenode.net](irc://chat.freenode.net/reicast).
The original reicast team consisted of drk||Raziel (mostly just writing code), PsyMan (debugging/testing and everything else) and a little bit of gb_away
The original reicast team consisted of drk||Raziel (mostly just writing code),
PsyMan (debugging/testing and everything else) and a little bit of gb_away
Special thanks
--------------
In previous iterations a lot of people have worked on this, notably David Miller (aka, ZeZu), the nullDC team, friends from #pcsx2 and all over the world :)
In previous iterations a lot of people have worked on this, notably David
Miller (aka, ZeZu), the nullDC team, friends from #pcsx2 and all over the world :)
[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/reicast/reicast-emulator/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

398
WorkDir/webui/debugger.html Normal file
View File

@ -0,0 +1,398 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset=utf-8 http-equiv="Content-Language" content="en"/>
<title>Minimal Websocket test app</title>
<style type="text/css">
div.title { font-size:18pt; font: Arial; font-weight:normal; text-align:center; color:#000000; }
.browser { font-size:18pt; font: Arial; font-weight:normal; text-align:center; color:#ffff00; vertical-align:middle; text-align:center; background:#d0b070; padding:12px; -webkit-border-radius:10px; -moz-border-radius:10px; border-radius:10px;}
.group2 { width:600px; vertical-align:middle; text-align:center; background:#f0f0e0; padding:12px; -webkit-border-radius:10px; -moz-border-radius:10px; border-radius:10px; }
.explain { vertical-align:middle; text-align:center; background:#f0f0c0; padding:12px; -webkit-border-radius:10px; -moz-border-radius:10px; border-radius:10px; color:#404000; }
.content { vertical-align:top; text-align:center; background:#fffff0; padding:12px; -webkit-border-radius:10px; -moz-border-radius:10px; border-radius:10px; }
.canvas { vertical-align:top; text-align:center; background:#efefd0; padding:12px; -webkit-border-radius:10px; -moz-border-radius:10px; border-radius:10px; }
</style>
</head>
<body>
<header></header>
<article>
<table><tr><td>
<table width="100%"><tr><td valign=middle align=center><a href="http://libwebsockets.org"><img src="/libwebsockets.org-logo.png"></a></td><td>
<section class="browser">Detected Browser: <div id=brow>...</div></section></td></tr></table>
</td></tr><tr><td>
<section id="increment" class="group2">
<div class="title">libwebsockets "dumb-increment-protocol"</div>
<table><tr><td>
<table class="content" width="200px">
<tr><td align=center><input type=button id=offset value="Reset counter" onclick="reset();" ></td></tr>
<tr><td width=200px align=center><div id=number> </div></td></tr>
<tr><td id=wsdi_statustd align=center class="explain"><div id=wsdi_status>Not initialized</div></td></tr>
</tr>
</table>
</td><td class="explain">
The incrementing number is coming from the server and is individual for
each connection to the server... try opening a second browser window.
<br/><br/>
The button zeros just this connection's number.
<br/><br/>
</td></tr></table>
</section>
<br>
<section id="mirror" class="group2">
<div class="title">libwebsockets "lws-mirror-protocol"</div>
<div class="explain">
Use the mouse to draw on the canvas below -- all other browser windows open
on this page see your drawing in realtime and you can see any of theirs as
well.
<br/><br/>
The lws-mirror protocol doesn't interpret what is being sent to it, it just
re-sends it to every other websocket it has a connection with using that
protocol, including the guy who sent the packet.
<br/><br/>
<b>libwebsockets-test-client</b> joins in by spamming circles on to this shared canvas when
run.
</div>
<table class="content">
<tr>
<td>Drawing color:
<select id="color" onchange="update_color();">
<option value=#000000>Black</option>
<option value=#0000ff>Blue</option>
<option value=#20ff20>Green</option>
<option value=#802020>Dark Red</option>
</select>
</td>
<td id=wslm_statustd align=center class="explain"><div id=wslm_status>Not initialized</div></td>
</tr>
<tr>
<td colspan=2 width=500 class="content">
<div id="wslm_drawing">
</div></td>
</tr>
</table>
</section>
</td></tr><tr><td>
Looking for support? <a href="http://libwebsockets.org">http://libwebsockets.org</a><br/>
Join the mailing list: <a href="http://ml.libwebsockets.org/mailman/listinfo/libwebsockets">http://ml.libwebsockets.org/mailman/listinfo/libwebsockets</a>
</td></tr></table>
</article>
<script>
/* BrowserDetect came from http://www.quirksmode.org/js/detect.html */
var BrowserDetect = {
init: function () {
this.browser = this.searchString(this.dataBrowser) || "An unknown browser";
this.version = this.searchVersion(navigator.userAgent)
|| this.searchVersion(navigator.appVersion)
|| "an unknown version";
this.OS = this.searchString(this.dataOS) || "an unknown OS";
},
searchString: function (data) {
for (var i=0;i<data.length;i++) {
var dataString = data[i].string;
var dataProp = data[i].prop;
this.versionSearchString = data[i].versionSearch || data[i].identity;
if (dataString) {
if (dataString.indexOf(data[i].subString) != -1)
return data[i].identity;
}
else if (dataProp)
return data[i].identity;
}
},
searchVersion: function (dataString) {
var index = dataString.indexOf(this.versionSearchString);
if (index == -1) return;
return parseFloat(dataString.substring(index+this.versionSearchString.length+1));
},
dataBrowser: [
{
string: navigator.userAgent,
subString: "Chrome",
identity: "Chrome"
},
{ string: navigator.userAgent,
subString: "OmniWeb",
versionSearch: "OmniWeb/",
identity: "OmniWeb"
},
{
string: navigator.vendor,
subString: "Apple",
identity: "Safari",
versionSearch: "Version"
},
{
prop: window.opera,
identity: "Opera",
versionSearch: "Version"
},
{
string: navigator.vendor,
subString: "iCab",
identity: "iCab"
},
{
string: navigator.vendor,
subString: "KDE",
identity: "Konqueror"
},
{
string: navigator.userAgent,
subString: "Firefox",
identity: "Firefox"
},
{
string: navigator.vendor,
subString: "Camino",
identity: "Camino"
},
{ // for newer Netscapes (6+)
string: navigator.userAgent,
subString: "Netscape",
identity: "Netscape"
},
{
string: navigator.userAgent,
subString: "MSIE",
identity: "Explorer",
versionSearch: "MSIE"
},
{
string: navigator.userAgent,
subString: "Gecko",
identity: "Mozilla",
versionSearch: "rv"
},
{ // for older Netscapes (4-)
string: navigator.userAgent,
subString: "Mozilla",
identity: "Netscape",
versionSearch: "Mozilla"
}
],
dataOS : [
{
string: navigator.platform,
subString: "Win",
identity: "Windows"
},
{
string: navigator.platform,
subString: "Mac",
identity: "Mac"
},
{
string: navigator.userAgent,
subString: "iPhone",
identity: "iPhone/iPod"
},
{
string: navigator.platform,
subString: "Linux",
identity: "Linux"
}
]
};
BrowserDetect.init();
document.getElementById("brow").textContent = " " + BrowserDetect.browser + " "
+ BrowserDetect.version +" " + BrowserDetect.OS +" ";
var pos = 0;
function get_appropriate_ws_url()
{
var pcol;
var u = document.URL;
/*
* We open the websocket encrypted if this page came on an
* https:// url itself, otherwise unencrypted
*/
if (u.substring(0, 5) == "https") {
pcol = "wss://";
u = u.substr(8);
} else {
pcol = "ws://";
if (u.substring(0, 4) == "http")
u = u.substr(7);
}
u = u.split('/');
/* + "/xxx" bit is for IE10 workaround */
return pcol + u[0] + "/xxx";
}
document.getElementById("number").textContent = get_appropriate_ws_url();
/* dumb increment protocol */
var socket_di;
if (typeof MozWebSocket != "undefined") {
socket_di = new MozWebSocket(get_appropriate_ws_url(),
"dumb-increment-protocol");
} else {
socket_di = new WebSocket(get_appropriate_ws_url(),
"dumb-increment-protocol");
}
try {
socket_di.onopen = function() {
document.getElementById("wsdi_statustd").style.backgroundColor = "#40ff40";
document.getElementById("wsdi_status").textContent = " websocket connection opened ";
}
socket_di.onmessage =function got_packet(msg) {
document.getElementById("number").textContent = msg.data + "\n";
}
socket_di.onclose = function(){
document.getElementById("wsdi_statustd").style.backgroundColor = "#ff4040";
document.getElementById("wsdi_status").textContent = " websocket connection CLOSED ";
}
} catch(exception) {
alert('<p>Error' + exception);
}
function reset() {
socket_di.send("reset\n");
}
/* lws-mirror protocol */
var down = 0;
var no_last = 1;
var last_x = 0, last_y = 0;
var ctx;
var socket_lm;
var color = "#000000";
if (typeof MozWebSocket != "undefined") {
socket_lm = new MozWebSocket(get_appropriate_ws_url(),
"lws-mirror-protocol");
} else {
socket_lm = new WebSocket(get_appropriate_ws_url(),
"lws-mirror-protocol");
}
try {
socket_lm.onopen = function() {
document.getElementById("wslm_statustd").style.backgroundColor = "#40ff40";
document.getElementById("wslm_status").textContent = " websocket connection opened ";
}
socket_lm.onmessage =function got_packet(msg) {
j = msg.data.split(';');
f = 0;
while (f < j.length - 1) {
i = j[f].split(' ');
if (i[0] == 'd') {
ctx.strokeStyle = i[1];
ctx.beginPath();
ctx.moveTo(+(i[2]), +(i[3]));
ctx.lineTo(+(i[4]), +(i[5]));
ctx.stroke();
}
if (i[0] == 'c') {
ctx.strokeStyle = i[1];
ctx.beginPath();
ctx.arc(+(i[2]), +(i[3]), +(i[4]), 0, Math.PI*2, true);
ctx.stroke();
}
f++;
}
}
socket_lm.onclose = function(){
document.getElementById("wslm_statustd").style.backgroundColor = "#ff4040";
document.getElementById("wslm_status").textContent = " websocket connection CLOSED ";
}
} catch(exception) {
alert('<p>Error' + exception);
}
var canvas = document.createElement('canvas');
canvas.height = 300;
canvas.width = 480;
ctx = canvas.getContext("2d");
document.getElementById('wslm_drawing').appendChild(canvas);
canvas.addEventListener('mousemove', ev_mousemove, false);
canvas.addEventListener('mousedown', ev_mousedown, false);
canvas.addEventListener('mouseup', ev_mouseup, false);
offsetX = offsetY = 0;
element = canvas;
if (element.offsetParent) {
do {
offsetX += element.offsetLeft;
offsetY += element.offsetTop;
} while ((element = element.offsetParent));
}
function update_color() {
color = document.getElementById("color").value;
}
function ev_mousedown (ev) {
down = 1;
}
function ev_mouseup(ev) {
down = 0;
no_last = 1;
}
function ev_mousemove (ev) {
var x, y;
if (ev.offsetX) {
x = ev.offsetX;
y = ev.offsetY;
} else {
x = ev.layerX - offsetX;
y = ev.layerY - offsetY;
}
if (!down)
return;
if (no_last) {
no_last = 0;
last_x = x;
last_y = y;
return;
}
socket_lm.send("d " + color + " " + last_x + " " + last_y + " " + x + ' ' + y + ';');
last_x = x;
last_y = y;
}
</script>
</body>
</html>

BIN
WorkDir/webui/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -40,47 +40,46 @@
#define CPU_X86 0x20000001
#define CPU_ARM 0x20000002
#define CPU_MIPS 0x20000003
#define CPU_X64 0x20000004
//BUILD_COMPILER
#define COMPILER_VC 0x30000001
#define COMPILER_GCC 0x30000002
#ifdef _WIN32
#if defined(_WIN32) && !defined(TARGET_WIN86)
#define TARGET_WIN86
#endif
#ifdef __GNUC__
#define BUILD_COMPILER COMPILER_GCC
#else
#define BUILD_COMPILER COMPILER_VC
#endif
#ifdef TARGET_WIN86
#define HOST_OS OS_WINDOWS
#define HOST_CPU CPU_X86
#define BUILD_COMPILER COMPILER_VC
#elif TARGET_PANDORA
#define HOST_OS OS_LINUX
#define HOST_CPU CPU_ARM
#define BUILD_COMPILER COMPILER_GCC
#elif TARGET_LINUX_ARMELv7
#define HOST_OS OS_LINUX
#define HOST_CPU CPU_ARM
#define BUILD_COMPILER COMPILER_GCC
#elif TARGET_LINUX_x86
#define HOST_OS OS_LINUX
#define HOST_CPU CPU_X86
#define BUILD_COMPILER COMPILER_GCC
#elif TARGET_LINUX_MIPS
#define HOST_OS OS_LINUX
#define HOST_CPU CPU_MIPS
#define BUILD_COMPILER COMPILER_GCC
#elif TARGET_GCW0
#define HOST_OS OS_LINUX
#define HOST_CPU CPU_MIPS
#define BUILD_COMPILER COMPILER_GCC
#elif TARGET_NACL32
#define HOST_OS OS_LINUX
#define HOST_CPU CPU_X86
#define BUILD_COMPILER COMPILER_GCC
#elif TARGET_IPHONE
#define HOST_OS OS_DARWIN
#define HOST_CPU CPU_ARM
#define BUILD_COMPILER COMPILER_GCC
#else
#error Invalid Target: TARGET_* not defined
#endif

View File

@ -412,6 +412,26 @@ void cfgLoadStr(const wchar * Section, const wchar * Key, wchar * Return,const
}
}
string cfgLoadStr(const wchar * Section, const wchar * Key, const wchar* Default)
{
verify(Section != 0 && strlen(Section) != 0);
verify(Key != 0 && strlen(Key) != 0);
if (Default == 0)
Default = "";
ConfigSection* cs = cfgdb.GetEntry(Section);
ConfigEntry* ce = cs->FindEntry(Key);
if (!ce)
{
cs->SetEntry(Key, Default, CEM_SAVE);
return Default;
}
else
{
return ce->GetValue();
}
}
//These are helpers , mainly :)
s32 cfgLoadInt(const wchar * Section, const wchar * Key,s32 Default)
{

View File

@ -13,6 +13,7 @@ bool cfgOpen();
s32 cfgLoadInt(const wchar * lpSection, const wchar * lpKey,s32 Default);
void cfgSaveInt(const wchar * lpSection, const wchar * lpKey, s32 Int);
void cfgLoadStr(const wchar * lpSection, const wchar * lpKey, wchar * lpReturn,const wchar* lpDefault);
string cfgLoadStr(const wchar * Section, const wchar * Key, const wchar* Default);
void cfgSaveStr(const wchar * lpSection, const wchar * lpKey, const wchar * lpString);
s32 cfgExists(const wchar * Section, const wchar * Key);
void cfgSetVitual(const wchar * lpSection, const wchar * lpKey, const wchar * lpString);

View File

@ -126,6 +126,15 @@ bool ParseCommandLine(int argc,wchar* argv[])
cl-=as;
arg+=as;
}
else if (strstr(*arg, ".cdi") || strstr(*arg, ".chd")) {
printf("Using '%s' as cd image\n", *arg);
cfgSetVitual("config", "image", *arg);
}
else if (strstr(*arg, ".elf")) {
printf("Using '%s' as reios elf file\n", *arg);
cfgSetVitual("config", "reios.enabled", "1");
cfgSetVitual("reios", "ElfFile", *arg);
}
else
{
printf("wtf %s is suposed to do ?\n",*arg);

View File

@ -8,8 +8,18 @@ RZDCY_SRC_DIR ?= $(call my-dir)
RZDCY_MODULES := cfg/ hw/arm7/ hw/aica/ hw/holly/ hw/ hw/gdrom/ hw/maple/ \
hw/mem/ hw/pvr/ hw/sh4/ hw/sh4/interpr/ hw/sh4/modules/ plugins/ profiler/ oslib/ \
hw/extdev/ hw/arm/ imgread/ linux/ ./ deps/zlib/ deps/chdr/ deps/crypto/ deps/libelf/ deps/chdpsr/ arm_emitter/ \
deps/libzip/ deps/libpng/ rend/
hw/extdev/ hw/arm/ imgread/ linux/ ./ deps/coreio/ deps/zlib/ deps/chdr/ deps/crypto/ deps/libelf/ deps/chdpsr/ arm_emitter/ \
deps/libzip/ deps/libpng/ rend/ reios/
ifdef WEBUI
RZDCY_MODULES += webui/
RZDCY_MODULES += deps/libwebsocket/
ifdef FOR_ANDROID
RZDCY_MODULES += deps/ifaddrs/
endif
endif
ifndef NO_REC
RZDCY_MODULES += hw/sh4/dyna/
@ -46,31 +56,39 @@ RZDCY_FILES += $(foreach dir,$(addprefix $(RZDCY_SRC_DIR)/,$(RZDCY_MODULES)),$(w
RZDCY_FILES += $(foreach dir,$(addprefix $(RZDCY_SRC_DIR)/,$(RZDCY_MODULES)),$(wildcard $(dir)*.S))
ifdef FOR_PANDORA
RZDCY_CXXFLAGS := \
RZDCY_CFLAGS := \
$(CFLAGS) -c -g -O3 -I$(RZDCY_SRC_DIR) -I$(RZDCY_SRC_DIR)/deps \
-DRELEASE -DPANDORA\
-march=armv7-a -mtune=cortex-a8 -mfpu=neon -mfloat-abi=softfp \
-frename-registers -fsingle-precision-constant -ffast-math \
-ftree-vectorize -fomit-frame-pointer -fno-exceptions -fno-rtti -std=gnu++11
-ftree-vectorize -fomit-frame-pointer
RZDCY_CFLAGS += -march=armv7-a -mtune=cortex-a8 -mfpu=neon
RZDCY_CFLAGS += -DTARGET_LINUX_ARMELv7
else
RZDCY_CXXFLAGS := \
RZDCY_CFLAGS := \
$(CFLAGS) -c -g -O3 -I$(RZDCY_SRC_DIR) -I$(RZDCY_SRC_DIR)/deps \
-D_ANDROID -DRELEASE\
-frename-registers -fsingle-precision-constant -ffast-math \
-ftree-vectorize -fomit-frame-pointer -fno-exceptions -fno-rtti -std=gnu++11
-ftree-vectorize -fomit-frame-pointer
ifndef NOT_ARM
RZDCY_CXXFLAGS += -march=armv7-a -mtune=cortex-a9 -mfpu=vfpv3-d16
RZDCY_CXXFLAGS += -DTARGET_LINUX_ARMELv7
RZDCY_CFLAGS += -march=armv7-a -mtune=cortex-a9 -mfpu=vfpv3-d16
RZDCY_CFLAGS += -DTARGET_LINUX_ARMELv7
else
ifndef ISMIPS
RZDCY_CXXFLAGS += -DTARGET_LINUX_x86
RZDCY_CFLAGS += -DTARGET_LINUX_x86
else
RZDCY_CXXFLAGS += -DTARGET_LINUX_MIPS
RZDCY_CFLAGS += -DTARGET_LINUX_MIPS
endif
endif
endif
ifdef NO_REC
RZDCY_CXXFLAGS += -DHOST_NO_REC
RZDCY_CFLAGS += -DHOST_NO_REC
endif
ifndef DESKTOPGL
RZDCY_CFLAGS += -DGLES
endif
RZDCY_CXXFLAGS := $(RZDCY_CFLAGS) -fno-exceptions -fno-rtti -std=gnu++11

View File

@ -2,6 +2,7 @@
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "deps/coreio/coreio.h"
#include "cdipsr.h"
// Global variables
@ -11,6 +12,10 @@ unsigned long temp_value;
/////////////////////////////////////////////////////////////////////////////
#define FILE core_file
#define fread(buff,sz,cnt,fc) core_fread(fc,buff,sz*cnt)
#define fseek core_fseek
#define ftell core_ftell
unsigned long ask_type(FILE *fsource, long header_position)
{
@ -42,8 +47,8 @@ unsigned long track_mode;
void CDI_read_track (FILE *fsource, image_s *image, track_s *track)
{
char TRACK_START_MARK[10] = { 0, 0, 0x01, 0, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF };
char current_start_mark[10];
unsigned char TRACK_START_MARK[10] = { 0, 0, 0x01, 0, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF };
unsigned char current_start_mark[10];
fread(&temp_value, 4, 1, fsource);
if (temp_value != 0)
@ -110,8 +115,7 @@ void CDI_get_tracks (FILE *fsource, image_s *image)
void CDI_init (FILE *fsource, image_s *image, char *fsourcename)
{
fseek(fsource, 0L, SEEK_END);
image->length = ftell(fsource);
image->length = core_fsize(fsource);
if (image->length < 8) printf( "Image file is too short");

View File

@ -1,7 +1,7 @@
#ifndef __CDI_H__
#define __CDI_H__
#include "deps/coreio/coreio.h"
/* Basic structures */
@ -38,12 +38,11 @@ typedef struct track_s
#define CDI_V3 0x80000005
#define CDI_V35 0x80000006
unsigned long ask_type(FILE *fsource, long header_position);
void CDI_init (FILE *fsource, image_s *image, char *fsourcename);
void CDI_get_sessions (FILE *fsource, image_s *image);
void CDI_get_tracks (FILE *fsource, image_s *image);
void CDI_read_track (FILE *fsource, image_s *image, track_s *track);
void CDI_skip_next_session (FILE *fsource, image_s *image);
unsigned long ask_type(core_file *fsource, long header_position);
void CDI_init(core_file *fsource, image_s *image, char *fsourcename);
void CDI_get_sessions(core_file *fsource, image_s *image);
void CDI_get_tracks(core_file *fsource, image_s *image);
void CDI_read_track(core_file *fsource, image_s *image, track_s *track);
void CDI_skip_next_session(core_file *fsource, image_s *image);
#endif

View File

@ -43,6 +43,7 @@
#define __CHD_H__
#include "coretypes.h"
#include "deps/coreio/coreio.h"
/***************************************************************************

View File

@ -302,22 +302,6 @@ static const codec_interface codec_interfaces[] =
},
};
int core_fread(FILE* f, void* buff, size_t len)
{
return fread(buff,1,len,f);
}
size_t core_fsize(FILE* f)
{
size_t p=ftell(f);
fseek(f,0,SEEK_END);
size_t rv=ftell(f);
fseek(f,p,SEEK_SET);
return rv;
}
#define core_fseek fseek
#define MIN min
#define MAX max
/***************************************************************************
@ -593,7 +577,7 @@ chd_error chd_open(const wchar *filename, int mode, chd_file *parent, chd_file *
}
/* open the file */
file=fopen(filename, "rb");
file=core_fopen(filename);
if (file == 0)
{
err = CHDERR_FILE_NOT_FOUND;
@ -610,7 +594,7 @@ chd_error chd_open(const wchar *filename, int mode, chd_file *parent, chd_file *
cleanup:
if ((err != CHDERR_NONE) && (file != NULL))
fclose(file);
core_fclose(file);
return err;
}
@ -653,7 +637,7 @@ void chd_close(chd_file *chd)
/* close the file */
if (chd->owns_file && chd->file != NULL)
fclose(chd->file);
core_fclose(chd->file);
if (PRINTF_MAX_HUNK) printf("Max hunk = %d/%d\n", chd->maxhunk, chd->header.totalhunks);
@ -807,7 +791,7 @@ chd_error chd_get_metadata(chd_file *chd, UINT32 searchtag, UINT32 searchindex,
/* read the metadata */
outputlen = min(outputlen, metaentry.length);
fseek(chd->file, metaentry.offset + METADATA_HEADER_SIZE, SEEK_SET);
core_fseek(chd->file, metaentry.offset + METADATA_HEADER_SIZE, SEEK_SET);
count = core_fread(chd->file, output, outputlen);
if (count != outputlen)
return CHDERR_READ_ERROR;

View File

@ -15,6 +15,4 @@ typedef s8 INT8;
#define INLINE inline
#endif
#define core_file FILE
#define ARRAY_LENGTH(x) (sizeof(x)/sizeof(x[0]))

298
core/deps/coreio/coreio.cpp Normal file
View File

@ -0,0 +1,298 @@
#include "coreio.h"
#include <time.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <sstream>
#include <string>
#include <iomanip>
#include <cctype>
#define TRUE 1
#define FALSE 0
#pragma comment (lib, "wsock32.lib")
#include <string>
#include <sstream>
#if HOST_OS == OS_LINUX
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netdb.h>
#include <unistd.h>
#endif
string url_encode(const string &value) {
ostringstream escaped;
escaped.fill('0');
escaped << hex;
for (string::const_iterator i = value.begin(), n = value.end(); i != n; ++i) {
string::value_type c = (*i);
// Keep alphanumeric and other accepted characters intact
if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~' || c == '/' || c =='%' ) {
escaped << c;
continue;
}
// Any other characters are percent-encoded
escaped << '%' << setw(2) << int((unsigned char)c);
}
return escaped.str();
}
size_t HTTP_GET(string host, int port,string path, size_t offs, size_t len, void* pdata){
string request;
string response;
struct sockaddr_in serveraddr;
int sock;
std::stringstream request2;
if (len) {
request2 << "GET " << path << " HTTP/1.1"<<endl;
request2 << "User-Agent: reicastdc" << endl;
//request2 << "" << endl;
request2 << "Host: " << host << endl;
request2 << "Accept: */*" << endl;
request2 << "Range: bytes=" << offs << "-" << (offs + len-1) << endl;
request2 << endl;
}
else {
request2 << "HEAD " << path << " HTTP/1.1"<<endl;
request2 << "User-Agent: reicastdc" << endl;
//request2 << "" << endl;
request2 << "Host: " << host << endl;
request2 << endl;
}
request = request2.str();
//init winsock
static bool init = false;
if (!init) {
#if HOST_OS == OS_WINDOWS
static WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 0), &wsaData) != 0)
die("WSAStartup fail");
#endif
init = true;
}
//open socket
if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
return -1;
//connect
memset(&serveraddr, 0, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = *(int*)gethostbyname( host.c_str() )->h_addr_list[0];
serveraddr.sin_port = htons((unsigned short) port);
if (connect(sock, (struct sockaddr *) &serveraddr, sizeof(serveraddr)) < 0)
return -1;
#if HOST_OS == OS_WINDOWS
BOOL v = TRUE;
#else
int v = 1;
#endif
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (const char*)&v, sizeof(v));
//send request
if (send(sock, request.c_str(), request.length(), 0) != request.length())
return -1;
/*
//get response
response = "";
resp_leng= BUFFERSIZE;
*/
/*
parse headers ..
*/
size_t content_length = 0;
size_t rv = 0;
for (;;) {
stringstream ss;
for (;;) {
char t;
if (recv(sock, &t, 1, 0) <= 0)
goto _cleanup;
if (t != '\n'){
ss << t;
continue;
}
string ln = ss.str();
if (ln.size() == 1)
goto _data;
string CL = "Content-Length:";
if (ln.substr(0, CL.size()) == CL) {
sscanf(ln.substr(CL.size(), ln.npos).c_str(),"%d", &content_length);
}
break;
}
}
_data:
if (len == 0) {
rv = content_length;
}
else {
verify(len == content_length); //crash is a bit too harsh here perhaps?
u8* ptr = (u8*)pdata;
do
{
int rcv = recv(sock, (char*)ptr, len, 0);
verify(rcv > 0 && len>= rcv);
len -= rcv;
ptr += rcv;
}
while (len >0);
rv = content_length;
}
_cleanup:
//disconnect
#if HOST_OS == OS_WINDOWS
closesocket(sock);
#else
close(sock);
#endif
/*
//cleanup
WSACleanup();
*/
return rv;
}
struct CORE_FILE {
FILE* f;
string path;
size_t seek_ptr;
string host;
int port;
};
core_file* core_fopen(const char* filename)
{
string p = filename;
CORE_FILE* rv = new CORE_FILE();
rv->f = 0;
rv->path = p;
if (p.substr(0,7)=="http://") {
rv->host = p.substr(7,p.npos);
rv->host = rv->host.substr(0, rv->host.find_first_of("/"));
rv->path = url_encode(p.substr(p.find("/", 7), p.npos));
rv->port = 80;
size_t pos = rv->host.find_first_of(":");
if (pos != rv->host.npos) {
string port = rv->host.substr(pos, rv->host.npos );
rv->host = rv->host.substr(0, rv->host.find_first_of(":"));
sscanf(port.c_str(),"%d",&rv->port);
}
} else {
rv->f = fopen(filename, "rb");
if (!rv->f) {
delete rv;
return 0;
}
}
core_fseek((core_file*)rv, 0, SEEK_SET);
return (core_file*)rv;
}
size_t core_fseek(core_file* fc, size_t offs, size_t origin) {
CORE_FILE* f = (CORE_FILE*)fc;
if (origin == SEEK_SET)
f->seek_ptr = offs;
else if (origin == SEEK_CUR)
f->seek_ptr += offs;
else
die("Invalid code path");
if (f->f)
fseek(f->f, f->seek_ptr, SEEK_SET);
return 0;
}
size_t core_ftell(core_file* fc) {
CORE_FILE* f = (CORE_FILE*)fc;
return f->seek_ptr;
}
int core_fread(core_file* fc, void* buff, size_t len)
{
CORE_FILE* f = (CORE_FILE*)fc;
if (f->f) {
fread(buff,1,len,f->f);
} else {
HTTP_GET(f->host, f->port, f->path, f->seek_ptr, len, buff);
}
f->seek_ptr += len;
return len;
}
int core_fclose(core_file* fc)
{
CORE_FILE* f = (CORE_FILE*)fc;
if (f->f) {
fclose(f->f);
}
else {
}
delete f;
return 0;
}
size_t core_fsize(core_file* fc)
{
CORE_FILE* f = (CORE_FILE*)fc;
if (f->f) {
size_t p=ftell(f->f);
fseek(f->f,0,SEEK_END);
size_t rv=ftell(f->f);
fseek(f->f,p,SEEK_SET);
return rv;
}
else {
return HTTP_GET(f->host, f->port, f->path, 0, 0,0);
}
}

13
core/deps/coreio/coreio.h Normal file
View File

@ -0,0 +1,13 @@
#pragma once
#include "types.h"
//#define core_file FILE
typedef void* core_file;
core_file* core_fopen(const char* filename);
size_t core_fseek(core_file* fc, size_t offs, size_t origin);
int core_fread(core_file* fc, void* buff, size_t len);
int core_fclose(core_file* fc);
size_t core_fsize(core_file* fc);
size_t core_ftell(core_file* fc);

161
core/deps/crypto/sha256.cpp Normal file
View File

@ -0,0 +1,161 @@
/*********************************************************************
* Filename: sha256.c
* Author: Brad Conte (brad AT bradconte.com)
* Copyright:
* Disclaimer: This code is presented "as is" without any guarantees.
* Details: Implementation of the SHA-256 hashing algorithm.
SHA-256 is one of the three algorithms in the SHA2
specification. The others, SHA-384 and SHA-512, are not
offered in this implementation.
Algorithm specification can be found here:
* http://csrc.nist.gov/publications/fips/fips180-2/fips180-2withchangenotice.pdf
This implementation uses little endian byte order.
*********************************************************************/
/*************************** HEADER FILES ***************************/
#include <stdlib.h>
#include <memory.h>
#include "sha256.h"
#define WORD uint32_t
#define BYTE uint8_t
/****************************** MACROS ******************************/
#define ROTLEFT(a,b) (((a) << (b)) | ((a) >> (32-(b))))
#define ROTRIGHT(a,b) (((a) >> (b)) | ((a) << (32-(b))))
#define CH(x,y,z) (((x) & (y)) ^ (~(x) & (z)))
#define MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
#define EP0(x) (ROTRIGHT(x,2) ^ ROTRIGHT(x,13) ^ ROTRIGHT(x,22))
#define EP1(x) (ROTRIGHT(x,6) ^ ROTRIGHT(x,11) ^ ROTRIGHT(x,25))
#define SIG0(x) (ROTRIGHT(x,7) ^ ROTRIGHT(x,18) ^ ((x) >> 3))
#define SIG1(x) (ROTRIGHT(x,17) ^ ROTRIGHT(x,19) ^ ((x) >> 10))
/**************************** VARIABLES *****************************/
static const WORD k[64] = {
0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5,
0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174,
0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da,
0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967,
0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85,
0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070,
0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3,
0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2
};
/*********************** FUNCTION DEFINITIONS ***********************/
void sha256_transform(SHA256_CTX *ctx, const BYTE data[])
{
WORD a, b, c, d, e, f, g, h, i, j, t1, t2, m[64];
for (i = 0, j = 0; i < 16; ++i, j += 4)
m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]);
for ( ; i < 64; ++i)
m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16];
a = ctx->state[0];
b = ctx->state[1];
c = ctx->state[2];
d = ctx->state[3];
e = ctx->state[4];
f = ctx->state[5];
g = ctx->state[6];
h = ctx->state[7];
for (i = 0; i < 64; ++i) {
t1 = h + EP1(e) + CH(e,f,g) + k[i] + m[i];
t2 = EP0(a) + MAJ(a,b,c);
h = g;
g = f;
f = e;
e = d + t1;
d = c;
c = b;
b = a;
a = t1 + t2;
}
ctx->state[0] += a;
ctx->state[1] += b;
ctx->state[2] += c;
ctx->state[3] += d;
ctx->state[4] += e;
ctx->state[5] += f;
ctx->state[6] += g;
ctx->state[7] += h;
}
void sha256_init(SHA256_CTX *ctx)
{
ctx->datalen = 0;
ctx->bitlen = 0;
ctx->state[0] = 0x6a09e667;
ctx->state[1] = 0xbb67ae85;
ctx->state[2] = 0x3c6ef372;
ctx->state[3] = 0xa54ff53a;
ctx->state[4] = 0x510e527f;
ctx->state[5] = 0x9b05688c;
ctx->state[6] = 0x1f83d9ab;
ctx->state[7] = 0x5be0cd19;
}
void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len)
{
WORD i;
for (i = 0; i < len; ++i) {
ctx->data[ctx->datalen] = data[i];
ctx->datalen++;
if (ctx->datalen == 64) {
sha256_transform(ctx, ctx->data);
ctx->bitlen += 512;
ctx->datalen = 0;
}
}
}
void sha256_final(SHA256_CTX *ctx, BYTE hash[])
{
WORD i;
i = ctx->datalen;
// Pad whatever data is left in the buffer.
if (ctx->datalen < 56) {
ctx->data[i++] = 0x80;
while (i < 56)
ctx->data[i++] = 0x00;
}
else {
ctx->data[i++] = 0x80;
while (i < 64)
ctx->data[i++] = 0x00;
sha256_transform(ctx, ctx->data);
memset(ctx->data, 0, 56);
}
// Append to the padding the total message's length in bits and transform.
ctx->bitlen += ctx->datalen * 8;
ctx->data[63] = (BYTE)(ctx->bitlen);
ctx->data[62] = (BYTE)(ctx->bitlen >> 8);
ctx->data[61] = (BYTE)(ctx->bitlen >> 16);
ctx->data[60] = (BYTE)(ctx->bitlen >> 24);
ctx->data[59] = (BYTE)(ctx->bitlen >> 32);
ctx->data[58] = (BYTE)(ctx->bitlen >> 40);
ctx->data[57] = (BYTE)(ctx->bitlen >> 48);
ctx->data[56] = (BYTE)(ctx->bitlen >> 56);
sha256_transform(ctx, ctx->data);
// Since this implementation uses little endian byte ordering and SHA uses big endian,
// reverse all the bytes when copying the final state to the output hash.
for (i = 0; i < 4; ++i) {
hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff;
hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff;
hash[i + 8] = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff;
hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff;
hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff;
hash[i + 20] = (ctx->state[5] >> (24 - i * 8)) & 0x000000ff;
hash[i + 24] = (ctx->state[6] >> (24 - i * 8)) & 0x000000ff;
hash[i + 28] = (ctx->state[7] >> (24 - i * 8)) & 0x000000ff;
}
}

37
core/deps/crypto/sha256.h Normal file
View File

@ -0,0 +1,37 @@
/*********************************************************************
* Filename: sha256.h
* Author: Brad Conte (brad AT bradconte.com)
* Copyright:
* Disclaimer: This code is presented "as is" without any guarantees.
* Details: Defines the API for the corresponding SHA1 implementation.
*********************************************************************/
#ifndef SHA256_H
#define SHA256_H
/*************************** HEADER FILES ***************************/
#include <stddef.h>
#include <stdint.h>
/****************************** MACROS ******************************/
#define SHA256_BLOCK_SIZE 32 // SHA256 outputs a 32 byte digest
/**************************** DATA TYPES ****************************/
/*
typedef uint8_t BYTE; // 8-bit byte
typedef uint32_t WORD; // 32-bit word, change to "long" for 16-bit machines
*/
typedef struct {
uint8_t data[64];
uint32_t datalen;
uint64_t bitlen;
uint32_t state[8];
} SHA256_CTX;
/*********************** FUNCTION DECLARATIONS **********************/
void sha256_init(SHA256_CTX *ctx);
void sha256_update(SHA256_CTX *ctx, const uint8_t data[], size_t len);
void sha256_final(SHA256_CTX *ctx, uint8_t hash[]);
#endif // SHA256_H

View File

@ -0,0 +1,6 @@
android-ifaddrs
===============
An implementation of getifaddrs() for Android, since the NDK does not natively support it.
Works just like you would expect on regular Linux. License information is present in each file (BSD license).

658
core/deps/ifaddrs/ifaddrs.c Normal file
View File

@ -0,0 +1,658 @@
/*
Copyright (c) 2013, Kenneth MacKay
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "ifaddrs.h"
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/socket.h>
#include <net/if_arp.h>
#include <netinet/in.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
typedef struct NetlinkList
{
struct NetlinkList *m_next;
struct nlmsghdr *m_data;
unsigned int m_size;
} NetlinkList;
static int netlink_socket(void)
{
int l_socket = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if(l_socket < 0)
{
return -1;
}
struct sockaddr_nl l_addr;
memset(&l_addr, 0, sizeof(l_addr));
l_addr.nl_family = AF_NETLINK;
if(bind(l_socket, (struct sockaddr *)&l_addr, sizeof(l_addr)) < 0)
{
close(l_socket);
return -1;
}
return l_socket;
}
static int netlink_send(int p_socket, int p_request)
{
char l_buffer[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + NLMSG_ALIGN(sizeof(struct rtgenmsg))];
memset(l_buffer, 0, sizeof(l_buffer));
struct nlmsghdr *l_hdr = (struct nlmsghdr *)l_buffer;
struct rtgenmsg *l_msg = (struct rtgenmsg *)NLMSG_DATA(l_hdr);
l_hdr->nlmsg_len = NLMSG_LENGTH(sizeof(*l_msg));
l_hdr->nlmsg_type = p_request;
l_hdr->nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;
l_hdr->nlmsg_pid = 0;
l_hdr->nlmsg_seq = p_socket;
l_msg->rtgen_family = AF_UNSPEC;
struct sockaddr_nl l_addr;
memset(&l_addr, 0, sizeof(l_addr));
l_addr.nl_family = AF_NETLINK;
return (sendto(p_socket, l_hdr, l_hdr->nlmsg_len, 0, (struct sockaddr *)&l_addr, sizeof(l_addr)));
}
static int netlink_recv(int p_socket, void *p_buffer, size_t p_len)
{
struct msghdr l_msg;
struct iovec l_iov = { p_buffer, p_len };
struct sockaddr_nl l_addr;
int l_result;
for(;;)
{
l_msg.msg_name = (void *)&l_addr;
l_msg.msg_namelen = sizeof(l_addr);
l_msg.msg_iov = &l_iov;
l_msg.msg_iovlen = 1;
l_msg.msg_control = NULL;
l_msg.msg_controllen = 0;
l_msg.msg_flags = 0;
int l_result = recvmsg(p_socket, &l_msg, 0);
if(l_result < 0)
{
if(errno == EINTR)
{
continue;
}
return -2;
}
if(l_msg.msg_flags & MSG_TRUNC)
{ // buffer was too small
return -1;
}
return l_result;
}
}
static struct nlmsghdr *getNetlinkResponse(int p_socket, int *p_size, int *p_done)
{
size_t l_size = 4096;
void *l_buffer = NULL;
for(;;)
{
free(l_buffer);
l_buffer = malloc(l_size);
if (l_buffer == NULL)
{
return NULL;
}
int l_read = netlink_recv(p_socket, l_buffer, l_size);
*p_size = l_read;
if(l_read == -2)
{
free(l_buffer);
return NULL;
}
if(l_read >= 0)
{
pid_t l_pid = getpid();
struct nlmsghdr *l_hdr;
for(l_hdr = (struct nlmsghdr *)l_buffer; NLMSG_OK(l_hdr, (unsigned int)l_read); l_hdr = (struct nlmsghdr *)NLMSG_NEXT(l_hdr, l_read))
{
if((pid_t)l_hdr->nlmsg_pid != l_pid || (int)l_hdr->nlmsg_seq != p_socket)
{
continue;
}
if(l_hdr->nlmsg_type == NLMSG_DONE)
{
*p_done = 1;
break;
}
if(l_hdr->nlmsg_type == NLMSG_ERROR)
{
free(l_buffer);
return NULL;
}
}
return l_buffer;
}
l_size *= 2;
}
}
static NetlinkList *newListItem(struct nlmsghdr *p_data, unsigned int p_size)
{
NetlinkList *l_item = malloc(sizeof(NetlinkList));
if (l_item == NULL)
{
return NULL;
}
l_item->m_next = NULL;
l_item->m_data = p_data;
l_item->m_size = p_size;
return l_item;
}
static void freeResultList(NetlinkList *p_list)
{
NetlinkList *l_cur;
while(p_list)
{
l_cur = p_list;
p_list = p_list->m_next;
free(l_cur->m_data);
free(l_cur);
}
}
static NetlinkList *getResultList(int p_socket, int p_request)
{
if(netlink_send(p_socket, p_request) < 0)
{
return NULL;
}
NetlinkList *l_list = NULL;
NetlinkList *l_end = NULL;
int l_size;
int l_done = 0;
while(!l_done)
{
struct nlmsghdr *l_hdr = getNetlinkResponse(p_socket, &l_size, &l_done);
if(!l_hdr)
{ // error
freeResultList(l_list);
return NULL;
}
NetlinkList *l_item = newListItem(l_hdr, l_size);
if (!l_item)
{
freeResultList(l_list);
return NULL;
}
if(!l_list)
{
l_list = l_item;
}
else
{
l_end->m_next = l_item;
}
l_end = l_item;
}
return l_list;
}
static size_t maxSize(size_t a, size_t b)
{
return (a > b ? a : b);
}
static size_t calcAddrLen(sa_family_t p_family, int p_dataSize)
{
switch(p_family)
{
case AF_INET:
return sizeof(struct sockaddr_in);
case AF_INET6:
return sizeof(struct sockaddr_in6);
case AF_PACKET:
return maxSize(sizeof(struct sockaddr_ll), offsetof(struct sockaddr_ll, sll_addr) + p_dataSize);
default:
return maxSize(sizeof(struct sockaddr), offsetof(struct sockaddr, sa_data) + p_dataSize);
}
}
static void makeSockaddr(sa_family_t p_family, struct sockaddr *p_dest, void *p_data, size_t p_size)
{
switch(p_family)
{
case AF_INET:
memcpy(&((struct sockaddr_in*)p_dest)->sin_addr, p_data, p_size);
break;
case AF_INET6:
memcpy(&((struct sockaddr_in6*)p_dest)->sin6_addr, p_data, p_size);
break;
case AF_PACKET:
memcpy(((struct sockaddr_ll*)p_dest)->sll_addr, p_data, p_size);
((struct sockaddr_ll*)p_dest)->sll_halen = p_size;
break;
default:
memcpy(p_dest->sa_data, p_data, p_size);
break;
}
p_dest->sa_family = p_family;
}
static void addToEnd(struct ifaddrs **p_resultList, struct ifaddrs *p_entry)
{
if(!*p_resultList)
{
*p_resultList = p_entry;
}
else
{
struct ifaddrs *l_cur = *p_resultList;
while(l_cur->ifa_next)
{
l_cur = l_cur->ifa_next;
}
l_cur->ifa_next = p_entry;
}
}
static int interpretLink(struct nlmsghdr *p_hdr, struct ifaddrs **p_resultList)
{
struct ifinfomsg *l_info = (struct ifinfomsg *)NLMSG_DATA(p_hdr);
size_t l_nameSize = 0;
size_t l_addrSize = 0;
size_t l_dataSize = 0;
size_t l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifinfomsg));
struct rtattr *l_rta;
for(l_rta = IFLA_RTA(l_info); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize))
{
void *l_rtaData = RTA_DATA(l_rta);
size_t l_rtaDataSize = RTA_PAYLOAD(l_rta);
switch(l_rta->rta_type)
{
case IFLA_ADDRESS:
case IFLA_BROADCAST:
l_addrSize += NLMSG_ALIGN(calcAddrLen(AF_PACKET, l_rtaDataSize));
break;
case IFLA_IFNAME:
l_nameSize += NLMSG_ALIGN(l_rtaSize + 1);
break;
case IFLA_STATS:
l_dataSize += NLMSG_ALIGN(l_rtaSize);
break;
default:
break;
}
}
struct ifaddrs *l_entry = malloc(sizeof(struct ifaddrs) + sizeof(int) + l_nameSize + l_addrSize + l_dataSize);
if (l_entry == NULL)
{
return -1;
}
memset(l_entry, 0, sizeof(struct ifaddrs));
l_entry->ifa_name = "";
char *l_index = ((char *)l_entry) + sizeof(struct ifaddrs);
char *l_name = l_index + sizeof(int);
char *l_addr = l_name + l_nameSize;
char *l_data = l_addr + l_addrSize;
// save the interface index so we can look it up when handling the addresses.
memcpy(l_index, &l_info->ifi_index, sizeof(int));
l_entry->ifa_flags = l_info->ifi_flags;
l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifinfomsg));
for(l_rta = IFLA_RTA(l_info); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize))
{
void *l_rtaData = RTA_DATA(l_rta);
size_t l_rtaDataSize = RTA_PAYLOAD(l_rta);
switch(l_rta->rta_type)
{
case IFLA_ADDRESS:
case IFLA_BROADCAST:
{
size_t l_addrLen = calcAddrLen(AF_PACKET, l_rtaDataSize);
makeSockaddr(AF_PACKET, (struct sockaddr *)l_addr, l_rtaData, l_rtaDataSize);
((struct sockaddr_ll *)l_addr)->sll_ifindex = l_info->ifi_index;
((struct sockaddr_ll *)l_addr)->sll_hatype = l_info->ifi_type;
if(l_rta->rta_type == IFLA_ADDRESS)
{
l_entry->ifa_addr = (struct sockaddr *)l_addr;
}
else
{
l_entry->ifa_broadaddr = (struct sockaddr *)l_addr;
}
l_addr += NLMSG_ALIGN(l_addrLen);
break;
}
case IFLA_IFNAME:
strncpy(l_name, l_rtaData, l_rtaDataSize);
l_name[l_rtaDataSize] = '\0';
l_entry->ifa_name = l_name;
break;
case IFLA_STATS:
memcpy(l_data, l_rtaData, l_rtaDataSize);
l_entry->ifa_data = l_data;
break;
default:
break;
}
}
addToEnd(p_resultList, l_entry);
return 0;
}
static struct ifaddrs *findInterface(int p_index, struct ifaddrs **p_links, int p_numLinks)
{
int l_num = 0;
struct ifaddrs *l_cur = *p_links;
while(l_cur && l_num < p_numLinks)
{
char *l_indexPtr = ((char *)l_cur) + sizeof(struct ifaddrs);
int l_index;
memcpy(&l_index, l_indexPtr, sizeof(int));
if(l_index == p_index)
{
return l_cur;
}
l_cur = l_cur->ifa_next;
++l_num;
}
return NULL;
}
static int interpretAddr(struct nlmsghdr *p_hdr, struct ifaddrs **p_resultList, int p_numLinks)
{
struct ifaddrmsg *l_info = (struct ifaddrmsg *)NLMSG_DATA(p_hdr);
struct ifaddrs *l_interface = findInterface(l_info->ifa_index, p_resultList, p_numLinks);
size_t l_nameSize = 0;
size_t l_addrSize = 0;
int l_addedNetmask = 0;
size_t l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifaddrmsg));
struct rtattr *l_rta;
for(l_rta = IFLA_RTA(l_info); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize))
{
void *l_rtaData = RTA_DATA(l_rta);
size_t l_rtaDataSize = RTA_PAYLOAD(l_rta);
if(l_info->ifa_family == AF_PACKET)
{
continue;
}
switch(l_rta->rta_type)
{
case IFA_ADDRESS:
case IFA_LOCAL:
if((l_info->ifa_family == AF_INET || l_info->ifa_family == AF_INET6) && !l_addedNetmask)
{ // make room for netmask
l_addrSize += NLMSG_ALIGN(calcAddrLen(l_info->ifa_family, l_rtaDataSize));
l_addedNetmask = 1;
}
case IFA_BROADCAST:
l_addrSize += NLMSG_ALIGN(calcAddrLen(l_info->ifa_family, l_rtaDataSize));
break;
case IFA_LABEL:
l_nameSize += NLMSG_ALIGN(l_rtaSize + 1);
break;
default:
break;
}
}
struct ifaddrs *l_entry = malloc(sizeof(struct ifaddrs) + l_nameSize + l_addrSize);
if (l_entry == NULL)
{
return -1;
}
memset(l_entry, 0, sizeof(struct ifaddrs));
l_entry->ifa_name = (l_interface ? l_interface->ifa_name : "");
char *l_name = ((char *)l_entry) + sizeof(struct ifaddrs);
char *l_addr = l_name + l_nameSize;
l_entry->ifa_flags = l_info->ifa_flags;
if(l_interface)
{
l_entry->ifa_flags |= l_interface->ifa_flags;
}
l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifaddrmsg));
for(l_rta = IFLA_RTA(l_info); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize))
{
void *l_rtaData = RTA_DATA(l_rta);
size_t l_rtaDataSize = RTA_PAYLOAD(l_rta);
switch(l_rta->rta_type)
{
case IFA_ADDRESS:
case IFA_BROADCAST:
case IFA_LOCAL:
{
size_t l_addrLen = calcAddrLen(l_info->ifa_family, l_rtaDataSize);
makeSockaddr(l_info->ifa_family, (struct sockaddr *)l_addr, l_rtaData, l_rtaDataSize);
if(l_info->ifa_family == AF_INET6)
{
if(IN6_IS_ADDR_LINKLOCAL((struct in6_addr *)l_rtaData) || IN6_IS_ADDR_MC_LINKLOCAL((struct in6_addr *)l_rtaData))
{
((struct sockaddr_in6 *)l_addr)->sin6_scope_id = l_info->ifa_index;
}
}
if(l_rta->rta_type == IFA_ADDRESS)
{ // apparently in a point-to-point network IFA_ADDRESS contains the dest address and IFA_LOCAL contains the local address
if(l_entry->ifa_addr)
{
l_entry->ifa_dstaddr = (struct sockaddr *)l_addr;
}
else
{
l_entry->ifa_addr = (struct sockaddr *)l_addr;
}
}
else if(l_rta->rta_type == IFA_LOCAL)
{
if(l_entry->ifa_addr)
{
l_entry->ifa_dstaddr = l_entry->ifa_addr;
}
l_entry->ifa_addr = (struct sockaddr *)l_addr;
}
else
{
l_entry->ifa_broadaddr = (struct sockaddr *)l_addr;
}
l_addr += NLMSG_ALIGN(l_addrLen);
break;
}
case IFA_LABEL:
strncpy(l_name, l_rtaData, l_rtaDataSize);
l_name[l_rtaDataSize] = '\0';
l_entry->ifa_name = l_name;
break;
default:
break;
}
}
if(l_entry->ifa_addr && (l_entry->ifa_addr->sa_family == AF_INET || l_entry->ifa_addr->sa_family == AF_INET6))
{
unsigned l_maxPrefix = (l_entry->ifa_addr->sa_family == AF_INET ? 32 : 128);
unsigned l_prefix = (l_info->ifa_prefixlen > l_maxPrefix ? l_maxPrefix : l_info->ifa_prefixlen);
char l_mask[16] = {0};
unsigned i;
for(i=0; i<(l_prefix/8); ++i)
{
l_mask[i] = 0xff;
}
if(l_prefix % 8)
{
l_mask[i] = 0xff << (8 - (l_prefix % 8));
}
makeSockaddr(l_entry->ifa_addr->sa_family, (struct sockaddr *)l_addr, l_mask, l_maxPrefix / 8);
l_entry->ifa_netmask = (struct sockaddr *)l_addr;
}
addToEnd(p_resultList, l_entry);
return 0;
}
static int interpretLinks(int p_socket, NetlinkList *p_netlinkList, struct ifaddrs **p_resultList)
{
int l_numLinks = 0;
pid_t l_pid = getpid();
for(; p_netlinkList; p_netlinkList = p_netlinkList->m_next)
{
unsigned int l_nlsize = p_netlinkList->m_size;
struct nlmsghdr *l_hdr;
for(l_hdr = p_netlinkList->m_data; NLMSG_OK(l_hdr, l_nlsize); l_hdr = NLMSG_NEXT(l_hdr, l_nlsize))
{
if((pid_t)l_hdr->nlmsg_pid != l_pid || (int)l_hdr->nlmsg_seq != p_socket)
{
continue;
}
if(l_hdr->nlmsg_type == NLMSG_DONE)
{
break;
}
if(l_hdr->nlmsg_type == RTM_NEWLINK)
{
if(interpretLink(l_hdr, p_resultList) == -1)
{
return -1;
}
++l_numLinks;
}
}
}
return l_numLinks;
}
static int interpretAddrs(int p_socket, NetlinkList *p_netlinkList, struct ifaddrs **p_resultList, int p_numLinks)
{
pid_t l_pid = getpid();
for(; p_netlinkList; p_netlinkList = p_netlinkList->m_next)
{
unsigned int l_nlsize = p_netlinkList->m_size;
struct nlmsghdr *l_hdr;
for(l_hdr = p_netlinkList->m_data; NLMSG_OK(l_hdr, l_nlsize); l_hdr = NLMSG_NEXT(l_hdr, l_nlsize))
{
if((pid_t)l_hdr->nlmsg_pid != l_pid || (int)l_hdr->nlmsg_seq != p_socket)
{
continue;
}
if(l_hdr->nlmsg_type == NLMSG_DONE)
{
break;
}
if(l_hdr->nlmsg_type == RTM_NEWADDR)
{
if (interpretAddr(l_hdr, p_resultList, p_numLinks) == -1)
{
return -1;
}
}
}
}
return 0;
}
int getifaddrs(struct ifaddrs **ifap)
{
if(!ifap)
{
return -1;
}
*ifap = NULL;
int l_socket = netlink_socket();
if(l_socket < 0)
{
return -1;
}
NetlinkList *l_linkResults = getResultList(l_socket, RTM_GETLINK);
if(!l_linkResults)
{
close(l_socket);
return -1;
}
NetlinkList *l_addrResults = getResultList(l_socket, RTM_GETADDR);
if(!l_addrResults)
{
close(l_socket);
freeResultList(l_linkResults);
return -1;
}
int l_result = 0;
int l_numLinks = interpretLinks(l_socket, l_linkResults, ifap);
if(l_numLinks == -1 || interpretAddrs(l_socket, l_addrResults, ifap, l_numLinks) == -1)
{
l_result = -1;
}
freeResultList(l_linkResults);
freeResultList(l_addrResults);
close(l_socket);
return l_result;
}
void freeifaddrs(struct ifaddrs *ifa)
{
struct ifaddrs *l_cur;
while(ifa)
{
l_cur = ifa;
ifa = ifa->ifa_next;
free(l_cur);
}
}

View File

@ -0,0 +1,54 @@
/*
* Copyright (c) 1995, 1999
* Berkeley Software Design, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* THIS SOFTWARE IS PROVIDED BY Berkeley Software Design, Inc. ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL Berkeley Software Design, Inc. BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* BSDI ifaddrs.h,v 2.5 2000/02/23 14:51:59 dab Exp
*/
#ifndef _IFADDRS_H_
#define _IFADDRS_H_
struct ifaddrs {
struct ifaddrs *ifa_next;
char *ifa_name;
unsigned int ifa_flags;
struct sockaddr *ifa_addr;
struct sockaddr *ifa_netmask;
struct sockaddr *ifa_dstaddr;
void *ifa_data;
};
/*
* This may have been defined in <net/if.h>. Note that if <net/if.h> is
* to be included it must be included before this header file.
*/
#ifndef ifa_broadaddr
#define ifa_broadaddr ifa_dstaddr /* broadcast address interface */
#endif
#include <sys/cdefs.h>
__BEGIN_DECLS
extern int getifaddrs(struct ifaddrs **ifap);
extern void freeifaddrs(struct ifaddrs *ifa);
__END_DECLS
#endif

View File

@ -0,0 +1,526 @@
Libwebsockets and included programs are provided under the terms of the GNU
Library General Public License (LGPL) 2.1, with the following exceptions:
1) Static linking of programs with the libwebsockets library does not
constitute a derivative work and does not require the author to provide
source code for the program, use the shared libwebsockets libraries, or
link their program against a user-supplied version of libwebsockets.
If you link the program to a modified version of libwebsockets, then the
changes to libwebsockets must be provided under the terms of the LGPL in
sections 1, 2, and 4.
2) You do not have to provide a copy of the libwebsockets license with
programs that are linked to the libwebsockets library, nor do you have to
identify the libwebsockets license in your program or documentation as
required by section 6 of the LGPL.
However, programs must still identify their use of libwebsockets. The
following example statement can be included in user documentation to
satisfy this requirement:
"[program] is based in part on the work of the libwebsockets project
(http://libwebsockets.org)"
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!

View File

@ -0,0 +1,20 @@
This is the libwebsockets C library for lightweight websocket clients and
servers. For support, visit
http://libwebsockets.org
and consider joining the project mailing list at
http://ml.libwebsockets.org/mailman/listinfo/libwebsockets
You can get the latest version of the library from git
http://git.libwebsockets.org
https://github.com/warmcat/libwebsockets
for more information:
README.build - information on building the library
README.coding - information for writing code using the library
README.test-apps - information about the test apps built with the library

View File

@ -0,0 +1,93 @@
/*
* This code originally came from here
*
* http://base64.sourceforge.net/b64.c
*
* with the following license:
*
* LICENCE: Copyright (c) 2001 Bob Trower, Trantor Standard Systems Inc.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated
* documentation files (the "Software"), to deal in the
* Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute,
* sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall
* be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
* KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
* OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* VERSION HISTORY:
* Bob Trower 08/04/01 -- Create Version 0.00.00B
*
* I cleaned it up quite a bit to match the (linux kernel) style of the rest
* of libwebsockets; this version is under LGPL2 like the rest of libwebsockets
* since he explictly allows sublicensing, but I give the URL above so you can
* get the original with Bob's super-liberal terms directly if you prefer.
*/
#include <stdio.h>
#include <string.h>
#include "private-libwebsockets.h"
static const char encode[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz0123456789+/";
static const char decode[] = "|$$$}rstuvwxyz{$$$$$$$>?@ABCDEFGHIJKLMNOPQRSTUVW"
"$$$$$$XYZ[\\]^_`abcdefghijklmnopq";
LWS_VISIBLE int
lws_b64_encode_string(const char *in, int in_len, char *out, int out_size)
{
unsigned char triple[3];
int i;
int len;
int line = 0;
int done = 0;
while (in_len) {
len = 0;
for (i = 0; i < 3; i++) {
if (in_len) {
triple[i] = *in++;
len++;
in_len--;
} else
triple[i] = 0;
}
if (!len)
continue;
if (done + 4 >= out_size)
return -1;
*out++ = encode[triple[0] >> 2];
*out++ = encode[((triple[0] & 0x03) << 4) |
((triple[1] & 0xf0) >> 4)];
*out++ = (len > 1 ? encode[((triple[1] & 0x0f) << 2) |
((triple[2] & 0xc0) >> 6)] : '=');
*out++ = (len > 2 ? encode[triple[2] & 0x3f] : '=');
done += 4;
line += 4;
}
if (done + 1 >= out_size)
return -1;
*out++ = '\0';
return done;
}

View File

@ -0,0 +1,441 @@
#include "private-libwebsockets.h"
struct libwebsocket *libwebsocket_client_connect_2(
struct libwebsocket_context *context,
struct libwebsocket *wsi
) {
struct libwebsocket_pollfd pfd;
#ifdef LWS_USE_IPV6
struct sockaddr_in6 server_addr6;
struct sockaddr_in6 client_addr6;
struct addrinfo hints, *result;
#endif
struct sockaddr_in server_addr4;
struct sockaddr_in client_addr4;
struct hostent *server_hostent;
struct sockaddr *v;
int n;
int plen = 0;
const char *ads;
lwsl_client("libwebsocket_client_connect_2\n");
/*
* proxy?
*/
if (context->http_proxy_port) {
plen = sprintf((char *)context->service_buffer,
"CONNECT %s:%u HTTP/1.0\x0d\x0a"
"User-agent: libwebsockets\x0d\x0a"
/*Proxy-authorization: basic aGVsbG86d29ybGQ= */
"\x0d\x0a",
lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS),
wsi->u.hdr.ah->c_port);
ads = context->http_proxy_address;
#ifdef LWS_USE_IPV6
if (LWS_IPV6_ENABLED(context))
server_addr6.sin6_port = htons(context->http_proxy_port);
else
#endif
server_addr4.sin_port = htons(context->http_proxy_port);
} else {
ads = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS);
#ifdef LWS_USE_IPV6
if (LWS_IPV6_ENABLED(context))
server_addr6.sin6_port = htons(wsi->u.hdr.ah->c_port);
else
#endif
server_addr4.sin_port = htons(wsi->u.hdr.ah->c_port);
}
/*
* prepare the actual connection (to the proxy, if any)
*/
lwsl_client("libwebsocket_client_connect_2: address %s\n", ads);
#ifdef LWS_USE_IPV6
if (LWS_IPV6_ENABLED(context)) {
memset(&hints, 0, sizeof(struct addrinfo));
n = getaddrinfo(ads, NULL, &hints, &result);
if (n) {
#ifdef _WIN32
lwsl_err("getaddrinfo: %ls\n", gai_strerrorW(n));
#else
lwsl_err("getaddrinfo: %s\n", gai_strerror(n));
#endif
goto oom4;
}
server_addr6.sin6_family = AF_INET6;
switch (result->ai_family) {
case AF_INET:
/* map IPv4 to IPv6 */
bzero((char *)&server_addr6.sin6_addr,
sizeof(struct in6_addr));
server_addr6.sin6_addr.s6_addr[10] = 0xff;
server_addr6.sin6_addr.s6_addr[11] = 0xff;
memcpy(&server_addr6.sin6_addr.s6_addr[12],
&((struct sockaddr_in *)result->ai_addr)->sin_addr,
sizeof(struct in_addr));
break;
case AF_INET6:
memcpy(&server_addr6.sin6_addr,
&((struct sockaddr_in6 *)result->ai_addr)->sin6_addr,
sizeof(struct in6_addr));
break;
default:
lwsl_err("Unknown address family\n");
freeaddrinfo(result);
goto oom4;
}
freeaddrinfo(result);
} else
#endif
{
server_hostent = gethostbyname(ads);
if (!server_hostent) {
lwsl_err("Unable to get host name from %s\n", ads);
goto oom4;
}
server_addr4.sin_family = AF_INET;
server_addr4.sin_addr =
*((struct in_addr *)server_hostent->h_addr);
bzero(&server_addr4.sin_zero, 8);
}
if (wsi->sock < 0) {
#ifdef LWS_USE_IPV6
if (LWS_IPV6_ENABLED(context))
wsi->sock = socket(AF_INET6, SOCK_STREAM, 0);
else
#endif
wsi->sock = socket(AF_INET, SOCK_STREAM, 0);
if (wsi->sock < 0) {
lwsl_warn("Unable to open socket\n");
goto oom4;
}
if (lws_plat_set_socket_options(context, wsi->sock)) {
lwsl_err("Failed to set wsi socket options\n");
compatible_close(wsi->sock);
goto oom4;
}
wsi->mode = LWS_CONNMODE_WS_CLIENT_WAITING_CONNECT;
insert_wsi_socket_into_fds(context, wsi);
libwebsocket_set_timeout(wsi,
PENDING_TIMEOUT_AWAITING_CONNECT_RESPONSE,
AWAITING_TIMEOUT);
#ifdef LWS_USE_IPV6
if (LWS_IPV6_ENABLED(context)) {
v = (struct sockaddr *)&client_addr6;
n = sizeof(client_addr6);
bzero((char *)v, n);
client_addr6.sin6_family = AF_INET6;
} else
#endif
{
v = (struct sockaddr *)&client_addr4;
n = sizeof(client_addr4);
bzero((char *)v, n);
client_addr4.sin_family = AF_INET;
}
if (context->iface) {
if (interface_to_sa(context, context->iface,
(struct sockaddr_in *)v, n) < 0) {
lwsl_err("Unable to find interface %s\n",
context->iface);
compatible_close(wsi->sock);
goto failed;
}
if (bind(wsi->sock, v, n) < 0) {
lwsl_err("Error binding to interface %s",
context->iface);
compatible_close(wsi->sock);
goto failed;
}
}
}
#ifdef LWS_USE_IPV6
if (LWS_IPV6_ENABLED(context)) {
v = (struct sockaddr *)&server_addr6;
n = sizeof(struct sockaddr_in6);
} else
#endif
{
v = (struct sockaddr *)&server_addr4;
n = sizeof(struct sockaddr);
}
if (connect(wsi->sock, v, n) == -1 || LWS_ERRNO == LWS_EISCONN) {
if (LWS_ERRNO == LWS_EALREADY || LWS_ERRNO == LWS_EINPROGRESS
|| LWS_ERRNO == LWS_EWOULDBLOCK) {
lwsl_client("nonblocking connect retry\n");
/*
* must do specifically a POLLOUT poll to hear
* about the connect completion
*/
if (lws_change_pollfd(wsi, 0, LWS_POLLOUT))
goto oom4;
return wsi;
}
if (LWS_ERRNO != LWS_EISCONN) {
lwsl_debug("Connect failed errno=%d\n", LWS_ERRNO);
goto failed;
}
}
lwsl_client("connected\n");
/* we are connected to server, or proxy */
if (context->http_proxy_port) {
/* OK from now on we talk via the proxy, so connect to that */
/*
* (will overwrite existing pointer,
* leaving old string/frag there but unreferenced)
*/
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS,
context->http_proxy_address))
goto failed;
wsi->u.hdr.ah->c_port = context->http_proxy_port;
n = send(wsi->sock, context->service_buffer, plen, MSG_NOSIGNAL);
if (n < 0) {
lwsl_debug("ERROR writing to proxy socket\n");
goto failed;
}
libwebsocket_set_timeout(wsi,
PENDING_TIMEOUT_AWAITING_PROXY_RESPONSE,
AWAITING_TIMEOUT);
wsi->mode = LWS_CONNMODE_WS_CLIENT_WAITING_PROXY_REPLY;
return wsi;
}
/*
* provoke service to issue the handshake directly
* we need to do it this way because in the proxy case, this is the
* next state and executed only if and when we get a good proxy
* response inside the state machine... but notice in SSL case this
* may not have sent anything yet with 0 return, and won't until some
* many retries from main loop. To stop that becoming endless,
* cover with a timeout.
*/
libwebsocket_set_timeout(wsi,
PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE, AWAITING_TIMEOUT);
wsi->mode = LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE;
pfd.fd = wsi->sock;
pfd.revents = LWS_POLLIN;
n = libwebsocket_service_fd(context, &pfd);
if (n < 0)
goto failed;
if (n) /* returns 1 on failure after closing wsi */
return NULL;
return wsi;
oom4:
free(wsi->u.hdr.ah);
free(wsi);
return NULL;
failed:
libwebsocket_close_and_free_session(context, wsi,
LWS_CLOSE_STATUS_NOSTATUS);
return NULL;
}
/**
* libwebsocket_client_connect() - Connect to another websocket server
* @context: Websocket context
* @address: Remote server address, eg, "myserver.com"
* @port: Port to connect to on the remote server, eg, 80
* @ssl_connection: 0 = ws://, 1 = wss:// encrypted, 2 = wss:// allow self
* signed certs
* @path: Websocket path on server
* @host: Hostname on server
* @origin: Socket origin name
* @protocol: Comma-separated list of protocols being asked for from
* the server, or just one. The server will pick the one it
* likes best.
* @ietf_version_or_minus_one: -1 to ask to connect using the default, latest
* protocol supported, or the specific protocol ordinal
*
* This function creates a connection to a remote server
*/
LWS_VISIBLE struct libwebsocket *
libwebsocket_client_connect(struct libwebsocket_context *context,
const char *address,
int port,
int ssl_connection,
const char *path,
const char *host,
const char *origin,
const char *protocol,
int ietf_version_or_minus_one)
{
struct libwebsocket *wsi;
wsi = (struct libwebsocket *) malloc(sizeof(struct libwebsocket));
if (wsi == NULL)
goto bail;
memset(wsi, 0, sizeof(*wsi));
wsi->sock = -1;
/* -1 means just use latest supported */
if (ietf_version_or_minus_one == -1)
ietf_version_or_minus_one = SPEC_LATEST_SUPPORTED;
wsi->ietf_spec_revision = ietf_version_or_minus_one;
wsi->user_space = NULL;
wsi->state = WSI_STATE_CLIENT_UNCONNECTED;
wsi->protocol = NULL;
wsi->pending_timeout = NO_PENDING_TIMEOUT;
#ifdef LWS_OPENSSL_SUPPORT
wsi->use_ssl = ssl_connection;
#else
if (ssl_connection) {
lwsl_err("libwebsockets not configured for ssl\n");
goto bail;
}
#endif
if (lws_allocate_header_table(wsi))
goto bail;
/*
* we're not necessarily in a position to action these right away,
* stash them... we only need during connect phase so u.hdr is fine
*/
wsi->u.hdr.ah->c_port = port;
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS, address))
goto bail1;
/* these only need u.hdr lifetime as well */
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_URI, path))
goto bail1;
if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_HOST, host))
goto bail1;
if (origin)
if (lws_hdr_simple_create(wsi,
_WSI_TOKEN_CLIENT_ORIGIN, origin))
goto bail1;
/*
* this is a list of protocols we tell the server we're okay with
* stash it for later when we compare server response with it
*/
if (protocol)
if (lws_hdr_simple_create(wsi,
_WSI_TOKEN_CLIENT_SENT_PROTOCOLS, protocol))
goto bail1;
wsi->protocol = &context->protocols[0];
/*
* Check with each extension if it is able to route and proxy this
* connection for us. For example, an extension like x-google-mux
* can handle this and then we don't need an actual socket for this
* connection.
*/
if (lws_ext_callback_for_each_extension_type(context, wsi,
LWS_EXT_CALLBACK_CAN_PROXY_CLIENT_CONNECTION,
(void *)address, port) > 0) {
lwsl_client("libwebsocket_client_connect: ext handling conn\n");
libwebsocket_set_timeout(wsi,
PENDING_TIMEOUT_AWAITING_EXTENSION_CONNECT_RESPONSE,
AWAITING_TIMEOUT);
wsi->mode = LWS_CONNMODE_WS_CLIENT_WAITING_EXTENSION_CONNECT;
return wsi;
}
lwsl_client("libwebsocket_client_connect: direct conn\n");
return libwebsocket_client_connect_2(context, wsi);
bail1:
free(wsi->u.hdr.ah);
bail:
free(wsi);
return NULL;
}
/**
* libwebsocket_client_connect_extended() - Connect to another websocket server
* @context: Websocket context
* @address: Remote server address, eg, "myserver.com"
* @port: Port to connect to on the remote server, eg, 80
* @ssl_connection: 0 = ws://, 1 = wss:// encrypted, 2 = wss:// allow self
* signed certs
* @path: Websocket path on server
* @host: Hostname on server
* @origin: Socket origin name
* @protocol: Comma-separated list of protocols being asked for from
* the server, or just one. The server will pick the one it
* likes best.
* @ietf_version_or_minus_one: -1 to ask to connect using the default, latest
* protocol supported, or the specific protocol ordinal
* @userdata: Pre-allocated user data
*
* This function creates a connection to a remote server
*/
LWS_VISIBLE struct libwebsocket *
libwebsocket_client_connect_extended(struct libwebsocket_context *context,
const char *address,
int port,
int ssl_connection,
const char *path,
const char *host,
const char *origin,
const char *protocol,
int ietf_version_or_minus_one,
void *userdata)
{
struct libwebsocket *ws =
libwebsocket_client_connect(context, address, port,
ssl_connection, path, host, origin, protocol,
ietf_version_or_minus_one);
if (ws && !ws->user_space && userdata)
ws->user_space = userdata ;
return ws ;
}

View File

@ -0,0 +1,402 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2014 Andy Green <andy@warmcat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation:
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include "private-libwebsockets.h"
int libwebsocket_client_rx_sm(struct libwebsocket *wsi, unsigned char c)
{
int callback_action = LWS_CALLBACK_CLIENT_RECEIVE;
int handled;
struct lws_tokens eff_buf;
int m;
switch (wsi->lws_rx_parse_state) {
case LWS_RXPS_NEW:
switch (wsi->ietf_spec_revision) {
case 13:
wsi->u.ws.opcode = c & 0xf;
wsi->u.ws.rsv = (c & 0x70);
wsi->u.ws.final = !!((c >> 7) & 1);
switch (wsi->u.ws.opcode) {
case LWS_WS_OPCODE_07__TEXT_FRAME:
case LWS_WS_OPCODE_07__BINARY_FRAME:
wsi->u.ws.frame_is_binary = wsi->u.ws.opcode ==
LWS_WS_OPCODE_07__BINARY_FRAME;
break;
}
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN;
break;
default:
lwsl_err("unknown spec version %02d\n",
wsi->ietf_spec_revision);
break;
}
break;
case LWS_RXPS_04_FRAME_HDR_LEN:
wsi->u.ws.this_frame_masked = !!(c & 0x80);
switch (c & 0x7f) {
case 126:
/* control frames are not allowed to have big lengths */
if (wsi->u.ws.opcode & 8)
goto illegal_ctl_length;
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN16_2;
break;
case 127:
/* control frames are not allowed to have big lengths */
if (wsi->u.ws.opcode & 8)
goto illegal_ctl_length;
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_8;
break;
default:
wsi->u.ws.rx_packet_length = c;
if (wsi->u.ws.this_frame_masked)
wsi->lws_rx_parse_state =
LWS_RXPS_07_COLLECT_FRAME_KEY_1;
else {
if (c)
wsi->lws_rx_parse_state =
LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
else {
wsi->lws_rx_parse_state = LWS_RXPS_NEW;
goto spill;
}
}
break;
}
break;
case LWS_RXPS_04_FRAME_HDR_LEN16_2:
wsi->u.ws.rx_packet_length = c << 8;
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN16_1;
break;
case LWS_RXPS_04_FRAME_HDR_LEN16_1:
wsi->u.ws.rx_packet_length |= c;
if (wsi->u.ws.this_frame_masked)
wsi->lws_rx_parse_state =
LWS_RXPS_07_COLLECT_FRAME_KEY_1;
else {
if (wsi->u.ws.rx_packet_length)
wsi->lws_rx_parse_state =
LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
else {
wsi->lws_rx_parse_state = LWS_RXPS_NEW;
goto spill;
}
}
break;
case LWS_RXPS_04_FRAME_HDR_LEN64_8:
if (c & 0x80) {
lwsl_warn("b63 of length must be zero\n");
/* kill the connection */
return -1;
}
#if defined __LP64__
wsi->u.ws.rx_packet_length = ((size_t)c) << 56;
#else
wsi->u.ws.rx_packet_length = 0;
#endif
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_7;
break;
case LWS_RXPS_04_FRAME_HDR_LEN64_7:
#if defined __LP64__
wsi->u.ws.rx_packet_length |= ((size_t)c) << 48;
#endif
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_6;
break;
case LWS_RXPS_04_FRAME_HDR_LEN64_6:
#if defined __LP64__
wsi->u.ws.rx_packet_length |= ((size_t)c) << 40;
#endif
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_5;
break;
case LWS_RXPS_04_FRAME_HDR_LEN64_5:
#if defined __LP64__
wsi->u.ws.rx_packet_length |= ((size_t)c) << 32;
#endif
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_4;
break;
case LWS_RXPS_04_FRAME_HDR_LEN64_4:
wsi->u.ws.rx_packet_length |= ((size_t)c) << 24;
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_3;
break;
case LWS_RXPS_04_FRAME_HDR_LEN64_3:
wsi->u.ws.rx_packet_length |= ((size_t)c) << 16;
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_2;
break;
case LWS_RXPS_04_FRAME_HDR_LEN64_2:
wsi->u.ws.rx_packet_length |= ((size_t)c) << 8;
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_1;
break;
case LWS_RXPS_04_FRAME_HDR_LEN64_1:
wsi->u.ws.rx_packet_length |= (size_t)c;
if (wsi->u.ws.this_frame_masked)
wsi->lws_rx_parse_state =
LWS_RXPS_07_COLLECT_FRAME_KEY_1;
else {
if (wsi->u.ws.rx_packet_length)
wsi->lws_rx_parse_state =
LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
else {
wsi->lws_rx_parse_state = LWS_RXPS_NEW;
goto spill;
}
}
break;
case LWS_RXPS_07_COLLECT_FRAME_KEY_1:
wsi->u.ws.frame_masking_nonce_04[0] = c;
if (c)
wsi->u.ws.all_zero_nonce = 0;
wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_2;
break;
case LWS_RXPS_07_COLLECT_FRAME_KEY_2:
wsi->u.ws.frame_masking_nonce_04[1] = c;
if (c)
wsi->u.ws.all_zero_nonce = 0;
wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_3;
break;
case LWS_RXPS_07_COLLECT_FRAME_KEY_3:
wsi->u.ws.frame_masking_nonce_04[2] = c;
if (c)
wsi->u.ws.all_zero_nonce = 0;
wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_4;
break;
case LWS_RXPS_07_COLLECT_FRAME_KEY_4:
wsi->u.ws.frame_masking_nonce_04[3] = c;
if (c)
wsi->u.ws.all_zero_nonce = 0;
if (wsi->u.ws.rx_packet_length)
wsi->lws_rx_parse_state =
LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
else {
wsi->lws_rx_parse_state = LWS_RXPS_NEW;
goto spill;
}
break;
case LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED:
if (!wsi->u.ws.rx_user_buffer)
lwsl_err("NULL client rx_user_buffer\n");
if ((!wsi->u.ws.this_frame_masked) || wsi->u.ws.all_zero_nonce)
wsi->u.ws.rx_user_buffer[LWS_SEND_BUFFER_PRE_PADDING +
(wsi->u.ws.rx_user_buffer_head++)] = c;
else
wsi->u.ws.rx_user_buffer[LWS_SEND_BUFFER_PRE_PADDING +
(wsi->u.ws.rx_user_buffer_head++)] =
c ^ wsi->u.ws.frame_masking_nonce_04[
(wsi->u.ws.frame_mask_index++) & 3];
if (--wsi->u.ws.rx_packet_length == 0) {
/* spill because we have the whole frame */
wsi->lws_rx_parse_state = LWS_RXPS_NEW;
goto spill;
}
/*
* if there's no protocol max frame size given, we are
* supposed to default to LWS_MAX_SOCKET_IO_BUF
*/
if (!wsi->protocol->rx_buffer_size &&
wsi->u.ws.rx_user_buffer_head !=
LWS_MAX_SOCKET_IO_BUF)
break;
else
if (wsi->protocol->rx_buffer_size &&
wsi->u.ws.rx_user_buffer_head !=
wsi->protocol->rx_buffer_size)
break;
/* spill because we filled our rx buffer */
spill:
handled = 0;
/*
* is this frame a control packet we should take care of at this
* layer? If so service it and hide it from the user callback
*/
switch (wsi->u.ws.opcode) {
case LWS_WS_OPCODE_07__CLOSE:
/* is this an acknowledgement of our close? */
if (wsi->state == WSI_STATE_AWAITING_CLOSE_ACK) {
/*
* fine he has told us he is closing too, let's
* finish our close
*/
lwsl_parser("seen server's close ack\n");
return -1;
}
lwsl_parser("client sees server close len = %d\n",
wsi->u.ws.rx_user_buffer_head);
/*
* parrot the close packet payload back
* we do not care about how it went, we are closing
* immediately afterwards
*/
libwebsocket_write(wsi, (unsigned char *)
&wsi->u.ws.rx_user_buffer[
LWS_SEND_BUFFER_PRE_PADDING],
wsi->u.ws.rx_user_buffer_head, LWS_WRITE_CLOSE);
wsi->state = WSI_STATE_RETURNED_CLOSE_ALREADY;
/* close the connection */
return -1;
case LWS_WS_OPCODE_07__PING:
lwsl_info("client received ping, doing pong\n");
/*
* parrot the ping packet payload back as a pong
* !!! this may block or have partial write or fail
* !!! very unlikely if the ping size is small
*/
libwebsocket_write(wsi, (unsigned char *)
&wsi->u.ws.rx_user_buffer[
LWS_SEND_BUFFER_PRE_PADDING],
wsi->u.ws.rx_user_buffer_head,
LWS_WRITE_PONG);
handled = 1;
break;
case LWS_WS_OPCODE_07__PONG:
lwsl_info("client receied pong\n");
lwsl_hexdump(&wsi->u.ws.rx_user_buffer[
LWS_SEND_BUFFER_PRE_PADDING],
wsi->u.ws.rx_user_buffer_head);
/* issue it */
callback_action = LWS_CALLBACK_CLIENT_RECEIVE_PONG;
break;
case LWS_WS_OPCODE_07__CONTINUATION:
case LWS_WS_OPCODE_07__TEXT_FRAME:
case LWS_WS_OPCODE_07__BINARY_FRAME:
break;
default:
lwsl_parser("Reserved opc 0x%2X\n", wsi->u.ws.opcode);
/*
* It's something special we can't understand here.
* Pass the payload up to the extension's parsing
* state machine.
*/
eff_buf.token = &wsi->u.ws.rx_user_buffer[
LWS_SEND_BUFFER_PRE_PADDING];
eff_buf.token_len = wsi->u.ws.rx_user_buffer_head;
if (lws_ext_callback_for_each_active(wsi,
LWS_EXT_CALLBACK_EXTENDED_PAYLOAD_RX,
&eff_buf, 0) <= 0) { /* not handle or fail */
lwsl_ext("Unhandled ext opc 0x%x\n",
wsi->u.ws.opcode);
wsi->u.ws.rx_user_buffer_head = 0;
return 0;
}
handled = 1;
break;
}
/*
* No it's real payload, pass it up to the user callback.
* It's nicely buffered with the pre-padding taken care of
* so it can be sent straight out again using libwebsocket_write
*/
if (handled)
goto already_done;
eff_buf.token = &wsi->u.ws.rx_user_buffer[
LWS_SEND_BUFFER_PRE_PADDING];
eff_buf.token_len = wsi->u.ws.rx_user_buffer_head;
if (lws_ext_callback_for_each_active(wsi,
LWS_EXT_CALLBACK_PAYLOAD_RX,
&eff_buf, 0) < 0) /* fail */
return -1;
if (eff_buf.token_len <= 0 &&
callback_action != LWS_CALLBACK_CLIENT_RECEIVE_PONG)
goto already_done;
eff_buf.token[eff_buf.token_len] = '\0';
if (!wsi->protocol->callback)
goto already_done;
if (callback_action == LWS_CALLBACK_CLIENT_RECEIVE_PONG)
lwsl_info("Client doing pong callback\n");
m = wsi->protocol->callback(
wsi->protocol->owning_server,
wsi,
(enum libwebsocket_callback_reasons)callback_action,
wsi->user_space,
eff_buf.token,
eff_buf.token_len);
/* if user code wants to close, let caller know */
if (m)
return 1;
already_done:
wsi->u.ws.rx_user_buffer_head = 0;
break;
default:
lwsl_err("client rx illegal state\n");
return 1;
}
return 0;
illegal_ctl_length:
lwsl_warn("Control frame asking for extended length is illegal\n");
/* kill the connection */
return -1;
}

View File

@ -0,0 +1,954 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2014 Andy Green <andy@warmcat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation:
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include "private-libwebsockets.h"
int lws_handshake_client(struct libwebsocket *wsi, unsigned char **buf, size_t len)
{
int n;
switch (wsi->mode) {
case LWS_CONNMODE_WS_CLIENT_WAITING_PROXY_REPLY:
case LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE:
case LWS_CONNMODE_WS_CLIENT_WAITING_SERVER_REPLY:
case LWS_CONNMODE_WS_CLIENT_WAITING_EXTENSION_CONNECT:
case LWS_CONNMODE_WS_CLIENT:
for (n = 0; n < len; n++)
if (libwebsocket_client_rx_sm(wsi, *(*buf)++)) {
lwsl_debug("client_rx_sm failed\n");
return 1;
}
return 0;
default:
break;
}
return 0;
}
int lws_client_socket_service(struct libwebsocket_context *context,
struct libwebsocket *wsi, struct libwebsocket_pollfd *pollfd)
{
int n;
char *p = (char *)&context->service_buffer[0];
int len;
unsigned char c;
switch (wsi->mode) {
case LWS_CONNMODE_WS_CLIENT_WAITING_CONNECT:
/*
* we are under PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE
* timeout protection set in client-handshake.c
*/
if (libwebsocket_client_connect_2(context, wsi) == NULL) {
/* closed */
lwsl_client("closed\n");
return -1;
}
/* either still pending connection, or changed mode */
return 0;
case LWS_CONNMODE_WS_CLIENT_WAITING_PROXY_REPLY:
/* handle proxy hung up on us */
if (pollfd->revents & LWS_POLLHUP) {
lwsl_warn("Proxy connection %p (fd=%d) dead\n",
(void *)wsi, pollfd->fd);
libwebsocket_close_and_free_session(context, wsi,
LWS_CLOSE_STATUS_NOSTATUS);
return 0;
}
n = recv(wsi->sock, context->service_buffer,
sizeof(context->service_buffer), 0);
if (n < 0) {
if (LWS_ERRNO == LWS_EAGAIN) {
lwsl_debug(
"Proxy read returned EAGAIN... retrying\n");
return 0;
}
libwebsocket_close_and_free_session(context, wsi,
LWS_CLOSE_STATUS_NOSTATUS);
lwsl_err("ERROR reading from proxy socket\n");
return 0;
}
context->service_buffer[13] = '\0';
if (strcmp((char *)context->service_buffer, "HTTP/1.0 200 ")) {
libwebsocket_close_and_free_session(context, wsi,
LWS_CLOSE_STATUS_NOSTATUS);
lwsl_err("ERROR proxy: %s\n", context->service_buffer);
return 0;
}
/* clear his proxy connection timeout */
libwebsocket_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
/* fallthru */
case LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE:
/*
* we are under PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE
* timeout protection set in client-handshake.c
*/
/*
* take care of our libwebsocket_callback_on_writable
* happening at a time when there's no real connection yet
*/
if (lws_change_pollfd(wsi, LWS_POLLOUT, 0))
return -1;
#ifdef LWS_OPENSSL_SUPPORT
/* we can retry this... just cook the SSL BIO the first time */
if (wsi->use_ssl && !wsi->ssl) {
const char *hostname = lws_hdr_simple_ptr(wsi,
_WSI_TOKEN_CLIENT_PEER_ADDRESS);
wsi->ssl = SSL_new(context->ssl_client_ctx);
#ifndef USE_CYASSL
SSL_set_mode(wsi->ssl,
SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
#endif
/*
* use server name indication (SNI), if supported,
* when establishing connection
*/
#ifdef USE_CYASSL
#ifdef CYASSL_SNI_HOST_NAME
CyaSSL_UseSNI(wsi->ssl, CYASSL_SNI_HOST_NAME,
hostname, strlen(hostname));
#endif
#else
SSL_set_tlsext_host_name(wsi->ssl, hostname);
#endif
#ifdef USE_CYASSL
/*
* CyaSSL does certificate verification differently
* from OpenSSL.
* If we should ignore the certificate, we need to set
* this before SSL_new and SSL_connect is called.
* Otherwise the connect will simply fail with error
* code -155
*/
if (wsi->use_ssl == 2)
CyaSSL_set_verify(wsi->ssl,
SSL_VERIFY_NONE, NULL);
#endif /* USE_CYASSL */
wsi->client_bio =
BIO_new_socket(wsi->sock, BIO_NOCLOSE);
SSL_set_bio(wsi->ssl, wsi->client_bio, wsi->client_bio);
#ifdef USE_CYASSL
CyaSSL_set_using_nonblock(wsi->ssl, 1);
#else
BIO_set_nbio(wsi->client_bio, 1); /* nonblocking */
#endif
SSL_set_ex_data(wsi->ssl,
openssl_websocket_private_data_index,
context);
}
if (wsi->use_ssl) {
lws_latency_pre(context, wsi);
n = SSL_connect(wsi->ssl);
lws_latency(context, wsi,
"SSL_connect LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE",
n, n > 0);
if (n < 0) {
n = SSL_get_error(wsi->ssl, n);
if (n == SSL_ERROR_WANT_READ ||
n == SSL_ERROR_WANT_WRITE) {
/*
* wants us to retry connect due to
* state of the underlying ssl layer...
* but since it may be stalled on
* blocked write, no incoming data may
* arrive to trigger the retry.
* Force (possibly many times if the SSL
* state persists in returning the
* condition code, but other sockets
* are getting serviced inbetweentimes)
* us to get called back when writable.
*/
lwsl_info(
"SSL_connect WANT_... retrying\n");
libwebsocket_callback_on_writable(
context, wsi);
wsi->mode = LWS_CONNMODE_WS_CLIENT_WAITING_SSL;
return 0; /* no error */
}
n = -1;
}
if (n <= 0) {
/*
* retry if new data comes until we
* run into the connection timeout or win
*/
n = ERR_get_error();
if (n != SSL_ERROR_NONE) {
lwsl_err("SSL connect error %lu: %s\n",
n,
ERR_error_string(n,
(char *)context->service_buffer));
return 0;
}
}
} else
wsi->ssl = NULL;
/* fallthru */
case LWS_CONNMODE_WS_CLIENT_WAITING_SSL:
if (wsi->use_ssl) {
if (wsi->mode == LWS_CONNMODE_WS_CLIENT_WAITING_SSL) {
lws_latency_pre(context, wsi);
n = SSL_connect(wsi->ssl);
lws_latency(context, wsi,
"SSL_connect LWS_CONNMODE_WS_CLIENT_WAITING_SSL",
n, n > 0);
if (n < 0) {
n = SSL_get_error(wsi->ssl, n);
if (n == SSL_ERROR_WANT_READ ||
n == SSL_ERROR_WANT_WRITE) {
/*
* wants us to retry connect due to
* state of the underlying ssl layer...
* but since it may be stalled on
* blocked write, no incoming data may
* arrive to trigger the retry.
* Force (possibly many times if the SSL
* state persists in returning the
* condition code, but other sockets
* are getting serviced inbetweentimes)
* us to get called back when writable.
*/
lwsl_info(
"SSL_connect WANT_... retrying\n");
libwebsocket_callback_on_writable(
context, wsi);
wsi->mode = LWS_CONNMODE_WS_CLIENT_WAITING_SSL;
return 0; /* no error */
}
n = -1;
}
if (n <= 0) {
/*
* retry if new data comes until we
* run into the connection timeout or win
*/
n = ERR_get_error();
if (n != SSL_ERROR_NONE) {
lwsl_err("SSL connect error %lu: %s\n",
n,
ERR_error_string(n,
(char *)context->service_buffer));
return 0;
}
}
}
#ifndef USE_CYASSL
/*
* See comment above about CyaSSL certificate
* verification
*/
lws_latency_pre(context, wsi);
n = SSL_get_verify_result(wsi->ssl);
lws_latency(context, wsi,
"SSL_get_verify_result LWS_CONNMODE..HANDSHAKE",
n, n > 0);
if ((n != X509_V_OK) && (
n != X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT ||
wsi->use_ssl != 2)) {
lwsl_err(
"server's cert didn't look good %d\n", n);
libwebsocket_close_and_free_session(context,
wsi, LWS_CLOSE_STATUS_NOSTATUS);
return 0;
}
#endif /* USE_CYASSL */
} else
wsi->ssl = NULL;
#endif
wsi->mode = LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE2;
libwebsocket_set_timeout(wsi,
PENDING_TIMEOUT_AWAITING_CLIENT_HS_SEND,
AWAITING_TIMEOUT);
/* fallthru */
case LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE2:
p = libwebsockets_generate_client_handshake(context, wsi, p);
if (p == NULL) {
lwsl_err("Failed to generate handshake for client\n");
libwebsocket_close_and_free_session(context, wsi,
LWS_CLOSE_STATUS_NOSTATUS);
return 0;
}
/* send our request to the server */
lws_latency_pre(context, wsi);
n = lws_ssl_capable_write(wsi, context->service_buffer, p - (char *)context->service_buffer);
lws_latency(context, wsi, "send lws_issue_raw", n, n == p - (char *)context->service_buffer);
switch (n) {
case LWS_SSL_CAPABLE_ERROR:
lwsl_debug("ERROR writing to client socket\n");
libwebsocket_close_and_free_session(context, wsi,
LWS_CLOSE_STATUS_NOSTATUS);
return 0;
case LWS_SSL_CAPABLE_MORE_SERVICE:
libwebsocket_callback_on_writable(context, wsi);
break;
}
wsi->u.hdr.parser_state = WSI_TOKEN_NAME_PART;
wsi->u.hdr.lextable_pos = 0;
wsi->mode = LWS_CONNMODE_WS_CLIENT_WAITING_SERVER_REPLY;
libwebsocket_set_timeout(wsi,
PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE,
AWAITING_TIMEOUT);
break;
case LWS_CONNMODE_WS_CLIENT_WAITING_SERVER_REPLY:
/* handle server hung up on us */
if (pollfd->revents & LWS_POLLHUP) {
lwsl_debug("Server connection %p (fd=%d) dead\n",
(void *)wsi, pollfd->fd);
goto bail3;
}
if (!(pollfd->revents & LWS_POLLIN))
break;
/* interpret the server response */
/*
* HTTP/1.1 101 Switching Protocols
* Upgrade: websocket
* Connection: Upgrade
* Sec-WebSocket-Accept: me89jWimTRKTWwrS3aRrL53YZSo=
* Sec-WebSocket-Nonce: AQIDBAUGBwgJCgsMDQ4PEC==
* Sec-WebSocket-Protocol: chat
*/
/*
* we have to take some care here to only take from the
* socket bytewise. The browser may (and has been seen to
* in the case that onopen() performs websocket traffic)
* coalesce both handshake response and websocket traffic
* in one packet, since at that point the connection is
* definitively ready from browser pov.
*/
len = 1;
while (wsi->u.hdr.parser_state != WSI_PARSING_COMPLETE &&
len > 0) {
n = lws_ssl_capable_read(wsi, &c, 1);
lws_latency(context, wsi, "send lws_issue_raw", n, n == 1);
switch (n) {
case LWS_SSL_CAPABLE_ERROR:
goto bail3;
case LWS_SSL_CAPABLE_MORE_SERVICE:
return 0;
}
if (libwebsocket_parse(wsi, c)) {
lwsl_warn("problems parsing header\n");
goto bail3;
}
}
/*
* hs may also be coming in multiple packets, there is a 5-sec
* libwebsocket timeout still active here too, so if parsing did
* not complete just wait for next packet coming in this state
*/
if (wsi->u.hdr.parser_state != WSI_PARSING_COMPLETE)
break;
/*
* otherwise deal with the handshake. If there's any
* packet traffic already arrived we'll trigger poll() again
* right away and deal with it that way
*/
return lws_client_interpret_server_handshake(context, wsi);
bail3:
lwsl_info(
"closing connection at LWS_CONNMODE...SERVER_REPLY\n");
libwebsocket_close_and_free_session(context, wsi,
LWS_CLOSE_STATUS_NOSTATUS);
return -1;
case LWS_CONNMODE_WS_CLIENT_WAITING_EXTENSION_CONNECT:
lwsl_ext("LWS_CONNMODE_WS_CLIENT_WAITING_EXTENSION_CONNECT\n");
break;
case LWS_CONNMODE_WS_CLIENT_PENDING_CANDIDATE_CHILD:
lwsl_ext("LWS_CONNMODE_WS_CLIENT_PENDING_CANDIDATE_CHILD\n");
break;
default:
break;
}
return 0;
}
/*
* In-place str to lower case
*/
static void
strtolower(char *s)
{
while (*s) {
*s = tolower((int)*s);
s++;
}
}
int
lws_client_interpret_server_handshake(struct libwebsocket_context *context,
struct libwebsocket *wsi)
{
const char *pc;
int okay = 0;
char *p;
int len;
#ifndef LWS_NO_EXTENSIONS
char ext_name[128];
struct libwebsocket_extension *ext;
void *v;
int more = 1;
const char *c;
#endif
int n;
int close_reason = LWS_CLOSE_STATUS_PROTOCOL_ERR;
/*
* well, what the server sent looked reasonable for syntax.
* Now let's confirm it sent all the necessary headers
*/
if (lws_hdr_total_length(wsi, WSI_TOKEN_ACCEPT) == 0) {
lwsl_info("no ACCEPT\n");
goto bail3;
}
p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP);
if (!p) {
lwsl_info("no URI\n");
goto bail3;
}
if (p && strncmp(p, "101", 3)) {
lwsl_warn(
"lws_client_handshake: got bad HTTP response '%s'\n", p);
goto bail3;
}
p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_UPGRADE);
if (!p) {
lwsl_info("no UPGRADE\n");
goto bail3;
}
strtolower(p);
if (strcmp(p, "websocket")) {
lwsl_warn(
"lws_client_handshake: got bad Upgrade header '%s'\n", p);
goto bail3;
}
p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_CONNECTION);
if (!p) {
lwsl_info("no Connection hdr\n");
goto bail3;
}
strtolower(p);
if (strcmp(p, "upgrade")) {
lwsl_warn("lws_client_int_s_hs: bad header %s\n", p);
goto bail3;
}
pc = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS);
if (pc == NULL)
lwsl_parser("lws_client_int_s_hs: no protocol list\n");
else
lwsl_parser("lws_client_int_s_hs: protocol list '%s'\n", pc);
/*
* confirm the protocol the server wants to talk was in the list
* of protocols we offered
*/
len = lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL);
if (!len) {
lwsl_info("lws_client_int_s_hs: WSI_TOKEN_PROTOCOL is null\n");
/*
* no protocol name to work from,
* default to first protocol
*/
wsi->protocol = &context->protocols[0];
goto check_extensions;
}
p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL);
len = strlen(p);
while (*pc && !okay) {
if (!strncmp(pc, p, len) &&
(pc[len] == ',' || pc[len] == '\0')) {
okay = 1;
continue;
}
while (*pc && *pc != ',')
pc++;
while (*pc && *pc != ' ')
pc++;
}
if (!okay) {
lwsl_err("lws_client_int_s_hs: got bad protocol %s\n", p);
goto bail2;
}
/*
* identify the selected protocol struct and set it
*/
n = 0;
wsi->protocol = NULL;
while (context->protocols[n].callback && !wsi->protocol) {
if (strcmp(p, context->protocols[n].name) == 0) {
wsi->protocol = &context->protocols[n];
break;
}
n++;
}
if (wsi->protocol == NULL) {
lwsl_err("lws_client_int_s_hs: fail protocol %s\n", p);
goto bail2;
}
check_extensions:
#ifndef LWS_NO_EXTENSIONS
/* instantiate the accepted extensions */
if (!lws_hdr_total_length(wsi, WSI_TOKEN_EXTENSIONS)) {
lwsl_ext("no client extenstions allowed by server\n");
goto check_accept;
}
/*
* break down the list of server accepted extensions
* and go through matching them or identifying bogons
*/
if (lws_hdr_copy(wsi, (char *)context->service_buffer,
sizeof(context->service_buffer), WSI_TOKEN_EXTENSIONS) < 0) {
lwsl_warn("ext list from server failed to copy\n");
goto bail2;
}
c = (char *)context->service_buffer;
n = 0;
while (more) {
if (*c && (*c != ',' && *c != ' ' && *c != '\t')) {
ext_name[n] = *c++;
if (n < sizeof(ext_name) - 1)
n++;
continue;
}
ext_name[n] = '\0';
if (!*c)
more = 0;
else {
c++;
if (!n)
continue;
}
/* check we actually support it */
lwsl_ext("checking client ext %s\n", ext_name);
n = 0;
ext = wsi->protocol->owning_server->extensions;
while (ext && ext->callback) {
if (strcmp(ext_name, ext->name)) {
ext++;
continue;
}
n = 1;
lwsl_ext("instantiating client ext %s\n", ext_name);
/* instantiate the extension on this conn */
wsi->active_extensions_user[
wsi->count_active_extensions] =
malloc(ext->per_session_data_size);
if (wsi->active_extensions_user[
wsi->count_active_extensions] == NULL) {
lwsl_err("Out of mem\n");
goto bail2;
}
memset(wsi->active_extensions_user[
wsi->count_active_extensions], 0,
ext->per_session_data_size);
wsi->active_extensions[
wsi->count_active_extensions] = ext;
/* allow him to construct his context */
ext->callback(wsi->protocol->owning_server,
ext, wsi,
LWS_EXT_CALLBACK_CLIENT_CONSTRUCT,
wsi->active_extensions_user[
wsi->count_active_extensions],
NULL, 0);
wsi->count_active_extensions++;
ext++;
}
if (n == 0) {
lwsl_warn("Unknown ext '%s'!\n", ext_name);
goto bail2;
}
n = 0;
}
check_accept:
#endif
/*
* Confirm his accept token is the one we precomputed
*/
p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_ACCEPT);
if (strcmp(p, wsi->u.hdr.ah->initial_handshake_hash_base64)) {
lwsl_warn("lws_client_int_s_hs: accept %s wrong vs %s\n", p,
wsi->u.hdr.ah->initial_handshake_hash_base64);
goto bail2;
}
/* allocate the per-connection user memory (if any) */
if (libwebsocket_ensure_user_space(wsi)) {
lwsl_err("Problem allocating wsi user mem\n");
goto bail2;
}
/*
* we seem to be good to go, give client last chance to check
* headers and OK it
*/
wsi->protocol->callback(context, wsi,
LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH,
wsi->user_space, NULL, 0);
/* clear his proxy connection timeout */
libwebsocket_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
/* free up his parsing allocations */
if (wsi->u.hdr.ah)
free(wsi->u.hdr.ah);
/* mark him as being alive */
wsi->state = WSI_STATE_ESTABLISHED;
wsi->mode = LWS_CONNMODE_WS_CLIENT;
/* union transition */
memset(&wsi->u, 0, sizeof(wsi->u));
wsi->u.ws.rxflow_change_to = LWS_RXFLOW_ALLOW;
/*
* create the frame buffer for this connection according to the
* size mentioned in the protocol definition. If 0 there, then
* use a big default for compatibility
*/
n = wsi->protocol->rx_buffer_size;
if (!n)
n = LWS_MAX_SOCKET_IO_BUF;
n += LWS_SEND_BUFFER_PRE_PADDING + LWS_SEND_BUFFER_POST_PADDING;
wsi->u.ws.rx_user_buffer = malloc(n);
if (!wsi->u.ws.rx_user_buffer) {
lwsl_err("Out of Mem allocating rx buffer %d\n", n);
goto bail2;
}
lwsl_info("Allocating client RX buffer %d\n", n);
if (setsockopt(wsi->sock, SOL_SOCKET, SO_SNDBUF, (const char *)&n, sizeof n)) {
lwsl_warn("Failed to set SNDBUF to %d", n);
goto bail3;
}
lwsl_debug("handshake OK for protocol %s\n", wsi->protocol->name);
/* call him back to inform him he is up */
wsi->protocol->callback(context, wsi,
LWS_CALLBACK_CLIENT_ESTABLISHED,
wsi->user_space, NULL, 0);
#ifndef LWS_NO_EXTENSIONS
/*
* inform all extensions, not just active ones since they
* already know
*/
ext = context->extensions;
while (ext && ext->callback) {
v = NULL;
for (n = 0; n < wsi->count_active_extensions; n++)
if (wsi->active_extensions[n] == ext)
v = wsi->active_extensions_user[n];
ext->callback(context, ext, wsi,
LWS_EXT_CALLBACK_ANY_WSI_ESTABLISHED, v, NULL, 0);
ext++;
}
#endif
return 0;
bail3:
free(wsi->u.ws.rx_user_buffer);
wsi->u.ws.rx_user_buffer = NULL;
close_reason = LWS_CLOSE_STATUS_NOSTATUS;
bail2:
if (wsi->protocol)
wsi->protocol->callback(context, wsi,
LWS_CALLBACK_CLIENT_CONNECTION_ERROR,
wsi->user_space, NULL, 0);
lwsl_info("closing connection due to bail2 connection error\n");
/* free up his parsing allocations */
if (wsi->u.hdr.ah)
free(wsi->u.hdr.ah);
libwebsocket_close_and_free_session(context, wsi, close_reason);
return 1;
}
char *
libwebsockets_generate_client_handshake(struct libwebsocket_context *context,
struct libwebsocket *wsi, char *pkt)
{
char buf[128];
char hash[20];
char key_b64[40];
char *p = pkt;
int n;
#ifndef LWS_NO_EXTENSIONS
struct libwebsocket_extension *ext;
int ext_count = 0;
#endif
/*
* create the random key
*/
n = libwebsockets_get_random(context, hash, 16);
if (n != 16) {
lwsl_err("Unable to read from random dev %s\n",
SYSTEM_RANDOM_FILEPATH);
libwebsocket_close_and_free_session(context, wsi,
LWS_CLOSE_STATUS_NOSTATUS);
return NULL;
}
lws_b64_encode_string(hash, 16, key_b64, sizeof(key_b64));
/*
* 00 example client handshake
*
* GET /socket.io/websocket HTTP/1.1
* Upgrade: WebSocket
* Connection: Upgrade
* Host: 127.0.0.1:9999
* Origin: http://127.0.0.1
* Sec-WebSocket-Key1: 1 0 2#0W 9 89 7 92 ^
* Sec-WebSocket-Key2: 7 7Y 4328 B2v[8(z1
* Cookie: socketio=websocket
*
* (稀0
*
* 04 example client handshake
*
* GET /chat HTTP/1.1
* Host: server.example.com
* Upgrade: websocket
* Connection: Upgrade
* Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
* Sec-WebSocket-Origin: http://example.com
* Sec-WebSocket-Protocol: chat, superchat
* Sec-WebSocket-Version: 4
*/
p += sprintf(p, "GET %s HTTP/1.1\x0d\x0a",
lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_URI));
p += sprintf(p,
"Pragma: no-cache\x0d\x0a""Cache-Control: no-cache\x0d\x0a");
p += sprintf(p, "Host: %s\x0d\x0a",
lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_HOST));
p += sprintf(p,
"Upgrade: websocket\x0d\x0a""Connection: Upgrade\x0d\x0a""Sec-WebSocket-Key: ");
strcpy(p, key_b64);
p += strlen(key_b64);
p += sprintf(p, "\x0d\x0a");
if (lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ORIGIN))
p += sprintf(p, "Origin: http://%s\x0d\x0a",
lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ORIGIN));
if (lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS))
p += sprintf(p, "Sec-WebSocket-Protocol: %s\x0d\x0a",
lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS));
/* tell the server what extensions we could support */
p += sprintf(p, "Sec-WebSocket-Extensions: ");
#ifndef LWS_NO_EXTENSIONS
ext = context->extensions;
while (ext && ext->callback) {
n = lws_ext_callback_for_each_extension_type(context, wsi,
LWS_EXT_CALLBACK_CHECK_OK_TO_PROPOSE_EXTENSION,
(char *)ext->name, 0);
if (n) { /* an extension vetos us */
lwsl_ext("ext %s vetoed\n", (char *)ext->name);
ext++;
continue;
}
n = context->protocols[0].callback(context, wsi,
LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED,
wsi->user_space, (char *)ext->name, 0);
/*
* zero return from callback means
* go ahead and allow the extension,
* it's what we get if the callback is
* unhandled
*/
if (n) {
ext++;
continue;
}
/* apply it */
if (ext_count)
*p++ = ',';
p += sprintf(p, "%s", ext->name);
ext_count++;
ext++;
}
#endif
p += sprintf(p, "\x0d\x0a");
if (wsi->ietf_spec_revision)
p += sprintf(p, "Sec-WebSocket-Version: %d\x0d\x0a",
wsi->ietf_spec_revision);
/* give userland a chance to append, eg, cookies */
context->protocols[0].callback(context, wsi,
LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER,
NULL, &p, (pkt + sizeof(context->service_buffer)) - p - 12);
p += sprintf(p, "\x0d\x0a");
/* prepare the expected server accept response */
key_b64[39] = '\0'; /* enforce composed length below buf sizeof */
n = sprintf(buf, "%s258EAFA5-E914-47DA-95CA-C5AB0DC85B11", key_b64);
SHA1((unsigned char *)buf, n, (unsigned char *)hash);
lws_b64_encode_string(hash, 20,
wsi->u.hdr.ah->initial_handshake_hash_base64,
sizeof(wsi->u.hdr.ah->initial_handshake_hash_base64));
return p;
}

View File

@ -0,0 +1,4 @@
#include "build.h"
#define LWS_BUILTIN_GETIFADDRS
#define LWS_NO_DAEMONIZE

View File

@ -0,0 +1,340 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2014 Andy Green <andy@warmcat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation:
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include "private-libwebsockets.h"
#ifndef LWS_BUILD_HASH
#define LWS_BUILD_HASH "unknown-build-hash"
#endif
static const char *library_version = LWS_LIBRARY_VERSION " " LWS_BUILD_HASH;
/**
* lws_get_library_version: get version and git hash library built from
*
* returns a const char * to a string like "1.1 178d78c"
* representing the library version followed by the git head hash it
* was built from
*/
LWS_VISIBLE const char *
lws_get_library_version(void)
{
return library_version;
}
/**
* libwebsocket_create_context() - Create the websocket handler
* @info: pointer to struct with parameters
*
* This function creates the listening socket (if serving) and takes care
* of all initialization in one step.
*
* After initialization, it returns a struct libwebsocket_context * that
* represents this server. After calling, user code needs to take care
* of calling libwebsocket_service() with the context pointer to get the
* server's sockets serviced. This must be done in the same process
* context as the initialization call.
*
* The protocol callback functions are called for a handful of events
* including http requests coming in, websocket connections becoming
* established, and data arriving; it's also called periodically to allow
* async transmission.
*
* HTTP requests are sent always to the FIRST protocol in @protocol, since
* at that time websocket protocol has not been negotiated. Other
* protocols after the first one never see any HTTP callack activity.
*
* The server created is a simple http server by default; part of the
* websocket standard is upgrading this http connection to a websocket one.
*
* This allows the same server to provide files like scripts and favicon /
* images or whatever over http and dynamic data over websockets all in
* one place; they're all handled in the user callback.
*/
LWS_VISIBLE struct libwebsocket_context *
libwebsocket_create_context(struct lws_context_creation_info *info)
{
struct libwebsocket_context *context = NULL;
char *p;
int pid_daemon = get_daemonize_pid();
lwsl_notice("Initial logging level %d\n", log_level);
lwsl_notice("Library version: %s\n", library_version);
#ifdef LWS_USE_IPV6
if (!(info->options & LWS_SERVER_OPTION_DISABLE_IPV6))
lwsl_notice("IPV6 compiled in and enabled\n");
else
lwsl_notice("IPV6 compiled in but disabled\n");
#else
lwsl_notice("IPV6 not compiled in\n");
#endif
lws_feature_status_libev(info);
lwsl_info(" LWS_MAX_HEADER_LEN: %u\n", LWS_MAX_HEADER_LEN);
lwsl_info(" LWS_MAX_PROTOCOLS: %u\n", LWS_MAX_PROTOCOLS);
lwsl_info(" SPEC_LATEST_SUPPORTED: %u\n", SPEC_LATEST_SUPPORTED);
lwsl_info(" AWAITING_TIMEOUT: %u\n", AWAITING_TIMEOUT);
lwsl_info(" SYSTEM_RANDOM_FILEPATH: '%s'\n", SYSTEM_RANDOM_FILEPATH);
lwsl_info(" LWS_MAX_ZLIB_CONN_BUFFER: %u\n", LWS_MAX_ZLIB_CONN_BUFFER);
if (lws_plat_context_early_init())
return NULL;
context = (struct libwebsocket_context *)
malloc(sizeof(struct libwebsocket_context));
if (!context) {
lwsl_err("No memory for websocket context\n");
return NULL;
}
memset(context, 0, sizeof(*context));
if (pid_daemon) {
context->started_with_parent = pid_daemon;
lwsl_notice(" Started with daemon pid %d\n", pid_daemon);
}
context->listen_service_extraseen = 0;
context->protocols = info->protocols;
context->listen_port = info->port;
context->http_proxy_port = 0;
context->http_proxy_address[0] = '\0';
context->options = info->options;
context->iface = info->iface;
/* to reduce this allocation, */
context->max_fds = getdtablesize();
lwsl_notice(" static allocation: %u + (%u x %u fds) = %u bytes\n",
sizeof(struct libwebsocket_context),
sizeof(struct libwebsocket_pollfd) +
sizeof(struct libwebsocket *),
context->max_fds,
sizeof(struct libwebsocket_context) +
((sizeof(struct libwebsocket_pollfd) +
sizeof(struct libwebsocket *)) *
context->max_fds));
context->fds = (struct libwebsocket_pollfd *)
malloc(sizeof(struct libwebsocket_pollfd) *
context->max_fds);
if (context->fds == NULL) {
lwsl_err("Unable to allocate fds array for %d connections\n",
context->max_fds);
free(context);
return NULL;
}
context->lws_lookup = (struct libwebsocket **)
malloc(sizeof(struct libwebsocket *) * context->max_fds);
if (context->lws_lookup == NULL) {
lwsl_err(
"Unable to allocate lws_lookup array for %d connections\n",
context->max_fds);
free(context->fds);
free(context);
return NULL;
}
memset(context->lws_lookup, 0, sizeof(struct libwebsocket *) *
context->max_fds);
if (lws_plat_init_fd_tables(context)) {
free(context->lws_lookup);
free(context->fds);
free(context);
return NULL;
}
lws_context_init_extensions(info, context);
context->user_space = info->user;
strcpy(context->canonical_hostname, "unknown");
lws_server_get_canonical_hostname(context, info);
/* split the proxy ads:port if given */
if (info->http_proxy_address) {
strncpy(context->http_proxy_address, info->http_proxy_address,
sizeof(context->http_proxy_address) - 1);
context->http_proxy_address[
sizeof(context->http_proxy_address) - 1] = '\0';
context->http_proxy_port = info->http_proxy_port;
} else {
#ifdef HAVE_GETENV
p = getenv("http_proxy");
if (p) {
strncpy(context->http_proxy_address, p,
sizeof(context->http_proxy_address) - 1);
context->http_proxy_address[
sizeof(context->http_proxy_address) - 1] = '\0';
p = strchr(context->http_proxy_address, ':');
if (p == NULL) {
lwsl_err("http_proxy needs to be ads:port\n");
goto bail;
}
*p = '\0';
context->http_proxy_port = atoi(p + 1);
}
#endif
}
if (context->http_proxy_address[0])
lwsl_notice(" Proxy %s:%u\n",
context->http_proxy_address,
context->http_proxy_port);
lwsl_notice(
" per-conn mem: %u + %u headers + protocol rx buf\n",
sizeof(struct libwebsocket),
sizeof(struct allocated_headers));
if (lws_context_init_server_ssl(info, context))
goto bail;
if (lws_context_init_client_ssl(info, context))
goto bail;
if (lws_context_init_server(info, context))
goto bail;
/*
* drop any root privs for this process
* to listen on port < 1023 we would have needed root, but now we are
* listening, we don't want the power for anything else
*/
lws_plat_drop_app_privileges(info);
/* initialize supported protocols */
for (context->count_protocols = 0;
info->protocols[context->count_protocols].callback;
context->count_protocols++) {
lwsl_parser(" Protocol: %s\n",
info->protocols[context->count_protocols].name);
info->protocols[context->count_protocols].owning_server =
context;
info->protocols[context->count_protocols].protocol_index =
context->count_protocols;
/*
* inform all the protocols that they are doing their one-time
* initialization if they want to
*/
info->protocols[context->count_protocols].callback(context,
NULL, LWS_CALLBACK_PROTOCOL_INIT, NULL, NULL, 0);
}
/*
* give all extensions a chance to create any per-context
* allocations they need
*/
if (info->port != CONTEXT_PORT_NO_LISTEN) {
if (lws_ext_callback_for_each_extension_type(context, NULL,
LWS_EXT_CALLBACK_SERVER_CONTEXT_CONSTRUCT,
NULL, 0) < 0)
goto bail;
} else
if (lws_ext_callback_for_each_extension_type(context, NULL,
LWS_EXT_CALLBACK_CLIENT_CONTEXT_CONSTRUCT,
NULL, 0) < 0)
goto bail;
return context;
bail:
libwebsocket_context_destroy(context);
return NULL;
}
/**
* libwebsocket_context_destroy() - Destroy the websocket context
* @context: Websocket context
*
* This function closes any active connections and then frees the
* context. After calling this, any further use of the context is
* undefined.
*/
LWS_VISIBLE void
libwebsocket_context_destroy(struct libwebsocket_context *context)
{
int n;
struct libwebsocket_protocols *protocol = context->protocols;
lwsl_notice("%s\n", __func__);
#ifdef LWS_LATENCY
if (context->worst_latency_info[0])
lwsl_notice("Worst latency: %s\n", context->worst_latency_info);
#endif
for (n = 0; n < context->fds_count; n++) {
struct libwebsocket *wsi =
context->lws_lookup[context->fds[n].fd];
if (!wsi)
continue;
libwebsocket_close_and_free_session(context,
wsi, LWS_CLOSE_STATUS_NOSTATUS /* no protocol close */);
n--;
}
/*
* give all extensions a chance to clean up any per-context
* allocations they might have made
*/
if (context->listen_port) {
if (lws_ext_callback_for_each_extension_type(context, NULL,
LWS_EXT_CALLBACK_SERVER_CONTEXT_DESTRUCT, NULL, 0) < 0)
return;
} else
if (lws_ext_callback_for_each_extension_type(context, NULL,
LWS_EXT_CALLBACK_CLIENT_CONTEXT_DESTRUCT, NULL, 0) < 0)
return;
/*
* inform all the protocols that they are done and will have no more
* callbacks
*/
while (protocol->callback) {
protocol->callback(context, NULL, LWS_CALLBACK_PROTOCOL_DESTROY,
NULL, NULL, 0);
protocol++;
}
lws_plat_context_early_destroy(context);
lws_ssl_context_destroy(context);
if (context->fds)
free(context->fds);
if (context->lws_lookup)
free(context->lws_lookup);
lws_plat_context_late_destroy(context);
free(context);
}

View File

@ -0,0 +1,294 @@
#include "private-libwebsockets.h"
#include "extension-deflate-frame.h"
#include <stdio.h>
#include <string.h>
#include <assert.h>
#define LWS_ZLIB_WINDOW_BITS 15
#define LWS_ZLIB_MEMLEVEL 8
int lws_extension_callback_deflate_frame(
struct libwebsocket_context *context,
struct libwebsocket_extension *ext,
struct libwebsocket *wsi,
enum libwebsocket_extension_callback_reasons reason,
void *user, void *in, size_t len)
{
struct lws_ext_deflate_frame_conn *conn =
(struct lws_ext_deflate_frame_conn *)user;
struct lws_tokens *eff_buf = (struct lws_tokens *)in;
size_t current_payload, remaining_payload, total_payload;
int n;
size_t len_so_far;
switch (reason) {
/*
* for deflate-frame, both client and server sides act the same
*/
case LWS_EXT_CALLBACK_CLIENT_CONSTRUCT:
case LWS_EXT_CALLBACK_CONSTRUCT:
conn->zs_in.zalloc = conn->zs_out.zalloc = Z_NULL;
conn->zs_in.zfree = conn->zs_out.zfree = Z_NULL;
conn->zs_in.opaque = conn->zs_out.opaque = Z_NULL;
n = inflateInit2(&conn->zs_in, -LWS_ZLIB_WINDOW_BITS);
if (n != Z_OK) {
lwsl_ext("deflateInit returned %d\n", n);
return 1;
}
n = deflateInit2(&conn->zs_out,
(context->listen_port ?
DEFLATE_FRAME_COMPRESSION_LEVEL_SERVER :
DEFLATE_FRAME_COMPRESSION_LEVEL_CLIENT),
Z_DEFLATED,
-LWS_ZLIB_WINDOW_BITS, LWS_ZLIB_MEMLEVEL,
Z_DEFAULT_STRATEGY);
if (n != Z_OK) {
lwsl_ext("deflateInit2 returned %d\n", n);
return 1;
}
conn->buf_pre_used = 0;
conn->buf_pre_length = 0;
conn->buf_in_length = sizeof(conn->buf_in);
conn->buf_out_length = sizeof(conn->buf_out);
conn->compressed_out = 0;
conn->buf_pre = NULL;
conn->buf_in = (unsigned char *)
malloc(LWS_SEND_BUFFER_PRE_PADDING +
conn->buf_in_length +
LWS_SEND_BUFFER_POST_PADDING);
if (!conn->buf_in)
goto bail;
conn->buf_out = (unsigned char *)
malloc(LWS_SEND_BUFFER_PRE_PADDING +
conn->buf_out_length +
LWS_SEND_BUFFER_POST_PADDING);
if (!conn->buf_out)
goto bail;
lwsl_ext("zlibs constructed\n");
break;
bail:
lwsl_err("Out of mem\n");
(void)inflateEnd(&conn->zs_in);
(void)deflateEnd(&conn->zs_out);
return -1;
case LWS_EXT_CALLBACK_DESTROY:
if (conn->buf_pre)
free(conn->buf_pre);
free(conn->buf_in);
free(conn->buf_out);
conn->buf_pre_used = 0;
conn->buf_pre_length = 0;
conn->buf_in_length = 0;
conn->buf_out_length = 0;
conn->compressed_out = 0;
(void)inflateEnd(&conn->zs_in);
(void)deflateEnd(&conn->zs_out);
lwsl_ext("zlibs destructed\n");
break;
case LWS_EXT_CALLBACK_PAYLOAD_RX:
if (!(wsi->u.ws.rsv & 0x40))
return 0;
/*
* inflate the incoming payload
*/
current_payload = eff_buf->token_len;
remaining_payload = wsi->u.ws.rx_packet_length;
if (remaining_payload) {
total_payload = conn->buf_pre_used +
current_payload +
remaining_payload;
if (conn->buf_pre_length < total_payload) {
conn->buf_pre_length = total_payload;
if (conn->buf_pre)
free(conn->buf_pre);
conn->buf_pre =
(unsigned char *)malloc(total_payload + 4);
if (!conn->buf_pre) {
lwsl_err("Out of memory\n");
return -1;
}
}
memcpy(conn->buf_pre + conn->buf_pre_used,
eff_buf->token, current_payload);
conn->buf_pre_used += current_payload;
eff_buf->token = NULL;
eff_buf->token_len = 0;
return 0;
}
if (conn->buf_pre_used) {
total_payload = conn->buf_pre_used +
current_payload;
memcpy(conn->buf_pre + conn->buf_pre_used,
eff_buf->token, current_payload);
conn->buf_pre_used = 0;
conn->zs_in.next_in = conn->buf_pre;
} else {
total_payload = current_payload;
conn->zs_in.next_in = (unsigned char *)eff_buf->token;
}
conn->zs_in.next_in[total_payload + 0] = 0;
conn->zs_in.next_in[total_payload + 1] = 0;
conn->zs_in.next_in[total_payload + 2] = 0xff;
conn->zs_in.next_in[total_payload + 3] = 0xff;
conn->zs_in.avail_in = total_payload + 4;
conn->zs_in.next_out =
conn->buf_in + LWS_SEND_BUFFER_PRE_PADDING;
conn->zs_in.avail_out = conn->buf_in_length;
while (1) {
n = inflate(&conn->zs_in, Z_SYNC_FLUSH);
switch (n) {
case Z_NEED_DICT:
case Z_STREAM_ERROR:
case Z_DATA_ERROR:
case Z_MEM_ERROR:
/*
* screwed.. close the connection...
* we will get a destroy callback to take care
* of closing nicely
*/
lwsl_info("zlib error inflate %d: %s\n",
n, conn->zs_in.msg);
return -1;
}
if (conn->zs_in.avail_out)
break;
len_so_far = conn->zs_in.next_out -
(conn->buf_in + LWS_SEND_BUFFER_PRE_PADDING);
conn->buf_in_length *= 2;
if (conn->buf_in_length > LWS_MAX_ZLIB_CONN_BUFFER) {
lwsl_ext("zlib in buffer hit limit %u\n",
LWS_MAX_ZLIB_CONN_BUFFER);
return -1;
}
conn->buf_in = (unsigned char *)realloc(conn->buf_in,
LWS_SEND_BUFFER_PRE_PADDING +
conn->buf_in_length +
LWS_SEND_BUFFER_POST_PADDING);
if (!conn->buf_in) {
lwsl_err("Out of memory\n");
return -1;
}
lwsl_debug(
"deflate-frame ext RX did realloc to %ld\n",
conn->buf_in_length);
conn->zs_in.next_out = conn->buf_in +
LWS_SEND_BUFFER_PRE_PADDING + len_so_far;
conn->zs_in.avail_out =
conn->buf_in_length - len_so_far;
}
/* rewrite the buffer pointers and length */
eff_buf->token =
(char *)(conn->buf_in + LWS_SEND_BUFFER_PRE_PADDING);
eff_buf->token_len = (int)(conn->zs_in.next_out -
(conn->buf_in + LWS_SEND_BUFFER_PRE_PADDING));
return 0;
case LWS_EXT_CALLBACK_PAYLOAD_TX:
/*
* deflate the outgoing payload
*/
current_payload = eff_buf->token_len;
conn->zs_out.next_in = (unsigned char *)eff_buf->token;
conn->zs_out.avail_in = current_payload;
conn->zs_out.next_out =
conn->buf_out + LWS_SEND_BUFFER_PRE_PADDING;
conn->zs_out.avail_out = conn->buf_out_length;
while (1) {
n = deflate(&conn->zs_out, Z_SYNC_FLUSH);
if (n == Z_STREAM_ERROR) {
/*
* screwed.. close the connection... we will
* get a destroy callback to take care of
* closing nicely
*/
lwsl_ext("zlib error deflate\n");
return -1;
}
if (conn->zs_out.avail_out)
break;
len_so_far = (conn->zs_out.next_out -
(conn->buf_out +
LWS_SEND_BUFFER_PRE_PADDING));
conn->buf_out_length *= 2;
if (conn->buf_out_length > LWS_MAX_ZLIB_CONN_BUFFER) {
lwsl_ext("zlib out hit limit %u\n",
LWS_MAX_ZLIB_CONN_BUFFER);
return -1;
}
conn->buf_out = (unsigned char *)realloc(
conn->buf_out,
LWS_SEND_BUFFER_PRE_PADDING +
conn->buf_out_length +
LWS_SEND_BUFFER_POST_PADDING);
if (!conn->buf_out) {
lwsl_err("Out of memory\n");
return -1;
}
lwsl_debug(
"deflate-frame ext TX did realloc to %ld\n",
conn->buf_in_length);
conn->zs_out.next_out = (conn->buf_out +
LWS_SEND_BUFFER_PRE_PADDING + len_so_far);
conn->zs_out.avail_out =
(conn->buf_out_length - len_so_far);
}
conn->compressed_out = 1;
/* rewrite the buffer pointers and length */
eff_buf->token = (char *)(conn->buf_out +
LWS_SEND_BUFFER_PRE_PADDING);
eff_buf->token_len = (int)(conn->zs_out.next_out -
(conn->buf_out + LWS_SEND_BUFFER_PRE_PADDING)) - 4;
return 0;
case LWS_EXT_CALLBACK_PACKET_TX_PRESEND:
if (conn->compressed_out) {
conn->compressed_out = 0;
*((unsigned char *)eff_buf->token) |= 0x40;
}
break;
case LWS_EXT_CALLBACK_CHECK_OK_TO_PROPOSE_EXTENSION:
/* Avoid x-webkit-deflate-frame extension on client */
if (!strcmp((char *)in, "x-webkit-deflate-frame"))
return 1;
break;
default:
break;
}
return 0;
}

View File

@ -0,0 +1,25 @@
#include "deps/zlib/zlib.h"
#define DEFLATE_FRAME_COMPRESSION_LEVEL_SERVER 1
#define DEFLATE_FRAME_COMPRESSION_LEVEL_CLIENT Z_DEFAULT_COMPRESSION
struct lws_ext_deflate_frame_conn {
z_stream zs_in;
z_stream zs_out;
size_t buf_pre_used;
size_t buf_pre_length;
size_t buf_in_length;
size_t buf_out_length;
int compressed_out;
unsigned char *buf_pre;
unsigned char *buf_in;
unsigned char *buf_out;
};
extern int lws_extension_callback_deflate_frame(
struct libwebsocket_context *context,
struct libwebsocket_extension *ext,
struct libwebsocket *wsi,
enum libwebsocket_extension_callback_reasons reason,
void *user, void *in, size_t len);

View File

@ -0,0 +1,166 @@
#include "private-libwebsockets.h"
#include "extension-deflate-stream.h"
#include <stdio.h>
#include <string.h>
#include <assert.h>
#define LWS_ZLIB_WINDOW_BITS 15
#define LWS_ZLIB_MEMLEVEL 8
int lws_extension_callback_deflate_stream(
struct libwebsocket_context *context,
struct libwebsocket_extension *ext,
struct libwebsocket *wsi,
enum libwebsocket_extension_callback_reasons reason,
void *user, void *in, size_t len)
{
struct lws_ext_deflate_stream_conn *conn =
(struct lws_ext_deflate_stream_conn *)user;
int n;
struct lws_tokens *eff_buf = (struct lws_tokens *)in;
switch (reason) {
/*
* for deflate-stream, both client and server sides act the same
*/
case LWS_EXT_CALLBACK_CLIENT_CONSTRUCT:
case LWS_EXT_CALLBACK_CONSTRUCT:
conn->zs_in.zalloc = conn->zs_out.zalloc = Z_NULL;
conn->zs_in.zfree = conn->zs_out.zfree = Z_NULL;
conn->zs_in.opaque = conn->zs_out.opaque = Z_NULL;
n = inflateInit2(&conn->zs_in, -LWS_ZLIB_WINDOW_BITS);
if (n != Z_OK) {
lwsl_err("deflateInit returned %d\n", n);
return 1;
}
n = deflateInit2(&conn->zs_out,
DEFLATE_STREAM_COMPRESSION_LEVEL, Z_DEFLATED,
-LWS_ZLIB_WINDOW_BITS, LWS_ZLIB_MEMLEVEL,
Z_DEFAULT_STRATEGY);
if (n != Z_OK) {
lwsl_err("deflateInit returned %d\n", n);
return 1;
}
lwsl_ext("zlibs constructed\n");
conn->remaining_in = 0;
break;
case LWS_EXT_CALLBACK_DESTROY:
(void)inflateEnd(&conn->zs_in);
(void)deflateEnd(&conn->zs_out);
lwsl_ext("zlibs destructed\n");
break;
case LWS_EXT_CALLBACK_PACKET_RX_PREPARSE:
/*
* inflate the incoming compressed data
* Notice, length may be 0 and pointer NULL
* in the case we are flushing with nothing new coming in
*/
if (conn->remaining_in) {
conn->zs_in.next_in = conn->buf_in;
conn->zs_in.avail_in = conn->remaining_in;
conn->remaining_in = 0;
} else {
conn->zs_in.next_in = (unsigned char *)eff_buf->token;
conn->zs_in.avail_in = eff_buf->token_len;
}
conn->zs_in.next_out = conn->buf_out;
conn->zs_in.avail_out = sizeof(conn->buf_out);
n = inflate(&conn->zs_in, Z_SYNC_FLUSH);
switch (n) {
case Z_NEED_DICT:
case Z_DATA_ERROR:
case Z_MEM_ERROR:
/*
* screwed.. close the connection... we will get a
* destroy callback to take care of closing nicely
*/
lwsl_err("zlib error inflate %d\n", n);
return -1;
}
/* rewrite the buffer pointers and length */
eff_buf->token = (char *)conn->buf_out;
eff_buf->token_len =
sizeof(conn->buf_out) - conn->zs_in.avail_out;
/* copy avail data if not consumed */
if (conn->zs_in.avail_in > 0) {
conn->remaining_in = conn->zs_in.avail_in;
memcpy(conn->buf_in, conn->zs_in.next_in,
conn->zs_in.avail_in);
return 1;
}
/*
* if we filled the output buffer, signal that we likely have
* more and need to be called again
*/
if (eff_buf->token_len == sizeof(conn->buf_out))
return 1;
/* we don't need calling again until new input data comes */
return 0;
case LWS_EXT_CALLBACK_FLUSH_PENDING_TX:
case LWS_EXT_CALLBACK_PACKET_TX_PRESEND:
/*
* deflate the outgoing compressed data
*/
conn->zs_out.next_in = (unsigned char *)eff_buf->token;
conn->zs_out.avail_in = eff_buf->token_len;
conn->zs_out.next_out = conn->buf_out;
conn->zs_out.avail_out = sizeof(conn->buf_out);
n = Z_PARTIAL_FLUSH;
if (reason == LWS_EXT_CALLBACK_FLUSH_PENDING_TX)
n = Z_FULL_FLUSH;
n = deflate(&conn->zs_out, n);
if (n == Z_STREAM_ERROR) {
/*
* screwed.. close the connection... we will get a
* destroy callback to take care of closing nicely
*/
lwsl_ext("zlib error deflate\n");
return -1;
}
/* rewrite the buffer pointers and length */
eff_buf->token = (char *)conn->buf_out;
eff_buf->token_len =
sizeof(conn->buf_out) - conn->zs_out.avail_out;
/*
* if we filled the output buffer, signal that we likely have
* more and need to be called again... even in deflate case
* we might sometimes need to spill more than came in
*/
if (eff_buf->token_len == sizeof(conn->buf_out))
return 1;
/* we don't need calling again until new input data comes */
return 0;
default:
break;
}
return 0;
}

View File

@ -0,0 +1,20 @@
#include "deps/zlib/zlib.h"
#define DEFLATE_STREAM_CHUNK 128
#define DEFLATE_STREAM_COMPRESSION_LEVEL 1
struct lws_ext_deflate_stream_conn {
z_stream zs_in;
z_stream zs_out;
int remaining_in;
unsigned char buf_in[LWS_MAX_SOCKET_IO_BUF];
unsigned char buf_out[LWS_MAX_SOCKET_IO_BUF];
};
extern int lws_extension_callback_deflate_stream(
struct libwebsocket_context *context,
struct libwebsocket_extension *ext,
struct libwebsocket *wsi,
enum libwebsocket_extension_callback_reasons reason,
void *user, void *in, size_t len);

View File

@ -0,0 +1,206 @@
#include "private-libwebsockets.h"
#include "extension-deflate-frame.h"
#include "extension-deflate-stream.h"
struct libwebsocket_extension libwebsocket_internal_extensions[] = {
#ifdef LWS_EXT_DEFLATE_STREAM
{
"deflate-stream",
lws_extension_callback_deflate_stream,
sizeof(struct lws_ext_deflate_stream_conn)
},
#else
{
"x-webkit-deflate-frame",
lws_extension_callback_deflate_frame,
sizeof(struct lws_ext_deflate_frame_conn)
},
{
"deflate-frame",
lws_extension_callback_deflate_frame,
sizeof(struct lws_ext_deflate_frame_conn)
},
#endif
{ /* terminator */
NULL, NULL, 0
}
};
LWS_VISIBLE void
lws_context_init_extensions(struct lws_context_creation_info *info,
struct libwebsocket_context *context)
{
context->extensions = info->extensions;
lwsl_info(" LWS_MAX_EXTENSIONS_ACTIVE: %u\n", LWS_MAX_EXTENSIONS_ACTIVE);
}
LWS_VISIBLE struct libwebsocket_extension *libwebsocket_get_internal_extensions()
{
return libwebsocket_internal_extensions;
}
/* 0 = nobody had nonzero return, 1 = somebody had positive return, -1 = fail */
int lws_ext_callback_for_each_active(struct libwebsocket *wsi, int reason,
void *arg, int len)
{
int n, m, handled = 0;
for (n = 0; n < wsi->count_active_extensions; n++) {
m = wsi->active_extensions[n]->callback(
wsi->protocol->owning_server,
wsi->active_extensions[n], wsi,
reason,
wsi->active_extensions_user[n],
arg, len);
if (m < 0) {
lwsl_ext(
"Extension '%s' failed to handle callback %d!\n",
wsi->active_extensions[n]->name, reason);
return -1;
}
if (m > handled)
handled = m;
}
return handled;
}
int lws_ext_callback_for_each_extension_type(
struct libwebsocket_context *context, struct libwebsocket *wsi,
int reason, void *arg, int len)
{
int n = 0, m, handled = 0;
struct libwebsocket_extension *ext = context->extensions;
while (ext && ext->callback && !handled) {
m = ext->callback(context, ext, wsi, reason,
(void *)(long)n, arg, len);
if (m < 0) {
lwsl_ext(
"Extension '%s' failed to handle callback %d!\n",
wsi->active_extensions[n]->name, reason);
return -1;
}
if (m)
handled = 1;
ext++;
n++;
}
return 0;
}
int
lws_issue_raw_ext_access(struct libwebsocket *wsi,
unsigned char *buf, size_t len)
{
int ret;
struct lws_tokens eff_buf;
int m;
int n;
eff_buf.token = (char *)buf;
eff_buf.token_len = len;
/*
* while we have original buf to spill ourselves, or extensions report
* more in their pipeline
*/
ret = 1;
while (ret == 1) {
/* default to nobody has more to spill */
ret = 0;
/* show every extension the new incoming data */
m = lws_ext_callback_for_each_active(wsi,
LWS_EXT_CALLBACK_PACKET_TX_PRESEND, &eff_buf, 0);
if (m < 0)
return -1;
if (m) /* handled */
ret = 1;
if ((char *)buf != eff_buf.token)
/*
* extension recreated it:
* need to buffer this if not all sent
*/
wsi->u.ws.clean_buffer = 0;
/* assuming they left us something to send, send it */
if (eff_buf.token_len) {
n = lws_issue_raw(wsi, (unsigned char *)eff_buf.token,
eff_buf.token_len);
if (n < 0) {
lwsl_info("closing from ext access\n");
return -1;
}
/* always either sent it all or privately buffered */
}
lwsl_parser("written %d bytes to client\n", eff_buf.token_len);
/* no extension has more to spill? Then we can go */
if (!ret)
break;
/* we used up what we had */
eff_buf.token = NULL;
eff_buf.token_len = 0;
/*
* Did that leave the pipe choked?
* Or we had to hold on to some of it?
*/
if (!lws_send_pipe_choked(wsi) && !wsi->truncated_send_len)
/* no we could add more, lets's do that */
continue;
lwsl_debug("choked\n");
/*
* Yes, he's choked. Don't spill the rest now get a callback
* when he is ready to send and take care of it there
*/
libwebsocket_callback_on_writable(
wsi->protocol->owning_server, wsi);
wsi->extension_data_pending = 1;
ret = 0;
}
return len;
}
int
lws_any_extension_handled(struct libwebsocket_context *context,
struct libwebsocket *wsi,
enum libwebsocket_extension_callback_reasons r,
void *v, size_t len)
{
int n;
int handled = 0;
/* maybe an extension will take care of it for us */
for (n = 0; n < wsi->count_active_extensions && !handled; n++) {
if (!wsi->active_extensions[n]->callback)
continue;
handled |= wsi->active_extensions[n]->callback(context,
wsi->active_extensions[n], wsi,
r, wsi->active_extensions_user[n], v, len);
}
return handled;
}

View File

@ -0,0 +1,171 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2013 Andy Green <andy@warmcat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation:
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include "private-libwebsockets.h"
/*
* -04 of the protocol (actually the 80th version) has a radically different
* handshake. The 04 spec gives the following idea
*
* The handshake from the client looks as follows:
*
* GET /chat HTTP/1.1
* Host: server.example.com
* Upgrade: websocket
* Connection: Upgrade
* Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
* Sec-WebSocket-Origin: http://example.com
* Sec-WebSocket-Protocol: chat, superchat
* Sec-WebSocket-Version: 4
*
* The handshake from the server looks as follows:
*
* HTTP/1.1 101 Switching Protocols
* Upgrade: websocket
* Connection: Upgrade
* Sec-WebSocket-Accept: me89jWimTRKTWwrS3aRrL53YZSo=
* Sec-WebSocket-Nonce: AQIDBAUGBwgJCgsMDQ4PEC==
* Sec-WebSocket-Protocol: chat
*/
/*
* We have to take care about parsing because the headers may be split
* into multiple fragments. They may contain unknown headers with arbitrary
* argument lengths. So, we parse using a single-character at a time state
* machine that is completely independent of packet size.
*/
LWS_VISIBLE int
libwebsocket_read(struct libwebsocket_context *context,
struct libwebsocket *wsi, unsigned char *buf, size_t len)
{
size_t n;
switch (wsi->state) {
case WSI_STATE_HTTP_BODY:
http_postbody:
while (len--) {
if (wsi->u.http.content_length_seen >= wsi->u.http.content_length)
break;
wsi->u.http.post_buffer[wsi->u.http.body_index++] = *buf++;
wsi->u.http.content_length_seen++;
n = wsi->protocol->rx_buffer_size;
if (!n)
n = LWS_MAX_SOCKET_IO_BUF;
if (wsi->u.http.body_index != n &&
wsi->u.http.content_length_seen != wsi->u.http.content_length)
continue;
if (wsi->protocol->callback) {
n = wsi->protocol->callback(
wsi->protocol->owning_server, wsi,
LWS_CALLBACK_HTTP_BODY,
wsi->user_space, wsi->u.http.post_buffer,
wsi->u.http.body_index);
wsi->u.http.body_index = 0;
if (n)
goto bail;
}
if (wsi->u.http.content_length_seen == wsi->u.http.content_length) {
/* he sent the content in time */
libwebsocket_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
n = wsi->protocol->callback(
wsi->protocol->owning_server, wsi,
LWS_CALLBACK_HTTP_BODY_COMPLETION,
wsi->user_space, NULL, 0);
wsi->u.http.body_index = 0;
if (n)
goto bail;
}
}
/*
* we need to spill here so everything is seen in the case
* there is no content-length
*/
if (wsi->u.http.body_index && wsi->protocol->callback) {
n = wsi->protocol->callback(
wsi->protocol->owning_server, wsi,
LWS_CALLBACK_HTTP_BODY,
wsi->user_space, wsi->u.http.post_buffer,
wsi->u.http.body_index);
wsi->u.http.body_index = 0;
if (n)
goto bail;
}
break;
case WSI_STATE_HTTP_ISSUING_FILE:
case WSI_STATE_HTTP:
wsi->state = WSI_STATE_HTTP_HEADERS;
wsi->u.hdr.parser_state = WSI_TOKEN_NAME_PART;
wsi->u.hdr.lextable_pos = 0;
/* fallthru */
case WSI_STATE_HTTP_HEADERS:
lwsl_parser("issuing %d bytes to parser\n", (int)len);
if (lws_handshake_client(wsi, &buf, len))
goto bail;
switch (lws_handshake_server(context, wsi, &buf, len)) {
case 1:
goto bail;
case 2:
goto http_postbody;
}
break;
case WSI_STATE_AWAITING_CLOSE_ACK:
case WSI_STATE_ESTABLISHED:
if (lws_handshake_client(wsi, &buf, len))
goto bail;
switch (wsi->mode) {
case LWS_CONNMODE_WS_SERVING:
if (libwebsocket_interpret_incoming_packet(wsi, buf, len) < 0) {
lwsl_info("interpret_incoming_packet has bailed\n");
goto bail;
}
break;
}
break;
default:
lwsl_err("libwebsocket_read: Unhandled state\n");
break;
}
return 0;
bail:
lwsl_debug("closing connection at libwebsocket_read bail:\n");
libwebsocket_close_and_free_session(context, wsi,
LWS_CLOSE_STATUS_NOSTATUS);
return -1;
}

View File

@ -0,0 +1,287 @@
/* pos 0000: 0 */ 0x67 /* 'g' */, 0x25, 0x00 /* (to 0x0025 state 1) */,
0x70 /* 'p' */, 0x27, 0x00 /* (to 0x002A state 5) */,
0x68 /* 'h' */, 0x30, 0x00 /* (to 0x0036 state 10) */,
0x63 /* 'c' */, 0x39, 0x00 /* (to 0x0042 state 15) */,
0x73 /* 's' */, 0x54, 0x00 /* (to 0x0060 state 26) */,
0x75 /* 'u' */, 0x93, 0x00 /* (to 0x00A2 state 56) */,
0x6F /* 'o' */, 0x99, 0x00 /* (to 0x00AB state 64) */,
0x0D /* '.' */, 0xA5, 0x00 /* (to 0x00BA state 77) */,
0x61 /* 'a' */, 0xDC, 0x00 /* (to 0x00F4 state 127) */,
0x69 /* 'i' */, 0xED, 0x00 /* (to 0x0108 state 134) */,
0x64 /* 'd' */, 0x5A, 0x01 /* (to 0x0178 state 225) */,
0x72 /* 'r' */, 0x5D, 0x01 /* (to 0x017E state 230) */,
0x08, /* fail */
/* pos 0025: 1 */ 0xE5 /* 'e' -> */,
/* pos 0026: 2 */ 0xF4 /* 't' -> */,
/* pos 0027: 3 */ 0xA0 /* ' ' -> */,
/* pos 0028: 4 */ 0x00, 0x00 /* - terminal marker 0 - */,
/* pos 002a: 5 */ 0x6F /* 'o' */, 0x07, 0x00 /* (to 0x0031 state 6) */,
0x72 /* 'r' */, 0x09, 0x01 /* (to 0x0136 state 171) */,
0x08, /* fail */
/* pos 0031: 6 */ 0xF3 /* 's' -> */,
/* pos 0032: 7 */ 0xF4 /* 't' -> */,
/* pos 0033: 8 */ 0xA0 /* ' ' -> */,
/* pos 0034: 9 */ 0x00, 0x01 /* - terminal marker 1 - */,
/* pos 0036: 10 */ 0x6F /* 'o' */, 0x07, 0x00 /* (to 0x003D state 11) */,
0x74 /* 't' */, 0xB2, 0x00 /* (to 0x00EB state 119) */,
0x08, /* fail */
/* pos 003d: 11 */ 0xF3 /* 's' -> */,
/* pos 003e: 12 */ 0xF4 /* 't' -> */,
/* pos 003f: 13 */ 0xBA /* ':' -> */,
/* pos 0040: 14 */ 0x00, 0x02 /* - terminal marker 2 - */,
/* pos 0042: 15 */ 0x6F /* 'o' */, 0x07, 0x00 /* (to 0x0049 state 16) */,
0x61 /* 'a' */, 0xF8, 0x00 /* (to 0x013D state 177) */,
0x08, /* fail */
/* pos 0049: 16 */ 0x6E /* 'n' */, 0x07, 0x00 /* (to 0x0050 state 17) */,
0x6F /* 'o' */, 0x0D, 0x01 /* (to 0x0159 state 203) */,
0x08, /* fail */
/* pos 0050: 17 */ 0x6E /* 'n' */, 0x07, 0x00 /* (to 0x0057 state 18) */,
0x74 /* 't' */, 0x0C, 0x01 /* (to 0x015F state 208) */,
0x08, /* fail */
/* pos 0057: 18 */ 0xE5 /* 'e' -> */,
/* pos 0058: 19 */ 0xE3 /* 'c' -> */,
/* pos 0059: 20 */ 0xF4 /* 't' -> */,
/* pos 005a: 21 */ 0xE9 /* 'i' -> */,
/* pos 005b: 22 */ 0xEF /* 'o' -> */,
/* pos 005c: 23 */ 0xEE /* 'n' -> */,
/* pos 005d: 24 */ 0xBA /* ':' -> */,
/* pos 005e: 25 */ 0x00, 0x03 /* - terminal marker 3 - */,
/* pos 0060: 26 */ 0xE5 /* 'e' -> */,
/* pos 0061: 27 */ 0xE3 /* 'c' -> */,
/* pos 0062: 28 */ 0xAD /* '-' -> */,
/* pos 0063: 29 */ 0xF7 /* 'w' -> */,
/* pos 0064: 30 */ 0xE5 /* 'e' -> */,
/* pos 0065: 31 */ 0xE2 /* 'b' -> */,
/* pos 0066: 32 */ 0xF3 /* 's' -> */,
/* pos 0067: 33 */ 0xEF /* 'o' -> */,
/* pos 0068: 34 */ 0xE3 /* 'c' -> */,
/* pos 0069: 35 */ 0xEB /* 'k' -> */,
/* pos 006a: 36 */ 0xE5 /* 'e' -> */,
/* pos 006b: 37 */ 0xF4 /* 't' -> */,
/* pos 006c: 38 */ 0xAD /* '-' -> */,
/* pos 006d: 39 */ 0x6B /* 'k' */, 0x19, 0x00 /* (to 0x0086 state 40) */,
0x70 /* 'p' */, 0x28, 0x00 /* (to 0x0098 state 47) */,
0x64 /* 'd' */, 0x40, 0x00 /* (to 0x00B3 state 71) */,
0x76 /* 'v' */, 0x49, 0x00 /* (to 0x00BF state 80) */,
0x6F /* 'o' */, 0x4F, 0x00 /* (to 0x00C8 state 88) */,
0x65 /* 'e' */, 0x54, 0x00 /* (to 0x00D0 state 95) */,
0x61 /* 'a' */, 0x5D, 0x00 /* (to 0x00DC state 106) */,
0x6E /* 'n' */, 0x62, 0x00 /* (to 0x00E4 state 113) */,
0x08, /* fail */
/* pos 0086: 40 */ 0xE5 /* 'e' -> */,
/* pos 0087: 41 */ 0xF9 /* 'y' -> */,
/* pos 0088: 42 */ 0x31 /* '1' */, 0x0A, 0x00 /* (to 0x0092 state 43) */,
0x32 /* '2' */, 0x0A, 0x00 /* (to 0x0095 state 45) */,
0x3A /* ':' */, 0x2F, 0x00 /* (to 0x00BD state 79) */,
0x08, /* fail */
/* pos 0092: 43 */ 0xBA /* ':' -> */,
/* pos 0093: 44 */ 0x00, 0x04 /* - terminal marker 4 - */,
/* pos 0095: 45 */ 0xBA /* ':' -> */,
/* pos 0096: 46 */ 0x00, 0x05 /* - terminal marker 5 - */,
/* pos 0098: 47 */ 0xF2 /* 'r' -> */,
/* pos 0099: 48 */ 0xEF /* 'o' -> */,
/* pos 009a: 49 */ 0xF4 /* 't' -> */,
/* pos 009b: 50 */ 0xEF /* 'o' -> */,
/* pos 009c: 51 */ 0xE3 /* 'c' -> */,
/* pos 009d: 52 */ 0xEF /* 'o' -> */,
/* pos 009e: 53 */ 0xEC /* 'l' -> */,
/* pos 009f: 54 */ 0xBA /* ':' -> */,
/* pos 00a0: 55 */ 0x00, 0x06 /* - terminal marker 6 - */,
/* pos 00a2: 56 */ 0xF0 /* 'p' -> */,
/* pos 00a3: 57 */ 0xE7 /* 'g' -> */,
/* pos 00a4: 58 */ 0xF2 /* 'r' -> */,
/* pos 00a5: 59 */ 0xE1 /* 'a' -> */,
/* pos 00a6: 60 */ 0xE4 /* 'd' -> */,
/* pos 00a7: 61 */ 0xE5 /* 'e' -> */,
/* pos 00a8: 62 */ 0xBA /* ':' -> */,
/* pos 00a9: 63 */ 0x00, 0x07 /* - terminal marker 7 - */,
/* pos 00ab: 64 */ 0xF2 /* 'r' -> */,
/* pos 00ac: 65 */ 0xE9 /* 'i' -> */,
/* pos 00ad: 66 */ 0xE7 /* 'g' -> */,
/* pos 00ae: 67 */ 0xE9 /* 'i' -> */,
/* pos 00af: 68 */ 0xEE /* 'n' -> */,
/* pos 00b0: 69 */ 0xBA /* ':' -> */,
/* pos 00b1: 70 */ 0x00, 0x08 /* - terminal marker 8 - */,
/* pos 00b3: 71 */ 0xF2 /* 'r' -> */,
/* pos 00b4: 72 */ 0xE1 /* 'a' -> */,
/* pos 00b5: 73 */ 0xE6 /* 'f' -> */,
/* pos 00b6: 74 */ 0xF4 /* 't' -> */,
/* pos 00b7: 75 */ 0xBA /* ':' -> */,
/* pos 00b8: 76 */ 0x00, 0x09 /* - terminal marker 9 - */,
/* pos 00ba: 77 */ 0x8A /* '.' -> */,
/* pos 00bb: 78 */ 0x00, 0x0A /* - terminal marker 10 - */,
/* pos 00bd: 79 */ 0x00, 0x0B /* - terminal marker 11 - */,
/* pos 00bf: 80 */ 0xE5 /* 'e' -> */,
/* pos 00c0: 81 */ 0xF2 /* 'r' -> */,
/* pos 00c1: 82 */ 0xF3 /* 's' -> */,
/* pos 00c2: 83 */ 0xE9 /* 'i' -> */,
/* pos 00c3: 84 */ 0xEF /* 'o' -> */,
/* pos 00c4: 85 */ 0xEE /* 'n' -> */,
/* pos 00c5: 86 */ 0xBA /* ':' -> */,
/* pos 00c6: 87 */ 0x00, 0x0C /* - terminal marker 12 - */,
/* pos 00c8: 88 */ 0xF2 /* 'r' -> */,
/* pos 00c9: 89 */ 0xE9 /* 'i' -> */,
/* pos 00ca: 90 */ 0xE7 /* 'g' -> */,
/* pos 00cb: 91 */ 0xE9 /* 'i' -> */,
/* pos 00cc: 92 */ 0xEE /* 'n' -> */,
/* pos 00cd: 93 */ 0xBA /* ':' -> */,
/* pos 00ce: 94 */ 0x00, 0x0D /* - terminal marker 13 - */,
/* pos 00d0: 95 */ 0xF8 /* 'x' -> */,
/* pos 00d1: 96 */ 0xF4 /* 't' -> */,
/* pos 00d2: 97 */ 0xE5 /* 'e' -> */,
/* pos 00d3: 98 */ 0xEE /* 'n' -> */,
/* pos 00d4: 99 */ 0xF3 /* 's' -> */,
/* pos 00d5: 100 */ 0xE9 /* 'i' -> */,
/* pos 00d6: 101 */ 0xEF /* 'o' -> */,
/* pos 00d7: 102 */ 0xEE /* 'n' -> */,
/* pos 00d8: 103 */ 0xF3 /* 's' -> */,
/* pos 00d9: 104 */ 0xBA /* ':' -> */,
/* pos 00da: 105 */ 0x00, 0x0E /* - terminal marker 14 - */,
/* pos 00dc: 106 */ 0xE3 /* 'c' -> */,
/* pos 00dd: 107 */ 0xE3 /* 'c' -> */,
/* pos 00de: 108 */ 0xE5 /* 'e' -> */,
/* pos 00df: 109 */ 0xF0 /* 'p' -> */,
/* pos 00e0: 110 */ 0xF4 /* 't' -> */,
/* pos 00e1: 111 */ 0xBA /* ':' -> */,
/* pos 00e2: 112 */ 0x00, 0x0F /* - terminal marker 15 - */,
/* pos 00e4: 113 */ 0xEF /* 'o' -> */,
/* pos 00e5: 114 */ 0xEE /* 'n' -> */,
/* pos 00e6: 115 */ 0xE3 /* 'c' -> */,
/* pos 00e7: 116 */ 0xE5 /* 'e' -> */,
/* pos 00e8: 117 */ 0xBA /* ':' -> */,
/* pos 00e9: 118 */ 0x00, 0x10 /* - terminal marker 16 - */,
/* pos 00eb: 119 */ 0xF4 /* 't' -> */,
/* pos 00ec: 120 */ 0xF0 /* 'p' -> */,
/* pos 00ed: 121 */ 0xAF /* '/' -> */,
/* pos 00ee: 122 */ 0xB1 /* '1' -> */,
/* pos 00ef: 123 */ 0xAE /* '.' -> */,
/* pos 00f0: 124 */ 0xB1 /* '1' -> */,
/* pos 00f1: 125 */ 0xA0 /* ' ' -> */,
/* pos 00f2: 126 */ 0x00, 0x11 /* - terminal marker 17 - */,
/* pos 00f4: 127 */ 0x63 /* 'c' */, 0x07, 0x00 /* (to 0x00FB state 128) */,
0x75 /* 'u' */, 0x54, 0x00 /* (to 0x014B state 190) */,
0x08, /* fail */
/* pos 00fb: 128 */ 0xE3 /* 'c' -> */,
/* pos 00fc: 129 */ 0xE5 /* 'e' -> */,
/* pos 00fd: 130 */ 0xF0 /* 'p' -> */,
/* pos 00fe: 131 */ 0xF4 /* 't' -> */,
/* pos 00ff: 132 */ 0x3A /* ':' */, 0x07, 0x00 /* (to 0x0106 state 133) */,
0x2D /* '-' */, 0x19, 0x00 /* (to 0x011B state 152) */,
0x08, /* fail */
/* pos 0106: 133 */ 0x00, 0x12 /* - terminal marker 18 - */,
/* pos 0108: 134 */ 0xE6 /* 'f' -> */,
/* pos 0109: 135 */ 0xAD /* '-' -> */,
/* pos 010a: 136 */ 0xED /* 'm' -> */,
/* pos 010b: 137 */ 0xEF /* 'o' -> */,
/* pos 010c: 138 */ 0xE4 /* 'd' -> */,
/* pos 010d: 139 */ 0xE9 /* 'i' -> */,
/* pos 010e: 140 */ 0xE6 /* 'f' -> */,
/* pos 010f: 141 */ 0xE9 /* 'i' -> */,
/* pos 0110: 142 */ 0xE5 /* 'e' -> */,
/* pos 0111: 143 */ 0xE4 /* 'd' -> */,
/* pos 0112: 144 */ 0xAD /* '-' -> */,
/* pos 0113: 145 */ 0xF3 /* 's' -> */,
/* pos 0114: 146 */ 0xE9 /* 'i' -> */,
/* pos 0115: 147 */ 0xEE /* 'n' -> */,
/* pos 0116: 148 */ 0xE3 /* 'c' -> */,
/* pos 0117: 149 */ 0xE5 /* 'e' -> */,
/* pos 0118: 150 */ 0xBA /* ':' -> */,
/* pos 0119: 151 */ 0x00, 0x13 /* - terminal marker 19 - */,
/* pos 011b: 152 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x0122 state 153) */,
0x6C /* 'l' */, 0x0E, 0x00 /* (to 0x012C state 162) */,
0x08, /* fail */
/* pos 0122: 153 */ 0xEE /* 'n' -> */,
/* pos 0123: 154 */ 0xE3 /* 'c' -> */,
/* pos 0124: 155 */ 0xEF /* 'o' -> */,
/* pos 0125: 156 */ 0xE4 /* 'd' -> */,
/* pos 0126: 157 */ 0xE9 /* 'i' -> */,
/* pos 0127: 158 */ 0xEE /* 'n' -> */,
/* pos 0128: 159 */ 0xE7 /* 'g' -> */,
/* pos 0129: 160 */ 0xBA /* ':' -> */,
/* pos 012a: 161 */ 0x00, 0x14 /* - terminal marker 20 - */,
/* pos 012c: 162 */ 0xE1 /* 'a' -> */,
/* pos 012d: 163 */ 0xEE /* 'n' -> */,
/* pos 012e: 164 */ 0xE7 /* 'g' -> */,
/* pos 012f: 165 */ 0xF5 /* 'u' -> */,
/* pos 0130: 166 */ 0xE1 /* 'a' -> */,
/* pos 0131: 167 */ 0xE7 /* 'g' -> */,
/* pos 0132: 168 */ 0xE5 /* 'e' -> */,
/* pos 0133: 169 */ 0xBA /* ':' -> */,
/* pos 0134: 170 */ 0x00, 0x15 /* - terminal marker 21 - */,
/* pos 0136: 171 */ 0xE1 /* 'a' -> */,
/* pos 0137: 172 */ 0xE7 /* 'g' -> */,
/* pos 0138: 173 */ 0xED /* 'm' -> */,
/* pos 0139: 174 */ 0xE1 /* 'a' -> */,
/* pos 013a: 175 */ 0xBA /* ':' -> */,
/* pos 013b: 176 */ 0x00, 0x16 /* - terminal marker 22 - */,
/* pos 013d: 177 */ 0xE3 /* 'c' -> */,
/* pos 013e: 178 */ 0xE8 /* 'h' -> */,
/* pos 013f: 179 */ 0xE5 /* 'e' -> */,
/* pos 0140: 180 */ 0xAD /* '-' -> */,
/* pos 0141: 181 */ 0xE3 /* 'c' -> */,
/* pos 0142: 182 */ 0xEF /* 'o' -> */,
/* pos 0143: 183 */ 0xEE /* 'n' -> */,
/* pos 0144: 184 */ 0xF4 /* 't' -> */,
/* pos 0145: 185 */ 0xF2 /* 'r' -> */,
/* pos 0146: 186 */ 0xEF /* 'o' -> */,
/* pos 0147: 187 */ 0xEC /* 'l' -> */,
/* pos 0148: 188 */ 0xBA /* ':' -> */,
/* pos 0149: 189 */ 0x00, 0x17 /* - terminal marker 23 - */,
/* pos 014b: 190 */ 0xF4 /* 't' -> */,
/* pos 014c: 191 */ 0xE8 /* 'h' -> */,
/* pos 014d: 192 */ 0xEF /* 'o' -> */,
/* pos 014e: 193 */ 0xF2 /* 'r' -> */,
/* pos 014f: 194 */ 0xE9 /* 'i' -> */,
/* pos 0150: 195 */ 0xFA /* 'z' -> */,
/* pos 0151: 196 */ 0xE1 /* 'a' -> */,
/* pos 0152: 197 */ 0xF4 /* 't' -> */,
/* pos 0153: 198 */ 0xE9 /* 'i' -> */,
/* pos 0154: 199 */ 0xEF /* 'o' -> */,
/* pos 0155: 200 */ 0xEE /* 'n' -> */,
/* pos 0156: 201 */ 0xBA /* ':' -> */,
/* pos 0157: 202 */ 0x00, 0x18 /* - terminal marker 24 - */,
/* pos 0159: 203 */ 0xEB /* 'k' -> */,
/* pos 015a: 204 */ 0xE9 /* 'i' -> */,
/* pos 015b: 205 */ 0xE5 /* 'e' -> */,
/* pos 015c: 206 */ 0xBA /* ':' -> */,
/* pos 015d: 207 */ 0x00, 0x19 /* - terminal marker 25 - */,
/* pos 015f: 208 */ 0xE5 /* 'e' -> */,
/* pos 0160: 209 */ 0xEE /* 'n' -> */,
/* pos 0161: 210 */ 0xF4 /* 't' -> */,
/* pos 0162: 211 */ 0xAD /* '-' -> */,
/* pos 0163: 212 */ 0x6C /* 'l' */, 0x07, 0x00 /* (to 0x016A state 213) */,
0x74 /* 't' */, 0x0C, 0x00 /* (to 0x0172 state 220) */,
0x08, /* fail */
/* pos 016a: 213 */ 0xE5 /* 'e' -> */,
/* pos 016b: 214 */ 0xEE /* 'n' -> */,
/* pos 016c: 215 */ 0xE7 /* 'g' -> */,
/* pos 016d: 216 */ 0xF4 /* 't' -> */,
/* pos 016e: 217 */ 0xE8 /* 'h' -> */,
/* pos 016f: 218 */ 0xBA /* ':' -> */,
/* pos 0170: 219 */ 0x00, 0x1A /* - terminal marker 26 - */,
/* pos 0172: 220 */ 0xF9 /* 'y' -> */,
/* pos 0173: 221 */ 0xF0 /* 'p' -> */,
/* pos 0174: 222 */ 0xE5 /* 'e' -> */,
/* pos 0175: 223 */ 0xBA /* ':' -> */,
/* pos 0176: 224 */ 0x00, 0x1B /* - terminal marker 27 - */,
/* pos 0178: 225 */ 0xE1 /* 'a' -> */,
/* pos 0179: 226 */ 0xF4 /* 't' -> */,
/* pos 017a: 227 */ 0xE5 /* 'e' -> */,
/* pos 017b: 228 */ 0xBA /* ':' -> */,
/* pos 017c: 229 */ 0x00, 0x1C /* - terminal marker 28 - */,
/* pos 017e: 230 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x0185 state 231) */,
0x65 /* 'e' */, 0x0A, 0x00 /* (to 0x018B state 236) */,
0x08, /* fail */
/* pos 0185: 231 */ 0xEE /* 'n' -> */,
/* pos 0186: 232 */ 0xE7 /* 'g' -> */,
/* pos 0187: 233 */ 0xE5 /* 'e' -> */,
/* pos 0188: 234 */ 0xBA /* ':' -> */,
/* pos 0189: 235 */ 0x00, 0x1D /* - terminal marker 29 - */,
/* pos 018b: 236 */ 0xE6 /* 'f' -> */,
/* pos 018c: 237 */ 0xE5 /* 'e' -> */,
/* pos 018d: 238 */ 0xF2 /* 'r' -> */,
/* pos 018e: 239 */ 0xE5 /* 'e' -> */,
/* pos 018f: 240 */ 0xF2 /* 'r' -> */,
/* pos 0190: 241 */ 0xBA /* ':' -> */,
/* pos 0191: 242 */ 0x00, 0x1E /* - terminal marker 30 - */,
/* total size 403 bytes */

View File

@ -0,0 +1,766 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2014 Andy Green <andy@warmcat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation:
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include "private-libwebsockets.h"
#if HOST_OS == OS_WINDOWS
#pragma comment(lib, "Ws2_32.lib")
#endif
int log_level = LLL_ERR | LLL_WARN | LLL_NOTICE;
static void (*lwsl_emit)(int level, const char *line) = lwsl_emit_stderr;
static const char * const log_level_names[] = {
"ERR",
"WARN",
"NOTICE",
"INFO",
"DEBUG",
"PARSER",
"HEADER",
"EXTENSION",
"CLIENT",
"LATENCY",
};
void
libwebsocket_close_and_free_session(struct libwebsocket_context *context,
struct libwebsocket *wsi, enum lws_close_status reason)
{
int n, m, ret;
int old_state;
unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 2 +
LWS_SEND_BUFFER_POST_PADDING];
struct lws_tokens eff_buf;
if (!wsi)
return;
old_state = wsi->state;
switch (old_state) {
case WSI_STATE_DEAD_SOCKET:
return;
/* we tried the polite way... */
case WSI_STATE_AWAITING_CLOSE_ACK:
goto just_kill_connection;
case WSI_STATE_FLUSHING_STORED_SEND_BEFORE_CLOSE:
if (wsi->truncated_send_len) {
libwebsocket_callback_on_writable(context, wsi);
return;
}
lwsl_info("wsi %p completed WSI_STATE_FLUSHING_STORED_SEND_BEFORE_CLOSE\n", wsi);
goto just_kill_connection;
default:
if (wsi->truncated_send_len) {
lwsl_info("wsi %p entering WSI_STATE_FLUSHING_STORED_SEND_BEFORE_CLOSE\n", wsi);
wsi->state = WSI_STATE_FLUSHING_STORED_SEND_BEFORE_CLOSE;
return;
}
break;
}
wsi->u.ws.close_reason = reason;
if (wsi->mode == LWS_CONNMODE_WS_CLIENT_WAITING_CONNECT ||
wsi->mode == LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE) {
context->protocols[0].callback(context, wsi,
LWS_CALLBACK_CLIENT_CONNECTION_ERROR, NULL, NULL, 0);
free(wsi->u.hdr.ah);
goto just_kill_connection;
}
if (wsi->mode == LWS_CONNMODE_HTTP_SERVING_ACCEPTED) {
if (wsi->u.http.post_buffer) {
free(wsi->u.http.post_buffer);
wsi->u.http.post_buffer = NULL;
}
if (wsi->u.http.fd != LWS_INVALID_FILE) {
lwsl_debug("closing http file\n");
compatible_file_close(wsi->u.http.fd);
wsi->u.http.fd = LWS_INVALID_FILE;
context->protocols[0].callback(context, wsi,
LWS_CALLBACK_CLOSED_HTTP, wsi->user_space, NULL, 0);
}
}
/*
* are his extensions okay with him closing? Eg he might be a mux
* parent and just his ch1 aspect is closing?
*/
if (lws_ext_callback_for_each_active(wsi,
LWS_EXT_CALLBACK_CHECK_OK_TO_REALLY_CLOSE, NULL, 0) > 0) {
lwsl_ext("extension vetoed close\n");
return;
}
/*
* flush any tx pending from extensions, since we may send close packet
* if there are problems with send, just nuke the connection
*/
do {
ret = 0;
eff_buf.token = NULL;
eff_buf.token_len = 0;
/* show every extension the new incoming data */
m = lws_ext_callback_for_each_active(wsi,
LWS_EXT_CALLBACK_FLUSH_PENDING_TX, &eff_buf, 0);
if (m < 0) {
lwsl_ext("Extension reports fatal error\n");
goto just_kill_connection;
}
if (m)
/*
* at least one extension told us he has more
* to spill, so we will go around again after
*/
ret = 1;
/* assuming they left us something to send, send it */
if (eff_buf.token_len)
if (lws_issue_raw(wsi, (unsigned char *)eff_buf.token,
eff_buf.token_len) != eff_buf.token_len) {
lwsl_debug("close: ext spill failed\n");
goto just_kill_connection;
}
} while (ret);
/*
* signal we are closing, libsocket_write will
* add any necessary version-specific stuff. If the write fails,
* no worries we are closing anyway. If we didn't initiate this
* close, then our state has been changed to
* WSI_STATE_RETURNED_CLOSE_ALREADY and we will skip this.
*
* Likewise if it's a second call to close this connection after we
* sent the close indication to the peer already, we are in state
* WSI_STATE_AWAITING_CLOSE_ACK and will skip doing this a second time.
*/
if (old_state == WSI_STATE_ESTABLISHED &&
reason != LWS_CLOSE_STATUS_NOSTATUS) {
lwsl_debug("sending close indication...\n");
/* make valgrind happy */
memset(buf, 0, sizeof(buf));
n = libwebsocket_write(wsi,
&buf[LWS_SEND_BUFFER_PRE_PADDING + 2],
0, LWS_WRITE_CLOSE);
if (n >= 0) {
/*
* we have sent a nice protocol level indication we
* now wish to close, we should not send anything more
*/
wsi->state = WSI_STATE_AWAITING_CLOSE_ACK;
/*
* ...and we should wait for a reply for a bit
* out of politeness
*/
libwebsocket_set_timeout(wsi,
PENDING_TIMEOUT_CLOSE_ACK, 1);
lwsl_debug("sent close indication, awaiting ack\n");
return;
}
lwsl_info("close: sending close packet failed, hanging up\n");
/* else, the send failed and we should just hang up */
}
just_kill_connection:
lwsl_debug("close: just_kill_connection\n");
/*
* we won't be servicing or receiving anything further from this guy
* delete socket from the internal poll list if still present
*/
remove_wsi_socket_from_fds(context, wsi);
wsi->state = WSI_STATE_DEAD_SOCKET;
if ((old_state == WSI_STATE_ESTABLISHED ||
wsi->mode == LWS_CONNMODE_WS_SERVING ||
wsi->mode == LWS_CONNMODE_WS_CLIENT)) {
if (wsi->u.ws.rx_user_buffer) {
free(wsi->u.ws.rx_user_buffer);
wsi->u.ws.rx_user_buffer = NULL;
}
if (wsi->u.ws.rxflow_buffer) {
free(wsi->u.ws.rxflow_buffer);
wsi->u.ws.rxflow_buffer = NULL;
}
if (wsi->truncated_send_malloc) {
/* not going to be completed... nuke it */
free(wsi->truncated_send_malloc);
wsi->truncated_send_malloc = NULL;
wsi->truncated_send_len = 0;
}
}
/* tell the user it's all over for this guy */
if (wsi->protocol && wsi->protocol->callback &&
((old_state == WSI_STATE_ESTABLISHED) ||
(old_state == WSI_STATE_RETURNED_CLOSE_ALREADY) ||
(old_state == WSI_STATE_AWAITING_CLOSE_ACK))) {
lwsl_debug("calling back CLOSED\n");
wsi->protocol->callback(context, wsi, LWS_CALLBACK_CLOSED,
wsi->user_space, NULL, 0);
} else if (wsi->mode == LWS_CONNMODE_HTTP_SERVING_ACCEPTED) {
lwsl_debug("calling back CLOSED_HTTP\n");
context->protocols[0].callback(context, wsi,
LWS_CALLBACK_CLOSED_HTTP, wsi->user_space, NULL, 0 );
} else
lwsl_debug("not calling back closed\n");
/* deallocate any active extension contexts */
if (lws_ext_callback_for_each_active(wsi, LWS_EXT_CALLBACK_DESTROY, NULL, 0) < 0)
lwsl_warn("extension destruction failed\n");
#ifndef LWS_NO_EXTENSIONS
for (n = 0; n < wsi->count_active_extensions; n++)
free(wsi->active_extensions_user[n]);
#endif
/*
* inform all extensions in case they tracked this guy out of band
* even though not active on him specifically
*/
if (lws_ext_callback_for_each_extension_type(context, wsi,
LWS_EXT_CALLBACK_DESTROY_ANY_WSI_CLOSING, NULL, 0) < 0)
lwsl_warn("ext destroy wsi failed\n");
/* lwsl_info("closing fd=%d\n", wsi->sock); */
if (!lws_ssl_close(wsi) && wsi->sock >= 0) {
n = shutdown(wsi->sock, SHUT_RDWR);
if (n)
lwsl_debug("closing: shutdown ret %d\n", LWS_ERRNO);
n = compatible_close(wsi->sock);
if (n)
lwsl_debug("closing: close ret %d\n", LWS_ERRNO);
}
/* outermost destroy notification for wsi (user_space still intact) */
context->protocols[0].callback(context, wsi,
LWS_CALLBACK_WSI_DESTROY, wsi->user_space, NULL, 0);
if (wsi->protocol && wsi->protocol->per_session_data_size &&
wsi->user_space) /* user code may own */
free(wsi->user_space);
free(wsi);
}
/**
* libwebsockets_get_peer_addresses() - Get client address information
* @context: Libwebsockets context
* @wsi: Local struct libwebsocket associated with
* @fd: Connection socket descriptor
* @name: Buffer to take client address name
* @name_len: Length of client address name buffer
* @rip: Buffer to take client address IP qotted quad
* @rip_len: Length of client address IP buffer
*
* This function fills in @name and @rip with the name and IP of
* the client connected with socket descriptor @fd. Names may be
* truncated if there is not enough room. If either cannot be
* determined, they will be returned as valid zero-length strings.
*/
LWS_VISIBLE void
libwebsockets_get_peer_addresses(struct libwebsocket_context *context,
struct libwebsocket *wsi, int fd, char *name, int name_len,
char *rip, int rip_len)
{
socklen_t len;
#ifdef LWS_USE_IPV6
struct sockaddr_in6 sin6;
#endif
struct sockaddr_in sin4;
struct hostent *host;
struct hostent *host1;
char ip[128];
unsigned char *p;
int n;
#ifdef AF_LOCAL
struct sockaddr_un *un;
#endif
int ret = -1;
rip[0] = '\0';
name[0] = '\0';
lws_latency_pre(context, wsi);
#ifdef LWS_USE_IPV6
if (LWS_IPV6_ENABLED(context)) {
len = sizeof(sin6);
if (getpeername(fd, (struct sockaddr *) &sin6, &len) < 0) {
lwsl_warn("getpeername: %s\n", strerror(LWS_ERRNO));
goto bail;
}
if (!lws_plat_inet_ntop(AF_INET6, &sin6.sin6_addr, rip, rip_len)) {
lwsl_err("inet_ntop", strerror(LWS_ERRNO));
goto bail;
}
// Strip off the IPv4 to IPv6 header if one exists
if (strncmp(rip, "::ffff:", 7) == 0)
memmove(rip, rip + 7, strlen(rip) - 6);
getnameinfo((struct sockaddr *)&sin6,
sizeof(struct sockaddr_in6), name,
name_len, NULL, 0, 0);
} else
#endif
{
len = sizeof(sin4);
if (getpeername(fd, (struct sockaddr *) &sin4, &len) < 0) {
lwsl_warn("getpeername: %s\n", strerror(LWS_ERRNO));
goto bail;
}
host = gethostbyaddr((char *) &sin4.sin_addr,
sizeof(sin4.sin_addr), AF_INET);
if (host == NULL) {
lwsl_warn("gethostbyaddr: %s\n", strerror(LWS_ERRNO));
goto bail;
}
strncpy(name, host->h_name, name_len);
name[name_len - 1] = '\0';
host1 = gethostbyname(host->h_name);
if (host1 == NULL)
goto bail;
p = (unsigned char *)host1;
n = 0;
while (p != NULL) {
p = (unsigned char *)host1->h_addr_list[n++];
if (p == NULL)
continue;
if ((host1->h_addrtype != AF_INET)
#ifdef AF_LOCAL
&& (host1->h_addrtype != AF_LOCAL)
#endif
)
continue;
if (host1->h_addrtype == AF_INET)
sprintf(ip, "%u.%u.%u.%u",
p[0], p[1], p[2], p[3]);
#ifdef AF_LOCAL
else {
un = (struct sockaddr_un *)p;
strncpy(ip, un->sun_path, sizeof(ip) - 1);
ip[sizeof(ip) - 1] = '\0';
}
#endif
p = NULL;
strncpy(rip, ip, rip_len);
rip[rip_len - 1] = '\0';
}
}
ret = 0;
bail:
lws_latency(context, wsi, "libwebsockets_get_peer_addresses", ret, 1);
}
/**
* libwebsocket_context_user() - get the user data associated with the context
* @context: Websocket context
*
* This returns the optional user allocation that can be attached to
* the context the sockets live in at context_create time. It's a way
* to let all sockets serviced in the same context share data without
* using globals statics in the user code.
*/
LWS_EXTERN void *
libwebsocket_context_user(struct libwebsocket_context *context)
{
return context->user_space;
}
/**
* libwebsocket_callback_all_protocol() - Callback all connections using
* the given protocol with the given reason
*
* @protocol: Protocol whose connections will get callbacks
* @reason: Callback reason index
*/
LWS_VISIBLE int
libwebsocket_callback_all_protocol(
const struct libwebsocket_protocols *protocol, int reason)
{
struct libwebsocket_context *context = protocol->owning_server;
int n;
struct libwebsocket *wsi;
for (n = 0; n < context->fds_count; n++) {
wsi = context->lws_lookup[context->fds[n].fd];
if (!wsi)
continue;
if (wsi->protocol == protocol)
protocol->callback(context, wsi,
reason, wsi->user_space, NULL, 0);
}
return 0;
}
/**
* libwebsocket_set_timeout() - marks the wsi as subject to a timeout
*
* You will not need this unless you are doing something special
*
* @wsi: Websocket connection instance
* @reason: timeout reason
* @secs: how many seconds
*/
LWS_VISIBLE void
libwebsocket_set_timeout(struct libwebsocket *wsi,
enum pending_timeout reason, int secs)
{
time_t now;
time(&now);
wsi->pending_timeout_limit = now + secs;
wsi->pending_timeout = reason;
}
/**
* libwebsocket_get_socket_fd() - returns the socket file descriptor
*
* You will not need this unless you are doing something special
*
* @wsi: Websocket connection instance
*/
LWS_VISIBLE int
libwebsocket_get_socket_fd(struct libwebsocket *wsi)
{
return wsi->sock;
}
#ifdef LWS_LATENCY
void
lws_latency(struct libwebsocket_context *context, struct libwebsocket *wsi,
const char *action, int ret, int completed)
{
unsigned long long u;
char buf[256];
u = time_in_microseconds();
if (!action) {
wsi->latency_start = u;
if (!wsi->action_start)
wsi->action_start = u;
return;
}
if (completed) {
if (wsi->action_start == wsi->latency_start)
sprintf(buf,
"Completion first try lat %luus: %p: ret %d: %s\n",
u - wsi->latency_start,
(void *)wsi, ret, action);
else
sprintf(buf,
"Completion %luus: lat %luus: %p: ret %d: %s\n",
u - wsi->action_start,
u - wsi->latency_start,
(void *)wsi, ret, action);
wsi->action_start = 0;
} else
sprintf(buf, "lat %luus: %p: ret %d: %s\n",
u - wsi->latency_start, (void *)wsi, ret, action);
if (u - wsi->latency_start > context->worst_latency) {
context->worst_latency = u - wsi->latency_start;
strcpy(context->worst_latency_info, buf);
}
lwsl_latency("%s", buf);
}
#endif
/**
* libwebsocket_rx_flow_control() - Enable and disable socket servicing for
* receieved packets.
*
* If the output side of a server process becomes choked, this allows flow
* control for the input side.
*
* @wsi: Websocket connection instance to get callback for
* @enable: 0 = disable read servicing for this connection, 1 = enable
*/
LWS_VISIBLE int
libwebsocket_rx_flow_control(struct libwebsocket *wsi, int enable)
{
if (enable == (wsi->u.ws.rxflow_change_to & LWS_RXFLOW_ALLOW))
return 0;
lwsl_info("libwebsocket_rx_flow_control(0x%p, %d)\n", wsi, enable);
wsi->u.ws.rxflow_change_to = LWS_RXFLOW_PENDING_CHANGE | !!enable;
return 0;
}
/**
* libwebsocket_rx_flow_allow_all_protocol() - Allow all connections with this protocol to receive
*
* When the user server code realizes it can accept more input, it can
* call this to have the RX flow restriction removed from all connections using
* the given protocol.
*
* @protocol: all connections using this protocol will be allowed to receive
*/
LWS_VISIBLE void
libwebsocket_rx_flow_allow_all_protocol(
const struct libwebsocket_protocols *protocol)
{
struct libwebsocket_context *context = protocol->owning_server;
int n;
struct libwebsocket *wsi;
for (n = 0; n < context->fds_count; n++) {
wsi = context->lws_lookup[context->fds[n].fd];
if (!wsi)
continue;
if (wsi->protocol == protocol)
libwebsocket_rx_flow_control(wsi, LWS_RXFLOW_ALLOW);
}
}
/**
* libwebsocket_canonical_hostname() - returns this host's hostname
*
* This is typically used by client code to fill in the host parameter
* when making a client connection. You can only call it after the context
* has been created.
*
* @context: Websocket context
*/
LWS_VISIBLE extern const char *
libwebsocket_canonical_hostname(struct libwebsocket_context *context)
{
return (const char *)context->canonical_hostname;
}
int user_callback_handle_rxflow(callback_function callback_function,
struct libwebsocket_context *context,
struct libwebsocket *wsi,
enum libwebsocket_callback_reasons reason, void *user,
void *in, size_t len)
{
int n;
n = callback_function(context, wsi, reason, user, in, len);
if (!n)
n = _libwebsocket_rx_flow_control(wsi);
return n;
}
/**
* libwebsocket_set_proxy() - Setups proxy to libwebsocket_context.
* @context: pointer to struct libwebsocket_context you want set proxy to
* @proxy: pointer to c string containing proxy in format address:port
*
* Returns 0 if proxy string was parsed and proxy was setup.
* Returns -1 if @proxy is NULL or has incorrect format.
*
* This is only required if your OS does not provide the http_proxy
* enviroment variable (eg, OSX)
*
* IMPORTANT! You should call this function right after creation of the
* libwebsocket_context and before call to connect. If you call this
* function after connect behavior is undefined.
* This function will override proxy settings made on libwebsocket_context
* creation with genenv() call.
*/
LWS_VISIBLE int
libwebsocket_set_proxy(struct libwebsocket_context *context, const char *proxy)
{
char *p;
if (!proxy)
return -1;
strncpy(context->http_proxy_address, proxy,
sizeof(context->http_proxy_address) - 1);
context->http_proxy_address[
sizeof(context->http_proxy_address) - 1] = '\0';
p = strchr(context->http_proxy_address, ':');
if (!p) {
lwsl_err("http_proxy needs to be ads:port\n");
return -1;
}
*p = '\0';
context->http_proxy_port = atoi(p + 1);
lwsl_notice(" Proxy %s:%u\n", context->http_proxy_address,
context->http_proxy_port);
return 0;
}
/**
* libwebsockets_get_protocol() - Returns a protocol pointer from a websocket
* connection.
* @wsi: pointer to struct websocket you want to know the protocol of
*
*
* Some apis can act on all live connections of a given protocol,
* this is how you can get a pointer to the active protocol if needed.
*/
LWS_VISIBLE const struct libwebsocket_protocols *
libwebsockets_get_protocol(struct libwebsocket *wsi)
{
return wsi->protocol;
}
LWS_VISIBLE int
libwebsocket_is_final_fragment(struct libwebsocket *wsi)
{
return wsi->u.ws.final;
}
LWS_VISIBLE unsigned char
libwebsocket_get_reserved_bits(struct libwebsocket *wsi)
{
return wsi->u.ws.rsv;
}
int
libwebsocket_ensure_user_space(struct libwebsocket *wsi)
{
if (!wsi->protocol)
return 1;
/* allocate the per-connection user memory (if any) */
if (wsi->protocol->per_session_data_size && !wsi->user_space) {
wsi->user_space = malloc(
wsi->protocol->per_session_data_size);
if (wsi->user_space == NULL) {
lwsl_err("Out of memory for conn user space\n");
return 1;
}
memset(wsi->user_space, 0,
wsi->protocol->per_session_data_size);
}
return 0;
}
LWS_VISIBLE void lwsl_emit_stderr(int level, const char *line)
{
char buf[300];
unsigned long long now;
int n;
buf[0] = '\0';
for (n = 0; n < LLL_COUNT; n++)
if (level == (1 << n)) {
now = time_in_microseconds() / 100;
sprintf(buf, "[%lu:%04d] %s: ", (unsigned long) now / 10000,
(int)(now % 10000), log_level_names[n]);
break;
}
fprintf(stderr, "%s%s", buf, line);
}
LWS_VISIBLE void _lws_log(int filter, const char *format, ...)
{
char buf[256];
va_list ap;
if (!(log_level & filter))
return;
va_start(ap, format);
vsnprintf(buf, sizeof(buf), format, ap);
buf[sizeof(buf) - 1] = '\0';
va_end(ap);
lwsl_emit(filter, buf);
}
/**
* lws_set_log_level() - Set the logging bitfield
* @level: OR together the LLL_ debug contexts you want output from
* @log_emit_function: NULL to leave it as it is, or a user-supplied
* function to perform log string emission instead of
* the default stderr one.
*
* log level defaults to "err", "warn" and "notice" contexts enabled and
* emission on stderr.
*/
LWS_VISIBLE void lws_set_log_level(int level, void (*log_emit_function)(int level,
const char *line))
{
log_level = level;
if (log_emit_function)
lwsl_emit = log_emit_function;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,396 @@
#include "private-libwebsockets.h"
#include "build.h"
#if HOST_OS != OS_WINDOWS
/*
* included from libwebsockets.c for unix builds
*/
unsigned long long time_in_microseconds(void)
{
struct timeval tv;
gettimeofday(&tv, NULL);
return (tv.tv_sec * 1000000) + tv.tv_usec;
}
LWS_VISIBLE int libwebsockets_get_random(struct libwebsocket_context *context,
void *buf, int len)
{
return read(context->fd_random, (char *)buf, len);
}
LWS_VISIBLE int lws_send_pipe_choked(struct libwebsocket *wsi)
{
struct libwebsocket_pollfd fds;
/* treat the fact we got a truncated send pending as if we're choked */
if (wsi->truncated_send_len)
return 1;
fds.fd = wsi->sock;
fds.events = POLLOUT;
fds.revents = 0;
if (poll(&fds, 1, 0) != 1)
return 1;
if ((fds.revents & POLLOUT) == 0)
return 1;
/* okay to send another packet without blocking */
return 0;
}
LWS_VISIBLE int
lws_poll_listen_fd(struct libwebsocket_pollfd *fd)
{
return poll(fd, 1, 0);
}
/*
* This is just used to interrupt poll waiting
* we don't have to do anything with it.
*/
static void lws_sigusr2(int sig)
{
}
/**
* libwebsocket_cancel_service() - Cancel servicing of pending websocket activity
* @context: Websocket context
*
* This function let a call to libwebsocket_service() waiting for a timeout
* immediately return.
*/
LWS_VISIBLE void
libwebsocket_cancel_service(struct libwebsocket_context *context)
{
char buf = 0;
if (write(context->dummy_pipe_fds[1], &buf, sizeof(buf)) != 1)
lwsl_err("Cannot write to dummy pipe");
}
LWS_VISIBLE void lwsl_emit_syslog(int level, const char *line)
{
/*
int syslog_level = LOG_DEBUG;
switch (level) {
case LLL_ERR:
syslog_level = LOG_ERR;
break;
case LLL_WARN:
syslog_level = LOG_WARNING;
break;
case LLL_NOTICE:
syslog_level = LOG_NOTICE;
break;
case LLL_INFO:
syslog_level = LOG_INFO;
break;
}
*/
printf("%s", line);
}
LWS_VISIBLE int
lws_plat_service(struct libwebsocket_context *context, int timeout_ms)
{
int n;
int m;
char buf;
/* stay dead once we are dead */
if (!context)
return 1;
lws_libev_run(context);
context->service_tid = context->protocols[0].callback(context, NULL,
LWS_CALLBACK_GET_THREAD_ID, NULL, NULL, 0);
n = poll(context->fds, context->fds_count, timeout_ms);
context->service_tid = 0;
if (n == 0) /* poll timeout */ {
libwebsocket_service_fd(context, NULL);
return 0;
}
if (n < 0) {
if (LWS_ERRNO != LWS_EINTR)
return -1;
return 0;
}
/* any socket with events to service? */
for (n = 0; n < context->fds_count; n++) {
if (!context->fds[n].revents)
continue;
if (context->fds[n].fd == context->dummy_pipe_fds[0]) {
if (read(context->fds[n].fd, &buf, 1) != 1)
lwsl_err("Cannot read from dummy pipe.");
continue;
}
m = libwebsocket_service_fd(context, &context->fds[n]);
if (m < 0)
return -1;
/* if something closed, retry this slot */
if (m)
n--;
}
return 0;
}
LWS_VISIBLE int
lws_plat_set_socket_options(struct libwebsocket_context *context, int fd)
{
int optval = 1;
socklen_t optlen = sizeof(optval);
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__)
struct protoent *tcp_proto;
#endif
if (context->ka_time) {
/* enable keepalive on this socket */
optval = 1;
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE,
(const void *)&optval, optlen) < 0)
return 1;
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__CYGWIN__)
/*
* didn't find a way to set these per-socket, need to
* tune kernel systemwide values
*/
#else
/* set the keepalive conditions we want on it too */
optval = context->ka_time;
if (setsockopt(fd, IPPROTO_IP, TCP_KEEPIDLE,
(const void *)&optval, optlen) < 0)
return 1;
optval = context->ka_interval;
if (setsockopt(fd, IPPROTO_IP, TCP_KEEPINTVL,
(const void *)&optval, optlen) < 0)
return 1;
optval = context->ka_probes;
if (setsockopt(fd, IPPROTO_IP, TCP_KEEPCNT,
(const void *)&optval, optlen) < 0)
return 1;
#endif
}
/* Disable Nagle */
optval = 1;
#if !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__NetBSD__)
setsockopt(fd, SOL_TCP, TCP_NODELAY, (const void *)&optval, optlen);
#else
tcp_proto = getprotobyname("TCP");
setsockopt(fd, tcp_proto->p_proto, TCP_NODELAY, &optval, optlen);
#endif
/* We are nonblocking... */
fcntl(fd, F_SETFL, O_NONBLOCK);
return 0;
}
LWS_VISIBLE void
lws_plat_drop_app_privileges(struct lws_context_creation_info *info)
{
if (info->gid != -1)
if (setgid(info->gid))
lwsl_warn("setgid: %s\n", strerror(LWS_ERRNO));
if (info->uid != -1)
if (setuid(info->uid))
lwsl_warn("setuid: %s\n", strerror(LWS_ERRNO));
}
LWS_VISIBLE int
lws_plat_init_fd_tables(struct libwebsocket_context *context)
{
if (lws_libev_init_fd_table(context))
/* libev handled it instead */
return 0;
if (pipe(context->dummy_pipe_fds)) {
lwsl_err("Unable to create pipe\n");
return 1;
}
/* use the read end of pipe as first item */
context->fds[0].fd = context->dummy_pipe_fds[0];
context->fds[0].events = LWS_POLLIN;
context->fds[0].revents = 0;
context->fds_count = 1;
context->fd_random = open(SYSTEM_RANDOM_FILEPATH, O_RDONLY);
if (context->fd_random < 0) {
lwsl_err("Unable to open random device %s %d\n",
SYSTEM_RANDOM_FILEPATH, context->fd_random);
return 1;
}
return 0;
}
static void sigpipe_handler(int x)
{
}
LWS_VISIBLE int
lws_plat_context_early_init(void)
{
sigset_t mask;
signal(SIGUSR2, lws_sigusr2);
sigemptyset(&mask);
sigaddset(&mask, SIGUSR2);
sigprocmask(SIG_BLOCK, &mask, NULL);
signal(SIGPIPE, sigpipe_handler);
return 0;
}
LWS_VISIBLE void
lws_plat_context_early_destroy(struct libwebsocket_context *context)
{
}
LWS_VISIBLE void
lws_plat_context_late_destroy(struct libwebsocket_context *context)
{
close(context->dummy_pipe_fds[0]);
close(context->dummy_pipe_fds[1]);
close(context->fd_random);
}
/* cast a struct sockaddr_in6 * into addr for ipv6 */
LWS_VISIBLE int
interface_to_sa(struct libwebsocket_context *context,
const char *ifname, struct sockaddr_in *addr, size_t addrlen)
{
int rc = -1;
struct ifaddrs *ifr;
struct ifaddrs *ifc;
#ifdef LWS_USE_IPV6
struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr;
#endif
getifaddrs(&ifr);
for (ifc = ifr; ifc != NULL && rc; ifc = ifc->ifa_next) {
if (!ifc->ifa_addr)
continue;
lwsl_info(" interface %s vs %s\n", ifc->ifa_name, ifname);
if (strcmp(ifc->ifa_name, ifname))
continue;
switch (ifc->ifa_addr->sa_family) {
case AF_INET:
#ifdef LWS_USE_IPV6
if (LWS_IPV6_ENABLED(context)) {
/* map IPv4 to IPv6 */
bzero((char *)&addr6->sin6_addr,
sizeof(struct in6_addr));
addr6->sin6_addr.s6_addr[10] = 0xff;
addr6->sin6_addr.s6_addr[11] = 0xff;
memcpy(&addr6->sin6_addr.s6_addr[12],
&((struct sockaddr_in *)ifc->ifa_addr)->sin_addr,
sizeof(struct in_addr));
} else
#endif
memcpy(addr,
(struct sockaddr_in *)ifc->ifa_addr,
sizeof(struct sockaddr_in));
break;
#ifdef LWS_USE_IPV6
case AF_INET6:
if (rc >= 0)
break;
memcpy(&addr6->sin6_addr,
&((struct sockaddr_in6 *)ifc->ifa_addr)->sin6_addr,
sizeof(struct in6_addr));
break;
#endif
default:
continue;
}
rc = 0;
}
freeifaddrs(ifr);
return rc;
}
LWS_VISIBLE void
lws_plat_insert_socket_into_fds(struct libwebsocket_context *context,
struct libwebsocket *wsi)
{
lws_libev_io(context, wsi, LWS_EV_START | LWS_EV_READ);
context->fds[context->fds_count++].revents = 0;
}
LWS_VISIBLE void
lws_plat_delete_socket_from_fds(struct libwebsocket_context *context,
struct libwebsocket *wsi, int m)
{
}
LWS_VISIBLE void
lws_plat_service_periodic(struct libwebsocket_context *context)
{
/* if our parent went down, don't linger around */
if (context->started_with_parent &&
kill(context->started_with_parent, 0) < 0)
kill(getpid(), SIGTERM);
}
LWS_VISIBLE int
lws_plat_change_pollfd(struct libwebsocket_context *context,
struct libwebsocket *wsi, struct libwebsocket_pollfd *pfd)
{
return 0;
}
LWS_VISIBLE int
lws_plat_open_file(const char* filename, unsigned long* filelen)
{
struct stat stat_buf;
int ret = open(filename, O_RDONLY);
if (ret < 0)
return LWS_INVALID_FILE;
fstat(ret, &stat_buf);
*filelen = stat_buf.st_size;
return ret;
}
#ifdef LWS_USE_IPV6
LWS_VISIBLE const char *
lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt)
{
return inet_ntop(af, src, dst, cnt);
}
#endif
#endif

View File

@ -0,0 +1,365 @@
#include "private-libwebsockets.h"
#include "build.h"
#if HOST_OS == OS_WINDOWS
unsigned long long
time_in_microseconds()
{
#define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL
FILETIME filetime;
ULARGE_INTEGER datetime;
#ifdef _WIN32_WCE
GetCurrentFT(&filetime);
#else
GetSystemTimeAsFileTime(&filetime);
#endif
/*
* As per Windows documentation for FILETIME, copy the resulting FILETIME structure to a
* ULARGE_INTEGER structure using memcpy (using memcpy instead of direct assignment can
* prevent alignment faults on 64-bit Windows).
*/
memcpy(&datetime, &filetime, sizeof(datetime));
/* Windows file times are in 100s of nanoseconds. */
return (datetime.QuadPart - DELTA_EPOCH_IN_MICROSECS) / 10;
}
#ifdef _WIN32_WCE
time_t time(time_t *t)
{
time_t ret = time_in_microseconds() / 1000000;
*t = ret;
return ret;
}
#endif
LWS_VISIBLE int libwebsockets_get_random(struct libwebsocket_context *context,
void *buf, int len)
{
int n;
char *p = (char *)buf;
for (n = 0; n < len; n++)
p[n] = (unsigned char)rand();
return n;
}
LWS_VISIBLE int lws_send_pipe_choked(struct libwebsocket *wsi)
{
return wsi->sock_send_blocking;
}
LWS_VISIBLE int lws_poll_listen_fd(struct libwebsocket_pollfd *fd)
{
fd_set readfds;
struct timeval tv = { 0, 0 };
assert(fd->events == LWS_POLLIN);
FD_ZERO(&readfds);
FD_SET(fd->fd, &readfds);
return select(fd->fd + 1, &readfds, NULL, NULL, &tv);
}
/**
* libwebsocket_cancel_service() - Cancel servicing of pending websocket activity
* @context: Websocket context
*
* This function let a call to libwebsocket_service() waiting for a timeout
* immediately return.
*/
LWS_VISIBLE void
libwebsocket_cancel_service(struct libwebsocket_context *context)
{
WSASetEvent(context->events[0]);
}
LWS_VISIBLE void lwsl_emit_syslog(int level, const char *line)
{
lwsl_emit_stderr(level, line);
}
LWS_VISIBLE int
lws_plat_service(struct libwebsocket_context *context, int timeout_ms)
{
int n;
int i;
DWORD ev;
WSANETWORKEVENTS networkevents;
struct libwebsocket_pollfd *pfd;
/* stay dead once we are dead */
if (context == NULL)
return 1;
context->service_tid = context->protocols[0].callback(context, NULL,
LWS_CALLBACK_GET_THREAD_ID, NULL, NULL, 0);
for (i = 0; i < context->fds_count; ++i) {
pfd = &context->fds[i];
if (pfd->fd == context->listen_service_fd)
continue;
if (pfd->events & LWS_POLLOUT) {
if (context->lws_lookup[pfd->fd]->sock_send_blocking)
continue;
pfd->revents = LWS_POLLOUT;
n = libwebsocket_service_fd(context, pfd);
if (n < 0)
return n;
}
}
ev = WSAWaitForMultipleEvents(context->fds_count + 1,
context->events, FALSE, timeout_ms, FALSE);
context->service_tid = 0;
if (ev == WSA_WAIT_TIMEOUT) {
libwebsocket_service_fd(context, NULL);
return 0;
}
if (ev == WSA_WAIT_EVENT_0) {
WSAResetEvent(context->events[0]);
return 0;
}
if (ev < WSA_WAIT_EVENT_0 || ev > WSA_WAIT_EVENT_0 + context->fds_count)
return -1;
pfd = &context->fds[ev - WSA_WAIT_EVENT_0 - 1];
if (WSAEnumNetworkEvents(pfd->fd,
context->events[ev - WSA_WAIT_EVENT_0],
&networkevents) == SOCKET_ERROR) {
lwsl_err("WSAEnumNetworkEvents() failed with error %d\n",
LWS_ERRNO);
return -1;
}
pfd->revents = networkevents.lNetworkEvents;
if (pfd->revents & LWS_POLLOUT)
context->lws_lookup[pfd->fd]->sock_send_blocking = FALSE;
return libwebsocket_service_fd(context, pfd);
}
LWS_VISIBLE int
lws_plat_set_socket_options(struct libwebsocket_context *context, int fd)
{
int optval = 1;
int optlen = sizeof(optval);
u_long optl = 1;
DWORD dwBytesRet;
struct tcp_keepalive alive;
struct protoent *tcp_proto;
if (context->ka_time) {
/* enable keepalive on this socket */
optval = 1;
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE,
(const char *)&optval, optlen) < 0)
return 1;
alive.onoff = TRUE;
alive.keepalivetime = context->ka_time;
alive.keepaliveinterval = context->ka_interval;
if (WSAIoctl(fd, SIO_KEEPALIVE_VALS, &alive, sizeof(alive),
NULL, 0, &dwBytesRet, NULL, NULL))
return 1;
}
/* Disable Nagle */
optval = 1;
tcp_proto = getprotobyname("TCP");
setsockopt(fd, tcp_proto->p_proto, TCP_NODELAY, (const char *)&optval, optlen);
/* We are nonblocking... */
ioctlsocket(fd, FIONBIO, &optl);
return 0;
}
LWS_VISIBLE void
lws_plat_drop_app_privileges(struct lws_context_creation_info *info)
{
}
LWS_VISIBLE int
lws_plat_init_fd_tables(struct libwebsocket_context *context)
{
context->events = (WSAEVENT *)malloc(sizeof(WSAEVENT) *
(context->max_fds + 1));
if (context->events == NULL) {
lwsl_err("Unable to allocate events array for %d connections\n",
context->max_fds);
return 1;
}
context->fds_count = 0;
context->events[0] = WSACreateEvent();
context->fd_random = 0;
return 0;
}
LWS_VISIBLE int
lws_plat_context_early_init(void)
{
WORD wVersionRequested;
WSADATA wsaData;
int err;
/* Use the MAKEWORD(lowbyte, highbyte) macro from Windef.h */
wVersionRequested = MAKEWORD(2, 2);
err = WSAStartup(wVersionRequested, &wsaData);
if (!err)
return 0;
/*
* Tell the user that we could not find a usable
* Winsock DLL
*/
lwsl_err("WSAStartup failed with error: %d\n", err);
return 1;
}
LWS_VISIBLE void
lws_plat_context_early_destroy(struct libwebsocket_context *context)
{
if (context->events) {
WSACloseEvent(context->events[0]);
free(context->events);
}
}
LWS_VISIBLE void
lws_plat_context_late_destroy(struct libwebsocket_context *context)
{
WSACleanup();
}
LWS_VISIBLE int
interface_to_sa(struct libwebsocket_context *context,
const char *ifname, struct sockaddr_in *addr, size_t addrlen)
{
return -1;
}
LWS_VISIBLE void
lws_plat_insert_socket_into_fds(struct libwebsocket_context *context,
struct libwebsocket *wsi)
{
context->fds[context->fds_count++].revents = 0;
context->events[context->fds_count] = WSACreateEvent();
WSAEventSelect(wsi->sock, context->events[context->fds_count], LWS_POLLIN);
}
LWS_VISIBLE void
lws_plat_delete_socket_from_fds(struct libwebsocket_context *context,
struct libwebsocket *wsi, int m)
{
WSACloseEvent(context->events[m + 1]);
context->events[m + 1] = context->events[context->fds_count + 1];
}
LWS_VISIBLE void
lws_plat_service_periodic(struct libwebsocket_context *context)
{
}
LWS_VISIBLE int
lws_plat_change_pollfd(struct libwebsocket_context *context,
struct libwebsocket *wsi, struct libwebsocket_pollfd *pfd)
{
long networkevents = LWS_POLLOUT | LWS_POLLHUP;
if ((pfd->events & LWS_POLLIN))
networkevents |= LWS_POLLIN;
if (WSAEventSelect(wsi->sock,
context->events[wsi->position_in_fds_table + 1],
networkevents) != SOCKET_ERROR)
return 0;
lwsl_err("WSAEventSelect() failed with error %d\n", LWS_ERRNO);
return 1;
}
LWS_VISIBLE HANDLE
lws_plat_open_file(const char* filename, unsigned long* filelen)
{
HANDLE ret;
WCHAR buffer[MAX_PATH];
MultiByteToWideChar(CP_UTF8, 0, filename, -1, buffer,
sizeof(buffer) / sizeof(buffer[0]));
ret = CreateFileW(buffer, GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (ret != LWS_INVALID_FILE)
*filelen = GetFileSize(ret, NULL);
return ret;
}
LWS_VISIBLE const char *
lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt)
{
WCHAR *buffer;
DWORD bufferlen = cnt;
BOOL ok = FALSE;
buffer = malloc(bufferlen);
if (!buffer) {
lwsl_err("Out of memory\n");
return NULL;
}
if (af == AF_INET) {
struct sockaddr_in srcaddr;
bzero(&srcaddr, sizeof(srcaddr));
srcaddr.sin_family = AF_INET;
memcpy(&(srcaddr.sin_addr), src, sizeof(srcaddr.sin_addr));
if (!WSAAddressToStringW((struct sockaddr*)&srcaddr, sizeof(srcaddr), 0, buffer, &bufferlen))
ok = TRUE;
#ifdef LWS_USE_IPV6
} else if (af == AF_INET6) {
struct sockaddr_in6 srcaddr;
bzero(&srcaddr, sizeof(srcaddr));
srcaddr.sin6_family = AF_INET6;
memcpy(&(srcaddr.sin6_addr), src, sizeof(srcaddr.sin6_addr));
if (!WSAAddressToStringW((struct sockaddr*)&srcaddr, sizeof(srcaddr), 0, buffer, &bufferlen))
ok = TRUE;
#endif
} else
lwsl_err("Unsupported type\n");
if (!ok) {
int rv = WSAGetLastError();
lwsl_err("WSAAddressToString() : %d\n", rv);
} else {
if (WideCharToMultiByte(CP_ACP, 0, buffer, bufferlen, dst, cnt, 0, NULL) <= 0)
ok = FALSE;
}
free(buffer);
return ok ? dst : NULL;
}
#endif

View File

@ -0,0 +1,174 @@
/* config.h.in. Generated from configure.ac by autoheader. */
#ifndef WIN32
#define _DEBUG
#endif
/* Define to 1 to use CyaSSL as a replacement for OpenSSL.
* LWS_OPENSSL_SUPPORT needs to be set also for this to work. */
/* #undef USE_CYASSL */
/* The Libwebsocket version */
#define LWS_LIBRARY_VERSION "1.3"
/* The current git commit hash that we're building from */
#define LWS_BUILD_HASH "c11b847"
/* Build with OpenSSL support */
/* #undef LWS_OPENSSL_SUPPORT */
/* The client should load and trust CA root certs it finds in the OS */
/* #undef LWS_SSL_CLIENT_USE_OS_CA_CERTS */
/* Sets the path where the client certs should be installed. */
#define LWS_OPENSSL_CLIENT_CERTS "../share"
/* Turn off websocket extensions */
/* #undef LWS_NO_EXTENSIONS */
/* Enable libev io loop */
/* #undef LWS_USE_LIBEV */
/* Build with support for ipv6 */
/* #undef LWS_USE_IPV6 */
/* Build with support for HTTP2 */
/* #undef LWS_USE_HTTP2 */
/* Turn on latency measuring code */
/* #undef LWS_LATENCY */
/* Don't build the daemonizeation api */
#define LWS_NO_DAEMONIZE
/* Build without server support */
/* #undef LWS_NO_SERVER */
/* Build without client support */
/* #undef LWS_NO_CLIENT */
/* If we should compile with MinGW support */
/* #undef LWS_MINGW_SUPPORT */
/* Use the BSD getifaddrs that comes with libwebsocket, for uclibc support */
#define LWS_BUILTIN_GETIFADDRS
/* Define to 1 if you have the `bzero' function. */
/* #undef HAVE_BZERO */
/* Define to 1 if you have the <dlfcn.h> header file. */
/* #undef HAVE_DLFCN_H */
/* Define to 1 if you have the <fcntl.h> header file. */
#define HAVE_FCNTL_H
/* Define to 1 if you have the `fork' function. */
/* #undef HAVE_FORK */
/* Define to 1 if you have the `getenv function. */
#define HAVE_GETENV
/* Define to 1 if you have the <in6addr.h> header file. */
/* #undef HAVE_IN6ADDR_H */
/* Define to 1 if you have the <inttypes.h> header file. */
/* #undef HAVE_INTTYPES_H */
/* Define to 1 if you have the `ssl' library (-lssl). */
/* #undef HAVE_LIBSSL */
/* Define to 1 if your system has a GNU libc compatible `malloc' function, and
to 0 otherwise. */
#define HAVE_MALLOC
/* Define to 1 if you have the <memory.h> header file. */
#define HAVE_MEMORY_H
/* Define to 1 if you have the `memset' function. */
#define HAVE_MEMSET
/* Define to 1 if you have the <netinet/in.h> header file. */
/* #undef HAVE_NETINET_IN_H */
/* Define to 1 if your system has a GNU libc compatible `realloc' function,
and to 0 otherwise. */
#define HAVE_REALLOC
/* Define to 1 if you have the `socket' function. */
/* #undef HAVE_SOCKET */
/* Define to 1 if you have the <stdint.h> header file. */
#define HAVE_STDINT_H
/* Define to 1 if you have the <stdlib.h> header file. */
#define HAVE_STDLIB_H
/* Define to 1 if you have the `strerror' function. */
#define HAVE_STRERROR
/* Define to 1 if you have the <strings.h> header file. */
/* #undef HAVE_STRINGS_H */
/* Define to 1 if you have the <string.h> header file. */
#define HAVE_STRING_H
/* Define to 1 if you have the <sys/prctl.h> header file. */
/* #undef HAVE_SYS_PRCTL_H */
/* Define to 1 if you have the <sys/socket.h> header file. */
/* #undef HAVE_SYS_SOCKET_H */
/* Define to 1 if you have the <sys/stat.h> header file. */
#define HAVE_SYS_STAT_H
/* Define to 1 if you have the <sys/types.h> header file. */
#define HAVE_SYS_TYPES_H
/* Define to 1 if you have the <unistd.h> header file. */
/* #undef HAVE_UNISTD_H */
/* Define to 1 if you have the `vfork' function. */
/* #undef HAVE_VFORK */
/* Define to 1 if you have the <vfork.h> header file. */
/* #undef HAVE_VFORK_H */
/* Define to 1 if `fork' works. */
#define HAVE_WORKING_FORK
/* Define to 1 if `vfork' works. */
#define HAVE_WORKING_VFORK
/* Define to 1 if you have the <zlib.h> header file. */
/* #undef HAVE_ZLIB_H */
/* Define to the sub-directory in which libtool stores uninstalled libraries.
*/
#undef LT_OBJDIR // We're not using libtool
/* Define to 1 if you have the ANSI C header files. */
#define STDC_HEADERS
/* Version number of package */
#define VERSION
/* Define to rpl_malloc if the replacement function should be used. */
/* #undef malloc */
/* Define to `int' if <sys/types.h> does not define. */
#define pid_t
/* Define to rpl_realloc if the replacement function should be used. */
/* #undef realloc */
/* Define to `unsigned int' if <sys/types.h> does not define. */
/* #undef size_t */
/* Define to 1 if we have getifaddrs */
/* #undef HAVE_GETIFADDRS */
/* Define as `fork' if `vfork' does not work. */
/* #undef vfork */
/* Define if the inline keyword doesn't exist. */
#define inline

View File

@ -0,0 +1,571 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2014 Andy Green <andy@warmcat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation:
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include "private-libwebsockets.h"
static int
libwebsocket_0405_frame_mask_generate(struct libwebsocket *wsi)
{
int n;
/* fetch the per-frame nonce */
n = libwebsockets_get_random(wsi->protocol->owning_server,
wsi->u.ws.frame_masking_nonce_04, 4);
if (n != 4) {
lwsl_parser("Unable to read from random device %s %d\n",
SYSTEM_RANDOM_FILEPATH, n);
return 1;
}
/* start masking from first byte of masking key buffer */
wsi->u.ws.frame_mask_index = 0;
return 0;
}
#ifdef _DEBUG
LWS_VISIBLE void lwsl_hexdump(void *vbuf, size_t len)
{
int n;
int m;
int start;
unsigned char *buf = (unsigned char *)vbuf;
char line[80];
char *p;
lwsl_parser("\n");
for (n = 0; n < len;) {
start = n;
p = line;
p += sprintf(p, "%04X: ", start);
for (m = 0; m < 16 && n < len; m++)
p += sprintf(p, "%02X ", buf[n++]);
while (m++ < 16)
p += sprintf(p, " ");
p += sprintf(p, " ");
for (m = 0; m < 16 && (start + m) < len; m++) {
if (buf[start + m] >= ' ' && buf[start + m] < 127)
*p++ = buf[start + m];
else
*p++ = '.';
}
while (m++ < 16)
*p++ = ' ';
*p++ = '\n';
*p = '\0';
lwsl_debug("%s", line);
}
lwsl_debug("\n");
}
#endif
/*
* notice this returns number of bytes consumed, or -1
*/
int lws_issue_raw(struct libwebsocket *wsi, unsigned char *buf, size_t len)
{
struct libwebsocket_context *context = wsi->protocol->owning_server;
int n;
size_t real_len = len;
int m;
if (!len)
return 0;
/* just ignore sends after we cleared the truncation buffer */
if (wsi->state == WSI_STATE_FLUSHING_STORED_SEND_BEFORE_CLOSE &&
!wsi->truncated_send_len)
return len;
if (wsi->truncated_send_len && (buf < wsi->truncated_send_malloc ||
buf > (wsi->truncated_send_malloc +
wsi->truncated_send_len +
wsi->truncated_send_offset))) {
lwsl_err("****** %x Sending new, pending truncated ...\n", wsi);
assert(0);
}
m = lws_ext_callback_for_each_active(wsi,
LWS_EXT_CALLBACK_PACKET_TX_DO_SEND, &buf, len);
if (m < 0)
return -1;
if (m) /* handled */ {
n = m;
goto handle_truncated_send;
}
if (wsi->sock < 0)
lwsl_warn("** error invalid sock but expected to send\n");
/*
* nope, send it on the socket directly
*/
lws_latency_pre(context, wsi);
n = lws_ssl_capable_write(wsi, buf, len);
lws_latency(context, wsi, "send lws_issue_raw", n, n == len);
switch (n) {
case LWS_SSL_CAPABLE_ERROR:
return -1;
case LWS_SSL_CAPABLE_MORE_SERVICE:
/* nothing got sent, not fatal, retry the whole thing later */
n = 0;
break;
}
handle_truncated_send:
/*
* we were already handling a truncated send?
*/
if (wsi->truncated_send_len) {
lwsl_info("***** %x partial send moved on by %d (vs %d)\n",
wsi, n, real_len);
wsi->truncated_send_offset += n;
wsi->truncated_send_len -= n;
if (!wsi->truncated_send_len) {
lwsl_info("***** %x partial send completed\n", wsi);
/* done with it, but don't free it */
n = real_len;
if (wsi->state == WSI_STATE_FLUSHING_STORED_SEND_BEFORE_CLOSE) {
lwsl_info("***** %x signalling to close now\n", wsi);
return -1; /* retry closing now */
}
}
/* always callback on writeable */
libwebsocket_callback_on_writable(
wsi->protocol->owning_server, wsi);
return n;
}
if (n == real_len)
/* what we just sent went out cleanly */
return n;
if (n && wsi->u.ws.clean_buffer)
/*
* This buffer unaffected by extension rewriting.
* It means the user code is expected to deal with
* partial sends. (lws knows the header was already
* sent, so on next send will just resume sending
* payload)
*/
return n;
/*
* Newly truncated send. Buffer the remainder (it will get
* first priority next time the socket is writable)
*/
lwsl_info("***** %x new partial sent %d from %d total\n",
wsi, n, real_len);
/*
* - if we still have a suitable malloc lying around, use it
* - or, if too small, reallocate it
* - or, if no buffer, create it
*/
if (!wsi->truncated_send_malloc ||
real_len - n > wsi->truncated_send_allocation) {
if (wsi->truncated_send_malloc)
free(wsi->truncated_send_malloc);
wsi->truncated_send_allocation = real_len - n;
wsi->truncated_send_malloc = malloc(real_len - n);
if (!wsi->truncated_send_malloc) {
lwsl_err("truncated send: unable to malloc %d\n",
real_len - n);
return -1;
}
}
wsi->truncated_send_offset = 0;
wsi->truncated_send_len = real_len - n;
memcpy(wsi->truncated_send_malloc, buf + n, real_len - n);
/* since something buffered, force it to get another chance to send */
libwebsocket_callback_on_writable(wsi->protocol->owning_server, wsi);
return real_len;
}
/**
* libwebsocket_write() - Apply protocol then write data to client
* @wsi: Websocket instance (available from user callback)
* @buf: The data to send. For data being sent on a websocket
* connection (ie, not default http), this buffer MUST have
* LWS_SEND_BUFFER_PRE_PADDING bytes valid BEFORE the pointer
* and an additional LWS_SEND_BUFFER_POST_PADDING bytes valid
* in the buffer after (buf + len). This is so the protocol
* header and trailer data can be added in-situ.
* @len: Count of the data bytes in the payload starting from buf
* @protocol: Use LWS_WRITE_HTTP to reply to an http connection, and one
* of LWS_WRITE_BINARY or LWS_WRITE_TEXT to send appropriate
* data on a websockets connection. Remember to allow the extra
* bytes before and after buf if LWS_WRITE_BINARY or LWS_WRITE_TEXT
* are used.
*
* This function provides the way to issue data back to the client
* for both http and websocket protocols.
*
* In the case of sending using websocket protocol, be sure to allocate
* valid storage before and after buf as explained above. This scheme
* allows maximum efficiency of sending data and protocol in a single
* packet while not burdening the user code with any protocol knowledge.
*
* Return may be -1 for a fatal error needing connection close, or a
* positive number reflecting the amount of bytes actually sent. This
* can be less than the requested number of bytes due to OS memory
* pressure at any given time.
*/
LWS_VISIBLE int libwebsocket_write(struct libwebsocket *wsi, unsigned char *buf,
size_t len, enum libwebsocket_write_protocol protocol)
{
int n;
int pre = 0;
int post = 0;
int masked7 = wsi->mode == LWS_CONNMODE_WS_CLIENT;
unsigned char *dropmask = NULL;
unsigned char is_masked_bit = 0;
size_t orig_len = len;
struct lws_tokens eff_buf;
if (len == 0 && protocol != LWS_WRITE_CLOSE &&
protocol != LWS_WRITE_PING && protocol != LWS_WRITE_PONG) {
lwsl_warn("zero length libwebsocket_write attempt\n");
return 0;
}
if (protocol == LWS_WRITE_HTTP)
goto send_raw;
/* websocket protocol, either binary or text */
if (wsi->state != WSI_STATE_ESTABLISHED)
return -1;
/* if we are continuing a frame that already had its header done */
if (wsi->u.ws.inside_frame)
goto do_more_inside_frame;
/* if he wants all partials buffered, never have a clean_buffer */
wsi->u.ws.clean_buffer = !wsi->protocol->no_buffer_all_partial_tx;
/*
* give a chance to the extensions to modify payload
* pre-TX mangling is not allowed to truncate
*/
eff_buf.token = (char *)buf;
eff_buf.token_len = len;
switch (protocol) {
case LWS_WRITE_PING:
case LWS_WRITE_PONG:
case LWS_WRITE_CLOSE:
break;
default:
if (lws_ext_callback_for_each_active(wsi,
LWS_EXT_CALLBACK_PAYLOAD_TX, &eff_buf, 0) < 0)
return -1;
}
/*
* an extension did something we need to keep... for example, if
* compression extension, it has already updated its state according
* to this being issued
*/
if ((char *)buf != eff_buf.token)
/*
* extension recreated it:
* need to buffer this if not all sent
*/
wsi->u.ws.clean_buffer = 0;
buf = (unsigned char *)eff_buf.token;
len = eff_buf.token_len;
switch (wsi->ietf_spec_revision) {
case 13:
if (masked7) {
pre += 4;
dropmask = &buf[0 - pre];
is_masked_bit = 0x80;
}
switch (protocol & 0xf) {
case LWS_WRITE_TEXT:
n = LWS_WS_OPCODE_07__TEXT_FRAME;
break;
case LWS_WRITE_BINARY:
n = LWS_WS_OPCODE_07__BINARY_FRAME;
break;
case LWS_WRITE_CONTINUATION:
n = LWS_WS_OPCODE_07__CONTINUATION;
break;
case LWS_WRITE_CLOSE:
n = LWS_WS_OPCODE_07__CLOSE;
/*
* 06+ has a 2-byte status code in network order
* we can do this because we demand post-buf
*/
if (wsi->u.ws.close_reason) {
/* reason codes count as data bytes */
buf -= 2;
buf[0] = wsi->u.ws.close_reason >> 8;
buf[1] = wsi->u.ws.close_reason;
len += 2;
}
break;
case LWS_WRITE_PING:
n = LWS_WS_OPCODE_07__PING;
break;
case LWS_WRITE_PONG:
n = LWS_WS_OPCODE_07__PONG;
break;
default:
lwsl_warn("lws_write: unknown write opc / protocol\n");
return -1;
}
if (!(protocol & LWS_WRITE_NO_FIN))
n |= 1 << 7;
if (len < 126) {
pre += 2;
buf[-pre] = n;
buf[-pre + 1] = len | is_masked_bit;
} else {
if (len < 65536) {
pre += 4;
buf[-pre] = n;
buf[-pre + 1] = 126 | is_masked_bit;
buf[-pre + 2] = len >> 8;
buf[-pre + 3] = len;
} else {
pre += 10;
buf[-pre] = n;
buf[-pre + 1] = 127 | is_masked_bit;
#if defined __LP64__
buf[-pre + 2] = (len >> 56) & 0x7f;
buf[-pre + 3] = len >> 48;
buf[-pre + 4] = len >> 40;
buf[-pre + 5] = len >> 32;
#else
buf[-pre + 2] = 0;
buf[-pre + 3] = 0;
buf[-pre + 4] = 0;
buf[-pre + 5] = 0;
#endif
buf[-pre + 6] = len >> 24;
buf[-pre + 7] = len >> 16;
buf[-pre + 8] = len >> 8;
buf[-pre + 9] = len;
}
}
break;
}
do_more_inside_frame:
/*
* Deal with masking if we are in client -> server direction and
* the protocol demands it
*/
if (wsi->mode == LWS_CONNMODE_WS_CLIENT) {
if (!wsi->u.ws.inside_frame)
if (libwebsocket_0405_frame_mask_generate(wsi)) {
lwsl_err("frame mask generation failed\n");
return -1;
}
/*
* in v7, just mask the payload
*/
if (dropmask) { /* never set if already inside frame */
for (n = 4; n < (int)len + 4; n++)
dropmask[n] = dropmask[n] ^
wsi->u.ws.frame_masking_nonce_04[
(wsi->u.ws.frame_mask_index++) & 3];
/* copy the frame nonce into place */
memcpy(dropmask, wsi->u.ws.frame_masking_nonce_04, 4);
}
}
send_raw:
switch (protocol) {
case LWS_WRITE_CLOSE:
/* lwsl_hexdump(&buf[-pre], len + post); */
case LWS_WRITE_HTTP:
case LWS_WRITE_PONG:
case LWS_WRITE_PING:
return lws_issue_raw(wsi, (unsigned char *)buf - pre,
len + pre + post);
default:
break;
}
wsi->u.ws.inside_frame = 1;
/*
* give any active extensions a chance to munge the buffer
* before send. We pass in a pointer to an lws_tokens struct
* prepared with the default buffer and content length that's in
* there. Rather than rewrite the default buffer, extensions
* that expect to grow the buffer can adapt .token to
* point to their own per-connection buffer in the extension
* user allocation. By default with no extensions or no
* extension callback handling, just the normal input buffer is
* used then so it is efficient.
*
* callback returns 1 in case it wants to spill more buffers
*
* This takes care of holding the buffer if send is incomplete, ie,
* if wsi->u.ws.clean_buffer is 0 (meaning an extension meddled with
* the buffer). If wsi->u.ws.clean_buffer is 1, it will instead
* return to the user code how much OF THE USER BUFFER was consumed.
*/
n = lws_issue_raw_ext_access(wsi, buf - pre, len + pre + post);
if (n <= 0)
return n;
if (n == len + pre + post) {
/* everything in the buffer was handled (or rebuffered...) */
wsi->u.ws.inside_frame = 0;
return orig_len;
}
/*
* it is how many bytes of user buffer got sent... may be < orig_len
* in which case callback when writable has already been arranged
* and user code can call libwebsocket_write() again with the rest
* later.
*/
return n - (pre + post);
}
LWS_VISIBLE int libwebsockets_serve_http_file_fragment(
struct libwebsocket_context *context, struct libwebsocket *wsi)
{
int n;
int m;
while (!lws_send_pipe_choked(wsi)) {
if (wsi->truncated_send_len) {
if (lws_issue_raw(wsi, wsi->truncated_send_malloc +
wsi->truncated_send_offset,
wsi->truncated_send_len) < 0) {
lwsl_info("closing from libwebsockets_serve_http_file_fragment\n");
return -1;
}
continue;
}
if (wsi->u.http.filepos == wsi->u.http.filelen)
goto all_sent;
compatible_file_read(n, wsi->u.http.fd, context->service_buffer,
sizeof(context->service_buffer));
if (n < 0)
return -1; /* caller will close */
if (n) {
m = libwebsocket_write(wsi, context->service_buffer, n,
LWS_WRITE_HTTP);
if (m < 0)
return -1;
wsi->u.http.filepos += m;
if (m != n)
/* adjust for what was not sent */
compatible_file_seek_cur(wsi->u.http.fd, m - n);
}
all_sent:
if (!wsi->truncated_send_len &&
wsi->u.http.filepos == wsi->u.http.filelen) {
wsi->state = WSI_STATE_HTTP;
if (wsi->protocol->callback)
/* ignore callback returned value */
user_callback_handle_rxflow(
wsi->protocol->callback, context, wsi,
LWS_CALLBACK_HTTP_FILE_COMPLETION,
wsi->user_space, NULL, 0);
return 1; /* >0 indicates completed */
}
}
lwsl_info("choked before able to send whole file (post)\n");
libwebsocket_callback_on_writable(context, wsi);
return 0; /* indicates further processing must be done */
}
LWS_VISIBLE int
lws_ssl_capable_read_no_ssl(struct libwebsocket *wsi, unsigned char *buf, int len)
{
int n;
n = recv(wsi->sock, buf, len, 0);
if (n >= 0)
return n;
lwsl_warn("error on reading from skt\n");
return LWS_SSL_CAPABLE_ERROR;
}
LWS_VISIBLE int
lws_ssl_capable_write_no_ssl(struct libwebsocket *wsi, unsigned char *buf, int len)
{
int n;
n = send(wsi->sock, buf, len, 0);
if (n >= 0)
return n;
if (LWS_ERRNO == LWS_EAGAIN ||
LWS_ERRNO == LWS_EWOULDBLOCK ||
LWS_ERRNO == LWS_EINTR) {
if (LWS_ERRNO == LWS_EWOULDBLOCK)
lws_set_blocking_send(wsi);
return LWS_SSL_CAPABLE_MORE_SERVICE;
}
lwsl_debug("ERROR writing len %d to skt %d\n", len, n);
return LWS_SSL_CAPABLE_ERROR;
}

View File

@ -0,0 +1,973 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2013 Andy Green <andy@warmcat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation:
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include "private-libwebsockets.h"
unsigned char lextable[] = {
#include "lextable.h"
};
#define FAIL_CHAR 0x08
int lextable_decode(int pos, char c)
{
c = tolower(c);
while (1) {
if (lextable[pos] & (1 << 7)) { /* 1-byte, fail on mismatch */
if ((lextable[pos] & 0x7f) != c)
return -1;
/* fall thru */
pos++;
if (lextable[pos] == FAIL_CHAR)
return -1;
return pos;
}
/* b7 = 0, end or 3-byte */
if (lextable[pos] < FAIL_CHAR) /* terminal marker */
return pos;
if (lextable[pos] == c) /* goto */
return pos + (lextable[pos + 1]) +
(lextable[pos + 2] << 8);
/* fall thru goto */
pos += 3;
/* continue */
}
}
int lws_allocate_header_table(struct libwebsocket *wsi)
{
wsi->u.hdr.ah = malloc(sizeof(*wsi->u.hdr.ah));
if (wsi->u.hdr.ah == NULL) {
lwsl_err("Out of memory\n");
return -1;
}
memset(wsi->u.hdr.ah->frag_index, 0, sizeof(wsi->u.hdr.ah->frag_index));
wsi->u.hdr.ah->next_frag_index = 0;
wsi->u.hdr.ah->pos = 0;
return 0;
}
LWS_VISIBLE int lws_hdr_total_length(struct libwebsocket *wsi, enum lws_token_indexes h)
{
int n;
int len = 0;
n = wsi->u.hdr.ah->frag_index[h];
if (!n)
return 0;
do {
len += wsi->u.hdr.ah->frags[n].len;
n = wsi->u.hdr.ah->frags[n].next_frag_index;
} while (n);
return len;
}
LWS_VISIBLE int lws_hdr_copy(struct libwebsocket *wsi, char *dest, int len,
enum lws_token_indexes h)
{
int toklen = lws_hdr_total_length(wsi, h);
int n;
if (toklen >= len)
return -1;
n = wsi->u.hdr.ah->frag_index[h];
if (!n)
return 0;
do {
strcpy(dest,
&wsi->u.hdr.ah->data[wsi->u.hdr.ah->frags[n].offset]);
dest += wsi->u.hdr.ah->frags[n].len;
n = wsi->u.hdr.ah->frags[n].next_frag_index;
} while (n);
return toklen;
}
char *lws_hdr_simple_ptr(struct libwebsocket *wsi, enum lws_token_indexes h)
{
int n;
n = wsi->u.hdr.ah->frag_index[h];
if (!n)
return NULL;
return &wsi->u.hdr.ah->data[wsi->u.hdr.ah->frags[n].offset];
}
int lws_hdr_simple_create(struct libwebsocket *wsi,
enum lws_token_indexes h, const char *s)
{
wsi->u.hdr.ah->next_frag_index++;
if (wsi->u.hdr.ah->next_frag_index ==
sizeof(wsi->u.hdr.ah->frags) / sizeof(wsi->u.hdr.ah->frags[0])) {
lwsl_warn("More hdr frags than we can deal with, dropping\n");
return -1;
}
wsi->u.hdr.ah->frag_index[h] = wsi->u.hdr.ah->next_frag_index;
wsi->u.hdr.ah->frags[wsi->u.hdr.ah->next_frag_index].offset =
wsi->u.hdr.ah->pos;
wsi->u.hdr.ah->frags[wsi->u.hdr.ah->next_frag_index].len = 0;
wsi->u.hdr.ah->frags[wsi->u.hdr.ah->next_frag_index].next_frag_index =
0;
do {
if (wsi->u.hdr.ah->pos == sizeof(wsi->u.hdr.ah->data)) {
lwsl_err("Ran out of header data space\n");
return -1;
}
wsi->u.hdr.ah->data[wsi->u.hdr.ah->pos++] = *s;
if (*s)
wsi->u.hdr.ah->frags[
wsi->u.hdr.ah->next_frag_index].len++;
} while (*s++);
return 0;
}
static char char_to_hex(const char c)
{
if (c >= '0' && c <= '9')
return c - '0';
if (c >= 'a' && c <= 'f')
return c - 'a' + 10;
if (c >= 'A' && c <= 'F')
return c - 'A' + 10;
return -1;
}
static int issue_char(struct libwebsocket *wsi, unsigned char c)
{
if (wsi->u.hdr.ah->pos == sizeof(wsi->u.hdr.ah->data)) {
lwsl_warn("excessive header content\n");
return -1;
}
wsi->u.hdr.ah->data[wsi->u.hdr.ah->pos++] = c;
if (c)
wsi->u.hdr.ah->frags[wsi->u.hdr.ah->next_frag_index].len++;
return 0;
}
int libwebsocket_parse(struct libwebsocket *wsi, unsigned char c)
{
int n;
switch (wsi->u.hdr.parser_state) {
case WSI_TOKEN_GET_URI:
case WSI_TOKEN_POST_URI:
case WSI_TOKEN_HOST:
case WSI_TOKEN_CONNECTION:
case WSI_TOKEN_KEY1:
case WSI_TOKEN_KEY2:
case WSI_TOKEN_PROTOCOL:
case WSI_TOKEN_UPGRADE:
case WSI_TOKEN_ORIGIN:
case WSI_TOKEN_SWORIGIN:
case WSI_TOKEN_DRAFT:
case WSI_TOKEN_CHALLENGE:
case WSI_TOKEN_KEY:
case WSI_TOKEN_VERSION:
case WSI_TOKEN_ACCEPT:
case WSI_TOKEN_NONCE:
case WSI_TOKEN_EXTENSIONS:
case WSI_TOKEN_HTTP:
case WSI_TOKEN_HTTP_ACCEPT:
case WSI_TOKEN_HTTP_IF_MODIFIED_SINCE:
case WSI_TOKEN_HTTP_ACCEPT_ENCODING:
case WSI_TOKEN_HTTP_ACCEPT_LANGUAGE:
case WSI_TOKEN_HTTP_PRAGMA:
case WSI_TOKEN_HTTP_CACHE_CONTROL:
case WSI_TOKEN_HTTP_AUTHORIZATION:
case WSI_TOKEN_HTTP_COOKIE:
case WSI_TOKEN_HTTP_CONTENT_LENGTH:
case WSI_TOKEN_HTTP_CONTENT_TYPE:
case WSI_TOKEN_HTTP_DATE:
case WSI_TOKEN_HTTP_RANGE:
case WSI_TOKEN_HTTP_REFERER:
lwsl_parser("WSI_TOK_(%d) '%c'\n", wsi->u.hdr.parser_state, c);
/* collect into malloc'd buffers */
/* optional initial space swallow */
if (!wsi->u.hdr.ah->frags[wsi->u.hdr.ah->frag_index[
wsi->u.hdr.parser_state]].len && c == ' ')
break;
if ((wsi->u.hdr.parser_state != WSI_TOKEN_GET_URI) && (wsi->u.hdr.parser_state != WSI_TOKEN_POST_URI))
goto check_eol;
/* special URI processing... end at space */
if (c == ' ') {
/* enforce starting with / */
if (!wsi->u.hdr.ah->frags[wsi->u.hdr.ah->next_frag_index].len)
if (issue_char(wsi, '/') < 0)
return -1;
c = '\0';
wsi->u.hdr.parser_state = WSI_TOKEN_SKIPPING;
goto spill;
}
/* special URI processing... convert %xx */
switch (wsi->u.hdr.ues) {
case URIES_IDLE:
if (c == '%') {
wsi->u.hdr.ues = URIES_SEEN_PERCENT;
goto swallow;
}
break;
case URIES_SEEN_PERCENT:
if (char_to_hex(c) < 0) {
/* regurgitate */
if (issue_char(wsi, '%') < 0)
return -1;
wsi->u.hdr.ues = URIES_IDLE;
/* continue on to assess c */
break;
}
wsi->u.hdr.esc_stash = c;
wsi->u.hdr.ues = URIES_SEEN_PERCENT_H1;
goto swallow;
case URIES_SEEN_PERCENT_H1:
if (char_to_hex(c) < 0) {
/* regurgitate */
issue_char(wsi, '%');
wsi->u.hdr.ues = URIES_IDLE;
/* regurgitate + assess */
if (libwebsocket_parse(wsi, wsi->u.hdr.esc_stash) < 0)
return -1;
/* continue on to assess c */
break;
}
c = (char_to_hex(wsi->u.hdr.esc_stash) << 4) |
char_to_hex(c);
wsi->u.hdr.ues = URIES_IDLE;
break;
}
/*
* special URI processing...
* convert /.. or /... or /../ etc to /
* convert /./ to /
* convert // or /// etc to /
* leave /.dir or whatever alone
*/
switch (wsi->u.hdr.ups) {
case URIPS_IDLE:
/* issue the first / always */
if (c == '/')
wsi->u.hdr.ups = URIPS_SEEN_SLASH;
break;
case URIPS_SEEN_SLASH:
/* swallow subsequent slashes */
if (c == '/')
goto swallow;
/* track and swallow the first . after / */
if (c == '.') {
wsi->u.hdr.ups = URIPS_SEEN_SLASH_DOT;
goto swallow;
} else
wsi->u.hdr.ups = URIPS_IDLE;
break;
case URIPS_SEEN_SLASH_DOT:
/* swallow second . */
if (c == '.') {
/*
* back up one dir level if possible
* safe against header fragmentation because
* the method URI can only be in 1 fragment
*/
if (wsi->u.hdr.ah->frags[wsi->u.hdr.ah->next_frag_index].len > 2) {
wsi->u.hdr.ah->pos--;
wsi->u.hdr.ah->frags[wsi->u.hdr.ah->next_frag_index].len--;
do {
wsi->u.hdr.ah->pos--;
wsi->u.hdr.ah->frags[wsi->u.hdr.ah->next_frag_index].len--;
} while (wsi->u.hdr.ah->frags[wsi->u.hdr.ah->next_frag_index].len > 1 &&
wsi->u.hdr.ah->data[wsi->u.hdr.ah->pos] != '/');
}
wsi->u.hdr.ups = URIPS_SEEN_SLASH_DOT_DOT;
goto swallow;
}
/* change /./ to / */
if (c == '/') {
wsi->u.hdr.ups = URIPS_SEEN_SLASH;
goto swallow;
}
/* it was like /.dir ... regurgitate the . */
wsi->u.hdr.ups = URIPS_IDLE;
issue_char(wsi, '.');
break;
case URIPS_SEEN_SLASH_DOT_DOT:
/* swallow prior .. chars and any subsequent . */
if (c == '.')
goto swallow;
/* last issued was /, so another / == // */
if (c == '/')
goto swallow;
else /* last we issued was / so SEEN_SLASH */
wsi->u.hdr.ups = URIPS_SEEN_SLASH;
break;
case URIPS_ARGUMENTS:
/* leave them alone */
break;
}
check_eol:
/* bail at EOL */
if (wsi->u.hdr.parser_state != WSI_TOKEN_CHALLENGE &&
c == '\x0d') {
c = '\0';
wsi->u.hdr.parser_state = WSI_TOKEN_SKIPPING_SAW_CR;
lwsl_parser("*\n");
}
if (c == '?') { /* start of URI arguments */
/* seal off uri header */
wsi->u.hdr.ah->data[wsi->u.hdr.ah->pos++] = '\0';
/* move to using WSI_TOKEN_HTTP_URI_ARGS */
wsi->u.hdr.ah->next_frag_index++;
wsi->u.hdr.ah->frags[
wsi->u.hdr.ah->next_frag_index].offset =
wsi->u.hdr.ah->pos;
wsi->u.hdr.ah->frags[
wsi->u.hdr.ah->next_frag_index].len = 0;
wsi->u.hdr.ah->frags[
wsi->u.hdr.ah->next_frag_index].next_frag_index = 0;
wsi->u.hdr.ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS] =
wsi->u.hdr.ah->next_frag_index;
/* defeat normal uri path processing */
wsi->u.hdr.ups = URIPS_ARGUMENTS;
goto swallow;
}
spill:
if (issue_char(wsi, c) < 0)
return -1;
swallow:
/* per-protocol end of headers management */
if (wsi->u.hdr.parser_state == WSI_TOKEN_CHALLENGE)
goto set_parsing_complete;
break;
/* collecting and checking a name part */
case WSI_TOKEN_NAME_PART:
lwsl_parser("WSI_TOKEN_NAME_PART '%c'\n", c);
wsi->u.hdr.lextable_pos =
lextable_decode(wsi->u.hdr.lextable_pos, c);
if (wsi->u.hdr.lextable_pos < 0) {
/* this is not a header we know about */
if (wsi->u.hdr.ah->frag_index[WSI_TOKEN_GET_URI] || wsi->u.hdr.ah->frag_index[WSI_TOKEN_POST_URI] ||
wsi->u.hdr.ah->frag_index[WSI_TOKEN_HTTP]) {
/*
* altready had the method, no idea what
* this crap is, ignore
*/
wsi->u.hdr.parser_state = WSI_TOKEN_SKIPPING;
break;
}
/*
* hm it's an unknown http method in fact,
* treat as dangerous
*/
lwsl_info("Unknown method - dropping\n");
return -1;
}
if (lextable[wsi->u.hdr.lextable_pos] < FAIL_CHAR) {
/* terminal state */
n = (lextable[wsi->u.hdr.lextable_pos] << 8) | lextable[wsi->u.hdr.lextable_pos + 1];
lwsl_parser("known hdr %d\n", n);
if (n == WSI_TOKEN_GET_URI &&
wsi->u.hdr.ah->frag_index[WSI_TOKEN_GET_URI]) {
lwsl_warn("Duplicated GET\n");
return -1;
} else if (n == WSI_TOKEN_POST_URI &&
wsi->u.hdr.ah->frag_index[WSI_TOKEN_POST_URI]) {
lwsl_warn("Duplicated POST\n");
return -1;
}
/*
* WSORIGIN is protocol equiv to ORIGIN,
* JWebSocket likes to send it, map to ORIGIN
*/
if (n == WSI_TOKEN_SWORIGIN)
n = WSI_TOKEN_ORIGIN;
wsi->u.hdr.parser_state = (enum lws_token_indexes)
(WSI_TOKEN_GET_URI + n);
if (wsi->u.hdr.parser_state == WSI_TOKEN_CHALLENGE)
goto set_parsing_complete;
goto start_fragment;
}
break;
start_fragment:
wsi->u.hdr.ah->next_frag_index++;
if (wsi->u.hdr.ah->next_frag_index ==
sizeof(wsi->u.hdr.ah->frags) /
sizeof(wsi->u.hdr.ah->frags[0])) {
lwsl_warn("More hdr frags than we can deal with\n");
return -1;
}
wsi->u.hdr.ah->frags[wsi->u.hdr.ah->next_frag_index].offset =
wsi->u.hdr.ah->pos;
wsi->u.hdr.ah->frags[wsi->u.hdr.ah->next_frag_index].len = 0;
wsi->u.hdr.ah->frags[
wsi->u.hdr.ah->next_frag_index].next_frag_index = 0;
n = wsi->u.hdr.ah->frag_index[wsi->u.hdr.parser_state];
if (!n) { /* first fragment */
wsi->u.hdr.ah->frag_index[wsi->u.hdr.parser_state] =
wsi->u.hdr.ah->next_frag_index;
break;
}
/* continuation */
while (wsi->u.hdr.ah->frags[n].next_frag_index)
n = wsi->u.hdr.ah->frags[n].next_frag_index;
wsi->u.hdr.ah->frags[n].next_frag_index =
wsi->u.hdr.ah->next_frag_index;
if (wsi->u.hdr.ah->pos == sizeof(wsi->u.hdr.ah->data)) {
lwsl_warn("excessive header content\n");
return -1;
}
wsi->u.hdr.ah->data[wsi->u.hdr.ah->pos++] = ' ';
wsi->u.hdr.ah->frags[wsi->u.hdr.ah->next_frag_index].len++;
break;
/* skipping arg part of a name we didn't recognize */
case WSI_TOKEN_SKIPPING:
lwsl_parser("WSI_TOKEN_SKIPPING '%c'\n", c);
if (c == '\x0d')
wsi->u.hdr.parser_state = WSI_TOKEN_SKIPPING_SAW_CR;
break;
case WSI_TOKEN_SKIPPING_SAW_CR:
lwsl_parser("WSI_TOKEN_SKIPPING_SAW_CR '%c'\n", c);
if (c == '\x0a') {
wsi->u.hdr.parser_state = WSI_TOKEN_NAME_PART;
wsi->u.hdr.lextable_pos = 0;
} else
wsi->u.hdr.parser_state = WSI_TOKEN_SKIPPING;
break;
/* we're done, ignore anything else */
case WSI_PARSING_COMPLETE:
lwsl_parser("WSI_PARSING_COMPLETE '%c'\n", c);
break;
default: /* keep gcc happy */
break;
}
return 0;
set_parsing_complete:
if (lws_hdr_total_length(wsi, WSI_TOKEN_UPGRADE)) {
if (lws_hdr_total_length(wsi, WSI_TOKEN_VERSION))
wsi->ietf_spec_revision =
atoi(lws_hdr_simple_ptr(wsi, WSI_TOKEN_VERSION));
lwsl_parser("v%02d hdrs completed\n", wsi->ietf_spec_revision);
}
wsi->u.hdr.parser_state = WSI_PARSING_COMPLETE;
wsi->hdr_parsing_completed = 1;
return 0;
}
/**
* lws_frame_is_binary: true if the current frame was sent in binary mode
*
* @wsi: the connection we are inquiring about
*
* This is intended to be called from the LWS_CALLBACK_RECEIVE callback if
* it's interested to see if the frame it's dealing with was sent in binary
* mode.
*/
LWS_VISIBLE int lws_frame_is_binary(struct libwebsocket *wsi)
{
return wsi->u.ws.frame_is_binary;
}
int
libwebsocket_rx_sm(struct libwebsocket *wsi, unsigned char c)
{
int n;
struct lws_tokens eff_buf;
int ret = 0;
switch (wsi->lws_rx_parse_state) {
case LWS_RXPS_NEW:
switch (wsi->ietf_spec_revision) {
case 13:
/*
* no prepended frame key any more
*/
wsi->u.ws.all_zero_nonce = 1;
goto handle_first;
default:
lwsl_warn("lws_rx_sm: unknown spec version %d\n",
wsi->ietf_spec_revision);
break;
}
break;
case LWS_RXPS_04_MASK_NONCE_1:
wsi->u.ws.frame_masking_nonce_04[1] = c;
if (c)
wsi->u.ws.all_zero_nonce = 0;
wsi->lws_rx_parse_state = LWS_RXPS_04_MASK_NONCE_2;
break;
case LWS_RXPS_04_MASK_NONCE_2:
wsi->u.ws.frame_masking_nonce_04[2] = c;
if (c)
wsi->u.ws.all_zero_nonce = 0;
wsi->lws_rx_parse_state = LWS_RXPS_04_MASK_NONCE_3;
break;
case LWS_RXPS_04_MASK_NONCE_3:
wsi->u.ws.frame_masking_nonce_04[3] = c;
if (c)
wsi->u.ws.all_zero_nonce = 0;
/*
* start from the zero'th byte in the XOR key buffer since
* this is the start of a frame with a new key
*/
wsi->u.ws.frame_mask_index = 0;
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_1;
break;
/*
* 04 logical framing from the spec (all this is masked when incoming
* and has to be unmasked)
*
* We ignore the possibility of extension data because we don't
* negotiate any extensions at the moment.
*
* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-------+-+-------------+-------------------------------+
* |F|R|R|R| opcode|R| Payload len | Extended payload length |
* |I|S|S|S| (4) |S| (7) | (16/63) |
* |N|V|V|V| |V| | (if payload len==126/127) |
* | |1|2|3| |4| | |
* +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
* | Extended payload length continued, if payload len == 127 |
* + - - - - - - - - - - - - - - - +-------------------------------+
* | | Extension data |
* +-------------------------------+ - - - - - - - - - - - - - - - +
* : :
* +---------------------------------------------------------------+
* : Application data :
* +---------------------------------------------------------------+
*
* We pass payload through to userland as soon as we get it, ignoring
* FIN. It's up to userland to buffer it up if it wants to see a
* whole unfragmented block of the original size (which may be up to
* 2^63 long!)
*/
case LWS_RXPS_04_FRAME_HDR_1:
handle_first:
wsi->u.ws.opcode = c & 0xf;
wsi->u.ws.rsv = c & 0x70;
wsi->u.ws.final = !!((c >> 7) & 1);
switch (wsi->u.ws.opcode) {
case LWS_WS_OPCODE_07__TEXT_FRAME:
case LWS_WS_OPCODE_07__BINARY_FRAME:
wsi->u.ws.frame_is_binary =
wsi->u.ws.opcode == LWS_WS_OPCODE_07__BINARY_FRAME;
break;
}
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN;
break;
case LWS_RXPS_04_FRAME_HDR_LEN:
wsi->u.ws.this_frame_masked = !!(c & 0x80);
switch (c & 0x7f) {
case 126:
/* control frames are not allowed to have big lengths */
if (wsi->u.ws.opcode & 8)
goto illegal_ctl_length;
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN16_2;
break;
case 127:
/* control frames are not allowed to have big lengths */
if (wsi->u.ws.opcode & 8)
goto illegal_ctl_length;
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_8;
break;
default:
wsi->u.ws.rx_packet_length = c & 0x7f;
if (wsi->u.ws.this_frame_masked)
wsi->lws_rx_parse_state =
LWS_RXPS_07_COLLECT_FRAME_KEY_1;
else
if (wsi->u.ws.rx_packet_length)
wsi->lws_rx_parse_state =
LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
else {
wsi->lws_rx_parse_state = LWS_RXPS_NEW;
goto spill;
}
break;
}
break;
case LWS_RXPS_04_FRAME_HDR_LEN16_2:
wsi->u.ws.rx_packet_length = c << 8;
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN16_1;
break;
case LWS_RXPS_04_FRAME_HDR_LEN16_1:
wsi->u.ws.rx_packet_length |= c;
if (wsi->u.ws.this_frame_masked)
wsi->lws_rx_parse_state =
LWS_RXPS_07_COLLECT_FRAME_KEY_1;
else
wsi->lws_rx_parse_state =
LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
break;
case LWS_RXPS_04_FRAME_HDR_LEN64_8:
if (c & 0x80) {
lwsl_warn("b63 of length must be zero\n");
/* kill the connection */
return -1;
}
#if defined __LP64__
wsi->u.ws.rx_packet_length = ((size_t)c) << 56;
#else
wsi->u.ws.rx_packet_length = 0;
#endif
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_7;
break;
case LWS_RXPS_04_FRAME_HDR_LEN64_7:
#if defined __LP64__
wsi->u.ws.rx_packet_length |= ((size_t)c) << 48;
#endif
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_6;
break;
case LWS_RXPS_04_FRAME_HDR_LEN64_6:
#if defined __LP64__
wsi->u.ws.rx_packet_length |= ((size_t)c) << 40;
#endif
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_5;
break;
case LWS_RXPS_04_FRAME_HDR_LEN64_5:
#if defined __LP64__
wsi->u.ws.rx_packet_length |= ((size_t)c) << 32;
#endif
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_4;
break;
case LWS_RXPS_04_FRAME_HDR_LEN64_4:
wsi->u.ws.rx_packet_length |= ((size_t)c) << 24;
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_3;
break;
case LWS_RXPS_04_FRAME_HDR_LEN64_3:
wsi->u.ws.rx_packet_length |= ((size_t)c) << 16;
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_2;
break;
case LWS_RXPS_04_FRAME_HDR_LEN64_2:
wsi->u.ws.rx_packet_length |= ((size_t)c) << 8;
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_LEN64_1;
break;
case LWS_RXPS_04_FRAME_HDR_LEN64_1:
wsi->u.ws.rx_packet_length |= ((size_t)c);
if (wsi->u.ws.this_frame_masked)
wsi->lws_rx_parse_state =
LWS_RXPS_07_COLLECT_FRAME_KEY_1;
else
wsi->lws_rx_parse_state =
LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
break;
case LWS_RXPS_07_COLLECT_FRAME_KEY_1:
wsi->u.ws.frame_masking_nonce_04[0] = c;
if (c)
wsi->u.ws.all_zero_nonce = 0;
wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_2;
break;
case LWS_RXPS_07_COLLECT_FRAME_KEY_2:
wsi->u.ws.frame_masking_nonce_04[1] = c;
if (c)
wsi->u.ws.all_zero_nonce = 0;
wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_3;
break;
case LWS_RXPS_07_COLLECT_FRAME_KEY_3:
wsi->u.ws.frame_masking_nonce_04[2] = c;
if (c)
wsi->u.ws.all_zero_nonce = 0;
wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_4;
break;
case LWS_RXPS_07_COLLECT_FRAME_KEY_4:
wsi->u.ws.frame_masking_nonce_04[3] = c;
if (c)
wsi->u.ws.all_zero_nonce = 0;
wsi->lws_rx_parse_state =
LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED;
wsi->u.ws.frame_mask_index = 0;
if (wsi->u.ws.rx_packet_length == 0) {
wsi->lws_rx_parse_state = LWS_RXPS_NEW;
goto spill;
}
break;
case LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED:
if (!wsi->u.ws.rx_user_buffer)
lwsl_err("NULL user buffer...\n");
if (wsi->u.ws.all_zero_nonce)
wsi->u.ws.rx_user_buffer[LWS_SEND_BUFFER_PRE_PADDING +
(wsi->u.ws.rx_user_buffer_head++)] = c;
else
wsi->u.ws.rx_user_buffer[LWS_SEND_BUFFER_PRE_PADDING +
(wsi->u.ws.rx_user_buffer_head++)] =
c ^ wsi->u.ws.frame_masking_nonce_04[
(wsi->u.ws.frame_mask_index++) & 3];
if (--wsi->u.ws.rx_packet_length == 0) {
/* spill because we have the whole frame */
wsi->lws_rx_parse_state = LWS_RXPS_NEW;
goto spill;
}
/*
* if there's no protocol max frame size given, we are
* supposed to default to LWS_MAX_SOCKET_IO_BUF
*/
if (!wsi->protocol->rx_buffer_size &&
wsi->u.ws.rx_user_buffer_head !=
LWS_MAX_SOCKET_IO_BUF)
break;
else
if (wsi->protocol->rx_buffer_size &&
wsi->u.ws.rx_user_buffer_head !=
wsi->protocol->rx_buffer_size)
break;
/* spill because we filled our rx buffer */
spill:
/*
* is this frame a control packet we should take care of at this
* layer? If so service it and hide it from the user callback
*/
lwsl_parser("spill on %s\n", wsi->protocol->name);
switch (wsi->u.ws.opcode) {
case LWS_WS_OPCODE_07__CLOSE:
/* is this an acknowledgement of our close? */
if (wsi->state == WSI_STATE_AWAITING_CLOSE_ACK) {
/*
* fine he has told us he is closing too, let's
* finish our close
*/
lwsl_parser("seen client close ack\n");
return -1;
}
lwsl_parser("server sees client close packet\n");
/* parrot the close packet payload back */
n = libwebsocket_write(wsi, (unsigned char *)
&wsi->u.ws.rx_user_buffer[
LWS_SEND_BUFFER_PRE_PADDING],
wsi->u.ws.rx_user_buffer_head,
LWS_WRITE_CLOSE);
if (n < 0)
lwsl_info("write of close ack failed %d\n", n);
wsi->state = WSI_STATE_RETURNED_CLOSE_ALREADY;
/* close the connection */
return -1;
case LWS_WS_OPCODE_07__PING:
lwsl_info("received %d byte ping, sending pong\n",
wsi->u.ws.rx_user_buffer_head);
lwsl_hexdump(&wsi->u.ws.rx_user_buffer[
LWS_SEND_BUFFER_PRE_PADDING],
wsi->u.ws.rx_user_buffer_head);
/* parrot the ping packet payload back as a pong */
n = libwebsocket_write(wsi, (unsigned char *)
&wsi->u.ws.rx_user_buffer[LWS_SEND_BUFFER_PRE_PADDING],
wsi->u.ws.rx_user_buffer_head, LWS_WRITE_PONG);
if (n < 0)
return -1;
/* ... then just drop it */
wsi->u.ws.rx_user_buffer_head = 0;
return 0;
case LWS_WS_OPCODE_07__PONG:
/* ... then just drop it */
wsi->u.ws.rx_user_buffer_head = 0;
return 0;
case LWS_WS_OPCODE_07__TEXT_FRAME:
case LWS_WS_OPCODE_07__BINARY_FRAME:
case LWS_WS_OPCODE_07__CONTINUATION:
break;
default:
lwsl_parser("passing opc %x up to exts\n",
wsi->u.ws.opcode);
/*
* It's something special we can't understand here.
* Pass the payload up to the extension's parsing
* state machine.
*/
eff_buf.token = &wsi->u.ws.rx_user_buffer[
LWS_SEND_BUFFER_PRE_PADDING];
eff_buf.token_len = wsi->u.ws.rx_user_buffer_head;
if (lws_ext_callback_for_each_active(wsi,
LWS_EXT_CALLBACK_EXTENDED_PAYLOAD_RX,
&eff_buf, 0) <= 0) /* not handle or fail */
lwsl_ext("ext opc opcode 0x%x unknown\n",
wsi->u.ws.opcode);
wsi->u.ws.rx_user_buffer_head = 0;
return 0;
}
/*
* No it's real payload, pass it up to the user callback.
* It's nicely buffered with the pre-padding taken care of
* so it can be sent straight out again using libwebsocket_write
*/
eff_buf.token = &wsi->u.ws.rx_user_buffer[
LWS_SEND_BUFFER_PRE_PADDING];
eff_buf.token_len = wsi->u.ws.rx_user_buffer_head;
if (lws_ext_callback_for_each_active(wsi,
LWS_EXT_CALLBACK_PAYLOAD_RX, &eff_buf, 0) < 0)
return -1;
if (eff_buf.token_len > 0) {
eff_buf.token[eff_buf.token_len] = '\0';
if (wsi->protocol->callback)
ret = user_callback_handle_rxflow(
wsi->protocol->callback,
wsi->protocol->owning_server,
wsi, LWS_CALLBACK_RECEIVE,
wsi->user_space,
eff_buf.token,
eff_buf.token_len);
else
lwsl_err("No callback on payload spill!\n");
}
wsi->u.ws.rx_user_buffer_head = 0;
break;
}
return ret;
illegal_ctl_length:
lwsl_warn("Control frame with xtended length is illegal\n");
/* kill the connection */
return -1;
}
/**
* libwebsockets_remaining_packet_payload() - Bytes to come before "overall"
* rx packet is complete
* @wsi: Websocket instance (available from user callback)
*
* This function is intended to be called from the callback if the
* user code is interested in "complete packets" from the client.
* libwebsockets just passes through payload as it comes and issues a buffer
* additionally when it hits a built-in limit. The LWS_CALLBACK_RECEIVE
* callback handler can use this API to find out if the buffer it has just
* been given is the last piece of a "complete packet" from the client --
* when that is the case libwebsockets_remaining_packet_payload() will return
* 0.
*
* Many protocols won't care becuse their packets are always small.
*/
LWS_VISIBLE size_t
libwebsockets_remaining_packet_payload(struct libwebsocket *wsi)
{
return wsi->u.ws.rx_packet_length;
}

View File

@ -0,0 +1,239 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2014 Andy Green <andy@warmcat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation:
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include "private-libwebsockets.h"
int
insert_wsi_socket_into_fds(struct libwebsocket_context *context,
struct libwebsocket *wsi)
{
struct libwebsocket_pollargs pa = { wsi->sock, LWS_POLLIN, 0 };
if (context->fds_count >= context->max_fds) {
lwsl_err("Too many fds (%d)\n", context->max_fds);
return 1;
}
if (wsi->sock >= context->max_fds) {
lwsl_err("Socket fd %d is too high (%d)\n",
wsi->sock, context->max_fds);
return 1;
}
assert(wsi);
assert(wsi->sock >= 0);
lwsl_info("insert_wsi_socket_into_fds: wsi=%p, sock=%d, fds pos=%d\n",
wsi, wsi->sock, context->fds_count);
context->protocols[0].callback(context, wsi,
LWS_CALLBACK_LOCK_POLL,
wsi->user_space, (void *) &pa, 0);
context->lws_lookup[wsi->sock] = wsi;
wsi->position_in_fds_table = context->fds_count;
context->fds[context->fds_count].fd = wsi->sock;
context->fds[context->fds_count].events = LWS_POLLIN;
lws_plat_insert_socket_into_fds(context, wsi);
/* external POLL support via protocol 0 */
context->protocols[0].callback(context, wsi,
LWS_CALLBACK_ADD_POLL_FD,
wsi->user_space, (void *) &pa, 0);
context->protocols[0].callback(context, wsi,
LWS_CALLBACK_UNLOCK_POLL,
wsi->user_space, (void *)&pa, 0);
return 0;
}
int
remove_wsi_socket_from_fds(struct libwebsocket_context *context,
struct libwebsocket *wsi)
{
int m;
struct libwebsocket_pollargs pa = { wsi->sock, 0, 0 };
lws_libev_io(context, wsi, LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE);
if (!--context->fds_count) {
context->protocols[0].callback(context, wsi,
LWS_CALLBACK_LOCK_POLL,
wsi->user_space, (void *) &pa, 0);
goto do_ext;
}
if (wsi->sock > context->max_fds) {
lwsl_err("Socket fd %d too high (%d)\n",
wsi->sock, context->max_fds);
return 1;
}
lwsl_info("%s: wsi=%p, sock=%d, fds pos=%d\n", __func__,
wsi, wsi->sock, wsi->position_in_fds_table);
context->protocols[0].callback(context, wsi,
LWS_CALLBACK_LOCK_POLL,
wsi->user_space, (void *)&pa, 0);
m = wsi->position_in_fds_table; /* replace the contents for this */
/* have the last guy take up the vacant slot */
context->fds[m] = context->fds[context->fds_count];
lws_plat_delete_socket_from_fds(context, wsi, m);
/*
* end guy's fds_lookup entry remains unchanged
* (still same fd pointing to same wsi)
*/
/* end guy's "position in fds table" changed */
context->lws_lookup[context->fds[context->fds_count].fd]->
position_in_fds_table = m;
/* deletion guy's lws_lookup entry needs nuking */
context->lws_lookup[wsi->sock] = NULL;
/* removed wsi has no position any more */
wsi->position_in_fds_table = -1;
do_ext:
/* remove also from external POLL support via protocol 0 */
if (wsi->sock) {
context->protocols[0].callback(context, wsi,
LWS_CALLBACK_DEL_POLL_FD, wsi->user_space,
(void *) &pa, 0);
}
context->protocols[0].callback(context, wsi,
LWS_CALLBACK_UNLOCK_POLL,
wsi->user_space, (void *) &pa, 0);
return 0;
}
int
lws_change_pollfd(struct libwebsocket *wsi, int _and, int _or)
{
struct libwebsocket_context *context = wsi->protocol->owning_server;
int tid;
int sampled_tid;
struct libwebsocket_pollfd *pfd;
struct libwebsocket_pollargs pa;
pfd = &context->fds[wsi->position_in_fds_table];
pa.fd = wsi->sock;
context->protocols[0].callback(context, wsi,
LWS_CALLBACK_LOCK_POLL, wsi->user_space, (void *) &pa, 0);
pa.prev_events = pfd->events;
pa.events = pfd->events = (pfd->events & ~_and) | _or;
context->protocols[0].callback(context, wsi,
LWS_CALLBACK_CHANGE_MODE_POLL_FD,
wsi->user_space, (void *) &pa, 0);
/*
* if we changed something in this pollfd...
* ... and we're running in a different thread context
* than the service thread...
* ... and the service thread is waiting ...
* then cancel it to force a restart with our changed events
*/
if (pa.prev_events != pa.events) {
if (lws_plat_change_pollfd(context, wsi, pfd)) {
lwsl_info("%s failed\n", __func__);
return 1;
}
sampled_tid = context->service_tid;
if (sampled_tid) {
tid = context->protocols[0].callback(context, NULL,
LWS_CALLBACK_GET_THREAD_ID, NULL, NULL, 0);
if (tid != sampled_tid)
libwebsocket_cancel_service(context);
}
}
context->protocols[0].callback(context, wsi,
LWS_CALLBACK_UNLOCK_POLL, wsi->user_space, (void *) &pa, 0);
return 0;
}
/**
* libwebsocket_callback_on_writable() - Request a callback when this socket
* becomes able to be written to without
* blocking
*
* @context: libwebsockets context
* @wsi: Websocket connection instance to get callback for
*/
LWS_VISIBLE int
libwebsocket_callback_on_writable(struct libwebsocket_context *context,
struct libwebsocket *wsi)
{
if (lws_ext_callback_for_each_active(wsi,
LWS_EXT_CALLBACK_REQUEST_ON_WRITEABLE, NULL, 0))
return 1;
if (wsi->position_in_fds_table < 0) {
lwsl_err("%s: failed to find socket %d\n", __func__, wsi->sock);
return -1;
}
if (lws_change_pollfd(wsi, 0, LWS_POLLOUT))
return -1;
lws_libev_io(context, wsi, LWS_EV_START | LWS_EV_WRITE);
return 1;
}
/**
* libwebsocket_callback_on_writable_all_protocol() - Request a callback for
* all connections using the given protocol when it
* becomes possible to write to each socket without
* blocking in turn.
*
* @protocol: Protocol whose connections will get callbacks
*/
LWS_VISIBLE int
libwebsocket_callback_on_writable_all_protocol(
const struct libwebsocket_protocols *protocol)
{
struct libwebsocket_context *context = protocol->owning_server;
int n;
struct libwebsocket *wsi;
for (n = 0; n < context->fds_count; n++) {
wsi = context->lws_lookup[context->fds[n].fd];
if (!wsi)
continue;
if (wsi->protocol == protocol)
libwebsocket_callback_on_writable(context, wsi);
}
return 0;
}

View File

@ -0,0 +1,917 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2013 Andy Green <andy@warmcat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation:
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include "build.h"
/* The Libwebsocket version */
#define LWS_LIBRARY_VERSION "1.3"
/* The current git commit hash that we're building from */
#define LWS_BUILD_HASH "c11b847"
/* System introspection configs */
#ifdef CMAKE_BUILD
#include "lws_config.h"
#else
#if HOST_OS == OS_WINDOWS
#define inline __inline
#else /* not WIN32 */
#include "config.h"
#endif /* not WIN32 */
#endif /* not CMAKE */
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <ctype.h>
#include <limits.h>
#include <stdarg.h>
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#if defined(WIN32) || defined(_WIN32)
#define LWS_NO_DAEMONIZE
#define LWS_ERRNO WSAGetLastError()
#define LWS_EAGAIN WSAEWOULDBLOCK
#define LWS_EALREADY WSAEALREADY
#define LWS_EINPROGRESS WSAEINPROGRESS
#define LWS_EINTR WSAEINTR
#define LWS_EISCONN WSAEISCONN
#define LWS_EWOULDBLOCK WSAEWOULDBLOCK
#define LWS_POLLHUP (FD_CLOSE)
#define LWS_POLLIN (FD_READ | FD_ACCEPT)
#define LWS_POLLOUT (FD_WRITE)
#define MSG_NOSIGNAL 0
#define SHUT_RDWR SD_BOTH
#define SOL_TCP IPPROTO_TCP
#define compatible_close(fd) closesocket(fd)
#define compatible_file_close(fd) CloseHandle(fd)
#define compatible_file_seek_cur(fd, offset) SetFilePointer(fd, offset, NULL, FILE_CURRENT)
#define compatible_file_read(amount, fd, buf, len) {\
DWORD _amount; \
if (!ReadFile(fd, buf, len, &_amount, NULL)) \
amount = -1; \
else \
amount = _amount; \
}
#define lws_set_blocking_send(wsi) wsi->sock_send_blocking = TRUE
#include <winsock2.h>
#include <windows.h>
#include <tchar.h>
#ifdef HAVE_IN6ADDR_H
#include <in6addr.h>
#endif
#include <mstcpip.h>
#ifndef __func__
#define __func__ __FUNCTION__
#endif
#ifdef _WIN32_WCE
#define vsnprintf _vsnprintf
#endif
#define LWS_INVALID_FILE INVALID_HANDLE_VALUE
#else /* not windows --> */
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <signal.h>
#include <strings.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#ifdef LWS_BUILTIN_GETIFADDRS
#include "deps/ifaddrs/ifaddrs.h"
#else
#include <ifaddrs.h>
#endif
//#include <sys/syslog.h>
#include <sys/un.h>
#include <sys/socket.h>
#include <netdb.h>
#ifndef LWS_NO_FORK
#ifdef HAVE_SYS_PRCTL_H
#include <sys/prctl.h>
#endif
#endif
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <poll.h>
#ifdef LWS_USE_LIBEV
#include <ev.h>
#endif /* LWS_USE_LIBEV */
#include <sys/mman.h>
#include <sys/time.h>
#define LWS_ERRNO errno
#define LWS_EAGAIN EAGAIN
#define LWS_EALREADY EALREADY
#define LWS_EINPROGRESS EINPROGRESS
#define LWS_EINTR EINTR
#define LWS_EISCONN EISCONN
#define LWS_EWOULDBLOCK EWOULDBLOCK
#define LWS_INVALID_FILE -1
#define LWS_POLLHUP (POLLHUP|POLLERR)
#define LWS_POLLIN (POLLIN)
#define LWS_POLLOUT (POLLOUT)
#define compatible_close(fd) close(fd)
#define compatible_file_close(fd) close(fd)
#define compatible_file_seek_cur(fd, offset) lseek(fd, offset, SEEK_CUR)
#define compatible_file_read(amount, fd, buf, len) \
amount = read(fd, buf, len);
#define lws_set_blocking_send(wsi)
#endif
#ifndef HAVE_BZERO
#define bzero(b, len) (memset((b), '\0', (len)), (void) 0)
#endif
#ifndef HAVE_STRERROR
#define strerror(x) ""
#endif
#ifdef LWS_OPENSSL_SUPPORT
#ifdef USE_CYASSL
#include <cyassl/openssl/ssl.h>
#include <cyassl/error.h>
unsigned char *
SHA1(const unsigned char *d, size_t n, unsigned char *md);
#else
#include <openssl/ssl.h>
#include <openssl/evp.h>
#include <openssl/err.h>
#include <openssl/md5.h>
#include <openssl/sha.h>
#endif /* not USE_CYASSL */
#endif
#include "libwebsockets.h"
#if defined(WIN32) || defined(_WIN32)
#ifndef BIG_ENDIAN
#define BIG_ENDIAN 4321 /* to show byte order (taken from gcc) */
#endif
#ifndef LITTLE_ENDIAN
#define LITTLE_ENDIAN 1234
#endif
#ifndef BYTE_ORDER
#define BYTE_ORDER LITTLE_ENDIAN
#endif
typedef unsigned __int64 u_int64_t;
#undef __P
#ifndef __P
#if __STDC__
#define __P(protos) protos
#else
#define __P(protos) ()
#endif
#endif
#else
#include <sys/stat.h>
#include <sys/cdefs.h>
#include <sys/time.h>
#if defined(__APPLE__)
#include <machine/endian.h>
#elif defined(__FreeBSD__)
#include <sys/endian.h>
#elif defined(__linux__)
#include <endian.h>
#endif
#if !defined(BYTE_ORDER)
# define BYTE_ORDER __BYTE_ORDER
#endif
#if !defined(LITTLE_ENDIAN)
# define LITTLE_ENDIAN __LITTLE_ENDIAN
#endif
#if !defined(BIG_ENDIAN)
# define BIG_ENDIAN __BIG_ENDIAN
#endif
#endif
/*
* Mac OSX as well as iOS do not define the MSG_NOSIGNAL flag,
* but happily have something equivalent in the SO_NOSIGPIPE flag.
*/
#ifdef __APPLE__
#define MSG_NOSIGNAL SO_NOSIGPIPE
#endif
#ifndef LWS_MAX_HEADER_LEN
#define LWS_MAX_HEADER_LEN 1024
#endif
#ifndef LWS_MAX_PROTOCOLS
#define LWS_MAX_PROTOCOLS 5
#endif
#ifndef LWS_MAX_EXTENSIONS_ACTIVE
#define LWS_MAX_EXTENSIONS_ACTIVE 3
#endif
#ifndef SPEC_LATEST_SUPPORTED
#define SPEC_LATEST_SUPPORTED 13
#endif
#ifndef AWAITING_TIMEOUT
#define AWAITING_TIMEOUT 5
#endif
#ifndef CIPHERS_LIST_STRING
#define CIPHERS_LIST_STRING "DEFAULT"
#endif
#ifndef LWS_SOMAXCONN
#define LWS_SOMAXCONN SOMAXCONN
#endif
#define MAX_WEBSOCKET_04_KEY_LEN 128
#define LWS_MAX_SOCKET_IO_BUF 4096
#ifndef SYSTEM_RANDOM_FILEPATH
#define SYSTEM_RANDOM_FILEPATH "/dev/urandom"
#endif
#ifndef LWS_MAX_ZLIB_CONN_BUFFER
#define LWS_MAX_ZLIB_CONN_BUFFER (64 * 1024)
#endif
/*
* if not in a connection storm, check for incoming
* connections this many normal connection services
*/
#define LWS_LISTEN_SERVICE_MODULO 10
enum lws_websocket_opcodes_07 {
LWS_WS_OPCODE_07__CONTINUATION = 0,
LWS_WS_OPCODE_07__TEXT_FRAME = 1,
LWS_WS_OPCODE_07__BINARY_FRAME = 2,
LWS_WS_OPCODE_07__NOSPEC__MUX = 7,
/* control extensions 8+ */
LWS_WS_OPCODE_07__CLOSE = 8,
LWS_WS_OPCODE_07__PING = 9,
LWS_WS_OPCODE_07__PONG = 0xa,
};
enum lws_connection_states {
WSI_STATE_HTTP,
WSI_STATE_HTTP_ISSUING_FILE,
WSI_STATE_HTTP_HEADERS,
WSI_STATE_HTTP_BODY,
WSI_STATE_DEAD_SOCKET,
WSI_STATE_ESTABLISHED,
WSI_STATE_CLIENT_UNCONNECTED,
WSI_STATE_RETURNED_CLOSE_ALREADY,
WSI_STATE_AWAITING_CLOSE_ACK,
WSI_STATE_FLUSHING_STORED_SEND_BEFORE_CLOSE,
};
enum lws_rx_parse_state {
LWS_RXPS_NEW,
LWS_RXPS_04_MASK_NONCE_1,
LWS_RXPS_04_MASK_NONCE_2,
LWS_RXPS_04_MASK_NONCE_3,
LWS_RXPS_04_FRAME_HDR_1,
LWS_RXPS_04_FRAME_HDR_LEN,
LWS_RXPS_04_FRAME_HDR_LEN16_2,
LWS_RXPS_04_FRAME_HDR_LEN16_1,
LWS_RXPS_04_FRAME_HDR_LEN64_8,
LWS_RXPS_04_FRAME_HDR_LEN64_7,
LWS_RXPS_04_FRAME_HDR_LEN64_6,
LWS_RXPS_04_FRAME_HDR_LEN64_5,
LWS_RXPS_04_FRAME_HDR_LEN64_4,
LWS_RXPS_04_FRAME_HDR_LEN64_3,
LWS_RXPS_04_FRAME_HDR_LEN64_2,
LWS_RXPS_04_FRAME_HDR_LEN64_1,
LWS_RXPS_07_COLLECT_FRAME_KEY_1,
LWS_RXPS_07_COLLECT_FRAME_KEY_2,
LWS_RXPS_07_COLLECT_FRAME_KEY_3,
LWS_RXPS_07_COLLECT_FRAME_KEY_4,
LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED
};
enum connection_mode {
LWS_CONNMODE_HTTP_SERVING,
LWS_CONNMODE_HTTP_SERVING_ACCEPTED, /* actual HTTP service going on */
LWS_CONNMODE_PRE_WS_SERVING_ACCEPT,
LWS_CONNMODE_WS_SERVING,
LWS_CONNMODE_WS_CLIENT,
/* transient, ssl delay hiding */
LWS_CONNMODE_SSL_ACK_PENDING,
/* transient modes */
LWS_CONNMODE_WS_CLIENT_WAITING_CONNECT,
LWS_CONNMODE_WS_CLIENT_WAITING_PROXY_REPLY,
LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE,
LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE2,
LWS_CONNMODE_WS_CLIENT_WAITING_SSL,
LWS_CONNMODE_WS_CLIENT_WAITING_SERVER_REPLY,
LWS_CONNMODE_WS_CLIENT_WAITING_EXTENSION_CONNECT,
LWS_CONNMODE_WS_CLIENT_PENDING_CANDIDATE_CHILD,
/* special internal types */
LWS_CONNMODE_SERVER_LISTENER,
};
enum {
LWS_RXFLOW_ALLOW = (1 << 0),
LWS_RXFLOW_PENDING_CHANGE = (1 << 1),
};
struct libwebsocket_protocols;
struct libwebsocket;
#ifdef LWS_USE_LIBEV
struct lws_io_watcher {
struct ev_io watcher;
struct libwebsocket_context* context;
};
struct lws_signal_watcher {
struct ev_signal watcher;
struct libwebsocket_context* context;
};
#endif /* LWS_USE_LIBEV */
struct libwebsocket_context {
#ifdef _WIN32
WSAEVENT *events;
#endif
struct libwebsocket_pollfd *fds;
struct libwebsocket **lws_lookup; /* fd to wsi */
int fds_count;
#ifdef LWS_USE_LIBEV
struct ev_loop* io_loop;
struct lws_io_watcher w_accept;
struct lws_signal_watcher w_sigint;
#endif /* LWS_USE_LIBEV */
int max_fds;
int listen_port;
const char *iface;
char http_proxy_address[128];
char canonical_hostname[128];
unsigned int http_proxy_port;
unsigned int options;
time_t last_timeout_check_s;
/*
* usable by anything in the service code, but only if the scope
* does not last longer than the service action (since next service
* of any socket can likewise use it and overwrite)
*/
unsigned char service_buffer[LWS_MAX_SOCKET_IO_BUF];
int started_with_parent;
int fd_random;
int listen_service_modulo;
int listen_service_count;
int listen_service_fd;
int listen_service_extraseen;
/*
* set to the Thread ID that's doing the service loop just before entry
* to poll indicates service thread likely idling in poll()
* volatile because other threads may check it as part of processing
* for pollfd event change.
*/
volatile int service_tid;
#ifndef _WIN32
int dummy_pipe_fds[2];
#endif
int ka_time;
int ka_probes;
int ka_interval;
#ifdef LWS_LATENCY
unsigned long worst_latency;
char worst_latency_info[256];
#endif
#ifdef LWS_OPENSSL_SUPPORT
int use_ssl;
int allow_non_ssl_on_ssl_port;
SSL_CTX *ssl_ctx;
SSL_CTX *ssl_client_ctx;
#endif
struct libwebsocket_protocols *protocols;
int count_protocols;
#ifndef LWS_NO_EXTENSIONS
struct libwebsocket_extension *extensions;
#endif
void *user_space;
};
enum {
LWS_EV_READ = (1 << 0),
LWS_EV_WRITE = (1 << 1),
LWS_EV_START = (1 << 2),
LWS_EV_STOP = (1 << 3),
};
#ifdef LWS_USE_LIBEV
#define LWS_LIBEV_ENABLED(context) (context->options & LWS_SERVER_OPTION_LIBEV)
LWS_EXTERN void lws_feature_status_libev(struct lws_context_creation_info *info);
LWS_EXTERN void
lws_libev_accept(struct libwebsocket_context *context,
struct libwebsocket *new_wsi, int accept_fd);
LWS_EXTERN void
lws_libev_io(struct libwebsocket_context *context,
struct libwebsocket *wsi, int flags);
LWS_EXTERN int
lws_libev_init_fd_table(struct libwebsocket_context *context);
LWS_EXTERN void
lws_libev_run(struct libwebsocket_context *context);
#else
#define LWS_LIBEV_ENABLED(context) (0)
#define lws_feature_status_libev(_a) \
lwsl_notice("libev support not compiled in\n")
#define lws_libev_accept(_a, _b, _c) (0)
#define lws_libev_io(_a, _b, _c) (0)
#define lws_libev_init_fd_table(_a) (0)
#define lws_libev_run(_a) (0)
#endif
#ifdef LWS_USE_IPV6
#define LWS_IPV6_ENABLED(context) (!(context->options & LWS_SERVER_OPTION_DISABLE_IPV6))
#else
#define LWS_IPV6_ENABLED(context) (0)
#endif
enum uri_path_states {
URIPS_IDLE,
URIPS_SEEN_SLASH,
URIPS_SEEN_SLASH_DOT,
URIPS_SEEN_SLASH_DOT_DOT,
URIPS_ARGUMENTS,
};
enum uri_esc_states {
URIES_IDLE,
URIES_SEEN_PERCENT,
URIES_SEEN_PERCENT_H1,
};
/*
* This is totally opaque to code using the library. It's exported as a
* forward-reference pointer-only declaration; the user can use the pointer with
* other APIs to get information out of it.
*/
struct lws_fragments {
unsigned short offset;
unsigned short len;
unsigned char next_frag_index;
};
struct allocated_headers {
unsigned short next_frag_index;
unsigned short pos;
unsigned char frag_index[WSI_TOKEN_COUNT];
struct lws_fragments frags[WSI_TOKEN_COUNT * 2];
char data[LWS_MAX_HEADER_LEN];
#ifndef LWS_NO_CLIENT
char initial_handshake_hash_base64[30];
unsigned short c_port;
#endif
};
struct _lws_http_mode_related {
struct allocated_headers *ah; /* mirroring _lws_header_related */
#if defined(WIN32) || defined(_WIN32)
HANDLE fd;
#else
int fd;
#endif
unsigned long filepos;
unsigned long filelen;
int content_length;
int content_length_seen;
int body_index;
unsigned char *post_buffer;
};
struct _lws_header_related {
struct allocated_headers *ah;
short lextable_pos;
unsigned char parser_state; /* enum lws_token_indexes */
enum uri_path_states ups;
enum uri_esc_states ues;
char esc_stash;
};
struct _lws_websocket_related {
char *rx_user_buffer;
int rx_user_buffer_head;
unsigned char frame_masking_nonce_04[4];
unsigned char frame_mask_index;
size_t rx_packet_length;
unsigned char opcode;
unsigned int final:1;
unsigned char rsv;
unsigned int frame_is_binary:1;
unsigned int all_zero_nonce:1;
short close_reason; /* enum lws_close_status */
unsigned char *rxflow_buffer;
int rxflow_len;
int rxflow_pos;
unsigned int rxflow_change_to:2;
unsigned int this_frame_masked:1;
unsigned int inside_frame:1; /* next write will be more of frame */
unsigned int clean_buffer:1; /* buffer not rewritten by extension */
};
struct libwebsocket {
/* lifetime members */
#ifdef LWS_USE_LIBEV
struct lws_io_watcher w_read;
struct lws_io_watcher w_write;
#endif /* LWS_USE_LIBEV */
const struct libwebsocket_protocols *protocol;
#ifndef LWS_NO_EXTENSIONS
struct libwebsocket_extension *
active_extensions[LWS_MAX_EXTENSIONS_ACTIVE];
void *active_extensions_user[LWS_MAX_EXTENSIONS_ACTIVE];
unsigned char count_active_extensions;
unsigned int extension_data_pending:1;
#endif
unsigned char ietf_spec_revision;
char mode; /* enum connection_mode */
char state; /* enum lws_connection_states */
char lws_rx_parse_state; /* enum lws_rx_parse_state */
char rx_frame_type; /* enum libwebsocket_write_protocol */
unsigned int hdr_parsing_completed:1;
char pending_timeout; /* enum pending_timeout */
time_t pending_timeout_limit;
int sock;
int position_in_fds_table;
#ifdef LWS_LATENCY
unsigned long action_start;
unsigned long latency_start;
#endif
/* truncated send handling */
unsigned char *truncated_send_malloc; /* non-NULL means buffering in progress */
unsigned int truncated_send_allocation; /* size of malloc */
unsigned int truncated_send_offset; /* where we are in terms of spilling */
unsigned int truncated_send_len; /* how much is buffered */
void *user_space;
/* members with mutually exclusive lifetimes are unionized */
union u {
struct _lws_http_mode_related http;
struct _lws_header_related hdr;
struct _lws_websocket_related ws;
} u;
#ifdef LWS_OPENSSL_SUPPORT
SSL *ssl;
BIO *client_bio;
unsigned int use_ssl:2;
#endif
#ifdef _WIN32
BOOL sock_send_blocking;
#endif
};
LWS_EXTERN int log_level;
LWS_EXTERN void
libwebsocket_close_and_free_session(struct libwebsocket_context *context,
struct libwebsocket *wsi, enum lws_close_status);
LWS_EXTERN int
remove_wsi_socket_from_fds(struct libwebsocket_context *context,
struct libwebsocket *wsi);
#ifndef LWS_LATENCY
static inline void lws_latency(struct libwebsocket_context *context,
struct libwebsocket *wsi, const char *action,
int ret, int completion) { while (0); }
static inline void lws_latency_pre(struct libwebsocket_context *context,
struct libwebsocket *wsi) { while (0); }
#else
#define lws_latency_pre(_context, _wsi) lws_latency(_context, _wsi, NULL, 0, 0)
extern void
lws_latency(struct libwebsocket_context *context,
struct libwebsocket *wsi, const char *action,
int ret, int completion);
#endif
LWS_EXTERN int
libwebsocket_client_rx_sm(struct libwebsocket *wsi, unsigned char c);
LWS_EXTERN int
libwebsocket_parse(struct libwebsocket *wsi, unsigned char c);
LWS_EXTERN int
lws_b64_selftest(void);
LWS_EXTERN struct libwebsocket *
wsi_from_fd(struct libwebsocket_context *context, int fd);
LWS_EXTERN int
insert_wsi_socket_into_fds(struct libwebsocket_context *context,
struct libwebsocket *wsi);
LWS_EXTERN int
lws_issue_raw(struct libwebsocket *wsi, unsigned char *buf, size_t len);
LWS_EXTERN int
libwebsocket_service_timeout_check(struct libwebsocket_context *context,
struct libwebsocket *wsi, unsigned int sec);
LWS_EXTERN struct libwebsocket *
libwebsocket_client_connect_2(struct libwebsocket_context *context,
struct libwebsocket *wsi);
LWS_EXTERN struct libwebsocket *
libwebsocket_create_new_server_wsi(struct libwebsocket_context *context);
LWS_EXTERN char *
libwebsockets_generate_client_handshake(struct libwebsocket_context *context,
struct libwebsocket *wsi, char *pkt);
LWS_EXTERN int
lws_handle_POLLOUT_event(struct libwebsocket_context *context,
struct libwebsocket *wsi, struct libwebsocket_pollfd *pollfd);
/*
* EXTENSIONS
*/
#ifndef LWS_NO_EXTENSIONS
LWS_VISIBLE void
lws_context_init_extensions(struct lws_context_creation_info *info,
struct libwebsocket_context *context);
LWS_EXTERN int
lws_any_extension_handled(struct libwebsocket_context *context,
struct libwebsocket *wsi,
enum libwebsocket_extension_callback_reasons r,
void *v, size_t len);
LWS_EXTERN int
lws_ext_callback_for_each_active(struct libwebsocket *wsi, int reason,
void *buf, int len);
LWS_EXTERN int
lws_ext_callback_for_each_extension_type(
struct libwebsocket_context *context, struct libwebsocket *wsi,
int reason, void *arg, int len);
#else
#define lws_any_extension_handled(_a, _b, _c, _d, _e) (0)
#define lws_ext_callback_for_each_active(_a, _b, _c, _d) (0)
#define lws_ext_callback_for_each_extension_type(_a, _b, _c, _d, _e) (0)
#define lws_issue_raw_ext_access lws_issue_raw
#define lws_context_init_extensions(_a, _b)
#endif
LWS_EXTERN int
lws_client_interpret_server_handshake(struct libwebsocket_context *context,
struct libwebsocket *wsi);
LWS_EXTERN int
libwebsocket_rx_sm(struct libwebsocket *wsi, unsigned char c);
LWS_EXTERN int
lws_issue_raw_ext_access(struct libwebsocket *wsi,
unsigned char *buf, size_t len);
LWS_EXTERN int
_libwebsocket_rx_flow_control(struct libwebsocket *wsi);
LWS_EXTERN int
user_callback_handle_rxflow(callback_function,
struct libwebsocket_context *context,
struct libwebsocket *wsi,
enum libwebsocket_callback_reasons reason, void *user,
void *in, size_t len);
LWS_EXTERN int
lws_plat_set_socket_options(struct libwebsocket_context *context, int fd);
LWS_EXTERN int
lws_allocate_header_table(struct libwebsocket *wsi);
LWS_EXTERN char *
lws_hdr_simple_ptr(struct libwebsocket *wsi, enum lws_token_indexes h);
LWS_EXTERN int
lws_hdr_simple_create(struct libwebsocket *wsi,
enum lws_token_indexes h, const char *s);
LWS_EXTERN int
libwebsocket_ensure_user_space(struct libwebsocket *wsi);
LWS_EXTERN int
lws_change_pollfd(struct libwebsocket *wsi, int _and, int _or);
#ifndef LWS_NO_SERVER
int lws_context_init_server(struct lws_context_creation_info *info,
struct libwebsocket_context *context);
LWS_EXTERN int handshake_0405(struct libwebsocket_context *context,
struct libwebsocket *wsi);
LWS_EXTERN int
libwebsocket_interpret_incoming_packet(struct libwebsocket *wsi,
unsigned char *buf, size_t len);
LWS_EXTERN void
lws_server_get_canonical_hostname(struct libwebsocket_context *context,
struct lws_context_creation_info *info);
#else
#define lws_context_init_server(_a, _b) (0)
#define libwebsocket_interpret_incoming_packet(_a, _b, _c) (0)
#define lws_server_get_canonical_hostname(_a, _b)
#endif
#ifndef LWS_NO_DAEMONIZE
LWS_EXTERN int get_daemonize_pid();
#else
#define get_daemonize_pid() (0)
#endif
LWS_EXTERN int interface_to_sa(struct libwebsocket_context *context,
const char *ifname, struct sockaddr_in *addr, size_t addrlen);
LWS_EXTERN void lwsl_emit_stderr(int level, const char *line);
#ifdef _WIN32
LWS_EXTERN HANDLE lws_plat_open_file(const char* filename, unsigned long* filelen);
#else
LWS_EXTERN int lws_plat_open_file(const char* filename, unsigned long* filelen);
#endif
enum lws_ssl_capable_status {
LWS_SSL_CAPABLE_ERROR = -1,
LWS_SSL_CAPABLE_MORE_SERVICE = -2,
};
#ifndef LWS_OPENSSL_SUPPORT
#define LWS_SSL_ENABLED(context) (0)
unsigned char *
SHA1(const unsigned char *d, size_t n, unsigned char *md);
#define lws_context_init_server_ssl(_a, _b) (0)
#define lws_ssl_destroy(_a)
#define lws_context_init_http2_ssl(_a)
#define lws_ssl_pending(_a) (0)
#define lws_ssl_capable_read lws_ssl_capable_read_no_ssl
#define lws_ssl_capable_write lws_ssl_capable_write_no_ssl
#define lws_server_socket_service_ssl(_a, _b, _c, _d, _e) (0)
#define lws_ssl_close(_a) (0)
#define lws_ssl_context_destroy(_a)
#else
#define LWS_SSL_ENABLED(context) (context->use_ssl)
LWS_EXTERN int lws_ssl_pending(struct libwebsocket *wsi);
LWS_EXTERN int openssl_websocket_private_data_index;
LWS_EXTERN int
lws_ssl_capable_read(struct libwebsocket *wsi, unsigned char *buf, int len);
LWS_EXTERN int
lws_ssl_capable_write(struct libwebsocket *wsi, unsigned char *buf, int len);
LWS_EXTERN int
lws_server_socket_service_ssl(struct libwebsocket_context *context,
struct libwebsocket **wsi, struct libwebsocket *new_wsi,
int accept_fd, struct libwebsocket_pollfd *pollfd);
LWS_EXTERN int
lws_ssl_close(struct libwebsocket *wsi);
LWS_EXTERN void
lws_ssl_context_destroy(struct libwebsocket_context *context);
#ifndef LWS_NO_SERVER
LWS_EXTERN int
lws_context_init_server_ssl(struct lws_context_creation_info *info,
struct libwebsocket_context *context);
#else
#define lws_context_init_server_ssl(_a, _b) (0)
#endif
LWS_EXTERN void
lws_ssl_destroy(struct libwebsocket_context *context);
/* HTTP2-related */
#ifdef LWS_USE_HTTP2
LWS_EXTERN void
lws_context_init_http2_ssl(struct libwebsocket_context *context);
#else
#define lws_context_init_http2_ssl(_a)
#endif
#endif
LWS_EXTERN int
lws_ssl_capable_read_no_ssl(struct libwebsocket *wsi, unsigned char *buf, int len);
LWS_EXTERN int
lws_ssl_capable_write_no_ssl(struct libwebsocket *wsi, unsigned char *buf, int len);
#ifndef LWS_NO_CLIENT
LWS_EXTERN int lws_client_socket_service(
struct libwebsocket_context *context,
struct libwebsocket *wsi, struct libwebsocket_pollfd *pollfd);
#ifdef LWS_OPENSSL_SUPPORT
LWS_EXTERN int lws_context_init_client_ssl(struct lws_context_creation_info *info,
struct libwebsocket_context *context);
#else
#define lws_context_init_client_ssl(_a, _b) (0)
#endif
LWS_EXTERN int lws_handshake_client(struct libwebsocket *wsi, unsigned char **buf, size_t len);
LWS_EXTERN void
libwebsockets_decode_ssl_error(void);
#else
#define lws_context_init_client_ssl(_a, _b) (0)
#define lws_handshake_client(_a, _b, _c) (0)
#endif
#ifndef LWS_NO_SERVER
LWS_EXTERN int lws_server_socket_service(
struct libwebsocket_context *context,
struct libwebsocket *wsi, struct libwebsocket_pollfd *pollfd);
LWS_EXTERN int _libwebsocket_rx_flow_control(struct libwebsocket *wsi);
LWS_EXTERN int lws_handshake_server(struct libwebsocket_context *context,
struct libwebsocket *wsi, unsigned char **buf, size_t len);
#else
#define lws_server_socket_service(_a, _b, _c) (0)
#define _libwebsocket_rx_flow_control(_a) (0)
#define lws_handshake_server(_a, _b, _c, _d) (0)
#endif
/*
* lws_plat_
*/
LWS_EXTERN void
lws_plat_delete_socket_from_fds(struct libwebsocket_context *context,
struct libwebsocket *wsi, int m);
LWS_EXTERN void
lws_plat_insert_socket_into_fds(struct libwebsocket_context *context,
struct libwebsocket *wsi);
LWS_EXTERN void
lws_plat_service_periodic(struct libwebsocket_context *context);
LWS_EXTERN int
lws_plat_change_pollfd(struct libwebsocket_context *context,
struct libwebsocket *wsi, struct libwebsocket_pollfd *pfd);
LWS_EXTERN int
lws_plat_context_early_init(void);
LWS_EXTERN void
lws_plat_context_early_destroy(struct libwebsocket_context *context);
LWS_EXTERN void
lws_plat_context_late_destroy(struct libwebsocket_context *context);
LWS_EXTERN int
lws_poll_listen_fd(struct libwebsocket_pollfd *fd);
LWS_EXTERN int
lws_plat_service(struct libwebsocket_context *context, int timeout_ms);
LWS_EXTERN int
lws_plat_init_fd_tables(struct libwebsocket_context *context);
LWS_EXTERN void
lws_plat_drop_app_privileges(struct lws_context_creation_info *info);
LWS_EXTERN unsigned long long
time_in_microseconds(void);
LWS_EXTERN const char *
lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt);

View File

@ -0,0 +1,280 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2013 Andy Green <andy@warmcat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation:
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include "private-libwebsockets.h"
#define LWS_CPYAPP(ptr, str) { strcpy(ptr, str); ptr += strlen(str); }
#ifndef LWS_NO_EXTENSIONS
LWS_VISIBLE int
lws_extension_server_handshake(struct libwebsocket_context *context,
struct libwebsocket *wsi, char **p)
{
int n;
char *c;
char ext_name[128];
struct libwebsocket_extension *ext;
int ext_count = 0;
int more = 1;
/*
* Figure out which extensions the client has that we want to
* enable on this connection, and give him back the list
*/
if (!lws_hdr_total_length(wsi, WSI_TOKEN_EXTENSIONS))
return 0;
/*
* break down the list of client extensions
* and go through them
*/
if (lws_hdr_copy(wsi, (char *)context->service_buffer,
sizeof(context->service_buffer),
WSI_TOKEN_EXTENSIONS) < 0)
return 1;
c = (char *)context->service_buffer;
lwsl_parser("WSI_TOKEN_EXTENSIONS = '%s'\n", c);
wsi->count_active_extensions = 0;
n = 0;
while (more) {
if (*c && (*c != ',' && *c != ' ' && *c != '\t')) {
ext_name[n] = *c++;
if (n < sizeof(ext_name) - 1)
n++;
continue;
}
ext_name[n] = '\0';
if (!*c)
more = 0;
else {
c++;
if (!n)
continue;
}
/* check a client's extension against our support */
ext = wsi->protocol->owning_server->extensions;
while (ext && ext->callback) {
if (strcmp(ext_name, ext->name)) {
ext++;
continue;
}
/*
* oh, we do support this one he
* asked for... but let's ask user
* code if it's OK to apply it on this
* particular connection + protocol
*/
n = wsi->protocol->owning_server->
protocols[0].callback(
wsi->protocol->owning_server,
wsi,
LWS_CALLBACK_CONFIRM_EXTENSION_OKAY,
wsi->user_space, ext_name, 0);
/*
* zero return from callback means
* go ahead and allow the extension,
* it's what we get if the callback is
* unhandled
*/
if (n) {
ext++;
continue;
}
/* apply it */
if (ext_count)
*(*p)++ = ',';
else
LWS_CPYAPP(*p,
"\x0d\x0aSec-WebSocket-Extensions: ");
*p += sprintf(*p, "%s", ext_name);
ext_count++;
/* instantiate the extension on this conn */
wsi->active_extensions_user[
wsi->count_active_extensions] =
malloc(ext->per_session_data_size);
if (wsi->active_extensions_user[
wsi->count_active_extensions] == NULL) {
lwsl_err("Out of mem\n");
return 1;
}
memset(wsi->active_extensions_user[
wsi->count_active_extensions], 0,
ext->per_session_data_size);
wsi->active_extensions[
wsi->count_active_extensions] = ext;
/* allow him to construct his context */
ext->callback(wsi->protocol->owning_server,
ext, wsi,
LWS_EXT_CALLBACK_CONSTRUCT,
wsi->active_extensions_user[
wsi->count_active_extensions], NULL, 0);
wsi->count_active_extensions++;
lwsl_parser("count_active_extensions <- %d\n",
wsi->count_active_extensions);
ext++;
}
n = 0;
}
return 0;
}
#endif
int
handshake_0405(struct libwebsocket_context *context, struct libwebsocket *wsi)
{
unsigned char hash[20];
int n;
char *response;
char *p;
int accept_len;
if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST) ||
!lws_hdr_total_length(wsi, WSI_TOKEN_KEY)) {
lwsl_parser("handshake_04 missing pieces\n");
/* completed header processing, but missing some bits */
goto bail;
}
if (lws_hdr_total_length(wsi, WSI_TOKEN_KEY) >=
MAX_WEBSOCKET_04_KEY_LEN) {
lwsl_warn("Client key too long %d\n", MAX_WEBSOCKET_04_KEY_LEN);
goto bail;
}
/*
* since key length is restricted above (currently 128), cannot
* overflow
*/
n = sprintf((char *)context->service_buffer,
"%s258EAFA5-E914-47DA-95CA-C5AB0DC85B11",
lws_hdr_simple_ptr(wsi, WSI_TOKEN_KEY));
SHA1(context->service_buffer, n, hash);
accept_len = lws_b64_encode_string((char *)hash, 20,
(char *)context->service_buffer,
sizeof(context->service_buffer));
if (accept_len < 0) {
lwsl_warn("Base64 encoded hash too long\n");
goto bail;
}
/* allocate the per-connection user memory (if any) */
if (libwebsocket_ensure_user_space(wsi))
goto bail;
/* create the response packet */
/* make a buffer big enough for everything */
response = (char *)context->service_buffer + MAX_WEBSOCKET_04_KEY_LEN;
p = response;
LWS_CPYAPP(p, "HTTP/1.1 101 Switching Protocols\x0d\x0a"
"Upgrade: WebSocket\x0d\x0a"
"Connection: Upgrade\x0d\x0a"
"Sec-WebSocket-Accept: ");
strcpy(p, (char *)context->service_buffer);
p += accept_len;
if (lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL)) {
LWS_CPYAPP(p, "\x0d\x0aSec-WebSocket-Protocol: ");
n = lws_hdr_copy(wsi, p, 128, WSI_TOKEN_PROTOCOL);
if (n < 0)
goto bail;
p += n;
}
#ifndef LWS_NO_EXTENSIONS
/*
* Figure out which extensions the client has that we want to
* enable on this connection, and give him back the list
*/
if (lws_extension_server_handshake(context, wsi, &p))
goto bail;
#endif
/* end of response packet */
LWS_CPYAPP(p, "\x0d\x0a\x0d\x0a");
if (!lws_any_extension_handled(context, wsi,
LWS_EXT_CALLBACK_HANDSHAKE_REPLY_TX,
response, p - response)) {
/* okay send the handshake response accepting the connection */
lwsl_parser("issuing resp pkt %d len\n", (int)(p - response));
#ifdef DEBUG
fwrite(response, 1, p - response, stderr);
#endif
n = libwebsocket_write(wsi, (unsigned char *)response,
p - response, LWS_WRITE_HTTP);
if (n != (p - response)) {
lwsl_debug("handshake_0405: ERROR writing to socket\n");
goto bail;
}
}
/* alright clean up and set ourselves into established state */
wsi->state = WSI_STATE_ESTABLISHED;
wsi->lws_rx_parse_state = LWS_RXPS_NEW;
/* notify user code that we're ready to roll */
if (wsi->protocol->callback)
wsi->protocol->callback(wsi->protocol->owning_server,
wsi, LWS_CALLBACK_ESTABLISHED,
wsi->user_space, NULL, 0);
return 0;
bail:
/* free up his parsing allocations */
if (wsi->u.hdr.ah)
free(wsi->u.hdr.ah);
return -1;
}

View File

@ -0,0 +1,868 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2013 Andy Green <andy@warmcat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation:
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include "private-libwebsockets.h"
int lws_context_init_server(struct lws_context_creation_info *info,
struct libwebsocket_context *context)
{
int n;
int sockfd;
struct sockaddr_in sin;
socklen_t len = sizeof(sin);
int opt = 1;
struct libwebsocket *wsi;
#ifdef LWS_USE_IPV6
struct sockaddr_in6 serv_addr6;
#endif
struct sockaddr_in serv_addr4;
struct sockaddr *v;
/* set up our external listening socket we serve on */
if (info->port == CONTEXT_PORT_NO_LISTEN)
return 0;
#ifdef LWS_USE_IPV6
if (LWS_IPV6_ENABLED(context))
sockfd = socket(AF_INET6, SOCK_STREAM, 0);
else
#endif
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
lwsl_err("ERROR opening socket\n");
return 1;
}
/*
* allow us to restart even if old sockets in TIME_WAIT
*/
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
(const void *)&opt, sizeof(opt));
lws_plat_set_socket_options(context, sockfd);
#ifdef LWS_USE_IPV6
if (LWS_IPV6_ENABLED(context)) {
v = (struct sockaddr *)&serv_addr6;
n = sizeof(struct sockaddr_in6);
bzero((char *) &serv_addr6, sizeof(serv_addr6));
serv_addr6.sin6_addr = in6addr_any;
serv_addr6.sin6_family = AF_INET6;
serv_addr6.sin6_port = htons(info->port);
} else
#endif
{
v = (struct sockaddr *)&serv_addr4;
n = sizeof(serv_addr4);
bzero((char *) &serv_addr4, sizeof(serv_addr4));
serv_addr4.sin_addr.s_addr = INADDR_ANY;
serv_addr4.sin_family = AF_INET;
serv_addr4.sin_port = htons(info->port);
if (info->iface) {
if (interface_to_sa(context, info->iface,
(struct sockaddr_in *)v, n) < 0) {
lwsl_err("Unable to find interface %s\n",
info->iface);
compatible_close(sockfd);
return 1;
}
}
} /* ipv4 */
n = bind(sockfd, v, n);
if (n < 0) {
lwsl_err("ERROR on binding to port %d (%d %d)\n",
info->port, n, LWS_ERRNO);
compatible_close(sockfd);
return 1;
}
if (getsockname(sockfd, (struct sockaddr *)&sin, &len) == -1)
lwsl_warn("getsockname: %s\n", strerror(LWS_ERRNO));
else
info->port = ntohs(sin.sin_port);
context->listen_port = info->port;
wsi = (struct libwebsocket *)malloc(sizeof(struct libwebsocket));
if (wsi == NULL) {
lwsl_err("Out of mem\n");
compatible_close(sockfd);
return 1;
}
memset(wsi, 0, sizeof(struct libwebsocket));
wsi->sock = sockfd;
wsi->mode = LWS_CONNMODE_SERVER_LISTENER;
insert_wsi_socket_into_fds(context, wsi);
context->listen_service_modulo = LWS_LISTEN_SERVICE_MODULO;
context->listen_service_count = 0;
context->listen_service_fd = sockfd;
listen(sockfd, LWS_SOMAXCONN);
lwsl_notice(" Listening on port %d\n", info->port);
return 0;
}
int
_libwebsocket_rx_flow_control(struct libwebsocket *wsi)
{
struct libwebsocket_context *context = wsi->protocol->owning_server;
/* there is no pending change */
if (!(wsi->u.ws.rxflow_change_to & LWS_RXFLOW_PENDING_CHANGE))
return 0;
/* stuff is still buffered, not ready to really accept new input */
if (wsi->u.ws.rxflow_buffer) {
/* get ourselves called back to deal with stashed buffer */
libwebsocket_callback_on_writable(context, wsi);
return 0;
}
/* pending is cleared, we can change rxflow state */
wsi->u.ws.rxflow_change_to &= ~LWS_RXFLOW_PENDING_CHANGE;
lwsl_info("rxflow: wsi %p change_to %d\n", wsi,
wsi->u.ws.rxflow_change_to & LWS_RXFLOW_ALLOW);
/* adjust the pollfd for this wsi */
if (wsi->u.ws.rxflow_change_to & LWS_RXFLOW_ALLOW) {
if (lws_change_pollfd(wsi, 0, LWS_POLLIN)) {
lwsl_info("%s: fail\n", __func__);
return -1;
}
} else
if (lws_change_pollfd(wsi, LWS_POLLIN, 0))
return -1;
return 0;
}
int lws_handshake_server(struct libwebsocket_context *context,
struct libwebsocket *wsi, unsigned char **buf, size_t len)
{
struct allocated_headers *ah;
char *uri_ptr = NULL;
int uri_len = 0;
char content_length_str[32];
int n;
/* LWS_CONNMODE_WS_SERVING */
while (len--) {
if (libwebsocket_parse(wsi, *(*buf)++)) {
lwsl_info("libwebsocket_parse failed\n");
goto bail_nuke_ah;
}
if (wsi->u.hdr.parser_state != WSI_PARSING_COMPLETE)
continue;
lwsl_parser("libwebsocket_parse sees parsing complete\n");
wsi->mode = LWS_CONNMODE_PRE_WS_SERVING_ACCEPT;
libwebsocket_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
/* is this websocket protocol or normal http 1.0? */
if (!lws_hdr_total_length(wsi, WSI_TOKEN_UPGRADE) ||
!lws_hdr_total_length(wsi, WSI_TOKEN_CONNECTION)) {
/* it's not websocket.... shall we accept it as http? */
if (!lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI) &&
!lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)) {
lwsl_warn("Missing URI in HTTP request\n");
goto bail_nuke_ah;
}
if (lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI) &&
lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)) {
lwsl_warn("GET and POST methods?\n");
goto bail_nuke_ah;
}
if (libwebsocket_ensure_user_space(wsi))
goto bail_nuke_ah;
if (lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI)) {
uri_ptr = lws_hdr_simple_ptr(wsi, WSI_TOKEN_GET_URI);
uri_len = lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI);
lwsl_info("HTTP GET request for '%s'\n",
lws_hdr_simple_ptr(wsi, WSI_TOKEN_GET_URI));
}
if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)) {
lwsl_info("HTTP POST request for '%s'\n",
lws_hdr_simple_ptr(wsi, WSI_TOKEN_POST_URI));
uri_ptr = lws_hdr_simple_ptr(wsi, WSI_TOKEN_POST_URI);
uri_len = lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI);
}
/*
* Hm we still need the headers so the
* callback can look at leaders like the URI, but we
* need to transition to http union state.... hold a
* copy of u.hdr.ah and deallocate afterwards
*/
ah = wsi->u.hdr.ah;
/* union transition */
memset(&wsi->u, 0, sizeof(wsi->u));
wsi->mode = LWS_CONNMODE_HTTP_SERVING_ACCEPTED;
wsi->state = WSI_STATE_HTTP;
wsi->u.http.fd = LWS_INVALID_FILE;
/* expose it at the same offset as u.hdr */
wsi->u.http.ah = ah;
/* HTTP header had a content length? */
wsi->u.http.content_length = 0;
if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI))
wsi->u.http.content_length = 100 * 1024 * 1024;
if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) {
lws_hdr_copy(wsi, content_length_str,
sizeof(content_length_str) - 1,
WSI_TOKEN_HTTP_CONTENT_LENGTH);
wsi->u.http.content_length = atoi(content_length_str);
}
if (wsi->u.http.content_length > 0) {
wsi->u.http.body_index = 0;
n = wsi->protocol->rx_buffer_size;
if (!n)
n = LWS_MAX_SOCKET_IO_BUF;
wsi->u.http.post_buffer = malloc(n);
if (!wsi->u.http.post_buffer) {
lwsl_err("Unable to allocate post buffer\n");
n = -1;
goto cleanup;
}
}
n = 0;
if (wsi->protocol->callback)
n = wsi->protocol->callback(context, wsi,
LWS_CALLBACK_FILTER_HTTP_CONNECTION,
wsi->user_space, uri_ptr, uri_len);
if (!n) {
/*
* if there is content supposed to be coming,
* put a timeout on it having arrived
*/
libwebsocket_set_timeout(wsi,
PENDING_TIMEOUT_HTTP_CONTENT,
AWAITING_TIMEOUT);
if (wsi->protocol->callback)
n = wsi->protocol->callback(context, wsi,
LWS_CALLBACK_HTTP,
wsi->user_space, uri_ptr, uri_len);
}
cleanup:
/* now drop the header info we kept a pointer to */
if (ah)
free(ah);
/* not possible to continue to use past here */
wsi->u.http.ah = NULL;
if (n) {
lwsl_info("LWS_CALLBACK_HTTP closing\n");
return 1; /* struct ah ptr already nuked */
}
/*
* (if callback didn't start sending a file)
* deal with anything else as body, whether
* there was a content-length or not
*/
if (wsi->state != WSI_STATE_HTTP_ISSUING_FILE)
wsi->state = WSI_STATE_HTTP_BODY;
return 2; /* goto http_postbody; */
}
if (!wsi->protocol)
lwsl_err("NULL protocol at libwebsocket_read\n");
/*
* It's websocket
*
* Make sure user side is happy about protocol
*/
while (wsi->protocol->callback) {
if (!lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL)) {
if (wsi->protocol->name == NULL)
break;
} else
if (wsi->protocol->name && strcmp(
lws_hdr_simple_ptr(wsi,
WSI_TOKEN_PROTOCOL),
wsi->protocol->name) == 0)
break;
wsi->protocol++;
}
/* we didn't find a protocol he wanted? */
if (wsi->protocol->callback == NULL) {
if (lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL) ==
NULL) {
lwsl_info("no protocol -> prot 0 handler\n");
wsi->protocol = &context->protocols[0];
} else {
lwsl_err("Req protocol %s not supported\n",
lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL));
goto bail_nuke_ah;
}
}
/* allocate wsi->user storage */
if (libwebsocket_ensure_user_space(wsi))
goto bail_nuke_ah;
/*
* Give the user code a chance to study the request and
* have the opportunity to deny it
*/
if ((wsi->protocol->callback)(wsi->protocol->owning_server, wsi,
LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION,
wsi->user_space,
lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL), 0)) {
lwsl_warn("User code denied connection\n");
goto bail_nuke_ah;
}
/*
* Perform the handshake according to the protocol version the
* client announced
*/
switch (wsi->ietf_spec_revision) {
case 13:
lwsl_parser("lws_parse calling handshake_04\n");
if (handshake_0405(context, wsi)) {
lwsl_info("hs0405 has failed the connection\n");
goto bail_nuke_ah;
}
break;
default:
lwsl_warn("Unknown client spec version %d\n",
wsi->ietf_spec_revision);
goto bail_nuke_ah;
}
/* drop the header info -- no bail_nuke_ah after this */
if (wsi->u.hdr.ah)
free(wsi->u.hdr.ah);
wsi->mode = LWS_CONNMODE_WS_SERVING;
/* union transition */
memset(&wsi->u, 0, sizeof(wsi->u));
wsi->u.ws.rxflow_change_to = LWS_RXFLOW_ALLOW;
/*
* create the frame buffer for this connection according to the
* size mentioned in the protocol definition. If 0 there, use
* a big default for compatibility
*/
n = wsi->protocol->rx_buffer_size;
if (!n)
n = LWS_MAX_SOCKET_IO_BUF;
n += LWS_SEND_BUFFER_PRE_PADDING + LWS_SEND_BUFFER_POST_PADDING;
wsi->u.ws.rx_user_buffer = malloc(n);
if (!wsi->u.ws.rx_user_buffer) {
lwsl_err("Out of Mem allocating rx buffer %d\n", n);
return 1;
}
lwsl_info("Allocating RX buffer %d\n", n);
if (setsockopt(wsi->sock, SOL_SOCKET, SO_SNDBUF, (const char *)&n, sizeof n)) {
lwsl_warn("Failed to set SNDBUF to %d", n);
return 1;
}
lwsl_parser("accepted v%02d connection\n",
wsi->ietf_spec_revision);
} /* while all chars are handled */
return 0;
bail_nuke_ah:
/* drop the header info */
if (wsi->u.hdr.ah)
free(wsi->u.hdr.ah);
return 1;
}
struct libwebsocket *
libwebsocket_create_new_server_wsi(struct libwebsocket_context *context)
{
struct libwebsocket *new_wsi;
new_wsi = (struct libwebsocket *)malloc(sizeof(struct libwebsocket));
if (new_wsi == NULL) {
lwsl_err("Out of memory for new connection\n");
return NULL;
}
memset(new_wsi, 0, sizeof(struct libwebsocket));
new_wsi->pending_timeout = NO_PENDING_TIMEOUT;
/* intialize the instance struct */
new_wsi->state = WSI_STATE_HTTP;
new_wsi->mode = LWS_CONNMODE_HTTP_SERVING;
new_wsi->hdr_parsing_completed = 0;
if (lws_allocate_header_table(new_wsi)) {
free(new_wsi);
return NULL;
}
/*
* these can only be set once the protocol is known
* we set an unestablished connection's protocol pointer
* to the start of the supported list, so it can look
* for matching ones during the handshake
*/
new_wsi->protocol = context->protocols;
new_wsi->user_space = NULL;
new_wsi->ietf_spec_revision = 0;
/*
* outermost create notification for wsi
* no user_space because no protocol selection
*/
context->protocols[0].callback(context, new_wsi,
LWS_CALLBACK_WSI_CREATE, NULL, NULL, 0);
return new_wsi;
}
int lws_server_socket_service(struct libwebsocket_context *context,
struct libwebsocket *wsi, struct libwebsocket_pollfd *pollfd)
{
struct libwebsocket *new_wsi = NULL;
int accept_fd = 0;
socklen_t clilen;
struct sockaddr_in cli_addr;
int n;
int len;
switch (wsi->mode) {
case LWS_CONNMODE_HTTP_SERVING:
case LWS_CONNMODE_HTTP_SERVING_ACCEPTED:
/* handle http headers coming in */
/* pending truncated sends have uber priority */
if (wsi->truncated_send_malloc) {
if (pollfd->revents & LWS_POLLOUT)
if (lws_issue_raw(wsi, wsi->truncated_send_malloc +
wsi->truncated_send_offset,
wsi->truncated_send_len) < 0) {
lwsl_info("closing from socket service\n");
return -1;
}
/*
* we can't afford to allow input processing send
* something new, so spin around he event loop until
* he doesn't have any partials
*/
break;
}
/* any incoming data ready? */
if (pollfd->revents & LWS_POLLIN) {
len = lws_ssl_capable_read(wsi,
context->service_buffer,
sizeof(context->service_buffer));
switch (len) {
case 0:
lwsl_info("lws_server_skt_srv: read 0 len\n");
/* lwsl_info(" state=%d\n", wsi->state); */
if (!wsi->hdr_parsing_completed)
free(wsi->u.hdr.ah);
/* fallthru */
case LWS_SSL_CAPABLE_ERROR:
libwebsocket_close_and_free_session(
context, wsi,
LWS_CLOSE_STATUS_NOSTATUS);
return 0;
case LWS_SSL_CAPABLE_MORE_SERVICE:
break;
}
/* just ignore incoming if waiting for close */
if (wsi->state != WSI_STATE_FLUSHING_STORED_SEND_BEFORE_CLOSE) {
/* hm this may want to send (via HTTP callback for example) */
n = libwebsocket_read(context, wsi,
context->service_buffer, len);
if (n < 0)
/* we closed wsi */
return 0;
/* hum he may have used up the writability above */
break;
}
}
/* this handles POLLOUT for http serving fragments */
if (!(pollfd->revents & LWS_POLLOUT))
break;
/* one shot */
if (lws_change_pollfd(wsi, LWS_POLLOUT, 0))
goto fail;
lws_libev_io(context, wsi, LWS_EV_STOP | LWS_EV_WRITE);
if (wsi->state != WSI_STATE_HTTP_ISSUING_FILE) {
n = user_callback_handle_rxflow(
wsi->protocol->callback,
wsi->protocol->owning_server,
wsi, LWS_CALLBACK_HTTP_WRITEABLE,
wsi->user_space,
NULL,
0);
if (n < 0)
libwebsocket_close_and_free_session(
context, wsi, LWS_CLOSE_STATUS_NOSTATUS);
break;
}
/* nonzero for completion or error */
if (libwebsockets_serve_http_file_fragment(context, wsi))
libwebsocket_close_and_free_session(context, wsi,
LWS_CLOSE_STATUS_NOSTATUS);
break;
case LWS_CONNMODE_SERVER_LISTENER:
/* pollin means a client has connected to us then */
if (!(pollfd->revents & LWS_POLLIN))
break;
/* listen socket got an unencrypted connection... */
clilen = sizeof(cli_addr);
lws_latency_pre(context, wsi);
accept_fd = accept(pollfd->fd, (struct sockaddr *)&cli_addr,
&clilen);
lws_latency(context, wsi,
"unencrypted accept LWS_CONNMODE_SERVER_LISTENER",
accept_fd, accept_fd >= 0);
if (accept_fd < 0) {
if (LWS_ERRNO == LWS_EAGAIN || LWS_ERRNO == LWS_EWOULDBLOCK) {
lwsl_debug("accept asks to try again\n");
break;
}
lwsl_warn("ERROR on accept: %s\n", strerror(LWS_ERRNO));
break;
}
lws_plat_set_socket_options(context, accept_fd);
/*
* look at who we connected to and give user code a chance
* to reject based on client IP. There's no protocol selected
* yet so we issue this to protocols[0]
*/
if ((context->protocols[0].callback)(context, wsi,
LWS_CALLBACK_FILTER_NETWORK_CONNECTION,
NULL, (void *)(long)accept_fd, 0)) {
lwsl_debug("Callback denied network connection\n");
compatible_close(accept_fd);
break;
}
new_wsi = libwebsocket_create_new_server_wsi(context);
if (new_wsi == NULL) {
compatible_close(accept_fd);
break;
}
new_wsi->sock = accept_fd;
/* the transport is accepted... give him time to negotiate */
libwebsocket_set_timeout(new_wsi,
PENDING_TIMEOUT_ESTABLISH_WITH_SERVER,
AWAITING_TIMEOUT);
/*
* A new connection was accepted. Give the user a chance to
* set properties of the newly created wsi. There's no protocol
* selected yet so we issue this to protocols[0]
*/
(context->protocols[0].callback)(context, new_wsi,
LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED, NULL, NULL, 0);
lws_libev_accept(context, new_wsi, accept_fd);
if (!LWS_SSL_ENABLED(context)) {
lwsl_debug("accepted new conn port %u on fd=%d\n",
ntohs(cli_addr.sin_port), accept_fd);
insert_wsi_socket_into_fds(context, new_wsi);
}
break;
default:
break;
}
if (lws_server_socket_service_ssl(context, &wsi, new_wsi, accept_fd, pollfd))
goto fail;
return 0;
fail:
libwebsocket_close_and_free_session(context, wsi,
LWS_CLOSE_STATUS_NOSTATUS);
return 1;
}
static const char *err400[] = {
"Bad Request",
"Unauthorized",
"Payment Required",
"Forbidden",
"Not Found",
"Method Not Allowed",
"Not Acceptable",
"Proxy Auth Required",
"Request Timeout",
"Conflict",
"Gone",
"Length Required",
"Precondition Failed",
"Request Entity Too Large",
"Request URI too Long",
"Unsupported Media Type",
"Requested Range Not Satisfiable",
"Expectation Failed"
};
static const char *err500[] = {
"Internal Server Error",
"Not Implemented",
"Bad Gateway",
"Service Unavailable",
"Gateway Timeout",
"HTTP Version Not Supported"
};
/**
* libwebsockets_return_http_status() - Return simple http status
* @context: libwebsockets context
* @wsi: Websocket instance (available from user callback)
* @code: Status index, eg, 404
* @html_body: User-readable HTML description, or NULL
*
* Helper to report HTTP errors back to the client cleanly and
* consistently
*/
LWS_VISIBLE int libwebsockets_return_http_status(
struct libwebsocket_context *context, struct libwebsocket *wsi,
unsigned int code, const char *html_body)
{
int n, m;
const char *description = "";
if (!html_body)
html_body = "";
if (code >= 400 && code < (400 + ARRAY_SIZE(err400)))
description = err400[code - 400];
if (code >= 500 && code < (500 + ARRAY_SIZE(err500)))
description = err500[code - 500];
n = sprintf((char *)context->service_buffer,
"HTTP/1.0 %u %s\x0d\x0a"
"Server: libwebsockets\x0d\x0a"
"Content-Type: text/html\x0d\x0a\x0d\x0a"
"<h1>%u %s</h1>%s",
code, description, code, description, html_body);
lwsl_info((const char *)context->service_buffer);
m = libwebsocket_write(wsi, context->service_buffer, n, LWS_WRITE_HTTP);
return m;
}
/**
* libwebsockets_serve_http_file() - Send a file back to the client using http
* @context: libwebsockets context
* @wsi: Websocket instance (available from user callback)
* @file: The file to issue over http
* @content_type: The http content type, eg, text/html
* @other_headers: NULL or pointer to \0-terminated other header string
*
* This function is intended to be called from the callback in response
* to http requests from the client. It allows the callback to issue
* local files down the http link in a single step.
*
* Returning <0 indicates error and the wsi should be closed. Returning
* >0 indicates the file was completely sent and the wsi should be closed.
* ==0 indicates the file transfer is started and needs more service later,
* the wsi should be left alone.
*/
LWS_VISIBLE int libwebsockets_serve_http_file(
struct libwebsocket_context *context,
struct libwebsocket *wsi, const char *file,
const char *content_type, const char *other_headers)
{
unsigned char *p = context->service_buffer;
int ret = 0;
int n;
wsi->u.http.fd = lws_plat_open_file(file, &wsi->u.http.filelen);
if (wsi->u.http.fd == LWS_INVALID_FILE) {
lwsl_err("Unable to open '%s'\n", file);
libwebsockets_return_http_status(context, wsi,
HTTP_STATUS_NOT_FOUND, NULL);
return -1;
}
p += sprintf((char *)p,
"HTTP/1.0 200 OK\x0d\x0aServer: libwebsockets\x0d\x0a""Content-Type: %s\x0d\x0a",
content_type);
if (other_headers) {
n = strlen(other_headers);
memcpy(p, other_headers, n);
p += n;
}
p += sprintf((char *)p,
"Content-Length: %lu\x0d\x0a\x0d\x0a", wsi->u.http.filelen);
ret = libwebsocket_write(wsi, context->service_buffer,
p - context->service_buffer, LWS_WRITE_HTTP);
if (ret != (p - context->service_buffer)) {
lwsl_err("_write returned %d from %d\n", ret, (p - context->service_buffer));
return -1;
}
wsi->u.http.filepos = 0;
wsi->state = WSI_STATE_HTTP_ISSUING_FILE;
return libwebsockets_serve_http_file_fragment(context, wsi);
}
int libwebsocket_interpret_incoming_packet(struct libwebsocket *wsi,
unsigned char *buf, size_t len)
{
size_t n = 0;
int m;
#if 0
lwsl_parser("received %d byte packet\n", (int)len);
lwsl_hexdump(buf, len);
#endif
/* let the rx protocol state machine have as much as it needs */
while (n < len) {
/*
* we were accepting input but now we stopped doing so
*/
if (!(wsi->u.ws.rxflow_change_to & LWS_RXFLOW_ALLOW)) {
/* his RX is flowcontrolled, don't send remaining now */
if (!wsi->u.ws.rxflow_buffer) {
/* a new rxflow, buffer it and warn caller */
lwsl_info("new rxflow input buffer len %d\n",
len - n);
wsi->u.ws.rxflow_buffer =
(unsigned char *)malloc(len - n);
wsi->u.ws.rxflow_len = len - n;
wsi->u.ws.rxflow_pos = 0;
memcpy(wsi->u.ws.rxflow_buffer,
buf + n, len - n);
} else
/* rxflow while we were spilling prev rxflow */
lwsl_info("stalling in existing rxflow buf\n");
return 1;
}
/* account for what we're using in rxflow buffer */
if (wsi->u.ws.rxflow_buffer)
wsi->u.ws.rxflow_pos++;
/* process the byte */
m = libwebsocket_rx_sm(wsi, buf[n++]);
if (m < 0)
return -1;
}
return 0;
}
LWS_VISIBLE void
lws_server_get_canonical_hostname(struct libwebsocket_context *context,
struct lws_context_creation_info *info)
{
if (info->options & LWS_SERVER_OPTION_SKIP_SERVER_CANONICAL_NAME)
return;
/* find canonical hostname */
gethostname((char *)context->canonical_hostname,
sizeof(context->canonical_hostname) - 1);
lwsl_notice(" canonical_hostname = %s\n", context->canonical_hostname);
}

View File

@ -0,0 +1,514 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2014 Andy Green <andy@warmcat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation:
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include "private-libwebsockets.h"
int
lws_handle_POLLOUT_event(struct libwebsocket_context *context,
struct libwebsocket *wsi, struct libwebsocket_pollfd *pollfd)
{
int n;
struct lws_tokens eff_buf;
int ret;
int m;
int handled = 0;
/* pending truncated sends have uber priority */
if (wsi->truncated_send_len) {
if (lws_issue_raw(wsi, wsi->truncated_send_malloc +
wsi->truncated_send_offset,
wsi->truncated_send_len) < 0) {
lwsl_info("lws_handle_POLLOUT_event signalling to close\n");
return -1;
}
/* leave POLLOUT active either way */
return 0;
} else
if (wsi->state == WSI_STATE_FLUSHING_STORED_SEND_BEFORE_CLOSE) {
lwsl_info("***** %x signalling to close in POLLOUT handler\n", wsi);
return -1; /* retry closing now */
}
m = lws_ext_callback_for_each_active(wsi, LWS_EXT_CALLBACK_IS_WRITEABLE,
NULL, 0);
if (handled == 1)
goto notify_action;
#ifndef LWS_NO_EXTENSIONS
if (!wsi->extension_data_pending || handled == 2)
goto user_service;
#endif
/*
* check in on the active extensions, see if they
* had pending stuff to spill... they need to get the
* first look-in otherwise sequence will be disordered
*
* NULL, zero-length eff_buf means just spill pending
*/
ret = 1;
while (ret == 1) {
/* default to nobody has more to spill */
ret = 0;
eff_buf.token = NULL;
eff_buf.token_len = 0;
/* give every extension a chance to spill */
m = lws_ext_callback_for_each_active(wsi,
LWS_EXT_CALLBACK_PACKET_TX_PRESEND,
&eff_buf, 0);
if (m < 0) {
lwsl_err("ext reports fatal error\n");
return -1;
}
if (m)
/*
* at least one extension told us he has more
* to spill, so we will go around again after
*/
ret = 1;
/* assuming they gave us something to send, send it */
if (eff_buf.token_len) {
n = lws_issue_raw(wsi, (unsigned char *)eff_buf.token,
eff_buf.token_len);
if (n < 0) {
lwsl_info("closing from POLLOUT spill\n");
return -1;
}
/*
* Keep amount spilled small to minimize chance of this
*/
if (n != eff_buf.token_len) {
lwsl_err("Unable to spill ext %d vs %s\n",
eff_buf.token_len, n);
return -1;
}
} else
continue;
/* no extension has more to spill */
if (!ret)
continue;
/*
* There's more to spill from an extension, but we just sent
* something... did that leave the pipe choked?
*/
if (!lws_send_pipe_choked(wsi))
/* no we could add more */
continue;
lwsl_info("choked in POLLOUT service\n");
/*
* Yes, he's choked. Leave the POLLOUT masked on so we will
* come back here when he is unchoked. Don't call the user
* callback to enforce ordering of spilling, he'll get called
* when we come back here and there's nothing more to spill.
*/
return 0;
}
#ifndef LWS_NO_EXTENSIONS
wsi->extension_data_pending = 0;
user_service:
#endif
/* one shot */
if (pollfd) {
if (lws_change_pollfd(wsi, LWS_POLLOUT, 0))
return 1;
lws_libev_io(context, wsi, LWS_EV_STOP | LWS_EV_WRITE);
}
notify_action:
if (wsi->mode == LWS_CONNMODE_WS_CLIENT)
n = LWS_CALLBACK_CLIENT_WRITEABLE;
else
n = LWS_CALLBACK_SERVER_WRITEABLE;
return user_callback_handle_rxflow(wsi->protocol->callback, context,
wsi, (enum libwebsocket_callback_reasons) n,
wsi->user_space, NULL, 0);
}
int
libwebsocket_service_timeout_check(struct libwebsocket_context *context,
struct libwebsocket *wsi, unsigned int sec)
{
/*
* if extensions want in on it (eg, we are a mux parent)
* give them a chance to service child timeouts
*/
if (lws_ext_callback_for_each_active(wsi, LWS_EXT_CALLBACK_1HZ, NULL, sec) < 0)
return 0;
if (!wsi->pending_timeout)
return 0;
/*
* if we went beyond the allowed time, kill the
* connection
*/
if (sec > wsi->pending_timeout_limit) {
lwsl_info("TIMEDOUT WAITING on %d\n", wsi->pending_timeout);
libwebsocket_close_and_free_session(context,
wsi, LWS_CLOSE_STATUS_NOSTATUS);
return 1;
}
return 0;
}
/**
* libwebsocket_service_fd() - Service polled socket with something waiting
* @context: Websocket context
* @pollfd: The pollfd entry describing the socket fd and which events
* happened.
*
* This function takes a pollfd that has POLLIN or POLLOUT activity and
* services it according to the state of the associated
* struct libwebsocket.
*
* The one call deals with all "service" that might happen on a socket
* including listen accepts, http files as well as websocket protocol.
*
* If a pollfd says it has something, you can just pass it to
* libwebsocket_serice_fd() whether it is a socket handled by lws or not.
* If it sees it is a lws socket, the traffic will be handled and
* pollfd->revents will be zeroed now.
*
* If the socket is foreign to lws, it leaves revents alone. So you can
* see if you should service yourself by checking the pollfd revents
* after letting lws try to service it.
*/
LWS_VISIBLE int
libwebsocket_service_fd(struct libwebsocket_context *context,
struct libwebsocket_pollfd *pollfd)
{
struct libwebsocket *wsi;
int n;
int m;
int listen_socket_fds_index = 0;
time_t now;
int timed_out = 0;
int our_fd = 0;
char draining_flow = 0;
int more;
struct lws_tokens eff_buf;
if (context->listen_service_fd)
listen_socket_fds_index = context->lws_lookup[
context->listen_service_fd]->position_in_fds_table;
/*
* you can call us with pollfd = NULL to just allow the once-per-second
* global timeout checks; if less than a second since the last check
* it returns immediately then.
*/
time(&now);
/* TODO: if using libev, we should probably use timeout watchers... */
if (context->last_timeout_check_s != now) {
context->last_timeout_check_s = now;
lws_plat_service_periodic(context);
/* global timeout check once per second */
if (pollfd)
our_fd = pollfd->fd;
for (n = 0; n < context->fds_count; n++) {
m = context->fds[n].fd;
wsi = context->lws_lookup[m];
if (!wsi)
continue;
if (libwebsocket_service_timeout_check(context, wsi, now))
/* he did time out... */
if (m == our_fd) {
/* it was the guy we came to service! */
timed_out = 1;
/* mark as handled */
pollfd->revents = 0;
}
}
}
/* the socket we came to service timed out, nothing to do */
if (timed_out)
return 0;
/* just here for timeout management? */
if (pollfd == NULL)
return 0;
/* no, here to service a socket descriptor */
wsi = context->lws_lookup[pollfd->fd];
if (wsi == NULL)
/* not lws connection ... leave revents alone and return */
return 0;
/*
* so that caller can tell we handled, past here we need to
* zero down pollfd->revents after handling
*/
/*
* deal with listen service piggybacking
* every listen_service_modulo services of other fds, we
* sneak one in to service the listen socket if there's anything waiting
*
* To handle connection storms, as found in ab, if we previously saw a
* pending connection here, it causes us to check again next time.
*/
if (context->listen_service_fd && pollfd !=
&context->fds[listen_socket_fds_index]) {
context->listen_service_count++;
if (context->listen_service_extraseen ||
context->listen_service_count ==
context->listen_service_modulo) {
context->listen_service_count = 0;
m = 1;
if (context->listen_service_extraseen > 5)
m = 2;
while (m--) {
/*
* even with extpoll, we prepared this
* internal fds for listen
*/
n = lws_poll_listen_fd(&context->fds[listen_socket_fds_index]);
if (n > 0) { /* there's a conn waiting for us */
libwebsocket_service_fd(context,
&context->
fds[listen_socket_fds_index]);
context->listen_service_extraseen++;
} else {
if (context->listen_service_extraseen)
context->
listen_service_extraseen--;
break;
}
}
}
}
/* handle session socket closed */
if ((!(pollfd->revents & LWS_POLLIN)) &&
(pollfd->revents & LWS_POLLHUP)) {
lwsl_debug("Session Socket %p (fd=%d) dead\n",
(void *)wsi, pollfd->fd);
goto close_and_handled;
}
/* okay, what we came here to do... */
switch (wsi->mode) {
case LWS_CONNMODE_HTTP_SERVING:
case LWS_CONNMODE_HTTP_SERVING_ACCEPTED:
case LWS_CONNMODE_SERVER_LISTENER:
case LWS_CONNMODE_SSL_ACK_PENDING:
n = lws_server_socket_service(context, wsi, pollfd);
if (n < 0)
goto close_and_handled;
goto handled;
case LWS_CONNMODE_WS_SERVING:
case LWS_CONNMODE_WS_CLIENT:
/* the guy requested a callback when it was OK to write */
if ((pollfd->revents & LWS_POLLOUT) &&
(wsi->state == WSI_STATE_ESTABLISHED ||
wsi->state == WSI_STATE_FLUSHING_STORED_SEND_BEFORE_CLOSE) &&
lws_handle_POLLOUT_event(context, wsi, pollfd)) {
lwsl_info("libwebsocket_service_fd: closing\n");
goto close_and_handled;
}
if (wsi->u.ws.rxflow_buffer &&
(wsi->u.ws.rxflow_change_to & LWS_RXFLOW_ALLOW)) {
lwsl_info("draining rxflow\n");
/* well, drain it */
eff_buf.token = (char *)wsi->u.ws.rxflow_buffer +
wsi->u.ws.rxflow_pos;
eff_buf.token_len = wsi->u.ws.rxflow_len -
wsi->u.ws.rxflow_pos;
draining_flow = 1;
goto drain;
}
/* any incoming data ready? */
if (!(pollfd->revents & LWS_POLLIN))
break;
read_pending:
eff_buf.token_len = lws_ssl_capable_read(wsi,
context->service_buffer,
sizeof(context->service_buffer));
switch (eff_buf.token_len) {
case 0:
lwsl_info("service_fd: closing due to 0 length read\n");
goto close_and_handled;
case LWS_SSL_CAPABLE_ERROR:
n = 0;
goto handled;
case LWS_SSL_CAPABLE_MORE_SERVICE:
goto close_and_handled;
}
/*
* give any active extensions a chance to munge the buffer
* before parse. We pass in a pointer to an lws_tokens struct
* prepared with the default buffer and content length that's in
* there. Rather than rewrite the default buffer, extensions
* that expect to grow the buffer can adapt .token to
* point to their own per-connection buffer in the extension
* user allocation. By default with no extensions or no
* extension callback handling, just the normal input buffer is
* used then so it is efficient.
*/
eff_buf.token = (char *)context->service_buffer;
drain:
do {
more = 0;
m = lws_ext_callback_for_each_active(wsi,
LWS_EXT_CALLBACK_PACKET_RX_PREPARSE, &eff_buf, 0);
if (m < 0)
goto close_and_handled;
if (m)
more = 1;
/* service incoming data */
if (eff_buf.token_len) {
n = libwebsocket_read(context, wsi,
(unsigned char *)eff_buf.token,
eff_buf.token_len);
if (n < 0) {
/* we closed wsi */
n = 0;
goto handled;
}
}
eff_buf.token = NULL;
eff_buf.token_len = 0;
} while (more);
if (draining_flow && wsi->u.ws.rxflow_buffer &&
wsi->u.ws.rxflow_pos == wsi->u.ws.rxflow_len) {
lwsl_info("flow buffer: drained\n");
free(wsi->u.ws.rxflow_buffer);
wsi->u.ws.rxflow_buffer = NULL;
/* having drained the rxflow buffer, can rearm POLLIN */
n = _libwebsocket_rx_flow_control(wsi); /* n ignored, needed for NO_SERVER case */
}
if (lws_ssl_pending(wsi))
goto read_pending;
break;
default:
#ifdef LWS_NO_CLIENT
break;
#else
n = lws_client_socket_service(context, wsi, pollfd);
goto handled;
#endif
}
n = 0;
goto handled;
close_and_handled:
libwebsocket_close_and_free_session(context, wsi,
LWS_CLOSE_STATUS_NOSTATUS);
n = 1;
handled:
pollfd->revents = 0;
return n;
}
/**
* libwebsocket_service() - Service any pending websocket activity
* @context: Websocket context
* @timeout_ms: Timeout for poll; 0 means return immediately if nothing needed
* service otherwise block and service immediately, returning
* after the timeout if nothing needed service.
*
* This function deals with any pending websocket traffic, for three
* kinds of event. It handles these events on both server and client
* types of connection the same.
*
* 1) Accept new connections to our context's server
*
* 2) Call the receive callback for incoming frame data received by
* server or client connections.
*
* You need to call this service function periodically to all the above
* functions to happen; if your application is single-threaded you can
* just call it in your main event loop.
*
* Alternatively you can fork a new process that asynchronously handles
* calling this service in a loop. In that case you are happy if this
* call blocks your thread until it needs to take care of something and
* would call it with a large nonzero timeout. Your loop then takes no
* CPU while there is nothing happening.
*
* If you are calling it in a single-threaded app, you don't want it to
* wait around blocking other things in your loop from happening, so you
* would call it with a timeout_ms of 0, so it returns immediately if
* nothing is pending, or as soon as it services whatever was pending.
*/
LWS_VISIBLE int
libwebsocket_service(struct libwebsocket_context *context, int timeout_ms)
{
return lws_plat_service(context, timeout_ms);
}

View File

@ -0,0 +1,301 @@
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* FIPS pub 180-1: Secure Hash Algorithm (SHA-1)
* based on: http://csrc.nist.gov/fips/fip180-1.txt
* implemented by Jun-ichiro itojun Itoh <itojun@itojun.org>
*/
#include "private-libwebsockets.h"
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
struct sha1_ctxt {
union {
unsigned char b8[20];
unsigned int b32[5];
} h;
union {
unsigned char b8[8];
u_int64_t b64[1];
} c;
union {
unsigned char b8[64];
unsigned int b32[16];
} m;
unsigned char count;
};
/* sanity check */
#if !defined(BYTE_ORDER) || !defined(LITTLE_ENDIAN) || !defined(BIG_ENDIAN)
# define unsupported 1
#elif BYTE_ORDER != BIG_ENDIAN
# if BYTE_ORDER != LITTLE_ENDIAN
# define unsupported 1
# endif
#endif
#ifndef unsupported
/* constant table */
static const unsigned int _K[] =
{ 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6 };
#define K(t) _K[(t) / 20]
#define F0(b, c, d) (((b) & (c)) | ((~(b)) & (d)))
#define F1(b, c, d) (((b) ^ (c)) ^ (d))
#define F2(b, c, d) (((b) & (c)) | ((b) & (d)) | ((c) & (d)))
#define F3(b, c, d) (((b) ^ (c)) ^ (d))
#define S(n, x) (((x) << (n)) | ((x) >> (32 - n)))
#define H(n) (ctxt->h.b32[(n)])
#define COUNT (ctxt->count)
#define BCOUNT (ctxt->c.b64[0] / 8)
#define W(n) (ctxt->m.b32[(n)])
#define PUTBYTE(x) { \
ctxt->m.b8[(COUNT % 64)] = (x); \
COUNT++; \
COUNT %= 64; \
ctxt->c.b64[0] += 8; \
if (COUNT % 64 == 0) \
sha1_step(ctxt); \
}
#define PUTPAD(x) { \
ctxt->m.b8[(COUNT % 64)] = (x); \
COUNT++; \
COUNT %= 64; \
if (COUNT % 64 == 0) \
sha1_step(ctxt); \
}
static void sha1_step __P((struct sha1_ctxt *));
static void
sha1_step(struct sha1_ctxt *ctxt)
{
unsigned int a, b, c, d, e, tmp;
size_t t, s;
#if BYTE_ORDER == LITTLE_ENDIAN
struct sha1_ctxt tctxt;
memcpy(&tctxt.m.b8[0], &ctxt->m.b8[0], 64);
ctxt->m.b8[0] = tctxt.m.b8[3]; ctxt->m.b8[1] = tctxt.m.b8[2];
ctxt->m.b8[2] = tctxt.m.b8[1]; ctxt->m.b8[3] = tctxt.m.b8[0];
ctxt->m.b8[4] = tctxt.m.b8[7]; ctxt->m.b8[5] = tctxt.m.b8[6];
ctxt->m.b8[6] = tctxt.m.b8[5]; ctxt->m.b8[7] = tctxt.m.b8[4];
ctxt->m.b8[8] = tctxt.m.b8[11]; ctxt->m.b8[9] = tctxt.m.b8[10];
ctxt->m.b8[10] = tctxt.m.b8[9]; ctxt->m.b8[11] = tctxt.m.b8[8];
ctxt->m.b8[12] = tctxt.m.b8[15]; ctxt->m.b8[13] = tctxt.m.b8[14];
ctxt->m.b8[14] = tctxt.m.b8[13]; ctxt->m.b8[15] = tctxt.m.b8[12];
ctxt->m.b8[16] = tctxt.m.b8[19]; ctxt->m.b8[17] = tctxt.m.b8[18];
ctxt->m.b8[18] = tctxt.m.b8[17]; ctxt->m.b8[19] = tctxt.m.b8[16];
ctxt->m.b8[20] = tctxt.m.b8[23]; ctxt->m.b8[21] = tctxt.m.b8[22];
ctxt->m.b8[22] = tctxt.m.b8[21]; ctxt->m.b8[23] = tctxt.m.b8[20];
ctxt->m.b8[24] = tctxt.m.b8[27]; ctxt->m.b8[25] = tctxt.m.b8[26];
ctxt->m.b8[26] = tctxt.m.b8[25]; ctxt->m.b8[27] = tctxt.m.b8[24];
ctxt->m.b8[28] = tctxt.m.b8[31]; ctxt->m.b8[29] = tctxt.m.b8[30];
ctxt->m.b8[30] = tctxt.m.b8[29]; ctxt->m.b8[31] = tctxt.m.b8[28];
ctxt->m.b8[32] = tctxt.m.b8[35]; ctxt->m.b8[33] = tctxt.m.b8[34];
ctxt->m.b8[34] = tctxt.m.b8[33]; ctxt->m.b8[35] = tctxt.m.b8[32];
ctxt->m.b8[36] = tctxt.m.b8[39]; ctxt->m.b8[37] = tctxt.m.b8[38];
ctxt->m.b8[38] = tctxt.m.b8[37]; ctxt->m.b8[39] = tctxt.m.b8[36];
ctxt->m.b8[40] = tctxt.m.b8[43]; ctxt->m.b8[41] = tctxt.m.b8[42];
ctxt->m.b8[42] = tctxt.m.b8[41]; ctxt->m.b8[43] = tctxt.m.b8[40];
ctxt->m.b8[44] = tctxt.m.b8[47]; ctxt->m.b8[45] = tctxt.m.b8[46];
ctxt->m.b8[46] = tctxt.m.b8[45]; ctxt->m.b8[47] = tctxt.m.b8[44];
ctxt->m.b8[48] = tctxt.m.b8[51]; ctxt->m.b8[49] = tctxt.m.b8[50];
ctxt->m.b8[50] = tctxt.m.b8[49]; ctxt->m.b8[51] = tctxt.m.b8[48];
ctxt->m.b8[52] = tctxt.m.b8[55]; ctxt->m.b8[53] = tctxt.m.b8[54];
ctxt->m.b8[54] = tctxt.m.b8[53]; ctxt->m.b8[55] = tctxt.m.b8[52];
ctxt->m.b8[56] = tctxt.m.b8[59]; ctxt->m.b8[57] = tctxt.m.b8[58];
ctxt->m.b8[58] = tctxt.m.b8[57]; ctxt->m.b8[59] = tctxt.m.b8[56];
ctxt->m.b8[60] = tctxt.m.b8[63]; ctxt->m.b8[61] = tctxt.m.b8[62];
ctxt->m.b8[62] = tctxt.m.b8[61]; ctxt->m.b8[63] = tctxt.m.b8[60];
#endif
a = H(0); b = H(1); c = H(2); d = H(3); e = H(4);
for (t = 0; t < 20; t++) {
s = t & 0x0f;
if (t >= 16)
W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^
W((s+2) & 0x0f) ^ W(s));
tmp = S(5, a) + F0(b, c, d) + e + W(s) + K(t);
e = d; d = c; c = S(30, b); b = a; a = tmp;
}
for (t = 20; t < 40; t++) {
s = t & 0x0f;
W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^
W((s+2) & 0x0f) ^ W(s));
tmp = S(5, a) + F1(b, c, d) + e + W(s) + K(t);
e = d; d = c; c = S(30, b); b = a; a = tmp;
}
for (t = 40; t < 60; t++) {
s = t & 0x0f;
W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^
W((s+2) & 0x0f) ^ W(s));
tmp = S(5, a) + F2(b, c, d) + e + W(s) + K(t);
e = d; d = c; c = S(30, b); b = a; a = tmp;
}
for (t = 60; t < 80; t++) {
s = t & 0x0f;
W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^
W((s+2) & 0x0f) ^ W(s));
tmp = S(5, a) + F3(b, c, d) + e + W(s) + K(t);
e = d; d = c; c = S(30, b); b = a; a = tmp;
}
H(0) = H(0) + a;
H(1) = H(1) + b;
H(2) = H(2) + c;
H(3) = H(3) + d;
H(4) = H(4) + e;
bzero(&ctxt->m.b8[0], 64);
}
/*------------------------------------------------------------*/
static void
sha1_init(struct sha1_ctxt *ctxt)
{
bzero(ctxt, sizeof(struct sha1_ctxt));
H(0) = 0x67452301;
H(1) = 0xefcdab89;
H(2) = 0x98badcfe;
H(3) = 0x10325476;
H(4) = 0xc3d2e1f0;
}
void
sha1_pad(struct sha1_ctxt *ctxt)
{
size_t padlen; /*pad length in bytes*/
size_t padstart;
PUTPAD(0x80);
padstart = COUNT % 64;
padlen = 64 - padstart;
if (padlen < 8) {
bzero(&ctxt->m.b8[padstart], padlen);
COUNT += padlen;
COUNT %= 64;
sha1_step(ctxt);
padstart = COUNT % 64; /* should be 0 */
padlen = 64 - padstart; /* should be 64 */
}
bzero(&ctxt->m.b8[padstart], padlen - 8);
COUNT += (padlen - 8);
COUNT %= 64;
#if BYTE_ORDER == BIG_ENDIAN
PUTPAD(ctxt->c.b8[0]); PUTPAD(ctxt->c.b8[1]);
PUTPAD(ctxt->c.b8[2]); PUTPAD(ctxt->c.b8[3]);
PUTPAD(ctxt->c.b8[4]); PUTPAD(ctxt->c.b8[5]);
PUTPAD(ctxt->c.b8[6]); PUTPAD(ctxt->c.b8[7]);
#else
PUTPAD(ctxt->c.b8[7]); PUTPAD(ctxt->c.b8[6]);
PUTPAD(ctxt->c.b8[5]); PUTPAD(ctxt->c.b8[4]);
PUTPAD(ctxt->c.b8[3]); PUTPAD(ctxt->c.b8[2]);
PUTPAD(ctxt->c.b8[1]); PUTPAD(ctxt->c.b8[0]);
#endif
}
void
sha1_loop(struct sha1_ctxt *ctxt, const unsigned char *input, size_t len)
{
size_t gaplen;
size_t gapstart;
size_t off;
size_t copysiz;
off = 0;
while (off < len) {
gapstart = COUNT % 64;
gaplen = 64 - gapstart;
copysiz = (gaplen < len - off) ? gaplen : len - off;
memcpy(&ctxt->m.b8[gapstart], &input[off], copysiz);
COUNT += copysiz;
COUNT %= 64;
ctxt->c.b64[0] += copysiz * 8;
if (COUNT % 64 == 0)
sha1_step(ctxt);
off += copysiz;
}
}
void
sha1_result(struct sha1_ctxt *ctxt, void *digest0)
{
unsigned char *digest;
digest = (unsigned char *)digest0;
sha1_pad(ctxt);
#if BYTE_ORDER == BIG_ENDIAN
memcpy(digest, &ctxt->h.b8[0], 20);
#else
digest[0] = ctxt->h.b8[3]; digest[1] = ctxt->h.b8[2];
digest[2] = ctxt->h.b8[1]; digest[3] = ctxt->h.b8[0];
digest[4] = ctxt->h.b8[7]; digest[5] = ctxt->h.b8[6];
digest[6] = ctxt->h.b8[5]; digest[7] = ctxt->h.b8[4];
digest[8] = ctxt->h.b8[11]; digest[9] = ctxt->h.b8[10];
digest[10] = ctxt->h.b8[9]; digest[11] = ctxt->h.b8[8];
digest[12] = ctxt->h.b8[15]; digest[13] = ctxt->h.b8[14];
digest[14] = ctxt->h.b8[13]; digest[15] = ctxt->h.b8[12];
digest[16] = ctxt->h.b8[19]; digest[17] = ctxt->h.b8[18];
digest[18] = ctxt->h.b8[17]; digest[19] = ctxt->h.b8[16];
#endif
}
/*
* This should look and work like the libcrypto implementation
*/
unsigned char *
SHA1(const unsigned char *d, size_t n, unsigned char *md)
{
struct sha1_ctxt ctx;
sha1_init(&ctx);
sha1_loop(&ctx, d, n);
sha1_result(&ctx, (void *)md);
return md;
}
#endif /*unsupported*/

View File

@ -35,6 +35,7 @@ typedef u64 unat;
//unused parameters
#pragma warning( disable : 4100)
*/
//basic includes from runtime lib
#include <stdlib.h>
#include <stdio.h>

View File

@ -237,7 +237,7 @@ void x86_block::ApplyPatches(u8* base)
x86_block::x86_block()
{
_patches=new vector<code_patch>;
_labels=new vector<code_patch>;
_labels=new vector<x86_Label*>;
opcode_count=0;
}
x86_block::~x86_block()

View File

@ -3,9 +3,26 @@
#include "hw/aica/aica_if.h"
#include "oslib/oslib.h"
/*
DSP rec_v1
Tries to emulate a guesstimation of the aica dsp, by directly emitting x86 opcodes.
This was my first dsp implementation, as implemented for nullDC 1.0.3.
This was derived from a schematic I drew for the dsp, based on
liberal interpretation of known specs, the saturn dsp, digital
electronics assumptions, as well as "best-fitted" my typical
test game suite.
Initiall code by skmp, now part of the reicast project.
See LICENSE & COPYRIGHT files further details
*/
ALIGN(4096) dsp_t dsp;
#if HOST_OS==OS_WINDOWS
#if HOST_OS==OS_WINDOWS && !defined(HOST_NO_REC)
#include "emitter/x86_emitter.h"
const bool SUPPORT_NOFL=false;
@ -14,7 +31,7 @@ const bool SUPPORT_NOFL=false;
#define assert verify
#pragma warning(disable:4311)
#define DYNOPT 0
struct _INST
{
unsigned int TRA;

View File

@ -16,6 +16,7 @@
#define clip_verify(x)
#endif
//Sound generation, mixin, and channel regs emulation
//x.15
s32 volume_lut[16];
//255 -> mute

View File

@ -1031,7 +1031,9 @@ void WriteMem_gdrom(u32 Addr, u32 data, u32 sz)
break;
case GD_DRVSEL:
printf("GDROM: Write to GD_DRVSEL\n");
if (data != 0) {
printf("GDROM: Write to GD_DRVSEL, !=0. Value is: %02X\n", data);
}
DriveSel = data;
break;

View File

@ -14,8 +14,8 @@
#include "hw/aica/aica_if.h"
//#include "naomi/naomi.h"
#include "types.h"
#include "hw/flashrom/flashrom.h"
#include "reios/reios.h"
RomChip sys_rom(BIOS_SIZE);
@ -55,6 +55,14 @@ void SaveRomFiles(const string& root)
sys_nvmem.Save(root, ROM_PREFIX, "nvmem.bin", "nvmem");
}
bool LoadHle(const string& root) {
if (!sys_nvmem.Load(root, ROM_PREFIX, "%nvmem.bin;%flash_wb.bin;%flash.bin;%flash.bin.bin", "nvram")) {
printf("No nvmem loaded\n");
}
return reios_init(sys_rom.data, sys_nvmem.data);
}
#if (DC_PLATFORM == DC_PLATFORM_NORMAL) || (DC_PLATFORM == DC_PLATFORM_DEV_UNIT) || (DC_PLATFORM == DC_PLATFORM_NAOMI) || (DC_PLATFORM == DC_PLATFORM_NAOMI2)
u32 ReadBios(u32 addr,u32 sz) { return sys_rom.Read(addr,sz); }

View File

@ -547,7 +547,9 @@ struct maple_sega_vmu: maple_base
}
}
config->SetImage(lcd_data_decoded);
#ifndef TARGET_PANDORA
push_vmu_screen(lcd_data_decoded);
#endif
#if 0
// Update LCD window
if (!dev->lcd.visible)
@ -799,11 +801,13 @@ struct maple_microphone: maple_base
w8(0x04);//status (just the bit for recording)
w8(0x0f);//gain (default)
w8(0);//exp ?
#ifndef TARGET_PANDORA
if(get_mic_data(micdata)){
w8(240);//ct (240 samples)
wptr(micdata, SIZE_OF_MIC_DATA);
}else{
}else
#endif
{
w8(0);
}

2
core/hw/maple/maple_devs.h Normal file → Executable file
View File

@ -30,5 +30,7 @@ struct maple_device
maple_device* maple_Create(MapleDeviceType type);
#define SIZE_OF_MIC_DATA 480 //ALSO DEFINED IN SipEmulator.java
#ifndef TARGET_PANDORA
int get_mic_data(u8* buffer); //implemented in Android.cpp
int push_vmu_screen(u8* buffer); //implemented in Android.cpp
#endif

View File

@ -48,7 +48,7 @@ void _vmem_get_ptrs(u32 sz,bool write,void*** vmap,void*** func)
void* _vmem_get_ptr2(u32 addr,u32& mask)
{
u32 page=addr>>24;
u32 iirf=(unat)_vmem_MemInfo_ptr[page];
unat iirf=(unat)_vmem_MemInfo_ptr[page];
void* ptr=(void*)(iirf&~HANDLER_MAX);
if (ptr==0) return 0;
@ -60,13 +60,13 @@ void* _vmem_get_ptr2(u32 addr,u32& mask)
void* _vmem_read_const(u32 addr,bool& ismem,u32 sz)
{
u32 page=addr>>24;
u32 iirf=(unat)_vmem_MemInfo_ptr[page];
unat iirf=(unat)_vmem_MemInfo_ptr[page];
void* ptr=(void*)(iirf&~HANDLER_MAX);
if (ptr==0)
{
ismem=false;
const u32 id=iirf;
const unat id=iirf;
if (sz==1)
{
return (void*)_vmem_RF8[id/4];
@ -100,13 +100,13 @@ void* _vmem_read_const(u32 addr,bool& ismem,u32 sz)
void* _vmem_page_info(u32 addr,bool& ismem,u32 sz,u32& page_sz,bool rw)
{
u32 page=addr>>24;
u32 iirf=(unat)_vmem_MemInfo_ptr[page];
unat iirf=(unat)_vmem_MemInfo_ptr[page];
void* ptr=(void*)(iirf&~HANDLER_MAX);
if (ptr==0)
{
ismem=false;
const u32 id=iirf;
const unat id=iirf;
page_sz=24;
if (sz==1)
{

View File

@ -1,44 +1,211 @@
#include "Renderer_if.h"
#include "ta.h"
#include "hw/pvr/pvr_mem.h"
#include "rend/TexCache.h"
#include "deps/zlib/zlib.h"
/*
rendv3 ideas
- multiple backends
- ESish
- OpenGL ES2.0
- OpenGL ES3.0
- OpenGL 3.1
- OpenGL 4.x
- Direct3D 10+ ?
- correct memory ordering model
- resource pools
- threaded ta
- threaded rendering
- rtts
- framebuffers
- overlays
PHASES
- TA submition (memops, dma)
- TA parsing (defered, rend thread)
- CORE render (in-order, defered, rend thread)
submition is done in-order
- Partial handling of TA values
- Gotchas with TA contexts
parsing is done on demand and out-of-order, and might be skipped
- output is only consumed by renderer
render is queued on RENDER_START, and won't stall the emulation or might be skipped
- VRAM integrity is an issue with out-of-order or delayed rendering.
- selective vram snapshots require ta parsing to complete in order with REND_START / REND_END
Complications
- For some apis (gles2, maybe gl31) texture allocation needs to happen on the gpu thread
- multiple versions of different time snapshots of the same texture are required
- ta parsing vs frameskip logic
Texture versioning and staging
A memory copy of the texture can be used to temporary store the texture before upload to vram
This can be moved to another thread
If the api supports async resource creation, we don't need the extra copy
Texcache lookups need to be versioned
rendv2x hacks
- Only a single pending render. Any renders while still pending are dropped (before parsing)
- wait and block for parse/texcache. Render is async
*/
u32 VertexCount=0;
u32 FrameCount=0;
u32 FrameCount=1;
Renderer* rend;
Renderer* renderer;
cResetEvent rs(false,true);
cResetEvent re(false,true);
int max_idx,max_mvo,max_op,max_pt,max_tr,max_vtx,max_modt, ovrn;
TA_context* _pvrrc;
void SetREP(TA_context* cntx);
void dump_frame(const char* file, TA_context* ctx, u8* vram, u8* vram_ref = NULL) {
FILE* fw = fopen(file, "wb");
//append to it
fseek(fw, 0, SEEK_END);
u32 bytes = ctx->tad.End() - ctx->tad.thd_root;
fwrite("TAFRAME3", 1, 8, fw);
fwrite(&ctx->rend.isRTT, 1, sizeof(ctx->rend.isRTT), fw);
fwrite(&ctx->rend.isAutoSort, 1, sizeof(ctx->rend.isAutoSort), fw);
fwrite(&ctx->rend.fb_X_CLIP.full, 1, sizeof(ctx->rend.fb_X_CLIP.full), fw);
fwrite(&ctx->rend.fb_Y_CLIP.full, 1, sizeof(ctx->rend.fb_Y_CLIP.full), fw);
fwrite(ctx->rend.global_param_op.head(), 1, sizeof(PolyParam), fw);
fwrite(ctx->rend.verts.head(), 1, 4 * sizeof(Vertex), fw);
u32 t = VRAM_SIZE;
fwrite(&t, 1, sizeof(t), fw);
u8* compressed;
uLongf compressed_size;
u8* src_vram = vram;
if (vram_ref) {
src_vram = (u8*)malloc(VRAM_SIZE);
for (int i = 0; i < VRAM_SIZE; i++) {
src_vram[i] = vram[i] ^ vram_ref[i];
}
}
compressed = (u8*)malloc(VRAM_SIZE+16);
compressed_size = VRAM_SIZE;
verify(compress(compressed, &compressed_size, src_vram, VRAM_SIZE) == Z_OK);
fwrite(&compressed_size, 1, sizeof(compressed_size), fw);
fwrite(compressed, 1, compressed_size, fw);
free(compressed);
if (src_vram != vram)
free(src_vram);
fwrite(&bytes, 1, sizeof(t), fw);
compressed = (u8*)malloc(bytes + 16);
compressed_size = VRAM_SIZE;
verify(compress(compressed, &compressed_size, ctx->tad.thd_root, bytes) == Z_OK);
fwrite(&compressed_size, 1, sizeof(compressed_size), fw);
fwrite(compressed, 1, compressed_size, fw);
free(compressed);
fclose(fw);
}
TA_context* read_frame(const char* file, u8* vram_ref) {
FILE* fw = fopen(file, "rb");
char id0[8] = { 0 };
u32 t = 0;
u32 t2 = 0;
fread(id0, 1, 8, fw);
if (memcmp(id0, "TAFRAME3", 8) != 0) {
fclose(fw);
return 0;
}
TA_context* ctx = tactx_Alloc();
ctx->Reset();
ctx->tad.Clear();
fread(&ctx->rend.isRTT, 1, sizeof(ctx->rend.isRTT), fw);
fread(&ctx->rend.isAutoSort, 1, sizeof(ctx->rend.isAutoSort), fw);
fread(&ctx->rend.fb_X_CLIP.full, 1, sizeof(ctx->rend.fb_X_CLIP.full), fw);
fread(&ctx->rend.fb_Y_CLIP.full, 1, sizeof(ctx->rend.fb_Y_CLIP.full), fw);
fread(ctx->rend.global_param_op.head(), 1, sizeof(PolyParam), fw);
fread(ctx->rend.verts.head(), 1, 4 * sizeof(Vertex), fw);
fread(&t, 1, sizeof(t), fw);
verify(t == VRAM_SIZE);
vram.UnLockRegion(0, VRAM_SIZE);
fread(&t2, 1, sizeof(t), fw);
u8* gz_stream = (u8*)malloc(t2);
fread(gz_stream, 1, t2, fw);
uncompress(vram.data, (uLongf*)&t, gz_stream, t2);
free(gz_stream);
fread(&t, 1, sizeof(t), fw);
fread(&t2, 1, sizeof(t), fw);
gz_stream = (u8*)malloc(t2);
fread(gz_stream, 1, t2, fw);
uncompress(ctx->tad.thd_data, (uLongf*)&t, gz_stream, t2);
free(gz_stream);
ctx->tad.thd_data += t;
fclose(fw);
}
bool rend_frame(TA_context* ctx, bool draw_osd) {
bool proc = renderer->Process(ctx);
re.Set();
bool do_swp = proc && renderer->Render();
if (do_swp && draw_osd)
renderer->DrawOSD();
return do_swp;
}
bool rend_single_frame()
{
//wait render start only if no frame pending
_pvrrc = DequeueRender();
while (!_pvrrc)
do
{
rs.Wait();
_pvrrc = DequeueRender();
}
bool do_swp=false;
while (!_pvrrc);
do_swp=rend->Render();
if (do_swp)
{
//OSD_DRAW();
}
bool do_swp = rend_frame(_pvrrc, true);
//clear up & free data ..
tactx_Recycle(_pvrrc);
FinishRender(_pvrrc);
_pvrrc=0;
return do_swp;
@ -74,24 +241,26 @@ void* rend_thread(void* p)
if (!rend->Init())
if (!renderer->Init())
die("rend->init() failed\n");
rend->Resize(640,480);
renderer->Resize(640, 480);
for(;;)
{
if (rend_single_frame())
rend->Present();
renderer->Present();
}
}
cThread rthd(rend_thread,0);
bool pend_rend = false;
void rend_start_render()
{
pend_rend = false;
bool is_rtt=(FB_W_SOF1& 0x1000000)!=0;
TA_context* ctx = tactx_Pop(CORE_CURRENT_CTX);
@ -122,8 +291,11 @@ void rend_start_render()
#if HOST_OS==OS_WINDOWS && 0
printf("max: idx: %d, vtx: %d, op: %d, pt: %d, tr: %d, mvo: %d, modt: %d, ov: %d\n", max_idx, max_vtx, max_op, max_pt, max_tr, max_mvo, max_modt, ovrn);
#endif
QueueRender(ctx);
rs.Set();
if (QueueRender(ctx)) {
palette_update();
rs.Set();
pend_rend = true;
}
}
else
{
@ -137,12 +309,14 @@ void rend_start_render()
void rend_end_render()
{
#if 0 //also disabled the printf, it takes quite some time ...
#if HOST_OS!=OS_WINDOWS && !defined(_ANDROID)
#if 1 //also disabled the printf, it takes quite some time ...
#if HOST_OS!=OS_WINDOWS && !(defined(_ANDROID) || defined(TARGET_PANDORA))
if (!re.state) printf("Render > Extended time slice ...\n");
#endif
rend_end_wait();
#endif
if (pend_rend)
re.Wait();
}
/*
@ -160,13 +334,13 @@ bool rend_init()
{
#if NO_REND
rend = rend_norend();
renderer = rend_norend();
#else
#if HOST_OS == OS_WINDOWS
rend = settings.pvr.rend == 0 ? rend_GLES2() : rend_D3D11() ;
renderer = settings.pvr.rend == 0 ? rend_GLES2() : rend_D3D11();
#else
rend = rend_GLES2();
renderer = rend_GLES2();
#endif
#endif

View File

@ -32,9 +32,12 @@ struct Renderer
virtual void Term()=0;
virtual bool Process(TA_context* ctx)=0;
virtual bool Render()=0;
virtual void Present()=0;
virtual void DrawOSD() { }
};

View File

@ -108,7 +108,7 @@ union FB_R_SIZE_type
u32 fb_x_size : 10; //0
u32 fb_y_size : 10; //10
u32 fb_modulus : 10; //20
u32 fb_res : 12; //30
u32 fb_res : 2; //30
};
u32 full;
};

View File

@ -264,7 +264,7 @@ void ta_vtx_ListCont()
void ta_vtx_ListInit()
{
SetCurrentTARC(TA_ISP_BASE);
ta_tad.Clear();
ta_tad.ClearPartial();
ta_cur_state=TAS_NS;
}

View File

@ -4,6 +4,9 @@
extern u32 fskip;
extern u32 FrameCount;
int frameskip=0;
bool FrameSkipping=false; // global switch to enable/disable frameskip
TA_context* ta_ctx;
tad_context ta_tad;
@ -32,14 +35,13 @@ void SetCurrentTARC(u32 addr)
//clear context
ta_ctx=0;
ta_tad.thd_data=0;
ta_tad.thd_root=0;
ta_tad.Reset(0);
}
}
bool TryDecodeTARC()
{
verify(ta_ctx);
verify(ta_ctx != 0);
if (vd_ctx == 0)
{
@ -47,7 +49,7 @@ bool TryDecodeTARC()
vd_ctx->rend.proc_start = vd_ctx->rend.proc_end + 32;
vd_ctx->rend.proc_end = vd_ctx->tad.thd_data;
vd_ctx->rend_inuse.Lock();
vd_rc = vd_ctx->rend;
@ -60,7 +62,7 @@ bool TryDecodeTARC()
void VDecEnd()
{
verify(vd_ctx);
verify(vd_ctx != 0);
vd_ctx->rend = vd_rc;
@ -72,27 +74,37 @@ void VDecEnd()
cMutex mtx_rqueue;
TA_context* rqueue;
void QueueRender(TA_context* ctx)
bool QueueRender(TA_context* ctx)
{
verify(ctx != 0);
if (FrameSkipping && frameskip) {
frameskip=1-frameskip;
tactx_Recycle(ctx);
fskip++;
return false;
}
if (rqueue) {
tactx_Recycle(ctx);
fskip++;
return false;
}
mtx_rqueue.Lock();
TA_context* old = rqueue;
rqueue=ctx;
mtx_rqueue.Unlock();
if (old)
{
tactx_Recycle(old);
fskip++;
}
verify(!old);
return true;
}
TA_context* DequeueRender()
{
mtx_rqueue.Lock();
TA_context* rv = rqueue;
rqueue = 0;
mtx_rqueue.Unlock();
if (rv)
@ -101,6 +113,16 @@ TA_context* DequeueRender()
return rv;
}
void FinishRender(TA_context* ctx)
{
verify(rqueue == ctx);
mtx_rqueue.Lock();
rqueue = 0;
mtx_rqueue.Unlock();
tactx_Recycle(ctx);
}
cMutex mtx_pool;
vector<TA_context*> ctx_pool;

View File

@ -20,6 +20,8 @@ struct PolyParam
//lets see what more :)
u32 texid;
TSP tsp;
TCW tcw;
PCW pcw;
@ -46,11 +48,29 @@ struct tad_context
{
u8* thd_data;
u8* thd_root;
u8* thd_old_data;
void Clear()
{
thd_data=thd_root;
thd_old_data = thd_data = thd_root;
}
void ClearPartial()
{
thd_old_data = thd_data;
thd_data = thd_root;
}
u8* End()
{
return thd_data == thd_root ? thd_old_data : thd_data;
}
void Reset(u8* ptr)
{
thd_data = thd_root = thd_old_data = ptr;
}
};
struct rend_context
@ -126,12 +146,12 @@ struct TA_context
void MarkRend()
{
rend.proc_start = rend.proc_end;
rend.proc_end = tad.thd_data;
rend.proc_start = tad.thd_root;
rend.proc_end = tad.End();
}
void Alloc()
{
tad.thd_root=tad.thd_data=(u8*)malloc(2*1024*1024);
tad.Reset((u8*)malloc(2*1024*1024));
rend.verts.InitBytes(1024*1024,&rend.Overrun); //up to 1 mb of vtx data/frame = ~ 38k vtx/frame
rend.idx.Init(60*1024,&rend.Overrun); //up to 60K indexes ( idx have stripification overhead )
@ -150,7 +170,7 @@ struct TA_context
tad.Clear();
rend_inuse.Lock();
rend.Clear();
rend.proc_end = rend.proc_start = tad.thd_data;
rend.proc_end = rend.proc_start = tad.thd_root;
rend_inuse.Unlock();
}
@ -177,6 +197,7 @@ extern rend_context vd_rc;
TA_context* tactx_Find(u32 addr, bool allocnew=false);
TA_context* tactx_Pop(u32 addr);
TA_context* tactx_Alloc();
void tactx_Recycle(TA_context* poped_ctx);
/*
@ -188,8 +209,9 @@ void tactx_Recycle(TA_context* poped_ctx);
#define TACTX_NONE (0xFFFFFFFF)
void SetCurrentTARC(u32 addr);
void QueueRender(TA_context* ctx);
bool QueueRender(TA_context* ctx);
TA_context* DequeueRender();
void FinishRender(TA_context* ctx);
bool TryDecodeTARC();
void VDecEnd();

View File

@ -8,6 +8,7 @@
#include "ta.h"
#include "ta_ctx.h"
#include "pvr_mem.h"
#include "rend/gles/gles.h"
#include "Renderer_if.h"
u32 ta_type_lut[256];
@ -376,7 +377,9 @@ strip_end:
}
public:
#define group_EN() if (data->pcw.Group_En){ TileClipMode(data->pcw.User_Clip);}
//Group_En bit seems ignored, thanks p1pkin
#define group_EN() /*if (data->pcw.Group_En) */{ TileClipMode(data->pcw.User_Clip);}
static Ta_Dma* TACALL ta_main(Ta_Dma* data,Ta_Dma* data_end)
{
do
@ -715,7 +718,7 @@ public:
{
VDECInit();
TaCmd=ta_main;
CurrentList = ListType_None;
ListIsFinished[0]=ListIsFinished[1]=ListIsFinished[2]=ListIsFinished[3]=ListIsFinished[4]=false;
}
@ -786,11 +789,18 @@ public:
}
d_pp->first=vdrc.idx.used();
d_pp->count=0;
d_pp->isp=pp->isp;
d_pp->tsp=pp->tsp;
d_pp->tcw=pp->tcw;
d_pp->pcw=pp->pcw;
d_pp->tileclip=tileclip_val;
d_pp->texid = -1;
if (d_pp->pcw.Texture) {
d_pp->texid = GetTexture(d_pp->tsp,d_pp->tcw);
}
}
}
@ -1181,6 +1191,12 @@ public:
d_pp->pcw=spr->pcw;
d_pp->tileclip=tileclip_val;
d_pp->texid = -1;
if (d_pp->pcw.Texture) {
d_pp->texid = GetTexture(d_pp->tsp,d_pp->tcw);
}
SFaceBaseColor=spr->BaseCol;
SFaceOffsColor=spr->OffsCol;
}
@ -1392,6 +1408,9 @@ FifoSplitter<0> TAFifo0;
int ta_parse_cnt = 0;
/*
Also: gotta stage textures here
*/
bool ta_parse_vdrc(TA_context* ctx)
{
bool rv=false;
@ -1539,6 +1558,8 @@ void FillBGP(TA_context* ctx)
u32 vertex_ptr=strip_vert_num*strip_vs+strip_base +3*4;
//now , all the info is ready :p
bgpp->texid = -1;
bgpp->isp.full=vri(strip_base);
bgpp->tsp.full=vri(strip_base+4);
bgpp->tcw.full=vri(strip_base+8);

View File

@ -4,6 +4,9 @@
*/
#include "types.h"
#ifndef HOST_NO_REC
#include "decoder.h"
#include "shil.h"
#include "ngen.h"
@ -654,7 +657,7 @@ u32 MatchDiv32(u32 pc , Sh4RegType &reg1,Sh4RegType &reg2 , Sh4RegType &reg3)
}
else
{
printf("%s\n",OpDesc[opcode]->diss);
//printf("DIV MATCH BROKEN BY: %s\n",OpDesc[opcode]->diss);
break;
}
@ -1169,7 +1172,7 @@ _end:
//Small-n-simple idle loop detector :p
if (state.info.has_readm && !state.info.has_writem && !state.info.has_fpu && blk->guest_opcodes<6)
{
if ((blk->BlockType==BET_Cond_0 || blk->BlockType==BET_Cond_1) && blk->BranchBlock<=blk->addr)
if (blk->BlockType==BET_Cond_0 || blk->BlockType==BET_Cond_1 && blk->BranchBlock<=blk->addr)
{
blk->guest_cycles*=3;
}
@ -1210,3 +1213,4 @@ _end:
blk=0;
}
#endif

View File

@ -212,7 +212,7 @@ DynarecCodeEntry* rdv_CompilePC()
{
u32 pc=next_pc;
if (emit_FreeSpace()<16*1024 || pc==0x8c0000e0 || pc==0x8c010000 || pc==0x8c008300)
if (emit_FreeSpace()<16*1024 || pc==0x8c0000e0 || pc==0xac010000 || pc==0xac008300)
recSh4_ClearCache();
RuntimeBlockInfo* rv=0;
@ -304,7 +304,7 @@ void* DYNACALL rdv_LinkBlock(u8* code,u32 dpc)
rbi=bm_GetStaleBlock(code);
}
verify(rbi);
verify((int)rbi);
u32 bcls=BET_GET_CLS(rbi->BlockType);

View File

@ -68,7 +68,7 @@ struct RegAlloc
nreg=(nreg_t)-1;
nregf=(nregf_t)-1;
RegAccess ra={pos,mode};
RegAccess ra={static_cast<u32>(pos),mode};
accesses.push_back(ra);
}
@ -87,7 +87,7 @@ struct RegAlloc
end=pos;
if (mode&AM_WRITE)
writeback=true;
RegAccess ra={pos,mode};
RegAccess ra={static_cast<u32>(pos),mode};
accesses.push_back(ra);
}

View File

@ -170,6 +170,8 @@ int AicaUpdate(int tag, int c, int j)
return AICA_TICK;
}
int DreamcastSecond(int tag, int c, int j)
{
settings.dreamcast.RTC++;
@ -183,7 +185,8 @@ int DreamcastSecond(int tag, int c, int j)
#endif
//printf("%d ticks\n",sh4_sched_intr);
sh4_sched_intr=0; return SH4_MAIN_CLOCK;
sh4_sched_intr=0;
return SH4_MAIN_CLOCK;
}
int UpdateSystem_rec()

View File

@ -61,8 +61,8 @@ void dofoo(sh4_opcode op)
// 0xxx
void cpu_iNimp(u32 op, const char* info)
{
printf("\n\nUnimplemented opcode : %X : %X \n", op,next_pc);
printf(info);
printf("\n\nUnimplemented opcode: %08X next_pc: %08X pr: %08X msg: %s\n", op, next_pc, pr, info);
//next_pc = pr; //debug hackfix: try to recover by returning from call
die("iNimp reached\n");
//sh4_cpu.Stop();
}

View File

@ -45,8 +45,9 @@ SCLSR2_type SCIF_SCLSR2;
void SerialWrite(u32 addr, u32 data)
{
//WriteSerial((u8)data);
//putc(data,stdout);
if (settings.debug.SerialConsole) {
putc(data, stdout);
}
}
//SCIF_SCFSR2 read

View File

@ -76,16 +76,35 @@ void UpdateTMU_i(u32 Cycles)
u32 tmu_ch_base[3];
u32 read_TMU_TCNTch(u32 ch)
{
return tmu_ch_base[ch]-((sh4_sched_now64()>>tmu_shift[ch])&tmu_mask[ch]);
return tmu_ch_base[ch] - ((sh4_sched_now64() >> tmu_shift[ch])&tmu_mask[ch]);
}
void sched_chan_tick(int ch)
{
//schedule next interrupt
//return TMU_TCOR(ch) << tmu_shift[ch];
u32 togo = read_TMU_TCNTch(ch);
u32 cycles = togo << tmu_shift[ch];
if (cycles > SH4_MAIN_CLOCK)
cycles = SH4_MAIN_CLOCK;
if (tmu_mask[ch])
sh4_sched_request(tmu_sched[ch], cycles );
else
sh4_sched_request(tmu_sched[ch], -1);
//sched_tmu_cb
}
void write_TMU_TCNTch(u32 ch, u32 data)
{
//u32 TCNT=read_TMU_TCNTch(ch);
tmu_ch_base[ch]=data+((sh4_sched_now64()>>tmu_shift[ch])&tmu_mask[ch]);
sched_chan_tick(ch);
}
template<u32 ch>
@ -105,24 +124,8 @@ void turn_on_off_ch(u32 ch, bool on)
u32 TCNT=read_TMU_TCNTch(ch);
tmu_mask[ch]=on?0xFFFFFFFF:0x00000000;
write_TMU_TCNTch(ch,TCNT);
}
void sched_chan_tick(int ch)
{
//max ticks
u32 tcnt = read_TMU_TCNTch(ch);
u64 togo=tcnt;
togo<<=tmu_shift[ch];
if (togo>SH4_MAIN_CLOCK)
togo=SH4_MAIN_CLOCK;
if (togo>-sh4_sched_now())
togo=-sh4_sched_now();
sh4_sched_request(tmu_sched[ch],togo);
sched_chan_tick(ch);
}
//Update internal counter registers
@ -173,6 +176,7 @@ void UpdateTMUCounts(u32 reg)
}
tmu_shift[reg]+=2;
write_TMU_TCNTch(reg,TCNT);
sched_chan_tick(reg);
}
//Write to status registers
@ -204,25 +208,38 @@ void write_TMU_TSTR(u32 addr, u32 data)
turn_on_off_ch(i,data&(1<<i));
}
int sched_tmu_cb(int tag, int sch_cycl, int jitter)
int sched_tmu_cb(int ch, int sch_cycl, int jitter)
{
/*
static int _target_intvr=200*1000*100;
static int sh4_last=-1, long_term;
if (sh4_last!=-1)
{
int eeel=sh4_sched_now()-sh4_last;
long_term+=eeel-_target_intvr;
if (tmu_mask[ch]) {
u32 tcnt = read_TMU_TCNTch(ch);
//printf("Dreamcast second... %d | lteerr %d\n",eeel,long_term);
u32 tcor = TMU_TCOR(ch);
u32 cycles = tcor << tmu_shift[ch];
//this is not 100% correct
if (abs((s32)tcnt) <= jitter) {
//raise interrupt, timer counted down
TMU_TCR(ch) |= tmu_underflow;
InterruptPend(tmu_intID[ch], 1);
//printf("Interrupt for %d, %d cycles\n", ch, sch_cycl);
//schedule next trigger by writing the TCNT register
write_TMU_TCNTch(ch, tcor - tcnt);
}
else {
//schedule next trigger by writing the TCNT register
write_TMU_TCNTch(ch, tcnt);
}
return 0; //has already been scheduled by TCNT write
}
else {
return 0; //this channel is disabled, no need to schedule next event
}
sh4_last=sh4_sched_now();
return _target_intvr;
*/
return jitter-1;
}
//Init/Res/Term
@ -264,11 +281,10 @@ void tmu_init()
//TMU TCPR2 0xFFD8002C 0x1FD8002C 32 Held Held Held Held Pclk
sh4_rio_reg(TMU,TMU_TCPR2_addr,RIO_FUNC,32,&TMU_TCPR2_read,&TMU_TCPR2_write);
for (int i=0;i<1;i++)
tmu_sched[i]=sh4_sched_register(i,&sched_tmu_cb);
sh4_sched_request(tmu_sched[0],-1);
for (int i = 0; i < 3; i++) {
tmu_sched[i] = sh4_sched_register(i, &sched_tmu_cb);
sh4_sched_request(tmu_sched[i], -1);
}
}

View File

@ -75,17 +75,21 @@ void SetFloatStatusReg()
//TODO: Implement this (needed for SOTB)
#if HOST_CPU==CPU_X86 && HOST_OS==OS_WINDOWS
#if HOST_CPU==CPU_X86
if (fpscr.RM==1) //if round to 0 , set the flag
temp|=(3<<13);
if (fpscr.DN) //denormals are considered 0
temp|=(1<<15);
_asm
{
ldmxcsr temp; //load the float status :)
}
#if BUILD_COMPILER == COMPILER_VC
_asm
{
ldmxcsr temp; //load the float status :)
}
#else
asm("ldmxcsr %0" : : "m"(temp));
#endif
#elif HOST_CPU==CPU_ARM
static const unsigned int x = 0x04086060;
unsigned int y = 0x02000000;

View File

@ -308,7 +308,7 @@ typedef void DYNACALL TaListVoidFP(void* data);
struct Sh4RCB
{
void* fpcb[8*1024*1024];
u64 _pad[(0x40000-sizeof(Sh4Context)-64-8)/8];
u64 _pad[(0x40000-sizeof(Sh4Context)-64-sizeof(void*)*2)/8];
TaListVoidFP* tacmd_voud; //*TODO* remove (not used)
sqw_fp* do_sqw_nommu;
u64 sq_buffer[64/8];

View File

@ -90,4 +90,5 @@ u32 GetRamPageFromAddress(u32 RamAddress);
bool LoadRomFiles(const string& root);
void SaveRomFiles(const string& root);
void SaveRomFiles(const string& root);
bool LoadHle(const string& root);

View File

@ -4,7 +4,7 @@
#include "dyna/decoder_opcodes.h"
#include "types.h"
#include "hw/sh4/dyna/shil.h"
#include "reios/reios.h"
OpCallFP* OpPtr[0x10000];
sh4_opcodelistentry* OpDesc[0x10000];
@ -124,7 +124,8 @@ sh4_opcodelistentry missing_opcode = {0,iNotImplemented,0,0,ReadWritePC,"missing
sh4_opcodelistentry opcodes[]=
{
//HLE
{0, reios_trap, Mask_none, REIOS_OPCODE, Branch_dir, "reios_trap", 100, 100, CO, fix_none },
//CPU
{dec_i0000_nnnn_0010_0011 ,i0000_nnnn_0010_0011 ,Mask_n ,0x0023 ,Branch_rel_d ,"braf <REG_N>" ,2,3,CO,fix_none}, //braf <REG_N>
{dec_i0000_nnnn_0000_0011 ,i0000_nnnn_0000_0011 ,Mask_n ,0x0003 ,Branch_rel_d ,"bsrf <REG_N>" ,2,3,CO,fix_none}, //bsrf <REG_N>

View File

@ -1,4 +1,3 @@
#pragma once
#include "types.h"
#include "sh4_rom.h"

View File

@ -86,17 +86,25 @@ int sh4_sched_register(int tag, sh4_sched_callback* ssc)
return list.size()-1;
}
/*
Return current cycle count, in 32 bits (wraps after 21 dreamcast seconds)
*/
u32 sh4_sched_now()
{
return sh4_sched_ffb-Sh4cntx.sh4_sched_next;
}
/*
Return current cycle count, in 64 bits (effectivelly never wraps)
*/
u64 sh4_sched_now64()
{
return sh4_sched_ffb-Sh4cntx.sh4_sched_next;
}
void sh4_sched_request(int id, int cycles)
{
verify(cycles <= SH4_MAIN_CLOCK);
list[id].start=sh4_sched_now();
list[id].end=list[id].start+cycles;

View File

@ -1,14 +1,47 @@
#include "types.h"
/*
tag, as passed on sh4_sched_register
sch_cycles, the cycle duration that the callback requested (sh4_sched_request)
jitter, the number of cycles that the callback was delayed, [0... 448]
*/
typedef int sh4_sched_callback(int tag, int sch_cycl, int jitter);
/*
Registed a callback to the scheduler. The returned id
is used for sh4_sched_request and sh4_sched_elapsed calls
*/
int sh4_sched_register(int tag, sh4_sched_callback* ssc);
/*
current time in SH4 cycles, referenced to boot.
Wraps every ~21 secs
*/
u32 sh4_sched_now();
/*
current time, in SH4 cycles, referenced to boot.
Does not wrap, 64 bits.
*/
u64 sh4_sched_now64();
/*
Schedule a callback to be called sh4 *cycles* after the
invocation of this function. *Cycles* range is (0, 200M].
Passing a value of 0 disables the callback.
If called multiple times, only the last call is in effect
*/
void sh4_sched_request(int id, int cycles);
/*
Returns how much time has passed for this callback
*/
int sh4_sched_elapsed(int id);
/*
Tick for *cycles*
*/
void sh4_sched_tick(int cycles);
extern u32 sh4_sched_intr;

View File

@ -6,9 +6,9 @@
#include "deps/chdpsr/cdipsr.h"
Disc* cdi_parse(wchar* file)
Disc* cdi_parse(const wchar* file)
{
FILE* fsource=fopen(file,"rb");
core_file* fsource=core_fopen(file);
if (!fsource)
return 0;
@ -34,7 +34,7 @@ Disc* cdi_parse(wchar* file)
CDI_get_tracks (fsource, &image);
image.header_position = ftell(fsource);
image.header_position = core_ftell(fsource);
printf("\nSession %d has %d track(s)\n",image.global_current_session,image.tracks);
@ -54,7 +54,7 @@ Disc* cdi_parse(wchar* file)
CDI_read_track (fsource, &image, &track);
image.header_position = ftell(fsource);
image.header_position = core_ftell(fsource);
// Show info
@ -98,7 +98,7 @@ Disc* cdi_parse(wchar* file)
t.CTRL=track.mode==0?0:4;
t.StartFAD=track.start_lba+track.pregap_length;
t.EndFAD=t.StartFAD+track.length-1;
t.file = new RawTrackFile(fopen(file,"rb"),track.position + track.pregap_length * track.sector_size,t.StartFAD,track.sector_size);
t.file = new RawTrackFile(core_fopen(file),track.position + track.pregap_length * track.sector_size,t.StartFAD,track.sector_size);
rv->tracks.push_back(t);
@ -115,21 +115,21 @@ Disc* cdi_parse(wchar* file)
if (track.total_length < track.length + track.pregap_length)
{
printf("\nThis track seems truncated. Skipping...\n");
fseek(fsource, track.position, SEEK_SET);
fseek(fsource, track.total_length, SEEK_CUR);
track.position = ftell(fsource);
core_fseek(fsource, track.position, SEEK_SET);
core_fseek(fsource, track.total_length, SEEK_CUR);
track.position = core_ftell(fsource);
}
else
{
printf("Track position: %d\n",track.position + track.pregap_length * track.sector_size);
fseek(fsource, track.position, SEEK_SET);
core_fseek(fsource, track.position, SEEK_SET);
// fseek(fsource, track->pregap_length * track->sector_size, SEEK_CUR);
// fseek(fsource, track->length * track->sector_size, SEEK_CUR);
fseek(fsource, track.total_length * track.sector_size, SEEK_CUR);
core_fseek(fsource, track.total_length * track.sector_size, SEEK_CUR);
//savetrack(fsource, &image, &track, &opts, &flags);
track.position = ftell(fsource);
track.position = core_ftell(fsource);
rv->EndFAD=track.start_lba +track.total_length;
// Generate cuesheet entries
@ -140,7 +140,7 @@ Disc* cdi_parse(wchar* file)
}
}
fseek(fsource, image.header_position, SEEK_SET);
core_fseek(fsource, image.header_position, SEEK_SET);
// Close loops

View File

@ -17,7 +17,7 @@ struct CHDDisc : Disc
hunk_mem=0;
}
bool TryOpen(wchar* file);
bool TryOpen(const wchar* file);
~CHDDisc()
{
@ -64,7 +64,7 @@ struct CHDTrack : TrackFile
}
};
bool CHDDisc::TryOpen(wchar* file)
bool CHDDisc::TryOpen(const wchar* file)
{
chd_error err=chd_open(file,CHD_OPEN_READ,0,&chd);
@ -141,7 +141,9 @@ bool CHDDisc::TryOpen(wchar* file)
if (total_frames!=549300 || tracks.size()<3)
{
printf("WARNING: chd: Total frames is wrong: %d frames in %d tracks\n",total_frames,tracks.size());
#ifndef NOT_REICAST
msgboxf("This is an improper dump!",MBX_ICONEXCLAMATION);
#endif
return false;
}
@ -151,7 +153,7 @@ bool CHDDisc::TryOpen(wchar* file)
}
Disc* chd_parse(wchar* file)
Disc* chd_parse(const wchar* file)
{
CHDDisc* rv = new CHDDisc();

View File

@ -1,16 +1,16 @@
#include "common.h"
Disc* chd_parse(wchar* file);
Disc* gdi_parse(wchar* file);
Disc* cdi_parse(wchar* file);
Disc* chd_parse(const wchar* file);
Disc* gdi_parse(const wchar* file);
Disc* cdi_parse(const wchar* file);
#if HOST_OS==OS_WINDOWS
Disc* ioctl_parse(wchar* file);
Disc* ioctl_parse(const wchar* file);
#endif
u32 NullDriveDiscType;
Disc* disc;
Disc*(*drivers[])(wchar* path)=
Disc*(*drivers[])(const wchar* path)=
{
chd_parse,
gdi_parse,
@ -25,8 +25,12 @@ u8 q_subchannel[96];
void PatchRegion_0(u8* sector,int size)
{
#ifndef NOT_REICAST
if (settings.imgread.PatchRegion==0)
return;
#else
return;
#endif
u8* usersect=sector;
@ -41,8 +45,12 @@ void PatchRegion_0(u8* sector,int size)
}
void PatchRegion_6(u8* sector,int size)
{
#ifndef NOT_REICAST
if (settings.imgread.PatchRegion==0)
return;
#else
return;
#endif
u8* usersect=sector;
@ -121,18 +129,29 @@ bool ConvertSector(u8* in_buff , u8* out_buff , int from , int to,int sector)
return true;
}
Disc* OpenDisc(const wchar* fn)
{
Disc* rv;
for (int i=0;drivers[i] && !(rv=drivers[i](fn));i++) ;
return rv;
}
bool InitDrive_(wchar* fn)
{
TermDrive();
//try all drivers
for (int i=0;drivers[i] && !(disc=drivers[i](fn));i++) ;
disc = OpenDisc(fn);
if (disc!=0)
{
printf("gdrom: Opened image \"%s\"\n",fn);
NullDriveDiscType=Busy;
#ifndef NOT_REICAST
libCore_gdrom_disc_change();
#endif
// Sleep(400); //busy for a bit // what, really ?
return true;
}
@ -144,6 +163,7 @@ bool InitDrive_(wchar* fn)
return false;
}
#ifndef NOT_REICAST
bool InitDrive(u32 fileflags)
{
if (settings.imgread.LoadDefaultImage)
@ -185,11 +205,11 @@ bool InitDrive(u32 fileflags)
if (!InitDrive_(fn))
{
//msgboxf("Selected image failed to load",MBX_ICONERROR);
NullDriveDiscType=NoDisk;
gd_setdisc();
sns_asc=0x29;
sns_ascq=0x00;
sns_key=0x6;
NullDriveDiscType=NoDisk;
gd_setdisc();
sns_asc=0x29;
sns_ascq=0x00;
sns_key=0x6;
return true;
}
else
@ -257,6 +277,7 @@ bool DiscSwap(u32 fileflags)
return true;
}
}
#endif
void TermDrive()
{

View File

@ -3,6 +3,8 @@
#include <vector>
using namespace std;
#include "deps/coreio/coreio.h"
extern u32 NullDriveDiscType;
struct TocTrackInfo
{
@ -240,7 +242,7 @@ struct Disc
{
u32 fmt=tracks[i].CTRL==4?2048:2352;
char fsto[1024];
sprintf(fsto,"%s%s%d.img",path.c_str(),"track",i);
sprintf(fsto,"%s%s%d.img",path.c_str(),".track",i);
FILE* fo=fopen(fsto,"wb");
@ -257,14 +259,16 @@ struct Disc
extern Disc* disc;
Disc* OpenDisc(const wchar* fn);
struct RawTrackFile : TrackFile
{
FILE* file;
core_file* file;
s32 offset;
u32 fmt;
bool cleanup;
RawTrackFile(FILE* file,u32 file_offs,u32 first_fad,u32 secfmt)
RawTrackFile(core_file* file,u32 file_offs,u32 first_fad,u32 secfmt)
{
verify(file!=0);
this->file=file;
@ -287,13 +291,13 @@ struct RawTrackFile : TrackFile
verify(false);
}
fseek(file,offset+FAD*fmt,SEEK_SET);
fread(dst,1,fmt,file);
core_fseek(file,offset+FAD*fmt,SEEK_SET);
core_fread(file, dst, fmt);
}
virtual ~RawTrackFile()
{
if (cleanup && file)
fclose(file);
core_fclose(file);
}
};

View File

@ -1,20 +1,36 @@
#include "common.h"
#include <ctype.h>
#include <sstream>
Disc* load_gdi(char* file)
Disc* load_gdi(const char* file)
{
u32 iso_tc;
Disc* disc = new Disc();
//memset(&gdi_toc,0xFFFFFFFF,sizeof(gdi_toc));
//memset(&gdi_ses,0xFFFFFFFF,sizeof(gdi_ses));
FILE* t=fopen(file,"rb");
core_file* t=core_fopen(file);
if (!t)
return 0;
fscanf(t,"%d\r\n",&iso_tc);
size_t gdi_len = core_fsize(t);
char gdi_data[8193] = { 0 };
if (gdi_len >= sizeof(gdi_data))
{
core_fclose(t);
return 0;
}
core_fread(t, gdi_data, gdi_len);
core_fclose(t);
istringstream gdi(gdi_data);
gdi >> iso_tc;
printf("\nGDI : %d tracks\n",iso_tc);
char temp[512];
char path[512];
strcpy(path,file);
size_t len=strlen(file);
@ -30,30 +46,38 @@ Disc* load_gdi(char* file)
s32 OFFSET=0;
for (u32 i=0;i<iso_tc;i++)
{
string track_filename;
//TRACK FADS CTRL SSIZE file OFFSET
fscanf(t,"%d %d %d %d",&TRACK,&FADS,&CTRL,&SSIZE);
//%s %d\r\n,temp,&OFFSET);
//disc->tracks.push_back(
while(isspace(fgetc(t))) ;
fseek(t,-1,SEEK_CUR);
if (fgetc(t)=='"')
gdi >> TRACK;
gdi >> FADS;
gdi >> CTRL;
gdi >> SSIZE;
char last;
do {
gdi >> last;
} while (isspace(last));
if (last == '"')
{
char c;
int i=0;
while((c=fgetc(t))!='"')
temp[i++]=c;
temp[i]=0;
for(;;) {
gdi >> last;
if (last == '"')
break;
track_filename += last;
}
}
else
{
fseek(t,-1,SEEK_CUR);
fscanf(t,"%s",temp);
gdi >> track_filename;
track_filename = last + track_filename;
}
fscanf(t,"%d\r\n",&OFFSET);
printf("file[%d] \"%s\": FAD:%d, CTRL:%d, SSIZE:%d, OFFSET:%d\n",TRACK,temp,FADS,CTRL,SSIZE,OFFSET);
gdi >> OFFSET;
printf("file[%d] \"%s\": FAD:%d, CTRL:%d, SSIZE:%d, OFFSET:%d\n", TRACK, track_filename.c_str(), FADS, CTRL, SSIZE, OFFSET);
Track t;
t.ADDR=0;
@ -63,8 +87,8 @@ Disc* load_gdi(char* file)
if (SSIZE!=0)
{
strcpy(pathptr,temp);
t.file = new RawTrackFile(fopen(path,"rb"),OFFSET,t.StartFAD,SSIZE);
strcpy(pathptr, track_filename.c_str());
t.file = new RawTrackFile(core_fopen(path),OFFSET,t.StartFAD,SSIZE);
}
disc->tracks.push_back(t);
}
@ -75,7 +99,7 @@ Disc* load_gdi(char* file)
}
Disc* gdi_parse(char* file)
Disc* gdi_parse(const char* file)
{
size_t len=strlen(file);
if (len>4)

View File

@ -363,7 +363,7 @@ void PhysicalTrack::Read(u32 FAD,u8* dst,SectorFormat* sector_type,u8* subcode,S
}
Disc* ioctl_parse(wchar* file)
Disc* ioctl_parse(const wchar* file)
{
if (strlen(file)==3 && GetDriveType(file)==DRIVE_CDROM)

329
core/khronos/APPLE/egl.h Normal file
View File

@ -0,0 +1,329 @@
/* -*- mode: c; tab-width: 8; -*- */
/* vi: set sw=4 ts=8: */
/* Reference version of egl.h for EGL 1.4.
* $Revision: 9356 $ on $Date: 2009-10-21 02:52:25 -0700 (Wed, 21 Oct 2009) $
*/
/*
** Copyright (c) 2007-2009 The Khronos Group Inc.
**
** Permission is hereby granted, free of charge, to any person obtaining a
** copy of this software and/or associated documentation files (the
** "Materials"), to deal in the Materials without restriction, including
** without limitation the rights to use, copy, modify, merge, publish,
** distribute, sublicense, and/or sell copies of the Materials, and to
** permit persons to whom the Materials are furnished to do so, subject to
** the following conditions:
**
** The above copyright notice and this permission notice shall be included
** in all copies or substantial portions of the Materials.
**
** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
*/
#ifndef __egl_h_
#define __egl_h_
/* All platform-dependent types and macro boilerplate (such as EGLAPI
* and EGLAPIENTRY) should go in eglplatform.h.
*/
#include <APPLE/eglplatform.h>
#ifdef __cplusplus
extern "C" {
#endif
/* EGL Types */
/* EGLint is defined in eglplatform.h */
typedef unsigned int EGLBoolean;
typedef unsigned int EGLenum;
typedef void *EGLConfig;
typedef void *EGLContext;
typedef void *EGLDisplay;
typedef void *EGLSurface;
typedef void *EGLClientBuffer;
/* EGL Versioning */
#define EGL_VERSION_1_0 1
#define EGL_VERSION_1_1 1
#define EGL_VERSION_1_2 1
#define EGL_VERSION_1_3 1
#define EGL_VERSION_1_4 1
/* EGL Enumerants. Bitmasks and other exceptional cases aside, most
* enums are assigned unique values starting at 0x3000.
*/
/* EGL aliases */
#define EGL_FALSE 0
#define EGL_TRUE 1
/* Out-of-band handle values */
#define EGL_DEFAULT_DISPLAY ((EGLNativeDisplayType)0)
#define EGL_NO_CONTEXT ((EGLContext)0)
#define EGL_NO_DISPLAY ((EGLDisplay)0)
#define EGL_NO_SURFACE ((EGLSurface)0)
/* Out-of-band attribute value */
#define EGL_DONT_CARE ((EGLint)-1)
/* Errors / GetError return values */
#define EGL_SUCCESS 0x3000
#define EGL_NOT_INITIALIZED 0x3001
#define EGL_BAD_ACCESS 0x3002
#define EGL_BAD_ALLOC 0x3003
#define EGL_BAD_ATTRIBUTE 0x3004
#define EGL_BAD_CONFIG 0x3005
#define EGL_BAD_CONTEXT 0x3006
#define EGL_BAD_CURRENT_SURFACE 0x3007
#define EGL_BAD_DISPLAY 0x3008
#define EGL_BAD_MATCH 0x3009
#define EGL_BAD_NATIVE_PIXMAP 0x300A
#define EGL_BAD_NATIVE_WINDOW 0x300B
#define EGL_BAD_PARAMETER 0x300C
#define EGL_BAD_SURFACE 0x300D
#define EGL_CONTEXT_LOST 0x300E /* EGL 1.1 - IMG_power_management */
/* Reserved 0x300F-0x301F for additional errors */
/* Config attributes */
#define EGL_BUFFER_SIZE 0x3020
#define EGL_ALPHA_SIZE 0x3021
#define EGL_BLUE_SIZE 0x3022
#define EGL_GREEN_SIZE 0x3023
#define EGL_RED_SIZE 0x3024
#define EGL_DEPTH_SIZE 0x3025
#define EGL_STENCIL_SIZE 0x3026
#define EGL_CONFIG_CAVEAT 0x3027
#define EGL_CONFIG_ID 0x3028
#define EGL_LEVEL 0x3029
#define EGL_MAX_PBUFFER_HEIGHT 0x302A
#define EGL_MAX_PBUFFER_PIXELS 0x302B
#define EGL_MAX_PBUFFER_WIDTH 0x302C
#define EGL_NATIVE_RENDERABLE 0x302D
#define EGL_NATIVE_VISUAL_ID 0x302E
#define EGL_NATIVE_VISUAL_TYPE 0x302F
#define EGL_SAMPLES 0x3031
#define EGL_SAMPLE_BUFFERS 0x3032
#define EGL_SURFACE_TYPE 0x3033
#define EGL_TRANSPARENT_TYPE 0x3034
#define EGL_TRANSPARENT_BLUE_VALUE 0x3035
#define EGL_TRANSPARENT_GREEN_VALUE 0x3036
#define EGL_TRANSPARENT_RED_VALUE 0x3037
#define EGL_NONE 0x3038 /* Attrib list terminator */
#define EGL_BIND_TO_TEXTURE_RGB 0x3039
#define EGL_BIND_TO_TEXTURE_RGBA 0x303A
#define EGL_MIN_SWAP_INTERVAL 0x303B
#define EGL_MAX_SWAP_INTERVAL 0x303C
#define EGL_LUMINANCE_SIZE 0x303D
#define EGL_ALPHA_MASK_SIZE 0x303E
#define EGL_COLOR_BUFFER_TYPE 0x303F
#define EGL_RENDERABLE_TYPE 0x3040
#define EGL_MATCH_NATIVE_PIXMAP 0x3041 /* Pseudo-attribute (not queryable) */
#define EGL_CONFORMANT 0x3042
/* Reserved 0x3041-0x304F for additional config attributes */
/* Config attribute values */
#define EGL_SLOW_CONFIG 0x3050 /* EGL_CONFIG_CAVEAT value */
#define EGL_NON_CONFORMANT_CONFIG 0x3051 /* EGL_CONFIG_CAVEAT value */
#define EGL_TRANSPARENT_RGB 0x3052 /* EGL_TRANSPARENT_TYPE value */
#define EGL_RGB_BUFFER 0x308E /* EGL_COLOR_BUFFER_TYPE value */
#define EGL_LUMINANCE_BUFFER 0x308F /* EGL_COLOR_BUFFER_TYPE value */
/* More config attribute values, for EGL_TEXTURE_FORMAT */
#define EGL_NO_TEXTURE 0x305C
#define EGL_TEXTURE_RGB 0x305D
#define EGL_TEXTURE_RGBA 0x305E
#define EGL_TEXTURE_2D 0x305F
/* Config attribute mask bits */
#define EGL_PBUFFER_BIT 0x0001 /* EGL_SURFACE_TYPE mask bits */
#define EGL_PIXMAP_BIT 0x0002 /* EGL_SURFACE_TYPE mask bits */
#define EGL_WINDOW_BIT 0x0004 /* EGL_SURFACE_TYPE mask bits */
#define EGL_VG_COLORSPACE_LINEAR_BIT 0x0020 /* EGL_SURFACE_TYPE mask bits */
#define EGL_VG_ALPHA_FORMAT_PRE_BIT 0x0040 /* EGL_SURFACE_TYPE mask bits */
#define EGL_MULTISAMPLE_RESOLVE_BOX_BIT 0x0200 /* EGL_SURFACE_TYPE mask bits */
#define EGL_SWAP_BEHAVIOR_PRESERVED_BIT 0x0400 /* EGL_SURFACE_TYPE mask bits */
#define EGL_OPENGL_ES_BIT 0x0001 /* EGL_RENDERABLE_TYPE mask bits */
#define EGL_OPENVG_BIT 0x0002 /* EGL_RENDERABLE_TYPE mask bits */
#define EGL_OPENGL_ES2_BIT 0x0004 /* EGL_RENDERABLE_TYPE mask bits */
#define EGL_OPENGL_BIT 0x0008 /* EGL_RENDERABLE_TYPE mask bits */
/* QueryString targets */
#define EGL_VENDOR 0x3053
#define EGL_VERSION 0x3054
#define EGL_EXTENSIONS 0x3055
#define EGL_CLIENT_APIS 0x308D
/* QuerySurface / SurfaceAttrib / CreatePbufferSurface targets */
#define EGL_HEIGHT 0x3056
#define EGL_WIDTH 0x3057
#define EGL_LARGEST_PBUFFER 0x3058
#define EGL_TEXTURE_FORMAT 0x3080
#define EGL_TEXTURE_TARGET 0x3081
#define EGL_MIPMAP_TEXTURE 0x3082
#define EGL_MIPMAP_LEVEL 0x3083
#define EGL_RENDER_BUFFER 0x3086
#define EGL_VG_COLORSPACE 0x3087
#define EGL_VG_ALPHA_FORMAT 0x3088
#define EGL_HORIZONTAL_RESOLUTION 0x3090
#define EGL_VERTICAL_RESOLUTION 0x3091
#define EGL_PIXEL_ASPECT_RATIO 0x3092
#define EGL_SWAP_BEHAVIOR 0x3093
#define EGL_MULTISAMPLE_RESOLVE 0x3099
/* EGL_RENDER_BUFFER values / BindTexImage / ReleaseTexImage buffer targets */
#define EGL_BACK_BUFFER 0x3084
#define EGL_SINGLE_BUFFER 0x3085
/* OpenVG color spaces */
#define EGL_VG_COLORSPACE_sRGB 0x3089 /* EGL_VG_COLORSPACE value */
#define EGL_VG_COLORSPACE_LINEAR 0x308A /* EGL_VG_COLORSPACE value */
/* OpenVG alpha formats */
#define EGL_VG_ALPHA_FORMAT_NONPRE 0x308B /* EGL_ALPHA_FORMAT value */
#define EGL_VG_ALPHA_FORMAT_PRE 0x308C /* EGL_ALPHA_FORMAT value */
/* Constant scale factor by which fractional display resolutions &
* aspect ratio are scaled when queried as integer values.
*/
#define EGL_DISPLAY_SCALING 10000
/* Unknown display resolution/aspect ratio */
#define EGL_UNKNOWN ((EGLint)-1)
/* Back buffer swap behaviors */
#define EGL_BUFFER_PRESERVED 0x3094 /* EGL_SWAP_BEHAVIOR value */
#define EGL_BUFFER_DESTROYED 0x3095 /* EGL_SWAP_BEHAVIOR value */
/* CreatePbufferFromClientBuffer buffer types */
#define EGL_OPENVG_IMAGE 0x3096
/* QueryContext targets */
#define EGL_CONTEXT_CLIENT_TYPE 0x3097
/* CreateContext attributes */
#define EGL_CONTEXT_CLIENT_VERSION 0x3098
/* Multisample resolution behaviors */
#define EGL_MULTISAMPLE_RESOLVE_DEFAULT 0x309A /* EGL_MULTISAMPLE_RESOLVE value */
#define EGL_MULTISAMPLE_RESOLVE_BOX 0x309B /* EGL_MULTISAMPLE_RESOLVE value */
/* BindAPI/QueryAPI targets */
#define EGL_OPENGL_ES_API 0x30A0
#define EGL_OPENVG_API 0x30A1
#define EGL_OPENGL_API 0x30A2
/* GetCurrentSurface targets */
#define EGL_DRAW 0x3059
#define EGL_READ 0x305A
/* WaitNative engines */
#define EGL_CORE_NATIVE_ENGINE 0x305B
/* EGL 1.2 tokens renamed for consistency in EGL 1.3 */
#define EGL_COLORSPACE EGL_VG_COLORSPACE
#define EGL_ALPHA_FORMAT EGL_VG_ALPHA_FORMAT
#define EGL_COLORSPACE_sRGB EGL_VG_COLORSPACE_sRGB
#define EGL_COLORSPACE_LINEAR EGL_VG_COLORSPACE_LINEAR
#define EGL_ALPHA_FORMAT_NONPRE EGL_VG_ALPHA_FORMAT_NONPRE
#define EGL_ALPHA_FORMAT_PRE EGL_VG_ALPHA_FORMAT_PRE
/* EGL extensions must request enum blocks from the Khronos
* API Registrar, who maintains the enumerant registry. Submit
* a bug in Khronos Bugzilla against task "Registry".
*/
/* EGL Functions */
EGLAPI EGLint EGLAPIENTRY eglGetError(void);
EGLAPI EGLDisplay EGLAPIENTRY eglGetDisplay(EGLNativeDisplayType display_id);
EGLAPI EGLBoolean EGLAPIENTRY eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor);
EGLAPI EGLBoolean EGLAPIENTRY eglTerminate(EGLDisplay dpy);
EGLAPI const char * EGLAPIENTRY eglQueryString(EGLDisplay dpy, EGLint name);
EGLAPI EGLBoolean EGLAPIENTRY eglGetConfigs(EGLDisplay dpy, EGLConfig *configs,
EGLint config_size, EGLint *num_config);
EGLAPI EGLBoolean EGLAPIENTRY eglChooseConfig(EGLDisplay dpy, const EGLint *attrib_list,
EGLConfig *configs, EGLint config_size,
EGLint *num_config);
EGLAPI EGLBoolean EGLAPIENTRY eglGetConfigAttrib(EGLDisplay dpy, EGLConfig config,
EGLint attribute, EGLint *value);
EGLAPI EGLSurface EGLAPIENTRY eglCreateWindowSurface(EGLDisplay dpy, EGLConfig config,
EGLNativeWindowType win,
const EGLint *attrib_list);
EGLAPI EGLSurface EGLAPIENTRY eglCreatePbufferSurface(EGLDisplay dpy, EGLConfig config,
const EGLint *attrib_list);
EGLAPI EGLSurface EGLAPIENTRY eglCreatePixmapSurface(EGLDisplay dpy, EGLConfig config,
EGLNativePixmapType pixmap,
const EGLint *attrib_list);
EGLAPI EGLBoolean EGLAPIENTRY eglDestroySurface(EGLDisplay dpy, EGLSurface surface);
EGLAPI EGLBoolean EGLAPIENTRY eglQuerySurface(EGLDisplay dpy, EGLSurface surface,
EGLint attribute, EGLint *value);
EGLAPI EGLBoolean EGLAPIENTRY eglBindAPI(EGLenum api);
EGLAPI EGLenum EGLAPIENTRY eglQueryAPI(void);
EGLAPI EGLBoolean EGLAPIENTRY eglWaitClient(void);
EGLAPI EGLBoolean EGLAPIENTRY eglReleaseThread(void);
EGLAPI EGLSurface EGLAPIENTRY eglCreatePbufferFromClientBuffer(
EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer,
EGLConfig config, const EGLint *attrib_list);
EGLAPI EGLBoolean EGLAPIENTRY eglSurfaceAttrib(EGLDisplay dpy, EGLSurface surface,
EGLint attribute, EGLint value);
EGLAPI EGLBoolean EGLAPIENTRY eglBindTexImage(EGLDisplay dpy, EGLSurface surface, EGLint buffer);
EGLAPI EGLBoolean EGLAPIENTRY eglReleaseTexImage(EGLDisplay dpy, EGLSurface surface, EGLint buffer);
EGLAPI EGLBoolean EGLAPIENTRY eglSwapInterval(EGLDisplay dpy, EGLint interval);
EGLAPI EGLContext EGLAPIENTRY eglCreateContext(EGLDisplay dpy, EGLConfig config,
EGLContext share_context,
const EGLint *attrib_list);
EGLAPI EGLBoolean EGLAPIENTRY eglDestroyContext(EGLDisplay dpy, EGLContext ctx);
EGLAPI EGLBoolean EGLAPIENTRY eglMakeCurrent(EGLDisplay dpy, EGLSurface draw,
EGLSurface read, EGLContext ctx);
EGLAPI EGLContext EGLAPIENTRY eglGetCurrentContext(void);
EGLAPI EGLSurface EGLAPIENTRY eglGetCurrentSurface(EGLint readdraw);
EGLAPI EGLDisplay EGLAPIENTRY eglGetCurrentDisplay(void);
EGLAPI EGLBoolean EGLAPIENTRY eglQueryContext(EGLDisplay dpy, EGLContext ctx,
EGLint attribute, EGLint *value);
EGLAPI EGLBoolean EGLAPIENTRY eglWaitGL(void);
EGLAPI EGLBoolean EGLAPIENTRY eglWaitNative(EGLint engine);
EGLAPI EGLBoolean EGLAPIENTRY eglSwapBuffers(EGLDisplay dpy, EGLSurface surface);
EGLAPI EGLBoolean EGLAPIENTRY eglCopyBuffers(EGLDisplay dpy, EGLSurface surface,
EGLNativePixmapType target);
/* This is a generic function pointer type, whose name indicates it must
* be cast to the proper type *and calling convention* before use.
*/
typedef void (*__eglMustCastToProperFunctionPointerType)(void);
/* Now, define eglGetProcAddress using the generic function ptr. type */
EGLAPI __eglMustCastToProperFunctionPointerType EGLAPIENTRY
eglGetProcAddress(const char *procname);
#ifdef __cplusplus
}
#endif
#endif /* __egl_h_ */

View File

@ -0,0 +1,124 @@
#ifndef __eglplatform_h_
#define __eglplatform_h_
/*
** Copyright (c) 2007-2009 The Khronos Group Inc.
**
** Permission is hereby granted, free of charge, to any person obtaining a
** copy of this software and/or associated documentation files (the
** "Materials"), to deal in the Materials without restriction, including
** without limitation the rights to use, copy, modify, merge, publish,
** distribute, sublicense, and/or sell copies of the Materials, and to
** permit persons to whom the Materials are furnished to do so, subject to
** the following conditions:
**
** The above copyright notice and this permission notice shall be included
** in all copies or substantial portions of the Materials.
**
** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
*/
/* Platform-specific types and definitions for egl.h
* $Revision: 12306 $ on $Date: 2010-08-25 09:51:28 -0700 (Wed, 25 Aug 2010) $
*
* Adopters may modify khrplatform.h and this file to suit their platform.
* You are encouraged to submit all modifications to the Khronos group so that
* they can be included in future versions of this file. Please submit changes
* by sending them to the public Khronos Bugzilla (http://khronos.org/bugzilla)
* by filing a bug against product "EGL" component "Registry".
*/
#include <APPLE/khrplatform.h>
/* Macros used in EGL function prototype declarations.
*
* EGL functions should be prototyped as:
*
* EGLAPI return-type EGLAPIENTRY eglFunction(arguments);
* typedef return-type (EXPAPIENTRYP PFNEGLFUNCTIONPROC) (arguments);
*
* KHRONOS_APICALL and KHRONOS_APIENTRY are defined in KHR/khrplatform.h
*/
#ifndef EGLAPI
#define EGLAPI KHRONOS_APICALL
#endif
#ifndef EGLAPIENTRY
#define EGLAPIENTRY KHRONOS_APIENTRY
#endif
#define EGLAPIENTRYP EGLAPIENTRY*
/* The types NativeDisplayType, NativeWindowType, and NativePixmapType
* are aliases of window-system-dependent types, such as X Display * or
* Windows Device Context. They must be defined in platform-specific
* code below. The EGL-prefixed versions of Native*Type are the same
* types, renamed in EGL 1.3 so all types in the API start with "EGL".
*
* Khronos STRONGLY RECOMMENDS that you use the default definitions
* provided below, since these changes affect both binary and source
* portability of applications using EGL running on different EGL
* implementations.
*/
#if defined(_WIN32) || defined(__VC32__) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__) /* Win32 and WinCE */
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN 1
#endif
#include <windows.h>
typedef HDC EGLNativeDisplayType;
typedef HBITMAP EGLNativePixmapType;
typedef HWND EGLNativeWindowType;
#elif defined(__WINSCW__) || defined(__SYMBIAN32__) /* Symbian */
typedef int EGLNativeDisplayType;
typedef void *EGLNativeWindowType;
typedef void *EGLNativePixmapType;
#elif defined(__unix__)
/* X11 (tentative) */
#include <X11/Xlib.h>
#include <X11/Xutil.h>
typedef Display *EGLNativeDisplayType;
typedef Pixmap EGLNativePixmapType;
typedef Window EGLNativeWindowType;
#elif defined(__GNUC__) && ( defined(__APPLE_CPP__) || defined(__APPLE_CC__) || defined(__MACOS_CLASSIC__) )
// this line is taken from the TargetConditionals.h file in any on the iOS or Mac OS X platforms
struct appleEGLWindow;
typedef int EGLNativeDisplayType;
typedef void *EGLNativePixmapType;
typedef struct appleEGLWindow *EGLNativeWindowType;
#else
#error "Platform not recognized"
#endif
/* EGL 1.2 types, renamed for consistency in EGL 1.3 */
typedef EGLNativeDisplayType NativeDisplayType;
typedef EGLNativePixmapType NativePixmapType;
typedef EGLNativeWindowType NativeWindowType;
/* Define EGLint. This must be a signed integral type large enough to contain
* all legal attribute names and values passed into and out of EGL, whether
* their type is boolean, bitmask, enumerant (symbolic constant), integer,
* handle, or other. While in general a 32-bit integer will suffice, if
* handles are 64 bit types, then EGLint should be defined as a signed 64-bit
* integer type.
*/
typedef khronos_int32_t EGLint;
#endif /* __eglplatform_h */

View File

@ -0,0 +1,269 @@
#ifndef __khrplatform_h_
#define __khrplatform_h_
/*
** Copyright (c) 2008-2009 The Khronos Group Inc.
**
** Permission is hereby granted, free of charge, to any person obtaining a
** copy of this software and/or associated documentation files (the
** "Materials"), to deal in the Materials without restriction, including
** without limitation the rights to use, copy, modify, merge, publish,
** distribute, sublicense, and/or sell copies of the Materials, and to
** permit persons to whom the Materials are furnished to do so, subject to
** the following conditions:
**
** The above copyright notice and this permission notice shall be included
** in all copies or substantial portions of the Materials.
**
** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
*/
/* Khronos platform-specific types and definitions.
*
* $Revision: 9356 $ on $Date: 2009-10-21 02:52:25 -0700 (Wed, 21 Oct 2009) $
*
* Adopters may modify this file to suit their platform. Adopters are
* encouraged to submit platform specific modifications to the Khronos
* group so that they can be included in future versions of this file.
* Please submit changes by sending them to the public Khronos Bugzilla
* (http://khronos.org/bugzilla) by filing a bug against product
* "Khronos (general)" component "Registry".
*
* A predefined template which fills in some of the bug fields can be
* reached using http://tinyurl.com/khrplatform-h-bugreport, but you
* must create a Bugzilla login first.
*
*
* See the Implementer's Guidelines for information about where this file
* should be located on your system and for more details of its use:
* http://www.khronos.org/registry/implementers_guide.pdf
*
* This file should be included as
* #include <KHR/khrplatform.h>
* by Khronos client API header files that use its types and defines.
*
* The types in khrplatform.h should only be used to define API-specific types.
*
* Types defined in khrplatform.h:
* khronos_int8_t signed 8 bit
* khronos_uint8_t unsigned 8 bit
* khronos_int16_t signed 16 bit
* khronos_uint16_t unsigned 16 bit
* khronos_int32_t signed 32 bit
* khronos_uint32_t unsigned 32 bit
* khronos_int64_t signed 64 bit
* khronos_uint64_t unsigned 64 bit
* khronos_intptr_t signed same number of bits as a pointer
* khronos_uintptr_t unsigned same number of bits as a pointer
* khronos_ssize_t signed size
* khronos_usize_t unsigned size
* khronos_float_t signed 32 bit floating point
* khronos_time_ns_t unsigned 64 bit time in nanoseconds
* khronos_utime_nanoseconds_t unsigned time interval or absolute time in
* nanoseconds
* khronos_stime_nanoseconds_t signed time interval in nanoseconds
* khronos_boolean_enum_t enumerated boolean type. This should
* only be used as a base type when a client API's boolean type is
* an enum. Client APIs which use an integer or other type for
* booleans cannot use this as the base type for their boolean.
*
* Tokens defined in khrplatform.h:
*
* KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values.
*
* KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0.
* KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0.
*
* Calling convention macros defined in this file:
* KHRONOS_APICALL
* KHRONOS_APIENTRY
* KHRONOS_APIATTRIBUTES
*
* These may be used in function prototypes as:
*
* KHRONOS_APICALL void KHRONOS_APIENTRY funcname(
* int arg1,
* int arg2) KHRONOS_APIATTRIBUTES;
*/
/*-------------------------------------------------------------------------
* Definition of KHRONOS_APICALL
*-------------------------------------------------------------------------
* This precedes the return type of the function in the function prototype.
*/
#if defined(_WIN32) && !defined(__SCITECH_SNAP__)
# define KHRONOS_APICALL __declspec(dllimport)
#elif defined (__SYMBIAN32__)
# define KHRONOS_APICALL IMPORT_C
#else
# define KHRONOS_APICALL
#endif
/*-------------------------------------------------------------------------
* Definition of KHRONOS_APIENTRY
*-------------------------------------------------------------------------
* This follows the return type of the function and precedes the function
* name in the function prototype.
*/
#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__)
/* Win32 but not WinCE */
# define KHRONOS_APIENTRY __stdcall
#else
# define KHRONOS_APIENTRY
#endif
/*-------------------------------------------------------------------------
* Definition of KHRONOS_APIATTRIBUTES
*-------------------------------------------------------------------------
* This follows the closing parenthesis of the function prototype arguments.
*/
#if defined (__ARMCC_2__)
#define KHRONOS_APIATTRIBUTES __softfp
#else
#define KHRONOS_APIATTRIBUTES
#endif
/*-------------------------------------------------------------------------
* basic type definitions
*-----------------------------------------------------------------------*/
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__)
/*
* Using <stdint.h>
*/
#include <stdint.h>
typedef int32_t khronos_int32_t;
typedef uint32_t khronos_uint32_t;
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif defined(__VMS ) || defined(__sgi)
/*
* Using <inttypes.h>
*/
#include <inttypes.h>
typedef int32_t khronos_int32_t;
typedef uint32_t khronos_uint32_t;
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif defined(_WIN32) && !defined(__SCITECH_SNAP__)
/*
* Win32
*/
typedef __int32 khronos_int32_t;
typedef unsigned __int32 khronos_uint32_t;
typedef __int64 khronos_int64_t;
typedef unsigned __int64 khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif defined(__sun__) || defined(__digital__)
/*
* Sun or Digital
*/
typedef int khronos_int32_t;
typedef unsigned int khronos_uint32_t;
#if defined(__arch64__) || defined(_LP64)
typedef long int khronos_int64_t;
typedef unsigned long int khronos_uint64_t;
#else
typedef long long int khronos_int64_t;
typedef unsigned long long int khronos_uint64_t;
#endif /* __arch64__ */
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif 0
/*
* Hypothetical platform with no float or int64 support
*/
typedef int khronos_int32_t;
typedef unsigned int khronos_uint32_t;
#define KHRONOS_SUPPORT_INT64 0
#define KHRONOS_SUPPORT_FLOAT 0
#else
/*
* Generic fallback
*/
#include <stdint.h>
typedef int32_t khronos_int32_t;
typedef uint32_t khronos_uint32_t;
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#endif
/*
* Types that are (so far) the same on all platforms
*/
typedef signed char khronos_int8_t;
typedef unsigned char khronos_uint8_t;
typedef signed short int khronos_int16_t;
typedef unsigned short int khronos_uint16_t;
typedef signed long int khronos_intptr_t;
typedef unsigned long int khronos_uintptr_t;
typedef signed long int khronos_ssize_t;
typedef unsigned long int khronos_usize_t;
#if KHRONOS_SUPPORT_FLOAT
/*
* Float type
*/
typedef float khronos_float_t;
#endif
#if KHRONOS_SUPPORT_INT64
/* Time types
*
* These types can be used to represent a time interval in nanoseconds or
* an absolute Unadjusted System Time. Unadjusted System Time is the number
* of nanoseconds since some arbitrary system event (e.g. since the last
* time the system booted). The Unadjusted System Time is an unsigned
* 64 bit value that wraps back to 0 every 584 years. Time intervals
* may be either signed or unsigned.
*/
typedef khronos_uint64_t khronos_utime_nanoseconds_t;
typedef khronos_int64_t khronos_stime_nanoseconds_t;
#endif
/*
* Dummy value used to pad enum types to 32 bits.
*/
#ifndef KHRONOS_MAX_ENUM
#define KHRONOS_MAX_ENUM 0x7FFFFFFF
#endif
/*
* Enumerated boolean type
*
* Values other than zero should be considered to be true. Therefore
* comparisons should not be made against KHRONOS_TRUE.
*/
typedef enum {
KHRONOS_FALSE = 0,
KHRONOS_TRUE = 1,
KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM
} khronos_boolean_enum_t;
#endif /* __khrplatform_h_ */

4544
core/khronos/GL3/gl3.h Normal file

File diff suppressed because it is too large Load Diff

1340
core/khronos/GL3/gl3w.c Normal file

File diff suppressed because it is too large Load Diff

1234
core/khronos/GL3/gl3w.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -22,6 +22,12 @@
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xutil.h>
#if !defined(GLES)
#include <GL/gl.h>
#include <GL/glx.h>
#endif
#endif
#if !defined(ANDROID)
@ -476,6 +482,8 @@ void os_SetWindowText(const char * text)
}
void* x11_glc;
int ndcid=0;
void os_CreateWindow()
{
@ -508,15 +516,36 @@ void os_CreateWindow()
// Gets the window parameters
sRootWindow = RootWindow(x11Display, x11Screen);
i32Depth = DefaultDepth(x11Display, x11Screen);
x11Visual = new XVisualInfo;
XMatchVisualInfo( x11Display, x11Screen, i32Depth, TrueColor, x11Visual);
if (!x11Visual)
{
printf("Error: Unable to acquire visual\n");
return;
}
x11Colormap = XCreateColormap( x11Display, sRootWindow, x11Visual->visual, AllocNone );
int depth = CopyFromParent;
#if !defined(GLES)
int attr32[] = { GLX_RGBA, GLX_DEPTH_SIZE, 32, GLX_DOUBLEBUFFER, GLX_STENCIL_SIZE, 8, None };
int attr24[] = { GLX_RGBA, GLX_DEPTH_SIZE, 24, GLX_DOUBLEBUFFER, GLX_STENCIL_SIZE, 8, None };
XVisualInfo* vi = glXChooseVisual(x11Display, 0, attr32);
if (!vi)
vi = glXChooseVisual(x11Display, 0, attr24);
if (!vi)
die("Failed to glXChooseVisual");
depth = vi->depth;
x11Visual = vi;
x11Colormap = XCreateColormap(x11Display, RootWindow(x11Display, x11Screen), vi->visual, AllocNone);
#else
i32Depth = DefaultDepth(x11Display, x11Screen);
x11Visual = new XVisualInfo;
XMatchVisualInfo( x11Display, x11Screen, i32Depth, TrueColor, x11Visual);
if (!x11Visual)
{
printf("Error: Unable to acquire visual\n");
return;
}
x11Colormap = XCreateColormap( x11Display, sRootWindow, x11Visual->visual, AllocNone );
#endif
sWA.colormap = x11Colormap;
// Add to these for handling other events
@ -536,18 +565,24 @@ void os_CreateWindow()
width=XDisplayWidth(x11Display,x11Screen);
height=XDisplayHeight(x11Display,x11Screen);
}
// Creates the X11 window
x11Window = XCreateWindow( x11Display, RootWindow(x11Display, x11Screen), (ndcid%3)*640, (ndcid/3)*480, width, height,
0, CopyFromParent, InputOutput, CopyFromParent, ui32Mask, &sWA);
0, depth, InputOutput, x11Visual->visual, ui32Mask, &sWA);
#ifdef TARGET_PANDORA
// fullscreen
Atom wmState = XInternAtom(x11Display, "_NET_WM_STATE", False);
Atom wmFullscreen = XInternAtom(x11Display, "_NET_WM_STATE_FULLSCREEN", False);
XChangeProperty(x11Display, x11Window, wmState, XA_ATOM, 32, PropModeReplace, (unsigned char *)&wmFullscreen, 1);
XMapRaised(x11Display, x11Window);
// fullscreen
Atom wmState = XInternAtom(x11Display, "_NET_WM_STATE", False);
Atom wmFullscreen = XInternAtom(x11Display, "_NET_WM_STATE_FULLSCREEN", False);
XChangeProperty(x11Display, x11Window, wmState, XA_ATOM, 32, PropModeReplace, (unsigned char *)&wmFullscreen, 1);
XMapRaised(x11Display, x11Window);
#else
XMapWindow(x11Display, x11Window);
XMapWindow(x11Display, x11Window);
#if !defined(GLES)
x11_glc = glXCreateContext(x11Display, x11Visual, NULL, GL_TRUE);
//glXMakeCurrent(x11Display, x11Window, glc);
#endif
#endif
XFlush(x11Display);
@ -722,3 +757,9 @@ return 1;
int get_mic_data(u8* buffer) { return 0; }
int push_vmu_screen(u8* buffer) { return 0; }
void os_DebugBreak()
{
raise(SIGTRAP);
}

View File

@ -12,6 +12,8 @@
#include "hw/maple/maple_cfg.h"
#include "hw/sh4/sh4_mem.h"
#include "webui/server.h"
settings_t settings;
/*
@ -41,7 +43,7 @@ int GetFile(char *szFileName, char *szParse=0,u32 flags=0)
if (strcmp(szFileName,"null")==0)
{
#if defined(OMAP4)
strcpy(szFileName,GetPath("gdimage/crazy_taxi.chd").c_str());
strcpy(szFileName,GetPath("/gdimage/crazy_taxi.chd").c_str());
#else
#if HOST_OS==OS_WINDOWS
OPENFILENAME ofn;
@ -64,7 +66,7 @@ int GetFile(char *szFileName, char *szParse=0,u32 flags=0)
//strcpy(szFileName,ofn.lpstrFile);
}
#else
strcpy(szFileName,GetPath("discs/game.gdi").c_str());
strcpy(szFileName,GetPath("/discs/game.gdi").c_str());
#endif
#endif
}
@ -127,6 +129,17 @@ void plugins_Reset(bool Manual)
}
void* webui_th(void* p)
{
#if (HOST_OS == OS_WINDOWS || HOST_OS == OS_LINUX) && !defined(TARGET_PANDORA)
webui_start();
#endif
return 0;
}
cThread webui_thd(&webui_th,0);
int dc_init(int argc,wchar* argv[])
{
setbuf(stdin,0);
@ -138,6 +151,8 @@ int dc_init(int argc,wchar* argv[])
return -1;
}
webui_thd.Start();
if(ParseCommandLine(argc,argv))
{
return 69;
@ -155,9 +170,12 @@ int dc_init(int argc,wchar* argv[])
int rv= 0;
if (!LoadRomFiles(GetPath("/data/")))
if (settings.bios.UseReios || !LoadRomFiles(GetPath("/data/")))
{
return -3;
if (!LoadHle(GetPath("/data/")))
return -3;
else
printf("Did not load bios, using reios\n");
}
#if !defined(HOST_NO_REC)
@ -231,8 +249,14 @@ void LoadSettings()
settings.pvr.ta_skip = cfgLoadInt("config","ta.skip",0);
settings.pvr.rend = cfgLoadInt("config","pvr.rend",0);
settings.debug.SerialConsole = cfgLoadInt("config", "Debug.SerialConsoleEnabled", 0) != 0;
settings.reios.ElfFile = cfgLoadStr("reios", "ElfFile","");
#endif
settings.bios.UseReios = cfgLoadInt("config", "bios.UseReios", 0);
#if (HOST_OS != OS_LINUX || defined(_ANDROID) || defined(TARGET_PANDORA))
settings.aica.BufferSize=2048;
#else

View File

@ -28,4 +28,6 @@ u32 INLINE bitscanrev(u32 v)
}
//FIX ME
#define __assume(x)
#define __assume(x)
void os_DebugBreak();

View File

@ -67,6 +67,7 @@ extern u32 samples_gen;
void print_blocks();
#ifndef HOST_NO_REC
//called every emulated second
void prof_periodical()
{
@ -197,7 +198,9 @@ void prof_periodical()
printf("********************\n");
memset(&prof.counters,0,sizeof(prof.counters));
}
#else
void prof_periodical() { }
#endif
/*
debprof: a remote debugger/profiler

View File

@ -41,7 +41,11 @@ MOV R1, #1
B _Z8YUV_dataPjj
TAWriteSQ_vram: @vram write ..
#ifdef TARGET_IPHONE
bkpt #0
#else
bkpt
#endif
ubfx r0,r3,#5,#18 @ get vram offset
add r3,r1,#0x04000000 @ get vram ptr from r1, part 1
add r3,#512 @ get ram ptr from r1, part 2
@ -157,13 +161,23 @@ arm_compilecode:
bl CompileCode
b arm_dispatch
#ifdef TARGET_IPHONE
Xarm_Reg: .word arm_Reg
XEntryPoints: .word EntryPoints
#endif
.global arm_mainloop
arm_mainloop: @(cntx,lookup_base,cycles)
push {r4,r5,r8,r9,lr}
#ifdef TARGET_IPHONE
ldr r8,Xarm_Reg @load cntx
ldr r4,XEntryPoints @load lookup base
#else
ldr r8,=arm_Reg @load cntx
ldr r4,=EntryPoints @load lookup base
#endif
ldr r5,[r8,#192] @load cycle count
add r5,r0 @add cycles for this timeslice
@ -172,7 +186,12 @@ push {r4,r5,r8,r9,lr}
.global arm_dispatch
arm_dispatch:
#ifdef TARGET_IPHONE
ldrd r0,r1,[r8,#184] @load: Next PC, interrupt
#else
ldrd r0,[r8,#184] @load: Next PC, interrupt
#endif
ubfx r2,r0,#2,#19
cmp r1,#0
bne arm_dofiq

103
core/reios/descrambl.cpp Normal file
View File

@ -0,0 +1,103 @@
/*
Based on work of Marcus Comstedt
http://mc.pp.se, http://mc.pp.se/dc/sw.html, http://mc.pp.se/dc/files/scramble.c
License: Gotta verify
Adapted by Stefanos Kornilios Mitsis Poiitidis (skmp) for reicast
*/
#include "descrambl.h"
#include <algorithm>
#define MAXCHUNK (2048*1024)
static unsigned int seed;
void my_srand(unsigned int n)
{
seed = n & 0xffff;
}
unsigned int my_rand()
{
seed = (seed * 2109 + 9273) & 0x7fff;
return (seed + 0xc000) & 0xffff;
}
/*
void load(FILE *fh, unsigned char *ptr, unsigned long sz)
{
if (fread(ptr, 1, sz, fh) != sz)
{
fprintf(stderr, "Read error!\n");
exit(1);
}
}
*/
void load_chunk(u8* &src, unsigned char *ptr, unsigned long sz)
{
verify(sz <= MAXCHUNK);
static int idx[MAXCHUNK / 32];
int i;
/* Convert chunk size to number of slices */
sz /= 32;
/* Initialize index table with unity,
so that each slice gets loaded exactly once */
for (i = 0; i < sz; i++)
idx[i] = i;
for (i = sz - 1; i >= 0; --i)
{
/* Select a replacement index */
int x = (my_rand() * i) >> 16;
/* Swap */
swap(idx[i], idx[x]);
/*
int tmp = idx[i];
idx[i] = idx[x];
idx[x] = tmp;
*/
/* Load resulting slice */
//load(fh, ptr + 32 * idx[i], 32);
memcpy(ptr + 32 * idx[i], src, 32);
src += 32;
}
}
void descrambl_buffer(u8* src, unsigned char *dst, unsigned long filesz)
{
unsigned long chunksz;
my_srand(filesz);
/* Descramble 2 meg blocks for as long as possible, then
gradually reduce the window down to 32 bytes (1 slice) */
for (chunksz = MAXCHUNK; chunksz >= 32; chunksz >>= 1)
while (filesz >= chunksz)
{
load_chunk(src, dst, chunksz);
filesz -= chunksz;
dst += chunksz;
}
/* Load final incomplete slice */
if (filesz)
memcpy(dst, src, filesz);
}
void descrambl_file(u32 FAD, u32 file_size, u8* dst) {
u8* temp_file = new u8[file_size + 2048];
libGDR_ReadSector(temp_file, FAD, (file_size+2047) / 2048, 2048);
descrambl_buffer(temp_file, dst, file_size);
delete[] temp_file;
}

3
core/reios/descrambl.h Normal file
View File

@ -0,0 +1,3 @@
#include "types.h"
void descrambl_file(u32 FAD, u32 file_size, u8* dst);

224
core/reios/gdrom_hle.cpp Normal file
View File

@ -0,0 +1,224 @@
/*
Basic gdrom syscall emulation
Adapted from some (very) old pre-nulldc hle code
*/
#include <stdio.h>
#include "types.h"
#include "hw/sh4/sh4_if.h"
#include "hw/sh4/sh4_mem.h"
#include "gdrom_hle.h"
#define SWAP32(a) ((((a) & 0xff) << 24) | (((a) & 0xff00) << 8) | (((a) >> 8) & 0xff00) | (((a) >> 24) & 0xff))
//#define debugf printf
#define debugf(...)
void GDROM_HLE_ReadSES(u32 addr)
{
u32 s = ReadMem32(addr + 0);
u32 b = ReadMem32(addr + 4);
u32 ba = ReadMem32(addr + 8);
u32 bb = ReadMem32(addr + 12);
printf("GDROM_HLE_ReadSES: doing nothing w/ %d, %d, %d, %d\n", s, b, ba, bb);
}
void GDROM_HLE_ReadTOC(u32 Addr)
{
u32 s = ReadMem32(Addr + 0);
u32 b = ReadMem32(Addr + 4);
u32* pDst = (u32*)GetMemPtr(b, 0);
//
debugf("GDROM READ TOC : %X %X \n\n", s, b);
libGDR_GetToc(pDst, s);
//The syscall swaps to LE it seems
for (int i = 0; i < 102; i++) {
pDst[i] = SWAP32(pDst[i]);
}
}
void read_sectors_to(u32 addr, u32 sector, u32 count) {
u8 * pDst = GetMemPtr(addr, 0);
if (pDst) {
libGDR_ReadSector(pDst, sector, count, 2048);
}
else {
u8 temp[2048];
while (count > 0) {
libGDR_ReadSector(temp, sector, 1, 2048);
for (int i = 0; i < 2048 / 4; i += 4) {
WriteMem32(addr, temp[i]);
addr += 4;
}
sector++;
count--;
}
}
}
void GDROM_HLE_ReadDMA(u32 addr)
{
u32 s = ReadMem32(addr + 0x00);
u32 n = ReadMem32(addr + 0x04);
u32 b = ReadMem32(addr + 0x08);
u32 u = ReadMem32(addr + 0x0C);
debugf("GDROM:\tPIO READ Sector=%d, Num=%d, Buffer=0x%08X, Unk01=0x%08X\n", s, n, b, u);
read_sectors_to(b, s, n);
}
void GDROM_HLE_ReadPIO(u32 addr)
{
u32 s = ReadMem32(addr + 0x00);
u32 n = ReadMem32(addr + 0x04);
u32 b = ReadMem32(addr + 0x08);
u32 u = ReadMem32(addr + 0x0C);
debugf("GDROM:\tPIO READ Sector=%d, Num=%d, Buffer=0x%08X, Unk01=0x%08X\n", s, n, b, u);
read_sectors_to(b, s, n);
}
void GDCC_HLE_GETSCD(u32 addr) {
u32 s = ReadMem32(addr + 0x00);
u32 n = ReadMem32(addr + 0x04);
u32 b = ReadMem32(addr + 0x08);
u32 u = ReadMem32(addr + 0x0C);
printf("GDROM: Doing nothing for GETSCD [0]=%d, [1]=%d, [2]=0x%08X, [3]=0x%08X\n", s, n, b, u);
}
#define r Sh4cntx.r
u32 SecMode[4];
void GD_HLE_Command(u32 cc, u32 prm)
{
switch(cc)
{
case GDCC_GETTOC:
printf("GDROM:\t*FIXME* CMD GETTOC PRM:%X\n",cc,prm);
break;
case GDCC_GETTOC2:
GDROM_HLE_ReadTOC(r[5]);
break;
case GDCC_GETSES:
debugf("GDROM:\tGETSES PRM:%X\n", cc, prm);
GDROM_HLE_ReadSES(r[5]);
break;
case GDCC_INIT:
printf("GDROM:\tCMD INIT PRM:%X\n",cc,prm);
break;
case GDCC_PIOREAD:
GDROM_HLE_ReadPIO(r[5]);
break;
case GDCC_DMAREAD:
debugf("GDROM:\tCMD DMAREAD PRM:%X\n", cc, prm);
GDROM_HLE_ReadDMA(r[5]);
break;
case GDCC_PLAY_SECTOR:
printf("GDROM:\tCMD PLAYSEC? PRM:%X\n",cc,prm);
break;
case GDCC_RELEASE:
printf("GDROM:\tCMD RELEASE? PRM:%X\n",cc,prm);
break;
case GDCC_STOP: printf("GDROM:\tCMD STOP PRM:%X\n",cc,prm); break;
case GDCC_SEEK: printf("GDROM:\tCMD SEEK PRM:%X\n",cc,prm); break;
case GDCC_PLAY: printf("GDROM:\tCMD PLAY PRM:%X\n",cc,prm); break;
case GDCC_PAUSE:printf("GDROM:\tCMD PAUSE PRM:%X\n",cc,prm); break;
case GDCC_READ:
printf("GDROM:\tCMD READ PRM:%X\n",cc,prm);
break;
case GDCC_GETSCD:
debugf("GDROM:\tGETSCD PRM:%X\n",cc,prm);
GDCC_HLE_GETSCD(r[5]);
break;
default: printf("GDROM:\tUnknown GDROM CC:%X PRM:%X\n",cc,prm); break;
}
}
void gdrom_hle_op()
{
static u32 last_cmd = 0xFFFFFFFF; // only works for last cmd, might help somewhere
static u32 dwReqID=0xF0FFFFFF; // ReqID, starting w/ high val
if( SYSCALL_GDROM == r[6] ) // GDROM SYSCALL
{
switch(r[7]) // COMMAND CODE
{
// *FIXME* NEED RET
case GDROM_SEND_COMMAND: // SEND GDROM COMMAND RET: - if failed + req id
debugf("\nGDROM:\tHLE SEND COMMAND CC:%X param ptr: %X\n",r[4],r[5]);
GD_HLE_Command(r[4],r[5]);
last_cmd = r[0] = --dwReqID; // RET Request ID
break;
case GDROM_CHECK_COMMAND: //
r[0] = last_cmd == r[4] ? 2 : 0; // RET Finished : Invalid
debugf("\nGDROM:\tHLE CHECK COMMAND REQID:%X param ptr: %X -> %X\n", r[4], r[5], r[0]);
last_cmd = 0xFFFFFFFF; // INVALIDATE CHECK CMD
break;
// NO return, NO params
case GDROM_MAIN:
debugf("\nGDROM:\tHLE GDROM_MAIN\n");
break;
case GDROM_INIT: printf("\nGDROM:\tHLE GDROM_INIT\n"); break;
case GDROM_RESET: printf("\nGDROM:\tHLE GDROM_RESET\n"); break;
case GDROM_CHECK_DRIVE: //
debugf("\nGDROM:\tHLE GDROM_CHECK_DRIVE r4:%X\n",r[4],r[5]);
WriteMem32(r[4]+0,0x02); // STANDBY
WriteMem32(r[4]+4,libGDR_GetDiscType()); // CDROM | 0x80 for GDROM
r[0]=0; // RET SUCCESS
break;
case GDROM_ABORT_COMMAND: //
printf("\nGDROM:\tHLE GDROM_ABORT_COMMAND r4:%X\n",r[4],r[5]);
r[0]=-1; // RET FAILURE
break;
case GDROM_SECTOR_MODE: //
printf("GDROM:\tHLE GDROM_SECTOR_MODE PTR_r4:%X\n",r[4]);
for(int i=0; i<4; i++) {
SecMode[i] = ReadMem32(r[4]+(i<<2));
printf("%08X%s",SecMode[i],((3==i) ? "\n" : "\t"));
}
r[0]=0; // RET SUCCESS
break;
default: printf("\nGDROM:\tUnknown SYSCALL: %X\n",r[7]); break;
}
}
else // MISC
{
printf("SYSCALL:\tSYSCALL: %X\n",r[7]);
}
}

Some files were not shown because too many files have changed in this diff Show More