Serverside Websocket Handshake fails with 'Sec-WebSocket-Accept mismatch' - php

I tried to create a Websocket Server in PHP which uses the current WebSocket Protocol 13.
I implemented the header creation according to the RFC but in Google Chrome (Version 31) it still fails with the Error message Error during WebSocket handshake: Sec-WebSocket-Accept mismatch. Firefox shows at least the response but it also doesn't fire the clientside onOpen Event.
Below is a minimal testserver in PHP with the code I use to create the header:
<?php
$srv = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_bind($srv, '0.0.0.0', 50500);
socket_listen($srv, 10);
for(;;){
$sock = socket_accept($srv);
// get header
$header = socket_read($sock, 1024);
// extract key
$keys = array();
preg_match_all("/Sec-WebSocket-Key:\s*(.*)\s*/", $header, $keys);
if(count($keys) < 1){
// Not a valid Websocket Handshake
socket_close($sock);
continue;
}
// create hash according to RFC
$key = $keys[1][0];
$accept = base64_encode(SHA1($key."258EAFA5-E914-47DA-95CA-C5AB0DC85B11", true));
// create header
$h = "HTTP/1.1 101 Switching Protocols\r\n";
$h .= "Upgrade: websocket\r\n";
$h .= "Connection: Upgrade\r\n";
$h .= "Sec-WebSocket-Accept: $accept\r\n";
$h .= "Sec-WebSocket-Protocol: 13\r\n\r\n";
echo $h;
// send header to client
socket_write($sock,$h,strlen($h));
}
?>
Am I missing something here?
Thanks in advance!

The regexp /Sec-WebSocket-Key:\s*(.*)\s*/ will include the \r characters in the matched content (the . matches \r but not \n) so your $key variable will have the wrong value. You need to figure out a regexp that matches everything but the \r\n sequence at the end.
Also, the server should only include the Sec-WebSocket-Protocol attribute if the client included it in the request, and the value must be one of the protocols requested in the client Sec-WebSocket-Protocol attribute. Otherwise the client will give an error, see RFC 6455.

Related

webpage not printing winsock information

I am trying to send data from a winsock application to a PHP script. I have already tried various header types without any success and I can't seem to find enough information.
Data is sent, but the PHP script is not printing any results, so I think the error is headers types.
C Winsock code
int main()
{
SOCKADDR_IN sock;
SOCKET s;
WSADATA wsa;
int lengthofrequest = 0;
char httprequest[180] =
"POST /test.php?name=alex&password=secret HTTP/1.1\r\n"
"Host: 127.0.0.1\r\n"
"Pragma: no-cache\r\n"
"Content-type: text/html\r\n"
"Connection: close\r\n"
"Content-Length: 25\r\n"
"\r\n";
lengthofrequest = strlen(httprequest);
// init winsock
if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
return WSASYSNOTREADY;
sock.sin_addr.s_addr = inet_addr("127.0.0.1");
sock.sin_family = AF_INET;
sock.sin_port = htons(80);
// create socket
s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (s == INVALID_SOCKET)
return 1;
// connect to the http panel
int conn = connect(s, (SOCKADDR*)&sock, sizeof(sock));
if (conn < 0)
return 1;
// send http header
send(s, httprequest, lengthofrequest, 0);
// close socket
closesocket(s);
WSACleanup();
return 0;
}
PHP script
<?php
if(isset($_POST['name'], $_POST['password']))
{
echo $_POST['name'];
echo $_POST['password'];
}
?>
I would appreciate any help or guidance. Thanks.
You are mixing post and get
Use $_GET to access your data because your data is appended to the url
Try this:
<?php
if(isset($_GET['name'], $_GET['password']))
{
echo $_GET['name'];
echo $_GET['password'];
}
this should work but you should consider changing your request type to GET in the C code as well to make your code clear and simple
What you are doin here is ambiguous:
"POST /test.php?name=alex&password=secret HTTP/1.1\r\n"
Because you are sending get parameters using post and the post body is actually empty.
Also you should omit ?> at the end of your php script.

HTTP 400 BAD REQUEST error while sending GET request with php

While I'm trying to send a GET request to an address with php I receive HTTP 400 BAD REQUEST message and I can't figure out why.
Here's my code:
function redirect($url)
{
error_reporting(E_ERROR | E_PARSE);
$components = parse_url($url);
$port = $components["port"];
$ip = $components["host"];
$path = $components["path"];
//create and connect socket with the parameters entered by the user
//$sock = socket_create(AF_INET,SOCK_STREAM,SOL_TCP);
echo "Establishing connection to the given adress...\n";
//Connection timeout limit is set to 10 seconds...
if(!isset($port))
{
$port = 80;
}
$sock = fsockopen($ip, $port,$errno, $errstr, 10) or die("Unable to connect...");
$request = "GET $path HTTP/1.1" . "\r\n\r\n";
fwrite($sock, $request);
while ($header = stream_get_line($sock, 1024, "\r\n")) {
$response.= $header . "\n";
}
echo $response;
$loc = "";
if (preg_match("/Location:\'(.*)\\n/", $response, $results))
$loc = $results[1];
echo $loc;
}
Any suggestions?
A GET request also contains a header wich include things like the useragent oder the encoding, you should have a look at what you need to send and what's optional
For this specific problem I've found a solution. If you want to get the headers from the request that I sent in the question above, you can use php's get_headers($url) method. It retrieves the headers that I was looking for. I'm really new to protocols and web-services and also to php (1 or 1.5 weeks), therefore I may have asked a silly question and not have been specific enough. Anyways thank you very much for your answers.
Have a nice day!

API - is the connection secure?

How to check if a connection to a REST API is secure?
1) I'm building an API and want to make sure that the connection is running SSL before the response is generated and returned to the client.. Username and password is passed in every request
2) And how can you make a fsockopen connection with SSL?
$this->url = 'api.domain.com';
$this->path = '/v1/json/account/user/pass/?id=123';
if($this->fp = fsockopen($this->url, $this->port, $errno, $errstr, $this->timeout)){
$write = "$this->method $this->path HTTP/1.1\r\n";
$write .= "Host: $this->url\r\n";
$write .= "Content-Type: $this->content_type\r\n";
$write .= "Connection: Close\r\n\r\n";
fwrite($this->fp, $write);
while($line = fgets($this->fp)){
if($line !== false) $this->response .= $line;
}
fclose($this->fp);
}
Just check $_SERVER['HTTPS'].
The PHP manual at http://php.net/manual/en/reserved.variables.server.php says:
'HTTPS'
Set to a non-empty value if the script was queried through the HTTPS protocol.
Note: Note that when using ISAPI with IIS, the value will be off if the request was not made > through the HTTPS protocol.
Of course you can. You can even force it to be. HTTP works on port 80 and HTTPS on 443. You can discard all traffic not comming from 443.

Seeking in remote flv files using PHP

I'm string to seek in a remotely hosted FLV file and have it stream locally. Streaming from start works, but when I try to 'seek', player stops.
I'm using this script to seek to remote file
$fp = fsockopen($host, 80, $errno, $errstr, 30);
$out = "GET $path_to_flv HTTP/1.1\r\n";
$out .= "Host: $host\r\n";
$out .= "Range: bytes=$pos-$end\r\n";
$out .= "Connection: Close\r\n\r\n";
fwrite($fp, $out);
$content = false;
while (!feof($fp))
{
$data = fgets($fp, 1024);
if($content) echo $data;
if($data == "\r\n")
{
$content = true;
header("Content-Type: video/x-flv");
header("Content-Length: " . (urlfilesize($file) - $pos));
if($pos > 0)
{
print("FLV");
print(pack('C', 1));
print(pack('C', 1));
print(pack('N', 9));
print(pack('N', 9));
}
}
}
fclose($fp);
Any ideas ?
UPDATE
so apparently, even though the server signals it accepts range requests (with the Accept-Ranges: bytes), it does not actually do so. to see if there is another way to make the flv seekable, let's have a look at the communication between flash player and server (i use wireshark for this):
the request when starting the player is:
GET /files/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/ HTTP/1.1
Host: xxxxxx.megavideo.com
<some more headers>
<no range header>
this is answered with a response like that:
HTTP/1.0 200 OK
Server: Apache/1.3.37 (Debian GNU/Linux) PHP/4.4.7
Content-Type: video/flv
ETag: "<video-id>"
Content-Length: <length of complete video>
<some more headers>
<the flv content>
now when i seek in the flash player, another request is sent. it is almost the same as the initial one, with the following difference:
GET /files/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/8800968 HTTP/1.1
<same headers as first request>
which gets answered with a response almost the same as the initial one, with a difference only in the Content-Length header.
which lets me assume that the 8800968 at the end of the request url is the "seek range" (the byte offset in the file after seeking) we are looking for, and the second response Content-Length is the initial Content-Length (the length of the whole file) minus this range. which is the case indeed.
with this information, it should be possible to get what you want. good luck!
UPDATE END
this will only work if the server supports HTTP RANGE requests. if it does, it will return a 206 Partial Content response code with a Content-Range header and your requested range of bytes. check for these in the response to your request.

How do you get the HTTP status code for a remote domain in php?

I would like to create a batch script, to go through 20,000 links in a DB, and weed out all the 404s and such. How would I get the HTTP status code for a remote url?
Preferably not using curl, since I dont have it installed.
CURL would be perfect but since you don't have it, you'll have to get down and dirty with sockets. The technique is:
Open a socket to the server.
Send an HTTP HEAD request.
Parse the response.
Here is a quick example:
<?php
$url = parse_url('http://www.example.com/index.html');
$host = $url['host'];
$port = $url['port'];
$path = $url['path'];
$query = $url['query'];
if(!$port)
$port = 80;
$request = "HEAD $path?$query HTTP/1.1\r\n"
."Host: $host\r\n"
."Connection: close\r\n"
."\r\n";
$address = gethostbyname($host);
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_connect($socket, $address, $port);
socket_write($socket, $request, strlen($request));
$response = split(' ', socket_read($socket, 1024));
print "<p>Response: ". $response[1] ."</p>\r\n";
socket_close($socket);
?>
UPDATE: I've added a few lines to parse the URL
If im not mistaken none of the php built-in functions return the http status of a remote url, so the best option would be to use sockets to open a connection to the server, send a request and parse the response status:
pseudo code:
parse url => $host, $port, $path
$http_request = "GET $path HTTP/1.0\nHhost: $host\n\n";
$fp = fsockopen($host, $port, $errno, $errstr, $timeout), check for any errors
fwrite($fp, $request)
while (!feof($fp)) {
$headers .= fgets($fp, 4096);
$status = <parse $headers >
if (<status read>)
break;
}
fclose($fp)
Another option is to use an already build http client class in php that can return the headers without fetching the full page content, there should be a few open source classes available on the net...
This page looks like it has a pretty good setup to download a page using either curl or fsockopen, and can get the HTTP headers using either method (which is what you want, really).
After using that method, you'd want to check $output['info']['http_code'] to get the data you want.
Hope that helps.
You can use PEAR's HTTP::head function.
http://pear.php.net/manual/en/package.http.http.head.php

Categories