Merge master
This commit is contained in:
commit
5d3c71bee7
|
@ -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
|
||||
|
|
84
.travis.yml
84
.travis.yml
|
@ -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"
|
||||
|
|
65
README.md
65
README.md
|
@ -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.
|
||||
[](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 :)
|
||||
|
||||
[](https://bitdeli.com/free "Bitdeli Badge")
|
||||
|
||||
|
|
|
@ -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>
|
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
17
core/build.h
17
core/build.h
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
40
core/core.mk
40
core/core.mk
|
@ -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
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
#define __CHD_H__
|
||||
|
||||
#include "coretypes.h"
|
||||
#include "deps/coreio/coreio.h"
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -15,6 +15,4 @@ typedef s8 INT8;
|
|||
#define INLINE inline
|
||||
#endif
|
||||
|
||||
#define core_file FILE
|
||||
|
||||
#define ARRAY_LENGTH(x) (sizeof(x)/sizeof(x[0]))
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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).
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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!
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -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 ;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
#include "build.h"
|
||||
|
||||
#define LWS_BUILTIN_GETIFADDRS
|
||||
#define LWS_NO_DAEMONIZE
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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);
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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 */
|
|
@ -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
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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*/
|
|
@ -35,6 +35,7 @@ typedef u64 unat;
|
|||
//unused parameters
|
||||
#pragma warning( disable : 4100)
|
||||
|
||||
*/
|
||||
//basic includes from runtime lib
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#define clip_verify(x)
|
||||
#endif
|
||||
|
||||
//Sound generation, mixin, and channel regs emulation
|
||||
//x.15
|
||||
s32 volume_lut[16];
|
||||
//255 -> mute
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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); }
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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() { }
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 ®1,Sh4RegType ®2 , Sh4RegType ®3)
|
|||
}
|
||||
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
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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);
|
|
@ -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>
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
#pragma once
|
||||
#include "types.h"
|
||||
#include "sh4_rom.h"
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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_ */
|
|
@ -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 */
|
|
@ -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_ */
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -28,4 +28,6 @@ u32 INLINE bitscanrev(u32 v)
|
|||
}
|
||||
|
||||
//FIX ME
|
||||
#define __assume(x)
|
||||
#define __assume(x)
|
||||
|
||||
void os_DebugBreak();
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
#include "types.h"
|
||||
|
||||
void descrambl_file(u32 FAD, u32 file_size, u8* dst);
|
|
@ -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
Loading…
Reference in New Issue