r3wt Posted April 28, 2014 Share Posted April 28, 2014 I am in development stages of a website. this website is primarily in php. today, i decided i reached a point in development where it was time to move everything to TLS, and not suprisingly the websocket connection is being refused and i'm not sure why. The only error i get is Notice: Undefined index: Sec-WebSocket-Key in /usr/share/nginx/html/models/funcs.php on line 933 Here is the handshaking function: function perform_handshaking($receved_header,$client_conn, $host, $port) { $headers = array(); $lines = preg_split("/\r\n/", $receved_header); foreach($lines as $line) { $line = chop($line); if(preg_match('/\A(\S+): (.*)\z/', $line, $matches)) { $headers[$matches[1]] = $matches[2]; } } $secKey = $headers['Sec-WebSocket-Key']; $secAccept = base64_encode(pack('H*', sha1($secKey . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'))); //hand shaking header $upgrade = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" . "Upgrade: websocket\r\n" . "Connection: Upgrade\r\n" . "WebSocket-Origin: $host\r\n" . "WebSocket-Location: wss://$host:$port/socket/server.php\r\n". "Sec-WebSocket-Accept:$secAccept\r\n\r\n"; socket_write($client_conn,$upgrade,strlen($upgrade)); } Here is the server // //socket server // require_once __DIR__ . '/../models/config.php'; $host = 'www.example'; $port = 8888; $admin = array("admin", "superadmin"); $null = NULL; $socket_key = fetchkey('0x33'); $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1); socket_bind($socket, 0, $port); socket_listen($socket); $clients = array($socket); while (true) { $changed = $clients; socket_select($changed, $null, $null, 0, 10); if (in_array($socket, $changed)) { $socket_new = socket_accept($socket); $clients[] = $socket_new; $header = socket_read($socket_new, 1024); perform_handshaking($header, $socket_new, $host, $port); socket_getpeername($socket_new, $ip); $found_socket = array_search($socket, $changed); sendOldMessages($socket_new); unset($changed[$found_socket]); } foreach ($changed as $changed_socket) { while(socket_recv($changed_socket, $buf, 1024, 0) >= 1) { $received_text = unmask($buf); $msg = json_decode($received_text); $msg_type = @$msg->type; $write_key = @$msg->writekey; if($msg_type == 'chatmsg' && $write_key == $socket_key) { $user_name = @$msg->name; $user_message = @$msg->message; $user_color = @$msg->color; if(in_array($user_name,$admin)) { if (strpos($user_message,'./ban') !== false) { newBan($user_message); break 2; } if (strpos($user_message,'./unban') !== false) { unBan($user_message); break 2; } } if(usernameExists($user_name)) { if($user_name == $null || $user_message == $null) { break 2; }else{ if(!isBant($user_name)) { newMessage($user_name,$user_message,$user_color); break 2; }else{ cooldown($user_name); } } } } } if($msg_type == 'alert' && $write_key == $socket_key) { $recipient = @$msg->user; $alert = @$msg->alert; $response_text = mask(json_encode(array('type'=>'alert', 'user'=>security($recipient), 'alert'=>security($alert)))); send_message($response_text); } if($msg_type == 'mktdata' && $write_key == $socket_key) { $pair = @$msg->pair; $price = @$msg->price; $response_text = mask(json_encode(array('type'=>'mktdata', 'pair'=>$pair, 'price'=>$price))); send_message($response_text); } $buf = @socket_read($changed_socket, 1024, PHP_NORMAL_READ); if ($buf === false) { $found_socket = array_search($changed_socket, $clients); @socket_getpeername($changed_socket, $ip); unset($clients[$found_socket]); } } } socket_close($sock); From what i can gather, i somehow need to give this server script access to my ssl certificate so it can decode the post request(this is my basic understanding of how tls works, could be wrong, correct me if i am please.) . Now, i am at a total loss as to what i should try next, and there is quiet a shortage of information on this subject related to php atleast. Some other information to note: chat messages are posted via ajax to a file, sendmsg.php that also must establish a connection to this socket server and broadcast the posted message. likewise, a cronjob runs to get new alert messages and market price statistics and connects and broadcasts the information to the server in basically the same way as sendmsg.php does. Any questions are welcome, and any advice is appreciated. i may drop some btc to anyone who is able to help me fix this and move on to the next challenge thanks, Garrett Quote Link to comment Share on other sites More sharing options...
Jacques1 Posted April 28, 2014 Share Posted April 28, 2014 Hi, the server needs to perform a full TLS handshake. Implementing this on your own is practically impossible and simply not a good idea. So there are basically three options: You use a WebSocket library which can do TLS (looks like Wrench is a good choice, but I've never tried it myself). You rewrite the code from scratch using the high-level Streams. This is what Wrench does under the hood. You use nginx as a reverse WebSocket proxy to convert the WSS traffic to plain WS. Quote Link to comment Share on other sites More sharing options...
r3wt Posted April 29, 2014 Author Share Posted April 29, 2014 well, i converted it all to the stream functions as you suggested, still no magic. finally get a response in the debug console WebSocket connection to 'wss://openex.info:8888/socket/server2.php' failed: Connection closed before receiving a handshake response here is the new code including all functions. it was hard to find replacements for all the socket functions, and i'm sure that i made a billion errors: // //php websocket server with tls // require_once __DIR__ . '/../models/config.php'; $host = 'openex.info'; $port = '8888'; $admin = array("admin", "superadmin"); $null = NULL; $socket_key = fetchkey('0x33'); $context = stream_context_create(); stream_context_set_option($context, 'ssl', 'local_cert', $ssl_cert); stream_context_set_option($context, 'ssl', 'passphrase', $ssl_pass); stream_context_set_option($context, 'ssl', 'allow_self_signed', false); stream_context_set_option($context, 'ssl', 'verify_peer', true); $socket = stream_socket_server('ssl://'.$host.':'.$port, $errno, $errstr, STREAM_SERVER_BIND|STREAM_SERVER_LISTEN, $context); $clients = array($socket); while(true) { $changed = $clients; stream_select($changed, $null, $null, 0, 10); if (in_array($socket, $changed)) { $socket_new = stream_socket_accept($socket); $clients[] = $socket_new; $header = fread($socket_new, 1024); perform_handshaking2($header, $socket_new, $host, $port); $peer = stream_socket_get_name($socket_new, true); $found_socket = array_search($socket, $changed); sendOldMessages2($peer); unset($changed[$found_socket]); } foreach ($changed as $changed_socket) { while(fread($changed_socket, $buf, 1024, 0) >= 1) { $received_text = unmask($buf); $msg = json_decode($received_text); $msg_type = @$msg->type; $write_key = @$msg->writekey; if($msg_type == 'chatmsg' && $write_key == $socket_key) { $user_name = @$msg->name; $user_message = @$msg->message; $user_color = @$msg->color; if(in_array($user_name,$admin)) { if (strpos($user_message,'./ban') !== false) { newBan2($user_message); break 2; } if (strpos($user_message,'./unban') !== false) { unBan2($user_message); break 2; } } if(usernameExists($user_name)) { if($user_name == $null || $user_message == $null) { break 2; }else{ if(!isBant2($user_name)) { newMessage2($user_name,$user_message,$user_color); break 2; }else{ cooldown2($user_name); } } } } } if($msg_type == 'alert' && $write_key == $socket_key) { $recipient = @$msg->user; $alert = @$msg->alert; $response_text = mask(json_encode(array('type'=>'alert', 'user'=>security($recipient), 'alert'=>security($alert)))); send_message2($response_text); } if($msg_type == 'mktdata' && $write_key == $socket_key) { $pair = @$msg->pair; $price = @$msg->price; $response_text = mask(json_encode(array('type'=>'mktdata', 'pair'=>$pair, 'price'=>$price))); send_message2($response_text); } $buf = @fread($changed_socket, 1024, PHP_NORMAL_READ); if ($buf === false) { $found_socket = array_search($changed_socket, $clients); @stream_socket_get_name($changed_socket, true); unset($clients[$found_socket]); } } } fclose($socket); function perform_handshaking2($receved_header,$client_conn, $host, $port) { $headers = array(); $lines = preg_split("/\r\n/", $receved_header); foreach($lines as $line) { $line = chop($line); if(preg_match('/\A(\S+): (.*)\z/', $line, $matches)) { $headers[$matches[1]] = $matches[2]; } } $secKey = $headers['Sec-WebSocket-Key']; $secAccept = base64_encode(pack('H*', sha1($secKey . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'))); //hand shaking header $upgrade = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" . "Upgrade: websocket\r\n" . "Connection: Upgrade\r\n" . "WebSocket-Origin: $host\r\n" . "WebSocket-Location: wss://$host:$port/socket/server2.php\r\n". "Sec-WebSocket-Accept:$secAccept\r\n\r\n"; fwrite($client_conn,$upgrade,strlen($upgrade)); } function send_message2($msg) { global $clients; foreach($clients as $changed_socket) { @fwrite($changed_socket,$msg); } return true; } function send_message_single_client2($msg,$client) { @fwrite($client,$msg); return true; } function newBan2($user_message) { global $mysqli; $command = explode(" ", $user_message); if(count($command) >= 2) { $toban = $command[1]; if(!empty($toban)) { if(usernameExists($toban)) { $begin = time(); if(isset($command[2])) { switch ($command[2]) { case '10m' : $length = 10 * 60; $duration = '10 minutes'; break; case '30m' : $length = 30 * 60; $duration = '30 minutes'; break; case '1h' : $length = 60 * 60; $duration = '1 hour'; break; case '1d' : $length = 24 * 60 * 60; $duration = '1 day'; break; case '1w' : $length = 7 * 24 * 60 * 60; $duration = '1 week'; break; default : return; } }else{ $length = 5 * 60; $duration = '5 minutes'; } $end = $begin + $length; $stmt = $mysqli->prepare("INSERT INTO uc_chat_bans (username,starttime,endtime,duration) VALUES (?,?,?,?)"); $stmt->bind_param('siis',$toban,$begin,$end,$duration); $stmt->execute(); $stmt->close(); $ban_nme = 'System'; $ban_msg = '** '.security($toban).' has been banned from chat for '.security($duration).'**'; $ban_col = '000000'; $ban_now = time(); $stmt = $mysqli->prepare("INSERT INTO uc_chat_msg (username,message,color,timestamp) VALUES (?,?,?,?);"); $stmt->bind_param('sssi',$ban_nme,$ban_msg,$ban_col,$ban_now); $stmt->execute(); $stmt->close(); $response_text = mask(json_encode(array('type'=>'userban', 'toban'=>$toban, 'bantime'=>$duration))); send_message2($response_text); //send data } } } } function unBan2($user_message) { global $mysqli; $command = explode(" ", $user_message); $tounban = $command[1]; $stmt = $mysqli->prepare("DELETE FROM uc_chat_bans WHERE username = ?"); $stmt->bind_param('s',$tounban); $stmt->execute(); $stmt->close(); } function isBant2($name) { $user111 = $user000 = null; global $mysqli; $time = time(); $stmt = $mysqli->prepare("SELECT username FROM uc_chat_bans WHERE endtime > ? AND username = ?"); $stmt->bind_param('is',$time,$name); $stmt->execute(); $stmt->bind_result($user000); while($stmt->fetch()) { $user111 = $user000; } if($user111 == $name) { $stmt->close(); return true; }else{ $stmt->close(); return false; } } function sendOldMessages2($socket_new) { global $mysqli; $query = $mysqli->query("SELECT * FROM (SELECT * FROM uc_chat_msg WHERE `hidden`='0' ORDER BY `id` DESC LIMIT 100) as last100 ORDER BY id"); while($row = $query->fetch_assoc()) { $response_text = mask(json_encode(array('type'=>'usermsg', 'name'=>security($row["username"]), 'message'=>security($row["message"]), 'color'=>security($row["color"]),'timestamp'=>security($row["timestamp"])))); send_message_single_client2($response_text, $socket_new);//send old messages to the new client } $query->close(); } function newMessage2($user_name,$user_message,$user_color) { global $mysqli; $now = time(); $stmt = $mysqli->prepare("INSERT INTO uc_chat_msg (username,message,color,timestamp) VALUES (?,?,?,?);"); $stmt->bind_param('sssi',$user_name,$user_message,$user_color,$now); $stmt->execute(); $stmt->close(); $response_text = mask(json_encode(array('type'=>'usermsg', 'name'=>security($user_name), 'message'=>security($user_message), 'color'=>security($user_color), 'timestamp'=>$now))); send_message($response_text); } function cooldown2($user_name) { $response_text = mask(json_encode(array('type'=>'cooldown', 'user'=>security($user_name)))); send_message($response_text); //send data } Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.