├── .github
├── CODEOWNERS
└── workflows
│ └── secrets-scanning.yml
├── .gitleaksignore
├── .pre-commit-config.yaml
├── LICENSE
├── README.md
├── app
├── board.php
├── boctf.php
├── bootstrap.php
├── conf
│ ├── .htaccess
│ └── conf.php
├── dashboard
│ ├── dashjson.php
│ ├── index.php
│ ├── js
│ │ ├── security_dashboard.js
│ │ └── ss
│ │ │ ├── ss.js
│ │ │ ├── ss_communication_ajax.js
│ │ │ ├── ss_dom_css.js
│ │ │ ├── ss_utility_serialize.js
│ │ │ └── ss_utility_url.js
│ ├── styles.css
│ └── templates
│ │ ├── .htaccess
│ │ └── dashboard.html
├── dashjson.php
├── index.php
├── js
│ ├── security.js
│ ├── security_dashboard.js
│ ├── sendmsg.js
│ └── ss
│ │ ├── ss.js
│ │ ├── ss_communication_ajax.js
│ │ ├── ss_dom_css.js
│ │ ├── ss_utility_serialize.js
│ │ └── ss_utility_url.js
├── json.php
├── lib
│ ├── .htaccess
│ ├── Auth.php
│ ├── DB.php
│ ├── Game.php
│ └── Team.php
├── scripts
│ ├── .htaccess
│ ├── game.pem
│ └── normalizer.php
├── sendmessage.php
├── styles.css
├── templates
│ ├── .htaccess
│ ├── board.html
│ ├── boctf.html
│ ├── dashboard.html
│ └── index.html
└── websock
│ ├── .gitignore
│ ├── .htaccess
│ ├── app
│ └── server.js
│ ├── logs.sh
│ ├── old
│ ├── run_server.sh
│ ├── server.php
│ ├── websocket.admin.php
│ ├── websocket.client.php
│ ├── websocket.exceptions.php
│ ├── websocket.framing.php
│ ├── websocket.functions.php
│ ├── websocket.message.php
│ ├── websocket.resources.php
│ ├── websocket.server.php
│ └── websocket.users.php
│ ├── package.json
│ ├── start_websockets.sh
│ └── stop_websockets.sh
├── catalog-info.yaml
├── composer.json
├── composer.lock
├── composer.phar
├── docker-compose.yml
├── docker
├── nginx
│ ├── Dockerfile
│ └── default.conf
└── php
│ └── Dockerfile
├── screenshots
├── backoffice.png
├── public dashboard.png
└── team dashboard.png
├── sql
├── .htaccess
└── schema.sql
└── vendor
├── autoload.php
├── composer
├── ClassLoader.php
├── LICENSE
├── autoload_classmap.php
├── autoload_namespaces.php
├── autoload_psr4.php
├── autoload_real.php
├── autoload_static.php
└── installed.json
└── textalk
└── websocket
├── .coveralls.yml
├── .gitignore
├── .travis.yml
├── Makefile
├── README.md
├── composer.json
├── composer.lock
├── examples
├── echoserver.php
└── send.php
├── lib
├── BadOpcodeException.php
├── BadUriException.php
├── Base.php
├── Client.php
├── ConnectionException.php
├── Exception.php
└── Server.php
├── phpunit.xml.dist
└── tests
└── unit
└── ClientTest.php
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @tmendo
--------------------------------------------------------------------------------
/.github/workflows/secrets-scanning.yml:
--------------------------------------------------------------------------------
1 | name: Detect Secrets
2 | on:
3 | pull_request:
4 | push:
5 | workflow_dispatch:
6 | jobs:
7 | secrets-scan:
8 | uses: probely/snyk-prodsec/.github/workflows/secrets-scanning.yml@main
9 | with:
10 | channel: probely-alerts
11 | secrets:
12 | SLACK_BOT_TOKEN: ${{ secrets.SLACK_SECRET }}
13 | GITLEAKS_LICENSE: ${{ secrets.GITLEAKS_LICENSE }}
14 |
--------------------------------------------------------------------------------
/.gitleaksignore:
--------------------------------------------------------------------------------
1 | 7cb3c9f95e5057ec0e5ddbffbc7ac3ffb5b48369:app/scripts/ssl.key:private-key:1
2 |
--------------------------------------------------------------------------------
/.pre-commit-config.yaml:
--------------------------------------------------------------------------------
1 | repos:
2 | - repo: https://github.com/gitleaks/gitleaks
3 | rev: v8.24.2
4 | hooks:
5 | - id: gitleaks
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Pixels Camp Security CTF Dashboard
2 |
3 | This is the repo for the Pixels Camp Security CTF Dashboard, which includes
4 |
5 | * a public dashboard
6 | * a private team area to submit answers/follow progress
7 | * a backoffice for the organization
8 |
9 | Public dashboard:
10 | 
11 |
12 | Private team area:
13 | 
14 |
15 | Organization backoffice:
16 | 
17 |
18 | Main features of the dashboard
19 |
20 | * start/stop the CTF
21 | * pause the counter (adds minutes)
22 | * anouncements system to teams
23 | * logs of correct answer submissions (in the backoffice)
24 | * logs of submissions (through syslog)
25 |
26 |
27 | ## Setup
28 |
29 | Each team gets a token to login in their private area. Set them up at the ```teams``` table, [here](https://github.com/Probely/CTF-Game/blob/master/sql/schema.sql). Don't reuse the tokens or else teams from previous CTFs could login as an opponent team.
30 |
31 | Run ```docker-compose up```. This makes the dashboard available at localhost:8050
32 |
33 | The private team area is at [localhost:8050](http://localhost:8050). You can login with one of the tokens from the ```teams``` table.
34 |
35 | The backoffice will be running [here](http://localhost:8050/boctf.php). You should (really!) protect this URL with some basic auth, or some other kind of ACL.
36 |
--------------------------------------------------------------------------------
/app/board.php:
--------------------------------------------------------------------------------
1 | board($conf['qa']);
22 | $hints = $game->getHints();
23 | $secs2end = $game->endTS() - time();
24 | //$time2end = gmdate("H:i:s", $secs2end);
25 |
26 | $atleastoneopen = false;
27 | foreach ($board as $cat => $que) {
28 | if (in_array('', $que)) {
29 | $atleastoneopen = true;
30 | }
31 | if (array_search('open', $que)) {
32 | $opencat = $cat;
33 | $openq = array_search('open', $que);
34 | }
35 | }
36 |
37 | //echo "
";var_dump($atleastoneopen);var_dump($board);
38 |
39 | // Submitting an answer
40 | if (isset($_POST['answer']) && $secs2end>0) {
41 | $category = $_POST['category'];
42 | $question = $_POST['question'];
43 | // Checking if the question is open
44 | if ($board[$category][$question] == 'open' ||
45 | $board[$category][$question] == 'lead'
46 | ) {
47 |
48 | $answer = strtolower(trim($_POST['answer']));
49 | $answer = preg_replace("/\r|\n/", "", $answer);
50 |
51 | $isCorrectAnswer = $answer === strtolower($conf['qa'][$category][$question]['a']);
52 |
53 | openlog('CTF', LOG_ODELAY, LOG_DAEMON);
54 | syslog(LOG_CRIT, "Dashboard [Team: ".$team->id()."|".$team->ip()."|".$category."|".$question."|Correct: ".($isCorrectAnswer?'true':'false')."] - ".$answer);
55 |
56 | // Validating the answer
57 | if ($isCorrectAnswer) {
58 | $team->answer($category, $question);
59 | $notification = true;
60 | $notificationmsg = 'Hurray! That is the correct answer!';
61 |
62 | if ($atleastoneopen) {
63 | $notificationmsg = $notificationmsg . ' Pick another question from the list to carry on.';
64 | if ($game->resetLeadQuestion($category, $question)) {
65 | // non error
66 | }
67 | }
68 | $game->reloadGameInfo();
69 | } else {
70 | $error = true;
71 | $errormsg = 'Wrong answer. Try again :(';
72 | }
73 | }
74 | } else if (isset($_GET['newleadcat']) && isset($_GET['newleadq'])
75 | && $game->isSelectLeadMode() && $secs2end>0
76 | ) { // Selected a new lead question
77 | // Checking if the question is not opened
78 | if (empty($board[$_GET['newleadcat']][$_GET['newleadq']])) {
79 | if ($game->setNewLeadQuestion($_GET['newleadcat'], $_GET['newleadq'])) {
80 | // non error
81 | $category = $_GET['newleadcat'];
82 | $question = $_GET['newleadq'];
83 | }
84 | $game->reloadGameInfo();
85 | }
86 | } else { // Loading a question
87 | // Check if the question is open. If not, return lead question
88 | if (isset($_GET['cat']) && isset($_GET['q']) && $board[$_GET['cat']][$_GET['q']] == 'open') {
89 | $category = $_GET['cat'];
90 | $question = $_GET['q'];
91 | } else {
92 | $category = $game->leadCat();
93 | $question = $game->leadPoints();
94 | //echo "
"; var_dump($category,$question, $opencat, $openq); 95 | if ((isset($board[$category][$question]) && $board[$category][$question] == 'answered')) { 96 | if (!isset($opencat) && !isset($openq)) { 97 | $allanswered = true; 98 | } else { 99 | $category = $opencat; 100 | $question = $openq; 101 | } 102 | } 103 | } 104 | 105 | } 106 | 107 | // We need to retrieve the info again because of answered questions or new lead questions 108 | $board = $team->board($conf['qa']); 109 | 110 | // Select current question 111 | if (!empty($category) && !empty($question) 112 | && ($board[$category][$question] == 'open' || $board[$category][$question] == 'lead') 113 | ) { 114 | $board[$category][$question] = ($board[$category][$question] == 'lead') ? 'lead current' : 'current'; 115 | } 116 | 117 | // Make the leaderboard 118 | $leaderboard = $game->leaderBoard(); 119 | 120 | // Selecting a new lead question? 121 | if ($game->isSelectLeadMode()) { 122 | $board = $team->boardSelectLead($board); 123 | $notification = true; 124 | $notificationmsg = 'Open a new question below!'; 125 | $msg = 'You should open a new question from the board on the left!'; 126 | } 127 | 128 | // Board Template 129 | require 'templates/board.html'; 130 | 131 | /* 132 | echo "
"; 133 | echo ""; print_r($board); 134 | echo "
\n\n\n\n\n\n". $game->json($board); 135 | echo "
"; print_r($game->db->normalizer($conf['qa'])); 136 | */ 137 | -------------------------------------------------------------------------------- /app/boctf.php: -------------------------------------------------------------------------------- 1 | startGame($conf); 12 | } else if (isset($_POST['end'])) { 13 | $db->endGame(); 14 | } else if (isset($_POST['pause']) && isset($_POST['text'])) { 15 | $db->pauseGame(intval($_POST['text'])); 16 | } elseif (isset($_POST['addhint']) && isset($_POST['text'])) { 17 | $db->addNewHint(trim($_POST['text'])); 18 | } 19 | 20 | // Redirect page to avoid multiple submits on refresh 21 | if (!empty($_POST)) { 22 | // Force clients to reload 23 | $game->forceClientRefresh(); 24 | 25 | header('Location:'.$_SERVER['PHP_SELF'].'?'.$_SERVER['QUERY_STRING']); 26 | die(); 27 | } 28 | 29 | $board = $game->dashboard($conf['qa']); 30 | $secs2end = $game->endTS() - time(); 31 | 32 | // Make the leaderboard 33 | $leaderboard = $game->dashleaderBoard(); 34 | 35 | // Board Template 36 | require 'templates/boctf.html'; 37 | 38 | /* 39 | echo "
"; 40 | echo ""; print_r($board);print_r($leaderboard); 41 | echo "
\n\n\n\n\n\n". $game->dashjson($board); 42 | */ 43 | -------------------------------------------------------------------------------- /app/bootstrap.php: -------------------------------------------------------------------------------- 1 | Those guys at Nameless Corporation take everybody for fools, yet they believe nobody would ever target them. Nobody smart anyhow, from what I\'ve seen so far. 29 |
30 | They have a microservice lying around with data from relevant employees. Maybe I can gather some information from it and do a bit of social engineering... 31 |
32 | The service is listening athttp://w100-329074e6b126304d.ctf.rules
and I\'ve already mapped a few key methods. Also, I duped some helpdesk drone into resetting an account with my own password. Help me put these people to shame! 33 |
34 |GET /token
(basic auth, returns a temporary access token)
35 |GET /users
(returns all employees in the rolodex, as JSON)
36 |GET /users/1
(returns data for employee #1, in JSON, based on access rights)
37 |PUT /users/1
(replaces employee #1\'s data with the JSON in the request body) 38 |
39 | Requests to/users
require aX-API-Token
header ortoken=
query string parameter. 40 |
41 | The credentials for/token
are the same ones used for the CTF with an username matching the team number (e.g. use "team1" as the username for team #1, etc.).'; 42 | $conf['qa']['Web Hacking'][100]['a'] = 'xxxxxxx'; 43 | $conf['qa']['Web Hacking'][200]['q'] = ' Question 200 '; 44 | $conf['qa']['Web Hacking'][200]['a'] = ' Answer 200'; 45 | $conf['qa']['Web Hacking'][300]['q'] = ' Question 300'; 46 | $conf['qa']['Web Hacking'][300]['a'] = ' Answer 300'; 47 | $conf['qa']['Web Hacking'][400]['q'] = ' Question 400'; 48 | $conf['qa']['Web Hacking'][400]['a'] = ' Answer 400'; 49 | 50 | 51 | $conf['qa']['Forensics'][100]['q'] = ' Question 100 '; 52 | $conf['qa']['Forensics'][100]['a'] = ' Answer 100'; 53 | $conf['qa']['Forensics'][200]['q'] = ' Question 200 '; 54 | $conf['qa']['Forensics'][200]['a'] = ' Answer 200'; 55 | $conf['qa']['Forensics'][300]['q'] = ' Question 300 '; 56 | $conf['qa']['Forensics'][300]['a'] = ' Answer 300'; 57 | $conf['qa']['Forensics'][400]['q'] = ' Question 400'; 58 | $conf['qa']['Forensics'][400]['a'] = ' Answer 400'; 59 | 60 | $conf['qa']['Pwnable'][100]['q'] = ' Question 100 '; 61 | $conf['qa']['Pwnable'][100]['a'] = ' Answer 100'; 62 | $conf['qa']['Pwnable'][200]['q'] = ' Question 200 '; 63 | $conf['qa']['Pwnable'][200]['a'] = ' Answer 200'; 64 | $conf['qa']['Pwnable'][300]['q'] = ' Question 300 '; 65 | $conf['qa']['Pwnable'][300]['a'] = ' Answer 300'; 66 | $conf['qa']['Pwnable'][400]['q'] = ' Question 400'; 67 | $conf['qa']['Pwnable'][400]['a'] = ' Answer 400'; 68 | 69 | $conf['qa']['Trivia'][100]['q'] = ' Question 100 '; 70 | $conf['qa']['Trivia'][100]['a'] = ' Answer 100'; 71 | $conf['qa']['Trivia'][200]['q'] = ' Question 200 '; 72 | $conf['qa']['Trivia'][200]['a'] = ' Answer 200'; 73 | $conf['qa']['Trivia'][300]['q'] = ' Question 300 '; 74 | $conf['qa']['Trivia'][300]['a'] = ' Answer 300'; 75 | $conf['qa']['Trivia'][400]['q'] = ' Question 400'; 76 | $conf['qa']['Trivia'][400]['a'] = ' Answer 400'; 77 | 78 | -------------------------------------------------------------------------------- /app/dashboard/dashjson.php: -------------------------------------------------------------------------------- 1 | dashboard($conf['qa']); 10 | $secs2end = $game->endTS() - time(); 11 | 12 | echo $game->dashjson($board); 13 | -------------------------------------------------------------------------------- /app/dashboard/index.php: -------------------------------------------------------------------------------- 1 | dashboard($conf['qa']); 9 | $secs2end = $game->endTS() - time(); 10 | 11 | // Make the leaderboard 12 | $leaderboard = $game->dashleaderBoard(); 13 | 14 | // Board Template 15 | require 'templates/dashboard.html'; 16 | 17 | /* 18 | echo "
"; 19 | echo ""; print_r($board);print_r($leaderboard); 20 | echo "
\n\n\n\n\n\n". $game->dashjson($board); 21 | */ 22 | -------------------------------------------------------------------------------- /app/dashboard/js/security_dashboard.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | window.PixelsCampSecurityContestDashboard = function() 4 | { 5 | this.init(); 6 | } 7 | 8 | 9 | window.PixelsCampSecurityContestDashboard.prototype = { 10 | 11 | init: function() 12 | { 13 | this._debug = false; 14 | 15 | this.ajax = false; 16 | 17 | this.counterWSConnect = 0; 18 | 19 | this.timeToEnd = 0; 20 | this.timeElm = null; 21 | 22 | this._countDown(); 23 | 24 | this._processInfo(); 25 | 26 | this._runProcessInfoTimer(); 27 | 28 | }, 29 | 30 | _runProcessInfoTimer: function() 31 | { 32 | var setInt = setInterval(function(){ 33 | location.reload(); 34 | //this._processInfo(); 35 | }.bindObj(this), (1000 * 15)); // intervalo de update 36 | }, 37 | 38 | _countDown: function() 39 | { 40 | this.timeElm = document.getElementById('timer'); 41 | if(this.timeElm != null) { 42 | var toEnd = this.timeElm.getAttribute('time'); 43 | toEnd = parseInt(toEnd, 10); 44 | 45 | this.timeToEnd = toEnd; 46 | 47 | setInterval(function() { 48 | this._goingDown(); 49 | }.bindObj(this), 1000); 50 | 51 | if(this._debug) { 52 | console.log(toEnd); 53 | } 54 | } 55 | }, 56 | 57 | _blinkIt: function() 58 | { 59 | this.setIntBlink = setInterval(function(){ 60 | if(SAPO.Dom.Css.getStyle(this.timeElm, 'opacity') == '0.5') { 61 | SAPO.Dom.Css.setOpacity(this.timeElm, '1'); 62 | //this.timeElm.style.visibility = 'visible'; 63 | } else { 64 | SAPO.Dom.Css.setOpacity(this.timeElm, '0.5'); 65 | //this.timeElm.style.visibility = 'hidden'; 66 | } 67 | }.bindObj(this), 500); 68 | }, 69 | 70 | _goingDown: function() 71 | { 72 | this.timeToEnd--; 73 | 74 | if(this.timeToEnd <= 0) { 75 | this.timeToEnd = 0; 76 | this.timeElm.style.color = 'red'; 77 | } 78 | 79 | if(this.timeToEnd <= 61 && this.timeElm.style.color != 'red') { 80 | setTimeout(function() { 81 | this.timeElm.style.color = 'red'; 82 | }.bindObj(this), 1000); 83 | } 84 | 85 | var hours = false; 86 | var minutes = false; 87 | var seconds = false; 88 | 89 | var hours = Math.floor(this.timeToEnd / (60 * 60)); 90 | 91 | var divisor_for_minutes = this.timeToEnd % (60 * 60); 92 | var minutes = Math.floor(divisor_for_minutes / 60); 93 | 94 | var divisor_for_seconds = divisor_for_minutes % 60; 95 | var seconds = Math.ceil(divisor_for_seconds); 96 | 97 | hours = (hours < 10) ? '0'+hours : hours; 98 | minutes = (minutes < 10) ? '0'+minutes : minutes; 99 | seconds = (seconds < 10) ? '0'+seconds : seconds; 100 | 101 | this.timeElm.innerHTML = ' '+hours+':'+minutes+':'+seconds+' '; 102 | 103 | if(this.timeToEnd <= 0) { 104 | var elmToRemove = document.getElementById('current_question'); 105 | if(elmToRemove != null) { 106 | elmToRemove.parentNode.removeChild(elmToRemove); 107 | } 108 | } 109 | }, 110 | 111 | 112 | _addPageEvents: function() 113 | { 114 | return; 115 | }, 116 | 117 | _getUpdatedInfo: function() 118 | { 119 | if(this.ajax) { 120 | this.ajax.transport.abort(); 121 | this.ajax = false; 122 | } 123 | var opt = { 124 | method: 'GET', 125 | onSuccess: this._updateMatrix.bindObj(this) 126 | }; 127 | 128 | this.ajax = new SAPO.Communication.Ajax('dashjson.php', opt); 129 | }, 130 | 131 | _updateMatrix: function(obj) 132 | { 133 | var res = eval('['+obj.responseText+']'); 134 | if(res.length > 0) { 135 | res = res[0]; 136 | 137 | if(typeof(res.board) != 'undefined' && typeof(res.leaderboard) != 'undefined') { 138 | this._processMatrixInfo(res.board); 139 | this._processScoreInfo(res.leaderboard); 140 | this._processTimeInfo(res.time); 141 | } 142 | if(this._debug) { 143 | console.log(res); 144 | } 145 | } 146 | this.ajax = false; 147 | }, 148 | 149 | _processInfo: function(data) 150 | { 151 | this._getUpdatedInfo(); 152 | }, 153 | 154 | _processMatrixInfo: function(data) 155 | { 156 | //return false; 157 | if(typeof(data) == 'object') { 158 | var curKey = false; 159 | var curClass = false; 160 | var aCurTeams = false; 161 | for(var i in data) { 162 | for(var j in data[i]) { 163 | curKey = i+'_'+j; 164 | curKey = curKey.toLowerCase(); 165 | curKey = curKey.replace(/\ /g, ''); 166 | 167 | curClass = data[i][j]['class']; 168 | aCurTeams = data[i][j]['teams']; 169 | 170 | var elm = document.getElementById(curKey); 171 | if(elm) { 172 | 173 | switch(curClass) { 174 | case 'open': 175 | elm.className = 'open'; 176 | var str = ''+j+'
'; 177 | if(aCurTeams.length > 0) { 178 | str += 'Teams Completed:
'+j+'
'+data+''; 247 | } 248 | }, 249 | 250 | 251 | debug: function(){} 252 | 253 | }; 254 | 255 | 256 | -------------------------------------------------------------------------------- /app/dashboard/js/ss/ss.js: -------------------------------------------------------------------------------- 1 | 2 | if(typeof SAPO==='undefined'){window.SAPO={};}else{window.SAPO=window.SAPO;} 3 | SAPO.namespace=function(ns){if(!ns||!ns.length){return null;} 4 | var levels=ns.split(".");var nsobj=SAPO;for(var i=(levels[0]==="SAPO")?1:0;i
'+j+'
'; 178 | if(aCurTeams.length > 0) { 179 | str += ''+j+'
'+data+''; 248 | } 249 | }, 250 | 251 | 252 | debug: function(){} 253 | 254 | }; 255 | 256 | 257 | -------------------------------------------------------------------------------- /app/js/sendmsg.js: -------------------------------------------------------------------------------- 1 | //var host = 'wss://game.sec.codebits.eu:12345/echo'; 2 | var host = 'wss://game-ctf.pixels.camp/ws'; 3 | var socket = false; 4 | 5 | function startWebsockets() { 6 | if(("WebSocket" in window)) { 7 | socket = new WebSocket(host); 8 | } /*else if(("MozWebSocket" in window)) { 9 | socket = new MozWebSocket(host); 10 | }*/ else { 11 | alert('your browser does not support websockets... choose another on :]'); 12 | } 13 | 14 | if(!socket) { 15 | alert('error'); 16 | } 17 | 18 | socket.onopen = function() { 19 | console.log('connected'); 20 | _runPing(); 21 | }; 22 | socket.onmessage = function(m) { console.log('message received'); }; 23 | socket.onclose = function() { 24 | console.log('closed'); 25 | setTimeout(function() { 26 | console.log('Reconnecting...'); 27 | startWebsockets(); 28 | }, 3000); 29 | }; 30 | } 31 | startWebsockets(); 32 | 33 | function _runPing() { 34 | socket.send('ping'); 35 | setTimeout(function() { 36 | _runPing(); 37 | }, 30000); 38 | } 39 | 40 | function sendMessage(event) { 41 | 42 | if(event.preventDefault) { 43 | event.preventDefault(); 44 | } 45 | if(window.attachEvent) { 46 | event.returnValue = false; 47 | } 48 | if(event.cancel !== null) { 49 | event.cancel = true; 50 | } 51 | 52 | var elm = document.getElementById('text_message'); 53 | if(elm == null) { 54 | return false; 55 | } 56 | 57 | var msgToSend = elm.value; 58 | 59 | var oObj = { 60 | sekjdfSAEwelnfsdWT: msgToSend 61 | }; 62 | 63 | //var str = SAPO.Utility.Serialize.get(oObj); 64 | var str = JSON.stringify(oObj); 65 | 66 | //alert('will send: '+str); 67 | 68 | socket.send(str); 69 | 70 | return false; 71 | } 72 | -------------------------------------------------------------------------------- /app/js/ss/ss.js: -------------------------------------------------------------------------------- 1 | 2 | if(typeof SAPO==='undefined'){window.SAPO={};}else{window.SAPO=window.SAPO;} 3 | SAPO.namespace=function(ns){if(!ns||!ns.length){return null;} 4 | var levels=ns.split(".");var nsobj=SAPO;for(var i=(levels[0]==="SAPO")?1:0;i
"; var_dump($category,$question, $opencat, $openq); 36 | if ((isset($board[$category][$question]) && $board[$category][$question] == 'answered')) { 37 | if (!isset($opencat) && !isset($openq)) { 38 | $allanswered = true; 39 | } else { 40 | $category = $opencat; 41 | $question = $openq; 42 | } 43 | } 44 | } 45 | 46 | if (!empty($category) && !empty($question) 47 | && ($board[$category][$question] == 'open' || $board[$category][$question] == 'lead') 48 | ) { 49 | $board[$category][$question] = ($board[$category][$question] == 'lead') ? 'lead current' : 'current'; 50 | } 51 | 52 | if ($game->isSelectLeadMode()) { 53 | $board = $team->boardSelectLead($board); 54 | } 55 | 56 | $board['selectmode'] = $game->isSelectLeadMode(); 57 | $board['allanswered'] = $allanswered; 58 | 59 | $secs2end = $game->endTS() - time(); 60 | if ($secs2end<=0) { 61 | $board['allanswered'] = true; 62 | } 63 | 64 | echo $game->json($board); 65 | -------------------------------------------------------------------------------- /app/lib/.htaccess: -------------------------------------------------------------------------------- 1 | order deny,allow 2 | deny from all 3 | -------------------------------------------------------------------------------- /app/lib/Auth.php: -------------------------------------------------------------------------------- 1 | db = $db; 14 | if (isset($_SESSION['teamid']) && isset($_SESSION['team'])) { 15 | $this->teamid = $_SESSION['teamid']; 16 | $this->team = $_SESSION['team']; 17 | $this->authip = $_SESSION['authip']; 18 | } 19 | } 20 | 21 | public function login($key) 22 | { 23 | $team = $this->db->getAuth($key); 24 | if (!$team) { 25 | return false; 26 | } 27 | $this->team = $team['team']; 28 | $this->teamid = $team['id']; 29 | $this->authip = $_SERVER['REMOTE_ADDR']; 30 | $_SESSION['authip'] = $this->authip; 31 | $_SESSION['teamid'] = $this->teamid; 32 | $_SESSION['team'] = $this->team; 33 | return true; 34 | } 35 | 36 | public static function check() 37 | { 38 | //echo "";print_r($_SESSION);print_r($_COOKIE); 39 | return (isset($_SESSION['teamid']) && isset($_SESSION['team']) && 40 | isset($_SESSION['authip']) && $_SESSION['authip'] == $_SERVER['REMOTE_ADDR']); 41 | } 42 | 43 | public static function ip_check() 44 | { 45 | global $conf; 46 | 47 | $ip=''; 48 | // Use cloudflare ip first (XXX: WARNING This should be removed if not using cloudflare!) 49 | if (array_key_exists('HTTP_CF_CONNECTING_IP', $_SERVER)) { 50 | $ip = $_SERVER['HTTP_CF_CONNECTING_IP']; 51 | } else { // Otherwise, remote addr 52 | $ip = $_SERVER['REMOTE_ADDR']; 53 | } 54 | 55 | foreach($conf['whitelist'] as $ipregexp) { 56 | if(preg_match($ipregexp, $ip) == 1) { 57 | return true; 58 | } 59 | } 60 | 61 | return false; 62 | } 63 | 64 | public function getTeamId() 65 | { 66 | return $this->teamid; 67 | } 68 | 69 | public function getTeam() 70 | { 71 | return $this->team; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /app/lib/Game.php: -------------------------------------------------------------------------------- 1 | db = $db; 23 | $this->teamid = isset($_SESSION['teamid']) ? $_SESSION['teamid'] : null; 24 | $this->team = isset($_SESSION['team']) ? $_SESSION['team'] : null; 25 | 26 | $this->reloadGameInfo(); 27 | } 28 | 29 | public function leadCat() 30 | { 31 | return $this->leadcat; 32 | } 33 | 34 | public function leadPoints() 35 | { 36 | return $this->leadpoints; 37 | } 38 | 39 | public function endTS() 40 | { 41 | return $this->end; 42 | } 43 | 44 | public function isSelectLeadMode() 45 | { 46 | return ($this->teamid == $this->yellowshirt && empty($this->leadcat) && empty($this->leadpoints)); 47 | } 48 | 49 | public function leaderBoard() 50 | { 51 | $this->leaderboard = (is_array($this->leaderboard) ? $this->leaderboard : $this->db->getLeaderBoard($this->teamid)); 52 | return $this->leaderboard; 53 | } 54 | 55 | public function dashleaderBoard() 56 | { 57 | $this->dashleaderboard = (is_array($this->leaderboard) ? $this->leaderboard : $this->db->getLeaderBoard($this->yellowshirt)); 58 | return $this->dashleaderboard; 59 | } 60 | 61 | // Game Dashboard 62 | public function json($board) 63 | { 64 | $secs2end = $this->endTS() - time(); 65 | return json_encode(array( 66 | 'board' => $board, 67 | 'leaderboard' => $this->leaderBoard(), 68 | 'time' => $secs2end, 69 | 'hints' => $this->_hints 70 | )); 71 | } 72 | 73 | // Public Dashboard 74 | public function dashjson($board) 75 | { 76 | $secs2end = $this->endTS() - time(); 77 | return json_encode(array( 78 | 'board' => $board, 79 | 'leaderboard' => $this->dashleaderBoard(), 80 | 'time' => $secs2end, 81 | 'hints' => $this->_hints 82 | )); 83 | } 84 | 85 | public function electNewLeadQuestion() 86 | { 87 | // check lowest point questions and rand 88 | // getOpenQuestions(), new array w/1st element cat=>points, sort array by value 89 | } 90 | 91 | public function resetLeadQuestion($postcat, $postquestion) 92 | { 93 | $response = $this->db->resetLeadQuestion($this->teamid, $postcat, $postquestion); 94 | $this->forceClientRefresh(); 95 | return $response; 96 | } 97 | 98 | public function setNewLeadQuestion($leadcat, $leadquestion) 99 | { 100 | $response = $this->db->setNewLeadQuestion($this->teamid, $leadcat, $leadquestion); 101 | $this->forceClientRefresh(); 102 | return $response; 103 | } 104 | 105 | public function reloadGameInfo() 106 | { 107 | $gameinfo = $this->db->getGameInfo(); 108 | $this->leadcat = $gameinfo['leadcat']; 109 | $this->leadpoints = $gameinfo['leadpoints']; 110 | $this->end = $gameinfo['end']; 111 | $this->yellowshirt = $gameinfo['yellowshirt']; 112 | $this->lastleadreset = $gameinfo['lastleadreset']; 113 | $this->_hints = $gameinfo['hints']; 114 | } 115 | 116 | public function getHints() 117 | { 118 | return $this->_hints; 119 | } 120 | 121 | public function dashboard($questions) 122 | { 123 | $board = array(); 124 | $stat = $this->db->getDashboard($this->leadCat(), $this->leadPoints()); 125 | foreach ($questions as $cat => $q) { 126 | foreach ($q as $points => $qa) { 127 | $board[$cat][$points]['class'] = (isset($stat[$cat][$points]['class'])) ? $stat[$cat][$points]['class'] : 'closed'; 128 | $board[$cat][$points]['teams'] = (isset($stat[$cat][$points]['teams'])) ? $stat[$cat][$points]['teams'] : ''; 129 | } 130 | } 131 | return $board; 132 | } 133 | 134 | public function closedQuestions() 135 | { 136 | $q = $this->db->getClosedQuestions(); 137 | return $q; 138 | } 139 | 140 | // Force clients to reload 141 | public function forceClientRefresh() 142 | { 143 | global $conf; 144 | 145 | try { 146 | // Connect to websocket 147 | $client = new Client($conf['websocket']); 148 | // Send empty message 149 | $client->send(json_encode(array($conf['websocket-key'] => NULL))); 150 | } catch (Exception $e) { 151 | return false; 152 | } 153 | return true; 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /app/lib/Team.php: -------------------------------------------------------------------------------- 1 | game = $game; 15 | $this->db = $this->game->db; 16 | $this->teamid = $_SESSION['teamid']; 17 | $this->team = $_SESSION['team']; 18 | } 19 | 20 | public function name() 21 | { 22 | return $this->team; 23 | } 24 | 25 | public function id() 26 | { 27 | return $this->teamid; 28 | } 29 | public function points() 30 | { 31 | return (isset($this->points) ? $this->points : $this->db->getTeamPoints($this->teamid)); 32 | } 33 | 34 | public function board($questions) 35 | { 36 | $board = array(); 37 | $stat = $this->db->getTeamStatusQuestions($this->teamid, $this->game->leadCat(), $this->game->leadPoints()); 38 | foreach ($questions as $cat => $q) { 39 | foreach ($q as $points => $qa) { 40 | $board[$cat][$points] = (isset($stat[$cat][$points])) ? $stat[$cat][$points] : ''; 41 | } 42 | } 43 | return $board; 44 | } 45 | 46 | public function boardSelectLead($questions) 47 | { 48 | $board = array(); 49 | foreach ($questions as $cat => $q) { 50 | foreach ($q as $points => $qa) { 51 | if (empty($questions[$cat][$points])) { 52 | $board[$cat][$points] = 'select_lead'; 53 | } else if ($questions[$cat][$points] == 'open') { 54 | $board[$cat][$points] = 'openstatic'; 55 | } else { 56 | $board[$cat][$points] = $questions[$cat][$points]; 57 | } 58 | } 59 | } 60 | return $board; 61 | } 62 | 63 | public function answer($cat, $question) 64 | { 65 | return $this->db->insertAnswer($this->teamid, $cat, $question); 66 | } 67 | 68 | public function ip() 69 | { 70 | $ipaddress = $_SERVER['REMOTE_ADDR']; 71 | if (isset($_SERVER['HTTP_CLIENT_IP'])) { 72 | $ipaddress .= ",".$_SERVER['HTTP_CLIENT_IP']; 73 | } else if(isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { 74 | $ipaddress .= ",".$_SERVER['HTTP_X_FORWARDED_FOR']; 75 | } else if(isset($_SERVER['HTTP_X_FORWARDED'])) { 76 | $ipaddress .= ",".$_SERVER['HTTP_X_FORWARDED']; 77 | } else if(isset($_SERVER['HTTP_FORWARDED_FOR'])) { 78 | $ipaddress .= ",".$_SERVER['HTTP_FORWARDED_FOR']; 79 | } else if(isset($_SERVER['HTTP_FORWARDED'])) { 80 | $ipaddress .= ",".$_SERVER['HTTP_FORWARDED']; 81 | } else if(isset($_SERVER['REMOTE_ADDR'])) { 82 | $ipaddress .= ",".$_SERVER['REMOTE_ADDR']; 83 | } 84 | 85 | return $ipaddress; 86 | } 87 | 88 | 89 | } 90 | -------------------------------------------------------------------------------- /app/scripts/.htaccess: -------------------------------------------------------------------------------- 1 | order deny,allow 2 | deny from all 3 | -------------------------------------------------------------------------------- /app/scripts/game.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIHJTCCBg2gAwIBAgIDBKAgMA0GCSqGSIb3DQEBBQUAMIGMMQswCQYDVQQGEwJJ 3 | TDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0 4 | YWwgQ2VydGlmaWNhdGUgU2lnbmluZzE4MDYGA1UEAxMvU3RhcnRDb20gQ2xhc3Mg 5 | MSBQcmltYXJ5IEludGVybWVkaWF0ZSBTZXJ2ZXIgQ0EwHhcNMTExMTA0MTkzNzIx 6 | WhcNMTIxMTA0MTU1ODIwWjBoMSAwHgYDVQQNExc1NTY0MTEtNm8zdnhKTXhaNlYy 7 | RjBWaDEdMBsGA1UEAxMUZ2FtZS5zZWMuY29kZWJpdHMuZXUxJTAjBgkqhkiG9w0B 8 | CQEWFmhvc3RtYXN0ZXJAY29kZWJpdHMuZXUwggEiMA0GCSqGSIb3DQEBAQUAA4IB 9 | DwAwggEKAoIBAQCzFnL7kyg55vF+e2efNCCvvA44vv5mzlHygI7eqnTPT1PrAtPp 10 | /v28m/ue9dK69MxmZ6wbQQd4mxE/1Roz8IkUW0nIppdA+AhQoMNtMHNVGzbQe7Xi 11 | ame1fu1WPwlxpyHMDSawUXs37K/eP6hAJUAqbjdDamjHBpzI90KEZ9nlpZhoP3Ue 12 | /PfY5hBEggyZiZHGtKHO3y3c2M62zkYeiissAVOJnVnU9RrtfQgfzIk97IfmXGGr 13 | dk0VJrViQn8aAtMgMETwcfF8HeEcN81BpAY/92icSArQBqjFYGdqQp0O8ikjcMCk 14 | l1zXNdHal5TYawSKLRNbnkZNZ0zAVb4uNwxnAgMBAAGjggOxMIIDrTAJBgNVHRME 15 | AjAAMAsGA1UdDwQEAwIDqDATBgNVHSUEDDAKBggrBgEFBQcDATAdBgNVHQ4EFgQU 16 | lZjcaV5hUFuPsH8B9RoIdRl/MnMwHwYDVR0jBBgwFoAU60I00Jiwq5/0G2sI98xk 17 | Lu8OLEUwLAYDVR0RBCUwI4IUZ2FtZS5zZWMuY29kZWJpdHMuZXWCC2NvZGViaXRz 18 | LmV1MIICIQYDVR0gBIICGDCCAhQwggIQBgsrBgEEAYG1NwECAjCCAf8wLgYIKwYB 19 | BQUHAgEWImh0dHA6Ly93d3cuc3RhcnRzc2wuY29tL3BvbGljeS5wZGYwNAYIKwYB 20 | BQUHAgEWKGh0dHA6Ly93d3cuc3RhcnRzc2wuY29tL2ludGVybWVkaWF0ZS5wZGYw 21 | gfcGCCsGAQUFBwICMIHqMCcWIFN0YXJ0Q29tIENlcnRpZmljYXRpb24gQXV0aG9y 22 | aXR5MAMCAQEagb5UaGlzIGNlcnRpZmljYXRlIHdhcyBpc3N1ZWQgYWNjb3JkaW5n 23 | IHRvIHRoZSBDbGFzcyAxIFZhbGlkYXRpb24gcmVxdWlyZW1lbnRzIG9mIHRoZSBT 24 | dGFydENvbSBDQSBwb2xpY3ksIHJlbGlhbmNlIG9ubHkgZm9yIHRoZSBpbnRlbmRl 25 | ZCBwdXJwb3NlIGluIGNvbXBsaWFuY2Ugb2YgdGhlIHJlbHlpbmcgcGFydHkgb2Js 26 | aWdhdGlvbnMuMIGcBggrBgEFBQcCAjCBjzAnFiBTdGFydENvbSBDZXJ0aWZpY2F0 27 | aW9uIEF1dGhvcml0eTADAgECGmRMaWFiaWxpdHkgYW5kIHdhcnJhbnRpZXMgYXJl 28 | IGxpbWl0ZWQhIFNlZSBzZWN0aW9uICJMZWdhbCBhbmQgTGltaXRhdGlvbnMiIG9m 29 | IHRoZSBTdGFydENvbSBDQSBwb2xpY3kuMDUGA1UdHwQuMCwwKqAooCaGJGh0dHA6 30 | Ly9jcmwuc3RhcnRzc2wuY29tL2NydDEtY3JsLmNybDCBjgYIKwYBBQUHAQEEgYEw 31 | fzA5BggrBgEFBQcwAYYtaHR0cDovL29jc3Auc3RhcnRzc2wuY29tL3N1Yi9jbGFz 32 | czEvc2VydmVyL2NhMEIGCCsGAQUFBzAChjZodHRwOi8vYWlhLnN0YXJ0c3NsLmNv 33 | bS9jZXJ0cy9zdWIuY2xhc3MxLnNlcnZlci5jYS5jcnQwIwYDVR0SBBwwGoYYaHR0 34 | cDovL3d3dy5zdGFydHNzbC5jb20vMA0GCSqGSIb3DQEBBQUAA4IBAQBCNlLbjOXD 35 | 0MIa1hiqxRDs3OTTJysmO9pb8ORHrcWqJZuHypb2zrgFZQreTXCmtrizwwk7wHAT 36 | h1PNxmaMDZ5FsSuZDoyOF8LOA8Am8+VmDT1iwU+GSiMU/THWXEC8gva0ecCeK6yJ 37 | uJ8ugoF4BddiXqh/IKWc+NI7PoJ3rnR2m9i1eKx3jXMdiXTPQyIInenoEP9dpQih 38 | L3bXyU8rq8kRdrgmDyL9HBZbL+P11piTX/VlkLkv4iHnnglOoFxPEGGZahEvMJz+ 39 | XdRE1camHNmg/XK7DKn69du3X7xYheHA3sUHJIbA7YnmaCP6hvPulKL5N1UUwDan 40 | fDpyMYpyxZuT 41 | -----END CERTIFICATE----- 42 | -------------------------------------------------------------------------------- /app/scripts/normalizer.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/php -q 2 | normalizer($conf['qa']); 9 | print_r($lead); 10 | -------------------------------------------------------------------------------- /app/sendmessage.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 46 | 47 | 48 | 49 | 50 |Send Message
51 |
52 |53 | Send a message to all users:57 | 58 | 59 | -------------------------------------------------------------------------------- /app/templates/.htaccess: -------------------------------------------------------------------------------- 1 | order deny,allow 2 | deny from all 3 | -------------------------------------------------------------------------------- /app/templates/board.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
54 |
55 | 56 |Scoreboard 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |16 |18 | 19 |Team: name() ." (Team ". $team->id() . ")"; ?>
17 |20 |22 | 23 |Your Score: points(); ?>
21 |24 | 25 |26 | 27 | 28 |29 | 30 |31 | 32 | 33 | 34 |35 | 36 |37 | 38 | 39 |40 | $q) { ?> 41 | 42 |92 | 93 | 94 | 95 | 96 | 97 | 129 | 130 | 131 | 142 | 143 | 144 | -------------------------------------------------------------------------------- /app/templates/boctf.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |43 | 44 |64 | 65 | 66 | 67 | 68 | 69 |45 | $status) { 48 | $id = preg_replace("/\s/", "", strtolower($cat)) . "_" . $points; 49 | if ($status == 'open' || $status == 'openstatic' || $status == 'lead' || $status == 'lead current') { 50 | if ($game->isSelectLeadMode()) { 51 | echo "\t\t\t\t\t
63 |- $points
\n"; 52 | } else { 53 | echo "\t\t\t\t\t- $points
\n"; 54 | } 55 | } else if ($status == 'select_lead' && $game->isSelectLeadMode()){ 56 | echo "\t\t\t\t\t- $points
\n"; 57 | } else { 58 | echo "\t\t\t\t\t- $points
\n"; 59 | } 60 | } 61 | ?> 62 |70 |76 | 77 | 78 |71 |
75 |- = Lead Question
72 |- = Open
73 |- = Answered
74 |style="display: none" > 79 |90 | 91 |Messages:
80 |81 | 0) { 83 | foreach ($hints as $hint) { 84 | echo "89 |\n"; 85 | } 86 | } 87 | ?> 88 | ${hint['hint']}
Scoreboard 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |18 |20 | 21 |Security Contest BO
19 |22 | 23 |24 | 25 |26 | 30 | 35 | 36 |37 |38 | 42 | 43 |44 | 45 | 46 |47 |58 | 59 |48 |
57 |49 | 50 | listAllAnswers() as $k => $answer) { 52 | echo " Category Question Points TeamID Answer TS "; 53 | } 54 | ?> 55 | 56 | ".$answer['cat']." ".$answer['question']." ". $answer['points'] . " " . $answer['teamid'] . " " . $answer['answeredts'] . " 60 |71 | 72 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /app/templates/dashboard.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |61 |
70 |62 | 63 | closedQuestions() as $k => $answer) { 65 | echo " Category Question TeamID Answer TS "; 66 | } 67 | ?> 68 | 69 | ".$answer['cat']." ".$answer['question']. " " . $answer['teamid'] . " " . $answer['answeredts'] . " Scoreboard 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |16 |18 | 19 |Security Contest Scoreboard
17 |20 | 21 |22 | 23 |24 | 25 | $q) { ?> 26 | 27 |59 | 60 | 77 | 78 |28 | 29 |49 | 50 |30 | $status) { 33 | $id = preg_replace("/\s/", "", strtolower($cat)) . "_" . $points; 34 | if ($status['class'] == 'open') { 35 | echo "\t\t\t\t\t
48 |- \n"; 40 | } else if ($status['class'] == 'lead') { 41 | echo "\t\t\t\t\t
$points
Teams Completed:
"; 36 | foreach ($status['teams'] as $k=>$teamid) { 37 | echo "
- $teamid
"; 38 | } 39 | echo "- \n"; 42 | } else { 43 | echo "\t\t\t\t\t
$points
Lead Question
- \n"; 44 | } 45 | } 46 | ?> 47 |
$points
51 |56 | 57 | 58 |52 |
55 |- = Lead Question
53 |- = Open
54 |79 | Updated every 5 seconds 80 |81 | 82 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /app/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |Scoreboard 7 | 8 | 9 | 10 | 11 |12 |14 | 15 |Invalid Login!
13 |16 | 21 |22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/websock/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /app/websock/.htaccess: -------------------------------------------------------------------------------- 1 | order deny,allow 2 | deny from all 3 | -------------------------------------------------------------------------------- /app/websock/app/server.js: -------------------------------------------------------------------------------- 1 | 2 | var config = { 3 | port: 31310 4 | }; 5 | var WebSocketServer = require('ws').Server; 6 | 7 | var wss = new WebSocketServer({port: config.port}, function(a) { 8 | console.log('Running on port ' + config.port); 9 | }); 10 | 11 | 12 | 13 | wss.on('connection', function connection(ws) { 14 | 15 | ws.on('message', function(message) { 16 | console.log('NEW MESSAGE', message); 17 | try { 18 | var aMsg = JSON.parse(message); 19 | } catch(ex) { 20 | var aMsg = message; 21 | } 22 | var isObj = false; 23 | var adminMessage; 24 | if(typeof(aMsg) !== 'object') { 25 | return; 26 | } 27 | if(typeof(aMsg) === 'object' && 'sekjdfSAEwelnfsdWT' in aMsg) { 28 | adminMessage = aMsg.sekjdfSAEwelnfsdWT; 29 | } 30 | 31 | var objToSend = {reload: true}; 32 | if(adminMessage) { 33 | objToSend.msg = adminMessage; 34 | } 35 | wss.clients.forEach(function(wsCli) { 36 | if(wsCli.readyState === wsCli.OPEN) { 37 | wsCli.send(JSON.stringify(objToSend)); 38 | } 39 | }); 40 | //console.log('received: ', aMsg); 41 | }); 42 | 43 | ws.on('close', function(ws) { 44 | console.log('User left server... Users online: ' + wss.clients.length); 45 | }); 46 | //ws.send({msg: 'Hello user :) '); 47 | 48 | console.log('NEw user connected... Total users: '+ wss.clients.length); 49 | }); 50 | 51 | 52 | -------------------------------------------------------------------------------- /app/websock/logs.sh: -------------------------------------------------------------------------------- 1 | ./node_modules/pm2/bin/pm2 logs 2 | -------------------------------------------------------------------------------- /app/websock/old/run_server.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | php -q server.php 4 | 5 | -------------------------------------------------------------------------------- /app/websock/old/server.php: -------------------------------------------------------------------------------- 1 | php demo.php 5 | require_once("websocket.server.php"); 6 | 7 | /** 8 | * This demo resource handler will respond to all messages sent to /echo/ on the socketserver below 9 | * 10 | * All this handler does is echoing the responds to the user 11 | * @author Chris 12 | * 13 | */ 14 | class DemoEchoHandler extends WebSocketResourceHandler{ 15 | public function onMessage(IWebSocketUser $user, IWebSocketMessage $msg){ 16 | $this->say("[ECHO] {$msg->getData()}"); 17 | 18 | $received = $msg->getData(); 19 | 20 | $aRec = json_decode($received); 21 | 22 | $this->say("[DEBUG_MSG] ".print_r($aRec, true).""); 23 | 24 | if(is_object($aRec) && isset($aRec->sekjdfSAEwelnfsdWT)) { 25 | $adminMessage = (string) $aRec->sekjdfSAEwelnfsdWT; 26 | } else { 27 | $adminMessage = false; 28 | } 29 | 30 | foreach($this->users as $userr) { 31 | if(!empty($adminMessage)) { 32 | $this->send($userr, json_encode(array('reload' => true, 'msg' => $adminMessage))); 33 | } else { 34 | $this->send($userr, json_encode(array('reload' => true))); 35 | } 36 | } 37 | } 38 | 39 | public function onAdminMessage(IWebSocketUser $user, stdClass $obj){ 40 | $this->say("[DEMO] Admin TEST received!"); 41 | 42 | $frame = WebSocketFrame::create(WebSocketOpcode::PongFrame); 43 | $this->server->sendFrame($user, $frame); 44 | } 45 | } 46 | 47 | /** 48 | * Demo socket server. Implements the basic eventlisteners and attaches a resource handler for /echo/ urls. 49 | * 50 | * 51 | * @author Chris 52 | * 53 | */ 54 | class DemoSocketServer extends WebSocketServer{ 55 | protected $debug = true; 56 | 57 | public function getAdminKey(){ 58 | return "superdupersecretkey"; 59 | } 60 | 61 | public function __construct($address, $port){ 62 | parent::__construct($address, $port); 63 | 64 | $this->addResourceHandler("echo", new DemoEchoHandler()); 65 | } 66 | protected function onConnect(IWebSocketUser $user){ 67 | $this->say("[DEMO] {$user->getId()} connected"); 68 | } 69 | 70 | public function onMessage($user, IWebSocketMessage $msg){ 71 | $this->say("[DEMO] {$user->getId()} says '{$msg->getData()}'"); 72 | } 73 | 74 | protected function onDisconnect(IWebSocketUser $user){ 75 | $this->say("[DEMO] {$user->getId()} disconnected"); 76 | } 77 | 78 | protected function onAdminTest(IWebSocketUser $user){ 79 | $this->say("[DEMO] Admin TEST received!"); 80 | 81 | $frame = WebSocketFrame::create(WebSocketOpcode::PongFrame); 82 | $this->sendFrame($user, $frame); 83 | } 84 | } 85 | 86 | // Start server 87 | //$server = new DemoSocketServer(0,12345); 88 | $server = new DemoSocketServer('0.0.0.0',54321); 89 | $server->run(); 90 | -------------------------------------------------------------------------------- /app/websock/old/websocket.admin.php: -------------------------------------------------------------------------------- 1 | adminKey = $adminKey; 11 | 12 | $this->addHeader("Admin-Key", $adminKey); 13 | } 14 | 15 | 16 | public function sendMessage(WebSocketAdminMessage $msg){ 17 | $wsmsg = WebSocketMessage::create(json_encode($msg)); 18 | 19 | parent::sendMessage($wsmsg); 20 | } 21 | } 22 | 23 | /** 24 | * Helper class to send Admin Messages to the WebSocketServer 25 | * 26 | * Makes the server execute onAdminXXXX() events 27 | * 28 | * @author Chris 29 | * 30 | */ 31 | class WebSocketAdminMessage extends stdClass{ 32 | public $task = null; 33 | 34 | private function __construct(){ 35 | 36 | } 37 | 38 | /** 39 | * Create a message that will be send to the instance of the WebSocketServer 40 | * 41 | * @param string $task 42 | * @return WebSocketAdminMessage 43 | */ 44 | public static function create($task){ 45 | $o = new self(); 46 | $o->task = $task; 47 | 48 | return $o; 49 | } 50 | } -------------------------------------------------------------------------------- /app/websock/old/websocket.client.php: -------------------------------------------------------------------------------- 1 | url = $url; 22 | 23 | if(in_array($parts['scheme'], array('ws')) === false) 24 | throw new WebSocketInvalidUrlScheme(); 25 | 26 | $this->host = $parts['host']; 27 | $this->port = $parts['port']; 28 | 29 | $this->origin = 'http://'.$this->host; 30 | 31 | if(isset($parts['path'])) 32 | $this->requestUri = $parts['path']; 33 | else $this->requestUri = "/"; 34 | 35 | if(isset($parts['query'])) 36 | $this->requestUri .= "?".$parts['query']; 37 | 38 | $this->socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); 39 | $this->setTimeOut(1); 40 | 41 | $this->buildHeaderArray(); 42 | } 43 | 44 | public function setTimeOut($seconds){ 45 | socket_set_option($this->socket,SOL_SOCKET, SO_RCVTIMEO, array("sec" => $seconds, "usec" => 0)); 46 | socket_set_option($this->socket,SOL_SOCKET, SO_SNDTIMEO, array("sec" => $seconds, "usec" => 0)); 47 | } 48 | 49 | /** 50 | * TODO: Proper header generation! 51 | * TODO: Check server response! 52 | */ 53 | public function open(){ 54 | socket_connect($this->socket, $this->host, $this->port); 55 | 56 | $buffer = $this->serializeHeaders(); 57 | 58 | socket_write($this->socket, $buffer, strlen($buffer)); 59 | 60 | // wait for response 61 | $buffer = socket_read($this->socket, 2048,PHP_BINARY_READ); 62 | $headers = WebSocketFunctions::parseHeaders($buffer); 63 | 64 | if($headers['Sec-Websocket-Accept'] != WebSocketFunctions::calcHybiResponse($this->handshakeChallenge)){ 65 | return false; 66 | } 67 | 68 | return true; 69 | } 70 | 71 | private function serializeHeaders(){ 72 | $str = ''; 73 | 74 | foreach($this->headers as $k => $v){ 75 | $str .= $k." ".$v."\r\n"; 76 | } 77 | 78 | return $str; 79 | } 80 | 81 | public function addHeader($key, $value){ 82 | $this->headers[$key.":"] = $value; 83 | } 84 | 85 | protected function buildHeaderArray(){ 86 | $this->handshakeChallenge = WebSocketFunctions::randHybiKey(); 87 | 88 | $this->headers = array( 89 | "GET" => "{$this->url} HTTP/1.1", 90 | "Connection:" => "Upgrade", 91 | "Host:" => "{$this->host}:{$this->port}", 92 | "Sec-WebSocket-Key:" => "{$this->handshakeChallenge}", 93 | "Sec-WebSocket-Origin:" => "{$this->origin}", 94 | "Sec-WebSocket-Version:" => 8, 95 | "Upgrade:" => "websocket" 96 | ); 97 | 98 | return $headers; 99 | } 100 | 101 | public function send($string){ 102 | $msg = WebSocketMessage::create($string); 103 | 104 | $this->sendMessage($msg); 105 | } 106 | 107 | public function sendMessage(IWebSocketMessage $msg){ 108 | // Sent all fragments 109 | foreach($msg->getFrames() as $frame){ 110 | $this->sendFrame($frame); 111 | } 112 | } 113 | 114 | public function sendFrame(IWebSocketFrame $frame){ 115 | $msg = $frame->encode(); 116 | socket_write($this->socket, $msg,strlen($msg)); 117 | } 118 | 119 | public function readFrame(){ 120 | $data = socket_read($this->socket,2048,PHP_BINARY_READ); 121 | 122 | if($data === false) 123 | return null; 124 | 125 | return WebSocketFrame::decode($data); 126 | } 127 | 128 | public function readMessage(){ 129 | $frame = $this->readFrame(); 130 | 131 | if($frame != null) 132 | $msg = WebSocketMessage::fromFrame($frame); 133 | else return null; 134 | 135 | while($msg->isFinalised() == false){ 136 | $frame = $this->readFrame(); 137 | 138 | if($frame != null) 139 | $msg->takeFrame($this->readFrame()); 140 | else return null; 141 | } 142 | 143 | return $msg; 144 | } 145 | 146 | public function close(){ 147 | socket_close($this->socket); 148 | } 149 | 150 | } -------------------------------------------------------------------------------- /app/websock/old/websocket.exceptions.php: -------------------------------------------------------------------------------- 1 | user = $user; 32 | } 33 | } 34 | 35 | class WebSocketInvalidKeyException extends Exception{ 36 | public function __construct($key1, $key2, $l8b){ 37 | parent::__construct("Client sent an invalid opening handshake!"); 38 | fwrite(STDERR, "Key 1: \t$key1\nKey 2: \t$key2\nL8b: \t$l8b"); 39 | } 40 | } -------------------------------------------------------------------------------- /app/websock/old/websocket.framing.php: -------------------------------------------------------------------------------- 1 | FIN = true; 103 | $o->payloadData = $data; 104 | $o->payloadLength = $data != null ? strlen($data) : 0; 105 | $o->setType($type); 106 | 107 | 108 | return $o; 109 | } 110 | 111 | public function isMasked(){ 112 | return $this->mask == 1; 113 | } 114 | 115 | protected function setType($type){ 116 | $this->opcode = $type; 117 | 118 | if($type == WebSocketOpcode::CloseFrame) 119 | $this->mask = 1; 120 | } 121 | 122 | protected static function IsBitSet($byte, $pos) 123 | { 124 | return ($byte & pow(2,$pos)) > 0 ? 1 : 0; 125 | } 126 | 127 | protected static function rotMask($data, $key){ 128 | $res = ''; 129 | for($i = 0; $i < strlen($data); $i++){ 130 | $j = $i % 4; 131 | 132 | $res .= chr(ord($data[$i]) ^ ord($key[$j])); 133 | } 134 | 135 | return $res; 136 | } 137 | 138 | public function getType(){ 139 | return $this->opcode; 140 | } 141 | 142 | public function encode(){ 143 | $this->payloadLength = strlen($this->payloadData); 144 | 145 | 146 | $firstByte = $this->opcode; 147 | 148 | $firstByte += 149 | $this->FIN * 128 150 | + $this->RSV1 * 64 151 | + $this->RSV2 * 32 152 | + $this->RSV3 * 16; 153 | 154 | $encoded = chr($firstByte); 155 | 156 | if($this->payloadLength <= 125){ 157 | $secondByte = $this->payloadLength; 158 | $secondByte += $this->mask * 128; 159 | 160 | $encoded .= chr($secondByte); 161 | } else if($this->payloadLength <= 255*255 - 1){ 162 | $secondByte = 126; 163 | $secondByte += $this->mask * 128; 164 | 165 | $encoded .= chr($secondByte).pack("n", $this->payloadLength); 166 | } else { 167 | // TODO: max length is now 32 bits instead of 64 !!!!! 168 | $secondByte = 127; 169 | $secondByte += $this->mask * 128; 170 | 171 | $encoded .= chr($secondByte); 172 | $encoded .= pack("N",0); 173 | $encoded .= pack("N",$this->payloadLength); 174 | } 175 | 176 | $key = 0; 177 | if($this->mask){ 178 | $key = pack("N", rand(0, pow(255,4) - 1)); 179 | $encoded .= $key; 180 | 181 | } 182 | 183 | if($this->payloadData) 184 | $encoded .= ($this->mask == 1) ? $this->rotMask($this->payloadData,$key) : $this->payloadData; 185 | 186 | return $encoded; 187 | } 188 | 189 | public static function decode($raw){ 190 | $frame = new self(); 191 | 192 | // Read the first two bytes, then chop them off 193 | list($firstByte, $secondByte) = substr($raw,0,2); 194 | $raw = substr($raw,2); 195 | 196 | $firstByte = ord($firstByte); 197 | $secondByte = ord($secondByte); 198 | 199 | $frame->FIN = self::IsBitSet($firstByte, 7); 200 | $frame->RSV1 = self::IsBitSet($firstByte, 6); 201 | $frame->RSV2 = self::IsBitSet($firstByte, 5); 202 | $frame->RSV3 = self::IsBitSet($firstByte, 4); 203 | 204 | $frame->mask = self::IsBitSet($secondByte, 7); 205 | 206 | $frame->opcode = ($firstByte & 0x0F); 207 | 208 | $len = $secondByte & ~128; 209 | 210 | 211 | if($len <= 125) 212 | $frame->payloadLength = $len; 213 | elseif($len == 126){ 214 | list($frame->payloadLength) = unpack("nfirst", $raw); 215 | $raw = substr($raw,2); 216 | } elseif($len = 127) { 217 | list($frame->payloadLength) = unpack("nfirst", $raw); 218 | $raw = substr($raw,4); 219 | } 220 | 221 | if($frame->mask){ 222 | $frame->maskingKey = substr($raw,0,4); 223 | $raw = substr($raw,4); 224 | } 225 | 226 | if(strlen($raw) != $frame->payloadLength) 227 | throw new WebSocketFrameSizeMismatch($frame); 228 | 229 | if($frame->mask) 230 | $frame->payloadData = self::rotMask($raw, $frame->maskingKey); 231 | else $frame->payloadData = $raw; 232 | 233 | return $frame; 234 | } 235 | 236 | public function isFinal(){ 237 | return $this->FIN == 1; 238 | } 239 | 240 | public function getData(){ 241 | return $this->payloadData; 242 | } 243 | } 244 | 245 | class WebSocketFrame76 implements IWebSocketFrame{ 246 | public $payloadData = ''; 247 | protected $opcode = WebSocketOpcode::TextFrame; 248 | 249 | public static function create($type, $data = null){ 250 | $o = new self(); 251 | 252 | $o->payloadData = $data; 253 | 254 | return $o; 255 | } 256 | 257 | public function encode(){ 258 | return chr(0).$this->payloadData.chr(255); 259 | } 260 | 261 | public function getData(){ 262 | return $this->payloadData; 263 | } 264 | 265 | public function getType(){ 266 | return $this->opcode; 267 | } 268 | 269 | public static function decode($str){ 270 | $o = new self(); 271 | $o->payloadData = substr($str, 1, strlen($str) - 2); 272 | 273 | return $o; 274 | } 275 | 276 | } -------------------------------------------------------------------------------- /app/websock/old/websocket.functions.php: -------------------------------------------------------------------------------- 1 | setData($data); 65 | return $o; 66 | } 67 | 68 | public function getFrames(){ 69 | $arr = array(); 70 | 71 | $arr[] = $this->frame; 72 | 73 | return $arr; 74 | } 75 | 76 | public function setData($data){ 77 | $this->data = $data; 78 | $this->frame = WebSocketFrame76::create(WebSocketOpcode::TextFrame, $data); 79 | } 80 | public function getData(){ 81 | return $this->frame->getData(); 82 | } 83 | 84 | public function isFinalised(){ 85 | return true; 86 | } 87 | 88 | /** 89 | * Creates a new WebSocketMessage76 from a IWebSocketFrame 90 | * @param IWebSocketFrame $frame 91 | * 92 | * @return WebSocketMessage76 Message composed of the frame provided 93 | */ 94 | public static function fromFrame(IWebSocketFrame $frame){ 95 | $o = new self(); 96 | $o->frame = $frame; 97 | 98 | return $o; 99 | } 100 | } 101 | 102 | /** 103 | * WebSocketMessage compatible with the latest draft. 104 | * Should be updated to keep up with the latest changes. 105 | * 106 | * @author Chris 107 | * 108 | */ 109 | class WebSocketMessage implements IWebSocketMessage{ 110 | /** 111 | * 112 | * Enter description here ... 113 | * @var WebSocketFrame[]; 114 | */ 115 | protected $frames = array(); 116 | protected $data = ''; 117 | 118 | public function setData($data){ 119 | $this->data = $data; 120 | 121 | $this->createFrames(); 122 | } 123 | 124 | public static function create($data){ 125 | $o = new self(); 126 | 127 | $o->setData($data); 128 | return $o; 129 | } 130 | 131 | public function getData(){ 132 | if($this->isFinalised() == false) 133 | throw new WebSocketMessageNotFinalised($this); 134 | 135 | $data = ''; 136 | 137 | foreach($this->frames as $frame){ 138 | $data .= $frame->getData(); 139 | } 140 | 141 | return $data; 142 | } 143 | 144 | 145 | public static function fromFrame(IWebSocketFrame $frame){ 146 | $o = new self(); 147 | $o->takeFrame($frame); 148 | 149 | return $o; 150 | } 151 | 152 | protected function createFrames(){ 153 | $this->frames = array(WebSocketFrame::create(WebSocketOpcode::TextFrame, $this->data)); 154 | } 155 | 156 | public function getFrames(){ 157 | return $this->frames; 158 | } 159 | 160 | public function isFinalised(){ 161 | if(count($this->frames) == 0) 162 | return false; 163 | 164 | return $this->frames[count($this->frames)-1]->isFinal(); 165 | } 166 | 167 | /** 168 | * Append a frame to the message 169 | * @param unknown_type $frame 170 | */ 171 | public function takeFrame(WebSocketFrame $frame){ 172 | $this->frames[] = $frame; 173 | } 174 | } -------------------------------------------------------------------------------- /app/websock/old/websocket.resources.php: -------------------------------------------------------------------------------- 1 | users = new SplObjectStorage(); 32 | } 33 | 34 | public function addUser(IWebSocketUser $user){ 35 | $this->users->attach($user); 36 | } 37 | 38 | public function removeUser(IWebSocketUser $user){ 39 | $this->users->detach($user); 40 | } 41 | 42 | public function setServer(WebSocketServer $server){ 43 | $this->server = $server; 44 | } 45 | 46 | public function say($msg =''){ 47 | return $this->server->say($msg); 48 | } 49 | 50 | public function send(IWebSocketUser $client, $str){ 51 | return $this->server->send($client, $str); 52 | } 53 | 54 | public function onMessage(IWebSocketUser $user, IWebSocketMessage $msg){} 55 | public function onAdminMessage(IWebSocketUser $user, stdClass $msg){} 56 | 57 | //abstract public function onMessage(WebSocketUser $user, IWebSocketMessage $msg); 58 | 59 | public function getUsers(){ 60 | return $this->users; 61 | } 62 | } -------------------------------------------------------------------------------- /app/websock/old/websocket.users.php: -------------------------------------------------------------------------------- 1 | socket = $socket; 66 | 67 | $address = ''; 68 | if(socket_getpeername($socket, &$address) === false) 69 | throw new Exception(); 70 | 71 | $this->ip = $address; 72 | $this->id = uniqid("u-"); 73 | 74 | $this->_server = $server; 75 | 76 | $this->lastTime = time(); 77 | } 78 | 79 | public function getId(){ 80 | return $this->id; 81 | } 82 | 83 | public function getHeaders(){ 84 | return $this->headers; 85 | } 86 | 87 | public function setHeaders($headers){ 88 | $this->headers = $headers; 89 | 90 | if(array_key_exists('Cookie', $this->headers) && is_array($this->headers['Cookie'])) { 91 | $this->cookie = array(); 92 | } else { 93 | if(array_key_exists("Cookie", $this->headers)){ 94 | $this->_cookies = WebSocketFunctions::cookie_parse($this->headers['Cookie']); 95 | }else $this->_cookies = array(); 96 | } 97 | 98 | $this->isAdmin = array_key_exists('Admin-Key', $this->headers) 99 | && $this->headers['Admin-Key'] == $this->getServerInstance()->getAdminKey(); 100 | 101 | // Incorrect admin-key 102 | if($this->isAdmin == false && array_key_exists('Admin-Key', $this->headers)) 103 | throw new WebSocketNotAuthorizedException($this); 104 | 105 | } 106 | 107 | public function createMessage($data){ 108 | if($this->protocol == WebSocketProtocolVersions::HIXIE_76) 109 | return WebSocketMessage76::create($data); 110 | else return WebSocketMessage::create($data); 111 | } 112 | 113 | public function hasHandshaked(){ 114 | return $this->handshaked; 115 | } 116 | 117 | public function setHandshaked(){ 118 | $this->handshaked = true; 119 | } 120 | 121 | public function getProtocolVersion(){ 122 | return $this->protocol; 123 | } 124 | 125 | public function getCookies(){ 126 | return $this->_cookies; 127 | } 128 | 129 | public function setProtocolVersion($version){ 130 | $this->protocol = $version; 131 | } 132 | 133 | public function getResource(){ 134 | return $this->resource; 135 | } 136 | 137 | public function setResource($resource){ 138 | $this->resource = $resource; 139 | } 140 | 141 | public function isAdmin(){ 142 | return $this->isAdmin; 143 | } 144 | 145 | public function getLastMessage(){ 146 | return $this->message; 147 | } 148 | 149 | public function getSocket(){ 150 | return $this->socket; 151 | } 152 | 153 | public function addMessage(IWebSocketMessage $msg){ 154 | $this->message = $msg; 155 | $this->lastTime = time(); 156 | } 157 | 158 | public function getLastMessageTime(){ 159 | return $this->lastTime; 160 | } 161 | 162 | public function getServerInstance(){ 163 | return $this->_server; 164 | } 165 | 166 | public function getIp(){ 167 | return $this->ip; 168 | } 169 | } -------------------------------------------------------------------------------- /app/websock/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ctfwsserver", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "Claudio Gamboa", 10 | "license": "ISC", 11 | "dependencies": { 12 | "mysql": "^2.11.1", 13 | "pm2": "^2.0.18", 14 | "ws": "^1.1.1" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /app/websock/start_websockets.sh: -------------------------------------------------------------------------------- 1 | ./node_modules/pm2/bin/pm2 start app/server.js 2 | -------------------------------------------------------------------------------- /app/websock/stop_websockets.sh: -------------------------------------------------------------------------------- 1 | ./node_modules/pm2/bin/pm2 stop app/server.js 2 | -------------------------------------------------------------------------------- /catalog-info.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: backstage.io/v1alpha1 2 | kind: Component 3 | metadata: 4 | name: CTF-Game 5 | spec: 6 | type: intentionally-vulnerable 7 | lifecycle: "-" 8 | owner: "-" -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "require": { 3 | "textalk/websocket": "1.0.*" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", 5 | "This file is @generated automatically" 6 | ], 7 | "hash": "f84ba71c3da1165dc6d66094d0028157", 8 | "content-hash": "bd455206e2cff444eabd9923726c6543", 9 | "packages": [ 10 | { 11 | "name": "textalk/websocket", 12 | "version": "1.0.3", 13 | "source": { 14 | "type": "git", 15 | "url": "https://github.com/Textalk/websocket-php.git", 16 | "reference": "ba2e5f9ef7cf24d536d1c864ae74b2a9599b86eb" 17 | }, 18 | "dist": { 19 | "type": "zip", 20 | "url": "https://api.github.com/repos/Textalk/websocket-php/zipball/ba2e5f9ef7cf24d536d1c864ae74b2a9599b86eb", 21 | "reference": "ba2e5f9ef7cf24d536d1c864ae74b2a9599b86eb", 22 | "shasum": "" 23 | }, 24 | "require-dev": { 25 | "phpunit/phpunit": "4.1.*", 26 | "phpunit/phpunit-selenium": "1.3.3", 27 | "satooshi/php-coveralls": "dev-master" 28 | }, 29 | "type": "library", 30 | "autoload": { 31 | "psr-4": { 32 | "WebSocket\\": "lib" 33 | } 34 | }, 35 | "notification-url": "https://packagist.org/downloads/", 36 | "license": [ 37 | "MIT" 38 | ], 39 | "authors": [ 40 | { 41 | "name": "Fredrik Liljegren", 42 | "email": "fredrik.liljegren@textalk.se" 43 | } 44 | ], 45 | "description": "WebSocket client and server", 46 | "time": "2015-04-11 05:45:54" 47 | } 48 | ], 49 | "packages-dev": [], 50 | "aliases": [], 51 | "minimum-stability": "stable", 52 | "stability-flags": [], 53 | "prefer-stable": false, 54 | "prefer-lowest": false, 55 | "platform": [], 56 | "platform-dev": [] 57 | } 58 | -------------------------------------------------------------------------------- /composer.phar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Probely/CTF-Game/1759dfbfe7f6d0dd974a9f709f1ab1ab08161bbe/composer.phar -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | services: 3 | nginx: 4 | build: docker/nginx 5 | ports: 6 | - "8050:80" 7 | links: 8 | - php 9 | volumes: 10 | - .:/var/www/ctf 11 | php: 12 | build: docker/php 13 | ports: 14 | - "9000" 15 | links: 16 | - mysql 17 | environment: 18 | DB_HOST: mysql 19 | volumes: 20 | - .:/var/www/ctf 21 | mysql: 22 | image: mysql 23 | ports: 24 | - "3306" 25 | environment: 26 | MYSQL_ROOT_PASSWORD: hellyeah 27 | volumes: 28 | - ./sql:/docker-entrypoint-initdb.d 29 | -------------------------------------------------------------------------------- /docker/nginx/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginx:latest 2 | COPY ./default.conf /etc/nginx/conf.d/ -------------------------------------------------------------------------------- /docker/nginx/default.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | 4 | root /var/www/ctf/app; 5 | 6 | location / { 7 | try_files $uri /index.php$is_args$args; 8 | } 9 | 10 | location ~ ^/.+\.php(/|$) { 11 | fastcgi_pass php:9000; 12 | include fastcgi_params; 13 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /docker/php/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:7-fpm 2 | RUN docker-php-ext-install pdo pdo_mysql -------------------------------------------------------------------------------- /screenshots/backoffice.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Probely/CTF-Game/1759dfbfe7f6d0dd974a9f709f1ab1ab08161bbe/screenshots/backoffice.png -------------------------------------------------------------------------------- /screenshots/public dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Probely/CTF-Game/1759dfbfe7f6d0dd974a9f709f1ab1ab08161bbe/screenshots/public dashboard.png -------------------------------------------------------------------------------- /screenshots/team dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Probely/CTF-Game/1759dfbfe7f6d0dd974a9f709f1ab1ab08161bbe/screenshots/team dashboard.png -------------------------------------------------------------------------------- /sql/.htaccess: -------------------------------------------------------------------------------- 1 | order deny,allow 2 | deny from all 3 | -------------------------------------------------------------------------------- /sql/schema.sql: -------------------------------------------------------------------------------- 1 | -- MySQL dump 10.13 Distrib 5.1.57, for apple-darwin10.8.0 (i386) 2 | -- 3 | -- Host: localhost Database: codebits 4 | -- ------------------------------------------------------ 5 | -- Server version 5.1.53 6 | 7 | /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; 8 | /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; 9 | /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; 10 | /*!40101 SET NAMES utf8 */; 11 | /*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; 12 | /*!40103 SET TIME_ZONE='+00:00' */; 13 | /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; 14 | /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; 15 | /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; 16 | /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; 17 | 18 | CREATE DATABASE IF NOT EXISTS CTF; 19 | USE CTF; 20 | 21 | -- 22 | -- Table structure for table `answers` 23 | -- 24 | 25 | DROP TABLE IF EXISTS `answers`; 26 | /*!40101 SET @saved_cs_client = @@character_set_client */; 27 | /*!40101 SET character_set_client = utf8 */; 28 | CREATE TABLE `answers` ( 29 | `teamid` int(11) NOT NULL, 30 | `cat` varchar(20) NOT NULL, 31 | `question` int(11) NOT NULL, 32 | `points` int(11) DEFAULT NULL, 33 | `answeredts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 34 | PRIMARY KEY (`teamid`,`cat`,`question`), 35 | KEY `teamid` (`teamid`) 36 | ) ENGINE=InnoDB DEFAULT CHARSET=latin1; 37 | /*!40101 SET character_set_client = @saved_cs_client */; 38 | 39 | -- 40 | -- Dumping data for table `answers` 41 | -- 42 | 43 | LOCK TABLES `answers` WRITE; 44 | /*!40000 ALTER TABLE `answers` DISABLE KEYS */; 45 | #INSERT INTO `answers` VALUES (1,'Trivia',100,90,'2011-10-31 17:23:50'),(1,'Trivia',200,200,'2011-10-31 17:23:50'),(1,'Trivia',400,400,'2011-10-31 17:23:50'),(2,'Forensics',300,300,'2011-10-31 17:23:40'),(2,'Trivia',100,100,'2011-10-31 17:23:50'),(2,'Trivia',300,300,'2011-10-31 17:23:50'),(2,'Web Hacking',100,100,'2011-10-31 17:24:29'),(2,'Web Hacking',200,180,'2011-11-02 14:45:43'),(3,'Trivia',100,80,'2011-11-02 14:45:43'),(3,'Trivia',200,160,'2011-11-02 14:45:43'),(4,'Trivia',100,80,'2011-11-02 14:45:43'),(4,'Web Hacking',200,200,'2011-11-02 14:45:43'),(5,'Trivia',100,80,'2011-11-02 14:45:43'); 46 | 47 | insert into answers values (5,'Trivia',100,100,now()); insert into answers values (5,'Trivia',200,100,now());insert into answers values (5,'Trivia',300,100,now()); insert into answers values (5,'Trivia',400,100,now()); 48 | insert into answers values (5,'Pwnable',100,100,now()); insert into answers values (5,'Pwnable',200,100,now());insert into answers values (5,'Pwnable',300,100,now());insert into answers values (5,'Pwnable',400,100,now()); 49 | insert into answers values (5,'Forensics',100,100,now()); insert into answers values (5,'Forensics',200,100,now());insert into answers values (5,'Forensics',300,100,now());insert into answers values (5,'Forensics',400,100,now()); 50 | insert into answers values (5,'Web Hacking',100,100,now());insert into answers values (5,'Web Hacking',200,100,now()); insert into answers values (5,'Web Hacking',300,100,now()); insert into answers values (5,'Web Hacking',400,100,now()); 51 | 52 | /*!40000 ALTER TABLE `answers` ENABLE KEYS */; 53 | UNLOCK TABLES; 54 | 55 | -- 56 | -- Table structure for table `game` 57 | -- 58 | 59 | DROP TABLE IF EXISTS `game`; 60 | /*!40101 SET @saved_cs_client = @@character_set_client */; 61 | /*!40101 SET character_set_client = utf8 */; 62 | CREATE TABLE `game` ( 63 | `leadcat` varchar(20), 64 | `leadpoints` int(11), 65 | `yellowshirt` int(11) DEFAULT NULL, 66 | `lastleadreset` timestamp NULL DEFAULT NULL, 67 | `end` timestamp NULL DEFAULT NULL, 68 | PRIMARY KEY (`leadcat`, `leadpoints`), 69 | KEY `yellowshirt` (`yellowshirt`), 70 | CONSTRAINT `yellowshirt` FOREIGN KEY (`yellowshirt`) REFERENCES `teams` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION 71 | ) ENGINE=InnoDB DEFAULT CHARSET=latin1; 72 | /*!40101 SET character_set_client = @saved_cs_client */; 73 | 74 | -- 75 | -- Dumping data for table `game` 76 | -- 77 | 78 | LOCK TABLES `game` WRITE; 79 | /*!40000 ALTER TABLE `game` DISABLE KEYS */; 80 | INSERT INTO `game` (leadcat,leadpoints,end) VALUES ('Trivia',100,from_unixtime(unix_timestamp(now())+10800)); 81 | /*!40000 ALTER TABLE `game` ENABLE KEYS */; 82 | UNLOCK TABLES; 83 | 84 | -- 85 | -- Table structure for table `teams` 86 | -- 87 | 88 | DROP TABLE IF EXISTS `teams`; 89 | /*!40101 SET @saved_cs_client = @@character_set_client */; 90 | /*!40101 SET character_set_client = utf8 */; 91 | CREATE TABLE `teams` ( 92 | `id` int(11) NOT NULL, 93 | `team` varchar(80) DEFAULT NULL, 94 | `tkey` varchar(80) DEFAULT NULL, 95 | PRIMARY KEY (`id`), 96 | UNIQUE KEY `tkey_UNIQUE` (`tkey`) 97 | ) ENGINE=InnoDB DEFAULT CHARSET=latin1; 98 | /*!40101 SET character_set_client = @saved_cs_client */; 99 | 100 | DROP TABLE IF EXISTS `hints`; 101 | /*!40101 SET @saved_cs_client = @@character_set_client */; 102 | /*!40101 SET character_set_client = utf8 */; 103 | CREATE TABLE `hints` ( 104 | `id` int(11) NOT NULL AUTO_INCREMENT, 105 | `hint` TEXT NOT NULL, 106 | `timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 107 | PRIMARY KEY (`id`) 108 | ) ENGINE=InnoDB DEFAULT CHARSET=latin1; 109 | /*!40101 SET character_set_client = @saved_cs_client */; 110 | 111 | -- 112 | -- Dumping data for table `teams` 113 | -- 114 | 115 | LOCK TABLES `teams` WRITE; 116 | /*!40000 ALTER TABLE `teams` DISABLE KEYS */; 117 | INSERT INTO `teams` VALUES (1,'Team1',sha1('5bd7b888f1327e07ecfcbed64a7ba4154bbe08bc')), 118 | (2,'team2',sha1('da1caf48a41f09b76df6e6692f03573508925364')), 119 | (3,'Team3',sha1('6e91a41941be135959c97a26ad43cbc6c8a68364')), 120 | (4,'team4',sha1('92e1b5d17c3b6ad12b6a711d70a077a3d32d27c9')), 121 | (5,'Team5',sha1('7849f1efc1578926a42a91198d872a47b6614adf')), 122 | (6,'team6',sha1('b8a49af1f5801d6943f271887511cac890cceaca')), 123 | (7,'Team7',sha1('caf47b50a973d1dcec68095021111caef0b8a680')), 124 | (8,'team8',sha1('b87611d1ab526a72f015417131faf618dcead012')), 125 | (9,'Team9',sha1('7fe046eb111dd620d9fc9b1b11cd0d6d0c35fb61')), 126 | (10,'team10',sha1('a31f110ac892c1c4fad24f5cc7131e58b3311396')); 127 | /*!40000 ALTER TABLE `teams` ENABLE KEYS */; 128 | UNLOCK TABLES; 129 | /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; 130 | 131 | /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; 132 | /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; 133 | /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; 134 | /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; 135 | /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; 136 | /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; 137 | /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; 138 | 139 | CREATE USER 'pixels'@'localhost' IDENTIFIED BY 'xxxxxxxxxxx'; 140 | GRANT SELECT,INSERT,UPDATE,DELETE ON CTF.* TO 'pixels'@'localhost'; 141 | 142 | 143 | -- Dump completed on 2011-11-02 15:06:05 144 | -------------------------------------------------------------------------------- /vendor/autoload.php: -------------------------------------------------------------------------------- 1 | array($vendorDir . '/textalk/websocket/lib'), 10 | ); 11 | -------------------------------------------------------------------------------- /vendor/composer/autoload_real.php: -------------------------------------------------------------------------------- 1 | = 50600 && !defined('HHVM_VERSION'); 27 | if ($useStaticLoader) { 28 | require_once __DIR__ . '/autoload_static.php'; 29 | 30 | call_user_func(\Composer\Autoload\ComposerStaticInitebb8a408ed842a32cbacb538793056a2::getInitializer($loader)); 31 | } else { 32 | $map = require __DIR__ . '/autoload_namespaces.php'; 33 | foreach ($map as $namespace => $path) { 34 | $loader->set($namespace, $path); 35 | } 36 | 37 | $map = require __DIR__ . '/autoload_psr4.php'; 38 | foreach ($map as $namespace => $path) { 39 | $loader->setPsr4($namespace, $path); 40 | } 41 | 42 | $classMap = require __DIR__ . '/autoload_classmap.php'; 43 | if ($classMap) { 44 | $loader->addClassMap($classMap); 45 | } 46 | } 47 | 48 | $loader->register(true); 49 | 50 | return $loader; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /vendor/composer/autoload_static.php: -------------------------------------------------------------------------------- 1 | 11 | array ( 12 | 'WebSocket\\' => 10, 13 | ), 14 | ); 15 | 16 | public static $prefixDirsPsr4 = array ( 17 | 'WebSocket\\' => 18 | array ( 19 | 0 => __DIR__ . '/..' . '/textalk/websocket/lib', 20 | ), 21 | ); 22 | 23 | public static function getInitializer(ClassLoader $loader) 24 | { 25 | return \Closure::bind(function () use ($loader) { 26 | $loader->prefixLengthsPsr4 = ComposerStaticInitebb8a408ed842a32cbacb538793056a2::$prefixLengthsPsr4; 27 | $loader->prefixDirsPsr4 = ComposerStaticInitebb8a408ed842a32cbacb538793056a2::$prefixDirsPsr4; 28 | 29 | }, null, ClassLoader::class); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /vendor/composer/installed.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "textalk/websocket", 4 | "version": "1.0.3", 5 | "version_normalized": "1.0.3.0", 6 | "source": { 7 | "type": "git", 8 | "url": "https://github.com/Textalk/websocket-php.git", 9 | "reference": "ba2e5f9ef7cf24d536d1c864ae74b2a9599b86eb" 10 | }, 11 | "dist": { 12 | "type": "zip", 13 | "url": "https://api.github.com/repos/Textalk/websocket-php/zipball/ba2e5f9ef7cf24d536d1c864ae74b2a9599b86eb", 14 | "reference": "ba2e5f9ef7cf24d536d1c864ae74b2a9599b86eb", 15 | "shasum": "" 16 | }, 17 | "require-dev": { 18 | "phpunit/phpunit": "4.1.*", 19 | "phpunit/phpunit-selenium": "1.3.3", 20 | "satooshi/php-coveralls": "dev-master" 21 | }, 22 | "time": "2015-04-11 05:45:54", 23 | "type": "library", 24 | "installation-source": "dist", 25 | "autoload": { 26 | "psr-4": { 27 | "WebSocket\\": "lib" 28 | } 29 | }, 30 | "notification-url": "https://packagist.org/downloads/", 31 | "license": [ 32 | "MIT" 33 | ], 34 | "authors": [ 35 | { 36 | "name": "Fredrik Liljegren", 37 | "email": "fredrik.liljegren@textalk.se" 38 | } 39 | ], 40 | "description": "WebSocket client and server" 41 | } 42 | ] 43 | -------------------------------------------------------------------------------- /vendor/textalk/websocket/.coveralls.yml: -------------------------------------------------------------------------------- 1 | service_name: travis-ci 2 | src_dir: lib 3 | -------------------------------------------------------------------------------- /vendor/textalk/websocket/.gitignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | /vendor/ 3 | -------------------------------------------------------------------------------- /vendor/textalk/websocket/.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 5.5 5 | - 5.4 6 | - 5.3 7 | 8 | before_script: 9 | - curl -s http://getcomposer.org/installer | php 10 | - php composer.phar install --dev --no-interaction 11 | 12 | script: 13 | - mkdir -p build/logs 14 | - ./vendor/bin/phpunit 15 | 16 | after_script: 17 | - php vendor/bin/coveralls -v 18 | -------------------------------------------------------------------------------- /vendor/textalk/websocket/Makefile: -------------------------------------------------------------------------------- 1 | install: composer.phar 2 | ./composer.phar install 3 | 4 | update: 5 | ./composer.phar update 6 | 7 | test: vendor/bin/phpunit build 8 | ./vendor/bin/phpunit --strict 9 | 10 | composer.phar: 11 | curl -s http://getcomposer.org/installer | php 12 | 13 | vendor/bin/phpunit: install 14 | 15 | build: 16 | mkdir build 17 | 18 | clean: 19 | rm composer.phar 20 | rm -r vendor 21 | rm -r build 22 | -------------------------------------------------------------------------------- /vendor/textalk/websocket/README.md: -------------------------------------------------------------------------------- 1 | Websocket Client for PHP 2 | ======================== 3 | 4 | [](https://travis-ci.org/Textalk/websocket-php) 5 | [](https://coveralls.io/r/Textalk/websocket-php) 6 | 7 | This package mainly contains a WebSocket client for PHP. 8 | 9 | I made it because the state of other WebSocket clients I could found was either very poor 10 | (sometimes failing on large frames) or had huge dependencies (React…). 11 | 12 | The Client should be good. If it isn't, tell me! 13 | 14 | The Server there because much of the code would be identical in writing a server, and because it is 15 | used for the tests. To be really useful though, there should be a Connection-class returned from a 16 | new Connection, and the Server-class only handling the handshake. Then you could hold a full array 17 | of Connections and check them periodically for new data, send something to them all or fork off a 18 | process handling one connection. But, I have no use for that right now. (Actually, I would 19 | suggest a language with better asynchronous handling than PHP for that.) 20 | 21 | Installing 22 | ---------- 23 | 24 | Preferred way to install is with [Composer](https://getcomposer.org/). 25 | 26 | Just add 27 | 28 | "require": { 29 | "textalk/websocket": "1.0.*" 30 | } 31 | 32 | in your projects composer.json. 33 | 34 | Client usage: 35 | ------------- 36 | ```php 37 | require('vendor/autoload.php'); 38 | 39 | use WebSocket\Client; 40 | 41 | $client = new Client("ws://echo.websocket.org/"); 42 | $client->send("Hello WebSocket.org!"); 43 | 44 | echo $client->receive(); // Will output 'Hello WebSocket.org!' 45 | ``` 46 | 47 | Developer install 48 | ----------------- 49 | 50 | Development depends on php, php-curl and php-xdebug. 51 | 52 | ```bash 53 | # Will get composer, install dependencies and run tests 54 | make test 55 | ``` 56 | 57 | Changelog 58 | --------- 59 | 60 | 1.0.0 61 | 62 | * Release as production ready. 63 | * Adding option to set/override headers. 64 | * Supporting basic authentication from user:pass in URL. 65 | -------------------------------------------------------------------------------- /vendor/textalk/websocket/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "textalk/websocket", 3 | "description": "WebSocket client and server", 4 | "license": "MIT", 5 | "authors": [ 6 | { 7 | "name": "Fredrik Liljegren", 8 | "email": "fredrik.liljegren@textalk.se" 9 | } 10 | ], 11 | "autoload": { 12 | "psr-4": {"WebSocket\\": "lib"} 13 | }, 14 | "require-dev": { 15 | "phpunit/phpunit": "4.1.*", 16 | "phpunit/phpunit-selenium": "1.3.3", 17 | "satooshi/php-coveralls": "dev-master" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /vendor/textalk/websocket/examples/echoserver.php: -------------------------------------------------------------------------------- 1 | 200)); 15 | 16 | echo $server->getPort(), "\n"; 17 | 18 | while ($connection = $server->accept()) { 19 | $test_id = $server->getPath(); 20 | $test_id = substr($test_id, 1); 21 | 22 | xdebug_start_code_coverage(XDEBUG_CC_UNUSED | XDEBUG_CC_DEAD_CODE); 23 | PHPUnit_Extensions_SeleniumCommon_ExitHandler::init(); 24 | 25 | try { 26 | while(1) { 27 | $message = $server->receive(); 28 | echo "Received $message\n\n"; 29 | 30 | if ($message === 'exit') { 31 | echo microtime(true), " Client told me to quit. Bye bye.\n"; 32 | echo microtime(true), " Close response: ", $server->close(), "\n"; 33 | echo microtime(true), " Close status: ", $server->getCloseStatus(), "\n"; 34 | save_coverage_data($test_id); 35 | exit; 36 | } 37 | 38 | if ($message === 'Dump headers') { 39 | $server->send(implode("\r\n", $server->getRequest())); 40 | } 41 | elseif ($auth = $server->getHeader('Authorization')) { 42 | $server->send("$auth - $message", 'text', false); 43 | } 44 | else { 45 | $server->send($message, 'text', false); 46 | } 47 | } 48 | } 49 | catch (WebSocket\ConnectionException $e) { 50 | echo "\n", microtime(true), " Client died: $e\n"; 51 | save_coverage_data($test_id); 52 | } 53 | } 54 | 55 | exit; 56 | 57 | 58 | function save_coverage_data($test_id) { 59 | $data = xdebug_get_code_coverage(); 60 | xdebug_stop_code_coverage(); 61 | 62 | if (!is_dir($GLOBALS['PHPUNIT_COVERAGE_DATA_DIRECTORY'])) { 63 | mkdir($GLOBALS['PHPUNIT_COVERAGE_DATA_DIRECTORY'], 0777, true); 64 | } 65 | $file = $GLOBALS['PHPUNIT_COVERAGE_DATA_DIRECTORY'] . '/' . $test_id 66 | . '.' . md5(uniqid(rand(), true)); 67 | 68 | echo "Saving coverage data to $file...\n"; 69 | file_put_contents($file, serialize($data)); 70 | } 71 | -------------------------------------------------------------------------------- /vendor/textalk/websocket/examples/send.php: -------------------------------------------------------------------------------- 1 | send($argv[2]); 10 | 11 | echo $client->receive(); 12 | -------------------------------------------------------------------------------- /vendor/textalk/websocket/lib/BadOpcodeException.php: -------------------------------------------------------------------------------- 1 | 1, 11 | 'binary' => 2, 12 | 'close' => 8, 13 | 'ping' => 9, 14 | 'pong' => 10, 15 | ); 16 | 17 | public function getLastOpcode() { return $this->last_opcode; } 18 | public function getCloseStatus() { return $this->close_status; } 19 | public function isConnected() { return $this->is_connected; } 20 | 21 | public function setTimeout($timeout) { 22 | $this->options['timeout'] = $timeout; 23 | 24 | if ($this->socket && get_resource_type($this->socket) === 'stream') { 25 | stream_set_timeout($this->socket, $timeout); 26 | } 27 | } 28 | 29 | public function send($payload, $opcode = 'text', $masked = true) { 30 | if (!$this->is_connected) $this->connect(); /// @todo This is a client function, fixme! 31 | 32 | if (!in_array($opcode, array_keys(self::$opcodes))) { 33 | throw new BadOpcodeException("Bad opcode '$opcode'. Try 'text' or 'binary'."); 34 | } 35 | 36 | // Binary string for header. 37 | $frame_head_binstr = ''; 38 | 39 | 40 | // Write FIN, final fragment bit. 41 | $final = true; /// @todo Support HUGE payloads. 42 | $frame_head_binstr .= $final ? '1' : '0'; 43 | 44 | // RSV 1, 2, & 3 false and unused. 45 | $frame_head_binstr .= '000'; 46 | 47 | // Opcode rest of the byte. 48 | $frame_head_binstr .= sprintf('%04b', self::$opcodes[$opcode]); 49 | 50 | // Use masking? 51 | $frame_head_binstr .= $masked ? '1' : '0'; 52 | 53 | // 7 bits of payload length... 54 | $payload_length = strlen($payload); 55 | if ($payload_length > 65535) { 56 | $frame_head_binstr .= decbin(127); 57 | $frame_head_binstr .= sprintf('%064b', $payload_length); 58 | } 59 | elseif ($payload_length > 125) { 60 | $frame_head_binstr .= decbin(126); 61 | $frame_head_binstr .= sprintf('%016b', $payload_length); 62 | } 63 | else { 64 | $frame_head_binstr .= sprintf('%07b', $payload_length); 65 | } 66 | 67 | $frame = ''; 68 | 69 | // Write frame head to frame. 70 | foreach (str_split($frame_head_binstr, 8) as $binstr) $frame .= chr(bindec($binstr)); 71 | 72 | // Handle masking 73 | if ($masked) { 74 | // generate a random mask: 75 | $mask = ''; 76 | for ($i = 0; $i < 4; $i++) $mask .= chr(rand(0, 255)); 77 | $frame .= $mask; 78 | } 79 | 80 | // Append payload to frame: 81 | for ($i = 0; $i < $payload_length; $i++) { 82 | $frame .= ($masked === true) ? $payload[$i] ^ $mask[$i % 4] : $payload[$i]; 83 | } 84 | 85 | $this->write($frame); 86 | } 87 | 88 | public function receive() { 89 | if (!$this->is_connected) $this->connect(); /// @todo This is a client function, fixme! 90 | 91 | // Just read the main fragment information first. 92 | $data = $this->read(2); 93 | 94 | // Is this the final fragment? // Bit 0 in byte 0 95 | /// @todo Handle huge payloads with multiple fragments. 96 | $final = (boolean) (ord($data[0]) & 1 << 7); 97 | 98 | // Should be unused, and must be false… // Bits 1, 2, & 3 99 | $rsv1 = (boolean) (ord($data[0]) & 1 << 6); 100 | $rsv2 = (boolean) (ord($data[0]) & 1 << 5); 101 | $rsv3 = (boolean) (ord($data[0]) & 1 << 4); 102 | 103 | // Parse opcode 104 | $opcode_int = ord($data[0]) & 31; // Bits 4-7 105 | $opcode_ints = array_flip(self::$opcodes); 106 | if (!array_key_exists($opcode_int, $opcode_ints)) { 107 | throw new ConnectionException("Bad opcode in websocket frame: $opcode_int"); 108 | } 109 | $opcode = $opcode_ints[$opcode_int]; 110 | $this->last_opcode = $opcode; 111 | 112 | // Masking? 113 | $mask = (boolean) (ord($data[1]) >> 7); // Bit 0 in byte 1 114 | 115 | $payload = ""; 116 | 117 | // Payload length 118 | $payload_length = (integer) ord($data[1]) & 127; // Bits 1-7 in byte 1 119 | if ($payload_length > 125) { 120 | if ($payload_length === 126) $data = $this->read(2); // 126: Payload is a 16-bit unsigned int 121 | else $data = $this->read(8); // 127: Payload is a 64-bit unsigned int 122 | $payload_length = bindec(self::sprintB($data)); 123 | } 124 | 125 | // Get masking key. 126 | if ($mask) $masking_key = $this->read(4); 127 | 128 | // Get the actual payload, if any (might not be for e.g. close frames. 129 | if ($payload_length > 0) { 130 | $data = $this->read($payload_length); 131 | 132 | if ($mask) { 133 | // Unmask payload. 134 | $payload = ''; 135 | for ($i = 0; $i < $payload_length; $i++) $payload .= ($data[$i] ^ $masking_key[$i % 4]); 136 | } 137 | else $payload = $data; 138 | } 139 | 140 | if ($opcode === 'close') { 141 | // Get the close status. 142 | if ($payload_length >= 2) { 143 | $status_bin = $payload[0] . $payload[1]; 144 | $status = bindec(sprintf("%08b%08b", ord($payload[0]), ord($payload[1]))); 145 | $this->close_status = $status; 146 | $payload = substr($payload, 2); 147 | } 148 | 149 | if ($this->is_closing) $this->is_closing = false; // A close response, all done. 150 | else $this->send($status_bin . 'Close acknowledged: ' . $status, 'close', true); // Respond. 151 | 152 | // And close the socket. 153 | fclose($this->socket); 154 | $this->is_connected = false; 155 | } 156 | 157 | return $payload; 158 | } 159 | 160 | /** 161 | * Tell the socket to close. 162 | * 163 | * @param integer $status http://tools.ietf.org/html/rfc6455#section-7.4 164 | * @param string $message A closing message, max 125 bytes. 165 | */ 166 | public function close($status = 1000, $message = 'ttfn') { 167 | $status_binstr = sprintf('%016b', $status); 168 | $status_str = ''; 169 | foreach (str_split($status_binstr, 8) as $binstr) $status_str .= chr(bindec($binstr)); 170 | $this->send($status_str . $message, 'close', true); 171 | 172 | $this->is_closing = true; 173 | $response = $this->receive(); // Receiving a close frame will close the socket now. 174 | 175 | return $response; 176 | } 177 | 178 | protected function write($data) { 179 | $written = fwrite($this->socket, $data); 180 | 181 | if ($written < strlen($data)) { 182 | throw new ConnectionException( 183 | "Could only write $written out of " . strlen($data) . " bytes." 184 | ); 185 | } 186 | } 187 | 188 | protected function read($length) { 189 | $data = ''; 190 | while (strlen($data) < $length) { 191 | $buffer = fread($this->socket, $length - strlen($data)); 192 | if ($buffer === false) { 193 | $metadata = stream_get_meta_data($this->socket); 194 | throw new ConnectionException( 195 | 'Broken frame, read ' . strlen($payload_data) . ' of stated ' 196 | . $payload_length . ' bytes. Stream state: ' 197 | . json_encode($metadata) 198 | ); 199 | } 200 | if ($buffer === '') { 201 | $metadata = stream_get_meta_data($this->socket); 202 | throw new ConnectionException( 203 | 'Empty read; connection dead? Stream state: ' . json_encode($metadata) 204 | ); 205 | } 206 | $data .= $buffer; 207 | } 208 | return $data; 209 | } 210 | 211 | 212 | /** 213 | * Helper to convert a binary to a string of '0' and '1'. 214 | */ 215 | protected static function sprintB($string) { 216 | $return = ''; 217 | for ($i = 0; $i < strlen($string); $i++) $return .= sprintf("%08b", ord($string[$i])); 218 | return $return; 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /vendor/textalk/websocket/lib/Client.php: -------------------------------------------------------------------------------- 1 | options = $options; 17 | 18 | if (!array_key_exists('timeout', $this->options)) $this->options['timeout'] = 5; 19 | 20 | $this->socket_uri = $uri; 21 | } 22 | 23 | public function __destruct() { 24 | if ($this->socket) { 25 | if (get_resource_type($this->socket) === 'stream') fclose($this->socket); 26 | $this->socket = null; 27 | } 28 | } 29 | 30 | /** 31 | * Perform WebSocket handshake 32 | */ 33 | protected function connect() { 34 | $url_parts = parse_url($this->socket_uri); 35 | $scheme = $url_parts['scheme']; 36 | $host = $url_parts['host']; 37 | $user = isset($url_parts['user']) ? $url_parts['user'] : ''; 38 | $pass = isset($url_parts['pass']) ? $url_parts['pass'] : ''; 39 | $port = isset($url_parts['port']) ? $url_parts['port'] : ($scheme === 'wss' ? 443 : 80); 40 | $path = isset($url_parts['path']) ? $url_parts['path'] : '/'; 41 | $query = isset($url_parts['query']) ? $url_parts['query'] : ''; 42 | $fragment = isset($url_parts['fragment']) ? $url_parts['fragment'] : ''; 43 | 44 | $path_with_query = $path; 45 | if (!empty($query)) $path_with_query .= '?' . $query; 46 | if (!empty($fragment)) $path_with_query .= '#' . $fragment; 47 | 48 | if (!in_array($scheme, array('ws', 'wss'))) { 49 | throw new BadUriException( 50 | "Url should have scheme ws or wss, not '$scheme' from URI '$this->socket_uri' ." 51 | ); 52 | } 53 | 54 | $host_uri = ($scheme === 'wss' ? 'ssl' : 'tcp') . '://' . $host; 55 | 56 | // Open the socket. @ is there to supress warning that we will catch in check below instead. 57 | $this->socket = @fsockopen($host_uri, $port, $errno, $errstr, $this->options['timeout']); 58 | 59 | if ($this->socket === false) { 60 | throw new ConnectionException( 61 | "Could not open socket to \"$host:$port\": $errstr ($errno)." 62 | ); 63 | } 64 | 65 | // Set timeout on the stream as well. 66 | stream_set_timeout($this->socket, $this->options['timeout']); 67 | 68 | // Generate the WebSocket key. 69 | $key = self::generateKey(); 70 | 71 | // Default headers (using lowercase for simpler array_merge below). 72 | $headers = array( 73 | 'host' => $host . ":" . $port, 74 | 'user-agent' => 'websocket-client-php', 75 | 'connection' => 'Upgrade', 76 | 'upgrade' => 'websocket', 77 | 'sec-websocket-key' => $key, 78 | 'sec-websocket-version' => '13', 79 | ); 80 | 81 | // Handle basic authentication. 82 | if ($user || $pass) { 83 | $headers['authorization'] = 'Basic ' . base64_encode($user . ':' . $pass) . "\r\n"; 84 | } 85 | 86 | // Deprecated way of adding origin (use headers instead). 87 | if (isset($this->options['origin'])) $headers['origin'] = $this->options['origin']; 88 | 89 | // Add and override with headers from options. 90 | if (isset($this->options['headers'])) { 91 | $headers = array_merge($headers, array_change_key_case($this->options['headers'])); 92 | } 93 | 94 | $header = 95 | "GET " . $path_with_query . " HTTP/1.1\r\n" 96 | . implode( 97 | "\r\n", array_map( 98 | function($key, $value) { return "$key: $value"; }, array_keys($headers), $headers 99 | ) 100 | ) 101 | . "\r\n\r\n"; 102 | 103 | // Send headers. 104 | $this->write($header); 105 | 106 | // Get server response. 107 | $response = ''; 108 | do { 109 | $buffer = stream_get_line($this->socket, 1024, "\r\n"); 110 | $response .= $buffer . "\n"; 111 | $metadata = stream_get_meta_data($this->socket); 112 | } while (!feof($this->socket) && $metadata['unread_bytes'] > 0); 113 | 114 | /// @todo Handle version switching 115 | 116 | // Validate response. 117 | if (!preg_match('#Sec-WebSocket-Accept:\s(.*)$#mUi', $response, $matches)) { 118 | $address = $scheme . '://' . $host . $path_with_query; 119 | throw new ConnectionException( 120 | "Connection to '{$address}' failed: Server sent invalid upgrade response:\n" 121 | . $response 122 | ); 123 | } 124 | 125 | $keyAccept = trim($matches[1]); 126 | $expectedResonse 127 | = base64_encode(pack('H*', sha1($key . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'))); 128 | 129 | if ($keyAccept !== $expectedResonse) { 130 | throw new ConnectionException('Server sent bad upgrade response.'); 131 | } 132 | 133 | $this->is_connected = true; 134 | } 135 | 136 | /** 137 | * Generate a random string for WebSocket key. 138 | * @return string Random string 139 | */ 140 | protected static function generateKey() { 141 | $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"$&/()=[]{}0123456789'; 142 | $key = ''; 143 | $chars_length = strlen($chars); 144 | for ($i = 0; $i < 16; $i++) $key .= $chars[mt_rand(0, $chars_length-1)]; 145 | return base64_encode($key); 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /vendor/textalk/websocket/lib/ConnectionException.php: -------------------------------------------------------------------------------- 1 | port = isset($options['port']) ? $options['port'] : 8000; 19 | $this->options = $options; 20 | 21 | do { 22 | $this->listening = @stream_socket_server("tcp://0.0.0.0:$this->port", $errno, $errstr); 23 | } while ($this->listening === false && $this->port++ < 10000); 24 | 25 | if (!$this->listening) { 26 | throw new ConnectionException("Could not open listening socket."); 27 | } 28 | } 29 | 30 | public function getPort() { return $this->port; } 31 | public function getPath() { return $this->request_path; } 32 | public function getRequest() { return $this->request; } 33 | 34 | public function getHeader($header) { 35 | foreach ($this->request as $row) { 36 | if (stripos($row, $header) !== false) { 37 | list($headername, $headervalue) = explode(":", $row); 38 | return trim($headervalue); 39 | } 40 | } 41 | return null; 42 | } 43 | 44 | public function accept() { 45 | $this->socket = stream_socket_accept($this->listening); 46 | 47 | if (array_key_exists('timeout', $this->options)) { 48 | stream_set_timeout($this->socket, $this->options['timeout']); 49 | } 50 | 51 | $this->performHandshake(); 52 | 53 | return $this->socket; 54 | } 55 | 56 | protected function performHandshake() { 57 | $request = ''; 58 | do { 59 | $buffer = stream_get_line($this->socket, 1024, "\r\n"); 60 | $request .= $buffer . "\n"; 61 | $metadata = stream_get_meta_data($this->socket); 62 | } while (!feof($this->socket) && $metadata['unread_bytes'] > 0); 63 | 64 | if (!preg_match('/GET (.*) HTTP\//mUi', $request, $matches)) { 65 | throw new ConnectionException("No GET in request:\n" . $request); 66 | } 67 | $get_uri = trim($matches[1]); 68 | $uri_parts = parse_url($get_uri); 69 | 70 | $this->request = explode("\n", $request); 71 | $this->request_path = $uri_parts['path']; 72 | /// @todo Get query and fragment as well. 73 | 74 | if (!preg_match('#Sec-WebSocket-Key:\s(.*)$#mUi', $request, $matches)) { 75 | throw new ConnectionException("Client had no Key in upgrade request:\n" . $request); 76 | } 77 | 78 | $key = trim($matches[1]); 79 | 80 | /// @todo Validate key length and base 64... 81 | $response_key = base64_encode(pack('H*', sha1($key . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'))); 82 | 83 | $header = "HTTP/1.1 101 Switching Protocols\r\n" 84 | . "Upgrade: websocket\r\n" 85 | . "Connection: Upgrade\r\n" 86 | . "Sec-WebSocket-Accept: $response_key\r\n" 87 | . "\r\n"; 88 | 89 | $this->write($header); 90 | $this->is_connected = true; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /vendor/textalk/websocket/phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 7 | cacheTokens="false" 8 | colors="true" 9 | convertErrorsToExceptions="true" 10 | convertNoticesToExceptions="true" 11 | convertWarningsToExceptions="true" 12 | forceCoversAnnotation="false" 13 | mapTestClassNameToCoveredClassName="false" 14 | processIsolation="false" 15 | stopOnError="true" 16 | stopOnFailure="true" 17 | stopOnIncomplete="false" 18 | stopOnSkipped="false" 19 | strict="true" 20 | verbose="true"> 21 | 44 | -------------------------------------------------------------------------------- /vendor/textalk/websocket/tests/unit/ClientTest.php: -------------------------------------------------------------------------------- 1 | %s 2>&1 & echo $! >> %s", $cmd, $outputfile, $pidfile)); 14 | 15 | usleep(500000); 16 | self::$port = trim(file_get_contents($outputfile)); 17 | 18 | echo "Server started with port: ", self::$port, "\n"; 19 | } 20 | 21 | public static function tearDownAfterClass() { 22 | $ws = new Client('ws://localhost:' . self::$port); 23 | $ws->send('exit'); 24 | } 25 | 26 | public function setup() { 27 | // Setup server side coverage catching 28 | $this->test_id = rand(); 29 | } 30 | 31 | protected function getCodeCoverage() { 32 | $files = glob(dirname(dirname(dirname(__FILE__))) . '/build/tmp/' . $this->test_id . '.*'); 33 | 34 | if (count($files) > 1) { 35 | echo "We have more than one coverage file...\n"; 36 | } 37 | 38 | foreach ($files as $file) { 39 | $buffer = file_get_contents($file); 40 | $coverage_data = unserialize($buffer); 41 | } 42 | 43 | if (!isset($coverage_data)) return array(); 44 | 45 | return $coverage_data; 46 | } 47 | 48 | public function run(PHPUnit_Framework_TestResult $result = NULL) { 49 | if ($result === NULL) { 50 | $result = $this->createResult(); 51 | } 52 | 53 | $this->collectCodeCoverageInformation = $result->getCollectCodeCoverageInformation(); 54 | 55 | parent::run($result); 56 | 57 | if ($this->collectCodeCoverageInformation) { 58 | $result->getCodeCoverage()->append( 59 | $this->getCodeCoverage(), $this 60 | ); 61 | } 62 | 63 | return $result; 64 | } 65 | 66 | public function testInstantiation() { 67 | $ws = new Client('ws://localhost:' . self::$port . '/' . $this->test_id); 68 | 69 | $this->assertInstanceOf('WebSocket\Client', $ws); 70 | } 71 | 72 | /** 73 | * @dataProvider dataLengthProvider 74 | */ 75 | public function testEcho($data_length) { 76 | $ws = new Client('ws://localhost:' . self::$port . '/' . $this->test_id); 77 | 78 | $greeting = ''; 79 | for ($i = 0; $i < $data_length; $i++) $greeting .= 'o'; 80 | 81 | $ws->send($greeting); 82 | $response = $ws->receive(); 83 | 84 | $this->assertEquals($greeting, $response); 85 | } 86 | 87 | public function testBasicAuth() { 88 | $user = 'JohnDoe'; 89 | $pass = 'eoDnhoJ'; 90 | 91 | $ws = new Client("ws://$user:$pass@localhost:" . self::$port . '/' . $this->test_id); 92 | 93 | $greeting = 'Howdy'; 94 | $ws->send($greeting); 95 | $response = $ws->receive(); 96 | 97 | // Echo server will prefix basic authed requests. 98 | $this->assertEquals("Basic Sm9obkRvZTplb0RuaG9K - $greeting", $response); 99 | } 100 | 101 | public function dataLengthProvider() { 102 | return array( 103 | array(8), 104 | array(126), 105 | array(127), 106 | array(128), 107 | array(65000), 108 | array(66000), 109 | ); 110 | } 111 | 112 | public function testOrgEchoTwice() { 113 | $ws = new Client('ws://localhost:' . self::$port . '/' . $this->test_id); 114 | 115 | for ($i = 0; $i < 2; $i++) { 116 | $greeting = 'Hello WebSockets ' . $i; 117 | $ws->send($greeting); 118 | $response = $ws->receive(); 119 | $this->assertEquals($greeting, $response); 120 | } 121 | } 122 | 123 | public function testClose() { 124 | // Start a NEW dedicated server for this test 125 | $cmd = 'php examples/echoserver.php'; 126 | $outputfile = 'build/serveroutput_close.txt'; 127 | $pidfile = 'build/server_close.pid'; 128 | exec(sprintf("%s > %s 2>&1 & echo $! >> %s", $cmd, $outputfile, $pidfile)); 129 | 130 | usleep(500000); 131 | $port = trim(file_get_contents($outputfile)); 132 | 133 | $ws = new Client('ws://localhost:' . $port . '/' . $this->test_id); 134 | $ws->send('exit'); 135 | $response = $ws->receive(); 136 | 137 | $this->assertEquals('ttfn', $response); 138 | $this->assertEquals(1000, $ws->getCloseStatus()); 139 | $this->assertFalse($ws->isConnected()); 140 | } 141 | 142 | /** 143 | * @expectedException WebSocket\BadUriException 144 | * @expectedExceptionMessage Url should have scheme ws or wss 145 | */ 146 | public function testBadUrl() { 147 | $ws = new Client('http://echo.websocket.org'); 148 | $ws->send('foo'); 149 | } 150 | 151 | /** 152 | * @expectedException WebSocket\ConnectionException 153 | */ 154 | public function testNonWSSite() { 155 | $ws = new Client('ws://example.org'); 156 | $ws->send('foo'); 157 | } 158 | 159 | public function testSslUrl() { 160 | $ws = new Client('wss://echo.websocket.org'); 161 | 162 | $greeting = 'Hello WebSockets'; 163 | $ws->send($greeting); 164 | $response = $ws->receive(); 165 | $this->assertEquals($greeting, $response); 166 | } 167 | 168 | public function testSslUrlMasked() { 169 | $ws = new Client('wss://echo.websocket.org'); 170 | 171 | $greeting = 'Hello WebSockets'; 172 | $ws->send($greeting, 'text', true); 173 | $response = $ws->receive(); 174 | $this->assertEquals($greeting, $response); 175 | } 176 | 177 | public function testMaskedEcho() { 178 | $ws = new Client('ws://localhost:' . self::$port . '/' . $this->test_id); 179 | 180 | $greeting = 'Hello WebSockets'; 181 | $ws->send($greeting, 'text', true); 182 | $response = $ws->receive(); 183 | $this->assertEquals($greeting, $response); 184 | } 185 | 186 | /** 187 | * @dataProvider timeoutProvider 188 | */ 189 | public function testTimeout($timeout) { 190 | try { 191 | $ws = new Client('ws://example.org:1111', array('timeout' => $timeout)); 192 | $start_time = microtime(true); 193 | $ws->send('foo'); 194 | } 195 | catch (WebSocket\ConnectionException $e) { 196 | $this->assertLessThan($timeout + 0.2, microtime(true) - $start_time); 197 | $this->assertGreaterThan($timeout - 0.2, microtime(true) - $start_time); 198 | } 199 | 200 | if (!isset($e)) $this->fail('Should have timed out and thrown a ConnectionException'); 201 | } 202 | 203 | public function timeoutProvider() { 204 | return array( 205 | array(1), 206 | array(2), 207 | ); 208 | } 209 | 210 | public function testChangeTimeout() { 211 | $timeout = 1; 212 | 213 | try { 214 | $ws = new Client('ws://example.org:1111', array('timeout' => 5)); 215 | $ws->setTimeout($timeout); 216 | $start_time = microtime(true); 217 | $ws->send('foo'); 218 | } 219 | catch (WebSocket\ConnectionException $e) { 220 | $this->assertLessThan($timeout + 0.2, microtime(true) - $start_time); 221 | $this->assertGreaterThan($timeout - 0.2, microtime(true) - $start_time); 222 | } 223 | 224 | if (!isset($e)) $this->fail('Should have timed out and thrown a ConnectionException'); 225 | } 226 | 227 | public function testDefaultHeaders() { 228 | $ws = new Client('ws://localhost:' . self::$port . '/' . $this->test_id); 229 | 230 | $ws->send('Dump headers'); 231 | 232 | $this->assertRegExp( 233 | "/GET \/$this->test_id HTTP\/1.1\r\n" 234 | . "host: localhost:".self::$port."\r\n" 235 | . "user-agent: websocket-client-php\r\n" 236 | . "connection: Upgrade\r\n" 237 | . "upgrade: websocket\r\n" 238 | . "sec-websocket-key: .*\r\n" 239 | . "sec-websocket-version: 13\r\n/", 240 | $ws->receive() 241 | ); 242 | } 243 | 244 | public function testUserAgentOverride() { 245 | $ws = new Client( 246 | 'ws://localhost:' . self::$port . '/' . $this->test_id, 247 | array('headers' => array('User-Agent' => 'Deep thought')) 248 | ); 249 | 250 | $ws->send('Dump headers'); 251 | 252 | $this->assertRegExp( 253 | "/GET \/$this->test_id HTTP\/1.1\r\n" 254 | . "host: localhost:".self::$port."\r\n" 255 | . "user-agent: Deep thought\r\n" 256 | . "connection: Upgrade\r\n" 257 | . "upgrade: websocket\r\n" 258 | . "sec-websocket-key: .*\r\n" 259 | . "sec-websocket-version: 13\r\n/", 260 | $ws->receive() 261 | ); 262 | } 263 | 264 | public function testAddingHeaders() { 265 | $ws = new Client( 266 | 'ws://localhost:' . self::$port . '/' . $this->test_id, 267 | array('headers' => array('X-Cooler-Than-Beeblebrox' => 'Slartibartfast')) 268 | ); 269 | 270 | $ws->send('Dump headers'); 271 | 272 | $this->assertRegExp( 273 | "/GET \/$this->test_id HTTP\/1.1\r\n" 274 | . "host: localhost:".self::$port."\r\n" 275 | . "user-agent: websocket-client-php\r\n" 276 | . "connection: Upgrade\r\n" 277 | . "upgrade: websocket\r\n" 278 | . "sec-websocket-key: .*\r\n" 279 | . "sec-websocket-version: 13\r\n" 280 | . "x-cooler-than-beeblebrox: Slartibartfast\r\n/", 281 | $ws->receive() 282 | ); 283 | } 284 | 285 | /** 286 | * @expectedException WebSocket\BadOpcodeException 287 | * @expectedExceptionMessage Bad opcode 'bad_opcode' 288 | */ 289 | public function testSendBadOpcode() { 290 | $ws = new Client('ws://localhost:' . self::$port); 291 | $ws->send('foo', 'bad_opcode'); 292 | } 293 | } 294 | --------------------------------------------------------------------------------22 | 26 |23 | 25 |tests/unit 24 |27 | 32 |30 | 31 | 33 | 43 |34 | 36 |lib/Server.php 35 |37 | 42 |38 | 40 |lib/Server.php 39 |lib/ 41 |